Merge "Adds a getter for IllustrationPreference content description." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 4e34b63..df4a3e5 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,10 +21,12 @@
         // !!! KEEP THIS LIST ALPHABETICAL !!!
         "aconfig_mediacodec_flags_java_lib",
         "android.adaptiveauth.flags-aconfig-java",
+        "android.app.appfunctions.flags-aconfig-java",
         "android.app.contextualsearch.flags-aconfig-java",
         "android.app.flags-aconfig-java",
         "android.app.ondeviceintelligence-aconfig-java",
         "android.app.smartspace.flags-aconfig-java",
+        "android.app.supervision.flags-aconfig-java",
         "android.app.usage.flags-aconfig-java",
         "android.app.wearable.flags-aconfig-java",
         "android.appwidget.flags-aconfig-java",
@@ -98,6 +100,7 @@
         "framework-jobscheduler-job.flags-aconfig-java",
         "framework_graphics_flags_java_lib",
         "hwui_flags_java_lib",
+        "interaction_jank_monitor_flags_lib",
         "libcore_exported_aconfig_flags_lib",
         "libgui_flags_java_lib",
         "power_flags_lib",
@@ -435,10 +438,23 @@
     name: "android.companion.virtualdevice.flags-aconfig",
     package: "android.companion.virtualdevice.flags",
     container: "system",
+    exportable: true,
     srcs: ["core/java/android/companion/virtual/flags/*.aconfig"],
 }
 
 java_aconfig_library {
+    name: "android.companion.virtualdevice.flags-aconfig-java-export",
+    aconfig_declarations: "android.companion.virtualdevice.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    mode: "exported",
+    min_sdk_version: "30",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+}
+
+java_aconfig_library {
     name: "android.companion.virtual.flags-aconfig-java",
     aconfig_declarations: "android.companion.virtual.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
@@ -690,7 +706,7 @@
     exportable: true,
     package: "android.media.tv.flags",
     container: "system",
-    srcs: ["media/java/android/media/tv/flags/media_tv.aconfig"],
+    srcs: ["media/java/android/media/tv/flags/*.aconfig"],
 }
 
 java_aconfig_library {
@@ -1211,6 +1227,21 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// Supervision
+aconfig_declarations {
+    name: "android.app.supervision.flags-aconfig",
+    exportable: true,
+    package: "android.app.supervision.flags",
+    container: "system",
+    srcs: ["core/java/android/app/supervision/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.supervision.flags-aconfig-java",
+    aconfig_declarations: "android.app.supervision.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // SurfaceFlinger
 java_aconfig_library {
     name: "surfaceflinger_flags_java_lib",
@@ -1383,6 +1414,33 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+// AppFunctions
+aconfig_declarations {
+    name: "android.app.appfunctions.flags-aconfig",
+    exportable: true,
+    package: "android.app.appfunctions.flags",
+    container: "system",
+    srcs: ["core/java/android/app/appfunctions/flags/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "android.app.appfunctions.flags-aconfig-java",
+    aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+java_aconfig_library {
+    name: "android.app.appfunctions.exported-flags-aconfig-java",
+    aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    mode: "exported",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+    ],
+    min_sdk_version: "30",
+}
+
 // Adaptive Auth
 aconfig_declarations {
     name: "android.adaptiveauth.flags-aconfig",
@@ -1533,3 +1591,17 @@
     aconfig_declarations: "dropbox_flags",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+// Zero Jank
+aconfig_declarations {
+    name: "interaction_jank_monitor_flags",
+    package: "com.android.internal.jank",
+    container: "system",
+    srcs: ["core/java/com/android/internal/jank/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "interaction_jank_monitor_flags_lib",
+    aconfig_declarations: "interaction_jank_monitor_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 7f4871f..f8907f3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -109,7 +109,7 @@
         ":android.hardware.security.keymint-V3-java-source",
         ":android.hardware.security.secureclock-V1-java-source",
         ":android.hardware.thermal-V2-java-source",
-        ":android.hardware.tv.tuner-V2-java-source",
+        ":android.hardware.tv.tuner-V3-java-source",
         ":android.security.apc-java-source",
         ":android.security.authorization-java-source",
         ":android.security.legacykeystore-java-source",
@@ -261,7 +261,6 @@
         "devicepolicyprotosnano",
         "ImmutabilityAnnotation",
 
-        "com.android.sysprop.init",
         "com.android.sysprop.localization",
         "PlatformProperties",
     ],
diff --git a/INPUT_OWNERS b/INPUT_OWNERS
index 06ead06..9b1016e 100644
--- a/INPUT_OWNERS
+++ b/INPUT_OWNERS
@@ -1,4 +1,5 @@
 # Bug component: 136048
+# Please assign bugs to android-framework-input-triage@.
 [email protected]
 [email protected]
 [email protected]
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 5f32ba0..ec58210 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -50,7 +50,7 @@
 framework_minus_apex_cmd = "$(location hoststubgen) " +
     "@$(location :ravenwood-standard-options) " +
     "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
-    "--out-impl-jar $(location ravenwood.jar) " +
+    "--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) "
@@ -183,7 +183,7 @@
         "--stats-file $(location hoststubgen_services.core_stats.csv) " +
         "--supported-api-list-file $(location hoststubgen_services.core_apis.csv) " +
 
-        "--out-impl-jar $(location ravenwood.jar) " +
+        "--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) " +
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 49384cd..dfacbc4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -137,13 +137,20 @@
       "name": "CtsStrictJavaPackagesTestCases"
     }
   ],
-  "postsubmit-ravenwood": [
+  "ravenwood-presubmit": [
     {
       "name": "CtsUtilTestCasesRavenwood",
       "host": true,
       "file_patterns": [
         "[Rr]avenwood"
       ]
+    },
+    {
+      "name": "RavenwoodBivalentTest",
+      "host": true,
+      "file_patterns": [
+        "[Rr]avenwood"
+      ]
     }
   ],
   "postsubmit-managedprofile-stress": [
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 86f41e1..c2d5470 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -17,6 +17,17 @@
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-metric-instrumentation" />
 
+    // Deal with Play Protect blocking apk installations.
+    // The first setting disables the verification, the second one lowers the timeout from
+    // 1hr to 10s, the third one resets the value after the test is complete, and the final
+    // setting skips the device reboot after modifying the settings.
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+        <option name="set-global-setting" key="verifier_engprod" value="1" />
+        <option name="restore-settings" value="true" />
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
diff --git a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
index fcb13a8..a12121f 100644
--- a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
@@ -37,9 +37,13 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.FutureTask;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
 
 /**
  * Benchmarks for {@link android.content.om.OverlayManager}.
@@ -49,7 +53,6 @@
     private static final int OVERLAY_PKG_COUNT = 10;
     private static Context sContext;
     private static OverlayManager sOverlayManager;
-    private static Executor sExecutor;
     private static ArrayList<TestPackageInstaller.InstalledPackage> sSmallOverlays =
             new ArrayList<>();
     private static ArrayList<TestPackageInstaller.InstalledPackage> sLargeOverlays =
@@ -58,18 +61,45 @@
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
+    // Uncheck the checked exceptions in a callable for convenient stream usage.
+    // Any exception will fail the test anyway.
+    private static <T> T uncheck(Callable<T> c) {
+        try {
+            return c.call();
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @BeforeClass
     public static void classSetUp() throws Exception {
         sContext = InstrumentationRegistry.getTargetContext();
         sOverlayManager = new OverlayManager(sContext);
-        sExecutor = (command) -> new Thread(command).start();
 
-        // Install all of the test overlays.
-        TestPackageInstaller installer = new TestPackageInstaller(sContext);
+        // Install all of the test overlays. Play Protect likes to block these for 10 sec each
+        // so let's install them in parallel to speed up the wait.
+        final var installer = new TestPackageInstaller(sContext);
+        final var es = Executors.newFixedThreadPool(2 * OVERLAY_PKG_COUNT);
+        final var smallFutures = new ArrayList<Future<TestPackageInstaller.InstalledPackage>>(
+                OVERLAY_PKG_COUNT);
+        final var largeFutures = new ArrayList<Future<TestPackageInstaller.InstalledPackage>>(
+                OVERLAY_PKG_COUNT);
         for (int i = 0; i < OVERLAY_PKG_COUNT; i++) {
-            sSmallOverlays.add(installer.installPackage("Overlay" + i +".apk"));
-            sLargeOverlays.add(installer.installPackage("LargeOverlay" + i +".apk"));
+            final var index = i;
+            smallFutures.add(es.submit(() -> installer.installPackage("Overlay" + index + ".apk")));
+            largeFutures.add(
+                    es.submit(() -> installer.installPackage("LargeOverlay" + index + ".apk")));
         }
+        es.shutdown();
+        assertTrue(es.awaitTermination(15 * 2 * OVERLAY_PKG_COUNT, TimeUnit.SECONDS));
+        sSmallOverlays.addAll(smallFutures.stream().map(f -> uncheck(f::get)).sorted(
+                Comparator.comparing(
+                        TestPackageInstaller.InstalledPackage::getPackageName)).toList());
+        sLargeOverlays.addAll(largeFutures.stream().map(f -> uncheck(f::get)).sorted(
+                Comparator.comparing(
+                        TestPackageInstaller.InstalledPackage::getPackageName)).toList());
     }
 
     @AfterClass
@@ -77,7 +107,6 @@
         for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
             overlay.uninstall();
         }
-
         for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
             overlay.uninstall();
         }
@@ -86,37 +115,39 @@
     @After
     public void tearDown() throws Exception {
         // Disable all test overlays after each test.
-        for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
-            assertSetEnabled(sContext, overlay.getPackageName(), false);
-        }
-
-        for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
-            assertSetEnabled(sContext, overlay.getPackageName(), false);
-        }
+        assertSetEnabled(false, sContext,
+                Stream.concat(sSmallOverlays.stream(), sLargeOverlays.stream()).map(
+                        p -> p.getPackageName()));
     }
 
     /**
-     * Enables the overlay and waits for the APK path change sto be propagated to the context
+     * Enables the overlay and waits for the APK path changes to be propagated to the context
      * AssetManager.
      */
-    private void assertSetEnabled(Context context, String overlayPackage, boolean eanabled)
-            throws Exception {
-        sOverlayManager.setEnabled(overlayPackage, true, UserHandle.SYSTEM);
+    private void assertSetEnabled(boolean enabled, Context context, Stream<String> packagesStream) {
+        final var overlayPackages = packagesStream.toList();
+        overlayPackages.forEach(
+                name -> sOverlayManager.setEnabled(name, enabled, UserHandle.SYSTEM));
 
         // Wait for the overlay changes to propagate
-        FutureTask<Boolean> task = new FutureTask<>(() -> {
-            while (true) {
-                for (String path : context.getAssets().getApkPaths()) {
-                    if (eanabled == path.contains(overlayPackage)) {
-                        return true;
-                    }
-                }
+        final var endTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(20);
+        final var expectedPackagesFound = enabled ? overlayPackages.size() : 0;
+        boolean assetsUpdated = false;
+        do {
+            final var packagesFound = Arrays.stream(context.getAssets().getApkPaths()).filter(
+                    assetPath -> overlayPackages.stream().anyMatch(assetPath::contains)).count();
+            if (packagesFound == expectedPackagesFound) {
+                assetsUpdated = true;
+                break;
             }
-        });
+            Thread.yield();
+        } while (System.nanoTime() < endTime);
+        assertTrue("Failed to set state to " + enabled + " for overlays " + overlayPackages,
+                assetsUpdated);
+    }
 
-        sExecutor.execute(task);
-        assertTrue("Failed to load overlay " + overlayPackage,
-                task.get(20, TimeUnit.SECONDS));
+    private void assertSetEnabled(boolean enabled, Context context, String overlayPackage) {
+        assertSetEnabled(enabled, context, Stream.of(overlayPackage));
     }
 
     @Test
@@ -124,11 +155,11 @@
         String packageName = sSmallOverlays.get(0).getPackageName();
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            assertSetEnabled(sContext, packageName, true);
+            assertSetEnabled(true, sContext, packageName);
 
             // Disable the overlay for the next iteration of the test
             state.pauseTiming();
-            assertSetEnabled(sContext, packageName, false);
+            assertSetEnabled(false, sContext, packageName);
             state.resumeTiming();
         }
     }
@@ -138,11 +169,11 @@
         String packageName = sSmallOverlays.get(0).getPackageName();
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            assertSetEnabled(sContext, packageName, true);
+            assertSetEnabled(true, sContext, packageName);
 
             // Disable the overlay and remove the idmap for the next iteration of the test
             state.pauseTiming();
-            assertSetEnabled(sContext, packageName, false);
+            assertSetEnabled(false, sContext, packageName);
             sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
             state.resumeTiming();
         }
@@ -153,11 +184,11 @@
         String packageName = sLargeOverlays.get(0).getPackageName();
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            assertSetEnabled(sContext, packageName, true);
+            assertSetEnabled(true, sContext, packageName);
 
             // Disable the overlay and remove the idmap for the next iteration of the test
             state.pauseTiming();
-            assertSetEnabled(sContext, packageName, false);
+            assertSetEnabled(false, sContext, packageName);
             sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
             state.resumeTiming();
         }
@@ -169,30 +200,28 @@
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            assertSetEnabled(sContext, packageName, true);
+            assertSetEnabled(true, sContext, packageName);
             state.resumeTiming();
 
-            assertSetEnabled(sContext, packageName, false);
+            assertSetEnabled(false, sContext, packageName);
         }
     }
 
     @Test
     public void getStringOneSmallOverlay() throws Exception {
         String packageName = sSmallOverlays.get(0).getPackageName();
-        assertSetEnabled(sContext, packageName, true);
+        assertSetEnabled(true, sContext, packageName);
 
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             sContext.getString(R.string.short_text);
         }
-
-        assertSetEnabled(sContext, packageName, false);
     }
 
     @Test
     public void getStringOneLargeOverlay() throws Exception {
         String packageName = sLargeOverlays.get(0).getPackageName();
-        assertSetEnabled(sContext, packageName, true);
+        assertSetEnabled(true, sContext, packageName);
 
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -200,16 +229,12 @@
                 sContext.getString(resId);
             }
         }
-
-        assertSetEnabled(sContext, packageName, false);
     }
 
     @Test
     public void getStringTenOverlays() throws Exception {
-        // Enable all test overlays
-        for (TestPackageInstaller.InstalledPackage overlay : sSmallOverlays) {
-            assertSetEnabled(sContext, overlay.getPackageName(), true);
-        }
+        // Enable all small test overlays
+        assertSetEnabled(true, sContext, sSmallOverlays.stream().map(p -> p.getPackageName()));
 
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
@@ -219,10 +244,8 @@
 
     @Test
     public void getStringLargeTenOverlays() throws Exception {
-        // Enable all test overlays
-        for (TestPackageInstaller.InstalledPackage overlay : sLargeOverlays) {
-            assertSetEnabled(sContext, overlay.getPackageName(), true);
-        }
+        // Enable all large test overlays
+        assertSetEnabled(true, sContext, sLargeOverlays.stream().map(p -> p.getPackageName()));
 
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index d7b1c9a2..f20b170 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -43,7 +43,6 @@
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -143,7 +142,7 @@
 
         // Always use the same server for consistency across the benchmarks.
         server = config.serverFactory().newServer(
-                ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+                config.messageSize(), config.protocol().getProtocols(),
                 ciphers(config));
 
         server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
@@ -197,7 +196,6 @@
      */
     @Test
     @Parameters(method = "getParams")
-    @Ignore("b/351034205")
     public void time(Config config) throws Exception {
         reset();
         setup(config);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
index 0655f45..ba2acb8 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
@@ -43,10 +43,10 @@
         factories.clientFactory, channelType, port, protocols, ciphers);
   }
 
-  public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+  public ServerEndpoint newServer(int messageSize,
       String[] protocols, String[] ciphers) throws IOException {
     return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
-        channelType, messageSize, protocols, ciphers);
+        messageSize, protocols, ciphers);
   }
 
   private static final class Factories {
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
index 3631c3f2..1e4f124 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -34,8 +34,6 @@
 import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLSocketFactory;
 
-import org.conscrypt.ChannelType;
-
 /**
  * A simple socket-based test server.
  */
@@ -63,7 +61,6 @@
     }
 
     private final ServerSocket serverSocket;
-    private final ChannelType channelType;
     private final SSLSocketFactory socketFactory;
     private final int messageSize;
     private final String[] protocols;
@@ -78,11 +75,10 @@
     private volatile Future<?> processFuture;
 
     ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
-            ChannelType channelType, int messageSize, String[] protocols,
+            int messageSize, String[] protocols,
             String[] cipherSuites) throws IOException {
-        this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+        this.serverSocket = serverSocketFactory.createServerSocket();
         this.socketFactory = socketFactory;
-        this.channelType = channelType;
         this.messageSize = messageSize;
         this.protocols = protocols;
         this.cipherSuites = cipherSuites;
@@ -134,7 +130,7 @@
                 if (stopping) {
                     return;
                 }
-                socket = channelType.accept(serverSocket, socketFactory);
+                socket = (SSLSocket) serverSocket.accept();
                 socket.setEnabledProtocols(protocols);
                 socket.setEnabledCipherSuites(cipherSuites);
 
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index 8916a3c..af3c405 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -43,7 +43,6 @@
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -131,8 +130,7 @@
 
         final ChannelType channelType = config.channelType();
 
-        server = config.serverFactory().newServer(
-            channelType, config.messageSize(),
+        server = config.serverFactory().newServer(config.messageSize(),
             new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
         server.setMessageProcessor(new MessageProcessor() {
             @Override
@@ -202,7 +200,6 @@
 
     @Test
     @Parameters(method = "getParams")
-    @Ignore("b/351034205")
     public void throughput(Config config) throws Exception {
         setup(config);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
index 80cd86c..237c747 100644
--- a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,11 +34,11 @@
 public class AdditionPerfTest {
 
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeAddConstantToLocalInt() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         int result = 0;
         while (state.keepRunning()) {
             result += 123;
@@ -46,7 +46,7 @@
     }
     @Test
     public void timeAddTwoLocalInts() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         int result = 0;
         int constant = 123;
         while (state.keepRunning()) {
@@ -55,7 +55,7 @@
     }
     @Test
     public void timeAddConstantToLocalLong() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         long result = 0;
         while (state.keepRunning()) {
             result += 123L;
@@ -63,7 +63,7 @@
     }
     @Test
     public void timeAddTwoLocalLongs() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         long result = 0;
         long constant = 123L;
         while (state.keepRunning()) {
@@ -72,7 +72,7 @@
     }
     @Test
     public void timeAddConstantToLocalFloat() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         float result = 0.0f;
         while (state.keepRunning()) {
             result += 123.0f;
@@ -80,7 +80,7 @@
     }
     @Test
     public void timeAddTwoLocalFloats() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         float result = 0.0f;
         float constant = 123.0f;
         while (state.keepRunning()) {
@@ -89,7 +89,7 @@
     }
     @Test
     public void timeAddConstantToLocalDouble() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         double result = 0.0;
         while (state.keepRunning()) {
             result += 123.0;
@@ -97,7 +97,7 @@
     }
     @Test
     public void timeAddTwoLocalDoubles() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         double result = 0.0;
         double constant = 123.0;
         while (state.keepRunning()) {
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
index 2f6c378..1222bc2 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,11 +33,11 @@
 public class ArrayCopyPerfTest {
 
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeManualArrayCopy() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         char[] src = new char[8192];
         while (state.keepRunning()) {
             char[] dst = new char[8192];
@@ -49,7 +49,7 @@
 
     @Test
     public void time_System_arrayCopy() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         char[] src = new char[8192];
         while (state.keepRunning()) {
             char[] dst = new char[8192];
@@ -59,7 +59,7 @@
 
     @Test
     public void time_Arrays_copyOf() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         char[] src = new char[8192];
         while (state.keepRunning()) {
             char[] dst = Arrays.copyOf(src, 8192);
@@ -68,7 +68,7 @@
 
     @Test
     public void time_Arrays_copyOfRange() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         char[] src = new char[8192];
         while (state.keepRunning()) {
             char[] dst = Arrays.copyOfRange(src, 0, 8192);
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
index d17add7..3f95e3e 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -38,7 +38,7 @@
     }
 
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     Foo[] mArray = new Foo[27];
     {
@@ -46,7 +46,7 @@
     }
     @Test
     public void timeArrayIteration() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             int sum = 0;
             for (int i = 0; i < mArray.length; i++) {
@@ -56,7 +56,7 @@
     }
     @Test
     public void timeArrayIterationCached() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             int sum = 0;
             Foo[] localArray = mArray;
@@ -69,7 +69,7 @@
     }
     @Test
     public void timeArrayIterationForEach() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             int sum = 0;
             for (Foo a: mArray) {
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
index 3a57db8..1423a13 100644
--- a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +39,7 @@
         int mSplat;
     }
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     ArrayList<Foo> mList = new ArrayList<Foo>();
     {
@@ -47,7 +47,7 @@
     }
     @Test
     public void timeArrayListIterationIndexed() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             int sum = 0;
             ArrayList<Foo> list = mList;
@@ -59,7 +59,7 @@
     }
     @Test
     public void timeArrayListIterationForEach() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             int sum = 0;
             for (Foo a : mList) {
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
index 3fb3bc8..0283105 100644
--- a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -38,7 +38,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class BigIntegerPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     // A simple sum of products computation, mostly so we can check timing in the
     // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
@@ -61,7 +62,7 @@
     // Execute the above rep times, optionally timing it.
     @Test
     public void repeatInner() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 10; i <= 10_000; i *= 10) {
                 inner(100, i);
@@ -85,7 +86,7 @@
     // Check results for equality, and print one, to compaare against reference.
     @Test
     public void repeatHarmonic1000() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 5; i <= 5_000; i *= 10) {
                 BigInteger refRes = harmonic1000(i);
@@ -106,7 +107,7 @@
     // us to time and check it for consistency as well.
     @Test
     public void repeatToString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 5; i <= 5_000; i *= 10) {
                 BigInteger refRes = harmonic1000(i);
@@ -146,7 +147,7 @@
     // to compare to reference.
     @Test
     public void repeatEApprox() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 10; i <= 10_000; i *= 10) {
                 BigInteger refRes = eApprox(100_000, i);
@@ -165,7 +166,7 @@
     // Test / time modPow()
     @Test
     public void repeatModPow() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 5; i <= 500; i *= 10) {
                 BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
@@ -198,7 +199,7 @@
     // Test / time modInverse()
     @Test
     public void repeatModInverse() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 10; i <= 10_000; i *= 10) {
                 BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
index 2a1b5d1..11ca73a 100644
--- a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
@@ -16,8 +16,9 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
+
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +40,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public final class BufferedZipFilePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     int[] mReadSize = new int[] {4, 32, 128};
     int[] mCompressedSize = new int[] {128, 1024, 8192, 65536};
@@ -67,7 +69,7 @@
 
     @Test
     public void timeUnbufferedRead() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mCompressedSize.length; i++) {
                 for (int j = 0; j < mReadSize.length; j++) {
@@ -87,7 +89,7 @@
 
     @Test
     public void timeBufferedRead() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mCompressedSize.length; i++) {
                 for (int j = 0; j < mReadSize.length; j++) {
diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
index 5f599ea..0abe194 100644
--- a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,7 +30,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ClassLoaderResourcePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
     private static final String MISSING_RESOURCE = "missing_entry";
@@ -40,7 +41,7 @@
         ClassLoader currentClassLoader = getClass().getClassLoader();
         Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));
 
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             currentClassLoader.getResource(EXISTENT_RESOURCE);
         }
@@ -51,7 +52,7 @@
         ClassLoader currentClassLoader = getClass().getClassLoader();
         Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));
 
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             currentClassLoader.getResource(MISSING_RESOURCE);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
index ea24984..52441d1 100644
--- a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ClonePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     static class CloneableObject implements Cloneable {
         public Object clone() throws CloneNotSupportedException {
@@ -1127,7 +1128,7 @@
     public void time_Object_clone() {
         try {
             CloneableObject o = new CloneableObject();
-            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            final BenchmarkState state = mBenchmarkRule.getState();
             while (state.keepRunning()) {
                 o.clone();
             }
@@ -1140,7 +1141,7 @@
     public void time_Object_manyFieldClone() {
         try {
             CloneableManyFieldObject o = new CloneableManyFieldObject();
-            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            final BenchmarkState state = mBenchmarkRule.getState();
             while (state.keepRunning()) {
                 o.clone();
             }
@@ -1153,7 +1154,7 @@
     public void time_Object_deepClone() {
         try {
             DeepCloneable o = new DeepCloneable();
-            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            final BenchmarkState state = mBenchmarkRule.getState();
             while (state.keepRunning()) {
                 o.clone();
             }
@@ -1165,7 +1166,7 @@
     @Test
     public void time_Array_clone() {
         int[] o = new int[32];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             o.clone();
         }
@@ -1177,7 +1178,7 @@
         for (int i = 0; i < o.length / 2; ++i) {
             o[i] = new Object();
         }
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             o.clone();
         }
@@ -1189,7 +1190,7 @@
         for (int i = 0; i < o.length / 2; ++i) {
             o[i] = new Object();
         }
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             o.clone();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
index 82247dc..e6c5aca 100644
--- a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,7 +36,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class DeepArrayOpsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private Object[] mArray;
     private Object[] mArray2;
@@ -99,7 +100,7 @@
     @Parameters(method = "getData")
     public void deepHashCode(int arrayLength) throws Exception {
         setUp(arrayLength);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Arrays.deepHashCode(mArray);
         }
@@ -109,7 +110,7 @@
     @Parameters(method = "getData")
     public void deepEquals(int arrayLength) throws Exception {
         setUp(arrayLength);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Arrays.deepEquals(mArray, mArray2);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
index 0bebf04..378137b 100644
--- a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,7 +30,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class FieldAccessPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static class Inner {
         public int mPublicInnerIntVal;
@@ -47,7 +48,7 @@
     @Test
     public void timeField() {
         int result = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = mIntVal;
         }
@@ -56,7 +57,7 @@
     @Test
     public void timeFieldFinal() {
         int result = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = mFinalIntVal;
         }
@@ -65,7 +66,7 @@
     @Test
     public void timeFieldStatic() {
         int result = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = sStaticIntVal;
         }
@@ -74,7 +75,7 @@
     @Test
     public void timeFieldStaticFinal() {
         int result = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = FINAL_INT_VAL;
         }
@@ -84,7 +85,7 @@
     public void timeFieldCached() {
         int result = 0;
         int cachedIntVal = this.mIntVal;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = cachedIntVal;
         }
@@ -94,7 +95,7 @@
     public void timeFieldPrivateInnerClassPublicField() {
         int result = 0;
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = inner.mPublicInnerIntVal;
         }
@@ -104,7 +105,7 @@
     public void timeFieldPrivateInnerClassProtectedField() {
         int result = 0;
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = inner.mProtectedInnerIntVal;
         }
@@ -114,7 +115,7 @@
     public void timeFieldPrivateInnerClassPrivateField() {
         int result = 0;
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = inner.mPrivateInnerIntVal;
         }
@@ -124,7 +125,7 @@
     public void timeFieldPrivateInnerClassPackageField() {
         int result = 0;
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = inner.mPackageInnerIntVal;
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
index 55c1027..610e8e5 100644
--- a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,13 +35,14 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class HashedCollectionsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeHashMapGet() {
         HashMap<String, String> map = new HashMap<String, String>();
         map.put("hello", "world");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.get("hello");
         }
@@ -53,7 +54,7 @@
         synchronized (map) {
             map.put("hello", "world");
         }
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             synchronized (map) {
                 map.get("hello");
@@ -65,7 +66,7 @@
     public void timeHashtableGet() {
         Hashtable<String, String> map = new Hashtable<String, String>();
         map.put("hello", "world");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.get("hello");
         }
@@ -75,7 +76,7 @@
     public void timeLinkedHashMapGet() {
         LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
         map.put("hello", "world");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.get("hello");
         }
@@ -85,7 +86,7 @@
     public void timeConcurrentHashMapGet() {
         ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
         map.put("hello", "world");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.get("hello");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
index da60a77..40c07e0 100644
--- a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,7 +41,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ImtConflictPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Before
     public void setup() {
@@ -280,7 +281,7 @@
     @Test
     public void timeConflictDepth01() {
         C0 c0 = new C0();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c0);
             callF0(c0);
@@ -308,7 +309,7 @@
     @Test
     public void timeConflictDepth02() {
         C1 c1 = new C1();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c1);
             callF19(c1);
@@ -336,7 +337,7 @@
     @Test
     public void timeConflictDepth03() {
         C2 c2 = new C2();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c2);
             callF19(c2);
@@ -364,7 +365,7 @@
     @Test
     public void timeConflictDepth04() {
         C3 c3 = new C3();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c3);
             callF19(c3);
@@ -392,7 +393,7 @@
     @Test
     public void timeConflictDepth05() {
         C4 c4 = new C4();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c4);
             callF19(c4);
@@ -420,7 +421,7 @@
     @Test
     public void timeConflictDepth06() {
         C5 c5 = new C5();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c5);
             callF19(c5);
@@ -448,7 +449,7 @@
     @Test
     public void timeConflictDepth07() {
         C6 c6 = new C6();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c6);
             callF19(c6);
@@ -476,7 +477,7 @@
     @Test
     public void timeConflictDepth08() {
         C7 c7 = new C7();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c7);
             callF19(c7);
@@ -504,7 +505,7 @@
     @Test
     public void timeConflictDepth09() {
         C8 c8 = new C8();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c8);
             callF19(c8);
@@ -532,7 +533,7 @@
     @Test
     public void timeConflictDepth10() {
         C9 c9 = new C9();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c9);
             callF19(c9);
@@ -560,7 +561,7 @@
     @Test
     public void timeConflictDepth11() {
         C10 c10 = new C10();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c10);
             callF19(c10);
@@ -588,7 +589,7 @@
     @Test
     public void timeConflictDepth12() {
         C11 c11 = new C11();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c11);
             callF19(c11);
@@ -616,7 +617,7 @@
     @Test
     public void timeConflictDepth13() {
         C12 c12 = new C12();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c12);
             callF19(c12);
@@ -644,7 +645,7 @@
     @Test
     public void timeConflictDepth14() {
         C13 c13 = new C13();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c13);
             callF19(c13);
@@ -672,7 +673,7 @@
     @Test
     public void timeConflictDepth15() {
         C14 c14 = new C14();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c14);
             callF19(c14);
@@ -700,7 +701,7 @@
     @Test
     public void timeConflictDepth16() {
         C15 c15 = new C15();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c15);
             callF19(c15);
@@ -728,7 +729,7 @@
     @Test
     public void timeConflictDepth17() {
         C16 c16 = new C16();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c16);
             callF19(c16);
@@ -756,7 +757,7 @@
     @Test
     public void timeConflictDepth18() {
         C17 c17 = new C17();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c17);
             callF19(c17);
@@ -784,7 +785,7 @@
     @Test
     public void timeConflictDepth19() {
         C18 c18 = new C18();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c18);
             callF19(c18);
@@ -812,7 +813,7 @@
     @Test
     public void timeConflictDepth20() {
         C19 c19 = new C19();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             callF0(c19);
             callF19(c19);
diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
index 6d9d0c9..e1d0bf2 100644
--- a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,7 +30,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class MethodInvocationPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     interface I {
         void emptyInterface();
@@ -65,12 +66,12 @@
     }
 
     public void timeInternalGetter() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         new C().timeInternalGetter(state);
     }
 
     public void timeInternalFieldAccess() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         new C().timeInternalFieldAccess(state);
     }
 
@@ -78,7 +79,7 @@
     @Test
     public void timeStringLength() {
         int result = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = "hello, world!".length();
         }
@@ -87,7 +88,7 @@
     @Test
     public void timeEmptyStatic() {
         C c = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             c.emptyStatic();
         }
@@ -96,7 +97,7 @@
     @Test
     public void timeEmptyVirtual() {
         C c = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             c.emptyVirtual();
         }
@@ -105,7 +106,7 @@
     @Test
     public void timeEmptyInterface() {
         I c = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             c.emptyInterface();
         }
@@ -138,7 +139,7 @@
     @Test
     public void timePrivateInnerPublicMethod() {
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             inner.publicMethod();
         }
@@ -147,7 +148,7 @@
     @Test
     public void timePrivateInnerProtectedMethod() {
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             inner.protectedMethod();
         }
@@ -156,7 +157,7 @@
     @Test
     public void timePrivateInnerPrivateMethod() {
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             inner.privateMethod();
         }
@@ -165,7 +166,7 @@
     @Test
     public void timePrivateInnerPackageMethod() {
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             inner.packageMethod();
         }
@@ -174,7 +175,7 @@
     @Test
     public void timePrivateInnerFinalPackageMethod() {
         Inner inner = new Inner();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             inner.finalPackageMethod();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
index 09b0977..c5e9d1e 100644
--- a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,12 +30,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class MultiplicationPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeMultiplyIntByConstant10() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 10;
         }
@@ -44,7 +45,7 @@
     @Test
     public void timeMultiplyIntByConstant8() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 8;
         }
@@ -54,7 +55,7 @@
     public void timeMultiplyIntByVariable10() {
         int result = 1;
         int factor = 10;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= factor;
         }
@@ -64,7 +65,7 @@
     public void timeMultiplyIntByVariable8() {
         int result = 1;
         int factor = 8;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= factor;
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
index ba21ed3..d073f91 100644
--- a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,7 +35,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReferenceGetPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     boolean mIntrinsicDisabled;
 
@@ -51,7 +52,7 @@
     @Test
     public void timeSoftReferenceGet() throws Exception {
         Reference soft = new SoftReference(mObj);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Object o = soft.get();
         }
@@ -60,7 +61,7 @@
     @Test
     public void timeWeakReferenceGet() throws Exception {
         Reference weak = new WeakReference(mObj);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Object o = weak.get();
         }
@@ -71,7 +72,7 @@
         Reference weak = new WeakReference(mObj);
         mObj = null;
         Runtime.getRuntime().gc();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Object o = weak.get();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
index 293752e..af13773 100644
--- a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,7 +34,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReferencePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private Object mObject;
 
@@ -42,7 +43,7 @@
     @Test
     public void timeAlloc() {
         ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new PhantomReference(mObject, queue);
         }
@@ -52,7 +53,7 @@
     @Test
     public void timeAllocAndEnqueue() {
         ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             (new PhantomReference<Object>(mObject, queue)).enqueue();
         }
@@ -62,7 +63,7 @@
     @Test
     public void timeAllocEnqueueAndPoll() {
         ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             (new PhantomReference<Object>(mObject, queue)).enqueue();
             queue.poll();
@@ -73,7 +74,7 @@
     @Test
     public void timeAllocEnqueueAndRemove() {
         ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             (new PhantomReference<Object>(mObject, queue)).enqueue();
             try {
@@ -102,7 +103,7 @@
         // Allocate a bunch of finalizable objects.
         int n = 0;
         AtomicInteger count = new AtomicInteger(0);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             n++;
             new FinalizableObject(count);
diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
index 528b751..cf573fa 100644
--- a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,7 +41,9 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SmallBigIntegerPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
+
     // We allocate about 2 1/3 BigIntegers per iteration.
     // Assuming 100 bytes/BigInteger, this gives us around 500MB total.
     static final BigInteger BIG_THREE = BigInteger.valueOf(3);
@@ -51,7 +53,7 @@
     public void testSmallBigInteger() {
         final Random r = new Random();
         BigInteger x = new BigInteger(20, r);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             // We know this converges, but the compiler doesn't.
             if (x.and(BigInteger.ONE).equals(BigInteger.ONE)) {
diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
index 1f301ac..d28154c 100644
--- a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,13 +30,14 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringDexCachePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeStringDexCacheAccess() {
         int v = 0;
         int count = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             // Deliberately obscured to make optimizations less likely.
             String s = (count >= 0) ? "hello, world!" : null;
diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
index 4268325..40a8db0 100644
--- a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,12 +30,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringIterationPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeStringIteration0() {
         String s = "hello, world!";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             char ch;
             for (int i = 0; i < s.length(); ++i) {
@@ -47,7 +48,7 @@
     @Test
     public void timeStringIteration1() {
         String s = "hello, world!";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             char ch;
             for (int i = 0, length = s.length(); i < length; ++i) {
@@ -59,7 +60,7 @@
     @Test
     public void timeStringIteration2() {
         String s = "hello, world!";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             char ch;
             char[] chars = s.toCharArray();
@@ -72,7 +73,7 @@
     @Test
     public void timeStringToCharArray() {
         String s = "hello, world!";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             char[] chars = s.toCharArray();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
index 6363e9c..25e4c43 100644
--- a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -16,7 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 import android.perftests.utils.PerfStatusReporter;
 
 import androidx.test.filters.LargeTest;
@@ -34,7 +35,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class SystemArrayCopyPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -51,7 +53,7 @@
         final int len = arrayLength;
         char[] src = new char[len];
         char[] dst = new char[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -63,7 +65,7 @@
         final int len = arrayLength;
         byte[] src = new byte[len];
         byte[] dst = new byte[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -75,7 +77,7 @@
         final int len = arrayLength;
         short[] src = new short[len];
         short[] dst = new short[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -87,7 +89,7 @@
         final int len = arrayLength;
         int[] src = new int[len];
         int[] dst = new int[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -99,7 +101,7 @@
         final int len = arrayLength;
         long[] src = new long[len];
         long[] dst = new long[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -111,7 +113,7 @@
         final int len = arrayLength;
         float[] src = new float[len];
         float[] dst = new float[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -123,7 +125,7 @@
         final int len = arrayLength;
         double[] src = new double[len];
         double[] dst = new double[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
@@ -135,7 +137,7 @@
         final int len = arrayLength;
         boolean[] src = new boolean[len];
         boolean[] dst = new boolean[len];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             System.arraycopy(src, 0, dst, 0, len);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
index cb3d3ac..147ea50 100644
--- a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -36,12 +36,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VirtualVersusInterfacePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeMapPut() {
         Map<String, String> map = new HashMap<String, String>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.put("hello", "world");
         }
@@ -50,7 +51,7 @@
     @Test
     public void timeHashMapPut() {
         HashMap<String, String> map = new HashMap<String, String>();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             map.put("hello", "world");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
index 5be8ee6..bb1c298 100644
--- a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,7 +36,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class XmlSerializePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private Object[] getParams() {
         return new Object[][] {
@@ -108,7 +109,7 @@
 
     private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor, int seed)
             throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             serializeRandomXml(ctor, seed);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
index a37b89d..9360a25 100644
--- a/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializerPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 import android.util.Xml;
 
 import androidx.test.filters.LargeTest;
@@ -44,11 +44,11 @@
 public class XmlSerializerPerfTest {
 
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeFastSerializer_nonIndent_depth100() throws IOException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             XmlSerializer serializer = Xml.newFastSerializer();
             runTest(serializer, 100);
@@ -57,7 +57,7 @@
 
     @Test
     public void timeFastSerializer_indent_depth100() throws IOException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             XmlSerializer serializer = Xml.newFastSerializer();
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
@@ -67,7 +67,7 @@
 
     @Test
     public void timeKXmlSerializer_nonIndent_depth100() throws IOException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             XmlSerializer serializer = XmlObjectFactory.newXmlSerializer();
             runTest(serializer, 100);
@@ -76,7 +76,7 @@
 
     @Test
     public void timeKXmlSerializer_indent_depth100() throws IOException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             XmlSerializer serializer = XmlObjectFactory.newXmlSerializer();
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
index ed669be..03f183a 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -42,7 +42,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ZipFilePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private File mFile;
 
@@ -65,7 +66,7 @@
     @Parameters(method = "getData")
     public void timeZipFileOpen(int numEntries) throws Exception {
         setUp(numEntries);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ZipFile zf = new ZipFile(mFile);
             state.pauseTiming();
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
index d239a05..3614061 100644
--- a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -44,7 +44,8 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ZipFileReadPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}});
@@ -91,7 +92,7 @@
     @Parameters(method = "getData")
     public void timeZipFileRead(int readBufferSize) throws Exception {
         byte[] readBuffer = new byte[readBufferSize];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ZipFile zipFile = new ZipFile(mFile);
             for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
index 487295c..8890f51 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,7 +35,8 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class AnnotatedElementPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private Class<?> mType;
     private Field mField;
@@ -52,7 +53,7 @@
 
     @Test
     public void timeGetTypeAnnotations() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mType.getAnnotations();
         }
@@ -60,7 +61,7 @@
 
     @Test
     public void timeGetFieldAnnotations() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.getAnnotations();
         }
@@ -68,7 +69,7 @@
 
     @Test
     public void timeGetMethodAnnotations() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mMethod.getAnnotations();
         }
@@ -76,7 +77,7 @@
 
     @Test
     public void timeGetParameterAnnotations() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mMethod.getParameterAnnotations();
         }
@@ -84,7 +85,7 @@
 
     @Test
     public void timeGetTypeAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mType.getAnnotation(Marker.class);
         }
@@ -92,7 +93,7 @@
 
     @Test
     public void timeGetFieldAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.getAnnotation(Marker.class);
         }
@@ -100,7 +101,7 @@
 
     @Test
     public void timeGetMethodAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mMethod.getAnnotation(Marker.class);
         }
@@ -108,7 +109,7 @@
 
     @Test
     public void timeIsTypeAnnotationPresent() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mType.isAnnotationPresent(Marker.class);
         }
@@ -116,7 +117,7 @@
 
     @Test
     public void timeIsFieldAnnotationPresent() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.isAnnotationPresent(Marker.class);
         }
@@ -124,7 +125,7 @@
 
     @Test
     public void timeIsMethodAnnotationPresent() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mMethod.isAnnotationPresent(Marker.class);
         }
@@ -134,7 +135,7 @@
 
     @Test
     public void timeGetAllReturnsLargeAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasLargeAnnotation.class.getAnnotations();
         }
@@ -142,7 +143,7 @@
 
     @Test
     public void timeGetAllReturnsSmallAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasSmallAnnotation.class.getAnnotations();
         }
@@ -150,7 +151,7 @@
 
     @Test
     public void timeGetAllReturnsMarkerAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasMarkerAnnotation.class.getAnnotations();
         }
@@ -158,7 +159,7 @@
 
     @Test
     public void timeGetAllReturnsNoAnnotation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasNoAnnotations.class.getAnnotations();
         }
@@ -166,7 +167,7 @@
 
     @Test
     public void timeGetAllReturnsThreeAnnotations() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasThreeAnnotations.class.getAnnotations();
         }
@@ -176,7 +177,7 @@
 
     @Test
     public void timeGetAnnotationsOnSubclass() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ExtendsHasThreeAnnotations.class.getAnnotations();
         }
@@ -184,7 +185,7 @@
 
     @Test
     public void timeGetDeclaredAnnotationsOnSubclass() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ExtendsHasThreeAnnotations.class.getDeclaredAnnotations();
         }
@@ -194,7 +195,7 @@
 
     @Test
     public void timeGetDeclaredClasses() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             AnnotatedElementPerfTest.class.getDeclaredClasses();
         }
@@ -202,7 +203,7 @@
 
     @Test
     public void timeGetDeclaringClass() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasSmallAnnotation.class.getDeclaringClass();
         }
@@ -211,7 +212,7 @@
     @Test
     public void timeGetEnclosingClass() {
         Object anonymousClass = new Object() {};
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             anonymousClass.getClass().getEnclosingClass();
         }
@@ -220,7 +221,7 @@
     @Test
     public void timeGetEnclosingConstructor() {
         Object anonymousClass = new Object() {};
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             anonymousClass.getClass().getEnclosingConstructor();
         }
@@ -229,7 +230,7 @@
     @Test
     public void timeGetEnclosingMethod() {
         Object anonymousClass = new Object() {};
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             anonymousClass.getClass().getEnclosingMethod();
         }
@@ -237,7 +238,7 @@
 
     @Test
     public void timeGetModifiers() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasSmallAnnotation.class.getModifiers();
         }
@@ -245,7 +246,7 @@
 
     @Test
     public void timeGetSimpleName() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasSmallAnnotation.class.getSimpleName();
         }
@@ -254,7 +255,7 @@
     @Test
     public void timeIsAnonymousClass() {
         Object anonymousClass = new Object() {};
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             anonymousClass.getClass().isAnonymousClass();
         }
@@ -262,7 +263,7 @@
 
     @Test
     public void timeIsLocalClass() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             HasSmallAnnotation.class.isLocalClass();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
index adc5d8c..baab860 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,14 +34,14 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class BidiPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final AttributedCharacterIterator CHAR_ITER =
             DecimalFormat.getInstance().formatToCharacterIterator(new BigDecimal(Math.PI));
 
     @Test
     public void time_createBidiFromIter() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi bidi = new Bidi(CHAR_ITER);
         }
@@ -49,7 +49,7 @@
 
     @Test
     public void time_createBidiFromCharArray() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi bd =
                     new Bidi(
@@ -64,7 +64,7 @@
 
     @Test
     public void time_createBidiFromString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi bidi = new Bidi("Hello", Bidi.DIRECTION_LEFT_TO_RIGHT);
         }
@@ -72,7 +72,7 @@
 
     @Test
     public void time_reorderVisually() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi.reorderVisually(
                     new byte[] {2, 1, 3, 0, 4}, 0, new String[] {"H", "e", "l", "l", "o"}, 0, 5);
@@ -81,7 +81,7 @@
 
     @Test
     public void time_hebrewBidi() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi bd =
                     new Bidi(
@@ -104,7 +104,7 @@
 
     @Test
     public void time_complicatedOverrideBidi() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi bd =
                     new Bidi(
@@ -119,7 +119,7 @@
 
     @Test
     public void time_requiresBidi() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Bidi.requiresBidi("\u05D0".toCharArray(), 1, 1); // false.
             Bidi.requiresBidi("\u05D0".toCharArray(), 0, 1); // true.
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
index 286d703..8a539f8 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,14 +32,14 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class BigIntegerPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeRandomDivision() throws Exception {
         Random r = new Random();
         BigInteger x = new BigInteger(1024, r);
         BigInteger y = new BigInteger(1024, r);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x.divide(y);
         }
@@ -50,7 +50,7 @@
         Random r = new Random();
         BigInteger x = new BigInteger(1024, r);
         BigInteger y = new BigInteger(1024, r);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x.gcd(y);
         }
@@ -61,7 +61,7 @@
         Random r = new Random();
         BigInteger x = new BigInteger(1024, r);
         BigInteger y = new BigInteger(1024, r);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x.multiply(y);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
index d646202..1b46ff4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -35,7 +35,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class BitSetPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{1000}, {10000}});
@@ -45,7 +45,7 @@
     @Parameters(method = "getData")
     public void timeIsEmptyTrue(int size) {
         BitSet bitSet = new BitSet(size);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             if (!bitSet.isEmpty()) throw new RuntimeException();
         }
@@ -56,7 +56,7 @@
     public void timeIsEmptyFalse(int size) {
         BitSet bitSet = new BitSet(size);
         bitSet.set(bitSet.size() - 1);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             if (bitSet.isEmpty()) throw new RuntimeException();
         }
@@ -66,7 +66,7 @@
     @Parameters(method = "getData")
     public void timeGet(int size) {
         BitSet bitSet = new BitSet(size);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         int i = 1;
         while (state.keepRunning()) {
             bitSet.get(++i % size);
@@ -77,7 +77,7 @@
     @Parameters(method = "getData")
     public void timeClear(int size) {
         BitSet bitSet = new BitSet(size);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         int i = 1;
         while (state.keepRunning()) {
             bitSet.clear(++i % size);
@@ -89,7 +89,7 @@
     public void timeSet(int size) {
         BitSet bitSet = new BitSet(size);
         int i = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             bitSet.set(++i % size);
         }
@@ -100,7 +100,7 @@
     public void timeSetOn(int size) {
         BitSet bitSet = new BitSet(size);
         int i = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             bitSet.set(++i % size, true);
         }
@@ -111,7 +111,7 @@
     public void timeSetOff(int size) {
         BitSet bitSet = new BitSet(size);
         int i = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             bitSet.set(++i % size, false);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
index b887f40..3c5e4fd 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,7 +36,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class BreakIteratorPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public enum Text {
         LIPSUM(
@@ -165,7 +165,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeBreakIterator(Text text) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             BreakIterator it = BreakIterator.getLineInstance(text.mLocale);
             it.setText(text.mText);
@@ -179,7 +179,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeIcuBreakIterator(Text text) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             android.icu.text.BreakIterator it =
                     android.icu.text.BreakIterator.getLineInstance(text.mLocale);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
index e4eaf12..6df67bc 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/BulkPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -39,7 +39,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class BulkPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -120,7 +120,7 @@
             throws Exception {
         ByteBuffer src = BulkPerfTest.newBuffer(align, sBuf, size);
         ByteBuffer data = BulkPerfTest.newBuffer(align, dBuf, size);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(align ? 0 : 1);
             data.position(align ? 0 : 1);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
index cb2438e..4cf46e5 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -46,7 +46,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ByteBufferPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public enum MyByteOrder {
         BIG(ByteOrder.BIG_ENDIAN),
@@ -121,7 +121,7 @@
     public void timeByteBuffer_getByte(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -136,7 +136,7 @@
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         byte[] dst = new byte[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(aligned ? 0 : 1);
@@ -150,7 +150,7 @@
     public void timeByteBuffer_getByte_indexed(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -164,7 +164,7 @@
     public void timeByteBuffer_getChar(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -180,7 +180,7 @@
         CharBuffer src =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
         char[] dst = new char[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -194,7 +194,7 @@
     public void timeByteBuffer_getChar_indexed(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -208,7 +208,7 @@
     public void timeByteBuffer_getDouble(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -224,7 +224,7 @@
         DoubleBuffer src =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
         double[] dst = new double[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -238,7 +238,7 @@
     public void timeByteBuffer_getFloat(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -254,7 +254,7 @@
         FloatBuffer src =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
         float[] dst = new float[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -268,7 +268,7 @@
     public void timeByteBuffer_getInt(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -283,7 +283,7 @@
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         IntBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
         int[] dst = new int[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -297,7 +297,7 @@
     public void timeByteBuffer_getLong(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -313,7 +313,7 @@
         LongBuffer src =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
         long[] dst = new long[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -327,7 +327,7 @@
     public void timeByteBuffer_getShort(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -343,7 +343,7 @@
         ShortBuffer src =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
         short[] dst = new short[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 src.position(0);
@@ -361,7 +361,7 @@
     public void timeByteBuffer_putByte(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(0);
             for (int i = 0; i < 1024; ++i) {
@@ -376,7 +376,7 @@
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         byte[] src = new byte[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(aligned ? 0 : 1);
@@ -390,7 +390,7 @@
     public void timeByteBuffer_putChar(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -406,7 +406,7 @@
         CharBuffer dst =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asCharBuffer();
         char[] src = new char[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -420,7 +420,7 @@
     public void timeByteBuffer_putDouble(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -436,7 +436,7 @@
         DoubleBuffer dst =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asDoubleBuffer();
         double[] src = new double[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -450,7 +450,7 @@
     public void timeByteBuffer_putFloat(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -466,7 +466,7 @@
         FloatBuffer dst =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asFloatBuffer();
         float[] src = new float[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -480,7 +480,7 @@
     public void timeByteBuffer_putInt(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -495,7 +495,7 @@
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         IntBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asIntBuffer();
         int[] src = new int[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -509,7 +509,7 @@
     public void timeByteBuffer_putLong(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -525,7 +525,7 @@
         LongBuffer dst =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asLongBuffer();
         long[] src = new long[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -539,7 +539,7 @@
     public void timeByteBuffer_putShort(
             MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             for (int i = 0; i < 1024; ++i) {
@@ -555,7 +555,7 @@
         ShortBuffer dst =
                 ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType).asShortBuffer();
         short[] src = new short[1024];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < 1024; ++i) {
                 dst.position(0);
@@ -567,7 +567,7 @@
     @Test
     @Parameters(method = "getData")
     public void time_new_byteArray() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             byte[] bs = new byte[8192];
         }
@@ -576,7 +576,7 @@
     @Test
     @Parameters(method = "getData")
     public void time_ByteBuffer_allocate() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ByteBuffer bs = ByteBuffer.allocate(8192);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
index 9ee927c..8c318cd 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -35,7 +35,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class ByteBufferScalarVersusVectorPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -112,7 +112,7 @@
             throws Exception {
         ByteBuffer src = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
         ByteBuffer dst = ByteBufferPerfTest.newBuffer(byteOrder, aligned, bufferType);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(0);
             dst.position(0);
@@ -127,7 +127,7 @@
     public void timeByteBufferBulkGet(boolean aligned) throws Exception {
         ByteBuffer src = ByteBuffer.allocate(aligned ? 8192 : 8192 + 1);
         byte[] dst = new byte[8192];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             src.get(dst, 0, dst.length);
@@ -139,7 +139,7 @@
     public void timeDirectByteBufferBulkGet(boolean aligned) throws Exception {
         ByteBuffer src = ByteBuffer.allocateDirect(aligned ? 8192 : 8192 + 1);
         byte[] dst = new byte[8192];
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             src.position(aligned ? 0 : 1);
             src.get(dst, 0, dst.length);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
index e4a4db7..12c1f8c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -38,7 +38,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharacterPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -84,7 +84,7 @@
     public void timeIsSpace(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
         boolean fake = false;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -104,7 +104,7 @@
     @Parameters(method = "getData")
     public void timeDigit(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -124,7 +124,7 @@
     @Parameters(method = "getData")
     public void timeGetNumericValue(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -144,7 +144,7 @@
     @Parameters(method = "getData")
     public void timeIsDigit(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -164,7 +164,7 @@
     @Parameters(method = "getData")
     public void timeIsIdentifierIgnorable(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -184,7 +184,7 @@
     @Parameters(method = "getData")
     public void timeIsJavaIdentifierPart(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -204,7 +204,7 @@
     @Parameters(method = "getData")
     public void timeIsJavaIdentifierStart(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -224,7 +224,7 @@
     @Parameters(method = "getData")
     public void timeIsLetter(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -244,7 +244,7 @@
     @Parameters(method = "getData")
     public void timeIsLetterOrDigit(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -264,7 +264,7 @@
     @Parameters(method = "getData")
     public void timeIsLowerCase(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -284,7 +284,7 @@
     @Parameters(method = "getData")
     public void timeIsSpaceChar(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -304,7 +304,7 @@
     @Parameters(method = "getData")
     public void timeIsUpperCase(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -324,7 +324,7 @@
     @Parameters(method = "getData")
     public void timeIsWhitespace(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -344,7 +344,7 @@
     @Parameters(method = "getData")
     public void timeToLowerCase(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
@@ -364,7 +364,7 @@
     @Parameters(method = "getData")
     public void timeToUpperCase(CharacterSet characterSet, Overload overload) {
         setUp(characterSet);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         if (overload == Overload.CHAR) {
             while (state.keepRunning()) {
                 for (int ch = 0; ch < 65536; ++ch) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
index 858c101..4dd890a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -33,7 +33,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharsetForNamePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static String[] charsetNames() {
         return new String[] {
@@ -52,7 +52,7 @@
     @Test
     @Parameters(method = "charsetNames")
     public void timeCharsetForName(String charsetName) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Charset.forName(charsetName);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
index a2fb7d7..3a71ce9 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,7 +34,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CharsetPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -91,7 +91,7 @@
     @Parameters(method = "getData")
     public void time_new_String_BString(int length, String name) throws Exception {
         byte[] bytes = makeBytes(makeString(length));
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new String(bytes, name);
         }
@@ -101,7 +101,7 @@
     @Parameters(method = "getData")
     public void time_new_String_BII(int length, String name) throws Exception {
         byte[] bytes = makeBytes(makeString(length));
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new String(bytes, 0, bytes.length);
         }
@@ -111,7 +111,7 @@
     @Parameters(method = "getData")
     public void time_new_String_BIIString(int length, String name) throws Exception {
         byte[] bytes = makeBytes(makeString(length));
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new String(bytes, 0, bytes.length, name);
         }
@@ -121,7 +121,7 @@
     @Parameters(method = "getData")
     public void time_String_getBytes(int length, String name) throws Exception {
         String string = makeString(length);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             string.getBytes(name);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
index 2047444..6c30a16 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java
@@ -16,8 +16,8 @@
 package android.libcore.regression;
 
 import android.icu.lang.UCharacter;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,7 +35,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class CharsetUtf8PerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private void makeUnicodeRange(int startingCodePoint, int endingCodePoint) {
         StringBuilder builder = new StringBuilder();
@@ -46,7 +46,7 @@
         }
 
         String str = builder.toString();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder builder2 = new StringBuilder();
             builder2.append(str);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
index 4ce8b41..dcdfd37 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,13 +32,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ChecksumPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeAdler_block() throws Exception {
         byte[] bytes = new byte[10000];
         Adler32 adler = new Adler32();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             adler.update(bytes);
         }
@@ -47,7 +47,7 @@
     @Test
     public void timeAdler_byte() throws Exception {
         Adler32 adler = new Adler32();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             adler.update(1);
         }
@@ -57,7 +57,7 @@
     public void timeCrc_block() throws Exception {
         byte[] bytes = new byte[10000];
         CRC32 crc = new CRC32();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             crc.update(bytes);
         }
@@ -66,7 +66,7 @@
     @Test
     public void timeCrc_byte() throws Exception {
         CRC32 crc = new CRC32();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             crc.update(1);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
index 6a7ec1a..6c175b1 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,7 +41,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class CipherInputStreamPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final int DATA_SIZE = 1024 * 1024;
     private static final byte[] DATA = new byte[DATA_SIZE];
@@ -80,7 +80,7 @@
 
     @Test
     public void timeEncrypt() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mCipherEncrypt.init(Cipher.ENCRYPT_MODE, mKey, mSpec);
             InputStream is = new CipherInputStream(new ByteArrayInputStream(DATA), mCipherEncrypt);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
index 238c028..136822e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -47,7 +47,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CipherPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection getCases() {
         int[] keySizes = new int[] {128, 192, 256};
@@ -180,7 +180,7 @@
             Mode mode, Padding padding, int keySize, int inputSize, Implementation implementation)
             throws Exception {
         setUp(mode, padding, keySize, implementation);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mCipherEncrypt.doFinal(DATA, 0, inputSize, mOutput);
         }
@@ -192,7 +192,7 @@
             Mode mode, Padding padding, int keySize, int inputSize, Implementation implementation)
             throws Exception {
         setUp(mode, padding, keySize, implementation);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mCipherDecrypt.doFinal(DATA, 0, inputSize, mOutput);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
index 7e55660..9efb7ce 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,7 +33,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class CollatorPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final RuleBasedCollator COLLATOR =
             (RuleBasedCollator) Collator.getInstance(Locale.US);
@@ -41,7 +41,7 @@
     @Test
     public void timeCollatorPrimary() {
         COLLATOR.setStrength(Collator.PRIMARY);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             COLLATOR.compare("abcde", "abcdf");
             COLLATOR.compare("abcde", "abcde");
@@ -52,7 +52,7 @@
     @Test
     public void timeCollatorSecondary() {
         COLLATOR.setStrength(Collator.SECONDARY);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             COLLATOR.compare("abcdÂ", "abcdÄ");
             COLLATOR.compare("abcdÂ", "abcdÂ");
@@ -63,7 +63,7 @@
     @Test
     public void timeCollatorTertiary() {
         COLLATOR.setStrength(Collator.TERTIARY);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             COLLATOR.compare("abcdE", "abcde");
             COLLATOR.compare("abcde", "abcde");
@@ -74,7 +74,7 @@
     @Test
     public void timeCollatorIdentical() {
         COLLATOR.setStrength(Collator.IDENTICAL);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             COLLATOR.compare("abcdȪ", "abcdȫ");
             COLLATOR.compare("abcdȪ", "abcdȪ");
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
index 100798a..4e5ceaf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -40,7 +40,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class CollectionsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{4}, {16}, {64}, {256}, {1024}});
@@ -60,7 +60,7 @@
     @Parameters(method = "getData")
     public void timeSort_arrayList(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, ArrayList.class);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Collections.sort(input);
         }
@@ -70,7 +70,7 @@
     @Parameters(method = "getData")
     public void timeSortWithComparator_arrayList(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, ArrayList.class);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Collections.sort(input, REVERSE);
         }
@@ -80,7 +80,7 @@
     @Parameters(method = "getData")
     public void timeSort_vector(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, Vector.class);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Collections.sort(input);
         }
@@ -90,7 +90,7 @@
     @Parameters(method = "getData")
     public void timeSortWithComparator_vector(int arrayListLength) throws Exception {
         List<Integer> input = buildList(arrayListLength, Vector.class);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Collections.sort(input, REVERSE);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
index b6784a8..b0ccd99 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DateFormatPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,7 +33,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public final class DateFormatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private Locale mLocale1;
     private Locale mLocale2;
@@ -50,7 +50,7 @@
 
     @Test
     public void timeGetDateTimeInstance() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DateFormat.getDateTimeInstance();
         }
@@ -58,7 +58,7 @@
 
     @Test
     public void timeGetDateTimeInstance_multiple() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, mLocale1);
             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, mLocale2);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
index 52f9873..3a2f6fa 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DecimalFormatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final String EXP_PATTERN = "##E0";
 
@@ -58,7 +58,7 @@
     public void formatWithGrouping(Object obj) {
         DF.setGroupingSize(3);
         DF.setGroupingUsed(true);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DF.format(obj);
         }
@@ -66,21 +66,21 @@
 
     public void format(String pattern, Object obj) {
         PATTERN_INSTANCE.applyPattern(pattern);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             PATTERN_INSTANCE.format(obj);
         }
     }
 
     public void format(Object obj) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DF.format(obj);
         }
     }
 
     public void formatToCharacterIterator(Object obj) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DF.formatToCharacterIterator(obj);
         }
@@ -88,14 +88,14 @@
 
 
     public void formatCurrencyUS(Object obj) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DF_CURRENCY_US.format(obj);
         }
     }
 
     public void formatCurrencyFR(Object obj) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DF_CURRENCY_FR.format(obj);
         }
@@ -213,7 +213,7 @@
 
     @Test
     public void time_instantiation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new DecimalFormat();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
index 6105420..4bc550e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DecimalFormatSymbolsPerfTest.java
@@ -15,8 +15,8 @@
  */
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,13 +31,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DecimalFormatSymbolsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static Locale sLocale = Locale.getDefault(Locale.Category.FORMAT);
 
     @Test
     public void time_instantiation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new DecimalFormatSymbols(sLocale);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
index fae74a5..597447b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DefaultCharsetPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DefaultCharsetPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void time_defaultCharset() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Charset.defaultCharset();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
index 2915363..b17d0f4 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DnsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,7 +32,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DnsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeDns() throws Exception {
@@ -53,7 +53,7 @@
                 "www.cnn.com",
                 "bad.host.mtv.corp.google.com",
         };
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         int i = 0;
         while (state.keepRunning()) {
             try {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
index dd7e5cc..4c8a8ea 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DoPrivilegedPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,11 +32,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DoPrivilegedPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeDirect() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String lineSeparator = System.getProperty("line.separator");
         }
@@ -44,7 +44,7 @@
 
     @Test
     public void timeFastAndSlow() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String lineSeparator;
             if (System.getSecurityManager() == null) {
@@ -61,7 +61,7 @@
 
     @Test
     public void timeNewAction() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() {
                 public String run() {
@@ -74,7 +74,7 @@
     @Test
     public void timeReusedAction() throws Exception {
         final PrivilegedAction<String> action = new ReusableAction("line.separator");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String lineSeparator = AccessController.doPrivileged(action);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
index e034a47..4ff65b1 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/DoublePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class DoublePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private double mD = 1.2;
     private long mL = 4608083138725491507L;
@@ -37,7 +37,7 @@
     @Test
     public void timeDoubleToLongBits() {
         long result = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Double.doubleToLongBits(mD);
         }
@@ -49,7 +49,7 @@
     @Test
     public void timeDoubleToRawLongBits() {
         long result = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Double.doubleToRawLongBits(mD);
         }
@@ -61,7 +61,7 @@
     @Test
     public void timeLongBitsToDouble() {
         double result = 123.0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Double.longBitsToDouble(mL);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
index fe1b599..aacdcee1 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/EqualsHashCodePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -37,7 +37,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class EqualsHashCodePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private enum Type {
         URI() {
@@ -82,7 +82,7 @@
         mA2 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
         mB1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
         mB2 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mA1.equals(mB1);
             mA1.equals(mA2);
@@ -95,7 +95,7 @@
     public void timeHashCode(Type type) throws Exception {
         mA1 = type.newInstance("https://mail.google.com/mail/u/0/?shva=1#inbox");
         mB1 = type.newInstance("http://developer.android.com/reference/java/net/URI.html");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mA1.hashCode();
             mB1.hashCode();
@@ -112,7 +112,7 @@
                         "http://developer.android.com/query?q="
                                 + QUERY.substring(0, QUERY.length() - 3)
                                 + "%AF");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mC1.equals(mC2);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
index ecbfc71..9a6864e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -41,11 +41,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ExpensiveObjectsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeNewDateFormatTimeInstance() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
             df.format(System.currentTimeMillis());
@@ -55,7 +55,7 @@
     @Test(timeout = 900000)
     public void timeClonedDateFormatTimeInstance() {
         DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ((DateFormat) df.clone()).format(System.currentTimeMillis());
         }
@@ -64,7 +64,7 @@
     @Test
     public void timeReusedDateFormatTimeInstance() {
         DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             synchronized (df) {
                 df.format(System.currentTimeMillis());
@@ -74,7 +74,7 @@
 
     @Test(timeout = 900000)
     public void timeNewCollator() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Collator.getInstance(Locale.US);
         }
@@ -83,7 +83,7 @@
     @Test
     public void timeClonedCollator() {
         Collator c = Collator.getInstance(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             c.clone();
         }
@@ -91,7 +91,7 @@
 
     @Test
     public void timeNewDateFormatSymbols() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new DateFormatSymbols(Locale.US);
         }
@@ -100,7 +100,7 @@
     @Test
     public void timeClonedDateFormatSymbols() {
         DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             dfs.clone();
         }
@@ -108,7 +108,7 @@
 
     @Test
     public void timeNewDecimalFormatSymbols() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new DecimalFormatSymbols(Locale.US);
         }
@@ -117,7 +117,7 @@
     @Test
     public void timeClonedDecimalFormatSymbols() {
         DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             dfs.clone();
         }
@@ -125,7 +125,7 @@
 
     @Test
     public void timeNewNumberFormat() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             NumberFormat.getInstance(Locale.US);
         }
@@ -134,7 +134,7 @@
     @Test
     public void timeClonedNumberFormat() {
         NumberFormat nf = NumberFormat.getInstance(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             nf.clone();
         }
@@ -142,7 +142,7 @@
 
     @Test
     public void timeLongToString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Long.toString(1024L);
         }
@@ -151,7 +151,7 @@
     @Test
     public void timeNumberFormatTrivialFormatDouble() {
         NumberFormat nf = NumberFormat.getInstance(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             nf.format(1024.0);
         }
@@ -159,7 +159,7 @@
 
     @Test
     public void timeNewSimpleDateFormat() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new SimpleDateFormat();
         }
@@ -167,7 +167,7 @@
 
     @Test
     public void timeNewGregorianCalendar() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new GregorianCalendar();
         }
@@ -176,7 +176,7 @@
     @Test
     public void timeClonedGregorianCalendar() {
         GregorianCalendar gc = new GregorianCalendar();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             gc.clone();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
index 0c14d64..cef7e8c7 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FilePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public final class FilePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeFileCreationWithEmptyChild() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new File("/foo", "/");
         }
@@ -43,7 +43,7 @@
 
     @Test
     public void timeFileCreationWithNormalizationNecessary() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new File("/foo//bar//baz//bag", "/baz/");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
index 7d7d83b..645c023 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FloatPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class FloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private float mFloat = 1.2f;
     private int mInt = 1067030938;
@@ -37,7 +37,7 @@
     @Test
     public void timeFloatToIntBits() {
         int result = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Float.floatToIntBits(mFloat);
         }
@@ -49,7 +49,7 @@
     @Test
     public void timeFloatToRawIntBits() {
         int result = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Float.floatToRawIntBits(mFloat);
         }
@@ -61,7 +61,7 @@
     @Test
     public void timeIntBitsToFloat() {
         float result = 123.0f;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Float.intBitsToFloat(mInt);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
index 08dda53..cf76137 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/FormatterPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,11 +35,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class FormatterPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeFormatter_NoFormatting() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that doesn't actually need any formatting");
@@ -48,7 +48,7 @@
 
     @Test
     public void timeStringBuilder_NoFormatting() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             sb.append("this is a reasonably short string that doesn't actually need formatting");
@@ -58,7 +58,7 @@
     @Test
     public void timeFormatter_OneInt() {
         Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that has an int %d in it", value);
@@ -69,7 +69,7 @@
     public void timeFormatter_OneIntArabic() {
         Locale arabic = new Locale("ar");
         Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format(arabic, "this is a reasonably short string that has an int %d in it", value);
@@ -78,7 +78,7 @@
 
     @Test
     public void timeStringBuilder_OneInt() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             sb.append("this is a reasonably short string that has an int ");
@@ -90,7 +90,7 @@
     @Test
     public void timeFormatter_OneHexInt() {
         Integer value = Integer.valueOf(1024); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that has an int %x in it", value);
@@ -99,7 +99,7 @@
 
     @Test
     public void timeStringBuilder_OneHexInt() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             sb.append("this is a reasonably short string that has an int ");
@@ -111,7 +111,7 @@
     @Test
     public void timeFormatter_OneFloat() {
         Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that has a float %f in it", value);
@@ -121,7 +121,7 @@
     @Test
     public void timeFormatter_OneFloat_dot2f() {
         Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that has a float %.2f in it", value);
@@ -131,7 +131,7 @@
     @Test
     public void timeFormatter_TwoFloats() {
         Float value = Float.valueOf(10.24f); // We're not trying to benchmark boxing here.
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a short string that has two floats %f and %f in it", value, value);
@@ -140,7 +140,7 @@
 
     @Test
     public void timeStringBuilder_OneFloat() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             sb.append("this is a reasonably short string that has a float ");
@@ -151,7 +151,7 @@
 
     @Test
     public void timeFormatter_OneString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Formatter f = new Formatter();
             f.format("this is a reasonably short string that has a string %s in it", "hello");
@@ -160,7 +160,7 @@
 
     @Test
     public void timeStringBuilder_OneString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             sb.append("this is a reasonably short string that has a string ");
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
index a09ad80..833575a 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IdnPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class IdnPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeToUnicode() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             IDN.toASCII("fass.de");
             IDN.toASCII("faß.de");
@@ -51,7 +51,7 @@
 
     @Test
     public void timeToAscii() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             IDN.toUnicode("xn--fss-qla.de");
             IDN.toUnicode("xn--n00d.com");
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
index be22814..1c901c8 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantDivisionPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +29,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class IntConstantDivisionPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeDivideIntByConstant2() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= 2;
         }
@@ -43,7 +43,7 @@
     @Test
     public void timeDivideIntByConstant8() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= 8;
         }
@@ -52,7 +52,7 @@
     @Test
     public void timeDivideIntByConstant10() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= 10;
         }
@@ -61,7 +61,7 @@
     @Test
     public void timeDivideIntByConstant100() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= 100;
         }
@@ -70,7 +70,7 @@
     @Test
     public void timeDivideIntByConstant100_HandOptimized() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = (int) ((0x51eb851fL * result) >>> 37);
         }
@@ -79,7 +79,7 @@
     @Test
     public void timeDivideIntByConstant2048() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= 2048;
         }
@@ -89,7 +89,7 @@
     public void timeDivideIntByVariable2() {
         int result = 1;
         int factor = 2;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= factor;
         }
@@ -99,7 +99,7 @@
     public void timeDivideIntByVariable10() {
         int result = 1;
         int factor = 10;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result /= factor;
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
index 4337c90..3d3af4c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantMultiplicationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +29,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class IntConstantMultiplicationPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeMultiplyIntByConstant6() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 6;
         }
@@ -43,7 +43,7 @@
     @Test
     public void timeMultiplyIntByConstant7() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 7;
         }
@@ -52,7 +52,7 @@
     @Test
     public void timeMultiplyIntByConstant8() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 8;
         }
@@ -61,7 +61,7 @@
     @Test
     public void timeMultiplyIntByConstant8_Shift() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result <<= 3;
         }
@@ -70,7 +70,7 @@
     @Test
     public void timeMultiplyIntByConstant10() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 10;
         }
@@ -79,7 +79,7 @@
     @Test
     public void timeMultiplyIntByConstant10_Shift() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = (result + (result << 2)) << 1;
         }
@@ -88,7 +88,7 @@
     @Test
     public void timeMultiplyIntByConstant2047() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 2047;
         }
@@ -97,7 +97,7 @@
     @Test
     public void timeMultiplyIntByConstant2048() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 2048;
         }
@@ -106,7 +106,7 @@
     @Test
     public void timeMultiplyIntByConstant2049() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= 2049;
         }
@@ -116,7 +116,7 @@
     public void timeMultiplyIntByVariable10() {
         int result = 1;
         int factor = 10;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= factor;
         }
@@ -126,7 +126,7 @@
     public void timeMultiplyIntByVariable8() {
         int result = 1;
         int factor = 8;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result *= factor;
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
index 1b6c502..7c86633 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntConstantRemainderPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +29,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class IntConstantRemainderPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeRemainderIntByConstant2() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= 2;
         }
@@ -43,7 +43,7 @@
     @Test
     public void timeRemainderIntByConstant8() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= 8;
         }
@@ -52,7 +52,7 @@
     @Test
     public void timeRemainderIntByConstant10() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= 10;
         }
@@ -61,7 +61,7 @@
     @Test
     public void timeRemainderIntByConstant100() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= 100;
         }
@@ -70,7 +70,7 @@
     @Test
     public void timeRemainderIntByConstant2048() {
         int result = 1;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= 2048;
         }
@@ -80,7 +80,7 @@
     public void timeRemainderIntByVariable2() {
         int result = 1;
         int factor = 2;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= factor;
         }
@@ -90,7 +90,7 @@
     public void timeRemainderIntByVariable10() {
         int result = 1;
         int factor = 10;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result %= factor;
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntegerPerfTest.java
index 170bb58..e2a9dcc 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntegerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntegerPerfTest.java
@@ -16,20 +16,20 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import org.junit.Rule;
 import org.junit.Test;
 
 public class IntegerPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeLongSignumBranch() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += signum1(-(++i));
             t += signum1(0);
@@ -41,7 +41,7 @@
     public void timeLongSignumBranchFree() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += signum2(-(++i));
             t += signum2(0);
@@ -61,7 +61,7 @@
     public void timeLongBitCount_BitSet() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += pop((long) ++i);
         }
@@ -89,7 +89,7 @@
     public void timeLongBitCount_2Int() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += pop2((long) ++i);
         }
@@ -105,7 +105,7 @@
     public void timeLongBitCount_Long() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += Long.bitCount((long) ++i);
         }
@@ -140,7 +140,7 @@
     public void timeNumberOfTrailingZerosHD() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += numberOfTrailingZerosHD(++i);
         }
@@ -150,7 +150,7 @@
     public void timeNumberOfTrailingZerosOL() {
         int t = 0;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             t += numberOfTrailingZerosOL(++i);
         }
@@ -163,7 +163,7 @@
                     "0", "1", "12", "123", "1234", "12345", "123456", "1234567", "12345678"
                 };
         int t = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int j = 0; j < intStrings.length; ++j) {
                 t += Integer.valueOf(intStrings[j]);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
index 0aa854e..669bfbf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/IntegralToStringPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class IntegralToStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final int SMALL = 12;
     private static final int MEDIUM = 12345;
@@ -37,7 +37,7 @@
 
     @Test
     public void time_IntegerToString_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(SMALL);
         }
@@ -45,7 +45,7 @@
 
     @Test
     public void time_IntegerToString_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(MEDIUM);
         }
@@ -53,7 +53,7 @@
 
     @Test
     public void time_IntegerToString_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(LARGE);
         }
@@ -61,7 +61,7 @@
 
     @Test
     public void time_IntegerToString2_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(SMALL, 2);
         }
@@ -69,7 +69,7 @@
 
     @Test
     public void time_IntegerToString2_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(MEDIUM, 2);
         }
@@ -77,7 +77,7 @@
 
     @Test
     public void time_IntegerToString2_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(LARGE, 2);
         }
@@ -85,7 +85,7 @@
 
     @Test
     public void time_IntegerToString10_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(SMALL, 10);
         }
@@ -93,7 +93,7 @@
 
     @Test
     public void time_IntegerToString10_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(MEDIUM, 10);
         }
@@ -101,7 +101,7 @@
 
     @Test
     public void time_IntegerToString10_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(LARGE, 10);
         }
@@ -109,7 +109,7 @@
 
     @Test
     public void time_IntegerToString16_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(SMALL, 16);
         }
@@ -117,7 +117,7 @@
 
     @Test
     public void time_IntegerToString16_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(MEDIUM, 16);
         }
@@ -125,7 +125,7 @@
 
     @Test
     public void time_IntegerToString16_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toString(LARGE, 16);
         }
@@ -133,7 +133,7 @@
 
     @Test
     public void time_IntegerToBinaryString_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toBinaryString(SMALL);
         }
@@ -141,7 +141,7 @@
 
     @Test
     public void time_IntegerToBinaryString_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toBinaryString(MEDIUM);
         }
@@ -149,7 +149,7 @@
 
     @Test
     public void time_IntegerToBinaryString_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toBinaryString(LARGE);
         }
@@ -157,7 +157,7 @@
 
     @Test
     public void time_IntegerToHexString_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toHexString(SMALL);
         }
@@ -165,7 +165,7 @@
 
     @Test
     public void time_IntegerToHexString_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toHexString(MEDIUM);
         }
@@ -173,7 +173,7 @@
 
     @Test
     public void time_IntegerToHexString_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Integer.toHexString(LARGE);
         }
@@ -181,7 +181,7 @@
 
     @Test
     public void time_StringBuilder_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(SMALL);
         }
@@ -189,7 +189,7 @@
 
     @Test
     public void time_StringBuilder_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(MEDIUM);
         }
@@ -197,7 +197,7 @@
 
     @Test
     public void time_StringBuilder_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(LARGE);
         }
@@ -205,7 +205,7 @@
 
     @Test
     public void time_Formatter_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%d", SMALL);
         }
@@ -213,7 +213,7 @@
 
     @Test
     public void time_Formatter_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%d", MEDIUM);
         }
@@ -221,7 +221,7 @@
 
     @Test
     public void time_Formatter_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%d", LARGE);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
index 9b3d7a0..cda8512 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/KeyPairGeneratorPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,7 +36,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class KeyPairGeneratorPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -78,7 +78,7 @@
     @Parameters(method = "getData")
     public void time(Algorithm algorithm, Implementation implementation) throws Exception {
         setUp(algorithm, implementation);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             KeyPair keyPair = mGenerator.generateKeyPair();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
index 1a9e19a..8b062d3 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/LoopingBackwardsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -39,7 +39,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class LoopingBackwardsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(new Object[][] {{2}, {20}, {2000}, {20000000}});
@@ -49,7 +49,7 @@
     @Parameters(method = "getData")
     public void timeForwards(int max) {
         int fake = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int j = 0; j < max; j++) {
                 fake += j;
@@ -61,7 +61,7 @@
     @Parameters(method = "getData")
     public void timeBackwards(int max) {
         int fake = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int j = max - 1; j >= 0; j--) {
                 fake += j;
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
index a8a704c..bcf556c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MathPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,7 +33,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class MathPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private final double mDouble = 1.2;
     private final float mFloat = 1.2f;
@@ -48,7 +48,7 @@
     @Test
     public void timeAbsD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.abs(mDouble);
         }
@@ -57,7 +57,7 @@
     @Test
     public void timeAbsF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.abs(mFloat);
         }
@@ -66,7 +66,7 @@
     @Test
     public void timeAbsI() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.abs(mInt);
         }
@@ -75,7 +75,7 @@
     @Test
     public void timeAbsL() {
         long result = mLong;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.abs(mLong);
         }
@@ -84,7 +84,7 @@
     @Test
     public void timeAcos() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.acos(mDouble);
         }
@@ -93,7 +93,7 @@
     @Test
     public void timeAsin() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.asin(mDouble);
         }
@@ -102,7 +102,7 @@
     @Test
     public void timeAtan() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.atan(mDouble);
         }
@@ -111,7 +111,7 @@
     @Test
     public void timeAtan2() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.atan2(3, 4);
         }
@@ -120,7 +120,7 @@
     @Test
     public void timeCbrt() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.cbrt(mDouble);
         }
@@ -129,7 +129,7 @@
     @Test
     public void timeCeil() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.ceil(mDouble);
         }
@@ -138,7 +138,7 @@
     @Test
     public void timeCopySignD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.copySign(mDouble, mDouble);
         }
@@ -147,7 +147,7 @@
     @Test
     public void timeCopySignF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.copySign(mFloat, mFloat);
         }
@@ -156,7 +156,7 @@
     @Test
     public void timeCopySignD_strict() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = StrictMath.copySign(mDouble, mDouble);
         }
@@ -165,7 +165,7 @@
     @Test
     public void timeCopySignF_strict() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = StrictMath.copySign(mFloat, mFloat);
         }
@@ -174,7 +174,7 @@
     @Test
     public void timeCos() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.cos(mDouble);
         }
@@ -183,7 +183,7 @@
     @Test
     public void timeCosh() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.cosh(mDouble);
         }
@@ -192,7 +192,7 @@
     @Test
     public void timeExp() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.exp(mDouble);
         }
@@ -201,7 +201,7 @@
     @Test
     public void timeExpm1() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.expm1(mDouble);
         }
@@ -210,7 +210,7 @@
     @Test
     public void timeFloor() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.floor(mDouble);
         }
@@ -219,7 +219,7 @@
     @Test
     public void timeGetExponentD() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.getExponent(mDouble);
         }
@@ -228,7 +228,7 @@
     @Test
     public void timeGetExponentF() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.getExponent(mFloat);
         }
@@ -237,7 +237,7 @@
     @Test
     public void timeHypot() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.hypot(mDouble, mDouble);
         }
@@ -246,7 +246,7 @@
     @Test
     public void timeIEEEremainder() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.IEEEremainder(mDouble, mDouble);
         }
@@ -255,7 +255,7 @@
     @Test
     public void timeLog() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.log(mDouble);
         }
@@ -264,7 +264,7 @@
     @Test
     public void timeLog10() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.log10(mDouble);
         }
@@ -273,7 +273,7 @@
     @Test
     public void timeLog1p() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.log1p(mDouble);
         }
@@ -282,7 +282,7 @@
     @Test
     public void timeMaxD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.max(mDouble, mDouble);
         }
@@ -291,7 +291,7 @@
     @Test
     public void timeMaxF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.max(mFloat, mFloat);
         }
@@ -300,7 +300,7 @@
     @Test
     public void timeMaxI() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.max(mInt, mInt);
         }
@@ -309,7 +309,7 @@
     @Test
     public void timeMaxL() {
         long result = mLong;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.max(mLong, mLong);
         }
@@ -318,7 +318,7 @@
     @Test
     public void timeMinD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.min(mDouble, mDouble);
         }
@@ -327,7 +327,7 @@
     @Test
     public void timeMinF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.min(mFloat, mFloat);
         }
@@ -336,7 +336,7 @@
     @Test
     public void timeMinI() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.min(mInt, mInt);
         }
@@ -345,7 +345,7 @@
     @Test
     public void timeMinL() {
         long result = mLong;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.min(mLong, mLong);
         }
@@ -354,7 +354,7 @@
     @Test
     public void timeNextAfterD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.nextAfter(mDouble, mDouble);
         }
@@ -363,7 +363,7 @@
     @Test
     public void timeNextAfterF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.nextAfter(mFloat, mFloat);
         }
@@ -372,7 +372,7 @@
     @Test
     public void timeNextUpD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.nextUp(mDouble);
         }
@@ -381,7 +381,7 @@
     @Test
     public void timeNextUpF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.nextUp(mFloat);
         }
@@ -390,7 +390,7 @@
     @Test
     public void timePow() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.pow(mDouble, mDouble);
         }
@@ -399,7 +399,7 @@
     @Test
     public void timeRandom() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.random();
         }
@@ -408,7 +408,7 @@
     @Test
     public void timeRint() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.rint(mDouble);
         }
@@ -417,7 +417,7 @@
     @Test
     public void timeRoundD() {
         long result = mLong;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.round(mDouble);
         }
@@ -426,7 +426,7 @@
     @Test
     public void timeRoundF() {
         int result = mInt;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.round(mFloat);
         }
@@ -435,7 +435,7 @@
     @Test
     public void timeScalbD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.scalb(mDouble, 5);
         }
@@ -444,7 +444,7 @@
     @Test
     public void timeScalbF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.scalb(mFloat, 5);
         }
@@ -453,7 +453,7 @@
     @Test
     public void timeSignumD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.signum(mDouble);
         }
@@ -462,7 +462,7 @@
     @Test
     public void timeSignumF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.signum(mFloat);
         }
@@ -471,7 +471,7 @@
     @Test
     public void timeSin() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.sin(mDouble);
         }
@@ -480,7 +480,7 @@
     @Test
     public void timeSinh() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.sinh(mDouble);
         }
@@ -489,7 +489,7 @@
     @Test
     public void timeSqrt() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.sqrt(mDouble);
         }
@@ -498,7 +498,7 @@
     @Test
     public void timeTan() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.tan(mDouble);
         }
@@ -507,7 +507,7 @@
     @Test
     public void timeTanh() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.tanh(mDouble);
         }
@@ -516,7 +516,7 @@
     @Test
     public void timeToDegrees() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.toDegrees(mDouble);
         }
@@ -525,7 +525,7 @@
     @Test
     public void timeToRadians() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.toRadians(mDouble);
         }
@@ -534,7 +534,7 @@
     @Test
     public void timeUlpD() {
         double result = mDouble;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.ulp(mDouble);
         }
@@ -543,7 +543,7 @@
     @Test
     public void timeUlpF() {
         float result = mFloat;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result = Math.ulp(mFloat);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
index 6da9666..8325dae 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MessageDigestPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -36,7 +36,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class MessageDigestPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -97,7 +97,7 @@
     @Test
     @Parameters(method = "getData")
     public void time(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(DATA, 0, DATA_SIZE);
@@ -108,7 +108,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeLargeArray(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(LARGE_DATA, 0, LARGE_DATA_SIZE);
@@ -119,7 +119,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSmallChunkOfLargeArray(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             digest.update(LARGE_DATA, LARGE_DATA_SIZE / 2, DATA_SIZE);
@@ -130,7 +130,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSmallByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             SMALL_BUFFER.position(0);
@@ -143,7 +143,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSmallDirectByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             SMALL_DIRECT_BUFFER.position(0);
@@ -156,7 +156,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeLargeByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_BUFFER.position(0);
@@ -169,7 +169,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeLargeDirectByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_DIRECT_BUFFER.position(0);
@@ -182,7 +182,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSmallChunkOfLargeByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_BUFFER.position(LARGE_BUFFER.capacity() / 2);
@@ -195,7 +195,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSmallChunkOfLargeDirectByteBuffer(Algorithm algorithm) throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             MessageDigest digest = MessageDigest.getInstance(algorithm.toString(), mProvider);
             LARGE_DIRECT_BUFFER.position(LARGE_DIRECT_BUFFER.capacity() / 2);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
index 060d18f..266d42c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/MutableIntPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -35,7 +35,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class MutableIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     enum Kind {
         ARRAY() {
@@ -105,21 +105,21 @@
     @Test
     @Parameters(method = "getData")
     public void timeCreate(Kind kind) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         kind.timeCreate(state);
     }
 
     @Test
     @Parameters(method = "getData")
     public void timeIncrement(Kind kind) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         kind.timeIncrement(state);
     }
 
     @Test
     @Parameters(method = "getData")
     public void timeGet(Kind kind) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         kind.timeGet(state);
     }
 }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
index 7cb3b22..c2f84fb 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,13 +32,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class NumberFormatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static Locale sLocale = Locale.getDefault(Locale.Category.FORMAT);
 
     @Test
     public void time_instantiation() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             NumberFormat.getInstance(sLocale);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
index 272b45a..cdf0911 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/NumberFormatTrivialFormatLongPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -36,12 +36,12 @@
 @LargeTest
 public class NumberFormatTrivialFormatLongPerfTest {
     @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeNumberFormatTrivialFormatLong() {
         NumberFormat nf = NumberFormat.getInstance(Locale.US);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             nf.format(1024L);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
index c3a0966..51f47bb 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/PriorityQueuePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -39,7 +39,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class PriorityQueuePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -108,7 +108,7 @@
         // At most allow the queue to empty 10%.
         int resizingThreshold = queueSize / 10;
         int i = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             // Reset queue every so often. This will be called more often for smaller
             // queueSizes, but since a copy is linear, it will also cost proportionally
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
index 2ac56be..1f20cae 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/PropertyAccessPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,7 +33,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public final class PropertyAccessPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private View mView = new View();
     private Method mSetX;
@@ -50,7 +50,7 @@
 
     @Test
     public void timeDirectSetter() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mView.mSetX(0.1f);
         }
@@ -58,7 +58,7 @@
 
     @Test
     public void timeDirectFieldSet() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mView.mX = 0.1f;
         }
@@ -66,7 +66,7 @@
 
     @Test
     public void timeDirectSetterAndBomXing() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float value = 0.1f;
             mView.mSetX(value);
@@ -75,7 +75,7 @@
 
     @Test
     public void timeDirectFieldSetAndBomXing() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float value = 0.1f;
             mView.mX = value;
@@ -84,7 +84,7 @@
 
     @Test
     public void timeReflectionSetterAndTwoBomXes() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mSetX.invoke(mView, 0.1f);
         }
@@ -92,7 +92,7 @@
 
     @Test
     public void timeReflectionSetterAndOneBomX() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mArgsBomX[0] = 0.1f;
             mSetX.invoke(mView, mArgsBomX);
@@ -101,7 +101,7 @@
 
     @Test
     public void timeReflectionFieldSet() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mX.setFloat(mView, 0.1f);
         }
@@ -109,7 +109,7 @@
 
     @Test
     public void timeGeneratedSetter() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mGeneratedSetter.setFloat(mView, 0.1f);
         }
@@ -117,7 +117,7 @@
 
     @Test
     public void timeGeneratedFieldSet() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mGeneratedField.setFloat(mView, 0.1f);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
index 7ad0141..0c16265 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ProviderPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -34,11 +34,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ProviderPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeStableProviders() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Cipher c = Cipher.getInstance("RSA");
         }
@@ -46,7 +46,7 @@
 
     @Test
     public void timeWithNewProvider() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Security.addProvider(new MockProvider());
             try {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
index c7b6cb5..5f1bfc2 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/RandomPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -32,11 +32,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class RandomPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeNewRandom() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Random rng = new Random();
             rng.nextInt();
@@ -46,7 +46,7 @@
     @Test
     public void timeReusedRandom() throws Exception {
         Random rng = new Random();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             rng.nextInt();
         }
@@ -55,7 +55,7 @@
     @Test
     public void timeReusedSecureRandom() throws Exception {
         SecureRandom rng = new SecureRandom();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             rng.nextInt();
         }
@@ -63,7 +63,7 @@
 
     @Test
     public void timeNewSecureRandom() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             SecureRandom rng = new SecureRandom();
             rng.nextInt();
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
index 44e5f22..008c94c 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/RealToStringPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class RealToStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final float SMALL = -123.45f;
     private static final float MEDIUM = -123.45e8f;
@@ -37,7 +37,7 @@
 
     @Test
     public void timeFloat_toString_NaN() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(Float.NaN);
         }
@@ -45,7 +45,7 @@
 
     @Test
     public void timeFloat_toString_NEGATIVE_INFINITY() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(Float.NEGATIVE_INFINITY);
         }
@@ -53,7 +53,7 @@
 
     @Test
     public void timeFloat_toString_POSITIVE_INFINITY() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(Float.POSITIVE_INFINITY);
         }
@@ -61,7 +61,7 @@
 
     @Test
     public void timeFloat_toString_zero() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(0.0f);
         }
@@ -69,7 +69,7 @@
 
     @Test
     public void timeFloat_toString_minusZero() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(-0.0f);
         }
@@ -77,7 +77,7 @@
 
     @Test
     public void timeFloat_toString_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(SMALL);
         }
@@ -85,7 +85,7 @@
 
     @Test
     public void timeFloat_toString_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(MEDIUM);
         }
@@ -93,7 +93,7 @@
 
     @Test
     public void timeFloat_toString_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.toString(LARGE);
         }
@@ -101,7 +101,7 @@
 
     @Test
     public void timeStringBuilder_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(SMALL);
         }
@@ -109,7 +109,7 @@
 
     @Test
     public void timeStringBuilder_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(MEDIUM);
         }
@@ -117,7 +117,7 @@
 
     @Test
     public void timeStringBuilder_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new StringBuilder().append(LARGE);
         }
@@ -125,7 +125,7 @@
 
     @Test
     public void timeFormatter_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%f", SMALL);
         }
@@ -133,7 +133,7 @@
 
     @Test
     public void timeFormatter_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%f", MEDIUM);
         }
@@ -141,7 +141,7 @@
 
     @Test
     public void timeFormatter_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%f", LARGE);
         }
@@ -149,7 +149,7 @@
 
     @Test
     public void timeFormatter_dot2f_small() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%.2f", SMALL);
         }
@@ -157,7 +157,7 @@
 
     @Test
     public void timeFormatter_dot2f_medium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%.2f", MEDIUM);
         }
@@ -165,7 +165,7 @@
 
     @Test
     public void timeFormatter_dot2f_large() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             String.format("%.2f", LARGE);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
index 6e00b1083..45b623d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ReflectionPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,12 +33,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectionPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeObject_getClass() throws Exception {
         C c = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             c.getClass();
         }
@@ -47,7 +47,7 @@
     @Test
     public void timeClass_getField() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.getField("f");
         }
@@ -56,7 +56,7 @@
     @Test
     public void timeClass_getDeclaredField() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.getDeclaredField("f");
         }
@@ -65,7 +65,7 @@
     @Test
     public void timeClass_getConstructor() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.getConstructor();
         }
@@ -75,7 +75,7 @@
     public void timeClass_newInstance() throws Exception {
         Class<?> klass = C.class;
         Constructor constructor = klass.getConstructor();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             constructor.newInstance();
         }
@@ -84,7 +84,7 @@
     @Test
     public void timeClass_getMethod() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.getMethod("m");
         }
@@ -93,7 +93,7 @@
     @Test
     public void timeClass_getDeclaredMethod() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.getDeclaredMethod("m");
         }
@@ -104,7 +104,7 @@
         Class<?> klass = C.class;
         Field f = klass.getDeclaredField("f");
         C instance = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             f.setInt(instance, 1);
         }
@@ -115,7 +115,7 @@
         Class<?> klass = C.class;
         Field f = klass.getDeclaredField("f");
         C instance = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             f.getInt(instance);
         }
@@ -126,7 +126,7 @@
         Class<?> klass = C.class;
         Method m = klass.getDeclaredMethod("m");
         C instance = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(instance);
         }
@@ -136,7 +136,7 @@
     public void timeMethod_invokeStaticV() throws Exception {
         Class<?> klass = C.class;
         Method m = klass.getDeclaredMethod("sm");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(null);
         }
@@ -147,7 +147,7 @@
         Class<?> klass = C.class;
         Method m = klass.getDeclaredMethod("setField", int.class);
         C instance = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(instance, 1);
         }
@@ -159,7 +159,7 @@
         Method m = klass.getDeclaredMethod("setField", int.class);
         C instance = new C();
         Integer one = Integer.valueOf(1);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(instance, one);
         }
@@ -169,7 +169,7 @@
     public void timeMethod_invokeStaticI() throws Exception {
         Class<?> klass = C.class;
         Method m = klass.getDeclaredMethod("setStaticField", int.class);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(null, 1);
         }
@@ -180,7 +180,7 @@
         Class<?> klass = C.class;
         Method m = klass.getDeclaredMethod("setStaticField", int.class);
         Integer one = Integer.valueOf(1);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             m.invoke(null, one);
         }
@@ -189,7 +189,7 @@
     @Test
     public void timeRegularMethodInvocation() throws Exception {
         C instance = new C();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             instance.setField(1);
         }
@@ -197,7 +197,7 @@
 
     @Test
     public void timeRegularConstructor() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             new C();
         }
@@ -206,7 +206,7 @@
     @Test
     public void timeClass_classNewInstance() throws Exception {
         Class<?> klass = C.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.newInstance();
         }
@@ -216,7 +216,7 @@
     public void timeClass_isInstance() throws Exception {
         D d = new D();
         Class<?> klass = IC.class;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             klass.isInstance(d);
         }
@@ -224,7 +224,7 @@
 
     @Test
     public void timeGetInstanceField() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             // TODO: Write a test script that generates both the classes we're
             // reflecting on and the test case for each of its fields.
@@ -234,7 +234,7 @@
 
     @Test
     public void timeGetStaticField() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             R.class.getField("WEEK_NUMBER_COLOR");
         }
@@ -242,7 +242,7 @@
 
     @Test
     public void timeGetInterfaceStaticField() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             F.class.getField("SF");
         }
@@ -250,7 +250,7 @@
 
     @Test
     public void timeGetSuperClassField() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             G.class.getField("f");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
index 5a9b5c3..da69f9f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SSLLoopbackPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -35,11 +35,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SSLLoopbackPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void time() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TestSSLContext context =
                     TestSSLContext.create(TestKeyStore.getClient(), TestKeyStore.getServer());
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
index 6d48cf2..9f2c312 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SSLSocketFactoryPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SSLSocketFactoryPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void time() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             SSLSocketFactory.getDefault();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
index 8641629..7c60c05 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SchemePrefixPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -37,7 +37,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class SchemePrefixPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     enum Strategy {
         JAVA() {
@@ -94,7 +94,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeSchemePrefix(Strategy strategy) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             strategy.execute("http://android.com");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
index afd1191..1812983 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SerializationPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +37,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SerializationPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static byte[] bytes(Object o) throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
@@ -110,7 +110,7 @@
     public void timeWriteNoObjects() throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
         ObjectOutputStream out = new ObjectOutputStream(baos);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             out.reset();
             baos.reset();
@@ -121,7 +121,7 @@
     private void readSingleObject(Object object) throws Exception {
         byte[] bytes = bytes(object);
         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             ObjectInputStream in = new ObjectInputStream(bais);
             in.readObject();
@@ -133,7 +133,7 @@
     private void writeSingleObject(Object o) throws Exception {
         ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
         ObjectOutputStream out = new ObjectOutputStream(baos);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             out.writeObject(o);
             out.reset();
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
index 6c26133..34e9bfb 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SignaturePerfTest.java
@@ -15,8 +15,8 @@
  */
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -41,7 +41,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class SignaturePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -117,7 +117,7 @@
     @Parameters(method = "getData")
     public void timeSign(Algorithm algorithm, Implementation implementation) throws Exception {
         setUp(algorithm);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Signature signer;
             switch (implementation) {
@@ -140,7 +140,7 @@
     @Parameters(method = "getData")
     public void timeVerify(Algorithm algorithm, Implementation implementation) throws Exception {
         setUp(algorithm);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Signature verifier;
             switch (implementation) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
index 274b51f..2fe6798 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/SimpleDateFormatPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -37,11 +37,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class SimpleDateFormatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void time_createFormatWithTimeZone() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z");
         }
@@ -50,7 +50,7 @@
     @Test
     public void time_parseWithTimeZoneShort() throws ParseException {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             sdf.parse("2000.01.01 PST");
         }
@@ -59,7 +59,7 @@
     @Test
     public void time_parseWithTimeZoneLong() throws ParseException {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             sdf.parse("2000.01.01 Pacific Standard Time");
         }
@@ -68,7 +68,7 @@
     @Test
     public void time_parseWithoutTimeZone() throws ParseException {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             sdf.parse("2000.01.01");
         }
@@ -76,7 +76,7 @@
 
     @Test
     public void time_createAndParseWithTimeZoneShort() throws ParseException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z");
             sdf.parse("2000.01.01 PST");
@@ -85,7 +85,7 @@
 
     @Test
     public void time_createAndParseWithTimeZoneLong() throws ParseException {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz");
             sdf.parse("2000.01.01 Pacific Standard Time");
@@ -95,7 +95,7 @@
     @Test
     public void time_formatWithTimeZoneShort() {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd z");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             sdf.format(new Date());
         }
@@ -104,7 +104,7 @@
     @Test
     public void time_formatWithTimeZoneLong() {
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd zzzz");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             sdf.format(new Date());
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
index b4c427b..fbe3cef 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StrictMathPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -33,7 +33,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StrictMathPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private final double mDouble = 1.2;
     private final float mFloat = 1.2f;
@@ -74,7 +74,7 @@
 
     @Test
     public void timeAbsD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.abs(mDouble);
         }
@@ -82,7 +82,7 @@
 
     @Test
     public void timeAbsF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.abs(mFloat);
         }
@@ -90,7 +90,7 @@
 
     @Test
     public void timeAbsI() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.abs(mInt);
         }
@@ -98,7 +98,7 @@
 
     @Test
     public void timeAbsL() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.abs(mLong);
         }
@@ -106,7 +106,7 @@
 
     @Test
     public void timeAcos() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.acos(mDouble);
         }
@@ -114,7 +114,7 @@
 
     @Test
     public void timeAsin() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.asin(mDouble);
         }
@@ -122,7 +122,7 @@
 
     @Test
     public void timeAtan() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.atan(mDouble);
         }
@@ -130,7 +130,7 @@
 
     @Test
     public void timeAtan2() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.atan2(3, 4);
         }
@@ -138,7 +138,7 @@
 
     @Test
     public void timeCbrt() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.cbrt(mDouble);
         }
@@ -146,7 +146,7 @@
 
     @Test
     public void timeCeilOverInterestingValues() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < CEIL_DOUBLES.length; ++i) {
                 StrictMath.ceil(CEIL_DOUBLES[i]);
@@ -156,7 +156,7 @@
 
     @Test
     public void timeCopySignD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.copySign(mDouble, mDouble);
         }
@@ -164,7 +164,7 @@
 
     @Test
     public void timeCopySignF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.copySign(mFloat, mFloat);
         }
@@ -172,7 +172,7 @@
 
     @Test
     public void timeCos() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.cos(mDouble);
         }
@@ -180,7 +180,7 @@
 
     @Test
     public void timeCosh() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.cosh(mDouble);
         }
@@ -188,7 +188,7 @@
 
     @Test
     public void timeExp() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.exp(mDouble);
         }
@@ -196,7 +196,7 @@
 
     @Test
     public void timeExpm1() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.expm1(mDouble);
         }
@@ -204,7 +204,7 @@
 
     @Test
     public void timeFloorOverInterestingValues() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < FLOOR_DOUBLES.length; ++i) {
                 StrictMath.floor(FLOOR_DOUBLES[i]);
@@ -214,7 +214,7 @@
 
     @Test
     public void timeGetExponentD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.getExponent(mDouble);
         }
@@ -222,7 +222,7 @@
 
     @Test
     public void timeGetExponentF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.getExponent(mFloat);
         }
@@ -230,7 +230,7 @@
 
     @Test
     public void timeHypot() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.hypot(mDouble, mDouble);
         }
@@ -238,7 +238,7 @@
 
     @Test
     public void timeIEEEremainder() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.IEEEremainder(mDouble, mDouble);
         }
@@ -246,7 +246,7 @@
 
     @Test
     public void timeLog() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.log(mDouble);
         }
@@ -254,7 +254,7 @@
 
     @Test
     public void timeLog10() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.log10(mDouble);
         }
@@ -262,7 +262,7 @@
 
     @Test
     public void timeLog1p() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.log1p(mDouble);
         }
@@ -270,7 +270,7 @@
 
     @Test
     public void timeMaxD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.max(mDouble, mDouble);
         }
@@ -278,7 +278,7 @@
 
     @Test
     public void timeMaxF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.max(mFloat, mFloat);
         }
@@ -286,7 +286,7 @@
 
     @Test
     public void timeMaxI() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.max(mInt, mInt);
         }
@@ -294,7 +294,7 @@
 
     @Test
     public void timeMaxL() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.max(mLong, mLong);
         }
@@ -302,7 +302,7 @@
 
     @Test
     public void timeMinD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.min(mDouble, mDouble);
         }
@@ -310,7 +310,7 @@
 
     @Test
     public void timeMinF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.min(mFloat, mFloat);
         }
@@ -318,7 +318,7 @@
 
     @Test
     public void timeMinI() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.min(mInt, mInt);
         }
@@ -326,7 +326,7 @@
 
     @Test
     public void timeMinL() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.min(mLong, mLong);
         }
@@ -334,7 +334,7 @@
 
     @Test
     public void timeNextAfterD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.nextAfter(mDouble, mDouble);
         }
@@ -342,7 +342,7 @@
 
     @Test
     public void timeNextAfterF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.nextAfter(mFloat, mFloat);
         }
@@ -350,7 +350,7 @@
 
     @Test
     public void timeNextUpD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.nextUp(mDouble);
         }
@@ -358,7 +358,7 @@
 
     @Test
     public void timeNextUpF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.nextUp(mFloat);
         }
@@ -366,7 +366,7 @@
 
     @Test
     public void timePow() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.pow(mDouble, mDouble);
         }
@@ -374,7 +374,7 @@
 
     @Test
     public void timeRandom() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.random();
         }
@@ -382,7 +382,7 @@
 
     @Test
     public void timeRint() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.rint(mDouble);
         }
@@ -390,7 +390,7 @@
 
     @Test
     public void timeRoundD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.round(mDouble);
         }
@@ -398,7 +398,7 @@
 
     @Test
     public void timeRoundF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.round(mFloat);
         }
@@ -406,7 +406,7 @@
 
     @Test
     public void timeScalbD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.scalb(mDouble, 5);
         }
@@ -414,7 +414,7 @@
 
     @Test
     public void timeScalbF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.scalb(mFloat, 5);
         }
@@ -422,7 +422,7 @@
 
     @Test
     public void timeSignumD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.signum(mDouble);
         }
@@ -430,7 +430,7 @@
 
     @Test
     public void timeSignumF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.signum(mFloat);
         }
@@ -438,7 +438,7 @@
 
     @Test
     public void timeSin() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.sin(mDouble);
         }
@@ -446,7 +446,7 @@
 
     @Test
     public void timeSinh() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.sinh(mDouble);
         }
@@ -454,7 +454,7 @@
 
     @Test
     public void timeSqrt() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.sqrt(mDouble);
         }
@@ -462,7 +462,7 @@
 
     @Test
     public void timeTan() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.tan(mDouble);
         }
@@ -470,7 +470,7 @@
 
     @Test
     public void timeTanh() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.tanh(mDouble);
         }
@@ -478,7 +478,7 @@
 
     @Test
     public void timeToDegrees() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.toDegrees(mDouble);
         }
@@ -486,7 +486,7 @@
 
     @Test
     public void timeToRadians() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.toRadians(mDouble);
         }
@@ -494,7 +494,7 @@
 
     @Test
     public void timeUlpD() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.ulp(mDouble);
         }
@@ -502,7 +502,7 @@
 
     @Test
     public void timeUlpF() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StrictMath.ulp(mFloat);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
index 2235cc56..0155154 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringBuilderPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -30,13 +30,13 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringBuilderPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public int mLength = 100;
 
     @Test
     public void timeAppendBoolean() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -47,7 +47,7 @@
 
     @Test
     public void timeAppendChar() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -59,7 +59,7 @@
     @Test
     public void timeAppendCharArray() {
         char[] chars = "chars".toCharArray();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -71,7 +71,7 @@
     @Test
     public void timeAppendCharSequence() {
         CharSequence cs = "chars";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -83,7 +83,7 @@
     @Test
     public void timeAppendSubCharSequence() {
         CharSequence cs = "chars";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -95,7 +95,7 @@
     @Test
     public void timeAppendDouble() {
         double d = 1.2;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -107,7 +107,7 @@
     @Test
     public void timeAppendFloat() {
         float f = 1.2f;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -119,7 +119,7 @@
     @Test
     public void timeAppendInt() {
         int n = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -131,7 +131,7 @@
     @Test
     public void timeAppendLong() {
         long l = 123;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -150,7 +150,7 @@
                         return "constant";
                     }
                 };
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
@@ -162,7 +162,7 @@
     @Test
     public void timeAppendString() {
         String s = "chars";
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             StringBuilder sb = new StringBuilder();
             for (int j = 0; j < mLength; ++j) {
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
index 9ab5000..5533745 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringEqualsPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -38,7 +38,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringEqualsPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private final String mLong1 =
             "Ahead-of-time compilation is possible as the compiler may just convert an instruction"
@@ -226,7 +226,7 @@
     // Benchmark cases of String.equals(null)
     @Test
     public void timeEqualsNull() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mMediumStrings.length; i++) {
                 mMediumStrings[i][0].equals(null);
@@ -237,7 +237,7 @@
     // Benchmark cases with very short (<5 character) Strings
     @Test
     public void timeEqualsShort() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mShortStrings.length; i++) {
                 mShortStrings[i][0].equals(mShortStrings[i][1]);
@@ -248,7 +248,7 @@
     // Benchmark cases with medium length (10-15 character) Strings
     @Test
     public void timeEqualsMedium() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mMediumStrings.length; i++) {
                 mMediumStrings[i][0].equals(mMediumStrings[i][1]);
@@ -259,7 +259,7 @@
     // Benchmark cases with long (>100 character) Strings
     @Test
     public void timeEqualsLong() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mLongStrings.length; i++) {
                 mLongStrings[i][0].equals(mLongStrings[i][1]);
@@ -270,7 +270,7 @@
     // Benchmark cases with very long (>1000 character) Strings
     @Test
     public void timeEqualsVeryLong() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mVeryLongStrings.length; i++) {
                 mVeryLongStrings[i][0].equals(mVeryLongStrings[i][1]);
@@ -281,7 +281,7 @@
     // Benchmark cases with non-word aligned Strings
     @Test
     public void timeEqualsNonWordAligned() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mNonalignedStrings.length; i++) {
                 mNonalignedStrings[i][0].equals(mNonalignedStrings[i][1]);
@@ -292,7 +292,7 @@
     // Benchmark cases with slight differences in the endings
     @Test
     public void timeEqualsEnd() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mEndStrings.length; i++) {
                 mEndStrings[i][0].equals(mEndStrings[i][1]);
@@ -303,7 +303,7 @@
     // Benchmark cases of comparing a string to a non-string object
     @Test
     public void timeEqualsNonString() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             for (int i = 0; i < mMediumStrings.length; i++) {
                 mMediumStrings[i][0].equals(mObjects[i]);
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
index b1e749c..a5662b0 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringIsEmptyPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +29,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringIsEmptyPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeIsEmpty_NonEmpty() {
         boolean result = true;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".isEmpty());
         }
@@ -44,7 +44,7 @@
     @Test
     public void timeIsEmpty_Empty() {
         boolean result = true;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result &= ("".isEmpty());
         }
@@ -54,7 +54,7 @@
     @Test
     public void timeLengthEqualsZero() {
         boolean result = true;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result &= !("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length() == 0);
         }
@@ -64,7 +64,7 @@
     @Test
     public void timeEqualsEmpty() {
         boolean result = true;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             result &= !"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".equals("");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
index 9e57591..41e64f2 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringLengthPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,12 +29,12 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringLengthPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeLength() {
         int length = 0;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             length = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".length();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
index a80514c..2cd2a09 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,7 +34,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     enum StringLengths {
         EMPTY(""),
@@ -69,7 +69,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeHashCode(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.hashCode();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
index 78ae395..219dccf 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplaceAllPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,7 +34,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringReplaceAllPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     // NOTE: These estimates of MOVEABLE / NON_MOVEABLE are based on a knowledge of
     // ART implementation details. They make a difference here because JNI calls related
@@ -86,7 +86,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceAllTrivialPatternNonExistent(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replaceAll("fish", "0");
         }
@@ -95,7 +95,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceTrivialPatternAllRepeated(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replaceAll("jklm", "0");
         }
@@ -104,7 +104,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceAllTrivialPatternSingleOccurrence(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replaceAll("qrst", "0");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
index 73911c7..d6fef5e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringReplacePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,7 +34,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringReplacePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     enum StringLengths {
         EMPTY(""),
@@ -80,7 +80,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceCharNonExistent(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace('z', '0');
         }
@@ -89,7 +89,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceCharRepeated(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace('a', '0');
         }
@@ -98,7 +98,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceSingleChar(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace('q', '0');
         }
@@ -107,7 +107,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceSequenceNonExistent(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace("fish", "0");
         }
@@ -116,7 +116,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceSequenceRepeated(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace("jklm", "0");
         }
@@ -125,7 +125,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeReplaceSingleSequence(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.replace("qrst", "0");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
index 1539271..9d0ec2f 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringSplitPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class StringSplitPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeStringSplitComma() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             "this,is,a,simple,example".split(",");
         }
@@ -43,7 +43,7 @@
 
     @Test
     public void timeStringSplitLiteralDot() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             "this.is.a.simple.example".split("\\.");
         }
@@ -51,7 +51,7 @@
 
     @Test
     public void timeStringSplitNewline() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             "this\nis\na\nsimple\nexample\n".split("\n");
         }
@@ -60,7 +60,7 @@
     @Test
     public void timePatternSplitComma() {
         Pattern p = Pattern.compile(",");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             p.split("this,is,a,simple,example");
         }
@@ -69,7 +69,7 @@
     @Test
     public void timePatternSplitLiteralDot() {
         Pattern p = Pattern.compile("\\.");
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             p.split("this.is.a.simple.example");
         }
@@ -77,7 +77,7 @@
 
     @Test
     public void timeStringSplitHard() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             "this,is,a,harder,example".split("[,]");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
index 0d5e62b..11950b7 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToBytesPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -35,7 +35,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringToBytesPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     enum StringLengths {
         EMPTY(""),
@@ -89,7 +89,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeGetBytesUtf8(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.getBytes(StandardCharsets.UTF_8);
         }
@@ -98,7 +98,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeGetBytesIso88591(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.getBytes(StandardCharsets.ISO_8859_1);
         }
@@ -107,7 +107,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeGetBytesAscii(StringLengths stringLengths) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             stringLengths.mValue.getBytes(StandardCharsets.US_ASCII);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
index ecdf809..4b27a16 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/StringToRealPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -34,7 +34,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public class StringToRealPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -53,7 +53,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeFloat_parseFloat(String string) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Float.parseFloat(string);
         }
@@ -62,7 +62,7 @@
     @Test
     @Parameters(method = "getData")
     public void timeDouble_parseDouble(String string) {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             Double.parseDouble(string);
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
index 2b2a6b5..0ab012d 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ThreadLocalPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +29,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ThreadLocalPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     private static final ThreadLocal<char[]> BUFFER =
             new ThreadLocal<char[]>() {
@@ -41,7 +41,7 @@
 
     @Test
     public void timeThreadLocal_get() {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             BUFFER.get();
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
index 6eb8fcc..ddf410e 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/TimeZonePerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -31,11 +31,11 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class TimeZonePerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     @Test
     public void timeTimeZone_getDefault() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getDefault();
         }
@@ -43,7 +43,7 @@
 
     @Test
     public void timeTimeZone_getTimeZoneUTC() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getTimeZone("UTC");
         }
@@ -52,7 +52,7 @@
     @Test
     public void timeTimeZone_getTimeZone_default() throws Exception {
         String defaultId = TimeZone.getDefault().getID();
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getTimeZone(defaultId);
         }
@@ -61,7 +61,7 @@
     // A time zone with relatively few transitions.
     @Test
     public void timeTimeZone_getTimeZone_America_Caracas() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getTimeZone("America/Caracas");
         }
@@ -70,7 +70,7 @@
     // A time zone with a lot of transitions.
     @Test
     public void timeTimeZone_getTimeZone_America_Santiago() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getTimeZone("America/Santiago");
         }
@@ -78,7 +78,7 @@
 
     @Test
     public void timeTimeZone_getTimeZone_GMT_plus_10() throws Exception {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             TimeZone.getTimeZone("GMT+10");
         }
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
index 288c646..a38763b 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/XMLEntitiesPerfTest.java
@@ -16,8 +16,8 @@
 
 package android.libcore.regression;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 
@@ -42,7 +42,7 @@
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class XMLEntitiesPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
     public static Collection<Object[]> getData() {
         return Arrays.asList(
@@ -85,7 +85,7 @@
     @Parameters(method = "getData")
     public void timeXmlParser(int length, float entityFraction) throws Exception {
         setUp(length, entityFraction);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             XmlPullParser parser = mXmlPullParserFactory.newPullParser();
             parser.setInput(new StringReader(mXml));
@@ -99,7 +99,7 @@
     @Parameters(method = "getData")
     public void timeDocumentBuilder(int length, float entityFraction) throws Exception {
         setUp(length, entityFraction);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             DocumentBuilder documentBuilder = mDocumentBuilderFactory.newDocumentBuilder();
             documentBuilder.parse(new InputSource(new StringReader(mXml)));
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
index 003c957..4076c9d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianIntPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectGetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     int mValue;
 
@@ -42,7 +47,7 @@
     @Test
     public void run() throws Throwable {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mField.getInt(this);
             x = (int) mField.getInt(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
index 4f21618..2c65dd4 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetFieldLittleEndianStringPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectGetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     String mValue;
 
@@ -42,7 +47,7 @@
     @Test
     public void run() throws Throwable {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mField.get(this);
             x = (String) mField.get(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
index 210014a..dcd25db 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianIntPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectGetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     static int sValue;
 
@@ -42,7 +47,7 @@
     @Test
     public void run() throws Throwable {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mField.getInt(null);
             x = (int) mField.getInt(null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
index 22c6827..c938a4c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectGetStaticFieldLittleEndianStringPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectGetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     static String sValue;
 
@@ -42,7 +47,7 @@
     @Test
     public void run() throws Throwable {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mField.get(null);
             x = (String) mField.get(null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
index 5b39109..618e1b5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianIntPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectSetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     int mValue;
 
@@ -41,7 +46,7 @@
 
     @Test
     public void run() throws Throwable {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.setInt(this, 42);
             mField.setInt(this, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
index 883e8a7..8c2e3ca 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetFieldLittleEndianStringPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectSetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     String mValue;
 
@@ -41,7 +46,7 @@
 
     @Test
     public void run() throws Throwable {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.set(this, "qwerty");
             mField.set(this, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
index 50bc85c..e888cc68 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianIntPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectSetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     static int sValue;
 
@@ -41,7 +46,7 @@
 
     @Test
     public void run() throws Throwable {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.setInt(null, 42);
             mField.setInt(null, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
index 13fa2bf..7016611 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/ReflectSetStaticFieldLittleEndianStringPerfTest.java
@@ -13,25 +13,30 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
 import java.lang.reflect.Field;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class ReflectSetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     static String sValue;
 
@@ -41,7 +46,7 @@
 
     @Test
     public void run() throws Throwable {
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mField.set(null, "qwerty");
             mField.set(null, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
index 85c9bae9..65c82cc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchangeAcquire(this, mField, ~42);
             x = (int) mVh.compareAndExchangeAcquire(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
index 2b8f430..a350b61 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeAcquireFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchangeAcquire(this, mField, null);
             x = (String) mVh.compareAndExchangeAcquire(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
index 246fa43..34f596e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchangeAcquire(sField, ~42);
             x = (int) mVh.compareAndExchangeAcquire(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
index d12ffae..2216d7b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,20 +34,19 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
 
-    public VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest()
-            throws Throwable {
+    public VarHandleCompareandexchangeAcquireStaticFieldLittleEndianStringPerfTest() throws Throwable {
         mVh = MethodHandles.lookup().findStaticVarHandle(this.getClass(), "sField", String.class);
     }
 
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchangeAcquire(sField, null);
             x = (String) mVh.compareAndExchangeAcquire(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
index 5ced115..bda551f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchange(this, mField, ~42);
             x = (int) mVh.compareAndExchange(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
index b955d50..f4d7893 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchange(this, mField, null);
             x = (String) mVh.compareAndExchange(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
index 601ff34..f438087 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchangeRelease(this, mField, ~42);
             x = (int) mVh.compareAndExchangeRelease(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
index 0e567f9..78df5c0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeReleaseFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchangeRelease(this, mField, null);
             x = (String) mVh.compareAndExchangeRelease(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
index 6be2870..f45cc62 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchangeRelease(sField, ~42);
             x = (int) mVh.compareAndExchangeRelease(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
index 84c186b..08aa7e2 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,20 +34,19 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
 
-    public VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest()
-            throws Throwable {
+    public VarHandleCompareandexchangeReleaseStaticFieldLittleEndianStringPerfTest() throws Throwable {
         mVh = MethodHandles.lookup().findStaticVarHandle(this.getClass(), "sField", String.class);
     }
 
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchangeRelease(sField, null);
             x = (String) mVh.compareAndExchangeRelease(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
index b093234..5d4b2e0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.compareAndExchange(sField, ~42);
             x = (int) mVh.compareAndExchange(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
index 0d2037b4..ba4f2c8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandexchangeStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.compareAndExchange(sField, null);
             x = (String) mVh.compareAndExchange(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
index ee31973..7fca450 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandsetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.compareAndSet(this, mField, ~42);
             success = mVh.compareAndSet(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
index 0571fef..7eb7ac0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandsetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.compareAndSet(this, mField, null);
             success = mVh.compareAndSet(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
index f619dab..ddfd407 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandsetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.compareAndSet(sField, ~42);
             success = mVh.compareAndSet(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
index fc443fa..f1f3968 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleCompareandsetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.compareAndSet(sField, null);
             success = mVh.compareAndSet(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
index bf3d58b..09127c4 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAcquire(this);
             x = (int) mVh.getAcquire(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
index 1f4bc31..87be4a6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetAcquireFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAcquire(this);
             x = (String) mVh.getAcquire(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
index 2085552..5d5fc11 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAcquire();
             x = (int) mVh.getAcquire();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
index d9c7d7b..c7034b8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetAcquireStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAcquire();
             x = (String) mVh.getAcquire();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
index acd2533..f22865b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,9 +34,9 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetArrayLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int ELEMENT_VALUE = 42;
-    int[] mArray = {ELEMENT_VALUE};
+    int[] mArray = { ELEMENT_VALUE };
     VarHandle mVh;
 
     public VarHandleGetArrayLittleEndianIntPerfTest() throws Throwable {
@@ -54,7 +55,7 @@
     public void run() {
         int[] a = mArray;
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.get(a, 0);
             x = (int) mVh.get(a, 0);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
index de9944a..fdb9e84 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetArrayLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,9 +34,9 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetArrayLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String ELEMENT_VALUE = "qwerty";
-    String[] mArray = {ELEMENT_VALUE};
+    String[] mArray = { ELEMENT_VALUE };
     VarHandle mVh;
 
     public VarHandleGetArrayLittleEndianStringPerfTest() throws Throwable {
@@ -54,7 +55,7 @@
     public void run() {
         String[] a = mArray;
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.get(a, 0);
             x = (String) mVh.get(a, 0);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
index a863929..347b0cf 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewBigEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -29,22 +30,22 @@
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
+
+import java.util.Arrays;
 import java.nio.ByteOrder;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetByteArrayViewBigEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int VALUE = 42;
-    byte[] mArray1 = {
-        (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE
-    };
-    byte[] mArray2 = {(byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE};
+    byte[] mArray1 = { (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE };
+    byte[] mArray2 = { (byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE };
     VarHandle mVh;
 
     public VarHandleGetByteArrayViewBigEndianIntPerfTest() throws Throwable {
         mVh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
-    }
+  }
 
     @Before
     public void setup() {
@@ -58,7 +59,7 @@
     public void run() {
         byte[] a = mArray1;
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.get(a, 0);
             x = (int) mVh.get(a, 0);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
index 4999b9b..dedc94f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetByteArrayViewLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -29,22 +30,22 @@
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
+
+import java.util.Arrays;
 import java.nio.ByteOrder;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetByteArrayViewLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int VALUE = 42;
-    byte[] mArray1 = {
-        (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24)
-    };
-    byte[] mArray2 = {(byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24)};
+    byte[] mArray1 = { (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24) };
+    byte[] mArray2 = { (byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24) };
     VarHandle mVh;
 
     public VarHandleGetByteArrayViewLittleEndianIntPerfTest() throws Throwable {
         mVh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN);
-    }
+  }
 
     @Before
     public void setup() {
@@ -58,7 +59,7 @@
     public void run() {
         byte[] a = mArray1;
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.get(a, 0);
             x = (int) mVh.get(a, 0);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
index ee80a6f..3f0f624 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.get(this);
             x = (int) mVh.get(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
index ec29f7a..9db6328 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.get(this);
             x = (String) mVh.get(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
index ee6a669..17b74a8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetOpaqueFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getOpaque(this);
             x = (int) mVh.getOpaque(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
index 1702b84..5df1380 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetOpaqueFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getOpaque(this);
             x = (String) mVh.getOpaque(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
index 514ddb9..f656ef2 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetOpaqueStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getOpaque();
             x = (int) mVh.getOpaque();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
index fbcee69..1087df3 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetOpaqueStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getOpaque();
             x = (String) mVh.getOpaque();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
index 2c56588..0043451 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.get();
             x = (int) mVh.get();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
index 8fce69e..0162637 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.get();
             x = (String) mVh.get();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
index ef530607..b0c4631 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetVolatileFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getVolatile(this);
             x = (int) mVh.getVolatile(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
index 64c0898..5cbbc08 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetVolatileFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getVolatile(this);
             x = (String) mVh.getVolatile(this);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
index 939100c..368ae69 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetVolatileStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getVolatile();
             x = (int) mVh.getVolatile();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
index 728b199..3387a8d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetVolatileStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -53,7 +54,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getVolatile();
             x = (String) mVh.getVolatile();
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
index bf5ef99..781e04f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddAcquireFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     float mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAddAcquire(this, 2.17f);
             x = (float) mVh.getAndAddAcquire(this, 2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
index d15705e..97f29ba 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAddAcquire(this, ~42);
             x = (int) mVh.getAndAddAcquire(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
index 222a60d..e108f7f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddAcquireStaticFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     static float sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAddAcquire(2.17f);
             x = (float) mVh.getAndAddAcquire(2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
index 7436476..d0ae322 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAddAcquire(~42);
             x = (int) mVh.getAndAddAcquire(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
index cca97f4..1b80c40 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     float mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAdd(this, 2.17f);
             x = (float) mVh.getAndAdd(this, 2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
index 170ee73..edacf181 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAdd(this, ~42);
             x = (int) mVh.getAndAdd(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
index 184f796..0e86b0d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddReleaseFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     float mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAddRelease(this, 2.17f);
             x = (float) mVh.getAndAddRelease(this, 2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
index 7e75c44..83446ff 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAddRelease(this, ~42);
             x = (int) mVh.getAndAddRelease(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
index 39c386b..c1f1e6f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddReleaseStaticFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     static float sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAddRelease(2.17f);
             x = (float) mVh.getAndAddRelease(2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
index 04ab531..1b154a1 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAddRelease(~42);
             x = (int) mVh.getAndAddRelease(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
index b71351f..7de128d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddStaticFieldLittleEndianFloatPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final float FIELD_VALUE = 3.14f;
     static float sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         float x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (float) mVh.getAndAdd(2.17f);
             x = (float) mVh.getAndAdd(2.17f);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
index e3955c0..c9a0926 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandaddStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandaddStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndAdd(~42);
             x = (int) mVh.getAndAdd(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
index adf05a6..fd9d9b1 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAndAcquire(this, ~42);
             x = (int) mVh.getAndBitwiseAndAcquire(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
index 4d657d9..c3c367f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAndAcquire(~42);
             x = (int) mVh.getAndBitwiseAndAcquire(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
index dc64174..e073d28 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAnd(this, ~42);
             x = (int) mVh.getAndBitwiseAnd(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
index 25d5631..ca78f5a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAndRelease(this, ~42);
             x = (int) mVh.getAndBitwiseAndRelease(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
index de2d548..599f186 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAndRelease(~42);
             x = (int) mVh.getAndBitwiseAndRelease(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
index 36544c6..71fc0ae 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseAndStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseAnd(~42);
             x = (int) mVh.getAndBitwiseAnd(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
index fb36d0c..8fc4eab 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOrAcquire(this, ~42);
             x = (int) mVh.getAndBitwiseOrAcquire(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
index 4194b12..3368953 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOrAcquire(~42);
             x = (int) mVh.getAndBitwiseOrAcquire(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
index 355c6e8..583a3a0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOr(this, ~42);
             x = (int) mVh.getAndBitwiseOr(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
index 401079d..1592fa6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOrRelease(this, ~42);
             x = (int) mVh.getAndBitwiseOrRelease(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
index 322dcbf..d496083 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOrRelease(~42);
             x = (int) mVh.getAndBitwiseOrRelease(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
index c982814..87276a5 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseOrStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseOr(~42);
             x = (int) mVh.getAndBitwiseOr(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
index 0b1cb32..f7a372f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXorAcquire(this, ~42);
             x = (int) mVh.getAndBitwiseXorAcquire(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
index 4737072..22726fc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXorAcquire(~42);
             x = (int) mVh.getAndBitwiseXorAcquire(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
index 204cd70..d071d6e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXor(this, ~42);
             x = (int) mVh.getAndBitwiseXor(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
index b3ffed7..be2aa9c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXorRelease(this, ~42);
             x = (int) mVh.getAndBitwiseXorRelease(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
index d0ab8de..b0a7dcf 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXorRelease(~42);
             x = (int) mVh.getAndBitwiseXorRelease(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
index b378b68..c5f99de 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandbitwiseXorStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndBitwiseXor(~42);
             x = (int) mVh.getAndBitwiseXor(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
index c7c66fe..572e0c8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSetAcquire(this, ~42);
             x = (int) mVh.getAndSetAcquire(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
index 98d6bd7..09be6d9 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetAcquireFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSetAcquire(this, null);
             x = (String) mVh.getAndSetAcquire(this, null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
index 206358f..4e0554a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSetAcquire(~42);
             x = (int) mVh.getAndSetAcquire(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
index 0532e73..5491522 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetAcquireStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSetAcquire(null);
             x = (String) mVh.getAndSetAcquire(null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
index f192d715..a9303c6 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSet(this, ~42);
             x = (int) mVh.getAndSet(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
index 0a8909c..bd4703f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSet(this, null);
             x = (String) mVh.getAndSet(this, null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
index bfcb0f4..d9aee00 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSetRelease(this, ~42);
             x = (int) mVh.getAndSetRelease(this, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
index c6b0509..2c79ca2 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetReleaseFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSetRelease(this, null);
             x = (String) mVh.getAndSetRelease(this, null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
index 45a01ed..ceff8163 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSetRelease(~42);
             x = (int) mVh.getAndSetRelease(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
index 3047281..9b83504 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetReleaseStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSetRelease(null);
             x = (String) mVh.getAndSetRelease(null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
index 6f1f1a0..638da6f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (int) mVh.getAndSet(~42);
             x = (int) mVh.getAndSet(~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
index c4d279f..25d41141 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleGetandsetStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleGetandsetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             x = (String) mVh.getAndSet(null);
             x = (String) mVh.getAndSet(null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
index c4f6005..64ea9f3 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,9 +34,9 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetArrayLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int ELEMENT_VALUE = 42;
-    int[] mArray = {ELEMENT_VALUE};
+    int[] mArray = { ELEMENT_VALUE };
     VarHandle mVh;
 
     public VarHandleSetArrayLittleEndianIntPerfTest() throws Throwable {
@@ -53,7 +54,7 @@
     public void run() {
         int[] a = mArray;
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(a, 0, ~42);
             mVh.set(a, 0, ~42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
index a6858c2..989d682 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetArrayLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,9 +34,9 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetArrayLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String ELEMENT_VALUE = "qwerty";
-    String[] mArray = {ELEMENT_VALUE};
+    String[] mArray = { ELEMENT_VALUE };
     VarHandle mVh;
 
     public VarHandleSetArrayLittleEndianStringPerfTest() throws Throwable {
@@ -53,7 +54,7 @@
     public void run() {
         String[] a = mArray;
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(a, 0, null);
             mVh.set(a, 0, null);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
index a994cbe..9d6d6b8 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewBigEndianIntPerfTest.java
@@ -13,59 +13,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
-import java.nio.ByteOrder;
+
 import java.util.Arrays;
+import java.nio.ByteOrder;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetByteArrayViewBigEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int VALUE = 42;
-    byte[] mArray1 = {
-        (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE
-    };
-    byte[] mArray2 = {(byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE};
+    byte[] mArray1 = { (byte) (VALUE >> 24), (byte) (VALUE >> 16), (byte) (VALUE >> 8), (byte) VALUE };
+    byte[] mArray2 = { (byte) (-1 >> 24), (byte) (-1 >> 16), (byte) (-1 >> 8), (byte) VALUE };
     VarHandle mVh;
 
     public VarHandleSetByteArrayViewBigEndianIntPerfTest() throws Throwable {
         mVh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
-    }
+  }
 
     @After
     public void teardown() {
         if (!Arrays.equals(mArray2, mArray1)) {
-            throw new RuntimeException(
-                    "array has unexpected values: "
-                            + mArray2[0]
-                            + " "
-                            + mArray2[1]
-                            + " "
-                            + mArray2[2]
-                            + " "
-                            + mArray2[3]);
+            throw new RuntimeException("array has unexpected values: " +
+                mArray2[0] + " " + mArray2[1] + " " + mArray2[2] + " " + mArray2[3]);
         }
     }
 
     @Test
     public void run() {
         byte[] a = mArray2;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(a, 0, VALUE);
             mVh.set(a, 0, VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
index 65412ec..e8c3fa3 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetByteArrayViewLittleEndianIntPerfTest.java
@@ -13,59 +13,52 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.VarHandle;
-import java.nio.ByteOrder;
+
 import java.util.Arrays;
+import java.nio.ByteOrder;
 
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetByteArrayViewLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int VALUE = 42;
-    byte[] mArray1 = {
-        (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24)
-    };
-    byte[] mArray2 = {(byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24)};
+    byte[] mArray1 = { (byte) VALUE, (byte) (VALUE >> 8), (byte) (VALUE >> 16), (byte) (VALUE >> 24) };
+    byte[] mArray2 = { (byte) VALUE, (byte) (-1 >> 8), (byte) (-1 >> 16), (byte) (-1 >> 24) };
     VarHandle mVh;
 
     public VarHandleSetByteArrayViewLittleEndianIntPerfTest() throws Throwable {
         mVh = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN);
-    }
+  }
 
     @After
     public void teardown() {
         if (!Arrays.equals(mArray2, mArray1)) {
-            throw new RuntimeException(
-                    "array has unexpected values: "
-                            + mArray2[0]
-                            + " "
-                            + mArray2[1]
-                            + " "
-                            + mArray2[2]
-                            + " "
-                            + mArray2[3]);
+            throw new RuntimeException("array has unexpected values: " +
+                mArray2[0] + " " + mArray2[1] + " " + mArray2[2] + " " + mArray2[3]);
         }
     }
 
     @Test
     public void run() {
         byte[] a = mArray2;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(a, 0, VALUE);
             mVh.set(a, 0, VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
index 573b0ff..08294c0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(this, FIELD_VALUE);
             mVh.set(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
index fe3c0fc..1e8a5bf 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(this, FIELD_VALUE);
             mVh.set(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
index f398899..2e5fb18 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetOpaqueFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setOpaque(this, FIELD_VALUE);
             mVh.setOpaque(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
index 7493120..86a771f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetOpaqueFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setOpaque(this, FIELD_VALUE);
             mVh.setOpaque(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
index 5e73269..903b310 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetOpaqueStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setOpaque(FIELD_VALUE);
             mVh.setOpaque(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
index 9a217d1..63cf7d2 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetOpaqueStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setOpaque(FIELD_VALUE);
             mVh.setOpaque(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
index 1ce2270..d1a358d 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setRelease(this, FIELD_VALUE);
             mVh.setRelease(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
index ed84528..b658324 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetReleaseFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setRelease(this, FIELD_VALUE);
             mVh.setRelease(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
index aeb9640..47cb779 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setRelease(FIELD_VALUE);
             mVh.setRelease(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
index 8959a0c..e48374e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetReleaseStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setRelease(FIELD_VALUE);
             mVh.setRelease(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
index 4007722..0470d67 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(FIELD_VALUE);
             mVh.set(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
index 7323158..00abb0b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetStaticFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.set(FIELD_VALUE);
             mVh.set(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
index f4119c2..c66b23b 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetVolatileFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setVolatile(this, FIELD_VALUE);
             mVh.setVolatile(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
index 9b9c261..1b36450 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetVolatileFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setVolatile(this, FIELD_VALUE);
             mVh.setVolatile(this, FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
index f125384..75f9274 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetVolatileStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         int x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setVolatile(FIELD_VALUE);
             mVh.setVolatile(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
index 2ad605d..8289d4f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest.java
@@ -13,16 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleSetVolatileStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -52,7 +53,7 @@
     @Test
     public void run() {
         String x;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             mVh.setVolatile(FIELD_VALUE);
             mVh.setVolatile(FIELD_VALUE);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
index 5ef3bf0..9fac842 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetAcquireFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetAcquire(this, mField, ~42);
             success = mVh.weakCompareAndSetAcquire(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
index 0c4ed66..2f60127 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetAcquireFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetAcquire(this, mField, null);
             success = mVh.weakCompareAndSetAcquire(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
index db6bd24..4efbd3e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetAcquire(sField, ~42);
             success = mVh.weakCompareAndSetAcquire(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
index d2b0bf7..099640c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,20 +34,19 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
 
-    public VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest()
-            throws Throwable {
+    public VarHandleWeakcompareandsetAcquireStaticFieldLittleEndianStringPerfTest() throws Throwable {
         mVh = MethodHandles.lookup().findStaticVarHandle(this.getClass(), "sField", String.class);
     }
 
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetAcquire(sField, null);
             success = mVh.weakCompareAndSetAcquire(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
index 3cd5ae6..ce8f0f0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSet(this, mField, ~42);
             success = mVh.weakCompareAndSet(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
index 6ddfc25..c4119dc 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSet(this, mField, null);
             success = mVh.weakCompareAndSet(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
index 375f0bc..abd981c 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetPlainFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetPlain(this, mField, ~42);
             success = mVh.weakCompareAndSetPlain(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
index 7e2492a..c71e65f 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetPlainFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetPlain(this, mField, null);
             success = mVh.weakCompareAndSetPlain(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
index 190118c..f3c8f3a 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetPlainStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetPlain(sField, ~42);
             success = mVh.weakCompareAndSetPlain(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
index 484ba1b..5c943a4 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetPlainStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetPlain(sField, null);
             success = mVh.weakCompareAndSetPlain(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
index 80e4e15..1755a15 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetReleaseFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     int mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetRelease(this, mField, ~42);
             success = mVh.weakCompareAndSetRelease(this, mField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
index fa26c59..77175b0 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetReleaseFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     String mField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetRelease(this, mField, null);
             success = mVh.weakCompareAndSetRelease(this, mField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
index 16bf2a20..985519e 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetRelease(sField, ~42);
             success = mVh.weakCompareAndSetRelease(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
index e1716de..69e6ca7 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,20 +34,19 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
 
-    public VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest()
-            throws Throwable {
+    public VarHandleWeakcompareandsetReleaseStaticFieldLittleEndianStringPerfTest() throws Throwable {
         mVh = MethodHandles.lookup().findStaticVarHandle(this.getClass(), "sField", String.class);
     }
 
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSetRelease(sField, null);
             success = mVh.weakCompareAndSetRelease(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
index dc6f2ad..88df5ff 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetStaticFieldLittleEndianIntPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final int FIELD_VALUE = 42;
     static int sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSet(sField, ~42);
             success = mVh.weakCompareAndSet(sField, 42);
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
index d1096c6..c296f668 100644
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest.java
@@ -13,15 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-// This file is generated by generate_java.py do not directly modify!
+ // This file is generated by generate_java.py do not directly modify!
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +34,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class VarHandleWeakcompareandsetStaticFieldLittleEndianStringPerfTest {
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final String FIELD_VALUE = "qwerty";
     static String sField = FIELD_VALUE;
     VarHandle mVh;
@@ -44,7 +46,7 @@
     @Test
     public void run() {
         boolean success;
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
             success = mVh.weakCompareAndSet(sField, null);
             success = mVh.weakCompareAndSet(sField, "qwerty");
diff --git a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
index cfcb1d2..a43569a 100755
--- a/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
+++ b/apct-tests/perftests/core/src/android/libcore/varhandles/generate_java.py
@@ -42,7 +42,7 @@
     return ''.join(c for c in word.title() if not c == '_')
 
 
-LOOP ="BenchmarkState state = mPerfStatusReporter.getBenchmarkState();\n        while (state.keepRunning())"
+LOOP ="final BenchmarkState state = mBenchmarkRule.getState();\n        while (state.keepRunning())"
 
 class Benchmark:
     def __init__(self, code, static, vartype, flavour, klass, method, memloc,
@@ -158,8 +158,8 @@
 VH_IMPORTS = """
 package android.libcore.varhandles;
 
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
+import androidx.benchmark.BenchmarkState;
+import androidx.benchmark.junit4.BenchmarkRule;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -179,7 +179,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class {name} {{
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final {vartype} FIELD_VALUE = {value1};
     {static_kwd}{vartype} {static_prefix}Field = FIELD_VALUE;
     VarHandle mVh;
@@ -273,7 +273,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class {name} {{
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final {vartype} ELEMENT_VALUE = {value1};
     {vartype}[] mArray = {{ ELEMENT_VALUE }};
     VarHandle mVh;
@@ -324,7 +324,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class {name} {{
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     static final {vartype} VALUE = {value1};
     byte[] mArray1 = {value1_byte_array};
     byte[] mArray2 = {value2_byte_array};
@@ -375,7 +375,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class {name} {{
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     Field mField;
     {static_kwd}{vartype} {static_prefix}Value;
 
@@ -407,7 +407,7 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class {name} {{
-    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    @Rule public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
     long mOffset;
     public {static_kwd}{vartype} {static_prefix}Value = {value1};
 
diff --git a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
index fbe67a4..c34936f 100644
--- a/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/VariableFontPerfTest.java
@@ -19,6 +19,7 @@
 import android.graphics.Paint;
 import android.graphics.RecordingCanvas;
 import android.graphics.RenderNode;
+import android.graphics.Typeface;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 
@@ -120,13 +121,34 @@
     public void testSetFontVariationSettings() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final Paint paint = new Paint(PAINT);
-        final Random random = new Random(0);
         while (state.keepRunning()) {
             state.pauseTiming();
-            int weight = random.nextInt(1000);
+            paint.setTypeface(null);
+            paint.setFontVariationSettings(null);
+            Typeface.clearTypefaceCachesForTestingPurpose();
             state.resumeTiming();
 
-            paint.setFontVariationSettings("'wght' " + weight);
+            paint.setFontVariationSettings("'wght' 450");
         }
+        Typeface.clearTypefaceCachesForTestingPurpose();
     }
+
+    @Test
+    public void testSetFontVariationSettings_Cached() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final Paint paint = new Paint(PAINT);
+        Typeface.clearTypefaceCachesForTestingPurpose();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            paint.setTypeface(null);
+            paint.setFontVariationSettings(null);
+            state.resumeTiming();
+
+            paint.setFontVariationSettings("'wght' 450");
+        }
+
+        Typeface.clearTypefaceCachesForTestingPurpose();
+    }
+
 }
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 856dba3..9eea712 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -45,3 +45,8 @@
         "trace_configs/trace_config_multi_user.textproto",
     ],
 }
+
+prebuilt_etc {
+    name: "trace_config_multi_user.textproto",
+    src: ":multi_user_trace_config",
+}
diff --git a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
index c00c8d5..06cd942 100644
--- a/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
+++ b/apct-tests/perftests/settingsprovider/src/android/provider/SettingsProviderPerfTest.java
@@ -36,7 +36,7 @@
 
 @RunWith(AndroidJUnit4.class)
 public final class SettingsProviderPerfTest {
-    private static final String NAMESPACE = "test@namespace";
+    private static final String NAMESPACE = "testing";
     private static final String SETTING_NAME1 = "test:setting1";
     private static final String SETTING_NAME2 = "test-setting2";
     private static final String UNSET_SETTING = "test_unset_setting";
diff --git a/apct-tests/perftests/tracing/Android.bp b/apct-tests/perftests/tracing/Android.bp
new file mode 100644
index 0000000..08e365b
--- /dev/null
+++ b/apct-tests/perftests/tracing/Android.bp
@@ -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.
+
+android_test {
+    name: "ProtologPerfTests",
+    team: "trendy_team_windowing_tools",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+        "collector-device-lib",
+        "platform-test-annotations",
+    ],
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
+    data: [":perfetto_artifacts"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/apct-tests/perftests/tracing/AndroidManifest.xml b/apct-tests/perftests/tracing/AndroidManifest.xml
new file mode 100644
index 0000000..68125df
--- /dev/null
+++ b/apct-tests/perftests/tracing/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.perftests.protolog">
+
+    <!-- For perfetto trace files -->
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.protolog">
+        <!-- <meta-data android:name="listener" android:value="android.protolog.ProtologPerfRunListener" /> -->
+    </instrumentation>
+</manifest>
diff --git a/apct-tests/perftests/tracing/AndroidTest.xml b/apct-tests/perftests/tracing/AndroidTest.xml
new file mode 100644
index 0000000..871a20c
--- /dev/null
+++ b/apct-tests/perftests/tracing/AndroidTest.xml
@@ -0,0 +1,67 @@
+<?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 ProtologPerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ProtologPerfTests.apk" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <option name="force-skip-system-props" value="true" />
+        <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+        <option name="run-command" value="cmd window dismiss-keyguard" />
+        <option name="run-command" value="cmd package compile -m speed com.android.perftests.wm" />
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+    </target_preparer>
+
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.protolog" />
+        <option name="hidden-api-checks" value="false"/>
+
+        <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
+        <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+
+        <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+        <!-- ProcLoadListener related arguments -->
+        <!-- Wait for device last minute threshold to reach 3 with 2 minute timeout before starting the test run -->
+        <option name="instrumentation-arg" key="procload-collector:per_run" value="true" />
+        <option name="instrumentation-arg" key="proc-loadavg-threshold" value="3" />
+        <option name="instrumentation-arg" key="proc-loadavg-timeout" value="120000" />
+        <option name="instrumentation-arg" key="proc-loadavg-interval" value="10000" />
+
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+        <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+    </test>
+
+    <!-- <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/local/tmp/ProtologPerfTests" /> -->
+        <!-- Needed for pulling the collected trace config on to the host -->
+        <!-- <option name="pull-pattern-keys" value="perfetto_file_path" />
+    </metrics_collector> -->
+</configuration>
diff --git a/apct-tests/perftests/tracing/OWNERS b/apct-tests/perftests/tracing/OWNERS
new file mode 100644
index 0000000..3f3308c
--- /dev/null
+++ b/apct-tests/perftests/tracing/OWNERS
@@ -0,0 +1 @@
+include platform/development:/tools/winscope/OWNERS
diff --git a/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java
new file mode 100644
index 0000000..f4759b8
--- /dev/null
+++ b/apct-tests/perftests/tracing/src/com/android/internal/protolog/ProtoLogPerfTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.protolog;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.perftests.utils.Stats;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogLevel;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public class ProtoLogPerfTest {
+    private final boolean mLogToProto;
+    private final boolean mLogToLogcat;
+
+    /**
+     * Generates the parameters used for this test class
+     */
+    @Parameters(name = "LOG_TO_{0}")
+    public static Collection<Object[]> params() {
+        var params = new ArrayList<Object[]>();
+
+        for (LogTo logTo : LogTo.values()) {
+            params.add(new Object[] { logTo });
+        }
+
+        return params;
+    }
+
+    public ProtoLogPerfTest(LogTo logTo) {
+        mLogToProto = switch (logTo) {
+            case ALL, PROTO_ONLY -> true;
+            case LOGCAT_ONLY, NONE -> false;
+        };
+
+        mLogToLogcat = switch (logTo) {
+            case ALL, LOGCAT_ONLY -> true;
+            case PROTO_ONLY, NONE -> false;
+        };
+    }
+
+    @BeforeClass
+    public static void init() {
+        ProtoLog.init(TestProtoLogGroup.values());
+    }
+
+    @Before
+    public void setUp() {
+        TestProtoLogGroup.TEST_GROUP.setLogToProto(mLogToProto);
+        TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat);
+    }
+
+    @Test
+    public void log_Processed_NoArgs() {
+        final var protoLog = ProtoLog.getSingleInstance();
+        final var perfMonitor = new PerfMonitor();
+
+        while (perfMonitor.keepRunning()) {
+            protoLog.log(
+                    LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
+                    0, (Object[]) null);
+        }
+    }
+
+    @Test
+    public void log_Processed_WithArgs() {
+        final var protoLog = ProtoLog.getSingleInstance();
+        final var perfMonitor = new PerfMonitor();
+
+        while (perfMonitor.keepRunning()) {
+            protoLog.log(
+                    LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 123,
+                    0b1110101001010100,
+                    new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+        }
+    }
+
+    @Test
+    public void log_Unprocessed_NoArgs() {
+        final var perfMonitor = new PerfMonitor();
+
+        while (perfMonitor.keepRunning()) {
+            ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message");
+        }
+    }
+
+    @Test
+    public void log_Unprocessed_WithArgs() {
+        final var perfMonitor = new PerfMonitor();
+
+        while (perfMonitor.keepRunning()) {
+            ProtoLog.d(TestProtoLogGroup.TEST_GROUP, "Test message %s, %d, %b", "arg1", 2, true);
+        }
+    }
+
+    private class PerfMonitor {
+        private int mIteration = 0;
+
+        private static final int WARM_UP_ITERATIONS = 10;
+        private static final int ITERATIONS = 1000;
+
+        private final ArrayList<Long> mResults = new ArrayList<>();
+
+        private long mStartTimeNs;
+
+        public boolean keepRunning() {
+            final long currentTime = System.nanoTime();
+
+            mIteration++;
+
+            if (mIteration > ITERATIONS) {
+                reportResults();
+                return false;
+            }
+
+            if (mIteration > WARM_UP_ITERATIONS) {
+                mResults.add(currentTime - mStartTimeNs);
+            }
+
+            mStartTimeNs = System.nanoTime();
+            return true;
+        }
+
+        public void reportResults() {
+            final var stats = new Stats(mResults);
+
+            Bundle status = new Bundle();
+            status.putLong("protologging_time_mean_ns", (long) stats.getMean());
+            status.putLong("protologging_time_median_ns", (long) stats.getMedian());
+            InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
+        }
+    }
+
+    private enum TestProtoLogGroup implements IProtoLogGroup {
+        TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+        private final boolean mEnabled;
+        private volatile boolean mLogToProto;
+        private volatile boolean mLogToLogcat;
+        private final String mTag;
+
+        /**
+         * @param enabled set to false to exclude all log statements for this group from
+         *     compilation, they will not be available in runtime.
+         * @param logToProto enable binary logging for the group
+         * @param logToLogcat enable text logging for the group
+         * @param tag name of the source of the logged message
+         */
+        TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+            this.mEnabled = enabled;
+            this.mLogToProto = logToProto;
+            this.mLogToLogcat = logToLogcat;
+            this.mTag = tag;
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return mEnabled;
+        }
+
+        @Override
+        public boolean isLogToProto() {
+            return mLogToProto;
+        }
+
+        @Override
+        public boolean isLogToLogcat() {
+            return mLogToLogcat;
+        }
+
+        @Override
+        public boolean isLogToAny() {
+            return mLogToLogcat || mLogToProto;
+        }
+
+        @Override
+        public String getTag() {
+            return mTag;
+        }
+
+        @Override
+        public void setLogToProto(boolean logToProto) {
+            this.mLogToProto = logToProto;
+        }
+
+        @Override
+        public void setLogToLogcat(boolean logToLogcat) {
+            this.mLogToLogcat = logToLogcat;
+        }
+
+        @Override
+        public int getId() {
+            return ordinal();
+        }
+    }
+
+    private enum LogTo {
+        PROTO_ONLY,
+        LOGCAT_ONLY,
+        ALL,
+        NONE,
+    }
+}
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
index 6d3c0d7..5157ce4 100644
--- a/apex/blobstore/TEST_MAPPING
+++ b/apex/blobstore/TEST_MAPPING
@@ -7,12 +7,7 @@
       "name": "CtsBlobStoreHostTestCases"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.blob"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_blob"
     }
   ]
 }
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index e489c1a..e5389b4 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -48,3 +48,30 @@
        purpose: PURPOSE_BUGFIX
    }
 }
+
+flag {
+   name: "create_work_chain_by_default"
+   namespace: "backstage_power"
+   description: "Create a workchain by default when acquiring a wakelock"
+   bug: "352676818"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
+
+flag {
+   name: "remove_user_during_user_switch"
+   namespace: "backstage_power"
+   description: "Remove started user if user will be stopped due to user switch"
+   bug: "321598070"
+}
+
+flag {
+   name: "use_correct_process_state_for_logging"
+   namespace: "backstage_power"
+   description: "Use correct process state for statsd logging"
+   bug: "361308212"
+   metadata {
+       purpose: PURPOSE_BUGFIX
+   }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index c2aeada..c1894f0 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -2629,8 +2629,13 @@
             for (int i=0; i<allowPowerExceptIdle.size(); i++) {
                 String pkg = allowPowerExceptIdle.valueAt(i);
                 try {
+                    // On some devices (eg. HSUM), some apps may
+                    // be not be pre-installed on user 0, but may be
+                    // pre-installed on FULL users. Look for pre-installed system
+                    // apps across all users to make sure they're properly
+                    // allowlisted.
                     ApplicationInfo ai = pm.getApplicationInfo(pkg,
-                            PackageManager.MATCH_SYSTEM_ONLY);
+                            PackageManager.MATCH_ANY_USER | PackageManager.MATCH_SYSTEM_ONLY);
                     int appid = UserHandle.getAppId(ai.uid);
                     mPowerSaveWhitelistAppsExceptIdle.put(ai.packageName, appid);
                     mPowerSaveWhitelistSystemAppIdsExceptIdle.put(appid, true);
@@ -2641,8 +2646,13 @@
             for (int i=0; i<allowPower.size(); i++) {
                 String pkg = allowPower.valueAt(i);
                 try {
+                    // On some devices (eg. HSUM), some apps may
+                    // be not be pre-installed on user 0, but may be
+                    // pre-installed on FULL users. Look for pre-installed system
+                    // apps across all users to make sure they're properly
+                    // allowlisted.
                     ApplicationInfo ai = pm.getApplicationInfo(pkg,
-                            PackageManager.MATCH_SYSTEM_ONLY);
+                            PackageManager.MATCH_ANY_USER | PackageManager.MATCH_SYSTEM_ONLY);
                     int appid = UserHandle.getAppId(ai.uid);
                     // These apps are on both the whitelist-except-idle as well
                     // as the full whitelist, so they apply in all cases.
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 6924cb2..b58cb88 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -1,23 +1,12 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
-      "file_patterns": [
-        "DeviceIdleController\\.java"
-      ],
-      "options": [
-        {"include-filter": "com.android.server.DeviceIdleControllerTest"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksMockingServicesTests_IdleController",
+      "file_patterns": ["DeviceIdleController\\.java"]
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "file_patterns": ["AppStateTrackerImpl\\.java"],
-      "options": [
-        {"include-filter": "com.android.server.AppStateTrackerTest"},
-        {"include-annotation": "android.platform.test.annotations.Presubmit"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksMockingServicesTests_AppStateTracker",
+      "file_patterns": ["AppStateTrackerImpl\\.java"]
     }
   ],
   "postsubmit": [
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 18ee6f2..0f3b1c3 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -319,7 +319,7 @@
      */
     int mSystemUiUid;
 
-    static boolean isTimeTickAlarm(Alarm a) {
+    private static boolean isTimeTickAlarm(Alarm a) {
         return a.uid == Process.SYSTEM_UID && TIME_TICK_TAG.equals(a.listenerTag);
     }
 
@@ -357,6 +357,7 @@
     }
 
     // TODO(b/172085676): Move inside alarm store.
+    @GuardedBy("mLock")
     private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
             new SparseArray<>();
     private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -2616,6 +2617,13 @@
                 mInFlightListeners.add(callback);
             }
         }
+
+        /** @see AlarmManagerInternal#getNextAlarmTriggerTimeForUser(int) */
+        @Override
+        public long getNextAlarmTriggerTimeForUser(@UserIdInt int userId) {
+            final AlarmManager.AlarmClockInfo nextAlarm = getNextAlarmClockImpl(userId);
+            return nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
+        }
     }
 
     boolean hasUseExactAlarmInternal(String packageName, int uid) {
@@ -3947,6 +3955,9 @@
             if (!RemovedAlarm.isLoggable(reason)) {
                 continue;
             }
+            if (isTimeTickAlarm(removed)) {
+                Slog.wtf(TAG, "Removed TIME_TICK alarm");
+            }
             RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
             if (bufferForUid == null) {
                 bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
@@ -4441,6 +4452,11 @@
         public void run() {
             ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
 
+            synchronized (mLock) {
+                mLastTimeChangeClockTime = mInjector.getCurrentTimeMillis();
+                mLastTimeChangeRealtime = mInjector.getElapsedRealtimeMillis();
+            }
+
             while (true) {
                 int result = mInjector.waitForAlarm();
                 final long nowRTC = mInjector.getCurrentTimeMillis();
@@ -4464,10 +4480,9 @@
                         expectedClockTime = lastTimeChangeClockTime
                                 + (nowELAPSED - mLastTimeChangeRealtime);
                     }
-                    if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime - 1000)
+                    if (nowRTC < (expectedClockTime - 1000)
                             || nowRTC > (expectedClockTime + 1000)) {
-                        // The change is by at least +/- 1000 ms (or this is the first change),
-                        // let's do it!
+                        // The change is by at least +/- 1000 ms, let's do it!
                         if (DEBUG_BATCH) {
                             Slog.v(TAG, "Time changed notification from kernel; rebatching");
                         }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index 0073335..020b510 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -17,11 +17,9 @@
 package com.android.server.alarm;
 
 import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
-import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
 
 import android.app.AlarmManager;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -88,11 +86,6 @@
                 if (removed.alarmClock != null && mOnAlarmClockRemoved != null) {
                     mOnAlarmClockRemoved.run();
                 }
-                if (isTimeTickAlarm(removed)) {
-                    // This code path is not invoked when delivering alarms, only when removing
-                    // alarms due to the caller cancelling it or getting uninstalled, etc.
-                    Slog.wtf(TAG, "Removed TIME_TICK alarm");
-                }
                 removedAlarms.add(removed);
             }
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/alarm/TEST_MAPPING
index d76ce74..ab0f178 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "FrameworksMockingServicesTests",
-            "options": [
-                {
-                  "include-filter": "com.android.server.alarm"
-                },
-                {
-                  "include-annotation": "android.platform.test.annotations.Presubmit"
-                },
-                {
-                  "exclude-annotation": "androidx.test.filters.FlakyTest"
-                }
-            ]
+            "name": "FrameworksMockingServicesTests_com_android_server_alarm"
         }
     ],
 
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index c0686116..afa509c 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -1,11 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.DeviceIdleControllerTest"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksMockingServicesTests_IdleController"
     }
   ],
   "postsubmit": [
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ff73a49..97c6e25 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1617,10 +1617,11 @@
 
     @NonNull
     public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
-        if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+        if (Flags.createWorkChainByDefault()
+                || WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
             WorkSource ws = new WorkSource();
             ws.createWorkChain()
-                    .addNode(sourceUid, sourcePackageName)
+                    .addNode(sourceUid, null)
                     .addNode(Process.SYSTEM_UID, "JobScheduler");
             return ws;
         } else {
@@ -1668,6 +1669,20 @@
     }
 
     @Override
+    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+        if (!Flags.removeUserDuringUserSwitch()
+                || from == null
+                || !mActivityManagerInternal.isEarlyPackageKillEnabledForUserSwitch(
+                                                                from.getUserIdentifier(),
+                                                                to.getUserIdentifier())) {
+            return;
+        }
+        synchronized (mLock) {
+            mStartedUsers = ArrayUtils.removeInt(mStartedUsers, from.getUserIdentifier());
+        }
+    }
+
+    @Override
     public void onUserStopping(@NonNull TargetUser user) {
         synchronized (mLock) {
             mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
@@ -5793,9 +5808,10 @@
 
     static void dumpHelp(PrintWriter pw) {
         pw.println("Job Scheduler (jobscheduler) dump options:");
-        pw.println("  [-h] [package] ...");
+        pw.println("  [-h] [package] [--proto] ...");
         pw.println("    -h: print this help");
         pw.println("  [package] is an optional package name to limit the output to.");
+        pw.println("    --proto: output dump in protocol buffer format.");
     }
 
     /** Sort jobs by caller UID, then by Job ID. */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d65a66c..be8e304 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -473,6 +473,14 @@
             mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid());
             mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid());
 
+            int procState = mService.getUidProcState(job.getUid());
+            if (Flags.useCorrectProcessStateForLogging()
+                    && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+                // Try to get the latest proc state from AMS, there might be some delay
+                // for the proc states worse than TRANSIENT_BACKGROUND.
+                procState = mActivityManagerInternal.getUidProcessState(job.getUid());
+            }
+
             FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                     job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
                     // Given that the source tag is set by the calling app, it should be connected
@@ -517,7 +525,7 @@
                     job.getEstimatedNetworkDownloadBytes(),
                     job.getEstimatedNetworkUploadBytes(),
                     job.getWorkCount(),
-                    ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
+                    ActivityManager.processStateAmToProto(procState),
                     job.getNamespaceHash(),
                     /* system_measured_source_download_bytes */ 0,
                     /* system_measured_source_upload_bytes */ 0,
@@ -1528,6 +1536,13 @@
         mJobPackageTracker.noteInactive(completedJob,
                 loggingInternalStopReason, loggingDebugReason);
         final int sourceUid = completedJob.getSourceUid();
+        int procState = mService.getUidProcState(completedJob.getUid());
+        if (Flags.useCorrectProcessStateForLogging()
+                && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+            // Try to get the latest proc state from AMS, there might be some delay
+            // for the proc states worse than TRANSIENT_BACKGROUND.
+            procState = mActivityManagerInternal.getUidProcessState(completedJob.getUid());
+        }
         FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                 completedJob.isProxyJob()
                         ? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid},
@@ -1573,7 +1588,7 @@
                 completedJob.getEstimatedNetworkUploadBytes(),
                 completedJob.getWorkCount(),
                 ActivityManager
-                        .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
+                        .processStateAmToProto(procState),
                 completedJob.getNamespaceHash(),
                 TrafficStats.getUidRxBytes(completedJob.getSourceUid())
                         - mInitialDownloadedBytesFromSource,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e82df12..16c2fd4 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -8,20 +8,10 @@
             ]
         },
         {
-            "name": "FrameworksMockingServicesTests",
-            "options": [
-                {"include-filter": "com.android.server.job"},
-                {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-                {"exclude-annotation": "androidx.test.filters.LargeTest"}
-            ]
+            "name": "FrameworksMockingServicesTests_com_android_server_job_Presubmit"
         },
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {"include-filter": "com.android.server.job"},
-                {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-                {"exclude-annotation": "androidx.test.filters.LargeTest"}
-            ]
+            "name": "FrameworksServicesTests_com_android_server_job_Presubmit"
         }
     ],
     "postsubmit": [
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index d92351d..c9d3407 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1989,10 +1989,8 @@
                 mAdminProtectedPackages.put(userId, packageNames);
             }
         }
-        if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
-            if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) {
-                postCheckIdleStates(userId);
-            }
+        if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) {
+            postCheckIdleStates(userId);
         }
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index a75415e..52670a2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -17,11 +17,7 @@
       ]
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.usage"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksServicesTests_com_android_server_usage_Presubmit"
     }
   ],
   "postsubmit": [
diff --git a/api/Android.bp b/api/Android.bp
index 341be3d53..533f9f6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -87,6 +87,7 @@
         "framework-permission",
         "framework-permission-s",
         "framework-profiling",
+        "framework-photopicker",
         "framework-scheduling",
         "framework-sdkextensions",
         "framework-statsd",
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index d991da5..b3a674f 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -890,7 +890,7 @@
     cmd: "rm -f $(genDir)/framework.aidl.merged && " +
         "for i in $(in); do " +
         "  rm -f $(genDir)/framework.aidl.tmp && " +
-        "  $(location sdkparcelables) $$i $(genDir)/framework.aidl.tmp && " +
+        "  $(location sdkparcelables) $$i $(genDir)/framework.aidl.tmp --guarantee_stable && " +
         "  cat $(genDir)/framework.aidl.tmp >> $(genDir)/framework.aidl.merged; " +
         "done && " +
         "sort -u $(genDir)/framework.aidl.merged > $(out)",
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 7e4f95b..01b20f4 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#include <android/bitmap.h>
+#include <android/gui/DisplayCaptureArgs.h>
+#include <binder/ProcessState.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdio.h>
@@ -33,7 +36,6 @@
 
 #include <ftl/concat.h>
 #include <ftl/optional.h>
-#include <gui/DisplayCaptureArgs.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <gui/SyncScreenCaptureListener.h>
diff --git a/cmds/uinput/examples/test-touchpad.evemu b/cmds/uinput/examples/test-touchpad.evemu
new file mode 100644
index 0000000..34ee572
--- /dev/null
+++ b/cmds/uinput/examples/test-touchpad.evemu
@@ -0,0 +1,44 @@
+# EVEMU 1.2
+# This is an evemu "recording" of an Apple Magic Trackpad (1st generation), but
+# that doesn't actually make any movements. It just runs for a very long time,
+# to make Android think a touchpad is connected. This is useful for testing
+# things like the settings in System > Touchpad, which only appear when one is
+# connected.
+#
+# It can be played by piping it to the uinput command over ADB:
+#     $ adb shell uinput - < test-touchpad.evemu
+N: Fake touchpad
+I: 0005 05ac 030e 0160
+P: 05 00 00 00 00 00 00 00
+B: 00 0b 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 01 00 00 00 00 00
+B: 01 20 e5 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 02 00 00 00 00 00 00 00 00
+B: 03 03 00 00 00 00 80 73 02
+B: 04 10 00 00 00 00 00 00 00
+B: 05 00 00 00 00 00 00 00 00
+B: 11 00 00 00 00 00 00 00 00
+B: 12 00 00 00 00 00 00 00 00
+A: 00 -2909 3167 4 0 46
+A: 01 -2456 2565 4 0 45
+A: 2f 0 15 0 0 0
+A: 30 0 1020 4 0 0
+A: 31 0 1020 4 0 0
+A: 34 -31 32 1 0 0
+A: 35 -2909 3167 4 0 46
+A: 36 -2456 2565 4 0 45
+A: 39 0 65535 0 0 0
+E: 0.000001 0004 0005 1234
+E: 0.000001 0000 0000 0000
+E: 1000000000.000000 0004 0005 1235
+E: 1000000000.000000 0000 0000 0000
diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING
index fd571c9..b78659c 100644
--- a/core/TEST_MAPPING
+++ b/core/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.inputmethod"
-        },
-        {
-          "include-filter": "com.android.internal.inputmethod"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_inputmethod",
       "file_patterns": [
         "core/java/com/android/internal/inputmethod/.*",
         "core/java/android/view/inputmethod/.*",
diff --git a/core/api/current.txt b/core/api/current.txt
index ea039a7..5685221 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26,6 +26,7 @@
     field public static final String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE";
     field public static final String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
     field public static final String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
     field public static final String BIND_CARRIER_MESSAGING_CLIENT_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE";
@@ -140,7 +141,7 @@
     field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
     field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
     field public static final String MANAGE_DEVICE_POLICY_APP_USER_DATA = "android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA";
-    field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
+    field public static final String MANAGE_DEVICE_POLICY_ASSIST_CONTENT = "android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT";
     field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT";
     field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL";
     field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
@@ -168,7 +169,7 @@
     field public static final String MANAGE_DEVICE_POLICY_LOCK = "android.permission.MANAGE_DEVICE_POLICY_LOCK";
     field public static final String MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS = "android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS";
     field public static final String MANAGE_DEVICE_POLICY_LOCK_TASK = "android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK";
-    field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
+    field public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
     field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA";
     field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE";
     field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
@@ -6844,9 +6845,6 @@
     method public android.app.Notification.MessagingStyle.Message setData(String, android.net.Uri);
   }
 
-  @FlaggedApi("android.app.api_rich_ongoing") public abstract static class Notification.RichOngoingStyle extends android.app.Notification.Style {
-  }
-
   public abstract static class Notification.Style {
     ctor @Deprecated public Notification.Style();
     method public android.app.Notification build();
@@ -7877,7 +7875,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DeviceAdminInfo> CREATOR;
     field public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1; // 0x1
-    field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; // 0x2
+    field public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2; // 0x2
     field public static final int HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED = 0; // 0x0
     field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
     field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
@@ -7966,13 +7964,13 @@
     field public static final String LOCK_TASK_POLICY = "lockTask";
     field public static final String PACKAGES_SUSPENDED_POLICY = "packagesSuspended";
     field public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
-    field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
+    field public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
     field public static final String PERMISSION_GRANT_POLICY = "permissionGrant";
     field public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY = "persistentPreferredActivity";
     field public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
-    field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String SECURITY_LOGGING_POLICY = "securityLogging";
+    field public static final String SECURITY_LOGGING_POLICY = "securityLogging";
     field public static final String STATUS_BAR_DISABLED_POLICY = "statusBarDisabled";
-    field @FlaggedApi("android.app.admin.flags.policy_engine_migration_v2_enabled") public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
+    field public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
     field public static final String USER_CONTROL_DISABLED_PACKAGES_POLICY = "userControlDisabledPackages";
   }
 
@@ -8083,7 +8081,7 @@
     method public CharSequence getStartUserSessionMessage(@NonNull android.content.ComponentName);
     method @Deprecated public boolean getStorageEncryption(@Nullable android.content.ComponentName);
     method public int getStorageEncryptionStatus();
-    method @FlaggedApi("android.app.admin.flags.esim_management_enabled") @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS) public java.util.Set<java.lang.Integer> getSubscriptionIds();
     method @Nullable public android.app.admin.SystemUpdatePolicy getSystemUpdatePolicy();
     method @Nullable public android.os.PersistableBundle getTransferOwnershipBundle();
     method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
@@ -8721,6 +8719,58 @@
 
 }
 
+package android.app.appfunctions {
+
+  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager {
+    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 java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+  }
+
+  @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 @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>);
+    field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
+  }
+
+  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.os.Bundle getExtras();
+    method @NonNull public String getFunctionIdentifier();
+    method @NonNull public android.app.appsearch.GenericDocument getParameters();
+    method @NonNull public String getTargetPackageName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionRequest> CREATOR;
+  }
+
+  public static final class ExecuteAppFunctionRequest.Builder {
+    ctor public ExecuteAppFunctionRequest.Builder(@NonNull String, @NonNull String);
+    method @NonNull public android.app.appfunctions.ExecuteAppFunctionRequest build();
+    method @NonNull public android.app.appfunctions.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle);
+    method @NonNull public android.app.appfunctions.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument);
+  }
+
+  @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getErrorMessage();
+    method @NonNull public android.os.Bundle getExtras();
+    method public int getResultCode();
+    method @NonNull public android.app.appsearch.GenericDocument getResultDocument();
+    method public boolean isSuccess();
+    method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle);
+    method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR;
+    field public static final String PROPERTY_RETURN_VALUE = "returnValue";
+    field public static final int RESULT_APP_UNKNOWN_ERROR = 2; // 0x2
+    field public static final int RESULT_DENIED = 1; // 0x1
+    field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
+    field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
+    field public static final int RESULT_OK = 0; // 0x0
+    field public static final int RESULT_TIMED_OUT = 5; // 0x5
+  }
+
+}
+
 package android.app.assist {
 
   public class AssistContent implements android.os.Parcelable {
@@ -9820,6 +9870,7 @@
     field public static final int RESULT_DISCOVERY_TIMEOUT = 2; // 0x2
     field public static final int RESULT_INTERNAL_ERROR = 3; // 0x3
     field public static final int RESULT_OK = -1; // 0xffffffff
+    field @FlaggedApi("android.companion.association_failure_code") public static final int RESULT_SECURITY_ERROR = 4; // 0x4
     field public static final int RESULT_USER_REJECTED = 1; // 0x1
   }
 
@@ -9829,7 +9880,7 @@
     method public void onAssociationPending(@NonNull android.content.IntentSender);
     method @Deprecated public void onDeviceFound(@NonNull android.content.IntentSender);
     method public abstract void onFailure(@Nullable CharSequence);
-    method @FlaggedApi("android.companion.association_failure_code") public void onFailure(int);
+    method @FlaggedApi("android.companion.association_failure_code") public void onFailure(int, @Nullable CharSequence);
   }
 
   public abstract class CompanionDeviceService extends android.app.Service {
@@ -10684,6 +10735,7 @@
     field public static final String ACTIVITY_SERVICE = "activity";
     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";
     field public static final String APP_OPS_SERVICE = "appops";
     field public static final String APP_SEARCH_SERVICE = "app_search";
     field public static final String AUDIO_SERVICE = "audio";
@@ -34059,7 +34111,7 @@
     field public static final String DISALLOW_AIRPLANE_MODE = "no_airplane_mode";
     field public static final String DISALLOW_AMBIENT_DISPLAY = "no_ambient_display";
     field public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
-    field @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
+    field public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
     field public static final String DISALLOW_AUTOFILL = "no_autofill";
     field public static final String DISALLOW_BLUETOOTH = "no_bluetooth";
     field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
@@ -34109,7 +34161,7 @@
     field public static final String DISALLOW_SHARE_INTO_MANAGED_PROFILE = "no_sharing_into_profile";
     field public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi";
-    field @FlaggedApi("android.app.admin.flags.esim_management_enabled") public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally";
+    field public static final String DISALLOW_SIM_GLOBALLY = "no_sim_globally";
     field public static final String DISALLOW_SMS = "no_sms";
     field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
     field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
@@ -43912,7 +43964,10 @@
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool";
     field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
-    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_screen_off_inactivity_timeout_duration_sec_int";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool";
+    field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int";
     field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
     field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
     field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
@@ -46044,7 +46099,7 @@
     method public int getCardIdForDefaultEuicc();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
     method public int getCarrierIdFromSimMccMnc();
-    method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void getCarrierRestrictionStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+    method @RequiresPermission(anyOf={android.Manifest.permission.READ_BASIC_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public void getCarrierRestrictionStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public int getDataNetworkType();
@@ -54865,6 +54920,8 @@
     method @Deprecated public void addAction(int);
     method public void addChild(android.view.View);
     method public void addChild(android.view.View, int);
+    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void addLabeledBy(@NonNull android.view.View);
+    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void addLabeledBy(@NonNull android.view.View, int);
     method public boolean canOpenPopup();
     method public int describeContents();
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String);
@@ -54893,6 +54950,7 @@
     method public int getInputType();
     method public android.view.accessibility.AccessibilityNodeInfo getLabelFor();
     method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy();
+    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") @NonNull public java.util.List<android.view.accessibility.AccessibilityNodeInfo> getLabeledByList();
     method public int getLiveRegion();
     method public int getMaxTextLength();
     method @NonNull public java.time.Duration getMinDurationBetweenContentChanges();
@@ -54953,6 +55011,8 @@
     method public boolean removeAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction);
     method public boolean removeChild(android.view.View);
     method public boolean removeChild(android.view.View, int);
+    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public boolean removeLabeledBy(@NonNull android.view.View);
+    method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public boolean removeLabeledBy(@NonNull android.view.View, int);
     method public void setAccessibilityDataSensitive(boolean);
     method public void setAccessibilityFocused(boolean);
     method public void setAvailableExtraData(java.util.List<java.lang.String>);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 445a572..ba16037 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -61,6 +61,7 @@
     field @FlaggedApi("com.android.internal.telephony.flags.use_oem_domain_selection_service") public static final String BIND_DOMAIN_SELECTION_SERVICE = "android.permission.BIND_DOMAIN_SELECTION_SERVICE";
     field public static final String BIND_DOMAIN_VERIFICATION_AGENT = "android.permission.BIND_DOMAIN_VERIFICATION_AGENT";
     field public static final String BIND_EUICC_SERVICE = "android.permission.BIND_EUICC_SERVICE";
+    field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE";
     field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE";
@@ -139,6 +140,8 @@
     field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE";
     field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES";
     field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED";
+    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS = "android.permission.EXECUTE_APP_FUNCTIONS";
+    field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXECUTE_APP_FUNCTIONS_TRUSTED = "android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED";
     field public static final String EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS = "android.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS";
     field public static final String FORCE_BACK = "android.permission.FORCE_BACK";
     field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
@@ -199,7 +202,7 @@
     field public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS";
     field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
-    field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
+    field public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
     field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
     field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
@@ -1294,7 +1297,7 @@
   }
 
   public final class DevicePolicyIdentifiers {
-    field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String AUDIT_LOGGING_POLICY = "auditLogging";
+    field public static final String AUDIT_LOGGING_POLICY = "auditLogging";
   }
 
   public class DevicePolicyKeyguardService extends android.app.Service {
@@ -1306,7 +1309,7 @@
 
   public class DevicePolicyManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPrecondition(@NonNull String, @NonNull String);
-    method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void clearAuditLogEventCallback();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void clearAuditLogEventCallback();
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
     method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void finalizeWorkProfileProvisioning(@NonNull android.os.UserHandle, @Nullable android.accounts.Account);
@@ -1326,7 +1329,7 @@
     method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
-    method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
     method public boolean isDeviceManaged();
     method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE) public boolean isDevicePotentiallyStolen();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
@@ -1342,8 +1345,8 @@
     method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException;
-    method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean);
-    method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
     method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
@@ -1420,7 +1423,7 @@
     field public static final int STATUS_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
     field public static final int STATUS_HAS_DEVICE_OWNER = 1; // 0x1
     field public static final int STATUS_HAS_PAIRED = 8; // 0x8
-    field @FlaggedApi("android.app.admin.flags.headless_device_owner_single_user_enabled") public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; // 0x11
+    field public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17; // 0x11
     field public static final int STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED = 16; // 0x10
     field public static final int STATUS_MANAGED_USERS_NOT_SUPPORTED = 9; // 0x9
     field public static final int STATUS_NONSYSTEM_USER_EXISTS = 5; // 0x5
@@ -3432,6 +3435,23 @@
 
 package android.companion.virtual {
 
+  @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public final class ActivityPolicyExemption implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public android.content.ComponentName getComponentName();
+    method public int getDisplayId();
+    method @Nullable public String getPackageName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.ActivityPolicyExemption> CREATOR;
+  }
+
+  @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final class ActivityPolicyExemption.Builder {
+    ctor public ActivityPolicyExemption.Builder();
+    method @NonNull public android.companion.virtual.ActivityPolicyExemption build();
+    method @NonNull public android.companion.virtual.ActivityPolicyExemption.Builder setComponentName(@NonNull android.content.ComponentName);
+    method @NonNull public android.companion.virtual.ActivityPolicyExemption.Builder setDisplayId(int);
+    method @NonNull public android.companion.virtual.ActivityPolicyExemption.Builder setPackageName(@NonNull String);
+  }
+
   public final class VirtualDevice implements android.os.Parcelable {
     method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomAudioInputSupport();
     method @FlaggedApi("android.companion.virtual.flags.vdm_public_apis") public boolean hasCustomCameraSupport();
@@ -3448,8 +3468,9 @@
   }
 
   public static interface VirtualDeviceManager.ActivityListener {
-    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, int, @Nullable android.content.IntentSender);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.content.IntentSender);
     method public void onDisplayEmpty(int);
+    method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowShown(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
     method @Deprecated public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
     method public default void onTopActivityChanged(int, @NonNull android.content.ComponentName, int);
   }
@@ -3465,7 +3486,7 @@
   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.content.ComponentName, int);
+    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 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 @NonNull public android.content.Context createContext();
@@ -3490,7 +3511,7 @@
     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 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.content.ComponentName, int);
+    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 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);
@@ -3528,7 +3549,7 @@
     field @Deprecated public static final int NAVIGATION_POLICY_DEFAULT_BLOCKED = 1; // 0x1
     field @FlaggedApi("android.companion.virtual.flags.dynamic_policy") public static final int POLICY_TYPE_ACTIVITY = 3; // 0x3
     field public static final int POLICY_TYPE_AUDIO = 1; // 0x1
-    field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6; // 0x6
+    field @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6; // 0x6
     field @FlaggedApi("android.companion.virtual.flags.virtual_camera") public static final int POLICY_TYPE_CAMERA = 5; // 0x5
     field @FlaggedApi("android.companion.virtual.flags.cross_device_clipboard") public static final int POLICY_TYPE_CLIPBOARD = 4; // 0x4
     field public static final int POLICY_TYPE_RECENTS = 2; // 0x2
@@ -3675,9 +3696,11 @@
     method public int getMinDelay();
     method @NonNull public String getName();
     method public float getPower();
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public int getReportingMode();
     method public float getResolution();
     method public int getType();
     method @Nullable public String getVendor();
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public boolean isWakeUpSensor();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensorConfig> CREATOR;
   }
@@ -3691,8 +3714,10 @@
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMaximumRange(float);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setMinDelay(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setPower(float);
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setReportingMode(int);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setResolution(float);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
+    method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setWakeUpSensor(boolean);
   }
 
   public interface VirtualSensorDirectChannelCallback {
@@ -4596,6 +4621,8 @@
     method @NonNull public String getPackageName();
     method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackFrom();
     method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackTo();
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public boolean isApex();
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public boolean isApkInApex();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR;
   }
@@ -4606,6 +4633,7 @@
     method public int getCommittedSessionId();
     method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages();
     method public int getRollbackId();
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public int getRollbackImpactLevel();
     method public boolean isStaged();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR;
@@ -6721,6 +6749,14 @@
     field public static final int STATUS_OK = 0; // 0x0
   }
 
+  @FlaggedApi("android.media.soundtrigger.generic_model_api") public static final class SoundTrigger.GenericSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
+    ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], int);
+    ctor public SoundTrigger.GenericSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[]);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.GenericSoundModel> CREATOR;
+  }
+
   public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
     ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
     method public int describeContents();
@@ -8062,6 +8098,7 @@
   public class Tuner implements java.lang.AutoCloseable {
     ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @Nullable String, int);
     method public int applyFrontend(@NonNull android.media.tv.tuner.frontend.FrontendInfo);
+    method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @RequiresPermission(allOf={"android.permission.TUNER_RESOURCE_ACCESS", "android.permission.ACCESS_TV_TUNER"}) public int applyFrontendByType(int);
     method public int cancelScanning();
     method public int cancelTuning();
     method public void clearOnTuneEventListener();
@@ -8138,6 +8175,7 @@
     field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001
     field public static final int TUNER_VERSION_2_0 = 131072; // 0x20000
     field public static final int TUNER_VERSION_3_0 = 196608; // 0x30000
+    field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int TUNER_VERSION_4_0 = 262144; // 0x40000
     field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0
   }
 
@@ -11133,6 +11171,7 @@
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
     method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
     method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
+    method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootPromptAndWipeUserData(@NonNull android.content.Context, @NonNull String) throws java.io.IOException;
     method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
     method @Deprecated public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
@@ -14197,7 +14236,7 @@
     field public static final int CAPABILITY_EMERGENCY_PREFERRED = 8192; // 0x2000
     field public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 512; // 0x200
     field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
-    field public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
+    field @Deprecated @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EXTRA_PLAY_CALL_RECORDING_TONE = "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
     field @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final String EXTRA_SKIP_CALL_FILTERING = "android.telecom.extra.SKIP_CALL_FILTERING";
     field public static final String EXTRA_SORT_ORDER = "android.telecom.extra.SORT_ORDER";
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 90af259..42f7615 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -597,19 +597,19 @@
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
     method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
-    method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
+    method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public void forceSetMaxPolicyStorageLimit(int);
     method public void forceUpdateUserSetupComplete(int);
     method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
     method @Deprecated public int getDeviceOwnerType(@NonNull android.content.ComponentName);
     method @Nullable public String getDevicePolicyManagementRoleHolderUpdaterPackage();
     method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
-    method @FlaggedApi("android.app.admin.flags.headless_device_owner_provisioning_fix_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getHeadlessDeviceOwnerMode();
     method public long getLastBugReportRequestTime();
     method public long getLastNetworkLogRetrievalTime();
     method public long getLastSecurityLogRetrievalTime();
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
-    method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
+    method @RequiresPermission("android.permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT") public int getPolicySizeForAdmin(@NonNull android.app.admin.EnforcingAdmin);
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
@@ -680,7 +680,7 @@
   }
 
   public final class EnforcingAdmin implements android.os.Parcelable {
-    ctor @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_internal_bug_fix_enabled") public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
+    ctor public EnforcingAdmin(@NonNull String, @NonNull android.app.admin.Authority, @NonNull android.os.UserHandle, @Nullable android.content.ComponentName);
   }
 
   public final class FlagUnion extends android.app.admin.ResolutionMechanism<java.lang.Integer> {
@@ -941,7 +941,7 @@
     method public int getAudioRecordingSessionId(int);
     method public int getDeviceIdForDisplayId(int);
     method public int getDevicePolicy(int, int);
-    method @FlaggedApi("android.companion.virtual.flags.interactive_screen_mirror") public boolean isVirtualDeviceOwnedMirrorDisplay(int);
+    method public boolean isVirtualDeviceOwnedMirrorDisplay(int);
     method public void playSoundEffect(int, int);
   }
 
@@ -1269,10 +1269,6 @@
 
 package android.content.rollback {
 
-  public final class RollbackInfo implements android.os.Parcelable {
-    method @FlaggedApi("android.content.pm.recoverability_detection") public int getRollbackImpactLevel();
-  }
-
   public final class RollbackManager {
     method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long);
     method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String);
@@ -1542,6 +1538,10 @@
     method @Deprecated public final void setPreviewSurface(android.view.Surface) throws java.io.IOException;
   }
 
+  public final class Sensor {
+    method public int getHandle();
+  }
+
   public final class SensorPrivacyManager {
     method @FlaggedApi("com.android.internal.camera.flags.camera_privacy_allowlist") @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setCameraPrivacyAllowlist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public void setSensorPrivacy(int, int, boolean);
@@ -1797,11 +1797,15 @@
 
   public class InputSettings {
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") public static int getAccessibilityBounceKeysThreshold(@NonNull android.content.Context);
+    method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysDelay(@NonNull android.content.Context);
+    method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysTimeout(@NonNull android.content.Context);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") public static int getAccessibilitySlowKeysThreshold(@NonNull android.content.Context);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") public static boolean isAccessibilityMouseKeysEnabled(@NonNull android.content.Context);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") public static boolean isAccessibilityStickyKeysEnabled(@NonNull android.content.Context);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityBounceKeysThreshold(@NonNull android.content.Context, int);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityMouseKeysEnabled(@NonNull android.content.Context, boolean);
+    method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysDelay(@NonNull android.content.Context, int);
+    method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysTimeout(@NonNull android.content.Context, int);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilitySlowKeysThreshold(@NonNull android.content.Context, int);
     method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityStickyKeysEnabled(@NonNull android.content.Context, boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float);
@@ -1992,6 +1996,7 @@
     method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
     method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public boolean isVolumeControlUsingVolumeGroups();
+    method public void permissionUpdateBarrier();
     method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int requestAudioFocusForTest(@NonNull android.media.AudioFocusRequest, @NonNull String, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setCsd(float);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setNotifAliasRingForTest(boolean);
@@ -2579,9 +2584,10 @@
 
   @FlaggedApi("android.os.vibrator.vendor_vibration_effects") public static final class VibrationEffect.VendorEffect extends android.os.VibrationEffect {
     method @Nullable public long[] computeCreateWaveformOffOnTimingsOrNull();
+    method public float getAdaptiveScale();
     method public long getDuration();
     method public int getEffectStrength();
-    method public float getLinearScale();
+    method public float getScale();
     method @NonNull public android.os.PersistableBundle getVendorData();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.VendorEffect> CREATOR;
@@ -3132,6 +3138,7 @@
 
   public abstract class DreamOverlayService extends android.app.Service {
     ctor public DreamOverlayService();
+    method @FlaggedApi("android.service.dreams.publish_preview_state_to_overlay") public final boolean isDreamInPreviewMode();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public void onEndDream();
     method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
@@ -3664,7 +3671,11 @@
     method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
     method public boolean hasAccess(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
+    field public static final int FLAG_ALWAYS_UNLOCKED = 512; // 0x200
+    field public static final int FLAG_OWN_FOCUS = 2048; // 0x800
+    field public static final int FLAG_TOUCH_FEEDBACK_DISABLED = 1024; // 0x400
     field public static final int FLAG_TRUSTED = 128; // 0x80
+    field public static final int REMOVE_MODE_DESTROY_CONTENT = 1; // 0x1
     field public static final int TYPE_EXTERNAL = 2; // 0x2
     field public static final int TYPE_INTERNAL = 1; // 0x1
     field public static final int TYPE_OVERLAY = 4; // 0x4
@@ -3675,6 +3686,7 @@
 
   public static final class Display.Mode implements android.os.Parcelable {
     ctor public Display.Mode(int, int, float);
+    method public boolean isSynthetic();
     method public boolean matches(int, int, float);
   }
 
@@ -4425,8 +4437,15 @@
 
   public class WindowInfosListenerForTest {
     ctor public WindowInfosListenerForTest();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void addWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
-    method public void removeWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void addWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void addWindowInfosListener(@NonNull java.util.function.BiConsumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>,java.util.List<android.window.WindowInfosListenerForTest.DisplayInfo>>);
+    method @Deprecated public void removeWindowInfosListener(@NonNull java.util.function.Consumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>>);
+    method public void removeWindowInfosListener(@NonNull java.util.function.BiConsumer<java.util.List<android.window.WindowInfosListenerForTest.WindowInfo>,java.util.List<android.window.WindowInfosListenerForTest.DisplayInfo>>);
+  }
+
+  public static class WindowInfosListenerForTest.DisplayInfo {
+    field public final int displayId;
+    field @NonNull public final android.graphics.Matrix transform;
   }
 
   public static class WindowInfosListenerForTest.WindowInfo {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 90de7ab..4fb35c3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7312,7 +7312,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void finish(int finishTask) {
         if (DEBUG_FINISH_ACTIVITY) {
-            Log.d("Instrumentation", "finishActivity: finishTask=" + finishTask, new Throwable());
+            Log.d(Instrumentation.TAG, "finishActivity: finishTask=" + finishTask, new Throwable());
         }
         if (mParent == null) {
             int resultCode;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 74e9583..b83be6b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import static android.app.Instrumentation.DEBUG_FINISH_ACTIVITY;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -65,6 +66,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IpcDataCache;
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -80,6 +82,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Singleton;
 import android.util.Size;
 import android.view.WindowInsetsController.Appearance;
@@ -92,6 +95,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.RateLimitingCache;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
@@ -226,6 +230,52 @@
 
     final ArrayMap<OnUidImportanceListener, MyUidObserver> mImportanceListeners = new ArrayMap<>();
 
+    /** Rate-Limiting Cache that allows no more than 400 calls to the service per second. */
+    private static final RateLimitingCache<List<RunningAppProcessInfo>> mRunningProcessesCache =
+            new RateLimitingCache<>(10, 4);
+
+    /** Rate-Limiting Cache that allows no more than 200 calls to the service per second. */
+    private static final RateLimitingCache<List<ProcessErrorStateInfo>> mErrorProcessesCache =
+            new RateLimitingCache<>(10, 2);
+
+    /**
+     * Query handler for mGetCurrentUserIdCache - returns a cached value of the current foreground
+     * user id if the backstage_power/android.app.cache_get_current_user_id flag is enabled.
+     */
+    private static final IpcDataCache.QueryHandler<Void, Integer> mGetCurrentUserIdQuery =
+            new IpcDataCache.QueryHandler<>() {
+                @Override
+                public Integer apply(Void query) {
+                    try {
+                        return getService().getCurrentUserId();
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+
+                @Override
+                public boolean shouldBypassCache(Void query) {
+                    // If the flag to enable the new caching behavior is off, bypass the cache.
+                    return !Flags.cacheGetCurrentUserId();
+                }
+            };
+
+    /** A cache which maintains the current foreground user id. */
+    private static final IpcDataCache<Void, Integer> mGetCurrentUserIdCache =
+            new IpcDataCache<>(1, IpcDataCache.MODULE_SYSTEM,
+                    /* api= */ "getCurrentUserId", /* cacheName= */ "CurrentUserIdCache",
+                    mGetCurrentUserIdQuery);
+
+    /**
+     * The current foreground user has changed - invalidate the cache. Currently only called from
+     * UserController when a user switch occurs.
+     * @hide
+     */
+    public static void invalidateGetCurrentUserIdCache() {
+        IpcDataCache.invalidateCache(
+                IpcDataCache.MODULE_SYSTEM, /* api= */ "getCurrentUserId");
+    }
+
     /**
      * Map of callbacks that have registered for {@link UidFrozenStateChanged} events.
      * Will be called when a Uid has become frozen or unfrozen.
@@ -3678,6 +3728,16 @@
      * specified.
      */
     public List<ProcessErrorStateInfo> getProcessesInErrorState() {
+        if (Flags.rateLimitGetProcessesInErrorState()) {
+            return mErrorProcessesCache.get(() -> {
+                return getProcessesInErrorStateInternal();
+            });
+        } else {
+            return getProcessesInErrorStateInternal();
+        }
+    }
+
+    private List<ProcessErrorStateInfo> getProcessesInErrorStateInternal() {
         try {
             return getService().getProcessesInErrorState();
         } catch (RemoteException e) {
@@ -4211,6 +4271,16 @@
      * specified.
      */
     public List<RunningAppProcessInfo> getRunningAppProcesses() {
+        if (!Flags.rateLimitGetRunningAppProcesses()) {
+            return getRunningAppProcessesInternal();
+        } else {
+            return mRunningProcessesCache.get(() -> {
+                return getRunningAppProcessesInternal();
+            });
+        }
+    }
+
+    private List<RunningAppProcessInfo> getRunningAppProcessesInternal() {
         try {
             return getService().getRunningAppProcesses();
         } catch (RemoteException e) {
@@ -5213,11 +5283,7 @@
     })
     @android.ravenwood.annotation.RavenwoodReplace
     public static int getCurrentUser() {
-        try {
-            return getService().getCurrentUserId();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mGetCurrentUserIdCache.query(null);
     }
 
     /** @hide */
@@ -6011,6 +6077,10 @@
          * Finishes all activities in this task and removes it from the recent tasks list.
          */
         public void finishAndRemoveTask() {
+            if (DEBUG_FINISH_ACTIVITY) {
+                Log.d(Instrumentation.TAG, "AppTask#finishAndRemoveTask: task="
+                        + getTaskInfo(), new Throwable());
+            }
             try {
                 mAppTaskImpl.finishAndRemoveTask();
             } catch (RemoteException e) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 89efa9b..d318812 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -961,6 +961,17 @@
             @Nullable VoiceInteractionManagerProvider provider);
 
     /**
+     * Get whether or not the previous user's packages will be killed before the user is
+     * stopped during a user switch.
+     *
+     * <p> The primary use case of this method is for {@link com.android.server.SystemService}
+     * classes to call this API in their
+     * {@link com.android.server.SystemService#onUserSwitching} method implementation to prevent
+     * restarting any of the previous user's processes that will be killed during the user switch.
+     */
+    public abstract boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId);
+
+    /**
      * Sets whether the current foreground user (and its profiles) should be stopped after switched
      * out.
      */
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 65acd49..91aa225 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1587,6 +1587,16 @@
         }
     }
 
+    /** @hide */
+    public static boolean hasLaunchTargetContainer(ActivityOptions options) {
+        return options.getLaunchDisplayId() != INVALID_DISPLAY
+                || options.getLaunchTaskDisplayArea() != null
+                || options.getLaunchTaskDisplayAreaFeatureId() != FEATURE_UNDEFINED
+                || options.getLaunchRootTask() != null
+                || options.getLaunchTaskId() != -1
+                || options.getLaunchTaskFragmentToken() != null;
+    }
+
     /**
      * Gets whether the activity is to be launched into LockTask mode.
      * @return {@code true} if the activity is to be launched into LockTask mode.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d455853..4350545 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4583,7 +4583,7 @@
     public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
             @Nullable SplashScreenView.SplashScreenViewParcelable parcelable,
             @NonNull SurfaceControl startingWindowLeash) {
-        final DecorView decorView = (DecorView) r.window.peekDecorView();
+        final DecorView decorView = r.window != null ? (DecorView) r.window.peekDecorView() : null;
         if (parcelable != null && decorView != null) {
             createSplashScreen(r, decorView, parcelable, startingWindowLeash);
         } else {
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 81e9df6..8370c2e 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -95,6 +95,8 @@
     private static final int FLAG_FULLSCREEN_OVERRIDE_SYSTEM = FLAG_BASE << 7;
     /** Top activity flag for whether has activity has been overridden to fullscreen by user. */
     private static final int FLAG_FULLSCREEN_OVERRIDE_USER = FLAG_BASE << 8;
+    /** Top activity flag for whether min aspect ratio of the activity has been overridden.*/
+    public static final int FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE = FLAG_BASE << 9;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
@@ -108,7 +110,8 @@
             FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP,
             FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON,
             FLAG_FULLSCREEN_OVERRIDE_SYSTEM,
-            FLAG_FULLSCREEN_OVERRIDE_USER
+            FLAG_FULLSCREEN_OVERRIDE_USER,
+            FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE
     })
     public @interface TopActivityFlag {}
 
@@ -118,7 +121,7 @@
     @TopActivityFlag
     private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP
             | FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM
-            | FLAG_FULLSCREEN_OVERRIDE_USER;
+            | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE;
 
     @TopActivityFlag
     private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED
@@ -301,6 +304,21 @@
         setTopActivityFlag(FLAG_LETTERBOXED, enable);
     }
 
+    /**
+     * @return {@code true} if the top activity's min aspect ratio has been overridden.
+     */
+    public boolean hasMinAspectRatioOverride() {
+        return isTopActivityFlagEnabled(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE);
+    }
+
+    /**
+     * Sets the top activity flag for whether the min aspect ratio of the activity has been
+     * overridden.
+     */
+    public void setHasMinAspectRatioOverride(boolean enable) {
+        setTopActivityFlag(FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE, enable);
+    }
+
     /** Clear all top activity flags and set to false. */
     public void clearTopActivityFlags() {
         mTopActivityFlags = FLAG_UNDEFINED;
@@ -392,6 +410,7 @@
                 + " topActivityLetterboxAppHeight=" + topActivityLetterboxAppHeight
                 + " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
                 + " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+                + " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
                 + " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString()
                 + "}";
     }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 21396a1..f27dc32 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -79,7 +79,6 @@
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
 import android.util.Pools;
@@ -3156,12 +3155,6 @@
     /** @hide */
     public static final String KEY_HISTORICAL_OPS = "historical_ops";
 
-    /** System properties for debug logging of noteOp call sites */
-    private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
-    private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
-    private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
-    private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
-
     /**
      * Retrieve the op switch that controls the given operation.
      * @hide
@@ -7459,15 +7452,15 @@
         }
 
         /**
-         * Similar to {@link #onOpChanged(String, String, int)} but includes the device for which
-         * the op mode has changed.
+         * Similar to {@link #onOpChanged(String, String)} but includes user and the device for
+         * which the op mode has changed.
          *
          * <p> Implement this method if callbacks are required on all devices.
          * If not implemented explicitly, the default implementation will notify for op changes
-         * on the default device {@link VirtualDeviceManager#PERSISTENT_DEVICE_ID_DEFAULT} only.
+         * on the default device only.
          *
-         * <p> If implemented, {@link #onOpChanged(String, String, int)}
-         * will not be called automatically.
+         * <p> If implemented, {@link #onOpChanged(String, String)} will not be called
+         * automatically.
          *
          * @param op The Op that changed.
          * @param packageName Package of the app whose Op changed.
@@ -8066,14 +8059,6 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(int code, int uid, @Mode int mode) {
         try {
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (code == OP_BLUETOOTH_CONNECT) {
-                Log.i(DEBUG_LOGGING_TAG,
-                        "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
-                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
-                                + " trace: "
-                                + Arrays.toString(Thread.currentThread().getStackTrace()));
-            }
             mService.setUidMode(code, uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -8094,15 +8079,6 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
         try {
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
-                Log.i(DEBUG_LOGGING_TAG,
-                        "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
-                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
-                                + " trace: "
-                                + Arrays.toString(Thread.currentThread().getStackTrace()));
-            }
-
             mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -8143,14 +8119,6 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
     public void setMode(int code, int uid, String packageName, @Mode int mode) {
         try {
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (code == OP_BLUETOOTH_CONNECT) {
-                Log.i(DEBUG_LOGGING_TAG,
-                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
-                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
-                                + " trace: "
-                                + Arrays.toString(Thread.currentThread().getStackTrace()));
-            }
             mService.setMode(code, uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -8173,14 +8141,6 @@
     public void setMode(@NonNull String op, int uid, @Nullable String packageName,
             @Mode int mode) {
         try {
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
-                Log.i(DEBUG_LOGGING_TAG,
-                        "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
-                                + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
-                                + " trace: "
-                                + Arrays.toString(Thread.currentThread().getStackTrace()));
-            }
             mService.setMode(strOpToOp(op), uid, packageName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 80764af..dbf9afd 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -112,6 +112,8 @@
 import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
 import android.provider.Settings;
+import android.ravenwood.annotation.RavenwoodKeepPartialClass;
+import android.ravenwood.annotation.RavenwoodReplace;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -157,6 +159,7 @@
 import java.util.function.Function;
 
 /** @hide */
+@RavenwoodKeepPartialClass
 public class ApplicationPackageManager extends PackageManager {
     private static final String TAG = "ApplicationPackageManager";
     private static final boolean DEBUG_ICONS = false;
@@ -2163,6 +2166,7 @@
     }
 
     @UnsupportedAppUsage
+    @RavenwoodReplace(reason = "<cinit> crashes due to unsupported class PropertyInvalidatedCache")
     static void configurationChanged() {
         synchronized (sSync) {
             sIconCache.clear();
@@ -2170,6 +2174,10 @@
         }
     }
 
+    private static void configurationChanged$ravenwood() {
+        /* no-op */
+    }
+
     @UnsupportedAppUsage
     protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
         mContext = context;
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 0ff5514..1e9a79b 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -210,6 +210,11 @@
     public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7;
 
     /**
+     * @see #getMonoticCreationTimeMs
+     */
+    private long mMonoticCreationTimeMs;
+
+    /**
      * @see #getStartupState
      */
     private @StartupState int mStartupState;
@@ -487,6 +492,15 @@
     }
 
     /**
+     * Monotonic elapsed time persisted across reboots.
+     *
+     * @hide
+     */
+    public long getMonoticCreationTimeMs() {
+        return mMonoticCreationTimeMs;
+    }
+
+    /**
      * The process id.
      *
      * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p>
@@ -669,7 +683,9 @@
     }
 
     /** @hide */
-    public ApplicationStartInfo() {}
+    public ApplicationStartInfo(long monotonicCreationTimeMs) {
+        mMonoticCreationTimeMs = monotonicCreationTimeMs;
+    }
 
     /** @hide */
     public ApplicationStartInfo(ApplicationStartInfo other) {
@@ -686,6 +702,7 @@
         mStartIntent = other.mStartIntent;
         mLaunchMode = other.mLaunchMode;
         mWasForceStopped = other.mWasForceStopped;
+        mMonoticCreationTimeMs = other.mMonoticCreationTimeMs;
     }
 
     private ApplicationStartInfo(@NonNull Parcel in) {
@@ -708,6 +725,7 @@
                 in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
         mLaunchMode = in.readInt();
         mWasForceStopped = in.readBoolean();
+        mMonoticCreationTimeMs = in.readLong();
     }
 
     private static String intern(@Nullable String source) {
@@ -786,6 +804,7 @@
         }
         proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
         proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped);
+        proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs);
         proto.end(token);
     }
 
@@ -869,6 +888,10 @@
                     mWasForceStopped = proto.readBoolean(
                             ApplicationStartInfoProto.WAS_FORCE_STOPPED);
                     break;
+                case (int) ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS:
+                    mMonoticCreationTimeMs = proto.readLong(
+                            ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS);
+                    break;
             }
         }
         proto.end(token);
@@ -881,6 +904,8 @@
         sb.append(prefix)
                 .append("ApplicationStartInfo ").append(seqSuffix).append(':')
                 .append('\n')
+                .append(" monotonicCreationTimeMs=").append(mMonoticCreationTimeMs)
+                .append('\n')
                 .append(" pid=").append(mPid)
                 .append(" realUid=").append(mRealUid)
                 .append(" packageUid=").append(mPackageUid)
@@ -949,14 +974,15 @@
             && mDefiningUid == o.mDefiningUid && mReason == o.mReason
             && mStartupState == o.mStartupState && mStartType == o.mStartType
             && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName)
-            && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped;
+            && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped
+            && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
-                mStartType, mLaunchMode, mProcessName,
-                mStartupTimestampsNs);
+                mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs,
+                mMonoticCreationTimeMs);
     }
 
     private boolean timestampsEquals(@NonNull ApplicationStartInfo other) {
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index 62b5412..e0a9371 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -49,7 +49,7 @@
 
     /**
      * Rule is of an unknown type. This is the default value if not provided by the owning app,
-     * and the value returned if the true type was added in an API level lower than the calling
+     * and the value returned if the true type was added in an API level higher than the calling
      * app's targetSdk.
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
@@ -315,7 +315,8 @@
     }
 
     /**
-     * Gets the zen policy.
+     * Gets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
      */
     @Nullable
     public ZenPolicy getZenPolicy() {
@@ -345,6 +346,17 @@
 
     /**
      * Sets the interruption filter that is applied when this rule is active.
+     *
+     * <ul>
+     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}, the rule will use
+     *     the {@link ZenPolicy} supplied to {@link #setZenPolicy} (or a default one).
+     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALARMS} or
+     *     {@link NotificationManager#INTERRUPTION_FILTER_NONE}, the rule will use a fixed
+     *     {@link ZenPolicy} matching the filter.
+     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALL}, the rule will not block
+     *     notifications, but can still have {@link ZenDeviceEffects}.
+     * </ul>
+     *
      * @param interruptionFilter The do not disturb mode to enter when this rule is active.
      */
     public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
@@ -374,7 +386,8 @@
     }
 
     /**
-     * Sets the zen policy.
+     * Sets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
+     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
      *
      * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
      * a {@code null} value here means the previous policy is retained.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 7e2a580..90fba29 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1160,7 +1160,7 @@
         }
         mMainThread.getInstrumentation().execStartActivity(
                 getOuterContext(), mMainThread.getApplicationThread(), null,
-                (Activity) null, intent, -1, options);
+                (Activity) null, intent, -1, applyLaunchDisplayIfNeeded(options));
     }
 
     /** @hide */
@@ -1170,8 +1170,8 @@
             ActivityTaskManager.getService().startActivityAsUser(
                     mMainThread.getApplicationThread(), getOpPackageName(), getAttributionTag(),
                     intent, intent.resolveTypeIfNeeded(getContentResolver()),
-                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
-                    user.getIdentifier());
+                    null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+                    applyLaunchDisplayIfNeeded(options), user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1194,7 +1194,8 @@
         }
         return mMainThread.getInstrumentation().execStartActivitiesAsUser(
                 getOuterContext(), mMainThread.getApplicationThread(), null,
-                (Activity) null, intents, options, userHandle.getIdentifier());
+                (Activity) null, intents, applyLaunchDisplayIfNeeded(options),
+                userHandle.getIdentifier());
     }
 
     @Override
@@ -1208,7 +1209,26 @@
         }
         mMainThread.getInstrumentation().execStartActivities(
                 getOuterContext(), mMainThread.getApplicationThread(), null,
-                (Activity) null, intents, options);
+                (Activity) null, intents, applyLaunchDisplayIfNeeded(options));
+    }
+
+    private Bundle applyLaunchDisplayIfNeeded(@Nullable Bundle options) {
+        if (!isAssociatedWithDisplay()) {
+            // return if this Context has no associated display.
+            return options;
+        }
+
+        final ActivityOptions activityOptions;
+        if (options != null) {
+            activityOptions = ActivityOptions.fromBundle(options);
+            if (ActivityOptions.hasLaunchTargetContainer(activityOptions)) {
+                // return if the options already has launching target.
+                return options;
+            }
+        } else {
+            activityOptions = ActivityOptions.makeBasic();
+        }
+        return activityOptions.setLaunchDisplayId(getAssociatedDisplayId()).toBundle();
     }
 
     @Override
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 55ce90d..0a05144 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -68,7 +68,6 @@
 import android.os.StrictMode;
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
-import android.view.IRecentsAnimationRunner;
 import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
@@ -129,13 +128,12 @@
     int startActivityFromGameSession(IApplicationThread caller, in String callingPackage,
             in String callingFeatureId, int callingPid, int callingUid, in Intent intent,
             int taskId, int userId);
-    void startRecentsActivity(in Intent intent, in long eventTime,
-            in IRecentsAnimationRunner recentsAnimationRunner);
     int startActivityFromRecents(int taskId, in Bundle options);
     int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
             in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
             int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
             boolean ignoreTargetSecurity, int userId);
+    void preloadRecentsActivity(in Intent intent);
 
     boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
             int userId);
@@ -167,7 +165,6 @@
     /** Focuses the top task on a display if it isn't already focused. Used for Recents. */
     void focusTopTask(int displayId);
 
-    void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES)")
     void updateLockTaskPackages(int userId, in String[] packages);
     boolean isInLockTaskMode();
@@ -226,7 +223,7 @@
             in IBinder activityToken, int flags);
     boolean isAssistDataAllowed();
     boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
-            in String callingPackageName, String callingAttributionTag);
+            in String callingPackageName, String callingAttributionTag, boolean fetchStructure);
 
     /**
      * Notify the system that the keyguard is going away.
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 69c3bd3..9dcfe89 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -45,7 +45,7 @@
     void injectInputEventToInputFilter(in InputEvent event);
     void syncInputTransactions(boolean waitForAnimations);
     boolean setRotation(int rotation);
-    boolean takeScreenshot(in Rect crop, in ScreenCaptureListener listener);
+    boolean takeScreenshot(in Rect crop, in ScreenCaptureListener listener, int displayId);
     boolean takeSurfaceControlScreenshot(in SurfaceControl surfaceControl, in ScreenCaptureListener listener);
     boolean clearWindowContentFrameStats(int windowId);
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index be27046..45852c7 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -98,7 +98,7 @@
      */
     public static final String REPORT_KEY_STREAMRESULT = "stream";
 
-    private static final String TAG = "Instrumentation";
+    static final String TAG = "Instrumentation";
 
     private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
 
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 4a06f7d..f56bf4d 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -28,6 +28,8 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.AttributeSet;
 import android.util.Slog;
 import android.util.Xml;
@@ -64,6 +66,7 @@
 // Add following to last Note: when guide is written:
 // For more information about the LocaleConfig overridden by the application, see TODO(b/261528306):
 // add link to guide
+@RavenwoodKeepWholeClass
 public class LocaleConfig implements Parcelable {
     private static final String TAG = "LocaleConfig";
     public static final String TAG_LOCALE_CONFIG = "locale-config";
@@ -104,6 +107,7 @@
      *
      * @see Context#createPackageContext(String, int).
      */
+    @RavenwoodThrow(blockedBy = LocaleManager.class)
     public LocaleConfig(@NonNull Context context) {
         this(context, true);
     }
@@ -117,10 +121,12 @@
      * @see Context#createPackageContext(String, int).
      */
     @NonNull
+    @RavenwoodThrow(blockedBy = LocaleManager.class)
     public static LocaleConfig fromContextIgnoringOverride(@NonNull Context context) {
         return new LocaleConfig(context, false);
     }
 
+    @RavenwoodThrow(blockedBy = LocaleManager.class)
     private LocaleConfig(@NonNull Context context, boolean allowOverride) {
         if (allowOverride) {
             LocaleManager localeManager = context.getSystemService(LocaleManager.class);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ef09dc4..7a36fbb 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1578,6 +1578,22 @@
     public static final String EXTRA_DECLINE_COLOR = "android.declineColor";
 
     /**
+     * {@link #extras} key: {@link Icon} of an image used as an overlay Icon on
+     * {@link Notification#mLargeIcon} for {@link EnRouteStyle} notifications.
+     * This extra is an {@code Icon}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_ENROUTE_OVERLAY_ICON = "android.enrouteOverlayIcon";
+
+    /**
+     * {@link #extras} key: text used as a sub-text for the largeIcon of
+     * {@link EnRouteStyle} notification. This extra is a {@code CharSequence}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
+    public static final String EXTRA_ENROUTE_LARGE_ICON_SUBTEXT = "android.enrouteLargeIconSubText";
+    /**
      * {@link #extras} key: whether the notification should be colorized as
      * supplied to {@link Builder#setColorized(boolean)}.
      */
@@ -2690,14 +2706,9 @@
         if (mAllowlistToken == null) {
             mAllowlistToken = processAllowlistToken;
         }
-        if (Flags.secureAllowlistToken()) {
-            // Propagate this token to all pending intents that are unmarshalled from the parcel,
-            // or keep the one we're already propagating, if that's the case.
-            if (!parcel.hasClassCookie(PendingIntent.class)) {
-                parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
-            }
-        } else {
-            // Propagate this token to all pending intents that are unmarshalled from the parcel.
+        // Propagate this token to all pending intents that are unmarshalled from the parcel,
+        // or keep the one we're already propagating, if that's the case.
+        if (!parcel.hasClassCookie(PendingIntent.class)) {
             parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
         }
 
@@ -3039,6 +3050,10 @@
             visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class));
         }
 
+        if (Flags.apiRichOngoing()) {
+            visitIconUri(visitor, extras.getParcelable(EXTRA_ENROUTE_OVERLAY_ICON, Icon.class));
+        }
+
         if (mBubbleMetadata != null) {
             visitIconUri(visitor, mBubbleMetadata.getIcon());
         }
@@ -3313,28 +3328,22 @@
             PendingIntent.addOnMarshaledListener(addedListener);
         }
         try {
-            if (Flags.secureAllowlistToken()) {
-                boolean mustClearCookie = false;
-                if (!parcel.hasClassCookie(Notification.class)) {
-                    // This is the "root" notification, and not an "inner" notification (including
-                    // publicVersion or anything else that might be embedded in extras). So we want
-                    // to use its token for every inner notification (might be null).
-                    parcel.setClassCookie(Notification.class, mAllowlistToken);
-                    mustClearCookie = true;
-                }
-                try {
-                    // IMPORTANT: Add marshaling code in writeToParcelImpl as we
-                    // want to intercept all pending events written to the parcel.
-                    writeToParcelImpl(parcel, flags);
-                } finally {
-                    if (mustClearCookie) {
-                        parcel.removeClassCookie(Notification.class, mAllowlistToken);
-                    }
-                }
-            } else {
+            boolean mustClearCookie = false;
+            if (!parcel.hasClassCookie(Notification.class)) {
+                // This is the "root" notification, and not an "inner" notification (including
+                // publicVersion or anything else that might be embedded in extras). So we want
+                // to use its token for every inner notification (might be null).
+                parcel.setClassCookie(Notification.class, mAllowlistToken);
+                mustClearCookie = true;
+            }
+            try {
                 // IMPORTANT: Add marshaling code in writeToParcelImpl as we
                 // want to intercept all pending events written to the parcel.
                 writeToParcelImpl(parcel, flags);
+            } finally {
+                if (mustClearCookie) {
+                    parcel.removeClassCookie(Notification.class, mAllowlistToken);
+                }
             }
 
             synchronized (this) {
@@ -3351,13 +3360,9 @@
     private void writeToParcelImpl(Parcel parcel, int flags) {
         parcel.writeInt(1);
 
-        if (Flags.secureAllowlistToken()) {
-            // Always use the same token as the root notification (might be null).
-            IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
-            parcel.writeStrongBinder(rootNotificationToken);
-        } else {
-            parcel.writeStrongBinder(mAllowlistToken);
-        }
+        // Always use the same token as the root notification (might be null).
+        IBinder rootNotificationToken = (IBinder) parcel.getClassCookie(Notification.class);
+        parcel.writeStrongBinder(rootNotificationToken);
 
         parcel.writeLong(when);
         parcel.writeLong(creationTime);
@@ -10979,15 +10984,141 @@
     }
 
     /**
-     * An object that can apply a rich ongoing notification style to a {@link Notification.Builder}
-     * object.
+     * TODO(b/360827871): Make EnRouteStyle public.
+     * A style used to represent the progress of a real-world journey with a known destination.
+     * For example:
+     * <ul>
+     *     <li>Delivery tracking</li>
+     *     <li>Ride progress</li>
+     *     <li>Flight tracking</li>
+     * </ul>
+     *
+     * The exact fields from {@link Notification} that are shown with this style may vary by
+     * the surface where this update appears, but the following fields are recommended:
+     * <ul>
+     *     <li>{@link Notification.Builder#setContentTitle}</li>
+     *     <li>{@link Notification.Builder#setContentText}</li>
+     *     <li>{@link Notification.Builder#setSubText}</li>
+     *     <li>{@link Notification.Builder#setLargeIcon}</li>
+     *     <li>{@link Notification.Builder#setProgress}</li>
+     *     <li>{@link Notification.Builder#setWhen} - This should be the future time of the next,
+     *     final, or most important stop on this journey.</li>
+     * </ul>
+     * @hide
      */
     @FlaggedApi(Flags.FLAG_API_RICH_ONGOING)
-    public abstract static class RichOngoingStyle extends Notification.Style {
+    public static class EnRouteStyle extends Notification.Style {
+
+        @Nullable
+        private Icon mOverlayIcon = null;
+
+        @Nullable
+        private CharSequence mLargeIconSubText = null;
+
+        public EnRouteStyle() {
+        }
+
+        /**
+         * Returns the overlay icon to be displayed on {@link Notification#mLargeIcon}.
+         * @see EnRouteStyle#setOverlayIcon
+         */
+        @Nullable
+        public Icon getOverlayIcon() {
+            return mOverlayIcon;
+        }
+
+        /**
+         * Optional icon to be displayed on {@link Notification#mLargeIcon}.
+         *
+         * This image will be cropped to a circle and will obscure
+         * a semicircle of the right side of the large icon.
+         */
+        @NonNull
+        public EnRouteStyle setOverlayIcon(@Nullable Icon overlayIcon) {
+            mOverlayIcon = overlayIcon;
+            return this;
+        }
+
+        /**
+         * Returns the sub-text for {@link Notification#mLargeIcon}.
+         * @see EnRouteStyle#setLargeIconSubText
+         */
+        @Nullable
+        public CharSequence getLargeIconSubText() {
+            return mLargeIconSubText;
+        }
+
+        /**
+         * Optional text which generally related to
+         * the {@link Notification.Builder#setLargeIcon} or {@link #setOverlayIcon} or both.
+         */
+        @NonNull
+        public EnRouteStyle setLargeIconSubText(@Nullable CharSequence largeIconSubText) {
+            mLargeIconSubText = stripStyling(largeIconSubText);
+            return this;
+        }
+
+         /**
+         * @hide
+         */
+        @Override
+        public boolean areNotificationsVisiblyDifferent(Style other) {
+            if (other == null || getClass() != other.getClass()) {
+                return true;
+            }
+
+            final EnRouteStyle enRouteStyle = (EnRouteStyle) other;
+            return !Objects.equals(mOverlayIcon, enRouteStyle.mOverlayIcon)
+                    || !Objects.equals(mLargeIconSubText, enRouteStyle.mLargeIconSubText);
+        }
+
         /**
          * @hide
          */
-        public RichOngoingStyle() {}
+        @Override
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+            extras.putParcelable(EXTRA_ENROUTE_OVERLAY_ICON, mOverlayIcon);
+            extras.putCharSequence(EXTRA_ENROUTE_LARGE_ICON_SUBTEXT, mLargeIconSubText);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        protected void restoreFromExtras(Bundle extras) {
+            super.restoreFromExtras(extras);
+            mOverlayIcon = extras.getParcelable(EXTRA_ENROUTE_OVERLAY_ICON, Icon.class);
+            mLargeIconSubText = extras.getCharSequence(EXTRA_ENROUTE_LARGE_ICON_SUBTEXT);
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void purgeResources() {
+            super.purgeResources();
+            if (mOverlayIcon != null) {
+                mOverlayIcon.convertToAshmem();
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void reduceImageSizes(Context context) {
+            super.reduceImageSizes(context);
+            if (mOverlayIcon != null) {
+                final Resources resources = context.getResources();
+                final boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
+
+                int rightIconSize = resources.getDimensionPixelSize(isLowRam
+                        ? R.dimen.notification_right_icon_size_low_ram
+                        : R.dimen.notification_right_icon_size);
+                mOverlayIcon.scaleDownIfNecessary(rightIconSize, rightIconSize);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 326d7ce..1b29b7a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -168,7 +168,11 @@
     /**
      * @hide
      */
-    public static final int MAX_VIBRATION_LENGTH = 1000;
+    public static final int MAX_VIBRATION_LENGTH = 500;
+    /**
+     * @hide
+     */
+    public static final int MAX_SERIALIZED_VIBRATION_LENGTH = 32_768;
 
     private static final String TAG_CHANNEL = "channel";
     private static final String ATT_NAME = "name";
@@ -368,6 +372,9 @@
         if (Flags.notificationChannelVibrationEffectApi()) {
             mVibrationEffect =
                     in.readInt() != 0 ? VibrationEffect.CREATOR.createFromParcel(in) : null;
+            if (Flags.notifChannelCropVibrationEffects() && mVibrationEffect != null) {
+                mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+            }
         }
         mUserLockedFields = in.readInt();
         mUserVisibleTaskShown = in.readByte() != 0;
@@ -582,6 +589,23 @@
         return input;
     }
 
+    // Returns trimmed vibration effect or null if not trimmable.
+    private VibrationEffect getTrimmedVibrationEffect(VibrationEffect effect) {
+        if (effect == null) {
+            return null;
+        }
+        // trim if possible; check serialized length; reject if it is still too long
+        VibrationEffect result = effect;
+        VibrationEffect trimmed = effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH);
+        if (trimmed != null) {
+            result = trimmed;
+        }
+        if (vibrationToString(result).length() > MAX_SERIALIZED_VIBRATION_LENGTH) {
+            return null;
+        }
+        return result;
+    }
+
     /**
      * @hide
      */
@@ -685,6 +709,11 @@
     public void setVibrationPattern(long[] vibrationPattern) {
         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
         this.mVibrationPattern = vibrationPattern;
+        if (Flags.notifChannelCropVibrationEffects()) {
+            if (vibrationPattern != null && vibrationPattern.length > MAX_VIBRATION_LENGTH) {
+                this.mVibrationPattern = Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH);
+            }
+        }
         if (Flags.notificationChannelVibrationEffectApi()) {
             try {
                 this.mVibrationEffect =
@@ -731,9 +760,21 @@
     public void setVibrationEffect(@Nullable VibrationEffect effect) {
         this.mVibrationEnabled = effect != null;
         this.mVibrationEffect = effect;
-        this.mVibrationPattern =
-                effect == null
-                ? null : effect.computeCreateWaveformOffOnTimingsOrNull();
+        if (Flags.notifChannelCropVibrationEffects() && effect != null) {
+            // Try converting to a vibration pattern and trimming that array. If not convertible
+            // to a pattern directly, try trimming the vibration effect if possible and storing
+            // that version instead.
+            long[] pattern = effect.computeCreateWaveformOffOnTimingsOrNull();
+            if (pattern != null) {
+                setVibrationPattern(pattern);
+            } else {
+                this.mVibrationEffect = getTrimmedVibrationEffect(mVibrationEffect);
+            }
+        } else {
+            this.mVibrationPattern =
+                    mVibrationEffect == null
+                            ? null : mVibrationEffect.computeCreateWaveformOffOnTimingsOrNull();
+        }
     }
 
     /**
@@ -753,9 +794,14 @@
 
     /**
      * Sets whether or not notifications posted to this channel can interrupt the user in
-     * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
+     * {@link android.app.NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
      *
-     * Only modifiable by the system and notification ranker.
+     * <p>Apps with Do Not Disturb policy access (see
+     * {@link NotificationManager#isNotificationPolicyAccessGranted()}) can set up their own
+     * channels this way, but only if the channel hasn't been updated by the user since its
+     * creation.
+     *
+     * <p>Otherwise, this value is only modifiable by the system and the notification ranker.
      */
     public void setBypassDnd(boolean bypassDnd) {
         this.mBypassDnd = bypassDnd;
@@ -1167,7 +1213,9 @@
             if (vibrationEffect != null) {
                 // Restore the effect only if it is not null. This allows to avoid undoing a
                 // `setVibrationPattern` call above, if that was done with a non-null pattern
-                // (e.g. back up from a version that did not support `setVibrationEffect`).
+                // (e.g. back up from a version that did not support `setVibrationEffect`), or
+                // when notif_channel_crop_vibration_effects is true, if there is an equivalent
+                // vibration pattern available.
                 setVibrationEffect(vibrationEffect);
             }
         }
@@ -1360,7 +1408,11 @@
             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
         }
         if (getVibrationEffect() != null) {
-            out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+            if (!Flags.notifChannelCropVibrationEffects() || getVibrationPattern() == null) {
+                // When notif_channel_crop_vibration_effects is on, only serialize the vibration
+                // effect if we do not already have an equivalent vibration pattern.
+                out.attribute(null, ATT_VIBRATION_EFFECT, vibrationToString(getVibrationEffect()));
+            }
         }
         if (getUserLockedFields() != 0) {
             out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields());
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 69b5222..83f9ff7 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -393,7 +393,7 @@
      * changes.
      *
      * <p>This broadcast is only sent to registered receivers and receivers in packages that have
-     * been granted Do Not Disturb access (see {@link #isNotificationPolicyAccessGranted()}).
+     * been granted Notification Policy access (see {@link #isNotificationPolicyAccessGranted()}).
      */
     @FlaggedApi(Flags.FLAG_MODES_API)
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -1440,10 +1440,36 @@
      * Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
      * Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
      * Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
-     * <p>
-     *     This method can be used in conjunction with or as a replacement to
-     *     {@link android.service.notification.ConditionProviderService#notifyCondition(Condition)}.
-     * </p>
+     *
+     * <p>This method can be used in conjunction with or as a replacement to
+     * {@link android.service.notification.ConditionProviderService#notifyCondition(Condition)}.
+     *
+     * <p>The condition change may be ignored if the user has activated or deactivated the rule
+     * manually -- the user can "override" the rule <em>this time</em>, with the rule resuming its
+     * normal operation for the next cycle. When this has happened, the supplied condition will be
+     * applied only once the automatic state is in agreement with the user-provided state. For
+     * example, assume that the {@link AutomaticZenRule} corresponds to a "Driving Mode" with
+     * automatic driving detection.
+     *
+     * <ol>
+     *     <li>App detects driving and notifies the system that the rule should be active, calling
+     *     this method with a {@link Condition} with {@link Condition#STATE_TRUE}).
+     *     <li>User deactivates ("snoozes") the rule for some reason. This overrides the
+     *     app-provided condition state.
+     *     <li>App is still detecting driving, so again calls with {@link Condition#STATE_TRUE}.
+     *     This is ignored by the system, as the user override prevails.
+     *     <li>Some time later, the app detects that driving stopped, so the rule should be
+     *     inactive, and calls with {@link Condition#STATE_FALSE}). This doesn't change the actual
+     *     rule state (it was already inactive due to the user's override), but clears the override.
+     *     <li>Some time later, the app detects that driving has started again, and notifies that
+     *     the rule should be active (calling with {@link Condition#STATE_TRUE} again). The rule is
+     *     activated.
+     * </ol>
+     *
+     * <p>Note that the behavior at step #3 is different if the app also specifies
+     * {@link Condition#SOURCE_USER_ACTION} as the {@link Condition#source} -- rule state updates
+     * coming from user actions are not ignored.
+     *
      * @param id The id of the rule whose state should change
      * @param condition The new state of this rule
      */
@@ -1627,7 +1653,7 @@
     }
 
     /**
-     * Checks the ability to modify notification do not disturb policy for the calling package.
+     * Checks the ability to modify Notification Policy for the calling package.
      *
      * <p>
      * Returns true if the calling package can modify notification policy.
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 0cc210b..84a4eb4 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -40,6 +40,9 @@
 import android.os.LocaleList;
 import android.os.Process;
 import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodReplace;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
@@ -70,6 +73,7 @@
 import java.util.function.Function;
 
 /** @hide */
+@RavenwoodKeepWholeClass
 public class ResourcesManager {
     static final String TAG = "ResourcesManager";
     private static final boolean DEBUG = false;
@@ -149,6 +153,7 @@
      * This will collect the package resources' paths from its ApplicationInfo and add them to all
      * existing and future contexts while the application is running.
      */
+    @RavenwoodThrow(reason = "FLAG_REGISTER_RESOURCE_PATHS is unsupported")
     public void registerResourcePaths(@NonNull String uniqueId, @NonNull ApplicationInfo appInfo) {
         if (!Flags.registerResourcePaths()) {
             return;
@@ -1405,6 +1410,7 @@
         return newKey;
     }
 
+    @RavenwoodThrow(reason = "AppInfo update not supported")
     public void appendPendingAppInfoUpdate(@NonNull String[] oldSourceDirs,
             @NonNull ApplicationInfo appInfo) {
         synchronized (mLock) {
@@ -1423,6 +1429,7 @@
         }
     }
 
+    @RavenwoodReplace(reason = "AppInfo update not supported")
     public final void applyAllPendingAppInfoUpdates() {
         synchronized (mLock) {
             if (mPendingAppInfoUpdates != null) {
@@ -1435,6 +1442,10 @@
         }
     }
 
+    private void applyAllPendingAppInfoUpdates$ravenwood() {
+        /* no-op */
+    }
+
     public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat) {
         synchronized (mLock) {
@@ -1877,6 +1888,7 @@
          * instance uses.
          */
         @Override
+        @RavenwoodThrow(blockedBy = ResourcesLoader.class)
         public void onLoadersChanged(@NonNull Resources resources,
                 @NonNull List<ResourcesLoader> newLoader) {
             synchronized (mLock) {
@@ -1906,6 +1918,7 @@
          * {@code loader} to apply any changes of the set of {@link ApkAssets}.
          **/
         @Override
+        @RavenwoodThrow(blockedBy = ResourcesLoader.class)
         public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
             synchronized (mLock) {
                 final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 14195c4..63e03914 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -1326,6 +1326,7 @@
         private boolean mClock;
         private boolean mNotificationIcons;
         private boolean mRotationSuggestion;
+        private boolean mQuickSettings;
 
         /** @hide */
         public DisableInfo(int flags1, int flags2) {
@@ -1338,6 +1339,7 @@
             mClock = (flags1 & DISABLE_CLOCK) != 0;
             mNotificationIcons = (flags1 & DISABLE_NOTIFICATION_ICONS) != 0;
             mRotationSuggestion = (flags2 & DISABLE2_ROTATE_SUGGESTIONS) != 0;
+            mQuickSettings = (flags2 & DISABLE2_QUICK_SETTINGS) != 0;
         }
 
         /** @hide */
@@ -1471,6 +1473,20 @@
         }
 
         /**
+         * @hide
+         */
+        public void setQuickSettingsDisabled(boolean disabled) {
+            mQuickSettings = disabled;
+        }
+
+        /**
+         * @hide
+         */
+        public boolean isQuickSettingsDisabled() {
+            return mQuickSettings;
+        }
+
+        /**
          * @return {@code true} if no components are disabled (default state)
          * @hide
          */
@@ -1478,7 +1494,7 @@
         public boolean areAllComponentsEnabled() {
             return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents
                     && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons
-                    && !mRotationSuggestion;
+                    && !mRotationSuggestion && !mQuickSettings;
         }
 
         /** @hide */
@@ -1492,6 +1508,7 @@
             mClock = false;
             mNotificationIcons = false;
             mRotationSuggestion = false;
+            mQuickSettings = false;
         }
 
         /**
@@ -1502,7 +1519,7 @@
         public boolean areAllComponentsDisabled() {
             return mStatusBarExpansion && mNavigateHome && mNotificationPeeking
                     && mRecents && mSearch && mSystemIcons && mClock && mNotificationIcons
-                    && mRotationSuggestion;
+                    && mRotationSuggestion && mQuickSettings;
         }
 
         /** @hide */
@@ -1516,6 +1533,7 @@
             mClock = true;
             mNotificationIcons = true;
             mRotationSuggestion = true;
+            mQuickSettings = true;
         }
 
         @NonNull
@@ -1533,6 +1551,7 @@
             sb.append(" mClock=").append(mClock ? "disabled" : "enabled");
             sb.append(" mNotificationIcons=").append(mNotificationIcons ? "disabled" : "enabled");
             sb.append(" mRotationSuggestion=").append(mRotationSuggestion ? "disabled" : "enabled");
+            sb.append(" mQuickSettings=").append(mQuickSettings ? "disabled" : "enabled");
 
             return sb.toString();
 
@@ -1557,6 +1576,7 @@
             if (mClock) disable1 |= DISABLE_CLOCK;
             if (mNotificationIcons) disable1 |= DISABLE_NOTIFICATION_ICONS;
             if (mRotationSuggestion) disable2 |= DISABLE2_ROTATE_SUGGESTIONS;
+            if (mQuickSettings) disable2 |= DISABLE2_QUICK_SETTINGS;
 
             return new Pair<Integer, Integer>(disable1, disable2);
         }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e73f471..e44e776 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.adservices.AdServicesFrameworkInitializer;
@@ -28,6 +30,9 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.ambientcontext.AmbientContextManager;
 import android.app.ambientcontext.IAmbientContextManager;
+import android.app.appfunctions.AppFunctionManager;
+import android.app.appfunctions.AppFunctionManagerConfiguration;
+import android.app.appfunctions.IAppFunctionManager;
 import android.app.appsearch.AppSearchManagerFrameworkInitializer;
 import android.app.blob.BlobStoreManagerFrameworkInitializer;
 import android.app.contentsuggestions.ContentSuggestionsManager;
@@ -44,6 +49,8 @@
 import android.app.search.SearchUiManager;
 import android.app.slice.SliceManager;
 import android.app.smartspace.SmartspaceManager;
+import android.app.supervision.ISupervisionManager;
+import android.app.supervision.SupervisionManager;
 import android.app.time.TimeManager;
 import android.app.timedetector.TimeDetector;
 import android.app.timedetector.TimeDetectorImpl;
@@ -471,7 +478,7 @@
                 if (service == null
                         && ctx.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
                         && android.server.Flags.allowRemovingVpnService()) {
-                    throw new ServiceNotFoundException(Context.VPN_MANAGEMENT_SERVICE);
+                    return null;
                 }
                 return new VpnManager(ctx, service);
             }});
@@ -925,6 +932,23 @@
                 return new CompanionDeviceManager(service, ctx.getOuterContext());
             }});
 
+        if (enableAppFunctionManager()) {
+            registerService(Context.APP_FUNCTION_SERVICE, AppFunctionManager.class,
+                    new CachedServiceFetcher<>() {
+                        @Override
+                        public AppFunctionManager createService(ContextImpl ctx)
+                                throws ServiceNotFoundException {
+                            if (!AppFunctionManagerConfiguration.isSupported(ctx)) {
+                                return null;
+                            }
+                            IAppFunctionManager service;
+                            service = IAppFunctionManager.Stub.asInterface(
+                                    ServiceManager.getServiceOrThrow(Context.APP_FUNCTION_SERVICE));
+                            return new AppFunctionManager(service, ctx.getOuterContext());
+                        }
+                    });
+        }
+
         registerService(Context.VIRTUAL_DEVICE_SERVICE, VirtualDeviceManager.class,
                 new CachedServiceFetcher<VirtualDeviceManager>() {
             @Override
@@ -1684,6 +1708,21 @@
                         return new E2eeContactKeysManager(ctx);
                     }});
 
+        registerService(Context.SUPERVISION_SERVICE, SupervisionManager.class,
+                new CachedServiceFetcher<>() {
+                    @Override
+                    public SupervisionManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        if (!android.app.supervision.flags.Flags.supervisionApi()) {
+                            throw new ServiceNotFoundException(
+                                    "SupervisionManager is not supported");
+                        }
+                        IBinder iBinder = ServiceManager.getServiceOrThrow(
+                                Context.SUPERVISION_SERVICE);
+                        ISupervisionManager service = ISupervisionManager.Stub.asInterface(iBinder);
+                        return new SupervisionManager(ctx, 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.
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 0deb842a..2358d67 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -29,12 +29,7 @@
         },
         {
             "file_patterns": ["(/|^)AppOpsManager.java"],
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.appop"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_appop"
         },
         {
             "file_patterns": ["(/|^)AppOpsManager.java"],
@@ -153,18 +148,7 @@
             "file_patterns": ["(/|^)ContextImpl.java"]
         },
         {
-            "name": "FrameworksCoreTests",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "exclude-annotation": "org.junit.Ignore"
-                },
-                {
-                    "include-filter": "android.content.ContextTest"
-                }
-            ],
+            "name": "FrameworksCoreTests_context",
             "file_patterns": ["(/|^)ContextImpl.java"]
         },
         {
@@ -177,35 +161,13 @@
             ]
         },
         {
-            "name": "FrameworksCoreTests",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "exclude-annotation": "org.junit.Ignore"
-                },
-                {
-                    "include-filter": "android.app.KeyguardManagerTest"
-                }
-            ],
+            "name": "FrameworksCoreTests_keyguard_manager",
             "file_patterns": [
                 "(/|^)KeyguardManager.java"
             ]
         },
         {
-            "name": "FrameworksCoreTests",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "exclude-annotation": "org.junit.Ignore"
-                },
-                {
-                    "include-filter": "android.app.PropertyInvalidatedCacheTests"
-                }
-            ],
+            "name": "FrameworksCoreTests_property_invalidated_cache",
             "file_patterns": [
                 "(/|^)PropertyInvalidatedCache.java"
             ]
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index c83dd65..99e6220 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -66,6 +66,12 @@
     public int taskId;
 
     /**
+     * The current effective uid of the identity of this task.
+     * @hide
+     */
+    public int effectiveUid;
+
+    /**
      * Whether or not this task has any running activities.
      */
     public boolean isRunning;
@@ -491,6 +497,7 @@
     void readFromParcel(Parcel source) {
         userId = source.readInt();
         taskId = source.readInt();
+        effectiveUid = source.readInt();
         displayId = source.readInt();
         isRunning = source.readBoolean();
         baseIntent = source.readTypedObject(Intent.CREATOR);
@@ -541,6 +548,7 @@
     void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(userId);
         dest.writeInt(taskId);
+        dest.writeInt(effectiveUid);
         dest.writeInt(displayId);
         dest.writeBoolean(isRunning);
         dest.writeTypedObject(baseIntent, 0);
@@ -589,6 +597,7 @@
     @Override
     public String toString() {
         return "TaskInfo{userId=" + userId + " taskId=" + taskId
+                + " effectiveUid=" + effectiveUid
                 + " displayId=" + displayId
                 + " isRunning=" + isRunning
                 + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index a249c39..6f8e335 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1274,7 +1274,7 @@
                 ScreenCapture.createSyncCaptureListener();
         try {
             if (!mUiAutomationConnection.takeScreenshot(
-                    new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture)) {
+                    new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture, mDisplayId)) {
                 return null;
             }
         } catch (RemoteException re) {
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 5e21e05..12f2081 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -16,8 +16,6 @@
 
 package android.app;
 
-import static android.view.Display.DEFAULT_DISPLAY;
-
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.annotation.NonNull;
@@ -228,7 +226,8 @@
     }
 
     @Override
-    public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener) {
+    public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener,
+            int displayId) {
         synchronized (mLock) {
             throwIfCalledByNotTrustedUidLocked();
             throwIfShutdownLocked();
@@ -240,7 +239,7 @@
             final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
                     .setSourceCrop(crop)
                     .build();
-            mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs, listener);
+            mWindowManager.captureDisplay(displayId, captureArgs, listener);
         } catch (RemoteException re) {
             re.rethrowAsRuntimeException();
         } finally {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 5903a7f..38f59ad 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1571,12 +1571,29 @@
      */
     @Nullable
     public Rect peekBitmapDimensions(@SetWallpaperFlags int which, boolean returnDefault) {
+        if (multiCrop()) {
+            return peekBitmapDimensionsAsUser(which, returnDefault, mContext.getUserId());
+        }
         checkExactlyOneWallpaperFlagSet(which);
         return sGlobals.peekWallpaperDimensions(mContext, returnDefault, which,
                 mContext.getUserId());
     }
 
     /**
+     * Overload of {@link #peekBitmapDimensions(int, boolean)} with a userId argument.
+     * TODO(b/360120606): remove the SuppressWarnings
+     * @hide
+     */
+    @SuppressWarnings("AndroidFrameworkContextUserId")
+    @FlaggedApi(FLAG_MULTI_CROP)
+    @Nullable
+    public Rect peekBitmapDimensionsAsUser(@SetWallpaperFlags int which, boolean returnDefault,
+            int userId) {
+        checkExactlyOneWallpaperFlagSet(which);
+        return sGlobals.peekWallpaperDimensions(mContext, returnDefault, which, userId);
+    }
+
+    /**
      * For the current user, given a list of display sizes, return a list of rectangles representing
      * the area of the current wallpaper that would be shown for each of these sizes.
      *
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index a12faca..c6d0f61 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -34,6 +34,7 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 import android.util.proto.WireTypeMismatchException;
@@ -55,6 +56,7 @@
  * @hide
  */
 @TestApi
+@RavenwoodKeepWholeClass
 public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
     /**
      * bounds that can differ from app bounds, which may include things such as insets.
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index fa646a7..4c97dbb 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -81,3 +81,36 @@
          purpose: PURPOSE_BUGFIX
      }
 }
+
+flag {
+     namespace: "backstage_power"
+     name: "rate_limit_get_running_app_processes"
+     description: "Rate limit calls to getRunningAppProcesses using a cache"
+     is_fixed_read_only: true
+     bug: "360374604"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "backstage_power"
+     name: "rate_limit_get_processes_in_error_state"
+     description: "Rate limit calls to getProcessesInErrorState using a cache"
+     is_fixed_read_only: true
+     bug: "361146083"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
+
+flag {
+     namespace: "backstage_power"
+     name: "cache_get_current_user_id"
+     description: "Add caching for getCurrentUserId"
+     is_fixed_read_only: true
+     bug: "361853873"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
diff --git a/core/java/android/app/admin/AccountTypePolicyKey.java b/core/java/android/app/admin/AccountTypePolicyKey.java
index 02e492b..515c1c6 100644
--- a/core/java/android/app/admin/AccountTypePolicyKey.java
+++ b/core/java/android/app/admin/AccountTypePolicyKey.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
 import android.os.Bundle;
 import android.os.Parcel;
 
@@ -54,9 +53,7 @@
     @TestApi
     public AccountTypePolicyKey(@NonNull String key, @NonNull String accountType) {
         super(key);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
-        }
+        PolicySizeVerifier.enforceMaxStringLength(accountType, "accountType");
         mAccountType = Objects.requireNonNull((accountType));
     }
 
diff --git a/core/java/android/app/admin/BundlePolicyValue.java b/core/java/android/app/admin/BundlePolicyValue.java
index c993671..00e67e6 100644
--- a/core/java/android/app/admin/BundlePolicyValue.java
+++ b/core/java/android/app/admin/BundlePolicyValue.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
 import android.os.Bundle;
 import android.os.Parcel;
 
@@ -31,9 +30,7 @@
 
     public BundlePolicyValue(Bundle value) {
         super(value);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
-        }
+        PolicySizeVerifier.enforceMaxBundleFieldsLength(value);
     }
 
     private BundlePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/ComponentNamePolicyValue.java b/core/java/android/app/admin/ComponentNamePolicyValue.java
index a7a2f7d..f092b7b 100644
--- a/core/java/android/app/admin/ComponentNamePolicyValue.java
+++ b/core/java/android/app/admin/ComponentNamePolicyValue.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
 import android.content.ComponentName;
 import android.os.Parcel;
 
@@ -31,9 +30,7 @@
 
     public ComponentNamePolicyValue(@NonNull ComponentName value) {
         super(value);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxComponentNameLength(value);
-        }
+        PolicySizeVerifier.enforceMaxComponentNameLength(value);
     }
 
     private ComponentNamePolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 46c9e78..cb2b8ad 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,12 +16,8 @@
 
 package android.app.admin;
 
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
-
-import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -195,7 +191,6 @@
      * DPCs should set the value of attribute "headless-device-owner-mode" inside the
      * "headless-system-user" tag as "single_user".
      */
-    @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
     public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
 
     /**
@@ -392,11 +387,8 @@
                     }
                     mSupportsTransferOwnership = true;
                 } else if (tagName.equals("headless-system-user")) {
-                    String deviceOwnerModeStringValue = null;
-                    if (Flags.headlessSingleUserCompatibilityFix()) {
-                        deviceOwnerModeStringValue = parser.getAttributeValue(
-                                 null, "headless-device-owner-mode");
-                    }
+                    String deviceOwnerModeStringValue = parser.getAttributeValue(
+                            null, "headless-device-owner-mode");
                     if (deviceOwnerModeStringValue == null) {
                         deviceOwnerModeStringValue =
                                 parser.getAttributeValue(null, "device-owner-mode");
@@ -409,13 +401,8 @@
                     } else if ("single_user".equalsIgnoreCase(deviceOwnerModeStringValue)) {
                         mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
                     } else {
-                        if (Flags.headlessSingleUserCompatibilityFix()) {
-                            Log.e(TAG, "Unknown headless-system-user mode: "
-                                    + deviceOwnerModeStringValue);
-                        } else {
-                            throw new XmlPullParserException(
-                                    "headless-system-user mode must be valid");
-                        }
+                        Log.e(TAG, "Unknown headless-system-user mode: "
+                                + deviceOwnerModeStringValue);
                     }
                 }
             }
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index eeaf0b3..c0e435c 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,9 +16,6 @@
 
 package android.app.admin;
 
-import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
-
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -50,7 +47,6 @@
     /**
      * String identifier for {@link DevicePolicyManager#setSecurityLoggingEnabled}.
      */
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     public static final String SECURITY_LOGGING_POLICY = "securityLogging";
 
     /**
@@ -58,7 +54,6 @@
      *
      * @hide
      */
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     @SystemApi
     public static final String AUDIT_LOGGING_POLICY = "auditLogging";
 
@@ -188,13 +183,11 @@
     /**
      * String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
      */
-    @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
     public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
 
     /**
      * String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
      */
-    @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
     public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 965e3c4..0f54cb7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -54,13 +54,8 @@
 import static android.Manifest.permission.SET_TIME;
 import static android.Manifest.permission.SET_TIME_ZONE;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
 import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
 import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
 import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
 import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
 import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
@@ -2989,7 +2984,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
     public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17;
 
     /**
@@ -4334,11 +4328,13 @@
      */
     @RequiresPermission(value = MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, conditional = true)
     @FlaggedApi(android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED)
+    @UserHandleAware
     public @ContentProtectionPolicy int getContentProtectionPolicy(@Nullable ComponentName admin) {
         throwIfParentInstance("getContentProtectionPolicy");
         if (mService != null) {
             try {
-                return mService.getContentProtectionPolicy(admin, mContext.getPackageName());
+                return mService.getContentProtectionPolicy(admin, mContext.getPackageName(),
+                        myUserId());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -10480,10 +10476,6 @@
     @WorkerThread
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
-        if (!Flags.dmrhSetAppRestrictions()) {
-            throwIfParentInstance("setApplicationRestrictions");
-        }
-
         if (mService != null) {
             try {
                 mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
@@ -11888,9 +11880,6 @@
     @WorkerThread
     public @NonNull Bundle getApplicationRestrictions(
             @Nullable ComponentName admin, String packageName) {
-        if (!Flags.dmrhSetAppRestrictions()) {
-            throwIfParentInstance("getApplicationRestrictions");
-        }
 
         if (mService != null) {
             try {
@@ -14235,21 +14224,11 @@
      */
     public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
         throwIfParentInstance("getParentProfileInstance");
-        try {
-            if (Flags.dmrhSetAppRestrictions()) {
-                UserManager um = mContext.getSystemService(UserManager.class);
-                if (!um.isManagedProfile()) {
-                    throw new SecurityException("The current user does not have a parent profile.");
-                }
-            } else {
-                if (!mService.isManagedProfile(admin)) {
-                    throw new SecurityException("The current user does not have a parent profile.");
-                }
-            }
-            return new DevicePolicyManager(mContext, mService, true);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        UserManager um = mContext.getSystemService(UserManager.class);
+        if (!um.isManagedProfile()) {
+            throw new SecurityException("The current user does not have a parent profile.");
         }
+        return new DevicePolicyManager(mContext, mService, true);
     }
 
     /**
@@ -14333,7 +14312,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
     public void setAuditLogEnabled(boolean enabled) {
         throwIfParentInstance("setAuditLogEnabled");
@@ -14350,7 +14328,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
     public boolean isAuditLogEnabled() {
         throwIfParentInstance("isAuditLogEnabled");
@@ -14372,7 +14349,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
     public void setAuditLogEventCallback(
             @NonNull @CallbackExecutor Executor executor,
@@ -14399,7 +14375,6 @@
      * @hide
      */
     @SystemApi
-    @FlaggedApi(FLAG_SECURITY_LOG_V2_ENABLED)
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING)
     public void clearAuditLogEventCallback() {
         throwIfParentInstance("clearAuditLogEventCallback");
@@ -17747,7 +17722,6 @@
      * @throws SecurityException if the caller is not authorized to call this method.
      * @return ids of all managed subscriptions currently downloaded by an admin on the device.
      */
-    @FlaggedApi(FLAG_ESIM_MANAGEMENT_ENABLED)
     @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS)
     @NonNull
     public Set<Integer> getSubscriptionIds() {
@@ -17816,7 +17790,6 @@
      */
     @TestApi
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
-    @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
     public void forceSetMaxPolicyStorageLimit(int storageLimit) {
         if (mService != null) {
             try {
@@ -17834,7 +17807,6 @@
      */
     @TestApi
     @RequiresPermission(permission.MANAGE_DEVICE_POLICY_STORAGE_LIMIT)
-    @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
     public int getPolicySizeForAdmin(@NonNull EnforcingAdmin admin) {
         if (mService != null) {
             try {
@@ -17853,13 +17825,9 @@
      * @hide
      */
     @TestApi
-    @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_PROVISIONING_FIX_ENABLED)
     @RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
     @DeviceAdminInfo.HeadlessDeviceOwnerMode
     public int getHeadlessDeviceOwnerMode() {
-        if (!Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
-            return HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
-        }
         if (mService != null) {
             try {
                 return mService.getHeadlessDeviceOwnerMode(mContext.getPackageName());
diff --git a/core/java/android/app/admin/EnforcingAdmin.java b/core/java/android/app/admin/EnforcingAdmin.java
index f70a53f..5f9bb9c 100644
--- a/core/java/android/app/admin/EnforcingAdmin.java
+++ b/core/java/android/app/admin/EnforcingAdmin.java
@@ -16,9 +16,6 @@
 
 package android.app.admin;
 
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED;
-
-import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -64,7 +61,6 @@
      *
      * @hide
      */
-    @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_INTERNAL_BUG_FIX_ENABLED)
     @TestApi
     public EnforcingAdmin(
             @NonNull String packageName, @NonNull Authority authority,
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c393a9e..d4e5c99 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -621,7 +621,7 @@
     void calculateHasIncompatibleAccounts();
 
     void setContentProtectionPolicy(in ComponentName who, String callerPackageName, int policy);
-    int getContentProtectionPolicy(in ComponentName who, String callerPackageName);
+    int getContentProtectionPolicy(in ComponentName who, String callerPackageName, int userId);
 
     int[] getSubscriptionIds(String callerPackageName);
 
diff --git a/core/java/android/app/admin/LockTaskPolicy.java b/core/java/android/app/admin/LockTaskPolicy.java
index 68b4ad8..ab32d46 100644
--- a/core/java/android/app/admin/LockTaskPolicy.java
+++ b/core/java/android/app/admin/LockTaskPolicy.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.app.admin.flags.Flags;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -135,10 +134,8 @@
     }
 
     private void setPackagesInternal(Set<String> packages) {
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            for (String p : packages) {
-                PolicySizeVerifier.enforceMaxPackageNameLength(p);
-            }
+        for (String p : packages) {
+            PolicySizeVerifier.enforceMaxPackageNameLength(p);
         }
         mPackages = new HashSet<>(packages);
     }
diff --git a/core/java/android/app/admin/PackagePermissionPolicyKey.java b/core/java/android/app/admin/PackagePermissionPolicyKey.java
index 1a04f6c..226c576 100644
--- a/core/java/android/app/admin/PackagePermissionPolicyKey.java
+++ b/core/java/android/app/admin/PackagePermissionPolicyKey.java
@@ -25,7 +25,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -59,10 +58,8 @@
     public PackagePermissionPolicyKey(@NonNull String identifier, @NonNull String packageName,
             @NonNull String permissionName) {
         super(identifier);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
-            PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
-        }
+        PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
+        PolicySizeVerifier.enforceMaxStringLength(permissionName, "permissionName");
         mPackageName = Objects.requireNonNull((packageName));
         mPermissionName = Objects.requireNonNull((permissionName));
     }
diff --git a/core/java/android/app/admin/PackagePolicyKey.java b/core/java/android/app/admin/PackagePolicyKey.java
index 9e31a23..8fa21db 100644
--- a/core/java/android/app/admin/PackagePolicyKey.java
+++ b/core/java/android/app/admin/PackagePolicyKey.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -55,9 +54,7 @@
     @TestApi
     public PackagePolicyKey(@NonNull String key, @NonNull String packageName) {
         super(key);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
-        }
+        PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
         mPackageName = Objects.requireNonNull((packageName));
     }
 
diff --git a/core/java/android/app/admin/PackageSetPolicyValue.java b/core/java/android/app/admin/PackageSetPolicyValue.java
index 8b253a2..24c50b0 100644
--- a/core/java/android/app/admin/PackageSetPolicyValue.java
+++ b/core/java/android/app/admin/PackageSetPolicyValue.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
 import android.os.Parcel;
 
 import java.util.HashSet;
@@ -32,10 +31,8 @@
 
     public PackageSetPolicyValue(@NonNull Set<String> value) {
         super(value);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            for (String packageName : value) {
-                PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
-            }
+        for (String packageName : value) {
+            PolicySizeVerifier.enforceMaxPackageNameLength(packageName);
         }
     }
 
diff --git a/core/java/android/app/admin/Provisioning_OWNERS b/core/java/android/app/admin/Provisioning_OWNERS
index 91b9761..09ebb26 100644
--- a/core/java/android/app/admin/Provisioning_OWNERS
+++ b/core/java/android/app/admin/Provisioning_OWNERS
@@ -1,4 +1,7 @@
 # Assign bugs to [email protected]
 [email protected]
 [email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
 file:EnterprisePlatform_OWNERS
diff --git a/core/java/android/app/admin/StringPolicyValue.java b/core/java/android/app/admin/StringPolicyValue.java
index 6efe9ad..bb07c23 100644
--- a/core/java/android/app/admin/StringPolicyValue.java
+++ b/core/java/android/app/admin/StringPolicyValue.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.admin.flags.Flags;
 import android.os.Parcel;
 
 import java.util.Objects;
@@ -30,9 +29,7 @@
 
     public StringPolicyValue(@NonNull String value) {
         super(value);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
-        }
+        PolicySizeVerifier.enforceMaxStringLength(value, "policyValue");
     }
 
     private StringPolicyValue(Parcel source) {
diff --git a/core/java/android/app/admin/UserRestrictionPolicyKey.java b/core/java/android/app/admin/UserRestrictionPolicyKey.java
index 9054287..16cfba4 100644
--- a/core/java/android/app/admin/UserRestrictionPolicyKey.java
+++ b/core/java/android/app/admin/UserRestrictionPolicyKey.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
 import android.os.Bundle;
 import android.os.Parcel;
 
@@ -45,9 +44,7 @@
     @TestApi
     public UserRestrictionPolicyKey(@NonNull String identifier, @NonNull String restriction) {
         super(identifier);
-        if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
-        }
+        PolicySizeVerifier.enforceMaxStringLength(restriction, "restriction");
         mRestriction = Objects.requireNonNull(restriction);
     }
 
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 9148e3c..9ed5aa6 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -4,6 +4,7 @@
 package: "android.app.admin.flags"
 container: "system"
 
+# Fully rolled out and must not be used.
 flag {
   name: "policy_engine_migration_v2_enabled"
   is_exported: true
@@ -28,16 +29,6 @@
 }
 
 flag {
-  name: "device_policy_size_tracking_internal_bug_fix_enabled"
-  namespace: "enterprise"
-  description: "Bug fix for tracking the total policy size and have a max threshold"
-  bug: "281543351"
-  metadata {
-      purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "onboarding_bugreport_v2_enabled"
   is_exported: true
   namespace: "enterprise"
@@ -77,13 +68,6 @@
 }
 
 flag {
-  name: "permission_migration_for_zero_trust_impl_enabled"
-  namespace: "enterprise"
-  description: "(Implementation) Migrate existing APIs to permission based, and enable DMRH to call them to collect Zero Trust signals."
-  bug: "289520697"
-}
-
-flag {
   name: "device_theft_api_enabled"
   is_exported: true
   namespace: "enterprise"
@@ -99,13 +83,71 @@
 }
 
 flag {
-  name: "coexistence_migration_for_non_emm_management_enabled"
+  name: "coexistence_migration_for_supervision_enabled"
+  is_exported: true
   namespace: "enterprise"
-  description: "Migrate existing APIs to be coexistable, and enable DMRH to call them to support non-EMM device management."
-  bug: "289520697"
+  description: "Migrate existing APIs that are used by supervision (Kids Module) to be coexistable."
+  bug: "356894721"
 }
 
 flag {
+ name: "reset_password_with_token_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for resetPasswordWithToken and setResetPasswordToken."
+ bug: "359187209"
+}
+
+flag {
+ name: "set_keyguard_disabled_features_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setKeyguardDisabledFeatures."
+ bug: "359186276"
+}
+
+flag {
+ name: "set_application_restrictions_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setApplicationRestrictions."
+ bug: "359188153"
+}
+
+flag {
+ name: "set_auto_time_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeEnabled."
+ bug: "359188869"
+}
+
+flag {
+ name: "set_backup_service_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setBackupServiceEnabled."
+ bug: "359188483"
+}
+
+flag {
+ name: "set_auto_time_zone_enabled_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setAutoTimeZoneEnabled."
+ bug: "364338300"
+}
+
+flag {
+ name: "set_permission_grant_state_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for setPermissionGrantState."
+ bug: "364338410"
+}
+
+# Fully rolled out and must not be used.
+flag {
   name: "security_log_v2_enabled"
   is_exported: true
   namespace: "enterprise"
@@ -114,23 +156,6 @@
 }
 
 flag {
-  name: "hsum_unlock_notification_fix"
-  namespace: "enterprise"
-  description: "Using the right userId when starting the work profile unlock flow "
-  bug: "327350831"
-  metadata {
-      purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "dumpsys_policy_engine_migration_enabled"
-  namespace: "enterprise"
-  description: "Update DumpSys to include information about migrated APIs in DPE"
-  bug: "304999634"
-}
-
-flag {
     name: "allow_querying_profile_type"
     is_exported: true
     namespace: "enterprise"
@@ -179,6 +204,7 @@
   bug: "295301164"
 }
 
+# Fully rolled out and must not be used.
 flag {
   name: "headless_device_owner_single_user_enabled"
   is_exported: true
@@ -196,36 +222,6 @@
 }
 
 flag {
-  name: "copy_account_with_retry_enabled"
-  namespace: "enterprise"
-  description: "Retry copy and remove account from personal to work profile in case of failure"
-  bug: "329424312"
-  metadata {
-      purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "power_exemption_bg_usage_fix"
-  namespace: "enterprise"
-  description: "Ensure aps with EXEMPT_FROM_POWER_RESTRICTIONS can execute in the background"
-  bug: "333379020"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "disallow_user_control_bg_usage_fix"
-  namespace: "enterprise"
-  description: "Make DPM.setUserControlDisabledPackages() ensure background usage is allowed"
-  bug: "326031059"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "disallow_user_control_stopped_state_fix"
   namespace: "enterprise"
   description: "Ensure DPM.setUserControlDisabledPackages() clears FLAG_STOPPED for the app"
@@ -243,43 +239,6 @@
 }
 
 flag {
-  name: "headless_device_owner_provisioning_fix_enabled"
-  namespace: "enterprise"
-  description: "Fix provisioning for single-user headless DO"
-  bug: "289515470"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "dmrh_set_app_restrictions"
-  namespace: "enterprise"
-  description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
-  bug: "328758346"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "allow_screen_brightness_control_on_cope"
-  namespace: "enterprise"
-  description: "Allow COPE admin to control screen brightness and timeout."
-  bug: "323894620"
-}
-
-flag {
-  name: "always_persist_do"
-  namespace: "enterprise"
-  description: "Always write device_owners2.xml so that migration flags aren't lost"
-  bug: "335232744"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "is_recursive_required_app_merging_enabled"
   namespace: "enterprise"
   description: "Guards a new flow for recursive required enterprise app list merging"
@@ -287,26 +246,6 @@
 }
 
 flag {
-  name: "headless_device_owner_delegate_security_logging_bug_fix"
-  namespace: "enterprise"
-  description: "Fix delegate security logging for single user headless DO."
-  bug: "289515470"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
-  name: "headless_single_user_bad_device_admin_state_fix"
-  namespace: "enterprise"
-  description: "Fix the bad state in DPMS caused by an earlier bug related to the headless single user change"
-  bug: "332477138"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "onboarding_bugreport_storage_bug_fix"
   namespace: "enterprise"
   description: "Add a separate storage limit for deferred bugreports"
@@ -317,16 +256,6 @@
 }
 
 flag {
-  name: "delete_private_space_under_restriction"
-  namespace: "enterprise"
-  description: "Delete private space if user restriction is set"
-  bug: "328758346"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
     name: "unmanaged_mode_migration"
     namespace: "enterprise"
     description: "Migrate APIs for unmanaged mode"
@@ -337,16 +266,6 @@
 }
 
 flag {
-    name: "headless_single_user_fixes"
-    namespace: "enterprise"
-    description: "Various fixes for headless single user mode"
-    bug: "289515470"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "backup_connected_apps_settings"
     namespace: "enterprise"
     description: "backup and restore connected work and personal apps user settings across devices"
@@ -354,16 +273,6 @@
 }
 
 flag {
-    name: "headless_single_user_compatibility_fix"
-    namespace: "enterprise"
-    description: "Fix for compatibility issue introduced from using single_user mode on pre-Android V builds"
-    bug: "338050276"
-    metadata {
-      purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "headless_single_min_target_sdk"
     namespace: "enterprise"
     description: "Only allow DPCs targeting Android V to provision into single user mode"
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
new file mode 100644
index 0000000..4682f3d
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -0,0 +1,122 @@
+/*
+ * 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.app.appfunctions;
+
+import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.annotation.UserHandleAware;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Provides app functions related functionalities.
+ *
+ * <p>App function is a specific piece of functionality that an app offers to the system. These
+ * functionalities can be integrated into various system features.
+ */
+// TODO(b/357551503): Implement get and set enabled app function APIs.
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+@SystemService(Context.APP_FUNCTION_SERVICE)
+public final class AppFunctionManager {
+    private final IAppFunctionManager mService;
+    private final Context mContext;
+
+    /**
+     * Creates an instance.
+     *
+     * @param service An interface to the backing service.
+     * @param context A {@link Context}.
+     * @hide
+     */
+    public AppFunctionManager(IAppFunctionManager service, Context context) {
+        mService = service;
+        mContext = context;
+    }
+
+    /**
+     * 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 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}.
+     */
+    // TODO(b/360864791): Document that apps can opt-out from being executed by callers with
+    //   EXECUTE_APP_FUNCTIONS and how a caller knows whether a function is opted out.
+    // TODO(b/357551503): Update documentation when get / set APIs are implemented that this will
+    //   also return RESULT_DENIED if the app function is disabled.
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
+    @UserHandleAware
+    public void executeAppFunction(
+            @NonNull ExecuteAppFunctionRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback) {
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        ExecuteAppFunctionAidlRequest aidlRequest =
+                new ExecuteAppFunctionAidlRequest(
+                        request, mContext.getUser(), mContext.getPackageName());
+        try {
+            mService.executeAppFunction(
+                    aidlRequest,
+                    new IExecuteAppFunctionCallback.Stub() {
+                        @Override
+                        public void onResult(ExecuteAppFunctionResponse result) {
+                            try {
+                                executor.execute(() -> callback.accept(result));
+                            } catch (RuntimeException e) {
+                                // Ideally shouldn't happen since errors are wrapped into the
+                                // response, but we catch it here for additional safety.
+                                callback.accept(
+                                        ExecuteAppFunctionResponse.newFailure(
+                                                getResultCode(e),
+                                                e.getMessage(),
+                                                /* extras= */ null));
+                            }
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
new file mode 100644
index 0000000..fa77e79
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerConfiguration.java
@@ -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 android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.enableAppFunctionManager;
+
+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
+ * systems.
+ *
+ * @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;
+    }
+
+    /**
+     * Indicates whether the current target is intended to support {@code AppFunctionManager}.
+     *
+     * @return {@code true} if supported; otherwise {@code false}
+     */
+    public boolean isSupported() {
+        return enableAppFunctionManager() && !isWatch();
+    }
+
+    /**
+     * Indicates whether the current target is intended to support {@code AppFunctionManager}.
+     *
+     * @param context context
+     * @return {@code true} if supported; otherwise {@code false}
+     */
+    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/AppFunctionManagerHelper.java b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
new file mode 100644
index 0000000..d6f45e4
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionManagerHelper.java
@@ -0,0 +1,222 @@
+/*
+ * 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.app.appfunctions;
+
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID;
+import static android.app.appfunctions.AppFunctionRuntimeMetadata.PROPERTY_ENABLED;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.APP_FUNCTION_INDEXER_PACKAGE;
+import static android.app.appfunctions.AppFunctionStaticMetadataHelper.STATIC_PROPERTY_ENABLED_BY_DEFAULT;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.JoinSpec;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
+import android.os.OutcomeReceiver;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class containing utilities for {@link AppFunctionManager}.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionManagerHelper {
+
+    /**
+     * Returns (through a callback) a boolean indicating whether the app function is enabled.
+     *
+     * <p>This method can only check app functions that are owned by the caller owned by packages
+     * visible to the caller.
+     *
+     * <p>If operation fails, the callback's {@link OutcomeReceiver#onError} is called with errors:
+     *
+     * <ul>
+     *   <li>{@link IllegalArgumentException}, if the function is not found
+     *   <li>{@link SecurityException}, if the caller does not have permission to query the target
+     *       package
+     * </ul>
+     *
+     * @param functionIdentifier the identifier of the app function to check (unique within the
+     *     target package) and in most cases, these are automatically generated by the AppFunctions
+     *     SDK
+     * @param targetPackage the package name of the app function's owner
+     * @param appSearchExecutor the executor to run the metadata search mechanism through AppSearch
+     * @param callbackExecutor the executor to run the callback
+     * @param callback the callback to receive the function enabled check result
+     * @hide
+     */
+    public static void isAppFunctionEnabled(
+            @NonNull String functionIdentifier,
+            @NonNull String targetPackage,
+            @NonNull AppSearchManager appSearchManager,
+            @NonNull Executor appSearchExecutor,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull OutcomeReceiver<Boolean, Exception> callback) {
+        Objects.requireNonNull(functionIdentifier);
+        Objects.requireNonNull(targetPackage);
+        Objects.requireNonNull(appSearchManager);
+        Objects.requireNonNull(appSearchExecutor);
+        Objects.requireNonNull(callbackExecutor);
+        Objects.requireNonNull(callback);
+
+        appSearchManager.createGlobalSearchSession(
+                appSearchExecutor,
+                (searchSessionResult) -> {
+                    if (!searchSessionResult.isSuccess()) {
+                        callbackExecutor.execute(
+                                () ->
+                                        callback.onError(
+                                                failedResultToException(searchSessionResult)));
+                        return;
+                    }
+                    try (GlobalSearchSession searchSession = searchSessionResult.getResultValue()) {
+                        SearchResults results =
+                                searchJoinedStaticWithRuntimeAppFunctions(
+                                        searchSession, targetPackage, functionIdentifier);
+                        results.getNextPage(
+                                appSearchExecutor,
+                                listAppSearchResult ->
+                                        callbackExecutor.execute(
+                                                () -> {
+                                                    if (listAppSearchResult.isSuccess()) {
+                                                        callback.onResult(
+                                                                getEnabledStateFromSearchResults(
+                                                                        Objects.requireNonNull(
+                                                                                listAppSearchResult
+                                                                        .getResultValue())));
+                                                    } else {
+                                                        callback.onError(
+                                                                failedResultToException(
+                                                                        listAppSearchResult));
+                                                    }
+                                                }));
+                    } catch (Exception e) {
+                        callbackExecutor.execute(() -> callback.onError(e));
+                    }
+                });
+    }
+
+    /**
+     * Searches joined app function static and runtime metadata using the function Id and the
+     * package.
+     *
+     * @hide
+     */
+    private static @NonNull SearchResults searchJoinedStaticWithRuntimeAppFunctions(
+            @NonNull GlobalSearchSession session,
+            @NonNull String targetPackage,
+            @NonNull String functionIdentifier) {
+        SearchSpec runtimeSearchSpec =
+                getAppFunctionRuntimeMetadataSearchSpecByFunctionId(targetPackage);
+        JoinSpec joinSpec =
+                new JoinSpec.Builder(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+                        .setNestedSearch(functionIdentifier, runtimeSearchSpec)
+                        .build();
+        SearchSpec joinedStaticWithRuntimeSearchSpec =
+                new SearchSpec.Builder()
+                        .setJoinSpec(joinSpec)
+                        .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+                        .addFilterSchemas(
+                                AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage(
+                                        targetPackage))
+                        .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                        .build();
+        return session.search(functionIdentifier, joinedStaticWithRuntimeSearchSpec);
+    }
+
+    /**
+     * Finds whether the function is enabled or not from the search results returned by {@link
+     * #searchJoinedStaticWithRuntimeAppFunctions}.
+     *
+     * @throws IllegalArgumentException if the function is not found in the results
+     * @hide
+     */
+    private static boolean getEnabledStateFromSearchResults(
+            @NonNull List<SearchResult> joinedStaticRuntimeResults) {
+        if (joinedStaticRuntimeResults.isEmpty()) {
+            // Function not found.
+            throw new IllegalArgumentException("App function not found.");
+        } else {
+            List<SearchResult> runtimeMetadataResults =
+                    joinedStaticRuntimeResults.getFirst().getJoinedResults();
+            if (!runtimeMetadataResults.isEmpty()) {
+                Boolean result =
+                        (Boolean)
+                                runtimeMetadataResults
+                                        .getFirst()
+                                        .getGenericDocument()
+                                        .getProperty(PROPERTY_ENABLED);
+                if (result != null) {
+                    return result;
+                }
+            }
+            // Runtime metadata not found. Using the default value in the static metadata.
+            return joinedStaticRuntimeResults
+                    .getFirst()
+                    .getGenericDocument()
+                    .getPropertyBoolean(STATIC_PROPERTY_ENABLED_BY_DEFAULT);
+        }
+    }
+
+    /**
+     * Returns search spec that queries app function metadata for a specific package name by its
+     * function identifier.
+     *
+     * @hide
+     */
+    public static @NonNull SearchSpec getAppFunctionRuntimeMetadataSearchSpecByFunctionId(
+            @NonNull String targetPackage) {
+        return new SearchSpec.Builder()
+                .addFilterPackageNames(APP_FUNCTION_INDEXER_PACKAGE)
+                .addFilterSchemas(
+                        AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage))
+                .addFilterProperties(
+                        AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(targetPackage),
+                        List.of(AppFunctionRuntimeMetadata.PROPERTY_FUNCTION_ID))
+                .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
+                .build();
+    }
+
+    /**
+     * Converts a failed app search result codes into an exception.
+     *
+     * @hide
+     */
+    public static @NonNull 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());
+        };
+    }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
new file mode 100644
index 0000000..c4e8b41
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -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 android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * Represents runtime function metadata of an app function.
+ *
+ * <p>This is a temporary solution for app function indexing, as later we would like to index the
+ * actual function signature entity class shape instead of just the schema info.
+ *
+ * @hide
+ */
+// TODO(b/357551503): Link to canonical docs rather than duplicating once they
+// are available.
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionRuntimeMetadata extends GenericDocument {
+    public static final String RUNTIME_SCHEMA_TYPE = "AppFunctionRuntimeMetadata";
+    public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+    public static final String APP_FUNCTION_RUNTIME_METADATA_DB = "appfunctions-db";
+    public static final String APP_FUNCTION_RUNTIME_NAMESPACE = "app_functions_runtime";
+    public static final String PROPERTY_FUNCTION_ID = "functionId";
+    public static final String PROPERTY_PACKAGE_NAME = "packageName";
+    public static final String PROPERTY_ENABLED = "enabled";
+    public static final String PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID =
+            "appFunctionStaticMetadataQualifiedId";
+    private static final String TAG = "AppSearchAppFunction";
+    private static final String RUNTIME_SCHEMA_TYPE_SEPARATOR = "-";
+
+    public AppFunctionRuntimeMetadata(@NonNull GenericDocument genericDocument) {
+        super(genericDocument);
+    }
+
+    /** Returns a per-app runtime metadata schema name, to store all functions for that package. */
+    public static String getRuntimeSchemaNameForPackage(@NonNull String pkg) {
+        return RUNTIME_SCHEMA_TYPE + RUNTIME_SCHEMA_TYPE_SEPARATOR + Objects.requireNonNull(pkg);
+    }
+
+    /** Returns the document id for an app function's runtime metadata. */
+    public static String getDocumentIdForAppFunction(
+            @NonNull String pkg, @NonNull String functionId) {
+        return pkg + "/" + functionId;
+    }
+
+    /**
+     * Different packages have different visibility requirements. To allow for different visibility,
+     * we need to have per-package app function schemas.
+     *
+     * <p>This schema should be set visible to callers from the package owner itself and for callers
+     * with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
+     * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+     *
+     * @param packageName The package name to create a schema for.
+     */
+    @NonNull
+    public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) {
+        return new AppSearchSchema.Builder(getRuntimeSchemaNameForPackage(packageName))
+                .addProperty(
+                        new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingType(
+                                        AppSearchSchema.StringPropertyConfig
+                                                .INDEXING_TYPE_EXACT_TERMS)
+                                .setTokenizerType(
+                                        AppSearchSchema.StringPropertyConfig
+                                                .TOKENIZER_TYPE_VERBATIM)
+                                .build())
+                .addProperty(
+                        new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_PACKAGE_NAME)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setIndexingType(
+                                        AppSearchSchema.StringPropertyConfig
+                                                .INDEXING_TYPE_EXACT_TERMS)
+                                .setTokenizerType(
+                                        AppSearchSchema.StringPropertyConfig
+                                                .TOKENIZER_TYPE_VERBATIM)
+                                .build())
+                .addProperty(
+                        new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .build())
+                .addProperty(
+                        new AppSearchSchema.StringPropertyConfig.Builder(
+                                        PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID)
+                                .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                .setJoinableValueType(
+                                        AppSearchSchema.StringPropertyConfig
+                                                .JOINABLE_VALUE_TYPE_QUALIFIED_ID)
+                                .build())
+                .addParentType(RUNTIME_SCHEMA_TYPE)
+                .build();
+    }
+
+    /** Returns the function id. This might look like "com.example.message#send_message". */
+    @NonNull
+    public String getFunctionId() {
+        return Objects.requireNonNull(getPropertyString(PROPERTY_FUNCTION_ID));
+    }
+
+    /** Returns the package name of the package that owns this function. */
+    @NonNull
+    public String getPackageName() {
+        return Objects.requireNonNull(getPropertyString(PROPERTY_PACKAGE_NAME));
+    }
+
+    /**
+     * Returns if the function is set to be enabled or not. If not set, the {@link
+     * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT} value would be used.
+     */
+    @Nullable
+    public Boolean getEnabled() {
+        return (Boolean) getProperty(PROPERTY_ENABLED);
+    }
+
+    /** Returns the qualified id linking to the static metadata of the app function. */
+    @Nullable
+    @VisibleForTesting
+    public String getAppFunctionStaticMetadataQualifiedId() {
+        return getPropertyString(PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID);
+    }
+
+    public static final class Builder extends GenericDocument.Builder<Builder> {
+        /**
+         * Creates a Builder for a {@link AppFunctionRuntimeMetadata}.
+         *
+         * @param packageName the name of the package that owns the function.
+         * @param functionId the id of the function.
+         * @param staticMetadataQualifiedId the qualified static metadata id that this runtime
+         *     metadata refers to.
+         */
+        public Builder(
+                @NonNull String packageName,
+                @NonNull String functionId,
+                @NonNull String staticMetadataQualifiedId) {
+            super(
+                    APP_FUNCTION_RUNTIME_NAMESPACE,
+                    getDocumentIdForAppFunction(
+                            Objects.requireNonNull(packageName),
+                            Objects.requireNonNull(functionId)),
+                    getRuntimeSchemaNameForPackage(packageName));
+            setPropertyString(PROPERTY_PACKAGE_NAME, packageName);
+            setPropertyString(PROPERTY_FUNCTION_ID, functionId);
+
+            // Set qualified id automatically
+            setPropertyString(
+                    PROPERTY_APP_FUNCTION_STATIC_METADATA_QUALIFIED_ID, staticMetadataQualifiedId);
+        }
+
+        /**
+         * 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}).
+         */
+        @NonNull
+        public Builder setEnabled(boolean enabled) {
+            setPropertyBoolean(PROPERTY_ENABLED, enabled);
+            return this;
+        }
+
+        /** Creates the {@link AppFunctionRuntimeMetadata} GenericDocument. */
+        @NonNull
+        public AppFunctionRuntimeMetadata build() {
+            return new AppFunctionRuntimeMetadata(super.build());
+        }
+    }
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
new file mode 100644
index 0000000..c27141a
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -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 android.app.appfunctions;
+
+import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE;
+import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode;
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+
+import android.annotation.FlaggedApi;
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class to provide app functions to the system.
+ *
+ * <p>Include the following in the manifest:
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".YourService"
+ *       android:permission="android.permission.BIND_APP_FUNCTION_SERVICE">
+ *    <intent-filter>
+ *      <action android:name="android.app.appfunctions.AppFunctionService" />
+ *    </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ *
+ * @see AppFunctionManager
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public abstract class AppFunctionService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service. To be supported, the
+     * service must also require the {@link BIND_APP_FUNCTION_SERVICE} permission so that other
+     * applications can not abuse it.
+     */
+    @NonNull
+    public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
+
+    private final Binder mBinder =
+            new IAppFunctionService.Stub() {
+                @Override
+                public void executeAppFunction(
+                        @NonNull ExecuteAppFunctionRequest request,
+                        @NonNull IExecuteAppFunctionCallback callback) {
+                    if (AppFunctionService.this.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
+                            == PERMISSION_DENIED) {
+                        throw new SecurityException("Can only be called by the system server.");
+                    }
+                    SafeOneTimeExecuteAppFunctionCallback safeCallback =
+                            new SafeOneTimeExecuteAppFunctionCallback(callback);
+                    try {
+                        AppFunctionService.this.onExecuteFunction(request, safeCallback::onResult);
+                    } catch (Exception ex) {
+                        // Apps should handle exceptions. But if they don't, report the error on
+                        // behalf of them.
+                        safeCallback.onResult(
+                                ExecuteAppFunctionResponse.newFailure(
+                                        getResultCode(ex), ex.getMessage(), /* extras= */ null));
+                    }
+                }
+            };
+
+    @NonNull
+    @Override
+    public final IBinder onBind(@Nullable Intent intent) {
+        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.
+     */
+    @MainThread
+    public abstract void onExecuteFunction(
+            @NonNull ExecuteAppFunctionRequest request,
+            @NonNull Consumer<ExecuteAppFunctionResponse> callback);
+}
diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
new file mode 100644
index 0000000..926cc9a
--- /dev/null
+++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java
@@ -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 android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.util.DocumentIdUtil;
+
+import java.util.Objects;
+
+/**
+ * Contains constants and helper related to static metadata represented with {@code
+ * com.android.server.appsearch.appsindexer.appsearchtypes.AppFunctionStaticMetadata}.
+ *
+ * <p>The constants listed here **must not change** and be kept consistent with the canonical static
+ * metadata class.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class AppFunctionStaticMetadataHelper {
+    public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata";
+    public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault";
+
+    public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions";
+
+    // These are constants that has to be kept the same with {@code
+    // com.android.server.appsearch.appsindexer.appsearchtypes.AppSearchHelper}.
+    public static final String APP_FUNCTION_STATIC_METADATA_DB = "apps-db";
+    public static final String APP_FUNCTION_INDEXER_PACKAGE = "android";
+
+    /** Returns a per-app static metadata schema name, to store all functions for that package. */
+    public static String getStaticSchemaNameForPackage(@NonNull String pkg) {
+        return STATIC_SCHEMA_TYPE + "-" + Objects.requireNonNull(pkg);
+    }
+
+    /** Returns the document id for an app function's static metadata. */
+    public static String getDocumentIdForAppFunction(
+            @NonNull String pkg, @NonNull String functionId) {
+        return pkg + "/" + functionId;
+    }
+
+    /**
+     * Returns the fully qualified Id used in AppSearch for the given package and function id app
+     * function static metadata.
+     */
+    public static String getStaticMetadataQualifiedId(String packageName, String functionId) {
+        return DocumentIdUtil.createQualifiedId(
+                APP_FUNCTION_INDEXER_PACKAGE,
+                APP_FUNCTION_STATIC_METADATA_DB,
+                APP_FUNCTION_STATIC_NAMESPACE,
+                getDocumentIdForAppFunction(packageName, functionId));
+    }
+}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.aidl b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.aidl
new file mode 100644
index 0000000..42ec45d
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.aidl
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+package android.app.appfunctions;
+
+import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
+
+/** {@hide} */
+parcelable ExecuteAppFunctionAidlRequest;
\ No newline at end of file
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
new file mode 100644
index 0000000..e623fa1
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionAidlRequest.java
@@ -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 android.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.util.Objects;
+
+/**
+ * An internal request to execute an app function.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public final class ExecuteAppFunctionAidlRequest implements Parcelable {
+
+    public static final Creator<ExecuteAppFunctionAidlRequest> CREATOR =
+            new Creator<ExecuteAppFunctionAidlRequest>() {
+                @Override
+                public ExecuteAppFunctionAidlRequest createFromParcel(Parcel in) {
+                    ExecuteAppFunctionRequest clientRequest =
+                            ExecuteAppFunctionRequest.CREATOR.createFromParcel(in);
+                    UserHandle userHandle = UserHandle.CREATOR.createFromParcel(in);
+                    String callingPackage = in.readString8();
+                    return new ExecuteAppFunctionAidlRequest(
+                            clientRequest, userHandle, callingPackage);
+                }
+
+                @Override
+                public ExecuteAppFunctionAidlRequest[] newArray(int size) {
+                    return new ExecuteAppFunctionAidlRequest[size];
+                }
+            };
+
+    /** The client request to execute an app function. */
+    private final ExecuteAppFunctionRequest mClientRequest;
+
+    /** The user handle of the user to execute the app function. */
+    private final UserHandle mUserHandle;
+
+    /** The package name of the app that is requesting to execute the app function. */
+    private final String mCallingPackage;
+
+    public ExecuteAppFunctionAidlRequest(
+            ExecuteAppFunctionRequest clientRequest, UserHandle userHandle, String callingPackage) {
+        this.mClientRequest = Objects.requireNonNull(clientRequest);
+        this.mUserHandle = Objects.requireNonNull(userHandle);
+        this.mCallingPackage = Objects.requireNonNull(callingPackage);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mClientRequest.writeToParcel(dest, flags);
+        mUserHandle.writeToParcel(dest, flags);
+        dest.writeString8(mCallingPackage);
+    }
+
+    /** Returns the client request to execute an app function. */
+    @NonNull
+    public ExecuteAppFunctionRequest getClientRequest() {
+        return mClientRequest;
+    }
+
+    /** Returns the user handle of the user to execute the app function. */
+    @NonNull
+    public UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    /** Returns the package name of the app that is requesting to execute the app function. */
+    @NonNull
+    public String getCallingPackage() {
+        return mCallingPackage;
+    }
+}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.aidl b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.aidl
new file mode 100644
index 0000000..a0b889e
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.app.appfunctions;
+
+import android.app.appfunctions.ExecuteAppFunctionRequest;
+
+parcelable ExecuteAppFunctionRequest;
\ No newline at end of file
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
new file mode 100644
index 0000000..fe7fd88
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.GenericDocument;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** A request to execute an app function. */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public final class ExecuteAppFunctionRequest implements Parcelable {
+    @NonNull
+    public static final Creator<ExecuteAppFunctionRequest> CREATOR =
+            new Creator<>() {
+                @Override
+                public ExecuteAppFunctionRequest createFromParcel(Parcel parcel) {
+                    String targetPackageName = parcel.readString8();
+                    String functionIdentifier = parcel.readString8();
+                    GenericDocumentWrapper parameters =
+                            GenericDocumentWrapper.CREATOR.createFromParcel(parcel);
+                    Bundle extras = parcel.readBundle(Bundle.class.getClassLoader());
+                    return new ExecuteAppFunctionRequest(
+                            targetPackageName, functionIdentifier, extras, parameters);
+                }
+
+                @Override
+                public ExecuteAppFunctionRequest[] newArray(int size) {
+                    return new ExecuteAppFunctionRequest[size];
+                }
+            };
+
+    /** Returns the package name of the app that hosts the function. */
+    @NonNull private final String mTargetPackageName;
+
+    /**
+     * Returns the unique string identifier of the app function to be executed. TODO(b/357551503):
+     * Document how callers can get the available function identifiers.
+     */
+    @NonNull private final String mFunctionIdentifier;
+
+    /** Returns additional metadata relevant to this function execution request. */
+    @NonNull private final Bundle mExtras;
+
+    /**
+     * Returns the parameters required to invoke this function. Within this [GenericDocument], the
+     * property names are the names of the function parameters and the property values are the
+     * values of those parameters.
+     *
+     * <p>The document may have missing parameters. Developers are advised to implement defensive
+     * handling measures.
+     *
+     * <p>TODO(b/357551503): Document how function parameters can be obtained for function execution
+     */
+    @NonNull private final GenericDocumentWrapper mParameters;
+
+    private ExecuteAppFunctionRequest(
+            @NonNull String targetPackageName,
+            @NonNull String functionIdentifier,
+            @NonNull Bundle extras,
+            @NonNull GenericDocumentWrapper parameters) {
+        mTargetPackageName = Objects.requireNonNull(targetPackageName);
+        mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
+        mExtras = Objects.requireNonNull(extras);
+        mParameters = Objects.requireNonNull(parameters);
+    }
+
+    /** Returns the package name of the app that hosts the function. */
+    @NonNull
+    public String getTargetPackageName() {
+        return mTargetPackageName;
+    }
+
+    /** Returns the unique string identifier of the app function to be executed. */
+    @NonNull
+    public String getFunctionIdentifier() {
+        return mFunctionIdentifier;
+    }
+
+    /**
+     * Returns the function parameters. The key is the parameter name, and the value is the
+     * parameter value.
+     *
+     * <p>The bundle may have missing parameters. Developers are advised to implement defensive
+     * handling measures.
+     */
+    @NonNull
+    public GenericDocument getParameters() {
+        return mParameters.getValue();
+    }
+
+    /** Returns the additional data relevant to this function execution. */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mTargetPackageName);
+        dest.writeString8(mFunctionIdentifier);
+        mParameters.writeToParcel(dest, flags);
+        dest.writeBundle(mExtras);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Builder for {@link ExecuteAppFunctionRequest}. */
+    public static final class Builder {
+        @NonNull private final String mTargetPackageName;
+        @NonNull private final String mFunctionIdentifier;
+        @NonNull private Bundle mExtras = Bundle.EMPTY;
+
+        @NonNull
+        private GenericDocument mParameters = new GenericDocument.Builder<>("", "", "").build();
+
+        public Builder(@NonNull String targetPackageName, @NonNull String functionIdentifier) {
+            mTargetPackageName = Objects.requireNonNull(targetPackageName);
+            mFunctionIdentifier = Objects.requireNonNull(functionIdentifier);
+        }
+
+        /** Sets the additional data relevant to this function execution. */
+        @NonNull
+        public Builder setExtras(@NonNull Bundle extras) {
+            mExtras = Objects.requireNonNull(extras);
+            return this;
+        }
+
+        /** Sets the function parameters. */
+        @NonNull
+        public Builder setParameters(@NonNull GenericDocument parameters) {
+            Objects.requireNonNull(parameters);
+            mParameters = parameters;
+            return this;
+        }
+
+        /** Builds the {@link ExecuteAppFunctionRequest}. */
+        @NonNull
+        public ExecuteAppFunctionRequest build() {
+            return new ExecuteAppFunctionRequest(
+                    mTargetPackageName,
+                    mFunctionIdentifier,
+                    mExtras,
+                    new GenericDocumentWrapper(mParameters));
+        }
+    }
+}
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.aidl b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.aidl
new file mode 100644
index 0000000..5194e7a
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.app.appfunctions;
+
+import android.app.appfunctions.ExecuteAppFunctionResponse;
+
+parcelable ExecuteAppFunctionResponse;
\ No newline at end of file
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
new file mode 100644
index 0000000..f6580e6
--- /dev/null
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -0,0 +1,280 @@
+/*
+ * 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.app.appfunctions;
+
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.GenericDocument;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/** The response to an app function execution. */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public final class ExecuteAppFunctionResponse implements Parcelable {
+    @NonNull
+    public static final Creator<ExecuteAppFunctionResponse> CREATOR =
+            new Creator<ExecuteAppFunctionResponse>() {
+                @Override
+                public ExecuteAppFunctionResponse createFromParcel(Parcel parcel) {
+                    GenericDocumentWrapper resultWrapper =
+                            Objects.requireNonNull(
+                                    GenericDocumentWrapper.CREATOR.createFromParcel(parcel));
+                    Bundle extras =
+                            Objects.requireNonNull(
+                                    parcel.readBundle(Bundle.class.getClassLoader()));
+                    int resultCode = parcel.readInt();
+                    String errorMessage = parcel.readString8();
+                    return new ExecuteAppFunctionResponse(
+                            resultWrapper, extras, resultCode, errorMessage);
+                }
+
+                @Override
+                public ExecuteAppFunctionResponse[] newArray(int size) {
+                    return new ExecuteAppFunctionResponse[size];
+                }
+            };
+
+    /**
+     * The name of the property that stores the function return value within the {@code
+     * resultDocument}.
+     *
+     * <p>See {@link GenericDocument#getProperty(String)} for more information.
+     *
+     * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will
+     * be empty {@link GenericDocument}.
+     *
+     * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will
+     * return {@code null}.
+     *
+     * <p>See {@link #getResultDocument} for more information on extracting the return value.
+     */
+    public static final String PROPERTY_RETURN_VALUE = "returnValue";
+
+    /** The call was successful. */
+    public static final int RESULT_OK = 0;
+
+    /** The caller does not have the permission to execute an app function. */
+    public static final int RESULT_DENIED = 1;
+
+    /** An unknown error occurred while processing the call in the AppFunctionService. */
+    public static final int RESULT_APP_UNKNOWN_ERROR = 2;
+
+    /**
+     * An internal error occurred within AppFunctionManagerService.
+     *
+     * <p>This error may be considered similar to {@link IllegalStateException}
+     */
+    public static final int RESULT_INTERNAL_ERROR = 3;
+
+    /**
+     * The caller supplied invalid arguments to the call.
+     *
+     * <p>This error may be considered similar to {@link IllegalArgumentException}.
+     */
+    public static final int RESULT_INVALID_ARGUMENT = 4;
+
+    /** The operation was timed out. */
+    public static final int RESULT_TIMED_OUT = 5;
+
+    /** The result code of the app function execution. */
+    @ResultCode private final int mResultCode;
+
+    /**
+     * The error message associated with the result, if any. This is {@code null} if the result code
+     * is {@link #RESULT_OK}.
+     */
+    @Nullable private final String mErrorMessage;
+
+    /**
+     * Returns the return value of the executed function.
+     *
+     * <p>The return value is stored in a {@link GenericDocument} with the key {@link
+     * #PROPERTY_RETURN_VALUE}.
+     *
+     * <p>See {@link #getResultDocument} for more information on extracting the return value.
+     */
+    @NonNull private final GenericDocumentWrapper mResultDocumentWrapper;
+
+    /** Returns the additional metadata data relevant to this function execution response. */
+    @NonNull private final Bundle mExtras;
+
+    private ExecuteAppFunctionResponse(
+            @NonNull GenericDocumentWrapper resultDocumentWrapper,
+            @NonNull Bundle extras,
+            @ResultCode int resultCode,
+            @Nullable String errorMessage) {
+        mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper);
+        mExtras = Objects.requireNonNull(extras);
+        mResultCode = resultCode;
+        mErrorMessage = errorMessage;
+    }
+
+    /**
+     * Returns result codes from throwable.
+     *
+     * @hide
+     */
+    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+    static @ResultCode int getResultCode(@NonNull Throwable t) {
+        if (t instanceof IllegalArgumentException) {
+            return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT;
+        }
+        return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR;
+    }
+
+    /**
+     * Returns a successful response.
+     *
+     * @param resultDocument The return value of the executed function.
+     * @param extras The additional metadata data relevant to this function execution response.
+     */
+    @NonNull
+    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+    public static ExecuteAppFunctionResponse newSuccess(
+            @NonNull GenericDocument resultDocument, @Nullable Bundle extras) {
+        Objects.requireNonNull(resultDocument);
+        Bundle actualExtras = getActualExtras(extras);
+        GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument);
+
+        return new ExecuteAppFunctionResponse(
+                resultDocumentWrapper, actualExtras, RESULT_OK, /* errorMessage= */ null);
+    }
+
+    /**
+     * Returns a failure response.
+     *
+     * @param resultCode The result code of the app function execution.
+     * @param extras The additional metadata data relevant to this function execution response.
+     * @param errorMessage The error message associated with the result, if any.
+     */
+    @NonNull
+    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+    public static ExecuteAppFunctionResponse newFailure(
+            @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) {
+        if (resultCode == RESULT_OK) {
+            throw new IllegalArgumentException("resultCode must not be RESULT_OK");
+        }
+        Bundle actualExtras = getActualExtras(extras);
+        GenericDocumentWrapper emptyWrapper =
+                new GenericDocumentWrapper(new GenericDocument.Builder<>("", "", "").build());
+        return new ExecuteAppFunctionResponse(emptyWrapper, actualExtras, resultCode, errorMessage);
+    }
+
+    private static Bundle getActualExtras(@Nullable Bundle extras) {
+        if (extras == null) {
+            return Bundle.EMPTY;
+        }
+        return extras;
+    }
+
+    /**
+     * Returns a generic document containing the return value of the executed function.
+     *
+     * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value.
+     *
+     * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed
+     * function does not produce a return value.
+     *
+     * <p>Sample code for extracting the return value:
+     *
+     * <pre>
+     *     GenericDocument resultDocument = response.getResultDocument();
+     *     Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE);
+     *     if (returnValue != null) {
+     *       // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString},
+     *       // {@link GenericDocument#getPropertyLong} etc.
+     *       // Do something with the returnValue
+     *     }
+     * </pre>
+     */
+    @NonNull
+    public GenericDocument getResultDocument() {
+        return mResultDocumentWrapper.getValue();
+    }
+
+    /** Returns the extras of the app function execution. */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Returns {@code true} if {@link #getResultCode} equals {@link
+     * ExecuteAppFunctionResponse#RESULT_OK}.
+     */
+    public boolean isSuccess() {
+        return getResultCode() == RESULT_OK;
+    }
+
+    /**
+     * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}.
+     */
+    @ResultCode
+    public int getResultCode() {
+        return mResultCode;
+    }
+
+    /**
+     * Returns the error message associated with this result.
+     *
+     * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}.
+     */
+    @Nullable
+    public String getErrorMessage() {
+        return mErrorMessage;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        mResultDocumentWrapper.writeToParcel(dest, flags);
+        dest.writeBundle(mExtras);
+        dest.writeInt(mResultCode);
+        dest.writeString8(mErrorMessage);
+    }
+
+    /**
+     * Result codes.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"RESULT_"},
+            value = {
+                RESULT_OK,
+                RESULT_DENIED,
+                RESULT_APP_UNKNOWN_ERROR,
+                RESULT_INTERNAL_ERROR,
+                RESULT_INVALID_ARGUMENT,
+                RESULT_TIMED_OUT,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ResultCode {}
+}
diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
new file mode 100644
index 0000000..84b1837
--- /dev/null
+++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java
@@ -0,0 +1,87 @@
+/*
+ * 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.app.appfunctions;
+
+import android.app.appsearch.GenericDocument;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * The Parcelable object contains a {@link GenericDocument} to allow the parcelization of it
+ * exceeding the binder limit.
+ *
+ * <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder
+ * directly or Android shared memory if the data is large.
+ *
+ * @hide
+ * @see Parcel#writeBlob(byte[])
+ */
+public final class GenericDocumentWrapper implements Parcelable {
+    public static final Creator<GenericDocumentWrapper> CREATOR =
+            new Creator<>() {
+                @Override
+                public GenericDocumentWrapper createFromParcel(Parcel in) {
+                    byte[] dataBlob = Objects.requireNonNull(in.readBlob());
+                    Parcel unmarshallParcel = Parcel.obtain();
+                    try {
+                        unmarshallParcel.unmarshall(dataBlob, 0, dataBlob.length);
+                        unmarshallParcel.setDataPosition(0);
+                        return new GenericDocumentWrapper(
+                                GenericDocument.createFromParcel(unmarshallParcel));
+                    } finally {
+                        unmarshallParcel.recycle();
+                    }
+                }
+
+                @Override
+                public GenericDocumentWrapper[] newArray(int size) {
+                    return new GenericDocumentWrapper[size];
+                }
+            };
+    @NonNull private final GenericDocument mGenericDocument;
+
+    public GenericDocumentWrapper(@NonNull GenericDocument genericDocument) {
+        mGenericDocument = Objects.requireNonNull(genericDocument);
+    }
+
+    /** Returns the wrapped {@link android.app.appsearch.GenericDocument} */
+    @NonNull
+    public GenericDocument getValue() {
+        return mGenericDocument;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        Parcel parcel = Parcel.obtain();
+        try {
+            mGenericDocument.writeToParcel(parcel, flags);
+            byte[] bytes = parcel.marshall();
+            dest.writeBlob(bytes);
+        } finally {
+            parcel.recycle();
+        }
+    }
+}
diff --git a/core/java/android/app/appfunctions/IAppFunctionManager.aidl b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
new file mode 100644
index 0000000..28827bb
--- /dev/null
+++ b/core/java/android/app/appfunctions/IAppFunctionManager.aidl
@@ -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 android.app.appfunctions;
+
+import android.app.appfunctions.ExecuteAppFunctionAidlRequest;
+import android.app.appfunctions.IExecuteAppFunctionCallback;
+
+/**
+ * Defines the interface for apps to interact with the app function execution service
+ * {@code AppFunctionManagerService} running in the system server process.
+ * @hide
+ */
+interface IAppFunctionManager {
+    /**
+    * Executes an app function provided by {@link AppFunctionService} through the system.
+    *
+    * @param request the request to execute an app function.
+    * @param callback the callback to report the result.
+    */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional = true)")
+    void executeAppFunction(
+        in ExecuteAppFunctionAidlRequest request,
+        in IExecuteAppFunctionCallback callback
+    );
+}
\ No newline at end of file
diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl
new file mode 100644
index 0000000..cc5a20c
--- /dev/null
+++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl
@@ -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 android.app.appfunctions;
+
+import android.os.Bundle;
+import android.app.appfunctions.IExecuteAppFunctionCallback;
+import android.app.appfunctions.ExecuteAppFunctionRequest;
+
+
+/**
+ * Defines the interface for the system server to request the execution of an app function within
+ * the app process.
+ *
+ * This interface is implemented by the app and exposed to the system server via a {@code Service}.
+ *
+ * @hide
+ */
+oneway interface IAppFunctionService {
+    /**
+     * Called by the system to execute a specific app function.
+     *
+     * @param request  the function execution request.
+     * @param callback a callback to report back the result.
+     */
+    void executeAppFunction(
+        in ExecuteAppFunctionRequest request,
+        in IExecuteAppFunctionCallback callback
+    );
+}
diff --git a/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl
new file mode 100644
index 0000000..5323f9b
--- /dev/null
+++ b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright 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 android.app.appfunctions;
+
+import android.app.appfunctions.ExecuteAppFunctionResponse;
+
+/** {@hide} */
+oneway interface IExecuteAppFunctionCallback {
+    void onResult(in ExecuteAppFunctionResponse result);
+}
diff --git a/core/java/android/app/appfunctions/OWNERS b/core/java/android/app/appfunctions/OWNERS
new file mode 100644
index 0000000..c6827cc
--- /dev/null
+++ b/core/java/android/app/appfunctions/OWNERS
@@ -0,0 +1,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
new file mode 100644
index 0000000..86fc369
--- /dev/null
+++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java
@@ -0,0 +1,75 @@
+/*
+ * 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.app.appfunctions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
+/**
+ * A wrapper of IExecuteAppFunctionCallback which swallows the {@link RemoteException}. This
+ * callback is intended for one-time use only. Subsequent calls to onResult() will be ignored.
+ *
+ * @hide
+ */
+public class SafeOneTimeExecuteAppFunctionCallback {
+    private static final String TAG = "SafeOneTimeExecuteApp";
+
+    private final AtomicBoolean mOnResultCalled = new AtomicBoolean(false);
+
+    @NonNull private final IExecuteAppFunctionCallback mCallback;
+
+    @Nullable private final Consumer<ExecuteAppFunctionResponse> mOnDispatchCallback;
+
+    public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) {
+        this(callback, /* onDispatchCallback= */ null);
+    }
+
+    /**
+     * @param callback The callback to wrap.
+     * @param onDispatchCallback An optional callback invoked after the wrapped callback has been
+     *     dispatched with a result. This callback receives the result that has been dispatched.
+     */
+    public SafeOneTimeExecuteAppFunctionCallback(
+            @NonNull IExecuteAppFunctionCallback callback,
+            @Nullable Consumer<ExecuteAppFunctionResponse> onDispatchCallback) {
+        mCallback = Objects.requireNonNull(callback);
+        mOnDispatchCallback = onDispatchCallback;
+    }
+
+    /** Invoke wrapped callback with the result. */
+    public void onResult(@NonNull ExecuteAppFunctionResponse result) {
+        if (!mOnResultCalled.compareAndSet(false, true)) {
+            Log.w(TAG, "Ignore subsequent calls to onResult()");
+            return;
+        }
+        try {
+            mCallback.onResult(result);
+        } catch (RemoteException ex) {
+            // Failed to notify the other end. Ignore.
+            Log.w(TAG, "Failed to invoke the callback", ex);
+        }
+        if (mOnDispatchCallback != null) {
+            mOnDispatchCallback.accept(result);
+        }
+    }
+}
diff --git a/core/java/android/app/appfunctions/flags/flags.aconfig b/core/java/android/app/appfunctions/flags/flags.aconfig
new file mode 100644
index 0000000..367effc
--- /dev/null
+++ b/core/java/android/app/appfunctions/flags/flags.aconfig
@@ -0,0 +1,11 @@
+package: "android.app.appfunctions.flags"
+container: "system"
+
+flag {
+    name: "enable_app_function_manager"
+    is_exported: true
+    is_fixed_read_only: true
+    namespace: "machine_learning"
+    description: "This flag the new App Function manager system service."
+    bug: "357551503"
+}
\ No newline at end of file
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index f751a23..9891e89 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -21,6 +21,23 @@
 }
 
 flag {
+  name: "modes_ui_icons"
+  namespace: "systemui"
+  description: "Shows current Priority Mode icon in lockscreen, status bar, and QS; dependent on flags modes_api and modes_ui"
+  bug: "360399800"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "modes_ui_test"
+  namespace: "systemui"
+  description: "Guards new CTS tests for Modes; dependent on flags modes_api and modes_ui"
+  bug: "360862012"
+}
+
+flag {
   name: "api_tvextender"
   is_exported: true
   namespace: "systemui"
@@ -111,6 +128,16 @@
 }
 
 flag {
+  name: "notif_channel_crop_vibration_effects"
+  namespace: "systemui"
+  description: "Limits the size of vibration effects that can be stored in a NotificationChannel"
+  bug: "345881518"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "evenly_divided_call_style_action_layout"
   namespace: "systemui"
   description: "Evenly divides horizontal space for action buttons in CallStyle notifications."
diff --git a/core/java/android/app/supervision/ISupervisionManager.aidl b/core/java/android/app/supervision/ISupervisionManager.aidl
new file mode 100644
index 0000000..8d25cad
--- /dev/null
+++ b/core/java/android/app/supervision/ISupervisionManager.aidl
@@ -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.
+ */
+
+package android.app.supervision;
+
+/**
+ * Internal IPC interface to the supervision service.
+ * {@hide}
+ */
+interface ISupervisionManager {
+    boolean isSupervisionEnabled();
+}
diff --git a/core/java/android/app/supervision/OWNERS b/core/java/android/app/supervision/OWNERS
new file mode 100644
index 0000000..afc5495
--- /dev/null
+++ b/core/java/android/app/supervision/OWNERS
@@ -0,0 +1,2 @@
[email protected]
[email protected]
diff --git a/core/java/android/app/supervision/SupervisionManager.java b/core/java/android/app/supervision/SupervisionManager.java
new file mode 100644
index 0000000..8611a92
--- /dev/null
+++ b/core/java/android/app/supervision/SupervisionManager.java
@@ -0,0 +1,57 @@
+/*
+ * 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.app.supervision;
+
+import android.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * Service for handling parental supervision.
+ *
+ * @hide
+ */
+@SystemService(Context.SUPERVISION_SERVICE)
+public class SupervisionManager {
+    private final Context mContext;
+    private final ISupervisionManager mService;
+
+    /**
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public SupervisionManager(Context context, ISupervisionManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Returns whether the device is supervised.
+     *
+     * @hide
+     */
+    public boolean isSupervisionEnabled() {
+        try {
+            return mService.isSupervisionEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+}
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
new file mode 100644
index 0000000..bcb5b36
--- /dev/null
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.app.supervision.flags"
+container: "system"
+
+flag {
+  name: "supervision_api"
+  is_exported: true
+  namespace: "supervision"
+  description: "Flag to enable the SupervisionService"
+  bug: "340351729"
+}
\ No newline at end of file
diff --git a/core/java/android/app/usage/OWNERS b/core/java/android/app/usage/OWNERS
index 57d958f..745e724 100644
--- a/core/java/android/app/usage/OWNERS
+++ b/core/java/android/app/usage/OWNERS
@@ -1,9 +1 @@
-# Bug component: 532296
-
[email protected]
[email protected]
[email protected]
[email protected]
-
-per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
-per-file *Broadcast* = [email protected]
+include /services/usage/OWNERS
\ No newline at end of file
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 4e0379e..7117f25 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -57,3 +57,10 @@
   description: "Enable support for persisting RemoteViews previews to Protobuf"
   bug: "306546610"
 }
+
+flag {
+  name: "remote_document_support"
+  namespace: "app_widgets"
+  description: "Remote document support features in Q2 2025 release"
+  bug: "339721781"
+}
\ No newline at end of file
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 1529842..1cdf3b1 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -21,7 +21,6 @@
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER;
 import static android.Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH;
 
-import static java.util.Collections.unmodifiableMap;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
@@ -58,7 +57,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
-import android.util.ArrayMap;
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.SparseArray;
@@ -78,7 +76,6 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.BiConsumer;
@@ -146,12 +143,19 @@
     /**
      * The result code to propagate back to the user activity, indicates the internal error
      * in CompanionDeviceManager.
-     * E.g. Missing necessary permissions or duplicate {@link AssociationRequest}s when create the
-     * {@link AssociationInfo}.
      */
     public static final int RESULT_INTERNAL_ERROR = 3;
 
     /**
+     * The result code to propagate back to the user activity and
+     * {@link Callback#onFailure(int, CharSequence)}, indicates app is not allow to create the
+     * association due to the security issue.
+     * E.g. There are missing necessary permissions when creating association.
+     */
+    @FlaggedApi(Flags.FLAG_ASSOCIATION_FAILURE_CODE)
+    public static final int RESULT_SECURITY_ERROR = 4;
+
+    /**
      * Requesting applications will receive the String in {@link Callback#onFailure} if the
      * association dialog is explicitly declined by the users. E.g. press the Don't allow
      * button.
@@ -374,7 +378,6 @@
          */
         public void onAssociationCreated(@NonNull AssociationInfo associationInfo) {}
 
-        //TODO(b/331459560): Add deprecated and remove abstract after API cut for W.
         /**
          * Invoked if the association could not be created.
          *
@@ -385,11 +388,15 @@
         /**
          * Invoked if the association could not be created.
          *
-         * @param resultCode indicate the particular reason why the association
-         *                   could not be created.
+         * Please note that both {@link #onFailure(CharSequence error)} and this
+         * API will be called if the association could not be created.
+         *
+         * @param errorCode indicate the particular error code why the association
+         *                  could not be created.
+         * @param error error message.
          */
         @FlaggedApi(Flags.FLAG_ASSOCIATION_FAILURE_CODE)
-        public void onFailure(@ResultCode int resultCode) {}
+        public void onFailure(@ResultCode int errorCode, @Nullable CharSequence error) {}
     }
 
     private final ICompanionDeviceManager mService;
@@ -1825,12 +1832,12 @@
         }
 
         @Override
-        public void onFailure(@ResultCode int resultCode) {
+        public void onFailure(@ResultCode int errorCode, @Nullable CharSequence error) {
             if (Flags.associationFailureCode()) {
-                execute(mCallback::onFailure, resultCode);
+                execute(mCallback::onFailure, errorCode, error);
             }
 
-            execute(mCallback::onFailure, RESULT_CODE_TO_REASON.get(resultCode));
+            execute(mCallback::onFailure, error);
         }
 
         private <T> void execute(Consumer<T> callback, T arg) {
@@ -1840,6 +1847,12 @@
                 mHandler.post(() -> callback.accept(arg));
             }
         }
+
+        private <T, U> void execute(BiConsumer<T, U> callback, T arg1, U arg2) {
+            if (mExecutor != null) {
+                mExecutor.execute(() -> callback.accept(arg1, arg2));
+            }
+        }
     }
 
     private static class OnAssociationsChangedListenerProxy
@@ -2014,15 +2027,4 @@
             }
         }
     }
-
-    private static final Map<Integer, String> RESULT_CODE_TO_REASON;
-    static {
-        final Map<Integer, String> map = new ArrayMap<>();
-        map.put(RESULT_CANCELED, REASON_CANCELED);
-        map.put(RESULT_USER_REJECTED, REASON_USER_REJECTED);
-        map.put(RESULT_DISCOVERY_TIMEOUT, REASON_DISCOVERY_TIMEOUT);
-        map.put(RESULT_INTERNAL_ERROR, REASON_INTERNAL_ERROR);
-
-        RESULT_CODE_TO_REASON = unmodifiableMap(map);
-    }
 }
diff --git a/core/java/android/companion/IAssociationRequestCallback.aidl b/core/java/android/companion/IAssociationRequestCallback.aidl
index b1be30a..a6f86a5 100644
--- a/core/java/android/companion/IAssociationRequestCallback.aidl
+++ b/core/java/android/companion/IAssociationRequestCallback.aidl
@@ -25,5 +25,5 @@
 
     oneway void onAssociationCreated(in AssociationInfo associationInfo);
 
-    oneway void onFailure(in int resultCode);
+    oneway void onFailure(in int errorCode, in CharSequence error);
 }
\ No newline at end of file
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index ee9114f..93d62cf 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -10,13 +10,6 @@
 }
 
 flag {
-    name: "companion_transport_apis"
-    namespace: "companion"
-    description: "Grants access to the companion transport apis."
-    bug: "288297505"
-}
-
-flag {
     name: "association_tag"
     is_exported: true
     namespace: "companion"
diff --git a/core/java/android/companion/virtual/ActivityPolicyExemption.aidl b/core/java/android/companion/virtual/ActivityPolicyExemption.aidl
new file mode 100644
index 0000000..2f89da3
--- /dev/null
+++ b/core/java/android/companion/virtual/ActivityPolicyExemption.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.companion.virtual;
+
+parcelable ActivityPolicyExemption;
diff --git a/core/java/android/companion/virtual/ActivityPolicyExemption.java b/core/java/android/companion/virtual/ActivityPolicyExemption.java
new file mode 100644
index 0000000..dc285d4
--- /dev/null
+++ b/core/java/android/companion/virtual/ActivityPolicyExemption.java
@@ -0,0 +1,197 @@
+/*
+ * 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.companion.virtual;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.companion.virtualdevice.flags.Flags;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.Display;
+
+import java.util.Objects;
+
+/**
+ * Specifies an exemption from the current default activity launch policy of a virtual device.
+ *
+ * <p>Note that changing the virtual device's activity launch policy will clear all current
+ * exemptions.</p>
+ *
+ * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
+ * @see VirtualDeviceManager.VirtualDevice#setDevicePolicy
+ * @see VirtualDeviceManager.VirtualDevice#addActivityPolicyExemption(ActivityPolicyExemption)
+ * @see VirtualDeviceManager.VirtualDevice#removeActivityPolicyExemption(ActivityPolicyExemption)
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ACTIVITY_CONTROL_API)
+@SystemApi
+public final class ActivityPolicyExemption implements Parcelable {
+
+    private final @Nullable ComponentName mComponentName;
+    private final @Nullable String mPackageName;
+    private final int mDisplayId;
+
+    private ActivityPolicyExemption(@Nullable ComponentName componentName,
+            @Nullable String packageName, int displayId) {
+        mComponentName = componentName;
+        mPackageName = packageName;
+        mDisplayId = displayId;
+    }
+
+    private ActivityPolicyExemption(@NonNull Parcel parcel) {
+        mComponentName = parcel.readTypedObject(ComponentName.CREATOR);
+        mPackageName = parcel.readString8();
+        mDisplayId = parcel.readInt();
+    }
+
+    /**
+     * Returns the exempt component name if this is a component level exemption, {@code null}
+     * otherwise.
+     *
+     * @see Builder#setComponentName(ComponentName)
+     */
+    public @Nullable ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Returns the exempt package name if this is a package level exemption, {@code null} otherwise.
+     *
+     * @see Builder#setPackageName(String)
+     */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the display ID relevant for this exemption if it is specific to a single display,
+     * {@link Display#INVALID_DISPLAY} otherwise.
+     *
+     * @see Builder#setDisplayId(int)
+     */
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mComponentName, flags);
+        dest.writeString8(mPackageName);
+        dest.writeInt(mDisplayId);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ActivityPolicyExemption> CREATOR =
+            new Parcelable.Creator<>() {
+                public ActivityPolicyExemption createFromParcel(Parcel in) {
+                    return new ActivityPolicyExemption(in);
+                }
+
+                public ActivityPolicyExemption[] newArray(int size) {
+                    return new ActivityPolicyExemption[size];
+                }
+            };
+
+    /**
+     * Builder for {@link ActivityPolicyExemption}.
+     */
+    @FlaggedApi(Flags.FLAG_ACTIVITY_CONTROL_API)
+    public static final class Builder {
+
+        private @Nullable ComponentName mComponentName;
+        private @Nullable String mPackageName;
+        private int mDisplayId = Display.INVALID_DISPLAY;
+
+        /**
+         * Specifies a component level exemption from the current default activity launch policy.
+         *
+         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
+         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
+         * then the specified component will be blocked from launching.
+         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
+         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the
+         * specified component will be allowed to launch.</p>
+         *
+         * <p>Setting a component name will clear any previously set package name.</p>
+         */
+        public @NonNull Builder setComponentName(@NonNull ComponentName componentName) {
+            mComponentName = Objects.requireNonNull(componentName);
+            mPackageName = null;
+            return this;
+        }
+
+        /**
+         * Specifies a package level exemption from the current default activity launch policy.
+         *
+         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
+         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
+         * then all activities from the specified package will be blocked from launching.
+         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
+         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then all
+         * activities from the specified package will be allowed to launch.</p>
+         *
+         * <p>Package level exemptions are independent of component level exemptions created via
+         * {@link #setComponentName(ComponentName)}, i.e. removing a package exemption will not
+         * remove any existing component exemptions, even if the component belongs to that package.
+         * </p>
+         *
+         * <p>Setting a package name will clear any previously set component name.</p>
+         */
+        public @NonNull Builder setPackageName(@NonNull String packageName) {
+            mComponentName = null;
+            mPackageName = Objects.requireNonNull(packageName);
+            return this;
+        }
+
+        /**
+         * Makes this exemption specific to the display with the given ID. If unset, or set to
+         * {@link Display#INVALID_DISPLAY}, then the exemption is applied to all displays that
+         * belong to the virtual device.
+         *
+         * @param displayId  the ID of the display, for which to apply the exemption. The display
+         *   must belong to the virtual device.
+         */
+        public @NonNull Builder setDisplayId(int displayId) {
+            mDisplayId = displayId;
+            return this;
+        }
+
+        /**
+         * Builds the {@link ActivityPolicyExemption} instance.
+         *
+         * @throws IllegalArgumentException if neither the component name nor the package name are
+         *   set.
+         */
+        @NonNull
+        public ActivityPolicyExemption build() {
+            if ((mComponentName == null) == (mPackageName == null)) {
+                throw new IllegalArgumentException(
+                        "Either component name or package name must be set");
+            }
+            return new ActivityPolicyExemption(mComponentName, mPackageName, mDisplayId);
+        }
+    }
+}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index a56bc02..8916ce2 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -17,6 +17,7 @@
 package android.companion.virtual;
 
 import android.app.PendingIntent;
+import android.companion.virtual.ActivityPolicyExemption;
 import android.companion.virtual.IVirtualDeviceActivityListener;
 import android.companion.virtual.IVirtualDeviceIntentInterceptor;
 import android.companion.virtual.IVirtualDeviceSoundEffectListener;
@@ -103,13 +104,13 @@
      * Adds an exemption to the default activity launch policy.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void addActivityPolicyExemption(in ComponentName exemption);
+    void addActivityPolicyExemption(in ActivityPolicyExemption exemption);
 
     /**
      * Removes an exemption to the default activity launch policy.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void removeActivityPolicyExemption(in ComponentName exemption);
+    void removeActivityPolicyExemption(in ActivityPolicyExemption exemption);
 
     /**
      * Specifies a policy for this virtual device on the given display.
@@ -118,18 +119,6 @@
     void setDevicePolicyForDisplay(int displayId, int policyType, int devicePolicy);
 
     /**
-     * Adds an exemption to the default activity launch policy on the given display.
-     */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void addActivityPolicyExemptionForDisplay(int displayId, in ComponentName exemption);
-
-    /**
-     * Removes an exemption to the default activity launch policy on the given display.
-     */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void removeActivityPolicyExemptionForDisplay(int displayId, in ComponentName exemption);
-
-    /**
      * Notifies that an audio session being started.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 564fb02..767f52a 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.IntentSender;
+import android.os.UserHandle;
 
 /**
  * Interface to listen for activity changes in a virtual device.
@@ -48,9 +49,18 @@
      *
      * @param displayId The display ID on which the activity tried to launch.
      * @param componentName The component name of the blocked activity.
-     * @param userId The user ID associated with the blocked activity.
+     * @param user The user associated with the blocked activity.
      * @param intentSender The original sender of the intent.
      */
-    void onActivityLaunchBlocked(int displayId, in ComponentName componentName, int userId,
+    void onActivityLaunchBlocked(int displayId, in ComponentName componentName, in UserHandle user,
             in IntentSender intentSender);
+
+    /**
+     * Called when a secure surface is shown on the device.
+     *
+     * @param displayId The display ID on which the secure surface was shown.
+     * @param componentName The component name of the activity that showed the secure surface.
+     * @param user The user associated with the activity.
+     */
+    void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
 }
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index 19eb497..de20a68 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -19,7 +19,7 @@
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
-import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 
@@ -65,6 +65,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.view.WindowManager;
 
@@ -136,14 +137,30 @@
 
                 @Override
                 public void onActivityLaunchBlocked(int displayId, ComponentName componentName,
-                        @UserIdInt int userId, IntentSender intentSender) {
+                        UserHandle user, IntentSender intentSender) {
                     final long token = Binder.clearCallingIdentity();
                     try {
                         synchronized (mActivityListenersLock) {
                             for (int i = 0; i < mActivityListeners.size(); i++) {
                                 mActivityListeners.valueAt(i)
                                         .onActivityLaunchBlocked(
-                                                displayId, componentName, userId, intentSender);
+                                                displayId, componentName, user, intentSender);
+                            }
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+
+                @Override
+                public void onSecureWindowShown(int displayId, ComponentName componentName,
+                        UserHandle user) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        synchronized (mActivityListenersLock) {
+                            for (int i = 0; i < mActivityListeners.size(); i++) {
+                                mActivityListeners.valueAt(i)
+                                        .onSecureWindowShown(displayId, componentName, user);
                             }
                         }
                     } finally {
@@ -151,6 +168,7 @@
                     }
                 }
             };
+
     private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
             new IVirtualDeviceSoundEffectListener.Stub() {
                 @Override
@@ -292,7 +310,7 @@
             case POLICY_TYPE_RECENTS:
             case POLICY_TYPE_CLIPBOARD:
             case POLICY_TYPE_ACTIVITY:
-            case POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR:
+            case POLICY_TYPE_BLOCKED_ACTIVITY:
                 break;
             default:
                 throw new IllegalArgumentException("Device policy " + policyType
@@ -305,17 +323,17 @@
         }
     }
 
-    void addActivityPolicyExemption(@NonNull ComponentName componentName) {
+    void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
-            mVirtualDevice.addActivityPolicyExemption(componentName);
+            mVirtualDevice.addActivityPolicyExemption(exemption);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
+    void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         try {
-            mVirtualDevice.removeActivityPolicyExemption(componentName);
+            mVirtualDevice.removeActivityPolicyExemption(exemption);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -340,23 +358,6 @@
         }
     }
 
-    void addActivityPolicyExemptionForDisplay(int displayId, @NonNull ComponentName componentName) {
-        try {
-            mVirtualDevice.addActivityPolicyExemptionForDisplay(displayId, componentName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    void removeActivityPolicyExemptionForDisplay(int displayId,
-            @NonNull ComponentName componentName) {
-        try {
-            mVirtualDevice.removeActivityPolicyExemptionForDisplay(displayId, componentName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     @NonNull
     VirtualDpad createVirtualDpad(@NonNull VirtualDpadConfig config) {
         try {
@@ -595,10 +596,16 @@
         }
 
         public void onActivityLaunchBlocked(int displayId, ComponentName componentName,
-                @UserIdInt int userId, IntentSender intentSender) {
+                UserHandle user, IntentSender intentSender) {
             mExecutor.execute(() ->
                     mActivityListener.onActivityLaunchBlocked(
-                            displayId, componentName, userId, intentSender));
+                            displayId, componentName, user, intentSender));
+        }
+
+        public void onSecureWindowShown(int displayId, ComponentName componentName,
+                UserHandle user) {
+            mExecutor.execute(() ->
+                    mActivityListener.onSecureWindowShown(displayId, componentName, user));
         }
     }
 
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 8b60580..473ab27 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -67,6 +67,7 @@
 import android.os.Binder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.Display;
@@ -531,7 +532,6 @@
      *
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR)
     @TestApi
     public boolean isVirtualDeviceOwnedMirrorDisplay(int displayId) {
         if (mService == null) {
@@ -765,14 +765,15 @@
          * <p>Note that changing the activity launch policy will clear current set of exempt
          * components.</p>
          *
-         * @see #removeActivityPolicyExemption
+         * @see #removeActivityPolicyExemption(ComponentName)
          * @see #setDevicePolicy
          */
         @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
-            mVirtualDeviceInternal.addActivityPolicyExemption(
-                    Objects.requireNonNull(componentName));
+            addActivityPolicyExemption(new ActivityPolicyExemption.Builder()
+                    .setComponentName(componentName)
+                    .build());
         }
 
         /**
@@ -788,14 +789,54 @@
          * <p>Note that changing the activity launch policy will clear current set of exempt
          * components.</p>
          *
-         * @see #addActivityPolicyExemption
+         * @see #addActivityPolicyExemption(ComponentName)
          * @see #setDevicePolicy
          */
         @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
-            mVirtualDeviceInternal.removeActivityPolicyExemption(
-                    Objects.requireNonNull(componentName));
+            removeActivityPolicyExemption(new ActivityPolicyExemption.Builder()
+                    .setComponentName(componentName)
+                    .build());
+        }
+
+        /**
+         * Specifies an exemption from the current activity launch policy.
+         *
+         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
+         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
+         * then all exempt activities be blocked from launching.
+         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
+         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then all
+         * exempt activities will be allowed to launch.</p>
+         *
+         * <p>Note that changing the activity launch policy will clear current set of exempt
+         * packages.</p>
+         * <p>Any change to the exemptions will only be applied for new activity launches.</p>
+         *
+         * @see #removeActivityPolicyExemption(ActivityPolicyExemption)
+         * @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));
+        }
+
+        /**
+         * Removes an exemption from the current activity launch policy.
+         *
+         * <p>Note that changing the activity launch policy will clear current set of exempt
+         * packages.</p>
+         * <p>Any change to the exemptions will only be applied for new activity launches.</p>
+         *
+         * @see #addActivityPolicyExemption(ActivityPolicyExemption)
+         * @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));
         }
 
         /**
@@ -825,69 +866,6 @@
         }
 
         /**
-         * Specifies a component name to be exempt from the given display's activity launch policy.
-         *
-         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
-         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
-         * then the specified component will be blocked from launching.
-         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
-         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the
-         * specified component will be allowed to launch.</p>
-         *
-         * <p>Note that changing the activity launch policy will clear current set of exempt
-         * components.</p>
-         * <p>Any change to the exemptions will only be applied for new activity launches.</p>
-         *
-         * @param componentName the component name to be exempt from the activity launch policy.
-         * @param displayId the ID of the display, for which to apply the exemption. The display
-         *   must belong to the virtual device.
-         * @throws IllegalArgumentException if the specified display does not belong to the virtual
-         *   device.
-         *
-         * @see #removeActivityPolicyExemption
-         * @see #setDevicePolicy
-         * @see Display#getDisplayId
-         */
-        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-        public void addActivityPolicyExemption(
-                @NonNull ComponentName componentName, int displayId) {
-            mVirtualDeviceInternal.addActivityPolicyExemptionForDisplay(
-                    displayId, Objects.requireNonNull(componentName));
-        }
-
-        /**
-         * Makes the specified component name adhere to the given display's activity launch policy.
-         *
-         * <p>If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} allows activity
-         * launches by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_DEFAULT}),
-         * then the specified component will be allowed to launch.
-         * If the current {@link VirtualDeviceParams#POLICY_TYPE_ACTIVITY} blocks activity launches
-         * by default, (i.e. it is {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM}), then the
-         * specified component will be blocked from launching.</p>
-         *
-         * <p>Note that changing the activity launch policy will clear current set of exempt
-         * components.</p>
-         *
-         * @param componentName the component name to be removed from the exemption list.
-         * @param displayId the ID of the display, for which to apply the exemption. The display
-         *   must belong to the virtual device.
-         * @throws IllegalArgumentException if the specified display does not belong to the virtual
-         *   device.
-         *
-         * @see #addActivityPolicyExemption
-         * @see #setDevicePolicy
-         * @see Display#getDisplayId
-         */
-        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-        public void removeActivityPolicyExemption(
-                @NonNull ComponentName componentName, int displayId) {
-            mVirtualDeviceInternal.removeActivityPolicyExemptionForDisplay(
-                    displayId, Objects.requireNonNull(componentName));
-        }
-
-        /**
          * Creates a virtual dpad.
          *
          * @param config the configurations of the virtual dpad.
@@ -1264,7 +1242,7 @@
          *
          * @param displayId The display ID on which the activity tried to launch.
          * @param componentName The component name of the blocked activity.
-         * @param userId The user ID associated with the blocked activity.
+         * @param user The user associated with the blocked activity.
          * @param intentSender The original sender of the intent. May be {@code null} if the sender
          *   expects an activity result to be reported. In that case
          *   {@link android.app.Activity#RESULT_CANCELED} was already reported back because the
@@ -1272,11 +1250,27 @@
          *   activity to a different display.
          *
          * @see VirtualDeviceParams#POLICY_TYPE_ACTIVITY
-         * @see VirtualDevice#addActivityPolicyExemption(ComponentName)
+         * @see VirtualDevice#addActivityPolicyExemption(ActivityPolicyExemption)
          */
         @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
         default void onActivityLaunchBlocked(int displayId, @NonNull ComponentName componentName,
-                @UserIdInt int userId, @Nullable IntentSender intentSender) {}
+                @NonNull UserHandle user, @Nullable IntentSender intentSender) {}
+
+        /**
+         * Called when a window with a secure surface is shown on the device.
+         *
+         * <p>Note that this is called only when the window is associated with an activity.</p>
+         *
+         * @param displayId The display ID on which the window was shown.
+         * @param componentName The component name of the activity that showed the window.
+         * @param user The user associated with the activity.
+         *
+         * @see Display#FLAG_SECURE
+         * @see WindowManager.LayoutParams#FLAG_SECURE
+         */
+        @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+        default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
+                @NonNull UserHandle user) {}
     }
 
     /**
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index c1fc51d..03b72bd 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -160,7 +160,7 @@
      */
     @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_SENSORS, POLICY_TYPE_AUDIO,
             POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY, POLICY_TYPE_CAMERA,
-            POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR})
+            POLICY_TYPE_BLOCKED_ACTIVITY})
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
     public @interface PolicyType {}
@@ -172,7 +172,7 @@
      * @hide
      */
     @IntDef(prefix = "POLICY_TYPE_", value = {POLICY_TYPE_RECENTS, POLICY_TYPE_ACTIVITY,
-            POLICY_TYPE_CLIPBOARD, POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR})
+            POLICY_TYPE_CLIPBOARD, POLICY_TYPE_BLOCKED_ACTIVITY})
     @Retention(RetentionPolicy.SOURCE)
     @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
     public @interface DynamicPolicyType {}
@@ -242,7 +242,7 @@
      * @see VirtualDeviceManager.VirtualDevice#removeActivityPolicyExemption
      */
     // TODO(b/333443509): Update the documentation of custom policy and link to the new policy
-    // POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR
+    // POLICY_TYPE_BLOCKED_ACTIVITY
     @FlaggedApi(Flags.FLAG_DYNAMIC_POLICY)
     public static final int POLICY_TYPE_ACTIVITY = 3;
 
@@ -292,7 +292,7 @@
      */
     // TODO(b/333443509): Link to POLICY_TYPE_ACTIVITY
     @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
-    public static final int POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR = 6;
+    public static final int POLICY_TYPE_BLOCKED_ACTIVITY = 6;
 
     private final int mLockState;
     @NonNull private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
@@ -1206,7 +1206,7 @@
             }
 
             if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
-                mDevicePolicies.delete(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR);
+                mDevicePolicies.delete(POLICY_TYPE_BLOCKED_ACTIVITY);
             }
 
             if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 91586b6..fc9c94dd 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -35,13 +35,6 @@
 }
 
 flag {
-  name: "consistent_display_flags"
-  namespace: "virtual_devices"
-  description: "Make virtual display flags consistent with display manager"
-  bug: "300905478"
-}
-
-flag {
   name: "vdm_custom_ime"
   is_exported: true
   namespace: "virtual_devices"
@@ -89,14 +82,6 @@
 }
 
 flag {
-  name: "interactive_screen_mirror"
-  is_exported: true
-  namespace: "virtual_devices"
-  description: "Enable interactive screen mirroring using Virtual Devices"
-  bug: "292212199"
-}
-
-flag {
   name: "virtual_stylus"
   is_exported: true
   namespace: "virtual_devices"
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index c3c3f0e..297fe8a 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -50,6 +50,7 @@
      name: "activity_control_api"
      description: "Enable APIs for fine grained activity policy, fallback and callbacks"
      bug: "333443509"
+     is_exported: true
 }
 
 flag {
@@ -103,3 +104,17 @@
   description: "Expose multiple surface for the virtual camera owner for different stream resolution"
   bug: "341083465"
 }
+
+flag {
+    namespace: "virtual_devices"
+    name: "device_aware_display_power"
+    description: "Device awareness in power and display APIs"
+    bug: "285020111"
+}
+
+flag {
+  name: "status_bar_and_insets"
+  namespace: "virtual_devices"
+  description: "Allow for status bar and insets on virtual devices"
+  bug: "350007866"
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index 21ad914..68bc9bc 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -17,17 +17,27 @@
 package android.companion.virtual.sensor;
 
 
+import static android.hardware.Sensor.REPORTING_MODE_CONTINUOUS;
+import static android.hardware.Sensor.REPORTING_MODE_ONE_SHOT;
+import static android.hardware.Sensor.REPORTING_MODE_ON_CHANGE;
+import static android.hardware.Sensor.REPORTING_MODE_SPECIAL_TRIGGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.companion.virtualdevice.flags.Flags;
 import android.hardware.Sensor;
 import android.hardware.SensorDirectChannel;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 
@@ -42,6 +52,13 @@
 public final class VirtualSensorConfig implements Parcelable {
     private static final String TAG = "VirtualSensorConfig";
 
+    // Defined in sensors.h
+    private static final int FLAG_WAKE_UP_SENSOR = 1;
+
+    // Mask for the reporting mode, bit 2, 3, 4.
+    private static final int REPORTING_MODE_MASK = 0xE;
+    private static final int REPORTING_MODE_SHIFT = 1;
+
     // Mask for direct mode highest rate level, bit 7, 8, 9.
     private static final int DIRECT_REPORT_MASK = 0x380;
     private static final int DIRECT_REPORT_SHIFT = 7;
@@ -62,6 +79,17 @@
 
     private final int mFlags;
 
+    /** @hide */
+    @IntDef(prefix = "REPORTING_MODE_", value = {
+            REPORTING_MODE_CONTINUOUS,
+            REPORTING_MODE_ON_CHANGE,
+            REPORTING_MODE_ONE_SHOT,
+            REPORTING_MODE_SPECIAL_TRIGGER
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ReportingMode {}
+
     private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor,
             float maximumRange, float resolution, float power, int minDelay, int maxDelay,
             int flags) {
@@ -193,8 +221,7 @@
     @SensorDirectChannel.RateLevel
     public int getHighestDirectReportRateLevel() {
         int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT);
-        return rateLevel <= SensorDirectChannel.RATE_VERY_FAST
-                ? rateLevel : SensorDirectChannel.RATE_VERY_FAST;
+        return Math.min(rateLevel, SensorDirectChannel.RATE_VERY_FAST);
     }
 
     /**
@@ -215,6 +242,28 @@
     }
 
     /**
+     * Returns whether the sensor is a wake-up sensor.
+     *
+     * @see Builder#setWakeUpSensor(boolean)
+     * @see Sensor#isWakeUpSensor()
+     */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+    public boolean isWakeUpSensor() {
+        return (mFlags & FLAG_WAKE_UP_SENSOR) > 0;
+    }
+
+    /**
+     * Returns the reporting mode of this sensor.
+     *
+     * @see Builder#setReportingMode(int)
+     * @see Sensor#getReportingMode()
+     */
+    @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+    public @ReportingMode int getReportingMode() {
+        return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
+    }
+
+    /**
      * Returns the sensor flags.
      *
      * @hide
@@ -383,6 +432,45 @@
             }
             return this;
         }
+
+        /**
+         * Sets whether this sensor is a wake up sensor.
+         *
+         * @see Sensor#isWakeUpSensor()
+         */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+        @NonNull
+        public VirtualSensorConfig.Builder setWakeUpSensor(boolean wakeUpSensor) {
+            if (wakeUpSensor) {
+                mFlags |= FLAG_WAKE_UP_SENSOR;
+            } else {
+                mFlags &= ~FLAG_WAKE_UP_SENSOR;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the reporting mode of this sensor.
+         *
+         * @throws IllegalArgumentException if the reporting mode is not one of
+         *   {@link Sensor#REPORTING_MODE_CONTINUOUS}, {@link Sensor#REPORTING_MODE_ON_CHANGE},
+         *   {@link Sensor#REPORTING_MODE_ONE_SHOT}, or
+         *   {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER}.
+         *
+         * @see Sensor#getReportingMode()
+         */
+        @FlaggedApi(Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+        @NonNull
+        public VirtualSensorConfig.Builder setReportingMode(@ReportingMode int reportingMode) {
+            if (reportingMode != REPORTING_MODE_CONTINUOUS
+                    && reportingMode != REPORTING_MODE_ON_CHANGE
+                    && reportingMode != REPORTING_MODE_ONE_SHOT
+                    && reportingMode != REPORTING_MODE_SPECIAL_TRIGGER) {
+                throw new IllegalArgumentException("Invalid reporting mode: " + reportingMode);
+            }
+            mFlags |= reportingMode << REPORTING_MODE_SHIFT;
+            return this;
+        }
     }
 
     @NonNull
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index da4ecdd..9649cab 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -42,7 +42,7 @@
  * will be invoked on that thread.
  * <p>
  * Syncs can be cancelled at any time by the framework. For example a sync that was not
- * user-initiated and lasts longer than 30 minutes will be considered timed-out and cancelled.
+ * user-initiated and lasts longer than 10 minutes will be considered timed-out and cancelled.
  * Similarly the framework will attempt to determine whether or not an adapter is making progress
  * by monitoring its network activity over the course of a minute. If the network traffic over this
  * window is close enough to zero the sync will be cancelled. You can also request the sync be
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 37f419d..ffed536 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -629,11 +629,10 @@
      * A builder for {@link AttributionSource}
      */
     public static final class Builder {
+        private boolean mHasBeenUsed;
         private @NonNull final AttributionSourceState mAttributionSourceState =
                 new AttributionSourceState();
 
-        private long mBuilderFieldsSet = 0L;
-
         /**
          * Creates a new Builder.
          *
@@ -642,8 +641,17 @@
          */
         public Builder(int uid) {
             mAttributionSourceState.uid = uid;
+            mAttributionSourceState.pid = Process.INVALID_PID;
+            mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+            mAttributionSourceState.token = sDefaultToken;
         }
 
+        /**
+         * Creates a builder that is ready to build a new {@link AttributionSource} where
+         * all fields (primitive, immutable data, pointers) are copied from the given
+         * {@link AttributionSource}. Builder methods can still be used to mutate fields further.
+         * @param current The source to copy fields from.
+         */
         public Builder(@NonNull AttributionSource current) {
             if (current == null) {
                 throw new IllegalArgumentException("current AttributionSource can not be null");
@@ -652,9 +660,11 @@
             mAttributionSourceState.pid = current.getPid();
             mAttributionSourceState.packageName = current.getPackageName();
             mAttributionSourceState.attributionTag = current.getAttributionTag();
-            mAttributionSourceState.token = current.getToken();
             mAttributionSourceState.renouncedPermissions =
                 current.mAttributionSourceState.renouncedPermissions;
+            mAttributionSourceState.deviceId = current.getDeviceId();
+            mAttributionSourceState.next = current.mAttributionSourceState.next;
+            mAttributionSourceState.token = current.getToken();
         }
 
         /**
@@ -666,7 +676,6 @@
          */
         public @NonNull Builder setPid(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
             mAttributionSourceState.pid = value;
             return this;
         }
@@ -676,7 +685,6 @@
          */
         public @NonNull Builder setPackageName(@Nullable String value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4;
             mAttributionSourceState.packageName = value;
             return this;
         }
@@ -686,7 +694,6 @@
          */
         public @NonNull Builder setAttributionTag(@Nullable String value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8;
             mAttributionSourceState.attributionTag = value;
             return this;
         }
@@ -717,11 +724,11 @@
          */
         @SystemApi
         @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
-        public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
+        public @NonNull Builder setRenouncedPermissions(
+                @Nullable Set<String> renouncedPermissions) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10;
-            mAttributionSourceState.renouncedPermissions = (value != null)
-                    ? value.toArray(new String[0]) : null;
+            mAttributionSourceState.renouncedPermissions = (renouncedPermissions != null)
+                    ? renouncedPermissions.toArray(new String[0]) : null;
             return this;
         }
 
@@ -734,7 +741,6 @@
         @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
         public @NonNull Builder setDeviceId(int deviceId) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x12;
             mAttributionSourceState.deviceId = deviceId;
             return this;
         }
@@ -744,7 +750,6 @@
          */
         public @NonNull Builder setNext(@Nullable AttributionSource value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
             mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
                     {value.mAttributionSourceState} : mAttributionSourceState.next;
             return this;
@@ -759,7 +764,6 @@
             if (value == null) {
                 throw new IllegalArgumentException("Null AttributionSource not permitted.");
             }
-            mBuilderFieldsSet |= 0x20;
             mAttributionSourceState.next =
                     new AttributionSourceState[]{value.mAttributionSourceState};
             return this;
@@ -768,28 +772,7 @@
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull AttributionSource build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
-
-            if ((mBuilderFieldsSet & 0x2) == 0) {
-                mAttributionSourceState.pid = Process.INVALID_PID;
-            }
-            if ((mBuilderFieldsSet & 0x4) == 0) {
-                mAttributionSourceState.packageName = null;
-            }
-            if ((mBuilderFieldsSet & 0x8) == 0) {
-                mAttributionSourceState.attributionTag = null;
-            }
-            if ((mBuilderFieldsSet & 0x10) == 0) {
-                mAttributionSourceState.renouncedPermissions = null;
-            }
-            if ((mBuilderFieldsSet & 0x12) == 0) {
-                mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
-            }
-            if ((mBuilderFieldsSet & 0x20) == 0) {
-                mAttributionSourceState.next = null;
-            }
-
-            mAttributionSourceState.token = sDefaultToken;
+            mHasBeenUsed = true;
 
             if (mAttributionSourceState.next == null) {
                 // The NDK aidl backend doesn't support null parcelable arrays.
@@ -799,7 +782,7 @@
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if (mHasBeenUsed) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9aebfc8..12c5d07 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
 import static android.content.flags.Flags.FLAG_ENABLE_BIND_PACKAGE_ISOLATED_PROCESS;
 
 import android.annotation.AttrRes;
@@ -32,6 +33,7 @@
 import android.annotation.Nullable;
 import android.annotation.PermissionMethod;
 import android.annotation.PermissionName;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
 import android.annotation.StringRes;
@@ -51,6 +53,7 @@
 import android.app.IServiceConnection;
 import android.app.VrManager;
 import android.app.ambientcontext.AmbientContextManager;
+import android.app.appfunctions.AppFunctionManager;
 import android.app.people.PeopleManager;
 import android.app.time.TimeManager;
 import android.companion.virtual.VirtualDeviceManager;
@@ -85,6 +88,8 @@
 import android.os.storage.StorageManager;
 import android.provider.E2eeContactKeysManager;
 import android.provider.MediaStore;
+import android.ravenwood.annotation.RavenwoodKeep;
+import android.ravenwood.annotation.RavenwoodKeepPartialClass;
 import android.telephony.TelephonyRegistryManager;
 import android.util.AttributeSet;
 import android.view.Display;
@@ -102,6 +107,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.compat.IPlatformCompatNative;
+import com.android.internal.protolog.ProtoLogConfigurationService;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -126,6 +132,7 @@
  * up-calls for application-level operations such as launching activities,
  * broadcasting and receiving intents, etc.
  */
+@RavenwoodKeepPartialClass
 public abstract class Context {
     /**
      * After {@link Build.VERSION_CODES#TIRAMISU},
@@ -929,6 +936,7 @@
      * @param resId Resource id for the CharSequence text
      */
     @NonNull
+    @RavenwoodKeep
     public final CharSequence getText(@StringRes int resId) {
         return getResources().getText(resId);
     }
@@ -942,6 +950,7 @@
      *         text information.
      */
     @NonNull
+    @RavenwoodKeep
     public final String getString(@StringRes int resId) {
         return getResources().getString(resId);
     }
@@ -958,6 +967,7 @@
      *         stripped of styled text information.
      */
     @NonNull
+    @RavenwoodKeep
     public final String getString(@StringRes int resId, Object... formatArgs) {
         return getResources().getString(resId, formatArgs);
     }
@@ -974,6 +984,7 @@
      *         does not exist.
      */
     @ColorInt
+    @RavenwoodKeep
     public final int getColor(@ColorRes int id) {
         return getResources().getColor(id, getTheme());
     }
@@ -1041,6 +1052,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
      */
     @NonNull
+    @RavenwoodKeep
     public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(attrs);
     }
@@ -1053,6 +1065,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
      */
     @NonNull
+    @RavenwoodKeep
     public final TypedArray obtainStyledAttributes(@StyleRes int resid,
             @NonNull @StyleableRes int[] attrs) throws Resources.NotFoundException {
         return getTheme().obtainStyledAttributes(resid, attrs);
@@ -1066,6 +1079,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
     @NonNull
+    @RavenwoodKeep
     public final TypedArray obtainStyledAttributes(
             @Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
@@ -1079,6 +1093,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
     @NonNull
+    @RavenwoodKeep
     public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
             @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
             @StyleRes int defStyleRes) {
@@ -4528,6 +4543,7 @@
      * <b>never</b> throw a {@link RuntimeException} if the name is not supported.
      */
     @SuppressWarnings("unchecked")
+    @RavenwoodKeep
     // TODO(b/347269120): Re-add @Nullable
     public final <T> T getSystemService(@NonNull Class<T> serviceClass) {
         // Because subclasses may override getSystemService(String) we cannot
@@ -6310,6 +6326,16 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link AppFunctionManager} for
+     * executing app functions.
+     *
+     * @see #getSystemService(String)
+     */
+    @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+    public static final String APP_FUNCTION_SERVICE = "app_function";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.content.integrity.AppIntegrityManager}.
      * @hide
      */
@@ -6653,6 +6679,8 @@
      * Use with {@link #getSystemService(String)} to retrieve a {@link
      * android.webkit.WebViewUpdateManager} for accessing the WebView update service.
      *
+     * <p>This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
+     *
      * @see #getSystemService(String)
      * @see android.webkit.WebViewUpdateManager
      * @hide
@@ -6660,6 +6688,7 @@
     @FlaggedApi(android.webkit.Flags.FLAG_UPDATE_SERVICE_IPC_WRAPPER)
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     @SuppressLint("ServiceName")
+    @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
     public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate";
 
     /**
@@ -6677,13 +6706,23 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve the
-     * {@link com.android.internal.protolog.ProtoLogService} for registering ProtoLog clients.
+     * {@link ProtoLogConfigurationService} for registering ProtoLog clients.
      *
      * @see #getSystemService(String)
-     * @see com.android.internal.protolog.ProtoLogService
+     * @see ProtoLogConfigurationService
      * @hide
      */
-    public static final String PROTOLOG_SERVICE = "protolog";
+    public static final String PROTOLOG_CONFIGURATION_SERVICE = "protolog_configuration";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.app.supervision.SupervisionManager}.
+     *
+     * @see #getSystemService(String)
+     * @see android.app.supervision.SupervisionManager
+     * @hide
+     */
+    public static final String SUPERVISION_SERVICE = "supervision";
 
     /**
      * Determine whether the given permission is allowed for a particular
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index cb57c7b..da3cc1bd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3767,7 +3767,7 @@
      * <p>The Intent will have the following extra value:</p>
      * <ul>
      *   <li><em>{@link android.content.Intent#EXTRA_PHONE_NUMBER}</em> -
-     *       the phone number originally intended to be dialed.</li>
+     *       the phone number dialed.</li>
      * </ul>
      * <p class="note">Starting in Android 15, this broadcast is no longer sent as an ordered
      * broadcast.  The <code>resultData</code> no longer has any effect and will not determine the
@@ -3800,6 +3800,14 @@
      * {@link android.Manifest.permission#PROCESS_OUTGOING_CALLS}
      * permission to receive this Intent.</p>
      *
+     * <p class="note">Starting in {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, this broadcast is
+     * no longer sent as an ordered broadcast, and does not allow activity launches.  This means
+     * that receivers may no longer change the phone number for the outgoing call, or cancel the
+     * outgoing call.  This functionality is only possible using the
+     * {@link android.telecom.CallRedirectionService} API.  Although background receivers are
+     * woken up to handle this intent, no guarantee is made as to the timeliness of the broadcast.
+     * </p>
+     *
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
@@ -7642,13 +7650,6 @@
             | FLAG_GRANT_PREFIX_URI_PERMISSION;
 
     /**
-     * Flags that are not normally set by application code, but set for you by the system.
-     */
-    private static final int SYSTEM_ONLY_FLAGS = FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
-            | FLAG_ACTIVITY_BROUGHT_TO_FRONT
-            | FLAG_RECEIVER_FROM_SHELL;
-
-    /**
      * Local flag indicating this instance was created by copy constructor.
      */
     private static final int LOCAL_FLAG_FROM_COPY = 1 << 0;
@@ -7701,11 +7702,6 @@
     @TestApi
     public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0;
 
-    /**
-     * Extended flags that are not normally set by application code, but set for you by the system.
-     */
-    private static final int SYSTEM_ONLY_EXTENDED_FLAGS = EXTENDED_FLAG_FILTER_MISMATCH;
-
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // toUri() and parseUri() options.
@@ -12649,28 +12645,6 @@
         }
     }
 
-    /**
-     * Prepare this {@link Intent} to enter system_server.
-     *
-     * @hide
-     */
-    public void prepareToEnterSystemServer() {
-        // Refuse possible leaked file descriptors
-        if (hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-        // These flags are set only by the system, and should be stripped out as soon as the intent
-        // is received by system_server from the caller so it can be properly updated later.
-        removeFlags(SYSTEM_ONLY_FLAGS);
-        removeExtendedFlags(SYSTEM_ONLY_EXTENDED_FLAGS);
-        if (mOriginalIntent != null) {
-            mOriginalIntent.prepareToEnterSystemServer();
-        }
-        if (mSelector != null) {
-            mSelector.prepareToEnterSystemServer();
-        }
-    }
-
     /** @hide */
     public boolean hasWebURI() {
         if (getData() == null) {
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index 41a4288..e353a01 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -22,24 +22,7 @@
       "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        },
-        {
-          "include-filter": "android.content.ContextTest"
-        },
-        {
-          "include-filter": "android.content.ComponentCallbacksControllerTest"
-        },
-        {
-          "include-filter": "android.content.ContextWrapperTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_android_content",
       "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java", "(/|^)ComponentCallbacksController.java"]
     },
     {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index cd3ce87..5779a44 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2501,33 +2501,19 @@
         return toStringInner(/* secure =*/ false, /* includeInternalData =*/ true, indent);
     }
 
-    private void addIndentOrComma(StringBuilder sb, String indent) {
-        if (indent != null) {
-            sb.append("\n  ");
-            sb.append(indent);
-        } else {
-            sb.append(", ");
-        }
+    /** @hide */
+    public String toSimpleString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(mId);
+        addReadableFlags(sb);
+        return sb.toString();
     }
 
-    private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
-        final StringBuilder sb = new StringBuilder();
-
-        if (indent != null) {
-            sb.append(indent);
-        }
-
-        sb.append("ShortcutInfo {");
-
-        sb.append("id=");
-        sb.append(secure ? "***" : mId);
-
-        sb.append(", flags=0x");
-        sb.append(Integer.toHexString(mFlags));
+    private void addReadableFlags(StringBuilder sb) {
         sb.append(" [");
         if ((mFlags & FLAG_SHADOW) != 0) {
-            // Note the shadow flag isn't actually used anywhere and it's just for dumpsys, so
-            // we don't have an isXxx for this.
+            // Note the shadow flag isn't actually used anywhere and it's
+            // just for dumpsys, so we don't have an isXxx for this.
             sb.append("Sdw");
         }
         if (!isEnabled()) {
@@ -2576,7 +2562,32 @@
             sb.append("Hid-L");
         }
         sb.append("]");
+    }
 
+    private void addIndentOrComma(StringBuilder sb, String indent) {
+        if (indent != null) {
+            sb.append("\n  ");
+            sb.append(indent);
+        } else {
+            sb.append(", ");
+        }
+    }
+
+    private String toStringInner(boolean secure, boolean includeInternalData, String indent) {
+        final StringBuilder sb = new StringBuilder();
+
+        if (indent != null) {
+            sb.append(indent);
+        }
+
+        sb.append("ShortcutInfo {");
+
+        sb.append("id=");
+        sb.append(secure ? "***" : mId);
+
+        sb.append(", flags=0x");
+        sb.append(Integer.toHexString(mFlags));
+        addReadableFlags(sb);
         addIndentOrComma(sb, indent);
 
         sb.append("packageName=");
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index b0ab11f..ffadd1e 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -112,6 +112,39 @@
                     "exclude-annotation":"org.junit.Ignore"
                 }
             ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJInstallationTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJUninstallationTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
         }
     ],
     "presubmit-large":[
@@ -171,6 +204,50 @@
                     "include-filter": "android.content.pm.cts.PackageManagerShellCommandMultiUserTest"
                 }
             ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJInstallationTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJUninstallationTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
+        },
+        {
+            "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+            "options":[
+               {
+                   "exclude-annotation":"androidx.test.filters.FlakyTest"
+               },
+               {
+                   "exclude-annotation":"org.junit.Ignore"
+               }
+            ]
         }
     ]
 }
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 59fca3b..28534ad 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -161,6 +161,26 @@
 }
 
 flag {
+    name: "fix_avatar_content_provider_null_authority"
+    namespace: "multiuser"
+    description: "Fix crash when content provider authority is null."
+    bug: "362880068"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+    name: "fix_avatar_picker_not_responding_for_new_user"
+    namespace: "multiuser"
+    description: "Avatar picker is not responding after selecting photo for new user."
+    bug: "358407488"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "fix_get_user_property_cache"
     namespace: "multiuser"
     description: "Cache is not optimised for getUserProperty for values below 0, eg. UserHandler.USER_NULL or UserHandle.USER_ALL"
@@ -288,6 +308,13 @@
 }
 
 flag {
+    name: "stop_previous_user_apps"
+    namespace: "multiuser"
+    description: "Stop the previous user apps early in a user switch"
+    bug: "323200731"
+}
+
+flag {
     name: "disable_private_space_items_on_home"
     namespace: "profile_experiences"
     description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically"
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 653e243..68b5d78 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -22,6 +22,8 @@
 import android.content.om.OverlayableInfo;
 import android.content.res.loader.AssetsProvider;
 import android.content.res.loader.ResourcesProvider;
+import android.ravenwood.annotation.RavenwoodClassLoadHook;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.GuardedBy;
@@ -45,6 +47,8 @@
  * making the creation of AssetManagers very cheap.
  * @hide
  */
+@RavenwoodKeepWholeClass
+@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public final class ApkAssets {
 
     /**
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index afddc77..986d881 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -24,6 +24,8 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodReplace;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
@@ -45,6 +47,7 @@
  * opened FileDescriptor that can be used to read the data, as well as the
  * offset and length of that entry's data in the file.
  */
+@RavenwoodKeepWholeClass
 public class AssetFileDescriptor implements Parcelable, Closeable {
     /**
      * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
@@ -300,10 +303,30 @@
 
         NonSeekableAutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
             super(fd.getParcelFileDescriptor());
-            super.skip(fd.getStartOffset());
+            skipRaw(fd.getStartOffset());
             mRemaining = (int) fd.getLength();
         }
 
+        @RavenwoodReplace
+        private long skipRaw(long count) throws IOException {
+            return super.skip(count);
+        }
+
+        private long skipRaw$ravenwood(long count) throws IOException {
+            // OpenJDK doesn't allow skip on pipes, so just use read.
+            final byte[] buf = new byte[(int) Math.min(1024, count)];
+            long totalRead = 0;
+            while (totalRead < count) {
+                final int toRead = (int) Math.min(count - totalRead, buf.length);
+                final int read = super.read(buf, 0, toRead);
+                if (read == -1) {
+                    break;
+                }
+                totalRead += read;
+            }
+            return totalRead;
+        }
+
         @Override
         public int available() throws IOException {
             return mRemaining >= 0
@@ -341,12 +364,12 @@
             if (mRemaining >= 0) {
                 if (mRemaining == 0) return -1;
                 if (count > mRemaining) count = mRemaining;
-                long res = super.skip(count);
+                long res = skipRaw(count);
                 if (res >= 0) mRemaining -= res;
                 return res;
             }
 
-            return super.skip(count);
+            return skipRaw(count);
         }
 
         @Override
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 899c2d6..6fd4d014 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -16,8 +16,8 @@
 
 package android.content.res;
 
-import static android.content.res.Resources.ID_NULL;
 import static android.app.ResourcesManager.ApkKey;
+import static android.content.res.Resources.ID_NULL;
 
 import android.annotation.AnyRes;
 import android.annotation.ArrayRes;
@@ -34,6 +34,9 @@
 import android.content.res.loader.ResourcesLoader;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodReplace;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -43,6 +46,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.om.OverlayConfig;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
 
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -66,11 +70,14 @@
  * files that have been bundled with the application as a simple stream of
  * bytes.
  */
+@RavenwoodKeepWholeClass
 public final class AssetManager implements AutoCloseable {
     private static final String TAG = "AssetManager";
     private static final boolean DEBUG_REFS = false;
 
-    private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
+    private static final String FRAMEWORK_APK_PATH = getFrameworkApkPath();
+    private static final String FRAMEWORK_APK_PATH_DEVICE = "/system/framework/framework-res.apk";
+    private static final String FRAMEWORK_APK_PATH_RAVENWOOD = "ravenwood-data/framework-res.apk";
 
     private static final Object sSync = new Object();
 
@@ -147,6 +154,7 @@
             return this;
         }
 
+        @RavenwoodThrow(blockedBy = ResourcesLoader.class)
         public Builder addLoader(ResourcesLoader loader) {
             mLoaders.add(loader);
             return this;
@@ -206,6 +214,16 @@
         }
     }
 
+    @RavenwoodReplace
+    private static String getFrameworkApkPath() {
+        return FRAMEWORK_APK_PATH_DEVICE;
+    }
+
+    private static String getFrameworkApkPath$ravenwood() {
+        return RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath()
+                + FRAMEWORK_APK_PATH_RAVENWOOD;
+    }
+
     /**
      * Create a new AssetManager containing only the basic system assets.
      * Applications will not generally use this method, instead retrieving the
@@ -260,7 +278,9 @@
             final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
             apkAssets.add(ApkAssets.loadFromPath(frameworkPath, ApkAssets.PROPERTY_SYSTEM));
 
+            // TODO(Ravenwood): overlay support?
             final String[] systemIdmapPaths =
+                    RavenwoodEnvironment.getInstance().isRunningOnRavenwood() ? new String[0] :
                     OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote();
             for (String idmapPath : systemIdmapPaths) {
                 apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM));
@@ -351,6 +371,7 @@
      * Changes the {@link ResourcesLoader ResourcesLoaders} used in this AssetManager.
      * @hide
      */
+    @RavenwoodThrow(blockedBy = ResourcesLoader.class)
     void setLoaders(@NonNull List<ResourcesLoader> newLoaders) {
         Objects.requireNonNull(newLoaders, "newLoaders");
 
@@ -578,6 +599,7 @@
 
     /** @hide */
     @NonNull
+    @RavenwoodThrow(blockedBy = ResourcesLoader.class)
     public List<ResourcesLoader> getLoaders() {
         return mLoaders == null ? Collections.emptyList() : Arrays.asList(mLoaders);
     }
@@ -1216,6 +1238,7 @@
     }
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @RavenwoodReplace
     void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
             @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
             long outIndicesAddress) {
@@ -1799,4 +1822,37 @@
      */
     @UnsupportedAppUsage
     public static native int getGlobalAssetManagerCount();
+
+    // Ravenwood Workarounds
+
+    /**
+     * ART has explicit support for allocating pinned (non-movable) array objects.
+     * On Ravenwood we allocate regular arrays and use critical array access in
+     * JNI as a best effort to reduce memory copying.
+     * TODO(b/359983716): Remove when Ravenwood switch to ART
+     */
+    void applyStyle$ravenwood(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
+            long outIndicesAddress) {
+        Objects.requireNonNull(inAttrs, "inAttrs");
+        var runtime = RavenwoodEnvironment.getInstance();
+        final int[] outValues = runtime.fromAddress(outValuesAddress);
+        final int[] outIndices = runtime.fromAddress(outIndicesAddress);
+        synchronized (this) {
+            // Need to synchronize on AssetManager because we will be accessing
+            // the native implementation of AssetManager.
+            ensureValidLocked();
+            nativeApplyStyleWithArray(mObject, themePtr, defStyleAttr, defStyleRes,
+                    parser != null ? parser.mParseState : 0, inAttrs, outValues,
+                    outIndices);
+        }
+    }
+
+    /**
+     * A variant of nativeApplyStyle(), accepting java arrays instead of raw pointers.
+     * TODO(b/359983716): Remove when Ravenwood switch to ART
+     */
+    private static native void nativeApplyStyleWithArray(long ptr, long themePtr,
+            @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
+            long xmlParserPtr, @NonNull int[] inAttrs, int[] outData, int[] outIndices);
 }
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 7b18117..0a264e3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -26,6 +26,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
@@ -143,6 +144,7 @@
  * @attr ref android.R.styleable#ColorStateListItem_color
  * @attr ref android.R.styleable#ColorStateListItem_lStar
  */
+@RavenwoodKeepWholeClass
 public class ColorStateList extends ComplexColor implements Parcelable {
     private static final String TAG = "ColorStateList";
 
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index f929c1f..d6620d1 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -28,6 +28,7 @@
 import android.os.Build.VERSION_CODES;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.view.InsetsSourceControl;
@@ -42,6 +43,7 @@
  * 
  *  {@hide} 
  */
+@RavenwoodKeepWholeClass
 public class CompatibilityInfo implements Parcelable {
     /** default compatibility info object for compatible applications */
     @UnsupportedAppUsage
diff --git a/core/java/android/content/res/ComplexColor.java b/core/java/android/content/res/ComplexColor.java
index 58c6fc5..a385ee3 100644
--- a/core/java/android/content/res/ComplexColor.java
+++ b/core/java/android/content/res/ComplexColor.java
@@ -18,13 +18,14 @@
 
 import android.annotation.ColorInt;
 import android.content.res.Resources.Theme;
-import android.graphics.Color;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * Defines an abstract class for the complex color information, like
  * {@link android.content.res.ColorStateList} or {@link android.content.res.GradientColor}
  * @hide
  */
+@RavenwoodKeepWholeClass
 public abstract class ComplexColor {
     private int mChangingConfigurations;
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 982224b..ef200c3 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -58,6 +58,7 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Slog;
@@ -89,6 +90,7 @@
  * with {@link android.app.Activity#getResources}:</p>
  * <pre>Configuration config = getResources().getConfiguration();</pre>
  */
+@RavenwoodKeepWholeClass
 public final class Configuration implements Parcelable, Comparable<Configuration> {
     /** @hide */
     public static final Configuration EMPTY = new Configuration();
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index 5e10a57..9dc097a 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -18,6 +18,7 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * A Cache class which can be used to cache resource objects that are easy to clone but more
@@ -25,6 +26,7 @@
  *
  * @hide For internal use only.
  */
+@RavenwoodKeepWholeClass
 public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
 
     @UnsupportedAppUsage
diff --git a/core/java/android/content/res/ConstantState.java b/core/java/android/content/res/ConstantState.java
index 09d4a59..cedfe02 100644
--- a/core/java/android/content/res/ConstantState.java
+++ b/core/java/android/content/res/ConstantState.java
@@ -16,6 +16,7 @@
 package android.content.res;
 
 import android.content.pm.ActivityInfo.Config;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * A cache class that can provide new instances of a particular resource which may change
@@ -29,6 +30,7 @@
  * changing configurations of each Animator in the set)
  * @hide
  */
+@RavenwoodKeepWholeClass
 abstract public class ConstantState<T> {
 
     /**
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
index d0ebe33..c139de6 100644
--- a/core/java/android/content/res/DrawableCache.java
+++ b/core/java/android/content/res/DrawableCache.java
@@ -19,13 +19,17 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.ravenwood.annotation.RavenwoodKeep;
+import android.ravenwood.annotation.RavenwoodKeepPartialClass;
 
 /**
  * Class which can be used to cache Drawable resources against a theme.
  */
+@RavenwoodKeepPartialClass
 class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
 
     @UnsupportedAppUsage
+    @RavenwoodKeep
     DrawableCache() {
     }
 
diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java
index 6ff96f4..798f906 100644
--- a/core/java/android/content/res/Element.java
+++ b/core/java/android/content/res/Element.java
@@ -19,6 +19,7 @@
 import static android.os.SystemProperties.PROP_VALUE_MAX;
 
 import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.Pools.SimplePool;
 import android.util.Slog;
 
@@ -33,6 +34,7 @@
  *
  * {@hide}
  */
+@RavenwoodKeepWholeClass
 public class Element {
     private static final int DEFAULT_MAX_STRING_ATTR_LENGTH = 32_768;
     private static final int MAX_POOL_SIZE = 128;
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 24ae31e..8aef45b 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -18,6 +18,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Typeface;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -36,6 +37,7 @@
  * Parser for xml type font resources.
  * @hide
  */
+@RavenwoodKeepWholeClass
 public class FontResourcesParser {
     private static final String TAG = "FontResourcesParser";
 
diff --git a/core/java/android/content/res/FontScaleConverter.java b/core/java/android/content/res/FontScaleConverter.java
index f4312a9..b2c5afa 100644
--- a/core/java/android/content/res/FontScaleConverter.java
+++ b/core/java/android/content/res/FontScaleConverter.java
@@ -20,6 +20,7 @@
 import android.annotation.AnyThread;
 import android.annotation.FlaggedApi;
 import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * A converter for non-linear font scaling. Converts font sizes given in "sp" dimensions to a
@@ -32,6 +33,7 @@
  * scale them slightly to preserve the visual hierarchy when compared to smaller fonts.
  */
 @FlaggedApi(Flags.FLAG_FONT_SCALE_CONVERTER_PUBLIC)
+@RavenwoodKeepWholeClass
 public interface FontScaleConverter {
     /**
      * Converts a dimension in "sp" to "dp".
diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java
index c7237ea..9087a9a 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -19,6 +19,7 @@
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.MathUtils;
 import android.util.SparseArray;
 
@@ -34,6 +35,7 @@
  *
  * @hide
  */
+@RavenwoodKeepWholeClass
 public class FontScaleConverterFactory {
     private static final float SCALE_KEY_MULTIPLIER = 100f;
 
diff --git a/core/java/android/content/res/FontScaleConverterImpl.java b/core/java/android/content/res/FontScaleConverterImpl.java
index 1968c4e..508507a 100644
--- a/core/java/android/content/res/FontScaleConverterImpl.java
+++ b/core/java/android/content/res/FontScaleConverterImpl.java
@@ -17,6 +17,7 @@
 package android.content.res;
 
 import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.MathUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -33,6 +34,7 @@
  */
 // Needs to be public so the Kotlin test can see it
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@RavenwoodKeepWholeClass
 public class FontScaleConverterImpl implements FontScaleConverter {
 
     /** @hide */
diff --git a/core/java/android/content/res/ResourceId.java b/core/java/android/content/res/ResourceId.java
index 3c7b5fc..c9e900f 100644
--- a/core/java/android/content/res/ResourceId.java
+++ b/core/java/android/content/res/ResourceId.java
@@ -16,11 +16,13 @@
 package android.content.res;
 
 import android.annotation.AnyRes;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * Provides a set of utility methods for dealing with Resource IDs.
  * @hide
  */
+@RavenwoodKeepWholeClass
 public final class ResourceId {
     /**
      * Checks whether the integer {@code id} is a valid resource ID, as generated by AAPT.
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 248ef1d..0559631 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -58,6 +58,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -124,6 +126,7 @@
  * <p>For more information about using resources, see the documentation about <a
  * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
  */
+@RavenwoodKeepWholeClass
 public class Resources {
     /**
      * The {@code null} resource ID. This denotes an invalid resource ID that is returned by the
@@ -417,6 +420,7 @@
      * @hide Pending API finalization.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @RavenwoodThrow(blockedBy = DrawableInflater.class)
     public final DrawableInflater getDrawableInflater() {
         if (mDrawableInflater == null) {
             mDrawableInflater = new DrawableInflater(this, mClassLoader);
@@ -478,6 +482,7 @@
      *
      * @return Typeface The Typeface data associated with the resource.
      */
+    @RavenwoodThrow(blockedBy = Typeface.class)
     @NonNull public Typeface getFont(@FontRes int id) throws NotFoundException {
         final TypedValue value = obtainTempTypedValue();
         try {
@@ -502,6 +507,7 @@
     /**
      * @hide
      */
+    @RavenwoodThrow(blockedBy = Typeface.class)
     public void preloadFonts(@ArrayRes int id) {
         final TypedArray array = obtainTypedArray(id);
         try {
@@ -915,6 +921,7 @@
      * @deprecated Use {@link #getDrawable(int, Theme)} instead.
      */
     @Deprecated
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
         final Drawable d = getDrawable(id, null);
         if (d != null && d.canApplyTheme()) {
@@ -939,6 +946,7 @@
      * @throws NotFoundException Throws NotFoundException if the given ID does
      *         not exist.
      */
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
             throws NotFoundException {
         return getDrawableForDensity(id, 0, theme);
@@ -974,6 +982,7 @@
      */
     @Nullable
     @Deprecated
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawableForDensity(@DrawableRes int id, int density)
             throws NotFoundException {
         return getDrawableForDensity(id, density, null);
@@ -997,6 +1006,7 @@
      *             not exist.
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
         final TypedValue value = obtainTempTypedValue();
         try {
@@ -1025,6 +1035,7 @@
      * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
      */
     @Deprecated
+    @RavenwoodThrow(blockedBy = Movie.class)
     public Movie getMovie(@RawRes int id) throws NotFoundException {
         final InputStream is = openRawResource(id);
         final Movie movie = Movie.decodeStream(is);
@@ -1783,6 +1794,7 @@
          * @throws NotFoundException Throws NotFoundException if the given ID
          *         does not exist.
          */
+        @RavenwoodThrow(blockedBy = Drawable.class)
         public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
             return Resources.this.getDrawable(id, this);
         }
@@ -2845,6 +2857,7 @@
      * @param appInfo The ApplicationInfo that contains resources paths of the package.
      */
     @FlaggedApi(android.content.res.Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    @RavenwoodThrow(reason = "FLAG_REGISTER_RESOURCE_PATHS is unsupported")
     public static void registerResourcePaths(@NonNull String uniqueId,
             @NonNull ApplicationInfo appInfo) {
         if (Flags.registerResourcePaths()) {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index d874270..e6b9342 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -47,6 +47,8 @@
 import android.os.LocaleList;
 import android.os.ParcelFileDescriptor;
 import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -82,6 +84,7 @@
  *
  * @hide
  */
+@RavenwoodKeepWholeClass
 public class ResourcesImpl {
     static final String TAG = "Resources";
 
@@ -689,6 +692,7 @@
     }
 
     @Nullable
+    @RavenwoodThrow(blockedBy = Drawable.class)
     Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
             int density, @Nullable Resources.Theme theme)
             throws NotFoundException {
@@ -1035,6 +1039,7 @@
      * Loads a font from XML or resources stream.
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = Typeface.class)
     public Typeface loadFont(Resources wrapper, TypedValue value, int id) {
         if (value.string == null) {
             throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 99b56a8..99a9d89 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -22,12 +22,14 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.loader.ResourcesLoader;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.text.TextUtils;
 
 import java.util.Arrays;
 import java.util.Objects;
 
 /** @hide */
+@RavenwoodKeepWholeClass
 public final class ResourcesKey {
     @Nullable
     @UnsupportedAppUsage
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index 0070a6f..290bc10 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -25,6 +25,8 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.text.LineBreakConfig;
+import android.ravenwood.annotation.RavenwoodClassLoadHook;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.text.Annotation;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -60,6 +62,8 @@
  *
  * {@hide}
  */
+@RavenwoodKeepWholeClass
+@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public final class StringBlock implements Closeable {
     private static final String TAG = "AssetManager";
     private static final boolean localLOGV = false;
diff --git a/core/java/android/content/res/TagCounter.java b/core/java/android/content/res/TagCounter.java
index 94deee7..c69a133 100644
--- a/core/java/android/content/res/TagCounter.java
+++ b/core/java/android/content/res/TagCounter.java
@@ -16,11 +16,14 @@
 
 package android.content.res;
 
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
 /**
  * Counter used to track the number of tags seen during manifest validation.
  *
  * {@hide}
  */
+@RavenwoodKeepWholeClass
 public class TagCounter {
     private static final int DEFAULT_MAX_COUNT = 512;
 
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
index 690dfcf..c7fcc1a 100644
--- a/core/java/android/content/res/ThemedResourceCache.java
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -22,6 +22,7 @@
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Resources.Theme;
 import android.content.res.Resources.ThemeKey;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.ArrayMap;
 import android.util.LongSparseArray;
 
@@ -32,6 +33,7 @@
  *
  * @param <T> type of data to cache
  */
+@RavenwoodKeepWholeClass
 abstract class ThemedResourceCache<T> {
     public static final int UNDEFINED_GENERATION = -1;
     @UnsupportedAppUsage
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index bb2d2a0..79185a1 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -27,6 +27,8 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.StrictMode;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+import android.ravenwood.annotation.RavenwoodThrow;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -46,6 +48,7 @@
  * The indices used to retrieve values from this structure correspond to
  * the positions of the attributes given to obtainStyledAttributes.
  */
+@RavenwoodKeepWholeClass
 public class TypedArray implements AutoCloseable {
 
     static TypedArray obtain(Resources res, int len) {
@@ -557,6 +560,7 @@
      * @hide
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = ComplexColor.class)
     public ComplexColor getComplexColor(@StyleableRes int index) {
         if (mRecycled) {
             throw new RuntimeException("Cannot make calls to a recycled instance!");
@@ -991,6 +995,7 @@
      *         not a color or drawable resource.
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawable(@StyleableRes int index) {
         return getDrawableForDensity(index, 0);
     }
@@ -1000,6 +1005,7 @@
      * @hide
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = Drawable.class)
     public Drawable getDrawableForDensity(@StyleableRes int index, int density) {
         if (mRecycled) {
             throw new RuntimeException("Cannot make calls to a recycled instance!");
@@ -1037,6 +1043,7 @@
      *         not a font resource.
      */
     @Nullable
+    @RavenwoodThrow(blockedBy = Typeface.class)
     public Typeface getFont(@StyleableRes int index) {
         if (mRecycled) {
             throw new RuntimeException("Cannot make calls to a recycled instance!");
diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java
index f72f3c4..1520768 100644
--- a/core/java/android/content/res/Validator.java
+++ b/core/java/android/content/res/Validator.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StyleableRes;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import com.android.internal.R;
 
@@ -32,6 +33,7 @@
  *
  * {@hide}
  */
+@RavenwoodKeepWholeClass
 public class Validator {
 
     private final ArrayDeque<Element> mElements = new ArrayDeque<>();
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index 7649b32..40c5324 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -24,6 +24,8 @@
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+import android.ravenwood.annotation.RavenwoodClassLoadHook;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.util.TypedValue;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -44,6 +46,8 @@
  * {@hide}
  */
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@RavenwoodKeepWholeClass
+@RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public final class XmlBlock implements AutoCloseable {
     private static final boolean DEBUG=false;
 
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 8df7c37..21122a1 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -16,6 +16,7 @@
 
 package android.content.rollback;
 
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
@@ -145,7 +146,10 @@
         mPendingRestores.remove(ri);
     }
 
-    /** @hide */
+    /**
+     * True if the package is an apex else false.
+     */
+    @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
     public boolean isApex() {
         return mIsApex;
     }
@@ -154,7 +158,11 @@
     public @PackageManager.RollbackDataPolicy int getRollbackDataPolicy() {
         return mRollbackDataPolicy;
     }
-    /** @hide */
+
+    /**
+     * True if the package is apk-in-apex else false.
+     */
+    @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
     public boolean isApkInApex() {
         return mIsApkInApex;
     }
diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java
index d128055..a20159d 100644
--- a/core/java/android/content/rollback/RollbackInfo.java
+++ b/core/java/android/content/rollback/RollbackInfo.java
@@ -19,8 +19,6 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
-import android.content.pm.Flags;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.os.Parcel;
@@ -136,11 +134,8 @@
      * Get rollback impact level. Refer {@link
      * android.content.pm.PackageInstaller.SessionParams#setRollbackImpactLevel(int)} for more info
      * on impact level.
-     *
-     * @hide
      */
-    @TestApi
-    @FlaggedApi(Flags.FLAG_RECOVERABILITY_DETECTION)
+    @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
     public @PackageManager.RollbackImpactLevel int getRollbackImpactLevel() {
         return mRollbackImpactLevel;
     }
diff --git a/core/java/android/database/OWNERS b/core/java/android/database/OWNERS
index 53f5bb0..50b7015 100644
--- a/core/java/android/database/OWNERS
+++ b/core/java/android/database/OWNERS
@@ -1,6 +1,2 @@
 include /SQLITE_OWNERS
 
[email protected]
[email protected]
[email protected]
-
diff --git a/core/java/android/database/sqlite/TEST_MAPPING b/core/java/android/database/sqlite/TEST_MAPPING
index 9dcf4e5..659cf6c 100644
--- a/core/java/android/database/sqlite/TEST_MAPPING
+++ b/core/java/android/database/sqlite/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "FrameworksCoreTests",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "exclude-annotation": "org.junit.Ignore"
-                },
-                {
-                    "include-filter": "android.database.sqlite.SQLiteRawStatementTest"
-                }
-            ],
+            "name": "FrameworksCoreTests_sqlite",
             "file_patterns": [
                 "(/|^)SQLiteRawStatement.java",
                 "(/|^)SQLiteDatabase.java",
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 10c3730..e0b9f60 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -17,7 +17,9 @@
 
 package android.hardware;
 
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputSensorInfo;
 import android.os.Build;
@@ -1182,6 +1184,8 @@
 
     /** @hide */
     @UnsupportedAppUsage
+    @SuppressLint("UnflaggedApi") // Promotion to TestApi
+    @TestApi
     public int getHandle() {
         return mHandle;
     }
diff --git a/core/java/android/hardware/biometrics/AuthenticateOptions.java b/core/java/android/hardware/biometrics/AuthenticateOptions.java
index 7766071..4dc6ea19 100644
--- a/core/java/android/hardware/biometrics/AuthenticateOptions.java
+++ b/core/java/android/hardware/biometrics/AuthenticateOptions.java
@@ -74,4 +74,7 @@
 
     /** The attribution tag, if any. */
     @Nullable String getAttributionTag();
+
+    /** If the authentication is requested due to mandatory biometrics being active. */
+    boolean isMandatoryBiometrics();
 }
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 61d8702..8975191 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -162,6 +162,13 @@
      * @hide
      */
     int BIOMETRIC_ERROR_POWER_PRESSED = 19;
+
+    /**
+     * Mandatory biometrics is not in effect.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE = 20;
+
     /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index fc72db3..6682b36 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -61,7 +61,6 @@
             BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             BIOMETRIC_ERROR_RE_ENROLL,
-            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             FINGERPRINT_ERROR_UNKNOWN,
             FINGERPRINT_ERROR_BAD_CALIBRATION,
             BIOMETRIC_ERROR_POWER_PRESSED})
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index de1cac4..9bc46b9 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -80,6 +80,20 @@
             BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
     /**
+     * Lockout error.
+     * @hide
+     */
+    public static final int BIOMETRIC_ERROR_LOCKOUT =
+            BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+
+    /**
+     * Mandatory biometrics is not effective.
+     * @hide
+     */
+    public static final int BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE =
+            BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+
+    /**
      * A security vulnerability has been discovered and the sensor is unavailable until a
      * security update has addressed this issue. This error can be received if for example,
      * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
@@ -113,7 +127,9 @@
             BIOMETRIC_ERROR_HW_UNAVAILABLE,
             BIOMETRIC_ERROR_NONE_ENROLLED,
             BIOMETRIC_ERROR_NO_HARDWARE,
-            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
+            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
+            BIOMETRIC_ERROR_LOCKOUT,
+            BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface BiometricError {}
 
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 9007b62..b11961c 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -638,17 +638,17 @@
          * Set caller's component name for getting logo icon/description. This should only be used
          * by ConfirmDeviceCredentialActivity, see b/337082634 for more context.
          *
-         * @param componentNameForConfirmDeviceCredentialActivity set the component name for
-         *                                                        ConfirmDeviceCredentialActivity.
+         * @param realCaller set the component name of real caller for
+         *                   ConfirmDeviceCredentialActivity.
          * @return This builder.
          * @hide
          */
         @NonNull
         @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
-        public Builder setComponentNameForConfirmDeviceCredentialActivity(
-                ComponentName componentNameForConfirmDeviceCredentialActivity) {
-            mPromptInfo.setComponentNameForConfirmDeviceCredentialActivity(
-                    componentNameForConfirmDeviceCredentialActivity);
+        public Builder setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) {
+            mPromptInfo.setRealCallerForConfirmDeviceCredentialActivity(realCaller);
+            mPromptInfo.setClassNameIfItIsConfirmDeviceCredentialActivity(
+                    mContext.getClass().getName());
             return this;
         }
 
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 17cd18c..b195225 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -49,7 +49,7 @@
     void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
             int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
             long requestId, int cookie, boolean allowBackgroundAuthentication,
-            boolean isForLegacyFingerprintManager);
+            boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics);
 
     // Starts authentication with the previously prepared client.
     void startPreparedClient(int cookie);
diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
index a9eca3f..4b9d5ce 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
@@ -28,6 +28,7 @@
 import android.hardware.biometrics.BiometricPrompt.ButtonInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -62,6 +63,7 @@
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable {
+    private static final String TAG = "PromptContentViewWithMoreOptionsButton";
     @VisibleForTesting
     static final int MAX_DESCRIPTION_CHARACTER_NUMBER = 225;
 
@@ -149,13 +151,12 @@
          *
          * @param description The description to display.
          * @return This builder.
-         * @throws IllegalArgumentException If description exceeds certain character limit.
          */
         @NonNull
         @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
         public Builder setDescription(@NonNull String description) {
             if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
-                throw new IllegalArgumentException("The character number of description exceeds "
+                Log.w(TAG, "The character number of description exceeds "
                         + MAX_DESCRIPTION_CHARACTER_NUMBER);
             }
             mDescription = description;
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 901f6b7..df5d864 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -57,7 +57,8 @@
     private boolean mIsForLegacyFingerprintManager = false;
     private boolean mShowEmergencyCallButton = false;
     private boolean mUseParentProfileForDeviceCredential = false;
-    private ComponentName mComponentNameForConfirmDeviceCredentialActivity = null;
+    private ComponentName mRealCallerForConfirmDeviceCredentialActivity = null;
+    private String mClassNameIfItIsConfirmDeviceCredentialActivity = null;
 
     public PromptInfo() {
 
@@ -89,8 +90,9 @@
         mIsForLegacyFingerprintManager = in.readBoolean();
         mShowEmergencyCallButton = in.readBoolean();
         mUseParentProfileForDeviceCredential = in.readBoolean();
-        mComponentNameForConfirmDeviceCredentialActivity = in.readParcelable(
+        mRealCallerForConfirmDeviceCredentialActivity = in.readParcelable(
                 ComponentName.class.getClassLoader(), ComponentName.class);
+        mClassNameIfItIsConfirmDeviceCredentialActivity = in.readString();
     }
 
     public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -136,7 +138,8 @@
         dest.writeBoolean(mIsForLegacyFingerprintManager);
         dest.writeBoolean(mShowEmergencyCallButton);
         dest.writeBoolean(mUseParentProfileForDeviceCredential);
-        dest.writeParcelable(mComponentNameForConfirmDeviceCredentialActivity, 0);
+        dest.writeParcelable(mRealCallerForConfirmDeviceCredentialActivity, 0);
+        dest.writeString(mClassNameIfItIsConfirmDeviceCredentialActivity);
     }
 
     // LINT.IfChange
@@ -155,7 +158,7 @@
             return true;
         } else if (mShowEmergencyCallButton) {
             return true;
-        } else if (mComponentNameForConfirmDeviceCredentialActivity != null) {
+        } else if (mRealCallerForConfirmDeviceCredentialActivity != null) {
             return true;
         }
         return false;
@@ -321,10 +324,8 @@
         mShowEmergencyCallButton = showEmergencyCallButton;
     }
 
-    public void setComponentNameForConfirmDeviceCredentialActivity(
-            ComponentName componentNameForConfirmDeviceCredentialActivity) {
-        mComponentNameForConfirmDeviceCredentialActivity =
-                componentNameForConfirmDeviceCredentialActivity;
+    public void setRealCallerForConfirmDeviceCredentialActivity(ComponentName realCaller) {
+        mRealCallerForConfirmDeviceCredentialActivity = realCaller;
     }
 
     public void setUseParentProfileForDeviceCredential(
@@ -332,6 +333,14 @@
         mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
     }
 
+    /**
+     * Set the class name of ConfirmDeviceCredentialActivity.
+     */
+    void setClassNameIfItIsConfirmDeviceCredentialActivity(String className) {
+        mClassNameIfItIsConfirmDeviceCredentialActivity = className;
+    }
+
+
     // Getters
 
     /**
@@ -455,8 +464,15 @@
         return mShowEmergencyCallButton;
     }
 
-    public ComponentName getComponentNameForConfirmDeviceCredentialActivity() {
-        return mComponentNameForConfirmDeviceCredentialActivity;
+    public ComponentName getRealCallerForConfirmDeviceCredentialActivity() {
+        return mRealCallerForConfirmDeviceCredentialActivity;
     }
 
+    /**
+     * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+     * not ConfirmDeviceCredentialActivity.
+     */
+    public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+       return mClassNameIfItIsConfirmDeviceCredentialActivity;
+    }
 }
diff --git a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
index d8b2867..86006f8 100644
--- a/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
+++ b/core/java/android/hardware/biometrics/PromptVerticalListContentView.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -49,6 +50,7 @@
  */
 @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
 public final class PromptVerticalListContentView implements PromptContentViewParcelable {
+    private static final String TAG = "PromptVerticalListContentView";
     @VisibleForTesting
     static final int MAX_ITEM_NUMBER = 20;
     @VisibleForTesting
@@ -155,12 +157,11 @@
          *
          * @param description The description to display.
          * @return This builder.
-         * @throws IllegalArgumentException If description exceeds certain character limit.
          */
         @NonNull
         public Builder setDescription(@NonNull String description) {
             if (description.length() > MAX_DESCRIPTION_CHARACTER_NUMBER) {
-                throw new IllegalArgumentException("The character number of description exceeds "
+                Log.w(TAG, "The character number of description exceeds "
                         + MAX_DESCRIPTION_CHARACTER_NUMBER);
             }
             mDescription = description;
@@ -172,8 +173,7 @@
          *
          * @param listItem The list item view to display
          * @return This builder.
-         * @throws IllegalArgumentException If this list item exceeds certain character limits or
-         *                                  the number of list items exceeds certain limit.
+         * @throws IllegalArgumentException If the number of list items exceeds certain limit.
          */
         @NonNull
         public Builder addListItem(@NonNull PromptContentItem listItem) {
@@ -188,8 +188,7 @@
          * @param listItem The list item view to display
          * @param index    The position at which to add the item
          * @return This builder.
-         * @throws IllegalArgumentException If this list item exceeds certain character limits or
-         *                                  the number of list items exceeds certain limit.
+         * @throws IllegalArgumentException If the number of list items exceeds certain limit.
          */
         @NonNull
         public Builder addListItem(@NonNull PromptContentItem listItem, int index) {
@@ -200,9 +199,8 @@
 
         private void checkItemLimits(@NonNull PromptContentItem listItem) {
             if (doesListItemExceedsCharLimit(listItem)) {
-                throw new IllegalArgumentException(
-                        "The character number of list item exceeds "
-                                + MAX_EACH_ITEM_CHARACTER_NUMBER);
+                Log.w(TAG, "The character number of list item exceeds "
+                        + MAX_EACH_ITEM_CHARACTER_NUMBER);
             }
             if (mContentList.size() > MAX_ITEM_NUMBER) {
                 throw new IllegalArgumentException(
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 7665fe8..04a810a 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -461,6 +461,12 @@
         @SuppressLint("NonUserGetterCalled")
         public boolean registerClient(Context ctx, IBinder token, int extension,
                 String cameraId, Map<String, CameraMetadataNative> characteristicsMapNative) {
+            if (!SystemProperties.getBoolean("ro.camerax.extensions.enabled",
+                    /*default*/ false)) {
+                Log.v(TAG, "Disabled camera extension property!");
+                return false;
+            }
+
             boolean ret = registerClientHelper(ctx, token, extension, false /*useFallback*/);
 
             if (Flags.concertMode()) {
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index fbed50a..ac72116 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1537,9 +1537,14 @@
      * be made, and for firing pre-capture flash pulses to estimate
      * scene brightness and required final capture flash power, when
      * the flash is enabled.</p>
-     * <p>Normally, this entry should be set to START for only a
-     * single request, and the application should wait until the
-     * sequence completes before starting a new one.</p>
+     * <p>Flash is enabled during precapture sequence when:</p>
+     * <ul>
+     * <li>AE mode is ON_ALWAYS_FLASH</li>
+     * <li>AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or</li>
+     * <li>AE mode is ON and flash mode is TORCH or SINGLE</li>
+     * </ul>
+     * <p>Normally, this entry should be set to START for only single request, and the
+     * application should wait until the sequence completes before starting a new one.</p>
      * <p>When a precapture metering sequence is finished, the camera device
      * may lock the auto-exposure routine internally to be able to accurately expose the
      * subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
@@ -2705,6 +2710,13 @@
      * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
      * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
      * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
+     * <p>When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+     * the AE mode, flash mode, and flash strength level remain the same between precapture
+     * trigger request and final capture request. The flash strength level being set during
+     * precapture sequence is used by the camera device as a reference. The actual strength
+     * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+     * exposure time and sensitivities between precapture and final capture for the specified
+     * strength level.</p>
      * <p><b>Range of valid values:</b><br>
      * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d652b4c..34ce92c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -935,9 +935,14 @@
      * be made, and for firing pre-capture flash pulses to estimate
      * scene brightness and required final capture flash power, when
      * the flash is enabled.</p>
-     * <p>Normally, this entry should be set to START for only a
-     * single request, and the application should wait until the
-     * sequence completes before starting a new one.</p>
+     * <p>Flash is enabled during precapture sequence when:</p>
+     * <ul>
+     * <li>AE mode is ON_ALWAYS_FLASH</li>
+     * <li>AE mode is ON_AUTO_FLASH and the scene is deemed too dark without flash, or</li>
+     * <li>AE mode is ON and flash mode is TORCH or SINGLE</li>
+     * </ul>
+     * <p>Normally, this entry should be set to START for only single request, and the
+     * application should wait until the sequence completes before starting a new one.</p>
      * <p>When a precapture metering sequence is finished, the camera device
      * may lock the auto-exposure routine internally to be able to accurately expose the
      * subsequent still capture image (<code>{@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} == STILL_CAPTURE</code>).
@@ -2821,8 +2826,6 @@
      * boost when the light level threshold is exceeded.</p>
      * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
      * indicate when it is not being applied by returning 'INACTIVE'.</p>
-     * <p>This key will be absent from the CaptureResult if AE mode is not set to
-     * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
      * <p>The default value will always be 'INACTIVE'.</p>
      * <p><b>Possible values:</b></p>
      * <ul>
@@ -2996,6 +2999,13 @@
      * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
      * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of <code>ON_AUTO_FLASH</code>, <code>ON_ALWAYS_FLASH</code>,
      * <code>ON_AUTO_FLASH_REDEYE</code>, <code>ON_EXTERNAL_FLASH</code> values, then the strengthLevel will be ignored.</p>
+     * <p>When AE mode is ON and flash mode is TORCH or SINGLE, the application should make sure
+     * the AE mode, flash mode, and flash strength level remain the same between precapture
+     * trigger request and final capture request. The flash strength level being set during
+     * precapture sequence is used by the camera device as a reference. The actual strength
+     * may be less, and the auto-exposure routine makes sure proper conversions of sensor
+     * exposure time and sensitivities between precapture and final capture for the specified
+     * strength level.</p>
      * <p><b>Range of valid values:</b><br>
      * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 1e7f70b..c7dba6c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -80,6 +80,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -90,6 +91,17 @@
     private final String TAG;
     private final boolean DEBUG = false;
 
+    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+        private static final ThreadFactory mFactory = Executors.defaultThreadFactory();
+
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread thread = mFactory.newThread(r);
+            thread.setName("CameraDeviceExecutor");
+            return thread;
+        }
+    };
+
     private static final int REQUEST_ID_NONE = -1;
 
     /**
@@ -354,7 +366,11 @@
         mCameraId = cameraId;
         if (Flags.singleThreadExecutor()) {
             mDeviceCallback = new ClientStateCallback(executor, callback);
-            mDeviceExecutor = Executors.newSingleThreadExecutor();
+            if (Flags.singleThreadExecutorNaming()) {
+                mDeviceExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
+            } else {
+                mDeviceExecutor = Executors.newSingleThreadExecutor();
+            }
         } else {
             mDeviceCallback = callback;
             mDeviceExecutor = executor;
@@ -2272,6 +2288,19 @@
                 // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
                 result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
                         getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+                Map<String, CameraCharacteristics> physicalIdToChars = getPhysicalIdToChars();
+                for (PhysicalCaptureResultInfo oneResultInfo : physicalResults) {
+                    String physicalId = oneResultInfo.getCameraId();
+                    CameraMetadataNative physicalResult = oneResultInfo.getCameraMetadata();
+                    CameraCharacteristics ch = physicalIdToChars.get(physicalId);
+                    if (ch != null)  {
+                        physicalResult.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+                                ch.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+                    } else {
+                        Log.e(TAG, "Unable to find characteristics for physical camera "
+                                + physicalId);
+                    }
+                }
 
                 final CaptureCallbackHolder holder =
                         CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 109b0a8..6a96a54 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -158,6 +158,8 @@
                 return "thermal";
             case BRIGHTNESS_MAX_REASON_POWER_IC:
                 return "power IC";
+            case BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE:
+                return "wear bedtime";
         }
         return "invalid";
     }
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cae33d0..85e33a8 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -174,10 +174,14 @@
     /**
      * Gets an instance of the display manager global singleton.
      *
+     * This method is actually unsupported on Ravenwood, however to support
+     * {@link android.app.ResourcesManager} we make this method always return null.
+     *
      * @return The display manager instance, may be null early in system startup
      * before the display manager has been fully initialized.
      */
     @UnsupportedAppUsage
+    // @RavenwoodIgnore(value = "null")
     public static DisplayManagerGlobal getInstance() {
         synchronized (DisplayManagerGlobal.class) {
             if (sInstance == null) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 7353dde..a2d24f6 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -504,6 +504,9 @@
         public float dozeScreenBrightness;
         public int dozeScreenStateReason;
 
+        // Override that makes display use normal brightness while dozing.
+        public boolean useNormalBrightnessForDoze;
+
         public DisplayPowerRequest() {
             policy = POLICY_BRIGHT;
             useProximitySensor = false;
@@ -537,6 +540,7 @@
             dozeScreenBrightness = other.dozeScreenBrightness;
             dozeScreenState = other.dozeScreenState;
             dozeScreenStateReason = other.dozeScreenStateReason;
+            useNormalBrightnessForDoze = other.useNormalBrightnessForDoze;
         }
 
         @Override
@@ -561,7 +565,8 @@
                     && boostScreenBrightness == other.boostScreenBrightness
                     && floatEquals(dozeScreenBrightness, other.dozeScreenBrightness)
                     && dozeScreenState == other.dozeScreenState
-                    && dozeScreenStateReason == other.dozeScreenStateReason;
+                    && dozeScreenStateReason == other.dozeScreenStateReason
+                    && useNormalBrightnessForDoze == other.useNormalBrightnessForDoze;
         }
 
         private boolean floatEquals(float f1, float f2) {
@@ -587,7 +592,8 @@
                     + ", dozeScreenBrightness=" + dozeScreenBrightness
                     + ", dozeScreenState=" + Display.stateToString(dozeScreenState)
                     + ", dozeScreenStateReason="
-                            + Display.stateReasonToString(dozeScreenStateReason);
+                            + Display.stateReasonToString(dozeScreenStateReason)
+                    + ", useNormalBrightnessForDoze=" + useNormalBrightnessForDoze;
         }
 
         public static String policyToString(int policy) {
diff --git a/core/java/android/hardware/face/FaceAuthenticateOptions.java b/core/java/android/hardware/face/FaceAuthenticateOptions.java
index 518f902a..8babbfa 100644
--- a/core/java/android/hardware/face/FaceAuthenticateOptions.java
+++ b/core/java/android/hardware/face/FaceAuthenticateOptions.java
@@ -120,6 +120,8 @@
     }
 
 
+    /** If the authentication is requested due to mandatory biometrics being active. */
+    private boolean mIsMandatoryBiometrics;
 
     // Code below generated by codegen v1.0.23.
     //
@@ -188,7 +190,8 @@
             @AuthenticateReason int authenticateReason,
             @PowerManager.WakeReason int wakeReason,
             @NonNull String opPackageName,
-            @Nullable String attributionTag) {
+            @Nullable String attributionTag,
+            boolean isMandatoryBiometrics) {
         this.mUserId = userId;
         this.mSensorId = sensorId;
         this.mDisplayState = displayState;
@@ -229,6 +232,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
+        this.mIsMandatoryBiometrics = isMandatoryBiometrics;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -261,7 +265,7 @@
      * The reason for this operation when requested by the system (sysui),
      * otherwise AUTHENTICATE_REASON_UNKNOWN.
      *
-     * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
+     * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
      * for more details about each reason.
      */
     @DataClass.Generated.Member
@@ -299,6 +303,14 @@
     }
 
     /**
+     * If the authentication is requested due to mandatory biometrics being active.
+     */
+    @DataClass.Generated.Member
+    public boolean isMandatoryBiometrics() {
+        return mIsMandatoryBiometrics;
+    }
+
+    /**
      * The sensor id for this operation.
      */
     @DataClass.Generated.Member
@@ -332,6 +344,15 @@
         return this;
     }
 
+    /**
+     * If the authentication is requested due to mandatory biometrics being active.
+     */
+    @DataClass.Generated.Member
+    public @NonNull FaceAuthenticateOptions setIsMandatoryBiometrics( boolean value) {
+        mIsMandatoryBiometrics = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
@@ -351,7 +372,8 @@
                 && mAuthenticateReason == that.mAuthenticateReason
                 && mWakeReason == that.mWakeReason
                 && java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
-                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag);
+                && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
+                && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics;
     }
 
     @Override
@@ -368,6 +390,7 @@
         _hash = 31 * _hash + mWakeReason;
         _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
+        _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics);
         return _hash;
     }
 
@@ -377,9 +400,10 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        byte flg = 0;
+        int flg = 0;
+        if (mIsMandatoryBiometrics) flg |= 0x80;
         if (mAttributionTag != null) flg |= 0x40;
-        dest.writeByte(flg);
+        dest.writeInt(flg);
         dest.writeInt(mUserId);
         dest.writeInt(mSensorId);
         dest.writeInt(mDisplayState);
@@ -400,7 +424,8 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        byte flg = in.readByte();
+        int flg = in.readInt();
+        boolean isMandatoryBiometrics = (flg & 0x80) != 0;
         int userId = in.readInt();
         int sensorId = in.readInt();
         int displayState = in.readInt();
@@ -449,6 +474,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
+        this.mIsMandatoryBiometrics = isMandatoryBiometrics;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -481,6 +507,7 @@
         private @PowerManager.WakeReason int mWakeReason;
         private @NonNull String mOpPackageName;
         private @Nullable String mAttributionTag;
+        private boolean mIsMandatoryBiometrics;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -524,7 +551,7 @@
          * The reason for this operation when requested by the system (sysui),
          * otherwise AUTHENTICATE_REASON_UNKNOWN.
          *
-         * See packages/SystemUI/src/com/android/systemui/deviceentry/shared/FaceAuthReason.kt
+         * See frameworks/base/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
          * for more details about each reason.
          */
         @DataClass.Generated.Member
@@ -573,10 +600,21 @@
             return this;
         }
 
+        /**
+         * If the authentication is requested due to mandatory biometrics being active.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setIsMandatoryBiometrics(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mIsMandatoryBiometrics = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull FaceAuthenticateOptions build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x80; // Mark builder used
+            mBuilderFieldsSet |= 0x100; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mUserId = defaultUserId();
@@ -606,12 +644,13 @@
                     mAuthenticateReason,
                     mWakeReason,
                     mOpPackageName,
-                    mAttributionTag);
+                    mAttributionTag,
+                    mIsMandatoryBiometrics);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x80) != 0) {
+            if ((mBuilderFieldsSet & 0x100) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -619,10 +658,10 @@
     }
 
     @DataClass.Generated(
-            time = 1677119626034L,
+            time = 1723436679828L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/face/FaceAuthenticateOptions.java",
-            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final  int AUTHENTICATE_REASON_UNKNOWN\npublic static final  int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final  int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final  int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final  int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final  int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final  int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final  int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final  int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  int defaultDisplayState()\nprivate static  int defaultAuthenticateReason()\nprivate static  int defaultWakeReason()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\[email protected](genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\npublic static final  int AUTHENTICATE_REASON_UNKNOWN\npublic static final  int AUTHENTICATE_REASON_STARTED_WAKING_UP\npublic static final  int AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_ASSISTANT_VISIBLE\npublic static final  int AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN\npublic static final  int AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED\npublic static final  int AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED\npublic static final  int AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED\npublic static final  int AUTHENTICATE_REASON_QS_EXPANDED\npublic static final  int AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER\npublic static final  int AUTHENTICATE_REASON_UDFPS_POINTER_DOWN\nprivate final @android.hardware.face.FaceAuthenticateOptions.AuthenticateReason int mAuthenticateReason\nprivate final @android.os.PowerManager.WakeReason int mWakeReason\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate  boolean mIsMandatoryBiometrics\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  int defaultDisplayState()\nprivate static  int defaultAuthenticateReason()\nprivate static  int defaultWakeReason()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nclass FaceAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\[email protected](genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
index dc66542..ddf1e5b 100644
--- a/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
+++ b/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java
@@ -97,6 +97,11 @@
         return null;
     }
 
+    /**
+     * If the authentication is requested due to mandatory biometrics being active.
+     */
+    private boolean mIsMandatoryBiometrics;
+
     // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
@@ -118,7 +123,8 @@
             @AuthenticateOptions.DisplayState int displayState,
             @NonNull String opPackageName,
             @Nullable String attributionTag,
-            @Nullable AuthenticateReason.Vendor vendorReason) {
+            @Nullable AuthenticateReason.Vendor vendorReason,
+            boolean isMandatoryBiometrics) {
         this.mUserId = userId;
         this.mSensorId = sensorId;
         this.mIgnoreEnrollmentState = ignoreEnrollmentState;
@@ -130,6 +136,7 @@
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
         this.mVendorReason = vendorReason;
+        this.mIsMandatoryBiometrics = isMandatoryBiometrics;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -199,6 +206,14 @@
     }
 
     /**
+     * If the authentication is requested due to mandatory biometrics being active.
+     */
+    @DataClass.Generated.Member
+    public boolean isMandatoryBiometrics() {
+        return mIsMandatoryBiometrics;
+    }
+
+    /**
      * The sensor id for this operation.
      */
     @DataClass.Generated.Member
@@ -244,6 +259,15 @@
         return this;
     }
 
+    /**
+     * If the authentication is requested due to mandatory biometrics being active.
+     */
+    @DataClass.Generated.Member
+    public @NonNull FingerprintAuthenticateOptions setIsMandatoryBiometrics( boolean value) {
+        mIsMandatoryBiometrics = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public boolean equals(@Nullable Object o) {
@@ -263,7 +287,8 @@
                 && mDisplayState == that.mDisplayState
                 && java.util.Objects.equals(mOpPackageName, that.mOpPackageName)
                 && java.util.Objects.equals(mAttributionTag, that.mAttributionTag)
-                && java.util.Objects.equals(mVendorReason, that.mVendorReason);
+                && java.util.Objects.equals(mVendorReason, that.mVendorReason)
+                && mIsMandatoryBiometrics == that.mIsMandatoryBiometrics;
     }
 
     @Override
@@ -280,6 +305,7 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mOpPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mAttributionTag);
         _hash = 31 * _hash + java.util.Objects.hashCode(mVendorReason);
+        _hash = 31 * _hash + Boolean.hashCode(mIsMandatoryBiometrics);
         return _hash;
     }
 
@@ -289,11 +315,12 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        byte flg = 0;
+        int flg = 0;
         if (mIgnoreEnrollmentState) flg |= 0x4;
+        if (mIsMandatoryBiometrics) flg |= 0x80;
         if (mAttributionTag != null) flg |= 0x20;
         if (mVendorReason != null) flg |= 0x40;
-        dest.writeByte(flg);
+        dest.writeInt(flg);
         dest.writeInt(mUserId);
         dest.writeInt(mSensorId);
         dest.writeInt(mDisplayState);
@@ -313,8 +340,9 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        byte flg = in.readByte();
+        int flg = in.readInt();
         boolean ignoreEnrollmentState = (flg & 0x4) != 0;
+        boolean isMandatoryBiometrics = (flg & 0x80) != 0;
         int userId = in.readInt();
         int sensorId = in.readInt();
         int displayState = in.readInt();
@@ -333,6 +361,7 @@
                 NonNull.class, null, mOpPackageName);
         this.mAttributionTag = attributionTag;
         this.mVendorReason = vendorReason;
+        this.mIsMandatoryBiometrics = isMandatoryBiometrics;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -365,6 +394,7 @@
         private @NonNull String mOpPackageName;
         private @Nullable String mAttributionTag;
         private @Nullable AuthenticateReason.Vendor mVendorReason;
+        private boolean mIsMandatoryBiometrics;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -456,10 +486,21 @@
             return this;
         }
 
+        /**
+         * If the authentication is requested due to mandatory biometrics being active.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setIsMandatoryBiometrics(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mIsMandatoryBiometrics = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull FingerprintAuthenticateOptions build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x80; // Mark builder used
+            mBuilderFieldsSet |= 0x100; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mUserId = defaultUserId();
@@ -489,12 +530,13 @@
                     mDisplayState,
                     mOpPackageName,
                     mAttributionTag,
-                    mVendorReason);
+                    mVendorReason,
+                    mIsMandatoryBiometrics);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x80) != 0) {
+            if ((mBuilderFieldsSet & 0x100) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -502,10 +544,10 @@
     }
 
     @DataClass.Generated(
-            time = 1689703591032L,
+            time = 1723436831455L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/fingerprint/FingerprintAuthenticateOptions.java",
-            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nprivate static  android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\[email protected](genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "private final  int mUserId\nprivate  int mSensorId\nprivate final  boolean mIgnoreEnrollmentState\nprivate final @android.hardware.biometrics.AuthenticateOptions.DisplayState int mDisplayState\nprivate @android.annotation.NonNull java.lang.String mOpPackageName\nprivate @android.annotation.Nullable java.lang.String mAttributionTag\nprivate @android.annotation.Nullable android.hardware.biometrics.common.AuthenticateReason.Vendor mVendorReason\nprivate  boolean mIsMandatoryBiometrics\nprivate static  int defaultUserId()\nprivate static  int defaultSensorId()\nprivate static  boolean defaultIgnoreEnrollmentState()\nprivate static  int defaultDisplayState()\nprivate static  java.lang.String defaultOpPackageName()\nprivate static  java.lang.String defaultAttributionTag()\nprivate static  android.hardware.biometrics.common.AuthenticateReason.Vendor defaultVendorReason()\nclass FingerprintAuthenticateOptions extends java.lang.Object implements [android.hardware.biometrics.AuthenticateOptions, android.os.Parcelable]\[email protected](genParcelable=true, genAidl=true, genBuilder=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 98e1137..2d96bba 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -25,7 +25,7 @@
 import android.hardware.input.IInputDeviceBatteryState;
 import android.hardware.input.IKeyboardBacklightListener;
 import android.hardware.input.IKeyboardBacklightState;
-import android.hardware.input.IKeyboardSystemShortcutListener;
+import android.hardware.input.IKeyGestureEventListener;
 import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.KeyboardLayoutSelectionResult;
@@ -241,13 +241,13 @@
 
     KeyGlyphMap getKeyGlyphMap(int deviceId);
 
-    @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
+    @PermissionManuallyEnforced
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
-    void registerKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    void registerKeyGestureEventListener(IKeyGestureEventListener listener);
 
-    @EnforcePermission("MONITOR_KEYBOARD_SYSTEM_SHORTCUTS")
+    @PermissionManuallyEnforced
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
-            + "android.Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)")
-    void unregisterKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener);
+            + "android.Manifest.permission.MANAGE_KEY_GESTURES)")
+    void unregisterKeyGestureEventListener(IKeyGestureEventListener listener);
 }
diff --git a/core/java/android/hardware/input/IKeyGestureEventListener.aidl b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
new file mode 100644
index 0000000..2c430f1
--- /dev/null
+++ b/core/java/android/hardware/input/IKeyGestureEventListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.hardware.input;
+
+/** @hide */
+oneway interface IKeyGestureEventListener {
+
+    /**
+     * Called when a key gesture event occurs.
+     */
+    void onKeyGestureEvent(int deviceId, in int[] keycodes, int modifierState, int shortcut);
+}
diff --git a/core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl b/core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl
deleted file mode 100644
index 8d44917..0000000
--- a/core/java/android/hardware/input/IKeyboardSystemShortcutListener.aidl
+++ /dev/null
@@ -1,27 +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 android.hardware.input;
-
-/** @hide */
-oneway interface IKeyboardSystemShortcutListener {
-
-    /**
-     * Called when the keyboard system shortcut is triggered.
-     */
-    void onKeyboardSystemShortcutTriggered(int deviceId, in int[] keycodes, int modifierState,
-                                           int shortcut);
-}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6bc522b..04cfcd8 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1378,33 +1378,31 @@
     }
 
     /**
-     * Registers a keyboard system shortcut listener for {@link KeyboardSystemShortcut} being
-     * triggered.
+     * Registers a key gesture event listener for {@link KeyGestureEvent} being triggered.
      *
      * @param executor an executor on which the callback will be called
-     * @param listener the {@link KeyboardSystemShortcutListener}
+     * @param listener the {@link KeyGestureEventListener}
      * @throws IllegalArgumentException if {@code listener} has already been registered previously.
      * @throws NullPointerException     if {@code listener} or {@code executor} is null.
      * @hide
-     * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+     * @see #unregisterKeyGestureEventListener(KeyGestureEventListener)
      */
-    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    public void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
-            @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
-        mGlobal.registerKeyboardSystemShortcutListener(executor, listener);
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    public void registerKeyGestureEventListener(@NonNull Executor executor,
+            @NonNull KeyGestureEventListener listener) throws IllegalArgumentException {
+        mGlobal.registerKeyGestureEventListener(executor, listener);
     }
 
     /**
-     * Unregisters a previously added keyboard system shortcut listener.
+     * Unregisters a previously added key gesture event listener.
      *
-     * @param listener the {@link KeyboardSystemShortcutListener}
+     * @param listener the {@link KeyGestureEventListener}
      * @hide
-     * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
+     * @see #registerKeyGestureEventListener(Executor, KeyGestureEventListener)
      */
-    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    public void unregisterKeyboardSystemShortcutListener(
-            @NonNull KeyboardSystemShortcutListener listener) {
-        mGlobal.unregisterKeyboardSystemShortcutListener(listener);
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    public void unregisterKeyGestureEventListener(@NonNull KeyGestureEventListener listener) {
+        mGlobal.unregisterKeyGestureEventListener(listener);
     }
 
     /**
@@ -1510,19 +1508,18 @@
     }
 
     /**
-     * A callback used to be notified about keyboard system shortcuts being triggered.
+     * A callback used to notify about key gesture event on completion.
      *
-     * @see #registerKeyboardSystemShortcutListener(Executor, KeyboardSystemShortcutListener)
-     * @see #unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+     * @see #registerKeyGestureEventListener(Executor, KeyGestureEventListener)
+     * @see #unregisterKeyGestureEventListener(KeyGestureEventListener)
      * @hide
      */
-    public interface KeyboardSystemShortcutListener {
+    public interface KeyGestureEventListener {
         /**
-         * Called when a keyboard system shortcut is triggered.
+         * Called when a key gesture event occurs.
          *
-         * @param systemShortcut the shortcut info about the shortcut that was triggered.
+         * @param event the gesture event that occurred.
          */
-        void onKeyboardSystemShortcutTriggered(int deviceId,
-                @NonNull KeyboardSystemShortcut systemShortcut);
+        void onKeyGestureEvent(@NonNull KeyGestureEvent event);
     }
 }
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index f7fa557..2a36238 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -25,8 +25,8 @@
 import android.hardware.SensorManager;
 import android.hardware.input.InputManager.InputDeviceBatteryListener;
 import android.hardware.input.InputManager.InputDeviceListener;
+import android.hardware.input.InputManager.KeyGestureEventListener;
 import android.hardware.input.InputManager.KeyboardBacklightListener;
-import android.hardware.input.InputManager.KeyboardSystemShortcutListener;
 import android.hardware.input.InputManager.OnTabletModeChangedListener;
 import android.hardware.input.InputManager.StickyModifierStateListener;
 import android.hardware.lights.Light;
@@ -111,13 +111,13 @@
     @Nullable
     private IStickyModifierStateListener mStickyModifierStateListener;
 
-    private final Object mKeyboardSystemShortcutListenerLock = new Object();
-    @GuardedBy("mKeyboardSystemShortcutListenerLock")
+    private final Object mKeyGestureEventListenerLock = new Object();
+    @GuardedBy("mKeyGestureEventListenerLock")
     @Nullable
-    private ArrayList<KeyboardSystemShortcutListenerDelegate> mKeyboardSystemShortcutListeners;
-    @GuardedBy("mKeyboardSystemShortcutListenerLock")
+    private ArrayList<KeyGestureEventListenerDelegate> mKeyGestureEventListeners;
+    @GuardedBy("mKeyGestureEventListenerLock")
     @Nullable
-    private IKeyboardSystemShortcutListener mKeyboardSystemShortcutListener;
+    private IKeyGestureEventListener mKeyGestureEventListener;
 
     // InputDeviceSensorManager gets notified synchronously from the binder thread when input
     // devices change, so it must be synchronized with the input device listeners.
@@ -567,7 +567,7 @@
         Objects.requireNonNull(listener, "listener must not be null");
 
         synchronized (mOnTabletModeChangedListeners) {
-            if (mOnTabletModeChangedListeners == null) {
+            if (mOnTabletModeChangedListeners.isEmpty()) {
                 initializeTabletModeListenerLocked();
             }
             int idx = findOnTabletModeChangedListenerLocked(listener);
@@ -1064,94 +1064,92 @@
         }
     }
 
-    private static final class KeyboardSystemShortcutListenerDelegate {
-        final KeyboardSystemShortcutListener mListener;
+    private static final class KeyGestureEventListenerDelegate {
+        final KeyGestureEventListener mListener;
         final Executor mExecutor;
 
-        KeyboardSystemShortcutListenerDelegate(KeyboardSystemShortcutListener listener,
+        KeyGestureEventListenerDelegate(KeyGestureEventListener listener,
                 Executor executor) {
             mListener = listener;
             mExecutor = executor;
         }
 
-        void onKeyboardSystemShortcutTriggered(int deviceId,
-                KeyboardSystemShortcut systemShortcut) {
-            mExecutor.execute(() ->
-                    mListener.onKeyboardSystemShortcutTriggered(deviceId, systemShortcut));
+        void onKeyGestureEvent(KeyGestureEvent event) {
+            mExecutor.execute(() -> mListener.onKeyGestureEvent(event));
         }
     }
 
-    private class LocalKeyboardSystemShortcutListener extends IKeyboardSystemShortcutListener.Stub {
+    private class LocalKeyGestureEventListener extends IKeyGestureEventListener.Stub {
 
         @Override
-        public void onKeyboardSystemShortcutTriggered(int deviceId, int[] keycodes,
-                int modifierState, int shortcut) {
-            synchronized (mKeyboardSystemShortcutListenerLock) {
-                if (mKeyboardSystemShortcutListeners == null) return;
-                final int numListeners = mKeyboardSystemShortcutListeners.size();
+        public void onKeyGestureEvent(int deviceId, int[] keycodes, int modifierState,
+                int gestureType) {
+            synchronized (mKeyGestureEventListenerLock) {
+                if (mKeyGestureEventListeners == null) return;
+                final int numListeners = mKeyGestureEventListeners.size();
                 for (int i = 0; i < numListeners; i++) {
-                    mKeyboardSystemShortcutListeners.get(i)
-                            .onKeyboardSystemShortcutTriggered(deviceId,
-                                    new KeyboardSystemShortcut(keycodes, modifierState, shortcut));
+                    mKeyGestureEventListeners.get(i)
+                            .onKeyGestureEvent(
+                                    new KeyGestureEvent(deviceId, keycodes, modifierState,
+                                            gestureType));
                 }
             }
         }
     }
 
     /**
-     * @see InputManager#registerKeyboardSystemShortcutListener(Executor,
-     * KeyboardSystemShortcutListener)
+     * @see InputManager#registerKeyGestureEventListener(Executor,
+     * KeyGestureEventListener)
      */
-    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    void registerKeyboardSystemShortcutListener(@NonNull Executor executor,
-            @NonNull KeyboardSystemShortcutListener listener) throws IllegalArgumentException {
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    void registerKeyGestureEventListener(@NonNull Executor executor,
+            @NonNull KeyGestureEventListener listener) throws IllegalArgumentException {
         Objects.requireNonNull(executor, "executor should not be null");
         Objects.requireNonNull(listener, "listener should not be null");
 
-        synchronized (mKeyboardSystemShortcutListenerLock) {
-            if (mKeyboardSystemShortcutListener == null) {
-                mKeyboardSystemShortcutListeners = new ArrayList<>();
-                mKeyboardSystemShortcutListener = new LocalKeyboardSystemShortcutListener();
+        synchronized (mKeyGestureEventListenerLock) {
+            if (mKeyGestureEventListener == null) {
+                mKeyGestureEventListeners = new ArrayList<>();
+                mKeyGestureEventListener = new LocalKeyGestureEventListener();
 
                 try {
-                    mIm.registerKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
+                    mIm.registerKeyGestureEventListener(mKeyGestureEventListener);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
             }
-            final int numListeners = mKeyboardSystemShortcutListeners.size();
+            final int numListeners = mKeyGestureEventListeners.size();
             for (int i = 0; i < numListeners; i++) {
-                if (mKeyboardSystemShortcutListeners.get(i).mListener == listener) {
+                if (mKeyGestureEventListeners.get(i).mListener == listener) {
                     throw new IllegalArgumentException("Listener has already been registered!");
                 }
             }
-            KeyboardSystemShortcutListenerDelegate delegate =
-                    new KeyboardSystemShortcutListenerDelegate(listener, executor);
-            mKeyboardSystemShortcutListeners.add(delegate);
+            KeyGestureEventListenerDelegate delegate =
+                    new KeyGestureEventListenerDelegate(listener, executor);
+            mKeyGestureEventListeners.add(delegate);
         }
     }
 
     /**
-     * @see InputManager#unregisterKeyboardSystemShortcutListener(KeyboardSystemShortcutListener)
+     * @see InputManager#unregisterKeyGestureEventListener(KeyGestureEventListener)
      */
-    @RequiresPermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    void unregisterKeyboardSystemShortcutListener(
-            @NonNull KeyboardSystemShortcutListener listener) {
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    void unregisterKeyGestureEventListener(@NonNull KeyGestureEventListener listener) {
         Objects.requireNonNull(listener, "listener should not be null");
 
-        synchronized (mKeyboardSystemShortcutListenerLock) {
-            if (mKeyboardSystemShortcutListeners == null) {
+        synchronized (mKeyGestureEventListenerLock) {
+            if (mKeyGestureEventListeners == null) {
                 return;
             }
-            mKeyboardSystemShortcutListeners.removeIf((delegate) -> delegate.mListener == listener);
-            if (mKeyboardSystemShortcutListeners.isEmpty()) {
+            mKeyGestureEventListeners.removeIf((delegate) -> delegate.mListener == listener);
+            if (mKeyGestureEventListeners.isEmpty()) {
                 try {
-                    mIm.unregisterKeyboardSystemShortcutListener(mKeyboardSystemShortcutListener);
+                    mIm.unregisterKeyGestureEventListener(mKeyGestureEventListener);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
-                mKeyboardSystemShortcutListeners = null;
-                mKeyboardSystemShortcutListener = null;
+                mKeyGestureEventListeners = null;
+                mKeyGestureEventListener = null;
             }
         }
     }
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index d85e41d..8592ded 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -20,10 +20,12 @@
 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS;
 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG;
 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG;
+import static com.android.hardware.input.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
 import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag;
 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.keyboardRepeatKeys;
 import static com.android.hardware.input.Flags.touchpadTapDragging;
 import static com.android.hardware.input.Flags.touchpadVisualizer;
 import static com.android.input.flags.Flags.enableInputFilterRustImpl;
@@ -40,6 +42,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.sysprop.InputProperties;
+import android.view.ViewConfiguration;
 
 /**
  * InputSettings encapsulates reading and writing settings related to input
@@ -90,6 +93,30 @@
      */
     public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1;
 
+    /**
+     * The minimum allowed repeat keys timeout before starting key repeats.
+     * @hide
+     */
+    public static final int MIN_KEY_REPEAT_TIMEOUT_MILLIS = 150;
+
+    /**
+     * The maximum allowed repeat keys timeout before starting key repeats.
+     * @hide
+     */
+    public static final int MAX_KEY_REPEAT_TIMEOUT_MILLIS = 2000;
+
+    /**
+     * The minimum allowed repeat keys delay between successive key repeats.
+     * @hide
+     */
+    public static final int MIN_KEY_REPEAT_DELAY_MILLIS = 20;
+
+    /**
+     * The maximum allowed repeat keys delay between successive key repeats.
+     * @hide
+     */
+    public static final int MAX_KEY_REPEAT_DELAY_MILLIS = 2000;
+
     private InputSettings() {
     }
 
@@ -336,6 +363,39 @@
     }
 
     /**
+     * Returns true if the touchpad visualizer is allowed to appear.
+     *
+     * @param context The application context.
+     * @return Whether it is allowed to show touchpad visualizer or not.
+     *
+     * @hide
+     */
+    public static boolean useTouchpadVisualizer(@NonNull Context context) {
+        if (!isTouchpadVisualizerFeatureFlagEnabled()) {
+            return false;
+        }
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, 0, UserHandle.USER_CURRENT) == 1;
+    }
+
+    /**
+     * Sets the touchpad visualizer behaviour.
+     *
+     * @param context The application context.
+     * @param enabled Will enable touchpad visualizer if true, disable it if false
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setTouchpadVisualizer(@NonNull Context context, boolean enabled) {
+        if (!isTouchpadVisualizerFeatureFlagEnabled()) {
+            return;
+        }
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.TOUCHPAD_VISUALIZER, enabled ? 1 : 0, UserHandle.USER_CURRENT);
+    }
+
+    /**
      * Returns true if the touchpad should allow tap dragging.
      *
      * The returned value only applies to gesture-compatible touchpads.
@@ -734,4 +794,141 @@
                 Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED, enabled ? 1 : 0,
                 UserHandle.USER_CURRENT);
     }
+
+    /**
+     * Whether "Repeat keys" feature flag is enabled.
+     *
+     * <p>
+     * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+     * key on the physical keyboard is held down. This accessibility feature allows the user
+     * to configure the timeout before the key repeats begin as well as the delay
+     * between successive key repeats.
+     * </p>
+     *
+     * @hide
+     */
+    public static boolean isRepeatKeysFeatureFlagEnabled() {
+        return keyboardRepeatKeys();
+    }
+
+    /**
+     * Get Accessibility repeat keys timeout duration in milliseconds.
+     * The default key repeat timeout is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_TIMEOUT_MS}.
+     *
+     * @param context The application context
+     * @return The time duration for which a key should be pressed after
+     *         which the pressed key will be repeated. The timeout must be between
+     *         {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+     *         {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+     *
+     * <p>
+     * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+     * key on the physical keyboard is held down. This accessibility feature allows the user
+     * to configure the timeout before the key repeats begin as well as the delay
+     * between successive key repeats.
+     * </p>
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+    public static int getAccessibilityRepeatKeysTimeout(@NonNull Context context) {
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(),
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Get Accessibility repeat keys delay rate in milliseconds.
+     * The default key repeat delay is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_DELAY_MS}.
+     *
+     * @param context The application context
+     * @return Time duration between successive key repeats when a key is
+     *         pressed down. The delay duration must be between
+     *         {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+     *         {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+     *
+     * <p>
+     * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+     * key on the physical keyboard is held down. This accessibility feature allows the user
+     * to configure the timeout before the key repeats begin as well as the delay
+     * between successive key repeats.
+     * </p>
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+    public static int getAccessibilityRepeatKeysDelay(@NonNull Context context) {
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(),
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Set Accessibility repeat keys timeout duration in milliseconds.
+     *
+     * @param timeoutTimeMillis time duration for which a key should be pressed after which the
+     *                          pressed key will be repeated. The timeout must be between
+     *                          {@link #MIN_KEY_REPEAT_TIMEOUT_MILLIS} and
+     *                          {@link #MAX_KEY_REPEAT_TIMEOUT_MILLIS}
+     *
+     *  <p>
+     * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+     * key on the physical keyboard is held down. This accessibility feature allows the user
+     * to configure the timeout before the key repeats begin as well as the delay
+     *  between successive key repeats.
+     * </p>
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setAccessibilityRepeatKeysTimeout(@NonNull Context context,
+            int timeoutTimeMillis) {
+        if (timeoutTimeMillis < MIN_KEY_REPEAT_TIMEOUT_MILLIS
+                || timeoutTimeMillis > MAX_KEY_REPEAT_TIMEOUT_MILLIS) {
+            throw new IllegalArgumentException(
+                    "Provided repeat keys timeout should be in range ("
+                            + MIN_KEY_REPEAT_TIMEOUT_MILLIS + ","
+                            + MAX_KEY_REPEAT_TIMEOUT_MILLIS + ")");
+        }
+        Settings.Secure.putIntForUser(context.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_TIMEOUT_MS, timeoutTimeMillis,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Set Accessibility repeat key delay duration in milliseconds.
+     *
+     * @param delayTimeMillis Time duration between successive key repeats when a key is
+     *                        pressed down. The delay duration must be between
+     *                        {@link #MIN_KEY_REPEAT_DELAY_MILLIS} and
+     *                        {@link #MAX_KEY_REPEAT_DELAY_MILLIS}
+     * <p>
+     * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular
+     * key on the physical keyboard is held down. This accessibility feature allows the user
+     * to configure the timeout before the key repeats begin as well as the delay
+     * between successive key repeats.
+     * </p>
+     *
+     * @hide
+     */
+    @TestApi
+    @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS)
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setAccessibilityRepeatKeysDelay(@NonNull Context context,
+            int delayTimeMillis) {
+        if (delayTimeMillis < MIN_KEY_REPEAT_DELAY_MILLIS
+                || delayTimeMillis > MAX_KEY_REPEAT_DELAY_MILLIS) {
+            throw new IllegalArgumentException(
+                    "Provided repeat keys delay should be in range ("
+                            + MIN_KEY_REPEAT_DELAY_MILLIS + ","
+                            + MAX_KEY_REPEAT_DELAY_MILLIS + ")");
+        }
+        Settings.Secure.putIntForUser(context.getContentResolver(),
+                Settings.Secure.KEY_REPEAT_DELAY_MS, delayTimeMillis,
+                UserHandle.USER_CURRENT);
+    }
 }
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
new file mode 100644
index 0000000..7a8dd33
--- /dev/null
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -0,0 +1,531 @@
+/*
+ * 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.hardware.input;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides information about the keyboard gesture event being triggered by an external keyboard.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public class KeyGestureEvent {
+
+    private final int mDeviceId;
+    @NonNull
+    private final int[] mKeycodes;
+    private final int mModifierState;
+    @KeyGestureType
+    private final int mKeyGestureType;
+
+
+    public static final int KEY_GESTURE_TYPE_UNSPECIFIED =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+    public static final int KEY_GESTURE_TYPE_HOME =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
+    public static final int KEY_GESTURE_TYPE_RECENT_APPS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
+    public static final int KEY_GESTURE_TYPE_BACK =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
+    public static final int KEY_GESTURE_TYPE_APP_SWITCH =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_TASKBAR =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
+    public static final int KEY_GESTURE_TYPE_TAKE_SCREENSHOT =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
+    public static final int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_UP =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
+    public static final int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
+    public static final int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
+    public static final int KEY_GESTURE_TYPE_VOLUME_UP =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
+    public static final int KEY_GESTURE_TYPE_VOLUME_DOWN =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
+    public static final int KEY_GESTURE_TYPE_VOLUME_MUTE =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
+    public static final int KEY_GESTURE_TYPE_ALL_APPS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_SEARCH =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
+    public static final int KEY_GESTURE_TYPE_LANGUAGE_SWITCH =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
+    public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
+    public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
+    public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
+    public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
+    public static final int KEY_GESTURE_TYPE_LOCK_SCREEN =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
+    public static final int KEY_GESTURE_TYPE_OPEN_NOTES =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_POWER =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
+    public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
+    public static final int KEY_GESTURE_TYPE_SLEEP =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
+    public static final int KEY_GESTURE_TYPE_WAKEUP =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
+    public static final int KEY_GESTURE_TYPE_MEDIA_KEY =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+    public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+    public static final int KEY_GESTURE_TYPE_DESKTOP_MODE =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
+    public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION =
+            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @IntDef(prefix = "KEY_GESTURE_TYPE_", value = {
+        KEY_GESTURE_TYPE_UNSPECIFIED,
+        KEY_GESTURE_TYPE_HOME,
+        KEY_GESTURE_TYPE_RECENT_APPS,
+        KEY_GESTURE_TYPE_BACK,
+        KEY_GESTURE_TYPE_APP_SWITCH,
+        KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+        KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+        KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+        KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+        KEY_GESTURE_TYPE_TOGGLE_TASKBAR,
+        KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+        KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+        KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+        KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+        KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+        KEY_GESTURE_TYPE_VOLUME_UP,
+        KEY_GESTURE_TYPE_VOLUME_DOWN,
+        KEY_GESTURE_TYPE_VOLUME_MUTE,
+        KEY_GESTURE_TYPE_ALL_APPS,
+        KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+        KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+        KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+        KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+        KEY_GESTURE_TYPE_SYSTEM_MUTE,
+        KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+        KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
+        KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+        KEY_GESTURE_TYPE_LOCK_SCREEN,
+        KEY_GESTURE_TYPE_OPEN_NOTES,
+        KEY_GESTURE_TYPE_TOGGLE_POWER,
+        KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+        KEY_GESTURE_TYPE_SLEEP,
+        KEY_GESTURE_TYPE_WAKEUP,
+        KEY_GESTURE_TYPE_MEDIA_KEY,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
+        KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
+        KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+        KEY_GESTURE_TYPE_DESKTOP_MODE,
+        KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface KeyGestureType {}
+
+    @DataClass.Generated.Member
+    public static String keyGestureTypeToString(@KeyGestureType int value) {
+        switch (value) {
+            case KEY_GESTURE_TYPE_UNSPECIFIED:
+                    return "KEY_GESTURE_TYPE_UNSPECIFIED";
+            case KEY_GESTURE_TYPE_HOME:
+                    return "KEY_GESTURE_TYPE_HOME";
+            case KEY_GESTURE_TYPE_RECENT_APPS:
+                    return "KEY_GESTURE_TYPE_RECENT_APPS";
+            case KEY_GESTURE_TYPE_BACK:
+                    return "KEY_GESTURE_TYPE_BACK";
+            case KEY_GESTURE_TYPE_APP_SWITCH:
+                    return "KEY_GESTURE_TYPE_APP_SWITCH";
+            case KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+                    return "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT";
+            case KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
+                    return "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT";
+            case KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+                    return "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS";
+            case KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+                    return "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL";
+            case KEY_GESTURE_TYPE_TOGGLE_TASKBAR:
+                    return "KEY_GESTURE_TYPE_TOGGLE_TASKBAR";
+            case KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+                    return "KEY_GESTURE_TYPE_TAKE_SCREENSHOT";
+            case KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+                    return "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER";
+            case KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+                    return "KEY_GESTURE_TYPE_BRIGHTNESS_UP";
+            case KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+                    return "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN";
+            case KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+                    return "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE";
+            case KEY_GESTURE_TYPE_VOLUME_UP:
+                    return "KEY_GESTURE_TYPE_VOLUME_UP";
+            case KEY_GESTURE_TYPE_VOLUME_DOWN:
+                    return "KEY_GESTURE_TYPE_VOLUME_DOWN";
+            case KEY_GESTURE_TYPE_VOLUME_MUTE:
+                    return "KEY_GESTURE_TYPE_VOLUME_MUTE";
+            case KEY_GESTURE_TYPE_ALL_APPS:
+                    return "KEY_GESTURE_TYPE_ALL_APPS";
+            case KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+                    return "KEY_GESTURE_TYPE_LAUNCH_SEARCH";
+            case KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+                    return "KEY_GESTURE_TYPE_LANGUAGE_SWITCH";
+            case KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+                    return "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS";
+            case KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+                    return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
+            case KEY_GESTURE_TYPE_SYSTEM_MUTE:
+                    return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
+            case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+                    return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
+            case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+                    return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
+            case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+                    return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
+            case KEY_GESTURE_TYPE_LOCK_SCREEN:
+                    return "KEY_GESTURE_TYPE_LOCK_SCREEN";
+            case KEY_GESTURE_TYPE_OPEN_NOTES:
+                    return "KEY_GESTURE_TYPE_OPEN_NOTES";
+            case KEY_GESTURE_TYPE_TOGGLE_POWER:
+                    return "KEY_GESTURE_TYPE_TOGGLE_POWER";
+            case KEY_GESTURE_TYPE_SYSTEM_NAVIGATION:
+                    return "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION";
+            case KEY_GESTURE_TYPE_SLEEP:
+                    return "KEY_GESTURE_TYPE_SLEEP";
+            case KEY_GESTURE_TYPE_WAKEUP:
+                    return "KEY_GESTURE_TYPE_WAKEUP";
+            case KEY_GESTURE_TYPE_MEDIA_KEY:
+                    return "KEY_GESTURE_TYPE_MEDIA_KEY";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
+            case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
+                    return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
+            case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+                    return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+            case KEY_GESTURE_TYPE_DESKTOP_MODE:
+                    return "KEY_GESTURE_TYPE_DESKTOP_MODE";
+            case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+                    return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    @DataClass.Generated.Member
+    public KeyGestureEvent(
+            int deviceId,
+            @NonNull int[] keycodes,
+            int modifierState,
+            @KeyGestureType int keyGestureType) {
+        this.mDeviceId = deviceId;
+        this.mKeycodes = keycodes;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mKeycodes);
+        this.mModifierState = modifierState;
+        this.mKeyGestureType = keyGestureType;
+
+        if (!(mKeyGestureType == KEY_GESTURE_TYPE_UNSPECIFIED)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_HOME)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_RECENT_APPS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_BACK)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_APP_SWITCH)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_ASSISTANT)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_TASKBAR)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TAKE_SCREENSHOT)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_UP)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_BRIGHTNESS_DOWN)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_UP)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_DOWN)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_VOLUME_MUTE)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_ALL_APPS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_SEARCH)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LANGUAGE_SWITCH)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_MUTE)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LOCK_SCREEN)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_OPEN_NOTES)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_TOGGLE_POWER)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_SYSTEM_NAVIGATION)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_SLEEP)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_WAKEUP)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_MEDIA_KEY)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_DESKTOP_MODE)
+                && !(mKeyGestureType == KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION)) {
+            throw new java.lang.IllegalArgumentException(
+                    "keyGestureType was " + mKeyGestureType + " but must be one of: "
+                            + "KEY_GESTURE_TYPE_UNSPECIFIED(" + KEY_GESTURE_TYPE_UNSPECIFIED + "), "
+                            + "KEY_GESTURE_TYPE_HOME(" + KEY_GESTURE_TYPE_HOME + "), "
+                            + "KEY_GESTURE_TYPE_RECENT_APPS(" + KEY_GESTURE_TYPE_RECENT_APPS + "), "
+                            + "KEY_GESTURE_TYPE_BACK(" + KEY_GESTURE_TYPE_BACK + "), "
+                            + "KEY_GESTURE_TYPE_APP_SWITCH(" + KEY_GESTURE_TYPE_APP_SWITCH + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT(" + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS(" + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + "), "
+                            + "KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL(" + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + "), "
+                            + "KEY_GESTURE_TYPE_TOGGLE_TASKBAR(" + KEY_GESTURE_TYPE_TOGGLE_TASKBAR + "), "
+                            + "KEY_GESTURE_TYPE_TAKE_SCREENSHOT(" + KEY_GESTURE_TYPE_TAKE_SCREENSHOT + "), "
+                            + "KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER(" + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + "), "
+                            + "KEY_GESTURE_TYPE_BRIGHTNESS_UP(" + KEY_GESTURE_TYPE_BRIGHTNESS_UP + "), "
+                            + "KEY_GESTURE_TYPE_BRIGHTNESS_DOWN(" + KEY_GESTURE_TYPE_BRIGHTNESS_DOWN + "), "
+                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP + "), "
+                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN + "), "
+                            + "KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE(" + KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE + "), "
+                            + "KEY_GESTURE_TYPE_VOLUME_UP(" + KEY_GESTURE_TYPE_VOLUME_UP + "), "
+                            + "KEY_GESTURE_TYPE_VOLUME_DOWN(" + KEY_GESTURE_TYPE_VOLUME_DOWN + "), "
+                            + "KEY_GESTURE_TYPE_VOLUME_MUTE(" + KEY_GESTURE_TYPE_VOLUME_MUTE + "), "
+                            + "KEY_GESTURE_TYPE_ALL_APPS(" + KEY_GESTURE_TYPE_ALL_APPS + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_SEARCH(" + KEY_GESTURE_TYPE_LAUNCH_SEARCH + "), "
+                            + "KEY_GESTURE_TYPE_LANGUAGE_SWITCH(" + KEY_GESTURE_TYPE_LANGUAGE_SWITCH + "), "
+                            + "KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS(" + KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS + "), "
+                            + "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK(" + KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK + "), "
+                            + "KEY_GESTURE_TYPE_SYSTEM_MUTE(" + KEY_GESTURE_TYPE_SYSTEM_MUTE + "), "
+                            + "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION(" + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION + "), "
+                            + "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS(" + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS + "), "
+                            + "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT(" + KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT + "), "
+                            + "KEY_GESTURE_TYPE_LOCK_SCREEN(" + KEY_GESTURE_TYPE_LOCK_SCREEN + "), "
+                            + "KEY_GESTURE_TYPE_OPEN_NOTES(" + KEY_GESTURE_TYPE_OPEN_NOTES + "), "
+                            + "KEY_GESTURE_TYPE_TOGGLE_POWER(" + KEY_GESTURE_TYPE_TOGGLE_POWER + "), "
+                            + "KEY_GESTURE_TYPE_SYSTEM_NAVIGATION(" + KEY_GESTURE_TYPE_SYSTEM_NAVIGATION + "), "
+                            + "KEY_GESTURE_TYPE_SLEEP(" + KEY_GESTURE_TYPE_SLEEP + "), "
+                            + "KEY_GESTURE_TYPE_WAKEUP(" + KEY_GESTURE_TYPE_WAKEUP + "), "
+                            + "KEY_GESTURE_TYPE_MEDIA_KEY(" + KEY_GESTURE_TYPE_MEDIA_KEY + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS(" + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS + "), "
+                            + "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME(" + KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME + "), "
+                            + "KEY_GESTURE_TYPE_DESKTOP_MODE(" + KEY_GESTURE_TYPE_DESKTOP_MODE + "), "
+                            + "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION(" + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ")");
+        }
+
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public int getDeviceId() {
+        return mDeviceId;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull int[] getKeycodes() {
+        return mKeycodes;
+    }
+
+    @DataClass.Generated.Member
+    public int getModifierState() {
+        return mModifierState;
+    }
+
+    @DataClass.Generated.Member
+    public @KeyGestureType int getKeyGestureType() {
+        return mKeyGestureType;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "KeyGestureEvent { " +
+                "deviceId = " + mDeviceId + ", " +
+                "keycodes = " + java.util.Arrays.toString(mKeycodes) + ", " +
+                "modifierState = " + mModifierState + ", " +
+                "keyGestureType = " + keyGestureTypeToString(mKeyGestureType) +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(KeyGestureEvent other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        KeyGestureEvent that = (KeyGestureEvent) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mDeviceId == that.mDeviceId
+                && java.util.Arrays.equals(mKeycodes, that.mKeycodes)
+                && mModifierState == that.mModifierState
+                && mKeyGestureType == that.mKeyGestureType;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mDeviceId;
+        _hash = 31 * _hash + java.util.Arrays.hashCode(mKeycodes);
+        _hash = 31 * _hash + mModifierState;
+        _hash = 31 * _hash + mKeyGestureType;
+        return _hash;
+    }
+
+    @DataClass.Generated(
+            time = 1723409092192L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/hardware/input/KeyGestureEvent.java",
+            inputSignatures = "private final  int mDeviceId\nprivate final @android.annotation.NonNull int[] mKeycodes\nprivate final  int mModifierState\nprivate final @android.hardware.input.KeyGestureEvent.KeyGestureType int mKeyGestureType\npublic static final  int KEY_GESTURE_TYPE_UNSPECIFIED\npublic static final  int KEY_GESTURE_TYPE_HOME\npublic static final  int KEY_GESTURE_TYPE_RECENT_APPS\npublic static final  int KEY_GESTURE_TYPE_BACK\npublic static final  int KEY_GESTURE_TYPE_APP_SWITCH\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_ASSISTANT\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_TASKBAR\npublic static final  int KEY_GESTURE_TYPE_TAKE_SCREENSHOT\npublic static final  int KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER\npublic static final  int KEY_GESTURE_TYPE_BRIGHTNESS_UP\npublic static final  int KEY_GESTURE_TYPE_BRIGHTNESS_DOWN\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN\npublic static final  int KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE\npublic static final  int KEY_GESTURE_TYPE_VOLUME_UP\npublic static final  int KEY_GESTURE_TYPE_VOLUME_DOWN\npublic static final  int KEY_GESTURE_TYPE_VOLUME_MUTE\npublic static final  int KEY_GESTURE_TYPE_ALL_APPS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_SEARCH\npublic static final  int KEY_GESTURE_TYPE_LANGUAGE_SWITCH\npublic static final  int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK\npublic static final  int KEY_GESTURE_TYPE_SYSTEM_MUTE\npublic static final  int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION\npublic static final  int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS\npublic static final  int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT\npublic static final  int KEY_GESTURE_TYPE_LOCK_SCREEN\npublic static final  int KEY_GESTURE_TYPE_OPEN_NOTES\npublic static final  int KEY_GESTURE_TYPE_TOGGLE_POWER\npublic static final  int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION\npublic static final  int KEY_GESTURE_TYPE_SLEEP\npublic static final  int KEY_GESTURE_TYPE_WAKEUP\npublic static final  int KEY_GESTURE_TYPE_MEDIA_KEY\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS\npublic static final  int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME\npublic static final  int KEY_GESTURE_TYPE_DESKTOP_MODE\npublic static final  int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION\nclass KeyGestureEvent extends java.lang.Object implements []\[email protected](genToString=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/hardware/input/KeyboardSystemShortcut.java b/core/java/android/hardware/input/KeyboardSystemShortcut.java
deleted file mode 100644
index 89cf877..0000000
--- a/core/java/android/hardware/input/KeyboardSystemShortcut.java
+++ /dev/null
@@ -1,522 +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 android.hardware.input;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.util.DataClass;
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Provides information about the keyboard shortcut being triggered by an external keyboard.
- *
- * @hide
- */
-@DataClass(genToString = true, genEqualsHashCode = true)
-public class KeyboardSystemShortcut {
-
-    private static final String TAG = "KeyboardSystemShortcut";
-
-    @NonNull
-    private final int[] mKeycodes;
-    private final int mModifierState;
-    @SystemShortcut
-    private final int mSystemShortcut;
-
-
-    public static final int SYSTEM_SHORTCUT_UNSPECIFIED =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
-    public static final int SYSTEM_SHORTCUT_HOME =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
-    public static final int SYSTEM_SHORTCUT_RECENT_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
-    public static final int SYSTEM_SHORTCUT_BACK =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
-    public static final int SYSTEM_SHORTCUT_APP_SWITCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_ASSISTANT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS;
-    public static final int SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL;
-    public static final int SYSTEM_SHORTCUT_TOGGLE_TASKBAR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR;
-    public static final int SYSTEM_SHORTCUT_TAKE_SCREENSHOT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT;
-    public static final int SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER;
-    public static final int SYSTEM_SHORTCUT_BRIGHTNESS_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP;
-    public static final int SYSTEM_SHORTCUT_BRIGHTNESS_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN;
-    public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP;
-    public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN;
-    public static final int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE;
-    public static final int SYSTEM_SHORTCUT_VOLUME_UP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP;
-    public static final int SYSTEM_SHORTCUT_VOLUME_DOWN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN;
-    public static final int SYSTEM_SHORTCUT_VOLUME_MUTE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE;
-    public static final int SYSTEM_SHORTCUT_ALL_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_SEARCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH;
-    public static final int SYSTEM_SHORTCUT_LANGUAGE_SWITCH =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH;
-    public static final int SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS;
-    public static final int SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
-    public static final int SYSTEM_SHORTCUT_SYSTEM_MUTE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
-    public static final int SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
-    public static final int SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
-    public static final int SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
-    public static final int SYSTEM_SHORTCUT_LOCK_SCREEN =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN;
-    public static final int SYSTEM_SHORTCUT_OPEN_NOTES =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES;
-    public static final int SYSTEM_SHORTCUT_TOGGLE_POWER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER;
-    public static final int SYSTEM_SHORTCUT_SYSTEM_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION;
-    public static final int SYSTEM_SHORTCUT_SLEEP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP;
-    public static final int SYSTEM_SHORTCUT_WAKEUP =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP;
-    public static final int SYSTEM_SHORTCUT_MEDIA_KEY =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
-    public static final int SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
-    public static final int SYSTEM_SHORTCUT_DESKTOP_MODE =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
-    public static final int SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION =
-            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
-
-
-
-    // Code below generated by codegen v1.0.23.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/input/KeyboardSystemShortcut.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    @IntDef(prefix = "SYSTEM_SHORTCUT_", value = {
-        SYSTEM_SHORTCUT_UNSPECIFIED,
-        SYSTEM_SHORTCUT_HOME,
-        SYSTEM_SHORTCUT_RECENT_APPS,
-        SYSTEM_SHORTCUT_BACK,
-        SYSTEM_SHORTCUT_APP_SWITCH,
-        SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
-        SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT,
-        SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS,
-        SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-        SYSTEM_SHORTCUT_TOGGLE_TASKBAR,
-        SYSTEM_SHORTCUT_TAKE_SCREENSHOT,
-        SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER,
-        SYSTEM_SHORTCUT_BRIGHTNESS_UP,
-        SYSTEM_SHORTCUT_BRIGHTNESS_DOWN,
-        SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP,
-        SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN,
-        SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE,
-        SYSTEM_SHORTCUT_VOLUME_UP,
-        SYSTEM_SHORTCUT_VOLUME_DOWN,
-        SYSTEM_SHORTCUT_VOLUME_MUTE,
-        SYSTEM_SHORTCUT_ALL_APPS,
-        SYSTEM_SHORTCUT_LAUNCH_SEARCH,
-        SYSTEM_SHORTCUT_LANGUAGE_SWITCH,
-        SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
-        SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK,
-        SYSTEM_SHORTCUT_SYSTEM_MUTE,
-        SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
-        SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS,
-        SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT,
-        SYSTEM_SHORTCUT_LOCK_SCREEN,
-        SYSTEM_SHORTCUT_OPEN_NOTES,
-        SYSTEM_SHORTCUT_TOGGLE_POWER,
-        SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
-        SYSTEM_SHORTCUT_SLEEP,
-        SYSTEM_SHORTCUT_WAKEUP,
-        SYSTEM_SHORTCUT_MEDIA_KEY,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER,
-        SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS,
-        SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
-        SYSTEM_SHORTCUT_DESKTOP_MODE,
-        SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @DataClass.Generated.Member
-    public @interface SystemShortcut {}
-
-    @DataClass.Generated.Member
-    public static String systemShortcutToString(@SystemShortcut int value) {
-        switch (value) {
-            case SYSTEM_SHORTCUT_UNSPECIFIED:
-                    return "SYSTEM_SHORTCUT_UNSPECIFIED";
-            case SYSTEM_SHORTCUT_HOME:
-                    return "SYSTEM_SHORTCUT_HOME";
-            case SYSTEM_SHORTCUT_RECENT_APPS:
-                    return "SYSTEM_SHORTCUT_RECENT_APPS";
-            case SYSTEM_SHORTCUT_BACK:
-                    return "SYSTEM_SHORTCUT_BACK";
-            case SYSTEM_SHORTCUT_APP_SWITCH:
-                    return "SYSTEM_SHORTCUT_APP_SWITCH";
-            case SYSTEM_SHORTCUT_LAUNCH_ASSISTANT:
-                    return "SYSTEM_SHORTCUT_LAUNCH_ASSISTANT";
-            case SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT:
-                    return "SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT";
-            case SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS:
-                    return "SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS";
-            case SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL:
-                    return "SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL";
-            case SYSTEM_SHORTCUT_TOGGLE_TASKBAR:
-                    return "SYSTEM_SHORTCUT_TOGGLE_TASKBAR";
-            case SYSTEM_SHORTCUT_TAKE_SCREENSHOT:
-                    return "SYSTEM_SHORTCUT_TAKE_SCREENSHOT";
-            case SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER:
-                    return "SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER";
-            case SYSTEM_SHORTCUT_BRIGHTNESS_UP:
-                    return "SYSTEM_SHORTCUT_BRIGHTNESS_UP";
-            case SYSTEM_SHORTCUT_BRIGHTNESS_DOWN:
-                    return "SYSTEM_SHORTCUT_BRIGHTNESS_DOWN";
-            case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP:
-                    return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP";
-            case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN:
-                    return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN";
-            case SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE:
-                    return "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE";
-            case SYSTEM_SHORTCUT_VOLUME_UP:
-                    return "SYSTEM_SHORTCUT_VOLUME_UP";
-            case SYSTEM_SHORTCUT_VOLUME_DOWN:
-                    return "SYSTEM_SHORTCUT_VOLUME_DOWN";
-            case SYSTEM_SHORTCUT_VOLUME_MUTE:
-                    return "SYSTEM_SHORTCUT_VOLUME_MUTE";
-            case SYSTEM_SHORTCUT_ALL_APPS:
-                    return "SYSTEM_SHORTCUT_ALL_APPS";
-            case SYSTEM_SHORTCUT_LAUNCH_SEARCH:
-                    return "SYSTEM_SHORTCUT_LAUNCH_SEARCH";
-            case SYSTEM_SHORTCUT_LANGUAGE_SWITCH:
-                    return "SYSTEM_SHORTCUT_LANGUAGE_SWITCH";
-            case SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS:
-                    return "SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS";
-            case SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK:
-                    return "SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK";
-            case SYSTEM_SHORTCUT_SYSTEM_MUTE:
-                    return "SYSTEM_SHORTCUT_SYSTEM_MUTE";
-            case SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION:
-                    return "SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION";
-            case SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS:
-                    return "SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS";
-            case SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT:
-                    return "SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT";
-            case SYSTEM_SHORTCUT_LOCK_SCREEN:
-                    return "SYSTEM_SHORTCUT_LOCK_SCREEN";
-            case SYSTEM_SHORTCUT_OPEN_NOTES:
-                    return "SYSTEM_SHORTCUT_OPEN_NOTES";
-            case SYSTEM_SHORTCUT_TOGGLE_POWER:
-                    return "SYSTEM_SHORTCUT_TOGGLE_POWER";
-            case SYSTEM_SHORTCUT_SYSTEM_NAVIGATION:
-                    return "SYSTEM_SHORTCUT_SYSTEM_NAVIGATION";
-            case SYSTEM_SHORTCUT_SLEEP:
-                    return "SYSTEM_SHORTCUT_SLEEP";
-            case SYSTEM_SHORTCUT_WAKEUP:
-                    return "SYSTEM_SHORTCUT_WAKEUP";
-            case SYSTEM_SHORTCUT_MEDIA_KEY:
-                    return "SYSTEM_SHORTCUT_MEDIA_KEY";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER";
-            case SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS:
-                    return "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS";
-            case SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
-                    return "SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
-            case SYSTEM_SHORTCUT_DESKTOP_MODE:
-                    return "SYSTEM_SHORTCUT_DESKTOP_MODE";
-            case SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION:
-                    return "SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION";
-            default: return Integer.toHexString(value);
-        }
-    }
-
-    @DataClass.Generated.Member
-    public KeyboardSystemShortcut(
-            @NonNull int[] keycodes,
-            int modifierState,
-            @SystemShortcut int systemShortcut) {
-        this.mKeycodes = keycodes;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mKeycodes);
-        this.mModifierState = modifierState;
-        this.mSystemShortcut = systemShortcut;
-
-        if (!(mSystemShortcut == SYSTEM_SHORTCUT_UNSPECIFIED)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_HOME)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_RECENT_APPS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_BACK)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_APP_SWITCH)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_ASSISTANT)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_TASKBAR)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TAKE_SCREENSHOT)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_BRIGHTNESS_UP)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_BRIGHTNESS_DOWN)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_UP)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_DOWN)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_VOLUME_MUTE)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_ALL_APPS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_SEARCH)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LANGUAGE_SWITCH)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_SYSTEM_MUTE)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LOCK_SCREEN)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_OPEN_NOTES)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_TOGGLE_POWER)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_SYSTEM_NAVIGATION)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_SLEEP)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_WAKEUP)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_MEDIA_KEY)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_DESKTOP_MODE)
-                && !(mSystemShortcut == SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION)) {
-            throw new java.lang.IllegalArgumentException(
-                    "systemShortcut was " + mSystemShortcut + " but must be one of: "
-                            + "SYSTEM_SHORTCUT_UNSPECIFIED(" + SYSTEM_SHORTCUT_UNSPECIFIED + "), "
-                            + "SYSTEM_SHORTCUT_HOME(" + SYSTEM_SHORTCUT_HOME + "), "
-                            + "SYSTEM_SHORTCUT_RECENT_APPS(" + SYSTEM_SHORTCUT_RECENT_APPS + "), "
-                            + "SYSTEM_SHORTCUT_BACK(" + SYSTEM_SHORTCUT_BACK + "), "
-                            + "SYSTEM_SHORTCUT_APP_SWITCH(" + SYSTEM_SHORTCUT_APP_SWITCH + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_ASSISTANT(" + SYSTEM_SHORTCUT_LAUNCH_ASSISTANT + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT(" + SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS(" + SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS + "), "
-                            + "SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL(" + SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL + "), "
-                            + "SYSTEM_SHORTCUT_TOGGLE_TASKBAR(" + SYSTEM_SHORTCUT_TOGGLE_TASKBAR + "), "
-                            + "SYSTEM_SHORTCUT_TAKE_SCREENSHOT(" + SYSTEM_SHORTCUT_TAKE_SCREENSHOT + "), "
-                            + "SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER(" + SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER + "), "
-                            + "SYSTEM_SHORTCUT_BRIGHTNESS_UP(" + SYSTEM_SHORTCUT_BRIGHTNESS_UP + "), "
-                            + "SYSTEM_SHORTCUT_BRIGHTNESS_DOWN(" + SYSTEM_SHORTCUT_BRIGHTNESS_DOWN + "), "
-                            + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP + "), "
-                            + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN + "), "
-                            + "SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE(" + SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE + "), "
-                            + "SYSTEM_SHORTCUT_VOLUME_UP(" + SYSTEM_SHORTCUT_VOLUME_UP + "), "
-                            + "SYSTEM_SHORTCUT_VOLUME_DOWN(" + SYSTEM_SHORTCUT_VOLUME_DOWN + "), "
-                            + "SYSTEM_SHORTCUT_VOLUME_MUTE(" + SYSTEM_SHORTCUT_VOLUME_MUTE + "), "
-                            + "SYSTEM_SHORTCUT_ALL_APPS(" + SYSTEM_SHORTCUT_ALL_APPS + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_SEARCH(" + SYSTEM_SHORTCUT_LAUNCH_SEARCH + "), "
-                            + "SYSTEM_SHORTCUT_LANGUAGE_SWITCH(" + SYSTEM_SHORTCUT_LANGUAGE_SWITCH + "), "
-                            + "SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS(" + SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS + "), "
-                            + "SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK(" + SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK + "), "
-                            + "SYSTEM_SHORTCUT_SYSTEM_MUTE(" + SYSTEM_SHORTCUT_SYSTEM_MUTE + "), "
-                            + "SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION(" + SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION + "), "
-                            + "SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS(" + SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS + "), "
-                            + "SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT(" + SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT + "), "
-                            + "SYSTEM_SHORTCUT_LOCK_SCREEN(" + SYSTEM_SHORTCUT_LOCK_SCREEN + "), "
-                            + "SYSTEM_SHORTCUT_OPEN_NOTES(" + SYSTEM_SHORTCUT_OPEN_NOTES + "), "
-                            + "SYSTEM_SHORTCUT_TOGGLE_POWER(" + SYSTEM_SHORTCUT_TOGGLE_POWER + "), "
-                            + "SYSTEM_SHORTCUT_SYSTEM_NAVIGATION(" + SYSTEM_SHORTCUT_SYSTEM_NAVIGATION + "), "
-                            + "SYSTEM_SHORTCUT_SLEEP(" + SYSTEM_SHORTCUT_SLEEP + "), "
-                            + "SYSTEM_SHORTCUT_WAKEUP(" + SYSTEM_SHORTCUT_WAKEUP + "), "
-                            + "SYSTEM_SHORTCUT_MEDIA_KEY(" + SYSTEM_SHORTCUT_MEDIA_KEY + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS(" + SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS + "), "
-                            + "SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME(" + SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME + "), "
-                            + "SYSTEM_SHORTCUT_DESKTOP_MODE(" + SYSTEM_SHORTCUT_DESKTOP_MODE + "), "
-                            + "SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION(" + SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION + ")");
-        }
-
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public @NonNull int[] getKeycodes() {
-        return mKeycodes;
-    }
-
-    @DataClass.Generated.Member
-    public int getModifierState() {
-        return mModifierState;
-    }
-
-    @DataClass.Generated.Member
-    public @SystemShortcut int getSystemShortcut() {
-        return mSystemShortcut;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public String toString() {
-        // You can override field toString logic by defining methods like:
-        // String fieldNameToString() { ... }
-
-        return "KeyboardSystemShortcut { " +
-                "keycodes = " + java.util.Arrays.toString(mKeycodes) + ", " +
-                "modifierState = " + mModifierState + ", " +
-                "systemShortcut = " + systemShortcutToString(mSystemShortcut) +
-        " }";
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public boolean equals(@Nullable Object o) {
-        // You can override field equality logic by defining either of the methods like:
-        // boolean fieldNameEquals(KeyboardSystemShortcut other) { ... }
-        // boolean fieldNameEquals(FieldType otherValue) { ... }
-
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        @SuppressWarnings("unchecked")
-        KeyboardSystemShortcut that = (KeyboardSystemShortcut) o;
-        //noinspection PointlessBooleanExpression
-        return true
-                && java.util.Arrays.equals(mKeycodes, that.mKeycodes)
-                && mModifierState == that.mModifierState
-                && mSystemShortcut == that.mSystemShortcut;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int hashCode() {
-        // You can override field hashCode logic by defining methods like:
-        // int fieldNameHashCode() { ... }
-
-        int _hash = 1;
-        _hash = 31 * _hash + java.util.Arrays.hashCode(mKeycodes);
-        _hash = 31 * _hash + mModifierState;
-        _hash = 31 * _hash + mSystemShortcut;
-        return _hash;
-    }
-
-    @DataClass.Generated(
-            time = 1722890917041L,
-            codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/hardware/input/KeyboardSystemShortcut.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull int[] mKeycodes\nprivate final  int mModifierState\nprivate final @android.hardware.input.KeyboardSystemShortcut.SystemShortcut int mSystemShortcut\npublic static final  int SYSTEM_SHORTCUT_UNSPECIFIED\npublic static final  int SYSTEM_SHORTCUT_HOME\npublic static final  int SYSTEM_SHORTCUT_RECENT_APPS\npublic static final  int SYSTEM_SHORTCUT_BACK\npublic static final  int SYSTEM_SHORTCUT_APP_SWITCH\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_ASSISTANT\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS\npublic static final  int SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL\npublic static final  int SYSTEM_SHORTCUT_TOGGLE_TASKBAR\npublic static final  int SYSTEM_SHORTCUT_TAKE_SCREENSHOT\npublic static final  int SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER\npublic static final  int SYSTEM_SHORTCUT_BRIGHTNESS_UP\npublic static final  int SYSTEM_SHORTCUT_BRIGHTNESS_DOWN\npublic static final  int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP\npublic static final  int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN\npublic static final  int SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE\npublic static final  int SYSTEM_SHORTCUT_VOLUME_UP\npublic static final  int SYSTEM_SHORTCUT_VOLUME_DOWN\npublic static final  int SYSTEM_SHORTCUT_VOLUME_MUTE\npublic static final  int SYSTEM_SHORTCUT_ALL_APPS\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_SEARCH\npublic static final  int SYSTEM_SHORTCUT_LANGUAGE_SWITCH\npublic static final  int SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS\npublic static final  int SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK\npublic static final  int SYSTEM_SHORTCUT_SYSTEM_MUTE\npublic static final  int SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION\npublic static final  int SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS\npublic static final  int SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT\npublic static final  int SYSTEM_SHORTCUT_LOCK_SCREEN\npublic static final  int SYSTEM_SHORTCUT_OPEN_NOTES\npublic static final  int SYSTEM_SHORTCUT_TOGGLE_POWER\npublic static final  int SYSTEM_SHORTCUT_SYSTEM_NAVIGATION\npublic static final  int SYSTEM_SHORTCUT_SLEEP\npublic static final  int SYSTEM_SHORTCUT_WAKEUP\npublic static final  int SYSTEM_SHORTCUT_MEDIA_KEY\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS\npublic static final  int SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME\npublic static final  int SYSTEM_SHORTCUT_DESKTOP_MODE\npublic static final  int SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION\nclass KeyboardSystemShortcut extends java.lang.Object implements []\[email protected](genToString=true, genEqualsHashCode=true)")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 6f1d63d8..fcd6c31 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -92,3 +92,28 @@
   description: "Dump keyboard shortcuts in dumpsys window"
   bug: "351963350"
 }
+
+flag {
+  name: "modifier_shortcut_manager_refactor"
+  namespace: "input"
+  description: "Refactor ModifierShortcutManager internal representation of shortcuts."
+  bug: "358603902"
+}
+
+flag {
+    namespace: "input_native"
+    name: "manage_key_gestures"
+    description: "Manage key gestures through Input APIs"
+    is_exported: true
+    bug: "358569822"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+  name: "keyboard_repeat_keys"
+  namespace: "input_native"
+  description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
+  bug: "336585002"
+}
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 3a58993..218b023 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -256,6 +256,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int[] getContextHubHandles() {
+        if (Flags.removeOldContextHubApis()) {
+            return null;
+        }
+
         try {
             return mService.getContextHubHandles();
         } catch (RemoteException e) {
@@ -277,6 +281,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public ContextHubInfo getContextHubInfo(int hubHandle) {
+        if (Flags.removeOldContextHubApis()) {
+            return null;
+        }
+
         try {
             return mService.getContextHubInfo(hubHandle);
         } catch (RemoteException e) {
@@ -308,6 +316,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         try {
             return mService.loadNanoApp(hubHandle, app);
         } catch (RemoteException e) {
@@ -335,6 +347,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int unloadNanoApp(int nanoAppHandle) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         try {
             return mService.unloadNanoApp(nanoAppHandle);
         } catch (RemoteException e) {
@@ -375,6 +391,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
+        if (Flags.removeOldContextHubApis()) {
+            return null;
+        }
+
         try {
             return mService.getNanoAppInstanceInfo(nanoAppHandle);
         } catch (RemoteException e) {
@@ -398,6 +418,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
+        if (Flags.removeOldContextHubApis()) {
+            return null;
+        }
+
         try {
             return mService.findNanoAppOnHub(hubHandle, filter);
         } catch (RemoteException e) {
@@ -433,6 +457,10 @@
     @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
     public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         try {
             return mService.sendMessage(hubHandle, nanoAppHandle, message);
         } catch (RemoteException e) {
@@ -648,6 +676,10 @@
     @Deprecated
     @SuppressLint("RequiresPermission")
     public int registerCallback(@NonNull Callback callback) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         return registerCallback(callback, null);
     }
 
@@ -657,6 +689,10 @@
      */
     @Deprecated
     public int registerCallback(ICallback callback) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         if (mLocalCallback != null) {
             Log.w(TAG, "Max number of local callbacks reached!");
             return -1;
@@ -682,6 +718,10 @@
     @Deprecated
     @SuppressLint("RequiresPermission")
     public int registerCallback(Callback callback, Handler handler) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         synchronized(this) {
             if (mCallback != null) {
                 Log.w(TAG, "Max number of callbacks reached!");
@@ -1041,16 +1081,20 @@
     @SuppressLint("RequiresPermission")
     @Deprecated
     public int unregisterCallback(@NonNull Callback callback) {
-      synchronized(this) {
-          if (callback != mCallback) {
-              Log.w(TAG, "Cannot recognize callback!");
-              return -1;
-          }
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
 
-          mCallback = null;
-          mCallbackHandler = null;
-      }
-      return 0;
+        synchronized (this) {
+            if (callback != mCallback) {
+                Log.w(TAG, "Cannot recognize callback!");
+                return -1;
+            }
+
+            mCallback = null;
+            mCallbackHandler = null;
+        }
+        return 0;
     }
 
     /**
@@ -1059,6 +1103,10 @@
      */
     @Deprecated
     public synchronized int unregisterCallback(ICallback callback) {
+        if (Flags.removeOldContextHubApis()) {
+            return -1;
+        }
+
         if (callback != mLocalCallback) {
             Log.w(TAG, "Cannot recognize local callback!");
             return -1;
diff --git a/core/java/android/hardware/radio/flags.aconfig b/core/java/android/hardware/radio/flags.aconfig
index c9ab62d..c99dc04 100644
--- a/core/java/android/hardware/radio/flags.aconfig
+++ b/core/java/android/hardware/radio/flags.aconfig
@@ -8,3 +8,11 @@
     description: "Feature flag for improved HD radio support with less vendor extensions"
     bug: "280300929"
 }
+
+flag {
+    name: "hd_radio_emergency_alert_system"
+    is_exported: true
+    namespace: "car_framework"
+    description: "Feature flag for HD radio emergency alert system support"
+    bug: "361348719"
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index bfff4db..9f3e3ad 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -29,6 +29,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.ElapsedRealtimeLong;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -874,10 +875,9 @@
     /*****************************************************************************
      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
      * patterns.
-     *
-     * @hide
      ****************************************************************************/
-    public static class GenericSoundModel extends SoundModel implements Parcelable {
+    @FlaggedApi(android.media.soundtrigger.Flags.FLAG_GENERIC_MODEL_API)
+    public static final class GenericSoundModel extends SoundModel implements Parcelable {
 
         public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR
                 = new Parcelable.Creator<GenericSoundModel>() {
@@ -890,11 +890,26 @@
             }
         };
 
+        /**
+         * Constructor for {@link GenericSoundModel} with version.
+         *
+         * @param uuid Unique identifier for this sound model.
+         * @param vendorUuid Unique vendor identifier for this sound model.
+         * @param data Opaque data for this sound model.
+         * @param version Vendor-specific version number of this sound model.
+         */
         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
                 @Nullable byte[] data, int version) {
             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
         }
 
+        /**
+         * Constructor for {@link GenericSoundModel} without version. The version is set to -1.
+         *
+         * @param uuid Unique identifier for this sound model.
+         * @param vendorUuid Unique vendor identifier for this sound model.
+         * @param data Opaque data for this sound model.
+         */
         @UnsupportedAppUsage
         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
                 @Nullable byte[] data) {
@@ -919,7 +934,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeString(getUuid().toString());
             if (getVendorUuid() == null) {
                 dest.writeInt(-1);
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index de39847..49e2358 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -58,8 +58,6 @@
 import static android.view.inputmethod.Flags.ctrlShiftShortcut;
 import static android.view.inputmethod.Flags.predictiveBackIme;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
 import android.annotation.AnyThread;
 import android.annotation.CallSuper;
 import android.annotation.DrawableRes;
@@ -500,36 +498,53 @@
     public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
 
     /**
-     * Enum flag to be used for {@link #setBackDisposition(int)}.
+     * The disposition mode that indicates the expected affordance for the back button.
      *
      * @hide
      */
-    @Retention(SOURCE)
-    @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
-            BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
-            prefix = "BACK_DISPOSITION_")
+    @IntDef(prefix = { "BACK_DISPOSITION_" }, value = {
+            BACK_DISPOSITION_DEFAULT,
+            BACK_DISPOSITION_WILL_NOT_DISMISS,
+            BACK_DISPOSITION_WILL_DISMISS,
+            BACK_DISPOSITION_ADJUST_NOTHING,
+    })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface BackDispositionMode {}
 
     /**
+     * The IME is active, and ready to accept touch/key events. It may or may not be visible.
+     *
      * @hide
-     * The IME is active.  It may or may not be visible.
      */
-    public static final int IME_ACTIVE = 0x1;
+    public static final int IME_ACTIVE = 1 << 0;
 
     /**
-     * @hide
      * The IME is perceptibly visible to the user.
+     *
+     * @hide
      */
-    public static final int IME_VISIBLE = 0x2;
+    public static final int IME_VISIBLE = 1 << 1;
 
     /**
-     * @hide
      * The IME is visible, but not yet perceptible to the user (e.g. fading in)
      * by {@link android.view.WindowInsetsController}.
      *
      * @see InputMethodManager#reportPerceptible
+     * @hide
      */
-    public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4;
+    public static final int IME_VISIBLE_IMPERCEPTIBLE = 1 << 2;
+
+    /**
+     * The IME window visibility state.
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "IME_" }, value = {
+            IME_ACTIVE,
+            IME_VISIBLE,
+            IME_VISIBLE_IMPERCEPTIBLE,
+    })
+    public @interface ImeWindowVisibility {}
 
     // Min and max values for back disposition.
     private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
@@ -649,9 +664,14 @@
     
     int mStatusIcon;
 
+    /** Latest reported value of back disposition mode. */
     @BackDispositionMode
     int mBackDisposition;
 
+    /** Latest reported value of IME window visibility state. */
+    @ImeWindowVisibility
+    private int mImeWindowVisibility;
+
     private Object mLock = new Object();
     @GuardedBy("mLock")
     private boolean mNotifyUserActionSent;
@@ -1032,7 +1052,7 @@
                 ImeTracker.forLogging().onFailed(statsToken,
                         ImeTracker.PHASE_IME_ON_SHOW_SOFT_INPUT_TRUE);
             }
-            setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+            setImeWindowVisibility(computeImeWindowVis());
 
             final boolean isVisible = isInputViewShown();
             final boolean visibilityChanged = isVisible != wasVisible;
@@ -1342,8 +1362,22 @@
         mImeSurfaceRemoverRunnable = null;
     }
 
-    private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
-        mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
+    /**
+     * Sets the IME window visibility state.
+     *
+     * @param vis the IME window visibility state to be set.
+     */
+    private void setImeWindowVisibility(@ImeWindowVisibility int vis) {
+        if (vis == mImeWindowVisibility) {
+            return;
+        }
+        mImeWindowVisibility = vis;
+        setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
+    }
+
+    private void setImeWindowStatus(@ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition) {
+        mPrivOps.setImeWindowStatusAsync(vis, backDisposition);
     }
 
     /** Set region of the keyboard to be avoided from back gesture */
@@ -1970,7 +2004,7 @@
             }
             // If user uses hard keyboard, IME button should always be shown.
             boolean showing = onEvaluateInputViewShown();
-            setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
+            setImeWindowVisibility(IME_ACTIVE | (showing ? IME_VISIBLE : 0));
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
@@ -2037,7 +2071,7 @@
             return;
         }
         mBackDisposition = disposition;
-        setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+        setImeWindowStatus(mImeWindowVisibility, mBackDisposition);
     }
 
     /**
@@ -3116,14 +3150,8 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
         mDecorViewWasVisible = mDecorViewVisible;
         mInShowWindow = true;
-        final int previousImeWindowStatus =
-                (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
-                        ? (!mWindowVisible ? -1 : IME_VISIBLE) : 0);
         startViews(prepareWindow(showInput));
-        final int nextImeWindowStatus = mapToImeWindowStatus();
-        if (previousImeWindowStatus != nextImeWindowStatus) {
-            setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
-        }
+        setImeWindowVisibility(computeImeWindowVis());
 
         mNavigationBarController.onWindowShown();
         // compute visibility
@@ -3301,7 +3329,7 @@
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_HIDE_WINDOW);
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
                 null /* icProto */);
-        setImeWindowStatus(0, mBackDisposition);
+        setImeWindowVisibility(0 /* vis */);
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
             // The ImeInsetsSourceProvider need the statsToken when dispatching the control. We
             // send the token here, so that another request in the provider can be cancelled.
@@ -4476,9 +4504,10 @@
         };
     }
 
-    private int mapToImeWindowStatus() {
-        return IME_ACTIVE
-                | (isInputViewShown() ? IME_VISIBLE : 0);
+    /** Computes the IME window visibility state. */
+    @ImeWindowVisibility
+    private int computeImeWindowVis() {
+        return IME_ACTIVE | (isInputViewShown() ? IME_VISIBLE : 0);
     }
 
     /**
diff --git a/core/java/android/os/ArtModuleServiceManager.java b/core/java/android/os/ArtModuleServiceManager.java
index e0b631d..995094b 100644
--- a/core/java/android/os/ArtModuleServiceManager.java
+++ b/core/java/android/os/ArtModuleServiceManager.java
@@ -37,10 +37,12 @@
     /** A class that exposes the method to obtain each system service. */
     public static final class ServiceRegisterer {
         @NonNull private final String mServiceName;
+        private final boolean mRetry;
 
         /** @hide */
-        public ServiceRegisterer(@NonNull String serviceName) {
+        public ServiceRegisterer(@NonNull String serviceName, boolean retry) {
             mServiceName = serviceName;
+            mRetry = retry;
         }
 
         /**
@@ -53,27 +55,47 @@
          */
         @Nullable
         public IBinder waitForService() {
-            return ServiceManager.waitForService(mServiceName);
+            if (mRetry) {
+                return ServiceManager.waitForService(mServiceName);
+            }
+            IBinder binder = ServiceManager.getService(mServiceName);
+            for (int remainingTimeMs = 5000; binder == null && remainingTimeMs > 0;
+                    remainingTimeMs -= 100) {
+                // There can be a race:
+                // 1. Client A invokes "ctl.start", which starts the service.
+                // 2. Client A gets a service handle from `ServiceManager.getService`.
+                // 3. Client B invokes "ctl.start", which does nothing because the service is
+                //    already running.
+                // 4. Client A drops the service handle. The service is notified that there is no
+                //    more client at that point, so it shuts down itself.
+                // 5. Client B cannot get a service handle from `ServiceManager.getService` because
+                //    the service is shut down.
+                // To address this problem, we invoke "ctl.start" repeatedly.
+                SystemProperties.set("ctl.start", mServiceName);
+                SystemClock.sleep(100);
+                binder = ServiceManager.getService(mServiceName);
+            }
+            return binder;
         }
     }
 
     /** Returns {@link ServiceRegisterer} for the "artd" service. */
     @NonNull
     public ServiceRegisterer getArtdServiceRegisterer() {
-        return new ServiceRegisterer("artd");
+        return new ServiceRegisterer("artd", true /* retry */);
     }
 
     /** Returns {@link ServiceRegisterer} for the "artd_pre_reboot" service. */
     @NonNull
     @FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
     public ServiceRegisterer getArtdPreRebootServiceRegisterer() {
-        return new ServiceRegisterer("artd_pre_reboot");
+        return new ServiceRegisterer("artd_pre_reboot", false /* retry */);
     }
 
     /** Returns {@link ServiceRegisterer} for the "dexopt_chroot_setup" service. */
     @NonNull
     @FlaggedApi(Flags.FLAG_USE_ART_SERVICE_V2)
     public ServiceRegisterer getDexoptChrootSetupServiceRegisterer() {
-        return new ServiceRegisterer("dexopt_chroot_setup");
+        return new ServiceRegisterer("dexopt_chroot_setup", true /* retry */);
     }
 }
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 623196b..492b825 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -71,7 +71,7 @@
             POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public static @interface PowerComponent {
+    public @interface PowerComponent {
     }
 
     public static final int POWER_COMPONENT_ANY = -1;
@@ -132,6 +132,16 @@
     }
 
     /**
+     * An integer that is either one of @PowerComponent constants or a custom component ID
+     * between FIRST_CUSTOM_POWER_COMPONENT_ID and LAST_CUSTOM_POWER_COMPONENT_ID.
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PowerComponentId {
+    }
+
+    /**
      * Identifiers of models used for power estimation.
      *
      * @hide
@@ -178,8 +188,8 @@
     public @interface ProcessState {
     }
 
+    public static final int PROCESS_STATE_ANY = -1;
     public static final int PROCESS_STATE_UNSPECIFIED = 0;
-    public static final int PROCESS_STATE_ANY = PROCESS_STATE_UNSPECIFIED;
     public static final int PROCESS_STATE_FOREGROUND = 1;
     public static final int PROCESS_STATE_BACKGROUND = 2;
     public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
@@ -216,16 +226,14 @@
     };
 
     static final int COLUMN_INDEX_BATTERY_CONSUMER_TYPE = 0;
-    static final int COLUMN_COUNT = 1;
-
     /**
      * Identifiers of consumed power aggregations per SCREEN state.
      *
      * @hide
      */
     @IntDef(prefix = {"SCREEN_STATE_"}, value = {
-            SCREEN_STATE_UNSPECIFIED,
             SCREEN_STATE_ANY,
+            SCREEN_STATE_UNSPECIFIED,
             SCREEN_STATE_ON,
             SCREEN_STATE_OTHER,
     })
@@ -233,8 +241,10 @@
     public @interface ScreenState {
     }
 
+    static final int COLUMN_COUNT = 1;
+
+    public static final int SCREEN_STATE_ANY = 0;
     public static final int SCREEN_STATE_UNSPECIFIED = 0;
-    public static final int SCREEN_STATE_ANY = SCREEN_STATE_UNSPECIFIED;
     public static final int SCREEN_STATE_ON = 1;
     public static final int SCREEN_STATE_OTHER = 2;  // Off, doze etc
 
@@ -255,8 +265,8 @@
      * @hide
      */
     @IntDef(prefix = {"POWER_STATE_"}, value = {
-            POWER_STATE_UNSPECIFIED,
             POWER_STATE_ANY,
+            POWER_STATE_UNSPECIFIED,
             POWER_STATE_BATTERY,
             POWER_STATE_OTHER,
     })
@@ -264,8 +274,8 @@
     public @interface PowerState {
     }
 
+    public static final int POWER_STATE_ANY = 0;
     public static final int POWER_STATE_UNSPECIFIED = 0;
-    public static final int POWER_STATE_ANY = POWER_STATE_UNSPECIFIED;
     public static final int POWER_STATE_BATTERY = 1;
     public static final int POWER_STATE_OTHER = 2;   // Plugged in, or on wireless charger, etc.
 
@@ -284,18 +294,18 @@
      * Identifies power attribution dimensions that a caller is interested in.
      */
     public static final class Dimensions {
-        public final @PowerComponent int powerComponent;
+        public final @PowerComponentId int powerComponentId;
         public final @ProcessState int processState;
         public final @ScreenState int screenState;
         public final @PowerState int powerState;
 
-        public Dimensions(@PowerComponent int powerComponent, @ProcessState int processState) {
-            this(powerComponent, processState, SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
+        public Dimensions(@PowerComponentId int powerComponentId, @ProcessState int processState) {
+            this(powerComponentId, processState, SCREEN_STATE_ANY, POWER_STATE_ANY);
         }
 
-        public Dimensions(@PowerComponent int powerComponent, int processState,
+        public Dimensions(@PowerComponentId int powerComponentId, int processState,
                 @ScreenState int screenState, @PowerState int powerState) {
-            this.powerComponent = powerComponent;
+            this.powerComponentId = powerComponentId;
             this.processState = processState;
             this.screenState = screenState;
             this.powerState = powerState;
@@ -305,11 +315,16 @@
         public String toString() {
             boolean dimensionSpecified = false;
             StringBuilder sb = new StringBuilder();
-            if (powerComponent != POWER_COMPONENT_ANY) {
-                sb.append("powerComponent=").append(sPowerComponentNames[powerComponent]);
+            if (powerComponentId != POWER_COMPONENT_ANY) {
+                sb.append("powerComponent=");
+                if (powerComponentId < POWER_COMPONENT_COUNT) {
+                    sb.append(sPowerComponentNames[powerComponentId]);
+                } else {
+                    sb.append("CUSTOM/").append(powerComponentId);
+                }
                 dimensionSpecified = true;
             }
-            if (processState != PROCESS_STATE_UNSPECIFIED) {
+            if (processState != PROCESS_STATE_ANY) {
                 if (dimensionSpecified) {
                     sb.append(", ");
                 }
@@ -353,7 +368,7 @@
      * in the same BatteryUsageStats.
      */
     public static final class Key {
-        public final @PowerComponent int powerComponent;
+        public final @PowerComponentId int powerComponentId;
         public final @ProcessState int processState;
         public final @ScreenState int screenState;
         public final @PowerState int powerState;
@@ -362,10 +377,10 @@
         final int mPowerColumnIndex;
         final int mDurationColumnIndex;
 
-        private Key(@PowerComponent int powerComponent, @ProcessState int processState,
+        private Key(@PowerComponentId int powerComponentId, @ProcessState int processState,
                 @ScreenState int screenState, @PowerState int powerState, int powerModelColumnIndex,
                 int powerColumnIndex, int durationColumnIndex) {
-            this.powerComponent = powerComponent;
+            this.powerComponentId = powerComponentId;
             this.processState = processState;
             this.screenState = screenState;
             this.powerState = powerState;
@@ -379,9 +394,13 @@
          * Returns true if this key should be included in an enumeration parameterized with
          * the supplied dimensions.
          */
-        boolean matches(@PowerComponent int powerComponent, @ProcessState int processState,
+        boolean matches(@PowerComponentId int powerComponent, @ProcessState int processState,
                 @ScreenState int screenState, @PowerState int powerState) {
-            if (powerComponent != POWER_COMPONENT_ANY && this.powerComponent != powerComponent) {
+            if (powerComponent != POWER_COMPONENT_ANY && this.powerComponentId != powerComponent) {
+                return false;
+            }
+            if (this.processState == PROCESS_STATE_UNSPECIFIED) {
+                // PROCESS_STATE_UNSPECIFIED is used for storing a precomputed total
                 return false;
             }
             if (processState != PROCESS_STATE_ANY && this.processState != processState) {
@@ -401,7 +420,7 @@
         public boolean equals(Object o) {
             // Skipping null and class check for performance
             final Key key = (Key) o;
-            return powerComponent == key.powerComponent
+            return powerComponentId == key.powerComponentId
                     && processState == key.processState
                     && screenState == key.screenState
                     && powerState == key.powerState;
@@ -409,7 +428,7 @@
 
         @Override
         public int hashCode() {
-            int result = powerComponent;
+            int result = powerComponentId;
             result = 31 * result + processState;
             result = 31 * result + screenState;
             result = 31 * result + powerState;
@@ -419,11 +438,15 @@
         /**
          * Returns a string suitable for use in dumpsys.
          */
-        public static String toString(@PowerComponent int powerComponent,
+        public static String toString(@PowerComponentId int powerComponent,
                 @ProcessState int processState, @ScreenState int screenState,
                 @PowerState int powerState) {
             StringBuilder sb = new StringBuilder();
-            sb.append(powerComponentIdToString(powerComponent));
+            if (powerComponent < POWER_COMPONENT_COUNT) {
+                sb.append(powerComponentIdToString(powerComponent));
+            } else {
+                sb.append("CUSTOM/").append(powerComponent);
+            }
             if (processState != PROCESS_STATE_UNSPECIFIED) {
                 sb.append(':');
                 sb.append(processStateToString(processState));
@@ -441,7 +464,7 @@
 
         @Override
         public String toString() {
-            return toString(powerComponent, processState, screenState, powerState);
+            return toString(powerComponentId, processState, screenState, powerState);
         }
     }
 
@@ -459,6 +482,13 @@
     }
 
     /**
+     * Returns the name of the specified power component, e.g. "CPU", "GPU" etc.
+     */
+    public String getPowerComponentName(@PowerComponentId int powerComponent) {
+        return mData.layout.getPowerComponentName(powerComponent);
+    }
+
+    /**
      * Total power consumed by this consumer, in mAh.
      */
     public double getConsumedPower() {
@@ -480,10 +510,18 @@
     }
 
     /**
+     * Returns indexes of all included power components.
+     */
+    @PowerComponentId
+    public int[] getPowerComponentIds() {
+        return mData.layout.powerComponentIds;
+    }
+
+    /**
      * Returns keys for various power values attributed to the specified component
      * held by this BatteryUsageStats object.
      */
-    public Key[] getKeys(@PowerComponent int componentId) {
+    public Key[] getKeys(@PowerComponentId int componentId) {
         return mData.layout.getKeys(componentId);
     }
 
@@ -491,7 +529,7 @@
      * Returns the key for the power attributed to the specified component,
      * for all values of other dimensions such as process state.
      */
-    public Key getKey(@PowerComponent int componentId) {
+    public Key getKey(@PowerComponentId int componentId) {
         return mData.layout.getKey(componentId, PROCESS_STATE_UNSPECIFIED, SCREEN_STATE_UNSPECIFIED,
                 POWER_STATE_UNSPECIFIED);
     }
@@ -499,7 +537,7 @@
     /**
      * Returns the key for the power attributed to the specified component and process state.
      */
-    public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
+    public Key getKey(@PowerComponentId int componentId, @ProcessState int processState) {
         return mData.layout.getKey(componentId, processState, SCREEN_STATE_UNSPECIFIED,
                 POWER_STATE_UNSPECIFIED);
     }
@@ -511,9 +549,9 @@
      *                    {@link BatteryConsumer#POWER_COMPONENT_CPU}.
      * @return Amount of consumed power in mAh.
      */
-    public double getConsumedPower(@PowerComponent int componentId) {
-        return mPowerComponents.getConsumedPower(componentId, PROCESS_STATE_UNSPECIFIED,
-                        SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
+    public double getConsumedPower(@PowerComponentId int componentId) {
+        return mPowerComponents.getConsumedPower(componentId, PROCESS_STATE_ANY,
+                        SCREEN_STATE_ANY, POWER_STATE_ANY);
     }
 
     /**
@@ -533,7 +571,7 @@
      * @param componentId The ID of the power component, e.g.
      *                    {@link BatteryConsumer#POWER_COMPONENT_CPU}.
      */
-    public @PowerModel int getPowerModel(@BatteryConsumer.PowerComponent int componentId) {
+    public @PowerModel int getPowerModel(@PowerComponentId int componentId) {
         return mPowerComponents.getPowerModel(
                 mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_UNSPECIFIED,
                         SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED));
@@ -554,9 +592,12 @@
      *
      * @param componentId The ID of the custom power component.
      * @return Amount of consumed power in mAh.
+     *
+     * @deprecated Use getConsumedPower instead
      */
+    @Deprecated
     public double getConsumedPowerForCustomComponent(int componentId) {
-        return mPowerComponents.getConsumedPowerForCustomComponent(componentId);
+        return getConsumedPower(componentId);
     }
 
     public int getCustomPowerComponentCount() {
@@ -580,8 +621,9 @@
      *                    {@link UidBatteryConsumer#POWER_COMPONENT_CPU}.
      * @return Amount of time in milliseconds.
      */
-    public long getUsageDurationMillis(@PowerComponent int componentId) {
-        return mPowerComponents.getUsageDurationMillis(getKey(componentId));
+    public long getUsageDurationMillis(@PowerComponentId int componentId) {
+        return mPowerComponents.getUsageDurationMillis(componentId, PROCESS_STATE_ANY,
+                SCREEN_STATE_ANY, POWER_STATE_ANY);
     }
 
     /**
@@ -598,17 +640,6 @@
     }
 
     /**
-     * Returns the amount of usage time attributed to the specified custom component
-     * since BatteryStats reset.
-     *
-     * @param componentId The ID of the custom power component.
-     * @return Amount of time in milliseconds.
-     */
-    public long getUsageDurationForCustomComponentMillis(int componentId) {
-        return mPowerComponents.getUsageDurationForCustomComponentMillis(componentId);
-    }
-
-    /**
      * Returns the name of the specified component.  Intended for logging and debugging.
      */
     public static String powerComponentIdToString(@BatteryConsumer.PowerComponent int componentId) {
@@ -826,13 +857,12 @@
         public final boolean processStateDataIncluded;
         public final boolean screenStateDataIncluded;
         public final boolean powerStateDataIncluded;
+        public final @PowerComponentId int[] powerComponentIds;
         public final Key[] keys;
         public final SparseArray<Key> indexedKeys;
         public final int totalConsumedPowerColumnIndex;
-        public final int firstCustomConsumedPowerColumn;
-        public final int firstCustomUsageDurationColumn;
         public final int columnCount;
-        private Key[][] mPerComponentKeys;
+        private SparseArray<Key[]> mPerComponentKeys;
 
         private BatteryConsumerDataLayout(int firstColumn, String[] customPowerComponentNames,
                 boolean powerModelsIncluded, boolean includeProcessStateData,
@@ -844,6 +874,15 @@
             this.screenStateDataIncluded = includeScreenState;
             this.powerStateDataIncluded = includePowerState;
 
+            powerComponentIds = new int[POWER_COMPONENT_COUNT + customPowerComponentCount];
+            int id = 0;
+            for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
+                powerComponentIds[id++] = componentId;
+            }
+            for (int i = 0; i < customPowerComponentCount; i++) {
+                powerComponentIds[id++] = FIRST_CUSTOM_POWER_COMPONENT_ID + i;
+            }
+
             int columnIndex = firstColumn;
 
             totalConsumedPowerColumnIndex = columnIndex++;
@@ -857,35 +896,41 @@
                     if (!includePowerState && powerState != POWER_STATE_UNSPECIFIED) {
                         continue;
                     }
-                    for (int componentId = 0; componentId < POWER_COMPONENT_COUNT; componentId++) {
+                    for (int i = 0; i < powerComponentIds.length; i++) {
                         columnIndex = addKeys(keyList, powerModelsIncluded, includeProcessStateData,
-                                componentId, screenState, powerState, columnIndex);
+                                powerComponentIds[i], screenState, powerState, columnIndex);
                     }
                 }
             }
 
-            firstCustomConsumedPowerColumn = columnIndex;
-            columnIndex += customPowerComponentCount;
-
-            firstCustomUsageDurationColumn = columnIndex;
-            columnIndex += customPowerComponentCount;
-
             columnCount = columnIndex;
 
             keys = keyList.toArray(KEY_ARRAY);
             indexedKeys = new SparseArray<>(keys.length);
             for (int i = 0; i < keys.length; i++) {
                 Key key = keys[i];
-                int index = keyIndex(key.powerComponent, key.processState, key.screenState,
-                        key.powerState);
-                indexedKeys.put(index, key);
+                indexedKeys.put(keyIndex(key.powerComponentId, key.processState, key.screenState,
+                        key.powerState), key);
+            }
+        }
+
+        public String getPowerComponentName(@PowerComponentId int powerComponentId) {
+            if (powerComponentId < POWER_COMPONENT_COUNT) {
+                return BatteryConsumer.powerComponentIdToString(powerComponentId);
+            } else if (powerComponentId >= FIRST_CUSTOM_POWER_COMPONENT_ID && powerComponentId
+                    < FIRST_CUSTOM_POWER_COMPONENT_ID + customPowerComponentCount) {
+                return customPowerComponentNames[powerComponentId
+                        - FIRST_CUSTOM_POWER_COMPONENT_ID];
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported power component " + powerComponentId);
             }
         }
 
         private int addKeys(List<Key> keys, boolean powerModelsIncluded,
-                boolean includeProcessStateData, int componentId,
+                boolean includeProcessStateData, @PowerComponentId int componentId,
                 int screenState, int powerState, int columnIndex) {
-            keys.add(new Key(componentId, PROCESS_STATE_ANY, screenState, powerState,
+            keys.add(new Key(componentId, PROCESS_STATE_UNSPECIFIED, screenState, powerState,
                     powerModelsIncluded
                             ? columnIndex++
                             : POWER_MODEL_NOT_INCLUDED,  // power model
@@ -896,14 +941,13 @@
             // Declare Keys for all process states, if needed
             if (includeProcessStateData) {
                 boolean isSupported = SUPPORTED_POWER_COMPONENTS_PER_PROCESS_STATE
-                        .binarySearch(componentId) >= 0;
+                        .binarySearch(componentId) >= 0
+                        || componentId >= FIRST_CUSTOM_POWER_COMPONENT_ID;
                 if (isSupported) {
-                    for (int processState = 0; processState < PROCESS_STATE_COUNT;
-                            processState++) {
-                        if (processState == PROCESS_STATE_UNSPECIFIED) {
+                    for (int processState = 0; processState < PROCESS_STATE_COUNT; processState++) {
+                        if (processState == PROCESS_STATE_UNSPECIFIED) { // Already added above
                             continue;
                         }
-
                         keys.add(new Key(componentId, processState, screenState, powerState,
                                 powerModelsIncluded
                                         ? columnIndex++
@@ -917,12 +961,12 @@
             return columnIndex;
         }
 
-        Key getKey(@PowerComponent int componentId, @ProcessState int processState,
+        Key getKey(@PowerComponentId int componentId, @ProcessState int processState,
                 @ScreenState int screenState, @PowerState int powerState) {
             return indexedKeys.get(keyIndex(componentId, processState, screenState, powerState));
         }
 
-        Key getKeyOrThrow(@PowerComponent int componentId, @ProcessState int processState,
+        Key getKeyOrThrow(@PowerComponentId int componentId, @ProcessState int processState,
                 @ScreenState int screenState, @PowerState int powerState) {
             Key key = getKey(componentId, processState, screenState, powerState);
             if (key == null) {
@@ -933,21 +977,21 @@
             return key;
         }
 
-        public Key[] getKeys(@PowerComponent int componentId) {
+        public Key[] getKeys(@PowerComponentId int componentId) {
             synchronized (this) {
                 if (mPerComponentKeys == null) {
-                    mPerComponentKeys = new Key[BatteryConsumer.POWER_COMPONENT_COUNT][];
+                    mPerComponentKeys = new SparseArray<>(powerComponentIds.length);
                 }
-                Key[] componentKeys = mPerComponentKeys[componentId];
+                Key[] componentKeys = mPerComponentKeys.get(componentId);
                 if (componentKeys == null) {
                     ArrayList<Key> out = new ArrayList<>();
                     for (Key key : keys) {
-                        if (key.powerComponent == componentId) {
+                        if (key.powerComponentId == componentId) {
                             out.add(key);
                         }
                     }
                     componentKeys = out.toArray(new Key[out.size()]);
-                    mPerComponentKeys[componentId] = componentKeys;
+                    mPerComponentKeys.put(componentId, componentKeys);
                 }
                 return componentKeys;
             }
@@ -991,18 +1035,18 @@
         }
 
         @Nullable
-        public Key[] getKeys(@PowerComponent int componentId) {
+        public Key[] getKeys(@PowerComponentId int componentId) {
             return mData.layout.getKeys(componentId);
         }
 
         @Nullable
-        public Key getKey(@PowerComponent int componentId, @ProcessState int processState) {
+        public Key getKey(@PowerComponentId int componentId, @ProcessState int processState) {
             return mData.layout.getKey(componentId, processState, SCREEN_STATE_UNSPECIFIED,
                     POWER_STATE_UNSPECIFIED);
         }
 
         @Nullable
-        public Key getKey(@PowerComponent int componentId, @ProcessState int processState,
+        public Key getKey(@PowerComponentId int componentId, @ProcessState int processState,
                 @ScreenState int screenState, @PowerState int powerState) {
             return mData.layout.getKey(componentId, processState, screenState, powerState);
         }
@@ -1015,7 +1059,7 @@
          * @param componentPower Amount of consumed power in mAh.
          */
         @NonNull
-        public T setConsumedPower(@PowerComponent int componentId, double componentPower) {
+        public T setConsumedPower(@PowerComponentId int componentId, double componentPower) {
             return setConsumedPower(componentId, componentPower, POWER_MODEL_POWER_PROFILE);
         }
 
@@ -1028,7 +1072,7 @@
          */
         @SuppressWarnings("unchecked")
         @NonNull
-        public T setConsumedPower(@PowerComponent int componentId, double componentPower,
+        public T setConsumedPower(@PowerComponentId int componentId, double componentPower,
                 @PowerModel int powerModel) {
             mPowerComponentsBuilder.setConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
                     componentPower, powerModel);
@@ -1037,7 +1081,7 @@
 
         @SuppressWarnings("unchecked")
         @NonNull
-        public T addConsumedPower(@PowerComponent int componentId, double componentPower,
+        public T addConsumedPower(@PowerComponentId int componentId, double componentPower,
                 @PowerModel int powerModel) {
             mPowerComponentsBuilder.addConsumedPower(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
                     componentPower, powerModel);
@@ -1059,26 +1103,6 @@
         }
 
         /**
-         * Sets the amount of drain attributed to the specified custom drain type.
-         *
-         * @param componentId    The ID of the custom power component.
-         * @param componentPower Amount of consumed power in mAh.
-         */
-        @SuppressWarnings("unchecked")
-        @NonNull
-        public T setConsumedPowerForCustomComponent(int componentId, double componentPower) {
-            mPowerComponentsBuilder.setConsumedPowerForCustomComponent(componentId, componentPower);
-            return (T) this;
-        }
-
-        @SuppressWarnings("unchecked")
-        @NonNull
-        public T addConsumedPowerForCustomComponent(int componentId, double componentPower) {
-            mPowerComponentsBuilder.addConsumedPowerForCustomComponent(componentId, componentPower);
-            return (T) this;
-        }
-
-        /**
          * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
          *
          * @param componentId              The ID of the power component, e.g.
@@ -1087,7 +1111,7 @@
          */
         @SuppressWarnings("unchecked")
         @NonNull
-        public T setUsageDurationMillis(@UidBatteryConsumer.PowerComponent int componentId,
+        public T setUsageDurationMillis(@PowerComponentId int componentId,
                 long componentUsageTimeMillis) {
             mPowerComponentsBuilder
                     .setUsageDurationMillis(getKey(componentId, PROCESS_STATE_UNSPECIFIED),
@@ -1095,7 +1119,6 @@
             return (T) this;
         }
 
-
         @SuppressWarnings("unchecked")
         @NonNull
         public T setUsageDurationMillis(Key key, long componentUsageTimeMillis) {
@@ -1104,21 +1127,6 @@
         }
 
         /**
-         * Sets the amount of time used by the specified custom component.
-         *
-         * @param componentId              The ID of the custom power component.
-         * @param componentUsageTimeMillis Amount of time in microseconds.
-         */
-        @SuppressWarnings("unchecked")
-        @NonNull
-        public T setUsageDurationForCustomComponentMillis(int componentId,
-                long componentUsageTimeMillis) {
-            mPowerComponentsBuilder.setUsageDurationForCustomComponentMillis(componentId,
-                    componentUsageTimeMillis);
-            return (T) this;
-        }
-
-        /**
          * Returns the total power accumulated by this builder so far. It may change
          * by the time the {@code build()} method is called.
          */
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c7751e3..c4d12d4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1997,6 +1997,8 @@
                 STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG
                 | STATE2_GPS_SIGNAL_QUALITY_MASK;
 
+        public static final int GNSS_SIGNAL_QUALITY_NONE = 2;
+
         @UnsupportedAppUsage
         public int states2;
 
@@ -2220,7 +2222,7 @@
             modemRailChargeMah = 0;
             wifiRailChargeMah = 0;
             states = 0;
-            states2 = 0;
+            states2 = GNSS_SIGNAL_QUALITY_NONE << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
             wakelockTag = null;
             wakeReasonTag = null;
             eventCode = EVENT_NONE;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index e039953..1fef602 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -95,7 +95,6 @@
     static final String XML_TAG_USER = "user";
     static final String XML_TAG_POWER_COMPONENTS = "power_components";
     static final String XML_TAG_COMPONENT = "component";
-    static final String XML_TAG_CUSTOM_COMPONENT = "custom_component";
     static final String XML_ATTR_ID = "id";
     static final String XML_ATTR_UID = "uid";
     static final String XML_ATTR_USER_ID = "user_id";
@@ -610,96 +609,109 @@
         final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
                 AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
 
-        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                componentId++) {
-            final double devicePowerMah = deviceConsumer.getConsumedPower(componentId);
-            final double appsPowerMah = appsConsumer.getConsumedPower(componentId);
+        for (@BatteryConsumer.PowerComponentId int powerComponent :
+                mBatteryConsumerDataLayout.powerComponentIds) {
+            final double devicePowerMah = deviceConsumer.getConsumedPower(powerComponent);
+            final double appsPowerMah = appsConsumer.getConsumedPower(powerComponent);
             if (devicePowerMah == 0 && appsPowerMah == 0) {
                 continue;
             }
 
-            printPowerComponent(pw, prefix, BatteryConsumer.powerComponentIdToString(componentId),
-                    devicePowerMah, appsPowerMah,
-                    BatteryConsumer.POWER_MODEL_UNDEFINED,
-                    deviceConsumer.getUsageDurationMillis(componentId));
+            printPowerComponent(pw, prefix,
+                    mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
+                    devicePowerMah, appsPowerMah, BatteryConsumer.POWER_MODEL_UNDEFINED,
+                    deviceConsumer.getUsageDurationMillis(powerComponent));
         }
 
-        for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-                componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                        + mCustomPowerComponentNames.length;
-                componentId++) {
-            final double devicePowerMah =
-                    deviceConsumer.getConsumedPowerForCustomComponent(componentId);
-            final double appsPowerMah =
-                    appsConsumer.getConsumedPowerForCustomComponent(componentId);
-            if (devicePowerMah == 0 && appsPowerMah == 0) {
-                continue;
+        String prefixPlus = prefix + "  ";
+        if (mIncludesPowerStateData && !mIncludesScreenStateData) {
+            for (@BatteryConsumer.PowerState int powerState = 0;
+                    powerState < BatteryConsumer.POWER_STATE_COUNT;
+                    powerState++) {
+                if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+                    dumpPowerComponents(pw, BatteryConsumer.SCREEN_STATE_ANY, powerState,
+                            prefixPlus);
+                }
             }
-
-            printPowerComponent(pw, prefix, deviceConsumer.getCustomPowerComponentName(componentId),
-                    devicePowerMah, appsPowerMah,
-                    BatteryConsumer.POWER_MODEL_UNDEFINED,
-                    deviceConsumer.getUsageDurationForCustomComponentMillis(componentId));
-        }
-
-        if (mIncludesScreenStateData || mIncludesPowerStateData) {
-            String prefixPlus = prefix + "  ";
-            StringBuilder stateLabel = new StringBuilder();
-            int screenState = BatteryConsumer.SCREEN_STATE_UNSPECIFIED;
-            int powerState = BatteryConsumer.POWER_STATE_UNSPECIFIED;
-            for (BatteryConsumer.Key key : mBatteryConsumerDataLayout.keys) {
-                if (key.processState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
-                    continue;
+        } else if (!mIncludesPowerStateData && mIncludesScreenStateData) {
+            for (@BatteryConsumer.ScreenState int screenState = 0;
+                    screenState < BatteryConsumer.SCREEN_STATE_COUNT;
+                    screenState++) {
+                if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                    dumpPowerComponents(pw, screenState, BatteryConsumer.POWER_STATE_ANY,
+                            prefixPlus);
                 }
-
-                if (key.screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED
-                        && key.powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
-                    // Totals already printed earlier in this method
-                    continue;
-                }
-
-                final double devicePowerMah = deviceConsumer.getConsumedPower(key);
-                final double appsPowerMah = appsConsumer.getConsumedPower(key);
-                if (devicePowerMah == 0 && appsPowerMah == 0) {
-                    continue;
-                }
-
-                if (key.screenState != screenState || key.powerState != powerState) {
-                    screenState = key.screenState;
-                    powerState = key.powerState;
-
-                    boolean empty = true;
-                    stateLabel.setLength(0);
-                    stateLabel.append("      (");
-                    if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
-                        stateLabel.append(BatteryConsumer.powerStateToString(powerState));
-                        empty = false;
-                    }
-                    if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
-                        if (!empty) {
-                            stateLabel.append(", ");
+            }
+        } else if (mIncludesPowerStateData && mIncludesScreenStateData) {
+            for (@BatteryConsumer.PowerState int powerState = 0;
+                    powerState < BatteryConsumer.POWER_STATE_COUNT;
+                    powerState++) {
+                if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+                    for (@BatteryConsumer.ScreenState int screenState = 0;
+                            screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
+                        if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                            dumpPowerComponents(pw, screenState, powerState, prefixPlus);
                         }
-                        stateLabel.append("screen ").append(
-                                BatteryConsumer.screenStateToString(screenState));
-                        empty = false;
-                    }
-                    if (!empty) {
-                        stateLabel.append(")");
-                        pw.println(stateLabel);
                     }
                 }
-                String label = BatteryConsumer.powerComponentIdToString(key.powerComponent);
-                printPowerComponent(pw, prefixPlus, label, devicePowerMah, appsPowerMah,
-                        mIncludesPowerModels ? deviceConsumer.getPowerModel(key)
-                                : BatteryConsumer.POWER_MODEL_UNDEFINED,
-                        deviceConsumer.getUsageDurationMillis(key));
             }
         }
+
         dumpSortedBatteryConsumers(pw, prefix, getUidBatteryConsumers());
         dumpSortedBatteryConsumers(pw, prefix, getUserBatteryConsumers());
         pw.println();
     }
 
+    private void dumpPowerComponents(PrintWriter pw,
+            @BatteryConsumer.ScreenState int screenState,
+            @BatteryConsumer.PowerState int powerState, String prefix) {
+        final BatteryConsumer deviceConsumer = getAggregateBatteryConsumer(
+                AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+        final BatteryConsumer appsConsumer = getAggregateBatteryConsumer(
+                AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+
+        boolean labelPrinted = false;
+        for (@BatteryConsumer.PowerComponentId int powerComponent :
+                mBatteryConsumerDataLayout.powerComponentIds) {
+            BatteryConsumer.Dimensions dimensions = new BatteryConsumer.Dimensions(
+                    powerComponent, BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState);
+            final double devicePowerMah = deviceConsumer.getConsumedPower(dimensions);
+            final double appsPowerMah = appsConsumer.getConsumedPower(dimensions);
+            if (devicePowerMah == 0 && appsPowerMah == 0) {
+                continue;
+            }
+
+            if (!labelPrinted) {
+                boolean empty = true;
+                StringBuilder stateLabel = new StringBuilder();
+                stateLabel.append("      (");
+                if (powerState != BatteryConsumer.POWER_STATE_ANY) {
+                    stateLabel.append(BatteryConsumer.powerStateToString(powerState));
+                    empty = false;
+                }
+                if (screenState != BatteryConsumer.SCREEN_STATE_ANY) {
+                    if (!empty) {
+                        stateLabel.append(", ");
+                    }
+                    stateLabel.append("screen ")
+                            .append(BatteryConsumer.screenStateToString(screenState));
+                    empty = false;
+                }
+                if (!empty) {
+                    stateLabel.append(")");
+                    pw.println(stateLabel);
+                    labelPrinted = true;
+                }
+            }
+            printPowerComponent(pw, prefix,
+                    mBatteryConsumerDataLayout.getPowerComponentName(powerComponent),
+                    devicePowerMah, appsPowerMah,
+                    mIncludesPowerModels ? deviceConsumer.getPowerModel(powerComponent)
+                            : BatteryConsumer.POWER_MODEL_UNDEFINED,
+                    deviceConsumer.getUsageDurationMillis(dimensions));
+        }
+    }
+
     private void printPowerComponent(PrintWriter pw, String prefix, String label,
             double devicePowerMah, double appsPowerMah, int powerModel, long durationMs) {
         StringBuilder sb = new StringBuilder();
@@ -951,12 +963,14 @@
 
         /**
          * Returns true if this Builder is configured to hold data for the specified
-         * custom power component ID.
+         * power component index.
          */
-        public boolean isSupportedCustomPowerComponent(int componentId) {
-            return componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
+        public boolean isSupportedPowerComponent(
+                @BatteryConsumer.PowerComponentId int componentId) {
+            return componentId < BatteryConsumer.POWER_COMPONENT_COUNT
+                    || (componentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
                     && componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                    + mBatteryConsumerDataLayout.customPowerComponentCount;
+                    + mBatteryConsumerDataLayout.customPowerComponentCount);
         }
 
         /**
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index d0ed297..a12606b 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -86,7 +86,7 @@
     private final long mFromTimestamp;
     private final long mToTimestamp;
     private final double mMinConsumedPowerThreshold;
-    private final @BatteryConsumer.PowerComponent int[] mPowerComponents;
+    private final @BatteryConsumer.PowerComponentId int[] mPowerComponents;
 
     private BatteryUsageStatsQuery(@NonNull Builder builder) {
         mFlags = builder.mFlags;
@@ -139,6 +139,7 @@
      * Returns the power components that should be estimated or null if all power components
      * are being requested.
      */
+    @BatteryConsumer.PowerComponentId
     public int[] getPowerComponents() {
         return mPowerComponents;
     }
@@ -228,7 +229,7 @@
         private long mFromTimestamp;
         private long mToTimestamp;
         private double mMinConsumedPowerThreshold = 0;
-        private @BatteryConsumer.PowerComponent int[] mPowerComponents;
+        private @BatteryConsumer.PowerComponentId int[] mPowerComponents;
 
         /**
          * Builds a read-only BatteryUsageStatsQuery object.
@@ -294,7 +295,7 @@
          * is all power components.
          */
         public Builder includePowerComponents(
-                @BatteryConsumer.PowerComponent int[] powerComponents) {
+                @BatteryConsumer.PowerComponentId int[] powerComponents) {
             mPowerComponents = powerComponents;
             return this;
         }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index b7556df..4bc3dbe 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -708,9 +708,16 @@
      *
      * @hide
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
     public final native void markVintfStability();
 
+    /** @hide */
+    private void markVintfStability$ravenwood() {
+        // This is not useful for Ravenwood which uses local binder.
+        // TODO(b/361785059): Use real native libbinder.
+    }
+
     /**
      * Use a VINTF-stability binder w/o VINTF requirements. Should be called
      * on a binder before it is sent out of process.
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1100731..c22f46c 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -646,6 +646,37 @@
     private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags);
 
     /**
+     * This list is to hold strong reference to the frozen state callbacks. The callbacks are only
+     * weakly referenced by JNI so the strong references here are needed to keep the callbacks
+     * around until the proxy is GC'ed.
+     */
+    private List<IFrozenStateChangeCallback> mFrozenStateChangeCallbacks =
+            Collections.synchronizedList(new ArrayList<>());
+
+    /**
+     * See {@link IBinder#addFrozenStateChangeCallback(IFrozenStateChangeCallback)}
+     */
+    public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+            throws RemoteException {
+        addFrozenStateChangeCallbackNative(callback);
+        mFrozenStateChangeCallbacks.add(callback);
+    }
+
+    /**
+     * See {@link IBinder#removeFrozenStateChangeCallback}
+     */
+    public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+        mFrozenStateChangeCallbacks.remove(callback);
+        return removeFrozenStateChangeCallbackNative(callback);
+    }
+
+    private native void addFrozenStateChangeCallbackNative(IFrozenStateChangeCallback callback)
+            throws RemoteException;
+
+    private native boolean removeFrozenStateChangeCallbackNative(
+            IFrozenStateChangeCallback callback);
+
+    /**
      * Perform a dump on the remote object
      *
      * @param fd The raw file descriptor that the dump is being sent to.
@@ -730,6 +761,17 @@
         }
     }
 
+    private static void invokeFrozenStateChangeCallback(
+            IFrozenStateChangeCallback callback, IBinder binderProxy, int stateIndex) {
+        try {
+            callback.onFrozenStateChanged(binderProxy,
+                    IFrozenStateChangeCallback.State.values()[stateIndex]);
+        } catch (RuntimeException exc) {
+            Log.w("BinderNative", "Uncaught exception from frozen state change callback",
+                    exc);
+        }
+    }
+
     /**
      * C++ pointer to BinderProxyNativeData. That consists of strong pointers to the
      * native IBinder object, and a DeathRecipientList.
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 92b630f..80f39bf 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -46,13 +46,13 @@
  * a {@link Message} object containing a bundle of data that will be
  * processed by the Handler's {@link #handleMessage} method (requiring that
  * you implement a subclass of Handler).
- * 
+ *
  * <p>When posting or sending to a Handler, you can either
  * allow the item to be processed as soon as the message queue is ready
  * to do so, or specify a delay before it gets processed or absolute time for
  * it to be processed.  The latter two allow you to implement timeouts,
  * ticks, and other timing-based behavior.
- * 
+ *
  * <p>When a
  * process is created for your application, its main thread is dedicated to
  * running a message queue that takes care of managing the top-level
@@ -85,13 +85,13 @@
          */
         boolean handleMessage(@NonNull Message msg);
     }
-    
+
     /**
      * Subclasses must implement this to receive messages.
      */
     public void handleMessage(@NonNull Message msg) {
     }
-    
+
     /**
      * Handle system messages here.
      */
@@ -343,8 +343,8 @@
      * The default implementation will either return the class name of the
      * message callback if any, or the hexadecimal representation of the
      * message "what" field.
-     *  
-     * @param message The message whose name is being queried 
+     *
+     * @param message The message whose name is being queried
      */
     @NonNull
     public String getMessageName(@NonNull Message message) {
@@ -367,7 +367,7 @@
 
     /**
      * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message.
-     * 
+     *
      * @param what Value to assign to the returned Message.what field.
      * @return A Message from the global message pool.
      */
@@ -376,12 +376,12 @@
     {
         return Message.obtain(this, what);
     }
-    
+
     /**
-     * 
-     * Same as {@link #obtainMessage()}, except that it also sets the what and obj members 
+     *
+     * Same as {@link #obtainMessage()}, except that it also sets the what and obj members
      * of the returned Message.
-     * 
+     *
      * @param what Value to assign to the returned Message.what field.
      * @param obj Value to assign to the returned Message.obj field.
      * @return A Message from the global message pool.
@@ -392,7 +392,7 @@
     }
 
     /**
-     * 
+     *
      * Same as {@link #obtainMessage()}, except that it also sets the what, arg1 and arg2 members of the returned
      * Message.
      * @param what Value to assign to the returned Message.what field.
@@ -405,10 +405,10 @@
     {
         return Message.obtain(this, what, arg1, arg2);
     }
-    
+
     /**
-     * 
-     * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the 
+     *
+     * Same as {@link #obtainMessage()}, except that it also sets the what, obj, arg1,and arg2 values on the
      * returned Message.
      * @param what Value to assign to the returned Message.what field.
      * @param arg1 Value to assign to the returned Message.arg1 field.
@@ -423,19 +423,19 @@
 
     /**
      * Causes the Runnable r to be added to the message queue.
-     * The runnable will be run on the thread to which this handler is 
-     * attached. 
-     *  
+     * The runnable will be run on the thread to which this handler is
+     * attached.
+     *
      * @param r The Runnable that will be executed.
-     * 
-     * @return Returns true if the Runnable was successfully placed in to the 
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
     public final boolean post(@NonNull Runnable r) {
        return  sendMessageDelayed(getPostMessage(r), 0);
     }
-    
+
     /**
      * Causes the Runnable r to be added to the message queue, to be run
      * at a specific time given by <var>uptimeMillis</var>.
@@ -446,8 +446,8 @@
      * @param r The Runnable that will be executed.
      * @param uptimeMillis The absolute time at which the callback should run,
      *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
-     *  
-     * @return Returns true if the Runnable was successfully placed in to the 
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.  Note that a
      *         result of true does not mean the Runnable will be processed -- if
@@ -457,7 +457,7 @@
     public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
         return sendMessageAtTime(getPostMessage(r), uptimeMillis);
     }
-    
+
     /**
      * Causes the Runnable r to be added to the message queue, to be run
      * at a specific time given by <var>uptimeMillis</var>.
@@ -470,21 +470,21 @@
      *         {@link #removeCallbacksAndMessages}.
      * @param uptimeMillis The absolute time at which the callback should run,
      *         using the {@link android.os.SystemClock#uptimeMillis} time-base.
-     * 
-     * @return Returns true if the Runnable was successfully placed in to the 
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.  Note that a
      *         result of true does not mean the Runnable will be processed -- if
      *         the looper is quit before the delivery time of the message
      *         occurs then the message will be dropped.
-     *         
+     *
      * @see android.os.SystemClock#uptimeMillis
      */
     public final boolean postAtTime(
             @NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
         return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
     }
-    
+
     /**
      * Causes the Runnable r to be added to the message queue, to be run
      * after the specified amount of time elapses.
@@ -492,12 +492,12 @@
      * is attached.
      * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
      * Time spent in deep sleep will add an additional delay to execution.
-     *  
+     *
      * @param r The Runnable that will be executed.
      * @param delayMillis The delay (in milliseconds) until the Runnable
      *        will be executed.
-     *        
-     * @return Returns true if the Runnable was successfully placed in to the 
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.  Note that a
      *         result of true does not mean the Runnable will be processed --
@@ -507,7 +507,7 @@
     public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
         return sendMessageDelayed(getPostMessage(r), delayMillis);
     }
-    
+
     /** @hide */
     public final boolean postDelayed(Runnable r, int what, long delayMillis) {
         return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);
@@ -547,10 +547,10 @@
      * <b>This method is only for use in very special circumstances -- it
      * can easily starve the message queue, cause ordering problems, or have
      * other unexpected side-effects.</b>
-     *  
+     *
      * @param r The Runnable that will be executed.
-     * 
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -635,8 +635,8 @@
      * Pushes a message onto the end of the message queue after all pending messages
      * before the current time. It will be received in {@link #handleMessage},
      * in the thread attached to this handler.
-     *  
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -646,8 +646,8 @@
 
     /**
      * Sends a Message containing only the what value.
-     *  
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -659,9 +659,9 @@
     /**
      * Sends a Message containing only the what value, to be delivered
      * after the specified amount of time elapses.
-     * @see #sendMessageDelayed(android.os.Message, long) 
-     * 
-     * @return Returns true if the message was successfully placed in to the 
+     * @see #sendMessageDelayed(android.os.Message, long)
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -672,11 +672,11 @@
     }
 
     /**
-     * Sends a Message containing only the what value, to be delivered 
+     * Sends a Message containing only the what value, to be delivered
      * at a specific time.
      * @see #sendMessageAtTime(android.os.Message, long)
-     *  
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -691,8 +691,8 @@
      * Enqueue a message into the message queue after all pending messages
      * before (current time + delayMillis). You will receive it in
      * {@link #handleMessage}, in the thread attached to this handler.
-     *  
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.  Note that a
      *         result of true does not mean the message will be processed -- if
@@ -713,12 +713,12 @@
      * Time spent in deep sleep will add an additional delay to execution.
      * You will receive it in {@link #handleMessage}, in the thread attached
      * to this handler.
-     * 
+     *
      * @param uptimeMillis The absolute time at which the message should be
      *         delivered, using the
      *         {@link android.os.SystemClock#uptimeMillis} time-base.
-     *         
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.  Note that a
      *         result of true does not mean the message will be processed -- if
@@ -743,8 +743,8 @@
      * <b>This method is only for use in very special circumstances -- it
      * can easily starve the message queue, cause ordering problems, or have
      * other unexpected side-effects.</b>
-     *  
-     * @return Returns true if the message was successfully placed in to the 
+     *
+     * @return Returns true if the message was successfully placed in to the
      *         message queue.  Returns false on failure, usually because the
      *         looper processing the message queue is exiting.
      */
@@ -798,6 +798,12 @@
     /**
      * Remove any pending posts of messages with code 'what' that are in the
      * message queue.
+     *
+     * Note that `Message#what` is 0 unless otherwise set.
+     * When calling `postMessage(Runnable)` or `postAtTime(Runnable, long)`,
+     * the `Runnable` is internally wrapped with a `Message` whose `what` is 0.
+     * Calling `removeMessages(0)` will remove all messages without a `what`,
+     * including posted `Runnable`s.
      */
     public final void removeMessages(int what) {
         mQueue.removeMessages(this, what, null);
@@ -889,7 +895,7 @@
     }
 
     // if we can get rid of this method, the handler need not remember its loop
-    // we could instead export a getMessageQueue() method... 
+    // we could instead export a getMessageQueue() method...
     @NonNull
     public final Looper getLooper() {
         return mLooper;
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 50242ba..8185e8e 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -376,4 +376,53 @@
      * return value instead.
      */
     public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags);
+
+    /** @hide */
+    interface IFrozenStateChangeCallback {
+        enum State {FROZEN, UNFROZEN};
+
+        /**
+         * Interface for receiving a callback when the process hosting an IBinder
+         * has changed its frozen state.
+         * @param who The IBinder whose hosting process has changed state.
+         * @param state The latest state.
+         */
+        void onFrozenStateChanged(@NonNull IBinder who, State state);
+    }
+
+    /**
+     * {@link addFrozenStateChangeCallback} provides a callback mechanism to notify about process
+     * frozen/unfrozen events. Upon registration and any subsequent state changes, the callback is
+     * invoked with the latest process frozen state.
+     *
+     * <p>If the listener process (the one using this API) is itself frozen, state change events
+     * might be combined into a single one with the latest frozen state. This single event would
+     * then be delivered when the listener process becomes unfrozen. Similarly, if an event happens
+     * before the previous event is consumed, they might be combined. This means the callback might
+     * not be called for every single state change, so don't rely on this API to count how many
+     * times the state has changed.</p>
+     *
+     * <p>The callback is automatically removed when all references to the binder proxy are
+     * dropped.</p>
+     *
+     * <p>You will only receive state change notifications for remote binders, as local binders by
+     * definition can't be frozen without you being frozen too.</p>
+     *
+     * <p>@throws {@link UnsupportedOperationException} if the kernel binder driver does not support
+     * this feature.
+     * @hide
+     */
+    default void addFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback)
+            throws RemoteException {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Unregister a {@link IFrozenStateChangeCallback}. The callback will no longer be invoked when
+     * the hosting process changes its frozen state.
+     * @hide
+     */
+    default boolean removeFrozenStateChangeCallback(@NonNull IFrozenStateChangeCallback callback) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index a1db9be..702fdc2 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -41,6 +41,9 @@
      * what this message is about. Each {@link Handler} has its own name-space
      * for message codes, so you do not need to worry about yours conflicting
      * with other handlers.
+     *
+     * If not specified, this value is 0.
+     * Use values other than 0 to indicate custom message codes.
      */
     public int what;
 
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 4cc057a..e80efd2 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -41,7 +41,6 @@
 import android.net.Uri;
 import android.os.MessageQueue.OnFileDescriptorEventListener;
 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
-import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
 import android.ravenwood.annotation.RavenwoodReplace;
 import android.ravenwood.annotation.RavenwoodThrow;
 import android.system.ErrnoException;
@@ -77,8 +76,6 @@
  * you to close it when done with it.
  */
 @RavenwoodKeepWholeClass
-@RavenwoodNativeSubstitutionClass(
-        "com.android.platform.test.ravenwood.nativesubstitution.ParcelFileDescriptor_host")
 public class ParcelFileDescriptor implements Parcelable, Closeable {
     private static final String TAG = "ParcelFileDescriptor";
 
@@ -206,11 +203,11 @@
         }
         mWrapped = null;
         mFd = fd;
-        setFdOwner(mFd);
+        IoUtils.setFdOwner(mFd, this);
 
         mCommFd = commChannel;
         if (mCommFd != null) {
-            setFdOwner(mCommFd);
+            IoUtils.setFdOwner(mCommFd, this);
         }
 
         mGuard.open("close");
@@ -298,7 +295,7 @@
     public static @NonNull ParcelFileDescriptor wrap(@NonNull ParcelFileDescriptor pfd,
             @NonNull Handler handler, @NonNull OnCloseListener listener) throws IOException {
         final FileDescriptor original = new FileDescriptor();
-        setFdInt(original, pfd.detachFd());
+        original.setInt$(pfd.detachFd());
         return fromFd(original, handler, listener);
     }
 
@@ -363,18 +360,10 @@
         }
     }
 
-    @RavenwoodReplace
     private static void closeInternal(FileDescriptor fd) {
         IoUtils.closeQuietly(fd);
     }
 
-    private static void closeInternal$ravenwood(FileDescriptor fd) {
-        try {
-            Os.close(fd);
-        } catch (ErrnoException ignored) {
-        }
-    }
-
     /**
      * Create a new ParcelFileDescriptor that is a dup of an existing
      * FileDescriptor.  This obeys standard POSIX semantics, where the
@@ -385,7 +374,7 @@
         try {
             final FileDescriptor fd = new FileDescriptor();
             int intfd = Os.fcntlInt(orig, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
-            setFdInt(fd, intfd);
+            fd.setInt$(intfd);
             return new ParcelFileDescriptor(fd);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -418,12 +407,12 @@
      */
     public static ParcelFileDescriptor fromFd(int fd) throws IOException {
         final FileDescriptor original = new FileDescriptor();
-        setFdInt(original, fd);
+        original.setInt$(fd);
 
         try {
             final FileDescriptor dup = new FileDescriptor();
             int intfd = Os.fcntlInt(original, (isAtLeastQ() ? F_DUPFD_CLOEXEC : F_DUPFD), 0);
-            setFdInt(dup, intfd);
+            dup.setInt$(intfd);
             return new ParcelFileDescriptor(dup);
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
@@ -446,7 +435,7 @@
      */
     public static ParcelFileDescriptor adoptFd(int fd) {
         final FileDescriptor fdesc = new FileDescriptor();
-        setFdInt(fdesc, fd);
+        fdesc.setInt$(fd);
 
         return new ParcelFileDescriptor(fdesc);
     }
@@ -703,7 +692,7 @@
     @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
     public static File getFile(FileDescriptor fd) throws IOException {
         try {
-            final String path = Os.readlink("/proc/self/fd/" + getFdInt(fd));
+            final String path = Os.readlink("/proc/self/fd/" + fd.getInt$());
             if (OsConstants.S_ISREG(Os.stat(path).st_mode)
                     || OsConstants.S_ISCHR(Os.stat(path).st_mode)) {
                 return new File(path);
@@ -783,7 +772,7 @@
             if (mClosed) {
                 throw new IllegalStateException("Already closed");
             }
-            return getFdInt(mFd);
+            return mFd.getInt$();
         }
     }
 
@@ -805,7 +794,7 @@
             if (mClosed) {
                 throw new IllegalStateException("Already closed");
             }
-            int fd = acquireRawFd(mFd);
+            int fd = IoUtils.acquireRawFd(mFd);
             writeCommStatusAndClose(Status.DETACHED, null);
             mClosed = true;
             mGuard.close();
@@ -1265,38 +1254,6 @@
         }
     }
 
-    private static native void setFdInt$ravenwood(FileDescriptor fd, int fdInt);
-    private static native int getFdInt$ravenwood(FileDescriptor fd);
-
-    @RavenwoodReplace
-    private static void setFdInt(FileDescriptor fd, int fdInt) {
-        fd.setInt$(fdInt);
-    }
-
-    @RavenwoodReplace
-    private static int getFdInt(FileDescriptor fd) {
-        return fd.getInt$();
-    }
-
-    @RavenwoodReplace
-    private void setFdOwner(FileDescriptor fd) {
-        IoUtils.setFdOwner(fd, this);
-    }
-
-    private void setFdOwner$ravenwood(FileDescriptor fd) {
-        // FD owners currently unsupported under Ravenwood; ignored
-    }
-
-    @RavenwoodReplace
-    private int acquireRawFd(FileDescriptor fd) {
-        return IoUtils.acquireRawFd(fd);
-    }
-
-    private int acquireRawFd$ravenwood(FileDescriptor fd) {
-        // FD owners currently unsupported under Ravenwood; return FD directly
-        return getFdInt(fd);
-    }
-
     @RavenwoodReplace
     private static boolean isAtLeastQ() {
         return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index f22e1ea..9200db3 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -60,14 +60,14 @@
      * Total power consumed by this consumer, aggregated over the specified dimensions, in mAh.
      */
     public double getConsumedPower(@NonNull BatteryConsumer.Dimensions dimensions) {
-        return getConsumedPower(dimensions.powerComponent, dimensions.processState,
+        return getConsumedPower(dimensions.powerComponentId, dimensions.processState,
                 dimensions.screenState, dimensions.powerState);
     }
 
     /**
      * Total power consumed by this consumer, aggregated over the specified dimensions, in mAh.
      */
-    public double getConsumedPower(@BatteryConsumer.PowerComponent int powerComponent,
+    public double getConsumedPower(@BatteryConsumer.PowerComponentId int powerComponent,
             @BatteryConsumer.ProcessState int processState,
             @BatteryConsumer.ScreenState int screenState,
             @BatteryConsumer.PowerState int powerState) {
@@ -76,85 +76,64 @@
             return mData.getDouble(mData.layout.totalConsumedPowerColumnIndex);
         }
 
-        if (powerComponent != POWER_COMPONENT_ANY
-                && ((mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY)
-                || (mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY))) {
-            BatteryConsumer.Key key = mData.layout.getKey(powerComponent,
-                    processState, screenState, powerState);
-            if (key != null) {
-                return mData.getDouble(key.mPowerColumnIndex);
-            }
+        if (!mData.layout.processStateDataIncluded && !(processState == PROCESS_STATE_UNSPECIFIED
+                || processState == PROCESS_STATE_ANY)) {
             return 0;
         }
 
-        if (mData.layout.processStateDataIncluded || mData.layout.screenStateDataIncluded
-                || mData.layout.powerStateDataIncluded) {
-            double total = 0;
-            for (BatteryConsumer.Key key : mData.layout.keys) {
-                if (key.processState != PROCESS_STATE_UNSPECIFIED
-                        && key.matches(powerComponent, processState, screenState, powerState)) {
-                    total += mData.getDouble(key.mPowerColumnIndex);
-                }
-            }
-            if (total != 0) {
-                return total;
-            }
-        }
-
-        BatteryConsumer.Key key = mData.layout.getKey(powerComponent, processState,
-                SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
-        if (key != null) {
+        BatteryConsumer.Key key = mData.layout.getKey(powerComponent,
+                mData.layout.processStateDataIncluded && processState != PROCESS_STATE_ANY
+                        ? processState : PROCESS_STATE_UNSPECIFIED,
+                mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY
+                        ? screenState : SCREEN_STATE_UNSPECIFIED,
+                mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY
+                        ? powerState : POWER_STATE_UNSPECIFIED);
+        if (key != null && mData.hasValue(key.mPowerColumnIndex)) {
             return mData.getDouble(key.mPowerColumnIndex);
-        } else {
-            return 0;
         }
+
+        double total = 0;
+        for (BatteryConsumer.Key k : mData.layout.keys) {
+            if (k.matches(powerComponent, processState, screenState, powerState)) {
+                total += mData.getDouble(k.mPowerColumnIndex);
+            }
+        }
+        return total;
     }
 
     /**
      * Total usage duration by this consumer, aggregated over the specified dimensions, in ms.
      */
     public long getUsageDurationMillis(@NonNull BatteryConsumer.Dimensions dimensions) {
-        return getUsageDurationMillis(dimensions.powerComponent, dimensions.processState,
+        return getUsageDurationMillis(dimensions.powerComponentId, dimensions.processState,
                 dimensions.screenState, dimensions.powerState);
     }
 
     /**
      * Total usage duration by this consumer, aggregated over the specified dimensions, in ms.
      */
-    public long getUsageDurationMillis(@BatteryConsumer.PowerComponent int powerComponent,
+    public long getUsageDurationMillis(@BatteryConsumer.PowerComponentId int powerComponent,
             @BatteryConsumer.ProcessState int processState,
             @BatteryConsumer.ScreenState int screenState,
             @BatteryConsumer.PowerState int powerState) {
-        if ((mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY)
-                || (mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY)) {
-            BatteryConsumer.Key key = mData.layout.getKey(powerComponent,
-                    processState, screenState, powerState);
-            if (key != null) {
-                return mData.getLong(key.mDurationColumnIndex);
-            }
-            return 0;
-        }
-
-        if (mData.layout.screenStateDataIncluded || mData.layout.powerStateDataIncluded) {
-            long total = 0;
-            for (BatteryConsumer.Key key : mData.layout.keys) {
-                if (key.processState != PROCESS_STATE_UNSPECIFIED
-                        && key.matches(powerComponent, processState, screenState, powerState)) {
-                    total += mData.getLong(key.mDurationColumnIndex);
-                }
-            }
-            if (total != 0) {
-                return total;
-            }
-        }
-
-        BatteryConsumer.Key key = mData.layout.getKey(powerComponent, processState,
-                SCREEN_STATE_UNSPECIFIED, POWER_STATE_UNSPECIFIED);
-        if (key != null) {
+        BatteryConsumer.Key key = mData.layout.getKey(powerComponent,
+                mData.layout.processStateDataIncluded && processState != PROCESS_STATE_ANY
+                        ? processState : PROCESS_STATE_UNSPECIFIED,
+                mData.layout.screenStateDataIncluded && screenState != SCREEN_STATE_ANY
+                        ? screenState : SCREEN_STATE_UNSPECIFIED,
+                mData.layout.powerStateDataIncluded && powerState != POWER_STATE_ANY
+                        ? powerState : POWER_STATE_UNSPECIFIED);
+        if (key != null && mData.hasValue(key.mDurationColumnIndex)) {
             return mData.getLong(key.mDurationColumnIndex);
-        } else {
-            return 0;
         }
+
+        long total = 0;
+        for (BatteryConsumer.Key k : mData.layout.keys) {
+            if (k.matches(powerComponent, processState, screenState, powerState)) {
+                total += mData.getLong(k.mDurationColumnIndex);
+            }
+        }
+        return total;
     }
 
     /**
@@ -168,39 +147,12 @@
         if (mData.hasValue(key.mPowerColumnIndex)) {
             return mData.getDouble(key.mPowerColumnIndex);
         }
-        return getConsumedPower(key.powerComponent, key.processState, key.screenState,
+        return getConsumedPower(key.powerComponentId, key.processState, key.screenState,
                 key.powerState);
     }
 
-    /**
-     * Returns the amount of drain attributed to the specified custom drain type.
-     *
-     * @param componentId The ID of the custom power component.
-     * @return Amount of consumed power in mAh.
-     */
-    public double getConsumedPowerForCustomComponent(int componentId) {
-        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
-            return mData.getDouble(mData.layout.firstCustomConsumedPowerColumn + index);
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported custom power component ID: " + componentId);
-        }
-    }
-
     public String getCustomPowerComponentName(int componentId) {
-        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
-            try {
-                return mData.layout.customPowerComponentNames[index];
-            } catch (ArrayIndexOutOfBoundsException e) {
-                throw new IllegalArgumentException(
-                        "Unsupported custom power component ID: " + componentId);
-            }
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported custom power component ID: " + componentId);
-        }
+        return mData.layout.getPowerComponentName(componentId);
     }
 
     @BatteryConsumer.PowerModel
@@ -224,63 +176,26 @@
             return mData.getLong(key.mDurationColumnIndex);
         }
 
-        return getUsageDurationMillis(key.powerComponent, key.processState, key.screenState,
+        return getUsageDurationMillis(key.powerComponentId, key.processState, key.screenState,
                 key.powerState);
     }
 
-    /**
-     * Returns the amount of usage time attributed to the specified custom component.
-     *
-     * @param componentId The ID of the custom power component.
-     * @return Amount of time in milliseconds.
-     */
-    public long getUsageDurationForCustomComponentMillis(int componentId) {
-        final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-        if (index >= 0 && index < mData.layout.customPowerComponentCount) {
-            return mData.getLong(mData.layout.firstCustomUsageDurationColumn + index);
-        } else {
-            throw new IllegalArgumentException(
-                    "Unsupported custom power component ID: " + componentId);
-        }
-    }
-
     void dump(PrintWriter pw, @BatteryConsumer.ScreenState int screenState,
             @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents) {
         StringBuilder sb = new StringBuilder();
-        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                componentId++) {
-            dump(sb, componentId, PROCESS_STATE_ANY, screenState, powerState, skipEmptyComponents);
+        for (@BatteryConsumer.PowerComponentId int id : mData.layout.powerComponentIds) {
+            dump(sb, id, PROCESS_STATE_ANY, screenState, powerState, skipEmptyComponents);
             if (mData.layout.processStateDataIncluded) {
                 for (int processState = 0; processState < BatteryConsumer.PROCESS_STATE_COUNT;
                         processState++) {
                     if (processState == PROCESS_STATE_UNSPECIFIED) {
                         continue;
                     }
-                    dump(sb, componentId, processState, screenState, powerState,
-                            skipEmptyComponents);
+                    dump(sb, id, processState, screenState, powerState, skipEmptyComponents);
                 }
             }
         }
 
-        // TODO(b/352835319): take into account screen and power states
-        if (screenState == SCREEN_STATE_ANY && powerState == POWER_STATE_ANY) {
-            final int customComponentCount = mData.layout.customPowerComponentCount;
-            for (int customComponentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-                    customComponentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                            + customComponentCount;
-                    customComponentId++) {
-                final double customComponentPower =
-                        getConsumedPowerForCustomComponent(customComponentId);
-                if (skipEmptyComponents && customComponentPower == 0) {
-                    continue;
-                }
-                sb.append(getCustomPowerComponentName(customComponentId));
-                sb.append("=");
-                sb.append(BatteryStats.formatCharge(customComponentPower));
-                sb.append(" ");
-            }
-        }
-
         // Remove trailing spaces
         while (!sb.isEmpty() && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
             sb.setLength(sb.length() - 1);
@@ -289,25 +204,25 @@
         pw.println(sb);
     }
 
-    private void dump(StringBuilder sb, @BatteryConsumer.PowerComponent int powerComponent,
+    private void dump(StringBuilder sb, @BatteryConsumer.PowerComponentId int powerComponent,
             @BatteryConsumer.ProcessState int processState,
             @BatteryConsumer.ScreenState int screenState,
             @BatteryConsumer.PowerState int powerState, boolean skipEmptyComponents) {
-        final double componentPower = getConsumedPower(powerComponent, processState, screenState,
+        final double power = getConsumedPower(powerComponent, processState, screenState,
                 powerState);
         final long durationMs = getUsageDurationMillis(powerComponent, processState, screenState,
                 powerState);
-        if (skipEmptyComponents && componentPower == 0 && durationMs == 0) {
+        if (skipEmptyComponents && power == 0 && durationMs == 0) {
             return;
         }
 
-        sb.append(BatteryConsumer.powerComponentIdToString(powerComponent));
-        if (processState != PROCESS_STATE_UNSPECIFIED) {
+        sb.append(mData.layout.getPowerComponentName(powerComponent));
+        if (processState != PROCESS_STATE_ANY) {
             sb.append(':');
             sb.append(BatteryConsumer.processStateToString(processState));
         }
         sb.append("=");
-        sb.append(BatteryStats.formatCharge(componentPower));
+        sb.append(BatteryStats.formatCharge(power));
 
         if (durationMs != 0) {
             sb.append(" (");
@@ -334,15 +249,14 @@
     private boolean writeStatsProtoImpl(@Nullable ProtoOutputStream proto) {
         boolean interestingData = false;
 
-        for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                componentId++) {
+        for (@BatteryConsumer.PowerComponentId int componentId : mData.layout.powerComponentIds) {
             final BatteryConsumer.Key[] keys = mData.layout.getKeys(componentId);
             for (BatteryConsumer.Key key : keys) {
                 final long powerDeciCoulombs = convertMahToDeciCoulombs(
-                        getConsumedPower(key.powerComponent, key.processState, key.screenState,
+                        getConsumedPower(key.powerComponentId, key.processState, key.screenState,
                                 key.powerState));
-                final long durationMs = getUsageDurationMillis(key.powerComponent, key.processState,
-                        key.screenState, key.powerState);
+                final long durationMs = getUsageDurationMillis(key.powerComponentId,
+                        key.processState, key.screenState, key.powerState);
 
                 if (powerDeciCoulombs == 0 && durationMs == 0) {
                     // No interesting data. Make sure not to even write the COMPONENT int.
@@ -356,7 +270,7 @@
                     return true;
                 }
 
-                if (key.processState == PROCESS_STATE_ANY) {
+                if (key.processState == PROCESS_STATE_UNSPECIFIED) {
                     writePowerComponentUsage(proto,
                             BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
                             componentId, powerDeciCoulombs, durationMs);
@@ -366,27 +280,6 @@
                 }
             }
         }
-        for (int idx = 0; idx < mData.layout.customPowerComponentCount; idx++) {
-            final int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + idx;
-            final long powerDeciCoulombs =
-                    convertMahToDeciCoulombs(getConsumedPowerForCustomComponent(componentId));
-            final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
-
-            if (powerDeciCoulombs == 0 && durationMs == 0) {
-                // No interesting data. Make sure not to even write the COMPONENT int.
-                continue;
-            }
-
-            interestingData = true;
-            if (proto == null) {
-                // We're just asked whether there is data, not to actually write it. And there is.
-                return true;
-            }
-
-            writePowerComponentUsage(proto,
-                    BatteryUsageStatsAtomsProto.BatteryConsumerData.POWER_COMPONENTS,
-                    componentId, powerDeciCoulombs, durationMs);
-        }
         return interestingData;
     }
 
@@ -427,8 +320,9 @@
         proto.end(slicesToken);
     }
 
-    private void writePowerComponentUsage(ProtoOutputStream proto, long tag, int componentId,
-            long powerDeciCoulombs, long durationMs) {
+    private void writePowerComponentUsage(ProtoOutputStream proto, long tag,
+            @BatteryConsumer.PowerComponentId int componentId, long powerDeciCoulombs,
+            long durationMs) {
         final long token = proto.start(tag);
         proto.write(
                 BatteryUsageStatsAtomsProto.BatteryConsumerData.PowerComponentUsage
@@ -460,7 +354,7 @@
             }
 
             serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
-            serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, key.powerComponent);
+            serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, key.powerComponentId);
             if (key.processState != PROCESS_STATE_UNSPECIFIED) {
                 serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_PROCESS_STATE,
                         key.processState);
@@ -485,32 +379,11 @@
             }
             serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
         }
-
-        final int customComponentEnd = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                + mData.layout.customPowerComponentCount;
-        for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-                componentId < customComponentEnd;
-                componentId++) {
-            final double powerMah = getConsumedPowerForCustomComponent(componentId);
-            final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
-            if (powerMah == 0 && durationMs == 0) {
-                continue;
-            }
-
-            serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
-            serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
-            if (powerMah != 0) {
-                serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
-            }
-            if (durationMs != 0) {
-                serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
-            }
-            serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
-        }
-
         serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
     }
 
+    // No longer part of the BatteryUsageStats XML format. Preserved for compatibility
+    private static final String XML_TAG_CUSTOM_COMPONENT_COMPAT = "custom_component";
 
     static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)
             throws XmlPullParserException, IOException {
@@ -525,7 +398,8 @@
                 && eventType != XmlPullParser.END_DOCUMENT) {
             if (eventType == XmlPullParser.START_TAG) {
                 switch (parser.getName()) {
-                    case BatteryUsageStats.XML_TAG_COMPONENT: {
+                    case BatteryUsageStats.XML_TAG_COMPONENT:
+                    case XML_TAG_CUSTOM_COMPONENT_COMPAT: {
                         int componentId = -1;
                         int processState = PROCESS_STATE_UNSPECIFIED;
                         int screenState = SCREEN_STATE_UNSPECIFIED;
@@ -564,27 +438,6 @@
                         builder.setUsageDurationMillis(key, durationMs);
                         break;
                     }
-                    case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: {
-                        int componentId = -1;
-                        double powerMah = 0;
-                        long durationMs = 0;
-                        for (int i = 0; i < parser.getAttributeCount(); i++) {
-                            switch (parser.getAttributeName(i)) {
-                                case BatteryUsageStats.XML_ATTR_ID:
-                                    componentId = parser.getAttributeInt(i);
-                                    break;
-                                case BatteryUsageStats.XML_ATTR_POWER:
-                                    powerMah = parser.getAttributeDouble(i);
-                                    break;
-                                case BatteryUsageStats.XML_ATTR_DURATION:
-                                    durationMs = parser.getAttributeLong(i);
-                                    break;
-                            }
-                        }
-                        builder.setConsumedPowerForCustomComponent(componentId, powerMah);
-                        builder.setUsageDurationForCustomComponentMillis(componentId, durationMs);
-                        break;
-                    }
                 }
             }
             eventType = parser.next();
@@ -631,36 +484,6 @@
             return this;
         }
 
-        /**
-         * Sets the amount of drain attributed to the specified custom drain type.
-         *
-         * @param componentId    The ID of the custom power component.
-         * @param componentPower Amount of consumed power in mAh.
-         */
-        @NonNull
-        public Builder setConsumedPowerForCustomComponent(int componentId, double componentPower) {
-            final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-            if (index < 0 || index >= mData.layout.customPowerComponentCount) {
-                throw new IllegalArgumentException(
-                        "Unsupported custom power component ID: " + componentId);
-            }
-            mData.putDouble(mData.layout.firstCustomConsumedPowerColumn + index, componentPower);
-            return this;
-        }
-
-        @NonNull
-        public Builder addConsumedPowerForCustomComponent(int componentId, double componentPower) {
-            final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-            if (index < 0 || index >= mData.layout.customPowerComponentCount) {
-                throw new IllegalArgumentException(
-                        "Unsupported custom power component ID: " + componentId);
-            }
-            mData.putDouble(mData.layout.firstCustomConsumedPowerColumn + index,
-                    mData.getDouble(mData.layout.firstCustomConsumedPowerColumn + index)
-                            + componentPower);
-            return this;
-        }
-
         @NonNull
         public Builder setUsageDurationMillis(BatteryConsumer.Key key,
                 long componentUsageDurationMillis) {
@@ -668,26 +491,6 @@
             return this;
         }
 
-        /**
-         * Sets the amount of time used by the specified custom component.
-         *
-         * @param componentId                  The ID of the custom power component.
-         * @param componentUsageDurationMillis Amount of time in milliseconds.
-         */
-        @NonNull
-        public Builder setUsageDurationForCustomComponentMillis(int componentId,
-                long componentUsageDurationMillis) {
-            final int index = componentId - BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-            if (index < 0 || index >= mData.layout.customPowerComponentCount) {
-                throw new IllegalArgumentException(
-                        "Unsupported custom power component ID: " + componentId);
-            }
-
-            mData.putLong(mData.layout.firstCustomUsageDurationColumn + index,
-                    componentUsageDurationMillis);
-            return this;
-        }
-
         public void addPowerAndDuration(PowerComponents.Builder other) {
             addPowerAndDuration(other.mData);
         }
@@ -706,19 +509,23 @@
             }
 
             for (BatteryConsumer.Key key : mData.layout.keys) {
-                BatteryConsumer.Key otherKey = otherData.layout.getKey(key.powerComponent,
+                BatteryConsumer.Key otherKey = otherData.layout.getKey(key.powerComponentId,
                         key.processState, key.screenState, key.powerState);
                 if (otherKey == null) {
                     continue;
                 }
-
-                mData.putDouble(key.mPowerColumnIndex,
-                        mData.getDouble(key.mPowerColumnIndex)
-                                + otherData.getDouble(otherKey.mPowerColumnIndex));
-                mData.putLong(key.mDurationColumnIndex,
-                        mData.getLong(key.mDurationColumnIndex)
-                                + otherData.getLong(otherKey.mDurationColumnIndex));
-
+                if (mData.hasValue(key.mPowerColumnIndex)
+                        || otherData.hasValue(otherKey.mPowerColumnIndex)) {
+                    mData.putDouble(key.mPowerColumnIndex,
+                            mData.getDouble(key.mPowerColumnIndex)
+                                    + otherData.getDouble(otherKey.mPowerColumnIndex));
+                }
+                if (mData.hasValue(key.mDurationColumnIndex)
+                        || otherData.hasValue(otherKey.mDurationColumnIndex)) {
+                    mData.putLong(key.mDurationColumnIndex,
+                            mData.getLong(key.mDurationColumnIndex)
+                                    + otherData.getLong(otherKey.mDurationColumnIndex));
+                }
                 if (key.mPowerModelColumnIndex == POWER_MODEL_NOT_INCLUDED) {
                     continue;
                 }
@@ -742,21 +549,6 @@
                             BatteryConsumer.POWER_MODEL_UNDEFINED);
                 }
             }
-
-            for (int i = mData.layout.customPowerComponentCount - 1; i >= 0; i--) {
-                final int powerColumnIndex = mData.layout.firstCustomConsumedPowerColumn + i;
-                final int otherPowerColumnIndex =
-                        otherData.layout.firstCustomConsumedPowerColumn + i;
-                mData.putDouble(powerColumnIndex,
-                        mData.getDouble(powerColumnIndex) + otherData.getDouble(
-                                otherPowerColumnIndex));
-
-                final int usageColumnIndex = mData.layout.firstCustomUsageDurationColumn + i;
-                final int otherDurationColumnIndex =
-                        otherData.layout.firstCustomUsageDurationColumn + i;
-                mData.putLong(usageColumnIndex, mData.getLong(usageColumnIndex)
-                        + otherData.getLong(otherDurationColumnIndex));
-            }
         }
 
         /**
@@ -765,15 +557,12 @@
          */
         public double getTotalPower() {
             double totalPowerMah = 0;
-            for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                    componentId++) {
-                totalPowerMah += mData.getDouble(
-                        mData.layout.getKeyOrThrow(componentId, PROCESS_STATE_ANY, SCREEN_STATE_ANY,
-                                POWER_STATE_ANY).mPowerColumnIndex);
-            }
-            for (int i = 0; i < mData.layout.customPowerComponentCount; i++) {
-                totalPowerMah += mData.getDouble(
-                        mData.layout.firstCustomConsumedPowerColumn + i);
+            for (BatteryConsumer.Key key : mData.layout.keys) {
+                if (key.processState == PROCESS_STATE_UNSPECIFIED
+                        && key.screenState == SCREEN_STATE_UNSPECIFIED
+                        && key.powerState == POWER_STATE_UNSPECIFIED) {
+                    totalPowerMah += mData.getDouble(key.mPowerColumnIndex);
+                }
             }
             return totalPowerMah;
         }
@@ -783,7 +572,7 @@
          */
         @NonNull
         public PowerComponents build() {
-            for (BatteryConsumer.Key key: mData.layout.keys) {
+            for (BatteryConsumer.Key key : mData.layout.keys) {
                 if (key.mPowerModelColumnIndex != POWER_MODEL_NOT_INCLUDED) {
                     if (mData.getInt(key.mPowerModelColumnIndex) == POWER_MODEL_UNINITIALIZED) {
                         mData.putInt(key.mPowerModelColumnIndex,
@@ -798,9 +587,7 @@
                 }
             }
 
-            if (mData.getDouble(mData.layout.totalConsumedPowerColumnIndex) == 0) {
-                mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
-            }
+            mData.putDouble(mData.layout.totalConsumedPowerColumnIndex, getTotalPower());
             return new PowerComponents(this);
         }
     }
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 1fb7937..29ccb85 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -145,10 +145,11 @@
      * @param screenBrightnessInt The overridden screen brightness between 1 and 255, or
      * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override. Not used if
      *                            screenBrightnessFloat is provided (is not NaN).
+     * @param useNormalBrightnessForDoze Whether use normal brightness while device is dozing.
      */
     public abstract void setDozeOverrideFromDreamManager(
             int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
-            int screenBrightnessInt);
+            int screenBrightnessInt, boolean useNormalBrightnessForDoze);
 
     /**
      * Used by sidekick manager to tell the power manager if it shouldn't change the display state
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index db06a6b..3b2041b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -838,16 +838,11 @@
     /**
      * Returns true if the current process is a 64-bit runtime.
      */
-    @android.ravenwood.annotation.RavenwoodReplace
+    @android.ravenwood.annotation.RavenwoodKeep
     public static final boolean is64Bit() {
         return VMRuntime.getRuntime().is64Bit();
     }
 
-    /** @hide */
-    public static final boolean is64Bit$ravenwood() {
-        return "amd64".equals(System.getProperty("os.arch"));
-    }
-
     private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood;
 
     /** @hide */
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index bb74a3e..398140d 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1170,11 +1171,27 @@
         return removedSubsCount.get() == subscriptionInfos.size();
     }
 
-    /** {@hide} */
-    public static void rebootPromptAndWipeUserData(Context context, String reason)
+    /**
+     * Reboot into recovery and prompt for wiping the device.
+     *
+     * This is used as last resort in case the device is not recoverable using
+     * other recovery steps. This first checks if fs-checkpoint is available, in
+     * which case we commit the checkpoint, otherwise it performs the reboot in
+     * recovery mode and shows user prompt for wiping the device.
+     *
+     * @param context      the context to use.
+     * @param reason       the reason to wipe.
+     *
+     * @throws IOException if something goes wrong.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
+    public static void rebootPromptAndWipeUserData(@NonNull Context context, @NonNull String reason)
             throws IOException {
         boolean checkpointing = false;
-        boolean needReboot = false;
         IVold vold = null;
         try {
             vold = IVold.Stub.asInterface(ServiceManager.checkService("vold"));
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index c801fabf..46ae9d8 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -379,6 +379,14 @@
         private int mReferenceCount;
 
         private MemoryRegistration(int size) {
+            // Round up to the nearest page size
+            final int PAGE_SIZE = OsConstants._SC_PAGE_SIZE;
+            if (PAGE_SIZE > 0) {
+                final int remainder = size % PAGE_SIZE;
+                if (remainder != 0) {
+                    size += PAGE_SIZE - remainder;
+                }
+            }
             mSize = size;
             mReferenceCount = 1;
             VMRuntime.getRuntime().registerNativeAllocation(mSize);
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 23bd30a..0ed1ab6 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -314,6 +314,15 @@
     }
 
     /**
+     * @see #currentNetworkTimeMillis(ITimeDetectorService)
+     * @hide
+     */
+    public static long currentNetworkTimeMillis() {
+        return currentNetworkTimeMillis(ITimeDetectorService.Stub
+                .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE)));
+    }
+
+    /**
      * Returns milliseconds since January 1, 1970 00:00:00.0 UTC, synchronized
      * using a remote network source outside the device.
      * <p>
@@ -331,14 +340,14 @@
      * at any time. Due to network delays, variations between servers, or local
      * (client side) clock drift, the accuracy of the returned times cannot be
      * guaranteed. In extreme cases, consecutive calls to {@link
-     * #currentNetworkTimeMillis()} could return times that are out of order.
+     * #currentNetworkTimeMillis(ITimeDetectorService)} could return times that
+     * are out of order.
      *
      * @throws DateTimeException when no network time can be provided.
      * @hide
      */
-    public static long currentNetworkTimeMillis() {
-        ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
-                .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+    public static long currentNetworkTimeMillis(
+            ITimeDetectorService timeDetectorService) {
         if (timeDetectorService != null) {
             UnixEpochTime time;
             try {
@@ -380,16 +389,21 @@
      * at any time. Due to network delays, variations between servers, or local
      * (client side) clock drift, the accuracy of the returned times cannot be
      * guaranteed. In extreme cases, consecutive calls to {@link
-     * Clock#millis()} on the returned {@link Clock}could return times that are
+     * Clock#millis()} on the returned {@link Clock} could return times that are
      * out of order.
      *
      * @throws DateTimeException when no network time can be provided.
      */
     public static @NonNull Clock currentNetworkTimeClock() {
         return new SimpleClock(ZoneOffset.UTC) {
+            private ITimeDetectorService mSvc;
             @Override
             public long millis() {
-                return SystemClock.currentNetworkTimeMillis();
+                if (mSvc == null) {
+                    mSvc = ITimeDetectorService.Stub
+                            .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
+                }
+                return SystemClock.currentNetworkTimeMillis(mSvc);
             }
         };
     }
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 2fde5e7..728db27 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -73,11 +73,7 @@
         "PowerComponents\\.java",
         "[^/]*BatteryConsumer[^/]*\\.java"
       ],
-      "name": "FrameworksCoreTests",
-      "options": [
-        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
-        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
-      ]
+      "name": "FrameworksCoreTests_battery_stats"
     },
     {
       "file_patterns": [
@@ -86,10 +82,7 @@
         "PowerComponents\\.java",
         "[^/]*BatteryConsumer[^/]*\\.java"
       ],
-      "name": "FrameworksServicesTests",
-      "options": [
-        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
-      ]
+      "name": "FrameworksServicesTests_battery_stats"
     },
     {
       "file_patterns": [
@@ -132,12 +125,7 @@
     },
     {
       "file_patterns": ["Environment[^/]*\\.java"],
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.os.EnvironmentTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_environment"
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index edb3a64..4a37e0a 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -520,8 +520,20 @@
      * @param counterValue The counter value.
      */
     public static void setCounter(@NonNull String counterName, long counterValue) {
-        if (isTagEnabled(TRACE_TAG_APP)) {
-            nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
+        setCounter(TRACE_TAG_APP, counterName, counterValue);
+    }
+
+    /**
+     * Writes trace message to indicate the value of a given counter under a given trace tag.
+     *
+     * @param traceTag The trace tag.
+     * @param counterName The counter name to appear in the trace.
+     * @param counterValue The counter value.
+     * @hide
+     */
+    public static void setCounter(long traceTag, @NonNull String counterName, long counterValue) {
+        if (isTagEnabled(traceTag)) {
+            nativeTraceCounter(traceTag, counterName, counterValue);
         }
     }
 
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
index 79892e0..6b7cb33 100644
--- a/core/java/android/os/TransactionTooLargeException.java
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -47,7 +47,7 @@
  * If possible, try to break up big requests into smaller pieces.
  * </p><p>
  * If you are implementing a service, it may help to impose size or complexity
- * contraints on the queries that clients can perform.  For example, if the result set
+ * constraints on the queries that clients can perform.  For example, if the result set
  * could become large, then don't allow the client to request more than a few records
  * at a time.  Alternately, instead of returning all of the available data all at once,
  * return the essential information first and make the client ask for additional information
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 28f2c25..f1ec0e4e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -969,9 +969,7 @@
 
     /**
      * Specifies if a user is disallowed from adding new users. This can only be set by device
-     * owners or profile owners on the primary user. The default value is <code>false</code>.
-     * <p>This restriction has no effect on secondary users and managed profiles since only the
-     * primary user can add other users.
+     * owners or profile owners on the main user. The default value is <code>false</code>.
      * <p> When the device is an organization-owned device provisioned with a managed profile,
      * this restriction will be set as a base restriction which cannot be removed by any admin.
      *
@@ -1980,7 +1978,6 @@
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
      * @see #getUserRestrictions()
      */
-    @FlaggedApi(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED)
     public static final String DISALLOW_SIM_GLOBALLY =
             "no_sim_globally";
 
@@ -2001,7 +1998,6 @@
      * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
      * @see #getUserRestrictions()
      */
-    @FlaggedApi(android.app.admin.flags.Flags.FLAG_ASSIST_CONTENT_USER_RESTRICTION_ENABLED)
     public static final String DISALLOW_ASSIST_CONTENT = "no_assist_content";
 
     /**
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index da863e5..5896227 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -151,31 +151,6 @@
      */
     public static final int USAGE_MEDIA = 0x10 | USAGE_CLASS_MEDIA;
 
-    /** @hide */
-    @IntDef(prefix = { "CATEGORY_" }, value = {
-            CATEGORY_UNKNOWN,
-            CATEGORY_KEYBOARD,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Category {}
-
-    /**
-     * Category value when the vibration category is unknown.
-     *
-     * @hide
-     */
-    public static final int CATEGORY_UNKNOWN = 0x0;
-
-    /**
-     * Category value for keyboard vibrations.
-     *
-     * <p>Most typical keyboard vibrations are haptic feedback for virtual keyboard key
-     * press/release, for example.
-     *
-     * @hide
-     */
-    public static final int CATEGORY_KEYBOARD = 1;
-
     /**
      * @hide
      */
@@ -252,14 +227,12 @@
     private final int mUsage;
     private final int mFlags;
     private final int mOriginalAudioUsage;
-    private final int mCategory;
 
     private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
-            @Flag int flags, @Category int category) {
+            @Flag int flags) {
         mUsage = usage;
         mOriginalAudioUsage = audioUsage;
         mFlags = flags & FLAG_ALL_SUPPORTED;
-        mCategory = category;
     }
 
     /**
@@ -297,20 +270,6 @@
     }
 
     /**
-     * Return the vibration category.
-     *
-     * <p>Vibration categories describe the source of the vibration, and it can be combined with
-     * the vibration usage to best match to a user setting, e.g. a vibration with usage touch and
-     * category keyboard can be used to control keyboard haptic feedback independently.
-     *
-     * @hide
-     */
-    @Category
-    public int getCategory() {
-        return mCategory;
-    }
-
-    /**
      * Check whether a flag is set
      * @return true if a flag is set and false otherwise
      */
@@ -362,14 +321,12 @@
         dest.writeInt(mUsage);
         dest.writeInt(mOriginalAudioUsage);
         dest.writeInt(mFlags);
-        dest.writeInt(mCategory);
     }
 
     private VibrationAttributes(Parcel src) {
         mUsage = src.readInt();
         mOriginalAudioUsage = src.readInt();
         mFlags = src.readInt();
-        mCategory = src.readInt();
     }
 
     public static final @NonNull Parcelable.Creator<VibrationAttributes>
@@ -392,12 +349,12 @@
         }
         VibrationAttributes rhs = (VibrationAttributes) o;
         return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage
-                && mFlags == rhs.mFlags && mCategory == rhs.mCategory;
+                && mFlags == rhs.mFlags;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUsage, mOriginalAudioUsage, mFlags, mCategory);
+        return Objects.hash(mUsage, mOriginalAudioUsage, mFlags);
     }
 
     @Override
@@ -405,7 +362,6 @@
         return "VibrationAttributes{"
                 + "mUsage=" + usageToString()
                 + ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
-                + ", mCategory=" + categoryToString()
                 + ", mFlags=" + mFlags
                 + '}';
     }
@@ -445,23 +401,6 @@
         }
     }
 
-    /** @hide */
-    public String categoryToString() {
-        return categoryToString(mCategory);
-    }
-
-    /** @hide */
-    public static String categoryToString(@Category int category) {
-        switch (category) {
-            case CATEGORY_UNKNOWN:
-                return "UNKNOWN";
-            case CATEGORY_KEYBOARD:
-                return "KEYBOARD";
-            default:
-                return "unknown category " + category;
-        }
-    }
-
     /**
      * Builder class for {@link VibrationAttributes} objects.
      * By default, all information is set to UNKNOWN.
@@ -471,7 +410,6 @@
         private int mUsage = USAGE_UNKNOWN;
         private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
         private int mFlags = 0x0;
-        private int mCategory = CATEGORY_UNKNOWN;
 
         /**
          * Constructs a new Builder with the defaults.
@@ -487,7 +425,6 @@
                 mUsage = vib.mUsage;
                 mOriginalAudioUsage = vib.mOriginalAudioUsage;
                 mFlags = vib.mFlags;
-                mCategory = vib.mCategory;
             }
         }
 
@@ -554,7 +491,7 @@
          */
         public @NonNull VibrationAttributes build() {
             VibrationAttributes ans = new VibrationAttributes(
-                    mUsage, mOriginalAudioUsage, mFlags, mCategory);
+                    mUsage, mOriginalAudioUsage, mFlags);
             return ans;
         }
 
@@ -570,19 +507,6 @@
         }
 
         /**
-         * Sets the attribute describing the category of the corresponding vibration.
-         *
-         * @param category The category for the vibration
-         * @return the same Builder instance.
-         *
-         * @hide
-         */
-        public @NonNull Builder setCategory(@Category int category) {
-            mCategory = category;
-            return this;
-        }
-
-        /**
          * Sets only the flags specified in the bitmask, leaving the other supported flag values
          * unchanged in the builder.
          *
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e68b7468..64a2dbc 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -346,7 +346,7 @@
     @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
     public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
         VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
-                VendorEffect.DEFAULT_SCALE);
+                VendorEffect.DEFAULT_SCALE, VendorEffect.DEFAULT_SCALE);
         vendorEffect.validate();
         return vendorEffect;
     }
@@ -540,6 +540,17 @@
     /** @hide */
     public abstract void validate();
 
+
+    /**
+     * If supported, truncate the length of this vibration effect to the provided length and return
+     * the result. Will always return null for repeating effects.
+     *
+     * @return The desired effect, or {@code null} if truncation is not applicable.
+     * @hide
+     */
+    @Nullable
+    public abstract VibrationEffect cropToLengthOrNull(int length);
+
     /**
      * Gets the estimated duration of the vibration in milliseconds.
      *
@@ -623,7 +634,7 @@
      * @hide
      */
     @NonNull
-    public abstract VibrationEffect scaleLinearly(float scaleFactor);
+    public abstract VibrationEffect applyAdaptiveScale(float scaleFactor);
 
     /**
      * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
@@ -866,6 +877,30 @@
             }
         }
 
+        /** @hide */
+        @Override
+        @Nullable
+        public VibrationEffect cropToLengthOrNull(int length) {
+            // drop repeating effects
+            if (mRepeatIndex >= 0) {
+                return null;
+            }
+
+            int segmentCount = mSegments.size();
+            if (segmentCount <= length) {
+                return this;
+            }
+
+            ArrayList truncated = new ArrayList(mSegments.subList(0, length));
+            Composed updated = new Composed(truncated, mRepeatIndex);
+            try {
+                updated.validate();
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+            return updated;
+        }
+
         @Override
         public long getDuration() {
             if (mRepeatIndex >= 0) {
@@ -948,7 +983,7 @@
         /** @hide */
         @NonNull
         @Override
-        public Composed scaleLinearly(float scaleFactor) {
+        public Composed applyAdaptiveScale(float scaleFactor) {
             return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
         }
 
@@ -1100,21 +1135,23 @@
 
         private final PersistableBundle mVendorData;
         private final int mEffectStrength;
-        private final float mLinearScale;
+        private final float mScale;
+        private final float mAdaptiveScale;
 
         /** @hide */
         VendorEffect(@NonNull Parcel in) {
             this(Objects.requireNonNull(
                     in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
-                    in.readInt(), in.readFloat());
+                    in.readInt(), in.readFloat(), in.readFloat());
         }
 
         /** @hide */
         public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
-                float linearScale) {
+                float scale, float adaptiveScale) {
             mVendorData = vendorData;
             mEffectStrength = effectStrength;
-            mLinearScale = linearScale;
+            mScale = scale;
+            mAdaptiveScale = adaptiveScale;
         }
 
         @NonNull
@@ -1126,8 +1163,12 @@
             return mEffectStrength;
         }
 
-        public float getLinearScale() {
-            return mLinearScale;
+        public float getScale() {
+            return mScale;
+        }
+
+        public float getAdaptiveScale() {
+            return mAdaptiveScale;
         }
 
         /** @hide */
@@ -1144,6 +1185,13 @@
                     "Vendor effect bundle must be non-empty");
         }
 
+        /** @hide */
+        @Override
+        @Nullable
+        public VibrationEffect cropToLengthOrNull(int length) {
+            return null;
+        }
+
         @Override
         public long getDuration() {
             return -1; // UNKNOWN
@@ -1175,7 +1223,8 @@
             if (mEffectStrength == effectStrength) {
                 return this;
             }
-            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mLinearScale);
+            VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mScale,
+                    mAdaptiveScale);
             updated.validate();
             return updated;
         }
@@ -1184,18 +1233,24 @@
         @NonNull
         @Override
         public VendorEffect scale(float scaleFactor) {
-            // Vendor effect strength cannot be scaled with this method.
-            return this;
+            if (Float.compare(mScale, scaleFactor) == 0) {
+                return this;
+            }
+            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor,
+                    mAdaptiveScale);
+            updated.validate();
+            return updated;
         }
 
         /** @hide */
         @NonNull
         @Override
-        public VibrationEffect scaleLinearly(float scaleFactor) {
-            if (Float.compare(mLinearScale, scaleFactor) == 0) {
+        public VibrationEffect applyAdaptiveScale(float scaleFactor) {
+            if (Float.compare(mAdaptiveScale, scaleFactor) == 0) {
                 return this;
             }
-            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor);
+            VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, mScale,
+                    scaleFactor);
             updated.validate();
             return updated;
         }
@@ -1216,29 +1271,31 @@
                 return false;
             }
             return mEffectStrength == other.mEffectStrength
-                    && (Float.compare(mLinearScale, other.mLinearScale) == 0)
+                    && (Float.compare(mScale, other.mScale) == 0)
+                    && (Float.compare(mAdaptiveScale, other.mAdaptiveScale) == 0)
                     && isPersistableBundleEquals(mVendorData, other.mVendorData);
         }
 
         @Override
         public int hashCode() {
             // PersistableBundle does not implement hashCode, so use its size as a shortcut.
-            return Objects.hash(mVendorData.size(), mEffectStrength, mLinearScale);
+            return Objects.hash(mVendorData.size(), mEffectStrength, mScale, mAdaptiveScale);
         }
 
         @Override
         public String toString() {
             return String.format(Locale.ROOT,
-                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f}",
-                    mVendorData, effectStrengthToString(mEffectStrength), mLinearScale);
+                    "VendorEffect{vendorData=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f}",
+                    mVendorData, effectStrengthToString(mEffectStrength), mScale, mAdaptiveScale);
         }
 
         /** @hide */
         @Override
         public String toDebugString() {
-            return String.format(Locale.ROOT, "vendorEffect=%s, strength=%s, scale=%.2f",
+            return String.format(Locale.ROOT,
+                    "vendorEffect=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f",
                     mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
-                    mLinearScale);
+                    mScale, mAdaptiveScale);
         }
 
         @Override
@@ -1251,7 +1308,8 @@
             out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
             out.writePersistableBundle(mVendorData);
             out.writeInt(mEffectStrength);
-            out.writeFloat(mLinearScale);
+            out.writeFloat(mScale);
+            out.writeFloat(mAdaptiveScale);
         }
 
         /**
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 53a1a67d..e3b1221 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -113,3 +113,14 @@
         purpose: PURPOSE_FEATURE
     }
 }
+
+flag {
+    namespace: "haptics"
+    name: "normalized_pwle_effects"
+    is_exported: true
+    description: "Enables functionality to create PWLE effects using advanced and simple APIs"
+    bug: "341052318"
+    metadata {
+        purpose: PURPOSE_FEATURE
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
index a15d9bc..b317b80 100644
--- a/core/java/android/permission/TEST_MAPPING
+++ b/core/java/android/permission/TEST_MAPPING
@@ -1,15 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "CtsPermissionTestCases",
-            "options": [
-                {
-                    "include-filter": "android.permission.cts.PermissionControllerTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.RuntimePermissionPresentationInfoTest"
-                }
-            ]
+            "name": "CtsPermissionTestCases_Platform"
         }
     ],
     "postsubmit": [
@@ -36,4 +28,4 @@
             ]
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 3fe063d..5174005 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -217,9 +217,27 @@
 }
 
 flag {
+    name: "check_op_validate_package"
+    namespace: "permissions"
+    description: "Validate package/uid match in checkOp similar to noteOp"
+    bug: "294609684"
+}
+
+flag {
     name: "location_bypass_privacy_dashboard_enabled"
     is_exported: true
     namespace: "permissions"
     description: "Show access entry of location bypass permission in the Privacy Dashboard"
     bug: "325536053"
 }
+
+flag {
+    name: "dont_remove_existing_uid_states"
+    is_fixed_read_only: true
+    namespace: "permissions"
+    description: "Double check if the uid still exists before attempting to remove its appops state"
+    bug: "353474742"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7ca40ea..98904fe 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1972,10 +1972,10 @@
             "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
 
     /**
-     * Activity Action: Show Do Not Disturb access settings.
+     * Activity Action: Show Notification Policy access settings.
      * <p>
-     * Users can grant and deny access to Do Not Disturb configuration from here. Managed
-     * profiles cannot grant Do Not Disturb access.
+     * Users can grant and deny access to Notification Policy (DND / Modes) configuration
+     * from here. Managed profiles cannot grant Notification Policy access.
      * See {@link android.app.NotificationManager#isNotificationPolicyAccessGranted()} for more
      * details.
      * <p>
@@ -14953,6 +14953,7 @@
          *
          * @hide
          */
+        @Readable
         public static final String MUTE_ALARM_STREAM_WITH_RINGER_MODE =
                 "mute_alarm_stream_with_ringer_mode";
 
@@ -19665,6 +19666,8 @@
             public static final int PAIRED_DEVICE_OS_TYPE_ANDROID = 1;
             /** @hide */
             public static final int PAIRED_DEVICE_OS_TYPE_IOS = 2;
+            /** @hide */
+            public static final int PAIRED_DEVICE_OS_TYPE_NONE = 3;
 
             /**
              * The bluetooth settings selected BLE role for the companion.
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 8bd6c85..c38ee08 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -3,6 +3,7 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 
 per-file *NetworkSecurityPolicy.java = file:net/OWNERS
 per-file Confirmation*.java = file:/keystore/OWNERS
diff --git a/core/java/android/security/advancedprotection/OWNERS b/core/java/android/security/advancedprotection/OWNERS
new file mode 100644
index 0000000..ddac8ed
--- /dev/null
+++ b/core/java/android/security/advancedprotection/OWNERS
@@ -0,0 +1,12 @@
+# Bug component: 315013
+
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
index 3a254c1..bdda42a 100644
--- a/core/java/android/security/net/config/SystemCertificateSource.java
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -19,6 +19,8 @@
 import android.os.Environment;
 import android.os.UserHandle;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.io.File;
 
 /**
@@ -45,7 +47,7 @@
         }
         File updatable_dir = new File("/apex/com.android.conscrypt/cacerts");
         if (updatable_dir.exists()
-                && !(updatable_dir.list().length == 0)) {
+                && !(ArrayUtils.isEmpty(updatable_dir.list()))) {
             return updatable_dir;
         }
         return new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts");
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index 1f1a351..2b75493 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -2,6 +2,22 @@
 container: "system"
 
 flag {
+  name: "chooser_album_text"
+  is_exported: true
+  namespace: "intentresolver"
+  description: "Flag controlling the album text subtype hint for sharesheet"
+  bug: "323380224"
+}
+
+flag {
+  name: "enable_sharesheet_metadata_extra"
+  is_exported: true
+  namespace: "intentresolver"
+  description: "This flag enables sharesheet metadata to be displayed to users."
+  bug: "318942069"
+}
+
+flag {
   name: "chooser_payload_toggling"
   is_exported: true
   namespace: "intentresolver"
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 013ec5f..711c414 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -51,11 +51,14 @@
      */
     private Executor mExecutor;
 
+    private Boolean mCurrentRedirectToWake;
+
     // An {@link IDreamOverlayClient} implementation that identifies itself when forwarding
     // requests to the {@link DreamOverlayService}
     private static class OverlayClient extends IDreamOverlayClient.Stub {
         private final WeakReference<DreamOverlayService> mService;
         private boolean mShowComplications;
+        private boolean mIsPreview;
         private ComponentName mDreamComponent;
         IDreamOverlayCallback mDreamOverlayCallback;
 
@@ -73,9 +76,11 @@
 
         @Override
         public void startDream(WindowManager.LayoutParams params, IDreamOverlayCallback callback,
-                String dreamComponent, boolean shouldShowComplications) throws RemoteException {
+                String dreamComponent, boolean isPreview, boolean shouldShowComplications)
+                throws RemoteException {
             mDreamComponent = ComponentName.unflattenFromString(dreamComponent);
             mShowComplications = shouldShowComplications;
+            mIsPreview = isPreview;
             mDreamOverlayCallback = callback;
             applyToDream(dreamOverlayService -> dreamOverlayService.startDream(this, params));
         }
@@ -122,6 +127,10 @@
             return mShowComplications;
         }
 
+        private boolean isDreamInPreviewMode() {
+            return mIsPreview;
+        }
+
         private ComponentName getComponent() {
             return mDreamComponent;
         }
@@ -132,6 +141,10 @@
         mExecutor.execute(() -> {
             endDreamInternal(mCurrentClient);
             mCurrentClient = client;
+            if (Flags.dreamWakeRedirect() && mCurrentRedirectToWake != null) {
+                mCurrentClient.redirectWake(mCurrentRedirectToWake);
+            }
+
             onStartDream(params);
         });
     }
@@ -282,8 +295,10 @@
             return;
         }
 
+        mCurrentRedirectToWake = redirect;
+
         if (mCurrentClient == null) {
-            throw new IllegalStateException("redirected wake with no dream present");
+            return;
         }
 
         mCurrentClient.redirectWake(redirect);
@@ -295,7 +310,6 @@
      *
      * @hide
      */
-    @FlaggedApi(Flags.FLAG_DREAM_WAKE_REDIRECT)
     public void onWakeRequested() {
     }
 
@@ -312,6 +326,19 @@
     }
 
     /**
+     * Returns whether dream is in preview mode.
+     */
+    @FlaggedApi(Flags.FLAG_PUBLISH_PREVIEW_STATE_TO_OVERLAY)
+    public final boolean isDreamInPreviewMode() {
+        if (mCurrentClient == null) {
+            throw new IllegalStateException(
+                    "requested if preview when no dream active");
+        }
+
+        return mCurrentClient.isDreamInPreviewMode();
+    }
+
+    /**
      * Returns the active dream component.
      * @hide
      */
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 06e53ac..ce31e1e 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -17,8 +17,8 @@
 package android.service.dreams;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
 import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
 import static android.service.dreams.Flags.startAndStopDozingInBackground;
 
 import android.annotation.FlaggedApi;
@@ -81,6 +81,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.function.Consumer;
 
 /**
@@ -267,10 +268,13 @@
     private boolean mDozing;
     private boolean mWindowless;
     private boolean mPreviewMode;
-    private volatile int mDozeScreenState = Display.STATE_UNKNOWN;
-    private volatile @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
-    private volatile int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
-    private volatile float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+    private int mDozeScreenState = Display.STATE_UNKNOWN;
+    private @Display.StateReason int mDozeScreenStateReason = Display.STATE_REASON_UNKNOWN;
+    private int mDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+    private float mDozeScreenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+    // This variable being true means dozing device expecting normal(non-doze) brightness.
+    private boolean mUseNormalBrightnessForDoze;
 
     private boolean mDebug = false;
 
@@ -913,13 +917,15 @@
      */
     @UnsupportedAppUsage
     public void startDozing() {
-        if (mCanDoze && !mDozing) {
-            mDozing = true;
-            updateDoze();
+        synchronized (this) {
+            if (mCanDoze && !mDozing) {
+                mDozing = true;
+                updateDoze();
+            }
         }
     }
 
-    private void updateDoze() {
+    private synchronized void updateDoze() {
         if (mDreamToken == null) {
             Slog.w(mTag, "Updating doze without a dream token.");
             return;
@@ -927,16 +933,20 @@
 
         if (mDozing) {
             try {
+                Slog.v(mTag, "UpdateDoze mDozeScreenState=" + mDozeScreenState
+                        + " mDozeScreenBrightness=" + mDozeScreenBrightness
+                        + " mDozeScreenBrightnessFloat=" + mDozeScreenBrightnessFloat);
                 if (startAndStopDozingInBackground()) {
                     mDreamManager.startDozingOneway(
                             mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                            mDozeScreenBrightnessFloat, mDozeScreenBrightness);
+                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+                            mUseNormalBrightnessForDoze);
                 } else {
                     mDreamManager.startDozing(
                             mDreamToken, mDozeScreenState, mDozeScreenStateReason,
-                            mDozeScreenBrightnessFloat, mDozeScreenBrightness);
+                            mDozeScreenBrightnessFloat, mDozeScreenBrightness,
+                            mUseNormalBrightnessForDoze);
                 }
-
             } catch (RemoteException ex) {
                 // system server died
             }
@@ -1005,7 +1015,8 @@
      */
     @UnsupportedAppUsage
     public void setDozeScreenState(int state) {
-        setDozeScreenState(state, Display.STATE_REASON_UNKNOWN);
+        setDozeScreenState(state, Display.STATE_REASON_UNKNOWN,
+                /* useNormalBrightnessForDoze= */ false);
     }
 
     /**
@@ -1043,19 +1054,43 @@
      * {@link Display#STATE_ON_SUSPEND}, {@link Display#STATE_OFF}, or {@link Display#STATE_UNKNOWN}
      * for the default behavior.
      * @param reason the reason for setting the specified screen state.
-     *
-     * @hide For use by system UI components only.
+     * @param useNormalBrightnessForDoze False means the default case where doze brightness is
+     * expected when device is dozing. True means display expects normal brightness for next doze
+     * request. Noted: unlike {@link #setDozeScreenBrightness} that sets a real brightness value for
+     * doze screen, this parameter only indicates whether the doze brightness is intended on next
+     * doze screen. The actual brightness value will be computed by {@link DisplayManager}
+     * internally.
+     * @hide For use by System UI components only.
      */
     @UnsupportedAppUsage
-    public void setDozeScreenState(int state, @Display.StateReason int reason) {
-        if (mDozeScreenState != state) {
-            mDozeScreenState = state;
-            mDozeScreenStateReason = reason;
-            updateDoze();
+    public void setDozeScreenState(int state, @Display.StateReason int reason,
+            boolean useNormalBrightnessForDoze) {
+        synchronized (this) {
+            if (mDozeScreenState != state
+                    || mUseNormalBrightnessForDoze != useNormalBrightnessForDoze) {
+                mDozeScreenState = state;
+                mDozeScreenStateReason = reason;
+                mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
+                updateDoze();
+            }
         }
     }
 
     /**
+     * Returns whether we want to use the normal brightness setting while in doze. This is useful
+     * on devices like Wear; when we allow the user to interact with a device that remains in
+     * doze (looking at time).
+     *
+     * @return a boolean that informs {@link DisplayManager} whether to adjust the display for the
+     * interacting user e.g. brightening the display.
+     * @hide For use by System UI components only.
+     */
+    @UnsupportedAppUsage
+    public boolean getUseNormalBrightnessForDoze() {
+        return mUseNormalBrightnessForDoze;
+    }
+
+    /**
      * Gets the screen brightness to use while dozing.
      *
      * @return The screen brightness while dozing as a value between
@@ -1103,9 +1138,11 @@
         if (brightness != PowerManager.BRIGHTNESS_DEFAULT) {
             brightness = clampAbsoluteBrightness(brightness);
         }
-        if (mDozeScreenBrightness != brightness) {
-            mDozeScreenBrightness = brightness;
-            updateDoze();
+        synchronized (this) {
+            if (mDozeScreenBrightness != brightness) {
+                mDozeScreenBrightness = brightness;
+                updateDoze();
+            }
         }
     }
 
@@ -1141,9 +1178,12 @@
         if (!Float.isNaN(brightness)) {
             brightness = clampAbsoluteBrightnessFloat(brightness);
         }
-        if (!BrightnessSynchronizer.floatEquals(mDozeScreenBrightnessFloat, brightness)) {
-            mDozeScreenBrightnessFloat = brightness;
-            updateDoze();
+
+        synchronized (this) {
+            if (!BrightnessSynchronizer.floatEquals(mDozeScreenBrightnessFloat, brightness)) {
+                mDozeScreenBrightnessFloat = brightness;
+                updateDoze();
+            }
         }
     }
 
@@ -1222,7 +1262,7 @@
     @Override
     public final IBinder onBind(Intent intent) {
         if (mDebug) Slog.v(mTag, "onBind() intent = " + intent);
-        mDreamServiceWrapper = new DreamServiceWrapper();
+        mDreamServiceWrapper = new DreamServiceWrapper(new WeakReference<>(this));
         final ComponentName overlayComponent = intent.getParcelableExtra(
                 EXTRA_DREAM_OVERLAY_COMPONENT, ComponentName.class);
 
@@ -1324,6 +1364,9 @@
      * Tells the dream to come to the front (which in turn tells the overlay to come to the front).
      */
     private void comeToFront() {
+        if (mOverlayConnection == null) {
+            return;
+        }
         mOverlayConnection.addConsumer(overlay -> {
             try {
                 overlay.comeToFront();
@@ -1589,7 +1632,8 @@
             i.setComponent(mInjector.getDreamActivityComponent());
             i.setPackage(mInjector.getDreamPackageName());
             i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
-            DreamActivity.setCallback(i, new DreamActivityCallbacks(mDreamToken));
+            DreamActivity.setCallback(i,
+                    new DreamActivityCallbacks(mDreamToken, new WeakReference<>(this)));
             final ServiceInfo serviceInfo = mInjector.getServiceInfo();
             final CharSequence title = fetchDreamLabel(mInjector.getPackageManager(),
                     mInjector.getResources(), serviceInfo, isPreviewMode);
@@ -1657,6 +1701,7 @@
                                 try {
                                     overlay.startDream(mWindow.getAttributes(), mOverlayCallback,
                                             mDreamComponent.flattenToString(),
+                                            mPreviewMode,
                                             mShouldShowComplications);
                                 } catch (RemoteException e) {
                                     Log.e(mTag, "could not send window attributes:" + e);
@@ -1803,22 +1848,37 @@
      * uses it to control the DreamService. It is also used to receive callbacks from the
      * DreamActivity.
      */
-    final class DreamServiceWrapper extends IDreamService.Stub {
+    static final class DreamServiceWrapper extends IDreamService.Stub {
+        final WeakReference<DreamService> mService;
+
+        DreamServiceWrapper(WeakReference<DreamService> service) {
+            mService = service;
+        }
+
+        private void post(Consumer<DreamService> consumer) {
+            final DreamService service = mService.get();
+
+            if (service == null) {
+                return;
+            }
+
+            service.mHandler.post(() -> consumer.accept(service));
+        }
+
         @Override
         public void attach(final IBinder dreamToken, final boolean canDoze,
                 final boolean isPreviewMode, IRemoteCallback started) {
-            mHandler.post(
-                    () -> DreamService.this.attach(dreamToken, canDoze, isPreviewMode, started));
+            post(dreamService -> dreamService.attach(dreamToken, canDoze, isPreviewMode, started));
         }
 
         @Override
         public void detach() {
-            mHandler.post(DreamService.this::detach);
+            post(DreamService::detach);
         }
 
         @Override
         public void wakeUp() {
-            mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
+            post(dreamService -> dreamService.wakeUp(true /*fromSystem*/));
         }
 
         @Override
@@ -1826,48 +1886,70 @@
             if (!dreamHandlesBeingObscured()) {
                 return;
             }
-
-            mHandler.post(DreamService.this::comeToFront);
+            post(DreamService::comeToFront);
         }
     }
 
+    private void onActivityCreated(DreamActivity activity, IBinder dreamToken) {
+        if (dreamToken != mDreamToken || mFinished) {
+            Slog.d(TAG, "DreamActivity was created after the dream was finished or "
+                    + "a new dream started, finishing DreamActivity");
+            if (!activity.isFinishing()) {
+                activity.finishAndRemoveTask();
+            }
+            return;
+        }
+        if (mActivity != null) {
+            Slog.w(TAG, "A DreamActivity has already been started, "
+                    + "finishing latest DreamActivity");
+            if (!activity.isFinishing()) {
+                activity.finishAndRemoveTask();
+            }
+            return;
+        }
+
+        mActivity = activity;
+        onWindowCreated(activity.getWindow());
+    }
+
+    private void onActivityDestroyed() {
+        mActivity = null;
+        mWindow = null;
+        detach();
+    }
+
     /** @hide */
     @VisibleForTesting
-    public final class DreamActivityCallbacks extends Binder {
+    public static final class DreamActivityCallbacks extends Binder {
         private final IBinder mActivityDreamToken;
+        private WeakReference<DreamService> mService;
 
-        DreamActivityCallbacks(IBinder token) {
+        DreamActivityCallbacks(IBinder token, WeakReference<DreamService> service)  {
             mActivityDreamToken = token;
+            mService = service;
         }
 
         /** Callback when the {@link DreamActivity} has been created */
         public void onActivityCreated(DreamActivity activity) {
-            if (mActivityDreamToken != mDreamToken || mFinished) {
-                Slog.d(TAG, "DreamActivity was created after the dream was finished or "
-                        + "a new dream started, finishing DreamActivity");
-                if (!activity.isFinishing()) {
-                    activity.finishAndRemoveTask();
-                }
-                return;
-            }
-            if (mActivity != null) {
-                Slog.w(TAG, "A DreamActivity has already been started, "
-                        + "finishing latest DreamActivity");
-                if (!activity.isFinishing()) {
-                    activity.finishAndRemoveTask();
-                }
+            final DreamService service = mService.get();
+
+            if (service == null) {
                 return;
             }
 
-            mActivity = activity;
-            onWindowCreated(activity.getWindow());
+            service.onActivityCreated(activity, mActivityDreamToken);
         }
 
         /** Callback when the {@link DreamActivity} has been destroyed */
         public void onActivityDestroyed() {
-            mActivity = null;
-            mWindow = null;
-            detach();
+            final DreamService service = mService.get();
+
+            if (service == null) {
+                return;
+            }
+
+            service.onActivityDestroyed();
+            mService = null;
         }
     }
 
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 611e791..1c0a2c0 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -43,7 +43,7 @@
     void finishSelf(in IBinder token, boolean immediate);
     /** @deprecated Please use startDozingOneway instead. */
     void startDozing(in IBinder token, int screenState, int reason, float screenBrightnessFloat,
-            int screenBrightnessInt);
+            int screenBrightnessInt, boolean useNormalBrightnessForDoze);
     void stopDozing(in IBinder token);
     void forceAmbientDisplayEnabled(boolean enabled);
     ComponentName[] getDreamComponentsForUser(int userId);
@@ -54,6 +54,7 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
     oneway void setDreamIsObscured(in boolean isObscured);
     oneway void startDozingOneway(in IBinder token, int screenState, int reason,
-            float screenBrightnessFloat, int screenBrightnessInt);
+            float screenBrightnessFloat, int screenBrightnessInt,
+            boolean useNormalBrightnessForDoze);
     oneway void finishSelfOneway(in IBinder token, boolean immediate);
 }
diff --git a/core/java/android/service/dreams/IDreamOverlayClient.aidl b/core/java/android/service/dreams/IDreamOverlayClient.aidl
index 0eb15a0..e9df402 100644
--- a/core/java/android/service/dreams/IDreamOverlayClient.aidl
+++ b/core/java/android/service/dreams/IDreamOverlayClient.aidl
@@ -31,11 +31,12 @@
     * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the
     *                dream.
     * @param dreamComponent The component name of the dream service requesting overlay.
+    * @param isPreview Whether the dream is in preview mode.
     * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock
     *                and weather.
     */
     void startDream(in LayoutParams params, in IDreamOverlayCallback callback,
-        in String dreamComponent, in boolean shouldShowComplications);
+        in String dreamComponent, in boolean isPreview, in boolean shouldShowComplications);
 
     /** Called when the dream is waking, to do any exit animations */
     void wakeUp();
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index 83e0adf..72f2de8 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -57,3 +57,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "publish_preview_state_to_overlay"
+    namespace: "systemui"
+    description: "send preview information from dream to overlay"
+    bug: "333734282"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/service/games/TEST_MAPPING b/core/java/android/service/games/TEST_MAPPING
index 3e551ef..9767bcd 100644
--- a/core/java/android/service/games/TEST_MAPPING
+++ b/core/java/android/service/games/TEST_MAPPING
@@ -2,15 +2,7 @@
   "presubmit": [
     // TODO(b/245615658): fix flaky CTS test CtsGameServiceTestCases and add it as presubmit
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "android.service.games"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_games_Presubmit"
     }
   ]
 }
\ No newline at end of file
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 224379b..3d8d933 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -25,7 +25,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-import static android.service.notification.Condition.STATE_TRUE;
 import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
 import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
 import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
@@ -76,8 +75,9 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -187,6 +187,13 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ConfigOrigin {}
 
+    /**
+     * Prefix for the ids of implicit Zen rules. Implicit rules are those created automatically
+     * on behalf of apps that call {@link NotificationManager#setNotificationPolicy} or
+     * {@link NotificationManager#setInterruptionFilter}.
+     */
+    private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
+
     public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
     public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
     public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
@@ -234,10 +241,11 @@
     // ZenModeConfig XML versions distinguishing key changes.
     public static final int XML_VERSION_ZEN_UPGRADE = 8;
     public static final int XML_VERSION_MODES_API = 11;
+    public static final int XML_VERSION_MODES_UI = 12;
 
-    // TODO: b/310620812 - Update XML_VERSION and update default_zen_config.xml accordingly when
-    //       modes_api is inlined.
-    private static final int XML_VERSION = 10;
+    // TODO: b/310620812, b/344831624 - Update XML_VERSION and update default_zen_config.xml
+    //  accordingly when modes_api / modes_ui are inlined.
+    private static final int XML_VERSION_PRE_MODES = 10;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
@@ -945,7 +953,13 @@
     }
 
     public static int getCurrentXmlVersion() {
-        return Flags.modesApi() ? XML_VERSION_MODES_API : XML_VERSION;
+        if (Flags.modesUi()) {
+            return XML_VERSION_MODES_UI;
+        } else if (Flags.modesApi()) {
+            return XML_VERSION_MODES_API;
+        } else {
+            return XML_VERSION_PRE_MODES;
+        }
     }
 
     public static ZenModeConfig readXml(TypedXmlPullParser parser)
@@ -1030,23 +1044,19 @@
                     rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
                             DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
                 } else if (MANUAL_TAG.equals(tag)) {
-                    ZenRule manualRule = readRuleXml(parser);
-                    if (manualRule != null) {
-                        rt.manualRule = manualRule;
-
-                        // Manual rule may be present prior to modes_ui if it were on, but in that
-                        // case it would not have a set policy, so make note of the need to set
-                        // it up later.
-                        readManualRule = true;
-                        if (rt.manualRule.zenPolicy == null) {
-                            readManualRuleWithoutPolicy = true;
-                        }
+                    rt.manualRule = readRuleXml(parser);
+                    // Manual rule may be present prior to modes_ui if it were on, but in that
+                    // case it would not have a set policy, so make note of the need to set
+                    // it up later.
+                    readManualRule = true;
+                    if (rt.manualRule.zenPolicy == null) {
+                        readManualRuleWithoutPolicy = true;
                     }
                 } else if (AUTOMATIC_TAG.equals(tag)
                         || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) {
                     final String id = parser.getAttributeValue(null, RULE_ATT_ID);
-                    final ZenRule automaticRule = readRuleXml(parser);
-                    if (id != null && automaticRule != null) {
+                    if (id != null) {
+                        final ZenRule automaticRule = readRuleXml(parser);
                         automaticRule.id = id;
                         if (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag)) {
                             String deletedRuleKey = deletedRuleKey(automaticRule);
@@ -1074,7 +1084,7 @@
                         rt.manualRule.type = AutomaticZenRule.TYPE_OTHER;
                         rt.manualRule.condition = new Condition(
                                 rt.manualRule.conditionId != null ? rt.manualRule.conditionId
-                                        : Uri.EMPTY, "", STATE_TRUE);
+                                        : Uri.EMPTY, "", Condition.STATE_TRUE);
                     }
                 }
                 return rt;
@@ -1163,16 +1173,13 @@
         out.endTag(null, ZEN_TAG);
     }
 
+    @NonNull
     public static ZenRule readRuleXml(TypedXmlPullParser parser) {
         final ZenRule rt = new ZenRule();
         rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
         rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
         final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
-        rt.zenMode = tryParseZenMode(zen, -1);
-        if (rt.zenMode == -1) {
-            Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
-            return null;
-        }
+        rt.zenMode = tryParseZenMode(zen, ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
         rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
         rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
@@ -2492,6 +2499,16 @@
 
     // ==== End built-in system conditions ====
 
+    /** Generate the rule id for the implicit rule for the specified package. */
+    public static String implicitRuleId(String forPackage) {
+        return IMPLICIT_RULE_ID_PREFIX + forPackage;
+    }
+
+    /** Returns whether the rule id corresponds to an implicit rule. */
+    public static boolean isImplicitRuleId(@NonNull String ruleId) {
+        return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
+    }
+
     private static int[] tryParseHourAndMinute(String value) {
         if (TextUtils.isEmpty(value)) return null;
         final int i = value.indexOf('.');
@@ -2540,10 +2557,34 @@
     }
 
     public static class ZenRule implements Parcelable {
+
+        /** No manual override. Rule owner can decide its state. */
+        public static final int OVERRIDE_NONE = 0;
+        /**
+         * User has manually activated a mode. This will temporarily overrule the rule owner's
+         * decision to deactivate it (see {@link #reconsiderConditionOverride}).
+         */
+        public static final int OVERRIDE_ACTIVATE = 1;
+        /**
+         * User has manually deactivated an active mode, or setting ZEN_MODE_OFF (for the few apps
+         * still allowed to do that) snoozed the mode. This will temporarily overrule the rule
+         * owner's decision to activate it (see {@link #reconsiderConditionOverride}).
+         */
+        public static final int OVERRIDE_DEACTIVATE = 2;
+
+        @IntDef(prefix = { "OVERRIDE" }, value = {
+                OVERRIDE_NONE,
+                OVERRIDE_ACTIVATE,
+                OVERRIDE_DEACTIVATE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ConditionOverride {}
+
         @UnsupportedAppUsage
         public boolean enabled;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public boolean snoozing;         // user manually disabled this instance
+        @Deprecated
+        public boolean snoozing; // user manually disabled this instance. Obsolete with MODES_UI
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public String name;              // required for automatic
         @UnsupportedAppUsage
@@ -2566,7 +2607,7 @@
         @AutomaticZenRule.Type
         public int type = AutomaticZenRule.TYPE_UNKNOWN;
         public String triggerDescription;
-        public String iconResName;
+        @Nullable public String iconResName;
         public boolean allowManualInvocation;
         @AutomaticZenRule.ModifiableField public int userModifiedFields;
         @ZenPolicy.ModifiableField public int zenPolicyUserModifiedFields;
@@ -2579,6 +2620,15 @@
         // ZenPolicy, so we store them here, only for the manual rule.
         @FlaggedApi(Flags.FLAG_MODES_UI)
         int legacySuppressedEffects;
+        /**
+         * Signals a user's action to (temporarily or permanently) activate or deactivate this
+         * rule, overruling the condition set by the owner. This value is not stored to disk, as
+         * it shouldn't survive reboots or be involved in B&R. It might be reset by certain
+         * owner-provided state transitions as well.
+         */
+        @FlaggedApi(Flags.FLAG_MODES_UI)
+        @ConditionOverride
+        int conditionOverride = OVERRIDE_NONE;
 
         public ZenRule() { }
 
@@ -2620,6 +2670,7 @@
                 if (Flags.modesUi()) {
                     disabledOrigin = source.readInt();
                     legacySuppressedEffects = source.readInt();
+                    conditionOverride = source.readInt();
                 }
             }
         }
@@ -2698,6 +2749,7 @@
                 if (Flags.modesUi()) {
                     dest.writeInt(disabledOrigin);
                     dest.writeInt(legacySuppressedEffects);
+                    dest.writeInt(conditionOverride);
                 }
             }
         }
@@ -2708,9 +2760,16 @@
                     .append("id=").append(id)
                     .append(",state=").append(condition == null ? "STATE_FALSE"
                             : Condition.stateToString(condition.state))
-                    .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
-                    .append(",snoozing=").append(snoozing)
-                    .append(",name=").append(name)
+                    .append(",enabled=").append(String.valueOf(enabled).toUpperCase());
+
+            if (Flags.modesUi()) {
+                sb.append(",conditionOverride=")
+                        .append(conditionOverrideToString(conditionOverride));
+            } else {
+                sb.append(",snoozing=").append(snoozing);
+            }
+
+            sb.append(",name=").append(name)
                     .append(",zenMode=").append(Global.zenModeToString(zenMode))
                     .append(",conditionId=").append(conditionId)
                     .append(",pkg=").append(pkg)
@@ -2753,6 +2812,15 @@
             return sb.append(']').toString();
         }
 
+        private static String conditionOverrideToString(@ConditionOverride int value) {
+            return switch(value) {
+                case OVERRIDE_ACTIVATE -> "OVERRIDE_ACTIVATE";
+                case OVERRIDE_DEACTIVATE -> "OVERRIDE_DEACTIVATE";
+                case OVERRIDE_NONE -> "OVERRIDE_NONE";
+                default -> "UNKNOWN";
+            };
+        }
+
         /** @hide */
         // TODO: add configuration activity
         public void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2763,7 +2831,11 @@
             proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
             proto.write(ZenRuleProto.ENABLED, enabled);
             proto.write(ZenRuleProto.ENABLER, enabler);
-            proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+            if (Flags.modesApi() && Flags.modesUi()) {
+                proto.write(ZenRuleProto.IS_SNOOZING, conditionOverride == OVERRIDE_DEACTIVATE);
+            } else {
+                proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
+            }
             proto.write(ZenRuleProto.ZEN_MODE, zenMode);
             if (conditionId != null) {
                 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
@@ -2816,7 +2888,8 @@
                 if (Flags.modesUi()) {
                     finalEquals = finalEquals
                             && other.disabledOrigin == disabledOrigin
-                            && other.legacySuppressedEffects == legacySuppressedEffects;
+                            && other.legacySuppressedEffects == legacySuppressedEffects
+                            && other.conditionOverride == conditionOverride;
                 }
             }
 
@@ -2832,7 +2905,8 @@
                             zenDeviceEffects, modified, allowManualInvocation, iconResName,
                             triggerDescription, type, userModifiedFields,
                             zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
-                            deletionInstant, disabledOrigin, legacySuppressedEffects);
+                            deletionInstant, disabledOrigin, legacySuppressedEffects,
+                            conditionOverride);
                 } else {
                     return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                             component, configurationActivity, pkg, id, enabler, zenPolicy,
@@ -2858,8 +2932,74 @@
             }
         }
 
+        // TODO: b/363193376 - Rename to isActive()
         public boolean isAutomaticActive() {
-            return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+            if (Flags.modesApi() && Flags.modesUi()) {
+                if (!enabled || getPkg() == null) {
+                    return false;
+                } else if (conditionOverride == OVERRIDE_ACTIVATE) {
+                    return true;
+                } else if (conditionOverride == OVERRIDE_DEACTIVATE) {
+                    return false;
+                } else {
+                    return isTrueOrUnknown();
+                }
+            } else {
+                return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
+            }
+        }
+
+        @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+        @ConditionOverride
+        public int getConditionOverride() {
+            if (Flags.modesApi() && Flags.modesUi()) {
+                return conditionOverride;
+            } else {
+                return snoozing ? OVERRIDE_DEACTIVATE : OVERRIDE_NONE;
+            }
+        }
+
+        public void setConditionOverride(@ConditionOverride int value) {
+            if (Flags.modesApi() && Flags.modesUi()) {
+                conditionOverride = value;
+            } else {
+                if (value == OVERRIDE_ACTIVATE) {
+                    Slog.wtf(TAG, "Shouldn't set OVERRIDE_ACTIVATE if MODES_UI is off");
+                } else if (value == OVERRIDE_DEACTIVATE) {
+                    snoozing = true;
+                } else if (value == OVERRIDE_NONE) {
+                    snoozing = false;
+                }
+            }
+        }
+
+        public void resetConditionOverride() {
+            setConditionOverride(OVERRIDE_NONE);
+        }
+
+        /**
+         * Possibly remove the override, depending on the rule owner's intended state.
+         *
+         * <p>This allows rule owners to "take over" manually-provided state with their smartness,
+         * but only once both agree.
+         *
+         * <p>For example, a manually activated rule wins over rule owner's opinion that it should
+         * be off, until the owner says it should be on, at which point it will turn off (without
+         * manual intervention) when the rule owner says it should be off. And symmetrically for
+         * manual deactivation (which used to be called "snoozing").
+         */
+        public void reconsiderConditionOverride() {
+            if (Flags.modesApi() && Flags.modesUi()) {
+                if (conditionOverride == OVERRIDE_ACTIVATE && isTrueOrUnknown()) {
+                    resetConditionOverride();
+                } else if (conditionOverride == OVERRIDE_DEACTIVATE && !isTrueOrUnknown()) {
+                    resetConditionOverride();
+                }
+            } else {
+                if (snoozing && !isTrueOrUnknown()) {
+                    snoozing = false;
+                }
+            }
         }
 
         public String getPkg() {
diff --git a/core/java/android/service/notification/ZenModeDiff.java b/core/java/android/service/notification/ZenModeDiff.java
index a37e227..05c2a9c 100644
--- a/core/java/android/service/notification/ZenModeDiff.java
+++ b/core/java/android/service/notification/ZenModeDiff.java
@@ -454,6 +454,8 @@
      */
     public static class RuleDiff extends BaseDiff {
         public static final String FIELD_ENABLED = "enabled";
+        public static final String FIELD_CONDITION_OVERRIDE = "conditionOverride";
+        @Deprecated
         public static final String FIELD_SNOOZING = "snoozing";
         public static final String FIELD_NAME = "name";
         public static final String FIELD_ZEN_MODE = "zenMode";
@@ -507,8 +509,15 @@
             if (from.enabled != to.enabled) {
                 addField(FIELD_ENABLED, new FieldDiff<>(from.enabled, to.enabled));
             }
-            if (from.snoozing != to.snoozing) {
-                addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+            if (Flags.modesApi() && Flags.modesUi()) {
+                if (from.conditionOverride != to.conditionOverride) {
+                    addField(FIELD_CONDITION_OVERRIDE,
+                            new FieldDiff<>(from.conditionOverride, to.conditionOverride));
+                }
+            } else {
+                if (from.snoozing != to.snoozing) {
+                    addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
+                }
             }
             if (!Objects.equals(from.name, to.name)) {
                 addField(FIELD_NAME, new FieldDiff<>(from.name, to.name));
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 2669391..be0d7b3 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -393,6 +393,46 @@
     }
 
     /**
+     * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+     * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_ALARMS} or an
+     * {@link android.app.AutomaticZenRule} specifies said filter.
+     *
+     * <p>Note that <em>visual effects</em> for filtered notifications are unset in this base
+     * policy, so should be merged on top of the default policy's visual effects (see
+     * {@link #overwrittenWith(ZenPolicy)}).
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static ZenPolicy getBasePolicyInterruptionFilterAlarms() {
+        return new ZenPolicy.Builder()
+                .disallowAllSounds()
+                .allowAlarms(true)
+                .allowMedia(true)
+                .allowPriorityChannels(false)
+                .build();
+    }
+
+    /**
+     * Base Zen Policy used when {@link android.app.NotificationManager#setInterruptionFilter} is
+     * called with {@link android.app.NotificationManager#INTERRUPTION_FILTER_NONE} or an
+     * {@link android.app.AutomaticZenRule} specifies said filter.
+     *
+     * <p>Note that <em>visual effects</em> for filtered notifications are unset in this base
+     * policy, so it should be merged on top of the device default policy's visual effects (see
+     * {@link #overwrittenWith(ZenPolicy)}).
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_MODES_API)
+    public static ZenPolicy getBasePolicyInterruptionFilterNone() {
+        return new ZenPolicy.Builder()
+                .disallowAllSounds()
+                .allowPriorityChannels(false)
+                .build();
+    }
+
+    /**
      * Conversation type that can bypass DND.
      * @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
      * {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 66d08f9..ad457ce 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -25,8 +25,10 @@
 import static android.graphics.Matrix.MSKEW_Y;
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.flags.Flags.disableDrawWakeLock;
 
 import static com.android.window.flags.Flags.FLAG_OFFLOAD_COLOR_EXTRACTION;
+import static com.android.window.flags.Flags.noDuplicateSurfaceDestroyedEvents;
 import static com.android.window.flags.Flags.noConsecutiveVisibilityEvents;
 import static com.android.window.flags.Flags.noVisibilityEventOnDisplayStateChange;
 import static com.android.window.flags.Flags.offloadColorExtraction;
@@ -50,6 +52,7 @@
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -207,6 +210,15 @@
     private boolean mIsWearOs;
 
     /**
+     * This change disables the {@code DRAW_WAKE_LOCK}, an internal wakelock acquired per-frame
+     * duration display DOZE. It was added to allow animation during AOD. This wakelock consumes
+     * battery severely if the animation is too heavy, so, it will be removed.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long DISABLE_DRAW_WAKE_LOCK_WALLPAPER = 361433696L;
+
+    /**
      * Wear products currently force a slight scaling transition to wallpapers
      * when the QSS is opened. However, on Wear 6 (SDK 35) and above, 1P watch faces
      * will be expected to either implement their own scaling, or to override this
@@ -255,6 +267,7 @@
          */
         private boolean mIsScreenTurningOn;
         boolean mReportedVisible;
+        boolean mReportedSurfaceCreated;
         boolean mDestroyed;
         // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
         // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once
@@ -360,6 +373,8 @@
         private SurfaceControl mScreenshotSurfaceControl;
         private Point mScreenshotSize = new Point();
 
+        private final boolean mDisableDrawWakeLock;
+
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
                 mRequestedFormat = PixelFormat.RGBX_8888;
@@ -404,6 +419,9 @@
             }
 
             private void prepareToDraw() {
+                if (mDisableDrawWakeLock) {
+                    return;
+                }
                 if (mDisplayState == Display.STATE_DOZE) {
                     try {
                         mSession.pokeDrawLock(mWindow);
@@ -544,6 +562,8 @@
         public Engine(Supplier<Long> clockFunction, Handler handler) {
             mClockFunction = clockFunction;
             mHandler = handler;
+            mDisableDrawWakeLock = CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK_WALLPAPER)
+                    && disableDrawWakeLock();
         }
 
         /**
@@ -1076,6 +1096,9 @@
             out.print(prefix); out.print("mDisplay="); out.println(mDisplay);
             out.print(prefix); out.print("mCreated="); out.print(mCreated);
                     out.print(" mSurfaceCreated="); out.print(mSurfaceCreated);
+                    if (noDuplicateSurfaceDestroyedEvents()) {
+                        out.print(" mReportedSurfaceCreated="); out.print(mReportedSurfaceCreated);
+                    }
                     out.print(" mIsCreating="); out.print(mIsCreating);
                     out.print(" mDrawingAllowed="); out.println(mDrawingAllowed);
             out.print(prefix); out.print("mWidth="); out.print(mWidth);
@@ -1381,6 +1404,7 @@
                         if (surfaceCreating) {
                             mIsCreating = true;
                             didSurface = true;
+                            mReportedSurfaceCreated = true;
                             if (DEBUG) Log.v(TAG, "onSurfaceCreated("
                                     + mSurfaceHolder + "): " + this);
                             Trace.beginSection("WPMS.Engine.onSurfaceCreated");
@@ -2264,8 +2288,10 @@
         }
 
         void reportSurfaceDestroyed() {
-            if (mSurfaceCreated) {
+            if ((!noDuplicateSurfaceDestroyedEvents() && mSurfaceCreated)
+                    || (noDuplicateSurfaceDestroyedEvents() && mReportedSurfaceCreated)) {
                 mSurfaceCreated = false;
+                mReportedSurfaceCreated = false;
                 mSurfaceHolder.ungetCallbacks();
                 SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                 if (callbacks != null) {
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 6a54d23..711578c 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -350,7 +350,7 @@
     private static final char PARAGRAPH_SEPARATOR = '\n';
 
     /**
-     * Move the cusrot to the closest paragraph start offset.
+     * Move the cursor to the closest paragraph start offset.
      *
      * @param text the spannable text
      * @param layout layout to be used for drawing.
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6ea462e..032f592 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -84,6 +84,8 @@
 import java.util.Locale;
 import java.util.regex.Pattern;
 
[email protected]
[email protected]
 public class TextUtils {
     private static final String TAG = "TextUtils";
 
@@ -1704,7 +1706,7 @@
         return true;
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
+    @android.ravenwood.annotation.RavenwoodKeep
     /* package */ static char[] obtain(int len) {
         char[] buf;
 
@@ -1719,11 +1721,7 @@
         return buf;
     }
 
-    /* package */ static char[] obtain$ravenwood(int len) {
-        return new char[len];
-    }
-
-    @android.ravenwood.annotation.RavenwoodReplace
+    @android.ravenwood.annotation.RavenwoodKeep
     /* package */ static void recycle(char[] temp) {
         if (temp.length > 1000)
             return;
@@ -1733,10 +1731,6 @@
         }
     }
 
-    /* package */ static void recycle$ravenwood(char[] temp) {
-        // Handled by typical GC
-    }
-
     /**
      * Html-encode the string.
      * @param s the string to be encoded
@@ -2161,6 +2155,7 @@
      *
      * Be careful: this code will need to be updated when vertical scripts will be supported
      */
+    @android.ravenwood.annotation.RavenwoodKeep
     public static int getLayoutDirectionFromLocale(Locale locale) {
         return ((locale != null && !locale.equals(Locale.ROOT)
                         && ULocale.forLocale(locale).isRightToLeft())
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index bb3f6c9..d33c95e 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -277,3 +277,30 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "typeface_cache_for_var_settings"
+  namespace: "text"
+  description: "Cache Typeface instance for font variation settings."
+  bug: "355462362"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "context_menu_hide_unavailable_items"
+  namespace: "text"
+  description: "Hide rather than disable unavailable Editor context menu items."
+  bug: "345709107"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "typeface_redesign"
+  namespace: "text"
+  description: "Decouple variation settings, weight and style information from Typeface class"
+  bug: "361260253"
+}
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index 04dba46..fb1bd17 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -48,6 +48,22 @@
 }
 
 flag {
+    name: "perfetto_wm_dump"
+    namespace: "windowing_tools"
+    description: "Migrate WindowManager dump to Perfetto"
+    is_fixed_read_only: true
+    bug: "323165543"
+}
+
+flag {
+    name: "perfetto_wm_dump_cts"
+    namespace: "windowing_tools"
+    description: "Migrate WindowManager dump in CTS tests to Perfetto"
+    is_fixed_read_only: true
+    bug: "323165543"
+}
+
+flag {
     name: "client_side_proto_logging"
     namespace: "windowing_tools"
     description: "Add support for client side protologging"
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index 4de7b62..b65471d 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -16,6 +16,8 @@
 
 package android.tracing.perfetto;
 
+import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.util.proto.ProtoInputStream;
 
 /**
@@ -41,6 +43,7 @@
      * @param configStream A ProtoInputStream to read the tracing instance's config.
      * @return A new data source instance setup with the provided config.
      */
+    @NonNull
     public abstract DataSourceInstanceType createInstance(
             ProtoInputStream configStream, int instanceIndex);
 
@@ -102,8 +105,8 @@
     /**
      * Override this method to create a custom TlsState object for your DataSource. A new instance
      * will be created per trace instance per thread.
-     *
      */
+    @Nullable
     public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) {
         return null;
     }
@@ -112,6 +115,7 @@
      * Override this method to create and use a custom IncrementalState object for your DataSource.
      *
      */
+    @Nullable
     public IncrementalStateType createIncrementalState(
             CreateIncrementalStateArgs<DataSourceInstanceType> args) {
         return null;
@@ -141,6 +145,7 @@
      * @return The DataSourceInstance at index instanceIndex.
      *         Null if the datasource instance at the requested index doesn't exist.
      */
+    @Nullable
     public DataSourceInstanceType getDataSourceInstanceLocked(int instanceIndex) {
         return (DataSourceInstanceType) nativeGetPerfettoInstanceLocked(mNativeObj, instanceIndex);
     }
@@ -159,6 +164,7 @@
      * @param rawConfig byte array of the PerfettoConfig encoded proto.
      * @return A new Java DataSourceInstance object.
      */
+    @NonNull
     private DataSourceInstanceType createInstance(byte[] rawConfig, int instanceIndex) {
         final ProtoInputStream inputStream = new ProtoInputStream(rawConfig);
         return this.createInstance(inputStream, instanceIndex);
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index f14485b..c5d3c1d 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -24,6 +24,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.FontScaleConverter;
 import android.os.SystemProperties;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.view.WindowManager;
 
 import java.lang.annotation.Retention;
@@ -45,6 +46,7 @@
  * </p>
  *
  */
+@RavenwoodKeepWholeClass
 public class DisplayMetrics {
 
     @IntDef(prefix = { "DENSITY_" }, value = {
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index fe536a6..22583ac 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -19,6 +19,7 @@
 import android.annotation.HalfFloat;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import libcore.util.FP16;
 
@@ -92,6 +93,7 @@
  * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
  */
 @SuppressWarnings("SimplifiableIfStatement")
+@RavenwoodKeepWholeClass
 public final class Half extends Number implements Comparable<Half> {
     /**
      * The number of bits used to represent a half-precision float value.
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index feb80cc..ca5798a 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -112,6 +112,11 @@
         }
     }
 
+    // @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+    public synchronized void clear() {
+        mLog.clear();
+    }
+
     public static class ReadOnlyLocalLog {
         private final LocalLog mLog;
         ReadOnlyLocalLog(LocalLog log) {
diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java
index be1ec41..9845f9e 100644
--- a/core/java/android/util/LruCache.java
+++ b/core/java/android/util/LruCache.java
@@ -18,7 +18,6 @@
 
 import android.compat.annotation.UnsupportedAppUsage;
 
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -226,16 +225,10 @@
         }
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     private Map.Entry<K, V> eldest() {
         return map.eldest();
     }
 
-    private Map.Entry<K, V> eldest$ravenwood() {
-        final Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
-        return it.hasNext() ? it.next() : null;
-    }
-
     /**
      * Removes the entry for {@code key} if it exists.
      *
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 3adbd68..9f54d9f 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.NetworkInfo;
+import android.net.NetworkCapabilities;
 import android.net.SntpClient;
 import android.os.Build;
 import android.os.SystemClock;
@@ -687,8 +687,16 @@
             if (connectivityManager == null) {
                 return false;
             }
-            final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
-
+            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);
             // 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
@@ -698,8 +706,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 (ni == null || !ni.isConnected()) {
-                if (LOGD) Log.d(TAG, "getNetwork: no connectivity");
+            if (!isConnectedToInternet) {
+                if (LOGD) Log.d(TAG, "getNetwork: no internet connectivity");
                 return false;
             }
             return true;
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index 17adb32..25f321e 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -16,6 +16,8 @@
 
 package android.util;
 
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
+
 import com.android.internal.R;
 
 /**
@@ -34,6 +36,7 @@
  * and not have static methods here but there is some concern about
  * performance since these methods are called during view drawing.
  */
+@RavenwoodKeepWholeClass
 public class StateSet {
     /**
      * The order here is very important to
diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING
index c681f86..64b2e6e 100644
--- a/core/java/android/util/TEST_MAPPING
+++ b/core/java/android/util/TEST_MAPPING
@@ -1,27 +1,11 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.util.CharsetUtilsTest"
-        },
-        {
-          "include-filter": "com.android.internal.util.FastDataTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_util_data_charset",
       "file_patterns": ["CharsetUtils|FastData"]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.util.XmlTest"
-        },
-        {
-          "include-filter": "android.util.BinaryXmlTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_xml",
       "file_patterns": ["Xml"]
     }
   ],
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 9668b6ad..26ab588 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -22,6 +22,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.content.pm.ActivityInfo.Config;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -30,6 +31,7 @@
  * Container for a dynamically typed data value.  Primarily used with
  * {@link android.content.res.Resources} for holding resource values.
  */
+@RavenwoodKeepWholeClass
 public class TypedValue {
     /** The value contains no data. */
     public static final int TYPE_NULL = 0x00;
@@ -827,4 +829,3 @@
         return sb.toString();
     }
 }
-
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 7668eec..3ae470a 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.util.apk.SourceStampVerifierTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_util_apk"
     }
   ],
   "presubmit-large": [
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 15b0c13..82c52a6 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -314,6 +314,8 @@
      * @hide
      * @see #getFlags()
      */
+    @SuppressLint("UnflaggedApi") // Promotion to TestApi
+    @TestApi
     public static final int FLAG_ALWAYS_UNLOCKED = 1 << 9;
 
     /**
@@ -323,6 +325,8 @@
      * @hide
      * @see #getFlags()
      */
+    @SuppressLint("UnflaggedApi") // Promotion to TestApi
+    @TestApi
     public static final int FLAG_TOUCH_FEEDBACK_DISABLED = 1 << 10;
 
     /**
@@ -336,6 +340,8 @@
      * @see #FLAG_TRUSTED
      * @hide
      */
+    @SuppressLint("UnflaggedApi") // Promotion to TestApi
+    @TestApi
     public static final int FLAG_OWN_FOCUS = 1 << 11;
 
     /**
@@ -642,6 +648,8 @@
      * @hide
      */
     // TODO (b/114338689): Remove the flag and use WindowManager#REMOVE_CONTENT_MODE_DESTROY
+    @SuppressLint("UnflaggedApi") // Promotion to TestApi
+    @TestApi
     public static final int REMOVE_MODE_DESTROY_CONTENT = 1;
 
     /** @hide */
@@ -2344,6 +2352,8 @@
          * SurfaceControl.DisplayMode
          * @hide
          */
+        @SuppressWarnings("UnflaggedApi") // For testing only
+        @TestApi
         public boolean isSynthetic() {
             return mIsSynthetic;
         }
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index bb508493..149d992 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -21,10 +21,12 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import java.util.Objects;
 
 /** @hide */
+@RavenwoodKeepWholeClass
 public class DisplayAdjustments {
     public static final DisplayAdjustments DEFAULT_DISPLAY_ADJUSTMENTS = new DisplayAdjustments();
 
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index ccec89b..ab9bd1f 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -350,12 +350,13 @@
             return;
         }
 
+        final View focusedView = getFocusedView();
+
         if (!view.isAutoHandwritingEnabled()) {
-            clearFocusedView(view);
+            clearFocusedView(focusedView);
             return;
         }
 
-        final View focusedView = getFocusedView();
         if (focusedView == view) {
             return;
         }
@@ -729,13 +730,13 @@
 
         /* The distance between point (x, y) and rect, there are 2 basic cases:
          * a) The distance is the distance from (x, y) to the closest corner on rect.
-         *                    o |     |
+         *          o |     |
          *         ---+-----+---
          *            |     |
          *         ---+-----+---
          *            |     |
          * b) The distance is the distance from (x, y) to the closest edge on rect.
-         *                      |  o  |
+         *            |  o  |
          *         ---+-----+---
          *            |     |
          *         ---+-----+---
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index c7e93c1..b801465 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -149,15 +149,17 @@
 
     private void setPreCommitProgress(float progress) {
         if (isHideAnimationInProgress()) return;
+        setInterpolatedProgress(BACK_GESTURE.getInterpolation(progress) * PEEK_FRACTION);
+    }
+
+    private void setInterpolatedProgress(float progress) {
         if (mWindowInsetsAnimationController != null) {
             float hiddenY = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
             float shownY = mWindowInsetsAnimationController.getShownStateInsets().bottom;
             float imeHeight = shownY - hiddenY;
-            float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
-            int newY = (int) (imeHeight - interpolatedProgress * (imeHeight * PEEK_FRACTION));
+            int newY = (int) (imeHeight - progress * imeHeight);
             if (mStartRootScrollY != 0) {
-                mViewRoot.setScrollY(
-                        (int) (mStartRootScrollY * (1 - interpolatedProgress * PEEK_FRACTION)));
+                mViewRoot.setScrollY((int) (mStartRootScrollY * (1 - progress)));
             }
             mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, newY), 1f,
                     progress);
@@ -171,21 +173,14 @@
             return;
         }
         mTriggerBack = triggerBack;
-        int currentBottomInset = mWindowInsetsAnimationController.getCurrentInsets().bottom;
-        int targetBottomInset;
-        if (triggerBack) {
-            targetBottomInset = mWindowInsetsAnimationController.getHiddenStateInsets().bottom;
-        } else {
-            targetBottomInset = mWindowInsetsAnimationController.getShownStateInsets().bottom;
-        }
-        mPostCommitAnimator = ValueAnimator.ofFloat(currentBottomInset, targetBottomInset);
+        float targetProgress = triggerBack ? 1f : 0f;
+        mPostCommitAnimator = ValueAnimator.ofFloat(
+                BACK_GESTURE.getInterpolation(mLastProgress) * PEEK_FRACTION, targetProgress);
         mPostCommitAnimator.setInterpolator(
                 triggerBack ? STANDARD_ACCELERATE : EMPHASIZED_DECELERATE);
         mPostCommitAnimator.addUpdateListener(animation -> {
-            int bottomInset = (int) ((float) animation.getAnimatedValue());
             if (mWindowInsetsAnimationController != null) {
-                mWindowInsetsAnimationController.setInsetsAndAlpha(Insets.of(0, 0, 0, bottomInset),
-                        1f, animation.getAnimatedFraction());
+                setInterpolatedProgress((float) animation.getAnimatedValue());
             } else {
                 reset();
             }
@@ -213,14 +208,8 @@
             notifyHideIme();
             // requesting IME as invisible during post-commit
             mInsetsController.setRequestedVisibleTypes(0, ime());
-            // Changes the animation state. This also notifies RootView of changed insets, which
-            // causes it to reset its scrollY to 0f (animated) if it was panned
             mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
         }
-        if (mStartRootScrollY != 0 && !triggerBack) {
-            // This causes RootView to update its scroll back to the panned position
-            mInsetsController.getHost().notifyInsetsChanged();
-        }
     }
 
     private void notifyHideIme() {
@@ -282,6 +271,10 @@
         return mPostCommitAnimator != null && mTriggerBack;
     }
 
+    boolean isAnimationInProgress() {
+        return mIsPreCommitAnimationInProgress || mWindowInsetsAnimationController != null;
+    }
+
     /**
      * Dump information about this ImeBackAnimationController
      *
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 6343313..e90b1c0 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -70,7 +70,14 @@
                         "ImeInsetsSourceConsumer#onAnimationFinished",
                         mController.getHost().getInputMethodManager(), null /* icProto */);
             }
-            boolean insetsChanged = super.onAnimationStateChanged(running);
+            boolean insetsChanged = false;
+            if (Flags.predictiveBackIme() && !running && isShowRequested()
+                    && mAnimationState == ANIMATION_STATE_HIDE) {
+                // A user controlled hide animation may have ended in the shown state (e.g.
+                // cancelled predictive back animation) -> Insets need to be reset to shown.
+                insetsChanged |= applyLocalVisibilityOverride();
+            }
+            insetsChanged |= super.onAnimationStateChanged(running);
             if (running && !isShowRequested()
                     && mController.isPredictiveBackImeHideAnimInProgress()) {
                 // IME predictive back animation switched from pre-commit to post-commit.
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6568912..91e9230 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -35,8 +35,8 @@
 import static android.view.InsetsController.LayoutInsetsDuringAnimation;
 import static android.view.InsetsSource.ID_IME;
 import static android.view.InsetsSource.SIDE_BOTTOM;
-import static android.view.InsetsSource.SIDE_NONE;
 import static android.view.InsetsSource.SIDE_LEFT;
+import static android.view.InsetsSource.SIDE_NONE;
 import static android.view.InsetsSource.SIDE_RIGHT;
 import static android.view.InsetsSource.SIDE_TOP;
 import static android.view.WindowInsets.Type.ime;
@@ -100,6 +100,8 @@
     private @InsetsType int mControllingTypes;
     private final InsetsAnimationControlCallbacks mController;
     private final WindowInsetsAnimation mAnimation;
+    private final long mDurationMs;
+    private final Interpolator mInterpolator;
     /** @see WindowInsetsAnimationController#hasZeroInsetsIme */
     private final boolean mHasZeroInsetsIme;
     private final CompatibilityInfo.Translator mTranslator;
@@ -120,8 +122,8 @@
     @VisibleForTesting
     public InsetsAnimationControlImpl(SparseArray<InsetsSourceControl> controls,
             @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener,
-            @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs,
-            Interpolator interpolator, @AnimationType int animationType,
+            @InsetsType int types, InsetsAnimationControlCallbacks controller,
+            InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
             CompatibilityInfo.Translator translator, @Nullable ImeTracker.Token statsToken) {
         mControls = controls;
@@ -155,8 +157,10 @@
         }
         mPendingInsets = mCurrentInsets;
 
-        mAnimation = new WindowInsetsAnimation(mTypes, interpolator,
-                durationMs);
+        mDurationMs = insetsAnimationSpec.getDurationMs(mHasZeroInsetsIme);
+        mInterpolator = insetsAnimationSpec.getInsetsInterpolator(mHasZeroInsetsIme);
+
+        mAnimation = new WindowInsetsAnimation(mTypes, mInterpolator, mDurationMs);
         mAnimation.setAlpha(getCurrentAlpha());
         mAnimationType = animationType;
         mLayoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
@@ -186,6 +190,16 @@
     }
 
     @Override
+    public long getDurationMs() {
+        return mDurationMs;
+    }
+
+    @Override
+    public Interpolator getInsetsInterpolator() {
+        return mInterpolator;
+    }
+
+    @Override
     public void setReadyDispatched(boolean dispatched) {
         mReadyDispatched = dispatched;
     }
diff --git a/core/java/android/view/InsetsAnimationSpec.java b/core/java/android/view/InsetsAnimationSpec.java
new file mode 100644
index 0000000..7ad6661
--- /dev/null
+++ b/core/java/android/view/InsetsAnimationSpec.java
@@ -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 android.view;
+
+import android.view.animation.Interpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Used by {@link InsetsAnimationControlImpl}
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public interface InsetsAnimationSpec {
+    /**
+     * @param hasZeroInsetsIme whether IME has no insets (floating, fullscreen or non-overlapping).
+     * @return Duration of animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}
+     */
+    long getDurationMs(boolean hasZeroInsetsIme);
+    /**
+     * @param hasZeroInsetsIme whether IME has no insets (floating, fullscreen or non-overlapping).
+     * @return The interpolator used for the animation
+     */
+    Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme);
+}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 1b3b3eb..fc185bc 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -33,7 +33,6 @@
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
-import android.view.animation.Interpolator;
 import android.view.inputmethod.ImeTracker;
 
 /**
@@ -110,15 +109,15 @@
     @UiThread
     public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls,
             @Nullable Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener,
-            @InsetsType int types, InsetsAnimationControlCallbacks controller, long durationMs,
-            Interpolator interpolator, @AnimationType int animationType,
+            @InsetsType int types, InsetsAnimationControlCallbacks controller,
+            InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
             CompatibilityInfo.Translator translator, Handler mainThreadHandler,
             @Nullable ImeTracker.Token statsToken) {
         mMainThreadHandler = mainThreadHandler;
         mOuterCallbacks = controller;
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types,
-                mCallbacks, durationMs, interpolator, animationType, layoutInsetsDuringAnimation,
+                mCallbacks, insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
                 translator, statsToken);
         InsetsAnimationThread.getHandler().post(() -> {
             if (mControl.isCancelled()) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7896cbd..8fdf91a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -366,7 +366,7 @@
      * animate insets.
      */
     public static class InternalAnimationControlListener
-            implements WindowInsetsAnimationControlListener {
+            implements WindowInsetsAnimationControlListener, InsetsAnimationSpec {
 
         private WindowInsetsAnimationController mController;
         private ValueAnimator mAnimator;
@@ -374,7 +374,6 @@
         private final boolean mHasAnimationCallbacks;
         private final @InsetsType int mRequestedTypes;
         private final @Behavior int mBehavior;
-        private final long mDurationMs;
         private final boolean mDisable;
         private final int mFloatingImeBottomInset;
         private final WindowInsetsAnimationControlListener mLoggingListener;
@@ -388,7 +387,6 @@
             mHasAnimationCallbacks = hasAnimationCallbacks;
             mRequestedTypes = requestedTypes;
             mBehavior = behavior;
-            mDurationMs = calculateDurationMs();
             mDisable = disable;
             mFloatingImeBottomInset = floatingImeBottomInset;
             mLoggingListener = loggingListener;
@@ -407,13 +405,14 @@
                 onAnimationFinish();
                 return;
             }
+            final boolean hasZeroInsetsIme = controller.hasZeroInsetsIme();
             mAnimator = ValueAnimator.ofFloat(0f, 1f);
-            mAnimator.setDuration(mDurationMs);
+            mAnimator.setDuration(controller.getDurationMs());
             mAnimator.setInterpolator(new LinearInterpolator());
             Insets hiddenInsets = controller.getHiddenStateInsets();
             // IME with zero insets is a special case: it will animate-in from offscreen and end
             // with final insets of zero and vice-versa.
-            hiddenInsets = controller.hasZeroInsetsIme()
+            hiddenInsets = hasZeroInsetsIme
                     ? Insets.of(hiddenInsets.left, hiddenInsets.top, hiddenInsets.right,
                             mFloatingImeBottomInset)
                     : hiddenInsets;
@@ -423,7 +422,7 @@
             Insets end = mShow
                     ? controller.getShownStateInsets()
                     : hiddenInsets;
-            Interpolator insetsInterpolator = getInsetsInterpolator();
+            Interpolator insetsInterpolator = controller.getInsetsInterpolator();
             Interpolator alphaInterpolator = getAlphaInterpolator();
             mAnimator.addUpdateListener(animation -> {
                 float rawFraction = animation.getAnimatedFraction();
@@ -486,9 +485,10 @@
             }
         }
 
-        protected Interpolator getInsetsInterpolator() {
+        @Override
+        public Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme) {
             if ((mRequestedTypes & ime()) != 0) {
-                if (mHasAnimationCallbacks) {
+                if (mHasAnimationCallbacks && !hasZeroInsetsIme) {
                     return SYNC_IME_INTERPOLATOR;
                 } else if (mShow) {
                     return LINEAR_OUT_SLOW_IN_INTERPOLATOR;
@@ -507,10 +507,9 @@
 
         Interpolator getAlphaInterpolator() {
             if ((mRequestedTypes & ime()) != 0) {
-                if (mHasAnimationCallbacks) {
+                if (mHasAnimationCallbacks && !mController.hasZeroInsetsIme()) {
                     return input -> 1f;
                 } else if (mShow) {
-
                     // Alpha animation takes half the time with linear interpolation;
                     return input -> Math.min(1f, 2 * input);
                 } else {
@@ -534,16 +533,10 @@
             if (DEBUG) Log.d(TAG, "onAnimationFinish showOnFinish: " + mShow);
         }
 
-        /**
-         * To get the animation duration in MS.
-         */
-        public long getDurationMs() {
-            return mDurationMs;
-        }
-
-        private long calculateDurationMs() {
+        @Override
+        public long getDurationMs(boolean hasZeroInsetsIme) {
             if ((mRequestedTypes & ime()) != 0) {
-                if (mHasAnimationCallbacks) {
+                if (mHasAnimationCallbacks && !hasZeroInsetsIme) {
                     return ANIMATION_DURATION_SYNC_IME_MS;
                 } else {
                     return ANIMATION_DURATION_UNSYNC_IME_MS;
@@ -593,13 +586,13 @@
     private static class PendingControlRequest {
 
         PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
-                long durationMs, Interpolator interpolator, @AnimationType int animationType,
+                InsetsAnimationSpec insetsAnimationSpec,
+                @AnimationType int animationType,
                 @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
                 CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
             this.types = types;
             this.listener = listener;
-            this.durationMs = durationMs;
-            this.interpolator = interpolator;
+            this.mInsetsAnimationSpec = insetsAnimationSpec;
             this.animationType = animationType;
             this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
             this.cancellationSignal = cancellationSignal;
@@ -608,8 +601,7 @@
 
         @InsetsType int types;
         final WindowInsetsAnimationControlListener listener;
-        final long durationMs;
-        final Interpolator interpolator;
+        final InsetsAnimationSpec mInsetsAnimationSpec;
         final @AnimationType int animationType;
         final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
         final CancellationSignal cancellationSignal;
@@ -1201,13 +1193,12 @@
 
         // We are about to playing the default animation. Passing a null frame indicates the
         // controlled types should be animated regardless of the frame.
-        controlAnimationUnchecked(
-                pendingRequest.types, pendingRequest.cancellationSignal,
-                pendingRequest.listener, null /* frame */,
-                true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
-                pendingRequest.animationType,
-                pendingRequest.layoutInsetsDuringAnimation,
-                pendingRequest.useInsetsAnimationThread, statsToken);
+        controlAnimationUnchecked(pendingRequest.types, pendingRequest.cancellationSignal,
+                pendingRequest.listener, null /* frame */, true /* fromIme */,
+                pendingRequest.mInsetsAnimationSpec,
+                pendingRequest.animationType, pendingRequest.layoutInsetsDuringAnimation,
+                pendingRequest.useInsetsAnimationThread, statsToken,
+                false /* fromPredictiveBack */);
     }
 
     @Override
@@ -1317,7 +1308,10 @@
             WindowInsetsAnimationControlListener listener,
             boolean fromIme, long durationMs, @Nullable Interpolator interpolator,
             @AnimationType int animationType, boolean fromPredictiveBack) {
-        if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0) {
+        if ((mState.calculateUncontrollableInsetsFromFrame(mFrame) & types) != 0
+                || (fromPredictiveBack && ((mRequestedVisibleTypes & ime()) == 0))) {
+            // abort if insets are uncontrollable or if control request is from predictive back but
+            // there is already a hide anim in progress
             listener.onCancelled(null);
             return;
         }
@@ -1327,20 +1321,29 @@
                     mHost.getInputMethodManager(), null /* icProto */);
         }
 
+        InsetsAnimationSpec spec = new InsetsAnimationSpec() {
+            @Override
+            public long getDurationMs(boolean hasZeroInsetsIme) {
+                return durationMs;
+            }
+            @Override
+            public Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme) {
+                return interpolator;
+            }
+        };
         // TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
-        controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
-                interpolator, animationType,
-                getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
-                false /* useInsetsAnimationThread */, null);
+        controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, spec,
+                animationType, getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
+                false /* useInsetsAnimationThread */, null, fromPredictiveBack);
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
             @Nullable CancellationSignal cancellationSignal,
             WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
-            long durationMs, Interpolator interpolator,
-            @AnimationType int animationType,
+            InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
-            boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+            boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+            boolean fromPredictiveBack) {
         final boolean visible = layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
 
         // Basically, we accept the requested visibilities from the upstream callers...
@@ -1349,8 +1352,8 @@
         // However, we might reject the request in some cases, such as delaying showing IME or
         // rejecting showing IME.
         controlAnimationUncheckedInner(types, cancellationSignal, listener, frame, fromIme,
-                durationMs, interpolator, animationType, layoutInsetsDuringAnimation,
-                useInsetsAnimationThread, statsToken);
+                insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
+                useInsetsAnimationThread, statsToken, fromPredictiveBack);
 
         // We are finishing setting the requested visible types. Report them to the server
         // and/or the app.
@@ -1360,10 +1363,10 @@
     private void controlAnimationUncheckedInner(@InsetsType int types,
             @Nullable CancellationSignal cancellationSignal,
             WindowInsetsAnimationControlListener listener, @Nullable Rect frame, boolean fromIme,
-            long durationMs, Interpolator interpolator,
-            @AnimationType int animationType,
+            InsetsAnimationSpec insetsAnimationSpec, @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
-            boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken) {
+            boolean useInsetsAnimationThread, @Nullable ImeTracker.Token statsToken,
+            boolean fromPredictiveBack) {
         if ((types & mTypesBeingCancelled) != 0) {
             final boolean monitoredAnimation =
                     animationType == ANIMATION_TYPE_SHOW || animationType == ANIMATION_TYPE_HIDE;
@@ -1418,8 +1421,8 @@
                     // TODO (b/323319146) remove layoutInsetsDuringAnimation from
                     //  PendingControlRequest, as it is now only used for showing
                     final PendingControlRequest request = new PendingControlRequest(types,
-                            listener, durationMs,
-                            interpolator, animationType, LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
+                            listener, insetsAnimationSpec, animationType,
+                            LAYOUT_INSETS_DURING_ANIMATION_SHOWN,
                             cancellationSignal, false /* useInsetsAnimationThread */);
                     mPendingImeControlRequest = request;
                     // only add a timeout when the control is not currently showing
@@ -1449,7 +1452,7 @@
             }
         } else {
             Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
-                    fromIme, types, controls, animationType, statsToken);
+                    fromIme, types, controls, animationType, statsToken, fromPredictiveBack);
             typesReady = typesReadyPair.first;
             boolean imeReady = typesReadyPair.second;
             if (DEBUG) {
@@ -1460,11 +1463,9 @@
             if (!imeReady) {
                 // IME isn't ready, all requested types will be animated once IME is ready
                 abortPendingImeControlRequest();
-                final PendingControlRequest request = new PendingControlRequest(types,
-                        listener, durationMs,
-                        interpolator, animationType, layoutInsetsDuringAnimation,
-                        cancellationSignal,
-                        useInsetsAnimationThread);
+                final PendingControlRequest request = new PendingControlRequest(types, listener,
+                        insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
+                        cancellationSignal, useInsetsAnimationThread);
                 mPendingImeControlRequest = request;
                 mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
                 if (DEBUG) Log.d(TAG, "Ime not ready. Create pending request");
@@ -1520,11 +1521,11 @@
 
         final InsetsAnimationControlRunner runner = useInsetsAnimationThread
                 ? new InsetsAnimationThreadControlRunner(controls,
-                        frame, mState, listener, typesReady, this, durationMs, interpolator,
-                        animationType, layoutInsetsDuringAnimation, mHost.getTranslator(),
-                        mHost.getHandler(), statsToken)
+                        frame, mState, listener, typesReady, this,
+                        insetsAnimationSpec, animationType, layoutInsetsDuringAnimation,
+                        mHost.getTranslator(), mHost.getHandler(), statsToken)
                 : new InsetsAnimationControlImpl(controls,
-                        frame, mState, listener, typesReady, this, durationMs, interpolator,
+                        frame, mState, listener, typesReady, this, insetsAnimationSpec,
                         animationType, layoutInsetsDuringAnimation, mHost.getTranslator(),
                         statsToken);
         if ((typesReady & WindowInsets.Type.ime()) != 0) {
@@ -1587,7 +1588,7 @@
      */
     private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types,
             SparseArray<InsetsSourceControl> controls, @AnimationType int animationType,
-            @Nullable ImeTracker.Token statsToken) {
+            @Nullable ImeTracker.Token statsToken, boolean fromPredictiveBack) {
         ImeTracker.forLogging().onProgress(statsToken,
                 ImeTracker.PHASE_CLIENT_COLLECT_SOURCE_CONTROLS);
 
@@ -1599,7 +1600,8 @@
                 continue;
             }
             boolean show = animationType == ANIMATION_TYPE_SHOW
-                    || animationType == ANIMATION_TYPE_USER;
+                    || (animationType == ANIMATION_TYPE_USER
+                            && (!fromPredictiveBack || !mHost.hasAnimationCallbacks()));
             boolean canRun = true;
             if (show) {
                 // Show request
@@ -1622,7 +1624,8 @@
                         break;
                 }
             } else {
-                consumer.requestHide(fromIme, statsToken);
+                consumer.requestHide(fromIme
+                        || (fromPredictiveBack && mHost.hasAnimationCallbacks()), statsToken);
             }
             if (!canRun) {
                 if (WARN) Log.w(TAG, String.format(
@@ -1677,9 +1680,10 @@
 
     private @LayoutInsetsDuringAnimation int getLayoutInsetsDuringAnimationMode(
             @InsetsType int types, boolean fromPredictiveBack) {
-        if (fromPredictiveBack) {
-            // When insets are animated by predictive back, we want insets to be shown to prevent a
-            // jump cut from shown to hidden at the start of the predictive back animation
+        if (fromPredictiveBack && !mHost.hasAnimationCallbacks()) {
+            // When insets are animated by predictive back and the app does not have an animation
+            // callback, we want insets to be shown to prevent a jump cut from shown to hidden at
+            // the start of the predictive back animation
             return LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
         }
         // Generally, we want to layout the opposite of the current state. This is to make animation
@@ -2023,10 +2027,11 @@
         // the controlled types should be animated regardless of the frame.
         controlAnimationUnchecked(
                 types, null /* cancellationSignal */, listener, null /* frame */, fromIme,
-                listener.getDurationMs(), listener.getInsetsInterpolator(),
+                listener /* insetsAnimationSpec */,
                 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
                 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
-                !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken);
+                !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken,
+                false /* fromPredictiveBack */);
     }
 
     /**
diff --git a/core/java/android/view/InsetsResizeAnimationRunner.java b/core/java/android/view/InsetsResizeAnimationRunner.java
index 6e62221..f90b841 100644
--- a/core/java/android/view/InsetsResizeAnimationRunner.java
+++ b/core/java/android/view/InsetsResizeAnimationRunner.java
@@ -233,6 +233,16 @@
     }
 
     @Override
+    public long getDurationMs() {
+        return 0;
+    }
+
+    @Override
+    public Interpolator getInsetsInterpolator() {
+        return null;
+    }
+
+    @Override
     public void setReadyDispatched(boolean dispatched) {
     }
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 477e35b..391d7573 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -310,21 +310,22 @@
         }
         final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
 
+        // If we don't have control or the leash (in case of the IME), we enforce the
+        // visibility to be hidden, as otherwise we would let the app know too early.
+        if (mSourceControl == null) {
+            if (DEBUG) {
+                Log.d(TAG, TextUtils.formatSimple(
+                        "applyLocalVisibilityOverride: No control in %s for type %s, "
+                                + "requestedVisible=%s",
+                        mController.getHost().getRootViewTitle(),
+                        WindowInsets.Type.toString(mType), requestedVisible));
+            }
+            return false;
+        }
         if (Flags.refactorInsetsController()) {
-            // If we don't have control or the leash (in case of the IME), we enforce the
-            // visibility to be hidden, as otherwise we would let the app know too early.
-            if (mSourceControl == null) {
-                if (DEBUG) {
-                    Log.d(TAG, TextUtils.formatSimple(
-                            "applyLocalVisibilityOverride: No control in %s for type %s, "
-                                    + "requestedVisible=%s",
-                            mController.getHost().getRootViewTitle(),
-                            WindowInsets.Type.toString(mType), requestedVisible));
-                }
-                return false;
-                // TODO(b/323136120) add a flag to the control, to define whether a leash is needed
-            } else if (mId != InsetsSource.ID_IME_CAPTION_BAR
-                    && mSourceControl.getLeash() == null) {
+            // TODO(b/323136120) add a flag to the control, to define whether a leash is
+            //  needed and make it generic for all types
+            if (mId == InsetsSource.ID_IME && mSourceControl.getLeash() == null) {
                 if (DEBUG) {
                     Log.d(TAG, TextUtils.formatSimple(
                             "applyLocalVisibilityOverride: Set the source visibility to false, as"
@@ -338,16 +339,6 @@
                 // changed state
                 return wasVisible;
             }
-        } else {
-            // If we don't have control, we are not able to change the visibility.
-            if (mSourceControl == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "applyLocalVisibilityOverride: No control in "
-                            + mController.getHost().getRootViewTitle()
-                            + " requestedVisible=" + requestedVisible);
-                }
-                return false;
-            }
         }
         if (source.isVisible() == requestedVisible) {
             return false;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 72d2d3b..326e34b 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -128,7 +128,16 @@
  *             ev.getPointerId(p), ev.getX(p), ev.getY(p));
  *     }
  * }
- * </code></pre></p>
+ * </code></pre></p><p>
+ * Developers should keep in mind that it is especially important to consume all samples
+ * in a batched event when processing relative values that report changes since the last
+ * event or sample. Examples of such relative axes include {@link #AXIS_RELATIVE_X},
+ * {@link #AXIS_RELATIVE_Y}, and many of the axes prefixed with {@code AXIS_GESTURE_}.
+ * In these cases, developers should first consume all historical values using
+ * {@link #getHistoricalAxisValue(int, int)} and then consume the current values using
+ * {@link #getAxisValue(int)} like in the example above, as these relative values are
+ * not accumulated in a batched event.
+ * </p>
  *
  * <h3>Device Types</h3>
  * <p>
@@ -1117,6 +1126,9 @@
      * the location but this axis reports the difference which allows the app to see
      * how the mouse is moved.
      * </ul>
+     * </p><p>
+     * These values are relative to the state from the last sample, not accumulated, so developers
+     * should make sure to process this axis value for all batched historical samples.
      * </p>
      *
      * @see #getAxisValue(int, int)
@@ -1130,6 +1142,9 @@
      * Axis constant: The movement of y position of a motion event.
      * <p>
      * This is similar to {@link #AXIS_RELATIVE_X} but for y-axis.
+     * </p><p>
+     * These values are relative to the state from the last sample, not accumulated, so developers
+     * should make sure to process this axis value for all batched historical samples.
      * </p>
      *
      * @see #getAxisValue(int, int)
@@ -1324,8 +1339,8 @@
      * swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of
      * -0.1.
      * </ul>
-     * These values are relative to the state from the last event, not accumulated, so developers
-     * should make sure to process this axis value for all batched historical events.
+     * These values are relative to the state from the last sample, not accumulated, so developers
+     * should make sure to process this axis value for all batched historical samples.
      * <p>
      * This axis is only set on the first pointer in a motion event.
      */
@@ -1345,8 +1360,8 @@
      * <li>For a touch pad, reports the distance that should be scrolled in the X axis as a result
      * of the user's two-finger scroll gesture, in display pixels.
      * </ul>
-     * These values are relative to the state from the last event, not accumulated, so developers
-     * should make sure to process this axis value for all batched historical events.
+     * These values are relative to the state from the last sample, not accumulated, so developers
+     * should make sure to process this axis value for all batched historical samples.
      * <p>
      * This axis is only set on the first pointer in a motion event.
      */
@@ -1367,8 +1382,8 @@
      * making a pinch gesture, as a proportion of the previous distance. For example, if the fingers
      * were 50 units apart and are now 52 units apart, the scale factor would be 1.04.
      * </ul>
-     * These values are relative to the state from the last event, not accumulated, so developers
-     * should make sure to process this axis value for all batched historical events.
+     * These values are relative to the state from the last sample, not accumulated, so developers
+     * should make sure to process this axis value for all batched historical samples.
      * <p>
      * This axis is only set on the first pointer in a motion event.
      */
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 1535145..815fd1c 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -556,12 +556,9 @@
                                 + "is a different type from the others. All frames should be the "
                                 + "same type.");
                     }
-                    if (drawableFrame.getIntrinsicWidth() != width ||
-                        drawableFrame.getIntrinsicHeight() != height) {
-                        throw new IllegalArgumentException("The bitmap size of " + i + "-th frame "
-                                + "is different. All frames should have the exact same size and "
-                                + "share the same hotspot.");
-                    }
+                    // TODO(b/361232935): Check when bitmap size of the ith frame is different
+                    // drawableFrame.getIntrinsicWidth() != width ||
+                    // drawableFrame.getIntrinsicHeight() != height
                     if (isVectorAnimation) {
                         drawableFrame = getBitmapDrawableFromVectorDrawable(resources,
                                 (VectorDrawable) drawableFrame, pointerScale);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9e4b27d..2dda835 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -445,16 +445,20 @@
         // Jank due to unknown reasons.
         public static final int UNKNOWN = 0x80;
 
-        public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs) {
+        public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
+                long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
             this.frameVsyncId = frameVsyncId;
             this.jankType = jankType;
             this.frameIntervalNs = frameIntervalNs;
-
+            this.scheduledAppFrameTimeNs = scheduledAppFrameTimeNs;
+            this.actualAppFrameTimeNs = actualAppFrameTimeNs;
         }
 
         public final long frameVsyncId;
         public final @JankType int jankType;
         public final long frameIntervalNs;
+        public final long scheduledAppFrameTimeNs;
+        public final long actualAppFrameTimeNs;
     }
 
     /**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 42d66ce..f7745d1 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -325,17 +325,62 @@
 
     private String mTag = TAG;
 
-    private final ISurfaceControlViewHostParent mSurfaceControlViewHostParent =
-            new ISurfaceControlViewHostParent.Stub() {
+    private static class SurfaceControlViewHostParent extends ISurfaceControlViewHostParent.Stub {
+
+        /**
+         * mSurfaceView is set in {@link #attach} and cleared in {@link #detach} to prevent
+         * temporary memory leaks. The remote process's ISurfaceControlViewHostParent binder
+         * reference extends this object's lifetime. If mSurfaceView is not cleared in
+         * {@link #detach}, then the SurfaceView and anything it references will not be promptly
+         * garbage collected.
+         */
+        @Nullable
+        private SurfaceView mSurfaceView;
+
+        void attach(SurfaceView sv) {
+            synchronized (this) {
+                try {
+                    sv.mSurfacePackage.getRemoteInterface().attachParentInterface(this);
+                    mSurfaceView = sv;
+                } catch (RemoteException e) {
+                    Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is alraedy "
+                            + "dead.");
+                }
+            }
+        }
+
+        void detach() {
+            synchronized (this) {
+                if (mSurfaceView == null) {
+                    return;
+                }
+                try {
+                    mSurfaceView.mSurfacePackage.getRemoteInterface().attachParentInterface(null);
+                } catch (RemoteException e) {
+                    Log.d(TAG, "Failed to remove parent interface from SCVH. Likely SCVH is "
+                            + "already dead");
+                }
+                mSurfaceView = null;
+            }
+        }
+
         @Override
         public void updateParams(WindowManager.LayoutParams[] childAttrs) {
-            mEmbeddedWindowParams.clear();
-            mEmbeddedWindowParams.addAll(Arrays.asList(childAttrs));
+            SurfaceView sv;
+            synchronized (this) {
+                sv = mSurfaceView;
+            }
+            if (sv == null) {
+                return;
+            }
 
-            if (isAttachedToWindow()) {
-                runOnUiThread(() -> {
-                    if (mParent != null) {
-                        mParent.recomputeViewAttributes(SurfaceView.this);
+            sv.mEmbeddedWindowParams.clear();
+            sv.mEmbeddedWindowParams.addAll(Arrays.asList(childAttrs));
+
+            if (sv.isAttachedToWindow()) {
+                sv.runOnUiThread(() -> {
+                    if (sv.mParent != null) {
+                        sv.mParent.recomputeViewAttributes(sv);
                     }
                 });
             }
@@ -343,34 +388,45 @@
 
         @Override
         public void forwardBackKeyToParent(@NonNull KeyEvent keyEvent) {
-                runOnUiThread(() -> {
-                    if (!isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) {
-                        return;
-                    }
-                    final ViewRootImpl vri = getViewRootImpl();
-                    if (vri == null) {
-                        return;
-                    }
-                    final InputManager inputManager = mContext.getSystemService(InputManager.class);
-                    if (inputManager == null) {
-                        return;
-                    }
-                    // Check that the event was created recently.
-                    final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime();
-                    if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) {
-                        Log.e(TAG, "Ignore the input event that exceed the tolerance time, "
-                                + "exceed " + timeDiff + "ms");
-                        return;
-                    }
-                    if (inputManager.verifyInputEvent(keyEvent) == null) {
-                        Log.e(TAG, "Received invalid input event");
-                        return;
-                    }
-                    vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
-                            true /* processImmediately */);
-                });
+            SurfaceView sv;
+            synchronized (this) {
+                sv = mSurfaceView;
+            }
+            if (sv == null) {
+                return;
+            }
+
+            sv.runOnUiThread(() -> {
+                if (!sv.isAttachedToWindow() || keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK) {
+                    return;
+                }
+                final ViewRootImpl vri = sv.getViewRootImpl();
+                if (vri == null) {
+                    return;
+                }
+                final InputManager inputManager = sv.mContext.getSystemService(InputManager.class);
+                if (inputManager == null) {
+                    return;
+                }
+                // Check that the event was created recently.
+                final long timeDiff = SystemClock.uptimeMillis() - keyEvent.getEventTime();
+                if (timeDiff > FORWARD_BACK_KEY_TOLERANCE_MS) {
+                    Log.e(TAG, "Ignore the input event that exceed the tolerance time, "
+                            + "exceed " + timeDiff + "ms");
+                    return;
+                }
+                if (inputManager.verifyInputEvent(keyEvent) == null) {
+                    Log.e(TAG, "Received invalid input event");
+                    return;
+                }
+                vri.enqueueInputEvent(keyEvent, null /* receiver */, 0 /* flags */,
+                        true /* processImmediately */);
+            });
         }
-    };
+    }
+
+    private final SurfaceControlViewHostParent mSurfaceControlViewHostParent =
+            new SurfaceControlViewHostParent();
 
     private final boolean mRtDrivenClipping = Flags.clipSurfaceviews();
 
@@ -930,13 +986,8 @@
             }
 
             if (mSurfacePackage != null) {
-                try {
-                    mSurfacePackage.getRemoteInterface().attachParentInterface(null);
-                    mEmbeddedWindowParams.clear();
-                } catch (RemoteException e) {
-                    Log.d(TAG, "Failed to remove parent interface from SCVH. Likely SCVH is "
-                            + "already dead");
-                }
+                mSurfaceControlViewHostParent.detach();
+                mEmbeddedWindowParams.clear();
                 if (releaseSurfacePackage) {
                     mSurfacePackage.release();
                     mSurfacePackage = null;
@@ -2067,12 +2118,7 @@
             applyTransactionOnVriDraw(transaction);
         }
         mSurfacePackage = p;
-        try {
-            mSurfacePackage.getRemoteInterface().attachParentInterface(
-                    mSurfaceControlViewHostParent);
-        } catch (RemoteException e) {
-            Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is already dead.");
-        }
+        mSurfaceControlViewHostParent.attach(this);
 
         if (isFocused()) {
             requestEmbeddedFocus(true);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index dbd65de..7b4ea41 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -141,7 +141,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.os.Vibrator;
 import android.service.credentials.CredentialProviderService;
 import android.sysprop.DisplayProperties;
 import android.text.InputType;
@@ -16444,7 +16443,11 @@
         ListenerInfo li = mListenerInfo;
         if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED) {
             try {
-                Trace.traceBegin(TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
+                if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+                    Trace.traceBegin(TRACE_TAG_VIEW,
+                            "View.onTouchListener#onTouch - " + getClass().getSimpleName()
+                                    + ", eventId - " + event.getId());
+                }
                 handled = li.mOnTouchListener.onTouch(this, event);
             } finally {
                 Trace.traceEnd(TRACE_TAG_VIEW);
@@ -27374,6 +27377,29 @@
     }
 
     /**
+     * Modifiers the input matrix such that it maps root view's coordinates to view-local
+     * coordinates.
+     *
+     * @param matrix input matrix to modify
+     * @hide
+     */
+    public void transformMatrixRootToLocal(@NonNull Matrix matrix) {
+        final ViewParent parent = mParent;
+        if (parent instanceof final View vp) {
+            vp.transformMatrixRootToLocal(matrix);
+            matrix.postTranslate(vp.mScrollX, vp.mScrollY);
+        }
+        // This method is different from transformMatrixToLocal that it doesn't perform any
+        // transformation for ViewRootImpl
+
+        matrix.postTranslate(-mLeft, -mTop);
+
+        if (!hasIdentityMatrix()) {
+            matrix.postConcat(getInverseMatrix());
+        }
+    }
+
+    /**
      * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", indexMapping = {
@@ -33985,7 +34011,7 @@
                 || mLastFrameTop != mTop)
                 && viewRootImpl.shouldCheckFrameRateCategory()
                 && parent instanceof View
-                && ((View) parent).mFrameContentVelocity <= 0
+                && ((View) parent).getFrameContentVelocity() <= 0
                 && !isInputMethodWindowType) {
 
             return FRAME_RATE_CATEGORY_HIGH_HINT | FRAME_RATE_CATEGORY_REASON_BOOST;
@@ -34152,7 +34178,8 @@
      * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
      * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
      * so use this method carefully. It's important to note that the preference is valid as
-     * long as the View is invalidated.
+     * long as the View is invalidated. Please also be aware that the requested frame rate
+     * will not propagate to child views when this API is used on a ViewGroup.
      *
      * @param frameRate the preferred frame rate of the view.
      */
@@ -34171,6 +34198,8 @@
      * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
      * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
      * Note that the frame rate value is valid as long as the View is invalidated.
+     * Please also be aware that the requested frame rate will not propagate to
+     * child views when this API is used on a ViewGroup.
      *
      * @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
      * or value passed to {@link #setRequestedFrameRate(float)}.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index fb2b8b9..63bf392 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -373,6 +373,7 @@
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
     private final int mOverflingDistance;
+    private final boolean mViewTouchScreenHapticScrollFeedbackEnabled;
     @UnsupportedAppUsage
     private final boolean mFadingMarqueeEnabled;
     private final long mGlobalActionsKeyTimeout;
@@ -437,6 +438,7 @@
         mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
         mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
         mPreferKeepClearForFocusEnabled = false;
+        mViewTouchScreenHapticScrollFeedbackEnabled = false;
     }
 
     /**
@@ -588,6 +590,12 @@
         mViewBasedRotaryEncoderScrollHapticsEnabledConfig =
                 res.getBoolean(
                         com.android.internal.R.bool.config_viewBasedRotaryEncoderHapticsEnabled);
+        mViewTouchScreenHapticScrollFeedbackEnabled =
+                Flags.enableTouchScrollFeedback()
+                        ? res.getBoolean(
+                        com.android.internal.R.bool
+                                .config_viewTouchScreenHapticScrollFeedbackEnabled)
+                        : false;
     }
 
     /**
@@ -1285,6 +1293,10 @@
             return mRotaryEncoderHapticScrollFeedbackEnabled;
         }
 
+        if ((source & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+            return mViewTouchScreenHapticScrollFeedbackEnabled;
+        }
+
         return false;
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6f88386..3b5286a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3093,74 +3093,74 @@
      */
     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
             View child, int desiredPointerIdBits) {
-        final boolean handled;
-
-        // Canceling motions is a special case.  We don't need to perform any transformations
-        // or filtering.  The important part is the action, not the contents.
         final int oldAction = event.getAction();
-        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
-            event.setAction(MotionEvent.ACTION_CANCEL);
-            if (child == null) {
-                handled = super.dispatchTouchEvent(event);
-            } else {
-                handled = child.dispatchTouchEvent(event);
+        try {
+            final boolean handled;
+            if (cancel) {
+                event.setAction(MotionEvent.ACTION_CANCEL);
             }
-            event.setAction(oldAction);
-            return handled;
-        }
 
-        // Calculate the number of pointers to deliver.
-        final int oldPointerIdBits = event.getPointerIdBits();
-        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
+            // Calculate the number of pointers to deliver.
+            final int oldPointerIdBits = event.getPointerIdBits();
+            int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
 
-        // If for some reason we ended up in an inconsistent state where it looks like we
-        // might produce a motion event with no pointers in it, then drop the event.
-        if (newPointerIdBits == 0) {
-            return false;
-        }
-
-        // If the number of pointers is the same and we don't need to perform any fancy
-        // irreversible transformations, then we can reuse the motion event for this
-        // dispatch as long as we are careful to revert any changes we make.
-        // Otherwise we need to make a copy.
-        final MotionEvent transformedEvent;
-        if (newPointerIdBits == oldPointerIdBits) {
-            if (child == null || child.hasIdentityMatrix()) {
-                if (child == null) {
-                    handled = super.dispatchTouchEvent(event);
+            // If for some reason we ended up in an inconsistent state where it looks like we
+            // might produce a non-cancel motion event with no pointers in it, then drop the event.
+            // Make sure that we don't drop any cancel events.
+            if (newPointerIdBits == 0) {
+                if (event.getAction() != MotionEvent.ACTION_CANCEL) {
+                    return false;
                 } else {
-                    final float offsetX = mScrollX - child.mLeft;
-                    final float offsetY = mScrollY - child.mTop;
-                    event.offsetLocation(offsetX, offsetY);
-
-                    handled = child.dispatchTouchEvent(event);
-
-                    event.offsetLocation(-offsetX, -offsetY);
+                    newPointerIdBits = oldPointerIdBits;
                 }
-                return handled;
-            }
-            transformedEvent = MotionEvent.obtain(event);
-        } else {
-            transformedEvent = event.split(newPointerIdBits);
-        }
-
-        // Perform any necessary transformations and dispatch.
-        if (child == null) {
-            handled = super.dispatchTouchEvent(transformedEvent);
-        } else {
-            final float offsetX = mScrollX - child.mLeft;
-            final float offsetY = mScrollY - child.mTop;
-            transformedEvent.offsetLocation(offsetX, offsetY);
-            if (! child.hasIdentityMatrix()) {
-                transformedEvent.transform(child.getInverseMatrix());
             }
 
-            handled = child.dispatchTouchEvent(transformedEvent);
-        }
+            // If the number of pointers is the same and we don't need to perform any fancy
+            // irreversible transformations, then we can reuse the motion event for this
+            // dispatch as long as we are careful to revert any changes we make.
+            // Otherwise we need to make a copy.
+            final MotionEvent transformedEvent;
+            if (newPointerIdBits == oldPointerIdBits) {
+                if (child == null || child.hasIdentityMatrix()) {
+                    if (child == null) {
+                        handled = super.dispatchTouchEvent(event);
+                    } else {
+                        final float offsetX = mScrollX - child.mLeft;
+                        final float offsetY = mScrollY - child.mTop;
+                        event.offsetLocation(offsetX, offsetY);
 
-        // Done.
-        transformedEvent.recycle();
-        return handled;
+                        handled = child.dispatchTouchEvent(event);
+
+                        event.offsetLocation(-offsetX, -offsetY);
+                    }
+                    return handled;
+                }
+                transformedEvent = MotionEvent.obtain(event);
+            } else {
+                transformedEvent = event.split(newPointerIdBits);
+            }
+
+            // Perform any necessary transformations and dispatch.
+            if (child == null) {
+                handled = super.dispatchTouchEvent(transformedEvent);
+            } else {
+                final float offsetX = mScrollX - child.mLeft;
+                final float offsetY = mScrollY - child.mTop;
+                transformedEvent.offsetLocation(offsetX, offsetY);
+                if (!child.hasIdentityMatrix()) {
+                    transformedEvent.transform(child.getInverseMatrix());
+                }
+
+                handled = child.dispatchTouchEvent(transformedEvent);
+            }
+
+            // Done.
+            transformedEvent.recycle();
+            return handled;
+
+        } finally {
+            event.setAction(oldAction);
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 02f7e95..2786c84 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -15,7 +15,10 @@
  */
 package android.view;
 
+import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+
 import android.animation.LayoutTransition;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -365,6 +368,18 @@
             }
             return null;
         }
+
+        /**
+         * @hide
+         */
+        @Override
+        @FlaggedApi(FLAG_VIEW_VELOCITY_API)
+        public float getFrameContentVelocity() {
+            if (mHostView != null) {
+                return mHostView.getFrameContentVelocity();
+            }
+            return super.getFrameContentVelocity();
+        }
     }
 
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c0700f..f021bdf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -133,6 +133,7 @@
 import static com.android.window.flags.Flags.insetsControlChangedItem;
 import static com.android.window.flags.Flags.insetsControlSeq;
 import static com.android.window.flags.Flags.setScPropertiesInClient;
+import static com.android.window.flags.Flags.systemUiImmersiveConfirmationDialog;
 
 import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
@@ -303,6 +304,7 @@
 import java.util.Queue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 /**
@@ -378,7 +380,7 @@
      * @hide
      */
     public static final boolean CLIENT_IMMERSIVE_CONFIRMATION =
-            SystemProperties.getBoolean("persist.wm.debug.client_immersive_confirmation", false);
+            systemUiImmersiveConfirmationDialog();
 
     /**
      * Set this system property to true to force the view hierarchy to render
@@ -1200,8 +1202,7 @@
     private String mLargestViewTraceName;
 
     private final boolean mAppStartInfoTimestampsFlagValue;
-    @GuardedBy("this")
-    private boolean mAppStartTimestampsSent = false;
+    private AtomicBoolean mAppStartTimestampsSent = new AtomicBoolean(false);
     private boolean mAppStartTrackingStarted = false;
     private long mRenderThreadDrawStartTimeNs = -1;
     private long mFirstFramePresentedTimeNs = -1;
@@ -2646,7 +2647,7 @@
                 destroySurface();
 
                 // Reset so they can be sent again for warm starts.
-                mAppStartTimestampsSent = false;
+                mAppStartTimestampsSent.set(false);
                 mAppStartTrackingStarted = false;
                 mRenderThreadDrawStartTimeNs = -1;
                 mFirstFramePresentedTimeNs = -1;
@@ -4387,14 +4388,7 @@
             mReportNextDraw = false;
             mLastReportNextDrawReason = null;
             mActiveSurfaceSyncGroup = null;
-            if (mHasPendingTransactions) {
-                // TODO: We shouldn't ever actually hit this, it means mPendingTransaction wasn't
-                // merged with a sync group or BLASTBufferQueue before making it to this point
-                // But better a one or two frame flicker than steady-state broken from dropping
-                // whatever is in this transaction
-                mPendingTransaction.apply();
-                mHasPendingTransactions = false;
-            }
+            mHasPendingTransactions = false;
             mSyncBuffer = false;
             if (isInWMSRequestedSync()) {
                 mWmsRequestSyncGroup.markSyncReady();
@@ -4502,42 +4496,29 @@
     }
 
     private void maybeSendAppStartTimes() {
-        synchronized (this) {
-            if (mAppStartTimestampsSent) {
-                // Don't send timestamps more than once.
-                return;
-            }
-
-            // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not
-            // post to main thread and check if we have it there.
-            if (mRenderThreadDrawStartTimeNs != -1) {
-                sendAppStartTimesLocked();
-            } else {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        synchronized (ViewRootImpl.this) {
-                            if (mRenderThreadDrawStartTimeNs == -1) {
-                                return;
-                            }
-                            sendAppStartTimesLocked();
-                        }
-                    }
-                });
-            }
+        if (mAppStartTimestampsSent.get()) {
+            // Don't send timestamps more than once.
+            return;
         }
-    }
 
-    @GuardedBy("this")
-    private void sendAppStartTimesLocked() {
-        try {
-            ActivityManager.getService().reportStartInfoViewTimestamps(
-                    mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
-            mAppStartTimestampsSent = true;
-        } catch (RemoteException e) {
-            // Ignore, timestamps may be lost.
-            if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
-        }
+        // Post to main thread
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mRenderThreadDrawStartTimeNs == -1) {
+                    return;
+                }
+
+                try {
+                    ActivityManager.getService().reportStartInfoViewTimestamps(
+                            mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs);
+                    mAppStartTimestampsSent.set(true);
+                } catch (RemoteException e) {
+                    // Ignore, timestamps may be lost.
+                    if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e);
+                }
+            }
+        });
     }
 
     /**
@@ -6113,6 +6094,12 @@
     }
 
     boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
+        if (mImeBackAnimationController.isAnimationInProgress()) {
+            // IME predictive back animation is currently in progress which means that scrollY is
+            // currently controlled by ImeBackAnimationController.
+            return false;
+        }
+
         final Rect ci = mAttachInfo.mContentInsets;
         final Rect vi = mAttachInfo.mVisibleInsets;
         int scrollY = 0;
@@ -13024,7 +13011,7 @@
 
     private boolean shouldSetFrameRateCategory() {
         // use toolkitSetFrameRate flag to gate the change
-        return shouldEnableDvrr() && mSurface.isValid() && shouldEnableDvrr();
+        return shouldEnableDvrr() && mSurface.isValid();
     }
 
     private boolean shouldSetFrameRate() {
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 6578e9b..d3ea982 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -23,6 +23,7 @@
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimation.Bounds;
+import android.view.animation.Interpolator;
 
 /**
  * Controller for app-driven animation of system windows.
@@ -188,4 +189,16 @@
      *  fullscreen or non-overlapping).
      */
     boolean hasZeroInsetsIme();
+
+    /**
+     * @hide
+     * @return The duration of the animation in {@link java.util.concurrent.TimeUnit#MILLISECONDS}.
+     */
+    long getDurationMs();
+
+    /**
+     * @hide
+     * @return The interpolator of the animation.
+     */
+    Interpolator getInsetsInterpolator();
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 017e004..67a207e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3462,6 +3462,15 @@
         public static final int PRIVATE_FLAG_NOT_MAGNIFIABLE = 1 << 22;
 
         /**
+         * Indicates that the window should receive key events including Action/Meta key.
+         * They will not be intercepted as usual and instead will be passed to the window with other
+         * key events.
+         * TODO(b/358569822) Remove this once we have nicer API for listening to shortcuts
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS = 1 << 23;
+
+        /**
          * Flag to indicate that the window is color space agnostic, and the color can be
          * interpreted to any color space.
          * @hide
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a4cea33..a87e5c8 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1051,6 +1051,52 @@
     }
 
     /**
+     * Registers callback for when user initialization has completed.
+     * Does nothing if the same callback is already registered.
+     *
+     * @param callback The callback to be registered
+     * @hide
+     */
+    public void registerUserInitializationCompleteCallback(
+            @NonNull IUserInitializationCompleteCallback callback) {
+        IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.registerUserInitializationCompleteCallback(callback);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while registering userInitializationCompleteCallback. ", re);
+        }
+    }
+
+    /**
+     * Unregisters callback for when user initialization has completed.
+     *
+     * @param callback The callback to be unregistered
+     * @hide
+     */
+    public void unregisterUserInitializationCompleteCallback(
+            @NonNull IUserInitializationCompleteCallback callback) {
+        IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.unregisterUserInitializationCompleteCallback(callback);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG,
+                    "Error while unregistering userInitializationCompleteCallback. ", re);
+        }
+    }
+
+    /**
      * Whether the current accessibility request comes from an
      * {@link AccessibilityService} with the {@link AccessibilityServiceInfo#isAccessibilityTool}
      * property set to true.
@@ -2049,9 +2095,7 @@
      * {@link android.view.Display#DEFAULT_DISPLAY}, is or lower than
      * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed.
      *
-     * @throws SecurityException if the app does not hold the
-     * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
-     * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+     * @throws SecurityException if the app does not hold the required permissions.
      *
      * @hide
      */
@@ -2079,9 +2123,7 @@
      *
      * @return {@code true} if the proxy is successfully unregistered.
      *
-     * @throws SecurityException if the app does not hold the
-     * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the
-     * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission.
+     * @throws SecurityException if the app does not hold the required permissions.
      *
      * @hide
      */
@@ -2134,8 +2176,8 @@
         try {
             return service.startFlashNotificationSequence(context.getOpPackageName(),
                     reason, mBinder);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while start flash notification sequence", re);
+        } catch (RemoteException | SecurityException e) {
+            Log.e(LOG_TAG, "Error while start flash notification sequence", e);
             return false;
         }
     }
@@ -2164,8 +2206,8 @@
 
         try {
             return service.stopFlashNotificationSequence(context.getOpPackageName());
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while stop flash notification sequence", re);
+        } catch (RemoteException | SecurityException e) {
+            Log.e(LOG_TAG, "Error while stop flash notification sequence", e);
             return false;
         }
     }
@@ -2192,8 +2234,8 @@
         try {
             return service.startFlashNotificationEvent(context.getOpPackageName(),
                     reason, reasonPkg);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while start flash notification event", re);
+        } catch (RemoteException | SecurityException e) {
+            Log.e(LOG_TAG, "Error while start flash notification event", e);
             return false;
         }
     }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a5ba294..fe6aafb 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -982,6 +982,7 @@
     private long mParentNodeId = UNDEFINED_NODE_ID;
     private long mLabelForId = UNDEFINED_NODE_ID;
     private long mLabeledById = UNDEFINED_NODE_ID;
+    private LongArray mLabeledByIds;
     private long mTraversalBefore = UNDEFINED_NODE_ID;
     private long mTraversalAfter = UNDEFINED_NODE_ID;
 
@@ -3599,6 +3600,133 @@
     }
 
     /**
+     * Adds the view which serves as the label of the view represented by
+     * this info for accessibility purposes. When multiple labels are
+     * added, the content from each label is combined in the order that
+     * they are added.
+     * <p>
+     * If visible text can be used to describe or give meaning to this UI,
+     * this method is preferred. For example, a TextView before an EditText
+     * in the UI usually specifies what information is contained in the
+     * EditText. Hence, the EditText is labeled by the TextView.
+     * </p>
+     *
+     * @param label A view that labels this node's source.
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+    public void addLabeledBy(@NonNull View label) {
+        addLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
+    }
+
+    /**
+     * Adds the view which serves as the label of the view represented by
+     * this info for accessibility purposes. If <code>virtualDescendantId</code>
+     * is {@link View#NO_ID} the root is set as the label. When multiple
+     * labels are added, the content from each label is combined in the order
+     * that they are added.
+     * <p>
+     * A virtual descendant is an imaginary View that is reported as a part of the view
+     * hierarchy for accessibility purposes. This enables custom views that draw complex
+     * content to report themselves as a tree of virtual views, thus conveying their
+     * logical structure.
+     * </p>
+     * <p>
+     * If visible text can be used to describe or give meaning to this UI,
+     * this method is preferred. For example, a TextView before an EditText
+     * in the UI usually specifies what information is contained in the
+     * EditText. Hence, the EditText is labeled by the TextView.
+     * </p>
+     * <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>
+     *
+     * @param root A root whose virtual descendant labels this node's source.
+     * @param virtualDescendantId The id of the virtual descendant.
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+    public void addLabeledBy(@NonNull View root, int virtualDescendantId) {
+        enforceNotSealed();
+        Preconditions.checkNotNull(root, "%s must not be null", root);
+        if (mLabeledByIds == null) {
+            mLabeledByIds = new LongArray();
+        }
+        mLabeledById = makeNodeId(root.getAccessibilityViewId(), virtualDescendantId);
+        mLabeledByIds.add(mLabeledById);
+    }
+
+    /**
+     * Gets the list of node infos which serve as the labels of the view represented by
+     * this info for accessibility purposes.
+     *
+     * @return The list of labels in the order that they were added.
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+    public @NonNull List<AccessibilityNodeInfo> getLabeledByList() {
+        enforceSealed();
+        List<AccessibilityNodeInfo> labels = new ArrayList<>();
+        if (mLabeledByIds == null) {
+            return labels;
+        }
+        for (int i = 0; i < mLabeledByIds.size(); i++) {
+            labels.add(getNodeForAccessibilityId(mConnectionId, mWindowId, mLabeledByIds.get(i)));
+        }
+        return labels;
+    }
+
+    /**
+     * Removes a label. If the label was not previously added to the node,
+     * calling this method has no effect.
+     * <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>
+     *
+     * @param label The node which serves as this node's label.
+     * @return true if the label was present
+     * @see #addLabeledBy(View)
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+    public boolean removeLabeledBy(@NonNull View label) {
+        return removeLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
+    }
+
+    /**
+     * Removes a label which is a virtual descendant of the given
+     * <code>root</code>. If <code>virtualDescendantId</code> is
+     * {@link View#NO_ID} the root is set as the label. If the label
+     * was not previously added to the node, calling this method has
+     * no effect.
+     *
+     * @param root The root of the virtual subtree.
+     * @param virtualDescendantId The id of the virtual node which serves as this node's label.
+     * @return true if the label was present
+     * @see #addLabeledBy(View, int)
+     */
+    @FlaggedApi(Flags.FLAG_SUPPORT_MULTIPLE_LABELEDBY)
+    public boolean removeLabeledBy(@NonNull View root, int virtualDescendantId) {
+        enforceNotSealed();
+        final LongArray labeledByIds = mLabeledByIds;
+        if (labeledByIds == null) {
+            return false;
+        }
+        final int rootAccessibilityViewId =
+                (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
+        final long labeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
+        if (mLabeledById == labeledById) {
+            mLabeledById = UNDEFINED_NODE_ID;
+        }
+        final int index = labeledByIds.indexOf(labeledById);
+        if (index < 0) {
+            return false;
+        }
+        labeledByIds.remove(index);
+        return true;
+    }
+
+    /**
      * Sets the view which serves as the label of the view represented by
      * this info for accessibility purposes.
      *
@@ -3631,7 +3759,17 @@
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
+        if (Flags.supportMultipleLabeledby()) {
+            if (mLabeledByIds == null) {
+                mLabeledByIds = new LongArray();
+            } else {
+                mLabeledByIds.clear();
+            }
+        }
         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
+        if (Flags.supportMultipleLabeledby()) {
+            mLabeledByIds.add(mLabeledById);
+        }
     }
 
     /**
@@ -4242,6 +4380,10 @@
         fieldIndex++;
         if (mLabeledById != DEFAULT.mLabeledById) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
+        if (!LongArray.elementsEqual(mLabeledByIds, DEFAULT.mLabeledByIds)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
         if (mTraversalBefore != DEFAULT.mTraversalBefore) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
         if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex);
@@ -4383,6 +4525,18 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mParentNodeId);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabelForId);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById);
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            final LongArray labeledByIds = mLabeledByIds;
+            if (labeledByIds == null) {
+                parcel.writeInt(0);
+            } else {
+                final int labeledByIdsSize = labeledByIds.size();
+                parcel.writeInt(labeledByIdsSize);
+                for (int i = 0; i < labeledByIdsSize; i++) {
+                    parcel.writeLong(labeledByIds.get(i));
+                }
+            }
+        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore);
         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter);
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
@@ -4550,6 +4704,7 @@
         mParentNodeId = other.mParentNodeId;
         mLabelForId = other.mLabelForId;
         mLabeledById = other.mLabeledById;
+        mLabeledByIds = other.mLabeledByIds;
         mTraversalBefore = other.mTraversalBefore;
         mTraversalAfter = other.mTraversalAfter;
         mMinDurationBetweenContentChanges = other.mMinDurationBetweenContentChanges;
@@ -4656,6 +4811,18 @@
         if (isBitSet(nonDefaultFields, fieldIndex++)) mParentNodeId = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabelForId = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong();
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            final int labeledByIdsSize = parcel.readInt();
+            if (labeledByIdsSize <= 0) {
+                mLabeledByIds = null;
+            } else {
+                mLabeledByIds = new LongArray(labeledByIdsSize);
+                for (int i = 0; i < labeledByIdsSize; i++) {
+                    final long labeledById = parcel.readLong();
+                    mLabeledByIds.add(labeledById);
+                }
+            }
+        }
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong();
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 72a1fe4..2de3ce8 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -29,6 +29,7 @@
 import android.view.accessibility.IAccessibilityManagerClient;
 import android.view.accessibility.AccessibilityWindowAttributes;
 import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
 import android.view.InputEvent;
 import android.view.IWindow;
 import android.view.MagnificationSpec;
@@ -156,13 +157,13 @@
     @EnforcePermission("INJECT_EVENTS")
     void injectInputEventToInputFilter(in InputEvent event);
 
-    @RequiresNoPermission
+    @EnforcePermission("MANAGE_ACCESSIBILITY")
     boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token);
 
-    @RequiresNoPermission
+    @EnforcePermission("MANAGE_ACCESSIBILITY")
     boolean stopFlashNotificationSequence(String opPkg);
 
-    @RequiresNoPermission
+    @EnforcePermission("MANAGE_ACCESSIBILITY")
     boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg);
 
     @RequiresNoPermission
@@ -192,4 +193,10 @@
 
     @EnforcePermission("MANAGE_ACCESSIBILITY")
     Bundle getA11yFeatureToTileMap(int userId);
+
+    @RequiresNoPermission
+    void registerUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
+
+    @RequiresNoPermission
+    void unregisterUserInitializationCompleteCallback(IUserInitializationCompleteCallback callback);
 }
diff --git a/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.aidl
new file mode 100644
index 0000000..fe6c8e2
--- /dev/null
+++ b/core/java/android/view/accessibility/IUserInitializationCompleteCallback.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.view.accessibility;
+
+/**
+ * A callback for when a new user finishes initializing
+ * NOTE: Must remain a oneway interface, as it is called from system_server while holding a lock.
+ * oneway allows it to return immediately and not hold the lock for longer than is necessary.
+ * @hide
+ */
+
+oneway interface IUserInitializationCompleteCallback {
+
+    /**
+     * Called when a user initialization completes.
+     *
+     * @param userId the id of the initialized user
+     */
+    @RequiresNoPermission
+    void onUserInitializationComplete(int userId);
+}
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ed2bf79..513587e 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -19,6 +19,13 @@
 }
 
 flag {
+    name: "a11y_selection_api"
+    namespace: "accessibility"
+    description: "Enables new APIs for an AccessibilityService to control selection across nodes."
+    bug: "362782866"
+}
+
+flag {
     namespace: "accessibility"
     name: "allow_shortcut_chooser_on_lockscreen"
     description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 09306c7..288be9c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -28,6 +28,7 @@
 import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.WindowInsets;
 
 import dalvik.system.CloseGuard;
 
@@ -881,12 +882,13 @@
     }
 
     /**
-     * @return if a window animation has outsets applied to it.
+     * @return the edges to which outsets should be applied if run as a windoow animation.
      *
      * @hide
      */
-    public boolean hasExtension() {
-        return false;
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        return 0x0;
     }
 
     /**
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 5aaa994..bbdc9d0 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -21,6 +21,7 @@
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -540,12 +541,12 @@
 
     /** @hide */
     @Override
-    public boolean hasExtension() {
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        int edge = 0x0;
         for (Animation animation : mAnimations) {
-            if (animation.hasExtension()) {
-                return true;
-            }
+            edge |= animation.getExtensionEdges();
         }
-        return false;
+        return edge;
     }
 }
diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java
index 210eb8a..1aeee07 100644
--- a/core/java/android/view/animation/ExtendAnimation.java
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Insets;
 import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 /**
  * An animation that controls the outset of an object.
@@ -151,9 +152,12 @@
 
     /** @hide */
     @Override
-    public boolean hasExtension() {
-        return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
-                || mFromInsets.bottom < 0;
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        return (mFromInsets.left < 0 || mToInsets.left < 0 ?  WindowInsets.Side.LEFT : 0)
+            | (mFromInsets.right < 0 || mToInsets.right < 0 ?  WindowInsets.Side.RIGHT : 0)
+            | (mFromInsets.top < 0 || mToInsets.top < 0 ?  WindowInsets.Side.TOP : 0)
+            | (mFromInsets.bottom < 0 || mToInsets.bottom < 0 ? WindowInsets.Side.BOTTOM : 0);
     }
 
     @Override
diff --git a/core/java/android/view/contentprotection/OWNERS b/core/java/android/view/contentprotection/OWNERS
index b3583a7..48052c6 100644
--- a/core/java/android/view/contentprotection/OWNERS
+++ b/core/java/android/view/contentprotection/OWNERS
@@ -1,4 +1,6 @@
-# Bug component: 544200
+# Bug component: 1040349
 
-include /core/java/android/view/contentcapture/OWNERS
[email protected]
[email protected]
[email protected]
 
diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig
index 338037f..e9c85684 100644
--- a/core/java/android/view/flags/scroll_feedback_flags.aconfig
+++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig
@@ -14,4 +14,11 @@
     name: "use_view_based_rotary_encoder_scroll_haptics"
     description: "If enabled, the rotary encoder scroll haptic implementation in the View class will be used, and the HapticScrollFeedbackProvider logic for rotary encoder haptic will be muted."
     bug: "299587011"
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "toolkit"
+    name: "enable_touch_scroll_feedback"
+    description: "Enables touchscreen haptic scroll feedback"
+    bug: "331830899"
+}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index 2f515fe..3a008aa 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -323,14 +323,14 @@
     static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             int lastClickToolType, @Nullable ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
+            @SoftInputShowHideReason int reason, boolean async) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return false;
         }
         try {
             return service.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
-                    resultReceiver, reason);
+                    resultReceiver, reason, async);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -339,14 +339,15 @@
     @AnyThread
     static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
-            @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason,
+            boolean async) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return false;
         }
         try {
             return service.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
-                    reason);
+                    reason, async);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -407,7 +408,7 @@
             @Nullable IRemoteInputConnection remoteInputConnection,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, boolean useAsyncShowHideMethod) {
         final IInputMethodManager service = getService();
         if (service == null) {
             return -1;
@@ -416,7 +417,7 @@
             service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
                     startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
                     remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
-                    imeDispatcher, advanceAngGetStartInputSequenceNumber());
+                    imeDispatcher, advanceAngGetStartInputSequenceNumber(), useAsyncShowHideMethod);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 1840bcb..4742f1e 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -625,8 +625,7 @@
      *        the text you are providing so it is not possible to correctly
      *        specify locations there.
      * @param textAttribute The extra information about the text.
-     * @return true on success, false if the input connection is no longer
-     *
+     * @return true on success, false if the input connection is no longer valid.
      */
     default boolean setComposingText(@NonNull CharSequence text, int newCursorPosition,
             @Nullable TextAttribute textAttribute) {
@@ -753,7 +752,7 @@
      *        you are providing so it is not possible to correctly specify
      *        locations there.
      * @param textAttribute The extra information about the text.
-     * @return true on success, false if the input connection is no longer
+     * @return true on success, false if the input connection is no longer valid.
      */
     default boolean commitText(@NonNull CharSequence text, int newCursorPosition,
             @Nullable TextAttribute textAttribute) {
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 0e66f7a..806a593 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -878,15 +878,18 @@
 
     /**
      * Returns {@link Intent} for IME language settings activity with
-     * {@link Intent#getAction() Intent action} {@link #ACTION_IME_LANGUAGE_SETTINGS},
-     * else <code>null</code> if
-     * {@link android.R.styleable#InputMethod_languageSettingsActivity} is not defined.
+     * {@link Intent#getAction() Intent action} {@link #ACTION_IME_LANGUAGE_SETTINGS}. If
+     * {@link android.R.styleable#InputMethod_languageSettingsActivity} is not defined, tries to
+     * fall back to the IME general settings activity. If
+     * {@link android.R.styleable#InputMethod_settingsActivity} is also not defined,
+     * returns {code null}.
      *
      * <p>To launch IME language settings, use this method to get the {@link Intent} to launch
      * the IME language settings activity.</p>
      * <p>e.g.<pre><code>startActivity(createImeLanguageSettingsActivityIntent());</code></pre></p>
      *
      * @attr ref R.styleable#InputMethod_languageSettingsActivity
+     * @attr ref R.styleable#InputMethod_settingsActivity
      */
     @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API)
     @Nullable
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b281015..2f649c2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -56,6 +56,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.app.PropertyInvalidatedCache;
+import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -441,6 +442,36 @@
     public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id.
 
     /**
+     * Use async method for {@link InputMethodManager#showSoftInput(View, int, ResultReceiver)},
+     * {@link InputMethodManager#showSoftInput(View, int)} and
+     * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int, ResultReceiver)},
+     * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} for apps targeting V+.
+     * <p>
+     * Apps can incorrectly rely on {@link InputMethodManager#showSoftInput(View, int)} and
+     * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)} method return type
+     * to interpret result of a request rather than relying on {@link ResultReceiver}. The return
+     * type of the method was never documented to have accurate info of visibility but few apps
+     * incorrectly rely on it.
+     * <p>
+     * Starting Android V, we use async calls into system_server which returns {@code true} if
+     * method call was made but return type doesn't guarantee execution.
+     * Apps targeting older versions will fallback to existing behavior of calling synchronous
+     * methods which had undocumented result in return type.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.
+
+    /**
+     * Version-gating is guarded by bug-fix flag.
+     */
+    private static final boolean ASYNC_SHOW_HIDE_METHOD_ENABLED =
+            !Flags.compatchangeForZerojankproxy()
+                || CompatChanges.isChangeEnabled(USE_ASYNC_SHOW_HIDE_METHOD);
+
+    /**
      * If {@code true}, avoid calling the
      * {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
      * by skipping the call to {@link IInputMethodManager#startInputOrWindowGainedFocus}
@@ -1365,10 +1396,10 @@
                                         ImeTracker.PHASE_CLIENT_HANDLE_SET_IME_VISIBILITY);
                                 if (visible) {
                                     insetsController.show(WindowInsets.Type.ime(),
-                                            false /* fromIme */, null /* statsToken */);
+                                            false /* fromIme */, statsToken);
                                 } else {
                                     insetsController.hide(WindowInsets.Type.ime(),
-                                            false /* fromIme */, null /* statsToken */);
+                                            false /* fromIme */, statsToken);
                                 }
                             }
                         } else {
@@ -2246,6 +2277,8 @@
      *             {@link View#isFocused view focus}, and its containing window has
      *             {@link View#hasWindowFocus window focus}. Otherwise the call fails and
      *             returns {@code false}.
+     * @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.
      */
     public boolean showSoftInput(View view, @ShowFlags int flags) {
         // Re-dispatch if there is a context mismatch.
@@ -2315,6 +2348,8 @@
      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
      * {@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.
      */
     public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
         return showSoftInput(view, flags, resultReceiver, SoftInputShowHideReason.SHOW_SOFT_INPUT);
@@ -2383,7 +2418,8 @@
                         flags,
                         mCurRootView.getLastClickToolType(),
                         resultReceiver,
-                        reason);
+                        reason,
+                        ASYNC_SHOW_HIDE_METHOD_ENABLED);
             }
         }
     }
@@ -2426,7 +2462,8 @@
                     flags,
                     mCurRootView.getLastClickToolType(),
                     resultReceiver,
-                    reason);
+                    reason,
+                    ASYNC_SHOW_HIDE_METHOD_ENABLED);
         }
     }
 
@@ -2459,6 +2496,9 @@
      *
      * @param windowToken The token of the window that is making the request,
      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
+     * @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 {@link ResultReceiver} in
+     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
      */
     public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
         return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2487,6 +2527,8 @@
      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
      * {@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.
      */
     public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
             ResultReceiver resultReceiver) {
@@ -2530,7 +2572,7 @@
                 return true;
             } else {
                 return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
-                        statsToken, flags, resultReceiver, reason);
+                        statsToken, flags, resultReceiver, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
             }
         }
     }
@@ -2573,7 +2615,7 @@
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
 
             return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, view.getWindowToken(),
-                    statsToken, flags, null, reason);
+                    statsToken, flags, null, reason, ASYNC_SHOW_HIDE_METHOD_ENABLED);
         }
     }
 
@@ -3350,7 +3392,7 @@
                         servedInputConnection == null ? null
                                 : servedInputConnection.asIRemoteAccessibilityInputConnection(),
                         view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
-                        mImeDispatcher);
+                        mImeDispatcher, ASYNC_SHOW_HIDE_METHOD_ENABLED);
             } else {
                 res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
@@ -3653,7 +3695,8 @@
                     statsToken,
                     HIDE_NOT_ALWAYS,
                     null,
-                    reason);
+                    reason,
+                    true /*async */);
         }
     }
 
@@ -3745,7 +3788,7 @@
 
             IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken, statsToken,
                     0 /* flags */, null /* resultReceiver */,
-                    SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API);
+                    SoftInputShowHideReason.HIDE_SOFT_INPUT_BY_INSETS_API, true /* async */);
         }
     }
 
diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig
index e294ee2..bae8aff 100644
--- a/core/java/android/view/inputmethod/flags.aconfig
+++ b/core/java/android/view/inputmethod/flags.aconfig
@@ -136,3 +136,14 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "compatchange_for_zerojankproxy"
+  namespace: "input_method"
+  description: "Version-gate the sync/async nature of IMM#show/hideSoftInput() when using zeroJankProxy."
+  bug: "352594277"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/view/textclassifier/TEST_MAPPING b/core/java/android/view/textclassifier/TEST_MAPPING
index 2f9e737..050c651 100644
--- a/core/java/android/view/textclassifier/TEST_MAPPING
+++ b/core/java/android/view/textclassifier/TEST_MAPPING
@@ -1,15 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.textclassifier"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_textclassifier"
     },
     {
       "name": "CtsTextClassifierTestCases",
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index d12eda3..7366b9a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -497,25 +497,32 @@
     public abstract  boolean getUseWebViewBackgroundForOverscrollBackground();
 
     /**
-     * Sets whether the WebView should save form data. In Android O, the
-     * platform has implemented a fully functional Autofill feature to store
-     * form data. Therefore, the Webview form data save feature is disabled.
+     * Sets whether the WebView should save form data. In {@link android.os.Build.VERSION_CODES#O},
+     * the platform has implemented a fully functional Autofill feature to store form data.
+     * Therefore, the Webview form data save feature is disabled.
      *
-     * Note that the feature will continue to be supported on older versions of
+     * <p>Note that the feature will continue to be supported on older versions of
      * Android as before.
      *
-     * @deprecated In Android O and afterwards, this function does not have
-     * any effect, the form data will be saved to platform's autofill service
-     * if applicable.
+     * @see #getSaveFormData
+     * @deprecated In Android O and afterwards, this function does not have any effect. Form data
+     * will be saved to platform's autofill service if applicable.
      */
     @Deprecated
     public abstract  void setSaveFormData(boolean save);
 
     /**
-     * Gets whether the WebView saves form data.
+     * Gets whether the WebView saves form data. In {@link android.os.Build.VERSION_CODES#O}, the
+     * platform has implemented a fully functional Autofill feature to store form data. Therefore,
+     * the Webview form data save feature is disabled.
+     *
+     * <p>Note that the feature will continue to be supported on older versions of
+     * Android as before.
      *
      * @return whether the WebView saves form data
      * @see #setSaveFormData
+     * @deprecated In Android O and afterwards, this function does not have any effect. Form data
+     * will be filled from the platform's autofill service if applicable.
      */
     @Deprecated
     public abstract boolean getSaveFormData();
@@ -741,9 +748,17 @@
     public abstract boolean getUseWideViewPort();
 
     /**
-     * Sets whether the WebView whether supports multiple windows. If set to
-     * true, {@link WebChromeClient#onCreateWindow} must be implemented by the
-     * host application. The default is {@code false}.
+     * Sets whether the WebView should support multiple windows.
+     *
+     * <p>If set to {@code true}, the {@link WebChromeClient#onCreateWindow}
+     * callback must be implemented by the application to handle the
+     * creation of new windows.
+     *
+     * <p>The default is {@code false}. When multiple window support is disabled,
+     * requests to open new windows (either from the {@code window.open()}
+     * JavaScript API or from links with {@code target="_blank"}) will instead
+     * be treated as top-level navigations, replacing the current page in the
+     * same WebView.
      *
      * @param support whether to support multiple windows
      */
@@ -1338,18 +1353,24 @@
     }
 
     /**
-     * Tells JavaScript to open windows automatically. This applies to the
-     * JavaScript function {@code window.open()}. The default is {@code false}.
+     * Allows JavaScript to open windows without a user gesture. This applies to
+     * the JavaScript function {@code window.open()}. The default is
+     * {@code false}: attempts without a user gesture will fail and do nothing.
+     * <p>
+     * This is not affected by the {@link #setSupportMultipleWindows} setting;
+     * the user gesture requirement is enforced even if multiple windows are
+     * disabled.
      *
-     * @param flag {@code true} if JavaScript can open windows automatically
+     * @param flag {@code true} if JavaScript can open windows without a user
+     *             gesture.
      */
     public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag);
 
     /**
-     * Gets whether JavaScript can open windows automatically.
+     * Gets whether JavaScript can open windows without a user gesture.
      *
-     * @return {@code true} if JavaScript can open windows automatically during
-     *         {@code window.open()}
+     * @return {@code true} if JavaScript can open windows without a user
+     *         gesture using {@code window.open()}
      * @see #setJavaScriptCanOpenWindowsAutomatically
      */
     public abstract boolean getJavaScriptCanOpenWindowsAutomatically();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f336b5d..ffe8c80 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3088,11 +3088,11 @@
         }
 
         if (Flags.updateServiceIpcWrapper()) {
-            WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
-            if (manager == null) {
+            if (WebViewFactory.isWebViewSupported()) {
+                return WebViewUpdateManager.getInstance().getCurrentWebViewPackage();
+            } else {
                 return null;
             }
-            return manager.getCurrentWebViewPackage();
         } else {
             IWebViewUpdateService service = WebViewFactory.getUpdateService();
             if (service == null) {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index c53a0e1..e10a398 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -208,7 +208,7 @@
         public MissingWebViewPackageException(Exception e) { super(e); }
     }
 
-    private static boolean isWebViewSupported() {
+    static boolean isWebViewSupported() {
         // No lock; this is a benign race as Boolean's state is final and the PackageManager call
         // will always return the same value.
         if (sWebViewSupported == null) {
@@ -318,7 +318,7 @@
         String libraryFileName;
         try {
             PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
-                    PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+                    PackageManager.GET_META_DATA);
             libraryFileName = getWebViewLibrary(packageInfo.applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(LOGTAG, "Couldn't find package " + packageName);
@@ -479,7 +479,6 @@
                 newPackageInfo = pm.getPackageInfo(
                     response.packageInfo.packageName,
                     PackageManager.GET_SHARED_LIBRARY_FILES
-                    | PackageManager.MATCH_DEBUG_TRIAGED_MISSING
                     // Make sure that we fetch the current provider even if its not
                     // installed for the current user
                     | PackageManager.MATCH_UNINSTALLED_PACKAGES
diff --git a/core/java/android/webkit/WebViewUpdateManager.java b/core/java/android/webkit/WebViewUpdateManager.java
index dd48df9..0eb71001 100644
--- a/core/java/android/webkit/WebViewUpdateManager.java
+++ b/core/java/android/webkit/WebViewUpdateManager.java
@@ -19,12 +19,14 @@
 import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 
 /** @hide */
@@ -43,8 +45,11 @@
      *
      * This exists for the benefit of callsites without a {@link Context}; prefer
      * {@link Context#getSystemService(Class)} otherwise.
+     *
+     * This can only be used on devices with {@link PackageManager#FEATURE_WEBVIEW}.
      */
     @SuppressLint("ManagerLookup") // service opts in to getSystemServiceWithNoContext()
+    @RequiresFeature(PackageManager.FEATURE_WEBVIEW)
     public static @Nullable WebViewUpdateManager getInstance() {
         return (WebViewUpdateManager) SystemServiceRegistry.getSystemServiceWithNoContext(
                 Context.WEBVIEW_UPDATE_SERVICE);
diff --git a/core/java/android/webkit/WebViewUpdateService.java b/core/java/android/webkit/WebViewUpdateService.java
index 6f53dde..01af182 100644
--- a/core/java/android/webkit/WebViewUpdateService.java
+++ b/core/java/android/webkit/WebViewUpdateService.java
@@ -34,11 +34,11 @@
      */
     public static WebViewProviderInfo[] getAllWebViewPackages() {
         if (Flags.updateServiceIpcWrapper()) {
-            WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
-            if (manager == null) {
+            if (WebViewFactory.isWebViewSupported()) {
+                return WebViewUpdateManager.getInstance().getAllWebViewPackages();
+            } else {
                 return new WebViewProviderInfo[0];
             }
-            return manager.getAllWebViewPackages();
         } else {
             IWebViewUpdateService service = getUpdateService();
             if (service == null) {
@@ -57,11 +57,11 @@
      */
     public static WebViewProviderInfo[] getValidWebViewPackages() {
         if (Flags.updateServiceIpcWrapper()) {
-            WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
-            if (manager == null) {
+            if (WebViewFactory.isWebViewSupported()) {
+                return WebViewUpdateManager.getInstance().getValidWebViewPackages();
+            } else {
                 return new WebViewProviderInfo[0];
             }
-            return manager.getValidWebViewPackages();
         } else {
             IWebViewUpdateService service = getUpdateService();
             if (service == null) {
@@ -80,11 +80,11 @@
      */
     public static String getCurrentWebViewPackageName() {
         if (Flags.updateServiceIpcWrapper()) {
-            WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
-            if (manager == null) {
+            if (WebViewFactory.isWebViewSupported()) {
+                return WebViewUpdateManager.getInstance().getCurrentWebViewPackageName();
+            } else {
                 return null;
             }
-            return manager.getCurrentWebViewPackageName();
         } else {
             IWebViewUpdateService service = getUpdateService();
             if (service == null) {
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index defe61e..b21a490c 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -9,3 +9,12 @@
     bug: "319292658"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "mainline_apis"
+    is_exported: true
+    namespace: "webview"
+    description: "New APIs required by WebViewBootstrap mainline module"
+    bug: "310653407"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ab6b512..3c854ea 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static android.view.flags.Flags.enableTouchScrollFeedback;
+import static android.view.flags.Flags.scrollFeedbackApi;
 import static android.view.flags.Flags.viewVelocityApi;
 
 import android.annotation.ColorInt;
@@ -82,7 +84,6 @@
 import android.view.autofill.AutofillId;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.ContentCaptureSession;
-import android.view.flags.Flags;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
@@ -3703,7 +3704,6 @@
                 // If it's non-null, we're already in a scroll.
                 mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
             }
-
             if (y != mLastY) {
                 // We may be here after stopping a fling and continuing to scroll.
                 // If so, we haven't disallowed intercepting touch events yet.
@@ -3735,8 +3735,15 @@
                 boolean atEdge = false;
                 if (incrementalDeltaY != 0) {
                     atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
-                }
 
+                    // TODO: b/360198915 - Add unit testing for using ScrollFeedbackProvider
+                    if (enableTouchScrollFeedback()) {
+                        initHapticScrollFeedbackProviderIfNotExists();
+                        mHapticScrollFeedbackProvider.onScrollProgress(
+                                vtev.getDeviceId(), vtev.getSource(), MotionEvent.AXIS_Y,
+                                incrementalDeltaY);
+                    }
+                }
                 // Check to see if we have bumped into the scroll limit
                 motionView = this.getChildAt(motionIndex);
                 if (motionView != null) {
@@ -3745,7 +3752,6 @@
                     final int motionViewRealTop = motionView.getTop();
                     if (atEdge) {
                         // Apply overscroll
-
                         int overscroll = -incrementalDeltaY -
                                 (motionViewRealTop - motionViewPrevTop);
                         if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
@@ -3772,6 +3778,15 @@
                                     mDirection = 0; // Reset when entering overscroll.
                                     mTouchMode = TOUCH_MODE_OVERSCROLL;
                                 }
+
+                                if (enableTouchScrollFeedback()) {
+                                    initHapticScrollFeedbackProviderIfNotExists();
+                                    mHapticScrollFeedbackProvider.onScrollLimit(
+                                            vtev.getDeviceId(), vtev.getSource(),
+                                            MotionEvent.AXIS_Y,
+                                            /* isStart= */ incrementalDeltaY > 0);
+                                }
+
                                 if (incrementalDeltaY > 0) {
                                     mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(),
                                             (float) x / getWidth());
@@ -3981,7 +3996,6 @@
         if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) {
             return true;
         }
-
         initVelocityTrackerIfNotExists();
         final MotionEvent vtev = MotionEvent.obtain(ev);
 
@@ -4520,7 +4534,7 @@
                     final int overscrollMode = getOverScrollMode();
 
                     if (!trackMotionScroll(delta, delta)) {
-                        if (Flags.scrollFeedbackApi()) {
+                        if (scrollFeedbackApi()) {
                             initHapticScrollFeedbackProviderIfNotExists();
                             mHapticScrollFeedbackProvider.onScrollProgress(
                                     event.getDeviceId(), event.getSource(), axis, delta);
@@ -4536,7 +4550,7 @@
                         float overscroll = (delta - (motionViewRealTop - motionViewPrevTop))
                                 / ((float) getHeight());
                         boolean hitTopLimit = delta > 0;
-                        if (Flags.scrollFeedbackApi()) {
+                        if (scrollFeedbackApi()) {
                             initHapticScrollFeedbackProviderIfNotExists();
                             mHapticScrollFeedbackProvider.onScrollLimit(
                                     event.getDeviceId(), event.getSource(), axis,
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 9931aea..ac5656d 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -289,8 +289,7 @@
 
     private synchronized void updateText(long now) {
         mNow = now;
-        long seconds = mCountDown ? mBase - now : now - mBase;
-        seconds /= 1000;
+        long seconds = Math.round((mCountDown ? mBase - now - 499 : now - mBase) / 1000f);
         boolean negative = false;
         if (seconds < 0) {
             seconds = -seconds;
@@ -348,9 +347,19 @@
     };
 
     private void postTickOnNextSecond() {
-        long nowMillis = SystemClock.elapsedRealtime();
-        int millis = (int) ((nowMillis - mBase) % 1000);
-        postDelayed(mTickRunnable, 1000 - millis);
+        long nowMillis = mNow;
+        long delayMillis;
+        if (mCountDown) {
+            delayMillis = (mBase - nowMillis) % 1000;
+            if (delayMillis <= 0) {
+                delayMillis += 1000;
+            }
+        } else {
+            delayMillis = 1000 - (Math.abs(nowMillis - mBase) % 1000);
+        }
+        // Aim for 1 millisecond into the next second so we don't update exactly on the second
+        delayMillis++;
+        postDelayed(mTickRunnable, delayMillis);
     }
 
     void dispatchChronometerTick() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d28c953..0acc6bd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -20,6 +20,7 @@
 import static android.widget.TextView.ACCESSIBILITY_ACTION_SMART_START_ID;
 
 import static com.android.graphics.hwui.flags.Flags.highContrastTextSmallTextRect;
+import static com.android.text.flags.Flags.contextMenuHideUnavailableItems;
 
 import android.R;
 import android.animation.ValueAnimator;
@@ -474,7 +475,11 @@
     private final AccessibilitySmartActions mA11ySmartActions;
     private InsertModeController mInsertModeController;
 
-    Editor(TextView textView) {
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
         mTextView.setFilters(mTextView.getFilters());
@@ -3206,16 +3211,6 @@
             }
         }
 
-        final int menuItemOrderUndo = 2;
-        final int menuItemOrderRedo = 3;
-        final int menuItemOrderCut = 4;
-        final int menuItemOrderCopy = 5;
-        final int menuItemOrderPaste = 6;
-        final int menuItemOrderPasteAsPlainText = 7;
-        final int menuItemOrderSelectAll = 8;
-        final int menuItemOrderShare = 9;
-        final int menuItemOrderAutofill = 10;
-
         menu.setOptionalIconsVisible(true);
         menu.setGroupDividerEnabled(true);
 
@@ -3224,7 +3219,18 @@
         final int keyboard = mTextView.getResources().getConfiguration().keyboard;
         menu.setQwertyMode(keyboard == Configuration.KEYBOARD_QWERTY);
 
-        final TypedArray a = mTextView.getContext().obtainStyledAttributes(new int[] {
+        setTextContextMenuItems(menu);
+
+        mPreserveSelection = true;
+
+        // No-op for the old context menu because it doesn't have icons.
+        adjustIconSpacing(menu);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public void setTextContextMenuItems(ContextMenu menu) {
+        final TypedArray a = mTextView.getContext().obtainStyledAttributes(new int[]{
                 // TODO: Make Undo/Redo be public attribute.
                 com.android.internal.R.attr.actionModeUndoDrawable,
                 com.android.internal.R.attr.actionModeRedoDrawable,
@@ -3235,68 +3241,146 @@
                 android.R.attr.actionModeShareDrawable,
         });
 
-        menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
-                com.android.internal.R.string.undo)
-                .setAlphabeticShortcut('z')
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
-                .setIcon(a.getDrawable(0))
-                .setEnabled(mTextView.canUndo());
-        menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
-                com.android.internal.R.string.redo)
-                .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
-                .setIcon(a.getDrawable(1))
-                .setEnabled(mTextView.canRedo());
+        final int menuItemOrderUndo = 2;
+        final int menuItemOrderRedo = 3;
+        final int menuItemOrderCut = 4;
+        final int menuItemOrderCopy = 5;
+        final int menuItemOrderPaste = 6;
+        final int menuItemOrderPasteAsPlainText = 7;
+        final int menuItemOrderSelectAll = 8;
+        final int menuItemOrderShare = 9;
+        final int menuItemOrderAutofill = 10;
 
-        menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
-                com.android.internal.R.string.cut)
-                .setAlphabeticShortcut('x')
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
-                .setIcon(a.getDrawable(2))
-                .setEnabled(mTextView.canCut());
-        menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
-                com.android.internal.R.string.copy)
-                .setAlphabeticShortcut('c')
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
-                .setIcon(a.getDrawable(3))
-                .setEnabled(mTextView.canCopy());
-        menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
-                com.android.internal.R.string.paste)
-                .setAlphabeticShortcut('v')
-                .setEnabled(mTextView.canPaste())
-                .setIcon(a.getDrawable(4))
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
-        menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
-                        menuItemOrderPasteAsPlainText,
-                com.android.internal.R.string.paste_as_plain_text)
-                .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
-                .setEnabled(mTextView.canPasteAsPlainText())
-                .setIcon(a.getDrawable(4))
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
-        menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
-                        menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
-                .setAlphabeticShortcut('a')
-                .setEnabled(mTextView.canSelectAllText())
-                .setIcon(a.getDrawable(5))
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        if (contextMenuHideUnavailableItems()) {
+            if (mTextView.canUndo()) {
+                menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+                                com.android.internal.R.string.undo)
+                        .setAlphabeticShortcut('z')
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                        .setIcon(a.getDrawable(0));
+            }
 
-        menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
-                com.android.internal.R.string.share)
-                .setEnabled(mTextView.canShare())
-                .setIcon(a.getDrawable(6))
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
-        final String selected = mTextView.getSelectedText();
-        menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
-                android.R.string.autofill)
-                .setEnabled(mTextView.canRequestAutofill()
-                        && (selected == null || selected.isEmpty()))
-                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            if (mTextView.canRedo()) {
+                menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+                                com.android.internal.R.string.redo)
+                        .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                        .setIcon(a.getDrawable(1));
+            }
 
-        mPreserveSelection = true;
+            if (mTextView.canCut()) {
+                menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+                                com.android.internal.R.string.cut)
+                        .setAlphabeticShortcut('x')
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                        .setIcon(a.getDrawable(2));
+            }
+
+            if (mTextView.canCopy()) {
+                menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+                                com.android.internal.R.string.copy)
+                        .setAlphabeticShortcut('c')
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                        .setIcon(a.getDrawable(3));
+            }
+
+            if (mTextView.canPaste()) {
+                menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+                                com.android.internal.R.string.paste)
+                        .setAlphabeticShortcut('v')
+                        .setIcon(a.getDrawable(4))
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            }
+
+            if (mTextView.canPasteAsPlainText()) {
+                menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+                                menuItemOrderPasteAsPlainText,
+                                com.android.internal.R.string.paste_as_plain_text)
+                        .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+                        .setIcon(a.getDrawable(4))
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            }
+
+            if (mTextView.canSelectAllText()) {
+                menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+                                menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+                        .setAlphabeticShortcut('a')
+                        .setIcon(a.getDrawable(5))
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            }
+
+            if (mTextView.canShare()) {
+                menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+                                com.android.internal.R.string.share)
+                        .setIcon(a.getDrawable(6))
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            }
+
+            final String selected = mTextView.getSelectedText();
+            if (mTextView.canRequestAutofill() && (selected == null || selected.isEmpty())) {
+                menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+                                android.R.string.autofill)
+                        .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            }
+        } else {
+            menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_UNDO, menuItemOrderUndo,
+                            com.android.internal.R.string.undo)
+                    .setAlphabeticShortcut('z')
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                    .setIcon(a.getDrawable(0))
+                    .setEnabled(mTextView.canUndo());
+            menu.add(CONTEXT_MENU_GROUP_UNDO_REDO, TextView.ID_REDO, menuItemOrderRedo,
+                            com.android.internal.R.string.redo)
+                    .setAlphabeticShortcut('z', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                    .setIcon(a.getDrawable(1))
+                    .setEnabled(mTextView.canRedo());
+
+            menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_CUT, menuItemOrderCut,
+                            com.android.internal.R.string.cut)
+                    .setAlphabeticShortcut('x')
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                    .setIcon(a.getDrawable(2))
+                    .setEnabled(mTextView.canCut());
+            menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_COPY, menuItemOrderCopy,
+                            com.android.internal.R.string.copy)
+                    .setAlphabeticShortcut('c')
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener)
+                    .setIcon(a.getDrawable(3))
+                    .setEnabled(mTextView.canCopy());
+            menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE, menuItemOrderPaste,
+                            com.android.internal.R.string.paste)
+                    .setAlphabeticShortcut('v')
+                    .setEnabled(mTextView.canPaste())
+                    .setIcon(a.getDrawable(4))
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_PASTE_AS_PLAIN_TEXT,
+                            menuItemOrderPasteAsPlainText,
+                            com.android.internal.R.string.paste_as_plain_text)
+                    .setAlphabeticShortcut('v', KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)
+                    .setEnabled(mTextView.canPasteAsPlainText())
+                    .setIcon(a.getDrawable(4))
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            menu.add(CONTEXT_MENU_GROUP_CLIPBOARD, TextView.ID_SELECT_ALL,
+                            menuItemOrderSelectAll, com.android.internal.R.string.selectAll)
+                    .setAlphabeticShortcut('a')
+                    .setEnabled(mTextView.canSelectAllText())
+                    .setIcon(a.getDrawable(5))
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+
+            menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_SHARE, menuItemOrderShare,
+                            com.android.internal.R.string.share)
+                    .setEnabled(mTextView.canShare())
+                    .setIcon(a.getDrawable(6))
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+            final String selected = mTextView.getSelectedText();
+            menu.add(CONTEXT_MENU_GROUP_MISC, TextView.ID_AUTOFILL, menuItemOrderAutofill,
+                            android.R.string.autofill)
+                    .setEnabled(mTextView.canRequestAutofill()
+                            && (selected == null || selected.isEmpty()))
+                    .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        }
         a.recycle();
-
-        // No-op for the old context menu because it doesn't have icons.
-        adjustIconSpacing(menu);
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 61ecc62..a4b28ad 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10627,7 +10627,7 @@
 
         int startOffset = mLayout.getOffsetForHorizontal(line, point.x);
         if (mLayout.isLevelBoundary(startOffset)) {
-            // TODO(b/247551937): Support gesture at level boundaries.
+            // Gesture at level boundaries is not supported.
             return handleGestureFailure(gesture);
         }
 
@@ -12255,7 +12255,11 @@
         return selectionMin >= 0 && selectionMax > 0 && selectionMin != selectionMax;
     }
 
-    String getSelectedText() {
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public String getSelectedText() {
         if (!hasSelection()) {
             return null;
         }
@@ -14080,7 +14084,11 @@
         structure.setInputType(getInputType());
     }
 
-    boolean canRequestAutofill() {
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public boolean canRequestAutofill() {
         if (!isAutofillable()) {
             return false;
         }
@@ -14367,7 +14375,7 @@
 
         Matrix matrix = mTempMatrix;
         matrix.reset();
-        transformMatrixToLocal(matrix);
+        transformMatrixRootToLocal(matrix);
         editorBounds.set(rect);
         // When the view has transformations like scaleX/scaleY computing the global visible
         // rectangle will already apply the transformations. The getLocalVisibleRect only offsets
@@ -15544,15 +15552,21 @@
         }
     }
 
-    boolean canUndo() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canUndo() {
         return mEditor != null && mEditor.canUndo();
     }
 
-    boolean canRedo() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canRedo() {
         return mEditor != null && mEditor.canRedo();
     }
 
-    boolean canCut() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canCut() {
         if (hasPasswordTransformationMethod()) {
             return false;
         }
@@ -15565,7 +15579,9 @@
         return false;
     }
 
-    boolean canCopy() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canCopy() {
         if (hasPasswordTransformationMethod()) {
             return false;
         }
@@ -15586,7 +15602,9 @@
                 && isSuggestionsEnabled() && mEditor.shouldOfferToShowSuggestions();
     }
 
-    boolean canShare() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canShare() {
         if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()
                 || !getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_textShareSupported)) {
@@ -15605,8 +15623,10 @@
         return mDeviceProvisionedState == DEVICE_PROVISIONED_YES;
     }
 
+    /** @hide */
+    @VisibleForTesting
     @UnsupportedAppUsage
-    boolean canPaste() {
+    public boolean canPaste() {
         return (mText instanceof Editable
                 && mEditor != null && mEditor.mKeyListener != null
                 && getSelectionStart() >= 0
@@ -15614,7 +15634,9 @@
                 && getClipboardManagerForUser().hasPrimaryClip());
     }
 
-    boolean canPasteAsPlainText() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canPasteAsPlainText() {
         if (!canPaste()) {
             return false;
         }
@@ -15636,7 +15658,9 @@
         return canShare();
     }
 
-    boolean canSelectAllText() {
+    /** @hide */
+    @VisibleForTesting
+    public boolean canSelectAllText() {
         return canSelectText() && !hasPasswordTransformationMethod()
                 && !(getSelectionStart() == 0 && getSelectionEnd() == mText.length());
     }
diff --git a/core/java/android/window/BackAnimationAdapter.java b/core/java/android/window/BackAnimationAdapter.java
index 5eb34e6..153e153 100644
--- a/core/java/android/window/BackAnimationAdapter.java
+++ b/core/java/android/window/BackAnimationAdapter.java
@@ -16,9 +16,12 @@
 
 package android.window;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.ArrayList;
+
 /**
  * Object that describes how to run a remote back animation.
  *
@@ -26,6 +29,7 @@
  */
 public class BackAnimationAdapter implements Parcelable {
     private final IBackAnimationRunner mRunner;
+    private int[] mSupportedAnimators;
 
     public BackAnimationAdapter(IBackAnimationRunner runner) {
         mRunner = runner;
@@ -33,12 +37,23 @@
 
     public BackAnimationAdapter(Parcel in) {
         mRunner = IBackAnimationRunner.Stub.asInterface(in.readStrongBinder());
+        mSupportedAnimators = new int[in.readInt()];
+        in.readIntArray(mSupportedAnimators);
     }
 
     public IBackAnimationRunner getRunner() {
         return mRunner;
     }
 
+    /** Update the latest animators in the system. */
+    public void updateSupportedAnimators(@NonNull ArrayList<Integer> animators) {
+        final int size = animators.size();
+        mSupportedAnimators = new int[size];
+        for (int i = size - 1; i >= 0; --i) {
+            mSupportedAnimators[i] = animators.get(i);
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -47,6 +62,8 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeStrongInterface(mRunner);
+        dest.writeInt(mSupportedAnimators.length);
+        dest.writeIntArray(mSupportedAnimators);
     }
 
     public static final @android.annotation.NonNull Creator<BackAnimationAdapter> CREATOR =
@@ -59,4 +76,19 @@
             return new BackAnimationAdapter[size];
         }
     };
+
+    /**
+     * Check if the back type is animatable.
+     */
+    public boolean isAnimatable(@BackNavigationInfo.BackTargetType int backType) {
+        if (mSupportedAnimators == null) {
+            return false;
+        }
+        for (int i = mSupportedAnimators.length - 1; i >= 0; --i) {
+            if (backType == mSupportedAnimators[i]) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/core/java/android/window/IBackAnimationRunner.aidl b/core/java/android/window/IBackAnimationRunner.aidl
index b1d7582..a801776 100644
--- a/core/java/android/window/IBackAnimationRunner.aidl
+++ b/core/java/android/window/IBackAnimationRunner.aidl
@@ -38,14 +38,13 @@
     /**
      * Called when the system is ready for the handler to start animating all the visible tasks.
      * @param apps The list of departing (type=MODE_CLOSING) and entering (type=MODE_OPENING)
-                   windows to animate,
-     * @param wallpapers The list of wallpapers to animate.
-     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     *             windows to animate,
+     * @param prepareOpenTransition If non-null, the animation should start after receive open
+     *             transition
      * @param finishedCallback The callback to invoke when the animation is finished.
      */
     void onAnimationStart(
             in RemoteAnimationTarget[] apps,
-            in RemoteAnimationTarget[] wallpapers,
-            in RemoteAnimationTarget[] nonApps,
+            in IBinder prepareOpenTransition,
             in IBackAnimationFinishedCallback finishedCallback) = 2;
 }
\ No newline at end of file
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index ac57c00..b8a11cf0 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import android.os.Bundle;
 import android.os.IBinder;
 import android.view.RemoteAnimationDefinition;
 import android.window.ITaskFragmentOrganizer;
@@ -24,14 +25,21 @@
 
 /** @hide */
 interface ITaskFragmentOrganizerController {
-
     /**
      * Registers a TaskFragmentOrganizer to manage TaskFragments. Registering a system
      * organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer will have additional
      * system capabilities.
+     *
+     * @param organizer          The TaskFragmentOrganizer to register
+     * @param isSystemOrganizer  If it is a system organizer
+     * @param outSavedState      Returning the saved state (if any) that previously saved. This is
+     *                           useful when retrieve the state from the same TaskFragmentOrganizer
+     *                           that was killed by the system (e.g. to reclaim memory). Note that
+     *                           the save state is dropped and unable to retrieve once the system
+     *                           restarts or the organizer is unregistered.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true)")
-    void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer);
+    void registerOrganizer(in ITaskFragmentOrganizer organizer, in boolean isSystemOrganizer, out Bundle outSavedState);
 
     /**
      * Unregisters a previously registered TaskFragmentOrganizer.
@@ -39,6 +47,25 @@
     void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
 
     /**
+     * Registers remote animations per transition type for the organizer. It will override the
+     * animations if the transition only contains windows that belong to the organized
+     * TaskFragments in the given Task.
+     */
+    void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
+        in RemoteAnimationDefinition definition);
+
+    /**
+     * Unregisters remote animations per transition type for the organizer.
+     */
+    void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
+
+    /**
+     * Saves the state in the system, where the state can be restored if the process of
+     * the TaskFragmentOrganizer is restarted.
+     */
+    void setSavedState(in ITaskFragmentOrganizer organizer, in Bundle savedState);
+
+    /**
      * Notifies the server that the organizer has finished handling the given transaction. The
      * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
      */
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 205f1de..9a7bce0 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -59,7 +59,6 @@
 import android.util.Log;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -152,9 +151,11 @@
         @VisibleForTesting
         public void setFrames(Rect frame, Rect systemBarInsets) {
             mFrame.set(frame);
-            mSystemBarInsets.set(systemBarInsets);
             mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
-            mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+            if (!Flags.drawSnapshotAspectRatioMatch() && systemBarInsets != null) {
+                mSystemBarInsets.set(systemBarInsets);
+                mSystemBarBackgroundPainter.setInsets(systemBarInsets);
+            }
         }
 
         private void drawSnapshot(boolean releaseAfterDraw) {
@@ -185,7 +186,6 @@
 
         private void drawSizeMismatchSnapshot() {
             final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
-            final SurfaceSession session = new SurfaceSession();
 
             // We consider nearly matched dimensions as there can be rounding errors and the user
             // won't notice very minute differences from scaling one dimension more than the other
@@ -193,7 +193,7 @@
                     && !Flags.drawSnapshotAspectRatioMatch();
 
             // Keep a reference to it such that it doesn't get destroyed when finalized.
-            SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
+            SurfaceControl childSurfaceControl = new SurfaceControl.Builder()
                     .setName(mTitle + " - task-snapshot-surface")
                     .setBLASTLayer()
                     .setFormat(buffer.getFormat())
@@ -396,9 +396,12 @@
         final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
         final ActivityManager.TaskDescription taskDescription =
                 getOrCreateTaskDescription(runningTaskInfo);
-        drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
-                attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
-        final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+        Rect systemBarInsets = null;
+        if (!Flags.drawSnapshotAspectRatioMatch()) {
+            drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
+                    attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
+            systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
+        }
         drawSurface.setFrames(windowBounds, systemBarInsets);
         drawSurface.drawSnapshot(releaseAfterDraw);
     }
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 8e429cb..4cc0d8a 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -34,6 +34,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 
 import com.android.window.flags.Flags;
@@ -165,17 +166,12 @@
      */
     @CallSuper
     public void registerOrganizer() {
-        // TODO(b/302420256) point to registerOrganizer(boolean) when flag is removed.
-        try {
-            getController().registerOrganizer(mInterface, false /* isSystemOrganizer */);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        registerOrganizer(false /* isSystemOrganizer */, null /* outSavedState */);
     }
 
     /**
      * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
-     *
+     * <p>
      * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
      * will have additional system capabilities, including: (1) it will receive SurfaceControl for
      * the organized TaskFragment, and (2) it needs to update the
@@ -187,8 +183,31 @@
     @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
     @FlaggedApi(Flags.FLAG_TASK_FRAGMENT_SYSTEM_ORGANIZER_FLAG)
     public void registerOrganizer(boolean isSystemOrganizer) {
+        registerOrganizer(isSystemOrganizer, null /* outSavedState */);
+    }
+
+    /**
+     * Registers a {@link TaskFragmentOrganizer} to manage TaskFragments.
+     * <p>
+     * Registering a system organizer requires MANAGE_ACTIVITY_TASKS permission, and the organizer
+     * will have additional system capabilities, including: (1) it will receive SurfaceControl for
+     * the organized TaskFragment, and (2) it needs to update the
+     * {@link android.view.SurfaceControl} following the window change accordingly.
+     *
+     * @param isSystemOrganizer  If it is a system organizer
+     * @param outSavedState      Returning the saved state (if any) that previously saved. This is
+     *                           useful when retrieve the state from the same TaskFragmentOrganizer
+     *                           that was killed by the system (e.g. to reclaim memory). Note that
+     *                           the save state is dropped and unable to retrieve once the system
+     *                           restarts or the organizer is unregistered.
+     * @hide
+     */
+    @CallSuper
+    @RequiresPermission(value = "android.permission.MANAGE_ACTIVITY_TASKS", conditional = true)
+    public void registerOrganizer(boolean isSystemOrganizer, @Nullable Bundle outSavedState) {
         try {
-            getController().registerOrganizer(mInterface, isSystemOrganizer);
+            getController().registerOrganizer(mInterface, isSystemOrganizer,
+                    outSavedState != null ? outSavedState : new Bundle());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -207,6 +226,58 @@
     }
 
     /**
+     * Registers remote animations per transition type for the organizer. It will override the
+     * animations if the transition only contains windows that belong to the organized
+     * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
+     * @hide
+     */
+    @CallSuper
+    public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
+        try {
+            getController().registerRemoteAnimations(mInterface, definition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters remote animations per transition type for the organizer.
+     * @hide
+     */
+    @CallSuper
+    public void unregisterRemoteAnimations() {
+        try {
+            getController().unregisterRemoteAnimations(mInterface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Saves the state in the system, where the state can be restored if the process of
+     * the TaskFragmentOrganizer is restarted.
+     *
+     * @hide
+     *
+     * @param state the state to save.
+     */
+    public void setSavedState(@NonNull Bundle state) {
+        if (!Flags.aeBackStackRestore()) {
+            return;
+        }
+
+        if (state.getSize() > 200000) {
+            throw new IllegalArgumentException("Saved state too large, " + state.getSize());
+        }
+
+        try {
+            getController().setSavedState(mInterface, state);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Notifies the server that the organizer has finished handling the given transaction. The
      * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
      *
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index 1555416..efd74c3 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -41,6 +41,8 @@
 
     private final int mDisplayId;
 
+    private final int mTaskId;
+
     private final boolean mVisible;
 
     private final boolean mHasDirectActivity;
@@ -49,9 +51,11 @@
 
     /** @hide */
     public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
-            boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
+            int taskId, boolean visible, boolean hasDirectActivity,
+            @Nullable SurfaceControl decorSurface) {
         mConfiguration.setTo(configuration);
         mDisplayId = displayId;
+        mTaskId = taskId;
         mVisible = visible;
         mHasDirectActivity = hasDirectActivity;
         mDecorSurface = decorSurface;
@@ -61,6 +65,7 @@
     public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
         mConfiguration.setTo(info.getConfiguration());
         mDisplayId = info.mDisplayId;
+        mTaskId = info.mTaskId;
         mVisible = info.mVisible;
         mHasDirectActivity = info.mHasDirectActivity;
         mDecorSurface = info.mDecorSurface;
@@ -87,6 +92,15 @@
     }
 
     /**
+     * The id of the parent Task.
+     *
+     * @hide
+     */
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
      * Whether the parent Task is visible or not
      *
      * @hide
@@ -120,7 +134,8 @@
             return false;
         }
         return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
-                && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity
+                && mTaskId == that.mTaskId && mVisible == that.mVisible
+                && mHasDirectActivity == that.mHasDirectActivity
                 && mDecorSurface == that.mDecorSurface;
     }
 
@@ -140,6 +155,7 @@
         return TaskFragmentParentInfo.class.getSimpleName() + ":{"
                 + "config=" + mConfiguration
                 + ", displayId=" + mDisplayId
+                + ", taskId=" + mTaskId
                 + ", visible=" + mVisible
                 + ", hasDirectActivity=" + mHasDirectActivity
                 + ", decorSurface=" + mDecorSurface
@@ -163,6 +179,7 @@
         final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
         return mConfiguration.equals(that.mConfiguration)
                 && mDisplayId == that.mDisplayId
+                && mTaskId == that.mTaskId
                 && mVisible == that.mVisible
                 && mHasDirectActivity == that.mHasDirectActivity
                 && mDecorSurface == that.mDecorSurface;
@@ -172,6 +189,7 @@
     public int hashCode() {
         int result = mConfiguration.hashCode();
         result = 31 * result + mDisplayId;
+        result = 31 * result + mTaskId;
         result = 31 * result + (mVisible ? 1 : 0);
         result = 31 * result + (mHasDirectActivity ? 1 : 0);
         result = 31 * result + Objects.hashCode(mDecorSurface);
@@ -183,6 +201,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         mConfiguration.writeToParcel(dest, flags);
         dest.writeInt(mDisplayId);
+        dest.writeInt(mTaskId);
         dest.writeBoolean(mVisible);
         dest.writeBoolean(mHasDirectActivity);
         dest.writeTypedObject(mDecorSurface, flags);
@@ -191,6 +210,7 @@
     private TaskFragmentParentInfo(Parcel in) {
         mConfiguration.readFromParcel(in);
         mDisplayId = in.readInt();
+        mTaskId = in.readInt();
         mVisible = in.readBoolean();
         mHasDirectActivity = in.readBoolean();
         mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 253337b..fe936f7 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -115,8 +115,11 @@
     @DataClass(genToString = true, genSetters = true, genBuilder = false, genConstructor = false)
     public static final class DisplayChange implements Parcelable {
         private final int mDisplayId;
+
+        // If non-null, these bounds changes should ignore any potential rotation changes.
         @Nullable private Rect mStartAbsBounds = null;
         @Nullable private Rect mEndAbsBounds = null;
+
         private int mStartRotation = WindowConfiguration.ROTATION_UNDEFINED;
         private int mEndRotation = WindowConfiguration.ROTATION_UNDEFINED;
         private boolean mPhysicalDisplayChanged = false;
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 34c6399..ac9bec3 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -26,6 +26,7 @@
 import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.InputConfig;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -35,6 +36,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -83,7 +85,7 @@
         public final boolean isVisible;
 
         /**
-         * Return the transform to get the bounds from display space into window space.
+         * The transform from display space to window space.
          */
         @NonNull
         public final Matrix transform;
@@ -150,12 +152,48 @@
         }
     }
 
+    /**
+     * Display properties passed to {@code @WindowInfosListenerForTest#onWindowInfosChanged}.
+     */
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
+    public static class DisplayInfo {
+
+        /**
+         * The display's id.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        public final int displayId;
+
+        /**
+         * The display's transform from physical display space to logical display space.
+         */
+        @SuppressLint("UnflaggedApi") // The API is only used for tests.
+        @NonNull
+        public final Matrix transform;
+
+        DisplayInfo(int displayId, @NonNull Matrix transform) {
+            this.displayId = displayId;
+            this.transform = transform;
+        }
+
+        @Override
+        public String toString() {
+            return TextUtils.formatSimple(
+                    "DisplayInfo{displayId=%s, transform=%s}", displayId, transform);
+        }
+    }
+
     private static final String TAG = "WindowInfosListenerForTest";
 
-    private ArrayMap<Consumer<List<WindowInfo>>, WindowInfosListener> mListeners;
+    private final ArrayMap<BiConsumer<List<WindowInfo>, List<DisplayInfo>>, WindowInfosListener>
+            mListeners;
+    private final ArrayMap<Consumer<List<WindowInfo>>, BiConsumer<List<WindowInfo>,
+            List<DisplayInfo>>>
+            mConsumersToBiConsumers;
 
     public WindowInfosListenerForTest() {
         mListeners = new ArrayMap<>();
+        mConsumersToBiConsumers = new ArrayMap<>();
     }
 
     /**
@@ -164,10 +202,34 @@
      *
      * @param consumer Consumer that is called with reverse Z ordered lists of WindowInfo instances
      *                 where the first value is the topmost window.
+     *
+     * @deprecated Use {@link #addWindowInfosListener(BiConsumer)} which provides window and
+     *             display info.
      */
+    @Deprecated
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
+    @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+    public void addWindowInfosListener(@NonNull Consumer<List<WindowInfo>> consumer) {
+        // This method isn't used in current versions of CTS but can't be removed yet because
+        // newer builds need to pass on some older versions of CTS.
+        BiConsumer<List<WindowInfo>, List<DisplayInfo>> biConsumer =
+                (windowHandles, displayInfos) -> consumer.accept(windowHandles);
+        mConsumersToBiConsumers.put(consumer, biConsumer);
+        addWindowInfosListener(biConsumer);
+    }
+
+    /**
+     * Register a listener that is called when the system's list of visible windows or displays has
+     * changes in position or visibility.
+     *
+     * @param consumer Consumer that is called with window and display info. {@code WindowInfo}
+     *                 instances are passed as a reverse Z ordered list of WindowInfo instances
+     *                 where the first value is the topmost window.
+     */
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
     @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
     public void addWindowInfosListener(
-            @NonNull Consumer<List<WindowInfo>> consumer) {
+            @NonNull BiConsumer<List<WindowInfo>, List<DisplayInfo>> consumer) {
         var calledWithInitialState = new CountDownLatch(1);
         var listener = new WindowInfosListener() {
             @Override
@@ -180,20 +242,47 @@
                             "Exception thrown while waiting for listener to be called with "
                                     + "initial state");
                 }
-                consumer.accept(buildWindowInfos(windowHandles, displayInfos));
+                var params = buildParams(windowHandles, displayInfos);
+                consumer.accept(params.first, params.second);
             }
         };
         mListeners.put(consumer, listener);
         Pair<InputWindowHandle[], WindowInfosListener.DisplayInfo[]> initialState =
                 listener.register();
-        consumer.accept(buildWindowInfos(initialState.first, initialState.second));
+        Pair<List<WindowInfo>, List<DisplayInfo>> params =
+                buildParams(initialState.first, initialState.second);
+
+        consumer.accept(params.first, params.second);
         calledWithInitialState.countDown();
     }
 
     /**
      * Unregisters the listener.
+     *
+     * @deprecated Use {@link #addWindowInfosListener(BiConsumer)} and
+     *             {@link #removeWindowInfosListener(BiConsumer)} instead.
      */
-    public void removeWindowInfosListener(@NonNull Consumer<List<WindowInfo>> consumer) {
+    @Deprecated
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
+    public void removeWindowInfosListener(
+            @NonNull Consumer<List<WindowInfo>> consumer) {
+        // This method isn't used in current versions of CTS but can't be removed yet because
+        // newer builds need to pass on some older versions of CTS.
+        var biConsumer = mConsumersToBiConsumers.remove(consumer);
+        if (biConsumer == null) {
+            return;
+        }
+        WindowInfosListener listener = mListeners.remove(biConsumer);
+        if (listener == null) {
+            return;
+        }
+        listener.unregister();
+    }
+
+    /** Unregisters the listener. */
+    @SuppressLint("UnflaggedApi") // The API is only used for tests.
+    public void removeWindowInfosListener(
+            @NonNull BiConsumer<List<WindowInfo>, List<DisplayInfo>> consumer) {
         WindowInfosListener listener = mListeners.remove(consumer);
         if (listener == null) {
             return;
@@ -201,15 +290,20 @@
         listener.unregister();
     }
 
-    private static List<WindowInfo> buildWindowInfos(
+    private static Pair<List<WindowInfo>, List<DisplayInfo>> buildParams(
             InputWindowHandle[] windowHandles, WindowInfosListener.DisplayInfo[] displayInfos) {
-        var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
+        var outWindowInfos = new ArrayList<WindowInfo>(windowHandles.length);
+        var outDisplayInfos = new ArrayList<DisplayInfo>(displayInfos.length);
 
         var displayInfoById = new SparseArray<WindowInfosListener.DisplayInfo>(displayInfos.length);
         for (var displayInfo : displayInfos) {
             displayInfoById.put(displayInfo.mDisplayId, displayInfo);
         }
 
+        for (var displayInfo : displayInfos) {
+            outDisplayInfos.add(new DisplayInfo(displayInfo.mDisplayId, displayInfo.mTransform));
+        }
+
         var tmp = new RectF();
         for (var handle : windowHandles) {
             var bounds = new Rect(handle.frame);
@@ -222,9 +316,10 @@
                 tmp.round(bounds);
             }
 
-            windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
-                    bounds, handle.inputConfig, handle.transform));
+            outWindowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name,
+                    handle.displayId, bounds, handle.inputConfig, handle.transform));
         }
-        return windowInfos;
+
+        return new Pair(outWindowInfos, outDisplayInfos);
     }
 }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 7bbc3db..b6c0d7c 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -306,7 +306,7 @@
     }
 
     private boolean callOnKeyPreIme() {
-        if (mViewRoot != null && !isOnBackInvokedCallbackEnabled(mViewRoot.mContext)) {
+        if (mViewRoot != null && !isOnBackInvokedCallbackEnabled()) {
             return mViewRoot.injectBackKeyEvents(/*preImeOnly*/ true);
         } else {
             return false;
@@ -505,7 +505,7 @@
             if (callback instanceof ImeBackAnimationController
                     || callback instanceof ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) {
                 // call onKeyPreIme API if the current callback is an IME callback and the app has
-                // not set enableOnBackInvokedCallback="false"
+                // not set enableOnBackInvokedCallback="true"
                 try {
                     boolean consumed = mOnKeyPreIme.getAsBoolean();
                     if (consumed) {
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 4f84817..3998ac6 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -2,14 +2,6 @@
 container: "system"
 
 flag {
-    name: "enable_scaled_resizing"
-    namespace: "lse_desktop_experience"
-    description: "Enable the resizing of un-resizable apps through scaling their bounds up/down"
-    bug: "320350734"
-    is_fixed_read_only: true
-}
-
-flag {
     name: "enable_desktop_windowing_mode"
     namespace: "lse_desktop_experience"
     description: "Enables desktop windowing"
@@ -31,6 +23,13 @@
 }
 
 flag {
+    name: "enable_windowing_scaled_resizing"
+    namespace: "lse_desktop_experience"
+    description: "Enables the resizing of non-resizable apps through scaling their bounds up/down"
+    bug: "319844447"
+}
+
+flag {
     name: "disable_non_resizable_app_snap_resizing"
     namespace: "lse_desktop_experience"
     description: "Stops non-resizable app desktop windows from being snap resized"
@@ -115,6 +114,16 @@
 }
 
 flag {
+    name: "respect_orientation_change_for_unresizeable"
+    namespace: "lse_desktop_experience"
+    description: "Whether to resize task to respect requested orientation change of unresizeable activity"
+    bug: "353338503"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "enable_camera_compat_for_desktop_windowing"
     namespace: "lse_desktop_experience"
     description: "Whether to apply Camera Compat treatment to fixed-orientation apps in desktop windowing mode"
@@ -225,3 +234,20 @@
     description: "Adds a minimize button the the caption bar"
     bug: "356843241"
 }
+
+flag {
+    name: "skip_compat_ui_education_in_desktop_mode"
+    namespace: "lse_desktop_experience"
+    description: "Ignore Compat UI educations when in Desktop Mode."
+    bug: "357062954"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "enable_desktop_windowing_persistence"
+    namespace: "lse_desktop_experience"
+    description: "Persists the desktop windowing session across reboots."
+    bug: "350456942"
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 6ce9725..cd31850 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -71,3 +71,11 @@
     bug: "339720406"
 }
 
+flag {
+    name: "bal_reduce_grace_period"
+    namespace: "responsible_apis"
+    description: "Changes to reduce or ideally remove the grace period exemption."
+    bug: "362575865"
+}
+
+
diff --git a/core/java/android/window/flags/wallpaper_manager.aconfig b/core/java/android/window/flags/wallpaper_manager.aconfig
index 01c78a0..efacc34 100644
--- a/core/java/android/window/flags/wallpaper_manager.aconfig
+++ b/core/java/android/window/flags/wallpaper_manager.aconfig
@@ -39,3 +39,23 @@
   description: "Prevent the system from sending visibility event on display state change."
   bug: "331725519"
 }
+
+flag {
+  name: "no_duplicate_surface_destroyed_events"
+  namespace: "systemui"
+  description: "Prevent the system from sending onSurfaceDestroyed() twice."
+  bug: "344461715"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "avoid_rebinding_intentionally_disconnected_wallpaper"
+  namespace: "systemui"
+  description: "Prevents rebinding with intentionally disconnected wallpaper services."
+  bug: "332871851"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 9aeccf4..a786fc2 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -19,16 +19,6 @@
 }
 
 flag {
-    name: "do_not_skip_ime_by_target_visibility"
-    namespace: "windowing_frontend"
-    description: "Avoid window traversal missing IME"
-    bug: "339375944"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "apply_lifecycle_on_pip_change"
     namespace: "windowing_frontend"
     description: "Make pip activity lifecyle change with windowing mode"
@@ -91,6 +81,14 @@
 }
 
 flag {
+  name: "transit_tracker_plumbing"
+  namespace: "windowing_frontend"
+  description: "Plumb and collect on transition tracking object instead of singleton"
+  bug: "325114242"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "transit_ready_tracking"
   namespace: "windowing_frontend"
   description: "Enable accurate transition readiness tracking"
@@ -202,6 +200,14 @@
 }
 
 flag {
+  name: "system_ui_immersive_confirmation_dialog"
+  namespace: "windowing_frontend"
+  description: "Enable the implementation of the immersive confirmation dialog on system UI side by default"
+  bug: "359713629"
+  is_fixed_read_only: true
+}
+
+flag {
   name: "ensure_wallpaper_in_transitions"
   namespace: "windowing_frontend"
   description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
@@ -212,6 +218,16 @@
 }
 
 flag {
+  name: "ensure_wallpaper_in_wear_transitions"
+  namespace: "windowing_frontend"
+  description: "Ensure that wallpaper window tokens are always present/available for collection in transitions on Wear"
+  bug: "355596979"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "custom_animations_behind_translucent"
   namespace: "windowing_frontend"
   description: "A change can use its own layer parameters to animate behind a translucent activity"
@@ -230,3 +246,14 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+  name: "always_capture_activity_snapshot"
+  namespace: "windowing_frontend"
+  description: "Always capture activity snapshot regardless predictive back status"
+  bug: "362183912"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index a6ae948..8077a55 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -61,16 +61,6 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "fix_pip_restore_to_overlay"
-    description: "Restore exit-pip activity back to ActivityEmbedding overlay"
-    bug: "297887697"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
-    namespace: "windowing_sdk"
     name: "activity_embedding_animation_customization_flag"
     description: "Whether the animation customization feature for AE is enabled"
     bug: "293658614"
@@ -120,11 +110,8 @@
 
 flag {
     namespace: "windowing_sdk"
-    name: "fix_no_container_update_without_resize"
-    description: "Fix the containers not being updated when the Task is brought to front and has the same configuration"
-    bug: "344721335"
+    name: "ae_back_stack_restore"
+    description: "Allow the ActivityEmbedding back stack to be restored after process restarted"
+    bug: "289875940"
     is_fixed_read_only: true
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
 }
diff --git a/core/java/com/android/internal/accessibility/TEST_MAPPING b/core/java/com/android/internal/accessibility/TEST_MAPPING
index 1c67399..b2b3041 100644
--- a/core/java/com/android/internal/accessibility/TEST_MAPPING
+++ b/core/java/com/android/internal/accessibility/TEST_MAPPING
@@ -2,6 +2,9 @@
   "imports": [
     {
       "path": "frameworks/base/services/accessibility/TEST_MAPPING"
+    },
+    {
+      "path": "frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/TEST_MAPPING"
     }
   ]
 }
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 4ccdf79..cf3a54b 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -109,45 +109,13 @@
     public static List<AccessibilityTarget> getInstalledTargets(Context context,
             @UserShortcutType int shortcutType) {
         final List<AccessibilityTarget> targets = new ArrayList<>();
-        targets.addAll(getAccessibilityFilteredTargets(context, shortcutType));
+        targets.addAll(getAccessibilityServiceTargets(context, shortcutType));
+        targets.addAll(getAccessibilityActivityTargets(context, shortcutType));
         targets.addAll(getAllowListingFeatureTargets(context, shortcutType));
 
         return targets;
     }
 
-    private static List<AccessibilityTarget> getAccessibilityFilteredTargets(Context context,
-            @UserShortcutType int shortcutType) {
-        final List<AccessibilityTarget> serviceTargets =
-                getAccessibilityServiceTargets(context, shortcutType);
-        final List<AccessibilityTarget> activityTargets =
-                getAccessibilityActivityTargets(context, shortcutType);
-
-        for (AccessibilityTarget activityTarget : activityTargets) {
-            serviceTargets.removeIf(
-                    serviceTarget -> arePackageNameAndLabelTheSame(serviceTarget, activityTarget));
-        }
-
-        final List<AccessibilityTarget> targets = new ArrayList<>();
-        targets.addAll(serviceTargets);
-        targets.addAll(activityTargets);
-
-        return targets;
-    }
-
-    private static boolean arePackageNameAndLabelTheSame(@NonNull AccessibilityTarget serviceTarget,
-            @NonNull AccessibilityTarget activityTarget) {
-        final ComponentName serviceComponentName =
-                ComponentName.unflattenFromString(serviceTarget.getId());
-        final ComponentName activityComponentName =
-                ComponentName.unflattenFromString(activityTarget.getId());
-        final boolean isSamePackageName = activityComponentName.getPackageName().equals(
-                serviceComponentName.getPackageName());
-        final boolean isSameLabel = activityTarget.getLabel().equals(
-                serviceTarget.getLabel());
-
-        return isSamePackageName && isSameLabel;
-    }
-
     private static List<AccessibilityTarget> getAccessibilityServiceTargets(Context context,
             @UserShortcutType int shortcutType) {
         final AccessibilityManager am = (AccessibilityManager) context.getSystemService(
diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
index 48f86ff..2e0ff3d 100644
--- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
+++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java
@@ -16,18 +16,32 @@
 
 package com.android.internal.accessibility.util;
 
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType.INVISIBLE_TOGGLE;
 import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR;
 import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.UserIdInt;
 import android.content.ComponentName;
 import android.content.Context;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
@@ -45,6 +59,7 @@
 
     private static final TextUtils.SimpleStringSplitter sStringColonSplitter =
             new TextUtils.SimpleStringSplitter(SERVICES_SEPARATOR);
+    private static final String TAG = "AccessibilityShortcutUtils";
 
     /**
      * Opts in component id into colon-separated {@link UserShortcutType}
@@ -162,24 +177,19 @@
      * @param type The shortcut type.
      * @return Mapping key in Settings.
      */
+    @SuppressLint("SwitchIntDef")
     public static String convertToKey(@UserShortcutType int type) {
-        switch (type) {
-            case UserShortcutType.SOFTWARE:
-                return Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
-            case UserShortcutType.GESTURE:
-                return Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
-            case UserShortcutType.HARDWARE:
-                return Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
-            case UserShortcutType.TRIPLETAP:
-                return Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
-            case UserShortcutType.TWOFINGER_DOUBLETAP:
-                return Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
-            case UserShortcutType.QUICK_SETTINGS:
-                return Settings.Secure.ACCESSIBILITY_QS_TARGETS;
-            default:
-                throw new IllegalArgumentException(
-                        "Unsupported user shortcut type: " + type);
-        }
+        return switch (type) {
+            case SOFTWARE -> Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
+            case GESTURE -> Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS;
+            case HARDWARE -> Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
+            case TRIPLETAP -> Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED;
+            case TWOFINGER_DOUBLETAP ->
+                    Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED;
+            case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS;
+            default -> throw new IllegalArgumentException(
+                    "Unsupported user shortcut type: " + type);
+        };
     }
 
     /**
@@ -191,14 +201,14 @@
     @UserShortcutType
     public static int convertToType(String key) {
         return switch (key) {
-            case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> UserShortcutType.SOFTWARE;
-            case Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS -> UserShortcutType.GESTURE;
-            case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> UserShortcutType.QUICK_SETTINGS;
-            case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> UserShortcutType.HARDWARE;
+            case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS -> SOFTWARE;
+            case Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS -> GESTURE;
+            case Settings.Secure.ACCESSIBILITY_QS_TARGETS -> QUICK_SETTINGS;
+            case Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE -> HARDWARE;
             case Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED ->
-                    UserShortcutType.TRIPLETAP;
+                    TRIPLETAP;
             case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED ->
-                    UserShortcutType.TWOFINGER_DOUBLETAP;
+                    TWOFINGER_DOUBLETAP;
             default -> throw new IllegalArgumentException(
                     "Unsupported user shortcut key: " + key);
         };
@@ -296,4 +306,42 @@
             return Collections.unmodifiableSet(targets);
         }
     }
+
+    /**
+     * Retrieves the button mode of the provided context.
+     * Returns -1 if the button mode is undefined.
+     * Valid button modes:
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR},
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU},
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}
+     */
+    public static int getButtonMode(Context context, @UserIdInt int userId) {
+        return Settings.Secure.getIntForUser(context.getContentResolver(),
+                ACCESSIBILITY_BUTTON_MODE, /* default value = */ -1, userId);
+    }
+
+    /**
+     * Sets the button mode of the provided context.
+     * Must be a valid button mode, or it will return false.
+     * Returns true if the setting was changed, false otherwise.
+     * Valid button modes:
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR},
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU},
+     * {@link Settings.Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}
+     */
+    public static boolean setButtonMode(Context context, int mode, @UserIdInt int userId) {
+        // Input validation
+        if (getButtonMode(context, userId) == mode) {
+            return false;
+        }
+        if ((mode
+                & (ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR
+                | ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
+                | ACCESSIBILITY_BUTTON_MODE_GESTURE)) != mode) {
+            Slog.w(TAG, "Tried to set button mode to unexpected value " + mode);
+            return false;
+        }
+        return Settings.Secure.putIntForUser(
+                context.getContentResolver(), ACCESSIBILITY_BUTTON_MODE, mode, userId);
+    }
 }
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index d72207d..ee5bd65 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -48,6 +48,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.graphics.Insets;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.Build;
@@ -60,6 +62,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.view.View;
+import android.view.WindowInsets;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -117,6 +120,12 @@
     }
 
     @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        setMiniresolverPadding();
+    }
+
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mInjector = createInjector();
@@ -333,8 +342,7 @@
         icon.setImageDrawable(
                 getAppIcon(target, launchIntent, targetUserId, pmForTargetUser));
 
-        View buttonContainer = findViewById(R.id.button_bar_container);
-        buttonContainer.setPadding(0, 0, 0, buttonContainer.getPaddingBottom());
+        setMiniresolverPadding();
 
         ((TextView) findViewById(R.id.open_cross_profile)).setText(
                 resolverTitle);
@@ -675,6 +683,17 @@
                 && android.multiuser.Flags.enablePrivateSpaceIntentRedirection();
     }
 
+    private void setMiniresolverPadding() {
+        Insets systemWindowInsets =
+                getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
+                        WindowInsets.Type.systemBars());
+
+        View buttonContainer = findViewById(R.id.button_bar_container);
+        buttonContainer.setPadding(0, 0, 0,
+                systemWindowInsets.bottom + getResources().getDimensionPixelOffset(
+                        R.dimen.resolver_button_bar_spacing));
+    }
+
     @VisibleForTesting
     protected Injector createInjector() {
         return new InjectorImpl();
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 27eebbe..ef4acd1 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -74,6 +75,7 @@
          * @param locale    the locale picked.
          */
         void onLocaleSelected(LocaleStore.LocaleInfo locale);
+        default void onParentLocaleSelected(LocaleStore.LocaleInfo locale) {}
     }
 
     /**
@@ -264,6 +266,11 @@
     public void onListItemClick(ListView parent, View v, int position, long id) {
         final LocaleStore.LocaleInfo locale =
                 (LocaleStore.LocaleInfo) parent.getAdapter().getItem(position);
+         if (locale == null) {
+            Log.d(TAG, "Can not get the locale.");
+            return;
+        }
+
         // Special case for resetting the app locale to equal the system locale.
         boolean isSystemLocale = locale.isSystemLocale();
         boolean isRegionLocale = locale.getParent() != null;
@@ -292,7 +299,7 @@
                         mListener, locale, mTranslatedOnly /* translate only */,
                         mOnActionExpandListener, this.mLocalePickerCollector);
             }
-
+            mListener.onParentLocaleSelected(locale);
             if (selector != null) {
                 getFragmentManager().beginTransaction()
                         .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
diff --git a/core/java/com/android/internal/app/TEST_MAPPING b/core/java/com/android/internal/app/TEST_MAPPING
index 08e1d57..b7930bc 100644
--- a/core/java/com/android/internal/app/TEST_MAPPING
+++ b/core/java/com/android/internal/app/TEST_MAPPING
@@ -5,19 +5,7 @@
       "file_patterns": ["(/|^)SuspendedAppActivity\\.java"]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-        "include-filter": "com.android.internal.app."
-        },
-        // Exclude currently failing tests from presubmit
-        {
-        "exclude-filter": "com.android.internal.app.IntentForwarderActivityTest"
-        },
-        {
-        "exclude-filter": "com.android.internal.app.WindowDecorActionBarTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_internal_app"
     }
   ]
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 91678c7..d8188e1 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -573,6 +573,12 @@
     public static final String GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL =
             "generated_preview_api_max_calls_per_interval";
 
+    /*
+     * (int) The max number of providers for which to keep generated previews.
+     */
+    public static final String GENERATED_PREVIEW_API_MAX_PROVIDERS =
+            "generated_preview_api_max_providers";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/content/om/TEST_MAPPING b/core/java/com/android/internal/content/om/TEST_MAPPING
index ab3abb1..c27c325 100644
--- a/core/java/com/android/internal/content/om/TEST_MAPPING
+++ b/core/java/com/android/internal/content/om/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.content."
-        }
-      ]
+      "name": "FrameworksCoreTests_internal_content"
     },
     {
       "name": "SelfTargetingOverlayDeviceTests"
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 9f5ed65..21fbf9d 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -134,7 +134,8 @@
      * Prints data on dumpsys.
      */
     public void dump(PrintWriter pw) {
-        pw.println("BrightnessSynchronizer");
+        pw.println("BrightnessSynchronizer:");
+        pw.println("-----------------------");
         pw.println("  mLatestIntBrightness=" + mLatestIntBrightness);
         pw.println("  mLatestFloatBrightness=" + mLatestFloatBrightness);
         pw.println("  mCurrentUpdate=" + mCurrentUpdate);
diff --git a/core/java/com/android/internal/infra/TEST_MAPPING b/core/java/com/android/internal/infra/TEST_MAPPING
index c09181f..35f0553 100644
--- a/core/java/com/android/internal/infra/TEST_MAPPING
+++ b/core/java/com/android/internal/infra/TEST_MAPPING
@@ -9,23 +9,10 @@
       ]
     },
     {
-      "name": "CtsPermissionTestCases",
-      "options": [
-          {
-            "include-filter": "android.permission.cts.PermissionControllerTest"
-          },
-          {
-            "exclude-annotation": "androidx.test.filters.FlakyTest"
-          }
-      ]
+      "name": "CtsPermissionTestCases_Platform"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.infra."
-        }
-      ]
+      "name": "FrameworksCoreTests_internal_infra"
     }
   ]
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 921363c..b009c99 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -20,6 +20,8 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -106,13 +108,10 @@
      *
      * @param vis visibility flags
      * @param backDisposition disposition flags
-     * @see android.inputmethodservice.InputMethodService#IME_ACTIVE
-     * @see android.inputmethodservice.InputMethodService#IME_VISIBLE
-     * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
-     * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
      */
     @AnyThread
-    public void setImeWindowStatusAsync(int vis, int backDisposition) {
+    public void setImeWindowStatusAsync(@ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 69d1cb3..1204ef3 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -210,8 +210,33 @@
      */
     public static final int CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE = 116;
 
+    /**
+     * Track interaction of exiting desktop mode on closing the last window.
+     *
+     * <p>Tracking starts when the last window is closed and finishes when the animation to exit
+     * desktop mode ends.
+     */
+    public static final int CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE = 117;
+
+    /**
+     * Track attempting to snap resize a desktop window via button or drag.
+     *
+     * <p>CUJ has 3 different tags:
+     * <ul>
+     *     <li>snap resizing resizable apps via maximize menu button: maximize_menu_resizable </li>
+     *     <li>snap resizing resizable via drag: drag_resizable </li>
+     *     <li>snap resizing non-resizable via drag: drag_non_resizable </li>
+     * </ul>
+     *
+     * <p>For non-resizable apps, the desktop window won't actually be resized, instead will return
+     * to its pre-dragged position. Attempting to snap resize a non-resizable app via the
+     * maximize menu will just result in no change, and a toast explaining the app can't be resized.
+     *
+     */
+    public static final int CUJ_DESKTOP_MODE_SNAP_RESIZE = 118;
+
     // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
-    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE;
+    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_SNAP_RESIZE;
 
     /** @hide */
     @IntDef({
@@ -319,7 +344,9 @@
             CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN,
             CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE,
             CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH,
-            CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+            CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE,
+            CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
+            CUJ_DESKTOP_MODE_SNAP_RESIZE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {}
@@ -438,6 +465,8 @@
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE;
+        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
+        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_SNAP_RESIZE] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_SNAP_RESIZE;
     }
 
     private Cuj() {
@@ -666,6 +695,10 @@
                 return "LAUNCHER_KEYBOARD_QUICK_SWITCH_APP_LAUNCH";
             case CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE:
                 return "DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE";
+            case CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE:
+                return "DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE";
+            case CUJ_DESKTOP_MODE_SNAP_RESIZE:
+                return "DESKTOP_MODE_SNAP_RESIZE";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 53ef49b..d474c6d 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -127,7 +127,7 @@
     private Runnable mWaitForFinishTimedOut;
 
     private static class JankInfo {
-        long frameVsyncId;
+        final long frameVsyncId;
         long totalDurationNanos;
         boolean isFirstFrame;
         boolean hwuiCallbackFired;
@@ -135,29 +135,42 @@
         @JankType int jankType;
         @RefreshRate int refreshRate;
 
-        static JankInfo createFromHwuiCallback(long frameVsyncId, long totalDurationNanos,
-                boolean isFirstFrame) {
-            return new JankInfo(frameVsyncId, true, false, JANK_NONE, UNKNOWN_REFRESH_RATE,
-                    totalDurationNanos, isFirstFrame);
+        static JankInfo createFromHwuiCallback(
+                long frameVsyncId, long totalDurationNanos, boolean isFirstFrame) {
+            return new JankInfo(frameVsyncId).update(totalDurationNanos, isFirstFrame);
         }
 
-        static JankInfo createFromSurfaceControlCallback(long frameVsyncId,
-                @JankType int jankType, @RefreshRate int refreshRate) {
-            return new JankInfo(
-                    frameVsyncId, false, true, jankType, refreshRate, 0, false /* isFirstFrame */);
+        static JankInfo createFromSurfaceControlCallback(SurfaceControl.JankData jankStat) {
+            return new JankInfo(jankStat.frameVsyncId).update(jankStat);
         }
 
-        private JankInfo(long frameVsyncId, boolean hwuiCallbackFired,
-                boolean surfaceControlCallbackFired, @JankType int jankType,
-                @RefreshRate int refreshRate,
-                long totalDurationNanos, boolean isFirstFrame) {
+        private JankInfo(long frameVsyncId) {
             this.frameVsyncId = frameVsyncId;
-            this.hwuiCallbackFired = hwuiCallbackFired;
-            this.surfaceControlCallbackFired = surfaceControlCallbackFired;
-            this.jankType = jankType;
-            this.refreshRate = refreshRate;
-            this.totalDurationNanos = totalDurationNanos;
+            this.hwuiCallbackFired = false;
+            this.surfaceControlCallbackFired = false;
+            this.jankType = JANK_NONE;
+            this.refreshRate = UNKNOWN_REFRESH_RATE;
+            this.totalDurationNanos = 0;
+            this.isFirstFrame = false;
+        }
+
+        private JankInfo update(SurfaceControl.JankData jankStat) {
+            this.surfaceControlCallbackFired = true;
+            this.jankType = jankStat.jankType;
+            this.refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
+            if (Flags.useSfFrameDuration()) {
+                this.totalDurationNanos = jankStat.actualAppFrameTimeNs;
+            }
+            return this;
+        }
+
+        private JankInfo update(long totalDurationNanos, boolean isFirstFrame) {
+            this.hwuiCallbackFired = true;
+            if (!Flags.useSfFrameDuration()) {
+                this.totalDurationNanos = totalDurationNanos;
+            }
             this.isFirstFrame = isFirstFrame;
+            return this;
         }
 
         @Override
@@ -457,16 +470,12 @@
                 if (!isInRange(jankStat.frameVsyncId)) {
                     continue;
                 }
-                int refreshRate = DisplayRefreshRate.getRefreshRate(jankStat.frameIntervalNs);
                 JankInfo info = findJankInfo(jankStat.frameVsyncId);
                 if (info != null) {
-                    info.surfaceControlCallbackFired = true;
-                    info.jankType = jankStat.jankType;
-                    info.refreshRate = refreshRate;
+                    info.update(jankStat);
                 } else {
                     mJankInfos.put((int) jankStat.frameVsyncId,
-                            JankInfo.createFromSurfaceControlCallback(
-                                    jankStat.frameVsyncId, jankStat.jankType, refreshRate));
+                            JankInfo.createFromSurfaceControlCallback(jankStat));
                 }
             }
             processJankInfos();
@@ -513,9 +522,7 @@
             }
             JankInfo info = findJankInfo(frameVsyncId);
             if (info != null) {
-                info.hwuiCallbackFired = true;
-                info.totalDurationNanos = totalDurationNanos;
-                info.isFirstFrame = isFirstFrame;
+                info.update(totalDurationNanos, isFirstFrame);
             } else {
                 mJankInfos.put((int) frameVsyncId, JankInfo.createFromHwuiCallback(
                         frameVsyncId, totalDurationNanos, isFirstFrame));
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 33610a0..c7e1fba 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -396,6 +396,9 @@
         int cujType = conf.mCujType;
         if (!shouldMonitor()) {
             return false;
+        } else if (!conf.hasValidView()) {
+            Log.w(TAG, "The view has since become invalid, aborting the CUJ.");
+            return false;
         }
 
         RunningTracker tracker = putTrackerIfNoCurrent(cujType, () ->
diff --git a/core/java/com/android/internal/jank/TEST_MAPPING b/core/java/com/android/internal/jank/TEST_MAPPING
index 4e00ff1..e7f3dc3 100644
--- a/core/java/com/android/internal/jank/TEST_MAPPING
+++ b/core/java/com/android/internal/jank/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.jank"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ],
+      "name": "FrameworksCoreTests_internal_jank",
       "file_patterns": [
         "core/java/com/android/internal/jank/.*",
         "core/tests/coretests/src/com/android/internal/jank/.*"
diff --git a/core/java/com/android/internal/jank/flags.aconfig b/core/java/com/android/internal/jank/flags.aconfig
new file mode 100644
index 0000000..b6b8bc5
--- /dev/null
+++ b/core/java/com/android/internal/jank/flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.internal.jank"
+container: "system"
+
+flag {
+  name: "use_sf_frame_duration"
+  namespace: "android_platform_window_surfaces"
+  description: "Whether to get the frame duration from SurfaceFlinger, or HWUI"
+  bug: "354763298"
+  is_fixed_read_only: true
+}
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 1c6c6a7..656d4c7 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -211,6 +211,7 @@
                         if (mInstance != 0) {
                             native_replySimple(mInstance, unique, FUSE_OK);
                         }
+                        mCallbackMap.remove(checkInode(inode));
                         mBytesMap.stopUsing(inode);
                         recycleLocked(args);
                     }
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 488e06f..aafef6c 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -100,6 +100,7 @@
          * to; or a custom power component ID (if the value
          * is &gt;= {@link BatteryConsumer#FIRST_CUSTOM_POWER_COMPONENT_ID}).
          */
+        @BatteryConsumer.PowerComponentId
         public final int powerComponentId;
         public final String name;
 
@@ -142,9 +143,10 @@
                     extras);
         }
 
-        public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
-                @Nullable SparseArray<String> stateLabels, int stateStatsArrayLength,
-                int uidStatsArrayLength, @NonNull PersistableBundle extras) {
+        public Descriptor(@BatteryConsumer.PowerComponentId int powerComponentId, String name,
+                int statsArrayLength, @Nullable SparseArray<String> stateLabels,
+                int stateStatsArrayLength, int uidStatsArrayLength,
+                @NonNull PersistableBundle extras) {
             if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
                 throw new IllegalArgumentException(
                         "statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
@@ -157,7 +159,7 @@
                 throw new IllegalArgumentException(
                         "uidStatsArrayLength is too high. Max = " + MAX_UID_STATS_ARRAY_LENGTH);
             }
-            this.powerComponentId = customPowerComponentId;
+            this.powerComponentId = powerComponentId;
             this.name = name;
             this.statsArrayLength = statsArrayLength;
             this.stateLabels = stateLabels != null ? stateLabels : new SparseArray<>();
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index ae43acf..258f402 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -6,11 +6,7 @@
         "Kernel[^/]*\\.java",
         "[^/]*Power[^/]*\\.java"
       ],
-      "name": "FrameworksCoreTests",
-      "options": [
-        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
-        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
-      ]
+      "name": "FrameworksCoreTests_battery_stats"
     },
     {
       "file_patterns": [
@@ -24,11 +20,7 @@
       "file_patterns": [
         "BinderDeathDispatcher\\.java"
       ],
-      "name": "FrameworksCoreTests",
-      "options": [
-        { "include-filter": "com.android.internal.os.BinderDeathDispatcherTest" },
-        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
-      ]
+      "name": "FrameworksCoreTests_internal_os_binder"
     },
     {
       "file_patterns": [
@@ -36,10 +28,7 @@
         "Kernel[^/]*\\.java",
         "[^/]*Power[^/]*\\.java"
       ],
-      "name": "FrameworksServicesTests",
-      "options": [
-        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
-      ]
+      "name": "FrameworksServicesTests_battery_stats"
     },
     {
       "file_patterns": [
@@ -50,25 +39,7 @@
       "name": "PowerStatsTests"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.os.KernelCpuUidFreqTimeReaderTest"
-        },
-        {
-          "include-filter": "com.android.internal.os.KernelCpuUidActiveTimeReaderTest"
-        },
-        {
-          "include-filter": "com.android.internal.os.KernelCpuUidClusterTimeReaderTest"
-        },
-        {
-          "include-filter": "com.android.internal.os.KernelSingleUidTimeReaderTest"
-        },
-        {
-          "include-filter": "com.android.internal.os.KernelCpuUidBpfMapReaderTest"
-        }
-
-      ],
+      "name": "FrameworksCoreTests_internal_os_kernel",
       "file_patterns": [
         "KernelCpuUidTimeReader\\.java",
         "KernelCpuUidBpfMapReader\\.java",
@@ -78,7 +49,7 @@
   ],
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
+      "name": "PowerStatsTests",
       "options": [
         {
           "include-filter": "com.android.server.power.stats.BstatsCpuTimesValidationTest"
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index b9cc457..2acda8a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -631,21 +631,20 @@
      */
     private static Runnable forkSystemServer(String abiList, String socketName,
             ZygoteServer zygoteServer) {
-        long capabilities = posixCapabilitiesAsBits(
-                OsConstants.CAP_IPC_LOCK,
-                OsConstants.CAP_KILL,
-                OsConstants.CAP_NET_ADMIN,
-                OsConstants.CAP_NET_BIND_SERVICE,
-                OsConstants.CAP_NET_BROADCAST,
-                OsConstants.CAP_NET_RAW,
-                OsConstants.CAP_SYS_MODULE,
-                OsConstants.CAP_SYS_NICE,
-                OsConstants.CAP_SYS_PTRACE,
-                OsConstants.CAP_SYS_TIME,
-                OsConstants.CAP_SYS_TTY_CONFIG,
-                OsConstants.CAP_WAKE_ALARM,
-                OsConstants.CAP_BLOCK_SUSPEND
-        );
+        long capabilities =
+                (1L << OsConstants.CAP_IPC_LOCK) |
+                (1L << OsConstants.CAP_KILL) |
+                (1L << OsConstants.CAP_NET_ADMIN) |
+                (1L << OsConstants.CAP_NET_BIND_SERVICE) |
+                (1L << OsConstants.CAP_NET_BROADCAST) |
+                (1L << OsConstants.CAP_NET_RAW) |
+                (1L << OsConstants.CAP_SYS_MODULE) |
+                (1L << OsConstants.CAP_SYS_NICE) |
+                (1L << OsConstants.CAP_SYS_PTRACE) |
+                (1L << OsConstants.CAP_SYS_TIME) |
+                (1L << OsConstants.CAP_SYS_TTY_CONFIG) |
+                (1L << OsConstants.CAP_WAKE_ALARM) |
+                (1L << OsConstants.CAP_BLOCK_SUSPEND);
         /* Containers run without some capabilities, so drop any caps that are not available. */
         StructCapUserHeader header = new StructCapUserHeader(
                 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
@@ -742,20 +741,6 @@
     }
 
     /**
-     * Gets the bit array representation of the provided list of POSIX capabilities.
-     */
-    private static long posixCapabilitiesAsBits(int... capabilities) {
-        long result = 0;
-        for (int capability : capabilities) {
-            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
-                throw new IllegalArgumentException(String.valueOf(capability));
-            }
-            result |= (1L << capability);
-        }
-        return result;
-    }
-
-    /**
      * This is the entry point for a Zygote process.  It creates the Zygote server, loads resources,
      * and handles other tasks related to preparing the process for forking into applications.
      *
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
index 987fd41..ec5ff4c 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
@@ -174,7 +174,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -298,9 +298,9 @@
     }
 
     @DataClass.Generated(
-            time = 1642560323360L,
+            time = 1723882842941L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedProviderImpl.java",
+            sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProviderImpl.java",
             inputSignatures = "private @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String authority\nprivate  boolean syncable\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String readPermission\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String writePermission\nprivate  boolean grantUriPermissions\nprivate  boolean forceUriPermissions\nprivate  boolean multiProcess\nprivate  int initOrder\nprivate @android.annotation.NonNull java.util.List<android.os.PatternMatcher> uriPermissionPatterns\nprivate @android.annotation.NonNull java.util.List<android.content.pm.PathPermission> pathPermissions\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.internal.pm.pkg.component.ParsedProviderImpl> CREATOR\npublic  com.android.internal.pm.pkg.component.ParsedProviderImpl setReadPermission(java.lang.String)\npublic  com.android.internal.pm.pkg.component.ParsedProviderImpl setWritePermission(java.lang.String)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addUriPermissionPattern(android.os.PatternMatcher)\npublic @android.annotation.NonNull com.android.internal.pm.pkg.component.ParsedProviderImpl addPathPermission(android.content.pm.PathPermission)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedProviderImpl extends com.android.internal.pm.pkg.component.ParsedMainComponentImpl implements [com.android.internal.pm.pkg.component.ParsedProvider, android.os.Parcelable]\[email protected](genSetters=true, genGetters=true, genParcelable=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 4828393..58818f3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -63,6 +63,9 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.graphics.drawable.LayerDrawable;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Pair;
@@ -114,6 +117,7 @@
 import com.android.window.flags.Flags;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /** @hide */
@@ -1348,8 +1352,15 @@
                     mCrossWindowBlurEnabled = enabled;
                     updateBackgroundBlurRadius();
                 };
+                // The executor to receive callback {@link mCrossWindowBlurEnabledListener}. It
+                // should be the executor for this {@link DecorView}'s ui thread (not necessarily
+                // the main thread).
+                final Executor executor = Looper.myLooper() == Looper.getMainLooper()
+                        ? getContext().getMainExecutor()
+                        : new HandlerExecutor(new Handler(Looper.myLooper()));
                 getContext().getSystemService(WindowManager.class)
-                        .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
+                        .addCrossWindowBlurEnabledListener(
+                                executor, mCrossWindowBlurEnabledListener);
                 getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
             } else {
                 updateBackgroundBlurRadius();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index ec004d0..e0529b3 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -418,6 +418,7 @@
             mElevation = preservedWindow.getElevation();
             mLoadElevation = false;
             mForceDecorInstall = true;
+            mDecorFitsSystemWindows = preservedWindow.decorFitsSystemWindows();
             setSystemBarAppearance(preservedWindow.getSystemBarAppearance());
             // If we're preserving window, carry over the app token from the preserved
             // window, as we'll be skipping the addView in handleResumeActivity(), and
@@ -2512,9 +2513,16 @@
         }
 
         mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
+
+        // For floating windows that are *allowed* to fill the screen (like Wear) content
+        // should still be wrapped if they're not explicitly requested as fullscreen.
+        final boolean isFloatingAndFullscreen = mIsFloating
+                && mAllowFloatingWindowsFillScreen
+                && a.getBoolean(R.styleable.Window_windowFullscreen, false);
+
         int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                 & (~getForcedWindowFlags());
-        if (mIsFloating && !mAllowFloatingWindowsFillScreen) {
+        if (mIsFloating && !isFloatingAndFullscreen) {
             setLayout(WRAP_CONTENT, WRAP_CONTENT);
             setFlags(0, flagsToUpdate);
         } else {
@@ -3325,6 +3333,7 @@
             Bundle args = new Bundle();
             args.putInt(Intent.EXTRA_ASSIST_INPUT_DEVICE_ID, event.getDeviceId());
             args.putLong(Intent.EXTRA_TIME, event.getEventTime());
+            args.putBoolean(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, true);
             ((SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE))
                     .launchAssist(args);
             return true;
diff --git a/core/java/com/android/internal/power/TEST_MAPPING b/core/java/com/android/internal/power/TEST_MAPPING
index 1946f5c..3f184b2 100644
--- a/core/java/com/android/internal/power/TEST_MAPPING
+++ b/core/java/com/android/internal/power/TEST_MAPPING
@@ -1,11 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
-        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
-      ]
+      "name": "FrameworksCoreTests_battery_stats"
     },
     {
       "name": "PowerStatsTests"
diff --git a/core/java/com/android/internal/protolog/IProtoLogClient.aidl b/core/java/com/android/internal/protolog/IProtoLogClient.aidl
index 969ed99..64944f4 100644
--- a/core/java/com/android/internal/protolog/IProtoLogClient.aidl
+++ b/core/java/com/android/internal/protolog/IProtoLogClient.aidl
@@ -20,7 +20,7 @@
  * The ProtoLog client interface.
  *
  * These clients will communicate bi-directionally with the ProtoLog service
- * (@see IProtoLogService.aidl) running in the system process.
+ * (@see IProtoLogConfigurationService.aidl) running in the system process.
  *
  * {@hide}
  */
diff --git a/core/java/com/android/internal/protolog/IProtoLogConfigurationService.aidl b/core/java/com/android/internal/protolog/IProtoLogConfigurationService.aidl
new file mode 100644
index 0000000..ce94828
--- /dev/null
+++ b/core/java/com/android/internal/protolog/IProtoLogConfigurationService.aidl
@@ -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.internal.protolog;
+
+import com.android.internal.protolog.IProtoLogClient;
+
+/**
+ * The ProtoLog Service interface.
+ *
+ * This service runs in system server.
+ *
+ * All ProtoLog clients (in theory one per process) will register themselves to this service.
+ * This service will then serve as the entry point for any shell command based interactions with
+ * protolog and get/forward any required information/actions from/to all the registered ProtoLog
+ * clients.
+ *
+ * Clients will be responsible for directly sending all their trace data to Perfetto without passing
+ * through this service, except viewer config data, the mappings from messages hashes (generated by
+ * the ProtoLog Tool if and when the tool processed the source code). So, this service is
+ * responsible for dumping the viewer config data from all clients. The reason for this is because
+ * we do this on Perfetto's onFlush callback when a tracing instance is stopped. However, if a
+ * client process is frozen then this will not dump; system server, where this service runs cannot
+ * be frozen. Secondly many processes (i.e. apps) will run the same code with the same viewer config
+ * data, so this service allows us to orchestrate which config gets dumped so we don't duplicate
+ * this information in the trace and waste valuable trace space.
+ *
+ * {@hide}
+ */
+interface IProtoLogConfigurationService {
+    interface IRegisterClientArgs {
+        String[] getGroups();
+        boolean[] getGroupsDefaultLogcatStatus();
+        String getViewerConfigFile();
+    }
+
+    void registerClient(IProtoLogClient client, IRegisterClientArgs args);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/protolog/IProtoLogService.aidl b/core/java/com/android/internal/protolog/IProtoLogService.aidl
deleted file mode 100644
index cc349ea..0000000
--- a/core/java/com/android/internal/protolog/IProtoLogService.aidl
+++ /dev/null
@@ -1,51 +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.internal.protolog;
-
-import com.android.internal.protolog.IProtoLogClient;
-
-/**
- * The ProtoLog Service interface.
- *
- * This service runs in system server.
- *
- * All ProtoLog clients (in theory one per process) will register themselves to this service.
- * This service will then serve as the entry point for any shell command based interactions with
- * protolog and get/forward any required information/actions from/to all the registered ProtoLog
- * clients.
- *
- * Clients will be responsible for directly sending all their trace data to Perfetto without passing
- * through this service, except viewer config data, the mappings from messages hashes (generated by
- * the ProtoLog Tool if and when the tool processed the source code). So, this service is
- * responsible for dumping the viewer config data from all clients. The reason for this is because
- * we do this on Perfetto's onFlush callback when a tracing instance is stopped. However, if a
- * client process is frozen then this will not dump; system server, where this service runs cannot
- * be frozen. Secondly many processes (i.e. apps) will run the same code with the same viewer config
- * data, so this service allows us to orchestrate which config gets dumped so we don't duplicate
- * this information in the trace and waste valuable trace space.
- *
- * {@hide}
- */
-interface IProtoLogService {
-    interface IRegisterClientArgs {
-        String[] getGroups();
-        boolean[] getGroupsDefaultLogcatStatus();
-        String getViewerConfigFile();
-    }
-
-    void registerClient(IProtoLogClient client, IRegisterClientArgs args);
-}
\ No newline at end of file
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index 572a599..8771cde 100644
--- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -29,6 +29,7 @@
 import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
 import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.ShellCommand;
 import android.os.SystemClock;
@@ -48,6 +49,8 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
@@ -419,10 +422,23 @@
     }
 
     @Override
+    @NonNull
+    public List<IProtoLogGroup> getRegisteredGroups() {
+        return mLogGroups.values().stream().toList();
+    }
+
     public void registerGroups(IProtoLogGroup... protoLogGroups) {
         for (IProtoLogGroup group : protoLogGroups) {
             mLogGroups.put(group.name(), group);
         }
+
+        final var hasGroupsLoggingToLogcat = Arrays.stream(protoLogGroups)
+                .anyMatch(IProtoLogGroup::isLogToLogcat);
+
+        final ILogger logger = (msg) -> Slog.i(TAG, msg);
+        if (hasGroupsLoggingToLogcat) {
+            mViewerConfig.loadViewerConfig(logger, mLegacyViewerConfigFilename);
+        }
     }
 }
 
diff --git a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
index ebdad6d..34e0418 100644
--- a/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LogcatOnlyProtoLogImpl.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.protolog.ProtoLog.REQUIRE_PROTOLOGTOOL;
 
+import android.annotation.NonNull;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -26,6 +27,9 @@
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogLevel;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Class only create and used to server temporarily for when there is source code pre-processing by
  * the ProtoLog tool, when the tracing to Perfetto flag is off, and the static REQUIRE_PROTOLOGTOOL
@@ -36,6 +40,8 @@
  */
 @Deprecated
 public class LogcatOnlyProtoLogImpl implements IProtoLog {
+    private static final String LOG_TAG = LogcatOnlyProtoLogImpl.class.getName();
+
     @Override
     public void log(LogLevel logLevel, IProtoLogGroup group, long messageHash, int paramsMask,
             Object[] args) {
@@ -44,19 +50,21 @@
 
     @Override
     public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object[] args) {
-        if (REQUIRE_PROTOLOGTOOL) {
-            throw new RuntimeException(
-                    "REQUIRE_PROTOLOGTOOL not set to false before the first log call.");
+        if (REQUIRE_PROTOLOGTOOL && group.isLogToProto()) {
+            Log.w(LOG_TAG, "ProtoLog message not processed. Failed to log it to proto. "
+                    + "Logging it below to logcat instead.");
         }
 
-        String formattedString = TextUtils.formatSimple(messageString, args);
-        switch (logLevel) {
-            case VERBOSE -> Log.v(group.getTag(), formattedString);
-            case INFO -> Log.i(group.getTag(), formattedString);
-            case DEBUG -> Log.d(group.getTag(), formattedString);
-            case WARN -> Log.w(group.getTag(), formattedString);
-            case ERROR -> Log.e(group.getTag(), formattedString);
-            case WTF -> Log.wtf(group.getTag(), formattedString);
+        if (group.isLogToLogcat() || group.isLogToProto()) {
+            String formattedString = TextUtils.formatSimple(messageString, args);
+            switch (logLevel) {
+                case VERBOSE -> Log.v(group.getTag(), formattedString);
+                case INFO -> Log.i(group.getTag(), formattedString);
+                case DEBUG -> Log.d(group.getTag(), formattedString);
+                case WARN -> Log.w(group.getTag(), formattedString);
+                case ERROR -> Log.e(group.getTag(), formattedString);
+                case WTF -> Log.wtf(group.getTag(), formattedString);
+            }
         }
     }
 
@@ -81,7 +89,8 @@
     }
 
     @Override
-    public void registerGroups(IProtoLogGroup... protoLogGroups) {
-        // Does nothing
+    @NonNull
+    public List<IProtoLogGroup> getRegisteredGroups() {
+        return Collections.emptyList();
     }
 }
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index e0c90d8..1fd933f 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.protolog;
 
+import static android.content.Context.PROTOLOG_CONFIGURATION_SERVICE;
 import static android.internal.perfetto.protos.InternedDataOuterClass.InternedData.PROTOLOG_STACKTRACE;
 import static android.internal.perfetto.protos.InternedDataOuterClass.InternedData.PROTOLOG_STRING_ARGS;
 import static android.internal.perfetto.protos.ProfileCommon.InternedString.IID;
@@ -33,6 +34,7 @@
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
 import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
 import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_MESSAGE;
@@ -45,6 +47,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.text.TextUtils;
@@ -75,6 +79,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
@@ -84,6 +89,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Stream;
 
 /**
  * A service for the ProtoLog logging system.
@@ -93,54 +99,98 @@
     public static final String NULL_STRING = "null";
     private final AtomicInteger mTracingInstances = new AtomicInteger();
 
-    private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
-            this::onTracingInstanceStart,
-            this::onTracingFlush,
-            this::onTracingInstanceStop
-    );
+    @NonNull
+    private final ProtoLogDataSource mDataSource;
     @Nullable
     private final ProtoLogViewerConfigReader mViewerConfigReader;
     @Nullable
     private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+    @NonNull
     private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
+    @NonNull
     private final Runnable mCacheUpdater;
 
+    @Nullable // null when the flag android.tracing.client_side_proto_logging is not flipped
+    private final IProtoLogConfigurationService mProtoLogConfigurationService;
+
+    @NonNull
     private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length];
+    @NonNull
     private final Map<String, int[]> mLogLevelCounts = new ArrayMap<>();
+    @NonNull
     private final Map<String, Integer> mCollectStackTraceGroupCounts = new ArrayMap<>();
 
     private final Lock mBackgroundServiceLock = new ReentrantLock();
     private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();
 
-    public PerfettoProtoLogImpl(@NonNull String viewerConfigFilePath, Runnable cacheUpdater) {
-        this(() -> {
-            try {
-                return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException("Failed to load viewer config file " + viewerConfigFilePath, e);
-            }
-        }, cacheUpdater);
+    public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups) {
+        this(null, null, null, () -> {}, groups);
     }
 
-    public PerfettoProtoLogImpl() {
-        this(null, null, () -> {});
+    public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups) {
+        this(null, null, null, cacheUpdater, groups);
     }
 
     public PerfettoProtoLogImpl(
-            @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
-            Runnable cacheUpdater
-    ) {
-        this(viewerConfigInputStreamProvider,
-                new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider),
-                cacheUpdater);
+            @NonNull String viewerConfigFilePath,
+            @NonNull Runnable cacheUpdater,
+            @NonNull IProtoLogGroup[] groups) {
+        this(viewerConfigFilePath,
+                null,
+                new ProtoLogViewerConfigReader(() -> {
+                    try {
+                        return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+                    } catch (FileNotFoundException e) {
+                        throw new RuntimeException(
+                                "Failed to load viewer config file " + viewerConfigFilePath, e);
+                    }
+                }),
+                cacheUpdater, groups);
     }
 
     @VisibleForTesting
     public PerfettoProtoLogImpl(
             @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
             @Nullable ProtoLogViewerConfigReader viewerConfigReader,
-            Runnable cacheUpdater
-    ) {
+            @NonNull Runnable cacheUpdater,
+            @NonNull IProtoLogGroup[] groups,
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @NonNull ProtoLogConfigurationService configurationService) {
+        this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater,
+                groups, dataSourceBuilder, configurationService);
+    }
+
+    private PerfettoProtoLogImpl(
+            @Nullable String viewerConfigFilePath,
+            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+            @NonNull Runnable cacheUpdater,
+            @NonNull IProtoLogGroup[] groups) {
+        this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
+                cacheUpdater, groups,
+                ProtoLogDataSource::new,
+                IProtoLogConfigurationService.Stub
+                        .asInterface(ServiceManager.getService(PROTOLOG_CONFIGURATION_SERVICE))
+        );
+    }
+
+    private PerfettoProtoLogImpl(
+            @Nullable String viewerConfigFilePath,
+            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
+            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+            @NonNull Runnable cacheUpdater,
+            @NonNull IProtoLogGroup[] groups,
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @Nullable IProtoLogConfigurationService configurationService) {
+        if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
+            throw new RuntimeException("Only one of viewerConfigFilePath and "
+                    + "viewerConfigInputStreamProvider can be set");
+        }
+
+        mDataSource = dataSourceBuilder.build(
+                this::onTracingInstanceStart,
+                this::onTracingFlush,
+                this::onTracingInstanceStop);
         Producer.init(InitArguments.DEFAULTS);
         DataSourceParams params =
                 new DataSourceParams.Builder()
@@ -152,6 +202,35 @@
         this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
         this.mViewerConfigReader = viewerConfigReader;
         this.mCacheUpdater = cacheUpdater;
+
+        registerGroupsLocally(groups);
+
+        if (android.tracing.Flags.clientSideProtoLogging()) {
+            mProtoLogConfigurationService = configurationService;
+            Objects.requireNonNull(mProtoLogConfigurationService,
+                    "ServiceManager returned a null ProtoLog Configuration Service");
+
+            try {
+                var args = new ProtoLogConfigurationService.RegisterClientArgs();
+
+                if (viewerConfigFilePath != null) {
+                    args.setViewerConfigFile(viewerConfigFilePath);
+                }
+
+                final var groupArgs = Stream.of(groups)
+                        .map(group -> new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(group.name(), group.isLogToLogcat()))
+                        .toArray(
+                                ProtoLogConfigurationService.RegisterClientArgs.GroupConfig[]::new);
+                args.setGroups(groupArgs);
+
+                mProtoLogConfigurationService.registerClient(this, args);
+            } catch (RemoteException e) {
+                throw new RuntimeException("Failed to register ProtoLog client");
+            }
+        } else {
+            mProtoLogConfigurationService = null;
+        }
     }
 
     /**
@@ -248,18 +327,27 @@
     }
 
     @Override
-    public void registerGroups(IProtoLogGroup... protoLogGroups) {
+    @NonNull
+    public List<IProtoLogGroup> getRegisteredGroups() {
+        return mLogGroups.values().stream().toList();
+    }
+
+    private void registerGroupsLocally(@NonNull IProtoLogGroup[] protoLogGroups) {
+        final var groupsLoggingToLogcat = new ArrayList<String>();
         for (IProtoLogGroup protoLogGroup : protoLogGroups) {
             mLogGroups.put(protoLogGroup.name(), protoLogGroup);
+
+            if (protoLogGroup.isLogToLogcat()) {
+                groupsLoggingToLogcat.add(protoLogGroup.name());
+            }
         }
 
-        final String[] groupsLoggingToLogcat = Arrays.stream(protoLogGroups)
-                .filter(IProtoLogGroup::isLogToLogcat)
-                .map(IProtoLogGroup::name)
-                .toArray(String[]::new);
-
         if (mViewerConfigReader != null) {
-            mViewerConfigReader.loadViewerConfig(groupsLoggingToLogcat);
+            // Load in background to avoid delay in boot process.
+            // The caveat is that any log message that is also logged to logcat will not be
+            // successfully decoded until this completes.
+            mBackgroundLoggingService.execute(() -> mViewerConfigReader
+                    .loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0])));
         }
     }
 
@@ -354,7 +442,9 @@
             Log.e(LOG_TAG, "Failed to wait for tracing to finish", e);
         }
 
-        dumpViewerConfig();
+        if (!android.tracing.Flags.clientSideProtoLogging()) {
+            dumpViewerConfig();
+        }
 
         Log.d(LOG_TAG, "Finished onTracingFlush");
     }
@@ -367,15 +457,10 @@
 
         Log.d(LOG_TAG, "Dumping viewer config to trace");
 
-        ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
-
-        if (pis == null) {
-            Slog.w(LOG_TAG, "Failed to get viewer input stream.");
-            return;
-        }
-
         mDataSource.trace(ctx -> {
             try {
+                ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
+
                 final ProtoOutputStream os = ctx.newTracePacket();
 
                 os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
@@ -449,6 +534,9 @@
                 case (int) GROUP_ID:
                     os.write(GROUP_ID, pis.readInt(GROUP_ID));
                     break;
+                case (int) LOCATION:
+                    os.write(LOCATION, pis.readString(LOCATION));
+                    break;
                 default:
                     throw new RuntimeException(
                             "Unexpected field id " + pis.getFieldNumber());
@@ -663,6 +751,10 @@
             incrementalState.protologMessageInterningSet.add(messageHash);
 
             final ProtoOutputStream os = ctx.newTracePacket();
+
+            // Dependent on the ProtoLog viewer config packet that contains the group information.
+            os.write(SEQUENCE_FLAGS, SEQ_NEEDS_INCREMENTAL_STATE);
+
             final long protologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
             final long messageConfigToken = os.start(MESSAGES);
 
@@ -687,7 +779,7 @@
         return UUID.nameUUIDFromBytes(fullStringIdentifier.getBytes()).getMostSignificantBits();
     }
 
-    private static final int STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL = 12;
+    private static final int STACK_SIZE_TO_PROTO_LOG_ENTRY_CALL = 6;
 
     private String collectStackTrace() {
         StackTraceElement[] stackTrace =  Thread.currentThread().getStackTrace();
@@ -860,37 +952,47 @@
     }
 
     private static class Message {
+        @Nullable
         private final Long mMessageHash;
+        @Nullable
         private final Integer mMessageMask;
+        @Nullable
         private final String mMessageString;
 
-        private Message(Long messageHash, int messageMask) {
+        private Message(long messageHash, int messageMask) {
             this.mMessageHash = messageHash;
             this.mMessageMask = messageMask;
             this.mMessageString = null;
         }
 
-        private Message(String messageString) {
+        private Message(@NonNull String messageString) {
             this.mMessageHash = null;
             final List<Integer> argTypes = LogDataType.parseFormatString(messageString);
             this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes);
             this.mMessageString = messageString;
         }
 
-        private int getMessageMask() {
+        @Nullable
+        private Integer getMessageMask() {
             return mMessageMask;
         }
 
+        @Nullable
         private String getMessage() {
             return mMessageString;
         }
 
+        @Nullable
         private String getMessage(@NonNull ProtoLogViewerConfigReader viewerConfigReader) {
             if (mMessageString != null) {
                 return mMessageString;
             }
 
-            return viewerConfigReader.getViewerString(mMessageHash);
+            if (mMessageHash != null) {
+                return viewerConfigReader.getViewerString(mMessageHash);
+            }
+
+            throw new RuntimeException("Both mMessageString and mMessageHash should never be null");
         }
     }
 }
diff --git a/core/java/com/android/internal/protolog/ProtoLog.java b/core/java/com/android/internal/protolog/ProtoLog.java
index 87678e5..bf77db7 100644
--- a/core/java/com/android/internal/protolog/ProtoLog.java
+++ b/core/java/com/android/internal/protolog/ProtoLog.java
@@ -20,6 +20,9 @@
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogLevel;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 /**
  * ProtoLog API - exposes static logging methods. Usage of this API is similar
  * to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
@@ -49,6 +52,37 @@
 
     private static IProtoLog sProtoLogInstance;
 
+    private static final Object sInitLock = new Object();
+
+    /**
+     * Initialize ProtoLog in this process.
+     * <p>
+     * This method MUST be called before any protologging is performed in this process.
+     * Ensure that all groups that will be used for protologging are registered.
+     *
+     * @param groups The ProtoLog groups that will be used in the process.
+     */
+    public static void init(IProtoLogGroup... groups) {
+        // These tracing instances are only used when we cannot or do not preprocess the source
+        // files to extract out the log strings. Otherwise, the trace calls are replaced with calls
+        // directly to the generated tracing implementations.
+        if (android.tracing.Flags.perfettoProtologTracing()) {
+            synchronized (sInitLock) {
+                if (sProtoLogInstance != null) {
+                    // The ProtoLog instance has already been initialized in this process
+                    final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups();
+                    final var allGroups = new ArrayList<>(alreadyRegisteredGroups);
+                    allGroups.addAll(Arrays.stream(groups).toList());
+                    groups = allGroups.toArray(new IProtoLogGroup[0]);
+                }
+
+                sProtoLogInstance = new PerfettoProtoLogImpl(groups);
+            }
+        } else {
+            sProtoLogInstance = new LogcatOnlyProtoLogImpl();
+        }
+    }
+
     /**
      * DEBUG level log.
      *
@@ -150,14 +184,6 @@
         return sProtoLogInstance;
     }
 
-    /**
-     * Registers available protolog groups. A group must be registered before it can be used.
-     * @param protoLogGroups The groups to register for use in protolog.
-     */
-    public static void registerGroups(IProtoLogGroup... protoLogGroups) {
-        sProtoLogInstance.registerGroups(protoLogGroups);
-    }
-
     private static void logStringMessage(LogLevel logLevel, IProtoLogGroup group,
             String stringMessage, Object... args) {
         if (sProtoLogInstance == null) {
@@ -169,14 +195,4 @@
             sProtoLogInstance.log(logLevel, group, stringMessage, args);
         }
     }
-
-    static {
-        if (android.tracing.Flags.perfettoProtologTracing()) {
-            sProtoLogInstance = new PerfettoProtoLogImpl();
-        } else {
-            // The first call to ProtoLog is likely to flip REQUIRE_PROTOLOGTOOL, which is when this
-            // static block will be executed before REQUIRE_PROTOLOGTOOL is actually set.
-            sProtoLogInstance = new LogcatOnlyProtoLogImpl();
-        }
-    }
 }
diff --git a/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java b/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java
index 3dab2e3..82d8d34 100644
--- a/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java
+++ b/core/java/com/android/internal/protolog/ProtoLogCommandHandler.java
@@ -29,18 +29,20 @@
 
 public class ProtoLogCommandHandler extends ShellCommand {
     @NonNull
-    private final ProtoLogService mProtoLogService;
+    private final ProtoLogConfigurationService mProtoLogConfigurationService;
     @Nullable
     private final PrintWriter mPrintWriter;
 
-    public ProtoLogCommandHandler(@NonNull ProtoLogService protoLogService) {
-        this(protoLogService, null);
+    public ProtoLogCommandHandler(
+            @NonNull ProtoLogConfigurationService protoLogConfigurationService) {
+        this(protoLogConfigurationService, null);
     }
 
     @VisibleForTesting
     public ProtoLogCommandHandler(
-            @NonNull ProtoLogService protoLogService, @Nullable PrintWriter printWriter) {
-        this.mProtoLogService = protoLogService;
+            @NonNull ProtoLogConfigurationService protoLogConfigurationService,
+            @Nullable PrintWriter printWriter) {
+        this.mProtoLogConfigurationService = protoLogConfigurationService;
         this.mPrintWriter = printWriter;
     }
 
@@ -94,7 +96,7 @@
 
         switch (cmd) {
             case "list": {
-                final String[] availableGroups = mProtoLogService.getGroups();
+                final String[] availableGroups = mProtoLogConfigurationService.getGroups();
                 if (availableGroups.length == 0) {
                     pw.println("No ProtoLog groups registered with ProtoLog service.");
                     return 0;
@@ -117,12 +119,13 @@
 
                 pw.println("ProtoLog group " + group + "'s status:");
 
-                if (!Set.of(mProtoLogService.getGroups()).contains(group)) {
+                if (!Set.of(mProtoLogConfigurationService.getGroups()).contains(group)) {
                     pw.println("UNREGISTERED");
                     return 0;
                 }
 
-                pw.println("LOG_TO_LOGCAT = " + mProtoLogService.isLoggingToLogcat(group));
+                pw.println("LOG_TO_LOGCAT = "
+                        + mProtoLogConfigurationService.isLoggingToLogcat(group));
                 return 0;
             }
             default: {
@@ -142,11 +145,11 @@
 
         switch (cmd) {
             case "enable" -> {
-                mProtoLogService.enableProtoLogToLogcat(processGroups());
+                mProtoLogConfigurationService.enableProtoLogToLogcat(processGroups());
                 return 0;
             }
             case "disable" -> {
-                mProtoLogService.disableProtoLogToLogcat(processGroups());
+                mProtoLogConfigurationService.disableProtoLogToLogcat(processGroups());
                 return 0;
             }
             default -> {
@@ -159,7 +162,7 @@
     @NonNull
     private String[] processGroups() {
         if (getRemainingArgsCount() == 0) {
-            return mProtoLogService.getGroups();
+            return mProtoLogConfigurationService.getGroups();
         }
 
         final List<String> groups = new ArrayList<>();
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
new file mode 100644
index 0000000..d54a80b
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -0,0 +1,470 @@
+/*
+ * 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.protolog;
+
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.SystemClock;
+import android.tracing.perfetto.DataSourceParams;
+import android.tracing.perfetto.InitArguments;
+import android.tracing.perfetto.Producer;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * The ProtoLog service is responsible for orchestrating centralized actions of the protolog tracing
+ * system. Currently this service has the following roles:
+ * - Handle shell commands to toggle logging ProtoLog messages for specified groups to logcat.
+ * - Handle viewer config dumping (the mapping from message hash to message string) for all protolog
+ *   clients. This is for two reasons: firstly, because client processes might be frozen so might
+ *   not response to the request to dump their viewer config when the trace is stopped; secondly,
+ *   multiple processes might be running the same code with the same viewer config, this centralized
+ *   service ensures we don't dump the same viewer config multiple times across processes.
+ * <p>
+ * {@link com.android.internal.protolog.IProtoLogClient ProtoLog clients} register themselves to
+ * this service on initialization.
+ * <p>
+ * This service is intended to run on the system server, such that it never gets frozen.
+ */
+@SystemService(Context.PROTOLOG_CONFIGURATION_SERVICE)
+public final class ProtoLogConfigurationService extends IProtoLogConfigurationService.Stub {
+    private static final String LOG_TAG = "ProtoLogConfigurationService";
+
+    private final ProtoLogDataSource mDataSource;
+
+    /**
+     * Keeps track of how many of each viewer config file is currently registered.
+     * Use to keep track of which viewer config files are actively being used in tracing and might
+     * need to be dumped on flush.
+     */
+    private final Map<String, Integer> mConfigFileCounts = new HashMap<>();
+    /**
+     * Keeps track of the viewer config file of each client if available.
+     */
+    private final Map<IProtoLogClient, String> mClientConfigFiles = new HashMap<>();
+
+    /**
+     * Keeps track of all the protolog groups that have been registered by clients and are still
+     * being actively traced.
+     */
+    private final Set<String> mRegisteredGroups = new HashSet<>();
+    /**
+     * Keeps track of all the clients that are actively tracing a given protolog group.
+     */
+    private final Map<String, Set<IProtoLogClient>> mGroupToClients = new HashMap<>();
+
+    /**
+     * Keeps track of whether or not a given group should be logged to logcat.
+     * True when logging to logcat, false otherwise.
+     */
+    private final Map<String, Boolean> mLogGroupToLogcatStatus = new TreeMap<>();
+
+    /**
+     * Keeps track of all the tracing instance ids that are actively running for ProtoLog.
+     */
+    private final Set<Integer> mRunningInstances = new HashSet<>();
+
+    private final ViewerConfigFileTracer mViewerConfigFileTracer;
+
+    public ProtoLogConfigurationService() {
+        this(ProtoLogDataSource::new, ProtoLogConfigurationService::dumpTransitionTraceConfig);
+    }
+
+    @VisibleForTesting
+    public ProtoLogConfigurationService(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
+        this(dataSourceBuilder, ProtoLogConfigurationService::dumpTransitionTraceConfig);
+    }
+
+    @VisibleForTesting
+    public ProtoLogConfigurationService(@NonNull ViewerConfigFileTracer tracer) {
+        this(ProtoLogDataSource::new, tracer);
+    }
+
+    private ProtoLogConfigurationService(
+            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+            @NonNull ViewerConfigFileTracer tracer) {
+        mDataSource = dataSourceBuilder.build(
+            this::onTracingInstanceStart,
+            this::onTracingInstanceFlush,
+            this::onTracingInstanceStop
+        );
+
+        // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
+        // receive the lifecycle callbacks of the datasource and write the viewer configs if and
+        // when required to the datasource.
+        Producer.init(InitArguments.DEFAULTS);
+        final var params = new DataSourceParams.Builder()
+                .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
+                .build();
+        mDataSource.register(params);
+
+        mViewerConfigFileTracer = tracer;
+    }
+
+    public static class RegisterClientArgs extends IRegisterClientArgs.Stub {
+        /**
+         * The viewer config file to be registered for this client ProtoLog process.
+         */
+        @Nullable
+        private String mViewerConfigFile = null;
+        /**
+         * The list of all groups that this client protolog process supports and might trace.
+         */
+        @NonNull
+        private String[] mGroups = new String[0];
+        /**
+         * The default logcat status of the ProtoLog client. True is logging to logcat, false
+         * otherwise. The indices should match the indices in {@link mGroups}.
+         */
+        @NonNull
+        private boolean[] mLogcatStatus = new boolean[0];
+
+        public record GroupConfig(@NonNull String group, boolean logToLogcat) {}
+
+        /**
+         * Specify groups to register with this client that will be used for protologging in this
+         * process.
+         * @param groups to register with this client.
+         * @return self
+         */
+        public RegisterClientArgs setGroups(GroupConfig... groups) {
+            mGroups = new String[groups.length];
+            mLogcatStatus = new boolean[groups.length];
+
+            for (int i = 0; i < groups.length; i++) {
+                mGroups[i] = groups[i].group;
+                mLogcatStatus[i] = groups[i].logToLogcat;
+            }
+
+            return this;
+        }
+
+        /**
+         * Set the viewer config file that the logs in this process are using.
+         * @param viewerConfigFile The file path of the viewer config.
+         * @return self
+         */
+        public RegisterClientArgs setViewerConfigFile(@NonNull String viewerConfigFile) {
+            mViewerConfigFile = viewerConfigFile;
+
+            return this;
+        }
+
+        @Override
+        @NonNull
+        public String[] getGroups() {
+            return mGroups;
+        }
+
+        @Override
+        @NonNull
+        public boolean[] getGroupsDefaultLogcatStatus() {
+            return mLogcatStatus;
+        }
+
+        @Nullable
+        @Override
+        public String getViewerConfigFile() {
+            return mViewerConfigFile;
+        }
+    }
+
+    @FunctionalInterface
+    public interface ViewerConfigFileTracer {
+        /**
+         * Write the viewer config data to the trace buffer.
+         *
+         * @param dataSource The target datasource to write the viewer config to.
+         * @param viewerConfigFilePath The path of the viewer config file which contains the data we
+         *                             want to write to the trace buffer.
+         * @throws FileNotFoundException if the viewerConfigFilePath is invalid.
+         */
+        void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath);
+    }
+
+    @Override
+    public void registerClient(@NonNull IProtoLogClient client, @NonNull IRegisterClientArgs args)
+            throws RemoteException {
+        client.asBinder().linkToDeath(() -> onClientBinderDeath(client), /* flags */ 0);
+
+        final String viewerConfigFile = args.getViewerConfigFile();
+        if (viewerConfigFile != null) {
+            registerViewerConfigFile(client, viewerConfigFile);
+        }
+
+        registerGroups(client, args.getGroups(), args.getGroupsDefaultLogcatStatus());
+    }
+
+    @Override
+    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        new ProtoLogCommandHandler(this)
+                .exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
+    /**
+     * Get the list of groups clients have registered to the protolog service.
+     * @return The list of ProtoLog groups registered with this service.
+     */
+    @NonNull
+    public String[] getGroups() {
+        return mRegisteredGroups.toArray(new String[0]);
+    }
+
+    /**
+     * Enable logging target groups to logcat.
+     * @param groups we want to enable logging them to logcat for.
+     */
+    public void enableProtoLogToLogcat(String... groups) {
+        toggleProtoLogToLogcat(true, groups);
+    }
+
+    /**
+     * Disable logging target groups to logcat.
+     * @param groups we want to disable from being logged to logcat.
+     */
+    public void disableProtoLogToLogcat(String... groups) {
+        toggleProtoLogToLogcat(false, groups);
+    }
+
+    /**
+     * Check if a group is logging to logcat
+     * @param group The group we want to check for
+     * @return True iff we are logging this group to logcat.
+     */
+    public boolean isLoggingToLogcat(@NonNull String group) {
+        final Boolean isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
+
+        if (isLoggingToLogcat == null) {
+            throw new RuntimeException(
+                    "Trying to get logcat logging status of non-registered group " + group);
+        }
+
+        return isLoggingToLogcat;
+    }
+
+    private void registerViewerConfigFile(
+            @NonNull IProtoLogClient client, @NonNull String viewerConfigFile) {
+        final var count = mConfigFileCounts.getOrDefault(viewerConfigFile, 0);
+        mConfigFileCounts.put(viewerConfigFile, count + 1);
+        mClientConfigFiles.put(client, viewerConfigFile);
+    }
+
+    private void registerGroups(@NonNull IProtoLogClient client, @NonNull String[] groups,
+            @NonNull boolean[] logcatStatuses) throws RemoteException {
+        if (groups.length != logcatStatuses.length) {
+            throw new RuntimeException(
+                    "Expected groups and logcatStatuses to have the same length, "
+                        + "but groups has length " + groups.length
+                        + " and logcatStatuses has length " + logcatStatuses.length);
+        }
+
+        for (int i = 0; i < groups.length; i++) {
+            String group = groups[i];
+            boolean logcatStatus = logcatStatuses[i];
+
+            mRegisteredGroups.add(group);
+
+            mGroupToClients.putIfAbsent(group, new HashSet<>());
+            mGroupToClients.get(group).add(client);
+
+            if (!mLogGroupToLogcatStatus.containsKey(group)) {
+                mLogGroupToLogcatStatus.put(group, logcatStatus);
+            }
+
+            boolean requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);
+            if (requestedLogToLogcat != logcatStatus) {
+                client.toggleLogcat(requestedLogToLogcat, new String[] { group });
+            }
+        }
+    }
+
+    private void toggleProtoLogToLogcat(boolean enabled, @NonNull String[] groups) {
+        final var clientToGroups = new HashMap<IProtoLogClient, Set<String>>();
+
+        for (String group : groups) {
+            final var clients = mGroupToClients.get(group);
+
+            if (clients == null) {
+                // No clients associated to this group
+                Log.w(LOG_TAG, "Attempting to toggle log to logcat for group " + group
+                        + " with no registered clients.");
+                continue;
+            }
+
+            for (IProtoLogClient client : clients) {
+                clientToGroups.putIfAbsent(client, new HashSet<>());
+                clientToGroups.get(client).add(group);
+            }
+        }
+
+        for (IProtoLogClient client : clientToGroups.keySet()) {
+            try {
+                client.toggleLogcat(enabled, clientToGroups.get(client).toArray(new String[0]));
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "Failed to toggle logcat status for groups on client", e);
+            }
+        }
+
+        for (String group : groups) {
+            mLogGroupToLogcatStatus.put(group, enabled);
+        }
+    }
+
+    private void onTracingInstanceStart(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+        mRunningInstances.add(instanceIdx);
+    }
+
+    private void onTracingInstanceFlush() {
+        for (String fileName : mConfigFileCounts.keySet()) {
+            mViewerConfigFileTracer.trace(mDataSource, fileName);
+        }
+    }
+
+    private void onTracingInstanceStop(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
+        mRunningInstances.remove(instanceIdx);
+    }
+
+    private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
+            @NonNull String viewerConfigFilePath) {
+        dataSource.trace(ctx -> {
+            final ProtoInputStream pis;
+            try {
+                pis = new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(
+                        "Failed to load viewer config file " + viewerConfigFilePath, e);
+            }
+
+            try {
+                final ProtoOutputStream os = ctx.newTracePacket();
+
+                os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+
+                final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
+                while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                    switch (pis.getFieldNumber()) {
+                        case (int) MESSAGES -> writeViewerConfigMessage(pis, os);
+                        case (int) GROUPS -> writeViewerConfigGroup(pis, os);
+                    }
+                }
+
+                os.end(outProtologViewerConfigToken);
+            } catch (IOException e) {
+                Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
+            }
+        });
+    }
+
+    private void onClientBinderDeath(@NonNull IProtoLogClient client) {
+        // Dump the tracing config now if no other client is going to dump the same config file.
+        String configFile = mClientConfigFiles.get(client);
+        if (configFile != null) {
+            final var newCount = mConfigFileCounts.get(configFile) - 1;
+            mConfigFileCounts.put(configFile, newCount);
+            boolean lastProcessWithViewerConfig = newCount == 0;
+            if (lastProcessWithViewerConfig) {
+                mViewerConfigFileTracer.trace(mDataSource, configFile);
+            }
+        }
+    }
+
+    private static void writeViewerConfigGroup(
+            @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+        final long inGroupToken = pis.start(GROUPS);
+        final long outGroupToken = os.start(GROUPS);
+
+        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (pis.getFieldNumber()) {
+                case (int) ID -> {
+                    int id = pis.readInt(ID);
+                    os.write(ID, id);
+                }
+                case (int) NAME -> {
+                    String name = pis.readString(NAME);
+                    os.write(NAME, name);
+                }
+                case (int) TAG -> {
+                    String tag = pis.readString(TAG);
+                    os.write(TAG, tag);
+                }
+                default ->
+                    throw new RuntimeException(
+                            "Unexpected field id " + pis.getFieldNumber());
+            }
+        }
+
+        pis.end(inGroupToken);
+        os.end(outGroupToken);
+    }
+
+    private static void writeViewerConfigMessage(
+            @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+        final long inMessageToken = pis.start(MESSAGES);
+        final long outMessagesToken = os.start(MESSAGES);
+
+        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (pis.getFieldNumber()) {
+                case (int) MESSAGE_ID -> os.write(MESSAGE_ID,
+                        pis.readLong(MESSAGE_ID));
+                case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
+                case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
+                case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
+                case (int) LOCATION -> os.write(LOCATION, pis.readString(LOCATION));
+                default ->
+                    throw new RuntimeException(
+                            "Unexpected field id " + pis.getFieldNumber());
+            }
+        }
+
+        pis.end(inMessageToken);
+        os.end(outMessagesToken);
+    }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index 6dc6585..1b2f5f7 100644
--- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -17,6 +17,7 @@
 package com.android.internal.protolog;
 
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.DEFAULT;
+import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.DEFAULT_LOG_FROM_LEVEL;
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.ENABLE_ALL;
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.GROUP_OVERRIDES;
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogConfig.TRACING_MODE;
@@ -24,6 +25,7 @@
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.GROUP_NAME;
 import static android.internal.perfetto.protos.ProtologConfig.ProtoLogGroup.LOG_FROM;
 
+import android.annotation.NonNull;
 import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
 import android.internal.perfetto.protos.ProtologCommon;
 import android.tracing.perfetto.CreateIncrementalStateArgs;
@@ -36,6 +38,7 @@
 import android.util.proto.ProtoInputStream;
 import android.util.proto.WireTypeMismatchException;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.LogLevel;
 
 import java.io.IOException;
@@ -43,26 +46,41 @@
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Consumer;
 
 public class ProtoLogDataSource extends DataSource<ProtoLogDataSource.Instance,
         ProtoLogDataSource.TlsState,
         ProtoLogDataSource.IncrementalState> {
+    private static final String DATASOURCE_NAME = "android.protolog";
 
+    @NonNull
     private final Instance.TracingInstanceStartCallback mOnStart;
+    @NonNull
     private final Runnable mOnFlush;
+    @NonNull
     private final Instance.TracingInstanceStopCallback mOnStop;
 
-    public ProtoLogDataSource(Instance.TracingInstanceStartCallback onStart, Runnable onFlush,
-            Instance.TracingInstanceStopCallback onStop) {
-        super("android.protolog");
+    public ProtoLogDataSource(
+            @NonNull Instance.TracingInstanceStartCallback onStart,
+            @NonNull Runnable onFlush,
+            @NonNull Instance.TracingInstanceStopCallback onStop) {
+        this(onStart, onFlush, onStop, DATASOURCE_NAME);
+    }
+
+    @VisibleForTesting
+    public ProtoLogDataSource(
+            @NonNull Instance.TracingInstanceStartCallback onStart,
+            @NonNull Runnable onFlush,
+            @NonNull Instance.TracingInstanceStopCallback onStop,
+            @NonNull String dataSourceName) {
+        super(dataSourceName);
         this.mOnStart = onStart;
         this.mOnFlush = onFlush;
         this.mOnStop = onStop;
     }
 
     @Override
-    public Instance createInstance(ProtoInputStream configStream, int instanceIndex) {
+    @NonNull
+    public Instance createInstance(@NonNull ProtoInputStream configStream, int instanceIndex) {
         ProtoLogConfig config = null;
 
         try {
@@ -92,7 +110,8 @@
     }
 
     @Override
-    public TlsState createTlsState(CreateTlsStateArgs<Instance> args) {
+    @NonNull
+    public TlsState createTlsState(@NonNull CreateTlsStateArgs<Instance> args) {
         try (Instance dsInstance = args.getDataSourceInstanceLocked()) {
             if (dsInstance == null) {
                 // Datasource instance has been removed
@@ -103,14 +122,17 @@
     }
 
     @Override
-    public IncrementalState createIncrementalState(CreateIncrementalStateArgs<Instance> args) {
+    @NonNull
+    public IncrementalState createIncrementalState(
+            @NonNull CreateIncrementalStateArgs<Instance> args) {
         return new IncrementalState();
     }
 
     public static class TlsState {
+        @NonNull
         private final ProtoLogConfig mConfig;
 
-        private TlsState(ProtoLogConfig config) {
+        private TlsState(@NonNull ProtoLogConfig config) {
             this.mConfig = config;
         }
 
@@ -190,73 +212,54 @@
         final Map<String, GroupConfig> groupConfigs = new HashMap<>();
 
         while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            if (configStream.getFieldNumber() == (int) TRACING_MODE) {
-                int tracingMode = configStream.readInt(TRACING_MODE);
-                switch (tracingMode) {
-                    case DEFAULT:
-                        break;
-                    case ENABLE_ALL:
-                        defaultLogFromLevel = LogLevel.DEBUG;
-                        break;
-                    default:
-                        throw new RuntimeException("Unhandled ProtoLog tracing mode type");
-                }
-            }
-            if (configStream.getFieldNumber() == (int) GROUP_OVERRIDES) {
-                final long group_overrides_token  = configStream.start(GROUP_OVERRIDES);
-
-                String tag = null;
-                LogLevel logFromLevel = defaultLogFromLevel;
-                boolean collectStackTrace = false;
-                while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-                    if (configStream.getFieldNumber() == (int) GROUP_NAME) {
-                        tag = configStream.readString(GROUP_NAME);
+            switch (configStream.getFieldNumber()) {
+                case (int) DEFAULT_LOG_FROM_LEVEL:
+                    int defaultLogFromLevelInt = configStream.readInt(DEFAULT_LOG_FROM_LEVEL);
+                    if (defaultLogFromLevelInt < defaultLogFromLevel.ordinal()) {
+                        defaultLogFromLevel =
+                                logLevelFromInt(defaultLogFromLevelInt);
                     }
-                    if (configStream.getFieldNumber() == (int) LOG_FROM) {
-                        final int logFromInt = configStream.readInt(LOG_FROM);
-                        switch (logFromInt) {
-                            case (ProtologCommon.PROTOLOG_LEVEL_DEBUG): {
-                                logFromLevel = LogLevel.DEBUG;
-                                break;
-                            }
-                            case (ProtologCommon.PROTOLOG_LEVEL_VERBOSE): {
-                                logFromLevel = LogLevel.VERBOSE;
-                                break;
-                            }
-                            case (ProtologCommon.PROTOLOG_LEVEL_INFO): {
-                                logFromLevel = LogLevel.INFO;
-                                break;
-                            }
-                            case (ProtologCommon.PROTOLOG_LEVEL_WARN): {
-                                logFromLevel = LogLevel.WARN;
-                                break;
-                            }
-                            case (ProtologCommon.PROTOLOG_LEVEL_ERROR): {
-                                logFromLevel = LogLevel.ERROR;
-                                break;
-                            }
-                            case (ProtologCommon.PROTOLOG_LEVEL_WTF): {
-                                logFromLevel = LogLevel.WTF;
-                                break;
-                            }
-                            default: {
-                                throw new RuntimeException("Unhandled log level");
-                            }
+                    break;
+                case (int) TRACING_MODE:
+                    int tracingMode = configStream.readInt(TRACING_MODE);
+                    switch (tracingMode) {
+                        case DEFAULT:
+                            break;
+                        case ENABLE_ALL:
+                            defaultLogFromLevel = LogLevel.DEBUG;
+                            break;
+                        default:
+                            throw new RuntimeException("Unhandled ProtoLog tracing mode type");
+                    }
+                    break;
+                case (int) GROUP_OVERRIDES:
+                    final long group_overrides_token  = configStream.start(GROUP_OVERRIDES);
+
+                    String tag = null;
+                    LogLevel logFromLevel = defaultLogFromLevel;
+                    boolean collectStackTrace = false;
+                    while (configStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                        if (configStream.getFieldNumber() == (int) GROUP_NAME) {
+                            tag = configStream.readString(GROUP_NAME);
+                        }
+                        if (configStream.getFieldNumber() == (int) LOG_FROM) {
+                            final int logFromInt = configStream.readInt(LOG_FROM);
+                            logFromLevel = logLevelFromInt(logFromInt);
+                        }
+                        if (configStream.getFieldNumber() == (int) COLLECT_STACKTRACE) {
+                            collectStackTrace = configStream.readBoolean(COLLECT_STACKTRACE);
                         }
                     }
-                    if (configStream.getFieldNumber() == (int) COLLECT_STACKTRACE) {
-                        collectStackTrace = configStream.readBoolean(COLLECT_STACKTRACE);
+
+                    if (tag == null) {
+                        throw new RuntimeException("Failed to decode proto config. "
+                                + "Got a group override without a group tag.");
                     }
-                }
 
-                if (tag == null) {
-                    throw new RuntimeException("Failed to decode proto config. "
-                            + "Got a group override without a group tag.");
-                }
+                    groupConfigs.put(tag, new GroupConfig(logFromLevel, collectStackTrace));
 
-                groupConfigs.put(tag, new GroupConfig(logFromLevel, collectStackTrace));
-
-                configStream.end(group_overrides_token);
+                    configStream.end(group_overrides_token);
+                    break;
             }
         }
 
@@ -265,29 +268,45 @@
         return new ProtoLogConfig(defaultLogFromLevel, groupConfigs);
     }
 
+    private LogLevel logLevelFromInt(int logFromInt) {
+        return switch (logFromInt) {
+            case (ProtologCommon.PROTOLOG_LEVEL_DEBUG) -> LogLevel.DEBUG;
+            case (ProtologCommon.PROTOLOG_LEVEL_VERBOSE) -> LogLevel.VERBOSE;
+            case (ProtologCommon.PROTOLOG_LEVEL_INFO) -> LogLevel.INFO;
+            case (ProtologCommon.PROTOLOG_LEVEL_WARN) -> LogLevel.WARN;
+            case (ProtologCommon.PROTOLOG_LEVEL_ERROR) -> LogLevel.ERROR;
+            case (ProtologCommon.PROTOLOG_LEVEL_WTF) -> LogLevel.WTF;
+            default -> throw new RuntimeException("Unhandled log level");
+        };
+    }
+
     public static class Instance extends DataSourceInstance {
 
         public interface TracingInstanceStartCallback {
-            void run(int instanceIdx, ProtoLogConfig config);
+            void run(int instanceIdx, @NonNull ProtoLogConfig config);
         }
 
         public interface TracingInstanceStopCallback {
-            void run(int instanceIdx, ProtoLogConfig config);
+            void run(int instanceIdx, @NonNull ProtoLogConfig config);
         }
 
+        @NonNull
         private final TracingInstanceStartCallback mOnStart;
+        @NonNull
         private final Runnable mOnFlush;
+        @NonNull
         private final TracingInstanceStopCallback mOnStop;
+        @NonNull
         private final ProtoLogConfig mConfig;
         private final int mInstanceIndex;
 
         public Instance(
-                DataSource<Instance, TlsState, IncrementalState> dataSource,
+                @NonNull DataSource<Instance, TlsState, IncrementalState> dataSource,
                 int instanceIdx,
-                ProtoLogConfig config,
-                TracingInstanceStartCallback onStart,
-                Runnable onFlush,
-                TracingInstanceStopCallback onStop
+                @NonNull ProtoLogConfig config,
+                @NonNull TracingInstanceStartCallback onStart,
+                @NonNull Runnable onFlush,
+                @NonNull TracingInstanceStopCallback onStop
         ) {
             super(dataSource, instanceIdx);
             this.mInstanceIndex = instanceIdx;
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
new file mode 100644
index 0000000..da78b62
--- /dev/null
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSourceBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * 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.protolog;
+
+import android.annotation.NonNull;
+
+public interface ProtoLogDataSourceBuilder {
+    /**
+     * Builder method for the DataSource the PerfettoProtoLogImpl is going to us.
+     * @param onStart The onStart callback that should be used by the created datasource.
+     * @param onFlush The onFlush callback that should be used by the created datasource.
+     * @param onStop The onStop callback that should be used by the created datasource.
+     * @return A new DataSource that uses the provided callbacks.
+     */
+    @NonNull
+    ProtoLogDataSource build(
+            @NonNull ProtoLogDataSource.Instance.TracingInstanceStartCallback onStart,
+            @NonNull Runnable onFlush,
+            @NonNull ProtoLogDataSource.Instance.TracingInstanceStopCallback onStop);
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 77ca7ce..da6d8cf 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -93,35 +93,30 @@
     }
 
     /**
-     * Registers available protolog groups. A group must be registered before it can be used.
-     * @param protoLogGroups The groups to register for use in protolog.
-     */
-    public static void registerGroups(IProtoLogGroup... protoLogGroups) {
-        getSingleInstance().registerGroups(protoLogGroups);
-    }
-
-    /**
      * Returns the single instance of the ProtoLogImpl singleton class.
      */
     public static synchronized IProtoLog getSingleInstance() {
         if (sServiceInstance == null) {
+            final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
+
             if (android.tracing.Flags.perfettoProtologTracing()) {
                 File f = new File(sViewerConfigPath);
                 if (!ProtoLog.REQUIRE_PROTOLOGTOOL && !f.exists()) {
                     // TODO(b/353530422): Remove - temporary fix to unblock b/352290057
-                    // In so tests the viewer config file might not exist in which we don't
+                    // In some tests the viewer config file might not exist in which we don't
                     // want to provide config path to the user
-                    sServiceInstance = new PerfettoProtoLogImpl(null, null, sCacheUpdater);
+                    sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
                 } else {
-                    sServiceInstance = new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater);
+                    sServiceInstance =
+                            new PerfettoProtoLogImpl(sViewerConfigPath, sCacheUpdater, groups);
                 }
             } else {
-                sServiceInstance = new LegacyProtoLogImpl(
+                var protologImpl = new LegacyProtoLogImpl(
                         sLegacyOutputFilePath, sLegacyViewerConfigPath, sCacheUpdater);
+                protologImpl.registerGroups(groups);
+                sServiceInstance = protologImpl;
             }
 
-            IProtoLogGroup[] groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
-            sServiceInstance.registerGroups(groups);
             sCacheUpdater.run();
         }
         return sServiceInstance;
diff --git a/core/java/com/android/internal/protolog/ProtoLogService.java b/core/java/com/android/internal/protolog/ProtoLogService.java
deleted file mode 100644
index 2333a06..0000000
--- a/core/java/com/android/internal/protolog/ProtoLogService.java
+++ /dev/null
@@ -1,458 +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.internal.protolog;
-
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.os.SystemClock;
-import android.tracing.perfetto.DataSourceParams;
-import android.tracing.perfetto.InitArguments;
-import android.tracing.perfetto.Producer;
-import android.util.Log;
-import android.util.proto.ProtoInputStream;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-/**
- * The ProtoLog service is responsible for orchestrating centralized actions of the protolog tracing
- * system. Currently this service has the following roles:
- * - Handle shell commands to toggle logging ProtoLog messages for specified groups to logcat.
- * - Handle viewer config dumping (the mapping from message hash to message string) for all protolog
- *   clients. This is for two reasons: firstly, because client processes might be frozen so might
- *   not response to the request to dump their viewer config when the trace is stopped; secondly,
- *   multiple processes might be running the same code with the same viewer config, this centralized
- *   service ensures we don't dump the same viewer config multiple times across processes.
- * <p>
- * {@link com.android.internal.protolog.IProtoLogClient ProtoLog clients} register themselves to
- * this service on initialization.
- * <p>
- * This service is intended to run on the system server, such that it never gets frozen.
- */
-@SystemService(Context.PROTOLOG_SERVICE)
-public final class ProtoLogService extends IProtoLogService.Stub {
-    private static final String LOG_TAG = "ProtoLogService";
-
-    private final ProtoLogDataSource mDataSource = new ProtoLogDataSource(
-            this::onTracingInstanceStart,
-            this::onTracingInstanceFlush,
-            this::onTracingInstanceStop
-    );
-
-    /**
-     * Keeps track of how many of each viewer config file is currently registered.
-     * Use to keep track of which viewer config files are actively being used in tracing and might
-     * need to be dumped on flush.
-     */
-    private final Map<String, Integer> mConfigFileCounts = new HashMap<>();
-    /**
-     * Keeps track of the viewer config file of each client if available.
-     */
-    private final Map<IProtoLogClient, String> mClientConfigFiles = new HashMap<>();
-
-    /**
-     * Keeps track of all the protolog groups that have been registered by clients and are still
-     * being actively traced.
-     */
-    private final Set<String> mRegisteredGroups = new HashSet<>();
-    /**
-     * Keeps track of all the clients that are actively tracing a given protolog group.
-     */
-    private final Map<String, Set<IProtoLogClient>> mGroupToClients = new HashMap<>();
-
-    /**
-     * Keeps track of whether or not a given group should be logged to logcat.
-     * True when logging to logcat, false otherwise.
-     */
-    private final Map<String, Boolean> mLogGroupToLogcatStatus = new TreeMap<>();
-
-    /**
-     * Keeps track of all the tracing instance ids that are actively running for ProtoLog.
-     */
-    private final Set<Integer> mRunningInstances = new HashSet<>();
-
-    private final ViewerConfigFileTracer mViewerConfigFileTracer;
-
-    public ProtoLogService() {
-        this(ProtoLogService::dumpTransitionTraceConfig);
-    }
-
-    @VisibleForTesting
-    public ProtoLogService(@NonNull ViewerConfigFileTracer tracer) {
-        // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
-        // receive the lifecycle callbacks of the datasource and write the viewer configs if and
-        // when required to the datasource.
-        Producer.init(InitArguments.DEFAULTS);
-        final var params = new DataSourceParams.Builder()
-                .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
-                .build();
-        mDataSource.register(params);
-
-        mViewerConfigFileTracer = tracer;
-    }
-
-    public static class RegisterClientArgs extends IRegisterClientArgs.Stub {
-        /**
-         * The viewer config file to be registered for this client ProtoLog process.
-         */
-        @Nullable
-        private String mViewerConfigFile = null;
-        /**
-         * The list of all groups that this client protolog process supports and might trace.
-         */
-        @NonNull
-        private String[] mGroups = new String[0];
-        /**
-         * The default logcat status of the ProtoLog client. True is logging to logcat, false
-         * otherwise. The indices should match the indices in {@link mGroups}.
-         */
-        @NonNull
-        private boolean[] mLogcatStatus = new boolean[0];
-
-        public record GroupConfig(@NonNull String group, boolean logToLogcat) {}
-
-        /**
-         * Specify groups to register with this client that will be used for protologging in this
-         * process.
-         * @param groups to register with this client.
-         * @return self
-         */
-        public RegisterClientArgs setGroups(GroupConfig... groups) {
-            mGroups = new String[groups.length];
-            mLogcatStatus = new boolean[groups.length];
-
-            for (int i = 0; i < groups.length; i++) {
-                mGroups[i] = groups[i].group;
-                mLogcatStatus[i] = groups[i].logToLogcat;
-            }
-
-            return this;
-        }
-
-        /**
-         * Set the viewer config file that the logs in this process are using.
-         * @param viewerConfigFile The file path of the viewer config.
-         * @return self
-         */
-        public RegisterClientArgs setViewerConfigFile(@NonNull String viewerConfigFile) {
-            mViewerConfigFile = viewerConfigFile;
-
-            return this;
-        }
-
-        @Override
-        @NonNull
-        public String[] getGroups() {
-            return mGroups;
-        }
-
-        @Override
-        @NonNull
-        public boolean[] getGroupsDefaultLogcatStatus() {
-            return mLogcatStatus;
-        }
-
-        @Nullable
-        @Override
-        public String getViewerConfigFile() {
-            return mViewerConfigFile;
-        }
-    }
-
-    @FunctionalInterface
-    public interface ViewerConfigFileTracer {
-        /**
-         * Write the viewer config data to the trace buffer.
-         *
-         * @param dataSource The target datasource to write the viewer config to.
-         * @param viewerConfigFilePath The path of the viewer config file which contains the data we
-         *                             want to write to the trace buffer.
-         * @throws FileNotFoundException if the viewerConfigFilePath is invalid.
-         */
-        void trace(@NonNull ProtoLogDataSource dataSource, @NonNull String viewerConfigFilePath)
-                throws FileNotFoundException;
-    }
-
-    @Override
-    public void registerClient(@NonNull IProtoLogClient client, @NonNull IRegisterClientArgs args)
-            throws RemoteException {
-        client.asBinder().linkToDeath(() -> onClientBinderDeath(client), /* flags */ 0);
-
-        final String viewerConfigFile = args.getViewerConfigFile();
-        if (viewerConfigFile != null) {
-            registerViewerConfigFile(client, viewerConfigFile);
-        }
-
-        registerGroups(client, args.getGroups(), args.getGroupsDefaultLogcatStatus());
-    }
-
-    @Override
-    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
-            @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback,
-            @NonNull ResultReceiver resultReceiver) throws RemoteException {
-        new ProtoLogCommandHandler(this)
-                .exec(this, in, out, err, args, callback, resultReceiver);
-    }
-
-    /**
-     * Get the list of groups clients have registered to the protolog service.
-     * @return The list of ProtoLog groups registered with this service.
-     */
-    @NonNull
-    public String[] getGroups() {
-        return mRegisteredGroups.toArray(new String[0]);
-    }
-
-    /**
-     * Enable logging target groups to logcat.
-     * @param groups we want to enable logging them to logcat for.
-     */
-    public void enableProtoLogToLogcat(String... groups) {
-        toggleProtoLogToLogcat(true, groups);
-    }
-
-    /**
-     * Disable logging target groups to logcat.
-     * @param groups we want to disable from being logged to logcat.
-     */
-    public void disableProtoLogToLogcat(String... groups) {
-        toggleProtoLogToLogcat(false, groups);
-    }
-
-    /**
-     * Check if a group is logging to logcat
-     * @param group The group we want to check for
-     * @return True iff we are logging this group to logcat.
-     */
-    public boolean isLoggingToLogcat(@NonNull String group) {
-        final Boolean isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
-
-        if (isLoggingToLogcat == null) {
-            throw new RuntimeException(
-                    "Trying to get logcat logging status of non-registered group " + group);
-        }
-
-        return isLoggingToLogcat;
-    }
-
-    private void registerViewerConfigFile(
-            @NonNull IProtoLogClient client, @NonNull String viewerConfigFile) {
-        final var count = mConfigFileCounts.getOrDefault(viewerConfigFile, 0);
-        mConfigFileCounts.put(viewerConfigFile, count + 1);
-        mClientConfigFiles.put(client, viewerConfigFile);
-    }
-
-    private void registerGroups(@NonNull IProtoLogClient client, @NonNull String[] groups,
-            @NonNull boolean[] logcatStatuses) throws RemoteException {
-        if (groups.length != logcatStatuses.length) {
-            throw new RuntimeException(
-                    "Expected groups and logcatStatuses to have the same length, "
-                        + "but groups has length " + groups.length
-                        + " and logcatStatuses has length " + logcatStatuses.length);
-        }
-
-        for (int i = 0; i < groups.length; i++) {
-            String group = groups[i];
-            boolean logcatStatus = logcatStatuses[i];
-
-            mRegisteredGroups.add(group);
-
-            mGroupToClients.putIfAbsent(group, new HashSet<>());
-            mGroupToClients.get(group).add(client);
-
-            if (!mLogGroupToLogcatStatus.containsKey(group)) {
-                mLogGroupToLogcatStatus.put(group, logcatStatus);
-            }
-
-            boolean requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);
-            if (requestedLogToLogcat != logcatStatus) {
-                client.toggleLogcat(requestedLogToLogcat, new String[] { group });
-            }
-        }
-    }
-
-    private void toggleProtoLogToLogcat(boolean enabled, @NonNull String[] groups) {
-        final var clientToGroups = new HashMap<IProtoLogClient, Set<String>>();
-
-        for (String group : groups) {
-            final var clients = mGroupToClients.get(group);
-
-            if (clients == null) {
-                // No clients associated to this group
-                Log.w(LOG_TAG, "Attempting to toggle log to logcat for group " + group
-                        + " with no registered clients.");
-                continue;
-            }
-
-            for (IProtoLogClient client : clients) {
-                clientToGroups.putIfAbsent(client, new HashSet<>());
-                clientToGroups.get(client).add(group);
-            }
-        }
-
-        for (IProtoLogClient client : clientToGroups.keySet()) {
-            try {
-                client.toggleLogcat(enabled, clientToGroups.get(client).toArray(new String[0]));
-            } catch (RemoteException e) {
-                throw new RuntimeException(
-                        "Failed to toggle logcat status for groups on client", e);
-            }
-        }
-
-        for (String group : groups) {
-            mLogGroupToLogcatStatus.put(group, enabled);
-        }
-    }
-
-    private void onTracingInstanceStart(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
-        mRunningInstances.add(instanceIdx);
-    }
-
-    private void onTracingInstanceFlush() {
-        for (String fileName : mConfigFileCounts.keySet()) {
-            try {
-                mViewerConfigFileTracer.trace(mDataSource, fileName);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private void onTracingInstanceStop(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
-        mRunningInstances.remove(instanceIdx);
-    }
-
-    private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
-            @NonNull String viewerConfigFilePath) throws FileNotFoundException {
-        final var pis = new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
-
-        dataSource.trace(ctx -> {
-            try {
-                final ProtoOutputStream os = ctx.newTracePacket();
-
-                os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
-                final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
-                while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-                    switch (pis.getFieldNumber()) {
-                        case (int) MESSAGES -> writeViewerConfigMessage(pis, os);
-                        case (int) GROUPS -> writeViewerConfigGroup(pis, os);
-                    }
-                }
-
-                os.end(outProtologViewerConfigToken);
-            } catch (IOException e) {
-                Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
-            }
-        });
-    }
-
-    private void onClientBinderDeath(@NonNull IProtoLogClient client) {
-        // Dump the tracing config now if no other client is going to dump the same config file.
-        String configFile = mClientConfigFiles.get(client);
-        if (configFile != null) {
-            final var newCount = mConfigFileCounts.get(configFile) - 1;
-            mConfigFileCounts.put(configFile, newCount);
-            boolean lastProcessWithViewerConfig = newCount == 0;
-            if (lastProcessWithViewerConfig) {
-                try {
-                    mViewerConfigFileTracer.trace(mDataSource, configFile);
-                } catch (FileNotFoundException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }
-    }
-
-    private static void writeViewerConfigGroup(
-            @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
-        final long inGroupToken = pis.start(GROUPS);
-        final long outGroupToken = os.start(GROUPS);
-
-        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            switch (pis.getFieldNumber()) {
-                case (int) ID -> {
-                    int id = pis.readInt(ID);
-                    os.write(ID, id);
-                }
-                case (int) NAME -> {
-                    String name = pis.readString(NAME);
-                    os.write(NAME, name);
-                }
-                case (int) TAG -> {
-                    String tag = pis.readString(TAG);
-                    os.write(TAG, tag);
-                }
-                default ->
-                    throw new RuntimeException(
-                            "Unexpected field id " + pis.getFieldNumber());
-            }
-        }
-
-        pis.end(inGroupToken);
-        os.end(outGroupToken);
-    }
-
-    private static void writeViewerConfigMessage(
-            @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
-        final long inMessageToken = pis.start(MESSAGES);
-        final long outMessagesToken = os.start(MESSAGES);
-
-        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
-            switch (pis.getFieldNumber()) {
-                case (int) MESSAGE_ID -> os.write(MESSAGE_ID,
-                        pis.readLong(MESSAGE_ID));
-                case (int) MESSAGE -> os.write(MESSAGE, pis.readString(MESSAGE));
-                case (int) LEVEL -> os.write(LEVEL, pis.readInt(LEVEL));
-                case (int) GROUP_ID -> os.write(GROUP_ID, pis.readInt(GROUP_ID));
-                default ->
-                    throw new RuntimeException(
-                            "Unexpected field id " + pis.getFieldNumber());
-            }
-        }
-
-        pis.end(inMessageToken);
-        os.end(outMessagesToken);
-    }
-}
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 38ca0d8..6c8996e 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -3,7 +3,6 @@
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
-
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
 import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
@@ -11,7 +10,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.proto.ProtoInputStream;
 
@@ -26,7 +24,9 @@
 public class ProtoLogViewerConfigReader {
     @NonNull
     private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
+    @NonNull
     private final Map<String, Set<Long>> mGroupHashes = new TreeMap<>();
+    @NonNull
     private final LongSparseArray<String> mLogMessageMap = new LongSparseArray<>();
 
     public ProtoLogViewerConfigReader(
@@ -38,18 +38,19 @@
      * Returns message format string for its hash or null if unavailable
      * or the viewer config is not loaded into memory.
      */
+    @Nullable
     public synchronized String getViewerString(long messageHash) {
         return mLogMessageMap.get(messageHash);
     }
 
-    public synchronized void loadViewerConfig(String[] groups) {
+    public synchronized void loadViewerConfig(@NonNull String[] groups) {
         loadViewerConfig(groups, (message) -> {});
     }
 
     /**
      * Loads the viewer config into memory. No-op if already loaded in memory.
      */
-    public synchronized void loadViewerConfig(String[] groups, @NonNull ILogger logger) {
+    public synchronized void loadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
         for (String group : groups) {
             if (mGroupHashes.containsKey(group)) {
                 continue;
@@ -70,14 +71,14 @@
         }
     }
 
-    public synchronized void unloadViewerConfig(String[] groups) {
+    public synchronized void unloadViewerConfig(@NonNull String[] groups) {
         unloadViewerConfig(groups, (message) -> {});
     }
 
     /**
      * Unload the viewer config from memory.
      */
-    public synchronized void unloadViewerConfig(String[] groups, @NonNull ILogger logger) {
+    public synchronized void unloadViewerConfig(@NonNull String[] groups, @NonNull ILogger logger) {
         for (String group : groups) {
             if (!mGroupHashes.containsKey(group)) {
                 continue;
@@ -91,8 +92,10 @@
         }
     }
 
-    private Map<Long, String> loadViewerConfigMappingForGroup(String group) throws IOException {
-        Long targetGroupId = loadGroupId(group);
+    @NonNull
+    private Map<Long, String> loadViewerConfigMappingForGroup(@NonNull String group)
+            throws IOException {
+        long targetGroupId = loadGroupId(group);
 
         final Map<Long, String> hashesForGroup = new TreeMap<>();
         final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
@@ -141,7 +144,7 @@
         return hashesForGroup;
     }
 
-    private Long loadGroupId(String group) throws IOException {
+    private long loadGroupId(@NonNull String group) throws IOException {
         final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
 
         while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
diff --git a/core/java/com/android/internal/protolog/TEST_MAPPING b/core/java/com/android/internal/protolog/TEST_MAPPING
new file mode 100644
index 0000000..37d57ee
--- /dev/null
+++ b/core/java/com/android/internal/protolog/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "ProtologPerfTests"
+    }
+  ]
+}
diff --git a/core/java/com/android/internal/protolog/common/IProtoLog.java b/core/java/com/android/internal/protolog/common/IProtoLog.java
index f5695ac..f06f08a 100644
--- a/core/java/com/android/internal/protolog/common/IProtoLog.java
+++ b/core/java/com/android/internal/protolog/common/IProtoLog.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.protolog.common;
 
+import java.util.List;
+
 /**
  * Interface for ProtoLog implementations.
  */
@@ -70,8 +72,7 @@
     boolean isEnabled(IProtoLogGroup group, LogLevel level);
 
     /**
-     * Registers available protolog groups. A group must be registered before it can be used.
-     * @param protoLogGroups The groups to register for use in protolog.
+     * @return an immutable list of the registered ProtoLog groups in this ProtoLog instance.
      */
-    void registerGroups(IProtoLogGroup... protoLogGroups);
+    List<IProtoLogGroup> getRegisteredGroups();
 }
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 8fe1813..319efe0 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -15,24 +15,33 @@
  */
 package com.android.internal.ravenwood;
 
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.ravenwood.annotation.RavenwoodNativeSubstitutionClass;
+import android.ravenwood.annotation.RavenwoodReplace;
 
 /**
  * Class to interact with the Ravenwood environment.
  */
[email protected]
+@RavenwoodKeepWholeClass
 @RavenwoodNativeSubstitutionClass(
         "com.android.platform.test.ravenwood.nativesubstitution.RavenwoodEnvironment_host")
 public final class RavenwoodEnvironment {
     public static final String TAG = "RavenwoodEnvironment";
 
-    private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
-    private static Workaround sWorkaround = new Workaround();
+    private static final RavenwoodEnvironment sInstance;
+    private static final Workaround sWorkaround;
 
     private RavenwoodEnvironment() {
-        if (isRunningOnRavenwood()) {
-            ensureRavenwoodInitializedInternal();
-        }
+    }
+
+    static {
+        sInstance = new RavenwoodEnvironment();
+        sWorkaround = new Workaround();
+        ensureRavenwoodInitialized();
+    }
+
+    private static RuntimeException notSupportedOnDevice() {
+        return new UnsupportedOperationException("This method can only be used on Ravenwood");
     }
 
     /**
@@ -47,16 +56,14 @@
      *
      * No-op if called on the device side.
      */
+    @RavenwoodReplace
     public static void ensureRavenwoodInitialized() {
     }
 
     private static void ensureRavenwoodInitialized$ravenwood() {
-        getInstance(); // This is enough to initialize the environment.
+        nativeEnsureRavenwoodInitialized();
     }
 
-    /** Initialize the ravenwood environment */
-    private static native void ensureRavenwoodInitializedInternal();
-
     /**
      * USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
      *
@@ -69,7 +76,7 @@
      * <p>If someone needs it without having access to the SDK, the following hack would work too.
      * <code>System.getProperty("java.class.path").contains("ravenwood")</code>
      */
-    @android.ravenwood.annotation.RavenwoodReplace
+    @RavenwoodReplace
     public boolean isRunningOnRavenwood() {
         return false;
     }
@@ -79,15 +86,47 @@
     }
 
     /**
-     * See {@link Workaround}. It's only usablke on Ravenwood.
+     * Get the object back from the address obtained from
+     * {@link dalvik.system.VMRuntime#addressOf(Object)}.
      */
-    public static Workaround workaround() {
-        if (getInstance().isRunningOnRavenwood()) {
-            return sWorkaround;
-        }
-        throw new IllegalStateException("Workaround can only be used on Ravenwood");
+    @RavenwoodReplace
+    public <T> T fromAddress(long address) {
+        throw notSupportedOnDevice();
     }
 
+    private <T> T fromAddress$ravenwood(long address) {
+        return nativeFromAddress(address);
+    }
+
+    /**
+     * See {@link Workaround}. It's only usable on Ravenwood.
+     */
+    @RavenwoodReplace
+    public static Workaround workaround() {
+        throw notSupportedOnDevice();
+    }
+
+    private static Workaround workaround$ravenwood() {
+        return sWorkaround;
+    }
+
+    /**
+     * @return the "ravenwood-runtime" directory.
+     */
+    @RavenwoodReplace
+    public String getRavenwoodRuntimePath() {
+        throw notSupportedOnDevice();
+    }
+
+    private String getRavenwoodRuntimePath$ravenwood() {
+        return nativeGetRavenwoodRuntimePath();
+    }
+
+    // Private native methods that are actually substituted on Ravenwood
+    private native <T> T nativeFromAddress(long address);
+    private native String nativeGetRavenwoodRuntimePath();
+    private static native void nativeEnsureRavenwoodInitialized();
+
     /**
      * A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
      * be empty, and all its APIs should be able to be implemented properly.
diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING
index 0af3b03..5bd9d2e 100644
--- a/core/java/com/android/internal/security/TEST_MAPPING
+++ b/core/java/com/android/internal/security/TEST_MAPPING
@@ -1,15 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.security."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        }
-      ]
+      "name": "FrameworksCoreTests_internal_security"
     },
     {
       "name": "UpdatableSystemFontTest",
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 1d43f6f..c834dde 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -209,11 +209,6 @@
     void onDisplayReady(int displayId);
 
     /**
-     * Notifies System UI whether the recents animation is running or not.
-     */
-    void onRecentsAnimationStateChanged(boolean running);
-
-    /**
      * Notifies System UI side of system bar attribute change on the specified display.
      *
      * @param displayId the ID of the display to notify.
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 7240aff..3adc6b2 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.statusbar;
 
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArrayMap;
@@ -30,7 +32,9 @@
     public final int mDisabledFlags1;                   // switch[0]
     public final int mAppearance;                       // switch[1]
     public final AppearanceRegion[] mAppearanceRegions; // switch[2]
+    @ImeWindowVisibility
     public final int mImeWindowVis;                     // switch[3]
+    @BackDispositionMode
     public final int mImeBackDisposition;               // switch[4]
     public final boolean mShowImeSwitcher;              // switch[5]
     public final int mDisabledFlags2;                   // switch[6]
@@ -42,10 +46,11 @@
     public final LetterboxDetails[] mLetterboxDetails;
 
     public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
-            int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
-            int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2,
-            boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes,
-            String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) {
+            int appearance, AppearanceRegion[] appearanceRegions,
+            @ImeWindowVisibility int imeWindowVis, @BackDispositionMode int imeBackDisposition,
+            boolean showImeSwitcher, int disabledFlags2, boolean navbarColorManagedByIme,
+            int behavior, int requestedVisibleTypes, String packageName, int transientBarTypes,
+            LetterboxDetails[] letterboxDetails) {
         mIcons = new ArrayMap<>(icons);
         mDisabledFlags1 = disabledFlags1;
         mAppearance = appearance;
diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java
index 76ce452..1938cdb 100644
--- a/core/java/com/android/internal/statusbar/StatusBarIcon.java
+++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.statusbar;
 
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -23,7 +24,18 @@
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
+/**
+ * Representation of an icon that should appear in the status bar.
+ *
+ * <p>This includes notifications, conversations, and icons displayed on the right side (e.g.
+ * Wifi, Vibration/Silence, Priority Modes, etc).
+ *
+ * <p>This class is {@link Parcelable} but the {@link #preloadedIcon} is not (and will be lost if
+ * the object is copied through parcelling). If {@link #preloadedIcon} is supplied, it must match
+ * the {@link #icon} resource/bitmap.
+ */
 public class StatusBarIcon implements Parcelable {
     public enum Type {
         // Notification: the sender avatar for important conversations
@@ -34,7 +46,22 @@
         // Notification: the small icon from the notification
         NotifSmallIcon,
         // The wi-fi, cellular or battery icon.
-        SystemIcon
+        SystemIcon,
+        // Some other icon, corresponding to a resource (possibly in a different package).
+        ResourceIcon
+    }
+
+    public enum Shape {
+        /**
+         * Icon view should use WRAP_CONTENT -- so that the horizontal space occupied depends on the
+         * icon's shape (skinny/fat icons take less/more). Most icons will want to use this option
+         * for a nicer-looking overall spacing in the status bar, as long as the icon is "known"
+         * (i.e. not coming from a 3P package).
+         */
+        WRAP_CONTENT,
+
+        /** Icon should always be displayed in a space as wide as the status bar is tall. */
+        FIXED_SPACE,
     }
 
     public UserHandle user;
@@ -45,9 +72,17 @@
     public int number;
     public CharSequence contentDescription;
     public Type type;
+    public Shape shape;
+
+    /**
+     * Optional {@link Drawable} corresponding to {@link #icon}. This field is not parcelable, so
+     * will be lost if the object is sent to a different process. If you set it, make sure to
+     * <em>also</em> set {@link #icon} pointing to the corresponding resource.
+     */
+    @Nullable public Drawable preloadedIcon;
 
     public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
-            CharSequence contentDescription, Type type) {
+            CharSequence contentDescription, Type type, Shape shape) {
         if (icon.getType() == Icon.TYPE_RESOURCE
                 && TextUtils.isEmpty(icon.getResPackage())) {
             // This is an odd situation where someone's managed to hand us an icon without a
@@ -62,6 +97,13 @@
         this.number = number;
         this.contentDescription = contentDescription;
         this.type = type;
+        this.shape = shape;
+    }
+
+    public StatusBarIcon(UserHandle user, String resPackage, Icon icon, int iconLevel, int number,
+            CharSequence contentDescription, Type type) {
+        this(user, resPackage, icon, iconLevel, number, contentDescription, type,
+                Shape.WRAP_CONTENT);
     }
 
     public StatusBarIcon(String iconPackage, UserHandle user,
@@ -86,8 +128,9 @@
     @Override
     public StatusBarIcon clone() {
         StatusBarIcon that = new StatusBarIcon(this.user, this.pkg, this.icon,
-                this.iconLevel, this.number, this.contentDescription, this.type);
+                this.iconLevel, this.number, this.contentDescription, this.type, this.shape);
         that.visible = this.visible;
+        that.preloadedIcon = this.preloadedIcon;
         return that;
     }
 
@@ -107,6 +150,7 @@
         this.number = in.readInt();
         this.contentDescription = in.readCharSequence();
         this.type = Type.valueOf(in.readString());
+        this.shape = Shape.valueOf(in.readString());
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -118,6 +162,7 @@
         out.writeInt(this.number);
         out.writeCharSequence(this.contentDescription);
         out.writeString(this.type.name());
+        out.writeString(this.shape.name());
     }
 
     public int describeContents() {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 1e2cad4..1e965c5 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -49,81 +49,41 @@
 
     private ArrayUtils() { /* cannot be instantiated */ }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static byte[] newUnpaddedByteArray(int minLen) {
         return (byte[])VMRuntime.getRuntime().newUnpaddedArray(byte.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static char[] newUnpaddedCharArray(int minLen) {
         return (char[])VMRuntime.getRuntime().newUnpaddedArray(char.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static int[] newUnpaddedIntArray(int minLen) {
         return (int[])VMRuntime.getRuntime().newUnpaddedArray(int.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static boolean[] newUnpaddedBooleanArray(int minLen) {
         return (boolean[])VMRuntime.getRuntime().newUnpaddedArray(boolean.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static long[] newUnpaddedLongArray(int minLen) {
         return (long[])VMRuntime.getRuntime().newUnpaddedArray(long.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static float[] newUnpaddedFloatArray(int minLen) {
         return (float[])VMRuntime.getRuntime().newUnpaddedArray(float.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     public static Object[] newUnpaddedObjectArray(int minLen) {
         return (Object[])VMRuntime.getRuntime().newUnpaddedArray(Object.class, minLen);
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @SuppressWarnings("unchecked")
     public static <T> T[] newUnpaddedArray(Class<T> clazz, int minLen) {
         return (T[])VMRuntime.getRuntime().newUnpaddedArray(clazz, minLen);
     }
 
-    public static byte[] newUnpaddedByteArray$ravenwood(int minLen) {
-        return new byte[minLen];
-    }
-
-    public static char[] newUnpaddedCharArray$ravenwood(int minLen) {
-        return new char[minLen];
-    }
-
-    public static int[] newUnpaddedIntArray$ravenwood(int minLen) {
-        return new int[minLen];
-    }
-
-    public static boolean[] newUnpaddedBooleanArray$ravenwood(int minLen) {
-        return new boolean[minLen];
-    }
-
-    public static long[] newUnpaddedLongArray$ravenwood(int minLen) {
-        return new long[minLen];
-    }
-
-    public static float[] newUnpaddedFloatArray$ravenwood(int minLen) {
-        return new float[minLen];
-    }
-
-    public static Object[] newUnpaddedObjectArray$ravenwood(int minLen) {
-        return new Object[minLen];
-    }
-
-    public static <T> T[] newUnpaddedArray$ravenwood(Class<T> clazz, int minLen) {
-        return (T[]) Array.newInstance(clazz, minLen);
-    }
-
     /**
      * Checks if the beginnings of two byte arrays are equal.
      *
diff --git a/core/java/com/android/internal/util/RateLimitingCache.java b/core/java/com/android/internal/util/RateLimitingCache.java
new file mode 100644
index 0000000..9916076
--- /dev/null
+++ b/core/java/com/android/internal/util/RateLimitingCache.java
@@ -0,0 +1,135 @@
+/*
+ * 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.util;
+
+import android.os.SystemClock;
+
+/**
+ * A speed/rate limiting cache that's used to cache a value to be returned as long as period hasn't
+ * elapsed and then fetches a new value after period has elapsed. Use this when AIDL calls are
+ * expensive but the value returned by those APIs don't change often enough (or the recency doesn't
+ * matter as much), to incur the cost every time. This class maintains the last fetch time and
+ * fetches a new value when period has passed. Do not use this for API calls that have side-effects.
+ * <p>
+ * By passing in an optional <code>count</code> during creation, this can be used as a rate
+ * limiter that allows up to <code>count</code> calls per period to be passed on to the query
+ * and then the cached value is returned for the remainder of the period. It uses a simple fixed
+ * window method to track rate. Use a window and count appropriate for bursts of calls and for
+ * high latency/cost of the AIDL call.
+ *
+ * @param <Value> The type of the return value
+ * @hide
+ */
[email protected]
+public class RateLimitingCache<Value> {
+
+    private volatile Value mCurrentValue;
+    private volatile long mLastTimestamp; // Can be last fetch time or window start of fetch time
+    private final long mPeriodMillis; // window size
+    private final int mLimit; // max per window
+    private int mCount = 0; // current count within window
+    private long mRandomOffset; // random offset to avoid batching of AIDL calls at window boundary
+
+    /**
+     * The interface to fetch the actual value, if the cache is null or expired.
+     * @hide
+     * @param <V> The return value type
+     */
+    public interface ValueFetcher<V> {
+        /** Called when the cache needs to be updated.
+         * @return the latest value fetched from the source
+         */
+        V fetchValue();
+    }
+
+    /**
+     * Create a speed limiting cache that returns the same value until periodMillis has passed
+     * and then fetches a new value via the {@link ValueFetcher}.
+     *
+     * @param periodMillis time to wait before fetching a new value. Use a negative period to
+     *                     indicate the value never changes and is fetched only once and
+     *                     cached. A value of 0 will mean always fetch a new value.
+     */
+    public RateLimitingCache(long periodMillis) {
+        this(periodMillis, 1);
+    }
+
+    /**
+     * Create a rate-limiting cache that allows up to <code>count</code> number of AIDL calls per
+     * period before it starts returning a cached value. The count resets when the next period
+     * begins.
+     *
+     * @param periodMillis the window of time in which <code>count</code> calls will fetch the
+     *                     newest value from the AIDL call.
+     * @param count how many times during the period it's ok to forward the request to the fetcher
+     *              in the {@link #get(ValueFetcher)} method.
+     */
+    public RateLimitingCache(long periodMillis, int count) {
+        mPeriodMillis = periodMillis;
+        mLimit = count;
+        if (mLimit > 1 && periodMillis > 1) {
+            mRandomOffset = (long) (Math.random() * (periodMillis / 2));
+        }
+    }
+
+    /**
+     * Returns the current time in <code>elapsedRealtime</code>. Can be overridden to use
+     * a different timebase that is monotonically increasing; for example, uptimeMillis()
+     * @return a monotonically increasing time in milliseconds
+     */
+    protected long getTime() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    /**
+     * Returns either the cached value, if called more frequently than the specific rate, or
+     * a new value is fetched and cached. Warning: if the caller is likely to mutate the returned
+     * object, override this method and make a clone before returning it.
+     * @return the cached or latest value
+     */
+    public Value get(ValueFetcher<Value> query) {
+        // If the value never changes
+        if (mPeriodMillis < 0 && mLastTimestamp != 0) {
+            return mCurrentValue;
+        }
+
+        synchronized (this) {
+            // Get the current time and add a random offset to avoid colliding with other
+            // caches with similar harmonic window boundaries
+            final long now = getTime() + mRandomOffset;
+            final boolean newWindow = now - mLastTimestamp >= mPeriodMillis;
+            if (newWindow || mCount < mLimit) {
+                // Fetch a new value
+                mCurrentValue = query.fetchValue();
+
+                // If rate limiting, set timestamp to start of this window
+                if (mLimit > 1) {
+                    mLastTimestamp = now - (now % mPeriodMillis);
+                } else {
+                    mLastTimestamp = now;
+                }
+
+                if (newWindow) {
+                    mCount = 1;
+                } else {
+                    mCount++;
+                }
+            }
+            return mCurrentValue;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/util/TEST_MAPPING b/core/java/com/android/internal/util/TEST_MAPPING
index 00a8118..a0221f3b 100644
--- a/core/java/com/android/internal/util/TEST_MAPPING
+++ b/core/java/com/android/internal/util/TEST_MAPPING
@@ -5,30 +5,11 @@
       "file_patterns": ["ScreenshotHelper"]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.util.XmlTest"
-        },
-        {
-          "include-filter": "android.util.BinaryXmlTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_xml",
       "file_patterns": ["Xml"]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.util.LatencyTrackerTest"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ],
+      "name": "FrameworksCoreTests_internal_util_latency_tracker",
       "file_patterns": ["LatencyTracker.java"]
     }
   ]
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b51678e..efbf887 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -76,10 +76,10 @@
 
     boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
             in ImeTracker.Token statsToken, int flags, int lastClickToolType,
-            in @nullable ResultReceiver resultReceiver, int reason);
+            in @nullable ResultReceiver resultReceiver, int reason, boolean async);
     boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
             in ImeTracker.Token statsToken, int flags,
-            in @nullable ResultReceiver resultReceiver, int reason);
+            in @nullable ResultReceiver resultReceiver, int reason, boolean async);
 
     /**
      * A test API for CTS to request hiding the current soft input window, with the request origin
@@ -120,7 +120,8 @@
             in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
             in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, int userId,
-            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+            boolean useAsyncShowHideMethod);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
index bc729f1..b6383d9 100644
--- a/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
+++ b/core/java/com/android/internal/widget/floatingtoolbar/LocalFloatingToolbarPopup.java
@@ -437,9 +437,8 @@
 
         // Initialize x ensuring that the toolbar isn't rendered behind the nav bar in
         // landscape.
-        final int x = Math.min(
-                contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
-                mViewPortOnScreen.right - mPopupWindow.getWidth());
+        final int x = Math.clamp(contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2,
+                mViewPortOnScreen.left, mViewPortOnScreen.right - mPopupWindow.getWidth());
 
         final int y;
 
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2abdd57..90cb10a 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -108,6 +108,7 @@
         "libtracing_perfetto",
         "libharfbuzz_ng",
         "liblog",
+        "libmediautils",
         "libminikin",
         "libz",
         "server_configurable_flags",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index b2bc19c..c0fe098 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -51,6 +51,10 @@
 # Sensor
 per-file android_hardware_SensorManager* = [email protected], [email protected], [email protected]
 
+# Security
+per-file android_os_SELinux.cpp = file:/core/java/android/security/OWNERS
+per-file android_security_* = file:/core/java/android/security/OWNERS
+
 per-file *Zygote* = file:/ZYGOTE_OWNERS
 per-file core_jni_helpers.* = file:/ZYGOTE_OWNERS
 per-file fd_utils.* = file:/ZYGOTE_OWNERS
@@ -67,7 +71,6 @@
 per-file android_os_storage_* = file:/core/java/android/os/storage/OWNERS
 per-file android_os_Trace* = file:/TRACE_OWNERS
 per-file android_se_* = file:/omapi/java/android/se/OWNERS
-per-file android_security_* = file:/core/java/android/security/OWNERS
 per-file android_view_* = file:/core/java/android/view/OWNERS
 per-file com_android_internal_net_* = file:/services/core/java/com/android/server/net/OWNERS
 
diff --git a/core/jni/TEST_MAPPING b/core/jni/TEST_MAPPING
index ea0b01e..fa73a4d 100644
--- a/core/jni/TEST_MAPPING
+++ b/core/jni/TEST_MAPPING
@@ -1,15 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.util.CharsetUtilsTest"
-        },
-        {
-          "include-filter": "com.android.internal.util.FastDataTest"
-        }
-      ],
+      "name": "FrameworksCoreTests_util_data_charset",
       "file_patterns": ["CharsetUtils|FastData"]
     },
     {
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 52a9578..0b801b9 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -428,7 +428,7 @@
     return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
 }
 
-static jboolean NativeIsUpToDate(jlong ptr) {
+static jboolean NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
     auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
     auto apk_assets = scoped_apk_assets->get();
     return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index 9614864..85a6bdf 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -72,14 +72,17 @@
 
 
 // This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
-// of bounds.
-static void throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
+// of bounds.  It returns true if an exception was thrown.
+static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
     if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
         int count = sqlite3_data_count(stmt(stmtPtr));
         std::string message = android::base::StringPrintf(
             "column index %d out of bounds [0,%d]", col, count - 1);
         char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
         throw_sqlite3_exception(env, SQLITE_RANGE, errmsg, message.c_str());
+        return true;
+    } else {
+        return false;
     }
 }
 
@@ -216,12 +219,16 @@
 
 
 static jint columnType(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     return sqlite3_column_type(stmt(stmtPtr), col);
 }
 
 static jstring columnName(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return nullptr;
+    }
     const jchar* name = static_cast<const jchar*>(sqlite3_column_name16(stmt(stmtPtr), col));
     if (name == nullptr) {
         throw_sqlite3_exception(env, db(stmtPtr), "error fetching columnName()");
@@ -232,14 +239,18 @@
 }
 
 static jint columnBytes(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     int r = sqlite3_column_bytes16(stmt(stmtPtr), col);
     throwIfError(env, stmtPtr);
     return r;
 }
 
 static jbyteArray columnBlob(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return nullptr;
+    }
     const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
     if (blob == nullptr) {
         if (throwIfError(env, stmtPtr)) {
@@ -262,7 +273,9 @@
 
 static int columnBuffer(JNIEnv* env, jclass, jlong stmtPtr, jint col,
         jbyteArray buffer, jint offset, jint length, jint srcOffset) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
     if (blob == nullptr) {
         throwIfError(env, stmtPtr);
@@ -281,22 +294,30 @@
 }
 
 static jdouble columnDouble(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     return sqlite3_column_double(stmt(stmtPtr), col);
 }
 
 static jint columnInt(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     return sqlite3_column_int(stmt(stmtPtr), col);
 }
 
 static jlong columnLong(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return 0;
+    }
     return sqlite3_column_int64(stmt(stmtPtr), col);
 }
 
 static jstring columnText(JNIEnv* env, jclass, jlong stmtPtr, jint col) {
-    throwIfInvalidColumn(env, stmtPtr, col);
+    if (throwIfInvalidColumn(env, stmtPtr, col)) {
+        return nullptr;
+    }
     const jchar* text = static_cast<const jchar*>(sqlite3_column_text16(stmt(stmtPtr), col));
     if (text == nullptr) {
         throwIfError(env, stmtPtr);
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 50832a5..8dd63cc 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -256,9 +256,21 @@
     }
 }
 
-static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
-        jint texName, jboolean singleBufferMode, jobject weakThiz)
-{
+static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, jint texName,
+                                jboolean singleBufferMode, jobject weakThiz) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<SurfaceTexture> surfaceTexture;
+    if (isDetached) {
+        surfaceTexture = new SurfaceTexture(GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
+    } else {
+        surfaceTexture =
+                new SurfaceTexture(texName, GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
+    }
+
+    if (singleBufferMode) {
+        surfaceTexture->setMaxBufferCount(1);
+    }
+#else
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
@@ -275,6 +287,7 @@
         surfaceTexture = new SurfaceTexture(consumer, texName,
                 GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
     }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
     if (surfaceTexture == 0) {
         jniThrowException(env, OutOfResourcesException,
@@ -287,11 +300,27 @@
             createProcessUniqueId()));
 
     // If the current context is protected, inform the producer.
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    surfaceTexture->setConsumerIsProtected(isProtectedContext());
+
+    SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
+    sp<Surface> surface = surfaceTexture->getSurface();
+    if (nullptr == surface) {
+        jniThrowException(env, IllegalStateException, "Unable to get surface from SurfaceTexture");
+        return;
+    }
+    sp<IGraphicBufferProducer> igbp = surface->getIGraphicBufferProducer();
+    if (nullptr == igbp) {
+        jniThrowException(env, IllegalStateException, "Unable to get IGBP from Surface");
+        return;
+    }
+    SurfaceTexture_setProducer(env, thiz, igbp);
+#else
     consumer->setConsumerIsProtected(isProtectedContext());
 
     SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
     SurfaceTexture_setProducer(env, thiz, producer);
-
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
         jniThrowRuntimeException(env,
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 638591f..9bccf5a 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -17,6 +17,7 @@
 
 //#define LOG_NDEBUG 0
 
+#include <atomic>
 #define LOG_TAG "AudioSystem-JNI"
 #include <android/binder_ibinder_jni.h>
 #include <android/binder_libbinder.h>
@@ -34,15 +35,16 @@
 #include <media/AudioContainers.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
+#include <mediautils/jthread.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <nativehelper/ScopedPrimitiveArray.h>
 #include <nativehelper/jni_macros.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include <sys/system_properties.h>
 #include <utils/Log.h>
 
-#include <thread>
 #include <optional>
 #include <sstream>
 #include <memory>
@@ -57,6 +59,7 @@
 #include "android_media_AudioMixerAttributes.h"
 #include "android_media_AudioProfile.h"
 #include "android_media_MicrophoneInfo.h"
+#include "android_media_JNIUtils.h"
 #include "android_util_Binder.h"
 #include "core_jni_helpers.h"
 
@@ -3375,42 +3378,48 @@
 class JavaSystemPropertyListener {
   public:
     JavaSystemPropertyListener(JNIEnv* env, jobject javaCallback, std::string sysPropName) :
-            mCallback(env->NewGlobalRef(javaCallback)),
-            mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}) {
-        mListenerThread = std::thread([this]() mutable {
-            JNIEnv* threadEnv = GetOrAttachJNIEnvironment(gVm);
-            while (!mCleanupSignal.load()) {
-                using namespace std::chrono_literals;
-                // 1s timeout so this thread can read the cleanup signal to (slowly) be able to
-                // be destroyed.
-                std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
-                if (newVal != "" && mLastVal != newVal) {
-                    threadEnv->CallVoidMethod(mCallback, gRunnableClassInfo.run);
-                    mLastVal = std::move(newVal);
+            mCallback {javaCallback, env},
+            mSysPropName(sysPropName),
+            mCachedProperty(android::base::CachedProperty{std::move(sysPropName)}),
+            mListenerThread([this](mediautils::stop_token stok) mutable {
+                while (!stok.stop_requested()) {
+                    using namespace std::chrono_literals;
+                    // 1s timeout so this thread can eventually respond to the stop token
+                    std::string newVal = mCachedProperty.WaitForChange(1000ms) ?: "";
+                    updateValue(newVal);
                 }
-            }
-            });
-    }
+            }) {}
 
-    ~JavaSystemPropertyListener() {
-        mCleanupSignal.store(true);
-        mListenerThread.join();
-        JNIEnv* env = GetOrAttachJNIEnvironment(gVm);
-        env->DeleteGlobalRef(mCallback);
+    void triggerUpdateIfChanged() {
+        // We must check the property without using the cached property due to thread safety issues
+        std::string newVal = base::GetProperty(mSysPropName, "");
+        updateValue(newVal);
     }
 
   private:
-    jobject mCallback;
+    void updateValue(std::string newVal) {
+        if (newVal == "") return;
+        std::lock_guard l{mLock};
+        if (mLastVal == newVal) return;
+        const auto threadEnv = GetOrAttachJNIEnvironment(gVm);
+        threadEnv->CallVoidMethod(mCallback.get(), gRunnableClassInfo.run);
+        mLastVal = std::move(newVal);
+    }
+
+    // Should outlive thread object
+    const GlobalRef mCallback;
+    const std::string mSysPropName;
     android::base::CachedProperty mCachedProperty;
-    std::thread mListenerThread;
-    std::atomic<bool> mCleanupSignal{false};
     std::string mLastVal = "";
+    std::mutex mLock;
+    const mediautils::jthread mListenerThread;
 };
 
+// A logical set keyed by address
 std::vector<std::unique_ptr<JavaSystemPropertyListener>> gSystemPropertyListeners;
 std::mutex gSysPropLock{};
 
-static void android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,  jobject thiz,
+static jlong android_media_AudioSystem_listenForSystemPropertyChange(JNIEnv *env,  jobject thiz,
         jstring sysProp,
         jobject javaCallback) {
     ScopedUtfChars sysPropChars{env, sysProp};
@@ -3418,6 +3427,19 @@
             std::string{sysPropChars.c_str()});
     std::unique_lock _l{gSysPropLock};
     gSystemPropertyListeners.push_back(std::move(listener));
+    return reinterpret_cast<jlong>(gSystemPropertyListeners.back().get());
+}
+
+static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env,  jobject thiz,
+        jlong nativeHandle) {
+    std::unique_lock _l{gSysPropLock};
+    const auto iter = std::find_if(gSystemPropertyListeners.begin(), gSystemPropertyListeners.end(),
+            [nativeHandle](const auto& x) { return reinterpret_cast<jlong>(x.get()) == nativeHandle; });
+    if (iter != gSystemPropertyListeners.end()) {
+        (*iter)->triggerUpdateIfChanged();
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid handle");
+    }
 }
 
 
@@ -3595,8 +3617,11 @@
          MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
          MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
          MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange",
-                                "(Ljava/lang/String;Ljava/lang/Runnable;)V",
+                                "(Ljava/lang/String;Ljava/lang/Runnable;)J",
                                 android_media_AudioSystem_listenForSystemPropertyChange),
+         MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate",
+                                "(J)V",
+                                android_media_AudioSystem_triggerSystemPropertyUpdate),
 
         };
 
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 84ca1ba..7a4670f4 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -53,7 +53,7 @@
 }
 
 struct SecurityContext_Delete {
-    void operator()(security_context_t p) const {
+    void operator()(char* p) const {
         freecon(p);
     }
 };
@@ -111,7 +111,7 @@
         return NULL;
     }
 
-    security_context_t tmp = NULL;
+    char* tmp = NULL;
     if (selabel_lookup(selabel_handle, &tmp, path_c_str, S_IFREG) != 0) {
       ALOGE("fileSelabelLookup => selabel_lookup for %s failed: %d", path_c_str, errno);
       return NULL;
@@ -138,7 +138,7 @@
         return NULL;
     }
 
-    security_context_t tmp = NULL;
+    char* tmp = NULL;
     int ret;
     if (isSocket) {
         ret = getpeercon(fd, &tmp);
@@ -184,7 +184,7 @@
  * Function: setFSCreateCon
  * Purpose: set security context used for creating a new file system object
  * Parameters:
- *       context: security_context_t representing the new context of a file system object,
+ *       context: char* representing the new context of a file system object,
  *                set to NULL to return to the default policy behavior
  * Returns: true on success, false on error
  * Exception: none
@@ -267,7 +267,7 @@
         return NULL;
     }
 
-    security_context_t tmp = NULL;
+    char* tmp = NULL;
     int ret = getfilecon(path.c_str(), &tmp);
     Unique_SecurityContext context(tmp);
 
@@ -293,7 +293,7 @@
         return NULL;
     }
 
-    security_context_t tmp = NULL;
+    char* tmp = NULL;
     int ret = getcon(&tmp);
     Unique_SecurityContext context(tmp);
 
@@ -320,7 +320,7 @@
         return NULL;
     }
 
-    security_context_t tmp = NULL;
+    char* tmp = NULL;
     int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
     Unique_SecurityContext context(tmp);
 
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3d0ab4e..7fe6731b 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -36,7 +36,6 @@
 #include "android-base/stringprintf.h"
 #include "android_content_res_ApkAssets.h"
 #include "android_runtime/AndroidRuntime.h"
-#include "android_util_Binder.h"
 #include "androidfw/Asset.h"
 #include "androidfw/AssetManager.h"
 #include "androidfw/AssetManager2.h"
@@ -104,6 +103,11 @@
   jmethodID put;
 } gArrayMapOffsets;
 
+static struct parcel_file_descriptor_offsets_t {
+  jclass mClass;
+  jmethodID mAdoptFd;
+} gParcelFileDescriptorOffsets;
+
 static jclass g_stringClass = nullptr;
 
 // ----------------------------------------------------------------------------
@@ -244,7 +248,6 @@
   return env->NewStringUTF(result.c_str());
 }
 
-#ifdef __ANDROID__ // Layoutlib does not support parcel
 static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
                                           jlongArray out_offsets) {
   off64_t start_offset, length;
@@ -269,22 +272,10 @@
 
   env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
 
-  jobject file_desc = jniCreateFileDescriptor(env, fd);
-  if (file_desc == nullptr) {
-    close(fd);
-    return nullptr;
-  }
-  return newParcelFileDescriptor(env, file_desc);
+  return env->CallStaticObjectMethod(gParcelFileDescriptorOffsets.mClass,
+                                     gParcelFileDescriptorOffsets.mAdoptFd,
+                                     fd);
 }
-#else
-static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
-                                          jlongArray out_offsets) {
-  jniThrowException(env, "java/lang/UnsupportedOperationException",
-                    "Implement me");
-  // never reached
-  return nullptr;
-}
-#endif
 
 static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
   return Asset::getGlobalCount();
@@ -1202,6 +1193,28 @@
   env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
 }
 
+// This version is compatible with standard JVMs, however slower without ART optimizations
+static void NativeApplyStyleWithArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
+                                      jint def_style_attr, jint def_style_resid,
+                                      jlong xml_parser_ptr, jintArray java_attrs,
+                                      jintArray java_values, jintArray java_indices) {
+  auto assetmanager = LockAndStartAssetManager(ptr);
+  Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
+  CHECK(theme->GetAssetManager() == &(*assetmanager));
+  (void) assetmanager;
+
+  ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
+  ScopedIntCriticalArrayRW out_values(env, java_values);
+  ScopedIntCriticalArrayRW out_indices(env, java_indices);
+  ScopedIntCriticalArrayRO attrs(env, java_attrs);
+
+  ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
+             static_cast<uint32_t>(def_style_resid),
+             reinterpret_cast<const uint32_t*>(attrs.get()), attrs.size(),
+             reinterpret_cast<uint32_t*>(out_values.get()),
+             reinterpret_cast<uint32_t*>(out_indices.get()));
+}
+
 static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
                                    jint def_style_attr, jint def_style_resid, jintArray java_values,
                                    jintArray java_attrs, jintArray out_java_values,
@@ -1581,6 +1594,7 @@
         // Style attribute related methods.
         {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
         {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
+        {"nativeApplyStyleWithArray", "(JJIIJ[I[I[I)V", (void*)NativeApplyStyleWithArray},
         {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
         {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
 
@@ -1666,6 +1680,11 @@
       GetMethodIDOrDie(env, gArrayMapOffsets.classObject, "put",
                        "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 
+  jclass pfdClass = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
+  gParcelFileDescriptorOffsets.mClass = MakeGlobalRefOrDie(env, pfdClass);
+  gParcelFileDescriptorOffsets.mAdoptFd =
+      GetStaticMethodIDOrDie(env, pfdClass, "adoptFd", "(I)Landroid/os/ParcelFileDescriptor;");
+
   return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
                               NELEM(gAssetManagerMethods));
 }
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2068bd7..921b77d 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -56,11 +56,11 @@
 //#undef ALOGV
 //#define ALOGV(...) fprintf(stderr, __VA_ARGS__)
 
-#define DEBUG_DEATH 0
-#if DEBUG_DEATH
-#define LOGDEATH ALOGD
+#define DEBUG_DEATH_FREEZE 0
+#if DEBUG_DEATH_FREEZE
+#define LOG_DEATH_FREEZE ALOGD
 #else
-#define LOGDEATH ALOGV
+#define LOG_DEATH_FREEZE ALOGV
 #endif
 
 using namespace android;
@@ -116,6 +116,7 @@
     jclass mClass;
     jmethodID mGetInstance;
     jmethodID mSendDeathNotice;
+    jmethodID mInvokeFrozenStateChangeCallback;
 
     // Object state.
     jfieldID mNativeData;  // Field holds native pointer to BinderProxyNativeData.
@@ -466,10 +467,25 @@
 public:
     sp<JavaBBinder> get(JNIEnv* env, jobject obj)
     {
-        AutoMutex _l(mLock);
-        sp<JavaBBinder> b = mBinder.promote();
-        if (b == NULL) {
-            b = new JavaBBinder(env, obj);
+        sp<JavaBBinder> b;
+        {
+            AutoMutex _l(mLock);
+            // must take lock to promote because we set the same wp<>
+            // on another thread.
+            b = mBinder.promote();
+        }
+
+        if (b) return b;
+
+        // b/360067751: constructor may trigger GC, so call outside lock
+        b = new JavaBBinder(env, obj);
+
+        {
+            AutoMutex _l(mLock);
+            // if it was constructed on another thread in the meantime,
+            // return that. 'b' will just get destructed.
+            if (sp<JavaBBinder> b2 = mBinder.promote(); b2) return b2;
+
             if (mVintf) {
                 ::android::internal::Stability::markVintf(b.get());
             }
@@ -532,23 +548,59 @@
 
 // ----------------------------------------------------------------------------
 
-// Per-IBinder death recipient bookkeeping.  This is how we reconcile local jobject
-// death recipient references passed in through JNI with the permanent corresponding
-// JavaDeathRecipient objects.
+// A JavaRecipient receives either death notifications or frozen state change
+// callbacks from natve code (IBinder) and dispatch the notifications to its
+// corresponding Java listener object.
+//
+// A RecipientList keeps tracks of all JavaRecipients for an IBinder. This way
+// we can find a JavaRecipient given a Java listener object.
+//
+// The implementation is shared between death recipients and frozen state change
+// callbacks via template. For death recipients the template is instantiated as
+// follows:
+//
+//                   IBinder::DeathRecipient
+//                            ^
+//                            |
+//                        (inherits)
+//                            |
+//            JavaRecipient<IBinder::DeathRecipient> <----> RecipientList<IBinder::DeathRecipient>
+//                            ^
+//                            |
+//                        (inherits)
+//                            |
+//                    JavaDeathRecipient
+//
+//
+// The instantiation for frozen state change callbacks are:
+//
+//             IBinder::FrozenStateChangeCallback
+//                           ^
+//                           |
+//                       (inherits)
+//                           |
+//     JavaRecipient<IBinder::FrozenStateChangeCallback>
+//                           ^                ^
+//                           |                |
+//                       (inherits)           +--> RecipientList<IBinder::FrozenStateChangeCallback>
+//                           |
+//              JavaFrozenStateChangeCallback
 
-class JavaDeathRecipient;
+template <typename T>
+class JavaRecipient;
 
-class DeathRecipientList : public RefBase {
-    List< sp<JavaDeathRecipient> > mList;
+template <typename T>
+class RecipientList : public RefBase {
+    List<sp<JavaRecipient<T> > > mList;
     Mutex mLock;
 
 public:
-    DeathRecipientList();
-    ~DeathRecipientList();
+    RecipientList();
+    ~RecipientList();
 
-    void add(const sp<JavaDeathRecipient>& recipient);
-    void remove(const sp<JavaDeathRecipient>& recipient);
-    sp<JavaDeathRecipient> find(jobject recipient);
+    void add(const sp<JavaRecipient<T> >& recipient);
+    void remove(const sp<JavaRecipient<T> >& recipient);
+    sp<JavaRecipient<T> > find(jobject recipient);
 
     Mutex& lock();  // Use with care; specifically for mutual exclusion during binder death
 };
@@ -569,11 +621,113 @@
 #endif // __BIONIC__
 #endif // BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
 
-class JavaDeathRecipient : public IBinder::DeathRecipient
-{
+template <typename T>
+constexpr const char* logPrefix();
+
+template <>
+constexpr const char* logPrefix<IBinder::DeathRecipient>() {
+    return "[DEATH]";
+}
+
+template <>
+constexpr const char* logPrefix<IBinder::FrozenStateChangeCallback>() {
+    return "[FREEZE]";
+}
+
+template <typename T>
+class JavaRecipient : public T {
 public:
-    JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
+    JavaRecipient(JNIEnv* env, jobject object, const sp<RecipientList<T> >& list,
+                  bool useWeakReference)
           : mVM(jnienv_to_javavm(env)), mObject(NULL), mObjectWeak(NULL), mList(list) {
+        if (useWeakReference) {
+            mObjectWeak = env->NewWeakGlobalRef(object);
+        } else {
+            mObject = env->NewGlobalRef(object);
+        }
+        // These objects manage their own lifetimes so are responsible for final bookkeeping.
+        // The list holds a strong reference to this object.
+        LOG_DEATH_FREEZE("%s Adding JavaRecipient %p to RecipientList %p", logPrefix<T>(), this,
+                         list.get());
+        list->add(this);
+    }
+
+    void clearReference() {
+        sp<RecipientList<T> > list = mList.promote();
+        if (list != NULL) {
+            LOG_DEATH_FREEZE("%s Removing JavaRecipient %p from RecipientList %p", logPrefix<T>(),
+                             this, list.get());
+            list->remove(this);
+        } else {
+            LOG_DEATH_FREEZE("%s clearReference() on JavaRecipient %p but RecipientList wp purged",
+                             logPrefix<T>(), this);
+        }
+    }
+
+    bool matches(jobject obj) {
+        bool result;
+        JNIEnv* env = javavm_to_jnienv(mVM);
+
+        if (mObject != NULL) {
+            result = env->IsSameObject(obj, mObject);
+        } else {
+            ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
+            result = env->IsSameObject(obj, me.get());
+        }
+        return result;
+    }
+
+    void warnIfStillLive() {
+        if (mObject != NULL) {
+            // Okay, something is wrong -- we have a hard reference to a live death
+            // recipient on the VM side, but the list is being torn down.
+            JNIEnv* env = javavm_to_jnienv(mVM);
+            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+            ScopedLocalRef<jstring> nameRef(env,
+                                            (jstring)env->CallObjectMethod(objClassRef.get(),
+                                                                           gClassOffsets.mGetName));
+            ScopedUtfChars nameUtf(env, nameRef.get());
+            if (nameUtf.c_str() != NULL) {
+                ALOGW("BinderProxy is being destroyed but the application did not call "
+                      "unlinkToDeath to unlink all of its death recipients beforehand.  "
+                      "Releasing leaked death recipient: %s",
+                      nameUtf.c_str());
+            } else {
+                ALOGW("BinderProxy being destroyed; unable to get DR object name");
+                env->ExceptionClear();
+            }
+        }
+    }
+
+protected:
+    virtual ~JavaRecipient() {
+        // ALOGI("Removing death ref: recipient=%p\n", mObject);
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        if (mObject != NULL) {
+            env->DeleteGlobalRef(mObject);
+        } else {
+            env->DeleteWeakGlobalRef(mObjectWeak);
+        }
+    }
+
+    JavaVM* const mVM;
+
+    // If useWeakReference is false (e.g. JavaDeathRecipient when target sdk version < 35), the
+    // Java-side Recipient is strongly referenced from mObject initially, and may later be demoted
+    // to a weak reference (mObjectWeak), e.g. upon linkToDeath() and then after binderDied() is
+    // called.
+    // If useWeakReference is true, the strong reference is never made here (i.e. mObject == NULL
+    // always). Instead, the strong reference to the Java-side Recipient is made in
+    // BinderProxy.{mDeathRecipients,mFrozenStateChangeCallbacks}. In the native world, only the
+    // weak reference is kept.
+    jobject mObject;
+    jweak mObjectWeak;
+    wp<RecipientList<T> > mList;
+};
+
+class JavaDeathRecipient : public JavaRecipient<IBinder::DeathRecipient> {
+public:
+    static bool useWeakReference() {
         // b/298374304: For apps targeting Android V or beyond, we no longer hold the global JNI ref
         // to the death recipient objects. This is to prevent the memory leak which can happen when
         // the death recipient object internally has a strong reference to the proxy object. Under
@@ -589,25 +743,26 @@
         // reference to. If however you want to get binderDied() regardless of the proxy object's
         // lifecycle, keep a strong reference to the death recipient object by yourself.
 #ifdef BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI
-        if (target_sdk_is_at_least_vic()) {
-            mObjectWeak = env->NewWeakGlobalRef(object);
-        } else
+        return target_sdk_is_at_least_vic();
+#else
+        return false;
 #endif
-        {
-            mObject = env->NewGlobalRef(object);
-        }
-        // These objects manage their own lifetimes so are responsible for final bookkeeping.
-        // The list holds a strong reference to this object.
-        LOGDEATH("Adding JDR %p to DRL %p", this, list.get());
-        list->add(this);
+    }
 
+    JavaDeathRecipient(JNIEnv* env, jobject object,
+                       const sp<RecipientList<IBinder::DeathRecipient> >& list)
+          : JavaRecipient(env, object, list, useWeakReference()) {
         gNumDeathRefsCreated.fetch_add(1, std::memory_order_relaxed);
         gcIfManyNewRefs(env);
     }
 
+    ~JavaDeathRecipient() {
+        gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
+    }
+
     void binderDied(const wp<IBinder>& who)
     {
-        LOGDEATH("Receiving binderDied() on JavaDeathRecipient %p\n", this);
+        LOG_DEATH_FREEZE("Receiving binderDied() on JavaDeathRecipient %p\n", this);
         if (mObject == NULL && mObjectWeak == NULL) {
             return;
         }
@@ -647,7 +802,7 @@
         // with our containing DeathRecipientList so that we can't delete the global ref on mObject
         // while the list is being iterated.
         if (mObject != NULL) {
-            sp<DeathRecipientList> list = mList.promote();
+            auto list = mList.promote();
             if (list != NULL) {
                 AutoMutex _l(list->lock());
 
@@ -658,126 +813,96 @@
         }
     }
 
-    void clearReference()
-    {
-        sp<DeathRecipientList> list = mList.promote();
-        if (list != NULL) {
-            LOGDEATH("Removing JDR %p from DRL %p", this, list.get());
-            list->remove(this);
-        } else {
-            LOGDEATH("clearReference() on JDR %p but DRL wp purged", this);
-        }
-    }
-
-    bool matches(jobject obj) {
-        bool result;
-        JNIEnv* env = javavm_to_jnienv(mVM);
-
-        if (mObject != NULL) {
-            result = env->IsSameObject(obj, mObject);
-        } else {
-            ScopedLocalRef<jobject> me(env, env->NewLocalRef(mObjectWeak));
-            result = env->IsSameObject(obj, me.get());
-        }
-        return result;
-    }
-
-    void warnIfStillLive() {
-        if (mObject != NULL) {
-            // Okay, something is wrong -- we have a hard reference to a live death
-            // recipient on the VM side, but the list is being torn down.
-            JNIEnv* env = javavm_to_jnienv(mVM);
-            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
-            ScopedLocalRef<jstring> nameRef(env,
-                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
-            ScopedUtfChars nameUtf(env, nameRef.get());
-            if (nameUtf.c_str() != NULL) {
-                ALOGW("BinderProxy is being destroyed but the application did not call "
-                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
-                        "Releasing leaked death recipient: %s", nameUtf.c_str());
-            } else {
-                ALOGW("BinderProxy being destroyed; unable to get DR object name");
-                env->ExceptionClear();
-            }
-        }
-    }
-
-protected:
-    virtual ~JavaDeathRecipient()
-    {
-        //ALOGI("Removing death ref: recipient=%p\n", mObject);
-        gNumDeathRefsDeleted.fetch_add(1, std::memory_order_relaxed);
-        JNIEnv* env = javavm_to_jnienv(mVM);
-        if (mObject != NULL) {
-            env->DeleteGlobalRef(mObject);
-        } else {
-            env->DeleteWeakGlobalRef(mObjectWeak);
-        }
-    }
-
 private:
-    JavaVM* const mVM;
-
-    // If target sdk version < 35, the Java-side DeathRecipient is strongly referenced from mObject
-    // upon linkToDeath() and then after binderDied() is called, the strong reference is demoted to
-    // a weak reference (mObjectWeak).
-    // If target sdk version >= 35, the strong reference is never made here (i.e. mObject == NULL
-    // always). Instead, the strong reference to the Java-side DeathRecipient is made in
-    // BinderProxy.mDeathRecipients. In the native world, only the weak reference is kept.
-    jobject mObject;
-    jweak mObjectWeak;
-    wp<DeathRecipientList> mList;
-
     // Whether binderDied was called or not.
     bool mFired = false;
 };
 
+class JavaFrozenStateChangeCallback : public JavaRecipient<IBinder::FrozenStateChangeCallback> {
+public:
+    JavaFrozenStateChangeCallback(
+            JNIEnv* env, jobject object,
+            const sp<RecipientList<IBinder::FrozenStateChangeCallback> >& list)
+          : JavaRecipient(env, object, list, /*useWeakReference=*/true) {}
+
+    void onStateChanged(const wp<IBinder>& who, State state) {
+        LOG_DEATH_FREEZE("Receiving onStateChanged() on JavaFrozenStateChangeCallback %p. state: "
+                         "%s\n",
+                         this, state == State::FROZEN ? "FROZEN" : "UNFROZEN");
+        if (mObjectWeak == NULL) {
+            return;
+        }
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        ScopedLocalRef<jobject> jBinderProxy(env, javaObjectForIBinder(env, who.promote()));
+
+        // Hold a local reference to the recipient. This may fail if the recipient is weakly
+        // referenced, in which case we can't deliver the notification.
+        ScopedLocalRef<jobject> jCallback(env, env->NewLocalRef(mObjectWeak));
+        if (jCallback.get() == NULL) {
+            return;
+        }
+        env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
+                                  gBinderProxyOffsets.mInvokeFrozenStateChangeCallback,
+                                  jCallback.get(), jBinderProxy.get(), state);
+        if (env->ExceptionCheck()) {
+            jthrowable excep = env->ExceptionOccurred();
+            binder_report_exception(env, excep,
+                                    "*** Uncaught exception returned from frozen state change "
+                                    "notification!");
+        }
+    }
+};
+
 // ----------------------------------------------------------------------------
 
-DeathRecipientList::DeathRecipientList() {
-    LOGDEATH("New DRL @ %p", this);
+template <typename T>
+RecipientList<T>::RecipientList() {
+    LOG_DEATH_FREEZE("%s New RecipientList @ %p", logPrefix<T>(), this);
 }
 
-DeathRecipientList::~DeathRecipientList() {
-    LOGDEATH("Destroy DRL @ %p", this);
+template <typename T>
+RecipientList<T>::~RecipientList() {
+    LOG_DEATH_FREEZE("%s Destroy RecipientList @ %p", logPrefix<T>(), this);
     AutoMutex _l(mLock);
 
-    // Should never happen -- the JavaDeathRecipient objects that have added themselves
+    // Should never happen -- the JavaRecipientList objects that have added themselves
     // to the list are holding references on the list object.  Only when they are torn
     // down can the list header be destroyed.
     if (mList.size() > 0) {
-        List< sp<JavaDeathRecipient> >::iterator iter;
-        for (iter = mList.begin(); iter != mList.end(); iter++) {
+        for (auto iter = mList.begin(); iter != mList.end(); iter++) {
             (*iter)->warnIfStillLive();
         }
     }
 }
 
-void DeathRecipientList::add(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::add(const sp<JavaRecipient<T> >& recipient) {
     AutoMutex _l(mLock);
 
-    LOGDEATH("DRL @ %p : add JDR %p", this, recipient.get());
+    LOG_DEATH_FREEZE("%s RecipientList @ %p : add JavaRecipient %p", logPrefix<T>(), this,
+                     recipient.get());
     mList.push_back(recipient);
 }
 
-void DeathRecipientList::remove(const sp<JavaDeathRecipient>& recipient) {
+template <typename T>
+void RecipientList<T>::remove(const sp<JavaRecipient<T> >& recipient) {
     AutoMutex _l(mLock);
 
-    List< sp<JavaDeathRecipient> >::iterator iter;
-    for (iter = mList.begin(); iter != mList.end(); iter++) {
+    for (auto iter = mList.begin(); iter != mList.end(); iter++) {
         if (*iter == recipient) {
-            LOGDEATH("DRL @ %p : remove JDR %p", this, recipient.get());
+            LOG_DEATH_FREEZE("%s RecipientList @ %p : remove JavaRecipient %p", logPrefix<T>(),
+                             this, recipient.get());
             mList.erase(iter);
             return;
         }
     }
 }
 
-sp<JavaDeathRecipient> DeathRecipientList::find(jobject recipient) {
+template <typename T>
+sp<JavaRecipient<T> > RecipientList<T>::find(jobject recipient) {
     AutoMutex _l(mLock);
 
-    List< sp<JavaDeathRecipient> >::iterator iter;
-    for (iter = mList.begin(); iter != mList.end(); iter++) {
+    for (auto iter = mList.begin(); iter != mList.end(); iter++) {
         if ((*iter)->matches(recipient)) {
             return *iter;
         }
@@ -785,10 +910,14 @@
     return NULL;
 }
 
-Mutex& DeathRecipientList::lock() {
+template <typename T>
+Mutex& RecipientList<T>::lock() {
     return mLock;
 }
 
+using DeathRecipientList = RecipientList<IBinder::DeathRecipient>;
+using FrozenStateChangeCallbackList = RecipientList<IBinder::FrozenStateChangeCallback>;
+
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -806,6 +935,11 @@
     // Death recipients for mObject. Reference counted only because DeathRecipients
     // hold a weak reference that can be temporarily promoted.
     sp<DeathRecipientList> mOrgue;  // Death recipients for mObject.
+
+    // Frozen state change callbacks for mObject. Reference counted only because
+    // JavaFrozenStateChangeCallback hold a weak reference that can be
+    // temporarily promoted.
+    sp<FrozenStateChangeCallbackList> mFrozenStateChangCallbackList;
 };
 
 BinderProxyNativeData* getBPNativeData(JNIEnv* env, jobject obj) {
@@ -825,12 +959,13 @@
     if (val->checkSubclass(&gBinderOffsets)) {
         // It's a JavaBBinder created by ibinderForJavaObject. Already has Java object.
         jobject object = static_cast<JavaBBinder*>(val.get())->object();
-        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
+        LOG_DEATH_FREEZE("objectForBinder %p: it's our own %p!\n", val.get(), object);
         return object;
     }
 
     BinderProxyNativeData* nativeData = new BinderProxyNativeData();
     nativeData->mOrgue = new DeathRecipientList;
+    nativeData->mFrozenStateChangCallbackList = new FrozenStateChangeCallbackList;
     nativeData->mObject = val;
 
     jobject object = env->CallStaticObjectMethod(gBinderProxyOffsets.mClass,
@@ -1433,7 +1568,7 @@
     BinderProxyNativeData *nd = getBPNativeData(env, obj);
     IBinder* target = nd->mObject.get();
 
-    LOGDEATH("linkToDeath: binder=%p recipient=%p\n", target, recipient);
+    LOG_DEATH_FREEZE("linkToDeath: binder=%p recipient=%p\n", target, recipient);
 
     if (!target->localBinder()) {
         DeathRecipientList* list = nd->mOrgue.get();
@@ -1464,15 +1599,15 @@
         return JNI_FALSE;
     }
 
-    LOGDEATH("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
+    LOG_DEATH_FREEZE("unlinkToDeath: binder=%p recipient=%p\n", target, recipient);
 
     if (!target->localBinder()) {
         status_t err = NAME_NOT_FOUND;
 
         // If we find the matching recipient, proceed to unlink using that
         DeathRecipientList* list = nd->mOrgue.get();
-        sp<JavaDeathRecipient> origJDR = list->find(recipient);
-        LOGDEATH("   unlink found list %p and JDR %p", list, origJDR.get());
+        sp<JavaRecipient<IBinder::DeathRecipient> > origJDR = list->find(recipient);
+        LOG_DEATH_FREEZE("   unlink found list %p and JDR %p", list, origJDR.get());
         if (origJDR != NULL) {
             wp<IBinder::DeathRecipient> dr;
             err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
@@ -1498,11 +1633,85 @@
     return res;
 }
 
+static void android_os_BinderProxy_addFrozenStateChangeCallback(
+        JNIEnv* env, jobject obj,
+        jobject callback) // throws RemoteException
+{
+    if (callback == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return;
+    }
+
+    BinderProxyNativeData* nd = getBPNativeData(env, obj);
+    IBinder* target = nd->mObject.get();
+
+    LOG_DEATH_FREEZE("addFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+    if (!target->localBinder()) {
+        FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+        auto jfscc = sp<JavaFrozenStateChangeCallback>::make(env, callback, list);
+        status_t err = target->addFrozenStateChangeCallback(jfscc);
+        if (err != NO_ERROR) {
+            // Failure adding the callback, so clear its reference now.
+            jfscc->clearReference();
+            signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
+        }
+    }
+}
+
+static jboolean android_os_BinderProxy_removeFrozenStateChangeCallback(JNIEnv* env, jobject obj,
+                                                                       jobject callback) {
+    jboolean res = JNI_FALSE;
+    if (callback == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return res;
+    }
+
+    BinderProxyNativeData* nd = getBPNativeData(env, obj);
+    IBinder* target = nd->mObject.get();
+    if (target == NULL) {
+        ALOGW("Binder has been finalized when calling removeFrozenStateChangeCallback() with "
+              "callback=%p)\n",
+              callback);
+        return JNI_FALSE;
+    }
+
+    LOG_DEATH_FREEZE("removeFrozenStateChangeCallback: binder=%p callback=%p\n", target, callback);
+
+    if (!target->localBinder()) {
+        status_t err = NAME_NOT_FOUND;
+
+        // If we find the matching callback, proceed to unlink using that
+        FrozenStateChangeCallbackList* list = nd->mFrozenStateChangCallbackList.get();
+        sp<JavaRecipient<IBinder::FrozenStateChangeCallback> > origJFSCC = list->find(callback);
+        LOG_DEATH_FREEZE("   removeFrozenStateChangeCallback found list %p and JFSCC %p", list,
+                         origJFSCC.get());
+        if (origJFSCC != NULL) {
+            err = target->removeFrozenStateChangeCallback(origJFSCC);
+            if (err == NO_ERROR) {
+                origJFSCC->clearReference();
+            }
+        }
+
+        if (err == NO_ERROR || err == DEAD_OBJECT) {
+            res = JNI_TRUE;
+        } else {
+            jniThrowException(env, "java/util/NoSuchElementException",
+                              base::StringPrintf("Frozen state change callback does not exist (%s)",
+                                                 statusToString(err).c_str())
+                                      .c_str());
+        }
+    }
+
+    return res;
+}
+
 static void BinderProxy_destroy(void* rawNativeData)
 {
     BinderProxyNativeData * nativeData = (BinderProxyNativeData *) rawNativeData;
-    LOGDEATH("Destroying BinderProxy: binder=%p drl=%p\n",
-            nativeData->mObject.get(), nativeData->mOrgue.get());
+    LOG_DEATH_FREEZE("Destroying BinderProxy: binder=%p drl=%p fsccl=%p\n",
+                     nativeData->mObject.get(), nativeData->mOrgue.get(),
+                     nativeData->mFrozenStateChangCallbackList.get());
     delete nativeData;
     IPCThreadState::self()->flushCommands();
 }
@@ -1537,6 +1746,10 @@
     {"transactNative",      "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void*)android_os_BinderProxy_transact},
     {"linkToDeathNative",   "(Landroid/os/IBinder$DeathRecipient;I)V", (void*)android_os_BinderProxy_linkToDeath},
     {"unlinkToDeathNative", "(Landroid/os/IBinder$DeathRecipient;I)Z", (void*)android_os_BinderProxy_unlinkToDeath},
+    {"addFrozenStateChangeCallbackNative",
+        "(Landroid/os/IBinder$IFrozenStateChangeCallback;)V", (void*)android_os_BinderProxy_addFrozenStateChangeCallback},
+    {"removeFrozenStateChangeCallbackNative",
+        "(Landroid/os/IBinder$IFrozenStateChangeCallback;)Z", (void*)android_os_BinderProxy_removeFrozenStateChangeCallback},
     {"getNativeFinalizer",  "()J", (void*)android_os_BinderProxy_getNativeFinalizer},
     {"getExtension",        "()Landroid/os/IBinder;", (void*)android_os_BinderProxy_getExtension},
 };
@@ -1559,6 +1772,10 @@
     gBinderProxyOffsets.mSendDeathNotice =
             GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
                                    "(Landroid/os/IBinder$DeathRecipient;Landroid/os/IBinder;)V");
+    gBinderProxyOffsets.mInvokeFrozenStateChangeCallback =
+            GetStaticMethodIDOrDie(env, clazz, "invokeFrozenStateChangeCallback",
+                                   "(Landroid/os/IBinder$IFrozenStateChangeCallback;Landroid/os/"
+                                   "IBinder;I)V");
     gBinderProxyOffsets.mNativeData = GetFieldIDOrDie(env, clazz, "mNativeData", "J");
 
     clazz = FindClassOrDie(env, "java/lang/Class");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0f53164..17c89f8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -2089,9 +2089,11 @@
         jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
                 gJankDataClassInfo.clazz, nullptr);
         for (size_t i = 0; i < jankData.size(); i++) {
-            jobject jJankData = env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
-                                               jankData[i].frameVsyncId, jankData[i].jankType,
-                                               jankData[i].frameIntervalNs);
+            jobject jJankData =
+                    env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
+                                   jankData[i].frameVsyncId, jankData[i].jankType,
+                                   jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
+                                   jankData[i].actualAppFrameTimeNs);
             env->SetObjectArrayElement(jJankDataArray, i, jJankData);
             env->DeleteLocalRef(jJankData);
         }
@@ -2727,7 +2729,7 @@
     jclass jankDataClazz =
                 FindClassOrDie(env, "android/view/SurfaceControl$JankData");
     gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
-    gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJ)V");
+    gJankDataClassInfo.ctor = GetMethodIDOrDie(env, gJankDataClassInfo.clazz, "<init>", "(JIJJJ)V");
     jclass onJankDataListenerClazz =
             FindClassOrDie(env, "android/view/SurfaceControl$OnJankDataListener");
     gJankDataListenerClassInfo.clazz = MakeGlobalRefOrDie(env, onJankDataListenerClazz);
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
index 1031542..1a52fb7 100644
--- a/core/jni/android_window_ScreenCapture.cpp
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -19,6 +19,7 @@
 
 #include <android/gui/BnScreenCaptureListener.h>
 #include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <gui/AidlUtil.h>
 #include <gui/SurfaceComposerClient.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
@@ -141,12 +142,13 @@
 };
 
 static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs) {
-    captureArgs.pixelFormat = static_cast<ui::PixelFormat>(
+    captureArgs.pixelFormat = static_cast<int32_t>(
             env->GetIntField(captureArgsObject, gCaptureArgsClassInfo.pixelFormat));
-    captureArgs.sourceCrop =
+    const auto sourceCrop =
             JNICommon::rectFromObj(env,
                                    env->GetObjectField(captureArgsObject,
                                                        gCaptureArgsClassInfo.sourceCrop));
+    captureArgs.sourceCrop = gui::aidl_utils::toARect(sourceCrop);
     captureArgs.frameScaleX =
             env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
     captureArgs.frameScaleY =
@@ -172,7 +174,7 @@
                 jniThrowNullPointerException(env, "Exclude layer is null");
                 return;
             }
-            captureArgs.excludeHandles.emplace(excludeObject->getHandle());
+            captureArgs.excludeHandles.emplace_back(excludeObject->getHandle());
         }
     }
     captureArgs.hintForSeamlessTransition =
@@ -182,18 +184,18 @@
 
 static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
                                                        jobject displayCaptureArgsObject) {
-    DisplayCaptureArgs captureArgs;
-    getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
+    DisplayCaptureArgs displayCaptureArgs;
+    getCaptureArgs(env, displayCaptureArgsObject, displayCaptureArgs.captureArgs);
 
-    captureArgs.displayToken =
+    displayCaptureArgs.displayToken =
             ibinderForJavaObject(env,
                                  env->GetObjectField(displayCaptureArgsObject,
                                                      gDisplayCaptureArgsClassInfo.displayToken));
-    captureArgs.width =
+    displayCaptureArgs.width =
             env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
-    captureArgs.height =
+    displayCaptureArgs.height =
             env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
-    return captureArgs;
+    return displayCaptureArgs;
 }
 
 static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
@@ -212,8 +214,8 @@
 
 static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
                                 jlong screenCaptureListenerObject, jboolean sync) {
-    LayerCaptureArgs captureArgs;
-    getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
+    LayerCaptureArgs layerCaptureArgs;
+    getCaptureArgs(env, layerCaptureArgsObject, layerCaptureArgs.captureArgs);
 
     SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
             env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
@@ -221,13 +223,13 @@
         return BAD_VALUE;
     }
 
-    captureArgs.layerHandle = layer->getHandle();
-    captureArgs.childrenOnly =
+    layerCaptureArgs.layerHandle = layer->getHandle();
+    layerCaptureArgs.childrenOnly =
             env->GetBooleanField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.childrenOnly);
 
     sp<gui::IScreenCaptureListener> captureListener =
             reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
-    return ScreenshotClient::captureLayers(captureArgs, captureListener, sync);
+    return ScreenshotClient::captureLayers(layerCaptureArgs, captureListener, sync);
 }
 
 static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index fba0d81..7ad18b8 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "NativeLibraryHelper"
 //#define LOG_NDEBUG 0
 
+#include <android-base/properties.h>
 #include <androidfw/ApkParsing.h>
 #include <androidfw/ZipFileRO.h>
 #include <androidfw/ZipUtils.h>
@@ -36,6 +37,7 @@
 #include <zlib.h>
 
 #include <memory>
+#include <string>
 
 #include "com_android_internal_content_FileSystemUtils.h"
 #include "core_jni_helpers.h"
@@ -125,72 +127,10 @@
     return INSTALL_SUCCEEDED;
 }
 
-/*
- * Copy the native library if needed.
- *
- * This function assumes the library and path names passed in are considered safe.
- */
-static install_status_t
-copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
-{
-    static const size_t kPageSize = getpagesize();
-    void** args = reinterpret_cast<void**>(arg);
-    jstring* javaNativeLibPath = (jstring*) args[0];
-    jboolean extractNativeLibs = *(jboolean*) args[1];
-    jboolean debuggable = *(jboolean*) args[2];
-
-    ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
-
-    uint32_t uncompLen;
-    uint32_t when;
-    uint32_t crc;
-
-    uint16_t method;
-    off64_t offset;
-    uint16_t extraFieldLength;
-    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
-                               &extraFieldLength)) {
-        ALOGE("Couldn't read zip entry info\n");
-        return INSTALL_FAILED_INVALID_APK;
-    }
-
-    // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
-    // easier to use wrap.sh because it only works when it is extracted, see
-    // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
-    bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
-
-    if (!extractNativeLibs && !forceExtractCurrentFile) {
-        // check if library is uncompressed and page-aligned
-        if (method != ZipFileRO::kCompressStored) {
-            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
-                fileName);
-            return INSTALL_FAILED_INVALID_APK;
-        }
-
-        if (offset % kPageSize != 0) {
-            ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
-                  "from apk.\n", fileName, kPageSize);
-            return INSTALL_FAILED_INVALID_APK;
-        }
-
-#ifdef ENABLE_PUNCH_HOLES
-        // if library is uncompressed, punch hole in it in place
-        if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
-            ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
-                  "%" PRIu64 "",
-                  fileName, zipFile->getZipFileName(), offset);
-        }
-
-        // if extra field for this zip file is present with some length, possibility is that it is
-        // padding added for zip alignment. Punch holes there too.
-        if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
-            ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
-        }
-#endif // ENABLE_PUNCH_HOLES
-
-        return INSTALL_SUCCEEDED;
-    }
-
+static install_status_t extractNativeLibFromApk(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+                                                const char* fileName,
+                                                const std::string nativeLibPath, uint32_t when,
+                                                uint32_t uncompLen, uint32_t crc) {
     // Build local file path
     const size_t fileNameLen = strlen(fileName);
     char localFileName[nativeLibPath.size() + fileNameLen + 2];
@@ -313,6 +253,88 @@
 }
 
 /*
+ * Copy the native library if needed.
+ *
+ * This function assumes the library and path names passed in are considered safe.
+ */
+static install_status_t copyFileIfChanged(JNIEnv* env, void* arg, ZipFileRO* zipFile,
+                                          ZipEntryRO zipEntry, const char* fileName) {
+    static const size_t kPageSize = getpagesize();
+    void** args = reinterpret_cast<void**>(arg);
+    jstring* javaNativeLibPath = (jstring*)args[0];
+    jboolean extractNativeLibs = *(jboolean*)args[1];
+    jboolean debuggable = *(jboolean*)args[2];
+    jboolean app_compat_16kb = *(jboolean*)args[3];
+    install_status_t ret = INSTALL_SUCCEEDED;
+
+    ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
+
+    uint32_t uncompLen;
+    uint32_t when;
+    uint32_t crc;
+
+    uint16_t method;
+    off64_t offset;
+    uint16_t extraFieldLength;
+    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc,
+                               &extraFieldLength)) {
+        ALOGE("Couldn't read zip entry info\n");
+        return INSTALL_FAILED_INVALID_APK;
+    }
+
+    // Always extract wrap.sh for debuggable, even if extractNativeLibs=false. This makes it
+    // easier to use wrap.sh because it only works when it is extracted, see
+    // frameworks/base/services/core/java/com/android/server/am/ProcessList.java.
+    bool forceExtractCurrentFile = debuggable && strcmp(fileName, "wrap.sh") == 0;
+
+    if (!extractNativeLibs && !forceExtractCurrentFile) {
+        // check if library is uncompressed and page-aligned
+        if (method != ZipFileRO::kCompressStored) {
+            ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+                  fileName);
+            return INSTALL_FAILED_INVALID_APK;
+        }
+
+        if (offset % kPageSize != 0) {
+            // If the library is zip-aligned correctly for 4kb devices and app compat is
+            // enabled, on 16kb devices fallback to extraction
+            if (offset % 0x1000 == 0 && app_compat_16kb) {
+                ALOGI("16kB AppCompat: Library '%s' is not PAGE(%zu)-aligned - falling back to "
+                      "extraction from apk\n",
+                      fileName, kPageSize);
+                return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(),
+                                               when, uncompLen, crc);
+            }
+
+            ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+                  "from apk.\n",
+                  fileName, kPageSize);
+            return INSTALL_FAILED_INVALID_APK;
+        }
+
+#ifdef ENABLE_PUNCH_HOLES
+        // if library is uncompressed, punch hole in it in place
+        if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+            ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+                  "%" PRIu64 "",
+                  fileName, zipFile->getZipFileName(), offset);
+        }
+
+        // if extra field for this zip file is present with some length, possibility is that it is
+        // padding added for zip alignment. Punch holes there too.
+        if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) {
+            ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName());
+        }
+#endif // ENABLE_PUNCH_HOLES
+
+        return INSTALL_SUCCEEDED;
+    }
+
+    return extractNativeLibFromApk(zipFile, zipEntry, fileName, nativeLibPath.c_str(), when,
+                                   uncompLen, crc);
+}
+
+/*
  * An iterator over all shared libraries in a zip file. An entry is
  * considered to be a shared library if all of the conditions below are
  * satisfied :
@@ -498,12 +520,24 @@
     return status;
 }
 
+static inline bool app_compat_16kb_enabled() {
+    static const size_t kPageSize = getpagesize();
+
+    // App compat is only applicable on 16kb-page-size devices.
+    if (kPageSize != 0x4000) {
+        return false;
+    }
+
+    return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+}
+
 static jint
 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
         jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
         jboolean extractNativeLibs, jboolean debuggable)
 {
-    void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable };
+    jboolean app_compat_16kb = app_compat_16kb_enabled();
+    void* args[] = { &javaNativeLibPath, &extractNativeLibs, &debuggable, &app_compat_16kb };
     return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
             copyFileIfChanged, reinterpret_cast<void*>(args));
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 062fab3..284c2997 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -19,14 +19,6 @@
 
 #include "com_android_internal_os_Zygote.h"
 
-#include <async_safe/log.h>
-
-// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
-#include <sys/mount.h>
-#include <linux/fs.h>
-#include <sys/types.h>
-#include <dirent.h>
-
 #include <algorithm>
 #include <array>
 #include <atomic>
@@ -41,32 +33,31 @@
 
 #include <android/fdsan.h>
 #include <arpa/inet.h>
+#include <dirent.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
 #include <malloc.h>
 #include <mntent.h>
-#include <paths.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/auxv.h>
 #include <sys/capability.h>
-#include <sys/cdefs.h>
 #include <sys/eventfd.h>
+#include <sys/mount.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#include <sys/system_properties.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <async_safe/log.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 020b27e..19f8299 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -391,6 +391,7 @@
 
 } // namespace android
 
+#ifndef _WIN32
 using namespace android;
 
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
@@ -407,3 +408,4 @@
 
     return JNI_VERSION_1_6;
 }
+#endif
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index 8de5458..78cf6f4 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -40,4 +40,5 @@
     optional bytes start_intent = 10;
     optional AppStartLaunchMode launch_mode = 11;
     optional bool was_force_stopped = 12;
+    optional int64 monotonic_creation_time_ms = 13;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 58f39a9..42c591b 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -1045,7 +1045,7 @@
     repeated Package packages = 2;
 }
 
-// sync with com.android.server.am.am.ProcessList.java
+// LINT.IfChange
 message AppsStartInfoProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -1064,4 +1064,6 @@
         repeated User users = 2;
     }
     repeated Package packages = 2;
+    optional int64 monotonic_time = 3;
 }
+// LINT.ThenChange(/services/core/java/com/android/server/am/AppStartInfoTracker.java)
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 12804d4..e7f0560 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -163,7 +163,7 @@
     optional bool vibrator_under_external_control = 5;
     optional bool low_power_mode = 6;
     optional bool vibrate_on = 24;
-    optional bool keyboard_vibration_on = 25;
+    reserved 25; // prev keyboard_vibration_on
     optional int32 default_vibration_amplitude = 26;
     optional int32 alarm_intensity = 18;
     optional int32 alarm_default_intensity = 19;
diff --git a/core/res/Android.bp b/core/res/Android.bp
index e900eb2..17d7bfa 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -156,6 +156,7 @@
     generate_product_characteristics_rro: true,
 
     flags_packages: [
+        "android.app.appfunctions.flags-aconfig",
         "android.app.contextualsearch.flags-aconfig",
         "android.content.pm.flags-aconfig",
         "android.provider.flags-aconfig",
@@ -164,7 +165,9 @@
         "com.android.window.flags.window-aconfig",
         "android.permission.flags-aconfig",
         "android.os.flags-aconfig",
+        "android.os.vibrator.flags-aconfig",
         "android.media.tv.flags-aconfig",
+        "com.android.hardware.input.input-aconfig",
     ],
 }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7aeabee..a19b71c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -830,7 +830,6 @@
     <protected-broadcast android:name="android.intent.action.PROFILE_REMOVED" />
     <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_SENT_ACTION" />
     <protected-broadcast android:name="com.android.internal.telephony.cat.SMS_DELIVERY_ACTION" />
-    <protected-broadcast android:name="com.android.internal.telephony.data.ACTION_RETRY" />
     <protected-broadcast android:name="android.companion.virtual.action.VIRTUAL_DEVICE_REMOVED" />
     <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_START_PREVIEW" />
     <protected-broadcast android:name="com.android.internal.intent.action.FLASH_NOTIFICATION_STOP_PREVIEW" />
@@ -844,6 +843,7 @@
     <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />
     <protected-broadcast android:name="com.android.uwb.uwbcountrycode.GEOCODE_RETRY" />
+    <protected-broadcast android:name="android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -2617,7 +2617,8 @@
          @hide
     -->
     <permission android:name="android.permission.VIBRATE_VENDOR_EFFECTS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged"
+        android:featureFlag="android.os.vibrator.vendor_vibration_effects" />
 
     <!-- @SystemApi Allows access to the vibrator state.
          <p>Protection level: signature
@@ -3765,7 +3766,6 @@
         privileged app such as the Assistant app.
         <p>Protection level: internal|role
         <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-        @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT"
         android:protectionLevel="internal|role" />
@@ -3836,7 +3836,6 @@
     <!-- Allows an application to use audit logging API.
         @hide
         @SystemApi
-        @FlaggedApi("android.app.admin.flags.security_log_v2_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING"
         android:protectionLevel="internal|role" />
@@ -4027,7 +4026,6 @@
         APIs protected by this permission on users different to the calling user.
         <p>Protection level: internal|role
         <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
-        @FlaggedApi("android.app.admin.flags.esim_management_enabled")
     -->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS"
         android:protectionLevel="internal|role" />
@@ -5605,12 +5603,13 @@
     <!-- This permission is required among systems services to always keep the
          binding with TvInputManagerService.
          <p>This should only be used by the OEM TvInputService.
+         @FlaggedApi("android.media.tv.flags.tif_unbind_inactive_tis")
          <p>Protection level: signature|privileged|vendorPrivileged
          @hide
     -->
     <permission android:name="android.permission.ALWAYS_BOUND_TV_INPUT"
         android:protectionLevel="signature|privileged|vendorPrivileged"
-        android:featureFlag="android.media.tv.flags.tis_always_bound_permission"/>
+        android:featureFlag="android.media.tv.flags.tif_unbind_inactive_tis"/>
 
     <!-- Must be required by a {@link android.media.tv.interactive.TvInteractiveAppService}
          to ensure that only the system can bind to it.
@@ -7649,7 +7648,8 @@
     <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
         android:protectionLevel="signature" />
 
-    <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+    <!-- @FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY) @SystemApi
+         Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
          ensure that only the system can bind to it.
          @hide This is not a third-party API (intended for OEMs and system apps).
     -->
@@ -8010,6 +8010,41 @@
     <permission android:name="android.permission.EXECUTE_APP_ACTION"
                 android:protectionLevel="internal|role" />
 
+    <!-- Must be required by an {@link android.app.appfunctions.AppFunctionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+         @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager")  -->
+    <permission android:name="android.permission.BIND_APP_FUNCTION_SERVICE"
+        android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows a trusted application to perform actions on behalf of users inside of
+         applications with privacy guarantees from the system.
+         <p>This permission is currently only granted to system packages in the
+         {@link android.app.role.SYSTEM_UI_INTELLIGENCE} role which complies with privacy
+         requirements outlined in the Android CDD section "9.8.6 Content Capture".
+         <p>Apps are not able to opt-out from caller having this permission.
+         <p>Protection level: internal|role
+         @hide
+         @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager")  -->
+    <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED"
+        android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
+        android:protectionLevel="internal|role" />
+
+    <!-- @SystemApi Allows an application to perform actions on behalf of users inside of
+         applications.
+         <p>This permission is currently only granted to preinstalled / system apps having the
+         {@link android.app.role.ASSISTANT} role.
+         <p>Apps contributing app functions can opt to disallow callers with this permission,
+         limiting to only callers with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}
+         instead.
+         <p>Protection level: internal|role
+         @hide
+         @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager")  -->
+    <permission android:name="android.permission.EXECUTE_APP_FUNCTIONS"
+        android:featureFlag="android.app.appfunctions.flags.enable_app_function_manager"
+        android:protectionLevel="internal|role" />
+
     <!-- Allows an application to display its suggestions using the autofill framework.
          <p>For now, this permission is only granted to the Browser application.
          <p>Protection level: internal|role
@@ -8132,11 +8167,12 @@
     <permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE"
                 android:protectionLevel="signature" />
 
-    <!-- Allows low-level access to monitor keyboard system shortcuts
+    <!-- Allows low-level access to manage key gestures.
          <p>Not for use by third-party applications.
          @hide -->
-    <permission android:name="android.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS"
-                android:protectionLevel="signature" />
+    <permission android:name="android.permission.MANAGE_KEY_GESTURES"
+                android:protectionLevel="signature"
+                android:featureFlag="com.android.hardware.input.manage_key_gestures" />
 
     <uses-permission android:name="android.permission.HANDLE_QUERY_PACKAGE_RESTART" />
 
diff --git a/core/res/OWNERS b/core/res/OWNERS
index b2b58d5..5293131 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -78,6 +78,11 @@
 per-file res/values/config_telephony.xml = file:/platform/frameworks/opt/telephony:/OWNERS
 per-file res/xml/sms_short_codes.xml = file:/platform/frameworks/opt/telephony:/OWNERS
 
+# Input Method Framework
+per-file res/*/*input_method* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+per-file res/*/*_ime* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+per-file res/*/ime_* = file:/services/core/java/com/android/server/inputmethod/OWNERS
+
 # TV Input Framework
 per-file res/values/config_tv_external_input_logging.xml = file:/services/core/java/com/android/server/tv/OWNERS
 
diff --git a/core/res/res/color/input_method_switch_on_item.xml b/core/res/res/color/input_method_switch_on_item.xml
new file mode 100644
index 0000000..49fe081
--- /dev/null
+++ b/core/res/res/color/input_method_switch_on_item.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="?attr/materialColorOnSecondaryContainer" />
+    <item android:color="?attr/materialColorOnSurface" />
+</selector>
diff --git a/core/res/res/drawable-car/car_activity_resolver_list_background.xml b/core/res/res/drawable-car/car_activity_resolver_list_background.xml
deleted file mode 100644
index dbbadd8..0000000
--- a/core/res/res/drawable-car/car_activity_resolver_list_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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">
-    <solid android:color="?attr/colorBackgroundFloating" />
-    <corners android:radius="@dimen/car_activity_resolver_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_animal_paw.xml b/core/res/res/drawable/ic_zen_mode_icon_animal_paw.xml
new file mode 100644
index 0000000..31004ec
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_animal_paw.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="M180,485Q138,485 109,456Q80,427 80,385Q80,343 109,314Q138,285 180,285Q222,285 251,314Q280,343 280,385Q280,427 251,456Q222,485 180,485ZM360,325Q318,325 289,296Q260,267 260,225Q260,183 289,154Q318,125 360,125Q402,125 431,154Q460,183 460,225Q460,267 431,296Q402,325 360,325ZM600,325Q558,325 529,296Q500,267 500,225Q500,183 529,154Q558,125 600,125Q642,125 671,154Q700,183 700,225Q700,267 671,296Q642,325 600,325ZM780,485Q738,485 709,456Q680,427 680,385Q680,343 709,314Q738,285 780,285Q822,285 851,314Q880,343 880,385Q880,427 851,456Q822,485 780,485ZM266,885Q221,885 190.5,850.5Q160,816 160,769Q160,717 195.5,678Q231,639 266,601Q295,570 316,533.5Q337,497 366,465Q388,439 417,422Q446,405 480,405Q514,405 543,421Q572,437 594,463Q622,495 643.5,532Q665,569 694,601Q729,639 764.5,678Q800,717 800,769Q800,816 769.5,850.5Q739,885 694,885Q640,885 587,876Q534,867 480,867Q426,867 373,876Q320,885 266,885Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_apartment_building.xml b/core/res/res/drawable/ic_zen_mode_icon_apartment_building.xml
new file mode 100644
index 0000000..30f01fa
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_apartment_building.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="M120,840L120,280L280,280L280,120L680,120L680,440L840,440L840,840L520,840L520,680L440,680L440,840L120,840ZM200,760L280,760L280,680L200,680L200,760ZM200,600L280,600L280,520L200,520L200,600ZM200,440L280,440L280,360L200,360L200,440ZM360,600L440,600L440,520L360,520L360,600ZM360,440L440,440L440,360L360,360L360,440ZM360,280L440,280L440,200L360,200L360,280ZM520,600L600,600L600,520L520,520L520,600ZM520,440L600,440L600,360L520,360L520,440ZM520,280L600,280L600,200L520,200L520,280ZM680,760L760,760L760,680L680,680L680,760ZM680,600L760,600L760,520L680,520L680,600Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_book.xml b/core/res/res/drawable/ic_zen_mode_icon_book.xml
new file mode 100644
index 0000000..c30d222
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_book.xml
@@ -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.
+-->
+<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"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M560,396L560,328Q593,314 627.5,307Q662,300 700,300Q726,300 751,304Q776,308 800,314L800,378Q776,369 751.5,364.5Q727,360 700,360Q662,360 627,369.5Q592,379 560,396ZM560,616L560,548Q593,534 627.5,527Q662,520 700,520Q726,520 751,524Q776,528 800,534L800,598Q776,589 751.5,584.5Q727,580 700,580Q662,580 627,589Q592,598 560,616ZM560,506L560,438Q593,424 627.5,417Q662,410 700,410Q726,410 751,414Q776,418 800,424L800,488Q776,479 751.5,474.5Q727,470 700,470Q662,470 627,479.5Q592,489 560,506ZM260,640Q307,640 351.5,650.5Q396,661 440,682L440,288Q399,264 353,252Q307,240 260,240Q224,240 188.5,247Q153,254 120,268Q120,268 120,268Q120,268 120,268L120,664Q120,664 120,664Q120,664 120,664Q155,652 189.5,646Q224,640 260,640ZM520,682Q564,661 608.5,650.5Q653,640 700,640Q736,640 770.5,646Q805,652 840,664Q840,664 840,664Q840,664 840,664L840,268Q840,268 840,268Q840,268 840,268Q807,254 771.5,247Q736,240 700,240Q653,240 607,252Q561,264 520,288L520,682ZM480,800Q432,762 376,741Q320,720 260,720Q218,720 177.5,731Q137,742 100,762Q79,773 59.5,761Q40,749 40,726L40,244Q40,233 45.5,223Q51,213 62,208Q108,184 158,172Q208,160 260,160Q318,160 373.5,175Q429,190 480,220Q531,190 586.5,175Q642,160 700,160Q752,160 802,172Q852,184 898,208Q909,213 914.5,223Q920,233 920,244L920,726Q920,749 900.5,761Q881,773 860,762Q823,742 782.5,731Q742,720 700,720Q640,720 584,741Q528,762 480,800ZM280,466Q280,466 280,466Q280,466 280,466L280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466L280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466Q280,466 280,466Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_child.xml b/core/res/res/drawable/ic_zen_mode_icon_child.xml
new file mode 100644
index 0000000..d11772a
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_child.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="M580,470Q559,470 544.5,455.5Q530,441 530,420Q530,399 544.5,384.5Q559,370 580,370Q601,370 615.5,384.5Q630,399 630,420Q630,441 615.5,455.5Q601,470 580,470ZM380,470Q359,470 344.5,455.5Q330,441 330,420Q330,399 344.5,384.5Q359,370 380,370Q401,370 415.5,384.5Q430,399 430,420Q430,441 415.5,455.5Q401,470 380,470ZM480,680Q420,680 371.5,647Q323,614 300,560L660,560Q637,614 588.5,647Q540,680 480,680ZM480,840Q405,840 339.5,811.5Q274,783 225.5,734.5Q177,686 148.5,620.5Q120,555 120,480Q120,405 148.5,339.5Q177,274 225.5,225.5Q274,177 339.5,148.5Q405,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM480,760Q596,760 678,678Q760,596 760,480Q760,364 678,282Q596,200 480,200Q474,200 468,200Q462,200 456,202Q450,208 448,215Q446,222 446,230Q446,251 460.5,265.5Q475,280 496,280Q505,280 512.5,277Q520,274 528,274Q540,274 548,283Q556,292 556,304Q556,327 534.5,333.5Q513,340 496,340Q451,340 418.5,307.5Q386,275 386,230Q386,227 386,224Q386,221 387,216Q304,246 252,317Q200,388 200,480Q200,596 282,678Q364,760 480,760ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_classical_building.xml b/core/res/res/drawable/ic_zen_mode_icon_classical_building.xml
new file mode 100644
index 0000000..2675126
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_classical_building.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,680L200,400L280,400L280,680L200,680ZM440,680L440,400L520,400L520,680L440,680ZM80,840L80,760L880,760L880,840L80,840ZM680,680L680,400L760,400L760,680L680,680ZM80,320L80,240L480,40L880,240L880,320L80,320ZM258,240L480,240L702,240L258,240ZM258,240L702,240L480,130L258,240Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_croissant.xml b/core/res/res/drawable/ic_zen_mode_icon_croissant.xml
new file mode 100644
index 0000000..199343d0
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_croissant.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="M804,678Q821,687 834,674Q847,661 838,644L780,536L738,644L804,678ZM604,640L652,640L748,402Q751,394 746.5,388.5Q742,383 736,380L656,348Q647,345 638.5,350Q630,355 628,364L604,640ZM308,640L356,640L332,364Q330,353 321.5,349Q313,345 304,348L224,380Q216,383 212.5,388.5Q209,394 212,402L308,640ZM156,678L222,644L180,536L122,644Q113,661 126,674Q139,687 156,678ZM436,640L524,640L554,302Q556,293 549.5,286.5Q543,280 534,280L426,280Q418,280 411.5,286.5Q405,293 406,302L436,640ZM138,760Q96,760 68,728.5Q40,697 40,654Q40,642 43.5,630.5Q47,619 52,608L140,440Q126,400 141,361Q156,322 194,306L274,274Q288,269 302,267Q316,265 330,268Q344,239 369,219.5Q394,200 426,200L534,200Q566,200 591,219.5Q616,239 630,268Q644,266 658,267.5Q672,269 686,274L766,306Q806,322 822,361Q838,400 820,438L908,606Q914,617 917,629Q920,641 920,654Q920,699 889.5,729.5Q859,760 814,760Q803,760 792,757.5Q781,755 770,750L708,720L250,720L194,750Q181,757 166.5,758.5Q152,760 138,760ZM480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_fork_and_knife.xml b/core/res/res/drawable/ic_zen_mode_icon_fork_and_knife.xml
new file mode 100644
index 0000000..1fa7379
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_fork_and_knife.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="M280,880L280,514Q229,500 194.5,458Q160,416 160,360L160,80L240,80L240,360L280,360L280,80L360,80L360,360L400,360L400,80L480,80L480,360Q480,416 445.5,458Q411,500 360,514L360,880L280,880ZM680,880L680,560L560,560L560,280Q560,197 618.5,138.5Q677,80 760,80L760,880L680,880Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_group_of_people.xml b/core/res/res/drawable/ic_zen_mode_icon_group_of_people.xml
new file mode 100644
index 0000000..c6194d5
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_group_of_people.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="M0,720L0,657Q0,614 44,587Q88,560 160,560Q173,560 185,560.5Q197,561 208,563Q194,584 187,607Q180,630 180,655L180,720L0,720ZM240,720L240,655Q240,623 257.5,596.5Q275,570 307,550Q339,530 383.5,520Q428,510 480,510Q533,510 577.5,520Q622,530 654,550Q686,570 703,596.5Q720,623 720,655L720,720L240,720ZM780,720L780,655Q780,629 773.5,606Q767,583 754,563Q765,561 776.5,560.5Q788,560 800,560Q872,560 916,586.5Q960,613 960,657L960,720L780,720ZM325,640L636,640L636,640Q626,620 580.5,605Q535,590 480,590Q425,590 379.5,605Q334,620 325,640ZM160,520Q127,520 103.5,496.5Q80,473 80,440Q80,406 103.5,383Q127,360 160,360Q194,360 217,383Q240,406 240,440Q240,473 217,496.5Q194,520 160,520ZM800,520Q767,520 743.5,496.5Q720,473 720,440Q720,406 743.5,383Q767,360 800,360Q834,360 857,383Q880,406 880,440Q880,473 857,496.5Q834,520 800,520ZM480,480Q430,480 395,445Q360,410 360,360Q360,309 395,274.5Q430,240 480,240Q531,240 565.5,274.5Q600,309 600,360Q600,410 565.5,445Q531,480 480,480ZM480,400Q497,400 508.5,388.5Q520,377 520,360Q520,343 508.5,331.5Q497,320 480,320Q463,320 451.5,331.5Q440,343 440,360Q440,377 451.5,388.5Q463,400 480,400ZM481,640L481,640Q481,640 481,640Q481,640 481,640Q481,640 481,640Q481,640 481,640L481,640ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_headphones.xml b/core/res/res/drawable/ic_zen_mode_icon_headphones.xml
new file mode 100644
index 0000000..f0bc7a2
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_headphones.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="M360,840L200,840Q167,840 143.5,816.5Q120,793 120,760L120,480Q120,405 148.5,339.5Q177,274 225.5,225.5Q274,177 339.5,148.5Q405,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480L840,760Q840,793 816.5,816.5Q793,840 760,840L600,840L600,520L760,520L760,480Q760,363 678.5,281.5Q597,200 480,200Q363,200 281.5,281.5Q200,363 200,480L200,520L360,520L360,840ZM280,600L200,600L200,760Q200,760 200,760Q200,760 200,760L280,760L280,600ZM680,600L680,760L760,760Q760,760 760,760Q760,760 760,760L760,600L680,600ZM280,600L280,600L200,600Q200,600 200,600Q200,600 200,600L200,600L280,600ZM680,600L760,600L760,600Q760,600 760,600Q760,600 760,600L680,600Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_heart.xml b/core/res/res/drawable/ic_zen_mode_icon_heart.xml
new file mode 100644
index 0000000..c9b1577
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_heart.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="M480,840L422,788Q321,697 255,631Q189,565 150,512.5Q111,460 95.5,416Q80,372 80,326Q80,232 143,169Q206,106 300,106Q352,106 399,128Q446,150 480,190Q514,150 561,128Q608,106 660,106Q754,106 817,169Q880,232 880,326Q880,372 864.5,416Q849,460 810,512.5Q771,565 705,631Q639,697 538,788L480,840ZM480,732Q576,646 638,584.5Q700,523 736,477.5Q772,432 786,396.5Q800,361 800,326Q800,266 760,226Q720,186 660,186Q613,186 573,212.5Q533,239 518,280L518,280L442,280L442,280Q427,239 387,212.5Q347,186 300,186Q240,186 200,226Q160,266 160,326Q160,361 174,396.5Q188,432 224,477.5Q260,523 322,584.5Q384,646 480,732ZM480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459L480,459L480,459L480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Q480,459 480,459Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_house.xml b/core/res/res/drawable/ic_zen_mode_icon_house.xml
new file mode 100644
index 0000000..e25d194
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_house.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="M160,840L160,465L88,520L40,456L160,364L160,240L240,240L240,303L480,120L920,456L872,519L800,465L800,840L160,840ZM240,760L440,760L440,600L520,600L520,760L720,760L720,404L480,221L240,404L240,760ZM160,200Q160,150 195,115Q230,80 280,80Q297,80 308.5,68.5Q320,57 320,40L400,40Q400,90 365,125Q330,160 280,160Q263,160 251.5,171.5Q240,183 240,200L160,200ZM240,760L440,760L440,760L520,760L520,760L720,760L720,760L480,760L240,760Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_lightbulb.xml b/core/res/res/drawable/ic_zen_mode_icon_lightbulb.xml
new file mode 100644
index 0000000..602e60d
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_lightbulb.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="M480,880Q454,880 433,867.5Q412,855 400,834L400,834Q367,834 343.5,810.5Q320,787 320,754L320,612Q261,573 225.5,509Q190,445 190,370Q190,249 274.5,164.5Q359,80 480,80Q601,80 685.5,164.5Q770,249 770,370Q770,447 734.5,510Q699,573 640,612L640,754Q640,787 616.5,810.5Q593,834 560,834L560,834Q548,855 527,867.5Q506,880 480,880ZM400,754L560,754L560,718L400,718L400,754ZM400,678L560,678L560,640L400,640L400,678ZM392,560L450,560L450,452L362,364L404,322L480,398L556,322L598,364L510,452L510,560L568,560Q622,534 656,483.5Q690,433 690,370Q690,282 629,221Q568,160 480,160Q392,160 331,221Q270,282 270,370Q270,433 304,483.5Q338,534 392,560ZM480,398L480,398L480,398L480,398L480,398L480,398L480,398L480,398L480,398ZM480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360Q480,360 480,360L480,360L480,360L480,360L480,360L480,360L480,360L480,360L480,360L480,360Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_lotus_flower.xml b/core/res/res/drawable/ic_zen_mode_icon_lotus_flower.xml
new file mode 100644
index 0000000..c1afd44
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_lotus_flower.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="M480,880Q407,871 335,840.5Q263,810 206.5,753Q150,696 115,609Q80,522 80,400L80,360L120,360Q171,360 225,373Q279,386 326,412Q338,326 380.5,235.5Q423,145 480,80Q537,145 579.5,235.5Q622,326 634,412Q681,386 735,373Q789,360 840,360L880,360L880,400Q880,522 845,609Q810,696 753.5,753Q697,810 625.5,840.5Q554,871 480,880ZM478,798Q467,632 379.5,547Q292,462 162,442Q173,613 263.5,697Q354,781 478,798ZM480,544Q495,522 516.5,498.5Q538,475 558,458Q556,401 535.5,339Q515,277 480,218Q445,277 424.5,339Q404,401 402,458Q422,475 444,498.5Q466,522 480,544ZM558,780Q595,768 635,745Q675,722 709.5,682.5Q744,643 768.5,584Q793,525 798,442Q704,456 633,504.5Q562,553 524,628Q536,660 544.5,698Q553,736 558,780ZM480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544ZM558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780ZM478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798ZM524,628L524,628Q524,628 524,628Q524,628 524,628L524,628L524,628L524,628Q524,628 524,628Q524,628 524,628L524,628Q524,628 524,628Q524,628 524,628ZM480,880L480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_palette.xml b/core/res/res/drawable/ic_zen_mode_icon_palette.xml
new file mode 100644
index 0000000..f31704d
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_palette.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="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 112.5,324Q145,251 200.5,197Q256,143 330,111.5Q404,80 488,80Q568,80 639,107.5Q710,135 763.5,183.5Q817,232 848.5,298.5Q880,365 880,442Q880,557 810,618.5Q740,680 640,680L566,680Q557,680 553.5,685Q550,690 550,696Q550,708 565,730.5Q580,753 580,782Q580,832 552.5,856Q525,880 480,880ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM260,520Q286,520 303,503Q320,486 320,460Q320,434 303,417Q286,400 260,400Q234,400 217,417Q200,434 200,460Q200,486 217,503Q234,520 260,520ZM380,360Q406,360 423,343Q440,326 440,300Q440,274 423,257Q406,240 380,240Q354,240 337,257Q320,274 320,300Q320,326 337,343Q354,360 380,360ZM580,360Q606,360 623,343Q640,326 640,300Q640,274 623,257Q606,240 580,240Q554,240 537,257Q520,274 520,300Q520,326 537,343Q554,360 580,360ZM700,520Q726,520 743,503Q760,486 760,460Q760,434 743,417Q726,400 700,400Q674,400 657,417Q640,434 640,460Q640,486 657,503Q674,520 700,520ZM480,800Q489,800 494.5,795Q500,790 500,782Q500,768 485,749Q470,730 470,692Q470,650 499,625Q528,600 570,600L640,600Q706,600 753,561.5Q800,523 800,442Q800,321 707.5,240.5Q615,160 488,160Q352,160 256,253Q160,346 160,480Q160,613 253.5,706.5Q347,800 480,800Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_running.xml b/core/res/res/drawable/ic_zen_mode_icon_running.xml
new file mode 100644
index 0000000..472b04e
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_running.xml
@@ -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.
+-->
+<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"
+    android:autoMirrored="true">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M520,920L520,680L436,600L396,776L120,720L136,640L328,680L392,356L320,384L320,520L240,520L240,332L398,264Q433,249 449.5,244.5Q466,240 480,240Q501,240 519,251Q537,262 548,280L588,344Q614,386 658.5,413Q703,440 760,440L760,520Q694,520 636.5,492.5Q579,465 540,420L516,540L600,620L600,920L520,920ZM540,220Q507,220 483.5,196.5Q460,173 460,140Q460,107 483.5,83.5Q507,60 540,60Q573,60 596.5,83.5Q620,107 620,140Q620,173 596.5,196.5Q573,220 540,220Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_shopping_cart.xml b/core/res/res/drawable/ic_zen_mode_icon_shopping_cart.xml
new file mode 100644
index 0000000..92cec4d
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_shopping_cart.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="M280,880Q247,880 223.5,856.5Q200,833 200,800Q200,767 223.5,743.5Q247,720 280,720Q313,720 336.5,743.5Q360,767 360,800Q360,833 336.5,856.5Q313,880 280,880ZM680,880Q647,880 623.5,856.5Q600,833 600,800Q600,767 623.5,743.5Q647,720 680,720Q713,720 736.5,743.5Q760,767 760,800Q760,833 736.5,856.5Q713,880 680,880ZM246,240L342,440L622,440Q622,440 622,440Q622,440 622,440L732,240Q732,240 732,240Q732,240 732,240L246,240ZM208,160L798,160Q821,160 833,180.5Q845,201 834,222L692,478Q681,498 662.5,509Q644,520 622,520L324,520L280,600Q280,600 280,600Q280,600 280,600L760,600L760,680L280,680Q235,680 212,640.5Q189,601 210,562L264,464L120,160L40,160L40,80L170,80L208,160ZM342,440L342,440L622,440Q622,440 622,440Q622,440 622,440L622,440L342,440Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_snowflake.xml b/core/res/res/drawable/ic_zen_mode_icon_snowflake.xml
new file mode 100644
index 0000000..1746e20
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_snowflake.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="M440,880L440,714L310,842L254,786L440,600L440,520L360,520L174,706L118,650L246,520L80,520L80,440L246,440L118,310L174,254L360,440L440,440L440,360L254,174L310,118L440,246L440,80L520,80L520,246L650,118L706,174L520,360L520,440L600,440L786,254L842,310L714,440L880,440L880,520L714,520L842,650L786,706L600,520L520,520L520,600L706,786L650,842L520,714L520,880L440,880Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_speech_bubble.xml b/core/res/res/drawable/ic_zen_mode_icon_speech_bubble.xml
new file mode 100644
index 0000000..4be98ab
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_speech_bubble.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="M80,880L80,160Q80,127 103.5,103.5Q127,80 160,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L240,720L80,880ZM206,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L160,160Q160,160 160,160Q160,160 160,160L160,685L206,640ZM160,640L160,640L160,160Q160,160 160,160Q160,160 160,160L160,160Q160,160 160,160Q160,160 160,160L160,640Q160,640 160,640Q160,640 160,640Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_train.xml b/core/res/res/drawable/ic_zen_mode_icon_train.xml
new file mode 100644
index 0000000..b6f3445
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_train.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="M160,620L160,240Q160,187 187.5,155.5Q215,124 260,107.5Q305,91 362.5,85.5Q420,80 480,80Q546,80 604.5,85.5Q663,91 706.5,107.5Q750,124 775,155.5Q800,187 800,240L800,620Q800,679 759.5,719.5Q719,760 660,760L720,820L720,840L640,840L560,760L400,760L320,840L240,840L240,820L300,760Q241,760 200.5,719.5Q160,679 160,620ZM480,160Q374,160 325,172.5Q276,185 258,200L706,200Q691,183 641.5,171.5Q592,160 480,160ZM240,400L440,400L440,280L240,280L240,400ZM660,480L300,480Q274,480 257,480Q240,480 240,480L240,480L720,480L720,480Q720,480 703,480Q686,480 660,480ZM520,400L720,400L720,280L520,280L520,400ZM340,640Q366,640 383,623Q400,606 400,580Q400,554 383,537Q366,520 340,520Q314,520 297,537Q280,554 280,580Q280,606 297,623Q314,640 340,640ZM620,640Q646,640 663,623Q680,606 680,580Q680,554 663,537Q646,520 620,520Q594,520 577,537Q560,554 560,580Q560,606 577,623Q594,640 620,640ZM300,680L660,680Q686,680 703,663Q720,646 720,620L720,480L240,480L240,620Q240,646 257,663Q274,680 300,680ZM480,200Q592,200 641.5,200Q691,200 706,200L258,200Q276,200 325,200Q374,200 480,200Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_icon_tv.xml b/core/res/res/drawable/ic_zen_mode_icon_tv.xml
new file mode 100644
index 0000000..eaa920a
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_icon_tv.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="M320,840L320,760L160,760Q127,760 103.5,736.5Q80,713 80,680L80,200Q80,167 103.5,143.5Q127,120 160,120L800,120Q833,120 856.5,143.5Q880,167 880,200L880,680Q880,713 856.5,736.5Q833,760 800,760L640,760L640,840L320,840ZM160,680L800,680Q800,680 800,680Q800,680 800,680L800,200Q800,200 800,200Q800,200 800,200L160,200Q160,200 160,200Q160,200 160,200L160,680Q160,680 160,680Q160,680 160,680ZM160,680Q160,680 160,680Q160,680 160,680L160,200Q160,200 160,200Q160,200 160,200L160,200Q160,200 160,200Q160,200 160,200L160,680Q160,680 160,680Q160,680 160,680Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_special_dnd.xml b/core/res/res/drawable/ic_zen_mode_type_special_dnd.xml
new file mode 100644
index 0000000..fee9b0e
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_mode_type_special_dnd.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="M280,520L680,520L680,440L280,440L280,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_mode_type_unknown.xml b/core/res/res/drawable/ic_zen_mode_type_unknown.xml
index c1afd44..04df5f9 100644
--- a/core/res/res/drawable/ic_zen_mode_type_unknown.xml
+++ b/core/res/res/drawable/ic_zen_mode_type_unknown.xml
@@ -21,5 +21,5 @@
     android:viewportWidth="960">
     <path
         android:fillColor="@android:color/white"
-        android:pathData="M480,880Q407,871 335,840.5Q263,810 206.5,753Q150,696 115,609Q80,522 80,400L80,360L120,360Q171,360 225,373Q279,386 326,412Q338,326 380.5,235.5Q423,145 480,80Q537,145 579.5,235.5Q622,326 634,412Q681,386 735,373Q789,360 840,360L880,360L880,400Q880,522 845,609Q810,696 753.5,753Q697,810 625.5,840.5Q554,871 480,880ZM478,798Q467,632 379.5,547Q292,462 162,442Q173,613 263.5,697Q354,781 478,798ZM480,544Q495,522 516.5,498.5Q538,475 558,458Q556,401 535.5,339Q515,277 480,218Q445,277 424.5,339Q404,401 402,458Q422,475 444,498.5Q466,522 480,544ZM558,780Q595,768 635,745Q675,722 709.5,682.5Q744,643 768.5,584Q793,525 798,442Q704,456 633,504.5Q562,553 524,628Q536,660 544.5,698Q553,736 558,780ZM480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544Q480,544 480,544ZM558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780Q558,780 558,780ZM478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798Q478,798 478,798ZM524,628L524,628Q524,628 524,628Q524,628 524,628L524,628L524,628L524,628Q524,628 524,628Q524,628 524,628L524,628Q524,628 524,628Q524,628 524,628ZM480,880L480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Q480,880 480,880Q480,880 480,880L480,880Z" />
+        android:pathData="M368,640L480,556L590,640L548,504L660,416L524,416L480,280L436,416L300,416L410,504L368,640ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,585Q80,574 87,566Q94,558 105,556Q129,548 144.5,527Q160,506 160,480Q160,454 144.5,433Q129,412 105,404Q94,402 87,394Q80,386 80,375L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,375Q880,386 873,394Q866,402 855,404Q831,412 815.5,433Q800,454 800,480Q800,506 815.5,527Q831,548 855,556Q866,558 873,566Q880,574 880,585L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,618Q763,596 741.5,559.5Q720,523 720,480Q720,437 741.5,400.5Q763,364 800,342L800,240Q800,240 800,240Q800,240 800,240L160,240Q160,240 160,240Q160,240 160,240L160,342Q197,364 218.5,400.5Q240,437 240,480Q240,523 218.5,559.5Q197,596 160,618L160,720Q160,720 160,720Q160,720 160,720ZM480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480Q480,480 480,480Q480,480 480,480Z" />
 </vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_zen_priority_modes.xml b/core/res/res/drawable/ic_zen_priority_modes.xml
new file mode 100644
index 0000000..9c72f51
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_priority_modes.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:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M280,520L680,520L680,440L280,440L280,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" />
+</vector>
diff --git a/core/res/res/layout-car/car_resolver_list.xml b/core/res/res/layout-car/car_resolver_list.xml
deleted file mode 100644
index 08c9861..0000000
--- a/core/res/res/layout-car/car_resolver_list.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 2019, The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-<com.android.internal.widget.ResolverDrawerLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/car_activity_resolver_width"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:id="@id/contentPanel">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:background="@drawable/car_activity_resolver_list_background">
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@drawable/car_activity_resolver_list_background"
-            android:orientation="horizontal"
-            android:paddingVertical="@dimen/car_padding_4"
-            android:paddingHorizontal="@dimen/car_padding_4" >
-            <TextView
-                android:id="@+id/profile_button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:visibility="gone" />
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="start"
-                android:textAppearance="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
-        </LinearLayout>
-
-        <FrameLayout
-            android:id="@+id/stub"
-            android:visibility="gone"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
-
-        <TabHost
-            android:id="@+id/profile_tabhost"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_centerHorizontal="true"
-            android:background="?android:attr/colorBackgroundFloating">
-            <LinearLayout
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-                <TabWidget
-                    android:id="@android:id/tabs"
-                    android:visibility="gone"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content">
-                </TabWidget>
-                <View
-                    android:id="@+id/resolver_tab_divider"
-                    android:visibility="gone"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content" />
-                <FrameLayout
-                    android:id="@android:id/tabcontent"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content">
-                    <com.android.internal.app.ResolverViewPager
-                        android:id="@+id/profile_pager"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"/>
-                </FrameLayout>
-            </LinearLayout>
-        </TabHost>
-
-        <LinearLayout
-            android:id="@+id/button_bar"
-            android:visibility="gone"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginVertical="@dimen/car_padding_4"
-            android:layout_marginHorizontal="@dimen/car_padding_4"
-            android:padding="0dp"
-            android:gravity="center"
-            android:background="@drawable/car_activity_resolver_list_background"
-            android:orientation="vertical">
-
-            <Button
-                android:id="@+id/button_once"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/car_button_height"
-                android:enabled="false"
-                android:layout_gravity="center"
-                android:layout_marginBottom="@dimen/car_padding_2"
-                android:text="@string/activity_resolver_use_once"
-                android:onClick="onButtonClick"/>
-
-            <Button
-                android:id="@+id/button_always"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/car_button_height"
-                android:enabled="false"
-                android:layout_gravity="center"
-                android:text="@string/activity_resolver_use_always"
-                android:onClick="onButtonClick"/>
-        </LinearLayout>
-
-    </LinearLayout>
-
-</com.android.internal.widget.ResolverDrawerLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-car/car_resolver_list_with_default.xml b/core/res/res/layout-car/car_resolver_list_with_default.xml
deleted file mode 100644
index 08cc7ff..0000000
--- a/core/res/res/layout-car/car_resolver_list_with_default.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 2019, The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-<com.android.internal.widget.ResolverDrawerLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/car_activity_resolver_width"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:id="@id/contentPanel">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:layout_gravity="center"
-        android:background="@drawable/car_activity_resolver_list_background">
-
-        <FrameLayout
-            android:id="@+id/stub"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:background="@drawable/car_activity_resolver_list_background"/>
-
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:minHeight="@dimen/car_activity_resolver_list_item_height"
-            android:orientation="horizontal">
-
-            <RadioButton
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:focusable="false"
-                android:clickable="false"
-                android:layout_marginStart="?attr/listPreferredItemPaddingStart"
-                android:layout_gravity="start|center_vertical"
-                android:checked="true"/>
-
-            <ImageView
-                android:id="@+id/icon"
-                android:layout_width="@dimen/car_icon_size"
-                android:layout_height="@dimen/car_icon_size"
-                android:layout_gravity="start|center_vertical"
-                android:layout_marginStart="@dimen/car_padding_4"
-                android:src="@drawable/resolver_icon_placeholder"
-                android:scaleType="fitCenter"/>
-
-            <TextView
-                android:id="@+id/title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginHorizontal="?attr/listPreferredItemPaddingStart"
-                style="?android:attr/textAppearanceListItem"
-                android:layout_gravity="start|center_vertical" />
-
-            <LinearLayout
-                android:id="@+id/profile_button"
-                android:visibility="gone"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content">
-
-                <ImageView
-                    android:id="@+id/icon"
-                    android:visibility="gone"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content" />
-
-                <TextView
-                    android:id="@id/text1"
-                    android:visibility="gone"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content" />
-            </LinearLayout>
-        </LinearLayout>
-
-        <TabHost
-            android:id="@+id/profile_tabhost"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_alignParentTop="true"
-            android:layout_centerHorizontal="true"
-            android:background="?attr/colorBackgroundFloating">
-            <LinearLayout
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-                <TabWidget
-                    android:id="@android:id/tabs"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:visibility="gone">
-                </TabWidget>
-                <View
-                    android:id="@+id/resolver_tab_divider"
-                    android:visibility="gone"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content" />
-                <FrameLayout
-                    android:id="@android:id/tabcontent"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content">
-                    <com.android.internal.app.ResolverViewPager
-                        android:id="@+id/profile_pager"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content">
-                    </com.android.internal.app.ResolverViewPager>
-                </FrameLayout>
-            </LinearLayout>
-        </TabHost>
-
-        <LinearLayout
-            android:id="@+id/button_bar"
-            android:visibility="gone"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginVertical="@dimen/car_padding_4"
-            android:layout_marginHorizontal="@dimen/car_padding_4"
-            android:gravity="center"
-            android:background="@drawable/car_activity_resolver_list_background"
-            android:orientation="vertical">
-
-            <Button
-                android:id="@+id/button_once"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/car_button_height"
-                android:enabled="false"
-                android:layout_gravity="center"
-                android:layout_marginBottom="@dimen/car_padding_2"
-                android:text="@string/activity_resolver_use_once"
-                android:onClick="onButtonClick"/>
-
-            <Button
-                android:id="@+id/button_always"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/car_button_height"
-                android:enabled="false"
-                android:layout_gravity="center"
-                android:text="@string/activity_resolver_use_always"
-                android:onClick="onButtonClick"/>
-        </LinearLayout>
-    </LinearLayout>
-
-</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/app_language_picker_current_locale_item.xml b/core/res/res/layout/app_language_picker_current_locale_item.xml
index 01b9cc5..edd6d64 100644
--- a/core/res/res/layout/app_language_picker_current_locale_item.xml
+++ b/core/res/res/layout/app_language_picker_current_locale_item.xml
@@ -18,26 +18,31 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <FrameLayout
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical">
+    <RelativeLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_weight=".8">
+        android:layout_marginEnd="6dip"
+        android:layout_marginTop="6dip"
+        android:layout_marginBottom="6dip"
+        android:layout_weight="1">
         <include
             android:id="@+id/language_picker_item"
             layout="@layout/language_picker_item" />
-    </FrameLayout>
+    </RelativeLayout>
 
     <LinearLayout
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight=".2"
+        android:layout_height="match_parent"
         android:gravity="center"
         android:minHeight="?android:attr/listPreferredItemHeight">
         <ImageView
             android:id="@+id/imageView"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_marginHorizontal="16dp"
             android:src="@drawable/ic_check_24dp"
             app:tint="?attr/colorAccentPrimaryVariant"
             android:contentDescription="@*android:string/checked"/>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 4d5f4f0..027f530 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -37,6 +37,7 @@
       <ListView
           android:id="@+id/autofill_dataset_list"
           android:layout_weight="1"
+          android:fadeScrollbars="false"
           android:layout_width="fill_parent"
           android:layout_height="0dp"
           android:drawSelectorOnTop="true"
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 610a212..058fe3f 100644
--- a/core/res/res/layout/input_method_switch_dialog_new.xml
+++ b/core/res/res/layout/input_method_switch_dialog_new.xml
@@ -71,9 +71,10 @@
             android:layout_height="wrap_content"
             android:background="@drawable/input_method_switch_button"
             android:layout_gravity="end"
-            android:text="@string/input_method_language_settings"
+            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"/>
 
     </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 09ed650..10d938c 100644
--- a/core/res/res/layout/input_method_switch_item_new.xml
+++ b/core/res/res/layout/input_method_switch_item_new.xml
@@ -70,6 +70,7 @@
                 android:ellipsize="marquee"
                 android:singleLine="true"
                 android:fontFamily="google-sans-text"
+                android:textColor="@color/input_method_switch_on_item"
                 android:textAppearance="?attr/textAppearanceListItem"/>
 
         </LinearLayout>
@@ -81,7 +82,7 @@
             android:gravity="center_vertical"
             android:layout_marginStart="12dp"
             android:src="@drawable/ic_check_24dp"
-            android:tint="?attr/materialColorOnSurface"
+            android:tint="@color/input_method_switch_on_item"
             android:visibility="gone"
             android:importantForAccessibility="no"/>
 
diff --git a/core/res/res/layout/language_picker_item.xml b/core/res/res/layout/language_picker_item.xml
index 88012a9..3e55f12 100644
--- a/core/res/res/layout/language_picker_item.xml
+++ b/core/res/res/layout/language_picker_item.xml
@@ -21,7 +21,6 @@
           android:gravity="center_vertical"
           android:minHeight="?android:attr/listPreferredItemHeight"
           android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-          android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
           android:textAppearance="?android:attr/textAppearanceListItem"
           android:layoutDirection="locale"
           android:textDirection="locale"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 043f2b2..a1dea82 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handskrif word nie in wagwoordvelde gesteun nie"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Terug"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Wissel invoermetode"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Maak invoermetodekieser oop"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Instellings"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Bergingspasie word min"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sommige stelselfunksies werk moontlik nie"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"’n App verberg die toestemmingversoek en jou antwoord kan dus nie geverifieer word nie."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op \'n kenmerk om dit te begin gebruik:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kies kenmerke om saam met die toeganklikheidknoppie te gebruik"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kies kenmerke om saam met die volumesleutelkortpad te gebruik"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is afgeskakel"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Wysig kortpaaie"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Outomaties aan satelliet gekoppel"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Jy kan boodskappe stuur en ontvang sonder ’n selfoon- of wi-fi-netwerk"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Gebruik satellietboodskappe?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en ontvang boodskappe sonder ’n selfoon- of wi-fi-netwerk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Maak Boodskappe oop"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe dit werk"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Sakrekenaar"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Jou vingerafdrukke kan nie meer herken word nie. Stel Vingerafdrukslot weer op."</string>
 </resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index c01cb69..2873785 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"የእጅ ጽሑፍ በይለፍ ቃል መስኮች ውስጥ አይደገፍም"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ተመለስ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"የግቤት ስልትን ቀይር"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"የግቤት ስልት መራጭን ክፈት"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ቅንብሮች"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"የማከማቻ ቦታ እያለቀ ነው"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነፃ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"አንድ መተግበሪያ የፍቃድ ጥያቄውን እያደበዘዘ ነው ስለዚህ የእርስዎ ምላሽ ሊረጋገጥ አይችልም።"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"አንድ ባህሪን መጠቀም ለመጀመር መታ ያድርጉት፦"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"በተደራሽነት አዝራር የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"በድምጽ ቁልፍ አቋራጭ የሚጠቀሙባቸው ባሕሪያት ይምረጡ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ጠፍቷል"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"አቋራጮችን አርትዕ ያድርጉ"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ተከናውኗል"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ከሳተላይት ጋር በራስ-ሰር ተገናኝቷል"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ያለ ሞባይል ወይም የWi-Fi አውታረ መረብ መልዕክቶችን መላክ እና መቀበል ይችላሉ"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"የሳተላይት መልዕክት መላላክን ይጠቀማሉ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ያለ ሞባይል ወይም የWi-Fi አውታረ መረብ መልዕክቶችን ይላኩ እና ይቀበሉ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ሒሳብ ማስያ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ካርታዎች"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"መተግበሪያዎች"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ከእንግዲህ የጣት አሻራዎችዎ ሊለዩ አይችሉም። በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ።"</string>
 </resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index b353893..7f02576 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -287,7 +287,7 @@
     <string name="global_action_settings" msgid="4671878836947494217">"الإعدادات"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"مساعدة"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string>
-    <string name="global_action_lockdown" msgid="2475471405907902963">"إلغاء التأمين"</string>
+    <string name="global_action_lockdown" msgid="2475471405907902963">"إلغاء الفتح الذكي"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
     <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"ردّ"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"إشعار جديد"</string>
@@ -301,7 +301,7 @@
     <string name="notification_channel_network_status" msgid="2127687368725272809">"حالة الشبكة"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"تنبيهات الشبكة"</string>
     <string name="notification_channel_network_available" msgid="6083697929214165169">"الشبكة متوفرة"</string>
-    <string name="notification_channel_vpn" msgid="1628529026203808999">"حالة الشبكة الافتراضية الخاصة"</string>
+    <string name="notification_channel_vpn" msgid="1628529026203808999">"‏حالة شبكة VPN"</string>
     <string name="notification_channel_device_admin" msgid="6384932669406095506">"التنبيهات من مشرف تكنولوجيا المعلومات"</string>
     <string name="notification_channel_alerts" msgid="5070241039583668427">"التنبيهات"</string>
     <string name="notification_channel_retail_mode" msgid="3732239154256431213">"عرض توضيحي لبائع التجزئة"</string>
@@ -646,7 +646,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"استخدام المقاييس الحيوية"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"استخدام المقاييس الحيوية أو قفل الشاشة"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"إثبات هويتك"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأكيد هويتك"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"استخدام المقاييس الحيوية للمتابعة"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"استخدام المقاييس الحيوية أو قفل الشاشة للمتابعة"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"معدّات المقاييس الحيوية غير متاحة."</string>
@@ -1198,6 +1198,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"الكتابة بخط اليد غير متاحة في حقول كلمات المرور"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"رجوع"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"تبديل أسلوب الإدخال"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"فتح أداة اختيار أسلوب الإدخال"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"الإعدادات"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"مساحة التخزين منخفضة"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"قد لا تعمل بعض وظائف النظام"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ليست هناك مساحة تخزين كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1355,7 +1357,7 @@
     <item msgid="5520925862115353992">"Wi-Fi"</item>
     <item msgid="1055487873974272842">"بلوتوث"</item>
     <item msgid="1616528372438698248">"إيثرنت"</item>
-    <item msgid="9177085807664964627">"‏شبكة افتراضية خاصة (VPN)"</item>
+    <item msgid="9177085807664964627">"‏شبكة VPN"</item>
   </string-array>
     <string name="network_switch_type_name_unknown" msgid="3665696841646851068">"نوع شبكة غير معروف"</string>
     <string name="accept" msgid="5447154347815825107">"قبول"</string>
@@ -1537,15 +1539,15 @@
     <string name="vr_listener_binding_label" msgid="8013112996671206429">"مستمع واقع افتراضي"</string>
     <string name="condition_provider_service_binding_label" msgid="8490641013951857673">"موفر الحالة"</string>
     <string name="notification_ranker_binding_label" msgid="432708245635563763">"خدمة ترتيب أهمية الإشعارات"</string>
-    <string name="vpn_title" msgid="5906991595291514182">"‏تم تفعيل الشبكة الافتراضية الخاصة (VPN)"</string>
+    <string name="vpn_title" msgid="5906991595291514182">"‏تم تفعيل شبكة VPN"</string>
     <string name="vpn_title_long" msgid="6834144390504619998">"‏تم تفعيل VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
     <string name="vpn_text" msgid="2275388920267251078">"انقر لإدارة الشبكة."</string>
     <string name="vpn_text_long" msgid="278540576806169831">"تم الاتصال بـ <xliff:g id="SESSION">%s</xliff:g>. انقر لإدارة الشبكة."</string>
-    <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"‏جارٍ الاتصال بشبكة افتراضية خاصة (VPN) دائمة التفعيل..."</string>
-    <string name="vpn_lockdown_connected" msgid="2853127976590658469">"‏تم الاتصال بشبكة افتراضية خاصة (VPN) دائمة التفعيل"</string>
-    <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"‏تم قطع الاتصال بالشبكة الافتراضية الخاصة (VPN) التي يتم تشغيلها دائمًا"</string>
+    <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"‏جارٍ الاتصال بشبكة VPN دائمة التفعيل..."</string>
+    <string name="vpn_lockdown_connected" msgid="2853127976590658469">"‏تم الاتصال بشبكة VPN دائمة التفعيل"</string>
+    <string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"‏تم قطع الاتصال بشبكة VPN التي يتم تشغيلها دائمًا"</string>
     <string name="vpn_lockdown_error" msgid="4453048646854247947">"‏تعذّر الاتصال بشبكة VPN التي يتم تشغيلها دائمًا."</string>
-    <string name="vpn_lockdown_config" msgid="8331697329868252169">"‏تغيير إعدادات الشبكة أو الشبكة الافتراضية الخاصة (VPN)"</string>
+    <string name="vpn_lockdown_config" msgid="8331697329868252169">"‏تغيير إعدادات الشبكة أو شبكة VPN"</string>
     <string name="upload_file" msgid="8651942222301634271">"اختيار ملف"</string>
     <string name="no_file_chosen" msgid="4146295695162318057">"لم يتم اختيار أي ملف"</string>
     <string name="reset" msgid="3865826612628171429">"إعادة الضبط"</string>
@@ -1748,7 +1750,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"تعذَّر التحقّق من ردّك بسبب حجب أحد التطبيقات طلب الحصول على الإذن."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"انقر على ميزة لبدء استخدامها:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"اختيار الميزات التي تريد استخدامها مع زر أدوات تمكين الوصول"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"اختيار الميزات التي تريد استخدامها مع اختصار مفتاح التحكّم في مستوى الصوت"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"تم إيقاف <xliff:g id="SERVICE_NAME">%s</xliff:g>."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"تعديل الاختصارات"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"تم"</string>
@@ -2426,6 +2429,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"تم الاتصال تلقائيًا بالقمر الصناعي"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"‏يمكنك إرسال الرسائل واستلامها بدون شبكة الجوّال أو شبكة Wi-Fi."</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"هل تريد المراسلة عبر القمر الصناعي؟"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏يمكنك إرسال الرسائل واستلامها بدون شبكة الجوّال أو شبكة Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
@@ -2451,4 +2456,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"الآلة الحاسبة"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"‏خرائط Google"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"التطبيقات"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"لم يعد بالإمكان التعرّف على بصمات أصابعك. يجب ضبط ميزة \"فتح الجهاز ببصمة الإصبع\" مجددًا."</string>
 </resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 4ddef5f..bc1dba27 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"পাছৱৰ্ড ক্ষেত্ৰসমূহত হস্তলিপি সমৰ্থিত নহয়"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"উভতি যাওক"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ইনপুটৰ পদ্ধতি সলনি কৰক"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ইনপুট পদ্ধতি বাছনিকর্তা খোলক"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ছেটিং"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ষ্ট’ৰেজৰ খালী ঠাই শেষ হৈ আছে"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ছিষ্টেমৰ কিছুমান কাৰ্যকলাপে কাম নকৰিবও পাৰে"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ছিষ্টেমৰ বাবে পৰ্যাপ্ত খালী ঠাই নাই। আপোনাৰ ২৫০এমবি খালী ঠাই থকাটো নিশ্চিত কৰক আৰু ৰিষ্টাৰ্ট কৰক।"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"এটা এপে অনুমতিৰ অনুৰোধটো অস্পষ্ট কৰি আছে আৰু সেয়েহে আপোনাৰ সঁহাৰিটো সত্যাপন কৰিব নোৱাৰি।"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনো এটা সুবিধা ব্যৱহাৰ কৰিবলৈ সেইটোত টিপক:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"সাধ্য-সুবিধা বুটামটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কীৰ শ্বৰ্টকাটটোৰ জৰিয়তে ব্যৱহাৰ কৰিবলৈ সুবিধাসমূহ বাছনি কৰক"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> বন্ধ কৰা হৈছে"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"শ্বৰ্টকাটসমূহ সম্পাদনা কৰক"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"কৰা হ’ল"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"উপগ্ৰহৰ সৈতে স্বয়ংক্ৰিয়ভাৱে সংযুক্ত হৈছে"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"আপুনি ম’বাইল বা ৱাই-ফাই নেটৱৰ্কৰ জৰিয়তে পাঠ বাৰ্তা পঠিয়াব বা লাভ কৰিব পাৰে"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"উপগ্ৰহৰ জৰিয়তে বাৰ্তা বিনিময়ৰ সুবিধাটো ব্যৱহাৰ কৰিবনে?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"আপুনি কোনো ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নোহোৱাকৈ বাৰ্তা পঠিয়াওক আৰু লাভ কৰক"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"কেলকুলেটৰ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"মেপ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"এপ্লিকেশ্বন"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"আপোনাৰ ফিংগাৰপ্ৰিণ্ট আৰু চিনাক্ত কৰিব নোৱাৰি। ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক।"</string>
 </resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 86c4c35..e93a57b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Parol sahələrində əlyazma dəstəklənmir"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Geriyə"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Daxiletmə metodunu dəyişdirin"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Daxiletmə metodu seçicisini açın"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ayarlar"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Yaddaş yeri bitir"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bəzi sistem funksiyaları işləməyə bilər"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Bir tətbiq icazə sorğusunu gizlətdiyi üçün cavabı yoxlamaq mümkün deyil."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> deaktiv edilib"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Qısayolları redaktə edin"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hazırdır"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Tətbiq kontenti güvənlik məsələlərinə görə ekran paylaşımından gizlədildi"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Peykə avtomatik qoşulub"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil və ya Wi-Fi şəbəkəsi olmadan mesaj göndərə və qəbul edə bilərsiniz"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Peyk mesajlaşmasından istifadə edilsin?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil və ya Wi-Fi şəbəkəsi olmadan mesajlar göndərin və qəbul edin"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulyator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Xəritə"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Tətbiqlər"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Barmaq izlərinizi artıq tanımaq mümkün deyil. Barmaqla Kilidaçmanı yenidən ayarlayın."</string>
 </resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 33a92e0..b83eca5 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -643,7 +643,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Koristite biometriju"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Koristite biometriju ili otključavanje ekrana"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite svoj identitet"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potvrdite identitet"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Koristite biometrijski podatak da biste nastavili"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Koristite biometrijski podatak ili otključavanje ekrana da biste nastavili"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrijski hardver nije dostupan"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Pisanje rukom nije podržano u poljima za lozinke"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazad"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promenite metod unosa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvori birač metoda unosa"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Podešavanja"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memorijski prostor je na izmaku"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda ne funkcionišu"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija krije zahtev za dozvolu, pa odgovor ne može da se verifikuje."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite neku funkciju da biste počeli da je koristite:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti sa dugmetom Pristupačnost"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije za prečicu tasterom jačine zvuka"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Izmenite prečice"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatski povezano sa satelitom"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Možete da šaljete i primate poruke bez mobilne ili WiFi mreže"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Želite da koristite satelitsku razmenu poruka?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mape"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Otisci prstiju više ne mogu da se prepoznaju. Ponovo podesite otključavanje otiskom prsta."</string>
 </resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index cd040c8..59645706 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -168,7 +168,7 @@
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Налады сеткавай бяспекі"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"Даведацца больш"</string>
     <string name="scNullCipherIssueActionGotIt" msgid="8747796640866585787">"ОК"</string>
-    <string name="fcComplete" msgid="1080909484660507044">"Код аб\'екта завершаны."</string>
+    <string name="fcComplete" msgid="1080909484660507044">"Код аб’екта завершаны."</string>
     <string name="fcError" msgid="5325116502080221346">"Праблема падлучэння ці няправільны код функцыі."</string>
     <string name="httpErrorOk" msgid="6206751415788256357">"ОК"</string>
     <string name="httpError" msgid="3406003584150566720">"Адбылася памылка сеткі."</string>
@@ -420,15 +420,15 @@
     <string name="permlab_runInBackground" msgid="541863968571682785">"працаваць у фоне"</string>
     <string name="permdesc_runInBackground" msgid="4344539472115495141">"Гэта праграма можа працаваць у фоне. Гэта можа прывесці да хутчэйшага спажывання зараду акумулятара."</string>
     <string name="permlab_useDataInBackground" msgid="783415807623038947">"выкарыстоўваць даныя ў фоне"</string>
-    <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Гэта праграма можа выкарыстоўваць даныя ў фоне. Гэта можа прывесці да павелічэння аб\'ёму трафіка."</string>
+    <string name="permdesc_useDataInBackground" msgid="1230753883865891987">"Гэта праграма можа выкарыстоўваць даныя ў фоне. Гэта можа прывесці да павелічэння аб’ёму трафіка."</string>
     <string name="permlab_schedule_exact_alarm" msgid="6683283918033029730">"Наладжванне выканання дзеянняў у дакладны час"</string>
     <string name="permdesc_schedule_exact_alarm" msgid="8198009212013211497">"Гэта праграма зможа наладжваць выкананне задач у патрэбны час у будучыні і будзе працаваць, нават калі вы не выкарыстоўваеце прыладу."</string>
     <string name="permlab_use_exact_alarm" msgid="348045139777131552">"Уключэнне будзільніка ці паказ напамінаў пра падзею"</string>
     <string name="permdesc_use_exact_alarm" msgid="7033761461886938912">"Гэта праграма зможа наладжваць выкананне такіх дзеянняў, як уключэнне будзільніка ці паказ напамінаў."</string>
     <string name="permlab_persistentActivity" msgid="464970041740567970">"прымусіць прыкладанне працаваць заўсёды"</string>
-    <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта."</string>
-    <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дазваляе праграме пастаянна захоўваць некаторыя свае часткі ў памяці прылады. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых праграм, і запаволіць працу прылады Android TV."</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб\'ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона."</string>
+    <string name="permdesc_persistentActivity" product="tablet" msgid="6055271149187369916">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб’ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу планшэта."</string>
+    <string name="permdesc_persistentActivity" product="tv" msgid="6800526387664131321">"Дазваляе праграме пастаянна захоўваць некаторыя свае часткі ў памяці прылады. Гэта можа абмежаваць аб’ём памяці, даступнай для іншых праграм, і запаволіць працу прылады Android TV."</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Дазваляе прыкладанню захоўваць некаторыя пастаянныя часткi ў памяцi. Гэта можа абмежаваць аб’ём памяці, даступнай для іншых прыкладанняў, i запаволiць працу тэлефона."</string>
     <string name="permlab_foregroundService" msgid="1768855976818467491">"запусціць актыўныя сэрвісы"</string>
     <string name="permdesc_foregroundService" msgid="8720071450020922795">"Дазваляе праграме выкарыстоўваць асноўныя сэрвісы."</string>
     <string name="permlab_foregroundServiceCamera" msgid="7814751737955715297">"запуск актыўнага сэрвісу тыпу \"camera\""</string>
@@ -460,7 +460,7 @@
     <string name="permlab_foregroundServiceSpecialUse" msgid="7973536745876645082">"запуск актыўнага сэрвісу тыпу \"specialUse\""</string>
     <string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"Дазваляе праграме выкарыстоўваць актыўныя сэрвісы тыпу \"specialUse\""</string>
     <string name="permlab_getPackageSize" msgid="375391550792886641">"вымерыць прастору для захоўвання прыкладання"</string>
-    <string name="permdesc_getPackageSize" msgid="742743530909966782">"Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб\'ём кэш-памяці"</string>
+    <string name="permdesc_getPackageSize" msgid="742743530909966782">"Дазваляе прыкладанням атрымліваць яго код, дадзеныя і аб’ём кэш-памяці"</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"змена сістэмных налад"</string>
     <string name="permdesc_writeSettings" msgid="8293047411196067188">"Дазваляе прыкладаннем змяняць дадзеныя налад сістэмы. Шкоднасныя прыкладанні могуць пашкодзіць канфігурацыю вашай сістэмы."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"запуск пры загрузцы сістэмы"</string>
@@ -505,7 +505,7 @@
     <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Падчас выкарыстання гэта праграма можа атрымліваць ад службаў геалакацыі звесткі пра ваша прыблізнае месцазнаходжанне. Каб праграма мела такія звесткі, на вашай прыладзе павінны быць уключаны службы геалакацыі."</string>
     <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"доступ да вызначэння месцазнаходжання ў фонавым рэжыме"</string>
     <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Гэта праграма можа мець доступ да даных пра месцазнаходжанне ў любы час, нават калі яна не выкарыстоўваецца."</string>
-    <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"змяняць налады аудыё"</string>
+    <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"змяняць налады аўдыя"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Дазваляе прыкладанням змяняць глабальныя налады гуку, такія як моц і тое, што дынамік выкарыстоўваецца для выхаду."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"запіс аўдыя"</string>
     <string name="permdesc_recordAudio" msgid="5857246765327514062">"Гэта праграма падчас яе выкарыстання можа запісваць аўдыя з дапамогай мікрафона."</string>
@@ -621,7 +621,7 @@
     <string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"Трасанкцыі з выкарыстаннем ахоўнага элемента"</string>
     <string name="permdesc_nfcTransactionEvent" msgid="1904286701876487397">"Праграма зможа атрымліваць інфармацыю пра трансакцыі, якія адбываюцца з выкарыстаннем ахоўнага элемента."</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"адключэнне блакiроўкi экрана"</string>
-    <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з\'яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны."</string>
+    <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з’яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны."</string>
     <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"запытваць узровень складанасці блакіроўкі экрана"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дазваляе праграме вызначаць узровень складанасці блакіроўкі экрана (высокі, сярэдні, нізкі ці нулявы), які залежыць ад даўжыні пароля і ад тыпу блакіроўкі экрана. Праграма можа прапанаваць карыстальнікам ускладніць блакіроўку экрана, аднак гэту прапанову можна ігнараваць. Заўважце, што праграма не можа ведаць тып і пароль блакіроўкі экрана, таму што яны захоўваюцца ў зашыфраваным выглядзе."</string>
     <string name="permlab_postNotification" msgid="4875401198597803658">"паказваць апавяшчэнні"</string>
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Палі для ўказання пароля не падтрымліваюць рукапісны ўвод"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Пераключэнне рэжыму ўводу"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Выбраць спосаб уводу"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Налады"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Месца для захавання на зыходзе"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1436,7 +1438,7 @@
     <string name="select_keyboard_layout_notification_title" msgid="5823199895322205589">"Наладзьце прыладу \"<xliff:g id="DEVICE_NAME">%s</xliff:g>\""</string>
     <string name="select_multiple_keyboards_layout_notification_title" msgid="6999491025126641938">"Наладзьце фізічныя клавіятуры"</string>
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Дакраніцеся, каб выбраць мову і раскладку"</string>
-    <string name="fast_scroll_alphabet" msgid="8854435958703888376">" АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШ\'ЫЬЭЮЯ"</string>
+    <string name="fast_scroll_alphabet" msgid="8854435958703888376">" АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШ’ЫЬЭЮЯ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Паказваць паверх іншых праграм"</string>
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> паказваецца паверх іншых праграм"</string>
@@ -1729,7 +1731,7 @@
     <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае спецыяльныя магчымасці. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nБягучыя функцыі:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВыбраныя функцыі можна змяніць у меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
     <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
     <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Уключыць хуткі доступ да сэрвісу \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
-    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\", якая з\'яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
+    <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\", якая з’яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады &gt; Спецыяльныя магчымасці\"."</string>
     <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Уключыць"</string>
     <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не ўключаць"</string>
     <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"УКЛЮЧАНА"</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Праграма хавае запыт дазволу, таму ваш адказ немагчыма спраўдзіць."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Каб пачаць выкарыстоўваць функцыю, націсніце на яе:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберыце функцыі, якія будзеце выкарыстоўваць з кнопкай спецыяльных магчымасцей"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберыце функцыі для выкарыстання з клавішай гучнасці"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сэрвіс \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" выключаны"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змяніць ярлыкі"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Гатова"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Аўтаматычна падключана да сістэм спадарожнікавай сувязі"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Вы можаце адпраўляць і атрымліваць паведамленні без доступу да мабільнай сеткі або Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Выкарыстоўваць абмен паведамленнямі па спадарожнікавай сувязі?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Вы можаце адпраўляць і атрымліваць паведамленні, калі падключэнне да мабільнай сеткі або сеткі Wi-Fi адсутнічае"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятар"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карты"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Праграмы"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Вашы адбіткі пальцаў больш не распазнаюцца. Паўторна наладзьце разблакіроўку адбіткам пальца."</string>
 </resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0fe1067..b6fad9e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"В полета за парола не се поддържа ръкописно въвеждане"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Превключване на метода на въвеждане"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отваряне на инструмента за избор на метод на въвеждане"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Настройки"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Мястото в хранилището е на изчерпване"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Възможно е някои функции на системата да не работят"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Отговорът ви не може да бъде потвърден, тъй като приложение прикрива заявката за разрешение."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Докоснете дадена функция, за да започнете да я използвате:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Избиране на функции, които да използвате с бутона за достъпност"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Избиране на функции, които да използвате с прекия път чрез бутона за силата на звука"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Изключихте <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Редактиране на преките пътища"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Автоматично установена връзка със сателит"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Можете да изпращате и получавате съобщения без мобилна или Wi-Fi мрежа"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Да се използват ли сателитни съобщения?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Изпращайте и получавайте съобщения без мобилна или Wi-Fi мрежа"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Отпечатъците ви вече не могат да бъдат разпознати. Настройте отново „Отключване с отпечатък“."</string>
 </resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b32cf2d..c434fe9 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -203,8 +203,8 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ব্যক্তিগত কাজের জন্য অ্যাডমিন এই ডিভাইস ব্যবহার করার অনুমতি দেয়নি"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"প্রাইভেট স্পেস সরিয়ে দেওয়া হয়েছে"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"এই ম্যানেজ করা ডিভাইসে আপনার সংস্থা প্রাইভেট স্পেসের অনুমতি দেয় না।"</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"ডিভাইসটি ম্যানেজ করা হচ্ছে"</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"আপনার প্রতিষ্ঠান এই ডিভাইসটি ম্যানেজ করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"অ্যাপগুলি আপনার লোকেশন অ্যাক্সেস করতে পারবে"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"আরও জানতে আইটি অ্যাডমিনের সাথে যোগাযোগ করুন"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"জিওফেন্সিং সার্ভিস"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"পাসওয়ার্ডের ফিল্ডে হাতের লেখা কাজ করে না"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ফিরে যান"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ইনপুট পদ্ধতি পাল্টান"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ইনপুট পদ্ধতির পিকার খুলুন"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"সেটিংস"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"কোনও অ্যাপ অনুমতির অনুরোধ আড়াল করছে তাই আপনার উত্তর যাচাই করা যাবে না।"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"কোনও ফিচার ব্যবহার করা শুরু করতে, সেটিতে ট্যাপ করুন:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"অ্যাক্সেসিবিলিটি বোতামের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ভলিউম কী শর্টকাটের সাহায্যে আপনি যেসব ফিচার ব্যবহার করতে চান সেগুলি বেছে নিন"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> বন্ধ করে দেওয়া হয়েছে"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"শর্টকাট এডিট করুন"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"হয়ে গেছে"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"স্যাটেলাইটের সাথে অটোমেটিক কানেক্ট করা হয়েছে"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"আপনি কোনও মেবাইল বা ওয়াই-ফাই নেটওয়ার্ক ছাড়াই মেসেজ পাঠাতে ও পেতে পারবেন"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"\'স্যাটেলাইট মেসেজিং\' ব্যবহার করবেন?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"কোনও মেবাইল বা ওয়াই-ফাই নেটওয়ার্ক ছাড়াই মেসেজ পাঠান ও রিসিভ করুন"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ক্যালকুলেটর"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ম্যাপ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"অ্যাপ্লিকেশন"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"আপনার ফিঙ্গারপ্রিন্ট আর শনাক্ত করা যাবে না। \'ফিঙ্গারপ্রিন্ট আনলক\' ফিচার আবার সেট-আপ করুন।"</string>
 </resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c950d7c..91c7701 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -204,7 +204,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizacija ne dozvoljava privatne prostore na ovom uređaju kojim se upravlja."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja."</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Za više informacija kontaktirajte IT administratora"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Rukopis nije podržan u poljima za lozinku"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazad"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promjena načina unosa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvaranje birača načina unosa"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Postavke"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke funkcije sistema možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija skriva zahtjev za odobrenje, pa se vaš odgovor ne može potvrditi."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite funkciju da je počnete koristiti:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odaberite funkcije koje ćete koristiti s dugmetom Pristupačnost"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odaberite funkcije koje ćete koristiti pomoću prečice tipke za jačinu zvuka"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredi prečice"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatski je povezano sa satelitom"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Možete slati i primati poruke bez mobilne ili WiFi mreže"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Koristiti satelitsku razmjenu poruka?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke bez mobilne ili WiFi mreže"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mape"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaši otisci prstiju se više ne mogu prepoznavati. Ponovo postavite otključavanje otiskom prsta."</string>
 </resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index bf9540c..cbe9c39 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"L\'escriptura a mà no s\'admet als camps de contrasenya"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Enrere"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Canvia el mètode d\'introducció de text"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Obre el selector de mètode d\'introducció"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuració"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"És possible que algunes funcions del sistema no funcionin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una aplicació està ocultant la sol·licitud de permís, de manera que la teva resposta no es pot verificar"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una funció per començar a utilitzar-la:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Tria les funcions que vols utilitzar amb el botó d\'accessibilitat"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tria les funcions que vols utilitzar amb la drecera per a tecles de volum"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> s\'ha desactivat"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edita les dreceres"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fet"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per seguretat"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"S\'ha connectat automàticament a un satèl·lit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Pots enviar i rebre missatges sense una xarxa mòbil o Wi‑Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Vols utilitzar els missatges per satèl·lit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envia i rep missatges sense una xarxa mòbil o Wi‑Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacions"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Les teves empremtes digitals ja no es poden reconèixer. Torna a configurar Desbloqueig amb empremta digital."</string>
 </resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 139058f..b9ceede 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"V polích pro hesla není psaní rukou podporováno"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Zpět"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Přepnout metodu zadávání"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otevřít výběr metody zadávání"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavení"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"V úložišti je málo místa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Některé systémové funkce nemusí fungovat"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Žádost o oprávnění skrývá nějaká aplikace, proto vaši odpověď nelze ověřit."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Chcete-li některou funkci začít používat, klepněte na ni:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vyberte funkce, které budete používat s tlačítkem přístupnosti"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vyberte funkce, které budete používat se zkratkou tlačítka hlasitosti"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Služba <xliff:g id="SERVICE_NAME">%s</xliff:g> byla vypnuta"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Upravit zkratky"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hotovo"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automaticky připojeno k satelitu"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Zprávy můžete odesílat a přijímat bez mobilní sítě nebo sítě Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Použít satelitní zprávy?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odesílejte a přijímejte zprávy bez mobilní sítě nebo Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otevřít Zprávy"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulačka"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapy"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikace"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaše otisky prstů se nedaří rozpoznat. Nastavte odemknutí otiskem prstu znovu."</string>
 </resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8ec7e22..d66ebd90 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Håndskrift understøttes ikke for adgangskodefelter"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tilbage"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Skift indtastningsmetode"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Åbn indtastningsmetodevælgeren"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Indstillinger"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der er snart ikke mere lagerplads"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nogle systemfunktioner virker måske ikke"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app skjuler anmodningen om tilladelse, så dit svar kan ikke verificeres."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryk på en funktion for at bruge den:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Vælg, hvilke funktioner du vil bruge med knappen til hjælpefunktioner"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Vælg de funktioner, du vil bruge via lydstyrkeknapperne"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> er blevet deaktiveret"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Rediger genveje"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Udfør"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Der blev automatisk oprettet forbindelse til satellit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan sende og modtage beskeder uden et mobil- eller Wi-Fi-netværk"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Vil du bruge satellitbeskeder?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og modtag beskeder uden et mobil- eller Wi-Fi-netværk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Åbn Beskeder"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Sådan fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Lommeregner"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kort"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Dine fingeraftryk kan ikke længere genkendes. Konfigurer fingeroplåsning igen."</string>
 </resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a7ae278..3879767 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -345,7 +345,7 @@
     <string name="permgroupdesc_phone" msgid="270048070781478204">"Telefonanrufe tätigen und verwalten"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Körpersensoren"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"auf Sensordaten zu deinen Vitaldaten zugreifen"</string>
-    <string name="permgrouplab_notifications" msgid="5472972361980668884">"Benachrichtigun­gen"</string>
+    <string name="permgrouplab_notifications" msgid="5472972361980668884">"Benachrichti­gun­gen"</string>
     <string name="permgroupdesc_notifications" msgid="4608679556801506580">"Benachrichtigungen anzeigen"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Fensterinhalte abrufen"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Die Inhalte eines Fensters, mit dem du interagierst, werden abgerufen."</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handschrift wird in Passwortfeldern nicht unterstützt"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Zurück"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Eingabemethode wechseln"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Auswahl für die Eingabemethode öffnen"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Einstellungen"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Der Speicherplatz wird knapp"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Einige Systemfunktionen funktionieren eventuell nicht."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Die Berechtigungsanfrage wird durch eine andere App verdeckt. Daher kann deine Antwort nicht geprüft werden."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Zum Auswählen der gewünschten Funktion tippen:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funktionen auswählen, die du mit der Schaltfläche \"Bedienungshilfen\" verwenden möchtest"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funktionen für Verknüpfung mit Lautstärketaste auswählen"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> wurde deaktiviert"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kurzbefehle bearbeiten"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fertig"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aus Sicherheitsgründen werden bei der Bildschirmfreigabe App-Inhalte ausgeblendet"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatisch mit Satellit verbunden"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Du kannst Nachrichten ohne Mobilfunknetz oder WLAN senden und empfangen"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Nachrichten per Satellit verwenden?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Du kannst ohne Mobilgerät oder WLAN Nachrichten senden und empfangen"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Rechner"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Anwendungen"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Deine Fingerabdrücke können nicht mehr erkannt werden. Bitte richte die Entsperrung per Fingerabdruck neu ein."</string>
 </resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index a6a3f6f..28a5e55 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1029,7 +1029,7 @@
     <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Έχετε πληκτρολογήσει τον αριθμό σας PIN εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το tablet σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
-    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στο Google.\n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
+    <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα σας ζητηθεί να ξεκλειδώσετε τη συσκευή σας Android TV χρησιμοποιώντας τα στοιχεία σύνδεσής σας στην Google.\n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση της σύνδεσής σας Google.\n\n Προσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%3$d</xliff:g> δευτερόλεπτα."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Προσπαθήσατε να ξεκλειδώσετε εσφαλμένα το tablet <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%2$d</xliff:g> προσπάθειες, το tablet θα επαναφερθεί στις εργοστασιακές ρυθμίσεις και όλα τα δεδομένα χρήστη θα χαθούν."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Δοκιμάσατε να ξεκλειδώσετε τη συσκευή Android TV <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές χωρίς επιτυχία. Μετά από ακόμα <xliff:g id="NUMBER_1">%2$d</xliff:g> ανεπιτυχείς προσπάθειες, θα γίνει επαναφορά των προεπιλεγμένων εργοστασιακών ρυθμίσεων στη συσκευή σας Android TV και όλα τα δεδομένα χρήστη θα χαθούν."</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Η γραφή δεν υποστηρίζεται στα πεδία κωδικού πρόσβασης"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Πίσω"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Εναλλαγή μεθόδου εισαγωγής"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Άνοιγμα εργαλείου επιλογής μεθόδου εισαγωγής"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ρυθμίσεις"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Μια εφαρμογή αποκρύπτει το αίτημα άδειας, με αποτέλεσμα να μην είναι δυνατή η επαλήθευση της απάντησής σας."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Πατήστε μια λειτουργία για να ξεκινήσετε να τη χρησιμοποιείτε:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με το κουμπί προσβασιμότητας."</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Επιλέξτε τις λειτουργίες που θέλετε να χρησιμοποιείτε με τη συντόμευση κουμπιού έντασης ήχου"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Η υπηρεσία <xliff:g id="SERVICE_NAME">%s</xliff:g> έχει απενεργοποιηθεί."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Επεξεργασία συντομεύσεων"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Τέλος"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Συνδέθηκε αυτόματα με δορυφόρο"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Μπορείτε να στέλνετε και να λαμβάνετε μηνύματα χωρίς δίκτυο κινητής τηλεφωνίας ή Wi-Fi."</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Χρήση της ανταλλαγής μηνυμάτων μέσω δορυφόρου;"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Αποστολή και λήψη μηνυμάτων χωρίς δίκτυο κινητής τηλεφωνίας ή Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Αριθμομηχανή"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Χάρτες"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Εφαρμογές"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Δεν είναι δυνατή πλέον η αναγνώριση των δακτυλικών αποτυπωμάτων σας. Ρυθμίστε ξανά τη λειτουργία Ξεκλείδωμα με δακτυλικό αποτύπωμα."</string>
 </resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a58b262..70d86e7 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handwriting is not supported in password fields"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Use satellite messaging?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
 </resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 656d177..19547f8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handwriting is not supported in password fields"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the accessibility button"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Auto connected to satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Use satellite messaging?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognized. Set up Fingerprint Unlock again."</string>
 </resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 234eb7b..d3f0c64 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handwriting is not supported in password fields"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Use satellite messaging?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
 </resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8c2a92e..7c3be15 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handwriting is not supported in password fields"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Back"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Switch input method"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Open input method picker"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Settings"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"An app is obscuring the permission request so your response cannot be verified."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tap a feature to start using it:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choose features to use with the Accessibility button"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choose features to use with the volume key shortcut"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> has been turned off"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit shortcuts"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Done"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Auto-connected to satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"You can send and receive messages without a mobile or Wi-Fi network"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Use satellite messaging?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send and receive messages without a mobile or Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Your fingerprints can no longer be recognised. Set up Fingerprint Unlock again."</string>
 </resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index c59c194..c1eade1 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎Handwriting is not supported in password fields‎‏‎‎‏‎"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎Back‎‏‎‎‏‎"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎Switch input method‎‏‎‎‏‎"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎Open input method picker‎‏‎‎‏‎"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎Settings‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎Storage space running out‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‎Some system functions may not work‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎Not enough storage for the system. Make sure you have 250MB of free space and restart.‎‏‎‎‏‎"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‎An app is obscuring the permission request so your response cannot be verified.‎‏‎‎‏‎"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎Tap a feature to start using it:‎‏‎‎‏‎"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎Choose features to use with the accessibility button‎‏‎‎‏‎"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎Choose features to use with the volume key shortcut‎‏‎‎‏‎"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="SERVICE_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎ has been turned off‎‏‎‎‏‎"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‏‎Edit shortcuts‎‏‎‎‏‎"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎Done‎‏‎‎‏‎"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‎App content hidden from screen share for security‎‏‎‎‏‎"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‎‏‏‎‎Auto connected to satellite‎‏‎‎‏‎"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎You can send and receive messages without a mobile or Wi-Fi network‎‏‎‎‏‎"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎Use satellite messaging?‎‏‎‎‏‎"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎Send and receive messages without a mobile or Wi-Fi network‎‏‎‎‏‎"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎Open Messages‎‏‎‎‏‎"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎How it works‎‏‎‎‏‎"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎Pending...‎‏‎‎‏‎"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎Calculator‎‏‎‎‏‎"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‏‏‏‎Maps‎‏‎‎‏‎"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‎Applications‎‏‎‎‏‎"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎Your fingerprints can no longer be recognized. Set up Fingerprint Unlock again.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3235b0e..773edd3 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La Escritura a mano no está disponible en campos de contraseñas"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de entrada"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir selector de método de entrada"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuración"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio de almacenamiento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no estén disponibles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una app está cubriendo la solicitud de permiso, por lo que no se puede verificar tu respuesta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Presiona una función para comenzar a usarla:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona las funciones a utilizar con el botón de accesibilidad"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona las funciones a usar con las teclas de volumen"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se desactivó <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Listo"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Conexión automática a satélite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Puedes enviar y recibir mensajes incluso si no tienes conexión a una red móvil o Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"¿Quieres usar la mensajería satelital?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ya no se pueden reconocer tus huellas dactilares. Vuelve a configurar el Desbloqueo con huellas dactilares."</string>
 </resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 4fa7d72..ff7d912 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La escritura a mano no está disponible en campos de contraseña"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de introducción de texto"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir selector de método de introducción"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ajustes"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Es posible que algunas funciones del sistema no funcionen."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Una aplicación está ocultando la solicitud de permiso, por lo que no se puede verificar tu respuesta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toca una función para empezar a usarla:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Selecciona qué funciones usar con el botón de accesibilidad"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Selecciona qué funciones usar con la tecla de volumen"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Se ha desactivado <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar accesos directos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hecho"</string>
@@ -2390,13 +2393,13 @@
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"No se puede proyectar a la pantalla"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"Usa otro cable y vuelve a intentarlo"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"Tu dispositivo está demasiado caliente y no puede proyectar a la pantalla hasta que se enfríe"</string>
-    <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Dual Screen"</string>
-    <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La función Dual Screen está activada"</string>
+    <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string>
+    <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La función Pantalla dual está activada"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando ambas pantallas para mostrar contenido"</string>
     <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"El dispositivo está demasiado caliente"</string>
-    <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Dual Screen no está disponible porque el teléfono se está calentando demasiado"</string>
-    <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Dual Screen no está disponible"</string>
-    <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Dual Screen no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."</string>
+    <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Pantalla dual no está disponible porque el teléfono se está calentando demasiado"</string>
+    <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"Pantalla dual no está disponible"</string>
+    <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Pantalla dual no está disponible porque la función Ahorro de batería está activada. Puedes desactivarla en Ajustes."</string>
     <string name="device_state_notification_settings_button" msgid="691937505741872749">"Ir a Ajustes"</string>
     <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Desactivar"</string>
     <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> configurado"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por seguridad"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automáticamente al satélite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Puedes enviar y recibir mensajes sin una red móvil o Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"¿Usar mensajes por satélite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía y recibe mensajes sin una red móvil ni Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicaciones"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Tus huellas digitales ya no pueden reconocerse. Vuelve a configurar Desbloqueo con huella digital."</string>
 </resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index cfcab8c..f313fb2 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Käsitsi kirjutamine ei ole parooliväljadel toetatud"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tagasi"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Sisestusmeetodi vahetamine"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Sisestusmeetodi valija avamine"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Seaded"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Talletusruum saab täis"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Rakendus varjab loataotlust, nii et teie vastust ei saa kinnitada."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Puudutage funktsiooni, et selle kasutamist alustada."</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valige funktsioonid, mida juurdepääsetavuse nupuga kasutada"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valige helitugevuse nupu otsetee funktsioonid"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> on välja lülitatud"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Muuda otseteid"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Valmis"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamisel turvalisuse huvides peidetud"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Satelliidiga loodi automaatselt ühendus"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Teil on võimalik sõnumeid saata ja vastu võtta ilma mobiilside- ja WiFi-võrguta"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Kas soovite kasutada satelliidipõhist sõnumsidet?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sõnumite saatmine ja vastuvõtmine ilma mobiilside- või WiFi-võrguta"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ava rakendus Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Tööpõhimõtted"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulaator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kaardid"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Rakendused"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Teie sõrmejälgi ei saa enam tuvastada. Seadistage sõrmejäljega avamine uuesti."</string>
 </resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 43ab92c..b0775d0 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -203,7 +203,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Erabilera pertsonalerako utzi du gailua administratzaileak"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Kendu egin da eremu pribatua"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Zure erakundeak ez ditu onartzen eremu pribatuak kudeatutako gailu honetan."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"Jabeak kudeatzen du gailua"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"Gailua kudeatuta dago"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Erakundeak kudeatzen du gailua eta baliteke sareko trafikoa gainbegiratzea. Sakatu hau xehetasunak ikusteko."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikazioek zure kokapena atzi dezakete"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Informazio gehiago lortzeko, jo IKT saileko administratzailearengana"</string>
@@ -252,7 +252,7 @@
     <string name="shutdown_confirm_question" msgid="796151167261608447">"Itzali egin nahi duzu?"</string>
     <string name="reboot_safemode_title" msgid="5853949122655346734">"Berrabiarazi modu seguruan"</string>
     <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Modu seguruan berrabiarazi nahi duzu? Instalatutako hirugarrenen aplikazioak desgaituko dira. Berriro berrabiarazi ondoren leheneratuko dira."</string>
-    <string name="recent_tasks_title" msgid="8183172372995396653">"Azkenak"</string>
+    <string name="recent_tasks_title" msgid="8183172372995396653">"Azkenaldikoak"</string>
     <string name="no_recent_tasks" msgid="9063946524312275906">"Ez dago azkenaldian erabilitako aplikaziorik."</string>
     <string name="global_actions" product="tablet" msgid="4412132498517933867">"Tabletaren aukerak"</string>
     <string name="global_actions" product="tv" msgid="3871763739487450369">"Android TV gailuaren aukerak"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Pasahitzen eremuetan ez da onartzen eskuzko idazketa"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atzera"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Aldatu idazketa-metodoa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ireki idazketa-metodoaren hautatzailea"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ezarpenak"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Memoria betetzen ari da"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -1332,7 +1334,7 @@
     <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Alarma-soinuak"</string>
     <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Jakinarazpen-soinuak"</string>
     <string name="ringtone_unknown" msgid="5059495249862816475">"Ezezaguna"</string>
-    <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa Wi-Fi sarean"</string>
+    <string name="wifi_available_sign_in" msgid="381054692557675237">"Hasi saioa wifi-sarean"</string>
     <string name="network_available_sign_in" msgid="1520342291829283114">"Hasi saioa sarean"</string>
     <!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
     <skip />
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikazio bat baimen-eskaera oztopatzen ari da eta, ondorioz, ezin da egiaztatu erantzuna."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Eginbide bat erabiltzen hasteko, saka ezazu:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Aukeratu zein eginbide erabili nahi duzun Erabilerraztasuna botoiarekin"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Aukeratu zein eginbide erabili nahi duzun bolumen-botoien lasterbidearekin"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Desaktibatu da <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editatu lasterbideak"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Eginda"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatikoki konektatu da satelitera"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Mezuak bidal eta jaso ditzakezu sare mugikorrik edo wifi-sarerik gabe"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Satelite bidezko mezularitza erabili nahi duzu?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Bidali eta jaso mezuak sare mugikorrik edo wifi-sarerik gabe"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulagailua"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikazioak"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Zure hatz-markak ez dira ezagutzen jada. Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea."</string>
 </resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 5d3d42b..5e474cd 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -612,8 +612,8 @@
     <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده می‌شود موقعیت نسبی بین دستگاه‌های «فراپهن‌باند» اطراف را مشخص کند"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏برقراری تعامل با دستگاه‌های Wi-Fi اطراف"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏به برنامه اجازه می‌دهد در دستگاه‌های Wi-Fi اطراف تبلیغ کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را تشخیص دهد"</string>
-    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پولی NFC"</string>
-    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی سرویس پولی NFC، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
+    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پرداخت NFC"</string>
+    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"‏به برنامه اجازه می‌دهد اطلاعات ترجیحی سرویس پرداخت NFC، مانند کمک‌های ثبت‌شده و مقصد مسیر را دریافت کند."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"کنترل ارتباط راه نزدیک"</string>
     <string name="permdesc_nfc" msgid="8352737680695296741">"‏به برنامه اجازه می‎دهد تا با تگ‌های NFC، کارت‌ها و فایل‌خوان ارتباط برقرار کند."</string>
     <string name="permlab_nfcTransactionEvent" msgid="5868209446710407679">"‏رویداد تراکنش Secure Element"</string>
@@ -1057,19 +1057,19 @@
     <string name="lockscreen_access_pattern_cell_added_verbose" msgid="2931364927622563465">"سلول <xliff:g id="CELL_INDEX">%1$s</xliff:g> اضافه شد"</string>
     <string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"الگو کامل شد"</string>
     <string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"ناحیه الگو"</string>
-    <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"‏%1$s. ابزارک %2$d از %3$d."</string>
-    <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"ابزارک اضافه کنید."</string>
+    <string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"‏%1$s. ابزاره %2$d از %3$d."</string>
+    <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"ابزاره اضافه کنید."</string>
     <string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"خالی"</string>
     <string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"منطقه بازگشایی گسترده شد."</string>
     <string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"منطقه بازگشایی کوچک شد."</string>
-    <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"ابزارک <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
+    <string name="keyguard_accessibility_widget" msgid="6776892679715699875">"ابزاره <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
     <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"انتخابگر کاربر"</string>
     <string name="keyguard_accessibility_status" msgid="6792745049712397237">"وضعیت"</string>
     <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"دوربین"</string>
     <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"کنترل‌های رسانه"</string>
-    <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"مرتب سازی مجدد ابزارک آغاز شد."</string>
-    <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"مرتب‌سازی مجدد ابزارک به پایان رسید."</string>
-    <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"ابزارک <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> حذف شد.‍"</string>
+    <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"مرتب سازی مجدد ابزاره آغاز شد."</string>
+    <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"مرتب‌سازی مجدد ابزاره به پایان رسید."</string>
+    <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"ابزاره <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> حذف شد.‍"</string>
     <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"گسترده کردن منطقه بازگشایی شده."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"باز کردن قفل با کشیدن انگشت روی صفحه."</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"باز کردن قفل با الگو."</string>
@@ -1183,7 +1183,7 @@
     <string name="copyUrl" msgid="6229645005987260230">"‏کپی URL"</string>
     <string name="selectTextMode" msgid="3225108910999318778">"انتخاب متن"</string>
     <string name="undo" msgid="3175318090002654673">"لغو"</string>
-    <string name="redo" msgid="7231448494008532233">"بازانجام"</string>
+    <string name="redo" msgid="7231448494008532233">"ازنو انجام دادن"</string>
     <string name="autofill" msgid="511224882647795296">"تکمیل خودکار"</string>
     <string name="textSelectionCABTitle" msgid="5151441579532476940">"انتخاب متن"</string>
     <string name="addToDictionary" msgid="8041821113480950096">"افزودن به واژه‌نامه"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"در فیلدهای گذرواژه از دست‌نویسی پشتیبانی نمی‌شود"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"برگشت"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"تغییر روش ورودی"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"باز کردن انتخابگر روش ورودی"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"تنظیمات"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"فضای ذخیره‌سازی درحال پر شدن است"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"فضای ذخیره‌سازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راه‌اندازی مجدد کنید."</string>
@@ -1504,7 +1506,7 @@
     <string name="permlab_queryAllPackages" msgid="2928450604653281650">"پُرسمان همه بسته‌ها"</string>
     <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"به برنامه اجازه می‌دهد همه بسته‌های نصب‌شده را ببیند."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگ‌نمایی، دو بار تک‌ضرب بزنید"</string>
-    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزارک انجام نشد."</string>
+    <string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزاره انجام نشد."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"برو"</string>
     <string name="ime_action_search" msgid="4501435960587287668">"جستجو"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"ارسال"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"پاسخ شما تأیید نشد زیرا یک برنامه درخواست اجازه را مسدود کرده است."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن تک‌ضرب بزنید:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگی‌های موردنظر برای استفاده با دکمه دسترس‌پذیری"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگی‌های موردنظر برای استفاده با میان‌بر کلید میزان صدا"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> خاموش شده است"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ویرایش میان‌برها"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"تمام"</string>
@@ -2389,13 +2392,13 @@
     <string name="connected_display_unavailable_notification_title" msgid="5265409360902073556">"قرینه‌سازی روی نمایشگر ممکن نبود"</string>
     <string name="connected_display_unavailable_notification_content" msgid="3845903313751217516">"از کابل دیگری استفاده کنید و دوباره امتحان کنید"</string>
     <string name="connected_display_thermally_unavailable_notification_content" msgid="9205758199439955949">"دستگاه بسیار گرم است و تا زمانی‌که خنک نشود نمی‌تواند محتوا را روی نمایشگر قرینه‌سازی کند."</string>
-    <string name="concurrent_display_notification_name" msgid="1526911253558311131">"‫Dual screen"</string>
-    <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"‏‫Dual Screen روشن است"</string>
+    <string name="concurrent_display_notification_name" msgid="1526911253558311131">"صفحه‌نمایش دوگانه"</string>
+    <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"‫«صفحه‌نمایش دوگانه» روشن است"</string>
     <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> از هر دو نمایشگر برای نمایش محتوا استفاده می‌کند"</string>
     <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"دستگاه بیش‌ازحد گرم شده است"</string>
-    <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"‏‫Dual Screen دردسترس نیست زیرا تلفن بیش‌ازحد گرم شده است"</string>
-    <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"‏Dual Screen دردسترس نیست"</string>
-    <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"‏Dual Screen دردسترس نیست چون «بهینه‌سازی باتری» روشن است. می‌توانید این ویژگی را در «تنظیمات» خاموش کنید."</string>
+    <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"‫«صفحه‌نمایش دوگانه» دردسترس نیست زیرا تلفن بیش‌ازحد گرم شده است"</string>
+    <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"«صفحه‌نمایش دوگانه» دردسترس نیست"</string>
+    <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"«صفحه‌نمایش دوگانه» دردسترس نیست چون «بهینه‌سازی باتری» روشن است. می‌توانید این ویژگی را در «تنظیمات» خاموش کنید."</string>
     <string name="device_state_notification_settings_button" msgid="691937505741872749">"رفتن به تنظیمات"</string>
     <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"خاموش کردن"</string>
     <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"‫<xliff:g id="DEVICE_NAME">%s</xliff:g> پیکربندی شد"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"به‌دلایل امنیتی، محتوای برنامه پس‌از هم‌رسانی صفحه‌نمایش پنهان می‌شود"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"به‌طور خودکار به ماهواره متصل شد"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"‏می‌توانید بدون شبکه تلفن همراه یا Wi-Fi پیام ارسال و دریافت کنید"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"از «پیام‌رسانی ماهواره‌ای» استفاده شود؟"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏ارسال و دریافت پیام بدون شبکه تلفن همراه یا Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیام‌نگار»"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ماشین‌حساب"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"نقشه"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"برنامه‌ها"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"اثر انگشتانتان دیگر قابل‌شناسایی نیست. «قفل‌گشایی با اثر انگشت» را دوباره راه‌اندازی کنید."</string>
 </resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 67dd02b..dec5b64 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Käsinkirjoitusta ei tueta salasanakentissä"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Takaisin"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Vaihda syöttötapaa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Avaa syöttötavan valinta"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Asetukset"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Tallennustila loppumassa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Sovellus peittää lupapyynnön, joten vastaustasi ei voi vahvistaa."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Aloita ominaisuuden käyttö napauttamalla sitä:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Valitse ominaisuudet, joita käytetään esteettömyyspainikkeella"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Valitse ominaisuudet, joita käytetään äänenvoimakkuuspikanäppäimellä"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> on laitettu pois päältä"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Muokkaa pikakuvakkeita"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Valmis"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Yhdistetty automaattisesti satelliittiin"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Voit lähettää ja vastaanottaa viestejä ilman mobiili‑ tai Wi-Fi-verkkoa"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Käytetäänkö satelliittiviestintää?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Lähetä ja vastaanota viestejä ilman mobiili- tai Wi-Fi-verkkoa"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Avaa Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Näin se toimii"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Laskin"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Sovellukset"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Sormenjälkiäsi ei voi enää tunnistaa. Ota sormenjälkiavaus uudelleen käyttöön."</string>
 </resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 7f99750..7cb9e06 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Les champs de mot de passe ne prennent pas en charge l\'écriture manuscrite"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Retour"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Changer de méthode d\'entrée"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ouvrir le sélecteur de méthode d\'entrée"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Paramètres"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Une appli masque la demande d\'autorisation de sorte que votre réponse ne peut pas être vérifiée."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toucher une fonctionnalité pour commencer à l\'utiliser :"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser à l\'aide du bouton d\'accessibilité"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a été désactivé"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifier les raccourcis"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué du Partage d\'écran par mesure de sécurité"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Connecté au satellite automatiquement"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Vous pouvez envoyer et recevoir des messages sans avoir recours à un appareil mobile ou à un réseau Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Utiliser la messagerie par satellite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyez et recevez des messages sans réseau cellulaire ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vos empreintes digitales ne peuvent plus être reconnues. Reconfigurez le Déverrouillage par empreinte digitale."</string>
 </resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 1711b286..ca3998c 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Les champs de mot de passe ne prennent pas en charge l\'écriture manuscrite"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Retour"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Changer le mode de saisie"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Ouvrir l\'outil de sélection du mode de saisie"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Paramètres"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Une application masque la demande d\'autorisation. Votre réponse ne peut donc pas être vérifiée."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Appuyez sur une fonctionnalité pour commencer à l\'utiliser :"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Choisir les fonctionnalités à utiliser avec le bouton Accessibilité"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Choisir les fonctionnalités à utiliser avec le raccourci des touches de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Le service <xliff:g id="SERVICE_NAME">%s</xliff:g> a été désactivé"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifier les raccourcis"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran par mesure de sécurité"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Connecté automatiquement au réseau satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Vous pouvez envoyer et recevoir des messages sans connexion au réseau mobile ou Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Utiliser la messagerie par satellite ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envoyer et recevoir des messages sans réseau mobile ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applications"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vos empreintes ne peuvent plus être reconnues. Reconfigurez le déverrouillage par empreinte digitale."</string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 7152818..e07218c 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Os campos dos contrasinais non admiten a escritura manual"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambia o método de introdución"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o selector do método de introdución de texto"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configuración"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Estase esgotando o espazo de almacenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"É posible que algunhas funcións do sistema non funcionen"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Hai unha aplicación que está ocultando a solicitude de permiso, polo que non se pode verificar a túa resposta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocar unha función para comezar a utilizala:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escoller as funcións que queres utilizar co botón Accesibilidade"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolle as funcións que queres utilizar co atallo da tecla de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>: desactivouse"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atallos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Feito"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por seguranza, ocultouse o contido da aplicación na pantalla compartida"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Conexión automática ao satélite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Podes enviar e recibir mensaxes sen unha rede de telefonía móbil ou wifi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Queres usar a mensaxaría por satélite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envía e recibe mensaxes sen ter acceso a redes de telefonía móbil ou wifi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicacións"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Xa non se recoñecen as túas impresións dixitais. Configura de novo o desbloqueo dactilar."</string>
 </resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a9ad4b7..2810b5e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -90,7 +90,7 @@
     <string name="notification_channel_emergency_callback" msgid="54074839059123159">"કટોકટી કૉલબૅક મોડ"</string>
     <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"મોબાઇલ ડેટાની સ્થિતિ"</string>
     <string name="notification_channel_sms" msgid="1243384981025535724">"SMS મેસેજ"</string>
-    <string name="notification_channel_voice_mail" msgid="8457433203106654172">"વૉઇસમેઇલ સંદેશા"</string>
+    <string name="notification_channel_voice_mail" msgid="8457433203106654172">"વૉઇસમેઇલ મેસેજ"</string>
     <string name="notification_channel_wfc" msgid="9048240466765169038">"વાઇ-ફાઇ કૉલિંગ"</string>
     <string name="notification_channel_sim" msgid="5098802350325677490">"સિમનું સ્ટેટસ"</string>
     <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"સિમ કાર્ડનું ઉચ્ચ પ્રાધાન્યતાનું સ્ટેટસ"</string>
@@ -122,7 +122,7 @@
     <string name="roamingTextSearching" msgid="5323235489657753486">"સેવા શોધી રહ્યું છે"</string>
     <string name="wfcRegErrorTitle" msgid="3193072971584858020">"વાઇ-ફાઇ કૉલિંગ સેટ કરી શકાયું નથી"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"વાઇ-ફાઇ પરથી કૉલ કરવા અને સંદેશા મોકલવા માટે પહેલાં તમારા કૅરિઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"વાઇ-ફાઇ પરથી કૉલ કરવા અને મેસેજ મોકલવા માટે પહેલાં તમારા મોબાઇલ ઑપરેટરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગમાંથી વાઇ-ફાઇ કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"તમારા કૅરિઅરમાં વાઇ-ફાઇ કૉલિંગ રજિસ્ટર કરવામાં સમસ્યા આવી: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -203,8 +203,8 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"વ્યવસ્થાપકે ડિવાઇસ વ્યક્તિગત ઉપયોગ માટે આપી દીધું છે"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ખાનગી સ્પેસ કાઢી નાખી"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"મેનેજ કરેલા ડિવાઇસ પર, તમારી સંસ્થા દ્વારા ખાનગી સ્પેસને મંજૂરી આપવામાં આવતી નથી."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"ડિવાઇસ મેનેજ થયેલ છે"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"ડિવાઇસ મેનેજ થયેલું છે"</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"તમારી સંસ્થા આ ડિવાઇસને મેનેજ કરે છે અને નેટવર્ક ટ્રાફિફ મૉનિટર કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ઍપ તમારા સ્થાનને ઍક્સેસ કરી શકે છે"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"વધુ જાણવા માટે તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"જીઓફેન્સિંગ સેવા"</string>
@@ -266,7 +266,7 @@
     <string name="global_action_logout" msgid="6093581310002476511">"સત્ર સમાપ્ત કરો"</string>
     <string name="global_action_screenshot" msgid="2610053466156478564">"સ્ક્રીનશૉટ"</string>
     <string name="bugreport_title" msgid="8549990811777373050">"બગ રિપોર્ટ"</string>
-    <string name="bugreport_message" msgid="5212529146119624326">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
+    <string name="bugreport_message" msgid="5212529146119624326">"આ, એક ઇ-મેઇલ મેસેજ તરીકે મોકલવા માટે, તમારા વર્તમાન ડિવાઇસના સ્ટેટસ વિશે માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટના શરુ થવાથી લઈને મોકલવા માટે તૈયાર થવા સુધીની પ્રક્રિયામાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
     <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ક્રિયાપ્રતિક્રિયાત્મક રિપોર્ટ"</string>
     <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"મોટાભાગના સંજોગોમાં આનો ઉપયોગ કરો. તે રિપોર્ટની પ્રગતિને ટ્રૅક કરવા, સમસ્યા વિશે વધુ વિગતો દાખલ કરવાની અને સ્ક્રીનશૉટ્સ લેવાની મંજૂરી આપે છે. તે કેટલાક ઓછા ઉપયોગમાં આવતાં વિભાગો કે જે જાણ કરવામાં વધુ સમય લેતાં હોય તેને છોડી દઈ શકે છે."</string>
     <string name="bugreport_option_full_title" msgid="7681035745950045690">"પૂર્ણ રિપોર્ટ"</string>
@@ -291,8 +291,8 @@
     <string name="notification_channel_security" msgid="8516754650348238057">"સુરક્ષા"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"કાર મોડ"</string>
     <string name="notification_channel_account" msgid="6436294521740148173">"એકાઉન્ટ સ્થિતિ"</string>
-    <string name="notification_channel_developer" msgid="1691059964407549150">"વિકાસકર્તા માટેના સંદેશા"</string>
-    <string name="notification_channel_developer_important" msgid="7197281908918789589">"ડેવલપર માટેના મહત્ત્વપૂર્ણ સંદેશા"</string>
+    <string name="notification_channel_developer" msgid="1691059964407549150">"ડેવલપર માટેના મેસેજ"</string>
+    <string name="notification_channel_developer_important" msgid="7197281908918789589">"ડેવલપર માટેના મહત્ત્વપૂર્ણ મેસેજ"</string>
     <string name="notification_channel_updates" msgid="7907863984825495278">"અપડેટ્સ"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"નેટવર્ક સ્થિતિ"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"નેટવર્ક ચેતવણીઓ"</string>
@@ -324,7 +324,7 @@
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"કૅલેન્ડર"</string>
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"તમારા કેલેન્ડરને ઍક્સેસ કરવાની"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS સંદેશા મોકલવાની અને જોવાની"</string>
+    <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS મેસેજ મોકલો અને જુઓ"</string>
     <string name="permgrouplab_storage" msgid="17339216290379241">"ફાઇલો"</string>
     <string name="permgroupdesc_storage" msgid="5378659041354582769">"તમારા ડિવાઇસ પરની ફાઇલો ઍક્સેસ કરો"</string>
     <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"મ્યુઝિક અને ઑડિયો"</string>
@@ -379,28 +379,28 @@
     <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"એપ્લિકેશનને આઉટગોઇંગ કૉલ દરમિયાન કૉલને એક અલગ નંબર પર રીડાયરેક્ટ કરવા અથવા કૉલને સંપૂર્ણપણે છોડી દેવાનાં વિકલ્પ સાથે ડાયલ થઈ રહેલા નંબરને જોવાની મંજૂરી આપે છે."</string>
     <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ફોન કૉલને જવાબ આપો"</string>
     <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ઍપ્લિકેશનને ઇનકમિંગ ફોન કૉલને જવાબ આપવાની મંજૂરી આપે છે."</string>
-    <string name="permlab_receiveSms" msgid="505961632050451881">"ટેક્સ્ટ સંદેશા (SMS) પ્રાપ્ત કરો"</string>
-    <string name="permdesc_receiveSms" msgid="1797345626687832285">"ઍપ્લિકેશનને SMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string>
-    <string name="permlab_receiveMms" msgid="4000650116674380275">"ટેક્સ્ટ સંદેશા (MMS) પ્રાપ્ત કરો"</string>
-    <string name="permdesc_receiveMms" msgid="958102423732219710">"ઍપ્લિકેશનને MMS સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ્લિકેશન તમને દર્શાવ્યા વિના તમારા ઉપકરણ પર મોકલેલ સંદેશાઓનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string>
-    <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"સેલ બ્રોડકાસ્ટ સંદેશા ફૉરવર્ડ કરો"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"સેલ બ્રોડકાસ્ટ સંદેશા પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string>
+    <string name="permlab_receiveSms" msgid="505961632050451881">"ટેક્સ્ટ મેસેજ (SMS) મેળવો"</string>
+    <string name="permdesc_receiveSms" msgid="1797345626687832285">"ઍપને SMS મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજનું નિરીક્ષણ કરી શકે છે અથવા કાઢી નાખી શકે છે."</string>
+    <string name="permlab_receiveMms" msgid="4000650116674380275">"ટેક્સ્ટ મેસેજ (MMS) મેળવો"</string>
+    <string name="permdesc_receiveMms" msgid="958102423732219710">"ઍપને MMS મેસેજ પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આનો અર્થ એ કે ઍપ તમને દર્શાવ્યા વિના તમારા ડિવાઇસ પર મોકલેલા મેસેજને મૉનિટર કરી શકે છે અથવા ડિલીટ કરી શકે છે."</string>
+    <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"સેલ બ્રોડકાસ્ટ મેસેજ ફૉરવર્ડ કરો"</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"સેલ બ્રોડકાસ્ટ મેસેજ પ્રાપ્ત થાય કે તરત ફૉરવર્ડ કરવા માટે સેલ બ્રોડકાસ્ટ મૉડ્યૂલ સાથે પ્રતિબદ્ધ થવા બાબતે ઍપને મંજૂરી આપે છે. તમને કટોકટીની પરિસ્થિતિની ચેતવણી આપવા માટે સેલ બ્રોડકાસ્ટ અલર્ટ અમુક સ્થાનોમાં ડિલિવર કરવામાં આવે છે. કટોકટી અંગેનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય, ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઑપરેશનમાં વિક્ષેપ પાડે તેમ બની શકે છે."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"ચાલી રહેલા કૉલ મેનેજ કરો"</string>
     <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"ઍપને તમારા ડિવાઇસ પર ચાલુ કૉલ વિશેની વિગતો જોવાની અને આ કૉલને નિયંત્રિત કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_accessLastKnownCellId" msgid="7638226620825665130">"છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરો."</string>
     <string name="permdesc_accessLastKnownCellId" msgid="6664621339249308857">"ઍપને ટેલિફોન દ્વારા પ્રદાન કરવામાં આવેલી છેલ્લી જ્ઞાત સેલ ઓળખને ઍક્સેસ કરવાની મંજૂરી આપે છે."</string>
-    <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"સેલ બ્રોડકાસ્ટ સંદેશા વાંચો"</string>
+    <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"સેલ બ્રોડકાસ્ટ મેસેજ વાંચો"</string>
     <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"ઍપ તમારા ડિવાઇસ દ્વારા પ્રાપ્ત થયેલ સેલ બ્રોડકાસ્ટ સંદેશાને વાંચવાની મંજૂરી આપે છે. સેલ બ્રોડકાસ્ટ ચેતવણીઓ તમને ઇમર્જન્સીની સ્થિતિઓ અંગે ચેતવવા માટે કેટલાક સ્થાનોમાં વિતરિત થાય છે. જ્યારે ઇમર્જન્સીનો સેલ બ્રોડકાસ્ટ પ્રાપ્ત થાય ત્યારે દુર્ભાવનાપૂર્ણ ઍપ તમારા ડિવાઇસના કાર્યપ્રદર્શન અથવા ઓપરેશનમાં હસ્તક્ષેપ કરી શકે છે."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"સબ્સ્ક્રાઇબ કરેલ ફીડ્સ વાંચો"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"એપ્લિકેશનને હાલમાં સમન્વયિત ફીડ્સ વિશે વિગતો મેળવવાની મંજૂરી આપે છે."</string>
-    <string name="permlab_sendSms" msgid="7757368721742014252">"SMS સંદેશા મોકલો અને જુઓ"</string>
-    <string name="permdesc_sendSms" msgid="6757089798435130769">"એપ્લિકેશનને SMS સંદેશા મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ્લિકેશનો તમારી પુષ્ટિ વિના સંદેશા મોકલીને તમારા નાણા ખર્ચાવી શકે છે."</string>
-    <string name="permlab_readSms" msgid="5164176626258800297">"તમારા ટેક્સ્ટ સંદેશા (SMS અથવા MMS) વાંચો"</string>
-    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string>
-    <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string>
-    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) સંદેશા વાંચી શકે છે."</string>
-    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"ટેક્સ્ટ સંદેશા (WAP) પ્રાપ્ત કરો"</string>
-    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"એપ્લિકેશનને WAP સંદેશા પ્રાપ્ત કરવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલ સંદેશાઓનું નિરીક્ષણ કરવાની અને કાઢી નાખવાની ક્ષમતાનો સમાવેશ થાય છે."</string>
+    <string name="permlab_sendSms" msgid="7757368721742014252">"SMS મેસેજ મોકલો અને જુઓ"</string>
+    <string name="permdesc_sendSms" msgid="6757089798435130769">"ઍપને SMS મેસેજ મોકલવાની મંજૂરી આપે છે. આના પરિણામે અનપેક્ષિત શુલ્ક લાગી શકે છે. દુર્ભાવનાપૂર્ણ ઍપ તમારા કન્ફર્મેશન વિના મેસેજ મોકલીને તમારા નાણા ખર્ચાવી શકે છે."</string>
+    <string name="permlab_readSms" msgid="5164176626258800297">"તમારા ટેક્સ્ટ મેસેજ (SMS અથવા MMS) વાંચો"</string>
+    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"આ ઍપ, તમારા ટેબ્લેટ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string>
+    <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string>
+    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"આ ઍપ, તમારા ફોન પર સંગ્રહિત તમામ SMS (ટેક્સ્ટ) મેસેજ વાંચી શકે છે."</string>
+    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"ટેક્સ્ટ મેસેજ  (WAP) મેળવો"</string>
+    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"ઍપને WAP મેસેજ મેળવવાની અને તેના પર પ્રક્રિયા કરવાની મંજૂરી આપે છે. આ પરવાનગીમાં તમને દર્શાવ્યા વિના તમને મોકલેલા મેસેજનું નિરીક્ષણ કરવાની અને ડિલીટ કરવાની ક્ષમતાનો સમાવેશ થાય છે."</string>
     <string name="permlab_getTasks" msgid="7460048811831750262">"ચાલુ ઍપ્લિકેશનો પુનઃપ્રાપ્ત કરો"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"એપ્લિકેશનને વર્તમાનમાં અને તાજેતરમાં ચાલી રહેલ Tasks વિશેની વિગતવાર માહિતી પુનઃપ્રાપ્ત કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને ઉપકરણ પર કઈ એપ્લિકેશન્સનો ઉપયોગ થાય છે તેના વિશેની માહિતી શોધવાની મંજૂરી આપી શકે છે."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"પ્રોફાઇલ અને ડિવાઇસ માલિકોને મેનેજ કરો"</string>
@@ -492,9 +492,9 @@
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"આ ઍપ, તમારા Android TV ડિવાઇસ પર સંગ્રહિત બધા કૅલેન્ડર ઇવેન્ટને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string>
     <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"આ ઍપ્લિકેશન, તમારા ફોન પર સંગ્રહિત તમામ કૅલેન્ડર ઇવેન્ટ્સને વાંચી શકે છે અને તમારા કૅલેન્ડર ડેટાને શેર કરી અથવા સાચવી શકે છે."</string>
     <string name="permlab_writeCalendar" msgid="6422137308329578076">"કૅલેન્ડર  ઇવેન્ટ્સ ઉમેરો અથવા સંશોધિત કરો અને માલિકની જાણ બહાર અતિથિઓને ઇમેઇલ મોકલો"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"આ ઍપ્લિકેશન, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string>
-    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string>
-    <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"આ ઍપ્લિકેશન, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ્સ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ્લિકેશન, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં સંદેશા મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ્સ બદલી શકે છે."</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"આ ઍપ, તમારા ટેબ્લેટ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના કૅલેન્ડર બદલી શકે છે."</string>
+    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"આ ઍપ, તમારા Android TV ડિવાઇસ પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, કાઢી નાખી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string>
+    <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"આ ઍપ, તમારા ફોન પર કૅલેન્ડર ઇવેન્ટ ઉમેરી, દૂર કરી અથવા બદલી શકે છે. આ ઍપ, કૅલેન્ડર માલિકો તરફથી આવતાં હોય તેવા લાગતાં મેસેજ મોકલી શકે છે અથવા તેમના માલિકોને સૂચિત કર્યા વિના ઇવેન્ટ બદલી શકે છે."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરો"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"એપ્લિકેશનને વધારાના સ્થાન પ્રદાતા આદેશોને ઍક્સેસ કરવાની મંજૂરી આપે છે. આ એપ્લિકેશનને GPS અથવા અન્ય સ્થાન સ્રોતોના ઓપરેશનમાં દખલ કરવાની મંજૂરી આપી શકે છે."</string>
     <string name="permlab_accessFineLocation" msgid="6426318438195622966">"ફૉરગ્રાઉન્ડમાં ફક્ત ચોક્કસ સ્થાન ઍક્સેસ કરો"</string>
@@ -1101,7 +1101,7 @@
     <string name="permlab_setAlarm" msgid="1158001610254173567">"એલાર્મ સેટ કરો"</string>
     <string name="permdesc_setAlarm" msgid="2185033720060109640">"એપ્લિકેશનને ઇન્સ્ટોલ કરેલ અલાર્મ ઘડિયાળ એપ્લિકેશનમાં અલાર્મ સેટ કરવાની મંજૂરી આપે છે. કેટલીક અલાર્મ ઘડિયાળ ઍપ્લિકેશનો, આ સુવિધા લાગુ કરી શકતી નથી."</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"વૉઇસમેઇલ ઉમેરો"</string>
-    <string name="permdesc_addVoicemail" msgid="5470312139820074324">"એપ્લિકેશનને તમારા વૉઇસમેઇલ ઇનબોક્સ પર સંદેશા ઉમેરવાની મંજૂરી આપે છે."</string>
+    <string name="permdesc_addVoicemail" msgid="5470312139820074324">"એપને તમારા વૉઇસમેઇલ ઇનબોક્સ પર મેસેજ ઉમેરવાની મંજૂરી આપે છે."</string>
     <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> દ્વારા તમારા ક્લિપબોર્ડ પરથી પેસ્ટ કરવામાં આવ્યું"</string>
     <string name="more_item_label" msgid="7419249600215749115">"વધુ"</string>
     <string name="prepend_shortcut_label" msgid="1743716737502867951">"મેનૂ+"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"પાસવર્ડ ફીલ્ડમાં હસ્તલેખન સપોર્ટેડ નથી"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"પાછળ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ઇનપુટ પદ્ધતિ સ્વિચ કરો"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ઇનપુટ પદ્ધતિ પિકર ખોલો"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"સેટિંગ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"કેટલાક સિસ્ટમ Tasks કામ કરી શકશે નહીં"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1357,11 +1359,11 @@
     <string name="accept" msgid="5447154347815825107">"સ્વીકારો"</string>
     <string name="decline" msgid="6490507610282145874">"નકારો"</string>
     <string name="select_character" msgid="3352797107930786979">"અક્ષર શામેલ કરો"</string>
-    <string name="sms_control_title" msgid="4748684259903148341">"SMS સંદેશા મોકલી રહ્યું છે"</string>
-    <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; મોટા પ્રમાણમાં SMS સંદેશા મોકલી રહ્યું છે. શું તમે સંદેશા મોકલવાનું ચાલુ રાખવા માટે આ એપ્લિકેશનને મંજૂરી આપવા માગો છો?"</string>
+    <string name="sms_control_title" msgid="4748684259903148341">"SMS મેસેજ મોકલી રહ્યાં છે"</string>
+    <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; મોટા પ્રમાણમાં SMS મેસેજ મોકલી રહ્યું છે. શું તમે મેસેજ મોકલવાનું ચાલુ રાખવા માટે આ એપને મંજૂરી આપવા માગો છો?"</string>
     <string name="sms_control_yes" msgid="4858845109269524622">"મંજૂરી આપો"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"નકારો"</string>
-    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; તમને &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; પર સંદેશ મોકલવા માગે છે."</string>
+    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;, &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt; પર મેસેજ મોકલવા માગે છે."</string>
     <string name="sms_short_code_details" msgid="2723725738333388351">"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર "<b>"શુલ્ક લાગી શકે છે"</b>"."</string>
     <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"આનાથી તમારા મોબાઇલ એકાઉન્ટ પર શુલ્ક લાગશે."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"મોકલો"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"કોઈ ઍપ પરવાનગીની વિનંતીને ઢાંકી રહી છે, તેથી તમારા પ્રતિસાદની ચકાસણી કરી શકાતી નથી."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"સુવિધાનો ઉપયોગ શરૂ કરવા તેના પર ટૅપ કરો:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ઍક્સેસિબિલિટી બટન વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"વૉલ્યૂમ કી શૉર્ટકટ વડે તમે ઉપયોગમાં લેવા માગો છો તે સુવિધાઓ પસંદ કરો"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> બંધ કરવામાં આવ્યું છે"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"શૉર્ટકટમાં ફેરફાર કરો"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"થઈ ગયું"</string>
@@ -2034,7 +2037,7 @@
     <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Androidના કોઈ જૂના વર્ઝન માટે આ ઍપ બનાવવામાં આવી હતી. તે કદાચ યોગ્ય રીતે કામ કરતી નથી અને તેમાં નવીનતમ સુરક્ષા અને પ્રાઇવસી સંબંધિત સંરક્ષણો શામેલ નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"અપડેટ માટે તપાસો"</string>
     <string name="deprecated_abi_message" msgid="6820548011196218091">"આ ઍપ Androidના નવીનતમ વર્ઝન સાથે સુસંગત નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string>
-    <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા સંદેશા છે"</string>
+    <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા મેસેજ છે"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"જોવા માટે SMS ઍપ્લિકેશન ખોલો"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"કેટલીક કાર્યક્ષમતા મર્યાદિત હોઈ શકે છે"</string>
     <string name="profile_encrypted_detail" msgid="5279730442756849055">"કાર્યાલયની પ્રોફાઇલ લૉક કરી"</string>
@@ -2150,7 +2153,7 @@
     <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ઓકે"</string>
     <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"બંધ કરો"</string>
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"વધુ જાણો"</string>
-    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને સંદેશા જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string>
+    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12માં Android માટે અનુકૂળ નોટિફિકેશનને બદલે વધુ સારા નોટિફિકેશન છે. આ સુવિધા સૂચિત ક્રિયાઓ અને જવાબો બતાવે છે તેમજ તમારા નોટિફિકેશનની યોગ્ય ગોઠવણી કરે છે.\n\nવધુ સારા નોટિફિકેશન સંપર્કોના નામ અને મેસેજ જેવી વ્યક્તિગત માહિતી સહિત નોટિફિકેશનનું બધું કન્ટેન્ટ ઍક્સેસ કરી શકે છે. આ સુવિધા ફોન કૉલના જવાબ આપવા કે \'ખલેલ પાડશો નહીં\'નું નિયંત્રણ કરવા જેવા નોટિફિકેશન છોડવાની કે તેનો જવાબ આપવાની ક્રિયા પણ કરી શકે છે."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"રૂટિન મોડની માહિતીનું નોટિફિકેશન"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"બૅટરી સેવરની સુવિધા ચાલુ કરી છે"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"બૅટરીની આવરદા વધારવા માટે બૅટરીનો વપરાશ ઘટાડી રહ્યાં છીએ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"સેટેલાઇટ સાથે ઑટોમૅટિક રીતે કનેક્ટેડ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"તમે મોબાઇલ અથવા વાઇ-ફાઇ નેટવર્ક વિના મેસેજ મોકલી અને પ્રાપ્ત કરી શકો છો"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"શું સૅટલાઇટ મેસેજિંગનો ઉપયોગ કરીએ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"મોબાઇલ કે વાઇ-ફાઇ નેટવર્ક વિના મેસેજ મોકલો અને મેળવો"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"કેલ્ક્યુલેટર"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"નકશા"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ઍપ્લિકેશનો"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"તમારી ફિંગરપ્રિન્ટને હવેથી ઓળખી શકાશે નહીં. ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો."</string>
 </resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 428e9af..700e91a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"हैंडराइटिंग की सुविधा, पासवर्ड वाले फ़ील्ड में काम नहीं करती है"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"वापस जाएं"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट का तरीका बदलें"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"\'इनपुट का तरीका\' पिकर को खोलें"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिंग"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"मेमोरी में जगह नहीं बची है"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"हो सकता है कुछ सिस्टम फ़ंक्शन काम नहीं करें"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ऐप्लिकेशन की वजह से, अनुमति का अनुरोध समझने में परेशानी हो रही है. इसलिए, आपके जवाब की पुष्टि नहीं की जा सकी."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"सुलभता बटन पर टैप करके, इस्तेमाल करने के लिए सुविधाएं चुनें"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"चुनें कि आवाज़ कम या ज़्यादा करने वाले बटन के शॉर्टकट से आप किन सुविधाओं का इस्तेमाल करना चाहते हैं"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> को बंद कर दिया गया है"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"शॉर्टकट में बदलाव करें"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"हो गया"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"सैटलाइट से अपने-आप कनेक्ट हो गया"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"मोबाइल या वाई-फ़ाई नेटवर्क के बिना भी मैसेज भेजे और पाए जा सकते हैं"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"क्या आपको सैटलाइट की मदद से मैसेज भेजना है?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल या वाई-फ़ाई नेटवर्क के बिना मैसेज भेजें और पाएं"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"कैल्कुलेटर"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"मैप"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ऐप्लिकेशन"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"अब आपके फ़िंगरप्रिंट की पहचान नहीं की जा सकती. फ़िंगरप्रिंट अनलॉक की सुविधा को दोबारा सेट अप करें."</string>
 </resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 5c56ec4..2596de2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -204,7 +204,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za osobnu upotrebu"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizacija ne dopušta privatne prostore na ovom upravljanom uređaju."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"Uređaj je upravljan"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Obratite se IT administratoru da biste saznali više"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Rukopis nije podržan u poljima za zaporku"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Natrag"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Promjena načina unosa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvori alat za odabir načina unosa"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Postavke"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Neke sistemske funkcije možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija prekriva upit za dopuštenje pa se vaš odgovor ne može potvrditi."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Dodirnite značajku da biste je počeli koristiti:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Odabir značajki za upotrebu pomoću gumba za Pristupačnost"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Odabir značajki za upotrebu pomoću prečaca tipki za glasnoću"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usluga <xliff:g id="SERVICE_NAME">%s</xliff:g> je isključena"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredite prečace"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gotovo"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatski povezano sa satelitom"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Možete slati i primati poruke bez mobilne mreže ili Wi-Fi mreže"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Želite li slati poruke putem satelita?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Šaljite i primajte poruke kad nije dostupna mobilna ili Wi-Fi mreža"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
@@ -2436,7 +2441,7 @@
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Postavi"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sad"</string>
     <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm za korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
-    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Promijeni korisnika"</string>
+    <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Promijenite korisnika"</string>
     <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string>
     <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite za isključivanje zvuka"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6535007304687100909">"Preglednik"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Karte"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaši se otisci prstiju više ne prepoznaju. Ponovo postavite otključavanje otiskom prsta."</string>
 </resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3da7722..09af506 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"A kézírás nem támogatott a jelszómezőkben"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Vissza"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Beviteli módszer váltása"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"A bevitelimód-választó megnyitása"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Beállítások"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kevés a szabad terület"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Az egyik alkalmazás eltakarja az engedélykérelmet, így az Ön válasza nem ellenőrizhető."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Koppintson valamelyik funkcióra a használatához:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Kiválaszthatja a Kisegítő lehetőségek gombbal használni kívánt funkciókat"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Kiválaszthatja a hangerőgombbal használni kívánt funkciókat"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kikapcsolva"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Gyorsparancsszerkesztés"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Kész"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Apptartalom elrejtve a megosztástól a biztonság érdekében"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatikusan csatlakozva a műholdhoz"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Küldhet és fogadhat üzeneteket mobil- és Wi-Fi-hálózat nélkül is"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Műholdas üzenetváltást szeretne használni?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Küldhet és fogadhat üzeneteket mobil- és Wi-Fi-hálózat nélkül is"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"A Messages megnyitása"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hogyan működik?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Számológép"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Térkép"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Alkalmazások"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Az ujjlenyomata már nem ismerhető fel. Állítsa be újra a Feloldás ujjlenyomattal funkciót."</string>
 </resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index dba6014..a28f413 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -525,7 +525,7 @@
     <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Այս հավելվածը կարող է հետզանգեր ստանալ՝ ցանկացած տեսախցիկի բացվելու (կնշվի բացող հավելվածը) և փակվելու դեպքում։"</string>
     <string name="permlab_cameraHeadlessSystemUser" msgid="680194666834500050">"Թույլատրել հավելվածին կամ ծառայությանը օգտագործել որպես միջերեսի համակարգային օգտատեր։"</string>
     <string name="permdesc_cameraHeadlessSystemUser" msgid="6963163319710996412">"Այս հավելվածին ձեր տեսախցիկը հասանելի է որպես առանց միջերեսի համակարգային օգտատեր։"</string>
-    <string name="permlab_vibrate" msgid="8596800035791962017">"կառավարել թրթռումը"</string>
+    <string name="permlab_vibrate" msgid="8596800035791962017">"կառավարել թրթռոցը"</string>
     <string name="permdesc_vibrate" msgid="8733343234582083721">"Թույլ է տալիս հավելվածին կառավարել թրթռոցը:"</string>
     <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Հավելվածին թույլ է տալիս օգտագործել սարքի թրթռալու ռեժիմը։"</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"ուղղակիորեն զանգել հեռախոսահամարներին"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Ձեռագիր ներածումը չի աջակցվում գաղտնաբառերի դաշտերում"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Հետ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Փոխել ներածման եղանակը"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Բացել ներածման եղանակի ընտրիչը"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Կարգավորումներ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Հիշողությունը սպառվում է"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Որոշ գործառույթներ կարող են չաշխատել"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Հավելվածը թաքցնում է թույլտվության հայտը, ուստի ձեր պատասխանը հնարավոր չէ ստուգել։"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ընտրեք՝ որ գործառույթն օգտագործել"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Ընտրեք գործառույթները, որոնք կբացվեն «Հատուկ գործառույթներ» կոճակի միջոցով"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ընտրեք գործառույթները, որոնք կբացվեն ձայնի կարգավորման կոճակի միջոցով"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ծառայությունն անջատված է"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Փոփոխել դյուրանցումները"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Պատրաստ է"</string>
@@ -2421,7 +2424,9 @@
     <string name="redacted_notification_action_title" msgid="6942924973335920935"></string>
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներով՝ բովանդակությունը թաքցվել է ցուցադրումից"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Ավտոմատ միացել է արբանյակին"</string>
-    <string name="satellite_notification_summary" msgid="5207364139430767162">"Դուք կարող եք ուղարկել և ստանալ հաղորդագրություններ՝ առանց բջջային կամ Wi-Fi կապի"</string>
+    <string name="satellite_notification_summary" msgid="5207364139430767162">"Դուք կարող եք հաղորդագրություններ ուղարկել և ստանալ առանց բջջային կամ Wi-Fi կապի"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Օգտագործե՞լ արբանյակային հաղորդագրումը"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Ուղարկեք և ստացեք հաղորդագրություններ առանց բջջային կամ Wi-Fi ցանցի"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Հաշվիչ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Քարտեզներ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Հավելվածներ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ձեր մատնահետքերն այլևս չեն կարող ճանաչվել։ Նորից կարգավորեք մատնահետքով ապակողպումը։"</string>
 </resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0e431f4..76c89ea 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -203,7 +203,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin melepaskan perangkat untuk penggunaan pribadi"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ruang privasi dihapus"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasi Anda tidak mengizinkan adanya ruang privasi di perangkat terkelola ini."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"Perangkat ini ada yang mengelola"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"Ini adalah perangkat terkelola"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasi mengelola perangkat ini dan mungkin memantau traffic jaringan. Ketuk untuk melihat detailnya."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikasi dapat mengakses lokasi Anda"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Hubungi admin IT untuk mempelajari lebih lanjut"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Tulisan tangan tidak didukung di kolom sandi"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Kembali"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Beralih metode input"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buka pemilih metode input"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Setelan"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang penyimpanan hampir habis"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1668,7 +1670,7 @@
     <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Transmisi layar ke perangkat"</string>
     <string name="media_route_chooser_searching" msgid="6119673534251329535">"Menelusuri perangkat…"</string>
     <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Setelan"</string>
-    <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Putuskan koneksi"</string>
+    <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Berhenti hubungkan"</string>
     <string name="media_route_status_scanning" msgid="8045156315309594482">"Memindai..."</string>
     <string name="media_route_status_connecting" msgid="5845597961412010540">"Menghubungkan..."</string>
     <string name="media_route_status_available" msgid="1477537663492007608">"Tersedia"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikasi menghalangi permintaan izin sehingga respons Anda tidak dapat diverifikasi."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketuk fitur untuk mulai menggunakannya:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih fitur yang akan digunakan dengan tombol aksesibilitas"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih fitur yang akan digunakan dengan pintasan tombol volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> telah dinonaktifkan"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit pintasan"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Selesai"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar karena alasan keamanan"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Menghubungkan otomatis ke satelit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Anda dapat mengirim dan menerima pesan tanpa jaringan seluler atau Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Gunakan fitur pesan satelit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mengirim dan menerima pesan tanpa jaringan seluler atau Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikasi"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Sidik jari Anda tidak dapat dikenali lagi. Siapkan Buka dengan Sidik Jari lagi."</string>
 </resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b2e03f3..3eb2e4b 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handskrift er ekki studd í reitum fyrir aðgangsorð"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Til baka"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Skipta um innfærsluaðferð"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Opna val á innfærsluaðferð"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Stillingar"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Geymslurýmið er senn á þrotum"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Forrit er að fela heimildarbeiðnina svo ekki er hægt að staðfesta svarið þitt."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ýttu á eiginleika til að byrja að nota hann:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Veldu eiginleika sem á að nota með aðgengishnappinum"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Veldu eiginleika sem á að nota með flýtileið hljóðstyrkstakka"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Slökkt hefur verið á <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Breyta flýtileiðum"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Lokið"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Tengdist sjálfkrafa við gervihnött"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Þú getur sent og móttekið skilaboð án tengingar við farsímakerfi eða Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Nota skilaboð í gegnum gervihnött?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Senda og fá skilaboð án tengingar við farsímakerfi eða Wi-Fi-net"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Reiknivél"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kort"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Forrit"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ekki er lengur hægt að bera kennsl á fingraförin þín. Settu fingrafarskenni upp aftur."</string>
 </resources>
diff --git a/core/res/res/values-it-feminine/strings.xml b/core/res/res/values-it-feminine/strings.xml
index 141b467..1a69a63 100644
--- a/core/res/res/values-it-feminine/strings.xml
+++ b/core/res/res/values-it-feminine/strings.xml
@@ -20,6 +20,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="relationTypeFriend" msgid="3192092625893980574">"Amica"</string>
     <string name="relationTypeSpouse" msgid="6916682664436031703">"Moglie"</string>
 </resources>
diff --git a/core/res/res/values-it-masculine/strings.xml b/core/res/res/values-it-masculine/strings.xml
index 7310eb8..86c4408 100644
--- a/core/res/res/values-it-masculine/strings.xml
+++ b/core/res/res/values-it-masculine/strings.xml
@@ -20,6 +20,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
     <string name="relationTypeSpouse" msgid="6916682664436031703">"Marito"</string>
 </resources>
diff --git a/core/res/res/values-it-neuter/strings.xml b/core/res/res/values-it-neuter/strings.xml
index ce433d7..7c33e91 100644
--- a/core/res/res/values-it-neuter/strings.xml
+++ b/core/res/res/values-it-neuter/strings.xml
@@ -20,6 +20,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="relationTypeFriend" msgid="3192092625893980574">"Amicə"</string>
     <string name="relationTypeSpouse" msgid="6916682664436031703">"Coniuge"</string>
 </resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 52fd5b1..29533d5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -969,7 +969,7 @@
     <string name="relationTypeChild" msgid="9076258911292693601">"Figlio"</string>
     <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Convivente"</string>
     <string name="relationTypeFather" msgid="3856225062864790596">"Padre"</string>
-    <string name="relationTypeFriend" msgid="3192092625893980574">"Persona amica"</string>
+    <string name="relationTypeFriend" msgid="3192092625893980574">"Amico"</string>
     <string name="relationTypeManager" msgid="2272860813153171857">"Dirigente"</string>
     <string name="relationTypeMother" msgid="2331762740982699460">"Madre"</string>
     <string name="relationTypeParent" msgid="4177920938333039882">"Genitore"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La scrittura a mano libera non è supportata nei campi per le password"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Indietro"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambia metodo di immissione"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Apri selettore metodo di immissione"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Impostazioni"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spazio di archiviazione in esaurimento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Alcune funzioni di sistema potrebbero non funzionare"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Un\'app nasconde la tua richiesta di autorizzazione, per cui non abbiamo potuto verificare la tua risposta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tocca una funzionalità per iniziare a usarla:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Scegli le funzionalità da usare con il pulsante Accessibilità"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Scegli le funzionalità da usare con la scorciatoia dei tasti del volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Il servizio <xliff:g id="SERVICE_NAME">%s</xliff:g> è stato disattivato"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Modifica scorciatoie"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Fine"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Connessione automatica al satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Puoi inviare e ricevere messaggi senza una rete mobile o Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Utilizzare i messaggi via satellite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Invia e ricevi messaggi senza una rete mobile o Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calcolatrice"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Applicazioni"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Non è più possibile riconoscere le tue impronte. Riconfigura lo Sblocco con l\'Impronta."</string>
 </resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 76e4e83..52b6173 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -609,8 +609,8 @@
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"‏מאפשרת לאפליקציה להתחבר למכשירי Bluetooth מותאמים"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"‏פרסום במכשירי Bluetooth בקרבת מקום"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"‏האפליקציה תוכל לפרסם במכשירי Bluetooth בקרבת מקום"</string>
-    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"זיהוי מיקום יחסי בין מכשירי \'תחום רחב סרט\' קרובים"</string>
-    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"האפליקציה תזהה את המיקום היחסי בין מכשירים קרובים שמשדרים בטכנולוגיה \'תחום רחב סרט\'"</string>
+    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"‏זיהוי מיקום יחסי בין מכשירי Ultra Wideband ‏(UWB) קרובים"</string>
+    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"‏האפליקציה תזהה את המיקום היחסי בין מכשירים קרובים שמשדרים בטכנולוגיית Ultra Wideband ‏(UWB)"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏אינטראקציה עם מכשירי Wi-Fi בקרבת מקום"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏האפליקציה תוכל לפרסם במכשירי Wi-Fi בקרבת מקום, להתחבר אליהם ולהעריך את המיקום היחסי שלהם"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏פרטים על שירות תשלום מועדף ב-NFC"</string>
@@ -700,11 +700,11 @@
     <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"כדאי לנסות דרך אחרת לביטול הנעילה"</string>
     <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"אפשר להשתמש בפתיחה ע\"י זיהוי הפנים כשטביעת האצבע שלך לא מזוהה, למשל כשהאצבעות שלך רטובות"</string>
     <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"אפשר להשתמש בביטול הנעילה בטביעת אצבע כשהפנים שלך לא מזוהות, למשל כשאין מספיק אור"</string>
-    <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"פתיחה ע\"י זיהוי הפנים"</string>
+    <string name="face_recalibrate_notification_name" msgid="7311163114750748686">"פתיחה בזיהוי פנים"</string>
     <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"בעיה בפתיחה ע\"י זיהוי הפנים"</string>
     <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"יש להקיש כדי למחוק את התבנית לזיהוי הפנים, ואז להוסיף תבנית חדשה לזיהוי הפנים"</string>
     <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"‏כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות &gt; פרטיות"</string>
-    <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ביטול הנעילה בטביעת אצבע"</string>
+    <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"פתיחה בטביעת אצבע"</string>
     <string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"לא ניתן להשתמש בחיישן טביעות האצבע"</string>
     <string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"צריך ליצור קשר עם ספק תיקונים."</string>
     <string name="face_acquired_insufficient" msgid="6889245852748492218">"לא ניתן ליצור את התבנית לזיהוי הפנים. יש לנסות שוב."</string>
@@ -1074,7 +1074,7 @@
     <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"הרחבה של אזור ביטול הנעילה."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"ביטול נעילה באמצעות הסטה."</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"ביטול נעילה על ידי שרטוט קו."</string>
-    <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"פתיחה ע\"י זיהוי הפנים."</string>
+    <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"פתיחה בזיהוי פנים"</string>
     <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"ביטול נעילה באמצעות קוד אימות."</string>
     <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"‏ביטול הנעילה של קוד האימות ל-SIM."</string>
     <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"‏ביטול נעילה של PUK ל-SIM."</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"אין תמיכה בכתיבה ידנית בשדות של הזנת סיסמה"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"חזרה"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"החלפה של שיטת הקלט"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"פתיחה של בוחר שיטות הקלט"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"הגדרות"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"מקום האחסון עומד להיגמר"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏אין מספיק מקום אחסון עבור המערכת. עליך לוודא שיש לך מקום פנוי בנפח של 250MB ולהתחיל שוב."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"אפליקציה מסתירה את בקשת ההרשאה כך שלא ניתן לאמת את התשובה שלך."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"יש להקיש על תכונה כדי להתחיל להשתמש בה:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"בחירת תכונה לשימוש עם לחצן הנגישות"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"בחירת תכונות לשימוש עם מקש הקיצור לעוצמת הקול"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"שירות <xliff:g id="SERVICE_NAME">%s</xliff:g> כבוי"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"עריכת קיצורי הדרך"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"סיום"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"חיבור אוטומטי ללוויין"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"‏אפשר לשלוח ולקבל הודעות ללא רשת סלולרית או רשת Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"רוצה לשלוח הודעות באמצעות תקשורת לוויינית?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏אפשר לשלוח ולקבל הודעות ללא רשת סלולרית או Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"‏לפתיחת Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"מחשבון"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"מפות"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"אפליקציות"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"טביעות האצבע שלך נשחקו ואי אפשר לזהות אותן. צריך להגדיר \'פתיחה בטביעת אצבע\' מחדש."</string>
 </resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4364b28..bcff813 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"パスワードの欄は手書き入力には対応していません"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"戻る"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"入力方法の切り替え"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"入力方法の選択ツールを開く"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"空き容量わずか"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"一部のシステム機能が動作しない可能性があります"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"権限のリクエストを遮っているアプリがあるため、同意の回答を確認できません。"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"使用を開始する機能をタップ:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ユーザー補助機能ボタンで使用する機能の選択"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"音量ボタンのショートカットで使用する機能の選択"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> はオフになっています"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ショートカットの編集"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完了"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"衛星に自動接続しました"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"モバイル ネットワークや Wi-Fi ネットワークを使わずにメッセージを送受信できます"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"衛星通信メッセージを使用しますか?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"モバイル ネットワークや Wi-Fi ネットワークがなくてもメッセージを送受信できます"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"電卓"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"マップ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"アプリ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"指紋を認識できなくなりました。指紋認証をもう一度設定してください。"</string>
 </resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 1e5cf93..0adebb0 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"პაროლის ველში ხელით წერა არ არის მხარდაჭერილი"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"უკან"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"შეყვანის მეთოდის გადართვა"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"შეყვანის მეთოდის ამომრჩევის გახსნა"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"პარამეტრები"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"თავისუფალი ადგილი იწურება"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"აპი მალავს ნებართვის მოთხოვნას, ასე რომ, თქვენი პასუხი ვერ დადასტურდება."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"შეეხეთ ფუნქციას მისი გამოყენების დასაწყებად:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ მარტივი წვდომის ღილაკით"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"აირჩიეთ ფუნქციები, რომელთა გამოყენებაც გსურთ ხმის ღილაკის მალსახმობით"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> გამორთულია"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"მალსახმობების რედაქტირება"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"მზადაა"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებისას აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"სატელიტთან ავტომატურად დაკავშირებულია"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"შეგიძლიათ გაგზავნოთ და მიიღოთ შეტყობინებები მობილური ან Wi-Fi ქსელის გარეშე"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"გსურთ შეტყობინებების სატელიტური მიმოცვლის გამოყენება?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"შეტყობინებების გაგზავნა და მიღება მობილური ან Wi-Fi ქსელის გარეშე"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"კალკულატორი"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"აპლიკაციები"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"თქვენი თითის ანაბეჭდის ამოცნობა ვეღარ ხერხდება. ხელახლა დააყენეთ ანაბეჭდით განბლოკვა."</string>
 </resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 2acf35d..d9ee240 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Құпия сөз өрістерінде қолмен жазу мүмкін емес."</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Артқа"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Енгізу әдісін ауыстыру"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Енгізу әдісін таңдау құралын ашу"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Параметрлер"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Жадта орын азайып барады"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Қолданба рұқсат сұрауын жасырып тұрғандықтан, жауабыңыз расталмайды."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны пайдалана бастау үшін түртіңіз:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"\"Арнайы мүмкіндіктер\" түймесімен қолданылатын функцияларды таңдаңыз"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дыбыс деңгейі пернелері тіркесімімен қолданылатын функцияларды таңдаңыз"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> қызметі өшірулі."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Жылдам пәрмендерді өзгерту"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Дайын"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Жерсерік қызметіне автоматты түрде қосылды"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Мобильдік не Wi-Fi желісіне қосылмастан хабар жібере аласыз және ала аласыз."</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Жерсерік арқылы хабар алмасасыз ба?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хабарландыруларды мобильдік желіге немесе Wi-Fi желісіне қосылмай жіберіңіз және алыңыз."</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Қолданбалар"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Саусағыңыздың іздері бұдан былай танылмайды. Саусақ ізімен ашу функциясын қайта реттеу"</string>
 </resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 7f3c09e..f3dd775 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"មិនអាចប្រើការសរសេរដោយដៃនៅក្នុងកន្លែងបញ្ចូលពាក្យសម្ងាត់បានទេ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ថយក្រោយ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ប្ដូរវិធីសាស្ត្រ​បញ្ចូល"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"បើក​ផ្ទាំងជ្រើសរើស​វិធីបញ្ចូល"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ការកំណត់"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"អស់​ទំហំ​ផ្ទុក"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"មិន​មាន​ទំហំ​ផ្ទុក​​គ្រប់​គ្រាន់​សម្រាប់​ប្រព័ន្ធ​។ សូម​ប្រាកដ​ថា​អ្នក​មាន​ទំហំ​ទំនេរ​ 250MB ហើយ​ចាប់ផ្ដើម​ឡើង​វិញ។"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"កម្មវិធីមួយកំពុងបិទបាំងសំណើសុំការអនុញ្ញាត ដូច្នេះមិនអាចផ្ទៀងផ្ទាត់ការឆ្លើយតបរបស់អ្នកបានទេ។"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ចុចមុខងារណាមួយ ដើម្បចាប់ផ្ដើមប្រើ៖"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ជ្រើសរើស​មុខងារ ដើម្បីប្រើ​ជាមួយ​ប៊ូតុង​ភាពងាយស្រួល"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ជ្រើសរើស​មុខងារ ដើម្បីប្រើ​ជាមួយផ្លូវកាត់គ្រាប់ចុច​កម្រិតសំឡេង"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"បានបិទ <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"កែ​ផ្លូវកាត់"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"រួចរាល់"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញ​អេក្រង់ដើម្បីសុវត្ថិភាព"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ភ្ជាប់ដោយស្វ័យប្រវត្តិទៅផ្កាយរណប"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"អ្នកអាចផ្ញើ និងទទួលសារដោយមិនប្រើបណ្តាញទូរសព្ទចល័ត ឬ Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ប្រើ​ការ​ផ្ញើ​សារ​តាម​ផ្កាយរណប​ឬ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ផ្ញើ និងទទួលសារដោយគ្មានបណ្ដាញ Wi-Fi ឬបណ្ដាញទូរសព្ទចល័ត"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"បើក​កម្មវិធី Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ម៉ាស៊ីនគិតលេខ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ផែនទី"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"កម្មវិធី"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"លែងអាចសម្គាល់ស្នាមម្រាមដៃរបស់អ្នកបានទៀតហើយ។ សូមរៀបចំការដោះសោ​ដោយស្កេន​ស្នាមម្រាមដៃម្ដងទៀត។"</string>
 </resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 4ef70df..a772e32 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ಪಾಸ್‌ವರ್ಡ್ ಫೀಲ್ಡ್‌ಗಳಲ್ಲಿ ಕೈಬರಹವನ್ನು ಬೆಂಬಲಿಸಲಾಗುವುದಿಲ್ಲ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ಹಿಂದಕ್ಕೆ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ಇನ್‌ಪುಟ್ ವಿಧಾನವನ್ನು ಬದಲಿಸಿ"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ಇನ್‌ಪುಟ್ ವಿಧಾನದ ಪಿಕರ್ ಅನ್ನು ತೆರೆಯಿರಿ"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1401,7 +1403,7 @@
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"USB ಪರಿಕರವನ್ನು ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="usb_notification_message" msgid="4715163067192110676">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"ಸಂಪರ್ಕಗೊಂಡಿರುವ ಸಾಧನವನ್ನು ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ. ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ಅನ್‌ಲಾಗ್ ಆಡಿಯೋ ಪರಿಕರ ಪತ್ತೆಯಾಗಿದೆ"</string>
+    <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ಅನ್‌ಲಾಗ್ ಆಡಿಯೋ ಆ್ಯಕ್ಸೆಸರಿ ಪತ್ತೆಯಾಗಿದೆ"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ಲಗತ್ತಿಸಲಾದ ಸಾಧನವು ಈ ಫೋನಿನೊಂದಿಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಇನ್ನಷ್ಟು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಕನೆಕ್ಟ್‌ ಆಗಿದೆ"</string>
     <string name="adb_active_notification_message" msgid="5617264033476778211">"USB ಡೀಬಗ್ ಮಾಡುವಿಕೆ ಆಫ್‌ ಮಾಡಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ಆ್ಯಪ್‌ವೊಂದು ಅನುಮತಿ ವಿನಂತಿಯನ್ನು ಮರೆಮಾಚುತ್ತಿರುವ ಕಾರಣ ನಿಮ್ಮ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಪರಿಶೀಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ವೈಶಿಷ್ಟ್ದ ಬಳಸುವುದನ್ನು ಪ್ರಾರಂಭಿಸಲು ಅದನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಬಟನ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ವಾಲ್ಯೂಮ್ ಕೀ ಶಾರ್ಟ್‌ಕಟ್ ಜೊತೆಗೆ ಬಳಸಲು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ಅನ್ನು ಆಫ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ಶಾರ್ಟ್‌ಕಟ್‌‍ಗಳನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
@@ -2071,7 +2074,7 @@
     <string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"ಸಮಯವನ್ನು ನಮೂದಿಸಲು ಪಠ್ಯದ ನಮೂನೆಗೆ ಬದಲಿಸಿ."</string>
     <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"ಸಮಯವನ್ನು ನಮೂದಿಸಲು ಗಡಿಯಾರದ ನಮೂನೆಗೆ ಬದಲಿಸಿ."</string>
     <string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"ಸ್ವಯಂತುಂಬುವಿಕೆ ಆಯ್ಕೆಗಳು"</string>
-    <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"ಸ್ವಯಂ ಭರ್ತಿಗಾಗಿ ಉಳಿಸಿ"</string>
+    <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"ಸ್ವಯಂ ಭರ್ತಿಗಾಗಿ ಸೇವ್ ಮಾಡಿ"</string>
     <string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"ವಿಷಯಗಳು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಭರ್ತಿಯಾಗಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"ಸ್ವಯಂಭರ್ತಿ ಸಲಹೆಗಳಿಲ್ಲ"</string>
     <string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{ಒಂದು ಸ್ವಯಂ ಭರ್ತಿ ಸಲಹೆಯಿದೆ}one{# ಸ್ವಯಂ ಭರ್ತಿ ಸಲಹೆಗಳಿವೆ}other{# ಸ್ವಯಂ ಭರ್ತಿ ಸಲಹೆಗಳಿವೆ}}"</string>
@@ -2150,7 +2153,7 @@
     <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"ಸರಿ"</string>
     <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ಆಫ್ ಮಾಡಿ"</string>
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
-    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ಅಧಿಸೂಚನೆ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."</string>
+    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"ವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು Android 12 ರಲ್ಲಿ Android ಅಡಾಪ್ಟಿವ್ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿವೆ. ಈ ವೈಶಿಷ್ಟ್ಯವು ಸೂಚಿಸಿದ ಕ್ರಿಯೆಗಳು ಮತ್ತು ಪ್ರತ್ಯುತ್ತರಗಳನ್ನು ತೋರಿಸುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಆಯೋಜಿಸುತ್ತದೆ.\n\nವರ್ಧಿತ ನೋಟಿಫಿಕೇಶನ್‌ಗಳು ಸಂಪರ್ಕ ಹೆಸರುಗಳು ಮತ್ತು ಸಂದೇಶಗಳಂತಹ ವೈಯಕ್ತಿಕ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್‌ ವಿಷಯವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಈ ವೈಶಿಷ್ಟ್ಯವು ಫೋನ್ ಕರೆಗಳಿಗೆ ಉತ್ತರಿಸುವುದು ಮತ್ತು \'ಅಡಚಣೆ ಮಾಡಬೇಡಿ\' ಅನ್ನು ನಿಯಂತ್ರಿಸುವಂತಹ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ವಜಾಗೊಳಿಸಬಹುದು ಅಥವಾ ಪ್ರತಿಕ್ರಿಯಿಸಬಹುದು."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ದೈನಂದಿನ ಸ್ಥಿತಿಯ ಮಾಹಿತಿಯ ನೋಟಿಫಿಕೇಶನ್"</string>
     <string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ಬ್ಯಾಟರಿ ಸೇವರ್ ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ಬ್ಯಾಟರಿ ಬಾಳಿಕೆಯನ್ನು ವಿಸ್ತರಿಸಲು ಬ್ಯಾಟರಿ ಬಳಕೆಯನ್ನು ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್‌‌ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್‌ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ಸ್ಯಾಟಲೈಟ್‌ಗೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ನೀವು ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದೆಯೇ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಬಹುದು ಮತ್ತು ಸ್ವೀಕರಿಸಬಹುದು"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್ ಅನ್ನು ಬಳಸಬೇಕೆ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲದೆಯೇ ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ಸ್ವೀಕರಿಸಿ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ಕ್ಯಾಲ್ಕ್ಯುಲೇಟರ್"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ಆ್ಯಪ್‌ಗಳು"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ಗಳನ್ನು ಇನ್ನು ಮುಂದೆ ಗುರುತಿಸಲಾಗುವುದಿಲ್ಲ. ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ."</string>
 </resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 2cdc59c..0e47e59 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"비밀번호 입력란은 필기 입력을 지원하지 않습니다."</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"뒤로"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"입력 방법 전환"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"입력 방법 선택 도구 열기"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"설정"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"저장 공간이 부족함"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"앱에서 권한 요청을 가려서 응답을 확인할 수 없습니다."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"기능을 사용하려면 탭하세요"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"접근성 버튼으로 사용할 기능 선택"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"볼륨 키 단축키로 사용할 기능 선택"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>이(가) 사용 중지됨"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"단축키 수정"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"완료"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"위성에 자동 연결됨"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"모바일 또는 Wi-Fi 네트워크 없이 메시지를 주고 받을 수 있습니다"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"위성 메시지를 사용하시겠습니까?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"모바일 또는 Wi-Fi 네트워크 없이 메시지 주고받기"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
@@ -2443,8 +2448,9 @@
     <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"이메일"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"음악"</string>
-    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"캘린더"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string>
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"계산기"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"지도"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"애플리케이션"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"지문을 더 이상 인식할 수 없습니다. 지문 잠금 해제를 다시 설정하세요."</string>
 </resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 82cb944..4c2ee5f 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Сырсөз киргизилүүчү жерге кол менен жаза албайсыз"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Артка"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Киргизүү ыкмасын өзгөртүү"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Киргизүү ыкмасын тандоо"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Параметрлер"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Айрым функциялар иштебеши мүмкүн"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системада сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Колдонмо уруксат суроону жашырып койгондуктан, жообуңузду ырастоо мүмкүн эмес."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Функцияны колдонуп баштоо үчүн аны таптап коюңуз:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Атайын мүмкүнчүлүктөр баскычы менен колдонгуңуз келген функцияларды тандаңыз"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Үндү катуулатуу/акырындатуу баскычтары менен кайсы функцияларды иштеткиңиз келет?"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> өчүрүлдү"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Кыска жолдорду түзөтүү"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Бүттү"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Спутникке автоматтык түрдө туташтырылган"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Сиз мобилдик же Wi-Fi тармагы жок эле билдирүүлөрдү жөнөтүп, ала аласыз"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Спутник аркылуу байланышасызбы?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Мобилдик же Wi-Fi тармагына туташпай эле билдирүүлөрдү жөнөтүп, алыңыз"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Эсептегич"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карталар"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Колдонмолор"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Манжаңыздын изи мындан ары таанылбайт. Манжа изи менен ачуу функциясын кайрадан тууралаңыз."</string>
 </resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 1f4bb88d..b627503 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ຊ່ອງຂໍ້ມູນລະຫັດຜ່ານບໍ່ຮອງຮັບການຂຽນດ້ວຍມື"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ກັບຄືນ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ສະຫຼັບວິທີການປ້ອນຂໍ້ມູນ"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ເປີດຕົວເລືອກວິທີການປ້ອນຂໍ້ມູນ"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ການຕັ້ງຄ່າ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"​ບໍ່​ມີ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ພຽງ​ພໍ​ສຳ​ລັບ​ລະ​ບົບ. ກວດ​ສອບ​ໃຫ້​ແນ່​ໃຈ​ວ່າ​ທ່ານ​ມີ​ພື້ນ​ທີ່​ຫວ່າງ​ຢ່າງ​ໜ້ອຍ 250MB ​ແລ້ວລອງ​ໃໝ່."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ແອັບໜຶ່ງກຳລັງປິດບັງຄຳຮ້ອງຂໍການອະນຸຍາດ ດັ່ງນັ້ນຈຶ່ງບໍ່ສາມາດຢັ້ງຢືນຄຳຕອບຂອງທ່ານໄດ້."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ແຕະໃສ່ຄຸນສົມບັດໃດໜຶ່ງເພື່ອເລີ່ມການນຳໃຊ້ມັນ:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບປຸ່ມການຊ່ວຍເຂົ້າເຖິງ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ເລືອກຄຸນສົມບັດເພື່ອໃຊ້ກັບທາງລັດປຸ່ມລະດັບສຽງ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"ປິດ <xliff:g id="SERVICE_NAME">%s</xliff:g> ໄວ້ແລ້ວ"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ແກ້ໄຂທາງລັດ"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ແລ້ວໆ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ເຊື່ອມຕໍ່ກັບດາວທຽມໂດຍອັດຕະໂນມັດ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ທ່ານສາມາດສົ່ງ ແລະ ຮັບຂໍ້ຄວາມໂດຍບໍ່ຕ້ອງໃຊ້ເຄືອຂ່າຍມືຖື ຫຼື Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ໃຊ້ການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມບໍ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ຮັບ ແລະ ສົ່ງຂໍ້ຄວາມໂດຍບໍ່ຕ້ອງໃຊ້ເຄືອຂ່າຍໂທລະສັບມືຖື ຫຼື Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ຈັກຄິດໄລ່"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"ແຜນທີ່"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ແອັບພລິເຄຊັນ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ລະບົບບໍ່ສາມາດຈຳແນກລາຍນິ້ວມືຂອງທ່ານໄດ້ອີກຕໍ່ໄປ. ກະລຸນາຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືອີກຄັ້ງ."</string>
 </resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 04ac536..8eace89 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Rašymas ranka nepalaikomas slaptažodžių laukuose"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atgal"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Perjungti įvesties metodą"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Atidaryti įvesties metodo rinkiklį"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nustatymai"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Mažėja laisvos saugyklos vietos"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Kai kurios sistemos funkcijos gali neveikti"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Programa užstoja leidimo užklausą, todėl negalima patvirtinti jūsų atsakymo."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Norėdami naudoti funkciją, palieskite ją:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Funkcijų, kurioms bus naudojamas pritaikomumo mygtukas, pasirinkimas"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Funkcijų, kurioms bus naudojamas garsumo spartusis klavišas, pasirinkimas"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Paslauga „<xliff:g id="SERVICE_NAME">%s</xliff:g>“ išjungta"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redaguoti sparčiuosius klavišus"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Atlikta"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatiškai prisijungta prie palydovinio ryšio"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Galite siųsti ir gauti pranešimus be mobiliojo ryšio ar „Wi-Fi“ tinklo"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Naudoti susirašinėjimą palydoviniais pranešimais?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Siųskite ir gaukite pranešimus be mobiliojo ryšio ar „Wi-Fi“ tinklo"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Atidaryti programą „Messages“"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kaip tai veikia"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Skaičiuotuvas"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Žemėlapiai"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programos"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Nebegalima atpažinti jūsų piršto atspaudų. Dar kartą nustatykite atrakinimą piršto atspaudu."</string>
 </resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 6d362e7..01fb9da 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Paroļu laukā netiek atbalstīta rokraksta funkcija."</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atpakaļ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Pārslēgt ievades metodi"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Atvērt ievades metodes atlasītāju"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Iestatījumi"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Paliek maz brīvas vietas"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Dažas sistēmas funkcijas var nedarboties."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Kāda lietotne padara atļaujas pieprasījumu nesaprotamu, tāpēc nevar verificēt jūsu atbildi."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Pieskarieties funkcijai, lai sāktu to izmantot"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izvēlieties funkcijas, ko izmantot ar pieejamības pogu"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izvēlieties funkcijas, ko izmantot ar skaļuma pogu saīsni"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Pakalpojums <xliff:g id="SERVICE_NAME">%s</xliff:g> ir izslēgts."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Rediģēt īsinājumtaustiņus"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gatavs"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automātiski izveidots savienojums ar satelītu"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Varat sūtīt un saņemt ziņojumus bez mobilā vai Wi-Fi tīkla."</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Vai izmantot satelīta ziņojumapmaiņu?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Sūtiet un saņemiet ziņojumus bez mobilā vai Wi‑Fi tīkla."</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Atvērt lietotni Ziņojumi"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Darbības principi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulators"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Lietojumprogrammas"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Jūsu pirkstu nospiedumus vairs nevar atpazīt. Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu."</string>
 </resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c764026..dbc4b75 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Ракописот не е поддржан во полињата за лозинка"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Префрлете го методот за внесување"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отворете го избирачот на метод за внесување"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Поставки"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Капацитетот е речиси полн"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некои системски функции може да не работат"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 MB и рестартирајте."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апликација го прикрива барањето за дозвола, па вашиот одговор не може да се потврди."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Допрете на функција за да почнете да ја користите:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Изберете ги функциите што ќе ги користите со копчето за пристапност"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Изберете ги функциите што ќе ги користите со кратенката за копчето за јачина на звук"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> е исклучена"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменете ги кратенките"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Поврзано со сателит автоматски"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Може да испраќате и примате пораки без мобилна или Wi-Fi мрежа"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Да се користи „Сателитска размена на пораки“?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Испраќајте и примајте пораки без мобилна или Wi-Fi мрежа"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликации"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Вашите отпечатоци веќе не може да се препознаат. Поставете „Отклучување со отпечаток“ повторно."</string>
 </resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index e1cff81..2b1c52f 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -204,7 +204,7 @@
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"സ്വകാര്യ സ്പേസ് നീക്കം ചെയ്തു"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"മാനേജ് ചെയ്യപ്പെടുന്ന ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ സ്ഥാപനം സ്വകാര്യ സ്പേസുകൾ അനുവദിക്കുന്നില്ല."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"നിങ്ങളുടെ സ്ഥാപനം ഈ ഉപകരണം മാനേജ് ചെയ്യുകയും നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ആപ്പുകൾക്ക് നിങ്ങളുടെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനാകും"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"കൂടുതലറിയാൻ നിങ്ങളുടെ ഐടി അഡ്‌മിനെ ബന്ധപ്പെടുക"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"ജിയോഫെൻസിംഗ് സേവനം"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"പാസ്‍വേഡ് ഫീൽഡുകളിൽ കയ്യെഴുത്ത് പിന്തുണയ്ക്കുന്നില്ല"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"മടങ്ങുക"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ഇൻപുട്ട് രീതി മാറുക"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ഇൻപുട്ട് രീതി പിക്കർ തുറക്കുക"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ക്രമീകരണം"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"സംഭരണയിടം കഴിഞ്ഞു"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"സിസ്‌റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ഒരു ആപ്പ്, അനുമതി അഭ്യർത്ഥന മറയ്‌ക്കുന്നതിനാൽ നിങ്ങളുടെ പ്രതികരണം പരിശോധിച്ചുറപ്പിക്കാനാകില്ല."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ഉപയോഗിച്ച് തുടങ്ങാൻ ഫീച്ചർ ടാപ്പ് ചെയ്യുക:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ഉപയോഗസഹായി ബട്ടണിന്റെ സഹായത്തോടെ, ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"വോളിയം കീ കുറുക്കുവഴിയിലൂടെ ഉപയോഗിക്കാൻ ഫീച്ചറുകൾ തിരഞ്ഞെടുക്കുക"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ഓഫാക്കിയിരിക്കുന്നു"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"കുറുക്കുവഴികൾ തിരുത്തുക"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"പൂർത്തിയാക്കി"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് ആപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"സാറ്റലൈറ്റിലേക്ക് സ്വയമേവ കണക്റ്റ് ചെയ്തു"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"മൊബൈലോ വൈഫൈ നെറ്റ്‌വർക്കോ ഇല്ലാതെ തന്നെ സന്ദേശങ്ങൾ അയയ്‌ക്കാനും സ്വീകരിക്കാനും നിങ്ങൾക്ക് കഴിയും"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"സാറ്റലൈറ്റ് സന്ദേശമയയ്ക്കൽ ഉപയോഗിക്കണോ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്‌വർക്ക് ഇല്ലാതെ സന്ദേശങ്ങൾ അയയ്ക്കുകയും സ്വീകരിക്കുകയും ചെയ്യുക"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"കാൽക്കുലേറ്റർ"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"മാപ്പുകൾ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റുകൾ ഇനി തിരിച്ചറിയാനാകില്ല. ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക."</string>
 </resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 90aee05..bf2f78c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Нууц үгний талбарт гараар бичихийг дэмждэггүй"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Буцах"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Оруулах аргыг сэлгэх"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Оруулах арга сонгогчийг нээх"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Тохиргоо"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сангийн хэмжээ дутагдаж байна"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Зарим систем функц ажиллахгүй байна"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апп зөвшөөрлийн хүсэлтийг хааж байгаа тул таны хариултыг баталгаажуулах боломжгүй."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Үүнийг ашиглаж эхлэхийн тулд онцлог дээр товшино уу:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Хандалтын товчлуурын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Дууны түвшний түлхүүрийн товчлолын тусламжтай ашиглах онцлогуудыг сонгоно уу"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g>-г унтраалаа"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Товчлолуудыг засах"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Болсон"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын үүднээс аппын контентыг дэлгэц хуваалцахаас нуусан"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Хиймэл дагуулд автоматаар холбогдсон"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Та мобайл эсвэл Wi-Fi сүлжээгүйгээр мессеж илгээх болон хүлээн авах боломжтой"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Хиймэл дагуулаар дамжин мессеж бичихийг ашиглах уу?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Хөдөлгөөнт холбооны эсвэл Wi-Fi сүлжээгүйгээр мессеж илгээх болон хүлээн авах"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Тооны машин"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Газрын зураг"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Аппликэйшн"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Таны хурууны хээг цаашид таних боломжгүй. Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу."</string>
 </resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 9f88b1c..67c3b98 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"पासवर्ड फील्डमध्ये हस्तलेखनाला सपोर्ट नाही"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"मागे जा"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट पद्धत स्विच करा"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"इनपुट पद्धत पिकर उघडा"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिंग्ज"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"संचयन स्थान संपत आहे"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"परवानगी मागणारी विनंती अ‍ॅपमुळे अस्पष्‍ट होत असल्‍याने, तुमच्या प्रतिसादाची पडताळणी केली जाऊ शकत नाही."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"वैशिष्ट्य वापरणे सुरू करण्यासाठी त्यावर टॅप करा:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"अ‍ॅक्सेसिबिलिटी बटणासोबत वापरायची असलेली ॲप्स निवडा"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"व्‍हॉल्‍यूम की शॉर्टकटसोबत वापरायची असलेली ॲप्स निवडा"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> बंद केले आहे"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"शॉर्टकट संपादित करा"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"पूर्ण झाले"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अ‍ॅपमधील आशय लपवला आहे"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"उपग्रहाशी आपोआप कनेक्ट केलेले आहे"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"तुम्ही मोबाइल किंवा वाय-फाय नेटवर्कशिवाय मेसेज पाठवू आणि मिळवू शकता"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"सॅटेलाइट मेसेजिंग वापरायचे आहे का?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल किंवा वाय-फाय नेटवर्कशिवाय मेसेज पाठवणे आणि मिळवणे"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"अ‍ॅप्लिकेशन"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"तुमची फिंगरप्रिंट यापुढे ओळखता येणार नाहीत. फिंगरप्रिंट अनलॉक पुन्हा सेट करा."</string>
 </resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 85a984d..5eac793 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -642,7 +642,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Membenarkan apl membaca lokasi daripada koleksi media anda."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Gunakan biometrik"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Gunakan biometrik atau kunci skrin"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Sahkan itu anda"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Sahkan diri anda"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Gunakan biometrik anda untuk meneruskan"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Gunakan biometrik atau kunci skrin anda untuk meneruskan pengesahan"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Perkakasan biometrik tidak tersedia"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Tulisan tangan tidak disokong dalam medan kata laluan"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Kembali"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Tukar kaedah masukan"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buka pemilih kaedah input"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Tetapan"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Ruang storan semakin berkurangan"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Apl menghalang permintaan kebenaran, maka jawapan anda tidak dapat disahkan."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Ketik ciri untuk mula menggunakan ciri itu:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pilih ciri untuk digunakan dengan butang kebolehaksesan"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pilih ciri untuk digunakan dengan pintasan kekunci kelantangan"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> telah dimatikan"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edit pintasan"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Selesai"</string>
@@ -2235,7 +2238,7 @@
     <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"Anda hanya boleh menghantar mesej SMS daripada apl Messages peribadi anda."</string>
     <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Gunakan penyemak imbas peribadi"</string>
     <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Gunakan penyemak imbas kerja"</string>
-    <string name="miniresolver_call" msgid="6386870060423480765">"Panggil"</string>
+    <string name="miniresolver_call" msgid="6386870060423480765">"Telefon"</string>
     <string name="miniresolver_switch" msgid="8011924662117617451">"Beralih"</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"PIN buka kunci rangkaian SIM"</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"PIN buka kunci subset rangkaian SIM"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Disambungkan secara automatik kepada satelit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Anda boleh menghantar dan menerima mesej tanpa rangkaian mudah alih atau Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Gunakan pemesejan satelit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Hantar dan terima mesej tanpa rangkaian mudah alih atau Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara ciri ini berfungsi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikasi"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Cap jari anda tidak dapat dicam lagi. Sediakan semula Buka Kunci Cap Jari."</string>
 </resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2ed315c..b09539c 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"စကားဝှက်အကွက်များတွင် လက်ရေးကို မပံ့ပိုးပါ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"နောက်သို့"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"လက်ကွက်ပြောင်းရန်"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"လက်ကွက်ရွေးစနစ် ဖွင့်ရန်"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ဆက်တင်များ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"အက်ပ်တစ်ခုသည် ခွင့်ပြုချက်တောင်းဆိုမှုကို ပိတ်နေသဖြင့် သင့်တုံ့ပြန်မှုကို စိစစ်၍မရပါ။"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ဝန်ဆောင်မှုကို စတင်အသုံးပြုရန် တို့ပါ−"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"အများသုံးနိုင်မှု ခလုတ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"အသံခလုတ် ဖြတ်လမ်းလင့်ခ်ဖြင့် အသုံးပြုရန် ဝန်ဆောင်မှုများကို ရွေးပါ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ကို ပိတ်ထားသည်"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ဖြတ်လမ်းများကို တည်းဖြတ်ရန်"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ပြီးပြီ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"လုံခြုံရေးအတွက် အက်ပ်အကြောင်းအရာကို ဖန်သားပြင် မျှဝေခြင်းတွင် ဖျောက်ထားသည်"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ဂြိုဟ်တုနှင့် အလိုအလျောက် ချိတ်ဆက်ထားသည်"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက်မရှိဘဲ မက်ဆေ့ဂျ်များကို ပို့နိုင်၊ လက်ခံနိုင်သည်"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်း သုံးမလား။"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက်မရှိဘဲ မက်ဆေ့ဂျ်များ ပို့နိုင်၊ လက်ခံနိုင်သည်"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ဂဏန်းတွက်စက်"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"အပလီကေးရှင်းများ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"သင့်လက်ဗွေများကို မသိရှိနိုင်တော့ပါ။ ‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ထပ်မံစနစ်ထည့်သွင်းပါ။"</string>
 </resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 7918aa7..8feb6e9 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Håndskrift støttes ikke i passordfelt"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tilbake"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Bytt inndatametode"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Åpne valg av inndatametode"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Innstillinger"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lite ledig lagringsplass"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det er ikke nok lagringsplass for systemet. Kontroller at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app dekker forespørselen om tillatelse, så svaret ditt kan ikke bekreftes."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trykk på en funksjon for å begynne å bruke den:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Velg funksjonene du vil bruke med Tilgjengelighet-knappen"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Velg funksjonene du vil bruke med volumtastsnarveien"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> er slått av"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Endre snarveier"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Ferdig"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatisk tilkoblet satellitt"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan sende og motta meldinger uten mobil- eller wifi-nettverk"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Vil du bruke satellittmeldinger?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Send og motta meldinger uten mobil- eller wifi-nettverk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apper"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Fingeravtrykkene dine kan ikke gjenkjennes lenger. Konfigurer opplåsing med fingeravtrykk på nytt."</string>
 </resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 340358d..9f3530a 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -203,7 +203,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"व्यवस्थापकले यन्त्रलाई व्यक्तिगत प्रयोगका लागि अस्वीकार गर्नुभयो"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"निजी स्पेस हटाइएको छ"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"तपाईंको सङ्गठन आफूले व्यवस्थापन गरेको यो डिभाइसमा निजी स्पेस राख्ने अनुमति दिँदैन।"</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"यन्त्र व्यवस्थित गरिएको छ"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"यो डिभाइस व्यवस्थित गरिएको छ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"तपाईंको संगठनले यस डिभाइसको व्यवस्थापन गर्दछ र नेटवर्क ट्राफिकको अनुगमन गर्न सक्छ। विवरणहरूका लागि ट्याप गर्नुहोस्।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"एपहरूले तपाईंको स्थान प्रयोग गर्न सक्छन्"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"थप जानकारी प्राप्त गर्न आफ्ना IT प्रशासकसँग सम्पर्क गर्नुहोस्"</string>
@@ -1097,7 +1097,7 @@
     <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"यस पृष्ठलाई छोड्नुहोस्"</string>
     <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"यही पृष्ठमा रहनुहोस्"</string>
     <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nके तपाईं यो पेजबाट नेभिगेट गर्न चाहनु हुन्छ भन्ने निश्चत छ?"</string>
-    <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> मार्फत स्वतः भरण गर्नुहोस्‌"</string>
+    <string name="autofill_window_title" msgid="4379134104008111961">"<xliff:g id="SERVICENAME">%1$s</xliff:g> मार्फत अटोफिल गर्नुहोस्‌"</string>
     <string name="permlab_setAlarm" msgid="1158001610254173567">"एउटा आलर्म सेट गर्नुहोस्"</string>
     <string name="permdesc_setAlarm" msgid="2185033720060109640">"स्थापना गरिएको सङ्केत घडी एपमा सङ्केत समय मिलाउन एपलाई अनुमति दिन्छ। केही सङ्केत घडी एपहरूले यो सुविधा कार्यान्वयन नगर्न सक्छन्।"</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"भ्वाइसमेल थप गर्नुहोस्"</string>
@@ -1184,7 +1184,7 @@
     <string name="selectTextMode" msgid="3225108910999318778">"पाठ चयन गर्नुहोस्"</string>
     <string name="undo" msgid="3175318090002654673">"अन्डू गर्नुहोस्"</string>
     <string name="redo" msgid="7231448494008532233">"रिडू गर्नुहोस्"</string>
-    <string name="autofill" msgid="511224882647795296">"स्वतः भरण"</string>
+    <string name="autofill" msgid="511224882647795296">"अटोफिल"</string>
     <string name="textSelectionCABTitle" msgid="5151441579532476940">"पाठ चयनता"</string>
     <string name="addToDictionary" msgid="8041821113480950096">"शब्दकोशमा थप्नुहोस्"</string>
     <string name="deleteText" msgid="4200807474529938112">"मेट्नुहोस्"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"पासवर्ड हाल्ने फिल्डहरूमा हस्तलेखन गर्न मिल्दैन"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"पछाडि"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट विधि बदल्नुहोस्"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"इनपुट विधि पिकर खोल्नुहोस्"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"सेटिङ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"भण्डारण ठाउँ सकिँदै छ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1440,7 +1442,7 @@
     <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string>
     <string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> अन्य एपहरूमा देखिँदैछ"</string>
     <string name="alert_windows_notification_message" msgid="6538171456970725333">"तपाईं <xliff:g id="NAME">%s</xliff:g> ले यो विशेषता प्रयोग नगरेको चाहनुहुन्न भने सेटिङहरू खोली यसलाई निष्क्रिय पार्न ट्याप गर्नुहोस्।"</string>
-    <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"निष्क्रिय पार्नुहोस्"</string>
+    <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"अफ गर्नुहोस्"</string>
     <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"जाँच गर्दै <xliff:g id="NAME">%s</xliff:g>…"</string>
     <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"हालको सामग्री समीक्षा गर्दै"</string>
     <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"मिडिया भण्डारणको जाँच गरिँदै छ"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"कुनै एपका कारण अनुमतिसम्बन्धी अनुरोध बुझ्न कठिनाइ भइरहेकाले तपाईंको जवाफको पुष्टि गर्न सकिएन।"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"कुनै सुविधा प्रयोग गर्न थाल्न उक्त सुविधामा ट्याप गर्नुहोस्:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"पहुँचको बटनमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"भोल्युम कुञ्जीको सर्टकटमार्फत प्रयोग गर्न चाहेका सुविधाहरू छनौट गर्नुहोस्"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> निष्क्रिय पारिएको छ"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"सर्टकटहरू सम्पादन गर्नुहोस्"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"सम्पन्न भयो"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"स्याटलाइटमा स्वतः कनेक्ट गरियो"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"तपाईं मोबाइल वा Wi-Fi नेटवर्कविनै म्यासेज पठाउन र प्राप्त गर्न सक्नुहुन्छ"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा प्रयोग गर्ने हो?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"मोबाइल वा Wi-Fi नेटवर्कविनै म्यासेजहरू पठाउनुहोस् र प्राप्त गर्नुहोस्"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"क्याल्कुलेटर"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"नक्सा"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"एपहरू"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"तपाईंको फिंगरप्रिन्ट अब पहिचान गर्न सकिँदैन। फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्।"</string>
 </resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 45f00cd..8209504 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handschrift wordt niet ondersteund in wachtwoordvelden"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Terug"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Invoermethode wijzigen"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Kiezer voor invoermethoden openen"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Instellingen"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Opslagruimte is bijna vol"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bepaalde systeemfuncties werken mogelijk niet"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -1719,7 +1721,7 @@
     <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Verwijderen"</string>
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
-    <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Wil je blijven luisteren op hoog volume?\n\nHet hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."</string>
+    <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Wil je blijven luisteren op hoog volume?\n\nHet koptelefoonvolume is langer dan de aanbevolen tijd hoog geweest. Dit kan je gehoor beschadigen."</string>
     <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Hard geluid gedetecteerd\n\nHet hoofdtelefoonvolume is hoger dan aanbevolen. Dit kan je gehoor beschadigen."</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Als de snelkoppeling aanstaat, houd je beide volumeknoppen 3 seconden ingedrukt om een toegankelijkheidsfunctie te starten."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Een app dekt het verzoek om rechten af, waardoor je reactie niet kan worden geverifieerd."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tik op een functie om deze te gebruiken:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Functies kiezen voor gebruik met de knop Toegankelijkheid"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Functies kiezen voor gebruik met de sneltoets via de volumeknop"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> is uitgezet"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Snelkoppelingen bewerken"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klaar"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Vanwege beveiligingsrisico\'s is app-content verborgen voor scherm delen"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatisch verbonden met satelliet"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Je kunt berichten sturen en krijgen zonder een mobiel of wifi-netwerk"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Satellietberichten gebruiken?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Stuur en krijg berichten zonder mobiel of wifi-netwerk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Rekenmachine"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Kaarten"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Je vingerafdrukken worden niet meer herkend. Stel Ontgrendelen met vingerafdruk opnieuw in."</string>
 </resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 97cd1da..45b6e9a 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -204,7 +204,7 @@
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ପ୍ରାଇଭେଟ ସ୍ପେସ କାଢ଼ି ଦିଆଯାଇଛି"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ପରିଚାଳିତ ଡିଭାଇସରେ ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଅନୁମତି ଦିଏ ନାହିଁ।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ଡିଭାଇସକୁ ପରିଚାଳନା କରାଯାଉଛି"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରନ୍ତି ଏବଂ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରନ୍ତି। ବିବରଣୀ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରେ ଏବଂ ନେଟୱାର୍କ ଟ୍ରାଫିକକୁ ମନିଟର କରିପାରେ। ବିବରଣୀ ପାଇଁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ଆପଗୁଡ଼ିକ ଆପଣଙ୍କ ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"ଅଧିକ ଜାଣିବାକୁ ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"ଜିଓଫେନସିଂ ସେବା"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ପାସୱାର୍ଡ ଫିଲ୍ଡଗୁଡ଼ିକରେ ହେଣ୍ଡରାଇଟିଂ ସମର୍ଥିତ ନୁହେଁ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ପଛକୁ ଫେରନ୍ତୁ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ଇନପୁଟ ପଦ୍ଧତି ସ୍ୱିଚ କରନ୍ତୁ"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ଇନପୁଟ ପଦ୍ଧତି ପିକରକୁ ଖୋଲନ୍ତୁ"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ସେଟିଂସ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ଷ୍ଟୋରେଜ୍‌ ସ୍ପେସ୍‌ ଶେଷ ହେବାରେ ଲାଗିଛି"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"କିଛି ସିଷ୍ଟମ ପ୍ରକାର୍ଯ୍ୟ କାମ କରିନପାରେ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ସିଷ୍ଟମ୍ ପାଇଁ ପ୍ରର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍‌ ନାହିଁ। ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ଆପଣଙ୍କ ପାଖରେ 250MB ଖାଲି ଜାଗା ଅଛି ଏବଂ ପୁନଃ ଆରମ୍ଭ କରନ୍ତୁ।"</string>
@@ -1668,7 +1670,7 @@
     <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"ଡିଭାଇସରେ ସ୍କ୍ରିନ୍‍ କାଷ୍ଟ କରନ୍ତୁ"</string>
     <string name="media_route_chooser_searching" msgid="6119673534251329535">"ଡିଭାଇସ୍‍ ଖୋଜାଯାଉଛି…"</string>
     <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"ସେଟିଂସ୍"</string>
-    <string name="media_route_controller_disconnect" msgid="7362617572732576959">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
+    <string name="media_route_controller_disconnect" msgid="7362617572732576959">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="media_route_status_scanning" msgid="8045156315309594482">"ସ୍କାନ୍‌ କରୁଛି…"</string>
     <string name="media_route_status_connecting" msgid="5845597961412010540">"ସଂଯୋଗ କରୁଛି..."</string>
     <string name="media_route_status_available" msgid="1477537663492007608">"ଉପଲବ୍ଧ"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ଏକ ଆପ ଅନୁମତି ଅନୁରୋଧକୁ ଅସ୍ପଷ୍ଟ କରୁଛି ତେଣୁ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ଏକ ଫିଚର୍ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ ଏହାକୁ ଟାପ୍ କରନ୍ତୁ:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ଭଲ୍ୟୁମ୍ କୀ ସର୍ଟକଟ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ବନ୍ଦ ହୋଇଯାଇଛି"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ସର୍ଟକଟଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ହୋଇଗଲା"</string>
@@ -2005,7 +2008,7 @@
     <string name="work_mode_off_title" msgid="6367463960165135829">"ୱାର୍କ ଆପ୍ସକୁ ପୁଣି ଚାଲୁ କରିବେ?"</string>
     <string name="work_mode_turn_on" msgid="5316648862401307800">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"ଜରୁରୀକାଳୀନ"</string>
-    <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ଏକ ସ୍କ୍ରିନ୍ ଲକ୍ ସେଟ୍ କରନ୍ତୁ"</string>
+    <string name="set_up_screen_lock_title" msgid="8346083801616474030">"ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"ଆପଣଙ୍କ ପ୍ରାଇଭେଟ ସ୍ପେସ ବ୍ୟବହାର କରିବାକୁ ଏହି ଡିଭାଇସରେ ଏକ ସ୍କ୍ରିନ ଲକ ସେଟ କରନ୍ତୁ"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ସାଟେଲାଇଟ ସହ ସ୍ୱତଃ କନେକ୍ଟ ହୋଇଛି"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ଏକ ମୋବାଇଲ କିମ୍ବା ୱାଇ-ଫାଇ ନେଟୱାର୍କ ବିନା ଆପଣ ମେସେଜ ପଠାଇପାରିବେ ଏବଂ ପାଇପାରିବେ"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ସେଟେଲାଇଟ ମେସେଜିଂକୁ ବ୍ୟବହାର କରିବେ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ଏକ ମୋବାଇଲ କିମ୍ବା ୱାଇ-ଫାଇ ନେଟୱାର୍କ ବିନା ମେସେଜ ପଠାନ୍ତୁ ଏବଂ ପାଆନ୍ତୁ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"କାଲକୁଲେଟର"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ଆପ୍ଲିକେସନଗୁଡ଼ିକ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ଆପଣଙ୍କ ଟିପଚିହ୍ନକୁ ଆଉ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ। ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
 </resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 5cd397e..c6d0b9f 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -204,7 +204,7 @@
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਨੂੰ ਹਟਾਇਆ ਗਿਆ"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਇਸ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਡੀਵਾਈਸ \'ਤੇ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੀ।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦੀ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦੀ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ਐਪਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀਆਂ ਹਨ"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"ਹੋਰ ਜਾਣਨ ਲਈ ਆਪਣੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"ਭੂਗੋਲਿਕ-ਘੇਰੇ ਸੰਬੰਧੀ ਸੇਵਾ"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ਪਾਸਵਰਡ ਖੇਤਰਾਂ ਵਿੱਚ ਲਿਖਾਈ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ਪਿੱਛੇ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ਇਨਪੁੱਟ ਵਿਧੀ ਨੂੰ ਸਵਿੱਚ ਕਰੋ"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ਇਨਪੁੱਟ ਵਿਧੀ ਚੋਣਕਾਰ ਨੂੰ ਖੋਲ੍ਹੋ"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ਕੋਈ ਐਪ ਇਜਾਜ਼ਤ ਸੰਬੰਧੀ ਬੇਨਤੀ ਨੂੰ ਅਸਪਸ਼ਟ ਕਰ ਰਹੀ ਹੈ, ਇਸ ਲਈ ਤੁਹਾਡੇ ਜਵਾਬ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ਕਿਸੇ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਵਰਤਣਾ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਉਸ \'ਤੇ ਟੈਪ ਕਰੋ:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ਅਵਾਜ਼ ਕੁੰਜੀ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਵਰਤਣ ਲਈ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚੁਣੋ"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ਸ਼ਾਰਟਕੱਟਾਂ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ਹੋ ਗਿਆ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"ਸੈਟੇਲਾਈਟ ਨਾਲ ਸਵੈ-ਕਨੈਕਟ ਹੋਇਆ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ਤੁਸੀਂ ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ਕੀ ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਤੋਂ ਬਿਨਾਂ ਸੁਨੇਹੇ ਭੇਜੋ ਅਤੇ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ਐਪਲੀਕੇਸ਼ਨਾਂ"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ਤੁਹਾਡੇ ਫਿੰਗਰਪ੍ਰਿੰਟਾਂ ਦੀ ਹੁਣ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 474eb01..0ae3bbf 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Pola haseł nie obsługują pisma odręcznego"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Wstecz"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Przełącz metodę wprowadzania"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otwórz selektor metody wprowadzania"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ustawienia"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Kończy się miejsce"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektóre funkcje systemu mogą nie działać"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacja zasłania prośbę o uprawnienia, więc nie można zweryfikować Twojej odpowiedzi."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Wybierz funkcję, aby zacząć z niej korzystać:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Wybierz funkcje, których chcesz używać z przyciskiem ułatwień dostępu"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Wybierz funkcje, do których chcesz używać skrótu z klawiszami głośności"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Usługa <xliff:g id="SERVICE_NAME">%s</xliff:g> została wyłączona"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Edytuj skróty"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatycznie połączono z satelitą"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Możesz wymieniać wiadomości bez dostępu do sieci komórkowej lub Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Przesyłać wiadomości przez satelitę?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Wysyłaj i odbieraj wiadomości bez sieci komórkowej czy Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otwórz Wiadomości"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to działa"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapy"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacje"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Nie można już rozpoznać Twoich odcisków palców. Skonfiguruj ponownie odblokowywanie odciskiem palca."</string>
 </resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 0b9cbf3a..e025d19 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Não há suporte para escrita à mão em campos de senha"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Mudar o método de entrada"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o seletor de método de entrada"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configurações"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1400,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para conferir mais opções."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo oculto no compartilhamento de tela por segurança"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automaticamente ao satélite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Você pode enviar e receber mensagens sem um dispositivo móvel ou uma rede Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Usar mensagens via satélite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"As impressões digitais não são mais reconhecidas. Configure o Desbloqueio por impressão digital de novo."</string>
 </resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 94f446a..71940ef 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -205,7 +205,7 @@
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espaço privado removido"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A sua organização não permite espaços privados neste dispositivo gerido."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo é gerido"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede. Toque para obter mais detalhes."</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede. Toque para ver mais detalhes."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"As apps podem aceder à sua localização"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"Contacte o administrador de TI para saber mais."</string>
     <string name="geofencing_service" msgid="3826902410740315456">"Serviço de geofencing"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"A escrita manual não é suportada nos campos de palavras-passe"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Alternar o método de introdução"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o selecionador do método de introdução"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Definições"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Está quase sem espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema poderão não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Uma app está a ocultar o pedido de autorização e, por isso, não é possível validar a sua resposta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque numa funcionalidade para começar a utilizá-la:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha funcionalidades para utilizar com o botão Acessibilidade"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha funcionalidades para usar com o atalho das teclas de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O serviço <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Ligação de satélite estabelecida automaticamente"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Pode enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Quer usar as mensagens por satélite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Envie e receba mensagens sem uma rede móvel ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre a app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicações"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Já não é possível reconhecer as suas impressões digitais. Configure o Desbloqueio por impressão digital novamente."</string>
 </resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 0b9cbf3a..e025d19 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Não há suporte para escrita à mão em campos de senha"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Voltar"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Mudar o método de entrada"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Abrir o seletor de método de entrada"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Configurações"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1400,7 +1402,7 @@
     <string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI via USB ativado"</string>
     <string name="usb_uvc_notification_title" msgid="2030032862673400008">"Dispositivo conectado como Webcam"</string>
     <string name="usb_accessory_notification_title" msgid="1385394660861956980">"Acessório USB conectado"</string>
-    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para conferir mais opções."</string>
+    <string name="usb_notification_message" msgid="4715163067192110676">"Toque para mais opções."</string>
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Carregando dispositivo conectado. Toque para ver mais opções."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Acessório de áudio analógico detectado"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"O dispositivo anexo não é compatível com esse smartphone. Toque para saber mais."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Um app está ocultando a solicitação de permissão e impedindo a verificação da sua resposta."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Toque em um recurso para começar a usá-lo:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Escolha recursos para usar com o botão de acessibilidade"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Escolha recursos para usar com o atalho da tecla de volume"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"O <xliff:g id="SERVICE_NAME">%s</xliff:g> foi desativado"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editar atalhos"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Concluído"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo oculto no compartilhamento de tela por segurança"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Conectado automaticamente ao satélite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Você pode enviar e receber mensagens sem um dispositivo móvel ou uma rede Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Usar mensagens via satélite?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Enviar e receber mensagens sem uma rede móvel ou Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculadora"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapas"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Apps"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"As impressões digitais não são mais reconhecidas. Configure o Desbloqueio por impressão digital de novo."</string>
 </resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index e7f4902..7e5e7c0 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Scrierea de mână nu este acceptată în câmpurile pentru parole"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Înapoi"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Schimbă metoda de introducere"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Deschide selectorul metodei de introducere a textului"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Setări"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Spațiul de stocare aproape ocupat"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigură-te că ai 250 MB de spațiu liber și repornește."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"O aplicație blochează solicitarea de permisiune, așa că răspunsul nu se poate verifica."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atinge o funcție ca să începi să o folosești:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alege funcțiile pe care să le folosești cu butonul de accesibilitate"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alege funcțiile pentru comanda rapidă a butonului de volum"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a fost dezactivat"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editează comenzile rapide"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gata"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"S-a conectat automat la satelit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Poți să trimiți și să primești mesaje fără o rețea mobilă sau Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Folosești mesajele prin satelit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Trimite și primește mesaje fără o rețea mobilă sau Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplicații"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"E posibil ca amprentele tale să nu mai fie recunoscute. Configurează din nou Deblocarea cu amprenta."</string>
 </resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 370d015..22501f2 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Поля для указания пароля не поддерживают рукописный ввод"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Сменить способ ввода"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Выбрать способ ввода"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Настройки"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Недостаточно памяти"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Некоторые функции могут не работать"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1737,7 +1739,7 @@
     <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Предоставить сервису \"<xliff:g id="SERVICE">%1$s</xliff:g>\" полный контроль над устройством?"</string>
     <string name="accessibility_service_warning_description" msgid="291674995220940133">"Полный контроль нужен приложениям для реализации специальных возможностей и не нужен большинству остальных."</string>
     <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Просмотр и контроль экрана"</string>
-    <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Сервис может просматривать весь контент на экране и отображать контент поверх других приложений"</string>
+    <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Сервис может просматривать весь контент на экране и отображать контент поверх других приложений."</string>
     <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Просмотр и выполнение действий"</string>
     <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Сервис может отслеживать ваше взаимодействие с приложениями и датчиками устройства и давать приложениям команды от вашего имени."</string>
     <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Разрешить"</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Невозможно принять ваш ответ, поскольку запрос разрешения скрыт другим приложением."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Выберите, какую функцию использовать:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Выберите функции, которые будут запускаться с помощью кнопки специальных возможностей"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Выберите функции, которые будут запускаться с помощью кнопки регулировки громкости"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервис \"<xliff:g id="SERVICE_NAME">%s</xliff:g>\" отключен."</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Изменить ярлыки"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Для безопасности содержимое приложения при демонстрации экрана скрыто."</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Автоматически подключено к системам спутниковой связи"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Вы можете отправлять и получать сообщения без доступа к мобильной сети или Wi-Fi."</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Включить спутниковый обмен сообщениями?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Отправляйте и получайте сообщения без подключения к мобильной сети или Wi-Fi."</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карты"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Приложения"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ваши отпечатки больше не распознаются. Настройте разблокировку по отпечатку пальца снова."</string>
 </resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index f3f5a700..af835d9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"මුරපද ක්ෂේත්‍ර අත් අකුරු සඳහා සහය නොදක්වයි"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ආපසු"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ආදාන ක්‍රමය මාරු කිරීම"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ආදාන ක්‍රම තෝරකය විවෘත කරන්න"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"සැකසීම්"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ආචයනය ඉඩ ප්‍රමාණය අඩු වී ඇත"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"සමහර පද්ධති කාර්යයන් ක්‍රියා නොකරනු ඇත"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"පද්ධතිය සඳහා ප්‍රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"යෙදුමක් අවසර ඉල්ලීම අඳුරු කරන බැවින්, ඔබේ ප්‍රතිචාරය සත්‍යාපනය කළ නොහැක."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"එය භාවිත කිරීම ආරම්භ කිරීමට විශේෂාංගයක් තට්ටු කරන්න:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ප්‍රවේශ්‍යතා බොත්තම සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"හඬ පරිමා යතුරු කෙටිමග සමග භාවිත කිරීමට විශේෂාංග තෝරා ගන්න"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ක්‍රියාවිරහිත කර ඇත"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"කෙටිමං සංස්කරණ කරන්න"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"නිමයි"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"චන්ද්‍රිකාවට ස්වයංක්‍රීයව සම්බන්ධ වේ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"ඔබට ජංගම හෝ Wi-Fi ජාලයක් නොමැතිව පණිවිඩ යැවීමට සහ ලැබීමට හැක"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"චන්ද්‍රිකා පණිවිඩ යැවීම භාවිතා කරන්න ද?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"ජංගම හෝ Wi-Fi ජාලයකින් තොරව පණිවිඩ යැවීම සහ ලැබීම"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්‍රියා කරන ආකාරය"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"ගණක යන්ත්‍රය"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"සිතියම්"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"යෙදුම්"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ඔබේ ඇඟිලි සලකුණු තවදුරටත් හඳුනාගත නොහැක. ඇඟිලි සලකුණු අගුළු හැරීම නැවත පිහිටුවන්න."</string>
 </resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cd89ebb..3289bbc 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"V poliach pre heslá nie je rukopis podporovaný"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Späť"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Prepnúť metódu vstupu"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Otvoriť výber metódy vstupu"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavenia"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nedostatok ukladacieho priestoru"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Niektoré systémové funkcie nemusia fungovať"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikácia zakrýva žiadosť o povolenie, takže vaša odpoveď sa nedá overiť."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Klepnutím na funkciu ju začnite používať:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Výber funkcií, ktoré chcete používať tlačidlom dostupnosti"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Výber funkcií, ktoré chcete používať klávesovou skratkou"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Služba <xliff:g id="SERVICE_NAME">%s</xliff:g> bola vypnutá"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Upraviť skratky"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Hotovo"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie je z bezpečnostných dôvodov pri zdieľaní obrazovky skrytý"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automaticky pripojené k satelitu"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Správy môžete odosielať a prijímať bez mobilnej siete či siete Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Chcete používať správy cez satelit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Odosielajte a prijímajte správy bez mobilnej siete či siete Wi‑Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvoriť Správy"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ako to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulačka"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mapy"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikácie"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaše odtlačky prstov sa už nedajú rozpoznať. Znova nastavte odomknutie odtlačkom prsta."</string>
 </resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 568718d..99ce046 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -644,7 +644,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Aplikaciji omogoča branje lokacij v predstavnostni zbirki."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Uporaba biometrike"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Uporaba biometrike ali odklepanja s poverilnico"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Preverite, da ste res vi"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Potrdite, da ste res vi"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Za nadaljevanje uporabite biometrični podatek."</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Za nadaljevanje uporabite biometrični podatek ali odklepanje s poverilnico."</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Strojna oprema za biometrične podatke ni na voljo"</string>
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Rokopis ni podprt v poljih za vnos gesla"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Nazaj"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Preklop načina vnosa"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Odpiranje izbirnika načina vnosa"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Nastavitve"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Prostor za shranjevanje bo pošel"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Nekatere sistemske funkcije morda ne delujejo"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Aplikacija zakriva zahtevo za dovoljenje, zato ni mogoče potrditi vašega odgovora."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Če želite začeti uporabljati funkcijo, se je dotaknite:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Izberite funkcije, ki jih želite uporabljati z gumbom za dostopnost"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Izberite funkcije, ki jih želite uporabljati z bližnjico na tipki za glasnost"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Storitev <xliff:g id="SERVICE_NAME">%s</xliff:g> je izklopljena"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Uredi bližnjice"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Končano"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Samodejno vzpostavljena povezava s satelitom"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Sporočila SMS lahko pošiljate in prejemate brez mobilnega omrežja ali omrežja Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Želite uporabiti satelitsko pošiljanje sporočil?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Pošiljanje in prejemanje sporočil brez mobilnega omrežja ali omrežja Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Računalo"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Zemljevidi"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacije"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Vaših prstnih odtisov ni več mogoče prepoznati. Znova nastavite odklepanje s prstnim odtisom."</string>
 </resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 998c960..d8fe109 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Shkrimi i dorës nuk mbështetet në fushat e fjalëkalimeve"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Pas"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Ndërro metodën e hyrjes"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Hap zgjedhësin e metodës së hyrjes"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Cilësimet"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Hapësira ruajtëse po mbaron"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Disa funksione të sistemit mund të mos punojnë"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Një aplikacion po fsheh kërkesën për leje, prandaj përgjigja jote nuk mund të verifikohet."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trokit te një veçori për të filluar ta përdorësh atë:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Zgjidh veçoritë që do të përdorësh me butonin e qasshmërisë"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Zgjidh veçoritë që do të përdorësh me shkurtoren e tastit të volumit"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> është çaktivizuar"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redakto shkurtoret"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"U krye"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"U lidh automatikisht me satelitin"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Mund të dërgosh dhe të marrësh mesazhe pa një rrjet celular apo rrjet Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Të përdoret shkëmbimi i mesazheve nëpërmjet satelitit?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Dërgo dhe merr mesazhe pa një rrjet celular ose Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Hap \"Mesazhet\""</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Si funksionon"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Makina llogaritëse"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Aplikacionet"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Gjurmët e tua të gishtave nuk mund të njihen më. Konfiguro përsëri \"Shkyçjen me gjurmën e gishtit\"."</string>
 </resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a88045c..0451b56 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -643,7 +643,7 @@
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Дозвољава апликацији да чита локације из медијске колекције."</string>
     <string name="biometric_app_setting_name" msgid="3339209978734534457">"Користите биометрију"</string>
     <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Користите биометрију или откључавање екрана"</string>
-    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдите свој идентитет"</string>
+    <string name="biometric_dialog_default_title" msgid="55026799173208210">"Потврдите идентитет"</string>
     <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Користите биометријски податак да бисте наставили"</string>
     <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Користите биометријски податак или откључавање екрана да бисте наставили"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Биометријски хардвер није доступан"</string>
@@ -1195,6 +1195,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Писање руком није подржано у пољима за лозинке"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Промените метод уноса"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Отвори бирач метода уноса"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Подешавања"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Меморијски простор је на измаку"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Неке системске функције можда не функционишу"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1745,7 +1747,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Апликација крије захтев за дозволу, па одговор не може да се верификује."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Додирните неку функцију да бисте почели да је користите:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Одаберите функције које ћете користити са дугметом Приступачност"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Одаберите функције за пречицу тастером јачине звука"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Услуга <xliff:g id="SERVICE_NAME">%s</xliff:g> је искључена"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Измените пречице"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -2423,6 +2426,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Аутоматски повезано са сателитом"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Можете да шаљете и примате поруке без мобилне или WiFi мреже"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Желите да користите сателитску размену порука?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Шаљите и примајте поруке без мобилне или WiFi мреже"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
@@ -2448,4 +2453,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калкулатор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Мапе"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Апликације"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Отисци прстију више не могу да се препознају. Поново подесите откључавање отиском прста."</string>
 </resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 8a8c3f3..9bde6af 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Handskrift stöds inte i lösenordsfält"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Tillbaka"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Byt inmatningsmetod"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Öppna inmatningsmetodsväljaren"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Inställningar"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Lagringsutrymmet börjar ta slut"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"En app döljer behörighetsbegäran så det går inte att verifiera svaret."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Tryck på funktioner som du vill aktivera:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Välj vilka funktioner du vill använda med hjälp av tillgänglighetsknappen"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Välj att funktioner att använda med hjälp av volymknappskortkommandot"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> har inaktiverats"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Redigera genvägar"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Klar"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Automatiskt ansluten till satellit"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Du kan skicka och ta emot meddelanden utan mobil- eller wifi-nätverk"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Vill du använda satellitmeddelanden?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Skicka och ta emot meddelanden utan ett mobil- eller wifi-nätverk"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkylator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Appar"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Det går inte längre att känna igen dina fingeravtryck. Ställ in fingeravtryckslås igen."</string>
 </resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 06a4b20..c286eeb 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -991,7 +991,7 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Ingiza PIN ili kufungua"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Nambari ya PIN uliyoweka si sahihi."</string>
     <string name="keyguard_label_text" msgid="3841953694564168384">"Ili kufungua, bofya Menyu kisha 0."</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Nambari ya dharura"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Namba ya dharura"</string>
     <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Hakuna huduma"</string>
     <string name="lockscreen_screen_locked" msgid="7364905540516041817">"skrini imefungwa."</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Bonyeza Menyu ili kufungua au kupiga simu ya dharura."</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Huwezi kuandika kwa mkono kwenye sehemu za kuweka nenosiri"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Rudi nyuma"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Badilisha mbinu ya kuingiza data"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Fungua kiteua mbinu ya kuingiza data"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Mipangilio"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Programu inazuia ombi la ruhusa kwa hivyo jibu lako haliwezi kuthibitishwa."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Gusa kipengele ili uanze kukitumia:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chagua vipengele vya kutumia na kitufe cha zana za ufikivu"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chagua vipengele vya kutumia na njia ya mkato ya kitufe cha sauti"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Huduma ya <xliff:g id="SERVICE_NAME">%s</xliff:g> imezimwa"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kubadilisha njia za mkato"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Nimemaliza"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Imeunganishwa kiotomatiki na satelaiti"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Unaweza kutuma na kupokea ujumbe bila mtandao wa simu au Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Ungependa kutuma ujumbe kupitia setilaiti?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Tuma na upokee ujumbe bila kutumia mtandao wa simu wala Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Fungua Programu ya Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Utaratibu wake"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kikokotoo"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Ramani"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Programu"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Alama zako za vidole hazitambuliki tena. Weka tena mipangilio ya Kufungua kwa Alama ya Kidole."</string>
 </resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 4f2dee2..b1ce514 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"கடவுச்சொல் புலங்களில் கையெழுத்து ஆதரிக்கப்படவில்லை"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"பின்செல்லும்"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"உள்ளீட்டு முறையை மாற்றும்"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"உள்ளீட்டு முறைத் தேர்வுக் கருவியைத் திறக்கும்"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"அமைப்புகள்"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"சேமிப்பிடம் குறைகிறது"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"அணுகல் கோரிக்கையை ஓர் ஆப்ஸ் மறைப்பதால் உங்கள் பதிலைச் சரிபார்க்க முடியாது."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ஒரு அம்சத்தைப் பயன்படுத்த அதைத் தட்டவும்:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"அணுகல்தன்மை பட்டன் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ஒலியளவு விசை ஷார்ட்கட் மூலம் பயன்படுத்த விரும்பும் அம்சங்களைத் தேர்வுசெய்யுங்கள்"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ஆஃப் செய்யப்பட்டுள்ளது"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ஷார்ட்கட்களை மாற்று"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"முடிந்தது"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"சாட்டிலைட்டுடன் தானாக இணைக்கப்பட்டது"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் நீங்கள் மெசேஜ்களை அனுப்பலாம் பெறலாம்"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"சாட்டிலைட் மெசேஜிங்கைப் பயன்படுத்தவா?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"மொபைல்/வைஃபை நெட்வொர்க் இல்லாமல் மெசேஜ்களை அனுப்பலாம், பெறலாம்"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"கால்குலேட்டர்"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ஆப்ஸ்"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"உங்கள் கைரேகைகளை இனி அடையாளம் காண முடியாது. கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்."</string>
 </resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 5c1f8b8..4b655d1 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -203,8 +203,8 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"వ్యక్తిగత వినియోగం కోసం నిర్వాహకులు పరికరాన్ని తీసి వేశారు"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ప్రైవేట్ స్పేస్ తీసివేయబడింది"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"మీ సంస్థ ఈ మేనేజ్ చేసే పరికరంలో ప్రైవేట్ స్పేస్‌లను అనుమతించదు."</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"పరికరం నిర్వహించబడింది"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"ఈ డివైజ్ మేనేజ్ చేయబడుతోంది"</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ డివైజ్‌ను మేనేజ్ చేస్తోంది, నెట్‌వర్క్ ట్రాఫిక్‌ను మానిటర్ చేసే అవకాశం ఉంది. వివరాల కోసం ట్యాప్ చేయండి."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"యాప్‌లు మీ లొకేషన్‌ను యాక్సెస్ చేయగలవు"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"మరింత తెలుసుకోవడానికి మీ IT అడ్మిన్‌ను కాంటాక్ట్ చేయండి"</string>
     <string name="geofencing_service" msgid="3826902410740315456">"భౌగోళిక సరిహద్దుల సర్వీస్"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"పాస్‌వర్డ్ ఫీల్డ్‌లలో చేతిరాతకు సపోర్ట్ లేదు"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"వెనుకకు"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ఇన్‌పుట్ విధానాన్ని మార్చండి"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"ఇన్‌పుట్ విధాన సెలెక్టర్‌ను తెరవండి"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"సెట్టింగ్‌లు"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"స్టోరేజ్‌ ఖాళీ అయిపోతోంది"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"సిస్టమ్ కోసం తగినంత స్టోరేజ్‌ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ఒక యాప్ అనుమతి రిక్వెస్ట్‌కు అడ్డు తగులుతోంది కాబట్టి మీ సమాధానం వెరిఫై చేయడం సాధ్యం కాదు."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్‌ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"వాల్యూమ్ కీ షార్ట్‌కట్‌తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ఆఫ్ చేయబడింది"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్‌కట్‌లను ఎడిట్ చేయండి"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"పూర్తయింది"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"శాటిలైట్‌కు ఆటోమేటిక్‌గా కనెక్ట్ చేయబడింది"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"మీరు మొబైల్ లేదా Wi-Fi నెట్‌వర్క్ లేకుండా మెసేజ్‌లను పంపవచ్చు, స్వీకరించవచ్చు"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"శాటిలైట్ మెసేజింగ్‌ను ఉపయోగించాలనుకుంటున్నారా?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"మొబైల్ లేదా Wi-Fi నెట్‌వర్క్ లేకుండా మెసేజ్‌లను పంపండి, స్వీకరించండి"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"క్యాలిక్యులేటర్"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"అప్లికేషన్‌లు"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"మీ వేలిముద్రలను ఇకపై గుర్తించడం సాధ్యం కాదు. వేలిముద్ర అన్‌లాక్‌ను మళ్లీ సెటప్ చేయండి."</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9096a03..d979c34 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ช่องรหัสผ่านไม่รองรับการเขียนด้วยลายมือ"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"กลับ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"สลับวิธีการป้อนข้อมูล"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"เปิดเครื่องมือเลือกวิธีการป้อนข้อมูล"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"การตั้งค่า"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"พื้นที่จัดเก็บเหลือน้อย"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"มีแอปหนึ่งบดบังคำขอสิทธิ์ เราจึงยืนยันการตอบกลับของคุณไม่ได้"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"แตะฟีเจอร์เพื่อเริ่มใช้"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"เลือกฟีเจอร์ที่จะใช้กับปุ่มการช่วยเหลือพิเศษ"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"เลือกฟีเจอร์ที่จะใช้กับทางลัดปุ่มปรับระดับเสียง"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"ปิด <xliff:g id="SERVICE_NAME">%s</xliff:g> แล้ว"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"แก้ไขทางลัด"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"เสร็จ"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอแล้วเพื่อความปลอดภัย"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"เชื่อมต่อกับดาวเทียมโดยอัตโนมัติ"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"คุณรับส่งข้อความผ่านดาวเทียมได้โดยไม่ต้องใช้เครือข่ายมือถือหรือ Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"ใช้การรับส่งข้อความผ่านดาวเทียมไหม"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"รับและส่งข้อความโดยไม่ต้องใช้เครือข่ายมือถือหรือ Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"เครื่องคิดเลข"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"แผนที่"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"แอปพลิเคชัน"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"ระบบจะไม่จดจำลายนิ้วมือของคุณอีกต่อไป ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
 </resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9febadb..fbbb32c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Hindi sinusuportahan ang sulat-kamay sa mga field ng password"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Bumalik"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Magpalit ng pamamaraan ng pag-input"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Buksan ang picker ng pamamaraan ng pag-input"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Mga Setting"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nauubusan na ang puwang ng storage"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -1719,7 +1721,7 @@
     <string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Alisin"</string>
     <string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
-    <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Magpatuloy sa pakikinig nang may malakas na volume?\n\nNaging malakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
+    <string name="csd_dose_reached_warning" product="default" msgid="491875107583931974">"Magpatuloy sa pakikinig nang may malakas na volume?\n\nMalakas ang volume nang mas matagal na sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
     <string name="csd_momentary_exposure_warning" product="default" msgid="7730840903435405501">"Naka-detect ng malakas na tunog\n\nMas malakas ang volume kaysa sa inirerekomenda, at posible nitong mapinsala ang pandinig mo"</string>
     <string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
     <string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"May app na pumipigil sa kahilingan sa pahintulot kaya hindi ma-verify ang iyong sagot."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"I-tap ang isang feature para simulan itong gamitin:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Pumili ng mga feature na gagana sa pamamagitan ng button ng accessibility"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Pumili ng mga feature na gagana sa pamamagitan ng shortcut ng volume key"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Na-off ang <xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"I-edit ang mga shortcut"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Tapos na"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Awtomatikong nakakonekta sa satellite"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Puwede kang magpadala at tumanggap ng mga mensahe nang walang mobile o Wi-Fi network"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Gumamit ng satellite messaging?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Magpadala at tumanggap ng mga mensahe nang walang mobile o Wi-Fi network"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Buksan ang Messages"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Paano ito gumagana"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mga Mapa"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Mga Application"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Hindi na makikilala ang iyong mga fingerprint. I-set up ulit ang Pag-unlock Gamit ang Fingerprint."</string>
 </resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9e73054..a015a8f 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"El yazısı, şifre alanlarında desteklenmiyor"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Geri"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Giriş yöntemini değiştir"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Giriş yöntemi seçiciyi aç"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Ayarlar"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Depolama alanı bitiyor"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Bazı sistem işlevleri çalışmayabilir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Bir uygulama, izin isteğini gizlediğinden yanıtınız doğrulanamıyor."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kullanmaya başlamak için bir özelliğe dokunun:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Erişilebilirlik düğmesiyle kullanılacak özellikleri seçin"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Ses tuşu kısayoluyla kullanılacak özellikleri seçin"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> kapatıldı"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Kısayolları düzenle"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Bitti"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Uyduya otomatik olarak bağlandı"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil veya kablosuz ağa bağlı olmadan mesaj alıp gönderebilirsiniz"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Uydu üzerinden mesajlaşma kullanılsın mı?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil veya kablosuz ağ kullanmadan mesaj gönderip alın"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Hesap Makinesi"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Haritalar"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Uygulamalar"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Parmak izleriniz artık tanınamıyor. Parmak İzi Kilidi\'ni tekrar kurun."</string>
 </resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index e7f9ddf..e41d3ce 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1196,6 +1196,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Рукописне введення не підтримується в полях паролів"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Змінити метод введення"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Відкрити засіб вибору методу введення"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Налаштування"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Закінчується пам’ять"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Деякі системні функції можуть не працювати"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1746,7 +1748,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Інший додаток перекриває запит на доступ, тому вашу відповідь не вдається підтвердити."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Натисніть функцію, щоб почати використовувати її:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Виберіть функції для кнопки спеціальних можливостей"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Виберіть функції для комбінації з клавішами гучності"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"Сервіс <xliff:g id="SERVICE_NAME">%s</xliff:g> вимкнено"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Змінити"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Готово"</string>
@@ -2424,6 +2427,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Автоматично підключено до супутника"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Ви можете надсилати й отримувати повідомлення, не використовуючи Wi-Fi або мобільну мережу"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Скористатися супутниковим обміном повідомленнями?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Надсилайте й отримуйте текстові повідомлення без мобільної мережі або Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
@@ -2449,4 +2454,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Калькулятор"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Карти"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Додатки"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Ваші відбитки пальців більше не розпізнаються. Налаштуйте розблокування відбитком пальця повторно."</string>
 </resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 26f8f63..d2f0898 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ہینڈ رائٹنگ پاس ورڈ فیلڈز میں تعاون یافتہ نہیں ہے"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"پیچھے"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"اندراج کا طریقہ سوئچ کریں"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"اندراج کے طریقے کا منتخب کنندہ کھولیں"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"ترتیبات"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"‏سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ایپ اجازت کی درخواست کو مبہم کر رہی ہے لہذا آپ کے جواب کی تصدیق نہیں کی جا سکتی۔"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ایک خصوصیت کا استعمال شروع کرنے کیلئے اسے تھپتھپائیں:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ایکسیسبیلٹی بٹن کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"والیوم کلید کے شارٹ کٹ کے ساتھ استعمال کرنے کیلئے خصوصیات منتخب کریں"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> کو آف کر دیا گیا ہے"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"شارٹ کٹس میں ترمیم کریں"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ہو گیا"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"سٹلائٹ سے خودکار طور پر منسلک ہے"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"‏آپ موبائل یا Wi-Fi نیٹ ورک کے بغیر پیغامات بھیج اور موصول کر سکتے ہیں"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"سیٹلائٹ پیغام رسانی کا استعمال کریں؟"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"‏موبائل یا Wi-Fi نیٹ ورک کے بغیر پیغامات بھیجیں اور موصول کریں"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"کیلکولیٹر"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"نقشے"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ایپلیکیشنز"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"آپ کے فنگر پرنٹس کو مزید پہچانا نہیں جا سکتا۔ فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں۔"</string>
 </resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 1670050..e9bc805 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Parol qatorlarida qoʻlyozma ishlatish imkonsiz"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Orqaga"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Matn kiritish usulini almashtirish"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Kiritish usulini tanlash oynasini ochish"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Sozlamalar"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Xotirada joy yetarli emas"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Ayrim funksiyalar ishlamasligi mumkin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Ilova ruxsat olish talabini berkitmoqda, shu sababdan javobingizni tasdiqlash imkonsiz."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Kerakli funksiyani tanlang"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Qulayliklar tugmasi bilan foydalanish uchun funksiyalarni tanlang"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Tovush tugmasi bilan ishga tushiriladigan funksiyalarni tanlang"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> faolsizlantirildi"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Tezkor tugmalarni tahrirlash"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"OK"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Sputnikka avtomatik ulandi"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Mobil yoki Wi-Fi tarmoqsiz xabarlarni yuborishingiz va qabul qilishingiz mumkin"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Sputnik orqali xabarlashuv ishlatilsinmi?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Mobil yoki Wi-Fi tarmoq blan aloqa yoʻqligida xabar yuboring va qabul qiling"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Kalkulyator"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Xaritalar"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ilovalar"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Barmoq izlaringiz endi tanilmaydi. Barmoq izi bilan ochishni qayta sozlang."</string>
 </resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 6e976a9..510edab 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Các trường mật khẩu không hỗ trợ tính năng Viết tay"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Quay lại"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Chuyển phương thức nhập"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Mở bộ chọn phương thức nhập"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Cài đặt"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Sắp hết dung lượng lưu trữ"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Một số chức năng hệ thống có thể không hoạt động"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"Một ứng dụng đang che khuất yêu cầu quyền này nên chúng tôi không thể xác minh phản hồi của bạn."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Nhấn vào một tính năng để bắt đầu sử dụng:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Chọn các tính năng để dùng với nút hỗ trợ tiếp cận"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Chọn các tính năng để dùng với phím tắt là phím âm lượng"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> đã bị tắt"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Chỉnh sửa phím tắt"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Xong"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Đã tự động kết nối với vệ tinh"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Bạn có thể gửi và nhận tin nhắn mà không cần có mạng di động hoặc mạng Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Sử dụng tính năng nhắn tin qua vệ tinh?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Gửi và nhận tin nhắn mà không cần mạng di động hoặc Wi-Fi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Mở ứng dụng Tin nhắn"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cách hoạt động"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Máy tính"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Bản đồ"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ứng dụng"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Hệ thống không nhận dạng được vân tay của bạn. Hãy thiết lập lại tính năng Mở khoá bằng vân tay."</string>
 </resources>
diff --git a/core/res/res/values-watch/themes_material.xml b/core/res/res/values-watch/themes_material.xml
index 674b3bc..001a0fc 100644
--- a/core/res/res/values-watch/themes_material.xml
+++ b/core/res/res/values-watch/themes_material.xml
@@ -43,11 +43,13 @@
     <!-- Override behaviour to set the theme colours for dialogs, keep them the same. -->
     <style name="ThemeOverlay.Material.Dialog" parent="ThemeOverlay.Material.BaseDialog">
         <item name="android:windowFullscreen">true</item>
+        <item name="backgroundDimEnabled">false</item>
     </style>
 
     <!-- Force the background and floating colours to be the default colours. -->
     <style name="Theme.Material.Dialog" parent="Theme.Material.BaseDialog">
         <item name="android:windowFullscreen">true</item>
+        <item name="backgroundDimEnabled">false</item>
         <item name="colorBackground">@color/background_material_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
@@ -56,6 +58,7 @@
     <!-- Force the background and floating colours to be the default colours. -->
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert">
         <item name="android:windowFullscreen">true</item>
+        <item name="backgroundDimEnabled">false</item>
         <item name="colorBackground">@color/background_material_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
@@ -64,6 +67,7 @@
     <!-- Force the background and floating colours to be the default colours. -->
     <style name="Theme.Material.Light.Dialog" parent="Theme.Material.Light.BaseDialog">
         <item name="android:windowFullscreen">true</item>
+        <item name="backgroundDimEnabled">false</item>
         <item name="colorBackground">@color/background_material_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_material_light</item>
         <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1cc0bfe..bcb54dc 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -203,7 +203,7 @@
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理员已将该设备开放给个人使用"</string>
     <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"私密空间已移除"</string>
     <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"贵组织不允许在此受管设备上使用私密空间。"</string>
-    <string name="network_logging_notification_title" msgid="554983187553845004">"设备为受管理设备"</string>
+    <string name="network_logging_notification_title" msgid="554983187553845004">"设备受到管理"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"应用可以访问您的位置信息"</string>
     <string name="location_changed_notification_text" msgid="7158423339982706912">"详情请与您的 IT 管理员联系"</string>
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"无法在密码字段中使用手写功能"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切换输入法"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"打开输入法选择器"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"设置"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"存储空间不足"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"某些系统功能可能无法正常使用"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"应用遮挡了权限请求,因此我们无法验证您的回复。"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"点按相应功能即可开始使用:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"选择可通过“无障碍”按钮使用的功能"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"选择可通过音量键快捷方式使用的功能"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"已关闭<xliff:g id="SERVICE_NAME">%s</xliff:g>"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"修改快捷方式"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见,屏幕共享画面已隐藏此应用的内容"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"自动连接到卫星"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"您无需使用移动网络或 WLAN 网络便能收发消息"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"使用卫星消息功能?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使没有移动网络或 WLAN 网络,也能收发消息"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"计算器"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地图"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"应用"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"系统无法再识别您的指纹。请重新设置“指纹解锁”功能。"</string>
 </resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index b0c4cfb..875eec0 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"密碼欄位無法使用手寫功能"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切換輸入方法"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"打開輸入方法點選器"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"有應用程式阻擋權限要求,因此系統無法驗證你的回應。"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕按即可開始使用所需功能:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要配搭無障礙功能按鈕使用的功能"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要用音量快速鍵的功能"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> 已關閉"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"編輯捷徑"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連線至衛星"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"你可在沒有流動/Wi-Fi 網絡的情況下收發訊息"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"要使用衛星訊息嗎?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"在沒有流動網絡或 Wi-Fi 網絡的情況下收發短訊"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"計算機"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地圖"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"無法再辨識你的指紋。請重新設定「指紋解鎖」功能。"</string>
 </resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 731905f..efa4c51 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"密碼欄位無法使用手寫功能"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"返回"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"切換輸入法"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"開啟輸入法挑選器"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"設定"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1405,7 +1407,7 @@
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"此外接裝置與這支手機不相容。輕觸即可瞭解詳情。"</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"已連接 USB 偵錯工具"</string>
     <string name="adb_active_notification_message" msgid="5617264033476778211">"輕觸即可關閉 USB 偵錯功能"</string>
-    <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"選取這個選項以停用 USB 偵錯功能。"</string>
+    <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"選取這個選項以停用 USB 偵錯功能"</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"無線偵錯已連線"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"輕觸即可關閉無線偵錯功能"</string>
     <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"選取即可停用無線偵錯功能。"</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"應用程式遮擋了權限要求,因此系統無法驗證你的回覆。"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"輕觸即可開始使用所需功能:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"選擇要搭配無障礙工具按鈕使用的功能"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"選擇要搭配音量快速鍵使用的功能"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"「<xliff:g id="SERVICE_NAME">%s</xliff:g>」已關閉"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"編輯捷徑"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"完成"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面隱藏了這個應用程式的內容"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"已自動連上衛星"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"你可以收發訊息,沒有行動/Wi-Fi 網路也無妨"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"要使用衛星訊息功能嗎?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"即使沒有行動或 Wi-Fi 網路,還是可以收發訊息"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"計算機"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"地圖"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"應用程式"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"系統無法再辨識你的指紋,請重新設定「指紋解鎖」。"</string>
 </resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 2ce2947..67a4361 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1194,6 +1194,8 @@
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Ukubhala ngesandla akusekelwa kuzinkambu zephasiwedi"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Emuva"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Shintsha indlela yokufaka"</string>
+    <string name="input_method_ime_switch_long_click_action_desc" msgid="3161942124116646998">"Vula okokukhetha kwendlela yokufaka"</string>
+    <string name="input_method_switcher_settings_button" msgid="5609835654697108485">"Amasethingi"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Isikhala sokulondoloza siyaphela"</string>
     <string name="low_internal_storage_view_text" msgid="8172166728369697835">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1744,7 +1746,8 @@
     <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"I-app ifihla isicelo semvume ngakho impendulo yakho ayikwazi ukuqinisekiswa."</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Thepha isici ukuqala ukusisebenzisa:"</string>
     <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Khetha izici ongazisebenzisa nenkinobho yokufinyeleleka"</string>
-    <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Khetha izici ongazisebenzisa nesinqamuleli sokhiye wevolumu"</string>
+    <!-- no translation found for accessibility_edit_shortcut_menu_volume_title (2245540598834891500) -->
+    <skip />
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"I-<xliff:g id="SERVICE_NAME">%s</xliff:g> ivaliwe"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Hlela izinqamuleli"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Kwenziwe"</string>
@@ -2422,6 +2425,8 @@
     <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string>
     <string name="satellite_notification_title" msgid="4026338973463121526">"Ixhumeke ngokuzenzakalelayo kusathelayithi"</string>
     <string name="satellite_notification_summary" msgid="5207364139430767162">"Ungathumela futhi wamukele imilayezo ngaphandle kwenethiwekhi yeselula noma ye-Wi-Fi"</string>
+    <string name="satellite_notification_manual_title" msgid="1097504441849466279">"Sebenzisa ukuthumela umyalezo ngesethelayithi?"</string>
+    <string name="satellite_notification_manual_summary" msgid="901206289846283445">"Thumela futhi wamukele imilayezo ngaphandle kwenethiwekhi yeselula noma yeWiFi"</string>
     <string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
@@ -2447,4 +2452,5 @@
     <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Isibali"</string>
     <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Amamephu"</string>
     <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Ama-application"</string>
+    <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"Isigxivizo somunwe wakho ngeke zisakwazi ukubonwa. Setha Ukuvula Ngesigxivizo Somunwe futhi."</string>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index df288f9..38aff75 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3031,9 +3031,8 @@
     <bool name="config_multiuserDelayUserDataLocking">false</bool>
 
     <!-- Whether the device allows full users to start in background visible on displays.
-         Note: this flag does NOT control the Communal Profile, which is not a full user.
-         Should be false for all devices in production. Can be enabled only for development use
-         in automotive vehicles with passenger displays. -->
+         Should be false for most devices, except automotive vehicle with passenger displays.
+         Note: this flag does NOT control the Communal Profile, which is not a full user. -->
     <bool name="config_multiuserVisibleBackgroundUsers">false</bool>
 
     <!-- Whether the device allows full users to start in background visible on the default display.
@@ -3081,6 +3080,11 @@
     <!-- Whether UI for multi user should be shown -->
     <bool name="config_enableMultiUserUI">false</bool>
 
+    <!-- Whether to boot system with the headless system user, i.e. user 0. If set to true,
+         system will be booted with the headless system user, or user 0. It has no effect if device
+         is not in Headless System User Mode (HSUM). -->
+    <bool name="config_bootToHeadlessSystemUser">false</bool>
+
     <!-- Whether multiple admins are allowed on the device. If set to true, new users can be created
          with admin privileges and admin privileges can be granted/revoked from existing users. -->
     <bool name="config_enableMultipleAdmins">false</bool>
@@ -4149,6 +4153,9 @@
     <!-- Indicating if keyboard vibration settings supported or not. -->
     <bool name="config_keyboardVibrationSettingsSupported">false</bool>
 
+    <!-- Indicating if ringtone vibration settings supported or not. -->
+    <bool name="config_ringtoneVibrationSettingsSupported">false</bool>
+
     <!-- If the device should still vibrate even in low power mode, for certain priority vibrations
      (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
     <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
@@ -4982,7 +4989,7 @@
     <!-- URI for camera shutter sound -->
     <string translatable="false" name="config_cameraShutterSound">/product/media/audio/ui/camera_click.ogg</string>
 
-    <!-- URI for default ringtone sound file to be used for silent ringer vibration -->
+    <!-- @deprecated This configuration is no longer used. -->
     <string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
 
     <!-- Default number of notifications from the same app before they are automatically grouped by the OS -->
@@ -7026,6 +7033,10 @@
          {@link InputDevice#SOURCE_ROTARY_ENCODER}s. -->
     <bool name="config_viewBasedRotaryEncoderHapticsEnabled">false</bool>
 
+    <!-- Whether the scroll haptic feedback implementation is enabled for
+    {@link InputDevice#SOURCE_TOUCHSCREEN}s. -->
+    <bool name="config_viewTouchScreenHapticScrollFeedbackEnabled">false</bool>
+
     <!-- Whether the media player is shown on the quick settings -->
     <bool name="config_quickSettingsShowMediaPlayer">true</bool>
 
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 61c7a8c..69437b4 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -237,6 +237,13 @@
     <integer name="config_emergency_call_wait_for_connection_timeout_millis">20000</integer>
     <java-symbol type="integer" name="config_emergency_call_wait_for_connection_timeout_millis" />
 
+    <!-- The time duration in millis after which Telephony will stop waiting for the OFF state
+         from cellular modem, consider the request to power off cellular modem as failed, and thus
+         treat the cellular modem state as ON.
+     -->
+    <integer name="config_satellite_wait_for_cellular_modem_off_timeout_millis">120000</integer>
+    <java-symbol type="integer" name="config_satellite_wait_for_cellular_modem_off_timeout_millis" />
+
     <!-- Indicates the data limit in bytes that can be used for bootstrap sim until factory reset.
          -1 means unlimited. -->
     <integer name="config_esim_bootstrap_data_limit_bytes">-1</integer>
@@ -275,6 +282,11 @@
     <string name="config_satellite_emergency_handover_intent_action" translatable="false"></string>
     <java-symbol type="string" name="config_satellite_emergency_handover_intent_action" />
 
+    <!-- The action of the intent that hidden menu sends to the app to launch demo mode for sos
+     emergency messaging via satellite. -->
+    <string name="config_satellite_demo_mode_sos_intent_action" translatable="false"></string>
+    <java-symbol type="string" name="config_satellite_demo_mode_sos_intent_action" />
+
     <!-- Whether outgoing satellite datagrams should be sent to modem in demo mode. When satellite
          is enabled for demo mode, if this config is enabled, outgoing datagrams will be sent to
          modem; otherwise, success results will be returned. If demo mode is disabled, outgoing
@@ -428,7 +440,19 @@
     <string name="config_satellite_carrier_roaming_esos_provisioned_class" translatable="false"></string>
     <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_class" />
 
-    <!-- Telephony satellite gateway intent for handling carrier roaming to satellite is using ESOS messaging. -->
-    <string name="config_satellite_carrier_roaming_esos_provisioned_intent_action" translatable="false"></string>
-    <java-symbol type="string" name="config_satellite_carrier_roaming_esos_provisioned_intent_action" />
+    <!-- The time duration in minutes to wait before retry validating a possible change
+         in satellite allowed region. The default value is 10 minutes. -->
+    <integer name="config_satellite_delay_minutes_before_retry_validating_possible_change_in_allowed_region">10</integer>
+    <java-symbol type="integer" name="config_satellite_delay_minutes_before_retry_validating_possible_change_in_allowed_region" />
+
+    <!-- The maximum retry count to validate a possible change in satellite allowed region.
+         The default value is 3 minutes. -->
+    <integer name="config_satellite_max_retry_count_for_validating_possible_change_in_allowed_region">3</integer>
+    <java-symbol type="integer" name="config_satellite_max_retry_count_for_validating_possible_change_in_allowed_region" />
+
+    <!-- The time duration in minutes for location query throttle interval.
+         The default value is 10 minutes. -->
+    <integer name="config_satellite_location_query_throttle_interval_minutes">10</integer>
+    <java-symbol type="integer" name="config_satellite_location_query_throttle_interval_minutes" />
+
 </resources>
diff --git a/core/res/res/values/config_tv_external_input_logging.xml b/core/res/res/values/config_tv_external_input_logging.xml
index 72e30be..293a183 100644
--- a/core/res/res/values/config_tv_external_input_logging.xml
+++ b/core/res/res/values/config_tv_external_input_logging.xml
@@ -24,27 +24,39 @@
      entries do not follow the convention, but all new entries should. -->
 
 <resources>
-    <bool name="config_tvExternalInputLoggingDisplayNameFilterEnabled">false</bool>
+    <bool name="config_tvExternalInputLoggingDisplayNameFilterEnabled">true</bool>
 
     <string-array name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames">
-        <item>Chromecast</item>
+        <item>ADT-4</item>
         <item>Chromecast HD</item>
-        <item>SHIELD</item>
-        <item>Roku</item>
-        <item>Roku Express 4</item>
-        <item>Home Theater</item>
         <item>Fire TV Stick</item>
-        <item>PlayStation 5</item>
+        <item>Freebox Player</item>
+        <item>Home Theater</item>
+        <item>Jarvis</item>
         <item>NintendoSwitch</item>
+        <item>onn. 4K Plus S</item>
+        <item>onn. Streaming</item>
+        <item>PlayStation 4</item>
+        <item>PlayStation 5</item>
+        <item>Roku 3</item>
+        <item>Roku Express 4</item>
     </string-array>
 
     <string-array name="config_tvExternalInputLoggingDeviceBrandNames">
-        <item>Chromecast</item>
-        <item>SHIELD</item>
-        <item>Roku</item>
         <item>Apple</item>
+        <item>Chromecast</item>
         <item>Fire TV</item>
-        <item>PlayStation</item>
+        <item>Freebox</item>
+        <item>Google</item>
+        <item>MiBOX</item>
+        <item>Microsoft</item>
         <item>Nintendo</item>
+        <item>NVIDIA</item>
+        <item>onn.</item>
+        <item>PlayStation</item>
+        <item>Roku</item>
+        <item>SHIELD</item>
+        <item>Sony</item>
+        <item>XBOX</item>
     </string-array>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb58339..606c7fd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3280,6 +3280,9 @@
     <!-- Accessibility text for the long click action on the switch input method button. This will
          be used following "Double-tap and hold to..." [CHAR LIMIT=NONE] -->
     <string name="input_method_ime_switch_long_click_action_desc">Open input method picker</string>
+    <!-- Button to access the language settings of the current input method
+         from the Input Method Switcher menu. [CHAR LIMIT=50]-->
+    <string name="input_method_switcher_settings_button">Settings</string>
 
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
@@ -3883,7 +3886,8 @@
 
     <!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. -->
     <string name="select_input_method">Choose input method</string>
-    <!-- Button to access the language settings of the current input method. [CHAR LIMIT=50]-->
+    <!-- Content description of the button to access the language settings of the current input method
+         from the Input Method Switcher menu, for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
     <string name="input_method_language_settings">Language Settings</string>
     <!-- Summary text of a toggle switch to enable/disable use of the IME while a physical
          keyboard is connected -->
@@ -4800,9 +4804,9 @@
     </string>
 
     <!-- Title for accessibility edit shortcut selection menu dialog, and dialog is triggered
-    from volume key shortcut. [CHAR LIMIT=100] -->
+    from volume keys shortcut. [CHAR LIMIT=100] -->
     <string name="accessibility_edit_shortcut_menu_volume_title">Choose features to use with the
-        volume key shortcut
+        volume keys shortcut
     </string>
 
     <!-- Text for showing the warning to user when uncheck the legacy app item in the accessibility
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index c084b4c..dc99634 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1504,26 +1504,26 @@
 
     <!-- @hide -->
     <style name="PointerIconVectorStyleFillGreen">
-        <item name="pointerIconVectorFill">#6DD58C</item>
-        <item name="pointerIconVectorFillInverse">#6DD58C</item>
+        <item name="pointerIconVectorFill">#1AA64A</item>
+        <item name="pointerIconVectorFillInverse">#1AA64A</item>
     </style>
 
     <!-- @hide -->
     <style name="PointerIconVectorStyleFillYellow">
-        <item name="pointerIconVectorFill">#FDD663</item>
-        <item name="pointerIconVectorFillInverse">#FDD663</item>
+        <item name="pointerIconVectorFill">#F55E57</item>
+        <item name="pointerIconVectorFillInverse">#F55E57</item>
     </style>
 
     <!-- @hide -->
     <style name="PointerIconVectorStyleFillPink">
-        <item name="pointerIconVectorFill">#F2B8B5</item>
-        <item name="pointerIconVectorFillInverse">#F2B8B5</item>
+        <item name="pointerIconVectorFill">#F94AAB</item>
+        <item name="pointerIconVectorFillInverse">#F94AAB</item>
     </style>
 
     <!-- @hide -->
     <style name="PointerIconVectorStyleFillBlue">
-        <item name="pointerIconVectorFill">#8AB4F8</item>
-        <item name="pointerIconVectorFillInverse">#8AB4F8</item>
+        <item name="pointerIconVectorFill">#009DC9</item>
+        <item name="pointerIconVectorFillInverse">#009DC9</item>
     </style>
 
     <!-- @hide -->
@@ -1535,7 +1535,7 @@
     <!-- @hide -->
     <style name="PointerIconVectorStyleStrokeBlack">
         <item name="pointerIconVectorStroke">@color/black</item>
-        <item name="pointerIconVectorStrokeInverse">@color/white</item>
+        <item name="pointerIconVectorStrokeInverse">@color/black</item>
     </style>
 
     <!-- @hide -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0d16e9c..4693894 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -363,6 +363,7 @@
   <java-symbol type="bool" name="config_canSwitchToHeadlessSystemUser"/>
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_enableMultipleAdmins"/>
+  <java-symbol type="bool" name="config_bootToHeadlessSystemUser"/>
   <java-symbol type="bool" name="config_omnipresentCommunalUser"/>
   <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
@@ -2131,6 +2132,7 @@
   <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
   <java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" />
   <java-symbol type="bool" name="config_keyboardVibrationSettingsSupported" />
+  <java-symbol type="bool" name="config_ringtoneVibrationSettingsSupported" />
   <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
   <java-symbol type="bool" name="config_ignoreVibrationsOnWirelessCharger" />
   <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
@@ -5465,6 +5467,8 @@
   <!-- For HapticFeedbackConstants configurability defined at HapticFeedbackCustomization -->
   <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
   <java-symbol type="xml" name="haptic_feedback_customization" />
+  <java-symbol type="xml" name="haptic_feedback_customization_source_rotary_encoder" />
+  <java-symbol type="xml" name="haptic_feedback_customization_source_touchscreen" />
 
   <!-- For ActivityManager PSS profiling configurability -->
   <java-symbol type="bool" name="config_am_disablePssProfiling" />
@@ -5487,8 +5491,11 @@
   <java-symbol type="bool" name="config_tvExternalInputLoggingDisplayNameFilterEnabled" />
   <java-symbol type="array" name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames" />
   <java-symbol type="array" name="config_tvExternalInputLoggingDeviceBrandNames" />
+
+  <!-- Scroll Feedback Configs -->
   <java-symbol type="bool" name="config_viewRotaryEncoderHapticScrollFedbackEnabled" />
   <java-symbol type="bool" name="config_viewBasedRotaryEncoderHapticsEnabled" />
+  <java-symbol type="bool" name="config_viewTouchScreenHapticScrollFeedbackEnabled" />
 
   <java-symbol type="bool" name="config_quickSettingsShowMediaPlayer" />
 
@@ -5563,6 +5570,7 @@
   <java-symbol type="string"  name="recs_notification_channel_label"/>
 
   <!-- Priority Modes icons -->
+  <java-symbol type="drawable" name="ic_zen_priority_modes" />
   <java-symbol type="drawable" name="ic_zen_mode_type_bedtime" />
   <java-symbol type="drawable" name="ic_zen_mode_type_driving" />
   <java-symbol type="drawable" name="ic_zen_mode_type_immersive" />
@@ -5572,6 +5580,7 @@
   <java-symbol type="drawable" name="ic_zen_mode_type_schedule_time" />
   <java-symbol type="drawable" name="ic_zen_mode_type_theater" />
   <java-symbol type="drawable" name="ic_zen_mode_type_unknown" />
+  <java-symbol type="drawable" name="ic_zen_mode_type_special_dnd" />
 
   <!-- System notification for background user sound -->
   <java-symbol type="string" name="bg_user_sound_notification_title_alarm" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index f5c6738..d35bfb7 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -131,7 +131,7 @@
         <item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.ProgressBar.Small.Inverse</item>
         <item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.ProgressBar.Large.Inverse</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="seekBarStyle">@style/Widget.DeviceDefault.SeekBar</item>
         <item name="ratingBarStyle">@style/Widget.DeviceDefault.RatingBar</item>
         <item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.RatingBar.Indicator</item>
@@ -212,30 +212,30 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
         <item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
         <item name="panelColorBackground">?attr/colorBackgroundFloating</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -314,28 +314,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -351,7 +351,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -431,28 +431,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -468,7 +468,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -550,28 +550,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -587,7 +587,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -668,28 +668,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -705,7 +705,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -801,28 +801,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -831,7 +831,7 @@
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -911,28 +911,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -948,7 +948,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1027,28 +1027,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1064,7 +1064,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1144,28 +1144,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1181,7 +1181,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1277,28 +1277,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1314,7 +1314,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1395,28 +1395,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1432,7 +1432,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1511,28 +1511,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1548,7 +1548,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1629,28 +1629,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1666,7 +1666,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1746,28 +1746,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1783,7 +1783,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1863,28 +1863,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -1900,7 +1900,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -1980,28 +1980,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -2017,7 +2017,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2097,28 +2097,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -2134,7 +2134,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2218,28 +2218,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -2251,7 +2251,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2336,28 +2336,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -2373,7 +2373,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2451,28 +2451,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -2488,7 +2488,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2642,7 +2642,7 @@
         <item name="progressBarStyleSmallInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Small.Inverse</item>
         <item name="progressBarStyleLargeInverse">@style/Widget.DeviceDefault.Light.ProgressBar.Large.Inverse</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="seekBarStyle">@style/Widget.DeviceDefault.Light.SeekBar</item>
         <item name="ratingBarStyle">@style/Widget.DeviceDefault.Light.RatingBar</item>
         <item name="ratingBarStyleIndicator">@style/Widget.DeviceDefault.Light.RatingBar.Indicator</item>
@@ -2720,28 +2720,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
         <item name="colorPopupBackground">?attr/colorBackgroundFloating</item>
@@ -2821,28 +2821,28 @@
     <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_dark_device_default_dark</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -2858,7 +2858,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -2937,28 +2937,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -2974,7 +2974,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3054,28 +3054,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3091,7 +3091,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3173,28 +3173,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3210,7 +3210,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3291,28 +3291,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3328,7 +3328,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3426,33 +3426,33 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3535,28 +3535,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3572,7 +3572,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3654,28 +3654,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3691,7 +3691,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3774,28 +3774,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3811,7 +3811,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -3895,26 +3895,26 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -3996,26 +3996,26 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4096,28 +4096,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4133,7 +4133,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4217,28 +4217,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4254,7 +4254,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4336,28 +4336,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4373,7 +4373,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4454,28 +4454,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4491,7 +4491,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4571,28 +4571,28 @@
 
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4608,7 +4608,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4688,28 +4688,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4725,7 +4725,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4803,28 +4803,28 @@
     <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_dark_device_default_light</item>
+        <item name="colorPrimaryDark">@color/primary_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -4840,7 +4840,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -4932,21 +4932,21 @@
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
         <!-- Color palette -->
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorPrimary">@color/primary_device_default_settings_light</item>
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorEdgeEffect">@color/edge_effect_device_default_light</item>
 
@@ -5044,16 +5044,16 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
@@ -5066,7 +5066,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5148,21 +5148,21 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5245,26 +5245,26 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_dark_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_dark_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_dark_device_default</item>
-        <item name="colorSurface">@color/surface_dark</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_dark</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_dark</item>
-        <item name="colorSurfaceHeader">@color/surface_header_dark</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_dark</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_dark</item>
+        <item name="colorSurface">@color/system_surface_container_dark</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_dark</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_dark</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_dark</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
-        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackground">@color/system_surface_container_dark</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_dark</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiary">@color/system_outline_dark</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_light</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_light</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_light</item>
 
@@ -5280,7 +5280,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5361,26 +5361,26 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -5471,7 +5471,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5487,26 +5487,26 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -5522,7 +5522,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5606,26 +5606,26 @@
         <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
-        <item name="colorAccentPrimaryVariant">@color/accent_primary_variant_light_device_default</item>
-        <item name="colorAccentSecondaryVariant">@color/accent_secondary_variant_light_device_default</item>
-        <item name="colorAccentTertiaryVariant">@color/accent_tertiary_variant_light_device_default</item>
-        <item name="colorSurface">@color/surface_light</item>
-        <item name="colorSurfaceHighlight">@color/surface_highlight_light</item>
-        <item name="colorSurfaceVariant">@color/surface_variant_light</item>
-        <item name="colorSurfaceHeader">@color/surface_header_light</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</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>
+        <item name="colorAccentSecondaryVariant">@color/system_secondary_container_light</item>
+        <item name="colorAccentTertiaryVariant">@color/system_tertiary_container_light</item>
+        <item name="colorSurface">@color/system_surface_container_light</item>
+        <item name="colorSurfaceHighlight">@color/system_surface_bright_light</item>
+        <item name="colorSurfaceVariant">@color/system_surface_container_high_light</item>
+        <item name="colorSurfaceHeader">@color/system_surface_container_highest_light</item>
         <item name="colorError">@color/error_color_device_default_light</item>
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
-        <item name="textColorPrimary">@color/text_color_primary_device_default_light</item>
-        <item name="textColorSecondary">@color/text_color_secondary_device_default_light</item>
-        <item name="textColorTertiary">@color/text_color_tertiary_device_default_light</item>
-        <item name="textColorPrimaryInverse">@color/text_color_primary_device_default_dark</item>
-        <item name="textColorSecondaryInverse">@color/text_color_secondary_device_default_dark</item>
-        <item name="textColorTertiaryInverse">@color/text_color_tertiary_device_default_dark</item>
-        <item name="textColorOnAccent">@color/text_color_on_accent_device_default</item>
+        <item name="textColorPrimary">@color/system_on_surface_light</item>
+        <item name="textColorSecondary">@color/system_on_surface_variant_light</item>
+        <item name="textColorTertiary">@color/system_outline_light</item>
+        <item name="textColorPrimaryInverse">@color/system_on_surface_dark</item>
+        <item name="textColorSecondaryInverse">@color/system_on_surface_variant_dark</item>
+        <item name="textColorTertiaryInverse">@color/system_outline_dark</item>
+        <item name="textColorOnAccent">@color/system_on_primary_dark</item>
         <item name="colorForeground">@color/foreground_device_default_light</item>
         <item name="colorForegroundInverse">@color/foreground_device_default_dark</item>
 
@@ -5641,7 +5641,7 @@
         <item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
 
         <!-- Progress bar attributes -->
-        <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
+        <item name="colorProgressBackgroundNormal">?attr/materialColorOutline</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
         <!-- Toolbar attributes -->
@@ -5788,9 +5788,9 @@
 
     <style name="ThemeOverlay.DeviceDefault.Accent">
         <item name="colorAccent">@color/accent_device_default_dark</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentSecondary">@color/system_secondary_dark</item>
+        <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
@@ -5863,9 +5863,9 @@
 
     <style name="ThemeOverlay.DeviceDefault.Accent.Light">
         <item name="colorAccent">@color/accent_device_default_light</item>
-        <item name="colorAccentPrimary">@color/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentSecondary">@color/system_secondary_dark</item>
+        <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
@@ -5938,13 +5938,13 @@
 
     <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
     <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
-           parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" />
+        parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" />
 
     <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/accent_primary_device_default</item>
-        <item name="colorAccentSecondary">@color/accent_secondary_device_default</item>
-        <item name="colorAccentTertiary">@color/accent_tertiary_device_default</item>
+        <item name="colorAccentPrimary">@color/system_primary_dark</item>
+        <item name="colorAccentSecondary">@color/system_secondary_dark</item>
+        <item name="colorAccentTertiary">@color/system_tertiary_dark</item>
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
@@ -6016,7 +6016,7 @@
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
-        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackground">@color/system_surface_container_light</item>
         <item name="colorBackgroundFloating">@color/background_device_default_light</item>
         <item name="layout_gravity">center</item>
         <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
diff --git a/core/res/res/xml/haptic_feedback_customization_source_rotary_encoder.xml b/core/res/res/xml/haptic_feedback_customization_source_rotary_encoder.xml
new file mode 100644
index 0000000..7ac0787
--- /dev/null
+++ b/core/res/res/xml/haptic_feedback_customization_source_rotary_encoder.xml
@@ -0,0 +1,18 @@
+<?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.
+  -->
+
+<haptic-feedback-constants/>
diff --git a/core/res/res/xml/haptic_feedback_customization_source_touchscreen.xml b/core/res/res/xml/haptic_feedback_customization_source_touchscreen.xml
new file mode 100644
index 0000000..7ac0787
--- /dev/null
+++ b/core/res/res/xml/haptic_feedback_customization_source_touchscreen.xml
@@ -0,0 +1,18 @@
+<?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.
+  -->
+
+<haptic-feedback-constants/>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index e963caff..92dfe38 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -73,6 +73,7 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.verification.VerificationWithTimeout;
 
 import java.util.ArrayList;
@@ -213,6 +214,18 @@
     }
 
     @Test
+    public void openSession_whenHalThrowsRemoteException() throws Exception {
+        android.hardware.radio.ITunerCallback tunerCallbackMock =
+                mock(android.hardware.radio.ITunerCallback.class);
+        Mockito.doThrow(new RemoteException("HAL service died")).when(mBroadcastRadioMock)
+                .setTunerCallback(any());
+
+        expect.withMessage("Null tuner session with HAL throwing remote exception")
+                .that(mRadioModule.openSession(tunerCallbackMock)).isNull();
+
+    }
+
+    @Test
     public void setConfiguration() throws Exception {
         openAidlClients(/* numClients= */ 1);
 
diff --git a/core/tests/GameManagerTests/Android.bp b/core/tests/GameManagerTests/Android.bp
index 0e3bc65..a252f8b 100644
--- a/core/tests/GameManagerTests/Android.bp
+++ b/core/tests/GameManagerTests/Android.bp
@@ -37,3 +37,10 @@
     certificate: "platform",
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "FrameworksCoreGameManagerTests_android_app",
+    base: "FrameworksCoreGameManagerTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.app"],
+}
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index 94bde68..127dbfd 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -20,6 +20,7 @@
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.BATTERY_STATS"/>
+    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
 
@@ -31,7 +32,8 @@
         <activity android:name=".BatteryConsumerPickerActivity"
                   android:label="Battery Stats"
                   android:launchMode="singleTop"
-                  android:exported="true">
+                  android:exported="true"
+                  android:enabled="false">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.LAUNCHER"/>
@@ -41,5 +43,25 @@
         <activity android:name=".BatteryStatsViewerActivity"
                   android:label="Battery Stats"
                   android:parentActivityName=".BatteryConsumerPickerActivity"/>
+
+        <activity android:name=".TrampolineActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.NoDisplay">
+            <intent-filter>
+                <action android:name="com.android.settings.action.IA_SETTINGS"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+
+            <meta-data android:name="com.android.settings.category"
+                android:value="com.android.settings.category.ia.development" />
+            <meta-data android:name="com.android.settings.title"
+                android:resource="@string/settings_title" />
+            <meta-data android:name="com.android.settings.summary"
+                android:resource="@string/settings_summary" />
+            <meta-data android:name="com.android.settings.group_key"
+                android:value="debug_debugging_category" />
+            <meta-data android:name="com.android.settings.order"
+                android:value="2" />
+        </activity>
     </application>
 </manifest>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
new file mode 100644
index 0000000..c23c148
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settings_title">Launch Battery Stats Viewer</string>
+    <string name="settings_summary">The Battery Stats Viewer will be visible in the Launcher after it is opened once.</string>
+</resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
new file mode 100644
index 0000000..b016488
--- /dev/null
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/TrampolineActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.core.batterystatsviewer;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class TrampolineActivity extends Activity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        showLauncherIcon();
+        launchMainActivity();
+    }
+
+    private void showLauncherIcon() {
+        PackageManager pm = getPackageManager();
+        pm.setComponentEnabledSetting(new ComponentName(this, BatteryConsumerPickerActivity.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    private void launchMainActivity() {
+        startActivity(new Intent(this, BatteryConsumerPickerActivity.class));
+    }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 2bbaf9c..99cbf05 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -21,6 +21,7 @@
     srcs: [
         "DisabledTestApp/src/**/*.java",
         "EnabledTestApp/src/**/*.java",
+        "BinderFrozenStateChangeCallbackTestApp/src/**/*.java",
         "BinderProxyCountingTestApp/src/**/*.java",
         "BinderProxyCountingTestService/src/**/*.java",
         "BinderDeathRecipientHelperApp/src/**/*.java",
@@ -119,6 +120,7 @@
         "libpowermanagertest_jni",
         "libviewRootImplTest_jni",
         "libworksourceparceltest_jni",
+        "libAppOpsTest_jni",
     ],
 
     sdk_version: "core_platform",
@@ -137,8 +139,10 @@
         ":BinderDeathRecipientHelperApp1",
         ":BinderDeathRecipientHelperApp2",
         ":com.android.cts.helpers.aosp",
+        ":BinderFrozenStateChangeCallbackTestApp",
         ":BinderProxyCountingTestApp",
         ":BinderProxyCountingTestService",
+        ":AppThatUsesAppOps",
     ],
 }
 
@@ -164,6 +168,7 @@
         "org.apache.http.legacy",
     ],
     sdk_version: "core_platform",
+    resource_zips: [":FrameworksCoreTests_apks_as_resources"],
 }
 
 // Rules to copy all the test apks to the intermediate raw resource directory
@@ -237,6 +242,7 @@
     static_libs: [
         "core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
         "androidx.core_core",
+        "androidx.core_core-ktx",
         "androidx.annotation_annotation",
         "androidx.test.rules",
         "androidx.test.ext.junit",
@@ -255,8 +261,11 @@
         "src/android/content/pm/UserInfoTest.java",
         "src/android/database/CursorWindowTest.java",
         "src/android/os/**/*.java",
+        "src/android/content/res/*.java",
+        "src/android/content/res/*.kt",
         "src/android/telephony/PinResultTest.java",
         "src/android/util/**/*.java",
+        "src/android/view/DisplayAdjustmentsTests.java",
         "src/android/view/DisplayTest.java",
         "src/android/view/DisplayInfoTest.java",
         "src/com/android/internal/logging/**/*.java",
@@ -274,6 +283,10 @@
         ":FrameworksCoreTests-helpers",
         ":FrameworksCoreTestDoubles-sources",
     ],
+    exclude_srcs: [
+        "src/android/content/res/FontScaleConverterActivityTest.java",
+    ],
+    resource_apk: "FrameworksCoreTests-resonly",
     aidl: {
         generate_get_transaction_name: true,
         local_include_dirs: ["aidl"],
@@ -792,3 +805,11 @@
     include_annotations: ["android.platform.test.annotations.PlatinumTest"],
     exclude_annotations: FLAKY_OR_IGNORED,
 }
+
+test_module_config {
+    name: "FrameworksCoreTests_android_tracing",
+    base: "FrameworksCoreTests",
+    team: "trendy_team_windowing_tools",
+    test_suites: ["device-tests"],
+    include_filters: ["android.tracing"],
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index fc3c2f3..da7da7d 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -19,6 +19,8 @@
           package="com.android.frameworks.coretests"
           android:sharedUserId="com.android.uid.test">
 
+    <attribution android:tag="testAttribution" android:label="@string/testAttributionLabel" />
+
     <permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED"
         android:protectionLevel="normal"
             android:label="@string/permlab_testGranted"
@@ -41,6 +43,7 @@
     <uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
     <uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
@@ -167,6 +170,9 @@
     <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
 
+    <!-- AppOpsLoggingTest permissions -->
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+
     <application
         android:theme="@style/Theme"
         android:supportsRtl="true"
@@ -1654,15 +1660,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.widget.TextViewContextMenuActivity"
-                  android:screenOrientation="locked"
-                  android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="android.animation.AnimatorSetActivity"
                   android:screenOrientation="locked"
                   android:exported="true">
@@ -1672,14 +1669,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.content.res.ResourceCacheActivity"
-                  android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <activity
             android:name="android.print.test.PrintDocumentActivity"
             android:theme="@style/Theme" />
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index bf2a5b8..b1f1e2c 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -22,8 +22,10 @@
         <option name="test-file-name" value="FrameworksCoreTests.apk" />
         <option name="test-file-name" value="BinderDeathRecipientHelperApp1.apk" />
         <option name="test-file-name" value="BinderDeathRecipientHelperApp2.apk" />
+        <option name="test-file-name" value="BinderFrozenStateChangeCallbackTestApp.apk" />
         <option name="test-file-name" value="BinderProxyCountingTestApp.apk" />
         <option name="test-file-name" value="BinderProxyCountingTestService.apk" />
+        <option name="test-file-name" value="AppThatUsesAppOps.apk" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/core/tests/coretests/AppThatUsesAppOps/Android.bp b/core/tests/coretests/AppThatUsesAppOps/Android.bp
new file mode 100644
index 0000000..6266435
--- /dev/null
+++ b/core/tests/coretests/AppThatUsesAppOps/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_team: "trendy_team_android_permissions",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+    name: "AppThatUsesAppOps",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "coretests-aidl",
+        "truth",
+        "junit",
+    ],
+}
diff --git a/core/tests/coretests/AppThatUsesAppOps/AndroidManifest.xml b/core/tests/coretests/AppThatUsesAppOps/AndroidManifest.xml
new file mode 100644
index 0000000..7c8d2f2
--- /dev/null
+++ b/core/tests/coretests/AppThatUsesAppOps/AndroidManifest.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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.app.appops.appthatusesappops">
+    <attribution android:tag="testAttribution" android:label="@string/testAttributionLabel" />
+
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+
+    <application
+            android:attributionsAreUserVisible="true">
+        <service android:name=".AppOpsUserService" android:exported="true" />
+    </application>
+</manifest>
diff --git a/core/tests/coretests/AppThatUsesAppOps/res/values/strings.xml b/core/tests/coretests/AppThatUsesAppOps/res/values/strings.xml
new file mode 100644
index 0000000..f2127fc
--- /dev/null
+++ b/core/tests/coretests/AppThatUsesAppOps/res/values/strings.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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="testAttributionLabel">An attribution</string>
+</resources>
diff --git a/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt b/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt
new file mode 100644
index 0000000..48053c1
--- /dev/null
+++ b/core/tests/coretests/AppThatUsesAppOps/src/android/app/appops/appthatusesappops/AppOpsUserService.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.app.appops.appthatusesappops
+
+import android.app.AppOpsManager
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AsyncNotedAppOp
+import android.app.Service
+import android.app.SyncNotedAppOp
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import com.android.frameworks.coretests.aidl.IAppOpsUserClient
+import com.android.frameworks.coretests.aidl.IAppOpsUserService
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+
+private const val LOG_TAG = "AppOpsUserService"
+private const val TIMEOUT_MILLIS = 10000L
+
+class AppOpsUserService : Service() {
+    private val testUid by lazy {
+        packageManager.getPackageUid("com.android.frameworks.coretests", 0)
+    }
+
+    /**
+     * Make sure that a lambda eventually finishes without throwing an exception.
+     *
+     * @param r The lambda to run.
+     * @param timeout the maximum time to wait
+     *
+     * @return the return value from the lambda
+     *
+     * @throws NullPointerException If the return value never becomes non-null
+     */
+    fun <T> eventually(timeout: Long = TIMEOUT_MILLIS, r: () -> T): T {
+        val start = System.currentTimeMillis()
+
+        while (true) {
+            try {
+                return r()
+            } catch (e: Throwable) {
+                val elapsed = System.currentTimeMillis() - start
+
+                if (elapsed < timeout) {
+                    Log.d(LOG_TAG, "Ignoring exception", e)
+
+                    Thread.sleep(minOf(100, timeout - elapsed))
+                } else {
+                    throw e
+                }
+            }
+        }
+    }
+
+    override fun onBind(intent: Intent?): IBinder {
+        return object : IAppOpsUserService.Stub() {
+            private val appOpsManager = getSystemService(AppOpsManager::class.java)!!
+
+            // Collected note-op calls inside of this process
+            private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+            private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+            private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+            private fun setNotedAppOpsCollector() {
+                appOpsManager.setOnOpNotedCallback(mainExecutor,
+                        object : AppOpsManager.OnOpNotedCallback() {
+                            override fun onNoted(op: SyncNotedAppOp) {
+                                noted.add(op to Throwable().stackTrace)
+                            }
+
+                            override fun onSelfNoted(op: SyncNotedAppOp) {
+                                selfNoted.add(op to Throwable().stackTrace)
+                            }
+
+                            override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+                                asyncNoted.add(asyncOp)
+                            }
+                        })
+            }
+
+            init {
+                try {
+                    appOpsManager.setOnOpNotedCallback(null, null)
+                } catch (ignored: IllegalStateException) {
+                }
+                setNotedAppOpsCollector()
+            }
+
+            /**
+             * Cheapo variant of {@link ParcelableException}
+             */
+            inline fun forwardThrowableFrom(r: () -> Unit) {
+                try {
+                    r()
+                } catch (t: Throwable) {
+                    val sw = StringWriter()
+                    t.printStackTrace(PrintWriter(sw))
+
+                    throw IllegalArgumentException("\n" + sw.toString() + "called by")
+                }
+            }
+
+            override fun callApiThatNotesSyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteSyncOpNative()
+
+                    // All native notes will be reported as async notes
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteNonPermissionSyncOpNative()
+
+                    // All native notes will be reported as async notes
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callOnewayApiThatNotesSyncOpNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOnewayNative()
+
+                    // There is no return value from a one-way call, hence async note is the only
+                    // option
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteSyncOpOtherUidNative()
+
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                    assertThat(asyncNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpNativelyAndCheckLog(client: IAppOpsUserClient) {
+                forwardThrowableFrom {
+                    client.noteAsyncOpNative()
+
+                    eventually {
+                        assertThat(asyncNoted.map { it.op }).containsExactly(OPSTR_COARSE_LOCATION)
+                    }
+                    assertThat(noted).isEmpty()
+                    assertThat(selfNoted).isEmpty()
+                }
+            }
+
+            override fun callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+                client: IAppOpsUserClient
+            ) {
+                forwardThrowableFrom {
+                    client.noteAsyncOpNativeWithCustomMessage()
+
+                    eventually {
+                        assertThat(asyncNoted[0].notingUid).isEqualTo(testUid)
+                        assertThat(asyncNoted[0].message).isEqualTo("native custom msg")
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
new file mode 100644
index 0000000..de97dda
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/Android.bp
@@ -0,0 +1,34 @@
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+    name: "BinderFrozenStateChangeCallbackTestApp",
+
+    static_libs: ["coretests-aidl"],
+    srcs: ["**/*.java"],
+
+    platform_apis: true,
+    certificate: "platform",
+
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..29c8f55
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/AndroidManifest.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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.bfscctestapp">
+
+    <application>
+        <service android:name=".BfsccTestAppCmdService"
+                 android:exported="true"/>
+    </application>
+</manifest>
diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java
new file mode 100644
index 0000000..77e8a40
--- /dev/null
+++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.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.frameworks.coretests.bfscctestapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class BfsccTestAppCmdService extends Service {
+    private IBfsccTestAppCmdService.Stub mBinder = new IBfsccTestAppCmdService.Stub() {
+        private final LinkedBlockingQueue<IBinder.IFrozenStateChangeCallback.State> mNotifications =
+                new LinkedBlockingQueue<>();
+
+        @Override
+        public void listenTo(IBinder binder) throws RemoteException {
+            binder.addFrozenStateChangeCallback(
+                    (IBinder who, IBinder.IFrozenStateChangeCallback.State state)
+                            -> mNotifications.offer(state));
+        }
+
+        @Override
+        public boolean[] waitAndConsumeNotifications() {
+            List<Boolean> results = new ArrayList<>();
+            try {
+                IBinder.IFrozenStateChangeCallback.State state =
+                        mNotifications.poll(5, TimeUnit.SECONDS);
+                if (state != null) {
+                    results.add(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+                }
+            } catch (InterruptedException e) {
+                return null;
+            }
+            while (mNotifications.size() > 0) {
+                results.add(mNotifications.poll()
+                        == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+            }
+            boolean[] convertedResults = new boolean[results.size()];
+            for (int i = 0; i < results.size(); i++) {
+                convertedResults[i] = results.get(i).booleanValue();
+            }
+            return convertedResults;
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
index 41b4c69..09d79a6 100644
--- a/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
+++ b/core/tests/coretests/BinderProxyCountingTestService/src/com/android/frameworks/coretests/binderproxycountingtestservice/BinderProxyCountingService.java
@@ -50,4 +50,4 @@
     public IBinder onBind(Intent intent) {
         return mBinder;
     }
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS
index b669e3b..6aefb63 100644
--- a/core/tests/coretests/OWNERS
+++ b/core/tests/coretests/OWNERS
@@ -4,3 +4,4 @@
 per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
 per-file SurfaceControlRegistryTests.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file VintfObjectTest.java = file:platform/system/libvintf:/OWNERS
+per-file AppOpsLoggingTest.kt,AppOpsLoggingTest.cpp,IAppOps*.aidl,AppThatUsesAppOps/* = file:/core/java/android/permission/OWNERS
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl
new file mode 100644
index 0000000..68b393c0
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserClient.aidl
@@ -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 com.android.frameworks.coretests.aidl;
+
+interface IAppOpsUserClient {
+    void noteSyncOpNative();
+    void noteNonPermissionSyncOpNative();
+    oneway void noteSyncOpOnewayNative();
+    void noteSyncOpOtherUidNative();
+    void noteAsyncOpNative();
+    void noteAsyncOpNativeWithCustomMessage();
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl
new file mode 100644
index 0000000..f5673c4
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IAppOpsUserService.aidl
@@ -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.frameworks.coretests.aidl;
+
+import com.android.frameworks.coretests.aidl.IAppOpsUserClient;
+
+interface IAppOpsUserService {
+    void callApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callOnewayApiThatNotesSyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(in IAppOpsUserClient client);
+    void callApiThatNotesAsyncOpNativelyAndCheckLog(in IAppOpsUserClient client);
+}
diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
new file mode 100644
index 0000000..d8d7dc4
--- /dev/null
+++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/IBfsccTestAppCmdService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.frameworks.coretests.aidl;
+
+interface IBfsccTestAppCmdService {
+   void listenTo(IBinder binder);
+   boolean[] waitAndConsumeNotifications();
+}
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index 538e7f3..d6379ca 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -91,3 +91,23 @@
     ],
     gtest: false,
 }
+
+cc_test_library {
+    name: "libAppOpsTest_jni",
+    srcs: ["AppOpsLoggingTest*.cpp"],
+    shared_libs: [
+        "libbinder",
+        "libpermission",
+        "libutils",
+        "liblog",
+    ],
+
+    header_libs: ["jni_headers"],
+    stl: "libc++_static",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    gtest: false,
+}
diff --git a/core/tests/coretests/jni/AppOpsLoggingTest.cpp b/core/tests/coretests/jni/AppOpsLoggingTest.cpp
new file mode 100644
index 0000000..98707ad
--- /dev/null
+++ b/core/tests/coretests/jni/AppOpsLoggingTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+#include <jni.h>
+#include <binder/AppOpsManager.h>
+#include <utils/String16.h>
+
+using namespace android;
+
+#include "android/log.h"
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "AppOpsLoggingTest"
+
+// Note op from native code
+extern "C" JNIEXPORT void JNICALL
+Java_android_app_AppOpsLoggingTestKt_nativeNoteOp(JNIEnv* env, jobject obj,
+        jint op, jint uid, jstring jCallingPackageName, jstring jAttributionTag, jstring jMessage) {
+    AppOpsManager appOpsManager;
+
+    const char *nativeCallingPackageName = env->GetStringUTFChars(jCallingPackageName, 0);
+    String16 callingPackageName(nativeCallingPackageName);
+
+    const char *nativeAttributionTag;
+    std::optional<String16> attributionTag;
+    if (jAttributionTag != nullptr) {
+        nativeAttributionTag = env->GetStringUTFChars(jAttributionTag, 0);
+        attributionTag = String16(nativeAttributionTag);
+    }
+
+    const char *nativeMessage;
+    String16 message;
+    if (jMessage != nullptr) {
+        nativeMessage = env->GetStringUTFChars(jMessage, 0);
+        message = String16(nativeMessage);
+    }
+
+    appOpsManager.noteOp(op, uid, callingPackageName, attributionTag, message);
+
+    env->ReleaseStringUTFChars(jCallingPackageName, nativeCallingPackageName);
+
+    if (jAttributionTag != nullptr) {
+        env->ReleaseStringUTFChars(jAttributionTag, nativeAttributionTag);
+    }
+
+    if (jMessage != nullptr) {
+        env->ReleaseStringUTFChars(jMessage, nativeMessage);
+    }
+}
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 09e1c69..209fb10 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -161,4 +161,7 @@
 
     <!-- Html description of the accessibility shortcut [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_html_description">Accessibility shortcut html description</string>
+
+    <!-- Attribution tag label [CHAR LIMIT=NONE] -->
+    <string name="testAttributionLabel">An attribution</string>
 </resources>
diff --git a/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt b/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt
new file mode 100644
index 0000000..a10d6a9
--- /dev/null
+++ b/core/tests/coretests/src/android/app/AppOpsLoggingTest.kt
@@ -0,0 +1,323 @@
+/*
+ * 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.app
+
+import android.app.AppOpsManager.OPSTR_ACCESS_ACCESSIBILITY
+import android.app.AppOpsManager.OPSTR_COARSE_LOCATION
+import android.app.AppOpsManager.OnOpNotedCallback
+import android.app.AppOpsManager.strOpToOp
+import android.content.BroadcastReceiver
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Intent
+import android.content.ServiceConnection
+import android.location.LocationManager
+import android.os.Binder
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Process
+import android.platform.test.annotations.AppModeFull
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.frameworks.coretests.aidl.IAppOpsUserClient
+import com.android.frameworks.coretests.aidl.IAppOpsUserService
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit.MILLISECONDS
+
+private const val LOG_TAG = "AppOpsLoggingTest"
+
+private const val TEST_SERVICE_PKG = "android.app.appops.appthatusesappops"
+private const val TIMEOUT_MILLIS = 10000L
+private const val TEST_ATTRIBUTION_TAG = "testAttribution"
+
+private external fun nativeNoteOp(
+    op: Int,
+    uid: Int,
+    packageName: String,
+    attributionTag: String? = null,
+    message: String? = null
+)
+
+@AppModeFull(reason = "Test relies on other app to connect to. Instant apps can't see other apps")
+class AppOpsLoggingTest {
+
+    private val context = InstrumentationRegistry.getInstrumentation().targetContext as Context
+    private val appOpsManager = context.getSystemService(AppOpsManager::class.java)!!
+
+    private val myUid = Process.myUid()
+    private val myUserHandle = Process.myUserHandle()
+    private val myPackage = context.packageName
+
+    private var wasLocationEnabled = false
+
+    private lateinit var testService: IAppOpsUserService
+    private lateinit var serviceConnection: ServiceConnection
+
+    // Collected note-op calls inside of this process
+    private val noted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+    private val selfNoted = mutableListOf<Pair<SyncNotedAppOp, Array<StackTraceElement>>>()
+    private val asyncNoted = mutableListOf<AsyncNotedAppOp>()
+
+    @Before
+    fun setLocationEnabled() {
+        val locationManager = context.getSystemService(LocationManager::class.java)!!
+        wasLocationEnabled = locationManager.isLocationEnabled
+        locationManager.setLocationEnabledForUser(true, myUserHandle)
+    }
+
+    @After
+    fun restoreLocationEnabled() {
+        val locationManager = context.getSystemService(LocationManager::class.java)!!
+        locationManager.setLocationEnabledForUser(wasLocationEnabled, myUserHandle)
+    }
+
+    @Before
+    fun loadNativeCode() {
+        System.loadLibrary("AppOpsTest_jni")
+    }
+
+    @Before
+    fun setNotedAppOpsCollectorAndClearCollectedNoteOps() {
+        setNotedAppOpsCollector()
+        clearCollectedNotedOps()
+    }
+
+    @Before
+    fun connectToService() {
+        val serviceIntent = Intent()
+        serviceIntent.component = ComponentName(TEST_SERVICE_PKG,
+            "$TEST_SERVICE_PKG.AppOpsUserService"
+        )
+
+        val newService = CompletableFuture<IAppOpsUserService>()
+        serviceConnection = object : ServiceConnection {
+            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+                newService.complete(IAppOpsUserService.Stub.asInterface(service))
+            }
+
+            override fun onServiceDisconnected(name: ComponentName?) {
+                fail("test service disconnected")
+            }
+        }
+
+        context.bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE)
+        testService = newService.get(TIMEOUT_MILLIS, MILLISECONDS)
+    }
+
+    private fun clearCollectedNotedOps() {
+        noted.clear()
+        selfNoted.clear()
+        asyncNoted.clear()
+    }
+
+    private fun setNotedAppOpsCollector() {
+        appOpsManager.setOnOpNotedCallback(
+            { it.run() },
+                object : OnOpNotedCallback() {
+                    override fun onNoted(op: SyncNotedAppOp) {
+                        Log.i("OPALA", "sync op: $, stack: $".format(op, Throwable().stackTrace))
+                        noted.add(op to Throwable().stackTrace)
+                    }
+
+                    override fun onSelfNoted(op: SyncNotedAppOp) {
+                        Log.i("OPALA", "self op: $, stack: $".format(op, Throwable().stackTrace))
+                        selfNoted.add(op to Throwable().stackTrace)
+                    }
+
+                    override fun onAsyncNoted(asyncOp: AsyncNotedAppOp) {
+                        Log.i("OPALA", "async op: $".format(asyncOp))
+                        asyncNoted.add(asyncOp)
+                    }
+                })
+    }
+
+    private inline fun rethrowThrowableFrom(r: () -> Unit) {
+        try {
+            r()
+        } catch (e: Throwable) {
+            throw e.cause ?: e
+        }
+    }
+
+    private fun <T> eventually(timeout: Long = TIMEOUT_MILLIS, r: () -> T): T {
+        val start = System.currentTimeMillis()
+
+        while (true) {
+            try {
+                return r()
+            } catch (e: Throwable) {
+                val elapsed = System.currentTimeMillis() - start
+
+                if (elapsed < timeout) {
+                    Log.d(LOG_TAG, "Ignoring exception", e)
+
+                    Thread.sleep(minOf(100, timeout - elapsed))
+                } else {
+                    throw e
+                }
+            }
+        }
+    }
+
+    @Test
+    fun noteSyncOpOnewayNative() {
+        rethrowThrowableFrom {
+            testService.callOnewayApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteSyncOpOtherUidNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpOtherUidNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun nativeSelfNoteAndCheckLog() {
+        nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+
+        assertThat(noted).isEmpty()
+        assertThat(selfNoted).isEmpty()
+
+        // All native notes will be reported as async notes
+        eventually {
+            assertThat(asyncNoted[0].attributionTag).isEqualTo(null)
+            // There is always a message.
+            assertThat(asyncNoted[0].message).isNotEqualTo(null)
+            assertThat(asyncNoted[0].op).isEqualTo(OPSTR_COARSE_LOCATION)
+            assertThat(asyncNoted[0].notingUid).isEqualTo(myUid)
+        }
+    }
+
+    @Test
+    fun noteSyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesSyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteNonPermissionSyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesNonPermissionSyncOpNativelyAndCheckLog(
+                AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpNativelyAndCheckCustomMessage() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpNativelyAndCheckCustomMessage(
+                AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun noteAsyncOpNativeAndCheckLog() {
+        rethrowThrowableFrom {
+            testService.callApiThatNotesAsyncOpNativelyAndCheckLog(AppOpsUserClient(context))
+        }
+    }
+
+    @Test
+    fun nativeSelfNoteWithAttributionAndMsgAndCheckLog() {
+        nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage,
+            attributionTag = TEST_ATTRIBUTION_TAG, message = "testMsg")
+
+        // All native notes will be reported as async notes
+        eventually {
+            assertThat(asyncNoted[0].attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
+            assertThat(asyncNoted[0].message).isEqualTo("testMsg")
+        }
+    }
+
+    @After
+    fun removeNotedAppOpsCollector() {
+        appOpsManager.setOnOpNotedCallback(null, null)
+    }
+
+    @After
+    fun disconnectFromService() {
+        context.unbindService(serviceConnection)
+    }
+
+    private inner class AppOpsUserClient(
+        context: Context
+    ) : IAppOpsUserClient.Stub() {
+        private val handler = Handler(Looper.getMainLooper())
+
+        private val myUid = Process.myUid()
+        private val myPackage = context.packageName
+
+        override fun noteSyncOpNative() {
+            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(), TEST_SERVICE_PKG)
+        }
+
+        override fun noteNonPermissionSyncOpNative() {
+            nativeNoteOp(
+                strOpToOp(OPSTR_ACCESS_ACCESSIBILITY), Binder.getCallingUid(), TEST_SERVICE_PKG
+            )
+        }
+
+        override fun noteSyncOpOnewayNative() {
+            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), Binder.getCallingUid(), TEST_SERVICE_PKG)
+        }
+
+        override fun noteSyncOpOtherUidNative() {
+            nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), myUid, myPackage)
+        }
+
+        override fun noteAsyncOpNative() {
+            val callingUid = Binder.getCallingUid()
+
+            handler.post {
+                nativeNoteOp(strOpToOp(OPSTR_COARSE_LOCATION), callingUid, TEST_SERVICE_PKG)
+            }
+        }
+
+        override fun noteAsyncOpNativeWithCustomMessage() {
+            val callingUid = Binder.getCallingUid()
+
+            handler.post {
+                nativeNoteOp(
+                    strOpToOp(OPSTR_COARSE_LOCATION),
+                    callingUid,
+                    TEST_SERVICE_PKG,
+                    message = "native custom msg"
+                )
+            }
+        }
+    }
+}
+
+class PublicActionReceiver : BroadcastReceiver() {
+    override fun onReceive(context: Context, intent: Intent?) {
+    }
+}
+
+class ProtectedActionReceiver : BroadcastReceiver() {
+    override fun onReceive(context: Context, intent: Intent?) {
+    }
+}
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
index c08e42b..e47ef2d 100644
--- a/core/tests/coretests/src/android/app/NotificationChannelTest.java
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -233,6 +233,33 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_NOTIFICATION_CHANNEL_VIBRATION_EFFECT_API,
+            Flags.FLAG_NOTIF_CHANNEL_CROP_VIBRATION_EFFECTS})
+    public void testLongVibrationFields_canWriteToXml() throws Exception {
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+        // populate pattern with contents
+        long[] pattern = new long[65550 / 2];
+        for (int i = 0; i < pattern.length; i++) {
+            pattern[i] = 100;
+        }
+        channel.setVibrationPattern(pattern);  // with flag on, also sets effect
+
+        // Send it through parceling & unparceling to simulate being passed through a binder call
+        NotificationChannel fromParcel = writeToAndReadFromParcel(channel);
+        assertThat(fromParcel.getVibrationPattern().length).isEqualTo(
+                NotificationChannel.MAX_VIBRATION_LENGTH);
+
+        // Confirm that this also survives writing to & restoring from XML
+        NotificationChannel result = backUpAndRestore(fromParcel);
+        assertThat(result.getVibrationPattern().length).isEqualTo(
+                NotificationChannel.MAX_VIBRATION_LENGTH);
+        assertThat(result.getVibrationEffect()).isNotNull();
+        assertThat(result.getVibrationEffect()
+                .computeCreateWaveformOffOnTimingsOrNull())
+                .isEqualTo(result.getVibrationPattern());
+    }
+
+    @Test
     public void testRestoreSoundUri_customLookup() throws Exception {
         Uri uriToBeRestoredUncanonicalized = Uri.parse("content://media/1");
         Uri uriToBeRestoredCanonicalized = Uri.parse("content://media/1?title=Song&canonical=1");
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
index bbc2458..fd9fda3a 100644
--- a/core/tests/coretests/src/android/content/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.content.ContentCaptureOptionsTest"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_content_capture_options"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/content/integrity/TEST_MAPPING b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
index 2920716..d22fe84 100644
--- a/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/integrity/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.content.integrity."
-        }
-      ]
+      "name": "FrameworksCoreTests_android_content_integrity"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/content/pm/TEST_MAPPING b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
index 978d80c..9ab438e 100644
--- a/core/tests/coretests/src/android/content/pm/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/pm/TEST_MAPPING
@@ -1,21 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.content.pm."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_android_content_pm_PreSubmit"
     }
   ],
   "postsubmit": [
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 6ffdee1..68882eb 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -16,80 +16,94 @@
 
 package android.content.res;
 
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
 import android.platform.test.annotations.Presubmit;
-import android.test.ActivityInstrumentationTestCase2;
+import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.TypedValue;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.frameworks.coretests.R;
 
-import java.lang.reflect.InvocationTargetException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @Presubmit
-public class ConfigurationBoundResourceCacheTest
-        extends ActivityInstrumentationTestCase2<ResourceCacheActivity> {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationBoundResourceCacheTest {
 
-    ConfigurationBoundResourceCache<Float> mCache;
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
 
-    public ConfigurationBoundResourceCacheTest() {
-        super(ResourceCacheActivity.class);
+    private ConfigurationBoundResourceCache<Float> mCache;
+    private Context mContext;
+
+    private void assertEquals(float expected, float actual) {
+        Assert.assertEquals(expected, actual, 0);
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mCache = new ConfigurationBoundResourceCache<>();
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
     }
 
-    @SmallTest
+    @Test
     public void testGetEmpty() {
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         assertNull(mCache.getInstance(-1, res, null));
     }
 
-    @SmallTest
+    @Test
     public void testSetGet() {
         mCache.put(1, null, new DummyFloatConstantState(5f),
                 ThemedResourceCache.UNDEFINED_GENERATION);
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         assertEquals(5f, mCache.getInstance(1, res, null));
         assertNotSame(5f, mCache.getInstance(1, res, null));
-        assertEquals(null, mCache.getInstance(1, res, getActivity().getTheme()));
+        assertNull(mCache.getInstance(1, res, mContext.getTheme()));
     }
 
-    @SmallTest
+    @Test
     public void testSetGetThemed() {
-        mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f),
+        mCache.put(1, mContext.getTheme(), new DummyFloatConstantState(5f),
                 ThemedResourceCache.UNDEFINED_GENERATION);
-        final Resources res = getActivity().getResources();
-        assertEquals(null, mCache.getInstance(1, res, null));
-        assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
-        assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+        final Resources res = mContext.getResources();
+        assertNull(mCache.getInstance(1, res, null));
+        assertEquals(5f, mCache.getInstance(1, res, mContext.getTheme()));
+        assertNotSame(5f, mCache.getInstance(1, res, mContext.getTheme()));
     }
 
-    @SmallTest
+    @Test
     public void testMultiThreadPutGet() {
-        mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f),
+        mCache.put(1, mContext.getTheme(), new DummyFloatConstantState(5f),
                 ThemedResourceCache.UNDEFINED_GENERATION);
         mCache.put(1, null, new DummyFloatConstantState(10f),
                 ThemedResourceCache.UNDEFINED_GENERATION);
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         assertEquals(10f, mCache.getInstance(1, res, null));
         assertNotSame(10f, mCache.getInstance(1, res, null));
-        assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()));
-        assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()));
+        assertEquals(5f, mCache.getInstance(1, res, mContext.getTheme()));
+        assertNotSame(5f, mCache.getInstance(1, res, mContext.getTheme()));
     }
 
-    @SmallTest
-    public void testVoidConfigChange()
-            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    @Test
+    public void testVoidConfigChange() {
         TypedValue staticValue = new TypedValue();
         long key = 3L;
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         res.getValue(R.dimen.resource_cache_test_generic, staticValue, true);
         float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics());
-        mCache.put(key, getActivity().getTheme(),
+        mCache.put(key, mContext.getTheme(),
                 new DummyFloatConstantState(staticDim, staticValue.changingConfigurations),
                 ThemedResourceCache.UNDEFINED_GENERATION);
         final Configuration cfg = res.getConfiguration();
@@ -98,21 +112,20 @@
                 Configuration.ORIENTATION_PORTRAIT
                 : Configuration.ORIENTATION_LANDSCAPE;
         int changes = calcConfigChanges(res, newCnf);
-        assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
+        assertEquals(staticDim, mCache.getInstance(key, res, mContext.getTheme()));
         mCache.onConfigurationChange(changes);
-        assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()));
+        assertEquals(staticDim, mCache.getInstance(key, res, mContext.getTheme()));
     }
 
-    @SmallTest
-    public void testEffectiveConfigChange()
-            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    @Test
+    public void testEffectiveConfigChange() {
         TypedValue changingValue = new TypedValue();
         long key = 4L;
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true);
         float changingDim = TypedValue.complexToDimension(changingValue.data,
                 res.getDisplayMetrics());
-        mCache.put(key, getActivity().getTheme(),
+        mCache.put(key, mContext.getTheme(),
                 new DummyFloatConstantState(changingDim, changingValue.changingConfigurations),
                 ThemedResourceCache.UNDEFINED_GENERATION);
 
@@ -123,26 +136,25 @@
                 : Configuration.ORIENTATION_LANDSCAPE;
         int changes = calcConfigChanges(res, newCnf);
         assertEquals(changingDim,
-                mCache.getInstance(key, res, getActivity().getTheme()));
+                mCache.getInstance(key, res, mContext.getTheme()));
         mCache.onConfigurationChange(changes);
-        assertNull(mCache.get(key, getActivity().getTheme()));
+        assertNull(mCache.get(key, mContext.getTheme()));
     }
 
-    @SmallTest
-    public void testConfigChangeMultipleResources()
-            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    @Test
+    public void testConfigChangeMultipleResources() {
         TypedValue staticValue = new TypedValue();
         TypedValue changingValue = new TypedValue();
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         res.getValue(R.dimen.resource_cache_test_generic, staticValue, true);
         res.getValue(R.dimen.resource_cache_test_orientation_dependent, changingValue, true);
         float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics());
         float changingDim = TypedValue.complexToDimension(changingValue.data,
                 res.getDisplayMetrics());
-        mCache.put(R.dimen.resource_cache_test_generic, getActivity().getTheme(),
+        mCache.put(R.dimen.resource_cache_test_generic, mContext.getTheme(),
                 new DummyFloatConstantState(staticDim, staticValue.changingConfigurations),
                 ThemedResourceCache.UNDEFINED_GENERATION);
-        mCache.put(R.dimen.resource_cache_test_orientation_dependent, getActivity().getTheme(),
+        mCache.put(R.dimen.resource_cache_test_orientation_dependent, mContext.getTheme(),
                 new DummyFloatConstantState(changingDim, changingValue.changingConfigurations),
                 ThemedResourceCache.UNDEFINED_GENERATION);
         final Configuration cfg = res.getConfiguration();
@@ -152,25 +164,24 @@
                 : Configuration.ORIENTATION_LANDSCAPE;
         int changes = calcConfigChanges(res, newCnf);
         assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
-                getActivity().getTheme()));
+                mContext.getTheme()));
         assertEquals(changingDim,
                 mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
-                        getActivity().getTheme()));
+                        mContext.getTheme()));
         mCache.onConfigurationChange(changes);
         assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res,
-                getActivity().getTheme()));
+                mContext.getTheme()));
         assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
-                getActivity().getTheme()));
+                mContext.getTheme()));
     }
 
-    @SmallTest
-    public void testConfigChangeMultipleThemes()
-            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    @Test
+    public void testConfigChangeMultipleThemes() {
         TypedValue[] staticValues = new TypedValue[]{new TypedValue(), new TypedValue()};
         TypedValue[] changingValues = new TypedValue[]{new TypedValue(), new TypedValue()};
         float staticDim = 0;
         float changingDim = 0;
-        final Resources res = getActivity().getResources();
+        final Resources res = mContext.getResources();
         for (int i = 0; i < 2; i++) {
             res.getValue(R.dimen.resource_cache_test_generic, staticValues[i], true);
             staticDim = TypedValue
@@ -180,7 +191,7 @@
                     true);
             changingDim = TypedValue.complexToDimension(changingValues[i].data,
                     res.getDisplayMetrics());
-            final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
+            final Resources.Theme theme = i == 0 ? mContext.getTheme() : null;
             mCache.put(R.dimen.resource_cache_test_generic, theme,
                     new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations),
                     ThemedResourceCache.UNDEFINED_GENERATION);
@@ -196,7 +207,7 @@
                 : Configuration.ORIENTATION_LANDSCAPE;
         int changes = calcConfigChanges(res, newCnf);
         for (int i = 0; i < 2; i++) {
-            final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
+            final Resources.Theme theme = i == 0 ? mContext.getTheme() : null;
             assertEquals(staticDim,
                     mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
             assertEquals(changingDim,
@@ -205,7 +216,7 @@
         }
         mCache.onConfigurationChange(changes);
         for (int i = 0; i < 2; i++) {
-            final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null;
+            final Resources.Theme theme = i == 0 ? mContext.getTheme() : null;
             assertEquals(staticDim,
                     mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme));
             assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res,
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 0d5cd72..83c7484 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -28,23 +28,27 @@
 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Surface.ROTATION_90;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.AtomicFile;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.server.usage.IntervalStatsProto;
 
-import junit.framework.TestCase;
-
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -54,10 +58,14 @@
 /**
  * Build/install/run: bit FrameworksCoreTests:android.content.res.ConfigurationTest
  */
-@RunWith(JUnit4.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
-public class ConfigurationTest extends TestCase {
+public class ConfigurationTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
     @Test
     public void testUpdateFromPreservesRoundBit() {
         Configuration config = new Configuration();
@@ -82,7 +90,7 @@
 
     @Test
     public void testReadWriteProto() throws Exception {
-        final Context context = InstrumentationRegistry.getTargetContext();
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         final File testDir = new File(context.getFilesDir(), "ConfigurationTest");
         testDir.mkdirs();
         final File proto = new File(testDir, "configs");
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 85f5d69..3fcd372 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -26,16 +26,17 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
-import android.app.Instrumentation;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
@@ -51,13 +52,14 @@
 @RunWith(AndroidJUnit4.class)
 public class FontResourcesParserTest {
 
-    private Instrumentation mInstrumentation;
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
     private Resources mResources;
 
     @Before
     public void setup() {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mResources = mInstrumentation.getContext().getResources();
+        mResources = InstrumentationRegistry.getInstrumentation().getContext().getResources();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
index c7d5825..c0a9bc2 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt
@@ -20,6 +20,8 @@
 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.RavenwoodFlagsValueProvider
+import android.platform.test.ravenwood.RavenwoodRule
 import android.util.SparseArray
 import androidx.core.util.forEach
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -27,15 +29,14 @@
 import androidx.test.filters.SmallTest
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.math.ceil
+import kotlin.math.floor
+import kotlin.random.Random.Default.nextFloat
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
-import kotlin.math.ceil
-import kotlin.math.floor
 import org.junit.Test
 import org.junit.runner.RunWith
-import java.lang.IllegalStateException
-import kotlin.random.Random.Default.nextFloat
 
 /**
  * Unit tests for FontScaleConverterFactory. Note that some similar tests are in
@@ -46,7 +47,15 @@
 class FontScaleConverterFactoryTest {
 
     @get:Rule
-    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+    val ravenwoodRule: RavenwoodRule = RavenwoodRule.Builder().build()
+
+    @get:Rule
+    val checkFlagsRule: CheckFlagsRule =
+        if (RavenwoodRule.isOnRavenwood()) {
+            RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+        } else {
+            DeviceFlagsValueProvider.createCheckFlagsRule()
+        }
 
     private var defaultLookupTables: SparseArray<FontScaleConverter>? = null
 
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
index 2c61442..0e5d926 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
@@ -17,8 +17,10 @@
 package android.content.res
 
 import android.platform.test.annotations.Presubmit
+import android.platform.test.ravenwood.RavenwoodRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -26,6 +28,9 @@
 @RunWith(AndroidJUnit4::class)
 class FontScaleConverterTest {
 
+    @get:Rule
+    val ravenwoodRule: RavenwoodRule = RavenwoodRule.Builder().build()
+
     @Test
     fun straightInterpolation() {
         val table = createTable(8f to 8f, 10f to 10f, 20f to 20f)
diff --git a/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java b/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java
deleted file mode 100644
index f37e549..0000000
--- a/core/tests/coretests/src/android/content/res/ResourceCacheActivity.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
-* 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.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES 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.res;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.os.Bundle;
-
-import java.lang.ref.WeakReference;
-
-public class ResourceCacheActivity extends Activity {
-    static WeakReference<ResourceCacheActivity> lastCreatedInstance;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        lastCreatedInstance = new WeakReference<ResourceCacheActivity>(this);
-    }
-
-    public static ResourceCacheActivity getLastCreatedInstance() {
-        return lastCreatedInstance == null ? null : lastCreatedInstance.get();
-    }
-}
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
index ac69a0f..6a09848 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -24,22 +24,29 @@
 import android.graphics.drawable.ColorStateListDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.ravenwood.RavenwoodRule;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @Presubmit
 @SmallTest
+@DisabledOnRavenwood(blockedBy = Drawable.class)
 @RunWith(AndroidJUnit4.class)
 public class ResourcesDrawableTest {
 
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
     @Test
     public void testLoadColorAsDrawable() {
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index 26e4349..fdfddc8 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -16,29 +16,52 @@
 
 package android.content.res;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.os.FileUtils;
 import android.os.LocaleList;
 import android.platform.test.annotations.Presubmit;
-import android.test.AndroidTestCase;
+import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.DisplayMetrics;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.frameworks.coretests.R;
 
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.File;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Locale;
 
 @Presubmit
-public class ResourcesLocaleTest extends AndroidTestCase {
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ResourcesLocaleTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    }
 
     private String extractApkAndGetPath(int id) throws Exception {
-        final Resources resources = getContext().getResources();
+        final Resources resources = mContext.getResources();
         try (InputStream is = resources.openRawResource(id)) {
-            File path = new File(getContext().getFilesDir(), resources.getResourceEntryName(id));
+            File path = new File(mContext.getFilesDir(), resources.getResourceEntryName(id));
             FileUtils.copyToFileOrThrow(is, path);
             return path.getAbsolutePath();
         }
@@ -53,6 +76,15 @@
         return new Resources(assets, dm, new Configuration());
     }
 
+    private Resources createResourcesWithSelfApk() {
+        final AssetManager assets = new AssetManager();
+        assertTrue(assets.addAssetPath(mContext.getPackageResourcePath()) != 0);
+
+        final DisplayMetrics dm = new DisplayMetrics();
+        dm.setToDefaults();
+        return new Resources(assets, dm, new Configuration());
+    }
+
     private static void ensureNoLanguage(Resources resources, String language) {
         final String[] supportedLocales = resources.getAssets().getNonSystemLocales();
         for (String languageTag : supportedLocales) {
@@ -65,7 +97,7 @@
         }
     }
 
-    @SmallTest
+    @Test
     public void testEnglishIsAlwaysConsideredSupported() throws Exception {
         final Resources resources = createResourcesWithApk(R.raw.locales);
         ensureNoLanguage(resources, "en");
@@ -82,7 +114,7 @@
                 resources.getConfiguration().getLocales().get(0));
     }
 
-    @SmallTest
+    @Test
     public void testSelectFirstSupportedLanguage() throws Exception {
         final Resources resources = createResourcesWithApk(R.raw.locales);
         ensureNoLanguage(resources, "fr");
@@ -99,7 +131,7 @@
                 resources.getConfiguration().getLocales().get(0));
     }
 
-    @SmallTest
+    @Test
     public void testDeprecatedISOLanguageCode() {
         assertResGetString(Locale.US, R.string.locale_test_res_1, "Testing ID");
         assertResGetString(Locale.forLanguageTag("id"), R.string.locale_test_res_2, "Pengujian IN");
@@ -115,7 +147,8 @@
         LocaleList locales = new LocaleList(locale);
         final Configuration config = new Configuration();
         config.setLocales(locales);
-        Context newContext = getContext().createConfigurationContext(config);
-        assertEquals(expectedString, newContext.getResources().getString(resId));
+        final Resources resources = createResourcesWithSelfApk();
+        resources.updateConfiguration(config, null);
+        assertEquals(expectedString, resources.getString(resId));
     }
 }
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index ee1b658..3eefe04 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -16,27 +16,34 @@
 
 package android.content.res;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.Postsubmit;
 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.RavenwoodFlagsValueProvider;
+import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -49,7 +56,7 @@
 
 @Postsubmit
 @RunWith(AndroidJUnit4.class)
-public class ResourcesManagerTest extends TestCase {
+public class ResourcesManagerTest {
     private static final int SECONDARY_DISPLAY_ID = 1;
     private static final String APP_ONE_RES_DIR = "app_one.apk";
     private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk";
@@ -57,14 +64,20 @@
     private static final String LIB_RES_DIR = "lib.apk";
     private static final String TEST_LIB = "com.android.frameworks.coretests.bdr_helper_app1";
 
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            RavenwoodRule.isOnRavenwood()
+                    ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
+                    : DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private ResourcesManager mResourcesManager;
     private Map<Integer, DisplayMetrics> mDisplayMetricsMap;
-    private PackageManager mPackageManager;
 
     @Before
     public void setUp() throws Exception {
-        super.setUp();
-
         mDisplayMetricsMap = new HashMap<>();
 
         DisplayMetrics defaultDisplayMetrics = new DisplayMetrics();
@@ -110,12 +123,11 @@
                 return mDisplayMetricsMap.get(displayId);
             }
         };
-
-        mPackageManager = InstrumentationRegistry.getContext().getPackageManager();
     }
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    private PackageManager getPackageManager() {
+        return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+    }
 
     @Test
     @SmallTest
@@ -356,6 +368,7 @@
     @Test
     @SmallTest
     @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    @DisabledOnRavenwood(blockedBy = PackageManager.class)
     public void testExistingResourcesAfterResourcePathsRegistration()
              throws PackageManager.NameNotFoundException {
         // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
@@ -370,7 +383,7 @@
         assertNotNull(resources);
         ResourcesImpl oriResImpl = resources.getImpl();
 
-        ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(TEST_LIB, 0);
         Resources.registerResourcePaths(TEST_LIB, appInfo);
 
         assertNotSame(oriResImpl, resources.getImpl());
@@ -390,6 +403,7 @@
     @Test
     @SmallTest
     @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    @DisabledOnRavenwood(blockedBy = PackageManager.class)
     public void testNewResourcesAfterResourcePathsRegistration()
             throws PackageManager.NameNotFoundException {
         // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
@@ -397,7 +411,7 @@
         ResourcesManager oriResourcesManager = ResourcesManager.getInstance();
         ResourcesManager.setInstance(mResourcesManager);
 
-        ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(TEST_LIB, 0);
         Resources.registerResourcePaths(TEST_LIB, appInfo);
 
         // Create a Resources after register resources' paths for a package.
@@ -420,6 +434,7 @@
     @Test
     @SmallTest
     @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    @DisabledOnRavenwood(blockedBy = PackageManager.class)
     public void testExistingResourcesCreatedByConstructorAfterResourcePathsRegistration()
             throws PackageManager.NameNotFoundException {
         // Inject ResourcesManager instance from this test to the ResourcesManager class so that all
@@ -437,7 +452,7 @@
 
         ResourcesImpl oriResImpl = resources.getImpl();
 
-        ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(TEST_LIB, 0);
         Resources.registerResourcePaths(TEST_LIB, appInfo);
 
         assertNotSame(oriResImpl, resources.getImpl());
@@ -456,6 +471,7 @@
     @Test
     @SmallTest
     @RequiresFlagsEnabled(Flags.FLAG_REGISTER_RESOURCE_PATHS)
+    @DisabledOnRavenwood(blockedBy = PackageManager.class)
     public void testNewResourcesWithOutdatedImplAfterResourcePathsRegistration()
             throws PackageManager.NameNotFoundException {
         ResourcesManager oriResourcesManager = ResourcesManager.getInstance();
@@ -467,7 +483,7 @@
         assertNotNull(old_resources);
         ResourcesImpl oldImpl = old_resources.getImpl();
 
-        ApplicationInfo appInfo = mPackageManager.getApplicationInfo(TEST_LIB, 0);
+        ApplicationInfo appInfo = getPackageManager().getApplicationInfo(TEST_LIB, 0);
         Resources.registerResourcePaths(TEST_LIB, appInfo);
 
         // Create another resources with identical parameters.
diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING
index 4ea6e40..4cce70e 100644
--- a/core/tests/coretests/src/android/content/res/TEST_MAPPING
+++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING
@@ -1,43 +1,12 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.content.res."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_android_content_res"
     }
   ],
   "postsubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.content.res."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Postsubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_android_content_res_PostSubmit"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 832ebe5..6dad3b7 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -999,4 +999,31 @@
             mDatabase.endTransaction();
         }
     }
+
+    /**
+     * This test verifies that the JNI exception thrown because of a bad column is actually thrown
+     * and does not crash the VM.
+     */
+    @Test
+    public void testJniExceptions() {
+        // Create the t1 table.
+        mDatabase.beginTransaction();
+        try {
+            mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+
+        mDatabase.beginTransactionReadOnly();
+        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
+            s.step();
+            s.getColumnText(5); // out-of-range column
+            fail("JNI exception not thrown");
+        } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
+            // Passing case.
+        } finally {
+            mDatabase.endTransaction();
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
new file mode 100644
index 0000000..8a54e5b
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintFontVariationTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package android.graphics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.test.InstrumentationTestCase;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.text.flags.Flags;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * PaintTest tests {@link Paint}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PaintFontVariationTest extends InstrumentationTestCase {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+    @Test
+    public void testDerivedFromSameTypeface() {
+        final Paint p = new Paint();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+        Typeface first = p.getTypeface();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+        Typeface second = p.getTypeface();
+
+        assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+    }
+
+    @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+    @Test
+    public void testDerivedFromChained() {
+        final Paint p = new Paint();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+        Typeface first = p.getTypeface();
+
+        assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+        Typeface second = p.getTypeface();
+
+        assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+    }
+}
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 0dec756..878ba70 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -16,13 +16,22 @@
 
 package android.graphics;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertNotEquals;
 
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.test.InstrumentationTestCase;
 import android.text.TextUtils;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.text.flags.Flags;
+
+import org.junit.Rule;
+
 import java.util.Arrays;
 import java.util.HashSet;
 
@@ -30,6 +39,9 @@
  * PaintTest tests {@link Paint}.
  */
 public class PaintTest extends InstrumentationTestCase {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
 
     static void assertEquals(String message, float[] expected, float[] actual) {
@@ -403,4 +415,33 @@
         assertEquals(6, getClusterCount(p, rtlStr + ltrStr));
         assertEquals(9, getClusterCount(p, ltrStr + rtlStr + ltrStr));
     }
+
+    @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+    public void testDerivedFromSameTypeface() {
+        final Paint p = new Paint();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+        Typeface first = p.getTypeface();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+        Typeface second = p.getTypeface();
+
+        assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+    }
+
+    @RequiresFlagsEnabled(Flags.FLAG_TYPEFACE_CACHE_FOR_VAR_SETTINGS)
+    public void testDerivedFromChained() {
+        final Paint p = new Paint();
+
+        p.setTypeface(Typeface.SANS_SERIF);
+        assertThat(p.setFontVariationSettings("'wght' 450")).isTrue();
+        Typeface first = p.getTypeface();
+
+        assertThat(p.setFontVariationSettings("'wght' 480")).isTrue();
+        Typeface second = p.getTypeface();
+
+        assertThat(first.getDerivedFrom()).isSameInstanceAs(second.getDerivedFrom());
+    }
 }
diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
index d6c0e99..a9bd263 100644
--- a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
+++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java
@@ -16,8 +16,6 @@
 
 package android.hardware.biometrics;
 
-import static android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.MAX_DESCRIPTION_CHARACTER_NUMBER;
-import static android.hardware.biometrics.PromptVerticalListContentView.MAX_EACH_ITEM_CHARACTER_NUMBER;
 import static android.hardware.biometrics.PromptVerticalListContentView.MAX_ITEM_NUMBER;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -120,17 +118,6 @@
     }
 
     @Test
-    public void testMoreOptionsButton_descriptionCharLimit() {
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> new PromptContentViewWithMoreOptionsButton.Builder().setDescription(
-                        generateRandomString(MAX_DESCRIPTION_CHARACTER_NUMBER + 1))
-        );
-
-        assertThat(e).hasMessageThat().contains(
-                "The character number of description exceeds ");
-    }
-
-    @Test
     public void testMoreOptionsButton_ExecutorNull() {
         PromptContentViewWithMoreOptionsButton.Builder builder =
                 new PromptContentViewWithMoreOptionsButton.Builder().setMoreOptionsButtonListener(
@@ -158,29 +145,6 @@
     }
 
     @Test
-    public void testVerticalList_descriptionCharLimit() {
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> new PromptVerticalListContentView.Builder().setDescription(
-                        generateRandomString(MAX_DESCRIPTION_CHARACTER_NUMBER + 1))
-        );
-
-        assertThat(e).hasMessageThat().contains(
-                "The character number of description exceeds ");
-    }
-
-    @Test
-    public void testVerticalList_itemCharLimit() {
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> new PromptVerticalListContentView.Builder().addListItem(
-                        new PromptContentItemBulletedText(
-                                generateRandomString(MAX_EACH_ITEM_CHARACTER_NUMBER + 1)))
-        );
-
-        assertThat(e).hasMessageThat().contains(
-                "The character number of list item exceeds ");
-    }
-
-    @Test
     public void testVerticalList_itemNumLimit() {
         PromptVerticalListContentView.Builder builder = new PromptVerticalListContentView.Builder();
 
diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
new file mode 100644
index 0000000..ee2e7e0
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.frameworks.coretests.aidl.IBfsccTestAppCmdService;
+import com.android.frameworks.coretests.bdr_helper_app.TestCommsReceiver;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Queue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests functionality of {@link android.os.IBinder.IFrozenStateChangeCallback}.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreUnderRavenwood(blockedBy = ActivityManager.class)
+public class BinderFrozenStateChangeNotificationTest {
+    private static final String TAG = BinderFrozenStateChangeNotificationTest.class.getSimpleName();
+
+    private static final String TEST_PACKAGE_NAME_1 =
+            "com.android.frameworks.coretests.bfscctestapp";
+    private static final String TEST_PACKAGE_NAME_2 =
+            "com.android.frameworks.coretests.bdr_helper_app1";
+    private static final String TEST_APP_CMD_SERVICE =
+            TEST_PACKAGE_NAME_1 + ".BfsccTestAppCmdService";
+
+    private static final int CALLBACK_WAIT_TIMEOUT_SECS = 5;
+
+    private IBfsccTestAppCmdService mBfsccTestAppCmdService;
+    private ServiceConnection mTestAppConnection;
+    private Context mContext;
+    private Handler mHandler;
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mHandler = new Handler(Looper.getMainLooper());
+        ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE)).killUid(
+                mContext.getPackageManager().getPackageUid(TEST_PACKAGE_NAME_1, 0),
+                "Wiping Test Package");
+        mTestAppConnection = bindService();
+    }
+
+    private IBinder getNewRemoteBinder(String testPackage) throws InterruptedException {
+        final CountDownLatch resultLatch = new CountDownLatch(1);
+        final AtomicInteger resultCode = new AtomicInteger(Activity.RESULT_CANCELED);
+        final AtomicReference<Bundle> resultExtras = new AtomicReference<>();
+
+        final Intent intent = new Intent(TestCommsReceiver.ACTION_GET_BINDER)
+                .setClassName(testPackage, TestCommsReceiver.class.getName());
+        mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                resultCode.set(getResultCode());
+                resultExtras.set(getResultExtras(true));
+                resultLatch.countDown();
+            }
+        }, mHandler, Activity.RESULT_CANCELED, null, null);
+
+        assertTrue("Request for binder timed out", resultLatch.await(5, TimeUnit.SECONDS));
+        assertEquals(Activity.RESULT_OK, resultCode.get());
+        return resultExtras.get().getBinder(TestCommsReceiver.EXTRA_KEY_BINDER);
+    }
+
+    private ServiceConnection bindService()
+            throws Exception {
+        final CountDownLatch bindLatch = new CountDownLatch(1);
+        ServiceConnection connection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                Log.i(TAG, "Service connected");
+                mBfsccTestAppCmdService = IBfsccTestAppCmdService.Stub.asInterface(service);
+                bindLatch.countDown();
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                Log.i(TAG, "Service disconnected");
+            }
+        };
+        mContext.bindService(
+                new Intent().setComponent(
+                        new ComponentName(TEST_PACKAGE_NAME_1, TEST_APP_CMD_SERVICE)),
+                connection,
+                Context.BIND_AUTO_CREATE
+                        | Context.BIND_NOT_FOREGROUND);
+        if (!bindLatch.await(5, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the service to bind");
+        }
+        return connection;
+    }
+
+    private void unbindService(ServiceConnection service) {
+        if (service != null) {
+            mContext.unbindService(service);
+        }
+    }
+
+    @Test
+    public void onStateChangeCalled() throws Exception {
+        final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+        if (createCallback(mBfsccTestAppCmdService.asBinder(), results) == null) {
+            return;
+        }
+        ensureUnfrozenCallback(results);
+        freezeApp1();
+        ensureFrozenCallback(results);
+        unfreezeApp1();
+        ensureUnfrozenCallback(results);
+    }
+
+    @Test
+    public void onStateChangeNotCalledAfterCallbackRemoved() throws Exception {
+        final LinkedBlockingQueue<Boolean> results = new LinkedBlockingQueue<>();
+        IBinder.IFrozenStateChangeCallback callback;
+        if ((callback = createCallback(mBfsccTestAppCmdService.asBinder(), results)) == null) {
+            return;
+        }
+        ensureUnfrozenCallback(results);
+        mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback);
+        freezeApp1();
+        assertEquals("No more callbacks should be invoked.", 0, results.size());
+    }
+
+    @Test
+    public void multipleCallbacks() throws Exception {
+        final LinkedBlockingQueue<Boolean> results1 = new LinkedBlockingQueue<>();
+        final LinkedBlockingQueue<Boolean> results2 = new LinkedBlockingQueue<>();
+        IBinder.IFrozenStateChangeCallback callback1;
+        if ((callback1 = createCallback(mBfsccTestAppCmdService.asBinder(), results1)) == null) {
+            return;
+        }
+        ensureUnfrozenCallback(results1);
+        freezeApp1();
+        ensureFrozenCallback(results1);
+        if (createCallback(mBfsccTestAppCmdService.asBinder(), results2) == null) {
+            return;
+        }
+        ensureFrozenCallback(results2);
+
+        unfreezeApp1();
+        ensureUnfrozenCallback(results1);
+        ensureUnfrozenCallback(results2);
+
+        mBfsccTestAppCmdService.asBinder().removeFrozenStateChangeCallback(callback1);
+        freezeApp1();
+        assertEquals("No more callbacks should be invoked.", 0, results1.size());
+        ensureFrozenCallback(results2);
+    }
+
+    @Test
+    public void onStateChangeCalledWithTheRightBinder() throws Exception {
+        final IBinder binder = mBfsccTestAppCmdService.asBinder();
+        final LinkedBlockingQueue<IBinder> results = new LinkedBlockingQueue<>();
+        IBinder.IFrozenStateChangeCallback callback =
+                (IBinder who, IBinder.IFrozenStateChangeCallback.State state) -> results.offer(who);
+        try {
+            binder.addFrozenStateChangeCallback(callback);
+        } catch (UnsupportedOperationException e) {
+            return;
+        }
+        assertEquals("Callback received the wrong Binder object.",
+                binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+        freezeApp1();
+        assertEquals("Callback received the wrong Binder object.",
+                binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+        unfreezeApp1();
+        assertEquals("Callback received the wrong Binder object.",
+                binder, results.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+    }
+
+    @After
+    public void tearDown() {
+        if (mTestAppConnection != null) {
+            mContext.unbindService(mTestAppConnection);
+        }
+    }
+
+    private IBinder.IFrozenStateChangeCallback createCallback(IBinder binder, Queue<Boolean> queue)
+            throws RemoteException {
+        try {
+            final IBinder.IFrozenStateChangeCallback callback =
+                    (IBinder who, IBinder.IFrozenStateChangeCallback.State state) ->
+                            queue.offer(state == IBinder.IFrozenStateChangeCallback.State.FROZEN);
+            binder.addFrozenStateChangeCallback(callback);
+            return callback;
+        } catch (UnsupportedOperationException e) {
+            return null;
+        }
+    }
+
+    private void ensureFrozenCallback(LinkedBlockingQueue<Boolean> queue)
+            throws InterruptedException {
+        assertEquals(Boolean.TRUE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+    }
+
+    private void ensureUnfrozenCallback(LinkedBlockingQueue<Boolean> queue)
+            throws InterruptedException {
+        assertEquals(Boolean.FALSE, queue.poll(CALLBACK_WAIT_TIMEOUT_SECS, TimeUnit.SECONDS));
+    }
+
+    private String executeShellCommand(String cmd) throws Exception {
+        return UiDevice.getInstance(
+                InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+    }
+
+    private void freezeApp1() throws Exception {
+        executeShellCommand("am freeze " + TEST_PACKAGE_NAME_1);
+    }
+
+    private void freezeApp2() throws Exception {
+        executeShellCommand("am freeze " + TEST_PACKAGE_NAME_2);
+    }
+
+    private void unfreezeApp1() throws Exception {
+        executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_1);
+    }
+
+    private void unfreezeApp2() throws Exception {
+        executeShellCommand("am unfreeze " + TEST_PACKAGE_NAME_2);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/VibrationAttributesTest.java b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
index d8142c8..5bdae0e 100644
--- a/core/tests/coretests/src/android/os/VibrationAttributesTest.java
+++ b/core/tests/coretests/src/android/os/VibrationAttributesTest.java
@@ -28,11 +28,9 @@
     @Test
     public void testSimple() throws Exception {
         final VibrationAttributes attr = new VibrationAttributes.Builder()
-                .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
                 .setUsage(VibrationAttributes.USAGE_ALARM)
                 .build();
 
-        assertEquals(VibrationAttributes.CATEGORY_KEYBOARD, attr.getCategory());
         assertEquals(VibrationAttributes.USAGE_ALARM, attr.getUsage());
     }
 }
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
index ecd2f76..b157c95 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerIntegrationTest.java
@@ -16,8 +16,11 @@
 
 package android.os.storage;
 
+import android.content.res.ObbInfo;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.ProxyFileDescriptorCallback;
+import android.os.ServiceManager;
 import android.system.ErrnoException;
 
 import androidx.test.filters.LargeTest;
@@ -104,7 +107,14 @@
     public void testMountBadPackageNameObb() throws Exception {
         final File file = createObbFile(OBB_FILE_3_BAD_PACKAGENAME, R.raw.obb_file3_bad_packagename);
         String filePath = file.getAbsolutePath();
-        mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+        try {
+            mountObb(filePath, OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
+            fail("mountObb should throw an exception as package name is incorrect");
+        } catch (Exception ex) {
+            assertEquals("Path " + filePath
+                            + " does not contain package name " + mContext.getPackageName(),
+                    ex.getMessage());
+        }
     }
 
     /**
@@ -154,6 +164,48 @@
         }
     }
 
+    @LargeTest
+    public void testObbInfo_withValidObbInfo_success() throws Exception {
+        final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+        String filePath = file.getAbsolutePath();
+        try {
+            mountObb(filePath);
+            unmountObb(filePath, DONT_FORCE);
+        } catch (Exception ex) {
+            fail("No exception expected, got " + ex.getMessage());
+        }
+    }
+
+    @LargeTest
+    public void testObbInfo_withInvalidObbInfo_exception() throws Exception {
+        final File file = createObbFile(OBB_FILE_1, R.raw.obb_file1);
+        String rawPath = file.getAbsolutePath();
+        String canonicalPath = file.getCanonicalPath();
+
+        ObbInfo obbInfo = ObbInfo.CREATOR.createFromParcel(Parcel.obtain());
+        obbInfo.packageName = "com.android.obbcrash";
+        obbInfo.version = 1;
+        obbInfo.filename = canonicalPath;
+
+        try {
+            IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")).mountObb(
+                    rawPath, canonicalPath, new ObbActionListener(), 0, obbInfo);
+            fail("mountObb should throw an exception as package name is incorrect");
+        } catch (SecurityException ex) {
+            assertEquals("Path " + canonicalPath
+                            + " does not contain package name " + mContext.getPackageName(),
+                    ex.getMessage());
+        }
+    }
+
+    private static class ObbActionListener extends IObbActionListener.Stub {
+        @SuppressWarnings("hiding")
+        @Override
+        public void onObbResult(String filename, int nonce, int status) {
+
+        }
+    }
+
     private static class MyThreadFactory implements ThreadFactory {
         Thread thread = null;
 
diff --git a/core/tests/coretests/src/android/service/TEST_MAPPING b/core/tests/coretests/src/android/service/TEST_MAPPING
index bec72d9..21f248d 100644
--- a/core/tests/coretests/src/android/service/TEST_MAPPING
+++ b/core/tests/coretests/src/android/service/TEST_MAPPING
@@ -1,17 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {"include-filter": "android.service.controls"},
-        {"include-filter": "android.service.controls.actions"},
-        {"include-filter": "android.service.controls.templates"},
-        {"include-filter": "android.service.euicc"},
-        {"include-filter": "android.service.notification"},
-        {"include-filter": "android.service.quicksettings"},
-        {"include-filter": "android.service.settings.suggestions"},
-        {"exclude-annotation": "org.junit.Ignore"}
-      ]
+      "name": "FrameworksCoreTests_android_service"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/tracing/TEST_MAPPING b/core/tests/coretests/src/android/tracing/TEST_MAPPING
new file mode 100644
index 0000000..4b7adf9
--- /dev/null
+++ b/core/tests/coretests/src/android/tracing/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "postsubmit": [
+    {
+       "name": "FrameworksCoreTests_android_tracing",
+        "file_patterns": [".*\\.java"]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/util/StateSetTest.java b/core/tests/coretests/src/android/util/StateSetTest.java
index 14e4e20..c9df83d 100644
--- a/core/tests/coretests/src/android/util/StateSetTest.java
+++ b/core/tests/coretests/src/android/util/StateSetTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -33,7 +32,6 @@
  * Tests for {@link StateSet}
  */
 @RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = StateSet.class)
 public class StateSetTest {
     @Rule
     public final RavenwoodRule mRavenwood = new RavenwoodRule();
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
index afbf8db..b86029b 100644
--- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -19,9 +19,11 @@
 import static org.junit.Assert.assertEquals;
 
 import android.content.res.Configuration;
+import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -34,6 +36,9 @@
 @RunWith(AndroidJUnit4.class)
 public class DisplayAdjustmentsTests {
 
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().build();
+
     @Test
     public void testDefaultConstructor_hasEmptyConfiguration() {
         DisplayAdjustments emptyAdjustments = new DisplayAdjustments();
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 4d9b591c..00ffda8 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -254,11 +254,8 @@
         float progress = 0.5f;
         mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
         // verify correct ime insets manipulation
-        float interpolatedProgress = BACK_GESTURE.getInterpolation(progress);
-        int expectedInset =
-                (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
         verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
-                eq(Insets.of(0, 0, 0, expectedInset)), eq(1f), anyFloat());
+                eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
     }
 
     @Test
@@ -268,12 +265,13 @@
             WindowInsetsAnimationControlListener animationControlListener = startBackGesture();
 
             // progress back gesture
-            mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
+            float progress = 0.5f;
+            mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, progress, EDGE_LEFT));
 
             // commit back gesture
             mBackAnimationController.onBackInvoked();
 
-            // verify setInsetsAndAlpha never called due onReady delayed
+            // verify setInsetsAndAlpha never called due to onReady delayed
             verify(mWindowInsetsAnimationController, never()).setInsetsAndAlpha(any(), anyInt(),
                     anyFloat());
             verify(mInsetsController, never()).setPredictiveBackImeHideAnimInProgress(eq(true));
@@ -283,7 +281,7 @@
 
             // verify setInsetsAndAlpha immediately called
             verify(mWindowInsetsAnimationController, times(1)).setInsetsAndAlpha(
-                    eq(Insets.of(0, 0, 0, IME_HEIGHT)), eq(1f), anyFloat());
+                    eq(Insets.of(0, 0, 0, getImeHeight(progress))), eq(1f), anyFloat());
             // verify post-commit hide anim has started
             verify(mInsetsController, times(1)).setPredictiveBackImeHideAnimInProgress(eq(true));
         });
@@ -319,4 +317,9 @@
 
         return animationControlListener.getValue();
     }
+
+    private int getImeHeight(float gestureProgress) {
+        float interpolatedProgress = BACK_GESTURE.getInterpolation(gestureProgress);
+        return (int) (IME_HEIGHT - interpolatedProgress * PEEK_FRACTION * IME_HEIGHT);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 668487d..786f1e8 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -40,6 +40,7 @@
 import android.util.SparseArray;
 import android.view.SurfaceControl.Transaction;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
+import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -117,11 +118,21 @@
         SparseArray<InsetsSourceControl> controls = new SparseArray<>();
         controls.put(ID_STATUS_BAR, topConsumer.getControl());
         controls.put(ID_NAVIGATION_BAR, navConsumer.getControl());
+        InsetsAnimationSpec spec = new InsetsAnimationSpec() {
+            @Override
+            public long getDurationMs(boolean hasZeroInsetsIme) {
+                return 10;
+            }
+            @Override
+            public Interpolator getInsetsInterpolator(boolean hasZeroInsetsIme) {
+                return new LinearInterpolator();
+            }
+        };
+
         mController = new InsetsAnimationControlImpl(controls,
                 new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
-                mMockController, 10 /* durationMs */, new LinearInterpolator(),
-                0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */,
-                null /* statsToken */);
+                mMockController, spec /* insetsAnimationSpecCreator */, 0 /* animationType */,
+                0 /* layoutInsetsDuringAnimation */, null /* translator */, null /* statsToken */);
         mController.setReadyDispatched(true);
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 7bc0d2f..bec8b1f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -69,6 +69,7 @@
 import android.view.WindowManager.BadTokenException;
 import android.view.WindowManager.LayoutParams;
 import android.view.animation.LinearInterpolator;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
 import android.widget.TextView;
 
@@ -136,7 +137,7 @@
             mTestHandler = new TestHandler(null, mTestClock);
             mTestHost = spy(new TestHost(mViewRoot));
             mController = new InsetsController(mTestHost, (controller, id, type) -> {
-                if (type == ime()) {
+                if (!Flags.refactorInsetsController() && type == ime()) {
                     return new InsetsSourceConsumer(id, type, controller.getState(),
                             Transaction::new, controller) {
 
@@ -260,7 +261,11 @@
             mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
             mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             // When using the animation thread, this must not invoke onReady()
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
         });
@@ -277,7 +282,12 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                InsetsSourceControl ime = createControl(ID_IME, ime());
+                mController.onControlsChanged(new InsetsSourceControl[]{ime});
+            }
             mController.show(all());
             // quickly jump to final state by cancelling it.
             mController.cancelExistingAnimations();
@@ -299,7 +309,11 @@
         mController.onControlsChanged(new InsetsSourceControl[] { ime });
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.getSourceConsumer(ID_IME, ime()).onWindowFocusGained(true);
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             mController.cancelExistingAnimations();
             assertTrue(isRequestedVisible(mController, ime()));
             mController.hide(ime(), true /* fromIme */, ImeTracker.Token.empty());
@@ -469,7 +483,12 @@
             assertFalse(mController.getState().peekSource(ID_IME).isVisible());
 
             // Pretend IME is calling
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                InsetsSourceControl ime = createControl(ID_IME, ime());
+                mController.onControlsChanged(new InsetsSourceControl[]{ime});
+            }
 
             // Gaining control shortly after
             mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
@@ -493,7 +512,12 @@
             mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
 
             // Pretend IME is calling
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                InsetsSourceControl ime = createControl(ID_IME, ime());
+                mController.onControlsChanged(new InsetsSourceControl[]{ime});
+            }
 
             assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
             mController.cancelExistingAnimations();
@@ -558,7 +582,13 @@
 
     @Test
     public void testControlImeNotReady() {
-        prepareControls();
+        if (!Flags.refactorInsetsController()) {
+            prepareControls();
+        } else {
+            // With the flag on, the IME control should not contain a leash, otherwise the custom
+            // animation will start immediately.
+            prepareControls(false /* imeControlHasLeash */);
+        }
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
@@ -571,7 +601,13 @@
             verify(listener, never()).onReady(any(), anyInt());
 
             // Pretend that IME is calling.
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                // Send the IME control with leash, so that the animation can start
+                InsetsSourceControl ime = createControl(ID_IME, ime(), true /* hasLeash */);
+                mController.onControlsChanged(new InsetsSourceControl[]{ime});
+            }
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -583,7 +619,13 @@
 
     @Test
     public void testControlImeNotReady_controlRevoked() {
-        prepareControls();
+        if (!Flags.refactorInsetsController()) {
+            prepareControls();
+        } else {
+            // With the flag on, the IME control should not contain a leash, otherwise the custom
+            // animation will start immediately.
+            prepareControls(false /* imeControlHasLeash */);
+        }
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
@@ -604,7 +646,13 @@
 
     @Test
     public void testControlImeNotReady_timeout() {
-        prepareControls();
+        if (!Flags.refactorInsetsController()) {
+            prepareControls();
+        } else {
+            // With the flag on, the IME control should not contain a leash, otherwise the custom
+            // animation will start immediately.
+            prepareControls(false /* imeControlHasLeash */);
+        }
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
@@ -655,7 +703,11 @@
             mController.onControlsChanged(createSingletonControl(ID_IME, ime()));
 
             // Pretend IME is calling
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
 
             InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
             copy.peekSource(ID_IME).setFrame(0, 1, 2, 3);
@@ -886,7 +938,11 @@
 
             // Showing invisible ime should only causes insets change once.
             clearInvocations(mTestHost);
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             verify(mTestHost, times(1)).notifyInsetsChanged();
 
             // Sending the same insets state should not cause insets change.
@@ -953,7 +1009,11 @@
             assertNull(imeInsetsConsumer.getControl());
 
             // Verify IME requested visibility should be updated to IME consumer from controller.
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             assertTrue(isRequestedVisible(mController, ime()));
 
             mController.hide(ime());
@@ -962,11 +1022,15 @@
     }
 
     @Test
-    public void testImeRequestedVisibleDuringPredictiveBackAnim() {
+    public void testImeRequestedVisibleDuringPredictiveBackAnimWithoutCallback() {
         prepareControls();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // show ime as initial state
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             mController.cancelExistingAnimations(); // fast forward show animation
             assertTrue(mController.getState().peekSource(ID_IME).isVisible());
 
@@ -987,11 +1051,52 @@
     }
 
     @Test
+    public void testImeRequestedInvisibleDuringPredictiveBackAnimWithCallback() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // set WindowInsetsAnimationCallback on ViewRoot
+            mViewRoot.getView().setWindowInsetsAnimationCallback(
+                    new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+                        @Override
+                        public WindowInsets onProgress(
+                                @NonNull WindowInsets insets,
+                                @NonNull List<WindowInsetsAnimation> runningAnimations) {
+                            return insets;
+                        }
+                    });
+
+            // show ime as initial state
+            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            mController.cancelExistingAnimations(); // fast forward show animation
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+            // start control request (for predictive back animation)
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+                    listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+                    ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+            // Verify that onReady is called (after next predraw)
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+            verify(listener).onReady(notNull(), eq(ime()));
+
+            // verify that insets are requested invisible during animation
+            assertFalse(isRequestedVisible(mController, ime()));
+        });
+    }
+
+    @Test
     public void testImeShowRequestCancelsPredictiveBackPostCommitAnim() {
         prepareControls();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            InsetsSourceControl ime = createControl(ID_IME, ime());
             // show ime as initial state
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             mController.cancelExistingAnimations(); // fast forward show animation
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
             assertTrue(mController.getState().peekSource(ID_IME).isVisible());
@@ -1008,12 +1113,20 @@
             assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
 
             // verify show request is ignored during pre commit phase of predictive back anim
-            mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            } else {
+                mController.onControlsChanged(new InsetsSourceControl[]{ime});
+            }
             assertEquals(ANIMATION_TYPE_USER, mController.getAnimationType(ime()));
 
             // verify show request is applied during post commit phase of predictive back anim
             mController.setPredictiveBackImeHideAnimInProgress(true);
-            mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, null /* statsToken */);
+            } else {
+                mController.show(ime(), false /* fromIme */, null /* statsToken */);
+            }
             assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ime()));
 
             // additionally verify that IME ends up visible
@@ -1027,7 +1140,11 @@
         prepareControls();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // show ime as initial state
-            mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
             mController.cancelExistingAnimations(); // fast forward show animation
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
             assertTrue(mController.getState().peekSource(ID_IME).isVisible());
@@ -1050,6 +1167,37 @@
         });
     }
 
+    @Test
+    public void testPredictiveBackControlRequestCancelledDuringImeHideAnim() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // show ime as initial state
+            if (!Flags.refactorInsetsController()) {
+                mController.show(ime(), true /* fromIme */, ImeTracker.Token.empty());
+            } else {
+                mController.show(ime(), false /* fromIme */, ImeTracker.Token.empty());
+            }
+            mController.cancelExistingAnimations(); // fast forward show animation
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+            assertTrue(mController.getState().peekSource(ID_IME).isVisible());
+
+            // start IME hide animation
+            mController.hide(ime(), true /* fromIme */, null /* statsToken */);
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+
+            // start control request (for predictive back animation)
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), /*cancellationSignal*/ null,
+                    listener, /*fromIme*/ false, /*duration*/ -1, /*interpolator*/ null,
+                    ANIMATION_TYPE_USER, /*fromPredictiveBack*/ true);
+
+            // verify that control request is cancelled and animation type remains HIDE
+            verify(listener).onCancelled(any());
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ime()));
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
@@ -1058,11 +1206,15 @@
     }
 
     private InsetsSourceControl createControl(int id, @InsetsType int type) {
+        return createControl(id, type, true);
+    }
+
+    private InsetsSourceControl createControl(int id, @InsetsType int type, boolean hasLeash) {
 
         // Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
         // attempt to release mLeash directly.
         SurfaceControl copy = new SurfaceControl(mLeash, "InsetsControllerTest.createControl");
-        return new InsetsSourceControl(id, type, copy,
+        return new InsetsSourceControl(id, type, hasLeash ? copy : null,
                 (type & WindowInsets.Type.defaultVisible()) != 0, new Point(), Insets.NONE);
     }
 
@@ -1071,9 +1223,13 @@
     }
 
     private InsetsSourceControl[] prepareControls() {
+        return prepareControls(true);
+    }
+
+    private InsetsSourceControl[] prepareControls(boolean imeControlHasLeash) {
         final InsetsSourceControl navBar = createControl(ID_NAVIGATION_BAR, navigationBars());
         final InsetsSourceControl statusBar = createControl(ID_STATUS_BAR, statusBars());
-        final InsetsSourceControl ime = createControl(ID_IME, ime());
+        final InsetsSourceControl ime = createControl(ID_IME, ime(), imeControlHasLeash);
 
         InsetsSourceControl[] controls = new InsetsSourceControl[3];
         controls[0] = navBar;
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index b8ff595..c631c6f 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -994,6 +994,35 @@
                 mViewRoot.getLastPreferredFrameRateCategory());
     }
 
+    /**
+     * If a View is an instance of ViewGroupOverlay,
+     * we obtain the velocity from its hostView.
+     */
+    @Test
+    @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API)
+    public void overlayViewGroupVelocity() throws Throwable {
+        if (!ViewProperties.vrr_enabled().orElse(true)) {
+            return;
+        }
+
+        FrameLayout host = new FrameLayout(mActivity);
+        View childView = new View(mActivity);
+        float velocity = 1000;
+
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup.LayoutParams fullSize = new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    ViewGroup.LayoutParams.MATCH_PARENT);
+            mActivity.setContentView(host, fullSize);
+            host.setFrameContentVelocity(velocity);
+            ViewGroupOverlay overlay = host.getOverlay();
+            overlay.add(childView);
+            assertEquals(velocity, host.getFrameContentVelocity());
+            assertEquals(host.getFrameContentVelocity(),
+                    ((View) childView.getParent()).getFrameContentVelocity());
+        });
+    }
+
     private void runAfterDraw(@NonNull Runnable runnable) {
         Handler handler = new Handler(Looper.getMainLooper());
         mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index a5137bdf..6e563ff 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 = 43;
+    private static final int NUM_MARSHALLED_PROPERTIES = 44;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
index f8beac2..c2cf40d 100644
--- a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.contentcapture"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_android_view_contentcapture"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/view/contentprotection/OWNERS b/core/tests/coretests/src/android/view/contentprotection/OWNERS
index b3583a7..3d09da3 100644
--- a/core/tests/coretests/src/android/view/contentprotection/OWNERS
+++ b/core/tests/coretests/src/android/view/contentprotection/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 544200
+# Bug component: 1040349
 
-include /core/java/android/view/contentcapture/OWNERS
+include /core/java/android/view/contentprotection/OWNERS
 
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
index 3cd4e17..3ef1ac1 100644
--- a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.view.contentprotection"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_android_view_contentprotection"
     }
   ]
 }
diff --git a/core/tests/coretests/src/android/widget/ChronometerTest.java b/core/tests/coretests/src/android/widget/ChronometerTest.java
index 3c73837..cb33117 100644
--- a/core/tests/coretests/src/android/widget/ChronometerTest.java
+++ b/core/tests/coretests/src/android/widget/ChronometerTest.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.app.Activity;
+import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 
 import androidx.test.filters.LargeTest;
@@ -28,7 +29,7 @@
 import java.util.concurrent.TimeUnit;
 
 /**
- * Test {@link DatePicker} focus changes.
+ * Test {@link Chronometer} counting up and down.
  */
 @SuppressWarnings("deprecation")
 @LargeTest
@@ -50,26 +51,48 @@
     }
 
     public void testChronometerTicksSequentially() throws Throwable {
-        final CountDownLatch latch = new CountDownLatch(5);
+        final CountDownLatch latch = new CountDownLatch(6);
         ArrayList<String> ticks = new ArrayList<>();
         runOnUiThread(() -> {
             mChronometer.setOnChronometerTickListener((chronometer) -> {
                 ticks.add(chronometer.getText().toString());
                 latch.countDown();
                 try {
-                    Thread.sleep(500);
+                    Thread.sleep(250);
                 } catch (InterruptedException e) {
                 }
             });
             mChronometer.start();
         });
-        assertTrue(latch.await(6, TimeUnit.SECONDS));
-        assertTrue(ticks.size() >= 5);
+        assertTrue(latch.await(5500, TimeUnit.MILLISECONDS));
         assertEquals("00:00", ticks.get(0));
         assertEquals("00:01", ticks.get(1));
         assertEquals("00:02", ticks.get(2));
         assertEquals("00:03", ticks.get(3));
         assertEquals("00:04", ticks.get(4));
+        assertEquals("00:05", ticks.get(5));
+    }
+
+    public void testChronometerCountDown() throws Throwable {
+        final CountDownLatch latch = new CountDownLatch(5);
+        ArrayList<String> ticks = new ArrayList<>();
+        runOnUiThread(() -> {
+            mChronometer.setBase(SystemClock.elapsedRealtime() + 3_000);
+            mChronometer.setCountDown(true);
+            mChronometer.post(() -> {
+                mChronometer.setOnChronometerTickListener((chronometer) -> {
+                    ticks.add(chronometer.getText().toString());
+                    latch.countDown();
+                });
+                mChronometer.start();
+            });
+        });
+        assertTrue(latch.await(4500, TimeUnit.MILLISECONDS));
+        assertEquals("00:02", ticks.get(0));
+        assertEquals("00:01", ticks.get(1));
+        assertEquals("00:00", ticks.get(2));
+        assertEquals("−00:01", ticks.get(3));
+        assertEquals("−00:02", ticks.get(4));
     }
 
     private void runOnUiThread(Runnable runnable) throws InterruptedException {
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuActivity.java b/core/tests/coretests/src/android/widget/TextViewContextMenuActivity.java
deleted file mode 100644
index 616f29b7..0000000
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import com.android.frameworks.coretests.R;
-
-public class TextViewContextMenuActivity extends Activity {
-    @Override
-    public void onCreate(Bundle savedBundleInstance) {
-        super.onCreate(savedBundleInstance);
-        setContentView(R.layout.textview_contextmenu);
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
index f9da832..3e76977 100644
--- a/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewContextMenuTest.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -23,51 +25,49 @@
 import static org.mockito.ArgumentMatchers.anyChar;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
 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.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.RemoteAction;
+import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.Icon;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.ContextMenu;
 import android.view.MenuItem;
 import android.view.textclassifier.TextClassification;
 
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.frameworks.coretests.R;
 
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.verification.VerificationMode;
 
 /**
  * TextViewTest tests {@link TextView}.
  */
 @RunWith(AndroidJUnit4.class)
-@MediumTest
 public class TextViewContextMenuTest {
     private static final String INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION =
             "android.text.coretest.textclassifiation";
     private static final String ACTION_TITLE = "ACTION_TITLE";
     private static final String ACTION_DESCRIPTION = "ACTION_DESCRIPTION";
 
-    @Rule
-    public final ActivityTestRule<TextViewContextMenuActivity> mActivityRule =
-            new ActivityTestRule<>(TextViewContextMenuActivity.class);
-
     // Setup MenuItem mock with chaining.
     private MenuItem newMockMenuItem() {
         MenuItem mockItem = mock(MenuItem.class);
@@ -81,43 +81,43 @@
         return mockItem;
     }
 
-    private RemoteAction createRemoteAction() {
+    private RemoteAction createRemoteAction(Context context) {
         Intent intent = new Intent(INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION)
-                .setPackage(mActivity.getPackageName());
-        PendingIntent pIntent = PendingIntent.getBroadcast(mActivity, 0, intent,
+                .setPackage(context.getPackageName());
+        PendingIntent pIntent = PendingIntent.getBroadcast(context, 0, intent,
                 PendingIntent.FLAG_MUTABLE);
         return new RemoteAction(
-                Icon.createWithResource(mActivity, android.R.drawable.btn_star),
+                Icon.createWithResource(context, android.R.drawable.btn_star),
                 ACTION_TITLE, ACTION_DESCRIPTION, pIntent);
     }
 
-    private Activity mActivity;
     private SelectionActionModeHelper mMockHelper;
-    private Editor.AssistantCallbackHelper mCallbackHelper;
+
+    @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
+            new SetFlagsRule.ClassRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();
 
     @Before
     public void setUp() {
-        mActivity = mActivityRule.getActivity();
-        EditText et = mActivity.findViewById(R.id.editText);
-
         mMockHelper = mock(SelectionActionModeHelper.class);
-        mCallbackHelper = et.getEditorForTesting().new AssistantCallbackHelper(mMockHelper);
     }
 
-    @UiThreadTest
     @Test
     public void testNoMenuInteraction_noTextClassification() {
         when(mMockHelper.getTextClassification()).thenReturn(null);
         ContextMenu menu = mock(ContextMenu.class);
-        mCallbackHelper.updateAssistMenuItems(menu, null);
+        EditText et = new EditText(getInstrumentation().getContext());
+        Editor.AssistantCallbackHelper cbh =
+                et.getEditorForTesting().new AssistantCallbackHelper(mMockHelper);
+        cbh.updateAssistMenuItems(menu, null);
         verifyNoMoreInteractions(menu);
     }
 
-    @UiThreadTest
     @Test
     public void testAddMenuForTextClassification() {
         // Setup
-        RemoteAction action = createRemoteAction();
+        Context context = getInstrumentation().getContext();
+        RemoteAction action = createRemoteAction(context);
         TextClassification classification = new TextClassification.Builder()
                 .addAction(action).build();
         when(mMockHelper.getTextClassification()).thenReturn(classification);
@@ -127,7 +127,10 @@
         when(menu.add(anyInt(), anyInt(), anyInt(), any())).thenReturn(mockMenuItem);
 
         // Execute
-        mCallbackHelper.updateAssistMenuItems(menu, null);
+        EditText et = new EditText(context);
+        Editor.AssistantCallbackHelper cbh =
+                et.getEditorForTesting().new AssistantCallbackHelper(mMockHelper);
+        cbh.updateAssistMenuItems(menu, null);
 
         // Verify
         ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -140,14 +143,14 @@
         verify(mockMenuItem, times(1)).setContentDescription(eq(ACTION_DESCRIPTION));
     }
 
-    @UiThreadTest
     @Test
     public void testAddMenuForLegacyTextClassification() {
         // Setup
+        Context context = getInstrumentation().getContext();
         Intent intent = new Intent(INTENT_ACTION_MOCK_ACTION_TEXT_CLASSIFICATION)
-                .setPackage(mActivity.getPackageName());
+                .setPackage(context.getPackageName());
         TextClassification classification = new TextClassification.Builder()
-                .setIcon(mActivity.getResources().getDrawable(android.R.drawable.star_on))
+                .setIcon(context.getResources().getDrawable(android.R.drawable.star_on))
                 .setLabel(ACTION_TITLE)
                 .setIntent(intent)
                 .build();
@@ -158,7 +161,10 @@
         when(menu.add(anyInt(), anyInt(), anyInt(), any())).thenReturn(mockMenuItem);
 
         // Execute
-        mCallbackHelper.updateAssistMenuItems(menu, null);
+        EditText et = new EditText(context);
+        Editor.AssistantCallbackHelper cbh =
+                et.getEditorForTesting().new AssistantCallbackHelper(mMockHelper);
+        cbh.updateAssistMenuItems(menu, null);
 
         // Verify
         ArgumentCaptor<Integer> idCaptor = ArgumentCaptor.forClass(Integer.class);
@@ -170,7 +176,6 @@
         assertThat(titleCaptor.getValue().toString()).isEqualTo(ACTION_TITLE);
     }
 
-    @UiThreadTest
     @Test
     public void testAdjustIconSpaces() {
         GradientDrawable gd = new GradientDrawable();
@@ -193,9 +198,8 @@
         when(menu.getItem(1)).thenReturn(mockNoIconMenu);
         when(menu.getItem(2)).thenReturn(mockNoIconMenu2);
 
-
         // Execute the test method
-        EditText et = mActivity.findViewById(R.id.editText);
+        EditText et = new EditText(getInstrumentation().getContext());
         Editor editor = et.getEditorForTesting();
         editor.adjustIconSpacing(menu);
 
@@ -215,7 +219,6 @@
         assertThat(paddingDrawable2).isSameInstanceAs(paddingDrawable);
     }
 
-    @UiThreadTest
     @Test
     public void testAdjustIconSpacesNoIconCase() {
         // Setup mocks
@@ -232,7 +235,7 @@
         when(menu.getItem(1)).thenReturn(mockNoIconMenu2);
 
         // Execute the test method
-        EditText et = mActivity.findViewById(R.id.editText);
+        EditText et = new EditText(getInstrumentation().getContext());
         Editor editor = et.getEditorForTesting();
         editor.adjustIconSpacing(menu);
 
@@ -241,8 +244,8 @@
         verify(mockNoIconMenu2, times(0)).setIcon(any());
     }
 
-    @UiThreadTest
     @Test
+    @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
     public void testAutofillMenuItemEnabledWhenNoTextSelected() {
         ContextMenu menu = mock(ContextMenu.class);
         MenuItem mockMenuItem = newMockMenuItem();
@@ -251,18 +254,19 @@
         when(menu.add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt()))
                 .thenReturn(mockAutofillMenuItem);
 
-        EditText et = mActivity.findViewById(R.id.editText);
-        et.setText("Test");
+        EditText et = spy(new EditText(getInstrumentation().getContext()));
+        doReturn(true).when(et).canRequestAutofill();
+        doReturn(null).when(et).getSelectedText();
 
-        Editor editor = et.getEditorForTesting();
-        editor.onCreateContextMenu(menu);
+        Editor editor = new Editor(et);
+        editor.setTextContextMenuItems(menu);
 
         verify(menu).add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt());
         verify(mockAutofillMenuItem).setEnabled(true);
     }
 
-    @UiThreadTest
     @Test
+    @DisableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
     public void testAutofillMenuItemNotEnabledWhenTextSelected() {
         ContextMenu menu = mock(ContextMenu.class);
         MenuItem mockMenuItem = newMockMenuItem();
@@ -271,14 +275,156 @@
         when(menu.add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt()))
                 .thenReturn(mockAutofillMenuItem);
 
-        EditText et = mActivity.findViewById(R.id.editText);
-        et.setText("Test");
-        et.selectAll();
-        Editor editor = et.getEditorForTesting();
-        editor.onCreateContextMenu(menu);
+        EditText et = spy(new EditText(getInstrumentation().getContext()));
+        doReturn(true).when(et).canRequestAutofill();
+        doReturn("test").when(et).getSelectedText();
+        Editor editor = new Editor(et);
+        editor.setTextContextMenuItems(menu);
 
         verify(menu).add(anyInt(), eq(TextView.ID_AUTOFILL), anyInt(), anyInt());
         verify(mockAutofillMenuItem).setEnabled(false);
     }
 
+    private interface EditTextSetup {
+        void run(EditText et);
+    }
+
+    private void verifyMenuItemNotAdded(EditTextSetup setup, int id, VerificationMode times) {
+        ContextMenu menu = mock(ContextMenu.class);
+        MenuItem mockMenuItem = newMockMenuItem();
+        when(menu.add(anyInt(), anyInt(), anyInt(), anyInt())).thenReturn(mockMenuItem);
+        EditText et = spy(new EditText(getInstrumentation().getContext()));
+        setup.run(et);
+        Editor editor = new Editor(et);
+        editor.setTextContextMenuItems(menu);
+        verify(menu, times).add(anyInt(), eq(id), anyInt(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuUndoNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canUndo(),
+                TextView.ID_UNDO, never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuUndoAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canUndo(), TextView.ID_UNDO,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuRedoNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRedo(), TextView.ID_REDO,
+                never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuRedoAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canRedo(), TextView.ID_REDO,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuCutNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCut(), TextView.ID_CUT,
+                never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuCutAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCut(), TextView.ID_CUT,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuCopyNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canCopy(), TextView.ID_COPY,
+                never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuCopyAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canCopy(), TextView.ID_COPY,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuPasteNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPaste(), TextView.ID_PASTE,
+                never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuPasteAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPaste(), TextView.ID_PASTE,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuPasteAsPlaintextNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canPasteAsPlainText(),
+                        TextView.ID_PASTE_AS_PLAIN_TEXT, never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuPasteAsPlaintextAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canPasteAsPlainText(),
+                        TextView.ID_PASTE_AS_PLAIN_TEXT, times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuSelectAllNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canSelectAllText(),
+                        TextView.ID_SELECT_ALL, never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuSelectAllAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canSelectAllText(),
+                        TextView.ID_SELECT_ALL, times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuShareNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canShare(), TextView.ID_SHARE,
+                never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuShareAddedWhenAvailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(true).when(spy).canShare(), TextView.ID_SHARE,
+                times(1));
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuAutofillNotAddedWhenUnavailable() {
+        verifyMenuItemNotAdded((spy) -> doReturn(false).when(spy).canRequestAutofill(),
+                TextView.ID_AUTOFILL, never());
+    }
+
+    @Test
+    @EnableFlags(com.android.text.flags.Flags.FLAG_CONTEXT_MENU_HIDE_UNAVAILABLE_ITEMS)
+    public void testContextMenuAutofillNotAddedWhenUnavailableBecauseTextSelected() {
+        verifyMenuItemNotAdded((spy) -> {
+            doReturn(true).when(spy).canRequestAutofill();
+            doReturn("test").when(spy).getSelectedText();
+        }, TextView.ID_AUTOFILL, never());
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
index 9aed8be..4a46244 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
+++ b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
@@ -1,21 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "com.android.internal.content."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksCoreTests_com_android_internal_content_Presubmit"
     }
   ]
 }
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 499caf5..c3a5b19c94 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -359,7 +359,7 @@
         tracker.end(FrameTracker.REASON_END_NORMAL);
 
         // Send incomplete callback for 102L
-        sendSfFrame(tracker, 102L, JANK_NONE);
+        sendSfFrame(tracker, 4, 102L, JANK_NONE);
 
         // Send janky but complete callbck fo 103L
         sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
@@ -629,7 +629,7 @@
         if (!tracker.mSurfaceOnly) {
             sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame);
         }
-        sendSfFrame(tracker, vsyncId, jankType);
+        sendSfFrame(tracker, durationMillis, vsyncId, jankType);
     }
 
     private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId,
@@ -645,11 +645,13 @@
         captor.getValue().run();
     }
 
-    private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) {
+    private void sendSfFrame(
+            FrameTracker tracker, long durationMillis, long vsyncId, @JankType int jankType) {
         final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
         doNothing().when(tracker).postCallback(captor.capture());
         mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
-                new JankData(vsyncId, jankType, FRAME_TIME_60Hz)
+                new JankData(vsyncId, jankType, FRAME_TIME_60Hz, FRAME_TIME_60Hz,
+                TimeUnit.MILLISECONDS.toNanos(durationMillis))
         });
         captor.getValue().run();
     }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 66de3d7..397cdcf 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -124,6 +124,16 @@
             return this;
         }
 
+        @Override
+        public void addFrozenStateChangeCallback(IFrozenStateChangeCallback callback)
+                throws RemoteException {
+        }
+
+        @Override
+        public boolean removeFrozenStateChangeCallback(IFrozenStateChangeCallback callback) {
+            return false;
+        }
+
         public void die() {
             isAlive = false;
             if (mRecipient != null) {
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
index a895378..149e132 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/StatusBarIconTest.java
@@ -18,6 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
 import android.os.Parcel;
 import android.os.UserHandle;
 
@@ -37,18 +40,55 @@
      */
     @Test
     public void testParcelable() {
+        final StatusBarIcon original = newStatusBarIcon();
+
+        final StatusBarIcon copy = parcelAndUnparcel(original);
+
+        assertSerializableFieldsEqual(copy, original);
+    }
+
+    @Test
+    public void testClone_withPreloaded() {
+        final StatusBarIcon original = newStatusBarIcon();
+        original.preloadedIcon = new ColorDrawable(Color.RED);
+
+        final StatusBarIcon copy = original.clone();
+
+        assertSerializableFieldsEqual(copy, original);
+        assertThat(copy.preloadedIcon).isNotNull();
+        assertThat(copy.preloadedIcon).isInstanceOf(ColorDrawable.class);
+        assertThat(((ColorDrawable) copy.preloadedIcon).getColor()).isEqualTo(Color.RED);
+    }
+
+    @Test
+    public void testClone_noPreloaded() {
+        final StatusBarIcon original = newStatusBarIcon();
+
+        final StatusBarIcon copy = original.clone();
+
+        assertSerializableFieldsEqual(copy, original);
+        assertThat(copy.preloadedIcon).isEqualTo(original.preloadedIcon);
+    }
+
+    private static StatusBarIcon newStatusBarIcon() {
         final UserHandle dummyUserHandle = UserHandle.of(100);
         final String dummyIconPackageName = "com.android.internal.statusbar.test";
-        final int dummyIconId = 123;
+        final Icon dummyIcon = Icon.createWithResource(dummyIconPackageName, 123);
         final int dummyIconLevel = 1;
         final int dummyIconNumber = 2;
         final CharSequence dummyIconContentDescription = "dummyIcon";
-        final StatusBarIcon original = new StatusBarIcon(dummyIconPackageName, dummyUserHandle,
-                dummyIconId, dummyIconLevel, dummyIconNumber, dummyIconContentDescription,
-                StatusBarIcon.Type.SystemIcon);
+        return new StatusBarIcon(
+                dummyUserHandle,
+                dummyIconPackageName,
+                dummyIcon,
+                dummyIconLevel,
+                dummyIconNumber,
+                dummyIconContentDescription,
+                StatusBarIcon.Type.SystemIcon,
+                StatusBarIcon.Shape.FIXED_SPACE);
+    }
 
-        final StatusBarIcon copy = clone(original);
-
+    private static void assertSerializableFieldsEqual(StatusBarIcon copy, StatusBarIcon original) {
         assertThat(copy.user).isEqualTo(original.user);
         assertThat(copy.pkg).isEqualTo(original.pkg);
         assertThat(copy.icon.sameAs(original.icon)).isTrue();
@@ -56,19 +96,18 @@
         assertThat(copy.visible).isEqualTo(original.visible);
         assertThat(copy.number).isEqualTo(original.number);
         assertThat(copy.contentDescription).isEqualTo(original.contentDescription);
+        assertThat(copy.type).isEqualTo(original.type);
+        assertThat(copy.shape).isEqualTo(original.shape);
     }
 
-    private StatusBarIcon clone(StatusBarIcon original) {
-        Parcel parcel = null;
+    private static StatusBarIcon parcelAndUnparcel(StatusBarIcon original) {
+        Parcel parcel = Parcel.obtain();
         try {
-            parcel = Parcel.obtain();
             original.writeToParcel(parcel, 0);
             parcel.setDataPosition(0);
             return StatusBarIcon.CREATOR.createFromParcel(parcel);
         } finally {
-            if (parcel != null) {
-                parcel.recycle();
-            }
+            parcel.recycle();
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
new file mode 100644
index 0000000..7541a84
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/RateLimitingCacheTest.java
@@ -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.internal.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the RateLimitingCache class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RateLimitingCacheTest {
+
+    private int mCounter = 0;
+
+    @Before
+    public void before() {
+        mCounter = -1;
+    }
+
+    RateLimitingCache.ValueFetcher<Integer> mFetcher = () -> {
+        return ++mCounter;
+    };
+
+    /**
+     * Test zero period passed into RateLimitingCache. A new value should be returned for each
+     * time the cache's get() is invoked.
+     */
+    @Test
+    public void testTtl_Zero() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(0);
+
+        int first = s.get(mFetcher);
+        assertEquals(first, 0);
+        int second = s.get(mFetcher);
+        assertEquals(second, 1);
+        SystemClock.sleep(20);
+        int third = s.get(mFetcher);
+        assertEquals(third, 2);
+    }
+
+    /**
+     * Test a period of 100ms passed into RateLimitingCache. A new value should not be fetched
+     * any more frequently than every 100ms.
+     */
+    @Test
+    public void testTtl_100() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
+
+        int first = s.get(mFetcher);
+        assertEquals(first, 0);
+        int second = s.get(mFetcher);
+        // Too early to change
+        assertEquals(second, 0);
+        SystemClock.sleep(150);
+        int third = s.get(mFetcher);
+        // Changed by now
+        assertEquals(third, 1);
+        int fourth = s.get(mFetcher);
+        // Too early to change again
+        assertEquals(fourth, 1);
+    }
+
+    /**
+     * Test a negative period passed into RateLimitingCache. A new value should only be fetched the
+     * first call to get().
+     */
+    @Test
+    public void testTtl_Negative() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(-1);
+
+        int first = s.get(mFetcher);
+        assertEquals(first, 0);
+        SystemClock.sleep(200);
+        // Should return the original value every time
+        int second = s.get(mFetcher);
+        assertEquals(second, 0);
+    }
+
+    /**
+     * Test making tons of calls to the speed-limiter and make sure number of fetches does not
+     * exceed expected number of fetches.
+     */
+    @Test
+    public void testTtl_Spam() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(100);
+        assertCount(s, 1000, 7, 15);
+    }
+
+    /**
+     * Test rate-limiting across multiple periods and make sure the expected number of fetches is
+     * within the specified rate.
+     */
+    @Test
+    public void testRate_10hz() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10);
+        // At 10 per second, 2 seconds should not exceed about 30, assuming overlap into left and
+        // right windows that allow 10 each
+        assertCount(s, 2000, 20, 33);
+    }
+
+    /**
+     * Test that using a different timebase works correctly.
+     */
+    @Test
+    public void testTimebase() {
+        RateLimitingCache<Integer> s = new RateLimitingCache<>(1000, 10) {
+            @Override
+            protected long getTime() {
+                return SystemClock.elapsedRealtime() / 2;
+            }
+        };
+        // Timebase is moving at half the speed, so only allows for 1 second worth in 2 seconds.
+        assertCount(s, 2000, 10, 22);
+    }
+
+    /**
+     * Helper to make repeated calls every 5 millis to verify the number of expected fetches for
+     * the given parameters.
+     * @param cache the cache object
+     * @param period the period for which to make get() calls
+     * @param minCount the lower end of the expected number of fetches, with a margin for error
+     * @param maxCount the higher end of the expected number of fetches, with a margin for error
+     */
+    private void assertCount(RateLimitingCache<Integer> cache, long period,
+            int minCount, int maxCount) {
+        long startTime = SystemClock.elapsedRealtime();
+        while (SystemClock.elapsedRealtime() < startTime + period) {
+            int value = cache.get(mFetcher);
+            SystemClock.sleep(5);
+        }
+        int latest = cache.get(mFetcher);
+        assertTrue("Latest should be between " + minCount + " and " + maxCount
+                        + " but is " + latest, latest <= maxCount && latest >= minCount);
+    }
+}
diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp
index 5f6eaf9..e11bc55 100644
--- a/core/tests/hdmitests/Android.bp
+++ b/core/tests/hdmitests/Android.bp
@@ -37,3 +37,10 @@
     certificate: "platform",
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "HdmiCecTests_hardware_hdmi",
+    base: "HdmiCecTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.hardware.hdmi"],
+}
diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp
index aca52a8..7fd813a 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -63,3 +63,13 @@
 
     certificate: "platform",
 }
+
+test_module_config {
+    name: "FrameworksMockingCoreTests_os_bundlerecyclingtest",
+    base: "FrameworksMockingCoreTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["android.os.BundleRecyclingTest"],
+}
diff --git a/core/tests/resourceflaggingtests/Android.bp b/core/tests/resourceflaggingtests/Android.bp
index efb8437..40bdc2b 100644
--- a/core/tests/resourceflaggingtests/Android.bp
+++ b/core/tests/resourceflaggingtests/Android.bp
@@ -26,6 +26,7 @@
     name: "ResourceFlaggingTests",
     srcs: [
         "src/**/*.java",
+        ":resource-flagging-test-app-r-java",
     ],
     platform_apis: true,
     certificate: "platform",
diff --git a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
index c1e3578..005538a 100644
--- a/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
+++ b/core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java
@@ -19,15 +19,22 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.FileUtils;
 import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.intenal.flaggedresources.R;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,7 +53,14 @@
     public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
         AssetManager assets = new AssetManager();
-        assertThat(assets.addAssetPath(extractApkAndGetPath(R.raw.resapp))).isNotEqualTo(0);
+        assets.setApkAssets(
+                new ApkAssets[]{
+                        ApkAssets.loadFromPath(
+                                extractApkAndGetPath(
+                                        com.android.resourceflaggingtests.R.raw.resapp
+                                )
+                        )
+                }, true);
 
         final DisplayMetrics dm = new DisplayMetrics();
         dm.setToDefaults();
@@ -55,35 +69,60 @@
 
     @Test
     public void testFlagDisabled() {
-        assertThat(getBoolean("res1")).isTrue();
+        assertThat(mResources.getBoolean(R.bool.bool1)).isTrue();
     }
 
     @Test
     public void testFlagEnabled() {
-        assertThat(getBoolean("res2")).isTrue();
+        assertThat(mResources.getBoolean(R.bool.bool2)).isTrue();
     }
 
     @Test
     public void testFlagEnabledDifferentCompilationUnit() {
-        assertThat(getBoolean("res3")).isTrue();
+        assertThat(mResources.getBoolean(R.bool.bool3)).isTrue();
     }
 
-    private boolean getBoolean(String name) {
-        int resId = mResources.getIdentifier(
-                name,
-                "bool",
-                "com.android.intenal.flaggedresources");
-        assertThat(resId).isNotEqualTo(0);
-        return mResources.getBoolean(resId);
+    @Test
+    public void testFlagDisabledStringArrayElement() {
+        assertThat(mResources.getStringArray(R.array.strarr1))
+                .isEqualTo(new String[]{"one", "two", "three"});
     }
 
-    private String getString(String name) {
-        int resId = mResources.getIdentifier(
-                name,
-                "string",
-                "com.android.intenal.flaggedresources");
-        assertThat(resId).isNotEqualTo(0);
-        return mResources.getString(resId);
+    @Test
+    public void testFlagDisabledIntArrayElement() {
+        assertThat(mResources.getIntArray(R.array.intarr1)).isEqualTo(new int[]{1, 2, 3});
+    }
+
+    @Test
+    public void testLayoutWithDisabledElements() {
+        LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout1, null);
+        assertThat(ll).isNotNull();
+        assertThat((View) ll.findViewById(R.id.text1)).isNotNull();
+        assertThat((View) ll.findViewById(R.id.disabled_text)).isNull();
+        assertThat((View) ll.findViewById(R.id.text2)).isNotNull();
+    }
+
+    private LayoutInflater getLayoutInflater() {
+        ContextWrapper c = new ContextWrapper(mContext) {
+            private LayoutInflater mInflater;
+
+            @Override
+            public Resources getResources() {
+                return mResources;
+            }
+
+            @Override
+            public Object getSystemService(String name) {
+                if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+                    if (mInflater == null) {
+                        mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
+                    }
+                    return mInflater;
+                }
+                return super.getSystemService(name);
+            }
+        };
+        return LayoutInflater.from(c);
     }
 
     private String extractApkAndGetPath(int id) throws Exception {
diff --git a/core/tests/timetests/Android.bp b/core/tests/timetests/Android.bp
index 51181a8..c33d5ee 100644
--- a/core/tests/timetests/Android.bp
+++ b/core/tests/timetests/Android.bp
@@ -23,3 +23,17 @@
     certificate: "platform",
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "FrameworksTimeCoreTests_android_app",
+    base: "FrameworksTimeCoreTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.app."],
+}
+
+test_module_config {
+    name: "FrameworksTimeCoreTests_android_service",
+    base: "FrameworksTimeCoreTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.service."],
+}
diff --git a/core/tests/vibrator/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
index bd3d944..f5b04ee 100644
--- a/core/tests/vibrator/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -60,7 +60,7 @@
 
 @RunWith(MockitoJUnitRunner.class)
 public class VibrationEffectTest {
-
+    private static final float TOLERANCE = 1e-2f;
     private static final String RINGTONE_URI_1 = "content://test/system/ringtone_1";
     private static final String RINGTONE_URI_2 = "content://test/system/ringtone_2";
     private static final String RINGTONE_URI_3 = "content://test/system/ringtone_3";
@@ -430,6 +430,86 @@
     }
 
     @Test
+    public void cropToLength_waveform_underLength() {
+        VibrationEffect effect = VibrationEffect.createWaveform(
+                /* timings= */ new long[]{0, 1, 2},
+                /* repeatIndex= */ -1);
+        VibrationEffect result = effect.cropToLengthOrNull(5);
+
+        assertThat(result).isEqualTo(effect); // unchanged
+    }
+
+    @Test
+    public void cropToLength_waveform_overLength() {
+        VibrationEffect effect = VibrationEffect.createWaveform(
+                /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+                /* repeatIndex= */ -1);
+        VibrationEffect result = effect.cropToLengthOrNull(4);
+
+        assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+                new long[]{0, 1, 2, 3},
+                -1));
+    }
+
+    @Test
+    public void cropToLength_waveform_repeating() {
+        // repeating waveforms cannot be truncated
+        VibrationEffect effect = VibrationEffect.createWaveform(
+                /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+                /* repeatIndex= */ 2);
+        VibrationEffect result = effect.cropToLengthOrNull(3);
+
+        assertThat(result).isNull();
+    }
+
+    @Test
+    public void cropToLength_waveform_withAmplitudes() {
+        VibrationEffect effect = VibrationEffect.createWaveform(
+                /* timings= */ new long[]{0, 1, 2, 3, 4, 5, 6},
+                /* amplitudes= */ new int[]{10, 20, 40, 10, 20, 40, 10},
+                /* repeatIndex= */ -1);
+        VibrationEffect result = effect.cropToLengthOrNull(3);
+
+        assertThat(result).isEqualTo(VibrationEffect.createWaveform(
+                new long[]{0, 1, 2},
+                new int[]{10, 20, 40},
+                -1));
+    }
+
+    @Test
+    public void cropToLength_composed() {
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
+                .compose();
+        VibrationEffect result = effect.cropToLengthOrNull(1);
+
+        assertThat(result).isNotNull();
+        assertThat(result).isEqualTo(VibrationEffect.startComposition()
+                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                        .compose());
+    }
+
+    @Test
+    public void cropToLength_composed_repeating() {
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .repeatEffectIndefinitely(TEST_ONE_SHOT)
+                .compose();
+        assertThat(effect.cropToLengthOrNull(1)).isNull();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void cropToLength_vendorEffect() {
+        PersistableBundle vendorData = new PersistableBundle();
+        vendorData.putInt("key", 1);
+        VibrationEffect effect = VibrationEffect.createVendorEffect(vendorData);
+
+        assertThat(effect.cropToLengthOrNull(2)).isNull();
+    }
+
+    @Test
     public void getRingtones_noPrebakedRingtones() {
         Resources r = mockRingtoneResources(new String[0]);
         Context context = mockContext(r);
@@ -709,7 +789,7 @@
     @Test
     public void testScaleWaveform() {
         VibrationEffect scaledUp = TEST_WAVEFORM.scale(1.5f);
-        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), 1e-5f);
+        assertEquals(1f, getStepSegment(scaledUp, 0).getAmplitude(), TOLERANCE);
 
         VibrationEffect scaledDown = TEST_WAVEFORM.scale(0.5f);
         assertTrue(1f > getStepSegment(scaledDown, 0).getAmplitude());
@@ -731,11 +811,11 @@
     public void testScaleVendorEffect() {
         VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
 
-        VibrationEffect scaledUp = effect.scale(1.5f);
-        assertEquals(effect, scaledUp);
+        VibrationEffect.VendorEffect scaledUp = (VibrationEffect.VendorEffect) effect.scale(1.5f);
+        assertEquals(1.5f, scaledUp.getScale());
 
-        VibrationEffect scaledDown = effect.scale(0.5f);
-        assertEquals(effect, scaledDown);
+        VibrationEffect.VendorEffect scaledDown = (VibrationEffect.VendorEffect) effect.scale(0.5f);
+        assertEquals(0.5f, scaledDown.getScale());
     }
 
     @Test
@@ -755,6 +835,70 @@
     }
 
     @Test
+    public void testApplyAdaptiveScaleOneShot() {
+        VibrationEffect oneShot = VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100);
+
+        VibrationEffect scaledUp = oneShot.applyAdaptiveScale(1.5f);
+        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+        VibrationEffect scaledDown = oneShot.applyAdaptiveScale(0.5f);
+        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+    }
+
+    @Test
+    public void testApplyAdaptiveScaleWaveform() {
+        VibrationEffect waveform = VibrationEffect.createWaveform(
+                new long[] { 100, 100 }, new int[] { 10, 0 }, -1);
+
+        VibrationEffect scaledUp = waveform.applyAdaptiveScale(1.5f);
+        assertThat(getStepSegment(scaledUp, 0).getAmplitude()).isWithin(TOLERANCE).of(15 / 255f);
+
+        VibrationEffect scaledDown = waveform.applyAdaptiveScale(0.5f);
+        assertThat(getStepSegment(scaledDown, 0).getAmplitude()).isWithin(TOLERANCE).of(5 / 255f);
+    }
+
+    @Test
+    public void testApplyAdaptiveScalePrebaked() {
+        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+        assertEquals(effect, scaledUp);
+
+        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+        assertEquals(effect, scaledDown);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+    public void testApplyAdaptiveScaleVendorEffect() {
+        VibrationEffect effect = VibrationEffect.createVendorEffect(createNonEmptyBundle());
+
+        VibrationEffect.VendorEffect scaledUp =
+                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(1.5f);
+        assertEquals(1.5f, scaledUp.getAdaptiveScale());
+
+        VibrationEffect.VendorEffect scaledDown =
+                (VibrationEffect.VendorEffect) effect.applyAdaptiveScale(0.5f);
+        assertEquals(0.5f, scaledDown.getAdaptiveScale());
+    }
+
+    @Test
+    public void testApplyAdaptiveScaleComposed() {
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
+                .addEffect(VibrationEffect.createOneShot(TEST_TIMING, /* amplitude= */ 100))
+                .compose();
+
+        VibrationEffect scaledUp = effect.applyAdaptiveScale(1.5f);
+        assertThat(getPrimitiveSegment(scaledUp, 0).getScale()).isWithin(TOLERANCE).of(0.75f);
+        assertThat(getStepSegment(scaledUp, 1).getAmplitude()).isWithin(TOLERANCE).of(150 / 255f);
+
+        VibrationEffect scaledDown = effect.applyAdaptiveScale(0.5f);
+        assertThat(getPrimitiveSegment(scaledDown, 0).getScale()).isWithin(TOLERANCE).of(0.25f);
+        assertThat(getStepSegment(scaledDown, 1).getAmplitude()).isWithin(TOLERANCE).of(50 / 255f);
+    }
+
+    @Test
     public void testApplyEffectStrengthToOneShotWaveformAndPrimitives() {
         VibrationEffect oneShot = VibrationEffect.createOneShot(100, 100);
         VibrationEffect waveform = VibrationEffect.createWaveform(new long[] { 10, 20 }, 0);
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 050f9b5..8f85617 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -78,6 +78,12 @@
     src: "package-shareduid-allowlist.xml",
 }
 
+prebuilt_etc {
+    name: "oem-defined-uids.xml",
+    sub_dir: "sysconfig",
+    src: "oem-defined-uids.xml",
+}
+
 // Privapp permission whitelist files
 
 prebuilt_etc {
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index a115c65..38ea4ac 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -93,5 +93,6 @@
         <permission name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS" />
         <permission name="android.permission.CONTROL_UI_TRACING" />
         <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" />
+        <permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/oem-defined-uids.xml b/data/etc/oem-defined-uids.xml
new file mode 100644
index 0000000..87435b9
--- /dev/null
+++ b/data/etc/oem-defined-uids.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.
+ -->
+
+<!--
+This XML defines a list of UIDs for OEMs to register as shared UIDs. They will be registered at the
+start of the system, which allows OEMs to create services with these UIDs. The range of these UIDs
+must be in the OEM reserved range.
+
+OEM must provide a preloaded app that is installed at boot time to retain the newly registered UID
+by adding a android:sharedUserId tag in the manifest of the preloaded app, with the value of the tag
+set to the name of the UID defined in this config file. Otherwise, the uid will be cleared at the
+end of the boot and this config file will take no effect.
+
+- The "name" XML attribute refers to the name of the shared UID. It must start with "android.uid.".
+- The "uid" XML attribute refers to the value of the shared UID. It must be in range [2900, 2999].
+
+Example usage
+    <oem-defined-uid name="android.uid.vendordata" uid="2918"/>
+    Indicates that a shared UID named "android.uid.vendordata" will be added to the system with the
+    UID of 2918.
+-->
+
+<config>
+</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 1fe6ca7..2f2f76b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -580,6 +580,9 @@
         <permission name="android.permission.PREPARE_FACTORY_RESET" />
         <!-- Permission required for CTS test - FileIntegrityManagerTest -->
         <permission name="android.permission.SETUP_FSVERITY" />
+        <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+        <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+        <permission name="android.permission.EXECUTE_APP_FUNCTIONS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -592,6 +595,10 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS" />
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.providers.tv">
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.tv">
         <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
         <permission name="android.permission.DVB_DEVICE"/>
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index f1a6b69..4edf52b 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -71,14 +71,9 @@
     },
 }
 
-prebuilt_fonts_xml {
+prebuilt_etc {
     name: "font_fallback.xml",
-    src: "font_fallback.xml",
-    soong_config_variables: {
-        use_var_font: {
-            src: "font_fallback_cjkvf.xml",
-        },
-    },
+    src: ":generate_font_fallback",
 }
 
 /////////////////////////////////
@@ -86,3 +81,41 @@
 // Because `system.img` is a dependency of `fontchain_lint`, it cannot be
 // converted to Android.bp for now.
 // After system.img can be generated by Soong, then it can be converted to Android.bp.
+
+filegroup {
+    name: "DroidSansMono",
+    srcs: ["font_config.json"],
+    required: [
+        "DroidSansMono.ttf",
+    ],
+}
+
+genrule {
+    name: "generate_font_fallback",
+    tools: [":generate_fonts_xml"],
+    tool_files: [
+        "alias.json",
+        "fallback_order.json",
+    ],
+    srcs: [
+        ":CarroisGothicSC",
+        ":ComingSoon",
+        ":CutiveMono",
+        ":DancingScript",
+        ":DroidSansMono",
+        ":Roboto",
+        ":RobotoFlex",
+        ":SourceSansPro",
+        ":noto-fonts",
+    ],
+    exclude_srcs: [
+        "alias.json",
+        "fallback_order.json",
+    ],
+    out: ["font_fallback.xml"],
+    cmd: "$(location :generate_fonts_xml) " +
+        "--alias=$(location alias.json) " +
+        "--fallback=$(location fallback_order.json) " +
+        "$(in) " +
+        "-o $(out)",
+}
diff --git a/data/fonts/alias.json b/data/fonts/alias.json
new file mode 100644
index 0000000..b5b867a
--- /dev/null
+++ b/data/fonts/alias.json
@@ -0,0 +1,37 @@
+[
+  // sans-serif aliases
+  { "name": "arial", "to": "sans-serif" },
+  { "name": "helvetica", "to": "sans-serif" },
+  { "name": "tahoma", "to": "sans-serif" },
+  { "name": "verdana", "to": "sans-serif" },
+  { "name": "sans-serif-black", "to": "sans-serif", "weight": "900" },
+  { "name": "sans-serif-light", "to": "sans-serif", "weight": "300" },
+  { "name": "sans-serif-medium", "to": "sans-serif", "weight": "500" },
+  { "name": "sans-serif-thin", "to": "sans-serif", "weight": "100" },
+
+  // sans-serif-condensed aliases
+  { "name": "sans-serif-condensed-light", "to": "sans-serif-condensed", "weight": "300" },
+  { "name": "sans-serif-condensed-medium", "to": "sans-serif-condensed", "weight": "500" },
+
+  // serif aliases
+  { "name": "ITC Stone Serif", "to": "serif" },
+  { "name": "baskerville", "to": "serif" },
+  { "name": "fantasy", "to": "serif" },
+  { "name": "georgia", "to": "serif" },
+  { "name": "goudy", "to": "serif" },
+  { "name": "palatino", "to": "serif" },
+  { "name": "times new roman", "to": "serif" },
+  { "name": "times", "to": "serif" },
+  { "name": "serif-bold", "to": "serif", "weight": "700" },
+
+  // monospace aliases
+  { "name": "monaco", "to": "monospace" },
+  { "name": "sans-serif-monospace", "to": "monospace" },
+
+  // serif-monospace aliases
+  { "name": "courier new", "to": "serif-monospace" },
+  { "name": "courier", "to": "serif-monospace" },
+
+  // source-sans-pro aliases
+  { "name": "source-sans-pro-semi-bold", "to": "source-sans-pro", "weight": "600" }
+]
diff --git a/data/fonts/fallback_order.json b/data/fonts/fallback_order.json
new file mode 100644
index 0000000..2fc3f3e
--- /dev/null
+++ b/data/fonts/fallback_order.json
@@ -0,0 +1,136 @@
+[
+  { "lang": "und-Arab" },
+  { "lang": "und-Ethi" },
+  { "lang": "und-Hebr" },
+  { "lang": "und-Thai" },
+  { "lang": "und-Armn" },
+  { "lang": "und-Geor,und-Geok" },
+  { "lang": "und-Deva" },
+  { "lang": "und-Gujr" },
+  { "lang": "und-Guru" },
+  { "lang": "und-Taml" },
+  { "lang": "und-Mlym" },
+  { "lang": "und-Beng" },
+  { "lang": "und-Telu" },
+  { "lang": "und-Knda" },
+  { "lang": "und-Orya" },
+  { "lang": "und-Sinh" },
+  { "lang": "und-Khmr" },
+  { "lang": "und-Laoo" },
+  { "lang": "und-Mymr" },
+  { "lang": "und-Thaa" },
+  { "lang": "und-Cham" },
+  { "lang": "und-Ahom" },
+  { "lang": "und-Adlm" },
+  { "lang": "und-Avst" },
+  { "lang": "und-Bali" },
+  { "lang": "und-Bamu" },
+  { "lang": "und-Batk" },
+  { "lang": "und-Brah" },
+  { "lang": "und-Bugi" },
+  { "lang": "und-Buhd" },
+  { "lang": "und-Cans" },
+  { "lang": "und-Cari" },
+  { "lang": "und-Cakm" },
+  { "lang": "und-Cher" },
+  { "lang": "und-Copt" },
+  { "lang": "und-Xsux" },
+  { "lang": "und-Cprt" },
+  { "lang": "und-Dsrt" },
+  { "lang": "und-Egyp" },
+  { "lang": "und-Elba" },
+  { "lang": "und-Glag" },
+  { "lang": "und-Goth" },
+  { "lang": "und-Hano" },
+  { "lang": "und-Armi" },
+  { "lang": "und-Phli" },
+  { "lang": "und-Prti" },
+  { "lang": "und-Java" },
+  { "lang": "und-Kthi" },
+  { "lang": "und-Kali" },
+  { "lang": "und-Khar" },
+  { "lang": "und-Lepc" },
+  { "lang": "und-Limb" },
+  { "lang": "und-Linb" },
+  { "lang": "und-Lisu" },
+  { "lang": "und-Lyci" },
+  { "lang": "und-Lydi" },
+  { "lang": "und-Mand" },
+  { "lang": "und-Mtei" },
+  { "lang": "und-Talu" },
+  { "lang": "und-Nkoo" },
+  { "lang": "und-Ogam" },
+  { "lang": "und-Olck" },
+  { "lang": "und-Ital" },
+  { "lang": "und-Xpeo" },
+  { "lang": "und-Sarb" },
+  { "lang": "und-Orkh" },
+  { "lang": "und-Osge" },
+  { "lang": "und-Osma" },
+  { "lang": "und-Phnx" },
+  { "lang": "und-Rjng" },
+  { "lang": "und-Runr" },
+  { "lang": "und-Samr" },
+  { "lang": "und-Saur" },
+  { "lang": "und-Shaw" },
+  { "lang": "und-Sund" },
+  { "lang": "und-Sylo" },
+  { "lang": "und-Syre" },
+  { "lang": "und-Syrn" },
+  { "lang": "und-Syrj" },
+  { "lang": "und-Tglg" },
+  { "lang": "und-Tagb" },
+  { "lang": "und-Lana" },
+  { "lang": "und-Tavt" },
+  { "lang": "und-Tibt" },
+  { "lang": "und-Tfng" },
+  { "lang": "und-Ugar" },
+  { "lang": "und-Vaii" },
+  // NotoSansSymbol-Regular-Subsetted doesn't have any language but should be
+  // placed before the CJK fonts for reproducing the same fallback order.
+  { "id": "NotoSansSymbols-Regular-Subsetted" },
+  { "lang": "zh-Hans" },
+  { "lang": "zh-Hant,zh-Bopo" },
+  { "lang": "ja" },
+  { "lang": "ko" },
+  { "lang": "und-Zsye" },
+  { "lang": "und-Zsym" },
+  { "lang": "und-Tale" },
+  { "lang": "und-Yiii" },
+  { "lang": "und-Mong" },
+  { "lang": "und-Phag" },
+  { "lang": "und-Hluw" },
+  { "lang": "und-Bass" },
+  { "lang": "und-Bhks" },
+  { "lang": "und-Hatr" },
+  { "lang": "und-Lina" },
+  { "lang": "und-Mani" },
+  { "lang": "und-Marc" },
+  { "lang": "und-Merc" },
+  { "lang": "und-Plrd" },
+  { "lang": "und-Mroo" },
+  { "lang": "und-Mult" },
+  { "lang": "und-Nbat" },
+  { "lang": "und-Newa" },
+  { "lang": "und-Narb" },
+  { "lang": "und-Perm" },
+  { "lang": "und-Hmng" },
+  { "lang": "und-Palm" },
+  { "lang": "und-Pauc" },
+  { "lang": "und-Shrd" },
+  { "lang": "und-Sora" },
+  { "lang": "und-Gong" },
+  { "lang": "und-Rohg" },
+  { "lang": "und-Khoj" },
+  { "lang": "und-Gonm" },
+  { "lang": "und-Wcho" },
+  { "lang": "und-Wara" },
+  { "lang": "und-Gran" },
+  { "lang": "und-Modi" },
+  { "lang": "und-Dogr" },
+  { "lang": "und-Medf" },
+  { "lang": "und-Soyo" },
+  { "lang": "und-Takr" },
+  { "lang": "und-Hmnp" },
+  { "lang": "und-Yezi" }
+]
diff --git a/data/fonts/font_config.json b/data/fonts/font_config.json
new file mode 100644
index 0000000..427e6cf
--- /dev/null
+++ b/data/fonts/font_config.json
@@ -0,0 +1,12 @@
+[
+    {
+        "name": "monospace",
+        "fonts": [
+            {
+                "file": "DroidSansMono.ttf",
+                "weight": "400",
+                "style": "normal"
+            }
+        ]
+    }
+]
\ No newline at end of file
diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml
index 53024ab..ae50da1 100644
--- a/data/fonts/font_fallback.xml
+++ b/data/fonts/font_fallback.xml
@@ -304,7 +304,7 @@
         <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght">
             NotoSansBengali-VF.ttf
         </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular">
+        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght">
             NotoSerifBengali-VF.ttf
         </font>
     </family>
@@ -354,7 +354,7 @@
         <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght">
             NotoSansSinhala-VF.ttf
         </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular">
+        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght">
             NotoSerifSinhala-VF.ttf
         </font>
     </family>
@@ -927,23 +927,23 @@
             NotoSansMedefaidrin-VF.ttf
         </font>
     </family>
-    <family lang="und-Soyo" supportedAxes="wght">
-        <font postScriptName="NotoSansSoyombo-Regular">
+    <family lang="und-Soyo">
+        <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght">
             NotoSansSoyombo-VF.ttf
         </font>
     </family>
-    <family lang="und-Takr" supportedAxes="wght">
-        <font postScriptName="NotoSansTakri-Regular">
+    <family lang="und-Takr">
+        <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght">
             NotoSansTakri-VF.ttf
         </font>
     </family>
-    <family lang="und-Hmnp" supportedAxes="wght">
-        <font postScriptName="NotoSerifHmongNyiakeng-Regular">
+    <family lang="und-Hmnp">
+        <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght">
             NotoSerifNyiakengPuachueHmong-VF.ttf
         </font>
     </family>
-    <family lang="und-Yezi" supportedAxes="wght">
-        <font postScriptName="NotoSerifYezidi-Regular">
+    <family lang="und-Yezi">
+        <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght">
             NotoSerifYezidi-VF.ttf
         </font>
     </family>
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index a4ee825..407d704 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -304,7 +304,7 @@
         <font postScriptName="NotoSansBengali-Regular" supportedAxes="wght">
             NotoSansBengali-VF.ttf
         </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular">
+        <font fallbackFor="serif" postScriptName="NotoSerifBengali-Regular" supportedAxes="wght">
             NotoSerifBengali-VF.ttf
         </font>
     </family>
@@ -354,7 +354,7 @@
         <font postScriptName="NotoSansSinhala-Regular" supportedAxes="wght">
             NotoSansSinhala-VF.ttf
         </font>
-        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular">
+        <font fallbackFor="serif" postScriptName="NotoSerifSinhala-Regular" supportedAxes="wght">
             NotoSerifSinhala-VF.ttf
         </font>
     </family>
@@ -943,23 +943,23 @@
             NotoSansMedefaidrin-VF.ttf
         </font>
     </family>
-    <family lang="und-Soyo" supportedAxes="wght">
-        <font postScriptName="NotoSansSoyombo-Regular">
+    <family lang="und-Soyo">
+        <font postScriptName="NotoSansSoyombo-Regular" supportedAxes="wght">
             NotoSansSoyombo-VF.ttf
         </font>
     </family>
-    <family lang="und-Takr" supportedAxes="wght">
-        <font postScriptName="NotoSansTakri-Regular">
+    <family lang="und-Takr">
+        <font postScriptName="NotoSansTakri-Regular" supportedAxes="wght">
             NotoSansTakri-VF.ttf
         </font>
     </family>
-    <family lang="und-Hmnp" supportedAxes="wght">
-        <font postScriptName="NotoSerifHmongNyiakeng-Regular">
+    <family lang="und-Hmnp">
+        <font postScriptName="NotoSerifHmongNyiakeng-Regular" supportedAxes="wght">
             NotoSerifNyiakengPuachueHmong-VF.ttf
         </font>
     </family>
-    <family lang="und-Yezi" supportedAxes="wght">
-        <font postScriptName="NotoSerifYezidi-Regular">
+    <family lang="und-Yezi">
+        <font postScriptName="NotoSerifYezidi-Regular" supportedAxes="wght">
             NotoSerifYezidi-VF.ttf
         </font>
     </family>
diff --git a/data/fonts/script/Android.bp b/data/fonts/script/Android.bp
new file mode 100644
index 0000000..3486285
--- /dev/null
+++ b/data/fonts/script/Android.bp
@@ -0,0 +1,36 @@
+// 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.
+
+python_library_host {
+    name: "generate_fonts_xml_lib",
+    srcs: [
+        "alias_builder.py",
+        "commandline.py",
+        "custom_json.py",
+        "fallback_builder.py",
+        "family_builder.py",
+        "font_builder.py",
+        "validators.py",
+        "xml_builder.py",
+    ],
+}
+
+python_binary_host {
+    name: "generate_fonts_xml",
+    main: "generate_fonts_xml_main.py",
+    srcs: ["generate_fonts_xml_main.py"],
+    libs: [
+        "generate_fonts_xml_lib",
+    ],
+}
diff --git a/data/fonts/script/alias_builder.py b/data/fonts/script/alias_builder.py
new file mode 100755
index 0000000..cfc5d67
--- /dev/null
+++ b/data/fonts/script/alias_builder.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build Alias instance with validating JSON contents."""
+
+import dataclasses
+
+from custom_json import _load_json_with_comment
+from validators import check_str
+from validators import check_weight_or_none
+
+
[email protected]
+class Alias:
+  name: str
+  to: str
+  weight: int | None
+
+
+_ALIAS_KEYS = set(["name", "to", "weight"])
+
+
+def parse_alias(obj) -> Alias:
+  """Convert given dict object to Alias instance."""
+  unknown_keys = obj.keys() - _ALIAS_KEYS
+  assert not unknown_keys, "Unknown keys found: %s" % unknown_keys
+  alias = Alias(
+      name=check_str(obj, "name"),
+      to=check_str(obj, "to"),
+      weight=check_weight_or_none(obj, "weight"),
+  )
+
+  assert alias.name != alias.to, "name and to must not be equal"
+
+  return alias
+
+
+def parse_alias_from_json(json_str) -> Alias:
+  """For testing purposes."""
+  return parse_alias(_load_json_with_comment(json_str))
+
+
+def parse_aliases(objs) -> [Alias]:
+  assert isinstance(objs, list), "aliases must be list"
+  return [parse_alias(obj) for obj in objs]
+
+
+def parse_aliases_from_json(json_str) -> [Alias]:
+  return parse_aliases(_load_json_with_comment(json_str))
diff --git a/data/fonts/script/commandline.py b/data/fonts/script/commandline.py
new file mode 100755
index 0000000..743b1b2
--- /dev/null
+++ b/data/fonts/script/commandline.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build commandline arguments."""
+
+import argparse
+import dataclasses
+from typing import Callable
+
+from alias_builder import Alias
+from alias_builder import parse_aliases_from_json
+from fallback_builder import FallbackEntry
+from fallback_builder import parse_fallback_from_json
+from family_builder import Family
+from family_builder import parse_families_from_json
+
+
[email protected]
+class CommandlineArgs:
+  outfile: str
+  fallback: [FallbackEntry]
+  aliases: [Alias]
+  families: [Family]
+
+
+def _create_argument_parser() -> argparse.ArgumentParser:
+  """Create argument parser."""
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-o', '--output')
+  parser.add_argument('--alias')
+  parser.add_argument('--fallback')
+  return parser
+
+
+def _fileread(path: str) -> str:
+  with open(path, 'r') as f:
+    return f.read()
+
+
+def parse_commandline(
+    args: [str], fileread: Callable[str, str] = _fileread
+) -> CommandlineArgs:
+  """Parses command line arguments and returns CommandlineArg."""
+  parser = _create_argument_parser()
+  args, inputs = parser.parse_known_args(args)
+
+  families = []
+  for i in inputs:
+    families = families + parse_families_from_json(fileread(i))
+
+  return CommandlineArgs(
+      outfile=args.output,
+      fallback=parse_fallback_from_json(fileread(args.fallback)),
+      aliases=parse_aliases_from_json(fileread(args.alias)),
+      families=families,
+  )
diff --git a/data/fonts/script/custom_json.py b/data/fonts/script/custom_json.py
new file mode 100755
index 0000000..8a07bb5
--- /dev/null
+++ b/data/fonts/script/custom_json.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""A custom json parser that additionally supports line comments."""
+
+import json
+import re
+
+# RegEx of removing line comment line in JSON.
+_LINE_COMMENT_RE = re.compile(r'\/\/[^\n\r]*[\n\r]')
+
+
+def _load_json_with_comment(json_str: str):
+  """Parse JSON string with accepting line comment."""
+  raw_text = re.sub(_LINE_COMMENT_RE, '', json_str)
+  return json.loads(raw_text)
diff --git a/data/fonts/script/fallback_builder.py b/data/fonts/script/fallback_builder.py
new file mode 100755
index 0000000..2b66740
--- /dev/null
+++ b/data/fonts/script/fallback_builder.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build Fallback instance with validating JSON contents."""
+
+import dataclasses
+
+from custom_json import _load_json_with_comment
+from validators import check_str_or_none
+
+
[email protected]
+class FallbackEntry:
+  lang: str | None
+  id: str | None
+
+
+_FALLBACK_KEYS = set(["lang", "id"])
+
+
+def _parse_entry(obj) -> FallbackEntry:
+  """Convert given dict object to FallbackEntry instance."""
+  unknown_keys = obj.keys() - _FALLBACK_KEYS
+  assert not unknown_keys, "Unknown keys found: %s" % unknown_keys
+  entry = FallbackEntry(
+      lang=check_str_or_none(obj, "lang"),
+      id=check_str_or_none(obj, "id"),
+  )
+
+  assert entry.lang or entry.id, "lang or id must be specified."
+  assert (
+      not entry.lang or not entry.id
+  ), "lang and id must not be specified at the same time"
+
+  return entry
+
+
+def parse_fallback(objs) -> [FallbackEntry]:
+  assert isinstance(objs, list), "fallback must be list"
+  assert objs, "at least one etnry must be specified"
+  return [_parse_entry(obj) for obj in objs]
+
+
+def parse_fallback_from_json(json_str) -> [FallbackEntry]:
+  """For testing purposes."""
+  return parse_fallback(_load_json_with_comment(json_str))
diff --git a/data/fonts/script/family_builder.py b/data/fonts/script/family_builder.py
new file mode 100755
index 0000000..9a6f8c5
--- /dev/null
+++ b/data/fonts/script/family_builder.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build Family instance with validating JSON contents."""
+
+import dataclasses
+
+from custom_json import _load_json_with_comment
+from font_builder import Font
+from font_builder import parse_fonts
+from validators import check_enum_or_none
+from validators import check_priority_or_none
+from validators import check_str_or_none
+
+_FAMILY_KEYS = set([
+    "id",
+    "lang",
+    "name",
+    "variant",
+    "fallbackFor",
+    "fonts",
+    "target",
+    "priority",
+])
+
+
[email protected]
+class Family:
+  id: str | None
+  lang: str | None
+  name: str | None
+  priority: int | None
+  variant: str | None
+  fallback_for: str | None
+  target: str | None
+  fonts: [Font]
+
+
+def _validate_family(family):
+  assert not family.lang or not family.name, (
+      "If lang attribute is specified, name attribute must not be specified: %s"
+      % family
+  )
+
+  if family.fallback_for:
+    assert family.target, (
+        "If fallbackFor is specified, must specify target: %s" % family
+    )
+  if family.target:
+    assert family.fallback_for, (
+        "If target is specified, must specify fallbackFor: %s" % family
+    )
+
+
+def _parse_family(obj, for_sanitization_test=False) -> Family:
+  """Create Family object from dictionary."""
+  unknown_keys = obj.keys() - _FAMILY_KEYS
+  assert not unknown_keys, "Unknown keys found: %s in %s" % (unknown_keys, obj)
+
+  if for_sanitization_test:
+    fonts = []
+  else:
+    fonts = parse_fonts(obj.get("fonts"))
+
+  family = Family(
+      id=check_str_or_none(obj, "id"),
+      lang=check_str_or_none(obj, "lang"),
+      name=check_str_or_none(obj, "name"),
+      priority=check_priority_or_none(obj, "priority"),
+      variant=check_enum_or_none(obj, "variant", ["elegant", "compact"]),
+      fallback_for=check_str_or_none(obj, "fallbackFor"),
+      target=check_str_or_none(obj, "target"),
+      fonts=fonts,
+  )
+
+  if not for_sanitization_test:
+    _validate_family(family)
+  return family
+
+
+def parse_family_from_json_for_sanitization_test(json_str) -> Family:
+  """For testing purposes."""
+  return _parse_family(
+      _load_json_with_comment(json_str), for_sanitization_test=True
+  )
+
+
+def parse_family_from_json(json_str) -> Family:
+  """For testing purposes."""
+  return _parse_family(_load_json_with_comment(json_str))
+
+
+def parse_families_from_json(json_str) -> [Family]:
+  objs = _load_json_with_comment(json_str)
+  assert isinstance(objs, list), "families must be list"
+  assert objs, "families must contains at least one family"
+  return [_parse_family(obj) for obj in objs]
diff --git a/data/fonts/script/font_builder.py b/data/fonts/script/font_builder.py
new file mode 100755
index 0000000..f0fe966
--- /dev/null
+++ b/data/fonts/script/font_builder.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build Font instance with validating JSON contents."""
+
+import dataclasses
+
+from custom_json import _load_json_with_comment
+from validators import check_enum_or_none
+from validators import check_float
+from validators import check_int_or_none
+from validators import check_str
+from validators import check_str_or_none
+from validators import check_tag
+from validators import check_weight_or_none
+
+
[email protected]
+class Font:
+  file: str
+  weight: int | None
+  style: str | None
+  index: int | None
+  supported_axes: str | None
+  post_script_name: str | None
+  axes: dict[str | float]
+
+
+_FONT_KEYS = set([
+    "file",
+    "weight",
+    "style",
+    "index",
+    "supportedAxes",
+    "postScriptName",
+    "axes",
+])
+
+
+def _check_axes(axes) -> dict[str | float] | None:
+  """Sanitize the variation axes."""
+  if axes is None:
+    return None
+  assert isinstance(axes, dict), "axes must be dict"
+
+  sanitized = {}
+  for key in axes.keys():
+    sanitized[check_tag(key)] = check_float(axes, key)
+
+  return sanitized
+
+
+def _parse_font(obj, for_sanitization_test=False) -> Font:
+  """Convert given dict object to Font instance."""
+  unknown_keys = obj.keys() - _FONT_KEYS
+  assert not unknown_keys, "Unknown keys found: %s" % unknown_keys
+  font = Font(
+      file=check_str(obj, "file"),
+      weight=check_weight_or_none(obj, "weight"),
+      style=check_enum_or_none(obj, "style", ["normal", "italic"]),
+      index=check_int_or_none(obj, "index"),
+      supported_axes=check_enum_or_none(
+          obj, "supportedAxes", ["wght", "wght,ital"]
+      ),
+      post_script_name=check_str_or_none(obj, "postScriptName"),
+      axes=_check_axes(obj.get("axes")),
+  )
+
+  if not for_sanitization_test:
+    assert font.file, "file must be specified"
+    if not font.supported_axes:
+      assert font.weight, (
+          "If supported_axes is not specified, weight should be specified: %s"
+          % obj
+      )
+      assert font.style, (
+          "If supported_axes is not specified, style should be specified: %s"
+          % obj
+      )
+
+  return font
+
+
+def parse_fonts(objs) -> Font:
+  assert isinstance(objs, list), "fonts must be list: %s" % (objs)
+  assert objs, "At least one font should be added."
+  return [_parse_font(obj) for obj in objs]
+
+
+def parse_font_from_json_for_sanitization_test(json_str: str) -> Font:
+  """For testing purposes."""
+  return _parse_font(
+      _load_json_with_comment(json_str), for_sanitization_test=False
+  )
+
+
+def parse_fonts_from_json_for_validation_test(json_str: str) -> [Font]:
+  """For testing purposes."""
+  return parse_fonts(_load_json_with_comment(json_str))
diff --git a/data/fonts/script/generate_fonts_xml_main.py b/data/fonts/script/generate_fonts_xml_main.py
new file mode 100755
index 0000000..2f97708
--- /dev/null
+++ b/data/fonts/script/generate_fonts_xml_main.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""A main module for generating XML from font config JSONs.
+
+The following is a JSON format of the font configuration.
+
+[  // Top level element is a list to be able to hold multiple families
+    { // Dict for defining single family entry
+
+        // Optional String: unique identifier.
+        // This can be used for identifying this family instance.
+        // Currently this is ued only for specifying the target of the fallback
+        // family.
+        "id": "Roboto",
+
+        // Optional String: name of this family if this family creates a new
+        // fallback. If multiple families define the same name, it is a build
+        // error.
+        "name": "sans-serif",
+
+        // Optional String: language tag of this family if this family is a
+        // fallback family. Only language tags declared in fallback_order.json
+        // can be used. Specifying unknown language tags is a build error.
+        "lang": "und-Latn",
+
+        // Optional String: variant of the family
+        // Currently only “compact”, “elegant” are supported.
+        "variant": "compact",
+
+        // Optional String: specify the fallback target used for this family.
+        // If this key is specified, "target" attribute must also be specified.
+        // If this key is specified, "name" and "lang" must not be specified.
+        // If the specified fallback target is not defined, it is a build error.
+        "fallbackFor": "roboto-flex",
+
+        // Optional String: specify the family target to include this family.
+        // If this key is specified, "fallbackFor" attribute must also be
+        // specified. If this key is specified, "name" and "lang" must not be
+        // specified. If the specified family target is not defined, it is a
+        // build error.
+        "target": "RobotoMain",
+
+        // Optional Integer: specify the priority of the family.
+        // The priority order is determined by fallback_order.json.
+        // This priority is only used when two or more font families are
+        // assigned to the same rank: e.g. NotoColorEmoji.ttf and
+        // NotoColorEmojiFlags.ttf.
+        // All families have priority 0 by default and any value from -100 to
+        // 100 is valid. Lowering priority value increases the priority.
+        "priority": 0,
+
+        // Mandatory List: specify list of fonts. At least one font is required.
+        "fonts": [
+            {  // Dict for defining a single font entry.
+
+                // Mandatory String: specify font file name in the system.
+                // This must be the file name in the system image.
+                "file": "Roboto-Regular.ttf",
+
+                // Optional String: specify the PostScript name of the font.
+                // This can be optional if the filename without extension is the
+                // same as the PostScript name.
+                "postScriptName": "Roboto",
+
+                // Optional String: specify weight of the font.
+                "weight": "100",
+
+                // Optional String: specify style of the font.
+                // Currently, only "normal" or "italic" is supported.
+                "style": "normal",
+
+                // Optional String: specify supported axes for automatic
+                // adjustment. Currently, only "wght" or "wght,ital" is
+                // supported.
+                "supportedAxes": "wght"
+
+                // Optional Dict: specify variation settings for this font.
+                "axes": {
+                    // Optional key to float dictionaty entry for speicying axis
+                    // values.
+                    "wdth": 100.0,
+                }
+            },
+        ]
+    }
+]
+"""
+
+import sys
+
+from commandline import parse_commandline
+from xml_builder import main
+
+if __name__ == "__main__":
+  args = parse_commandline(sys.argv[1:])
+  main(args)
diff --git a/data/fonts/script/test/Android.bp b/data/fonts/script/test/Android.bp
new file mode 100644
index 0000000..ff1ba4c
--- /dev/null
+++ b/data/fonts/script/test/Android.bp
@@ -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 {
+    default_team: "trendy_team_android_text",
+}
+
+python_test_host {
+    name: "generate_fonts_xml_test",
+    main: "test_main.py",
+    srcs: [
+        "test_*.py",
+    ],
+    libs: ["generate_fonts_xml_lib"],
+    test_options: {
+        unit_test: true,
+    },
+}
diff --git a/data/fonts/script/test/test_alias_builder.py b/data/fonts/script/test/test_alias_builder.py
new file mode 100755
index 0000000..c8ce961
--- /dev/null
+++ b/data/fonts/script/test/test_alias_builder.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import sys
+import unittest
+
+from alias_builder import parse_alias_from_json
+
+
+class AliasBuilderTest(unittest.TestCase):
+
+  def test_parse_alias_invalid_name(self):
+    self.assertRaises(
+        AssertionError, parse_alias_from_json, """{ "name": [], "to": "to" }"""
+    )
+    self.assertRaises(
+        AssertionError, parse_alias_from_json, """{ "name": 1, "to": "to" }"""
+    )
+    self.assertRaises(
+        AssertionError, parse_alias_from_json, """{ "name": 0.5, "to": "to" }"""
+    )
+
+  def test_parse_alias_invalid_to(self):
+    self.assertRaises(
+        AssertionError,
+        parse_alias_from_json,
+        """{ "name": "name", "to": [] }""",
+    )
+    self.assertRaises(
+        AssertionError, parse_alias_from_json, """{ "name": "name", "to": 1 }"""
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_alias_from_json,
+        """{ "name": "name", "to": 0.4 }""",
+    )
+
+  def test_parse_alias_invalid_id(self):
+    self.assertRaises(
+        AssertionError,
+        parse_alias_from_json,
+        """{ "name": "name", "to": "to", "weight": [] }""",
+    )
+
+  def test_parse_alias_invalid_to(self):
+    self.assertRaises(
+        AssertionError,
+        parse_alias_from_json,
+        """{ "name": "name", "to": "name", "weight": [] }""",
+    )
+
+  def test_parse_alias(self):
+    alias = parse_alias_from_json("""
+    {
+      "name": "arial",
+      "to": "sans-serif"
+    }""")
+
+    self.assertEqual("arial", alias.name)
+    self.assertEqual("sans-serif", alias.to)
+    self.assertIsNone(alias.weight)
+
+  def test_parse_alias2(self):
+    alias = parse_alias_from_json("""
+    {
+      "name": "sans-serif-thin",
+      "to": "sans-serif",
+      "weight": 100
+    }""")
+
+    self.assertEqual("sans-serif-thin", alias.name)
+    self.assertEqual("sans-serif", alias.to)
+    self.assertEqual(100, alias.weight)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_commandline.py b/data/fonts/script/test/test_commandline.py
new file mode 100755
index 0000000..75318cc
--- /dev/null
+++ b/data/fonts/script/test/test_commandline.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import functools
+import sys
+import unittest
+
+import commandline
+
+
+class CommandlineTest(unittest.TestCase):
+
+  def fileread(filemap, path):
+    return filemap[path]
+
+  def test_commandline(self):
+    filemap = {}
+    filemap["aliases.json"] = (
+        """[{"name": "sans-serif-thin", "to": "sans-serif", "weight": 100}]"""
+    )
+    filemap["fallbacks.json"] = (
+        """[{"lang": "und-Arab"},{"lang": "und-Ethi"}]"""
+    )
+    filemap["family.json"] = """[{
+      "name": "sans-serif",
+      "fonts": [{
+        "file": "Roboto-Regular.ttf",
+        "supportedAxes": "wght,ital",
+        "axes": { "wdth": "100" }
+      }]
+    }, {
+      "name": "sans-serif-condensed",
+      "fonts": [{
+        "file": "Roboto-Regular.ttf",
+        "supportedAxes": "wght,ital",
+        "axes": { "wdth": "75" }
+      }]
+    }]"""
+
+    filemap["family2.json"] = """[{
+      "name": "roboto-flex",
+      "fonts": [{
+        "file": "RobotoFlex-Regular.ttf",
+        "supportedAxes": "wght",
+        "axes": { "wdth": "100" }
+      }]
+    }]"""
+
+    args = commandline.parse_commandline(
+        [
+            "-o",
+            "output.xml",
+            "--alias",
+            "aliases.json",
+            "--fallback",
+            "fallbacks.json",
+            "family.json",
+            "family2.json",
+        ],
+        functools.partial(CommandlineTest.fileread, filemap),
+    )
+
+    self.assertEquals("output.xml", args.outfile)
+
+    self.assertEquals(1, len(args.aliases))
+    self.assertEquals("sans-serif-thin", args.aliases[0].name)
+    self.assertEquals("sans-serif", args.aliases[0].to)
+    self.assertEquals(100, args.aliases[0].weight)
+
+    self.assertEquals(2, len(args.fallback))
+    # Order is not a part of expectation. Check the expected lang is included.
+    langs = set(["und-Arab", "und-Ethi"])
+    self.assertTrue(args.fallback[0].lang in langs)
+    self.assertTrue(args.fallback[1].lang in langs)
+
+    self.assertEquals(3, len(args.families))
+    # Order is not a part of expectation. Check the expected name is included.
+    names = set(["sans-serif", "sans-serif-condensed", "roboto-flex"])
+    self.assertTrue(args.families[0].name in names)
+    self.assertTrue(args.families[1].name in names)
+    self.assertTrue(args.families[2].name in names)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_custom_json.py b/data/fonts/script/test/test_custom_json.py
new file mode 100755
index 0000000..64586b4
--- /dev/null
+++ b/data/fonts/script/test/test_custom_json.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import sys
+import tempfile
+import unittest
+
+from custom_json import _load_json_with_comment
+
+
+class JsonParseTest(unittest.TestCase):
+
+  def test_json_with_comment(self):
+    self.assertEqual(
+        [],
+        _load_json_with_comment("""
+    // The line comment can be used in font JSON configuration.
+    []
+    """),
+    )
+
+  def test_json_with_comment_double_line_comment(self):
+    self.assertEqual(
+        [],
+        _load_json_with_comment("""
+    // The double line comment // should work.
+    []
+    """),
+    )
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_fallback_builder.py b/data/fonts/script/test/test_fallback_builder.py
new file mode 100755
index 0000000..1f6b600
--- /dev/null
+++ b/data/fonts/script/test/test_fallback_builder.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import sys
+import unittest
+
+from fallback_builder import parse_fallback_from_json
+
+
+class FallbackBuilderTest(unittest.TestCase):
+
+  def test_parse_fallback_invalid_lang(self):
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "lang": [] }]"""
+    )
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "lang": 1 }]"""
+    )
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "lang": 0.5 }]"""
+    )
+
+  def test_parse_fallback_invalid_id(self):
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "id": [] }]"""
+    )
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "id": 1 }]"""
+    )
+    self.assertRaises(
+        AssertionError, parse_fallback_from_json, """[{ "id": 0.5 }]"""
+    )
+
+  def test_parse_fallback_invalid(self):
+    self.assertRaises(
+        AssertionError,
+        parse_fallback_from_json,
+        """[{ "lang": "ja", "id": "Roboto-Regular.ttf" }]""",
+    )
+    self.assertRaises(AssertionError, parse_fallback_from_json, """[]""")
+    self.assertRaises(AssertionError, parse_fallback_from_json, """[{}]""")
+
+  def test_parse_fallback(self):
+    fallback = parse_fallback_from_json("""[
+    { "lang": "und-Arab" },
+    { "id": "NotoSansSymbols-Regular-Subsetted.ttf" },
+    { "lang": "ja" }
+    ]""")
+
+    self.assertEqual(3, len(fallback))
+
+    self.assertEqual("und-Arab", fallback[0].lang)
+    self.assertIsNone(fallback[0].id)
+
+    self.assertIsNone(fallback[1].lang)
+    self.assertEqual("NotoSansSymbols-Regular-Subsetted.ttf", fallback[1].id)
+
+    self.assertEqual("ja", fallback[2].lang)
+    self.assertIsNone(fallback[2].id)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_family_builder.py b/data/fonts/script/test/test_family_builder.py
new file mode 100755
index 0000000..5b20cee
--- /dev/null
+++ b/data/fonts/script/test/test_family_builder.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import sys
+import unittest
+
+from family_builder import parse_family_from_json
+from family_builder import parse_family_from_json_for_sanitization_test
+
+_VALID_FONT_JSON = """[{ "file": "a.ttf", "weight": 400, "style": "normal" }]"""
+
+
+class FamilyBuilderTest(unittest.TestCase):
+
+  def test_parse_family_invalid_id(self):
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "id": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "id": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "id": 0.5 }""",
+    )
+
+  def test_parse_family_invalid_lang(self):
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "lang": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "lang": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "lang": 0.5 }""",
+    )
+
+  def test_parse_family_invalid_name(self):
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "name": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "name": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "name": 0.5 }""",
+    )
+
+  def test_parse_family_invalid_variant(self):
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "variant": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "variant": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "variant": 0.5 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "variant": "default" }""",
+    )
+
+  def test_parse_family_invalid_fallback_for(self):
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "fallbackFor": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "fallbackFor": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json_for_sanitization_test,
+        """{ "name": 0.5 }""",
+    )
+
+  def test_parse_invalid_family(self):
+    # fallbackFor and target should be specified altogether
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json,
+        """{ "fallbackFor": "serif", "fonts": %s } """ % _VALID_FONT_JSON,
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json,
+        """{ "target": "Roboto", "fonts": %s } """ % _VALID_FONT_JSON,
+    )
+
+    # Invalid fonts
+    self.assertRaises(AssertionError, parse_family_from_json, """{} """)
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json,
+        """{ "fonts": [] } """,
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_family_from_json,
+        """{ "fonts": {} } """,
+    )
+
+  def test_parse_family(self):
+    family = parse_family_from_json("""
+    {
+      "lang": "und-Arab",
+      "variant": "compact",
+      "fonts": [{
+        "file": "NotoNaskhArabicUI-Regular.ttf",
+        "postScriptName": "NotoNaskhArabicUI",
+        "weight": "400",
+        "style": "normal"
+      }, {
+        "file": "NotoNaskhArabicUI-Bold.ttf",
+        "weight": "700",
+        "style": "normal"
+      }]
+    }""")
+
+    self.assertEqual("und-Arab", family.lang)
+    self.assertEqual("compact", family.variant)
+    self.assertEqual(2, len(family.fonts))
+    self.assertIsNone(family.id)
+    self.assertIsNone(family.name)
+    self.assertIsNone(family.fallback_for)
+    self.assertIsNone(family.target)
+
+  def test_parse_family2(self):
+    family = parse_family_from_json("""
+    {
+      "id": "NotoSansCJK_zh-Hans",
+      "lang": "zh-Hans",
+      "fonts": [{
+        "file": "NotoSansCJK-Regular.ttc",
+        "postScriptName": "NotoSansCJKJP-Regular",
+        "weight": "400",
+        "style": "normal",
+        "supportedAxes": "wght",
+        "axes": {
+          "wght": "400"
+        },
+        "index": "2"
+      }]
+    }""")
+
+    self.assertEqual("NotoSansCJK_zh-Hans", family.id)
+    self.assertEqual("zh-Hans", family.lang)
+    self.assertEqual(1, len(family.fonts))
+    self.assertIsNone(family.name)
+    self.assertIsNone(family.target)
+
+  def test_parse_family3(self):
+    family = parse_family_from_json("""
+    {
+      "lang": "zh-Hans",
+      "fonts": [{
+        "file": "NotoSerifCJK-Regular.ttc",
+        "postScriptName": "NotoSerifCJKjp-Regular",
+        "weight": "400",
+        "style": "normal",
+        "index": "2"
+      }],
+      "target": "NotoSansCJK_zh-Hans",
+      "fallbackFor": "serif"
+    }
+    """)
+
+    self.assertEqual("zh-Hans", family.lang)
+    self.assertEqual(1, len(family.fonts))
+    self.assertEqual("serif", family.fallback_for)
+    self.assertEqual("NotoSansCJK_zh-Hans", family.target)
+    self.assertIsNone(family.name)
+    self.assertIsNone(family.variant)
+
+  def test_parse_family4(self):
+    family = parse_family_from_json("""
+    {
+      "name": "sans-serif",
+      "fonts": [{
+        "file": "Roboto-Regular.ttf",
+        "supportedAxes": "wght,ital",
+        "axes": {
+          "wdth": "100"
+        }
+      }]
+    }
+    """)
+
+    self.assertEqual("sans-serif", family.name)
+    self.assertEqual(1, len(family.fonts))
+    self.assertIsNone(family.lang)
+    self.assertIsNone(family.fallback_for)
+    self.assertIsNone(family.target)
+    self.assertIsNone(family.variant)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_font_builder.py b/data/fonts/script/test/test_font_builder.py
new file mode 100755
index 0000000..a114cd3
--- /dev/null
+++ b/data/fonts/script/test/test_font_builder.py
@@ -0,0 +1,379 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import json
+import sys
+import unittest
+
+from font_builder import parse_font_from_json_for_sanitization_test, parse_fonts_from_json_for_validation_test
+
+
+class FontBuilderTest(unittest.TestCase):
+
+  def test_parse_font_invalid_file(self):
+    # File must be string
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "file": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "file": -10 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "file": 0.5 }""",
+    )
+
+  def test_parse_font_invalid_weight(self):
+    # Weight only accept integer or string as integer.
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": 0.5 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": "0.5" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": -10 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": 1001 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": "-10" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "weight": "1001" }""",
+    )
+
+  def test_parse_font_invalid_style(self):
+    # Style only accept string "noromal" or "italic"
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "style": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "style": 0 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "style": "foo" }""",
+    )
+
+  def test_parse_font_invalid_index(self):
+    # Index only accepts integer or string as integer that equals or larger than zero.
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "index": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "index": "foo" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "index": -1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "index": "-1" }""",
+    )
+
+  def test_parse_font_invalid_supportedAxes(self):
+    # The supportedAxes only accepts wght or wght,ital.
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": 0 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": 0.5 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": "1" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": "ital" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "supportedAxes": "wghtital" }""",
+    )
+
+  def test_parse_font_invalid_post_script_name(self):
+    # The postScriptName only accepts string.
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "postScriptName": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "postScriptName": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "postScriptName": 0.5 }""",
+    )
+
+  def test_parse_font_invalid_axes(self):
+    # The axes accept OpenType tag to float value.
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "axes": [] }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "axes": "foo" }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "axes": 1 }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{
+                        "axes":{
+                          "wght": "ital"
+                        }
+                      }""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{
+                        "axes":{
+                          "weight": 100
+                        }
+                      }""",
+    )
+
+  def test_parse_font_unknown_key(self):
+    self.assertRaises(
+        AssertionError,
+        parse_font_from_json_for_sanitization_test,
+        """{ "font": "Roboto-Regular.ttf" }""",
+    )
+
+  def test_parse_font_invalid_font(self):
+    # empty fonts are not allowed
+    self.assertRaises(
+        AssertionError, parse_fonts_from_json_for_validation_test, """[]"""
+    )
+    # At least file should be specified
+    self.assertRaises(
+        AssertionError, parse_fonts_from_json_for_validation_test, """[{}]"""
+    )
+    # If supportedAxes is not spccified, weight and style should be specified.
+    self.assertRaises(
+        AssertionError,
+        parse_fonts_from_json_for_validation_test,
+        """[{
+                        "file": "Roboto-Regular.ttf",
+                        "weight": 400
+                      }]""",
+    )
+    self.assertRaises(
+        AssertionError,
+        parse_fonts_from_json_for_validation_test,
+        """[{
+                        "file": "Roboto-Regular.ttf",
+                        "style": "normal"
+                      }]""",
+    )
+
+  def test_parse_font(self):
+    fonts = parse_fonts_from_json_for_validation_test("""[
+      {
+        "file": "Roboto-Regular.ttf",
+        "weight": 700,
+        "style": "normal",
+        "axes": {
+          "wght": 700
+        }
+      }, {
+        "file": "Roboto-Italic.ttf",
+        "weight": 700,
+        "style": "italic",
+        "axes": {
+          "wght": 700
+        }
+      }
+    ]""")
+    self.assertEqual(2, len(fonts))
+
+    self.assertEqual("Roboto-Regular.ttf", fonts[0].file)
+    self.assertEqual(700, fonts[0].weight)
+    self.assertEqual("normal", fonts[0].style)
+    self.assertEqual(1, len(fonts[0].axes))
+    self.assertEqual(700, fonts[0].axes["wght"])
+    self.assertIsNone(fonts[0].index)
+    self.assertIsNone(fonts[0].supported_axes)
+    self.assertIsNone(fonts[0].post_script_name)
+
+    self.assertEqual("Roboto-Italic.ttf", fonts[1].file)
+    self.assertEqual(700, fonts[1].weight)
+    self.assertEqual("italic", fonts[1].style)
+    self.assertEqual(1, len(fonts[1].axes))
+    self.assertEqual(700, fonts[1].axes["wght"])
+    self.assertIsNone(fonts[1].index)
+    self.assertIsNone(fonts[1].supported_axes)
+    self.assertIsNone(fonts[1].post_script_name)
+
+  def test_parse_font2(self):
+    fonts = parse_fonts_from_json_for_validation_test("""[
+      {
+        "file": "RobotoFlex-Regular.ttf",
+        "supportedAxes": "wght",
+        "axes": {
+          "wdth": 100
+        }
+      }
+    ]""")
+    self.assertEqual(1, len(fonts))
+
+    self.assertEqual("RobotoFlex-Regular.ttf", fonts[0].file)
+    self.assertEqual(1, len(fonts[0].axes))
+    self.assertEqual(100, fonts[0].axes["wdth"])
+    self.assertIsNone(fonts[0].index)
+    self.assertIsNone(fonts[0].weight)
+    self.assertIsNone(fonts[0].style)
+    self.assertIsNone(fonts[0].post_script_name)
+
+  def test_parse_font3(self):
+    fonts = parse_fonts_from_json_for_validation_test("""[
+      {
+        "file": "SourceSansPro-Regular.ttf",
+        "weight": 400,
+        "style": "normal"
+      }, {
+        "file": "SourceSansPro-Italic.ttf",
+        "weight": 400,
+        "style": "italic"
+      }, {
+        "file": "SourceSansPro-SemiBold.ttf",
+        "weight": 600,
+        "style": "normal"
+      }, {
+        "file": "SourceSansPro-SemiBoldItalic.ttf",
+        "weight": 600,
+        "style": "italic"
+      }, {
+        "file": "SourceSansPro-Bold.ttf",
+        "weight": 700,
+        "style": "normal"
+      }, {
+        "file": "SourceSansPro-BoldItalic.ttf",
+        "weight": 700,
+        "style": "italic"
+      }
+    ]""")
+
+    self.assertEqual(6, len(fonts))
+
+    self.assertEqual("SourceSansPro-Regular.ttf", fonts[0].file)
+    self.assertEqual(400, fonts[0].weight)
+    self.assertEqual("normal", fonts[0].style)
+
+    self.assertEqual("SourceSansPro-Italic.ttf", fonts[1].file)
+    self.assertEqual(400, fonts[1].weight)
+    self.assertEqual("italic", fonts[1].style)
+
+    self.assertEqual("SourceSansPro-SemiBold.ttf", fonts[2].file)
+    self.assertEqual(600, fonts[2].weight)
+    self.assertEqual("normal", fonts[2].style)
+
+    self.assertEqual("SourceSansPro-SemiBoldItalic.ttf", fonts[3].file)
+    self.assertEqual(600, fonts[3].weight)
+    self.assertEqual("italic", fonts[3].style)
+
+    self.assertEqual("SourceSansPro-Bold.ttf", fonts[4].file)
+    self.assertEqual(700, fonts[4].weight)
+    self.assertEqual("normal", fonts[4].style)
+
+    self.assertEqual("SourceSansPro-BoldItalic.ttf", fonts[5].file)
+    self.assertEqual(700, fonts[5].weight)
+    self.assertEqual("italic", fonts[5].style)
+
+  def test_parse_font4(self):
+    fonts = parse_fonts_from_json_for_validation_test("""[
+      {
+        "file": "NotoSerifCJK-Regular.ttc",
+        "postScriptName": "NotoSerifCJKjp-Regular",
+        "weight": "400",
+        "style": "normal",
+        "index": "2"
+      }
+    ]""")
+    self.assertEqual(1, len(fonts))
+
+    self.assertEqual("NotoSerifCJK-Regular.ttc", fonts[0].file)
+    self.assertEqual("NotoSerifCJKjp-Regular", fonts[0].post_script_name)
+    self.assertEqual(400, fonts[0].weight)
+    self.assertEqual("normal", fonts[0].style)
+    self.assertEqual(2, fonts[0].index)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/test/test_main.py b/data/fonts/script/test/test_main.py
new file mode 100755
index 0000000..7a2a9da
--- /dev/null
+++ b/data/fonts/script/test/test_main.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import os
+import sys
+import tempfile
+import unittest
+
+import test_alias_builder
+import test_commandline
+import test_custom_json
+import test_fallback_builder
+import test_family_builder
+import test_font_builder
+import test_xml_builder
+
+if __name__ == "__main__":
+  loader = unittest.TestLoader()
+  # TODO: can we load all tests from the directory?
+  testsuite = unittest.suite.TestSuite()
+  testsuite.addTest(loader.loadTestsFromModule(test_alias_builder))
+  testsuite.addTest(loader.loadTestsFromModule(test_commandline))
+  testsuite.addTest(loader.loadTestsFromModule(test_custom_json))
+  testsuite.addTest(loader.loadTestsFromModule(test_fallback_builder))
+  testsuite.addTest(loader.loadTestsFromModule(test_family_builder))
+  testsuite.addTest(loader.loadTestsFromModule(test_font_builder))
+  testsuite.addTest(loader.loadTestsFromModule(test_xml_builder))
+  assert testsuite.countTestCases()
+  unittest.TextTestRunner(verbosity=2).run(testsuite)
diff --git a/data/fonts/script/test/test_xml_builder.py b/data/fonts/script/test/test_xml_builder.py
new file mode 100755
index 0000000..24a033b
--- /dev/null
+++ b/data/fonts/script/test/test_xml_builder.py
@@ -0,0 +1,344 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import random
+import sys
+import unittest
+
+from alias_builder import parse_aliases_from_json
+from commandline import CommandlineArgs
+from fallback_builder import parse_fallback_from_json
+from family_builder import parse_family_from_json
+from xml_builder import FallbackOrder
+from xml_builder import generate_xml
+
+_SANS_SERIF = parse_family_from_json("""{
+  "name": "sans-serif",
+  "fonts": [{
+    "file": "Roboto-Regular.ttf",
+    "supportedAxes": "wght,ital",
+    "axes": { "wdth": "100" }
+  }]
+}""")
+
+_SERIF = parse_family_from_json("""{
+  "name": "serif",
+  "fonts": [{
+    "file": "NotoSerif-Regular.ttf",
+    "postScriptName": "NotoSerif",
+    "weight": "400",
+    "style": "normal"
+  }, {
+    "file": "NotoSerif-Bold.ttf",
+    "weight": "700",
+    "style": "normal"
+  }, {
+    "file": "NotoSerif-Italic.ttf",
+    "weight": "400",
+    "style": "italic"
+  }, {
+    "file": "NotoSerif-BoldItalic.ttf",
+    "weight": "700",
+    "style": "italic"
+  }]
+}""")
+
+_ROBOTO_FLEX = parse_family_from_json("""{
+  "name": "roboto-flex",
+  "fonts": [{
+    "file": "RobotoFlex-Regular.ttf",
+    "supportedAxes": "wght",
+    "axes": { "wdth": "100" }
+  }]
+}""")
+
+_ARABIC = parse_family_from_json("""{
+  "lang": "und-Arab",
+  "variant": "elegant",
+  "fonts": [{
+    "file": "NotoNaskhArabic-Regular.ttf",
+    "postScriptName": "NotoNaskhArabic",
+    "weight": "400",
+    "style": "normal"
+  }, {
+    "file": "NotoNaskhArabic-Bold.ttf",
+    "weight": "700",
+    "style": "normal"
+  }]
+}""")
+
+_ARABIC_UI = parse_family_from_json("""{
+  "lang": "und-Arab",
+  "variant": "compact",
+  "fonts": [{
+    "file": "NotoNaskhArabicUI-Regular.ttf",
+    "postScriptName": "NotoNaskhArabicUI",
+    "weight": "400",
+    "style": "normal"
+  }, {
+    "file": "NotoNaskhArabicUI-Bold.ttf",
+    "weight": "700",
+    "style": "normal"
+  }]
+}""")
+
+_HANS = parse_family_from_json("""{
+ "lang": "zh-Hans",
+  "fonts": [{
+    "file": "NotoSansCJK-Regular.ttc",
+    "postScriptName": "NotoSansCJKJP-Regular",
+    "weight": "400",
+    "style": "normal",
+    "supportedAxes": "wght",
+    "axes": { "wght": "400" },
+    "index": "2"
+  }],
+  "id": "NotoSansCJK_zh-Hans"
+}""")
+
+_JA = parse_family_from_json("""{
+  "lang": "ja",
+  "fonts": [{
+    "file": "NotoSansCJK-Regular.ttc",
+    "postScriptName": "NotoSansCJKJP-Regular",
+    "weight": "400",
+    "style": "normal",
+    "supportedAxes": "wght",
+    "axes": { "wght": "400" },
+    "index": "0"
+  }],
+  "id": "NotoSansCJK_ja"
+}""")
+
+_JA_HENTAIGANA = parse_family_from_json("""{
+  "lang": "ja",
+  "priority": 100,
+  "fonts": [{
+    "file": "NotoSerifHentaigana.ttf",
+    "postScriptName": "NotoSerifHentaigana-ExtraLight",
+    "supportedAxes": "wght",
+    "axes": { "wght": "400" }
+  }]
+}""")
+
+_HANS_SERIF = parse_family_from_json("""{
+  "lang": "zh-Hans",
+  "fonts": [{
+    "file": "NotoSerifCJK-Regular.ttc",
+    "postScriptName": "NotoSerifCJKjp-Regular",
+    "weight": "400",
+    "style": "normal",
+    "index": "2"
+  }],
+  "fallbackFor": "serif",
+  "target": "NotoSansCJK_zh-Hans"
+}""")
+
+_JA_SERIF = parse_family_from_json("""{
+  "lang": "ja",
+  "fonts": [{
+    "file": "NotoSerifCJK-Regular.ttc",
+    "postScriptName": "NotoSerifCJKjp-Regular",
+    "weight": "400",
+    "style": "normal",
+    "index": "0"
+  }],
+  "fallbackFor": "serif",
+  "target": "NotoSansCJK_ja"
+}""")
+
+_FALLBACK = parse_fallback_from_json("""[
+  { "lang": "und-Arab" },
+  { "lang": "zh-Hans" },
+  { "lang": "ja" }
+]""")
+
+_ALIASES = parse_aliases_from_json("""[
+  {
+    "name": "sans-serif-thin",
+    "to" : "sans-serif",
+    "weight": 100
+  }
+]""")
+
+
+class FallbackOrderTest(unittest.TestCase):
+
+  def test_fallback_order(self):
+    order = FallbackOrder(_FALLBACK)
+
+    # Arabic and Arabic UI are prioritized over Simplified Chinese
+    self.assertTrue(order(_ARABIC) < order(_HANS))
+    self.assertTrue(order(_ARABIC_UI) < order(_HANS))
+
+    # Simplified Chinese is prioritized over Japanese
+    self.assertTrue(order(_HANS) < order(_JA))
+
+  def test_fallback_order_variant(self):
+    order = FallbackOrder(_FALLBACK)
+
+    # Arabic is prioritize over Arabic UI
+    self.assertTrue(order(_ARABIC) < order(_ARABIC_UI))
+
+  def test_fallback_order_unknown_priority(self):
+    order = FallbackOrder(parse_fallback_from_json("""[
+      { "lang": "zh-Hans" }
+    ]"""))
+
+    self.assertRaises(AssertionError, order, _ARABIC)
+
+  def test_fallback_order_id_and_lang(self):
+    order = FallbackOrder(_FALLBACK)
+
+    # If both ID and lang matches the fallback, the ID is used.
+    self.assertTrue(order(_HANS) < order(_JA))
+
+
+class XmlBuilderTest(unittest.TestCase):
+
+  def test_no_duplicate_families(self):
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=[],
+        families=[_SANS_SERIF, _ROBOTO_FLEX, _ROBOTO_FLEX],
+    )
+
+  def test_mandatory_sans_serif(self):
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=[],
+        families=[_ARABIC, _ARABIC_UI, _HANS, _JA],
+    )
+
+  def test_missing_fallback_target(self):
+    # serif family is necessary for fallback.
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=[],
+        families=[_SANS_SERIF, _HANS_SERIF],
+    )
+
+    # target family is necessary for fallback.
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=[],
+        families=[_SANS_SERIF, _SERIF, _HANS_SERIF],
+    )
+
+  def test_missing_alias_target(self):
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=parse_aliases_from_json("""[{
+        "name": "serif-thin",
+        "to" : "serif",
+        "weight": 100
+      }]"""),
+        families=[_SANS_SERIF, _HANS_SERIF],
+    )
+
+  def test_duplicated_alias(self):
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=parse_aliases_from_json("""[{
+        "name": "serif-thin",
+        "to" : "serif",
+        "weight": 100
+      },{
+        "name": "serif-thin",
+        "to" : "serif",
+        "weight": 100
+      }]"""),
+        families=[_SANS_SERIF, _SERIF, _HANS_SERIF],
+    )
+
+  def test_same_priority(self):
+    self.assertRaises(
+        AssertionError,
+        generate_xml,
+        fallback=_FALLBACK,
+        aliases=[],
+        families=[_SANS_SERIF, _JA, _JA],
+    )
+
+  def test_generate_xml(self):
+    xml = generate_xml(
+        fallback=_FALLBACK,
+        aliases=_ALIASES,
+        families=[
+            _SANS_SERIF,
+            _SERIF,
+            _ARABIC,
+            _ARABIC_UI,
+            _HANS,
+            _HANS_SERIF,
+            _JA,
+            _JA_SERIF,
+            _JA_HENTAIGANA,
+        ],
+    )
+
+    self.expect_xml(xml)
+
+  def test_generate_xml_reordered(self):
+    families = [
+        _SANS_SERIF,
+        _SERIF,
+        _ARABIC,
+        _ARABIC_UI,
+        _HANS,
+        _HANS_SERIF,
+        _JA,
+        _JA_SERIF,
+        _JA_HENTAIGANA,
+    ]
+
+    for i in range(0, 10):
+      random.shuffle(families)
+      xml = generate_xml(
+          fallback=_FALLBACK, aliases=_ALIASES, families=families
+      )
+
+      self.expect_xml(xml)
+
+  def expect_xml(self, xml):
+    self.assertEquals("sans-serif", xml.families[0].name)  # _SANS_SERIF
+    self.assertEquals("serif", xml.families[1].name)  # _SERIF
+    self.assertEquals("und-Arab", xml.families[2].lang)  # __ARABIC
+    self.assertEquals("elegant", xml.families[2].variant)
+    self.assertEquals("und-Arab", xml.families[3].lang)  # _ARABIC_UI
+    self.assertEquals("zh-Hans", xml.families[4].lang)  # _HANS (_HANS_SERIF)
+    self.assertEquals(2, len(xml.families[4].fonts))
+    self.assertEquals("serif", xml.families[4].fonts[1].fallback_for)
+    self.assertEquals("ja", xml.families[5].lang)  # _HANS (_HANS_SERIF)
+    self.assertEquals("serif", xml.families[5].fonts[1].fallback_for)
+
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)
diff --git a/data/fonts/script/validators.py b/data/fonts/script/validators.py
new file mode 100755
index 0000000..9407a59
--- /dev/null
+++ b/data/fonts/script/validators.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Validators commonly used."""
+
+
+def check_str_or_none(d, key: str) -> str | None:
+  value = d.get(key)
+  assert value is None or isinstance(value, str), (
+      "%s type must be str or None." % key
+  )
+  return value
+
+
+def check_str(d, key: str) -> str:
+  value = d.get(key)
+  assert isinstance(value, str), "%s type must be str." % key
+  return value
+
+
+def check_int_or_none(d, key: str) -> int | None:
+  """Chcek if the given value of key in dict is int or None."""
+  value = d.get(key)
+  if value is None:
+    return None
+  elif isinstance(value, int):
+    return value
+  elif isinstance(value, str):
+    try:
+      return int(value)
+    except ValueError as e:
+      raise AssertionError() from e
+  else:
+    raise AssertionError("%s type must be int or str or None." % key)
+
+
+def check_float(d, key: str) -> float:
+  """Chcek if the given value of key in dict is float."""
+  value = d.get(key)
+  if isinstance(value, float):
+    return value
+  elif isinstance(value, int):
+    return float(value)
+  elif isinstance(value, str):
+    try:
+      return float(value)
+    except ValueError as e:
+      raise AssertionError() from e
+  else:
+    raise AssertionError("Float value is expeted but it is %s" % key)
+
+
+def check_weight_or_none(d, key: str) -> int | None:
+  value = check_int_or_none(d, key)
+
+  assert value is None or (
+      value >= 0 and value <= 1000
+  ), "weight must be larger than 0 and lower than 1000."
+  return value
+
+
+def check_priority_or_none(d, key: str) -> int | None:
+  value = check_int_or_none(d, key)
+
+  assert value is None or (
+      value >= -100 and value <= 100
+  ), "priority must be between -100 (highest) to 100 (lowest)"
+  return value
+
+
+def check_enum_or_none(d, key: str, enum: [str]) -> str | None:
+  value = check_str_or_none(d, key)
+
+  assert value is None or value in enum, "%s must be None or one of %s" % (
+      key,
+      enum,
+  )
+  return value
+
+
+def check_tag(value) -> str:
+  if len(value) != 4 or not value.isascii():
+    raise AssertionError("OpenType tag must be 4 ASCII letters: %s" % value)
+  return value
diff --git a/data/fonts/script/xml_builder.py b/data/fonts/script/xml_builder.py
new file mode 100755
index 0000000..38daebc
--- /dev/null
+++ b/data/fonts/script/xml_builder.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+"""Build XML."""
+
+import dataclasses
+import functools
+from xml.dom import minidom
+from xml.etree import ElementTree
+from alias_builder import Alias
+from commandline import CommandlineArgs
+from fallback_builder import FallbackEntry
+from family_builder import Family
+from font_builder import Font
+
+
[email protected]
+class XmlFont:
+  """Class used for writing XML. All elements are str or None."""
+
+  file: str
+  weight: str | None
+  style: str | None
+  index: str | None
+  supported_axes: str | None
+  post_script_name: str | None
+  fallback_for: str | None
+  axes: dict[str | str]
+
+
+def font_to_xml_font(font: Font, fallback_for=None) -> XmlFont:
+  axes = None
+  if font.axes:
+    axes = {key: str(value) for key, value in font.axes.items()}
+  return XmlFont(
+      file=font.file,
+      weight=str(font.weight) if font.weight is not None else None,
+      style=font.style,
+      index=str(font.index) if font.index is not None else None,
+      supported_axes=font.supported_axes,
+      post_script_name=font.post_script_name,
+      fallback_for=fallback_for,
+      axes=axes,
+  )
+
+
[email protected]
+class XmlFamily:
+  """Class used for writing XML. All elements are str or None."""
+
+  name: str | None
+  lang: str | None
+  variant: str | None
+  fonts: [XmlFont]
+
+
+def family_to_xml_family(family: Family) -> XmlFamily:
+  return XmlFamily(
+      name=family.name,
+      lang=family.lang,
+      variant=family.variant,
+      fonts=[font_to_xml_font(f) for f in family.fonts],
+  )
+
+
[email protected]
+class XmlAlias:
+  """Class used for writing XML. All elements are str or None."""
+
+  name: str
+  to: str
+  weight: str | None
+
+
+def alias_to_xml_alias(alias: Alias) -> XmlAlias:
+  return XmlAlias(
+      name=alias.name,
+      to=alias.to,
+      weight=str(alias.weight) if alias.weight is not None else None,
+  )
+
+
[email protected]
+class FallbackXml:
+  families: [XmlFamily]
+  aliases: [XmlAlias]
+
+
+class FallbackOrder:
+  """Provides a ordering of the family."""
+
+  def __init__(self, fallback: [FallbackEntry]):
+    # Preprocess fallbacks from flatten key to priority value.
+    # The priority is a index appeared the fallback entry.
+    # The key will be lang or file prefixed string, e.g. "lang:und-Arab" -> 0,
+    # "file:Roboto-Regular.ttf" -> 10, etc.
+    fallback_priority = {}
+    for priority, fallback in enumerate(fallback):
+      if fallback.lang:
+        fallback_priority['lang:%s' % fallback.lang] = priority
+      else:  # fallback.file is not None
+        fallback_priority['id:%s' % fallback.id] = priority
+
+    self.priority = fallback_priority
+
+  def __call__(self, family: Family):
+    """Returns priority of the family. Lower value means higher priority."""
+    priority = None
+    if family.id:
+      priority = self.priority.get('id:%s' % family.id)
+    if not priority and family.lang:
+      priority = self.priority.get('lang:%s' % family.lang)
+
+    assert priority is not None, 'Unknown priority for %s' % family
+
+    # Priority adjustments.
+    # First, give extra score to compact for compatibility.
+    priority = priority * 10
+    if family.variant == 'compact':
+      priority = priority + 5
+
+    # Next, give extra priority score. The priority is -100 to 100,
+    # Not to mixed in other scores, shift this range to 0 to 200 and give it
+    # to current priority.
+    priority = priority * 1000
+    custom_priority = family.priority if family.priority else 0
+    priority = priority + custom_priority + 100
+
+    return priority
+
+
+def generate_xml(
+    fallback: [FallbackEntry], aliases: [Alias], families: [Family]
+) -> FallbackXml:
+  """Generats FallbackXML objects."""
+
+  # Step 1. Categorize families into following three.
+
+  # The named family is converted to XmlFamily in this step.
+  named_families: [str | XmlFamily] = {}
+  # The list of Families used for locale fallback.
+  fallback_families: [Family] = []
+  # The list of Families that has fallbackFor attribute.
+  font_fallback_families: [Family] = []
+
+  for family in families:
+    if family.name:  # process named family
+      assert family.name not in named_families, (
+          'Duplicated named family entry: %s' % family.name
+      )
+      named_families[family.name] = family_to_xml_family(family)
+    elif family.fallback_for:
+      font_fallback_families.append(family)
+    else:
+      fallback_families.append(family)
+
+  # Step 2. Convert Alias to XmlAlias with validation.
+  xml_aliases = []
+  available_names = set(named_families.keys())
+  for alias in aliases:
+    assert alias.name not in available_names, (
+        'duplicated name alias: %s' % alias
+    )
+    available_names.add(alias.name)
+
+  for alias in aliases:
+    assert alias.to in available_names, 'unknown alias to: %s' % alias
+    xml_aliases.append(alias_to_xml_alias(alias))
+
+  # Step 3. Reorder the fallback families with fallback priority.
+  order = FallbackOrder(fallback)
+  fallback_families.sort(
+      key=functools.cmp_to_key(lambda l, r: order(l) - order(r))
+  )
+  for i, j in zip(fallback_families, fallback_families[1:]):
+    assert order(i) != order(j), 'Same priority: %s vs %s' % (i, j)
+
+  # Step 4. Place named families first.
+  # Place sans-serif at the top of family list.
+  assert 'sans-serif' in named_families, 'sans-serif family must exists'
+  xml_families = [family_to_xml_family(named_families.pop('sans-serif'))]
+  xml_families = xml_families + list(named_families.values())
+
+  # Step 5. Convert fallback_families from Family to XmlFamily.
+  # Also create ID to XmlFamily map which is used for resolving fallbackFor
+  # attributes.
+  id_to_family: [str | XmlFamily] = {}
+  for family in fallback_families:
+    xml_family = family_to_xml_family(family)
+    xml_families.append(xml_family)
+    if family.id:
+      id_to_family[family.id] = xml_family
+
+  # Step 6. Add font fallback to the target XmlFamily
+  for family in font_fallback_families:
+    assert family.fallback_for in named_families, (
+        'Unknown fallback for: %s' % family
+    )
+    assert family.target in id_to_family, 'Unknown target for %s' % family
+
+    xml_family = id_to_family[family.target]
+    xml_family.fonts = xml_family.fonts + [
+        font_to_xml_font(f, family.fallback_for) for f in family.fonts
+    ]
+
+  # Step 7. Build output
+  return FallbackXml(aliases=xml_aliases, families=xml_families)
+
+
+def write_xml(outfile: str, xml: FallbackXml):
+  """Writes given xml object into into outfile as XML."""
+  familyset = ElementTree.Element('familyset')
+
+  for family in xml.families:
+    family_node = ElementTree.SubElement(familyset, 'family')
+    if family.lang:
+      family_node.set('lang', family.lang)
+    if family.name:
+      family_node.set('name', family.name)
+    if family.variant:
+      family_node.set('variant', family.variant)
+
+    for font in family.fonts:
+      font_node = ElementTree.SubElement(family_node, 'font')
+      if font.weight:
+        font_node.set('weight', font.weight)
+      if font.style:
+        font_node.set('style', font.style)
+      if font.index:
+        font_node.set('index', font.index)
+      if font.supported_axes:
+        font_node.set('supportedAxes', font.supported_axes)
+      if font.fallback_for:
+        font_node.set('fallbackFor', font.fallback_for)
+      if font.post_script_name:
+        font_node.set('postScriptName', font.post_script_name)
+
+      font_node.text = font.file
+
+      if font.axes:
+        for tag, value in font.axes.items():
+          axis_node = ElementTree.SubElement(font_node, 'axis')
+          axis_node.set('tag', tag)
+          axis_node.set('stylevalue', value)
+
+  for alias in xml.aliases:
+    alias_node = ElementTree.SubElement(familyset, 'alias')
+    alias_node.set('name', alias.name)
+    alias_node.set('to', alias.to)
+    if alias.weight:
+      alias_node.set('weight', alias.weight)
+
+  doc = minidom.parseString(ElementTree.tostring(familyset, 'utf-8'))
+  with open(outfile, 'w') as f:
+    doc.writexml(f, encoding='utf-8', newl='\n', indent='', addindent='  ')
+
+
+def main(args: CommandlineArgs):
+  xml = generate_xml(args.fallback, args.aliases, args.families)
+  write_xml(args.outfile, xml)
diff --git a/data/keyboards/Vendor_0957_Product_0033.idc b/data/keyboards/Vendor_0957_Product_0033.idc
new file mode 100644
index 0000000..7dfbe2c
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0033.idc
@@ -0,0 +1,23 @@
+# 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.
+#
+
+# Input Device Configuration file for Google Reference RCU Remote.
+# PID 0033 is for new G20 with start button.
+
+# Basic Parameters
+keyboard.layout = Vendor_0957_Product_0031
+# The reason why we set is follow https://docs.partner.android.com/tv/build/gtv/boot-resume
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
diff --git a/graphics/java/android/graphics/Matrix44.java b/graphics/java/android/graphics/Matrix44.java
index a99e201..683f614 100644
--- a/graphics/java/android/graphics/Matrix44.java
+++ b/graphics/java/android/graphics/Matrix44.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import com.android.graphics.hwui.flags.Flags;
 
@@ -30,6 +31,7 @@
  * in row-major order. The values and operations are treated as column vectors.
  */
 @FlaggedApi(Flags.FLAG_MATRIX_44)
+@RavenwoodKeepWholeClass
 public class Matrix44 {
     final float[] mBackingArray;
     /**
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 618e6dc..c7b8941 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Drawable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -35,6 +36,7 @@
  * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
  * @see Drawable#getOutline(Outline)
  */
+@RavenwoodKeepWholeClass
 public final class Outline {
     private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
 
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index 748d66c..76c17154 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 /**
  * A {@link Parcelable} wrapper for a {@link ColorSpace}. In order to enable parceling, the
@@ -27,6 +28,7 @@
  * {@link ColorSpace.Rgb} instance that has an ICC parametric transfer function as returned by
  * {@link ColorSpace.Rgb#getTransferParameters()}.
  */
+@RavenwoodKeepWholeClass
 public final class ParcelableColorSpace implements Parcelable {
     private final ColorSpace mColorSpace;
 
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 3ec5b9c..a872e03 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -17,10 +17,12 @@
 package android.graphics;
 
 import android.annotation.IntDef;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
+@RavenwoodKeepWholeClass
 public class PixelFormat {
     /** @hide */
     @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index fd78816..889a7785 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -56,6 +56,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.text.flags.Flags;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
@@ -74,6 +75,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -143,6 +145,23 @@
     private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
     private static final Object sDynamicCacheLock = new Object();
 
+    private static final LruCache<Long, LruCache<String, Typeface>> sVariableCache =
+            new LruCache<>(16);
+    private static final Object sVariableCacheLock = new Object();
+
+    /** @hide */
+    @VisibleForTesting
+    public static void clearTypefaceCachesForTestingPurpose() {
+        synchronized (sWeightCacheLock) {
+            sWeightTypefaceCache.clear();
+        }
+        synchronized (sDynamicCacheLock) {
+            sDynamicTypefaceCache.evictAll();
+        }
+        synchronized (sVariableCacheLock) {
+            sVariableCache.evictAll();
+        }
+    }
 
     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
     static Typeface sDefaultTypeface;
@@ -195,6 +214,8 @@
     @UnsupportedAppUsage
     public final long native_instance;
 
+    private final Typeface mDerivedFrom;
+
     private final String mSystemFontFamilyName;
 
     private final Runnable mCleaner;
@@ -274,6 +295,18 @@
     }
 
     /**
+     * Returns the Typeface used for creating this Typeface.
+     *
+     * Maybe null if this is not derived from other Typeface.
+     * TODO(b/357707916): Make this public API.
+     * @hide
+     */
+    @VisibleForTesting
+    public final @Nullable Typeface getDerivedFrom() {
+        return mDerivedFrom;
+    }
+
+    /**
      * Returns the system font family name if the typeface was created from a system font family,
      * otherwise returns null.
      */
@@ -1021,9 +1054,51 @@
         return typeface;
     }
 
-    /** @hide */
+    private static String axesToVarKey(@NonNull List<FontVariationAxis> axes) {
+        // The given list can be mutated because it is allocated in Paint#setFontVariationSettings.
+        // Currently, Paint#setFontVariationSettings is the only code path reaches this method.
+        axes.sort(Comparator.comparingInt(FontVariationAxis::getOpenTypeTagValue));
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < axes.size(); ++i) {
+            final FontVariationAxis fva = axes.get(i);
+            sb.append(fva.getTag());
+            sb.append(fva.getStyleValue());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * TODO(b/357707916): Make this public API.
+     * @hide
+     */
     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
             @NonNull List<FontVariationAxis> axes) {
+        if (Flags.typefaceCacheForVarSettings()) {
+            final Typeface target = (family == null) ? Typeface.DEFAULT : family;
+            final Typeface base = (target.mDerivedFrom == null) ? target : target.mDerivedFrom;
+
+            final String key = axesToVarKey(axes);
+
+            synchronized (sVariableCacheLock) {
+                LruCache<String, Typeface> innerCache = sVariableCache.get(base.native_instance);
+                if (innerCache == null) {
+                    // Cache up to 16 var instance per root Typeface
+                    innerCache = new LruCache<>(16);
+                    sVariableCache.put(base.native_instance, innerCache);
+                } else {
+                    Typeface cached = innerCache.get(key);
+                    if (cached != null) {
+                        return cached;
+                    }
+                }
+                Typeface typeface = new Typeface(
+                        nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
+                        base.getSystemFontFamilyName(), base);
+                innerCache.put(key, typeface);
+                return typeface;
+            }
+        }
+
         final Typeface base = family == null ? Typeface.DEFAULT : family;
         Typeface typeface = new Typeface(
                 nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
@@ -1184,11 +1259,19 @@
     // don't allow clients to call this directly
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Typeface(long ni) {
-        this(ni, null);
+        this(ni, null, null);
+    }
+
+
+    // don't allow clients to call this directly
+    // This is kept for robolectric.
+    private Typeface(long ni, @Nullable String systemFontFamilyName) {
+        this(ni, systemFontFamilyName, null);
     }
 
     // don't allow clients to call this directly
-    private Typeface(long ni, @Nullable String systemFontFamilyName) {
+    private Typeface(long ni, @Nullable String systemFontFamilyName,
+            @Nullable Typeface derivedFrom) {
         if (ni == 0) {
             throw new RuntimeException("native typeface cannot be made");
         }
@@ -1198,6 +1281,7 @@
         mStyle = nativeGetStyle(ni);
         mWeight = nativeGetWeight(ni);
         mSystemFontFamilyName = systemFontFamilyName;
+        mDerivedFrom = derivedFrom;
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/TEST_MAPPING b/graphics/java/android/graphics/drawable/TEST_MAPPING
index 1018702..4f06452 100644
--- a/graphics/java/android/graphics/drawable/TEST_MAPPING
+++ b/graphics/java/android/graphics/drawable/TEST_MAPPING
@@ -12,13 +12,7 @@
     },
     {
 
-      "name": "FrameworksCoreTests",
-      "file_patterns": ["(/|^)Icon\\.java"],
-      "options" : [
-        {
-          "include-filter": "android.graphics.drawable.IconTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_drawable"
     }
   ]
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
new file mode 100644
index 0000000..4ce2942
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java
@@ -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 androidx.window.extensions.embedding;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume
+ * organizing the TaskFragments if the app process is restarted.
+ */
+@SuppressWarnings("GuardedBy")
+class BackupHelper {
+    private static final String TAG = "BackupHelper";
+    private static final boolean DEBUG = Build.isDebuggable();
+
+    private static final String KEY_TASK_CONTAINERS = "KEY_TASK_CONTAINERS";
+    @NonNull
+    private final SplitController mController;
+    @NonNull
+    private final BackupIdler mBackupIdler = new BackupIdler();
+    private boolean mBackupIdlerScheduled;
+
+    BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) {
+        mController = splitController;
+
+        if (!savedState.isEmpty()) {
+            restoreState(savedState);
+        }
+    }
+
+    /**
+     * Schedules a back-up request. It is no-op if there was a request scheduled and not yet
+     * completed.
+     */
+    void scheduleBackup() {
+        if (!mBackupIdlerScheduled) {
+            mBackupIdlerScheduled = true;
+            Looper.getMainLooper().getQueue().addIdleHandler(mBackupIdler);
+        }
+    }
+
+    final class BackupIdler implements MessageQueue.IdleHandler {
+        @Override
+        public boolean queueIdle() {
+            synchronized (mController.mLock) {
+                mBackupIdlerScheduled = false;
+                startBackup();
+            }
+            return false;
+        }
+    }
+
+    private void startBackup() {
+        final List<TaskContainer> taskContainers = mController.getTaskContainers();
+        if (taskContainers.isEmpty()) {
+            Log.w(TAG, "No task-container to back up");
+            return;
+        }
+
+        if (DEBUG) Log.d(TAG, "Start to back up " + taskContainers);
+        final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = new ArrayList<>(
+                taskContainers.size());
+        for (TaskContainer taskContainer : taskContainers) {
+            parcelableTaskContainerDataList.add(taskContainer.getParcelableData());
+        }
+        final Bundle state = new Bundle();
+        state.setClassLoader(ParcelableTaskContainerData.class.getClassLoader());
+        state.putParcelableList(KEY_TASK_CONTAINERS, parcelableTaskContainerDataList);
+        mController.setSavedState(state);
+    }
+
+    private void restoreState(@NonNull Bundle savedState) {
+        if (savedState.isEmpty()) {
+            return;
+        }
+
+        final List<ParcelableTaskContainerData> parcelableTaskContainerDataList =
+                savedState.getParcelableArrayList(KEY_TASK_CONTAINERS,
+                        ParcelableTaskContainerData.class);
+        for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) {
+            final TaskContainer taskContainer = new TaskContainer(data, mController);
+            if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId());
+            // TODO(b/289875940): implement the TaskContainer restoration.
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 1eb95c1..9ea2943 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -70,6 +70,10 @@
     @NonNull
     private final TaskFragmentCallback mCallback;
 
+    @VisibleForTesting
+    @Nullable
+    TaskFragmentAnimationController mAnimationController;
+
     /**
      * Callback that notifies the controller about changes to task fragments.
      */
@@ -87,6 +91,25 @@
         mCallback = callback;
     }
 
+    @Override
+    public void unregisterOrganizer() {
+        if (mAnimationController != null) {
+            mAnimationController.unregisterRemoteAnimations();
+            mAnimationController = null;
+        }
+        super.unregisterOrganizer();
+    }
+
+    /**
+     * Overrides the animation for transitions of embedded activities organized by this organizer.
+     */
+    void overrideSplitAnimation() {
+        if (mAnimationController == null) {
+            mAnimationController = new TaskFragmentAnimationController(this);
+        }
+        mAnimationController.registerRemoteAnimations();
+    }
+
     /**
      * Starts a new Activity and puts it into split with an existing Activity side-by-side.
      * @param launchingFragmentToken    token for the launching TaskFragment. If it exists, it will
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
new file mode 100644
index 0000000..817cfce
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java
@@ -0,0 +1,116 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * This class holds the Parcelable data of a {@link SplitContainer}.
+ */
+class ParcelableSplitContainerData implements Parcelable {
+
+    /**
+     * A reference to the target {@link SplitContainer} that owns the data. This will not be
+     * parcelled and will be {@code null} when the data is created from a parcel.
+     */
+    @Nullable
+    final SplitContainer mSplitContainer;
+
+    @NonNull
+    final IBinder mToken;
+
+    @NonNull
+    private final IBinder mPrimaryContainerToken;
+
+    @NonNull
+    private final IBinder mSecondaryContainerToken;
+
+    // TODO(b/289875940): making this as non-null once the tag can be auto-generated from the rule.
+    @Nullable
+    final String mSplitRuleTag;
+
+    /**
+     * Whether the selection of which container is primary can be changed at runtime. Runtime
+     * updates is currently possible only for {@link SplitPinContainer}
+     *
+     * @see SplitPinContainer
+     */
+    final boolean mIsPrimaryContainerMutable;
+
+    ParcelableSplitContainerData(@NonNull SplitContainer splitContainer, @NonNull IBinder token,
+            @NonNull IBinder primaryContainerToken, @NonNull IBinder secondaryContainerToken,
+            @Nullable String splitRuleTag, boolean isPrimaryContainerMutable) {
+        mSplitContainer = splitContainer;
+        mToken = token;
+        mPrimaryContainerToken = primaryContainerToken;
+        mSecondaryContainerToken = secondaryContainerToken;
+        mSplitRuleTag = splitRuleTag;
+        mIsPrimaryContainerMutable = isPrimaryContainerMutable;
+    }
+
+    private ParcelableSplitContainerData(Parcel in) {
+        mSplitContainer = null;
+        mToken = in.readStrongBinder();
+        mPrimaryContainerToken = in.readStrongBinder();
+        mSecondaryContainerToken = in.readStrongBinder();
+        mSplitRuleTag = in.readString();
+        mIsPrimaryContainerMutable = in.readBoolean();
+    }
+
+    public static final Creator<ParcelableSplitContainerData> CREATOR = new Creator<>() {
+        @Override
+        public ParcelableSplitContainerData createFromParcel(Parcel in) {
+            return new ParcelableSplitContainerData(in);
+        }
+
+        @Override
+        public ParcelableSplitContainerData[] newArray(int size) {
+            return new ParcelableSplitContainerData[size];
+        }
+    };
+
+    @NonNull
+    private IBinder getPrimaryContainerToken() {
+        return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken()
+                : mPrimaryContainerToken;
+    }
+
+    @NonNull
+    private IBinder getSecondaryContainerToken() {
+        return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken()
+                : mSecondaryContainerToken;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mToken);
+        dest.writeStrongBinder(getPrimaryContainerToken());
+        dest.writeStrongBinder(getSecondaryContainerToken());
+        dest.writeString(mSplitRuleTag);
+        dest.writeBoolean(mIsPrimaryContainerMutable);
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
new file mode 100644
index 0000000..7377d00
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java
@@ -0,0 +1,127 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class holds the Parcelable data of a {@link TaskContainer}.
+ */
+class ParcelableTaskContainerData implements Parcelable {
+
+    /**
+     * A reference to the target {@link TaskContainer} that owns the data. This will not be
+     * parcelled and will be {@code null} when the data is created from a parcel.
+     */
+    @Nullable
+    final TaskContainer mTaskContainer;
+
+    /**
+     * The unique task id.
+     */
+    final int mTaskId;
+
+    /**
+     * The parcelable data of the active TaskFragmentContainers in this Task.
+     * Note that this will only be populated before parcelling, and will not be copied when
+     * making a new instance copy.
+     */
+    @NonNull
+    private final List<ParcelableTaskFragmentContainerData>
+            mParcelableTaskFragmentContainerDataList = new ArrayList<>();
+
+    /**
+     * The parcelable data of the SplitContainers in this Task.
+     * Note that this will only be populated before parcelling, and will not be copied when
+     * making a new instance copy.
+     */
+    @NonNull
+    private final List<ParcelableSplitContainerData> mParcelableSplitContainerDataList =
+            new ArrayList<>();
+
+    ParcelableTaskContainerData(int taskId, @NonNull TaskContainer taskContainer) {
+        if (taskId == INVALID_TASK_ID) {
+            throw new IllegalArgumentException("Invalid Task id");
+        }
+
+        mTaskId = taskId;
+        mTaskContainer = taskContainer;
+    }
+
+    ParcelableTaskContainerData(@NonNull ParcelableTaskContainerData data,
+            @NonNull TaskContainer taskContainer) {
+        mTaskId = data.mTaskId;
+        mTaskContainer = taskContainer;
+    }
+
+    private ParcelableTaskContainerData(Parcel in) {
+        mTaskId = in.readInt();
+        mTaskContainer = null;
+        in.readParcelableList(mParcelableTaskFragmentContainerDataList,
+                ParcelableTaskFragmentContainerData.class.getClassLoader(),
+                ParcelableTaskFragmentContainerData.class);
+        in.readParcelableList(mParcelableSplitContainerDataList,
+                ParcelableSplitContainerData.class.getClassLoader(),
+                ParcelableSplitContainerData.class);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mTaskId);
+        dest.writeParcelableList(getParcelableTaskFragmentContainerDataList(), flags);
+        dest.writeParcelableList(getParcelableSplitContainerDataList(), flags);
+    }
+
+    @NonNull
+    List<? extends ParcelableTaskFragmentContainerData>
+            getParcelableTaskFragmentContainerDataList() {
+        return mTaskContainer != null ? mTaskContainer.getParcelableTaskFragmentContainerDataList()
+                : mParcelableTaskFragmentContainerDataList;
+    }
+
+    @NonNull
+    List<? extends ParcelableSplitContainerData> getParcelableSplitContainerDataList() {
+        return mTaskContainer != null ? mTaskContainer.getParcelableSplitContainerDataList()
+                : mParcelableSplitContainerDataList;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ParcelableTaskContainerData> CREATOR = new Creator<>() {
+        @Override
+        public ParcelableTaskContainerData createFromParcel(Parcel in) {
+            return new ParcelableTaskContainerData(in);
+        }
+
+        @Override
+        public ParcelableTaskContainerData[] newArray(int size) {
+            return new ParcelableTaskContainerData[size];
+        }
+    };
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
new file mode 100644
index 0000000..a79a89a
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskFragmentContainerData.java
@@ -0,0 +1,105 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * This class holds the Parcelable data of a {@link TaskFragmentContainer}.
+ */
+class ParcelableTaskFragmentContainerData implements Parcelable {
+
+    /**
+     * Client-created token that uniquely identifies the task fragment container instance.
+     */
+    @NonNull
+    final IBinder mToken;
+
+    /**
+     * The tag specified in launch options. {@code null} if this taskFragment container is not an
+     * overlay container.
+     */
+    @Nullable
+    final String mOverlayTag;
+
+    /**
+     * The associated {@link Activity#getActivityToken()} of the overlay container.
+     * Must be {@code null} for non-overlay container.
+     * <p>
+     * If an overlay container is associated with an activity, this overlay container will be
+     * dismissed when the associated activity is destroyed. If the overlay container is visible,
+     * activity will be launched on top of the overlay container and expanded to fill the parent
+     * container.
+     */
+    @Nullable
+    final IBinder mAssociatedActivityToken;
+
+    /**
+     * Bounds that were requested last via {@link android.window.WindowContainerTransaction}.
+     */
+    @NonNull
+    final Rect mLastRequestedBounds;
+
+    ParcelableTaskFragmentContainerData(@NonNull IBinder token, @Nullable String overlayTag,
+            @Nullable IBinder associatedActivityToken) {
+        mToken = token;
+        mOverlayTag = overlayTag;
+        mAssociatedActivityToken = associatedActivityToken;
+        mLastRequestedBounds = new Rect();
+    }
+
+    private ParcelableTaskFragmentContainerData(Parcel in) {
+        mToken = in.readStrongBinder();
+        mOverlayTag = in.readString();
+        mAssociatedActivityToken = in.readStrongBinder();
+        mLastRequestedBounds = in.readTypedObject(Rect.CREATOR);
+    }
+
+    public static final Creator<ParcelableTaskFragmentContainerData> CREATOR = new Creator<>() {
+        @Override
+        public ParcelableTaskFragmentContainerData createFromParcel(Parcel in) {
+            return new ParcelableTaskFragmentContainerData(in);
+        }
+
+        @Override
+        public ParcelableTaskFragmentContainerData[] newArray(int size) {
+            return new ParcelableTaskFragmentContainerData[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mToken);
+        dest.writeString(mOverlayTag);
+        dest.writeStrongBinder(mAssociatedActivityToken);
+        dest.writeTypedObject(mLastRequestedBounds, flags);
+    }
+
+}
+
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 39cface..6d436ec 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -33,6 +33,8 @@
  */
 class SplitContainer {
     @NonNull
+    private final ParcelableSplitContainerData mParcelableData;
+    @NonNull
     private TaskFragmentContainer mPrimaryContainer;
     @NonNull
     private final TaskFragmentContainer mSecondaryContainer;
@@ -44,16 +46,6 @@
     /** @see SplitContainer#getDefaultSplitAttributes() */
     @NonNull
     private SplitAttributes mDefaultSplitAttributes;
-    @NonNull
-    private final IBinder mToken;
-
-    /**
-     * Whether the selection of which container is primary can be changed at runtime. Runtime
-     * updates is currently possible only for {@link SplitPinContainer}
-     *
-     * @see SplitPinContainer
-     */
-    private final boolean mIsPrimaryContainerMutable;
 
     SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
             @NonNull Activity primaryActivity,
@@ -69,13 +61,14 @@
             @NonNull TaskFragmentContainer secondaryContainer,
             @NonNull SplitRule splitRule,
             @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) {
+        mParcelableData = new ParcelableSplitContainerData(this, new Binder("SplitContainer"),
+                primaryContainer.getToken(), secondaryContainer.getToken(), splitRule.getTag(),
+                isPrimaryContainerMutable);
         mPrimaryContainer = primaryContainer;
         mSecondaryContainer = secondaryContainer;
         mSplitRule = splitRule;
         mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes();
         mCurrentSplitAttributes = splitAttributes;
-        mToken = new Binder("SplitContainer");
-        mIsPrimaryContainerMutable = isPrimaryContainerMutable;
 
         if (shouldFinishPrimaryWithSecondary(splitRule)) {
             if (mPrimaryContainer.getRunningActivityCount() == 1
@@ -94,7 +87,7 @@
     }
 
     void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) {
-        if (!mIsPrimaryContainerMutable) {
+        if (!mParcelableData.mIsPrimaryContainerMutable) {
             throw new IllegalStateException("Cannot update primary TaskFragmentContainer");
         }
         mPrimaryContainer = primaryContainer;
@@ -150,7 +143,12 @@
 
     @NonNull
     IBinder getToken() {
-        return mToken;
+        return mParcelableData.mToken;
+    }
+
+    @NonNull
+    ParcelableSplitContainerData getParcelableData() {
+        return mParcelableData;
     }
 
     /**
@@ -201,7 +199,7 @@
             return null;
         }
         return new SplitInfo(primaryActivityStack, secondaryActivityStack,
-                mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken));
+                mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mParcelableData.mToken));
     }
 
     static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
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 25e7107..f2f2b7ea 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -58,18 +58,22 @@
 import android.app.ActivityClient;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
+import android.app.AppGlobals;
 import android.app.Application;
 import android.app.Instrumentation;
 import android.app.servertransaction.ClientTransactionListenerController;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -116,6 +120,7 @@
 public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
         ActivityEmbeddingComponent, DividerPresenter.DragEventCallback {
     static final String TAG = "SplitController";
+    static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
 
     // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without
     //  association. It's not set in WM Extensions nor Wm Jetpack library currently.
@@ -695,12 +700,8 @@
                         break;
                     case TYPE_ACTIVITY_REPARENTED_TO_TASK:
                         final IBinder candidateAssociatedActToken, lastOverlayToken;
-                        if (Flags.fixPipRestoreToOverlay()) {
-                            candidateAssociatedActToken = change.getOtherActivityToken();
-                            lastOverlayToken = change.getTaskFragmentToken();
-                        } else {
-                            candidateAssociatedActToken = lastOverlayToken = null;
-                        }
+                        candidateAssociatedActToken = change.getOtherActivityToken();
+                        lastOverlayToken = change.getTaskFragmentToken();
                         onActivityReparentedToTask(
                                 wct,
                                 taskId,
@@ -910,7 +911,7 @@
 
         if (taskContainer.isVisible()) {
             updateContainersInTask(wct, taskContainer);
-        } else if (Flags.fixNoContainerUpdateWithoutResize()) {
+        } else {
             // the TaskFragmentContainers need to be updated when the task becomes visible
             taskContainer.mTaskFragmentContainersNeedsUpdate = true;
         }
@@ -923,7 +924,8 @@
 
         // Update all TaskFragments in the Task. Make a copy of the list since some may be
         // removed on updating.
-        final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
+        final List<TaskFragmentContainer> containers
+                = new ArrayList<>(taskContainer.getTaskFragmentContainers());
         for (int i = containers.size() - 1; i >= 0; i--) {
             final TaskFragmentContainer container = containers.get(i);
             // Wait until onTaskFragmentAppeared to update new container.
@@ -1023,10 +1025,6 @@
     @Nullable
     OverlayContainerRestoreParams getOverlayContainerRestoreParams(
             @Nullable IBinder associatedActivityToken, @Nullable IBinder overlayToken) {
-        if (!Flags.fixPipRestoreToOverlay()) {
-            return null;
-        }
-
         if (associatedActivityToken == null || overlayToken == null) {
             return null;
         }
@@ -2544,6 +2542,21 @@
         return mTaskContainers.get(taskId);
     }
 
+    @NonNull
+    @GuardedBy("mLock")
+    List<TaskContainer> getTaskContainers() {
+        final ArrayList<TaskContainer> taskContainers = new ArrayList<>();
+        for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+            taskContainers.add(mTaskContainers.valueAt(i));
+        }
+        return taskContainers;
+    }
+
+    @GuardedBy("mLock")
+    void setSavedState(@NonNull Bundle savedState) {
+        mPresenter.setSavedState(savedState);
+    }
+
     @GuardedBy("mLock")
     void addTaskContainer(int taskId, TaskContainer taskContainer) {
         mTaskContainers.put(taskId, taskContainer);
@@ -2837,6 +2850,12 @@
         return getActiveSplitForContainer(container) != null;
     }
 
+    void scheduleBackup() {
+        synchronized (mLock) {
+            mPresenter.scheduleBackup();
+        }
+    }
+
     private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter {
 
         @Override
@@ -3294,4 +3313,17 @@
             transactionRecord.apply(false /* shouldApplyIndependently */);
         }
     }
+
+    // TODO(b/207070762): cleanup with legacy app transition
+    private static boolean getShellTransitEnabled() {
+        try {
+            if (AppGlobals.getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_AUTOMOTIVE, 0)) {
+                return SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
+            }
+        } catch (RemoteException re) {
+            Log.w(TAG, "Error getting system features");
+        }
+        return true;
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 99716e7..abc7b29 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -158,6 +158,8 @@
 
     private final WindowLayoutComponentImpl mWindowLayoutComponent;
     private final SplitController mController;
+    @NonNull
+    private final BackupHelper mBackupHelper;
 
     SplitPresenter(@NonNull Executor executor,
             @NonNull WindowLayoutComponentImpl windowLayoutComponent,
@@ -165,7 +167,23 @@
         super(executor, controller);
         mWindowLayoutComponent = windowLayoutComponent;
         mController = controller;
-        registerOrganizer();
+        final Bundle outSavedState = new Bundle();
+        if (Flags.aeBackStackRestore()) {
+            outSavedState.setClassLoader(ParcelableTaskContainerData.class.getClassLoader());
+            registerOrganizer(false /* isSystemOrganizer */, outSavedState);
+        } else {
+            registerOrganizer();
+        }
+        mBackupHelper = new BackupHelper(controller, outSavedState);
+        if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
+            // TODO(b/207070762): cleanup with legacy app transition
+            // Animation will be handled by WM Shell when Shell transition is enabled.
+            overrideSplitAnimation();
+        }
+    }
+
+    void scheduleBackup() {
+        mBackupHelper.scheduleBackup();
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 20ad53e..608a3be 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -16,7 +16,6 @@
 
 package androidx.window.extensions.embedding;
 
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 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;
@@ -48,6 +47,8 @@
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
 import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
 
+import com.android.window.flags.Flags;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -56,8 +57,9 @@
 class TaskContainer {
     private static final String TAG = TaskContainer.class.getSimpleName();
 
-    /** The unique task id. */
-    private final int mTaskId;
+    /** Parcelable data of this TaskContainer. */
+    @NonNull
+    private final ParcelableTaskContainerData mParcelableTaskContainerData;
 
     /** Active TaskFragments in this Task. */
     @NonNull
@@ -80,6 +82,9 @@
     @NonNull
     private TaskFragmentParentInfo mInfo;
 
+    @NonNull
+    private SplitController mSplitController;
+
     /**
      * TaskFragments that the organizer has requested to be closed. They should be removed when
      * the organizer receives
@@ -116,30 +121,68 @@
     /**
      * The {@link TaskContainer} constructor
      *
-     * @param taskId         The ID of the Task, which must match {@link Activity#getTaskId()} with
-     *                       {@code activityInTask}.
-     * @param activityInTask The {@link Activity} in the Task with {@code taskId}. It is used to
-     *                       initialize the {@link TaskContainer} properties.
+     * @param taskId          The ID of the Task, which must match {@link Activity#getTaskId()} with
+     *                        {@code activityInTask}.
+     * @param activityInTask  The {@link Activity} in the Task with {@code taskId}. It is used to
+     *                        initialize the {@link TaskContainer} properties.
+     * @param splitController The {@link SplitController}.
      */
-    TaskContainer(int taskId, @NonNull Activity activityInTask) {
-        if (taskId == INVALID_TASK_ID) {
-            throw new IllegalArgumentException("Invalid Task id");
-        }
-        mTaskId = taskId;
+    TaskContainer(int taskId, @NonNull Activity activityInTask,
+            @NonNull SplitController splitController) {
+        mParcelableTaskContainerData = new ParcelableTaskContainerData(taskId, this);
+
         final TaskProperties taskProperties = TaskProperties
                 .getTaskPropertiesFromActivity(activityInTask);
         mInfo = new TaskFragmentParentInfo(
                 taskProperties.getConfiguration(),
                 taskProperties.getDisplayId(),
+                taskId,
                 // Note that it is always called when there's a new Activity is started, which
                 // implies the host task is visible and has an activity in the task.
                 true /* visible */,
                 true /* hasDirectActivity */,
                 null /* decorSurface */);
+        mSplitController = splitController;
+    }
+
+    /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */
+    TaskContainer(@NonNull ParcelableTaskContainerData data,
+            @NonNull SplitController splitController) {
+        mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this);
+        mSplitController = splitController;
+        for (ParcelableTaskFragmentContainerData tfData :
+                data.getParcelableTaskFragmentContainerDataList()) {
+            final TaskFragmentContainer container =
+                    new TaskFragmentContainer(tfData, splitController, this);
+            mContainers.add(container);
+        }
+    }
+
+    @NonNull
+    ParcelableTaskContainerData getParcelableData() {
+        return mParcelableTaskContainerData;
+    }
+
+    @NonNull
+    List<ParcelableTaskFragmentContainerData> getParcelableTaskFragmentContainerDataList() {
+        final List<ParcelableTaskFragmentContainerData> data = new ArrayList<>(mContainers.size());
+        for (TaskFragmentContainer container : mContainers) {
+            data.add(container.getParcelableData());
+        }
+        return data;
+    }
+
+    @NonNull
+    List<ParcelableSplitContainerData> getParcelableSplitContainerDataList() {
+        final List<ParcelableSplitContainerData> data = new ArrayList<>(mSplitContainers.size());
+        for (SplitContainer splitContainer : mSplitContainers) {
+            data.add(splitContainer.getParcelableData());
+        }
+        return data;
     }
 
     int getTaskId() {
-        return mTaskId;
+        return mParcelableTaskContainerData.mTaskId;
     }
 
     int getDisplayId() {
@@ -152,7 +195,8 @@
 
     void setInvisible() {
         mInfo = new TaskFragmentParentInfo(mInfo.getConfiguration(), mInfo.getDisplayId(),
-                false /* visible */, mInfo.hasDirectActivity(), mInfo.getDecorSurface());
+                mInfo.getTaskId(), false /* visible */, mInfo.hasDirectActivity(),
+                mInfo.getDecorSurface());
     }
 
     boolean hasDirectActivity() {
@@ -571,6 +615,12 @@
         // Update overlay container after split pin container since the overlay should be on top of
         // pin container.
         updateAlwaysOnTopOverlayIfNecessary();
+
+        // TODO(b/289875940): Making backup-restore as an opt-in solution, before the flag goes
+        //  to next-food.
+        if (Flags.aeBackStackRestore()) {
+            mSplitController.scheduleBackup();
+        }
     }
 
     private void updateAlwaysOnTopOverlayIfNecessary() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
new file mode 100644
index 0000000..33220c4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -0,0 +1,224 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
+ *
+ * The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
+ */
+class TaskFragmentAnimationAdapter {
+
+    /**
+     * If {@link #mOverrideLayer} is set to this value, we don't want to override the surface layer.
+     */
+    private static final int LAYER_NO_OVERRIDE = -1;
+
+    @NonNull
+    final Animation mAnimation;
+    @NonNull
+    final RemoteAnimationTarget mTarget;
+    @NonNull
+    final SurfaceControl mLeash;
+    /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+    @NonNull
+    private final Rect mWholeAnimationBounds = new Rect();
+    /**
+     * Area in absolute coordinate that should represent all the content to show for this window.
+     * This should be the end bounds for opening window, and start bounds for closing window in case
+     * the window is resizing during the open/close transition.
+     */
+    @NonNull
+    private final Rect mContentBounds = new Rect();
+    /** Offset relative to the window parent surface for {@link #mContentBounds}. */
+    @NonNull
+    private final Point mContentRelOffset = new Point();
+
+    @NonNull
+    final Transformation mTransformation = new Transformation();
+    @NonNull
+    final float[] mMatrix = new float[9];
+    @NonNull
+    final float[] mVecs = new float[4];
+    @NonNull
+    final Rect mRect = new Rect();
+    private boolean mIsFirstFrame = true;
+    private int mOverrideLayer = LAYER_NO_OVERRIDE;
+
+    TaskFragmentAnimationAdapter(@NonNull Animation animation,
+            @NonNull RemoteAnimationTarget target) {
+        this(animation, target, target.leash, target.screenSpaceBounds);
+    }
+
+    /**
+     * @param leash the surface to animate.
+     * @param wholeAnimationBounds  area in absolute coordinate that the animation surface shouldn't
+     *                              go beyond.
+     */
+    TaskFragmentAnimationAdapter(@NonNull Animation animation,
+            @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+            @NonNull Rect wholeAnimationBounds) {
+        mAnimation = animation;
+        mTarget = target;
+        mLeash = leash;
+        mWholeAnimationBounds.set(wholeAnimationBounds);
+        if (target.mode == MODE_CLOSING) {
+            // When it is closing, we want to show the content at the start position in case the
+            // window is resizing as well. For example, when the activities is changing from split
+            // to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
+            final Rect startBounds = target.startBounds;
+            final Rect endBounds = target.screenSpaceBounds;
+            mContentBounds.set(startBounds);
+            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+            mContentRelOffset.offset(
+                    startBounds.left - endBounds.left,
+                    startBounds.top - endBounds.top);
+        } else {
+            mContentBounds.set(target.screenSpaceBounds);
+            mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
+        }
+    }
+
+    /**
+     * Surface layer to be set at the first frame of the animation. We will not set the layer if it
+     * is set to {@link #LAYER_NO_OVERRIDE}.
+     */
+    final void overrideLayer(int layer) {
+        mOverrideLayer = layer;
+    }
+
+    /** Called on frame update. */
+    final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
+        if (mIsFirstFrame) {
+            t.show(mLeash);
+            if (mOverrideLayer != LAYER_NO_OVERRIDE) {
+                t.setLayer(mLeash, mOverrideLayer);
+            }
+            mIsFirstFrame = false;
+        }
+
+        // Extract the transformation to the current time.
+        mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
+                mTransformation);
+        t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
+        onAnimationUpdateInner(t);
+    }
+
+    /** To be overridden by subclasses to adjust the animation surface change. */
+    void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+        // Update the surface position and alpha.
+        mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
+        t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+        t.setAlpha(mLeash, mTransformation.getAlpha());
+
+        // Get current surface bounds in absolute coordinate.
+        // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
+        final int positionX = Math.round(mMatrix[MTRANS_X]);
+        final int positionY = Math.round(mMatrix[MTRANS_Y]);
+        final Rect cropRect = new Rect(mContentBounds);
+        cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
+
+        // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+        final int offsetX = cropRect.left;
+        final int offsetY = cropRect.top;
+
+        // Intersect to make sure the animation happens within the whole animation bounds.
+        if (!cropRect.intersect(mWholeAnimationBounds)) {
+            // Hide the surface when it is outside of the animation area.
+            t.setAlpha(mLeash, 0);
+        }
+
+        // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+        cropRect.offset(-offsetX, -offsetY);
+        t.setCrop(mLeash, cropRect);
+    }
+
+    /** Called after animation finished. */
+    final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
+        onAnimationUpdate(t, mAnimation.getDuration());
+    }
+
+    final long getDurationHint() {
+        return mAnimation.computeDurationHint();
+    }
+
+    /**
+     * Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
+     * size change.
+     */
+    static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
+
+        SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+            // Start leash is the snapshot of the starting surface.
+            super(animation, target, target.startLeash, target.screenSpaceBounds);
+        }
+
+        @Override
+        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+            // Snapshot should always be placed at the top left of the animation leash.
+            mTransformation.getMatrix().postTranslate(0, 0);
+            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+            t.setAlpha(mLeash, mTransformation.getAlpha());
+        }
+    }
+
+    /**
+     * Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
+     */
+    static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
+
+        BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
+            super(animation, target);
+        }
+
+        @Override
+        void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+            mTransformation.getMatrix().postTranslate(
+                    mTarget.localBounds.left, mTarget.localBounds.top);
+            t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
+            t.setAlpha(mLeash, mTransformation.getAlpha());
+
+            // The following applies an inverse scale to the clip-rect so that it crops "after" the
+            // scale instead of before.
+            mVecs[1] = mVecs[2] = 0;
+            mVecs[0] = mVecs[3] = 1;
+            mTransformation.getMatrix().mapVectors(mVecs);
+            mVecs[0] = 1.f / mVecs[0];
+            mVecs[3] = 1.f / mVecs[3];
+            final Rect clipRect = mTransformation.getClipRect();
+            mRect.left = (int) (clipRect.left * mVecs[0] + 0.5f);
+            mRect.right = (int) (clipRect.right * mVecs[0] + 0.5f);
+            mRect.top = (int) (clipRect.top * mVecs[3] + 0.5f);
+            mRect.bottom = (int) (clipRect.bottom * mVecs[3] + 0.5f);
+            t.setWindowCrop(mLeash, mRect);
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
new file mode 100644
index 0000000..d7eb9a0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -0,0 +1,79 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+
+import android.util.Log;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Controls the TaskFragment remote animations. */
+class TaskFragmentAnimationController {
+
+    private static final String TAG = "TaskFragAnimationCtrl";
+    static final boolean DEBUG = false;
+
+    private final TaskFragmentOrganizer mOrganizer;
+    private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
+    @VisibleForTesting
+    final RemoteAnimationDefinition mDefinition;
+    private boolean mIsRegistered;
+
+    TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) {
+        mOrganizer = organizer;
+        mDefinition = new RemoteAnimationDefinition();
+        final RemoteAnimationAdapter animationAdapter =
+                new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
+        mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
+    }
+
+    void registerRemoteAnimations() {
+        if (DEBUG) {
+            Log.v(TAG, "registerRemoteAnimations");
+        }
+        if (mIsRegistered) {
+            return;
+        }
+        mOrganizer.registerRemoteAnimations(mDefinition);
+        mIsRegistered = true;
+    }
+
+    void unregisterRemoteAnimations() {
+        if (DEBUG) {
+            Log.v(TAG, "unregisterRemoteAnimations");
+        }
+        if (!mIsRegistered) {
+            return;
+        }
+        mOrganizer.unregisterRemoteAnimations();
+        mIsRegistered = false;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
new file mode 100644
index 0000000..d9b73a8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -0,0 +1,309 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_CHANGING;
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+
+/** To run the TaskFragment animations. */
+class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+    private static final String TAG = "TaskFragAnimationRunner";
+    private final Handler mHandler;
+    private final TaskFragmentAnimationSpec mAnimationSpec;
+
+    TaskFragmentAnimationRunner() {
+        HandlerThread animationThread = new HandlerThread(
+                "androidx.window.extensions.embedding", THREAD_PRIORITY_DISPLAY);
+        animationThread.start();
+        mHandler = animationThread.getThreadHandler();
+        mAnimationSpec = new TaskFragmentAnimationSpec(mHandler);
+    }
+
+    @Nullable
+    private Animator mAnimator;
+
+    @Override
+    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] apps,
+            @NonNull RemoteAnimationTarget[] wallpapers,
+            @NonNull RemoteAnimationTarget[] nonApps,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        if (wallpapers.length != 0 || nonApps.length != 0) {
+            throw new IllegalArgumentException("TaskFragment shouldn't handle animation with"
+                    + "wallpaper or non-app windows.");
+        }
+        if (TaskFragmentAnimationController.DEBUG) {
+            Log.v(TAG, "onAnimationStart transit=" + transit);
+        }
+        mHandler.post(() -> startAnimation(transit, apps, finishedCallback));
+    }
+
+    @Override
+    public void onAnimationCancelled() {
+        mHandler.post(this::cancelAnimation);
+    }
+
+    /** Creates and starts animation. */
+    private void startAnimation(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        if (mAnimator != null) {
+            Log.w(TAG, "start new animation when the previous one is not finished yet.");
+            mAnimator.cancel();
+        }
+        mAnimator = createAnimator(transit, targets, finishedCallback);
+        mAnimator.start();
+    }
+
+    /** Cancels animation. */
+    private void cancelAnimation() {
+        if (mAnimator == null) {
+            return;
+        }
+        mAnimator.cancel();
+        mAnimator = null;
+    }
+
+    /** Creates the animator given the transition type and windows. */
+    @NonNull
+    private Animator createAnimator(@WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets,
+            @NonNull IRemoteAnimationFinishedCallback finishedCallback) {
+        final List<TaskFragmentAnimationAdapter> adapters =
+                createAnimationAdapters(transit, targets);
+        long duration = 0;
+        for (TaskFragmentAnimationAdapter adapter : adapters) {
+            duration = Math.max(duration, adapter.getDurationHint());
+        }
+        final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+        animator.setDuration(duration);
+        animator.addUpdateListener((anim) -> {
+            // Update all adapters in the same transaction.
+            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            for (TaskFragmentAnimationAdapter adapter : adapters) {
+                adapter.onAnimationUpdate(t, animator.getCurrentPlayTime());
+            }
+            t.apply();
+        });
+        animator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationStart(Animator animation) {}
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                for (TaskFragmentAnimationAdapter adapter : adapters) {
+                    adapter.onAnimationEnd(t);
+                }
+                t.apply();
+
+                try {
+                    finishedCallback.onAnimationFinished();
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+                mAnimator = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {}
+
+            @Override
+            public void onAnimationRepeat(Animator animation) {}
+        });
+        return animator;
+    }
+
+    /** List of {@link TaskFragmentAnimationAdapter} to handle animations on all window targets. */
+    @NonNull
+    private List<TaskFragmentAnimationAdapter> createAnimationAdapters(
+            @WindowManager.TransitionOldType int transit,
+            @NonNull RemoteAnimationTarget[] targets) {
+        switch (transit) {
+            case TRANSIT_OLD_ACTIVITY_OPEN:
+            case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+                return createOpenAnimationAdapters(targets);
+            case TRANSIT_OLD_ACTIVITY_CLOSE:
+            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+                return createCloseAnimationAdapters(targets);
+            case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
+                return createChangeAnimationAdapters(targets);
+            default:
+                throw new IllegalArgumentException("Unhandled transit type=" + transit);
+        }
+    }
+
+    @NonNull
+    private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        return createOpenCloseAnimationAdapters(targets, true /* isOpening */,
+                mAnimationSpec::loadOpenAnimation);
+    }
+
+    @NonNull
+    private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        return createOpenCloseAnimationAdapters(targets, false /* isOpening */,
+                mAnimationSpec::loadCloseAnimation);
+    }
+
+    /**
+     * Creates {@link TaskFragmentAnimationAdapter} for OPEN and CLOSE types of transition.
+     * @param isOpening {@code true} for OPEN type, {@code false} for CLOSE type.
+     */
+    @NonNull
+    private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets, boolean isOpening,
+            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
+        // We need to know if the target window is only a partial of the whole animation screen.
+        // If so, we will need to adjust it to make the whole animation screen looks like one.
+        final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
+        final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
+        final Rect openingWholeScreenBounds = new Rect();
+        final Rect closingWholeScreenBounds = new Rect();
+        for (RemoteAnimationTarget target : targets) {
+            if (target.mode != MODE_CLOSING) {
+                openingTargets.add(target);
+                openingWholeScreenBounds.union(target.screenSpaceBounds);
+            } else {
+                closingTargets.add(target);
+                closingWholeScreenBounds.union(target.screenSpaceBounds);
+                // Union the start bounds since this may be the ClosingChanging animation.
+                closingWholeScreenBounds.union(target.startBounds);
+            }
+        }
+
+        // For OPEN transition, open windows should be above close windows.
+        // For CLOSE transition, open windows should be below close windows.
+        int offsetLayer = TYPE_LAYER_OFFSET;
+        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+        for (RemoteAnimationTarget target : openingTargets) {
+            final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+                    animationProvider, openingWholeScreenBounds);
+            if (isOpening) {
+                adapter.overrideLayer(offsetLayer++);
+            }
+            adapters.add(adapter);
+        }
+        for (RemoteAnimationTarget target : closingTargets) {
+            final TaskFragmentAnimationAdapter adapter = createOpenCloseAnimationAdapter(target,
+                    animationProvider, closingWholeScreenBounds);
+            if (!isOpening) {
+                adapter.overrideLayer(offsetLayer++);
+            }
+            adapters.add(adapter);
+        }
+        return adapters;
+    }
+
+    @NonNull
+    private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
+            @NonNull RemoteAnimationTarget target,
+            @NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
+            @NonNull Rect wholeAnimationBounds) {
+        final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
+        return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+                wholeAnimationBounds);
+    }
+
+    @NonNull
+    private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
+            @NonNull RemoteAnimationTarget[] targets) {
+        if (shouldUseJumpCutForChangeAnimation(targets)) {
+            return new ArrayList<>();
+        }
+
+        final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
+        for (RemoteAnimationTarget target : targets) {
+            if (target.mode == MODE_CHANGING) {
+                // This is the target with bounds change.
+                final Animation[] animations =
+                        mAnimationSpec.createChangeBoundsChangeAnimations(target);
+                // Adapter for the starting snapshot leash.
+                adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
+                        animations[0], target));
+                // Adapter for the ending bounds changed leash.
+                adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
+                        animations[1], target));
+                continue;
+            }
+
+            // These are the other targets that don't have bounds change in the same transition.
+            final Animation animation;
+            if (target.hasAnimatingParent) {
+                // No-op if it will be covered by the changing parent window.
+                animation = TaskFragmentAnimationSpec.createNoopAnimation(target);
+            } else if (target.mode == MODE_CLOSING) {
+                animation = mAnimationSpec.createChangeBoundsCloseAnimation(target);
+            } else {
+                animation = mAnimationSpec.createChangeBoundsOpenAnimation(target);
+            }
+            adapters.add(new TaskFragmentAnimationAdapter(animation, target));
+        }
+        return adapters;
+    }
+
+    /**
+     * Whether we should use jump cut for the change transition.
+     * This normally happens when opening a new secondary with the existing primary using a
+     * different split layout. This can be complicated, like from horizontal to vertical split with
+     * new split pairs.
+     * Uses a jump cut animation to simplify.
+     */
+    private boolean shouldUseJumpCutForChangeAnimation(@NonNull RemoteAnimationTarget[] targets) {
+        boolean hasOpeningWindow = false;
+        boolean hasClosingWindow = false;
+        for (RemoteAnimationTarget target : targets) {
+            if (target.hasAnimatingParent) {
+                continue;
+            }
+            hasOpeningWindow |= target.mode == MODE_OPENING;
+            hasClosingWindow |= target.mode == MODE_CLOSING;
+        }
+        return hasOpeningWindow && hasClosingWindow;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
new file mode 100644
index 0000000..1f866c3
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -0,0 +1,267 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
+
+import android.app.ActivityThread;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.TranslateAnimation;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.policy.AttributeCache;
+import com.android.internal.policy.TransitionAnimation;
+
+/** Animation spec for TaskFragment transition. */
+// TODO(b/206557124): provide an easier way to customize animation
+class TaskFragmentAnimationSpec {
+
+    private static final String TAG = "TaskFragAnimationSpec";
+    private static final int CHANGE_ANIMATION_DURATION = 517;
+    private static final int CHANGE_ANIMATION_FADE_DURATION = 80;
+    private static final int CHANGE_ANIMATION_FADE_OFFSET = 30;
+
+    private final Context mContext;
+    private final TransitionAnimation mTransitionAnimation;
+    private final Interpolator mFastOutExtraSlowInInterpolator;
+    private final LinearInterpolator mLinearInterpolator;
+    private float mTransitionAnimationScaleSetting;
+
+    TaskFragmentAnimationSpec(@NonNull Handler handler) {
+        mContext = ActivityThread.currentActivityThread().getApplication();
+        mTransitionAnimation = new TransitionAnimation(mContext, false /* debug */, TAG);
+        // Initialize the AttributeCache for the TransitionAnimation.
+        AttributeCache.init(mContext);
+        mFastOutExtraSlowInInterpolator = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.fast_out_extra_slow_in);
+        mLinearInterpolator = new LinearInterpolator();
+
+        // The transition animation should be adjusted based on the developer option.
+        final ContentResolver resolver = mContext.getContentResolver();
+        mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+        resolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
+                new SettingsObserver(handler));
+    }
+
+    /** For target that doesn't need to be animated. */
+    @NonNull
+    static Animation createNoopAnimation(@NonNull RemoteAnimationTarget target) {
+        // Noop but just keep the target showing/hiding.
+        final float alpha = target.mode == MODE_CLOSING ? 0f : 1f;
+        return new AlphaAnimation(alpha, alpha);
+    }
+
+    /** Animation for target that is opening in a change transition. */
+    @NonNull
+    Animation createChangeBoundsOpenAnimation(@NonNull RemoteAnimationTarget target) {
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        final Rect bounds = target.screenSpaceBounds;
+        final int startLeft;
+        final int startTop;
+        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+            // The window will be animated in from left or right depending on its position.
+            startTop = 0;
+            startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+        } else {
+            // The window will be animated in from top or bottom depending on its position.
+            startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+            startLeft = 0;
+        }
+
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
+        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+        animation.setDuration(CHANGE_ANIMATION_DURATION);
+        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    /** Animation for target that is closing in a change transition. */
+    @NonNull
+    Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        // Use startBounds if the window is closing in case it may also resize.
+        final Rect bounds = target.startBounds;
+        final int endTop;
+        final int endLeft;
+        if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
+            // The window will be animated out to left or right depending on its position.
+            endTop = 0;
+            endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
+        } else {
+            // The window will be animated out to top or bottom depending on its position.
+            endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
+            endLeft = 0;
+        }
+
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
+        animation.setInterpolator(mFastOutExtraSlowInInterpolator);
+        animation.setDuration(CHANGE_ANIMATION_DURATION);
+        animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    /**
+     * Animation for target that is changing (bounds change) in a change transition.
+     * @return the return array always has two elements. The first one is for the start leash, and
+     *         the second one is for the end leash.
+     */
+    @NonNull
+    Animation[] createChangeBoundsChangeAnimations(@NonNull RemoteAnimationTarget target) {
+        // Both start bounds and end bounds are in screen coordinates. We will post translate
+        // to the local coordinates in TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Rect startBounds = target.startBounds;
+        final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
+        final Rect endBounds = target.screenSpaceBounds;
+        float scaleX = ((float) startBounds.width()) / endBounds.width();
+        float scaleY = ((float) startBounds.height()) / endBounds.height();
+        // Start leash is a child of the end leash. Reverse the scale so that the start leash won't
+        // be scaled up with its parent.
+        float startScaleX = 1.f / scaleX;
+        float startScaleY = 1.f / scaleY;
+
+        // The start leash will be fade out.
+        final AnimationSet startSet = new AnimationSet(false /* shareInterpolator */);
+        final Animation startAlpha = new AlphaAnimation(1f, 0f);
+        startAlpha.setInterpolator(mLinearInterpolator);
+        startAlpha.setDuration(CHANGE_ANIMATION_FADE_DURATION);
+        startAlpha.setStartOffset(CHANGE_ANIMATION_FADE_OFFSET);
+        startSet.addAnimation(startAlpha);
+        final Animation startScale = new ScaleAnimation(startScaleX, startScaleX, startScaleY,
+                startScaleY);
+        startScale.setInterpolator(mFastOutExtraSlowInInterpolator);
+        startScale.setDuration(CHANGE_ANIMATION_DURATION);
+        startSet.addAnimation(startScale);
+        startSet.initialize(startBounds.width(), startBounds.height(), endBounds.width(),
+                endBounds.height());
+        startSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+        // The end leash will be moved into the end position while scaling.
+        final AnimationSet endSet = new AnimationSet(true /* shareInterpolator */);
+        endSet.setInterpolator(mFastOutExtraSlowInInterpolator);
+        final Animation endScale = new ScaleAnimation(scaleX, 1, scaleY, 1);
+        endScale.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(endScale);
+        // The position should be 0-based as we will post translate in
+        // TaskFragmentAnimationAdapter#onAnimationUpdate
+        final Animation endTranslate = new TranslateAnimation(startBounds.left - endBounds.left, 0,
+                startBounds.top - endBounds.top, 0);
+        endTranslate.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(endTranslate);
+        // The end leash is resizing, we should update the window crop based on the clip rect.
+        final Rect startClip = new Rect(startBounds);
+        final Rect endClip = new Rect(endBounds);
+        startClip.offsetTo(0, 0);
+        endClip.offsetTo(0, 0);
+        final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+        clipAnim.setDuration(CHANGE_ANIMATION_DURATION);
+        endSet.addAnimation(clipAnim);
+        endSet.initialize(startBounds.width(), startBounds.height(), parentBounds.width(),
+                parentBounds.height());
+        endSet.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+
+        return new Animation[]{startSet, endSet};
+    }
+
+    @NonNull
+    Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
+            @NonNull Rect wholeAnimationBounds) {
+        final boolean isEnter = target.mode != MODE_CLOSING;
+        final Animation animation;
+        // Background color on TaskDisplayArea has already been set earlier in
+        // WindowContainer#getAnimationAdapter.
+        if (target.showBackdrop) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_open_enter
+                    : com.android.internal.R.anim.task_fragment_open_exit);
+        }
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are opening at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are launching together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+                wholeAnimationBounds.width(), wholeAnimationBounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    @NonNull
+    Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
+            @NonNull Rect wholeAnimationBounds) {
+        final boolean isEnter = target.mode != MODE_CLOSING;
+        final Animation animation;
+        if (target.showBackdrop) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_close_enter
+                    : com.android.internal.R.anim.task_fragment_close_exit);
+        }
+        // Use the whole animation bounds instead of the change bounds, so that when multiple change
+        // targets are closing at the same time, the animation applied to each will be the same.
+        // Otherwise, we may see gap between the activities that are finishing together.
+        animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
+                wholeAnimationBounds.width(), wholeAnimationBounds.height());
+        animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
+        return animation;
+    }
+
+    private float getTransitionAnimationScaleSetting() {
+        return WindowManager.fixScale(Settings.Global.getFloat(mContext.getContentResolver(),
+                Settings.Global.TRANSITION_ANIMATION_SCALE, mContext.getResources().getFloat(
+                                R.dimen.config_appTransitionAnimationDurationScaleDefault)));
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver(@NonNull Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mTransitionAnimationScaleSetting = getTransitionAnimationScaleSetting();
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index d0e2c99..dc1d983 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -36,7 +36,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -54,15 +53,13 @@
 class TaskFragmentContainer {
     private static final int APPEAR_EMPTY_TIMEOUT_MS = 3000;
 
+    /** Parcelable data of this TaskFragmentContainer. */
+    @NonNull
+    private final ParcelableTaskFragmentContainerData mParcelableData;
+
     @NonNull
     private final SplitController mController;
 
-    /**
-     * Client-created token that uniquely identifies the task fragment container instance.
-     */
-    @NonNull
-    private final IBinder mToken;
-
     /** Parent leaf Task. */
     @NonNull
     private final TaskContainer mTaskContainer;
@@ -104,9 +101,6 @@
      */
     private final List<IBinder> mActivitiesToFinishOnExit = new ArrayList<>();
 
-    @Nullable
-    private final String mOverlayTag;
-
     /**
      * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
      * for {@link #isOverlay()} container.
@@ -114,29 +108,13 @@
     @NonNull
     private final Bundle mLaunchOptions = new Bundle();
 
-    /**
-     * The associated {@link Activity#getActivityToken()} of the overlay container.
-     * Must be {@code null} for non-overlay container.
-     * <p>
-     * If an overlay container is associated with an activity, this overlay container will be
-     * dismissed when the associated activity is destroyed. If the overlay container is visible,
-     * activity will be launched on top of the overlay container and expanded to fill the parent
-     * container.
-     */
-    @Nullable
-    private final IBinder mAssociatedActivityToken;
-
     /** Indicates whether the container was cleaned up after the last activity was removed. */
     private boolean mIsFinished;
 
     /**
-     * Bounds that were requested last via {@link android.window.WindowContainerTransaction}.
-     */
-    private final Rect mLastRequestedBounds = new Rect();
-
-    /**
      * Windowing mode that was requested last via {@link android.window.WindowContainerTransaction}.
      */
+    // TODO(b/289875940): review this and other field that might need to be moved in the base class.
     @WindowingMode
     private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
 
@@ -209,17 +187,17 @@
             @NonNull SplitController controller,
             @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
             @Nullable Bundle launchOptions, @Nullable Activity associatedActivity) {
+        mParcelableData = new ParcelableTaskFragmentContainerData(
+                new Binder("TaskFragmentContainer"), overlayTag,
+                associatedActivity != null ? associatedActivity.getActivityToken() : null);
+
         if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
                 || (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
             throw new IllegalArgumentException(
                     "One and only one of pending activity and intent must be non-null");
         }
         mController = controller;
-        mToken = new Binder("TaskFragmentContainer");
         mTaskContainer = taskContainer;
-        mOverlayTag = overlayTag;
-        mAssociatedActivityToken = associatedActivity != null
-                ? associatedActivity.getActivityToken() : null;
 
         if (launchOptions != null) {
             mLaunchOptions.putAll(launchOptions);
@@ -257,21 +235,29 @@
         mPendingAppearedIntent = pendingAppearedIntent;
 
         // Save the information necessary for restoring the overlay when needed.
-        if (Flags.fixPipRestoreToOverlay() && overlayTag != null && pendingAppearedIntent != null
+        if (overlayTag != null && pendingAppearedIntent != null
                 && associatedActivity != null && !associatedActivity.isFinishing()) {
             final IBinder associatedActivityToken = associatedActivity.getActivityToken();
-            final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken,
-                    launchOptions, pendingAppearedIntent);
+            final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(
+                    mParcelableData.mToken, launchOptions, pendingAppearedIntent);
             mController.mOverlayRestoreParams.put(associatedActivityToken, params);
         }
     }
 
+    /** This is only used when restoring it from a {@link ParcelableTaskFragmentContainerData}. */
+    TaskFragmentContainer(@NonNull ParcelableTaskFragmentContainerData data,
+            @NonNull SplitController splitController, @NonNull TaskContainer taskContainer) {
+        mParcelableData = data;
+        mController = splitController;
+        mTaskContainer = taskContainer;
+    }
+
     /**
      * Returns the client-created token that uniquely identifies this container.
      */
     @NonNull
     IBinder getTaskFragmentToken() {
-        return mToken;
+        return mParcelableData.mToken;
     }
 
     /** List of non-finishing activities that belong to this container and live in this process. */
@@ -390,7 +376,8 @@
             return null;
         }
         return new ActivityStack(activities, isEmpty(),
-                ActivityStack.Token.createFromBinder(mToken), mOverlayTag);
+                ActivityStack.Token.createFromBinder(mParcelableData.mToken),
+                mParcelableData.mOverlayTag);
     }
 
     /** Adds the activity that will be reparented to this container. */
@@ -414,7 +401,7 @@
         final ActivityThread.ActivityClientRecord record = ActivityThread
                 .currentActivityThread().getActivityClient(activityToken);
         if (record != null) {
-            record.mTaskFragmentToken = mToken;
+            record.mTaskFragmentToken = mParcelableData.mToken;
         }
     }
 
@@ -470,7 +457,7 @@
         if (!isOverlayWithActivityAssociation()) {
             return;
         }
-        if (mAssociatedActivityToken == activityToken) {
+        if (mParcelableData.mAssociatedActivityToken == activityToken) {
             // If the associated activity is destroyed, also finish this overlay container.
             mController.mPresenter.cleanupContainer(wct, this, false /* shouldFinishDependent */);
         }
@@ -777,8 +764,8 @@
      * @see WindowContainerTransaction#setRelativeBounds
      */
     boolean areLastRequestedBoundsEqual(@Nullable Rect relBounds) {
-        return (relBounds == null && mLastRequestedBounds.isEmpty())
-                || mLastRequestedBounds.equals(relBounds);
+        return (relBounds == null && mParcelableData.mLastRequestedBounds.isEmpty())
+                || mParcelableData.mLastRequestedBounds.equals(relBounds);
     }
 
     /**
@@ -788,14 +775,14 @@
      */
     void setLastRequestedBounds(@Nullable Rect relBounds) {
         if (relBounds == null) {
-            mLastRequestedBounds.setEmpty();
+            mParcelableData.mLastRequestedBounds.setEmpty();
         } else {
-            mLastRequestedBounds.set(relBounds);
+            mParcelableData.mLastRequestedBounds.set(relBounds);
         }
     }
 
     @NonNull Rect getLastRequestedBounds() {
-        return mLastRequestedBounds;
+        return mParcelableData.mLastRequestedBounds;
     }
 
     /**
@@ -966,6 +953,16 @@
         return mTaskContainer.getTaskId();
     }
 
+    @NonNull
+    IBinder getToken() {
+        return mParcelableData.mToken;
+    }
+
+    @NonNull
+    ParcelableTaskFragmentContainerData getParcelableData() {
+        return mParcelableData;
+    }
+
     /** Gets the parent Task. */
     @NonNull
     TaskContainer getTaskContainer() {
@@ -1012,7 +1009,7 @@
 
     /** Returns whether this taskFragment container is an overlay container. */
     boolean isOverlay() {
-        return mOverlayTag != null;
+        return mParcelableData.mOverlayTag != null;
     }
 
     /**
@@ -1021,7 +1018,7 @@
      */
     @Nullable
     String getOverlayTag() {
-        return mOverlayTag;
+        return mParcelableData.mOverlayTag;
     }
 
     /**
@@ -1046,7 +1043,7 @@
      */
     @Nullable
     IBinder getAssociatedActivityToken() {
-        return mAssociatedActivityToken;
+        return mParcelableData.mAssociatedActivityToken;
     }
 
     /**
@@ -1054,11 +1051,11 @@
      * a non-fill-parent overlay without activity association.
      */
     boolean isAlwaysOnTopOverlay() {
-        return isOverlay() && mAssociatedActivityToken == null;
+        return isOverlay() && mParcelableData.mAssociatedActivityToken == null;
     }
 
     boolean isOverlayWithActivityAssociation() {
-        return isOverlay() && mAssociatedActivityToken != null;
+        return isOverlay() && mParcelableData.mAssociatedActivityToken != null;
     }
 
     @Override
@@ -1075,13 +1072,13 @@
     private String toString(boolean includeContainersToFinishOnExit) {
         return "TaskFragmentContainer{"
                 + " parentTaskId=" + getTaskId()
-                + " token=" + mToken
+                + " token=" + mParcelableData.mToken
                 + " topNonFinishingActivity=" + getTopNonFinishingActivity()
                 + " runningActivityCount=" + getRunningActivityCount()
                 + " isFinished=" + mIsFinished
-                + " overlayTag=" + mOverlayTag
-                + " associatedActivityToken=" + mAssociatedActivityToken
-                + " lastRequestedBounds=" + mLastRequestedBounds
+                + " overlayTag=" + mParcelableData.mOverlayTag
+                + " associatedActivityToken=" + mParcelableData.mAssociatedActivityToken
+                + " lastRequestedBounds=" + mParcelableData.mLastRequestedBounds
                 + " pendingAppearedActivities=" + mPendingAppearedActivities
                 + (includeContainersToFinishOnExit ? " containersToFinishOnExit="
                 + containersToFinishOnExitToString() : "")
@@ -1204,7 +1201,7 @@
 
             if (taskContainer == null) {
                 // Adding a TaskContainer if no existed one.
-                taskContainer = new TaskContainer(mTaskId, mActivityInTask);
+                taskContainer = new TaskContainer(mTaskId, mActivityInTask, mSplitController);
                 mSplitController.addTaskContainer(mTaskId, taskContainer);
             }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
deleted file mode 100644
index 60bc7be..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ /dev/null
@@ -1,109 +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 androidx.window.sidecar;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.Application;
-import android.content.Context;
-import android.hardware.devicestate.DeviceStateManager;
-import android.os.Bundle;
-import android.os.IBinder;
-
-import androidx.annotation.NonNull;
-import androidx.window.common.BaseDataProducer;
-import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
-import androidx.window.common.EmptyLifecycleCallbacksAdapter;
-import androidx.window.common.RawFoldingFeatureProducer;
-import androidx.window.common.layout.CommonFoldingFeature;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Reference implementation of androidx.window.sidecar OEM interface for use with
- * WindowManager Jetpack.
- */
-class SampleSidecarImpl extends StubSidecar {
-    private List<CommonFoldingFeature> mStoredFeatures = new ArrayList<>();
-
-    SampleSidecarImpl(Context context) {
-        ((Application) context.getApplicationContext())
-                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
-        RawFoldingFeatureProducer settingsFeatureProducer = new RawFoldingFeatureProducer(context);
-        BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer =
-                new DeviceStateManagerFoldingFeatureProducer(context,
-                        settingsFeatureProducer,
-                        context.getSystemService(DeviceStateManager.class));
-
-        foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
-    }
-
-    private void setStoredFeatures(List<CommonFoldingFeature> storedFeatures) {
-        mStoredFeatures = storedFeatures;
-    }
-
-    private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
-        setStoredFeatures(storedFeatures);
-        updateDeviceState(getDeviceState());
-        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
-            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
-            updateWindowLayout(windowToken, newLayout);
-        }
-    }
-
-    @NonNull
-    @Override
-    public SidecarDeviceState getDeviceState() {
-        return SidecarHelper.calculateDeviceState(mStoredFeatures);
-    }
-
-    @NonNull
-    @Override
-    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
-        return SidecarHelper.calculateWindowLayoutInfo(windowToken, mStoredFeatures);
-    }
-
-    @Override
-    protected void onListenersChanged() {
-        if (hasListeners()) {
-            onDisplayFeaturesChanged(mStoredFeatures);
-        }
-    }
-
-    private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
-        @Override
-        public void onActivityCreated(@NonNull Activity activity,
-                @Nullable Bundle savedInstanceState) {
-            super.onActivityCreated(activity, savedInstanceState);
-            onDisplayFeaturesChangedForActivity(activity);
-        }
-
-        @Override
-        public void onActivityConfigurationChanged(@NonNull Activity activity) {
-            super.onActivityConfigurationChanged(activity);
-            onDisplayFeaturesChangedForActivity(activity);
-        }
-
-        private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) {
-            IBinder token = activity.getWindow().getAttributes().token;
-            if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) {
-                onDisplayFeaturesChanged(mStoredFeatures);
-            }
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarImpl.java
new file mode 100644
index 0000000..a1de206
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarImpl.java
@@ -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 androidx.window.sidecar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.window.common.BaseDataProducer;
+import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
+import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.common.RawFoldingFeatureProducer;
+import androidx.window.common.layout.CommonFoldingFeature;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+class SidecarImpl implements SidecarInterface {
+
+    private static final String TAG = "WindowManagerSidecar";
+
+    @Nullable
+    private SidecarCallback mSidecarCallback;
+    private final ArraySet<IBinder> mWindowLayoutChangeListenerTokens = new ArraySet<>();
+    private boolean mDeviceStateChangeListenerRegistered;
+    @NonNull
+    private List<CommonFoldingFeature> mStoredFeatures = new ArrayList<>();
+
+    SidecarImpl(Context context) {
+        ((Application) context.getApplicationContext())
+                .registerActivityLifecycleCallbacks(new SidecarImpl.NotifyOnConfigurationChanged());
+        RawFoldingFeatureProducer settingsFeatureProducer = new RawFoldingFeatureProducer(context);
+        BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer =
+                new DeviceStateManagerFoldingFeatureProducer(context,
+                        settingsFeatureProducer,
+                        context.getSystemService(DeviceStateManager.class));
+
+        foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
+    }
+
+    @NonNull
+    @Override
+    public SidecarDeviceState getDeviceState() {
+        return SidecarHelper.calculateDeviceState(mStoredFeatures);
+    }
+
+    @NonNull
+    @Override
+    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+        return SidecarHelper.calculateWindowLayoutInfo(windowToken, mStoredFeatures);
+    }
+
+    @Override
+    public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) {
+        mSidecarCallback = sidecarCallback;
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) {
+        mWindowLayoutChangeListenerTokens.add(iBinder);
+        onListenersChanged();
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) {
+        mWindowLayoutChangeListenerTokens.remove(iBinder);
+        onListenersChanged();
+    }
+
+    @Override
+    public void onDeviceStateListenersChanged(boolean isEmpty) {
+        mDeviceStateChangeListenerRegistered = !isEmpty;
+        onListenersChanged();
+    }
+
+    private void setStoredFeatures(@NonNull List<CommonFoldingFeature> storedFeatures) {
+        mStoredFeatures = Objects.requireNonNull(storedFeatures);
+    }
+
+    private void onDisplayFeaturesChanged(@NonNull List<CommonFoldingFeature> storedFeatures) {
+        setStoredFeatures(storedFeatures);
+        updateDeviceState(getDeviceState());
+        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+            updateWindowLayout(windowToken, newLayout);
+        }
+    }
+
+    void updateDeviceState(@NonNull SidecarDeviceState newState) {
+        if (mSidecarCallback != null) {
+            try {
+                mSidecarCallback.onDeviceStateChanged(newState);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "App is using an outdated Window Jetpack library", e);
+            }
+        }
+    }
+
+    void updateWindowLayout(@NonNull IBinder windowToken,
+            @NonNull SidecarWindowLayoutInfo newLayout) {
+        if (mSidecarCallback != null) {
+            try {
+                mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout);
+            } catch (AbstractMethodError e) {
+                Log.e(TAG, "App is using an outdated Window Jetpack library", e);
+            }
+        }
+    }
+
+    @NonNull
+    private Set<IBinder> getWindowsListeningForLayoutChanges() {
+        return mWindowLayoutChangeListenerTokens;
+    }
+
+    protected boolean hasListeners() {
+        return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered;
+    }
+
+    private void onListenersChanged() {
+        if (hasListeners()) {
+            onDisplayFeaturesChanged(mStoredFeatures);
+        }
+    }
+
+
+    private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
+        @Override
+        public void onActivityCreated(@NonNull Activity activity,
+                @Nullable Bundle savedInstanceState) {
+            super.onActivityCreated(activity, savedInstanceState);
+            onDisplayFeaturesChangedForActivity(activity);
+        }
+
+        @Override
+        public void onActivityConfigurationChanged(@NonNull Activity activity) {
+            super.onActivityConfigurationChanged(activity);
+            onDisplayFeaturesChangedForActivity(activity);
+        }
+
+        private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) {
+            IBinder token = activity.getWindow().getAttributes().token;
+            if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) {
+                onDisplayFeaturesChanged(mStoredFeatures);
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 686a31b..1e306fc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -36,7 +36,7 @@
     @Nullable
     public static SidecarInterface getSidecarImpl(Context context) {
         return isWindowExtensionsEnabled()
-                ? new SampleSidecarImpl(context.getApplicationContext())
+                ? new SidecarImpl(context.getApplicationContext())
                 : null;
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
deleted file mode 100644
index 46c1f3b..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java
+++ /dev/null
@@ -1,96 +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 androidx.window.sidecar;
-
-import android.os.IBinder;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Basic implementation of the {@link SidecarInterface}. An OEM can choose to use it as the base
- * class for their implementation.
- */
-abstract class StubSidecar implements SidecarInterface {
-
-    private static final String TAG = "WindowManagerSidecar";
-
-    private SidecarCallback mSidecarCallback;
-    final Set<IBinder> mWindowLayoutChangeListenerTokens = new HashSet<>();
-    private boolean mDeviceStateChangeListenerRegistered;
-
-    StubSidecar() {
-    }
-
-    @Override
-    public void setSidecarCallback(@NonNull SidecarCallback sidecarCallback) {
-        this.mSidecarCallback = sidecarCallback;
-    }
-
-    @Override
-    public void onWindowLayoutChangeListenerAdded(@NonNull IBinder iBinder) {
-        this.mWindowLayoutChangeListenerTokens.add(iBinder);
-        this.onListenersChanged();
-    }
-
-    @Override
-    public void onWindowLayoutChangeListenerRemoved(@NonNull IBinder iBinder) {
-        this.mWindowLayoutChangeListenerTokens.remove(iBinder);
-        this.onListenersChanged();
-    }
-
-    @Override
-    public void onDeviceStateListenersChanged(boolean isEmpty) {
-        this.mDeviceStateChangeListenerRegistered = !isEmpty;
-        this.onListenersChanged();
-    }
-
-    void updateDeviceState(SidecarDeviceState newState) {
-        if (this.mSidecarCallback != null) {
-            try {
-                mSidecarCallback.onDeviceStateChanged(newState);
-            } catch (AbstractMethodError e) {
-                Log.e(TAG, "App is using an outdated Window Jetpack library", e);
-            }
-        }
-    }
-
-    void updateWindowLayout(@NonNull IBinder windowToken,
-            @NonNull SidecarWindowLayoutInfo newLayout) {
-        if (this.mSidecarCallback != null) {
-            try {
-                mSidecarCallback.onWindowLayoutChanged(windowToken, newLayout);
-            } catch (AbstractMethodError e) {
-                Log.e(TAG, "App is using an outdated Window Jetpack library", e);
-            }
-        }
-    }
-
-    @NonNull
-    Set<IBinder> getWindowsListeningForLayoutChanges() {
-        return mWindowLayoutChangeListenerTokens;
-    }
-
-    protected boolean hasListeners() {
-        return !mWindowLayoutChangeListenerTokens.isEmpty() || mDeviceStateChangeListenerRegistered;
-    }
-
-    protected abstract void onListenersChanged();
-}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index 7dc78fd..5c85778 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -222,7 +222,7 @@
         doReturn(resources).when(activity).getResources();
         doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
 
-        return new TaskContainer(TASK_ID, activity);
+        return new TaskContainer(TASK_ID, activity, mock(SplitController.class));
     }
 
     static TaskContainer createTestTaskContainer(@NonNull SplitController controller) {
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 8911d18..ac004c3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -23,6 +23,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -84,6 +86,24 @@
     }
 
     @Test
+    public void testUnregisterOrganizer() {
+        mOrganizer.overrideSplitAnimation();
+        mOrganizer.unregisterOrganizer();
+
+        verify(mOrganizer).unregisterRemoteAnimations();
+    }
+
+    @Test
+    public void testOverrideSplitAnimation() {
+        assertNull(mOrganizer.mAnimationController);
+
+        mOrganizer.overrideSplitAnimation();
+
+        assertNotNull(mOrganizer.mAnimationController);
+        verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition);
+    }
+
+    @Test
     public void testExpandTaskFragment() {
         final TaskContainer taskContainer = createTestTaskContainer();
         doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 475475b..5b97e7e 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -549,7 +549,7 @@
         assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer);
 
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */));
 
         mSplitController.updateOverlayContainer(mTransaction, overlayContainer);
@@ -618,7 +618,8 @@
         final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
                 new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
-                true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+                TASK_ID, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */);
         parentInfo.getConfiguration().windowConfiguration.getBounds().offset(10, 10);
 
         mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
@@ -642,7 +643,8 @@
         final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(
                 new Configuration(taskProperties.getConfiguration()), taskProperties.getDisplayId(),
-                true /* visible */, false /* hasDirectActivity */, null /* decorSurface */);
+                TASK_ID, true /* visible */, false /* hasDirectActivity */,
+                null /* decorSurface */);
 
         mSplitController.onTaskFragmentParentInfoChanged(mTransaction, TASK_ID, parentInfo);
 
@@ -874,8 +876,6 @@
 
     @Test
     public void testOnActivityReparentedToTask_overlayRestoration() {
-        mSetFlagRule.enableFlags(Flags.FLAG_FIX_PIP_RESTORE_TO_OVERLAY);
-
         // Prepares and mock the data necessary for the test.
         final IBinder activityToken = mActivity.getActivityToken();
         final Intent intent = new Intent();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index d852204..0512412 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1164,7 +1164,7 @@
     public void testOnTransactionReady_taskFragmentParentInfoChanged() {
         final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
         final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */);
         transaction.addChange(new TaskFragmentTransaction.Change(
                 TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
@@ -1625,7 +1625,7 @@
         final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
         final Configuration configuration = new Configuration();
         final TaskFragmentParentInfo originalInfo = new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */);
         mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
                 TASK_ID, originalInfo);
@@ -1634,7 +1634,7 @@
         // Making a public configuration change while the Task is invisible.
         configuration.densityDpi += 100;
         final TaskFragmentParentInfo invisibleInfo = new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, false /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, false /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */);
         mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
                 TASK_ID, invisibleInfo);
@@ -1646,7 +1646,7 @@
 
         // Updates when Task to become visible
         final TaskFragmentParentInfo visibleInfo = new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */);
         mSplitController.onTaskFragmentParentInfoChanged(mock(WindowContainerTransaction.class),
                 TASK_ID, visibleInfo);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 2847232..97f4d07 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -23,6 +23,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
 
 import static org.junit.Assert.assertEquals;
@@ -82,7 +83,7 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_MULTI_WINDOW,
@@ -90,7 +91,7 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */));
 
         assertEquals(WINDOWING_MODE_FREEFORM,
@@ -111,14 +112,14 @@
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */));
 
         assertFalse(taskContainer.isInPictureInPicture());
 
         configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
         taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
-                DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */,
+                DEFAULT_DISPLAY, TASK_ID, true /* visible */, false /* hasDirectActivity */,
                 null /* decorSurface */));
 
         assertTrue(taskContainer.isInPictureInPicture());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
new file mode 100644
index 0000000..a1e9f08
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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 androidx.window.extensions.embedding;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.Mockito.never;
+
+import android.platform.test.annotations.Presubmit;
+import android.window.TaskFragmentOrganizer;
+
+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.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test class for {@link TaskFragmentAnimationController}.
+ *
+ * Build/Install/Run:
+ *  atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskFragmentAnimationControllerTest {
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
+
+    @Mock
+    private TaskFragmentOrganizer mOrganizer;
+    private TaskFragmentAnimationController mAnimationController;
+
+    @Before
+    public void setup() {
+        mAnimationController = new TaskFragmentAnimationController(mOrganizer);
+    }
+
+    @Test
+    public void testRegisterRemoteAnimations() {
+        mAnimationController.registerRemoteAnimations();
+
+        verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+
+        mAnimationController.registerRemoteAnimations();
+
+        // No extra call if it has been registered.
+        verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
+    }
+
+    @Test
+    public void testUnregisterRemoteAnimations() {
+        mAnimationController.unregisterRemoteAnimations();
+
+        // No call if it is not registered.
+        verify(mOrganizer, never()).unregisterRemoteAnimations();
+
+        mAnimationController.registerRemoteAnimations();
+        mAnimationController.unregisterRemoteAnimations();
+
+        verify(mOrganizer).unregisterRemoteAnimations();
+
+        mAnimationController.unregisterRemoteAnimations();
+
+        // No extra call if it has been unregistered.
+        verify(mOrganizer).unregisterRemoteAnimations();
+    }
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 5135e9e..a79bc97 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -39,28 +39,6 @@
     path: "src",
 }
 
-// Sources that have no dependencies that can be used directly downstream of this library
-// TODO(b/322791067): move these sources to WindowManager-Shell-shared
-filegroup {
-    name: "wm_shell_util-sources",
-    srcs: [
-        "src/com/android/wm/shell/animation/Interpolators.java",
-        "src/com/android/wm/shell/common/bubbles/*.kt",
-        "src/com/android/wm/shell/common/bubbles/*.java",
-        "src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt",
-        "src/com/android/wm/shell/common/split/SplitScreenConstants.java",
-        "src/com/android/wm/shell/common/TransactionPool.java",
-        "src/com/android/wm/shell/common/TriangleShape.java",
-        "src/com/android/wm/shell/common/desktopmode/*.kt",
-        "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java",
-        "src/com/android/wm/shell/pip/PipContentOverlay.java",
-        "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java",
-        "src/com/android/wm/shell/sysui/ShellSharedConstants.java",
-        "src/com/android/wm/shell/util/**/*.java",
-    ],
-    path: "src",
-}
-
 // Aidls which can be used directly downstream of this library
 filegroup {
     name: "wm_shell-aidls",
@@ -166,6 +144,16 @@
     },
 }
 
+java_library {
+    name: "WindowManager-Shell-lite-proto",
+
+    srcs: ["src/com/android/wm/shell/desktopmode/education/data/proto/**/*.proto"],
+
+    proto: {
+        type: "lite",
+    },
+}
+
 filegroup {
     name: "wm_shell-shared-aidls",
 
@@ -185,9 +173,11 @@
         ":wm_shell-shared-aidls",
     ],
     static_libs: [
+        "androidx.core_core-animation",
         "androidx.dynamicanimation_dynamicanimation",
         "jsr330",
     ],
+    kotlincflags: ["-Xjvm-default=all"],
 }
 
 java_library {
@@ -206,15 +196,16 @@
         // TODO(b/168581922) protologtool do not support kotlin(*.kt)
         ":wm_shell-sources-kt",
         ":wm_shell-aidls",
+        ":wm_shell-shared-aidls",
     ],
     resource_dirs: [
         "res",
     ],
     static_libs: [
         "androidx.appcompat_appcompat",
-        "androidx.core_core-animation",
         "androidx.core_core-ktx",
         "androidx.arch.core_core-runtime",
+        "androidx.datastore_datastore",
         "androidx.compose.material3_material3",
         "androidx-constraintlayout_constraintlayout",
         "androidx.dynamicanimation_dynamicanimation",
@@ -225,6 +216,7 @@
         "//frameworks/libs/systemui:iconloader_base",
         "com_android_wm_shell_flags_lib",
         "WindowManager-Shell-proto",
+        "WindowManager-Shell-lite-proto",
         "WindowManager-Shell-shared",
         "perfetto_trace_java_protos",
         "dagger2",
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index bbbc23e..3b739c3 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
     <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" />
 
     <application>
         <activity
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index 2e19d52..c6044a45 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,5 +1,5 @@
 [email protected]
 
 # Give submodule owners in shell resource approval
-per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
+per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
 per-file res*/*/tv_*.xml = [email protected]
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 56ea7c2..526ccd5 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -25,11 +25,10 @@
 }
 
 flag {
-    name: "enable_pip2_implementation"
+    name: "enable_pip2"
     namespace: "multitasking"
     description: "Enables the new implementation of PiP (PiP2)"
-    bug: "290220798"
-    is_fixed_read_only: true
+    bug: "311462191"
 }
 
 flag {
@@ -128,3 +127,27 @@
     description: "Enable bubble bar to be shown in the persistent task bar"
     bug: "346391377"
 }
+
+flag {
+    name: "bubble_view_info_executors"
+    namespace: "multitasking"
+    description: "Use executors to inflate bubble views"
+    bug: "353894869"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+   name: "enable_bubble_to_fullscreen"
+   namespace: "multitasking"
+   description: "Enable an option to move bubbles to fullscreen"
+   bug: "363326492"
+}
+
+flag {
+    name: "enable_flexible_split"
+    namespace: "multitasking"
+    description: "Enables flexibile split feature for split screen"
+    bug: "349828130"
+}
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
index 027b28e..991cdcf 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
index 027b28e..991cdcf 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/onDevice/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
index e02c89a..c729442 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/dark_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
index e02c89a..c729442 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/goldens/robolectric/phone/light_portrait_bubbles_education.png
Binary files differ
diff --git a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
index d35f493..f09969d 100644
--- a/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
+++ b/libs/WindowManager/Shell/multivalentScreenshotTests/src/com/android/wm/shell/bubbles/BubbleEducationViewScreenshotTest.kt
@@ -16,7 +16,7 @@
 package com.android.wm.shell.bubbles
 
 import android.view.LayoutInflater
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupView
 import com.android.wm.shell.testing.goldenpathmanager.WMShellGoldenPathManager
 import com.android.wm.shell.R
 import org.junit.Rule
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index ae60d8b..b38d00da 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -30,7 +30,7 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors.directExecutor
 import org.junit.Before
@@ -268,7 +268,8 @@
             )
         positioner.update(deviceConfig)
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT)
     }
@@ -294,6 +295,7 @@
                 0 /* taskId */,
                 null /* locus */,
                 true /* isDismissable */,
+                directExecutor(),
                 directExecutor()
             ) {}
 
@@ -322,6 +324,7 @@
                 0 /* taskId */,
                 null /* locus */,
                 true /* isDismissable */,
+                directExecutor(),
                 directExecutor()
             ) {}
 
@@ -416,7 +419,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         // This bubble will have max height so it'll always be top aligned
         assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -433,7 +437,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         // Always top aligned in phone portrait
         assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -452,7 +457,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         // This bubble will have max height which is always top aligned on small tablets
         assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -470,7 +476,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         // This bubble will have max height which is always top aligned on small tablets
         assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -489,7 +496,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         // This bubble will have max height which is always top aligned on landscape, large tablet
         assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */))
@@ -507,7 +515,8 @@
         positioner.update(deviceConfig)
 
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
 
         val manageButtonHeight =
             context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height)
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 5e67333..96ffa03 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
@@ -53,6 +53,7 @@
 import org.mockito.kotlin.mock
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 import java.util.concurrent.Semaphore
 import java.util.concurrent.TimeUnit
 import java.util.function.Consumer
@@ -101,6 +102,7 @@
                 BubbleLogger(UiEventLoggerFake()),
                 positioner,
                 BubbleEducationController(context),
+                shellExecutor,
                 shellExecutor
             )
         bubbleStackViewManager = FakeBubbleStackViewManager()
@@ -363,6 +365,7 @@
                 /* taskId= */ 0,
                 "locus",
                 /* isDismissable= */ true,
+                directExecutor(),
                 directExecutor()
             ) {}
         inflateBubble(bubble)
@@ -372,7 +375,8 @@
     private fun createAndInflateBubble(): Bubble {
         val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
         val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
-        val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+        val bubble =
+            Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor(), directExecutor())
         inflateBubble(bubble)
         return bubble
     }
@@ -458,5 +462,7 @@
         override fun isShowingAsBubbleBar(): Boolean = false
 
         override fun hideCurrentInputMethod() {}
+
+        override fun updateBubbleBarLocation(location: BubbleBarLocation) {}
     }
 }
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
new file mode 100644
index 0000000..9fdde128
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -0,0 +1,349 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
+import android.graphics.Color
+import android.os.Handler
+import android.os.UserManager
+import android.view.IWindowManager
+import android.view.WindowManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.internal.protolog.ProtoLog
+import com.android.internal.statusbar.IStatusBarService
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.WindowManagerShellWrapper
+import com.android.wm.shell.bubbles.properties.BubbleProperties
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayInsetsController
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.shared.TransactionPool
+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.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTransitions
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/** Test inflating bubbles with [BubbleViewInfoTask]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleViewInfoTaskTest {
+
+    private val context = ApplicationProvider.getApplicationContext<Context>()
+    private lateinit var metadataFlagListener: Bubbles.BubbleMetadataFlagListener
+    private lateinit var iconFactory: BubbleIconFactory
+    private lateinit var bubbleController: BubbleController
+    private lateinit var mainExecutor: TestExecutor
+    private lateinit var bgExecutor: TestExecutor
+    private lateinit var bubbleStackView: BubbleStackView
+    private lateinit var bubblePositioner: BubblePositioner
+    private lateinit var expandedViewManager: BubbleExpandedViewManager
+
+    private val bubbleTaskViewFactory = BubbleTaskViewFactory {
+        BubbleTaskView(mock<TaskView>(), directExecutor())
+    }
+
+    @Before
+    fun setUp() {
+        ProtoLog.REQUIRE_PROTOLOGTOOL = false
+        metadataFlagListener = Bubbles.BubbleMetadataFlagListener {}
+        iconFactory =
+            BubbleIconFactory(
+                context,
+                60,
+                30,
+                Color.RED,
+                context.resources.getDimensionPixelSize(R.dimen.importance_ring_stroke_width)
+            )
+
+        mainExecutor = TestExecutor()
+        bgExecutor = TestExecutor()
+        val windowManager = context.getSystemService(WindowManager::class.java)
+        val shellInit = ShellInit(mainExecutor)
+        val shellCommandHandler = ShellCommandHandler()
+        val shellController =
+            ShellController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                mock<DisplayInsetsController>(),
+                mainExecutor
+            )
+        bubblePositioner = BubblePositioner(context, windowManager)
+        val bubbleData =
+            BubbleData(
+                context,
+                mock<BubbleLogger>(),
+                bubblePositioner,
+                BubbleEducationController(context),
+                mainExecutor,
+                bgExecutor
+            )
+
+        val surfaceSynchronizer = { obj: Runnable -> obj.run() }
+
+        val bubbleDataRepository =
+            BubbleDataRepository(
+                mock<LauncherApps>(),
+                mainExecutor,
+                bgExecutor,
+                BubblePersistentRepository(context)
+            )
+
+        bubbleController =
+            BubbleController(
+                context,
+                shellInit,
+                shellCommandHandler,
+                shellController,
+                bubbleData,
+                surfaceSynchronizer,
+                FloatingContentCoordinator(),
+                bubbleDataRepository,
+                mock<IStatusBarService>(),
+                windowManager,
+                WindowManagerShellWrapper(mainExecutor),
+                mock<UserManager>(),
+                mock<LauncherApps>(),
+                mock<BubbleLogger>(),
+                mock<TaskStackListenerImpl>(),
+                mock<ShellTaskOrganizer>(),
+                bubblePositioner,
+                mock<DisplayController>(),
+                null,
+                null,
+                mainExecutor,
+                mock<Handler>(),
+                bgExecutor,
+                mock<TaskViewTransitions>(),
+                mock<Transitions>(),
+                SyncTransactionQueue(TransactionPool(), mainExecutor),
+                mock<IWindowManager>(),
+                mock<BubbleProperties>()
+            )
+
+        val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
+        bubbleStackView =
+            BubbleStackView(
+                context,
+                bubbleStackViewManager,
+                bubblePositioner,
+                bubbleData,
+                surfaceSynchronizer,
+                FloatingContentCoordinator(),
+                bubbleController,
+                mainExecutor
+            )
+        expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
+    }
+
+    @Test
+    fun start_runsOnExecutors() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+
+        task.start()
+
+        assertThat(bubble.isInflated).isFalse()
+        assertThat(bubble.expandedView).isNull()
+        assertThat(task.isFinished).isFalse()
+
+        bgExecutor.flushAll()
+        assertThat(bubble.isInflated).isFalse()
+        assertThat(bubble.expandedView).isNull()
+        assertThat(task.isFinished).isFalse()
+
+        mainExecutor.flushAll()
+        assertThat(bubble.isInflated).isTrue()
+        assertThat(bubble.expandedView).isNotNull()
+        assertThat(task.isFinished).isTrue()
+    }
+
+    @Test
+    fun startSync_runsImmediately() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+
+        task.startSync()
+        assertThat(bubble.isInflated).isTrue()
+        assertThat(bubble.expandedView).isNotNull()
+        assertThat(task.isFinished).isTrue()
+    }
+
+    @Test
+    fun start_calledTwice_throwsIllegalStateException() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+        task.start()
+        Assert.assertThrows(IllegalStateException::class.java) { task.start() }
+    }
+
+    @Test
+    fun startSync_calledTwice_throwsIllegalStateException() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+        task.startSync()
+        Assert.assertThrows(IllegalStateException::class.java) { task.startSync() }
+    }
+
+    @Test
+    fun start_callbackNotified() {
+        val bubble = createBubbleWithShortcut()
+        var bubbleFromCallback: Bubble? = null
+        val callback = BubbleViewInfoTask.Callback { b: Bubble? -> bubbleFromCallback = b }
+        val task = createBubbleViewInfoTask(bubble, callback)
+        task.start()
+        bgExecutor.flushAll()
+        mainExecutor.flushAll()
+        assertThat(bubbleFromCallback).isSameInstanceAs(bubble)
+    }
+
+    @Test
+    fun startSync_callbackNotified() {
+        val bubble = createBubbleWithShortcut()
+        var bubbleFromCallback: Bubble? = null
+        val callback = BubbleViewInfoTask.Callback { b: Bubble? -> bubbleFromCallback = b }
+        val task = createBubbleViewInfoTask(bubble, callback)
+        task.startSync()
+        assertThat(bubbleFromCallback).isSameInstanceAs(bubble)
+    }
+
+    @Test
+    fun cancel_beforeBackgroundWorkStarts_bubbleNotInflated() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+        task.start()
+
+        // Cancel before allowing background or main executor to run
+        task.cancel()
+        bgExecutor.flushAll()
+        mainExecutor.flushAll()
+
+        assertThat(bubble.isInflated).isFalse()
+        assertThat(bubble.expandedView).isNull()
+        assertThat(task.isFinished).isTrue()
+    }
+
+    @Test
+    fun cancel_afterBackgroundWorkBeforeMainThreadWork_bubbleNotInflated() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+        task.start()
+
+        // Cancel after background executor runs, but before main executor runs
+        bgExecutor.flushAll()
+        task.cancel()
+        mainExecutor.flushAll()
+
+        assertThat(bubble.isInflated).isFalse()
+        assertThat(bubble.expandedView).isNull()
+        assertThat(task.isFinished).isTrue()
+    }
+
+    @Test
+    fun cancel_beforeStart_bubbleNotInflated() {
+        val bubble = createBubbleWithShortcut()
+        val task = createBubbleViewInfoTask(bubble)
+        task.cancel()
+        task.start()
+        bgExecutor.flushAll()
+        mainExecutor.flushAll()
+
+        assertThat(task.isFinished).isTrue()
+        assertThat(bubble.isInflated).isFalse()
+        assertThat(bubble.expandedView).isNull()
+    }
+
+    private fun createBubbleWithShortcut(): Bubble {
+        val shortcutInfo = ShortcutInfo.Builder(context, "mockShortcutId").build()
+        return Bubble(
+            "mockKey",
+            shortcutInfo,
+            1000,
+            Resources.ID_NULL,
+            "mockTitle",
+            0 /* taskId */,
+            "mockLocus",
+            true /* isDismissible */,
+            mainExecutor,
+            bgExecutor,
+            metadataFlagListener
+        )
+    }
+
+    private fun createBubbleViewInfoTask(
+        bubble: Bubble,
+        callback: BubbleViewInfoTask.Callback? = null
+    ): BubbleViewInfoTask {
+        return BubbleViewInfoTask(
+            bubble,
+            context,
+            expandedViewManager,
+            bubbleTaskViewFactory,
+            bubblePositioner,
+            bubbleStackView,
+            null /* layerView */,
+            iconFactory,
+            false /* skipInflation */,
+            callback,
+            mainExecutor,
+            bgExecutor
+        )
+    }
+
+    private class TestExecutor : ShellExecutor {
+
+        private val runnables: MutableList<Runnable> = mutableListOf()
+
+        override fun execute(runnable: Runnable) {
+            runnables.add(runnable)
+        }
+
+        override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+            execute(runnable)
+        }
+
+        override fun removeCallbacks(runnable: Runnable?) {}
+
+        override fun hasCallback(runnable: Runnable?): Boolean = false
+
+        fun flushAll() {
+            while (runnables.isNotEmpty()) {
+                runnables.removeAt(0).run()
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 935d129..ecb2b25 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -31,12 +31,12 @@
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner
 import com.android.wm.shell.bubbles.DeviceConfig
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
diff --git a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml
index 5ecba38..a36b21f 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_back_button_dark.xml
@@ -15,6 +15,7 @@
   -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:autoMirrored="true"
         android:width="32.0dp"
         android:height="32.0dp"
         android:viewportWidth="32.0"
diff --git a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
index ab4e29a..7b33534 100644
--- a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
+++ b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
@@ -17,19 +17,16 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="32.0dp"
         android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
-        android:tint="@color/decor_button_dark_color">
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
     <group android:scaleX="0.5"
             android:scaleY="0.5"
-            android:translateX="8.0"
-            android:translateY="8.0" >
+            android:translateX="6.0"
+            android:translateY="6.0" >
         <path
-            android:fillColor="@android:color/white"
-            android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
-        <path
-            android:fillColor="@android:color/white"
-            android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
+            android:fillColor="@android:color/black"
+            android:fillType="evenOdd"
+            android:pathData="M23.0,1.0v22.0H1V1h22zm-3,19H4V4h16v16z"/>
     </group>
 </vector>
 
diff --git a/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml
new file mode 100644
index 0000000..91c8f54
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_restore_button_dark.xml
@@ -0,0 +1,31 @@
+<?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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32.0dp"
+        android:height="32.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <group android:scaleX="0.5"
+            android:scaleY="0.5"
+            android:translateX="6.0"
+            android:translateY="6.0" >
+        <path
+            android:fillColor="@android:color/black"
+            android:fillType="evenOdd"
+            android:pathData="M23,16H8V1h15v15zm-12,-3V4h9v9h-9zM4,8H1v15h15v-3H4V8z"/>
+    </group>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml
new file mode 100644
index 0000000..b35dc02
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_header_ic_minimize.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6,21V19H18V21Z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index a0a06f1..806d026 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
         android:textAlignment="center"
         android:text="@string/bubble_bar_education_manage_text"/>
 
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index b489a5c..7fa586c 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<com.android.wm.shell.common.bubbles.BubblePopupView
+<com.android.wm.shell.shared.bubbles.BubblePopupView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
@@ -53,4 +53,4 @@
         android:textAlignment="center"
         android:text="@string/bubble_bar_education_stack_text"/>
 
-</com.android.wm.shell.common.bubbles.BubblePopupView>
\ No newline at end of file
+</com.android.wm.shell.shared.bubbles.BubblePopupView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/compat_ui_restart_button_layout.xml b/libs/WindowManager/Shell/res/layout/compat_ui_restart_button_layout.xml
new file mode 100644
index 0000000..d00c69c
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/compat_ui_restart_button_layout.xml
@@ -0,0 +1,41 @@
+<?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="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="bottom|end">
+
+    <include android:id="@+id/size_compat_hint"
+        android:visibility="gone"
+        android:layout_width="@dimen/compat_hint_width"
+        android:layout_height="wrap_content"
+        layout="@layout/compat_mode_hint"/>
+
+    <ImageButton
+        android:id="@+id/size_compat_restart_button"
+        android:visibility="gone"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/compat_button_margin"
+        android:layout_marginBottom="@dimen/compat_button_margin"
+        android:src="@drawable/size_compat_restart_button_ripple"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/restart_button_description"/>
+
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
index 7b31c14..7dcb3c2 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
@@ -76,6 +76,18 @@
         android:layout_height="40dp"
         android:layout_weight="1"/>
 
+    <ImageButton
+        android:id="@+id/minimize_window"
+        android:layout_width="44dp"
+        android:layout_height="40dp"
+        android:paddingHorizontal="10dp"
+        android:paddingVertical="8dp"
+        android:layout_marginEnd="8dp"
+        android:contentDescription="@string/minimize_button_text"
+        android:src="@drawable/desktop_mode_header_ic_minimize"
+        android:scaleType="centerCrop"
+        android:gravity="end"/>
+
     <com.android.wm.shell.windowdecor.MaximizeButtonView
         android:id="@+id/maximize_button_view"
         android:layout_width="44dp"
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index e58ff6a..0ed5a72 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Beweeg na regs bo"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Beweeg na links onder"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Beweeg na regs onder"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"vou kieslys uit"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"vou kieslys in"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Skuif links"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Skuif regs"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uit"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"vou <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> in"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-instellings"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Maak borrel toe"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Moenie dat gesprek \'n borrel word nie"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klets met borrels"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nuwe gesprekke verskyn as swerwende ikone, of borrels Tik op borrel om dit oop te maak. Sleep om dit te skuif."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Kies"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skermskoot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Maak in blaaier oop"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string>
     <string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Maak kieslys oop"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gryp skerm vas"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hierdie app se grootte kan nie verander word nie"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 3208ea9..c4d9158 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"የግርጌውን ግራ አንቀሳቅስ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ምናሌን ዘርጋ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ምናሌን ሰብስብ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ወደ ግራ ውሰድ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ወደ ቀኝ ውሰድ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ዘርጋ"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ን ሰብስብ"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"የ<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ቅንብሮች"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"አረፋን አሰናብት"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ውይይቶችን በአረፋ አታሳይ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"አረፋዎችን በመጠቀም ይወያዩ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"አዲስ ውይይቶች እንደ ተንሳፋፊ አዶዎች ወይም አረፋዎች ሆነው ይታያሉ። አረፋን ለመክፈት መታ ያድርጉ። ለመውሰድ ይጎትቱት።"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ምረጥ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ ዕይታ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"በአሳሽ ውስጥ ክፈት"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string>
     <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ማያ ገጹን አሳድግ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ይህ መተግበሪያ መጠኑ ሊቀየር አይችልም"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 18db50e..ced9ee9 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"الانتقال إلى أعلى اليسار"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نقل إلى أسفل يمين الشاشة"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نقل إلى أسفل اليسار"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"توسيع القائمة"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"تصغير القائمة"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"نقل لليسار"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"نقل لليمين"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"توسيع <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"تصغير <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"إعدادات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"إغلاق فقاعة المحادثة"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"عدم عرض المحادثة كفقاعة محادثة"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"الدردشة باستخدام فقاعات المحادثات"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"تظهر المحادثات الجديدة كرموز عائمة أو كفقاعات. انقر لفتح فقاعة المحادثة، واسحبها لتحريكها."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"اختيار"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"لقطة شاشة"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"فتح في المتصفِّح"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string>
     <string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"فتح القائمة"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"التقاط صورة للشاشة"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"لا يمكن تغيير حجم نافذة هذا التطبيق"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 1951772..273d043 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"বুটামটো বাওঁফালে নিয়ক"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"তলৰ সোঁফালে নিয়ক"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"মেনু বিস্তাৰ কৰক"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"মেনু সংকোচন কৰক"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"বাওঁফাললৈ নিয়ক"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"সোঁফাললৈ নিয়ক"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বিস্তাৰ কৰক"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> সংকোচন কৰক"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ছেটিং"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল অগ্ৰাহ্য কৰক"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"বাৰ্তালাপ বাবল নকৰিব"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles ব্যৱহাৰ কৰি চাট কৰক"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন বাৰ্তালাপ উপঙি থকা চিহ্নসমূহ অথবা bubbles হিচাপে প্ৰদর্শিত হয়। Bubbles খুলিবলৈ টিপক। এইটো স্থানান্তৰ কৰিবলৈ টানি নিয়ক।"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"বাছনি কৰক"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"স্ক্ৰীনশ্বট"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ব্ৰাউজাৰত খোলক"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খোলক"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্ৰীন স্নেপ কৰক"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই এপ্‌টোৰ আকাৰ সলনি কৰিব নোৱাৰি"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 32e0dd6..81bb544 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuxarıya sağa köçürün"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Aşağıya sola köçürün"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Aşağıya sağa köçürün"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menyunu genişləndirin"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menyunu yığcamlaşdırın"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sola köçürün"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sağa köçürün"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişləndirin: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"yığcamlaşdırın: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Yumrucuğu ləğv edin"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Söhbəti yumrucuqda göstərmə"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Yumrucuqlardan istifadə edərək söhbət edin"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni söhbətlər üzən nişanlar və ya yumrucuqlar kimi görünür. Yumrucuğu açmaq üçün toxunun. Hərəkət etdirmək üçün sürüşdürün."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seçin"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skrinşot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerdə açın"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string>
     <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menyunu açın"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranı çəkin"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu tətbiqin ölçüsünü dəyişmək olmur"</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 1656e02..898b844 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premesti gore desno"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premesti dole levo"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premesti dole desno"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširi meni"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"skupi meni"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomerite nalevo"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomerite nadesno"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skupite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Podešavanja za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne koristi oblačiće za konverzaciju"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ćaskajte u oblačićima"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nove konverzacije se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da biste otvorili oblačić. Prevucite da biste ga premestili."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Izaberite"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snimak ekrana"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvorite u pregledaču"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvorite meni"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Uklopi ekran"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veličina ove aplikacije ne može da se promeni"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 26f3d3c..c340729 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -57,7 +57,7 @@
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Запусціць рэжым кіравання адной рукой"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Выйсці з рэжыму кіравання адной рукой"</string>
     <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Налады ўсплывальных чатаў у праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
-    <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Дадатковае меню"</string>
+    <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Меню з пашырэннем"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Зноў дадаць у стос"</string>
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
     <string name="bubble_content_description_stack" msgid="8071515017164630429">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" і яшчэ <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перамясціце правей і вышэй"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перамясціць лявей і ніжэй"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перамясціць правей і ніжэй"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"разгарнуць меню"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"згарнуць меню"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Перамясціць улева"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Перамясціць управа"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: разгарнуць"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: згарнуць"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя чаты"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя чаты. Націсніце, каб адкрыць усплывальны чат. Перацягніце яго, каб перамясціць."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Выбраць"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Здымак экрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Адкрыць у браўзеры"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Адкрыць меню"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Размясціць на палавіне экрана"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Немагчыма змяніць памер праграмы"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 7c4f25e..076a815 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Преместване горе вдясно"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Преместване долу вляво"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Преместване долу вдясно"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"разгъване на менюто"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"свиване на менюто"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Преместване наляво"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Преместване надясно"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"разгъване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"свиване на <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Настройки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отхвърляне на балончетата"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Без балончета за разговора"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Чат с балончета"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори се показват като плаващи икони, или балончета. Докоснете балонче, за да го отворите, или го плъзнете, за да го преместите."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Избиране"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Екранна снимка"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отваряне в браузър"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
     <string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Отваряне на менюто"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Прилепване на екрана"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Това приложение не може да бъде преоразмерено"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 4286162..b0d6696 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"উপরে ডানদিকে সরান"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"নিচে বাঁদিকে সরান"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"নিচে ডান দিকে সরান"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"মেনু বড় করে দেখুন"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"মেনু আড়াল করুন"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"বাঁদিকে সরান"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ডানদিকে সরান"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> বড় করুন"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> আড়াল করুন"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> সেটিংস"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"বাবল খারিজ করুন"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"কথোপকথন বাবল হিসেবে দেখাবে না"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"বাবল ব্যবহার করে চ্যাট করুন"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"নতুন কথোপকথন ভেসে থাকা আইকন বা বাবল হিসেবে দেখানো হয়। বাবল খুলতে ট্যাপ করুন। সেটি সরাতে ধরে টেনে আনুন।"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"বেছে নিন"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"স্ক্রিনশট"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ব্রাউজারে খুলুন"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string>
     <string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"মেনু খুলুন"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"স্ক্রিনে অ্যাপ মানানসই হিসেবে ছোট বড় করুন"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"এই অ্যাপ ছোট বড় করা যাবে না"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 11f5e48..0196e5e 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pomjerite gore desno"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pomjeri dolje lijevo"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pomjerite dolje desno"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširivanje menija"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sužavanje menija"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomicanje ulijevo"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomicanje udesno"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširivanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sužavanje oblačića <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke aplikacije <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nemoj prikazivati razgovor u oblačićima"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatajte koristeći oblačiće"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori se prikazuju kao plutajuće ikone ili oblačići. Dodirnite da otvorite oblačić. Prevucite da ga premjestite."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Odabir"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snimak ekrana"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvaranje u pregledniku"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje menija"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snimi ekran"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index e1fc7d3..fa4b627 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mou a dalt a la dreta"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mou a baix a l\'esquerra"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mou a baix a la dreta"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"desplega el menú"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"replega el menú"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mou cap a l\'esquerra"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mou cap a la dreta"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"replega <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuració de l\'aplicació <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora la bombolla"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostris la conversa com a bombolla"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xateja amb bombolles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les converses noves es mostren com a icones flotants o bombolles. Toca per obrir una bombolla. Arrossega-la per moure-la."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selecciona"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Obre al navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string>
     <string name="close_text" msgid="4986518933445178928">"Tanca"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Obre el menú"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajusta la pantalla"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No es pot canviar la mida d\'aquesta aplicació"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index e428210..3956fca 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Přesunout vpravo nahoru"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Přesunout vlevo dolů"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Přesunout vpravo dolů"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozbalit nabídku"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sbalit nabídku"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Přesunout doleva"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Přesunout doprava"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sbalit <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavení <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavřít bublinu"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovat konverzaci v bublinách"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatujte pomocí bublin"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzace se zobrazují jako plovoucí ikony, neboli bubliny. Klepnutím bublinu otevřete. Přetažením ji posunete."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Vybrat"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snímek obrazovky"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otevřít v prohlížeči"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otevřít nabídku"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Rozpůlit obrazovku"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikost aplikace nelze změnit"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 36c0330..afe4a1ab 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flyt op til højre"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flyt ned til venstre"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flyt ned til højre"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"udvid menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"minimer menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flyt til venstre"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flyt til højre"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"udvid <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Indstillinger for <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Afvis boble"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Vis ikke samtaler i bobler"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat ved hjælp af bobler"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som svævende ikoner eller bobler. Tryk for at åbne boblen. Træk for at flytte den."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Vælg"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Åbn i browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string>
     <string name="close_text" msgid="4986518933445178928">"Luk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Åbn menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tilpas skærm"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Størrelsen på denne app kan ikke justeres"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 0ad112c..1e50339 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Nach oben rechts verschieben"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Nach unten links verschieben"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Nach unten rechts verschieben"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"Menü maximieren"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"Menü minimieren"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Nach links bewegen"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Nach rechts bewegen"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> maximieren"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> minimieren"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Einstellungen für <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubble schließen"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Unterhaltung nicht als Bubble anzeigen"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bubbles zum Chatten verwenden"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Neue Unterhaltungen erscheinen als unverankerte Symbole, „Bubbles“ genannt. Wenn du eine Bubble öffnen möchtest, tippe sie an. Wenn du sie verschieben möchtest, zieh an ihr."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Auswählen"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Im Browser öffnen"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string>
     <string name="close_text" msgid="4986518933445178928">"Schließen"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menü öffnen"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Bildschirm teilen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Die Größe dieser App kann nicht geändert werden"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 1e2fec1..5c3c6de 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Μετακίνηση επάνω δεξιά"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Μετακίνηση κάτω αριστερά"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Μετακίνηση κάτω δεξιά"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ανάπτυξη μενού"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"σύμπτυξη μενού"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Μετακίνηση αριστερά"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Μετακίνηση δεξιά"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ανάπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"σύμπτυξη <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Ρυθμίσεις <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Παράβλ. για συννεφ."</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Να μην γίνει προβολή της συζήτησης σε συννεφάκια."</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Συζητήστε χρησιμοποιώντας συννεφάκια."</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Οι νέες συζητήσεις εμφανίζονται ως κινούμενα εικονίδια ή συννεφάκια. Πατήστε για να ανοίξετε το συννεφάκι. Σύρετε για να το μετακινήσετε."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Επιλογή"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Στιγμιότυπο οθόνης"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Άνοιγμα σε πρόγραμμα περιήγησης"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Νέο παράθυρο"</string>
     <string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Άνοιγμα μενού"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Προβολή στο μισό της οθόνης"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Δεν είναι δυνατή η αλλαγή μεγέθους αυτής της εφαρμογής"</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 71701c9..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</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 5ab4af3..f5b0a27 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -65,10 +65,15 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+    <string name="bubble_fullscreen_text" msgid="1006758103218086231">"Move to fullscreen"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
@@ -117,9 +122,11 @@
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"New Window"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open Menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximize Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap Screen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</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 71701c9..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</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 71701c9..51c69e5 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Move top right"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Move bottom left"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Move bottom right"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expand menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"collapse menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Move left"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Move right"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expand <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"collapse <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> settings"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dismiss bubble"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Don’t bubble conversation"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat using bubbles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Select"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"New window"</string>
     <string name="close_text" msgid="4986518933445178928">"Close"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Open menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Snap screen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"This app can\'t be resized"</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 42d5f06..6292be5 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -65,10 +65,15 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎Move top right‎‏‎‎‏‎"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‎Move bottom left‎‏‎‎‏‎"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎Move bottom right‎‏‎‎‏‎"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎expand menu‎‏‎‎‏‎"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‎collapse menu‎‏‎‎‏‎"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎Move left‎‏‎‎‏‎"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎Move right‎‏‎‎‏‎"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‎‎‎‎expand ‎‏‎‎‏‏‎<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎collapse ‎‏‎‎‏‏‎<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>‎‏‎‎‏‏‏‎ settings‎‏‎‎‏‎"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎Dismiss bubble‎‏‎‎‏‎"</string>
+    <string name="bubble_fullscreen_text" msgid="1006758103218086231">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‏‎Move to fullscreen‎‏‎‎‏‎"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎Don’t bubble conversation‎‏‎‎‏‎"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎Chat using bubbles‎‏‎‎‏‎"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎New conversations appear as floating icons, or bubbles. Tap to open bubble. Drag to move it.‎‏‎‎‏‎"</string>
@@ -117,9 +122,11 @@
     <string name="select_text" msgid="5139083974039906583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎Select‎‏‎‎‏‎"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‏‏‏‎Screenshot‎‏‎‎‏‎"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‎‎‏‎‎‎‎Open in browser‎‏‎‎‏‎"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‎New Window‎‏‎‎‏‎"</string>
     <string name="close_text" msgid="4986518933445178928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‎‎‎‎Close‎‏‎‎‏‎"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎Close Menu‎‏‎‎‏‎"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‎‎‎Open Menu‎‏‎‎‏‎"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‏‏‎Maximize Screen‎‏‎‎‏‎"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‎Snap Screen‎‏‎‎‏‎"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎This app can\'t be resized‎‏‎‎‏‎"</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 1e30912..8644780 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ubicar arriba a la derecha"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ubicar abajo a la izquierda"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ubicar abajo a la derecha"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expandir menú"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"contraer menú"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover hacia la izquierda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover hacia la derecha"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Descartar burbuja"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar la conversación en burbuja"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat con burbujas"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como elementos flotantes o burbujas. Presiona para abrir la burbuja. Arrástrala para moverla."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seleccionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir el menú"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta app"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 312e297..9718bf1 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba a la derecha"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abajo a la izquierda."</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abajo a la derecha"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"mostrar menú"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ocultar menú"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover hacia la izquierda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover hacia la derecha"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"desplegar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Ajustes de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cerrar burbuja"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"No mostrar conversación en burbuja"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatea con burbujas"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Las conversaciones nuevas aparecen como iconos flotantes llamados \"burbujas\". Toca una burbuja para abrirla. Arrástrala para moverla."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seleccionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string>
     <string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar pantalla"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"No se puede cambiar el tamaño de esta aplicación"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 5e4ef81..d36a8d1 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Teisalda üles paremale"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Teisalda alla vasakule"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Teisalda alla paremale"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menüü laiendamine"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menüü ahendamine"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Liiguta vasakule"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Liiguta paremale"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laienda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ahenda <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Rakenduse <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> seaded"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Sule mull"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ära kuva vestlust mullina"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Vestelge mullide abil"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uued vestlused kuvatakse hõljuvate ikoonidena ehk mullidena. Puudutage mulli avamiseks. Lohistage mulli, et seda liigutada."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Vali"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekraanipilt"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Avamine brauseris"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string>
     <string name="close_text" msgid="4986518933445178928">"Sule"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ava menüü"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Kuva poolel ekraanil"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Selle rakenduse aknasuurust ei saa muuta"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 40d67a2..2ee086e 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Eraman goialdera, eskuinetara"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Eraman behealdera, ezkerretara"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Eraman behealdera, eskuinetara"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"zabaldu menua"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"tolestu menua"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Eraman ezkerrera"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Eraman eskuinera"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zabaldu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tolestu <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> aplikazioaren ezarpenak"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baztertu burbuila"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ez erakutsi elkarrizketak burbuila gisa"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Txateatu burbuilen bidez"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Elkarrizketa berriak ikono gainerakor edo burbuila gisa agertzen dira. Sakatu burbuila irekitzeko. Arrasta ezazu mugitzeko."</string>
@@ -76,7 +82,7 @@
     <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Aplikazioaren burbuilak desaktibatzeko, sakatu Kudeatu"</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string>
     <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azkenaldiko burbuilak eta baztertutakoak agertuko dira hemen"</string>
     <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Txateatu burbuilak erabilita"</string>
     <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Elkarrizketa berriak ikono gisa agertzen dira pantailaren beheko izkinan. Zabaltzeko, saka itzazu. Baztertzeko, aldiz, arrasta itzazu."</string>
     <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Kontrolatu burbuilak edonoiz"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Hautatu"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Pantaila-argazkia"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ireki arakatzailean"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string>
     <string name="close_text" msgid="4986518933445178928">"Itxi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ireki menua"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zatitu pantaila"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezin zaio aldatu tamaina aplikazio honi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 55da319..f4cdd5f 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"انتقال به بالا سمت چپ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"انتقال به پایین سمت راست"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"انتقال به پایین سمت چپ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ازهم بازکردن منو"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"جمع کردن منو"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"انتقال به‌چپ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"انتقال به‌راست"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ازهم باز کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"جمع کردن <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"تنظیمات <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"رد کردن حبابک"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"مکالمه در حباب نشان داده نشود"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"گپ بااستفاده از حبابک‌ها"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"مکالمه‌های جدید به‌صورت نمادهای شناور یا حبابک‌ها نشان داده می‌شوند. برای باز کردن حبابک‌ها تک‌ضرب بزنید. برای جابه‌جایی، آن را بکشید."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"انتخاب"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"نماگرفت"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"باز کردن در مرورگر"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string>
     <string name="close_text" msgid="4986518933445178928">"بستن"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"باز کردن منو"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"بزرگ کردن صفحه"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اندازه این برنامه را نمی‌توان تغییر داد"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index c2610ff..6be2ee2 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Siirrä oikeaan yläreunaan"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Siirrä vasempaan alareunaan"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Siirrä oikeaan alareunaan"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"laajenna valikko"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"tiivistä valikko"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Siirrä vasemmalle"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Siirrä oikealle"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"laajenna <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"tiivistä <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: asetukset"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ohita kupla"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Älä näytä kuplia keskusteluista"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chattaile kuplien avulla"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Uudet keskustelut näkyvät kelluvina kuvakkeina tai kuplina. Avaa kupla napauttamalla. Siirrä sitä vetämällä."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Valitse"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Kuvakaappaus"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Avaa selaimessa"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string>
     <string name="close_text" msgid="4986518933445178928">"Sulje"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Avaa valikko"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Jaa näyttö"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Tämän sovellusikkunan kokoa ei voi muuttaa"</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 7a59b62..5470099 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"développer le menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"réduire le menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Déplacer vers la gauche"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Déplacer vers la droite"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorer la bulle"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher les conversations dans des bulles"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Clavarder en utilisant des bulles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes (de bulles). Touchez une bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Sélectionner"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans le navigateur"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aligner l\'écran"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 0cf944f..63b5994 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Déplacer en haut à droite"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Déplacer en bas à gauche"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Déplacer en bas à droite"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"développer le menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"réduire le menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Déplacer vers la gauche"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Déplacer vers la droite"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Développer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Réduire <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Paramètres <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Fermer la bulle"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne pas afficher la conversation dans une bulle"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatter en utilisant des bulles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Les nouvelles conversations s\'affichent sous forme d\'icônes flottantes ou de bulles. Appuyez sur la bulle pour l\'ouvrir. Faites-la glisser pour la déplacer."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Sélectionner"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Capture d\'écran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans un navigateur"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
     <string name="close_text" msgid="4986518933445178928">"Fermer"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Ouvrir le menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fractionner l\'écran"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Impossible de redimensionner cette appli"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 0f2a601..36ad521 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover arriba á dereita"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover abaixo á esquerda"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover abaixo á dereita"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"despregar o menú"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"contraer o menú"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover cara á esquerda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover cara á dereita"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"despregar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"contraer <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configuración de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar burbulla"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mostrar a conversa como burbulla"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatear usando burbullas"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"As conversas novas aparecen como iconas flotantes ou burbullas. Toca para abrir a burbulla e arrastra para movela."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seleccionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de pantalla"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string>
     <string name="close_text" msgid="4986518933445178928">"Pechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menú"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar pantalla"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non se pode cambiar o tamaño desta aplicación"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 6151da0..868ef5b 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ઉપર જમણે ખસેડો"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"નીચે ડાબે ખસેડો"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"નીચે જમણે ખસેડો"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"મેનૂ મોટું કરો"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"મેનૂ નાનું કરો"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ડાબે ખસેડો"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"જમણે ખસેડો"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> મોટું કરો"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> નાનું કરો"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> સેટિંગ"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"બબલને છોડી દો"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"વાતચીતને બબલ કરશો નહીં"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"બબલનો ઉપયોગ કરીને ચૅટ કરો"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"નવી વાતચીત ફ્લોટિંગ આઇકન અથવા બબલ જેવી દેખાશે. બબલને ખોલવા માટે ટૅપ કરો. તેને ખસેડવા માટે ખેંચો."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"પસંદ કરો"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"સ્ક્રીનશૉટ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"બ્રાઉઝરમાં ખોલો"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string>
     <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"મેનૂ ખોલો"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"સ્ક્રીન સ્નૅપ કરો"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"આ ઍપના કદમાં વધઘટ કરી શકાતો નથી"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 04e76bc..31c7307 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सबसे ऊपर दाईं ओर ले जाएं"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"बाईं ओर सबसे नीचे ले जाएं"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"सबसे नीचे दाईं ओर ले जाएं"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेन्यू बड़ा करें"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेन्यू छोटा करें"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"बाईं ओर ले जाएं"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"दाईं ओर ले जाएं"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को बड़ा करें"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> को छोटा करें"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> की सेटिंग"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारिज करें"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"बातचीत को बबल न करें"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल्स का इस्तेमाल करके चैट करें"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"नई बातचीत फ़्लोटिंग आइकॉन या बबल्स की तरह दिखेंगी. बबल को खोलने के लिए टैप करें. इसे एक जगह से दूसरी जगह ले जाने के लिए खींचें और छोड़ें."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"चुनें"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रीनशॉट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउज़र में खोलें"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"मेन्यू खोलें"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्नैप स्क्रीन"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"इस ऐप्लिकेशन का साइज़ नहीं बदला जा सकता"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index aa2ee17..d99a65d8 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premjesti u gornji desni kut"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premjesti u donji lijevi kut"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premjestite u donji desni kut"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"proširi izbornik"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sažmi izbornik"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pomakni ulijevo"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pomakni udesno"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"proširite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sažmite oblačić <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Postavke za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Odbaci oblačić"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Zaustavi razgovor u oblačićima"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Oblačići u chatu"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi razgovori pojavljuju se kao pomične ikone ili oblačići. Dodirnite za otvaranje oblačića. Povucite da biste ga premjestili."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Odaberite"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snimka zaslona"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvori u pregledniku"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
     <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvaranje izbornika"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Izradi snimku zaslona"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nije moguće promijeniti veličinu aplikacije"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 8ffeeed..bed760e 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Áthelyezés fel és jobbra"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Áthelyezés le és balra"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Áthelyezés le és jobbra"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menü kibontása"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menü összecsukása"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mozgatás balra"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mozgatás jobbra"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> kibontása"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> összecsukása"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> beállításai"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Buborék elvetése"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ne jelenjen meg a beszélgetés buborékban"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Buborékokat használó csevegés"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Az új beszélgetések lebegő ikonként, vagyis buborékként jelennek meg. A buborék megnyitásához koppintson rá. Áthelyezéshez húzza a kívánt helyre."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Kiválasztás"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Képernyőkép"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Megnyitás böngészőben"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string>
     <string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menü megnyitása"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Igazodás a képernyő adott részéhez"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ezt az alkalmazást nem lehet átméretezni"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index b3fccfa..fcb7254 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Տեղափոխել վերև՝ աջ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Տեղափոխել ներքև՝ ձախ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Տեղափոխել ներքև՝ աջ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ծավալել ընտրացանկը"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ծալել ընտրացանկը"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Տեղափոխել ձախ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Տեղափոխել աջ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծավալել"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>. ծալել"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – կարգավորումներ"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Փակել ամպիկը"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Զրույցը չցուցադրել ամպիկի տեսքով"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Զրույցի ամպիկներ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Նոր զրույցները կհայտնվեն լողացող պատկերակների կամ ամպիկների տեսքով։ Հպեք՝ ամպիկը բացելու համար։ Քաշեք՝ այն տեղափոխելու համար։"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Ընտրել"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Սքրինշոթ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Բացել դիտարկիչում"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string>
     <string name="close_text" msgid="4986518933445178928">"Փակել"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Բացել ընտրացանկը"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ծալել էկրանը"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Այս հավելվածի չափը հնարավոր չէ փոխել"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 38b24f9..85a9bbf 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pindahkan ke kanan atas"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pindahkan ke kiri bawah"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pindahkan ke kanan bawah"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"luaskan menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ciutkan menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pindahkan ke kiri"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pindahkan ke kanan"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"luaskan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ciutkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Setelan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Tutup balon"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan gunakan percakapan balon"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat dalam tampilan balon"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Percakapan baru muncul sebagai ikon mengambang, atau balon. Ketuk untuk membuka balon. Tarik untuk memindahkannya."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Pilih"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buka di browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Gabungkan Layar"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ukuran aplikasi ini tidak dapat diubah"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 5b1f4d2..8041162 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Færa efst til hægri"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Færa neðst til vinstri"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Færðu neðst til hægri"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"stækka valmynd"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"draga saman valmynd"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Færa til vinstri"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Færa til hægri"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"stækka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"minnka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Stillingar <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Loka blöðru"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ekki setja samtal í blöðru"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Spjalla með blöðrum"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Ný samtöl birtast sem fljótandi tákn eða blöðrur. Ýttu til að opna blöðru. Dragðu hana til að færa."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Velja"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skjámynd"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Opna í vafra"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string>
     <string name="close_text" msgid="4986518933445178928">"Loka"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Opna valmynd"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Smelluskjár"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Ekki er hægt að breyta stærð þessa forrits"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index adc9569..3ba6873 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sposta in alto a destra"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sposta in basso a sinistra"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sposta in basso a destra"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"espandi menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"comprimi menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sposta a sinistra"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sposta a destra"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"espandi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"comprimi <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Impostazioni <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignora bolla"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Non mettere la conversazione nella bolla"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta utilizzando le bolle"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Le nuove conversazioni vengono mostrate come icone mobili o bolle. Tocca per aprire la bolla. Trascinala per spostarla."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seleziona"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Apri nel browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string>
     <string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Apri menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Aggancia schermo"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Non è possibile ridimensionare questa app"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index 9bae1c9..e1854fa1 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"העברה לפינה הימנית העליונה"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"העברה לפינה השמאלית התחתונה"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"העברה לפינה הימנית התחתונה"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"הרחבת התפריט"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"כיווץ התפריט"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"הזזה שמאלה"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"הזזה ימינה"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"הרחבה של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"כיווץ של <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"הגדרות <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"סגירת בועה"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"אין להציג בועות לשיחה"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"לדבר בבועות"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"שיחות חדשות מופיעות כסמלים צפים, או בועות. יש להקיש כדי לפתוח בועה. יש לגרור כדי להזיז אותה."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"בחירה"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"צילום מסך"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"פתיחה בדפדפן"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string>
     <string name="close_text" msgid="4986518933445178928">"סגירה"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"פתיחת התפריט"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"כיווץ המסך"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"לא ניתן לשנות את גודל החלון של האפליקציה הזו"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index fff794e..1f1ddc7 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"右上に移動"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"左下に移動"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"右下に移動"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"メニューを開く"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"メニューを閉じる"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"左に移動"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"右に移動"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を開きます"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>を閉じます"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> の設定"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"バブルを閉じる"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"会話をバブルで表示しない"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"チャットでバブルを使う"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"新しい会話はフローティング アイコン(バブル)として表示されます。タップするとバブルが開きます。ドラッグしてバブルを移動できます。"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"選択"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"スクリーンショット"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ブラウザで開く"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string>
     <string name="close_text" msgid="4986518933445178928">"閉じる"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"メニューを開く"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"画面のスナップ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"このアプリはサイズ変更できません"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index a73c4f0..e201a20 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"გადაანაცვლეთ ზევით და მარჯვნივ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ქვევით და მარცხნივ გადატანა"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"მენიუს გაფართოება"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"მენიუს ჩაკეცვა"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"მარცხნივ გადატანა"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"მარჯვნივ გადატანა"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის გაფართოება"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-ის ჩაკეცვა"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-ის პარამეტრები"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ბუშტის დახურვა"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"აიკრძალოს საუბრის ბუშტები"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ჩეთი ბუშტების გამოყენებით"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ახალი საუბრები გამოჩნდება როგორც მოტივტივე ხატულები ან ბუშტები. შეეხეთ ბუშტის გასახსნელად. გადაიტანეთ ჩავლებით."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"არჩევა"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ეკრანის ანაბეჭდი"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ბრაუზერში გახსნა"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string>
     <string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"მენიუს გახსნა"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"აპლიკაციის დაპატარავება ეკრანზე"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"აპის ზომის შეცვლა შეუძლებელია"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 5a7197e..1c335dd 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жоғары оң жаққа жылжыту"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төменгі сол жаққа жылжыту"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төменгі оң жаққа жылжыту"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"мәзірді жаю"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"мәзірді жию"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Солға жылжыту"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Оңға жылжыту"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жаю"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>: жию"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлері"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Қалқымалы хабарды жабу"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Әңгіменің қалқыма хабары көрсетілмесін"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Қалқыма хабарлар арқылы сөйлесу"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңа әңгімелер қалқыма белгішелер немесе хабарлар түрінде көрсетіледі. Қалқыма хабарды ашу үшін түртіңіз. Жылжыту үшін сүйреңіз."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Таңдау"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Браузерден ашу"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Мәзірді ашу"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды бөлу"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бұл қолданбаның өлшемі өзгертілмейді."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 4db7aea..d0cceee 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ផ្លាស់ទីទៅផ្នែកខាងលើខាងស្ដាំ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង​"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ផ្លាស់ទីទៅផ្នែកខាងក្រោម​ខាងស្ដាំ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ពង្រីក​ម៉ឺនុយ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"បង្រួម​ម៉ឺនុយ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ផ្លាស់ទី​ទៅ​ឆ្វេង"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ផ្លាស់ទីទៅ​ស្តាំ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ពង្រីក <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"បង្រួម <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"ការកំណត់ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ច្រានចោល​ពពុះ"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"កុំបង្ហាញ​ការសន្ទនា​ជាពពុះ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ជជែក​ដោយប្រើ​ពពុះ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ការសន្ទនាថ្មីៗ​បង្ហាញជា​​ពពុះ ឬរូបអណ្ដែត។ ចុច ដើម្បីបើក​ពពុះ។ អូស ដើម្បី​ផ្លាស់ទី​ពពុះនេះ។"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ជ្រើសរើស"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"រូបថតអេក្រង់"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"បើកក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"វិនដូ​ថ្មី"</string>
     <string name="close_text" msgid="4986518933445178928">"បិទ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"បើកម៉ឺនុយ"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ថតអេក្រង់"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"មិនអាចប្ដូរទំហំ​កម្មវិធីនេះ​បានទេ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 5615afc..63b5c68 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -38,16 +38,16 @@
     <string name="activity_launch_on_secondary_display_failed_text" msgid="4226485344988071769">"ಸೆಕೆಂಡರಿ ಡಿಸ್‌ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾರಂಭಿಸುವಿಕೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ."</string>
     <string name="accessibility_divider" msgid="6407584574218956849">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string>
     <string name="divider_title" msgid="1963391955593749442">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್ ಡಿವೈಡರ್"</string>
-    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಪೂರ್ಣ ಪರದೆ"</string>
+    <string name="accessibility_action_divider_left_full" msgid="1792313656305328536">"ಎಡ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="accessibility_action_divider_left_70" msgid="8859845045360659250">"70% ಎಡಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"50% ಎಡಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"30% ಎಡಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಪೂರ್ಣ ಪರದೆ"</string>
-    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಪೂರ್ಣ ಪರದೆ"</string>
+    <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"ಬಲ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
+    <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"ಮೇಲಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"70% ಮೇಲಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"50% ಮೇಲಕ್ಕೆ"</string>
     <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"30% ಮೇಲಕ್ಕೆ"</string>
-    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಪೂರ್ಣ ಪರದೆ"</string>
+    <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"ಕೆಳಗಿನ ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="accessibility_split_left" msgid="1713683765575562458">"ಎಡಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
     <string name="accessibility_split_right" msgid="8441001008181296837">"ಬಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
     <string name="accessibility_split_top" msgid="2789329702027147146">"ಮೇಲಕ್ಕೆ ವಿಭಜಿಸಿ"</string>
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ಸ್ಕ್ರೀನ್‌ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ಮೆನು ವಿಸ್ತರಿಸಿ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ಮೆನು ಸಂಕುಚಿಸಿ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ಬಬಲ್ ವಜಾಗೊಳಿಸಿ"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ಸಂಭಾಷಣೆಯನ್ನು ಬಬಲ್ ಮಾಡಬೇಡಿ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ಬಬಲ್ಸ್ ಬಳಸಿ ಚಾಟ್ ಮಾಡಿ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ಹೊಸ ಸಂಭಾಷಣೆಗಳು ತೇಲುವ ಐಕಾನ್‌ಗಳು ಅಥವಾ ಬಬಲ್ಸ್ ಆಗಿ ಗೋಚರಿಸುತ್ತವೆ. ಬಬಲ್ ತೆರೆಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಅದನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಲು ಎಳೆಯಿರಿ."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ಬ್ರೌಸರ್‌ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ಮೆನು ತೆರೆಯಿರಿ"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ಸ್ನ್ಯಾಪ್ ಸ್ಕ್ರೀನ್"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಗಾತ್ರಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
index 3dfe573..efb7930 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings_tv.xml
@@ -20,7 +20,7 @@
     <string name="notification_channel_tv_pip" msgid="2576686079160402435">"ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರ"</string>
     <string name="pip_notification_unknown_title" msgid="2729870284350772311">"(ಶೀರ್ಷಿಕೆ ರಹಿತ ಕಾರ್ಯಕ್ರಮ)"</string>
     <string name="pip_close" msgid="2955969519031223530">"ಮುಚ್ಚಿರಿ"</string>
-    <string name="pip_fullscreen" msgid="7278047353591302554">"ಪೂರ್ಣ ಪರದೆ"</string>
+    <string name="pip_fullscreen" msgid="7278047353591302554">"ಫುಲ್ ಸ್ಕ್ರೀನ್"</string>
     <string name="pip_move" msgid="158770205886688553">"ಸರಿಸಿ"</string>
     <string name="pip_expand" msgid="1051966011679297308">"ವಿಸ್ತೃತಗೊಳಿಸಿ"</string>
     <string name="pip_collapse" msgid="3903295106641385962">"ಕುಗ್ಗಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 33b980d..b5efd10 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"오른쪽 상단으로 이동"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"왼쪽 하단으로 이동"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"오른쪽 하단으로 이동"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"메뉴 펼치기"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"메뉴 접기"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"왼쪽으로 이동"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"오른쪽으로 이동"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 펼치기"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> 접기"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> 설정"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"대화창 닫기"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"대화를 대화창으로 표시하지 않기"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"대화창으로 채팅하기"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"새로운 대화가 플로팅 아이콘인 대화창으로 표시됩니다. 대화창을 열려면 탭하세요. 드래그하여 이동할 수 있습니다."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"선택"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"스크린샷"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"브라우저에서 열기"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"새 창"</string>
     <string name="close_text" msgid="4986518933445178928">"닫기"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"메뉴 열기"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"화면 분할"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"이 앱은 크기를 조절할 수 없습니다."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index bf3ca52..e001efe 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Жогорку оң жакка жылдыруу"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Төмөнкү сол жакка жылдыруу"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Төмөнкү оң жакка жылдыруу"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"менюну жайып көрсөтүү"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"менюну жыйыштыруу"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Солго жылдыруу"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Оңго жылдыруу"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жайып көрсөтүү"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> жыйыштыруу"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> параметрлери"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Калкып чыкма билдирмени жабуу"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Жазышууда калкып чыкма билдирмелер көрүнбөсүн"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Калкып чыкма билдирмелер аркылуу маектешүү"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Жаңы жазышуулар калкыма сүрөтчөлөр же калкып чыкма билдирмелер түрүндө көрүнөт. Калкып чыкма билдирмелерди ачуу үчүн тийип коюңуз. Жылдыруу үчүн сүйрөңүз."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Тандоо"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Серепчиден ачуу"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string>
     <string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Менюну ачуу"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Экранды сүрөткө тартып алуу"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Бул колдонмонун өлчөмүн өзгөртүүгө болбойт"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index ac6fa00..029c95b 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ຍ້າຍຂວາເທິງ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ຍ້າຍຂວາລຸ່ມ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ຂະຫຍາຍເມນູ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ຫຍໍ້ເມນູລົງ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ຍ້າຍໄປທາງຊ້າຍ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ຍ້າຍໄປທາງຂວາ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ຂະຫຍາຍ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ຫຍໍ້ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ລົງ"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"ການຕັ້ງຄ່າ <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ປິດຟອງໄວ້"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ຢ່າໃຊ້ຟອງໃນການສົນທະນາ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ສົນທະນາໂດຍໃຊ້ຟອງ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ການສົນທະນາໃໝ່ຈະປາກົດເປັນໄອຄອນ ຫຼື ຟອງແບບລອຍ. ແຕະເພື່ອເປີດຟອງ. ລາກເພື່ອຍ້າຍມັນ."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ເລືອກ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ຮູບໜ້າຈໍ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ເປີດໃນໂປຣແກຣມທ່ອງເວັບ"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ໜ້າຈໍໃໝ່"</string>
     <string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ເປີດເມນູ"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ສະແນັບໜ້າຈໍ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ບໍ່ສາມາດປັບຂະໜາດແອັບນີ້ໄດ້"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 97b53d3..791ddcd 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Perkelti į viršų dešinėje"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Perkelti į apačią kairėje"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Perkelti į apačią dešinėje"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"išskleisti meniu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sutraukti meniu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Perkelti kairėn"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Perkelti dešinėn"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"išskleisti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"sutraukti „<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>“"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"„<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>“ nustatymai"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Atsisakyti burbulo"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerodyti pokalbio burbule"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Pokalbis naudojant burbulus"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nauji pokalbiai rodomi kaip slankiosios piktogramos arba burbulai. Palieskite, kad atidarytumėte burbulą. Vilkite, kad perkeltumėte."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Pasirinkti"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrano kopija"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Atidaryti naršyklėje"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string>
     <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Atidaryti meniu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Sutraukti ekraną"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Negalima keisti šios programos dydžio"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 3a7e90a..8a86687 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Pārvietot augšpusē pa labi"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Pārvietot apakšpusē pa kreisi"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Pārvietot apakšpusē pa labi"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"izvērst izvēlni"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"sakļaut izvēlni"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Pārvietot pa kreisi"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Pārvietot pa labi"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Izvērst “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Sakļaut “<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Lietotnes <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> iestatījumi"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Nerādīt burbuli"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nerādīt sarunu burbuļos"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Tērzēšana, izmantojot burbuļus"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Jaunas sarunas tiek rādītas kā peldošas ikonas vai burbuļi. Pieskarieties, lai atvērtu burbuli. Velciet, lai to pārvietotu."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Atlasīt"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekrānuzņēmums"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Atvērt pārlūkā"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string>
     <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Atvērt izvēlni"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fiksēt ekrānu"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Šīs lietotnes loga lielumu nevar mainīt."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index f7867ce..3a6b2f0 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести долу лево"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести долу десно"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"го проширува менито"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"го собира менито"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Преместете налево"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Преместете надесно"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"прошири <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"собери <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Поставки за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Отфрли балонче"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не прикажувај го разговорот во балончиња"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Разговор во балончиња"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новите разговори ќе се појавуваат како лебдечки икони или балончиња. Допрете за отворање на балончето. Повлечете за да го преместите."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Изберете"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Слика од екранот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отвори во прелистувач"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворете"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Отвори го менито"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Подели го екранот на половина"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Не може да се промени големината на апликацијава"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 68ff074..26e4a46 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"മെനു വികസിപ്പിക്കുക"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"മെനു ചുരുക്കുക"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ഇടത്തേക്ക് നീക്കുക"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"വലത്തേക്ക് നീക്കുക"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ചുരുക്കുക"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ക്രമീകരണം"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ബബിൾ ഡിസ്മിസ് ചെയ്യൂ"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"സംഭാഷണം ബബിൾ ചെയ്യരുത്"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ബബിളുകൾ ഉപയോഗിച്ച് ചാറ്റ് ചെയ്യുക"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"പുതിയ സംഭാഷണങ്ങൾ ഫ്ലോട്ടിംഗ് ഐക്കണുകളോ ബബിളുകളോ ആയി ദൃശ്യമാവുന്നു. ബബിൾ തുറക്കാൻ ടാപ്പ് ചെയ്യൂ. ഇത് നീക്കാൻ വലിച്ചിടുക."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"തിരഞ്ഞെടുക്കുക"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"സ്ക്രീൻഷോട്ട്"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ബ്രൗസറിൽ തുറക്കുക"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്‍ഡോ"</string>
     <string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"മെനു തുറക്കുക"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"സ്‌ക്രീൻ സ്‌നാപ്പ് ചെയ്യുക"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ഈ ആപ്പിന്റെ വലുപ്പം മാറ്റാനാകില്ല"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 27b38bc..505a4ad 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Баруун дээш зөөх"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Зүүн доош зөөх"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Баруун доош зөөх"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"цэсийг дэлгэх"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"цэсийг хураах"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Зүүн тийш зөөх"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Баруун тийш зөөх"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г дэлгэх"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>-г хураах"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-н тохиргоо"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Бөмбөлгийг хаах"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Харилцан яриаг бүү бөмбөлөг болго"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Бөмбөлөг ашиглан чатлаарай"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Шинэ харилцан яриа нь хөвөгч дүрс тэмдэг эсвэл бөмбөлөг хэлбэрээр харагддаг. Бөмбөлгийг нээхийн тулд товшино уу. Түүнийг зөөхийн тулд чирнэ үү."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Сонгох"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Дэлгэцийн агшин"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Хөтчид нээх"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string>
     <string name="close_text" msgid="4986518933445178928">"Хаах"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Цэс нээх"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Дэлгэцийг таллах"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Энэ аппын хэмжээг өөрчлөх боломжгүй"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index f57f063..cc35f11 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"वर उजवीकडे हलवा"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"तळाशी डावीकडे हलवा"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"तळाशी उजवीकडे हलवा"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेनूचा विस्तार करा"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेनू कोलॅप्स करा"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"डावीकडे हलवा"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"उजवीकडे हलवा"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> विस्तार करा"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोलॅप्स करा"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> सेटिंग्ज"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल डिसमिस करा"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"संभाषणाला बबल करू नका"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबल वापरून चॅट करा"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"नवीन संभाषणे फ्लोटिंग आयकन किंवा बबल म्हणून दिसतात. बबल उघडण्यासाठी टॅप करा. हे हलवण्यासाठी ड्रॅग करा."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"निवडा"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रीनशॉट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउझरमध्ये उघडा"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string>
     <string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"मेनू उघडा"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रीन स्नॅप करा"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"या अ‍ॅपचा आकार बदलला जाऊ शकत नाही"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 9c3b871..61d6614 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Alihkan ke atas sebelah kanan"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Alihkan ke bawah sebelah kiri"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Alihkan ke bawah sebelah kanan"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"kembangkan menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"kuncupkan menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Alih ke kiri"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Alih ke kanan"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"kembangkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kuncupkan <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Tetapan <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ketepikan gelembung"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Jangan jadikan perbualan dalam bentuk gelembung"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bersembang menggunakan gelembung"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Perbualan baharu muncul sebagai ikon terapung atau gelembung. Ketik untuk membuka gelembung. Seret untuk mengalihkan gelembung tersebut."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Pilih"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Tangkapan skrin"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buka dalam penyemak imbas"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string>
     <string name="close_text" msgid="4986518933445178928">"Tutup"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Buka Menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Tangkap Skrin"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Apl ini tidak boleh diubah saiz"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 2685c56..7841e07 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ညာဘက်ထိပ်သို့ ရွှေ့ပါ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"မီနူးကို ပိုပြပါ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"မီနူးကို လျှော့ပြပါ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ဘယ်သို့ရွှေ့ရန်"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ညာသို့ရွှေ့ရန်"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချဲ့ရန်"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ကို ချုံ့ရန်"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ဆက်တင်များ"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ပူဖောင်းကွက် ပယ်ရန်"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"စကားဝိုင်းကို ပူဖောင်းကွက် မပြုလုပ်ပါနှင့်"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ပူဖောင်းကွက် သုံး၍ ချတ်လုပ်ခြင်း"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"စကားဝိုင်းအသစ်များကို မျောနေသည့် သင်္ကေတများ သို့မဟုတ် ပူဖောင်းကွက်များအဖြစ် မြင်ရပါမည်။ ပူဖောင်းကွက်ကိုဖွင့်ရန် တို့ပါ။ ရွှေ့ရန် ၎င်းကို ဖိဆွဲပါ။"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ရွေးရန်"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ဘရောင်ဇာတွင် ဖွင့်ရန်"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string>
     <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"မီနူး ဖွင့်ရန်"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"စခရင်ကို ချုံ့မည်"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ဤအက်ပ်ကို အရွယ်ပြင်၍ မရပါ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 36c0b8b..ea2ae1b 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytt til øverst til høyre"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytt til nederst til venstre"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytt til nederst til høyre"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"vis menyen"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"skjul menyen"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flytt til venstre"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flytt til høyre"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"vis <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"skjul <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>-innstillinger"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Lukk boblen"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ikke vis samtaler i bobler"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat med bobler"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nye samtaler vises som flytende ikoner eller bobler. Trykk for å åpne en boble. Dra for å flytte den."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Velg"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skjermbilde"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Åpne i nettleseren"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string>
     <string name="close_text" msgid="4986518933445178928">"Lukk"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Åpne menyen"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fest skjermen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Du kan ikke endre størrelse på denne appen"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 4e96f5f5..a3bd5ee 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"सिरानमा दायाँतिर सार्नुहोस्"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"पुछारमा बायाँतिर सार्नुहोस्"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"पुछारमा दायाँतिर सार्नुहोस्"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"मेनु एक्स्पान्ड गर्नुहोस्"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"मेनु कोल्याप्स गर्नुहोस्"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"बायाँतिर सार्नुहोस्"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"दायाँतिर सार्नुहोस्"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> का सेटिङहरू"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"बबल खारेज गर्नुहोस्"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"वार्तालाप बबलको रूपमा नदेखाउनुहोस्"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"बबलहरू प्रयोग गरी कुराकानी गर्नुहोस्"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"नयाँ वार्तालापहरू तैरने आइकन वा बबलका रूपमा देखिन्छन्। बबल खोल्न ट्याप गर्नुहोस्। बबल सार्न सो बबललाई ड्र्याग गर्नुहोस्।"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"चयन गर्नुहोस्"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"स्क्रिनसट"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउजरमा खोल्नुहोस्"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string>
     <string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"मेनु खोल्नुहोस्"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"स्क्रिन स्न्याप गर्नुहोस्"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"यो एपको आकार बदल्न मिल्दैन"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 5ae084a..ad2c60f 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Naar rechtsboven verplaatsen"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Naar linksonder verplaatsen"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Naar rechtsonder verplaatsen"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menu uitvouwen"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menu samenvouwen"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Naar links verplaatsen"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Naar rechts verplaatsen"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> uitvouwen"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> samenvouwen"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Instellingen voor <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bubbel sluiten"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Gesprekken niet in bubbels tonen"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatten met bubbels"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nieuwe gesprekken worden als zwevende iconen of bubbels getoond. Tik om een bubbel te openen. Sleep om een bubbel te verplaatsen."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selecteren"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Openen in browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nieuw venster"</string>
     <string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menu openen"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Scherm halveren"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Het formaat van deze app kan niet worden aangepast"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 536c6fe..76f2715 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ଉପର-ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ମେନୁକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ମେନୁକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ବାମକୁ ମୁଭ କରନ୍ତୁ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ଡାହାଣକୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ସେଟିଂସ୍"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ବବଲ୍ ଖାରଜ କରନ୍ତୁ"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ବାର୍ତ୍ତାଳାପକୁ ବବଲ୍ କରନ୍ତୁ ନାହିଁ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ବବଲଗୁଡ଼ିକୁ ବ୍ୟବହାର କରି ଚାଟ୍ କରନ୍ତୁ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ନୂଆ ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ ଫ୍ଲୋଟିଂ ଆଇକନ୍ କିମ୍ବା ବବଲ୍ ଭାବେ ଦେଖାଯିବ। ବବଲ୍ ଖୋଲିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଏହାକୁ ମୁଭ୍ କରିବାକୁ ଟାଣନ୍ତୁ।"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ଚୟନ କରନ୍ତୁ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ସ୍କ୍ରିନସଟ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ମେନୁ ଖୋଲନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ସ୍କ୍ରିନକୁ ସ୍ନାପ କରନ୍ତୁ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ଏହି ଆପକୁ ରିସାଇଜ କରାଯାଇପାରିବ ନାହିଁ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index bdef224..cd7fd47 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ਮੀਨੂ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ਮੀਨੂ ਨੂੰ ਸਮੇਟੋ"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ਸੱਜੇ ਲਿਜਾਓ"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ਸੈਟਿੰਗਾਂ"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕਰੋ"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ਗੱਲਬਾਤ \'ਤੇ ਬਬਲ ਨਾ ਲਾਓ"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"ਬਬਲ ਵਰਤਦੇ ਹੋਏ ਚੈਟ ਕਰੋ"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"ਨਵੀਆਂ ਗੱਲਾਂਬਾਤਾਂ ਫਲੋਟਿੰਗ ਪ੍ਰਤੀਕਾਂ ਜਾਂ ਬਬਲ ਦੇ ਰੂਪ ਵਿੱਚ ਦਿਸਦੀਆਂ ਹਨ। ਬਬਲ ਨੂੰ ਖੋਲ੍ਹਣ ਲਈ ਟੈਪ ਕਰੋ। ਇਸਨੂੰ ਲਿਜਾਣ ਲਈ ਘਸੀਟੋ।"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ਚੁਣੋ"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string>
     <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ਸਕ੍ਰੀਨ ਨੂੰ ਸਨੈਪ ਕਰੋ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ਇਸ ਐਪ ਦਾ ਆਕਾਰ ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 41c4bb0..d33b6f1 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Przenieś w prawy górny róg"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Przenieś w lewy dolny róg"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Przenieś w prawy dolny róg"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozwiń menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"zwiń menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Przenieś w lewo"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Przenieś w prawo"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zwiń dymek <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> – ustawienia"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zamknij dymek"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nie wyświetlaj rozmowy jako dymka"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Czatuj, korzystając z dymków"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nowe rozmowy będą wyświetlane jako pływające ikony lub dymki. Kliknij, by otworzyć dymek. Przeciągnij, by go przenieść."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Wybierz"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Zrzut ekranu"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otwórz w przeglądarce"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string>
     <string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otwórz menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Przyciągnij ekran"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Nie można zmienić rozmiaru tej aplikacji"</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 83d34c0..c7a00ff 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"abrir menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"fechar menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selecionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de tela"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</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 425ad05..d141447 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover p/ parte sup. direita"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover p/ parte infer. esquerda"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover p/ parte inf. direita"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"expandir menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"reduzir menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"expandir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"reduzir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Definições de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ignorar balão"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não apresentar a conversa em balões"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse no chat através de balões"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"As novas conversas aparecem como ícones flutuantes ou balões. Toque para abrir o balão. Arraste para o mover."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selecionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de ecrã"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Encaixar ecrã"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar esta app"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 83d34c0..c7a00ff 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mover para canto superior direito"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mover para canto inferior esquerdo"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mover para canto inferior direito"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"abrir menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"fechar menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Mover para a esquerda"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Mover para a direita"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"abrir <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"fechar <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Configurações de <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Dispensar balão"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Não criar balões de conversa"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Converse usando balões"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novas conversas aparecerão como ícones flutuantes, ou balões. Toque para abrir o balão. Arraste para movê-lo."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selecionar"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captura de tela"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
     <string name="close_text" msgid="4986518933445178928">"Fechar"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Abrir o menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ajustar tela"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Não é possível redimensionar o app"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index ea1a6b7..9bc7660 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"extinde meniul"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"restrânge meniul"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Deplasează spre stânga"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Deplasează spre dreapta"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"extinde <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"restrânge <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișa conversația în balon"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atinge pentru a deschide balonul. Trage pentru a-l muta."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Selectează"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Captură de ecran"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Deschide în browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string>
     <string name="close_text" msgid="4986518933445178928">"Închide"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Deschide meniul"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Micșorează fereastra și fixeaz-o"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Aplicația nu poate fi redimensionată"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index d0292be..044e3b02 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Переместить в правый верхний угол"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Переместить в левый нижний угол"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Переместить в правый нижний угол"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"развернуть меню"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"свернуть меню"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Переместить влево"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Переместить вправо"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"Развернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"Свернуть <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>: настройки"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Скрыть всплывающий чат"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показывать всплывающий чат для разговора"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Всплывающие чаты"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новые разговоры будут появляться в виде плавающих значков, или всплывающих чатов. Чтобы открыть чат, нажмите на него, а чтобы переместить – перетащите."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Выбрать"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Скриншот"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Открыть в браузере"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Открыть меню"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Свернуть"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Изменить размер приложения нельзя."</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 7994222..da2541e 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ඉහළ දකුණට ගෙන යන්න"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"පහළ වමට ගෙන යන්න"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"පහළ දකුණට ගෙන යන්න"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"මෙනුව දිග හරින්න"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"මෙනුව හකුළන්න"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"වමට ගෙන යන්න"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"දකුණට ගෙන යන්න"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> දිග හරින්න"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> හකුළන්න"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> සැකසීම්"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"බුබුලු ඉවත ලන්න"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"සංවාදය බුබුලු නොදමන්න"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"බුබුලු භාවිතයෙන් කතාබහ කරන්න"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"නව සංවාද පාවෙන අයිකන හෝ බුබුලු ලෙස දිස් වේ. බුබුල විවෘත කිරීමට තට්ටු කරන්න. එය ගෙන යාමට අදින්න."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"තෝරන්න"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"තිර රුව"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"බ්‍රව්සරයේ විවෘත කරන්න"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string>
     <string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"මෙනුව විවෘත කරන්න"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"ස්නැප් තිරය"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"මෙම යෙදුම ප්‍රතිප්‍රමාණ කළ නොහැක"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 896a85b..394a4ca 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Presunúť doprava nahor"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Presunúť doľava nadol"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Presunúť doprava nadol"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"rozbaliť ponuku"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"zbaliť ponuku"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Posunúť doľava"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Posunúť doprava"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"rozbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"zbaliť <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavenia aplikácie <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Zavrieť bublinu"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nezobrazovať konverzáciu ako bublinu"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Čet pomocou bublín"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nové konverzácie sa zobrazujú ako plávajúce ikony či bubliny. Bublinu otvoríte klepnutím. Premiestnite ju presunutím."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Vybrať"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Snímka obrazovky"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Otvoriť v prehliadači"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
     <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Otvoriť ponuku"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Zobraziť polovicu obrazovky"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Veľkosť tejto aplikácie sa nedá zmeniť"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 3418c48..a90c2c2 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Premakni zgoraj desno"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Premakni spodaj levo"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Premakni spodaj desno"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"razširi meni"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"strni meni"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Premakni levo"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Premakni desno"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"razširitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"strnitev oblačka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Nastavitve za <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Opusti oblaček"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Pogovora ne prikaži v oblačku"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Klepet z oblački"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Novi pogovori so prikazani kot lebdeče ikone ali oblački. Če želite odpreti oblaček, se ga dotaknite. Če ga želite premakniti, ga povlecite."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Izberi"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Posnetek zaslona"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Odpri v brskalniku"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string>
     <string name="close_text" msgid="4986518933445178928">"Zapri"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Odpri meni"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Pripni zaslon"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Velikosti te aplikacije ni mogoče spremeniti"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 306fcaa..706e75f 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Lëviz lart djathtas"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Zhvendos poshtë majtas"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Lëvize poshtë djathtas"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"zgjero menynë"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"palos menynë"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Lëvize majtas"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Lëvize djathtas"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"zgjero <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"palos <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Cilësimet e <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Hiqe flluskën"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Mos e vendos bisedën në flluskë"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bisedo duke përdorur flluskat"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Bisedat e reja shfaqen si ikona pluskuese ose flluska. Trokit për të hapur flluskën. Zvarrit për ta zhvendosur."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Zgjidh"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Pamja e ekranit"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Hape në shfletues"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string>
     <string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Hap menynë"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Regjistro ekranin"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Përmasat e këtij aplikacioni nuk mund të ndryshohen"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 8fd824e..539a00a2 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Премести горе десно"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Премести доле лево"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Премести доле десно"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"прошири мени"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"скупи мени"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Померите налево"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Померите надесно"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"проширите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"скупите облачић <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Подешавања за <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Одбаци облачић"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не користи облачиће за конверзацију"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Ћаскајте у облачићима"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нове конверзације се приказују као плутајуће иконе или облачићи. Додирните да бисте отворили облачић. Превуците да бисте га преместили."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Изаберите"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Снимак екрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Отворите у прегледачу"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string>
     <string name="close_text" msgid="4986518933445178928">"Затворите"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Отворите мени"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Уклопи екран"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Величина ове апликације не може да се промени"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index b6375c6..549eb10 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Flytta högst upp till höger"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Flytta längst ned till vänster"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Flytta längst ned till höger"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"utöka menyn"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"komprimera menyn"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Flytta åt vänster"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Flytta åt höger"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"utöka <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"komprimera <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Inställningar för <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Stäng bubbla"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Visa inte konversationen i bubblor"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Chatta med bubblor"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Nya konversationer visas som flytande ikoner, så kallade bubblor. Tryck på bubblan om du vill öppna den. Dra den om du vill flytta den."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Välj"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skärmbild"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Öppna i webbläsaren"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string>
     <string name="close_text" msgid="4986518933445178928">"Stäng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Öppna menyn"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Fäst skärmen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Det går inte att ändra storlek på appen"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 2eadf34..9c34690 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sogeza juu kulia"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sogeza chini kushoto"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sogeza chini kulia"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"panua menyu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"kunja menyu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sogeza kushoto"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sogeza kulia"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"panua <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"kunja <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Mipangilio ya <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Ondoa kiputo"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Usiweke viputo kwenye mazungumzo"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Piga gumzo ukitumia viputo"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Mazungumzo mapya huonekena kama aikoni au viputo vinavyoelea. Gusa ili ufungue kiputo. Buruta ili ukisogeze."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Chagua"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Picha ya skrini"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Fungua katika kivinjari"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string>
     <string name="close_text" msgid="4986518933445178928">"Funga"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Fungua Menyu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Panga Madirisha kwenye Skrini"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Huwezi kubadilisha ukubwa wa programu hii"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 36f69ce..7e996f3 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"மேலே வலப்புறமாக நகர்த்து"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"கீழே இடப்புறமாக நகர்த்து"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"கீழே வலதுபுறமாக நகர்த்து"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"மெனுவை விரிவாக்கு"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"மெனுவைச் சுருக்கு"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"இடப்புறம் நகர்த்து"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"வலப்புறம் நகர்த்து"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> ஐச் சுருக்கும்"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> அமைப்புகள்"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"குமிழை அகற்று"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"உரையாடலைக் குமிழாக்காதே"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"குமிழ்களைப் பயன்படுத்தி அரட்டையடியுங்கள்"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"புதிய உரையாடல்கள் மிதக்கும் ஐகான்களாகவோ குமிழ்களாகவோ தோன்றும். குமிழைத் திறக்க தட்டவும். நகர்த்த இழுக்கவும்."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"தேர்ந்தெடுக்கும்"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ஸ்கிரீன்ஷாட்"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"உலாவியில் திறக்கும்"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string>
     <string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"மெனுவைத் திற"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"திரையை ஸ்னாப் செய்"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"இந்த ஆப்ஸின் அளவை மாற்ற முடியாது"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 2446ae8..984cea8 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ఎగువ కుడివైపునకు జరుపు"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"దిగువ ఎడమవైపునకు తరలించు"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"దిగవు కుడివైపునకు జరుపు"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"మెనూను విస్తరించండి"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"మెనూను కుదించండి"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ఎడమ వైపుగా జరపండి"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"కుడి వైపుగా జరపండి"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> విస్తరించండి"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>‌ను కుదించండి"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> సెట్టింగ్‌లు"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"బబుల్‌ను విస్మరించు"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"సంభాషణను బబుల్ చేయవద్దు"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"బబుల్స్‌ను ఉపయోగించి చాట్ చేయండి"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"కొత్త సంభాషణలు తేలియాడే చిహ్నాలుగా లేదా బబుల్స్ లాగా కనిపిస్తాయి. బబుల్‌ని తెరవడానికి నొక్కండి. తరలించడానికి లాగండి."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"ఎంచుకోండి"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"స్క్రీన్‌షాట్"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"బ్రౌజర్‌లో తెరవండి"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string>
     <string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"మెనూను తెరవండి"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"స్క్రీన్‌ను స్నాప్ చేయండి"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ఈ యాప్ సైజ్‌ను మార్చడం సాధ్యపడదు"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 10cf216..3460b07 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"ย้ายไปด้านขวาบน"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"ย้ายไปด้านซ้ายล่าง"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"ย้ายไปด้านขวาล่าง"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"ขยายเมนู"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"ยุบเมนู"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"ย้ายไปทางซ้าย"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"ย้ายไปทางขวา"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"ขยาย <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"ยุบ <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"การตั้งค่า <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"ปิดบับเบิล"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"ไม่ต้องแสดงการสนทนาเป็นบับเบิล"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"แชทโดยใช้บับเบิล"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"การสนทนาใหม่ๆ จะปรากฏเป็นไอคอนแบบลอยหรือบับเบิล แตะเพื่อเปิดบับเบิล ลากเพื่อย้ายที่"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"เลือก"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"ภาพหน้าจอ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"เปิดในเบราว์เซอร์"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string>
     <string name="close_text" msgid="4986518933445178928">"ปิด"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"เปิดเมนู"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"สแนปหน้าจอ"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"ปรับขนาดแอปนี้ไม่ได้"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 497ac56b..dc30912 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Ilipat sa kanan sa itaas"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Ilipat sa kaliwa sa ibaba"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Ilipat sa kanan sa ibaba"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"i-expand ang menu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"i-collapse ang menu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Ilipat pakaliwa"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Ilipat pakanan"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"I-expand ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"i-collapse ang <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Mga setting ng <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"I-dismiss ang bubble"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Huwag ipakita sa bubble ang mga pag-uusap"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Mag-chat gamit ang bubbles"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Lumalabas bilang mga nakalutang na icon o bubble ang mga bagong pag-uusap. I-tap para buksan ang bubble. I-drag para ilipat ito."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Piliin"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Screenshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Buksan sa browser"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string>
     <string name="close_text" msgid="4986518933445178928">"Isara"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Buksan ang Menu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"I-snap ang Screen"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Hindi nare-resize ang app na ito"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 05eb1ba..e9e2173 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Sağ üste taşı"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Sol alta taşı"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Sağ alta taşı"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menüyü genişlet"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menüyü daralt"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Sola taşı"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Sağa taşı"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"genişlet: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"daralt: <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ayarları"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Baloncuğu kapat"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Görüşmeyi baloncuk olarak görüntüleme"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Baloncukları kullanarak sohbet edin"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yeni görüşmeler kayan simgeler veya baloncuk olarak görünür. Açmak için baloncuğa dokunun. Baloncuğu taşımak için sürükleyin."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Seç"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ekran görüntüsü"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Tarayıcıda aç"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string>
     <string name="close_text" msgid="4986518933445178928">"Kapat"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menüyü Aç"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranın Yarısına Tuttur"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu uygulama yeniden boyutlandırılamaz"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 5c3cfaa..e1b6e35 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Перемістити праворуч угору"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Перемістити ліворуч униз"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Перемістити праворуч униз"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"розгорнути меню"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"згорнути меню"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Перемістити ліворуч"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Перемістити праворуч"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"розгорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"згорнути \"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>\""</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Налаштування параметра \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Закрити підказку"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не показувати спливаючі чати для розмов"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Спливаючий чат"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Нові повідомлення чату з\'являються у вигляді спливаючих значків. Щоб відкрити чат, натисніть його, а щоб перемістити – перетягніть."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Вибрати"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Знімок екрана"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Відкрити у вебпереглядачі"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string>
     <string name="close_text" msgid="4986518933445178928">"Закрити"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Відкрити меню"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Зафіксувати екран"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Розмір вікна цього додатка не можна змінити"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 98c433b..0508e6d 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"اوپر دائیں جانب لے جائيں"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"نیچے بائیں جانب لے جائیں"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"نیچے دائیں جانب لے جائیں"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"مینو کو پھیلائیں"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"مینو کو سکیڑیں"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"بائیں منتقل کریں"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"دائیں منتقل کریں"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو پھیلائیں"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g> کو سکیڑیں"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ترتیبات"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"بلبلہ برخاست کریں"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"گفتگو بلبلہ نہ کریں"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"بلبلے کے ذریعے چیٹ کریں"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"نئی گفتگوئیں فلوٹنگ آئیکن یا بلبلے کے طور پر ظاہر ہوں گی۔ بلبلہ کھولنے کے لیے تھپتھپائیں۔ اسے منتقل کرنے کے لیے گھسیٹیں۔"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"منتخب کریں"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"اسکرین شاٹ"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"براؤزر میں کھولیں"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string>
     <string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"مینو کھولیں"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"اسکرین کا اسناپ شاٹ لیں"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"اس ایپ کا سائز تبدیل نہیں کیا جا سکتا"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 3ec676d..5de3b7b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Yuqori oʻngga surish"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Quyi chapga surish"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Quyi oʻngga surish"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"menyuni ochish"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"menyuni yopish"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Chapga siljitish"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Oʻngga siljitish"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yoyish"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>ni yopish"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> sozlamalari"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Bulutchani yopish"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Suhbatlar bulutchalar shaklida chiqmasin"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Bulutchalar yordamida subhatlashish"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Yangi xabarlar qalqib chiquvchi belgilar yoki bulutchalar kabi chiqadi. Xabarni ochish uchun bildirishnoma ustiga bosing. Xabarni qayta joylash uchun bildirishnomani suring."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Tanlash"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Skrinshot"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerda ochish"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string>
     <string name="close_text" msgid="4986518933445178928">"Yopish"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Menyuni ochish"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Ekranni biriktirish"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Bu ilova hajmini oʻzgartirish imkonsiz"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 125d703..9b2f898 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Chuyển lên trên cùng bên phải"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Chuyển tới dưới cùng bên trái"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Chuyển tới dưới cùng bên phải"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"mở rộng trình đơn"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"thu gọn trình đơn"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Di chuyển sang trái"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Di chuyển sang phải"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"mở rộng <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"thu gọn <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Cài đặt <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Đóng bong bóng"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Dừng sử dụng bong bóng cho cuộc trò chuyện"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Trò chuyện bằng bong bóng trò chuyện"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Các cuộc trò chuyện mới sẽ xuất hiện dưới dạng biểu tượng nổi hoặc bong bóng trò chuyện. Nhấn để mở bong bóng trò chuyện. Kéo để di chuyển bong bóng trò chuyện."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Chọn"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Ảnh chụp màn hình"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Mở trong trình duyệt"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string>
     <string name="close_text" msgid="4986518933445178928">"Đóng"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Mở Trình đơn"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Điều chỉnh kích thước màn hình"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Không thể đổi kích thước của ứng dụng này"</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 6ccc38b..b45b76e 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上角"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下角"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下角"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展开菜单"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收起菜单"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"左移"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"右移"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展开“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收起“<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>”"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>设置"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"关闭消息气泡"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不以消息气泡形式显示对话"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用消息气泡聊天"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"新对话会以浮动图标或消息气泡形式显示。点按即可打开消息气泡。拖动即可移动消息气泡。"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"选择"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"屏幕截图"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在浏览器中打开"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"新窗口"</string>
     <string name="close_text" msgid="4986518933445178928">"关闭"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"打开菜单"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"屏幕快照"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"无法调整此应用的大小"</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 2787c1e..ae776b8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移去右上角"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移去左下角"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移去右下角"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展開選單"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收合選單"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"向左移"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"向右移"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"打開<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收埋<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉小視窗氣泡"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要透過小視窗顯示對話"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"使用小視窗進行即時通訊"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"新對話會以浮動圖示 (小視窗) 顯示。輕按即可開啟小視窗。拖曳即可移動小視窗。"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"選取"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"打開選單"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"此應用程式無法調整大小"</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 72050fb..5bfc6b8 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"移至右上方"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"移至左下方"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"移至右下方"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"展開選單"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"收合選單"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"向左移"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"向右移"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"展開「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"收合「<xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>」"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"「<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>」設定"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"關閉對話框"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"不要以對話框形式顯示對話"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"透過對話框來聊天"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"新的對話會以浮動圖示或對話框形式顯示。輕觸即可開啟對話框,拖曳則可移動對話框。"</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"選取"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"螢幕截圖"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
     <string name="close_text" msgid="4986518933445178928">"關閉"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"開啟選單"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"貼齊畫面"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"這個應用程式無法調整大小"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index ac62a7b..0598d62 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -65,10 +65,16 @@
     <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Hambisa phezulu ngakwesokudla"</string>
     <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Hambisa inkinobho ngakwesokunxele"</string>
     <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Hambisa inkinobho ngakwesokudla"</string>
+    <string name="bubble_accessibility_action_expand_menu" msgid="8637233525952938845">"nweba imenyu"</string>
+    <string name="bubble_accessibility_action_collapse_menu" msgid="2975310870146231463">"goqa imenyu"</string>
+    <string name="bubble_accessibility_action_move_bar_left" msgid="4803535120353716759">"Iya kwesokunxele"</string>
+    <string name="bubble_accessibility_action_move_bar_right" msgid="7686542531917510421">"Iya kwesokudla"</string>
     <string name="bubble_accessibility_announce_expand" msgid="5388792092888203776">"nweba <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubble_accessibility_announce_collapse" msgid="3178806224494537097">"goqa <xliff:g id="BUBBLE_TITLE">%1$s</xliff:g>"</string>
     <string name="bubbles_app_settings" msgid="3617224938701566416">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> izilungiselelo"</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Cashisa ibhamuza"</string>
+    <!-- no translation found for bubble_fullscreen_text (1006758103218086231) -->
+    <skip />
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Ungayibhamuzi ingxoxo"</string>
     <string name="bubbles_user_education_title" msgid="2112319053732691899">"Xoxa usebenzisa amabhamuza"</string>
     <string name="bubbles_user_education_description" msgid="4215862563054175407">"Izingxoxo ezintsha zivela njengezithonjana ezintantayo, noma amabhamuza. Thepha ukuze uvule ibhamuza. Hudula ukuze ulihambise."</string>
@@ -117,9 +123,11 @@
     <string name="select_text" msgid="5139083974039906583">"Khetha"</string>
     <string name="screenshot_text" msgid="1477704010087786671">"Isithombe-skrini"</string>
     <string name="open_in_browser_text" msgid="9181692926376072904">"Vula kubhrawuza"</string>
+    <string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string>
     <string name="close_text" msgid="4986518933445178928">"Vala"</string>
     <string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
     <string name="expand_menu_text" msgid="3847736164494181168">"Vula Imenyu"</string>
     <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
     <string name="desktop_mode_maximize_menu_snap_text" msgid="2065251022783880154">"Thwebula Isikrini"</string>
+    <string name="desktop_mode_non_resizable_snap_text" msgid="1049800446363800707">"Le app ayikwazi ukushintshwa usayizi"</string>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 39f6d8c..fe8b818 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -183,4 +183,7 @@
     <!-- This is to be overridden to define a list of packages mapped to web links which will be
          parsed and utilized for desktop windowing's app-to-web feature. -->
     <string name="generic_links_list" translatable="false"/>
+
+    <!-- Apps that can trigger Desktop Windowing App handle Education -->
+    <string-array name="desktop_windowing_app_handle_education_allowlist_apps"></string-array>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e6807ac..2d98a2b 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -91,12 +91,16 @@
     <!-- Divider handle size for legacy split screen -->
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
-    <!-- Divider handle size for split screen -->
-    <dimen name="split_divider_handle_region_width">96dp</dimen>
-    <dimen name="split_divider_handle_region_height">48dp</dimen>
 
-    <dimen name="split_divider_handle_width">72dp</dimen>
-    <dimen name="split_divider_handle_height">3dp</dimen>
+    <dimen name="split_divider_handle_region_width">80dp</dimen>
+    <dimen name="split_divider_handle_region_height">48dp</dimen>
+    <!-- The divider touch zone height is intentionally halved in portrait to avoid colliding
+         with the app handle.-->
+    <dimen name="desktop_mode_portrait_split_divider_handle_region_height">24dp</dimen>
+
+    <!-- Divider handle size for split screen -->
+    <dimen name="split_divider_handle_width">40dp</dimen>
+    <dimen name="split_divider_handle_height">4dp</dimen>
 
     <dimen name="split_divider_bar_width">10dp</dimen>
     <dimen name="split_divider_corner_size">42dp</dimen>
@@ -456,6 +460,11 @@
          start of this area. -->
     <dimen name="desktop_mode_customizable_caption_margin_end">152dp</dimen>
 
+    <!-- The width of the right-aligned region that is taken up by caption elements and extra
+         margins when the caption has the minimize button. This will be merged with the above value
+         once the minimize button becomes default. -->
+    <dimen name="desktop_mode_customizable_caption_with_minimize_button_margin_end">204dp</dimen>
+
     <!-- The default minimum allowed window width when resizing a window in desktop mode. -->
     <dimen name="desktop_mode_minimum_window_width">386dp</dimen>
 
@@ -538,20 +547,23 @@
     <!-- The size of the icon shown in the resize veil. -->
     <dimen name="desktop_mode_resize_veil_icon_size">96dp</dimen>
 
-    <!-- The with of the border around the app task for edge resizing, when
+    <!-- The width of the border outside the app task eligible for edge resizing, when
          enable_windowing_edge_drag_resize is enabled. -->
-    <dimen name="desktop_mode_edge_handle">12dp</dimen>
+    <dimen name="freeform_edge_handle_outset">10dp</dimen>
+
+    <!-- The size of the border inside the app task eligible for edge resizing, when
+         enable_windowing_edge_drag_resize is enabled. -->
+    <dimen name="freeform_edge_handle_inset">2dp</dimen>
 
     <!-- The original width of the border around the app task for edge resizing, when
          enable_windowing_edge_drag_resize is disabled. -->
     <dimen name="freeform_resize_handle">15dp</dimen>
 
     <!-- The size of the corner region for drag resizing with touch, when a larger touch region is
-         appropriate. Applied when enable_windowing_edge_drag_resize is enabled. -->
+         appropriate. -->
     <dimen name="desktop_mode_corner_resize_large">48dp</dimen>
 
-    <!-- The original size of the corner region for darg resizing, when
-         enable_windowing_edge_drag_resize is disabled. -->
+    <!-- The size of the corner region for drag resizing with a cursor or a stylus. -->
     <dimen name="freeform_resize_corner">44dp</dimen>
 
     <!-- The thickness in dp for all desktop drag transition regions. -->
@@ -572,6 +584,13 @@
     <!-- The vertical inset to apply to the app chip's ripple drawable -->
     <dimen name="desktop_mode_header_app_chip_ripple_inset_vertical">4dp</dimen>
 
+    <!-- The corner radius of the minimize button's ripple drawable -->
+    <dimen name="desktop_mode_header_minimize_ripple_radius">18dp</dimen>
+    <!-- The vertical inset to apply to the minimize button's ripple drawable -->
+    <dimen name="desktop_mode_header_minimize_ripple_inset_vertical">4dp</dimen>
+    <!-- The horizontal inset to apply to the minimize button's ripple drawable -->
+    <dimen name="desktop_mode_header_minimize_ripple_inset_horizontal">6dp</dimen>
+
     <!-- The corner radius of the maximize button's ripple drawable -->
     <dimen name="desktop_mode_header_maximize_ripple_radius">18dp</dimen>
     <!-- The vertical inset to apply to the maximize button's ripple drawable -->
diff --git a/libs/WindowManager/Shell/res/values/ids.xml b/libs/WindowManager/Shell/res/values/ids.xml
index bc59a23..debcba0 100644
--- a/libs/WindowManager/Shell/res/values/ids.xml
+++ b/libs/WindowManager/Shell/res/values/ids.xml
@@ -42,6 +42,8 @@
     <item type="id" name="action_move_top_right"/>
     <item type="id" name="action_move_bottom_left"/>
     <item type="id" name="action_move_bottom_right"/>
+    <item type="id" name="action_move_bubble_bar_left"/>
+    <item type="id" name="action_move_bubble_bar_right"/>
 
     <item type="id" name="dismiss_view"/>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/integers.xml b/libs/WindowManager/Shell/res/values/integers.xml
index 583bf33..300baea 100644
--- a/libs/WindowManager/Shell/res/values/integers.xml
+++ b/libs/WindowManager/Shell/res/values/integers.xml
@@ -22,4 +22,16 @@
     <integer name="bubbles_overflow_columns">4</integer>
     <!-- Maximum number of bubbles we allow in overflow before we dismiss the oldest one. -->
     <integer name="bubbles_max_overflow">16</integer>
+    <!-- App Handle Education - Minimum number of times an app should have been launched, in order
+         to be eligible to show education in it -->
+    <integer name="desktop_windowing_education_min_app_launch_count">3</integer>
+    <!-- App Handle Education - Interval at which app usage stats should be queried and updated in
+         cache periodically -->
+    <integer name="desktop_windowing_education_app_usage_cache_interval_seconds">86400</integer>
+    <!-- App Handle Education - Time interval in seconds for which we'll analyze app usage
+         stats to determine if minimum usage requirements are met.  -->
+    <integer name="desktop_windowing_education_app_launch_interval_seconds">2592000</integer>
+    <!-- App Handle Education - Required time passed in seconds since device has been setup
+         in order to be eligible to show education -->
+    <integer name="desktop_windowing_education_required_time_since_setup_seconds">604800</integer>
 </resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index df5f1e4..a353db7 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -143,6 +143,10 @@
     <string name="bubble_accessibility_action_expand_menu">expand menu</string>
     <!-- Click action label for bubbles to collapse menu. [CHAR LIMIT=30]-->
     <string name="bubble_accessibility_action_collapse_menu">collapse menu</string>
+    <!-- Action in accessibility menu to move the bubble bar to the left side of the screen. [CHAR_LIMIT=30] -->
+    <string name="bubble_accessibility_action_move_bar_left">Move left</string>
+    <!-- Action in accessibility menu to move the bubble bar to the right side of the screen. [CHAR_LIMIT=30] -->
+    <string name="bubble_accessibility_action_move_bar_right">Move right</string>
     <!-- Accessibility announcement when the stack of bubbles expands. [CHAR LIMIT=NONE]-->
     <string name="bubble_accessibility_announce_expand">expand <xliff:g id="bubble_title" example="Messages">%1$s</xliff:g></string>
     <!-- Accessibility announcement when the stack of bubbles collapses. [CHAR LIMIT=NONE]-->
@@ -151,6 +155,8 @@
     <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string>
     <!-- Text used for the bubble dismiss area. Bubbles dragged to, or flung towards, this area will go away. [CHAR LIMIT=30] -->
     <string name="bubble_dismiss_text">Dismiss bubble</string>
+    <!-- Text used to move the bubble to fullscreen. [CHAR LIMIT=30] -->
+    <string name="bubble_fullscreen_text">Move to fullscreen</string>
     <!-- Button text to stop a conversation from bubbling [CHAR LIMIT=60]-->
     <string name="bubbles_dont_bubble_conversation">Don\u2019t bubble conversation</string>
     <!-- Title text for the bubbles feature education cling shown when a bubble is on screen for the first time. [CHAR LIMIT=60]-->
@@ -298,4 +304,6 @@
     <string name="desktop_mode_maximize_menu_maximize_text">Maximize Screen</string>
     <!-- Maximize menu snap buttons string. -->
     <string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
+    <!-- Snap resizing non-resizable string. -->
+    <string name="desktop_mode_non_resizable_snap_text">This app can\'t be resized</string>
 </resources>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl
new file mode 100644
index 0000000..e21bf8f
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.shared;
+
+parcelable GroupedRecentTaskInfo;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java
new file mode 100644
index 0000000..65e079e
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedRecentTaskInfo.java
@@ -0,0 +1,236 @@
+/*
+ * 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.shared;
+
+import android.annotation.IntDef;
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.shared.split.SplitBounds;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Simple container for recent tasks.  May contain either a single or pair of tasks.
+ */
+public class GroupedRecentTaskInfo implements Parcelable {
+
+    public static final int TYPE_SINGLE = 1;
+    public static final int TYPE_SPLIT = 2;
+    public static final int TYPE_FREEFORM = 3;
+
+    @IntDef(prefix = {"TYPE_"}, value = {
+            TYPE_SINGLE,
+            TYPE_SPLIT,
+            TYPE_FREEFORM
+    })
+    public @interface GroupType {}
+
+    @NonNull
+    private final ActivityManager.RecentTaskInfo[] mTasks;
+    @Nullable
+    private final SplitBounds mSplitBounds;
+    @GroupType
+    private final int mType;
+    // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
+    //  replacement for RecentTaskInfo
+    private final int[] mMinimizedTaskIds;
+
+    /**
+     * Create new for a single task
+     */
+    public static GroupedRecentTaskInfo forSingleTask(
+            @NonNull ActivityManager.RecentTaskInfo task) {
+        return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
+                TYPE_SINGLE, null /* minimizedFreeformTasks */);
+    }
+
+    /**
+     * Create new for a pair of tasks in split screen
+     */
+    public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
+            @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
+        return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
+                splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
+    }
+
+    /**
+     * Create new for a group of freeform tasks
+     */
+    public static GroupedRecentTaskInfo forFreeformTasks(
+            @NonNull ActivityManager.RecentTaskInfo[] tasks,
+            @NonNull Set<Integer> minimizedFreeformTasks) {
+        return new GroupedRecentTaskInfo(
+                tasks,
+                null /* splitBounds */,
+                TYPE_FREEFORM,
+                minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
+    }
+
+    private GroupedRecentTaskInfo(
+            @NonNull ActivityManager.RecentTaskInfo[] tasks,
+            @Nullable SplitBounds splitBounds,
+            @GroupType int type,
+            @Nullable int[] minimizedFreeformTaskIds) {
+        mTasks = tasks;
+        mSplitBounds = splitBounds;
+        mType = type;
+        mMinimizedTaskIds = minimizedFreeformTaskIds;
+        ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
+    }
+
+    private static void ensureAllMinimizedIdsPresent(
+            @NonNull ActivityManager.RecentTaskInfo[] tasks,
+            @Nullable int[] minimizedFreeformTaskIds) {
+        if (minimizedFreeformTaskIds == null) {
+            return;
+        }
+        if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
+                taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
+            throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
+        }
+    }
+
+    GroupedRecentTaskInfo(Parcel parcel) {
+        mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
+        mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
+        mType = parcel.readInt();
+        mMinimizedTaskIds = parcel.createIntArray();
+    }
+
+    /**
+     * Get primary {@link ActivityManager.RecentTaskInfo}
+     */
+    @NonNull
+    public ActivityManager.RecentTaskInfo getTaskInfo1() {
+        return mTasks[0];
+    }
+
+    /**
+     * Get secondary {@link ActivityManager.RecentTaskInfo}.
+     *
+     * Used in split screen.
+     */
+    @Nullable
+    public ActivityManager.RecentTaskInfo getTaskInfo2() {
+        if (mTasks.length > 1) {
+            return mTasks[1];
+        }
+        return null;
+    }
+
+    /**
+     * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
+     */
+    @NonNull
+    public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
+        return Arrays.asList(mTasks);
+    }
+
+    /**
+     * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+     */
+    @Nullable
+    public SplitBounds getSplitBounds() {
+        return mSplitBounds;
+    }
+
+    /**
+     * Get type of this recents entry. One of {@link GroupType}
+     */
+    @GroupType
+    public int getType() {
+        return mType;
+    }
+
+    public int[] getMinimizedTaskIds() {
+        return mMinimizedTaskIds;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder taskString = new StringBuilder();
+        for (int i = 0; i < mTasks.length; i++) {
+            if (i == 0) {
+                taskString.append("Task");
+            } else {
+                taskString.append(", Task");
+            }
+            taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
+        }
+        if (mSplitBounds != null) {
+            taskString.append(", SplitBounds: ").append(mSplitBounds);
+        }
+        taskString.append(", Type=");
+        switch (mType) {
+            case TYPE_SINGLE:
+                taskString.append("TYPE_SINGLE");
+                break;
+            case TYPE_SPLIT:
+                taskString.append("TYPE_SPLIT");
+                break;
+            case TYPE_FREEFORM:
+                taskString.append("TYPE_FREEFORM");
+                break;
+        }
+        taskString.append(", Minimized Task IDs: ");
+        taskString.append(Arrays.toString(mMinimizedTaskIds));
+        return taskString.toString();
+    }
+
+    private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
+        if (taskInfo == null) {
+            return null;
+        }
+        return "id=" + taskInfo.taskId
+                + " baseIntent=" + (taskInfo.baseIntent != null
+                        ? taskInfo.baseIntent.getComponent()
+                        : "null")
+                + " winMode=" + WindowConfiguration.windowingModeToString(
+                        taskInfo.getWindowingMode());
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedArray(mTasks, flags);
+        parcel.writeTypedObject(mSplitBounds, flags);
+        parcel.writeInt(mType);
+        parcel.writeIntArray(mMinimizedTaskIds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
+            new Creator<GroupedRecentTaskInfo>() {
+        public GroupedRecentTaskInfo createFromParcel(Parcel source) {
+            return new GroupedRecentTaskInfo(source);
+        }
+        public GroupedRecentTaskInfo[] newArray(int size) {
+            return new GroupedRecentTaskInfo[size];
+        }
+    };
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java
new file mode 100644
index 0000000..8f7a2e5
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/ShellSharedConstants.java
@@ -0,0 +1,48 @@
+/*
+ * 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.shared;
+
+/**
+ * General shell-related constants that are shared with users of the library.
+ */
+public class ShellSharedConstants {
+    // See IPip.aidl
+    public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
+    // See IBubbles.aidl
+    public static final String KEY_EXTRA_SHELL_BUBBLES = "extra_shell_bubbles";
+    // See ISplitScreen.aidl
+    public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
+    // See IOneHanded.aidl
+    public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed";
+    // See IShellTransitions.aidl
+    public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS =
+            "extra_shell_shell_transitions";
+    // See IStartingWindow.aidl
+    public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
+            "extra_shell_starting_window";
+    // See IRecentTasks.aidl
+    public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks";
+    // See IBackAnimation.aidl
+    public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
+    // See IDesktopMode.aidl
+    public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode";
+    // See IDragAndDrop.aidl
+    public static final String KEY_EXTRA_SHELL_DRAG_AND_DROP = "extra_shell_drag_and_drop";
+    // See IRecentsAnimationController.aidl
+    public static final String KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION =
+            "extra_shell_can_hand_off_animation";
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransactionPool.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransactionPool.java
new file mode 100644
index 0000000..0c5d88d
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransactionPool.java
@@ -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.wm.shell.shared;
+
+import android.util.Pools;
+import android.view.SurfaceControl;
+
+/**
+ * Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
+ */
+public class TransactionPool {
+    private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
+            new Pools.SynchronizedPool<>(4);
+
+    public TransactionPool() {
+    }
+
+    /** Gets a transaction from the pool. */
+    public SurfaceControl.Transaction acquire() {
+        SurfaceControl.Transaction t = mTransactionPool.acquire();
+        if (t == null) {
+            return new SurfaceControl.Transaction();
+        }
+        return t;
+    }
+
+    /**
+     * Return a transaction to the pool. DO NOT call {@link SurfaceControl.Transaction#close()} if
+     * returning to pool.
+     */
+    public void release(SurfaceControl.Transaction t) {
+        if (!mTransactionPool.release(t)) {
+            t.close();
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TriangleShape.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TriangleShape.java
new file mode 100644
index 0000000..0ca5327
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TriangleShape.java
@@ -0,0 +1,77 @@
+/*
+ * 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.shared;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Wrapper around {@link PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ *
+ * This is the copy from SystemUI/recents.
+ */
+public class TriangleShape extends PathShape {
+    private Path mTriangularPath;
+
+    public TriangleShape(Path path, float stdWidth, float stdHeight) {
+        super(path, stdWidth, stdHeight);
+        mTriangularPath = path;
+    }
+
+    public static TriangleShape create(float width, float height, boolean isPointingUp) {
+        Path triangularPath = new Path();
+        if (isPointingUp) {
+            triangularPath.moveTo(0, height);
+            triangularPath.lineTo(width, height);
+            triangularPath.lineTo(width / 2, 0);
+            triangularPath.close();
+        } else {
+            triangularPath.moveTo(0, 0);
+            triangularPath.lineTo(width / 2, height);
+            triangularPath.lineTo(width, 0);
+            triangularPath.close();
+        }
+        return new TriangleShape(triangularPath, width, height);
+    }
+
+    /** Create an arrow TriangleShape that points to the left or the right */
+    public static TriangleShape createHorizontal(
+            float width, float height, boolean isPointingLeft) {
+        Path triangularPath = new Path();
+        if (isPointingLeft) {
+            triangularPath.moveTo(0, height / 2);
+            triangularPath.lineTo(width, height);
+            triangularPath.lineTo(width, 0);
+            triangularPath.close();
+        } else {
+            triangularPath.moveTo(0, height);
+            triangularPath.lineTo(width, height / 2);
+            triangularPath.lineTo(0, 0);
+            triangularPath.close();
+        }
+        return new TriangleShape(triangularPath, width, height);
+    }
+
+    @Override
+    public void getOutline(@NonNull Outline outline) {
+        outline.setPath(mTriangularPath);
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
new file mode 100644
index 0000000..f45dc3a
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
@@ -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.wm.shell.shared.animation;
+
+import android.graphics.Path;
+import android.view.animation.BackGestureInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Common interpolators used in wm shell library.
+ */
+public class Interpolators {
+
+    public static final Interpolator LINEAR = new LinearInterpolator();
+
+    /**
+     * Interpolator for alpha in animation.
+     */
+    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+    /**
+     * Interpolator for alpha out animation.
+     */
+    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
+
+    /**
+     * Interpolator for fast out linear in animation.
+     */
+    public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+    /**
+     * Interpolator for fast out slow in animation.
+     */
+    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+    /**
+     * Interpolator for linear out slow in animation.
+     */
+    public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+    /**
+     * The default emphasized interpolator. Used for hero / emphasized movement of content.
+     */
+    public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+    /**
+     * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+     * is disappearing e.g. when moving off screen.
+     */
+    public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+            0.3f, 0f, 0.8f, 0.15f);
+
+    /**
+     * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+     * is appearing e.g. when coming from off screen
+     */
+    public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+            0.05f, 0.7f, 0.1f, 1f);
+
+    /**
+     * The standard decelerating interpolator that should be used on every regular movement of
+     * content that is appearing e.g. when coming from off screen.
+     */
+    public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(0f, 0f, 0f, 1f);
+
+    /**
+     * Interpolator to be used when animating a move based on a click. Pair with enough duration.
+     */
+    public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
+    /**
+     * Interpolator to be used when animating a panel closing.
+     */
+    public static final Interpolator PANEL_CLOSE_ACCELERATED =
+            new PathInterpolator(0.3f, 0, 0.5f, 1);
+
+    public static final PathInterpolator SLOWDOWN_INTERPOLATOR =
+            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
+
+    public static final PathInterpolator DIM_INTERPOLATOR =
+            new PathInterpolator(.23f, .87f, .52f, -0.11f);
+
+    /**
+     * Use this interpolator for animating progress values coming from the back callback to get
+     * the predictive-back-typical decelerate motion.
+     *
+     * This interpolator is similar to {@link Interpolators#STANDARD_DECELERATE} but has a slight
+     * acceleration phase at the start.
+     */
+    public static final Interpolator BACK_GESTURE = new BackGestureInterpolator();
+
+    // Create the default emphasized interpolator
+    private static PathInterpolator createEmphasizedInterpolator() {
+        Path path = new Path();
+        // Doing the same as fast_out_extra_slow_in
+        path.moveTo(0f, 0f);
+        path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+        path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+        return new PathInterpolator(path);
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
new file mode 100644
index 0000000..7086691
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BaseBubblePinController.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.shared.bubbles
+
+import android.graphics.Point
+import android.graphics.RectF
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.core.animation.Animator
+import androidx.core.animation.AnimatorListenerAdapter
+import androidx.core.animation.ObjectAnimator
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController.LocationChangeListener
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
+
+/**
+ * Base class for common logic shared between different bubble views to support pinning bubble bar
+ * to left or right edge of screen.
+ *
+ * Handles drag events and allows a [LocationChangeListener] to be registered that is notified when
+ * location of the bubble bar should change.
+ *
+ * Shows a drop target when releasing a view would update the [BubbleBarLocation].
+ */
+abstract class BaseBubblePinController(private val screenSizeProvider: () -> Point) {
+
+    private var initialLocationOnLeft = false
+    private var onLeft = false
+    private var dismissZone: RectF? = null
+    private var stuckToDismissTarget = false
+    private var screenCenterX = 0
+    private var listener: LocationChangeListener? = null
+    private var dropTargetAnimator: ObjectAnimator? = null
+
+    /**
+     * Signal the controller that dragging interaction has started.
+     *
+     * @param initialLocationOnLeft side of the screen where bubble bar is pinned to
+     */
+    fun onDragStart(initialLocationOnLeft: Boolean) {
+        this.initialLocationOnLeft = initialLocationOnLeft
+        onLeft = initialLocationOnLeft
+        screenCenterX = screenSizeProvider.invoke().x / 2
+        dismissZone = getExclusionRect()
+    }
+
+    /** View has moved to [x] and [y] screen coordinates */
+    fun onDragUpdate(x: Float, y: Float) {
+        if (dismissZone?.contains(x, y) == true) return
+
+        val wasOnLeft = onLeft
+        onLeft = x < screenCenterX
+        if (wasOnLeft != onLeft) {
+            onLocationChange(if (onLeft) LEFT else RIGHT)
+        } else if (stuckToDismissTarget) {
+            // Moved out of the dismiss view back to initial side, if we have a drop target, show it
+            getDropTargetView()?.apply { animateIn() }
+        }
+        // Make sure this gets cleared
+        stuckToDismissTarget = false
+    }
+
+    /** Signal the controller that view has been dragged to dismiss view. */
+    fun onStuckToDismissTarget() {
+        stuckToDismissTarget = true
+        // Notify that location may be reset
+        val shouldResetLocation = onLeft != initialLocationOnLeft
+        if (shouldResetLocation) {
+            onLeft = initialLocationOnLeft
+            listener?.onChange(if (onLeft) LEFT else RIGHT)
+        }
+        getDropTargetView()?.apply {
+            animateOut {
+                if (shouldResetLocation) {
+                    updateLocation(if (onLeft) LEFT else RIGHT)
+                }
+            }
+        }
+    }
+
+    /** Signal the controller that dragging interaction has finished. */
+    fun onDragEnd() {
+        getDropTargetView()?.let { view -> view.animateOut { removeDropTargetView(view) } }
+        dismissZone = null
+        listener?.onRelease(if (onLeft) LEFT else RIGHT)
+    }
+
+    /**
+     * [LocationChangeListener] that is notified when dragging interaction has resulted in bubble
+     * bar to be pinned on the other edge
+     */
+    fun setListener(listener: LocationChangeListener?) {
+        this.listener = listener
+    }
+
+    /** Get width for exclusion rect where dismiss takes over drag */
+    protected abstract fun getExclusionRectWidth(): Float
+    /** Get height for exclusion rect where dismiss takes over drag */
+    protected abstract fun getExclusionRectHeight(): Float
+
+    /** Create the drop target view and attach it to the parent */
+    protected abstract fun createDropTargetView(): View
+
+    /** Get the drop target view if it exists */
+    protected abstract fun getDropTargetView(): View?
+
+    /** Remove the drop target view */
+    protected abstract fun removeDropTargetView(view: View)
+
+    /** Update size and location of the drop target view */
+    protected abstract fun updateLocation(location: BubbleBarLocation)
+
+    private fun onLocationChange(location: BubbleBarLocation) {
+        showDropTarget(location)
+        listener?.onChange(location)
+    }
+
+    private fun getExclusionRect(): RectF {
+        val rect = RectF(0f, 0f, getExclusionRectWidth(), getExclusionRectHeight())
+        // Center it around the bottom center of the screen
+        val screenBottom = screenSizeProvider.invoke().y
+        rect.offsetTo(screenCenterX - rect.width() / 2, screenBottom - rect.height())
+        return rect
+    }
+
+    private fun showDropTarget(location: BubbleBarLocation) {
+        val targetView = getDropTargetView() ?: createDropTargetView().apply { alpha = 0f }
+        if (targetView.alpha > 0) {
+            targetView.animateOut {
+                updateLocation(location)
+                targetView.animateIn()
+            }
+        } else {
+            updateLocation(location)
+            targetView.animateIn()
+        }
+    }
+
+    private fun View.animateIn() {
+        dropTargetAnimator?.cancel()
+        dropTargetAnimator =
+            ObjectAnimator.ofFloat(this, View.ALPHA, 1f)
+                .setDuration(DROP_TARGET_ALPHA_IN_DURATION)
+                .addEndAction { dropTargetAnimator = null }
+        dropTargetAnimator?.start()
+    }
+
+    private fun View.animateOut(endAction: Runnable? = null) {
+        dropTargetAnimator?.cancel()
+        dropTargetAnimator =
+            ObjectAnimator.ofFloat(this, View.ALPHA, 0f)
+                .setDuration(DROP_TARGET_ALPHA_OUT_DURATION)
+                .addEndAction {
+                    endAction?.run()
+                    dropTargetAnimator = null
+                }
+        dropTargetAnimator?.start()
+    }
+
+    private fun <T : Animator> T.addEndAction(runnable: Runnable): T {
+        addListener(
+            object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    runnable.run()
+                }
+            }
+        )
+        return this
+    }
+
+    /** Receive updates on location changes */
+    interface LocationChangeListener {
+        /**
+         * Bubble bar has been dragged to a new [BubbleBarLocation]. And the drag is still in
+         * progress.
+         *
+         * Triggered when drag gesture passes the middle of the screen and before touch up. Can be
+         * triggered multiple times per gesture.
+         *
+         * @param location new location as a result of the ongoing drag operation
+         */
+        fun onChange(location: BubbleBarLocation) {}
+
+        /**
+         * Bubble bar has been released in the [BubbleBarLocation].
+         *
+         * @param location final location of the bubble bar once drag is released
+         */
+        fun onRelease(location: BubbleBarLocation)
+    }
+
+    companion object {
+        @VisibleForTesting const val DROP_TARGET_ALPHA_IN_DURATION = 150L
+        @VisibleForTesting const val DROP_TARGET_ALPHA_OUT_DURATION = 100L
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
new file mode 100644
index 0000000..4fe7611
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.shared.bubbles;
+
+parcelable BubbleBarLocation;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
new file mode 100644
index 0000000..191875d
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.shared.bubbles
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * The location of the bubble bar.
+ */
+enum class BubbleBarLocation : Parcelable {
+    /**
+     * Place bubble bar at the default location for the chosen system language.
+     * If an RTL language is used, it is on the left. Otherwise on the right.
+     */
+    DEFAULT,
+    /** Default bubble bar location is overridden. Place bubble bar on the left. */
+    LEFT,
+    /** Default bubble bar location is overridden. Place bubble bar on the right. */
+    RIGHT;
+
+    /**
+     * Returns whether bubble bar is pinned to the left edge or right edge.
+     */
+    fun isOnLeft(isRtl: Boolean): Boolean {
+        if (this == DEFAULT) {
+            return isRtl
+        }
+        return this == LEFT
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    override fun writeToParcel(dest: Parcel, flags: Int) {
+        dest.writeString(name)
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR = object : Parcelable.Creator<BubbleBarLocation> {
+            override fun createFromParcel(parcel: Parcel): BubbleBarLocation {
+                return parcel.readString()?.let { valueOf(it) } ?: DEFAULT
+            }
+
+            override fun newArray(size: Int) = arrayOfNulls<BubbleBarLocation>(size)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
new file mode 100644
index 0000000..5bde1e8
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarUpdate.java
@@ -0,0 +1,186 @@
+/*
+ * 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.shared.bubbles;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents an update to bubbles state. This is passed through
+ * {@link com.android.wm.shell.bubbles.IBubblesListener} to launcher so that taskbar may render
+ * bubbles. This should be kept this as minimal as possible in terms of data.
+ */
+public class BubbleBarUpdate implements Parcelable {
+
+    public static final String BUNDLE_KEY = "update";
+
+    public final boolean initialState;
+    public boolean expandedChanged;
+    public boolean expanded;
+    public boolean shouldShowEducation;
+    @Nullable
+    public String selectedBubbleKey;
+    @Nullable
+    public BubbleInfo addedBubble;
+    @Nullable
+    public BubbleInfo updatedBubble;
+    @Nullable
+    public String suppressedBubbleKey;
+    @Nullable
+    public String unsupressedBubbleKey;
+    @Nullable
+    public BubbleBarLocation bubbleBarLocation;
+    @Nullable
+    public Point expandedViewDropTargetSize;
+    public boolean showOverflowChanged;
+    public boolean showOverflow;
+
+    // This is only populated if bubbles have been removed.
+    public List<RemovedBubble> removedBubbles = new ArrayList<>();
+
+    // This is only populated if the order of the bubbles has changed.
+    public List<String> bubbleKeysInOrder = new ArrayList<>();
+
+    // This is only populated the first time a listener is connected so it gets the current state.
+    public List<BubbleInfo> currentBubbleList = new ArrayList<>();
+
+
+    public BubbleBarUpdate() {
+        this(false);
+    }
+
+    private BubbleBarUpdate(boolean initialState) {
+        this.initialState = initialState;
+    }
+
+    public BubbleBarUpdate(Parcel parcel) {
+        initialState = parcel.readBoolean();
+        expandedChanged = parcel.readBoolean();
+        expanded = parcel.readBoolean();
+        shouldShowEducation = parcel.readBoolean();
+        selectedBubbleKey = parcel.readString();
+        addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
+                BubbleInfo.class);
+        updatedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
+                BubbleInfo.class);
+        suppressedBubbleKey = parcel.readString();
+        unsupressedBubbleKey = parcel.readString();
+        removedBubbles = parcel.readParcelableList(new ArrayList<>(),
+                RemovedBubble.class.getClassLoader(), RemovedBubble.class);
+        parcel.readStringList(bubbleKeysInOrder);
+        currentBubbleList = parcel.readParcelableList(new ArrayList<>(),
+                BubbleInfo.class.getClassLoader(), BubbleInfo.class);
+        bubbleBarLocation = parcel.readParcelable(BubbleBarLocation.class.getClassLoader(),
+                BubbleBarLocation.class);
+        expandedViewDropTargetSize = parcel.readParcelable(Point.class.getClassLoader(),
+                Point.class);
+        showOverflowChanged = parcel.readBoolean();
+        showOverflow = parcel.readBoolean();
+    }
+
+    /**
+     * Returns whether anything has changed in this update.
+     */
+    public boolean anythingChanged() {
+        return expandedChanged
+                || selectedBubbleKey != null
+                || addedBubble != null
+                || updatedBubble != null
+                || !removedBubbles.isEmpty()
+                || !bubbleKeysInOrder.isEmpty()
+                || suppressedBubbleKey != null
+                || unsupressedBubbleKey != null
+                || !currentBubbleList.isEmpty()
+                || bubbleBarLocation != null
+                || showOverflowChanged;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "BubbleBarUpdate{"
+                + " initialState=" + initialState
+                + " expandedChanged=" + expandedChanged
+                + " expanded=" + expanded
+                + " selectedBubbleKey=" + selectedBubbleKey
+                + " shouldShowEducation=" + shouldShowEducation
+                + " addedBubble=" + addedBubble
+                + " updatedBubble=" + updatedBubble
+                + " suppressedBubbleKey=" + suppressedBubbleKey
+                + " unsuppressedBubbleKey=" + unsupressedBubbleKey
+                + " removedBubbles=" + removedBubbles
+                + " bubbles=" + bubbleKeysInOrder
+                + " currentBubbleList=" + currentBubbleList
+                + " bubbleBarLocation=" + bubbleBarLocation
+                + " expandedViewDropTargetSize=" + expandedViewDropTargetSize
+                + " showOverflowChanged=" + showOverflowChanged
+                + " showOverflow=" + showOverflow
+                + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeBoolean(initialState);
+        parcel.writeBoolean(expandedChanged);
+        parcel.writeBoolean(expanded);
+        parcel.writeBoolean(shouldShowEducation);
+        parcel.writeString(selectedBubbleKey);
+        parcel.writeParcelable(addedBubble, flags);
+        parcel.writeParcelable(updatedBubble, flags);
+        parcel.writeString(suppressedBubbleKey);
+        parcel.writeString(unsupressedBubbleKey);
+        parcel.writeParcelableList(removedBubbles, flags);
+        parcel.writeStringList(bubbleKeysInOrder);
+        parcel.writeParcelableList(currentBubbleList, flags);
+        parcel.writeParcelable(bubbleBarLocation, flags);
+        parcel.writeParcelable(expandedViewDropTargetSize, flags);
+        parcel.writeBoolean(showOverflowChanged);
+        parcel.writeBoolean(showOverflow);
+    }
+
+    /**
+     * Create update for initial set of values.
+     * <p>
+     * Used when bubble bar is newly created.
+     */
+    public static BubbleBarUpdate createInitialState() {
+        return new BubbleBarUpdate(true);
+    }
+
+    @NonNull
+    public static final Creator<BubbleBarUpdate> CREATOR =
+            new Creator<>() {
+                public BubbleBarUpdate createFromParcel(Parcel source) {
+                    return new BubbleBarUpdate(source);
+                }
+
+                public BubbleBarUpdate[] newArray(int size) {
+                    return new BubbleBarUpdate[size];
+                }
+            };
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
new file mode 100644
index 0000000..3396bc4
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleConstants.java
@@ -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 com.android.wm.shell.shared.bubbles;
+
+/**
+ * Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
+ */
+public class BubbleConstants {
+
+    /** The alpha for the scrim shown when bubbles are expanded. */
+    public static float BUBBLE_EXPANDED_SCRIM_ALPHA = .32f;
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
new file mode 100644
index 0000000..5876682
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleInfo.java
@@ -0,0 +1,196 @@
+/*
+ * 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.shared.bubbles;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Contains information necessary to present a bubble.
+ */
+public class BubbleInfo implements Parcelable {
+
+    private String mKey; // Same key as the Notification
+    private int mFlags;  // Flags from BubbleMetadata
+    @Nullable
+    private String mShortcutId;
+    private int mUserId;
+    private String mPackageName;
+    /**
+     * All notification bubbles require a shortcut to be set on the notification, however, the
+     * app could still specify an Icon and PendingIntent to use for the bubble. In that case
+     * this icon will be populated. If the bubble is entirely shortcut based, this will be null.
+     */
+    @Nullable
+    private Icon mIcon;
+    @Nullable
+    private String mTitle;
+    @Nullable
+    private String mAppName;
+    private boolean mIsImportantConversation;
+    private boolean mShowAppBadge;
+
+    public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
+            int userId, String packageName, @Nullable String title, @Nullable String appName,
+            boolean isImportantConversation, boolean showAppBadge) {
+        mKey = key;
+        mFlags = flags;
+        mShortcutId = shortcutId;
+        mIcon = icon;
+        mUserId = userId;
+        mPackageName = packageName;
+        mTitle = title;
+        mAppName = appName;
+        mIsImportantConversation = isImportantConversation;
+        mShowAppBadge = showAppBadge;
+    }
+
+    private BubbleInfo(Parcel source) {
+        mKey = source.readString();
+        mFlags = source.readInt();
+        mShortcutId = source.readString();
+        mIcon = source.readTypedObject(Icon.CREATOR);
+        mUserId = source.readInt();
+        mPackageName = source.readString();
+        mTitle = source.readString();
+        mAppName = source.readString();
+        mIsImportantConversation = source.readBoolean();
+        mShowAppBadge = source.readBoolean();
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    @Nullable
+    public String getShortcutId() {
+        return mShortcutId;
+    }
+
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    public int getFlags() {
+        return mFlags;
+    }
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Nullable
+    public String getTitle() {
+        return mTitle;
+    }
+
+    @Nullable
+    public String getAppName() {
+        return mAppName;
+    }
+
+    public boolean isImportantConversation() {
+        return mIsImportantConversation;
+    }
+
+    public boolean showAppBadge() {
+        return mShowAppBadge;
+    }
+
+    /**
+     * Whether this bubble is currently being hidden from the stack.
+     */
+    public boolean isBubbleSuppressed() {
+        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE) != 0;
+    }
+
+    /**
+     * Whether this bubble is able to be suppressed (i.e. has the developer opted into the API
+     * to
+     * hide the bubble when in the same content).
+     */
+    public boolean isBubbleSuppressable() {
+        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESSABLE_BUBBLE) != 0;
+    }
+
+    /**
+     * Whether the notification for this bubble is hidden from the shade.
+     */
+    public boolean isNotificationSuppressed() {
+        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION) != 0;
+    }
+
+    /** Sets the flags for this bubble. */
+    public void setFlags(int flags) {
+        mFlags = flags;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof BubbleInfo)) return false;
+        BubbleInfo bubble = (BubbleInfo) o;
+        return Objects.equals(mKey, bubble.mKey);
+    }
+
+    @Override
+    public int hashCode() {
+        return mKey.hashCode();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mKey);
+        parcel.writeInt(mFlags);
+        parcel.writeString(mShortcutId);
+        parcel.writeTypedObject(mIcon, flags);
+        parcel.writeInt(mUserId);
+        parcel.writeString(mPackageName);
+        parcel.writeString(mTitle);
+        parcel.writeString(mAppName);
+        parcel.writeBoolean(mIsImportantConversation);
+        parcel.writeBoolean(mShowAppBadge);
+    }
+
+    @NonNull
+    public static final Creator<BubbleInfo> CREATOR =
+            new Creator<>() {
+                public BubbleInfo createFromParcel(Parcel source) {
+                    return new BubbleInfo(source);
+                }
+
+                public BubbleInfo[] newArray(int size) {
+                    return new BubbleInfo[size];
+                }
+            };
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
new file mode 100644
index 0000000..8681acf
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupDrawable.kt
@@ -0,0 +1,232 @@
+/*
+ * 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.shared.bubbles
+
+import android.annotation.ColorInt
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Matrix
+import android.graphics.Outline
+import android.graphics.Paint
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import kotlin.math.atan
+import kotlin.math.cos
+import kotlin.math.sin
+import kotlin.properties.Delegates
+
+/** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */
+class BubblePopupDrawable(val config: Config) : Drawable() {
+    /** The direction of the arrow in the popup drawable */
+    enum class ArrowDirection {
+        UP,
+        DOWN
+    }
+
+    /** The arrow position on the side of the popup bubble */
+    sealed class ArrowPosition {
+        object Start : ArrowPosition()
+        object Center : ArrowPosition()
+        object End : ArrowPosition()
+        class Custom(val value: Float) : ArrowPosition()
+    }
+
+    /** The configuration for drawable features */
+    data class Config(
+        @ColorInt val color: Int,
+        val cornerRadius: Float,
+        val contentPadding: Int,
+        val arrowWidth: Float,
+        val arrowHeight: Float,
+        val arrowRadius: Float
+    )
+
+    /**
+     * The direction of the arrow in the popup drawable. It affects the content padding and requires
+     * it to be updated in the view.
+     */
+    var arrowDirection: ArrowDirection by
+        Delegates.observable(ArrowDirection.UP) { _, _, _ -> requestPathUpdate() }
+
+    /**
+     * Arrow position along the X axis and its direction. The position is adjusted to the content
+     * corner radius when applied so it doesn't go into rounded corner area
+     */
+    var arrowPosition: ArrowPosition by
+        Delegates.observable(ArrowPosition.Center) { _, _, _ -> requestPathUpdate() }
+
+    private val path = Path()
+    private val paint = Paint()
+    private var shouldUpdatePath = true
+
+    init {
+        paint.color = config.color
+        paint.style = Paint.Style.FILL
+        paint.isAntiAlias = true
+    }
+
+    override fun draw(canvas: Canvas) {
+        updatePathIfNeeded()
+        canvas.drawPath(path, paint)
+    }
+
+    override fun onBoundsChange(bounds: Rect) {
+        requestPathUpdate()
+    }
+
+    /** Should be applied to the view padding if arrow direction changes */
+    override fun getPadding(padding: Rect): Boolean {
+        padding.set(
+            config.contentPadding,
+            config.contentPadding,
+            config.contentPadding,
+            config.contentPadding
+        )
+        when (arrowDirection) {
+            ArrowDirection.UP -> padding.top += config.arrowHeight.toInt()
+            ArrowDirection.DOWN -> padding.bottom += config.arrowHeight.toInt()
+        }
+        return true
+    }
+
+    override fun getOutline(outline: Outline) {
+        updatePathIfNeeded()
+        outline.setPath(path)
+    }
+
+    override fun getOpacity(): Int {
+        return paint.alpha
+    }
+
+    override fun setAlpha(alpha: Int) {
+        paint.alpha = alpha
+    }
+
+    override fun setColorFilter(colorFilter: ColorFilter?) {
+        paint.colorFilter = colorFilter
+    }
+
+    /** Schedules path update for the next redraw */
+    private fun requestPathUpdate() {
+        shouldUpdatePath = true
+    }
+
+    /** Updates the path if required, when bounds or arrow direction/position changes */
+    private fun updatePathIfNeeded() {
+        if (shouldUpdatePath) {
+            updatePath()
+            shouldUpdatePath = false
+        }
+    }
+
+    /** Updates the path value using the current bounds, config, arrow direction and position */
+    private fun updatePath() {
+        if (bounds.isEmpty) return
+        // Reset the path state
+        path.reset()
+        // The content rect where the filled rounded rect will be drawn
+        val contentRect = RectF(bounds)
+        when (arrowDirection) {
+            ArrowDirection.UP -> {
+                // Add rounded arrow pointing up to the path
+                addRoundedArrowPositioned(path, arrowPosition)
+                // Inset content rect by the arrow size from the top
+                contentRect.top += config.arrowHeight
+            }
+            ArrowDirection.DOWN -> {
+                val matrix = Matrix()
+                // Flip the path with the matrix to draw arrow pointing down
+                matrix.setScale(1f, -1f, bounds.width() / 2f, bounds.height() / 2f)
+                path.transform(matrix)
+                // Add rounded arrow with the flipped matrix applied, will point down
+                addRoundedArrowPositioned(path, arrowPosition)
+                // Restore the path matrix to the original state with inverted matrix
+                matrix.invert(matrix)
+                path.transform(matrix)
+                // Inset content rect by the arrow size from the bottom
+                contentRect.bottom -= config.arrowHeight
+            }
+        }
+        // Add the content area rounded rect
+        path.addRoundRect(contentRect, config.cornerRadius, config.cornerRadius, Path.Direction.CW)
+    }
+
+    /** Add a rounded arrow pointing up in the horizontal position on the canvas */
+    private fun addRoundedArrowPositioned(path: Path, position: ArrowPosition) {
+        val matrix = Matrix()
+        var translationX = positionValue(position) - config.arrowWidth / 2
+        // Offset to position between rounded corners of the content view
+        translationX = translationX.coerceIn(config.cornerRadius,
+                bounds.width() - config.cornerRadius - config.arrowWidth)
+        // Translate to add the arrow in the center horizontally
+        matrix.setTranslate(-translationX, 0f)
+        path.transform(matrix)
+        // Add rounded arrow
+        addRoundedArrow(path)
+        // Restore the path matrix to the original state with inverted matrix
+        matrix.invert(matrix)
+        path.transform(matrix)
+    }
+
+    /** Adds a rounded arrow pointing up to the path, can be flipped if needed */
+    private fun addRoundedArrow(path: Path) {
+        // Theta is half of the angle inside the triangle tip
+        val thetaTan = config.arrowWidth / (config.arrowHeight * 2f)
+        val theta = atan(thetaTan)
+        val thetaDeg = Math.toDegrees(theta.toDouble()).toFloat()
+        // The center Y value of the circle for the triangle tip
+        val tipCircleCenterY = config.arrowRadius / sin(theta)
+        // The length from triangle tip to intersection point with the circle
+        val tipIntersectionSideLength = config.arrowRadius / thetaTan
+        // The offset from the top to the point of intersection
+        val intersectionTopOffset = tipIntersectionSideLength * cos(theta)
+        // The offset from the center to the point of intersection
+        val intersectionCenterOffset = tipIntersectionSideLength * sin(theta)
+        // The center X of the triangle
+        val arrowCenterX = config.arrowWidth / 2f
+
+        // Set initial position in bottom left of the arrow
+        path.moveTo(0f, config.arrowHeight)
+        // Add the left side of the triangle
+        path.lineTo(arrowCenterX - intersectionCenterOffset, intersectionTopOffset)
+        // Add the arc from the left to the right side of the triangle
+        path.arcTo(
+            /* left = */ arrowCenterX - config.arrowRadius,
+            /* top = */ tipCircleCenterY - config.arrowRadius,
+            /* right = */ arrowCenterX + config.arrowRadius,
+            /* bottom = */ tipCircleCenterY + config.arrowRadius,
+            /* startAngle = */ 180 + thetaDeg,
+            /* sweepAngle = */ 180 - (2 * thetaDeg),
+            /* forceMoveTo = */ false
+        )
+        // Add the right side of the triangle
+        path.lineTo(config.arrowWidth, config.arrowHeight)
+        // Close the path
+        path.close()
+    }
+
+    /** The value of the arrow position provided the position and current bounds */
+    private fun positionValue(position: ArrowPosition): Float {
+        return when (position) {
+            is ArrowPosition.Start -> 0f
+            is ArrowPosition.Center -> bounds.width().toFloat() / 2f
+            is ArrowPosition.End -> bounds.width().toFloat()
+            is ArrowPosition.Custom -> position.value
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.kt
new file mode 100644
index 0000000..802d7d1
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubblePopupView.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.wm.shell.shared.bubbles
+
+import android.content.Context
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/** A popup container view that uses [BubblePopupDrawable] as a background */
+open class BubblePopupView
+@JvmOverloads
+constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+    var popupDrawable: BubblePopupDrawable? = null
+        private set
+
+    /**
+     * Sets up the popup drawable with the config provided. Required to remove dependency on local
+     * resources
+     */
+    fun setupBackground(config: BubblePopupDrawable.Config) {
+        popupDrawable = BubblePopupDrawable(config)
+        background = popupDrawable
+        forceLayout()
+    }
+
+    /**
+     * Sets the arrow direction for the background drawable and updates the padding to fit the
+     * content inside of the popup drawable
+     */
+    fun setArrowDirection(direction: BubblePopupDrawable.ArrowDirection) {
+        popupDrawable?.let {
+            it.arrowDirection = direction
+            val padding = Rect()
+            if (it.getPadding(padding)) {
+                setPadding(padding.left, padding.top, padding.right, padding.bottom)
+            }
+        }
+    }
+
+    /** Sets the arrow position for the background drawable and triggers redraw */
+    fun setArrowPosition(position: BubblePopupDrawable.ArrowPosition) {
+        popupDrawable?.let {
+            it.arrowPosition = position
+            invalidate()
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
new file mode 100644
index 0000000..0c05156
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissCircleView.java
@@ -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.wm.shell.shared.bubbles;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.Gravity;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
+import androidx.core.content.ContextCompat;
+
+/**
+ * Circular view with a semitransparent, circular background with an 'X' inside it.
+ *
+ * This is used by both Bubbles and PIP as the dismiss target.
+ */
+public class DismissCircleView extends FrameLayout {
+    @DrawableRes int mBackgroundResId;
+    @DimenRes int mIconSizeResId;
+
+    private final ImageView mIconView = new ImageView(getContext());
+
+    public DismissCircleView(Context context) {
+        super(context);
+        addView(mIconView);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        setBackground(ContextCompat.getDrawable(getContext(), mBackgroundResId));
+        setViewSizes();
+    }
+
+    /**
+     * Sets up view with the provided resource ids.
+     * Decouples resource dependency in order to be used externally (e.g. Launcher)
+     *
+     * @param backgroundResId drawable resource id of the circle background
+     * @param iconResId drawable resource id of the icon for the dismiss view
+     * @param iconSizeResId dimen resource id of the icon size
+     */
+    public void setup(@DrawableRes int backgroundResId, @DrawableRes int iconResId,
+            @DimenRes int iconSizeResId) {
+        mBackgroundResId = backgroundResId;
+        mIconSizeResId = iconSizeResId;
+
+        setBackground(ContextCompat.getDrawable(getContext(), backgroundResId));
+        mIconView.setImageDrawable(ContextCompat.getDrawable(getContext(), iconResId));
+        setViewSizes();
+    }
+
+    /** Retrieves the current dimensions for the icon and circle and applies them. */
+    private void setViewSizes() {
+        final int iconSize = getResources().getDimensionPixelSize(mIconSizeResId);
+        mIconView.setLayoutParams(
+                new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
new file mode 100644
index 0000000..2bb66b0
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DismissView.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.shared.bubbles
+
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.GradientDrawable
+import android.util.IntProperty
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.widget.FrameLayout
+import androidx.annotation.ColorRes
+import androidx.annotation.DimenRes
+import androidx.annotation.DrawableRes
+import androidx.core.content.ContextCompat
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
+import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+
+/**
+ * View that handles interactions between DismissCircleView and BubbleStackView.
+ *
+ * @note [setup] method should be called after initialisation
+ */
+class DismissView(context: Context) : FrameLayout(context) {
+    /**
+     * The configuration is used to provide module specific resource ids
+     *
+     * @see [setup] method
+     */
+    data class Config(
+            /** The resource id to set on the dismiss target circle view */
+            val dismissViewResId: Int,
+            /** dimen resource id of the dismiss target circle view size */
+            @DimenRes val targetSizeResId: Int,
+            /** dimen resource id of the icon size in the dismiss target */
+            @DimenRes val iconSizeResId: Int,
+            /** dimen resource id of the bottom margin for the dismiss target */
+            @DimenRes var bottomMarginResId: Int,
+            /** dimen resource id of the height for dismiss area gradient */
+            @DimenRes val floatingGradientHeightResId: Int,
+            /** color resource id of the dismiss area gradient color */
+            @ColorRes val floatingGradientColorResId: Int,
+            /** drawable resource id of the dismiss target background */
+            @DrawableRes val backgroundResId: Int,
+            /** drawable resource id of the icon for the dismiss target */
+            @DrawableRes val iconResId: Int
+    )
+
+    companion object {
+        private const val SHOULD_SETUP =
+                "The view isn't ready. Should be called after `setup`"
+        private val TAG = DismissView::class.simpleName
+    }
+
+    var circle = DismissCircleView(context)
+    var isShowing = false
+    var config: Config? = null
+
+    private val animator = PhysicsAnimator.getInstance(circle)
+    private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
+    private val DISMISS_SCRIM_FADE_MS = 200L
+    private var wm: WindowManager =
+            context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+    private var gradientDrawable: GradientDrawable? = null
+
+    private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
+            object : IntProperty<GradientDrawable>("alpha") {
+        override fun setValue(d: GradientDrawable, percent: Int) {
+            d.alpha = percent
+        }
+        override fun get(d: GradientDrawable): Int {
+            return d.alpha
+        }
+    }
+
+    init {
+        setClipToPadding(false)
+        setClipChildren(false)
+        setVisibility(View.INVISIBLE)
+        addView(circle)
+    }
+
+    /**
+     * Sets up view with the provided resource ids.
+     *
+     * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called
+     * with default params in module specific extension:
+     * @see [DismissView.setup] in DismissViewExt.kt
+     */
+    fun setup(config: Config) {
+        this.config = config
+
+        // Setup layout
+        layoutParams = LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                resources.getDimensionPixelSize(config.floatingGradientHeightResId),
+                Gravity.BOTTOM)
+        updatePadding()
+
+        // Setup gradient
+        gradientDrawable = createGradient(color = config.floatingGradientColorResId)
+        setBackgroundDrawable(gradientDrawable)
+
+        // Setup DismissCircleView
+        circle.id = config.dismissViewResId
+        circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId)
+        val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId)
+        circle.layoutParams = LayoutParams(targetSize, targetSize,
+                Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
+        // Initial position with circle offscreen so it's animated up
+        circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
+                .toFloat()
+    }
+
+    /**
+     * Animates this view in.
+     */
+    fun show() {
+        if (isShowing) return
+        val gradientDrawable = checkExists(gradientDrawable) ?: return
+        isShowing = true
+        setVisibility(View.VISIBLE)
+        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+                gradientDrawable.alpha, 255)
+        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+        alphaAnim.start()
+
+        animator.cancel()
+        animator
+            .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+            .start()
+    }
+
+    /**
+     * Animates this view out, as well as the circle that encircles the bubbles, if they
+     * were dragged into the target and encircled.
+     */
+    fun hide() {
+        if (!isShowing) return
+        val gradientDrawable = checkExists(gradientDrawable) ?: return
+        isShowing = false
+        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+                gradientDrawable.alpha, 0)
+        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+        alphaAnim.start()
+        animator
+            .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+                spring)
+            .withEndActions({
+                visibility = View.INVISIBLE
+                circle.scaleX = 1f
+                circle.scaleY = 1f
+            })
+            .start()
+    }
+
+    /**
+     * Cancels the animator for the dismiss target.
+     */
+    fun cancelAnimators() {
+        animator.cancel()
+    }
+
+    fun updateResources() {
+        val config = checkExists(config) ?: return
+        updatePadding()
+        layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
+        val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
+        circle.layoutParams.width = targetSize
+        circle.layoutParams.height = targetSize
+        circle.requestLayout()
+    }
+
+    private fun createGradient(@ColorRes color: Int): GradientDrawable {
+        val gradientColor = ContextCompat.getColor(context, color)
+        val alpha = 0.7f * 255
+        val gradientColorWithAlpha = Color.argb(alpha.toInt(),
+                Color.red(gradientColor),
+                Color.green(gradientColor),
+                Color.blue(gradientColor))
+        val gd = GradientDrawable(
+                GradientDrawable.Orientation.BOTTOM_TOP,
+                intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
+        gd.setDither(true)
+        gd.setAlpha(0)
+        return gd
+    }
+
+    private fun updatePadding() {
+        val config = checkExists(config) ?: return
+        val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
+        val navInset = insets.getInsetsIgnoringVisibility(
+                WindowInsets.Type.navigationBars())
+        setPadding(0, 0, 0, navInset.bottom +
+                resources.getDimensionPixelSize(config.bottomMarginResId))
+    }
+
+    /**
+     * Checks if the value is set up and exists, if not logs an exception.
+     * Used for convenient logging in case `setup` wasn't called before
+     *
+     * @return value provided as argument
+     */
+    private fun <T>checkExists(value: T?): T? {
+        if (value == null) Log.e(TAG, SHOULD_SETUP)
+        return value
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/OWNERS
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/OWNERS
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
new file mode 100644
index 0000000..b1f4e33
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RelativeTouchListener.kt
@@ -0,0 +1,180 @@
+/*
+ * 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.wm.shell.shared.bubbles
+
+import android.graphics.PointF
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewConfiguration
+import kotlin.math.hypot
+
+/**
+ * Listener which receives [onDown], [onMove], and [onUp] events, with relevant information about
+ * the coordinates of the touch and the view relative to the initial ACTION_DOWN event and the
+ * view's initial position.
+ */
+abstract class RelativeTouchListener : View.OnTouchListener {
+
+    /**
+     * Called when an ACTION_DOWN event is received for the given view.
+     *
+     * @return False if the object is not interested in MotionEvents at this time, or true if we
+     * should consume this event and subsequent events, and begin calling [onMove].
+     */
+    abstract fun onDown(v: View, ev: MotionEvent): Boolean
+
+    /**
+     * Called when an ACTION_MOVE event is received for the given view. This signals that the view
+     * is being dragged.
+     *
+     * @param viewInitialX The view's translationX value when this touch gesture started.
+     * @param viewInitialY The view's translationY value when this touch gesture started.
+     * @param dx Horizontal distance covered since the initial ACTION_DOWN event, in pixels.
+     * @param dy Vertical distance covered since the initial ACTION_DOWN event, in pixels.
+     */
+    abstract fun onMove(
+        v: View,
+        ev: MotionEvent,
+        viewInitialX: Float,
+        viewInitialY: Float,
+        dx: Float,
+        dy: Float
+    )
+
+    /**
+     * Called when an ACTION_UP event is received for the given view. This signals that a drag or
+     * fling gesture has completed.
+     *
+     * @param viewInitialX The view's translationX value when this touch gesture started.
+     * @param viewInitialY The view's translationY value when this touch gesture started.
+     * @param dx Horizontal distance covered, in pixels.
+     * @param dy Vertical distance covered, in pixels.
+     * @param velX The final horizontal velocity of the gesture, in pixels/second.
+     * @param velY The final vertical velocity of the gesture, in pixels/second.
+     */
+    abstract fun onUp(
+        v: View,
+        ev: MotionEvent,
+        viewInitialX: Float,
+        viewInitialY: Float,
+        dx: Float,
+        dy: Float,
+        velX: Float,
+        velY: Float
+    )
+
+    open fun onCancel(
+        v: View,
+        ev: MotionEvent,
+        viewInitialX: Float,
+        viewInitialY: Float
+    ) {}
+
+    /** The raw coordinates of the last ACTION_DOWN event. */
+    private var touchDown: PointF? = null
+
+    /** The coordinates of the view, at the time of the last ACTION_DOWN event. */
+    private val viewPositionOnTouchDown = PointF()
+
+    private val velocityTracker = VelocityTracker.obtain()
+
+    private var touchSlop: Int = -1
+    private var movedEnough = false
+
+    private var performedLongClick = false
+
+    override fun onTouch(v: View, ev: MotionEvent): Boolean {
+        addMovement(ev)
+
+        val dx = touchDown?.let { ev.rawX - it.x } ?: 0f
+        val dy = touchDown?.let { ev.rawY - it.y } ?: 0f
+
+        when (ev.action) {
+            MotionEvent.ACTION_DOWN -> {
+                if (!onDown(v, ev)) {
+                    return false
+                }
+
+                // Grab the touch slop, it might have changed if the config changed since the
+                // last gesture.
+                touchSlop = ViewConfiguration.get(v.context).scaledTouchSlop
+
+                touchDown = PointF(ev.rawX, ev.rawY)
+                viewPositionOnTouchDown.set(v.translationX, v.translationY)
+
+                performedLongClick = false
+                v.handler?.postDelayed({
+                    if (v.isLongClickable) {
+                        performedLongClick = v.performLongClick()
+                    }
+                }, ViewConfiguration.getLongPressTimeout().toLong())
+            }
+
+            MotionEvent.ACTION_MOVE -> {
+                if (touchDown == null) return false
+                if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
+                    movedEnough = true
+                    v.handler?.removeCallbacksAndMessages(null)
+                }
+
+                if (movedEnough) {
+                    onMove(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy)
+                }
+            }
+
+            MotionEvent.ACTION_UP -> {
+                if (touchDown == null) return false
+                if (movedEnough) {
+                    velocityTracker.computeCurrentVelocity(1000 /* units */)
+                    onUp(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy,
+                            velocityTracker.xVelocity, velocityTracker.yVelocity)
+                } else if (!performedLongClick) {
+                    v.performClick()
+                } else {
+                    v.handler?.removeCallbacksAndMessages(null)
+                }
+
+                velocityTracker.clear()
+                movedEnough = false
+                touchDown = null
+            }
+
+            MotionEvent.ACTION_CANCEL -> {
+                if (touchDown == null) return false
+                v.handler?.removeCallbacksAndMessages(null)
+                velocityTracker.clear()
+                movedEnough = false
+                touchDown = null
+                onCancel(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y)
+            }
+        }
+
+        return true
+    }
+
+    /**
+     * Adds a movement to the velocity tracker using raw screen coordinates.
+     */
+    private fun addMovement(event: MotionEvent) {
+        val deltaX = event.rawX - event.x
+        val deltaY = event.rawY - event.y
+        event.offsetLocation(deltaX, deltaY)
+        velocityTracker.addMovement(event)
+        event.offsetLocation(-deltaX, -deltaY)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
new file mode 100644
index 0000000..c83696c
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/RemovedBubble.java
@@ -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.wm.shell.shared.bubbles;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a removed bubble, defining the key and reason the bubble was removed.
+ */
+public class RemovedBubble implements Parcelable {
+
+    private final String mKey;
+    private final int mRemovalReason;
+
+    public RemovedBubble(String key, int removalReason) {
+        mKey = key;
+        mRemovalReason = removalReason;
+    }
+
+    public RemovedBubble(Parcel parcel) {
+        mKey = parcel.readString();
+        mRemovalReason = parcel.readInt();
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    public int getRemovalReason() {
+        return mRemovalReason;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mKey);
+        dest.writeInt(mRemovalReason);
+    }
+
+    @NonNull
+    public static final Creator<RemovedBubble> CREATOR =
+            new Creator<RemovedBubble>() {
+                public RemovedBubble createFromParcel(Parcel source) {
+                    return new RemovedBubble(source);
+                }
+                public RemovedBubble[] newArray(int size) {
+                    return new RemovedBubble[size];
+                }
+            };
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
index 434885f..424d4bf 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeFlags.kt
@@ -22,10 +22,14 @@
 import com.android.window.flags.Flags
 
 /*
- * A shared class to check desktop mode flags state.
+ * An enum to check desktop mode flags state.
  *
- * The class computes whether a Desktop Windowing flag should be enabled by using the aconfig flag
- * value and the developer option override state (if applicable).
+ * This enum provides a centralized way to control the behavior of flags related to desktop
+ * windowing features which are aiming for developer preview before their release. It allows
+ * developer option to override the default behavior of these flags.
+ *
+ * NOTE: Flags should only be added to this enum when they have received Product and UX
+ * alignment that the feature is ready for developer preview, otherwise just do a flag check.
  */
 enum class DesktopModeFlags(
     // Function called to obtain aconfig flag value.
@@ -44,7 +48,7 @@
     TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
     SIZE_CONSTRAINTS(Flags::enableDesktopWindowingSizeConstraints, true),
     DISABLE_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
-    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
+    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false),
     ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
     BACK_NAVIGATION(Flags::enableDesktopWindowingBackNavigation, true),
     EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
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 282385a..341ca0e 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
@@ -24,6 +24,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
 
+import java.io.PrintWriter;
+
 /**
  * Constants for desktop mode feature
  */
@@ -203,4 +205,19 @@
     private static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
         return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
     }
+
+    /** Dumps DesktopModeStatus flags and configs. */
+    public static void dump(PrintWriter pw, String prefix, Context context) {
+        String innerPrefix = prefix + "  ";
+        pw.print(prefix); pw.println(TAG);
+        pw.print(innerPrefix); pw.print("maxTaskLimit="); pw.println(getMaxTaskLimit(context));
+
+        pw.print(innerPrefix); pw.print("maxTaskLimit config override=");
+        pw.println(context.getResources().getInteger(
+                R.integer.config_maxDesktopWindowingActiveTasks));
+
+        SystemProperties.Handle maxTaskLimitHandle = SystemProperties.find(MAX_TASK_LIMIT_SYS_PROP);
+        pw.print(innerPrefix); pw.print("maxTaskLimit sysprop=");
+        pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
+    }
 }
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.aidl
new file mode 100644
index 0000000..f7ddf71
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.shared.desktopmode;
+
+parcelable DesktopModeTransitionSource;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.kt
new file mode 100644
index 0000000..d15fbed
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeTransitionSource.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.shared.desktopmode
+
+import android.os.Parcel
+import android.os.Parcelable
+
+/** Transition source types for Desktop Mode. */
+enum class DesktopModeTransitionSource : Parcelable {
+    /** Transitions that originated as a consequence of task dragging. */
+    TASK_DRAG,
+    /** Transitions that originated from an app from Overview. */
+    APP_FROM_OVERVIEW,
+    /** Transitions that originated from app handle menu button */
+    APP_HANDLE_MENU_BUTTON,
+    /** Transitions that originated as a result of keyboard shortcuts. */
+    KEYBOARD_SHORTCUT,
+    /** Transitions with source unknown. */
+    UNKNOWN;
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    override fun writeToParcel(dest: Parcel, flags: Int) {
+        dest.writeString(name)
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR =
+            object : Parcelable.Creator<DesktopModeTransitionSource> {
+                override fun createFromParcel(parcel: Parcel): DesktopModeTransitionSource {
+                    return parcel.readString()?.let { valueOf(it) } ?: UNKNOWN
+                }
+
+                override fun newArray(size: Int) = arrayOfNulls<DesktopModeTransitionSource>(size)
+            }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/draganddrop/DragAndDropConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/draganddrop/DragAndDropConstants.java
new file mode 100644
index 0000000..4127adc
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/draganddrop/DragAndDropConstants.java
@@ -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.wm.shell.shared.draganddrop;
+
+/** Constants that can be used by both Shell and other users of the library, e.g. Launcher */
+public class DragAndDropConstants {
+
+    /**
+     * An Intent extra that Launcher can use to specify a region of the screen where Shell should
+     * ignore drag events.
+     */
+    public static final String EXTRA_DISALLOW_HIT_REGION = "DISALLOW_HIT_REGION";
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
new file mode 100644
index 0000000..b92b8ef
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
@@ -0,0 +1,359 @@
+/*
+ * 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.wm.shell.shared.handles;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.annotation.TargetApi;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Handler;
+import android.view.CompositionSamplingListener;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+/**
+ * A helper class to sample regions on the screen and inspect its luminosity.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
+        View.OnLayoutChangeListener {
+
+    // Luminance threshold to determine black/white contrast for the navigation affordances.
+    // Passing the threshold of this luminance value will make the button black otherwise white
+    private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f;
+    // Luminance change threshold that allows applying new value if difference was exceeded
+    private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f;
+
+    private final Handler mHandler = new Handler();
+    private final View mSampledView;
+
+    private final CompositionSamplingListener mSamplingListener;
+
+    /**
+     * The requested sampling bounds that we want to sample from
+     */
+    private final Rect mSamplingRequestBounds = new Rect();
+
+    /**
+     * The sampling bounds that are currently registered.
+     */
+    private final Rect mRegisteredSamplingBounds = new Rect();
+    private final SamplingCallback mCallback;
+    private final Executor mBackgroundExecutor;
+    private final SysuiCompositionSamplingListener mCompositionSamplingListener;
+    private boolean mSamplingEnabled = false;
+    private boolean mSamplingListenerRegistered = false;
+
+    private float mLastMedianLuma;
+    private float mCurrentMedianLuma;
+    private boolean mWaitingOnDraw;
+    private boolean mIsDestroyed;
+
+    private boolean mFirstSamplingAfterStart;
+    private boolean mWindowVisible;
+    private boolean mWindowHasBlurs;
+    private SurfaceControl mRegisteredStopLayer = null;
+    // A copy of mRegisteredStopLayer where we own the life cycle and can access from a bg thread.
+    private SurfaceControl mWrappedStopLayer = null;
+    private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() {
+        @Override
+        public void onDraw() {
+            // We need to post the remove runnable, since it's not allowed to remove in onDraw
+            mHandler.post(mRemoveDrawRunnable);
+            RegionSamplingHelper.this.onDraw();
+        }
+    };
+    private Runnable mRemoveDrawRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mSampledView.getViewTreeObserver().removeOnDrawListener(mUpdateOnDraw);
+        }
+    };
+
+    /**
+     * @deprecated Pass a main executor.
+     */
+    public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+            Executor backgroundExecutor) {
+        this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(),
+                backgroundExecutor);
+    }
+
+    public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+            Executor mainExecutor, Executor backgroundExecutor) {
+        this(sampledView, samplingCallback, mainExecutor,
+                backgroundExecutor, new SysuiCompositionSamplingListener());
+    }
+
+    @VisibleForTesting
+    RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+            Executor mainExecutor, Executor backgroundExecutor,
+            SysuiCompositionSamplingListener compositionSamplingListener) {
+        mBackgroundExecutor = backgroundExecutor;
+        mCompositionSamplingListener = compositionSamplingListener;
+        mSamplingListener = new CompositionSamplingListener(mainExecutor) {
+            @Override
+            public void onSampleCollected(float medianLuma) {
+                if (mSamplingEnabled) {
+                    updateMedianLuma(medianLuma);
+                }
+            }
+        };
+        mSampledView = sampledView;
+        mSampledView.addOnAttachStateChangeListener(this);
+        mSampledView.addOnLayoutChangeListener(this);
+
+        mCallback = samplingCallback;
+    }
+
+    /**
+     * Make callback accessible
+     */
+    @VisibleForTesting
+    public SamplingCallback getCallback() {
+        return mCallback;
+    }
+
+    private void onDraw() {
+        if (mWaitingOnDraw) {
+            mWaitingOnDraw = false;
+            updateSamplingListener();
+        }
+    }
+
+    public void start(Rect initialSamplingBounds) {
+        if (!mCallback.isSamplingEnabled()) {
+            return;
+        }
+        if (initialSamplingBounds != null) {
+            mSamplingRequestBounds.set(initialSamplingBounds);
+        }
+        mSamplingEnabled = true;
+        // make sure we notify once
+        mLastMedianLuma = -1;
+        mFirstSamplingAfterStart = true;
+        updateSamplingListener();
+    }
+
+    public void stop() {
+        mSamplingEnabled = false;
+        updateSamplingListener();
+    }
+
+    public void stopAndDestroy() {
+        stop();
+        mBackgroundExecutor.execute(mSamplingListener::destroy);
+        mIsDestroyed = true;
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View view) {
+        updateSamplingListener();
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View view) {
+        stopAndDestroy();
+    }
+
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        updateSamplingRect();
+    }
+
+    private void updateSamplingListener() {
+        boolean isSamplingEnabled = mSamplingEnabled
+                && !mSamplingRequestBounds.isEmpty()
+                && mWindowVisible
+                && !mWindowHasBlurs
+                && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
+        if (isSamplingEnabled) {
+            ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
+            SurfaceControl stopLayerControl = null;
+            if (viewRootImpl != null) {
+                stopLayerControl = viewRootImpl.getSurfaceControl();
+            }
+            if (stopLayerControl == null || !stopLayerControl.isValid()) {
+                if (!mWaitingOnDraw) {
+                    mWaitingOnDraw = true;
+                    // The view might be attached but we haven't drawn yet, so wait until the
+                    // next draw to update the listener again with the stop layer, such that our
+                    // own drawing doesn't affect the sampling.
+                    if (mHandler.hasCallbacks(mRemoveDrawRunnable)) {
+                        mHandler.removeCallbacks(mRemoveDrawRunnable);
+                    } else {
+                        mSampledView.getViewTreeObserver().addOnDrawListener(mUpdateOnDraw);
+                    }
+                }
+                // If there's no valid surface, let's just sample without a stop layer, so we
+                // don't have to delay
+                stopLayerControl = null;
+            }
+            if (!mSamplingRequestBounds.equals(mRegisteredSamplingBounds)
+                    || mRegisteredStopLayer != stopLayerControl) {
+                // We only want to re-register if something actually changed
+                unregisterSamplingListener();
+                mSamplingListenerRegistered = true;
+                SurfaceControl wrappedStopLayer = wrap(stopLayerControl);
+
+                // pass this to background thread to avoid empty Rect race condition
+                final Rect boundsCopy = new Rect(mSamplingRequestBounds);
+
+                mBackgroundExecutor.execute(() -> {
+                    if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) {
+                        return;
+                    }
+                    mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
+                            wrappedStopLayer, boundsCopy);
+                });
+                mRegisteredSamplingBounds.set(mSamplingRequestBounds);
+                mRegisteredStopLayer = stopLayerControl;
+                mWrappedStopLayer = wrappedStopLayer;
+            }
+            mFirstSamplingAfterStart = false;
+        } else {
+            unregisterSamplingListener();
+        }
+    }
+
+    @VisibleForTesting
+    protected SurfaceControl wrap(SurfaceControl stopLayerControl) {
+        return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl,
+                "regionSampling");
+    }
+
+    private void unregisterSamplingListener() {
+        if (mSamplingListenerRegistered) {
+            mSamplingListenerRegistered = false;
+            SurfaceControl wrappedStopLayer = mWrappedStopLayer;
+            mRegisteredStopLayer = null;
+            mWrappedStopLayer = null;
+            mRegisteredSamplingBounds.setEmpty();
+            mBackgroundExecutor.execute(() -> {
+                mCompositionSamplingListener.unregister(mSamplingListener);
+                if (wrappedStopLayer != null && wrappedStopLayer.isValid()) {
+                    wrappedStopLayer.release();
+                }
+            });
+        }
+    }
+
+    private void updateMedianLuma(float medianLuma) {
+        mCurrentMedianLuma = medianLuma;
+
+        // If the difference between the new luma and the current luma is larger than threshold
+        // then apply the current luma, this is to prevent small changes causing colors to flicker
+        if (Math.abs(mCurrentMedianLuma - mLastMedianLuma)
+                > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) {
+            mCallback.onRegionDarknessChanged(
+                    medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */);
+            mLastMedianLuma = medianLuma;
+        }
+    }
+
+    public void updateSamplingRect() {
+        Rect sampledRegion = mCallback.getSampledRegion(mSampledView);
+        if (!mSamplingRequestBounds.equals(sampledRegion)) {
+            mSamplingRequestBounds.set(sampledRegion);
+            updateSamplingListener();
+        }
+    }
+
+    public void setWindowVisible(boolean visible) {
+        mWindowVisible = visible;
+        updateSamplingListener();
+    }
+
+    /**
+     * If we're blurring the shade window.
+     */
+    public void setWindowHasBlurs(boolean hasBlurs) {
+        mWindowHasBlurs = hasBlurs;
+        updateSamplingListener();
+    }
+
+    public void dump(PrintWriter pw) {
+        dump("", pw);
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "RegionSamplingHelper:");
+        pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow());
+        pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow()
+                ? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
+                : "notAttached"));
+        pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled);
+        pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered);
+        pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds);
+        pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
+        pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma);
+        pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma);
+        pw.println(prefix + "\tmWindowVisible: " + mWindowVisible);
+        pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs);
+        pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw);
+        pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer);
+        pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer);
+        pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed);
+    }
+
+    public interface SamplingCallback {
+        /**
+         * Called when the darkness of the sampled region changes
+         * @param isRegionDark true if the sampled luminance is below the luminance threshold
+         */
+        void onRegionDarknessChanged(boolean isRegionDark);
+
+        /**
+         * Get the sampled region of interest from the sampled view
+         * @param sampledView The view that this helper is attached to for convenience
+         * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid
+         * sampling in this frame
+         */
+        Rect getSampledRegion(View sampledView);
+
+        /**
+         * @return if sampling should be enabled in the current configuration
+         */
+        default boolean isSamplingEnabled() {
+            return true;
+        }
+    }
+
+    @VisibleForTesting
+    public static class SysuiCompositionSamplingListener {
+        public void register(CompositionSamplingListener listener,
+                int displayId, SurfaceControl stopLayer, Rect samplingArea) {
+            CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea);
+        }
+
+        /**
+         * Unregisters a sampling listener.
+         */
+        public void unregister(CompositionSamplingListener listener) {
+            CompositionSamplingListener.unregister(listener);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObject.kt
new file mode 100644
index 0000000..efdc6f8
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObject.kt
@@ -0,0 +1,702 @@
+/*
+ * 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.shared.magnetictarget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.PointF
+import android.os.VibrationAttributes
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.view.MotionEvent
+import android.view.VelocityTracker
+import android.view.View
+import android.view.ViewConfiguration
+import androidx.dynamicanimation.animation.DynamicAnimation
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.dynamicanimation.animation.SpringForce
+import com.android.wm.shell.shared.animation.PhysicsAnimator
+import kotlin.math.abs
+import kotlin.math.hypot
+
+/**
+ * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic
+ * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless
+ * they're moved away or released. Releasing objects inside a magnetic target typically performs an
+ * action on the object.
+ *
+ * MagnetizedObject also supports flinging to targets, which will result in the object being pulled
+ * into the target and released as if it was dragged into it.
+ *
+ * To use this class, either construct an instance with an object of arbitrary type, or use the
+ * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set
+ * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents
+ * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the
+ * event consumed by the MagnetizedObject and don't move the object unless it begins returning false
+ * again.
+ *
+ * @param context Context, used to retrieve a Vibrator instance for vibration effects.
+ * @param underlyingObject The actual object that we're magnetizing.
+ * @param xProperty Property that sets the x value of the object's position.
+ * @param yProperty Property that sets the y value of the object's position.
+ */
+abstract class MagnetizedObject<T : Any>(
+    val context: Context,
+
+    /** The actual object that is animated. */
+    val underlyingObject: T,
+
+    /** Property that gets/sets the object's X value. */
+    val xProperty: FloatPropertyCompat<in T>,
+
+    /** Property that gets/sets the object's Y value. */
+    val yProperty: FloatPropertyCompat<in T>
+) {
+
+    /** Return the width of the object. */
+    abstract fun getWidth(underlyingObject: T): Float
+
+    /** Return the height of the object. */
+    abstract fun getHeight(underlyingObject: T): Float
+
+    /**
+     * Fill the provided array with the location of the top-left of the object, relative to the
+     * entire screen. Compare to [View.getLocationOnScreen].
+     */
+    abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray)
+
+    /** Methods for listening to events involving a magnetized object.  */
+    interface MagnetListener {
+
+        /**
+         * Called when touch events move within the magnetic field of a target, causing the
+         * object to animate to the target and become 'stuck' there. The animation happens
+         * automatically here - you should not move the object. You can, however, change its state
+         * to indicate to the user that it's inside the target and releasing it will have an effect.
+         *
+         * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call
+         * to [onUnstuckFromTarget] or [onReleasedInTarget].
+         *
+         * @param target The target that the object is now stuck to.
+         * @param draggedObject The object that is stuck to the target.
+         */
+        fun onStuckToTarget(target: MagneticTarget, draggedObject: MagnetizedObject<*>)
+
+        /**
+         * Called when the object is no longer stuck to a target. This means that either touch
+         * events moved outside of the magnetic field radius, or that a forceful fling out of the
+         * target was detected.
+         *
+         * The object won't be automatically animated out of the target, since you're responsible
+         * for moving the object again. You should move it (or animate it) using your own
+         * movement/animation logic.
+         *
+         * Reverse any effects applied in [onStuckToTarget] here.
+         *
+         * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event
+         * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing
+         * and [maybeConsumeMotionEvent] is now returning false.
+         *
+         * @param target The target that this object was just unstuck from.
+         * @param draggedObject The object being unstuck from the target.
+         * @param velX The X velocity of the touch gesture when it exited the magnetic field.
+         * @param velY The Y velocity of the touch gesture when it exited the magnetic field.
+         * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that
+         * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude
+         * that the user wants to un-stick the object despite no touch events occurring outside of
+         * the magnetic field radius.
+         */
+        fun onUnstuckFromTarget(
+            target: MagneticTarget,
+            draggedObject: MagnetizedObject<*>,
+            velX: Float,
+            velY: Float,
+            wasFlungOut: Boolean
+        )
+
+        /**
+         * Called when the object is released inside a target, or flung towards it with enough
+         * velocity to reach it.
+         *
+         * @param target The target that the object was released in.
+         * @param draggedObject The object released in the target.
+         */
+        fun onReleasedInTarget(target: MagneticTarget, draggedObject: MagnetizedObject<*>)
+    }
+
+    private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject)
+    private val objectLocationOnScreen = IntArray(2)
+
+    /**
+     * Targets that have been added to this object. These will all be considered when determining
+     * magnetic fields and fling trajectories.
+     */
+    private val associatedTargets = ArrayList<MagneticTarget>()
+
+    private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
+    private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+    private val vibrationAttributes: VibrationAttributes = VibrationAttributes.createForUsage(
+            VibrationAttributes.USAGE_TOUCH)
+
+    private var touchDown = PointF()
+    private var touchSlop = 0
+    private var movedBeyondSlop = false
+
+    /** Whether touch events are presently occurring within the magnetic field area of a target. */
+    val objectStuckToTarget: Boolean
+        get() = targetObjectIsStuckTo != null
+
+    /** The target the object is stuck to, or null if the object is not stuck to any target. */
+    private var targetObjectIsStuckTo: MagneticTarget? = null
+
+    /**
+     * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent]
+     * will always return false and no magnetic effects will occur.
+     */
+    lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+    /**
+     * Optional update listener to provide to the PhysicsAnimator that is used to spring the object
+     * into the target.
+     */
+    var physicsAnimatorUpdateListener: PhysicsAnimator.UpdateListener<T>? = null
+
+    /**
+     * Optional end listener to provide to the PhysicsAnimator that is used to spring the object
+     * into the target.
+     */
+    var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
+
+    /**
+     * Method that is called when the object should be animated stuck to the target. The default
+     * implementation uses the object's x and y properties to animate the object centered inside the
+     * target. You can override this if you need custom animation.
+     *
+     * The method is invoked with the MagneticTarget that the object is sticking to, the X and Y
+     * velocities of the gesture that brought the object into the magnetic radius, whether or not it
+     * was flung, and a callback you must call after your animation completes.
+     */
+    var animateStuckToTarget: (MagneticTarget, Float, Float, Boolean, (() -> Unit)?) -> Unit =
+            ::animateStuckToTargetInternal
+
+    /**
+     * Sets whether forcefully flinging the object vertically towards a target causes it to be
+     * attracted to the target and then released immediately, despite never being dragged within the
+     * magnetic field.
+     */
+    var flingToTargetEnabled = true
+
+    /**
+     * If fling to target is enabled, forcefully flinging the object towards a target will cause
+     * it to be attracted to the target and then released immediately, despite never being dragged
+     * within the magnetic field.
+     *
+     * This sets the width of the area considered 'near' enough a target to be considered a fling,
+     * in terms of percent of the target view's width. For example, setting this to 3f means that
+     * flings towards a 100px-wide target will be considered 'near' enough if they're towards the
+     * 300px-wide area around the target.
+     *
+     * Flings whose trajectory intersects the area will be attracted and released - even if the
+     * target view itself isn't intersected:
+     *
+     * |             |
+     * |           0 |
+     * |          /  |
+     * |         /   |
+     * |      X /    |
+     * |.....###.....|
+     *
+     *
+     * Flings towards the target whose trajectories do not intersect the area will be treated as
+     * normal flings and the magnet will leave the object alone:
+     *
+     * |             |
+     * |             |
+     * |   0         |
+     * |  /          |
+     * | /    X      |
+     * |.....###.....|
+     *
+     */
+    var flingToTargetWidthPercent = 3f
+
+    /**
+     * Sets the minimum velocity (in pixels per second) required to fling an object to the target
+     * without dragging it into the magnetic field.
+     */
+    var flingToTargetMinVelocity = 4000f
+
+    /**
+     * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck
+     * to the target. If this velocity is reached, the object will be freed even if it wasn't moved
+     * outside the magnetic field radius.
+     */
+    var flingUnstuckFromTargetMinVelocity = 4000f
+
+    /**
+     * Sets the maximum X velocity above which the object will not stick to the target. Even if the
+     * object is dragged through the magnetic field, it will not stick to the target until the
+     * horizontal velocity is below this value.
+     */
+    var stickToTargetMaxXVelocity = 2000f
+
+    /**
+     * Enable or disable haptic vibration effects when the object interacts with the magnetic field.
+     *
+     * If you're experiencing crashes when the object enters targets, ensure that you have the
+     * android.permission.VIBRATE permission!
+     */
+    var hapticsEnabled = true
+
+    /** Default spring configuration to use for animating the object into a target. */
+    var springConfig = PhysicsAnimator.SpringConfig(
+            SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+
+    /**
+     * Spring configuration to use to spring the object into a target specifically when it's flung
+     * towards (rather than dragged near) it.
+     */
+    var flungIntoTargetSpringConfig = springConfig
+
+    /**
+     * Adds the provided MagneticTarget to this object. The object will now be attracted to the
+     * target if it strays within its magnetic field or is flung towards it.
+     *
+     * If this target (or its magnetic field) overlaps another target added to this object, the
+     * prior target will take priority.
+     */
+    fun addTarget(target: MagneticTarget) {
+        associatedTargets.add(target)
+        target.updateLocationOnScreen()
+    }
+
+    /**
+     * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target.
+     *
+     * @return The MagneticTarget instance for the given View. This can be used to change the
+     * target's magnetic field radius after it's been added. It can also be added to other
+     * magnetized objects.
+     */
+    fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget {
+        return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) }
+    }
+
+    /**
+     * Removes the given target from this object. The target will no longer attract the object.
+     */
+    fun removeTarget(target: MagneticTarget) {
+        associatedTargets.remove(target)
+    }
+
+    /**
+     * Removes all associated targets from this object.
+     */
+    fun clearAllTargets() {
+        associatedTargets.clear()
+    }
+
+    /**
+     * Provide this method with all motion events that move the magnetized object. If the
+     * location of the motion events moves within the magnetic field of a target, or indicate a
+     * fling-to-target gesture, this method will return true and you should not move the object
+     * yourself until it returns false again.
+     *
+     * Note that even when this method returns true, you should continue to pass along new motion
+     * events so that we know when the events move back outside the magnetic field area.
+     *
+     * This method will always return false if you haven't set a [magnetListener].
+     */
+    fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean {
+        // Short-circuit if we don't have a listener or any targets, since those are required.
+        if (associatedTargets.size == 0) {
+            return false
+        }
+
+        // When a gesture begins, recalculate target views' positions on the screen in case they
+        // have changed. Also, clear state.
+        if (ev.action == MotionEvent.ACTION_DOWN) {
+            updateTargetViews()
+
+            // Clear the velocity tracker and stuck target.
+            velocityTracker.clear()
+            targetObjectIsStuckTo = null
+
+            // Set the touch down coordinates and reset movedBeyondSlop.
+            touchDown.set(ev.rawX, ev.rawY)
+            movedBeyondSlop = false
+        }
+
+        // Always pass events to the VelocityTracker.
+        addMovement(ev)
+
+        // If we haven't yet moved beyond the slop distance, check if we have.
+        if (!movedBeyondSlop) {
+            val dragDistance = hypot(ev.rawX - touchDown.x, ev.rawY - touchDown.y)
+            if (dragDistance > touchSlop) {
+                // If we're beyond the slop distance, save that and continue.
+                movedBeyondSlop = true
+            } else {
+                // Otherwise, don't do anything yet.
+                return false
+            }
+        }
+
+        val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
+            val distanceFromTargetCenter = hypot(
+                    ev.rawX - target.centerOnDisplayX(),
+                    ev.rawY - target.centerOnDisplayY())
+            distanceFromTargetCenter < target.magneticFieldRadiusPx
+        }
+
+        // If we aren't currently stuck to a target, and we're in the magnetic field of a target,
+        // we're newly stuck.
+        val objectNewlyStuckToTarget =
+                !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null
+
+        // If we are currently stuck to a target, we're in the magnetic field of a target, and that
+        // target isn't the one we're currently stuck to, then touch events have moved into a
+        // adjacent target's magnetic field.
+        val objectMovedIntoDifferentTarget =
+                objectStuckToTarget &&
+                        targetObjectIsInMagneticFieldOf != null &&
+                        targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf
+
+        if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) {
+            velocityTracker.computeCurrentVelocity(1000)
+            val velX = velocityTracker.xVelocity
+            val velY = velocityTracker.yVelocity
+
+            // If the object is moving too quickly within the magnetic field, do not stick it. This
+            // only applies to objects newly stuck to a target. If the object is moved into a new
+            // target, it wasn't moving at all (since it was stuck to the previous one).
+            if (objectNewlyStuckToTarget && abs(velX) > stickToTargetMaxXVelocity) {
+                return false
+            }
+
+            // This touch event is newly within the magnetic field - let the listener know, and
+            // animate sticking to the magnet.
+            targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
+            cancelAnimations()
+            magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!, this)
+            animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null)
+
+            vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+        } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
+            velocityTracker.computeCurrentVelocity(1000)
+
+            // This touch event is newly outside the magnetic field - let the listener know. It will
+            // move the object out of the target using its own movement logic.
+            cancelAnimations()
+            magnetListener.onUnstuckFromTarget(
+                    targetObjectIsStuckTo!!, this,
+                    velocityTracker.xVelocity, velocityTracker.yVelocity,
+                    wasFlungOut = false)
+            targetObjectIsStuckTo = null
+
+            vibrateIfEnabled(VibrationEffect.EFFECT_TICK)
+        }
+
+        // First, check for relevant gestures concluding with an ACTION_UP.
+        if (ev.action == MotionEvent.ACTION_UP) {
+            velocityTracker.computeCurrentVelocity(1000 /* units */)
+            val velX = velocityTracker.xVelocity
+            val velY = velocityTracker.yVelocity
+
+            // Cancel the magnetic animation since we might still be springing into the magnetic
+            // target, but we're about to fling away or release.
+            cancelAnimations()
+
+            if (objectStuckToTarget) {
+                if (-velY > flingUnstuckFromTargetMinVelocity) {
+                    // If the object is stuck, but it was forcefully flung away from the target in
+                    // the upward direction, tell the listener so the object can be animated out of
+                    // the target.
+                    magnetListener.onUnstuckFromTarget(
+                            targetObjectIsStuckTo!!, this,
+                            velX, velY, wasFlungOut = true)
+                } else {
+                    // If the object is stuck and not flung away, it was released inside the target.
+                    magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!, this)
+                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+                }
+
+                // Either way, we're no longer stuck.
+                targetObjectIsStuckTo = null
+                return true
+            }
+
+            // The target we're flinging towards, or null if we're not flinging towards any target.
+            val flungToTarget = associatedTargets.firstOrNull { target ->
+                isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY)
+            }
+
+            if (flungToTarget != null) {
+                // If this is a fling-to-target, animate the object to the magnet and then release
+                // it.
+                magnetListener.onStuckToTarget(flungToTarget, this)
+                targetObjectIsStuckTo = flungToTarget
+
+                animateStuckToTarget(flungToTarget, velX, velY, true) {
+                    magnetListener.onReleasedInTarget(flungToTarget, this)
+                    targetObjectIsStuckTo = null
+                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
+                }
+
+                return true
+            }
+
+            // If it's not either of those things, we are not interested.
+            return false
+        }
+
+        return objectStuckToTarget // Always consume touch events if the object is stuck.
+    }
+
+    /** Plays the given vibration effect if haptics are enabled. */
+    @SuppressLint("MissingPermission")
+    private fun vibrateIfEnabled(effectId: Int) {
+        if (hapticsEnabled) {
+            vibrator.vibrate(VibrationEffect.createPredefined(effectId), vibrationAttributes)
+        }
+    }
+
+    /** Adds the movement to the velocity tracker using raw coordinates. */
+    private fun addMovement(event: MotionEvent) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        val deltaX = event.rawX - event.x
+        val deltaY = event.rawY - event.y
+        event.offsetLocation(deltaX, deltaY)
+        velocityTracker.addMovement(event)
+        event.offsetLocation(-deltaX, -deltaY)
+    }
+
+    /** Animates sticking the object to the provided target with the given start velocities.  */
+    private fun animateStuckToTargetInternal(
+        target: MagneticTarget,
+        velX: Float,
+        velY: Float,
+        flung: Boolean,
+        after: (() -> Unit)? = null
+    ) {
+        target.updateLocationOnScreen()
+        getLocationOnScreen(underlyingObject, objectLocationOnScreen)
+
+        // Calculate the difference between the target's center coordinates and the object's.
+        // Animating the object's x/y properties by these values will center the object on top
+        // of the magnetic target.
+        val xDiff = target.centerOnScreen.x -
+                getWidth(underlyingObject) / 2f - objectLocationOnScreen[0]
+        val yDiff = target.centerOnScreen.y -
+                getHeight(underlyingObject) / 2f - objectLocationOnScreen[1]
+
+        val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig
+
+        cancelAnimations()
+
+        // Animate to the center of the target.
+        animator
+                .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX,
+                        springConfig)
+                .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
+                        springConfig)
+
+        if (physicsAnimatorUpdateListener != null) {
+            animator.addUpdateListener(physicsAnimatorUpdateListener!!)
+        }
+
+        if (physicsAnimatorEndListener != null) {
+            animator.addEndListener(physicsAnimatorEndListener!!)
+        }
+
+        if (after != null) {
+            animator.withEndActions(after)
+        }
+
+        animator.start()
+    }
+
+    /**
+     * Whether or not the provided values match a 'fast fling' towards the provided target. If it
+     * does, we consider it a fling-to-target gesture.
+     */
+    private fun isForcefulFlingTowardsTarget(
+        target: MagneticTarget,
+        rawX: Float,
+        rawY: Float,
+        velX: Float,
+        velY: Float
+    ): Boolean {
+        if (!flingToTargetEnabled) {
+            return false
+        }
+
+        // Whether velocity is sufficient, depending on whether we're flinging into a target at the
+        // top or the bottom of the screen.
+        val velocitySufficient =
+                if (rawY < target.centerOnDisplayY()) velY > flingToTargetMinVelocity
+                else velY < flingToTargetMinVelocity
+
+        if (!velocitySufficient) {
+            return false
+        }
+
+        // Whether the trajectory of the fling intersects the target area.
+        var targetCenterXIntercept = rawX
+
+        // Only do math if the X velocity is non-zero, otherwise X won't change.
+        if (velX != 0f) {
+            // Rise over run...
+            val slope = velY / velX
+            // ...y = mx + b, b = y / mx...
+            val yIntercept = rawY - slope * rawX
+
+            // ...calculate the x value when y = the target's y-coordinate.
+            targetCenterXIntercept = (target.centerOnDisplayY() - yIntercept) / slope
+        }
+
+        // The width of the area we're looking for a fling towards.
+        val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent
+
+        // Velocity was sufficient, so return true if the intercept is within the target area.
+        return targetCenterXIntercept > target.centerOnDisplayX() - targetAreaWidth / 2 &&
+                targetCenterXIntercept < target.centerOnDisplayX() + targetAreaWidth / 2
+    }
+
+    /** Cancel animations on this object's x/y properties. */
+    internal fun cancelAnimations() {
+        animator.cancel(xProperty, yProperty)
+    }
+
+    /** Updates the locations on screen of all of the [associatedTargets]. */
+    internal fun updateTargetViews() {
+        associatedTargets.forEach { it.updateLocationOnScreen() }
+
+        // Update the touch slop, since the configuration may have changed.
+        if (associatedTargets.size > 0) {
+            touchSlop =
+                    ViewConfiguration.get(associatedTargets[0].targetView.context).scaledTouchSlop
+        }
+    }
+
+    /**
+     * Represents a target view with a magnetic field radius and cached center-on-screen
+     * coordinates.
+     *
+     * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then
+     * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to
+     * multiple objects.
+     */
+    class MagneticTarget(
+        val targetView: View,
+        var magneticFieldRadiusPx: Int
+    ) {
+        val centerOnScreen = PointF()
+
+        /**
+         * Set screen vertical offset amount.
+         *
+         * Screen surface may be vertically shifted in some cases, for example when one-handed mode
+         * is enabled. [MagneticTarget] and [MagnetizedObject] set their location in screen
+         * coordinates (see [MagneticTarget.centerOnScreen] and
+         * [MagnetizedObject.getLocationOnScreen] respectively).
+         *
+         * When a [MagnetizedObject] is dragged, the touch location is determined by
+         * [MotionEvent.getRawX] and [MotionEvent.getRawY]. These work in display coordinates. When
+         * screen is shifted due to one-handed mode, display coordinates and screen coordinates do
+         * not match. To determine if a [MagnetizedObject] is dragged into a [MagneticTarget], view
+         * location on screen is translated to display coordinates using this offset value.
+         */
+        var screenVerticalOffset: Int = 0
+
+        private val tempLoc = IntArray(2)
+
+        fun updateLocationOnScreen() {
+            targetView.post {
+                targetView.getLocationOnScreen(tempLoc)
+
+                // Add half of the target size to get the center, and subtract translation since the
+                // target could be animating in while we're doing this calculation.
+                centerOnScreen.set(
+                        tempLoc[0] + targetView.width / 2f - targetView.translationX,
+                        tempLoc[1] + targetView.height / 2f - targetView.translationY)
+            }
+        }
+
+        /**
+         * Get target center coordinate on x-axis on display. [centerOnScreen] has to be up to date
+         * by calling [updateLocationOnScreen] first.
+         */
+        fun centerOnDisplayX(): Float {
+            return centerOnScreen.x
+        }
+
+        /**
+         * Get target center coordinate on y-axis on display. [centerOnScreen] has to be up to date
+         * by calling [updateLocationOnScreen] first. Use [screenVerticalOffset] to update the
+         * screen offset compared to the display.
+         */
+        fun centerOnDisplayY(): Float {
+            return centerOnScreen.y + screenVerticalOffset
+        }
+    }
+
+    companion object {
+        /**
+         * Magnetizes the given view. Magnetized views are attracted to one or more magnetic
+         * targets. Magnetic targets attract objects that are dragged near them, and hold them there
+         * unless they're moved away or released. Releasing objects inside a magnetic target
+         * typically performs an action on the object.
+         *
+         * Magnetized views can also be flung to targets, which will result in the view being pulled
+         * into the target and released as if it was dragged into it.
+         *
+         * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to
+         * receive event callbacks. In your touch handler, pass all MotionEvents that move this view
+         * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by
+         * MagnetizedObject and don't move the view unless it begins returning false again.
+         *
+         * The view will be moved via translationX/Y properties, and its
+         * width/height will be determined via getWidth()/getHeight(). If you are animating
+         * something other than a view, or want to position your view using properties other than
+         * translationX/Y, implement an instance of [MagnetizedObject].
+         *
+         * Note that the magnetic library can't re-order your view automatically. If the view
+         * renders on top of the target views, it will obscure the target when it sticks to it.
+         * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget].
+         */
+        @JvmStatic
+        fun <T : View> magnetizeView(view: T): MagnetizedObject<T> {
+            return object : MagnetizedObject<T>(
+                    view.context,
+                    view,
+                    DynamicAnimation.TRANSLATION_X,
+                    DynamicAnimation.TRANSLATION_Y) {
+                override fun getWidth(underlyingObject: T): Float {
+                    return underlyingObject.width.toFloat()
+                }
+
+                override fun getHeight(underlyingObject: T): Float {
+                    return underlyingObject.height.toFloat() }
+
+                override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) {
+                    underlyingObject.getLocationOnScreen(loc)
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
new file mode 100644
index 0000000..cf39415
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java
@@ -0,0 +1,269 @@
+/*
+ * 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.shared.pip;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+import android.window.TaskSnapshot;
+
+/**
+ * Represents the content overlay used during the entering PiP animation.
+ */
+public abstract class PipContentOverlay {
+    // Fixed string used in WMShellFlickerTests
+    protected static final String LAYER_NAME = "PipContentOverlay";
+
+    protected SurfaceControl mLeash;
+
+    /** Attaches the internal {@link #mLeash} to the given parent leash. */
+    public abstract void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash);
+
+    /** Detaches the internal {@link #mLeash} from its parent by removing itself. */
+    public void detach(SurfaceControl.Transaction tx) {
+        if (mLeash != null && mLeash.isValid()) {
+            tx.remove(mLeash);
+            tx.apply();
+        }
+    }
+
+    @Nullable
+    public SurfaceControl getLeash() {
+        return mLeash;
+    }
+
+    /**
+     * Animates the internal {@link #mLeash} by a given fraction.
+     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
+     *                 call apply on this transaction, it should be applied on the caller side.
+     * @param currentBounds {@link Rect} of the current animation bounds.
+     * @param fraction progress of the animation ranged from 0f to 1f.
+     */
+    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+            Rect currentBounds, float fraction);
+
+    /** A {@link PipContentOverlay} uses solid color. */
+    public static final class PipColorOverlay extends PipContentOverlay {
+        private static final String TAG = PipColorOverlay.class.getSimpleName();
+
+        private final Context mContext;
+
+        public PipColorOverlay(Context context) {
+            mContext = context;
+            mLeash = new SurfaceControl.Builder(new SurfaceSession())
+                    .setCallsite(TAG)
+                    .setName(LAYER_NAME)
+                    .setColorLayer()
+                    .build();
+        }
+
+        @Override
+        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+            tx.show(mLeash);
+            tx.setLayer(mLeash, Integer.MAX_VALUE);
+            tx.setColor(mLeash, getContentOverlayColor(mContext));
+            tx.setAlpha(mLeash, 0f);
+            tx.reparent(mLeash, parentLeash);
+            tx.apply();
+        }
+
+        @Override
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
+            atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+        }
+
+        private float[] getContentOverlayColor(Context context) {
+            final TypedArray ta = context.obtainStyledAttributes(new int[] {
+                    android.R.attr.colorBackground });
+            try {
+                int colorAccent = ta.getColor(0, 0);
+                return new float[] {
+                        Color.red(colorAccent) / 255f,
+                        Color.green(colorAccent) / 255f,
+                        Color.blue(colorAccent) / 255f };
+            } finally {
+                ta.recycle();
+            }
+        }
+    }
+
+    /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
+    public static final class PipSnapshotOverlay extends PipContentOverlay {
+        private static final String TAG = PipSnapshotOverlay.class.getSimpleName();
+
+        private final TaskSnapshot mSnapshot;
+        private final Rect mSourceRectHint;
+
+        public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
+            mSnapshot = snapshot;
+            mSourceRectHint = new Rect(sourceRectHint);
+            mLeash = new SurfaceControl.Builder(new SurfaceSession())
+                    .setCallsite(TAG)
+                    .setName(LAYER_NAME)
+                    .build();
+        }
+
+        @Override
+        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+            final float taskSnapshotScaleX = (float) mSnapshot.getTaskSize().x
+                    / mSnapshot.getHardwareBuffer().getWidth();
+            final float taskSnapshotScaleY = (float) mSnapshot.getTaskSize().y
+                    / mSnapshot.getHardwareBuffer().getHeight();
+            tx.show(mLeash);
+            tx.setLayer(mLeash, Integer.MAX_VALUE);
+            tx.setBuffer(mLeash, mSnapshot.getHardwareBuffer());
+            // Relocate the content to parentLeash's coordinates.
+            tx.setPosition(mLeash, -mSourceRectHint.left, -mSourceRectHint.top);
+            tx.setScale(mLeash, taskSnapshotScaleX, taskSnapshotScaleY);
+            tx.reparent(mLeash, parentLeash);
+            tx.apply();
+        }
+
+        @Override
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
+            // Do nothing. Keep the snapshot till animation ends.
+        }
+    }
+
+    /** A {@link PipContentOverlay} shows app icon on solid color background. */
+    public static final class PipAppIconOverlay extends PipContentOverlay {
+        private static final String TAG = PipAppIconOverlay.class.getSimpleName();
+        // The maximum size for app icon in pixel.
+        private static final int MAX_APP_ICON_SIZE_DP = 72;
+
+        private final Context mContext;
+        private final int mAppIconSizePx;
+        private final Rect mAppBounds;
+        private final int mOverlayHalfSize;
+        private final Matrix mTmpTransform = new Matrix();
+        private final float[] mTmpFloat9 = new float[9];
+
+        private Bitmap mBitmap;
+
+        public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
+                Drawable appIcon, int appIconSizePx) {
+            mContext = context;
+            final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+                    MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
+            mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
+
+            final int overlaySize = getOverlaySize(appBounds, destinationBounds);
+            mOverlayHalfSize = overlaySize >> 1;
+
+            // When the activity is in the secondary split, make sure the scaling center is not
+            // offset.
+            mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
+
+            mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
+            prepareAppIconOverlay(appIcon);
+            mLeash = new SurfaceControl.Builder(new SurfaceSession())
+                    .setCallsite(TAG)
+                    .setName(LAYER_NAME)
+                    .build();
+        }
+
+        /**
+         * Returns the size of the app icon overlay.
+         *
+         * In order to have the overlay always cover the pip window during the transition,
+         * the overlay will be drawn with the max size of the start and end bounds in different
+         * rotation.
+         */
+        public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
+            final int appWidth = appBounds.width();
+            final int appHeight = appBounds.height();
+
+            return Math.max(Math.max(appWidth, appHeight),
+                    Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
+        }
+
+        @Override
+        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
+            tx.show(mLeash);
+            tx.setLayer(mLeash, Integer.MAX_VALUE);
+            tx.setBuffer(mLeash, mBitmap.getHardwareBuffer());
+            tx.setAlpha(mLeash, 0f);
+            tx.reparent(mLeash, parentLeash);
+            tx.apply();
+        }
+
+        @Override
+        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
+                Rect currentBounds, float fraction) {
+            mTmpTransform.reset();
+            // In order for the overlay to always cover the pip window, the overlay may have a
+            // size larger than the pip window. Make sure that app icon is at the center.
+            final int appBoundsCenterX = mAppBounds.centerX();
+            final int appBoundsCenterY = mAppBounds.centerY();
+            mTmpTransform.setTranslate(
+                    appBoundsCenterX - mOverlayHalfSize,
+                    appBoundsCenterY - mOverlayHalfSize);
+            // Scale back the bitmap with the pivot point at center.
+            final float scale = Math.min(
+                    (float) mAppBounds.width() / currentBounds.width(),
+                    (float) mAppBounds.height() / currentBounds.height());
+            mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
+            atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
+                    .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
+        }
+
+        @Override
+        public void detach(SurfaceControl.Transaction tx) {
+            super.detach(tx);
+            if (mBitmap != null && !mBitmap.isRecycled()) {
+                mBitmap.recycle();
+            }
+        }
+
+        private void prepareAppIconOverlay(Drawable appIcon) {
+            final Canvas canvas = new Canvas();
+            canvas.setBitmap(mBitmap);
+            final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+                    android.R.attr.colorBackground });
+            try {
+                int colorAccent = ta.getColor(0, 0);
+                canvas.drawRGB(
+                        Color.red(colorAccent),
+                        Color.green(colorAccent),
+                        Color.blue(colorAccent));
+            } finally {
+                ta.recycle();
+            }
+            final Rect appIconBounds = new Rect(
+                    mOverlayHalfSize - mAppIconSizePx / 2,
+                    mOverlayHalfSize - mAppIconSizePx / 2,
+                    mOverlayHalfSize + mAppIconSizePx / 2,
+                    mOverlayHalfSize + mAppIconSizePx / 2);
+            appIcon.setBounds(appIconBounds);
+            appIcon.draw(canvas);
+            mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java
new file mode 100644
index 0000000..7c1faa66
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitBounds.java
@@ -0,0 +1,153 @@
+/*
+ * 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.shared.split;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+
+import java.util.Objects;
+
+/**
+ * Container of various information needed to display split screen
+ * tasks/leashes/etc in Launcher
+ */
+public class SplitBounds implements Parcelable {
+    public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds";
+
+    public final Rect leftTopBounds;
+    public final Rect rightBottomBounds;
+    /** This rect represents the actual gap between the two apps */
+    public final Rect visualDividerBounds;
+    // This class is orientation-agnostic, so we compute both for later use
+    public final float topTaskPercent;
+    public final float leftTaskPercent;
+    public final float dividerWidthPercent;
+    public final float dividerHeightPercent;
+    public final @PersistentSnapPosition int snapPosition;
+    /**
+     * If {@code true}, that means at the time of creation of this object, the
+     * split-screened apps were vertically stacked. This is useful in scenarios like
+     * rotation where the bounds won't change, but this variable can indicate what orientation
+     * the bounds were originally in
+     */
+    public final boolean appsStackedVertically;
+    public final int leftTopTaskId;
+    public final int rightBottomTaskId;
+
+    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+            int rightBottomTaskId, @PersistentSnapPosition int snapPosition) {
+        this.leftTopBounds = leftTopBounds;
+        this.rightBottomBounds = rightBottomBounds;
+        this.leftTopTaskId = leftTopTaskId;
+        this.rightBottomTaskId = rightBottomTaskId;
+        this.snapPosition = snapPosition;
+
+        if (rightBottomBounds.top > leftTopBounds.top) {
+            // vertical apps, horizontal divider
+            this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
+                    leftTopBounds.right, rightBottomBounds.top);
+            appsStackedVertically = true;
+        } else {
+            // horizontal apps, vertical divider
+            this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
+                    rightBottomBounds.left, leftTopBounds.bottom);
+            appsStackedVertically = false;
+        }
+
+        float totalWidth = rightBottomBounds.right - leftTopBounds.left;
+        float totalHeight = rightBottomBounds.bottom - leftTopBounds.top;
+        leftTaskPercent = leftTopBounds.width() / totalWidth;
+        topTaskPercent = leftTopBounds.height() / totalHeight;
+        dividerWidthPercent = visualDividerBounds.width() / totalWidth;
+        dividerHeightPercent = visualDividerBounds.height() / totalHeight;
+    }
+
+    public SplitBounds(Parcel parcel) {
+        leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
+        rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
+        visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
+        topTaskPercent = parcel.readFloat();
+        leftTaskPercent = parcel.readFloat();
+        appsStackedVertically = parcel.readBoolean();
+        leftTopTaskId = parcel.readInt();
+        rightBottomTaskId = parcel.readInt();
+        dividerWidthPercent = parcel.readFloat();
+        dividerHeightPercent = parcel.readFloat();
+        snapPosition = parcel.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeTypedObject(leftTopBounds, flags);
+        parcel.writeTypedObject(rightBottomBounds, flags);
+        parcel.writeTypedObject(visualDividerBounds, flags);
+        parcel.writeFloat(topTaskPercent);
+        parcel.writeFloat(leftTaskPercent);
+        parcel.writeBoolean(appsStackedVertically);
+        parcel.writeInt(leftTopTaskId);
+        parcel.writeInt(rightBottomTaskId);
+        parcel.writeFloat(dividerWidthPercent);
+        parcel.writeFloat(dividerHeightPercent);
+        parcel.writeInt(snapPosition);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SplitBounds)) {
+            return false;
+        }
+        // Only need to check the base fields (the other fields are derived from these)
+        final SplitBounds other = (SplitBounds) obj;
+        return Objects.equals(leftTopBounds, other.leftTopBounds)
+                && Objects.equals(rightBottomBounds, other.rightBottomBounds)
+                && leftTopTaskId == other.leftTopTaskId
+                && rightBottomTaskId == other.rightBottomTaskId;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
+    }
+
+    @Override
+    public String toString() {
+        return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
+                + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
+                + "Divider: " + visualDividerBounds + "\n"
+                + "AppsVertical? " + appsStackedVertically + "\n"
+                + "snapPosition: " + snapPosition;
+    }
+
+    public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
+        @Override
+        public SplitBounds createFromParcel(Parcel in) {
+            return new SplitBounds(in);
+        }
+
+        @Override
+        public SplitBounds[] newArray(int size) {
+            return new SplitBounds[size];
+        }
+    };
+}
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
new file mode 100644
index 0000000..498dc8b
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -0,0 +1,146 @@
+/*
+ * 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.shared.split;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+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_UNDEFINED;
+
+import android.annotation.IntDef;
+
+import com.android.wm.shell.shared.TransitionUtil;
+
+/** Helper utility class of methods and constants that are available to be imported in Launcher. */
+public class SplitScreenConstants {
+    /** Duration used for every split fade-in or fade-out. */
+    public static final int FADE_DURATION = 133;
+    /** Duration where we keep an app veiled to allow it to redraw itself behind the scenes. */
+    public static final int VEIL_DELAY_DURATION = 300;
+
+    /** Key for passing in widget intents when invoking split from launcher workspace. */
+    public static final String KEY_EXTRA_WIDGET_INTENT = "key_extra_widget_intent";
+
+    ///////////////
+    // IMPORTANT for the following SPLIT_POSITION and SNAP_TO constants:
+    // These int values must not be changed -- they are persisted to user-defined app pairs, and
+    // will break things if changed.
+    //
+
+    /**
+     * Split position isn't specified normally meaning to use what ever it is currently set to.
+     */
+    public static final int SPLIT_POSITION_UNDEFINED = -1;
+
+    /**
+     * Specifies that a split is positioned at the top half of the screen if
+     * in portrait mode or at the left half of the screen if in landscape mode.
+     */
+    public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
+
+    /**
+     * Specifies that a split is positioned at the bottom half of the screen if
+     * in portrait mode or at the right half of the screen if in landscape mode.
+     */
+    public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
+
+    @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
+            SPLIT_POSITION_UNDEFINED,
+            SPLIT_POSITION_TOP_OR_LEFT,
+            SPLIT_POSITION_BOTTOM_OR_RIGHT
+    })
+    public @interface SplitPosition {
+    }
+
+    /** A snap target in the first half of the screen, where the split is roughly 30-70. */
+    public static final int SNAP_TO_30_70 = 0;
+
+    /** The 50-50 snap target */
+    public static final int SNAP_TO_50_50 = 1;
+
+    /** A snap target in the latter half of the screen, where the split is roughly 70-30. */
+    public static final int SNAP_TO_70_30 = 2;
+
+    /**
+     * These snap targets are used for split pairs in a stable, non-transient state. They may be
+     * persisted in Launcher when the user saves an app pair. They are a subset of
+     * {@link SnapPosition}.
+     */
+    @IntDef(prefix = { "SNAP_TO_" }, value = {
+            SNAP_TO_30_70,
+            SNAP_TO_50_50,
+            SNAP_TO_70_30
+    })
+    public @interface PersistentSnapPosition {}
+
+    /**
+     * Checks if the snapPosition in question is a {@link PersistentSnapPosition}.
+     */
+    public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) {
+        return snapPosition == SNAP_TO_30_70
+                || snapPosition == SNAP_TO_50_50
+                || snapPosition == SNAP_TO_70_30;
+    }
+
+    /** The divider doesn't snap to any target and is freely placeable. */
+    public static final int SNAP_TO_NONE = 10;
+
+    /** If the divider reaches this value, the left/top task should be dismissed. */
+    public static final int SNAP_TO_START_AND_DISMISS = 11;
+
+    /** If the divider reaches this value, the right/bottom task should be dismissed. */
+    public static final int SNAP_TO_END_AND_DISMISS = 12;
+
+    /** A snap target positioned near the screen edge for a minimized task */
+    public static final int SNAP_TO_MINIMIZE = 13;
+
+    @IntDef(prefix = { "SNAP_TO_" }, value = {
+            SNAP_TO_30_70,
+            SNAP_TO_50_50,
+            SNAP_TO_70_30,
+            SNAP_TO_NONE,
+            SNAP_TO_START_AND_DISMISS,
+            SNAP_TO_END_AND_DISMISS,
+            SNAP_TO_MINIMIZE
+    })
+    public @interface SnapPosition {}
+
+    ///////////////
+
+    public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
+    public static final int[] CONTROLLED_WINDOWING_MODES =
+            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
+    public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
+            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW,
+            WINDOWING_MODE_FREEFORM};
+
+    /** Flag applied to a transition change to identify it as a divider bar for animation. */
+    public static final int FLAG_IS_DIVIDER_BAR = TransitionUtil.FLAG_IS_DIVIDER_BAR;
+
+    public static final String splitPositionToString(@SplitPosition int pos) {
+        switch (pos) {
+            case SPLIT_POSITION_UNDEFINED:
+                return "SPLIT_POSITION_UNDEFINED";
+            case SPLIT_POSITION_TOP_OR_LEFT:
+                return "SPLIT_POSITION_TOP_OR_LEFT";
+            case SPLIT_POSITION_BOTTOM_OR_RIGHT:
+                return "SPLIT_POSITION_BOTTOM_OR_RIGHT";
+            default:
+                return "UNKNOWN";
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/startingsurface/SplashScreenExitAnimationUtils.java
new file mode 100644
index 0000000..da9bf7a
--- /dev/null
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/startingsurface/SplashScreenExitAnimationUtils.java
@@ -0,0 +1,459 @@
+/*
+ * 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.shared.startingsurface;
+
+import static android.view.Choreographer.CALLBACK_COMMIT;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.MathUtils;
+import android.util.Slog;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.shared.animation.Interpolators;
+
+/**
+ * Utilities for creating the splash screen window animations.
+ * @hide
+ */
+public class SplashScreenExitAnimationUtils {
+    private static final boolean DEBUG_EXIT_ANIMATION = false;
+    private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+    private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
+    private static final String TAG = "SplashScreenExitAnimationUtils";
+
+    private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
+    private static final Interpolator MASK_RADIUS_INTERPOLATOR =
+            new PathInterpolator(0f, 0f, 0.4f, 1f);
+    private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
+
+    /**
+     * This splash screen exit animation type uses a radial vanish to hide
+     * the starting window and slides up the main window content.
+     * @hide
+     */
+    public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
+
+    /**
+     * This splash screen exit animation type fades out the starting window
+     * to reveal the main window content.
+     * @hide
+     */
+    public static final int TYPE_FADE_OUT = 1;
+
+    /** @hide */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_RADIAL_VANISH_SLIDE_UP,
+            TYPE_FADE_OUT,
+    })
+    public @interface ExitAnimationType {}
+
+    /**
+     * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
+     * window with rounded corner radius.
+     */
+    public static void startAnimations(@ExitAnimationType int animationType,
+            ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
+            int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
+            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+            Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
+        ValueAnimator animator;
+        if (animationType == TYPE_FADE_OUT) {
+            animator = createFadeOutAnimation(splashScreenView, animationDuration,
+                    iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
+                    appRevealDuration, animatorListener);
+        } else {
+            animator = createRadialVanishSlideUpAnimator(splashScreenView,
+                    firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+                    animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+                    appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
+        }
+        animator.start();
+    }
+
+    /**
+     * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
+     * window.
+     * @hide
+     */
+    public static void startAnimations(ViewGroup splashScreenView,
+            SurfaceControl firstWindowSurface, int mainWindowShiftLength,
+            TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+            int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+            int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
+        // Start the default 'reveal' animation.
+        startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
+                firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
+                animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
+                appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
+    }
+
+    /**
+     * Creates the animator to fade out the icon, reveal the app, and shift up main window.
+     * @hide
+     */
+    private static ValueAnimator createRadialVanishSlideUpAnimator(ViewGroup splashScreenView,
+            SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
+            TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
+            int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
+            int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
+            float roundedCornerRadius) {
+        // reveal app
+        final float transparentRatio = 0.8f;
+        final int globalHeight = splashScreenView.getHeight();
+        final int verticalCircleCenter = 0;
+        final int finalVerticalLength = globalHeight - verticalCircleCenter;
+        final int halfWidth = splashScreenView.getWidth() / 2;
+        final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+                Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+        final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
+        final float[] stops = {0f, transparentRatio, 1f};
+
+        RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView);
+        radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+        radialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
+        radialVanishAnimation.setRadialPaintParam(colors, stops);
+
+        View occludeHoleView = null;
+        ShiftUpAnimation shiftUpAnimation = null;
+        if (firstWindowSurface != null && firstWindowSurface.isValid()) {
+            // shift up main window
+            occludeHoleView = new View(splashScreenView.getContext());
+            if (DEBUG_EXIT_ANIMATION_BLEND) {
+                occludeHoleView.setBackgroundColor(Color.BLUE);
+            } else if (splashScreenView instanceof SplashScreenView) {
+                occludeHoleView.setBackgroundColor(
+                        ((SplashScreenView) splashScreenView).getInitBackgroundColor());
+            } else {
+                occludeHoleView.setBackgroundColor(
+                        isDarkTheme(splashScreenView.getContext()) ? Color.BLACK : Color.WHITE);
+            }
+            final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+                    WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+            splashScreenView.addView(occludeHoleView, params);
+
+            shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView,
+                    firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame,
+                    mMainWindowShiftLength, roundedCornerRadius);
+        }
+
+        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.setDuration(animationDuration);
+        animator.setInterpolator(Interpolators.LINEAR);
+        if (animatorListener != null) {
+            animator.addListener(animatorListener);
+        }
+        View finalOccludeHoleView = occludeHoleView;
+        ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation;
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (finalShiftUpAnimation != null) {
+                    finalShiftUpAnimation.finish();
+                }
+                splashScreenView.removeView(radialVanishAnimation);
+                splashScreenView.removeView(finalOccludeHoleView);
+            }
+        });
+        animator.addUpdateListener(animation -> {
+            float linearProgress = (float) animation.getAnimatedValue();
+
+            // Fade out progress
+            final float iconProgress =
+                    ICON_INTERPOLATOR.getInterpolation(getProgress(
+                            linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+            View iconView = null;
+            View brandingView = null;
+            if (splashScreenView instanceof SplashScreenView) {
+                iconView = ((SplashScreenView) splashScreenView).getIconView();
+                brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+            }
+            if (iconView != null) {
+                iconView.setAlpha(iconStartAlpha * (1 - iconProgress));
+            }
+            if (brandingView != null) {
+                brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress));
+            }
+
+            final float revealLinearProgress = getProgress(linearProgress, appRevealDelay,
+                    appRevealDuration, animationDuration);
+
+            radialVanishAnimation.onAnimationProgress(revealLinearProgress);
+
+            if (finalShiftUpAnimation != null) {
+                finalShiftUpAnimation.onAnimationProgress(revealLinearProgress);
+            }
+        });
+        return animator;
+    }
+
+    private static float getProgress(float linearProgress, long delay, long duration,
+                                     int animationDuration) {
+        return MathUtils.constrain(
+                (linearProgress * (animationDuration) - delay) / duration,
+                0.0f,
+                1.0f
+        );
+    }
+
+    private static boolean isDarkTheme(Context context) {
+        Configuration configuration = context.getResources().getConfiguration();
+        int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        return nightMode == Configuration.UI_MODE_NIGHT_YES;
+    }
+
+    private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
+            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
+            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
+            Animator.AnimatorListener animatorListener) {
+
+        if (DEBUG_EXIT_FADE_ANIMATION) {
+            splashScreenView.setBackgroundColor(Color.BLUE);
+        }
+
+        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.setDuration(animationDuration);
+        animator.setInterpolator(Interpolators.LINEAR);
+        animator.addUpdateListener(animation -> {
+
+            float linearProgress = (float) animation.getAnimatedValue();
+
+            // Icon fade out progress (always starts immediately)
+            final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
+                            linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
+            View iconView = null;
+            View brandingView = null;
+
+            if (splashScreenView instanceof SplashScreenView) {
+                iconView = ((SplashScreenView) splashScreenView).getIconView();
+                brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
+            }
+            if (iconView != null) {
+                iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
+            }
+            if (brandingView != null) {
+                brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
+            }
+
+            // Splash screen fade out progress (possibly delayed)
+            final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
+                    getProgress(linearProgress, appRevealDelay,
+                    appRevealDuration, animationDuration));
+
+            splashScreenView.setAlpha(1f - splashFadeProgress);
+
+            if (DEBUG_EXIT_FADE_ANIMATION) {
+                Slog.d(TAG, "progress -> animation: " + linearProgress
+                        + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
+                        + "\t splash alpha: " + splashScreenView.getAlpha()
+                );
+            }
+        });
+        if (animatorListener != null) {
+            animator.addListener(animatorListener);
+        }
+        return animator;
+    }
+
+    /**
+     * View which creates a circular reveal of the underlying view.
+     * @hide
+     */
+    @SuppressLint("ViewConstructor")
+    public static class RadialVanishAnimation extends View {
+        private final ViewGroup mView;
+        private int mInitRadius;
+        private int mFinishRadius;
+
+        private final Point mCircleCenter = new Point();
+        private final Matrix mVanishMatrix = new Matrix();
+        private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        public RadialVanishAnimation(ViewGroup target) {
+            super(target.getContext());
+            mView = target;
+            mView.addView(this);
+            if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
+                ((ViewGroup.MarginLayoutParams) getLayoutParams()).setMargins(0, 0, 0, 0);
+            }
+            mVanishPaint.setAlpha(0);
+        }
+
+        void onAnimationProgress(float linearProgress) {
+            if (mVanishPaint.getShader() == null) {
+                return;
+            }
+
+            final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress);
+            final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress);
+            final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress;
+
+            mVanishMatrix.setScale(scale, scale);
+            mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
+            mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
+            mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress));
+
+            postInvalidate();
+        }
+
+        void setRadius(int initRadius, int finishRadius) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+                        + " final " + finishRadius);
+            }
+            mInitRadius = initRadius;
+            mFinishRadius = finishRadius;
+        }
+
+        void setCircleCenter(int x, int y) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+            }
+            mCircleCenter.set(x, y);
+        }
+
+        void setRadialPaintParam(int[] colors, float[] stops) {
+            // setup gradient shader
+            final RadialGradient rShader =
+                    new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+            mVanishPaint.setShader(rShader);
+            if (!DEBUG_EXIT_ANIMATION_BLEND) {
+                // We blend the reveal gradient with the splash screen using DST_OUT so that the
+                // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and
+                // fully invisible when radius = finishRadius AND gradient opacity is 1.
+                mVanishPaint.setBlendMode(BlendMode.DST_OUT);
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+        }
+    }
+
+    /**
+     * Shifts up the main window.
+     * @hide
+     */
+    public static final class ShiftUpAnimation {
+        private final float mFromYDelta;
+        private final float mToYDelta;
+        private final View mOccludeHoleView;
+        private final SyncRtSurfaceTransactionApplier mApplier;
+        private final Matrix mTmpTransform = new Matrix();
+        private final SurfaceControl mFirstWindowSurface;
+        private final ViewGroup mSplashScreenView;
+        private final TransactionPool mTransactionPool;
+        private final Rect mFirstWindowFrame;
+        private final int mMainWindowShiftLength;
+
+        public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView,
+                                SurfaceControl firstWindowSurface, ViewGroup splashScreenView,
+                                TransactionPool transactionPool, Rect firstWindowFrame,
+                                int mainWindowShiftLength, float roundedCornerRadius) {
+            mFromYDelta = firstWindowFrame.top
+                    - Math.max(firstWindowFrame.top, roundedCornerRadius);
+            mToYDelta = toYDelta;
+            mOccludeHoleView = occludeHoleView;
+            mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
+            mFirstWindowSurface = firstWindowSurface;
+            mSplashScreenView = splashScreenView;
+            mTransactionPool = transactionPool;
+            mFirstWindowFrame = firstWindowFrame;
+            mMainWindowShiftLength = mainWindowShiftLength;
+        }
+
+        void onAnimationProgress(float linearProgress) {
+            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
+                    || !mSplashScreenView.isAttachedToWindow()) {
+                return;
+            }
+
+            final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
+            final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;
+
+            mOccludeHoleView.setTranslationY(dy);
+            mTmpTransform.setTranslate(0 /* dx */, dy);
+
+            // set the vsyncId to ensure the transaction doesn't get applied too early.
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+            mTmpTransform.postTranslate(mFirstWindowFrame.left,
+                    mFirstWindowFrame.top + mMainWindowShiftLength);
+
+            SyncRtSurfaceTransactionApplier.SurfaceParams
+                    params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+                    .Builder(mFirstWindowSurface)
+                    .withMatrix(mTmpTransform)
+                    .withMergeTransaction(tx)
+                    .build();
+            mApplier.scheduleApply(params);
+
+            mTransactionPool.release(tx);
+        }
+
+        void finish() {
+            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+                return;
+            }
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            if (mSplashScreenView.isAttachedToWindow()) {
+                tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+
+                SyncRtSurfaceTransactionApplier.SurfaceParams
+                        params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+                        .Builder(mFirstWindowSurface)
+                        .withWindowCrop(null)
+                        .withMergeTransaction(tx)
+                        .build();
+                mApplier.scheduleApply(params);
+            } else {
+                tx.setWindowCrop(mFirstWindowSurface, null);
+                tx.apply();
+            }
+            mTransactionPool.release(tx);
+
+            Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
+                    mFirstWindowSurface::release, null);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index 8d30db6..5355138 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -18,6 +18,7 @@
 
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import android.annotation.CallSuper;
 import android.graphics.Point;
@@ -146,6 +147,14 @@
     /** To be overridden by subclasses to adjust the animation surface change. */
     void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
         // Update the surface position and alpha.
+        if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+                && mAnimation.getExtensionEdges() != 0x0
+                && !(mChange.hasFlags(FLAG_TRANSLUCENT)
+                        && mChange.getActivityComponent() != null)) {
+            // Extend non-translucent activities
+            t.setEdgeExtensionEffect(mLeash, mAnimation.getExtensionEdges());
+        }
+
         mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
         t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
         t.setAlpha(mLeash, mTransformation.getAlpha());
@@ -165,7 +174,7 @@
         if (!cropRect.intersect(mWholeAnimationBounds)) {
             // Hide the surface when it is outside of the animation area.
             t.setAlpha(mLeash, 0);
-        } else if (mAnimation.hasExtension()) {
+        } else if (mAnimation.getExtensionEdges() != 0) {
             // Allow the surface to be shown in its original bounds in case we want to use edge
             // extensions.
             cropRect.union(mContentBounds);
@@ -180,6 +189,10 @@
     @CallSuper
     void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
         onAnimationUpdate(t, mAnimation.getDuration());
+        if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+                && mAnimation.getExtensionEdges() != 0x0) {
+            t.setEdgeExtensionEffect(mLeash, /* edge */ 0);
+        }
     }
 
     final long getDurationHint() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 5696a54..d2cef4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -144,8 +144,10 @@
             // ending states.
             prepareForJumpCut(info, startTransaction);
         } else {
-            addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
-                    postStartTransactionCallbacks, adapters);
+            if (!com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+                addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
+                        postStartTransactionCallbacks, adapters);
+            }
             addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
             for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
                 duration = Math.max(duration, adapter.getDurationHint());
@@ -341,7 +343,7 @@
             @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
         for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
             final Animation animation = adapter.mAnimation;
-            if (!animation.hasExtension()) {
+            if (animation.getExtensionEdges() == 0) {
                 continue;
             }
             if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
index 26edd7d2..be1f71e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java
@@ -23,6 +23,8 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
+import com.android.wm.shell.shared.animation.Interpolators;
+
 import javax.inject.Inject;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
deleted file mode 100644
index ce0bf8b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ /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.wm.shell.animation;
-
-import android.graphics.Path;
-import android.view.animation.BackGestureInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Common interpolators used in wm shell library.
- */
-public class Interpolators {
-
-    public static final Interpolator LINEAR = new LinearInterpolator();
-
-    /**
-     * Interpolator for alpha in animation.
-     */
-    public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
-
-    /**
-     * Interpolator for alpha out animation.
-     */
-    public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
-
-    /**
-     * Interpolator for fast out linear in animation.
-     */
-    public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
-
-    /**
-     * Interpolator for fast out slow in animation.
-     */
-    public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
-
-    /**
-     * Interpolator for linear out slow in animation.
-     */
-    public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
-
-    /**
-     * The default emphasized interpolator. Used for hero / emphasized movement of content.
-     */
-    public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
-
-    /**
-     * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
-     * is disappearing e.g. when moving off screen.
-     */
-    public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
-            0.3f, 0f, 0.8f, 0.15f);
-
-    /**
-     * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
-     * is appearing e.g. when coming from off screen
-     */
-    public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
-            0.05f, 0.7f, 0.1f, 1f);
-
-    /**
-     * The standard decelerating interpolator that should be used on every regular movement of
-     * content that is appearing e.g. when coming from off screen.
-     */
-    public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(0f, 0f, 0f, 1f);
-
-    /**
-     * Interpolator to be used when animating a move based on a click. Pair with enough duration.
-     */
-    public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f);
-
-    /**
-     * Interpolator to be used when animating a panel closing.
-     */
-    public static final Interpolator PANEL_CLOSE_ACCELERATED =
-            new PathInterpolator(0.3f, 0, 0.5f, 1);
-
-    public static final PathInterpolator SLOWDOWN_INTERPOLATOR =
-            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
-
-    public static final PathInterpolator DIM_INTERPOLATOR =
-            new PathInterpolator(.23f, .87f, .52f, -0.11f);
-
-    /**
-     * Use this interpolator for animating progress values coming from the back callback to get
-     * the predictive-back-typical decelerate motion.
-     *
-     * This interpolator is similar to {@link Interpolators#STANDARD_DECELERATE} but has a slight
-     * acceleration phase at the start.
-     */
-    public static final Interpolator BACK_GESTURE = new BackGestureInterpolator();
-
-    // Create the default emphasized interpolator
-    private static PathInterpolator createEmphasizedInterpolator() {
-        Path path = new Path();
-        // Doing the same as fast_out_extra_slow_in
-        path.moveTo(0f, 0f);
-        path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
-        path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
-        return new PathInterpolator(path);
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index bfe1306a..6207e5b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -1,6 +1,8 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
index d754d04..26f7b36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationBackground.java
@@ -20,6 +20,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
@@ -59,6 +60,23 @@
      */
     public void ensureBackground(Rect startRect, int color,
             @NonNull SurfaceControl.Transaction transaction, int statusbarHeight) {
+        ensureBackground(startRect, color, transaction, statusbarHeight,
+                null /* cropBounds */, 0 /* cornerRadius */);
+    }
+
+    /**
+     * Ensures the back animation background color layer is present.
+     *
+     * @param startRect The start bounds of the closing target.
+     * @param color The background color.
+     * @param transaction The animation transaction.
+     * @param statusbarHeight The height of the statusbar (in px).
+     * @param cropBounds The crop bounds of the surface, set to non-empty to show wallpaper.
+     * @param cornerRadius The radius of corner, only work when cropBounds is not empty.
+     */
+    public void ensureBackground(Rect startRect, int color,
+            @NonNull SurfaceControl.Transaction transaction, int statusbarHeight,
+            @Nullable Rect cropBounds, float cornerRadius) {
         if (mBackgroundSurface != null) {
             return;
         }
@@ -78,6 +96,10 @@
         transaction.setColor(mBackgroundSurface, colorComponents)
                 .setLayer(mBackgroundSurface, BACKGROUND_LAYER)
                 .show(mBackgroundSurface);
+        if (cropBounds != null && !cropBounds.isEmpty()) {
+            transaction.setCrop(mBackgroundSurface, cropBounds)
+                    .setCornerRadius(mBackgroundSurface, cornerRadius);
+        }
         mStartBounds = startRect;
         mIsRequestingStatusBarAppearance = false;
         mStatusbarHeight = statusbarHeight;
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 7275c64..7b3b207 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
@@ -29,7 +29,7 @@
 import static com.android.window.flags.Flags.migratePredictiveBackTransition;
 import static com.android.window.flags.Flags.predictiveBackSystemAnims;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -74,6 +74,7 @@
 import android.window.IOnBackInvokedCallback;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +96,7 @@
 
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Predicate;
 
 /**
  * Controls the window animation run when a user initiates a back gesture.
@@ -145,7 +147,8 @@
     private final Handler mBgHandler;
     private final WindowManager mWindowManager;
     private final Transitions mTransitions;
-    private final BackTransitionHandler mBackTransitionHandler;
+    @VisibleForTesting
+    final BackTransitionHandler mBackTransitionHandler;
     @VisibleForTesting
     final Rect mTouchableArea = new Rect();
 
@@ -173,7 +176,8 @@
     @Nullable
     private IOnBackInvokedCallback mActiveCallback;
     @Nullable
-    private RemoteAnimationTarget[] mApps;
+    @VisibleForTesting
+    RemoteAnimationTarget[] mApps;
 
     @VisibleForTesting
     final RemoteCallback mNavigationObserver = new RemoteCallback(
@@ -569,8 +573,14 @@
     private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
         try {
             startLatencyTracking();
+            final BackAnimationAdapter adapter = mEnableAnimations.get()
+                    ? mBackAnimationAdapter : null;
+            if (adapter != null && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
+                adapter.updateSupportedAnimators(
+                        mShellBackAnimationRegistry.getSupportedAnimators());
+            }
             mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
-                    mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
+                    mNavigationObserver, adapter);
             onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
         } catch (RemoteException remoteException) {
             Log.e(TAG, "Failed to initAnimation", remoteException);
@@ -1063,14 +1073,30 @@
         return true;
     }
 
+    private void kickStartAnimation() {
+        startSystemAnimation();
+
+        // Dispatch the first progress after animation start for
+        // smoothing the initial animation, instead of waiting for next
+        // onMove.
+        final BackMotionEvent backFinish = mCurrentTracker
+                .createProgressEvent();
+        dispatchOnBackProgressed(mActiveCallback, backFinish);
+        if (!mBackGestureStarted) {
+            // if the down -> up gesture happened before animation
+            // start, we have to trigger the uninterruptible transition
+            // to finish the back animation.
+            startPostCommitAnimation();
+        }
+    }
+
     private void createAdapter() {
         IBackAnimationRunner runner =
                 new IBackAnimationRunner.Stub() {
                     @Override
                     public void onAnimationStart(
                             RemoteAnimationTarget[] apps,
-                            RemoteAnimationTarget[] wallpapers,
-                            RemoteAnimationTarget[] nonApps,
+                            IBinder token,
                             IBackAnimationFinishedCallback finishedCallback) {
                         mShellExecutor.execute(
                                 () -> {
@@ -1082,21 +1108,12 @@
                                     }
                                     mBackAnimationFinishedCallback = finishedCallback;
                                     mApps = apps;
-                                    startSystemAnimation();
-                                    mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
-
-                                    // Dispatch the first progress after animation start for
-                                    // smoothing the initial animation, instead of waiting for next
-                                    // onMove.
-                                    final BackMotionEvent backFinish = mCurrentTracker
-                                            .createProgressEvent();
-                                    dispatchOnBackProgressed(mActiveCallback, backFinish);
-                                    if (!mBackGestureStarted) {
-                                        // if the down -> up gesture happened before animation
-                                        // start, we have to trigger the uninterruptible transition
-                                        // to finish the back animation.
-                                        startPostCommitAnimation();
+                                    // app only visible after transition ready, break for now.
+                                    if (token != null) {
+                                        return;
                                     }
+                                    kickStartAnimation();
+                                    mBackTransitionHandler.consumeQueuedTransitionIfNeeded();
                                 });
                     }
 
@@ -1169,16 +1186,18 @@
         }
 
         private void applyFinishOpenTransition() {
-            if (mFinishOpenTransaction != null) {
-                mFinishOpenTransaction.apply();
-                mFinishOpenTransaction = null;
-            }
-            if (mFinishOpenTransitionCallback != null) {
-                mFinishOpenTransitionCallback.onTransitionFinished(null);
-                mFinishOpenTransitionCallback = null;
-            }
             mOpenTransitionInfo = null;
             mPrepareOpenTransition = null;
+            if (mFinishOpenTransaction != null) {
+                final SurfaceControl.Transaction t = mFinishOpenTransaction;
+                mFinishOpenTransaction = null;
+                t.apply();
+            }
+            if (mFinishOpenTransitionCallback != null) {
+                final Transitions.TransitionFinishCallback callback = mFinishOpenTransitionCallback;
+                mFinishOpenTransitionCallback = null;
+                callback.onTransitionFinished(null);
+            }
         }
 
         private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
@@ -1196,6 +1215,9 @@
                 @NonNull SurfaceControl.Transaction st,
                 @NonNull SurfaceControl.Transaction ft,
                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
+            if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
+                kickStartAnimation();
+            }
             // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
             // need to post to ShellExecutor when called.
             if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
@@ -1209,7 +1231,7 @@
             }
 
             if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
-                    && !isGestureBackTransition(info)) {
+                    && isNotGestureBackTransition(info)) {
                 return false;
             }
 
@@ -1259,19 +1281,24 @@
             ComponentName openComponent = null;
             int tmpSize;
             int openTaskId = INVALID_TASK_ID;
+            WindowContainerToken openToken = null;
             for (int j = init.getChanges().size() - 1; j >= 0; --j) {
                 final TransitionInfo.Change change = init.getChanges().get(j);
                 if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
                     openComponent = findComponentName(change);
                     openTaskId = findTaskId(change);
+                    openToken = findToken(change);
                     if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
                         openShowWallpaper = true;
                     }
                     break;
                 }
             }
-            if (openComponent == null && openTaskId == INVALID_TASK_ID) {
-                // shouldn't happen.
+            if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+                // This shouldn't happen, but if that happen, consume the initial transition anyway.
+                Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+                        + "animated target from the open transition=" + mOpenTransitionInfo);
+                mOpenTransitionInfo = null;
                 return;
             }
             // find first non-prepare open target
@@ -1302,7 +1329,7 @@
                 boolean moveToTop = false;
                 for (int j = info.getChanges().size() - 1; j >= 0; --j) {
                     final TransitionInfo.Change change = info.getChanges().get(j);
-                    if (isSameChangeTarget(openComponent, openTaskId, change)) {
+                    if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
                         moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
                         info.getChanges().remove(j);
                     } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
@@ -1310,15 +1337,21 @@
                         info.getChanges().remove(j);
                     }
                 }
-                tmpSize = info.getChanges().size();
-                for (int i = 0; i < tmpSize; ++i) {
-                    final TransitionInfo.Change change = init.getChanges().get(i);
-                    if (moveToTop) {
-                        if (isSameChangeTarget(openComponent, openTaskId, change)) {
-                            change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
+                // Ignore merge if there is no close target
+                if (!info.getChanges().isEmpty()) {
+                    tmpSize = init.getChanges().size();
+                    for (int i = 0; i < tmpSize; ++i) {
+                        final TransitionInfo.Change change = init.getChanges().get(i);
+                        if (change.hasFlags(FLAG_IS_WALLPAPER)) {
+                            continue;
                         }
+                        if (moveToTop) {
+                            if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+                                change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
+                            }
+                        }
+                        info.getChanges().add(i, change);
                     }
-                    info.getChanges().add(i, change);
                 }
             } else {
                 // Open transition, the transition info should be:
@@ -1342,7 +1375,7 @@
                 if (nonBackClose && nonBackOpen) {
                     for (int j = info.getChanges().size() - 1; j >= 0; --j) {
                         final TransitionInfo.Change change = info.getChanges().get(j);
-                        if (isSameChangeTarget(openComponent, openTaskId, change)) {
+                        if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
                             info.getChanges().remove(j);
                         } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
                             info.getChanges().remove(j);
@@ -1352,6 +1385,8 @@
             }
             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending "
                     + "transitions result=%s", info);
+            // Only handle one merge transition request.
+            mOpenTransitionInfo = null;
         }
 
         @Override
@@ -1362,9 +1397,18 @@
                 mClosePrepareTransition = null;
             }
             // try to handle unexpected transition
-            mergePendingTransitions(info);
+            if (mOpenTransitionInfo != null) {
+                mergePendingTransitions(info);
+            }
 
-            if (!isGestureBackTransition(info) || shouldCancelAnimation(info)
+            if (info.getType() == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION
+                    && !mCloseTransitionRequested && info.getChanges().isEmpty() && mApps != null) {
+                // Wait for post animation finish
+                finishCallback.onTransitionFinished(null);
+                t.apply();
+                return;
+            }
+            if (isNotGestureBackTransition(info) || shouldCancelAnimation(info)
                     || !mCloseTransitionRequested) {
                 if (mPrepareOpenTransition != null) {
                     applyFinishOpenTransition();
@@ -1376,6 +1420,7 @@
             }
             // Handle the commit transition if this handler is running the open transition.
             finishCallback.onTransitionFinished(null);
+            t.apply();
             if (mCloseTransitionRequested) {
                 if (mApps == null || mApps.length == 0) {
                     if (mQueuedTransition == null) {
@@ -1395,8 +1440,8 @@
 
         // Cancel close animation if something happen unexpected, let another handler to handle
         private boolean shouldCancelAnimation(@NonNull TransitionInfo info) {
-            final boolean noCloseAllowed =
-                    info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
+            final boolean noCloseAllowed = !mCloseTransitionRequested
+                    && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
             boolean unableToHandle = false;
             boolean filterTargets = false;
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -1447,7 +1492,8 @@
          * Check whether this transition is prepare for predictive back animation, which could
          * happen when core make an activity become visible.
          */
-        private boolean handlePrepareTransition(
+        @VisibleForTesting
+        boolean handlePrepareTransition(
                 @NonNull TransitionInfo info,
                 @NonNull SurfaceControl.Transaction st,
                 @NonNull SurfaceControl.Transaction ft,
@@ -1455,7 +1501,11 @@
             if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
                 return false;
             }
-
+            // Must have open target, must not have close target.
+            if (hasAnimationInMode(info, TransitionUtil::isClosingMode)
+                    || !hasAnimationInMode(info, TransitionUtil::isOpeningMode)) {
+                return false;
+            }
             SurfaceControl openingLeash = null;
             if (mApps != null) {
                 for (int i = mApps.length - 1; i >= 0; --i) {
@@ -1465,15 +1515,28 @@
                 }
             }
             if (openingLeash != null) {
+                int rootIdx = -1;
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                     final TransitionInfo.Change c = info.getChanges().get(i);
+                    if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+                        st.setAlpha(c.getLeash(), 1.0f);
+                        continue;
+                    }
                     if (TransitionUtil.isOpeningMode(c.getMode())) {
                         final Point offset = c.getEndRelOffset();
                         st.setPosition(c.getLeash(), offset.x, offset.y);
                         st.reparent(c.getLeash(), openingLeash);
                         st.setAlpha(c.getLeash(), 1.0f);
+                        rootIdx = TransitionUtil.rootIndexFor(c, info);
                     }
                 }
+                // The root leash and the leash of opening target should actually in the same level,
+                // but since the root leash is created after opening target, it will have higher
+                // layer in surface flinger. Move the root leash to lower level, so it won't affect
+                // the playing animation.
+                if (rootIdx >= 0 && info.getRootCount() > 0) {
+                    st.setLayer(info.getRoot(rootIdx).getLeash(), -1);
+                }
             }
             st.apply();
             mFinishOpenTransaction = ft;
@@ -1482,29 +1545,27 @@
             return true;
         }
 
-        private boolean isGestureBackTransition(@NonNull TransitionInfo info) {
-            for (int i = info.getChanges().size() - 1; i >= 0; --i) {
-                final TransitionInfo.Change c = info.getChanges().get(i);
-                if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
-                        && (TransitionUtil.isOpeningMode(c.getMode())
-                        || TransitionUtil.isClosingMode(c.getMode()))) {
-                    return true;
-                }
-            }
-            return false;
-        }
         /**
          * Check whether this transition is triggered from back gesture commitment.
          * Reparent the transition targets to animation leashes, so the animation won't be broken.
          */
-        private boolean handleCloseTransition(@NonNull TransitionInfo info,
+        @VisibleForTesting
+        boolean handleCloseTransition(@NonNull TransitionInfo info,
                 @NonNull SurfaceControl.Transaction st,
                 @NonNull SurfaceControl.Transaction ft,
                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
-            if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
-                    || !mCloseTransitionRequested) {
+            if (!mCloseTransitionRequested) {
                 return false;
             }
+            // must have close target
+            if (!hasAnimationInMode(info, TransitionUtil::isClosingMode)) {
+                return false;
+            }
+            if (mApps == null) {
+                // animation is done
+                applyAndFinish(st, ft, finishCallback);
+                return true;
+            }
             SurfaceControl openingLeash = null;
             SurfaceControl closingLeash = null;
             for (int i = mApps.length - 1; i >= 0; --i) {
@@ -1518,10 +1579,15 @@
             if (openingLeash != null && closingLeash != null) {
                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                     final TransitionInfo.Change c = info.getChanges().get(i);
+                    if (c.hasFlags(FLAG_IS_WALLPAPER)) {
+                        st.setAlpha(c.getLeash(), 1.0f);
+                        continue;
+                    }
                     if (TransitionUtil.isOpeningMode(c.getMode())) {
                         final Point offset = c.getEndRelOffset();
                         st.setPosition(c.getLeash(), offset.x, offset.y);
                         st.reparent(c.getLeash(), openingLeash);
+                        st.setAlpha(c.getLeash(), 1.0f);
                     } else if (TransitionUtil.isClosingMode(c.getMode())) {
                         st.reparent(c.getLeash(), closingLeash);
                     }
@@ -1592,6 +1658,25 @@
         }
     }
 
+    private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) {
+        return !hasAnimationInMode(info, TransitionUtil::isOpenOrCloseMode);
+    }
+
+    private static boolean hasAnimationInMode(@NonNull TransitionInfo info,
+            Predicate<Integer> mode) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change c = info.getChanges().get(i);
+            if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) && mode.test(c.getMode())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static WindowContainerToken findToken(TransitionInfo.Change change) {
+        return change.getContainer();
+    }
+
     private static ComponentName findComponentName(TransitionInfo.Change change) {
         final ComponentName componentName = change.getActivityComponent();
         if (componentName != null) {
@@ -1613,11 +1698,13 @@
     }
 
     private static boolean isSameChangeTarget(ComponentName topActivity, int taskId,
-            TransitionInfo.Change change) {
+            WindowContainerToken token, TransitionInfo.Change change) {
         final ComponentName openChange = findComponentName(change);
         final int firstTaskId = findTaskId(change);
+        final WindowContainerToken openToken = findToken(change);
         return (openChange != null && openChange == topActivity)
-                || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId);
+                || (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
+                || (openToken != null && token == openToken);
     }
 
     private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index c7e8df9..32e809a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -51,8 +51,8 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.animation.Interpolators
 import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.animation.Interpolators
 import kotlin.math.abs
 import kotlin.math.max
 import kotlin.math.min
@@ -189,10 +189,13 @@
         preparePreCommitEnteringRectMovement()
 
         background.ensureBackground(
-            closingTarget!!.windowConfiguration.bounds,
-            getBackgroundColor(),
-            transaction,
-            statusbarHeight
+                closingTarget!!.windowConfiguration.bounds,
+                getBackgroundColor(),
+                transaction,
+                statusbarHeight,
+                if (closingTarget!!.windowConfiguration.tasksAreFloating())
+                    closingTarget!!.localBounds else null,
+                cornerRadius
         )
         ensureScrimLayer()
         if (isLetterboxed && enteringHasSameLetterbox) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index e2b0513..3fcceca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -51,7 +51,7 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 import javax.inject.Inject;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index c747e1e..66d8a5f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -20,7 +20,7 @@
 import android.window.BackEvent
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 import javax.inject.Inject
 import kotlin.math.max
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
index 6fafa75..ae2c7b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/ShellBackAnimationRegistry.java
@@ -23,6 +23,8 @@
 import android.util.SparseArray;
 import android.window.BackNavigationInfo;
 
+import java.util.ArrayList;
+
 /** Registry for all types of default back animations */
 public class ShellBackAnimationRegistry {
     private static final String TAG = "ShellBackPreview";
@@ -31,6 +33,8 @@
     private ShellBackAnimation mDefaultCrossActivityAnimation;
     private final ShellBackAnimation mCustomizeActivityAnimation;
     private final ShellBackAnimation mCrossTaskAnimation;
+    private boolean mSupportedAnimatorsChanged = false;
+    private final ArrayList<Integer> mSupportedAnimators = new ArrayList<>();
 
     public ShellBackAnimationRegistry(
             @ShellBackAnimation.CrossActivity @Nullable ShellBackAnimation crossActivityAnimation,
@@ -60,7 +64,7 @@
         mDefaultCrossActivityAnimation = crossActivityAnimation;
         mCustomizeActivityAnimation = customizeActivityAnimation;
         mCrossTaskAnimation = crossTaskAnimation;
-
+        updateSupportedAnimators();
         // TODO(b/236760237): register dialog close animation when it's completed.
     }
 
@@ -71,6 +75,7 @@
         if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
             mDefaultCrossActivityAnimation = null;
         }
+        updateSupportedAnimators();
     }
 
     void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
@@ -79,6 +84,24 @@
         if (BackNavigationInfo.TYPE_CROSS_ACTIVITY == type) {
             mDefaultCrossActivityAnimation = null;
         }
+        updateSupportedAnimators();
+    }
+
+    private void updateSupportedAnimators() {
+        mSupportedAnimators.clear();
+        for (int i = mAnimationDefinition.size() - 1; i >= 0; --i) {
+            mSupportedAnimators.add(mAnimationDefinition.keyAt(i));
+        }
+        mSupportedAnimatorsChanged = true;
+    }
+
+    boolean hasSupportedAnimatorsChanged() {
+        return mSupportedAnimatorsChanged;
+    }
+
+    ArrayList<Integer> getSupportedAnimators() {
+        mSupportedAnimatorsChanged = false;
+        return mSupportedAnimators;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index f9a1d94..c1dadad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 import java.util.EnumSet;
 
@@ -357,7 +357,9 @@
 
     void showBadge() {
         Bitmap appBadgeBitmap = mBubble.getAppBadge();
-        if (appBadgeBitmap == null) {
+        final boolean isAppLaunchIntent = (mBubble instanceof Bubble)
+                && ((Bubble) mBubble).isAppLaunchIntent();
+        if (appBadgeBitmap == null || isAppLaunchIntent) {
             mAppIcon.setVisibility(GONE);
             return;
         }
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 7dbbb04..0c95934 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
@@ -50,9 +50,12 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
 import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -78,6 +81,7 @@
     private final LocusId mLocusId;
 
     private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
 
     private long mLastUpdated;
     private long mLastAccessed;
@@ -110,7 +114,10 @@
     @Nullable
     private BubbleTaskView mBubbleTaskView;
 
+    @Nullable
     private BubbleViewInfoTask mInflationTask;
+    @Nullable
+    private BubbleViewInfoTaskLegacy mInflationTaskLegacy;
     private boolean mInflateSynchronously;
     private boolean mPendingIntentCanceled;
     private boolean mIsImportantConversation;
@@ -202,7 +209,9 @@
     @VisibleForTesting(visibility = PRIVATE)
     public Bubble(@NonNull final String key, @NonNull final ShortcutInfo shortcutInfo,
             final int desiredHeight, final int desiredHeightResId, @Nullable final String title,
-            int taskId, @Nullable final String locus, boolean isDismissable, Executor mainExecutor,
+            int taskId, @Nullable final String locus, boolean isDismissable,
+            @ShellMainThread Executor mainExecutor,
+            @ShellBackgroundThread Executor bgExecutor,
             final Bubbles.BubbleMetadataFlagListener listener) {
         Objects.requireNonNull(key);
         Objects.requireNonNull(shortcutInfo);
@@ -221,6 +230,7 @@
         mTitle = title;
         mShowBubbleUpdateDot = false;
         mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mTaskId = taskId;
         mBubbleMetadataFlagListener = listener;
         mIsAppBubble = false;
@@ -232,7 +242,8 @@
             @Nullable Icon icon,
             boolean isAppBubble,
             String key,
-            Executor mainExecutor) {
+            @ShellMainThread Executor mainExecutor,
+            @ShellBackgroundThread Executor bgExecutor) {
         mGroupKey = null;
         mLocusId = null;
         mFlags = 0;
@@ -242,25 +253,48 @@
         mKey = key;
         mShowBubbleUpdateDot = false;
         mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mTaskId = INVALID_TASK_ID;
         mAppIntent = intent;
         mDesiredHeight = Integer.MAX_VALUE;
         mPackageName = intent.getPackage();
+    }
 
+    private Bubble(ShortcutInfo info, @ShellMainThread Executor mainExecutor,
+            @ShellBackgroundThread Executor bgExecutor) {
+        mGroupKey = null;
+        mLocusId = null;
+        mFlags = 0;
+        mUser = info.getUserHandle();
+        mIcon = info.getIcon();
+        mIsAppBubble = false;
+        mKey = getBubbleKeyForShortcut(info);
+        mShowBubbleUpdateDot = false;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
+        mTaskId = INVALID_TASK_ID;
+        mAppIntent = null;
+        mDesiredHeight = Integer.MAX_VALUE;
+        mPackageName = info.getPackage();
+        mShortcutInfo = info;
     }
 
     /** Creates an app bubble. */
-    public static Bubble createAppBubble(
-            Intent intent,
-            UserHandle user,
-            @Nullable Icon icon,
-            Executor mainExecutor) {
+    public static Bubble createAppBubble(Intent intent, UserHandle user, @Nullable Icon icon,
+            @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
         return new Bubble(intent,
                 user,
                 icon,
                 /* isAppBubble= */ true,
                 /* key= */ getAppBubbleKeyForApp(intent.getPackage(), user),
-                mainExecutor);
+                mainExecutor, bgExecutor);
+    }
+
+    /** Creates a shortcut bubble. */
+    public static Bubble createShortcutBubble(
+            ShortcutInfo info,
+            @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
+        return new Bubble(info, mainExecutor, bgExecutor);
     }
 
     /**
@@ -273,11 +307,19 @@
         return KEY_APP_BUBBLE + ":" + user.getIdentifier()  + ":" + packageName;
     }
 
+    /**
+     * Returns the key for a shortcut bubble using {@code packageName}, {@code user}, and the
+     * {@code shortcutInfo} id.
+     */
+    public static String getBubbleKeyForShortcut(ShortcutInfo info) {
+        return info.getPackage() + ":" + info.getUserId() + ":" + info.getId();
+    }
+
     @VisibleForTesting(visibility = PRIVATE)
     public Bubble(@NonNull final BubbleEntry entry,
             final Bubbles.BubbleMetadataFlagListener listener,
             final Bubbles.PendingIntentCanceledListener intentCancelListener,
-            Executor mainExecutor) {
+            @ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
         mIsAppBubble = false;
         mKey = entry.getKey();
         mGroupKey = entry.getGroupKey();
@@ -292,6 +334,7 @@
             });
         };
         mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mTaskId = INVALID_TASK_ID;
         setEntry(entry);
     }
@@ -306,7 +349,8 @@
                 getPackageName(),
                 getTitle(),
                 getAppName(),
-                isImportantConversation());
+                isImportantConversation(),
+                !isAppLaunchIntent());
     }
 
     @Override
@@ -525,40 +569,71 @@
             @Nullable BubbleBarLayerView layerView,
             BubbleIconFactory iconFactory,
             boolean skipInflation) {
-        if (isBubbleLoading()) {
-            mInflationTask.cancel(true /* mayInterruptIfRunning */);
-        }
-        mInflationTask = new BubbleViewInfoTask(this,
-                context,
-                expandedViewManager,
-                taskViewFactory,
-                positioner,
-                stackView,
-                layerView,
-                iconFactory,
-                skipInflation,
-                callback,
-                mMainExecutor);
-        if (mInflateSynchronously) {
-            mInflationTask.onPostExecute(mInflationTask.doInBackground());
+        ProtoLog.v(WM_SHELL_BUBBLES, "Inflate bubble key=%s", getKey());
+        if (Flags.bubbleViewInfoExecutors()) {
+            if (mInflationTask != null && !mInflationTask.isFinished()) {
+                mInflationTask.cancel();
+            }
+            mInflationTask = new BubbleViewInfoTask(this,
+                    context,
+                    expandedViewManager,
+                    taskViewFactory,
+                    positioner,
+                    stackView,
+                    layerView,
+                    iconFactory,
+                    skipInflation,
+                    callback,
+                    mMainExecutor,
+                    mBgExecutor);
+            if (mInflateSynchronously) {
+                mInflationTask.startSync();
+            } else {
+                mInflationTask.start();
+            }
         } else {
-            mInflationTask.execute();
+            if (mInflationTaskLegacy != null && mInflationTaskLegacy.getStatus() != FINISHED) {
+                mInflationTaskLegacy.cancel(true /* mayInterruptIfRunning */);
+            }
+            mInflationTaskLegacy = new BubbleViewInfoTaskLegacy(this,
+                    context,
+                    expandedViewManager,
+                    taskViewFactory,
+                    positioner,
+                    stackView,
+                    layerView,
+                    iconFactory,
+                    skipInflation,
+                    bubble -> {
+                        if (callback != null) {
+                            callback.onBubbleViewsReady(bubble);
+                        }
+                    },
+                    mMainExecutor);
+            if (mInflateSynchronously) {
+                mInflationTaskLegacy.onPostExecute(mInflationTaskLegacy.doInBackground());
+            } else {
+                mInflationTaskLegacy.execute();
+            }
         }
     }
 
-    private boolean isBubbleLoading() {
-        return mInflationTask != null && mInflationTask.getStatus() != FINISHED;
-    }
-
     boolean isInflated() {
         return (mIconView != null && mExpandedView != null) || mBubbleBarExpandedView != null;
     }
 
     void stopInflation() {
-        if (mInflationTask == null) {
-            return;
+        if (Flags.bubbleViewInfoExecutors()) {
+            if (mInflationTask == null) {
+                return;
+            }
+            mInflationTask.cancel();
+        } else {
+            if (mInflationTaskLegacy == null) {
+                return;
+            }
+            mInflationTaskLegacy.cancel(true /* mayInterruptIfRunning */);
         }
-        mInflationTask.cancel(true /* mayInterruptIfRunning */);
     }
 
     void setViewInfo(BubbleViewInfoTask.BubbleViewInfo info) {
@@ -594,6 +669,42 @@
     }
 
     /**
+     * @deprecated {@link BubbleViewInfoTaskLegacy} is deprecated.
+     */
+    @Deprecated
+    void setViewInfoLegacy(BubbleViewInfoTaskLegacy.BubbleViewInfo info) {
+        if (!isInflated()) {
+            mIconView = info.imageView;
+            mExpandedView = info.expandedView;
+            mBubbleBarExpandedView = info.bubbleBarExpandedView;
+        }
+
+        mShortcutInfo = info.shortcutInfo;
+        mAppName = info.appName;
+        if (mTitle == null) {
+            mTitle = mAppName;
+        }
+        mFlyoutMessage = info.flyoutMessage;
+
+        mBadgeBitmap = info.badgeBitmap;
+        mRawBadgeBitmap = info.rawBadgeBitmap;
+        mBubbleBitmap = info.bubbleBitmap;
+
+        mDotColor = info.dotColor;
+        mDotPath = info.dotPath;
+
+        if (mExpandedView != null) {
+            mExpandedView.update(this /* bubble */);
+        }
+        if (mBubbleBarExpandedView != null) {
+            mBubbleBarExpandedView.update(this /* bubble */);
+        }
+        if (mIconView != null) {
+            mIconView.setRenderedBubble(this /* bubble */);
+        }
+    }
+
+    /**
      * Set visibility of bubble in the expanded state.
      *
      * <p>Note that this contents visibility doesn't affect visibility at {@link android.view.View},
@@ -888,6 +999,17 @@
         return mIntent;
     }
 
+    /**
+     * Whether this bubble represents the full app, i.e. the intent used is the launch
+     * intent for an app. In this case we don't show a badge on the icon.
+     */
+    public boolean isAppLaunchIntent() {
+        if (Flags.enableBubbleAnything() && mAppIntent != null) {
+            return mAppIntent.hasCategory("android.intent.category.LAUNCHER");
+        }
+        return false;
+    }
+
     @Nullable
     PendingIntent getDeleteIntent() {
         return mDeleteIntent;
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 949a723..c545d73 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
@@ -35,7 +35,7 @@
 import static com.android.wm.shell.bubbles.Bubbles.DISMISS_SHORTCUT_REMOVED;
 import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_CHANGED;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_BUBBLES;
 
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
@@ -104,14 +104,14 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -1225,7 +1225,7 @@
         mBubblePositioner.setBubbleBarLocation(location);
         mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
         if (mBubbleData.getSelectedBubble() != null) {
-            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
+            showExpandedViewForBubbleBar();
         }
     }
 
@@ -1243,7 +1243,7 @@
         }
         if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) {
             // We did not remove the selected bubble. Expand it again
-            mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
+            showExpandedViewForBubbleBar();
         }
     }
 
@@ -1335,6 +1335,40 @@
     }
 
     /**
+     * Expands and selects a bubble created or found via the provided shortcut info.
+     *
+     * @param info the shortcut info for the bubble.
+     */
+    public void expandStackAndSelectBubble(ShortcutInfo info) {
+        if (!Flags.enableBubbleAnything()) return;
+        Bubble b = mBubbleData.getOrCreateBubble(info); // Removes from overflow
+        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - shortcut=%s", info);
+        if (b.isInflated()) {
+            mBubbleData.setSelectedBubbleAndExpandStack(b);
+        } else {
+            b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+        }
+    }
+
+    /**
+     * Expands and selects a bubble created or found for this app.
+     *
+     * @param intent the intent for the bubble.
+     */
+    public void expandStackAndSelectBubble(Intent intent) {
+        if (!Flags.enableBubbleAnything()) return;
+        Bubble b = mBubbleData.getOrCreateBubble(intent); // Removes from overflow
+        ProtoLog.v(WM_SHELL_BUBBLES, "expandStackAndSelectBubble - intent=%s", intent);
+        if (b.isInflated()) {
+            mBubbleData.setSelectedBubbleAndExpandStack(b);
+        } else {
+            b.enable(Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
+            inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+        }
+    }
+
+    /**
      * Expands and selects a bubble based on the provided {@link BubbleEntry}. If no bubble
      * exists for this entry, and it is able to bubble, a new bubble will be created.
      *
@@ -1461,7 +1495,7 @@
                 b.setAppBubbleIntent(intent);
             } else {
                 // App bubble does not exist, lets add and expand it
-                b = Bubble.createAppBubble(intent, user, icon, mMainExecutor);
+                b = Bubble.createAppBubble(intent, user, icon, mMainExecutor, mBackgroundExecutor);
             }
             ProtoLog.d(WM_SHELL_BUBBLES, "inflateAndAdd %s", appBubbleKey);
             b.setShouldAutoExpand(true);
@@ -1963,15 +1997,10 @@
 
         @Override
         public void expansionChanged(boolean isExpanded) {
-            if (mLayerView != null) {
-                if (!isExpanded) {
-                    mLayerView.collapse();
-                } else {
-                    BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
-                    if (selectedBubble != null) {
-                        mLayerView.showExpandedView(selectedBubble);
-                    }
-                }
+            // in bubble bar mode, let the request to show the expanded view come from launcher.
+            // only collapse here if we're collapsing.
+            if (mLayerView != null && !isExpanded) {
+                mLayerView.collapse();
             }
         }
 
@@ -2117,6 +2146,13 @@
         }
     };
 
+    private void showExpandedViewForBubbleBar() {
+        BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
+        if (selectedBubble != null && mLayerView != null) {
+            mLayerView.showExpandedView(selectedBubble);
+        }
+    }
+
     private void updateOverflowButtonDot() {
         BubbleOverflow overflow = mBubbleData.getOverflow();
         if (overflow == null) return;
@@ -2183,7 +2219,6 @@
         // And since all children are removed, remove the summary.
         removeCallback.accept(-1);
 
-        // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
         mBubbleData.addSummaryToSuppress(summary.getStatusBarNotification().getGroupKey(),
                 summary.getKey());
     }
@@ -2323,6 +2358,7 @@
      * @param entry   the entry to bubble.
      */
     static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
+        if (Flags.enableBubbleAnything()) return true;
         PendingIntent intent = entry.getBubbleMetadata() != null
                 ? entry.getBubbleMetadata().getIntent()
                 : null;
@@ -2439,6 +2475,16 @@
         }
 
         @Override
+        public void showShortcutBubble(ShortcutInfo info) {
+            mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(info));
+        }
+
+        @Override
+        public void showAppBubble(Intent intent) {
+            mMainExecutor.execute(() -> mController.expandStackAndSelectBubble(intent));
+        }
+
+        @Override
         public void showBubble(String key, int topOnScreen) {
             mMainExecutor.execute(
                     () -> mController.expandStackAndSelectBubbleFromLauncher(key, topOnScreen));
@@ -2488,6 +2534,15 @@
                 if (mLayerView != null) mLayerView.updateExpandedView();
             });
         }
+
+        @Override
+        public void showExpandedView() {
+            mMainExecutor.execute(() -> {
+                if (mLayerView != null) {
+                    showExpandedViewForBubbleBar();
+                }
+            });
+        }
     }
 
     private class BubblesImpl implements Bubbles {
@@ -2634,6 +2689,13 @@
         }
 
         @Override
+        public void expandStackAndSelectBubble(ShortcutInfo info) {
+            mMainExecutor.execute(() -> {
+                BubbleController.this.expandStackAndSelectBubble(info);
+            });
+        }
+
+        @Override
         public void expandStackAndSelectBubble(Bubble bubble) {
             mMainExecutor.execute(() -> {
                 BubbleController.this.expandStackAndSelectBubble(bubble);
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 b6da761..709a7bd 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
@@ -23,8 +23,10 @@
 import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
 import android.content.LocusId;
 import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -39,8 +41,10 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubbles.DismissReason;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
-import com.android.wm.shell.common.bubbles.RemovedBubble;
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.RemovedBubble;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -199,6 +203,7 @@
     private final BubblePositioner mPositioner;
     private final BubbleEducationController mEducationController;
     private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
     /** Bubbles that are actively in the stack. */
     private final List<Bubble> mBubbles;
     /** Bubbles that aged out to overflow. */
@@ -244,12 +249,14 @@
     private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
 
     public BubbleData(Context context, BubbleLogger bubbleLogger, BubblePositioner positioner,
-            BubbleEducationController educationController, Executor mainExecutor) {
+            BubbleEducationController educationController, @ShellMainThread Executor mainExecutor,
+            @ShellBackgroundThread Executor bgExecutor) {
         mContext = context;
         mLogger = bubbleLogger;
         mPositioner = positioner;
         mEducationController = educationController;
         mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mOverflow = new BubbleOverflow(context, positioner);
         mBubbles = new ArrayList<>();
         mOverflowBubbles = new ArrayList<>();
@@ -421,23 +428,20 @@
         Bubble bubbleToReturn = getBubbleInStackWithKey(key);
 
         if (bubbleToReturn == null) {
-            bubbleToReturn = getOverflowBubbleWithKey(key);
-            if (bubbleToReturn != null) {
-                // Promoting from overflow
-                mOverflowBubbles.remove(bubbleToReturn);
-                if (mOverflowBubbles.isEmpty()) {
-                    mStateChange.showOverflowChanged = true;
+            // Check if it's in the overflow
+            bubbleToReturn = findAndRemoveBubbleFromOverflow(key);
+            if (bubbleToReturn == null) {
+                if (entry != null) {
+                    // Not in the overflow, have an entry, so it's a new bubble
+                    bubbleToReturn = new Bubble(entry,
+                            mBubbleMetadataFlagListener,
+                            mCancelledListener,
+                            mMainExecutor,
+                            mBgExecutor);
+                } else {
+                    // If there's no entry it must be a persisted bubble
+                    bubbleToReturn = persistedBubble;
                 }
-            } else if (mPendingBubbles.containsKey(key)) {
-                // Update while it was pending
-                bubbleToReturn = mPendingBubbles.get(key);
-            } else if (entry != null) {
-                // New bubble
-                bubbleToReturn = new Bubble(entry, mBubbleMetadataFlagListener, mCancelledListener,
-                        mMainExecutor);
-            } else {
-                // Persisted bubble being promoted
-                bubbleToReturn = persistedBubble;
             }
         }
 
@@ -448,6 +452,46 @@
         return bubbleToReturn;
     }
 
+    Bubble getOrCreateBubble(ShortcutInfo info) {
+        String bubbleKey = Bubble.getBubbleKeyForShortcut(info);
+        Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+        if (bubbleToReturn == null) {
+            bubbleToReturn = Bubble.createShortcutBubble(info, mMainExecutor, mBgExecutor);
+        }
+        return bubbleToReturn;
+    }
+
+    Bubble getOrCreateBubble(Intent intent) {
+        UserHandle user = UserHandle.of(mCurrentUserId);
+        String bubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(),
+                user);
+        Bubble bubbleToReturn = findAndRemoveBubbleFromOverflow(bubbleKey);
+        if (bubbleToReturn == null) {
+            bubbleToReturn = Bubble.createAppBubble(intent, user, null, mMainExecutor, mBgExecutor);
+        }
+        return bubbleToReturn;
+    }
+
+    @Nullable
+    private Bubble findAndRemoveBubbleFromOverflow(String key) {
+        Bubble bubbleToReturn = getBubbleInStackWithKey(key);
+        if (bubbleToReturn != null) {
+            return bubbleToReturn;
+        }
+        bubbleToReturn = getOverflowBubbleWithKey(key);
+        if (bubbleToReturn != null) {
+            mOverflowBubbles.remove(bubbleToReturn);
+            // Promoting from overflow
+            mOverflowBubbles.remove(bubbleToReturn);
+            if (mOverflowBubbles.isEmpty()) {
+                mStateChange.showOverflowChanged = true;
+            }
+        } else if (mPendingBubbles.containsKey(key)) {
+            bubbleToReturn = mPendingBubbles.get(key);
+        }
+        return bubbleToReturn;
+    }
+
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
      * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context, BubbleExpandedViewManager,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index df12999..818ba45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -31,6 +31,9 @@
 import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
 import com.android.wm.shell.bubbles.storage.BubbleVolatileRepository
 import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import java.util.concurrent.Executor
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
@@ -41,7 +44,8 @@
 
 class BubbleDataRepository(
     private val launcherApps: LauncherApps,
-    private val mainExecutor: ShellExecutor,
+    @ShellMainThread private val mainExecutor: ShellExecutor,
+    @ShellBackgroundThread private val bgExecutor: Executor,
     private val persistentRepository: BubblePersistentRepository,
 ) {
     private val volatileRepository = BubbleVolatileRepository(launcherApps)
@@ -259,8 +263,8 @@
                                 entity.locus,
                                 entity.isDismissable,
                                 mainExecutor,
-                                bubbleMetadataFlagListener
-                        )
+                                bgExecutor,
+                                bubbleMetadataFlagListener)
                     }
         }
         mainExecutor.execute { cb(bubbles) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index fdb4523..5295526 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -71,7 +71,7 @@
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.AlphaOptimizedButton;
-import com.android.wm.shell.common.TriangleShape;
+import com.android.wm.shell.shared.TriangleShape;
 import com.android.wm.shell.taskview.TaskView;
 
 import java.io.PrintWriter;
@@ -232,6 +232,9 @@
                     fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
                     fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
 
+                    final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+                            || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
+
                     if (mBubble.isAppBubble()) {
                         Context context =
                                 mContext.createContextAsUser(
@@ -246,7 +249,8 @@
                                 /* options= */ null);
                         mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
                                 launchBounds);
-                    } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+                    } else if (!mIsOverflow && isShortcutBubble) {
+                        ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
                         options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
                                 options, launchBounds);
@@ -917,7 +921,11 @@
             return;
         }
         boolean isNew = mBubble == null || didBackingContentChange(bubble);
-        if (isNew || bubble.getKey().equals(mBubble.getKey())) {
+        boolean isUpdate = bubble != null && mBubble != null
+                && bubble.getKey().equals(mBubble.getKey());
+        ProtoLog.d(WM_SHELL_BUBBLES, "BubbleExpandedView - update bubble=%s; isNew=%b; isUpdate=%b",
+                bubble.getKey(), isNew, isUpdate);
+        if (isNew || isUpdate) {
             mBubble = bubble;
             mManageButton.setContentDescription(getResources().getString(
                     R.string.bubbles_settings_button_description, bubble.getAppName()));
@@ -1148,5 +1156,7 @@
         pw.print(prefix); pw.println("BubbleExpandedView:");
         pw.print(prefix); pw.print("  taskId: "); pw.println(mTaskId);
         pw.print(prefix); pw.print("  stackView: "); pw.println(mStackView);
+        pw.print(prefix); pw.print("  contentVisibility: "); pw.println(mIsContentVisible);
+        pw.print(prefix); pw.print("  isAnimating: "); pw.println(mIsAnimating);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
index 3d9bf03..ec4854b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.bubbles
 
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+
 /** Manager interface for bubble expanded views. */
 interface BubbleExpandedViewManager {
 
@@ -30,6 +32,7 @@
     fun isStackExpanded(): Boolean
     fun isShowingAsBubbleBar(): Boolean
     fun hideCurrentInputMethod()
+    fun updateBubbleBarLocation(location: BubbleBarLocation)
 
     companion object {
         /**
@@ -78,6 +81,10 @@
                 override fun hideCurrentInputMethod() {
                     controller.hideCurrentInputMethod()
                 }
+
+                override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+                    controller.bubbleBarLocation = location
+                }
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 42de401..1711dca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -19,8 +19,8 @@
 import static android.graphics.Paint.ANTI_ALIAS_FLAG;
 import static android.graphics.Paint.FILTER_BITMAP_FLAG;
 
-import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
-import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
@@ -50,7 +50,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.TriangleShape;
+import com.android.wm.shell.shared.TriangleShape;
 
 /**
  * Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
index bdb09e1..fd110a276 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePopupViewExt.kt
@@ -17,8 +17,8 @@
 
 import android.graphics.Color
 import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
 
 /**
  * A convenience method to setup the [BubblePopupView] with the correct config using local resources
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 0cf187b..c386c93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -32,7 +32,7 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 
 /**
  * Keeps track of display size, configuration, and specific bubble sizes. One place for all
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index efa1031..2795881 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -19,15 +19,15 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
-import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
 import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.LEFT;
 import static com.android.wm.shell.bubbles.BubblePositioner.StackPinnedEdge.RIGHT;
-import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -82,7 +82,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
 import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
 import com.android.wm.shell.bubbles.animation.ExpandedAnimationController;
@@ -92,10 +91,11 @@
 import com.android.wm.shell.bubbles.animation.StackAnimationController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.bubbles.RelativeTouchListener;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
+import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import java.io.PrintWriter;
 import java.math.BigDecimal;
@@ -339,6 +339,7 @@
         pw.println(mExpandedViewContainer.getAnimationMatrix());
         pw.print("  stack visibility :       "); pw.println(getVisibility());
         pw.print("  temporarilyInvisible:    "); pw.println(mTemporarilyInvisible);
+        pw.print("  expandedViewTemporarilyHidden: "); pw.println(mExpandedViewTemporarilyHidden);
         mStackAnimationController.dump(pw);
         mExpandedAnimationController.dump(pw);
 
@@ -1127,6 +1128,8 @@
                 if (expandedView != null) {
                     // We need to be Z ordered on top in order for alpha animations to work.
                     expandedView.setSurfaceZOrderedOnTop(true);
+                    ProtoLog.d(WM_SHELL_BUBBLES, "expandedViewAlphaAnimation - start=%s",
+                            expandedView.getBubbleKey());
                     expandedView.setAnimating(true);
                     mExpandedViewContainer.setVisibility(VISIBLE);
                 }
@@ -1142,6 +1145,8 @@
                         // = 0f remains in effect.
                         && !mExpandedViewTemporarilyHidden) {
                     expandedView.setSurfaceZOrderedOnTop(false);
+                    ProtoLog.d(WM_SHELL_BUBBLES, "expandedViewAlphaAnimation - end=%s",
+                            expandedView.getBubbleKey());
                     expandedView.setAnimating(false);
                 }
             }
@@ -1601,6 +1606,11 @@
                 getResources().getColor(android.R.color.system_neutral1_1000)));
         mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
                 getResources().getColor(android.R.color.system_neutral1_1000)));
+        if (mShowingManage) {
+            // the manage menu location depends on the manage button location which may need a
+            // layout pass, so post this to the looper
+            post(() -> showManageMenu(true));
+        }
     }
 
     /**
@@ -2165,7 +2175,14 @@
         final BubbleViewProvider previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
         mExpandedViewAnimationController.setExpandedView(getExpandedView());
-
+        final String previouslySelectedKey = previouslySelected != null
+                ? previouslySelected.getKey()
+                : "null";
+        final String newlySelectedKey = mExpandedBubble != null
+                ? mExpandedBubble.getKey()
+                : "null";
+        ProtoLog.d(WM_SHELL_BUBBLES, "showNewlySelectedBubble b=%s, previouslySelected=%s,"
+                        + " mIsExpanded=%b", newlySelectedKey, previouslySelectedKey, mIsExpanded);
         if (mIsExpanded) {
             hideCurrentInputMethod();
 
@@ -2567,6 +2584,8 @@
             expandedView.setContentAlpha(0f);
             expandedView.setBackgroundAlpha(0f);
 
+            ProtoLog.d(WM_SHELL_BUBBLES, "animateBubbleExpansion, setAnimating true for bubble=%s",
+                    expandedView.getBubbleKey());
             // We'll be starting the alpha animation after a slight delay, so set this flag early
             // here.
             expandedView.setAnimating(true);
@@ -2731,6 +2750,11 @@
         mAnimatingOutSurfaceAlphaAnimator.reverse();
         mExpandedViewAlphaAnimator.start();
 
+        if (mExpandedBubble != null) {
+            ProtoLog.d(WM_SHELL_BUBBLES, "animateSwitchBubbles, switchingTo b=%s",
+                    mExpandedBubble.getKey());
+        }
+
         if (mPositioner.showBubblesVertically()) {
             float translationX = mStackAnimationController.isStackOnLeftSide()
                     ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5e2141a..5f8f0fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -36,6 +36,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.taskview.TaskView;
 
 /**
@@ -110,6 +111,8 @@
                     fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
                     fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
 
+                    final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
+                            || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
                     if (mBubble.isAppBubble()) {
                         Context context =
                                 mContext.createContextAsUser(
@@ -124,7 +127,7 @@
                                 /* options= */ null);
                         mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
                                 launchBounds);
-                    } else if (mBubble.hasMetadataShortcutId()) {
+                    } else if (isShortcutBubble) {
                         options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
                                 options, launchBounds);
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 69119cf..13855f7 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
@@ -20,6 +20,7 @@
 import static com.android.wm.shell.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,13 +35,13 @@
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
 import android.util.Log;
 import android.util.PathParser;
 import android.view.LayoutInflater;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
+import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.wm.shell.R;
@@ -50,14 +51,14 @@
 import java.lang.ref.WeakReference;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Simple task to inflate views & load necessary info to display a bubble.
  */
-public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask.BubbleViewInfo> {
+public class BubbleViewInfoTask {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleViewInfoTask" : TAG_BUBBLES;
 
-
     /**
      * Callback to find out when the bubble has been inflated & necessary data loaded.
      */
@@ -68,17 +69,22 @@
         void onBubbleViewsReady(Bubble bubble);
     }
 
-    private Bubble mBubble;
-    private WeakReference<Context> mContext;
-    private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
-    private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
-    private WeakReference<BubblePositioner> mPositioner;
-    private WeakReference<BubbleStackView> mStackView;
-    private WeakReference<BubbleBarLayerView> mLayerView;
-    private BubbleIconFactory mIconFactory;
-    private boolean mSkipInflation;
-    private Callback mCallback;
-    private Executor mMainExecutor;
+    private final Bubble mBubble;
+    private final WeakReference<Context> mContext;
+    private final WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
+    private final WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
+    private final WeakReference<BubblePositioner> mPositioner;
+    private final WeakReference<BubbleStackView> mStackView;
+    private final WeakReference<BubbleBarLayerView> mLayerView;
+    private final BubbleIconFactory mIconFactory;
+    private final boolean mSkipInflation;
+    private final Callback mCallback;
+    private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
+
+    private final AtomicBoolean mStarted = new AtomicBoolean();
+    private final AtomicBoolean mCancelled = new AtomicBoolean();
+    private final AtomicBoolean mFinished = new AtomicBoolean();
 
     /**
      * Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -94,7 +100,8 @@
             BubbleIconFactory factory,
             boolean skipInflation,
             Callback c,
-            Executor mainExecutor) {
+            Executor mainExecutor,
+            Executor bgExecutor) {
         mBubble = b;
         mContext = new WeakReference<>(context);
         mExpandedViewManager = new WeakReference<>(expandedViewManager);
@@ -106,40 +113,123 @@
         mSkipInflation = skipInflation;
         mCallback = c;
         mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
     }
 
-    @Override
-    protected BubbleViewInfo doInBackground(Void... voids) {
+    /**
+     * Load bubble view info in background using {@code bgExecutor} specified in constructor.
+     * <br>
+     * Use {@link #cancel()} to stop the task.
+     *
+     * @throws IllegalStateException if the task is already started
+     */
+    public void start() {
+        verifyCanStart();
+        if (mCancelled.get()) {
+            // We got cancelled even before start was called. Exit early
+            mFinished.set(true);
+            return;
+        }
+        mBgExecutor.execute(() -> {
+            if (mCancelled.get()) {
+                // We got cancelled while background executor was busy and this was waiting
+                mFinished.set(true);
+                return;
+            }
+            BubbleViewInfo viewInfo = loadViewInfo();
+            if (mCancelled.get()) {
+                // Do not schedule anything on main executor if we got cancelled.
+                // Loading view info involves inflating views and it is possible we get cancelled
+                // during it.
+                mFinished.set(true);
+                return;
+            }
+            mMainExecutor.execute(() -> {
+                // Before updating view info check that we did not get cancelled while waiting
+                // main executor to pick up the work
+                if (!mCancelled.get()) {
+                    updateViewInfo(viewInfo);
+                }
+                mFinished.set(true);
+            });
+        });
+    }
+
+    private void verifyCanStart() {
+        if (mStarted.getAndSet(true)) {
+            throw new IllegalStateException("Task already started");
+        }
+    }
+
+    /**
+     * Load bubble view info synchronously.
+     *
+     * @throws IllegalStateException if the task is already started
+     */
+    public void startSync() {
+        verifyCanStart();
+        if (mCancelled.get()) {
+            mFinished.set(true);
+            return;
+        }
+        updateViewInfo(loadViewInfo());
+        mFinished.set(true);
+    }
+
+    /**
+     * Cancel the task. Stops the task from running if called before {@link #start()} or
+     * {@link #startSync()}
+     */
+    public void cancel() {
+        mCancelled.set(true);
+    }
+
+    /**
+     * Return {@code true} when the task has completed loading the view info.
+     */
+    public boolean isFinished() {
+        return mFinished.get();
+    }
+
+    @Nullable
+    private BubbleViewInfo loadViewInfo() {
         if (!verifyState()) {
             // If we're in an inconsistent state, then switched modes and should just bail now.
             return null;
         }
+        ProtoLog.v(WM_SHELL_BUBBLES, "Task loading bubble view info key=%s", mBubble.getKey());
         if (mLayerView.get() != null) {
-            return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
-                    mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
-                    mBubble, mSkipInflation);
+            return BubbleViewInfo.populateForBubbleBar(mContext.get(), mTaskViewFactory.get(),
+                    mLayerView.get(), mIconFactory, mBubble, mSkipInflation);
         } else {
-            return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
-                    mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
-                    mBubble, mSkipInflation);
+            return BubbleViewInfo.populate(mContext.get(), mTaskViewFactory.get(),
+                    mPositioner.get(), mStackView.get(), mIconFactory, mBubble, mSkipInflation);
         }
     }
 
-    @Override
-    protected void onPostExecute(BubbleViewInfo viewInfo) {
-        if (isCancelled() || viewInfo == null) {
+    private void updateViewInfo(@Nullable BubbleViewInfo viewInfo) {
+        if (viewInfo == null || !verifyState()) {
             return;
         }
+        ProtoLog.v(WM_SHELL_BUBBLES, "Task updating bubble view info key=%s", mBubble.getKey());
+        if (!mBubble.isInflated()) {
+            if (viewInfo.expandedView != null) {
+                ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing expanded view key=%s",
+                        mBubble.getKey());
+                viewInfo.expandedView.initialize(mExpandedViewManager.get(), mStackView.get(),
+                        mPositioner.get(), false /* isOverflow */, viewInfo.taskView);
+            } else if (viewInfo.bubbleBarExpandedView != null) {
+                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.execute(() -> {
-            if (!verifyState()) {
-                return;
-            }
-            mBubble.setViewInfo(viewInfo);
-            if (mCallback != null) {
-                mCallback.onBubbleViewsReady(mBubble);
-            }
-        });
+        mBubble.setViewInfo(viewInfo);
+        if (mCallback != null) {
+            mCallback.onBubbleViewsReady(mBubble);
+        }
     }
 
     private boolean verifyState() {
@@ -157,6 +247,9 @@
     public static class BubbleViewInfo {
         // TODO(b/273312602): for foldables it might make sense to populate all of the views
 
+        // Only set if views where inflated as part of the task
+        @Nullable BubbleTaskView taskView;
+
         // Always populated
         ShortcutInfo shortcutInfo;
         String appName;
@@ -176,9 +269,7 @@
 
         @Nullable
         public static BubbleViewInfo populateForBubbleBar(Context c,
-                BubbleExpandedViewManager expandedViewManager,
                 BubbleTaskViewFactory taskViewFactory,
-                BubblePositioner positioner,
                 BubbleBarLayerView layerView,
                 BubbleIconFactory iconFactory,
                 Bubble b,
@@ -186,12 +277,11 @@
             BubbleViewInfo info = new BubbleViewInfo();
 
             if (!skipInflation && !b.isInflated()) {
-                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
+                ProtoLog.v(WM_SHELL_BUBBLES, "Task inflating bubble bar views key=%s", b.getKey());
+                info.taskView = b.getOrCreateBubbleTaskView(taskViewFactory);
                 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);
             }
 
             if (!populateCommonInfo(info, c, b, iconFactory)) {
@@ -205,7 +295,6 @@
         @VisibleForTesting
         @Nullable
         public static BubbleViewInfo populate(Context c,
-                BubbleExpandedViewManager expandedViewManager,
                 BubbleTaskViewFactory taskViewFactory,
                 BubblePositioner positioner,
                 BubbleStackView stackView,
@@ -216,17 +305,15 @@
 
             // View inflation: only should do this once per bubble
             if (!skipInflation && !b.isInflated()) {
+                ProtoLog.v(WM_SHELL_BUBBLES, "Task inflating bubble views key=%s", b.getKey());
                 LayoutInflater inflater = LayoutInflater.from(c);
                 info.imageView = (BadgedImageView) inflater.inflate(
                         R.layout.bubble_view, stackView, false /* attachToRoot */);
                 info.imageView.initialize(positioner);
 
-                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
+                info.taskView = b.getOrCreateBubbleTaskView(taskViewFactory);
                 info.expandedView = (BubbleExpandedView) inflater.inflate(
                         R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
-                info.expandedView.initialize(
-                        expandedViewManager, stackView, positioner, false /* isOverflow */,
-                        bubbleTaskView);
             }
 
             if (!populateCommonInfo(info, c, b, iconFactory)) {
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
new file mode 100644
index 0000000..5cfebf8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -0,0 +1,346 @@
+/*
+ * 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.wm.shell.bubbles;
+
+import static com.android.wm.shell.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
+import static com.android.wm.shell.bubbles.BadgedImageView.WHITE_SCRIM_ALPHA;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
+import android.util.Log;
+import android.util.PathParser;
+import android.view.LayoutInflater;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
+import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Simple task to inflate views & load necessary info to display a bubble.
+ *
+ * @deprecated Deprecated since this is using an AsyncTask. Use {@link BubbleViewInfoTask} instead.
+ */
+@Deprecated
+// TODO(b/353894869): remove once flag for loading view info with executors rolls out
+public class BubbleViewInfoTaskLegacy extends
+        AsyncTask<Void, Void, BubbleViewInfoTaskLegacy.BubbleViewInfo> {
+    private static final String TAG =
+            TAG_WITH_CLASS_NAME ? "BubbleViewInfoTaskLegacy" : TAG_BUBBLES;
+
+
+    /**
+     * Callback to find out when the bubble has been inflated & necessary data loaded.
+     */
+    public interface Callback {
+        /**
+         * Called when data has been loaded for the bubble.
+         */
+        void onBubbleViewsReady(Bubble bubble);
+    }
+
+    private Bubble mBubble;
+    private WeakReference<Context> mContext;
+    private WeakReference<BubbleExpandedViewManager> mExpandedViewManager;
+    private WeakReference<BubbleTaskViewFactory> mTaskViewFactory;
+    private WeakReference<BubblePositioner> mPositioner;
+    private WeakReference<BubbleStackView> mStackView;
+    private WeakReference<BubbleBarLayerView> mLayerView;
+    private BubbleIconFactory mIconFactory;
+    private boolean mSkipInflation;
+    private Callback mCallback;
+    private Executor mMainExecutor;
+
+    /**
+     * Creates a task to load information for the provided {@link Bubble}. Once all info
+     * is loaded, {@link Callback} is notified.
+     */
+    BubbleViewInfoTaskLegacy(Bubble b,
+            Context context,
+            BubbleExpandedViewManager expandedViewManager,
+            BubbleTaskViewFactory taskViewFactory,
+            BubblePositioner positioner,
+            @Nullable BubbleStackView stackView,
+            @Nullable BubbleBarLayerView layerView,
+            BubbleIconFactory factory,
+            boolean skipInflation,
+            Callback c,
+            Executor mainExecutor) {
+        mBubble = b;
+        mContext = new WeakReference<>(context);
+        mExpandedViewManager = new WeakReference<>(expandedViewManager);
+        mTaskViewFactory = new WeakReference<>(taskViewFactory);
+        mPositioner = new WeakReference<>(positioner);
+        mStackView = new WeakReference<>(stackView);
+        mLayerView = new WeakReference<>(layerView);
+        mIconFactory = factory;
+        mSkipInflation = skipInflation;
+        mCallback = c;
+        mMainExecutor = mainExecutor;
+    }
+
+    @Override
+    protected BubbleViewInfo doInBackground(Void... voids) {
+        if (!verifyState()) {
+            // If we're in an inconsistent state, then switched modes and should just bail now.
+            return null;
+        }
+        if (mLayerView.get() != null) {
+            return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
+                    mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
+                    mBubble, mSkipInflation);
+        } else {
+            return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
+                    mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
+                    mBubble, mSkipInflation);
+        }
+    }
+
+    @Override
+    protected void onPostExecute(BubbleViewInfo viewInfo) {
+        if (isCancelled() || viewInfo == null) {
+            return;
+        }
+
+        mMainExecutor.execute(() -> {
+            if (!verifyState()) {
+                return;
+            }
+            mBubble.setViewInfoLegacy(viewInfo);
+            if (mCallback != null) {
+                mCallback.onBubbleViewsReady(mBubble);
+            }
+        });
+    }
+
+    private boolean verifyState() {
+        if (mExpandedViewManager.get().isShowingAsBubbleBar()) {
+            return mLayerView.get() != null;
+        } else {
+            return mStackView.get() != null;
+        }
+    }
+
+    /**
+     * Info necessary to render a bubble.
+     */
+    @VisibleForTesting
+    public static class BubbleViewInfo {
+        // TODO(b/273312602): for foldables it might make sense to populate all of the views
+
+        // Always populated
+        ShortcutInfo shortcutInfo;
+        String appName;
+        Bitmap rawBadgeBitmap;
+
+        // Only populated when showing in taskbar
+        @Nullable BubbleBarExpandedView bubbleBarExpandedView;
+
+        // These are only populated when not showing in taskbar
+        @Nullable BadgedImageView imageView;
+        @Nullable BubbleExpandedView expandedView;
+        int dotColor;
+        Path dotPath;
+        @Nullable Bubble.FlyoutMessage flyoutMessage;
+        Bitmap bubbleBitmap;
+        Bitmap badgeBitmap;
+
+        @Nullable
+        public static BubbleViewInfo populateForBubbleBar(Context c,
+                BubbleExpandedViewManager expandedViewManager,
+                BubbleTaskViewFactory taskViewFactory,
+                BubblePositioner positioner,
+                BubbleBarLayerView layerView,
+                BubbleIconFactory iconFactory,
+                Bubble b,
+                boolean skipInflation) {
+            BubbleViewInfo info = new BubbleViewInfo();
+
+            if (!skipInflation && !b.isInflated()) {
+                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
+                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);
+            }
+
+            if (!populateCommonInfo(info, c, b, iconFactory)) {
+                // if we failed to update common fields return null
+                return null;
+            }
+
+            return info;
+        }
+
+        @VisibleForTesting
+        @Nullable
+        public static BubbleViewInfo populate(Context c,
+                BubbleExpandedViewManager expandedViewManager,
+                BubbleTaskViewFactory taskViewFactory,
+                BubblePositioner positioner,
+                BubbleStackView stackView,
+                BubbleIconFactory iconFactory,
+                Bubble b,
+                boolean skipInflation) {
+            BubbleViewInfo info = new BubbleViewInfo();
+
+            // View inflation: only should do this once per bubble
+            if (!skipInflation && !b.isInflated()) {
+                LayoutInflater inflater = LayoutInflater.from(c);
+                info.imageView = (BadgedImageView) inflater.inflate(
+                        R.layout.bubble_view, stackView, false /* attachToRoot */);
+                info.imageView.initialize(positioner);
+
+                BubbleTaskView bubbleTaskView = b.getOrCreateBubbleTaskView(taskViewFactory);
+                info.expandedView = (BubbleExpandedView) inflater.inflate(
+                        R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
+                info.expandedView.initialize(
+                        expandedViewManager, stackView, positioner, false /* isOverflow */,
+                        bubbleTaskView);
+            }
+
+            if (!populateCommonInfo(info, c, b, iconFactory)) {
+                // if we failed to update common fields return null
+                return null;
+            }
+
+            // Flyout
+            info.flyoutMessage = b.getFlyoutMessage();
+            if (info.flyoutMessage != null) {
+                info.flyoutMessage.senderAvatar =
+                        loadSenderAvatar(c, info.flyoutMessage.senderIcon);
+            }
+            return info;
+        }
+    }
+
+    /**
+     * Modifies the given {@code info} object and populates common fields in it.
+     *
+     * <p>This method returns {@code true} if the update was successful and {@code false} otherwise.
+     * Callers should assume that the info object is unusable if the update was unsuccessful.
+     */
+    private static boolean populateCommonInfo(
+            BubbleViewInfo info, Context c, Bubble b, BubbleIconFactory iconFactory) {
+        if (b.getShortcutInfo() != null) {
+            info.shortcutInfo = b.getShortcutInfo();
+        }
+
+        // App name & app icon
+        PackageManager pm = BubbleController.getPackageManagerForUser(c,
+                b.getUser().getIdentifier());
+        ApplicationInfo appInfo;
+        Drawable badgedIcon;
+        Drawable appIcon;
+        try {
+            appInfo = pm.getApplicationInfo(
+                    b.getPackageName(),
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (appInfo != null) {
+                info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
+            }
+            appIcon = pm.getApplicationIcon(b.getPackageName());
+            badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
+        } catch (PackageManager.NameNotFoundException exception) {
+            // If we can't find package... don't think we should show the bubble.
+            Log.w(TAG, "Unable to find package: " + b.getPackageName());
+            return false;
+        }
+
+        Drawable bubbleDrawable = null;
+        try {
+            // Badged bubble image
+            bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
+                    b.getIcon());
+        } catch (Exception e) {
+            // If we can't create the icon we'll default to the app icon
+            Log.w(TAG, "Exception creating icon for the bubble: " + b.getKey());
+        }
+
+        if (bubbleDrawable == null) {
+            // Default to app icon
+            bubbleDrawable = appIcon;
+        }
+
+        BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
+                b.isImportantConversation());
+        info.badgeBitmap = badgeBitmapInfo.icon;
+        // Raw badge bitmap never includes the important conversation ring
+        info.rawBadgeBitmap = b.isImportantConversation()
+                ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
+                : badgeBitmapInfo.icon;
+
+        float[] bubbleBitmapScale = new float[1];
+        info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
+
+        // Dot color & placement
+        Path iconPath = PathParser.createPathFromPathData(
+                c.getResources().getString(com.android.internal.R.string.config_icon_mask));
+        Matrix matrix = new Matrix();
+        float scale = bubbleBitmapScale[0];
+        float radius = DEFAULT_PATH_SIZE / 2f;
+        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+                radius /* pivot y */);
+        iconPath.transform(matrix);
+        info.dotPath = iconPath;
+        info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
+                Color.WHITE, WHITE_SCRIM_ALPHA);
+        return true;
+    }
+
+    @Nullable
+    static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
+        Objects.requireNonNull(context);
+        if (icon == null) return null;
+        try {
+            if (icon.getType() == Icon.TYPE_URI
+                    || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+                context.grantUriPermission(context.getPackageName(),
+                        icon.getUri(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
+            return icon.loadDrawable(context);
+        } catch (Exception e) {
+            Log.w(TAG, "loadSenderAvatar failed: " + e.getMessage());
+            return null;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 589dfd2..62895fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -23,6 +23,7 @@
 
 import android.app.NotificationChannel;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
 import android.content.pm.UserInfo;
 import android.graphics.drawable.Icon;
 import android.hardware.HardwareBuffer;
@@ -37,9 +38,9 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
@@ -118,6 +119,14 @@
 
     /**
      * Request the stack expand if needed, then select the specified Bubble as current.
+     * If no bubble exists for this entry, one is created.
+     *
+     * @param info the shortcut info to use to create the bubble.
+     */
+    void expandStackAndSelectBubble(ShortcutInfo info);
+
+    /**
+     * Request the stack expand if needed, then select the specified Bubble as current.
      *
      * @param bubble the bubble to be selected
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
index 48692d4..00a8172 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt
@@ -18,7 +18,7 @@
 package com.android.wm.shell.bubbles
 
 import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.DismissView
 
 fun DismissView.setup() {
     setup(DismissView.Config(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 0907ddd..1855b93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -18,8 +18,9 @@
 
 import android.content.Intent;
 import android.graphics.Rect;
+import android.content.pm.ShortcutInfo;
 import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 
 /**
  * Interface that is exposed to remote callers (launcher) to manipulate the bubbles feature when
@@ -48,4 +49,10 @@
     oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10;
 
     oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
+
+    oneway void showShortcutBubble(in ShortcutInfo info) = 12;
+
+    oneway void showAppBubble(in Intent intent) = 13;
+
+    oneway void showExpandedView() = 14;
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
index 14d29cd..eb907db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubblesListener.aidl
@@ -17,7 +17,7 @@
 package com.android.wm.shell.bubbles;
 
 import android.os.Bundle;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 /**
  * Listener interface that Launcher attaches to SystemUI to get bubbles callbacks.
  */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index da71b1c..39a2a7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,7 +27,7 @@
 import android.widget.LinearLayout
 import com.android.internal.R.color.system_neutral1_900
 import com.android.wm.shell.R
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 
 /**
  * User education view to highlight the manage button that allows a user to configure the settings
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index c4108c4..1660619 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,7 +26,7 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.wm.shell.R
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 
 /**
  * User education view to highlight the collapsed stack of bubbles. Shown only the first time a user
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index f925eae..8f0dfb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -33,13 +33,13 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BadgedImageView;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleStackView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import com.google.android.collect.Sets;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
index fbef6b5..7cb537a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java
@@ -40,9 +40,9 @@
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.animation.FlingAnimationUtils;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BubbleExpandedView;
 import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 47d4d07..91585dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -42,8 +42,8 @@
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleStackView;
 import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import com.google.android.collect.Sets;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 8e58db1..565fde0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -24,9 +24,9 @@
 import static android.view.View.X;
 import static android.view.View.Y;
 
-import static com.android.wm.shell.animation.Interpolators.EMPHASIZED;
-import static com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.wm.shell.bubbles.bar.BubbleBarExpandedView.CORNER_RADIUS;
+import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
+import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -42,13 +42,13 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubblePositioner;
 import com.android.wm.shell.bubbles.BubbleViewProvider;
 import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject.MagneticTarget;
+import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject.MagneticTarget;
 
 /**
  * Helper class to animate a {@link BubbleBarExpandedView} on a bubble.
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 b7834db..f90b2aa 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
@@ -26,6 +26,7 @@
 import android.graphics.Insets;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.LayoutInflater;
@@ -35,6 +36,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
@@ -43,6 +46,7 @@
 import com.android.wm.shell.bubbles.BubbleTaskView;
 import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
 import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
 import com.android.wm.shell.taskview.TaskView;
 
 import java.util.function.Supplier;
@@ -82,6 +86,7 @@
     private static final String TAG = BubbleBarExpandedView.class.getSimpleName();
     private static final int INVALID_TASK_ID = -1;
 
+    private Bubble mBubble;
     private BubbleExpandedViewManager mManager;
     private BubblePositioner mPositioner;
     private boolean mIsOverflow;
@@ -190,16 +195,7 @@
             // Handle view needs to draw on top of task view.
             bringChildToFront(mHandleView);
 
-            mHandleView.setAccessibilityDelegate(new AccessibilityDelegate() {
-                @Override
-                public void onInitializeAccessibilityNodeInfo(View host,
-                        AccessibilityNodeInfo info) {
-                    super.onInitializeAccessibilityNodeInfo(host, info);
-                    info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
-                            AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
-                            R.string.bubble_accessibility_action_expand_menu)));
-                }
-            });
+            mHandleView.setAccessibilityDelegate(new HandleViewAccessibilityDelegate());
         }
         mMenuViewController = new BubbleBarMenuViewController(mContext, this);
         mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
@@ -232,6 +228,13 @@
             public void onDismissBubble(Bubble bubble) {
                 mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE);
             }
+
+            @Override
+            public void onMoveToFullscreen(Bubble bubble) {
+                if (mTaskView != null) {
+                    mTaskView.moveToFullscreen();
+                }
+            }
         });
         mHandleView.setOnClickListener(view -> {
             mMenuViewController.showMenu(true /* animated */);
@@ -338,6 +341,7 @@
 
     /** Updates the bubble shown in the expanded view. */
     public void update(Bubble bubble) {
+        mBubble = bubble;
         mBubbleTaskViewHelper.update(bubble);
         mMenuViewController.updateMenu(bubble);
     }
@@ -476,4 +480,51 @@
             invalidateOutline();
         }
     }
+
+    private class HandleViewAccessibilityDelegate extends AccessibilityDelegate {
+        @Override
+        public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+                @NonNull AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(host, info);
+            info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+                    AccessibilityNodeInfo.ACTION_CLICK, getResources().getString(
+                    R.string.bubble_accessibility_action_expand_menu)));
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
+            info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
+            if (mPositioner.isBubbleBarOnLeft()) {
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+                        R.id.action_move_bubble_bar_right, getResources().getString(
+                        R.string.bubble_accessibility_action_move_bar_right)));
+            } else {
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
+                        R.id.action_move_bubble_bar_left, getResources().getString(
+                        R.string.bubble_accessibility_action_move_bar_left)));
+            }
+        }
+
+        @Override
+        public boolean performAccessibilityAction(@NonNull View host, int action,
+                @Nullable Bundle args) {
+            if (super.performAccessibilityAction(host, action, args)) {
+                return true;
+            }
+            if (action == AccessibilityNodeInfo.ACTION_COLLAPSE) {
+                mManager.collapseStack();
+                return true;
+            }
+            if (action == AccessibilityNodeInfo.ACTION_DISMISS) {
+                mManager.dismissBubble(mBubble, Bubbles.DISMISS_USER_GESTURE);
+                return true;
+            }
+            if (action == R.id.action_move_bubble_bar_left) {
+                mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT);
+                return true;
+            }
+            if (action == R.id.action_move_bubble_bar_right) {
+                mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT);
+                return true;
+            }
+            return false;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index d45ed0d..07463bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -20,9 +20,9 @@
 import android.view.MotionEvent
 import android.view.View
 import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.DismissView
-import com.android.wm.shell.common.bubbles.RelativeTouchListener
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject
+import com.android.wm.shell.shared.bubbles.DismissView
+import com.android.wm.shell.shared.bubbles.RelativeTouchListener
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
 
 /** Controller for handling drag interactions with [BubbleBarExpandedView] */
 @SuppressLint("ClickableViewAccessibility")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 9fa85cf..1c9c195 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -16,8 +16,8 @@
 
 package com.android.wm.shell.bubbles.bar;
 
-import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
-import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
 import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE;
 
 import android.annotation.Nullable;
@@ -44,9 +44,9 @@
 import com.android.wm.shell.bubbles.DeviceConfig;
 import com.android.wm.shell.bubbles.DismissViewUtils;
 import com.android.wm.shell.bubbles.bar.BubbleBarExpandedViewDragController.DragListener;
-import com.android.wm.shell.common.bubbles.BaseBubblePinController;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.DismissView;
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.DismissView;
 
 import kotlin.Unit;
 
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 0d72998..5148107 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
@@ -29,6 +29,7 @@
 import androidx.dynamicanimation.animation.DynamicAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.Bubble;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
@@ -219,6 +220,21 @@
                 }
         ));
 
+        if (Flags.enableBubbleAnything() || Flags.enableBubbleToFullscreen()) {
+            menuActions.add(new BubbleBarMenuView.MenuAction(
+                    Icon.createWithResource(resources,
+                            R.drawable.desktop_mode_ic_handle_menu_fullscreen),
+                    resources.getString(R.string.bubble_fullscreen_text),
+                    tintColor,
+                    view -> {
+                        hideMenu(true /* animated */);
+                        if (mListener != null) {
+                            mListener.onMoveToFullscreen(bubble);
+                        }
+                    }
+            ));
+        }
+
         return menuActions;
     }
 
@@ -249,5 +265,10 @@
          * Dismiss bubble and remove it from the bubble stack
          */
         void onDismissBubble(Bubble bubble);
+
+        /**
+         * Move the bubble to fullscreen.
+         */
+        void onMoveToFullscreen(Bubble bubble);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index e108f7b..9fd255d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -34,9 +34,9 @@
 import com.android.wm.shell.bubbles.BubbleEducationController
 import com.android.wm.shell.bubbles.BubbleViewProvider
 import com.android.wm.shell.bubbles.setup
-import com.android.wm.shell.common.bubbles.BubblePopupDrawable
-import com.android.wm.shell.common.bubbles.BubblePopupView
 import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
+import com.android.wm.shell.shared.bubbles.BubblePopupView
 import kotlin.math.roundToInt
 
 /** Manages bubble education presentation and animation */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
index 651bf02..23ba2bf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinController.kt
@@ -25,8 +25,8 @@
 import androidx.core.view.updateLayoutParams
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner
-import com.android.wm.shell.common.bubbles.BaseBubblePinController
-import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.bubbles.BaseBubblePinController
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
 
 /**
  * Controller to manage pinning bubble bar to left or right when dragging starts from the bubble bar
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index dcbc72a..f532be6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -23,6 +23,7 @@
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
 import android.util.ArraySet;
+import android.util.Size;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -193,8 +194,8 @@
 
 
     /** Called when a display rotate requested. */
-    public void onDisplayRotateRequested(WindowContainerTransaction wct, int displayId,
-            int fromRotation, int toRotation) {
+    public void onDisplayChangeRequested(WindowContainerTransaction wct, int displayId,
+            Rect startAbsBounds, Rect endAbsBounds, int fromRotation, int toRotation) {
         synchronized (mDisplays) {
             final DisplayRecord dr = mDisplays.get(displayId);
             if (dr == null) {
@@ -203,7 +204,16 @@
             }
 
             if (dr.mDisplayLayout != null) {
-                dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation);
+                if (endAbsBounds != null) {
+                    // If there is a change in the display dimensions update the layout as well;
+                    // note that endAbsBounds should ignore any potential rotation changes, so
+                    // we still need to rotate the layout after if needed.
+                    dr.mDisplayLayout.resizeTo(dr.mContext.getResources(),
+                            new Size(endAbsBounds.width(), endAbsBounds.height()));
+                }
+                if (fromRotation != toRotation) {
+                    dr.mDisplayLayout.rotateTo(dr.mContext.getResources(), toRotation);
+                }
             }
 
             mChangeController.dispatchOnDisplayChange(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 3fa51a9..f03daad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -52,6 +52,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 
 import java.util.ArrayList;
@@ -290,17 +291,11 @@
             if (hadImeSourceControl != hasImeSourceControl) {
                 dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl);
             }
+            final boolean hasImeLeash = hasImeSourceControl && imeSourceControl.getLeash() != null;
 
             boolean pendingImeStartAnimation = false;
-            boolean canAnimate;
-            if (android.view.inputmethod.Flags.refactorInsetsController()) {
-                canAnimate = hasImeSourceControl && imeSourceControl.getLeash() != null;
-            } else {
-                canAnimate = hasImeSourceControl;
-            }
-
             boolean positionChanged = false;
-            if (canAnimate) {
+            if (hasImeLeash) {
                 if (mAnimation != null) {
                     final Point lastSurfacePosition = hadImeSourceControl
                             ? mImeSourceControl.getSurfacePosition() : null;
@@ -324,6 +319,13 @@
                 // continue the bar to slide to the end (even without visible IME)
                 mAnimation.cancel();
             }
+
+            // Make mImeSourceControl point to the new control before starting the animation.
+            if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
+                mImeSourceControl.release(SurfaceControl::release);
+            }
+            mImeSourceControl = imeSourceControl;
+
             if (positionChanged) {
                 if (android.view.inputmethod.Flags.refactorInsetsController()) {
                     // For showing the IME, the leash has to be available first. Hiding
@@ -337,11 +339,6 @@
                 }
             }
 
-            if (hadImeSourceControl && mImeSourceControl != imeSourceControl) {
-                mImeSourceControl.release(SurfaceControl::release);
-            }
-            mImeSourceControl = imeSourceControl;
-
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
                 if (pendingImeStartAnimation) {
                     startAnimation(true, true /* forceRestart */);
@@ -464,11 +461,12 @@
 
         private void startAnimation(final boolean show, final boolean forceRestart,
                 @NonNull final ImeTracker.Token statsToken) {
+            if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
+                if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
+                return;
+            }
             if (android.view.inputmethod.Flags.refactorInsetsController()) {
-                if (mImeSourceControl == null || mImeSourceControl.getLeash() == null) {
-                    if (DEBUG) Slog.d(TAG, "No leash available, not starting the animation.");
-                    return;
-                } else if (!mImeRequestedVisible && show) {
+                if (!mImeRequestedVisible && show) {
                     // we have a control with leash, but the IME was not requested visible before,
                     // therefore aborting the show animation.
                     Slog.e(TAG, "IME was not requested visible, not starting the show animation.");
@@ -477,7 +475,7 @@
                 }
             }
             final InsetsSource imeSource = mInsetsState.peekSource(InsetsSource.ID_IME);
-            if (imeSource == null || mImeSourceControl == null) {
+            if (imeSource == null) {
                 ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_WM_ANIMATION_CREATE);
                 return;
             }
@@ -514,8 +512,10 @@
                 }
                 mAnimation.cancel();
             }
-            final float defaultY = mImeSourceControl.getSurfacePosition().y;
-            final float x = mImeSourceControl.getSurfacePosition().x;
+            final InsetsSourceControl animatingControl = new InsetsSourceControl(mImeSourceControl);
+            final SurfaceControl animatingLeash = animatingControl.getLeash();
+            final float defaultY = animatingControl.getSurfacePosition().y;
+            final float x = animatingControl.getSurfacePosition().x;
             final float hiddenY = defaultY + mImeFrame.height();
             final float shownY = defaultY;
             final float startY = show ? hiddenY : shownY;
@@ -537,13 +537,10 @@
             mAnimation.addUpdateListener(animation -> {
                 SurfaceControl.Transaction t = mTransactionPool.acquire();
                 float value = (float) animation.getAnimatedValue();
-                if (!android.view.inputmethod.Flags.refactorInsetsController() || (
-                        mImeSourceControl != null && mImeSourceControl.getLeash() != null)) {
-                    t.setPosition(mImeSourceControl.getLeash(), x, value);
-                    final float alpha = (mAnimateAlpha || isFloating)
-                            ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
-                    t.setAlpha(mImeSourceControl.getLeash(), alpha);
-                }
+                t.setPosition(animatingLeash, x, value);
+                final float alpha = (mAnimateAlpha || isFloating)
+                        ? (value - hiddenY) / (shownY - hiddenY) : 1f;
+                t.setAlpha(animatingLeash, alpha);
                 dispatchPositionChanged(mDisplayId, imeTop(value), t);
                 t.apply();
                 mTransactionPool.release(t);
@@ -560,7 +557,7 @@
                     ValueAnimator valueAnimator = (ValueAnimator) animation;
                     float value = (float) valueAnimator.getAnimatedValue();
                     SurfaceControl.Transaction t = mTransactionPool.acquire();
-                    t.setPosition(mImeSourceControl.getLeash(), x, value);
+                    t.setPosition(animatingLeash, x, value);
                     if (DEBUG) {
                         Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
                                 + imeTop(hiddenY) + "->" + imeTop(shownY)
@@ -572,19 +569,19 @@
                     final float alpha = (mAnimateAlpha || isFloating)
                             ? (value - hiddenY) / (shownY - hiddenY)
                             : 1.f;
-                    t.setAlpha(mImeSourceControl.getLeash(), alpha);
+                    t.setAlpha(animatingLeash, alpha);
                     if (mAnimationDirection == DIRECTION_SHOW) {
                         ImeTracker.forLogging().onProgress(mStatsToken,
                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
-                        t.show(mImeSourceControl.getLeash());
+                        t.show(animatingLeash);
                     }
                     if (DEBUG_IME_VISIBILITY) {
                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START,
                                 mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
                                 mDisplayId, mAnimationDirection, alpha, value, endY,
-                                Objects.toString(mImeSourceControl.getLeash()),
-                                Objects.toString(mImeSourceControl.getInsetsHint()),
-                                Objects.toString(mImeSourceControl.getSurfacePosition()),
+                                Objects.toString(animatingLeash),
+                                Objects.toString(animatingControl.getInsetsHint()),
+                                Objects.toString(animatingControl.getSurfacePosition()),
                                 Objects.toString(mImeFrame));
                     }
                     t.apply();
@@ -598,31 +595,23 @@
                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_CANCEL,
                                 mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
                                 mDisplayId,
-                                Objects.toString(mImeSourceControl.getInsetsHint()));
+                                Objects.toString(animatingControl.getInsetsHint()));
                     }
                 }
 
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    boolean hasLeash =
-                            mImeSourceControl != null && mImeSourceControl.getLeash() != null;
                     if (DEBUG) Slog.d(TAG, "onAnimationEnd " + mCancelled);
                     SurfaceControl.Transaction t = mTransactionPool.acquire();
                     if (!mCancelled) {
-                        if (!android.view.inputmethod.Flags.refactorInsetsController()
-                                || hasLeash) {
-                            t.setPosition(mImeSourceControl.getLeash(), x, endY);
-                            t.setAlpha(mImeSourceControl.getLeash(), 1.f);
-                        }
+                        t.setPosition(animatingLeash, x, endY);
+                        t.setAlpha(animatingLeash, 1.f);
                     }
                     dispatchEndPositioning(mDisplayId, mCancelled, t);
                     if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) {
                         ImeTracker.forLogging().onProgress(mStatsToken,
                                 ImeTracker.PHASE_WM_ANIMATION_RUNNING);
-                        if (!android.view.inputmethod.Flags.refactorInsetsController()
-                                || hasLeash) {
-                            t.hide(mImeSourceControl.getLeash());
-                        }
+                        t.hide(animatingLeash);
                         removeImeSurface(mDisplayId);
                         ImeTracker.forLogging().onHidden(mStatsToken);
                     } else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
@@ -635,13 +624,9 @@
                         EventLog.writeEvent(IMF_IME_REMOTE_ANIM_END,
                                 mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE,
                                 mDisplayId, mAnimationDirection, endY,
-                                Objects.toString(
-                                        mImeSourceControl != null ? mImeSourceControl.getLeash()
-                                                : "null"),
-                                Objects.toString(mImeSourceControl != null
-                                        ? mImeSourceControl.getInsetsHint() : "null"),
-                                Objects.toString(mImeSourceControl != null
-                                        ? mImeSourceControl.getSurfacePosition() : "null"),
+                                Objects.toString(animatingLeash),
+                                Objects.toString(animatingControl.getInsetsHint()),
+                                Objects.toString(animatingControl.getSurfacePosition()),
                                 Objects.toString(mImeFrame));
                     }
                     t.apply();
@@ -649,6 +634,7 @@
 
                     mAnimationDirection = DIRECTION_NONE;
                     mAnimation = null;
+                    animatingControl.release(SurfaceControl::release);
                 }
             });
             if (!show) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 84e32a2..b6a1686 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -35,6 +35,7 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
+import android.util.Size;
 import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
@@ -244,6 +245,16 @@
         recalcInsets(res);
     }
 
+    /**
+     * Update the dimensions of this layout.
+     */
+    public void resizeTo(Resources res, Size displaySize) {
+        mWidth = displaySize.getWidth();
+        mHeight = displaySize.getHeight();
+
+        recalcInsets(res);
+    }
+
     /** Get this layout's non-decor insets. */
     public Rect nonDecorInsets() {
         return mNonDecorInsets;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index fad3dee..1929729 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -42,6 +42,7 @@
                     .setSourceCrop(crop)
                     .setCaptureSecureLayers(true)
                     .setAllowProtected(true)
+                    .setHintForSeamlessTransition(true)
                     .build()));
     }
 
@@ -78,6 +79,9 @@
             mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
             mTransaction.reparent(mScreenshot, mParentSurfaceControl);
             mTransaction.setLayer(mScreenshot, mLayer);
+            if (buffer.containsHdrLayers()) {
+                mTransaction.setDimmingEnabled(mScreenshot, false);
+            }
             mTransaction.show(mScreenshot);
             mTransaction.apply();
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index f792392..bcd40a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -29,6 +29,7 @@
 import android.window.WindowOrganizer;
 
 import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.transition.LegacyTransitions;
 
 import java.util.ArrayList;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
deleted file mode 100644
index 4c34566..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TransactionPool.java
+++ /dev/null
@@ -1,50 +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.wm.shell.common;
-
-import android.util.Pools;
-import android.view.SurfaceControl;
-
-/**
- * Provides a synchronized pool of {@link SurfaceControl.Transaction}s to minimize allocations.
- */
-public class TransactionPool {
-    private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
-            new Pools.SynchronizedPool<>(4);
-
-    public TransactionPool() {
-    }
-
-    /** Gets a transaction from the pool. */
-    public SurfaceControl.Transaction acquire() {
-        SurfaceControl.Transaction t = mTransactionPool.acquire();
-        if (t == null) {
-            return new SurfaceControl.Transaction();
-        }
-        return t;
-    }
-
-    /**
-     * Return a transaction to the pool. DO NOT call {@link SurfaceControl.Transaction#close()} if
-     * returning to pool.
-     */
-    public void release(SurfaceControl.Transaction t) {
-        if (!mTransactionPool.release(t)) {
-            t.close();
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java
deleted file mode 100644
index 7079190..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/TriangleShape.java
+++ /dev/null
@@ -1,77 +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.wm.shell.common;
-
-import android.graphics.Outline;
-import android.graphics.Path;
-import android.graphics.drawable.shapes.PathShape;
-
-import androidx.annotation.NonNull;
-
-/**
- * Wrapper around {@link PathShape}
- * that creates a shape with a triangular path (pointing up or down).
- *
- * This is the copy from SystemUI/recents.
- */
-public class TriangleShape extends PathShape {
-    private Path mTriangularPath;
-
-    public TriangleShape(Path path, float stdWidth, float stdHeight) {
-        super(path, stdWidth, stdHeight);
-        mTriangularPath = path;
-    }
-
-    public static TriangleShape create(float width, float height, boolean isPointingUp) {
-        Path triangularPath = new Path();
-        if (isPointingUp) {
-            triangularPath.moveTo(0, height);
-            triangularPath.lineTo(width, height);
-            triangularPath.lineTo(width / 2, 0);
-            triangularPath.close();
-        } else {
-            triangularPath.moveTo(0, 0);
-            triangularPath.lineTo(width / 2, height);
-            triangularPath.lineTo(width, 0);
-            triangularPath.close();
-        }
-        return new TriangleShape(triangularPath, width, height);
-    }
-
-    /** Create an arrow TriangleShape that points to the left or the right */
-    public static TriangleShape createHorizontal(
-            float width, float height, boolean isPointingLeft) {
-        Path triangularPath = new Path();
-        if (isPointingLeft) {
-            triangularPath.moveTo(0, height / 2);
-            triangularPath.lineTo(width, height);
-            triangularPath.lineTo(width, 0);
-            triangularPath.close();
-        } else {
-            triangularPath.moveTo(0, height);
-            triangularPath.lineTo(width, height / 2);
-            triangularPath.lineTo(0, 0);
-            triangularPath.close();
-        }
-        return new TriangleShape(triangularPath, width, height);
-    }
-
-    @Override
-    public void getOutline(@NonNull Outline outline) {
-        outline.setPath(mTriangularPath);
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
deleted file mode 100644
index eec2468..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ /dev/null
@@ -1,210 +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.wm.shell.common.bubbles
-
-import android.graphics.Point
-import android.graphics.RectF
-import android.view.View
-import androidx.annotation.VisibleForTesting
-import androidx.core.animation.Animator
-import androidx.core.animation.AnimatorListenerAdapter
-import androidx.core.animation.ObjectAnimator
-import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
-
-/**
- * Base class for common logic shared between different bubble views to support pinning bubble bar
- * to left or right edge of screen.
- *
- * Handles drag events and allows a [LocationChangeListener] to be registered that is notified when
- * location of the bubble bar should change.
- *
- * Shows a drop target when releasing a view would update the [BubbleBarLocation].
- */
-abstract class BaseBubblePinController(private val screenSizeProvider: () -> Point) {
-
-    private var initialLocationOnLeft = false
-    private var onLeft = false
-    private var dismissZone: RectF? = null
-    private var stuckToDismissTarget = false
-    private var screenCenterX = 0
-    private var listener: LocationChangeListener? = null
-    private var dropTargetAnimator: ObjectAnimator? = null
-
-    /**
-     * Signal the controller that dragging interaction has started.
-     *
-     * @param initialLocationOnLeft side of the screen where bubble bar is pinned to
-     */
-    fun onDragStart(initialLocationOnLeft: Boolean) {
-        this.initialLocationOnLeft = initialLocationOnLeft
-        onLeft = initialLocationOnLeft
-        screenCenterX = screenSizeProvider.invoke().x / 2
-        dismissZone = getExclusionRect()
-    }
-
-    /** View has moved to [x] and [y] screen coordinates */
-    fun onDragUpdate(x: Float, y: Float) {
-        if (dismissZone?.contains(x, y) == true) return
-
-        val wasOnLeft = onLeft
-        onLeft = x < screenCenterX
-        if (wasOnLeft != onLeft) {
-            onLocationChange(if (onLeft) LEFT else RIGHT)
-        } else if (stuckToDismissTarget) {
-            // Moved out of the dismiss view back to initial side, if we have a drop target, show it
-            getDropTargetView()?.apply { animateIn() }
-        }
-        // Make sure this gets cleared
-        stuckToDismissTarget = false
-    }
-
-    /** Signal the controller that view has been dragged to dismiss view. */
-    fun onStuckToDismissTarget() {
-        stuckToDismissTarget = true
-        // Notify that location may be reset
-        val shouldResetLocation = onLeft != initialLocationOnLeft
-        if (shouldResetLocation) {
-            onLeft = initialLocationOnLeft
-            listener?.onChange(if (onLeft) LEFT else RIGHT)
-        }
-        getDropTargetView()?.apply {
-            animateOut {
-                if (shouldResetLocation) {
-                    updateLocation(if (onLeft) LEFT else RIGHT)
-                }
-            }
-        }
-    }
-
-    /** Signal the controller that dragging interaction has finished. */
-    fun onDragEnd() {
-        getDropTargetView()?.let { view -> view.animateOut { removeDropTargetView(view) } }
-        dismissZone = null
-        listener?.onRelease(if (onLeft) LEFT else RIGHT)
-    }
-
-    /**
-     * [LocationChangeListener] that is notified when dragging interaction has resulted in bubble
-     * bar to be pinned on the other edge
-     */
-    fun setListener(listener: LocationChangeListener?) {
-        this.listener = listener
-    }
-
-    /** Get width for exclusion rect where dismiss takes over drag */
-    protected abstract fun getExclusionRectWidth(): Float
-    /** Get height for exclusion rect where dismiss takes over drag */
-    protected abstract fun getExclusionRectHeight(): Float
-
-    /** Create the drop target view and attach it to the parent */
-    protected abstract fun createDropTargetView(): View
-
-    /** Get the drop target view if it exists */
-    protected abstract fun getDropTargetView(): View?
-
-    /** Remove the drop target view */
-    protected abstract fun removeDropTargetView(view: View)
-
-    /** Update size and location of the drop target view */
-    protected abstract fun updateLocation(location: BubbleBarLocation)
-
-    private fun onLocationChange(location: BubbleBarLocation) {
-        showDropTarget(location)
-        listener?.onChange(location)
-    }
-
-    private fun getExclusionRect(): RectF {
-        val rect = RectF(0f, 0f, getExclusionRectWidth(), getExclusionRectHeight())
-        // Center it around the bottom center of the screen
-        val screenBottom = screenSizeProvider.invoke().y
-        rect.offsetTo(screenCenterX - rect.width() / 2, screenBottom - rect.height())
-        return rect
-    }
-
-    private fun showDropTarget(location: BubbleBarLocation) {
-        val targetView = getDropTargetView() ?: createDropTargetView().apply { alpha = 0f }
-        if (targetView.alpha > 0) {
-            targetView.animateOut {
-                updateLocation(location)
-                targetView.animateIn()
-            }
-        } else {
-            updateLocation(location)
-            targetView.animateIn()
-        }
-    }
-
-    private fun View.animateIn() {
-        dropTargetAnimator?.cancel()
-        dropTargetAnimator =
-            ObjectAnimator.ofFloat(this, View.ALPHA, 1f)
-                .setDuration(DROP_TARGET_ALPHA_IN_DURATION)
-                .addEndAction { dropTargetAnimator = null }
-        dropTargetAnimator?.start()
-    }
-
-    private fun View.animateOut(endAction: Runnable? = null) {
-        dropTargetAnimator?.cancel()
-        dropTargetAnimator =
-            ObjectAnimator.ofFloat(this, View.ALPHA, 0f)
-                .setDuration(DROP_TARGET_ALPHA_OUT_DURATION)
-                .addEndAction {
-                    endAction?.run()
-                    dropTargetAnimator = null
-                }
-        dropTargetAnimator?.start()
-    }
-
-    private fun <T : Animator> T.addEndAction(runnable: Runnable): T {
-        addListener(
-            object : AnimatorListenerAdapter() {
-                override fun onAnimationEnd(animation: Animator) {
-                    runnable.run()
-                }
-            }
-        )
-        return this
-    }
-
-    /** Receive updates on location changes */
-    interface LocationChangeListener {
-        /**
-         * Bubble bar has been dragged to a new [BubbleBarLocation]. And the drag is still in
-         * progress.
-         *
-         * Triggered when drag gesture passes the middle of the screen and before touch up. Can be
-         * triggered multiple times per gesture.
-         *
-         * @param location new location as a result of the ongoing drag operation
-         */
-        fun onChange(location: BubbleBarLocation) {}
-
-        /**
-         * Bubble bar has been released in the [BubbleBarLocation].
-         *
-         * @param location final location of the bubble bar once drag is released
-         */
-        fun onRelease(location: BubbleBarLocation)
-    }
-
-    companion object {
-        @VisibleForTesting const val DROP_TARGET_ALPHA_IN_DURATION = 150L
-        @VisibleForTesting const val DROP_TARGET_ALPHA_OUT_DURATION = 100L
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
deleted file mode 100644
index 3c5beeb..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.aidl
+++ /dev/null
@@ -1,19 +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.wm.shell.common.bubbles;
-
-parcelable BubbleBarLocation;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.kt
deleted file mode 100644
index f0bdfde..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarLocation.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.wm.shell.common.bubbles
-
-import android.os.Parcel
-import android.os.Parcelable
-
-/**
- * The location of the bubble bar.
- */
-enum class BubbleBarLocation : Parcelable {
-    /**
-     * Place bubble bar at the default location for the chosen system language.
-     * If an RTL language is used, it is on the left. Otherwise on the right.
-     */
-    DEFAULT,
-    /** Default bubble bar location is overridden. Place bubble bar on the left. */
-    LEFT,
-    /** Default bubble bar location is overridden. Place bubble bar on the right. */
-    RIGHT;
-
-    /**
-     * Returns whether bubble bar is pinned to the left edge or right edge.
-     */
-    fun isOnLeft(isRtl: Boolean): Boolean {
-        if (this == DEFAULT) {
-            return isRtl
-        }
-        return this == LEFT
-    }
-
-    override fun describeContents(): Int {
-        return 0
-    }
-
-    override fun writeToParcel(dest: Parcel, flags: Int) {
-        dest.writeString(name)
-    }
-
-    companion object {
-        @JvmField
-        val CREATOR = object : Parcelable.Creator<BubbleBarLocation> {
-            override fun createFromParcel(parcel: Parcel): BubbleBarLocation {
-                return parcel.readString()?.let { valueOf(it) } ?: DEFAULT
-            }
-
-            override fun newArray(size: Int) = arrayOfNulls<BubbleBarLocation>(size)
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
deleted file mode 100644
index ec3c601..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleBarUpdate.java
+++ /dev/null
@@ -1,186 +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.wm.shell.common.bubbles;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Point;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents an update to bubbles state. This is passed through
- * {@link com.android.wm.shell.bubbles.IBubblesListener} to launcher so that taskbar may render
- * bubbles. This should be kept this as minimal as possible in terms of data.
- */
-public class BubbleBarUpdate implements Parcelable {
-
-    public static final String BUNDLE_KEY = "update";
-
-    public final boolean initialState;
-    public boolean expandedChanged;
-    public boolean expanded;
-    public boolean shouldShowEducation;
-    @Nullable
-    public String selectedBubbleKey;
-    @Nullable
-    public BubbleInfo addedBubble;
-    @Nullable
-    public BubbleInfo updatedBubble;
-    @Nullable
-    public String suppressedBubbleKey;
-    @Nullable
-    public String unsupressedBubbleKey;
-    @Nullable
-    public BubbleBarLocation bubbleBarLocation;
-    @Nullable
-    public Point expandedViewDropTargetSize;
-    public boolean showOverflowChanged;
-    public boolean showOverflow;
-
-    // This is only populated if bubbles have been removed.
-    public List<RemovedBubble> removedBubbles = new ArrayList<>();
-
-    // This is only populated if the order of the bubbles has changed.
-    public List<String> bubbleKeysInOrder = new ArrayList<>();
-
-    // This is only populated the first time a listener is connected so it gets the current state.
-    public List<BubbleInfo> currentBubbleList = new ArrayList<>();
-
-
-    public BubbleBarUpdate() {
-        this(false);
-    }
-
-    private BubbleBarUpdate(boolean initialState) {
-        this.initialState = initialState;
-    }
-
-    public BubbleBarUpdate(Parcel parcel) {
-        initialState = parcel.readBoolean();
-        expandedChanged = parcel.readBoolean();
-        expanded = parcel.readBoolean();
-        shouldShowEducation = parcel.readBoolean();
-        selectedBubbleKey = parcel.readString();
-        addedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
-                BubbleInfo.class);
-        updatedBubble = parcel.readParcelable(BubbleInfo.class.getClassLoader(),
-                BubbleInfo.class);
-        suppressedBubbleKey = parcel.readString();
-        unsupressedBubbleKey = parcel.readString();
-        removedBubbles = parcel.readParcelableList(new ArrayList<>(),
-                RemovedBubble.class.getClassLoader(), RemovedBubble.class);
-        parcel.readStringList(bubbleKeysInOrder);
-        currentBubbleList = parcel.readParcelableList(new ArrayList<>(),
-                BubbleInfo.class.getClassLoader(), BubbleInfo.class);
-        bubbleBarLocation = parcel.readParcelable(BubbleBarLocation.class.getClassLoader(),
-                BubbleBarLocation.class);
-        expandedViewDropTargetSize = parcel.readParcelable(Point.class.getClassLoader(),
-                Point.class);
-        showOverflowChanged = parcel.readBoolean();
-        showOverflow = parcel.readBoolean();
-    }
-
-    /**
-     * Returns whether anything has changed in this update.
-     */
-    public boolean anythingChanged() {
-        return expandedChanged
-                || selectedBubbleKey != null
-                || addedBubble != null
-                || updatedBubble != null
-                || !removedBubbles.isEmpty()
-                || !bubbleKeysInOrder.isEmpty()
-                || suppressedBubbleKey != null
-                || unsupressedBubbleKey != null
-                || !currentBubbleList.isEmpty()
-                || bubbleBarLocation != null
-                || showOverflowChanged;
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        return "BubbleBarUpdate{"
-                + " initialState=" + initialState
-                + " expandedChanged=" + expandedChanged
-                + " expanded=" + expanded
-                + " selectedBubbleKey=" + selectedBubbleKey
-                + " shouldShowEducation=" + shouldShowEducation
-                + " addedBubble=" + addedBubble
-                + " updatedBubble=" + updatedBubble
-                + " suppressedBubbleKey=" + suppressedBubbleKey
-                + " unsuppressedBubbleKey=" + unsupressedBubbleKey
-                + " removedBubbles=" + removedBubbles
-                + " bubbles=" + bubbleKeysInOrder
-                + " currentBubbleList=" + currentBubbleList
-                + " bubbleBarLocation=" + bubbleBarLocation
-                + " expandedViewDropTargetSize=" + expandedViewDropTargetSize
-                + " showOverflowChanged=" + showOverflowChanged
-                + " showOverflow=" + showOverflow
-                + " }";
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeBoolean(initialState);
-        parcel.writeBoolean(expandedChanged);
-        parcel.writeBoolean(expanded);
-        parcel.writeBoolean(shouldShowEducation);
-        parcel.writeString(selectedBubbleKey);
-        parcel.writeParcelable(addedBubble, flags);
-        parcel.writeParcelable(updatedBubble, flags);
-        parcel.writeString(suppressedBubbleKey);
-        parcel.writeString(unsupressedBubbleKey);
-        parcel.writeParcelableList(removedBubbles, flags);
-        parcel.writeStringList(bubbleKeysInOrder);
-        parcel.writeParcelableList(currentBubbleList, flags);
-        parcel.writeParcelable(bubbleBarLocation, flags);
-        parcel.writeParcelable(expandedViewDropTargetSize, flags);
-        parcel.writeBoolean(showOverflowChanged);
-        parcel.writeBoolean(showOverflow);
-    }
-
-    /**
-     * Create update for initial set of values.
-     * <p>
-     * Used when bubble bar is newly created.
-     */
-    public static BubbleBarUpdate createInitialState() {
-        return new BubbleBarUpdate(true);
-    }
-
-    @NonNull
-    public static final Creator<BubbleBarUpdate> CREATOR =
-            new Creator<>() {
-                public BubbleBarUpdate createFromParcel(Parcel source) {
-                    return new BubbleBarUpdate(source);
-                }
-
-                public BubbleBarUpdate[] newArray(int size) {
-                    return new BubbleBarUpdate[size];
-                }
-            };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
deleted file mode 100644
index 0329b8d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleConstants.java
+++ /dev/null
@@ -1,26 +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.wm.shell.common.bubbles;
-
-/**
- * Constants shared between bubbles in shell & things we have to do for bubbles in launcher.
- */
-public class BubbleConstants {
-
-    /** The alpha for the scrim shown when bubbles are expanded. */
-    public static float BUBBLE_EXPANDED_SCRIM_ALPHA = .32f;
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
deleted file mode 100644
index 829af08..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java
+++ /dev/null
@@ -1,188 +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.wm.shell.common.bubbles;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.graphics.drawable.Icon;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Contains information necessary to present a bubble.
- */
-public class BubbleInfo implements Parcelable {
-
-    private String mKey; // Same key as the Notification
-    private int mFlags;  // Flags from BubbleMetadata
-    @Nullable
-    private String mShortcutId;
-    private int mUserId;
-    private String mPackageName;
-    /**
-     * All notification bubbles require a shortcut to be set on the notification, however, the
-     * app could still specify an Icon and PendingIntent to use for the bubble. In that case
-     * this icon will be populated. If the bubble is entirely shortcut based, this will be null.
-     */
-    @Nullable
-    private Icon mIcon;
-    @Nullable
-    private String mTitle;
-    @Nullable
-    private String mAppName;
-    private boolean mIsImportantConversation;
-
-    public BubbleInfo(String key, int flags, @Nullable String shortcutId, @Nullable Icon icon,
-            int userId, String packageName, @Nullable String title, @Nullable String appName,
-            boolean isImportantConversation) {
-        mKey = key;
-        mFlags = flags;
-        mShortcutId = shortcutId;
-        mIcon = icon;
-        mUserId = userId;
-        mPackageName = packageName;
-        mTitle = title;
-        mAppName = appName;
-        mIsImportantConversation = isImportantConversation;
-    }
-
-    private BubbleInfo(Parcel source) {
-        mKey = source.readString();
-        mFlags = source.readInt();
-        mShortcutId = source.readString();
-        mIcon = source.readTypedObject(Icon.CREATOR);
-        mUserId = source.readInt();
-        mPackageName = source.readString();
-        mTitle = source.readString();
-        mAppName = source.readString();
-        mIsImportantConversation = source.readBoolean();
-    }
-
-    public String getKey() {
-        return mKey;
-    }
-
-    @Nullable
-    public String getShortcutId() {
-        return mShortcutId;
-    }
-
-    @Nullable
-    public Icon getIcon() {
-        return mIcon;
-    }
-
-    public int getFlags() {
-        return mFlags;
-    }
-
-    public int getUserId() {
-        return mUserId;
-    }
-
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    @Nullable
-    public String getTitle() {
-        return mTitle;
-    }
-
-    @Nullable
-    public String getAppName() {
-        return mAppName;
-    }
-
-    public boolean isImportantConversation() {
-        return mIsImportantConversation;
-    }
-
-    /**
-     * Whether this bubble is currently being hidden from the stack.
-     */
-    public boolean isBubbleSuppressed() {
-        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_BUBBLE) != 0;
-    }
-
-    /**
-     * Whether this bubble is able to be suppressed (i.e. has the developer opted into the API
-     * to
-     * hide the bubble when in the same content).
-     */
-    public boolean isBubbleSuppressable() {
-        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESSABLE_BUBBLE) != 0;
-    }
-
-    /**
-     * Whether the notification for this bubble is hidden from the shade.
-     */
-    public boolean isNotificationSuppressed() {
-        return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION) != 0;
-    }
-
-    /** Sets the flags for this bubble. */
-    public void setFlags(int flags) {
-        mFlags = flags;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (!(o instanceof BubbleInfo)) return false;
-        BubbleInfo bubble = (BubbleInfo) o;
-        return Objects.equals(mKey, bubble.mKey);
-    }
-
-    @Override
-    public int hashCode() {
-        return mKey.hashCode();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeString(mKey);
-        parcel.writeInt(mFlags);
-        parcel.writeString(mShortcutId);
-        parcel.writeTypedObject(mIcon, flags);
-        parcel.writeInt(mUserId);
-        parcel.writeString(mPackageName);
-        parcel.writeString(mTitle);
-        parcel.writeString(mAppName);
-        parcel.writeBoolean(mIsImportantConversation);
-    }
-
-    @NonNull
-    public static final Creator<BubbleInfo> CREATOR =
-            new Creator<>() {
-                public BubbleInfo createFromParcel(Parcel source) {
-                    return new BubbleInfo(source);
-                }
-
-                public BubbleInfo[] newArray(int size) {
-                    return new BubbleInfo[size];
-                }
-            };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
deleted file mode 100644
index 887af17..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupDrawable.kt
+++ /dev/null
@@ -1,232 +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.wm.shell.common.bubbles
-
-import android.annotation.ColorInt
-import android.graphics.Canvas
-import android.graphics.ColorFilter
-import android.graphics.Matrix
-import android.graphics.Outline
-import android.graphics.Paint
-import android.graphics.Path
-import android.graphics.Rect
-import android.graphics.RectF
-import android.graphics.drawable.Drawable
-import kotlin.math.atan
-import kotlin.math.cos
-import kotlin.math.sin
-import kotlin.properties.Delegates
-
-/** A drawable for the [BubblePopupView] that draws a popup background with a directional arrow */
-class BubblePopupDrawable(val config: Config) : Drawable() {
-    /** The direction of the arrow in the popup drawable */
-    enum class ArrowDirection {
-        UP,
-        DOWN
-    }
-
-    /** The arrow position on the side of the popup bubble */
-    sealed class ArrowPosition {
-        object Start : ArrowPosition()
-        object Center : ArrowPosition()
-        object End : ArrowPosition()
-        class Custom(val value: Float) : ArrowPosition()
-    }
-
-    /** The configuration for drawable features */
-    data class Config(
-        @ColorInt val color: Int,
-        val cornerRadius: Float,
-        val contentPadding: Int,
-        val arrowWidth: Float,
-        val arrowHeight: Float,
-        val arrowRadius: Float
-    )
-
-    /**
-     * The direction of the arrow in the popup drawable. It affects the content padding and requires
-     * it to be updated in the view.
-     */
-    var arrowDirection: ArrowDirection by
-        Delegates.observable(ArrowDirection.UP) { _, _, _ -> requestPathUpdate() }
-
-    /**
-     * Arrow position along the X axis and its direction. The position is adjusted to the content
-     * corner radius when applied so it doesn't go into rounded corner area
-     */
-    var arrowPosition: ArrowPosition by
-        Delegates.observable(ArrowPosition.Center) { _, _, _ -> requestPathUpdate() }
-
-    private val path = Path()
-    private val paint = Paint()
-    private var shouldUpdatePath = true
-
-    init {
-        paint.color = config.color
-        paint.style = Paint.Style.FILL
-        paint.isAntiAlias = true
-    }
-
-    override fun draw(canvas: Canvas) {
-        updatePathIfNeeded()
-        canvas.drawPath(path, paint)
-    }
-
-    override fun onBoundsChange(bounds: Rect) {
-        requestPathUpdate()
-    }
-
-    /** Should be applied to the view padding if arrow direction changes */
-    override fun getPadding(padding: Rect): Boolean {
-        padding.set(
-            config.contentPadding,
-            config.contentPadding,
-            config.contentPadding,
-            config.contentPadding
-        )
-        when (arrowDirection) {
-            ArrowDirection.UP -> padding.top += config.arrowHeight.toInt()
-            ArrowDirection.DOWN -> padding.bottom += config.arrowHeight.toInt()
-        }
-        return true
-    }
-
-    override fun getOutline(outline: Outline) {
-        updatePathIfNeeded()
-        outline.setPath(path)
-    }
-
-    override fun getOpacity(): Int {
-        return paint.alpha
-    }
-
-    override fun setAlpha(alpha: Int) {
-        paint.alpha = alpha
-    }
-
-    override fun setColorFilter(colorFilter: ColorFilter?) {
-        paint.colorFilter = colorFilter
-    }
-
-    /** Schedules path update for the next redraw */
-    private fun requestPathUpdate() {
-        shouldUpdatePath = true
-    }
-
-    /** Updates the path if required, when bounds or arrow direction/position changes */
-    private fun updatePathIfNeeded() {
-        if (shouldUpdatePath) {
-            updatePath()
-            shouldUpdatePath = false
-        }
-    }
-
-    /** Updates the path value using the current bounds, config, arrow direction and position */
-    private fun updatePath() {
-        if (bounds.isEmpty) return
-        // Reset the path state
-        path.reset()
-        // The content rect where the filled rounded rect will be drawn
-        val contentRect = RectF(bounds)
-        when (arrowDirection) {
-            ArrowDirection.UP -> {
-                // Add rounded arrow pointing up to the path
-                addRoundedArrowPositioned(path, arrowPosition)
-                // Inset content rect by the arrow size from the top
-                contentRect.top += config.arrowHeight
-            }
-            ArrowDirection.DOWN -> {
-                val matrix = Matrix()
-                // Flip the path with the matrix to draw arrow pointing down
-                matrix.setScale(1f, -1f, bounds.width() / 2f, bounds.height() / 2f)
-                path.transform(matrix)
-                // Add rounded arrow with the flipped matrix applied, will point down
-                addRoundedArrowPositioned(path, arrowPosition)
-                // Restore the path matrix to the original state with inverted matrix
-                matrix.invert(matrix)
-                path.transform(matrix)
-                // Inset content rect by the arrow size from the bottom
-                contentRect.bottom -= config.arrowHeight
-            }
-        }
-        // Add the content area rounded rect
-        path.addRoundRect(contentRect, config.cornerRadius, config.cornerRadius, Path.Direction.CW)
-    }
-
-    /** Add a rounded arrow pointing up in the horizontal position on the canvas */
-    private fun addRoundedArrowPositioned(path: Path, position: ArrowPosition) {
-        val matrix = Matrix()
-        var translationX = positionValue(position) - config.arrowWidth / 2
-        // Offset to position between rounded corners of the content view
-        translationX = translationX.coerceIn(config.cornerRadius,
-                bounds.width() - config.cornerRadius - config.arrowWidth)
-        // Translate to add the arrow in the center horizontally
-        matrix.setTranslate(-translationX, 0f)
-        path.transform(matrix)
-        // Add rounded arrow
-        addRoundedArrow(path)
-        // Restore the path matrix to the original state with inverted matrix
-        matrix.invert(matrix)
-        path.transform(matrix)
-    }
-
-    /** Adds a rounded arrow pointing up to the path, can be flipped if needed */
-    private fun addRoundedArrow(path: Path) {
-        // Theta is half of the angle inside the triangle tip
-        val thetaTan = config.arrowWidth / (config.arrowHeight * 2f)
-        val theta = atan(thetaTan)
-        val thetaDeg = Math.toDegrees(theta.toDouble()).toFloat()
-        // The center Y value of the circle for the triangle tip
-        val tipCircleCenterY = config.arrowRadius / sin(theta)
-        // The length from triangle tip to intersection point with the circle
-        val tipIntersectionSideLength = config.arrowRadius / thetaTan
-        // The offset from the top to the point of intersection
-        val intersectionTopOffset = tipIntersectionSideLength * cos(theta)
-        // The offset from the center to the point of intersection
-        val intersectionCenterOffset = tipIntersectionSideLength * sin(theta)
-        // The center X of the triangle
-        val arrowCenterX = config.arrowWidth / 2f
-
-        // Set initial position in bottom left of the arrow
-        path.moveTo(0f, config.arrowHeight)
-        // Add the left side of the triangle
-        path.lineTo(arrowCenterX - intersectionCenterOffset, intersectionTopOffset)
-        // Add the arc from the left to the right side of the triangle
-        path.arcTo(
-            /* left = */ arrowCenterX - config.arrowRadius,
-            /* top = */ tipCircleCenterY - config.arrowRadius,
-            /* right = */ arrowCenterX + config.arrowRadius,
-            /* bottom = */ tipCircleCenterY + config.arrowRadius,
-            /* startAngle = */ 180 + thetaDeg,
-            /* sweepAngle = */ 180 - (2 * thetaDeg),
-            /* forceMoveTo = */ false
-        )
-        // Add the right side of the triangle
-        path.lineTo(config.arrowWidth, config.arrowHeight)
-        // Close the path
-        path.close()
-    }
-
-    /** The value of the arrow position provided the position and current bounds */
-    private fun positionValue(position: ArrowPosition): Float {
-        return when (position) {
-            is ArrowPosition.Start -> 0f
-            is ArrowPosition.Center -> bounds.width().toFloat() / 2f
-            is ArrowPosition.End -> bounds.width().toFloat()
-            is ArrowPosition.Custom -> position.value
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
deleted file mode 100644
index 444fbf7..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubblePopupView.kt
+++ /dev/null
@@ -1,66 +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.wm.shell.common.bubbles
-
-import android.content.Context
-import android.graphics.Rect
-import android.util.AttributeSet
-import android.widget.LinearLayout
-
-/** A popup container view that uses [BubblePopupDrawable] as a background */
-open class BubblePopupView
-@JvmOverloads
-constructor(
-    context: Context,
-    attrs: AttributeSet? = null,
-    defStyleAttr: Int = 0,
-    defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
-    var popupDrawable: BubblePopupDrawable? = null
-        private set
-
-    /**
-     * Sets up the popup drawable with the config provided. Required to remove dependency on local
-     * resources
-     */
-    fun setupBackground(config: BubblePopupDrawable.Config) {
-        popupDrawable = BubblePopupDrawable(config)
-        background = popupDrawable
-        forceLayout()
-    }
-
-    /**
-     * Sets the arrow direction for the background drawable and updates the padding to fit the
-     * content inside of the popup drawable
-     */
-    fun setArrowDirection(direction: BubblePopupDrawable.ArrowDirection) {
-        popupDrawable?.let {
-            it.arrowDirection = direction
-            val padding = Rect()
-            if (it.getPadding(padding)) {
-                setPadding(padding.left, padding.top, padding.right, padding.bottom)
-            }
-        }
-    }
-
-    /** Sets the arrow position for the background drawable and triggers redraw */
-    fun setArrowPosition(position: BubblePopupDrawable.ArrowPosition) {
-        popupDrawable?.let {
-            it.arrowPosition = position
-            invalidate()
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
deleted file mode 100644
index 7c5bb21..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
+++ /dev/null
@@ -1,76 +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.wm.shell.common.bubbles;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.view.Gravity;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import androidx.annotation.DimenRes;
-import androidx.annotation.DrawableRes;
-import androidx.core.content.ContextCompat;
-
-/**
- * Circular view with a semitransparent, circular background with an 'X' inside it.
- *
- * This is used by both Bubbles and PIP as the dismiss target.
- */
-public class DismissCircleView extends FrameLayout {
-    @DrawableRes int mBackgroundResId;
-    @DimenRes int mIconSizeResId;
-
-    private final ImageView mIconView = new ImageView(getContext());
-
-    public DismissCircleView(Context context) {
-        super(context);
-        addView(mIconView);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        setBackground(ContextCompat.getDrawable(getContext(), mBackgroundResId));
-        setViewSizes();
-    }
-
-    /**
-     * Sets up view with the provided resource ids.
-     * Decouples resource dependency in order to be used externally (e.g. Launcher)
-     *
-     * @param backgroundResId drawable resource id of the circle background
-     * @param iconResId drawable resource id of the icon for the dismiss view
-     * @param iconSizeResId dimen resource id of the icon size
-     */
-    public void setup(@DrawableRes int backgroundResId, @DrawableRes int iconResId,
-            @DimenRes int iconSizeResId) {
-        mBackgroundResId = backgroundResId;
-        mIconSizeResId = iconSizeResId;
-
-        setBackground(ContextCompat.getDrawable(getContext(), backgroundResId));
-        mIconView.setImageDrawable(ContextCompat.getDrawable(getContext(), iconResId));
-        setViewSizes();
-    }
-
-    /** Retrieves the current dimensions for the icon and circle and applies them. */
-    private void setViewSizes() {
-        final int iconSize = getResources().getDimensionPixelSize(mIconSizeResId);
-        mIconView.setLayoutParams(
-                new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
deleted file mode 100644
index e06de9e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
+++ /dev/null
@@ -1,229 +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.wm.shell.common.bubbles
-
-import android.animation.ObjectAnimator
-import android.content.Context
-import android.graphics.Color
-import android.graphics.drawable.GradientDrawable
-import android.util.IntProperty
-import android.util.Log
-import android.view.Gravity
-import android.view.View
-import android.view.ViewGroup
-import android.view.WindowInsets
-import android.view.WindowManager
-import android.widget.FrameLayout
-import androidx.annotation.ColorRes
-import androidx.annotation.DimenRes
-import androidx.annotation.DrawableRes
-import androidx.core.content.ContextCompat
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
-import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
-import com.android.wm.shell.shared.animation.PhysicsAnimator
-
-/**
- * View that handles interactions between DismissCircleView and BubbleStackView.
- *
- * @note [setup] method should be called after initialisation
- */
-class DismissView(context: Context) : FrameLayout(context) {
-    /**
-     * The configuration is used to provide module specific resource ids
-     *
-     * @see [setup] method
-     */
-    data class Config(
-            /** The resource id to set on the dismiss target circle view */
-            val dismissViewResId: Int,
-            /** dimen resource id of the dismiss target circle view size */
-            @DimenRes val targetSizeResId: Int,
-            /** dimen resource id of the icon size in the dismiss target */
-            @DimenRes val iconSizeResId: Int,
-            /** dimen resource id of the bottom margin for the dismiss target */
-            @DimenRes var bottomMarginResId: Int,
-            /** dimen resource id of the height for dismiss area gradient */
-            @DimenRes val floatingGradientHeightResId: Int,
-            /** color resource id of the dismiss area gradient color */
-            @ColorRes val floatingGradientColorResId: Int,
-            /** drawable resource id of the dismiss target background */
-            @DrawableRes val backgroundResId: Int,
-            /** drawable resource id of the icon for the dismiss target */
-            @DrawableRes val iconResId: Int
-    )
-
-    companion object {
-        private const val SHOULD_SETUP =
-                "The view isn't ready. Should be called after `setup`"
-        private val TAG = DismissView::class.simpleName
-    }
-
-    var circle = DismissCircleView(context)
-    var isShowing = false
-    var config: Config? = null
-
-    private val animator = PhysicsAnimator.getInstance(circle)
-    private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
-    private val DISMISS_SCRIM_FADE_MS = 200L
-    private var wm: WindowManager =
-            context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
-    private var gradientDrawable: GradientDrawable? = null
-
-    private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
-            object : IntProperty<GradientDrawable>("alpha") {
-        override fun setValue(d: GradientDrawable, percent: Int) {
-            d.alpha = percent
-        }
-        override fun get(d: GradientDrawable): Int {
-            return d.alpha
-        }
-    }
-
-    init {
-        setClipToPadding(false)
-        setClipChildren(false)
-        setVisibility(View.INVISIBLE)
-        addView(circle)
-    }
-
-    /**
-     * Sets up view with the provided resource ids.
-     *
-     * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called
-     * with default params in module specific extension:
-     * @see [DismissView.setup] in DismissViewExt.kt
-     */
-    fun setup(config: Config) {
-        this.config = config
-
-        // Setup layout
-        layoutParams = LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                resources.getDimensionPixelSize(config.floatingGradientHeightResId),
-                Gravity.BOTTOM)
-        updatePadding()
-
-        // Setup gradient
-        gradientDrawable = createGradient(color = config.floatingGradientColorResId)
-        setBackgroundDrawable(gradientDrawable)
-
-        // Setup DismissCircleView
-        circle.id = config.dismissViewResId
-        circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId)
-        val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId)
-        circle.layoutParams = LayoutParams(targetSize, targetSize,
-                Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
-        // Initial position with circle offscreen so it's animated up
-        circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
-                .toFloat()
-    }
-
-    /**
-     * Animates this view in.
-     */
-    fun show() {
-        if (isShowing) return
-        val gradientDrawable = checkExists(gradientDrawable) ?: return
-        isShowing = true
-        setVisibility(View.VISIBLE)
-        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
-                gradientDrawable.alpha, 255)
-        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
-        alphaAnim.start()
-
-        animator.cancel()
-        animator
-            .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
-            .start()
-    }
-
-    /**
-     * Animates this view out, as well as the circle that encircles the bubbles, if they
-     * were dragged into the target and encircled.
-     */
-    fun hide() {
-        if (!isShowing) return
-        val gradientDrawable = checkExists(gradientDrawable) ?: return
-        isShowing = false
-        val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
-                gradientDrawable.alpha, 0)
-        alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
-        alphaAnim.start()
-        animator
-            .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
-                spring)
-            .withEndActions({
-                visibility = View.INVISIBLE
-                circle.scaleX = 1f
-                circle.scaleY = 1f
-            })
-            .start()
-    }
-
-    /**
-     * Cancels the animator for the dismiss target.
-     */
-    fun cancelAnimators() {
-        animator.cancel()
-    }
-
-    fun updateResources() {
-        val config = checkExists(config) ?: return
-        updatePadding()
-        layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId)
-        val targetSize = resources.getDimensionPixelSize(config.targetSizeResId)
-        circle.layoutParams.width = targetSize
-        circle.layoutParams.height = targetSize
-        circle.requestLayout()
-    }
-
-    private fun createGradient(@ColorRes color: Int): GradientDrawable {
-        val gradientColor = ContextCompat.getColor(context, color)
-        val alpha = 0.7f * 255
-        val gradientColorWithAlpha = Color.argb(alpha.toInt(),
-                Color.red(gradientColor),
-                Color.green(gradientColor),
-                Color.blue(gradientColor))
-        val gd = GradientDrawable(
-                GradientDrawable.Orientation.BOTTOM_TOP,
-                intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
-        gd.setDither(true)
-        gd.setAlpha(0)
-        return gd
-    }
-
-    private fun updatePadding() {
-        val config = checkExists(config) ?: return
-        val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
-        val navInset = insets.getInsetsIgnoringVisibility(
-                WindowInsets.Type.navigationBars())
-        setPadding(0, 0, 0, navInset.bottom +
-                resources.getDimensionPixelSize(config.bottomMarginResId))
-    }
-
-    /**
-     * Checks if the value is set up and exists, if not logs an exception.
-     * Used for convenient logging in case `setup` wasn't called before
-     *
-     * @return value provided as argument
-     */
-    private fun <T>checkExists(value: T?): T? {
-        if (value == null) Log.e(TAG, SHOULD_SETUP)
-        return value
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
deleted file mode 100644
index 4e55ba2..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
+++ /dev/null
@@ -1,180 +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.wm.shell.common.bubbles
-
-import android.graphics.PointF
-import android.view.MotionEvent
-import android.view.VelocityTracker
-import android.view.View
-import android.view.ViewConfiguration
-import kotlin.math.hypot
-
-/**
- * Listener which receives [onDown], [onMove], and [onUp] events, with relevant information about
- * the coordinates of the touch and the view relative to the initial ACTION_DOWN event and the
- * view's initial position.
- */
-abstract class RelativeTouchListener : View.OnTouchListener {
-
-    /**
-     * Called when an ACTION_DOWN event is received for the given view.
-     *
-     * @return False if the object is not interested in MotionEvents at this time, or true if we
-     * should consume this event and subsequent events, and begin calling [onMove].
-     */
-    abstract fun onDown(v: View, ev: MotionEvent): Boolean
-
-    /**
-     * Called when an ACTION_MOVE event is received for the given view. This signals that the view
-     * is being dragged.
-     *
-     * @param viewInitialX The view's translationX value when this touch gesture started.
-     * @param viewInitialY The view's translationY value when this touch gesture started.
-     * @param dx Horizontal distance covered since the initial ACTION_DOWN event, in pixels.
-     * @param dy Vertical distance covered since the initial ACTION_DOWN event, in pixels.
-     */
-    abstract fun onMove(
-        v: View,
-        ev: MotionEvent,
-        viewInitialX: Float,
-        viewInitialY: Float,
-        dx: Float,
-        dy: Float
-    )
-
-    /**
-     * Called when an ACTION_UP event is received for the given view. This signals that a drag or
-     * fling gesture has completed.
-     *
-     * @param viewInitialX The view's translationX value when this touch gesture started.
-     * @param viewInitialY The view's translationY value when this touch gesture started.
-     * @param dx Horizontal distance covered, in pixels.
-     * @param dy Vertical distance covered, in pixels.
-     * @param velX The final horizontal velocity of the gesture, in pixels/second.
-     * @param velY The final vertical velocity of the gesture, in pixels/second.
-     */
-    abstract fun onUp(
-        v: View,
-        ev: MotionEvent,
-        viewInitialX: Float,
-        viewInitialY: Float,
-        dx: Float,
-        dy: Float,
-        velX: Float,
-        velY: Float
-    )
-
-    open fun onCancel(
-        v: View,
-        ev: MotionEvent,
-        viewInitialX: Float,
-        viewInitialY: Float
-    ) {}
-
-    /** The raw coordinates of the last ACTION_DOWN event. */
-    private var touchDown: PointF? = null
-
-    /** The coordinates of the view, at the time of the last ACTION_DOWN event. */
-    private val viewPositionOnTouchDown = PointF()
-
-    private val velocityTracker = VelocityTracker.obtain()
-
-    private var touchSlop: Int = -1
-    private var movedEnough = false
-
-    private var performedLongClick = false
-
-    override fun onTouch(v: View, ev: MotionEvent): Boolean {
-        addMovement(ev)
-
-        val dx = touchDown?.let { ev.rawX - it.x } ?: 0f
-        val dy = touchDown?.let { ev.rawY - it.y } ?: 0f
-
-        when (ev.action) {
-            MotionEvent.ACTION_DOWN -> {
-                if (!onDown(v, ev)) {
-                    return false
-                }
-
-                // Grab the touch slop, it might have changed if the config changed since the
-                // last gesture.
-                touchSlop = ViewConfiguration.get(v.context).scaledTouchSlop
-
-                touchDown = PointF(ev.rawX, ev.rawY)
-                viewPositionOnTouchDown.set(v.translationX, v.translationY)
-
-                performedLongClick = false
-                v.handler?.postDelayed({
-                    if (v.isLongClickable) {
-                        performedLongClick = v.performLongClick()
-                    }
-                }, ViewConfiguration.getLongPressTimeout().toLong())
-            }
-
-            MotionEvent.ACTION_MOVE -> {
-                if (touchDown == null) return false
-                if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) {
-                    movedEnough = true
-                    v.handler?.removeCallbacksAndMessages(null)
-                }
-
-                if (movedEnough) {
-                    onMove(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy)
-                }
-            }
-
-            MotionEvent.ACTION_UP -> {
-                if (touchDown == null) return false
-                if (movedEnough) {
-                    velocityTracker.computeCurrentVelocity(1000 /* units */)
-                    onUp(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy,
-                            velocityTracker.xVelocity, velocityTracker.yVelocity)
-                } else if (!performedLongClick) {
-                    v.performClick()
-                } else {
-                    v.handler?.removeCallbacksAndMessages(null)
-                }
-
-                velocityTracker.clear()
-                movedEnough = false
-                touchDown = null
-            }
-
-            MotionEvent.ACTION_CANCEL -> {
-                if (touchDown == null) return false
-                v.handler?.removeCallbacksAndMessages(null)
-                velocityTracker.clear()
-                movedEnough = false
-                touchDown = null
-                onCancel(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y)
-            }
-        }
-
-        return true
-    }
-
-    /**
-     * Adds a movement to the velocity tracker using raw screen coordinates.
-     */
-    private fun addMovement(event: MotionEvent) {
-        val deltaX = event.rawX - event.x
-        val deltaY = event.rawY - event.y
-        event.offsetLocation(deltaX, deltaY)
-        velocityTracker.addMovement(event)
-        event.offsetLocation(-deltaX, -deltaY)
-    }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
deleted file mode 100644
index f90591b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RemovedBubble.java
+++ /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.wm.shell.common.bubbles;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents a removed bubble, defining the key and reason the bubble was removed.
- */
-public class RemovedBubble implements Parcelable {
-
-    private final String mKey;
-    private final int mRemovalReason;
-
-    public RemovedBubble(String key, int removalReason) {
-        mKey = key;
-        mRemovalReason = removalReason;
-    }
-
-    public RemovedBubble(Parcel parcel) {
-        mKey = parcel.readString();
-        mRemovalReason = parcel.readInt();
-    }
-
-    public String getKey() {
-        return mKey;
-    }
-
-    public int getRemovalReason() {
-        return mRemovalReason;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mKey);
-        dest.writeInt(mRemovalReason);
-    }
-
-    @NonNull
-    public static final Creator<RemovedBubble> CREATOR =
-            new Creator<RemovedBubble>() {
-                public RemovedBubble createFromParcel(Parcel source) {
-                    return new RemovedBubble(source);
-                }
-                public RemovedBubble[] newArray(int size) {
-                    return new RemovedBubble[size];
-                }
-            };
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl
deleted file mode 100644
index c968e80..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.aidl
+++ /dev/null
@@ -1,19 +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.wm.shell.common.desktopmode;
-
-parcelable DesktopModeTransitionSource;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt
deleted file mode 100644
index dbbf1786..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/desktopmode/DesktopModeTransitionSource.kt
+++ /dev/null
@@ -1,54 +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.wm.shell.common.desktopmode
-
-import android.os.Parcel
-import android.os.Parcelable
-
-/** Transition source types for Desktop Mode. */
-enum class DesktopModeTransitionSource : Parcelable {
-    /** Transitions that originated as a consequence of task dragging. */
-    TASK_DRAG,
-    /** Transitions that originated from an app from Overview. */
-    APP_FROM_OVERVIEW,
-    /** Transitions that originated from app handle menu button */
-    APP_HANDLE_MENU_BUTTON,
-    /** Transitions that originated as a result of keyboard shortcuts. */
-    KEYBOARD_SHORTCUT,
-    /** Transitions with source unknown. */
-    UNKNOWN;
-
-    override fun describeContents(): Int {
-        return 0
-    }
-
-    override fun writeToParcel(dest: Parcel, flags: Int) {
-        dest.writeString(name)
-    }
-
-    companion object {
-        @JvmField
-        val CREATOR =
-            object : Parcelable.Creator<DesktopModeTransitionSource> {
-                override fun createFromParcel(parcel: Parcel): DesktopModeTransitionSource {
-                    return parcel.readString()?.let { valueOf(it) } ?: UNKNOWN
-                }
-
-                override fun newArray(size: Int) = arrayOfNulls<DesktopModeTransitionSource>(size)
-            }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
deleted file mode 100644
index 123d4dc..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt
+++ /dev/null
@@ -1,701 +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.wm.shell.common.magnetictarget
-
-import android.annotation.SuppressLint
-import android.content.Context
-import android.graphics.PointF
-import android.os.VibrationAttributes
-import android.os.VibrationEffect
-import android.os.Vibrator
-import android.view.MotionEvent
-import android.view.VelocityTracker
-import android.view.View
-import android.view.ViewConfiguration
-import androidx.dynamicanimation.animation.DynamicAnimation
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.dynamicanimation.animation.SpringForce
-import com.android.wm.shell.shared.animation.PhysicsAnimator
-import kotlin.math.abs
-import kotlin.math.hypot
-
-/**
- * Utility class for creating 'magnetized' objects that are attracted to one or more magnetic
- * targets. Magnetic targets attract objects that are dragged near them, and hold them there unless
- * they're moved away or released. Releasing objects inside a magnetic target typically performs an
- * action on the object.
- *
- * MagnetizedObject also supports flinging to targets, which will result in the object being pulled
- * into the target and released as if it was dragged into it.
- *
- * To use this class, either construct an instance with an object of arbitrary type, or use the
- * [MagnetizedObject.magnetizeView] shortcut method if you're magnetizing a view. Then, set
- * [magnetListener] to receive event callbacks. In your touch handler, pass all MotionEvents
- * that move this object to [maybeConsumeMotionEvent]. If that method returns true, consider the
- * event consumed by the MagnetizedObject and don't move the object unless it begins returning false
- * again.
- *
- * @param context Context, used to retrieve a Vibrator instance for vibration effects.
- * @param underlyingObject The actual object that we're magnetizing.
- * @param xProperty Property that sets the x value of the object's position.
- * @param yProperty Property that sets the y value of the object's position.
- */
-abstract class MagnetizedObject<T : Any>(
-    val context: Context,
-
-    /** The actual object that is animated. */
-    val underlyingObject: T,
-
-    /** Property that gets/sets the object's X value. */
-    val xProperty: FloatPropertyCompat<in T>,
-
-    /** Property that gets/sets the object's Y value. */
-    val yProperty: FloatPropertyCompat<in T>
-) {
-
-    /** Return the width of the object. */
-    abstract fun getWidth(underlyingObject: T): Float
-
-    /** Return the height of the object. */
-    abstract fun getHeight(underlyingObject: T): Float
-
-    /**
-     * Fill the provided array with the location of the top-left of the object, relative to the
-     * entire screen. Compare to [View.getLocationOnScreen].
-     */
-    abstract fun getLocationOnScreen(underlyingObject: T, loc: IntArray)
-
-    /** Methods for listening to events involving a magnetized object.  */
-    interface MagnetListener {
-
-        /**
-         * Called when touch events move within the magnetic field of a target, causing the
-         * object to animate to the target and become 'stuck' there. The animation happens
-         * automatically here - you should not move the object. You can, however, change its state
-         * to indicate to the user that it's inside the target and releasing it will have an effect.
-         *
-         * [maybeConsumeMotionEvent] is now returning true and will continue to do so until a call
-         * to [onUnstuckFromTarget] or [onReleasedInTarget].
-         *
-         * @param target The target that the object is now stuck to.
-         * @param draggedObject The object that is stuck to the target.
-         */
-        fun onStuckToTarget(target: MagneticTarget, draggedObject: MagnetizedObject<*>)
-
-        /**
-         * Called when the object is no longer stuck to a target. This means that either touch
-         * events moved outside of the magnetic field radius, or that a forceful fling out of the
-         * target was detected.
-         *
-         * The object won't be automatically animated out of the target, since you're responsible
-         * for moving the object again. You should move it (or animate it) using your own
-         * movement/animation logic.
-         *
-         * Reverse any effects applied in [onStuckToTarget] here.
-         *
-         * If [wasFlungOut] is true, [maybeConsumeMotionEvent] returned true for the ACTION_UP event
-         * that concluded the fling. If [wasFlungOut] is false, that means a drag gesture is ongoing
-         * and [maybeConsumeMotionEvent] is now returning false.
-         *
-         * @param target The target that this object was just unstuck from.
-         * @param draggedObject The object being unstuck from the target.
-         * @param velX The X velocity of the touch gesture when it exited the magnetic field.
-         * @param velY The Y velocity of the touch gesture when it exited the magnetic field.
-         * @param wasFlungOut Whether the object was unstuck via a fling gesture. This means that
-         * an ACTION_UP event was received, and that the gesture velocity was sufficient to conclude
-         * that the user wants to un-stick the object despite no touch events occurring outside of
-         * the magnetic field radius.
-         */
-        fun onUnstuckFromTarget(
-            target: MagneticTarget,
-            draggedObject: MagnetizedObject<*>,
-            velX: Float,
-            velY: Float,
-            wasFlungOut: Boolean
-        )
-
-        /**
-         * Called when the object is released inside a target, or flung towards it with enough
-         * velocity to reach it.
-         *
-         * @param target The target that the object was released in.
-         * @param draggedObject The object released in the target.
-         */
-        fun onReleasedInTarget(target: MagneticTarget, draggedObject: MagnetizedObject<*>)
-    }
-
-    private val animator: PhysicsAnimator<T> = PhysicsAnimator.getInstance(underlyingObject)
-    private val objectLocationOnScreen = IntArray(2)
-
-    /**
-     * Targets that have been added to this object. These will all be considered when determining
-     * magnetic fields and fling trajectories.
-     */
-    private val associatedTargets = ArrayList<MagneticTarget>()
-
-    private val velocityTracker: VelocityTracker = VelocityTracker.obtain()
-    private val vibrator: Vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
-    private val vibrationAttributes: VibrationAttributes = VibrationAttributes.createForUsage(
-            VibrationAttributes.USAGE_TOUCH)
-
-    private var touchDown = PointF()
-    private var touchSlop = 0
-    private var movedBeyondSlop = false
-
-    /** Whether touch events are presently occurring within the magnetic field area of a target. */
-    val objectStuckToTarget: Boolean
-        get() = targetObjectIsStuckTo != null
-
-    /** The target the object is stuck to, or null if the object is not stuck to any target. */
-    private var targetObjectIsStuckTo: MagneticTarget? = null
-
-    /**
-     * Sets the listener to receive events. This must be set, or [maybeConsumeMotionEvent]
-     * will always return false and no magnetic effects will occur.
-     */
-    lateinit var magnetListener: MagnetizedObject.MagnetListener
-
-    /**
-     * Optional update listener to provide to the PhysicsAnimator that is used to spring the object
-     * into the target.
-     */
-    var physicsAnimatorUpdateListener: PhysicsAnimator.UpdateListener<T>? = null
-
-    /**
-     * Optional end listener to provide to the PhysicsAnimator that is used to spring the object
-     * into the target.
-     */
-    var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
-
-    /**
-     * Method that is called when the object should be animated stuck to the target. The default
-     * implementation uses the object's x and y properties to animate the object centered inside the
-     * target. You can override this if you need custom animation.
-     *
-     * The method is invoked with the MagneticTarget that the object is sticking to, the X and Y
-     * velocities of the gesture that brought the object into the magnetic radius, whether or not it
-     * was flung, and a callback you must call after your animation completes.
-     */
-    var animateStuckToTarget: (MagneticTarget, Float, Float, Boolean, (() -> Unit)?) -> Unit =
-            ::animateStuckToTargetInternal
-
-    /**
-     * Sets whether forcefully flinging the object vertically towards a target causes it to be
-     * attracted to the target and then released immediately, despite never being dragged within the
-     * magnetic field.
-     */
-    var flingToTargetEnabled = true
-
-    /**
-     * If fling to target is enabled, forcefully flinging the object towards a target will cause
-     * it to be attracted to the target and then released immediately, despite never being dragged
-     * within the magnetic field.
-     *
-     * This sets the width of the area considered 'near' enough a target to be considered a fling,
-     * in terms of percent of the target view's width. For example, setting this to 3f means that
-     * flings towards a 100px-wide target will be considered 'near' enough if they're towards the
-     * 300px-wide area around the target.
-     *
-     * Flings whose trajectory intersects the area will be attracted and released - even if the
-     * target view itself isn't intersected:
-     *
-     * |             |
-     * |           0 |
-     * |          /  |
-     * |         /   |
-     * |      X /    |
-     * |.....###.....|
-     *
-     *
-     * Flings towards the target whose trajectories do not intersect the area will be treated as
-     * normal flings and the magnet will leave the object alone:
-     *
-     * |             |
-     * |             |
-     * |   0         |
-     * |  /          |
-     * | /    X      |
-     * |.....###.....|
-     *
-     */
-    var flingToTargetWidthPercent = 3f
-
-    /**
-     * Sets the minimum velocity (in pixels per second) required to fling an object to the target
-     * without dragging it into the magnetic field.
-     */
-    var flingToTargetMinVelocity = 4000f
-
-    /**
-     * Sets the minimum velocity (in pixels per second) required to fling un-stuck an object stuck
-     * to the target. If this velocity is reached, the object will be freed even if it wasn't moved
-     * outside the magnetic field radius.
-     */
-    var flingUnstuckFromTargetMinVelocity = 4000f
-
-    /**
-     * Sets the maximum X velocity above which the object will not stick to the target. Even if the
-     * object is dragged through the magnetic field, it will not stick to the target until the
-     * horizontal velocity is below this value.
-     */
-    var stickToTargetMaxXVelocity = 2000f
-
-    /**
-     * Enable or disable haptic vibration effects when the object interacts with the magnetic field.
-     *
-     * If you're experiencing crashes when the object enters targets, ensure that you have the
-     * android.permission.VIBRATE permission!
-     */
-    var hapticsEnabled = true
-
-    /** Default spring configuration to use for animating the object into a target. */
-    var springConfig = PhysicsAnimator.SpringConfig(
-            SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY)
-
-    /**
-     * Spring configuration to use to spring the object into a target specifically when it's flung
-     * towards (rather than dragged near) it.
-     */
-    var flungIntoTargetSpringConfig = springConfig
-
-    /**
-     * Adds the provided MagneticTarget to this object. The object will now be attracted to the
-     * target if it strays within its magnetic field or is flung towards it.
-     *
-     * If this target (or its magnetic field) overlaps another target added to this object, the
-     * prior target will take priority.
-     */
-    fun addTarget(target: MagneticTarget) {
-        associatedTargets.add(target)
-        target.updateLocationOnScreen()
-    }
-
-    /**
-     * Shortcut that accepts a View and a magnetic field radius and adds it as a magnetic target.
-     *
-     * @return The MagneticTarget instance for the given View. This can be used to change the
-     * target's magnetic field radius after it's been added. It can also be added to other
-     * magnetized objects.
-     */
-    fun addTarget(target: View, magneticFieldRadiusPx: Int): MagneticTarget {
-        return MagneticTarget(target, magneticFieldRadiusPx).also { addTarget(it) }
-    }
-
-    /**
-     * Removes the given target from this object. The target will no longer attract the object.
-     */
-    fun removeTarget(target: MagneticTarget) {
-        associatedTargets.remove(target)
-    }
-
-    /**
-     * Removes all associated targets from this object.
-     */
-    fun clearAllTargets() {
-        associatedTargets.clear()
-    }
-
-    /**
-     * Provide this method with all motion events that move the magnetized object. If the
-     * location of the motion events moves within the magnetic field of a target, or indicate a
-     * fling-to-target gesture, this method will return true and you should not move the object
-     * yourself until it returns false again.
-     *
-     * Note that even when this method returns true, you should continue to pass along new motion
-     * events so that we know when the events move back outside the magnetic field area.
-     *
-     * This method will always return false if you haven't set a [magnetListener].
-     */
-    fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean {
-        // Short-circuit if we don't have a listener or any targets, since those are required.
-        if (associatedTargets.size == 0) {
-            return false
-        }
-
-        // When a gesture begins, recalculate target views' positions on the screen in case they
-        // have changed. Also, clear state.
-        if (ev.action == MotionEvent.ACTION_DOWN) {
-            updateTargetViews()
-
-            // Clear the velocity tracker and stuck target.
-            velocityTracker.clear()
-            targetObjectIsStuckTo = null
-
-            // Set the touch down coordinates and reset movedBeyondSlop.
-            touchDown.set(ev.rawX, ev.rawY)
-            movedBeyondSlop = false
-        }
-
-        // Always pass events to the VelocityTracker.
-        addMovement(ev)
-
-        // If we haven't yet moved beyond the slop distance, check if we have.
-        if (!movedBeyondSlop) {
-            val dragDistance = hypot(ev.rawX - touchDown.x, ev.rawY - touchDown.y)
-            if (dragDistance > touchSlop) {
-                // If we're beyond the slop distance, save that and continue.
-                movedBeyondSlop = true
-            } else {
-                // Otherwise, don't do anything yet.
-                return false
-            }
-        }
-
-        val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
-            val distanceFromTargetCenter = hypot(
-                    ev.rawX - target.centerOnDisplayX(),
-                    ev.rawY - target.centerOnDisplayY())
-            distanceFromTargetCenter < target.magneticFieldRadiusPx
-        }
-
-        // If we aren't currently stuck to a target, and we're in the magnetic field of a target,
-        // we're newly stuck.
-        val objectNewlyStuckToTarget =
-                !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null
-
-        // If we are currently stuck to a target, we're in the magnetic field of a target, and that
-        // target isn't the one we're currently stuck to, then touch events have moved into a
-        // adjacent target's magnetic field.
-        val objectMovedIntoDifferentTarget =
-                objectStuckToTarget &&
-                        targetObjectIsInMagneticFieldOf != null &&
-                        targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf
-
-        if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) {
-            velocityTracker.computeCurrentVelocity(1000)
-            val velX = velocityTracker.xVelocity
-            val velY = velocityTracker.yVelocity
-
-            // If the object is moving too quickly within the magnetic field, do not stick it. This
-            // only applies to objects newly stuck to a target. If the object is moved into a new
-            // target, it wasn't moving at all (since it was stuck to the previous one).
-            if (objectNewlyStuckToTarget && abs(velX) > stickToTargetMaxXVelocity) {
-                return false
-            }
-
-            // This touch event is newly within the magnetic field - let the listener know, and
-            // animate sticking to the magnet.
-            targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
-            cancelAnimations()
-            magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!, this)
-            animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null)
-
-            vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
-        } else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
-            velocityTracker.computeCurrentVelocity(1000)
-
-            // This touch event is newly outside the magnetic field - let the listener know. It will
-            // move the object out of the target using its own movement logic.
-            cancelAnimations()
-            magnetListener.onUnstuckFromTarget(
-                    targetObjectIsStuckTo!!, this,
-                    velocityTracker.xVelocity, velocityTracker.yVelocity,
-                    wasFlungOut = false)
-            targetObjectIsStuckTo = null
-
-            vibrateIfEnabled(VibrationEffect.EFFECT_TICK)
-        }
-
-        // First, check for relevant gestures concluding with an ACTION_UP.
-        if (ev.action == MotionEvent.ACTION_UP) {
-            velocityTracker.computeCurrentVelocity(1000 /* units */)
-            val velX = velocityTracker.xVelocity
-            val velY = velocityTracker.yVelocity
-
-            // Cancel the magnetic animation since we might still be springing into the magnetic
-            // target, but we're about to fling away or release.
-            cancelAnimations()
-
-            if (objectStuckToTarget) {
-                if (-velY > flingUnstuckFromTargetMinVelocity) {
-                    // If the object is stuck, but it was forcefully flung away from the target in
-                    // the upward direction, tell the listener so the object can be animated out of
-                    // the target.
-                    magnetListener.onUnstuckFromTarget(
-                            targetObjectIsStuckTo!!, this,
-                            velX, velY, wasFlungOut = true)
-                } else {
-                    // If the object is stuck and not flung away, it was released inside the target.
-                    magnetListener.onReleasedInTarget(targetObjectIsStuckTo!!, this)
-                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
-                }
-
-                // Either way, we're no longer stuck.
-                targetObjectIsStuckTo = null
-                return true
-            }
-
-            // The target we're flinging towards, or null if we're not flinging towards any target.
-            val flungToTarget = associatedTargets.firstOrNull { target ->
-                isForcefulFlingTowardsTarget(target, ev.rawX, ev.rawY, velX, velY)
-            }
-
-            if (flungToTarget != null) {
-                // If this is a fling-to-target, animate the object to the magnet and then release
-                // it.
-                magnetListener.onStuckToTarget(flungToTarget, this)
-                targetObjectIsStuckTo = flungToTarget
-
-                animateStuckToTarget(flungToTarget, velX, velY, true) {
-                    magnetListener.onReleasedInTarget(flungToTarget, this)
-                    targetObjectIsStuckTo = null
-                    vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
-                }
-
-                return true
-            }
-
-            // If it's not either of those things, we are not interested.
-            return false
-        }
-
-        return objectStuckToTarget // Always consume touch events if the object is stuck.
-    }
-
-    /** Plays the given vibration effect if haptics are enabled. */
-    @SuppressLint("MissingPermission")
-    private fun vibrateIfEnabled(effectId: Int) {
-        if (hapticsEnabled) {
-            vibrator.vibrate(VibrationEffect.createPredefined(effectId), vibrationAttributes)
-        }
-    }
-
-    /** Adds the movement to the velocity tracker using raw coordinates. */
-    private fun addMovement(event: MotionEvent) {
-        // Add movement to velocity tracker using raw screen X and Y coordinates instead
-        // of window coordinates because the window frame may be moving at the same time.
-        val deltaX = event.rawX - event.x
-        val deltaY = event.rawY - event.y
-        event.offsetLocation(deltaX, deltaY)
-        velocityTracker.addMovement(event)
-        event.offsetLocation(-deltaX, -deltaY)
-    }
-
-    /** Animates sticking the object to the provided target with the given start velocities.  */
-    private fun animateStuckToTargetInternal(
-        target: MagneticTarget,
-        velX: Float,
-        velY: Float,
-        flung: Boolean,
-        after: (() -> Unit)? = null
-    ) {
-        target.updateLocationOnScreen()
-        getLocationOnScreen(underlyingObject, objectLocationOnScreen)
-
-        // Calculate the difference between the target's center coordinates and the object's.
-        // Animating the object's x/y properties by these values will center the object on top
-        // of the magnetic target.
-        val xDiff = target.centerOnScreen.x -
-                getWidth(underlyingObject) / 2f - objectLocationOnScreen[0]
-        val yDiff = target.centerOnScreen.y -
-                getHeight(underlyingObject) / 2f - objectLocationOnScreen[1]
-
-        val springConfig = if (flung) flungIntoTargetSpringConfig else springConfig
-
-        cancelAnimations()
-
-        // Animate to the center of the target.
-        animator
-                .spring(xProperty, xProperty.getValue(underlyingObject) + xDiff, velX,
-                        springConfig)
-                .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY,
-                        springConfig)
-
-        if (physicsAnimatorUpdateListener != null) {
-            animator.addUpdateListener(physicsAnimatorUpdateListener!!)
-        }
-
-        if (physicsAnimatorEndListener != null) {
-            animator.addEndListener(physicsAnimatorEndListener!!)
-        }
-
-        if (after != null) {
-            animator.withEndActions(after)
-        }
-
-        animator.start()
-    }
-
-    /**
-     * Whether or not the provided values match a 'fast fling' towards the provided target. If it
-     * does, we consider it a fling-to-target gesture.
-     */
-    private fun isForcefulFlingTowardsTarget(
-        target: MagneticTarget,
-        rawX: Float,
-        rawY: Float,
-        velX: Float,
-        velY: Float
-    ): Boolean {
-        if (!flingToTargetEnabled) {
-            return false
-        }
-
-        // Whether velocity is sufficient, depending on whether we're flinging into a target at the
-        // top or the bottom of the screen.
-        val velocitySufficient =
-                if (rawY < target.centerOnDisplayY()) velY > flingToTargetMinVelocity
-                else velY < flingToTargetMinVelocity
-
-        if (!velocitySufficient) {
-            return false
-        }
-
-        // Whether the trajectory of the fling intersects the target area.
-        var targetCenterXIntercept = rawX
-
-        // Only do math if the X velocity is non-zero, otherwise X won't change.
-        if (velX != 0f) {
-            // Rise over run...
-            val slope = velY / velX
-            // ...y = mx + b, b = y / mx...
-            val yIntercept = rawY - slope * rawX
-
-            // ...calculate the x value when y = the target's y-coordinate.
-            targetCenterXIntercept = (target.centerOnDisplayY() - yIntercept) / slope
-        }
-
-        // The width of the area we're looking for a fling towards.
-        val targetAreaWidth = target.targetView.width * flingToTargetWidthPercent
-
-        // Velocity was sufficient, so return true if the intercept is within the target area.
-        return targetCenterXIntercept > target.centerOnDisplayX() - targetAreaWidth / 2 &&
-                targetCenterXIntercept < target.centerOnDisplayX() + targetAreaWidth / 2
-    }
-
-    /** Cancel animations on this object's x/y properties. */
-    internal fun cancelAnimations() {
-        animator.cancel(xProperty, yProperty)
-    }
-
-    /** Updates the locations on screen of all of the [associatedTargets]. */
-    internal fun updateTargetViews() {
-        associatedTargets.forEach { it.updateLocationOnScreen() }
-
-        // Update the touch slop, since the configuration may have changed.
-        if (associatedTargets.size > 0) {
-            touchSlop =
-                    ViewConfiguration.get(associatedTargets[0].targetView.context).scaledTouchSlop
-        }
-    }
-
-    /**
-     * Represents a target view with a magnetic field radius and cached center-on-screen
-     * coordinates.
-     *
-     * Instances of MagneticTarget are passed to a MagnetizedObject's [addTarget], and can then
-     * attract the object if it's dragged near or flung towards it. MagneticTargets can be added to
-     * multiple objects.
-     */
-    class MagneticTarget(
-        val targetView: View,
-        var magneticFieldRadiusPx: Int
-    ) {
-        val centerOnScreen = PointF()
-
-        /**
-         * Set screen vertical offset amount.
-         *
-         * Screen surface may be vertically shifted in some cases, for example when one-handed mode
-         * is enabled. [MagneticTarget] and [MagnetizedObject] set their location in screen
-         * coordinates (see [MagneticTarget.centerOnScreen] and
-         * [MagnetizedObject.getLocationOnScreen] respectively).
-         *
-         * When a [MagnetizedObject] is dragged, the touch location is determined by
-         * [MotionEvent.getRawX] and [MotionEvent.getRawY]. These work in display coordinates. When
-         * screen is shifted due to one-handed mode, display coordinates and screen coordinates do
-         * not match. To determine if a [MagnetizedObject] is dragged into a [MagneticTarget], view
-         * location on screen is translated to display coordinates using this offset value.
-         */
-        var screenVerticalOffset: Int = 0
-
-        private val tempLoc = IntArray(2)
-
-        fun updateLocationOnScreen() {
-            targetView.post {
-                targetView.getLocationOnScreen(tempLoc)
-
-                // Add half of the target size to get the center, and subtract translation since the
-                // target could be animating in while we're doing this calculation.
-                centerOnScreen.set(
-                        tempLoc[0] + targetView.width / 2f - targetView.translationX,
-                        tempLoc[1] + targetView.height / 2f - targetView.translationY)
-            }
-        }
-
-        /**
-         * Get target center coordinate on x-axis on display. [centerOnScreen] has to be up to date
-         * by calling [updateLocationOnScreen] first.
-         */
-        fun centerOnDisplayX(): Float {
-            return centerOnScreen.x
-        }
-
-        /**
-         * Get target center coordinate on y-axis on display. [centerOnScreen] has to be up to date
-         * by calling [updateLocationOnScreen] first. Use [screenVerticalOffset] to update the
-         * screen offset compared to the display.
-         */
-        fun centerOnDisplayY(): Float {
-            return centerOnScreen.y + screenVerticalOffset
-        }
-    }
-
-    companion object {
-        /**
-         * Magnetizes the given view. Magnetized views are attracted to one or more magnetic
-         * targets. Magnetic targets attract objects that are dragged near them, and hold them there
-         * unless they're moved away or released. Releasing objects inside a magnetic target
-         * typically performs an action on the object.
-         *
-         * Magnetized views can also be flung to targets, which will result in the view being pulled
-         * into the target and released as if it was dragged into it.
-         *
-         * To use the returned MagnetizedObject<View> instance, first set [magnetListener] to
-         * receive event callbacks. In your touch handler, pass all MotionEvents that move this view
-         * to [maybeConsumeMotionEvent]. If that method returns true, consider the event consumed by
-         * MagnetizedObject and don't move the view unless it begins returning false again.
-         *
-         * The view will be moved via translationX/Y properties, and its
-         * width/height will be determined via getWidth()/getHeight(). If you are animating
-         * something other than a view, or want to position your view using properties other than
-         * translationX/Y, implement an instance of [MagnetizedObject].
-         *
-         * Note that the magnetic library can't re-order your view automatically. If the view
-         * renders on top of the target views, it will obscure the target when it sticks to it.
-         * You'll want to bring the view to the front in [MagnetListener.onStuckToTarget].
-         */
-        @JvmStatic
-        fun <T : View> magnetizeView(view: T): MagnetizedObject<T> {
-            return object : MagnetizedObject<T>(
-                    view.context,
-                    view,
-                    DynamicAnimation.TRANSLATION_X,
-                    DynamicAnimation.TRANSLATION_Y) {
-                override fun getWidth(underlyingObject: T): Float {
-                    return underlyingObject.width.toFloat()
-                }
-
-                override fun getHeight(underlyingObject: T): Float {
-                    return underlyingObject.height.toFloat() }
-
-                override fun getLocationOnScreen(underlyingObject: T, loc: IntArray) {
-                    underlyingObject.getLocationOnScreen(loc)
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
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 dcf84d9..7070ce9 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
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager
 import android.graphics.Rect
 import android.os.RemoteException
-import android.os.SystemProperties
 import android.util.DisplayMetrics
 import android.util.Log
 import android.util.Pair
@@ -178,9 +177,7 @@
                 "org.chromium.arc", 0)
             val isTv = AppGlobals.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_LEANBACK, 0)
-            isPip2ExperimentEnabled = SystemProperties.getBoolean(
-                    "persist.wm_shell.pip2", false) ||
-                    (Flags.enablePip2Implementation() && !isArc && !isTv)
+            isPip2ExperimentEnabled = Flags.enablePip2() && !isArc && !isTv
         }
         return isPip2ExperimentEnabled as Boolean
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index 999da24..bdbd4cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -32,7 +32,7 @@
 import android.view.View;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 /**
  * View for the handle in the docked stack divider.
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 2e1789a..8156a9c 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,14 +19,14 @@
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_30_70;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_70_30;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_MINIMIZE;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_30_70;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_70_30;
+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;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition;
 
 import android.content.res.Resources;
 import android.graphics.Rect;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 1bc1795..e7848e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -56,8 +56,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 /**
  * Divider for multi window splits.
@@ -228,7 +229,9 @@
                 : R.dimen.split_divider_handle_region_width);
         mHandleRegionHeight = getResources().getDimensionPixelSize(isLeftRightSplit
                 ? R.dimen.split_divider_handle_region_width
-                : R.dimen.split_divider_handle_region_height);
+                : DesktopModeStatus.canEnterDesktopMode(mContext)
+                        ? R.dimen.desktop_mode_portrait_split_divider_handle_region_height
+                        : R.dimen.split_divider_handle_region_height);
     }
 
     void onInsetsChanged(InsetsState insetsState, boolean animate) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index e2988bc..7175e36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -25,8 +25,8 @@
 
 import static com.android.wm.shell.common.split.SplitLayout.BEHIND_APP_VEIL_LAYER;
 import static com.android.wm.shell.common.split.SplitLayout.FRONT_APP_VEIL_LAYER;
-import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
-import static com.android.wm.shell.common.split.SplitScreenConstants.VEIL_DELAY_DURATION;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FADE_DURATION;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.VEIL_DELAY_DURATION;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
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 0e050694..2a934cb 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
@@ -26,13 +26,15 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_DOUBLE_TAP_DIVIDER;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SPLIT_SCREEN_RESIZE;
-import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
-import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
+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_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;
+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 static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
 
 import android.animation.Animator;
@@ -65,15 +67,15 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.common.split.SplitScreenConstants.SnapPosition;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.splitscreen.StageTaskListener;
 
@@ -813,7 +815,9 @@
         float growPortion = 1 - shrinkPortion;
 
         ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
-        animator.setInterpolator(Interpolators.EMPHASIZED);
+        // Set the base animation to proceed linearly. Each component of the animation (movement,
+        // shrinking, growing) overrides it with a different interpolator later.
+        animator.setInterpolator(LINEAR);
         animator.addUpdateListener(animation -> {
             if (leash == null) return;
             if (roundCorners) {
@@ -822,10 +826,11 @@
             }
 
             final float progress = (float) animation.getAnimatedValue();
-            float instantaneousX = tempStart.left + progress * diffX;
-            float instantaneousY = tempStart.top + progress * diffY;
-            int width = (int) (tempStart.width() + progress * diffWidth);
-            int height = (int) (tempStart.height() + progress * diffHeight);
+            final float moveProgress = EMPHASIZED.getInterpolation(progress);
+            float instantaneousX = tempStart.left + moveProgress * diffX;
+            float instantaneousY = tempStart.top + moveProgress * diffY;
+            int width = (int) (tempStart.width() + moveProgress * diffWidth);
+            int height = (int) (tempStart.height() + moveProgress * diffHeight);
 
             if (isGoingBehind) {
                 float shrinkDiffX; // the position adjustments needed for this frame
@@ -897,8 +902,8 @@
                             taskInfo, mTempRect, t, isGoingBehind, leash, 0, 0);
                 }
             } else {
-                final int diffOffsetX = (int) (progress * offsetX);
-                final int diffOffsetY = (int) (progress * offsetY);
+                final int diffOffsetX = (int) (moveProgress * offsetX);
+                final int diffOffsetY = (int) (moveProgress * offsetY);
                 t.setPosition(leash, instantaneousX + diffOffsetX, instantaneousY + diffOffsetY);
                 mTempRect.set(0, 0, width, height);
                 mTempRect.offsetTo(-diffOffsetX, -diffOffsetY);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
deleted file mode 100644
index 8c06de7..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java
+++ /dev/null
@@ -1,146 +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.wm.shell.common.split;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-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_UNDEFINED;
-
-import android.annotation.IntDef;
-
-import com.android.wm.shell.shared.TransitionUtil;
-
-/** Helper utility class of methods and constants that are available to be imported in Launcher. */
-public class SplitScreenConstants {
-    /** Duration used for every split fade-in or fade-out. */
-    public static final int FADE_DURATION = 133;
-    /** Duration where we keep an app veiled to allow it to redraw itself behind the scenes. */
-    public static final int VEIL_DELAY_DURATION = 400;
-
-    /** Key for passing in widget intents when invoking split from launcher workspace. */
-    public static final String KEY_EXTRA_WIDGET_INTENT = "key_extra_widget_intent";
-
-    ///////////////
-    // IMPORTANT for the following SPLIT_POSITION and SNAP_TO constants:
-    // These int values must not be changed -- they are persisted to user-defined app pairs, and
-    // will break things if changed.
-    //
-
-    /**
-     * Split position isn't specified normally meaning to use what ever it is currently set to.
-     */
-    public static final int SPLIT_POSITION_UNDEFINED = -1;
-
-    /**
-     * Specifies that a split is positioned at the top half of the screen if
-     * in portrait mode or at the left half of the screen if in landscape mode.
-     */
-    public static final int SPLIT_POSITION_TOP_OR_LEFT = 0;
-
-    /**
-     * Specifies that a split is positioned at the bottom half of the screen if
-     * in portrait mode or at the right half of the screen if in landscape mode.
-     */
-    public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1;
-
-    @IntDef(prefix = {"SPLIT_POSITION_"}, value = {
-            SPLIT_POSITION_UNDEFINED,
-            SPLIT_POSITION_TOP_OR_LEFT,
-            SPLIT_POSITION_BOTTOM_OR_RIGHT
-    })
-    public @interface SplitPosition {
-    }
-
-    /** A snap target in the first half of the screen, where the split is roughly 30-70. */
-    public static final int SNAP_TO_30_70 = 0;
-
-    /** The 50-50 snap target */
-    public static final int SNAP_TO_50_50 = 1;
-
-    /** A snap target in the latter half of the screen, where the split is roughly 70-30. */
-    public static final int SNAP_TO_70_30 = 2;
-
-    /**
-     * These snap targets are used for split pairs in a stable, non-transient state. They may be
-     * persisted in Launcher when the user saves an app pair. They are a subset of
-     * {@link SnapPosition}.
-     */
-    @IntDef(prefix = { "SNAP_TO_" }, value = {
-            SNAP_TO_30_70,
-            SNAP_TO_50_50,
-            SNAP_TO_70_30
-    })
-    public @interface PersistentSnapPosition {}
-
-    /**
-     * Checks if the snapPosition in question is a {@link PersistentSnapPosition}.
-     */
-    public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) {
-        return snapPosition == SNAP_TO_30_70
-                || snapPosition == SNAP_TO_50_50
-                || snapPosition == SNAP_TO_70_30;
-    }
-
-    /** The divider doesn't snap to any target and is freely placeable. */
-    public static final int SNAP_TO_NONE = 10;
-
-    /** If the divider reaches this value, the left/top task should be dismissed. */
-    public static final int SNAP_TO_START_AND_DISMISS = 11;
-
-    /** If the divider reaches this value, the right/bottom task should be dismissed. */
-    public static final int SNAP_TO_END_AND_DISMISS = 12;
-
-    /** A snap target positioned near the screen edge for a minimized task */
-    public static final int SNAP_TO_MINIMIZE = 13;
-
-    @IntDef(prefix = { "SNAP_TO_" }, value = {
-            SNAP_TO_30_70,
-            SNAP_TO_50_50,
-            SNAP_TO_70_30,
-            SNAP_TO_NONE,
-            SNAP_TO_START_AND_DISMISS,
-            SNAP_TO_END_AND_DISMISS,
-            SNAP_TO_MINIMIZE
-    })
-    public @interface SnapPosition {}
-
-    ///////////////
-
-    public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD};
-    public static final int[] CONTROLLED_WINDOWING_MODES =
-            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED};
-    public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE =
-            {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW,
-            WINDOWING_MODE_FREEFORM};
-
-    /** Flag applied to a transition change to identify it as a divider bar for animation. */
-    public static final int FLAG_IS_DIVIDER_BAR = TransitionUtil.FLAG_IS_DIVIDER_BAR;
-
-    public static final String splitPositionToString(@SplitPosition int pos) {
-        switch (pos) {
-            case SPLIT_POSITION_UNDEFINED:
-                return "SPLIT_POSITION_UNDEFINED";
-            case SPLIT_POSITION_TOP_OR_LEFT:
-                return "SPLIT_POSITION_TOP_OR_LEFT";
-            case SPLIT_POSITION_BOTTOM_OR_RIGHT:
-                return "SPLIT_POSITION_BOTTOM_OR_RIGHT";
-            default:
-                return "UNKNOWN";
-        }
-    }
-}
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 f9259e7..bdbcb46 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
@@ -16,34 +16,25 @@
 
 package com.android.wm.shell.common.split;
 
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED;
-
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+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.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.app.ActivityManager;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
-import android.os.UserHandle;
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.wm.shell.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
-
-import java.util.Arrays;
-import java.util.List;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
 
 /** Helper utility class for split screen components to use. */
 public class SplitScreenUtils {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index c2ee223..6146ecd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -39,6 +39,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
@@ -67,6 +68,7 @@
 import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import java.util.function.IntPredicate;
 import java.util.function.Predicate;
 
 /**
@@ -189,6 +191,9 @@
     @NonNull
     private final CompatUIStatusManager mCompatUIStatusManager;
 
+    @NonNull
+    private final IntPredicate mInDesktopModePredicate;
+
     public CompatUIController(@NonNull Context context,
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
@@ -202,7 +207,8 @@
             @NonNull CompatUIConfiguration compatUIConfiguration,
             @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
             @NonNull AccessibilityManager accessibilityManager,
-            @NonNull CompatUIStatusManager compatUIStatusManager) {
+            @NonNull CompatUIStatusManager compatUIStatusManager,
+            @NonNull IntPredicate isDesktopModeEnablePredicate) {
         mContext = context;
         mShellController = shellController;
         mDisplayController = displayController;
@@ -218,6 +224,7 @@
         mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis(
                 DISAPPEAR_DELAY_MS, flags);
         mCompatUIStatusManager = compatUIStatusManager;
+        mInDesktopModePredicate = isDesktopModeEnablePredicate;
         shellInit.addInitCallback(this::onInit, this);
     }
 
@@ -251,7 +258,9 @@
             updateActiveTaskInfo(taskInfo);
         }
 
-        if (taskInfo.configuration == null || taskListener == null) {
+        // We close all the Compat UI educations in case we're in desktop mode.
+        if (taskInfo.configuration == null || taskListener == null
+                || isInDesktopMode(taskInfo.displayId)) {
             // Null token means the current foreground activity is not in compatibility mode.
             removeLayouts(taskInfo.taskId);
             return;
@@ -350,7 +359,6 @@
         mOnInsetsChangedListeners.remove(displayId);
     }
 
-
     @Override
     public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
         updateDisplayLayout(displayId);
@@ -692,7 +700,8 @@
         mContext.startActivityAsUser(intent, userHandle);
     }
 
-    private void removeLayouts(int taskId) {
+    @VisibleForTesting
+    void removeLayouts(int taskId) {
         final CompatUIWindowManager compatLayout = mActiveCompatLayouts.get(taskId);
         if (compatLayout != null) {
             compatLayout.release();
@@ -822,7 +831,11 @@
      */
     static class CompatUIHintsState {
         boolean mHasShownSizeCompatHint;
-        boolean mHasShownCameraCompatHint;
         boolean mHasShownUserAspectRatioSettingsButtonHint;
     }
+
+    private boolean isInDesktopMode(int displayId) {
+        return Flags.skipCompatUiEducationInDesktopMode()
+                && mInDesktopModePredicate.test(displayId);
+    }
 }
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 8ce7837..17869e9 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
@@ -19,8 +19,6 @@
 import static android.view.WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP;
 import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
 
-import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.DESKTOP_WINDOWING_MODE;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.TaskInfo;
@@ -40,6 +38,7 @@
 import com.android.wm.shell.compatui.api.CompatUIEvent;
 import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 
 import java.util.function.Consumer;
 
@@ -83,7 +82,7 @@
         super(context, taskInfo, syncQueue, taskListener, displayLayout);
         mCallback = callback;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
-        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+        if (DesktopModeStatus.canEnterDesktopMode(context)
                 && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(context)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
@@ -139,7 +138,7 @@
             boolean canShow) {
         final boolean prevHasSizeCompat = mHasSizeCompat;
         mHasSizeCompat = taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat();
-        if (DESKTOP_WINDOWING_MODE.isEnabled(mContext)
+        if (DesktopModeStatus.canEnterDesktopMode(mContext)
                 && DesktopModeFlags.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)) {
             // Don't show the SCM button for freeform tasks
             mHasSizeCompat &= !taskInfo.isFreeform();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
index 9ee50ac..831b331 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt
@@ -16,28 +16,222 @@
 
 package com.android.wm.shell.compatui.api
 
-import android.util.Log
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PixelFormat
+import android.graphics.Point
+import android.os.Binder
+import android.view.IWindow
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.SurfaceSession
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.SyncTransactionQueue
 
 /**
  * The component created after a {@link CompatUISpec} definition
  */
 class CompatUIComponent(
     private val spec: CompatUISpec,
-    private val id: String
+    private val id: String,
+    private var context: Context,
+    private val state: CompatUIState,
+    private var compatUIInfo: CompatUIInfo,
+    private val syncQueue: SyncTransactionQueue,
+    private var displayLayout: DisplayLayout?
+) : WindowlessWindowManager(
+    compatUIInfo.taskInfo.configuration,
+    /* rootSurface */
+    null,
+    /* hostInputToken */
+    null
 ) {
 
+    private val tag
+        get() = "CompatUI {id = $id}"
+
+    private var leash: SurfaceControl? = null
+
+    private var layout: View? = null
+
+    /**
+     * Utility class for adding and releasing a View hierarchy for this [ ] to `mLeash`.
+     */
+    protected var viewHost: SurfaceControlViewHost? = null
+
+    override fun setConfiguration(configuration: Configuration?) {
+        super.setConfiguration(configuration)
+        configuration?.let {
+            context = context.createConfigurationContext(it)
+        }
+    }
+
     /**
      * Invoked every time a new CompatUIInfo comes from core
      * @param newInfo The new CompatUIInfo object
-     * @param sharedState The state shared between all the component
      */
-    fun update(newInfo: CompatUIInfo, state: CompatUIState) {
-        // TODO(b/322817374): To be removed when the implementation is provided.
-        Log.d("CompatUIComponent", "update() newInfo: $newInfo state:$state")
+    fun update(newInfo: CompatUIInfo) {
+        updateComponentState(newInfo, state.stateForComponent(id))
+        updateUI(state)
     }
 
     fun release() {
-        // TODO(b/322817374): To be removed when the implementation is provided.
-        Log.d("CompatUIComponent", "release()")
+        spec.log("$tag releasing.....")
+        // Implementation empty
+        // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+        layout?.visibility = View.GONE
+        layout = null
+        spec.layout.viewReleaser()
+        spec.log("$tag layout releaser invoked!")
+        viewHost?.release()
+        viewHost = null
+        leash?.run {
+            val localLeash: SurfaceControl = this
+            syncQueue.runInSync { t: SurfaceControl.Transaction ->
+                t.remove(
+                    localLeash
+                )
+            }
+            leash = null
+            spec.log("$tag leash removed")
+        }
+        spec.log("$tag released")
     }
-}
\ No newline at end of file
+
+    override fun getParentSurface(
+        window: IWindow,
+        attrs: WindowManager.LayoutParams
+    ): SurfaceControl? {
+        val className = javaClass.simpleName
+        val builder = SurfaceControl.Builder(SurfaceSession())
+                .setContainerLayer()
+                .setName(className + "Leash")
+                .setHidden(false)
+                .setCallsite("$className#attachToParentSurface")
+        attachToParentSurface(builder)
+        leash = builder.build()
+        initSurface(leash)
+        return leash
+    }
+
+    fun attachToParentSurface(builder: SurfaceControl.Builder) {
+        compatUIInfo.listener?.attachChildSurfaceToTask(compatUIInfo.taskInfo.taskId, builder)
+    }
+
+    fun initLayout(newCompatUIInfo: CompatUIInfo) {
+        compatUIInfo = newCompatUIInfo
+        spec.log("$tag updating...")
+        check(viewHost == null) { "A UI has already been created with this window manager." }
+        val componentState: CompatUIComponentState? = state.stateForComponent(id)
+        spec.log("$tag state: $componentState")
+        // We inflate the layout
+        layout = spec.layout.viewBuilder(context, compatUIInfo, componentState)
+        spec.log("$tag layout: $layout")
+        viewHost = createSurfaceViewHost().apply {
+            spec.log("$tag adding view $layout to host $this")
+            setView(layout!!, getWindowLayoutParams())
+        }
+        updateSurfacePosition()
+    }
+
+    /** Creates a [SurfaceControlViewHost] for this window manager.  */
+    fun createSurfaceViewHost(): SurfaceControlViewHost =
+        SurfaceControlViewHost(context, context.display, this, javaClass.simpleName)
+
+    fun relayout() {
+        spec.log("$tag relayout...")
+        viewHost?.run {
+            relayout(getWindowLayoutParams())
+            updateSurfacePosition()
+        }
+    }
+
+    protected fun updateSurfacePosition() {
+        spec.log("$tag updateSurfacePosition on layout $layout")
+        layout?.let {
+            updateSurfacePosition(
+                spec.layout.positionFactory(
+                    it,
+                    compatUIInfo,
+                    state.sharedState,
+                    state.stateForComponent(id)
+                )
+            )
+        }
+    }
+
+    protected fun getWindowLayoutParams(width: Int, height: Int): WindowManager.LayoutParams {
+        // Cannot be wrap_content as this determines the actual window size
+        val winParams =
+            WindowManager.LayoutParams(
+                width,
+                height,
+                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+                spec.layout.layoutParamFlags,
+                PixelFormat.TRANSLUCENT
+            )
+        winParams.token = Binder()
+        winParams.title = javaClass.simpleName + compatUIInfo.taskInfo.taskId
+        winParams.privateFlags =
+            winParams.privateFlags or (WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
+                    or WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+        spec.log("$tag getWindowLayoutParams $winParams")
+        return winParams
+    }
+
+    /** Gets the layout params.  */
+    protected fun getWindowLayoutParams(): WindowManager.LayoutParams =
+        layout?.run {
+            measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+            spec.log(
+                "$tag getWindowLayoutParams size: ${measuredWidth}x$measuredHeight"
+            )
+            return getWindowLayoutParams(measuredWidth, measuredHeight)
+        } ?: WindowManager.LayoutParams()
+
+    protected fun updateSurfacePosition(position: Point) {
+        spec.log("$tag updateSurfacePosition on leash $leash")
+        leash?.run {
+            syncQueue.runInSync { t: SurfaceControl.Transaction ->
+                if (!isValid) {
+                    spec.log("$tag The leash has been released.")
+                    return@runInSync
+                }
+                spec.log("$tag settings position  $position")
+                t.setPosition(this, position.x.toFloat(), position.y.toFloat())
+            }
+        }
+    }
+
+    private fun updateComponentState(
+        newInfo: CompatUIInfo,
+        componentState: CompatUIComponentState?
+    ) {
+        spec.log("$tag component state updating.... $componentState")
+        compatUIInfo = newInfo
+    }
+
+    private fun updateUI(state: CompatUIState) {
+        spec.log("$tag updating ui")
+        setConfiguration(compatUIInfo.taskInfo.configuration)
+        val componentState: CompatUIComponentState? = state.stateForComponent(id)
+        layout?.run {
+            spec.log("$tag viewBinder execution...")
+            spec.layout.viewBinder(this, compatUIInfo, state.sharedState, componentState)
+            relayout()
+        }
+    }
+
+    private fun initSurface(leash: SurfaceControl?) {
+        syncQueue.runInSync { t: SurfaceControl.Transaction ->
+            if (leash == null || !leash.isValid) {
+                spec.log("$tag The leash has been released.")
+                return@runInSync
+            }
+            t.setLayer(leash, spec.layout.zOrder)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentFactory.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentFactory.kt
new file mode 100644
index 0000000..55821ff
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentFactory.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.wm.shell.compatui.api
+
+/**
+ * Abstracts the component responsible for the creation of a component
+ */
+interface CompatUIComponentFactory {
+
+    fun create(
+        spec: CompatUISpec,
+        compId: String,
+        state: CompatUIState,
+        compatUIInfo: CompatUIInfo,
+    ): CompatUIComponent
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentState.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentState.kt
index dcaea00..ec21924 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentState.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponentState.kt
@@ -18,7 +18,6 @@
 
 /**
  * Abstraction of all the component specific state. Each
- * component can create its own state implementing this
- * tagging interface.
+ * component can create its own state implementing this interface.
  */
-interface CompatUIComponentState
\ No newline at end of file
+interface CompatUIComponentState
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUISpec.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUISpec.kt
index 022906c..de400f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUISpec.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUISpec.kt
@@ -16,6 +16,11 @@
 
 package com.android.wm.shell.compatui.api
 
+import android.content.Context
+import android.graphics.Point
+import android.view.View
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 
@@ -39,6 +44,28 @@
 )
 
 /**
+ * Layout configuration
+ */
+data class CompatUILayout(
+    val zOrder: Int = 0,
+    val layoutParamFlags: Int = FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCH_MODAL,
+    val viewBuilder: (Context, CompatUIInfo, CompatUIComponentState?) -> View,
+    val viewBinder: (
+        View,
+        CompatUIInfo,
+        CompatUISharedState,
+        CompatUIComponentState?
+    ) -> Unit = { _, _, _, _ -> },
+    val positionFactory: (
+        View,
+        CompatUIInfo,
+        CompatUISharedState,
+        CompatUIComponentState?
+    ) -> Point,
+    val viewReleaser: () -> Unit = {}
+)
+
+/**
  * Describes each compat ui component to the framework.
  */
 class CompatUISpec(
@@ -47,5 +74,7 @@
     // unique component identifier in the system.
     val name: String,
     // The lifecycle definition
-    val lifecycle: CompatUILifecyclePredicates
+    val lifecycle: CompatUILifecyclePredicates,
+    // The layout definition
+    val layout: CompatUILayout
 )
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/components/RestartButtonSpec.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/components/RestartButtonSpec.kt
new file mode 100644
index 0000000..e18cc0e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/components/RestartButtonSpec.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.compatui.components
+
+import android.annotation.SuppressLint
+import android.graphics.Point
+import android.view.LayoutInflater
+import android.view.View
+import android.window.TaskConstants
+import com.android.wm.shell.R
+import com.android.wm.shell.compatui.api.CompatUILayout
+import com.android.wm.shell.compatui.api.CompatUILifecyclePredicates
+import com.android.wm.shell.compatui.api.CompatUISpec
+
+/**
+ * CompatUISpec for the Restart Button
+ */
+@SuppressLint("InflateParams")
+val RestartButtonSpec = CompatUISpec(
+    name = "restartButton",
+    lifecycle = CompatUILifecyclePredicates(
+        creationPredicate = { info, _ ->
+            info.taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat
+        },
+        removalPredicate = { info, _, _ ->
+            !info.taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat
+        }
+    ),
+    layout = CompatUILayout(
+        zOrder = TaskConstants.TASK_CHILD_LAYER_COMPAT_UI + 10,
+        viewBuilder = { ctx, _, _ ->
+            LayoutInflater.from(ctx).inflate(
+                R.layout.compat_ui_restart_button_layout,
+                null
+            )
+        },
+        viewBinder = { view, _, _, _ ->
+            view.visibility = View.VISIBLE
+            view.findViewById<View>(R.id.size_compat_restart_button)?.visibility = View.VISIBLE
+        },
+        // TODO(b/360288344): Calculate right position from stable bounds
+        positionFactory = { _, _, _, _ -> Point(500, 500) }
+    )
+)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIComponentFactory.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIComponentFactory.kt
new file mode 100644
index 0000000..4eea6a3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIComponentFactory.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.wm.shell.compatui.impl
+
+import android.content.Context
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIComponent
+import com.android.wm.shell.compatui.api.CompatUIComponentFactory
+import com.android.wm.shell.compatui.api.CompatUIInfo
+import com.android.wm.shell.compatui.api.CompatUISpec
+import com.android.wm.shell.compatui.api.CompatUIState
+
+/**
+ * Default {@link CompatUIComponentFactory } implementation
+ */
+class DefaultCompatUIComponentFactory(
+    private val context: Context,
+    private val syncQueue: SyncTransactionQueue,
+    private val displayController: DisplayController
+) : CompatUIComponentFactory {
+    override fun create(
+        spec: CompatUISpec,
+        compId: String,
+        state: CompatUIState,
+        compatUIInfo: CompatUIInfo
+    ): CompatUIComponent =
+        CompatUIComponent(
+            spec,
+            compId,
+            context,
+            state,
+            compatUIInfo,
+            syncQueue,
+            displayController.getDisplayLayout(compatUIInfo.taskInfo.displayId)
+        )
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
index a7d1b42..02db85a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandler.kt
@@ -16,7 +16,8 @@
 
 package com.android.wm.shell.compatui.impl
 
-import com.android.wm.shell.compatui.api.CompatUIComponent
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.compatui.api.CompatUIComponentFactory
 import com.android.wm.shell.compatui.api.CompatUIComponentIdGenerator
 import com.android.wm.shell.compatui.api.CompatUIEvent
 import com.android.wm.shell.compatui.api.CompatUIHandler
@@ -24,7 +25,6 @@
 import com.android.wm.shell.compatui.api.CompatUIRepository
 import com.android.wm.shell.compatui.api.CompatUIState
 import java.util.function.Consumer
-import java.util.function.IntSupplier
 
 /**
  * Default implementation of {@link CompatUIHandler} to handle CompatUI components
@@ -32,7 +32,9 @@
 class DefaultCompatUIHandler(
     private val compatUIRepository: CompatUIRepository,
     private val compatUIState: CompatUIState,
-    private val componentIdGenerator: CompatUIComponentIdGenerator
+    private val componentIdGenerator: CompatUIComponentIdGenerator,
+    private val componentFactory: CompatUIComponentFactory,
+    private val executor: ShellExecutor
 ) : CompatUIHandler {
 
     private var compatUIEventSender: Consumer<CompatUIEvent>? = null
@@ -41,23 +43,36 @@
         compatUIRepository.iterateOn { spec ->
             // We get the identifier for the component depending on the task and spec
             val componentId = componentIdGenerator.generateId(compatUIInfo, spec)
-            // We check in the state if the component already exists
-            var comp = compatUIState.getUIComponent(componentId)
-            if (comp == null) {
+            spec.log("Evaluating component $componentId")
+            // We check in the state if the component does not yet exist
+            var component = compatUIState.getUIComponent(componentId)
+            if (component == null) {
+                spec.log("Component $componentId not present")
                 // We evaluate the predicate
                 if (spec.lifecycle.creationPredicate(compatUIInfo, compatUIState.sharedState)) {
+                    spec.log("Component $componentId should be created")
                     // We create the component and store in the
                     // global state
-                    comp = CompatUIComponent(spec, componentId)
+                    component =
+                        componentFactory.create(spec, componentId, compatUIState, compatUIInfo)
+                    spec.log("Component $componentId created $component")
                     // We initialize the state for the component
                     val compState = spec.lifecycle.stateBuilder(
                         compatUIInfo,
                         compatUIState.sharedState
                     )
-                    compatUIState.registerUIComponent(componentId, comp, compState)
+                    spec.log("Component $componentId initial state $compState")
+                    compatUIState.registerUIComponent(componentId, component, compState)
+                    spec.log("Component $componentId registered")
+                    // We initialize the layout for the component
+                    component.initLayout(compatUIInfo)
+                    spec.log("Component $componentId layout created")
                     // Now we can invoke the update passing the shared state and
                     // the state specific to the component
-                    comp.update(compatUIInfo, compatUIState)
+                    executor.execute {
+                        component.update(compatUIInfo)
+                        spec.log("Component $componentId updated with $compatUIInfo")
+                    }
                 }
             } else {
                 // The component is present. We check if we need to remove it
@@ -66,13 +81,18 @@
                         compatUIState.sharedState,
                         compatUIState.stateForComponent(componentId)
                     )) {
+                    spec.log("Component $componentId should be removed")
                     // We clean the component
-                    comp.release()
-                    // We remove the component
+                    component.release()
+                    spec.log("Component $componentId released")
                     compatUIState.unregisterUIComponent(componentId)
+                    spec.log("Component $componentId removed from registry")
                 } else {
-                    // The component exists so we need to invoke the update methods
-                    comp.update(compatUIInfo, compatUIState)
+                    executor.execute {
+                        // The component exists so we need to invoke the update methods
+                        component.update(compatUIInfo)
+                        spec.log("Component $componentId updated with $compatUIInfo")
+                    }
                 }
             }
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 0110937..33e4fd8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -30,9 +30,9 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.dagger.pip.TvPipModule;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 04cd225ea4a..4adea23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -62,7 +62,6 @@
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TabletopModeController;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
 import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -77,10 +76,13 @@
 import com.android.wm.shell.compatui.CompatUIController;
 import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
 import com.android.wm.shell.compatui.CompatUIStatusManager;
+import com.android.wm.shell.compatui.api.CompatUIComponentFactory;
 import com.android.wm.shell.compatui.api.CompatUIComponentIdGenerator;
 import com.android.wm.shell.compatui.api.CompatUIHandler;
 import com.android.wm.shell.compatui.api.CompatUIRepository;
 import com.android.wm.shell.compatui.api.CompatUIState;
+import com.android.wm.shell.compatui.components.RestartButtonSpecKt;
+import com.android.wm.shell.compatui.impl.DefaultCompatUIComponentFactory;
 import com.android.wm.shell.compatui.impl.DefaultCompatUIHandler;
 import com.android.wm.shell.compatui.impl.DefaultCompatUIRepository;
 import com.android.wm.shell.compatui.impl.DefaultComponentIdGenerator;
@@ -102,6 +104,7 @@
 import com.android.wm.shell.recents.RecentsTransitionHandler;
 import com.android.wm.shell.recents.TaskStackTransitionObserver;
 import com.android.wm.shell.shared.ShellTransitions;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -134,6 +137,7 @@
 import dagger.Provides;
 
 import java.util.Optional;
+import java.util.function.IntPredicate;
 
 /**
  * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
@@ -144,7 +148,11 @@
  * dependencies that are device/form factor SystemUI implementation specific should go into their
  * respective modules (ie. {@link WMShellModule} for handheld, {@link TvWMShellModule} for tv, etc.)
  */
-@Module(includes = WMShellConcurrencyModule.class)
+@Module(
+        includes = {
+                WMShellConcurrencyModule.class,
+                WMShellCoroutinesModule.class
+        })
 public abstract class WMShellBaseModule {
 
     //
@@ -258,16 +266,23 @@
             Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler,
             Lazy<AccessibilityManager> accessibilityManager,
             CompatUIRepository compatUIRepository,
+            Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
             @NonNull CompatUIState compatUIState,
             @NonNull CompatUIComponentIdGenerator componentIdGenerator,
+            @NonNull CompatUIComponentFactory compatUIComponentFactory,
             CompatUIStatusManager compatUIStatusManager) {
         if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) {
             return Optional.empty();
         }
         if (Flags.appCompatUiFramework()) {
-            return Optional.of(new DefaultCompatUIHandler(compatUIRepository, compatUIState,
-                    componentIdGenerator));
+            return Optional.of(
+                    new DefaultCompatUIHandler(compatUIRepository, compatUIState,
+                            componentIdGenerator, compatUIComponentFactory, mainExecutor));
         }
+        final IntPredicate inDesktopModePredicate =
+                desktopModeTaskRepository.<IntPredicate>map(modeTaskRepository -> displayId ->
+                        modeTaskRepository.getVisibleTaskCount(displayId) > 0)
+                            .orElseGet(() -> displayId -> false);
         return Optional.of(
                 new CompatUIController(
                         context,
@@ -283,7 +298,8 @@
                         compatUIConfiguration.get(),
                         compatUIShellCommandHandler.get(),
                         accessibilityManager.get(),
-                        compatUIStatusManager));
+                        compatUIStatusManager,
+                        inDesktopModePredicate));
     }
 
     @WMSingleton
@@ -308,6 +324,15 @@
 
     @WMSingleton
     @Provides
+    static CompatUIComponentFactory provideCompatUIComponentFactory(
+            @NonNull Context context,
+            @NonNull SyncTransactionQueue syncQueue,
+            @NonNull DisplayController displayController) {
+        return new DefaultCompatUIComponentFactory(context, syncQueue, displayController);
+    }
+
+    @WMSingleton
+    @Provides
     static CompatUIComponentIdGenerator provideCompatUIComponentIdGenerator() {
         return new DefaultComponentIdGenerator();
     }
@@ -315,7 +340,10 @@
     @WMSingleton
     @Provides
     static CompatUIRepository provideCompatUIRepository() {
-        return new DefaultCompatUIRepository();
+        // TODO(b/360288344) Integrate Dagger Multibinding
+        final CompatUIRepository repository = new DefaultCompatUIRepository();
+        repository.addSpec(RestartButtonSpecKt.getRestartButtonSpec());
+        return repository;
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
new file mode 100644
index 0000000..cc47dbb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellCoroutinesModule.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.dagger
+
+import android.os.Handler
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.annotations.ShellBackgroundThread
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import dagger.Module
+import dagger.Provides
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainCoroutineDispatcher
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.android.asCoroutineDispatcher
+import kotlinx.coroutines.asCoroutineDispatcher
+
+/**
+ * Providers for various WmShell-specific coroutines-related constructs.
+ *
+ * Providers of [MainCoroutineDispatcher] intentionally creates the dispatcher with a [Handler]
+ * backing it instead of a [ShellExecutor] because [ShellExecutor.asCoroutineDispatcher] will
+ * create a [CoroutineDispatcher] whose [CoroutineDispatcher.isDispatchNeeded] is effectively never
+ * dispatching. This is because even if dispatched, the backing [ShellExecutor.execute] always runs
+ * the [Runnable] immediately if called from the same thread, whereas
+ * [Handler.asCoroutineDispatcher] will create a [MainCoroutineDispatcher] that correctly
+ * dispatches (queues) when [CoroutineDispatcher.isDispatchNeeded] is true using [Handler.post].
+ * For callers that do need a non-dispatching version, [MainCoroutineDispatcher.immediate] is
+ * available.
+ */
+@Module
+class WMShellCoroutinesModule {
+  @Provides
+  @ShellMainThread
+  fun provideMainDispatcher(
+    @ShellMainThread mainHandler: Handler
+  ): MainCoroutineDispatcher = mainHandler.asCoroutineDispatcher()
+
+  @Provides
+  @ShellBackgroundThread
+  fun provideBackgroundDispatcher(
+      @ShellBackgroundThread backgroundHandler: Handler
+  ): MainCoroutineDispatcher = backgroundHandler.asCoroutineDispatcher()
+
+  @Provides
+  @WMSingleton
+  @ShellMainThread
+  fun provideApplicationScope(
+      @ShellMainThread applicationDispatcher: MainCoroutineDispatcher,
+  ): CoroutineScope = CoroutineScope(applicationDispatcher)
+
+  @Provides
+  @WMSingleton
+  @ShellBackgroundThread
+  fun provideBackgroundCoroutineScope(
+      @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher,
+  ): CoroutineScope = CoroutineScope(backgroundDispatcher)
+
+  @Provides
+  @WMSingleton
+  @ShellBackgroundThread
+  fun provideBackgroundCoroutineContext(
+      @ShellBackgroundThread backgroundDispatcher: MainCoroutineDispatcher
+  ): CoroutineContext = backgroundDispatcher + SupervisorJob()
+}
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 b8b62a7..02ecfd9 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
@@ -56,10 +56,11 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
 import com.android.wm.shell.dagger.pip.PipModule;
 import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
+import com.android.wm.shell.desktopmode.DesktopModeDragAndDropTransitionHandler;
 import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
 import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -72,6 +73,9 @@
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
 import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
+import com.android.wm.shell.desktopmode.education.AppHandleEducationFilter;
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.draganddrop.GlobalDragListener;
 import com.android.wm.shell.freeform.FreeformComponents;
@@ -83,6 +87,7 @@
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.annotations.ShellAnimationThread;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -114,6 +119,8 @@
 import dagger.Module;
 import dagger.Provides;
 
+import kotlinx.coroutines.CoroutineScope;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -163,8 +170,10 @@
             BubbleLogger logger,
             BubblePositioner positioner,
             BubbleEducationController educationController,
-            @ShellMainThread ShellExecutor mainExecutor) {
-        return new BubbleData(context, logger, positioner, educationController, mainExecutor);
+            @ShellMainThread ShellExecutor mainExecutor,
+            @ShellBackgroundThread ShellExecutor bgExecutor) {
+        return new BubbleData(context, logger, positioner, educationController, mainExecutor,
+                bgExecutor);
     }
 
     // Note: Handler needed for LauncherApps.register
@@ -197,7 +206,7 @@
             IWindowManager wmService) {
         return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
                 null /* synchronizer */, floatingContentCoordinator,
-                new BubbleDataRepository(launcherApps, mainExecutor,
+                new BubbleDataRepository(launcherApps, mainExecutor, bgExecutor,
                         new BubblePersistentRepository(context)),
                 statusBarService, windowManager, windowManagerShellWrapper, userManager,
                 launcherApps, logger, taskStackListener, organizer, positioner, displayController,
@@ -231,7 +240,9 @@
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor,
             AppToWebGenericLinksParser genericLinksParser,
-            MultiInstanceHelper multiInstanceHelper) {
+            MultiInstanceHelper multiInstanceHelper,
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
         if (DesktopModeStatus.canEnterDesktopMode(context)) {
             return new DesktopModeWindowDecorViewModel(
                     context,
@@ -252,7 +263,9 @@
                     rootTaskDisplayAreaOrganizer,
                     interactionJankMonitor,
                     genericLinksParser,
-                    multiInstanceHelper);
+                    multiInstanceHelper,
+                    desktopTasksLimiter,
+                    desktopActivityOrientationHandler);
         }
         return new CaptionWindowDecorViewModel(
                 context,
@@ -300,6 +313,7 @@
             ShellInit shellInit,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorViewModel) {
         // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
         //                    override for this controller from the base module
@@ -307,7 +321,7 @@
                 ? shellInit
                 : null;
         return new FreeformTaskListener(context, init, shellTaskOrganizer,
-                desktopModeTaskRepository, windowDecorViewModel);
+                desktopModeTaskRepository, launchAdjacentController, windowDecorViewModel);
     }
 
     @WMSingleton
@@ -319,9 +333,19 @@
             WindowDecorViewModel windowDecorViewModel,
             DisplayController displayController,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellAnimationThread ShellExecutor animExecutor) {
-        return new FreeformTaskTransitionHandler(shellInit, transitions, context,
-                windowDecorViewModel, displayController, mainExecutor, animExecutor);
+            @ShellAnimationThread ShellExecutor animExecutor,
+            @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
+            InteractionJankMonitor interactionJankMonitor) {
+        return new FreeformTaskTransitionHandler(
+                shellInit,
+                transitions,
+                context,
+                windowDecorViewModel,
+                displayController,
+                mainExecutor,
+                animExecutor,
+                desktopModeTaskRepository,
+                interactionJankMonitor);
     }
 
     @WMSingleton
@@ -545,6 +569,7 @@
             ReturnToDragStartAnimator returnToDragStartAnimator,
             EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
             ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
+            DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler,
             ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
             DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
@@ -560,7 +585,8 @@
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 dragAndDropController, transitions, keyguardManager,
                 returnToDragStartAnimator, enterDesktopTransitionHandler,
-                exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
+                exitDesktopTransitionHandler, desktopModeDragAndDropTransitionHandler,
+                toggleResizeDesktopTaskTransitionHandler,
                 dragToDesktopTransitionHandler, desktopModeTaskRepository,
                 desktopModeLoggerTransitionObserver, launchAdjacentController,
                 recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter,
@@ -595,8 +621,8 @@
     @WMSingleton
     @Provides
     static ReturnToDragStartAnimator provideReturnToDragStartAnimator(
-            InteractionJankMonitor interactionJankMonitor) {
-        return new ReturnToDragStartAnimator(interactionJankMonitor);
+            Context context, InteractionJankMonitor interactionJankMonitor) {
+        return new ReturnToDragStartAnimator(context, interactionJankMonitor);
     }
 
 
@@ -642,6 +668,14 @@
 
     @WMSingleton
     @Provides
+    static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler(
+            Transitions transitions
+    ) {
+        return new DesktopModeDragAndDropTransitionHandler(transitions);
+    }
+
+    @WMSingleton
+    @Provides
     @DynamicOverride
     static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
         return new DesktopModeTaskRepository();
@@ -649,6 +683,24 @@
 
     @WMSingleton
     @Provides
+    static Optional<DesktopActivityOrientationChangeHandler> provideActivityOrientationHandler(
+            Context context,
+            ShellInit shellInit,
+            ShellTaskOrganizer shellTaskOrganizer,
+            TaskStackListenerImpl taskStackListener,
+            ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
+            @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository
+    ) {
+        if (DesktopModeStatus.canEnterDesktopMode(context)) {
+            return Optional.of(new DesktopActivityOrientationChangeHandler(
+                    context, shellInit, shellTaskOrganizer, taskStackListener,
+                    toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository));
+        }
+        return Optional.empty();
+    }
+
+    @WMSingleton
+    @Provides
     static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver(
             Context context,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@@ -679,6 +731,32 @@
         return new DesktopModeEventLogger();
     }
 
+    @WMSingleton
+    @Provides
+    static AppHandleEducationDatastoreRepository provideAppHandleEducationDatastoreRepository(
+            Context context) {
+        return new AppHandleEducationDatastoreRepository(context);
+    }
+
+    @WMSingleton
+    @Provides
+    static AppHandleEducationFilter provideAppHandleEducationFilter(
+            Context context,
+            AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository) {
+        return new AppHandleEducationFilter(context, appHandleEducationDatastoreRepository);
+    }
+
+    @WMSingleton
+    @Provides
+    static AppHandleEducationController provideAppHandleEducationController(
+            AppHandleEducationFilter appHandleEducationFilter,
+            ShellTaskOrganizer shellTaskOrganizer,
+            AppHandleEducationDatastoreRepository appHandleEducationDatastoreRepository,
+            @ShellMainThread CoroutineScope applicationScope) {
+        return new AppHandleEducationController(appHandleEducationFilter,
+                shellTaskOrganizer, appHandleEducationDatastoreRepository, applicationScope);
+    }
+
     //
     // Drag and drop
     //
@@ -720,7 +798,8 @@
     @Provides
     static Object provideIndependentShellComponentsToCreate(
             DragAndDropController dragAndDropController,
-            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional
+            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional,
+            AppHandleEducationController appHandleEducationController
     ) {
         return new Object();
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 06c1e68..3464fef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -42,9 +42,11 @@
 import com.android.wm.shell.pip2.phone.PipController;
 import com.android.wm.shell.pip2.phone.PipMotionHelper;
 import com.android.wm.shell.pip2.phone.PipScheduler;
+import com.android.wm.shell.pip2.phone.PipTaskListener;
 import com.android.wm.shell.pip2.phone.PipTouchHandler;
 import com.android.wm.shell.pip2.phone.PipTransition;
 import com.android.wm.shell.pip2.phone.PipTransitionState;
+import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -72,11 +74,13 @@
             PipBoundsAlgorithm pipBoundsAlgorithm,
             Optional<PipController> pipController,
             PipTouchHandler pipTouchHandler,
+            PipTaskListener pipTaskListener,
             @NonNull PipScheduler pipScheduler,
-            @NonNull PipTransitionState pipStackListenerController) {
+            @NonNull PipTransitionState pipStackListenerController,
+            @NonNull PipUiStateChangeController pipUiStateChangeController) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
-                pipBoundsState, null, pipBoundsAlgorithm, pipScheduler,
-                pipStackListenerController);
+                pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
+                pipScheduler, pipStackListenerController, pipUiStateChangeController);
     }
 
     @WMSingleton
@@ -121,9 +125,11 @@
     @Provides
     static PipScheduler providePipScheduler(Context context,
             PipBoundsState pipBoundsState,
+            PhonePipMenuController pipMenuController,
             @ShellMainThread ShellExecutor mainExecutor,
             PipTransitionState pipTransitionState) {
-        return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState);
+        return new PipScheduler(context, pipBoundsState, pipMenuController,
+                mainExecutor, pipTransitionState);
     }
 
     @WMSingleton
@@ -181,4 +187,24 @@
     static PipTransitionState providePipTransitionState(@ShellMainThread Handler handler) {
         return new PipTransitionState(handler);
     }
+
+    @WMSingleton
+    @Provides
+    static PipUiStateChangeController providePipUiStateChangeController(
+            PipTransitionState pipTransitionState) {
+        return new PipUiStateChangeController(pipTransitionState);
+    }
+
+    @WMSingleton
+    @Provides
+    static PipTaskListener providePipTaskListener(Context context,
+            ShellTaskOrganizer shellTaskOrganizer,
+            PipTransitionState pipTransitionState,
+            PipScheduler pipScheduler,
+            PipBoundsState pipBoundsState,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new PipTaskListener(context, shellTaskOrganizer, pipTransitionState,
+                pipScheduler, pipBoundsState, pipBoundsAlgorithm, mainExecutor);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
new file mode 100644
index 0000000..59e0068
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
@@ -0,0 +1,112 @@
+/*
+ * 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
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.ScreenOrientation
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.graphics.Rect
+import android.util.Size
+import android.window.WindowContainerTransaction
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.TaskStackListenerCallback
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+
+/** Handles task resizing to respect orientation change of non-resizeable activities in desktop. */
+class DesktopActivityOrientationChangeHandler(
+    context: Context,
+    shellInit: ShellInit,
+    private val shellTaskOrganizer: ShellTaskOrganizer,
+    private val taskStackListener: TaskStackListenerImpl,
+    private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler,
+    private val taskRepository: DesktopModeTaskRepository,
+) {
+
+    init {
+        if (DesktopModeStatus.canEnterDesktopMode(context)) {
+            shellInit.addInitCallback({ onInit() }, this)
+        }
+    }
+
+    private fun onInit() {
+        taskStackListener.addListener(object : TaskStackListenerCallback {
+            override fun onActivityRequestedOrientationChanged(
+                taskId: Int,
+                @ScreenOrientation requestedOrientation: Int
+            ) {
+                // Handle requested screen orientation changes at runtime.
+                handleActivityOrientationChange(taskId, requestedOrientation)
+            }
+        })
+    }
+
+    /**
+     * Triggered with onTaskInfoChanged to handle:
+     * * New activity launching from same task with different orientation
+     * * Top activity closing in same task with different orientation to previous activity
+     */
+    fun handleActivityOrientationChange(oldTask: RunningTaskInfo, newTask: RunningTaskInfo) {
+        val newTopActivityInfo = newTask.topActivityInfo ?: return
+        val oldTopActivityInfo = oldTask.topActivityInfo ?: return
+        // Check if screen orientation is different from old task info so there is no duplicated
+        // calls to handle runtime requested orientation changes.
+        if (oldTopActivityInfo.screenOrientation != newTopActivityInfo.screenOrientation) {
+            handleActivityOrientationChange(newTask.taskId, newTopActivityInfo.screenOrientation)
+        }
+    }
+
+    private fun handleActivityOrientationChange(
+        taskId: Int,
+        @ScreenOrientation requestedOrientation: Int
+    ) {
+        if (!Flags.respectOrientationChangeForUnresizeable()) return
+        val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return
+        if (!isDesktopModeShowing(task.displayId) || !task.isFreeform || task.isResizeable) return
+
+        val taskBounds = task.configuration.windowConfiguration.bounds
+        val taskHeight = taskBounds.height()
+        val taskWidth = taskBounds.width()
+        if (taskWidth == taskHeight) return
+        val orientation =
+            if (taskWidth > taskHeight) ORIENTATION_LANDSCAPE else ORIENTATION_PORTRAIT
+
+        // Non-resizeable activity requested opposite orientation.
+        if (orientation == ORIENTATION_PORTRAIT
+                && ActivityInfo.isFixedOrientationLandscape(requestedOrientation)
+            || orientation == ORIENTATION_LANDSCAPE
+                && ActivityInfo.isFixedOrientationPortrait(requestedOrientation)) {
+
+            val finalSize = Size(taskHeight, taskWidth)
+            // Use the center x as the resizing anchor point.
+            val left = taskBounds.centerX() - finalSize.width / 2
+            val right = left + finalSize.width
+            val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height)
+
+            val wct = WindowContainerTransaction().setBounds(task.token, finalBounds)
+            resizeHandler.startTransition(wct)
+        }
+    }
+
+    private fun isDesktopModeShowing(displayId: Int): Boolean =
+        taskRepository.getVisibleTaskCount(displayId) > 0
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index 31c8f1e..cca7500 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -18,8 +18,8 @@
 
 import android.graphics.Region;
 
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt
new file mode 100644
index 0000000..a7a4a10
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt
@@ -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.wm.shell.desktopmode
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_OPEN
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
+
+/**
+ * Transition handler for drag-and-drop (i.e., tab tear) transitions that occur in desktop mode.
+ */
+class DesktopModeDragAndDropTransitionHandler(private val transitions: Transitions) :
+    Transitions.TransitionHandler {
+    private val pendingTransitionTokens: MutableList<IBinder> = mutableListOf()
+
+    /**
+     * Begin a transition when a [android.app.PendingIntent] is dropped without a window to
+     * accept it.
+     */
+    fun handleDropEvent(wct: WindowContainerTransaction): IBinder {
+        val token = transitions.startTransition(TRANSIT_OPEN, wct, this)
+        pendingTransitionTokens.add(token)
+        return token
+    }
+
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: TransitionFinishCallback
+    ): Boolean {
+        if (!pendingTransitionTokens.contains(transition)) return false
+        val change = findRelevantChange(info)
+        val leash = change.leash
+        val endBounds = change.endAbsBounds
+        startTransaction.hide(leash)
+            .setWindowCrop(leash, endBounds.width(), endBounds.height())
+            .apply()
+        val animator = ValueAnimator()
+        animator.setFloatValues(0f, 1f)
+        animator.setDuration(FADE_IN_ANIMATION_DURATION)
+        val t = SurfaceControl.Transaction()
+        animator.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationStart(animation: Animator) {
+                t.show(leash)
+                t.apply()
+            }
+
+            override fun onAnimationEnd(animation: Animator) {
+                finishCallback.onTransitionFinished(null)
+            }
+        })
+        animator.addUpdateListener { animation: ValueAnimator ->
+            t.setAlpha(leash, animation.animatedFraction)
+            t.apply()
+        }
+        animator.start()
+        pendingTransitionTokens.remove(transition)
+        return true
+    }
+
+    private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
+        val matchingChanges =
+            info.changes.filter { c ->
+                isValidTaskChange(c) && c.mode == TRANSIT_OPEN
+            }
+        if (matchingChanges.size != 1) {
+            throw IllegalStateException(
+                "Expected 1 relevant change but found: ${matchingChanges.size}"
+            )
+        }
+        return matchingChanges.first()
+    }
+
+    private fun isValidTaskChange(change: TransitionInfo.Change): Boolean {
+        return change.taskInfo != null && change.taskInfo?.taskId != -1
+    }
+
+    override fun handleRequest(
+        transition: IBinder,
+        request: TransitionRequestInfo
+    ): WindowContainerTransaction? {
+        return null
+    }
+
+    companion object {
+        const val FADE_IN_ANIMATION_DURATION = 300L
+    }
+}
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 400882a..02cbe01 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.desktopmode
 
+import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.protolog.ProtoLog
 import com.android.internal.util.FrameworkStatsLog
 import com.android.wm.shell.protolog.ShellProtoLogGroup
@@ -73,25 +74,9 @@
             sessionId,
             taskUpdate.instanceId
         )
-        FrameworkStatsLog.write(
-            DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
-            /* task_event */
+        logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
-            /* instance_id */
-            taskUpdate.instanceId,
-            /* uid */
-            taskUpdate.uid,
-            /* task_height */
-            taskUpdate.taskHeight,
-            /* task_width */
-            taskUpdate.taskWidth,
-            /* task_x */
-            taskUpdate.taskX,
-            /* task_y */
-            taskUpdate.taskY,
-            /* session_id */
-            sessionId
-        )
+            sessionId, taskUpdate)
     }
 
     /**
@@ -105,25 +90,9 @@
             sessionId,
             taskUpdate.instanceId
         )
-        FrameworkStatsLog.write(
-            DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
-            /* task_event */
+        logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
-            /* instance_id */
-            taskUpdate.instanceId,
-            /* uid */
-            taskUpdate.uid,
-            /* task_height */
-            taskUpdate.taskHeight,
-            /* task_width */
-            taskUpdate.taskWidth,
-            /* task_x */
-            taskUpdate.taskX,
-            /* task_y */
-            taskUpdate.taskY,
-            /* session_id */
-            sessionId
-        )
+            sessionId, taskUpdate)
     }
 
     /**
@@ -137,10 +106,16 @@
             sessionId,
             taskUpdate.instanceId
         )
+        logTaskUpdate(
+            FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+            sessionId, taskUpdate)
+    }
+
+    private fun logTaskUpdate(taskEvent: Int, sessionId: Int, taskUpdate: TaskUpdate) {
         FrameworkStatsLog.write(
             DESKTOP_MODE_TASK_UPDATE_ATOM_ID,
             /* task_event */
-            FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
+            taskEvent,
             /* instance_id */
             taskUpdate.instanceId,
             /* uid */
@@ -154,7 +129,11 @@
             /* task_y */
             taskUpdate.taskY,
             /* session_id */
-            sessionId
+            sessionId,
+            taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON,
+            taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON,
+            /* visible_task_count */
+            taskUpdate.visibleTaskCount
         )
     }
 
@@ -168,6 +147,8 @@
          * @property taskWidth width of the task in px
          * @property taskX x-coordinate of the top-left corner
          * @property taskY y-coordinate of the top-left corner
+         * @property minimizeReason the reason the task was minimized
+         * @property unminimizeEvent the reason the task was unminimized
          *
          */
         data class TaskUpdate(
@@ -177,8 +158,53 @@
             val taskWidth: Int,
             val taskX: Int,
             val taskY: Int,
+            val minimizeReason: MinimizeReason? = null,
+            val unminimizeReason: UnminimizeReason? = null,
+            val visibleTaskCount: Int,
         )
 
+        // Default value used when the task was not minimized.
+        @VisibleForTesting
+        const val UNSET_MINIMIZE_REASON =
+            FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__UNSET_MINIMIZE
+
+        /** The reason a task was minimized. */
+        enum class MinimizeReason (val reason: Int) {
+            TASK_LIMIT(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
+            ),
+            MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
+            ),
+        }
+
+        // Default value used when the task was not unminimized.
+        @VisibleForTesting
+        const val UNSET_UNMINIMIZE_REASON =
+            FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNSET_UNMINIMIZE
+
+        /** The reason a task was unminimized. */
+        enum class UnminimizeReason (val reason: Int) {
+            UNKNOWN(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_UNKNOWN
+            ),
+            TASKBAR_TAP(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_TASKBAR_TAP
+            ),
+            ALT_TAB(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_ALT_TAB
+            ),
+            TASK_LAUNCH(
+                FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__UNMINIMIZE_REASON__UNMINIMIZE_TASK_LAUNCH
+            ),
+        }
+
         /**
          * Enum EnterReason mapped to the EnterReason definition in
          * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto
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 73aa7ce..0637474 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
@@ -22,6 +22,8 @@
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.content.Context
 import android.os.IBinder
+import android.os.SystemProperties
+import android.os.Trace
 import android.util.SparseArray
 import android.view.SurfaceControl
 import android.view.WindowManager
@@ -83,6 +85,10 @@
     // Caching whether the previous transition was exit to overview.
     private var wasPreviousTransitionExitToOverview: Boolean = false
 
+    // Caching whether the previous transition was exit due to screen off. This helps check if a
+    // following enter reason could be Screen On
+    private var wasPreviousTransitionExitByScreenOff: Boolean = false
+
     // The instanceId for the current logging session
     private var loggerInstanceId: InstanceId? = null
 
@@ -288,44 +294,66 @@
         postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
     ) {
         postTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
-            val currentTaskUpdate = buildTaskUpdateForTask(taskInfo)
+            val currentTaskUpdate = buildTaskUpdateForTask(taskInfo,
+                postTransitionVisibleFreeformTasks.size())
             val previousTaskInfo = preTransitionVisibleFreeformTasks[taskId]
             when {
                 // new tasks added
-                previousTaskInfo == null ->
+                previousTaskInfo == null -> {
                     desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate)
+                    Trace.setCounter(
+                        Trace.TRACE_TAG_WINDOW_MANAGER,
+                        VISIBLE_TASKS_COUNTER_NAME,
+                        postTransitionVisibleFreeformTasks.size().toLong()
+                    )
+                    SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+                        postTransitionVisibleFreeformTasks.size().toString())
+                }
                 // old tasks that were resized or repositioned
                 // TODO(b/347935387): Log changes only once they are stable.
-                buildTaskUpdateForTask(previousTaskInfo) != currentTaskUpdate ->
-                    desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
+                buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size())
+                        != currentTaskUpdate ->
+                            desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate)
             }
         }
 
         // find old tasks that were removed
         preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
             if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
-                desktopModeEventLogger.logTaskRemoved(sessionId, buildTaskUpdateForTask(taskInfo))
+                desktopModeEventLogger.logTaskRemoved(sessionId,
+                    buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size()))
+                Trace.setCounter(
+                    Trace.TRACE_TAG_WINDOW_MANAGER,
+                    VISIBLE_TASKS_COUNTER_NAME,
+                    postTransitionVisibleFreeformTasks.size().toLong()
+                )
+                SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+                    postTransitionVisibleFreeformTasks.size().toString())
             }
         }
     }
 
-    private fun buildTaskUpdateForTask(taskInfo: TaskInfo): TaskUpdate {
+    private fun buildTaskUpdateForTask(taskInfo: TaskInfo, visibleTasks: Int): TaskUpdate {
         val screenBounds = taskInfo.configuration.windowConfiguration.bounds
         val positionInParent = taskInfo.positionInParent
         return TaskUpdate(
             instanceId = taskInfo.taskId,
-            uid = taskInfo.userId,
+            uid = taskInfo.effectiveUid,
             taskHeight = screenBounds.height(),
             taskWidth = screenBounds.width(),
             taskX = positionInParent.x,
             taskY = positionInParent.y,
+            visibleTaskCount = visibleTasks,
         )
     }
 
     /** Get [EnterReason] for this session enter */
-    private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason =
-        when {
-            transitionInfo.type == WindowManager.TRANSIT_WAKE -> EnterReason.SCREEN_ON
+    private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
+       val enterReason = when {
+            transitionInfo.type == WindowManager.TRANSIT_WAKE
+                   // If there is a screen lock, desktop window entry is after dismissing keyguard
+                   || (transitionInfo.type == WindowManager.TRANSIT_TO_BACK
+                   && wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON
             transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
                 EnterReason.APP_HANDLE_DRAG
             transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
@@ -353,11 +381,17 @@
                 EnterReason.UNKNOWN_ENTER
             }
         }
+        wasPreviousTransitionExitByScreenOff = false
+        return enterReason
+    }
 
     /** Get [ExitReason] for this session exit */
     private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
          when {
-            transitionInfo.type == WindowManager.TRANSIT_SLEEP -> ExitReason.SCREEN_OFF
+            transitionInfo.type == WindowManager.TRANSIT_SLEEP -> {
+                wasPreviousTransitionExitByScreenOff = true
+                ExitReason.SCREEN_OFF
+            }
             transitionInfo.type == WindowManager.TRANSIT_CLOSE -> ExitReason.TASK_FINISHED
             transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -> ExitReason.DRAG_TO_EXIT
             transitionInfo.type == TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON ->
@@ -400,4 +434,12 @@
         return this.type == WindowManager.TRANSIT_TO_FRONT &&
             this.flags == WindowManager.TRANSIT_FLAG_IS_RECENTS
     }
+
+    companion object {
+        @VisibleForTesting
+        const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks"
+        @VisibleForTesting
+        const val VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY =
+            "debug.tracing." + VISIBLE_TASKS_COUNTER_NAME
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
index eca3c1f..dba8c93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.desktopmode
 
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.android.wm.shell.sysui.ShellCommandHandler
 import java.io.PrintWriter
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 8375294..9d04169 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -194,6 +194,11 @@
     fun getActiveNonMinimizedOrderedTasks(displayId: Int): List<Int> =
         getFreeformTasksInZOrder(displayId).filter { !isMinimizedTask(it) }
 
+    /** Returns the count of active non-minimized tasks for [displayId]. */
+    fun getActiveNonMinimizedTaskCount(displayId: Int): Int {
+        return getActiveTasks(displayId).count { !isMinimizedTask(it) }
+    }
+
     /** Returns a list of freeform tasks, ordered from top-bottom (top at index 0). */
     fun getFreeformTasksInZOrder(displayId: Int): ArrayList<Int> =
         ArrayList(desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder ?: emptyList())
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
index b24bd10..d6fccd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
@@ -17,7 +17,7 @@
 package com.android.wm.shell.desktopmode
 
 import android.view.WindowManager.TransitionType
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
 import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TYPES
 
 /**
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 6c03dc3..c8ffe28 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
@@ -66,7 +66,7 @@
     val initialSize: Size =
         when (taskInfo.configuration.orientation) {
             ORIENTATION_LANDSCAPE -> {
-                if (taskInfo.isResizeable) {
+                if (taskInfo.canChangeAspectRatio) {
                     if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
                         // For portrait resizeable activities, respect apps fullscreen width but
                         // apply ideal size height.
@@ -85,7 +85,7 @@
             ORIENTATION_PORTRAIT -> {
                 val customPortraitWidthForLandscapeApp =
                     screenBounds.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
-                if (taskInfo.isResizeable) {
+                if (taskInfo.canChangeAspectRatio) {
                     if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
                         // For landscape resizeable activities, respect apps fullscreen height and
                         // apply custom app width.
@@ -171,6 +171,18 @@
         minOf(appBounds.height(), appBounds.width()).toFloat()
 }
 
+/** Returns true if task's width or height is maximized else returns false. */
+fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+    return taskBounds.width() == stableBounds.width() ||
+            taskBounds.height() == stableBounds.height()
+}
+
+/** Returns true if task bound is equal to stable bounds else returns false. */
+fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+    return taskBounds.width() == stableBounds.width() &&
+            taskBounds.height() == stableBounds.height()
+}
+
 /**
  * Calculates the desired initial bounds for applications in desktop windowing. This is done as a
  * scale of the screen bounds.
@@ -189,6 +201,13 @@
     }
 
 /**
+ * Whether the activity's aspect ratio can be changed or if it should be maintained as if it was
+ * unresizeable.
+ */
+private val TaskInfo.canChangeAspectRatio: Boolean
+    get() = isResizeable && !appCompatTaskInfo.hasMinAspectRatioOverride()
+
+/**
  * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the
  * entire screen, as area can be offset by left and top start.
  */
@@ -204,7 +223,7 @@
     return Rect(newLeft, newTop, newRight, newBottom)
 }
 
-fun TaskInfo.hasPortraitTopActivity(): Boolean {
+private fun TaskInfo.hasPortraitTopActivity(): Boolean {
     val topActivityScreenOrientation =
         topActivityInfo?.screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED
     val appBounds = configuration.windowConfiguration.appBounds
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 09f9139..bfc0ee8 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
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.desktopmode;
 
-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.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -29,7 +28,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
@@ -70,6 +68,37 @@
         TO_SPLIT_RIGHT_INDICATOR
     }
 
+    /**
+     * The conditions surrounding the drag event that led to the indicator's creation.
+     */
+    public enum DragStartState {
+        /** The indicator is resulting from a freeform task drag. */
+        FROM_FREEFORM,
+        /** The indicator is resulting from a split screen task drag */
+        FROM_SPLIT,
+        /** The indicator is resulting from a fullscreen task drag */
+        FROM_FULLSCREEN,
+        /** The indicator is resulting from an Intent generated during a drag-and-drop event */
+        DRAGGED_INTENT;
+
+        /**
+         * Get the {@link DragStartState} of a drag event based on the windowing mode of the task.
+         * Note that DRAGGED_INTENT will be specified by the caller if needed and not returned
+         * here.
+         */
+        public static DesktopModeVisualIndicator.DragStartState getDragStartState(
+                ActivityManager.RunningTaskInfo taskInfo
+        ) {
+            if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+                return FROM_FULLSCREEN;
+            } else if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+                return FROM_SPLIT;
+            } else if (taskInfo.isFreeform()) {
+                return FROM_FREEFORM;
+            } else return null;
+        }
+    }
+
     private final Context mContext;
     private final DisplayController mDisplayController;
     private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer;
@@ -82,11 +111,13 @@
 
     private View mView;
     private IndicatorType mCurrentType;
+    private DragStartState mDragStartState;
 
     public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue,
             ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
             Context context, SurfaceControl taskSurface,
-            RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer) {
+            RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer,
+            DragStartState dragStartState) {
         mSyncQueue = syncQueue;
         mTaskInfo = taskInfo;
         mDisplayController = displayController;
@@ -94,6 +125,7 @@
         mTaskSurface = taskSurface;
         mRootTdaOrganizer = taskDisplayAreaOrganizer;
         mCurrentType = IndicatorType.NO_INDICATOR;
+        mDragStartState = dragStartState;
     }
 
     /**
@@ -101,7 +133,7 @@
      * display, including no visible indicator.
      */
     @NonNull
-    IndicatorType updateIndicatorType(PointF inputCoordinates, int windowingMode) {
+    IndicatorType updateIndicatorType(PointF inputCoordinates) {
         final DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
         // If we are in freeform, we don't want a visible indicator in the "freeform" drag zone.
         IndicatorType result = IndicatorType.NO_INDICATOR;
@@ -111,14 +143,13 @@
         // account for the possibility of the task going off the top of the screen by captionHeight
         final int captionHeight = mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_freeform_decor_caption_height);
-        final Region fullscreenRegion = calculateFullscreenRegion(layout, windowingMode,
+        final Region fullscreenRegion = calculateFullscreenRegion(layout, captionHeight);
+        final Region splitLeftRegion = calculateSplitLeftRegion(layout, transitionAreaWidth,
                 captionHeight);
-        final Region splitLeftRegion = calculateSplitLeftRegion(layout, windowingMode,
-                transitionAreaWidth, captionHeight);
-        final Region splitRightRegion = calculateSplitRightRegion(layout, windowingMode,
-                transitionAreaWidth, captionHeight);
-        final Region toDesktopRegion = calculateToDesktopRegion(layout, windowingMode,
-                splitLeftRegion, splitRightRegion, fullscreenRegion);
+        final Region splitRightRegion = calculateSplitRightRegion(layout, transitionAreaWidth,
+                captionHeight);
+        final Region toDesktopRegion = calculateToDesktopRegion(layout, splitLeftRegion,
+                splitRightRegion, fullscreenRegion);
         if (fullscreenRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
             result = IndicatorType.TO_FULLSCREEN_INDICATOR;
         }
@@ -131,20 +162,22 @@
         if (toDesktopRegion.contains((int) inputCoordinates.x, (int) inputCoordinates.y)) {
             result = IndicatorType.TO_DESKTOP_INDICATOR;
         }
-        transitionIndicator(result);
+        if (mDragStartState != DragStartState.DRAGGED_INTENT) {
+            transitionIndicator(result);
+        }
         return result;
     }
 
     @VisibleForTesting
-    Region calculateFullscreenRegion(DisplayLayout layout,
-            @WindowConfiguration.WindowingMode int windowingMode, int captionHeight) {
+    Region calculateFullscreenRegion(DisplayLayout layout, int captionHeight) {
         final Region region = new Region();
-        int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+        int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
+                || mDragStartState == DragStartState.DRAGGED_INTENT
                 ? mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness)
                 : 2 * layout.stableInsets().top;
         // A Rect at the top of the screen that takes up the center 40%.
-        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+        if (mDragStartState == DragStartState.FROM_FREEFORM) {
             final float toFullscreenScale = mContext.getResources().getFloat(
                     R.dimen.desktop_mode_fullscreen_region_scale);
             final float toFullscreenWidth = (layout.width() * toFullscreenScale);
@@ -153,9 +186,11 @@
                     (int) ((layout.width() / 2f) + (toFullscreenWidth / 2f)),
                     transitionHeight));
         }
-        // A screen-wide Rect if the task is in fullscreen or split.
-        if (windowingMode == WINDOWING_MODE_FULLSCREEN
-                || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+        // A screen-wide Rect if the task is in fullscreen, split, or a dragged intent.
+        if (mDragStartState == DragStartState.FROM_FULLSCREEN
+                || mDragStartState == DragStartState.FROM_SPLIT
+                || mDragStartState == DragStartState.DRAGGED_INTENT
+        ) {
             region.union(new Rect(0,
                     -captionHeight,
                     layout.width(),
@@ -166,12 +201,11 @@
 
     @VisibleForTesting
     Region calculateToDesktopRegion(DisplayLayout layout,
-            @WindowConfiguration.WindowingMode int windowingMode,
             Region splitLeftRegion, Region splitRightRegion,
             Region toFullscreenRegion) {
         final Region region = new Region();
         // If in desktop, we need no region. Otherwise it's the same for all windowing modes.
-        if (windowingMode != WINDOWING_MODE_FREEFORM) {
+        if (mDragStartState != DragStartState.FROM_FREEFORM) {
             region.union(new Rect(0, 0, layout.width(), layout.height()));
             region.op(splitLeftRegion, Region.Op.DIFFERENCE);
             region.op(splitRightRegion, Region.Op.DIFFERENCE);
@@ -182,11 +216,10 @@
 
     @VisibleForTesting
     Region calculateSplitLeftRegion(DisplayLayout layout,
-            @WindowConfiguration.WindowingMode int windowingMode,
             int transitionEdgeWidth, int captionHeight) {
         final Region region = new Region();
         // In freeform, keep the top corners clear.
-        int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+        int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
                 ? mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
                 -captionHeight;
@@ -196,11 +229,10 @@
 
     @VisibleForTesting
     Region calculateSplitRightRegion(DisplayLayout layout,
-            @WindowConfiguration.WindowingMode int windowingMode,
             int transitionEdgeWidth, int captionHeight) {
         final Region region = new Region();
         // In freeform, keep the top corners clear.
-        int transitionHeight = windowingMode == WINDOWING_MODE_FREEFORM
+        int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM
                 ? mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.desktop_mode_split_from_desktop_height) :
                 -captionHeight;
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 f54b44b..1d16980 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
@@ -34,10 +34,12 @@
 import android.graphics.PointF
 import android.graphics.Rect
 import android.graphics.Region
+import android.os.Binder
 import android.os.IBinder
 import android.os.SystemProperties
 import android.util.Size
 import android.view.Display.DEFAULT_DISPLAY
+import android.view.DragEvent
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_NONE
@@ -51,6 +53,7 @@
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.internal.protolog.ProtoLog
@@ -66,11 +69,9 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SingleInstanceRemoteListener
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
 import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
+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.draganddrop.DragAndDropController
@@ -79,6 +80,7 @@
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.ShellSharedConstants
 import com.android.wm.shell.shared.annotations.ExternalThread
 import com.android.wm.shell.shared.annotations.ShellMainThread
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
@@ -86,12 +88,14 @@
 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
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
+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
 import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE
 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.sysui.ShellSharedConstants
 import com.android.wm.shell.transition.OneShotRemoteHandler
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility
@@ -121,6 +125,7 @@
     private val returnToDragStartAnimator: ReturnToDragStartAnimator,
     private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
     private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
+    private val desktopModeDragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler,
     private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
     private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
     private val taskRepository: DesktopModeTaskRepository,
@@ -146,12 +151,6 @@
             visualIndicator?.releaseVisualIndicator(t)
             visualIndicator = null
         }
-    private val taskVisibilityListener =
-        object : VisibleTasksListener {
-            override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
-                launchAdjacentController.launchAdjacentEnabled = visibleTasksCount == 0
-            }
-        }
     private val dragToDesktopStateListener =
         object : DragToDesktopStateListener {
             override fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction) {
@@ -170,12 +169,18 @@
             }
         }
 
+    @VisibleForTesting
+    var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
+
     /** Task id of the task currently being dragged from fullscreen/split. */
     val draggingTaskId
         get() = dragToDesktopTransitionHandler.draggingTaskId
 
     private var recentsAnimationRunning = false
     private lateinit var splitScreenController: SplitScreenController
+    // 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
 
     init {
         desktopMode = DesktopModeImpl()
@@ -194,7 +199,6 @@
             this
         )
         transitions.addHandler(this)
-        taskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor)
         dragToDesktopTransitionHandler.dragToDesktopStateListener = dragToDesktopStateListener
         recentsTransitionHandler.addTransitionStateListener(
             object : RecentsTransitionStateListener {
@@ -322,7 +326,7 @@
             logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
             return false
         }
-        logV("moveBackgroundTaskToDesktop with taskId=%d, displayId=%d", taskId)
+        logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
         // TODO(342378842): Instead of using default display, support multiple displays
         val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
             DEFAULT_DISPLAY, wct, taskId)
@@ -431,6 +435,20 @@
         taskRepository.addClosingTask(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) {
+        if (taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
+            removeWallpaperActivity(wct)
+        }
+        // Do not call taskRepository.minimizeTask because it will be called by DekstopTasksLimiter.
+    }
+
     /** Move a task with given `taskId` to fullscreen */
     fun moveToFullscreen(taskId: Int, transitionSource: DesktopModeTransitionSource) {
         shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
@@ -595,13 +613,10 @@
         val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
         val destinationBounds = Rect()
 
-        val isMaximized = if (taskInfo.isResizeable) {
-            currentTaskBounds == stableBounds
-        } else {
-            currentTaskBounds.width() == stableBounds.width()
-                    || currentTaskBounds.height() == stableBounds.height()
-        }
-
+        val isMaximized = isTaskMaximized(taskInfo, stableBounds)
+        // If the task is currently maximized, we will toggle it not to be and vice versa. This is
+        // helpful to eliminate the current task from logic to calculate taskbar corner rounding.
+        val willMaximize = !isMaximized
         if (isMaximized) {
             // The desktop task is at the maximized width and/or height of the stable bounds.
             // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
@@ -636,6 +651,18 @@
             }
         }
 
+
+
+        val shouldRestoreToSnap =
+            isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)
+
+        logD("willMaximize = %s", willMaximize)
+        logD("shouldRestoreToSnap = %s", shouldRestoreToSnap)
+
+        val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap ||
+                doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId)
+
+        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
@@ -644,23 +671,98 @@
         }
     }
 
+    private fun isTaskMaximized(
+        taskInfo: RunningTaskInfo,
+        stableBounds: Rect
+    ): Boolean {
+        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+
+        return if (taskInfo.isResizeable) {
+            isTaskBoundsEqual(currentTaskBounds, stableBounds)
+        } else {
+            isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
+        }
+    }
+
+    private fun isMaximizedToStableBoundsEdges(
+        taskInfo: RunningTaskInfo,
+        stableBounds: Rect
+    ): Boolean {
+        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+        return isTaskBoundsEqual(currentTaskBounds, stableBounds)
+    }
+
+    /** Returns if current task bound is snapped to half screen */
+    private fun isTaskSnappedToHalfScreen(
+        taskInfo: RunningTaskInfo,
+        taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds
+    ): Boolean =
+        getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds ||
+                getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds
+
+    @VisibleForTesting
+    fun doesAnyTaskRequireTaskbarRounding(
+        displayId: Int,
+        excludeTaskId: Int? = null,
+    ): Boolean {
+        val doesAnyTaskRequireTaskbarRounding =
+            taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
+                // exclude current task since maximize/restore transition has not taken place yet.
+                .filterNot { taskId -> taskId == excludeTaskId }
+                .any { taskId ->
+                    val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!!
+                    val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
+                    val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) }
+                    logD("taskInfo = %s", taskInfo)
+                    logD(
+                        "isTaskSnappedToHalfScreen(taskInfo) = %s",
+                        isTaskSnappedToHalfScreen(taskInfo)
+                    )
+                    logD(
+                        "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s",
+                        isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+                    )
+                    isTaskSnappedToHalfScreen(taskInfo)
+                            || isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+                }
+
+        logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding)
+        return doesAnyTaskRequireTaskbarRounding
+    }
+
     /**
      * Quick-resize to the right or left half of the stable bounds.
      *
      * @param taskInfo current task that is being snap-resized via dragging or maximize menu button
+     * @param taskSurface the leash of the task being dragged
      * @param currentDragBounds current position of the task leash being dragged (or current task
      *                          bounds if being snapped resize via maximize menu button)
      * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
      */
     fun snapToHalfScreen(
         taskInfo: RunningTaskInfo,
+        taskSurface: SurfaceControl,
         currentDragBounds: Rect,
         position: SnapPosition
     ) {
         val destinationBounds = getSnapBounds(taskInfo, position)
+        if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) {
+            // Handle the case where we attempt to snap resize when already snap resized: the task
+            // position won't need to change but we want to animate the surface going back to the
+            // snapped position from the "dragged-to-the-edge" position.
+            if (destinationBounds != currentDragBounds) {
+                returnToDragStartAnimator.start(
+                    taskInfo.taskId,
+                    taskSurface,
+                    startBounds = currentDragBounds,
+                    endBounds = destinationBounds,
+                    isResizable = taskInfo.isResizeable
+                )
+            }
+            return
+        }
 
-        if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
-
+        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
         val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
             toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
@@ -679,15 +781,23 @@
     ) {
         releaseVisualIndicator()
         if (!taskInfo.isResizeable && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(context)) {
+            interactionJankMonitor.begin(
+                taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
+            )
+
             // reposition non-resizable app back to its original position before being dragged
             returnToDragStartAnimator.start(
                 taskInfo.taskId,
                 taskSurface,
                 startBounds = currentDragBounds,
-                endBounds = dragStartBounds
+                endBounds = dragStartBounds,
+                isResizable = taskInfo.isResizeable,
             )
         } else {
-            snapToHalfScreen(taskInfo, currentDragBounds, position)
+            interactionJankMonitor.begin(
+                taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
+            )
+            snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)
         }
     }
 
@@ -784,6 +894,10 @@
             .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
             .reversed() // Start from the back so the front task is brought forward last
             .forEach { task -> wct.reorder(task.token, /* onTop= */ true) }
+
+        taskbarDesktopTaskListener?.
+            onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
+
         return taskToMinimize
     }
 
@@ -799,6 +913,7 @@
         val intent = Intent(context, DesktopWallpaperActivity::class.java)
         val options =
             ActivityOptions.makeBasic().apply {
+                launchWindowingMode = WINDOWING_MODE_FULLSCREEN
                 pendingIntentBackgroundActivityStartMode =
                     ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
             }
@@ -858,6 +973,7 @@
         val triggerTask = request.triggerTask
         var shouldHandleMidRecentsFreeformLaunch =
             recentsAnimationRunning && isFreeformRelaunch(triggerTask, request)
+        val isDragAndDropFullscreenTransition = taskContainsDragAndDropCookie(triggerTask)
         val shouldHandleRequest =
             when {
                 // Handle freeform relaunch during recents animation
@@ -866,6 +982,13 @@
                     reason = "recents animation is running"
                     false
                 }
+                // Don't handle request if this was a tear to fullscreen transition.
+                // handleFullscreenTaskLaunch moves fullscreen intents to freeform;
+                // this is an exception to the rule
+                isDragAndDropFullscreenTransition -> {
+                    dragAndDropFullscreenCookie = null
+                    false
+                }
                 // Handle task closing for the last window if wallpaper is available
                 shouldHandleTaskClosing(request) -> true
                 // Only handle open or to front transitions
@@ -884,8 +1007,7 @@
                     false
                 }
                 // Only handle fullscreen or freeform tasks
-                triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN &&
-                    triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> {
+                !triggerTask.isFullscreen && !triggerTask.isFreeform -> {
                     reason = "windowingMode not handled (${triggerTask.windowingMode})"
                     false
                 }
@@ -920,6 +1042,9 @@
         return result
     }
 
+    private fun taskContainsDragAndDropCookie(taskInfo: RunningTaskInfo?) =
+        taskInfo?.launchCookies?.any { it == dragAndDropFullscreenCookie } ?: false
+
     /**
      * Applies the proper surface states (rounded corners) to tasks when desktop mode is active.
      * This is intended to be used when desktop mode is part of another animation but isn't, itself,
@@ -1045,6 +1170,17 @@
             wct.reorder(task.token, true)
             return wct
         }
+        // If task is already visible, it must have been handled already and added to desktop mode.
+        // Cascade task only if it's not visible yet.
+        if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)
+                && !taskRepository.isVisibleTask(task.taskId)) {
+            val displayLayout = displayController.getDisplayLayout(task.displayId)
+            if (displayLayout != null) {
+                val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
+                cascadeWindow(task, initialBounds, displayLayout)
+                wct.setBounds(task.token, initialBounds)
+            }
+        }
         if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
         }
@@ -1069,12 +1205,11 @@
                 addMoveToDesktopChanges(wct, task)
                 // In some launches home task is moved behind new task being launched. Make sure
                 // that's not the case for launches in desktop.
-                moveHomeTask(wct, toTop = false)
-                // Move existing minimized tasks behind Home
-                taskRepository.getFreeformTasksInZOrder(task.displayId)
-                    .filter { taskId -> taskRepository.isMinimizedTask(taskId) }
-                    .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
-                    .forEach { taskInfo -> wct.reorder(taskInfo.token, /* onTop= */ false) }
+                if (task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0) {
+                    bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
+                    wct.reorder(task.token, true)
+                }
+
                 // Desktop Mode is already showing and we're launching a new Task - we might need to
                 // minimize another Task.
                 val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task)
@@ -1097,18 +1232,29 @@
     /** Handle task closing by removing wallpaper activity if it's the last active task */
     private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
         logV("handleTaskClosing")
+        if (!isDesktopModeShowing(task.displayId))
+            return null
+
         val wct = WindowContainerTransaction()
         if (taskRepository.isOnlyVisibleNonClosingTask(task.taskId)
-            && taskRepository.wallpaperActivityToken != null) {
+            && taskRepository.wallpaperActivityToken != null
+        ) {
             // Remove wallpaper activity when the last active task is removed
             removeWallpaperActivity(wct)
         }
         taskRepository.addClosingTask(task.displayId, task.taskId)
         // If a CLOSE or TO_BACK is triggered on a desktop task, remove the task.
         if (DesktopModeFlags.BACK_NAVIGATION.isEnabled(context) &&
-            taskRepository.isVisibleTask(task.taskId)) {
+            taskRepository.isVisibleTask(task.taskId)
+        ) {
             wct.removeTask(task.token)
         }
+        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+            doesAnyTaskRequireTaskbarRounding(
+                task.displayId,
+                task.id
+            )
+        )
         return if (wct.isEmpty) null else wct
     }
 
@@ -1134,18 +1280,9 @@
         }
 
         if (DesktopModeFlags.CASCADING_WINDOWS.isEnabled(context)) {
-            val stableBounds = Rect()
-            displayLayout.getStableBoundsForDesktopMode(stableBounds)
-
-            val activeTasks = taskRepository
-                .getActiveNonMinimizedOrderedTasks(taskInfo.displayId)
-            activeTasks.firstOrNull()?.let { activeTask ->
-                shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
-                    cascadeWindow(context.resources, stableBounds,
-                        it.configuration.windowConfiguration.bounds, initialBounds)
-                }
-            }
+            cascadeWindow(taskInfo, initialBounds, displayLayout)
         }
+
         if (canChangeTaskPosition(taskInfo)) {
             wct.setBounds(taskInfo.token, initialBounds)
         }
@@ -1180,6 +1317,19 @@
         }
     }
 
+    private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) {
+        val stableBounds = Rect()
+        displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+        val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId)
+        activeTasks.firstOrNull()?.let { activeTask ->
+            shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
+                cascadeWindow(context.resources, stableBounds,
+                    it.configuration.windowConfiguration.bounds, bounds)
+            }
+        }
+    }
+
     /**
      * Adds split screen changes to a transaction. Note that bounds are not reset here due to
      * animation; see {@link onDesktopSplitSelectAnimComplete}
@@ -1299,15 +1449,17 @@
         taskBounds: Rect
     ) {
         if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
-        updateVisualIndicator(taskInfo, taskSurface, inputX, taskBounds.top.toFloat())
+        updateVisualIndicator(taskInfo, taskSurface, inputX, taskBounds.top.toFloat(),
+            DragStartState.FROM_FREEFORM)
     }
 
     fun updateVisualIndicator(
         taskInfo: RunningTaskInfo,
-        taskSurface: SurfaceControl,
+        taskSurface: SurfaceControl?,
         inputX: Float,
-        taskTop: Float
-    ): IndicatorType {
+        taskTop: Float,
+        dragStartState: DragStartState
+    ): DesktopModeVisualIndicator.IndicatorType {
         // If the visual indicator does not exist, create it.
         val indicator =
             visualIndicator
@@ -1317,10 +1469,11 @@
                     displayController,
                     context,
                     taskSurface,
-                    rootTaskDisplayAreaOrganizer
+                    rootTaskDisplayAreaOrganizer,
+                    dragStartState
                 )
         if (visualIndicator == null) visualIndicator = indicator
-        return indicator.updateIndicatorType(PointF(inputX, taskTop), taskInfo.windowingMode)
+        return indicator.updateIndicatorType(PointF(inputX, taskTop))
     }
 
     /**
@@ -1353,7 +1506,6 @@
         val indicatorType =
             indicator.updateIndicatorType(
                 PointF(inputCoordinate.x, currentDragBounds.top.toFloat()),
-                taskInfo.windowingMode
             )
         when (indicatorType) {
             IndicatorType.TO_FULLSCREEN_INDICATOR -> {
@@ -1397,6 +1549,8 @@
         }
         // A freeform drag-move ended, remove the indicator immediately.
         releaseVisualIndicator()
+        taskbarDesktopTaskListener
+            ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId))
     }
 
     /**
@@ -1414,7 +1568,7 @@
         // End the drag_hold CUJ interaction.
         interactionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         val indicator = getVisualIndicator() ?: return IndicatorType.NO_INDICATOR
-        val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
+        val indicatorType = indicator.updateIndicatorType(inputCoordinates)
         when (indicatorType) {
             IndicatorType.TO_DESKTOP_INDICATOR -> {
                 // Start a new jank interaction for the drag release to desktop window animation.
@@ -1466,9 +1620,10 @@
         taskRepository.setExclusionRegionListener(listener, callbackExecutor)
     }
 
+    // TODO(b/358114479): Move this implementation into a separate class.
     override fun onUnhandledDrag(
         launchIntent: PendingIntent,
-        dragSurface: SurfaceControl,
+        dragEvent: DragEvent,
         onFinishCallback: Consumer<Boolean>
     ): Boolean {
         // TODO(b/320797628): Pass through which display we are dropping onto
@@ -1476,7 +1631,6 @@
             // Not currently in desktop mode, ignore the drop
             return false
         }
-
         val launchComponent = getComponent(launchIntent)
         if (!multiInstanceHelper.supportsMultiInstanceSplit(launchComponent)) {
             // TODO(b/320797628): Should only return early if there is an existing running task, and
@@ -1484,20 +1638,69 @@
             logV("Dropped intent does not support multi-instance")
             return false
         }
-
+        val taskInfo = getFocusedFreeformTask(DEFAULT_DISPLAY) ?: return false
+        // TODO(b/358114479): Update drag and drop handling to give us visibility into when another
+        //  window will accept a drag event. This way, we can hide the indicator when we won't
+        //  be handling the transition here, allowing us to display the indicator accurately.
+        //  For now, we create the indicator only on drag end and immediately dispose it.
+        val indicatorType = updateVisualIndicator(taskInfo, dragEvent.dragSurface,
+            dragEvent.x, dragEvent.y,
+            DragStartState.DRAGGED_INTENT)
+        releaseVisualIndicator()
+        val windowingMode = when (indicatorType) {
+            IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+                WINDOWING_MODE_FULLSCREEN
+            }
+            IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+            IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+            IndicatorType.TO_DESKTOP_INDICATOR
+            -> {
+                WINDOWING_MODE_FREEFORM
+            }
+            else -> error("Invalid indicator type: $indicatorType")
+        }
+        val displayLayout = displayController.getDisplayLayout(DEFAULT_DISPLAY) ?: return false
+        val newWindowBounds = Rect()
+        when (indicatorType) {
+            IndicatorType.TO_DESKTOP_INDICATOR -> {
+                // Use default bounds, but with the top-center at the drop point.
+                newWindowBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+                newWindowBounds.offsetTo(
+                    dragEvent.x.toInt() - (newWindowBounds.width() / 2),
+                    dragEvent.y.toInt()
+                )
+            }
+            IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
+                newWindowBounds.set(getSnapBounds(taskInfo, SnapPosition.RIGHT))
+            }
+            IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
+                newWindowBounds.set(getSnapBounds(taskInfo, SnapPosition.LEFT))
+            }
+            else -> {
+                // Use empty bounds for the fullscreen case.
+            }
+        }
         // Start a new transition to launch the app
         val opts =
             ActivityOptions.makeBasic().apply {
-                launchWindowingMode = WINDOWING_MODE_FREEFORM
+                launchWindowingMode = windowingMode
+                launchBounds = newWindowBounds
+                pendingIntentBackgroundActivityStartMode =
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
                 pendingIntentLaunchFlags =
                     Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                setPendingIntentBackgroundActivityStartMode(
-                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED
-                )
             }
+        if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            dragAndDropFullscreenCookie = Binder()
+            opts.launchCookie = dragAndDropFullscreenCookie
+        }
         val wct = WindowContainerTransaction()
         wct.sendPendingIntent(launchIntent, null, opts.toBundle())
-        transitions.startTransition(TRANSIT_OPEN, wct, null /* handler */)
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
+            desktopModeDragAndDropTransitionHandler.handleDropEvent(wct)
+        } else {
+            transitions.startTransition(TRANSIT_OPEN, wct, null)
+        }
 
         // Report that this is handled by the listener
         onFinishCallback.accept(true)
@@ -1505,7 +1708,7 @@
         // We've assumed responsibility of cleaning up the drag surface, so do that now
         // TODO(b/320797628): Do an actual animation here for the drag surface
         val t = SurfaceControl.Transaction()
-        t.remove(dragSurface)
+        t.remove(dragEvent.dragSurface)
         t.apply()
         return true
     }
@@ -1513,6 +1716,7 @@
     private fun dump(pw: PrintWriter, prefix: String) {
         val innerPrefix = "$prefix  "
         pw.println("${prefix}DesktopTasksController")
+        DesktopModeStatus.dump(pw, innerPrefix, context)
         taskRepository.dump(pw, innerPrefix)
     }
 
@@ -1583,17 +1787,39 @@
                 }
             }
 
+        private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener =
+                object : TaskbarDesktopTaskListener {
+                    override fun onTaskbarCornerRoundingUpdate(
+                        hasTasksRequiringTaskbarRounding: Boolean) {
+                        ProtoLog.v(
+                                WM_SHELL_DESKTOP_MODE,
+                                "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
+                                        "doesAnyTaskRequireTaskbarRounding=%s",
+                                hasTasksRequiringTaskbarRounding
+                        )
+
+                        remoteListener.call { l ->
+                            l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
+                        }
+                    }
+                }
+
         init {
             remoteListener =
                 SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
                     controller,
                     { c ->
-                        c.taskRepository.addVisibleTasksListener(
-                            listener,
-                            c.mainExecutor
-                        )
+                        run {
+                            c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor)
+                            c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener
+                        }
                     },
-                    { c -> c.taskRepository.removeVisibleTasksListener(listener) }
+                    { c ->
+                        run {
+                            c.taskRepository.removeVisibleTasksListener(listener)
+                            c.taskbarDesktopTaskListener = null
+                        }
+                    }
                 )
         }
 
@@ -1676,6 +1902,16 @@
         private const val TAG = "DesktopTasksController"
     }
 
+    /** Defines interface for classes that can listen to changes for task resize. */
+    // TODO(b/343931111): Migrate to using TransitionObservers when ready
+    interface TaskbarDesktopTaskListener {
+        /**
+         * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped
+         * left/right and rounded corners are enabled.
+         */
+        fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean)
+    }
+
     /** The positions on a screen that a task can snap to. */
     enum class SnapPosition {
         RIGHT,
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 38675129..597637d 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
@@ -70,11 +70,11 @@
 
     // TODO(b/333018485): replace this observer when implementing the minimize-animation
     private inner class MinimizeTransitionObserver : TransitionObserver {
-        private val mPendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
-        private val mActiveTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+        private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
+        private val activeTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>()
 
         fun addPendingTransitionToken(transition: IBinder, taskDetails: TaskDetails) {
-            mPendingTransitionTokensAndTasks[transition] = taskDetails
+            pendingTransitionTokensAndTasks[transition] = taskDetails
         }
 
         override fun onTransitionReady(
@@ -83,9 +83,7 @@
             startTransaction: SurfaceControl.Transaction,
             finishTransaction: SurfaceControl.Transaction
         ) {
-            val taskToMinimize = mPendingTransitionTokensAndTasks.remove(transition) ?: return
-            taskToMinimize.transitionInfo = info
-            mActiveTransitionTokensAndTasks[transition] = taskToMinimize
+            val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
 
             if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
 
@@ -97,6 +95,8 @@
                 return
             }
 
+            taskToMinimize.transitionInfo = info
+            activeTransitionTokensAndTasks[transition] = taskToMinimize
             [email protected](
                     taskToMinimize.displayId, taskToMinimize.taskId)
         }
@@ -121,7 +121,7 @@
         }
 
         override fun onTransitionStarting(transition: IBinder) {
-            val mActiveTaskDetails = mActiveTransitionTokensAndTasks[transition]
+            val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
             if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
                 // Begin minimize window CUJ instrumentation.
                 interactionJankMonitor.begin(
@@ -132,11 +132,11 @@
         }
 
         override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
-            if (mActiveTransitionTokensAndTasks.remove(merged) != null) {
+            if (activeTransitionTokensAndTasks.remove(merged) != null) {
                 interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
             }
-            mPendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
-                mPendingTransitionTokensAndTasks[playing] = taskToTransfer
+            pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
+                pendingTransitionTokensAndTasks[playing] = taskToTransfer
             }
         }
 
@@ -144,14 +144,14 @@
             ProtoLog.v(
                     ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                     "DesktopTasksLimiter: transition %s finished", transition)
-            if (mActiveTransitionTokensAndTasks.remove(transition) != null) {
+            if (activeTransitionTokensAndTasks.remove(transition) != null) {
                 if (aborted) {
                     interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
                 } else {
                     interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
                 }
             }
-            mPendingTransitionTokensAndTasks.remove(transition)
+            pendingTransitionTokensAndTasks.remove(transition)
         }
     }
 
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 9874f4c..d72ec90 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
@@ -22,26 +22,28 @@
 import android.os.Bundle
 import android.os.IBinder
 import android.os.SystemClock
+import android.os.SystemProperties
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CLOSE
 import android.window.TransitionInfo
 import android.window.TransitionInfo.Change
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
-import androidx.dynamicanimation.animation.SpringForce
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.dynamicanimation.animation.SpringForce
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.protolog.ProtoLog
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.animation.FloatProperties
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import com.android.wm.shell.shared.TransitionUtil
 import com.android.wm.shell.shared.animation.PhysicsAnimator
+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.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
@@ -893,13 +895,10 @@
     ) {
 
     private val positionSpringConfig =
-        PhysicsAnimator.SpringConfig(
-            SpringForce.STIFFNESS_LOW,
-            SpringForce.DAMPING_RATIO_LOW_BOUNCY
-        )
+        PhysicsAnimator.SpringConfig(POSITION_SPRING_STIFFNESS, POSITION_SPRING_DAMPING_RATIO)
 
     private val sizeSpringConfig =
-        PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_NO_BOUNCY)
+        PhysicsAnimator.SpringConfig(SIZE_SPRING_STIFFNESS, SIZE_SPRING_DAMPING_RATIO)
 
     /**
      * @return layers in order:
@@ -929,7 +928,7 @@
         finishTransaction.hide(homeLeash)
         // Setup freeform tasks before animation
         state.freeformTaskChanges.forEach { change ->
-            val startScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE
+            val startScale = FREEFORM_TASKS_INITIAL_SCALE
             val startX =
                 change.endAbsBounds.left + change.endAbsBounds.width() * (1 - startScale) / 2
             val startY =
@@ -994,9 +993,22 @@
                     (animBounds.width() - startBounds.width()).toFloat() /
                         (endBounds.width() - startBounds.width())
                 val animScale = startScale + animFraction * (1 - startScale)
-                // Freeform animation starts 50% in the animation
-                val freeformAnimFraction = max(animFraction - 0.5f, 0f) * 2f
-                val freeformStartScale = DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE
+                // Freeform animation starts with freeform animation offset relative to the commit
+                // animation and plays until the commit animation ends. For instance:
+                // - if the freeform animation offset is `0.0` the freeform tasks animate alongside
+                // - if the freeform animation offset is `0.6` the freeform tasks will
+                //   start animating at 60% fraction of the commit animation and will complete when
+                //   the commit animation fraction is 100%.
+                // - if the freeform animation offset is `1.0` then freeform tasks will appear
+                //   without animation after commit animation finishes.
+                val freeformAnimFraction =
+                    if (FREEFORM_TASKS_ANIM_OFFSET != 1f) {
+                        max(animFraction - FREEFORM_TASKS_ANIM_OFFSET, 0f) /
+                            (1f - FREEFORM_TASKS_ANIM_OFFSET)
+                    } else {
+                        0f
+                    }
+                val freeformStartScale = FREEFORM_TASKS_INITIAL_SCALE
                 val freeformAnimScale =
                     freeformStartScale + freeformAnimFraction * (1 - freeformStartScale)
                 tx.apply {
@@ -1032,10 +1044,53 @@
     }
 
     companion object {
+        /** The freeform tasks initial scale when committing the drag-to-desktop gesture. */
+        private val FREEFORM_TASKS_INITIAL_SCALE =
+            propertyValue("freeform_tasks_initial_scale", scale = 100f, default = 0.9f)
+
+        /** The freeform tasks animation offset relative to the whole animation duration. */
+        private val FREEFORM_TASKS_ANIM_OFFSET =
+            propertyValue("freeform_tasks_anim_offset", scale = 100f, default = 0.5f)
+
+        /** The spring force stiffness used to place the window into the final position. */
+        private val POSITION_SPRING_STIFFNESS =
+            propertyValue("position_stiffness", default = SpringForce.STIFFNESS_LOW)
+
+        /** The spring force damping ratio used to place the window into the final position. */
+        private val POSITION_SPRING_DAMPING_RATIO =
+            propertyValue(
+                "position_damping_ratio",
+                scale = 100f,
+                default = SpringForce.DAMPING_RATIO_LOW_BOUNCY
+            )
+
+        /** The spring force stiffness used to resize the window into the final bounds. */
+        private val SIZE_SPRING_STIFFNESS =
+            propertyValue("size_stiffness", default = SpringForce.STIFFNESS_LOW)
+
+        /** The spring force damping ratio used to resize the window into the final bounds. */
+        private val SIZE_SPRING_DAMPING_RATIO =
+            propertyValue(
+                "size_damping_ratio",
+                scale = 100f,
+                default = SpringForce.DAMPING_RATIO_NO_BOUNCY
+            )
+
+        /** Drag to desktop transition system properties group. */
+        @VisibleForTesting
+        const val SYSTEM_PROPERTIES_GROUP = "persist.wm.debug.desktop_transitions.drag_to_desktop"
+
         /**
-         * The initial scale of the freeform tasks in the animation to commit the drag-to-desktop
-         * gesture.
+         * Drag to desktop transition system property value with [name].
+         *
+         * @param scale an optional scale to apply to the value read from the system property.
+         * @param default a default value to return if the system property isn't set.
          */
-        private const val DRAG_TO_DESKTOP_FREEFORM_TASK_INITIAL_SCALE = 0.9f
+        @VisibleForTesting
+        fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float =
+            SystemProperties.getInt(
+                /* key= */ "$SYSTEM_PROPERTIES_GROUP.$name",
+                /* def= */ (default * scale).toInt()
+            ) / scale
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 04506c1..80e106f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -41,7 +41,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index 171378f..e87be52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -44,7 +44,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.Cuj;
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.transition.Transitions;
 
 import java.util.ArrayList;
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 a7ec203..b036e40e 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
@@ -18,8 +18,8 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.window.RemoteTransition;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.desktopmode.IDesktopTaskListener;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 
 /**
  * Interface that is exposed to remote callers to manipulate desktop mode features.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ebdfdc..c2acb87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -27,4 +27,10 @@
 
     /** @deprecated this is no longer supported. */
     oneway void onStashedChanged(int displayId, boolean stashed);
+
+    /**
+     * Shows taskbar corner radius when running desktop tasks are updated if
+     * [hasTasksRequiringTaskbarRounding] is true.
+     */
+    oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding);
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index be67a40..f4df42c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -19,23 +19,28 @@
 import android.animation.Animator
 import android.animation.RectEvaluator
 import android.animation.ValueAnimator
+import android.content.Context
 import android.graphics.Rect
 import android.view.SurfaceControl
+import android.widget.Toast
 import androidx.core.animation.addListener
+import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
+import com.android.wm.shell.R
 import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener
 import java.util.function.Supplier
 
 /** Animates the task surface moving from its current drag position to its pre-drag position. */
 class ReturnToDragStartAnimator(
+    private val context: Context,
     private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
     private val interactionJankMonitor: InteractionJankMonitor
 ) {
     private var boundsAnimator: Animator? = null
     private lateinit var taskRepositionAnimationListener: OnTaskRepositionAnimationListener
 
-    constructor(interactionJankMonitor: InteractionJankMonitor) :
-            this(Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
+    constructor(context: Context, interactionJankMonitor: InteractionJankMonitor) :
+            this(context, Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
 
     /** Sets a listener for the start and end of the reposition animation. */
     fun setTaskRepositionAnimationListener(listener: OnTaskRepositionAnimationListener) {
@@ -43,7 +48,13 @@
     }
 
     /** Builds new animator and starts animation of task leash reposition. */
-    fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) {
+    fun start(
+        taskId: Int,
+        taskSurface: SurfaceControl,
+        startBounds: Rect,
+        endBounds: Rect,
+        isResizable: Boolean
+    ) {
         val tx = transactionSupplier.get()
 
         boundsAnimator?.cancel()
@@ -76,8 +87,14 @@
                                 .apply()
                             taskRepositionAnimationListener.onAnimationEnd(taskId)
                             boundsAnimator = null
-                            // TODO(b/354658237) - show toast with relevant string
-                            // TODO(b/339582583) - add Jank CUJ using interactionJankMonitor
+                            if (!isResizable) {
+                                Toast.makeText(
+                                    context,
+                                    R.string.desktop_mode_non_resizable_snap_text,
+                                    Toast.LENGTH_SHORT
+                                ).show()
+                            }
+                            interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
                         }
                     )
                     addUpdateListener { anim ->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index bf185a4..96719fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -117,8 +117,8 @@
                             finishCallback.onTransitionFinished(null)
                             initialBounds = null
                             boundsAnimator = null
-                            interactionJankMonitor.end(
-                                Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+                            interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+                            interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
                         }
                     )
                     addUpdateListener { anim ->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt
new file mode 100644
index 0000000..6013e97
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.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.wm.shell.desktopmode.education
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.SystemProperties
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Controls app handle education end to end.
+ *
+ * Listen to the user trigger for app handle education, calls an api to check if the education
+ * should be shown and calls an api to show education.
+ */
+@OptIn(kotlinx.coroutines.FlowPreview::class)
[email protected]
+class AppHandleEducationController(
+    private val appHandleEducationFilter: AppHandleEducationFilter,
+    shellTaskOrganizer: ShellTaskOrganizer,
+    private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository,
+    @ShellMainThread private val applicationCoroutineScope: CoroutineScope
+) {
+  init {
+    runIfEducationFeatureEnabled {
+      // TODO: b/361038716 - Use app handle state flow instead of focus task change flow
+      val focusTaskChangeFlow = focusTaskChangeFlow(shellTaskOrganizer)
+      applicationCoroutineScope.launch {
+        // Central block handling the app's educational flow end-to-end.
+        // This flow listens to the changes to the result of
+        // [WindowingEducationProto#hasEducationViewedTimestampMillis()] in datastore proto object
+        isEducationViewedFlow()
+            .flatMapLatest { isEducationViewed ->
+              if (isEducationViewed) {
+                // If the education is viewed then return emptyFlow() that completes immediately.
+                // This will help us to not listen to focus task changes after the education has
+                // been viewed already.
+                emptyFlow()
+              } else {
+                // This flow listens for focus task changes, which trigger the app handle education.
+                focusTaskChangeFlow
+                    .filter { runningTaskInfo ->
+                      runningTaskInfo.topActivityInfo?.packageName?.let {
+                        appHandleEducationFilter.shouldShowAppHandleEducation(it)
+                      } ?: false && runningTaskInfo.windowingMode != WINDOWING_MODE_FREEFORM
+                    }
+                    .distinctUntilChanged()
+              }
+            }
+            .debounce(
+                APP_HANDLE_EDUCATION_DELAY) // Wait for few seconds, if the focus task changes.
+            // During the delay then current emission will be cancelled.
+            .flowOn(Dispatchers.IO)
+            .collectLatest {
+              // Fire and forget show education suspend function, manage entire lifecycle of
+              // tooltip in UI class.
+            }
+      }
+    }
+  }
+
+  private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
+    if (Flags.enableDesktopWindowingAppHandleEducation()) block()
+  }
+
+  private fun isEducationViewedFlow(): Flow<Boolean> =
+      appHandleEducationDatastoreRepository.dataStoreFlow
+          .map { preferences -> preferences.hasEducationViewedTimestampMillis() }
+          .distinctUntilChanged()
+
+  private fun focusTaskChangeFlow(shellTaskOrganizer: ShellTaskOrganizer): Flow<RunningTaskInfo> =
+      callbackFlow {
+        val focusTaskChange = ShellTaskOrganizer.FocusListener { taskInfo -> trySend(taskInfo) }
+        shellTaskOrganizer.addFocusListener(focusTaskChange)
+        awaitClose { shellTaskOrganizer.removeFocusListener(focusTaskChange) }
+      }
+
+  private companion object {
+    val APP_HANDLE_EDUCATION_DELAY: Long
+      get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
+  }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
new file mode 100644
index 0000000..51bdb40
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt
@@ -0,0 +1,123 @@
+/*
+ * 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.education
+
+import android.annotation.IntegerRes
+import android.app.usage.UsageStatsManager
+import android.content.Context
+import android.os.SystemClock
+import android.provider.Settings.Secure
+import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+import java.time.Duration
+
+/** Filters incoming app handle education triggers based on set conditions. */
+class AppHandleEducationFilter(
+    private val context: Context,
+    private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository
+) {
+  private val usageStatsManager =
+      context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
+
+  /** Returns true if conditions to show app handle education are met, returns false otherwise. */
+  suspend fun shouldShowAppHandleEducation(focusAppPackageName: String): Boolean {
+    val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto()
+    return isFocusAppInAllowlist(focusAppPackageName) &&
+        !isOtherEducationShowing() &&
+        hasSufficientTimeSinceSetup() &&
+        !isEducationViewedBefore(windowingEducationProto) &&
+        !isFeatureUsedBefore(windowingEducationProto) &&
+        hasMinAppUsage(windowingEducationProto, focusAppPackageName)
+  }
+
+  private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean =
+      focusAppPackageName in
+          context.resources.getStringArray(
+              R.array.desktop_windowing_app_handle_education_allowlist_apps)
+
+  // TODO: b/350953004 - Add checks based on App compat
+  // TODO: b/350951797 - Add checks based on PKT tips education
+  private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing()
+
+  private fun isTaskbarEducationShowing(): Boolean =
+      Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1
+
+  private fun hasSufficientTimeSinceSetup(): Boolean =
+      Duration.ofMillis(SystemClock.elapsedRealtime()) >
+          convertIntegerResourceToDuration(
+              R.integer.desktop_windowing_education_required_time_since_setup_seconds)
+
+  private fun isEducationViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
+      windowingEducationProto.hasEducationViewedTimestampMillis()
+
+  private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
+      windowingEducationProto.hasFeatureUsedTimestampMillis()
+
+  private suspend fun hasMinAppUsage(
+      windowingEducationProto: WindowingEducationProto,
+      focusAppPackageName: String
+  ): Boolean =
+      (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >=
+          context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count)
+
+  private suspend fun launchCountByPackageName(
+      windowingEducationProto: WindowingEducationProto
+  ): Map<String, Int> =
+      if (isAppUsageCacheStale(windowingEducationProto)) {
+        // Query and return user stats, update cache in datastore
+        getAndCacheAppUsageStats()
+      } else {
+        // Return cached usage stats
+        windowingEducationProto.appHandleEducation.appUsageStatsMap
+      }
+
+  private fun isAppUsageCacheStale(windowingEducationProto: WindowingEducationProto): Boolean {
+    val currentTime = currentTimeInDuration()
+    val lastUpdateTime =
+        Duration.ofMillis(
+            windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis)
+    val appUsageStatsCachingInterval =
+        convertIntegerResourceToDuration(
+            R.integer.desktop_windowing_education_app_usage_cache_interval_seconds)
+    return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval
+  }
+
+  private suspend fun getAndCacheAppUsageStats(): Map<String, Int> {
+    val currentTime = currentTimeInDuration()
+    val appUsageStats = queryAppUsageStats()
+    appHandleEducationDatastoreRepository.updateAppUsageStats(appUsageStats, currentTime)
+    return appUsageStats
+  }
+
+  private fun queryAppUsageStats(): Map<String, Int> {
+    val endTime = currentTimeInDuration()
+    val appLaunchInterval =
+        convertIntegerResourceToDuration(
+            R.integer.desktop_windowing_education_app_launch_interval_seconds)
+    val startTime = endTime - appLaunchInterval
+
+    return usageStatsManager
+        .queryAndAggregateUsageStats(startTime.toMillis(), endTime.toMillis())
+        .mapValues { it.value.appLaunchCount }
+  }
+
+  private fun convertIntegerResourceToDuration(@IntegerRes resourceId: Int): Duration =
+      Duration.ofSeconds(context.resources.getInteger(resourceId).toLong())
+
+  private fun currentTimeInDuration(): Duration = Duration.ofMillis(System.currentTimeMillis())
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
new file mode 100644
index 0000000..f420c5b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt
@@ -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.wm.shell.desktopmode.education.data
+
+import android.content.Context
+import android.util.Log
+import androidx.datastore.core.CorruptionException
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.core.Serializer
+import androidx.datastore.dataStoreFile
+import com.android.framework.protobuf.InvalidProtocolBufferException
+import com.android.internal.annotations.VisibleForTesting
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+import java.time.Duration
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.first
+
+/**
+ * Manages interactions with the App Handle education datastore.
+ *
+ * This class provides a layer of abstraction between the UI/business logic and the underlying
+ * DataStore.
+ */
+class AppHandleEducationDatastoreRepository
+@VisibleForTesting
+constructor(private val dataStore: DataStore<WindowingEducationProto>) {
+  constructor(
+      context: Context
+  ) : this(
+      DataStoreFactory.create(
+          serializer = WindowingEducationProtoSerializer,
+          produceFile = { context.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_FILEPATH) }))
+
+  /** Provides dataStore.data flow and handles exceptions thrown during collection */
+  val dataStoreFlow: Flow<WindowingEducationProto> =
+      dataStore.data.catch { exception ->
+        // dataStore.data throws an IOException when an error is encountered when reading data
+        if (exception is IOException) {
+          Log.e(
+              TAG,
+              "Error in reading app handle education related data from datastore, data is " +
+                  "stored in a file named $APP_HANDLE_EDUCATION_DATASTORE_FILEPATH",
+              exception)
+        } else {
+          throw exception
+        }
+      }
+
+  /**
+   * Reads and returns the [WindowingEducationProto] Proto object from the DataStore. If the
+   * DataStore is empty or there's an error reading, it returns the default value of Proto.
+   */
+  suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first()
+
+  /**
+   * Updates [AppHandleEducation.appUsageStats] and
+   * [AppHandleEducation.appUsageStatsLastUpdateTimestampMillis] fields in datastore with
+   * [appUsageStats] and [appUsageStatsLastUpdateTimestamp].
+   */
+  suspend fun updateAppUsageStats(
+      appUsageStats: Map<String, Int>,
+      appUsageStatsLastUpdateTimestamp: Duration
+  ) {
+    val currentAppHandleProto = windowingEducationProto().appHandleEducation.toBuilder()
+    currentAppHandleProto
+        .putAllAppUsageStats(appUsageStats)
+        .setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestamp.toMillis())
+    dataStore.updateData { preferences: WindowingEducationProto ->
+      preferences.toBuilder().setAppHandleEducation(currentAppHandleProto).build()
+    }
+  }
+
+  companion object {
+    private const val TAG = "AppHandleEducationDatastoreRepository"
+    private const val APP_HANDLE_EDUCATION_DATASTORE_FILEPATH = "app_handle_education.pb"
+
+    object WindowingEducationProtoSerializer : Serializer<WindowingEducationProto> {
+
+      override val defaultValue: WindowingEducationProto =
+          WindowingEducationProto.getDefaultInstance()
+
+      override suspend fun readFrom(input: InputStream): WindowingEducationProto =
+          try {
+            WindowingEducationProto.parseFrom(input)
+          } catch (exception: InvalidProtocolBufferException) {
+            throw CorruptionException("Cannot read proto.", exception)
+          }
+
+      override suspend fun writeTo(windowingProto: WindowingEducationProto, output: OutputStream) =
+          windowingProto.writeTo(output)
+    }
+  }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
new file mode 100644
index 0000000..d29ec53
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+option java_package = "com.android.wm.shell.desktopmode.education.data";
+option java_multiple_files = true;
+
+// Desktop Windowing education data
+message WindowingEducationProto {
+  // Timestamp in milliseconds of when the education was last viewed.
+  optional int64 education_viewed_timestamp_millis = 1;
+  // Timestamp in milliseconds of when the feature was last used.
+  optional int64 feature_used_timestamp_millis = 2;
+  oneof education_data {
+    // Fields specific to app handle education
+    AppHandleEducation app_handle_education = 3;
+  }
+
+  message AppHandleEducation {
+    // Map that stores app launch count for corresponding package
+    map<string, int32> app_usage_stats = 1;
+    // Timestamp of when app_usage_stats was last cached
+    optional int64 app_usage_stats_last_update_timestamp_millis = 2;
+  }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
index 0acc7df..faa97ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md
@@ -98,9 +98,8 @@
 ### Exposing shared code for use in Launcher
 Launcher doesn't currently build against the Shell library, but needs to have access to some shared
 AIDL interfaces and constants.  Currently, all AIDL files, and classes under the
-`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that
+`com.android.wm.shell.shared` package are automatically built into the `SystemUISharedLib` that
 Launcher uses.
 
-If the new code doesn't fall into those categories, they can be added explicitly in the Shell's
-[Android.bp](/libs/WindowManager/Shell/Android.bp) file under the
-`wm_shell_util-sources` filegroup.
\ No newline at end of file
+If the new code doesn't fall into those categories, they should be moved to the Shell shared
+package (`com.android.wm.shell.shared`) under the `WindowManager-Shell-shared` library.
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index 84f6af41..72d1a76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -27,10 +27,13 @@
   traces in Winscope)
 
 ### Kotlin
+Kotlin protologging is supported but not as optimized as in Java.
 
-Protolog tool does not yet have support for Kotlin code (see [b/168581922](https://b.corp.google.com/issues/168581922)).
-For logging in Kotlin, use the [KtProtoLog](/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt)
-class which has a similar API to the Java ProtoLog class.
+The Protolog tool does not yet have support for Kotlin code ([b/168581922](https://b.corp.google.com/issues/168581922)).
+
+What this implies is that ProtoLogs are not pre-processed to extract the static strings out when used in Kotlin. So,
+there is no memory gain when using ProtoLogging in Kotlin. The logs will still be traced to Perfetto, but with a subtly
+worse performance due to the additional string interning that needs to be done at run time instead of at build time.
 
 ### Enabling ProtoLog command line logging
 Run these commands to enable protologs (in logcat) for WM Core ([list of all core tags](/core/java/com/android/internal/protolog/ProtoLogGroup.java)):
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java
deleted file mode 100644
index 20da54e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropConstants.java
+++ /dev/null
@@ -1,27 +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.wm.shell.draganddrop;
-
-/** Constants that can be used by both Shell and other users of the library, e.g. Launcher */
-public class DragAndDropConstants {
-
-    /**
-     * An Intent extra that Launcher can use to specify a region of the screen where Shell should
-     * ignore drag events.
-     */
-    public static final String EXTRA_DISALLOW_HIT_REGION = "DISALLOW_HIT_REGION";
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index e00353d..cf02fb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -32,7 +32,7 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_DRAG_AND_DROP;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -70,6 +70,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.annotations.ExternalMainThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
@@ -127,7 +128,7 @@
          * drag.
          */
         default boolean onUnhandledDrag(@NonNull PendingIntent launchIntent,
-                @NonNull SurfaceControl dragSurface,
+                @NonNull DragEvent dragEvent,
                 @NonNull Consumer<Boolean> onFinishCallback) {
             return false;
         }
@@ -329,9 +330,18 @@
             return false;
         }
 
+        DragSession dragSession = null;
         if (event.getAction() == ACTION_DRAG_STARTED) {
             mActiveDragDisplay = displayId;
-            pd.isHandlingDrag = DragUtils.canHandleDrag(event);
+            dragSession = new DragSession(ActivityTaskManager.getInstance(),
+                    mDisplayController.getDisplayLayout(displayId), event.getClipData(),
+                    event.getDragFlags());
+            dragSession.initialize();
+            final ActivityManager.RunningTaskInfo taskInfo = dragSession.runningTaskInfo;
+            // Desktop tasks will have their own drag handling.
+            final boolean isDesktopDrag = taskInfo != null && taskInfo.isFreeform()
+                    && DesktopModeStatus.canEnterDesktopMode(mContext);
+            pd.isHandlingDrag = DragUtils.canHandleDrag(event) && !isDesktopDrag;
             ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
                     "Clip description: handlingDrag=%b itemCount=%d mimeTypes=%s flags=%s",
                     pd.isHandlingDrag, event.getClipData().getItemCount(),
@@ -349,10 +359,7 @@
                     Slog.w(TAG, "Unexpected drag start during an active drag");
                     return false;
                 }
-                pd.dragSession = new DragSession(ActivityTaskManager.getInstance(),
-                        mDisplayController.getDisplayLayout(displayId), event.getClipData(),
-                        event.getDragFlags());
-                pd.dragSession.initialize();
+                pd.dragSession = dragSession;
                 pd.activeDragCount++;
                 pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
                 if (pd.dragSession.hideDragSourceTaskId != -1) {
@@ -437,7 +444,7 @@
         }
 
         final boolean handled = notifyListeners(
-                l -> l.onUnhandledDrag(launchIntent, dragEvent.getDragSurface(), onFinishCallback));
+                l -> l.onUnhandledDrag(launchIntent, dragEvent, onFinishCallback));
         if (!handled) {
             // Nobody handled this, we still have to notify WM
             onFinishCallback.accept(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 7e03624..6fec0c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -32,15 +32,15 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.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.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.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -69,8 +69,8 @@
 import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.lang.annotation.Retention;
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 d03a561..3fecbe7 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
@@ -21,15 +21,14 @@
 import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS;
 import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+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 android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -61,9 +60,9 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 
 import java.io.PrintWriter;
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 18cd2d8..f9749ec 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
@@ -16,10 +16,9 @@
 
 package com.android.wm.shell.draganddrop;
 
-import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_IN;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
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 456767a..83cc18b 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
@@ -27,6 +27,7 @@
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -49,6 +50,7 @@
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
     private final WindowDecorViewModel mWindowDecorationViewModel;
+    private final LaunchAdjacentController mLaunchAdjacentController;
 
     private final SparseArray<State> mTasks = new SparseArray<>();
 
@@ -62,11 +64,13 @@
             ShellInit shellInit,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
+            LaunchAdjacentController launchAdjacentController,
             WindowDecorViewModel windowDecorationViewModel) {
         mContext = context;
         mShellTaskOrganizer = shellTaskOrganizer;
         mWindowDecorationViewModel = windowDecorationViewModel;
         mDesktopModeTaskRepository = desktopModeTaskRepository;
+        mLaunchAdjacentController = launchAdjacentController;
         if (shellInit != null) {
             shellInit.addInitCallback(this::onInit, this);
         }
@@ -106,6 +110,7 @@
                 }
             });
         }
+        updateLaunchAdjacentController();
     }
 
     @Override
@@ -123,6 +128,7 @@
         if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
             mWindowDecorationViewModel.destroyWindowDecoration(taskInfo);
         }
+        updateLaunchAdjacentController();
     }
 
     @Override
@@ -144,6 +150,17 @@
                         taskInfo.isVisible);
             });
         }
+        updateLaunchAdjacentController();
+    }
+
+    private void updateLaunchAdjacentController() {
+        for (int i = 0; i < mTasks.size(); i++) {
+            if (mTasks.valueAt(i).mTaskInfo.isVisible) {
+                mLaunchAdjacentController.setLaunchAdjacentEnabled(false);
+                return;
+            }
+        }
+        mLaunchAdjacentController.setLaunchAdjacentEnabled(true);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index 8402775..1ffa541 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -19,6 +19,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
+import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -37,8 +39,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -56,7 +60,9 @@
     private final Context mContext;
     private final Transitions mTransitions;
     private final WindowDecorViewModel mWindowDecorViewModel;
+    private final DesktopModeTaskRepository mDesktopModeTaskRepository;
     private final DisplayController mDisplayController;
+    private final InteractionJankMonitor mInteractionJankMonitor;
     private final ShellExecutor mMainExecutor;
     private final ShellExecutor mAnimExecutor;
 
@@ -71,11 +77,15 @@
             WindowDecorViewModel windowDecorViewModel,
             DisplayController displayController,
             ShellExecutor mainExecutor,
-            ShellExecutor animExecutor) {
+            ShellExecutor animExecutor,
+            DesktopModeTaskRepository desktopModeTaskRepository,
+            InteractionJankMonitor interactionJankMonitor) {
         mTransitions = transitions;
         mContext = context;
         mWindowDecorViewModel = windowDecorViewModel;
+        mDesktopModeTaskRepository = desktopModeTaskRepository;
         mDisplayController = displayController;
+        mInteractionJankMonitor = interactionJankMonitor;
         mMainExecutor = mainExecutor;
         mAnimExecutor = animExecutor;
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -107,9 +117,11 @@
     }
 
     @Override
-    public void startMinimizedModeTransition(WindowContainerTransaction wct) {
+    public IBinder startMinimizedModeTransition(WindowContainerTransaction wct) {
         final int type = WindowManager.TRANSIT_TO_BACK;
-        mPendingTransitionTokens.add(mTransitions.startTransition(type, wct, this));
+        final IBinder token = mTransitions.startTransition(type, wct, this);
+        mPendingTransitionTokens.add(token);
+        return token;
     }
 
 
@@ -238,13 +250,22 @@
                     startBounds.top + (animation.getAnimatedFraction() * screenHeight));
             t.apply();
         });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                animations.remove(animator);
-                onAnimFinish.run();
-            }
-        });
+        if (mDesktopModeTaskRepository.getActiveNonMinimizedTaskCount(
+                        change.getTaskInfo().displayId) == 1) {
+            // Starting the jank trace if closing the last window in desktop mode.
+            mInteractionJankMonitor.begin(
+                    sc, mContext, CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
+        }
+        animator.addListener(
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        animations.remove(animator);
+                        onAnimFinish.run();
+                        mInteractionJankMonitor.end(
+                                CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
+                    }
+                });
         animations.add(animator);
         return true;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
index 8da4c6a..ea68a69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionStarter.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.freeform;
 
+import android.os.IBinder;
 import android.window.WindowContainerTransaction;
 
 /**
@@ -38,8 +39,9 @@
      *
      * @param wct the {@link WindowContainerTransaction} that changes the windowing mode
      *
+     * @return the started transition
      */
-    void startMinimizedModeTransition(WindowContainerTransaction wct);
+    IBinder startMinimizedModeTransition(WindowContainerTransaction wct);
 
     /**
      * Starts close window transition
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 93351c3..83b5bf6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -9,3 +9,5 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 962309f..1827923 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -23,7 +23,7 @@
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
 import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED;
 
 import android.annotation.BinderThread;
 import android.content.ComponentName;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 852382d..4df649c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -40,9 +40,10 @@
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
+import com.android.wm.shell.shared.pip.PipContentOverlay;
 import com.android.wm.shell.transition.Transitions;
 
 import java.lang.annotation.Retention;
@@ -418,7 +419,7 @@
         }
 
         SurfaceControl getContentOverlayLeash() {
-            return mContentOverlay == null ? null : mContentOverlay.mLeash;
+            return mContentOverlay == null ? null : mContentOverlay.getLeash();
         }
 
         void setColorContentOverlay(Context context) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
deleted file mode 100644
index ff2d46e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java
+++ /dev/null
@@ -1,269 +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.wm.shell.pip;
-
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.TypedValue;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.TaskSnapshot;
-
-/**
- * Represents the content overlay used during the entering PiP animation.
- */
-public abstract class PipContentOverlay {
-    // Fixed string used in WMShellFlickerTests
-    protected static final String LAYER_NAME = "PipContentOverlay";
-
-    protected SurfaceControl mLeash;
-
-    /** Attaches the internal {@link #mLeash} to the given parent leash. */
-    public abstract void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash);
-
-    /** Detaches the internal {@link #mLeash} from its parent by removing itself. */
-    public void detach(SurfaceControl.Transaction tx) {
-        if (mLeash != null && mLeash.isValid()) {
-            tx.remove(mLeash);
-            tx.apply();
-        }
-    }
-
-    @Nullable
-    public SurfaceControl getLeash() {
-        return mLeash;
-    }
-
-    /**
-     * Animates the internal {@link #mLeash} by a given fraction.
-     * @param atomicTx {@link SurfaceControl.Transaction} to operate, you should not explicitly
-     *                 call apply on this transaction, it should be applied on the caller side.
-     * @param currentBounds {@link Rect} of the current animation bounds.
-     * @param fraction progress of the animation ranged from 0f to 1f.
-     */
-    public abstract void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
-            Rect currentBounds, float fraction);
-
-    /** A {@link PipContentOverlay} uses solid color. */
-    public static final class PipColorOverlay extends PipContentOverlay {
-        private static final String TAG = PipColorOverlay.class.getSimpleName();
-
-        private final Context mContext;
-
-        public PipColorOverlay(Context context) {
-            mContext = context;
-            mLeash = new SurfaceControl.Builder(new SurfaceSession())
-                    .setCallsite(TAG)
-                    .setName(LAYER_NAME)
-                    .setColorLayer()
-                    .build();
-        }
-
-        @Override
-        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
-            tx.show(mLeash);
-            tx.setLayer(mLeash, Integer.MAX_VALUE);
-            tx.setColor(mLeash, getContentOverlayColor(mContext));
-            tx.setAlpha(mLeash, 0f);
-            tx.reparent(mLeash, parentLeash);
-            tx.apply();
-        }
-
-        @Override
-        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
-                Rect currentBounds, float fraction) {
-            atomicTx.setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
-        }
-
-        private float[] getContentOverlayColor(Context context) {
-            final TypedArray ta = context.obtainStyledAttributes(new int[] {
-                    android.R.attr.colorBackground });
-            try {
-                int colorAccent = ta.getColor(0, 0);
-                return new float[] {
-                        Color.red(colorAccent) / 255f,
-                        Color.green(colorAccent) / 255f,
-                        Color.blue(colorAccent) / 255f };
-            } finally {
-                ta.recycle();
-            }
-        }
-    }
-
-    /** A {@link PipContentOverlay} uses {@link TaskSnapshot}. */
-    public static final class PipSnapshotOverlay extends PipContentOverlay {
-        private static final String TAG = PipSnapshotOverlay.class.getSimpleName();
-
-        private final TaskSnapshot mSnapshot;
-        private final Rect mSourceRectHint;
-
-        public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) {
-            mSnapshot = snapshot;
-            mSourceRectHint = new Rect(sourceRectHint);
-            mLeash = new SurfaceControl.Builder(new SurfaceSession())
-                    .setCallsite(TAG)
-                    .setName(LAYER_NAME)
-                    .build();
-        }
-
-        @Override
-        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
-            final float taskSnapshotScaleX = (float) mSnapshot.getTaskSize().x
-                    / mSnapshot.getHardwareBuffer().getWidth();
-            final float taskSnapshotScaleY = (float) mSnapshot.getTaskSize().y
-                    / mSnapshot.getHardwareBuffer().getHeight();
-            tx.show(mLeash);
-            tx.setLayer(mLeash, Integer.MAX_VALUE);
-            tx.setBuffer(mLeash, mSnapshot.getHardwareBuffer());
-            // Relocate the content to parentLeash's coordinates.
-            tx.setPosition(mLeash, -mSourceRectHint.left, -mSourceRectHint.top);
-            tx.setScale(mLeash, taskSnapshotScaleX, taskSnapshotScaleY);
-            tx.reparent(mLeash, parentLeash);
-            tx.apply();
-        }
-
-        @Override
-        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
-                Rect currentBounds, float fraction) {
-            // Do nothing. Keep the snapshot till animation ends.
-        }
-    }
-
-    /** A {@link PipContentOverlay} shows app icon on solid color background. */
-    public static final class PipAppIconOverlay extends PipContentOverlay {
-        private static final String TAG = PipAppIconOverlay.class.getSimpleName();
-        // The maximum size for app icon in pixel.
-        private static final int MAX_APP_ICON_SIZE_DP = 72;
-
-        private final Context mContext;
-        private final int mAppIconSizePx;
-        private final Rect mAppBounds;
-        private final int mOverlayHalfSize;
-        private final Matrix mTmpTransform = new Matrix();
-        private final float[] mTmpFloat9 = new float[9];
-
-        private Bitmap mBitmap;
-
-        public PipAppIconOverlay(Context context, Rect appBounds, Rect destinationBounds,
-                Drawable appIcon, int appIconSizePx) {
-            mContext = context;
-            final int maxAppIconSizePx = (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP,
-                    MAX_APP_ICON_SIZE_DP, context.getResources().getDisplayMetrics());
-            mAppIconSizePx = Math.min(maxAppIconSizePx, appIconSizePx);
-
-            final int overlaySize = getOverlaySize(appBounds, destinationBounds);
-            mOverlayHalfSize = overlaySize >> 1;
-
-            // When the activity is in the secondary split, make sure the scaling center is not
-            // offset.
-            mAppBounds = new Rect(0, 0, appBounds.width(), appBounds.height());
-
-            mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888);
-            prepareAppIconOverlay(appIcon);
-            mLeash = new SurfaceControl.Builder(new SurfaceSession())
-                    .setCallsite(TAG)
-                    .setName(LAYER_NAME)
-                    .build();
-        }
-
-        /**
-         * Returns the size of the app icon overlay.
-         *
-         * In order to have the overlay always cover the pip window during the transition,
-         * the overlay will be drawn with the max size of the start and end bounds in different
-         * rotation.
-         */
-        public static int getOverlaySize(Rect appBounds, Rect destinationBounds) {
-            final int appWidth = appBounds.width();
-            final int appHeight = appBounds.height();
-
-            return Math.max(Math.max(appWidth, appHeight),
-                    Math.max(destinationBounds.width(), destinationBounds.height())) + 1;
-        }
-
-        @Override
-        public void attach(SurfaceControl.Transaction tx, SurfaceControl parentLeash) {
-            tx.show(mLeash);
-            tx.setLayer(mLeash, Integer.MAX_VALUE);
-            tx.setBuffer(mLeash, mBitmap.getHardwareBuffer());
-            tx.setAlpha(mLeash, 0f);
-            tx.reparent(mLeash, parentLeash);
-            tx.apply();
-        }
-
-        @Override
-        public void onAnimationUpdate(SurfaceControl.Transaction atomicTx,
-                Rect currentBounds, float fraction) {
-            mTmpTransform.reset();
-            // In order for the overlay to always cover the pip window, the overlay may have a
-            // size larger than the pip window. Make sure that app icon is at the center.
-            final int appBoundsCenterX = mAppBounds.centerX();
-            final int appBoundsCenterY = mAppBounds.centerY();
-            mTmpTransform.setTranslate(
-                    appBoundsCenterX - mOverlayHalfSize,
-                    appBoundsCenterY - mOverlayHalfSize);
-            // Scale back the bitmap with the pivot point at center.
-            final float scale = Math.min(
-                    (float) mAppBounds.width() / currentBounds.width(),
-                    (float) mAppBounds.height() / currentBounds.height());
-            mTmpTransform.postScale(scale, scale, appBoundsCenterX, appBoundsCenterY);
-            atomicTx.setMatrix(mLeash, mTmpTransform, mTmpFloat9)
-                    .setAlpha(mLeash, fraction < 0.5f ? 0 : (fraction - 0.5f) * 2);
-        }
-
-        @Override
-        public void detach(SurfaceControl.Transaction tx) {
-            super.detach(tx);
-            if (mBitmap != null && !mBitmap.isRecycled()) {
-                mBitmap.recycle();
-            }
-        }
-
-        private void prepareAppIconOverlay(Drawable appIcon) {
-            final Canvas canvas = new Canvas();
-            canvas.setBitmap(mBitmap);
-            final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
-                    android.R.attr.colorBackground });
-            try {
-                int colorAccent = ta.getColor(0, 0);
-                canvas.drawRGB(
-                        Color.red(colorAccent),
-                        Color.green(colorAccent),
-                        Color.blue(colorAccent));
-            } finally {
-                ta.recycle();
-            }
-            final Rect appIconBounds = new Rect(
-                    mOverlayHalfSize - mAppIconSizePx / 2,
-                    mOverlayHalfSize - mAppIconSizePx / 2,
-                    mOverlayHalfSize + mAppIconSizePx / 2,
-                    mOverlayHalfSize + mAppIconSizePx / 2);
-            appIcon.setBounds(appIconBounds);
-            appIcon.draw(canvas);
-            mBitmap = mBitmap.copy(Bitmap.Config.HARDWARE, false /* mutable */);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 428cc91..ab222c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -25,8 +25,6 @@
 
 import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
 import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.FRACTION_START;
@@ -42,6 +40,8 @@
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isRemovePipDirection;
+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 static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
@@ -76,7 +76,6 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
@@ -90,7 +89,9 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.pip.PipContentOverlay;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.transition.Transitions;
 
@@ -98,6 +99,7 @@
 import java.lang.ref.WeakReference;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.StringJoiner;
 import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
@@ -361,8 +363,7 @@
     SurfaceControl mPipOverlay;
 
     /**
-     * The app bounds used for the buffer size of the
-     * {@link com.android.wm.shell.pip.PipContentOverlay.PipAppIconOverlay}.
+     * The app bounds used for the buffer size of the {@link PipContentOverlay.PipAppIconOverlay}.
      *
      * Note that this is empty if the overlay is removed or if it's some other type of overlay
      * defined in {@link PipContentOverlay}.
@@ -496,7 +497,9 @@
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                 "startSwipePipToHome: %s, state=%s", componentName, mPipTransitionState);
         mPipTransitionState.setInSwipePipToHomeTransition(true);
-        if (!ENABLE_SHELL_TRANSITIONS) {
+        if (ENABLE_SHELL_TRANSITIONS) {
+            mPipTransitionController.onStartSwipePipToHome();
+        } else {
             sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP);
         }
         setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
@@ -693,6 +696,16 @@
                 return;
             }
 
+            if (mSplitScreenOptional.isPresent()) {
+                // If pip activity will reparent to origin task case and if the origin task still
+                // under split root, apply exit split transaction to make it expand to fullscreen.
+                SplitScreenController split = mSplitScreenOptional.get();
+                if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) {
+                    split.prepareExitSplitScreen(wct, split.getStageOfTask(
+                            mTaskInfo.lastParentTaskIdBeforePip),
+                            SplitScreenController.EXIT_REASON_APP_FINISHED);
+                }
+            }
             mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds);
             return;
         }
@@ -819,6 +832,7 @@
                     mPictureInPictureParams.getTitle());
             mPipParamsChangedForwarder.notifySubtitleChanged(
                     mPictureInPictureParams.getSubtitle());
+            logRemoteActions(mPictureInPictureParams);
         }
 
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
@@ -1100,6 +1114,7 @@
         }
         applyNewPictureInPictureParams(newParams);
         mPictureInPictureParams = newParams;
+        logRemoteActions(mPictureInPictureParams);
     }
 
     @Override
@@ -1408,6 +1423,16 @@
         }
     }
 
+    private void logRemoteActions(@NonNull PictureInPictureParams params) {
+        StringJoiner sj = new StringJoiner("|", "[", "]");
+        if (params.hasSetActions()) {
+            params.getActions().forEach((action) -> sj.add(action.getTitle()));
+        }
+
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: PIP remote actions=%s", TAG, sj.toString());
+    }
+
     /**
      * Animates resizing of the pinned stack given the duration.
      */
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 ba97c832..05d1984 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
@@ -41,6 +41,7 @@
 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
 import static com.android.wm.shell.pip.PipTransitionState.ENTERED_PIP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_CLEANUP_PIP_EXIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP_TO_SPLIT;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP;
@@ -74,6 +75,7 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.pip.PipContentOverlay;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.CounterRotatorHelper;
@@ -122,6 +124,8 @@
     @Nullable
     private IBinder mMoveToBackTransition;
     private IBinder mRequestedEnterTransition;
+    private IBinder mCleanupTransition;
+
     private WindowContainerToken mRequestedEnterTask;
     /** The Task window that is currently in PIP windowing mode. */
     @Nullable
@@ -191,6 +195,11 @@
     }
 
     @Override
+    protected boolean isInSwipePipToHomeTransition() {
+        return mPipTransitionState.getInSwipePipToHomeTransition();
+    }
+
+    @Override
     public void startExitTransition(int type, WindowContainerTransaction out,
             @Nullable Rect destinationBounds) {
         if (destinationBounds != null) {
@@ -227,10 +236,12 @@
 
         // Exiting PIP.
         final int type = info.getType();
-        if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition)) {
+        if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition)
+                || transition.equals(mCleanupTransition)) {
             mExitDestinationBounds.setEmpty();
             mExitTransition = null;
             mMoveToBackTransition = null;
+            mCleanupTransition = null;
             mHasFadeOut = false;
             if (mFinishCallback != null) {
                 callFinishCallback(null /* wct */);
@@ -264,6 +275,9 @@
                     removePipImmediately(info, startTransaction, finishTransaction, finishCallback,
                             pipTaskInfo);
                     break;
+                case TRANSIT_CLEANUP_PIP_EXIT:
+                    cleanupPipExitTransition(startTransaction, finishCallback);
+                    break;
                 default:
                     throw new IllegalStateException("mExitTransition with unexpected transit type="
                             + transitTypeToString(type));
@@ -285,6 +299,20 @@
 
         // Entering PIP.
         if (isEnteringPip(info)) {
+            if (!mPipTransitionState.isInPip() && TransitionUtil.hasDisplayChange(info)) {
+                final TransitionInfo.Change pipChange = getPipChange(info);
+                if (pipChange != null) {
+                    // Clear old crop.
+                    updatePipForUnhandledTransition(pipChange, startTransaction, finishTransaction);
+                }
+                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                        "%s: ignore exited PiP with display change", TAG);
+                // This should be an exited pip. E.g. a display change transition happens when
+                // the exiting pip is animating, then mergeAnimation -> end -> onFinishResize ->
+                // onExitPipFinished was called, i.e. pip state is UNDEFINED. So do not handle
+                // the incoming transition as entering pip.
+                return false;
+            }
             if (handleEnteringPipWithDisplayChange(transition, info, startTransaction,
                     finishTransaction, finishCallback)) {
                 // The destination position is applied directly and let default transition handler
@@ -749,7 +777,19 @@
                 mPipAnimationController.resetAnimatorState();
                 finishTransaction.remove(pipLeash);
             }
-            finishCallback.onTransitionFinished(wct);
+
+            if (mFixedRotationState == FIXED_ROTATION_TRANSITION) {
+                // TODO(b/358226697): start a new transition with the WCT instead of applying it in
+                //  the {@link finishCallback}, to ensure shell creates a transition for it.
+                finishCallback.onTransitionFinished(wct);
+            } else {
+                // Apply wct in separate transition so that it can be correctly handled by the
+                // {@link FreeformTaskTransitionObserver} when desktop windowing (which does not
+                // utilize fixed rotation transitions for exiting pip) is enabled (See b/288910069).
+                mCleanupTransition = mTransitions.startTransition(
+                        TRANSIT_CLEANUP_PIP_EXIT, wct, this);
+                finishCallback.onTransitionFinished(null);
+            }
         };
         mFinishTransaction = finishTransaction;
 
@@ -895,6 +935,16 @@
         finishCallback.onTransitionFinished(null);
     }
 
+    /**
+     * For {@link Transitions#TRANSIT_CLEANUP_PIP_EXIT} which applies final config changes needed
+     * after the exit from pip transition animation finishes.
+     */
+    private void cleanupPipExitTransition(@NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        startTransaction.apply();
+        finishCallback.onTransitionFinished(null);
+    }
+
     /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */
     private boolean isEnteringPip(@NonNull TransitionInfo info) {
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
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 fc9e2be..9b81581 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
@@ -189,6 +189,32 @@
         mPipTransitionCallbacks.put(callback, executor);
     }
 
+    protected void onStartSwipePipToHome() {
+        if (Flags.enablePipUiStateCallbackOnEntering()) {
+            try {
+                ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
+                        new PictureInPictureUiState.Builder()
+                                .setTransitioningToPip(true)
+                                .build());
+            } catch (RemoteException | IllegalStateException e) {
+                ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                        "Failed to set alert PiP state change.");
+            }
+        }
+    }
+
+    /**
+     * Used in {@link #sendOnPipTransitionStarted(int)} to decide whether we should send the
+     * PictureInPictureUiState change callback on transition start.
+     * For instance, in auto-enter-pip case, {@link #onStartSwipePipToHome()} should have signaled
+     * the app, and we can return {@code true} here to avoid double callback.
+     *
+     * @return {@code true} if there is a ongoing swipe pip to home transition.
+     */
+    protected boolean isInSwipePipToHomeTransition() {
+        return false;
+    }
+
     protected void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
         final Rect pipBounds = mPipBoundsState.getBounds();
@@ -199,7 +225,8 @@
             entry.getValue().execute(
                     () -> entry.getKey().onPipTransitionStarted(direction, pipBounds));
         }
-        if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()) {
+        if (isInPipDirection(direction) && Flags.enablePipUiStateCallbackOnEntering()
+                && !isInSwipePipToHomeTransition()) {
             try {
                 ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
                         new PictureInPictureUiState.Builder()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index da6221e..755e958 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -32,7 +32,7 @@
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -240,6 +240,12 @@
      */
     private final DisplayChangeController.OnDisplayChangingListener mRotationController = (
             displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> {
+        if (fromRotation == toRotation) {
+            // OnDisplayChangingListener also gets triggered upon Display size changes;
+            // in PiP1, those are handled separately by OnDisplaysChangedListener callbacks.
+            return;
+        }
+
         if (mPipTransitionController.handleRotateDisplay(fromRotation, toRotation, t)) {
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index f929389..06d2311 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -35,10 +35,10 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.DismissViewUtils;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import kotlin.Unit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 0d7f7f6..c8b52c6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -63,11 +63,11 @@
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 999ab95..82fbfad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -39,7 +39,6 @@
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipPerfHintController;
@@ -49,6 +48,7 @@
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
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/PipEnterExitAnimator.java
index 8a9302b..8ebdc96 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/PipEnterExitAnimator.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.content.Context;
 import android.graphics.Rect;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 import androidx.annotation.NonNull;
@@ -51,8 +52,10 @@
 
     @NonNull private final SurfaceControl mLeash;
     private final SurfaceControl.Transaction mStartTransaction;
-    private final int mEnterAnimationDuration;
+    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;
@@ -62,37 +65,59 @@
     private final Rect mStartBounds = new Rect();
     private final Rect mEndBounds = new Rect();
 
+    @Nullable private final Rect mSourceRectHint;
+    private final Rect mSourceRectHintInsets = new Rect();
+    private final Rect mZeroInsets = new Rect(0, 0, 0, 0);
+
     // Bounds updated by the evaluator as animator is running.
     private final Rect mAnimatedRect = new Rect();
 
     private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
     private final RectEvaluator mRectEvaluator;
+    private final RectEvaluator mInsetEvaluator;
     private final PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
 
     public PipEnterExitAnimator(Context context,
             @NonNull SurfaceControl leash,
             SurfaceControl.Transaction startTransaction,
+            SurfaceControl.Transaction finishTransaction,
             @NonNull Rect baseBounds,
             @NonNull Rect startBounds,
             @NonNull Rect endBounds,
-            @BOUNDS int direction) {
+            @Nullable Rect sourceRectHint,
+            @BOUNDS int direction,
+            @Surface.Rotation int rotation) {
         mLeash = leash;
         mStartTransaction = startTransaction;
+        mFinishTransaction = finishTransaction;
         mBaseBounds.set(baseBounds);
         mStartBounds.set(startBounds);
         mAnimatedRect.set(startBounds);
         mEndBounds.set(endBounds);
         mRectEvaluator = new RectEvaluator(mAnimatedRect);
+        mInsetEvaluator = new RectEvaluator(new Rect());
         mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context);
         mDirection = direction;
+        mRotation = rotation;
+
+        mSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) : null;
+        if (mSourceRectHint != null) {
+            mSourceRectHintInsets.set(
+                    mSourceRectHint.left - mBaseBounds.left,
+                    mSourceRectHint.top - mBaseBounds.top,
+                    mBaseBounds.right - mSourceRectHint.right,
+                    mBaseBounds.bottom - mSourceRectHint.bottom
+            );
+        }
 
         mSurfaceControlTransactionFactory =
                 new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
-        mEnterAnimationDuration = context.getResources()
+        mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipEnterAnimationDuration);
 
-        setDuration(mEnterAnimationDuration);
+        setObjectValues(startBounds, endBounds);
+        setDuration(mEnterExitAnimationDuration);
         setEvaluator(mRectEvaluator);
         addListener(this);
         addUpdateListener(this);
@@ -118,6 +143,14 @@
 
     @Override
     public void onAnimationEnd(@NonNull Animator animation) {
+        if (mFinishTransaction != null) {
+            // 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());
+        }
         if (mAnimationEndCallback != null) {
             mAnimationEndCallback.run();
         }
@@ -127,19 +160,32 @@
     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
 
-        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, null,
-                mBaseBounds, mAnimatedRect, null, isInPipDirection(), fraction)
+        mPipSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint,
+                mBaseBounds, mAnimatedRect, insets, isInPipDirection(), fraction)
                 .round(tx, mLeash, isInPipDirection())
                 .shadow(tx, mLeash, isInPipDirection());
         tx.apply();
     }
 
+    private Rect getInsets(float fraction) {
+        Rect startInsets = isInPipDirection() ? mZeroInsets : mSourceRectHintInsets;
+        Rect endInsets = isInPipDirection() ? mSourceRectHintInsets : 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/animation/PipResizeAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
index 88f9e4c..d565776 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipResizeAnimator.java
@@ -134,9 +134,10 @@
             Rect baseBounds, Rect targetBounds, float degrees) {
         Matrix transformTensor = new Matrix();
         final float[] mMatrixTmp = new float[9];
-        final float scale = (float) targetBounds.width() / baseBounds.width();
+        final float scaleX = (float) targetBounds.width() / baseBounds.width();
+        final float scaleY = (float) targetBounds.height() / baseBounds.height();
 
-        transformTensor.setScale(scale, scale);
+        transformTensor.setScale(scaleX, scaleY);
         transformTensor.postTranslate(targetBounds.left, targetBounds.top);
         transformTensor.postRotate(degrees, targetBounds.centerX(), targetBounds.centerY());
 
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 dc21f82..e9c4c14 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
@@ -19,7 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_PIP;
 
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
@@ -29,7 +29,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaInfo;
 import android.window.WindowContainerTransaction;
@@ -200,17 +199,8 @@
         DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
         mPipDisplayLayoutState.setDisplayLayout(layout);
 
-        mDisplayController.addDisplayWindowListener(this);
         mDisplayController.addDisplayChangingController(this);
         mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
-                new DisplayInsetsController.OnInsetsChangedListener() {
-                    @Override
-                    public void insetsChanged(InsetsState insetsState) {
-                        setDisplayLayout(mDisplayController
-                                        .getDisplayLayout(mPipDisplayLayoutState.getDisplayId()));
-                    }
-                });
-        mDisplayInsetsController.addInsetsChangedListener(mPipDisplayLayoutState.getDisplayId(),
                 new ImeListener(mDisplayController, mPipDisplayLayoutState.getDisplayId()) {
                     @Override
                     public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -285,34 +275,37 @@
         setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
     }
 
-    @Override
-    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
-        if (displayId != mPipDisplayLayoutState.getDisplayId()) {
-            return;
-        }
-        setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
-    }
-
     /**
      * A callback for any observed transition that contains a display change in its
-     * {@link android.window.TransitionRequestInfo} with a non-zero rotation delta.
+     * {@link android.window.TransitionRequestInfo},
      */
     @Override
     public void onDisplayChange(int displayId, int fromRotation, int toRotation,
             @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t) {
+        if (displayId != mPipDisplayLayoutState.getDisplayId()) {
+            return;
+        }
+        final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
+        final float boundsScale = mPipBoundsState.getBoundsScale();
+
+        // Update the display layout caches even if we are not in PiP.
+        setDisplayLayout(mDisplayController.getDisplayLayout(displayId));
+
         if (!mPipTransitionState.isInPip()) {
             return;
         }
 
-        // Calculate the snap fraction pre-rotation.
-        float snapFraction = mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds());
+        mPipTouchHandler.updateMinMaxSize(mPipBoundsState.getAspectRatio());
 
-        // Update the caches to reflect the new display layout and movement bounds.
-        mPipDisplayLayoutState.rotateTo(toRotation);
+        // 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 width, height and snap fraction invariant.
-        Rect toBounds = mPipBoundsState.getBounds();
+        // The policy is to keep PiP snap fraction invariant.
         mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
         mPipBoundsState.setBounds(toBounds);
         t.setBounds(mPipTransitionState.mPipTaskToken, toBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
index e7e7970..b3070f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipDismissTargetHandler.java
@@ -35,10 +35,10 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.bubbles.DismissViewUtils;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import kotlin.Unit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
index c54e4cd..a29104c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMenuView.java
@@ -61,11 +61,11 @@
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.Interpolators;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.pip.PipUiEventLogger;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
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 83253c6..0324fdb 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
@@ -42,7 +42,6 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
@@ -51,12 +50,12 @@
 import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
 
 import java.util.Optional;
-import java.util.function.Consumer;
 
 /**
  * A helper to animate and manipulate the PiP.
@@ -134,18 +133,6 @@
     private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
             new PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_NO_BOUNCY);
 
-    @Nullable private Runnable mUpdateMovementBoundsRunnable;
-
-    private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
-        if (mPipBoundsState.getBounds().equals(newBounds)) {
-            return;
-        }
-
-        mMenuController.updateMenuLayout(newBounds);
-        mPipBoundsState.setBounds(newBounds);
-        maybeUpdateMovementBounds();
-    };
-
     /**
      * Whether we're springing to the touch event location (vs. moving it to that position
      * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was
@@ -683,16 +670,6 @@
         cleanUpHighPerfSessionMaybe();
     }
 
-    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
-        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
-    }
-
-    private void maybeUpdateMovementBounds() {
-        if (mUpdateMovementBoundsRunnable != null)  {
-            mUpdateMovementBoundsRunnable.run();
-        }
-    }
-
     /**
      * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
      * we return these bounds from
@@ -720,7 +697,7 @@
     /**
      * Directly resizes the PiP to the given {@param bounds}.
      */
-    private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
+    void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
         if (mPipBoundsState.getMotionBoundsState().isInMotion()) {
             // Do not carry out any resizing if we are dragging or physics animator is running.
             return;
@@ -813,7 +790,7 @@
         cleanUpHighPerfSessionMaybe();
 
         // Signal that the transition is done - should update transition state by default.
-        mPipScheduler.scheduleFinishResizePip(false /* configAtEnd */);
+        mPipScheduler.scheduleFinishResizePip(destinationBounds, false /* configAtEnd */);
     }
 
     private void startResizeAnimation(SurfaceControl.Transaction startTx,
@@ -829,8 +806,6 @@
                 startTx, finishTx, mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
                 destinationBounds, duration, 0f /* angle */);
         animator.setAnimationEndCallback(() -> {
-            mUpdateBoundsCallback.accept(destinationBounds);
-
             // In case an ongoing drag/fling was present before a deterministic resize transition
             // kicked in, we need to update the update bounds properly before cleaning in-motion
             // state.
@@ -839,7 +814,7 @@
 
             cleanUpHighPerfSessionMaybe();
             // Signal that we are done with resize transition
-            mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
+            mPipScheduler.scheduleFinishResizePip(destinationBounds, true /* configAtEnd */);
         });
         animator.start();
     }
@@ -849,7 +824,6 @@
             // The physics animation ended, though we may not necessarily be done animating, such as
             // when we're still dragging after moving out of the magnetic target. Only set the final
             // bounds state and clear motion bounds completely if the whole animation is over.
-            mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion());
             mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded();
         }
         mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index d28204a..f5ef64d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -50,7 +50,6 @@
 import com.android.wm.shell.pip2.animation.PipResizeAnimator;
 
 import java.io.PrintWriter;
-import java.util.function.Consumer;
 
 /**
  * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -86,8 +85,6 @@
     private final Rect mUserResizeBounds = new Rect();
     private final Rect mDownBounds = new Rect();
     private final Rect mStartBoundsAfterRelease = new Rect();
-    private final Runnable mUpdateMovementBoundsRunnable;
-    private final Consumer<Rect> mUpdateResizeBoundsCallback;
 
     private float mTouchSlop;
 
@@ -121,7 +118,6 @@
             PipTouchState pipTouchState,
             PipScheduler pipScheduler,
             PipTransitionState pipTransitionState,
-            Runnable updateMovementBoundsRunnable,
             PipUiEventLogger pipUiEventLogger,
             PhonePipMenuController menuActivityController,
             ShellExecutor mainExecutor,
@@ -138,18 +134,9 @@
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
 
-        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mPhonePipMenuController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
         mPinchResizingAlgorithm = new PipPinchResizingAlgorithm();
-
-        mUpdateResizeBoundsCallback = (rect) -> {
-            mUserResizeBounds.set(rect);
-            // mMotionHelper.synchronizePinnedStackBounds();
-            mPipBoundsState.setBounds(rect);
-            mUpdateMovementBoundsRunnable.run();
-            resetState();
-        };
     }
 
     void init() {
@@ -563,11 +550,13 @@
                         mLastResizeBounds, duration, mAngle);
                 animator.setAnimationEndCallback(() -> {
                     // All motion operations have actually finished, so make bounds cache updates.
-                    mUpdateResizeBoundsCallback.accept(mLastResizeBounds);
+                    mUserResizeBounds.set(mLastResizeBounds);
+                    resetState();
                     cleanUpHighPerfSessionMaybe();
 
                     // Signal that we are done with resize transition
-                    mPipScheduler.scheduleFinishResizePip(true /* configAtEnd */);
+                    mPipScheduler.scheduleFinishResizePip(
+                            mLastResizeBounds, true /* configAtEnd */);
                 });
                 animator.start();
                 break;
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 ac670cf..f4defdc 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
@@ -52,11 +52,14 @@
 
     private final Context mContext;
     private final PipBoundsState mPipBoundsState;
+    private final PhonePipMenuController mPipMenuController;
     private final ShellExecutor mMainExecutor;
     private final PipTransitionState mPipTransitionState;
     private PipSchedulerReceiver mSchedulerReceiver;
     private PipTransitionController mPipTransitionController;
 
+    @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
@@ -94,10 +97,12 @@
 
     public PipScheduler(Context context,
             PipBoundsState pipBoundsState,
+            PhonePipMenuController pipMenuController,
             ShellExecutor mainExecutor,
             PipTransitionState pipTransitionState) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
+        mPipMenuController = pipMenuController;
         mMainExecutor = mainExecutor;
         mPipTransitionState = pipTransitionState;
 
@@ -189,9 +194,13 @@
      * Signals to Core to finish the PiP resize transition.
      * Note that we do not allow any actual WM Core changes at this point.
      *
+     * @param toBounds destination bounds used only for internal state updates - not sent to Core.
      * @param configAtEnd true if we are waiting for config updates at the end of the transition.
      */
-    public void scheduleFinishResizePip(boolean configAtEnd) {
+    public void scheduleFinishResizePip(Rect toBounds, boolean configAtEnd) {
+        // Make updates to the internal state to reflect new bounds
+        onFinishingPipResize(toBounds);
+
         SurfaceControl.Transaction tx = null;
         if (configAtEnd) {
             tx = new SurfaceControl.Transaction();
@@ -238,4 +247,23 @@
         tx.setMatrix(leash, transformTensor, mMatrixTmp);
         tx.apply();
     }
+
+    void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) {
+        mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
+    }
+
+    private void maybeUpdateMovementBounds() {
+        if (mUpdateMovementBoundsRunnable != null)  {
+            mUpdateMovementBoundsRunnable.run();
+        }
+    }
+
+    private void onFinishingPipResize(Rect newBounds) {
+        if (mPipBoundsState.getBounds().equals(newBounds)) {
+            return;
+        }
+        mPipBoundsState.setBounds(newBounds);
+        mPipMenuController.updateMenuLayout(newBounds);
+        maybeUpdateMovementBounds();
+    }
 }
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
new file mode 100644
index 0000000..262c14d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -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.pip2.phone;
+
+import static com.android.wm.shell.pip2.phone.PipTransition.ANIMATING_BOUNDS_CHANGE_DURATION;
+
+import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.util.Preconditions;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.pip2.animation.PipResizeAnimator;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
+
+/**
+ * A Task Listener implementation used only for CUJs and trigger paths that cannot be initiated via
+ * Transitions framework directly.
+ * Hence, it's the intention to keep the usage of this class for a very limited set of cases.
+ */
+public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
+        PipTransitionState.PipTransitionStateChangedListener {
+    private static final int ASPECT_RATIO_CHANGE_DURATION = 250;
+    private static final String ANIMATING_ASPECT_RATIO_CHANGE = "animating_aspect_ratio_change";
+
+    private final Context mContext;
+    private final PipTransitionState mPipTransitionState;
+    private final PipScheduler mPipScheduler;
+    private final PipBoundsState mPipBoundsState;
+    private final PipBoundsAlgorithm mPipBoundsAlgorithm;
+    private final ShellExecutor mMainExecutor;
+    private final PictureInPictureParams mPictureInPictureParams =
+            new PictureInPictureParams.Builder().build();
+
+    private boolean mWaitingForAspectRatioChange = false;
+
+    public PipTaskListener(Context context,
+            ShellTaskOrganizer shellTaskOrganizer,
+            PipTransitionState pipTransitionState,
+            PipScheduler pipScheduler,
+            PipBoundsState pipBoundsState,
+            PipBoundsAlgorithm pipBoundsAlgorithm,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        mContext = context;
+        mPipTransitionState = pipTransitionState;
+        mPipScheduler = pipScheduler;
+        mPipBoundsState = pipBoundsState;
+        mPipBoundsAlgorithm = pipBoundsAlgorithm;
+        mMainExecutor = mainExecutor;
+
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
+        if (PipUtils.isPip2ExperimentEnabled()) {
+            mMainExecutor.execute(() -> {
+                shellTaskOrganizer.addListenerForType(this,
+                        ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP);
+            });
+        }
+    }
+
+    void setPictureInPictureParams(@Nullable PictureInPictureParams params) {
+        if (mPictureInPictureParams.equals(params)) {
+            return;
+        }
+        mPictureInPictureParams.copyOnlySet(params != null ? params
+                : new PictureInPictureParams.Builder().build());
+    }
+
+    @NonNull
+    public PictureInPictureParams getPictureInPictureParams() {
+        return mPictureInPictureParams;
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        PictureInPictureParams params = taskInfo.pictureInPictureParams;
+        if (mPictureInPictureParams.equals(params)) {
+            return;
+        }
+        setPictureInPictureParams(params);
+        float newAspectRatio = mPictureInPictureParams.getAspectRatioFloat();
+        if (PipUtils.aspectRatioChanged(newAspectRatio, mPipBoundsState.getAspectRatio())) {
+            mPipTransitionState.setOnIdlePipTransitionStateRunnable(() -> {
+                onAspectRatioChanged(newAspectRatio);
+            });
+        }
+    }
+
+    private void onAspectRatioChanged(float ratio) {
+        mPipBoundsState.setAspectRatio(ratio);
+
+        final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds(
+                mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio());
+        // Avoid scheduling a resize transition if destination bounds are unchanged, otherise
+        // we could end up with a no-op transition.
+        if (!destinationBounds.equals(mPipBoundsState.getBounds())) {
+            Bundle extra = new Bundle();
+            extra.putBoolean(ANIMATING_ASPECT_RATIO_CHANGE, true);
+            mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+        }
+    }
+
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+        switch (newState) {
+            case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+                mWaitingForAspectRatioChange = extra.getBoolean(ANIMATING_ASPECT_RATIO_CHANGE);
+                if (!mWaitingForAspectRatioChange) break;
+
+                mPipScheduler.scheduleAnimateResizePip(
+                        mPipBoundsAlgorithm.getAdjustedDestinationBounds(
+                                mPipBoundsState.getBounds(), mPipBoundsState.getAspectRatio()),
+                        false /* configAtEnd */, ASPECT_RATIO_CHANGE_DURATION);
+                break;
+            case PipTransitionState.CHANGING_PIP_BOUNDS:
+                final SurfaceControl.Transaction startTx = extra.getParcelable(
+                        PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+                final SurfaceControl.Transaction finishTx = extra.getParcelable(
+                        PipTransition.PIP_FINISH_TX, SurfaceControl.Transaction.class);
+                final Rect destinationBounds = extra.getParcelable(
+                        PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
+                final int duration = extra.getInt(ANIMATING_BOUNDS_CHANGE_DURATION,
+                        PipTransition.BOUNDS_CHANGE_JUMPCUT_DURATION);
+
+                Preconditions.checkNotNull(mPipTransitionState.mPinnedTaskLeash,
+                        "Leash is null for bounds transition.");
+
+                if (mWaitingForAspectRatioChange) {
+                    PipResizeAnimator animator = new PipResizeAnimator(mContext,
+                            mPipTransitionState.mPinnedTaskLeash, startTx, finishTx,
+                            destinationBounds,
+                            mPipBoundsState.getBounds(), destinationBounds, duration,
+                            0f /* delta */);
+                    animator.setAnimationEndCallback(() -> {
+                        mPipScheduler.scheduleFinishResizePip(
+                                destinationBounds, false /* configAtEnd */);
+                    });
+                    animator.start();
+                }
+                break;
+        }
+    }
+}
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 d75fa00..029f001 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
@@ -206,7 +206,7 @@
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
         mMotionHelper = pipMotionHelper;
-        mMotionHelper.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
+        mPipScheduler.setUpdateMovementBoundsRunnable(this::updateMovementBounds);
         mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
                 mMotionHelper, mainExecutor);
         mTouchState = new PipTouchState(ViewConfiguration.get(context),
@@ -219,8 +219,8 @@
                 menuController::hideMenu,
                 mainExecutor);
         mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
-                pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState,
-                this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor,
+                pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState, pipUiEventLogger,
+                menuController, mainExecutor,
                 mPipPerfHintController);
         mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
 
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 7790c51..f93233e 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
@@ -36,6 +36,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -50,10 +51,10 @@
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipMenuController;
 import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.pip.PipContentOverlay;
 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.shared.pip.PipContentOverlay;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
@@ -87,6 +88,7 @@
     //
 
     private final Context mContext;
+    private final PipTaskListener mPipTaskListener;
     private final PipScheduler mPipScheduler;
     private final PipTransitionState mPipTransitionState;
 
@@ -118,12 +120,15 @@
             PipBoundsState pipBoundsState,
             PipMenuController pipMenuController,
             PipBoundsAlgorithm pipBoundsAlgorithm,
+            PipTaskListener pipTaskListener,
             PipScheduler pipScheduler,
-            PipTransitionState pipTransitionState) {
+            PipTransitionState pipTransitionState,
+            PipUiStateChangeController pipUiStateChangeController) {
         super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
                 pipBoundsAlgorithm);
 
         mContext = context;
+        mPipTaskListener = pipTaskListener;
         mPipScheduler = pipScheduler;
         mPipScheduler.setPipTransitionController(this);
         mPipTransitionState = pipTransitionState;
@@ -137,6 +142,11 @@
         }
     }
 
+    @Override
+    protected boolean isInSwipePipToHomeTransition() {
+        return mPipTransitionState.isInSwipePipToHomeTransition();
+    }
+
     //
     // Transition collection stage lifecycle hooks
     //
@@ -389,17 +399,22 @@
         SurfaceControl pipLeash = mPipTransitionState.mPinnedTaskLeash;
         Preconditions.checkNotNull(pipLeash, "Leash is null for bounds transition.");
 
+        Rect sourceRectHint = null;
+        if (pipChange.getTaskInfo() != null
+                && pipChange.getTaskInfo().pictureInPictureParams != null) {
+            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+        }
+
         PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
-                startTransaction, startBounds, startBounds, endBounds,
-                PipEnterExitAnimator.BOUNDS_ENTER);
+                startTransaction, finishTransaction, startBounds, startBounds, endBounds,
+                sourceRectHint, PipEnterExitAnimator.BOUNDS_ENTER, Surface.ROTATION_0);
 
         tx.addTransactionCommittedListener(mPipScheduler.getMainExecutor(),
                 this::onClientDrawAtTransitionEnd);
         finishWct.setBoundsChangeTransaction(pipTaskToken, tx);
 
-        animator.setAnimationEndCallback(() -> {
-            finishCallback.onTransitionFinished(finishWct.isEmpty() ? null : finishWct);
-        });
+        animator.setAnimationEndCallback(() ->
+                finishCallback.onTransitionFinished(finishWct));
 
         animator.start();
         return true;
@@ -443,19 +458,53 @@
 
         TransitionInfo.Change pipChange = getChangeByToken(info, pipToken);
         if (pipChange == null) {
-            return false;
+            // pipChange is null, check to see if we've reparented the PIP activity for
+            // the multi activity case. If so we should use the activity leash instead
+            for (TransitionInfo.Change change : info.getChanges()) {
+                if (change.getTaskInfo() == null
+                        && change.getLastParent() != null
+                        && change.getLastParent().equals(pipToken)) {
+                    pipChange = change;
+                    break;
+                }
+            }
+
+            // failsafe
+            if (pipChange == null) {
+                return false;
+            }
+        }
+
+        // for multi activity, we need to manually set the leash layer
+        if (pipChange.getTaskInfo() == null) {
+            TransitionInfo.Change parent = getChangeByToken(info, pipChange.getParent());
+            if (parent != null) {
+                startTransaction.setLayer(parent.getLeash(), Integer.MAX_VALUE - 1);
+            }
         }
 
         Rect startBounds = pipChange.getStartAbsBounds();
         Rect endBounds = pipChange.getEndAbsBounds();
         SurfaceControl pipLeash = pipChange.getLeash();
+        Preconditions.checkNotNull(pipLeash, "Leash is null for exit transition.");
+
+        Rect sourceRectHint = null;
+        if (pipChange.getTaskInfo() != null
+                && pipChange.getTaskInfo().pictureInPictureParams != null) {
+            // single activity
+            sourceRectHint = pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint();
+        } else if (mPipTaskListener.getPictureInPictureParams().hasSourceBoundsHint()) {
+            // multi activity
+            sourceRectHint = mPipTaskListener.getPictureInPictureParams().getSourceRectHint();
+        }
 
         PipEnterExitAnimator animator = new PipEnterExitAnimator(mContext, pipLeash,
-                startTransaction, startBounds, startBounds, endBounds,
-                PipEnterExitAnimator.BOUNDS_EXIT);
+                startTransaction, finishTransaction, endBounds, startBounds, endBounds,
+                sourceRectHint, PipEnterExitAnimator.BOUNDS_EXIT, Surface.ROTATION_0);
+
         animator.setAnimationEndCallback(() -> {
-            finishCallback.onTransitionFinished(null);
             mPipTransitionState.setState(PipTransitionState.EXITED_PIP);
+            finishCallback.onTransitionFinished(null);
         });
 
         animator.start();
@@ -504,6 +553,7 @@
         // cache the original task token to check for multi-activity case later
         final ActivityManager.RunningTaskInfo pipTask = request.getPipTask();
         PictureInPictureParams pipParams = pipTask.pictureInPictureParams;
+        mPipTaskListener.setPictureInPictureParams(pipParams);
         mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo,
                 pipParams, mPipBoundsAlgorithm);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java
new file mode 100644
index 0000000..224016e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipUiStateChangeController.java
@@ -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.wm.shell.pip2.phone;
+
+import android.app.ActivityTaskManager;
+import android.app.Flags;
+import android.app.PictureInPictureUiState;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.util.function.Consumer;
+
+/**
+ * Controller class manages the {@link android.app.PictureInPictureUiState} callbacks sent to app.
+ */
+public class PipUiStateChangeController implements
+        PipTransitionState.PipTransitionStateChangedListener {
+
+    private final PipTransitionState mPipTransitionState;
+
+    private Consumer<PictureInPictureUiState> mPictureInPictureUiStateConsumer;
+
+    public PipUiStateChangeController(PipTransitionState pipTransitionState) {
+        mPipTransitionState = pipTransitionState;
+        mPipTransitionState.addPipTransitionStateChangedListener(this);
+        mPictureInPictureUiStateConsumer = pictureInPictureUiState -> {
+            try {
+                ActivityTaskManager.getService().onPictureInPictureUiStateChanged(
+                        pictureInPictureUiState);
+            } catch (RemoteException | IllegalStateException e) {
+                ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                        "Failed to send PictureInPictureUiState.");
+            }
+        };
+    }
+
+    @Override
+    public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+            @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+        if (newState == PipTransitionState.SWIPING_TO_PIP) {
+            onIsTransitioningToPipUiStateChange(true /* isTransitioningToPip */);
+        } else if (newState == PipTransitionState.ENTERING_PIP
+                && !mPipTransitionState.isInSwipePipToHomeTransition()) {
+            onIsTransitioningToPipUiStateChange(true /* isTransitioningToPip */);
+        } else if (newState == PipTransitionState.ENTERED_PIP) {
+            onIsTransitioningToPipUiStateChange(false /* isTransitioningToPip */);
+        }
+    }
+
+    @VisibleForTesting
+    void setPictureInPictureUiStateConsumer(Consumer<PictureInPictureUiState> consumer) {
+        mPictureInPictureUiStateConsumer = consumer;
+    }
+
+    private void onIsTransitioningToPipUiStateChange(boolean isTransitioningToPip) {
+        if (Flags.enablePipUiStateCallbackOnEntering()
+                && mPictureInPictureUiStateConsumer != null) {
+            mPictureInPictureUiStateConsumer.accept(new PictureInPictureUiState.Builder()
+                    .setTransitioningToPip(isTransitioningToPip)
+                    .build());
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
index 4048c5b..ebfd357 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasks.aidl
@@ -24,7 +24,7 @@
 import android.view.IRecentsAnimationRunner;
 
 import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 
 /**
  * Interface that is exposed to remote callers to fetch recent tasks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index 77b8663..8c5d1e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -19,8 +19,8 @@
 import android.annotation.Nullable;
 import android.graphics.Color;
 
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 import com.android.wm.shell.shared.annotations.ExternalThread;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
 
 import java.util.List;
 import java.util.concurrent.Executor;
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 da7e03f..9af33a8 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
@@ -19,11 +19,14 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.pm.PackageManager.FEATURE_PC;
 
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
 
+import android.Manifest;
+import android.annotation.RequiresPermission;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.IApplicationThread;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -51,16 +54,16 @@
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.split.SplitBounds;
 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.Transitions;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.SplitBounds;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -158,6 +161,7 @@
         return new IRecentTasksImpl(this);
     }
 
+    @RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
     private void onInit() {
         mShellController.addExternalInterface(KEY_EXTRA_SHELL_RECENT_TASKS,
                 this::createExternalInterface, this);
@@ -168,6 +172,8 @@
             mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
                     mMainExecutor);
         }
+        mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
+                mMainExecutor, isKeyguardLocked -> notifyRecentTasksChanged());
     }
 
     void setTransitionHandler(RecentsTransitionHandler handler) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index ad3f4f8c..c90da05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -29,8 +29,8 @@
 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
-import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION;
+import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
 
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index 1cbb8bb..1dad413 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -33,7 +33,7 @@
 
 /**
  * Main stage for split-screen mode. When split-screen is active all standard activity types launch
- * on the main stage, except for task that are explicitly pinned to the {@link SideStage}.
+ * on the main stage, except for task that are explicitly pinned to the {@link StageTaskListener}.
  * @see StageCoordinator
  */
 class MainStage extends StageTaskListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
deleted file mode 100644
index 27fd309..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ /dev/null
@@ -1,73 +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.wm.shell.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up
- * here. All other task are launch in the {@link MainStage}.
- *
- * @see StageCoordinator
- */
-class SideStage extends StageTaskListener {
-    private static final String TAG = SideStage.class.getSimpleName();
-
-    SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
-            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
-            SurfaceSession surfaceSession, IconProvider iconProvider,
-            Optional<WindowDecorViewModel> windowDecorViewModel) {
-        super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
-                iconProvider, windowDecorViewModel);
-    }
-
-    boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
-                mChildrenTaskInfo.size(), toTop);
-        if (mChildrenTaskInfo.size() == 0) return false;
-        wct.reparentTasks(
-                mRootTaskInfo.token,
-                null /* newParent */,
-                null /* windowingModes */,
-                null /* activityTypes */,
-                toTop);
-        return true;
-    }
-
-    boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
-        final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
-                task != null);
-        if (task == null) return false;
-        wct.reparent(task.token, newParent, false /* onTop */);
-        return true;
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 06c57bd..526c1d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -25,9 +25,9 @@
 import android.window.RemoteTransition;
 
 import com.android.internal.logging.InstanceId;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 
 import java.util.concurrent.Executor;
 
@@ -50,7 +50,7 @@
 
     /**
      * The side stage type.
-     * @see SideStage
+     * @see StageTaskListener
      */
     int STAGE_TYPE_SIDE = 1;
 
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 83f827a..7e165af 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
@@ -27,16 +27,16 @@
 import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
 import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
 import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
-import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
 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.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
+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 static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -90,16 +90,16 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.annotations.ExternalThread;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
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 af11ebc..e1b474d 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
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell.splitscreen;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
 
 import com.android.wm.shell.sysui.ShellCommandHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index c11a112..8400494 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -21,12 +21,12 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 
-import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
-import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
-import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FADE_DURATION;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
 import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
 import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -47,9 +47,9 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.protolog.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.transition.OneShotRemoteHandler;
 import com.android.wm.shell.transition.Transitions;
@@ -504,7 +504,9 @@
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
         }
 
-        // TODO: handle transition consumed for active remote handler
+        if (mActiveRemoteHandler != null) {
+            mActiveRemoteHandler.onTransitionConsumed(transition, aborted, finishT);
+        }
     }
 
     void onFinish(WindowContainerTransaction wct) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index a0bf843..1e6fa28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -32,8 +32,9 @@
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
 import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+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 static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_DRAG;
 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER;
 import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_MULTI_INSTANCE;
@@ -57,8 +58,9 @@
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
 
 /**
@@ -133,6 +135,11 @@
             @SplitPosition int mainStagePosition, int mainStageUid,
             @SplitPosition int sideStagePosition, int sideStageUid,
             boolean isLandscape) {
+        if (hasStartedSession()) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: no-op, previous session has not ended");
+            return;
+        }
+
         mLoggerSessionId = mIdSequence.newInstanceId();
         int enterReason = getLoggerEnterReason(isLandscape);
         updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
@@ -140,6 +147,14 @@
         updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
                 sideStageUid);
         updateSplitRatioState(splitRatio);
+
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logEnter: enterReason=%d splitRatio=%f "
+                        + "mainStagePosition=%d mainStageUid=%d sideStagePosition=%d "
+                        + "sideStageUid=%d isLandscape=%b mEnterSessionId=%d mLoggerSessionId=%d",
+                enterReason, splitRatio, mLastMainStagePosition, mLastMainStageUid,
+                mLastSideStagePosition, mLastSideStageUid, isLandscape,
+                mEnterSessionId != null ? mEnterSessionId.getId() : 0, mLoggerSessionId.getId());
+
         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
                 enterReason,
@@ -212,14 +227,25 @@
             @SplitPosition int mainStagePosition, int mainStageUid,
             @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
         if (mLoggerSessionId == null) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: no-op, mLoggerSessionId is null");
             // Ignore changes until we've started logging the session
             return;
         }
         if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
                 && sideStagePosition != SPLIT_POSITION_UNDEFINED)
                         || (mainStageUid != 0 && sideStageUid != 0)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                    "logExit: no-op, only main or side stage should be set, not both/none");
             throw new IllegalArgumentException("Only main or side stage should be set");
         }
+
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logExit: exitReason=%d mainStagePosition=%d"
+                        + " mainStageUid=%d sideStagePosition=%d sideStageUid=%d isLandscape=%b"
+                        + " mLoggerSessionId=%d", getLoggerExitReason(exitReason),
+                getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), mainStageUid,
+                getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), sideStageUid,
+                isLandscape, mLoggerSessionId.getId());
+
         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
                 0 /* enterReason */,
@@ -304,25 +330,34 @@
      */
     public void logResize(float splitRatio) {
         if (mLoggerSessionId == null) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, mLoggerSessionId is null");
             // Ignore changes until we've started logging the session
             return;
         }
         if (splitRatio <= 0f || splitRatio >= 1f) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+                    "logResize: no-op, splitRatio indicates that user is dismissing, not resizing");
             // Don't bother reporting resizes that end up dismissing the split, that will be logged
             // via the exit event
             return;
         }
         if (!updateSplitRatioState(splitRatio)) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: no-op, split ratio was not changed");
             // Ignore if there are no user perceived changes
             return;
         }
+
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logResize: splitRatio=%f mLoggerSessionId=%d",
+                mLastSplitRatio, mLoggerSessionId.getId());
         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
                 0 /* enterReason */,
                 0 /* exitReason */,
                 mLastSplitRatio,
-                0 /* mainStagePosition */, 0 /* mainStageUid */,
-                0 /* sideStagePosition */, 0 /* sideStageUid */,
+                mLastMainStagePosition,
+                mLastMainStageUid,
+                mLastSideStagePosition,
+                mLastSideStageUid,
                 0 /* dragInstanceId */,
                 mLoggerSessionId.getId());
     }
@@ -333,6 +368,7 @@
     public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
             @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
         if (mLoggerSessionId == null) {
+            ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: no-op, mLoggerSessionId is null");
             // Ignore changes until we've started logging the session
             return;
         }
@@ -341,6 +377,11 @@
                 mainStageUid);
         updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
                 sideStageUid);
+
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "logSwap: mainStagePosition=%d mainStageUid=%d "
+                + "sideStagePosition=%d sideStageUid=%d mLoggerSessionId=%d",
+                mLastMainStagePosition, mLastMainStageUid, mLastSideStagePosition,
+                mLastSideStageUid, mLoggerSessionId.getId());
         FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
                 FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
                 0 /* enterReason */,
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 9bf5159..f3959cc 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
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -34,16 +35,16 @@
 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.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
 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.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+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 static com.android.wm.shell.shared.split.SplitScreenConstants.splitPositionToString;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -125,21 +126,21 @@
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.common.split.SplitLayout;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.common.split.SplitScreenUtils;
 import com.android.wm.shell.common.split.SplitWindowManager;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.split.SplitBounds;
+import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
 import com.android.wm.shell.transition.DefaultMixedHandler;
 import com.android.wm.shell.transition.Transitions;
-import com.android.wm.shell.util.SplitBounds;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
 import dalvik.annotation.optimization.NeverCompile;
@@ -154,13 +155,13 @@
 
 /**
  * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
- * {@link SideStage} stages.
+ * other stages.
  * Some high-level rules:
- * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at
+ * - The {@link StageCoordinator} is only considered active if the other stages contain at
  * least one child task.
  * - The {@link MainStage} should only have children if the coordinator is active.
  * - The {@link SplitLayout} divider is only visible if both the {@link MainStage}
- * and {@link SideStage} are visible.
+ * and other stages 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).}
@@ -175,7 +176,7 @@
 
     private final MainStage mMainStage;
     private final StageListenerImpl mMainStageListener = new StageListenerImpl();
-    private final SideStage mSideStage;
+    private final StageTaskListener mSideStage;
     private final StageListenerImpl mSideStageListener = new StageListenerImpl();
     @SplitPosition
     private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -337,7 +338,7 @@
                 mSurfaceSession,
                 iconProvider,
                 mWindowDecorViewModel);
-        mSideStage = new SideStage(
+        mSideStage = new StageTaskListener(
                 mContext,
                 mTaskOrganizer,
                 mDisplayId,
@@ -366,7 +367,7 @@
 
     @VisibleForTesting
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
-            ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
+            ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage,
             DisplayController displayController, DisplayImeController displayImeController,
             DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
             Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
@@ -423,6 +424,15 @@
         return mMainStage.isActive();
     }
 
+    /**
+     * Deactivates main stage by removing the stage from the top level split root (usually when a
+     * task underneath gets removed from the stage root).
+     * @param reparentToTop whether we want to put the stage root back on top
+     */
+    private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) {
+        mMainStage.deactivate(wct, reparentToTop);
+    }
+
     /** @return whether this transition-request has the launch-adjacent flag. */
     public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) {
         final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
@@ -497,10 +507,10 @@
 
         /**
          * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
-         * {@link SideStage} no longer has children.
+         * other stages no longer have children.
          */
         final boolean result = mSideStage.removeTask(taskId,
-                mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null,
+                isSplitActive() ? mMainStage.mRootTaskInfo.token : null,
                 wct);
         mTaskOrganizer.applyTransaction(wct);
         return result;
@@ -617,7 +627,7 @@
         }
 
         // If split screen is not activated, we're expecting to open a pair of apps to split.
-        final int extraTransitType = mMainStage.isActive()
+        final int extraTransitType = isSplitActive()
                 ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
         prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
 
@@ -660,7 +670,7 @@
         }
 
         // If split screen is not activated, we're expecting to open a pair of apps to split.
-        final int extraTransitType = mMainStage.isActive()
+        final int extraTransitType = isSplitActive()
                 ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
         prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering);
 
@@ -684,6 +694,7 @@
         setSideStagePosition(splitPosition, wct);
         options1 = options1 != null ? options1 : new Bundle();
         addActivityOptions(options1, mSideStage);
+        prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct);
         wct.startTask(taskId1, options1);
 
         startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId);
@@ -714,6 +725,7 @@
         options1 = options1 != null ? options1 : new Bundle();
         addActivityOptions(options1, mSideStage);
         wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+        prepareTasksForSplitScreen(new int[] {taskId}, wct);
 
         startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
     }
@@ -757,11 +769,30 @@
         options1 = options1 != null ? options1 : new Bundle();
         addActivityOptions(options1, mSideStage);
         wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+        prepareTasksForSplitScreen(new int[] {taskId}, wct);
 
         startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId);
     }
 
     /**
+     * Prepares the tasks whose IDs are provided in `taskIds` for split screen by clearing their
+     * bounds and windowing mode so that they can inherit the bounds and the windowing mode of
+     * their root stages.
+     *
+     * @param taskIds an array of task IDs whose bounds will be cleared.
+     * @param wct     transaction to clear the bounds on the tasks.
+     */
+    private void prepareTasksForSplitScreen(int[] taskIds, WindowContainerTransaction wct) {
+        for (int taskId : taskIds) {
+            ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
+            if (task != null) {
+                wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
+                        .setBounds(task.token, null);
+            }
+        }
+    }
+
+    /**
      * Starts with the second task to a split pair in one transition.
      *
      * @param wct        transaction to start the first task
@@ -771,7 +802,7 @@
     private void startWithTask(WindowContainerTransaction wct, int mainTaskId,
             @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
-        if (!mMainStage.isActive()) {
+        if (!isSplitActive()) {
             // Build a request WCT that will launch both apps such that task 0 is on the main stage
             // while task 1 is on the side stage.
             mMainStage.activate(wct, false /* reparent */);
@@ -838,7 +869,7 @@
             return;
         }
 
-        if (!mMainStage.isActive()) {
+        if (!isSplitActive()) {
             // Build a request WCT that will launch both apps such that task 0 is on the main stage
             // while task 1 is on the side stage.
             mMainStage.activate(wct, false /* reparent */);
@@ -1088,7 +1119,7 @@
      */
     void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) {
         mKeyguardActive = active;
-        if (!mMainStage.isActive()) {
+        if (!isSplitActive()) {
             return;
         }
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
@@ -1132,7 +1163,7 @@
      * will do a no-op.
      */
     void dismissSplitKeepingLastActiveStage(@ExitReason int reason) {
-        if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
+        if (!isSplitActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) {
             // no-op
             return;
         }
@@ -1155,8 +1186,8 @@
     private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
             @ExitReason int exitReason) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
-                childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
-        if (!mMainStage.isActive()) return;
+                childrenToTop == mMainStage, exitReasonToString(exitReason), isSplitActive());
+        if (!isSplitActive()) return;
 
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         applyExitSplitScreen(childrenToTop, wct, exitReason);
@@ -1166,7 +1197,7 @@
             WindowContainerTransaction wct, @ExitReason int exitReason) {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
                 exitReasonToString(exitReason));
-        if (!mMainStage.isActive() || mIsExiting) return;
+        if (!isSplitActive() || mIsExiting) return;
 
         onSplitScreenExit();
         clearSplitPairedInRecents(exitReason);
@@ -1178,7 +1209,7 @@
         mSplitLayout.getInvisibleBounds(mTempRect1);
         if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
             mSideStage.removeAllTasks(wct, false /* toTop */);
-            mMainStage.deactivate(wct, false /* toTop */);
+            deactivateSplit(wct, false /* reparentToTop */);
             wct.reorder(mRootTaskInfo.token, false /* onTop */);
             setRootForceTranslucent(true, wct);
             wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1207,7 +1238,7 @@
                 childrenToTop.fadeOutDecor(() -> {
                     WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
                     mIsExiting = false;
-                    mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
+                    deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */);
                     mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
                     finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
                     setRootForceTranslucent(true, finishedWCT);
@@ -1230,7 +1261,7 @@
     }
 
     void dismissSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
-        if (!mMainStage.isActive()) return;
+        if (!isSplitActive()) return;
         final int stage = getStageOfTask(toTopTaskId);
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareExitSplitScreen(stage, wct);
@@ -1340,10 +1371,10 @@
      */
     void prepareExitSplitScreen(@StageType int stageToTop,
             @NonNull WindowContainerTransaction wct) {
-        if (!mMainStage.isActive()) return;
+        if (!isSplitActive()) return;
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
         mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
-        mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
+        deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN);
     }
 
     private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1449,12 +1480,11 @@
         mSkipEvictingMainStageChildren = false;
         mSplitRequest = null;
         updateRecentTasksSplitPair();
-        if (!mLogger.hasStartedSession()) {
-            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
-                    getMainStagePosition(), mMainStage.getTopChildTaskUid(),
-                    getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                    mSplitLayout.isLeftRightSplit());
-        }
+
+        mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+                getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+                getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+                mSplitLayout.isLeftRightSplit());
     }
 
     void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
@@ -1640,7 +1670,7 @@
         mRootTaskInfo = taskInfo;
         if (mSplitLayout != null
                 && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
-                && mMainStage.isActive()) {
+                && isSplitActive()) {
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
                     taskInfo.taskId);
             // Clear the divider remote animating flag as the divider will be re-rendered to apply
@@ -1894,7 +1924,7 @@
                 stageListener == mMainStageListener);
         final boolean hasChildren = stageListener.mHasChildren;
         final boolean isSideStage = stageListener == mSideStageListener;
-        if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
+        if (!hasChildren && !mIsExiting && isSplitActive()) {
             if (isSideStage && mMainStageListener.mVisible) {
                 // Exit to main stage if side stage no longer has children.
                 mSplitLayout.flingDividerToDismiss(
@@ -1909,7 +1939,7 @@
                 // Dismiss split screen in the background once any sides of the split become empty.
                 exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED);
             }
-        } else if (isSideStage && hasChildren && !mMainStage.isActive()) {
+        } else if (isSideStage && hasChildren && !isSplitActive()) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             prepareEnterSplitScreen(wct);
 
@@ -1930,15 +1960,13 @@
             clearRequestIfPresented();
             updateRecentTasksSplitPair();
 
-            if (!mLogger.hasStartedSession()) {
-                if (!mLogger.hasValidEnterSessionId()) {
-                    mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
-                }
-                mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
-                        getMainStagePosition(), mMainStage.getTopChildTaskUid(),
-                        getSideStagePosition(), mSideStage.getTopChildTaskUid(),
-                        mSplitLayout.isLeftRightSplit());
+            if (!mLogger.hasStartedSession() && !mLogger.hasValidEnterSessionId()) {
+                mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
             }
+            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+                    getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+                    getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+                    mSplitLayout.isLeftRightSplit());
         }
     }
 
@@ -2124,7 +2152,7 @@
 
     private void onDisplayChange(int displayId, int fromRotation, int toRotation,
             @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
-        if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+        if (displayId != DEFAULT_DISPLAY || !isSplitActive()) {
             return;
         }
 
@@ -2419,7 +2447,7 @@
             // Not entering or exiting, so just do some house-keeping and validation.
 
             // If we're not in split-mode, just abort so something else can handle it.
-            if (!mMainStage.isActive()) return false;
+            if (!isSplitActive()) return false;
 
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
             mSplitLayout.setFreezeDividerWindow(false);
@@ -2661,7 +2689,7 @@
     public void onTransitionAnimationComplete() {
         ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
         // If still playing, let it finish.
-        if (!mMainStage.isActive() && !mIsExiting) {
+        if (!isSplitActive() && !mIsExiting) {
             // Update divider state after animation so that it is still around and positioned
             // properly for the animation itself.
             mSplitLayout.release();
@@ -3125,7 +3153,7 @@
                 + (mSplitLayout != null ? mSplitLayout.isLeftRightSplit() : "null"));
         pw.println(innerPrefix + "MainStage");
         pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition()));
-        pw.println(childPrefix + "isActive=" + mMainStage.isActive());
+        pw.println(childPrefix + "isActive=" + isSplitActive());
         mMainStage.dump(pw, childPrefix);
         pw.println(innerPrefix + "MainStageListener");
         mMainStageListener.dump(pw, childPrefix);
@@ -3245,7 +3273,7 @@
         @Override
         public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
             ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
-            if (mMainStage.isActive()) {
+            if (isSplitActive()) {
                 final boolean isMainStage = mMainStageListener == this;
 
                 // If visible, we preserve the app and keep it running. If an app becomes
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 f19eb3f..4593553 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,9 +22,9 @@
 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+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;
 
@@ -475,6 +475,30 @@
         });
     }
 
+    // --------
+    // Previously only used in SideStage
+    boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+                mChildrenTaskInfo.size(), toTop);
+        if (mChildrenTaskInfo.size() == 0) return false;
+        wct.reparentTasks(
+                mRootTaskInfo.token,
+                null /* newParent */,
+                null /* windowingModes */,
+                null /* activityTypes */,
+                toTop);
+        return true;
+    }
+
+    boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
+        final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+                task != null);
+        if (task == null) return false;
+        wct.reparent(task.token, newParent, false /* onTop */);
+        return true;
+    }
+
     private void sendStatusChanged() {
         mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
index e0f6394..bb2f60b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java
@@ -39,8 +39,8 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.split.SplitScreenConstants;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
 
 /**
  * Handles the interaction logic with the {@link TvSplitMenuView}.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
index 88e9757..b758b53 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java
@@ -19,8 +19,8 @@
 import static android.view.KeyEvent.ACTION_DOWN;
 import static android.view.KeyEvent.KEYCODE_BACK;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+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 android.content.Context;
 import android.util.AttributeSet;
@@ -31,7 +31,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.split.SplitScreenConstants;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
 
 /**
  * A View for the Menu Window.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index b65e978..3468156 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -32,8 +32,8 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.splitscreen.StageCoordinator;
 import com.android.wm.shell.sysui.ShellCommandHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 81ca48f..4451ee8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -28,9 +28,9 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.split.SplitScreenConstants;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.shared.split.SplitScreenConstants;
 import com.android.wm.shell.splitscreen.StageCoordinator;
 import com.android.wm.shell.transition.Transitions;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index edb5aba..42b8b73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -29,7 +29,8 @@
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.R;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
+import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
 
 /**
  * Default animation for exiting the splash screen window.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
deleted file mode 100644
index ea8c0eb..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java
+++ /dev/null
@@ -1,459 +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.wm.shell.startingsurface;
-
-import static android.view.Choreographer.CALLBACK_COMMIT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.IntDef;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.BlendMode;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.view.Choreographer;
-import android.view.SurfaceControl;
-import android.view.SyncRtSurfaceTransactionApplier;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-import android.window.SplashScreenView;
-
-import com.android.wm.shell.animation.Interpolators;
-import com.android.wm.shell.common.TransactionPool;
-
-/**
- * Utilities for creating the splash screen window animations.
- * @hide
- */
-public class SplashScreenExitAnimationUtils {
-    private static final boolean DEBUG_EXIT_ANIMATION = false;
-    private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
-    private static final boolean DEBUG_EXIT_FADE_ANIMATION = false;
-    private static final String TAG = "SplashScreenExitAnimationUtils";
-
-    private static final Interpolator ICON_INTERPOLATOR = new PathInterpolator(0.15f, 0f, 1f, 1f);
-    private static final Interpolator MASK_RADIUS_INTERPOLATOR =
-            new PathInterpolator(0f, 0f, 0.4f, 1f);
-    private static final Interpolator SHIFT_UP_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
-
-    /**
-     * This splash screen exit animation type uses a radial vanish to hide
-     * the starting window and slides up the main window content.
-     * @hide
-     */
-    public static final int TYPE_RADIAL_VANISH_SLIDE_UP = 0;
-
-    /**
-     * This splash screen exit animation type fades out the starting window
-     * to reveal the main window content.
-     * @hide
-     */
-    public static final int TYPE_FADE_OUT = 1;
-
-    /** @hide */
-    @IntDef(prefix = { "TYPE_" }, value = {
-            TYPE_RADIAL_VANISH_SLIDE_UP,
-            TYPE_FADE_OUT,
-    })
-    public @interface ExitAnimationType {}
-
-    /**
-     * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
-     * window with rounded corner radius.
-     */
-    static void startAnimations(@ExitAnimationType int animationType,
-            ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
-            int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
-            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
-            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
-            Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
-        ValueAnimator animator;
-        if (animationType == TYPE_FADE_OUT) {
-            animator = createFadeOutAnimation(splashScreenView, animationDuration,
-                    iconFadeOutDuration, iconStartAlpha, brandingStartAlpha, appRevealDelay,
-                    appRevealDuration, animatorListener);
-        } else {
-            animator = createRadialVanishSlideUpAnimator(splashScreenView,
-                    firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
-                    animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
-                    appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
-        }
-        animator.start();
-    }
-
-    /**
-     * Creates and starts the animator to fade out the icon, reveal the app, and shift up main
-     * window.
-     * @hide
-     */
-    public static void startAnimations(ViewGroup splashScreenView,
-            SurfaceControl firstWindowSurface, int mainWindowShiftLength,
-            TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
-            int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
-            int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener) {
-        // Start the default 'reveal' animation.
-        startAnimations(TYPE_RADIAL_VANISH_SLIDE_UP, splashScreenView,
-                firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
-                animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
-                appRevealDelay, appRevealDuration, animatorListener, 0f /* roundedCornerRadius */);
-    }
-
-    /**
-     * Creates the animator to fade out the icon, reveal the app, and shift up main window.
-     * @hide
-     */
-    private static ValueAnimator createRadialVanishSlideUpAnimator(ViewGroup splashScreenView,
-            SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
-            TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
-            int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
-            int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
-            float roundedCornerRadius) {
-        // reveal app
-        final float transparentRatio = 0.8f;
-        final int globalHeight = splashScreenView.getHeight();
-        final int verticalCircleCenter = 0;
-        final int finalVerticalLength = globalHeight - verticalCircleCenter;
-        final int halfWidth = splashScreenView.getWidth() / 2;
-        final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
-                Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
-        final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
-        final float[] stops = {0f, transparentRatio, 1f};
-
-        RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView);
-        radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
-        radialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
-        radialVanishAnimation.setRadialPaintParam(colors, stops);
-
-        View occludeHoleView = null;
-        ShiftUpAnimation shiftUpAnimation = null;
-        if (firstWindowSurface != null && firstWindowSurface.isValid()) {
-            // shift up main window
-            occludeHoleView = new View(splashScreenView.getContext());
-            if (DEBUG_EXIT_ANIMATION_BLEND) {
-                occludeHoleView.setBackgroundColor(Color.BLUE);
-            } else if (splashScreenView instanceof SplashScreenView) {
-                occludeHoleView.setBackgroundColor(
-                        ((SplashScreenView) splashScreenView).getInitBackgroundColor());
-            } else {
-                occludeHoleView.setBackgroundColor(
-                        isDarkTheme(splashScreenView.getContext()) ? Color.BLACK : Color.WHITE);
-            }
-            final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
-                    WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
-            splashScreenView.addView(occludeHoleView, params);
-
-            shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView,
-                    firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame,
-                    mMainWindowShiftLength, roundedCornerRadius);
-        }
-
-        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
-        animator.setDuration(animationDuration);
-        animator.setInterpolator(Interpolators.LINEAR);
-        if (animatorListener != null) {
-            animator.addListener(animatorListener);
-        }
-        View finalOccludeHoleView = occludeHoleView;
-        ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation;
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (finalShiftUpAnimation != null) {
-                    finalShiftUpAnimation.finish();
-                }
-                splashScreenView.removeView(radialVanishAnimation);
-                splashScreenView.removeView(finalOccludeHoleView);
-            }
-        });
-        animator.addUpdateListener(animation -> {
-            float linearProgress = (float) animation.getAnimatedValue();
-
-            // Fade out progress
-            final float iconProgress =
-                    ICON_INTERPOLATOR.getInterpolation(getProgress(
-                            linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
-            View iconView = null;
-            View brandingView = null;
-            if (splashScreenView instanceof SplashScreenView) {
-                iconView = ((SplashScreenView) splashScreenView).getIconView();
-                brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
-            }
-            if (iconView != null) {
-                iconView.setAlpha(iconStartAlpha * (1 - iconProgress));
-            }
-            if (brandingView != null) {
-                brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress));
-            }
-
-            final float revealLinearProgress = getProgress(linearProgress, appRevealDelay,
-                    appRevealDuration, animationDuration);
-
-            radialVanishAnimation.onAnimationProgress(revealLinearProgress);
-
-            if (finalShiftUpAnimation != null) {
-                finalShiftUpAnimation.onAnimationProgress(revealLinearProgress);
-            }
-        });
-        return animator;
-    }
-
-    private static float getProgress(float linearProgress, long delay, long duration,
-                                     int animationDuration) {
-        return MathUtils.constrain(
-                (linearProgress * (animationDuration) - delay) / duration,
-                0.0f,
-                1.0f
-        );
-    }
-
-    private static boolean isDarkTheme(Context context) {
-        Configuration configuration = context.getResources().getConfiguration();
-        int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        return nightMode == Configuration.UI_MODE_NIGHT_YES;
-    }
-
-    private static ValueAnimator createFadeOutAnimation(ViewGroup splashScreenView,
-            int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
-            float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
-            Animator.AnimatorListener animatorListener) {
-
-        if (DEBUG_EXIT_FADE_ANIMATION) {
-            splashScreenView.setBackgroundColor(Color.BLUE);
-        }
-
-        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
-        animator.setDuration(animationDuration);
-        animator.setInterpolator(Interpolators.LINEAR);
-        animator.addUpdateListener(animation -> {
-
-            float linearProgress = (float) animation.getAnimatedValue();
-
-            // Icon fade out progress (always starts immediately)
-            final float iconFadeProgress = ICON_INTERPOLATOR.getInterpolation(getProgress(
-                            linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
-            View iconView = null;
-            View brandingView = null;
-
-            if (splashScreenView instanceof SplashScreenView) {
-                iconView = ((SplashScreenView) splashScreenView).getIconView();
-                brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
-            }
-            if (iconView != null) {
-                iconView.setAlpha(iconStartAlpha * (1f - iconFadeProgress));
-            }
-            if (brandingView != null) {
-                brandingView.setAlpha(brandingStartAlpha * (1f - iconFadeProgress));
-            }
-
-            // Splash screen fade out progress (possibly delayed)
-            final float splashFadeProgress = Interpolators.ALPHA_OUT.getInterpolation(
-                    getProgress(linearProgress, appRevealDelay,
-                    appRevealDuration, animationDuration));
-
-            splashScreenView.setAlpha(1f - splashFadeProgress);
-
-            if (DEBUG_EXIT_FADE_ANIMATION) {
-                Slog.d(TAG, "progress -> animation: " + linearProgress
-                        + "\t icon alpha: " + ((iconView != null) ? iconView.getAlpha() : "n/a")
-                        + "\t splash alpha: " + splashScreenView.getAlpha()
-                );
-            }
-        });
-        if (animatorListener != null) {
-            animator.addListener(animatorListener);
-        }
-        return animator;
-    }
-
-    /**
-     * View which creates a circular reveal of the underlying view.
-     * @hide
-     */
-    @SuppressLint("ViewConstructor")
-    public static class RadialVanishAnimation extends View {
-        private final ViewGroup mView;
-        private int mInitRadius;
-        private int mFinishRadius;
-
-        private final Point mCircleCenter = new Point();
-        private final Matrix mVanishMatrix = new Matrix();
-        private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
-        public RadialVanishAnimation(ViewGroup target) {
-            super(target.getContext());
-            mView = target;
-            mView.addView(this);
-            if (getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
-                ((ViewGroup.MarginLayoutParams) getLayoutParams()).setMargins(0, 0, 0, 0);
-            }
-            mVanishPaint.setAlpha(0);
-        }
-
-        void onAnimationProgress(float linearProgress) {
-            if (mVanishPaint.getShader() == null) {
-                return;
-            }
-
-            final float radiusProgress = MASK_RADIUS_INTERPOLATOR.getInterpolation(linearProgress);
-            final float alphaProgress = Interpolators.ALPHA_OUT.getInterpolation(linearProgress);
-            final float scale = mInitRadius + (mFinishRadius - mInitRadius) * radiusProgress;
-
-            mVanishMatrix.setScale(scale, scale);
-            mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
-            mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
-            mVanishPaint.setAlpha(Math.round(0xFF * alphaProgress));
-
-            postInvalidate();
-        }
-
-        void setRadius(int initRadius, int finishRadius) {
-            if (DEBUG_EXIT_ANIMATION) {
-                Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
-                        + " final " + finishRadius);
-            }
-            mInitRadius = initRadius;
-            mFinishRadius = finishRadius;
-        }
-
-        void setCircleCenter(int x, int y) {
-            if (DEBUG_EXIT_ANIMATION) {
-                Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
-            }
-            mCircleCenter.set(x, y);
-        }
-
-        void setRadialPaintParam(int[] colors, float[] stops) {
-            // setup gradient shader
-            final RadialGradient rShader =
-                    new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
-            mVanishPaint.setShader(rShader);
-            if (!DEBUG_EXIT_ANIMATION_BLEND) {
-                // We blend the reveal gradient with the splash screen using DST_OUT so that the
-                // splash screen is fully visible when radius = 0 (or gradient opacity is 0) and
-                // fully invisible when radius = finishRadius AND gradient opacity is 1.
-                mVanishPaint.setBlendMode(BlendMode.DST_OUT);
-            }
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-            canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
-        }
-    }
-
-    /**
-     * Shifts up the main window.
-     * @hide
-     */
-    public static final class ShiftUpAnimation {
-        private final float mFromYDelta;
-        private final float mToYDelta;
-        private final View mOccludeHoleView;
-        private final SyncRtSurfaceTransactionApplier mApplier;
-        private final Matrix mTmpTransform = new Matrix();
-        private final SurfaceControl mFirstWindowSurface;
-        private final ViewGroup mSplashScreenView;
-        private final TransactionPool mTransactionPool;
-        private final Rect mFirstWindowFrame;
-        private final int mMainWindowShiftLength;
-
-        public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView,
-                                SurfaceControl firstWindowSurface, ViewGroup splashScreenView,
-                                TransactionPool transactionPool, Rect firstWindowFrame,
-                                int mainWindowShiftLength, float roundedCornerRadius) {
-            mFromYDelta = firstWindowFrame.top
-                    - Math.max(firstWindowFrame.top, roundedCornerRadius);
-            mToYDelta = toYDelta;
-            mOccludeHoleView = occludeHoleView;
-            mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
-            mFirstWindowSurface = firstWindowSurface;
-            mSplashScreenView = splashScreenView;
-            mTransactionPool = transactionPool;
-            mFirstWindowFrame = firstWindowFrame;
-            mMainWindowShiftLength = mainWindowShiftLength;
-        }
-
-        void onAnimationProgress(float linearProgress) {
-            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
-                    || !mSplashScreenView.isAttachedToWindow()) {
-                return;
-            }
-
-            final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
-            final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;
-
-            mOccludeHoleView.setTranslationY(dy);
-            mTmpTransform.setTranslate(0 /* dx */, dy);
-
-            // set the vsyncId to ensure the transaction doesn't get applied too early.
-            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
-            tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
-            mTmpTransform.postTranslate(mFirstWindowFrame.left,
-                    mFirstWindowFrame.top + mMainWindowShiftLength);
-
-            SyncRtSurfaceTransactionApplier.SurfaceParams
-                    params = new SyncRtSurfaceTransactionApplier.SurfaceParams
-                    .Builder(mFirstWindowSurface)
-                    .withMatrix(mTmpTransform)
-                    .withMergeTransaction(tx)
-                    .build();
-            mApplier.scheduleApply(params);
-
-            mTransactionPool.release(tx);
-        }
-
-        void finish() {
-            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
-                return;
-            }
-            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
-            if (mSplashScreenView.isAttachedToWindow()) {
-                tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
-
-                SyncRtSurfaceTransactionApplier.SurfaceParams
-                        params = new SyncRtSurfaceTransactionApplier.SurfaceParams
-                        .Builder(mFirstWindowSurface)
-                        .withWindowCrop(null)
-                        .withMergeTransaction(tx)
-                        .build();
-                mApplier.scheduleApply(params);
-            } else {
-                tx.setWindowCrop(mFirstWindowSurface, null);
-                tx.apply();
-            }
-            mTransactionPool.release(tx);
-
-            Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
-                    mFirstWindowSurface::release, null);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 759f97f..81f444b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -78,8 +78,8 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.TransactionPool;
 
 import java.util.List;
 import java.util.function.Consumer;
@@ -352,12 +352,17 @@
     /** Extract the window background color from {@code attrs}. */
     private static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
-        final Drawable themeBGDrawable;
+        Drawable themeBGDrawable = null;
         if (attrs.mWindowBgColor != 0) {
             themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
         } else if (attrs.mWindowBgResId != 0) {
-            themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
-        } else {
+            try {
+                themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
+            } catch (Resources.NotFoundException e) {
+                Slog.w(TAG, "Unable get drawable from resource", e);
+            }
+        }
+        if (themeBGDrawable == null) {
             themeBGDrawable = createDefaultBackgroundDrawable();
             Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 97a695f..fac3592 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -46,8 +46,8 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
 
 /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index fa084c58..7cb8e8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -23,7 +23,7 @@
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS;
 
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.TaskInfo;
@@ -48,7 +48,7 @@
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
index bad5baf..2a22d4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java
@@ -36,7 +36,7 @@
 import android.window.TaskSnapshot;
 
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 
 class WindowlessSnapshotWindowCreator {
     private static final int DEFAULT_FADEOUT_DURATION = 233;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
index 1a38449..e1d7600 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java
@@ -36,7 +36,7 @@
 import android.window.StartingWindowRemovalInfo;
 
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 
 class WindowlessSplashWindowCreator extends AbsSplashWindowCreator {
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
index 287e779..1140c82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInit.java
@@ -48,7 +48,7 @@
 
     public ShellInit(ShellExecutor mainExecutor) {
         mMainExecutor = mainExecutor;
-        ProtoLog.registerGroups(ShellProtoLogGroup.values());
+        ProtoLog.init(ShellProtoLogGroup.values());
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java
deleted file mode 100644
index c886cc9..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java
+++ /dev/null
@@ -1,48 +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.wm.shell.sysui;
-
-/**
- * General shell-related constants that are shared with users of the library.
- */
-public class ShellSharedConstants {
-    // See IPip.aidl
-    public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
-    // See IBubbles.aidl
-    public static final String KEY_EXTRA_SHELL_BUBBLES = "extra_shell_bubbles";
-    // See ISplitScreen.aidl
-    public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
-    // See IOneHanded.aidl
-    public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed";
-    // See IShellTransitions.aidl
-    public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS =
-            "extra_shell_shell_transitions";
-    // See IStartingWindow.aidl
-    public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
-            "extra_shell_starting_window";
-    // See IRecentTasks.aidl
-    public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks";
-    // See IBackAnimation.aidl
-    public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
-    // See IDesktopMode.aidl
-    public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode";
-    // See IDragAndDrop.aidl
-    public static final String KEY_EXTRA_SHELL_DRAG_AND_DROP = "extra_shell_drag_and_drop";
-    // See IRecentsAnimationController.aidl
-    public static final String KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION =
-            "extra_shell_can_hand_off_animation";
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
index a85188a..82c0aaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskView.java
@@ -118,6 +118,13 @@
         mTaskViewTaskController.startShortcutActivity(shortcut, options, launchBounds);
     }
 
+    /**
+     * Moves the current task in taskview out of the view and back to fullscreen.
+     */
+    public void moveToFullscreen() {
+        mTaskViewTaskController.moveToFullscreen();
+    }
+
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
         if (mTaskViewTaskController.isUsingShellTransitions()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 9750d3e..e74342e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.taskview;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 
 import android.annotation.NonNull;
@@ -256,6 +257,24 @@
         mTaskViewTransitions.startInstantTransition(TRANSIT_CHANGE, wct);
     }
 
+    /**
+     * Moves the current task in TaskView out of the view and back to fullscreen.
+     */
+    public void moveToFullscreen() {
+        if (mTaskToken == null) return;
+        mShellExecutor.execute(() -> {
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setWindowingMode(mTaskToken, WINDOWING_MODE_UNDEFINED);
+            wct.setAlwaysOnTop(mTaskToken, false);
+            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
+            mTaskViewTransitions.moveTaskViewToFullscreen(wct, this);
+            if (mListener != null) {
+                // Task is being "removed" from the clients perspective
+                mListener.onTaskRemovalStarted(mTaskInfo.taskId);
+            }
+        });
+    }
+
     private void prepareActivityOptions(ActivityOptions options, Rect launchBounds) {
         final Binder launchCookie = new Binder();
         mShellExecutor.execute(() -> {
@@ -585,7 +604,6 @@
                 });
             }
             mTaskViewBase.onTaskVanished(taskInfo);
-            mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, false);
         }
     }
 
@@ -699,6 +717,9 @@
             mTaskViewBase.setResizeBgColor(startTransaction, backgroundColor);
         }
 
+        // After the embedded task has appeared, set it to non-trimmable. This is important
+        // to prevent recents from trimming and removing the embedded task.
+        wct.setTaskTrimmableFromRecents(taskInfo.token, false /* isTrimmableFromRecents */);
         mTaskViewBase.onTaskAppeared(mTaskInfo, mTaskLeash);
         if (mListener != null) {
             final int taskId = mTaskInfo.taskId;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 15fe7ab..39648f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -236,6 +236,12 @@
         startNextTransition();
     }
 
+    void moveTaskViewToFullscreen(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskViewTaskController taskView) {
+        mPending.add(new PendingTransition(TRANSIT_CHANGE, wct, taskView, null /* cookie */));
+        startNextTransition();
+    }
+
     /** Starts a new transition to make the given {@code taskView} visible. */
     public void setTaskViewVisible(TaskViewTaskController taskView, boolean visible) {
         setTaskViewVisible(taskView, visible, false /* reorder */);
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 7784784..4fc6c44 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
@@ -112,8 +112,8 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.sysui.ShellInit;
 
@@ -502,15 +502,19 @@
                 backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
                         backgroundColorForTransition);
 
-                if (!isTask && a.hasExtension()) {
-                    if (!TransitionUtil.isOpeningType(mode)) {
-                        // Can screenshot now (before startTransaction is applied)
-                        edgeExtendWindow(change, a, startTransaction, finishTransaction);
+                if (!isTask && a.getExtensionEdges() != 0x0) {
+                    if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+                        finishTransaction.setEdgeExtensionEffect(change.getLeash(), /* edge */ 0);
                     } else {
-                        // Need to screenshot after startTransaction is applied otherwise activity
-                        // may not be visible or ready yet.
-                        postStartTransactionCallbacks
-                                .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+                        if (!TransitionUtil.isOpeningType(mode)) {
+                            // Can screenshot now (before startTransaction is applied)
+                            edgeExtendWindow(change, a, startTransaction, finishTransaction);
+                        } else {
+                            // Need to screenshot after startTransaction is applied otherwise
+                            // activity may not be visible or ready yet.
+                            postStartTransactionCallbacks
+                                    .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+                        }
                     }
                 }
 
@@ -558,7 +562,7 @@
 
                 buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
                         mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
-                        clipRect);
+                        clipRect, change.getActivityComponent() != null);
 
                 final TransitionInfo.AnimationOptions options;
                 if (Flags.moveAnimationOptionsToChange()) {
@@ -823,7 +827,7 @@
             @NonNull Animation anim, @NonNull SurfaceControl leash,
             @NonNull Runnable finishCallback, @NonNull TransactionPool pool,
             @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
-            @Nullable Rect clipRect) {
+            @Nullable Rect clipRect, boolean isActivity) {
         final SurfaceControl.Transaction transaction = pool.acquire();
         final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
         final Transformation transformation = new Transformation();
@@ -835,13 +839,13 @@
             final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime());
 
             applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix,
-                    position, cornerRadius, clipRect);
+                    position, cornerRadius, clipRect, isActivity);
         };
         va.addUpdateListener(updateListener);
 
         final Runnable finisher = () -> {
             applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix,
-                    position, cornerRadius, clipRect);
+                    position, cornerRadius, clipRect, isActivity);
 
             pool.release(transaction);
             mainExecutor.execute(() -> {
@@ -931,7 +935,8 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+                change.getActivityComponent() != null);
     }
 
     private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -955,7 +960,8 @@
         a.restrictDuration(MAX_ANIMATION_DURATION);
         a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
-                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+                mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+                change.getActivityComponent() != null);
     }
 
     private static int getWallpaperTransitType(TransitionInfo info) {
@@ -1005,9 +1011,14 @@
 
     private static void applyTransformation(long time, SurfaceControl.Transaction t,
             SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
-            Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
+            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);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 9b27e41..3d79a1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -30,6 +30,7 @@
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
+import com.android.window.flags.Flags;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -71,9 +72,17 @@
 
             final int mode = change.getMode();
             final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
-            if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
-                    && (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture)) {
-                notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode) || isBackGesture);
+            if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+                if (Flags.migratePredictiveBackTransition()) {
+                    if (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode)) {
+                        notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode));
+                    }
+                } else {
+                    if (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture) {
+                        notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)
+                                || isBackGesture);
+                    }
+                }
             }
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index 8cc7f21..30d7245 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -20,8 +20,8 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index c5dc668..209fc39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -114,7 +114,6 @@
                 t.clear();
                 mMainExecutor.execute(() -> {
                     finishCallback.onTransitionFinished(wct);
-                    mRemote = null;
                 });
             }
         };
@@ -193,6 +192,8 @@
     public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
             @Nullable SurfaceControl.Transaction finishTransaction) {
         try {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                    "OneShot onTransitionConsumed for %s", mRemote);
             mRemote.getRemoteTransition().onTransitionConsumed(transition, aborted);
         } catch (RemoteException e) {
             Log.e(Transitions.TAG, "Error calling onTransitionConsumed()", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 391c5fe..fd4d568 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -18,7 +18,7 @@
 
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
 import static com.android.wm.shell.transition.DefaultMixedHandler.handoverTransitionLeashes;
 import static com.android.wm.shell.transition.MixedTransitionHelper.animateEnterPipFromSplit;
 import static com.android.wm.shell.transition.MixedTransitionHelper.animateKeyguard;
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 e196254..0bf9d36 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
@@ -47,7 +47,7 @@
 import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 
 import java.util.ArrayList;
 
@@ -325,21 +325,21 @@
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
         buildSurfaceAnimation(animations, mRotateEnterAnimation, mSurfaceControl, finishCallback,
                 mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
-                null /* clipRect */);
+                null /* clipRect */, false /* isActivity */);
     }
 
     private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
         buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
                 mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
-                null /* clipRect */);
+                null /* clipRect */, false /* isActivity */);
     }
 
     private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
             @NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
         buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
                 mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
-                null /* clipRect */);
+                null /* clipRect */, false /* isActivity */);
     }
 
     private void startColorAnimation(float animationScale, @NonNull ShellExecutor animExecutor) {
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 c850ff8..7dc336b 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
@@ -38,12 +38,12 @@
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
-import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
+import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
 import static com.android.window.flags.Flags.migratePredictiveBackTransition;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -85,12 +85,12 @@
 import com.android.wm.shell.common.ExternalInterfaceBinder;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 import com.android.wm.shell.shared.IHomeTransitionListener;
 import com.android.wm.shell.shared.IShellTransitions;
 import com.android.wm.shell.shared.ShellTransitions;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.shared.TransitionUtil;
 import com.android.wm.shell.shared.annotations.ExternalThread;
 import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -193,6 +193,9 @@
     /** Remote Transition that split accepts but ultimately needs to be animated by the remote. */
     public static final int TRANSIT_SPLIT_PASSTHROUGH = TRANSIT_FIRST_CUSTOM + 18;
 
+    /** Transition to set windowing mode after exit pip transition is finished animating. */
+    public static final int TRANSIT_CLEANUP_PIP_EXIT = WindowManager.TRANSIT_FIRST_CUSTOM + 19;
+
     /** Transition type for desktop mode transitions. */
     public static final int TRANSIT_DESKTOP_MODE_TYPES =
             WindowManager.TRANSIT_FIRST_CUSTOM + 100;
@@ -717,7 +720,11 @@
                 Log.e(TAG, "Got duplicate transitionReady for " + transitionToken);
                 // The transition is already somewhere else in the pipeline, so just return here.
                 t.apply();
-                existing.mFinishT.merge(finishT);
+                if (existing.mFinishT != null) {
+                    existing.mFinishT.merge(finishT);
+                } else {
+                    existing.mFinishT = finishT;
+                }
                 return;
             }
             // This usually means the system is in a bad state and may not recover; however,
@@ -1183,12 +1190,15 @@
             }
             if (request.getDisplayChange() != null) {
                 TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
-                if (change.getEndRotation() != change.getStartRotation()) {
-                    // Is a rotation, so dispatch to all displayChange listeners
+                if (change.getStartRotation() != change.getEndRotation()
+                        || (change.getStartAbsBounds() != null
+                        && !change.getStartAbsBounds().equals(change.getEndAbsBounds()))) {
+                    // Is a display change, so dispatch to all displayChange listeners
                     if (wct == null) {
                         wct = new WindowContainerTransaction();
                     }
-                    mDisplayController.onDisplayRotateRequested(wct, change.getDisplayId(),
+                    mDisplayController.onDisplayChangeRequested(wct, change.getDisplayId(),
+                            change.getStartAbsBounds(), change.getEndAbsBounds(),
                             change.getStartRotation(), change.getEndRotation());
                 }
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
index e6d35e8..2ca749c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java
@@ -23,7 +23,7 @@
 import android.view.SurfaceControl;
 
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener;
 import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
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 88bfebf..f783b45 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
@@ -34,7 +34,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.protolog.ProtoLog;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 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/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index bb5d546..d28287d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -19,8 +19,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+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 static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
@@ -41,7 +41,7 @@
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.wm.shell.common.DisplayInsetsController;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
 import com.android.wm.shell.splitscreen.SplitScreenController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl
deleted file mode 100644
index 15797cd..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.aidl
+++ /dev/null
@@ -1,19 +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.wm.shell.util;
-
-parcelable GroupedRecentTaskInfo;
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
deleted file mode 100644
index a2d2b9a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/GroupedRecentTaskInfo.java
+++ /dev/null
@@ -1,234 +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.wm.shell.util;
-
-import android.annotation.IntDef;
-import android.app.ActivityManager;
-import android.app.WindowConfiguration;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Simple container for recent tasks.  May contain either a single or pair of tasks.
- */
-public class GroupedRecentTaskInfo implements Parcelable {
-
-    public static final int TYPE_SINGLE = 1;
-    public static final int TYPE_SPLIT = 2;
-    public static final int TYPE_FREEFORM = 3;
-
-    @IntDef(prefix = {"TYPE_"}, value = {
-            TYPE_SINGLE,
-            TYPE_SPLIT,
-            TYPE_FREEFORM
-    })
-    public @interface GroupType {}
-
-    @NonNull
-    private final ActivityManager.RecentTaskInfo[] mTasks;
-    @Nullable
-    private final SplitBounds mSplitBounds;
-    @GroupType
-    private final int mType;
-    // TODO(b/348332802): move isMinimized inside each Task object instead once we have a
-    //  replacement for RecentTaskInfo
-    private final int[] mMinimizedTaskIds;
-
-    /**
-     * Create new for a single task
-     */
-    public static GroupedRecentTaskInfo forSingleTask(
-            @NonNull ActivityManager.RecentTaskInfo task) {
-        return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task}, null,
-                TYPE_SINGLE, null /* minimizedFreeformTasks */);
-    }
-
-    /**
-     * Create new for a pair of tasks in split screen
-     */
-    public static GroupedRecentTaskInfo forSplitTasks(@NonNull ActivityManager.RecentTaskInfo task1,
-            @NonNull ActivityManager.RecentTaskInfo task2, @Nullable SplitBounds splitBounds) {
-        return new GroupedRecentTaskInfo(new ActivityManager.RecentTaskInfo[]{task1, task2},
-                splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */);
-    }
-
-    /**
-     * Create new for a group of freeform tasks
-     */
-    public static GroupedRecentTaskInfo forFreeformTasks(
-            @NonNull ActivityManager.RecentTaskInfo[] tasks,
-            @NonNull Set<Integer> minimizedFreeformTasks) {
-        return new GroupedRecentTaskInfo(
-                tasks,
-                null /* splitBounds */,
-                TYPE_FREEFORM,
-                minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
-    }
-
-    private GroupedRecentTaskInfo(
-            @NonNull ActivityManager.RecentTaskInfo[] tasks,
-            @Nullable SplitBounds splitBounds,
-            @GroupType int type,
-            @Nullable int[] minimizedFreeformTaskIds) {
-        mTasks = tasks;
-        mSplitBounds = splitBounds;
-        mType = type;
-        mMinimizedTaskIds = minimizedFreeformTaskIds;
-        ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
-    }
-
-    private static void ensureAllMinimizedIdsPresent(
-            @NonNull ActivityManager.RecentTaskInfo[] tasks,
-            @Nullable int[] minimizedFreeformTaskIds) {
-        if (minimizedFreeformTaskIds == null) {
-            return;
-        }
-        if (!Arrays.stream(minimizedFreeformTaskIds).allMatch(
-                taskId -> Arrays.stream(tasks).anyMatch(task -> task.taskId == taskId))) {
-            throw new IllegalArgumentException("Minimized task IDs contain non-existent Task ID.");
-        }
-    }
-
-    GroupedRecentTaskInfo(Parcel parcel) {
-        mTasks = parcel.createTypedArray(ActivityManager.RecentTaskInfo.CREATOR);
-        mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
-        mType = parcel.readInt();
-        mMinimizedTaskIds = parcel.createIntArray();
-    }
-
-    /**
-     * Get primary {@link ActivityManager.RecentTaskInfo}
-     */
-    @NonNull
-    public ActivityManager.RecentTaskInfo getTaskInfo1() {
-        return mTasks[0];
-    }
-
-    /**
-     * Get secondary {@link ActivityManager.RecentTaskInfo}.
-     *
-     * Used in split screen.
-     */
-    @Nullable
-    public ActivityManager.RecentTaskInfo getTaskInfo2() {
-        if (mTasks.length > 1) {
-            return mTasks[1];
-        }
-        return null;
-    }
-
-    /**
-     * Get all {@link ActivityManager.RecentTaskInfo}s grouped together.
-     */
-    @NonNull
-    public List<ActivityManager.RecentTaskInfo> getTaskInfoList() {
-        return Arrays.asList(mTasks);
-    }
-
-    /**
-     * Return {@link SplitBounds} if this is a split screen entry or {@code null}
-     */
-    @Nullable
-    public SplitBounds getSplitBounds() {
-        return mSplitBounds;
-    }
-
-    /**
-     * Get type of this recents entry. One of {@link GroupType}
-     */
-    @GroupType
-    public int getType() {
-        return mType;
-    }
-
-    public int[] getMinimizedTaskIds() {
-        return mMinimizedTaskIds;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder taskString = new StringBuilder();
-        for (int i = 0; i < mTasks.length; i++) {
-            if (i == 0) {
-                taskString.append("Task");
-            } else {
-                taskString.append(", Task");
-            }
-            taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks[i]));
-        }
-        if (mSplitBounds != null) {
-            taskString.append(", SplitBounds: ").append(mSplitBounds);
-        }
-        taskString.append(", Type=");
-        switch (mType) {
-            case TYPE_SINGLE:
-                taskString.append("TYPE_SINGLE");
-                break;
-            case TYPE_SPLIT:
-                taskString.append("TYPE_SPLIT");
-                break;
-            case TYPE_FREEFORM:
-                taskString.append("TYPE_FREEFORM");
-                break;
-        }
-        taskString.append(", Minimized Task IDs: ");
-        taskString.append(Arrays.toString(mMinimizedTaskIds));
-        return taskString.toString();
-    }
-
-    private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
-        if (taskInfo == null) {
-            return null;
-        }
-        return "id=" + taskInfo.taskId
-                + " baseIntent=" + (taskInfo.baseIntent != null
-                        ? taskInfo.baseIntent.getComponent()
-                        : "null")
-                + " winMode=" + WindowConfiguration.windowingModeToString(
-                        taskInfo.getWindowingMode());
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeTypedArray(mTasks, flags);
-        parcel.writeTypedObject(mSplitBounds, flags);
-        parcel.writeInt(mType);
-        parcel.writeIntArray(mMinimizedTaskIds);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
-            new Creator<GroupedRecentTaskInfo>() {
-        public GroupedRecentTaskInfo createFromParcel(Parcel source) {
-            return new GroupedRecentTaskInfo(source);
-        }
-        public GroupedRecentTaskInfo[] newArray(int size) {
-            return new GroupedRecentTaskInfo[size];
-        }
-    };
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
deleted file mode 100644
index 482aaab..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
deleted file mode 100644
index 3e06d2d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/SplitBounds.java
+++ /dev/null
@@ -1,153 +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.wm.shell.util;
-
-import android.graphics.Rect;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-
-import java.util.Objects;
-
-/**
- * Container of various information needed to display split screen
- * tasks/leashes/etc in Launcher
- */
-public class SplitBounds implements Parcelable {
-    public static final String KEY_EXTRA_SPLIT_BOUNDS = "key_SplitBounds";
-
-    public final Rect leftTopBounds;
-    public final Rect rightBottomBounds;
-    /** This rect represents the actual gap between the two apps */
-    public final Rect visualDividerBounds;
-    // This class is orientation-agnostic, so we compute both for later use
-    public final float topTaskPercent;
-    public final float leftTaskPercent;
-    public final float dividerWidthPercent;
-    public final float dividerHeightPercent;
-    public final @PersistentSnapPosition int snapPosition;
-    /**
-     * If {@code true}, that means at the time of creation of this object, the
-     * split-screened apps were vertically stacked. This is useful in scenarios like
-     * rotation where the bounds won't change, but this variable can indicate what orientation
-     * the bounds were originally in
-     */
-    public final boolean appsStackedVertically;
-    public final int leftTopTaskId;
-    public final int rightBottomTaskId;
-
-    public SplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
-            int rightBottomTaskId, @PersistentSnapPosition int snapPosition) {
-        this.leftTopBounds = leftTopBounds;
-        this.rightBottomBounds = rightBottomBounds;
-        this.leftTopTaskId = leftTopTaskId;
-        this.rightBottomTaskId = rightBottomTaskId;
-        this.snapPosition = snapPosition;
-
-        if (rightBottomBounds.top > leftTopBounds.top) {
-            // vertical apps, horizontal divider
-            this.visualDividerBounds = new Rect(leftTopBounds.left, leftTopBounds.bottom,
-                    leftTopBounds.right, rightBottomBounds.top);
-            appsStackedVertically = true;
-        } else {
-            // horizontal apps, vertical divider
-            this.visualDividerBounds = new Rect(leftTopBounds.right, leftTopBounds.top,
-                    rightBottomBounds.left, leftTopBounds.bottom);
-            appsStackedVertically = false;
-        }
-
-        float totalWidth = rightBottomBounds.right - leftTopBounds.left;
-        float totalHeight = rightBottomBounds.bottom - leftTopBounds.top;
-        leftTaskPercent = leftTopBounds.width() / totalWidth;
-        topTaskPercent = leftTopBounds.height() / totalHeight;
-        dividerWidthPercent = visualDividerBounds.width() / totalWidth;
-        dividerHeightPercent = visualDividerBounds.height() / totalHeight;
-    }
-
-    public SplitBounds(Parcel parcel) {
-        leftTopBounds = parcel.readTypedObject(Rect.CREATOR);
-        rightBottomBounds = parcel.readTypedObject(Rect.CREATOR);
-        visualDividerBounds = parcel.readTypedObject(Rect.CREATOR);
-        topTaskPercent = parcel.readFloat();
-        leftTaskPercent = parcel.readFloat();
-        appsStackedVertically = parcel.readBoolean();
-        leftTopTaskId = parcel.readInt();
-        rightBottomTaskId = parcel.readInt();
-        dividerWidthPercent = parcel.readFloat();
-        dividerHeightPercent = parcel.readFloat();
-        snapPosition = parcel.readInt();
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeTypedObject(leftTopBounds, flags);
-        parcel.writeTypedObject(rightBottomBounds, flags);
-        parcel.writeTypedObject(visualDividerBounds, flags);
-        parcel.writeFloat(topTaskPercent);
-        parcel.writeFloat(leftTaskPercent);
-        parcel.writeBoolean(appsStackedVertically);
-        parcel.writeInt(leftTopTaskId);
-        parcel.writeInt(rightBottomTaskId);
-        parcel.writeFloat(dividerWidthPercent);
-        parcel.writeFloat(dividerHeightPercent);
-        parcel.writeInt(snapPosition);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (!(obj instanceof SplitBounds)) {
-            return false;
-        }
-        // Only need to check the base fields (the other fields are derived from these)
-        final SplitBounds other = (SplitBounds) obj;
-        return Objects.equals(leftTopBounds, other.leftTopBounds)
-                && Objects.equals(rightBottomBounds, other.rightBottomBounds)
-                && leftTopTaskId == other.leftTopTaskId
-                && rightBottomTaskId == other.rightBottomTaskId;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(leftTopBounds, rightBottomBounds, leftTopTaskId, rightBottomTaskId);
-    }
-
-    @Override
-    public String toString() {
-        return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
-                + "RightBottom: " + rightBottomBounds + ", taskId: " + rightBottomTaskId +  "\n"
-                + "Divider: " + visualDividerBounds + "\n"
-                + "AppsVertical? " + appsStackedVertically + "\n"
-                + "snapPosition: " + snapPosition;
-    }
-
-    public static final Creator<SplitBounds> CREATOR = new Creator<SplitBounds>() {
-        @Override
-        public SplitBounds createFromParcel(Parcel in) {
-            return new SplitBounds(in);
-        }
-
-        @Override
-        public SplitBounds[] newArray(int size) {
-            return new SplitBounds[size];
-        }
-    };
-}
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 d8dba71..11976ae 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
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
@@ -26,7 +27,6 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -174,8 +174,12 @@
 
         if (decoration == null) return;
 
+        if (!shouldShowWindowDecor(taskInfo)) {
+            destroyWindowDecoration(taskInfo);
+            return;
+        }
+
         decoration.relayout(taskInfo);
-        setupCaptionColor(taskInfo, decoration);
     }
 
     @Override
@@ -237,19 +241,13 @@
         decoration.close();
     }
 
-    private void setupCaptionColor(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
-        if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
-            decoration.setCaptionColor(Color.TRANSPARENT);
-        } else {
-            final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
-            decoration.setCaptionColor(statusBarColor);
-        }
-    }
-
     private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
         if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             return true;
         }
+        if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+            return false;
+        }
         if (taskInfo.getActivityType() != ACTIVITY_TYPE_STANDARD) {
             return false;
         }
@@ -311,7 +309,6 @@
         windowDecoration.setTaskDragResizer(taskPositioner);
         windowDecoration.relayout(taskInfo, startT, finishT,
                 false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */);
-        setupCaptionColor(taskInfo, windowDecoration);
     }
 
     private class CaptionTouchEventListener implements
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 401b78d..349ee0b 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
@@ -19,6 +19,7 @@
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -34,7 +35,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
 import android.util.Size;
 import android.view.Choreographer;
@@ -260,6 +260,8 @@
             setupRootView();
         }
 
+        bindData(mResult.mRootView, taskInfo);
+
         if (!isDragResizeable) {
             closeDragResizeListener();
             return;
@@ -286,7 +288,8 @@
         final Resources res = mResult.mRootView.getResources();
         mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
                 new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(mContext, res),
-                getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop);
+                getResizeHandleEdgeInset(res), getFineResizeCornerSize(res),
+                getLargeResizeCornerSize(res)), touchSlop);
     }
 
     /**
@@ -305,7 +308,27 @@
         maximize.setOnClickListener(mOnCaptionButtonClickListener);
     }
 
-    void setCaptionColor(int captionColor) {
+    private void bindData(View rootView, RunningTaskInfo taskInfo) {
+        // Set up the tint first so that the drawable can be stylized when loaded.
+        setupCaptionColor(taskInfo);
+
+        final boolean isFullscreen =
+                taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+        rootView.findViewById(R.id.maximize_window)
+                .setBackgroundResource(isFullscreen ? R.drawable.decor_restore_button_dark
+                        : R.drawable.decor_maximize_button_dark);
+    }
+
+    private void setupCaptionColor(RunningTaskInfo taskInfo) {
+        if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
+            setCaptionColor(Color.TRANSPARENT);
+        } else {
+            final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
+            setCaptionColor(statusBarColor);
+        }
+    }
+
+    private void setCaptionColor(int captionColor) {
         if (mResult.mRootView == null) {
             return;
         }
@@ -322,20 +345,16 @@
                 caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
 
         final View back = caption.findViewById(R.id.back_button);
-        final VectorDrawable backBackground = (VectorDrawable) back.getBackground();
-        backBackground.setTintList(buttonTintColor);
+        back.setBackgroundTintList(buttonTintColor);
 
         final View minimize = caption.findViewById(R.id.minimize_window);
-        final VectorDrawable minimizeBackground = (VectorDrawable) minimize.getBackground();
-        minimizeBackground.setTintList(buttonTintColor);
+        minimize.setBackgroundTintList(buttonTintColor);
 
         final View maximize = caption.findViewById(R.id.maximize_window);
-        final VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground();
-        maximizeBackground.setTintList(buttonTintColor);
+        maximize.setBackgroundTintList(buttonTintColor);
 
         final View close = caption.findViewById(R.id.close_window);
-        final VectorDrawable closeBackground = (VectorDrawable) close.getBackground();
-        closeBackground.setTintList(buttonTintColor);
+        close.setBackgroundTintList(buttonTintColor);
     }
 
     boolean isHandlingDragResize() {
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 8c8f205..c88c1e2 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
@@ -34,13 +34,13 @@
 import static android.view.WindowInsets.Type.statusBars;
 
 import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR;
 import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+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.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import android.annotation.NonNull;
@@ -56,6 +56,7 @@
 import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -74,6 +75,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.widget.Toast;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
@@ -95,16 +97,18 @@
 import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
-import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
 import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
+import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
+import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -149,6 +153,7 @@
     private final InputManager mInputManager;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final MultiInstanceHelper mMultiInstanceHelper;
+    private final Optional<DesktopTasksLimiter> mDesktopTasksLimiter;
     private boolean mTransitionDragActive;
 
     private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
@@ -162,6 +167,8 @@
     private TaskOperations mTaskOperations;
     private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
     private final Transitions mTransitions;
+    private final Optional<DesktopActivityOrientationChangeHandler>
+            mActivityOrientationChangeHandler;
 
     private SplitScreenController mSplitScreenController;
 
@@ -210,7 +217,9 @@
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             InteractionJankMonitor interactionJankMonitor,
             AppToWebGenericLinksParser genericLinksParser,
-            MultiInstanceHelper multiInstanceHelper
+            MultiInstanceHelper multiInstanceHelper,
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler
     ) {
         this(
                 context,
@@ -235,7 +244,9 @@
                 SurfaceControl.Transaction::new,
                 rootTaskDisplayAreaOrganizer,
                 new SparseArray<>(),
-                interactionJankMonitor);
+                interactionJankMonitor,
+                desktopTasksLimiter,
+                activityOrientationChangeHandler);
     }
 
     @VisibleForTesting
@@ -262,7 +273,9 @@
             Supplier<SurfaceControl.Transaction> transactionFactory,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler) {
         mContext = context;
         mMainExecutor = shellExecutor;
         mMainHandler = mainHandler;
@@ -289,6 +302,8 @@
         mSysUIPackageName = mContext.getResources().getString(
                 com.android.internal.R.string.config_systemUi);
         mInteractionJankMonitor = interactionJankMonitor;
+        mDesktopTasksLimiter = desktopTasksLimiter;
+        mActivityOrientationChangeHandler = activityOrientationChangeHandler;
         mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
             DesktopModeWindowDecoration decoration;
             RunningTaskInfo taskInfo;
@@ -380,6 +395,8 @@
             incrementEventReceiverTasks(taskInfo.displayId);
         }
         decoration.relayout(taskInfo);
+        mActivityOrientationChangeHandler.ifPresent(handler ->
+                handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
     }
 
     @Override
@@ -469,9 +486,14 @@
 
         if (!decoration.mTaskInfo.isResizeable
                 && DesktopModeFlags.DISABLE_SNAP_RESIZE.isEnabled(mContext)) {
-            //TODO(b/354658237) - show toast with relevant string
+            Toast.makeText(mContext,
+                    R.string.desktop_mode_non_resizable_snap_text, Toast.LENGTH_SHORT).show();
         } else {
-            mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo,
+            mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,
+                    Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable");
+            mDesktopTasksController.snapToHalfScreen(
+                    decoration.mTaskInfo,
+                    decoration.mTaskSurface,
                     decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),
                     left ? SnapPosition.LEFT : SnapPosition.RIGHT);
         }
@@ -485,16 +507,16 @@
         if (decoration == null) {
             return;
         }
-        openInBrowser(uri);
+        openInBrowser(uri, decoration.getUser());
         decoration.closeHandleMenu();
         decoration.closeMaximizeMenu();
     }
 
-    private void openInBrowser(Uri uri) {
+    private void openInBrowser(Uri uri, @NonNull UserHandle userHandle) {
         final Intent intent = Intent.makeMainSelectorActivity(ACTION_MAIN, CATEGORY_APP_BROWSER)
                 .setData(uri)
                 .addFlags(FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
+        mContext.startActivityAsUser(intent, userHandle);
     }
 
     private void onToDesktop(int taskId, DesktopModeTransitionSource source) {
@@ -613,6 +635,12 @@
                 //  {@link DesktopModeWindowDecoration#setOnMaximizeOrRestoreClickListener}, which
                 //  should shared with the maximize menu's maximize/restore actions.
                 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));
             }
         }
 
@@ -626,7 +654,7 @@
             }
             if (id != R.id.caption_handle && id != R.id.desktop_mode_caption
                     && id != R.id.open_menu_button && id != R.id.close_window
-                    && id != R.id.maximize_window) {
+                    && id != R.id.maximize_window && id != R.id.minimize_window) {
                 return false;
             }
 
@@ -766,7 +794,7 @@
                 return true;
             }
             final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window
-                    || id == R.id.open_menu_button);
+                    || id == R.id.open_menu_button || id == R.id.minimize_window);
             switch (e.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
                     mDragPointerId = e.getPointerId(0);
@@ -1029,7 +1057,7 @@
                 }
                 final boolean shouldStartTransitionDrag =
                         relevantDecor.checkTouchEventInFocusedCaptionHandle(ev)
-                        || Flags.enableAdditionalWindowsAboveStatusBar();
+                                || Flags.enableAdditionalWindowsAboveStatusBar();
                 if (dragFromStatusBarAllowed && shouldStartTransitionDrag) {
                     mTransitionDragActive = true;
                 }
@@ -1037,8 +1065,13 @@
             }
             case MotionEvent.ACTION_UP: {
                 if (mTransitionDragActive) {
+                    final DesktopModeVisualIndicator.DragStartState dragStartState =
+                            DesktopModeVisualIndicator.DragStartState
+                                    .getDragStartState(relevantDecor.mTaskInfo);
+                    if (dragStartState == null) return;
                     mDesktopTasksController.updateVisualIndicator(relevantDecor.mTaskInfo,
-                            relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
+                            relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY(),
+                            dragStartState);
                     mTransitionDragActive = false;
                     if (mMoveToDesktopAnimator != null) {
                         // Though this isn't a hover event, we need to update handle's hover state
@@ -1078,10 +1111,15 @@
                             && mMoveToDesktopAnimator == null) {
                         return;
                     }
+                    final DesktopModeVisualIndicator.DragStartState dragStartState =
+                            DesktopModeVisualIndicator.DragStartState
+                                    .getDragStartState(relevantDecor.mTaskInfo);
+                    if (dragStartState == null) return;
                     final DesktopModeVisualIndicator.IndicatorType indicatorType =
                             mDesktopTasksController.updateVisualIndicator(
                                     relevantDecor.mTaskInfo,
-                                    relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY());
+                                    relevantDecor.mTaskSurface, ev.getRawX(), ev.getRawY(),
+                                    dragStartState);
                     if (indicatorType != TO_FULLSCREEN_INDICATOR) {
                         if (mMoveToDesktopAnimator == null) {
                             mMoveToDesktopAnimator = new MoveToDesktopAnimator(
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 538d0fb..aa43c8d 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
@@ -26,11 +26,12 @@
 import static android.view.MotionEvent.ACTION_UP;
 
 import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
-import static com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
 import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeHandleEdgeInset;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -53,6 +54,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.util.Size;
 import android.util.Slog;
 import android.view.Choreographer;
@@ -78,10 +80,10 @@
 import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
 import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
@@ -112,6 +114,7 @@
     private final Choreographer mChoreographer;
     private final SyncTransactionQueue mSyncQueue;
     private final SplitScreenController mSplitScreenController;
+    private final WindowManagerWrapper mWindowManagerWrapper;
 
     private WindowDecorationViewHolder mWindowDecorViewHolder;
     private View.OnClickListener mOnCaptionButtonClickListener;
@@ -186,7 +189,9 @@
                 taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
                 rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new,
                 SurfaceControl.Transaction::new,  WindowContainerTransaction::new,
-                SurfaceControl::new, new SurfaceControlViewHostFactory() {},
+                SurfaceControl::new, new WindowManagerWrapper(
+                        context.getSystemService(WindowManager.class)),
+                new SurfaceControlViewHostFactory() {},
                 DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE,
                 multiInstanceHelper);
     }
@@ -209,6 +214,7 @@
             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
             Supplier<SurfaceControl> surfaceControlSupplier,
+            WindowManagerWrapper windowManagerWrapper,
             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
             MaximizeMenuFactory maximizeMenuFactory,
             HandleMenuFactory handleMenuFactory,
@@ -227,6 +233,7 @@
         mMaximizeMenuFactory = maximizeMenuFactory;
         mHandleMenuFactory = handleMenuFactory;
         mMultiInstanceHelper = multiInstanceHelper;
+        mWindowManagerWrapper = windowManagerWrapper;
     }
 
     /**
@@ -479,6 +486,10 @@
         return mGenericLink;
     }
 
+    UserHandle getUser() {
+        return mUserContext.getUser();
+    }
+
     private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
         if (!isDragResizable(mTaskInfo)) {
             if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
@@ -515,8 +526,8 @@
         if (mDragResizeListener.setGeometry(
                 new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
                         new Size(mResult.mWidth, mResult.mHeight),
-                        getResizeEdgeHandleSize(mContext, res), getFineResizeCornerSize(res),
-                        getLargeResizeCornerSize(res)), touchSlop)
+                        getResizeEdgeHandleSize(mContext, res), getResizeHandleEdgeInset(res),
+                        getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
                 || !mTaskInfo.positionInParent.equals(mPositionInParent)) {
             updateExclusionRegion();
         }
@@ -568,7 +579,8 @@
             return new AppHandleViewHolder(
                     mResult.mRootView,
                     mOnCaptionTouchListener,
-                    mOnCaptionButtonClickListener
+                    mOnCaptionButtonClickListener,
+                    mWindowManagerWrapper
             );
         } else if (mRelayoutParams.mLayoutResId
                 == R.layout.desktop_mode_app_header) {
@@ -642,6 +654,10 @@
             final RelayoutParams.OccludingCaptionElement controlsElement =
                     new RelayoutParams.OccludingCaptionElement();
             controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
+            if (Flags.enableMinimizeButton()) {
+                controlsElement.mWidthResId =
+                      R.dimen.desktop_mode_customizable_caption_with_minimize_button_margin_end;
+            }
             controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
             relayoutParams.mOccludingCaptionElements.add(controlsElement);
         } else if (isAppHandle && !Flags.enableAdditionalWindowsAboveStatusBar()) {
@@ -978,6 +994,7 @@
         updateGenericLink();
         mHandleMenu = mHandleMenuFactory.create(
                 this,
+                mWindowManagerWrapper,
                 mRelayoutParams.mLayoutResId,
                 mAppIconBitmap,
                 mAppName,
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 0f2de70..cb9781e 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
@@ -82,32 +82,26 @@
         final int oldRight = repositionTaskBounds.right;
         final int oldBottom = repositionTaskBounds.bottom;
 
-
         repositionTaskBounds.set(taskBoundsAtDragStart);
 
         // Make sure the new resizing destination in any direction falls within the stable bounds.
-        // If not, set the bounds back to the old location that was valid to avoid conflicts with
-        // some regions such as the gesture area.
         if ((ctrlType & CTRL_TYPE_LEFT) != 0) {
-            final int candidateLeft = repositionTaskBounds.left + (int) delta.x;
-            repositionTaskBounds.left = (candidateLeft > stableBounds.left)
-                    ? candidateLeft : oldLeft;
+            repositionTaskBounds.left = Math.max(repositionTaskBounds.left + (int) delta.x,
+                    stableBounds.left);
         }
         if ((ctrlType & CTRL_TYPE_RIGHT) != 0) {
-            final int candidateRight = repositionTaskBounds.right + (int) delta.x;
-            repositionTaskBounds.right = (candidateRight < stableBounds.right)
-                    ? candidateRight : oldRight;
+            repositionTaskBounds.right = Math.min(repositionTaskBounds.right + (int) delta.x,
+                    stableBounds.right);
         }
         if ((ctrlType & CTRL_TYPE_TOP) != 0) {
-            final int candidateTop = repositionTaskBounds.top + (int) delta.y;
-            repositionTaskBounds.top = (candidateTop > stableBounds.top)
-                    ? candidateTop : oldTop;
+            repositionTaskBounds.top = Math.max(repositionTaskBounds.top + (int) delta.y,
+                    stableBounds.top);
         }
         if ((ctrlType & CTRL_TYPE_BOTTOM) != 0) {
-            final int candidateBottom = repositionTaskBounds.bottom + (int) delta.y;
-            repositionTaskBounds.bottom = (candidateBottom < stableBounds.bottom)
-                    ? candidateBottom : oldBottom;
+            repositionTaskBounds.bottom = Math.min(repositionTaskBounds.bottom + (int) delta.y,
+                    stableBounds.bottom);
         }
+
         // If width or height are negative or exceeding the width or height constraints, revert the
         // respective bounds to use previous bound dimensions.
         if (isExceedingWidthConstraint(repositionTaskBounds, stableBounds, displayController,
@@ -120,14 +114,12 @@
             repositionTaskBounds.top = oldTop;
             repositionTaskBounds.bottom = oldBottom;
         }
-        // If there are no changes to the bounds after checking new bounds against minimum width
-        // and height, do not set bounds and return false
-        if (oldLeft == repositionTaskBounds.left && oldTop == repositionTaskBounds.top
-                && oldRight == repositionTaskBounds.right
-                && oldBottom == repositionTaskBounds.bottom) {
-            return false;
-        }
-        return true;
+
+        // If there are no changes to the bounds after checking new bounds against minimum and
+        // maximum width and height, do not set bounds and return false
+        return oldLeft != repositionTaskBounds.left || oldTop != repositionTaskBounds.top
+                || oldRight != repositionTaskBounds.right
+                || oldBottom != repositionTaskBounds.bottom;
     }
 
     /**
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 014d61d..6dedf6d 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.windowdecor;
 
+import static android.view.InputDevice.SOURCE_MOUSE;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 
 import static com.android.wm.shell.shared.desktopmode.DesktopModeFlags.EDGE_DRAG_RESIZE;
@@ -44,27 +45,32 @@
 final class DragResizeWindowGeometry {
     private final int mTaskCornerRadius;
     private final Size mTaskSize;
-    // The size of the handle applied to the edges of the window, for the user to drag resize.
-    private final int mResizeHandleThickness;
+    // The size of the handle outside the task window applied to the edges of the window, for the
+    // user to drag resize.
+    private final int mResizeHandleEdgeOutset;
+    // The size of the handle inside the task window applied to the edges of the window, for the
+    // user to drag resize.
+    private final int mResizeHandleEdgeInset;
     // The task corners to permit drag resizing with a course input, such as touch.
-
     private final @NonNull TaskCorners mLargeTaskCorners;
     // The task corners to permit drag resizing with a fine input, such as stylus or cursor.
     private final @NonNull TaskCorners mFineTaskCorners;
     // The bounds for each edge drag region, which can resize the task in one direction.
-    private final @NonNull TaskEdges mTaskEdges;
+    final @NonNull TaskEdges mTaskEdges;
 
     DragResizeWindowGeometry(int taskCornerRadius, @NonNull Size taskSize,
-            int resizeHandleThickness, int fineCornerSize, int largeCornerSize) {
+            int resizeHandleEdgeOutset, int resizeHandleEdgeInset, int fineCornerSize,
+            int largeCornerSize) {
         mTaskCornerRadius = taskCornerRadius;
         mTaskSize = taskSize;
-        mResizeHandleThickness = resizeHandleThickness;
+        mResizeHandleEdgeOutset = resizeHandleEdgeOutset;
+        mResizeHandleEdgeInset = resizeHandleEdgeInset;
 
         mLargeTaskCorners = new TaskCorners(mTaskSize, largeCornerSize);
         mFineTaskCorners = new TaskCorners(mTaskSize, fineCornerSize);
 
         // Save touch areas for each edge.
-        mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleThickness);
+        mTaskEdges = new TaskEdges(mTaskSize, mResizeHandleEdgeOutset, mResizeHandleEdgeInset);
     }
 
     /**
@@ -72,11 +78,18 @@
      */
     static int getResizeEdgeHandleSize(@NonNull Context context, @NonNull Resources res) {
         return EDGE_DRAG_RESIZE.isEnabled(context)
-                ? res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
+                ? res.getDimensionPixelSize(R.dimen.freeform_edge_handle_outset)
                 : res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
     }
 
     /**
+     * Returns the resource value to use for the edge resize handle inside the task bounds.
+     */
+    static int getResizeHandleEdgeInset(@NonNull Resources res) {
+        return res.getDimensionPixelSize(R.dimen.freeform_edge_handle_inset);
+    }
+
+    /**
      * Returns the resource value to use for course input, such as touch, that benefits from a large
      * square on each of the window's corners.
      */
@@ -95,7 +108,8 @@
     /**
      * Returns the size of the task this geometry is calculated for.
      */
-    @NonNull Size getTaskSize() {
+    @NonNull
+    Size getTaskSize() {
         // Safe to return directly since size is immutable.
         return mTaskSize;
     }
@@ -153,7 +167,10 @@
     static boolean isEdgeResizePermitted(@NonNull Context context, @NonNull MotionEvent e) {
         if (EDGE_DRAG_RESIZE.isEnabled(context)) {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
-                    || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
+                    || e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE
+                    // Touchpad input
+                    || (e.isFromSource(SOURCE_MOUSE)
+                        && e.getToolType(0) == MotionEvent.TOOL_TYPE_FINGER);
         } else {
             return e.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
         }
@@ -217,13 +234,15 @@
             ctrlType |= CTRL_TYPE_BOTTOM;
         }
         // If the touch is within one of the four corners, check if it is within the bounds of the
-        // // handle.
+        // handle.
         if ((ctrlType & (CTRL_TYPE_LEFT | CTRL_TYPE_RIGHT)) != 0
                 && (ctrlType & (CTRL_TYPE_TOP | CTRL_TYPE_BOTTOM)) != 0) {
             return checkDistanceFromCenter(ctrlType, x, y);
         }
-        // Otherwise, we should make sure we don't resize tasks inside task bounds.
-        return (x < 0 || y < 0 || x >= mTaskSize.getWidth() || y >= mTaskSize.getHeight())
+        // Allow a small resize handle inside the task bounds defined by the edge inset.
+        return (x <= mResizeHandleEdgeInset || y <= mResizeHandleEdgeInset
+                || x >= mTaskSize.getWidth() - mResizeHandleEdgeInset
+                || y >= mTaskSize.getHeight() - mResizeHandleEdgeInset)
                 ? ctrlType : CTRL_TYPE_UNDEFINED;
     }
 
@@ -237,7 +256,7 @@
         final Point cornerRadiusCenter = calculateCenterForCornerRadius(ctrlType);
         double distanceFromCenter = Math.hypot(x - cornerRadiusCenter.x, y - cornerRadiusCenter.y);
 
-        if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness
+        if (distanceFromCenter < mTaskCornerRadius + mResizeHandleEdgeOutset
                 && distanceFromCenter >= mTaskCornerRadius) {
             return ctrlType;
         }
@@ -288,7 +307,8 @@
 
         return this.mTaskCornerRadius == other.mTaskCornerRadius
                 && this.mTaskSize.equals(other.mTaskSize)
-                && this.mResizeHandleThickness == other.mResizeHandleThickness
+                && this.mResizeHandleEdgeOutset == other.mResizeHandleEdgeOutset
+                && this.mResizeHandleEdgeInset == other.mResizeHandleEdgeInset
                 && this.mFineTaskCorners.equals(other.mFineTaskCorners)
                 && this.mLargeTaskCorners.equals(other.mLargeTaskCorners)
                 && this.mTaskEdges.equals(other.mTaskEdges);
@@ -299,7 +319,8 @@
         return Objects.hash(
                 mTaskCornerRadius,
                 mTaskSize,
-                mResizeHandleThickness,
+                mResizeHandleEdgeOutset,
+                mResizeHandleEdgeInset,
                 mFineTaskCorners,
                 mLargeTaskCorners,
                 mTaskEdges);
@@ -421,26 +442,27 @@
         private final @NonNull Rect mBottomEdgeBounds;
         private final @NonNull Region mRegion;
 
-        private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness) {
+        private TaskEdges(@NonNull Size taskSize, int resizeHandleThickness,
+                int resizeHandleEdgeInset) {
             // Save touch areas for each edge.
             mTopEdgeBounds = new Rect(
                     -resizeHandleThickness,
                     -resizeHandleThickness,
                     taskSize.getWidth() + resizeHandleThickness,
-                    0);
+                    resizeHandleThickness);
             mLeftEdgeBounds = new Rect(
                     -resizeHandleThickness,
                     0,
-                    0,
+                    resizeHandleEdgeInset,
                     taskSize.getHeight());
             mRightEdgeBounds = new Rect(
-                    taskSize.getWidth(),
+                    taskSize.getWidth() - resizeHandleEdgeInset,
                     0,
                     taskSize.getWidth() + resizeHandleThickness,
                     taskSize.getHeight());
             mBottomEdgeBounds = new Rect(
                     -resizeHandleThickness,
-                    taskSize.getHeight(),
+                    taskSize.getHeight() - resizeHandleEdgeInset,
                     taskSize.getWidth() + resizeHandleThickness,
                     taskSize.getHeight() + resizeHandleThickness);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index c16c16f..748046e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -45,7 +45,7 @@
 import androidx.core.view.isGone
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
-import com.android.wm.shell.common.split.SplitScreenConstants
+import com.android.wm.shell.shared.split.SplitScreenConstants
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
@@ -64,6 +64,7 @@
  */
 class HandleMenu(
     private val parentDecor: DesktopModeWindowDecoration,
+    private val windowManagerWrapper: WindowManagerWrapper,
     private val layoutResId: Int,
     private val appIconBitmap: Bitmap?,
     private val appName: CharSequence?,
@@ -178,7 +179,7 @@
         handleMenuViewContainer =
             if (!taskInfo.isFreeform && Flags.enableAdditionalWindowsAboveStatusBar()) {
                 AdditionalSystemViewContainer(
-                    context = context,
+                    windowManagerWrapper = windowManagerWrapper,
                     taskId = taskInfo.taskId,
                     x = x,
                     y = y,
@@ -635,6 +636,7 @@
 interface HandleMenuFactory {
     fun create(
         parentDecor: DesktopModeWindowDecoration,
+        windowManagerWrapper: WindowManagerWrapper,
         layoutResId: Int,
         appIconBitmap: Bitmap?,
         appName: CharSequence?,
@@ -652,6 +654,7 @@
 object DefaultHandleMenuFactory : HandleMenuFactory {
     override fun create(
         parentDecor: DesktopModeWindowDecoration,
+        windowManagerWrapper: WindowManagerWrapper,
         layoutResId: Int,
         appIconBitmap: Bitmap?,
         appName: CharSequence?,
@@ -665,6 +668,7 @@
     ): HandleMenu {
         return HandleMenu(
             parentDecor,
+            windowManagerWrapper,
             layoutResId,
             appIconBitmap,
             appName,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
index e3d2234..9590ccd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt
@@ -30,7 +30,7 @@
 import androidx.core.animation.doOnEnd
 import androidx.core.view.children
 import com.android.wm.shell.R
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 
 /** Animates the Handle Menu opening. */
 class HandleMenuAnimator(
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 095d337..9c73e4a 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
@@ -59,10 +59,10 @@
 import androidx.core.animation.addListener
 import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE
-import com.android.wm.shell.animation.Interpolators.FAST_OUT_LINEAR_IN
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.SyncTransactionQueue
+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
 import com.android.wm.shell.windowdecor.common.DecorThemeUtil
 import com.android.wm.shell.windowdecor.common.OPACITY_12
@@ -121,8 +121,14 @@
 
     /** Closes the maximize window and releases its view. */
     fun close() {
-        maximizeMenuView?.animateCloseMenu {
-            maximizeMenu?.releaseView()
+        val view = maximizeMenuView
+        val menu = maximizeMenu
+        if (view == null) {
+            menu?.releaseView()
+        } else {
+            view.animateCloseMenu {
+                menu?.releaseView()
+            }
         }
         maximizeMenu = null
         maximizeMenuView = null
@@ -318,7 +324,7 @@
             rootView.setOnTouchListener { _, event ->
                 if (event.actionMasked == ACTION_OUTSIDE) {
                     onOutsideTouchListener?.invoke()
-                    false
+                    return@setOnTouchListener false
                 }
                 true
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
index 4417209..3f828f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/OWNERS
@@ -1 +1,3 @@
 [email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
index ad238c3..61b9393 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
@@ -23,6 +23,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.hardware.input.InputManager;
+import android.os.IBinder;
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.InputDevice;
@@ -84,13 +85,17 @@
         }
     }
 
-    void minimizeTask(WindowContainerToken taskToken) {
-        WindowContainerTransaction wct = new WindowContainerTransaction();
+    IBinder minimizeTask(WindowContainerToken taskToken) {
+        return minimizeTask(taskToken, new WindowContainerTransaction());
+    }
+
+    IBinder minimizeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) {
         wct.reorder(taskToken, false);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            mTransitionStarter.startMinimizedModeTransition(wct);
+            return mTransitionStarter.startMinimizedModeTransition(wct);
         } else {
             mSyncQueue.queue(wct);
+            return null;
         }
     }
 
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 7f2c1a8..4a884eb5 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
@@ -104,9 +104,6 @@
                 wct.reorder(mDesktopWindowDecoration.mTaskInfo.token, true);
                 mTaskOrganizer.applyTransaction(wct);
             }
-        } else {
-            mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
-                    mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW);
         }
         mDragStartListener.onDragStart(mDesktopWindowDecoration.mTaskInfo.taskId);
         mRepositionTaskBounds.set(mTaskBoundsAtDragStart);
@@ -133,6 +130,9 @@
                 mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
             }
         } else if (mCtrlType == CTRL_TYPE_UNDEFINED) {
+            // Begin window drag CUJ instrumentation only when drag position moves.
+            mInteractionJankMonitor.begin(mDesktopWindowDecoration.mTaskSurface,
+                    mDesktopWindowDecoration.mContext, CUJ_DESKTOP_MODE_DRAG_WINDOW);
             final SurfaceControl.Transaction t = mTransactionSupplier.get();
             DragPositioningCallbackUtility.setPositionOnDrag(mDesktopWindowDecoration,
                     mRepositionTaskBounds, mTaskBoundsAtDragStart, mRepositionStartPoint, t, x, y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.kt
new file mode 100644
index 0000000..5c2ff1b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowManagerWrapper.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.wm.shell.windowdecor
+
+import android.view.View
+import android.view.WindowManager
+
+/**
+ * A wrapper for [WindowManager] to make view manipulation operations related to window
+ * decors more testable.
+ */
+class WindowManagerWrapper (
+    private val windowManager: WindowManager
+){
+
+    fun addView(v: View, lp: WindowManager.LayoutParams) {
+        windowManager.addView(v, lp)
+    }
+
+    fun removeViewImmediate(v: View) {
+        windowManager.removeViewImmediate(v)
+    }
+
+    fun updateViewLayout(v: View, lp: WindowManager.LayoutParams) {
+        windowManager.updateViewLayout(v, lp)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index cadd80e..226b0fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -24,13 +24,14 @@
 import android.view.SurfaceControl
 import android.view.View
 import android.view.WindowManager
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
 
 /**
  * An [AdditionalViewContainer] that uses the system [WindowManager] instance. Intended
  * for view containers that should be above the status bar layer.
  */
 class AdditionalSystemViewContainer(
-    context: Context,
+    private val windowManagerWrapper: WindowManagerWrapper,
     taskId: Int,
     x: Int,
     y: Int,
@@ -39,9 +40,20 @@
     flags: Int,
     override val view: View
 ) : AdditionalViewContainer() {
+    val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
+        width, height, x, y,
+        WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+        flags,
+        PixelFormat.TRANSPARENT
+    ).apply {
+        title = "Additional view container of Task=$taskId"
+        gravity = Gravity.LEFT or Gravity.TOP
+        setTrustedOverlay()
+    }
 
     constructor(
         context: Context,
+        windowManagerWrapper: WindowManagerWrapper,
         taskId: Int,
         x: Int,
         y: Int,
@@ -50,7 +62,7 @@
         flags: Int,
         @LayoutRes layoutId: Int
     ) : this(
-        context = context,
+        windowManagerWrapper = windowManagerWrapper,
         taskId = taskId,
         x = x,
         y = y,
@@ -61,9 +73,16 @@
     )
 
     constructor(
-        context: Context, taskId: Int, x: Int, y: Int, width: Int, height: Int, flags: Int
+        context: Context,
+        windowManagerWrapper: WindowManagerWrapper,
+        taskId: Int,
+        x: Int,
+        y: Int,
+        width: Int,
+        height: Int,
+        flags: Int
     ) : this(
-        context = context,
+        windowManagerWrapper = windowManagerWrapper,
         taskId = taskId,
         x = x,
         y = y,
@@ -73,24 +92,12 @@
         view = View(context)
     )
 
-    val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
-
     init {
-        val lp = WindowManager.LayoutParams(
-            width, height, x, y,
-            WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
-            flags,
-            PixelFormat.TRANSPARENT
-        ).apply {
-            title = "Additional view container of Task=$taskId"
-            gravity = Gravity.LEFT or Gravity.TOP
-            setTrustedOverlay()
-        }
-        windowManager?.addView(view, lp)
+        windowManagerWrapper.addView(view, lp)
     }
 
     override fun releaseView() {
-        windowManager?.removeViewImmediate(view)
+        windowManagerWrapper.removeViewImmediate(view)
     }
 
     override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
@@ -98,6 +105,6 @@
             this.x = x.toInt()
             this.y = y.toInt()
         }
-        windowManager?.updateViewLayout(view, lp)
+        windowManagerWrapper.updateViewLayout(view, lp)
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 753723c..dfa5ab4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -31,7 +31,8 @@
 import android.widget.ImageButton
 import com.android.window.flags.Flags
 import com.android.wm.shell.R
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 
 /**
@@ -41,14 +42,14 @@
 internal class AppHandleViewHolder(
     rootView: View,
     onCaptionTouchListener: View.OnTouchListener,
-    onCaptionButtonClickListener: OnClickListener
+    onCaptionButtonClickListener: OnClickListener,
+    private val windowManagerWrapper: WindowManagerWrapper
 ) : WindowDecorationViewHolder(rootView) {
 
     companion object {
         private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
     }
     private lateinit var taskInfo: RunningTaskInfo
-    private val windowManager = context.getSystemService(WindowManager::class.java)
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
     private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
     private val inputManager = context.getSystemService(InputManager::class.java)
@@ -96,11 +97,12 @@
                                           handleWidth: Int,
                                           handleHeight: Int) {
         if (!Flags.enableAdditionalWindowsAboveStatusBar()) return
-        statusBarInputLayer = AdditionalSystemViewContainer(context, taskInfo.taskId,
-            handlePosition.x, handlePosition.y, handleWidth, handleHeight,
+        statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
+            taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
-        val view = statusBarInputLayer?.view
-        val lp = view?.layoutParams as WindowManager.LayoutParams
+        val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
+        val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer" +
+                "LayoutParams")
         lp.title = "Handle Input Layer of task " + taskInfo.taskId
         lp.setTrustedOverlay()
         // Make this window a spy window to enable it to pilfer pointers from the system-wide
@@ -120,7 +122,7 @@
             captionHandle.dispatchTouchEvent(event)
             true
         }
-        windowManager.updateViewLayout(view, lp)
+        windowManagerWrapper.updateViewLayout(view, lp)
     }
 
     private fun updateStatusBarInputLayer(globalPosition: Point) {
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 d0eb6da..033d695 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
@@ -35,6 +35,7 @@
 import androidx.compose.material3.dynamicLightColorScheme
 import androidx.compose.ui.graphics.toArgb
 import androidx.core.content.withStyledAttributes
+import androidx.core.view.isGone
 import androidx.core.view.isVisible
 import com.android.internal.R.attr.materialColorOnSecondaryContainer
 import com.android.internal.R.attr.materialColorOnSurface
@@ -42,6 +43,7 @@
 import com.android.internal.R.attr.materialColorSurfaceContainerHigh
 import com.android.internal.R.attr.materialColorSurfaceContainerLow
 import com.android.internal.R.attr.materialColorSurfaceDim
+import com.android.window.flags.Flags.enableMinimizeButton
 import com.android.wm.shell.R
 import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
 import com.android.wm.shell.windowdecor.MaximizeButtonView
@@ -82,9 +84,9 @@
         .getDimensionPixelSize(R.dimen.desktop_mode_header_buttons_ripple_radius)
 
     /**
-     * The app chip, maximize and close button's height extends to the top & bottom edges of the
-     * header, and their width may be larger than their height. This is by design to increase the
-     * clickable and hover-able bounds of the view as much as possible. However, to prevent the
+     * The app chip, minimize, maximize and close button's height extends to the top & bottom edges
+     * of the header, and their width may be larger than their height. This is by design to increase
+     * the clickable and hover-able bounds of the view as much as possible. However, to prevent the
      * ripple drawable from being as large as the views (and asymmetrical), insets are applied to
      * the background ripple drawable itself to give the appearance of a smaller button
      * (with padding between itself and the header edges / sibling buttons) but without affecting
@@ -94,6 +96,12 @@
         vertical = context.resources
             .getDimensionPixelSize(R.dimen.desktop_mode_header_app_chip_ripple_inset_vertical)
     )
+    private val minimizeDrawableInsets = DrawableInsets(
+        vertical = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_minimize_ripple_inset_vertical),
+        horizontal = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_minimize_ripple_inset_horizontal)
+    )
     private val maximizeDrawableInsets = DrawableInsets(
         vertical = context.resources
             .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_vertical),
@@ -115,6 +123,7 @@
     private val maximizeButtonView: MaximizeButtonView =
             rootView.requireViewById(R.id.maximize_button_view)
     private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window)
+    private val minimizeWindowButton: ImageButton = rootView.requireViewById(R.id.minimize_window)
     private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name)
     private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon)
     val appNameTextWidth: Int
@@ -131,6 +140,8 @@
         maximizeWindowButton.setOnGenericMotionListener(onCaptionGenericMotionListener)
         maximizeWindowButton.onLongClickListener = onLongClickListener
         closeWindowButton.setOnTouchListener(onCaptionTouchListener)
+        minimizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+        minimizeWindowButton.setOnTouchListener(onCaptionTouchListener)
         appNameTextView.text = appName
         appIconImageView.setImageBitmap(appIconBitmap)
         maximizeButtonView.onHoverAnimationFinishedListener =
@@ -157,11 +168,13 @@
         val alpha = Color.alpha(color)
         closeWindowButton.imageTintList = ColorStateList.valueOf(color)
         maximizeWindowButton.imageTintList = ColorStateList.valueOf(color)
+        minimizeWindowButton.imageTintList = ColorStateList.valueOf(color)
         expandMenuButton.imageTintList = ColorStateList.valueOf(color)
         appNameTextView.isVisible = !taskInfo.isTransparentCaptionBarAppearance
         appNameTextView.setTextColor(color)
         appIconImageView.imageAlpha = alpha
         maximizeWindowButton.imageAlpha = alpha
+        minimizeWindowButton.imageAlpha = alpha
         closeWindowButton.imageAlpha = alpha
         expandMenuButton.imageAlpha = alpha
         context.withStyledAttributes(
@@ -176,8 +189,10 @@
             openMenuButton.background = getDrawable(0)
             maximizeWindowButton.background = getDrawable(1)
             closeWindowButton.background = getDrawable(1)
+            minimizeWindowButton.background = getDrawable(1)
         }
         maximizeButtonView.setAnimationTints(isDarkMode())
+        minimizeWindowButton.isGone = !enableMinimizeButton()
     }
 
     private fun bindDataWithThemedHeaders(taskInfo: RunningTaskInfo) {
@@ -212,6 +227,16 @@
             }
             appIconImageView.imageAlpha = foregroundAlpha
         }
+        // Minimize button.
+        minimizeWindowButton.apply {
+            imageTintList = colorStateList
+            background = createRippleDrawable(
+                color = foregroundColor,
+                cornerRadius = headerButtonsRippleRadius,
+                drawableInsets = minimizeDrawableInsets
+            )
+        }
+        minimizeWindowButton.isGone = !enableMinimizeButton()
         // Maximize button.
         maximizeButtonView.setAnimationTints(
             darkMode = header.appTheme == Theme.DARK,
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index d1be12f..65e50f8 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -18,3 +18,5 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS b/libs/WindowManager/Shell/tests/e2e/desktopmode/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/OWNERS
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/OWNERS
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp
new file mode 100644
index 0000000..50581f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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 {
+    // 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: "WMShellFlickerTestsDesktopMode",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellScenariosDesktopMode",
+        "WMShellTestUtils",
+    ],
+    data: ["trace_config/*"],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml
new file mode 100644
index 0000000..1bbbefa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.wm.shell.flicker">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Allow the test to write directly to /sdcard/ -->
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <!-- Write secure settings -->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Enable / Disable tracing !-->
+    <uses-permission android:name="android.permission.DUMP" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <!-- Capture screen recording -->
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+    <!-- Workaround grant runtime permission exception from b/152733071 -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <!-- Force-stop test apps -->
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <!-- Control test app's media session -->
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    <!-- ATM.removeRootTasksWithActivityTypes() -->
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+    <!-- Enable bubble notification-->
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <!-- Allow the test to connect to perfetto trace processor -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+    <application android:requestLegacyExternalStorage="true"
+                 android:networkSecurityConfig="@xml/network_security_config"
+                 android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+
+        <service android:name=".NotificationListener"
+                 android:exported="true"
+                 android:label="WMShellTestsNotificationListenerService"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
+
+        <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            tools:node="remove" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker"
+                     android:label="WindowManager Shell Flicker Tests">
+    </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml
new file mode 100644
index 0000000..40dbbac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/AndroidTestTemplate.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="push-file"
+                key="trace_config.textproto"
+                value="/data/misc/perfetto-traces/trace_config.textproto"
+        />
+        <!--Install the content provider automatically when we push some file in sdcard folder.-->
+        <!--Needed to avoid the installation during the test suite.-->
+        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+        <!-- PerfettoListener related arguments -->
+        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+        <option name="instrumentation-arg"
+                key="perfetto_config_file"
+                value="trace_config.textproto"
+        />
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell.flicker/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/res/xml/network_security_config.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
new file mode 100644
index 0000000..b697d80
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitLandscape.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.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CloseAllAppWithAppHeaderExitLandscape : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_90) {
+    @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
+    @Test
+    override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig()
+                .use(FlickerServiceConfig.DEFAULT)
+                .use(CLOSE_APP)
+                .use(CLOSE_LAST_APP)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
new file mode 100644
index 0000000..a11e876
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/CloseAllAppWithAppHeaderExitPortrait.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.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CloseAllAppWithAppHeaderExitPortrait : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_0) {
+    @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
+    @Test
+    override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig()
+                .use(FlickerServiceConfig.DEFAULT)
+                .use(CLOSE_APP)
+                .use(CLOSE_LAST_APP)
+    }
+}
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
new file mode 100644
index 0000000..ec1d4f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -0,0 +1,337 @@
+/*
+ * 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.flicker
+
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+import android.tools.PlatformConsts.DESKTOP_MODE_MINIMUM_WINDOW_WIDTH
+import android.tools.flicker.AssertionInvocationGroup
+import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize
+import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
+import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
+import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
+import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
+import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxBoundsInOnlyOneDimension
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayHeight
+import android.tools.flicker.assertors.assertions.AppWindowHasMaxDisplayWidth
+import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast
+import android.tools.flicker.assertors.assertions.AppWindowInsideDisplayBoundsAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
+import android.tools.flicker.assertors.assertions.AppWindowMaintainsAspectRatioAlways
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
+import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds
+import android.tools.flicker.assertors.assertions.AppWindowReturnsToStartBoundsAndPosition
+import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTopWindow
+import android.tools.flicker.config.AssertionTemplates
+import android.tools.flicker.config.FlickerConfigEntry
+import android.tools.flicker.config.ScenarioId
+import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP
+import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER
+import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP
+import android.tools.flicker.extractors.ITransitionMatcher
+import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
+import android.tools.flicker.extractors.TaggedCujTransitionMatcher
+import android.tools.flicker.extractors.TaggedScenarioExtractorBuilder
+import android.tools.traces.events.CujType
+import android.tools.traces.wm.Transition
+import android.tools.traces.wm.TransitionType
+
+class DesktopModeFlickerScenarios {
+    companion object {
+        val END_DRAG_TO_DESKTOP =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
+                extractor =
+                ShellTransitionScenarioExtractor(
+                    transitionMatcher =
+                    object : ITransitionMatcher {
+                        override fun findAll(
+                            transitions: Collection<Transition>
+                        ): Collection<Transition> {
+                            return transitions.filter {
+                                // TODO(351168217) Use jank CUJ to extract a longer trace
+                                it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP
+                            }
+                        }
+                    }
+                ),
+                assertions =
+                AssertionTemplates.COMMON_ASSERTIONS +
+                        listOf(
+                            AppLayerIsVisibleAlways(DESKTOP_MODE_APP),
+                            AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
+                            AppWindowHasDesktopModeInitialBoundsAtTheEnd(DESKTOP_MODE_APP),
+                            AppWindowBecomesVisible(DESKTOP_WALLPAPER)
+                        )
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        // Use this scenario for closing an app in desktop windowing, except the last app. For the
+        // last app use CLOSE_LAST_APP scenario
+        val CLOSE_APP =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("CLOSE_APP"),
+                extractor =
+                ShellTransitionScenarioExtractor(
+                    transitionMatcher =
+                    object : ITransitionMatcher {
+                        override fun findAll(
+                            transitions: Collection<Transition>
+                        ): Collection<Transition> {
+                            // In case there are multiple windows closing, filter out the
+                            // last window closing. It should use the CLOSE_LAST_APP
+                            // scenario below.
+                            return transitions
+                                .filter { it.type == TransitionType.CLOSE }
+                                .sortedByDescending { it.id }
+                                .drop(1)
+                        }
+                    }
+                ),
+                assertions =
+                AssertionTemplates.COMMON_ASSERTIONS +
+                        listOf(
+                            AppWindowOnTopAtStart(DESKTOP_MODE_APP),
+                            AppLayerIsVisibleAtStart(DESKTOP_MODE_APP),
+                            AppLayerIsInvisibleAtEnd(DESKTOP_MODE_APP)
+                        ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val CLOSE_LAST_APP =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("CLOSE_LAST_APP"),
+                extractor =
+                ShellTransitionScenarioExtractor(
+                    transitionMatcher =
+                    object : ITransitionMatcher {
+                        override fun findAll(
+                            transitions: Collection<Transition>
+                        ): Collection<Transition> {
+                            val lastTransition =
+                                transitions
+                                    .filter { it.type == TransitionType.CLOSE }
+                                    .maxByOrNull { it.id }!!
+                            return listOf(lastTransition)
+                        }
+                    }
+                ),
+                assertions =
+                AssertionTemplates.COMMON_ASSERTIONS +
+                        listOf(
+                            AppWindowIsInvisibleAtEnd(DESKTOP_MODE_APP),
+                            LauncherWindowReplacesAppAsTopWindow(DESKTOP_MODE_APP),
+                            AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER)
+                        )
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val CORNER_RESIZE =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("CORNER_RESIZE"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
+            )
+
+        val EDGE_RESIZE =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("EDGE_RESIZE"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(
+                            AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+                        ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val CORNER_RESIZE_TO_MINIMUM_SIZE =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions =
+                AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(
+                            AppWindowHasSizeOfAtLeast(
+                                DESKTOP_MODE_APP,
+                                DESKTOP_MODE_MINIMUM_WINDOW_WIDTH,
+                                DESKTOP_MODE_MINIMUM_WINDOW_HEIGHT
+                            )
+                        )
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val SNAP_RESIZE_LEFT_WITH_BUTTON =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val SNAP_RESIZE_RIGHT_WITH_BUTTON =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_BUTTON"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val SNAP_RESIZE_LEFT_WITH_DRAG =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_DRAG"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val SNAP_RESIZE_RIGHT_WITH_DRAG =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("SNAP_RESIZE_RIGHT_WITH_DRAG"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
+                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_SNAP_RESIZE)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = listOf(
+                    AppWindowIsVisibleAlways(NON_RESIZABLE_APP),
+                    AppWindowOnTopAtEnd(NON_RESIZABLE_APP),
+                    AppWindowRemainInsideDisplayBounds(NON_RESIZABLE_APP),
+                    AppWindowMaintainsAspectRatioAlways(NON_RESIZABLE_APP),
+                    AppWindowReturnsToStartBoundsAndPosition(NON_RESIZABLE_APP)
+                ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val MAXIMIZE_APP =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("MAXIMIZE_APP"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(
+                            AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+                            AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
+                            AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+                        ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val MAXIMIZE_APP_NON_RESIZABLE =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("MAXIMIZE_APP_NON_RESIZABLE"),
+                extractor =
+                TaggedScenarioExtractorBuilder()
+                    .setTargetTag(CujType.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
+                    .setTransitionMatcher(
+                        TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+                    )
+                    .build(),
+                assertions =
+                AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+                        listOf(
+                            AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+                            AppWindowMaintainsAspectRatioAlways(DESKTOP_MODE_APP),
+                            AppWindowHasMaxBoundsInOnlyOneDimension(DESKTOP_MODE_APP)
+                        ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+
+        val CASCADE_APP =
+            FlickerConfigEntry(
+                scenarioId = ScenarioId("CASCADE_APP"),
+                extractor =
+                ShellTransitionScenarioExtractor(
+                    transitionMatcher =
+                    object : ITransitionMatcher {
+                        override fun findAll(
+                            transitions: Collection<Transition>
+                        ): Collection<Transition> {
+                                return transitions.filter { it.type == TransitionType.OPEN }
+                        }
+                    }
+                ),
+                assertions =
+                        listOf(
+                            AppWindowInsideDisplayBoundsAtEnd(DESKTOP_MODE_APP),
+                            AppWindowOnTopAtEnd(DESKTOP_MODE_APP),
+                            AppWindowBecomesVisible(DESKTOP_MODE_APP),
+                            AppWindowAlignsWithOnlyOneDisplayCornerAtEnd(DESKTOP_MODE_APP)
+                        ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+            )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt
new file mode 100644
index 0000000..f7b2556
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterDesktopWithDragLandscape : EnterDesktopWithDrag(Rotation.ROTATION_90) {
+    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
+    @Test
+    override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
+
+    companion object {
+
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.kt
new file mode 100644
index 0000000..f4bf0f9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/EnterDesktopWithDragPortrait.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.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterDesktopWithDragPortrait : EnterDesktopWithDrag(Rotation.ROTATION_0) {
+    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
+    @Test
+    override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.kt
new file mode 100644
index 0000000..2179566
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppLandscape.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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppLandscape : MaximizeAppWindow(rotation = ROTATION_90) {
+    @ExpectedScenarios(["MAXIMIZE_APP"])
+    @Test
+    override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt
new file mode 100644
index 0000000..b173a60
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizableLandscape.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize non-resizable app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until
+ * filling the vertical or horizontal stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppNonResizableLandscape : MaximizeAppWindow(
+    rotation = ROTATION_90,
+    isResizable = false
+) {
+    @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"])
+    @Test
+    override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.kt
new file mode 100644
index 0000000..88888ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppNonResizablePortrait.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP_NON_RESIZABLE
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize non-resizable app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, maintaining its aspect ratio, until
+ * filling the vertical or horizontal stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppNonResizablePortrait : MaximizeAppWindow(isResizable = false) {
+    @ExpectedScenarios(["MAXIMIZE_APP_NON_RESIZABLE"])
+    @Test
+    override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP_NON_RESIZABLE)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.kt
new file mode 100644
index 0000000..b79fd203
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MaximizeAppPortrait.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MAXIMIZE_APP
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Maximize app window by pressing the maximize button on the app header.
+ *
+ * Assert that the app window keeps the same increases in size, filling the vertical and horizontal
+ * stable display bounds.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class MaximizeAppPortrait : MaximizeAppWindow() {
+    @ExpectedScenarios(["MAXIMIZE_APP"])
+    @Test
+    override fun maximizeAppWindow() = super.maximizeAppWindow()
+
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(MAXIMIZE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.kt
new file mode 100644
index 0000000..a07fa99
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModeLandscape.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.wm.shell.flicker
+
+import android.tools.Rotation.ROTATION_90
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModeLandscape : OpenAppsInDesktopMode(rotation = ROTATION_90) {
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApps() = super.openApps()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.kt
new file mode 100644
index 0000000..c7a958a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenAppsInDesktopModePortrait.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CASCADE_APP
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class OpenAppsInDesktopModePortrait : OpenAppsInDesktopMode() {
+    @ExpectedScenarios(["CASCADE_APP"])
+    @Test
+    override fun openApps() = super.openApps()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CASCADE_APP)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
new file mode 100644
index 0000000..45e5fdc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the smallest possible height and width in
+ * landscape mode.
+ *
+ * Assert that the minimum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMinimumWindowSizeLandscape : ResizeAppWithCornerResize(
+    rotation = Rotation.ROTATION_90,
+    horizontalChange = -1500,
+    verticalChange = 1500) {
+    @ExpectedScenarios(["CORNER_RESIZE_TO_MINIMUM_SIZE"])
+    @Test
+    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MINIMUM_SIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt
new file mode 100644
index 0000000..62a2571
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMinimumWindowSizePortrait.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the smallest possible height and width in portrait mode.
+ *
+ * Assert that the minimum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMinimumWindowSizePortrait : ResizeAppWithCornerResize(horizontalChange = -1500,
+    verticalChange = 1500) {
+    @ExpectedScenarios(["CORNER_RESIZE_TO_MINIMUM_SIZE"])
+    @Test
+    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MINIMUM_SIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.kt
new file mode 100644
index 0000000..ea8b10b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizeLandscape.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.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithCornerResizeLandscape : ResizeAppWithCornerResize(Rotation.ROTATION_90) {
+    @ExpectedScenarios(["CORNER_RESIZE"])
+    @Test
+    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.kt
new file mode 100644
index 0000000..d7bba6e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithCornerResizePortrait.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.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithCornerResizePortrait : ResizeAppWithCornerResize(Rotation.ROTATION_0) {
+    @ExpectedScenarios(["CORNER_RESIZE"])
+    @Test
+    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
new file mode 100644
index 0000000..c3abf23
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeMouse.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeMouse : ResizeAppWithEdgeResize(InputMethod.MOUSE) {
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
new file mode 100644
index 0000000..86b0e6f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeStylus.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeStylus : ResizeAppWithEdgeResize(InputMethod.STYLUS) {
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
new file mode 100644
index 0000000..e6bb9ef
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppWithEdgeResizeTouchpad.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.EDGE_RESIZE
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppWithEdgeResizeTouchpad : ResizeAppWithEdgeResize(InputMethod.TOUCHPAD) {
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeRight() = super.resizeAppWithEdgeResizeRight()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeLeft() = super.resizeAppWithEdgeResizeLeft()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeTop() = super.resizeAppWithEdgeResizeTop()
+
+    @ExpectedScenarios(["EDGE_RESIZE"])
+    @Test
+    override fun resizeAppWithEdgeResizeBottom() = super.resizeAppWithEdgeResizeBottom()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(EDGE_RESIZE)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.kt
new file mode 100644
index 0000000..b5090086
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithButton.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_BUTTON
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using the Snap Left button from the maximize menu.
+ *
+ * Assert that the app window fills the left half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowLeftWithButton : SnapResizeAppWindowWithButton(toLeft = true) {
+    @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_BUTTON"])
+    @Test
+    override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_BUTTON)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.kt
new file mode 100644
index 0000000..a22e760
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowLeftWithDrag.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_LEFT_WITH_DRAG
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window by dragging it to the left edge of the screen.
+ *
+ * Assert that the app window fills the left half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowLeftWithDrag : SnapResizeAppWindowWithDrag(toLeft = true) {
+    @ExpectedScenarios(["SNAP_RESIZE_LEFT_WITH_DRAG"])
+    @Test
+    override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_LEFT_WITH_DRAG)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.kt
new file mode 100644
index 0000000..375a2b8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithButton.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_BUTTON
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window using the Snap Right button from the maximize menu.
+ *
+ * Assert that the app window fills the right half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowRightWithButton : SnapResizeAppWindowWithButton(toLeft = false) {
+    @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_BUTTON"])
+    @Test
+    override fun snapResizeAppWindowWithButton() = super.snapResizeAppWindowWithButton()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_BUTTON)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.kt
new file mode 100644
index 0000000..4a9daf7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeAppWindowRightWithDrag.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_RIGHT_WITH_DRAG
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize app window by dragging it to the right edge of the screen.
+ *
+ * Assert that the app window fills the right half the display after being snap resized.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeAppWindowRightWithDrag : SnapResizeAppWindowWithDrag(toLeft = false) {
+    @ExpectedScenarios(["SNAP_RESIZE_RIGHT_WITH_DRAG"])
+    @Test
+    override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(SNAP_RESIZE_RIGHT_WITH_DRAG)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.kt
new file mode 100644
index 0000000..582658f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowLeftWithDrag.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize non-resizable app window by dragging it to the left edge of the screen.
+ *
+ * Assert that the app window keeps the same size and returns to its original pre-drag position.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeNonResizableAppWindowLeftWithDrag :
+    SnapResizeAppWindowWithDrag(toLeft = true, isResizable = false) {
+    @ExpectedScenarios(["SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"])
+    @Test
+    override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+                .use(SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.kt
new file mode 100644
index 0000000..7205ec4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/SnapResizeNonResizableAppWindowRightWithDrag.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.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Snap resize non-resizable app window by dragging it to the right edge of the screen.
+ *
+ * Assert that the app window keeps the same size and returns to its original pre-drag position.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SnapResizeNonResizableAppWindowRightWithDrag :
+    SnapResizeAppWindowWithDrag(toLeft = false, isResizable = false) {
+    @ExpectedScenarios(["SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE"])
+    @Test
+    override fun snapResizeAppWindowWithDrag() = super.snapResizeAppWindowWithDrag()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+                .use(SNAP_RESIZE_WITH_DRAG_NON_RESIZABLE)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/trace_config/trace_config.textproto
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/service/trace_config/trace_config.textproto
rename to libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/trace_config/trace_config.textproto
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp
new file mode 100644
index 0000000..4389f09
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/Android.bp
@@ -0,0 +1,46 @@
+//
+// 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 {
+    // 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"],
+}
+
+java_library {
+    name: "WMShellScenariosDesktopMode",
+    platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellTestUtils",
+        "wm-shell-flicker-utils",
+        "androidx.test.ext.junit",
+        "flickertestapplib",
+        "flickerlib-helpers",
+        "flickerlib-trace_processor_shell",
+        "platform-test-annotations",
+        "wm-flicker-common-app-helpers",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl",
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
new file mode 100644
index 0000000..e9056f3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.MailAppHelper
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class CloseAllAppsWithAppHeaderExit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val nonResizeableApp = DesktopModeAppHelper(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)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        nonResizeableApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun closeAllAppsInDesktop() {
+        nonResizeableApp.closeDesktopApp(wmHelper, device)
+        mailApp.closeDesktopApp(wmHelper, device)
+        testApp.closeDesktopApp(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
new file mode 100644
index 0000000..5a69b27
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DesktopScenarioCustomAppTestBase.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.traces.parsers.WindowManagerStateHelper
+import android.tools.traces.parsers.toFlickerComponent
+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.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Ignore
+
+/** Base test class for desktop CUJ with customizable test app. */
+@Ignore("Base Test Class")
+abstract class DesktopScenarioCustomAppTestBase(
+    isResizeable: Boolean = true,
+    isLandscapeApp: Boolean = true
+) {
+    val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    val tapl = LauncherInstrumentation()
+    val wmHelper = WindowManagerStateHelper(instrumentation)
+    val device = UiDevice.getInstance(instrumentation)
+    // TODO(b/363181411): Consolidate in LetterboxAppHelper.
+    val testApp = when {
+        isResizeable && isLandscapeApp -> SimpleAppHelper(instrumentation)
+        isResizeable && !isLandscapeApp -> SimpleAppHelper(
+                instrumentation,
+                launcherName = ActivityOptions.PortraitOnlyActivity.LABEL,
+                component = ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent()
+            )
+        // NonResizeablAppHelper has no fixed orientation.
+        !isResizeable && isLandscapeApp -> NonResizeableAppHelper(instrumentation)
+        // Opens NonResizeablePortraitActivity.
+        else -> LetterboxAppHelper(instrumentation)
+    }.let { DesktopModeAppHelper(it) }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
new file mode 100644
index 0000000..ca1dc1a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
+{
+    private val imeAppHelper = ImeAppHelper(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    override fun dragAppWindow() {
+        val (startXIme, startYIme) = getWindowDragStartCoordinate(imeAppHelper)
+
+        imeApp.dragWindow(startXIme, startYIme,
+            endX = startXIme + 150, endY = startYIme + 150,
+            wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.kt
new file mode 100644
index 0000000..6d52a11
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindowAndPip.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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class DragAppWindowMultiWindowAndPip : DragAppWindowScenarioTestBase()
+{
+    private val imeAppHelper = ImeAppHelper(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val pipApp = PipAppHelper(instrumentation)
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        // Set string extra to ensure the app is on PiP mode at launch
+        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true"))
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    override fun dragAppWindow() {
+        val (startXIme, startYIme) = getWindowDragStartCoordinate(imeAppHelper)
+
+        imeApp.dragWindow(startXIme, startYIme,
+            endX = startXIme + 150, endY = startYIme + 150,
+            wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        pipApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt
new file mode 100644
index 0000000..7219287
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowScenarioTestBase.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.device.apphelpers.StandardAppHelper
+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.wm.shell.Utils
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+/** Base test class for window drag CUJ. */
+@Ignore("Base Test Class")
+abstract class DragAppWindowScenarioTestBase {
+
+    val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    val tapl = LauncherInstrumentation()
+    val wmHelper = WindowManagerStateHelper(instrumentation)
+    val device = UiDevice.getInstance(instrumentation)
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+    @Test abstract fun dragAppWindow()
+
+    /** Return the top-center coordinate of the app header as the start coordinate. */
+    fun getWindowDragStartCoordinate(appHelper: StandardAppHelper): Pair<Int, Int> {
+        val windowRect = wmHelper.getWindowRegion(appHelper).bounds
+        // Set start x-coordinate as center of app header.
+        val startX = windowRect.centerX()
+        val startY = windowRect.top
+        return Pair(startX, startY)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.kt
new file mode 100644
index 0000000..91cfd17
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowSingleWindow.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.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class DragAppWindowSingleWindow : DragAppWindowScenarioTestBase()
+{
+    private val simpleAppHelper = SimpleAppHelper(instrumentation)
+    private val testApp = DesktopModeAppHelper(simpleAppHelper)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    override fun dragAppWindow() {
+        val (startXTest, startYTest) = getWindowDragStartCoordinate(simpleAppHelper)
+        testApp.dragWindow(startXTest, startYTest,
+            endX = startXTest + 150, endY = startYTest + 150,
+            wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.kt
new file mode 100644
index 0000000..1073050
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithAppHandleMenu.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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+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.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithAppHandleMenu {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val simpleAppHelper = SimpleAppHelper(instrumentation)
+    private val testApp = DesktopModeAppHelper(simpleAppHelper)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+    }
+
+    @Test
+    open fun enterDesktopWithAppHandleMenu() {
+        simpleAppHelper.launchViaIntent(wmHelper)
+        testApp.enterDesktopModeFromAppHandleMenu(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
new file mode 100644
index 0000000..f4d6414
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class EnterDesktopWithDrag
+@JvmOverloads
+constructor(
+    val rotation: Rotation = Rotation.ROTATION_0,
+    isResizeable: Boolean = true,
+    isLandscapeApp: Boolean = true
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
+
+    @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)
+        tapl.enableTransientTaskbar(false)
+    }
+
+    @Test
+    open fun enterDesktopWithDrag() {
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
new file mode 100644
index 0000000..b616e53
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.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.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ExitDesktopWithDragToTopDragZone
+@JvmOverloads
+constructor(
+    val rotation: Rotation = Rotation.ROTATION_0,
+    isResizeable: Boolean = true,
+    isLandscapeApp: Boolean = true
+) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
+
+    @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)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun exitDesktopWithDragToTopDragZone() {
+        testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
new file mode 100644
index 0000000..426f40b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+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.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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class MaximizeAppWindow
+@JvmOverloads
+constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = if (isResizable) {
+        DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    } else {
+        DesktopModeAppHelper(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)
+    }
+
+    @Test
+    open fun maximizeAppWindow() {
+        testApp.maximiseDesktopApp(wmHelper, device)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
new file mode 100644
index 0000000..b86765e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt
@@ -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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+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.ImeAppHelper
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.window.flags.Flags
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+/**
+ * Base scenario test for minimizing the least recently used window when a new window is opened
+ * above the window limit. For tangor devices, which this test currently runs on, the window limit
+ * is 4.
+ */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class MinimizeWindowOnAppOpen()
+{
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
+    private val letterboxAppHelper = DesktopModeAppHelper(LetterboxAppHelper(instrumentation))
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun openAppToMinimizeWindow() {
+        // Launch a new app while 4 apps are already open on desktop. This should result in the
+        // first app we opened to be minimized.
+        letterboxAppHelper.launchViaIntent(wmHelper)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+        letterboxAppHelper.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
new file mode 100644
index 0000000..a5c794b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Postsubmit
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.NavBar
+import android.tools.Rotation
+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.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val firstApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val secondApp = MailAppHelper(instrumentation)
+    private val thirdApp = NewTasksAppHelper(instrumentation)
+    private val fourthApp = ImeAppHelper(instrumentation)
+    private val fifthApp = NonResizeableAppHelper(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_3BUTTON, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+        tapl.enableTransientTaskbar(false)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+        firstApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun openApps() {
+        secondApp.launchViaIntent(wmHelper)
+        thirdApp.launchViaIntent(wmHelper)
+        fourthApp.launchViaIntent(wmHelper)
+        fifthApp.launchViaIntent(wmHelper)
+    }
+
+    @After
+    fun teardown() {
+        fifthApp.exit(wmHelper)
+        fourthApp.exit(wmHelper)
+        thirdApp.exit(wmHelper)
+        secondApp.exit(wmHelper)
+        firstApp.exit(wmHelper)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
new file mode 100644
index 0000000..b6bca7a9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppCornerMultiWindow
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0,
+    val horizontalChange: Int = 50,
+    val verticalChange: Int = -50) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(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)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun resizeAppWithCornerResize() {
+        imeApp.cornerResize(wmHelper,
+            device,
+            DesktopModeAppHelper.Corners.RIGHT_TOP,
+            horizontalChange,
+            verticalChange)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
new file mode 100644
index 0000000..285ea13
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.ImeAppHelper
+import com.android.server.wm.flicker.helpers.MailAppHelper
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppCornerMultiWindowAndPip
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0,
+    val horizontalChange: Int = 50,
+    val verticalChange: Int = -50) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val pipApp = PipAppHelper(instrumentation)
+    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
+    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
+    private val imeApp = DesktopModeAppHelper(ImeAppHelper(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)
+        // Set string extra to ensure the app is on PiP mode at launch
+        pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = mapOf("enter_pip" to "true"))
+        testApp.enterDesktopWithDrag(wmHelper, device)
+        mailApp.launchViaIntent(wmHelper)
+        newTasksApp.launchViaIntent(wmHelper)
+        imeApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun resizeAppWithCornerResize() {
+        imeApp.cornerResize(wmHelper,
+            device,
+            DesktopModeAppHelper.Corners.RIGHT_TOP,
+            horizontalChange,
+            verticalChange)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+        pipApp.exit(wmHelper)
+        mailApp.exit(wmHelper)
+        newTasksApp.exit(wmHelper)
+        imeApp.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
new file mode 100644
index 0000000..42940a9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppWithCornerResize
+@JvmOverloads
+constructor(
+    val rotation: Rotation = Rotation.ROTATION_0,
+    val horizontalChange: Int = 200,
+    val verticalChange: Int = -200,
+    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 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)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun resizeAppWithCornerResize() {
+        testApp.cornerResize(
+            wmHelper,
+            device,
+            DesktopModeAppHelper.Corners.RIGHT_TOP,
+            horizontalChange,
+            verticalChange
+        )
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+
+    companion object {
+        enum class AppProperty {
+            STANDARD,
+            NON_RESIZABLE
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
new file mode 100644
index 0000000..d094967
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.MotionEventHelper
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class ResizeAppWithEdgeResize
+@JvmOverloads
+constructor(
+    val inputMethod: MotionEventHelper.InputMethod,
+    val rotation: Rotation = Rotation.ROTATION_90
+) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    private val motionEventHelper = MotionEventHelper(instrumentation, inputMethod)
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(
+            Flags.enableDesktopWindowingMode()
+                    && Flags.enableWindowingEdgeDragResize() && tapl.isTablet
+        )
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun resizeAppWithEdgeResizeRight() {
+        testApp.edgeResize(
+            wmHelper,
+            motionEventHelper,
+            DesktopModeAppHelper.Edges.RIGHT
+        )
+    }
+
+    @Test
+    open fun resizeAppWithEdgeResizeLeft() {
+        testApp.edgeResize(
+            wmHelper,
+            motionEventHelper,
+            DesktopModeAppHelper.Edges.LEFT
+        )
+    }
+
+    @Test
+    open fun resizeAppWithEdgeResizeTop() {
+        testApp.edgeResize(
+            wmHelper,
+            motionEventHelper,
+            DesktopModeAppHelper.Edges.TOP
+        )
+    }
+
+    @Test
+    open fun resizeAppWithEdgeResizeBottom() {
+        testApp.edgeResize(
+            wmHelper,
+            motionEventHelper,
+            DesktopModeAppHelper.Edges.BOTTOM
+        )
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
new file mode 100644
index 0000000..33242db
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+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.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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class SnapResizeAppWindowWithButton
+@JvmOverloads
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = if (isResizable) {
+        DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    } else {
+        DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+    }
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun snapResizeAppWindowWithButton() {
+        testApp.snapResizeDesktopApp(wmHelper, device, instrumentation.context, toLeft)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
new file mode 100644
index 0000000..14eb779
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.platform.test.annotations.Postsubmit
+import android.tools.NavBar
+import android.tools.Rotation
+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.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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class SnapResizeAppWindowWithDrag
+@JvmOverloads
+constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = if (isResizable) {
+        DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+    } else {
+        DesktopModeAppHelper(NonResizeableAppHelper(instrumentation))
+    }
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun snapResizeAppWindowWithDrag() {
+        testApp.dragToSnapResizeRegion(wmHelper, device, toLeft)
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
new file mode 100644
index 0000000..53e36e23
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.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.wm.shell.scenarios
+
+import android.platform.test.annotations.Postsubmit
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/**
+* Base test for opening recent apps overview from desktop mode.
+*
+* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
+*/
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+open class SwitchToOverviewFromDesktop
+@JvmOverloads
+constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(navigationMode, Rotation.ROTATION_0)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        testApp.enterDesktopWithDrag(wmHelper, device)
+    }
+
+    @Test
+    open fun switchToOverview() {
+        tapl.getLaunchedAppState().switchToOverview()
+    }
+
+    @After
+    fun teardown() {
+        testApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/e2e/splitscreen/OWNERS
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/OWNERS
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
new file mode 100644
index 0000000..a231e38
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/Android.bp
@@ -0,0 +1,329 @@
+//
+// 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 {
+    default_team: "trendy_team_multitasking_windowing",
+    // 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"],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenBase-src",
+    srcs: [
+        "src/**/benchmark/*.kt",
+    ],
+}
+
+java_library {
+    name: "WMShellFlickerTestsSplitScreenBase",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenBase-src",
+    ],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "wm-shell-flicker-utils",
+        "androidx.test.ext.junit",
+        "flickertestapplib",
+        "flickerlib",
+        "flickerlib-helpers",
+        "flickerlib-trace_processor_shell",
+        "platform-test-annotations",
+        "wm-flicker-common-app-helpers",
+        "wm-flicker-common-assertions",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreen",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*.kt"],
+    exclude_srcs: ["src/**/benchmark/*.kt"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellFlickerTestsSplitScreenBase",
+    ],
+    data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merges
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroup1-src",
+    srcs: [
+        "src/**/A*.kt",
+        "src/**/B*.kt",
+        "src/**/C*.kt",
+        "src/**/D*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroup2-src",
+    srcs: [
+        "src/**/E*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroup3-src",
+    srcs: [
+        "src/**/S*.kt",
+    ],
+}
+
+filegroup {
+    name: "WMShellFlickerTestsSplitScreenGroupOther-src",
+    srcs: [
+        "src/**/*.kt",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroup1",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenGroup1-src",
+    ],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellFlickerTestsSplitScreenBase",
+    ],
+    data: ["trace_config/*"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroup2",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenGroup2-src",
+    ],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellFlickerTestsSplitScreenBase",
+    ],
+    data: ["trace_config/*"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroup3",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenGroup3-src",
+    ],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellFlickerTestsSplitScreenBase",
+    ],
+    data: ["trace_config/*"],
+}
+
+android_test {
+    name: "WMShellFlickerTestsSplitScreenGroupOther",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.splitscreen",
+    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: [
+        ":WMShellFlickerTestsSplitScreenGroupOther-src",
+    ],
+    exclude_srcs: [
+        ":WMShellFlickerTestsSplitScreenGroup1-src",
+        ":WMShellFlickerTestsSplitScreenGroup2-src",
+        ":WMShellFlickerTestsSplitScreenGroup3-src",
+    ],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellFlickerTestsSplitScreenBase",
+    ],
+    data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merges
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsRotation module
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-CatchAll",
+    base: "WMShellFlickerTestsSplitScreen",
+    exclude_filters: [
+        "com.android.wm.shell.flicker.splitscreen.CopyContentInSplit",
+        "com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByDivider",
+        "com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByGoHome",
+        "com.android.wm.shell.flicker.splitscreen.DragDividerToResize",
+        "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromAllApps",
+        "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromNotification",
+        "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromShortcut",
+        "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromTaskbar",
+        "com.android.wm.shell.flicker.splitscreen.EnterSplitScreenFromOverview",
+        "com.android.wm.shell.flicker.splitscreen.MultipleShowImeRequestsInSplitScreen",
+        "com.android.wm.shell.flicker.splitscreen.SwitchAppByDoubleTapDivider",
+        "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromAnotherApp",
+        "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromHome",
+        "com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromRecent",
+        "com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairs",
+        "com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairsNoPip",
+        "com.android.wm.shell.flicker.splitscreen.",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-CopyContentInSplit",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.CopyContentInSplit"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-DismissSplitScreenByDivider",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByDivider"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-DismissSplitScreenByGoHome",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.DismissSplitScreenByGoHome"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-DragDividerToResize",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.DragDividerToResize"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromAllApps",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromAllApps"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromNotification",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromNotification"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromShortcut",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromShortcut"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenByDragFromTaskbar",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenByDragFromTaskbar"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-EnterSplitScreenFromOverview",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.EnterSplitScreenFromOverview"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-MultipleShowImeRequestsInSplitScreen",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.MultipleShowImeRequestsInSplitScreen"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchAppByDoubleTapDivider",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchAppByDoubleTapDivider"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromAnotherApp",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromAnotherApp"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromHome",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromHome"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchBackToSplitFromRecent",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBackToSplitFromRecent"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchBetweenSplitPairs",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairs"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-SwitchBetweenSplitPairsNoPip",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.SwitchBetweenSplitPairsNoPip"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsSplitScreen-UnlockKeyguardToSplitScreen",
+    base: "WMShellFlickerTestsSplitScreen",
+    include_filters: ["com.android.wm.shell.flicker.splitscreen.UnlockKeyguardToSplitScreen"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidManifest.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidManifest.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/OWNERS
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/splitscreen/OWNERS
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/OWNERS
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
new file mode 100644
index 0000000..a9dba4a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.ScenarioBuilder
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.SERVICE_TRACE_CONFIG
+import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark
+import com.android.wm.shell.flicker.utils.ICommonAssertions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switch between two split pairs.
+ *
+ * To run this test: `atest WMShellFlickerTestsSplitScreenGroupOther:MultipleShowImeRequestsInSplitScreen`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
[email protected](FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) :
+        MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions {
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            defaultSetup(this)
+            defaultTeardown(this)
+            thisTransition(this)
+        }
+
+    @Presubmit
+    @Test
+    fun imeLayerAlwaysVisible() =
+            flicker.assertLayers {
+                this.isVisible(ComponentNameMatcher.IME)
+            }
+
+    companion object {
+        private fun createFlickerTest(
+            navBarMode: NavBar
+        ) = LegacyFlickerTest(ScenarioBuilder()
+            .withStartRotation(Rotation.ROTATION_0)
+            .withEndRotation(Rotation.ROTATION_0)
+            .withNavBarMode(navBarMode), resultReaderProvider = { scenario ->
+            android.tools.flicker.datastore.CachedResultReader(
+                scenario, SERVICE_TRACE_CONFIG
+            )
+        })
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() = listOf(
+            createFlickerTest(NavBar.MODE_GESTURAL),
+            createFlickerTest(NavBar.MODE_3BUTTON)
+        )
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairsNoPip.kt
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
new file mode 100644
index 0000000..3018b56
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Presubmit
+import android.tools.NavBar
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.flicker.subject.layers.LayersTraceSubject
+import android.tools.flicker.subject.region.RegionSubject
+import android.tools.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark
+import com.android.wm.shell.flicker.utils.ICommonAssertions
+import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test unlocking insecure keyguard to back to split screen tasks and verify the transition
+ * behavior.
+ *
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
+ */
+@RequiresDevice
+@FlakyTest(bugId = 293578017)
+@RunWith(Parameterized::class)
[email protected](FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
+    UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            defaultTeardown(this)
+            thisTransition(this)
+        }
+
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    // TODO(b/293578017) remove once that bug is resolve
+    @Test
+    @Presubmit
+    fun visibleLayersShownMoreThanOneConsecutiveEntry_withoutWallpaper() =
+        flicker.assertLayers {
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(
+                LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+                    listOf(WALLPAPER_BBQ_WRAPPER)
+            )
+        }
+
+    @Test
+    fun splitScreenDividerIsVisibleAtEnd() {
+        flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
+    }
+
+    @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
+
+    @Test
+    fun primaryAppBoundsIsVisibleAtEnd() =
+        flicker.splitAppLayerBoundsIsVisibleAtEnd(
+            primaryApp,
+            landscapePosLeft = false,
+            portraitPosTop = false
+        )
+
+    @Test
+    fun secondaryAppBoundsIsVisibleAtEnd() =
+        flicker.splitAppLayerBoundsIsVisibleAtEnd(
+            secondaryApp,
+            landscapePosLeft = true,
+            portraitPosTop = true
+        )
+
+    @Test fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+
+    @Test fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+
+    @Test
+    fun notOverlapsForPrimaryAndSecondaryAppLayers() {
+        flicker.assertLayers {
+            this.invoke("notOverlapsForPrimaryAndSecondaryLayers") {
+                val primaryAppRegions =
+                    it.subjects
+                        .filter { subject ->
+                            subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
+                        }
+                        .mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
+
+                val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
+                it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
+            }
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() =
+            LegacyFlickerTestFactory.nonRotationTests(
+                supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+            )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/trace_config/trace_config.textproto
similarity index 100%
rename from libs/WindowManager/Shell/tests/flicker/splitscreen/trace_config/trace_config.textproto
rename to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/trace_config/trace_config.textproto
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp
new file mode 100644
index 0000000..dd0018a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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 {
+    // 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: "WMShellFlickerServiceTests",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellScenariosSplitScreen",
+        "WMShellTestUtils",
+    ],
+    data: [
+        ":FlickerTestApp",
+        "trace_config/*",
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml
new file mode 100644
index 0000000..662e7f3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.wm.shell">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Allow the test to write directly to /sdcard/ -->
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <!-- Write secure settings -->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Enable / Disable tracing !-->
+    <uses-permission android:name="android.permission.DUMP" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <!-- Capture screen recording -->
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+    <!-- Workaround grant runtime permission exception from b/152733071 -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <!-- Force-stop test apps -->
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <!-- Control test app's media session -->
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    <!-- ATM.removeRootTasksWithActivityTypes() -->
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+    <!-- Enable bubble notification-->
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <!-- Allow the test to connect to perfetto trace processor -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+    <application android:requestLegacyExternalStorage="true"
+                 android:networkSecurityConfig="@xml/network_security_config"
+                 android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+
+        <service android:name=".NotificationListener"
+                 android:exported="true"
+                 android:label="WMShellTestsNotificationListenerService"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
+
+        <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            tools:node="remove" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell"
+                     android:label="WindowManager Flicker Service Tests">
+    </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
new file mode 100644
index 0000000..6c903a2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="WMShell Platinum Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+        <!-- DefaultUITraceListener args -->
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt
new file mode 100644
index 0000000..3cb9cf2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavLandscape.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+    @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt
new file mode 100644
index 0000000..b27a8ae
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/CopyContentInSplitGesturalNavPortrait.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+    @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..9388114
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavLandscape :
+    DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+
+    // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
+    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @Test
+    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..30ef492
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavPortrait :
+    DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+
+    // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
+    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @Test
+    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..059f967
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
+    DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
+    @Test
+    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..0c6d546
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
+    DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
+    @Test
+    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt
new file mode 100644
index 0000000..14fb72b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+    @Test
+    override fun dragDividerToResize() = super.dragDividerToResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt
new file mode 100644
index 0000000..9be61a5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/DragDividerToResizeGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+    @Test
+    override fun dragDividerToResize() = super.dragDividerToResize()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
new file mode 100644
index 0000000..c12d199
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
+    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
+    @Test
+    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
new file mode 100644
index 0000000..11cd38a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
+    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
+    @Test
+    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
new file mode 100644
index 0000000..66d4bfa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
+    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromNotification() =
+        super.enterSplitScreenByDragFromNotification()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
new file mode 100644
index 0000000..f3a11eb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
+    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromNotification() =
+        super.enterSplitScreenByDragFromNotification()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
new file mode 100644
index 0000000..327ecc3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
+    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
new file mode 100644
index 0000000..dd5a395
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
+    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
new file mode 100644
index 0000000..8e7cf30
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
+    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
new file mode 100644
index 0000000..0324dac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
+    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
new file mode 100644
index 0000000..2fa141e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
+    EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
new file mode 100644
index 0000000..0176913
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
+    EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..1db28dc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
+    SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios([])
+    @Test
+    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..c69167b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
+    SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios([])
+    @Test
+    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
new file mode 100644
index 0000000..602283a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
+    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
new file mode 100644
index 0000000..7cc14e0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
+    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..daf6547
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
+    SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..b0f5e65
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
+    SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
new file mode 100644
index 0000000..88fa783
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
+    SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
new file mode 100644
index 0000000..aa36f44
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
+    SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+    @Test
+    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
new file mode 100644
index 0000000..292f413
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
new file mode 100644
index 0000000..865958f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+
+    @ExpectedScenarios(["QUICKSWITCH"])
+    @Test
+    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
new file mode 100644
index 0000000..6c36e84
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+
+    @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
+    @Test
+    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
new file mode 100644
index 0000000..61ccd36
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/src/com/android/server/wm/shell/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+
+    @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
+    @Test
+    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+    companion object {
+        @JvmStatic
+        @FlickerConfigProvider
+        fun flickerConfigProvider(): FlickerConfig =
+            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp
new file mode 100644
index 0000000..90210b1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/Android.bp
@@ -0,0 +1,82 @@
+//
+// 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 {
+    // 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"],
+}
+
+filegroup {
+    name: "WMShellFlickerServicePlatinumTests-src",
+    srcs: [
+        "src/**/*.kt",
+    ],
+}
+
+java_library {
+    name: "wm-shell-flicker-platinum-tests",
+    platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
+    srcs: [
+        ":WMShellFlickerServicePlatinumTests-src",
+    ],
+    static_libs: [
+        "wm-shell-flicker-utils",
+        "WMShellScenariosSplitScreen",
+    ],
+}
+
+android_test {
+    name: "WMShellFlickerServicePlatinumTests",
+    platform_apis: true,
+    certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
+    manifest: "AndroidManifest.xml",
+    test_config_template: "AndroidTestTemplate.xml",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    srcs: [":WMShellFlickerServicePlatinumTests-src"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellScenariosSplitScreen",
+        "WMShellTestUtils",
+        "ui-trace-collector",
+        "collector-device-lib",
+        "wm-shell-flicker-utils",
+        "androidx.test.ext.junit",
+        "flickertestapplib",
+        "flickerlib-helpers",
+        "flickerlib-trace_processor_shell",
+        "platform-test-annotations",
+        "wm-flicker-common-app-helpers",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl",
+    ],
+    data: [
+        ":FlickerTestApp",
+        "trace_config/*",
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml
new file mode 100644
index 0000000..662e7f3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidManifest.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.wm.shell">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Allow the test to write directly to /sdcard/ -->
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <!-- Write secure settings -->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Enable / Disable tracing !-->
+    <uses-permission android:name="android.permission.DUMP" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <!-- Capture screen recording -->
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+    <!-- Workaround grant runtime permission exception from b/152733071 -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <!-- Force-stop test apps -->
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <!-- Control test app's media session -->
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    <!-- ATM.removeRootTasksWithActivityTypes() -->
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+    <!-- Enable bubble notification-->
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <!-- Allow the test to connect to perfetto trace processor -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+
+    <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+    <application android:requestLegacyExternalStorage="true"
+                 android:networkSecurityConfig="@xml/network_security_config"
+                 android:largeHeap="true">
+        <uses-library android:name="android.test.runner"/>
+
+        <service android:name=".NotificationListener"
+                 android:exported="true"
+                 android:label="WMShellTestsNotificationListenerService"
+                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
+
+        <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+        <provider
+            android:name="androidx.startup.InitializationProvider"
+            android:authorities="${applicationId}.androidx-startup"
+            tools:node="remove" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell"
+                     android:label="WindowManager Flicker Service Tests">
+    </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
new file mode 100644
index 0000000..6c903a2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="WMShell Platinum Tests {MODULE}">
+    <option name="test-tag" value="FlickerTests"/>
+    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+    <option name="isolated-storage" value="false"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- disable DeprecatedTargetSdk warning -->
+        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on"/>
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true"/>
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all"/>
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame"/>
+        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+        <!-- ensure lock screen mode is swipe -->
+        <option name="run-command" value="locksettings set-disabled false"/>
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command"
+                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+        <option name="run-command" value="cmd window tracing size 20480"/>
+        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="test-user-token" value="%TEST_USER%"/>
+        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+        <option name="run-command" value="settings put system show_touches 1"/>
+        <option name="run-command" value="settings put system pointer_location 1"/>
+        <option name="teardown-command"
+                value="settings delete secure show_ime_with_hard_keyboard"/>
+        <option name="teardown-command" value="settings delete system show_touches"/>
+        <option name="teardown-command" value="settings delete system pointer_location"/>
+        <option name="teardown-command"
+                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="{MODULE}.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk"/>
+    </target_preparer>
+    <!-- Needed for pushing the trace config file -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="{PACKAGE}"/>
+        <option name="shell-timeout" value="6600s"/>
+        <option name="test-timeout" value="6000s"/>
+        <option name="hidden-api-checks" value="false"/>
+        <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/>
+        <!-- DefaultUITraceListener args -->
+        <option name="instrumentation-arg" key="per_run" value="true"/>
+        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
+    </test>
+    <!-- Needed for pulling the collected trace config on to the host -->
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+                value="/data/user/0/com.android.wm.shell/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/res/xml/network_security_config.xml
similarity index 100%
copy from libs/WindowManager/Shell/tests/flicker/service/res/xml/network_security_config.xml
copy to libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/res/xml/network_security_config.xml
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt
new file mode 100644
index 0000000..4c2ca67
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavLandscape.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.CopyContentInSplit
+import org.junit.Test
+
+open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun copyContentInSplit() = super.copyContentInSplit()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt
new file mode 100644
index 0000000..0cca310
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/CopyContentInSplitGesturalNavPortrait.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.CopyContentInSplit
+import org.junit.Test
+
+open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun copyContentInSplit() = super.copyContentInSplit()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..7aa62cf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+
+open class DismissSplitScreenByDividerGesturalNavLandscape :
+    DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..de11fc6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+
+open class DismissSplitScreenByDividerGesturalNavPortrait :
+    DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..daa6aac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+
+open class DismissSplitScreenByGoHomeGesturalNavLandscape :
+    DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..ff57d00
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+
+open class DismissSplitScreenByGoHomeGesturalNavPortrait :
+    DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt
new file mode 100644
index 0000000..0ac19c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavLandscape.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DragDividerToResize
+import org.junit.Test
+
+open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dragDividerToResize() = super.dragDividerToResize()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt
new file mode 100644
index 0000000..5713602e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/DragDividerToResizeGesturalNavPortrait.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.DragDividerToResize
+import org.junit.Test
+
+open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun dragDividerToResize() = super.dragDividerToResize()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
new file mode 100644
index 0000000..d7333f1a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
+    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
new file mode 100644
index 0000000..e29a140
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
+    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
new file mode 100644
index 0000000..9ccccb1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
+    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromNotification() =
+        super.enterSplitScreenByDragFromNotification()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
new file mode 100644
index 0000000..87a4d08
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
+    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromNotification() =
+        super.enterSplitScreenByDragFromNotification()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
new file mode 100644
index 0000000..559652c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
+    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
new file mode 100644
index 0000000..bcb8e0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
+    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
new file mode 100644
index 0000000..39e0fed
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
+    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
new file mode 100644
index 0000000..6431629
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+
+open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
+    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
new file mode 100644
index 0000000..2093424
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+
+open class EnterSplitScreenFromOverviewGesturalNavLandscape :
+    EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
new file mode 100644
index 0000000..f89259d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+
+open class EnterSplitScreenFromOverviewGesturalNavPortrait :
+    EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..e5aff0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+
+open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
+    SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..defade9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+
+open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
+    SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
new file mode 100644
index 0000000..e28deca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+
+open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
+    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
new file mode 100644
index 0000000..99fb06c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+
+open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
+    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..7045e66
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+
+open class SwitchBackToSplitFromHomeGesturalNavLandscape :
+    SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..b2da052
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+
+open class SwitchBackToSplitFromHomeGesturalNavPortrait :
+    SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
new file mode 100644
index 0000000..04d7f62
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+
+open class SwitchBackToSplitFromRecentGesturalNavLandscape :
+    SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
new file mode 100644
index 0000000..bc36fb7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+
+open class SwitchBackToSplitFromRecentGesturalNavPortrait :
+    SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt
new file mode 100644
index 0000000..ceda4da
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+
+open class SwitchBetweenSplitPairsGesturalNavLandscape :
+    SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt
new file mode 100644
index 0000000..365c5cc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.Rotation
+import com.android.wm.shell.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+
+open class SwitchBetweenSplitPairsGesturalNavPortrait :
+    SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
new file mode 100644
index 0000000..a866297
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
new file mode 100644
index 0000000..6d59001
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/src/com/android/wm/shell/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import com.android.wm.shell.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+@RunWith(BlockJUnit4ClassRunner::class)
+open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+    @PlatinumTest(focusArea = "sysui")
+    @Presubmit
+    @Test
+    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp
new file mode 100644
index 0000000..60c7de7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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 {
+    // 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"],
+}
+
+java_library {
+    name: "WMShellScenariosSplitScreen",
+    platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "WMShellFlickerTestsBase",
+        "WMShellTestUtils",
+        "wm-shell-flicker-utils",
+        "androidx.test.ext.junit",
+        "flickertestapplib",
+        "flickerlib-helpers",
+        "flickerlib-trace_processor_shell",
+        "platform-test-annotations",
+        "wm-flicker-common-app-helpers",
+        "wm-flicker-common-assertions",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl",
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.kt
new file mode 100644
index 0000000..ba46542
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/CopyContentInSplit.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class CopyContentInSplit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+    private val textEditApp = SplitScreenUtils.getIme(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
+    }
+
+    @Test
+    open fun copyContentInSplit() {
+        SplitScreenUtils.copyContentInSplit(instrumentation, device, primaryApp, textEditApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
new file mode 100644
index 0000000..d774a31
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByDivider.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+    }
+
+    @Test
+    open fun dismissSplitScreenByDivider() {
+        if (tapl.isTablet) {
+            SplitScreenUtils.dragDividerToDismissSplit(
+                device,
+                wmHelper,
+                dragToRight = false,
+                dragToBottom = true
+            )
+        } else {
+            SplitScreenUtils.dragDividerToDismissSplit(
+                device,
+                wmHelper,
+                dragToRight = true,
+                dragToBottom = true
+            )
+        }
+        wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.kt
new file mode 100644
index 0000000..5aa1619
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DismissSplitScreenByGoHome.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByGoHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+    }
+
+    @Test
+    open fun dismissSplitScreenByGoHome() {
+        tapl.goHome()
+        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.kt
new file mode 100644
index 0000000..668f367
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/DragDividerToResize.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DragDividerToResize
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+        // TODO: b/349075982 - Remove once launcher rotation and checks are stable.
+        tapl.setExpectedRotationCheckEnabled(false)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+    }
+
+    @Test
+    open fun dragDividerToResize() {
+        SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt
new file mode 100644
index 0000000..06c7b9b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+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("Base Test Class")
+abstract class EnterSplitScreenByDragFromAllApps
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(tapl.isTablet)
+
+        tapl.goHome()
+
+        primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        tapl.enableBlockTimeout(true)
+    }
+
+    @Test
+    open fun enterSplitScreenByDragFromAllApps() {
+        tapl.showTaskbarIfHidden()
+        tapl.launchedAppState.taskbar
+            .openAllApps()
+            .getAppIcon(secondaryApp.appName)
+            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+        tapl.enableBlockTimeout(false)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.kt
new file mode 100644
index 0000000..96b22bf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromNotification.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.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.MultiWindowUtils
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+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("Base Test Class")
+abstract class EnterSplitScreenByDragFromNotification
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+    private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(tapl.isTablet)
+
+        MultiWindowUtils.executeShellCommand(
+                instrumentation,
+                "settings put system notification_cooldown_enabled 0"
+        )
+        // Send a notification
+        sendNotificationApp.launchViaIntent(wmHelper)
+        sendNotificationApp.postNotification(wmHelper)
+        tapl.goHome()
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+    }
+
+    @Test
+    open fun enterSplitScreenByDragFromNotification() {
+        SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+        sendNotificationApp.exit(wmHelper)
+
+        MultiWindowUtils.executeShellCommand(
+                instrumentation,
+                "settings reset system notification_cooldown_enabled"
+        )
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.kt
new file mode 100644
index 0000000..9e05b63
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromShortcut.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.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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+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("Base Test Class")
+abstract class EnterSplitScreenByDragFromShortcut
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(tapl.isTablet)
+
+        tapl.goHome()
+        SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+        primaryApp.launchViaIntent(wmHelper)
+        ChangeDisplayOrientationRule.setRotation(rotation)
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        tapl.enableBlockTimeout(true)
+    }
+
+    @Test
+    open fun enterSplitScreenByDragFromShortcut() {
+        tapl.showTaskbarIfHidden()
+        tapl.launchedAppState.taskbar
+            .getAppIcon(secondaryApp.appName)
+            .openDeepShortcutMenu()
+            .getMenuItem("Split Screen Secondary Activity")
+            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+        // TODO: Do we want this check in here? Add to the other tests?
+        //        flicker.splitScreenEntered(
+        //                primaryApp,
+        //                secondaryApp,
+        //                fromOtherApp = false,
+        //                appExistAtStart = false
+        //        )
+    }
+
+    @After
+    fun teardwon() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+        tapl.enableBlockTimeout(false)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 0000000..9090055
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+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("Base Test Class")
+abstract class EnterSplitScreenByDragFromTaskbar
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(tapl.isTablet)
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        tapl.enableBlockTimeout(true)
+
+        tapl.goHome()
+        SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+        primaryApp.launchViaIntent(wmHelper)
+    }
+
+    @Test
+    open fun enterSplitScreenByDragFromTaskbar() {
+        tapl.showTaskbarIfHidden()
+        tapl.launchedAppState.taskbar
+            .getAppIcon(secondaryApp.appName)
+            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+        tapl.enableBlockTimeout(false)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.kt
new file mode 100644
index 0000000..d5cc92e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/EnterSplitScreenFromOverview.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenFromOverview
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        primaryApp.launchViaIntent(wmHelper)
+        secondaryApp.launchViaIntent(wmHelper)
+        tapl.goHome()
+        wmHelper
+            .StateSyncBuilder()
+            .withAppTransitionIdle()
+            .withHomeActivityVisible()
+            .waitForAndVerify()
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+    }
+
+    @Test
+    open fun enterSplitScreenFromOverview() {
+        SplitScreenUtils.splitFromOverview(tapl, device, rotation)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 0000000..26203d4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchAppByDoubleTapDivider.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.helpers.WindowUtils
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchAppByDoubleTapDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        val overview = tapl.workspace.switchToOverview()
+        if (overview.hasTasks()) {
+            overview.dismissAllTasks()
+        }
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+    }
+
+    @Test
+    open fun switchAppByDoubleTapDivider() {
+        SplitScreenUtils.doubleTapDividerToSwitch(device)
+        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+        waitForLayersToSwitch(wmHelper)
+        waitForWindowsToSwitch(wmHelper)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+
+    private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+        wmHelper
+            .StateSyncBuilder()
+            .add("appWindowsSwitched") {
+                val primaryAppWindow =
+                    it.wmState.visibleWindows.firstOrNull { window ->
+                        primaryApp.windowMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+                val secondaryAppWindow =
+                    it.wmState.visibleWindows.firstOrNull { window ->
+                        secondaryApp.windowMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+
+                if (isLandscape(rotation)) {
+                    return@add if (isTablet()) {
+                        secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+                    } else {
+                        primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+                    }
+                } else {
+                    return@add if (isTablet()) {
+                        primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+                    } else {
+                        primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+                    }
+                }
+            }
+            .waitForAndVerify()
+    }
+
+    private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+        wmHelper
+            .StateSyncBuilder()
+            .add("appLayersSwitched") {
+                val primaryAppLayer =
+                    it.layerState.visibleLayers.firstOrNull { window ->
+                        primaryApp.layerMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+                val secondaryAppLayer =
+                    it.layerState.visibleLayers.firstOrNull { window ->
+                        secondaryApp.layerMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+
+                val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+                val secondaryVisibleRegion =
+                    secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+                if (isLandscape(rotation)) {
+                    return@add if (isTablet()) {
+                        secondaryVisibleRegion.right <= primaryVisibleRegion.left
+                    } else {
+                        primaryVisibleRegion.right <= secondaryVisibleRegion.left
+                    }
+                } else {
+                    return@add if (isTablet()) {
+                        primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+                    } else {
+                        primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+                    }
+                }
+            }
+            .waitForAndVerify()
+    }
+
+    private fun isLandscape(rotation: Rotation): Boolean {
+        val displayBounds = WindowUtils.getDisplayBounds(rotation)
+        return displayBounds.width() > displayBounds.height()
+    }
+
+    private fun isTablet(): Boolean {
+        val sizeDp: Point = device.displaySizeDp
+        val LARGE_SCREEN_DP_THRESHOLD = 600
+        return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.kt
new file mode 100644
index 0000000..2ccffa85
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromAnotherApp.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromAnotherApp
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+    private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+
+        thirdApp.launchViaIntent(wmHelper)
+        wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+    }
+
+    @Test
+    open fun switchBackToSplitFromAnotherApp() {
+        tapl.launchedAppState.quickSwitchToPreviousApp()
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
new file mode 100644
index 0000000..8673c46
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+
+        tapl.goHome()
+        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+    }
+
+    @Test
+    open fun switchBackToSplitFromHome() {
+        tapl.workspace.quickSwitchToPreviousApp()
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
new file mode 100644
index 0000000..c7cbc3e4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBackToSplitFromRecent.kt
@@ -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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromRecent
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.workspace.switchToOverview().dismissAllTasks()
+
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+
+        tapl.goHome()
+        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+    }
+
+    @Test
+    open fun switchBackToSplitFromRecent() {
+        tapl.workspace.switchToOverview().currentTask.open()
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.kt
new file mode 100644
index 0000000..4ded148
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/SwitchBetweenSplitPairs.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.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBetweenSplitPairs
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+    private val thirdApp = SplitScreenUtils.getIme(instrumentation)
+    private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(rotation.value)
+
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
+        SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
+        SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+    }
+
+    @Test
+    open fun switchBetweenSplitPairs() {
+        tapl.launchedAppState.quickSwitchToPreviousApp()
+        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+        thirdApp.exit(wmHelper)
+        fourthApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt
new file mode 100644
index 0000000..7b062fc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/scenarios/src/com/android/wm/shell/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.scenarios
+
+import android.app.Instrumentation
+import android.tools.NavBar
+import android.tools.Rotation
+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.wm.shell.Utils
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class UnlockKeyguardToSplitScreen {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+    @Before
+    fun setup() {
+        tapl.setEnableRotation(true)
+        tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+
+        SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp)
+    }
+
+    @Test
+    open fun unlockKeyguardToSplitScreen() {
+        device.sleep()
+        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+        device.wakeUp()
+        device.pressMenu()
+        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+    }
+
+    @After
+    fun teardown() {
+        primaryApp.exit(wmHelper)
+        secondaryApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/Android.bp b/libs/WindowManager/Shell/tests/e2e/utils/Android.bp
new file mode 100644
index 0000000..51d9c40
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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 {
+    // 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_team: "trendy_team_windowing_tools",
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+    name: "WMShellTestUtils",
+    srcs: ["src/**/*.kt"],
+    static_libs: ["WMShellFlickerTestsBase"],
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
new file mode 100644
index 0000000..c0fafef
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/utils/src/com/android/wm/shell/Utils.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell
+
+import android.app.Instrumentation
+import android.platform.test.rule.EnsureDeviceSettingsRule
+import android.platform.test.rule.NavigationModeRule
+import android.platform.test.rule.PressHomeRule
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.NavBar
+import android.tools.Rotation
+import android.tools.device.apphelpers.MessagingAppHelper
+import android.tools.flicker.rules.ArtifactSaverRule
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.flicker.rules.LaunchAppRule
+import android.tools.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.RuleChain
+
+object Utils {
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+    fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
+        return RuleChain.outerRule(ArtifactSaverRule())
+            .around(UnlockScreenRule())
+            .around(NavigationModeRule(navigationMode.value, false))
+            .around(
+                LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
+            )
+            .around(RemoveAllTasksButHomeRule())
+            .around(
+                ChangeDisplayOrientationRule(
+                    rotation,
+                    resetOrientationAfterTest = false,
+                    clearCacheAfterParsing = false
+                )
+            )
+            .around(PressHomeRule())
+            .around(EnsureDeviceSettingsRule())
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 4abaf5b..7305f49 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -68,6 +68,7 @@
         "flickerlib-helpers",
         "flickerlib-trace_processor_shell",
         "platform-test-annotations",
+        "platform-test-rules",
         "wm-flicker-common-app-helpers",
         "wm-flicker-common-assertions",
         "launcher-helper-lib",
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
index e151ab2..29a9f10 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_app_compat",
     // 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"
@@ -23,6 +24,9 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merge
+
 filegroup {
     name: "WMShellFlickerTestsAppCompat-src",
     srcs: [
@@ -41,3 +45,80 @@
     static_libs: ["WMShellFlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merge
+
+android_test {
+    name: "WMShellFlickerTestsAppCompat",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker",
+    instrumentation_target_package: "com.android.wm.shell.flicker",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*.kt"],
+    static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsAppCompat module
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-CatchAll",
+    base: "WMShellFlickerTestsAppCompat",
+    exclude_filters: [
+        "com.android.wm.shell.flicker.appcompat.OpenAppInSizeCompatModeTest",
+        "com.android.wm.shell.flicker.appcompat.OpenTransparentActivityTest",
+        "com.android.wm.shell.flicker.appcompat.QuickSwitchLauncherToLetterboxAppTest",
+        "com.android.wm.shell.flicker.appcompat.RepositionFixedPortraitAppTest",
+        "com.android.wm.shell.flicker.appcompat.RestartAppInSizeCompatModeTest",
+        "com.android.wm.shell.flicker.appcompat.RotateImmersiveAppInFullscreenTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-OpenAppInSizeCompatModeTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.OpenAppInSizeCompatModeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-OpenTransparentActivityTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.OpenTransparentActivityTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-QuickSwitchLauncherToLetterboxAppTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.QuickSwitchLauncherToLetterboxAppTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-RepositionFixedPortraitAppTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.RepositionFixedPortraitAppTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-RestartAppInSizeCompatModeTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.RestartAppInSizeCompatModeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsAppCompat-RotateImmersiveAppInFullscreenTest",
+    base: "WMShellFlickerTestsAppCompat",
+    include_filters: ["com.android.wm.shell.flicker.appcompat.RotateImmersiveAppInFullscreenTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
new file mode 100644
index 0000000..a36a4f8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/OWNERS
@@ -0,0 +1,2 @@
+# Window Manager > App Compat
+# Bug component: 970984
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
index f0b4f1f..2ff7ab2 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_multitasking_windowing",
     // 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"
@@ -34,3 +35,57 @@
     static_libs: ["WMShellFlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsBubbles module
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-CatchAll",
+    base: "WMShellFlickerTestsBubbles",
+    exclude_filters: [
+        "com.android.wm.shell.flicker.bubble.ChangeActiveActivityFromBubbleTest",
+        "com.android.wm.shell.flicker.bubble.DragToDismissBubbleScreenTest",
+        "com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleOnLocksreenTest",
+        "com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleTest",
+        "com.android.wm.shell.flicker.bubble.SendBubbleNotificationTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-ChangeActiveActivityFromBubbleTest",
+    base: "WMShellFlickerTestsBubbles",
+    include_filters: ["com.android.wm.shell.flicker.bubble.ChangeActiveActivityFromBubbleTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-DragToDismissBubbleScreenTest",
+    base: "WMShellFlickerTestsBubbles",
+    include_filters: ["com.android.wm.shell.flicker.bubble.DragToDismissBubbleScreenTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-OpenActivityFromBubbleOnLocksreenTest",
+    base: "WMShellFlickerTestsBubbles",
+    include_filters: ["com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleOnLocksreenTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-OpenActivityFromBubbleTest",
+    base: "WMShellFlickerTestsBubbles",
+    include_filters: ["com.android.wm.shell.flicker.bubble.OpenActivityFromBubbleTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsBubbles-SendBubbleNotificationTest",
+    base: "WMShellFlickerTestsBubbles",
+    include_filters: ["com.android.wm.shell.flicker.bubble.SendBubbleNotificationTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for WMShellFlickerTestsBubbles module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 2ee53f4..d7ea9f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -100,14 +100,14 @@
     @Postsubmit
     @Test
     fun navBarLayerIsVisibleAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerIsVisibleAtEnd()
     }
 
     @Postsubmit
     @Test
     fun navBarLayerPositionAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerPositionAtEnd()
     }
 
@@ -154,7 +154,7 @@
     @Postsubmit
     @Test
     fun taskBarLayerIsVisibleAtEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index faeb342..4165ed0 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_multitasking_windowing",
     // 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"
@@ -24,6 +25,14 @@
 }
 
 filegroup {
+    name: "WMShellFlickerTestsPipApps-src",
+    srcs: ["src/**/apps/*.kt"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin cleanup after gcl merges
+
+filegroup {
     name: "WMShellFlickerTestsPip1-src",
     srcs: [
         "src/**/A*.kt",
@@ -52,11 +61,6 @@
     srcs: ["src/**/common/*.kt"],
 }
 
-filegroup {
-    name: "WMShellFlickerTestsPipApps-src",
-    srcs: ["src/**/apps/*.kt"],
-}
-
 android_test {
     name: "WMShellFlickerTestsPip1",
     defaults: ["WMShellFlickerTestsDefault"],
@@ -107,6 +111,21 @@
     data: ["trace_config/*"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// End cleanup after gcl merges
+
+android_test {
+    name: "WMShellFlickerTestsPip",
+    defaults: ["WMShellFlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.wm.shell.flicker.pip",
+    instrumentation_target_package: "com.android.wm.shell.flicker.pip",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*.kt"],
+    static_libs: ["WMShellFlickerTestsBase"],
+    data: ["trace_config/*"],
+}
+
 android_test {
     name: "WMShellFlickerTestsPipApps",
     defaults: ["WMShellFlickerTestsDefault"],
@@ -146,3 +165,185 @@
     test_plan_include: "csuitePlan.xml",
     test_config_template: "csuiteDefaultTemplate.xml",
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for WMShellFlickerTestsPip module
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-CatchAll",
+    base: "WMShellFlickerTestsPip",
+    exclude_filters: [
+        "com.android.wm.shell.flicker.pip.AutoEnterPipOnGoToHomeTest",
+        "com.android.wm.shell.flicker.pip.AutoEnterPipWithSourceRectHintTest",
+        "com.android.wm.shell.flicker.pip.ClosePipBySwipingDownTest",
+        "com.android.wm.shell.flicker.pip.ClosePipWithDismissButtonTest",
+        "com.android.wm.shell.flicker.pip.EnterPipOnUserLeaveHintTest",
+        "com.android.wm.shell.flicker.pip.EnterPipViaAppUiButtonTest",
+        "com.android.wm.shell.flicker.pip.ExitPipToAppViaExpandButtonTest",
+        "com.android.wm.shell.flicker.pip.ExitPipToAppViaIntentTest",
+        "com.android.wm.shell.flicker.pip.ExpandPipOnDoubleClickTest",
+        "com.android.wm.shell.flicker.pip.ExpandPipOnPinchOpenTest",
+        "com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest",
+        "com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest",
+        "com.android.wm.shell.flicker.pip.MovePipDownOnShelfHeightChange",
+        "com.android.wm.shell.flicker.pip.MovePipOnImeVisibilityChangeTest",
+        "com.android.wm.shell.flicker.pip.MovePipUpOnShelfHeightChangeTest",
+        "com.android.wm.shell.flicker.pip.PipAspectRatioChangeTest",
+        "com.android.wm.shell.flicker.pip.PipDragTest",
+        "com.android.wm.shell.flicker.pip.PipDragThenSnapTest",
+        "com.android.wm.shell.flicker.pip.PipPinchInTest",
+        "com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned",
+        "com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-AutoEnterPipOnGoToHomeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.AutoEnterPipOnGoToHomeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-AutoEnterPipWithSourceRectHintTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.AutoEnterPipWithSourceRectHintTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ClosePipBySwipingDownTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ClosePipBySwipingDownTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ClosePipWithDismissButtonTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ClosePipWithDismissButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-EnterPipOnUserLeaveHintTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.EnterPipOnUserLeaveHintTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-EnterPipViaAppUiButtonTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.EnterPipViaAppUiButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ExitPipToAppViaExpandButtonTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ExitPipToAppViaExpandButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ExitPipToAppViaIntentTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ExitPipToAppViaIntentTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ExpandPipOnDoubleClickTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ExpandPipOnDoubleClickTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ExpandPipOnPinchOpenTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ExpandPipOnPinchOpenTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-FromSplitScreenAutoEnterPipOnGoToHomeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-FromSplitScreenEnterPipOnUserLeaveHintTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-MovePipDownOnShelfHeightChange",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.MovePipDownOnShelfHeightChange"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-MovePipOnImeVisibilityChangeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.MovePipOnImeVisibilityChangeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-MovePipUpOnShelfHeightChangeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.MovePipUpOnShelfHeightChangeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-PipAspectRatioChangeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.PipAspectRatioChangeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-PipDragTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.PipDragTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-PipDragThenSnapTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.PipDragThenSnapTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-PipPinchInTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.PipPinchInTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-SetRequestedOrientationWhilePinned",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-ShowPipAndRotateDisplay",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for WMShellFlickerTestsPip module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b85d793..a9ed13a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,10 +16,14 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
+import android.tools.flicker.subject.exceptions.IncorrectRegionException
+import android.tools.flicker.subject.layers.LayerSubject
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.pip.common.EnterPipTransition
@@ -29,6 +33,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
+import kotlin.math.abs
 
 /**
  * Test entering pip from an app via auto-enter property when navigating to home.
@@ -67,9 +72,24 @@
 
     override val defaultTeardown: FlickerBuilder.() -> Unit = { teardown { pipApp.exit(wmHelper) } }
 
-    @FlakyTest(bugId = 293133362)
+    private val widthNotSmallerThan: LayerSubject.(LayerSubject) -> Unit = {
+        val width = visibleRegion.region.bounds.width()
+        val otherWidth = it.visibleRegion.region.bounds.width()
+        if (width < otherWidth && abs(width - otherWidth) > EPSILON) {
+            val errorMsgBuilder =
+                ExceptionMessageBuilder()
+                    .forSubject(this)
+                    .forIncorrectRegion("width. $width smaller than $otherWidth")
+                    .setExpected(width)
+                    .setActual(otherWidth)
+            throw IncorrectRegionException(errorMsgBuilder)
+        }
+    }
+
+    @Postsubmit
     @Test
     override fun pipLayerReduces() {
+        Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
         flicker.assertLayers {
             val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
             pipLayerList.zipWithNext { previous, current ->
@@ -78,6 +98,32 @@
         }
     }
 
+    /** Checks that [pipApp] window's width is first decreasing then increasing. */
+    @Postsubmit
+    @Test
+    fun pipLayerWidthDecreasesThenIncreases() {
+        Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+        flicker.assertLayers {
+            val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+            var previousLayer = pipLayerList[0]
+            var currentLayer = previousLayer
+            var i = 0
+            invoke("layer area is decreasing") {
+                if (i < pipLayerList.size - 1) {
+                    previousLayer = currentLayer
+                    currentLayer = pipLayerList[++i]
+                    previousLayer.widthNotSmallerThan(currentLayer)
+                }
+            }.then().invoke("layer are is increasing", true /* isOptional */) {
+                if (i < pipLayerList.size - 1) {
+                    previousLayer = currentLayer
+                    currentLayer = pipLayerList[++i]
+                    currentLayer.widthNotSmallerThan(previousLayer)
+                }
+            }
+        }
+    }
+
     /** Checks that [pipApp] window is animated towards default position in right bottom corner */
     @FlakyTest(bugId = 255578530)
     @Test
@@ -108,4 +154,9 @@
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
+
+    companion object {
+        // TODO(b/363080056): A margin of error allowed on certain layer size calculations.
+        const val EPSILON = 1
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index a5e0550..3ffc9d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -21,6 +21,7 @@
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.traces.component.ComponentNameMatcher
+import com.android.wm.shell.Flags
 import com.android.wm.shell.flicker.pip.common.ClosePipTransition
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -60,7 +61,7 @@
             val pipCenterY = pipRegion.centerY()
             val displayCenterX = device.displayWidth / 2
             val barComponent =
-                if (flicker.scenario.isTablet) {
+                if (flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()) {
                     ComponentNameMatcher.TASK_BAR
                 } else {
                     ComponentNameMatcher.NAV_BAR
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 70be58f..429774f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -35,7 +35,7 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
     override val thisTransition: FlickerBuilder.() -> Unit = {
-        transitions { pipApp.changeAspectRatio() }
+        transitions { pipApp.changeAspectRatio(wmHelper) }
     }
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 90b9798..cbd4a52 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -181,6 +181,13 @@
         super.taskBarWindowIsAlwaysVisible()
     }
 
+    // Overridden to remove @Postsubmit annotation
+    @Test
+    @FlakyTest(bugId = 294993100)
+    override fun pipLayerHasCorrectCornersAtEnd() {
+        // No rounded corners as we go back to fullscreen in new orientation.
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index ed2a0a7..578a9b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -147,6 +147,12 @@
     @Test
     override fun entireScreenCovered() = super.entireScreenCovered()
 
+    @Postsubmit
+    @Test
+    override fun pipLayerHasCorrectCornersAtEnd() {
+        flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+    }
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 3a0eeb6..68fa7c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -103,7 +103,7 @@
     @Postsubmit
     @Test
     override fun taskBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         // Netflix starts in immersive fullscreen mode, so taskbar bar is not visible at start
         flicker.assertLayersStart { this.isInvisible(ComponentNameMatcher.TASK_BAR) }
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
index 5c539a5..72be3d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
@@ -88,7 +88,7 @@
     @Postsubmit
     @Test
     override fun taskBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         // YouTube starts in immersive fullscreen mode, so taskbar bar is not visible at start
         flicker.assertLayersStart { this.isInvisible(ComponentNameMatcher.TASK_BAR) }
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
index 8cb81b4..f57335c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ClosePipTransition.kt
@@ -70,6 +70,11 @@
         }
     }
 
+    @Test
+    override fun pipLayerHasCorrectCornersAtEnd() {
+        // PiP might have completely faded out by this point, so corner radii not applicable.
+    }
+
     companion object {
         /**
          * Creates the test configurations.
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
index 0742cf9..ce84eb6 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/ExitPipToAppTransition.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip.common
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.Rotation
 import android.tools.flicker.legacy.LegacyFlickerTest
@@ -123,6 +124,12 @@
         }
     }
 
+    @Postsubmit
+    @Test
+    override fun pipLayerHasCorrectCornersAtEnd() {
+        flicker.assertLayersEnd { hasNoRoundedCorners(pipApp) }
+    }
+
     /** {@inheritDoc} */
     @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index 99c1ad2..bc2bfdb 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.content.Intent
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.Rotation
 import android.tools.flicker.legacy.FlickerBuilder
@@ -105,4 +106,10 @@
                 .doesNotContain(false)
         }
     }
+
+    @Postsubmit
+    @Test
+    open fun pipLayerHasCorrectCornersAtEnd() {
+        flicker.assertLayersEnd { hasRoundedCorners(pipApp) }
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/service/Android.bp b/libs/WindowManager/Shell/tests/flicker/service/Android.bp
deleted file mode 100644
index a5bc261..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/Android.bp
+++ /dev/null
@@ -1,75 +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 {
-    // 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"],
-}
-
-filegroup {
-    name: "WMShellFlickerServicePlatinumTests-src",
-    srcs: [
-        "src/**/platinum/*.kt",
-        "src/**/scenarios/*.kt",
-        "src/**/common/*.kt",
-    ],
-}
-
-java_library {
-    name: "wm-shell-flicker-platinum-tests",
-    platform_apis: true,
-    optimize: {
-        enabled: false,
-    },
-    srcs: [
-        ":WMShellFlickerServicePlatinumTests-src",
-    ],
-    static_libs: [
-        "wm-shell-flicker-utils",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerServiceTests",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.service",
-    instrumentation_target_package: "com.android.wm.shell.flicker.service",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: ["src/**/*.kt"],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerServicePlatinumTests",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.service",
-    instrumentation_target_package: "com.android.wm.shell.flicker.service",
-    test_config_template: "AndroidTestTemplate.xml",
-    test_suites: [
-        "device-tests",
-        "device-platinum-tests",
-    ],
-    srcs: [":WMShellFlickerServicePlatinumTests-src"],
-    static_libs: ["WMShellFlickerTestsBase"],
-    data: ["trace_config/*"],
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
deleted file mode 100644
index d54b694..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidManifest.xml
+++ /dev/null
@@ -1,77 +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.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          xmlns:tools="http://schemas.android.com/tools"
-          package="com.android.wm.shell.flicker.service">
-
-    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
-    <!-- Read and write traces from external storage -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <!-- Allow the test to write directly to /sdcard/ -->
-    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
-    <!-- Write secure settings -->
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <!-- Capture screen contents -->
-    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
-    <!-- Enable / Disable tracing !-->
-    <uses-permission android:name="android.permission.DUMP" />
-    <!-- Run layers trace -->
-    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
-    <!-- Capture screen recording -->
-    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
-    <!-- Workaround grant runtime permission exception from b/152733071 -->
-    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
-    <uses-permission android:name="android.permission.READ_LOGS"/>
-    <!-- Force-stop test apps -->
-    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
-    <!-- Control test app's media session -->
-    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
-    <!-- ATM.removeRootTasksWithActivityTypes() -->
-    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
-    <!-- Enable bubble notification-->
-    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
-    <!-- Allow the test to connect to perfetto trace processor -->
-    <uses-permission android:name="android.permission.INTERNET"/>
-
-    <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
-    <application android:requestLegacyExternalStorage="true"
-                 android:networkSecurityConfig="@xml/network_security_config"
-                 android:largeHeap="true">
-        <uses-library android:name="android.test.runner"/>
-
-        <service android:name=".NotificationListener"
-                 android:exported="true"
-                 android:label="WMShellTestsNotificationListenerService"
-                 android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
-            <intent-filter>
-                <action android:name="android.service.notification.NotificationListenerService" />
-            </intent-filter>
-        </service>
-
-        <!-- (b/197936012) Remove startup provider due to test timeout issue -->
-        <provider
-            android:name="androidx.startup.InitializationProvider"
-            android:authorities="${applicationId}.androidx-startup"
-            tools:node="remove" />
-    </application>
-
-    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.wm.shell.flicker.service"
-                     android:label="WindowManager Flicker Service Tests">
-    </instrumentation>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
deleted file mode 100644
index a66dfb4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}">
-    <option name="test-tag" value="FlickerTests"/>
-    <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
-    <option name="isolated-storage" value="false"/>
-
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- disable DeprecatedTargetSdk warning -->
-        <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/>
-        <!-- keeps the screen on during tests -->
-        <option name="screen-always-on" value="on"/>
-        <!-- prevents the phone from restarting -->
-        <option name="force-skip-system-props" value="true"/>
-        <!-- set WM tracing verbose level to all -->
-        <option name="run-command" value="cmd window tracing level all"/>
-        <!-- set WM tracing to frame (avoid incomplete states) -->
-        <option name="run-command" value="cmd window tracing frame"/>
-        <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
-        <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
-        <!-- ensure lock screen mode is swipe -->
-        <option name="run-command" value="locksettings set-disabled false"/>
-        <!-- restart launcher to activate TAPL -->
-        <option name="run-command"
-                value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
-        <!-- Increase trace size: 20mb for WM and 80mb for SF -->
-        <option name="run-command" value="cmd window tracing size 20480"/>
-        <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="test-user-token" value="%TEST_USER%"/>
-        <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
-        <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
-        <option name="run-command" value="settings put system show_touches 1"/>
-        <option name="run-command" value="settings put system pointer_location 1"/>
-        <option name="teardown-command"
-                value="settings delete secure show_ime_with_hard_keyboard"/>
-        <option name="teardown-command" value="settings delete system show_touches"/>
-        <option name="teardown-command" value="settings delete system pointer_location"/>
-        <option name="teardown-command"
-                value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true"/>
-        <option name="test-file-name" value="{MODULE}.apk"/>
-        <option name="test-file-name" value="FlickerTestApp.apk"/>
-    </target_preparer>
-    <!-- Enable mocking GPS location by the test app -->
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command"
-                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location allow"/>
-        <option name="teardown-command"
-                value="appops set com.android.wm.shell.flicker.pip.apps android:mock_location deny"/>
-    </target_preparer>
-
-    <!-- Needed for pushing the trace config file -->
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="push-file"
-                key="trace_config.textproto"
-                value="/data/misc/perfetto-traces/trace_config.textproto"
-        />
-        <!--Install the content provider automatically when we push some file in sdcard folder.-->
-        <!--Needed to avoid the installation during the test suite.-->
-        <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="{PACKAGE}"/>
-        <option name="shell-timeout" value="6600s"/>
-        <option name="test-timeout" value="6000s"/>
-        <option name="hidden-api-checks" value="false"/>
-        <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
-        <!-- PerfettoListener related arguments -->
-        <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
-        <option name="instrumentation-arg"
-                key="perfetto_config_file"
-                value="trace_config.textproto"
-        />
-        <option name="instrumentation-arg" key="per_run" value="true"/>
-        <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
-    </test>
-    <!-- Needed for pulling the collected trace config on to the host -->
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="pull-pattern-keys" value="perfetto_file_path"/>
-        <option name="directory-keys"
-                value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
-        <option name="collect-on-run-ended-only" value="true"/>
-        <option name="clean-up" value="true"/>
-    </metrics_collector>
-</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.kt
deleted file mode 100644
index 4c6c6cc..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/common/Utils.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.wm.shell.flicker.service.common
-
-import android.app.Instrumentation
-import android.platform.test.rule.NavigationModeRule
-import android.platform.test.rule.PressHomeRule
-import android.platform.test.rule.UnlockScreenRule
-import android.tools.NavBar
-import android.tools.Rotation
-import android.tools.device.apphelpers.MessagingAppHelper
-import android.tools.flicker.rules.ArtifactSaverRule
-import android.tools.flicker.rules.ChangeDisplayOrientationRule
-import android.tools.flicker.rules.LaunchAppRule
-import android.tools.flicker.rules.RemoveAllTasksButHomeRule
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.rules.RuleChain
-
-object Utils {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-
-    fun testSetupRule(navigationMode: NavBar, rotation: Rotation): RuleChain {
-        return RuleChain.outerRule(ArtifactSaverRule())
-            .around(UnlockScreenRule())
-            .around(NavigationModeRule(navigationMode.value, false))
-            .around(
-                LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
-            )
-            .around(RemoveAllTasksButHomeRule())
-            .around(
-                ChangeDisplayOrientationRule(
-                    rotation,
-                    resetOrientationAfterTest = false,
-                    clearCacheAfterParsing = false
-                )
-            )
-            .around(PressHomeRule())
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
deleted file mode 100644
index 5563bb9..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitLandscape.kt
+++ /dev/null
@@ -1,47 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class CloseAllAppWithAppHeaderExitLandscape : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_90) {
-    @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
-    @Test
-    override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig()
-                .use(FlickerServiceConfig.DEFAULT)
-                .use(CLOSE_APP)
-                .use(CLOSE_LAST_APP)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
deleted file mode 100644
index 3d16d2219..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/CloseAllAppWithAppHeaderExitPortrait.kt
+++ /dev/null
@@ -1,47 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_APP
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CLOSE_LAST_APP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class CloseAllAppWithAppHeaderExitPortrait : CloseAllAppsWithAppHeaderExit(Rotation.ROTATION_0) {
-    @ExpectedScenarios(["CLOSE_APP", "CLOSE_LAST_APP"])
-    @Test
-    override fun closeAllAppsInDesktop() = super.closeAllAppsInDesktop()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig()
-                .use(FlickerServiceConfig.DEFAULT)
-                .use(CLOSE_APP)
-                .use(CLOSE_LAST_APP)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
deleted file mode 100644
index 430f80b..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
+++ /dev/null
@@ -1,164 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.flicker.AssertionInvocationGroup
-import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd
-import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
-import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
-import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible
-import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
-import android.tools.flicker.assertors.assertions.AppWindowHasSizeOfAtLeast
-import android.tools.flicker.assertors.assertions.AppWindowIsInvisibleAtEnd
-import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
-import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
-import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTopWindow
-import android.tools.flicker.config.AssertionTemplates
-import android.tools.flicker.config.FlickerConfigEntry
-import android.tools.flicker.config.ScenarioId
-import android.tools.flicker.config.desktopmode.Components
-import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER
-import android.tools.flicker.extractors.ITransitionMatcher
-import android.tools.flicker.extractors.ShellTransitionScenarioExtractor
-import android.tools.flicker.extractors.TaggedCujTransitionMatcher
-import android.tools.flicker.extractors.TaggedScenarioExtractorBuilder
-import android.tools.traces.events.CujType
-import android.tools.traces.wm.Transition
-import android.tools.traces.wm.TransitionType
-
-class DesktopModeFlickerScenarios {
-    companion object {
-        val END_DRAG_TO_DESKTOP =
-            FlickerConfigEntry(
-                scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
-                extractor =
-                    ShellTransitionScenarioExtractor(
-                        transitionMatcher =
-                            object : ITransitionMatcher {
-                                override fun findAll(
-                                    transitions: Collection<Transition>
-                                ): Collection<Transition> {
-                                    return transitions.filter {
-                                        // TODO(351168217) Use jank CUJ to extract a longer trace
-                                        it.type == TransitionType.DESKTOP_MODE_END_DRAG_TO_DESKTOP
-                                    }
-                                }
-                            }
-                    ),
-                assertions =
-                    AssertionTemplates.COMMON_ASSERTIONS +
-                        listOf(
-                                AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
-                                AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
-                                AppWindowHasDesktopModeInitialBoundsAtTheEnd(
-                                    Components.DESKTOP_MODE_APP
-                                ),
-                                AppWindowBecomesVisible(DESKTOP_WALLPAPER)
-                            )
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
-            )
-
-        // Use this scenario for closing an app in desktop windowing, except the last app. For the
-        // last app use CLOSE_LAST_APP scenario
-        val CLOSE_APP =
-            FlickerConfigEntry(
-                scenarioId = ScenarioId("CLOSE_APP"),
-                extractor =
-                    ShellTransitionScenarioExtractor(
-                        transitionMatcher =
-                            object : ITransitionMatcher {
-                                override fun findAll(
-                                    transitions: Collection<Transition>
-                                ): Collection<Transition> {
-                                    // In case there are multiple windows closing, filter out the
-                                    // last window closing. It should use the CLOSE_LAST_APP
-                                    // scenario below.
-                                    return transitions
-                                        .filter { it.type == TransitionType.CLOSE }
-                                        .sortedByDescending { it.id }
-                                        .drop(1)
-                                }
-                            }
-                    ),
-                assertions =
-                    AssertionTemplates.COMMON_ASSERTIONS +
-                        listOf(
-                                AppWindowOnTopAtStart(Components.DESKTOP_MODE_APP),
-                                AppLayerIsVisibleAtStart(Components.DESKTOP_MODE_APP),
-                                AppLayerIsInvisibleAtEnd(Components.DESKTOP_MODE_APP),
-                            )
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
-            )
-
-        val CLOSE_LAST_APP =
-            FlickerConfigEntry(
-                scenarioId = ScenarioId("CLOSE_LAST_APP"),
-                extractor =
-                    ShellTransitionScenarioExtractor(
-                        transitionMatcher =
-                            object : ITransitionMatcher {
-                                override fun findAll(
-                                    transitions: Collection<Transition>
-                                ): Collection<Transition> {
-                                    val lastTransition =
-                                        transitions
-                                            .filter { it.type == TransitionType.CLOSE }
-                                            .maxByOrNull { it.id }!!
-                                    return listOf(lastTransition)
-                                }
-                            }
-                    ),
-                assertions =
-                    AssertionTemplates.COMMON_ASSERTIONS +
-                        listOf(
-                                AppWindowIsInvisibleAtEnd(Components.DESKTOP_MODE_APP),
-                                LauncherWindowReplacesAppAsTopWindow(Components.DESKTOP_MODE_APP),
-                                AppWindowIsInvisibleAtEnd(DESKTOP_WALLPAPER)
-                            )
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
-            )
-
-        val CORNER_RESIZE =
-            FlickerConfigEntry(
-                scenarioId = ScenarioId("CORNER_RESIZE"),
-                extractor =
-                    TaggedScenarioExtractorBuilder()
-                        .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
-                        .setTransitionMatcher(
-                            TaggedCujTransitionMatcher(associatedTransitionRequired = false)
-                        )
-                        .build(),
-                assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS
-            )
-
-        val CORNER_RESIZE_TO_MINIMUM_SIZE =
-            FlickerConfigEntry(
-                scenarioId = ScenarioId("CORNER_RESIZE_TO_MINIMUM_SIZE"),
-                extractor =
-                    TaggedScenarioExtractorBuilder()
-                        .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
-                        .setTransitionMatcher(
-                            TaggedCujTransitionMatcher(associatedTransitionRequired = false)
-                        )
-                        .build(),
-                assertions =
-                    AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
-                        listOf(AppWindowHasSizeOfAtLeast(Components.DESKTOP_MODE_APP, 770, 700))
-                            .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
-            )
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
deleted file mode 100644
index 9dfafe9..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterDesktopWithDragLandscape : EnterDesktopWithDrag(Rotation.ROTATION_90) {
-    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
-    @Test
-    override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
-
-    companion object {
-
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
deleted file mode 100644
index 1c7d623..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/EnterDesktopWithDragPortrait.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.END_DRAG_TO_DESKTOP
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterDesktopWithDragPortrait : EnterDesktopWithDrag(Rotation.ROTATION_0) {
-    @ExpectedScenarios(["END_DRAG_TO_DESKTOP"])
-    @Test
-    override fun enterDesktopWithDrag() = super.enterDesktopWithDrag()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(END_DRAG_TO_DESKTOP)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
deleted file mode 100644
index 6319cf7..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizeLandscape.kt
+++ /dev/null
@@ -1,52 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Resize app window using corner resize to the smallest possible height and width in
- * landscape mode.
- *
- * Assert that the minimum window size constraint is maintained.
- */
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppToMinimumWindowSizeLandscape : ResizeAppWithCornerResize(
-    rotation = Rotation.ROTATION_90,
-    horizontalChange = -1500,
-    verticalChange = 1500) {
-    @ExpectedScenarios(["CORNER_RESIZE_TO_MINIMUM_SIZE"])
-    @Test
-    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MINIMUM_SIZE)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt
deleted file mode 100644
index 431f6e3..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppToMinimumWindowSizePortrait.kt
+++ /dev/null
@@ -1,48 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MINIMUM_SIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Resize app window using corner resize to the smallest possible height and width in portrait mode.
- *
- * Assert that the minimum window size constraint is maintained.
- */
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppToMinimumWindowSizePortrait : ResizeAppWithCornerResize(horizontalChange = -1500,
-    verticalChange = 1500) {
-    @ExpectedScenarios(["CORNER_RESIZE_TO_MINIMUM_SIZE"])
-    @Test
-    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MINIMUM_SIZE)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt
deleted file mode 100644
index 8d1a530..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizeLandscape.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppWithCornerResizeLandscape : ResizeAppWithCornerResize(Rotation.ROTATION_90) {
-    @ExpectedScenarios(["CORNER_RESIZE"])
-    @Test
-    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt
deleted file mode 100644
index 2d81c8c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/ResizeAppWithCornerResizePortrait.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.desktopmode.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.desktopmode.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class ResizeAppWithCornerResizePortrait : ResizeAppWithCornerResize(Rotation.ROTATION_0) {
-    @ExpectedScenarios(["CORNER_RESIZE"])
-    @Test
-    override fun resizeAppWithCornerResize() = super.resizeAppWithCornerResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt
deleted file mode 100644
index 9ba3a45..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/CloseAllAppsWithAppHeaderExitTest.kt
+++ /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 com.android.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.CloseAllAppsWithAppHeaderExit
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [CloseAllAppsWithAppHeaderExit]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class CloseAllAppsWithAppHeaderExitTest : CloseAllAppsWithAppHeaderExit() {
-
-    @Test
-    override fun closeAllAppsInDesktop() {
-        super.closeAllAppsInDesktop()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt
deleted file mode 100644
index ed1d488..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowMultiWindowTest.kt
+++ /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 com.android.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.DragAppWindowMultiWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [DragAppWindowMultiWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowMultiWindowTest : DragAppWindowMultiWindow()
-{
-    @Test
-    override fun dragAppWindow() {
-        super.dragAppWindow()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt
deleted file mode 100644
index d8b9348..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/DragAppWindowSingleWindowTest.kt
+++ /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 com.android.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.DragAppWindowSingleWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [DragAppWindowSingleWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowSingleWindowTest : DragAppWindowSingleWindow()
-{
-    @Test
-    override fun dragAppWindow() {
-        super.dragAppWindow()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt
deleted file mode 100644
index 546ce2d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithAppHandleMenuTest.kt
+++ /dev/null
@@ -1,33 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithAppHandleMenu
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [EnterDesktopWithAppHandleMenu]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithAppHandleMenuTest : EnterDesktopWithAppHandleMenu() {
-    @Test
-    override fun enterDesktopWithAppHandleMenu() {
-        super.enterDesktopWithAppHandleMenu()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt
deleted file mode 100644
index b5fdb16..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/EnterDesktopWithDragTest.kt
+++ /dev/null
@@ -1,35 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.EnterDesktopWithDrag
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [EnterDesktopWithDrag]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithDragTest : EnterDesktopWithDrag(Rotation.ROTATION_0) {
-
-    @Test
-    override fun enterDesktopWithDrag() {
-        super.enterDesktopWithDrag()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt
deleted file mode 100644
index 8f802d2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ExitDesktopWithDragToTopDragZoneTest.kt
+++ /dev/null
@@ -1,35 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ExitDesktopWithDragToTopDragZone
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [ExitDesktopWithDragToTopDragZone]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ExitDesktopWithDragToTopDragZoneTest :
-    ExitDesktopWithDragToTopDragZone(Rotation.ROTATION_0) {
-    @Test
-    override fun exitDesktopWithDragToTopDragZone() {
-        super.exitDesktopWithDragToTopDragZone()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt
deleted file mode 100644
index f899082..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MaximizeAppWindowTest.kt
+++ /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 com.android.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.MaximizeAppWindow
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [MaximizeAppWindow]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class MaximizeAppWindowTest : MaximizeAppWindow()
-{
-    @Test
-    override fun maximizeAppWindow() {
-        super.maximizeAppWindow()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt
deleted file mode 100644
index 63c428a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/MinimizeWindowOnAppOpenTest.kt
+++ /dev/null
@@ -1,36 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.MinimizeWindowOnAppOpen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [MinimizeWindowOnAppOpen]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class MinimizeWindowOnAppOpenTest : MinimizeWindowOnAppOpen()
-{
-    @Test
-    override fun openAppToMinimizeWindow() {
-        // Launch a new app while 4 apps are already open on desktop. This should result in the
-        // first app we opened to be minimized.
-        super.openAppToMinimizeWindow()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt
deleted file mode 100644
index 4797aaf..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/ResizeAppWithCornerResizeTest.kt
+++ /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 com.android.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.ResizeAppWithCornerResize
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [ResizeAppWithCornerResize]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize(Rotation.ROTATION_0) {
-    @Test
-    override fun resizeAppWithCornerResize() {
-        super.resizeAppWithCornerResize()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt
deleted file mode 100644
index 9a71361..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/functional/SwitchToOverviewFromDesktopTest.kt
+++ /dev/null
@@ -1,33 +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.wm.shell.flicker.service.desktopmode.functional
-
-import android.platform.test.annotations.Postsubmit
-import com.android.wm.shell.flicker.service.desktopmode.scenarios.SwitchToOverviewFromDesktop
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-/** Functional test for [SwitchToOverviewFromDesktop]. */
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop() {
-    @Test
-    override fun switchToOverview() {
-        super.switchToOverview()
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
deleted file mode 100644
index e77a457..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ /dev/null
@@ -1,77 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.MailAppHelper
-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.flicker.service.common.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("Base Test Class")
-abstract class CloseAllAppsWithAppHeaderExit
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
-    private val nonResizeableApp = DesktopModeAppHelper(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)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-        mailApp.launchViaIntent(wmHelper)
-        nonResizeableApp.launchViaIntent(wmHelper)
-    }
-
-    @Test
-    open fun closeAllAppsInDesktop() {
-        nonResizeableApp.closeDesktopApp(wmHelper, device)
-        mailApp.closeDesktopApp(wmHelper, device)
-        testApp.closeDesktopApp(wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt
deleted file mode 100644
index bbf0ce5..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowMultiWindow.kt
+++ /dev/null
@@ -1,66 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.MailAppHelper
-import com.android.server.wm.flicker.helpers.NewTasksAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.window.flags.Flags
-import org.junit.After
-import org.junit.Assume
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-/** Base scenario test for window drag CUJ with multiple windows. */
-@Ignore("Base Test Class")
-abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
-{
-    private val imeAppHelper = ImeAppHelper(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
-    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
-    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-        mailApp.launchViaIntent(wmHelper)
-        newTasksApp.launchViaIntent(wmHelper)
-        imeApp.launchViaIntent(wmHelper)
-    }
-
-    @Test
-    override fun dragAppWindow() {
-        val (startXIme, startYIme) = getWindowDragStartCoordinate(imeAppHelper)
-
-        imeApp.dragWindow(startXIme, startYIme,
-            endX = startXIme + 150, endY = startYIme + 150,
-            wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-        mailApp.exit(wmHelper)
-        newTasksApp.exit(wmHelper)
-        imeApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt
deleted file mode 100644
index a613ca1..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowScenarioTestBase.kt
+++ /dev/null
@@ -1,55 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-import android.tools.device.apphelpers.StandardAppHelper
-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.wm.shell.flicker.service.common.Utils
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-/** Base test class for window drag CUJ. */
-@Ignore("Base Test Class")
-abstract class DragAppWindowScenarioTestBase {
-
-    val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    val tapl = LauncherInstrumentation()
-    val wmHelper = WindowManagerStateHelper(instrumentation)
-    val device = UiDevice.getInstance(instrumentation)
-
-    @Rule
-    @JvmField
-    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
-
-    @Test abstract fun dragAppWindow()
-
-    /** Return the top-center coordinate of the app header as the start coordinate. */
-    fun getWindowDragStartCoordinate(appHelper: StandardAppHelper): Pair<Int, Int> {
-        val windowRect = wmHelper.getWindowRegion(appHelper).bounds
-        // Set start x-coordinate as center of app header.
-        val startX = windowRect.centerX()
-        val startY = windowRect.top
-        return Pair(startX, startY)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt
deleted file mode 100644
index 0655620..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/DragAppWindowSingleWindow.kt
+++ /dev/null
@@ -1,53 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.window.flags.Flags
-import org.junit.After
-import org.junit.Assume
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-/** Base scenario test for window drag CUJ with single window. */
-@Ignore("Base Test Class")
-abstract class DragAppWindowSingleWindow : DragAppWindowScenarioTestBase()
-{
-    private val simpleAppHelper = SimpleAppHelper(instrumentation)
-    private val testApp = DesktopModeAppHelper(simpleAppHelper)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @Test
-    override fun dragAppWindow() {
-        val (startXTest, startYTest) = getWindowDragStartCoordinate(simpleAppHelper)
-        testApp.dragWindow(startXTest, startYTest,
-            endX = startXTest + 150, endY = startYTest + 150,
-            wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt
deleted file mode 100644
index 47a215a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithAppHandleMenu.kt
+++ /dev/null
@@ -1,59 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import org.junit.After
-import org.junit.Assume
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-/** Base test class for enter desktop with app handle menu CUJ. */
-@Ignore("Base Test Class")
-abstract class EnterDesktopWithAppHandleMenu {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val simpleAppHelper = SimpleAppHelper(instrumentation)
-    private val testApp = DesktopModeAppHelper(simpleAppHelper)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-    }
-
-    @Test
-    open fun enterDesktopWithAppHandleMenu() {
-        simpleAppHelper.launchViaIntent(wmHelper)
-        testApp.enterDesktopModeFromAppHandleMenu(wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.kt
deleted file mode 100644
index fe139d2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/EnterDesktopWithDrag.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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.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("Base Test Class")
-abstract class EnterDesktopWithDrag
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(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)
-    }
-
-    @Test
-    open fun enterDesktopWithDrag() {
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt
deleted file mode 100644
index 0b6c9af..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ /dev/null
@@ -1,68 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.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("Base Test Class")
-abstract class ExitDesktopWithDragToTopDragZone
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(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)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @Test
-    open fun exitDesktopWithDragToTopDragZone() {
-        testApp.exitDesktopWithDragToTopDragZone(wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt
deleted file mode 100644
index 20e2167c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MaximizeAppWindow.kt
+++ /dev/null
@@ -1,65 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.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
-
-/** Base scenario test for maximize app window CUJ in desktop mode. */
-@Ignore("Base Test Class")
-abstract class MaximizeAppWindow
-{
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL,
-        Rotation.ROTATION_0)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @Test
-    open fun maximizeAppWindow() {
-        testApp.maximiseDesktopApp(wmHelper, device)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
deleted file mode 100644
index c847710..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/MinimizeWindowOnAppOpen.kt
+++ /dev/null
@@ -1,80 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-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.ImeAppHelper
-import com.android.server.wm.flicker.helpers.LetterboxAppHelper
-import com.android.server.wm.flicker.helpers.MailAppHelper
-import com.android.server.wm.flicker.helpers.NewTasksAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.window.flags.Flags
-import org.junit.After
-import org.junit.Assume
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-
-/**
- * Base scenario test for minimizing the least recently used window when a new window is opened
- * above the window limit. For tangor devices, which this test currently runs on, the window limit
- * is 4.
- */
-@Ignore("Base Test Class")
-abstract class MinimizeWindowOnAppOpen()
-{
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-    private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation))
-    private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation))
-    private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation))
-    private val letterboxAppHelper = DesktopModeAppHelper(LetterboxAppHelper(instrumentation))
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-        mailApp.launchViaIntent(wmHelper)
-        newTasksApp.launchViaIntent(wmHelper)
-        imeApp.launchViaIntent(wmHelper)
-    }
-
-    @Test
-    open fun openAppToMinimizeWindow() {
-        // Launch a new app while 4 apps are already open on desktop. This should result in the
-        // first app we opened to be minimized.
-        letterboxAppHelper.launchViaIntent(wmHelper)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-        mailApp.exit(wmHelper)
-        newTasksApp.exit(wmHelper)
-        imeApp.exit(wmHelper)
-        letterboxAppHelper.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt
deleted file mode 100644
index 136cf37..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/ResizeAppWithCornerResize.kt
+++ /dev/null
@@ -1,76 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.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("Base Test Class")
-abstract class ResizeAppWithCornerResize
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0,
-    val horizontalChange: Int = 50,
-    val verticalChange: Int = -50) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(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)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @Test
-    open fun resizeAppWithCornerResize() {
-        testApp.cornerResize(wmHelper,
-            device,
-            DesktopModeAppHelper.Corners.RIGHT_TOP,
-            horizontalChange,
-            verticalChange)
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
deleted file mode 100644
index b4cadf4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/scenarios/SwitchToOverviewFromDesktop.kt
+++ /dev/null
@@ -1,70 +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.wm.shell.flicker.service.desktopmode.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.SimpleAppHelper
-import com.android.window.flags.Flags
-import com.android.wm.shell.flicker.service.common.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
-
-/**
-* Base test for opening recent apps overview from desktop mode.
-*
-* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
-*/
-@Ignore("Base Test Class")
-abstract class SwitchToOverviewFromDesktop
-@JvmOverloads
-constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(navigationMode, Rotation.ROTATION_0)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
-        testApp.enterDesktopWithDrag(wmHelper, device)
-    }
-
-    @Test
-    open fun switchToOverview() {
-        tapl.getLaunchedAppState().switchToOverview()
-    }
-
-    @After
-    fun teardown() {
-        testApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
deleted file mode 100644
index 3ab6a1e..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Android > Android OS & Apps > Framework (Java + Native) > Window Manager > WM Shell > Split Screen
-# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
deleted file mode 100644
index 1684a26..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
+++ /dev/null
@@ -1,40 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
-    @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
deleted file mode 100644
index 3b5fad6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
+++ /dev/null
@@ -1,40 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
-    @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
deleted file mode 100644
index 2b8a903..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ /dev/null
@@ -1,45 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DismissSplitScreenByDividerGesturalNavLandscape :
-    DismissSplitScreenByDivider(Rotation.ROTATION_90) {
-
-    // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
-    @ExpectedScenarios(["ENTIRE_TRACE"])
-    @Test
-    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
deleted file mode 100644
index b284fe1..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ /dev/null
@@ -1,45 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DismissSplitScreenByDividerGesturalNavPortrait :
-    DismissSplitScreenByDivider(Rotation.ROTATION_0) {
-
-    // TODO(b/300260196): Not detecting SPLIT_SCREEN_EXIT right now
-    @ExpectedScenarios(["ENTIRE_TRACE"])
-    @Test
-    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
deleted file mode 100644
index a400ee4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DismissSplitScreenByGoHomeGesturalNavLandscape :
-    DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
-    @Test
-    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
deleted file mode 100644
index 7f5ee4c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DismissSplitScreenByGoHomeGesturalNavPortrait :
-    DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["APP_CLOSE_TO_HOME"]) // SPLIT_SCREEN_EXIT not yet tagged here (b/301222449)
-    @Test
-    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
deleted file mode 100644
index 1b075c4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
-    @Test
-    override fun dragDividerToResize() = super.dragDividerToResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
deleted file mode 100644
index 6ca3737..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
-    @Test
-    override fun dragDividerToResize() = super.dragDividerToResize()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
deleted file mode 100644
index f7d231f0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
-    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
-    @Test
-    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
deleted file mode 100644
index ab819fa..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
-    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["ENTIRE_TRACE"]) // missing SPLIT_SCREEN_ENTER tag (b/301093332)
-    @Test
-    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
deleted file mode 100644
index a6b732c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ /dev/null
@@ -1,45 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
-    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromNotification() =
-        super.enterSplitScreenByDragFromNotification()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
deleted file mode 100644
index 07e5f4b..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ /dev/null
@@ -1,45 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
-    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromNotification() =
-        super.enterSplitScreenByDragFromNotification()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
deleted file mode 100644
index 2725694..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
-    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
deleted file mode 100644
index 58cc4d7..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
-    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
deleted file mode 100644
index 85897a1..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
-    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
deleted file mode 100644
index 891b6df..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
-    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
deleted file mode 100644
index 7983652..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenFromOverviewGesturalNavLandscape :
-    EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
deleted file mode 100644
index 1bdea66..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class EnterSplitScreenFromOverviewGesturalNavPortrait :
-    EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
deleted file mode 100644
index bab0c0a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchAppByDoubleTapDividerGesturalNavLandscape :
-    SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios([])
-    @Test
-    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
deleted file mode 100644
index 17a59ab..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchAppByDoubleTapDividerGesturalNavPortrait :
-    SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios([])
-    @Test
-    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
deleted file mode 100644
index 2c36d64..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
-    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
deleted file mode 100644
index 6e91d04..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
-    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
deleted file mode 100644
index a921b46..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromHomeGesturalNavLandscape :
-    SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
deleted file mode 100644
index 05f8912..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromHomeGesturalNavPortrait :
-    SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
deleted file mode 100644
index 1ae1f53..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromRecentGesturalNavLandscape :
-    SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
deleted file mode 100644
index e14ca550..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ /dev/null
@@ -1,44 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBackToSplitFromRecentGesturalNavPortrait :
-    SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
-    @Test
-    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
deleted file mode 100644
index ce0c4c4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
deleted file mode 100644
index 5a8d2d5..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ /dev/null
@@ -1,43 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.Rotation
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
-
-    @ExpectedScenarios(["QUICKSWITCH"])
-    @Test
-    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
deleted file mode 100644
index d442615..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ /dev/null
@@ -1,42 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
-
-    @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
-    @Test
-    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
deleted file mode 100644
index ddc8a06..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ /dev/null
@@ -1,42 +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.wm.shell.flicker.service.splitscreen.flicker
-
-import android.tools.flicker.FlickerConfig
-import android.tools.flicker.annotation.ExpectedScenarios
-import android.tools.flicker.annotation.FlickerConfigProvider
-import android.tools.flicker.config.FlickerConfig
-import android.tools.flicker.config.FlickerServiceConfig
-import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(FlickerServiceJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
-
-    @ExpectedScenarios(["LOCKSCREEN_UNLOCK_ANIMATION"])
-    @Test
-    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-
-    companion object {
-        @JvmStatic
-        @FlickerConfigProvider
-        fun flickerConfigProvider(): FlickerConfig =
-            FlickerConfig().use(FlickerServiceConfig.DEFAULT)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
deleted file mode 100644
index 64293b2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
+++ /dev/null
@@ -1,30 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-
-open class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun copyContentInSplit() = super.copyContentInSplit()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
deleted file mode 100644
index 517ba2d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
+++ /dev/null
@@ -1,30 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-
-open class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun copyContentInSplit() = super.copyContentInSplit()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
deleted file mode 100644
index 1bafe3b..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Test
-
-open class DismissSplitScreenByDividerGesturalNavLandscape :
-    DismissSplitScreenByDivider(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
deleted file mode 100644
index fd0100f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
-import org.junit.Test
-
-open class DismissSplitScreenByDividerGesturalNavPortrait :
-    DismissSplitScreenByDivider(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
deleted file mode 100644
index 850b3d8..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Test
-
-open class DismissSplitScreenByGoHomeGesturalNavLandscape :
-    DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
deleted file mode 100644
index 0b752bf..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
-import org.junit.Test
-
-open class DismissSplitScreenByGoHomeGesturalNavPortrait :
-    DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
deleted file mode 100644
index 3c52aa7..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,30 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-
-open class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dragDividerToResize() = super.dragDividerToResize()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
deleted file mode 100644
index c2e21b8..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,30 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-
-open class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dragDividerToResize() = super.dragDividerToResize()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
deleted file mode 100644
index bf85ab4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
-    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
deleted file mode 100644
index 0ac4ca2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
-    EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
deleted file mode 100644
index 80bd088..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
+++ /dev/null
@@ -1,32 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
-    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromNotification() =
-        super.enterSplitScreenByDragFromNotification()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
deleted file mode 100644
index 0dffb4a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
+++ /dev/null
@@ -1,32 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
-    EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromNotification() =
-        super.enterSplitScreenByDragFromNotification()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
deleted file mode 100644
index b721f2f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
-    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
deleted file mode 100644
index 22cbc77..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
-    EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
deleted file mode 100644
index ac0f9e2..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
-    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
deleted file mode 100644
index f7a229d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
-import org.junit.Test
-
-open class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
-    EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
deleted file mode 100644
index 6dbbcb0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Test
-
-open class EnterSplitScreenFromOverviewGesturalNavLandscape :
-    EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
deleted file mode 100644
index bd69ea9..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
-import org.junit.Test
-
-open class EnterSplitScreenFromOverviewGesturalNavPortrait :
-    EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
deleted file mode 100644
index 404b96f..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Test
-
-open class SwitchAppByDoubleTapDividerGesturalNavLandscape :
-    SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
deleted file mode 100644
index a79687d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
-import org.junit.Test
-
-open class SwitchAppByDoubleTapDividerGesturalNavPortrait :
-    SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
deleted file mode 100644
index b52eb4c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Test
-
-open class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
-    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
deleted file mode 100644
index d79620c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
-import org.junit.Test
-
-open class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
-    SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
deleted file mode 100644
index d27bfa1..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Test
-
-open class SwitchBackToSplitFromHomeGesturalNavLandscape :
-    SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
deleted file mode 100644
index 3c7d4d4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
-import org.junit.Test
-
-open class SwitchBackToSplitFromHomeGesturalNavPortrait :
-    SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
deleted file mode 100644
index 26a2034..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Test
-
-open class SwitchBackToSplitFromRecentGesturalNavLandscape :
-    SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
deleted file mode 100644
index 5154b35..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
-import org.junit.Test
-
-open class SwitchBackToSplitFromRecentGesturalNavPortrait :
-    SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
deleted file mode 100644
index 86451c5..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-
-open class SwitchBetweenSplitPairsGesturalNavLandscape :
-    SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
deleted file mode 100644
index baf72b4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
+++ /dev/null
@@ -1,31 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-
-open class SwitchBetweenSplitPairsGesturalNavPortrait :
-    SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
deleted file mode 100644
index 9caab9b..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
+++ /dev/null
@@ -1,32 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-@RunWith(BlockJUnit4ClassRunner::class)
-open class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
deleted file mode 100644
index bf484e5..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
+++ /dev/null
@@ -1,32 +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.wm.shell.flicker.service.splitscreen.platinum
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-@RunWith(BlockJUnit4ClassRunner::class)
-open class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
deleted file mode 100644
index 6171074..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ /dev/null
@@ -1,67 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class CopyContentInSplit
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-    private val textEditApp = SplitScreenUtils.getIme(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp, rotation)
-    }
-
-    @Test
-    open fun copyContentInSplit() {
-        SplitScreenUtils.copyContentInSplit(instrumentation, device, primaryApp, textEditApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
deleted file mode 100644
index c1a8ee7..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ /dev/null
@@ -1,80 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class DismissSplitScreenByDivider
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-    }
-
-    @Test
-    open fun dismissSplitScreenByDivider() {
-        if (tapl.isTablet) {
-            SplitScreenUtils.dragDividerToDismissSplit(
-                device,
-                wmHelper,
-                dragToRight = false,
-                dragToBottom = true
-            )
-        } else {
-            SplitScreenUtils.dragDividerToDismissSplit(
-                device,
-                wmHelper,
-                dragToRight = true,
-                dragToBottom = true
-            )
-        }
-        wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
deleted file mode 100644
index 600855a..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ /dev/null
@@ -1,66 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class DismissSplitScreenByGoHome
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-    }
-
-    @Test
-    open fun dismissSplitScreenByGoHome() {
-        tapl.goHome()
-        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
deleted file mode 100644
index b5a6d83..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ /dev/null
@@ -1,67 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class DragDividerToResize
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-        // TODO: b/349075982 - Remove once launcher rotation and checks are stable.
-        tapl.setExpectedRotationCheckEnabled(false)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-    }
-
-    @Test
-    open fun dragDividerToResize() {
-        SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
deleted file mode 100644
index a189325..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ /dev/null
@@ -1,80 +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.wm.shell.flicker.service.splitscreen.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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-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("Base Test Class")
-abstract class EnterSplitScreenByDragFromAllApps
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(tapl.isTablet)
-
-        tapl.goHome()
-
-        primaryApp.launchViaIntent(wmHelper)
-        ChangeDisplayOrientationRule.setRotation(rotation)
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        tapl.enableBlockTimeout(true)
-    }
-
-    @Test
-    open fun enterSplitScreenByDragFromAllApps() {
-        tapl.showTaskbarIfHidden()
-        tapl.launchedAppState.taskbar
-            .openAllApps()
-            .getAppIcon(secondaryApp.appName)
-            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-        tapl.enableBlockTimeout(false)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
deleted file mode 100644
index bcd0f12..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ /dev/null
@@ -1,88 +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.wm.shell.flicker.service.splitscreen.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.MultiWindowUtils
-import com.android.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-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("Base Test Class")
-abstract class EnterSplitScreenByDragFromNotification
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-    private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(tapl.isTablet)
-
-        MultiWindowUtils.executeShellCommand(
-                instrumentation,
-                "settings put system notification_cooldown_enabled 0"
-        )
-        // Send a notification
-        sendNotificationApp.launchViaIntent(wmHelper)
-        sendNotificationApp.postNotification(wmHelper)
-        tapl.goHome()
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        primaryApp.launchViaIntent(wmHelper)
-        ChangeDisplayOrientationRule.setRotation(rotation)
-    }
-
-    @Test
-    open fun enterSplitScreenByDragFromNotification() {
-        SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-        sendNotificationApp.exit(wmHelper)
-
-        MultiWindowUtils.executeShellCommand(
-                instrumentation,
-                "settings reset system notification_cooldown_enabled"
-        )
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
deleted file mode 100644
index 3f07be0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ /dev/null
@@ -1,89 +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.wm.shell.flicker.service.splitscreen.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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-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("Base Test Class")
-abstract class EnterSplitScreenByDragFromShortcut
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(tapl.isTablet)
-
-        tapl.goHome()
-        SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
-        primaryApp.launchViaIntent(wmHelper)
-        ChangeDisplayOrientationRule.setRotation(rotation)
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        tapl.enableBlockTimeout(true)
-    }
-
-    @Test
-    open fun enterSplitScreenByDragFromShortcut() {
-        tapl.showTaskbarIfHidden()
-        tapl.launchedAppState.taskbar
-            .getAppIcon(secondaryApp.appName)
-            .openDeepShortcutMenu()
-            .getMenuItem("Split Screen Secondary Activity")
-            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-
-        // TODO: Do we want this check in here? Add to the other tests?
-        //        flicker.splitScreenEntered(
-        //                primaryApp,
-        //                secondaryApp,
-        //                fromOtherApp = false,
-        //                appExistAtStart = false
-        //        )
-    }
-
-    @After
-    fun teardwon() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-        tapl.enableBlockTimeout(false)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
deleted file mode 100644
index 5328013..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
+++ /dev/null
@@ -1,77 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-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("Base Test Class")
-abstract class EnterSplitScreenByDragFromTaskbar
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        Assume.assumeTrue(tapl.isTablet)
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        tapl.enableBlockTimeout(true)
-
-        tapl.goHome()
-        SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
-        primaryApp.launchViaIntent(wmHelper)
-    }
-
-    @Test
-    open fun enterSplitScreenByDragFromTaskbar() {
-        tapl.showTaskbarIfHidden()
-        tapl.launchedAppState.taskbar
-            .getAppIcon(secondaryApp.appName)
-            .dragToSplitscreen(secondaryApp.packageName, primaryApp.packageName)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-        tapl.enableBlockTimeout(false)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
deleted file mode 100644
index be4035d..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ /dev/null
@@ -1,73 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class EnterSplitScreenFromOverview
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        primaryApp.launchViaIntent(wmHelper)
-        secondaryApp.launchViaIntent(wmHelper)
-        tapl.goHome()
-        wmHelper
-            .StateSyncBuilder()
-            .withAppTransitionIdle()
-            .withHomeActivityVisible()
-            .waitForAndVerify()
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-    }
-
-    @Test
-    open fun enterSplitScreenFromOverview() {
-        SplitScreenUtils.splitFromOverview(tapl, device, rotation)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
deleted file mode 100644
index 2406bde..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ /dev/null
@@ -1,155 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.graphics.Point
-import android.tools.NavBar
-import android.tools.Rotation
-import android.tools.helpers.WindowUtils
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class SwitchAppByDoubleTapDivider
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        val overview = tapl.workspace.switchToOverview()
-        if (overview.hasTasks()) {
-            overview.dismissAllTasks()
-        }
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-    }
-
-    @Test
-    open fun switchAppByDoubleTapDivider() {
-        SplitScreenUtils.doubleTapDividerToSwitch(device)
-        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
-
-        waitForLayersToSwitch(wmHelper)
-        waitForWindowsToSwitch(wmHelper)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-
-    private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
-        wmHelper
-            .StateSyncBuilder()
-            .add("appWindowsSwitched") {
-                val primaryAppWindow =
-                    it.wmState.visibleWindows.firstOrNull { window ->
-                        primaryApp.windowMatchesAnyOf(window)
-                    }
-                        ?: return@add false
-                val secondaryAppWindow =
-                    it.wmState.visibleWindows.firstOrNull { window ->
-                        secondaryApp.windowMatchesAnyOf(window)
-                    }
-                        ?: return@add false
-
-                if (isLandscape(rotation)) {
-                    return@add if (isTablet()) {
-                        secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
-                    } else {
-                        primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
-                    }
-                } else {
-                    return@add if (isTablet()) {
-                        primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
-                    } else {
-                        primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
-                    }
-                }
-            }
-            .waitForAndVerify()
-    }
-
-    private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
-        wmHelper
-            .StateSyncBuilder()
-            .add("appLayersSwitched") {
-                val primaryAppLayer =
-                    it.layerState.visibleLayers.firstOrNull { window ->
-                        primaryApp.layerMatchesAnyOf(window)
-                    }
-                        ?: return@add false
-                val secondaryAppLayer =
-                    it.layerState.visibleLayers.firstOrNull { window ->
-                        secondaryApp.layerMatchesAnyOf(window)
-                    }
-                        ?: return@add false
-
-                val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
-                val secondaryVisibleRegion =
-                    secondaryAppLayer.visibleRegion?.bounds ?: return@add false
-
-                if (isLandscape(rotation)) {
-                    return@add if (isTablet()) {
-                        secondaryVisibleRegion.right <= primaryVisibleRegion.left
-                    } else {
-                        primaryVisibleRegion.right <= secondaryVisibleRegion.left
-                    }
-                } else {
-                    return@add if (isTablet()) {
-                        primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
-                    } else {
-                        primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
-                    }
-                }
-            }
-            .waitForAndVerify()
-    }
-
-    private fun isLandscape(rotation: Rotation): Boolean {
-        val displayBounds = WindowUtils.getDisplayBounds(rotation)
-        return displayBounds.width() > displayBounds.height()
-    }
-
-    private fun isTablet(): Boolean {
-        val sizeDp: Point = device.displaySizeDp
-        val LARGE_SCREEN_DP_THRESHOLD = 600
-        return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
deleted file mode 100644
index de26982..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class SwitchBackToSplitFromAnotherApp
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-    private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-
-        thirdApp.launchViaIntent(wmHelper)
-        wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
-    }
-
-    @Test
-    open fun switchBackToSplitFromAnotherApp() {
-        tapl.launchedAppState.quickSwitchToPreviousApp()
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
deleted file mode 100644
index 873b019..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class SwitchBackToSplitFromHome
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-
-        tapl.goHome()
-        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
-    }
-
-    @Test
-    open fun switchBackToSplitFromHome() {
-        tapl.workspace.quickSwitchToPreviousApp()
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
deleted file mode 100644
index 15934d0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ /dev/null
@@ -1,71 +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.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class SwitchBackToSplitFromRecent
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.workspace.switchToOverview().dismissAllTasks()
-
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-
-        tapl.goHome()
-        wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
-    }
-
-    @Test
-    open fun switchBackToSplitFromRecent() {
-        tapl.workspace.switchToOverview().currentTask.open()
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
deleted file mode 100644
index 79e69ae..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class SwitchBetweenSplitPairs
-@JvmOverloads
-constructor(val rotation: Rotation = Rotation.ROTATION_0) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-    private val thirdApp = SplitScreenUtils.getIme(instrumentation)
-    private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
-
-    @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(rotation.value)
-
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
-        SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp, rotation)
-        SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
-    }
-
-    @Test
-    open fun switchBetweenSplitPairs() {
-        tapl.launchedAppState.quickSwitchToPreviousApp()
-        SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-        thirdApp.exit(wmHelper)
-        fourthApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
deleted file mode 100644
index 0f932d4..0000000
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.service.splitscreen.scenarios
-
-import android.app.Instrumentation
-import android.tools.NavBar
-import android.tools.Rotation
-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.wm.shell.flicker.service.common.Utils
-import com.android.wm.shell.flicker.utils.SplitScreenUtils
-import org.junit.After
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Rule
-import org.junit.Test
-
-@Ignore("Base Test Class")
-abstract class UnlockKeyguardToSplitScreen {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val tapl = LauncherInstrumentation()
-    private val wmHelper = WindowManagerStateHelper(instrumentation)
-    private val device = UiDevice.getInstance(instrumentation)
-    private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
-    private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
-
-    @Rule
-    @JvmField
-    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
-
-    @Before
-    fun setup() {
-        tapl.setEnableRotation(true)
-        tapl.setExpectedRotation(Rotation.ROTATION_0.value)
-
-        SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp)
-    }
-
-    @Test
-    open fun unlockKeyguardToSplitScreen() {
-        device.sleep()
-        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
-        device.wakeUp()
-        device.pressMenu()
-        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
-    }
-
-    @After
-    fun teardown() {
-        primaryApp.exit(wmHelper)
-        secondaryApp.exit(wmHelper)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
deleted file mode 100644
index 35b2f56..0000000
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp
+++ /dev/null
@@ -1,156 +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 {
-    // 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"],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenBase-src",
-    srcs: [
-        "src/**/benchmark/*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup1-src",
-    srcs: [
-        "src/**/A*.kt",
-        "src/**/B*.kt",
-        "src/**/C*.kt",
-        "src/**/D*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup2-src",
-    srcs: [
-        "src/**/E*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroup3-src",
-    srcs: [
-        "src/**/S*.kt",
-    ],
-}
-
-filegroup {
-    name: "WMShellFlickerTestsSplitScreenGroupOther-src",
-    srcs: [
-        "src/**/*.kt",
-    ],
-}
-
-java_library {
-    name: "WMShellFlickerTestsSplitScreenBase",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenBase-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "wm-shell-flicker-utils",
-        "androidx.test.ext.junit",
-        "flickertestapplib",
-        "flickerlib",
-        "flickerlib-helpers",
-        "flickerlib-trace_processor_shell",
-        "platform-test-annotations",
-        "wm-flicker-common-app-helpers",
-        "wm-flicker-common-assertions",
-        "launcher-helper-lib",
-        "launcher-aosp-tapl",
-    ],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup1",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup2",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroup3",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup3-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
-
-android_test {
-    name: "WMShellFlickerTestsSplitScreenGroupOther",
-    defaults: ["WMShellFlickerTestsDefault"],
-    manifest: "AndroidManifest.xml",
-    package_name: "com.android.wm.shell.flicker.splitscreen",
-    instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen",
-    test_config_template: "AndroidTestTemplate.xml",
-    srcs: [
-        ":WMShellFlickerTestsSplitScreenGroupOther-src",
-    ],
-    exclude_srcs: [
-        ":WMShellFlickerTestsSplitScreenGroup1-src",
-        ":WMShellFlickerTestsSplitScreenGroup2-src",
-        ":WMShellFlickerTestsSplitScreenGroup3-src",
-    ],
-    static_libs: [
-        "WMShellFlickerTestsBase",
-        "WMShellFlickerTestsSplitScreenBase",
-    ],
-    data: ["trace_config/*"],
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
deleted file mode 100644
index 4bd9ca0..0000000
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/res/xml/network_security_config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<network-security-config>
-    <domain-config cleartextTrafficPermitted="true">
-        <domain includeSubdomains="true">localhost</domain>
-    </domain-config>
-</network-security-config>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt
deleted file mode 100644
index dad5db9..0000000
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.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.wm.shell.flicker.splitscreen
-
-import android.platform.test.annotations.Presubmit
-import android.tools.Rotation
-import android.tools.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.flicker.legacy.FlickerBuilder
-import android.tools.flicker.legacy.LegacyFlickerTest
-import android.tools.flicker.legacy.LegacyFlickerTestFactory
-import android.tools.traces.component.ComponentNameMatcher
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark
-import com.android.wm.shell.flicker.utils.ICommonAssertions
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test quick switch between two split pairs.
- *
- * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
[email protected](FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) :
-        MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions {
-    override val transition: FlickerBuilder.() -> Unit
-        get() = {
-            defaultSetup(this)
-            defaultTeardown(this)
-            thisTransition(this)
-        }
-
-    @Presubmit
-    @Test
-    fun imeLayerAlwaysVisible() =
-            flicker.assertLayers {
-                this.isVisible(ComponentNameMatcher.IME)
-            }
-
-    companion object {
-        @Parameterized.Parameters(name = "{0}")
-        @JvmStatic
-        fun getParams() = LegacyFlickerTestFactory.nonRotationTests(
-                supportedRotations = listOf(Rotation.ROTATION_0)
-        )
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
deleted file mode 100644
index d349988..0000000
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ /dev/null
@@ -1,130 +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.wm.shell.flicker.splitscreen
-
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
-import android.tools.NavBar
-import android.tools.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.flicker.legacy.FlickerBuilder
-import android.tools.flicker.legacy.LegacyFlickerTest
-import android.tools.flicker.legacy.LegacyFlickerTestFactory
-import android.tools.flicker.subject.layers.LayersTraceSubject
-import android.tools.flicker.subject.region.RegionSubject
-import android.tools.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.splitscreen.benchmark.UnlockKeyguardToSplitScreenBenchmark
-import com.android.wm.shell.flicker.utils.ICommonAssertions
-import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
-import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.utils.layerIsVisibleAtEnd
-import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test unlocking insecure keyguard to back to split screen tasks and verify the transition
- * behavior.
- *
- * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
- */
-@RequiresDevice
-@FlakyTest(bugId = 293578017)
-@RunWith(Parameterized::class)
[email protected](FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
-    UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
-    /** {@inheritDoc} */
-    override val transition: FlickerBuilder.() -> Unit
-        get() = {
-            defaultTeardown(this)
-            thisTransition(this)
-        }
-
-    @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
-    // TODO(b/293578017) remove once that bug is resolve
-    @Test
-    @Presubmit
-    fun visibleLayersShownMoreThanOneConsecutiveEntry_withoutWallpaper() =
-        flicker.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
-                    listOf(WALLPAPER_BBQ_WRAPPER)
-            )
-        }
-
-    @Test
-    fun splitScreenDividerIsVisibleAtEnd() {
-        flicker.assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
-    }
-
-    @Test fun primaryAppLayerIsVisibleAtEnd() = flicker.layerIsVisibleAtEnd(primaryApp)
-
-    @Test
-    fun primaryAppBoundsIsVisibleAtEnd() =
-        flicker.splitAppLayerBoundsIsVisibleAtEnd(
-            primaryApp,
-            landscapePosLeft = false,
-            portraitPosTop = false
-        )
-
-    @Test
-    fun secondaryAppBoundsIsVisibleAtEnd() =
-        flicker.splitAppLayerBoundsIsVisibleAtEnd(
-            secondaryApp,
-            landscapePosLeft = true,
-            portraitPosTop = true
-        )
-
-    @Test fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
-
-    @Test fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
-
-    @Test
-    fun notOverlapsForPrimaryAndSecondaryAppLayers() {
-        flicker.assertLayers {
-            this.invoke("notOverlapsForPrimaryAndSecondaryLayers") {
-                val primaryAppRegions =
-                    it.subjects
-                        .filter { subject ->
-                            subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
-                        }
-                        .mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
-
-                val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
-                it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
-            }
-        }
-    }
-
-    companion object {
-        @Parameterized.Parameters(name = "{0}")
-        @JvmStatic
-        fun getParams() =
-            LegacyFlickerTestFactory.nonRotationTests(
-                supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
-            )
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
index 4465a16..acaf021 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/ICommonAssertions.kt
@@ -28,12 +28,16 @@
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
+import com.android.wm.shell.Flags
 import org.junit.Assume
 import org.junit.Test
 
 interface ICommonAssertions {
     val flicker: LegacyFlickerTest
 
+    val usesTaskbar: Boolean
+        get() = flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()
+
     /** Checks that all parts of the screen are covered during the transition */
     @Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered()
 
@@ -43,7 +47,7 @@
     @Presubmit
     @Test
     fun navBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerIsVisibleAtStartAndEnd()
     }
 
@@ -54,7 +58,7 @@
     @Presubmit
     @Test
     fun navBarLayerPositionAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerPositionAtStartAndEnd()
     }
 
@@ -66,7 +70,7 @@
     @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarWindowIsAlwaysVisible()
     }
 
@@ -76,7 +80,7 @@
     @Presubmit
     @Test
     fun taskBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarLayerIsVisibleAtStartAndEnd()
     }
 
@@ -88,7 +92,7 @@
     @Presubmit
     @Test
     fun taskBarWindowIsAlwaysVisible() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarWindowIsAlwaysVisible()
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index a040865..006a4a9 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -44,6 +44,8 @@
         "androidx.test.runner",
         "androidx.test.rules",
         "androidx.test.ext.junit",
+        "androidx.datastore_datastore",
+        "kotlinx_coroutines_test",
         "androidx.dynamicanimation_dynamicanimation",
         "dagger2",
         "frameworks-base-testutils",
@@ -92,3 +94,10 @@
         "com.android.wm.shell.tests",
     ],
 }
+
+test_module_config {
+    name: "WMShellUnitTests_shell_back",
+    base: "WMShellUnitTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.wm.shell.back"],
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 4998702..f31722d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -19,6 +19,7 @@
 import com.android.wm.shell.common.ShellExecutor;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Really basic test executor. It just gathers all events in a blob. The only option is to
@@ -52,4 +53,9 @@
             mRunnables.remove(0).run();
         }
     }
+
+    /** Returns the list of callbacks for this executor. */
+    public List<Runnable> getCallbacks() {
+        return mRunnables;
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 56fad95..1e4b8b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -16,8 +16,19 @@
 
 package com.android.wm.shell.back;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
 import static android.window.BackNavigationInfo.KEY_NAVIGATION_FINISHED;
+import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,6 +36,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
@@ -32,6 +44,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.app.ActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.pm.ApplicationInfo;
@@ -40,6 +53,7 @@
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.provider.Settings;
@@ -51,11 +65,16 @@
 import android.view.MotionEvent;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
+import android.view.WindowManager;
 import android.window.BackEvent;
 import android.window.BackMotionEvent;
 import android.window.BackNavigationInfo;
 import android.window.IBackAnimationFinishedCallback;
 import android.window.IOnBackInvokedCallback;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
@@ -64,10 +83,10 @@
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.shared.ShellSharedConstants;
 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.sysui.ShellSharedConstants;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
@@ -128,6 +147,8 @@
     private ShellBackAnimationRegistry mShellBackAnimationRegistry;
     private Rect mTouchableRegion;
 
+    private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -165,6 +186,8 @@
         mShellExecutor.flushAll();
         mTouchableRegion = new Rect(0, 0, 100, 100);
         mController.mTouchableArea.set(mTouchableRegion);
+        mBackTransitionHandler = mController.mBackTransitionHandler;
+        spyOn(mBackTransitionHandler);
     }
 
     private void createNavigationInfo(int backType,
@@ -606,6 +629,198 @@
                 mCrossTaskBackAnimation.getRunner());
     }
 
+    @Test
+    public void testCloseAsExpectTransition() {
+        final int openTaskId = 1;
+        final int closeTaskId = 2;
+        mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+        final IBinder mockBinder = mock(IBinder.class);
+        final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+        // Single close
+        final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+                FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+        final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+                FLAG_BACK_GESTURE_ANIMATED);
+
+        TransitionInfo tInfo = createTransitionInfo(TRANSIT_CLOSE, open, close);
+        mBackTransitionHandler.mCloseTransitionRequested = true;
+        Transitions.TransitionFinishCallback callback =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+        verify(mBackTransitionHandler).handleCloseTransition(
+                eq(tInfo), eq(st), eq(ft), eq(callback));
+        mBackTransitionHandler.onAnimationFinished();
+        verify(callback).onTransitionFinished(any());
+        mBackTransitionHandler.mCloseTransitionRequested = false;
+
+        // PREPARE + CLOSE
+        tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+        callback = mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+        verify(mBackTransitionHandler).handlePrepareTransition(
+                eq(tInfo), eq(st), eq(ft), eq(callback));
+        mBackTransitionHandler.mCloseTransitionRequested = true;
+        TransitionInfo tInfo2 = createTransitionInfo(TRANSIT_CLOSE, close);
+        Transitions.TransitionFinishCallback mergeCallback =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.mergeAnimation(
+                mock(IBinder.class), tInfo2, st, mock(IBinder.class), mergeCallback);
+        mBackTransitionHandler.onAnimationFinished();
+        verify(callback).onTransitionFinished(any());
+        verify(mergeCallback).onTransitionFinished(any());
+        mBackTransitionHandler.mCloseTransitionRequested = false;
+
+        // PREPARE contains close info
+        tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open, close);
+        callback = mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.mCloseTransitionRequested = true;
+        mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+        verify(mBackTransitionHandler).handleCloseTransition(
+                eq(tInfo), eq(st), eq(ft), eq(callback));
+        mBackTransitionHandler.onAnimationFinished();
+        verify(callback).onTransitionFinished(any());
+        mBackTransitionHandler.mCloseTransitionRequested = false;
+
+        // PREPARE then Cancel
+        tInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+        callback = mock(Transitions.TransitionFinishCallback.class);
+        final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+                TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+                null /* remoteTransition */);
+        mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+        mBackTransitionHandler.startAnimation(mockBinder, tInfo, st, ft, callback);
+        verify(mBackTransitionHandler).handlePrepareTransition(
+                eq(tInfo), eq(st), eq(ft), eq(callback));
+        final TransitionInfo.Change openToClose = createAppChange(openTaskId, TRANSIT_CLOSE,
+                FLAG_BACK_GESTURE_ANIMATED);
+        tInfo2 = createTransitionInfo(TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, openToClose);
+        mBackTransitionHandler.mClosePrepareTransition = mock(IBinder.class);
+        mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.mergeAnimation(mBackTransitionHandler.mClosePrepareTransition,
+                tInfo2, st, mock(IBinder.class), mergeCallback);
+        assertTrue("Change should be consumed", tInfo2.getChanges().isEmpty());
+        mBackTransitionHandler.onAnimationFinished();
+        verify(callback).onTransitionFinished(any());
+    }
+
+    @Test
+    public void testCancelUnexpectedTransition() {
+        final int openTaskId = 1;
+        final int closeTaskId = 2;
+        mController.mApps = createAppAnimationTargets(openTaskId, closeTaskId);
+        final IBinder mockBinder = mock(IBinder.class);
+        final SurfaceControl.Transaction st = mock(SurfaceControl.Transaction.class);
+        final SurfaceControl.Transaction ft = mock(SurfaceControl.Transaction.class);
+        final TransitionInfo.Change open = createAppChange(openTaskId, TRANSIT_OPEN,
+                FLAG_BACK_GESTURE_ANIMATED | FLAG_MOVED_TO_TOP);
+        final TransitionInfo.Change close = createAppChange(closeTaskId, TRANSIT_CLOSE,
+                FLAG_BACK_GESTURE_ANIMATED);
+
+        // Didn't trigger close transition
+        mBackTransitionHandler.mCloseTransitionRequested = false;
+        TransitionInfo prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION,
+                open, close);
+        final Transitions.TransitionFinishCallback callback =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.handleRequest(mockBinder, mock(TransitionRequestInfo.class));
+        boolean canHandle = mBackTransitionHandler.startAnimation(
+                mockBinder, prepareInfo, st, ft, callback);
+        assertFalse("Should not handle transition", canHandle);
+        assertNull(mBackTransitionHandler.mOnAnimationFinishCallback);
+
+        // Didn't trigger close transition, but receive close target.
+        final TransitionRequestInfo requestInfo = new TransitionRequestInfo(
+                TRANSIT_PREPARE_BACK_NAVIGATION, null /* triggerTask */,
+                null /* remoteTransition */);
+        prepareInfo = createTransitionInfo(TRANSIT_PREPARE_BACK_NAVIGATION, open);
+        final Transitions.TransitionFinishCallback callback2 =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+        canHandle = mBackTransitionHandler.startAnimation(mockBinder,
+                prepareInfo, st, ft, callback2);
+        assertTrue("Handle prepare transition" , canHandle);
+        verify(mBackTransitionHandler).handlePrepareTransition(
+                eq(prepareInfo), eq(st), eq(ft), eq(callback2));
+        final TransitionInfo closeInfo = createTransitionInfo(TRANSIT_CLOSE, close);
+        Transitions.TransitionFinishCallback mergeCallback =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), closeInfo, ft,
+                mock(IBinder.class), mergeCallback);
+        verify(callback2).onTransitionFinished(any());
+        verify(mergeCallback, never()).onTransitionFinished(any());
+
+        // Didn't trigger close transition, but contains open target.
+        final int openTaskId2 = 3;
+        final Transitions.TransitionFinishCallback callback3 =
+                mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.handleRequest(mockBinder, requestInfo);
+        canHandle = mBackTransitionHandler.startAnimation(
+                mockBinder, prepareInfo, st, ft, callback3);
+        assertTrue("Handle prepare transition" , canHandle);
+        verify(mBackTransitionHandler).handlePrepareTransition(
+                eq(prepareInfo), eq(st), eq(ft), eq(callback3));
+        final TransitionInfo.Change open2 = createAppChange(
+                openTaskId2, TRANSIT_OPEN, FLAG_MOVED_TO_TOP);
+        final TransitionInfo openInfo = createTransitionInfo(TRANSIT_OPEN, open2, close);
+        mergeCallback = mock(Transitions.TransitionFinishCallback.class);
+        mBackTransitionHandler.mergeAnimation(mock(IBinder.class), openInfo, ft,
+                mock(IBinder.class), mergeCallback);
+        verify(callback3).onTransitionFinished(any());
+        verify(mergeCallback, never()).onTransitionFinished(any());
+    }
+
+    private RemoteAnimationTarget[] createAppAnimationTargets(int openTaskId, int closeTaskId) {
+        final RemoteAnimationTarget openT = createSingleAnimationTarget(openTaskId,
+                RemoteAnimationTarget.MODE_OPENING);
+        final RemoteAnimationTarget closeT = createSingleAnimationTarget(closeTaskId,
+                RemoteAnimationTarget.MODE_CLOSING);
+        return new RemoteAnimationTarget[]{openT, closeT};
+    }
+
+    private RemoteAnimationTarget createSingleAnimationTarget(int taskId, int mode) {
+        final Rect fakeR = new Rect();
+        final Point fakeP = new Point();
+        final ActivityManager.RunningTaskInfo openTaskInfo = new ActivityManager.RunningTaskInfo();
+        openTaskInfo.taskId = taskId;
+        openTaskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+        return new RemoteAnimationTarget(
+                taskId, mode, mock(SurfaceControl.class), false, fakeR, fakeR,
+                0, fakeP, fakeR, fakeR, new WindowConfiguration(), false,
+                mock(SurfaceControl.class), fakeR, openTaskInfo, false);
+    }
+    private TransitionInfo.Change createAppChange(
+            int taskId, @TransitionInfo.TransitionMode int mode,
+            @TransitionInfo.ChangeFlags int flags) {
+        final TransitionInfo.Change change;
+        SurfaceControl.Builder b = new SurfaceControl.Builder()
+                .setName("test task");
+        if (taskId != INVALID_TASK_ID) {
+            final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+            taskInfo.taskId = taskId;
+            taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+            change = new TransitionInfo.Change(
+                    taskInfo.token, b.build());
+            change.setTaskInfo(taskInfo);
+        } else {
+            change = new TransitionInfo.Change(
+                null, b.build());
+
+        }
+        change.setMode(mode);
+        change.setFlags(flags);
+        return change;
+    }
+
+    private static TransitionInfo createTransitionInfo(
+            @WindowManager.TransitionType int type, TransitionInfo.Change ... changes) {
+        final TransitionInfo info = new TransitionInfo(type, 0);
+        for (int i = 0; i < changes.length; ++i) {
+            info.addChange(changes[i]);
+        }
+        return info;
+    }
+
     private void verifySystemBackBehavior(int type, BackAnimationRunner animation)
             throws RemoteException {
         final BackAnimationRunner animationRunner = spy(animation);
@@ -666,7 +881,7 @@
         RemoteAnimationTarget[] targets = new RemoteAnimationTarget[]{animationTarget};
         if (mController.mBackAnimationAdapter != null) {
             mController.mBackAnimationAdapter.getRunner().onAnimationStart(
-                    targets, null, null, mBackAnimationFinishedCallback);
+                    targets, null /* prepareOpenTransition */, mBackAnimationFinishedCallback);
             mShellExecutor.flushAll();
         }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
index e359957..9ec62c9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
@@ -124,6 +124,7 @@
 
     private val testHandler = Handler(Looper.getMainLooper())
     private val mainExecutor = HandlerExecutor(testHandler)
+    private val bgExecutor = HandlerExecutor(testHandler)
     private val launcherApps = mock<LauncherApps>()
 
     private val persistedBubbles = SparseArray<List<BubbleEntity>>()
@@ -134,7 +135,8 @@
     @Before
     fun setup() {
         persistentRepository = BubblePersistentRepository(mContext)
-        dataRepository = spy(BubbleDataRepository(launcherApps, mainExecutor, persistentRepository))
+        dataRepository =
+            spy(BubbleDataRepository(launcherApps, mainExecutor, bgExecutor, persistentRepository))
 
         persistedBubbles.put(0, user0BubbleEntities)
         persistedBubbles.put(1, user1BubbleEntities)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index c138a24..6fa3788 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -50,8 +50,8 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.bubbles.BubbleData.TimeSource;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 
 import com.google.common.collect.ImmutableList;
 
@@ -117,6 +117,8 @@
     private BubbleEducationController mEducationController;
     @Mock
     private ShellExecutor mMainExecutor;
+    @Mock
+    private ShellExecutor mBgExecutor;
 
     @Captor
     private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -144,47 +146,47 @@
         when(ranking.isTextChanged()).thenReturn(true);
         mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
         mBubbleInterruptive = new Bubble(mEntryInterruptive, mBubbleMetadataFlagListener, null,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
         mBubbleDismissed = new Bubble(mEntryDismissed, mBubbleMetadataFlagListener, null,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         mEntryLocusId = createBubbleEntry(1, "keyLocus", "package.e", null,
                 new LocusId("locusId1"));
         mBubbleLocusId = new Bubble(mEntryLocusId,
                 mBubbleMetadataFlagListener,
                 null /* pendingIntentCanceledListener */,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         mBubbleA1 = new Bubble(mEntryA1,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleA2 = new Bubble(mEntryA2,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleA3 = new Bubble(mEntryA3,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleB1 = new Bubble(mEntryB1,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleB2 = new Bubble(mEntryB2,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleB3 = new Bubble(mEntryB3,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
         mBubbleC1 = new Bubble(mEntryC1,
                 mBubbleMetadataFlagListener,
                 mPendingIntentCanceledListener,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         Intent appBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
         appBubbleIntent.setPackage(mContext.getPackageName());
@@ -192,12 +194,12 @@
                 appBubbleIntent,
                 new UserHandle(1),
                 mock(Icon.class),
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         mPositioner = new TestableBubblePositioner(mContext,
                 mContext.getSystemService(WindowManager.class));
         mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController,
-                mMainExecutor);
+                mMainExecutor, mBgExecutor);
 
         // Used by BubbleData to set lastAccessedTime
         when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
index afec1ee..dca5fc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTest.java
@@ -43,7 +43,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.bubbles.BubbleInfo;
+import com.android.wm.shell.shared.bubbles.BubbleInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -61,6 +61,8 @@
     private StatusBarNotification mSbn;
     @Mock
     private ShellExecutor mMainExecutor;
+    @Mock
+    private ShellExecutor mBgExecutor;
 
     private BubbleEntry mBubbleEntry;
     private Bundle mExtras;
@@ -85,7 +87,8 @@
         when(mNotif.getBubbleMetadata()).thenReturn(metadata);
         when(mSbn.getKey()).thenReturn("mock");
         mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
-        mBubble = new Bubble(mBubbleEntry, mBubbleMetadataFlagListener, null, mMainExecutor);
+        mBubble = new Bubble(mBubbleEntry, mBubbleMetadataFlagListener, null, mMainExecutor,
+                mBgExecutor);
     }
 
     @Test
@@ -176,7 +179,8 @@
 
     @Test
     public void testBubbleIsConversation_hasNoShortcut() {
-        Bubble bubble = new Bubble(mBubbleEntry, mBubbleMetadataFlagListener, null, mMainExecutor);
+        Bubble bubble = new Bubble(mBubbleEntry, mBubbleMetadataFlagListener, null, mMainExecutor,
+                mBgExecutor);
         assertThat(bubble.getShortcutInfo()).isNull();
         assertThat(bubble.isConversation()).isFalse();
     }
@@ -199,7 +203,7 @@
         Intent intent = new Intent(mContext, BubblesTestActivity.class);
         intent.setPackage(mContext.getPackageName());
         Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1 /* userId */),
-                null /* icon */, mMainExecutor);
+                null /* icon */, mMainExecutor, mBgExecutor);
         BubbleInfo bubbleInfo = bubble.asBubbleBarBubble();
 
         assertThat(bubble.getShortcutInfo()).isNull();
@@ -215,6 +219,6 @@
                 .build();
         return new Bubble("mockKey", shortcutInfo, 10, Resources.ID_NULL,
                 "mockTitle", 0 /* taskId */, "mockLocus", true /* isDismissible */,
-                mMainExecutor, mBubbleMetadataFlagListener);
+                mMainExecutor, mBgExecutor, mBubbleMetadataFlagListener);
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
index 4a4c5e8..4ac066e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt
@@ -70,10 +70,10 @@
     private lateinit var bubble: Bubble
     private lateinit var bubbleController: BubbleController
     private lateinit var mainExecutor: ShellExecutor
+    private lateinit var bgExecutor: ShellExecutor
     private lateinit var bubbleStackView: BubbleStackView
     private lateinit var bubbleBarLayerView: BubbleBarLayerView
     private lateinit var bubblePositioner: BubblePositioner
-    private lateinit var expandedViewManager: BubbleExpandedViewManager
 
     private val bubbleTaskViewFactory = BubbleTaskViewFactory {
         BubbleTaskView(mock<TaskView>(), mock<Executor>())
@@ -92,6 +92,7 @@
             )
 
         mainExecutor = TestShellExecutor()
+        bgExecutor = TestShellExecutor()
         val windowManager = context.getSystemService(WindowManager::class.java)
         val shellInit = ShellInit(mainExecutor)
         val shellCommandHandler = ShellCommandHandler()
@@ -104,7 +105,8 @@
                 mock<BubbleLogger>(),
                 bubblePositioner,
                 BubbleEducationController(context),
-                mainExecutor
+                mainExecutor,
+                bgExecutor
             )
         val surfaceSynchronizer = { obj: Runnable -> obj.run() }
 
@@ -132,7 +134,7 @@
                 null,
                 mainExecutor,
                 mock<Handler>(),
-                mock<ShellExecutor>(),
+                bgExecutor,
                 mock<TaskViewTransitions>(),
                 mock<Transitions>(),
                 mock<SyncTransactionQueue>(),
@@ -152,7 +154,6 @@
                 bubbleController,
                 mainExecutor
             )
-        expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
         bubbleBarLayerView = BubbleBarLayerView(context, bubbleController, bubbleData)
     }
 
@@ -162,7 +163,6 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populate(
                 context,
-                expandedViewManager,
                 bubbleTaskViewFactory,
                 bubblePositioner,
                 bubbleStackView,
@@ -190,9 +190,7 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                 context,
-                expandedViewManager,
                 bubbleTaskViewFactory,
-                bubblePositioner,
                 bubbleBarLayerView,
                 iconFactory,
                 bubble,
@@ -226,9 +224,7 @@
         val info =
             BubbleViewInfoTask.BubbleViewInfo.populateForBubbleBar(
                 context,
-                expandedViewManager,
                 bubbleTaskViewFactory,
-                bubblePositioner,
                 bubbleBarLayerView,
                 iconFactory,
                 bubble,
@@ -256,7 +252,7 @@
             "mockLocus",
             true /* isDismissible */,
             mainExecutor,
-            metadataFlagListener
-        )
+            bgExecutor,
+            metadataFlagListener)
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 654d7a8e..f8f0db9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -43,6 +43,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
deleted file mode 100644
index 27e0b19..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleBarLocationTest.kt
+++ /dev/null
@@ -1,53 +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.wm.shell.common.bubbles
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.DEFAULT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
-import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class BubbleBarLocationTest : ShellTestCase() {
-
-    @Test
-    fun isOnLeft_rtlEnabled_defaultsToLeft() {
-        assertThat(DEFAULT.isOnLeft(isRtl = true)).isTrue()
-    }
-
-    @Test
-    fun isOnLeft_rtlDisabled_defaultsToRight() {
-        assertThat(DEFAULT.isOnLeft(isRtl = false)).isFalse()
-    }
-
-    @Test
-    fun isOnLeft_left_trueForAllLanguageDirections() {
-        assertThat(LEFT.isOnLeft(isRtl = false)).isTrue()
-        assertThat(LEFT.isOnLeft(isRtl = true)).isTrue()
-    }
-
-    @Test
-    fun isOnLeft_right_falseForAllLanguageDirections() {
-        assertThat(RIGHT.isOnLeft(isRtl = false)).isFalse()
-        assertThat(RIGHT.isOnLeft(isRtl = true)).isFalse()
-    }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
deleted file mode 100644
index 5b22edd..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/bubbles/BubbleInfoTest.kt
+++ /dev/null
@@ -1,63 +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.wm.shell.common.bubbles
-
-import android.os.Parcel
-import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class BubbleInfoTest : ShellTestCase() {
-
-    @Test
-    fun bubbleInfo() {
-        val bubbleInfo =
-            BubbleInfo(
-                "key",
-                0,
-                "shortcut id",
-                null,
-                6,
-                "com.some.package",
-                "title",
-                "Some app",
-                true
-            )
-        val parcel = Parcel.obtain()
-        bubbleInfo.writeToParcel(parcel, PARCELABLE_WRITE_RETURN_VALUE)
-        parcel.setDataPosition(0)
-
-        val bubbleInfoFromParcel = BubbleInfo.CREATOR.createFromParcel(parcel)
-
-        assertThat(bubbleInfo.key).isEqualTo(bubbleInfoFromParcel.key)
-        assertThat(bubbleInfo.flags).isEqualTo(bubbleInfoFromParcel.flags)
-        assertThat(bubbleInfo.shortcutId).isEqualTo(bubbleInfoFromParcel.shortcutId)
-        assertThat(bubbleInfo.icon).isEqualTo(bubbleInfoFromParcel.icon)
-        assertThat(bubbleInfo.userId).isEqualTo(bubbleInfoFromParcel.userId)
-        assertThat(bubbleInfo.packageName).isEqualTo(bubbleInfoFromParcel.packageName)
-        assertThat(bubbleInfo.title).isEqualTo(bubbleInfoFromParcel.title)
-        assertThat(bubbleInfo.appName).isEqualTo(bubbleInfoFromParcel.appName)
-        assertThat(bubbleInfo.isImportantConversation)
-            .isEqualTo(bubbleInfoFromParcel.isImportantConversation)
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
deleted file mode 100644
index 8bb182d..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ /dev/null
@@ -1,526 +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.wm.shell.common.magnetictarget
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.view.MotionEvent
-import android.view.View
-import androidx.dynamicanimation.animation.FloatPropertyCompat
-import androidx.test.filters.SmallTest
-import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
-import org.junit.Assert.assertEquals
-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
-import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyFloat
-import org.mockito.Mockito
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-
[email protected]
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-class MagnetizedObjectTest : ShellTestCase() {
-    /** Incrementing value for fake MotionEvent timestamps. */
-    private var time = 0L
-
-    /** Value to add to each new MotionEvent's timestamp. */
-    private var timeStep = 100
-
-    private val underlyingObject = this
-
-    private lateinit var targetView: View
-
-    private val targetSize = 200
-    private val targetCenterX = 500
-    private val targetCenterY = 900
-    private val magneticFieldRadius = 200
-
-    private var objectX = 0f
-    private var objectY = 0f
-    private val objectSize = 50f
-
-    private lateinit var magneticTarget: MagnetizedObject.MagneticTarget
-    private lateinit var magnetizedObject: MagnetizedObject<*>
-    private lateinit var magnetListener: MagnetizedObject.MagnetListener
-
-    private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
-        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
-            objectX = value
-        }
-        override fun getValue(target: MagnetizedObjectTest?): Float {
-            return objectX
-        }
-    }
-
-    private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
-        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
-            objectY = value
-        }
-
-        override fun getValue(target: MagnetizedObjectTest?): Float {
-            return objectY
-        }
-    }
-
-    @Before
-    fun setup() {
-        PhysicsAnimatorTestUtils.prepareForTest()
-
-        // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached
-        // to a real window (it'll always return x = 0, y = 0).
-        targetView = mock(View::class.java)
-        `when`(targetView.context).thenReturn(context)
-
-        // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's
-        // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900).
-        `when`(targetView.width).thenReturn(targetSize) // width = 200
-        `when`(targetView.height).thenReturn(targetSize) // height = 200
-        doAnswer { invocation ->
-            (invocation.arguments[0] as IntArray).also { location ->
-                // Return the top left of the target.
-                location[0] = targetCenterX - targetSize / 2 // x = 400
-                location[1] = targetCenterY - targetSize / 2 // y = 800
-            }
-        }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
-        doAnswer { invocation ->
-            (invocation.arguments[0] as Runnable).run()
-            true
-        }.`when`(targetView).post(ArgumentMatchers.any())
-        `when`(targetView.context).thenReturn(context)
-
-        magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
-
-        magnetListener = mock(MagnetizedObject.MagnetListener::class.java)
-        magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>(
-                context, underlyingObject, xProperty, yProperty) {
-            override fun getWidth(underlyingObject: MagnetizedObjectTest): Float {
-                return objectSize
-            }
-
-            override fun getHeight(underlyingObject: MagnetizedObjectTest): Float {
-                return objectSize
-            }
-
-            override fun getLocationOnScreen(
-                underlyingObject: MagnetizedObjectTest,
-                loc: IntArray
-            ) {
-                loc[0] = objectX.toInt()
-                loc[1] = objectY.toInt() }
-        }
-
-        magnetizedObject.magnetListener = magnetListener
-        magnetizedObject.addTarget(magneticTarget)
-
-        timeStep = 100
-    }
-
-    @Test
-    fun testMotionEventConsumption() {
-        // Start at (0, 0). No magnetic field here.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 0, y = 0, action = MotionEvent.ACTION_DOWN)))
-
-        // Move to (400, 400), which is solidly outside the magnetic field.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 200, y = 200)))
-
-        // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were
-        // square. It's not, because they're not.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX - magneticFieldRadius + 5,
-                y = targetCenterY - magneticFieldRadius + 5)))
-
-        // Move to (400, 800). That's solidly in the radius so the magnetic target should begin
-        // consuming events.
-        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX - 100,
-                y = targetCenterY - 100)))
-
-        // Release at (400, 800). Since we're in the magnetic target, it should return true and
-        // consume the ACTION_UP.
-        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 400, y = 800, action = MotionEvent.ACTION_UP)))
-
-        // ACTION_DOWN outside the field.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 200, y = 200, action = MotionEvent.ACTION_DOWN)))
-
-        // Move to the center. We absolutely should consume events there.
-        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX,
-                y = targetCenterY)))
-
-        // Drag out to (0, 0) and we should be returning false again.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 0, y = 0)))
-
-        // The ACTION_UP event shouldn't be consumed either since it's outside the field.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = 0, y = 0, action = MotionEvent.ACTION_UP)))
-    }
-
-    @Test
-    fun testMotionEventConsumption_downInMagneticField() {
-        // We should not consume DOWN events even if they occur in the field.
-        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
-    }
-
-    @Test
-    fun testMoveIntoAroundAndOutOfMagneticField() {
-        // Move around but don't touch the magnetic field.
-        dispatchMotionEvents(
-                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(x = 100, y = 100),
-                getMotionEvent(x = 200, y = 200))
-
-        // You can't become unstuck if you were never stuck in the first place.
-        verify(magnetListener, never()).onStuckToTarget(magneticTarget,
-                magnetizedObject)
-        verify(magnetListener, never()).onUnstuckFromTarget(
-                eq(magneticTarget), eq(magnetizedObject),
-                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
-                eq(false))
-
-        // Move into and then around inside the magnetic field.
-        dispatchMotionEvents(
-                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
-                getMotionEvent(x = targetCenterX, y = targetCenterY),
-                getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100))
-
-        // We should only have received one call to onStuckToTarget and none to unstuck.
-        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
-        verify(magnetListener, never()).onUnstuckFromTarget(
-                eq(magneticTarget), eq(magnetizedObject),
-                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
-                eq(false))
-
-        // Move out of the field and then release.
-        dispatchMotionEvents(
-                getMotionEvent(x = 100, y = 100),
-                getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP))
-
-        // We should have received one unstuck call and no more stuck calls. We also should never
-        // have received an onReleasedInTarget call.
-        verify(magnetListener, times(1)).onUnstuckFromTarget(
-                eq(magneticTarget), eq(magnetizedObject),
-                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
-                eq(false))
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testMoveIntoOutOfAndBackIntoMagneticField() {
-        // Move into the field
-        dispatchMotionEvents(
-                getMotionEvent(
-                        x = targetCenterX - magneticFieldRadius,
-                        y = targetCenterY - magneticFieldRadius,
-                        action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(
-                        x = targetCenterX, y = targetCenterY))
-
-        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
-        verify(magnetListener, never()).onReleasedInTarget(magneticTarget, magnetizedObject)
-
-        // Move back out.
-        dispatchMotionEvents(
-                getMotionEvent(
-                        x = targetCenterX - magneticFieldRadius,
-                        y = targetCenterY - magneticFieldRadius))
-
-        verify(magnetListener, times(1)).onUnstuckFromTarget(
-                eq(magneticTarget),
-                eq(magnetizedObject),
-                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
-                eq(false))
-        verify(magnetListener, never()).onReleasedInTarget(magneticTarget, magnetizedObject)
-
-        // Move in again and release in the magnetic field.
-        dispatchMotionEvents(
-                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
-                getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50),
-                getMotionEvent(x = targetCenterX, y = targetCenterY),
-                getMotionEvent(
-                        x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP))
-
-        verify(magnetListener, times(2)).onStuckToTarget(magneticTarget, magnetizedObject)
-        verify(magnetListener).onReleasedInTarget(magneticTarget, magnetizedObject)
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testFlingTowardsTarget_towardsTarget() {
-        timeStep = 10
-
-        // Forcefully fling the object towards the target (but never touch the magnetic field).
-        dispatchMotionEvents(
-                getMotionEvent(
-                        x = 0,
-                        y = 0,
-                        action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(
-                        x = targetCenterX / 2,
-                        y = targetCenterY / 2),
-                getMotionEvent(
-                        x = targetCenterX,
-                        y = targetCenterY - magneticFieldRadius * 2,
-                        action = MotionEvent.ACTION_UP))
-
-        // Nevertheless it should have ended up stuck to the target.
-        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
-    }
-
-    @Test
-    fun testFlingTowardsTarget_towardsButTooSlow() {
-        // Very, very slowly fling the object towards the target (but never touch the magnetic
-        // field). This value is only used to create MotionEvent timestamps, it will not block the
-        // test for 10 seconds.
-        timeStep = 10000
-        dispatchMotionEvents(
-                getMotionEvent(
-                        x = targetCenterX,
-                        y = 0,
-                        action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(
-                        x = targetCenterX,
-                        y = targetCenterY / 2),
-                getMotionEvent(
-                        x = targetCenterX,
-                        y = targetCenterY - magneticFieldRadius * 2,
-                        action = MotionEvent.ACTION_UP))
-
-        // No sticking should have occurred.
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testFlingTowardsTarget_missTarget() {
-        timeStep = 10
-        // Forcefully fling the object down, but not towards the target.
-        dispatchMotionEvents(
-                getMotionEvent(
-                        x = 0,
-                        y = 0,
-                        action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(
-                        x = 0,
-                        y = targetCenterY / 2),
-                getMotionEvent(
-                        x = 0,
-                        y = targetCenterY - magneticFieldRadius * 2,
-                        action = MotionEvent.ACTION_UP))
-
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testMagnetAnimation() {
-        // Make sure the object starts at (0, 0).
-        assertEquals(0f, objectX)
-        assertEquals(0f, objectY)
-
-        // Trigger the magnet animation, and block the test until it ends.
-        PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
-        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX - 250,
-                y = targetCenterY - 250,
-                action = MotionEvent.ACTION_DOWN))
-
-        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
-                x = targetCenterX,
-                y = targetCenterY))
-
-        // The object's (top-left) position should now position it centered over the target.
-        assertEquals(targetCenterX - objectSize / 2, objectX)
-        assertEquals(targetCenterY - objectSize / 2, objectY)
-    }
-
-    @Test
-    fun testMultipleTargets() {
-        val secondMagneticTarget = getSecondMagneticTarget()
-
-        // Drag into the second target.
-        dispatchMotionEvents(
-                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(x = 100, y = 900))
-
-        // Verify that we received an onStuck for the second target, and no others.
-        verify(magnetListener).onStuckToTarget(secondMagneticTarget, magnetizedObject)
-        verifyNoMoreInteractions(magnetListener)
-
-        // Drag into the original target.
-        dispatchMotionEvents(
-                getMotionEvent(x = 0, y = 0),
-                getMotionEvent(x = 500, y = 900))
-
-        // We should have unstuck from the second one and stuck into the original one.
-        verify(magnetListener).onUnstuckFromTarget(
-                eq(secondMagneticTarget), eq(magnetizedObject),
-                anyFloat(), anyFloat(), eq(false))
-        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testMultipleTargets_flingIntoSecond() {
-        val secondMagneticTarget = getSecondMagneticTarget()
-
-        timeStep = 10
-
-        // Fling towards the second target.
-        dispatchMotionEvents(
-                getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(x = 100, y = 350),
-                getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP))
-
-        // Verify that we received an onStuck for the second target.
-        verify(magnetListener).onStuckToTarget(secondMagneticTarget, magnetizedObject)
-
-        // Fling towards the first target.
-        dispatchMotionEvents(
-                getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN),
-                getMotionEvent(x = 400, y = 350),
-                getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP))
-
-        // Verify that we received onStuck for the original target.
-        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
-    }
-
-    @Test
-    fun testMagneticTargetHasScreenOffset_moveIntoAndReleaseInTarget() {
-        magneticTarget.screenVerticalOffset = 500
-
-        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY))
-        // Moved into the target location, but it should be shifted due to screen offset.
-        // Should not get stuck.
-        verify(magnetListener, never()).onStuckToTarget(magneticTarget, magnetizedObject)
-
-        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY + 500))
-        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
-
-        dispatchMotionEvents(
-            getMotionEvent(
-                x = targetCenterX,
-                y = targetCenterY + 500,
-                action = MotionEvent.ACTION_UP
-            )
-        )
-
-        verify(magnetListener).onReleasedInTarget(magneticTarget, magnetizedObject)
-        verifyNoMoreInteractions(magnetListener)
-    }
-
-    @Test
-    fun testMagneticTargetHasScreenOffset_screenOffsetUpdates() {
-        magneticTarget.screenVerticalOffset = 500
-        val adjustedTargetCenter = targetCenterY + 500
-
-        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = adjustedTargetCenter))
-        dispatchMotionEvents(getMotionEvent(x = 0, y = 0))
-        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
-        verify(magnetListener)
-                .onUnstuckFromTarget(eq(magneticTarget), eq(magnetizedObject),
-                        anyFloat(), anyFloat(), anyBoolean())
-
-        // Offset if removed, we should now get stuck at the target location
-        magneticTarget.screenVerticalOffset = 0
-        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY))
-        verify(magnetListener, times(2)).onStuckToTarget(magneticTarget, magnetizedObject)
-    }
-
-    @Test
-    fun testMagneticTargetHasScreenOffset_flingTowardsTarget() {
-        timeStep = 10
-
-        magneticTarget.screenVerticalOffset = 500
-        val adjustedTargetCenter = targetCenterY + 500
-
-        // Forcefully fling the object towards the target (but never touch the magnetic field).
-        dispatchMotionEvents(
-            getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
-            getMotionEvent(x = targetCenterX / 2, y = adjustedTargetCenter / 2),
-            getMotionEvent(
-                x = targetCenterX,
-                y = adjustedTargetCenter - magneticFieldRadius * 2,
-                action = MotionEvent.ACTION_UP
-            )
-        )
-
-        // Nevertheless it should have ended up stuck to the target.
-        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
-    }
-
-    private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget {
-        // The first target view is at bounds (400, 800, 600, 1000) and it has a center of
-        // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900).
-        val secondTargetView = mock(View::class.java)
-        val secondTargetCenterX = 100
-        val secondTargetCenterY = 900
-
-        `when`(secondTargetView.context).thenReturn(context)
-        `when`(secondTargetView.width).thenReturn(targetSize) // width = 200
-        `when`(secondTargetView.height).thenReturn(targetSize) // height = 200
-        doAnswer { invocation ->
-            (invocation.arguments[0] as Runnable).run()
-            true
-        }.`when`(secondTargetView).post(ArgumentMatchers.any())
-        doAnswer { invocation ->
-            (invocation.arguments[0] as IntArray).also { location ->
-                // Return the top left of the target.
-                location[0] = secondTargetCenterX - targetSize / 2 // x = 0
-                location[1] = secondTargetCenterY - targetSize / 2 // y = 800
-            }
-        }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any())
-
-        return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius)
-    }
-
-    /**
-     * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default).
-     * The event's time fields will be incremented by 10ms each time this is called, so tha
-     * VelocityTracker works.
-     */
-    private fun getMotionEvent(
-        x: Int,
-        y: Int,
-        action: Int = MotionEvent.ACTION_MOVE
-    ): MotionEvent {
-        return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0)
-                .also { time += timeStep }
-    }
-
-    /** Dispatch all of the provided events to the target view. */
-    private fun dispatchMotionEvents(vararg events: MotionEvent) {
-        events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) }
-    }
-
-    /** Prevents Kotlin from being mad that eq() is nullable. */
-    private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index cfe8e07..09fcd8b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -19,9 +19,9 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
+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.google.common.truth.Truth.assertThat;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt
deleted file mode 100644
index fe26110..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenConstantsTest.kt
+++ /dev/null
@@ -1,59 +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.wm.shell.common.split
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class SplitScreenConstantsTest {
-
-    /**
-     * Ensures that some important constants are not changed from their set values. These values
-     * are persisted in user-defined app pairs, and changing them will break things.
-     */
-    @Test
-    fun shouldKeepExistingConstantValues() {
-        assertEquals(
-            "the value of SPLIT_POSITION_TOP_OR_LEFT should be 0",
-            0,
-            SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT,
-        )
-        assertEquals(
-            "the value of SPLIT_POSITION_BOTTOM_OR_RIGHT should be 1",
-            1,
-            SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT,
-        )
-        assertEquals(
-            "the value of SNAP_TO_30_70 should be 0",
-            0,
-            SplitScreenConstants.SNAP_TO_30_70,
-        )
-        assertEquals(
-            "the value of SNAP_TO_50_50 should be 1",
-            1,
-            SplitScreenConstants.SNAP_TO_50_50,
-        )
-        assertEquals(
-            "the value of SNAP_TO_70_30 should be 2",
-            2,
-            SplitScreenConstants.SNAP_TO_70_30,
-        )
-    }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index b39cf19..d5287e7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -35,9 +35,12 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.AndroidTestingRunner;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -90,6 +93,9 @@
     public final CheckFlagsRule mCheckFlagsRule =
             DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private CompatUIController mController;
     private ShellInit mShellInit;
     @Mock
@@ -122,7 +128,6 @@
     private CompatUIConfiguration mCompatUIConfiguration;
     @Mock
     private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
-
     @Mock
     private AccessibilityManager mAccessibilityManager;
 
@@ -132,6 +137,8 @@
     @NonNull
     private CompatUIStatusManager mCompatUIStatusManager;
 
+    private boolean mInDesktopModePredicateResult;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -157,7 +164,7 @@
                 mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
                 mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
                 mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager,
-                mCompatUIStatusManager) {
+                mCompatUIStatusManager, i -> mInDesktopModePredicateResult) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
@@ -685,6 +692,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(false);
@@ -695,6 +703,34 @@
                 eq(mMockTaskListener));
     }
 
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
+    @EnableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
+    public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagEnabled() {
+        mInDesktopModePredicateResult = false;
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+        mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+        verify(mController, never()).removeLayouts(taskInfo.taskId);
+
+        mInDesktopModePredicateResult = true;
+        mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+        verify(mController).removeLayouts(taskInfo.taskId);
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
+    @DisableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
+    public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagDisabled() {
+        mInDesktopModePredicateResult = false;
+        TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+        mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+        verify(mController, never()).removeLayouts(taskInfo.taskId);
+
+        mInDesktopModePredicateResult = true;
+        mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+        verify(mController, never()).removeLayouts(taskInfo.taskId);
+    }
+
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
         return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
                 /* isFocused */ false, /* isTopActivityTransparent */ false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIComponentTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIComponentTest.kt
new file mode 100644
index 0000000..2c203c4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIComponentTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.compatui.impl
+
+import android.app.ActivityManager
+import android.graphics.Point
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIComponent
+import com.android.wm.shell.compatui.api.CompatUIComponentState
+import com.android.wm.shell.compatui.api.CompatUIInfo
+import com.android.wm.shell.compatui.api.CompatUIState
+import junit.framework.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/**
+ * Tests for {@link CompatUIComponent}.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:CompatUIComponentTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class CompatUIComponentTest : ShellTestCase() {
+
+    private lateinit var component: CompatUIComponent
+    private lateinit var layout: FakeCompatUILayout
+    private lateinit var spec: FakeCompatUISpec
+    private lateinit var state: CompatUIState
+    private lateinit var info: CompatUIInfo
+    private lateinit var syncQueue: SyncTransactionQueue
+    private lateinit var displayLayout: DisplayLayout
+    private lateinit var view: View
+    private lateinit var position: Point
+    private lateinit var componentState: CompatUIComponentState
+
+    @JvmField
+    @Rule
+    val compatUIHandlerRule: CompatUIHandlerRule = CompatUIHandlerRule()
+
+    @Before
+    fun setUp() {
+        state = CompatUIState()
+        view = View(mContext)
+        position = Point(123, 456)
+        layout = FakeCompatUILayout(viewBuilderReturn = view, positionBuilderReturn = position)
+        spec = FakeCompatUISpec("comp", layout = layout)
+        info = testCompatUIInfo()
+        syncQueue = mock<SyncTransactionQueue>()
+        displayLayout = mock<DisplayLayout>()
+        component =
+            CompatUIComponent(spec.getSpec(),
+                "compId",
+                mContext,
+                state,
+                info,
+                syncQueue,
+                displayLayout)
+        componentState = object : CompatUIComponentState {}
+        state.registerUIComponent("compId", component, componentState)
+    }
+
+    @Test
+    fun `when initLayout is invoked spec fields are used`() {
+        compatUIHandlerRule.postBlocking {
+            component.initLayout(info)
+        }
+        with(layout) {
+            assertViewBuilderInvocation(1)
+            assertEquals(info, lastViewBuilderCompatUIInfo)
+            assertEquals(componentState, lastViewBuilderCompState)
+            assertViewBinderInvocation(0)
+            assertPositionFactoryInvocation(1)
+            assertEquals(info, lastPositionFactoryCompatUIInfo)
+            assertEquals(view, lastPositionFactoryView)
+            assertEquals(componentState, lastPositionFactoryCompState)
+            assertEquals(state.sharedState, lastPositionFactorySharedState)
+        }
+    }
+
+    @Test
+    fun `when update is invoked only position and binder spec fields are used`() {
+        compatUIHandlerRule.postBlocking {
+            component.initLayout(info)
+            layout.resetState()
+            component.update(info)
+        }
+        with(layout) {
+            assertViewBuilderInvocation(0)
+            assertViewBinderInvocation(1)
+            assertPositionFactoryInvocation(1)
+        }
+    }
+
+    private fun testCompatUIInfo(): CompatUIInfo {
+        val taskInfo = ActivityManager.RunningTaskInfo()
+        taskInfo.taskId = 1
+        return CompatUIInfo(taskInfo, null)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIHandlerRule.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIHandlerRule.kt
new file mode 100644
index 0000000..4b8b65c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIHandlerRule.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.wm.shell.compatui.impl
+
+import android.os.HandlerThread
+import java.util.concurrent.CountDownLatch
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Utility {@link TestRule} to manage Handlers in Compat UI tests.
+ */
+class CompatUIHandlerRule : TestRule {
+
+    private lateinit var handler: HandlerThread
+
+    /**
+     * Makes the HandlerThread available during the test
+     */
+    override fun apply(base: Statement?, description: Description?): Statement {
+        handler = HandlerThread("CompatUIHandler").apply {
+            start()
+        }
+        return object : Statement() {
+            @Throws(Throwable::class)
+            override fun evaluate() {
+                try {
+                    base!!.evaluate()
+                } finally {
+                    handler.quitSafely()
+                }
+            }
+        }
+    }
+
+    /**
+     * Posts a {@link Runnable} for the Handler
+     * @param runnable The Runnable to execute
+     */
+    fun postBlocking(runnable: Runnable) {
+        val countDown = CountDownLatch(/* count = */ 1)
+        handler.threadHandler.post{
+            runnable.run()
+            countDown.countDown()
+        }
+        try {
+            countDown.await()
+        } catch (e: InterruptedException) {
+            // No-op
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIStateUtil.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIStateUtil.kt
index 43bd412..4f0e5b9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIStateUtil.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/CompatUIStateUtil.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.compatui.impl
 
 import com.android.wm.shell.compatui.api.CompatUIComponentState
-import com.android.wm.shell.compatui.api.CompatUISpec
 import com.android.wm.shell.compatui.api.CompatUIState
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertNotNull
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandlerTest.kt
index 8136074..66852ad5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIHandlerTest.kt
@@ -18,13 +18,21 @@
 
 import android.app.ActivityManager
 import android.testing.AndroidTestingRunner
+import android.view.View
 import androidx.test.filters.SmallTest
+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.ShellExecutor
+import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.compatui.api.CompatUIComponentState
 import com.android.wm.shell.compatui.api.CompatUIInfo
 import com.android.wm.shell.compatui.api.CompatUIState
 import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 
 /**
  * Tests for {@link DefaultCompatUIHandler}.
@@ -34,20 +42,37 @@
  */
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class DefaultCompatUIHandlerTest {
+class DefaultCompatUIHandlerTest : ShellTestCase() {
+
+    @JvmField
+    @Rule
+    val compatUIHandlerRule: CompatUIHandlerRule = CompatUIHandlerRule()
 
     lateinit var compatUIRepository: FakeCompatUIRepository
     lateinit var compatUIHandler: DefaultCompatUIHandler
     lateinit var compatUIState: CompatUIState
     lateinit var fakeIdGenerator: FakeCompatUIComponentIdGenerator
+    lateinit var syncQueue: SyncTransactionQueue
+    lateinit var displayController: DisplayController
+    lateinit var shellExecutor: TestShellExecutor
+    lateinit var componentFactory: FakeCompatUIComponentFactory
 
     @Before
     fun setUp() {
+        shellExecutor = TestShellExecutor()
         compatUIRepository = FakeCompatUIRepository()
         compatUIState = CompatUIState()
         fakeIdGenerator = FakeCompatUIComponentIdGenerator("compId")
-        compatUIHandler = DefaultCompatUIHandler(compatUIRepository, compatUIState,
-            fakeIdGenerator)
+        syncQueue = mock<SyncTransactionQueue>()
+        displayController = mock<DisplayController>()
+        componentFactory = FakeCompatUIComponentFactory(mContext, syncQueue, displayController)
+        compatUIHandler =
+            DefaultCompatUIHandler(
+                compatUIRepository,
+                compatUIState,
+                fakeIdGenerator,
+                componentFactory,
+                shellExecutor)
     }
 
     @Test
@@ -57,12 +82,18 @@
             creationReturn = false,
             removalReturn = false
         )
-        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle).getSpec()
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec =
+            FakeCompatUISpec(name = "one",
+                lifecycle = fakeLifecycle,
+                layout = fakeCompatUILayout).getSpec()
         compatUIRepository.addSpec(fakeCompatUISpec)
 
         val generatedId = fakeIdGenerator.generatedComponentId
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeIdGenerator.assertGenerateInvocations(1)
         fakeLifecycle.assertCreationInvocation(1)
@@ -71,7 +102,9 @@
         compatUIState.assertHasNoStateFor(generatedId)
         compatUIState.assertHasNoComponentFor(generatedId)
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
         fakeLifecycle.assertCreationInvocation(2)
         fakeLifecycle.assertRemovalInvocation(0)
         fakeLifecycle.assertInitialStateInvocation(0)
@@ -86,12 +119,18 @@
             creationReturn = true,
             removalReturn = false
         )
-        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle).getSpec()
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec =
+            FakeCompatUISpec(name = "one",
+                lifecycle = fakeLifecycle,
+                layout = fakeCompatUILayout).getSpec()
         compatUIRepository.addSpec(fakeCompatUISpec)
 
         val generatedId = fakeIdGenerator.generatedComponentId
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(0)
@@ -99,7 +138,9 @@
         compatUIState.assertHasNoStateFor(generatedId)
         compatUIState.assertHasComponentFor(generatedId)
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(1)
@@ -117,12 +158,18 @@
             removalReturn = false,
             initialState = { _, _ -> fakeComponentState }
         )
-        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle).getSpec()
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec =
+            FakeCompatUISpec(name = "one",
+                lifecycle = fakeLifecycle,
+                layout = fakeCompatUILayout).getSpec()
         compatUIRepository.addSpec(fakeCompatUISpec)
 
         val generatedId = fakeIdGenerator.generatedComponentId
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(0)
@@ -130,7 +177,9 @@
         compatUIState.assertHasStateEqualsTo(generatedId, fakeComponentState)
         compatUIState.assertHasComponentFor(generatedId)
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(1)
@@ -148,12 +197,18 @@
             removalReturn = true,
             initialState = { _, _ -> fakeComponentState }
         )
-        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle).getSpec()
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec =
+            FakeCompatUISpec(name = "one",
+                lifecycle = fakeLifecycle,
+                layout = fakeCompatUILayout).getSpec()
         compatUIRepository.addSpec(fakeCompatUISpec)
 
         val generatedId = fakeIdGenerator.generatedComponentId
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(0)
@@ -161,7 +216,9 @@
         compatUIState.assertHasStateEqualsTo(generatedId, fakeComponentState)
         compatUIState.assertHasComponentFor(generatedId)
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
 
         fakeLifecycle.assertCreationInvocation(1)
         fakeLifecycle.assertRemovalInvocation(1)
@@ -177,17 +234,56 @@
             creationReturn = true,
             removalReturn = true,
         )
-        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle).getSpec()
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle,
+            fakeCompatUILayout).getSpec()
         compatUIRepository.addSpec(fakeCompatUISpec)
         // Component creation
         fakeIdGenerator.assertGenerateInvocations(0)
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
         fakeIdGenerator.assertGenerateInvocations(1)
 
-        compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
         fakeIdGenerator.assertGenerateInvocations(2)
     }
 
+    @Test
+    fun `viewBuilder and viewBinder invoked if component is created and released when destroyed`() {
+        // We add a spec to the repository
+        val fakeLifecycle = FakeCompatUILifecyclePredicates(
+            creationReturn = true,
+            removalReturn = true,
+        )
+        val fakeCompatUILayout = FakeCompatUILayout(viewBuilderReturn = View(mContext))
+        val fakeCompatUISpec = FakeCompatUISpec("one", fakeLifecycle,
+            fakeCompatUILayout).getSpec()
+        compatUIRepository.addSpec(fakeCompatUISpec)
+
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
+        shellExecutor.flushAll()
+        componentFactory.assertInvocations(1)
+        fakeCompatUILayout.assertViewBuilderInvocation(1)
+        fakeCompatUILayout.assertViewBinderInvocation(1)
+        fakeCompatUILayout.assertViewReleaserInvocation(0)
+
+        compatUIHandlerRule.postBlocking {
+            compatUIHandler.onCompatInfoChanged(testCompatUIInfo())
+        }
+        shellExecutor.flushAll()
+
+        componentFactory.assertInvocations(1)
+        fakeCompatUILayout.assertViewBuilderInvocation(1)
+        fakeCompatUILayout.assertViewBinderInvocation(1)
+        fakeCompatUILayout.assertViewReleaserInvocation(1)
+    }
+
+
     private fun testCompatUIInfo(): CompatUIInfo {
         val taskInfo = ActivityManager.RunningTaskInfo()
         taskInfo.taskId = 1
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
index e35acb2..319122d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
@@ -16,9 +16,13 @@
 
 package com.android.wm.shell.compatui.impl
 
+
+import android.graphics.Point
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.testing.AndroidTestingRunner
+import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.wm.shell.compatui.api.CompatUILayout
 import com.android.wm.shell.compatui.api.CompatUILifecyclePredicates
 import com.android.wm.shell.compatui.api.CompatUIRepository
 import com.android.wm.shell.compatui.api.CompatUISpec
@@ -89,8 +93,14 @@
     }
 
     private fun specById(name: String): CompatUISpec =
-        CompatUISpec(name = name, lifecycle = CompatUILifecyclePredicates(
-            creationPredicate = { _, _ -> true },
-            removalPredicate = { _, _, _ -> true }
-        ))
+        CompatUISpec(name = name,
+            lifecycle = CompatUILifecyclePredicates(
+                creationPredicate = { _, _ -> true },
+                removalPredicate = { _, _, _ -> true }
+            ),
+            layout = CompatUILayout(
+                viewBuilder = { ctx, _, _ -> View(ctx) },
+                positionFactory = { _, _, _, _ -> Point(0, 0) }
+            )
+        )
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUIComponentFactory.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUIComponentFactory.kt
new file mode 100644
index 0000000..782add8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUIComponentFactory.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.impl
+
+import android.content.Context
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIComponent
+import com.android.wm.shell.compatui.api.CompatUIComponentFactory
+import com.android.wm.shell.compatui.api.CompatUIInfo
+import com.android.wm.shell.compatui.api.CompatUISpec
+import com.android.wm.shell.compatui.api.CompatUIState
+import junit.framework.Assert.assertEquals
+
+/**
+ * Fake {@link CompatUIComponentFactory} implementation.
+ */
+class FakeCompatUIComponentFactory(
+    private val context: Context,
+    private val syncQueue: SyncTransactionQueue,
+    private val displayController: DisplayController
+) : CompatUIComponentFactory {
+
+    var lastSpec: CompatUISpec? = null
+    var lastCompId: String? = null
+    var lastState: CompatUIState? = null
+    var lastInfo: CompatUIInfo? = null
+
+    var numberInvocations = 0
+
+    override fun create(
+        spec: CompatUISpec,
+        compId: String,
+        state: CompatUIState,
+        compatUIInfo: CompatUIInfo
+    ): CompatUIComponent {
+        lastSpec = spec
+        lastCompId = compId
+        lastState = state
+        lastInfo = compatUIInfo
+        numberInvocations++
+        return CompatUIComponent(
+            spec,
+            compId,
+            context,
+            state,
+            compatUIInfo,
+            syncQueue,
+            displayController.getDisplayLayout(compatUIInfo.taskInfo.displayId)
+        )
+    }
+
+    fun assertInvocations(expected: Int) =
+        assertEquals(expected, numberInvocations)
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILayout.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILayout.kt
new file mode 100644
index 0000000..d7a178a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILayout.kt
@@ -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.wm.shell.compatui.impl
+
+import android.content.Context
+import android.graphics.Point
+import android.view.View
+import com.android.wm.shell.compatui.api.CompatUIComponentState
+import com.android.wm.shell.compatui.api.CompatUIInfo
+import com.android.wm.shell.compatui.api.CompatUILayout
+import com.android.wm.shell.compatui.api.CompatUISharedState
+import junit.framework.Assert.assertEquals
+
+/**
+ * Fake class for {@link CompatUILayout}
+ */
+class FakeCompatUILayout(
+    private val zOrderReturn: Int = 0,
+    private val layoutParamFlagsReturn: Int = 0,
+    private val viewBuilderReturn: View,
+    private val positionBuilderReturn: Point = Point(0, 0)
+) {
+
+    var viewBuilderInvocation = 0
+    var viewBinderInvocation = 0
+    var positionFactoryInvocation = 0
+    var viewReleaserInvocation = 0
+
+    var lastViewBuilderContext: Context? = null
+    var lastViewBuilderCompatUIInfo: CompatUIInfo? = null
+    var lastViewBuilderCompState: CompatUIComponentState? = null
+    var lastViewBinderView: View? = null
+    var lastViewBinderCompatUIInfo: CompatUIInfo? = null
+    var lastViewBinderSharedState: CompatUISharedState? = null
+    var lastViewBinderCompState: CompatUIComponentState? = null
+    var lastPositionFactoryView: View? = null
+    var lastPositionFactoryCompatUIInfo: CompatUIInfo? = null
+    var lastPositionFactorySharedState: CompatUISharedState? = null
+    var lastPositionFactoryCompState: CompatUIComponentState? = null
+
+    fun getLayout() = CompatUILayout(
+        zOrder = zOrderReturn,
+        layoutParamFlags = layoutParamFlagsReturn,
+        viewBuilder = { ctx, info, componentState ->
+            lastViewBuilderContext = ctx
+            lastViewBuilderCompatUIInfo = info
+            lastViewBuilderCompState = componentState
+            viewBuilderInvocation++
+            viewBuilderReturn
+        },
+        viewBinder = { view, info, sharedState, componentState ->
+            lastViewBinderView = view
+            lastViewBinderCompatUIInfo = info
+            lastViewBinderCompState = componentState
+            lastViewBinderSharedState = sharedState
+            viewBinderInvocation++
+        },
+        positionFactory = { view, info, sharedState, componentState ->
+            lastPositionFactoryView = view
+            lastPositionFactoryCompatUIInfo = info
+            lastPositionFactoryCompState = componentState
+            lastPositionFactorySharedState = sharedState
+            positionFactoryInvocation++
+            positionBuilderReturn
+        },
+        viewReleaser = { viewReleaserInvocation++ }
+    )
+
+    fun assertViewBuilderInvocation(expected: Int) =
+        assertEquals(expected, viewBuilderInvocation)
+
+    fun assertViewBinderInvocation(expected: Int) =
+        assertEquals(expected, viewBinderInvocation)
+
+    fun assertViewReleaserInvocation(expected: Int) =
+        assertEquals(expected, viewReleaserInvocation)
+
+    fun assertPositionFactoryInvocation(expected: Int) =
+        assertEquals(expected, positionFactoryInvocation)
+
+    fun resetState() {
+        viewBuilderInvocation = 0
+        viewBinderInvocation = 0
+        positionFactoryInvocation = 0
+        viewReleaserInvocation = 0
+        lastViewBuilderCompatUIInfo = null
+        lastViewBuilderCompState = null
+        lastViewBinderView = null
+        lastViewBinderCompatUIInfo = null
+        lastViewBinderSharedState = null
+        lastViewBinderCompState = null
+        lastPositionFactoryView = null
+        lastPositionFactoryCompatUIInfo = null
+        lastPositionFactorySharedState = null
+        lastPositionFactoryCompState = null
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILifecyclePredicates.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILifecyclePredicates.kt
index bbaa2db..f742ca3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILifecyclePredicates.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUILifecyclePredicates.kt
@@ -26,8 +26,8 @@
  * Fake class for {@link CompatUILifecycle}
  */
 class FakeCompatUILifecyclePredicates(
-    private val creationReturn: Boolean,
-    private val removalReturn: Boolean,
+    private val creationReturn: Boolean = false,
+    private val removalReturn: Boolean = false,
     private val initialState: (
         CompatUIInfo,
         CompatUISharedState
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUISpec.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUISpec.kt
index 1ecd52e..0912bf11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUISpec.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/FakeCompatUISpec.kt
@@ -23,10 +23,13 @@
  */
 class FakeCompatUISpec(
     val name: String,
-    val lifecycle: FakeCompatUILifecyclePredicates
+    val lifecycle: FakeCompatUILifecyclePredicates = FakeCompatUILifecyclePredicates(),
+    val layout: FakeCompatUILayout
 ) {
     fun getSpec(): CompatUISpec = CompatUISpec(
         name = name,
-        lifecycle = lifecycle.getLifecycle()
+        log = {str -> android.util.Log.d("COMPAT_UI_TEST", str)},
+        lifecycle = lifecycle.getLifecycle(),
+        layout = layout.getLayout()
     )
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
new file mode 100644
index 0000000..b14f163
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -0,0 +1,263 @@
+/*
+ * 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
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+import android.graphics.Rect
+import android.os.Binder
+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.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.common.TaskStackListenerImpl
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertTrue
+import kotlin.test.assertNotNull
+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.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+/**
+ * Test class for {@link DesktopActivityOrientationChangeHandler}
+ *
+ * Usage: atest WMShellUnitTests:DesktopActivityOrientationChangeHandlerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE)
+class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
+    @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+    @Mock lateinit var testExecutor: ShellExecutor
+    @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+    @Mock lateinit var transitions: Transitions
+    @Mock lateinit var resizeTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
+    @Mock lateinit var taskStackListener: TaskStackListenerImpl
+
+    private lateinit var mockitoSession: StaticMockitoSession
+    private lateinit var handler: DesktopActivityOrientationChangeHandler
+    private lateinit var shellInit: ShellInit
+    private lateinit var taskRepository: DesktopModeTaskRepository
+    // Mock running tasks are registered here so we can get the list from mock shell task organizer.
+    private val runningTasks = mutableListOf<RunningTaskInfo>()
+
+    @Before
+    fun setUp() {
+        mockitoSession =
+            mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .spyStatic(DesktopModeStatus::class.java)
+                .startMocking()
+        doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+        shellInit = spy(ShellInit(testExecutor))
+        taskRepository = DesktopModeTaskRepository()
+        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+
+        handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
+            taskStackListener, resizeTransitionHandler, taskRepository)
+
+        shellInit.init()
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
+
+        runningTasks.clear()
+    }
+
+    @Test
+    fun instantiate_addInitCallback() {
+        verify(shellInit).addInitCallback(any(), any<DesktopActivityOrientationChangeHandler>())
+    }
+
+    @Test
+    fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
+        whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+        clearInvocations(shellInit)
+
+        handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
+            taskStackListener, resizeTransitionHandler, taskRepository)
+
+        verify(shellInit, never()).addInitCallback(any(),
+            any<DesktopActivityOrientationChangeHandler>())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_resizeable_doNothing() {
+        val task = setUpFreeformTask()
+
+        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE)
+
+        verify(resizeTransitionHandler, never()).startTransition(any(), any())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_nonResizeableFullscreen_doNothing() {
+        val task = createFullscreenTask()
+        task.isResizeable = false
+        val activityInfo = ActivityInfo()
+        activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT
+        task.topActivityInfo = activityInfo
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        taskRepository.addActiveTask(DEFAULT_DISPLAY, task.taskId)
+        taskRepository.updateTaskVisibility(DEFAULT_DISPLAY, task.taskId, visible = true)
+        runningTasks.add(task)
+
+        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE)
+
+        verify(resizeTransitionHandler, never()).startTransition(any(), any())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_nonResizeablePortrait_requestSameOrientation_doNothing() {
+        val task = setUpFreeformTask(isResizeable = false)
+        val newTask = setUpFreeformTask(isResizeable = false,
+            orientation = SCREEN_ORIENTATION_SENSOR_PORTRAIT)
+
+        handler.handleActivityOrientationChange(task, newTask)
+
+        verify(resizeTransitionHandler, never()).startTransition(any(), any())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_notInDesktopMode_doNothing() {
+        val task = setUpFreeformTask(isResizeable = false)
+        taskRepository.updateTaskVisibility(task.displayId, task.taskId, visible = false)
+
+        taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
+            SCREEN_ORIENTATION_LANDSCAPE)
+
+        verify(resizeTransitionHandler, never()).startTransition(any(), any())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_nonResizeablePortrait_respectLandscapeRequest() {
+        val task = setUpFreeformTask(isResizeable = false)
+        val oldBounds = task.configuration.windowConfiguration.bounds
+        val newTask = setUpFreeformTask(isResizeable = false,
+            orientation = SCREEN_ORIENTATION_LANDSCAPE)
+
+        handler.handleActivityOrientationChange(task, newTask)
+
+        val wct = getLatestResizeDesktopTaskWct()
+        val finalBounds = findBoundsChange(wct, newTask)
+        assertNotNull(finalBounds)
+        val finalWidth = finalBounds.width()
+        val finalHeight = finalBounds.height()
+        // Bounds is landscape.
+        assertTrue(finalWidth > finalHeight)
+        // Aspect ratio remains the same.
+        assertEquals(oldBounds.height() / oldBounds.width(), finalWidth / finalHeight)
+        // Anchor point for resizing is at the center.
+        assertEquals(oldBounds.centerX(), finalBounds.centerX())
+    }
+
+    @Test
+    fun handleActivityOrientationChange_nonResizeableLandscape_respectPortraitRequest() {
+        val oldBounds = Rect(0, 0, 500, 200)
+        val task = setUpFreeformTask(
+            isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE, bounds = oldBounds
+        )
+        val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds)
+
+        handler.handleActivityOrientationChange(task, newTask)
+
+        val wct = getLatestResizeDesktopTaskWct()
+        val finalBounds = findBoundsChange(wct, newTask)
+        assertNotNull(finalBounds)
+        val finalWidth = finalBounds.width()
+        val finalHeight = finalBounds.height()
+        // Bounds is portrait.
+        assertTrue(finalHeight > finalWidth)
+        // Aspect ratio remains the same.
+        assertEquals(oldBounds.width() / oldBounds.height(), finalHeight / finalWidth)
+        // Anchor point for resizing is at the center.
+        assertEquals(oldBounds.centerX(), finalBounds.centerX())
+    }
+
+    private fun setUpFreeformTask(
+        displayId: Int = DEFAULT_DISPLAY,
+        isResizeable: Boolean = true,
+        orientation: Int = SCREEN_ORIENTATION_PORTRAIT,
+        bounds: Rect? = Rect(0, 0, 200, 500)
+    ): RunningTaskInfo {
+        val task = createFreeformTask(displayId, bounds)
+        val activityInfo = ActivityInfo()
+        activityInfo.screenOrientation = orientation
+        task.topActivityInfo = activityInfo
+        task.isResizeable = isResizeable
+        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        taskRepository.addActiveTask(displayId, task.taskId)
+        taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+        taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
+        runningTasks.add(task)
+        return task
+    }
+
+    private fun getLatestResizeDesktopTaskWct(
+        currentBounds: Rect? = null
+    ): WindowContainerTransaction {
+        val arg: ArgumentCaptor<WindowContainerTransaction> =
+            ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+        verify(resizeTransitionHandler, atLeastOnce())
+            .startTransition(capture(arg), eq(currentBounds))
+        return arg.value
+    }
+
+    private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+        wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+}
\ No newline at end of file
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 4548fcb..ca97229 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,14 +16,17 @@
 
 package com.android.wm.shell.desktopmode
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
 import com.android.internal.util.FrameworkStatsLog
 import com.android.modules.utils.testing.ExtendedMockitoRule
 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.TaskUpdate
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason
+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 org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.mockito.kotlin.eq
@@ -33,7 +36,7 @@
  */
 class DesktopModeEventLoggerTest {
 
-    private val desktopModeEventLogger  = DesktopModeEventLogger()
+    private val desktopModeEventLogger = DesktopModeEventLogger()
 
     @JvmField
     @Rule
@@ -44,7 +47,7 @@
     fun logSessionEnter_enterReason() = runBlocking {
         desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER)
 
-        ExtendedMockito.verify {
+        verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
                 /* event */
@@ -63,7 +66,7 @@
     fun logSessionExit_exitReason() = runBlocking {
         desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT)
 
-        ExtendedMockito.verify {
+        verify {
             FrameworkStatsLog.write(
                 eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED),
                 /* event */
@@ -82,7 +85,7 @@
     fun logTaskAdded_taskUpdate() = runBlocking {
         desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE)
 
-        ExtendedMockito.verify {
+        verify {
             FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED),
@@ -99,7 +102,11 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID))
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                /* visible_task_count */
+                eq(TASK_COUNT))
         }
     }
 
@@ -107,7 +114,7 @@
     fun logTaskRemoved_taskUpdate() = runBlocking {
         desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE)
 
-        ExtendedMockito.verify {
+        verify {
             FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
                 /* task_event */
                 eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED),
@@ -124,7 +131,11 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID))
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                /* visible_task_count */
+                eq(TASK_COUNT))
         }
     }
 
@@ -132,10 +143,11 @@
     fun logTaskInfoChanged_taskUpdate() = runBlocking {
         desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE)
 
-        ExtendedMockito.verify {
+        verify {
             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 */
@@ -149,7 +161,77 @@
                 /* task_y */
                 eq(TASK_UPDATE.taskY),
                 /* session_id */
-                eq(SESSION_ID))
+                eq(SESSION_ID),
+                eq(UNSET_MINIMIZE_REASON),
+                eq(UNSET_UNMINIMIZE_REASON),
+                /* visible_task_count */
+                eq(TASK_COUNT))
+        }
+    }
+
+    @Test
+    fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking {
+        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
+            createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT))
+
+        verify {
+            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+                /* task_event */
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                /* instance_id */
+                eq(TASK_UPDATE.instanceId),
+                /* uid */
+                eq(TASK_UPDATE.uid),
+                /* task_height */
+                eq(TASK_UPDATE.taskHeight),
+                /* task_width */
+                eq(TASK_UPDATE.taskWidth),
+                /* task_x */
+                eq(TASK_UPDATE.taskX),
+                /* task_y */
+                eq(TASK_UPDATE.taskY),
+                /* session_id */
+                eq(SESSION_ID),
+                /* minimize_reason */
+                eq(MinimizeReason.TASK_LIMIT.reason),
+                /* unminimize_reason */
+                eq(UNSET_UNMINIMIZE_REASON),
+                /* visible_task_count */
+                eq(TASK_COUNT))
+        }
+    }
+
+    @Test
+    fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking {
+        desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID,
+            createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP))
+
+        verify {
+            FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE),
+                /* task_event */
+                eq(FrameworkStatsLog
+                    .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED),
+                /* instance_id */
+                eq(TASK_UPDATE.instanceId),
+                /* uid */
+                eq(TASK_UPDATE.uid),
+                /* task_height */
+                eq(TASK_UPDATE.taskHeight),
+                /* task_width */
+                eq(TASK_UPDATE.taskWidth),
+                /* task_x */
+                eq(TASK_UPDATE.taskX),
+                /* task_y */
+                eq(TASK_UPDATE.taskY),
+                /* session_id */
+                eq(SESSION_ID),
+                /* minimize_reason */
+                eq(UNSET_MINIMIZE_REASON),
+                /* unminimize_reason */
+                eq(UnminimizeReason.TASKBAR_TAP.reason),
+                /* visible_task_count */
+                eq(TASK_COUNT))
         }
     }
 
@@ -161,9 +243,17 @@
         private const val TASK_Y = 0
         private const val TASK_HEIGHT = 100
         private const val TASK_WIDTH = 100
+        private const val TASK_COUNT = 1
 
         private val TASK_UPDATE = TaskUpdate(
-            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y
+            TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y,
+            visibleTaskCount = TASK_COUNT,
         )
+
+        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)
     }
 }
\ No newline at end of file
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 d459639..d399b20 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
@@ -22,6 +22,8 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.os.IBinder
+import android.os.SystemProperties
+import android.os.Trace
 import android.testing.AndroidTestingRunner
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
@@ -38,6 +40,7 @@
 import android.window.TransitionInfo.Change
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.modules.utils.testing.ExtendedMockitoRule
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.common.ShellExecutor
@@ -86,7 +89,11 @@
   @JvmField
   @Rule
   val extendedMockitoRule =
-      ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
+      ExtendedMockitoRule.Builder(this)
+          .mockStatic(DesktopModeStatus::class.java)
+          .mockStatic(SystemProperties::class.java)
+          .mockStatic(Trace::class.java)
+          .build()!!
 
   private val testExecutor = mock<ShellExecutor>()
   private val mockShellInit = mock<ShellInit>()
@@ -138,7 +145,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FREEFORM_INTENT,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -152,7 +160,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -165,7 +174,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_MENU_BUTTON,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -178,7 +188,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -191,7 +202,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.KEYBOARD_SHORTCUT_ENTER,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -201,7 +213,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -231,7 +244,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -261,7 +275,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -291,7 +306,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -324,7 +340,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_FROM_OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -335,7 +352,8 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.UNKNOWN_ENTER,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -345,7 +363,51 @@
 
     callOnTransitionReady(transitionInfo)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
+  }
+
+  @Test
+  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)
+    callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+    verifyTaskRemovedAndExitLogging(sessionId, 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)
+    val transitionInfo = TransitionInfoBuilder(TRANSIT_TO_BACK, 0).addChange(change).build()
+
+    callOnTransitionReady(transitionInfo)
+
+    verifyTaskAddedAndEnterLogging(EnterReason.SCREEN_ON,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
+  }
+
+  @Test
+  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)
+    callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build())
+    verifyTaskRemovedAndExitLogging(sessionId, 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
+    // to desktop
+    val transitionInfo =
+        TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0)
+            .addChange(createChange(TRANSIT_TO_FRONT, freeformTask))
+            .build()
+    callOnTransitionReady(transitionInfo)
+
+    verifyTaskAddedAndEnterLogging(EnterReason.APP_HANDLE_DRAG,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -481,7 +543,8 @@
     val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build()
     callOnTransitionReady(transitionInfo2)
 
-    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW, DEFAULT_TASK_UPDATE)
+    verifyTaskAddedAndEnterLogging(EnterReason.OVERVIEW,
+        DEFAULT_TASK_UPDATE.copy(visibleTaskCount = 1))
   }
 
   @Test
@@ -497,7 +560,8 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskAdded(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
+        .logTaskAdded(eq(sessionId),
+            eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2)))
     verify(desktopModeEventLogger, never()).logSessionEnter(any(), any())
   }
 
@@ -519,7 +583,8 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100)))
+            eq(sessionId),
+            eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)))
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
@@ -548,7 +613,8 @@
             eq(sessionId),
             eq(
                 DEFAULT_TASK_UPDATE.copy(
-                    taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100)))
+                    taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100,
+                    visibleTaskCount = 1)))
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
@@ -572,7 +638,8 @@
 
     verify(desktopModeEventLogger, times(1))
         .logTaskInfoChanged(
-            eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100)))
+            eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
+                taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)))
     verifyZeroInteractions(desktopModeEventLogger)
 
     // task 2 resize
@@ -596,7 +663,9 @@
                 DEFAULT_TASK_UPDATE.copy(
                     instanceId = 2,
                     taskWidth = DEFAULT_TASK_WIDTH + 100,
-                    taskHeight = DEFAULT_TASK_HEIGHT - 100)))
+                    taskHeight = DEFAULT_TASK_HEIGHT - 100,
+                    visibleTaskCount = 2)),
+            )
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
@@ -614,7 +683,8 @@
     callOnTransitionReady(transitionInfo)
 
     verify(desktopModeEventLogger, times(1))
-        .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2)))
+        .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy(
+            instanceId = 2, visibleTaskCount = 1)))
     verify(desktopModeEventLogger, never()).logSessionExit(any(), any())
   }
 
@@ -632,6 +702,17 @@
     assertNotNull(sessionId)
     verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason))
     verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate))
+    ExtendedMockito.verify {
+        Trace.setCounter(
+            eq(Trace.TRACE_TAG_WINDOW_MANAGER),
+            eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_NAME),
+            eq(taskUpdate.visibleTaskCount.toLong()))
+    }
+    ExtendedMockito.verify {
+        SystemProperties.set(
+            eq(DesktopModeLoggerTransitionObserver.VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY),
+            eq(taskUpdate.visibleTaskCount.toString()))
+    }
     verifyZeroInteractions(desktopModeEventLogger)
   }
 
@@ -653,6 +734,7 @@
     const val DEFAULT_TASK_WIDTH = 200
     const val DEFAULT_TASK_X = 30
     const val DEFAULT_TASK_Y = 70
+    const val DEFAULT_VISIBLE_TASK_COUNT = 0
     val DEFAULT_TASK_UPDATE =
         TaskUpdate(
             DEFAULT_TASK_ID,
@@ -661,6 +743,7 @@
             DEFAULT_TASK_WIDTH,
             DEFAULT_TASK_X,
             DEFAULT_TASK_Y,
+            visibleTaskCount = DEFAULT_VISIBLE_TASK_COUNT,
         )
 
     fun createTaskInfo(
@@ -674,7 +757,7 @@
     ) =
         ActivityManager.RunningTaskInfo().apply {
           taskId = id
-          userId = uid
+          effectiveUid = uid
           configuration.windowConfiguration.apply {
             windowingMode = windowMode
             positionInParent = Point(taskX, taskY)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
index 518c00d..db4e93d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypesTest.kt
@@ -18,11 +18,6 @@
 
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.TASK_DRAG
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON
 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
@@ -33,6 +28,11 @@
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType
 import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getExitTransitionType
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.APP_FROM_OVERVIEW
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.KEYBOARD_SHORTCUT
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.TASK_DRAG
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt
new file mode 100644
index 0000000..8fab410
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.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.wm.shell.desktopmode
+
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUtilsTest {
+    @Test
+    fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() {
+        assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue()
+    }
+
+    @Test
+    fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() {
+        assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse()
+    }
+
+    @Test
+    fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() {
+        assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue()
+    }
+
+    @Test
+    fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() {
+        assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+    }
+
+    @Test
+    fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() {
+        assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+    }
+
+    @Test
+    fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() {
+        assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue()
+    }
+
+    private companion object {
+        val task1Bounds = Rect(0, 0, 0, 0)
+        val task2Bounds = Rect(1, 1, 1, 1)
+        val task3Bounds = Rect(0, 1, 0, 1)
+        val task4Bounds = Rect(1, 2, 2, 1)
+        val stableBounds = Rect(1, 1, 1, 1)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
index 2dea43b..f558e87 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt
@@ -17,9 +17,6 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.graphics.Rect
 import android.graphics.Region
 import android.testing.AndroidTestingRunner
@@ -38,6 +35,11 @@
 import org.mockito.Mock
 import org.mockito.kotlin.whenever
 
+/**
+ * Test class for [DesktopModeVisualIndicator]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeVisualIndicatorTest
+ */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 class DesktopModeVisualIndicatorTest : ShellTestCase() {
@@ -52,8 +54,6 @@
 
     @Before
     fun setUp() {
-        visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
-            context, taskSurface, taskDisplayAreaOrganizer)
         whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
         whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
         whenever(displayLayout.stableInsets()).thenReturn(STABLE_INSETS)
@@ -61,41 +61,52 @@
 
     @Test
     fun testFullscreenRegionCalculation() {
-        var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
+        var testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, 2 * STABLE_INSETS.top))
-        testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
-            WINDOWING_MODE_FREEFORM, CAPTION_HEIGHT)
 
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
+        testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
         val transitionHeight = context.resources.getDimensionPixelSize(
             R.dimen.desktop_mode_transition_region_thickness)
         val toFullscreenScale = mContext.resources.getFloat(
             R.dimen.desktop_mode_fullscreen_region_scale
         )
         val toFullscreenWidth = displayLayout.width() * toFullscreenScale
-
         assertThat(testRegion.bounds).isEqualTo(Rect(
             (DISPLAY_BOUNDS.width() / 2f - toFullscreenWidth / 2f).toInt(),
             -50,
             (DISPLAY_BOUNDS.width() / 2f + toFullscreenWidth / 2f).toInt(),
             transitionHeight))
-        testRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
-            WINDOWING_MODE_MULTI_WINDOW, CAPTION_HEIGHT)
+
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
+        testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, 2 * STABLE_INSETS.top))
+
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
+        testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT)
+        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 2400, transitionHeight))
     }
 
     @Test
     fun testSplitLeftRegionCalculation() {
         val transitionHeight = context.resources.getDimensionPixelSize(
             R.dimen.desktop_mode_split_from_desktop_height)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, transitionHeight, 32, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
+        testRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(0, -50, 32, 1600))
     }
 
@@ -103,27 +114,35 @@
     fun testSplitRightRegionCalculation() {
         val transitionHeight = context.resources.getDimensionPixelSize(
             R.dimen.desktop_mode_split_from_desktop_height)
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         var testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)
         testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            WINDOWING_MODE_FREEFORM, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, transitionHeight, 2400, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)
         testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            WINDOWING_MODE_MULTI_WINDOW, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+        assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.DRAGGED_INTENT)
+        testRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         assertThat(testRegion.bounds).isEqualTo(Rect(2368, -50, 2400, 1600))
     }
 
     @Test
     fun testToDesktopRegionCalculation() {
+        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)
         val fullscreenRegion = visualIndicator.calculateFullscreenRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, CAPTION_HEIGHT)
+            CAPTION_HEIGHT)
         val splitLeftRegion = visualIndicator.calculateSplitLeftRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         val splitRightRegion = visualIndicator.calculateSplitRightRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
+            TRANSITION_AREA_WIDTH, CAPTION_HEIGHT)
         val desktopRegion = visualIndicator.calculateToDesktopRegion(displayLayout,
-            WINDOWING_MODE_FULLSCREEN, splitLeftRegion, splitRightRegion, fullscreenRegion)
+            splitLeftRegion, splitRightRegion, fullscreenRegion)
         var testRegion = Region()
         testRegion.union(DISPLAY_BOUNDS)
         testRegion.op(splitLeftRegion, Region.Op.DIFFERENCE)
@@ -132,6 +151,11 @@
         assertThat(desktopRegion).isEqualTo(testRegion)
     }
 
+    private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
+        visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo, displayController,
+            context, taskSurface, taskDisplayAreaOrganizer, dragStartState)
+    }
+
     companion object {
         private const val TRANSITION_AREA_WIDTH = 32
         private const val CAPTION_HEIGHT = 50
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 7bb5449..10557dd 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
@@ -83,9 +83,8 @@
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource.UNKNOWN
-import com.android.wm.shell.common.split.SplitScreenConstants
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
@@ -95,6 +94,8 @@
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN
+import com.android.wm.shell.shared.split.SplitScreenConstants
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -122,12 +123,12 @@
 import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mock
 import org.mockito.Mockito
-import org.mockito.Mockito.any
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
 import org.mockito.kotlin.anyOrNull
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.eq
@@ -160,6 +161,7 @@
   @Mock lateinit var mReturnToDragStartAnimator: ReturnToDragStartAnimator
   @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
   @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
+  @Mock lateinit var dragAndDropTransitionHandler: DesktopModeDragAndDropTransitionHandler
   @Mock
   lateinit var toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler
   @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
@@ -174,6 +176,7 @@
   @Mock
   private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
   @Mock private lateinit var mockSurface: SurfaceControl
+  @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
 
   private lateinit var mockitoSession: StaticMockitoSession
   private lateinit var controller: DesktopTasksController
@@ -236,6 +239,8 @@
     val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
     verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
     recentsTransitionStateListener = captor.value
+
+    controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
   }
 
   private fun createController(): DesktopTasksController {
@@ -254,6 +259,7 @@
         mReturnToDragStartAnimator,
         enterDesktopTransitionHandler,
         exitDesktopTransitionHandler,
+        dragAndDropTransitionHandler,
         toggleResizeDesktopTaskTransitionHandler,
         dragToDesktopTransitionHandler,
         taskRepository,
@@ -280,6 +286,52 @@
   }
 
   @Test
+  fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
+    setUpFreeformTask()
+
+    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
+  }
+
+  @Test
+  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
+    val task1 = setUpFreeformTask()
+
+    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+    controller.toggleDesktopTaskSize(task1)
+    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+    assertThat(argumentCaptor.value).isTrue()
+  }
+
+  @Test
+  fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
+    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+    setUpFreeformTask(bounds = stableBounds, active = true)
+    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+  }
+
+  @Test
+  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() {
+    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+    val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
+
+    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+    controller.toggleDesktopTaskSize(task1)
+    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+    assertThat(argumentCaptor.value).isFalse()
+  }
+
+  @Test
+  fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
+    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+    setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
+
+    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+  }
+
+
+  @Test
   fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
     whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
     clearInvocations(shellInit)
@@ -641,6 +693,41 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun handleRequest_newFreeformTaskLaunch_cascadeApplied() {
+    assumeTrue(ENABLE_SHELL_TRANSITIONS)
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+    val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS, active = false)
+
+    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+    assertNotNull(wct, "should handle request")
+    val finalBounds = findBoundsChange(wct, freeformTask)
+    assertThat(stableBounds.getDesktopTaskPosition(finalBounds!!))
+      .isEqualTo(DesktopTaskPosition.BottomRight)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
+  fun handleRequest_freeformTaskAlreadyExistsInDesktopMode_cascadeNotApplied() {
+    assumeTrue(ENABLE_SHELL_TRANSITIONS)
+    setUpLandscapeDisplay()
+    val stableBounds = Rect()
+    displayLayout.getStableBoundsForDesktopMode(stableBounds)
+
+    setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+    val freeformTask = setUpFreeformTask(bounds = DEFAULT_LANDSCAPE_BOUNDS)
+
+    val wct = controller.handleRequest(Binder(), createTransition(freeformTask))
+
+    assertNull(wct, "should not handle request")
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_CASCADING_WINDOWS)
   fun addMoveToDesktopChanges_positionBottomRight() {
     setUpLandscapeDisplay()
     val stableBounds = Rect()
@@ -832,6 +919,18 @@
 
   @Test
   @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+  fun addMoveToDesktopChanges_landscapeDevice_portraitResizableApp_aspectRatioOverridden() {
+    setUpLandscapeDisplay()
+    val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+      shouldLetterbox = true, aspectRatioOverrideApplied = true)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+  }
+
+  @Test
+  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
   fun addMoveToDesktopChanges_portraitDevice_userFullscreenOverride_defaultPortraitBounds() {
     setUpPortraitDisplay()
     val task = setUpFullscreenTask(enableUserFullscreenOverride = true)
@@ -853,6 +952,19 @@
   }
 
   @Test
+  @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+  fun addMoveToDesktopChanges_portraitDevice_landscapeResizableApp_aspectRatioOverridden() {
+    setUpPortraitDisplay()
+    val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE,
+      deviceOrientation = ORIENTATION_PORTRAIT,
+      shouldLetterbox = true, aspectRatioOverrideApplied = true)
+    val wct = WindowContainerTransaction()
+    controller.addMoveToDesktopChanges(wct, task)
+
+    assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+  }
+
+  @Test
   fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
     val task = setUpFullscreenTask()
     val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
@@ -1367,6 +1479,78 @@
   }
 
   @Test
+  fun onDesktopWindowMinimize_noActiveTask_doesntUpdateTransaction() {
+    val wct = WindowContainerTransaction()
+    controller.onDesktopWindowMinimize(wct, taskId = 1)
+    // Nothing happens.
+    assertThat(wct.hierarchyOps).isEmpty()
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_singleActiveTask_noWallpaperActivityToken_doesntUpdateTransaction() {
+    val task = setUpFreeformTask()
+    val wct = WindowContainerTransaction()
+    controller.onDesktopWindowMinimize(wct, taskId = task.taskId)
+    // Nothing happens.
+    assertThat(wct.hierarchyOps).isEmpty()
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
+    val task = setUpFreeformTask()
+    val wallpaperToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = wallpaperToken
+
+    val wct = WindowContainerTransaction()
+    // The only active task is being minimized.
+    controller.onDesktopWindowMinimize(wct, taskId = task.taskId)
+    // Adds remove wallpaper operation
+    wct.assertRemoveAt(index = 0, wallpaperToken)
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_singleActiveTask_alreadyMinimized_doesntUpdateTransaction() {
+    val task = setUpFreeformTask()
+    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()
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_multipleActiveTasks_doesntUpdateTransaction() {
+    val task1 = setUpFreeformTask()
+    setUpFreeformTask()
+    val wallpaperToken = MockToken().token()
+    taskRepository.wallpaperActivityToken = wallpaperToken
+
+    val wct = WindowContainerTransaction()
+    controller.onDesktopWindowMinimize(wct, taskId = task1.taskId)
+    // Doesn't modify transaction
+    assertThat(wct.hierarchyOps).isEmpty()
+  }
+
+  @Test
+  fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
+    val task1 = setUpFreeformTask()
+    val task2 = setUpFreeformTask()
+    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)
+    // Adds remove wallpaper operation
+    wct.assertRemoveAt(index = 0, wallpaperToken)
+  }
+
+  @Test
   fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
@@ -1381,8 +1565,34 @@
     assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
         .isEqualTo(WINDOWING_MODE_FREEFORM)
 
-    assertThat(wct.hierarchyOps).hasSize(2)
-    wct.assertReorderAt(1, homeTask, toTop = false)
+    assertThat(wct.hierarchyOps).hasSize(1)
+  }
+
+  @Test
+  fun handleRequest_fullscreenTaskWithTaskOnHome_freeformVisible_returnSwitchToFreeformWCT() {
+    assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+    val homeTask = setUpHomeTask()
+    val freeformTask = setUpFreeformTask()
+    markTaskVisible(freeformTask)
+    val fullscreenTask = createFullscreenTask()
+    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+    assertNotNull(wct, "should handle request")
+    assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+      .isEqualTo(WINDOWING_MODE_FREEFORM)
+
+    // There are 5 hops that are happening in this case:
+    // 1. Moving the fullscreen task to top as we add moveToDesktop() changes
+    // 2. Bringing home task to front
+    // 3. Pending intent for the wallpaper
+    // 4. Bringing the existing freeform task to top
+    // 5. Bringing the fullscreen task back at the top
+    assertThat(wct.hierarchyOps).hasSize(5)
+    wct.assertReorderAt(1, homeTask, toTop = true)
+    wct.assertReorderAt(4, fullscreenTask, toTop = true)
   }
 
   @Test
@@ -1407,19 +1617,34 @@
     val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
     freeformTasks.forEach { markTaskVisible(it) }
     val fullscreenTask = createFullscreenTask()
-    val homeTask = setUpHomeTask(DEFAULT_DISPLAY)
 
     val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
 
     // Make sure we reorder the new task to top, and the back task to the bottom
-    assertThat(wct!!.hierarchyOps.size).isEqualTo(3)
+    assertThat(wct!!.hierarchyOps.size).isEqualTo(2)
     wct.assertReorderAt(0, fullscreenTask, toTop = true)
-    wct.assertReorderAt(1, homeTask, toTop = false)
-    wct.assertReorderAt(2, freeformTasks[0], toTop = false)
+    wct.assertReorderAt(1, freeformTasks[0], toTop = false)
   }
 
   @Test
-  fun handleRequest_fullscreenTaskToFreeform_alreadyBeyondLimit_existingAndNewTasksAreMinimized() {
+  fun handleRequest_fullscreenTaskWithTaskOnHome_bringsTasksOverLimit_otherTaskIsMinimized() {
+    assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+    val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
+    freeformTasks.forEach { markTaskVisible(it) }
+    val fullscreenTask = createFullscreenTask()
+    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
+
+    val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+
+    // Make sure we reorder the new task to top, and the back task to the bottom
+    assertThat(wct!!.hierarchyOps.size).isEqualTo(9)
+    wct.assertReorderAt(0, fullscreenTask, toTop = true)
+    wct.assertReorderAt(8, freeformTasks[0], toTop = false)
+  }
+
+  @Test
+  fun handleRequest_fullscreenTaskWithTaskOnHome_beyondLimit_existingAndNewTasksAreMinimized() {
     assumeTrue(ENABLE_SHELL_TRANSITIONS)
 
     val minimizedTask = setUpFreeformTask()
@@ -1428,15 +1653,16 @@
     freeformTasks.forEach { markTaskVisible(it) }
     val homeTask = setUpHomeTask()
     val fullscreenTask = createFullscreenTask()
+    fullscreenTask.baseIntent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME)
 
     val wct = controller.handleRequest(Binder(), createTransition(fullscreenTask))
 
-    assertThat(wct!!.hierarchyOps.size).isEqualTo(4)
+    assertThat(wct!!.hierarchyOps.size).isEqualTo(10)
     wct.assertReorderAt(0, fullscreenTask, toTop = true)
-    // Make sure we reorder the home task to the bottom, and minimized tasks below the home task.
-    wct.assertReorderAt(1, homeTask, toTop = false)
-    wct.assertReorderAt(2, minimizedTask, toTop = false)
-    wct.assertReorderAt(3, freeformTasks[0], toTop = false)
+    // Make sure we reorder the home task to the top, desktop tasks to top of them and minimized
+    // task is under the home task.
+    wct.assertReorderAt(1, homeTask, toTop = true)
+    wct.assertReorderAt(9, freeformTasks[0], toTop = false)
   }
 
   @Test
@@ -1784,6 +2010,19 @@
   }
 
   @Test
+  @EnableFlags(
+    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+  )
+  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_notInDesktop_doesNotHandle() {
+    val task = setUpFreeformTask()
+    markTaskHidden(task)
+
+    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))
+
+    assertNull(result, "Should not handle request")
+  }
+
+  @Test
   @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
   @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
   fun handleRequest_backTransition_singleTaskNoToken_noBackNav_doesNotHandle() {
@@ -2189,27 +2428,6 @@
   }
 
   @Test
-  fun desktopTasksVisibilityChange_visible_setLaunchAdjacentDisabled() {
-    val task = setUpFreeformTask()
-    clearInvocations(launchAdjacentController)
-
-    markTaskVisible(task)
-    shellExecutor.flushAll()
-    verify(launchAdjacentController).launchAdjacentEnabled = false
-  }
-
-  @Test
-  fun desktopTasksVisibilityChange_invisible_setLaunchAdjacentEnabled() {
-    val task = setUpFreeformTask()
-    markTaskVisible(task)
-    clearInvocations(launchAdjacentController)
-
-    markTaskHidden(task)
-    shellExecutor.flushAll()
-    verify(launchAdjacentController).launchAdjacentEnabled = true
-  }
-
-  @Test
   fun moveFocusedTaskToDesktop_fullscreenTaskIsMovedToDesktop() {
     val task1 = setUpFullscreenTask()
     val task2 = setUpFullscreenTask()
@@ -2314,7 +2532,7 @@
   fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task = setUpFullscreenTask()
@@ -2330,7 +2548,7 @@
   fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
@@ -2346,7 +2564,7 @@
   fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2363,7 +2581,7 @@
   fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2380,7 +2598,7 @@
   fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2400,7 +2618,7 @@
   fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
@@ -2416,7 +2634,7 @@
   fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2435,7 +2653,7 @@
   fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2455,7 +2673,7 @@
   fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2475,7 +2693,7 @@
   fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
     val spyController = spy(controller)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
         .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
 
     val task =
@@ -2532,7 +2750,7 @@
 
     val currentDragBounds = Rect(100, 200, 500, 1000)
     whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
-    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+    whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull()))
       .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
 
     spyController.onDragPositioningEnd(
@@ -2641,7 +2859,7 @@
   }
 
   @Test
-  fun getSnapBounds_calculatesBoundsForResizable() {
+  fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {
     val bounds = Rect(100, 100, 300, 300)
     val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
       topActivityInfo = ActivityInfo().apply {
@@ -2656,13 +2874,45 @@
       STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
     )
 
-    controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT)
+    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
     // Assert bounds set to stable bounds
     val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)
     assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
   }
 
   @Test
+  fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() {
+    assumeTrue(ENABLE_SHELL_TRANSITIONS)
+    // Set up task to already be in snapped-left bounds
+    val bounds = Rect(
+      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom
+    )
+    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
+      topActivityInfo = ActivityInfo().apply {
+        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
+        configuration.windowConfiguration.appBounds = bounds
+      }
+      isResizeable = true
+    }
+
+    // Attempt to snap left again
+    val currentDragBounds = Rect(bounds).apply { offset(-100, 0) }
+    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)
+
+    // Assert that task is NOT updated via WCT
+    verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any())
+
+    // Assert that task leash is updated via Surface Animations
+    verify(mReturnToDragStartAnimator).start(
+      eq(task.taskId),
+      eq(mockSurface),
+      eq(currentDragBounds),
+      eq(bounds),
+      eq(true)
+    )
+  }
+
+  @Test
   @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
   fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {
     val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply {
@@ -2693,7 +2943,8 @@
       eq(task.taskId),
       eq(mockSurface),
       eq(currentDragBounds),
-      eq(preDragBounds)
+      eq(preDragBounds),
+      eq(false)
     )
   }
 
@@ -2816,14 +3067,17 @@
 
   private fun setUpFreeformTask(
       displayId: Int = DEFAULT_DISPLAY,
-      bounds: Rect? = null
+      bounds: Rect? = null,
+      active: Boolean = true
   ): RunningTaskInfo {
     val task = createFreeformTask(displayId, bounds)
     val activityInfo = ActivityInfo()
     task.topActivityInfo = activityInfo
     whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
-    taskRepository.addActiveTask(displayId, task.taskId)
-    taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+    if (active) {
+      taskRepository.addActiveTask(displayId, task.taskId)
+      taskRepository.updateTaskVisibility(displayId, task.taskId, visible = true)
+    }
     taskRepository.addOrMoveFreeformTaskToTop(displayId, task.taskId)
     runningTasks.add(task)
     return task
@@ -2845,7 +3099,8 @@
     shouldLetterbox: Boolean = false,
     gravity: Int = Gravity.NO_GRAVITY,
     enableUserFullscreenOverride: Boolean = false,
-    enableSystemFullscreenOverride: Boolean = false
+    enableSystemFullscreenOverride: Boolean = false,
+    aspectRatioOverrideApplied: Boolean = false
   ): RunningTaskInfo {
     val task = createFullscreenTask(displayId)
     val activityInfo = ActivityInfo()
@@ -2860,6 +3115,7 @@
       appCompatTaskInfo.isSystemFullscreenOverrideEnabled = enableSystemFullscreenOverride
 
       if (shouldLetterbox) {
+        appCompatTaskInfo.setHasMinAspectRatioOverride(aspectRatioOverrideApplied)
         if (deviceOrientation == ORIENTATION_LANDSCAPE &&
             screenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
           // Letterbox to portrait size
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 c97bcfb..5b02837 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
@@ -8,6 +8,7 @@
 import android.app.WindowConfiguration.WindowingMode
 import android.graphics.PointF
 import android.os.IBinder
+import android.os.SystemProperties
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.SurfaceControl
@@ -16,12 +17,13 @@
 import android.window.TransitionInfo.FLAG_IS_WALLPAPER
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.TestRunningTaskInfoBuilder
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+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
 import com.android.wm.shell.transition.Transitions
 import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
@@ -29,19 +31,24 @@
 import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
 import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
 import java.util.function.Supplier
+import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
+import org.junit.After
 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.Mock
+import org.mockito.MockitoSession
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.verifyZeroInteractions
 import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
 
 /** Tests of [DragToDesktopTransitionHandler]. */
 @SmallTest
@@ -61,10 +68,12 @@
 
     private lateinit var defaultHandler: DragToDesktopTransitionHandler
     private lateinit var springHandler: SpringDragToDesktopTransitionHandler
+    private lateinit var mockitoSession: MockitoSession
 
     @Before
     fun setUp() {
-        defaultHandler = DefaultDragToDesktopTransitionHandler(
+        defaultHandler =
+            DefaultDragToDesktopTransitionHandler(
                     context,
                     transitions,
                     taskDisplayAreaOrganizer,
@@ -72,7 +81,8 @@
                     transactionSupplier,
                 )
                 .apply { setSplitScreenController(splitScreenController) }
-        springHandler = SpringDragToDesktopTransitionHandler(
+        springHandler =
+            SpringDragToDesktopTransitionHandler(
                     context,
                     transitions,
                     taskDisplayAreaOrganizer,
@@ -80,6 +90,16 @@
                     transactionSupplier,
                 )
                 .apply { setSplitScreenController(splitScreenController) }
+        mockitoSession =
+            ExtendedMockito.mockitoSession()
+                .strictness(Strictness.LENIENT)
+                .mockStatic(SystemProperties::class.java)
+                .startMocking()
+    }
+
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
     }
 
     @Test
@@ -357,6 +377,77 @@
         verify(finishCallback).onTransitionFinished(null)
     }
 
+    @Test
+    fun propertyValue_returnsSystemPropertyValue() {
+        val name = "property_name"
+        val value = 10f
+
+        whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt()))
+            .thenReturn(value.toInt())
+
+        assertEquals(
+            "Expects to return system properties stored value",
+            /* expected= */ value,
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name)
+        )
+    }
+
+    @Test
+    fun propertyValue_withScale_returnsScaledSystemPropertyValue() {
+        val name = "property_name"
+        val value = 10f
+        val scale = 100f
+
+        whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), anyInt()))
+            .thenReturn(value.toInt())
+
+        assertEquals(
+            "Expects to return scaled system properties stored value",
+            /* expected= */ value / scale,
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(name, scale = scale)
+        )
+    }
+
+    @Test
+    fun propertyValue_notSet_returnsDefaultValue() {
+        val name = "property_name"
+        val defaultValue = 50f
+
+        whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(defaultValue.toInt())))
+            .thenReturn(defaultValue.toInt())
+
+        assertEquals(
+            "Expects to return the default value",
+            /* expected= */ defaultValue,
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
+                name,
+                default = defaultValue
+            )
+        )
+    }
+
+    @Test
+    fun propertyValue_withScaleNotSet_returnsDefaultValue() {
+        val name = "property_name"
+        val defaultValue = 0.5f
+        val scale = 100f
+        // Default value is multiplied when provided as a default value for [SystemProperties]
+        val scaledDefault = (defaultValue * scale).toInt()
+
+        whenever(SystemProperties.getInt(eq(systemPropertiesKey(name)), eq(scaledDefault)))
+            .thenReturn(scaledDefault)
+
+        assertEquals(
+            "Expects to return the default value",
+            /* expected= */ defaultValue,
+            /* actual= */ SpringDragToDesktopTransitionHandler.propertyValue(
+                name,
+                default = defaultValue,
+                scale = scale
+            )
+        )
+    }
+
     private fun startDrag(
         handler: DragToDesktopTransitionHandler,
         task: RunningTaskInfo = createTask(),
@@ -462,4 +553,7 @@
             )
         }
     }
+
+    private fun systemPropertiesKey(name: String) =
+        "${SpringDragToDesktopTransitionHandler.SYSTEM_PROPERTIES_GROUP}.$name"
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
index e5157c9..e0463b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -48,7 +48,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt
new file mode 100644
index 0000000..765021f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.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.wm.shell.desktopmode.education
+
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.DataStoreFactory
+import androidx.datastore.dataStoreFile
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+import com.android.wm.shell.util.createWindowingEducationProto
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.time.Duration
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@ExperimentalCoroutinesApi
+class AppHandleEducationDatastoreRepositoryTest {
+  private val testContext: Context = InstrumentationRegistry.getInstrumentation().targetContext
+  private lateinit var testDatastore: DataStore<WindowingEducationProto>
+  private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+  private lateinit var datastoreScope: CoroutineScope
+
+  @Before
+  fun setUp() {
+    Dispatchers.setMain(StandardTestDispatcher())
+    datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
+    testDatastore =
+        DataStoreFactory.create(
+            serializer =
+                AppHandleEducationDatastoreRepository.Companion.WindowingEducationProtoSerializer,
+            scope = datastoreScope) {
+              testContext.dataStoreFile(APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE)
+            }
+    datastoreRepository = AppHandleEducationDatastoreRepository(testDatastore)
+  }
+
+  @After
+  fun tearDown() {
+    File(ApplicationProvider.getApplicationContext<Context>().filesDir, "datastore")
+        .deleteRecursively()
+
+    datastoreScope.cancel()
+  }
+
+  @Test
+  fun getWindowingEducationProto_returnsCorrectProto() =
+      runTest(StandardTestDispatcher()) {
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                educationViewedTimestampMillis = 123L,
+                featureUsedTimestampMillis = 124L,
+                appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+                appUsageStatsLastUpdateTimestampMillis = 125L)
+        testDatastore.updateData { windowingEducationProto }
+
+        val resultProto = datastoreRepository.windowingEducationProto()
+
+        assertThat(resultProto).isEqualTo(windowingEducationProto)
+      }
+
+  @Test
+  fun updateAppUsageStats_updatesDatastoreProto() =
+      runTest(StandardTestDispatcher()) {
+        val appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 3)
+        val appUsageStatsLastUpdateTimestamp = Duration.ofMillis(123L)
+        val windowingEducationProto =
+            createWindowingEducationProto(
+                appUsageStats = appUsageStats,
+                appUsageStatsLastUpdateTimestampMillis =
+                    appUsageStatsLastUpdateTimestamp.toMillis())
+
+        datastoreRepository.updateAppUsageStats(appUsageStats, appUsageStatsLastUpdateTimestamp)
+
+        val result = testDatastore.data.first()
+        assertThat(result).isEqualTo(windowingEducationProto)
+      }
+
+  companion object {
+    private const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+    private const val APP_HANDLE_EDUCATION_DATASTORE_TEST_FILE = "app_handle_education_test.pb"
+  }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt
new file mode 100644
index 0000000..c0d71c0b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.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.wm.shell.desktopmode.education
+
+import android.app.usage.UsageStats
+import android.app.usage.UsageStatsManager
+import android.content.Context
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import android.testing.TestableResources
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.R
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
+import com.android.wm.shell.util.createWindowingEducationProto
+import com.google.common.truth.Truth.assertThat
+import kotlin.Int.Companion.MAX_VALUE
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class AppHandleEducationFilterTest : ShellTestCase() {
+  @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository
+  @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
+  private lateinit var educationFilter: AppHandleEducationFilter
+  private lateinit var testableResources: TestableResources
+  private lateinit var testableContext: TestableContext
+
+  @Before
+  fun setup() {
+    MockitoAnnotations.initMocks(this)
+    testableContext = TestableContext(mContext)
+    testableResources =
+        testableContext.orCreateTestableResources.apply {
+          addOverride(
+              R.array.desktop_windowing_app_handle_education_allowlist_apps,
+              arrayOf(GMAIL_PACKAGE_NAME))
+          addOverride(R.integer.desktop_windowing_education_required_time_since_setup_seconds, 0)
+          addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+          addOverride(
+              R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, MAX_VALUE)
+          addOverride(R.integer.desktop_windowing_education_app_launch_interval_seconds, 100)
+        }
+    testableContext.addMockSystemService(Context.USAGE_STATS_SERVICE, mockUsageStatsManager)
+    educationFilter = AppHandleEducationFilter(testableContext, datastoreRepository)
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_isTriggerValid_returnsTrue() = runTest {
+    // setup() makes sure that all of the conditions satisfy and #shouldShowAppHandleEducation
+    // should return true
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    assertThat(result).isTrue()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_focusAppNotInAllowlist_returnsFalse() = runTest {
+    // Pass Youtube as current focus app, it is not in allowlist hence #shouldShowAppHandleEducation
+    // should return false
+    testableResources.addOverride(
+        R.array.desktop_windowing_app_handle_education_allowlist_apps, arrayOf(GMAIL_PACKAGE_NAME))
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(YOUTUBE_PACKAGE_NAME to 4),
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(YOUTUBE_PACKAGE_NAME)
+
+    assertThat(result).isFalse()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_timeSinceSetupIsNotSufficient_returnsFalse() = runTest {
+    // Time required to have passed setup is > 100 years, hence #shouldShowAppHandleEducation should
+    // return false
+    testableResources.addOverride(
+        R.integer.desktop_windowing_education_required_time_since_setup_seconds, MAX_VALUE)
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    assertThat(result).isFalse()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_educationViewedBefore_returnsFalse() = runTest {
+    // Education has been viewed before, hence #shouldShowAppHandleEducation should return false
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+            educationViewedTimestampMillis = 123L,
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    assertThat(result).isFalse()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_featureUsedBefore_returnsFalse() = runTest {
+    // Feature has been used before, hence #shouldShowAppHandleEducation should return false
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+            featureUsedTimestampMillis = 123L,
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    assertThat(result).isFalse()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_doesNotHaveMinAppUsage_returnsFalse() = runTest {
+    // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence
+    // #shouldShowAppHandleEducation should return false
+    testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2),
+            appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE)
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    assertThat(result).isFalse()
+  }
+
+  @Test
+  fun shouldShowAppHandleEducation_appUsageStatsStale_queryAppUsageStats() = runTest {
+    // UsageStats caching interval is set to 0ms, that means caching should happen very frequently
+    testableResources.addOverride(
+        R.integer.desktop_windowing_education_app_usage_cache_interval_seconds, 0)
+    // The DataStore currently holds a proto object where Gmail's app launch count is recorded as 4.
+    // This value exceeds the minimum required count of 3.
+    testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3)
+    val windowingEducationProto =
+        createWindowingEducationProto(
+            appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4),
+            appUsageStatsLastUpdateTimestampMillis = 0)
+    // The mocked UsageStatsManager is configured to return a launch count of 2 for Gmail.
+    // This value is below the minimum required count of 3.
+    `when`(mockUsageStatsManager.queryAndAggregateUsageStats(anyLong(), anyLong()))
+        .thenReturn(mapOf(GMAIL_PACKAGE_NAME to UsageStats().apply { mAppLaunchCount = 2 }))
+    `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto)
+
+    val result = educationFilter.shouldShowAppHandleEducation(GMAIL_PACKAGE_NAME)
+
+    // Result should be false as queried usage stats should be considered to determine the result
+    // instead of cached stats
+    assertThat(result).isFalse()
+  }
+
+  companion object {
+    private const val GMAIL_PACKAGE_NAME = "com.google.android.gm"
+    private const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube"
+  }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 97fa8d6..645b296 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -27,9 +27,9 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_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;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
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 6ec6bed..763d015 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
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
+import android.view.SurfaceControl;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -35,6 +36,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
 import com.android.wm.shell.sysui.ShellInit;
@@ -65,7 +67,11 @@
     @Mock
     private WindowDecorViewModel mWindowDecorViewModel;
     @Mock
+    private SurfaceControl mMockSurfaceControl;
+    @Mock
     private DesktopModeTaskRepository mDesktopModeTaskRepository;
+    @Mock
+    private LaunchAdjacentController mLaunchAdjacentController;
     private FreeformTaskListener mFreeformTaskListener;
     private StaticMockitoSession mMockitoSession;
 
@@ -80,6 +86,7 @@
                 mShellInit,
                 mTaskOrganizer,
                 Optional.of(mDesktopModeTaskRepository),
+                mLaunchAdjacentController,
                 mWindowDecorViewModel);
     }
 
@@ -107,6 +114,31 @@
                 .addOrMoveFreeformTaskToTop(fullscreenTask.displayId, fullscreenTask.taskId);
     }
 
+    @Test
+    public void testVisibilityTaskChanged_visible_setLaunchAdjacentDisabled() {
+        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        verify(mLaunchAdjacentController).setLaunchAdjacentEnabled(false);
+    }
+
+    @Test
+    public void testVisibilityTaskChanged_NotVisible_setLaunchAdjacentEnabled() {
+        ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        task.isVisible = true;
+
+        mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
+
+        task.isVisible = false;
+        mFreeformTaskListener.onTaskInfoChanged(task);
+
+        verify(mLaunchAdjacentController).setLaunchAdjacentEnabled(true);
+    }
+
     @After
     public void tearDown() {
         mMockitoSession.finishMocking();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 8ad3d2a..7d063a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -48,10 +48,10 @@
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.shared.ShellSharedConstants;
 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.sysui.ShellSharedConstants;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 75d2145..6ddb678 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -67,10 +67,10 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.shared.ShellSharedConstants;
 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.sysui.ShellSharedConstants;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
deleted file mode 100644
index f3f3c37..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/PipTransitionStateTest.java
+++ /dev/null
@@ -1,115 +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.wm.shell.pip2;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcelable;
-import android.testing.AndroidTestingRunner;
-
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
-import com.android.wm.shell.pip2.phone.PipTransitionState;
-
-import junit.framework.Assert;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-/**
- * Unit test against {@link PhoneSizeSpecSource}.
- *
- * This test mocks the PiP2 flag to be true.
- */
-@RunWith(AndroidTestingRunner.class)
-public class PipTransitionStateTest extends ShellTestCase {
-    private static final String EXTRA_ENTRY_KEY = "extra_entry_key";
-    private PipTransitionState mPipTransitionState;
-    private PipTransitionState.PipTransitionStateChangedListener mStateChangedListener;
-    private Parcelable mEmptyParcelable;
-
-    @Mock
-    private Handler mMainHandler;
-
-    @Before
-    public void setUp() {
-        mPipTransitionState = new PipTransitionState(mMainHandler);
-        mPipTransitionState.setState(PipTransitionState.UNDEFINED);
-        mEmptyParcelable = new Bundle();
-    }
-
-    @Test
-    public void testEnteredState_withoutExtra() {
-        mStateChangedListener = (oldState, newState, extra) -> {
-            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
-            Assert.assertNull(extra);
-        };
-        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
-        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
-        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
-    }
-
-    @Test
-    public void testEnteredState_withExtra() {
-        mStateChangedListener = (oldState, newState, extra) -> {
-            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
-            Assert.assertNotNull(extra);
-            Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
-        };
-        Bundle extra = new Bundle();
-        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
-
-        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
-        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP, extra);
-        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testEnteringState_withoutExtra() {
-        mPipTransitionState.setState(PipTransitionState.ENTERING_PIP);
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testSwipingToPipState_withoutExtra() {
-        mPipTransitionState.setState(PipTransitionState.SWIPING_TO_PIP);
-    }
-
-    @Test
-    public void testCustomState_withExtra_thenEntered_withoutExtra() {
-        final int customState = mPipTransitionState.getCustomState();
-        mStateChangedListener = (oldState, newState, extra) -> {
-            if (newState == customState) {
-                Assert.assertNotNull(extra);
-                Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
-                return;
-            } else if (newState == PipTransitionState.ENTERED_PIP) {
-                Assert.assertNull(extra);
-                return;
-            }
-            Assert.fail("Neither custom not ENTERED_PIP state is received.");
-        };
-        Bundle extra = new Bundle();
-        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
-
-        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
-        mPipTransitionState.setState(customState, extra);
-        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
-        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
new file mode 100644
index 0000000..571ae93
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTransitionStateTest.java
@@ -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.wm.shell.pip2.phone;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.testing.AndroidTestingRunner;
+
+import com.android.wm.shell.ShellTestCase;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+/**
+ * Unit test against {@link PipTransitionState}.
+ *
+ * This test mocks the PiP2 flag to be true.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipTransitionStateTest extends ShellTestCase {
+    private static final String EXTRA_ENTRY_KEY = "extra_entry_key";
+    private PipTransitionState mPipTransitionState;
+    private PipTransitionState.PipTransitionStateChangedListener mStateChangedListener;
+    private Parcelable mEmptyParcelable;
+
+    @Mock
+    private Handler mMainHandler;
+
+    @Before
+    public void setUp() {
+        mPipTransitionState = new PipTransitionState(mMainHandler);
+        mPipTransitionState.setState(PipTransitionState.UNDEFINED);
+        mEmptyParcelable = new Bundle();
+    }
+
+    @Test
+    public void testEnteredState_withoutExtra() {
+        mStateChangedListener = (oldState, newState, extra) -> {
+            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
+            Assert.assertNull(extra);
+        };
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+
+    @Test
+    public void testEnteredState_withExtra() {
+        mStateChangedListener = (oldState, newState, extra) -> {
+            Assert.assertEquals(PipTransitionState.ENTERED_PIP, newState);
+            Assert.assertNotNull(extra);
+            Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
+        };
+        Bundle extra = new Bundle();
+        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
+
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP, extra);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testEnteringState_withoutExtra() {
+        mPipTransitionState.setState(PipTransitionState.ENTERING_PIP);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSwipingToPipState_withoutExtra() {
+        mPipTransitionState.setState(PipTransitionState.SWIPING_TO_PIP);
+    }
+
+    @Test
+    public void testCustomState_withExtra_thenEntered_withoutExtra() {
+        final int customState = mPipTransitionState.getCustomState();
+        mStateChangedListener = (oldState, newState, extra) -> {
+            if (newState == customState) {
+                Assert.assertNotNull(extra);
+                Assert.assertEquals(mEmptyParcelable, extra.getParcelable(EXTRA_ENTRY_KEY));
+                return;
+            } else if (newState == PipTransitionState.ENTERED_PIP) {
+                Assert.assertNull(extra);
+                return;
+            }
+            Assert.fail("Neither custom not ENTERED_PIP state is received.");
+        };
+        Bundle extra = new Bundle();
+        extra.putParcelable(EXTRA_ENTRY_KEY, mEmptyParcelable);
+
+        mPipTransitionState.addPipTransitionStateChangedListener(mStateChangedListener);
+        mPipTransitionState.setState(customState, extra);
+        mPipTransitionState.setState(PipTransitionState.ENTERED_PIP);
+        mPipTransitionState.removePipTransitionStateChangedListener(mStateChangedListener);
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
new file mode 100644
index 0000000..82cdfd5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipUiStateChangeControllerTests.java
@@ -0,0 +1,123 @@
+/*
+ * 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.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.Flags;
+import android.app.PictureInPictureUiState;
+import android.os.Bundle;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.AndroidTestingRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Consumer;
+
+/**
+ * Unit test against {@link PipUiStateChangeController}.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipUiStateChangeControllerTests {
+
+    @Mock
+    private PipTransitionState mPipTransitionState;
+
+    private Consumer<PictureInPictureUiState> mPictureInPictureUiStateConsumer;
+    private ArgumentCaptor<PictureInPictureUiState> mArgumentCaptor;
+
+    private PipUiStateChangeController mPipUiStateChangeController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mPipUiStateChangeController = new PipUiStateChangeController(mPipTransitionState);
+        mPictureInPictureUiStateConsumer = spy(pictureInPictureUiState -> {});
+        mPipUiStateChangeController.setPictureInPictureUiStateConsumer(
+                mPictureInPictureUiStateConsumer);
+        mArgumentCaptor = ArgumentCaptor.forClass(PictureInPictureUiState.class);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+    public void onPipTransitionStateChanged_swipePipStart_callbackIsTransitioningToPipTrue() {
+        when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+        mPipUiStateChangeController.onPipTransitionStateChanged(
+                PipTransitionState.UNDEFINED, PipTransitionState.SWIPING_TO_PIP, Bundle.EMPTY);
+
+        verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+        assertTrue(mArgumentCaptor.getValue().isTransitioningToPip());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+    public void onPipTransitionStateChanged_swipePipOngoing_noCallbackIsTransitioningToPip() {
+        when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+        mPipUiStateChangeController.onPipTransitionStateChanged(
+                PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
+
+        verifyZeroInteractions(mPictureInPictureUiStateConsumer);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+    public void onPipTransitionStateChanged_swipePipFinish_callbackIsTransitioningToPipFalse() {
+        when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(true);
+
+        mPipUiStateChangeController.onPipTransitionStateChanged(
+                PipTransitionState.SWIPING_TO_PIP, PipTransitionState.ENTERED_PIP, Bundle.EMPTY);
+
+        verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+        assertFalse(mArgumentCaptor.getValue().isTransitioningToPip());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+    public void onPipTransitionStateChanged_tapHomeStart_callbackIsTransitioningToPipTrue() {
+        when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(false);
+
+        mPipUiStateChangeController.onPipTransitionStateChanged(
+                PipTransitionState.UNDEFINED, PipTransitionState.ENTERING_PIP, Bundle.EMPTY);
+
+        verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+        assertTrue(mArgumentCaptor.getValue().isTransitioningToPip());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_PIP_UI_STATE_CALLBACK_ON_ENTERING)
+    public void onPipTransitionStateChanged_tapHomeFinish_callbackIsTransitioningToPipFalse() {
+        when(mPipTransitionState.isInSwipePipToHomeTransition()).thenReturn(false);
+
+        mPipUiStateChangeController.onPipTransitionStateChanged(
+                PipTransitionState.ENTERING_PIP, PipTransitionState.ENTERED_PIP, Bundle.EMPTY);
+
+        verify(mPictureInPictureUiStateConsumer).accept(mArgumentCaptor.capture());
+        assertFalse(mArgumentCaptor.getValue().isTransitioningToPip());
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
index 15b73c5..0c3f98a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedRecentTaskInfoTest.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.recents
 
 import android.app.ActivityManager
-import android.app.ActivityManager.RecentTaskInfo
 import android.graphics.Rect
 import android.os.Parcel
 import android.testing.AndroidTestingRunner
@@ -25,13 +24,13 @@
 import android.window.WindowContainerToken
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
-import com.android.wm.shell.util.GroupedRecentTaskInfo
-import com.android.wm.shell.util.GroupedRecentTaskInfo.CREATOR
-import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM
-import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE
-import com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT
-import com.android.wm.shell.util.SplitBounds
+import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedRecentTaskInfo.CREATOR
+import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM
+import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE
+import com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT
+import com.android.wm.shell.shared.split.SplitBounds
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50
 import com.google.common.truth.Correspondence
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index a0aab2e..386253c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -22,7 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -46,6 +46,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.KeyguardManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -68,13 +69,13 @@
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.TaskStackListenerImpl;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.ShellSharedConstants;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.split.SplitBounds;
 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.sysui.ShellSharedConstants;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
-import com.android.wm.shell.util.SplitBounds;
 
 import org.junit.After;
 import org.junit.Before;
@@ -136,6 +137,8 @@
 
         mMainExecutor = new TestShellExecutor();
         when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
+        when(mContext.getSystemService(KeyguardManager.class))
+                .thenReturn(mock(KeyguardManager.class));
         mShellInit = spy(new ShellInit(mMainExecutor));
         mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mDisplayInsetsController, mMainExecutor));
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
index b790aee..248393c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java
@@ -1,6 +1,6 @@
 package com.android.wm.shell.recents;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -12,7 +12,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.util.SplitBounds;
+import com.android.wm.shell.shared.split.SplitBounds;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
new file mode 100644
index 0000000..b9bf95b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleBarLocationTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.shared.bubbles
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.DEFAULT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation.RIGHT
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BubbleBarLocationTest : ShellTestCase() {
+
+    @Test
+    fun isOnLeft_rtlEnabled_defaultsToLeft() {
+        assertThat(DEFAULT.isOnLeft(isRtl = true)).isTrue()
+    }
+
+    @Test
+    fun isOnLeft_rtlDisabled_defaultsToRight() {
+        assertThat(DEFAULT.isOnLeft(isRtl = false)).isFalse()
+    }
+
+    @Test
+    fun isOnLeft_left_trueForAllLanguageDirections() {
+        assertThat(LEFT.isOnLeft(isRtl = false)).isTrue()
+        assertThat(LEFT.isOnLeft(isRtl = true)).isTrue()
+    }
+
+    @Test
+    fun isOnLeft_right_falseForAllLanguageDirections() {
+        assertThat(RIGHT.isOnLeft(isRtl = false)).isFalse()
+        assertThat(RIGHT.isOnLeft(isRtl = true)).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
new file mode 100644
index 0000000..641063c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/bubbles/BubbleInfoTest.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.shared.bubbles
+
+import android.os.Parcel
+import android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class BubbleInfoTest : ShellTestCase() {
+
+    @Test
+    fun bubbleInfo() {
+        val bubbleInfo =
+            BubbleInfo(
+                "key",
+                0,
+                "shortcut id",
+                null,
+                6,
+                "com.some.package",
+                "title",
+                "Some app",
+                true,
+                true
+            )
+        val parcel = Parcel.obtain()
+        bubbleInfo.writeToParcel(parcel, PARCELABLE_WRITE_RETURN_VALUE)
+        parcel.setDataPosition(0)
+
+        val bubbleInfoFromParcel = BubbleInfo.CREATOR.createFromParcel(parcel)
+
+        assertThat(bubbleInfo.key).isEqualTo(bubbleInfoFromParcel.key)
+        assertThat(bubbleInfo.flags).isEqualTo(bubbleInfoFromParcel.flags)
+        assertThat(bubbleInfo.shortcutId).isEqualTo(bubbleInfoFromParcel.shortcutId)
+        assertThat(bubbleInfo.icon).isEqualTo(bubbleInfoFromParcel.icon)
+        assertThat(bubbleInfo.userId).isEqualTo(bubbleInfoFromParcel.userId)
+        assertThat(bubbleInfo.packageName).isEqualTo(bubbleInfoFromParcel.packageName)
+        assertThat(bubbleInfo.title).isEqualTo(bubbleInfoFromParcel.title)
+        assertThat(bubbleInfo.appName).isEqualTo(bubbleInfoFromParcel.appName)
+        assertThat(bubbleInfo.isImportantConversation)
+            .isEqualTo(bubbleInfoFromParcel.isImportantConversation)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
new file mode 100644
index 0000000..d3e291f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/handles/RegionSamplingHelperTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.wm.shell.shared.handles
+
+
+import android.graphics.Rect
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.concurrent.futures.DirectExecutor
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+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.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+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
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.argumentCaptor
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RunWithLooper
+class RegionSamplingHelperTest : ShellTestCase() {
+
+    @Mock
+    lateinit var sampledView: View
+    @Mock
+    lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback
+    @Mock
+    lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener
+    @Mock
+    lateinit var viewRootImpl: ViewRootImpl
+    @Mock
+    lateinit var surfaceControl: SurfaceControl
+    @Mock
+    lateinit var wrappedSurfaceControl: SurfaceControl
+    @JvmField @Rule
+    var rule = MockitoJUnit.rule()
+    lateinit var regionSamplingHelper: RegionSamplingHelper
+
+    @Before
+    fun setup() {
+        whenever(sampledView.isAttachedToWindow).thenReturn(true)
+        whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl)
+        whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
+        whenever(surfaceControl.isValid).thenReturn(true)
+        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
+        whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
+        getInstrumentation().runOnMainSync(Runnable {
+            regionSamplingHelper = object : RegionSamplingHelper(
+                sampledView, samplingCallback,
+                DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener
+            ) {
+                override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+                    return wrappedSurfaceControl
+                }
+            }
+        })
+        regionSamplingHelper.setWindowVisible(true)
+    }
+
+    @Test
+    fun testStart_register() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_unregister() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.setWindowVisible(false)
+        verify(compositionListener).unregister(any())
+    }
+
+    @Test
+    fun testStart_hasBlur_neverRegisters() {
+        regionSamplingHelper.setWindowHasBlurs(true)
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener, never())
+            .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_stopAndDestroy() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.stopAndDestroy()
+        verify(compositionListener).unregister(any())
+    }
+
+    @Test
+    fun testCompositionSamplingListener_has_nonEmptyRect() {
+        // simulate race condition
+        val fakeExecutor = TestShellExecutor() // pass in as backgroundExecutor
+        val fakeSamplingCallback = mock(RegionSamplingHelper.SamplingCallback::class.java)
+
+        whenever(fakeSamplingCallback.isSamplingEnabled).thenReturn(true)
+        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
+        getInstrumentation().runOnMainSync(Runnable {
+            regionSamplingHelper = object : RegionSamplingHelper(
+                sampledView, fakeSamplingCallback,
+                DirectExecutor.INSTANCE, fakeExecutor, compositionListener
+            ) {
+                override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+                    return wrappedSurfaceControl
+                }
+            }
+        })
+        regionSamplingHelper.setWindowVisible(true)
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+
+        // make sure background task is enqueued
+        assertThat(fakeExecutor.getCallbacks().size).isEqualTo(1)
+
+        // make sure regionSamplingHelper will have empty Rect
+        whenever(fakeSamplingCallback.getSampledRegion(any())).thenReturn(Rect(0, 0, 0, 0))
+        regionSamplingHelper.onLayoutChange(sampledView, 0, 0, 0, 0, 0, 0, 0, 0)
+
+        // resume running of background thread
+        fakeExecutor.flushAll()
+
+        // grab Rect passed into compositionSamplingListener and make sure it's not empty
+        val argumentGrabber = argumentCaptor<Rect>()
+        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl),
+            argumentGrabber.capture())
+        assertThat(argumentGrabber.firstValue.isEmpty).isFalse()
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObjectTest.kt
new file mode 100644
index 0000000..8711ee0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/magnetictarget/MagnetizedObjectTest.kt
@@ -0,0 +1,526 @@
+/*
+ * 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.wm.shell.shared.magnetictarget
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.MotionEvent
+import android.view.View
+import androidx.dynamicanimation.animation.FloatPropertyCompat
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
+import org.junit.Assert.assertEquals
+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
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.Mockito
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
+
[email protected]
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MagnetizedObjectTest : ShellTestCase() {
+    /** Incrementing value for fake MotionEvent timestamps. */
+    private var time = 0L
+
+    /** Value to add to each new MotionEvent's timestamp. */
+    private var timeStep = 100
+
+    private val underlyingObject = this
+
+    private lateinit var targetView: View
+
+    private val targetSize = 200
+    private val targetCenterX = 500
+    private val targetCenterY = 900
+    private val magneticFieldRadius = 200
+
+    private var objectX = 0f
+    private var objectY = 0f
+    private val objectSize = 50f
+
+    private lateinit var magneticTarget: MagnetizedObject.MagneticTarget
+    private lateinit var magnetizedObject: MagnetizedObject<*>
+    private lateinit var magnetListener: MagnetizedObject.MagnetListener
+
+    private val xProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+            objectX = value
+        }
+        override fun getValue(target: MagnetizedObjectTest?): Float {
+            return objectX
+        }
+    }
+
+    private val yProperty = object : FloatPropertyCompat<MagnetizedObjectTest>("") {
+        override fun setValue(target: MagnetizedObjectTest?, value: Float) {
+            objectY = value
+        }
+
+        override fun getValue(target: MagnetizedObjectTest?): Float {
+            return objectY
+        }
+    }
+
+    @Before
+    fun setup() {
+        PhysicsAnimatorTestUtils.prepareForTest()
+
+        // Mock the view since a real view's getLocationOnScreen() won't work unless it's attached
+        // to a real window (it'll always return x = 0, y = 0).
+        targetView = mock(View::class.java)
+        `when`(targetView.context).thenReturn(context)
+
+        // The mock target view will pretend that it's 200x200, and at (400, 800). This means it's
+        // occupying the bounds (400, 800, 600, 1000) and it has a center of (500, 900).
+        `when`(targetView.width).thenReturn(targetSize) // width = 200
+        `when`(targetView.height).thenReturn(targetSize) // height = 200
+        doAnswer { invocation ->
+            (invocation.arguments[0] as IntArray).also { location ->
+                // Return the top left of the target.
+                location[0] = targetCenterX - targetSize / 2 // x = 400
+                location[1] = targetCenterY - targetSize / 2 // y = 800
+            }
+        }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any())
+        doAnswer { invocation ->
+            (invocation.arguments[0] as Runnable).run()
+            true
+        }.`when`(targetView).post(ArgumentMatchers.any())
+        `when`(targetView.context).thenReturn(context)
+
+        magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius)
+
+        magnetListener = mock(MagnetizedObject.MagnetListener::class.java)
+        magnetizedObject = object : MagnetizedObject<MagnetizedObjectTest>(
+                context, underlyingObject, xProperty, yProperty) {
+            override fun getWidth(underlyingObject: MagnetizedObjectTest): Float {
+                return objectSize
+            }
+
+            override fun getHeight(underlyingObject: MagnetizedObjectTest): Float {
+                return objectSize
+            }
+
+            override fun getLocationOnScreen(
+                underlyingObject: MagnetizedObjectTest,
+                loc: IntArray
+            ) {
+                loc[0] = objectX.toInt()
+                loc[1] = objectY.toInt() }
+        }
+
+        magnetizedObject.magnetListener = magnetListener
+        magnetizedObject.addTarget(magneticTarget)
+
+        timeStep = 100
+    }
+
+    @Test
+    fun testMotionEventConsumption() {
+        // Start at (0, 0). No magnetic field here.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0, action = MotionEvent.ACTION_DOWN)))
+
+        // Move to (400, 400), which is solidly outside the magnetic field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 200, y = 200)))
+
+        // Move to (305, 705). This would be in the magnetic field radius if magnetic fields were
+        // square. It's not, because they're not.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX - magneticFieldRadius + 5,
+                y = targetCenterY - magneticFieldRadius + 5)))
+
+        // Move to (400, 800). That's solidly in the radius so the magnetic target should begin
+        // consuming events.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX - 100,
+                y = targetCenterY - 100)))
+
+        // Release at (400, 800). Since we're in the magnetic target, it should return true and
+        // consume the ACTION_UP.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 400, y = 800, action = MotionEvent.ACTION_UP)))
+
+        // ACTION_DOWN outside the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 200, y = 200, action = MotionEvent.ACTION_DOWN)))
+
+        // Move to the center. We absolutely should consume events there.
+        assertTrue(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY)))
+
+        // Drag out to (0, 0) and we should be returning false again.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0)))
+
+        // The ACTION_UP event shouldn't be consumed either since it's outside the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = 0, y = 0, action = MotionEvent.ACTION_UP)))
+    }
+
+    @Test
+    fun testMotionEventConsumption_downInMagneticField() {
+        // We should not consume DOWN events even if they occur in the field.
+        assertFalse(magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_DOWN)))
+    }
+
+    @Test
+    fun testMoveIntoAroundAndOutOfMagneticField() {
+        // Move around but don't touch the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 100),
+                getMotionEvent(x = 200, y = 200))
+
+        // You can't become unstuck if you were never stuck in the first place.
+        verify(magnetListener, never()).onStuckToTarget(magneticTarget,
+                magnetizedObject)
+        verify(magnetListener, never()).onUnstuckFromTarget(
+                eq(magneticTarget), eq(magnetizedObject),
+                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+
+        // Move into and then around inside the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+                getMotionEvent(x = targetCenterX, y = targetCenterY),
+                getMotionEvent(x = targetCenterX + 100, y = targetCenterY + 100))
+
+        // We should only have received one call to onStuckToTarget and none to unstuck.
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
+        verify(magnetListener, never()).onUnstuckFromTarget(
+                eq(magneticTarget), eq(magnetizedObject),
+                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+
+        // Move out of the field and then release.
+        dispatchMotionEvents(
+                getMotionEvent(x = 100, y = 100),
+                getMotionEvent(x = 100, y = 100, action = MotionEvent.ACTION_UP))
+
+        // We should have received one unstuck call and no more stuck calls. We also should never
+        // have received an onReleasedInTarget call.
+        verify(magnetListener, times(1)).onUnstuckFromTarget(
+                eq(magneticTarget), eq(magnetizedObject),
+                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMoveIntoOutOfAndBackIntoMagneticField() {
+        // Move into the field
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX - magneticFieldRadius,
+                        y = targetCenterY - magneticFieldRadius,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX, y = targetCenterY))
+
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
+        verify(magnetListener, never()).onReleasedInTarget(magneticTarget, magnetizedObject)
+
+        // Move back out.
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX - magneticFieldRadius,
+                        y = targetCenterY - magneticFieldRadius))
+
+        verify(magnetListener, times(1)).onUnstuckFromTarget(
+                eq(magneticTarget),
+                eq(magnetizedObject),
+                ArgumentMatchers.anyFloat(), ArgumentMatchers.anyFloat(),
+                eq(false))
+        verify(magnetListener, never()).onReleasedInTarget(magneticTarget, magnetizedObject)
+
+        // Move in again and release in the magnetic field.
+        dispatchMotionEvents(
+                getMotionEvent(x = targetCenterX - 100, y = targetCenterY - 100),
+                getMotionEvent(x = targetCenterX + 50, y = targetCenterY + 50),
+                getMotionEvent(x = targetCenterX, y = targetCenterY),
+                getMotionEvent(
+                        x = targetCenterX, y = targetCenterY, action = MotionEvent.ACTION_UP))
+
+        verify(magnetListener, times(2)).onStuckToTarget(magneticTarget, magnetizedObject)
+        verify(magnetListener).onReleasedInTarget(magneticTarget, magnetizedObject)
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_towardsTarget() {
+        timeStep = 10
+
+        // Forcefully fling the object towards the target (but never touch the magnetic field).
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = 0,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX / 2,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        // Nevertheless it should have ended up stuck to the target.
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_towardsButTooSlow() {
+        // Very, very slowly fling the object towards the target (but never touch the magnetic
+        // field). This value is only used to create MotionEvent timestamps, it will not block the
+        // test for 10 seconds.
+        timeStep = 10000
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = targetCenterX,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        // No sticking should have occurred.
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testFlingTowardsTarget_missTarget() {
+        timeStep = 10
+        // Forcefully fling the object down, but not towards the target.
+        dispatchMotionEvents(
+                getMotionEvent(
+                        x = 0,
+                        y = 0,
+                        action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(
+                        x = 0,
+                        y = targetCenterY / 2),
+                getMotionEvent(
+                        x = 0,
+                        y = targetCenterY - magneticFieldRadius * 2,
+                        action = MotionEvent.ACTION_UP))
+
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMagnetAnimation() {
+        // Make sure the object starts at (0, 0).
+        assertEquals(0f, objectX)
+        assertEquals(0f, objectY)
+
+        // Trigger the magnet animation, and block the test until it ends.
+        PhysicsAnimatorTestUtils.setAllAnimationsBlock(true)
+        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX - 250,
+                y = targetCenterY - 250,
+                action = MotionEvent.ACTION_DOWN))
+
+        magnetizedObject.maybeConsumeMotionEvent(getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY))
+
+        // The object's (top-left) position should now position it centered over the target.
+        assertEquals(targetCenterX - objectSize / 2, objectX)
+        assertEquals(targetCenterY - objectSize / 2, objectY)
+    }
+
+    @Test
+    fun testMultipleTargets() {
+        val secondMagneticTarget = getSecondMagneticTarget()
+
+        // Drag into the second target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 900))
+
+        // Verify that we received an onStuck for the second target, and no others.
+        verify(magnetListener).onStuckToTarget(secondMagneticTarget, magnetizedObject)
+        verifyNoMoreInteractions(magnetListener)
+
+        // Drag into the original target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 0, y = 0),
+                getMotionEvent(x = 500, y = 900))
+
+        // We should have unstuck from the second one and stuck into the original one.
+        verify(magnetListener).onUnstuckFromTarget(
+                eq(secondMagneticTarget), eq(magnetizedObject),
+                anyFloat(), anyFloat(), eq(false))
+        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMultipleTargets_flingIntoSecond() {
+        val secondMagneticTarget = getSecondMagneticTarget()
+
+        timeStep = 10
+
+        // Fling towards the second target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 100, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 100, y = 350),
+                getMotionEvent(x = 100, y = 650, action = MotionEvent.ACTION_UP))
+
+        // Verify that we received an onStuck for the second target.
+        verify(magnetListener).onStuckToTarget(secondMagneticTarget, magnetizedObject)
+
+        // Fling towards the first target.
+        dispatchMotionEvents(
+                getMotionEvent(x = 300, y = 0, action = MotionEvent.ACTION_DOWN),
+                getMotionEvent(x = 400, y = 350),
+                getMotionEvent(x = 500, y = 650, action = MotionEvent.ACTION_UP))
+
+        // Verify that we received onStuck for the original target.
+        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
+    }
+
+    @Test
+    fun testMagneticTargetHasScreenOffset_moveIntoAndReleaseInTarget() {
+        magneticTarget.screenVerticalOffset = 500
+
+        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY))
+        // Moved into the target location, but it should be shifted due to screen offset.
+        // Should not get stuck.
+        verify(magnetListener, never()).onStuckToTarget(magneticTarget, magnetizedObject)
+
+        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY + 500))
+        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
+
+        dispatchMotionEvents(
+            getMotionEvent(
+                x = targetCenterX,
+                y = targetCenterY + 500,
+                action = MotionEvent.ACTION_UP
+            )
+        )
+
+        verify(magnetListener).onReleasedInTarget(magneticTarget, magnetizedObject)
+        verifyNoMoreInteractions(magnetListener)
+    }
+
+    @Test
+    fun testMagneticTargetHasScreenOffset_screenOffsetUpdates() {
+        magneticTarget.screenVerticalOffset = 500
+        val adjustedTargetCenter = targetCenterY + 500
+
+        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = adjustedTargetCenter))
+        dispatchMotionEvents(getMotionEvent(x = 0, y = 0))
+        verify(magnetListener).onStuckToTarget(magneticTarget, magnetizedObject)
+        verify(magnetListener)
+                .onUnstuckFromTarget(eq(magneticTarget), eq(magnetizedObject),
+                        anyFloat(), anyFloat(), anyBoolean())
+
+        // Offset if removed, we should now get stuck at the target location
+        magneticTarget.screenVerticalOffset = 0
+        dispatchMotionEvents(getMotionEvent(x = targetCenterX, y = targetCenterY))
+        verify(magnetListener, times(2)).onStuckToTarget(magneticTarget, magnetizedObject)
+    }
+
+    @Test
+    fun testMagneticTargetHasScreenOffset_flingTowardsTarget() {
+        timeStep = 10
+
+        magneticTarget.screenVerticalOffset = 500
+        val adjustedTargetCenter = targetCenterY + 500
+
+        // Forcefully fling the object towards the target (but never touch the magnetic field).
+        dispatchMotionEvents(
+            getMotionEvent(x = 0, y = 0, action = MotionEvent.ACTION_DOWN),
+            getMotionEvent(x = targetCenterX / 2, y = adjustedTargetCenter / 2),
+            getMotionEvent(
+                x = targetCenterX,
+                y = adjustedTargetCenter - magneticFieldRadius * 2,
+                action = MotionEvent.ACTION_UP
+            )
+        )
+
+        // Nevertheless it should have ended up stuck to the target.
+        verify(magnetListener, times(1)).onStuckToTarget(magneticTarget, magnetizedObject)
+    }
+
+    private fun getSecondMagneticTarget(): MagnetizedObject.MagneticTarget {
+        // The first target view is at bounds (400, 800, 600, 1000) and it has a center of
+        // (500, 900). We'll add a second one at bounds (0, 800, 200, 1000) with center (100, 900).
+        val secondTargetView = mock(View::class.java)
+        val secondTargetCenterX = 100
+        val secondTargetCenterY = 900
+
+        `when`(secondTargetView.context).thenReturn(context)
+        `when`(secondTargetView.width).thenReturn(targetSize) // width = 200
+        `when`(secondTargetView.height).thenReturn(targetSize) // height = 200
+        doAnswer { invocation ->
+            (invocation.arguments[0] as Runnable).run()
+            true
+        }.`when`(secondTargetView).post(ArgumentMatchers.any())
+        doAnswer { invocation ->
+            (invocation.arguments[0] as IntArray).also { location ->
+                // Return the top left of the target.
+                location[0] = secondTargetCenterX - targetSize / 2 // x = 0
+                location[1] = secondTargetCenterY - targetSize / 2 // y = 800
+            }
+        }.`when`(secondTargetView).getLocationOnScreen(ArgumentMatchers.any())
+
+        return magnetizedObject.addTarget(secondTargetView, magneticFieldRadius)
+    }
+
+    /**
+     * Return a MotionEvent at the given coordinates, with the given action (or MOVE by default).
+     * The event's time fields will be incremented by 10ms each time this is called, so tha
+     * VelocityTracker works.
+     */
+    private fun getMotionEvent(
+        x: Int,
+        y: Int,
+        action: Int = MotionEvent.ACTION_MOVE
+    ): MotionEvent {
+        return MotionEvent.obtain(time, time, action, x.toFloat(), y.toFloat(), 0)
+                .also { time += timeStep }
+    }
+
+    /** Dispatch all of the provided events to the target view. */
+    private fun dispatchMotionEvents(vararg events: MotionEvent) {
+        events.forEach { magnetizedObject.maybeConsumeMotionEvent(it) }
+    }
+
+    /** Prevents Kotlin from being mad that eq() is nullable. */
+    private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/split/SplitScreenConstantsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/split/SplitScreenConstantsTest.kt
new file mode 100644
index 0000000..19c18be
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/split/SplitScreenConstantsTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.wm.shell.shared.split.SplitScreenConstants
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SplitScreenConstantsTest {
+
+    /**
+     * Ensures that some important constants are not changed from their set values. These values
+     * are persisted in user-defined app pairs, and changing them will break things.
+     */
+    @Test
+    fun shouldKeepExistingConstantValues() {
+        assertEquals(
+            "the value of SPLIT_POSITION_TOP_OR_LEFT should be 0",
+            0,
+            SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT,
+        )
+        assertEquals(
+            "the value of SPLIT_POSITION_BOTTOM_OR_RIGHT should be 1",
+            1,
+            SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT,
+        )
+        assertEquals(
+            "the value of SNAP_TO_30_70 should be 0",
+            0,
+            SplitScreenConstants.SNAP_TO_30_70,
+        )
+        assertEquals(
+            "the value of SNAP_TO_50_50 should be 1",
+            1,
+            SplitScreenConstants.SNAP_TO_50_50,
+        )
+        assertEquals(
+            "the value of SNAP_TO_70_30 should be 2",
+            2,
+            SplitScreenConstants.SNAP_TO_70_30,
+        )
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
deleted file mode 100644
index 549bd3f..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java
+++ /dev/null
@@ -1,93 +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.wm.shell.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-
-import java.util.Optional;
-
-/** Tests for {@link SideStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class SideStageTests extends ShellTestCase {
-    @Mock private ShellTaskOrganizer mTaskOrganizer;
-    @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
-    @Mock private SyncTransactionQueue mSyncQueue;
-    @Mock private ActivityManager.RunningTaskInfo mRootTask;
-    @Mock private SurfaceControl mRootLeash;
-    @Mock private IconProvider mIconProvider;
-    @Spy private WindowContainerTransaction mWct;
-    private SurfaceSession mSurfaceSession = new SurfaceSession();
-    private SideStage mSideStage;
-
-    @Before
-    @UiThreadTest
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mRootTask = new TestRunningTaskInfoBuilder().build();
-        mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
-                mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
-        mSideStage.onTaskAppeared(mRootTask, mRootLeash);
-    }
-
-    @Test
-    public void testAddTask() {
-        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-
-        mSideStage.addTask(task, mWct);
-
-        verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
-    }
-
-    @Test
-    public void testRemoveTask() {
-        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
-        assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isFalse();
-
-        mSideStage.mChildrenTaskInfo.put(task.taskId, task);
-        assertThat(mSideStage.removeTask(task.taskId, null, mWct)).isTrue();
-        verify(mWct).reparent(eq(task.token), isNull(), eq(false));
-    }
-}
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 1c5d5e9..9260a07 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
@@ -23,8 +23,8 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+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 org.junit.Assert.assertEquals;
 import static org.junit.Assume.assumeTrue;
@@ -70,14 +70,14 @@
 import com.android.wm.shell.common.MultiInstanceHelper;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.ShellSharedConstants;
+import com.android.wm.shell.shared.TransactionPool;
 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.sysui.ShellSharedConstants;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 29d3fb4..4de2278 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -35,9 +35,9 @@
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitLayout;
 import com.android.wm.shell.recents.RecentTasksController;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
@@ -74,7 +74,7 @@
         final SurfaceControl mRootLeash;
 
         TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
-                ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage,
+                ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage,
                 DisplayController displayController, DisplayImeController imeController,
                 DisplayInsetsController insetsController, SplitLayout splitLayout,
                 Transitions transitions, TransactionPool transactionPool,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 22b408c..e167433 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -76,9 +76,9 @@
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.transition.DefaultMixedHandler;
 import com.android.wm.shell.transition.TestRemoteTransition;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
@@ -117,7 +117,7 @@
     private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
     private SplitLayout mSplitLayout;
     private MainStage mMainStage;
-    private SideStage mSideStage;
+    private StageTaskListener mSideStage;
     private StageCoordinator mStageCoordinator;
     private SplitScreenTransitions mSplitScreenTransitions;
 
@@ -137,7 +137,7 @@
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
                 mIconProvider, Optional.of(mWindowDecorViewModel)));
         mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
-        mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+        mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
                 mIconProvider, Optional.of(mWindowDecorViewModel)));
         mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
@@ -219,7 +219,7 @@
 
     @Test
     @UiThreadTest
-    public void testRemoteTransitionConsumed() {
+    public void testRemoteTransitionConsumedForStartAnimation() {
         // Omit side child change
         TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
                 .addChange(TRANSIT_OPEN, mMainChild)
@@ -238,7 +238,30 @@
         assertTrue(accepted);
 
         assertTrue(testRemote.isConsumed());
+    }
 
+    @Test
+    @UiThreadTest
+    public void testRemoteTransitionConsumed() {
+        // Omit side child change
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+                .addChange(TRANSIT_OPEN, mMainChild)
+                .build();
+        TestRemoteTransition testRemote = new TestRemoteTransition();
+
+        IBinder transition = mSplitScreenTransitions.startEnterTransition(
+                TRANSIT_OPEN, new WindowContainerTransaction(),
+                new RemoteTransition(testRemote, "Test"), mStageCoordinator,
+                TRANSIT_SPLIT_SCREEN_PAIR_OPEN, false);
+        mMainStage.onTaskAppeared(mMainChild, createMockSurface());
+        mStageCoordinator.startAnimation(transition, info,
+                mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class),
+                mock(Transitions.TransitionFinishCallback.class));
+        mStageCoordinator.onTransitionConsumed(transition, false /*aborted*/,
+                mock(SurfaceControl.Transaction.class));
+
+        assertTrue(testRemote.isConsumed());
     }
 
     @Test
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 ff6c7ee..c9e1414 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
@@ -19,13 +19,12 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
-import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_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;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
 import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
 import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -69,9 +68,9 @@
 import com.android.wm.shell.common.LaunchAdjacentController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.split.SplitDecorManager;
 import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -100,7 +99,7 @@
     @Mock
     private MainStage mMainStage;
     @Mock
-    private SideStage mSideStage;
+    private StageTaskListener mSideStage;
     @Mock
     private SplitLayout mSplitLayout;
     @Mock
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 946a7ef..acd612eb 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
@@ -25,6 +25,7 @@
 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.verify;
 
@@ -52,6 +53,7 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.util.Optional;
 
@@ -76,6 +78,8 @@
     private IconProvider mIconProvider;
     @Mock
     private WindowDecorViewModel mWindowDecorViewModel;
+    @Spy
+    private WindowContainerTransaction mWct;
     @Captor
     private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor;
     private SurfaceSession mSurfaceSession = new SurfaceSession();
@@ -177,4 +181,22 @@
         mStageTaskListener.evictAllChildren(wct);
         assertFalse(wct.isEmpty());
     }
+
+    @Test
+    public void testAddTask() {
+        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+        mStageTaskListener.addTask(task, mWct);
+
+        verify(mWct).reparent(eq(task.token), eq(mRootTask.token), eq(true));
+    }
+
+    @Test
+    public void testRemoveTask() {
+        final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
+        assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isFalse();
+
+        mStageTaskListener.mChildrenTaskInfo.put(task.taskId, task);
+        assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isTrue();
+        verify(mWct).reparent(eq(task.token), isNull(), eq(false));
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index af6c077..5f75423 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -74,7 +74,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
index ff76a2f..7fd1c11 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
@@ -42,11 +42,11 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.ShellSharedConstants;
+import com.android.wm.shell.shared.TransactionPool;
 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.sysui.ShellSharedConstants;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 0434742..1984885 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -294,16 +294,6 @@
     }
 
     @Test
-    public void testUnsetOnBackPressedOnTaskRoot_legacyTransitions() {
-        assumeFalse(Transitions.ENABLE_SHELL_TRANSITIONS);
-        mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash);
-        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
-        mTaskViewTaskController.onTaskVanished(mTaskInfo);
-        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
-    }
-
-    @Test
     public void testOnNewTask_noSurface() {
         assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
         WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -443,19 +433,6 @@
     }
 
     @Test
-    public void testUnsetOnBackPressedOnTaskRoot() {
-        assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS);
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
-                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
-                mLeash, wct);
-        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(true));
-
-        mTaskViewTaskController.prepareCloseAnimation();
-        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mTaskInfo.token), eq(false));
-    }
-
-    @Test
     public void testSetObscuredTouchRect() {
         mTaskView.setObscuredTouchRect(
                 new Rect(/* left= */ 0, /* top= */ 10, /* right= */ 100, /* bottom= */ 120));
@@ -713,4 +690,26 @@
         verify(mViewHandler).post(any());
         verify(mTaskView).setResizeBackgroundColor(eq(Color.BLUE));
     }
+
+    @Test
+    public void testOnAppeared_setsTrimmableTask() {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+                mLeash, wct);
+
+        assertThat(wct.getHierarchyOps().get(0).isTrimmableFromRecents()).isFalse();
+    }
+
+    @Test
+    public void testMoveToFullscreen_callsTaskRemovalStarted() {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTaskViewTaskController.prepareOpenAnimation(true /* newTask */,
+                new SurfaceControl.Transaction(), new SurfaceControl.Transaction(), mTaskInfo,
+                mLeash, wct);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskViewTaskController.moveToFullscreen();
+
+        verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
index 6bc7e49..0c18229 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -48,7 +48,7 @@
 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.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.After;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 0db10ef..f51a960 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -38,6 +38,9 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionInfo.TransitionMode;
@@ -46,17 +49,19 @@
 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.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.shared.IHomeTransitionListener;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -70,6 +75,8 @@
 @RunWith(AndroidJUnit4.class)
 public class HomeTransitionObserverTest extends ShellTestCase {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
     private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
     private final TransactionPool mTransactionPool = mock(TransactionPool.class);
     private final Context mContext =
@@ -187,6 +194,7 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
     public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException {
         TransitionInfo info = mock(TransitionInfo.class);
         TransitionInfo.Change change = mock(TransitionInfo.Change.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java
index 574a87a..a5a27e2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/MockTransactionPool.java
@@ -21,7 +21,7 @@
 
 import android.view.SurfaceControl;
 
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.util.StubTransaction;
 
 public class MockTransactionPool extends TransactionPool {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 81e6d07..7c63fda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -107,12 +107,12 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.recents.RecentTasksController;
 import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.ShellSharedConstants;
+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.sysui.ShellSharedConstants;
 import com.android.wm.shell.util.StubTransaction;
 
 import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
index 8196c5a..8fe0c38 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
@@ -35,7 +35,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
 import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
 
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 acc0bce..cf2de91 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
@@ -40,7 +40,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.shared.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.TransitionInfoBuilder;
 import com.android.wm.shell.transition.Transitions;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt
new file mode 100644
index 0000000..def4b91
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationProtoUtils.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.util
+
+import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
+
+/**
+ * Constructs a [WindowingEducationProto] object, populating its fields with the provided
+ * parameters.
+ *
+ * Any fields without corresponding parameters will retain their default values.
+ */
+fun createWindowingEducationProto(
+    educationViewedTimestampMillis: Long? = null,
+    featureUsedTimestampMillis: Long? = null,
+    appUsageStats: Map<String, Int>? = null,
+    appUsageStatsLastUpdateTimestampMillis: Long? = null
+): WindowingEducationProto =
+    WindowingEducationProto.newBuilder()
+        .apply {
+          if (educationViewedTimestampMillis != null) {
+            setEducationViewedTimestampMillis(educationViewedTimestampMillis)
+          }
+          if (featureUsedTimestampMillis != null) {
+            setFeatureUsedTimestampMillis(featureUsedTimestampMillis)
+          }
+          setAppHandleEducation(
+              createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis))
+        }
+        .build()
+
+/**
+ * Constructs a [WindowingEducationProto.AppHandleEducation] object, populating its fields with the
+ * provided parameters.
+ *
+ * Any fields without corresponding parameters will retain their default values.
+ */
+fun createAppHandleEducationProto(
+    appUsageStats: Map<String, Int>? = null,
+    appUsageStatsLastUpdateTimestampMillis: Long? = null
+): WindowingEducationProto.AppHandleEducation =
+    WindowingEducationProto.AppHandleEducation.newBuilder()
+        .apply {
+          if (appUsageStats != null) putAllAppUsageStats(appUsageStats)
+          if (appUsageStatsLastUpdateTimestampMillis != null) {
+            setAppUsageStatsLastUpdateTimestampMillis(appUsageStatsLastUpdateTimestampMillis)
+          }
+        }
+        .build()
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 6d68797..be0549b 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
@@ -34,6 +34,7 @@
 import android.hardware.input.InputManager
 import android.net.Uri
 import android.os.Handler
+import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.CheckFlagsRule
@@ -56,7 +57,9 @@
 import android.view.SurfaceView
 import android.view.View
 import android.view.WindowInsets.Type.statusBars
+import android.widget.Toast
 import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -77,11 +80,13 @@
 import com.android.wm.shell.common.MultiInstanceHelper
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
+import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.DesktopTasksLimiter
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.sysui.ShellCommandHandler
 import com.android.wm.shell.sysui.ShellController
@@ -92,6 +97,8 @@
 import java.util.Optional
 import java.util.function.Consumer
 import java.util.function.Supplier
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Before
@@ -106,8 +113,9 @@
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
+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
@@ -116,8 +124,7 @@
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
+
 
 /**
  * Tests of [DesktopModeWindowDecorViewModel]
@@ -157,8 +164,14 @@
     @Mock private lateinit var mockWindowManager: IWindowManager
     @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
     @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
+    @Mock private lateinit var mockUserHandle: UserHandle
+    @Mock private lateinit var mockToast: Toast
     private val bgExecutor = TestShellExecutor()
     @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
+    @Mock private lateinit var mockTasksLimiter: DesktopTasksLimiter
+    @Mock private lateinit var mockFreeformTaskTransitionStarter: FreeformTaskTransitionStarter
+    @Mock private lateinit var mockActivityOrientationChangeHandler:
+            DesktopActivityOrientationChangeHandler
     private lateinit var spyContext: TestableContext
 
     private val transactionFactory = Supplier<SurfaceControl.Transaction> {
@@ -180,6 +193,7 @@
                 .strictness(Strictness.LENIENT)
                 .spyStatic(DesktopModeStatus::class.java)
                 .spyStatic(DragPositioningCallbackUtility::class.java)
+                .spyStatic(Toast::class.java)
                 .startMocking()
         doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(Mockito.any()) }
 
@@ -210,13 +224,17 @@
                 transactionFactory,
                 mockRootTaskDisplayAreaOrganizer,
                 windowDecorByTaskIdSpy,
-                mockInteractionJankMonitor
+                mockInteractionJankMonitor,
+                Optional.of(mockTasksLimiter),
+                Optional.of(mockActivityOrientationChangeHandler)
         )
         desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
         whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
         whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS)
         whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor)
 
+        doReturn(mockToast).`when` { Toast.makeText(any(), anyInt(), anyInt()) }
+
         // InputChannel cannot be mocked because it passes to InputEventReceiver.
         val inputChannels = InputChannel.openInputChannelPair(TAG)
         inputChannels.first().dispose()
@@ -283,11 +301,8 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     fun testCreateAndDisposeEventReceiver() {
-        val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        setUpMockDecorationForTask(task)
-
-        onTaskOpening(task)
-        desktopModeWindowDecorViewModel.destroyWindowDecoration(task)
+        val decor = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+        desktopModeWindowDecorViewModel.destroyWindowDecoration(decor.mTaskInfo)
 
         verify(mockInputMonitorFactory).create(any(), any())
         verify(mockInputMonitor).dispose()
@@ -342,9 +357,8 @@
         val inputManager = mock(InputManager::class.java)
         spyContext.addMockSystemService(InputManager::class.java, inputManager)
 
-        val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java)
         desktopModeWindowDecorViewModel
-                .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter)
+                .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)
 
         onClickListener.onClick(view)
 
@@ -356,6 +370,65 @@
     }
 
     @Test
+    fun testCloseButtonInFreeform_closeWindow() {
+        val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
+                as ArgumentCaptor<View.OnClickListener>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onCaptionButtonClickListener = onClickListenerCaptor
+        )
+
+        val view = mock(View::class.java)
+        whenever(view.id).thenReturn(R.id.close_window)
+
+        desktopModeWindowDecorViewModel
+            .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)
+
+        onClickListenerCaptor.value.onClick(view)
+
+        val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
+        verify(mockFreeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
+        val wct = transactionCaptor.firstValue
+
+        assertEquals(1, wct.getHierarchyOps().size)
+        assertEquals(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK,
+                     wct.getHierarchyOps().get(0).getType())
+        assertEquals(decor.mTaskInfo.token.asBinder(), wct.getHierarchyOps().get(0).getContainer())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_MINIMIZE_BUTTON)
+    fun testMinimizeButtonInFreefrom_minimizeWindow() {
+        val onClickListenerCaptor = forClass(View.OnClickListener::class.java)
+                as ArgumentCaptor<View.OnClickListener>
+        val decor = createOpenTaskDecoration(
+            windowingMode = WINDOWING_MODE_FREEFORM,
+            onCaptionButtonClickListener = onClickListenerCaptor
+        )
+
+        val view = mock(View::class.java)
+        whenever(view.id).thenReturn(R.id.minimize_window)
+
+        desktopModeWindowDecorViewModel
+            .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)
+
+        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())
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODALS_POLICY)
     fun testDecorationIsCreatedForTopTranslucentActivitiesWithStyleFloating() {
         val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true).apply {
@@ -427,15 +500,9 @@
 
     @Test
     fun testKeyguardState_notifiesAllDecors() {
-        val task1 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration1 = setUpMockDecorationForTask(task1)
-        onTaskOpening(task1)
-        val task2 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration2 = setUpMockDecorationForTask(task2)
-        onTaskOpening(task2)
-        val task3 = createTask(windowingMode = WINDOWING_MODE_FREEFORM)
-        val decoration3 = setUpMockDecorationForTask(task3)
-        onTaskOpening(task3)
+        val decoration1 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+        val decoration2 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
+        val decoration3 = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FREEFORM)
 
         desktopModeOnKeyguardChangedListener
             .onKeyguardVisibilityChanged(true /* visible */, true /* occluded */,
@@ -533,6 +600,7 @@
 
     @Test
     fun testOnDecorSnappedLeft_snapResizes() {
+        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
         val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -543,8 +611,13 @@
         val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
         onLeftSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController)
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+        verify(mockDesktopTasksController).snapToHalfScreen(
+            eq(decor.mTaskInfo),
+            taskSurfaceCaptor.capture(),
+            eq(currentBounds),
+            eq(SnapPosition.LEFT)
+        )
+        assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)
     }
 
     @Test
@@ -565,6 +638,7 @@
     @Test
     @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
     fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() {
+        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
         val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -575,8 +649,13 @@
         val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
         onLeftSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController)
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+        verify(mockDesktopTasksController).snapToHalfScreen(
+            eq(decor.mTaskInfo),
+            taskSurfaceCaptor.capture(),
+            eq(currentBounds),
+            eq(SnapPosition.LEFT)
+        )
+        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
 
     @Test
@@ -593,11 +672,13 @@
         onLeftSnapClickListenerCaptor.value.invoke()
 
         verify(mockDesktopTasksController, never())
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT)
+            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT))
+        verify(mockToast).show()
     }
 
     @Test
     fun testOnDecorSnappedRight_snapResizes() {
+        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
         val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -608,8 +689,13 @@
         val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
         onRightSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController)
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+        verify(mockDesktopTasksController).snapToHalfScreen(
+            eq(decor.mTaskInfo),
+            taskSurfaceCaptor.capture(),
+            eq(currentBounds),
+            eq(SnapPosition.RIGHT)
+        )
+        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
 
     @Test
@@ -630,6 +716,7 @@
     @Test
     @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)
     fun testOnSnapResizeRight_nonResizable_decorSnappedRight() {
+        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()
         val onRightSnapClickListenerCaptor = forClass(Function0::class.java)
                 as ArgumentCaptor<Function0<Unit>>
         val decor = createOpenTaskDecoration(
@@ -640,8 +727,13 @@
         val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds
         onRightSnapClickListenerCaptor.value.invoke()
 
-        verify(mockDesktopTasksController)
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+        verify(mockDesktopTasksController).snapToHalfScreen(
+            eq(decor.mTaskInfo),
+            taskSurfaceCaptor.capture(),
+            eq(currentBounds),
+            eq(SnapPosition.RIGHT)
+        )
+        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)
     }
 
     @Test
@@ -658,7 +750,8 @@
         onRightSnapClickListenerCaptor.value.invoke()
 
         verify(mockDesktopTasksController, never())
-            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT)
+            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT))
+        verify(mockToast).show()
     }
 
     @Test
@@ -825,12 +918,12 @@
 
         openInBrowserListenerCaptor.value.accept(uri)
 
-        verify(spyContext).startActivity(argThat { intent ->
+        verify(spyContext).startActivityAsUser(argThat { intent ->
             intent.data == uri
                     && ((intent.flags and Intent.FLAG_ACTIVITY_NEW_TASK) != 0)
                     && intent.categories.contains(Intent.CATEGORY_LAUNCHER)
                     && intent.action == Intent.ACTION_MAIN
-        })
+        }, eq(mockUserHandle))
     }
 
     @Test
@@ -964,6 +1057,7 @@
 
     private fun createOpenTaskDecoration(
         @WindowingMode windowingMode: Int,
+        taskSurface: SurfaceControl = SurfaceControl(),
         onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
             forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
         onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -978,9 +1072,11 @@
             forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
         onOpenInBrowserClickListener: ArgumentCaptor<Consumer<Uri>> =
             forClass(Consumer::class.java) as ArgumentCaptor<Consumer<Uri>>,
+        onCaptionButtonClickListener: ArgumentCaptor<View.OnClickListener> =
+            forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>
     ): DesktopModeWindowDecoration {
         val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode))
-        onTaskOpening(decor.mTaskInfo)
+        onTaskOpening(decor.mTaskInfo, taskSurface)
         verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
         verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
         verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
@@ -988,6 +1084,8 @@
         verify(decor).setOnToFullscreenClickListener(onToFullscreenClickListenerCaptor.capture())
         verify(decor).setOnToSplitScreenClickListener(onToSplitScreenClickListenerCaptor.capture())
         verify(decor).setOpenInBrowserClickListener(onOpenInBrowserClickListener.capture())
+        verify(decor).setCaptionListeners(
+                onCaptionButtonClickListener.capture(), any(), any(), any())
         return decor
     }
 
@@ -1037,6 +1135,7 @@
         ).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))
                 .thenReturn(true)
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 596adfb..258c860 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
@@ -173,9 +173,12 @@
     @Mock
     private AppToWebGenericLinksParser mMockGenericLinksParser;
     @Mock
+    private WindowManager mMockWindowManager;
+    @Mock
     private HandleMenu mMockHandleMenu;
     @Mock
     private HandleMenuFactory mMockHandleMenuFactory;
+    @Mock
     private MultiInstanceHelper mMockMultiInstanceHelper;
     @Captor
     private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@@ -220,9 +223,10 @@
         final Display defaultDisplay = mock(Display.class);
         doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY);
         doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
-        when(mMockHandleMenuFactory.create(any(), anyInt(), any(), any(),
+        when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(),
                 any(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), anyInt()))
                 .thenReturn(mMockHandleMenu);
+        when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
     }
 
     @After
@@ -385,6 +389,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void updateRelayoutParams_fullscreen_inputChannelNotNeeded() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -401,6 +406,7 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void updateRelayoutParams_multiwindow_inputChannelNotNeeded() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -491,7 +497,7 @@
                 .isTrue();
     }
 
-    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
+    @Test
     public void relayout_fullscreenTask_appliesTransactionImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -518,7 +524,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -530,7 +535,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void relayout_fullscreenTask_postsViewHostCreation() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -559,7 +563,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void relayout_removesExistingHandlerCallback() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -574,7 +577,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
     public void close_removesExistingHandlerCallback() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
@@ -826,7 +828,7 @@
     }
 
     private void verifyHandleMenuCreated(@Nullable Uri uri) {
-        verify(mMockHandleMenuFactory).create(any(), anyInt(), any(), any(),
+        verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(),
                 any(), anyBoolean(), anyBoolean(), eq(uri), anyInt(), anyInt(), anyInt());
     }
 
@@ -894,8 +896,9 @@
                 mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
                 mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
                 WindowContainerTransaction::new, SurfaceControl::new,
+                new WindowManagerWrapper(mMockWindowManager),
                 mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
-                        mMockMultiInstanceHelper);
+                mMockMultiInstanceHelper);
         windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
                 mMockTouchEventListener, mMockTouchEventListener);
         windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index e529711..1f33ae6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -93,7 +93,7 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
         mockitoSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
-                .spyStatic(DesktopModeStatus::class.java).startMocking()
+            .spyStatic(DesktopModeStatus::class.java).startMocking()
 
         whenever(taskToken.asBinder()).thenReturn(taskBinder)
         whenever(mockDisplayController.getDisplayLayout(DISPLAY_ID)).thenReturn(mockDisplayLayout)
@@ -108,9 +108,9 @@
         whenever(mockContext.getResources()).thenReturn(mockResources)
         whenever(mockWindowDecoration.mDecorWindowContext.resources).thenReturn(mockResources)
         whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_width))
-                .thenReturn(DESKTOP_MODE_MIN_WIDTH)
+            .thenReturn(DESKTOP_MODE_MIN_WIDTH)
         whenever(mockResources.getDimensionPixelSize(R.dimen.desktop_mode_minimum_window_height))
-                .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
+            .thenReturn(DESKTOP_MODE_MIN_HEIGHT)
         whenever(mockDisplay.displayId).thenAnswer { DISPLAY_ID }
     }
 
@@ -129,9 +129,11 @@
         val newY = STARTING_BOUNDS.top.toFloat() + 95
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -149,9 +151,11 @@
         val newY = STARTING_BOUNDS.top.toFloat() + 5
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 5)
@@ -169,9 +173,11 @@
         val newY = STARTING_BOUNDS.top.toFloat() + 105
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
 
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
@@ -189,9 +195,11 @@
         val newY = STARTING_BOUNDS.top.toFloat() + 80
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top + 80)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
@@ -208,9 +216,11 @@
 
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_TOP,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
@@ -221,52 +231,95 @@
     fun testDragEndSnapsTaskBoundsWhenOutsideValidDragArea() {
         val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.top.toFloat())
         val repositionTaskBounds = Rect(STARTING_BOUNDS)
-        val validDragArea = Rect(DISPLAY_BOUNDS.left - 100,
+        val validDragArea = Rect(
+            DISPLAY_BOUNDS.left - 100,
             STABLE_BOUNDS.top,
             DISPLAY_BOUNDS.right - 100,
-            DISPLAY_BOUNDS.bottom - 100)
+            DISPLAY_BOUNDS.bottom - 100
+        )
 
-        DragPositioningCallbackUtility.updateTaskBounds(repositionTaskBounds, STARTING_BOUNDS,
-            startingPoint, startingPoint.x - 1000, (DISPLAY_BOUNDS.bottom + 1000).toFloat())
-        DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(repositionTaskBounds,
-            validDragArea)
+        DragPositioningCallbackUtility.updateTaskBounds(
+            repositionTaskBounds, STARTING_BOUNDS,
+            startingPoint, startingPoint.x - 1000, (DISPLAY_BOUNDS.bottom + 1000).toFloat()
+        )
+        DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
+            repositionTaskBounds,
+            validDragArea
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(validDragArea.left)
         assertThat(repositionTaskBounds.top).isEqualTo(validDragArea.bottom)
         assertThat(repositionTaskBounds.right)
-                .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
+            .isEqualTo(validDragArea.left + STARTING_BOUNDS.width())
         assertThat(repositionTaskBounds.bottom)
-                .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
+            .isEqualTo(validDragArea.bottom + STARTING_BOUNDS.height())
     }
 
     @Test
     fun testChangeBounds_toDisallowedBounds_freezesAtLimit() {
-        val startingPoint = PointF(STARTING_BOUNDS.right.toFloat(),
-            STARTING_BOUNDS.bottom.toFloat())
+        val startingPoint = PointF(
+            STARTING_BOUNDS.right.toFloat(),
+            STARTING_BOUNDS.bottom.toFloat()
+        )
         val repositionTaskBounds = Rect(STARTING_BOUNDS)
         // Initial resize to width and height 110px.
         var newX = STARTING_BOUNDS.right.toFloat() + 10
         var newY = STARTING_BOUNDS.bottom.toFloat() + 10
         var delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
-        assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
-            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration))
+        assertTrue(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
         // Resize width to 120px, height to disallowed area which should not result in a change.
         newX += 10
         newY = DISALLOWED_RESIZE_AREA.top.toFloat()
         delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
-        assertTrue(DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
-            repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration))
+        assertTrue(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right + 20)
-        assertThat(repositionTaskBounds.bottom).isEqualTo(STARTING_BOUNDS.bottom + 10)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STABLE_BOUNDS.bottom)
+    }
+
+
+    @Test
+    fun testChangeBounds_beyondStableBounds_freezesAtStableBounds() {
+        val startingPoint = PointF(
+            STARTING_BOUNDS.right.toFloat(),
+            STARTING_BOUNDS.bottom.toFloat()
+        )
+        val repositionTaskBounds = Rect(STARTING_BOUNDS)
+
+        // Resize to beyond stable bounds.
+        val newX = STARTING_BOUNDS.right.toFloat() + STABLE_BOUNDS.width()
+        val newY = STARTING_BOUNDS.bottom.toFloat() + STABLE_BOUNDS.height()
+
+        val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
+        assertTrue(
+            DragPositioningCallbackUtility.changeBounds(
+                CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+                repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
+                mockWindowDecoration
+            )
+        )
+        assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
+        assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
+        assertThat(repositionTaskBounds.right).isEqualTo(STABLE_BOUNDS.right)
+        assertThat(repositionTaskBounds.bottom).isEqualTo(STABLE_BOUNDS.bottom)
     }
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeLessThanMin_shouldNotChangeBounds() {
-        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
+        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) }
         initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
         val startingPoint =
             PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
@@ -277,9 +330,11 @@
         val newY = STARTING_BOUNDS.bottom.toFloat() - 99
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
@@ -289,7 +344,7 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun taskMinWidthHeightUndefined_changeBoundsInDesktopModeAllowedSize_shouldChangeBounds() {
-        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
+        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) }
         initializeTaskInfo(taskMinWidth = -1, taskMinHeight = -1)
         val startingPoint =
             PointF(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat())
@@ -300,9 +355,11 @@
         val newY = STARTING_BOUNDS.bottom.toFloat() - 80
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 80)
@@ -321,9 +378,11 @@
         val newY = STARTING_BOUNDS.bottom.toFloat() - 99
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right)
@@ -342,9 +401,11 @@
         val newY = STARTING_BOUNDS.bottom.toFloat() - 50
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, STARTING_BOUNDS, STABLE_BOUNDS, delta, mockDisplayController,
-            mockWindowDecoration)
+            mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.left).isEqualTo(STARTING_BOUNDS.left)
         assertThat(repositionTaskBounds.top).isEqualTo(STARTING_BOUNDS.top)
         assertThat(repositionTaskBounds.right).isEqualTo(STARTING_BOUNDS.right - 50)
@@ -355,8 +416,10 @@
     @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun testChangeBounds_windowSizeExceedsStableBounds_shouldBeAllowedToChangeBounds() {
         val startingPoint =
-            PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
-                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
+            PointF(
+                OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
+                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat()
+            )
         val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
         // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
         // the disallowed drag area.
@@ -365,9 +428,11 @@
         val newY = STABLE_BOUNDS.bottom.toFloat() - offset
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
-            mockDisplayController, mockWindowDecoration)
+            mockDisplayController, mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.width()).isGreaterThan(STABLE_BOUNDS.right)
         assertThat(repositionTaskBounds.height()).isGreaterThan(STABLE_BOUNDS.bottom)
     }
@@ -375,10 +440,12 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_SIZE_CONSTRAINTS)
     fun testChangeBoundsInDesktopMode_windowSizeExceedsStableBounds_shouldBeLimitedToDisplaySize() {
-        doReturn(true).`when`{ DesktopModeStatus.canEnterDesktopMode(mockContext) }
+        doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(mockContext) }
         val startingPoint =
-            PointF(OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
-                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat())
+            PointF(
+                OFF_CENTER_STARTING_BOUNDS.right.toFloat(),
+                OFF_CENTER_STARTING_BOUNDS.bottom.toFloat()
+            )
         val repositionTaskBounds = Rect(OFF_CENTER_STARTING_BOUNDS)
         // Increase height and width by STABLE_BOUNDS. Subtract by 5px so that it doesn't reach
         // the disallowed drag area.
@@ -387,9 +454,11 @@
         val newY = STABLE_BOUNDS.bottom.toFloat() - offset
         val delta = DragPositioningCallbackUtility.calculateDelta(newX, newY, startingPoint)
 
-        DragPositioningCallbackUtility.changeBounds(CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
+        DragPositioningCallbackUtility.changeBounds(
+            CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM,
             repositionTaskBounds, OFF_CENTER_STARTING_BOUNDS, STABLE_BOUNDS, delta,
-            mockDisplayController, mockWindowDecoration)
+            mockDisplayController, mockWindowDecoration
+        )
         assertThat(repositionTaskBounds.width()).isLessThan(STABLE_BOUNDS.right)
         assertThat(repositionTaskBounds.height()).isLessThan(STABLE_BOUNDS.bottom)
     }
@@ -423,7 +492,8 @@
             DISPLAY_BOUNDS.left,
             DISPLAY_BOUNDS.bottom - NAVBAR_HEIGHT,
             DISPLAY_BOUNDS.right,
-            DISPLAY_BOUNDS.bottom)
+            DISPLAY_BOUNDS.bottom
+        )
         private val STABLE_BOUNDS = Rect(
             DISPLAY_BOUNDS.left,
             DISPLAY_BOUNDS.top,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index d8f395d..1691f07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -45,6 +45,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Tests for {@link DragResizeWindowGeometry}.
  *
@@ -57,11 +60,12 @@
     private static final Size TASK_SIZE = new Size(500, 1000);
     private static final int TASK_CORNER_RADIUS = 10;
     private static final int EDGE_RESIZE_THICKNESS = 12;
+    private static final int EDGE_RESIZE_HANDLE_INSET = 4;
     private static final int FINE_CORNER_SIZE = EDGE_RESIZE_THICKNESS * 2 + 10;
     private static final int LARGE_CORNER_SIZE = FINE_CORNER_SIZE + 10;
     private static final DragResizeWindowGeometry GEOMETRY = new DragResizeWindowGeometry(
-            TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE,
-            LARGE_CORNER_SIZE);
+            TASK_CORNER_RADIUS, TASK_SIZE, EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET,
+            FINE_CORNER_SIZE, LARGE_CORNER_SIZE);
     // Points in the edge resize handle. Note that coordinates start from the top left.
     private static final Point TOP_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
             -EDGE_RESIZE_THICKNESS / 2);
@@ -71,6 +75,16 @@
             TASK_SIZE.getWidth() + EDGE_RESIZE_THICKNESS / 2, TASK_SIZE.getHeight() / 2);
     private static final Point BOTTOM_EDGE_POINT = new Point(TASK_SIZE.getWidth() / 2,
             TASK_SIZE.getHeight() + EDGE_RESIZE_THICKNESS / 2);
+    // Points in the inset of the task bounds still within the edge resize handle.
+    // Note that coordinates start from the top left.
+    private static final Point TOP_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
+            EDGE_RESIZE_HANDLE_INSET / 2);
+    private static final Point LEFT_INSET_POINT = new Point(EDGE_RESIZE_HANDLE_INSET / 2,
+            TASK_SIZE.getHeight() / 2);
+    private static final Point RIGHT_INSET_POINT = new Point(
+            TASK_SIZE.getWidth() - EDGE_RESIZE_HANDLE_INSET / 2, TASK_SIZE.getHeight() / 2);
+    private static final Point BOTTOM_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
+            TASK_SIZE.getHeight() - EDGE_RESIZE_HANDLE_INSET / 2);
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -85,18 +99,23 @@
                 .addEqualityGroup(
                         GEOMETRY,
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
-                                EDGE_RESIZE_THICKNESS, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+                                EDGE_RESIZE_THICKNESS, EDGE_RESIZE_HANDLE_INSET, FINE_CORNER_SIZE,
+                                LARGE_CORNER_SIZE))
                 .addEqualityGroup(
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
-                                EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
+                                EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE),
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
-                                EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
+                                EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+                                FINE_CORNER_SIZE, LARGE_CORNER_SIZE))
                 .addEqualityGroup(
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
-                                EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+                                EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+                                FINE_CORNER_SIZE,
                                 LARGE_CORNER_SIZE + 5),
                         new DragResizeWindowGeometry(TASK_CORNER_RADIUS, TASK_SIZE,
-                                EDGE_RESIZE_THICKNESS + 10, FINE_CORNER_SIZE,
+                                EDGE_RESIZE_THICKNESS + 10, EDGE_RESIZE_HANDLE_INSET,
+                                FINE_CORNER_SIZE,
                                 LARGE_CORNER_SIZE + 5))
                 .testEquals();
     }
@@ -127,7 +146,7 @@
         assertThat(region.contains(point.x - EDGE_RESIZE_THICKNESS, point.y)).isTrue();
         // Vertically along the edge is not contained.
         assertThat(region.contains(point.x, point.y - EDGE_RESIZE_THICKNESS)).isFalse();
-        assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS)).isFalse();
+        assertThat(region.contains(point.x, point.y + EDGE_RESIZE_THICKNESS + 10)).isFalse();
     }
 
     private static void verifyVerticalEdge(@NonNull Region region, @NonNull Point point) {
@@ -188,18 +207,18 @@
     }
 
     private void validateCtrlTypeForEdges(boolean isTouchscreen, boolean isEdgeResizePermitted) {
-        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
-                LEFT_EDGE_POINT.x, LEFT_EDGE_POINT.y)).isEqualTo(
-                        isEdgeResizePermitted ? CTRL_TYPE_LEFT : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
-                TOP_EDGE_POINT.x, TOP_EDGE_POINT.y)).isEqualTo(
-                        isEdgeResizePermitted ? CTRL_TYPE_TOP : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
-                RIGHT_EDGE_POINT.x, RIGHT_EDGE_POINT.y)).isEqualTo(
-                        isEdgeResizePermitted ? CTRL_TYPE_RIGHT : CTRL_TYPE_UNDEFINED);
-        assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
-                BOTTOM_EDGE_POINT.x, BOTTOM_EDGE_POINT.y)).isEqualTo(
-                        isEdgeResizePermitted ? CTRL_TYPE_BOTTOM : CTRL_TYPE_UNDEFINED);
+        List<Point> points = Arrays.asList(LEFT_EDGE_POINT, TOP_EDGE_POINT, RIGHT_EDGE_POINT,
+                BOTTOM_EDGE_POINT, LEFT_INSET_POINT, TOP_INSET_POINT, RIGHT_INSET_POINT,
+                BOTTOM_INSET_POINT);
+        List<Integer> expectedCtrlType = Arrays.asList(CTRL_TYPE_LEFT, CTRL_TYPE_TOP,
+                CTRL_TYPE_RIGHT, CTRL_TYPE_BOTTOM, CTRL_TYPE_LEFT, CTRL_TYPE_TOP, CTRL_TYPE_RIGHT,
+                CTRL_TYPE_BOTTOM);
+
+        for (int i = 0; i < points.size(); i++) {
+            assertThat(GEOMETRY.calculateCtrlType(mContext, isTouchscreen, isEdgeResizePermitted,
+                    points.get(i).x, points.get(i).y)).isEqualTo(
+                    isEdgeResizePermitted ? expectedCtrlType.get(i) : CTRL_TYPE_UNDEFINED);
+        }
     }
 
     @Test
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 2ce59ff..3a3e965 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
@@ -678,6 +678,7 @@
             CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
         val rectAfterDrag = Rect(STARTING_BOUNDS)
         rectAfterDrag.right += 2000
+        rectAfterDrag.bottom = STABLE_BOUNDS_LANDSCAPE.bottom
         // First drag; we should fetch stable bounds.
         verify(mockDisplayLayout, Mockito.times(1)).getStableBounds(any())
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
@@ -705,8 +706,8 @@
             STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
             STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
             CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
-        rectAfterDrag.right -= 2000
-        rectAfterDrag.bottom += 2000
+        rectAfterDrag.right = STABLE_BOUNDS_PORTRAIT.right
+        rectAfterDrag.bottom = STARTING_BOUNDS.bottom + 2000
 
         verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index a1c7947..100abbb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -33,6 +33,7 @@
 import android.view.SurfaceControl
 import android.view.SurfaceControlViewHost
 import android.view.View
+import android.view.WindowManager
 import androidx.core.graphics.toPointF
 import androidx.test.filters.SmallTest
 import com.android.window.flags.Flags
@@ -41,9 +42,9 @@
 import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.common.DisplayController
 import com.android.wm.shell.common.DisplayLayout
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
+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.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
 import com.android.wm.shell.splitscreen.SplitScreenController
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
@@ -77,6 +78,8 @@
     @Mock
     private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
     @Mock
+    private lateinit var mockWindowManager: WindowManager
+    @Mock
     private lateinit var onClickListener: View.OnClickListener
     @Mock
     private lateinit var onTouchListener: View.OnTouchListener
@@ -230,8 +233,9 @@
             }
             else -> error("Invalid windowing mode")
         }
-        val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId, appIcon, appName,
-            splitScreenController, shouldShowWindowingPill = true,
+        val handleMenu = HandleMenu(mockDesktopWindowDecoration,
+            WindowManagerWrapper(mockWindowManager),
+            layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true,
             shouldShowNewWindowButton = true,
             null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50,
             captionX = captionX
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 08a6e1b..6ae16ed 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
@@ -372,6 +372,7 @@
             CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
         val rectAfterDrag = Rect(STARTING_BOUNDS)
         rectAfterDrag.right += 2000
+        rectAfterDrag.bottom = STABLE_BOUNDS_LANDSCAPE.bottom
         // First drag; we should fetch stable bounds.
         verify(mockDisplayLayout, times(1)).getStableBounds(any())
         verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
@@ -396,8 +397,8 @@
         performDrag(STARTING_BOUNDS.right.toFloat(), STARTING_BOUNDS.bottom.toFloat(),
             STARTING_BOUNDS.right.toFloat() + 2000, STARTING_BOUNDS.bottom.toFloat() + 2000,
             CTRL_TYPE_RIGHT or CTRL_TYPE_BOTTOM)
-        rectAfterDrag.right -= 2000
-        rectAfterDrag.bottom += 2000
+        rectAfterDrag.right = STABLE_BOUNDS_PORTRAIT.right
+        rectAfterDrag.bottom = STARTING_BOUNDS.bottom + 2000
 
         verify(mockTransitions).startTransition(eq(TRANSIT_CHANGE), argThat { wct ->
             return@argThat wct.changes.any { (token, change) ->
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
index 28b4eb6..0f52ed7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainerTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.R
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.WindowManagerWrapper
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,6 +71,7 @@
                 WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
         viewContainer = AdditionalSystemViewContainer(
             mockContext,
+            WindowManagerWrapper(mockWindowManager),
             TASK_ID,
             X,
             Y,
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 43a70c1..5a4cff0 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -309,9 +309,8 @@
         return NULL;
     }
 
-    // We succeeded, so relinquish control of dataMap
     pAsset->mAccessMode = mode;
-    return std::move(pAsset);
+    return pAsset;
 }
 
 /*
@@ -328,9 +327,8 @@
       return NULL;
   }
 
-  // We succeeded, so relinquish control of dataMap
   pAsset->mAccessMode = mode;
-  return std::move(pAsset);
+  return pAsset;
 }
 
 /*
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 822a387..0fa31c7 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -107,7 +107,7 @@
 }
 
 AssetManager2::AssetManager2() {
-  configurations_.resize(1);
+  configurations_.emplace_back();
 }
 
 bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
@@ -438,8 +438,8 @@
   return false;
 }
 
-void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations,
-    bool force_refresh) {
+void AssetManager2::SetConfigurations(std::span<const ResTable_config> configurations,
+                                      bool force_refresh) {
   int diff = 0;
   if (force_refresh) {
     diff = -1;
@@ -452,8 +452,10 @@
       }
     }
   }
-  configurations_ = std::move(configurations);
-
+  configurations_.clear();
+  for (auto&& config : configurations) {
+    configurations_.emplace_back(config);
+  }
   if (diff) {
     RebuildFilterList();
     InvalidateCaches(static_cast<uint32_t>(diff));
diff --git a/libs/androidfw/BigBuffer.cpp b/libs/androidfw/BigBuffer.cpp
index bedfc49..43b56c3 100644
--- a/libs/androidfw/BigBuffer.cpp
+++ b/libs/androidfw/BigBuffer.cpp
@@ -17,8 +17,8 @@
 #include <androidfw/BigBuffer.h>
 
 #include <algorithm>
+#include <iterator>
 #include <memory>
-#include <vector>
 
 #include "android-base/logging.h"
 
@@ -78,10 +78,27 @@
 
 std::string BigBuffer::to_string() const {
   std::string result;
+  result.reserve(size_);
   for (const Block& block : blocks_) {
     result.append(block.buffer.get(), block.buffer.get() + block.size);
   }
   return result;
 }
 
+void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
+  std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
+  size_ += buffer.size_;
+  buffer.blocks_.clear();
+  buffer.size_ = 0;
+}
+
+void BigBuffer::BackUp(size_t count) {
+  Block& block = blocks_.back();
+  block.size -= count;
+  size_ -= count;
+  // BigBuffer is supposed to always give zeroed memory, but backing up usually means
+  // something has been already written into the block. Erase it.
+  std::fill_n(block.buffer.get() + block.size, count, 0);
+}
+
 }  // namespace android
diff --git a/libs/androidfw/PosixUtils.cpp b/libs/androidfw/PosixUtils.cpp
index 8ddc572..49ee8f7 100644
--- a/libs/androidfw/PosixUtils.cpp
+++ b/libs/androidfw/PosixUtils.cpp
@@ -119,7 +119,7 @@
       auto err = ReadFile(stderr[0]);
       result.stderr_str = err ? std::move(*err) : "";
       close(stderr[0]);
-      return std::move(result);
+      return result;
   }
 }
 
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index ac46bc5..0fdeefa 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -32,6 +32,7 @@
 #include "androidfw/AssetManager.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
+#include "ftl/small_vector.h"
 
 namespace android {
 
@@ -159,9 +160,10 @@
 
   // Sets/resets the configuration for this AssetManager. This will cause all
   // caches that are related to the configuration change to be invalidated.
-  void SetConfigurations(std::vector<ResTable_config> configurations, bool force_refresh = false);
+  void SetConfigurations(std::span<const ResTable_config> configurations,
+                         bool force_refresh = false);
 
-  inline const std::vector<ResTable_config>& GetConfigurations() const {
+  std::span<const ResTable_config> GetConfigurations() const {
     return configurations_;
   }
 
@@ -470,13 +472,13 @@
 
   // An array mapping package ID to index into package_groups. This keeps the lookup fast
   // without taking too much memory.
-  std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
+  std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_ = {};
 
-  uint32_t default_locale_;
+  uint32_t default_locale_ = 0;
 
   // The current configurations set for this AssetManager. When this changes, cached resources
   // may need to be purged.
-  std::vector<ResTable_config> configurations_;
+  ftl::SmallVector<ResTable_config, 1> configurations_;
 
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
diff --git a/libs/androidfw/include/androidfw/BigBuffer.h b/libs/androidfw/include/androidfw/BigBuffer.h
index b99a4ed..c4cd7c5 100644
--- a/libs/androidfw/include/androidfw/BigBuffer.h
+++ b/libs/androidfw/include/androidfw/BigBuffer.h
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-#ifndef _ANDROID_BIG_BUFFER_H
-#define _ANDROID_BIG_BUFFER_H
+#pragma once
 
-#include <cstring>
 #include <memory>
 #include <string>
 #include <type_traits>
+#include <utility>
 #include <vector>
 
 #include "android-base/logging.h"
@@ -150,24 +149,11 @@
 
 template <typename T>
 inline T* BigBuffer::NextBlock(size_t count) {
-  static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
+  static_assert(std::is_standard_layout_v<T>, "T must be standard_layout type");
   CHECK(count != 0);
   return reinterpret_cast<T*>(NextBlockImpl(sizeof(T) * count));
 }
 
-inline void BigBuffer::BackUp(size_t count) {
-  Block& block = blocks_.back();
-  block.size -= count;
-  size_ -= count;
-}
-
-inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) {
-  std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_));
-  size_ += buffer.size_;
-  buffer.blocks_.clear();
-  buffer.size_ = 0;
-}
-
 inline void BigBuffer::Pad(size_t bytes) {
   NextBlock<char>(bytes);
 }
@@ -188,5 +174,3 @@
 }
 
 }  // namespace android
-
-#endif  // _ANDROID_BIG_BUFFER_H
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index c62f095..3f22884 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -113,7 +113,7 @@
   desired_config.language[1] = 'e';
 
   AssetManager2 assetmanager;
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_});
 
   auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -137,7 +137,7 @@
   desired_config.language[1] = 'e';
 
   AssetManager2 assetmanager;
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
 
   auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -466,10 +466,10 @@
 TEST_F(AssetManager2Test, DensityOverride) {
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_});
-  assetmanager.SetConfigurations({{
+  assetmanager.SetConfigurations({{{
     .density = ResTable_config::DENSITY_XHIGH,
     .sdkVersion = 21,
-  }});
+  }}});
 
   auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
   ASSERT_TRUE(value.has_value());
@@ -721,7 +721,7 @@
   ResTable_config desired_config;
 
   AssetManager2 assetmanager;
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_});
   assetmanager.SetResourceResolutionLoggingEnabled(false);
 
@@ -736,7 +736,7 @@
   ResTable_config desired_config;
 
   AssetManager2 assetmanager;
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_});
 
   auto result = assetmanager.GetLastResourceResolution();
@@ -751,7 +751,7 @@
 
   AssetManager2 assetmanager;
   assetmanager.SetResourceResolutionLoggingEnabled(true);
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_});
 
   auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -774,7 +774,7 @@
 
   AssetManager2 assetmanager;
   assetmanager.SetResourceResolutionLoggingEnabled(true);
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
 
   auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -796,7 +796,7 @@
 
   AssetManager2 assetmanager;
   assetmanager.SetResourceResolutionLoggingEnabled(true);
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({basic_assets_});
 
   auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -817,7 +817,7 @@
 
   AssetManager2 assetmanager;
   assetmanager.SetResourceResolutionLoggingEnabled(true);
-  assetmanager.SetConfigurations({desired_config});
+  assetmanager.SetConfigurations({{desired_config}});
   assetmanager.SetApkAssets({overlayable_assets_});
 
   const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index e3fc0a0..ec2abb8 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -66,7 +66,7 @@
   AssetManager2 assetmanager;
   assetmanager.SetApkAssets(apk_assets);
   if (config != nullptr) {
-    assetmanager.SetConfigurations({*config});
+    assetmanager.SetConfigurations({{{*config}}});
   }
 
   while (state.KeepRunning()) {
diff --git a/libs/androidfw/tests/BigBuffer_test.cpp b/libs/androidfw/tests/BigBuffer_test.cpp
index 382d21e..7e38f17 100644
--- a/libs/androidfw/tests/BigBuffer_test.cpp
+++ b/libs/androidfw/tests/BigBuffer_test.cpp
@@ -98,4 +98,20 @@
   ASSERT_EQ(8u, buffer.size());
 }
 
+TEST(BigBufferTest, BackUpZeroed) {
+  BigBuffer buffer(16);
+
+  auto block = buffer.NextBlock<char>(2);
+  ASSERT_TRUE(block != nullptr);
+  ASSERT_EQ(2u, buffer.size());
+  block[0] = 0x01;
+  block[1] = 0x02;
+  buffer.BackUp(1);
+  ASSERT_EQ(1u, buffer.size());
+  auto new_block = buffer.NextBlock<char>(1);
+  ASSERT_TRUE(new_block != nullptr);
+  ASSERT_EQ(2u, buffer.size());
+  ASSERT_EQ(0, *new_block);
+}
+
 }  // namespace android
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 181d141..afcb0c1 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -260,7 +260,7 @@
   ResTable_config night{};
   night.uiMode = ResTable_config::UI_MODE_NIGHT_YES;
   night.version = 8u;
-  am_night.SetConfigurations({night});
+  am_night.SetConfigurations({{night}});
 
   auto theme = am.NewTheme();
   {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d71f3b6..23cd3ce 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -143,6 +143,7 @@
                 "aconfig_text_flags_c_lib",
                 "server_configurable_flags",
                 "libaconfig_storage_read_api_cc",
+                "libgraphicsenv",
             ],
             static_libs: [
                 "libEGL_blobCache",
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 5f5ffe9..27add35 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -17,11 +17,12 @@
 #include "AutoBackendTextureRelease.h"
 
 #include <SkImage.h>
-#include <include/gpu/ganesh/SkImageGanesh.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/GrBackendSurface.h>
 #include <include/gpu/MutableTextureState.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
 #include <include/gpu/vk/VulkanMutableTextureState.h>
+
 #include "renderthread/RenderThread.h"
 #include "utils/Color.h"
 #include "utils/PaintUtils.h"
diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h
index f0eb2a8..d58cd17 100644
--- a/libs/hwui/AutoBackendTextureRelease.h
+++ b/libs/hwui/AutoBackendTextureRelease.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
-#include <GrAHardwareBufferUtils.h>
-#include <GrBackendSurface.h>
 #include <SkImage.h>
 #include <android/hardware_buffer.h>
+#include <include/android/GrAHardwareBufferUtils.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
 #include <system/graphics.h>
 
 namespace android {
diff --git a/libs/hwui/ColorFilter.h b/libs/hwui/ColorFilter.h
index 31c9db7..3a3bfb47 100644
--- a/libs/hwui/ColorFilter.h
+++ b/libs/hwui/ColorFilter.h
@@ -106,7 +106,7 @@
 
 private:
     sk_sp<SkColorFilter> createInstance() override {
-        return SkColorFilters::Matrix(mMatrix.data());
+        return SkColorFilters::Matrix(mMatrix.data(), SkColorFilters::Clamp::kNo);
     }
 
 private:
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index ac75c07..c1c30f5 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -49,6 +49,15 @@
 #endif  // __ANDROID__
 }
 
+inline bool typeface_redesign() {
+#ifdef __ANDROID__
+    static bool flag = com_android_text_flags_typeface_redesign();
+    return flag;
+#else
+    return true;
+#endif  // __ANDROID__
+}
+
 }  // namespace text_feature
 
 }  // namespace android
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 27ea150..a2748b0 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -21,8 +21,6 @@
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 #include <GLES3/gl3.h>
-#include <GrDirectContext.h>
-#include <GrTypes.h>
 #include <SkBitmap.h>
 #include <SkCanvas.h>
 #include <SkImage.h>
@@ -30,6 +28,8 @@
 #include <SkImageInfo.h>
 #include <SkRefCnt.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
 #include <utils/GLUtils.h>
 #include <utils/NdkUtils.h>
 #include <utils/Trace.h>
diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h
index 8c6ca97..afc6adf 100644
--- a/libs/hwui/Mesh.h
+++ b/libs/hwui/Mesh.h
@@ -17,8 +17,8 @@
 #ifndef MESH_H_
 #define MESH_H_
 
-#include <GrDirectContext.h>
 #include <SkMesh.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 #include <include/gpu/ganesh/SkMeshGanesh.h>
 #include <jni.h>
 #include <log/log.h>
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 1217b47..b6476c9 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -42,6 +42,11 @@
 constexpr bool initialize_gl_always() {
     return false;
 }
+
+constexpr bool skip_eglmanager_telemetry() {
+    return false;
+}
+
 constexpr bool resample_gainmap_regions() {
     return false;
 }
@@ -103,6 +108,7 @@
 
 bool Properties::clipSurfaceViews = false;
 bool Properties::hdr10bitPlus = false;
+bool Properties::skipTelemetry = false;
 bool Properties::resampleGainmapRegions = false;
 
 int Properties::timeoutMultiplier = 1;
@@ -183,6 +189,8 @@
                                                    hwui_flags::resample_gainmap_regions());
 
     timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+    skipTelemetry = base::GetBoolProperty(PROPERTY_SKIP_EGLMANAGER_TELEMETRY,
+                                          hwui_flags::skip_eglmanager_telemetry());
 
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 73e80ce..db47152 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -234,6 +234,8 @@
  */
 #define PROPERTY_INITIALIZE_GL_ALWAYS "debug.hwui.initialize_gl_always"
 
+#define PROPERTY_SKIP_EGLMANAGER_TELEMETRY "debug.hwui.skip_eglmanager_telemetry"
+
 ///////////////////////////////////////////////////////////////////////////////
 // Misc
 ///////////////////////////////////////////////////////////////////////////////
@@ -342,6 +344,7 @@
 
     static bool clipSurfaceViews;
     static bool hdr10bitPlus;
+    static bool skipTelemetry;
     static bool resampleGainmapRegions;
 
     static int timeoutMultiplier;
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index d026379..60d7f2d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,9 +16,12 @@
 
 #include "RecordingCanvas.h"
 
-#include <GrRecordingContext.h>
 #include <SkMesh.h>
 #include <hwui/Paint.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrRecordingContext.h>
+#include <include/gpu/ganesh/SkMeshGanesh.h>
 #include <log/log.h>
 
 #include <experimental/type_traits>
@@ -48,9 +51,6 @@
 #include "Tonemapper.h"
 #include "VectorDrawable.h"
 #include "effects/GainmapRenderer.h"
-#include "include/gpu/GpuTypes.h"  // from Skia
-#include "include/gpu/GrDirectContext.h"
-#include "include/gpu/ganesh/SkMeshGanesh.h"
 #include "pipeline/skia/AnimatedDrawables.h"
 #include "pipeline/skia/FunctorDrawable.h"
 #ifdef __ANDROID__
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index 13c0b00..faea6d4 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -2,6 +2,13 @@
 container: "system"
 
 flag {
+  name: "runtime_color_filters_blenders"
+  namespace: "core_graphics"
+  description: "API for AGSL authored runtime color filters and blenders"
+  bug: "358126864"
+}
+
+flag {
   name: "clip_shader"
   is_exported: true
   namespace: "core_graphics"
@@ -99,6 +106,13 @@
 }
 
 flag {
+  name: "skip_eglmanager_telemetry"
+  namespace: "core_graphics"
+  description: "Skip telemetry in EglManager's calls to eglCreateContext to avoid polluting telemetry"
+  bug: "347911216"
+}
+
+flag {
   name: "resample_gainmap_regions"
   namespace: "core_graphics"
   description: "Resample gainmaps when decoding regions, to improve visual quality"
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index f8574ee..1510ce1 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -27,6 +27,8 @@
 #include <cutils/compiler.h>
 #include <log/log.h>
 #include <minikin/Layout.h>
+
+#include "FeatureFlags.h"
 #include "MinikinSkia.h"
 #include "Paint.h"
 #include "Typeface.h"
@@ -71,27 +73,42 @@
     static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) {
         float saveSkewX = paint->getSkFont().getSkewX();
         bool savefakeBold = paint->getSkFont().isEmbolden();
-        const minikin::MinikinFont* curFont = nullptr;
-        size_t start = 0;
-        size_t nGlyphs = layout.nGlyphs();
-        for (size_t i = 0; i < nGlyphs; i++) {
-            const minikin::MinikinFont* nextFont = layout.typeface(i).get();
-            if (i > 0 && nextFont != curFont) {
+        if (text_feature::typeface_redesign()) {
+            for (uint32_t runIdx = 0; runIdx < layout.getFontRunCount(); ++runIdx) {
+                uint32_t start = layout.getFontRunStart(runIdx);
+                uint32_t end = layout.getFontRunEnd(runIdx);
+                const minikin::FakedFont& fakedFont = layout.getFontRunFont(runIdx);
+
+                std::shared_ptr<minikin::MinikinFont> font = fakedFont.typeface();
                 SkFont* skfont = &paint->getSkFont();
-                MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
-                f(start, i);
+                MinikinFontSkia::populateSkFont(skfont, font.get(), fakedFont.fakery);
+                f(start, end);
                 skfont->setSkewX(saveSkewX);
                 skfont->setEmbolden(savefakeBold);
-                start = i;
             }
-            curFont = nextFont;
-        }
-        if (nGlyphs > start) {
-            SkFont* skfont = &paint->getSkFont();
-            MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
-            f(start, nGlyphs);
-            skfont->setSkewX(saveSkewX);
-            skfont->setEmbolden(savefakeBold);
+        } else {
+            const minikin::MinikinFont* curFont = nullptr;
+            size_t start = 0;
+            size_t nGlyphs = layout.nGlyphs();
+            for (size_t i = 0; i < nGlyphs; i++) {
+                const minikin::MinikinFont* nextFont = layout.typeface(i).get();
+                if (i > 0 && nextFont != curFont) {
+                    SkFont* skfont = &paint->getSkFont();
+                    MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
+                    f(start, i);
+                    skfont->setSkewX(saveSkewX);
+                    skfont->setEmbolden(savefakeBold);
+                    start = i;
+                }
+                curFont = nextFont;
+            }
+            if (nGlyphs > start) {
+                SkFont* skfont = &paint->getSkFont();
+                MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
+                f(start, nGlyphs);
+                skfont->setSkewX(saveSkewX);
+                skfont->setEmbolden(savefakeBold);
+            }
         }
     }
 };
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
index 756b937..3800645 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -16,12 +16,11 @@
 
 #include "ATraceMemoryDump.h"
 
+#include <include/gpu/ganesh/GrDirectContext.h>
 #include <utils/Trace.h>
 
 #include <cstring>
 
-#include "GrDirectContext.h"
-
 namespace android {
 namespace uirenderer {
 namespace skiapipeline {
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
index 777d1a2..86ff33f 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include <GrDirectContext.h>
 #include <SkString.h>
 #include <SkTraceMemoryDump.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 
 #include <string>
 #include <unordered_map>
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index 5d3fb30..85432bf 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -15,24 +15,26 @@
  */
 
 #include "GLFunctorDrawable.h"
-#include <GrDirectContext.h>
+
+#include <effects/GainmapRenderer.h>
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
 #include <private/hwui/DrawGlInfo.h>
+
 #include "FunctorDrawable.h"
-#include "GrBackendSurface.h"
 #include "RenderNode.h"
 #include "SkAndroidFrameworkUtils.h"
 #include "SkCanvas.h"
 #include "SkCanvasAndroid.h"
 #include "SkClipStack.h"
-#include "SkRect.h"
 #include "SkM44.h"
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include "include/gpu/GpuTypes.h" // from Skia
-#include <include/gpu/gl/GrGLTypes.h>
-#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include "utils/GLUtils.h"
-#include <effects/GainmapRenderer.h>
+#include "SkRect.h"
 #include "renderthread/CanvasContext.h"
+#include "utils/GLUtils.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 99f54c1..8f3366c 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -16,17 +16,17 @@
 
 #include "LayerDrawable.h"
 
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
 #include <shaders/shaders.h>
 #include <utils/Color.h>
 #include <utils/MathUtils.h>
 
 #include "DeviceInfo.h"
-#include "GrBackendSurface.h"
 #include "SkColorFilter.h"
 #include "SkRuntimeEffect.h"
 #include "SkSurface.h"
 #include "Tonemapper.h"
-#include "gl/GrGLTypes.h"
 #include "math/mat4.h"
 #include "system/graphics-base-v1.0.h"
 #include "system/window.h"
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 8e07a2f..22f59a6 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -16,9 +16,9 @@
 
 #include "ShaderCache.h"
 
-#include <GrDirectContext.h>
 #include <SkData.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 #include <log/log.h>
 #include <openssl/sha.h>
 
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 40dfc9d..4c01161 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -17,10 +17,10 @@
 #pragma once
 
 #include <FileBlobCache.h>
-#include <GrContextOptions.h>
 #include <SkRefCnt.h>
 #include <cutils/compiler.h>
 #include <ftl/shared_mutex.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
 #include <utils/Mutex.h>
 
 #include <memory>
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index e4b1f916..0768f45 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -16,14 +16,14 @@
 
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 
-#include <GrBackendSurface.h>
 #include <SkBlendMode.h>
 #include <SkImageInfo.h>
 #include <cutils/properties.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include <include/gpu/gl/GrGLTypes.h>
+#include <include/gpu/ganesh/gl/GrGLTypes.h>
 #include <strings.h>
 
 #include "DeferredLayerUpdater.h"
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index d06dba0..e1de1e6 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -16,14 +16,14 @@
 
 #include "pipeline/skia/SkiaVulkanPipeline.h"
 
-#include <GrDirectContext.h>
-#include <GrTypes.h>
 #include <SkSurface.h>
 #include <SkTypes.h>
 #include <cutils/properties.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
 #include <strings.h>
-#include <vk/GrVkTypes.h>
 
 #include "DeferredLayerUpdater.h"
 #include "LightingInfo.h"
diff --git a/libs/hwui/pipeline/skia/StretchMask.h b/libs/hwui/pipeline/skia/StretchMask.h
index dc698b8..0baed9f 100644
--- a/libs/hwui/pipeline/skia/StretchMask.h
+++ b/libs/hwui/pipeline/skia/StretchMask.h
@@ -15,9 +15,10 @@
  */
 #pragma once
 
-#include "GrRecordingContext.h"
-#include <effects/StretchEffect.h>
 #include <SkSurface.h>
+#include <effects/StretchEffect.h>
+#include <include/gpu/ganesh/GrRecordingContext.h>
+
 #include "SkiaDisplayList.h"
 
 namespace android::uirenderer {
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 21fe6ff..1ebc3c8 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -19,12 +19,12 @@
 #include <SkAndroidFrameworkUtils.h>
 #include <SkImage.h>
 #include <SkM44.h>
-#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
 #include <private/hwui/DrawVkInfo.h>
 #include <utils/Color.h>
 #include <utils/Trace.h>
-#include <vk/GrVkTypes.h>
 
 #include <thread>
 
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ac2a936..2771780 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,10 +16,10 @@
 
 #include "CacheManager.h"
 
-#include <GrContextOptions.h>
-#include <GrTypes.h>
 #include <SkExecutor.h>
 #include <SkGraphics.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
 #include <math.h>
 #include <utils/Trace.h>
 
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index bcfa4f3..94f1005 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -18,7 +18,7 @@
 #define CACHEMANAGER_H
 
 #ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
-#include <GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 #endif
 #include <SkSurface.h>
 #include <utils/String8.h>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8bb11ba..dfda25d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -761,8 +761,8 @@
         if (mExpectSurfaceStats) {
             reportMetricsWithPresentTime();
             {  // acquire lock
-                std::lock_guard lock(mLast4FrameMetricsInfosMutex);
-                FrameMetricsInfo& next = mLast4FrameMetricsInfos.next();
+                std::lock_guard lock(mLastFrameMetricsInfosMutex);
+                FrameMetricsInfo& next = mLastFrameMetricsInfos.next();
                 next.frameInfo = mCurrentFrameInfo;
                 next.frameNumber = frameCompleteNr;
                 next.surfaceId = mSurfaceControlGenerationId;
@@ -816,12 +816,12 @@
     int32_t surfaceControlId;
 
     {  // acquire lock
-        std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
-        if (mLast4FrameMetricsInfos.size() != mLast4FrameMetricsInfos.capacity()) {
+        std::scoped_lock lock(mLastFrameMetricsInfosMutex);
+        if (mLastFrameMetricsInfos.size() != mLastFrameMetricsInfos.capacity()) {
             // Not enough frames yet
             return;
         }
-        auto frameMetricsInfo = mLast4FrameMetricsInfos.front();
+        auto frameMetricsInfo = mLastFrameMetricsInfos.front();
         forthBehind = frameMetricsInfo.frameInfo;
         frameNumber = frameMetricsInfo.frameNumber;
         surfaceControlId = frameMetricsInfo.surfaceId;
@@ -869,12 +869,12 @@
     }
 }
 
-FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId) {
-    std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
-    for (size_t i = 0; i < mLast4FrameMetricsInfos.size(); i++) {
-        if (mLast4FrameMetricsInfos[i].frameNumber == frameNumber &&
-            mLast4FrameMetricsInfos[i].surfaceId == surfaceControlId) {
-            return mLast4FrameMetricsInfos[i].frameInfo;
+FrameInfo* CanvasContext::getFrameInfoFromLastFew(uint64_t frameNumber, uint32_t surfaceControlId) {
+    std::scoped_lock lock(mLastFrameMetricsInfosMutex);
+    for (size_t i = 0; i < mLastFrameMetricsInfos.size(); i++) {
+        if (mLastFrameMetricsInfos[i].frameNumber == frameNumber &&
+            mLastFrameMetricsInfos[i].surfaceId == surfaceControlId) {
+            return mLastFrameMetricsInfos[i].frameInfo;
         }
     }
 
@@ -894,7 +894,7 @@
     }
     uint64_t frameNumber = functions.getFrameNumberFunc(stats);
 
-    FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
+    FrameInfo* frameInfo = instance->getFrameInfoFromLastFew(frameNumber, surfaceControlId);
 
     if (frameInfo != nullptr) {
         std::scoped_lock lock(instance->mFrameInfoMutex);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index e2e3fa3..cb37538 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -260,7 +260,7 @@
     void finishFrame(FrameInfo* frameInfo);
 
     /**
-     * Invoke 'reportFrameMetrics' on the last frame stored in 'mLast4FrameInfos'.
+     * Invoke 'reportFrameMetrics' on the last frame stored in 'mLastFrameInfos'.
      * Populate the 'presentTime' field before calling.
      */
     void reportMetricsWithPresentTime();
@@ -271,7 +271,7 @@
         int32_t surfaceId;
     };
 
-    FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
+    FrameInfo* getFrameInfoFromLastFew(uint64_t frameNumber, uint32_t surfaceControlId);
 
     Frame getFrame();
 
@@ -336,9 +336,9 @@
 
     // List of data of frames that are awaiting GPU completion reporting. Used to compute frame
     // metrics and determine whether or not to report the metrics.
-    RingBuffer<FrameMetricsInfo, 4> mLast4FrameMetricsInfos
-            GUARDED_BY(mLast4FrameMetricsInfosMutex);
-    std::mutex mLast4FrameMetricsInfosMutex;
+    RingBuffer<FrameMetricsInfo, 6> mLastFrameMetricsInfos
+            GUARDED_BY(mLastFrameMetricsInfosMutex);
+    std::mutex mLastFrameMetricsInfosMutex;
 
     std::string mName;
     JankTracker mJankTracker;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 708b011..6010445 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
 #include <EGL/eglext.h>
 #include <GLES/gl.h>
 #include <cutils/properties.h>
+#include <graphicsenv/GpuStatsInfo.h>
 #include <log/log.h>
 #include <sync/sync.h>
 #include <utils/Trace.h>
@@ -366,6 +367,10 @@
         contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
         contextAttributes.push_back(Properties::contextPriority);
     }
+    if (Properties::skipTelemetry) {
+        contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID);
+        contextAttributes.push_back(android::GpuStatsInfo::SKIP_TELEMETRY);
+    }
     contextAttributes.push_back(EGL_NONE);
     mEglContext = eglCreateContext(
             mEglDisplay, EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a024aeb..92c6ad1 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,12 +16,12 @@
 
 #include "RenderThread.h"
 
-#include <GrContextOptions.h>
 #include <android-base/properties.h>
 #include <dlfcn.h>
-#include <gl/GrGLInterface.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
 #include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
 #include <private/android/choreographer.h>
 #include <sys/resource.h>
 #include <ui/FatVector.h>
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 045d26f..86fddba 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,9 +17,9 @@
 #ifndef RENDERTHREAD_H_
 #define RENDERTHREAD_H_
 
-#include <GrDirectContext.h>
 #include <SkBitmap.h>
 #include <cutils/compiler.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 #include <surface_control_private.h>
 #include <utils/Thread.h>
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4d185c6..bce84ae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -18,19 +18,19 @@
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
-#include <GrBackendSemaphore.h>
-#include <GrBackendSurface.h>
-#include <GrDirectContext.h>
-#include <GrTypes.h>
 #include <android/sync.h>
 #include <gui/TraceUtils.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrBackendSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
 #include <include/gpu/ganesh/SkSurfaceGanesh.h>
 #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
 #include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
 #include <include/gpu/ganesh/vk/GrVkDirectContext.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
 #include <include/gpu/vk/VulkanBackendContext.h>
 #include <ui/FatVector.h>
-#include <vk/GrVkTypes.h>
 
 #include <sstream>
 
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 08f9d42..f042571 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -20,9 +20,9 @@
 #if !defined(VK_USE_PLATFORM_ANDROID_KHR)
 #define VK_USE_PLATFORM_ANDROID_KHR
 #endif
-#include <GrContextOptions.h>
 #include <SkSurface.h>
 #include <android-base/unique_fd.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
 #include <utils/StrongPointer.h>
 #include <vk/VulkanExtensions.h>
 #include <vulkan/vulkan.h>
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
index 0f29613..20c2b1a 100644
--- a/libs/hwui/renderthread/VulkanSurface.cpp
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -16,12 +16,13 @@
 
 #include "VulkanSurface.h"
 
-#include <include/android/SkSurfaceAndroid.h>
-#include <GrDirectContext.h>
 #include <SkSurface.h>
+#include <gui/TraceUtils.h>
+#include <include/android/SkSurfaceAndroid.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+
 #include <algorithm>
 
-#include <gui/TraceUtils.h>
 #include "VulkanManager.h"
 #include "utils/Color.h"
 
diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index fd596d9..e427c97 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -16,6 +16,7 @@
 
 #include "tests/common/TestContext.h"
 
+#include <com_android_graphics_libgui_flags.h>
 #include <cutils/trace.h>
 
 namespace android {
@@ -101,6 +102,14 @@
 }
 
 void TestContext::createOffscreenSurface() {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    mConsumer = new BufferItemConsumer(GRALLOC_USAGE_HW_COMPOSER, 4);
+    const ui::Size& resolution = getActiveDisplayResolution();
+    mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
+    mSurface = mConsumer->getSurface();
+    mSurface->setMaxDequeuedBufferCount(3);
+    mSurface->setAsyncMode(true);
+#else
     sp<IGraphicBufferProducer> producer;
     sp<IGraphicBufferConsumer> consumer;
     BufferQueue::createBufferQueue(&producer, &consumer);
@@ -110,6 +119,7 @@
     const ui::Size& resolution = getActiveDisplayResolution();
     mConsumer->setDefaultBufferSize(resolution.getWidth(), resolution.getHeight());
     mSurface = new Surface(producer);
+#endif  // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 }
 
 void TestContext::waitForVsync() {
@@ -144,4 +154,4 @@
 
 }  // namespace test
 }  // namespace uirenderer
-}  // namespace android
+}  // namespace android
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 0f8bd13..b714534 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <GrDirectContext.h>
 #include <Properties.h>
 #include <SkData.h>
 #include <SkRefCnt.h>
@@ -22,6 +21,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <gtest/gtest.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index eecc741..1afef75 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -25,6 +25,9 @@
 #include <input/Input.h>
 #include <log/log.h>
 
+#define INDENT "  "
+#define INDENT2 "    "
+
 namespace {
 // Time to spend fading out the pointer completely.
 const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms
@@ -449,6 +452,24 @@
     return mLocked.resourcesLoaded;
 }
 
+std::string MouseCursorController::dump() const {
+    std::string dump = INDENT "MouseCursorController:\n";
+    std::scoped_lock lock(mLock);
+    dump += StringPrintf(INDENT2 "viewport: %s\n", mLocked.viewport.toString().c_str());
+    dump += StringPrintf(INDENT2 "stylusHoverMode: %s\n",
+                         mLocked.stylusHoverMode ? "true" : "false");
+    dump += StringPrintf(INDENT2 "pointerFadeDirection: %d\n", mLocked.pointerFadeDirection);
+    dump += StringPrintf(INDENT2 "updatePointerIcon: %s\n",
+                         mLocked.updatePointerIcon ? "true" : "false");
+    dump += StringPrintf(INDENT2 "resourcesLoaded: %s\n",
+                         mLocked.resourcesLoaded ? "true" : "false");
+    dump += StringPrintf(INDENT2 "requestedPointerType: %d\n", mLocked.requestedPointerType);
+    dump += StringPrintf(INDENT2 "resolvedPointerType: %d\n", mLocked.resolvedPointerType);
+    dump += StringPrintf(INDENT2 "skipScreenshot: %s\n", mLocked.skipScreenshot ? "true" : "false");
+    dump += StringPrintf(INDENT2 "animating: %s\n", mLocked.animating ? "true" : "false");
+    return dump;
+}
+
 bool MouseCursorController::doAnimations(nsecs_t timestamp) {
     std::scoped_lock lock(mLock);
     bool keepFading = doFadingAnimationLocked(timestamp);
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 78f6413..8600341 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -67,6 +67,8 @@
 
     bool resourcesLoaded();
 
+    std::string dump() const;
+
 private:
     mutable std::mutex mLock;
 
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 11b27a2..5ae967b 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -25,6 +25,7 @@
 #include <android-base/stringprintf.h>
 #include <android-base/thread_annotations.h>
 #include <ftl/enum.h>
+#include <input/PrintTools.h>
 
 #include <mutex>
 
@@ -353,6 +354,8 @@
     for (const auto& [_, spotController] : mLocked.spotControllers) {
         spotController.dump(dump, INDENT3);
     }
+    dump += INDENT2 "Cursor Controller:\n";
+    dump += addLinePrefix(mCursorController.dump(), INDENT3);
     return dump;
 }
 
diff --git a/location/TEST_MAPPING b/location/TEST_MAPPING
index 10da632..256affd 100644
--- a/location/TEST_MAPPING
+++ b/location/TEST_MAPPING
@@ -11,10 +11,7 @@
       "name": "CtsLocationNoneTestCases"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [{
-        "include-filter": "com.android.server.location"
-      }]
+      "name": "FrameworksMockingServicesTests_location"
     }
   ]
 }
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index f6e76a2..cf3f740 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -437,7 +437,7 @@
   }
 
   public class LocationManager {
-    method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
+    method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
     method @Nullable public String getExtraLocationControllerPackage();
@@ -452,7 +452,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
-    method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
+    method @Deprecated @FlaggedApi("android.location.flags.deprecate_provider_request_apis") @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void removeProviderRequestChangedListener(@NonNull android.location.provider.ProviderRequest.ChangedListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d921730..6342489 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -28,6 +28,7 @@
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
@@ -47,6 +48,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.location.flags.Flags;
 import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
@@ -2957,8 +2959,13 @@
      *
      * @param executor the executor that the callback runs on
      * @param listener the listener to register
+     *
+     * @deprecated Do not use - this API was intended for testing only, and may not return any
+     * results in the future.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_DEPRECATE_PROVIDER_REQUEST_APIS)
+    @Deprecated
     @SystemApi
     @RequiresPermission(allOf = {Manifest.permission.LOCATION_HARDWARE,
             Manifest.permission.INTERACT_ACROSS_USERS})
@@ -2973,8 +2980,13 @@
      * Removes a {@link ProviderRequest.ChangedListener} that has been added.
      *
      * @param listener the listener to remove.
+     *
+     * @deprecated Do not use - this API was intended for testing only, and may not return any
+     * results in the future.
      * @hide
      */
+    @FlaggedApi(Flags.FLAG_DEPRECATE_PROVIDER_REQUEST_APIS)
+    @Deprecated
     @SystemApi
     @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
     public void removeProviderRequestChangedListener(
diff --git a/location/java/android/location/flags/location.aconfig b/location/java/android/location/flags/location.aconfig
index 0edaaef..3c387d3 100644
--- a/location/java/android/location/flags/location.aconfig
+++ b/location/java/android/location/flags/location.aconfig
@@ -2,6 +2,13 @@
 container: "system"
 
 flag {
+    name: "deprecate_provider_request_apis"
+    namespace: "location"
+    description: "Deprecates LocationManager ProviderChanged APIs"
+    bug: "361811782"
+}
+
+flag {
     name: "keep_gnss_stationary_throttling"
     namespace: "location"
     description: "Keeps stationary throttling for the GNSS provider even if the disable_stationary_throttling flag is true."
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 25c767a..25fae76 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -84,6 +84,7 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 import android.view.KeyEvent;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1026,7 +1027,7 @@
      * <p>This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
      * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
-     * unless the app has been granted Do Not Disturb Access.
+     * unless the app has been granted Notification Policy Access.
      * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      *
      * @param streamType The stream type to adjust. One of {@link #STREAM_VOICE_CALL},
@@ -1378,7 +1379,7 @@
      * <p>This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
      * * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
-     * unless the app has been granted Do Not Disturb Access.
+     * unless the app has been granted Notification Policy Access.
      * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      * @param ringerMode The ringer mode, one of {@link #RINGER_MODE_NORMAL},
      *            {@link #RINGER_MODE_SILENT}, or {@link #RINGER_MODE_VIBRATE}.
@@ -1402,7 +1403,7 @@
      * <p>This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
      * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
-     * the app has been granted Do Not Disturb Access.
+     * the app has been granted Notification Policy Access.
      * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      * @param streamType The stream whose volume index should be set.
      * @param index The volume index to set. See
@@ -4283,7 +4284,7 @@
                                     final OnAudioFocusChangeListener listener =
                                             fri.mRequest.getOnAudioFocusChangeListener();
                                     if (listener != null) {
-                                        Log.d(TAG, "dispatching onAudioFocusChange("
+                                        Slog.i(TAG, "dispatching onAudioFocusChange("
                                                 + msg.arg1 + ") to " + msg.obj);
                                         listener.onAudioFocusChange(msg.arg1);
                                     }
@@ -4566,8 +4567,8 @@
      *     when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}.
      * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
      *     requesting audio focus.
-     * @param durationHint use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
-     *      is temporary, and focus will be abandonned shortly. Examples of transient requests are
+     * @param focusReqType use {@link #AUDIOFOCUS_GAIN_TRANSIENT} to indicate this focus request
+     *      is temporary, and focus will be abandoned shortly. Examples of transient requests are
      *      for the playback of driving directions, or notifications sounds.
      *      Use {@link #AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK} to indicate also that it's ok for
      *      the previous focus owner to keep playing if it ducks its audio output.
@@ -4592,13 +4593,13 @@
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public int requestAudioFocus(OnAudioFocusChangeListener l,
             @NonNull AudioAttributes requestAttributes,
-            int durationHint,
+            int focusReqType,
             int flags) throws IllegalArgumentException {
         if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) {
             throw new IllegalArgumentException("Invalid flags 0x"
                     + Integer.toHexString(flags).toUpperCase());
         }
-        return requestAudioFocus(l, requestAttributes, durationHint,
+        return requestAudioFocus(l, requestAttributes, focusReqType,
                 flags & AUDIOFOCUS_FLAGS_APPS,
                 null /* no AudioPolicy*/);
     }
@@ -4613,7 +4614,7 @@
      *     {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
      * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
      *     requesting audio focus.
-     * @param durationHint see the description of the same parameter in
+     * @param focusReqType see the description of the same parameter in
      *     {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
      * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
      *     {@link #AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS}, and {@link #AUDIOFOCUS_FLAG_LOCK}.
@@ -4635,14 +4636,14 @@
     })
     public int requestAudioFocus(OnAudioFocusChangeListener l,
             @NonNull AudioAttributes requestAttributes,
-            int durationHint,
+            int focusReqType,
             int flags,
             AudioPolicy ap) throws IllegalArgumentException {
         // parameter checking
         if (requestAttributes == null) {
             throw new IllegalArgumentException("Illegal null AudioAttributes argument");
         }
-        if (!AudioFocusRequest.isValidFocusGain(durationHint)) {
+        if (!AudioFocusRequest.isValidFocusGain(focusReqType)) {
             throw new IllegalArgumentException("Invalid duration hint");
         }
         if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
@@ -4663,7 +4664,7 @@
                     "Illegal null audio policy when locking audio focus");
         }
 
-        final AudioFocusRequest afr = new AudioFocusRequest.Builder(durationHint)
+        final AudioFocusRequest afr = new AudioFocusRequest.Builder(focusReqType)
                 .setOnAudioFocusChangeListenerInt(l, null /* no Handler for this legacy API */)
                 .setAudioAttributes(requestAttributes)
                 .setAcceptsDelayedFocusGain((flags & AUDIOFOCUS_FLAG_DELAY_OK)
@@ -5024,16 +5025,16 @@
      * to identify this use case.
      * @param streamType use STREAM_RING for focus requests when ringing, VOICE_CALL for
      *    the establishment of the call
-     * @param durationHint the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
+     * @param focusReqType the type of focus request. AUDIOFOCUS_GAIN_TRANSIENT is recommended so
      *    media applications resume after a call
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public void requestAudioFocusForCall(int streamType, int durationHint) {
+    public void requestAudioFocusForCall(int streamType, int focusReqType) {
         final IAudioService service = getService();
         try {
             service.requestAudioFocus(new AudioAttributes.Builder()
                         .setInternalLegacyStreamType(streamType).build(),
-                    durationHint, mICallBack, null,
+                    focusReqType, mICallBack, null,
                     AudioSystem.IN_VOICE_COMM_FOCUS_ID,
                     getContext().getOpPackageName(),
                     getContext().getAttributionTag(),
@@ -8828,7 +8829,7 @@
      * <p>This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
      * <p>From N onward, ringer mode adjustments that would toggle Do Not Disturb are not allowed
-     * unless the app has been granted Do Not Disturb Access.
+     * unless the app has been granted Notification Policy Access.
      * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      * <p>This API checks if the caller has the necessary permissions based on the provided
      * component name, uid, and pid values.
@@ -8869,7 +8870,7 @@
      * <p>This method has no effect if the device implements a fixed volume policy
      * as indicated by {@link #isVolumeFixed()}.
      * <p>From N onward, volume adjustments that would toggle Do Not Disturb are not allowed unless
-     * the app has been granted Do Not Disturb Access.
+     * the app has been granted Notification Policy Access.
      * See {@link NotificationManager#isNotificationPolicyAccessGranted()}.
      * <p>This API checks if the caller has the necessary permissions based on the provided
      * component name, uid, and pid values.
@@ -10127,6 +10128,24 @@
 
     /**
      * @hide
+     * Blocks until permission updates have propagated through the audio system.
+     * Only useful in tests, where adoptShellPermissions can change the permission state of
+     * an app without the app being killed.
+     */
+    @TestApi
+    @SuppressWarnings("UnflaggedApi") // @TestApi without associated feature.
+    public void permissionUpdateBarrier() {
+        final IAudioService service = getService();
+        try {
+            service.permissionUpdateBarrier();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
+     * @hide
      * Return the list of independent stream types for volume control.
      * A stream type is considered independent when the volume changes of that type do not
      * affect any other independent volume control stream type.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index a255f73..ebdfd3e 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2655,7 +2655,16 @@
     /**
      * Register a native listener for system property sysprop
      * @param callback the listener which fires when the property changes
+     * @return a native handle for use in subsequent methods
      * @hide
      */
-    public static native void listenForSystemPropertyChange(String sysprop, Runnable callback);
+    public static native long listenForSystemPropertyChange(String sysprop, Runnable callback);
+
+    /**
+     * Trigger a sysprop listener update, if the property has been updated: synchronously validating
+     * there are no pending sysprop changes.
+     * @param handle the handle returned by {@link listenForSystemPropertyChange}
+     * @hide
+     */
+    public static native void triggerSystemPropertyUpdate(long handle);
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index d20b7f0..a96562d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -101,6 +101,8 @@
 
     oneway void portEvent(in int portId, in int event, in @nullable PersistableBundle extras);
 
+    void permissionUpdateBarrier();
+
     // Java-only methods below.
     void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
 
@@ -250,7 +252,7 @@
 
     boolean isBluetoothA2dpOn();
 
-    int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
+    int requestAudioFocus(in AudioAttributes aa, int focusReqType, IBinder cb,
             IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
             in String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk);
 
@@ -560,7 +562,7 @@
 
     long getMaxAdditionalOutputDeviceDelay(in AudioDeviceAttributes device);
 
-    int requestAudioFocusForTest(in AudioAttributes aa, int durationHint, IBinder cb,
+    int requestAudioFocusForTest(in AudioAttributes aa, int focusReqType, IBinder cb,
             in IAudioFocusDispatcher fd, in String clientId, in String callingPackageName,
             int flags, int uid, int sdk);
 
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 1930c3d..b84990b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -288,8 +288,7 @@
 
     /**
      * Returns a proxy MediaRouter2 instance that allows you to control the routing of an app
-     * specified by {@code clientPackageName}. Returns {@code null} if the specified package name
-     * does not exist.
+     * specified by {@code clientPackageName}.
      *
      * <p>Proxy MediaRouter2 instances operate differently than regular MediaRouter2 instances:
      *
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index e78dc31..b448ecd 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.Utils.parseVibrationEffect;
+
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentProvider;
@@ -24,17 +26,23 @@
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources.NotFoundException;
 import android.database.Cursor;
+import android.media.audio.Flags;
 import android.media.audiofx.HapticGenerator;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.Trace;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
 import android.provider.Settings;
 import android.util.Log;
+
 import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -62,6 +70,11 @@
     // keep references on active Ringtones until stopped or completion listener called.
     private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>();
 
+    private static final VibrationAttributes VIBRATION_ATTRIBUTES =
+            new VibrationAttributes.Builder().setUsage(VibrationAttributes.USAGE_RINGTONE).build();
+
+    private static final int VIBRATION_LOOP_DELAY_MS = 200;
+
     private final Context mContext;
     private final AudioManager mAudioManager;
     private VolumeShaper.Configuration mVolumeShaperConfig;
@@ -95,6 +108,10 @@
     private float mVolume = 1.0f;
     private boolean mHapticGeneratorEnabled = false;
     private final Object mPlaybackSettingsLock = new Object();
+    private final Vibrator mVibrator;
+    private final boolean mRingtoneVibrationSupported;
+    private VibrationEffect mVibrationEffect;
+    private boolean mIsVibrating;
 
     /** {@hide} */
     @UnsupportedAppUsage
@@ -104,6 +121,8 @@
         mAllowRemote = allowRemote;
         mRemotePlayer = allowRemote ? mAudioManager.getRingtonePlayer() : null;
         mRemoteToken = allowRemote ? new Binder() : null;
+        mVibrator = mContext.getSystemService(Vibrator.class);
+        mRingtoneVibrationSupported = Utils.isRingtoneVibrationSettingsSupported(mContext);
     }
 
     /**
@@ -487,6 +506,23 @@
         if (mUri == null) {
             destroyLocalPlayer();
         }
+        if (Flags.enableRingtoneHapticsCustomization()
+                && mRingtoneVibrationSupported && mUri != null) {
+            mVibrationEffect = parseVibrationEffect(mVibrator, Utils.getVibrationUri(mUri));
+            if (mVibrationEffect != null) {
+                mVibrationEffect =
+                        mVibrationEffect.applyRepeatingIndefinitely(true, VIBRATION_LOOP_DELAY_MS);
+            }
+        }
+    }
+
+    /**
+     * Returns the {@link VibrationEffect} has been created for this ringtone.
+     * @hide
+     */
+    @VisibleForTesting
+    public VibrationEffect getVibrationEffect() {
+        return mVibrationEffect;
     }
 
     /** {@hide} */
@@ -530,6 +566,17 @@
                 Log.w(TAG, "Neither local nor remote playback available");
             }
         }
+        if (Flags.enableRingtoneHapticsCustomization() && mRingtoneVibrationSupported) {
+            playVibration();
+        }
+    }
+
+    private void playVibration() {
+        if (mVibrationEffect == null) {
+            return;
+        }
+        mIsVibrating = true;
+        mVibrator.vibrate(mVibrationEffect, VIBRATION_ATTRIBUTES);
     }
 
     /**
@@ -545,6 +592,11 @@
                 Log.w(TAG, "Problem stopping ringtone: " + e);
             }
         }
+        if (Flags.enableRingtoneHapticsCustomization()
+                && mRingtoneVibrationSupported && mIsVibrating) {
+            mVibrator.cancel();
+            mIsVibrating = false;
+        }
     }
 
     private void destroyLocalPlayer() {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 47e3a0f..f0ab6ec 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -34,6 +34,7 @@
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.StaleDataException;
+import android.media.audio.Flags;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
@@ -809,6 +810,12 @@
         // Don't set the stream type
         Ringtone ringtone = getRingtone(context, ringtoneUri, -1 /* streamType */,
                 volumeShaperConfig, false);
+        if (Flags.enableRingtoneHapticsCustomization()
+                && Utils.isRingtoneVibrationSettingsSupported(context)
+                && Utils.hasVibration(ringtoneUri) && hasHapticChannels(ringtoneUri)) {
+            audioAttributes = new AudioAttributes.Builder(
+                    audioAttributes).setHapticChannelsMuted(true).build();
+        }
         if (ringtone != null) {
             ringtone.setAudioAttributesField(audioAttributes);
             if (!ringtone.createLocalMediaPlayer()) {
diff --git a/media/java/android/media/Utils.java b/media/java/android/media/Utils.java
index d07f611..41e9b65 100644
--- a/media/java/android/media/Utils.java
+++ b/media/java/android/media/Utils.java
@@ -20,12 +20,17 @@
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.os.vibrator.persistence.ParsedVibration;
+import android.os.vibrator.persistence.VibrationXmlParser;
 import android.provider.OpenableColumns;
 import android.util.Log;
 import android.util.Pair;
@@ -36,7 +41,11 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -55,6 +64,8 @@
 public class Utils {
     private static final String TAG = "Utils";
 
+    public static final String VIBRATION_URI_PARAM = "vibration_uri";
+
     /**
      * Sorts distinct (non-intersecting) range array in ascending order.
      * @throws java.lang.IllegalArgumentException if ranges are not distinct
@@ -688,4 +699,78 @@
         }
         return anonymizeBluetoothAddress(address);
     }
+
+    /**
+     * Whether the device supports ringtone vibration settings.
+     *
+     * @param context the {@link Context}
+     * @return {@code true} if the device supports ringtone vibration
+     */
+    public static boolean isRingtoneVibrationSettingsSupported(Context context) {
+        final Resources res = context.getResources();
+        return res != null && res.getBoolean(
+                com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported);
+    }
+
+    /**
+     * Whether the given ringtone Uri has vibration Uri parameter
+     *
+     * @param ringtoneUri the ringtone Uri
+     * @return {@code true} if the Uri has vibration parameter
+     */
+    public static boolean hasVibration(Uri ringtoneUri) {
+        final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM);
+        return vibrationUriString != null;
+    }
+
+    /**
+     * Gets the vibration Uri from given ringtone Uri
+     *
+     * @param ringtoneUri the ringtone Uri
+     * @return parsed {@link Uri} of vibration parameter, {@code null} if the vibration parameter
+     * is not found.
+     */
+    public static Uri getVibrationUri(Uri ringtoneUri) {
+        final String vibrationUriString = ringtoneUri.getQueryParameter(VIBRATION_URI_PARAM);
+        if (vibrationUriString == null) {
+            return null;
+        }
+        return Uri.parse(vibrationUriString);
+    }
+
+    /**
+     * Returns the parsed {@link VibrationEffect} from given vibration Uri.
+     *
+     * @param vibrator the vibrator to resolve the vibration file
+     * @param vibrationUri the vibration file Uri to represent a vibration
+     */
+    @SuppressWarnings("FlaggedApi") // VibrationXmlParser is available internally as hidden APIs.
+    public static VibrationEffect parseVibrationEffect(Vibrator vibrator, Uri vibrationUri) {
+        if (vibrationUri == null) {
+            Log.w(TAG, "The vibration Uri is null.");
+            return null;
+        }
+        String filePath = vibrationUri.getPath();
+        if (filePath == null) {
+            Log.w(TAG, "The file path is null.");
+            return null;
+        }
+        File vibrationFile = new File(filePath);
+        if (vibrationFile.exists() && vibrationFile.canRead()) {
+            try {
+                FileInputStream fileInputStream = new FileInputStream(vibrationFile);
+                ParsedVibration parsedVibration =
+                        VibrationXmlParser.parseDocument(
+                                new InputStreamReader(fileInputStream, StandardCharsets.UTF_8));
+                return parsedVibration.resolve(vibrator);
+            } catch (IOException e) {
+                Log.e(TAG, "FileNotFoundException" + e);
+            }
+        } else {
+            // File not found or cannot be read
+            Log.w(TAG, "File exists:" + vibrationFile.exists()
+                    + ", canRead:" + vibrationFile.canRead());
+        }
+        return null;
+    }
 }
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index 7c41f96..7252135 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -144,3 +144,10 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_audio_input_device_routing_and_volume_control"
+    namespace: "media_better_together"
+    description: "Allows audio input devices routing and volume control via system settings."
+    bug: "355684672"
+}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 1c5049e..3cc0ad2 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -90,23 +90,24 @@
         mDisplayManager = displayManager;
     }
 
-    /**
-     * Register a listener to receive notifications about when the {@link MediaProjection} or
-     * captured content changes state.
-     *
-     * <p>The callback must be registered before invoking
-     * {@link #createVirtualDisplay(String, int, int, int, int, Surface, VirtualDisplay.Callback,
-     * Handler)} to ensure that any notifications on the callback are not missed. The client must
-     * implement {@link Callback#onStop()} and clean up any resources it is holding, e.g. the
-     * {@link VirtualDisplay} and {@link Surface}.
-     *
-     * @param callback The callback to call.
-     * @param handler  The handler on which the callback should be invoked, or
-     *                 null if the callback should be invoked on the calling thread's looper.
-     * @throws NullPointerException If the given callback is null.
-     * @see #unregisterCallback
-     */
-    public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
+  /**
+   * Register a listener to receive notifications about when the {@link MediaProjection} or captured
+   * content changes state.
+   *
+   * <p>The callback must be registered before invoking {@link #createVirtualDisplay(String, int,
+   * int, int, int, Surface, VirtualDisplay.Callback, Handler)} to ensure that any notifications on
+   * the callback are not missed. The client must implement {@link Callback#onStop()} and clean up
+   * any resources it is holding, e.g. the {@link VirtualDisplay} and {@link Surface}. This should
+   * also update any application UI indicating the MediaProjection status as MediaProjection has
+   * stopped.
+   *
+   * @param callback The callback to call.
+   * @param handler The handler on which the callback should be invoked, or null if the callback
+   *     should be invoked on the calling thread's looper.
+   * @throws NullPointerException If the given callback is null.
+   * @see #unregisterCallback
+   */
+  public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
         try {
             final Callback c = Objects.requireNonNull(callback);
             if (handler == null) {
@@ -158,74 +159,67 @@
         return createVirtualDisplay(builder, callback, handler);
     }
 
-    /**
-     * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
-     * contents of the screen.
-     *
-     * <p>To correctly clean up resources associated with a capture, the application must register a
-     * {@link Callback} before invocation. The app must override {@link Callback#onStop()} to clean
-     * up (by invoking{@link VirtualDisplay#release()}, {@link Surface#release()} and related
-     * resources).
-     *
-     * @param name     The name of the virtual display, must be non-empty.
-     * @param width    The width of the virtual display in pixels. Must be greater than 0.
-     * @param height   The height of the virtual display in pixels. Must be greater than 0.
-     * @param dpi      The density of the virtual display in dpi. Must be greater than 0.
-     * @param surface  The surface to which the content of the virtual display should be rendered,
-     *                 or null if there is none initially.
-     * @param flags    A combination of virtual display flags. See {@link DisplayManager} for the
-     *                 full list of flags. Note that
-     *                 {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}
-     *                 is always enabled. The following flags may be overridden, depending on how
-     *                 the component with {android.Manifest.permission.MANAGE_MEDIA_PROJECTION}
-     *                 handles the user's consent:
-     *                 {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
-     *                 {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR},
-     *                 {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}.
-     * @param callback Callback invoked when the virtual display's state changes, or null.
-     * @param handler  The {@link android.os.Handler} on which the callback should be invoked, or
-     *                 null if the callback should be invoked on the calling thread's main
-     *                 {@link android.os.Looper}.
-     * @throws IllegalStateException If the target SDK is {@link
-     *                               android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up, and
-     *                               if no {@link Callback} is registered.
-     * @throws SecurityException In any of the following scenarios:
-     *                               <ol>
-     *                                 <li>If attempting to create a new virtual display
-     *                                 associated with this MediaProjection instance after it has
-     *                                 been stopped by invoking {@link #stop()}.
-     *                                 <li>If attempting to create a new virtual display
-     *                                 associated with this MediaProjection instance after a
-     *                                 {@link MediaProjection.Callback#onStop()} callback has been
-     *                                 received due to the user or the system stopping the
-     *                                 MediaProjection session.
-     *                                 <li>If the target SDK is {@link
-     *                                 android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up,
-     *                                 and if this instance has already taken a recording through
-     *                                 {@code #createVirtualDisplay}, but {@link #stop()} wasn't
-     *                                 invoked to end the recording.
-     *                                 <li>If the target SDK is {@link
-     *                                 android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up,
-     *                                 and if {@link MediaProjectionManager#getMediaProjection}
-     *                                 was invoked more than once to get this
-     *                                 {@code MediaProjection} instance.
-     *                               </ol>
-     *                               In cases 2 & 3, no exception is thrown if the target SDK is
-     *                               less than
-     *                               {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U}.
-     *                               Instead, recording doesn't begin until the user re-grants
-     *                               consent in the dialog.
-     * @return The created {@link VirtualDisplay}, or {@code null} if no {@link VirtualDisplay}
-     * could be created.
-     * @see VirtualDisplay
-     * @see VirtualDisplay.Callback
-     */
-    @SuppressWarnings("RequiresPermission")
-    @Nullable
-    public VirtualDisplay createVirtualDisplay(@NonNull String name,
-            int width, int height, int dpi, @VirtualDisplayFlag int flags,
-            @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback,
-            @Nullable Handler handler) {
+  /**
+   * Creates a {@link android.hardware.display.VirtualDisplay} to capture the contents of the
+   * screen.
+   *
+   * <p>To correctly clean up resources associated with a capture, the application must register a
+   * {@link Callback} before invocation. The app must override {@link Callback#onStop()} to clean up
+   * resources (by invoking{@link VirtualDisplay#release()}, {@link Surface#release()} and related
+   * resources) and to update any available UI regarding the MediaProjection status.
+   *
+   * @param name The name of the virtual display, must be non-empty.
+   * @param width The width of the virtual display in pixels. Must be greater than 0.
+   * @param height The height of the virtual display in pixels. Must be greater than 0.
+   * @param dpi The density of the virtual display in dpi. Must be greater than 0.
+   * @param surface The surface to which the content of the virtual display should be rendered, or
+   *     null if there is none initially.
+   * @param flags A combination of virtual display flags. See {@link DisplayManager} for the full
+   *     list of flags. Note that {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION} is always
+   *     enabled. The following flags may be overridden, depending on how the component with
+   *     {android.Manifest.permission.MANAGE_MEDIA_PROJECTION} handles the user's consent: {@link
+   *     DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, {@link
+   *     DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}, {@link
+   *     DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}.
+   * @param callback Callback invoked when the virtual display's state changes, or null.
+   * @param handler The {@link android.os.Handler} on which the callback should be invoked, or null
+   *     if the callback should be invoked on the calling thread's main {@link android.os.Looper}.
+   * @throws IllegalStateException If the target SDK is {@link
+   *     android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and up, and if no {@link Callback} is
+   *     registered.
+   * @throws SecurityException In any of the following scenarios:
+   *     <ol>
+   *       <li>If attempting to create a new virtual display associated with this MediaProjection
+   *           instance after it has been stopped by invoking {@link #stop()}.
+   *       <li>If attempting to create a new virtual display associated with this MediaProjection
+   *           instance after a {@link MediaProjection.Callback#onStop()} callback has been received
+   *           due to the user or the system stopping the MediaProjection session.
+   *       <li>If the target SDK is {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and
+   *           up, and if this instance has already taken a recording through {@code
+   *           #createVirtualDisplay}, but {@link #stop()} wasn't invoked to end the recording.
+   *       <li>If the target SDK is {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U} and
+   *           up, and if {@link MediaProjectionManager#getMediaProjection} was invoked more than
+   *           once to get this {@code MediaProjection} instance.
+   *     </ol>
+   *     In cases 2 & 3, no exception is thrown if the target SDK is less than {@link
+   *     android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE U}. Instead, recording doesn't begin until
+   *     the user re-grants consent in the dialog.
+   * @return The created {@link VirtualDisplay}, or {@code null} if no {@link VirtualDisplay} could
+   *     be created.
+   * @see VirtualDisplay
+   * @see VirtualDisplay.Callback
+   */
+  @SuppressWarnings("RequiresPermission")
+  @Nullable
+  public VirtualDisplay createVirtualDisplay(
+      @NonNull String name,
+      int width,
+      int height,
+      int dpi,
+      @VirtualDisplayFlag int flags,
+      @Nullable Surface surface,
+      @Nullable VirtualDisplay.Callback callback,
+      @Nullable Handler handler) {
         if (shouldMediaProjectionRequireCallback()) {
             if (mCallbacks.isEmpty()) {
                 final IllegalStateException e = new IllegalStateException(
@@ -322,14 +316,20 @@
          * Called when the MediaProjection session is no longer valid.
          *
          * <p>Once a MediaProjection has been stopped, it's up to the application to release any
-         * resources it may be holding (e.g. releasing the {@link VirtualDisplay} and
-         * {@link Surface}).
+         * resources it may be holding (e.g. releasing the {@link VirtualDisplay} and {@link
+         * Surface}). If the application is displaying any UI indicating the MediaProjection state
+         * it should be updated to indicate that MediaProjection is no longer active.
          *
-         * <p>After this callback any call to
-         * {@link MediaProjection#createVirtualDisplay} will fail, even if no such
-         * {@link VirtualDisplay} was ever created for this MediaProjection session.
+         * <p>MediaProjection stopping can be a result of the system stopping the ongoing
+         * MediaProjection due to various reasons, such as another MediaProjection session starting.
+         * MediaProjection may also stop due to the user explicitly stopping ongoing MediaProjection
+         * via any available system-level UI.
+         *
+         * <p>After this callback any call to {@link MediaProjection#createVirtualDisplay} will
+         * fail, even if no such {@link VirtualDisplay} was ever created for this MediaProjection
+         * session.
          */
-        public void onStop() { }
+        public void onStop() {}
 
         /**
          * Invoked immediately after capture begins or when the size of the captured region changes,
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 7a7137a..03fd2c6 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -64,7 +64,9 @@
  *       holding, e.g. the {@link VirtualDisplay} and {@link Surface}. The MediaProjection may
  *       further no longer create any new {@link VirtualDisplay}s via {@link
  *       MediaProjection#createVirtualDisplay(String, int, int, int, int, Surface,
- *       VirtualDisplay.Callback, Handler)}.
+ *       VirtualDisplay.Callback, Handler)}. Note that the `onStop()` callback can be a result of
+ *       the system stopping MediaProjection due to various reasons or the user stopping the
+ *       MediaProjection via UI affordances in system-level UI.
  *   <li>Start the screen capture session for media projection by calling {@link
  *       MediaProjection#createVirtualDisplay(String, int, int, int, int, Surface,
  *       android.hardware.display.VirtualDisplay.Callback, Handler)}.
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index be93abb..87cb3e7 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -70,6 +70,7 @@
     void releaseSession(in IBinder sessionToken, int userId);
     int getClientPid(in String sessionId);
     int getClientPriority(int useCase, in String sessionId);
+    int getClientUserId(in String sessionId);
 
     void setMainSession(in IBinder sessionToken, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index aed3e60..25b6bfa 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -760,6 +760,14 @@
      * @hide
      */
     public static final int UNKNOWN_CLIENT_PID = -1;
+    /**
+     * An unknown state of the client userId gets from the TvInputManager. Client gets this value
+     * when query through {@link #getClientUserId(String sessionId)} fails.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
+    public static final int UNKNOWN_CLIENT_USER_ID = -1;
 
     /**
      * Broadcast intent action when the user blocked content ratings change. For use with the
@@ -2510,6 +2518,21 @@
     };
 
     /**
+     * Get a the client user id when creating the session with the session id provided.
+     *
+     * @param sessionId a String of session id that is used to query the client user id.
+     * @return the client pid when created the session. Returns {@link #UNKNOWN_CLIENT_USER_ID}
+     *         if the call fails.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+    @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
+    public int getClientUserId(@NonNull String sessionId) {
+        return getClientUserIdInternal(sessionId);
+    }
+
+    /**
      * Returns a priority for the given use case type and the client's foreground or background
      * status.
      *
@@ -2599,6 +2622,18 @@
         return clientPid;
     }
 
+    @FlaggedApi(Flags.FLAG_KIDS_MODE_TVDB_SHARING)
+    private int getClientUserIdInternal(String sessionId) {
+        Preconditions.checkNotNull(sessionId);
+        int clientUserId = UNKNOWN_CLIENT_USER_ID;
+        try {
+            clientUserId = mService.getClientUserId(sessionId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return clientUserId;
+    }
+
     private int getClientPriorityInternal(int useCase, String sessionId) {
         try {
             return mService.getClientPriority(useCase, sessionId);
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index e604cb7..82e6ed3 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -91,7 +91,7 @@
     private static final Object sMainTvViewLock = new Object();
     private static WeakReference<TvView> sMainTvView = NULL_TV_VIEW;
 
-    private final Handler mHandler = new Handler();
+    private Handler mHandler = new Handler();
     private Session mSession;
     private SurfaceView mSurfaceView;
     private Surface mSurface;
@@ -207,6 +207,17 @@
         mCallback = callback;
     }
 
+    /**
+     * Sets the handler to be invoked when an event is dispatched to this TvView.
+     * If handler is not set by this function, TvView will use its default handler.
+     *
+     * @param handler The handler to handle events.
+     * @hide
+     */
+    public void setHandler(@NonNull Handler handler) {
+        mHandler = handler;
+    }
+
     /** @hide */
     public Session getInputSession() {
         return mSession;
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 3196ba1..93bca21 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -26,9 +26,25 @@
 }
 
 flag {
-    name: "tis_always_bound_permission"
+    name: "tif_unbind_inactive_tis"
     is_exported: true
     namespace: "media_tv"
-    description: "Introduce ALWAYS_BOUND_TV_INPUT for TIS."
-    bug: "332201346"
-}
\ No newline at end of file
+    description: "Unbind hardware TIS when not needed"
+    bug: "279189366"
+}
+
+flag {
+    name: "kids_mode_tvdb_sharing"
+    is_exported: true
+    namespace: "media_tv"
+    description: "Performance and Storage Optimization in Google TV Kids Mode."
+    bug: "288383796"
+}
+
+flag {
+    name: "tuner_w_apis"
+    is_exported: true
+    namespace: "media_tv"
+    description: "Tuner V4.0 APIs for Android W"
+    bug: "320419647"
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index f28c2c1..92f6eaf 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -18,6 +18,7 @@
 
 import android.annotation.BytesLong;
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -32,6 +33,7 @@
 import android.hardware.tv.tuner.FrontendScanType;
 import android.media.MediaCodec;
 import android.media.tv.TvInputService;
+import android.media.tv.flags.Flags;
 import android.media.tv.tuner.dvr.DvrPlayback;
 import android.media.tv.tuner.dvr.DvrRecorder;
 import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
@@ -288,6 +290,7 @@
     private static int sTunerVersion = TunerVersionChecker.TUNER_VERSION_UNKNOWN;
     private DemuxInfo mDesiredDemuxInfo = new DemuxInfo(Filter.TYPE_UNDEFINED);
 
+    private boolean mClosed = false;
     private Frontend mFrontend;
     private EventHandler mHandler;
     @Nullable
@@ -813,6 +816,9 @@
      */
     @Override
     public void close() {
+        if (mClosed) {
+            return;
+        }
         acquireTRMSLock("close()");
         try {
             releaseAll();
@@ -820,6 +826,7 @@
             TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner");
         } finally {
             releaseTRMSLock();
+            mClosed = true;
         }
     }
 
@@ -888,19 +895,14 @@
         try {
             if (mFrontendCiCamHandle != null) {
                 if (DEBUG) {
-                    Log.d(TAG, "unlinking CiCam : " + mFrontendCiCamHandle + " for " +  mClientId);
+                    Log.d(TAG, "releasing CiCam : " + mFrontendCiCamHandle + " for " +  mClientId);
                 }
-                int result = nativeUnlinkCiCam(mFrontendCiCamId);
-                if (result == RESULT_SUCCESS) {
-                    mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
-                    replicateCiCamSettings(null);
-                } else {
-                    Log.e(TAG, "nativeUnlinkCiCam(" + mFrontendCiCamHandle + ") for mClientId:"
-                            + mClientId + "failed with result:" + result);
-                }
+                nativeUnlinkCiCam(mFrontendCiCamId);
+                mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+                replicateCiCamSettings(null);
             } else {
                 if (DEBUG) {
-                    Log.d(TAG, "NOT unlinking CiCam : " + mClientId);
+                    Log.d(TAG, "NOT releasing CiCam : " + mClientId);
                 }
             }
         } finally {
@@ -1665,11 +1667,9 @@
                 if (mFrontendCiCamHandle != null && mFrontendCiCamId != null
                         && mFrontendCiCamId == ciCamId) {
                     int result = nativeUnlinkCiCam(ciCamId);
-                    if (result == RESULT_SUCCESS) {
-                        mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
-                        mFrontendCiCamId = null;
-                        mFrontendCiCamHandle = null;
-                    }
+                    mTunerResourceManager.releaseCiCam(mFrontendCiCamHandle, mClientId);
+                    mFrontendCiCamId = null;
+                    mFrontendCiCamHandle = null;
                     return result;
                 }
             }
@@ -2531,6 +2531,53 @@
     }
 
     /**
+     * Request a frontend by frontend type.
+     *
+     * <p> This API is used (before {@link #tune(FrontendSettings)}) if the applications want to
+     * select a frontend of a particular type for {@link #tune(FrontendSettings)} when there are
+     * multiple frontends of the same type present, allowing the system to select which one is
+     * applied. The applied frontend will be one of the not-in-use frontends. If all frontends are
+     * in-use, this API will reclaim and apply the frontend owned by the lowest priority client if
+     * current client has higher priority. Otherwise, this API will not apply any frontend and
+     * return {@link #RESULT_UNAVAILABLE}.
+     *
+     * @param desiredFrontendType the Type of the desired fronted. Should be one of
+     *                            {@link android.media.tv.tuner.frontend.FrontendSettings.Type}
+     * @return result status of open operation.
+     * @see #applyFrontend(FrontendInfo)
+     * @see #tune(FrontendSettings)
+     */
+    @Result
+    @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+    @RequiresPermission(
+        allOf = {"android.permission.TUNER_RESOURCE_ACCESS", "android.permission.ACCESS_TV_TUNER"})
+    public int applyFrontendByType(@FrontendSettings.Type int desiredFrontendType) {
+        mFrontendLock.lock();
+        try {
+            if (mFeOwnerTuner != null) {
+                Log.e(TAG, "Operation connot be done by sharee of tuner");
+                return RESULT_INVALID_STATE;
+            }
+            if (mFrontendHandle != null) {
+                Log.e(TAG, "A frontend has been opened before");
+                return RESULT_INVALID_STATE;
+            }
+
+            mDesiredFrontendId = null;
+            mFrontendType = desiredFrontendType;
+            if (DEBUG) {
+                Log.d(TAG, "Applying frontend with type " + mFrontendType);
+            }
+            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
+                return RESULT_UNAVAILABLE;
+            }
+            return RESULT_SUCCESS;
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
      * Open a shared filter instance.
      *
      * @param context the context of the caller.
diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java
index f29a93c..a7c0415 100644
--- a/media/java/android/media/tv/tuner/TunerVersionChecker.java
+++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java
@@ -16,9 +16,11 @@
 
 package android.media.tv.tuner;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.media.tv.flags.Flags;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -40,7 +42,7 @@
     /** @hide */
     @IntDef(prefix = "TUNER_VERSION_",
             value = {TUNER_VERSION_UNKNOWN, TUNER_VERSION_1_0, TUNER_VERSION_1_1,
-                    TUNER_VERSION_2_0})
+                    TUNER_VERSION_2_0, TUNER_VERSION_3_0, TUNER_VERSION_4_0})
     @Retention(RetentionPolicy.SOURCE)
     public @interface TunerVersion {}
     /**
@@ -63,6 +65,11 @@
      * Tuner version 3.0.
      */
     public static final int TUNER_VERSION_3_0 = (3 << 16);
+    /**
+     * Tuner version 4.0.
+     */
+    @FlaggedApi(Flags.FLAG_TUNER_W_APIS)
+    public static final int TUNER_VERSION_4_0 = (4 << 16);
 
     /**
      * Get the current running Tuner version.
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 7f487e5..c44e26f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -92,7 +92,7 @@
         "[email protected]",
         "[email protected]",
         "android.hardware.drm-V1-ndk",
-        "android.hardware.tv.tuner-V2-ndk",
+        "android.hardware.tv.tuner-V3-ndk",
     ],
 
     header_libs: [
@@ -180,7 +180,7 @@
 
     shared_libs: [
         "[email protected]",
-        "android.hardware.tv.tuner-V2-ndk",
+        "android.hardware.tv.tuner-V3-ndk",
         "libbinder_ndk",
         "libandroid_runtime",
         "libcutils",
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 371e3d2..019b1e0 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -17,35 +17,31 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "ImageReader_JNI"
 #define ATRACE_TAG ATRACE_TAG_CAMERA
-#include "android_media_Utils.h"
+#include <android/hardware_buffer_jni.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_graphics_GraphicBuffer.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <android_runtime/android_view_Surface.h>
+#include <com_android_graphics_libgui_flags.h>
 #include <cutils/atomic.h>
-#include <utils/Log.h>
-#include <utils/misc.h>
+#include <grallocusage/GrallocUsageConversion.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+#include <inttypes.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <stdint.h>
+#include <ui/Rect.h>
 #include <utils/List.h>
-#include <utils/Trace.h>
+#include <utils/Log.h>
 #include <utils/String8.h>
+#include <utils/Trace.h>
+#include <utils/misc.h>
 
 #include <cstdio>
 
-#include <gui/BufferItemConsumer.h>
-#include <gui/Surface.h>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_view_Surface.h>
-#include <android_runtime/android_graphics_GraphicBuffer.h>
-#include <android_runtime/android_hardware_HardwareBuffer.h>
-#include <grallocusage/GrallocUsageConversion.h>
-
-#include <private/android/AHardwareBufferHelpers.h>
-
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-
-#include <stdint.h>
-#include <inttypes.h>
-#include <android/hardware_buffer_jni.h>
-
-#include <ui/Rect.h>
+#include "android_media_Utils.h"
 
 #define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID       "mNativeContext"
 #define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID   "mNativeBuffer"
@@ -393,18 +389,25 @@
     }
     sp<JNIImageReaderContext> ctx(new JNIImageReaderContext(env, weakThiz, clazz, maxImages));
 
-    sp<IGraphicBufferProducer> gbProducer;
-    sp<IGraphicBufferConsumer> gbConsumer;
-    BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
-    sp<BufferItemConsumer> bufferConsumer;
     String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
             width, height, nativeHalFormat, maxImages, getpid(),
             createProcessUniqueId());
     uint64_t consumerUsage =
             android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
 
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+    sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumerUsage, maxImages,
+                                                                   /*controlledByApp*/ true);
+    sp<IGraphicBufferProducer> gbProducer =
+            bufferConsumer->getSurface()->getIGraphicBufferProducer();
+#else
+    sp<IGraphicBufferProducer> gbProducer;
+    sp<IGraphicBufferConsumer> gbConsumer;
+    BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
+    sp<BufferItemConsumer> bufferConsumer;
     bufferConsumer = new BufferItemConsumer(gbConsumer, consumerUsage, maxImages,
             /*controlledByApp*/true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     if (bufferConsumer == nullptr) {
         jniThrowExceptionFmt(env, "java/lang/RuntimeException",
                 "Failed to allocate native buffer consumer for hal format 0x%x and usage 0x%x",
@@ -413,7 +416,11 @@
     }
 
     if (consumerUsage & GRALLOC_USAGE_PROTECTED) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+        bufferConsumer->setConsumerIsProtected(true);
+#else
         gbConsumer->setConsumerIsProtected(true);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
     }
 
     ctx->setBufferConsumer(bufferConsumer);
diff --git a/media/jni/android_media_MediaCodecLinearBlock.h b/media/jni/android_media_MediaCodecLinearBlock.h
index 060abfd..ffbf0a8 100644
--- a/media/jni/android_media_MediaCodecLinearBlock.h
+++ b/media/jni/android_media_MediaCodecLinearBlock.h
@@ -62,7 +62,7 @@
             std::shared_ptr<C2Buffer> buffer =
                 C2Buffer::CreateLinearBuffer(block.subBlock(offset, size));
             for (const std::shared_ptr<const C2Info> &info : mBuffer->info()) {
-                std::shared_ptr<C2Param> param = std::move(C2Param::Copy(*info));
+                std::shared_ptr<C2Param> param = C2Param::Copy(*info);
                 buffer->setInfo(std::static_pointer_cast<C2Info>(param));
             }
             return buffer;
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
index 1bb82f8..4637ccd 100644
--- a/media/mca/filterfw/native/core/gl_env.cpp
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -15,21 +15,23 @@
  */
 // #define LOG_NDEBUG 0
 
-#include "base/logging.h"
-#include "base/utilities.h"
 #include "core/gl_env.h"
-#include "core/shader_program.h"
-#include "core/vertex_frame.h"
-#include "system/window.h"
+
+#include <EGL/eglext.h>
+#include <com_android_graphics_libgui_flags.h>
+#include <gui/BufferQueue.h>
+#include <gui/GLConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
 
 #include <map>
 #include <string>
-#include <EGL/eglext.h>
 
-#include <gui/BufferQueue.h>
-#include <gui/Surface.h>
-#include <gui/GLConsumer.h>
-#include <gui/IGraphicBufferProducer.h>
+#include "base/logging.h"
+#include "base/utilities.h"
+#include "core/shader_program.h"
+#include "core/vertex_frame.h"
+#include "system/window.h"
 
 namespace android {
 namespace filterfw {
@@ -165,12 +167,18 @@
   }
 
   // Create dummy surface using a GLConsumer
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+  surfaceTexture_ = new GLConsumer(0, GLConsumer::TEXTURE_EXTERNAL, /*useFenceSync=*/true,
+                                   /*isControlledByApp=*/false);
+  window_ = surfaceTexture_->getSurface();
+#else
   sp<IGraphicBufferProducer> producer;
   sp<IGraphicBufferConsumer> consumer;
   BufferQueue::createBufferQueue(&producer, &consumer);
   surfaceTexture_ = new GLConsumer(consumer, 0, GLConsumer::TEXTURE_EXTERNAL,
           true, false);
   window_ = new Surface(producer);
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
 
   surfaces_[0] = SurfaceWindowPair(eglCreateWindowSurface(display(), config, window_.get(), NULL), NULL);
   if (CheckEGLError("eglCreateWindowSurface")) return false;
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index a991a71f..7acb8c7 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -74,8 +74,4 @@
     symbol_file: "libamidi.map.txt",
 
     first_version: "29",
-    export_header_libs: [
-        "amidi",
-    ],
-
 }
diff --git a/media/tests/AudioPolicyTest/Android.bp b/media/tests/AudioPolicyTest/Android.bp
index 3dc2a0a..43b1a35 100644
--- a/media/tests/AudioPolicyTest/Android.bp
+++ b/media/tests/AudioPolicyTest/Android.bp
@@ -24,3 +24,11 @@
     resource_dirs: ["res"],
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "audiopolicytest_audiopolicytest_audiopolicydeathtest_Presubmit",
+    base: "audiopolicytest",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.audiopolicytest.AudioPolicyDeathTest"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
diff --git a/media/tests/mediatestutils/Android.bp b/media/tests/mediatestutils/Android.bp
index 88938e2..8c68f21 100644
--- a/media/tests/mediatestutils/Android.bp
+++ b/media/tests/mediatestutils/Android.bp
@@ -27,9 +27,11 @@
     name: "mediatestutils",
     srcs: [
         "java/com/android/media/mediatestutils/TestUtils.java",
+        "java/com/android/media/mediatestutils/PermissionUpdateBarrierRule.java",
     ],
     static_libs: [
         "androidx.concurrent_concurrent-futures",
+        "androidx.test.runner",
         "guava",
         "mediatestutils_host",
     ],
diff --git a/media/tests/mediatestutils/java/com/android/media/mediatestutils/PermissionUpdateBarrierRule.java b/media/tests/mediatestutils/java/com/android/media/mediatestutils/PermissionUpdateBarrierRule.java
new file mode 100644
index 0000000..c51b5de
--- /dev/null
+++ b/media/tests/mediatestutils/java/com/android/media/mediatestutils/PermissionUpdateBarrierRule.java
@@ -0,0 +1,59 @@
+/*
+ * 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.media.mediatestutils;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Barrier to wait for permission updates to propagate to audioserver, to avoid flakiness when using
+ * {@code com.android.compatability.common.util.AdoptShellPermissionsRule}. Note, this rule should
+ * <b> always </b> be placed after the adopt permission rule. Don't use rule when changing
+ * permission state in {@code @Before}, since that executes after all rules.
+ */
+public class PermissionUpdateBarrierRule implements TestRule {
+
+    private final Context mContext;
+
+    /**
+     * @param context the context to use
+     */
+    public PermissionUpdateBarrierRule(Context context) {
+        mContext = context;
+    }
+
+    public PermissionUpdateBarrierRule() {
+        this(InstrumentationRegistry.getInstrumentation().getContext());
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                mContext.getSystemService(AudioManager.class).permissionUpdateBarrier();
+                base.evaluate();
+            }
+        };
+    }
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index c4c4102..3eb99c3 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -27,9 +27,6 @@
     symbol_file: "libandroid.map.txt",
     first_version: "9",
     unversioned_until: "current",
-    export_header_libs: [
-        "libandroid_headers",
-    ],
 }
 
 cc_defaults {
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index cf7aea4..5b6b6c0 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -222,6 +222,10 @@
     field public static final String CATEGORY_PAYMENT = "payment";
     field public static final String EXTRA_CATEGORY = "category";
     field public static final String EXTRA_SERVICE_COMPONENT = "component";
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2; // 0x2
+    field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1; // 0xffffffff
     field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1
     field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2
     field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0
@@ -232,6 +236,8 @@
     method public final void notifyUnhandled();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract void onDeactivated(int);
+    method @FlaggedApi("android.nfc.nfc_event_listener") public void onObserveModeStateChanged(boolean);
+    method @FlaggedApi("android.nfc.nfc_event_listener") public void onPreferredServiceChanged(boolean);
     method public abstract byte[] processCommandApdu(byte[], android.os.Bundle);
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>);
     method public final void sendResponseApdu(byte[]);
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 3375e18c..cc5ff81 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -57,14 +57,38 @@
 
   @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List<java.lang.String> getActiveNfceeList();
     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 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 synchronizeScreenState();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
+    field public static final int HCE_ACTIVATE = 1; // 0x1
+    field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2
+    field public static final int HCE_DEACTIVATE = 3; // 0x3
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1
   }
 
   public static interface NfcOemExtension.Callback {
+    method public void onApplyRouting(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onBootFinished(int);
+    method public void onBootStarted();
+    method public void onCardEmulationActivated(boolean);
+    method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onDisableFinished(int);
+    method public void onDisableStarted();
+    method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onEnableFinished(int);
+    method public void onEnableStarted();
+    method public void onHceEventReceived(int);
+    method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onReaderOptionChanged(boolean);
+    method public void onRfDiscoveryStarted(boolean);
+    method public void onRfFieldActivated(boolean);
+    method public void onRoutingChanged();
+    method public void onStateUpdated(int);
     method public void onTagConnected(boolean, @NonNull android.nfc.Tag);
+    method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>);
   }
 
 }
@@ -74,6 +98,9 @@
   public final class CardEmulation {
     method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<android.nfc.cardemulation.ApduServiceInfo> getServices(@NonNull String, int);
+    method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported();
+    method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int);
+    method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity);
   }
 
 }
diff --git a/nfc/api/system-lint-baseline.txt b/nfc/api/system-lint-baseline.txt
index 761c8e6..c7a6181 100644
--- a/nfc/api/system-lint-baseline.txt
+++ b/nfc/api/system-lint-baseline.txt
@@ -9,6 +9,18 @@
     Field 'ACTION_TRANSACTION_DETECTED' is missing @BroadcastBehavior
 
 
+CallbackMethodName: android.nfc.NfcOemExtension.Callback#shouldSkipRoutingChange():
+    Callback method names must follow the on<Something> style: shouldSkipRoutingChange
+
+
+MethodNameTense: android.nfc.NfcOemExtension.Callback#onEnable():
+    Unexpected tense; probably meant `enabled`, was `onEnable`
+
+
+MissingNullability: android.nfc.cardemulation.CardEmulation#overrideRoutingTable(android.app.Activity, String, String) parameter #1:
+    Missing nullability on parameter `protocol` in method `overrideRoutingTable`
+MissingNullability: android.nfc.cardemulation.CardEmulation#overrideRoutingTable(android.app.Activity, String, String) parameter #2:
+    Missing nullability on parameter `technology` in method `overrideRoutingTable`
 MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent):
     Missing nullability on method `onBind` return
 MissingNullability: android.nfc.cardemulation.OffHostApduService#onBind(android.content.Intent) parameter #0:
@@ -96,10 +108,12 @@
 RequiresPermission: android.nfc.tech.TagTechnology#connect():
     Method 'connect' documentation mentions permissions without declaring @RequiresPermission
 
+
 SamShouldBeLast: android.nfc.NfcAdapter#enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle):
     SAM-compatible parameters (such as parameter 2, "callback", in android.nfc.NfcAdapter.enableReaderMode) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.nfc.NfcAdapter#ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler):
     SAM-compatible parameters (such as parameter 3, "tagRemovedListener", in android.nfc.NfcAdapter.ignore) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 
+
 SdkConstant: android.nfc.NfcAdapter#ACTION_REQUIRE_UNLOCK_FOR_NFC:
     Field 'ACTION_REQUIRE_UNLOCK_FOR_NFC' is missing @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index 90ca92f..e2ec952 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -62,7 +62,7 @@
 
     void dispatch(in Tag tag);
 
-    void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
+    void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras, String pkg);
 
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
     void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
@@ -88,7 +88,7 @@
     boolean isReaderOptionEnabled();
     boolean isReaderOptionSupported();
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
-    boolean enableReaderOption(boolean enable);
+    boolean enableReaderOption(boolean enable, in String pkg);
     boolean isObserveModeSupported();
     boolean isObserveModeEnabled();
     boolean setObserveMode(boolean enabled, String pkg);
@@ -100,7 +100,7 @@
     void unregisterWlcStateListener(in INfcWlcStateListener listener);
     WlcListenerDeviceInfo getWlcListenerDeviceInfo();
 
-    void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
+    void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags, String pkg);
 
     void notifyPollingLoop(in PollingFrame frame);
     void notifyHceDeactivated();
@@ -113,4 +113,5 @@
     void clearPreference();
     void setScreenState();
     void checkFirmware();
+    List<String> fetchActiveNfceeList();
 }
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index cb97f23..19b9e0f 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -48,6 +48,7 @@
     boolean setServiceEnabledForCategoryOther(int userHandle, in ComponentName app, boolean status);
     boolean isDefaultPaymentRegistered();
 
-    boolean overrideRoutingTable(int userHandle, String protocol, String technology);
-    boolean recoverRoutingTable(int userHandle);
+    void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg);
+    void recoverRoutingTable(int userHandle);
+    boolean isEuiccSupported();
 }
diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
index 6c9096d..e49ef7e 100644
--- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
+++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl
@@ -16,10 +16,29 @@
 package android.nfc;
 
 import android.nfc.Tag;
+import android.os.ResultReceiver;
 
 /**
  * @hide
  */
 interface INfcOemExtensionCallback {
    void onTagConnected(boolean connected, in Tag tag);
+   void onStateUpdated(int state);
+   void onApplyRouting(in ResultReceiver isSkipped);
+   void onNdefRead(in ResultReceiver isSkipped);
+   void onEnable(in ResultReceiver isAllowed);
+   void onDisable(in ResultReceiver isAllowed);
+   void onBootStarted();
+   void onEnableStarted();
+   void onDisableStarted();
+   void onBootFinished(int status);
+   void onEnableFinished(int status);
+   void onDisableFinished(int status);
+   void onTagDispatch(in ResultReceiver isSkipped);
+   void onRoutingChanged();
+   void onHceEventReceived(int action);
+   void onReaderOptionChanged(boolean enabled);
+   void onCardEmulationActivated(boolean isActivated);
+   void onRfFieldActivated(boolean isActivated);
+   void onRfDiscoveryStarted(boolean isDiscoveryStarted);
 }
diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java
index 0eb846d..909eca7 100644
--- a/nfc/java/android/nfc/NfcActivityManager.java
+++ b/nfc/java/android/nfc/NfcActivityManager.java
@@ -236,7 +236,8 @@
 
     public void setReaderMode(Binder token, int flags, Bundle extras) {
         if (DBG) Log.d(TAG, "Setting reader mode");
-        NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(token, this, flags, extras));
+        NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(
+                token, this, flags, extras, mAdapter.getContext().getPackageName()));
     }
 
     /**
@@ -395,7 +396,8 @@
 
     private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
         NfcAdapter.callService(
-            () -> NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech));
+                () -> NfcAdapter.sService.updateDiscoveryTechnology(
+                        token, pollTech, listenTech, mAdapter.getContext().getPackageName()));
     }
 
 }
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index b36b705..f478793 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -721,7 +721,7 @@
      *
      * @return List<String> containing secure elements on the device which supports
      *                      off host card emulation. eSE for Embedded secure element,
-     *                      SIM for UICC and so on.
+     *                      SIM for UICC, eSIM for EUICC and so on.
      * @hide
      */
     public @NonNull List<String> getSupportedOffHostSecureElements() {
@@ -741,6 +741,11 @@
         if (pm.hasSystemFeature(PackageManager.FEATURE_NFC_OFF_HOST_CARD_EMULATION_ESE)) {
             offHostSE.add("eSE");
         }
+        if (Flags.enableCardEmulationEuicc()
+                && callServiceReturn(
+                        () -> sCardEmulationService.isEuiccSupported(), false)) {
+            offHostSE.add("eSIM");
+        }
         return offHostSE;
     }
 
@@ -1731,7 +1736,8 @@
         }
         Binder token = new Binder();
         int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
-        callService(() -> sService.setReaderMode(token, null, flags, null));
+        callService(() -> sService.setReaderMode(
+                token, null, flags, null, mContext.getPackageName()));
     }
 
     /**
@@ -1804,7 +1810,8 @@
                 || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {
             Binder token = new Binder();
             callService( () ->
-                sService.updateDiscoveryTechnology(token, pollTechnology, listenTechnology));
+                    sService.updateDiscoveryTechnology(
+                            token, pollTechnology, listenTechnology, mContext.getPackageName()));
         } else {
             mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
         }
@@ -2011,7 +2018,8 @@
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
-        return callServiceReturn(() ->  sService.enableReaderOption(enable), false);
+        return callServiceReturn(() ->
+                sService.enableReaderOption(enable, mContext.getPackageName()), false);
 
     }
 
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 2ec819c..d51b704 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -18,15 +18,34 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.util.Log;
 
+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.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
 
 /**
  * Used for OEM extension APIs.
@@ -42,10 +61,59 @@
     private static final int OEM_EXTENSION_RESPONSE_THRESHOLD_MS = 2000;
     private final NfcAdapter mAdapter;
     private final NfcOemExtensionCallback mOemNfcExtensionCallback;
+    private boolean mIsRegistered = false;
+    private final Map<Callback, Executor> mCallbackMap = new HashMap<>();
     private final Context mContext;
-    private Executor mExecutor = null;
-    private Callback mCallback = null;
     private final Object mLock = new Object();
+    private boolean mCardEmulationActivated = false;
+    private boolean mRfFieldActivated = false;
+    private boolean mRfDiscoveryStarted = false;
+
+    /**
+     * Event that Host Card Emulation is activated.
+     */
+    public static final int HCE_ACTIVATE = 1;
+    /**
+     * Event that some data is transferred in Host Card Emulation.
+     */
+    public static final int HCE_DATA_TRANSFERRED = 2;
+    /**
+     * Event that Host Card Emulation is deactivated.
+     */
+    public static final int HCE_DEACTIVATE = 3;
+    /**
+     * Possible events from {@link Callback#onHceEventReceived}.
+     *
+     * @hide
+     */
+    @IntDef(value = {
+            HCE_ACTIVATE,
+            HCE_DATA_TRANSFERRED,
+            HCE_DEACTIVATE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HostCardEmulationAction {}
+
+    /**
+     * Status OK
+     */
+    public static final int STATUS_OK = 0;
+    /**
+     * Status unknown error
+     */
+    public static final int STATUS_UNKNOWN_ERROR = 1;
+
+    /**
+     * Status codes passed to OEM extension callbacks.
+     *
+     * @hide
+     */
+    @IntDef(value = {
+            STATUS_OK,
+            STATUS_UNKNOWN_ERROR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StatusCode {}
 
     /**
      * Interface for Oem extensions for NFC.
@@ -59,22 +127,153 @@
          * @param tag Tag details
          */
         void onTagConnected(boolean connected, @NonNull Tag tag);
+
+        /**
+         * Update the Nfc Adapter State
+         * @param state new state that need to be updated
+         */
+        void onStateUpdated(@NfcAdapter.AdapterState int state);
+        /**
+         * Check if NfcService apply routing method need to be skipped for
+         * some feature.
+         * @param isSkipped The {@link Consumer} to be completed. If apply routing can be skipped,
+         *                  the {@link Consumer#accept(Object)} should be called with
+         *                  {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
+         */
+        void onApplyRouting(@NonNull Consumer<Boolean> isSkipped);
+        /**
+         * Check if NfcService ndefRead method need to be skipped To skip
+         * and start checking for presence of tag
+         * @param isSkipped The {@link Consumer} to be completed. If Ndef read can be skipped,
+         *                  the {@link Consumer#accept(Object)} should be called with
+         *                  {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
+         */
+        void onNdefRead(@NonNull Consumer<Boolean> isSkipped);
+        /**
+         * Method to check if Nfc is allowed to be enabled by OEMs.
+         * @param isAllowed The {@link Consumer} to be completed. If enabling NFC is allowed,
+         *                  the {@link Consumer#accept(Object)} should be called with
+         *                  {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
+         * false if NFC cannot be enabled at this time.
+         */
+        @SuppressLint("MethodNameTense")
+        void onEnable(@NonNull Consumer<Boolean> isAllowed);
+        /**
+         * Method to check if Nfc is allowed to be disabled by OEMs.
+         * @param isAllowed The {@link Consumer} to be completed. If disabling NFC is allowed,
+         *                  the {@link Consumer#accept(Object)} should be called with
+         *                  {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
+         * false if NFC cannot be disabled at this time.
+         */
+        void onDisable(@NonNull Consumer<Boolean> isAllowed);
+
+        /**
+         * Callback to indicate that Nfc starts to boot.
+         */
+        void onBootStarted();
+
+        /**
+         * Callback to indicate that Nfc starts to enable.
+         */
+        void onEnableStarted();
+
+        /**
+         * Callback to indicate that Nfc starts to enable.
+         */
+        void onDisableStarted();
+
+        /**
+         * Callback to indicate if NFC boots successfully or not.
+         * @param status the status code indicating if boot finished successfully
+         */
+        void onBootFinished(@StatusCode int status);
+
+        /**
+         * Callback to indicate if NFC is successfully enabled.
+         * @param status the status code indicating if enable finished successfully
+         */
+        void onEnableFinished(@StatusCode int status);
+
+        /**
+         * Callback to indicate if NFC is successfully disabled.
+         * @param status the status code indicating if disable finished successfully
+         */
+        void onDisableFinished(@StatusCode int status);
+
+        /**
+         * Check if NfcService tag dispatch need to be skipped.
+         * @param isSkipped The {@link Consumer} to be completed. If tag dispatch can be skipped,
+         *                  the {@link Consumer#accept(Object)} should be called with
+         *                  {@link Boolean#TRUE}, otherwise call with {@link Boolean#FALSE}.
+         */
+        void onTagDispatch(@NonNull Consumer<Boolean> isSkipped);
+
+        /**
+         * Notifies routing configuration is changed.
+         */
+        void onRoutingChanged();
+
+        /**
+         * API to activate start stop cpu boost on hce event.
+         *
+         * <p>When HCE is activated, transferring data, and deactivated,
+         * must call this method to activate, start and stop cpu boost respectively.
+         * @param action Flag indicating actions to activate, start and stop cpu boost.
+         */
+        void onHceEventReceived(@HostCardEmulationAction int action);
+
+        /**
+         * API to notify when reader option has been changed using
+         * {@link NfcAdapter#enableReaderOption(boolean)} by some app.
+         * @param enabled Flag indicating ReaderMode enabled/disabled
+         */
+        void onReaderOptionChanged(boolean enabled);
+
+        /**
+        * Notifies NFC is activated in listen mode.
+        * NFC Forum NCI-2.3 ch.5.2.6 specification
+        *
+        * <p>NFCC is ready to communicate with a Card reader
+        *
+        * @param isActivated true, if card emulation activated, else de-activated.
+        */
+        void onCardEmulationActivated(boolean isActivated);
+
+        /**
+        * Notifies the Remote NFC Endpoint RF Field is activated.
+        * NFC Forum NCI-2.3 ch.5.3 specification
+        *
+        * @param isActivated true, if RF Field is ON, else RF Field is OFF.
+        */
+        void onRfFieldActivated(boolean isActivated);
+
+        /**
+        * Notifies the NFC RF discovery is started or in the IDLE state.
+        * NFC Forum NCI-2.3 ch.5.2 specification
+        *
+        * @param isDiscoveryStarted true, if RF discovery started, else RF state is Idle.
+        */
+        void onRfDiscoveryStarted(boolean isDiscoveryStarted);
     }
 
 
     /**
      * Constructor to be used only by {@link NfcAdapter}.
-     * @hide
      */
-    public NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) {
+    NfcOemExtension(@NonNull Context context, @NonNull NfcAdapter adapter) {
         mContext = context;
         mAdapter = adapter;
         mOemNfcExtensionCallback = new NfcOemExtensionCallback();
     }
 
     /**
-     * Register an {@link Callback} to listen for UWB oem extension callbacks
+     * Register an {@link Callback} to listen for NFC oem extension callbacks
+     * Multiple clients can register and callbacks will be invoked asynchronously.
+     *
      * <p>The provided callback will be invoked by the given {@link Executor}.
+     * As part of {@link #registerCallback(Executor, Callback)} the
+     * {@link Callback} will be invoked with current NFC state
+     * before the {@link #registerCallback(Executor, Callback)} function completes.
      *
      * @param executor an {@link Executor} to execute given callback
      * @param callback oem implementation of {@link Callback}
@@ -84,15 +283,35 @@
     public void registerCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull Callback callback) {
         synchronized (mLock) {
-            if (mCallback != null) {
+            if (executor == null || callback == null) {
+                Log.e(TAG, "Executor and Callback must not be null!");
+                throw new IllegalArgumentException();
+            }
+
+            if (mCallbackMap.containsKey(callback)) {
                 Log.e(TAG, "Callback already registered. Unregister existing callback before"
                         + "registering");
                 throw new IllegalArgumentException();
             }
-            NfcAdapter.callService(() -> {
-                NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
-                mCallback = callback;
-                mExecutor = executor;
+            mCallbackMap.put(callback, executor);
+            if (!mIsRegistered) {
+                NfcAdapter.callService(() -> {
+                    NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
+                    mIsRegistered = true;
+                });
+            } else {
+                updateNfCState(callback, executor);
+            }
+        }
+    }
+
+    private void updateNfCState(Callback callback, Executor executor) {
+        if (callback != null) {
+            Log.i(TAG, "updateNfCState");
+            executor.execute(() -> {
+                callback.onCardEmulationActivated(mCardEmulationActivated);
+                callback.onRfFieldActivated(mRfFieldActivated);
+                callback.onRfDiscoveryStarted(mRfDiscoveryStarted);
             });
         }
     }
@@ -111,15 +330,19 @@
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public void unregisterCallback(@NonNull Callback callback) {
         synchronized (mLock) {
-            if (mCallback == null || mCallback != callback) {
+            if (!mCallbackMap.containsKey(callback) || !mIsRegistered) {
                 Log.e(TAG, "Callback not registered");
                 throw new IllegalArgumentException();
             }
-            NfcAdapter.callService(() -> {
-                NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
-                mCallback = null;
-                mExecutor = null;
-            });
+            if (mCallbackMap.size() == 1) {
+                NfcAdapter.callService(() -> {
+                    NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
+                    mIsRegistered = false;
+                    mCallbackMap.remove(callback);
+                });
+            } else {
+                mCallbackMap.remove(callback);
+            }
         }
     }
 
@@ -153,20 +376,229 @@
         NfcAdapter.callService(() -> NfcAdapter.sService.checkFirmware());
     }
 
+    /**
+     * Get the Active NFCEE (NFC Execution Environment) List
+     *
+     * @return List of activated secure elements on success
+     *         which can contain "eSE" and "UICC", otherwise empty list.
+     */
+    @NonNull
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    public List<String> getActiveNfceeList() {
+        return NfcAdapter.callServiceReturn(() ->
+            NfcAdapter.sService.fetchActiveNfceeList(), new ArrayList<String>());
+    }
+
     private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
+
         @Override
         public void onTagConnected(boolean connected, Tag tag) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoid2ArgCallback(connected, tag, cb::onTagConnected, ex));
+        }
+
+        @Override
+        public void onCardEmulationActivated(boolean isActivated) throws RemoteException {
+            mCardEmulationActivated = isActivated;
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(isActivated, cb::onCardEmulationActivated, ex));
+        }
+
+        @Override
+        public void onRfFieldActivated(boolean isActivated) throws RemoteException {
+            mRfFieldActivated = isActivated;
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(isActivated, cb::onRfFieldActivated, ex));
+        }
+
+        @Override
+        public void onRfDiscoveryStarted(boolean isDiscoveryStarted) throws RemoteException {
+            mRfDiscoveryStarted = isDiscoveryStarted;
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(isDiscoveryStarted, cb::onRfDiscoveryStarted, ex));
+        }
+
+        @Override
+        public void onStateUpdated(int state) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(state, cb::onStateUpdated, ex));
+        }
+
+        @Override
+        public void onApplyRouting(ResultReceiver isSkipped) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(
+                        new ReceiverWrapper(isSkipped), cb::onApplyRouting, ex));
+        }
+        @Override
+        public void onNdefRead(ResultReceiver isSkipped) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(
+                        new ReceiverWrapper(isSkipped), cb::onNdefRead, ex));
+        }
+        @Override
+        public void onEnable(ResultReceiver isAllowed) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(
+                        new ReceiverWrapper(isAllowed), cb::onEnable, ex));
+        }
+        @Override
+        public void onDisable(ResultReceiver isAllowed) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(
+                        new ReceiverWrapper(isAllowed), cb::onDisable, ex));
+        }
+        @Override
+        public void onBootStarted() throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(null, (Object input) -> cb.onBootStarted(), ex));
+        }
+        @Override
+        public void onEnableStarted() throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(null, (Object input) -> cb.onEnableStarted(), ex));
+        }
+        @Override
+        public void onDisableStarted() throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(null, (Object input) -> cb.onDisableStarted(), ex));
+        }
+        @Override
+        public void onBootFinished(int status) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(status, cb::onBootFinished, ex));
+        }
+        @Override
+        public void onEnableFinished(int status) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(status, cb::onEnableFinished, ex));
+        }
+        @Override
+        public void onDisableFinished(int status) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(status, cb::onDisableFinished, ex));
+        }
+        @Override
+        public void onTagDispatch(ResultReceiver isSkipped) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(
+                        new ReceiverWrapper(isSkipped), cb::onTagDispatch, ex));
+        }
+        @Override
+        public void onRoutingChanged() throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(null, (Object input) -> cb.onRoutingChanged(), ex));
+        }
+        @Override
+        public void onHceEventReceived(int action) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(action, cb::onHceEventReceived, ex));
+        }
+
+        @Override
+        public void onReaderOptionChanged(boolean enabled) throws RemoteException {
+            mCallbackMap.forEach((cb, ex) ->
+                    handleVoidCallback(enabled, cb::onReaderOptionChanged, ex));
+        }
+
+        private <T> void handleVoidCallback(
+                T input, Consumer<T> callbackMethod, Executor executor) {
             synchronized (mLock) {
-                if (mCallback == null || mExecutor == null) {
-                    return;
-                }
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    mExecutor.execute(() -> mCallback.onTagConnected(connected, tag));
+                    executor.execute(() -> callbackMethod.accept(input));
+                } catch (RuntimeException ex) {
+                    throw ex;
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
         }
+
+        private <T1, T2> void handleVoid2ArgCallback(
+                T1 input1, T2 input2, BiConsumer<T1, T2> callbackMethod, Executor executor) {
+            synchronized (mLock) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callbackMethod.accept(input1, input2));
+                } catch (RuntimeException ex) {
+                    throw ex;
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        private <S, T> S handleNonVoidCallbackWithInput(
+                S defaultValue, T input, Function<T, S> callbackMethod) throws RemoteException {
+            synchronized (mLock) {
+                final long identity = Binder.clearCallingIdentity();
+                S result = defaultValue;
+                try {
+                    ExecutorService executor = Executors.newSingleThreadExecutor();
+                    FutureTask<S> futureTask = new FutureTask<>(() -> callbackMethod.apply(input));
+                    var unused = executor.submit(futureTask);
+                    try {
+                        result = futureTask.get(
+                                OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
+                    } catch (ExecutionException | InterruptedException e) {
+                        e.printStackTrace();
+                    } catch (TimeoutException e) {
+                        Log.w(TAG, "Callback timed out: " + callbackMethod);
+                        e.printStackTrace();
+                    } finally {
+                        executor.shutdown();
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+                return result;
+            }
+        }
+
+        private <T> T handleNonVoidCallbackWithoutInput(T defaultValue, Supplier<T> callbackMethod)
+                throws RemoteException {
+            synchronized (mLock) {
+                final long identity = Binder.clearCallingIdentity();
+                T result = defaultValue;
+                try {
+                    ExecutorService executor = Executors.newSingleThreadExecutor();
+                    FutureTask<T> futureTask = new FutureTask<>(callbackMethod::get);
+                    var unused = executor.submit(futureTask);
+                    try {
+                        result = futureTask.get(
+                                OEM_EXTENSION_RESPONSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
+                    } catch (ExecutionException | InterruptedException e) {
+                        e.printStackTrace();
+                    } catch (TimeoutException e) {
+                        Log.w(TAG, "Callback timed out: " + callbackMethod);
+                        e.printStackTrace();
+                    } finally {
+                        executor.shutdown();
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+                return result;
+            }
+        }
+    }
+
+    private class ReceiverWrapper implements Consumer<Boolean> {
+        private final ResultReceiver mResultReceiver;
+
+        ReceiverWrapper(ResultReceiver resultReceiver) {
+            mResultReceiver = resultReceiver;
+        }
+
+        @Override
+        public void accept(Boolean result) {
+            mResultReceiver.send(result ? 1 : 0, null);
+        }
+
+        @Override
+        public Consumer<Boolean> andThen(Consumer<? super Boolean> after) {
+            return Consumer.super.andThen(after);
+        }
     }
 }
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 3cf0a4d..ea5a036 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -308,6 +308,8 @@
                         mOffHostName = "eSE1";
                     } else if (mOffHostName.equals("SIM")) {
                         mOffHostName = "SIM1";
+                    } else if (Flags.enableCardEmulationEuicc() && mOffHostName.equals("eSIM")) {
+                        mOffHostName = "eSIM1";
                     }
                 }
                 mStaticOffHostName = mOffHostName;
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index e0438ce..a72a896 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.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 android.provider.Settings.SettingNotFoundException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashMap;
 import java.util.HexFormat;
 import java.util.List;
@@ -148,6 +151,27 @@
      *    that service will be invoked directly.
      */
     public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2;
+    /**
+     * Route to Device Host (DH).
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0;
+    /**
+     * Route to eSE.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1;
+    /**
+     * Route to UICC.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC = 2;
+
+    /**
+     * Route unset.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET = -1;
 
     static boolean sIsInitialized = false;
     static HashMap<Context, CardEmulation> sCardEmus = new HashMap<Context, CardEmulation>();
@@ -524,11 +548,13 @@
 
         List<String> validSE = adapter.getSupportedOffHostSecureElements();
         if ((offHostSecureElement.startsWith("eSE") && !validSE.contains("eSE"))
-                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))) {
+                || (offHostSecureElement.startsWith("SIM") && !validSE.contains("SIM"))
+                || (offHostSecureElement.startsWith("eSIM") && !validSE.contains("eSIM"))) {
             return false;
         }
 
-        if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")) {
+        if (!offHostSecureElement.startsWith("eSE") && !offHostSecureElement.startsWith("SIM")
+                && !(Flags.enableCardEmulationEuicc() && offHostSecureElement.startsWith("eSIM"))) {
             return false;
         }
 
@@ -536,6 +562,8 @@
             offHostSecureElement = "eSE1";
         } else if (offHostSecureElement.equals("SIM")) {
             offHostSecureElement = "SIM1";
+        } else if (Flags.enableCardEmulationEuicc() && offHostSecureElement.equals("eSIM")) {
+            offHostSecureElement = "eSIM1";
         }
         final String offHostSecureElementV = new String(offHostSecureElement);
         return callServiceReturn(() ->
@@ -716,7 +744,7 @@
      *
      * @return the preferred payment service description
      */
-    @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
+    @RequiresPermission(Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
     public CharSequence getDescriptionForPreferredPaymentService() {
         ApduServiceInfo serviceInfo = callServiceReturn(() ->
@@ -865,59 +893,111 @@
                 sService.setServiceEnabledForCategoryOther(userId, service, status), false);
     }
 
+    /** @hide */
+    @IntDef(prefix = "PROTOCOL_AND_TECHNOLOGY_ROUTE_",
+            value = {
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_DH,
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE,
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC,
+                    PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolAndTechnologyRoute {}
+
      /**
       * Setting NFC controller routing table, which includes Protocol Route and Technology Route,
       * while this Activity is in the foreground.
       *
-      * The parameter set to null can be used to keep current values for that entry.
+      * The parameter set to {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET}
+      * can be used to keep current values for that entry. Either
+      * Protocol Route or Technology Route should be override when calling this API, otherwise
+      * throw {@link IllegalArgumentException}.
       * <p>
       * Example usage in an Activity that requires to set proto route to "ESE" and keep tech route:
       * <pre>
       * protected void onResume() {
-      *     mNfcAdapter.overrideRoutingTable(this , "ESE" , null);
+      *     mNfcAdapter.overrideRoutingTable(
+      *         this, {@link #PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE}, null);
       * }</pre>
       * </p>
-      * Also activities must call this method when it goes to the background,
-      * with all parameters set to null.
+      * Also activities must call {@link #recoverRoutingTable(Activity)}
+      * when it goes to the background. Only the package of the
+      * currently preferred service (the service set as preferred by the current foreground
+      * application via {@link CardEmulation#setPreferredService(Activity, ComponentName)} or the
+      * current Default Wallet Role Holder {@link RoleManager#ROLE_WALLET}),
+      * otherwise a call to this method will fail and throw {@link SecurityException}.
       * @param activity The Activity that requests NFC controller routing table to be changed.
-      * @param protocol ISO-DEP route destination, which can be "DH" or "UICC" or "ESE".
-      * @param technology Tech-A, Tech-B route destination, which can be "DH" or "UICC" or "ESE".
-      * @return true if operation is successful and false otherwise
-      *
+      * @param protocol ISO-DEP route destination, where the possible inputs are defined
+      *                 in {@link ProtocolAndTechnologyRoute}.
+      * @param technology Tech-A, Tech-B and Tech-F route destination, where the possible inputs
+      *                   are defined in {@link ProtocolAndTechnologyRoute}
+      * @throws SecurityException if the caller is not the preferred NFC service
+      * @throws IllegalArgumentException if the activity is not resumed or the caller is not in the
+      * foreground.
+      * <p>
       * This is a high risk API and only included to support mainline effort
       * @hide
       */
-    public boolean overrideRoutingTable(Activity activity, String protocol, String technology) {
-        if (activity == null) {
-            throw new NullPointerException("activity or service or category is null");
-        }
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public void overrideRoutingTable(
+            @NonNull Activity activity, @ProtocolAndTechnologyRoute int protocol,
+            @ProtocolAndTechnologyRoute int technology) {
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        return callServiceReturn(() ->
+        String protocolRoute = switch (protocol) {
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null;
+            default -> throw new IllegalStateException("Unexpected value: " + protocol);
+        };
+        String technologyRoute = switch (technology) {
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_DH -> "DH";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE -> "ESE";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UICC -> "UICC";
+            case PROTOCOL_AND_TECHNOLOGY_ROUTE_UNSET -> null;
+            default -> throw new IllegalStateException("Unexpected value: " + protocol);
+        };
+        callService(() ->
                 sService.overrideRoutingTable(
-                    mContext.getUser().getIdentifier(), protocol, technology), false);
+                    mContext.getUser().getIdentifier(),
+                    protocolRoute,
+                    technologyRoute,
+                    mContext.getPackageName()));
     }
 
     /**
      * Restore the NFC controller routing table,
-     * which was changed by {@link #overrideRoutingTable(Activity, String, String)}
+     * which was changed by {@link #overrideRoutingTable(Activity, int, int)}
      *
      * @param activity The Activity that requested NFC controller routing table to be changed.
-     * @return true if operation is successful and false otherwise
+     * @throws IllegalArgumentException if the caller is not in the foreground.
      *
      * @hide
      */
-    public boolean recoverRoutingTable(Activity activity) {
-        if (activity == null) {
-            throw new NullPointerException("activity is null");
-        }
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE)
+    public void recoverRoutingTable(@NonNull Activity activity) {
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        return callServiceReturn(() ->
+        callService(() ->
                 sService.recoverRoutingTable(
-                    mContext.getUser().getIdentifier()), false);
+                    mContext.getUser().getIdentifier()));
+    }
+
+    /**
+     * Is EUICC supported as a Secure Element EE which supports off host card emulation.
+     *
+     * @return true if the device supports EUICC for off host card emulation, false otherwise.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC)
+    public boolean isEuiccSupported() {
+        return callServiceReturn(() -> sService.isEuiccSupported(), false);
     }
 
     /**
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index c3c74a6..cd8e19c 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -242,6 +242,16 @@
     /**
      * @hide
      */
+    public static final int MSG_OBSERVE_MODE_CHANGE = 5;
+
+    /**
+     * @hide
+     */
+    public static final int MSG_PREFERRED_SERVICE_CHANGED = 6;
+
+    /**
+     * @hide
+     */
     public static final String KEY_DATA = "data";
 
     /**
@@ -333,7 +343,17 @@
                         processPollingFrames(pollingFrames);
                     }
                     break;
-            default:
+                case MSG_OBSERVE_MODE_CHANGE:
+                    if (android.nfc.Flags.nfcEventListener()) {
+                        onObserveModeStateChanged(msg.arg1 == 1);
+                    }
+                    break;
+                case MSG_PREFERRED_SERVICE_CHANGED:
+                    if (android.nfc.Flags.nfcEventListener()) {
+                        onPreferredServiceChanged(msg.arg1 == 1);
+                    }
+                    break;
+                default:
                 super.handleMessage(msg);
             }
         }
@@ -441,4 +461,26 @@
      * @param reason Either {@link #DEACTIVATION_LINK_LOSS} or {@link #DEACTIVATION_DESELECTED}
      */
     public abstract void onDeactivated(int reason);
+
+
+    /**
+     * This method is called when this service is the preferred Nfc service and
+     * Observe mode has been enabled or disabled.
+     *
+     * @param isEnabled true if observe mode has been enabled, false if it has been disabled
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+    public void onObserveModeStateChanged(boolean isEnabled) {
+
+    }
+
+    /**
+     * This method is called when this service gains or loses preferred Nfc service status.
+     *
+     * @param isPreferred true is this service has become the preferred Nfc service,
+     * false if it is no longer the preferred service
+     */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER)
+    public void onPreferredServiceChanged(boolean isPreferred) {
+    }
 }
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index f16fa80..cc9a97c 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -2,6 +2,14 @@
 container: "system"
 
 flag {
+    name: "nfc_event_listener"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable NFC Event listener APIs"
+    bug: "356447790"
+}
+
+flag {
     name: "enable_nfc_mainline"
     is_exported: true
     namespace: "nfc"
@@ -125,3 +133,27 @@
     description: "Add Settings.ACTION_MANAGE_OTHER_NFC_SERVICES_SETTINGS"
     bug: "358129872"
 }
+
+flag {
+    name: "nfc_override_recover_routing_table"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable override and recover routing table"
+    bug: "329043523"
+}
+
+flag {
+    name: "nfc_watchdog"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable watchdog for the NFC system process"
+    bug: "362937338"
+}
+
+flag {
+    name: "enable_card_emulation_euicc"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable EUICC card emulation"
+    bug: "321314635"
+}
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index a0b3469..08155dd 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -133,7 +133,8 @@
                 <LinearLayout
                     android:id="@+id/negative_multiple_devices_layout"
                     android:layout_width="wrap_content"
-                    android:layout_height="48dp"
+                    android:layout_height="match_parent"
+                    android:padding="6dp"
                     android:gravity="center"
                     android:visibility="gone">
 
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index abfccb0..798e7d5 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Kies ’n toestel wat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; moet bestuur"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Kies ’n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om op te stel"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Hierdie app sal toegelaat word om inligting te sinkroniseer, soos die naam van iemand wat bel, en sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hê"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Hierdie app sal toegelaat word om inligting te sinkroniseer, soos die naam van iemand wat bel, en sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> hê"</string>
     <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="2872254734959842579">"Hierdie app sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hê"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang tot hierdie inligting op jou foon"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Laat &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou foon se apps te stroom?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s sal toegang hê tot enigiets wat sigbaar is of gespeel word op die foon, insluitend oudio, foto’s, wagwoorde en boodskappe.&lt;br/&gt;&lt;br/&gt;%1$s sal apps kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Oorkruistoestel-dienste"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toestemming om apps tussen jou toestelle te stroom"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek toestemming namens jou <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> om apps tussen jou toestelle te vertoon en te stroom"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sal toegang hê tot enigiets wat sigbaar is of gespeel word op die <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, insluitend oudio, foto’s, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek toestemming namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> om apps tussen jou toestelle te vertoon en te stroom"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Gee &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang tot hierdie inligting op jou foon"</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_title_computer" msgid="4671071173916176037">"Google Play Dienste"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toegang tot jou foon se foto’s, media en kennisgewings"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Laat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om hierdie handeling uit te voer?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Laat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; toe om jou foon se apps en stelselkenmerke te stroom?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s sal toegang hê tot enigiets wat sigbaar is of gespeel word op jou foon, insluitend oudio, foto’s, betaalinligting, wagwoorde en boodskappe.&lt;br/&gt;&lt;br/&gt;%1$s sal apps en stelselkenmerke kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en ander stelselkenmerke na toestelle in die omtrek te stroom"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Gee &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; te stroom?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 en stelselkenmerke na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en ander stelselkenmerke tussen jou toestelle 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Verander media-uitset"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Kennisgewings"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Stroming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Maak en bestuur foonoproepe"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lees en skryf foonoproeprekord neer"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Stuur en bekyk SMS-boodskappe"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vind, koppel aan en bepaal die relatiewe posisie van toestelle in die omtrek"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lees alle kennisgewings, insluitend inligting soos kontakte, boodskappe en foto’s&lt;br/&gt;• Stuur kennisgewings&lt;br/&gt;&lt;br/&gt;Jy kan hierdie app se vermoë om kennisgewings te lees en te stuur, enige tyd in Instellings bestuur &gt; Kennisgewings."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stroom jou foon se apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stroom apps en ander stelselkenmerke van jou foon af"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Kry toegang tot ’n lys beskikbare toestelle en beheer watter een oudio of video van ander apps af stroom of uitsaai"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"foon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"toestel"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index 0d69359..59e1f03 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የሚያስተዳድረው መሣሪያ ይምረጡ"</string>
     <string name="chooser_title" msgid="2235819929238267637">"የሚያዋቅሩት <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን እንዲያሰምር እና እነዚህን ፈቃዶች በእርስዎ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ እንዲደርስ ይፈቀድለታል"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን እንዲያሰምር እና እነዚህን ፈቃዶች በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እንዲደርስ ይፈቀድለታል"</string>
     <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="2872254734959842579">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን መረጃ ከስልክዎ እንዲደርስበት ይፍቀዱለት"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የስልክዎን መተግበሪያዎች በዥረት እንዲያስተላልፍ ይፈቀድለት?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ኦዲዮ፣ ፎቶዎች፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ በስልክ ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል።&lt;br/&gt;&lt;br/&gt;%1$s ይህን ፈቃድ እስኪያስወግዱ ድረስ መተግበሪያዎችን በዥረት ማስተላለፍ ይችላል።"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"መሣሪያ ተሻጋሪ አገልግሎቶች"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> በእርስዎ መሣሪያዎች መካከል መተግበሪያዎችን በዥረት ለመልቀቅ የእርስዎን <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> በመወከል ለማሳየት እና መተግበሪያዎችን በዥረት ለማስተላለፍ ፈቃድ እየጠየቀ ነው"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን መረጃ ከስልክዎ ላይ እንዲደርስ ይፍቀዱለት"</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_title_computer" msgid="4671071173916176037">"የGoogle Play አገልግሎቶች"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> የስልክዎን ፎቶዎች፣ ሚዲያ እና ማሳወቂያዎች ለመድረስ የእርስዎን <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ይህን እርምጃ እንዲወስድ ፈቃድ ይሰጠው?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; የስልክዎን መተግበሪያዎች እና የሥርዓት ባህሪያት በዥረት እንዲያስተላልፍ ይፈቀድለት?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ኦዲዮ፣ ፎቶዎች፣ የክፍያ መረጃ፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ በስልክዎ ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል። የዚህን ፈቃድ መዳረሻ እስከሚያስወግዱ ድረስ &lt;br/&gt;&lt;br/&gt;%1$s መተግበሪያዎችን እና የሥርዓት ባህሪያትን በዥረት ለማስተላለፍ ይችላል።"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> በመወከል በአቅራቢያ ላሉ መሣሪያዎች መተግበሪያዎች እና ሌሎች የስርዓት ባህሪያትን በዥረት ለመልቀቅ ፈቃድ እየጠየቀ ነው"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> መተግበሪያዎች እና የሥርዓት ባህሪያት ወደ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;? በዥረት እንዲለቅ ይፍቀዱ"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"የሚዲያ ውጤትን ይቀይሩ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ፎቶዎች እና ሚዲያ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ማሳወቂያዎች"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"መተግበሪያዎች"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"በዥረት መልቀቅ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"የስልክ ጥሪዎች ያድርጉ እና ያስተዳድሩ"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"የስልክ ጥሪ ምዝግብ ማስታወሻን ያንብቡ እና ይጻፉ"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"የኤስኤምኤስ መልዕክቶችን ይላኩና ይመልከቱ"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"በአቅራቢያ ያሉ መሣሪያዎችን ማግኘት፣ ከእነሱ ጋር መገናኘት እና አንጻራዊ ቦታቸውን መወሰን"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"እንደ እውቂያዎች፣ መልዕክቶች እና ፎቶዎች ያሉ መረጃዎችን ጨምሮ ሁሉንም ማሳወቂያዎች ማንበብ"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• እንደ እውቂያዎች፣ መልዕክቶች እና ፎቶዎች ያሉ መረጃዎችን ጨምሮ ሁሉንም ማሳወቂያዎች ያንብቡ&lt;br/&gt;• ማሳወቂያዎችን መላክ&lt;br/&gt;&lt;br/&gt;የዚህን መተግበሪያ ማሳወቂያዎችን የማንበብ እና የመላክ ችሎታን በቅንብሮች &gt; ማሳወቂያዎች ውስጥ ማስተዳደር ይችላሉ።"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"የስልክዎን መተግበሪያዎች በዥረት ይልቀቁ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ከስልክዎ ሆነው መተግበሪያዎች እና ሌሎች የስርዓት ባህሪያትን በዥረት ይልቀቁ"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"የሚገኙትን መሣሪያዎች ዝርዝር ይድረሱ እና የትኛዎቹ ኦዲዮን ወይም ቪዲዮን ከሌሎች መተግበሪያዎች በዥረት እንደሚለቅቁ ይቆጣጠሩ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ስልክ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ጡባዊ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ቲቪ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"መሣሪያ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index 9106721..89c7efb 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"الساعة"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"‏اختيار جهاز ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"اختيار \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\" للإعداد"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"سيتم السماح لهذا التطبيق بمزامنة المعلومات، مثلاً اسم المتصل، والوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"سيتم السماح لهذا التطبيق بمزامنة المعلومات، مثلاً اسم المتصل، واستخدام هذه الأذونات على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"سيتم السماح لهذا التطبيق بالوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"‏السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بالوصول إلى هذه المعلومات من هاتفك"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"‏هل تريد منح &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; إذنًا لبث التطبيقات المُثبَّتة على هاتفك؟"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"‏سيتمكّن %1$s من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على الهاتف، بما في ذلك الملفات الصوتية والصور وكلمات المرور والرسائل.&lt;br/&gt;&lt;br/&gt;سيتمكّن %1$s من بث التطبيقات إلى أن توقف إمكانية استخدام هذا الإذن."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"الخدمات التي تعمل بين الأجهزة"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"يطلب تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" لبثّ محتوى التطبيقات بين أجهزتك."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"تطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> لعرض التطبيقات وبثها بين أجهزتك"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"سيتم السماح لهذا التطبيق باستخدام هذه الأذونات على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"‏هل تريد السماح لتطبيق &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_app_streaming" msgid="1274464413649731829">"‏سيتمكّن \"<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="1944832605534698562">"يطلب \"<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="4693714143506569253">"‏السماح لتطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; بالوصول إلى هذه المعلومات من هاتفك"</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_title_computer" msgid="4671071173916176037">"‏خدمات Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"يطلب تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" للوصول إلى الصور والوسائط والإشعارات في هاتفك."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"‏هل تريد السماح للتطبيق &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; باتّخاذ هذا الإجراء؟"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"‏هل تريد منح &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; إذنًا لبث التطبيقات والوصول إلى ميزات النظام على هاتفك؟"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"‏سيتمكّن %1$s من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على هاتفك، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.&lt;br/&gt;&lt;br/&gt;سيتمكّن %1$s من بث التطبيقات والوصول إلى ميزات النظام إلى أن توقف إمكانية استخدام هذا الإذن."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" لبثّ التطبيقات وميزات النظام الأخرى إلى أجهزتك المجاورة."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"هل تريد السماح لجهاز <xliff:g id="DEVICE_NAME_0">%1$s</xliff:g> ببث التطبيقات وميزات النظام من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> إلى <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"‏سيتمكّن \"<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_nearby_device_streaming" msgid="8509848562931818793">"يطلب \"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"تغيير جهاز إخراج الوسائط"</string>
     <string name="permission_storage" msgid="6831099350839392343">"الصور والوسائط"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"الإشعارات"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"التطبيقات"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"البثّ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"إجراء المكالمات الهاتفية وإدارتها"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"قراءة سجلّ المكالمات الهاتفية والكتابة إليه"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"إرسال الرسائل القصيرة وعرضها"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"يمكن العثور على الأجهزة المجاورة والربط بها وتحديد موقعها النسبي."</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"يمكن لهذا الملف الشخصي قراءة جميع الإشعارات، بما في ذلك المعلومات، مثل جهات الاتصال والرسائل والصور."</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• قراءة كل الإشعارات بما فيها المعلومات، مثل جهات الاتصال والرسائل والصور&lt;br/&gt;• إرسال الإشعارات&lt;br/&gt;&lt;br/&gt;يمكنك إدارة الإذن الممنوح لهذا التطبيق بقراءة الإشعارات وإرسالها في أي وقت من خلال الإعدادات &gt; الإشعارات."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"بث تطبيقات هاتفك"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"بثّ التطبيقات وميزات النظام الأخرى من هاتفك"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"يتيح لك هذا الإذن الاطّلاع على مجموعة من الأجهزة المتاحة وتحديد الجهاز المسموح له بتشغيل الصوت أو الفيديو أو بثّهما من التطبيقات الأخرى."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"هاتف"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"جهاز لوحي"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"تلفزيون"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"جهاز"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 3d1554b..3d252ca 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিবলগীয়া এটা ডিভাইচ বাছনি কৰক"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ছেট আপ কৰিবলৈ এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"এই এপ্‌টোক ফ’ন কৰা লোকৰ নামৰ দৰে তথ্য ছিংক কৰিবলৈ আৰু আপোনাৰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"এই এপ্‌টোক ফ’ন কৰা লোকৰ নামৰ দৰে তথ্য ছিংক কৰিবলৈ আৰু আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string>
     <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="2872254734959842579">"এই এপ্‌টোক আপোনাৰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ এপ্‌ ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$sএ আপোনাৰ ফ’নত দৃশ্যমান অথবা প্লে কৰা অডিঅ’, ফট’ পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।&lt;br/&gt;&lt;br/&gt;%1$sএ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত এপ্‌সমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্ৰছ-ডিভাইচ সেৱাসমূহ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ ডিভাইচসমূহৰ মাজত এপ্‌ ষ্ট্ৰীম কৰাৰ বাবে অনুৰোধ জনাইছে"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ ডিভাইচসমূহৰ মাজত এপ্‌ দেখুৱাবলৈ আৰু ষ্ট্ৰীম কৰিবলৈ অনুৰোধ জনাইছে"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"এই এপ্‌টোক আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</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_title_computer" msgid="4671071173916176037">"Google Play সেৱা"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ ফ’নৰ ফট’, মিডিয়া আৰু জাননী এক্সেছ কৰাৰ বাবে অনুৰোধ জনাইছে"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ক এই কাৰ্যটো সম্পাদন কৰিবলৈ দিবনে?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ ফ’নৰ এপ্‌ আৰু ছিষ্টেমৰ সুবিধা ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$sএ আপোনাৰ ফ’নত দৃশ্যমান অথবা প্লে কৰা অডিঅ’, ফট’ পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।&lt;br/&gt;&lt;br/&gt;%1$sএ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত এপ্‌সমূহ আৰু ছিষ্টেমৰ সুবিধাসমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ নিকটৱৰ্তী ডিভাইচত এপ্‌ আৰু ছিষ্টেমৰ অন্য সুবিধাসমূহ ষ্ট্ৰীম কৰাৰ অনুমতি দিবলৈ অনুৰোধ জনাইছে"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ এপ্‌সমূহ আৰু ছিষ্টেমৰ সুবিধাসমূহ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;ত ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"মিডিয়া আউটপুট সলনি কৰক"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ফট’ আৰু মিডিয়া"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"জাননী"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"এপ্"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ষ্ট্ৰীম কৰি থকা হৈছে"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ফ’ন কল কৰক আৰু সেয়া পৰিচলনা কৰক"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ফ’নৰ কল লগ পঢ়ক আৰু লিখক"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"এছএমএছ বার্তাবোৰ প্ৰেৰণ কৰক আৰু চাওক"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"নিকটৱৰ্তী ডিভাইচসমূহ বিচাৰক, সেইসমূহৰ সৈতে সংযোগ কৰক আৰু সেইসমূহৰ প্ৰাসংগিক অৱস্থান নিৰ্ধাৰণ কৰক"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"সম্পৰ্কসূচী, বাৰ্তা আৰু ফট’ৰ দৰে তথ্যকে ধৰি আটাইবোৰ জাননী পঢ়ক"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• সম্পৰ্কসূচী, বাৰ্তা আৰু ফট’ৰ দৰে তথ্যকে ধৰি আটাইবোৰ জাননী পঢ়ক&lt;br/&gt;• জাননী পঠিয়াওক&lt;br/&gt;&lt;br/&gt;আপুনি যিকোনো সময়তে ছেটিং &gt; জাননীলৈ গৈ এই এপ্‌টোৰ জাননী পঢ়া আৰু পঠিওৱাৰ সক্ষমতা পৰিচালনা কৰিব পাৰে।"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"আপোনাৰ ফ’নৰ এপ্ ষ্ট্ৰীম কৰক"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"আপোনাৰ ফ’নৰ পৰা এপ্‌ আৰু ছিষ্টেমৰ অন্য সুবিধাসমূহ ষ্ট্ৰীম কৰক"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"উপলব্ধ ডিভাইচৰ সূচী এখন এক্সেছ কৰক আৰু কোনটোৱে ষ্ট্ৰীম কৰে অথবা অন্য এপৰ পৰা অডিঅ’ অথবা ভিডিঅ’ কাষ্ট কৰে সেয়া নিয়ন্ত্ৰণ কৰক"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ফ’ন"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"টেবলেট"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"টিভি"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ডিভাইচ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index 6128c5c..2a636a9 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək cihaz seçin"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Ayarlamaq üçün <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Bu tətbiq zəng edənin adı kimi məlumatları sinxronlaşdıra, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Bu tətbiq zəng edənin adı kimi məlumatları sinxronlaşdıra, <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazında bu icazələrə daxil ola biləcək"</string>
     <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="2872254734959842579">"Bu tətbiq <xliff:g id="DEVICE_NAME">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefon tətbiqlərini yayımlamaq icazəsi verilsin?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s audio, foto, parol və mesajlar daxil olmaqla telefonda görünən və ya oxudulan məzmuna giriş əldə edəcək.&lt;br/&gt;&lt;br/&gt;%1$s bu icazəyə giriş silinənədək tətbiqləri yayımlayacaq."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlararası xidmətlər"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> adından cihazlar arasında tətbiqləri yayımlamaq icazəsi istəyir"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqləri göstərmək və cihazlar arasında yayımlamaq üçün <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> əvəzinə icazə tələb edir"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; üçün <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> tətbiqlərini &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazına sinxronlaşdırmaq icazəsi verilsin?"</string>
+    <string name="summary_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> audio, foto, 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ına yayımlaya biləcək."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqləri göstərmək və cihazlar arasında yayımlamaq üçün <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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</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_title_computer" msgid="4671071173916176037">"Google Play xidmətləri"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> adından telefonun foto, media və bildirişlərinə giriş icazəsi istəyir"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; cihazına bu əməliyyatı yerinə yetirmək icazəsi verilsin?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; tətbiqinə telefon tətbiqlərini və sistem funksiyalarını yayımlamaq icazəsi verilsin?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s audio, foto, ödəniş məlumatı, parol və mesajlar daxil olmaqla telefonda görünən və ya oxudulan məzmuna giriş əldə edəcək.&lt;br/&gt;&lt;br/&gt;%1$s bu icazəyə giriş silinənədək tətbiqləri və sistem funksiyalarını yayımlayacaq."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından tətbiq və digər sistem funksiyalarını yaxınlıqdakı cihazlara yayımlamaq icazəsi sitəyir"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; üçün <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> tətbiq və sistem funksiyalarını &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; cihazına yayımlamaq icazəsi verilsin?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 işə salınan 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ətbiq və sistem funksiyalarını <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazına yayımlaya biləcək."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> cihazlar arasında tətbiqləri və digər sistem funksiyalarını yayımlamaq üçün <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media çıxışını dəyişin"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto və media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Bildirişlər"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Tətbiqlər"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayım"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon zənglərinin edilməsi və idarəsi"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon zəng siyahısının oxunması və yazılması"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajlarının göndərilməsi və onlara baxmaq"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yaxınlıqdakı cihazların tapılması, onlara qoşulmaq və nisbi mövqelərinin təyin edilməsi"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirişlərin oxunması"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kontakt, mesaj və foto kimi məlumatlar daxil olmaqla bütün bildirişlərin oxunması&lt;br/&gt;• Bildirişlərin göndərilməsi&lt;br/&gt;&lt;br/&gt;Bu tətbiqin bildirişləri oxumaq və göndərmək imkanını Ayarlar &gt; Bildirişlər bölməsində idarə edə bilərsiniz."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun tətbiqlərini yayımlayın"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefondan tətbiq və digər sistem funksiyalarını yayımlayın"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Əlçatan cihazların siyahısına daxil olun, digər tətbiqlərdən audio və ya video yayımlayacaq cihazı idarə edin"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefonda"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"planşetdə"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"cihaz"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index dde4906..010b59a 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> koji želite da podesite"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Ovoj aplikaciji će biti dozvoljeno da sinhronizuje podatke, poput imena osobe koja upućuje poziv, i pristupa tim dozvolama na vašem uređaju (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Ovoj aplikaciji će biti dozvoljeno da sinhronizuje podatke, poput imena pozivaoca, i pristupa tim dozvolama na uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Ovoj aplikaciji će biti dozvoljeno da pristupa ovim dozvolama na vašem uređaju (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Želite li da dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strimuje aplikacije na telefonu?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s će imati pristup svemu što se vidi ili pušta na telefonu, uključujući zvuk, slike, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;%1$s će moći da strimuje aplikacije dok ne uklonite pristup ovoj dozvoli."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na više uređaja"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> da prikazuje i strimuje aplikacije između uređaja"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Želite li 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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<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 prikazuje i strimuje aplikacije između uređaja"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Dozvolite da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama sa telefona"</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_title_computer" msgid="4671071173916176037">"Google Play usluge"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Želite li da dozvolite da &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; obavi ovu radnju?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Želite li da dozvolite da &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; strimuje aplikacije i funkcije sistema na telefonu?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s će imati pristup svemu što se vidi ili pušta na telefonu, uključujući zvuk, slike, informacije o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;%1$s će moći da strimuje aplikacije i funkcije sistema dok ne uklonite pristup ovoj dozvoli."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije i druge sistemske funkcije na uređaje u blizini"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Želite li da dozvolite da &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 i sistemske funkcije na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovoj dozvoli."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 druge sistemske funkcije između uređaja"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promena medijskog izlaza"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Obaveštenja"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Upućivanje telefonskih poziva i upravljanje njima"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje evidencije poziva na telefonu"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregled SMS poruka"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje uređaja u blizini, utvrđivanje njihove relativne pozicije i povezivanje sa njima"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obaveštenja, uključujući informacija poput kontakata, poruka i slika&lt;br/&gt;• Slanje obaveštenja&lt;br/&gt;&lt;br/&gt;Da biste upravljali dozvolama ove aplikacije za čitanje i slanje obaveštenja, idite u Podešavanja &gt; Obaveštenja."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strimujte aplikacije na telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strimujte aplikacije i druge sistemske funkcije sa telefona"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Pristup listi dostupnih uređaja i kontrola uređaja koji strimuje ili prebacuje audio ili video iz drugih aplikacija"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefonu"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tabletu"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"uređaj"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 266e6be..5cc26a7 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Выберыце прыладу (<xliff:g id="APP_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Выберыце імя <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для наладжвання"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Гэтая праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) на вашай прыладзе \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" і атрымае наступныя дазволы"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) на вашай прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" і атрымае наступныя дазволы"</string>
     <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="2872254734959842579">"Гэтая праграма будзе мець на вашай прыладзе \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" наступныя дазволы"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Дазволіць праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; трансліраваць праграмы з вашага тэлефона?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"Праграма \"%1$s\" будзе мець доступ да ўсяго, што адлюстроўваецца на экране тэлефона ці прайграецца на ім, уключаючы аўдыя, фота, паролі і паведамленні.&lt;br/&gt;&lt;br/&gt;Праграма \"%1$s\" зможа трансліраваць праграмы, пакуль вы не адклічаце гэты дазвол."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" на трансляцыю праграм паміж вашымі прыладамі"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імені вашай прылады \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" на паказ і трансляцыю праграм паміж прыладамі"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Гэта праграма будзе мець на вашай прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" наступныя дазволы"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Дазволіць праграме &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="1274464413649731829">"Праграма \"<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="1944832605534698562">"Праграма \"<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="4693714143506569253">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</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_title_computer" msgid="4671071173916176037">"Сэрвісы Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" на доступ да фота, медыяфайлаў і апавяшчэнняў на вашым тэлефоне"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Дазволіць прыладзе &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; выканаць гэта дзеянне?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Дазволіць прыладзе &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; трансліраваць праграмы і сістэмныя функцыі з вашага тэлефона?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Прылада \"%1$s\" будзе мець доступ да ўсяго, што адлюстроўваецца на экране тэлефона ці прайграецца на ім, уключаючы аўдыя, фота, плацежную інфармацыю, паролі і паведамленні.&lt;br/&gt;&lt;br/&gt;Прылада \"%1$s\" зможа трансліраваць праграмы і сістэмныя функцыі, пакуль вы не адклічаце гэты дазвол."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на перадачу плынню змесціва праграм і іншых функцый сістэмы на прылады паблізу"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Дазволіць прыладзе &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; трансліраваць праграмы і сістэмныя функцыі прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на прыладу &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Праграма \"<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_nearby_device_streaming" msgid="8509848562931818793">"Праграма \"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Змяніць прыладу вываду медыя"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Фота і медыяфайлы"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Апавяшчэнні"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Праграмы"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Перадача плынню"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Ажыццяўленне тэлефонных выклікаў і кіраванне імі"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Чытанне і запіс журнала тэлефонных выклікаў"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Адпраўка SMS-паведамленняў і іх прагляд"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Пошук прылад паблізу, падключэнне да іх і вызначэнне іх адноснага месцазнаходжання"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Чытанне ўсіх апавяшчэнняў, у тым ліку такой інфармацыі, як кантакты, паведамленні і фота"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Чытанне ўсіх апавяшчэнняў, у тым ліку такой інфармацыі, як кантакты, паведамленні і фота.&lt;br/&gt;• Адпраўка апавяшчэнняў.&lt;br/&gt;&lt;br/&gt;Дазволы гэтай праграмы на чытанне і адпраўку апавяшчэнняў можна змяніць у любы час, выбраўшы пункт меню \"Налады &gt; Апавяшчэнні\"."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Трансляцыя змесціва праграм з вашага тэлефона"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Перадача плынню змесціва праграм і іншых функцый сістэмы з вашага тэлефона"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Прагляд спіса даступных прылад і кіраванне тым, на якой з іх будзе трансліравацца аўдыя і відэа з іншых праграм"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"тэлефон"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"планшэт"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"тэлевізар"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"прылада"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index 2316baa..747a071 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Изберете устройство, което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, за да го настроите"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Това приложение ще получи право да синхронизира различна информация, като например името на обаждащия се, и достъп до следните разрешения за вашия <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Това приложение ще получи право да синхронизира различна информация, като например името на обаждащия се, и достъп до следните разрешения за вашия <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Това приложение ще има достъп до следните разрешения за вашето <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Разрешете на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да осъществява достъп до тази информация от телефона ви"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Да се разреши ли на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да предава поточно приложенията на телефона ви?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ще има достъп до всичко, което се показва или възпроизвежда на телефона, включително аудиосъдържание, снимки, пароли и съобщения.&lt;br/&gt;&lt;br/&gt;%1$s ще може да предава поточно приложения, докато не премахнете това разрешение."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да показва и да предава поточно приложения между устройствата ви"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Това приложение ще има достъп до следните разрешения за вашите <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Да се разреши ли на &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"Разрешете на &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да осъществява достъп до тази информация от телефона ви"</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_title_computer" msgid="4671071173916176037">"Услуги за Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за достъп до снимките, мултимедията и известията на телефона ви"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Разрешавате ли на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; да предприема това действие?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Да се разреши ли на &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; да предава поточно приложенията и системните функции на телефона ви?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ще има достъп до всичко, което се показва или възпроизвежда на телефона, включително аудиосъдържание, снимки, данни за плащане, пароли и съобщения.&lt;br/&gt;&lt;br/&gt;%1$s ще може да предава поточно приложения и системни функции, докато не премахнете това разрешение."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения и други системни функции към устройства в близост"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Да се разреши ли на &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; да предава поточно към &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; приложенията и системните функции на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Промяна на мултимедийния изход"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Снимки и мултимедия"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Известия"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Приложения"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Поточно предаване"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Извършване и управление на телефонни обаждания"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Четене и запис на списъка с обажданията"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Изпращане и преглед на SMS съобщения"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Намиране и свързване с устройства в близост, както и определяне на относителната им позиция"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Четене на всички известия, включително различна информация, като например контакти, съобщения и снимки"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Четене на всички известия, включително различна информация, като например контакти, съобщения и снимки.&lt;br/&gt;• Изпращане на известия.&lt;br/&gt;&lt;br/&gt;Можете да управлявате способността на това приложение да чете и изпраща известия по всяко време в „Настройки“ &gt; „Известия“."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Поточно предаване на приложенията на телефона ви"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Поточно предаване на приложения и други системни функции от телефона ви"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Осъществяване на достъп до списъка с налични устройства и контролиране на това кое да се използва за поточно предаване или предаване на аудио/видео от други приложения"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефон"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"таблет"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"телевизор"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"устройство"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index 771f4ec..ef9fddd 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ম্যানেজ করা যাবে এমন একটি ডিভাইস বেছে নিন"</string>
     <string name="chooser_title" msgid="2235819929238267637">"সেট-আপ করতে কোনও <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"এই অ্যাপকে, কল করছেন এমন কোনও ব্যক্তির নামের মতো তথ্য সিঙ্ক করতে এবং আপনার <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে দেওয়া হবে"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"কল করছেন এমন কোনও ব্যক্তির নামের মতো তথ্য সিঙ্ক ও আপনার <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করার জন্য, এই অ্যাপকে অনুমতি দেওয়া হবে"</string>
     <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="2872254734959842579">"এই অ্যাপ আপনার <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে পারবে"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"আপনার ফোন থেকে &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; অ্যাপকে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; আপনার ফোনের অ্যাপ স্ট্রিম করার অনুমতি দেবেন?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s, অডিও, ফটো, পাসওয়ার্ড এবং মেসেজ সহ ফোনে দেখা বা চালানো যায় এমন সব কিছু অ্যাক্সেস করতে পারবে।&lt;br/&gt;&lt;br/&gt;আপনি এই অনুমতি না সরানো পর্যন্ত %1$s অ্যাপ স্ট্রিম করতে পারবে।"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্রস-ডিভাইস পরিষেবা"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"আপনার ডিভাইসগুলির মধ্যে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"আপনার ডিভাইসের মধ্যে অ্যাপ, ডিসপ্লে এবং স্ট্রিম করতে আপনার <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চাইছে"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করার জন্য এই অ্যাপকে অনুমতি দেওয়া হবে"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"আপনার <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="1274464413649731829">"অডিও, ফটো, পাসওয়ার্ড ও মেসেজ সহ <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="1944832605534698562">"আপনার বিভিন্ন ডিভাইসের মধ্যে অ্যাপ, ডিসপ্লে এবং স্ট্রিম করার জন্য আপনার <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="4693714143506569253">"আপনার ফোন থেকে &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</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_title_computer" msgid="4671071173916176037">"Google Play পরিষেবা"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"আপনার ফোনের ফটো, মিডিয়া এবং তথ্য অ্যাক্সেস করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-কে এই কাজটি করতে দেবেন?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; আপনার ফোনের অ্যাপ এবং সিস্টেম ফিচার স্ট্রিম করার অনুমতি দেবেন?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s, অডিও, ফটো, পেমেন্টের তথ্য, পাসওয়ার্ড এবং মেসেজ সহ আপনার ফোনে দেখা বা চালানো যায় এমন সব কিছু অ্যাক্সেস করতে পারবে।&lt;br/&gt;&lt;br/&gt;আপনি এই অনুমতি না সরানো পর্যন্ত %1$s অ্যাপ এবং সিস্টেম ফিচার স্ট্রিম করতে পারবে।"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"আশেপাশের ডিভাইসে অ্যাপ ও অন্যান্য সিস্টেম ফিচার স্ট্রিম করার জন্য আপনার <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চেয়ে অনুরোধ করছে"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"আপনার <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর অ্যাপ ও সিস্টেমের ফিচার &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?-এ স্ট্রিম করার জন্য &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;-কে অনুমতি দেবেন?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"অডিও, ফটো, পেমেন্টের তথ্য, পাসওয়ার্ড ও মেসেজ সহ আপনার <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_nearby_device_streaming" msgid="8509848562931818793">"আপনার বিভিন্ন ডিভাইসের মধ্যে অ্যাপ ও সিস্টেমের অন্যান্য ফিচার স্ট্রিম করার জন্য, আপনার <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"মিডিয়া আউটপুট পরিবর্তন করুন"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ফটো ও মিডিয়া"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"বিজ্ঞপ্তি"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"অ্যাপ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"স্ট্রিমিং"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ফোন কল করুন এবং ম্যানেজ করুন"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ফোন কল লগ দেখে তাতে পরিবর্তন করার অনুমতি দিন"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"এসএমএস মেসেজ দেখুন ও পাঠান"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"আশেপাশের ডিভাইস খুঁজুন, তার সাথে কানেক্ট করুন এবং তার আপেক্ষিক অবস্থান নির্ধারণ করুন"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"পরিচিতি, মেসেজ এবং ফটোর মতো তথ্য সহ সমস্ত বিজ্ঞপ্তি পড়ুন"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• পরিচিতি, মেসেজ এবং ফটোর মতো তথ্য সহ সমস্ত বিজ্ঞপ্তি পড়ুন&lt;br/&gt;• বিজ্ঞপ্তি পাঠান&lt;br/&gt;&lt;br/&gt;আপনি সেটিংস &gt; বিজ্ঞপ্তি থেকে এই অ্যাপটির বিজ্ঞপ্তি পড়ার এবং পাঠানোর ক্ষমতা ম্যানেজ করতে পারবেন।"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"আপনার ফোনের অ্যাপ স্ট্রিম করুন"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"আপনার ফোন থেকে অ্যাপ ও অন্যান্য সিস্টেম ফিচার স্ট্রিম করে"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"উপলভ্য থাকা ডিভাইসের তালিকা অ্যাক্সেস করুন এবং কোন ডিভাইস অন্যান্য অ্যাপ থেকে অডিও বা ভিডিও স্ট্রিম অথবা কাস্ট করতে পারে তা কন্ট্রোল করুন"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ফোন"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ট্যাবলেট"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"টিভি"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ডিভাইস"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 247d017..69ae96f 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"sat"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Odaberite <xliff:g id="PROFILE_NAME">%1$s</xliff:g> da postavite"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aplikaciji će biti dozvoljeni sinhroniziranje informacija, kao što je ime osobe koja upućuje poziv i pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aplikaciji će biti dozvoljeno da sinhronizira informacije, kao što je ime osobe koja upućuje poziv, i pristupa ovim odobrenjima koje sadržava vaš <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pristupa ovim informacijama s telefona"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Dozvoliti uređaju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da prenosi aplikacije telefona?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s će imati pristup svemu što je vidljivo ili se reproducira na telefonu, uključujući zvuk, fotografije, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;%1$s će moći prenositi aplikacije dok ne uklonite pristup ovom odobrenju."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluga na više uređaja"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime vašeg uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> da prikazuje i prenosi aplikacije između vaših uređaja"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"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 prikazuje i prenosi aplikacije između vaših uređaja"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Dozvolite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa ovim informacijama s vašeg telefona"</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_title_computer" msgid="4671071173916176037">"Google Play usluge"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> zahtijeva odobrenje da pristupi fotografijama, medijima i obavještenjima na telefonu"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Dozvoliti uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; da poduzme ovu radnju?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Dozvoliti uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; da prenosi aplikacije telefona i funkcije sistema?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s će imati pristup svemu što je vidljivo ili se reproducira na telefonu, uključujući zvuk, fotografije, podatke o plaćanju, lozinke i poruke.&lt;br/&gt;&lt;br/&gt;%1$s će moći prenositi aplikacije i funkcije sistema dok ne uklonite pristup ovom odobrenju."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> traži odobrenje da prenosi aplikacije i druge funkcije sistema na uređajima u blizini"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Dozvoliti uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 i funkcije sistema na uređaju <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovom odobrenju."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"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 prenosi aplikacije i druge funkcije sistema između vaših uređaja"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promjena medijskog izlaza"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Obavještenja"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Prijenos"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Pozivanje i upravljanje telefonskim pozivima"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika telefonskih poziva"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledanje SMS poruka"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje uređaja u blizini, povezivanje s njima i određivanje njihovog relativnog položaja"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• čitanje svih obavještenja, uključujući informacije poput kontakata, poruka i fotografija&lt;br/&gt;• slanje obavještenja&lt;br/&gt;&lt;br/&gt;Da upravljate mogućnošću ove aplikacije da čita i šalje obavještenja, idite u Postavke &gt; Obavještenja."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Prenosite aplikacije s telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Prijenos aplikacija i drugih funkcija sistema s vašeg telefona"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Pristupa listi dostupnih uređaja i kontrolira koji od njih prenosi ili emitira zvuk ili videozapis iz drugih aplikacija"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"uređaj"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index a40f2bd..f8c4084 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -21,26 +21,23 @@
     <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Tria un dispositiu perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> per configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, i accedir a aquests permisos al dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, i accedir a aquests permisos al <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Aquesta aplicació podrà accedir a aquests permisos del dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; accedeixi a aquesta informació del telèfon"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vols permetre que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions del telèfon?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al telèfon, inclosos àudios, fotos, contrasenyes i missatges.&lt;br/&gt;&lt;br/&gt;%1$s podrà reproduir en continu aplicacions fins que suprimeixis l\'accés a aquest permís."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serveis multidispositiu"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) per reproduir en continu aplicacions entre els dispositius"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> per mostrar i reproduir en continu aplicacions entre els dispositius"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Vols permetre que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions del <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="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclosos àudios, fotos, contrasenyes i missatges.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> podrà reproduir en continu aplicacions a <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="1944832605534698562">"<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 mostrar i reproduir en continu aplicacions entre els dispositius"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permet que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; accedeixi a aquesta informació del telèfon"</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_title_computer" msgid="4671071173916176037">"Serveis de Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) per accedir a les fotos, el contingut multimèdia i les notificacions del telèfon"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vols permetre que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dugui a terme aquesta acció?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vols permetre que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions del telèfon i les funcions del sistema?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al telèfon, inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.&lt;br/&gt;&lt;br/&gt;%1$s podrà reproduir en continu aplicacions i funcions del sistema fins que suprimeixis l\'accés a aquest permís."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> sol·licita permís en nom del teu dispositiu (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) per reproduir en continu aplicacions i altres funcions del sistema en dispositius propers"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vols permetre que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; reprodueixi en continu les aplicacions i les funcions del sistema del <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>?"</string>
+    <!-- String.format failed for translation -->
+    <!-- no translation found for summary_nearby_device_streaming (962267343109051648) -->
+    <skip />
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 reproduir en continu aplicacions i altres funcions del sistema entre els dispositius"</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>
@@ -62,8 +59,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Canvia la sortida multimèdia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificacions"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacions"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Estríming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Fer i gestionar trucades telefòniques"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Llegir i escriure el registre de trucades del telèfon"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar i llegir missatges SMS"</string>
@@ -73,10 +68,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Determinar la posició relativa dels dispositius propers, trobar-los i connectar-s\'hi"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Llegir totes les notificacions, inclosa informació com ara els contactes, els missatges i les fotos.&lt;br/&gt;• Enviar notificacions.&lt;br/&gt;&lt;br/&gt;A Configuració &gt; Notificacions, pots gestionar en qualsevol moment els permisos de l\'aplicació per llegir i enviar notificacions."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Reprodueix en continu aplicacions del telèfon"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Reprodueix en continu aplicacions i altres funcions del sistema des del telèfon"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accedeix a una llista de dispositius disponibles i controla quin reprodueix o emet àudio o vídeo des d\'altres aplicacions"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telèfon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tauleta"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televisor"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositiu"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index 3acd179..04e7380 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vyberte zařízení, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete nastavit"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, a získat na zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> přístup k těmto oprávněním"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Tato aplikace bude moct synchronizovat údaje, jako je jméno volajícího, a získat na zařízení typu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> přístup k těmto oprávněním"</string>
     <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="2872254734959842579">"Tato aplikace bude mít ve vašem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> povolený přístup k těmto oprávněním"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 telefonu"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Povolit zařízení &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovat aplikace telefonu?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s bude mít přístup ke všemu, co na telefonu zobrazíte nebo přehrajete, včetně zvuku, fotek, hesel a zpráv.&lt;br/&gt;&lt;br/&gt;%1$s bude moci streamovat aplikace, dokud přístup k tomuto oprávnění neodeberete."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pro více zařízení"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění ke streamování aplikací mezi zařízeními"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění k zobrazení a streamování obsahu mezi zařízeními"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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, 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="1944832605534698562">"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 zobrazení a streamování obsahu mezi zařízeními"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"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 telefonu"</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_title_computer" msgid="4671071173916176037">"Služby Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením v telefonu"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Povolit zařízení &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; podniknout tuto akci?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Povolit zařízení &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovat aplikace a systémové funkce telefonu?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s bude mít přístup ke všemu, co na telefonu zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.&lt;br/&gt;&lt;br/&gt;%1$s bude moci streamovat aplikace a systémové funkce, dokud přístup k tomuto oprávnění neodeberete."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem vašeho zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace a další systémové funkce do zařízení v okolí"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Povolit aplikaci &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 a systémové funkce 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_nearby_device_streaming" msgid="8509848562931818793">"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í ke streamování aplikací a dalších systémových funkcí mezi zařízeními"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Změna mediálního výstupu"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotky a média"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Oznámení"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikace"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamování"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Uskutečňování a správa telefonních hovorů"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čtení seznamu hovorů a zapisování do něj"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Odesílání a zobrazování zpráv SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vyhledávání zařízení v okolí, připojování se k nim a zjišťování jejich relativní polohy"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čtení veškerých oznámení včetně informací, jako jsou kontakty, zprávy a fotky"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čtení veškerých oznámení, včetně informací, jako jsou kontakty, zprávy a fotky&lt;br/&gt;• Odesílání oznámení&lt;br/&gt;&lt;br/&gt;Oprávnění této aplikace číst a odesílat oznámení můžete kdykoli změnit v Nastavení &gt; Oznámení."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamujte aplikace v telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streamování aplikací a dalších systémových funkcí z telefonu"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Přístup k seznamu dostupných zařízení a určení, které bude streamovat nebo odesílat zvuk či video z ostatních aplikací"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefonu"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tabletu"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televize"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"zařízení"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index 9d0846c..d948804 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ur"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vælg en enhed, som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Vælg en <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, som du vil konfigurere"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Denne app får tilladelse til at synkronisere oplysninger, f.eks. navne på dem, der ringer, og adgang til disse tilladelser på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Denne app får tilladelse til at synkronisere oplysninger, f.eks. navne på dem, der ringer, og adgang til disse tilladelser på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Denne app får adgang til disse tilladelser på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Giv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adgang til disse oplysninger fra din telefon"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vil du give &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilladelse til at streame din telefons apps?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s har adgang til alt, der er synligt eller afspilles på telefonen, herunder lyd, billeder, adgangskoder og beskeder.&lt;br/&gt;&lt;br/&gt;%1$s kan streame apps, indtil du fjerner adgangen til denne tilladelse."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester til flere enheder"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at streame apps mellem dine enheder"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at vise og streame apps mellem dine enheder"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<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 vise og streame apps mellem dine enheder"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Tillad, at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; får adgang til disse oplysninger fra din telefon"</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_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at få adgang til din telefons billeder, medier og notifikationer"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vil du tillade, at &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; foretager denne handling?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vil du give &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; tilladelse til at streame din telefons apps og systemfunktioner?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s har adgang til alt, der er synligt eller afspilles på din telefon, herunder lyd, billeder, adgangskoder og beskeder.&lt;br/&gt;&lt;br/&gt;%1$s kan streame apps og systemfunktioner, indtil du fjerner adgangen til denne tilladelse."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 streame apps og andre systemfunktioner til enheder i nærheden"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vil du give &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 og systemfunktioner til <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, indtil du fjerner adgangen til denne tilladelse."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 streame apps og andre systemfunktioner mellem dine enheder"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Skift medieoutput"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Billeder og medier"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifikationer"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Foretage og administrere telefonopkald"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Læse og redigere opkaldshistorik"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Sende og se sms-beskeder"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finde, oprette forbindelse til og fastslå den omtrentlige lokation af enheder i nærheden"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Læse alle notifikationer, herunder oplysninger som f.eks. kontakter, beskeder og billeder&lt;br/&gt;• Sende notifikationer&lt;br/&gt;&lt;br/&gt;Du kan til enhver tid administrere appens mulighed for at læse og sende notifikationer under Indstillinger &gt; Notifikationer."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream din telefons apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps og andre systemfunktioner fra din telefon"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Få adgang til en liste over tilgængelige enheder, og vælg, hvilken enhed der skal streame eller caste lyd eller video fra andre apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"fjernsyn"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"enhed"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 3f730fc..8d65f68 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Gerät auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
     <string name="chooser_title" msgid="2235819929238267637">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> zum Einrichten auswählen"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Diese App darf dann Daten wie den Namen eines Anrufers synchronisieren und auf diese Berechtigungen auf deinem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zugreifen"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Diese App darf dann Daten wie den Namen eines Anrufers synchronisieren und auf diese Berechtigungen auf deinem Gerät (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) zugreifen"</string>
     <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="2872254734959842579">"Diese App darf dann auf diese Berechtigungen auf deinem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zugreifen:"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; Zugriff auf diese Informationen von deinem Smartphone gewähren"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Zulassen, dass &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; die Apps auf deinem Smartphone streamt?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s hat dann Zugriff auf alle Inhalte, die auf deinem Smartphone sichtbar sind oder abgespielt werden, einschließlich Audio, Fotos, Passwörter und Nachrichten.&lt;br/&gt;&lt;br/&gt;%1$s kann so lange Apps streamen, bis du diese Berechtigung entfernst."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung, gegenseitig das Anzeigen und Streamen von Apps zu erlauben"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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_app_streaming" msgid="1274464413649731829">"<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örter 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="1944832605534698562">"<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, Apps zwischen deinen Geräten anzuzeigen und zu streamen"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; Zugriff auf diese Informationen von deinem Smartphone gewähren"</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_title_computer" msgid="4671071173916176037">"Google Play-Dienste"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Zugriff auf die Fotos, Medien und Benachrichtigungen deines Smartphones"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Darf das Gerät &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; diese Aktion ausführen?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Zulassen, dass &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; die Apps und Systemfunktionen auf deinem Smartphone streamt?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s hat dann Zugriff auf alle Inhalte, die auf deinem Smartphone sichtbar sind oder abgespielt werden, einschließlich Audio, Fotos, Zahlungsinformationen, Passwörter und Nachrichten.&lt;br/&gt;&lt;br/&gt;%1$s kann so lange Apps und System­funktionen streamen, bis du diese Berechtigung entfernst."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 andere Systemfunktionen auf Geräte in der Nähe zu streamen"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; zu streamen?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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örter und Nachrichten.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kann so lange Apps und Systemfunktionen auf „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“ streamen, bis du diese Berechtigung entfernst."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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, Apps und andere Systemfunktionen zwischen deinen Geräten 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Medienausgabe ändern"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos und Medien"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Benachrichtigungen"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Anrufe starten und verwalten"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Auf die Anrufliste zugreifen und sie bearbeiten"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS senden und ansehen"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Geräte in der Nähe finden, eine Verbindung zu ihnen herstellen und ihren relativen Standort ermitteln"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakten, Nachrichten und Fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle Benachrichtigungen sehen, einschließlich Informationen wie Kontakte, Nachrichten und Fotos&lt;br/&gt;• Benachrichtigungen senden&lt;br/&gt;&lt;br/&gt;Du kannst die Berechtigungen dieser App zum Sehen und Senden von Benachrichtigungen jederzeit unter „Einstellungen &gt; Benachrichtigungen“ ändern."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Smartphone-Apps streamen"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps und andere Systemfunktionen von deinem Smartphone streamen"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Auf eine Liste mit verfügbaren Geräten zugreifen und festlegen, welches Gerät Audio- oder Videoinhalte aus anderen Apps streamt oder überträgt"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Smartphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"Tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"Fernseher"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"Gerät"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 2d27790..4a186d5 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Επιλέξτε μια συσκευή για διαχείριση μέσω της εφαρμογής &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για ρύθμιση"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες, όπως το όνομα ενός ατόμου που σας καλεί, και να αποκτά πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες, όπως το όνομα ενός ατόμου που σας καλεί, και να αποκτά πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Να επιτρέπεται στο &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας."</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Να επιτρέπεται στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; η μετάδοση σε ροή των εφαρμογών του τηλεφώνου σας;"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"Το %1$s θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο τηλέφωνο, συμπεριλαμβανομένων ήχων, φωτογραφιών, κωδικών πρόσβασης και μηνυμάτων.&lt;br/&gt;&lt;br/&gt;Το %1$s θα μπορεί να μεταδώσει εφαρμογές σε ροή μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Υπηρεσίες πολλών συσκευών"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> για προβολή και μετάδοση εφαρμογών σε ροή μεταξύ των συσκευών σας"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Να επιτρέπεται στην εφαρμογή &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="1274464413649731829">"Η εφαρμογή <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="1944832605534698562">"Η εφαρμογή <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="4693714143506569253">"Επιτρέψτε στην εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; να έχει πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας"</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_title_computer" msgid="4671071173916176037">"Υπηρεσίες Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Να επιτρέπεται στη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; να εκτελεί αυτή την ενέργεια;"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Να επιτρέπεται στη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; η μετάδοση σε ροή των εφαρμογών και των λειτουργιών συστήματος του τηλεφώνου σας;"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Το %1$s θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο τηλέφωνο, συμπεριλαμβανομένων ήχων, φωτογραφιών, στοιχείων πληρωμής, κωδικών πρόσβασης και μηνυμάτων.&lt;br/&gt;&lt;br/&gt;Το %1$s θα μπορεί να μεταδώσει εφαρμογές και λειτουργίες συστήματος σε ροή μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής σας <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για ροή εφαρμογών και άλλων λειτουργιών του συστήματος σε συσκευές σε κοντινή απόσταση"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Να επιτρέπεται στη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; να κάνει ροή των εφαρμογών και των λειτουργιών συστήματος της συσκευής <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> στη συσκευή &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;;"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Η εφαρμογή <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_nearby_device_streaming" msgid="8509848562931818793">"Η εφαρμογή <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Αλλαγή εξόδου μέσων"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Φωτογραφίες και μέσα"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Ειδοποιήσεις"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Εφαρμογές"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Ροή"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Πραγματοποίηση και διαχείριση τηλεφωνικών κλήσεων"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ανάγνωση και εγγραφή αρχείου καταγραφής τηλεφωνικών κλήσεων"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Αποστολή και προβολή μηνυμάτων SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Εύρεση, σύνδεση και προσδιορισμός της σχετικής τοποθεσίας συσκευών σε κοντινή απόσταση"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ανάγνωση όλων των ειδοποιήσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφίες"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ανάγνωση όλων των ειδοποιήσεων, συμπεριλαμβανομένων πληροφοριών όπως επαφές, μηνύματα και φωτογραφίες&lt;br/&gt;• Αποστολή ειδοποιήσεων&lt;br/&gt;&lt;br/&gt;Μπορείτε να διαχειριστείτε τη δυνατότητα της εφαρμογής να διαβάζει και να στέλνει ειδοποιήσεις οποιαδήποτε στιγμή στις Ρυθμίσεις &gt; Ειδοποιήσεις."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Μεταδώστε σε ροή τις εφαρμογές του τηλεφώνου σας"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Ροή εφαρμογών και άλλων λειτουργιών του συστήματος από το τηλέφωνό σας"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Αποκτήστε πρόσβαση σε μια λίστα διαθέσιμων συσκευών και ελέγξτε ποιες κάνουν ροή ή μετάδοση ήχου ή βίντεο από άλλες εφαρμογές"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"τηλέφωνο"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"τηλεόραση"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"συσκευή"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
index e23a48c..346758a 100644
--- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s will have access to anything that\'s visible or played on the phone, including audio, photos, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps until you remove access to this permission."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to display and stream apps between your devices"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on the <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, 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="1944832605534698562">"<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 display and stream apps between your devices"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to take this action?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps and system features?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s will have access to anything that\'s visible or played on your phone, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps and system features until you remove access to this permission."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 stream apps and other system features to nearby devices"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 and system features to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 stream apps and other system features between your devices"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Change media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Make and manage phone calls"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Read and write phone call log"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Send and view SMS messages"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Find, connect to and determine the relative position of Nearby devices"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Read all notifications, including information like contacts, messages and photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Read all notifications, including info like contacts, messages and photos&lt;br/&gt;• Send notifications&lt;br/&gt;&lt;br/&gt;You can manage this app\'s ability to read and send notifications at any time in Settings &gt; Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps and other system features from your phone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Access a list of available devices and control which one streams or casts audio or video from other apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"phone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"device"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
index 0160609..5716476 100644
--- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone’s apps?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s will have access to anything that’s visible or played on the phone, including audio, photos, passwords, and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps until you remove access to this permission."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to display and stream apps between your devices"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that’s visible or played on the <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, 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="1944832605534698562">"<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 display and stream apps between your devices"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to access your phone’s photos, media, and notifications"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to take this action?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone’s apps and system features?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s will have access to anything that’s visible or played on your phone, including audio, photos, payment info, passwords, and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps and system features until you remove access to this permission."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 stream apps and other system features to nearby devices"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 and system features to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 stream apps and other system features between your devices"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Change media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Make and manage phone calls"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Read and write phone call log"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Send and view SMS messages"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Find, connect to, and determine the relative position of nearby devices"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Read all notifications, including information like contacts, messages, and photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Read all notifications, including info like contacts, messages, and photos&lt;br/&gt;• Send notifications&lt;br/&gt;&lt;br/&gt;You can manage this app\'s ability to read and send notifications anytime in Settings &gt; Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps and other system features from your phone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Access a list of available devices and control which one streams or casts audio or video from other apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"phone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"device"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
index e23a48c..346758a 100644
--- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s will have access to anything that\'s visible or played on the phone, including audio, photos, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps until you remove access to this permission."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to display and stream apps between your devices"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on the <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, 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="1944832605534698562">"<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 display and stream apps between your devices"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to take this action?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps and system features?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s will have access to anything that\'s visible or played on your phone, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps and system features until you remove access to this permission."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 stream apps and other system features to nearby devices"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 and system features to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 stream apps and other system features between your devices"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Change media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Make and manage phone calls"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Read and write phone call log"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Send and view SMS messages"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Find, connect to and determine the relative position of Nearby devices"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Read all notifications, including information like contacts, messages and photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Read all notifications, including info like contacts, messages and photos&lt;br/&gt;• Send notifications&lt;br/&gt;&lt;br/&gt;You can manage this app\'s ability to read and send notifications at any time in Settings &gt; Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps and other system features from your phone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Access a list of available devices and control which one streams or casts audio or video from other apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"phone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"device"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
index e23a48c..346758a 100644
--- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"watch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s will have access to anything that\'s visible or played on the phone, including audio, photos, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps until you remove access to this permission."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to display and stream apps between your devices"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on the <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, 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="1944832605534698562">"<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 display and stream apps between your devices"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; to access this information from your phone"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to take this action?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; to stream your phone\'s apps and system features?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s will have access to anything that\'s visible or played on your phone, including audio, photos, payment info, passwords and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps and system features until you remove access to this permission."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 stream apps and other system features to nearby devices"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Allow &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 and system features to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 stream apps and other system features between your devices"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Change media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Make and manage phone calls"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Read and write phone call log"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Send and view SMS messages"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Find, connect to and determine the relative position of Nearby devices"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Read all notifications, including information like contacts, messages and photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Read all notifications, including info like contacts, messages and photos&lt;br/&gt;• Send notifications&lt;br/&gt;&lt;br/&gt;You can manage this app\'s ability to read and send notifications at any time in Settings &gt; Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream your phone’s apps"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Stream apps and other system features from your phone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Access a list of available devices and control which one streams or casts audio or video from other apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"phone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"device"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
index a601926..6c6a000 100644
--- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎watch‎‏‎‎‏‎"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎Choose a device to be managed by &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;‎‏‎‎‏‎"</string>
     <string name="chooser_title" msgid="2235819929238267637">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‎‏‎‏‎Choose a ‎‏‎‎‏‏‎<xliff:g id="PROFILE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to set up‎‏‎‎‏‎"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‎This app will be allowed to sync info, like the name of someone calling, and access these permissions on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎This app will be allowed to sync info, like the name of someone calling, and access these permissions on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <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="2872254734959842579">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎This app will be allowed to access these permissions on your ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to access this information from your phone‎‏‎‎‏‎"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to stream your phone’s apps?‎‏‎‎‏‎"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‏‎%1$s will have access to anything that’s visible or played on the phone, including audio, photos, passwords, and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps until you remove access to this permission.‎‏‎‎‏‎"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‏‎‏‎Cross-device services‎‏‎‎‏‎"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to stream apps between your devices‎‏‎‎‏‎"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to display and stream apps between your devices‎‏‎‎‏‎"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎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_app_streaming" msgid="1274464413649731829">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ will have access to anything that’s visible or played on the ‎‏‎‎‏‏‎<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>‎‏‎‎‏‏‏‎, including audio, photos, 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="1944832605534698562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎<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 display and stream apps between your devices‎‏‎‎‏‎"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to access this information from your phone‎‏‎‎‏‎"</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_title_computer" msgid="4671071173916176037">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎Google Play services‎‏‎‎‏‎"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is requesting permission on behalf of your ‎‏‎‎‏‏‎<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ to access your phone’s photos, media, and notifications‎‏‎‎‏‎"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to take this action?‎‏‎‎‏‎"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt; to stream your phone’s apps and system features?‎‏‎‎‏‎"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎%1$s will have access to anything that’s visible or played on your phone, including audio, photos, payment info, passwords, and messages.&lt;br/&gt;&lt;br/&gt;%1$s will be able to stream apps and system features until you remove access to this permission.‎‏‎‎‏‎"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎<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 stream apps and other system features to nearby devices‎‏‎‎‏‎"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‎Allow &lt;strong&gt;‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>‎‏‎‎‏‏‏‎&lt;/strong&gt;?‎‏‎‎‏‎"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‏‏‎<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 and system features to ‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‎‏‎‎‏‏‏‎ until you remove access to this permission.‎‏‎‎‏‎"</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎<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 stream apps and other system features between your devices‎‏‎‎‏‎"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎Change media output‎‏‎‎‏‎"</string>
     <string name="permission_storage" msgid="6831099350839392343">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‎Photos and media‎‏‎‎‏‎"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‎Notifications‎‏‎‎‏‎"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎Apps‎‏‎‎‏‎"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‏‎Streaming‎‏‎‎‏‎"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎Make and manage phone calls‎‏‎‎‏‎"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎Read and write phone call log‎‏‎‎‏‎"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎Send and view SMS messages‎‏‎‎‏‎"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎Find, connect to, and determine the relative position of nearby devices‎‏‎‎‏‎"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎Read all notifications, including information like contacts, messages, and photos‎‏‎‎‏‎"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎• Read all notifications, including info like contacts, messages, and photos&lt;br/&gt;• Send notifications&lt;br/&gt;&lt;br/&gt;You can manage this app\'s ability to read and send notifications anytime in Settings &gt; Notifications.‎‏‎‎‏‎"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎Stream your phone’s apps‎‏‎‎‏‎"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‎Stream apps and other system features from your phone‎‏‎‎‏‎"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎Access a list of available devices and control which one streams or casts audio or video from other apps‎‏‎‎‏‎"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎phone‎‏‎‎‏‎"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‎‎tablet‎‏‎‎‏‎"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎tv‎‏‎‎‏‎"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎device‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 5ae0c2b..4df8365 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Elige un dispositivo para que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; lo administre"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Esta app podrá sincronizar información, como el nombre de alguien cuando te llame, y acceder a los siguientes permisos en tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Esta app podrá sincronizar información, como el nombre de alguien cuando te llame, y acceder a los siguientes permisos en tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Esta app podrá acceder a los siguientes permisos en tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita apps del teléfono?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s tendrá acceso a todo el contenido visible o que se reproduzca en el teléfono, lo que incluye audio, fotos, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá transmitir apps, a menos que se quite el acceso a este permiso."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para transmitir apps entre dispositivos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar y transmitir apps entre dispositivos"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita las apps de <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="1274464413649731829">"<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_TYPE">%2$s</xliff:g>, lo que incluye audio, fotos, 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="1944832605534698562">"<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 mostrar y transmitir apps entre dispositivos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permite que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</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_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu teléfono"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"¿Permites que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; realice esta acción?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; transmita funciones del sistema y apps del teléfono?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s tendrá acceso a todo el contenido visible o que se reproduzca en tu teléfono, lo que incluye audio, fotos, información de pago, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá transmitir apps y funciones del sistema, a menos que se quite el acceso a este permiso."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps y otras funciones del sistema a dispositivos cercanos"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"¿Quieres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; transmita las apps y las funciones del sistema de <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> a &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 y funciones del sistema a <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que se quite el acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 transmitir apps y otras funciones del sistema entre dispositivos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar la salida multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmisión"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y administrar llamadas telefónicas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir el registro de llamadas telefónicas"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar dispositivos cercanos, conectarse a ellos y determinar su posición relativa"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluso con información como contactos, mensajes y fotos&lt;br/&gt;• Enviar notificaciones&lt;br/&gt;&lt;br/&gt;Puedes administrar la capacidad de esta app para leer y enviar notificaciones en cualquier momento en Configuración &gt; Notificaciones."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmitir las apps de tu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmite apps y otras funciones del sistema desde tu teléfono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accede a una lista de dispositivos disponibles y controla el que transmite audio o video de otras apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"teléfono"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index cdabc6c..f929d24 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Elige un dispositivo para que lo gestione &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Elige el <xliff:g id="PROFILE_NAME">%1$s</xliff:g> que quieras configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Esta aplicación podrá sincronizar información, como el nombre de la persona que llama, y acceder a estos permisos de tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Esta aplicación podrá sincronizar información, como el nombre de la persona que llama, y acceder a estos permisos de tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Esta aplicación podrá acceder a estos permisos de tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"¿Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita las aplicaciones de tu dispositivo?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s tendrá acceso a todo lo que sea visible o se reproduzca en el teléfono, incluidos audio, fotos, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá emitir aplicaciones hasta que quites el acceso a este permiso."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar y emitir aplicaciones en otros dispositivos tuyos"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"¿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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<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 mostrar y emitir aplicaciones en otros dispositivos tuyos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información de tu teléfono"</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_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acceder a las fotos, los archivos multimedia y las notificaciones de tu teléfono"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"¿Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; realice esta acción?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"¿Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; emita las aplicaciones y funciones del sistema de tu dispositivo?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s tendrá acceso a todo lo que sea visible o se reproduzca en el teléfono, incluidos audio, fotos, información para pagos, contraseñas y mensajes.&lt;br/&gt;&lt;br/&gt;%1$s podrá emitir aplicaciones y funciones del sistema hasta que quites el acceso a este permiso."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 emitir aplicaciones y otras funciones del sistema en dispositivos cercanos"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"¿Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 y funciones del sistema en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hasta que quites el acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 emitir aplicaciones y otras funciones del sistema en otros dispositivos tuyos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar salida multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos y elementos multimedia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificaciones"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicaciones"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Emitir"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Hacer y gestionar llamadas telefónicas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Leer y escribir en el registro de llamadas del teléfono"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar y ver mensajes SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Buscar, conectarse y determinar la posición relativa de dispositivos cercanos"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Leer todas las notificaciones, incluida información como contactos, mensajes y fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Leer todas las notificaciones, incluida información como contactos, mensajes y fotos&lt;br/&gt;• Enviar notificaciones&lt;br/&gt;&lt;br/&gt;Puedes gestionar los permisos de esta aplicación para leer y enviar notificaciones cuando quieras en Ajustes &gt; Notificaciones."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Muestra en streaming las aplicaciones de tu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite aplicaciones y otras funciones del sistema desde tu teléfono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accede a una lista de dispositivos disponibles y controla cuál transmite o envía audio o vídeo desde otras aplicaciones"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"teléfono"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televisión"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index e02cb04..5fcadcc 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Valige seade, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Valige <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mis seadistada"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Sellel rakendusel lubatakse sünkroonida teavet (nt helistaja nime) ja antakse need load teie seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Sellel rakendusel lubatakse sünkroonida teavet (nt helistaja nime) ja antakse juurdepääs nendele lubadele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Sellele rakendusele antakse need load teie seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pääseda teie telefonis juurde sellele teabele"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Kas lubate seadmel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; oma telefoni rakendusi voogesitada?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s saab juurdepääsu kõigele, mis on telefonis nähtaval või esitatav, sh helile, fotodele, paroolidele ja sõnumitele.&lt;br/&gt;&lt;br/&gt;%1$s saab rakendusi voogesitada kuni eemaldate juurdepääsu sellele loale."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Seadmeülesed teenused"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi kuvada ja voogesitada"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Kas lubate rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; voogesitada seadmes &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; rakendusi, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
+    <string name="summary_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> saab kuvada või esitada sh heli, fotod, paroolid ja sõnumid.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> saab voogesitada rakendusi ja süsteemifunktsioone seadmes <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, kuni eemaldate juurdepääsu sellele loale."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi kuvada ja voogesitada"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Lubage rakendusel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pääseda teie telefonis juurde sellele teabele"</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_title_computer" msgid="4671071173916176037">"Google Play teenused"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba pääseda juurde telefoni fotodele, meediale ja märguannetele"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Kas lubada seadmel &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; teha seda toimingut?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Kas lubate seadmel &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; oma telefoni rakendusi ja süsteemifunktsioone voogesitada?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s saab juurdepääsu kõigele, mis on teie telefonis nähtav või esitatav, sh heli, fotod, makseteave, paroolid ja sõnumid.&lt;br/&gt;&lt;br/&gt;%1$s saab voogesitada rakendusi ja süsteemifunktsioone, kuni eemaldate juurdepääsu sellele loale."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba voogesitada rakendusi ja muid süsteemi funktsioone läheduses olevatesse seadmetesse"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Kas lubate rakendusel &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; voogesitada seadmes &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; rakendusi ja süsteemifunktsioone, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> saab kuvada või esitada, sh heli, fotod, makseteave, paroolid ja sõnumid.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> saab voogesitada rakendusi ja süsteemifunktsioone seadmes <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, kuni eemaldate juurdepääsu sellele loale."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba voogesitada teie seadmete vahel rakendusi ja muid süsteemifunktsioone"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Meediaväljundi muutmine"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Märguanded"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Rakendused"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Voogesitus"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Helistamine ja telefonikõnede haldamine"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoni kõnelogi lugemine ja kirjutamine"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-sõnumite saatmine ja vaatamine"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Läheduses olevate seadmete leidmine, nendega ühenduse loomine ja nende suhtelise asendi määramine"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kõikide märguannete, sh sellise teabe nagu kontaktid, sõnumid ja fotod lugemine&lt;br/&gt;• Märguannete saatmine&lt;br/&gt;&lt;br/&gt;Saate selle rakenduse võimalusi märguannete lugemiseks ja saatmiseks igal ajal hallata jaotises Seaded &gt; Märguanded."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefoni rakenduste voogesitamine"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Rakenduste ja muude süsteemi funktsioonide voogesitamine teie telefonist"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Juurdepääs saadaolevate seadmete loendile ja juhtida, milline neist voogesitab või edastab heli või videot teistest rakendustest"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tahvelarvuti"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"teler"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"seade"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index ca84970..46da125 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu behar duen gailua"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Aukeratu konfiguratu nahi duzun <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Informazioa sinkronizatu (esate baterako, deitzaileen izenak) eta baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Informazioa sinkronizatzeko (esate baterako, deitzaileen izenak) eta hauetarako baimenak izango ditu aplikazioak <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> erabiltzean:"</string>
     <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="2872254734959842579">"Baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak:"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gailuari zure telefonoko aplikazioak zuzenean igortzeko baimena eman nahi diozu?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s gailuak telefonoan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, pasahitzak eta mezuak barne.&lt;br/&gt;&lt;br/&gt;%1$s aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailuarteko zerbitzuak"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikazioak gailuen artean bistaratzeko eta zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Baimen hauek izango ditu aplikazioak <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> erabiltzean:"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"Aplikazioak gailuen artean bistaratzeko eta 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="4693714143506569253">"Eman telefonoko informazio hau erabiltzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</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_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak erabiltzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ekintza hau gauzatzeko baimena eman nahi diozu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; gailuari zure telefonoko aplikazioak eta sistemaren eginbideak zuzenean igortzeko baimena eman nahi diozu?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s gailuak telefonoan 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;%1$s aplikazioak eta sistemaren eginbideak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikazioak eta sistemaren beste eginbide batzuk inguruko gailuetara 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="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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g> gailura zuzenean igortzeko baimena eman nahi diozu?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 eta sistemaren eginbideak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"Aplikazioak eta sistemaren beste eginbide batzuk zure gailuen artean 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Aldatu multimedia-irteera"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Jakinarazpenak"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikazioak"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Igortzea"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Telefono-deiak egin eta kudeatu"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefonoko deien erregistroa irakurri eta bertan idatzi"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mezuak bidali eta ikusi"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Inguruko gailuak aurkitu, haietara konektatu eta haien posizio erlatiboa zehaztu"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Jakinarazpen guztiak irakurri; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa.&lt;br/&gt;• Jakinarazpenak bidali.&lt;br/&gt;&lt;br/&gt;Aplikazioak jakinarazpenak irakurri eta bidaltzeko dituen baimenak kudeatzeko, joan Ezarpenak &gt; Jakinarazpenak atalera."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Igorri zuzenean telefonoko aplikazioak"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Igorri aplikazioak eta sistemaren beste eginbide batzuk telefonotik"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Erabilgarri dauden gailuen zerrenda atzitu eta beste aplikazio batzuen bidez audioa edo bideoa zuzenean erreproduzitzen edo igortzen zein gailu ari den kontrolatu"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Telefonoa"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"Tableta"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"telebista"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"gailua"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index b3432ed..6a651d9 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"‏انتخاب دستگاه برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای راه‌اندازی"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"به این برنامه اجازه داده می‌شود اطلاعاتی مثل نام تماس‌گیرنده را همگام‌سازی کند و به این اجازه‌ها در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> شما دسترسی داشته باشد"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"به این برنامه اجازه داده می‌شود اطلاعاتی مثل نام تماس‌گیرنده را همگام‌سازی کند و به این اجازه‌ها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی داشته باشد"</string>
     <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="2872254734959842579">"این برنامه مجاز می‌شود به این اجازه‌ها در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> شما دسترسی پیدا کند"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"‏اجازه دادن به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای دسترسی به اطلاعات تلفن"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"‏به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه می‌دهید برنامه‌های تلفنتان را جاری‌سازی کند؟"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"‏‫%1$s به هرچیزی که در تلفن نمایان است یا پخش می‌شود، ازجمله صداها، عکس‌ها، گذرواژه‌ها، و پیام‌ها دسترسی خواهد داشت.&lt;br/&gt;&lt;br/&gt;تا زمانی که دسترسی به این اجازه را حذف نکنید، %1$s می‌تواند برنامه‌ها را جاری‌سازی کند."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"سرویس‌های بین‌دستگاهی"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> اجازه می‌خواهد برنامه‌ها را بین دستگاه‌های شما جاری‌سازی کند"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> برای نمایش و جاری‌سازی برنامه‌ها بین دستگاه‌های شما اجازه می‌خواهد"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"این برنامه قادر خواهد بود به این اجازه‌ها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی پیدا کند"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"‏به &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="1274464413649731829">"‏‫<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="1944832605534698562">"‫<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="4693714143506569253">"‏به &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه دسترسی به این اطلاعات در دستگاهتان داده شود"</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_title_computer" msgid="4671071173916176037">"‏خدمات Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> اجازه می‌خواهد به عکس‌ها، رسانه‌ها، و اعلان‌های تلفن شما دسترسی پیدا کند"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"‏به &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه داده شود این اقدام را انجام دهد؟"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"‏به &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; اجازه می‌دهید برنامه‌های تلفنتان و ویژگی‌های سیستم را جاری‌سازی کند؟"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"‏‫%1$s به هرچیزی که در تلفن نمایان است یا پخش می‌شود، ازجمله صداها، عکس‌ها، اطلاعات پرداخت، گذرواژه‌ها، و پیام‌ها دسترسی خواهد داشت.&lt;br/&gt;&lt;br/&gt;تا زمانی که دسترسی به این اجازه را حذف نکنید، %1$s می‌تواند برنامه‌ها و ویژگی‌های سیستم را جاری‌سازی کند."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه می‌خواهد تا برنامه‌ها و دیگر ویژگی‌های سیستم را در دستگاه‌های اطراف جاری‌سازی کند."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"‏به &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; اجازه می‌دهید برنامه‌ها و ویژگی‌های سیستم <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> را در &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; جاری‌سازی کند؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"‏‫<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_nearby_device_streaming" msgid="8509848562931818793">"‫<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"تغییر دادن خروجی رسانه"</string>
     <string name="permission_storage" msgid="6831099350839392343">"عکس‌ها و رسانه‌ها"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"اعلان‌ها"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"برنامه‌ها"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"جاری‌سازی"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"برقراری و مدیریت تماس‌های تلفنی"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"خواندن و نوشتن گزارش تماس تلفنی"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"ارسال و مشاهده پیامک‌ها"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"پیدا کردن، تعیین موقعیت نسبی، و متصل شدن به دستگاه‌های اطراف"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"خواندن همه اعلان‌ها، شامل اطلاعاتی مثل مخاطبین، پیام‌ها، و عکس‌ها"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• خواندن همه اعلان‌ها، شامل اطلاعاتی مثل مخاطبین، پیام‌ها، و عکس‌ها&lt;br/&gt;• ارسال اعلان&lt;br/&gt;&lt;br/&gt;هرزمان بخواهید می‌توانید این توانایی برنامه برای خواندن و ارسال اعلان را در «تنظیمات > اعلان‌ها» مدیریت کنید."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"جاری‌سازی برنامه‌های تلفن"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"برنامه‌ها و دیگر ویژگی‌های سیستم را از تلفن شما جاری‌سازی می‌کند"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"دسترسی به فهرست دستگاه‌های دردسترس و کنترل اینکه کدام دستگاه صدا یا ویدیو را از برنامه‌های دیگر جاری‌سازی یا پخش کند"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"تلفن"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"رایانه لوحی"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"تلویزیون"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"دستگاه"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index 0671410..5626f0a 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"kello"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Valitse laite, jota &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, niin voit suorittaa käyttöönoton"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Sovellus saa luvan synkronoida tietoja (esimerkiksi soittajan nimen) ja pääsyn näihin lupiin laitteella (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Sovellus saa luvan synkronoida tietoja (esimerkiksi soittajan nimen) ja pääsyn näihin lupiin laitteella (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
     <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="2872254734959842579">"Tämä sovellus saa käyttää näitä lupia laitteella (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Salli, että &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; saa pääsyn näihin puhelimesi tietoihin"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Saako &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; striimata puhelimen sovelluksia?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s saa pääsyn kaikkeen puhelimessa näkyvään tai pelattavaan, mukaan lukien audioon, kuviin, salasanoihin ja viesteihin.&lt;br/&gt;&lt;br/&gt;%1$s voi striimata sovelluksia, kunnes poistat luvan."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Laitteidenväliset palvelut"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia laitteidesi välillä"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) puolesta lupaa näyttää ja striimata sovelluksia laitteidesi välillä"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Sovellus saa käyttää näitä lupia <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa näyttää ja striimata sovelluksia laitteidesi välillä"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Salli pääsy tähän tietoon puhelimellasi: &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</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_title_computer" msgid="4671071173916176037">"Google Play Palvelut"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) puolesta lupaa päästä puhelimesi kuviin, mediaan ja ilmoituksiin"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Sallitko, että &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; voi suorittaa tämän toiminnon?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Saako &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; striimata puhelimen sovelluksia ja järjestelmäominaisuuksia?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s saa pääsyn kaikkeen puhelimessa näkyvään tai pelattavaan, mukaan lukien audioon, kuviin, maksutietoihin, salasanoihin ja viesteihin.&lt;br/&gt;&lt;br/&gt;%1$s voi striimata sovelluksia ja järjestelmäominaisuuksia, kunnes poistat luvan."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia ja muita järjestelmän ominaisuuksia lähellä oleviin laitteisiin."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Saako &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;)?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 ja järjestelmäominaisuuksia laitteelle (<xliff:g id="DEVICE_NAME">%3$s</xliff:g>), kunnes poistat luvan."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia ja muita järjestelmän ominaisuuksia laitteidesi välillä"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Median ulostulon muuttaminen"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Kuvat ja media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Ilmoitukset"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Sovellukset"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striimaus"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"soittaa ja hallinnoida puheluita"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"lukea puhelulokia ja kirjoittaa siihen"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"lähettää ja lukea tekstiviestejä"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"löytää lähellä olevia laitteita, muodostaa niihin yhteyden ja määrittää niiden suhteellisen sijainnin"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• lukea kaikkia ilmoituksia, esim. yhteystietoihin, viesteihin ja kuviin liittyviä tietoja&lt;br/&gt;• lähettää ilmoituksia.&lt;br/&gt;&lt;br/&gt;Voit milloin tahansa valita, saako sovellus lukea ja lähettää ilmoituksia, avaamalla Asetukset &gt; Ilmoitukset."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Striimaa puhelimen sovelluksia"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Striimaa sovelluksia ja muita järjestelmän ominaisuuksia puhelimesta"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Näkee saatavilla olevat laitteet ja voi ohjata sitä, millä niistä striimataan muiden sovellusten audiota tai videota"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"puhelin"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tabletti"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV:llä"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"laitteella"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 5b58961..61a8b73 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choisir un appareil qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Choisir un appareil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pour le configurer"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Cette appli sera autorisée à synchroniser des informations, comme le nom de l\'appelant, et à accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Cette appli sera autorisée à synchroniser des informations, comme le nom de l\'appelant, et à accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Cette appli pourra accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Autorisez &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations à partir de votre téléphone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à diffuser les applis de votre téléphone?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s aura accès à tout ce qui est visible ou lu sur le téléphone, y compris l\'audio, les photos, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;%1$s pourra diffuser des applis jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services multiappareils"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour diffuser des applis entre vos appareils"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation, au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, d\'afficher et de diffuser des applis entre vos appareils"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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 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="1944832605534698562">"<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 afficher et diffuser des applis entre vos appareils"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Autorisez &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations à partir de votre téléphone"</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_title_computer" msgid="4671071173916176037">"Services Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour accéder aux photos, aux fichiers multimédias et aux notifications de votre téléphone"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à effectuer cette action?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à diffuser les applis et les fonctionnalités du système de votre téléphone?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s aura accès à tout ce qui est visible ou lu sur le téléphone, y compris l\'audio, les photos, les renseignements de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;%1$s pourra diffuser des applis jusqu\'à ce que vous retiriez l\'accès à cette autorisation."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 d\'autres fonctionnalités du système sur des appareils à proximité"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 et des fonctionnalités du système 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_nearby_device_streaming" msgid="8509848562931818793">"<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 diffuser des applis et d\'autres fonctionnalités du système entre vos appareils"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos et fichiers multimédias"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Applis"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Diffusion en continu"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Passez et gérez des appels téléphoniques"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lisez et écrivez le journal d\'appels téléphoniques"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyez et affichez des messages texte"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouvez et déterminez la position relative des appareils à proximité, et connectez-vous à ceux-ci"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lisez toutes les notifications, y compris les renseignements tels que les contacts, les messages et les photos&lt;br/&gt;• Envoyez des notifications&lt;br/&gt;&lt;br/&gt;Vous pouvez gérer la capacité de cette appli à lire et à envoyer des notifications à tout moment dans Paramètres > Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffusez les applis de votre téléphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applis et d\'autres fonctionnalités du système à partir de votre téléphone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accédez à une liste d\'appareils accessibles et contrôlez ceux qui diffusent du contenu audio ou vidéo à partir d\'autres applis"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"téléphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablette"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"télévision"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"appareil"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index 7015c50..b319364 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"montre"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Sélectionner l\'appareil qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Sélectionner votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g> à configurer"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Cette appli sera autorisée à synchroniser des infos (comme le nom de l\'appelant) et disposera de ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Cette appli sera autorisée à synchroniser des infos (comme le nom de l\'appelant) et disposera de ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Cette appli sera autorisée à accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations depuis votre téléphone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à caster les applications de votre téléphone ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s aura accès à tout ce qui est visible ou lu sur le téléphone, y compris les contenus audio, les photos, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;%1$s pourra caster des applications jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services inter-appareils"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour caster des applis d\'un appareil à l\'autre"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour afficher et caster des applications d\'un appareil à l\'autre"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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 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="1944832605534698562">"<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 afficher et caster des applis d\'un appareil à l\'autre"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Autoriser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; à accéder à ces informations depuis votre téléphone"</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_title_computer" msgid="4671071173916176037">"Services Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour accéder aux photos, contenus multimédias et notifications de votre téléphone"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à effectuer cette action ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; à streamer les applications et les fonctionnalités système de votre téléphone ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s aura accès à tout ce qui est visible ou lu sur le téléphone, y compris les contenus audio, les photos, les informations de paiement, les mots de passe et les messages.&lt;br/&gt;&lt;br/&gt;%1$s pourra caster des applications et des fonctionnalités système jusqu\'à ce que vous supprimiez l\'accès à cette autorisation."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 d\'autres fonctionnalités système en streaming sur des appareils à proximité"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Autoriser &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 et des fonctionnalités système 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_nearby_device_streaming" msgid="8509848562931818793">"<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 caster des applis et d\'autres fonctionnalités système d\'un appareil à l\'autre"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Modifier la sortie multimédia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Photos et contenus multimédias"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifications"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Applis"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Effectuer et gérer des appels téléphoniques"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Consulter et créer les journaux d\'appels du téléphone"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Envoyer et consulter des SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trouver les appareils à proximité, s\'y connecter et déterminer leur position relative"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lire toutes les notifications, y compris des informations comme les contacts, messages et photos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lire toutes les notifications, y compris les informations comme les contacts, les messages et les photos&lt;br/&gt;• Envoyer des notifications&lt;br/&gt;&lt;br/&gt;Vous pouvez gérer les autorisations de lecture et d\'envoi des notifications à tout moment dans Paramètres &gt; Notifications."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Diffuser en streaming les applis de votre téléphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Diffusez des applis et d\'autres fonctionnalités système en streaming depuis votre téléphone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accédez à une liste d\'appareils disponibles et choisissez lequel caste ou lit en streaming une piste audio ou une vidéo à partir d\'autres applis."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"téléphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablette"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"appareil"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index da7d4a2..ccab521 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolle un dispositivo para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Escolle o perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) que queiras configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
     <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="2872254734959842579">"Esta aplicación poderá acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información desde o teu teléfono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Queres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; emita as aplicacións do teu teléfono?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s terá acceso a todo o que sexa visible ou se reproduza no teléfono, como audio, fotos, contrasinais e mensaxes.&lt;br/&gt;&lt;br/&gt;%1$s poderá emitir aplicacións ata que quites o acceso a este permiso."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizos multidispositivo"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) para emitir contido de aplicacións entre os teus aparellos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) para mostrar e emitir aplicacións noutros dispositivos teus"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), como audio, fotos, 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="1944832605534698562">"<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 mostrar e emitir aplicacións entre dispositivos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acceda a esta información do teu teléfono"</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_title_computer" msgid="4671071173916176037">"Servizos de Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) para acceder ás fotos, ao contido multimedia e ás notificacións do teléfono"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Queres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; leve a cabo esta acción?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Queres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; emita as aplicacións e as funcións do sistema do teu teléfono?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s terá acceso a todo o que sexa visible ou se reproduza no teléfono, como audio, fotos, información de pago, contrasinais e mensaxes.&lt;br/&gt;&lt;br/&gt;%1$s poderá emitir aplicacións e funcións do sistema ata que quites o acceso a este permiso."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 emitir o contido das aplicacións e doutras funcións do sistema en dispositivos próximos"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Queres permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), como audio, fotos, contrasinais e mensaxes.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá emitir aplicacións e outras funcións do sistema en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ata que quites o acceso a este permiso."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 emitir aplicacións e outras funcións do sistema entre dispositivos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambiar a saída multimedia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e contido multimedia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificacións"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicacións"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Reprodución en tempo real"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Fai e xestiona chamadas telefónicas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Le e edita o rexistro de chamadas do teléfono"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Envía e consulta mensaxes SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Busca dispositivos próximos, establece conexión con eles e determina a súa posición relativa"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Le todas as notificacións (que poden incluír información como contactos, mensaxes e fotos)&lt;br/&gt;• Envía notificacións&lt;br/&gt;&lt;br/&gt;En Configuración &gt; Notificacións podes xestionar en calquera momento a capacidade desta aplicación para ler e enviar notificacións."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Emite as aplicacións do teu teléfono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emite o contido das aplicacións e doutras funcións do sistema desde o teléfono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accede a unha lista de dispositivos dispoñibles e controla cal deles reproduce en tempo real ou emite audio ou vídeo desde outras aplicacións"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"teléfono"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tableta"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televisión"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index e34cf9a..816717b 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ ડિવાઇસ પસંદ કરો"</string>
     <string name="chooser_title" msgid="2235819929238267637">"સેટઅપ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"આ ઍપને, કૉલ કરનાર વ્યક્તિનું નામ જેવી માહિતી સિંક કરવાની અને તમારા <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"આ ઍપને, કૉલ કરનાર વ્યક્તિનું નામ જેવી માહિતી સિંક કરવાની અને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે"</string>
     <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="2872254734959842579">"આ ઍપને તમારા <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂરી આપો"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"શું &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને તમારા ફોનની ઍપને સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$sની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે ફોન પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, પાસવર્ડ અને મેસેજ શામેલ છે.&lt;br/&gt;&lt;br/&gt;%1$s ત્યાં સુધી ઍપને સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ક્રોસ-ડિવાઇસ સેવાઓ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> વતી તમારા ડિવાઇસ વચ્ચે ઍપ સ્ટ્રીમ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"તમારા એક ડિવાઇસ પરથી બીજા ડિવાઇસ પર ઍપને ડિસ્પ્લે તેમજ સ્ટ્રીમ કરવા માટે, <xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> વતી પરવાનગી માગી રહી છે"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"આ ઍપને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"શું &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="1274464413649731829">"<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="1944832605534698562">"તમારા એક ડિવાઇસ પરથી બીજા ડિવાઇસ પર ઍપને ડિસ્પ્લે તેમજ સ્ટ્રીમ કરવા માટે, <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="4693714143506569253">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ને મંજૂરી આપો"</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_title_computer" msgid="4671071173916176037">"Google Play સેવાઓ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> વતી તમારા ફોનના ફોટા, મીડિયા અને નોટિફિકેશન ઍક્સેસ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ને આ પગલું ભરવાની મંજૂરી આપીએ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"શું &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ને તમારા ફોનની ઍપ અને સિસ્ટમની સુવિધાઓને સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$sની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે તમારા ફોન પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, ચુકવણીની માહિતી, પાસવર્ડ અને મેસેજ શામેલ છે.&lt;br/&gt;&lt;br/&gt;%1$s ત્યાં સુધી ઍપ અને સિસ્ટમની સુવિધાઓને સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> નજીકના ડિવાઇસ પર ઍપ અને સિસ્ટમની અન્ય સુવિધાઓ સ્ટ્રીમ કરવા તમારા <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"શું &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપ અને સિસ્ટમની સુવિધાઓને &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"મીડિયા આઉટપુટ બદલો"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ફોટા અને મીડિયા"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"નોટિફિકેશન"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ઍપ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"સ્ટ્રીમિંગ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ફોન કૉલ કરવાની અને મેનેજ કરવાની"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ફોન કૉલ લૉગ વાંચવાની અને લખવાની"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS મેસેજ મોકલવાની અને જોવાની"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"નજીકના ડિવાઇસ શોધવાની, તેમની સાથે કનેક્ટ કરવાની અને તેમની સંબંધિત સ્થિતિ નિર્ધારિત કરવાની"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"સંપર્કો, મેસેજ અને ફોટા જેવી માહિતી સહિતના બધા નોટિફિકેશન વાંચવાની"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• સંપર્કો, મેસેજ અને ફોટા જેવી માહિતી સહિતના બધા નોટિફિકેશન વાંચવાની&lt;br/&gt;• નોટિફિકેશન મોકલવાની&lt;br/&gt;&lt;br/&gt;તમે કોઈપણ સમયે સેટિંગમાં &gt; નોટિફિકેશનમાં જઈને આ ઍપની નોટિફિકેશન વાંચવાની અને મોકલવાની ક્ષમતાને મેનેજ કરી શકો છો."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"તમારા ફોનની ઍપ સ્ટ્રીમ કરો"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"તમારા ફોન પરથી ઍપ અને સિસ્ટમની અન્ય સુવિધાઓ સ્ટ્રીમ કરો"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ઉપલબ્ધ ડિવાઇસની સૂચિ ઍક્સેસ કરો અને અન્ય ઍપમાંથી ઑડિયો કે વીડિયો ક્યા ડિવાઇસ દ્વારા સ્ટ્રીમ કે કાસ્ટ કરવામાં આવે તેનું નિયંત્રણ કરો"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ફોન"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ટૅબ્લેટ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ટીવી"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ડિવાઇસ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 92d94f3..b5a40e95f 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; से मैनेज किया जाने वाला डिवाइस चुनें"</string>
     <string name="chooser_title" msgid="2235819929238267637">"सेट अप करने के लिए कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस करने के साथ-साथ कॉल करने वाले व्यक्ति के नाम जैसी जानकारी सिंक कर पाएगा"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस करने के साथ-साथ कॉल करने वाले व्यक्ति के नाम जैसी जानकारी सिंक कर पाएगा"</string>
     <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="2872254734959842579">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को आपके फ़ोन के ऐप्लिकेशन स्ट्रीम करने की अनुमति देनी है?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चल रहा हो. जैसे, ऑडियो, फ़ोटो, पासवर्ड, और मैसेज.&lt;br/&gt;&lt;br/&gt;%1$s तब तक ऐप्लिकेशन स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपके डिवाइसों के बीच ऐप्लिकेशन स्ट्रीम करने की अनुमति मांग रहा है"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, एक डिवाइस के ऐप्लिकेशन, दूसरे डिवाइस पर दिखाने और स्ट्रीम करने की अनुमति मांग रहा है"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"क्या &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="1274464413649731829">"<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="1944832605534698562">"आपके <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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपने फ़ोन में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रहा है"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"क्या &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; को यह कार्रवाई करने की अनुमति देनी है?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; को आपके फ़ोन के ऐप्लिकेशन और सिस्टम की सुविधाएं स्ट्रीम करने की अनुमति देनी है?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s के पास ऐसे कॉन्टेंट का ऐक्सेस होगा जो फ़ोन पर दिख रहा हो या चल रहा हो. जैसे, ऑडियो, फ़ोटो, पेमेंट की जानकारी, पासवर्ड, और मैसेज.&lt;br/&gt;&lt;br/&gt;%1$s तब तक ऐप्लिकेशन और सिस्टम की सुविधाओं को स्ट्रीम करेगा, जब तक आप इस अनुमति को हटा न दें."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, ऐप्लिकेशन और दूसरे सिस्टम की सुविधाओं को आस-पास मौजूद डिवाइसों पर स्ट्रीम करने की अनुमति मांग रहा है"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"क्या &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> के ऐप्लिकेशन और सिस्टम की सुविधाओं को &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; पर स्ट्रीम करने की अनुमति देनी है?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"आपके <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट में बदलाव करे"</string>
     <string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"सूचनाएं"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ऐप्लिकेशन"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रीमिंग"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"फ़ोन कॉल करने और उन्हें मैनेज करने की अनुमति दें"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"कॉल लॉग की जानकारी देखने और उसमें बदलाव करने की अनुमति दें"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"एसएमएस (मैसेज) भेजने और देखने की अनुमति दें"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"आस-पास मौजूद डिवाइसों को खोजने, उनसे कनेक्ट करने, और उनकी जगह की जानकारी का पता लगाने की अनुमति दें"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"सभी सूचनाएं देखने की अनुमति दें. जैसे, संपर्क, मैसेज, और फ़ोटो की जानकारी"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• सभी सूचनाएं देखने की अनुमति दें. जैसे, संपर्क, मैसेज, और फ़ोटो की जानकारी&lt;br/&gt;• सूचनाएं भेजने की अनुमति दें&lt;br/&gt;&lt;br/&gt;सेटिंग &gt; सूचनाएं में जाकर इस ऐप्लिकेशन के लिए, सूचनाओं को देखने और उनमें बदलाव करने की अनुमति में कभी भी बदलाव किया जा सकता है."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"अपने फ़ोन पर मौजूद ऐप्लिकेशन स्ट्रीम करें"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"अपने फ़ोन से ऐप्लिकेशन और सिस्टम की दूसरी सुविधाओं को स्ट्रीम करें"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"उपलब्ध डिवाइसों की लिस्ट देखें. साथ ही, कंट्रोल करें कि कौनसा डिवाइस, स्ट्रीम कर सकता है या दूसरे ऐप्लिकेशन से ऑडियो या वीडियो कास्ट कर सकता है"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"फ़ोन"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"टैबलेट"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"टीवी"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"डिवाइस"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml
index cc17655..2d94b67 100644
--- a/packages/CompanionDeviceManager/res/values-hr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"satom"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> koji želite postaviti"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aplikacija će moći sinkronizirati podatke kao što je ime pozivatelja i pristupiti tim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aplikacija će moći sinkronizirati podatke kao što je ime pozivatelja i pristupiti tim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Aplikacija će moći pristupati ovim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Omogućite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa informacijama s vašeg telefona"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Želite li dopustiti aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da streama aplikacije vašeg telefona?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"Aplikacija %1$s imat će pristup svemu što je vidljivo ili se reproducira na telefonu, uključujući zvuk, fotografije, zaporke i poruke.&lt;br/&gt;&lt;br/&gt;Aplikacija %1$s moći će streamati aplikacije dok ne uklonite pristup za to dopuštenje."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na različitim uređajima"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za stream aplikacija s jednog uređaja na drugi"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za prikaz i streaming aplikacija s jednog uređaja na drugi"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Ž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_app_streaming" msgid="1274464413649731829">"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, 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="1944832605534698562">"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 prikaz i streaming aplikacija s jednog uređaja na drugi"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Omogućite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da pristupa informacijama s vašeg telefona"</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_title_computer" msgid="4671071173916176037">"Usluge za Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za pristup fotografijama, medijskim sadržajima i obavijestima na telefonu"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Želite li uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dopustiti da izvrši tu radnju?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Želite li dopustiti uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; da streama aplikacije i značajke sustava vašeg telefona?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Aplikacija %1$s imat će pristup svemu što je vidljivo ili se reproducira na telefonu, uključujući zvuk, fotografije, podatke o plaćanju, zaporke i poruke.&lt;br/&gt;&lt;br/&gt;Aplikacija %1$s moći će streamati aplikacije i značajke sustava dok ne uklonite pristup za to dopuštenje."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"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 emitiranje aplikacija i drugih značajki sustava na uređajima u blizini"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Želite li dopustiti uređaju &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> imat će pristup svemu što je vidljivo ili se reproducira na vašem uređaju <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, uključujući zvuk, fotografije, informacije 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 i značajke sustava 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_nearby_device_streaming" msgid="8509848562931818793">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtjeva dopuštenja u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za stream aplikacija i drugih značajki sustava između vaših uređaja"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Promjena medijskog izlaza"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Obavijesti"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Uspostavljanje telefonskih poziva i upravljanje njima"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čitanje i pisanje zapisnika poziva telefona"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Slanje i pregledavanje SMS poruka"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Pronalaženje i određivanje relativnog položaja uređaja u blizini i povezivanje s njima"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čitanje svih obavijesti, uključujući podatke kao što su kontakti, poruke i fotografije&lt;br/&gt;• Slanje obavijesti&lt;br/&gt;&lt;br/&gt;Uvijek možete upravljati mogućnostima ove aplikacije da čita i šalje obavijesti u postavkama i obavijestima"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikacija vašeg telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Emitirajte stream aplikacija i drugih značajki sustava s vašeg telefona"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Pristupite popisu dostupnih uređaja i upravljajte time koji streama ili emitira zvuk ili videozapise iz drugih aplikacija"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefonu"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tabletu"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"uređaj"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml
index c929ee4..b63f8ab 100644
--- a/packages/CompanionDeviceManager/res/values-hu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"óra"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"A(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; alkalmazással kezelni kívánt eszköz kiválasztása"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Válassza ki a beállítani kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> nevet."</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Ez az alkalmazás képes lesz szinkronizálni információkat (például a hívó fél nevét), és hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközén"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Ez az alkalmazás képes lesz szinkronizálni információkat (például a hívó fél nevét), és hozzáférhet majd ezekhez az engedélyekhez a következőn: <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Az alkalmazás hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközén"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; számára a telefonja alkalmazásainak streamelését?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"A(z) %1$s hozzáférhet mindenhez, ami a telefonon látható vagy lejátszható, így az audiotartalomhoz, fényképekhez, 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) %1$s képes lesz majd az alkalmazások streamelésére."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Többeszközös szolgáltatások"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nevében az alkalmazások eszközök közötti streameléséhez"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nevében az alkalmazások eszközök közötti megjelenítéséhez és streameléséhez."</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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, 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 és a rendszerfunkciók <xliff:g id="DEVICE_NAME">%3$s</xliff:g> eszközre való streamelésére."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"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 eszközök közötti megjelenítéséhez és streameléséhez."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</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_title_computer" msgid="4671071173916176037">"Google Play-szolgáltatások"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nevében a telefonon tárolt fotókhoz, médiatartalmakhoz és értesítésekhez való hozzáféréshez"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; számára ennek a műveletnek a végrehajtását?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; számára a telefonja alkalmazásainak és rendszerfunkcióinak streamelését?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"A(z) %1$s hozzáférhet mindenhez, ami a telefonon látható vagy lejátszható, így az audiotartalomhoz, 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) %1$s képes lesz majd az alkalmazások és a rendszerfunkciók streamelésére."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"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 más rendszerfunkciók közeli eszközökre történő streamelésére"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Engedélyezi a(z) &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 és a rendszerfunkciók <xliff:g id="DEVICE_NAME">%3$s</xliff:g> eszközre való streamelésére."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"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 más rendszerfunkcióknak eszközök közötti streameléséhez"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Médiakimenet módosítása"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Értesítések"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Alkalmazások"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streamelés"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Telefonhívások kezdeményezése és kezelése"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Hívásnapló olvasása és írása"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS-ek küldése és megtekintése"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Megkeresheti a közeli eszközöket, meghatározhatja viszonylagos helyzetüket és csatlakozhat hozzájuk."</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók."</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Elolvashat minden értesítést, ideértve az olyan információkat, mint a névjegyek, az üzenetek és a fotók.&lt;br/&gt;• Értesítéseket küldhet.&lt;br/&gt;&lt;br/&gt;A Beállítások &gt; Értesítések menüpontban bármikor kezelheti az alkalmazás értesítések olvasására és küldésére való képességét."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"A telefon alkalmazásainak streamelése"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Alkalmazások és más rendszerfunkciók streamelése a telefonról"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Hozzáférhet a rendelkezésre álló eszközök listájához, és szabályozhatja, hogy melyik streamelhet vagy küldhet át hang- vagy videotartalmat más alkalmazásokból"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefonján"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"táblagépén"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tévé"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"eszköz"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index a655a3ec..f90a324 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Ընտրեք սարքը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածի միջոցով"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Կարգավորելու համար ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Այս հավելվածը կկարողանա համաժամացնել տվյալները, օր․՝ զանգողի անունը, և կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ում"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Այս հավելվածը կկարողանա համաժամացնել տվյալները, օր․՝ զանգողի անունը, և կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string>
     <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="2872254734959842579">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ում"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-ին հեռարձակել ձեր հեռախոսի հավելվածները"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s-ին հասանելի կլինի հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, գաղտնաբառերը և հաղորդագրությունները։&lt;br/&gt;&lt;br/&gt;%1$s-ը կկարողանա հավելվածներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Միջսարքային ծառայություններ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Թույլատրե՞լ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"Թույլատրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</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_title_computer" msgid="4671071173916176037">"Google Play ծառայություններ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր հեռախոսի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածին կատարել այս գործողությունը"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-ին հեռարձակել ձեր հեռախոսի հավելվածները և համակարգի գործառույթները"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s-ին հասանելի կլինի ձեր հեռախոսում ցուցադրվող կամ նվագարկվող բովանդակությունը՝ ներառյալ աուդիոն, լուսանկարները, վճարային տեղեկությունները, գաղտնաբառերը և հաղորդագրությունները։&lt;br/&gt;&lt;br/&gt;%1$s-ը կկարողանա հավելվածներ և համակարգի գործառույթներ հեռարձակել, քանի դեռ չեք չեղարկել այս թույլտվությունը։"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ մոտակա սարքերին հավելվածներ և համակարգի այլ գործառույթներ հեռարձակելու համար"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Թույլատրե՞լ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; սարքին հեռարձակել ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ի հավելվածները և համակարգի գործառույթները &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; սարքին։"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Փոխել մեդիա արտածումը"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Լուսանկարներ և մուլտիմեդիա"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Ծանուցումներ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Հավելվածներ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Հեռարձակում"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Կատարել զանգեր և կառավարել զանգերը"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Դիտել հեռախոսի զանգերի մատյանը և կատարել գրանցում"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Ուղարկել և կարդալ SMS հաղորդագրություններ"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Գտնել և որոշել մոտակա սարքերի մոտավոր դիրքը և միանալ դրանց"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Կարդալ բոլոր ծանուցումները, ներառյալ տեղեկությունները, օրինակ՝ կոնտակտները, հաղորդագրությունները և լուսանկարները"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Կարդալ բոլոր ծանուցումները, այդ թվում՝ կոնտակտները, հաղորդագրությունները և լուսանկարները&lt;br/&gt;• Ուղարկել ծանուցումներ&lt;br/&gt;&lt;br/&gt;Դուք ցանկացած պահի կարող եք կառավարել այս հավելվածի՝ ծանուցումներ կարդալու հնարավորությունը՝ անցնելով Կարգավորումներ &gt; Ծանուցումներ։"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Հեռարձակել հեռախոսի հավելվածները"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Հեռարձակել հավելվածներ և համակարգի այլ գործառույթներ հեռախոսում"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Բացել հասանելի սարքերի ցանկը և կառավարել, թե որ սարքը աուդիո կամ վիդեո հեռարձակի այլ հավելվածներից"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"հեռախոս"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"պլանշետ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"հեռուստացույց"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"սարք"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 1481ca1..7a9f485 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pilih perangkat untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk disiapkan"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aplikasi ini akan diizinkan menyinkronkan info, seperti nama penelepon, dan mengakses izin ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Anda"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aplikasi ini akan diizinkan menyinkronkan info, seperti nama penelepon, dan mengakses izin ini di <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> Anda"</string>
     <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="2872254734959842579">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Anda"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses informasi ini dari ponsel Anda"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; menstreaming aplikasi di ponsel Anda?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s akan memiliki akses ke apa pun yang ditampilkan atau diputar di ponsel, termasuk audio, foto, sandi, dan pesan.&lt;br/&gt;&lt;br/&gt;%1$s akan dapat menstreaming aplikasi hingga Anda menghapus izin ini."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Layanan lintas perangkat"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk menstreaming aplikasi di antara perangkat Anda"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk menampilkan dan menstreaming aplikasi di antara perangkat Anda"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; men-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_app_streaming" msgid="1274464413649731829">"<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, sandi, dan pesan.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat men-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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menampilkan dan men-streaming aplikasi di antara perangkat Anda"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Izinkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengakses informasi ini dari ponsel Anda"</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_title_computer" msgid="4671071173916176037">"Layanan Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi ponsel Anda"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Izinkan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; melakukan tindakan ini?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Izinkan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; menstreaming aplikasi dan fitur sistem di ponsel Anda?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s akan memiliki akses ke apa pun yang ditampilkan atau diputar di ponsel Anda, termasuk audio, foto, info pembayaran, sandi, dan pesan.&lt;br/&gt;&lt;br/&gt;%1$s akan dapat menstreaming aplikasi dan fitur sistem hingga Anda menghapus izin ini."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstreaming aplikasi dan fitur sistem lainnya ke perangkat di sekitar"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Izinkan &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; men-streaming aplikasi dan fitur sistem <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ke &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 men-streaming aplikasi dan fitur sistem ke <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hingga Anda menghapus izin ini."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk men-streaming aplikasi dan fitur sistem lainnya di antara perangkat 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mengubah output media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifikasi"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikasi"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Melakukan dan mengelola panggilan telepon"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Membaca dan menulis log panggilan telepon"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Mengirim dan melihat pesan SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Menemukan, meng­hu­bung­kan, dan menentukan posisi relatif dari perangkat di sekitar"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto&lt;br/&gt;• Mengirim notifikasi&lt;br/&gt;&lt;br/&gt;Anda dapat mengelola kemampuan aplikasi untuk membaca dan mengirim notifikasi kapan saja di Setelan &gt; Notifikasi."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikasi ponsel"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Menstreaming aplikasi dan fitur sistem lainnya dari ponsel Anda"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Mengakses daftar perangkat yang tersedia dan mengontrol perangkat mana yang melakukan streaming atau transmisi audio atau video dari aplikasi lain"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ponsel"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"perangkat"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index befc55c..3a41706 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"úr"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Veldu tæki sem &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Veldu <xliff:g id="PROFILE_NAME">%1$s</xliff:g> til að setja upp"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Þetta forrit fær heimild til að samstilla upplýsingar, t.d. nafn þess sem hringir, og fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Þetta forrit fær heimild til að samstilla upplýsingar, t.d. nafn þess sem hringir, og fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Þetta forrit fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Leyfa &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; að streyma forritum símans?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s fær aðgang að öllu sem er sýnilegt eða spilað í símanum, þ.m.t. hljóði, myndum, aðgangsorðum og skilaboðum.&lt;br/&gt;&lt;br/&gt;%1$s getur streymt forritum þar til þú fjarlægir aðgang að þessari heimild."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til að streyma forritum á milli tækjanna þinna"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til að birta og streyma forritum á milli tækjanna þinna"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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> í ;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="1274464413649731829">"<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 aðgang að þessari heimild."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að birta og streyma forritum á milli tækjanna þinna"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</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_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um aðgang að myndum, margmiðlunarefni og tilkynningum símans þíns fyrir hönd <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Leyfa &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; að framkvæma þessa aðgerð?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Leyfa &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; að streyma forritum og kerfiseiginleikum símans?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s fær aðgang að öllu sem er sýnilegt eða spilað í símanum, þ.m.t. hljóði, myndum, greiðsluupplýsingum, aðgangsorðum og skilaboðum.&lt;br/&gt;&lt;br/&gt;%1$s getur streymt forritum og kerfiseiginleikum þar til þú fjarlægir aðgang að þessari heimild."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum og öðrum kerfiseiginleikum í nálægum tækjum"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Leyfa &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 og kerfiseiginleikum í <xliff:g id="DEVICE_NAME">%3$s</xliff:g> þar til þú fjarlægir aðgang að þessari heimild."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum og öðrum kerfiseiginleikum á milli tækjanna þinna"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Breyta margmiðlunarúttaki"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Tilkynningar"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Forrit"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streymi"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Hringja og stjórna símtölum"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lesa og skrifa símtalaskrá símans"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Senda og skoða SMS-skilaboð"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finna, tengjast og áætla staðsetningu nálægra tækja"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lesa allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lesa allar tilkynningar, þ.m.t. upplýsingar á borð við tengiliði, skilaboð og myndir&lt;br/&gt;• Senda tilkynningar&lt;br/&gt;&lt;br/&gt;Þú getur stjórnað getu forritsins til að lesa og senda tilkynningar hvenær sem er í „Stillingar &gt; Tilkynningar“."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streymdu forritum símans"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streymdu forritum og öðrum kerfiseiginleikum úr símanum"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Fá aðgang að lista yfir tæki sem eru í boði og stjórna hver þeirra geta streymt og sent út hljóð og vídeó úr öðrum forritum"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"símanum"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"spjaldtölvunni"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"sjónvarp"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"tæki"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml
index 3575ff3..b85ce64 100644
--- a/packages/CompanionDeviceManager/res/values-it/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-it/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Scegli un dispositivo che sia gestito da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> da configurare"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, e accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, e accedere alle seguenti autorizzazioni <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Questa app potrà accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Consenti a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di accedere a queste informazioni dal tuo telefono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Consentire a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di riprodurre in streaming le app del tuo smartphone?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s avrà accesso a tutto ciò che è visibile o riprodotto sullo smartphone, inclusi audio, foto, password e messaggi.&lt;br/&gt;&lt;br/&gt;%1$s sarà in grado di riprodurre in streaming le app finché non rimuoverai l\'accesso a questa autorizzazione."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizi cross-device"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> l\'autorizzazione a trasmettere app in streaming tra i dispositivi"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> l\'autorizzazione a visualizzare e riprodurre in streaming app tra i dispositivi"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Questa app potrà accedere alle seguenti autorizzazioni <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclusi audio, foto, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione a mostrare e riprodurre in streaming app tra i dispositivi"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Consenti a &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; di accedere a questa informazione dal tuo telefono"</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_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> l\'autorizzazione ad accedere a foto, contenuti multimediali e notifiche del telefono"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vuoi consentire a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; di compiere questa azione?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Consentire a &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; di riprodurre in streaming app e funzionalità di sistema del tuo smartphone?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s avrà accesso a tutto ciò che è visibile o riprodotto sullo smartphone, inclusi audio, foto, dati di pagamento, password e messaggi.&lt;br/&gt;&lt;br/&gt;%1$s sarà in grado di riprodurre in streaming app e funzionalità di sistema finché non rimuoverai l\'accesso a questa autorizzazione."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione a trasmettere in streaming app e altre funzionalità di sistema ai dispositivi nelle vicinanze"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Consentire all\'app &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> avrà accesso a tutti i contenuti visibili o riprodotti <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 app e funzionalità di sistema su <xliff:g id="DEVICE_NAME">%3$s</xliff:g> finché non rimuoverai l\'accesso a questa autorizzazione."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione a riprodurre in streaming app e altre funzionalità di sistema tra i dispositivi"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Cambia uscita  multimediale"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notifiche"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"App"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Esegue e gestisce le telefonate"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Legge e modifica il registro chiamate dello smartphone"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Invia e visualizza SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Trova e si connette ai dispositivi nelle vicinanze, oltre a stabilire la loro posizione relativa"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Legge tutte le notifiche, incluse informazioni come contatti, messaggi e foto&lt;br/&gt;• Invia notifiche&lt;br/&gt;&lt;br/&gt;Puoi gestire la capacità dell\'app di leggere e inviare notifiche in qualsiasi momento in Impostazioni &gt; Notifiche."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Trasmetti in streaming le app del tuo telefono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Trasmettere in streaming app e altre funzionalità di sistema dal telefono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accedi a un elenco di dispositivi disponibili e controlla quale di questi riproduce in streaming o trasmette audio o video da altre app"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefono"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"della TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"del dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 6aafb55..8148f56 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"‏בחירה של מכשיר לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> להגדרה"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, ולגשת להרשאות האלה ב<xliff:g id="DEVICE_NAME">%1$s</xliff:g> שלך"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, ולגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string>
     <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="2872254734959842579">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_NAME">%1$s</xliff:g> שלך"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"‏מתן אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת למידע הזה מהטלפון שלך"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"‏לאשר לאפליקציית &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לשדר את האפליקציות של הטלפון?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"‏ל-%1$s תהיה גישה לכל מה שרואים או מפעילים בטלפון, כולל אודיו, תמונות, סיסמאות והודעות.&lt;br/&gt;&lt;br/&gt;‎‏ל-%1$s תהיה אפשרות לשדר אפליקציות עד שהגישה להרשאה הזו תוסר."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"שירותים למספר מכשירים"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור המכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"האפליקציה \'<xliff:g id="APP_NAME">%1$s</xliff:g>\' מבקשת הרשאה למכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי להציג ולשדר אפליקציות בין המכשירים שלך"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"‏לאשר לאפליקציית &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="1274464413649731829">"‏לאפליקציה <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="1944832605534698562">"האפליקציה <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="4693714143506569253">"‏מתן אישור לאפליקציה &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לגשת למידע הזה מהטלפון שלך"</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_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור המכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות בטלפון שלך"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"‏לתת הרשאה למכשיר &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; לבצע את הפעולה הזו?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"‏לאשר לאפליקציית &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; לשדר את האפליקציות ואת תכונות המערכת של הטלפון?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"‏ל-%1$s תהיה גישה לכל מה שרואים או מפעילים בטלפון, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.&lt;br/&gt;&lt;br/&gt;‎‏ל-%1$s תהיה אפשרות לשדר אפליקציות ותכונות מערכת עד שהגישה להרשאה הזו תוסר."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור <xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי להעביר אפליקציות ותכונות מערכת אחרות בסטרימינג למכשירים בקרבת מקום"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"‏לאשר ל-&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; לשדר אפליקציות ותכונות מערכת אחרות של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> למכשיר &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"‏לאפליקציה <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_nearby_device_streaming" msgid="8509848562931818793">"האפליקציה <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"שינוי פלט המדיה"</string>
     <string name="permission_storage" msgid="6831099350839392343">"תמונות ומדיה"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"התראות"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"אפליקציות"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"סטרימינג"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ביצוע וניהול של שיחות טלפון"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"קריאה וכתיבה של נתוני יומן השיחות בטלפון"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"‏שליחה של הודעות SMS וצפייה בהן"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"אפשרות למצוא מכשירים בקרבת מקום, להתחבר אליהם ולאתר את המיקום היחסי שלהם"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"אפשרות לקרוא את כל ההתראות, כולל מידע כמו אנשי קשר, הודעות ותמונות"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• קריאה של כל ההתראות, כולל מידע כמו אנשי קשר, הודעות ותמונות&lt;br/&gt;• שליחת התראות&lt;br/&gt;&lt;br/&gt;אפשר לשנות את היכולת של האפליקציה לקרוא ולשלוח התראות בכל שלב ב\'הגדרות\' &gt; \'התראות\'."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"שידור אפליקציות מהטלפון"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"העברה של אפליקציות ותכונות מערכת אחרות בסטרימינג מהטלפון"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"‏גישה לרשימה של מכשירים זמינים ובחירה איזה מהם ישמש כדי לשדר אודיו או וידאו או להפעיל Cast שלהם מאפליקציות אחרות"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"טלפון"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"טאבלט"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"טלוויזיה"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"מכשיר"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 0f16394..173a72f 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; の管理対象となるデバイスの選択"</string>
     <string name="chooser_title" msgid="2235819929238267637">"設定する<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"このアプリは、通話相手の名前などの情報を同期したり、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>の以下の権限にアクセスしたりできるようになります"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"このアプリは、通話相手の名前などの情報を同期したり、<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>の以下の権限にアクセスしたりできるようになります"</string>
     <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="2872254734959842579">"このアプリは、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>の以下の権限にアクセスできるようになります"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"スマートフォンのこの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; にスマートフォンのアプリをストリーミングすることを許可しますか?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s は、音声、写真、パスワード、メッセージを含め、スマートフォンで表示、再生されるすべてのコンテンツにアクセスできるようになります。&lt;br/&gt;&lt;br/&gt;この権限へのアクセス権を削除するまで、%1$s はアプリをストリーミングできます。"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"クロスデバイス サービス"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> に代わってデバイス間でアプリを表示およびストリーミングする権限をリクエストしています"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"このアプリは、<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>の以下の権限にアクセスできるようになります"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"<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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"スマートフォンのこの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</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_title_computer" msgid="4671071173916176037">"Google Play 開発者サービス"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> に代わってスマートフォンの写真、メディア、通知にアクセスする権限をリクエストしています"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; にこの操作の実行を許可しますか?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; にスマートフォンのアプリとシステム機能をストリーミングすることを許可しますか?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s は、音声、写真、お支払い情報、パスワード、メッセージを含め、スマートフォンで表示、再生されるすべてのコンテンツにアクセスできるようになります。&lt;br/&gt;&lt;br/&gt;この権限へのアクセス権を削除するまで、%1$s はアプリとシステム機能をストリーミングできます。"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_NAME">%2$s</xliff:g> に代わって、アプリやその他のシステム機能を付近のデバイスにストリーミングする権限をリクエストしています"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> のアプリとシステム機能を &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; にストリーミングすることを &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; に許可しますか?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"メディア出力の変更"</string>
     <string name="permission_storage" msgid="6831099350839392343">"写真とメディア"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"アプリ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ストリーミング"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"電話の発信と管理"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"通話履歴の読み取りと書き込み"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS メッセージの送信と表示"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"付近のデバイスの検出、接続、相対位置の特定"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"すべての通知の読み取り(連絡先、メッセージ、写真に関する情報を含む)"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• すべての通知の読み取り(連絡先、メッセージ、写真に関する情報を含む)&lt;br/&gt;• 通知の送信&lt;br/&gt;&lt;br/&gt;このアプリの通知読み取り機能と通知送信機能はいつでも [設定] &gt; [通知] で管理できます。"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"スマートフォンのアプリをストリーミングします"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"アプリやその他のシステム機能をスマートフォンからストリーミングする"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"他のアプリから音声や動画をストリーミングまたはキャストする際に使用可能なデバイスとコントロールのリストにアクセスできます"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"スマートフォン"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"タブレット"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"テレビ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"デバイス"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index ced2802..507d13c 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"აირჩიეთ მოწყობილობა, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; აპმა"</string>
     <string name="chooser_title" msgid="2235819929238267637">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> დასაყენებლად"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ეს აპი შეძლებს, დაასინქრონოს ინფორმაცია, მაგალითად, შემომავალი ზარის ავტორის სახელი და წვდომა იქონიოს ამ ნებართვებზე თქვენს <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ში"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ეს აპი შეძლებს ინფორმაციის (მაგალითად, იმ ადამიანის სახელი, რომელიც გირეკავთ) სინქრონიზებას და თქვენს <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-ზე არსებულ ამ ნებართვებზე წვდომას"</string>
     <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="2872254734959842579">"ეს აპი შეძლებს ამ ნებართვებზე წვდომას თქვენს <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ში"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"ნება დართეთ, რომ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"გსურთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-ს მისცეთ ტელეფონის აპების სტრიმინგის საშუალება?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s-ს ექნება წვდომა ყველაფერზე, რაც ჩანს ან უკრავს ტელეფონზე, მათ შორის, აუდიოზე, ფოტოებზე, პაროლებსა და შეტყობინებებზე.&lt;br/&gt;&lt;br/&gt;%1$s შეძლებს აპების გაშვებას მანამ, სანამ თქვენ არ გააუქმებთ წვდომას ამ ნებართვაზე."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"მოწყობილობათშორისი სერვისები"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-ის სახელით, რომ მოწყობილობებს შორის სტრიმინგი შეძლოს"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს ნებართვას თქვენი (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) სახელით, რათა წარმოაჩინოს და მოახდინოს აპების გაშვება თქვენს მოწყობილობებს შორის"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ეს აპი შეძლებს წვდომას თქვენს <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-ზე არსებულ ამ ნებართვებზე"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"გსურთ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"ნება დართეთ, რომ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-ის სახელით, რომ წვდომა ჰქონდეს თქვენი ტელეფონის ფოტოებზე, მედიასა და შეტყობინებებზე"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"გსურთ ნება მისცეთ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს&lt;/strong&gt; ამ მოქმედების შესასრულებლად?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"გსურთ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-ს მისცეთ თქვენი ტელეფონის აპებისა და სისტემის ფუნქციების სტრიმინგის საშუალება?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s-ს ექნება წვდომა ყველაფერზე, რაც ჩანს ან უკრავს ტელეფონზე, მათ შორის, აუდიოზე, ფოტოებზე, გადახდის ინფორმაციაზე, პაროლებსა და შეტყობინებებზე.&lt;br/&gt;&lt;br/&gt;%1$s შეძლებს აპებისა და სისტემის ფუნქციების გაშვებას მანამ, სანამ თქვენ არ გააუქმებთ წვდომას ამ ნებართვაზე."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს თქვენი <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-ის სახელით აპების და სისტემის სხვა ფუნქციების ახლომახლო მოწყობილობებზე სტრიმინგის ნებართვას"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"გსურთ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;-ს მისცეთ თქვენი <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-ის აპებისა და სისტემის ფუნქციების სტრიმინგის საშუალება &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;-ზე?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"გამომავალი მედიის გამოტანის"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ფოტოები და მედია"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"შეტყობინებები"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"აპები"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"სტრიმინგი"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"სატელეფონო ზარების განხორციელება და მართვა"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"სატელეფონო ზარების ჟურნალის წაკითხვა და მასში ჩაწერა"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS შეტყობინებების გაგზავნა და ნახვა"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ახლომახლო მოწყობილობების პოვნა, მათთან დაკავშირება და მათი შედარებითი პოზიციის დადგენა"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ყველა შეტყობინების, მათ შორის ისეთი ინფორმაციის წაკითხვა, როგორიცაა კონტაქტები, შეტყობინებები და ფოტოები"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• ყველა შეტყობინების, მათ შორის კონტაქტების, შეტყობინებებისა და ფოტოების წაკითხვა&lt;br/&gt;• შეტყობინებების გაგზავნა&lt;br/&gt;&lt;br/&gt;შეგიძლიათ, მართოთ ამ აპის შესაძლებლობა, წაიკითხოს და გაგზავნოს შეტყობინებები ნებისმიერ დროს პარამეტრებში &gt; შეტყობინებები."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"თქვენი ტელეფონის აპების სტრიმინგი"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"აწარმოეთ აპების და სისტემის სხვა ფუნქციების სტრიმინგი თქვენი ტელეფონიდან"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"წვდომა ხელმისაწვდომი მოწყობილობების სიაზე და აკონტროლეთ რომელი უშვებს აუდიოს ან ვიდეოს სხვა აპებიდან."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ტელეფონი"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ტაბლეტი"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ტელევიზორი"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"მოწყობილობა"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index b981bf4..16a02dc 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын құрылғыны таңдаңыз"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Реттеу үшін <xliff:g id="PROFILE_NAME">%1$s</xliff:g> таңдаңыз"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Бұл қолданба қоңырау шалушының аты сияқты деректі синхрондай алады және <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысындағы мына рұқсаттарды пайдалана алады."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Бұл қолданба қоңырау шалушының аты сияқты деректі синхрондай алады және құрылғыдағы (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) осы рұқсаттарды пайдалана алады."</string>
     <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="2872254734959842579">"Бұл қолданба <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысында осы рұқсаттарды пайдалана алады."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына телефондағы қолданбаларды трансляциялауға рұқсат берілсін бе?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s телефонда көрінетін не ойнатылатын кез келген контентті (аудиофайлдарды, фотосуреттерді, құпия сөздер мен хабарларды қоса алғанда) пайдалана алады.&lt;br/&gt;&lt;br/&gt;Өзіңіз осы рұқсатты өшірмесеңіз, %1$s қолданбаларды трансляциялай алады."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Аралық құрылғы қызметтері"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> атынан құрылғылар арасында қолданбалар трансляциялау үшін рұқсат сұрайды."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> атынан құрылғылар арасында қолданбаларды көрсетуге және трансляциялауға рұқсат сұрайды."</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Бұл қолданба құрылғыда (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) осы рұқсаттарды пайдалана алады."</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</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_title_computer" msgid="4671071173916176037">"Google Play қызметтері"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> атынан телефондағы фотосуреттерді, медиафайлдар мен хабарландыруларды пайдалану үшін рұқсат сұрайды."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; құрылғысына бұл әрекетті орындауға рұқсат беру керек пе?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; құрылғысына телефондағы қолданбаларды және жүйе функцияларын трансляциялауға рұқсат берілсін бе?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s телефонда көрінетін не ойнатылатын кез келген контентті (аудиофайлдарды, фотосуреттерді, төлем туралы ақпаратты, құпия сөздер мен хабарларды қоса алғанда) пайдалана алады.&lt;br/&gt;&lt;br/&gt;Өзіңіз осы рұқсатты өшірмесеңіз, %1$s қолданбалар мен жүйе функцияларын трансляциялай алады."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан қолданбалар мен басқа да жүйе функцияларын маңайдағы құрылғыларға трансляциялау рұқсатын сұрап тұр."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) қолданбалар мен жүйе функцияларын &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; құрылғысына трансляциялауға рұқсат берілсін бе?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Мультимедиа шығысын өзгерту"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Фотосуреттер мен медиафайлдар"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Хабарландырулар"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Қолданбалар"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Трансляция"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Қоңырау шалу және телефон қоңырауларын басқару"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Телефонның қоңыраулар журналын оқу және жазу"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS хабарларын жіберу және көру"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Маңайдағы құрылғыларды тауып, олармен байланысып, бір-біріне қатысты локациясын анықтайды."</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Барлық хабарландыруды, соның ішінде контактілер, хабарлар және фотосуреттер сияқты ақпаратты оқиды."</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Барлық хабарландыруды, соның ішінде контактілер, хабарлар және фотосуреттер сияқты ақпаратты оқиды&lt;br/&gt;• Хабарландырулар жібереді&lt;br/&gt;&lt;br/&gt;Бұл қолданбаның хабарландыруларды оқу және жіберу мүмкіндігін \"Параметрлер &gt; Хабарландырулар\" тармағында кез келген уақытта басқара аласыз."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Телефон қолданбаларын трансляциялайды."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Қолданбалар мен басқа да жүйе функцияларын телефоннан трансляциялау"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Қолжетімді құрылғылар тізімін ашып, қай құрылғының басқа қолданбалардан аудионы не бейнені трансляциялайтынын басқара аласыз."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефон"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"планшет"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"теледидар"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"құрылғы"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 41cdf95..e59db72 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"ជ្រើសរើសឧបករណ៍ ដើម្បីដាក់ក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីរៀបចំ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម និងចូលប្រើប្រាស់ការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> របស់អ្នក"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម និងចូលប្រើការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string>
     <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="2872254734959842579">"កម្មវិធីនេះ​នឹងត្រូវបានអនុញ្ញាតឱ្យ​ចូលប្រើប្រាស់ការអនុញ្ញាតទាំងនេះ​នៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> របស់អ្នក"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ចូលប្រើព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ផ្សាយកម្មវិធីលើទូរសព្ទរបស់អ្នកឬ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើទូរសព្ទ រួមទាំងសំឡេង រូបថត ពាក្យសម្ងាត់ និងសារ។&lt;br/&gt;&lt;br/&gt;%1$s នឹងអាចផ្សាយកម្មវិធី រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"សេវាកម្មឆ្លងកាត់ឧបករណ៍"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីបញ្ចាំងកម្មវិធីរវាងឧបករណ៍របស់អ្នក"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីបង្ហាញ និងផ្សាយកម្មវិធីរវាងឧបករណ៍នានារបស់អ្នក"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"កម្មវិធីនេះ​នឹងត្រូវបានអនុញ្ញាតឱ្យ​ចូលប្រើការអនុញ្ញាតទាំងនេះ​នៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"អនុញ្ញាតឱ្យ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ចូលមើលព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</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_title_computer" msgid="4671071173916176037">"សេវាកម្ម Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីចូលប្រើរូបថត មេឌៀ និងការជូនដំណឹងរបស់ទូរសព្ទអ្នក"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ធ្វើសកម្មភាពនេះឬ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ផ្សាយមុខងារប្រព័ន្ធ និងកម្មវិធីលើទូរសព្ទរបស់អ្នកឬ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើទូរសព្ទរបស់អ្នក រួមទាំងសំឡេង រូបថត ព័ត៌មាននៃការទូទាត់ប្រាក់ ពាក្យសម្ងាត់ និងសារ។&lt;br/&gt;&lt;br/&gt;%1$s នឹងអាចផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធ រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំ​ការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីចាក់ផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធផ្សេងទៀត​ទៅកាន់​ឧបករណ៍នៅជិត"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"អនុញ្ញាតឱ្យ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; ផ្សាយមុខងារប្រព័ន្ធ និងកម្មវិធីលើ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នកទៅ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; ឬ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ប្ដូរឧបករណ៍មេឌៀ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"រូបថត និងមេឌៀ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ការ​ជូនដំណឹង"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"កម្មវិធី"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ការផ្សាយ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ហៅទូរសព្ទ និងគ្រប់គ្រងការហៅទូរសព្ទ"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"អាន និងសរសេរ​កំណត់ហេតុហៅទូរសព្ទ"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"ផ្ញើ និងមើលសារ SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ស្វែងរក ភ្ជាប់ និងកំណត់​ទីតាំងដែលពាក់ព័ន្ធ​របស់ឧបករណ៍​នៅជិត"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"អាន​ការជូនដំណឹង​ទាំងអស់ រួមទាំង​ព័ត៌មាន​ដូចជា ទំនាក់ទំនង សារ និងរូបថតជាដើម"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• អានការជូនដំណឹងទាំងអស់ រួមទាំងព័ត៌មានដូចជា ទំនាក់ទំនង សារ និងរូបថតជាដើម&lt;br/&gt;• ផ្ញើការជូនដំណឹង&lt;br/&gt;&lt;br/&gt;អ្នកអាចគ្រប់គ្រងលទ្ធភាពរបស់កម្មវិធីនេះក្នុងការអាន និងផ្ញើការជូនដំណឹងបានគ្រប់ពេលនៅក្នុងការកំណត់ &gt; ការជូនដំណឹង។"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ផ្សាយកម្មវិធីរបស់ទូរសព្ទអ្នក"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ចាក់ផ្សាយ​កម្មវិធី និងមុខងារប្រព័ន្ធ​ផ្សេងទៀត​ពីទូរសព្ទ​របស់អ្នក"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ចូលប្រើប្រាស់បញ្ជីឧបករណ៍ដែលអាចប្រើបាន និងគ្រប់គ្រងថាតើឧបករណ៍ណាចាក់ ឬបញ្ជូនសំឡេង ឬវីដេអូពីកម្មវិធីផ្សេងទៀត"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ទូរសព្ទ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ថេប្លេត"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ទូរទស្សន៍"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ឧបករណ៍"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 7dbcef0..ea7d824 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ ಸಾಧನವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ಸೆಟಪ್ ಮಾಡಲು <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆರಿಸಿ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಮತ್ತು ಈ ಅನುಮತಿಗಳನ್ನು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಲ್ಲಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ಯಾರೋ ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಮತ್ತು <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್‌ ಮಾಡಲು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string>
     <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="2872254734959842579">"ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಿ"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಯಾವುದೇ ಕಂಟೆಂಟ್‌ಗೆ %1$s ಆ್ಯಕ್ಸೆಸ್ ಹೊಂದಿರುತ್ತದೆ.&lt;br/&gt;&lt;br/&gt;ನೀವು ಈ ಅನುಮತಿಗೆ ಇರುವ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ %1$s ಗೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ಕ್ರಾಸ್-ಡಿವೈಸ್ ಸೇವೆಗಳು"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲು ಮತ್ತು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿವೆ"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್‌ ಮಾಡಲು ಈ ಆ್ಯಪ್‌ ಅನ್ನು ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"ನಿಮ್ಮ <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="1274464413649731829">"ನಿಮ್ಮ <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="1944832605534698562">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್‌ಗಳನ್ನು ಪ್ರದರ್ಶಿಸಲು ಮತ್ತು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <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="4693714143506569253">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಿ"</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_title_computer" msgid="4671071173916176037">"Google Play ಸೇವೆಗಳು"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"ನಿಮ್ಮ ಫೋನ್‌ನ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ಈ ಆ್ಯಕ್ಷನ್ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನುಮತಿಸಬೇಕೇ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿನ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ ಫೋನ್‌ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಯಾವುದೇ ಕಂಟೆಂಟ್‌ಗೆ.&lt;br/&gt;&lt;br/&gt;ನೀವು ಈ ಅನುಮತಿಗೆ ಇರುವ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ %1$s ಗೆ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳಿಗೆ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಇತರ ಸಿಸ್ಟಂ ಫೀಚರ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ರ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್‌ಗಳನ್ನು <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ಗೆ ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME_0">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"ನಿಮ್ಮ <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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ಮೀಡಿಯಾ ಔಟ್‌‌ಪುಟ್ ಬದಲಾಯಿಸಿ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ಫೋಟೋಗಳು ಮತ್ತು ಮಾಧ್ಯಮ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ನೋಟಿಫಿಕೇಶನ್‌ಗಳು"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ಆ್ಯಪ್‌ಗಳು"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ಸ್ಟ್ರೀಮಿಂಗ್"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಿ ಹಾಗೂ ನಿರ್ವಹಿಸಿ"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ಪೋನ್‌ ಕರೆಯ ಲಾಗ್‌ ಅನ್ನು ಓದಿ ಮತ್ತು ಬರೆಯಿರಿ"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಿ ಮತ್ತು ವೀಕ್ಷಿಸಿ"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳನ್ನು ಹುಡುಕಬಹುದು, ಅವುಗಳಿಗೆ ಕನೆಕ್ಟ್ ಆಗಬಹುದು ಮತ್ತು ಅವುಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಬಹುದು"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ಸಂಪರ್ಕಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಫೋಟೋಗಳಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಓದಬಹುದು"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• ಸಂಪರ್ಕಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ಫೋಟೋಗಳಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಓದಿ&lt;br/&gt;• ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಕಳುಹಿಸಿ&lt;br/&gt;&lt;br/&gt;ಈ ಆ್ಯಪ್‌ನ ನೋಟಿಫಿಕೇಶನ್‌ಗಳನ್ನು ಓದುವ ಮತ್ತು ಕಳುಹಿಸುವ ಸಾಮರ್ಥ್ಯವನ್ನು ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು &gt; ನೋಟಿಫಿಕೇಶನ್‌ಗಳಲ್ಲಿ ನಿರ್ವಹಿಸಬಹುದು."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ನಿಮ್ಮ ಫೋನ್‍ನ ಆ್ಯಪ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ನಿಮ್ಮ ಫೋನ್‌ನಿಂದ ಆ್ಯಪ್‌ಗಳು ಮತ್ತು ಇತರ ಸಿಸ್ಟಂ ಫೀಚರ್‌ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ಲಭ್ಯವಿರುವ ಸಾಧನಗಳ ಪಟ್ಟಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ ಮತ್ತು ಇತರ ಆ್ಯಪ್‌ಗಳಿಂದ ಯಾವುದು ಆಡಿಯೋ ಅಥವಾ ವೀಡಿಯೊವನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡುವುದು ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುವುದನ್ನು ಕಂಟ್ರೋಲ್ ಮಾಡಿ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ಫೋನ್"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ಟ್ಯಾಬ್ಲೆಟ್"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ಟಿವಿ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ಸಾಧನ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 925f21b..4ff768e 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"시계"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 관리할 기기 선택"</string>
     <string name="chooser_title" msgid="2235819929238267637">"설정할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 선택"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"이 앱이 정보(예: 발신자 이름)를 동기화하고 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"이 앱이 정보(예: 발신자 이름)를 동기화하고 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string>
     <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="2872254734959842579">"앱이 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;이 휴대전화의 이 정보에 액세스하도록 허용합니다."</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 휴대전화의 앱을 스트리밍하도록 허용하시겠습니까?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s에서 오디오, 사진, 비밀번호, 메시지 등 휴대전화에 표시되거나 휴대전화에서 재생되는 모든 항목에 액세스할 수 있습니다.&lt;br/&gt;&lt;br/&gt;이 권한에 대한 액세스를 삭제할 때까지 %1$s에서 앱을 스트리밍할 수 있습니다."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"교차 기기 서비스"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 연결된 기기의 앱을 표시하고 스트리밍할 권한을 요청하고 있습니다."</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"앱이 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;이 휴대전화에서 이 정보에 액세스하도록 허용"</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_title_computer" msgid="4671071173916176037">"Google Play 서비스"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 휴대전화의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; 기기가 이 작업을 수행하도록 허용하시겠습니까?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 휴대전화의 앱 및 시스템 기능을 스트리밍하도록 허용하시겠습니까?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s에서 오디오, 사진, 결제 정보, 비밀번호, 메시지 등 휴대전화에 표시되거나 휴대전화에서 재생되는 모든 항목에 액세스할 수 있습니다.&lt;br/&gt;&lt;br/&gt;이 권한에 대한 액세스를 삭제할 때까지 %1$s에서 앱 및 시스템 기능을 스트리밍할 수 있습니다."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 근처 기기로 앱 및 기타 시스템 기능을 스트리밍할 권한을 요청하고 있습니다."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>의 앱 및 시스템 기능을 &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; 기기로 스트리밍하도록 허용하시겠습니까?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"미디어 출력 변경"</string>
     <string name="permission_storage" msgid="6831099350839392343">"사진 및 미디어"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"알림"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"앱"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"스트리밍"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"전화 걸기 및 관리"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"통화 기록 읽고 쓰기"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS 메시지 보내기 및 보기"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"근처 기기를 찾아 연결하고 기기 간 상대 위치를 파악할 수 있습니다."</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"연락처, 메시지, 사진 등의 정보를 포함한 모든 알림을 읽을 수 있습니다."</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• 연락처, 메시지, 사진과 같은 정보를 포함해 모든 알림을 읽습니다.&lt;br/&gt;• 알림을 전송합니다.&lt;br/&gt;&lt;br/&gt;언제든지 설정 &gt; 알림에서 알림을 읽고 전송할 수 있는 이 앱의 능력을 관리할 수 있습니다."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"휴대전화의 앱을 스트리밍합니다."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"내 휴대전화의 앱 및 기타 시스템 기능 스트리밍"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"사용할 수 있는 기기 목록에 액세스하고, 다른 앱에서 오디오 또는 동영상을 스트리밍하거나 전송할 기기를 제어합니다."</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"스마트폰"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"태블릿"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"기기"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index 23a2e7f..3e265a2 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"саат"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; аркылуу башкарыла турган түзмөктү тандаңыз"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Тууралоо үчүн <xliff:g id="PROFILE_NAME">%1$s</xliff:g> тандаңыз"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Бул колдонмого маалыматты, мисалы, чалып жаткан адамдын аты-жөнүн шайкештирүүгө жана <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Бул колдонмого маалыматты, мисалы, чалып жаткан адамдын аты-жөнүн шайкештирүүгө жана <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
     <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="2872254734959842579">"Бул колдонмого <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы колдонмолорду өткөрүүгө уруксат бересизби?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s телефондо көрүнгөн же ойнотулган аудиофайлдар, сүрөттөр, сырсөздөр жана билдирүүлөр сыяктуу нерселерди көрө алат. Бул уруксатты алып салмайынча, &lt;br/&gt;&lt;br/&gt;%1$s колдонмолорду өткөрө берет."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду алып ойнотууга уруксат сурап жатат"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрдүн ортосунда колдонмолорду көрсөтүү жана алып ойнотуу үчүн уруксат сурап жатат"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Бул колдонмого <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</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_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; түзмөгүнө бул аракетти аткарууга уруксат бересизби?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы колдонмолорду жана системалык функцияларды өткөргөнгө уруксат бересизби?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s телефонуңузда көрүнгөн же ойнотулган аудиофайлдар, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөр сыяктуу нерселерди көрө алат. Бул уруксатты алып салмайынча, &lt;br/&gt;&lt;br/&gt;%1$s колдонмолорду жана системдик функцияларды өткөрө алат."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан жакын жердеги түзмөктөрдө колдонмолорду жана системанын башка функцияларын алып ойнотууга уруксат сурап жатат"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; түзмөгүнө <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүндөгү колдонмолорду жана тутумдун функцияларын &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; түзмөгүндө алып ойнотууга уруксат бересизби?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Медианы чыгаруучу булакты өзгөртүү"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиафайлдар"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Билдирмелер"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Колдонмолор"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Алып ойнотуу"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Телефон чалуу жана аларды башкаруу"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Телефондогу чалуулар тизмесин окуу жана жазуу"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS билдирүүлөрдү жөнөтүү жана көрсөтүү"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Жакын жердеги түзмөктөрдү таап, аларга туташып, абалын аныктоо"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуу"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуу&lt;br/&gt;• Билдирмелерди жөнөтүү&lt;br/&gt;&lt;br/&gt;Бул колдонмонун билдирмелерди окуу жана жөнөтүү мүмкүнчүлүгүн каалаган убакта Параметрлер &gt; Билдирмелер бөлүмүнөн тескей аласыз."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Телефондогу колдонмолорду алып ойнотуу"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Телефонуңуздагы колдонмолорду жана системанын башка функцияларын алып ойнотуу"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Жеткиликтүү түзмөктөрдүн тизмесине кирип, кайсынысы башка колдонмолордон аудио же видеону алып ойнотуп же тышкы экранга чыгара турганын көзөмөлдөңүз"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефон"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"планшет"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"сыналгы"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"түзмөк"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 4a97f0d..2917c3e 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"ເລືອກອຸປະກອນທີ່ຈະໃຫ້ມີການຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ທີ່ຈະຕັ້ງຄ່າ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ແອັບນີ້ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ຊິ້ງຂໍ້ມູນ, ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ ແລະ ສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຂອງທ່ານ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ແອັບນີ້ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ຊິ້ງຂໍ້ມູນ ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ ແລະ ສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ຂອງທ່ານ"</string>
     <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="2872254734959842579">"ແອັບນີ້ຈະໄດ້ຮັບສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຂອງທ່ານ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"ອະນຸຍາດ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ສະ​ຕ​ຣີມແອັບຂອງໂທລະສັບຂອງທ່ານບໍ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ຈະມີສິດເຂົ້າເຖິງທຸກຢ່າງທີ່ເຫັນໄດ້ ຫຼື ຫຼິ້ນຢູ່ໂທລະສັບ, ຮວມທັງສຽງ, ຮູບພາບ, ລະຫັດຜ່ານ ແລະ ຂໍ້ຄວາມ.&lt;br/&gt;&lt;br/&gt;%1$s ຈະສາມາດສະ​ຕ​ຣີມແອັບໄດ້ຈົນກວ່າທ່ານຈະລຶບສິດເຂົ້າເຖິງການອະນຸຍາດນີ້ອອກ."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ບໍລິການຂ້າມອຸປະກອນ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ເພື່ອສະຕຣີມແອັບລະຫວ່າງອຸປະກອນຕ່າງໆຂອງທ່ານ"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກໍາລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ຂອງທ່ານເພື່ອສະແດງ ແລະ ຖ່າຍທອດແອັບລະຫວ່າງອຸປະກອນຂອງທ່ານ"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ແອັບນີ້ຈະໄດ້ຮັບສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ຂອງທ່ານ"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"ອະນຸຍາດໃຫ້ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"ອະນຸຍາດ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</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_title_computer" msgid="4671071173916176037">"ບໍລິການ Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ເພື່ອເຂົ້າເຖິງຮູບພາບ, ສື່ ແລະ ການແຈ້ງເຕືອນໃນໂທລະສັບຂອງທ່ານ"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ອະນຸຍາດ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອດຳເນີນຄຳສັ່ງນີ້ບໍ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ສະ​ຕ​ຣີມແອັບຂອງໂທລະສັບ ແລະ ຄຸນສົມບັດລະບົບຂອງທ່ານບໍ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ຈະມີສິດເຂົ້າເຖິງທຸກຢ່າງທີ່ເບິ່ງເຫັນ ຫຼື ຫຼິ້ນຢູ່ໂທລະສັບຂອງທ່ານ, ຮວມທັງສຽງ, ຮູບພາບ, ຂໍ້ມູນການຈ່າຍເງິນ, ລະຫັດຜ່ານ ແລະ ຂໍ້ຄວາມ.&lt;br/&gt;&lt;br/&gt;%1$s ຈະສາມາດສະ​ຕ​ຣີມແອັບ ແລະ ຄຸນສົມບັດຂອງລະບົບຈົນກວ່າທ່ານຈະລຶບສິດເຂົ້າເຖິງການອະນຸຍາດນີ້ອອກ."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກໍາລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ຂອງທ່ານເພື່ອສະຕຣີມແອັບ ແລະ ຄຸນສົມບັດລະບົບອື່ນໆໄປຫາອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"ອະນຸຍາດໃຫ້ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; ສະຕຣີມແອັບ ແລະ ຄຸນສົມບັດຂອງລະບົບຂອງ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ຂອງທ່ານໄປຫາ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; ບໍ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ປ່ຽນເອົ້າພຸດສື່"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ຮູບພາບ ແລະ ມີເດຍ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ການແຈ້ງເຕືອນ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ແອັບ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ກຳລັງສະຕຣີມ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ໂທອອກ ແລະ ຈັດການການໂທ"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ອ່ານ ແລະ ຂຽນບັນທຶກການໂທຂອງໂທລະສັບ"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"ສົ່ງ ແລະ ເບິ່ງຂໍ້ຄວາມ SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ຊອກຫາ, ເຊື່ອມຕໍ່ ແລະ ລະບຸສະຖານທີ່ທີ່ກ່ຽວຂ້ອງກັນຂອງອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ອ່ານການແຈ້ງເຕືອນທັງໝົດ, ຮວມທັງຂໍ້ມູນ ເຊັ່ນ: ລາຍຊື່ຜູ້ຕິດຕໍ່, ຂໍ້ຄວາມ ແລະ ຮູບພາບ"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• ອ່ານການແຈ້ງເຕືອນທັງໝົດ, ຮວມທັງຂໍ້ມູນ ເຊັ່ນ: ລາຍຊື່ຜູ້ຕິດຕໍ່, ຂໍ້ຄວາມ ແລະ ຮູບພາບ&lt;br/&gt;• ສົ່ງການແຈ້ງເຕືອນ&lt;br/&gt;&lt;br/&gt;ທ່ານສາມາດຈັດການຄວາມສາມາດຂອງແອັບນີ້ໃນການອ່ານ ແລະ ສົ່ງການແຈ້ງເຕືອນໄດ້ທຸກເວລາໃນການຕັ້ງຄ່າ&gt; ການແຈ້ງເຕືອນ."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ສະຕຣີມແອັບຂອງໂທລະສັບທ່ານ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ສະຕຣີມແອັບ ແລະ ຄຸນສົມບັດລະບົບອື່ນໆຈາກໂທລະສັບຂອງທ່ານ"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ເຂົ້າເຖິງລາຍຊື່ຂອງອຸປະກອນທີ່ມີ ແລະ ຄວບຄຸມອຸປະກອນທີ່ຈະສະຕຣີມ ຫຼື ສົ່ງສັນຍານສຽງ ຫຼື ວິດີໂອຈາກແອັບອື່ນໆ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ໂທລະສັບ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ແທັບເລັດ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ໂທລະທັດ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ອຸປະກອນ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index e931353..a1b5cd9 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Įrenginio, kuris bus valdomas naudojant programą &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, pasirinkimas"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Norimo nustatyti <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pasirinkimas"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Šiai programai bus leidžiama sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, ir pasiekti toliau nurodytus <xliff:g id="DEVICE_NAME">%1$s</xliff:g> leidimus"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Šiai programai bus leidžiama sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, ir pasiekti toliau nurodytus <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> leidimus"</string>
     <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="2872254734959842579">"Šiai programai bus leidžiama pasiekti toliau nurodytus <xliff:g id="DEVICE_NAME">%1$s</xliff:g> leidimus."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pasiekti šią informaciją iš jūsų telefono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Leisti programai &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; srautu perduoti telefono programas?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"„%1$s“ galės pasiekti visą telefone matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, slaptažodžius ir pranešimus.&lt;br/&gt;&lt;br/&gt;„%1$s“ galės perduoti srautu programas, kol pašalinsite prieigą prie šio leidimo."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Pasl. keliuose įrenginiuose"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš vieno įrenginio į kitą"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>“ vardu, kad galėtų rodyti ir srautu perduoti programas iš vieno įrenginio į kitą"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą „<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>“ matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, 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="1944832605534698562">"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ų rodyti ir srautu perduoti programas iš vieno įrenginio į kitą"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Leisti &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pasiekti šią informaciją iš jūsų telefono"</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_title_computer" msgid="4671071173916176037">"„Google Play“ paslaugos"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>“ vardu, kad galėtų pasiekti telefono nuotraukas, mediją ir pranešimus"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Leisti &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; atlikti šį veiksmą?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Leisti programai &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; srautu perduoti telefono programas ir sistemos funkcijas?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"„%1$s“ galės pasiekti visą telefone 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;„%1$s“ galės perduoti srautu programas ir sistemos funkcijas, kol pašalinsite prieigą prie šio leidimo."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"„<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ų srautu perduoti programas ir kitas sistemos funkcijas įrenginiams netoliese"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Leisti programai &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą <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 ir sistemos funkcijas į „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“, kol pašalinsite prieigą prie šio leidimo."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"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ų srautu perduoti programas ir kitas sistemos funkcijas iš vieno įrenginio į kitą"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Keisti medijos išvestį"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Nuotraukos ir medija"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Pranešimai"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Programos"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Srautinis perdavimas"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Skambinti ir tvarkyti telefonų skambučius"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Skaityti ir rašyti telefono skambučių žurnalą"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Siųsti ir peržiūrėti SMS pranešimus"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Rasti apytikslę netoliese esančių įrenginių poziciją, aptikti juos ir prisijungti prie jų"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Skaityti visus pranešimus, įskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Skaityti visus pranešimus, įskaitant tokią informaciją kaip kontaktai, pranešimai ir nuotraukos&lt;br/&gt;• Siųsti pranešimus&lt;br/&gt;&lt;br/&gt;Galite bet kada tvarkyti šios programos leidimą skaityti ir siųsti pranešimus skiltyje „Nustatymai“ &gt; „Pranešimai“."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefono programų perdavimas srautu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Srautu perduokite programas ir kitas sistemos funkcijas iš telefono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Pasiekti pasiekiamų įrenginių sąrašą ir valdyti, kuris leidžia garso arba vaizdo įrašo srautą arba perduoda įrašą iš kitų programų"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"planšetiniame kompiuteryje"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"įrenginys"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 20282b3..880b981 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Izvēlieties ierīci, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Jāizvēlas <xliff:g id="PROFILE_NAME">%1$s</xliff:g> iestatīšanai"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Šī lietotne drīkstēs sinhronizēt informāciju, piemēram, zvanītāja vārdu, un piekļūt norādītajām atļaujām jūsu ierīcē (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Šī lietotne drīkstēs sinhronizēt informāciju, piemēram, zvanītāja vārdu, un piekļūt šīm <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> atļaujām."</string>
     <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="2872254734959842579">"Šai lietotnei tiks sniegta piekļuve norādītajām atļaujām jūsu ierīcē (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 tālruņa"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; straumēt jūsu tālruņa lietotnes?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s varēs piekļūt visam saturam, kas ir redzams vai tiek atskaņots jūsu tālrunī, tostarp audio, fotoattēliem, parolēm un ziņojumiem.&lt;br/&gt;&lt;br/&gt;%1$s varēs straumēt lietotnes, līdz noņemsiet tās piekļuvi šai atļaujai."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Vairāku ierīču pakalpojumi"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju rādīt un straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju attēlot un straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <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="4693714143506569253">"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 tālruņa"</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_title_computer" msgid="4671071173916176037">"Google Play pakalpojumi"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju piekļūt jūsu tālruņa fotoattēliem, multivides saturam un paziņojumiem šīs ierīces vārdā: <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vai atļaut ierīcei &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; veikt šo darbību?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vai atļaut ierīcei &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; straumēt jūsu tālruņa lietotnes un sistēmas funkcijas?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s varēs piekļūt visam saturam, kas ir redzams vai tiek atskaņots jūsu tālrunī, tostarp audio, fotoattēliem, maksājumu informācijai, parolēm un ziņojumiem.&lt;br/&gt;&lt;br/&gt;%1$s varēs straumēt lietotnes un sistēmas funkcijas, līdz noņemsiet tās piekļuvi šai atļaujai."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju tuvumā esošās ierīcēs straumēt lietotnes un citas sistēmas funkcijas šīs ierīces vārdā: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vai atļaut lietotnei &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 un sistēmas funkcijas 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_nearby_device_streaming" msgid="8509848562931818793">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju straumēt lietotnes un citas sistēmas funkcijas starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DEVICE_NAME">%2$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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mainīt multivides izvadi"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotoattēli un multivides faili"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Paziņojumi"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Lietotnes"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Straumēšana"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Veikt un pārvaldīt tālruņa zvanus"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Lasīt un rakstīt tālruņa zvanu žurnālu"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Sūtīt un skatīt īsziņas"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrast tuvumā esošas ierīces, izveidot ar tām savienojumu un noteikt to relatīvo atrašanās vietu"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Lasīt visus paziņojumus, tostarp tādu informāciju kā kontaktpersonas, ziņojumi un fotoattēli"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Lasīt visus paziņojumus, tostarp tādu informāciju kā kontaktpersonas, ziņojumi un fotoattēli&lt;br/&gt;• Sūtīt paziņojumus&lt;br/&gt;&lt;br/&gt;Jebkurā brīdī sadaļā “Iestatījumi un paziņojumi” varat pārvaldīt šīs lietotnes atļauju lasīt un sūtīt paziņojumus."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Straumēt jūsu tālruņa lietotnes"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"No sava tālruņa straumējiet lietotnes un citas sistēmas funkcijas"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Piekļuve pieejamo ierīču sarakstam un iespēja kontrolēt, kura ierīce straumē vai apraida audio vai video no citām lietotnēm"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"tālrunī"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"planšetdatorā"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televizora"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ierīces"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index 6a162c0..219f9d14 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Изберете уред со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> за поставување"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Апликацијава ќе има дозвола да ги синхронизира податоците како што се имињата на јавувачите и да пристапува до следниве дозволи на вашиот <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Оваа апликација ќе има дозвола да ги синхронизира податоците како што се имињата на јавувачите и да пристапува до следниве дозволи на вашиот <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Овозможете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да пристапува до овие податоци на телефонот"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Да се дозволи &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да ги стримува апликациите од вашиот телефон?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ќе има пристап до сè што е видливо или репродуцирано на телефонот, вклучувајќи ги и аудиото, фотографиите, лозинките и пораките.&lt;br/&gt;&lt;br/&gt;%1$s ќе може да стримува апликации додека не ја повлечете дозволава."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Повеќенаменски услуги"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за да стримува апликации помеѓу вашите уреди"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за да прикажува и стримува апликации меѓу вашите уреди"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Да се дозволи &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="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ќе има пристап до сè што е видливо или репродуцирано на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including вклучувајќи ги и аудиото, фотографиите, лозинките и пораките.&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="1944832605534698562">"<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="4693714143506569253">"Дозволете &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да пристапува до овие податоци на телефонот"</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_title_computer" msgid="4671071173916176037">"Услуги на Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за да пристапува до фотографиите, аудиовизуелните содржини и известувањата на телефонот"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ќе дозволите &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; да го преземе ова дејство?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Да се дозволи &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; да ги стримува апликациите и системските функции од вашиот телефон?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ќе има пристап до сè што е видливо или репродуцирано на телефонот, вклучувајќи ги и аудиото, фотографиите, податоците за плаќање, лозинките и пораките.&lt;br/&gt;&lt;br/&gt;%1$s ќе може да стримува апликации и системски функции додека не ја повлечете дозволава."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации и други системски функции на уредите во близина"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Да се дозволи &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; да ги стримува апликациите и системските функции од <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Менување излез за аудио/видео"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Аудиовизуелни содржини"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Известувања"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Апликации"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Стриминг"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Упатува и управува со телефонски повици"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Чита и пишува евиденција на повици во телефонот"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Испраќа и прикажува SMS-пораки"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Наоѓа и се поврзува со уреди во близина и да ја утврдува нивната релативна положба"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ги чита сите известувања, меѓу кои и податоци како контакти, пораки и фотографии"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ги чита сите известувања, меѓу кои и податоци како контакти, пораки и фотографии&lt;br/&gt;• Испраќа известувања&lt;br/&gt;&lt;br/&gt;Може да управувате со способноста на апликацијава да чита и испраќа известувања кога било во „Поставки &gt; Известувања“."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Стримувајте ги апликациите на телефонот"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Апликации за стриминг и други системски функции од вашиот телефон"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Пристап до списокот со достапни уреди и контрола кои стримуваат или емитуваат аудио или видео од други апликации"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"Телефон"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"Таблет"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ТВ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"уред"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index b1934af..199f782 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു ഉപകരണം തിരഞ്ഞെടുക്കുക"</string>
     <string name="chooser_title" msgid="2235819929238267637">"സജ്ജീകരിക്കാൻ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ സമന്വയിപ്പിക്കാനും നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ ഈ അനുമതികൾ ആക്സസ് ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കും"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ സമന്വയിപ്പിക്കാനും നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഈ അനുമതികൾ ആക്സസ് ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കും"</string>
     <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="2872254734959842579">"നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ആപ്പിനെ അനുവദിക്കുക"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ഓഡിയോ, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ ഫോണിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാത്തിലേക്കും %1$s-ന് ആക്‌സസ് ഉണ്ടായിരിക്കും.&lt;br/&gt;&lt;br/&gt;നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ %1$s-ന് ആപ്പുകൾ സ്ട്രീം ചെയ്യാനാകും."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ക്രോസ്-ഉപകരണ സേവനങ്ങൾ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"നിങ്ങളുടെ ഉപകരണങ്ങളിൽ ഒന്നിൽ നിന്ന് അടുത്തതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> എന്നത് അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"നിങ്ങളുടെ ഉപകരണങ്ങൾക്കിടയിൽ ആപ്പുകൾ സ്ട്രീം ചെയ്യാനും പ്രദർശിപ്പിക്കാനും നിങ്ങളുടെ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> എന്നത് അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്‌സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"നിങ്ങളുടെ <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="1274464413649731829">"ഓഡിയോ, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <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="1944832605534698562">"നിങ്ങളുടെ ഉപകരണങ്ങൾക്കിടയിൽ ആപ്പുകൾ സ്ട്രീം ചെയ്യാനും പ്രദർശിപ്പിക്കാനും നിങ്ങളുടെ <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="4693714143506569253">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്‌സസ് ചെയ്യാൻ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ആപ്പിനെ അനുവദിക്കുക"</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_title_computer" msgid="4671071173916176037">"Google Play സേവനങ്ങൾ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"നിങ്ങളുടെ ഫോണിലെ ഫോട്ടോകൾ, മീഡിയ, അറിയിപ്പുകൾ എന്നിവ ആക്സസ് ചെയ്യാൻ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ഈ പ്രവർത്തനം നടത്താൻ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളെയും സ്ട്രീം ചെയ്യാൻ, &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ഓഡിയോ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിവരങ്ങൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ ഫോണിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാത്തിലേക്കും %1$s-ന് ആക്‌സസ് ഉണ്ടായിരിക്കും.&lt;br/&gt;&lt;br/&gt;നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ %1$s-ന് ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാനാകും."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"സമീപമുള്ള ഉപകരണങ്ങളിൽ ആപ്പുകളും മറ്റ് സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാൻ നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിന്റെ ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; എന്നതിലേക്ക് സ്ട്രീം ചെയ്യാൻ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; എന്നതിനെ അനുവദിക്കണോ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"ഓഡിയോ, ഫോട്ടോകൾ, പാസ്‌വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <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_nearby_device_streaming" msgid="8509848562931818793">"നിങ്ങളുടെ ഉപകരണങ്ങളിൽ ഒന്നിൽ നിന്ന് അടുത്തതിലേക്ക് ആപ്പുകളും മറ്റ് സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാൻ <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"മീഡിയ ഔട്ട്പുട്ട് മാറ്റുക"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ഫോട്ടോകളും മീഡിയയും"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"അറിയിപ്പുകൾ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ആപ്പുകൾ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"സ്ട്രീമിംഗ്"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ഫോൺ കോളുകൾ വിളിക്കുക, മാനേജ് ചെയ്യുക"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ഫോൺ കോൾ ചരിത്രം എഴുതുകയും വായിക്കുകയും ചെയ്യുക"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS സന്ദേശങ്ങൾ അയയ്ക്കുക, കാണുക"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"സമീപമുള്ള ഉപകരണങ്ങൾ കണ്ടെത്താനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും കഴിയും"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"കോൺടാക്‌റ്റുകൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഉൾപ്പെടെ എല്ലാ അറിയിപ്പുകളും വായിക്കുക"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• കോൺടാക്‌റ്റുകൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഉൾപ്പെടെ എല്ലാ അറിയിപ്പുകളും വായിക്കുക&lt;br/&gt;• അറിയിപ്പുകൾ അയയ്ക്കുക&lt;br/&gt;&lt;br/&gt;അറിയിപ്പുകൾ വായിക്കാനും അയയ്ക്കാനുമുള്ള ഈ ആപ്പിന്റെ ശേഷി ക്രമീകരണവും അറിയിപ്പുകളും എന്നതിൽ ഏതുസമയത്തും മാനേജ് ചെയ്യാം."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകൾ സ്‌ട്രീം ചെയ്യുക"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ആപ്പുകളും മറ്റ് സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാം"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ലഭ്യമായ ഉപകരണങ്ങളുടെ ലിസ്റ്റ് ആക്സസ് ചെയ്ത്, അവയിൽ ഏതാണ് മറ്റ് ആപ്പുകളിൽ നിന്ന് ഓഡിയോ അല്ലെങ്കിൽ വീഡിയോ സ്‌ട്രീം ചെയ്യേണ്ടത് അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യേണ്ടത് എന്ന് നിയന്ത്രിക്കുക"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ഫോൺ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ടാബ്‌ലെറ്റ്"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ടിവി"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ഉപകരണം"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index 0c4bfcb..94f862c 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-н удирдах төхөөрөмжийг сонгоно уу"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Тохируулахын тулд <xliff:g id="PROFILE_NAME">%1$s</xliff:g> сонгоно уу"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Энэ аппад залгаж буй хүний нэр зэрэг мэдээллийг синк хийх болон таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Энэ аппад залгаж буй хэн нэгний нэр зэрэг мэдээллийг синк хийх болон таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> дээрх эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string>
     <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="2872254734959842579">"Энэ апп таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандах эрхтэй байх болно"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Таны утасны аппуудыг дамжуулахыг &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>-д&lt;/strong&gt; зөвшөөрөх үү?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s аудио, зураг, нууц үг болон мессежүүдийг зэрэг утсан дээр харагдсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй болно.&lt;br/&gt;&lt;br/&gt;%1$s таныг энэ зөвшөөрлийг хасах хүртэл аппуудыг дамжуулах боломжтой байх болно."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Төхөөрөмж хоорондын үйлчилгээ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Таны төхөөрөмжүүд хооронд апп дамжуулахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс таны төхөөрөмжүүдийн хооронд аппууд үзүүлж, дамжуулах зөвшөөрлийг хүсэж байна"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Энэ аппад таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> дээрх эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</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_title_computer" msgid="4671071173916176037">"Google Play үйлчилгээ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Таны утасны зураг, медиа болон мэдэгдэлд хандахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;-д энэ үйлдлийг хийхийг зөвшөөрөх үү?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Таны утасны апп болон системийн онцлогуудыг дамжуулахыг &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-д&lt;/strong&gt; зөвшөөрөх үү?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s аудио, зураг, төлбөрийн мэдээлэл, нууц үг болон мессеж зэрэг утсан дээр харагдсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй болно.&lt;br/&gt;&lt;br/&gt;%1$s таныг энэ зөвшөөрлийг хасах хүртэл апп болон системийн онцлогуудыг дамжуулах боломжтой байх болно."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс аппууд болон системийн бусад онцлогийг ойролцоох төхөөрөмжүүд рүү дамжуулах зөвшөөрөл хүсэж байна"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;-д таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н апп болон системийн онцлогуудыг &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;-д дамжуулахыг зөвшөөрөх үү?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Медиа гаралтыг өөрчлөх"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Зураг болон медиа"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Мэдэгдэл"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Аппууд"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Дамжуулах"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Утасны дуудлага хийх болон удирдах"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Утасны дуудлагын жагсаалтыг унших болон бичих"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS мессеж илгээх болон харах"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Ойролцоох төхөөрөмжүүдийн харьцангуй байрлалыг олох, тодорхойлох болон тэдгээрт холбогдох"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Харилцагчид, мессеж болон зургууд зэрэг мэдээллийг оруулаад бүх мэдэгдлийг унших"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Харилцагчид, мессеж болон зургууд зэрэг мэдээллийг оруулаад бүх мэдэгдлийг унших&lt;br/&gt;• Мэдэгдлүүдийг илгээх&lt;br/&gt;&lt;br/&gt;Та энэ аппын мэдэгдлүүдийг унших болон илгээх чадамжийг Тохиргоо &gt; мэдэгдлүүдэд хүссэн үедээ удирдах боломжтой."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Утасныхаа аппуудыг дамжуулаарай"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Утаснаасаа аппууд болон системийн бусад онцлогийг дамжуулаарай"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Боломжтой төхөөрөмжүүдийн жагсаалтад хандаж, аль нь бусад аппаас аудио эсвэл видео дамжуулах, нэвтрүүлэхийг хянана уу"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"утас"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"таблет"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"тв"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"төхөөрөмж"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 52a7a55..c758d29 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -21,30 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी डिव्हाइस निवडा"</string>
     <string name="chooser_title" msgid="2235819929238267637">"सेट करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"या अ‍ॅपला कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करण्याची आणि तुमच्या <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर या परवानग्या अ‍ॅक्सेस करण्याची अनुमती असेल"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"या अ‍ॅपला कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करण्याची आणि तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर पुढील परवानग्या अ‍ॅक्सेस करण्याची अनुमती दिली जाईल"</string>
     <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="2872254734959842579">"या अ‍ॅपला तुमच्या <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर या परवानग्या अ‍ॅक्सेस करण्याची अनुमती असेल"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही माहिती तुमच्या फोनवरून अ‍ॅक्सेस करण्यासाठी अनुमती द्या"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला तुमच्या फोनची अ‍ॅप्स स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for summary_app_streaming (295548145144086753) -->
-    <skip />
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिव्हाइस सेवा"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"तुमच्या डिव्हाइसदरम्यान ॲप्स स्ट्रीम करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"तुमच्या डिव्हाइसदरम्यान ॲप्स दाखवण्यासाठी आणि स्ट्रीम करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"या अ‍ॅपला तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर या परवानग्या अ‍ॅक्सेस करण्याची अनुमती दिली जाईल"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"तुमच्या डिव्हाइसदरम्यान ॲप्स दाखवण्यासाठी आणि स्ट्रीम करण्यासाठी <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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही माहिती तुमच्या फोनवरून अ‍ॅक्सेस करण्यासाठी अनुमती द्या"</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_title_computer" msgid="4671071173916176037">"Google Play सेवा"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"तुमच्या फोनमधील फोटो, मीडिया आणि सूचना ॲक्सेस करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ला ही कृती करण्याची अनुमती द्यायची आहे का?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ला तुमच्या फोनची अ‍ॅप्स आणि सिस्टीमची वैशिष्ट्ये स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for summary_nearby_device_streaming (4039565463149145573) -->
-    <skip />
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे जवळपासच्या डिव्हाइसवर अ‍ॅप्स आणि इतर सिस्टीम वैशिष्‍ट्ये स्ट्रीम करण्यासाठी तुमच्या <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करा"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; ला तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> अ‍ॅप्स आणि सिस्टीमची वैशिष्ट्ये &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;वर स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"तुमच्या डिव्हाइसदरम्यान ॲप्स आणि इतर सिस्टीम वैशिष्‍ट्ये स्ट्रीम करण्यासाठी <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>
@@ -66,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदला"</string>
     <string name="permission_storage" msgid="6831099350839392343">"फोटो आणि मीडिया"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"सूचना"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ॲप्स"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रीमिंग"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"फोन कॉल करणे आणि ते व्यवस्थापित करणे"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"फोन कॉल लॉग रीड अँड राइट करणे"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"एसएमएस पाठवणे आणि पाहणे"</string>
@@ -77,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"जवळपासची डिव्हाइस शोधणे, त्यांच्याशी कनेक्ट करणे आणि त्यांचे संबंधित स्थान निर्धारित करणे"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"संपर्क, मेसेज आणि फोटो यांसारख्या माहितीसह सर्व सूचना वाचणे"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• संपर्क, मेसेज आणि फोटोसारख्या माहितीसह सर्व सूचना वाचणे&lt;br/&gt;• सूचना पाठवणे&lt;br/&gt;&lt;br/&gt;तुम्ही या अ‍ॅपची सूचना वाचण्याची आणि पाठवण्याची क्षमता सेटिंग्ज &gt; सूचना मध्ये कधीही व्यवस्थापित करू शकता."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"तुमच्या फोनवरील ॲप्स स्ट्रीम करा"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"तुमच्या फोनवरून अ‍ॅप्स आणि इतर सिस्टीम वैशिष्‍ट्ये स्ट्रीम करा"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"उपलब्ध डिव्‍हाइसची सूची अ‍ॅक्सेस करा आणि इतर कोणत्‍या अ‍ॅप्सवरून ऑडिओ किंवा व्हिडिओ स्ट्रीम केला अथवा कास्‍ट केला हे नियंत्रित करा"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"फोन"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"टॅबलेट"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"टीव्ही"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"डिव्हाइस"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index 21132a9..424b815b 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pilih peranti untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk disediakan"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Apl ini akan dibenarkan untuk menyegerakkan maklumat seperti nama individu yang membuat panggilan dan mengakses kebenaran ini pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> anda"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Apl ini akan dibenarkan untuk menyegerakkan maklumat, seperti nama individu yang membuat panggilan dan mengakses kebenaran ini pada <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> anda"</string>
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengurus &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"peranti"</string>
-    <string name="summary_glasses" msgid="2872254734959842579">"Apl ini akan dibenarkan untuk mengakses kebenaran yang berikut pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> anda"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; mengakses maklumat ini daripada telefon anda"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk menstrim apl telefon anda?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada telefon termasuk audio, foto, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;%1$s akan dapat menstrim apl sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Perkhidmatan silang peranti"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> anda untuk menstrim apl antara peranti anda"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> anda untuk memaparkan dan menstrim apl antara peranti anda"</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="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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>, termasuk audio, foto, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> anda untuk memaparkan dan menstrim apl antara peranti anda"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses maklumat ini daripada telefon anda"</string>
+    <string name="title_computer" msgid="4782923323932440751">"Benarkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengakses maklumat ini daripada <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
-    <string name="helper_title_computer" msgid="4671071173916176037">"Perkhidmatan Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> anda untuk mengakses foto, media dan pemberitahuan telefon anda"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Benarkan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; mengambil tindakan ini?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Benarkan &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk menstrim apl dan ciri sistem telefon anda?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s akan mendapat akses kepada semua perkara yang dipaparkan atau dimainkan pada telefon anda termasuk audio, foto, maklumat pembayaran, kata laluan dan mesej.&lt;br/&gt;&lt;br/&gt;%1$s akan dapat menstrim apl dan ciri sistem sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 menstrim apl dan ciri sistem yang lain pada peranti berdekatan"</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="4295322493408411976">"Benarkan &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 dan ciri sistem kepada <xliff:g id="DEVICE_NAME">%3$s</xliff:g> sehingga anda mengalih keluar akses kepada kebenaran ini."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 menstrim apl dan ciri sistem lain antara peranti 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>
     <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Tukar output media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Pemberitahuan"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apl"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Penstriman"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Membuat dan mengurus panggilan telefon"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Baca dan tulis log panggilan telefon"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Menghantar dan melihat mesej SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Mencari, menyambung dan menentukan kedudukan relatif peranti berdekatan"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Membaca semua pemberitahuan, termasuk maklumat seperti kenalan, mesej dan foto&lt;br/&gt;• Menghantar pemberitahuan&lt;br/&gt;&lt;br/&gt;Anda boleh mengurus keupayaan apl ini untuk membaca dan menghantar pemberitahuan pada bila-bila masa dalam Tetapan &gt; Pemberitahuan."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strim apl telefon anda"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strim apl dan ciri sistem yang lain daripada telefon anda"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Akses senarai peranti tersedia dan kawal peranti yang menstrim atau menghantar audio atau video daripada apl lain"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"peranti"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 12242b9..6185f314 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် စက်တစ်ခုကို ရွေးပါ"</string>
     <string name="chooser_title" msgid="2235819929238267637">"စနစ်ထည့်သွင်းရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးပါ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်ရန်နှင့် သင့် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်ရန်နှင့် သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string>
     <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="2872254734959842579">"သင့် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုမည်"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သင့်ဖုန်း၏ အက်ပ်များ တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s သည် အသံ၊ ဓာတ်ပုံ၊ စကားဝှက်၊ မက်ဆေ့ဂျ် အပါအဝင် ဖုန်းတွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။&lt;br/&gt;&lt;br/&gt;ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း %1$s သည် အက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"စက်များကြားသုံး ဝန်ဆောင်မှုများ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်စက်များအကြား အက်ပ်များ ပြပြီး တိုက်ရိုက်ဖွင့်ရန် <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; အား သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုခြင်း"</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_title_computer" msgid="4671071173916176037">"Google Play ဝန်ဆောင်မှုများ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်ဖုန်း၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို ဤသို့လုပ်ဆောင်ခွင့်ပြုမလား။"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သင့်ဖုန်း၏ အက်ပ်နှင့် စနစ်အင်္ဂါရပ်များ တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s သည် အသံ၊ ဓာတ်ပုံ၊ ငွေချေအချက်အလက်၊ စကားဝှက်နှင့် မက်ဆေ့ဂျ်များ အပါအဝင် ဖုန်းတွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။&lt;br/&gt;&lt;br/&gt;ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း %1$s သည် အက်ပ်နှင့် စနစ်အင်္ဂါရပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် အနီးတစ်ဝိုက်ရှိ အက်ပ်များနှင့် အခြားစနစ်အင်္ဂါရပ်များကို တိုက်ရိုက်ဖွင့်ရန် သင့် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ၏ အက်ပ်နှင့် စနစ်တူးလ်များကို &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; တွင် တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"မီဒီယာအထွက် ပြောင်းခြင်း"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ဓာတ်ပုံနှင့် မီဒီယာများ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"အကြောင်းကြားချက်များ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"အက်ပ်များ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"တိုက်ရိုက်ဖွင့်ခြင်း"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်နိုင်၊ စီမံနိုင်သည်"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ဖုန်းခေါ်ဆိုမှတ်တမ်းကို ဖတ်နိုင်၊ ရေးနိုင်သည်"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS မက်ဆေ့ဂျ်များ ပို့နိုင်၊ ကြည့်နိုင်သည်"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"အနီးတစ်ဝိုက်ရှိ စက်များ၏ ဆက်စပ်နေရာကို ရှာခြင်း၊ ချိတ်ဆက်ခြင်းနှင့် သတ်မှတ်ခြင်းတို့ လုပ်နိုင်သည်"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"အဆက်အသွယ်၊ မက်ဆေ့ဂျ်နှင့် ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များအပါအဝင် အကြောင်းကြားချက်အားလုံးကို ဖတ်နိုင်သည်"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• အဆက်အသွယ်၊ မက်ဆေ့ဂျ်နှင့် ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များအပါအဝင် အကြောင်းကြားချက်အားလုံးကို ဖတ်ခြင်း&lt;br/&gt;• အကြောင်းကြားချက်များ ပို့ခြင်း&lt;br/&gt;&lt;br/&gt;ဆက်တင်များ &gt; အကြောင်းကြားချက်များ တွင် အကြောင်းကြားချက်များအား ဤအက်ပ်၏ ဖတ်ခွင့်နှင့် ပို့ခွင့်ကို အချိန်မရွေး စီမံနိုင်သည်။"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"သင့်ဖုန်းရှိအက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်သည်"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"သင့်ဖုန်းမှ အက်ပ်များနှင့် အခြားစနစ်အင်္ဂါရပ်များကို တိုက်ရိုက်ဖွင့်သည်"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"အခြားအက်ပ်များတွင် အသံ (သို့) ဗီဒီယို တိုက်ရိုက်ဖွင့်၍ (သို့) ကာစ်လုပ်၍ ရနိုင်သော စက်ပစ္စည်းစာရင်းကို ဝင်ကြည့်ပြီး ရွေးလိုက်သည့်တစ်ခုကို ထိန်းချုပ်နိုင်သည်"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ဖုန်း"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"တက်ဘလက်"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"စက်"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index 4605c18..00700ec 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Velg en enhet som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal konfigureres"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Denne appen får tillatelse til å synkronisere informasjon som navnet til noen som ringer, og har disse tillatelsene på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Denne appen får tillatelse til å synkronisere informasjon, for eksempel navnet til folk som ringer, og har disse tillatelsene på <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Denne appen får disse tillatelsene på enheten din (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Gi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilgang til denne informasjonen fra telefonen din"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vil du tillate at &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; strømmer appene på telefonen?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s får tilgang til alt som er synlig eller spilles av på telefonen, inkludert lyd, bilder, passord og meldinger.&lt;br/&gt;&lt;br/&gt;%1$s kan strømme apper til du fjerner denne tillatelsen."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester på flere enheter"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper mellom enhetene dine, på vegne av <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å vise og strømme apper mellom enhetene dine, på vegne av <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Denne appen får disse tillatelsene på <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får tilgang til alt som vises eller spilles av på <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inkludert lyd, bilder, passord og meldinger.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> får muligheten til å 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å vise og strømme apper mellom enhetene dine, 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="4693714143506569253">"Gi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tilgang til denne informasjonen fra telefonen din"</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_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å få tilgang til bilder, medier og varsler på telefonen din, på vegne av <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vil du la &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; gjøre dette?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vil du tillate at &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; strømmer appene og systemfunksjonene på telefonen."</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s får tilgang til alt som er synlig eller spilles av på telefonen, inkludert lyd, bilder, betalingsopplysninger, passord og meldinger.&lt;br/&gt;&lt;br/&gt;%1$s kan strømme apper og systemfunksjoner til du fjerner denne tillatelsen."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til å strømme apper og andre systemfunksjoner til enheter i nærheten"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vil du la &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får tilgang til 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> får muligheten til å strømme apper og systemfunksjoner til <xliff:g id="DEVICE_NAME">%3$s</xliff:g> frem til du fjerner tilgangen til denne tillatelsen."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper og andre systemfunksjoner mellom enhetene dine 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Bytt medieutgang"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Bilder og medier"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Varsler"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apper"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strømming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Ring og administrer anrop"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Les og skriv samtalelogg"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Send og les SMS-meldinger"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Finn, koble til og fastslå den relative posisjonen til enheter i nærheten"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Les alle varsler, inkludert informasjon som kontakter, meldinger og bilder&lt;br/&gt;• Send varsler&lt;br/&gt;&lt;br/&gt;Du kan når som helst administrere om denne appen kan lese og sende varsler, i Innstillinger &gt; Varsler."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Strøm appene på telefonen"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Strøm apper og andre systemfunksjoner fra telefonen"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Se en liste over tilgjengelige enheter, og kontroller hvilken enhet som strømmer eller caster lyd eller video fra andre apper"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"nettbrett"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV-en"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"enheten"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index fc28789..1af4d85 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको डिभाइस चयन गर्नुहोस्"</string>
     <string name="chooser_title" msgid="2235819929238267637">"सेट अप गर्नका लागि <xliff:g id="PROFILE_NAME">%1$s</xliff:g> छनौट गर्नुहोस्"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"तपाईंको <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा यो एपलाई कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्ने र यी कुराहरू गर्ने अनुमति दिइने छ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्ने र यी कुराहरू गर्ने अनुमति दिइने छ"</string>
     <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="2872254734959842579">"तपाईंको <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएका एपहरू स्ट्रिम गर्न दिने हो?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ले तपाईंको फोनमा देखिने वा प्ले गरिने अडियो, फोटो, पासवर्ड र म्यासेजलगायतका सबै कुरा प्रयोग गर्न सक्ने छ।&lt;br/&gt;&lt;br/&gt;तपाईंले यो अनुमति रद्द नगरेसम्म %1$s ले एपहरू स्ट्रिम गर्न पाइराख्ने छ।"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रस-डिभाइस सेवाहरू"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> को तर्फबाट तपाईंका कुनै एउटा डिभाइसबाट अर्को डिभाइसमा एप स्ट्रिम गर्ने अनुमति माग्दै छ"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> को तर्फबाट तपाईंका कुनै एउटा डिभाइसबाट अर्को डिभाइसमा एप देखाउने तथा स्ट्रिम गर्ने अनुमति माग्दै छ"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Allow &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</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_title_computer" msgid="4671071173916176037">"Google Play services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको फोनमा भएका फोटो, मिडिया र सूचनाहरू हेर्ने तथा प्रयोग गर्ने अनुमति माग्दै छ"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई यो कार्य गर्ने अनुमति दिने हो?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको फोनमा भएका एप तथा सिस्टमका सुविधाहरू स्ट्रिम गर्न दिने हो?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ले तपाईंको फोनमा देखिने वा प्ले गरिने अडियो, फोटो, भुक्तानीसम्बन्धी जानकारी, पासवर्ड र म्यासेजलगायतका सबै कुरा प्रयोग गर्न सक्ने छ।&lt;br/&gt;&lt;br/&gt;तपाईंले यो अनुमति रद्द नगरेसम्म %1$s ले एप तथा सिस्टमका सुविधाहरू स्ट्रिम गर्न पाइराख्ने छ।"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट नजिकैका डिभाइसहरूमा एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएका एप तथा सिस्टमका सुविधाहरू &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; मा स्ट्रिम गर्न दिने हो?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"मिडिया आउटपुट बदल्नुहोस्"</string>
     <string name="permission_storage" msgid="6831099350839392343">"फोटो र मिडिया"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"सूचनाहरू"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"एपहरू"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"स्ट्रिमिङ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"फोन कल गर्ने र व्यवस्थापन गर्ने"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"फोन कलको लग रिड र राइट गर्ने"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS म्यासेज पठाउने र हेर्ने"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"नजिकैका डिभाइसहरू भेट्टाउने, ती डिभाइससँग कनेक्ट गर्ने र तिनको सापेक्ष स्थिति निर्धारण गर्ने"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"कन्ट्याक्ट, म्यासेज र फोटो जस्ता जानकारीलगायतका सबै सूचनाहरू रिड गर्ने"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• कन्ट्याक्ट, म्यासेज र फोटो जस्ता जानकारीलगायतका सबै सूचनाहरू रिड गर्ने&lt;br/&gt;• सूचनाहरू पठाउने&lt;br/&gt;&lt;br/&gt;तपाईं जुनसुकै बेला सेटिङ &gt; सूचनाहरू खण्डमा गई यो एपलाई सूचनाहरू रिड गर्न र पठाउन दिने कि नदिने भन्ने कुरा व्यवस्थापन गर्न सक्नुहुन्छ।"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"आफ्नो फोनका एपहरू प्रयोग गर्नुहोस्"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"आफ्नो फोनबाट एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्नुहोस्"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"उपलब्ध डिभाइसहरूको सूची एक्सेस गर्नुहोस् र अन्य एपका अडियो वा भिडियो कुन डिभाइसबाट कास्ट गर्ने भन्ने कुरा नियन्त्रण गर्नुहोस्"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"फोन"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ट्याब्लेट"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"टिभी"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"डिभाइस"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 4b17c7d..ad1cfe0 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Een apparaat kiezen om te beheren met &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om in te stellen"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Deze app kan informatie synchroniseren (zoals de naam van iemand die belt) en krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Deze app kan informatie synchroniseren (zoals de naam van iemand die belt) en krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Deze app krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je telefoon"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan de apps van je telefoon te streamen?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s heeft toegang tot alles wat zichtbaar is of wordt afgespeeld op de telefoon, waaronder audio, foto\'s, wachtwoorden en berichten.&lt;br/&gt;&lt;br/&gt;%1$s kan apps streamen totdat je dit recht verwijdert."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toestemming om apps tussen je apparaten weer te geven en te streamen"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps tussen je apparaten weer te geven en te streamen"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je telefoon"</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_title_computer" msgid="4671071173916176037">"Google Play-services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je telefoon"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Toestaan dat &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; deze actie uitvoert?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; toestaan de apps en systeemfuncties van je telefoon te streamen?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s heeft toegang tot alles wat zichtbaar is of wordt afgespeeld op je telefoon, waaronder audio, foto\'s, betalingsgegevens, wachtwoorden en berichten.&lt;br/&gt;&lt;br/&gt;%1$s kan apps en systeemfuncties streamen totdat je dit recht verwijdert."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens je <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en andere systeemfuncties naar apparaten in de buurt te streamen"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; te streamen?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 en systeemfuncties naar <xliff:g id="DEVICE_NAME">%3$s</xliff:g> streamen totdat je dit recht verwijdert."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en andere systeemfuncties te streamen tussen je apparaten"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media-uitvoer wijzigen"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Meldingen"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Bellen en gesprekken beheren"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefoongesprekslijst lezen en schrijven"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Sms-berichten sturen en bekijken"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Apparaten in de buurt vinden, er verbinding mee maken en de relatieve positie ervan bepalen"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Alle meldingen lezen, waaronder informatie zoals contacten, berichten en foto\'s&lt;br/&gt;• Meldingen sturen&lt;br/&gt;&lt;br/&gt;Je kunt de mogelijkheden van deze app om meldingen te lezen en te sturen, beheren wanneer je wilt via Instellingen &gt; Meldingen."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Stream de apps van je telefoon"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Apps en andere systeemfuncties streamen vanaf je telefoon"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Krijg toegang tot een lijst met beschikbare apparaten en beheer welk apparaat audio of video streamt of cast via andere apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefoon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"apparaat"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index c134cf1..58c6476 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ ଡିଭାଇସ ବାଛନ୍ତୁ"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ସେଟ ଅପ କରିବାକୁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ବାଛନ୍ତୁ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏବଂ ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏବଂ ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string>
     <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="2872254734959842579">"ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ ଫୋନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ %1$sର ଆକ୍ସେସ ରହିବ।&lt;br/&gt;&lt;br/&gt;ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ %1$s ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"କ୍ରସ-ଡିଭାଇସ ସେବାଗୁଡ଼ିକ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଡିସପ୍ଲେ ଏବଂ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"ଆପଣଙ୍କ <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="1274464413649731829">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ <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="1944832605534698562">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଡିସପ୍ଲେ ଏବଂ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <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="4693714143506569253">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</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_title_computer" msgid="4671071173916176037">"Google Play ସେବାଗୁଡ଼ିକ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"ଆପଣଙ୍କ ଫୋନର ଫଟୋ, ମିଡିଆ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ଏହି ପଦକ୍ଷେପ ନେବା ପାଇଁ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ଅଡିଓ, ଫଟୋ, ପେମେଣ୍ଟ ସୂଚନା, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ ଆପଣଙ୍କ ଫୋନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ %1$sର ଆକ୍ସେସ ରହିବ।&lt;br/&gt;&lt;br/&gt;ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ %1$s ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକରେ ଆପ୍ସ ଏବଂ ଅନ୍ୟ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ର ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;ରେ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"ଅଡିଓ, ଫଟୋ, ପେମେଣ୍ଟ ସୂଚନା, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ ଆପଣଙ୍କ <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_nearby_device_streaming" msgid="8509848562931818793">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସ ଏବଂ ଅନ୍ୟ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ମିଡିଆ ଆଉଟପୁଟ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ଫଟୋ ଏବଂ ମିଡିଆ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ଆପ୍ସ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ଷ୍ଟ୍ରିମିଂ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ଫୋନ କଲଗୁଡ଼ିକ କରିବା ଏବଂ ସେଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ଫୋନ କଲ ଲଗକୁ ପଢ଼ିବା ଏବଂ ଲେଖିବା"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ମେସେଜ ପଠାଇବା ଏବଂ ଭ୍ୟୁ କରିବା"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକୁ ଖୋଜିବା, କନେକ୍ଟ କରିବା ଏବଂ ସେଗୁଡ଼ିକର ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କରିବା"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"କଣ୍ଟାକ୍ଟ, ମେସେଜ ଏବଂ ଫଟୋଗୁଡ଼ିକ ପରି ସୂଚନା ସମେତ ସମସ୍ତ ବିଜ୍ଞପ୍ତିକୁ ପଢ଼ନ୍ତୁ"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• କଣ୍ଟାକ୍ଟ, ମେସେଜ ଏବଂ ଫଟୋ ପରି ସୂଚନା ସମେତ ସମସ୍ତ ବିଜ୍ଞପ୍ତିକୁ ପଢ଼ନ୍ତୁ&lt;br/&gt;• ବିଜ୍ଞପ୍ତି ପଠାନ୍ତୁ&lt;br/&gt;&lt;br/&gt;ଆପଣ ସେଟିଂସ &gt; ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକରେ ଯେ କୌଣସି ସମୟରେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଢିବା ଏବଂ ପଠାଇବା ପାଇଁ ଏହି ଆପର କ୍ଷମତାକୁ ପରିଚାଳନା କରିପାରିବେ।"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ଆପଣଙ୍କ ଫୋନରୁ ଆପ୍ସ ଏବଂ ଅନ୍ୟ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ଉପଲବ୍ଧ ଡିଭାଇସଗୁଡ଼ିକର ଏକ ତାଲିକାକୁ ଆକ୍ସେସ କରନ୍ତୁ ଏବଂ କେଉଁ ଡିଭାଇସ ଅନ୍ୟ ଆପ୍ସରୁ ଅଡିଓ କିମ୍ୱା ଭିଡିଓ ଷ୍ଟ୍ରିମ ବା କାଷ୍ଟ କରିବ ତାହା ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ଫୋନ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ଟାବଲେଟ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ଟିଭି"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ଡିଭାଇସ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index 448362f..d80c667 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ ਕੋਈ ਡੀਵਾਈਸ ਚੁਣੋ"</string>
     <string name="chooser_title" msgid="2235819929238267637">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰਨ ਅਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰਨ ਅਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
     <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="2872254734959842579">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ਕੀ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।&lt;br/&gt;&lt;br/&gt;%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗਾ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"ਕ੍ਰਾਸ-ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਦਿਖਾਉਣ ਅਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"ਕੀ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</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_title_computer" msgid="4671071173916176037">"Google Play ਸੇਵਾਵਾਂ"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ਕੀ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ਕੀ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, ਫ਼ੋਨ \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।&lt;br/&gt;&lt;br/&gt;%1$s ਐਪਾਂ ਨੂੰ ਉਦੋਂ ਤੱਕ ਸਟ੍ਰੀਮ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕੇਗਾ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਹਟਾ ਦਿੰਦੇ।"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ \'ਤੇ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"ਕੀ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀਆਂ ਐਪਾਂ ਨੂੰ &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"ਮੀਡੀਆ ਆਊਟਪੁੱਟ ਬਦਲੋ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਮੀਡੀਆ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"ਸੂਚਨਾਵਾਂ"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ਐਪਾਂ"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ਸਟ੍ਰੀਮਿੰਗ"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ਫ਼ੋਨ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਉਨ੍ਹਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ਫ਼ੋਨ ਦੇ ਕਾਲ ਲੌਗ ਨੂੰ ਪੜ੍ਹਣ ਅਤੇ ਲਿਖਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS ਸੁਨੇਹੇ ਭੇਜਣ ਅਤੇ ਦੇਖਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਨੂੰ ਲੱਭਣ, ਉਨ੍ਹਾਂ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ, ਜਿਵੇਂ ਕਿ ਸੰਪਰਕਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਫ਼ੋਟੋਆਂ ਦੀ ਜਾਣਕਾਰੀ"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ, ਜਿਵੇਂ ਕਿ ਸੰਪਰਕਾਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਫ਼ੋਟੋਆਂ ਦੀ ਜਾਣਕਾਰੀ&lt;br/&gt;• ਸੂਚਨਾਵਾਂ ਭੇਜਣ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ&lt;br/&gt;&lt;br/&gt;ਤੁਸੀਂ ਸੈਟਿੰਗਾਂ &gt; ਸੂਚਨਾਵਾਂ ਵਿੱਚ ਜਾ ਕੇ ਕਿਸੇ ਵੀ ਵੇਲੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਪੜ੍ਹਨ ਅਤੇ ਭੇਜਣ ਦੀ ਇਸ ਐਪ ਦੀ ਯੋਗਤਾ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹੋ।"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ਆਪਣੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ਆਪਣੇ ਫ਼ੋਨ ਤੋਂ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"ਉਪਲਬਧ ਡੀਵਾਈਸਾਂ ਦੀ ਸੂਚੀ ਤੱਕ ਪਹੁੰਚ ਕਰੋ ਅਤੇ ਇਹ ਕੰਟਰੋਲ ਕਰੋ ਕਿ ਕਿਹੜਾ ਡੀਵਾਈਸ ਹੋਰ ਐਪਾਂ ਤੋਂ ਆਡੀਓ ਜਾਂ ਵੀਡੀਓ ਨੂੰ ਸਟ੍ਰੀਮ ਜਾਂ ਕਾਸਟ ਕਰ ਸਕਦਾ ਹੈ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ਫ਼ੋਨ"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ਟੈਬਲੈੱਟ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ਟੀਵੀ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"ਡੀਵਾਈਸ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml
index 7579678..42a05b4 100644
--- a/packages/CompanionDeviceManager/res/values-pl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Wybierz urządzenie, którym ma zarządzać aplikacja &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, aby go skonfigurować"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aplikacja będzie mogła synchronizować informacje takie jak nazwa dzwoniącego oraz korzystać z tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aplikacja będzie mogła synchronizować informacje takie jak nazwa dzwoniącego oraz korzystać z tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
     <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="2872254734959842579">"Aplikacja będzie miała dostęp do tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Zezwól urządzeniu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na dostęp do tych informacji na Twoim telefonie"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Zezwolić aplikacji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na strumieniowanie aplikacji z telefonu?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s będzie mieć dostęp do wszystkiego, co jest widoczne i odtwarzane na telefonie, w tym do dźwięku, zdjęć, haseł i wiadomości.&lt;br/&gt;&lt;br/&gt;%1$s będzie w stanie strumieniować aplikacje, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usługi na innym urządzeniu"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o uprawnienia dotyczące strumieniowego odtwarzania treści z aplikacji na innym urządzeniu"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o pozwolenie na wyświetlanie i strumieniowanie aplikacji między Twoimi urządzeniami"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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ęć, 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="1944832605534698562">"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 wyświetlanie i strumieniowanie aplikacji między Twoimi urządzeniami"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"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 telefonie"</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_title_computer" msgid="4671071173916176037">"Usługi Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o uprawnienia dotyczące dostępu do zdjęć, multimediów i powiadomień na telefonie"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Zezwolić urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; na wykonanie tego działania?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Zezwolić urządzeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; na strumieniowanie aplikacji i funkcji systemowych z telefonu?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s będzie mieć dostęp do wszystkiego, co jest widoczne i odtwarzane na telefonie, w tym do dźwięku, zdjęć, danych do płatności, haseł i wiadomości.&lt;br/&gt;&lt;br/&gt;%1$s będzie w stanie strumieniować aplikacje i funkcje systemowe, dopóki nie usuniesz dostępu do tego uprawnienia."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"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 do strumieniowego odtwarzania treści i innych funkcji systemowych na urządzeniach w pobliżu"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Zezwolić aplikacji &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 i funkcje systemowe 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_nearby_device_streaming" msgid="8509848562931818793">"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 innych funkcji systemowych między Twoimi urządzeniami"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Zmień wyjście multimediów"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Zdjęcia i multimedia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Powiadomienia"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacje"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Strumieniowanie"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Nawiązywanie połączeń telefonicznych i zarządzanie nimi"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Odczytywanie i zapisywanie rejestru połączeń telefonicznych"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Wysyłanie i wyświetlanie SMS‑ów"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Znajdowanie urządzeń w pobliżu, określanie ich względnego położenia oraz łączenie się z nimi"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Odczytywanie wszystkich powiadomień, w tym informacji takich jak kontakty, wiadomości i zdjęcia"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Odczytywanie wszystkich powiadomień, w tym informacji takich jak kontakty, wiadomości i zdjęcia&lt;br/&gt;• Wysyłanie powiadomień&lt;br/&gt;&lt;br/&gt;W każdej chwili możesz zmienić uprawnienia tej aplikacji do odczytywania i wysyłania powiadomień, klikając Ustawienia &gt; Powiadomienia."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Odtwarzaj strumieniowo aplikacje z telefonu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Aplikacje do odtwarzania strumieniowego i inne funkcje systemowe na Twoim telefonie"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Uzyskaj dostęp do listy dostępnych urządzeń i określ, które z nich ma strumieniować lub przesyłać ścieżkę audio bądź wideo z innych aplikacji"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"telewizorze"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"urządzeniu"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index eb7b533..1ef9635 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos apps do smartphone?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, senhas e mensagens. O app &lt;br/&gt;&lt;br/&gt;%1$s poderá fazer o streaming de apps até que você remova o acesso a essa permissão."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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, 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="1944832605534698562">"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 mostrar e fazer streaming de apps entre seus dispositivos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Autorizar que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</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_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; realize esta ação?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming de apps e recursos do sistema do smartphone?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. &lt;br/&gt;&lt;br/&gt;O %1$s poderá acessar e transferir informações de apps e recursos do sistema até que você remova essa permissão."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 e recursos do sistema 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_nearby_device_streaming" msgid="8509848562931818793">"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 fazer streaming de aplicativos e outros recursos do sistema entre seus dispositivos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos&lt;br/&gt;• Enviar notificações&lt;br/&gt;&lt;br/&gt;Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Acesse uma lista de dispositivos disponíveis e controle quais deles podem reproduzir ou transmitir áudio ou vídeo de outros apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"smartphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index c951334..8a464e3 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Escolha um perfil de <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, e aceder a estas autorizações no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, e aceder a estas autorizações no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 telemóvel"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça stream das apps do telemóvel?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"O dispositivo %1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, palavras-passe e mensagens.&lt;br/&gt;&lt;br/&gt;O %1$s vai poder fazer stream de apps até remover o acesso a esta autorização."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para apresentar e fazer stream de apps entre os seus dispositivos"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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_TYPE">%2$s</xliff:g>, incluindo áudio, fotos, 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="1944832605534698562">"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 apresentar e fazer stream de apps entre os seus dispositivos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permita que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aceda a estas informações do seu telemóvel"</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_title_computer" msgid="4671071173916176037">"Serviços do Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu telemóvel"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; faça esta ação?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; faça stream das apps e das funcionalidades do sistema do telemóvel?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O dispositivo %1$s vai ter acesso a tudo o que seja visível ou reproduzido no telemóvel, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.&lt;br/&gt;&lt;br/&gt;O %1$s vai poder fazer stream de apps e funcionalidades do sistema até remover o acesso a esta autorização."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"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 outras funcionalidades do sistema para dispositivos próximos"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 e funcionalidades do sistema 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_nearby_device_streaming" msgid="8509848562931818793">"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 fazer stream de apps e outras funcionalidades do sistema entre os seus dispositivos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Alterar a saída de multimédia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e multimédia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerir chamadas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e escrever o registo de chamadas do telemóvel"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e ver mensagens SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, estabelecer ligação e determinar a posição relativa dos dispositivos próximos"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contactos, mensagens e fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como contactos, mensagens e fotos&lt;br/&gt;• Enviar notificações&lt;br/&gt;&lt;br/&gt;Pode gerir a capacidade de esta app ler e enviar notificações em qualquer altura em Definições &gt; Notificações."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Faça stream das apps do telemóvel"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Faça stream de apps e outras funcionalidades do sistema a partir do telemóvel"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Aceda a uma lista de dispositivos disponíveis e controle qual deles faz stream ou transmite áudio ou vídeo de outras apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telemóvel"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index eb7b533..1ef9635 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming dos apps do smartphone?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"O app %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, senhas e mensagens. O app &lt;br/&gt;&lt;br/&gt;%1$s poderá fazer o streaming de apps até que você remova o acesso a essa permissão."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mostrar e fazer streaming de apps entre seus dispositivos"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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, 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="1944832605534698562">"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 mostrar e fazer streaming de apps entre seus dispositivos"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Autorizar que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; acesse estas informações do smartphone"</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_title_computer" msgid="4671071173916176037">"Google Play Services"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; realize esta ação?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; faça streaming de apps e recursos do sistema do smartphone?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"O %1$s terá acesso a tudo o que estiver visível ou for aberto no smartphone, incluindo áudios, fotos, informações de pagamento, senhas e mensagens. &lt;br/&gt;&lt;br/&gt;O %1$s poderá acessar e transferir informações de apps e recursos do sistema até que você remova essa permissão."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Permitir que &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 e recursos do sistema 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_nearby_device_streaming" msgid="8509848562931818793">"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 fazer streaming de aplicativos e outros recursos do sistema entre seus dispositivos"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Mudar saída de mídia"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificações"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Apps"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Fazer e gerenciar ligações telefônicas"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Ler e gravar o registro de chamadas telefônicas"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Enviar e visualizar mensagens SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Encontrar, determinar o posicionamento relativo e se conectar a dispositivos por perto"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Ler todas as notificações, incluindo informações como contatos, mensagens e fotos"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Ler todas as notificações, incluindo informações como, por exemplo, contatos, mensagens e fotos&lt;br/&gt;• Enviar notificações&lt;br/&gt;&lt;br/&gt;Você pode gerenciar a capacidade deste app de ler e enviar notificações a qualquer momento em \"Configurações\" e \"Notificações\"."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Fazer transmissão dos apps no seu smartphone"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Fazer streaming de apps e outros recursos do sistema pelo smartphone"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Acesse uma lista de dispositivos disponíveis e controle quais deles podem reproduzir ou transmitir áudio ou vídeo de outros apps"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"smartphone"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispositivo"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 99067df..cb0572d 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Alege un dispozitiv pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Alege un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> de configurat"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, și să acceseze aceste permisiuni pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, și să acceseze aceste permisiuni pe <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Aplicația va putea să acceseze următoarele permisiuni pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Permite ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Permiți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să poată reda în stream aplicațiile de pe telefonul tău?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s va avea acces la tot ce este vizibil sau redat pe telefon, inclusiv conținutul audio, fotografii, parole și mesaje.&lt;br/&gt;&lt;br/&gt;%1$s va putea reda în stream aplicații până când elimini accesul la această permisiune."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele tale"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> de a afișa și a reda în stream aplicații între dispozitive"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a afișa și a reda în stream aplicații între dispozitive"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Permite ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</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_title_computer" msgid="4671071173916176037">"Servicii Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permiți ca &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; să realizeze această acțiune?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Permiți ca &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; să poată reda în stream aplicațiile și funcțiile de sistem de pe telefonul tău?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s va avea acces la tot ce este vizibil sau redat pe telefon, inclusiv conținutul audio, fotografii, informații de plată, parole și mesaje.&lt;br/&gt;&lt;br/&gt;%1$s va putea reda în stream aplicații și funcții de sistem până când elimini accesul la această permisiune."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<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 conținut din aplicații și alte funcții de sistem pe dispozitivele din apropiere"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Permiți ca &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 și funcții de sistem pe <xliff:g id="DEVICE_NAME">%3$s</xliff:g> până când elimini accesul la această permisiune."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<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 alte funcții de sistem între dispozitive"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Schimbă ieșirea media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografii și media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Notificări"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplicații"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Să inițieze și să gestioneze apeluri telefonice"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Să citească și să scrie jurnalul de apeluri telefonice"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Să trimită și să vadă SMS-urile"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Să găsească, să se conecteze la dispozitivele apropiate și să determine poziția relativă a acestora"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile&lt;br/&gt;• Să trimită notificări&lt;br/&gt;&lt;br/&gt;Poți să gestionezi oricând permisiunea acestei aplicații de a citi și trimite notificări în Setări &gt; Notificări."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Să redea în stream aplicațiile telefonului"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Redă în stream conținut din aplicații și alte funcții de sistem de pe telefon"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Accesează lista dispozitivelor disponibile și alege-l pe cel care redă în stream sau proiectează conținut audio sau video din alte aplicații"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tabletă"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televizor"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"dispozitiv"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 15456aa..f2e0367 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"часы"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Выберите устройство, которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Выберите <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для настройки"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Это приложение сможет синхронизировать данные, например имена вызывающих абонентов, а также получит указанные разрешения на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Это приложение сможет синхронизировать данные, например имена звонящих, и получит такие же разрешения на вашем устройстве (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)."</string>
     <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="2872254734959842579">"Это приложение получит указанные разрешения на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; получать эту информацию с вашего телефона"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Разрешить устройству &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслировать приложения с вашего телефона?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"У устройства \"%1$s\" будет доступ ко всему, что показывается или воспроизводится на телефоне, включая аудиофайлы, фотографии, пароли и сообщения.<br/>&lt;br/&gt;&lt;br/&gt;Устройство \"%1$s\" сможет транслировать приложения, пока вы не отзовете это разрешение."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы для нескольких устройств"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы транслировать приложения между устройствами."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запрашивает разрешение на трансляцию приложений между устройствами."</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Это приложение получит указанные разрешения на вашем устройстве (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)."</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Разрешить приложению &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="1274464413649731829">"У приложения \"<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="1944832605534698562">"Приложение \"<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="4693714143506569253">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; получать эту информацию с вашего телефона"</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_title_computer" msgid="4671071173916176037">"Сервисы Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы получить доступ к фотографиям, медиаконтенту и уведомлениям на телефоне."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Разрешить приложению &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; выполнять это действие?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Разрешить устройству &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; транслировать приложения и системные функции с вашего телефона?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"У устройства \"%1$s\" будет доступ ко всему, что показывается или воспроизводится на вашем телефоне, включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.&lt;br/&gt;&lt;br/&gt;Устройство \"%1$s\" сможет транслировать приложения и системные функции, пока вы не отзовете это разрешение."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения и системные функции на устройства поблизости."</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Разрешить устройству &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; транслировать приложения и системные функции с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) на устройство &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"У приложения \"<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_nearby_device_streaming" msgid="8509848562931818793">"Приложение \"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Смена источника вывода медиа"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Фотографии и медиафайлы"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Уведомления"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Приложения"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Потоковая передача"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Совершение телефонных звонков и управление ими"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Просмотр списка телефонных вызовов и создание записей в нем"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Отправка и просмотр SMS-сообщений"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Поиск устройств поблизости, подключение к ним и определение их относительного местоположения"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Чтение всех уведомлений, в том числе сведений о контактах, сообщениях и фотографиях"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Чтение всех уведомлений, в том числе сведений о контактах, сообщениях и фотографиях&lt;br/&gt;• Отправка уведомлений&lt;br/&gt;&lt;br/&gt;В разделе \"Настройки &gt; Уведомления\" вы можете в любое время разрешить или запретить этому приложению читать и отправлять уведомления."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Трансляция приложений с телефона."</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Трансляция приложений и системных функций с телефона"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Просмотр списка доступных устройств и управление тем, на котором будет транслироваться аудио и видео из других приложений"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефоне"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"планшете"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"телевизор"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"другое устройство"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index c1d79e5..0b2369a 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; විසින් කළමනා කරනු ලැබීමට උපාංගයක් තෝරන්න"</string>
     <string name="chooser_title" msgid="2235819929238267637">"සැකසීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"මෙම යෙදුමට අමතන කෙනෙකුගේ නම වැනි, තොරතුරු සමමුහූර්ත කිරීමට, සහ ඔබේ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> මත මෙම අවසර වෙත ප්‍රවේශ වීමට ඉඩ දෙනු ලැබේ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"මෙම යෙදුමට අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට, සහ ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්‍රවේශ වීමට ඉඩ දෙනු ඇත"</string>
     <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="2872254734959842579">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> මත මෙම අවසර වෙත ප්‍රවේශ වීමට ඉඩ දෙනු ලැබේ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්‍රවේශ වීමට ඉඩ දෙන්න"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"ඔබේ දුරකථනයේ යෙදුම් ප්‍රවාහ කිරීමට &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හා ඉඩ දෙන්න ද?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ශ්‍රව්‍ය, ඡායාරූප, මුරපද සහ පණිවිඩ ඇතුළු දුරකථනය මත දැකිය හැකි හෝ වාදනය කර ඇති ඕනෑම දෙයකට %1$s හට ප්‍රවේශය තිබෙනු ඇත. ඔබ මෙම අවසරය වෙත ප්‍රවේශය ඉවත් කරන තෙක් &lt;br/&gt;&lt;br/&gt;%1$s හට යෙදුම් ප්‍රවාහ කිරීමට හැකි වෙනු ඇත."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"හරස්-උපාංග සේවා"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ උපාංග අතර යෙදුම් ප්‍රවාහ කිරීමට අවසරය ඉල්ලමින් සිටියි"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"ඔබේ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> වෙනුවෙන් <xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ උපාංග අතර යෙදුම් සංදර්ශනය කිරීමට සහ ප්‍රවාහ කිරීමට අවසර ඉල්ලයි"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්‍රවේශ වීමට ඉඩ දෙනු ඇත"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"ඔබේ <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="1274464413649731829">"<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="1944832605534698562">"ඔබේ උපාංග අතර යෙදුම් සංදර්ශනය කිරීමට සහ ප්‍රවාහ කිරීමට <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="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්‍රවේශ වීමට ඉඩ දෙන්න"</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_title_computer" msgid="4671071173916176037">"Google Play සේවා"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ දුරකථනයේ ඡායාරූප, මාධ්‍ය, සහ දැනුම්දීම් වෙත ප්‍රවේශ වීමට අවසරය ඉල්ලමින් සිටියි"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"මෙම ක්‍රියාව කිරීමට &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඉඩ දෙන්න ද?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"ඔබේ දුරකථනයේ යෙදුම් සහ පද්ධති විශේෂාංග ප්‍රවාහ කිරීමට &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; හට ඉඩ දෙන්න ද?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ශ්‍රව්‍ය, ඡායාරූප, ගෙවීම් තතු, මුරපද සහ පණිවිඩ ඇතුළු දුරකථනය මත දැකිය හැකි හෝ වාදනය කර ඇති ඕනෑම දෙයකට %1$s හට ප්‍රවේශය තිබෙනු ඇත. ඔබ මෙම අවසරය වෙත ප්‍රවේශය ඉවත් කරන තෙක් &lt;br/&gt;&lt;br/&gt;%1$s හට යෙදුම් සහ පද්ධති විශේෂාංග ප්‍රවාහ කිරීමට හැකි වෙනු ඇත."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් යෙදුම් සහ අනෙකුත් පද්ධති විශේෂාංග අවට උපාංග වෙත ප්‍රවාහ කිරීමට අවසර ඉල්ලයි"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි යෙදුම් සහ පද්ධති විශේෂාංග &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; වෙත ප්‍රවාහ කිරීමට &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; හට ඉඩ දෙන්න ද?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"මාධ්‍ය ප්‍රතිදානය වෙනස් කරන්න"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ඡායාරූප සහ මාධ්‍ය"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"දැනුම්දීම්"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"යෙදුම්"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ප්‍රවාහ කිරීම"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"දුරකථන ඇමතුම් ගැනීම සහ කළමනාකරණය කිරීම"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"දුරකථන ඇමතුම් ලොගය කියවන්න සහ ලියන්න"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS පණිවිඩ යැවීම සහ බැලීම"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"අවට උපාංගවල සාපේක්ෂ පිහිටීම සොයා ගන්න, සම්බන්ධ වන්න, සහ තීරණය කරන්න"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"සම්බන්ධතා, පණිවිඩ, සහ ඡායාරූප වැනි තොරතුරු ඇතුළුව, සියලු දැනුම්දීම් කියවන්න"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• සම්බන්ධතා, පණිවිඩ, සහ ඡායාරූප වැනි තතු ඇතුළුව, සියලු දැනුම්දීම් කියවන්න&lt;br/&gt;• දැනුම්දීම් යවන්න&lt;br/&gt;&lt;br/&gt;ඔබට සැකසීම් &gt; දැනුම්දීම් තුළ ඕනෑම වේලාවක මෙම යෙදුමට දැනුම්දීම් කියවීමට සහ යැවීමට ඇති හැකියාව කළමනාකරණය කළ හැක."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"ඔබේ දුරකථනයේ යෙදුම් ප්‍රවාහ කරන්න"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"ඔබේ දුරකථනයෙන් යෙදුම් සහ අනෙකුත් පද්ධති විශේෂාංග ප්‍රවාහ කරන්න"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"පවතින උපාංග ලැයිස්තුවකට ප්‍රවේශ වන්න සහ වෙනත් යෙදුම් වෙතින් ශ්‍රව්‍ය හෝ වීඩියෝ ප්‍රවාහ කරන්නේ කුමන එකක් ද යන්න පාලනය කරන්න"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"දුරකථනය"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ටැබ්ලටය"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"රූපවාහිනිය"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"උපාංගය"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index c0d27f1..2819a92 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vyberte zariadenie, ktoré bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý nastavíte"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, a získavať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, a získavať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 z vášho telefónu"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Chcete povoliť zariadeniu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovať aplikácie telefónu?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s bude mať prístup k všetkému, čo v telefóne zobrazíte alebo prehrajete, vrátane zvuku, fotiek, hesiel a správ.&lt;br/&gt;&lt;br/&gt;%1$s bude môcť streamovať aplikácie, kým toto povolenie neodstránite."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie streamovať aplikácie medzi vašimi zariadeniami."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie zobrazovať a streamovať aplikácie medzi zariadeniami"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"Aplikácia <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, hesiel a správ.&lt;br/&gt;&lt;br/&gt;Aplikácia <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="1944832605534698562">"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 zobrazovať a streamovať aplikácie medzi zariadeniami"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"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 z vášho telefónu"</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_title_computer" msgid="4671071173916176037">"Služby Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Chcete povoliť zariadeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; vykonať túto akciu?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Chcete povoliť zariadeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; streamovať aplikácie a systémové funkcie telefónu?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s bude mať prístup k všetkému, čo v telefóne zobrazíte alebo prehrajete, vrátane zvuku, fotiek, platobných údajov, hesiel a správ.&lt;br/&gt;&lt;br/&gt;%1$s bude môcť streamovať aplikácie a systémové funkcie, kým toto povolenie neodstránite."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie a ďalšie systémové funkcie do zariadení v okolí"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Chcete povoliť zariadeniu &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; streamovať aplikácie a funkcie systému zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zariadenia &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Aplikácia <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;Aplikácia <xliff:g id="APP_NAME_1">%1$s</xliff:g> bude môcť streamovať aplikácie a funkcie systému 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_nearby_device_streaming" msgid="8509848562931818793">"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 streamovať aplikácie a ďalšie funkcie systému medzi zariadeniami"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Zmena výstupu médií"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotky a médiá"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Upozornenia"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikácie"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Volanie a správa telefonických hovorov"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Čítanie a zapisovanie do zoznamu telefonických hovorov"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Posielanie a zobrazovanie správ SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Vyhľadajte zariadenia v okolí, pripojte sa k nim a určite ich relatívnu polohu"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Čítajte všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky&lt;br/&gt;• Odosielajte upozornenia&lt;br/&gt;&lt;br/&gt;Schopnosť tejto aplikácie čítať a odosielať upozornenia môžete kedykoľvek spravovať v sekcii Nastavenia &gt; Upozornenia."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamovať aplikácie telefónu"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streaming aplikácii a ďalších systémových funkcií zo zariadenia"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Získajte prístup k dostupným zariadeniam a ovládajte, ktoré bude streamovať alebo prenášať zvuk či video z iných aplikácií"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefón"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televízor"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"zariadenie"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index a895c25..3e4f8db 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ura"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Izbira naprave, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Izberite profil naprave »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>« za nastavitev"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, in dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«."</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, in dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>«."</string>
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Želite aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovoliti upravljanje naprave &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"naprava"</string>
-    <string name="summary_glasses" msgid="2872254734959842579">"Ta aplikacija bo lahko dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«."</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Dovolite, da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dostopa do teh podatkov v vašem telefonu"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Ali aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite, da pretočno predvaja aplikacije telefona?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"Aplikacija %1$s bo imela dostop do vsega, kar je prikazano ali se predvaja v telefonu, vključno z zvokom, fotografijami, gesli in sporočili.&lt;br/&gt;&lt;br/&gt;Aplikacija %1$s bo lahko pretočno predvajala aplikacije, dokler ne odstranite dostopa do tega dovoljenja."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve v več napravah"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij v vaših napravah."</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za prikaz in pretočno predvajanje aplikacij v vaših napravah."</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Ta aplikacija bo lahko dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>«."</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Ali aplikaciji &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite, da pretočno predvaja aplikacije naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« v napravi &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="1274464413649731829">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> bo imela dostop do vsega, kar je prikazano ali se predvaja v napravi »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>«, vključno z zvokom, fotografijami, gesli in sporočili.&lt;br/&gt;&lt;br/&gt;Aplikacija <xliff:g id="APP_NAME_1">%1$s</xliff:g> bo lahko pretočno predvajala aplikacije v napravo »<xliff:g id="DEVICE_NAME">%3$s</xliff:g>«, dokler ne odstranite dostopa do tega dovoljenja."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_NAME">%2$s</xliff:g>« zahteva dovoljenje za prikaz in pretočno predvajanje aplikacij v vaših napravah."</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Dovolite, da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dostopa do teh podatkov v vašem telefonu"</string>
+    <string name="title_computer" msgid="4782923323932440751">"Dovolite, da &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; dostopa do teh podatkov v vaši napravi »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>«."</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
-    <string name="helper_title_computer" msgid="4671071173916176037">"Storitve Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za dostop do fotografij, predstavnosti in obvestil v telefonu."</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ali napravi &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite izvedbo tega dejanja?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Ali napravi &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; dovolite, da pretočno predvaja aplikacije in sistemske funkcije telefona?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Naprava %1$s bo imela dostop do vsega, kar je prikazano ali se predvaja v telefonu, vključno z zvokom, fotografijami, podatki za plačilo, gesli in sporočili.&lt;br/&gt;&lt;br/&gt;Naprava %1$s bo lahko pretočno predvajala aplikacije in sistemske funkcije, dokler ne odstranite dostopa do tega dovoljenja."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij in drugih sistemskih funkcij v napravah v bližini."</string>
+    <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_NAME">%2$s</xliff:g>« zahteva dovoljenje za dostop do fotografij, predstavnosti in obvestil v napravi »<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>«."</string>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Ali napravi &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; dovolite, da pretočno predvaja aplikacije in sistemske funkcije naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« v napravi &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> bo imela dostop do vsega, kar je prikazano ali se predvaja v napravi »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>«, vključno z zvokom, fotografijami, podatki za plačilo, gesli in sporočili.&lt;br/&gt;&lt;br/&gt;Aplikacija <xliff:g id="APP_NAME_1">%1$s</xliff:g> bo lahko pretočno predvajala aplikacije in sistemske funkcije v napravo »<xliff:g id="DEVICE_NAME">%3$s</xliff:g>«, dokler ne odstranite dostopa do tega dovoljenja."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij in drugih sistemskih funkcij v vaših napravah."</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, v telefonu in izbrani napravi."</string>
     <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Spreminjanje izhoda za predstavnost"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografije in predstavnost"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Obvestila"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacije"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Pretočno predvajanje"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Opravljanje in upravljanje telefonskih klicev"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Branje in zapisovanje dnevnika klicev v telefonu"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Pošiljanje in ogled sporočil SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Iskanje naprav v bližini, povezovanje z njimi in določanje njihovega relativnega položaja"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Branje vseh obvestil, vključno s podatki, kot so stiki, sporočila in fotografije&lt;br/&gt;• Pošiljanje obvestil&lt;br/&gt;&lt;br/&gt;Zmožnost branja in pošiljanja obvestil za to aplikacijo lahko kadar koli upravljate v meniju »Nastavitve« &gt; »Obvestila«."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Pretočno predvajanje aplikacij telefona"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Pretočno predvajanje aplikacij in drugih sistemskih funkcij iz telefona"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Dostop do seznama razpoložljivih naprav in upravljanje, katera naprava pretočno ali na daljavo predvaja zvok ali videoposnetek iz drugih aplikacij"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablični računalnik"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televizor"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"naprava"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 63e8cb6..9d52281 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Zgjidh një pajisje që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Zgjidh një <xliff:g id="PROFILE_NAME">%1$s</xliff:g> për konfigurimin"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Këtij aplikacioni do t\'i lejohet të sinkronizojë informacione, si p.sh. emrin e dikujt që po telefonon, si dhe të ketë qasje në këto leje në <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Këtij aplikacioni do t\'i lejohet të sinkronizojë informacione, si p.sh. emrin e dikujt që po telefonon, si dhe të ketë qasje në këto leje te <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Këtij aplikacioni do t\'i lejohet qasja te këto leje në <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të ketë qasje në këtë informacion nga telefoni yt"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Të lejohet që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të transmetojë aplikacionet e telefonit tënd?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose që luhet në telefon, duke përfshirë audion, fotografitë, fjalëkalimet dhe mesazhet.&lt;br/&gt;&lt;br/&gt;%1$s do të mund t\'i transmetojë aplikacionet derisa ta heqësh qasjen për këtë leje."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Shërbimet mes pajisjeve"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të shfaqur dhe transmetuar aplikacionet mes pajisjeve të tua"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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ë, 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="1944832605534698562">"<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ë shfaqur dhe transmetuar aplikacionet mes pajisjeve të tua"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Lejo që &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; të ketë qasje në këtë informacion nga telefoni yt"</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_title_computer" msgid="4671071173916176037">"Shërbimet e Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet e telefonit tënd"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Të lejohet që &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; të ndërmarrë këtë veprim?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Të lejohet që &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; të transmetojë aplikacionet dhe veçoritë e sistemit të telefonit tënd?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s do të ketë qasje te çdo gjë që është e dukshme ose që luhet në telefon, duke përfshirë audion, fotografitë, informacionet për pagesën, fjalëkalimet dhe mesazhet.&lt;br/&gt;&lt;br/&gt;%1$s do të mund t\'i transmetojë aplikacionet dhe veçoritë e sistemit derisa ta heqësh qasjen për këtë leje."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"\"<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>) tënde për të transmetuar aplikacione dhe veçori të tjera të sistemit te pajisjet në afërsi"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Të lejohet që &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 dhe veçoritë e sistemit 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_nearby_device_streaming" msgid="8509848562931818793">"<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ë tjera të sistemit ndërmjet pajisjeve të tua"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Të ndryshojë daljen e medias"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotografitë dhe media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Njoftimet"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Aplikacionet"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Transmetimi"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Të kryejë dhe të menaxhojë telefonatat"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Të lexojë dhe shkruajë në evidencën e telefonatave"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Të dërgojë dhe të shikojë mesazhet SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Të gjejë, të lidhet dhe të përcaktojë pozicionin e përafërt të pajisjeve në afërsi"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Të lexojë të gjitha njoftimet, duke përfshirë informacione si kontaktet, mesazhet dhe fotografitë&lt;br/&gt;• Të dërgojë njoftime&lt;br/&gt;&lt;br/&gt;Mund ta menaxhosh aftësinë e këtij aplikacioni që të lexojë dhe të dërgojë njoftime në çdo kohë te Cilësimet &gt; Njoftimet."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Transmeto aplikacionet e telefonit tënd"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Transmeto aplikacionet dhe veçoritë e tjera të sistemit nga telefoni yt"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Të qaset te një listë e pajisjeve të disponueshme dhe të kontrollojë se cila transmeton audion ose videon nga aplikacionet e tjera"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"televizori"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"pajisja"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 22eafa5..69af8ab 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"сат"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Одаберите уређај којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> који желите да подесите"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Овој апликацији ће бити дозвољено да синхронизује податке, попут имена особе која упућује позив, и приступа тим дозволама на вашем уређају (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Овој апликацији ће бити дозвољено да синхронизује податке, попут имена позиваоца, и приступа тим дозволама на уређају <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Овој апликацији ће бити дозвољено да приступа овим дозволама на вашем уређају (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа овим информацијама са телефона"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Желите ли да дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; стримује апликације на телефону?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s ће имати приступ свему што се види или пушта на телефону, укључујући звук, слике, лозинке и поруке.&lt;br/&gt;&lt;br/&gt;%1$s ће моћи да стримује апликације док не уклоните приступ овој дозволи."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуге на више уређаја"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за стримовање апликација између уређаја"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да приказује и стримује апликације између уређаја"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Овој апликацији ће бити дозвољено да приступа овим дозволама на уређају <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Желите ли да дозволите да &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"Дозволите да &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; приступа овим информацијама са телефона"</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_title_computer" msgid="4671071173916176037">"Google Play услуге"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за приступ сликама, медијском садржају и обавештењима са телефона"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Желите ли да дозволите да &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; обави ову радњу?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Желите ли да дозволите да &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; стримује апликације и функције система на телефону?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s ће имати приступ свему што се види или пушта на телефону, укључујући звук, слике, информације о плаћању, лозинке и поруке.&lt;br/&gt;&lt;br/&gt;%1$s ће моћи да стримује апликације и функције система док не уклоните приступ овој дозволи."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације и друге системске функције на уређаје у близини"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Желите ли да дозволите да &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; стримује апликације и системске функције уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Промена медијског излаза"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Слике и медији"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Обавештења"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Апликације"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Стриминг"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Упућивање телефонских позива и управљање њима"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Читање и писање евиденције позива на телефону"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Слање и преглед SMS порука"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Проналажење уређаја у близини, утврђивање њихове релативне позиције и повезивање са њима"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Читање свих обавештења, укључујући информација попут контаката, порука и слика"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Читање свих обавештења, укључујући информација попут контаката, порука и слика&lt;br/&gt;• Слање обавештења&lt;br/&gt;&lt;br/&gt;Да бисте управљали дозволама ове апликације за читање и слање обавештења, идите у Подешавања &gt; Обавештења."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Стримујте апликације на телефону"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Стримујте апликације и друге системске функције са телефона"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Приступ листи доступних уређаја и контрола уређаја који стримује или пребацује аудио или видео из других апликација"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефону"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"таблету"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"тв"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"уређај"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml
index 2c0f9c4..6ec27d2 100644
--- a/packages/CompanionDeviceManager/res/values-sv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Välj en enhet för hantering av &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för konfigurering"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Appen får tillåtelse att synkronisera information, till exempel namnet på någon som ringer, och få tillgång till dessa behörigheter på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Appen får synkronisera information, till exempel namnet på någon som ringer, och får åtkomst till dessa behörigheter på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <string name="confirmation_title_glasses" msgid="8288346850537727333">"Tillåt att &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; hanterar &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_glasses" msgid="3506504967216601277">"enhet"</string>
-    <string name="summary_glasses" msgid="2872254734959842579">"Appen får tillåtelse att använda dessa behörigheter på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Ge &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; åtkomstbehörighet till denna information på telefonen"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vill du tillåta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; att streama telefonens appar?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s får åtkomst till allt som visas eller spelas på telefonen, inklusive ljud, foton, lösenord och meddelanden.&lt;br/&gt;&lt;br/&gt;%1$s kan streama appar tills du tar bort behörigheten."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjänster för flera enheter"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att låta <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> streama appar mellan enheter"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att visa och streama appar mellan enheter åt din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Appen får åtkomst till dessa behörigheter på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Vill du tillåta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; att streama appar på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> till &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får åtkomst till allt som visas eller spelas upp på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inklusive ljud, foton, lösenord och meddelanden.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kan streama appar till <xliff:g id="DEVICE_NAME">%3$s</xliff:g> tills du tar bort åtkomsten till den här behörigheten."</string>
+    <string name="helper_summary_app_streaming" msgid="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet för din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> att visa och streama appar mellan dina enheter"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Ge &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; åtkomstbehörighet till denna information på telefonen"</string>
+    <string name="title_computer" msgid="4782923323932440751">"Ge &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; åtkomst till denna information på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
     <string name="summary_computer" msgid="3798467601598297062"></string>
-    <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjänster"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att ge <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> åtkomst till foton, mediefiler och aviseringar på telefonen"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vill du tillåta att &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; utför denna åtgärd?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vill du tillåta &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; att streama telefonens appar och systemfunktioner?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s får åtkomst till allt som visas eller spelas på telefonen, inklusive ljud, foton, betalningsuppgifter, lösenord och meddelanden.&lt;br/&gt;&lt;br/&gt;%1$s kan streama appar och systemfunktioner tills du tar bort behörigheten."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att streama appar och andra systemfunktioner till enheter i närheten för din <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet för din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> att få åtkomst till foton, mediefiler och aviseringar på din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vill du tillåta &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; att streama appar och systemfunktioner på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> till &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får åtkomst till allt som visas eller spelas upp på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inklusive ljud, foton, betalningsuppgifter, lösenord och meddelanden.&lt;br/&gt;&lt;br/&gt;<xliff:g id="APP_NAME_1">%1$s</xliff:g> kan streama appar och systemfunktioner till <xliff:g id="DEVICE_NAME">%3$s</xliff:g> tills du tar bort åtkomsten till den här behörigheten."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet för din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> att streama appar och andra systemfunktioner mellan dina enheter"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
     <string name="summary_generic" msgid="1761976003668044801">"Den här appen kommer att kunna synkronisera information mellan telefonen och den valda enheten, till exempel namnet på någon som ringer"</string>
     <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Ändra uppspelning av media"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Foton och media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Aviseringar"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Appar"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Ringa och hantera telefonsamtal"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Läsa och skriva samtalshistorik"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Skicka och se sms"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Hitta, ansluta till och avgöra den relativa positionen för enheter i närheten"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Läsa alla aviseringar, inklusive information som kontakter, meddelanden och foton"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Läsa alla aviseringar, inklusive sådant som kontakter, meddelanden och foton&lt;br/&gt;• Skicka aviseringar&lt;br/&gt;&lt;br/&gt;Du kan hantera appens möjlighet att läsa och skicka aviseringar när du vill i Inställningar &gt; Aviseringar."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streama telefonens appar"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streama appar och andra systemfunktioner från din telefon"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Få åtkomst till en lista över tillgängliga enheter och styr vilken som streamar eller castar ljud eller video från andra appar"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"surfplatta"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"enhet"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 5820279..8efcb2b 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"saa"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Chagua kifaa cha kudhibitiwa na &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili uweke mipangilio"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Programu hii itaruhusiwa kusawazisha maelezo, kama vile jina la mtu anayepiga simu na kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Programu hii itaruhusiwa kusawazisha maelezo, kama vile jina la mtu anayepiga simu na kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yako"</string>
     <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="2872254734959842579">"Programu hii itaruhusiwa kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; itiririshe programu za simu yako?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s itakuwa na uwezo wa kufikia chochote kinachoonekana au kuchezwa kwenye simu, ikiwa ni pamoja na sauti, picha, manenosiri na ujumbe.&lt;br/&gt;&lt;br/&gt;%1$s itaweza kutiririsha programu hadi uondoe ruhusa hii."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yako ili ionyeshe na kutiririsha programu kati ya vifaa vyako"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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>, ikiwa ni pamoja na sauti, picha, 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="1944832605534698562">"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 ionyeshe na kutiririsha programu kati ya vifaa vyako"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</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_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; itekeleze kitendo hiki?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; itiririshe programu za simu na vipengele vya mfumo wako?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s itakuwa na uwezo wa kufikia chochote kinachoonekana au kuchezwa kwenye simu yako, ikiwa ni pamoja na sauti, picha, maelezo ya malipo, manenosiri na ujumbe.&lt;br/&gt;&lt;br/&gt;%1$s itaweza kutiririsha programu na vipengele vya mfumo hadi uondoe ruhusa hii."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> chako ili itiririshe programu na vipengele vingine vya mfumo kwenye vifaa vilivyo karibu"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Ungependa kuruhusu &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 na vipengele vya mfumo kwenye <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hadi utakapoondoa ruhusa hii."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"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 itiririshe programu na vipengele vingine vya mfumo kati ya vifaa vyako"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Badilisha tokeo la maudhui"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Arifa"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Programu"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Kutiririsha maudhui"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Kupiga na kudhibiti simu"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Kusoma na kuandika rekodi ya namba za simu"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Kutuma na kuona ujumbe wa SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Kutafuta, kuunganisha na kubaini nafasi ya makadirio ya vifaa vilivyo karibu"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kusoma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Soma arifa zote, ikiwa ni pamoja na maelezo kama vile anwani, ujumbe na picha&lt;br/&gt;• Tuma arifa&lt;br/&gt;&lt;br/&gt;Unaweza kudhibiti uwezo wa programu hii wa kusoma na kutuma arifa wakati wowote katika Mipangilio &gt; Arifa."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Tiririsha programu za simu yako"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Kutiririsha programu na vipengele vya mfumo kwenye simu yako"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Fikia orodha ya vifaa vinavyopatikana na udhibiti unavyotumia kutiririsha au kutuma maudhui ya sauti au video kutoka kwenye programu nyingine`"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"simu"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"kompyuta kibao"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"kifaa"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 5133073..1a59c09 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸால் நிர்வகிக்கப்பட வேண்டிய சாதனத்தைத் தேர்வுசெய்யுங்கள்"</string>
     <string name="chooser_title" msgid="2235819929238267637">"அமைக்க <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்வுசெய்யவும்"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"அழைப்பவரின் பெயர் போன்ற தகவல்களை ஒத்திசைக்கவும் உங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுகவும் இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"அழைப்பவரின் பெயர் போன்ற தகவல்களை ஒத்திசைக்கவும் உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுகவும் இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string>
     <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="2872254734959842579">"உங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"மொபைலில் உள்ள இந்தத் தகவல்களை அணுக, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவும்"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"உங்கள் மொபைலின் ஆப்ஸை ஸ்ட்ரீம் செய்ய &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ஆடியோ, படங்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட மொபைலில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் %1$s அணுகும்.&lt;br/&gt;&lt;br/&gt;இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை %1$s ஆல் ஆப்ஸை ஸ்ட்ரீம் செய்ய முடியும்."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"பன்முக சாதன சேவைகள்"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் அனுமதியைக் கோருகிறது"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸைக் காட்சிப்படுத்தவும் ஸ்ட்ரீம் செய்யவும் உங்கள் <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கேட்கிறது"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"உங்கள் <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="1274464413649731829">"ஆடியோ, படங்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <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="1944832605534698562">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸைக் காட்சிப்படுத்தவும் ஸ்ட்ரீம் செய்யவும் உங்கள் <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="4693714143506569253">"உங்கள் மொபைலிலிருந்து இந்தத் தகவலை அணுக &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதியுங்கள்"</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_title_computer" msgid="4671071173916176037">"Google Play சேவைகள்"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"உங்கள் மொபைலில் உள்ள படங்கள், மீடியா, அறிவிப்புகள் ஆகியவற்றை அணுக உங்கள் <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் அனுமதியைக் கோருகிறது"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"இந்தச் செயலைச் செய்ய &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ஐ அனுமதிக்கவா?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"உங்கள் மொபைலின் ஆப்ஸ் மற்றும் சிஸ்டம் அம்சங்களை ஸ்ட்ரீம் செய்ய &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அனுமதிக்கவா?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட உங்கள் மொபைலில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் %1$s அணுகும்.&lt;br/&gt;&lt;br/&gt;இந்த அனுமதிக்கான அணுகலை நீங்கள் அகற்றும் வரை %1$s ஆல் ஆப்ஸ் மற்றும் சிஸ்டம் அம்சங்களை ஸ்ட்ரீம் செய்ய முடியும்."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"அருகிலுள்ள சாதனங்களுக்கு ஆப்ஸையும் பிற சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சாதனத்தின் ஆப்ஸையும் சிஸ்டம் அம்சங்களையும் &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; சாதனத்தில் ஸ்ட்ரீம் செய்ய &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; சாதனத்தை அனுமதிக்கவா?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"ஆடியோ, படங்கள், பேமெண்ட் தகவல்கள், கடவுச்சொற்கள், மெசேஜ்கள் உட்பட <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_nearby_device_streaming" msgid="8509848562931818793">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸையும் பிற சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்ய உங்கள் <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"மீடியா அவுட்புட்டை மாற்றுதல்"</string>
     <string name="permission_storage" msgid="6831099350839392343">"படங்கள் மற்றும் மீடியா"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"அறிவிப்புகள்"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ஆப்ஸ்"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"ஸ்ட்ரீமிங்"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"மொபைல் அழைப்புகளைச் செய்யலாம் நிர்வகிக்கலாம்"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"மொபைல் அழைப்புப் பதிவைப் படிக்கலாம் எழுதலாம்"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS செய்திகளை அனுப்பலாம் பார்க்கலாம்"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"அருகிலுள்ள சாதனங்களைக் கண்டறியலாம், அவற்றுடன் இணையலாம், அவற்றின் தூரத்தைத் தீர்மானிக்கலாம்"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"தொடர்புகள், மெசேஜ்கள், படங்கள் போன்ற தகவல்கள் உட்பட அனைத்து அறிவிப்புகளையும் படிக்கலாம்"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• தொடர்புகள், மெசேஜ்கள், படங்கள் போன்ற தகவல்கள் உட்பட அனைத்து அறிவிப்புகளையும் படிக்கலாம்&lt;br/&gt;• அறிவிப்புகளை அனுப்பலாம்&lt;br/&gt;&lt;br/&gt;இந்த ஆப்ஸின், அறிவிப்புகளைப் படிக்கும் மற்றும் அனுப்பும் திறனை எப்போது வேண்டுமானாலும் அமைப்புகள் &gt; அறிவிப்புகள் என்பதில் நிர்வகிக்கலாம்."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"உங்கள் மொபைல் ஆப்ஸை ஸ்ட்ரீம் செய்யலாம்"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"உங்கள் மொபைலில் இருந்து ஆப்ஸையும் பிற சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்யலாம்"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"கிடைக்கக்கூடிய சாதனங்களின் பட்டியலை அணுகி, அவற்றில் எது பிற ஆப்ஸின் ஆடியோ/வீடியோவைப் பிளே செய்யலாம் அல்லது அலைபரப்பலாம் என்பதைக் கட்டுப்படுத்தும்"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"மொபைல்"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"டேப்லெட்"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"டிவி"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"சாதனம்"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 2643b2d4..63d86fc 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడే పరికరాన్ని ఎంచుకోండి"</string>
     <string name="chooser_title" msgid="2235819929238267637">"సెటప్ చేయడానికి <xliff:g id="PROFILE_NAME">%1$s</xliff:g>‌ను ఎంచుకోండి"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని సింక్ చేయడానికి, మీ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని సింక్ చేయడానికి, మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>‌లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string>
     <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="2872254734959842579">"మీ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; యాప్‌ను అనుమతించండి"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"మీ ఫోన్ యాప్‌లను స్ట్రీమ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించాలా?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"ఆడియో, ఫోటోలు, పాస్‌వర్డ్‌లు, మెసేజ్‌లతో సహా ఫోన్‌లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా %1$sకు యాక్సెస్ ఉంటుంది.&lt;br/&gt;&lt;br/&gt;మీరు ఈ అనుమతికి యాక్సెస్‌ను తీసివేసే వరకు %1$s యాప్‌లను స్ట్రీమ్ చేయగలదు."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"క్రాస్-డివైజ్ సర్వీసులు"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"మీ పరికరాల మధ్య యాప్‌లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"మీ పరికరాలలో యాప్‌లను డిస్‌ప్లే చేయడానికి, స్ట్రీమ్ చేయడానికి <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున <xliff:g id="APP_NAME">%1$s</xliff:g> అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>‌లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"మీ <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="1274464413649731829">"ఆడియో, ఫోటోలు, పాస్‌వర్డ్‌లు, మెసేజ్‌లతో సహా మీ <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="1944832605534698562">"మీ పరికరాలలో యాప్‌లను డిస్‌ప్లే చేయడానికి, స్ట్రీమ్ చేయడానికి <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="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; యాప్‌ను అనుమతించండి"</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_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> మీ ఫోన్‌లోని ఫోటోలను, మీడియాను, ఇంకా నోటిఫికేషన్‌లను యాక్సెస్ చేయడానికి మీ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ఈ చర్యను అమలు చేయడానికి &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించాలా?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"మీ ఫోన్ యాప్‌లను, సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేయడానికి &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;ను అనుమతించాలా?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్‌వర్డ్‌లు, మెసేజ్‌లతో సహా మీ ఫోన్‌లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా %1$sకు యాక్సెస్ ఉంటుంది.&lt;br/&gt;&lt;br/&gt;మీరు ఈ అనుమతికి యాక్సెస్‌ను తీసివేసే వరకు %1$s యాప్‌లను, సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేయగలదు."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"సమీపంలోని పరికరాలకు యాప్‌లను, ఇతర సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> యాప్‌లను, సిస్టమ్ ఫీచర్‌లను &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;‌కు స్ట్రీమ్ చేయడానికి &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;‌ను అనుమతించాలా?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్‌వర్డ్‌లతో సహా మీ <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_nearby_device_streaming" msgid="8509848562931818793">"మీ పరికరాలలో యాప్‌లను, ఇతర సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేయడానికి <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"మీడియా అవుట్‌పుట్‌ను మార్చండి"</string>
     <string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"నోటిఫికేషన్‌లు"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"యాప్‌లు"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"స్ట్రీమింగ్"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"ఫోన్ కాల్స్‌ను చేయగలదు, మేనేజ్ చేయగలదు"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"ఫోన్ కాల్ లాగ్‌ను చదవగలదు, రాయగలదు"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS మెసేజ్‌లను పంపగలదు, చూడగలదు"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"సమీపంలోని పరికరాలను కనుగొనగలదు, వాటికి కనెక్ట్ అవ్వగలదు, అవి ఎంత దూరంలో ఉన్నాయో తెలుసుకోగలదు"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"కాంటాక్ట్‌లు, మెసేజ్‌లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్‌లను చదవగలదు"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• కాంటాక్ట్‌లు, మెసేజ్‌లు, ఫోటోల వంటి సమాచారంతో సహా నోటిఫికేషన్‌లన్నింటిని చదవగలదు&lt;br/&gt;• నోటిఫికేషన్‌లను పంపగలదు&lt;br/&gt;&lt;br/&gt;నోటిఫికేషన్‌లను చదవగల, పంపగల ఈ యాప్ సామర్థ్యాన్ని మీరు సెట్టింగ్‌లు &gt; నోటిఫికేషన్‌లలో ఎప్పుడైనా మేనేజ్ చేయవచ్చు."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"మీ ఫోన్‌లోని యాప్‌లను స్ట్రీమ్ చేయండి"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"మీ ఫోన్ నుండి యాప్‌లను, ఇతర సిస్టమ్ ఫీచర్‌లను స్ట్రీమ్ చేస్తుంది"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"అందుబాటులో ఉన్న పరికరాల లిస్ట్‌ను యాక్సెస్ చేసి, ఇతర యాప్‌లలోని ఆడియో లేదా వీడియోను ఏ పరికరం స్ట్రీమ్ చేయాలో లేదా ప్రసారం చేయాలో కంట్రోల్ చేయండి"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ఫోన్"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"టాబ్లెట్"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"టీవీ"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"పరికరం"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index a2424ec..eb9a6ed 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"เลือกอุปกรณ์ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะตั้งค่า"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"แอปนี้จะได้รับอนุญาตให้ซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา และมีสิทธิ์เข้าถึงข้อมูลเหล่านี้ใน<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ของคุณ"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"แอปนี้จะได้รับอนุญาตให้ซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา และมีสิทธิ์เข้าถึงข้อมูลเหล่านี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string>
     <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="2872254734959842579">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ของคุณ"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; สตรีมแอปในโทรศัพท์ของคุณไหม"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบนโทรศัพท์ ซึ่งรวมถึงเสียง รูปภาพ รหัสผ่าน และข้อความ&lt;br/&gt;&lt;br/&gt;%1$s จะสามารถสตรีมแอปได้จนกว่าคุณจะนำสิทธิ์เข้าถึงนี้ออก"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"บริการข้ามอุปกรณ์"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> เพื่อสตรีมแอประหว่างอุปกรณ์ต่างๆ ของคุณ"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> เพื่อแสดงและสตรีมแอประหว่างอุปกรณ์ต่างๆ ของคุณ"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"อนุญาตให้ &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="1274464413649731829">"<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="1944832605534698562">"<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="4693714143506569253">"อนุญาตให้ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</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_title_computer" msgid="4671071173916176037">"บริการ Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> เพื่อเข้าถึงรูปภาพ สื่อ และการแจ้งเตือนในโทรศัพท์ของคุณ"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"อนุญาตให้ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ทำงานนี้ไหม"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"อนุญาตให้ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; สตรีมแอปและฟีเจอร์ของระบบในโทรศัพท์ไหม"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบนโทรศัพท์ของคุณ ซึ่งรวมถึงเสียง รูปภาพ ข้อมูลการชำระเงิน รหัสผ่าน และข้อความ&lt;br/&gt;&lt;br/&gt;%1$s จะสามารถสตรีมแอปและฟีเจอร์ของระบบได้จนกว่าคุณจะนำสิทธิ์เข้าถึงนี้ออก"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปและฟีเจอร์อื่นๆ ของระบบไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"อนุญาตให้ &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; สตรีมแอปและฟีเจอร์ของระบบใน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณไปยัง &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt; ไหม"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"เปลี่ยนเอาต์พุตสื่อ"</string>
     <string name="permission_storage" msgid="6831099350839392343">"รูปภาพและสื่อ"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"การแจ้งเตือน"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"แอป"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"สตรีมมิง"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"โทรและจัดการการโทร"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"อ่านและเขียนบันทึกการโทรของโทรศัพท์"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"ส่งและดูข้อความ SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"ค้นหา เชื่อมต่อ และระบุตำแหน่งซึ่งสัมพันธ์กับอุปกรณ์ที่อยู่ใกล้เคียง"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"อ่านการแจ้งเตือนทั้งหมด รวมถึงข้อมูลอย่างรายชื่อติดต่อ ข้อความ และรูปภาพ"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• อ่านการแจ้งเตือนทั้งหมด รวมถึงข้อมูลอย่างรายชื่อติดต่อ ข้อความ และรูปภาพ&lt;br/&gt;• ส่งการแจ้งเตือน&lt;br/&gt;&lt;br/&gt;คุณจัดการความสามารถในการอ่านและส่งการแจ้งเตือนของแอปนี้ได้ทุกเมื่อในการตั้งค่า &gt; การแจ้งเตือน"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"สตรีมแอปของโทรศัพท์คุณ"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"สตรีมแอปและฟีเจอร์อื่นๆ ของระบบจากโทรศัพท์"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"เข้าถึงรายการอุปกรณ์ที่มีและควบคุมอุปกรณ์ที่จะสตรีมหรือแคสต์เสียงหรือวิดีโอจากแอปอื่นๆ"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"โทรศัพท์"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"แท็บเล็ต"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"ทีวี"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"อุปกรณ์"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index 45bc01d..bce6c7c 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"relo"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pumili ng device na papamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para mag-set up"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Papayagan ang app na ito na mag-sync ng impormasyon, tulad ng pangalan ng taong tumatawag, at i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Papayagan ang app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, at ma-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Papayagan ang app na ito na i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 telepono"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"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 telepono?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"Magkakaroon ng access ang %1$s sa kahit anong nakikita o nape-play sa telepono, kasama ang audio, mga larawan, mga password, at mga mensahe.&lt;br/&gt;&lt;br/&gt;Makakapag-stream ng mga app ang %1$s hanggang sa alisin mo ang access sa pahintulot na ito."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Mga cross-device na serbisyo"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Humihiling ng pahintulot ang <xliff:g id="APP_NAME">%1$s</xliff:g> para sa iyong <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> na makapagpakita at makapag-stream ng mga app sa mga device mo"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"Magkakaroon ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> ng access sa kahit anong nakikita o pine-play sa <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, kasama na ang audio, mga larawan, password, at 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="1944832605534698562">"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 makapagpakita at makapag-stream ng mga app sa mga device mo"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Payagan ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-access ang impormasyon sa iyong telepono"</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_title_computer" msgid="4671071173916176037">"Mga serbisyo ng Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Payagan ang &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; na gawin ang pagkilos na ito?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Payagan ang &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; na i-stream ang mga app at feature ng system ng iyong telepono?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"Magkakaroon ng access ang %1$s sa kahit anong nakikita o nape-play sa iyong telepono, kasama ang audio, mga larawan, impormasyon sa pagbabayad, mga password, at mga mensahe.&lt;br/&gt;&lt;br/&gt;Makakapag-stream ng mga app at feature ng system ang %1$s hanggang sa alisin mo ang access sa pahintulot na ito."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Humihiling ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para mag-stream ng mga app at iba pang feature ng system sa mga kalapit na device"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Payagan ang &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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_nearby_device_streaming" msgid="8509848562931818793">"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 mag-stream ng mga app at iba pang feature ng system sa pagitan ng mga device mo"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Palitan ang media output"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Mga Notification"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Mga App"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Streaming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Tumawag at mamahala ng mga tawag sa telepono"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"I-read at i-write ang log ng tawag sa telepono"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Magpadala at tumingin ng mga mensaheng SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Maghanap ng, kumonekta sa, at tukuyin ang relatibong posisyon ng mga kalapit na device"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Basahin ang lahat ng notification, kabilang ang impormasyon tulad ng mga contact, mensahe, at larawan&lt;br/&gt;• Magpadala ng mga notification&lt;br/&gt;&lt;br/&gt;Puwede mong pamahalaan ang kakayahan ng app na ito na magbasa at magpadala ng mga notification kahit kailan sa Mga Setting &gt; Mga Notification."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"I-stream ang mga app ng iyong telepono"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Mag-stream ng mga app at iba pang feature ng system mula sa iyong telepono"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"I-access ang listahan ng mga available na device at kontrolin kung alin ang magsi-stream o magka-cast ng audio o video mula sa iba pang app"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telepono"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"device"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index 815d256a..131f2e3 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"saat"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir cihaz seçin"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Ayarlamak için bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Bu uygulamanın arayan kişinin adı gibi bilgileri senkronize etmesine ve <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızda aşağıdaki izinlere erişmesine izin verilir"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Bu uygulamanın arayan kişinin adı gibi bilgileri senkronize etmesine ve <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazınızda aşağıdaki izinlere erişmesine izin verilir"</string>
     <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="2872254734959842579">"Bu uygulamanın <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; adlı cihazın telefonunuzdaki uygulamaları aktarmasına izin verilsin mi?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s; ses, fotoğraflar, şifreler ve mesajlar da dahil olmak üzere telefonda görünen veya oynatılan her şeye erişebilecek.&lt;br/&gt;&lt;br/&gt;%1$s siz bu iznin erişimini kaldırana kadar uygulamaları aktarabilecek."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlar arası hizmetler"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulama akışı gerçekleştirmek için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulamaları göstermek ve aktarmak için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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_app_streaming" msgid="1274464413649731829">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>; ses, fotoğraflar, şifreler ve mesajlar da dahil olmak üzere <xliff:g id="DEVICE_TYPE">%2$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_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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulamaları göstermek ve aktarmak için <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</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_title_computer" msgid="4671071173916176037">"Google Play Hizmetleri"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>, telefonunuzdaki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; cihazının bu işlemi yapmasına izin verilsin mi?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; adlı cihazın telefonunuzdaki uygulamaları ve sistem özelliklerini aktarmasına izin verilsin mi?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s; ses, fotoğraflar, ödeme bilgileri, şifreler ve mesajlar da dahil olmak üzere telefonunuzda görünen veya oynatılan her şeye erişebilecek.&lt;br/&gt;&lt;br/&gt;%1$s siz bu iznin erişimini kaldırana kadar uygulamaları ve diğer sistem özelliklerini aktarabilecek."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına uygulamaları ve diğer sistem özelliklerini yakındaki cihazlara aktarmak için izin istiyor"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; cihazına aktarmasına izin verilsin mi?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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ı ve diğer sistem özelliklerini <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazına aktarabilecek."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulamaları ve diğer sistem özelliklerini aktarmak için <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Medya çıkışını değiştir"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Fotoğraflar ve medya"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Bildirimler"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Uygulamalar"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Yayınlama"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon aramaları yapma ve yönetme"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon arama kaydını okuma ve yazma"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS mesajları gönderme ve görüntüleme"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Yakındaki cihazları keşfedip bağlanma ve bu cihazların göreli konumunu belirleme"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Kişiler, mesajlar ve fotoğraflar da dahil olmak üzere tüm bildirimleri okuma"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Kişiler, mesajlar ve fotoğraflar gibi bilgiler dahil tüm bildirimleri okuma&lt;br/&gt;• Bildirim gönderme&lt;br/&gt;&lt;br/&gt;Dilediğiniz zaman bu uygulamanın bildirim okuma ve gönderme iznini Ayarlar &gt; Bildirimler bölümünden yönetebilirsiniz."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefonunuzun uygulamalarını yayınlayabilir"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefonunuzdan uygulamaları ve diğer sistem özelliklerini yayınlayabilir"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Kullanılabilir cihazların listesine erişip diğer uygulamalardan ses veya video akışı ya da yayını yapabilecek cihazları kontrol edin"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"cihaz"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 745e4f7..f170d3c 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Виберіть пристрій, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для налаштування"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) і отримає доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) і отримає доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
     <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="2872254734959842579">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Надайте додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ до цієї інформації з телефона"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Дозволити додатку &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; транслювати додатки телефона?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s матиме доступ до контенту, що відображається чи відтворюється на телефоні, зокрема до аудіо, фото, паролів і повідомлень.&lt;br/&gt;&lt;br/&gt;%1$s зможе транслювати додатки, поки ви не скасуєте цей дозвіл."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервіси для кількох пристроїв"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків між вашими пристроями"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запитує дозвіл на відображення й транслювання додатків на ваших пристроях"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"Дозволити додатку &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="1274464413649731829">"Додаток <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="1944832605534698562">"Додаток <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="4693714143506569253">"Надайте пристрою &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; доступ до цієї інформації з телефона"</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_title_computer" msgid="4671071173916176037">"Сервіси Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запитує дозвіл на доступ до фотографій, медіафайлів і сповіщень вашого телефона"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Дозволити додатку &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; виконувати цю дію?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Дозволити пристрою &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; транслювати додатки й системні функції телефона?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s матиме доступ до контенту, що відображається чи відтворюється на телефоні, зокрема до аудіо, фото, платіжної інформації, паролів і повідомлень.&lt;br/&gt;&lt;br/&gt;%1$s зможе транслювати додатки й системні функції, поки ви не скасуєте цей дозвіл."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) запитує дозвіл на трансляцію додатків та інших системних функцій на пристрої поблизу"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Дозволити пристрою &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; транслювати додатки й системні функції на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на пристрій &lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"Додаток <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_nearby_device_streaming" msgid="8509848562931818793">"Додаток <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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Змін. пристр. для відтв. медіа"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Фотографії та медіафайли"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Сповіщення"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Додатки"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Потокове передавання"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Телефонувати й керувати дзвінками"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Переглядати й записувати дані в журналі викликів телефона"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Надсилати й переглядати SMS-повідомлення"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Знаходити пристрої поблизу, підключатися до них і визначати їх відносне розташування"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Читати всі сповіщення, зокрема таку інформацію, як контакти, повідомлення й фотографії"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Читати всі сповіщення, зокрема таку інформацію, як контакти, повідомлення й фотографії&lt;br/&gt;• Надсилати сповіщення&lt;br/&gt;&lt;br/&gt;Ви можете будь-коли змінити дозвіл цього додатка на перегляд і надсилання сповіщень, вибравши \"Налаштування\" &gt; \"Сповіщення\"."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Транслювати додатки телефона"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Транслюйте додатки й інші системні функції зі свого телефона"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Отримувати доступ до списку доступних пристроїв і вибирати, з якого з них транслюватиметься аудіо або відео з інших додатків"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"телефоні"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"планшеті"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"телевізорі"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"пристрої"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index d92a2e0..fd6537e 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -21,28 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"‏‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‎ کے ذریعے منتخب کیے جانے کیلئے آلہ منتخب کریں"</string>
     <string name="chooser_title" msgid="2235819929238267637">"سیٹ اپ کرنے کے لیے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا انتخاب کریں"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"اس ایپ کو آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر کسی کال کرنے والے کے نام جیسی معلومات کی مطابقت پذیری کرنے اور ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر کسی کال کرنے والے کے نام جیسی معلومات کی مطابقت پذیری کرنے اور ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
     <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="2872254734959842579">"اس ایپ کو آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"‏اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"‏اجازت دیں&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; اپنے فون کی ایپس کو سلسلہ بندی کرنے کے لیے؟"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"‏%1$s کو فون پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، پاس ورڈز اور پیغامات۔&lt;br/&gt;&lt;br/&gt;اس وقت تک %1$s ایپس کو اسٹریم کر سکے گا جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کو ڈسپلے اور اسٹریم کرنے کے لیے اجازت کی درخواست کر رہی ہے"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"‏&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_app_streaming" msgid="1274464413649731829">"‏<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="1944832605534698562">"<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="4693714143506569253">"‏اپنے فون سے اس معلومات تک رسائی حاصل کرنے کی &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</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_title_computer" msgid="4671071173916176037">"‏Google Play سروسز"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے فون کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت کی درخواست کر رہی ہے"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"‏&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; کو یہ کارروائی انجام دینے کی اجازت دیں؟"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"‏آپ کے فون کی ایپس اور سسٹم کی خصوصیات کو سلسلہ بندی کرنے کی &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں؟"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for summary_nearby_device_streaming (4039565463149145573) -->
-    <skip />
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> آپ کے <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے ایپس اور سسٹم کی دیگر خصوصیات کی سلسلہ بندی قریبی آلات پر کرنے کی اجازت طلب کر رہی ہے"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"‏‎&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;‎ کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس اور سسٹم کی خصوصیات کو ‎&lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;‎ پر سلسلہ بندی کرنے کی اجازت دیں؟"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"‏<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_nearby_device_streaming" msgid="8509848562931818793">"<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>
@@ -64,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"میڈیا آؤٹ پٹ کو تبدیل کریں"</string>
     <string name="permission_storage" msgid="6831099350839392343">"تصاویر اور میڈیا"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"اطلاعات"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"ایپس"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"سلسلہ بندی"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"فون کالز کریں اور ان کا نظم کریں"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"فون کال لاگز پڑھیں اور لکھیں"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"‏SMS پیغامات بھیجیں اور دیکھیں"</string>
@@ -75,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"قریبی آلات کی متعلقہ پوزیشن تلاش کریں، ان سے منسلک کریں اور اس کا تعین کریں"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"رابطوں، پیغامات اور تصاویر جیسی معلومات سمیت تمام اطلاعات پڑھیں"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"‏• تمام اطلاعات کو پڑھیں، بشمول رابطے، پیغامات اور تصاویر&lt;br/&gt;• اطلاعات بھیجیں&lt;br/&gt;&lt;br/&gt;آپ ترتیبات &gt; اطلاعات میں کسی بھی وقت اس ایپ کی اطلاعات کو پڑھنے اور بھیجنے کی اہلیت کا نظم کر سکتے ہیں۔"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"اپنے فون سے ایپس اور سسٹم کی دیگر خصوصیات کی سلسلہ بندی کریں"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"دستیاب آلات کی فہرست تک رسائی حاصل کریں اور کنٹرول کریں کہ کون سی دوسری ایپس سے آڈیو یا ویڈیو کی سلسلہ بندی کرتی ہے یا کاسٹ کرتی ہے"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"فون"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ٹیبلیٹ"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"آلہ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index 80cd92c..f7a1ef7 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"soat"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; boshqaradigan qurilmani tanlang"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Sozlash uchun <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini tanlang"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Bu ilovaga chaqiruvchining ismi kabi maʼlumotlarni sinxronlash va <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida quyidagi amallarni bajarishga ruxsat beriladi"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Bu ilovaga chaqiruvchining ismi kabi maʼlumotlarni sinxronlash va <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi amallarni bajarishga ruxsat beriladi"</string>
     <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="2872254734959842579">"Bu ilova <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; telefondagi ilovalarni striming qilishiga ruxsat berasizmi?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s telefonda 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, %1$s ilovalarni striming qila oladi."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Qurilmalararo xizmatlar"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Qurilamalararo ilovalar strimingi uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nomidan ilovalarni koʻrsatish va striming qilishga ruxsat soʻramoqda"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"Bu ilova <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"&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_app_streaming" msgid="1274464413649731829">"<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, 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="1944832605534698562">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan ilovalarni koʻrsatish va striming qilishga ruxsat soʻramoqda"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</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_title_computer" msgid="4671071173916176037">"Google Play xizmatlari"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"Telefoningizdagi rasm, media va bildirishnomalarga kirish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasiga bu amalni bajarish uchun ruxsat berilsinmi?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; telefondagi ilovalar va tizim funksiyalarini striming qilishiga ruxsat berasizmi?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s telefonda 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, %1$s ilovalarni va tizim funksiyalarini striming qila oladi."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmangizdan nomidan atrofdagi qurilmalarga ilova va boshqa tizim funksiyalarini uzatish uchun ruxsat olmoqchi"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; qurilmasiga striming qilishiga ruxsat berasizmi?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 va tizim funksiyalarini <xliff:g id="DEVICE_NAME">%3$s</xliff:g> qurilmasiga striming qila oladi."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"<xliff:g id="APP_NAME">%1$s</xliff:g> qurilmalaringiz orasida ilovalar va boshqa tizim funksiyalarini striming qilish uchun <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Media chiqishini tanlash"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Suratlar va media"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Bildirishnomalar"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Ilovalar"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Striming"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Telefon qilish va chaqiruvlarni boshqarish"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Telefon chaqiruvlari jurnalini koʻrish va oʻzgartirish"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"SMS yuborish va ularni oʻqish"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Atrofdagi qurilmalarni qidirish, joylashuvini aniqlash va ularga ulanish"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oʻqish"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Barcha bildirishnomalarni, jumladan, kontaktlar, xabarlar va suratlarni oʻqish&lt;br/&gt;• Bildirishnoma yuborish&lt;br/&gt;&lt;br/&gt;Ilovaning bildirishnomalarga ruxsatini istalgan vaqt Sozlamalar > Bildirishnomalar orqali boshqarish mumkin."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Telefondagi ilovalarni translatsiya qilish"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Telefoningizdan ilovalar va tizim funksiyalarini translatsiya qilish"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Mavjud qurilmalar roʻyxatini olish va qaysi biri boshqa ilovalarga audio yoki video uzatishini boshqarish"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"telefon"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"planshet"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"qurilma"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index ca3f5cc..e668a69 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Chọn một thiết bị sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; quản lý"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> để thiết lập"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Ứng dụng này sẽ được phép đồng bộ hoá thông tin (chẳng hạn như tên của người đang gọi điện) và dùng những quyền sau trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> của bạn"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Ứng dụng này sẽ được phép đồng bộ hoá thông tin (chẳng hạn như tên của người đang gọi điện) và dùng những quyền sau trên <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> của bạn"</string>
     <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="2872254734959842579">"Ứng dụng này sẽ được phép dùng những quyền sau trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> của bạn"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"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 điện thoại của bạn"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"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 điện thoại của bạn?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc được phát trên điện thoại, bao gồm âm thanh, hình ảnh, mật khẩu và tin nhắn.&lt;br/&gt;&lt;br/&gt;%1$s sẽ có thể truyền trực tuyến các ứng dụng cho đến khi bạn ngừng cấp quyền này cho ứng dụng đó."</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Dịch vụ trên nhiều thiết bị"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay mặt cho <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> để hiển thị và truyền trực tuyến các ứng dụng giữa các thiết bị của bạn"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"<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>, bao gồm cả âm thanh, ảnh, 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="1944832605534698562">"<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> để hiển thị và truyền trực tuyến các ứng dụng giữa các thiết bị 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="4693714143506569253">"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 điện thoại của bạn"</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_title_computer" msgid="4671071173916176037">"Dịch vụ Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên điện thoại của bạn"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Cho phép &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; thực hiện hành động này?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Cho phép &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; truyền trực tuyến các ứng dụng và tính năng hệ thống trên điện thoại của bạn?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc được phát trên điện thoại, bao gồm âm thanh, hình ảnh, thông tin thanh toán, mật khẩu và tin nhắn.&lt;br/&gt;&lt;br/&gt;%1$s sẽ có thể truyền trực tuyến các ứng dụng và tính năng hệ thống cho đến khi bạn ngừng cấp quyền này cho ứng dụng đó."</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang thay <xliff:g id="DEVICE_NAME">%2$s</xliff:g> yêu cầu quyền truyền trực tuyến ứng dụng và các tính năng khác của hệ thống đến các thiết bị ở gần"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Cho phép &lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"<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 và tính năng của hệ thố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_nearby_device_streaming" msgid="8509848562931818793">"<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à những tính năng khác của hệ thống giữa các thiết bị 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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Thay đổi đầu ra đa phương tiện"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Ảnh và nội dung nghe nhìn"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Thông báo"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Ứng dụng"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Truyền trực tuyến"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Gọi và quản lý cuộc gọi điện thoại"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Đọc và ghi nhật ký cuộc gọi điện thoại"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Gửi và xem tin nhắn SMS"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Tìm, kết nối và xác định vị trí tương đối của các thiết bị ở gần"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Đọc tất cả thông báo, kể cả những thông tin như danh bạ, tin nhắn và ảnh"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Đọc tất cả thông báo, kể cả những thông tin như danh bạ, tin nhắn và ảnh&lt;br/&gt;• Gửi thông báo&lt;br/&gt;&lt;br/&gt;Bạn có thể quản lý tính năng đọc và gửi thông báo của ứng dụng này bất cứ lúc nào trong phần Cài đặt &gt; Thông báo."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Truyền các ứng dụng trên điện thoại của bạn"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Truyền trực tuyến ứng dụng và các tính năng khác của hệ thống từ điện thoại của bạn"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Truy cập danh sách thiết bị hiện có và kiểm soát việc thiết bị nào sẽ phát trực tuyến hoặc truyền âm thanh hoặc video từ các ứng dụng khác"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"điện thoại"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"máy tính bảng"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"TV"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"thiết bị"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 6daf4ff..8adb160 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"手表"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理的设备"</string>
     <string name="chooser_title" msgid="2235819929238267637">"选择 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 进行设置"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"该应用将可以同步信息(例如来电者的姓名),并可以获得您<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的以下权限"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"该应用将能同步信息(例如来电者的姓名),并能获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string>
     <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="2872254734959842579">"该应用将可以获得您<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的以下权限"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”&lt;strong&gt;&lt;/strong&gt;访问您手机中的这项信息"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"要允许 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 流式传输手机的应用吗?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s 将能够访问手机上可见或播放的任何内容,包括音频、照片、密码和消息。&lt;br/&gt;&lt;br/&gt;%1$s 将能够流式传输应用,除非您撤消此访问权限。"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨设备服务"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求在您的设备之间流式传输应用内容"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 请求在设备之间显示和流式传输应用"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"该应用将能获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"允许&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="1274464413649731829">"“<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="1944832605534698562">"“<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="4693714143506569253">"允许 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 访问您手机中的这项信息"</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_title_computer" msgid="4671071173916176037">"Google Play 服务"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求访问您手机上的照片、媒体内容和通知"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"允许&lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt;进行此操作?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"要允许 &lt;strong&gt;<xliff:g id="DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; 流式传输手机的应用和系统功能吗?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s 将能够访问手机上可见或播放的任何内容,包括音频、照片、付款信息、密码和消息。&lt;br/&gt;&lt;br/&gt;%1$s 将能够流式传输应用和系统功能,除非您撤消此访问权限。"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_NAME">%2$s</xliff:g>请求将应用和其他系统功能流式传输到附近的设备"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"允许&lt;strong&gt;<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;将您<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上的应用和系统功能流式传输到&lt;strong&gt;<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>&lt;/strong&gt;吗?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"“<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_nearby_device_streaming" msgid="8509848562931818793">"“<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"更改媒体输出"</string>
     <string name="permission_storage" msgid="6831099350839392343">"照片和媒体内容"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"应用"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"流式传输"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"拨打电话和管理通话"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"读取和写入手机通话记录"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"发送和查看短信"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"查找、连接附近的设备以及确定附近设备的相对位置"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"读取所有通知,包括通讯录、消息和照片等信息"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• 读取所有通知,包括通讯录、消息和照片等信息&lt;br/&gt;• 发送通知&lt;br/&gt;&lt;br/&gt;您随时可以依次前往“设置”&gt;“通知”并在其中管理此应用读取和发送通知的权限。"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"流式传输手机上的应用"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"从您的手机流式传输应用和其他系统功能"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"访问可用设备列表,并控制哪个设备可流式传输或投放其他应用的音频/视频"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"手机"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"平板电脑"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"电视"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"设备"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index ad9b4a2..fee37dd8 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"選擇要讓 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 管理的裝置"</string>
     <string name="chooser_title" msgid="2235819929238267637">"選擇要設定的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"此應用程式將可同步資訊 (例如來電者的名稱),並可在<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上取得以下權限"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"此應用程式將可同步資訊 (例如來電者的名稱),並可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string>
     <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="2872254734959842579">"此應用程式將可在<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上取得以下權限"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取你手機中的這項資料"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」串流手機應用程式內容嗎?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"%1$s 將能存取手機上顯示或播放的任何內容,包括音訊、相片、密碼和訊息。&lt;br/&gt;&lt;br/&gt;%1$s 將能串流應用程式內容,直至你移除此存取權限為止。"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求權限,以便在裝置間串流應用程式的內容"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求權限,以便在裝置間顯示和串流應用程式的內容"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"此應用程式將可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"要允許「<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="1274464413649731829">"「<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="1944832605534698562">"「<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="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取你手機中的這項資料"</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_title_computer" msgid="4671071173916176037">"Google Play 服務"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求權限,以便存取手機上的相片、媒體和通知"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;執行此操作嗎?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」串流手機應用程式內容和系統功能嗎?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"%1$s 將能存取手機上顯示或播放的任何內容,包括音訊、相片、付款資料、密碼和訊息。&lt;br/&gt;&lt;br/&gt;%1$s 將能串流應用程式內容和系統功能,直至你移除此存取權限為止。"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求權限,才能在附近的裝置上串流播放應用程式和其他系統功能"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"要允許「<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>」串流<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>應用程式內容和系統功能至「<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>」嗎?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"「<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_nearby_device_streaming" msgid="8509848562931818793">"「<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"變更媒體輸出"</string>
     <string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"應用程式"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"串流"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"撥打及管理通話"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"讀取及寫入手機通話記錄"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"傳送和查看短訊"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"尋找、連接及判斷附近裝置的相對位置"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"讀取所有通知,包括聯絡人、訊息和相片等資訊"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• 讀取所有通知,包括聯絡人、訊息和相片等資訊&lt;br/&gt;• 傳送通知&lt;br/&gt;&lt;br/&gt;你隨時可前往 [設定] &gt; [通知],管理此應用程式讀取和傳送通知的功能。"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"串流播放手機應用程式內容"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"串流播放手機中的應用程式和其他系統功能"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"存取可用裝置清單,並控制哪一部裝置可串流播放或投放其他應用程式的音訊或影片"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"手機"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"平板電腦"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"電視"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"裝置"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index a5817c4..28a3baf 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -21,26 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"選擇要讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的裝置"</string>
     <string name="chooser_title" msgid="2235819929238267637">"選擇要設定的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"這個應用程式將可同步處理資訊 (例如來電者名稱)、取得<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的這些權限"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"這個應用程式將可同步處理資訊 (例如來電者名稱) 及取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string>
     <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="2872254734959842579">"這個應用程式將可取得<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的這些權限"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取手機中的這項資訊"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;串流傳輸手機的應用程式嗎?"</string>
-    <string name="summary_app_streaming" msgid="295548145144086753">"「%1$s」將可存取手機顯示或播放的所有內容,包括音訊、相片、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「%1$s」將可串流傳輸應用程式,直到你移除這個權限為止。"</string>
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"為了在裝置間串流傳輸應用程式內容,「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求相關權限"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求必要權限,以便在裝置間顯示及串流傳輸應用程式"</string>
+    <string name="summary_glasses" msgid="5469208629679579157">"這個應用程式將可取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string>
+    <string name="title_app_streaming" msgid="6845373585257287200">"要允許「<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="1274464413649731829">"「<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="1944832605534698562">"「<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="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;存取你手機中的這項資訊"</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_title_computer" msgid="4671071173916176037">"Google Play 服務"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"為了存取手機上的相片、媒體和通知,「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求相關權限"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;執行這項操作嗎?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;串流傳輸手機的應用程式及存取系統功能嗎?"</string>
-    <string name="summary_nearby_device_streaming" msgid="4039565463149145573">"「%1$s」將可存取手機顯示或播放的所有內容,包括音訊、相片、付款資訊、密碼和訊息。&lt;br/&gt;&lt;br/&gt;「%1$s」將可串流傳輸應用程式及存取系統功能,直到你移除這個權限為止。"</string>
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求必要權限,才能在鄰近裝置上串流播放應用程式和其他系統功能"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"要允許「<xliff:g id="DEVICE_NAME_0">%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_1">%3$s</xliff:g>&lt;/strong&gt; 嗎?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"「<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_nearby_device_streaming" msgid="8509848562931818793">"「<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>
@@ -62,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"變更媒體輸出"</string>
     <string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"通知"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"應用程式"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"串流"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"撥打電話及管理通話"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"讀取及寫入通話記錄"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"傳送及查看簡訊"</string>
@@ -73,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"尋找、連線及判斷鄰近裝置的相對位置"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"讀取所有通知,包括聯絡人、訊息和相片等資訊"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• 讀取所有通知,包括聯絡人、訊息和相片等資訊&lt;br/&gt;• 傳送通知&lt;br/&gt;&lt;br/&gt;你隨時可以前往「設定」&gt;「通知」,管理這個應用程式讀取和傳送通知的功能。"</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"串流傳輸手機應用程式內容"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"串流播放手機中的應用程式和其他系統功能"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"存取可用裝置清單,並控制哪一部裝置可串流播放或投放其他應用程式的音訊或影片"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"手機"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"平板電腦"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"電視"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"裝置"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 4b00dcb..5966f8b 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -21,30 +21,21 @@
     <string name="profile_name_watch" msgid="576290739483672360">"buka"</string>
     <string name="chooser_title_non_profile" msgid="6035023914517087400">"Khetha idivayisi engaphathwa nge-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="chooser_title" msgid="2235819929238267637">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ukusetha"</string>
-    <string name="summary_watch" msgid="7962014927042971830">"Le-app izovunyelwa ukuvumelanisa ulwazi, olufana negama lomuntu ofonayo, iphinde ifinyelele lezi zimvume ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> yakho"</string>
+    <string name="summary_watch" msgid="8134580124808507407">"Le app izovunyelwa ukuvumelanisa ulwazi, olufana negama lomuntu ofonayo, iphinde ifinyelele lezi zimvume ku-<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yakho"</string>
     <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="2872254734959842579">"Le-app izovunyelwa ukufinyelela lezi zimvume ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> yakho"</string>
-    <string name="title_app_streaming" msgid="2270331024626446950">"Vumela i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifinyelele lolu lwazi kusukela efonini yakho"</string>
-    <string name="title_app_streaming_with_mirroring" msgid="3364582597581570658">"Vumela i-<xliff:g id="APP_NAME">%1$s</xliff:g> ukusakaza ama-app efoni yakho?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for summary_app_streaming (295548145144086753) -->
-    <skip />
-    <string name="helper_title_app_streaming" msgid="4151687003439969765">"Amasevisi amadivayisi amaningi"</string>
-    <string name="helper_summary_app_streaming" msgid="2396773196949578425">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yakho ukuze isakaze-bukhoma ama-app phakathi kwamadivayisi akho"</string>
-    <string name="helper_summary_app_streaming_with_mirroring" msgid="6138581029144467467">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yakho yokubonisa nokusakaza ama-app phakathi kwamadivayisi wakho"</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>
+    <string name="title_app_streaming" msgid="6845373585257287200">"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_app_streaming" msgid="1274464413649731829">"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>, okuhlanganisa umsindo, izithombe, 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="1944832605534698562">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> yakho yokubonisa nokusakaza ama-app phakathi kwamadivayisi wakho"</string>
     <string name="title_automotive_projection" msgid="3296005598978412847"></string>
     <string name="summary_automotive_projection" msgid="8683801274662496164"></string>
-    <string name="title_computer" msgid="4693714143506569253">"Vumela &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukufinyelela lolu lwazi kusuka efonini yakho"</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_title_computer" msgid="4671071173916176037">"Amasevisi we-Google Play"</string>
-    <string name="helper_summary_computer" msgid="8774832742608187072">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yakho ukuze ifinyelele izithombe zefoni yakho, imidiya nezaziso"</string>
-    <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vumela i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ukwenza lesi senzo?"</string>
-    <string name="title_nearby_device_streaming_with_mirroring" msgid="242855799919611657">"Vumela i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ukuze usakaze ama-app wefoni yakho nezakhi zesistimu?"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for summary_nearby_device_streaming (4039565463149145573) -->
-    <skip />
-    <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukusakaza ama-app nezinye izakhi zesistimu kumadivayisi aseduze"</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>
+    <string name="title_nearby_device_streaming" msgid="4295322493408411976">"Vumela &lt;strong&gt;i-<xliff:g id="DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt; ukuze 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_1">%3$s</xliff:g>&lt;/strong&gt;?"</string>
+    <string name="summary_nearby_device_streaming" msgid="962267343109051648">"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 nezakhi zesistimu ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g> uze ususe ukufinyelela kule mvume."</string>
+    <string name="helper_summary_nearby_device_streaming" msgid="8509848562931818793">"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 isakaze ama-app nezinye zakhi zesistimu phakathi kwamadivayisi wakho"</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>
@@ -66,8 +57,6 @@
     <string name="permission_media_routing_control" msgid="5498639511586715253">"Shintsha umphumela wemidiya"</string>
     <string name="permission_storage" msgid="6831099350839392343">"Izithombe nemidiya"</string>
     <string name="permission_notifications" msgid="4099418516590632909">"Izaziso"</string>
-    <string name="permission_app_streaming" msgid="6009695219091526422">"Ama-app"</string>
-    <string name="permission_nearby_device_streaming" msgid="1023325519477349499">"Iyasakaza"</string>
     <string name="permission_phone_summary" msgid="8246321093970051702">"Yenza futhi ulawule amakholi efoni"</string>
     <string name="permission_call_logs_summary" msgid="7545243592757693321">"Funda futhi ubhale irekhodi lamakholi efoni"</string>
     <string name="permission_sms_summary" msgid="8499509535410068616">"Thumela futhi ubuke imiyalezo ye-SMS"</string>
@@ -77,10 +66,10 @@
     <string name="permission_nearby_devices_summary" msgid="1306752848196464817">"Thola, uxhume, futhi unqume indawo yamadivayisi aseduze"</string>
     <string name="permission_notification_listener_access_summary" msgid="7856071768185367749">"Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe"</string>
     <string name="permission_notifications_summary" msgid="2272810466047367030">"• Funda zonke izaziso, okuhlanganisa ulwazi loxhumana nabo, imiyalezo, nezithombe&lt;br/&gt;•Thumela izaziso&lt;br/&gt;&lt;br/&gt;Ungakwazi ukulawula ikhono lale app lokufunda nokuthumela izaziso noma nini kokuthi Amasethingi Nezaziso."</string>
-    <string name="permission_app_streaming_summary" msgid="606923325679670624">"Sakaza ama-app wefoni yakho"</string>
     <string name="permission_storage_summary" msgid="3918240895519506417"></string>
-    <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Sakaza ama-app nezinye izakhi zesistimu kusuka kufoni yakho"</string>
     <string name="permission_media_routing_control_summary" msgid="2714631092321412250">"Finyelela kuhlu lwamadivayisi atholakalayo futhi ulawule ukuthi iyiphi esakaza umsindo noma ividiyo kusuka kwamanye ama-app"</string>
     <string name="device_type" product="default" msgid="8268703872070046263">"ifoni"</string>
     <string name="device_type" product="tablet" msgid="5038791954983067774">"ithebulethi"</string>
+    <string name="device_type" product="tv" msgid="5355611506659405636">"i-tv"</string>
+    <string name="device_type" product="device" msgid="1526125965802507189">"idivayisi"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index e8e24f4..fe7cfc6 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -103,11 +103,10 @@
     <style name="NegativeButtonMultipleDevices"
            parent="@android:style/Widget.Material.Button.Colored">
         <item name="android:layout_width">wrap_content</item>
-        <item name="android:layout_height">36dp</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:minHeight">36dp</item>>
         <item name="android:textAllCaps">false</item>
         <item name="android:textSize">14sp</item>
-        <item name="android:paddingLeft">6dp</item>
-        <item name="android:paddingRight">6dp</item>
         <item name="android:background">@drawable/btn_negative_multiple_devices</item>
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
     </style>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index 6117330..7974a37 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -19,6 +19,7 @@
 import static android.companion.CompanionDeviceManager.RESULT_CANCELED;
 import static android.companion.CompanionDeviceManager.RESULT_DISCOVERY_TIMEOUT;
 import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
+import static android.companion.CompanionDeviceManager.RESULT_SECURITY_ERROR;
 import static android.companion.CompanionDeviceManager.RESULT_USER_REJECTED;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
@@ -33,6 +34,7 @@
 import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_TITLES;
 import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES;
 import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_SELF_MANAGED_PROFILES;
+import static com.android.companiondevicemanager.Utils.RESULT_CODE_TO_REASON;
 import static com.android.companiondevicemanager.Utils.getApplicationLabel;
 import static com.android.companiondevicemanager.Utils.getHtmlFromResources;
 import static com.android.companiondevicemanager.Utils.getIcon;
@@ -51,6 +53,7 @@
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
+import android.companion.Flags;
 import android.companion.IAssociationRequestCallback;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -231,7 +234,7 @@
         boolean forCancelDialog = intent.getBooleanExtra(EXTRA_FORCE_CANCEL_CONFIRMATION, false);
         if (forCancelDialog) {
             Slog.i(TAG, "Cancelling the user confirmation");
-            cancel(RESULT_CANCELED);
+            cancel(RESULT_CANCELED, null);
             return;
         }
 
@@ -243,10 +246,15 @@
         if (appCallback == null) {
             return;
         }
-        Slog.e(TAG, "More than one AssociationRequests are processing.");
 
         try {
-            appCallback.onFailure(RESULT_INTERNAL_ERROR);
+            if (Flags.associationFailureCode()) {
+                appCallback.onFailure(
+                        RESULT_SECURITY_ERROR, "More than one AssociationRequests are processing.");
+            } else {
+                appCallback.onFailure(
+                        RESULT_INTERNAL_ERROR, "More than one AssociationRequests are processing.");
+            }
         } catch (RemoteException ignore) {
         }
     }
@@ -257,7 +265,7 @@
 
         // TODO: handle config changes without cancelling.
         if (!isDone()) {
-            cancel(RESULT_CANCELED); // will finish()
+            cancel(RESULT_CANCELED, null); // will finish()
         }
     }
 
@@ -331,7 +339,7 @@
                 && CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
             synchronized (LOCK) {
                 if (sDiscoveryStarted) {
-                    cancel(RESULT_DISCOVERY_TIMEOUT);
+                    cancel(RESULT_DISCOVERY_TIMEOUT, null);
                 }
             }
         }
@@ -371,7 +379,7 @@
         mCdmServiceReceiver.send(RESULT_CODE_ASSOCIATION_APPROVED, data);
     }
 
-    private void cancel(int failureCode) {
+    private void cancel(int errorCode, @Nullable CharSequence error) {
         if (isDone()) {
             Slog.w(TAG, "Already done: " + (mApproved ? "Approved" : "Cancelled"));
             return;
@@ -385,13 +393,14 @@
 
         // First send callback to the app directly...
         try {
-            Slog.i(TAG, "Sending onFailure to app due to failureCode=" + failureCode);
-            mAppCallback.onFailure(failureCode);
+            CharSequence errorMessage = error != null
+                    ? error : RESULT_CODE_TO_REASON.get(errorCode);
+            mAppCallback.onFailure(errorCode, errorMessage);
         } catch (RemoteException ignore) {
         }
 
         // ... then set result and finish ("sending" onActivityResult()).
-        setResultAndFinish(null, failureCode);
+        setResultAndFinish(null, errorCode);
     }
 
     private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
@@ -436,7 +445,7 @@
             }
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
-            cancel(RESULT_INTERNAL_ERROR);
+            cancel(RESULT_INTERNAL_ERROR, e.getMessage());
             return;
         }
 
@@ -625,7 +634,7 @@
         // Disable the button, to prevent more clicks.
         v.setEnabled(false);
 
-        cancel(RESULT_USER_REJECTED);
+        cancel(RESULT_USER_REJECTED, null);
     }
 
     private void onShowHelperDialog(View view) {
@@ -755,8 +764,8 @@
             };
 
     @Override
-    public void onShowHelperDialogFailed() {
-        cancel(RESULT_INTERNAL_ERROR);
+    public void onShowHelperDialogFailed(CharSequence errorMessage) {
+        cancel(RESULT_INTERNAL_ERROR, errorMessage);
     }
 
     @Override
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
index ec92987..b2d78da 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionVendorHelperDialogFragment.java
@@ -54,7 +54,7 @@
     private Button mButton;
 
     interface CompanionVendorHelperDialogListener {
-        void onShowHelperDialogFailed();
+        void onShowHelperDialogFailed(CharSequence error);
         void onHelperDialogDismissed();
     }
 
@@ -110,7 +110,7 @@
             appLabel = getApplicationLabel(getContext(), packageName, userId);
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
-            mListener.onShowHelperDialogFailed();
+            mListener.onShowHelperDialogFailed(e.getMessage());
             return;
         }
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
index 8c14f80..2f97132 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/Utils.java
@@ -16,6 +16,15 @@
 
 package com.android.companiondevicemanager;
 
+import static android.companion.CompanionDeviceManager.REASON_CANCELED;
+import static android.companion.CompanionDeviceManager.REASON_DISCOVERY_TIMEOUT;
+import static android.companion.CompanionDeviceManager.REASON_USER_REJECTED;
+import static android.companion.CompanionDeviceManager.RESULT_CANCELED;
+import static android.companion.CompanionDeviceManager.RESULT_DISCOVERY_TIMEOUT;
+import static android.companion.CompanionDeviceManager.RESULT_USER_REJECTED;
+
+import static java.util.Collections.unmodifiableMap;
+
 import android.annotation.NonNull;
 import android.annotation.StringRes;
 import android.content.Context;
@@ -31,6 +40,9 @@
 import android.os.ResultReceiver;
 import android.text.Html;
 import android.text.Spanned;
+import android.util.ArrayMap;
+
+import java.util.Map;
 
 /**
  * Utilities.
@@ -40,6 +52,17 @@
             "android.companion.vendor_icon";
     private static final String COMPANION_DEVICE_ACTIVITY_VENDOR_NAME =
             "android.companion.vendor_name";
+    // This map solely the common error messages that occur during the Association
+    // creation process.
+    static final Map<Integer, String> RESULT_CODE_TO_REASON;
+    static {
+        final Map<Integer, String> map = new ArrayMap<>();
+        map.put(RESULT_CANCELED, REASON_CANCELED);
+        map.put(RESULT_USER_REJECTED, REASON_USER_REJECTED);
+        map.put(RESULT_DISCOVERY_TIMEOUT, REASON_DISCOVERY_TIMEOUT);
+
+        RESULT_CODE_TO_REASON = unmodifiableMap(map);
+    }
 
     /**
      * Convert an instance of a "locally-defined" ResultReceiver to an instance of
diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml
index 2d98870e..2976e66 100644
--- a/packages/CredentialManager/res/values-be/strings.xml
+++ b/packages/CredentialManager/res/values-be/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Стварыць ключ доступу на іншай прыладзе?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Захаваць пароль на іншай прыладзе?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Захаваць спосаб уваходу на іншай прыладзе?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Выкарыстоўваць папку \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" для ўсіх спосабаў уваходу?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Выкарыстоўваць <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> для ўсіх спосабаў уваходу?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"Каб вам было прасцей уваходзіць у сістэму, вашы паролі і ключы доступу будуць захоўвацца ў менеджары пароляў для <xliff:g id="USERNAME">%1$s</xliff:g>."</string>
     <string name="set_as_default" msgid="4415328591568654603">"Выкарыстоўваць стандартна"</string>
     <string name="settings" msgid="6536394145760913145">"Налады"</string>
diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml
index 6a4974d..66ad31b 100644
--- a/packages/CredentialManager/res/values-eu/strings.xml
+++ b/packages/CredentialManager/res/values-eu/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Beste gailu batean sarbide-gako bat sortu nahi duzu?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Beste gailu batean pasahitza gorde nahi duzu?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Beste gailu batean saioa hasteko modua gorde nahi duzu?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> erabili nahi duzu kredentzial guztietarako?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> erabili nahi duzu beti saioa hasteko?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> erabiltzailearen pasahitz-kudeatzaile honek pasahitzak eta sarbide-gakoak gordeko ditu saioa erraz has dezazun"</string>
     <string name="set_as_default" msgid="4415328591568654603">"Ezarri lehenetsi gisa"</string>
     <string name="settings" msgid="6536394145760913145">"Ezarpenak"</string>
diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml
index 897f444..ffbe7c8 100644
--- a/packages/CredentialManager/res/values-kn/strings.xml
+++ b/packages/CredentialManager/res/values-kn/strings.xml
@@ -53,7 +53,7 @@
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"ಇನ್ನೊಂದು ಸಾಧನದಲ್ಲಿ ಪಾಸ್‌ವರ್ಡ್ ಉಳಿಸಬೇಕೆ?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"ಮತ್ತೊಂದು ಸಾಧನದಲ್ಲಿ ಸೈನ್-ಇನ್ ಅನ್ನು ಉಳಿಸಬೇಕೆ?"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"ನಿಮ್ಮ ಎಲ್ಲಾ ಸೈನ್-ಇನ್‌ಗಳಿಗಾಗಿ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ಅನ್ನು ಬಳಸಬೇಕೇ?"</string>
-    <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ ಈ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕವು ನಿಮಗೆ ಸುಲಭವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡುವುದಕ್ಕೆ ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಪಾಸ್‌ಕೀಗಳನ್ನು ಸಂಗ್ರಹಿಸುತ್ತದೆ"</string>
+    <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> ಗಾಗಿ ಈ ಪಾಸ್‌ವರ್ಡ್ ನಿರ್ವಾಹಕವು ನಿಮಗೆ ಸುಲಭವಾಗಿ ಸೈನ್ ಇನ್ ಮಾಡುವುದಕ್ಕೆ ಸಹಾಯ ಮಾಡಲು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು ಮತ್ತು ಪಾಸ್‌ಕೀಗಳನ್ನು ಸ್ಟೋರ್ ಮಾಡುತ್ತದೆ"</string>
     <string name="set_as_default" msgid="4415328591568654603">"ಡೀಫಾಲ್ಟ್ ಆಗಿ ಸೆಟ್ ಮಾಡಿ"</string>
     <string name="settings" msgid="6536394145760913145">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="use_once" msgid="9027366575315399714">"ಒಂದು ಬಾರಿ ಬಳಸಿ"</string>
diff --git a/packages/CredentialManager/res/values-ky/strings.xml b/packages/CredentialManager/res/values-ky/strings.xml
index 4172b51..f54e1b2 100644
--- a/packages/CredentialManager/res/values-ky/strings.xml
+++ b/packages/CredentialManager/res/values-ky/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Башка түзмөктө киргизүүчү ачкычты түзөсүзбү?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Сырсөздү башка түзмөктө сактайсызбы?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Кирүү маалыматын башка түзмөктө сактайсызбы?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> бардык аккаунттарга кирүү үчүн колдонулсунбу?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> бардык аккаунттарга кирүү үчүн колдоносузбу?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"Сырсөздөрүңүздү жана киргизүүчү ачкычтарыңызды <xliff:g id="USERNAME">%1$s</xliff:g> аккаунтуңуздагы сырсөздөрдү башкаргычка сактап коюп, каалаган убакта колдоно берсеңиз болот"</string>
     <string name="set_as_default" msgid="4415328591568654603">"Демейки катары коюу"</string>
     <string name="settings" msgid="6536394145760913145">"Параметрлер"</string>
diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml
index 7eebec5..10b651b 100644
--- a/packages/CredentialManager/res/values-mr/strings.xml
+++ b/packages/CredentialManager/res/values-mr/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"दुसऱ्या डिव्हाइसवर पासकी तयार करायची आहे का?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"दुसऱ्या डिव्हाइसवर पासवर्ड सेव्ह करायचा आहे का?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"दुसऱ्या डिव्हाइसवर साइन-इन सेव्ह करायचे आहे का?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"तुमच्या सर्व साइन-इन साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>वापरायचे का?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"तुमच्या सर्व साइन-इन साठी <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> वापरायचे का?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"तुम्हाला सहजरीत्या साइन इन करण्यात मदत करण्यासाठी हा <xliff:g id="USERNAME">%1$s</xliff:g> चा पासवर्ड व्यवस्थापक तुमचे पासवर्ड आणि पासकी स्टोअर करेल"</string>
     <string name="set_as_default" msgid="4415328591568654603">"डिफॉल्ट म्हणून सेट करा"</string>
     <string name="settings" msgid="6536394145760913145">"सेटिंग्ज"</string>
diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml
index 309299c..3381ece 100644
--- a/packages/CredentialManager/res/values-pa/strings.xml
+++ b/packages/CredentialManager/res/values-pa/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"ਕੀ ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ \'ਤੇ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"ਕੀ ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ \'ਤੇ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"ਕੀ ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ \'ਤੇ ਸਾਈਨ-ਇਨ ਕ੍ਰੀਡੈਂਸ਼ੀਅਲ ਰੱਖਿਅਤ ਕਰਨੇ ਹਨ?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ਕੀ ਆਪਣੇ ਸਾਰੇ ਸਾਈਨ-ਇਨਾਂ ਲਈ<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"ਕੀ ਆਪਣੇ ਸਾਰੇ ਸਾਈਨ-ਇਨਾਂ ਲਈ <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> ਦਾ ਇਹ Password Manager ਆਸਾਨੀ ਨਾਲ ਸਾਈਨ-ਇਨ ਕਰਨ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕਰਨ ਲਈ ਤੁਹਾਡੇ ਪਾਸਵਰਡਾਂ ਅਤੇ ਪਾਸਕੀਆਂ ਨੂੰ ਸਟੋਰ ਕਰੇਗਾ"</string>
     <string name="set_as_default" msgid="4415328591568654603">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਵਜੋਂ ਸੈੱਟ ਕਰੋ"</string>
     <string name="settings" msgid="6536394145760913145">"ਸੈਟਿੰਗਾਂ"</string>
diff --git a/packages/CredentialManager/res/values-pl/strings.xml b/packages/CredentialManager/res/values-pl/strings.xml
index caa7b09..6514887 100644
--- a/packages/CredentialManager/res/values-pl/strings.xml
+++ b/packages/CredentialManager/res/values-pl/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Utworzyć klucz dostępu na innym urządzeniu?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Zapisać hasło na innym urządzeniu?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Zapisać sposób logowania się na innym urządzeniu?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Używać usługi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> w przypadku wszystkich danych logowania?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Używać usługi <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> do wszystkich danych logowania?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"Menedżer haseł na koncie <xliff:g id="USERNAME">%1$s</xliff:g> będzie zapisywał Twoje hasła i klucze dostępu, aby ułatwić Ci logowanie"</string>
     <string name="set_as_default" msgid="4415328591568654603">"Ustaw jako domyślną"</string>
     <string name="settings" msgid="6536394145760913145">"Ustawienia"</string>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
index 9186c59..c065f8f 100644
--- a/packages/CredentialManager/res/values-pt-rPT/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -54,7 +54,7 @@
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Guardar o início de sessão noutro dispositivo?"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"Usar <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> para todos os seus inícios de sessão?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"Este gestor de palavras-passe de <xliff:g id="USERNAME">%1$s</xliff:g> armazena as suas palavras-passe e chaves de acesso para ajudar a iniciar sessão facilmente"</string>
-    <string name="set_as_default" msgid="4415328591568654603">"Definir como predefinição"</string>
+    <string name="set_as_default" msgid="4415328591568654603">"Predefinir"</string>
     <string name="settings" msgid="6536394145760913145">"Definições"</string>
     <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string>
     <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> palavras-passe • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string>
diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml
index 9e42dbb..6f12bee 100644
--- a/packages/CredentialManager/res/values-ro/strings.xml
+++ b/packages/CredentialManager/res/values-ro/strings.xml
@@ -53,7 +53,7 @@
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvezi parola pe alt dispozitiv?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvezi datele de conectare pe alt dispozitiv?"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"Folosești <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> pentru toate conectările?"</string>
-    <string name="use_provider_for_all_description" msgid="1998772715863958997">"Managerul de parole pentru <xliff:g id="USERNAME">%1$s</xliff:g> îți va stoca parolele și cheile de acces, pentru a te ajuta să te conectezi cu ușurință"</string>
+    <string name="use_provider_for_all_description" msgid="1998772715863958997">"Managerul de parole pentru <xliff:g id="USERNAME">%1$s</xliff:g> îți va stoca parolele și cheile de acces, pentru a te ajuta să te conectezi cu ușurință."</string>
     <string name="set_as_default" msgid="4415328591568654603">"Setează ca prestabilite"</string>
     <string name="settings" msgid="6536394145760913145">"Setări"</string>
     <string name="use_once" msgid="9027366575315399714">"Folosește o dată"</string>
diff --git a/packages/CredentialManager/res/values-ru/strings.xml b/packages/CredentialManager/res/values-ru/strings.xml
index 936ff79..13a782b 100644
--- a/packages/CredentialManager/res/values-ru/strings.xml
+++ b/packages/CredentialManager/res/values-ru/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Создать ключ доступа на другом устройстве?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Сохранить пароль на другом устройстве?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Сохранить учетные данные на другом устройстве?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Всегда входить с помощью приложения \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\"?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Всегда использовать <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> для входа?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"В этом менеджере паролей пользователь <xliff:g id="USERNAME">%1$s</xliff:g> сможет сохранять пароли и ключи доступа для быстрого входа."</string>
     <string name="set_as_default" msgid="4415328591568654603">"Использовать по умолчанию"</string>
     <string name="settings" msgid="6536394145760913145">"Настройки"</string>
diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml
index 96d2676..d359a73 100644
--- a/packages/CredentialManager/res/values-ta/strings.xml
+++ b/packages/CredentialManager/res/values-ta/strings.xml
@@ -53,7 +53,7 @@
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"வேறொரு சாதனத்தில் கடவுச்சொல்லைச் சேமிக்கவா?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"வேறொரு சாதனத்தில் உள்நுழைவைச் சேமிக்கவா?"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"உங்கள் அனைத்து உள்நுழைவுகளுக்கும் <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>ஐப் பயன்படுத்தவா?"</string>
-    <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> என்ற மின்னஞ்சல் முகவரிக்கான இந்தக் கடவுச்சொல் நிர்வாகி உங்கள் கடவுச்சொற்களையும் கடவுச்சாவிகளையும் சேமித்து நீங்கள் எளிதாக உள்நுழைய உதவும்"</string>
+    <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> என்ற கணக்குக்கான இந்தக் கடவுச்சொல் நிர்வாகி உங்கள் கடவுச்சொற்களையும் கடவுச்சாவிகளையும் சேமித்து நீங்கள் எளிதாக உள்நுழைய உதவும்"</string>
     <string name="set_as_default" msgid="4415328591568654603">"இயல்பானதாக அமை"</string>
     <string name="settings" msgid="6536394145760913145">"அமைப்புகள்"</string>
     <string name="use_once" msgid="9027366575315399714">"ஒருமுறை பயன்படுத்தவும்"</string>
diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml
index b423b2c..d8d6ffd 100644
--- a/packages/CredentialManager/res/values-tr/strings.xml
+++ b/packages/CredentialManager/res/values-tr/strings.xml
@@ -56,7 +56,7 @@
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> için bu şifre yöneticisi, şifrelerinizi ve geçiş anahtarlarınızı saklayarak kolayca oturum açmanıza yardımcı olur"</string>
     <string name="set_as_default" msgid="4415328591568654603">"Varsayılan olarak ayarla"</string>
     <string name="settings" msgid="6536394145760913145">"Ayarlar"</string>
-    <string name="use_once" msgid="9027366575315399714">"Bir kez kullanın"</string>
+    <string name="use_once" msgid="9027366575315399714">"Bir kez kullan"</string>
     <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> geçiş anahtarı"</string>
     <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> şifre"</string>
     <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> geçiş anahtarı"</string>
diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml
index e746130..2c483bd 100644
--- a/packages/CredentialManager/res/values-uk/strings.xml
+++ b/packages/CredentialManager/res/values-uk/strings.xml
@@ -52,7 +52,7 @@
     <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Створити ключ доступу на іншому пристрої?"</string>
     <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Зберегти пароль на іншому пристрої?"</string>
     <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Зберегти дані для входу на іншому пристрої?"</string>
-    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Використовувати сервіс <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> в усіх випадках входу?"</string>
+    <string name="use_provider_for_all_title" msgid="4201020195058980757">"Завжди використовувати <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> для входу?"</string>
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"Цей менеджер паролів для користувача <xliff:g id="USERNAME">%1$s</xliff:g> зберігатиме ваші паролі й ключі доступу, щоб ви могли легко входити в облікові записи"</string>
     <string name="set_as_default" msgid="4415328591568654603">"Вибрати за умовчанням"</string>
     <string name="settings" msgid="6536394145760913145">"Налаштування"</string>
diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml
index c6ac743..c01ccbe 100644
--- a/packages/CredentialManager/res/values-zh-rHK/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml
@@ -56,7 +56,7 @@
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"此密碼管理工具將儲存「<xliff:g id="USERNAME">%1$s</xliff:g>」的密碼和密鑰,協助你輕鬆登入"</string>
     <string name="set_as_default" msgid="4415328591568654603">"設定為預設"</string>
     <string name="settings" msgid="6536394145760913145">"設定"</string>
-    <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+    <string name="use_once" msgid="9027366575315399714">"使用一次"</string>
     <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼 • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密鑰"</string>
     <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
     <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密鑰"</string>
diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml
index 6b22e36..2d1e8e1 100644
--- a/packages/CredentialManager/res/values-zh-rTW/strings.xml
+++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml
@@ -56,7 +56,7 @@
     <string name="use_provider_for_all_description" msgid="1998772715863958997">"<xliff:g id="USERNAME">%1$s</xliff:g> 的密碼管理工具會儲存密碼和密碼金鑰,協助你輕鬆登入"</string>
     <string name="set_as_default" msgid="4415328591568654603">"設為預設"</string>
     <string name="settings" msgid="6536394145760913145">"設定"</string>
-    <string name="use_once" msgid="9027366575315399714">"單次使用"</string>
+    <string name="use_once" msgid="9027366575315399714">"僅限這次"</string>
     <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼 • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 個密碼金鑰"</string>
     <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 個密碼"</string>
     <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 個密碼金鑰"</string>
diff --git a/packages/CredentialManager/wear/res/values-nb/strings.xml b/packages/CredentialManager/wear/res/values-nb/strings.xml
index 0c45c9c..4b62b0e 100644
--- a/packages/CredentialManager/wear/res/values-nb/strings.xml
+++ b/packages/CredentialManager/wear/res/values-nb/strings.xml
@@ -22,8 +22,8 @@
     <string name="use_password_title" msgid="4655101984031246476">"Vil du bruke passord?"</string>
     <string name="dialog_dismiss_button" msgid="989567669882005067">"Lukk"</string>
     <string name="dialog_continue_button" msgid="8630290044077052145">"Fortsett"</string>
-    <string name="dialog_sign_in_options_button" msgid="448002958902615054">"Påloggingsalternativer"</string>
-    <string name="sign_in_options_title" msgid="6720572645638986680">"Påloggingsalternativer"</string>
+    <string name="dialog_sign_in_options_button" msgid="448002958902615054">"Påloggingvalg"</string>
+    <string name="sign_in_options_title" msgid="6720572645638986680">"Påloggingsvalg"</string>
     <string name="provider_list_title" msgid="6803918216129492212">"Administrer pålogginger"</string>
     <string name="choose_sign_in_title" msgid="3616025924746872202">"Velg en pålogging"</string>
     <string name="choose_passkey_title" msgid="8459270617632817465">"Velg passnøkkel"</string>
diff --git a/packages/CredentialManager/wear/res/values-sv/strings.xml b/packages/CredentialManager/wear/res/values-sv/strings.xml
index 2d0254a6..0d4d12f 100644
--- a/packages/CredentialManager/wear/res/values-sv/strings.xml
+++ b/packages/CredentialManager/wear/res/values-sv/strings.xml
@@ -23,7 +23,7 @@
     <string name="dialog_dismiss_button" msgid="989567669882005067">"Stäng"</string>
     <string name="dialog_continue_button" msgid="8630290044077052145">"Fortsätt"</string>
     <string name="dialog_sign_in_options_button" msgid="448002958902615054">"Inloggningsalternativ"</string>
-    <string name="sign_in_options_title" msgid="6720572645638986680">"Inloggningsalternativ"</string>
+    <string name="sign_in_options_title" msgid="6720572645638986680">"Inloggnings­alternativ"</string>
     <string name="provider_list_title" msgid="6803918216129492212">"Hantera inloggningar"</string>
     <string name="choose_sign_in_title" msgid="3616025924746872202">"Välj en inloggning"</string>
     <string name="choose_passkey_title" msgid="8459270617632817465">"Välj nyckel"</string>
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
index e58de64..5bd26e113 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/CredentialsScreenChip.kt
@@ -48,45 +48,14 @@
 /* Used as credential suggestion or user action chip. */
 @Composable
 fun CredentialsScreenChip(
-    label: String,
+    primaryText: @Composable () -> Unit,
+    secondaryText: (@Composable () -> Unit)? = null,
     onClick: () -> Unit,
-    secondaryLabel: String? = null,
     icon: Drawable? = null,
     isAuthenticationEntryLocked: Boolean? = null,
-    textAlign: TextAlign = TextAlign.Center,
     modifier: Modifier = Modifier,
     colors: ChipColors = ChipDefaults.secondaryChipColors()
 ) {
-        return CredentialsScreenChip(
-                    onClick,
-                    text = {
-                        WearButtonText(
-                            text = label,
-                            textAlign = textAlign,
-                            maxLines = 2
-                        )
-                    },
-                    secondaryLabel,
-                    icon,
-                    isAuthenticationEntryLocked,
-                    modifier,
-                    colors
-        )
-}
-
-
-
-/* Used as credential suggestion or user action chip. */
-@Composable
-fun CredentialsScreenChip(
-    onClick: () -> Unit,
-    text: @Composable () -> Unit,
-    secondaryLabel: String? = null,
-    icon: Drawable? = null,
-    isAuthenticationEntryLocked: Boolean? = null,
-    modifier: Modifier = Modifier,
-    colors: ChipColors = ChipDefaults.primaryChipColors(),
-    ) {
     val labelParam: (@Composable RowScope.() -> Unit) =
         {
             var horizontalArrangement = Arrangement.Start
@@ -94,19 +63,15 @@
                 horizontalArrangement = Arrangement.Center
             }
             Row(horizontalArrangement = horizontalArrangement, modifier = modifier.fillMaxWidth()) {
-                text()
+                primaryText()
             }
         }
 
     val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
-        secondaryLabel?.let {
+        secondaryText?.let {
             {
                 Row {
-                    WearSecondaryLabel(
-                        text = secondaryLabel,
-                        color = WearMaterialTheme.colors.onSurfaceVariant
-                    )
-
+                    secondaryText()
                     if (isAuthenticationEntryLocked != null) {
                         if (isAuthenticationEntryLocked) {
                             Icon(
@@ -156,9 +121,19 @@
 @Composable
 fun CredentialsScreenChipPreview() {
     CredentialsScreenChip(
-        label = "Elisa Beckett",
+        primaryText = {
+            WearButtonText(
+                text = "Elisa Beckett",
+                textAlign = TextAlign.Start,
+            )
+        },
         onClick = { },
-        secondaryLabel = "[email protected]",
+        secondaryText = {
+            WearSecondaryLabel(
+                text = "[email protected]",
+                color = WearMaterialTheme.colors.onSurfaceVariant
+            )
+        },
         icon = null,
     )
 }
@@ -166,8 +141,13 @@
 @Composable
 fun SignInOptionsChip(onClick: () -> Unit) {
     CredentialsScreenChip(
-        label = stringResource(R.string.dialog_sign_in_options_button),
-        textAlign = TextAlign.Start,
+        primaryText = {
+            WearButtonText(
+                text = stringResource(R.string.dialog_sign_in_options_button),
+                textAlign = TextAlign.Center,
+                maxLines = 2
+            )
+        },
         onClick = onClick,
     )
 }
@@ -182,7 +162,7 @@
 fun ContinueChip(onClick: () -> Unit) {
     CredentialsScreenChip(
         onClick = onClick,
-        text = {
+        primaryText = {
             WearButtonText(
                 text = stringResource(R.string.dialog_continue_button),
                 textAlign = TextAlign.Center,
@@ -202,14 +182,21 @@
 @Composable
 fun DismissChip(onClick: () -> Unit) {
     CredentialsScreenChip(
-        label = stringResource(R.string.dialog_dismiss_button),
+        primaryText = {
+            WearButtonText(
+                text = stringResource(R.string.dialog_dismiss_button),
+                textAlign = TextAlign.Center,
+                maxLines = 2
+            )
+        },
         onClick = onClick,
     )
 }
 @Composable
 fun LockedProviderChip(
     authenticationEntryInfo: AuthenticationEntryInfo,
-    onClick: () -> Unit
+    secondaryMaxLines: Int = 1,
+    onClick: () -> Unit,
 ) {
     val secondaryLabel = stringResource(
         if (authenticationEntryInfo.isUnlockedAndEmpty)
@@ -218,10 +205,21 @@
     )
 
     CredentialsScreenChip(
-        label = authenticationEntryInfo.title,
+        primaryText = {
+            WearButtonText(
+                text = authenticationEntryInfo.title,
+                textAlign = TextAlign.Start,
+                maxLines = 2,
+            )
+        },
         icon = authenticationEntryInfo.icon,
-        secondaryLabel = secondaryLabel,
-        textAlign = TextAlign.Start,
+        secondaryText = {
+            WearSecondaryLabel(
+                text = secondaryLabel,
+                color = WearMaterialTheme.colors.onSurfaceVariant,
+                maxLines = secondaryMaxLines
+                )
+        },
         isAuthenticationEntryLocked = !authenticationEntryInfo.isUnlockedAndEmpty,
         onClick = onClick,
     )
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
index a7b13ad..a1dc568 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/components/Texts.kt
@@ -16,7 +16,6 @@
 
 package com.android.credentialmanager.common.ui.components
 
-import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material3.Text
@@ -93,15 +92,16 @@
 fun WearSecondaryLabel(
     text: String,
     color: Color = WearMaterialTheme.colors.onSurface,
-    modifier: Modifier = Modifier
+    modifier: Modifier = Modifier,
+    maxLines: Int = 1,
 ) {
     Text(
-        modifier = modifier.fillMaxSize(),
+        modifier = modifier.wrapContentSize(),
         text = text,
         color = color,
         style = WearMaterialTheme.typography.caption1,
         overflow = TextOverflow.Ellipsis,
         textAlign = TextAlign.Start,
-        maxLines = 1,
+        maxLines = maxLines,
     )
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
index ef32c94..932b345 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFlattenScreen.kt
@@ -24,13 +24,13 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.Alignment
 import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry
 import com.android.credentialmanager.FlowEngine
 import com.android.credentialmanager.R
 import com.android.credentialmanager.common.ui.components.WearButtonText
+import com.android.credentialmanager.ui.components.LockedProviderChip
 import com.android.credentialmanager.common.ui.components.WearSecondaryLabel
 import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
@@ -38,6 +38,8 @@
 import com.google.android.horologist.compose.layout.ScalingLazyColumn
 import com.google.android.horologist.compose.layout.rememberColumnState
 import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
+import androidx.compose.ui.text.style.TextAlign
+import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
 
 /**
  * Screen that shows multiple credentials to select from, grouped by accounts
@@ -69,6 +71,7 @@
                     text = stringResource(R.string.sign_in_options_title),
                     textAlign = TextAlign.Center,
                     modifier = Modifier.weight(0.854f).fillMaxSize(),
+                    maxLines = 2,
                 )
                 Spacer(Modifier.weight(0.073f)) // 7.3% side margin
             }
@@ -94,19 +97,39 @@
             userNameEntries.sortedCredentialEntryList.forEach { credential: CredentialEntryInfo ->
                 item {
                     CredentialsScreenChip(
-                        label = credential.userName,
+                        primaryText = {
+                            WearButtonText(
+                                text = credential.userName,
+                                textAlign = TextAlign.Start,
+                                maxLines = 2,
+                            )
+                        },
                         onClick = { selectEntry(credential, false) },
-                        secondaryLabel =
-                        credential.credentialTypeDisplayName.ifEmpty {
-                            credential.providerDisplayName
+                        secondaryText =
+                        {
+                            WearSecondaryLabel(
+                                text = credential.credentialTypeDisplayName.ifEmpty {
+                                    credential.providerDisplayName
+                                },
+                                color = WearMaterialTheme.colors.onSurfaceVariant,
+                                maxLines = 2
+                            )
                         },
                         icon = credential.icon,
-                        textAlign = TextAlign.Start
                     )
 
                     CredentialsScreenChipSpacer()
                 }
             }
+
+            credentialSelectorUiState.authenticationEntryList.forEach { authenticationEntryInfo ->
+                item {
+                    LockedProviderChip(authenticationEntryInfo, secondaryMaxLines = 2) {
+                        selectEntry(authenticationEntryInfo, false)
+                    }
+                    CredentialsScreenChipSpacer()
+                }
+            }
         }
 
         if (credentialSelectorUiState.actionEntryList.isNotEmpty()) {
@@ -120,7 +143,8 @@
                             bottom = 4.dp,
                             start = 0.dp,
                             end = 0.dp
-                        ).fillMaxWidth(0.87f)
+                        ).fillMaxWidth(0.87f),
+                        maxLines = 2
                 )
                     Spacer(Modifier.weight(0.0624f)) // 6.24% side margin
                 }
@@ -128,9 +152,15 @@
             credentialSelectorUiState.actionEntryList.forEach { actionEntry ->
                 item {
                     CredentialsScreenChip(
-                        label = actionEntry.title,
+                        primaryText = {
+                            WearButtonText(
+                                text = actionEntry.title,
+                                textAlign = TextAlign.Start,
+                                maxLines = 2
+                            )
+                        },
                         onClick = { selectEntry(actionEntry, false) },
-                        secondaryLabel = null,
+                        secondaryText = null,
                         icon = actionEntry.icon,
                     )
                     CredentialsScreenChipSpacer()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
index 38307b0..b56b982b 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/multiple/MultiCredentialsFoldScreen.kt
@@ -17,7 +17,6 @@
 package com.android.credentialmanager.ui.screens.multiple
 
 import androidx.compose.foundation.layout.Row
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.foundation.layout.fillMaxSize
 import com.android.credentialmanager.R
 import androidx.compose.ui.res.stringResource
@@ -40,6 +39,10 @@
 import com.android.credentialmanager.model.CredentialType
 import com.android.credentialmanager.ui.components.BottomSpacer
 import com.android.credentialmanager.ui.components.CredentialsScreenChipSpacer
+import com.android.credentialmanager.common.ui.components.WearButtonText
+import com.android.credentialmanager.common.ui.components.WearSecondaryLabel
+import androidx.compose.ui.text.style.TextAlign
+import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
 
 /**
  * Screen that shows multiple credentials to select from.
@@ -82,14 +85,25 @@
             credentials.forEach { credential: CredentialEntryInfo ->
                 item {
                     CredentialsScreenChip(
-                        label = credential.userName,
+                        primaryText =
+                        {
+                            WearButtonText(
+                                text = credential.userName,
+                                textAlign = TextAlign.Start,
+                                maxLines = 2
+                            )
+                        },
                         onClick = { selectEntry(credential, false) },
-                        secondaryLabel =
-                        credential.credentialTypeDisplayName.ifEmpty {
-                            credential.providerDisplayName
+                        secondaryText = {
+                            WearSecondaryLabel(
+                                text = credential.credentialTypeDisplayName.ifEmpty {
+                                    credential.providerDisplayName
+                                },
+                                color = WearMaterialTheme.colors.onSurfaceVariant,
+                                maxLines = 1 // See b/359649621 for context
+                            )
                         },
                         icon = credential.icon,
-                        textAlign = TextAlign.Start
                     )
                     CredentialsScreenChipSpacer()
                 }
diff --git a/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
index ac47fbd..391b16d 100644
--- a/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
+++ b/packages/EasterEgg/src/com/android/egg/paint/PaintActivity.java
@@ -23,7 +23,6 @@
 
 import android.app.Activity;
 import android.content.res.Configuration;
-import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.os.Bundle;
 import android.view.MotionEvent;
@@ -38,9 +37,7 @@
 
 import com.android.egg.R;
 
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.stream.IntStream;
 
 public class PaintActivity extends Activity {
@@ -60,31 +57,28 @@
     private View.OnClickListener buttonHandler = new View.OnClickListener() {
         @Override
         public void onClick(View view) {
-            switch (view.getId()) {
-                case R.id.btnBrush:
-                    view.setSelected(true);
-                    hideToolbar(colors);
-                    toggleToolbar(brushes);
-                    break;
-                case R.id.btnColor:
-                    view.setSelected(true);
-                    hideToolbar(brushes);
-                    toggleToolbar(colors);
-                    break;
-                case R.id.btnClear:
-                    painting.clear();
-                    break;
-                case R.id.btnSample:
-                    sampling = true;
-                    view.setSelected(true);
-                    break;
-                case R.id.btnZen:
-                    painting.setZenMode(!painting.getZenMode());
-                    view.animate()
-                            .setStartDelay(200)
-                            .setInterpolator(new OvershootInterpolator())
-                            .rotation(painting.getZenMode() ? 0f : 90f);
-                    break;
+            // With non final fields in the R class we can't switch on the
+            // id since the case values are no longer constants.
+            int viewId = view.getId();
+            if (viewId == R.id.btnBrush) {
+                view.setSelected(true);
+                hideToolbar(colors);
+                toggleToolbar(brushes);
+            } else if (viewId == R.id.btnColor) {
+                view.setSelected(true);
+                hideToolbar(brushes);
+                toggleToolbar(colors);
+            } else if (viewId == R.id.btnClear) {
+                painting.clear();
+            } else if (viewId == R.id.btnSample) {
+                sampling = true;
+                view.setSelected(true);
+            } else if (viewId == R.id.btnZen) {
+                painting.setZenMode(!painting.getZenMode());
+                view.animate()
+                        .setStartDelay(200)
+                        .setInterpolator(new OvershootInterpolator())
+                        .rotation(painting.getZenMode() ? 0f : 90f);
             }
         }
     };
diff --git a/packages/ExternalStorageProvider/TEST_MAPPING b/packages/ExternalStorageProvider/TEST_MAPPING
new file mode 100644
index 0000000..dfa0c84
--- /dev/null
+++ b/packages/ExternalStorageProvider/TEST_MAPPING
@@ -0,0 +1,15 @@
+{
+  "postsubmit": [
+    {
+      "name": "ExternalStorageProviderTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/packages/ExternalStorageProvider/tests/Android.bp b/packages/ExternalStorageProvider/tests/Android.bp
index 86c62ef..097bb860 100644
--- a/packages/ExternalStorageProvider/tests/Android.bp
+++ b/packages/ExternalStorageProvider/tests/Android.bp
@@ -12,6 +12,10 @@
 
     manifest: "AndroidManifest.xml",
 
+    test_suites: [
+        "general-tests",
+    ],
+
     srcs: [
         "src/**/*.java",
     ],
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index b3fb1e7..717ec02 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -1,4 +1,39 @@
 {
+  "presubmit": [
+    {
+      "name": "CtsPackageInstallerCUJInstallationTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUninstallationTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    }
+  ],
   "postsubmit": [
     {
       "name": "CtsPackageInstallTestCases",
@@ -28,6 +63,50 @@
     },
     {
       "name": "CtsIntentSignatureTestCases"
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUninstallationTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+      "options":[
+        {
+          "exclude-annotation":"androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation":"org.junit.Ignore"
+        }
+      ]
     }
   ]
 }
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 13661e3..66303ed 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -111,7 +111,7 @@
     <string name="notification_installation_success_status" msgid="3172502643504323321">"“<xliff:g id="APPNAME">%1$s</xliff:g>” is suksesvol geïnstalleer"</string>
     <string name="unarchive_application_title" msgid="7958278328280721421">"Stel <xliff:g id="APPNAME">%1$s</xliff:g> terug vanaf <xliff:g id="INSTALLERNAME">%2$s</xliff:g>?"</string>
     <string name="unarchive_body_text" msgid="8244155079861708964">"Hierdie app sal in die agtergrond begin aflaai"</string>
-    <string name="restore" msgid="8460854736328970444">"Stel terug"</string>
+    <string name="restore" msgid="8460854736328970444">"Laai terug"</string>
     <string name="unarchive_error_offline_title" msgid="4021785324565678605">"Jy is vanlyn"</string>
     <string name="unarchive_error_offline_body" msgid="2256042209364094099">"Om hierdie app terug te stel, gaan jou internetverbinding na en probeer weer"</string>
     <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Iets het skeefgeloop"</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index a4c3097..ae421da 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -35,7 +35,7 @@
     <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Праграма не ўсталявана, таму што яна несумяшчальная з вашым планшэтам."</string>
     <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Гэта праграма несумяшчальная з вашым тэлевізарам."</string>
     <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Праграма не ўсталявана, таму што яна несумяшчальная з вашым тэлефонам."</string>
-    <string name="install_failed_invalid_apk" msgid="8581007676422623930">"Праграма не ўсталявана, таму што пакет, магчыма, з\'яўляецца несапраўдным."</string>
+    <string name="install_failed_invalid_apk" msgid="8581007676422623930">"Праграма не ўсталявана, таму што пакет, магчыма, з’яўляецца несапраўдным."</string>
     <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"На вашым планшэце не ўдалося ўсталяваць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"На вашым тэлевізары не ўдалося ўсталяваць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="install_failed_msg" product="default" msgid="6484461562647915707">"На вашым тэлефоне не ўдалося ўсталяваць праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
@@ -57,7 +57,7 @@
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"Не ўдалося выдаліць праграму."</string>
     <string name="uninstall_application_title" msgid="4045420072401428123">"Выдаліць праграму"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"Выдаліць абнаўленне"</string>
-    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> з\'яўляецца часткай наступнай праграмы:"</string>
+    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> з’яўляецца часткай наступнай праграмы:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"Выдаліць гэту праграму?"</string>
     <string name="archive_application_text" msgid="8482325710714386348">"Вашы асабістыя даныя будуць захаваны"</string>
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"Архіваваць гэту праграму для ўсіх карыстальнікаў? Вашы асабістыя даныя будуць захаваны."</string>
@@ -117,7 +117,7 @@
     <string name="unarchive_error_generic_title" msgid="7123457671482449992">"Адбылася памылка"</string>
     <string name="unarchive_error_generic_body" msgid="4486803312463813079">"Пры аднаўленні праграмы ўзнікла праблема"</string>
     <string name="unarchive_error_storage_title" msgid="5080723357273852630">"Не хапае месца ў сховішчы"</string>
-    <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Каб аднавіць гэту праграму, вызваліце месца на прыладзе. Неабходны аб\'ём месца ў сховішчы: <xliff:g id="BYTES">%1$s</xliff:g>."</string>
+    <string name="unarchive_error_storage_body" msgid="6879544407568780524">"Каб аднавіць гэту праграму, вызваліце месца на прыладзе. Неабходны аб’ём месца ў сховішчы: <xliff:g id="BYTES">%1$s</xliff:g>."</string>
     <string name="unarchive_action_required_title" msgid="4971245740162604619">"Патрабуецца дзеянне"</string>
     <string name="unarchive_action_required_body" msgid="1679431572983989231">"Каб аднавіць гэту праграму, выканайце далейшыя інструкцыі"</string>
     <string name="unarchive_error_installer_disabled_title" msgid="4815715617014985605">"Усталёўшчык \"<xliff:g id="INSTALLERNAME">%1$s</xliff:g>\" адключаны"</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index 739d272..adedc95 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -111,7 +111,7 @@
     <string name="notification_installation_success_status" msgid="3172502643504323321">"ติดตั้ง “<xliff:g id="APPNAME">%1$s</xliff:g>” สำเร็จแล้ว"</string>
     <string name="unarchive_application_title" msgid="7958278328280721421">"กู้คืน <xliff:g id="APPNAME">%1$s</xliff:g> จาก <xliff:g id="INSTALLERNAME">%2$s</xliff:g> ใช่ไหม"</string>
     <string name="unarchive_body_text" msgid="8244155079861708964">"แอปนี้จะเริ่มดาวน์โหลดในเบื้องหลัง"</string>
-    <string name="restore" msgid="8460854736328970444">"กู้คืน"</string>
+    <string name="restore" msgid="8460854736328970444">"คืนค่า"</string>
     <string name="unarchive_error_offline_title" msgid="4021785324565678605">"คุณออฟไลน์อยู่"</string>
     <string name="unarchive_error_offline_body" msgid="2256042209364094099">"หากต้องการกู้คืนแอปนี้ ให้ตรวจสอบการเชื่อมต่ออินเทอร์เน็ตแล้วลองอีกครั้ง"</string>
     <string name="unarchive_error_generic_title" msgid="7123457671482449992">"เกิดข้อผิดพลาด"</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
index d33433f..2fb32a7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveErrorFragment.java
@@ -16,10 +16,12 @@
 
 package com.android.packageinstaller;
 
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.BroadcastOptions;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.PendingIntent;
@@ -161,25 +163,31 @@
             return;
         }
 
+        // Allow the error handling actvities to start in the background.
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setPendingIntentBackgroundActivityStartMode(
+                MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
         switch (mStatus) {
             case PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED:
                 activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
-                        null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                        null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+                        options.toBundle());
                 break;
             case PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE:
                 if (mExtraIntent != null) {
                     activity.startIntentSender(mExtraIntent.getIntentSender(), /* fillInIntent= */
-                            null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0);
+                            null, /* flagsMask= */ 0, FLAG_ACTIVITY_NEW_TASK, /* extraFlags= */ 0,
+                            options.toBundle());
                 } else {
                     Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
-                    startActivity(intent);
+                    startActivity(intent, options.toBundle());
                 }
                 break;
             case PackageInstaller.UNARCHIVAL_ERROR_INSTALLER_DISABLED:
                 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                 Uri uri = Uri.fromParts("package", mInstallerPackageName, null);
                 intent.setData(uri);
-                startActivity(intent);
+                startActivity(intent, options.toBundle());
                 break;
             default:
                 // Do nothing. The rest of the dialogs are purely informational.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 9ad3e3c..170cb45 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -91,8 +91,7 @@
         // be stale, if e.g. the app was uninstalled while the activity was destroyed.
         super.onCreate(null);
 
-        // TODO(b/318521110) Enable PIA v2 for archive dialog.
-        if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) {
+        if (usePiaV2() && !isTv()) {
             Log.i(TAG, "Using Pia V2");
 
             boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
@@ -225,11 +224,6 @@
         showConfirmationDialog();
     }
 
-    private boolean isArchiveDialog(Intent intent) {
-        return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
-                & PackageManager.DELETE_ARCHIVE) != 0;
-    }
-
     /**
      * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
      * to archive an app if requested.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
index 186b69b..3b0faf0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt
@@ -736,7 +736,8 @@
             val appInfo = packageManager.getApplicationInfo(
                 pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES
             )
-            if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
+            // If the package is archived, treat it as an update case.
+            if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) {
                 return false
             }
         } catch (e: PackageManager.NameNotFoundException) {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 0091a3e8..96525f6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -32,6 +32,8 @@
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageInstaller
 import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.content.pm.PackageManager.PackageInfoFlags
 import android.content.pm.VersionedPackage
 import android.graphics.drawable.Icon
 import android.os.Build
@@ -51,6 +53,9 @@
 import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid
 import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted
 import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame
+import com.android.packageinstaller.v2.model.UninstallAborted.Companion.ABORT_REASON_UNINSTALL_DONE
+import android.content.pm.Flags as PmFlags
+import android.multiuser.Flags as MultiuserFlags
 
 class UninstallRepository(private val context: Context) {
 
@@ -71,6 +76,7 @@
     private var uninstallFromAllUsers = false
     private var isClonedApp = false
     private var uninstallId = 0
+    private var deleteFlags = 0
 
     fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage {
         this.intent = intent
@@ -155,7 +161,9 @@
         try {
             targetAppInfo = packageManager.getApplicationInfo(
                 targetPackageName!!,
-                PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong())
+                ApplicationInfoFlags.of(
+                    PackageManager.MATCH_ANY_USER.toLong() or PackageManager.MATCH_ARCHIVED_PACKAGES
+                )
             )
         } catch (e: PackageManager.NameNotFoundException) {
             Log.e(LOG_TAG, "Unable to get packageName")
@@ -180,9 +188,27 @@
             }
         }
 
+        parseDeleteFlags(intent)
+
         return UninstallReady()
     }
 
+    /**
+     * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent}
+     * to archive an app if requested.
+     *
+     * Do not parse other flags because developers might pass here any flags which might cause
+     * unintended behaviour.
+     * For more context {@link com.android.server.pm.PackageArchiver#requestArchive}.
+     */
+    private fun parseDeleteFlags(intent: Intent) {
+        val flags = intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0)
+        val archive = flags and PackageManager.DELETE_ARCHIVE
+        val keepData = flags and PackageManager.DELETE_KEEP_DATA
+
+        deleteFlags = archive or keepData
+    }
+
     fun generateUninstallDetails(): UninstallStage {
         val messageBuilder = StringBuilder()
 
@@ -201,6 +227,8 @@
         }
 
         val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
+        val isArchive =
+            PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0)
         val myUserHandle = Process.myUserHandle()
         val isSingleUser = isSingleUser()
 
@@ -215,34 +243,54 @@
                 )
             )
         } else if (uninstallFromAllUsers && !isSingleUser) {
-            messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users))
+            val messageString = if (isArchive) {
+                context.getString(R.string.archive_application_text_all_users)
+            } else {
+                context.getString(R.string.uninstall_application_text_all_users)
+            }
+            messageBuilder.append(messageString)
         } else if (uninstalledUser != myUserHandle) {
             // Uninstalling user is issuing uninstall for another user
             val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
                 .getSystemService(UserManager::class.java)
             val userName = customUserManager!!.userName
-            var messageString = context.getString(
-                    R.string.uninstall_application_text_user,
-                userName
-            )
+
+            var messageString = if (isArchive) {
+                context.getString(R.string.archive_application_text_user, userName)
+            } else {
+                context.getString(R.string.uninstall_application_text_user, userName)
+            }
+
             if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) {
                 if (customUserManager.isManagedProfile) {
-                    messageString = context.getString(
+                    messageString = if (isArchive) {
+                        context.getString(
+                            R.string.archive_application_text_current_user_work_profile, userName
+                        )
+                    } else {
+                        context.getString(
                             R.string.uninstall_application_text_current_user_work_profile, userName
-                    )
+                        )
+                    }
                 } else if (customUserManager.isCloneProfile){
                     isClonedApp = true
                     messageString = context.getString(
                             R.string.uninstall_application_text_current_user_clone_profile
                     )
                 } else if (Flags.allowPrivateProfile()
-                        && android.multiuser.Flags.enablePrivateSpaceFeatures()
+                        && MultiuserFlags.enablePrivateSpaceFeatures()
                         && customUserManager.isPrivateProfile
                 ) {
                     // TODO(b/324244123): Get these Strings from a User Property API.
-                    messageString = context.getString(
+                    messageString = if (isArchive) {
+                        context.getString(
+                            R.string.archive_application_text_current_user_private_profile, userName
+                        )
+                    } else {
+                        context.getString(
                             R.string.uninstall_application_text_current_user_private_profile
-                    )
+                        )
+                    }
                 }
             }
             messageBuilder.append(messageString)
@@ -262,6 +310,8 @@
                     targetAppLabel
                 )
             )
+        } else if (isArchive) {
+            messageBuilder.append(context.getString(R.string.archive_application_text))
         } else {
             messageBuilder.append(context.getString(R.string.uninstall_application_text))
         }
@@ -270,15 +320,21 @@
 
         val title = if (isClonedApp) {
             context.getString(R.string.cloned_app_label, targetAppLabel)
+        } else if (isArchive) {
+            context.getString(R.string.archiving_app_label, targetAppLabel)
         } else {
             targetAppLabel.toString()
         }
 
         var suggestToKeepAppData = false
         try {
-            val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0)
+            val pkgInfo = packageManager.getPackageInfo(
+                targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES)
+            )
             suggestToKeepAppData =
-                pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData()
+                pkgInfo.applicationInfo != null
+                    && pkgInfo.applicationInfo!!.hasFragileUserData()
+                    && !isArchive
         } catch (e: PackageManager.NameNotFoundException) {
             Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e)
         }
@@ -291,7 +347,7 @@
             )
         }
 
-        return UninstallUserActionRequired(title, message, appDataSize)
+        return UninstallUserActionRequired(title, message, appDataSize, isArchive)
     }
 
     /**
@@ -444,10 +500,11 @@
             callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message)
 
             // Since the caller already received the results, just finish the app at this point
-            uninstallResult.value = null
+            uninstallResult.value = UninstallAborted(ABORT_REASON_UNINSTALL_DONE)
             return
         }
         val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)
+
         if (returnResult || callingActivity != null) {
             val intent = Intent()
             intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus)
@@ -717,6 +774,7 @@
     ): Boolean {
         var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0
         flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0
+        flags = flags or deleteFlags
 
         return try {
             context.createContextAsUser(targetUser, 0)
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
index f086209..316e8b7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt
@@ -38,7 +38,8 @@
 data class UninstallUserActionRequired(
     val title: String? = null,
     val message: String? = null,
-    val appDataSize: Long = 0
+    val appDataSize: Long = 0,
+    val isArchive: Boolean = false
 ) : UninstallStage(STAGE_USER_ACTION_REQUIRED)
 
 data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) :
@@ -96,6 +97,11 @@
                 dialogTextResource = R.string.user_is_not_allowed_dlg_text
             }
 
+            ABORT_REASON_UNINSTALL_DONE -> {
+                dialogTitleResource = 0
+                dialogTextResource = 0
+            }
+
             else -> {
                 dialogTitleResource = 0
                 dialogTextResource = R.string.generic_error_dlg_text
@@ -107,6 +113,7 @@
         const val ABORT_REASON_GENERIC_ERROR = 0
         const val ABORT_REASON_APP_UNAVAILABLE = 1
         const val ABORT_REASON_USER_NOT_ALLOWED = 2
+        const val ABORT_REASON_UNINSTALL_DONE = 3
     }
 }
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
index e2ab316..c61a2ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/InstallLaunch.kt
@@ -16,7 +16,9 @@
 
 package com.android.packageinstaller.v2.ui
 
-import android.app.Activity
+import android.app.Activity.RESULT_CANCELED
+import android.app.Activity.RESULT_FIRST_USER
+import android.app.Activity.RESULT_OK
 import android.app.AppOpsManager
 import android.content.ActivityNotFoundException
 import android.content.Intent
@@ -135,7 +137,7 @@
                     }
 
                     InstallAborted.ABORT_REASON_POLICY -> showPolicyRestrictionDialog(aborted)
-                    else -> setResult(Activity.RESULT_CANCELED, null, true)
+                    else -> setResult(RESULT_CANCELED, null, true)
                 }
             }
 
@@ -169,7 +171,7 @@
                 val success = installStage as InstallSuccess
                 if (success.shouldReturnResult) {
                     val successIntent = success.resultIntent
-                    setResult(Activity.RESULT_OK, successIntent, true)
+                    setResult(RESULT_OK, successIntent, true)
                 } else {
                     val successDialog = InstallSuccessFragment(success)
                     showDialogInner(successDialog)
@@ -180,7 +182,7 @@
                 val failed = installStage as InstallFailed
                 if (failed.shouldReturnResult) {
                     val failureIntent = failed.resultIntent
-                    setResult(Activity.RESULT_FIRST_USER, failureIntent, true)
+                    setResult(RESULT_FIRST_USER, failureIntent, true)
                 } else {
                     val failureDialog = InstallFailedFragment(failed)
                     showDialogInner(failureDialog)
@@ -219,7 +221,7 @@
             shouldFinish = blockedByPolicyDialog == null
             showDialogInner(blockedByPolicyDialog)
         }
-        setResult(Activity.RESULT_CANCELED, null, shouldFinish)
+        setResult(RESULT_CANCELED, null, shouldFinish)
     }
 
     /**
@@ -257,6 +259,10 @@
 
     fun setResult(resultCode: Int, data: Intent?, shouldFinish: Boolean) {
         super.setResult(resultCode, data)
+        if (resultCode != RESULT_OK) {
+            // Let callers know that the install was cancelled
+            installViewModel!!.cleanupInstall()
+        }
         if (shouldFinish) {
             finish()
         }
@@ -282,7 +288,7 @@
         if (stageCode == InstallStage.STAGE_USER_ACTION_REQUIRED) {
             installViewModel!!.cleanupInstall()
         }
-        setResult(Activity.RESULT_CANCELED, null, true)
+        setResult(RESULT_CANCELED, null, true)
     }
 
     override fun onNegativeResponse(resultCode: Int, data: Intent?) {
@@ -318,7 +324,7 @@
         if (localLogv) {
             Log.d(LOG_TAG, "Opening $intent")
         }
-        setResult(Activity.RESULT_OK, intent, true)
+        setResult(RESULT_OK, intent, true)
         if (intent != null && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
             startActivity(intent)
         }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
index 87af1ae..524b4e6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java
@@ -60,7 +60,7 @@
         Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData);
         AlertDialog.Builder builder = new AlertDialog.Builder(requireContext())
             .setTitle(mDialogData.getTitle())
-            .setPositiveButton(R.string.ok,
+            .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok,
                 (dialogInt, which) -> mUninstallActionListener.onPositiveResponse(
                     mKeepData != null && mKeepData.isChecked()))
             .setNegativeButton(R.string.cancel,
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index 9021f7c..5b3f931 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -99,7 +99,7 @@
     <item msgid="79513688117503758">"Μικρή πλευρά"</item>
   </string-array>
   <string-array name="orientation_labels">
-    <item msgid="4061931020926489228">"Πορτραίτο"</item>
+    <item msgid="4061931020926489228">"Πορτρέτο"</item>
     <item msgid="3199660090246166812">"Οριζόντια"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Δεν ήταν δυνατή η εγγραφή στο αρχείο"</string>
diff --git a/packages/PrintSpooler/res/values-kn/strings.xml b/packages/PrintSpooler/res/values-kn/strings.xml
index 150ede4..27279a7 100644
--- a/packages/PrintSpooler/res/values-kn/strings.xml
+++ b/packages/PrintSpooler/res/values-kn/strings.xml
@@ -35,7 +35,7 @@
     <string name="install_for_print_preview" msgid="6366303997385509332">"ಪೂರ್ವವೀಕ್ಷಣೆಗಾಗಿ PDF ವೀಕ್ಷಕವನ್ನು ಸ್ಥಾಪಿಸಿ"</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="save_as_pdf" msgid="5718454119847596853">"PDF ರೂಪದಲ್ಲಿ ಸೇವ್ ಮಾಡಿ"</string>
     <string name="all_printers" msgid="5018829726861876202">"ಎಲ್ಲಾ ಪ್ರಿಂಟರ್‌ಗಳು…"</string>
     <string name="print_dialog" msgid="32628687461331979">"ಮುದ್ರಣ ಸಂವಾದ"</string>
     <string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 8a25726..0d4ef3c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -20,7 +20,7 @@
        xmlns:tools="http://schemas.android.com/tools"
        tools:targetApi="28"
        android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
     <corners
         android:topLeftRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="0dp"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 7e626e5..3072772 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -20,7 +20,7 @@
        xmlns:tools="http://schemas.android.com/tools"
        tools:targetApi="28"
        android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
     <corners
         android:topLeftRadius="0dp"
         android:topRightRadius="?android:attr/dialogCornerRadius"
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index 9f4980b..f1790f9 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -20,7 +20,7 @@
        xmlns:tools="http://schemas.android.com/tools"
        tools:targetApi="28"
        android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
     <corners
         android:radius="?android:attr/dialogCornerRadius"
     />
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
index 67b5107..f0da7b4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
@@ -18,7 +18,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" />
     <corners
         android:radius="0dp"
     />
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
index 2fe446d..155ee83 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp
@@ -20,6 +20,7 @@
     static_libs: [
         "androidx.preference_preference",
         "SettingsLibSettingsTheme",
+        "settingslib_selectorwithwidgetpreference_flags_lib",
     ],
 
     sdk_version: "system_current",
@@ -30,3 +31,25 @@
         "com.android.mediaprovider",
     ],
 }
+
+aconfig_declarations {
+    name: "settingslib_selectorwithwidgetpreference_flags",
+    package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags",
+    container: "system",
+    srcs: [
+        "aconfig/selectorwithwidgetpreference.aconfig",
+    ],
+}
+
+java_aconfig_library {
+    name: "settingslib_selectorwithwidgetpreference_flags_lib",
+    aconfig_declarations: "settingslib_selectorwithwidgetpreference_flags",
+
+    min_sdk_version: "30",
+    sdk_version: "system_current",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.permission",
+        "com.android.mediaprovider",
+    ],
+}
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig b/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
new file mode 100644
index 0000000..70cda47
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/aconfig/selectorwithwidgetpreference.aconfig
@@ -0,0 +1,13 @@
+package: "com.android.settingslib.widget.selectorwithwidgetpreference.flags"
+container: "system"
+
+flag {
+    name: "allow_set_title_max_lines"
+    namespace: "accessibility"
+    description: "Allow changes to the title max lines so it's not always fixed to 2"
+    bug: "356726764"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.xml
new file mode 100644
index 0000000..7ffde25
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/values/attrs.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.
+  -->
+
+<resources>
+    <declare-styleable name="SelectorWithWidgetPreference">
+        <!-- The maximum number of lines for rendering the title. -->
+        <attr name="titleMaxLines" format="integer" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
index f2ce8a9..34de5c4 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/src/com/android/settingslib/widget/SelectorWithWidgetPreference.java
@@ -17,15 +17,21 @@
 package com.android.settingslib.widget;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageView;
+import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.CheckBoxPreference;
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settingslib.widget.preference.selector.R;
+import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
 
 /**
  * Selector preference (checkbox or radio button) with an optional additional widget.
@@ -41,6 +47,8 @@
  * on the right side that can open another page.
  */
 public class SelectorWithWidgetPreference extends CheckBoxPreference {
+    @VisibleForTesting
+    static final int DEFAULT_MAX_LINES = 2;
 
     /**
      * Interface definition for a callback to be invoked when the preference is clicked.
@@ -63,6 +71,8 @@
     private boolean mIsCheckBox = false;  // whether to display this button as a checkbox
 
     private View.OnClickListener mExtraWidgetOnClickListener;
+    private int mTitleMaxLines;
+
 
     /**
      * Perform inflation from XML and apply a class-specific base style.
@@ -74,9 +84,10 @@
      *                 resource that supplies default values for the view. Can be 0 to not
      *                 look for defaults.
      */
-    public SelectorWithWidgetPreference(Context context, AttributeSet attrs, int defStyle) {
+    public SelectorWithWidgetPreference(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyle) {
         super(context, attrs, defStyle);
-        init();
+        init(context, attrs, defStyle, /* defStyleRes= */ 0);
     }
 
     /**
@@ -88,7 +99,7 @@
      */
     public SelectorWithWidgetPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init();
+        init(context, attrs, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
     }
 
     /**
@@ -100,7 +111,7 @@
     public SelectorWithWidgetPreference(Context context, boolean isCheckbox) {
         super(context, null);
         mIsCheckBox = isCheckbox;
-        init();
+        init(context, /* attrs= */ null, /* defStyleAttr= */ 0, /* defStyleRes= */ 0);
     }
 
     /**
@@ -161,6 +172,11 @@
         mExtraWidgetContainer = holder.findViewById(R.id.selector_extra_widget_container);
 
         setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
+
+        if (Flags.allowSetTitleMaxLines()) {
+            TextView title = (TextView) holder.findViewById(android.R.id.title);
+            title.setMaxLines(mTitleMaxLines);
+        }
     }
 
     /**
@@ -200,7 +216,8 @@
         return mIsCheckBox;
     }
 
-    private void init() {
+    private void init(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
         if (mIsCheckBox) {
             setWidgetLayoutResource(R.layout.preference_widget_checkbox);
         } else {
@@ -208,5 +225,16 @@
         }
         setLayoutResource(R.layout.preference_selector_with_widget);
         setIconSpaceReserved(false);
+
+        if (Flags.allowSetTitleMaxLines()) {
+            final TypedArray a =
+                    context.obtainStyledAttributes(
+                            attrs, R.styleable.SelectorWithWidgetPreference, defStyleAttr,
+                            defStyleRes);
+            mTitleMaxLines =
+                    a.getInt(R.styleable.SelectorWithWidgetPreference_titleMaxLines,
+                            DEFAULT_MAX_LINES);
+            a.recycle();
+        }
     }
 }
diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
index 4c75344..526ce14 100644
--- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
+++ b/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_preference.xml
@@ -19,8 +19,8 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:layout_marginStart="16dp"
-    android:layout_marginEnd="16dp">
+    android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+    android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd">
 
     <Spinner
         android:id="@+id/spinner"
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
index eba9c2c..9aa0bc3 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
@@ -15,16 +15,20 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp"
+        android:bottom="16dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_preference_bg_color" />
+                android:color="@color/settingslib_materialColorSurfaceBright" />
             <corners
                 android:radius="@dimen/settingslib_preference_corner_radius" />
+            <padding
+                android:bottom="16dp"/>
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
index 5c60f37..554cba5 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
@@ -15,19 +15,23 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp"
+        android:bottom="16dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_preference_bg_color" />
+                android:color="@color/settingslib_materialColorSurfaceBright" />
             <corners
                 android:topLeftRadius="4dp"
                 android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
                 android:topRightRadius="4dp"
                 android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
+            <padding
+                android:bottom="16dp"/>
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
index de64efd..f4766ee 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
@@ -15,19 +15,20 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp"
+        android:bottom="16dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+                android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:topLeftRadius="4dp"
-                android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
-                android:topRightRadius="4dp"
-                android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
+                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+            <padding
+                android:bottom="16dp"/>
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
index dd70f4f..b89a0dd 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
@@ -15,16 +15,17 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
         android:top="2dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_preference_bg_color" />
+                android:color="@color/settingslib_materialColorSurfaceBright" />
             <corners
                 android:radius="4dp" />
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
index fffc6c8..40eafc2 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
@@ -15,16 +15,17 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
         android:top="2dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+                android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="4dp" />
+                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
index f83e3b1..f4766ee 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
@@ -15,16 +15,20 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
-        android:top="2dp">
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
+        android:top="2dp"
+        android:bottom="16dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+                android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:radius="@dimen/settingslib_preference_corner_radius" />
+                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
+            <padding
+                android:bottom="16dp"/>
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
index ab79d18..7955e44 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
@@ -15,14 +15,15 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
         android:top="2dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_preference_bg_color" />
+                android:color="@color/settingslib_materialColorSurfaceBright" />
             <corners
                 android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
                 android:bottomLeftRadius="4dp"
@@ -30,4 +31,4 @@
                 android:bottomRightRadius="4dp" />
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
index 112ec73..40eafc2 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
@@ -15,19 +15,17 @@
   limitations under the License.
   -->
 
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
     <item
-        android:left="?android:attr/listPreferredItemPaddingStart"
-        android:right="?android:attr/listPreferredItemPaddingEnd"
+        android:start="?android:attr/listPreferredItemPaddingStart"
+        android:end="?android:attr/listPreferredItemPaddingEnd"
         android:top="2dp">
         <shape android:shape="rectangle">
             <solid
-                android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+                android:color="@color/settingslib_materialColorSurfaceContainer" />
             <corners
-                android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
-                android:bottomLeftRadius="4dp"
-                android:topRightRadius="@dimen/settingslib_preference_corner_radius"
-                android:bottomRightRadius="4dp" />
+                android:radius="@dimen/settingslib_preference_corner_radius_selected" />
         </shape>
     </item>
-</layer-list>
\ No newline at end of file
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
index eda7daa..7f466f6 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
@@ -18,6 +18,5 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:baselineAligned="false"
-    android:layout_marginTop="16dp">
+    android:baselineAligned="false">
 </LinearLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
index d783956..193ae61 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
@@ -17,4 +17,5 @@
 
 <resources>
     <dimen name="settingslib_preference_corner_radius">20dp</dimen>
+    <dimen name="settingslib_preference_corner_radius_selected">28dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index f36344a..3011ce0 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-beta05"
+    extra["jetpackComposeVersion"] = "1.7.0-rc01"
 }
 
 subprojects {
@@ -37,7 +37,7 @@
 
     plugins.withType<AndroidBasePlugin> {
         configure<BaseExtension> {
-            compileSdkVersion(34)
+            compileSdkVersion(35)
 
             defaultConfig {
                 minSdk = 21
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 1cca73a..d01c0b9 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
 #
 
 [versions]
-agp = "8.5.1"
+agp = "8.6.0"
 compose-compiler = "1.5.11"
 dexmaker-mockito = "2.28.3"
 jvm = "17"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
new file mode 100644
index 0000000..50432f3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.10-bin.zip
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip b/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
deleted file mode 100644
index 9a97e46..0000000
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-8.9-bin.zip
+++ /dev/null
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index 2c35211..a4b76b9 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 9f29c77..9a7f4b6 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,6 @@
 
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=gradle-8.9-bin.zip
+distributionUrl=gradle-8.10-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index ce3d96e..f0c2ea6 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -54,13 +54,13 @@
 dependencies {
     api(project(":SettingsLibColor"))
     api("androidx.appcompat:appcompat:1.7.0")
-    api("androidx.compose.material3:material3:1.3.0-beta04")
+    api("androidx.compose.material3:material3:1.3.0-rc01")
     api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
     api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
     api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
     api("androidx.lifecycle:lifecycle-livedata-ktx")
     api("androidx.lifecycle:lifecycle-runtime-compose")
-    api("androidx.navigation:navigation-compose:2.8.0-beta05")
+    api("androidx.navigation:navigation-compose:2.8.0-rc01")
     api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
     api("com.google.android.material:material:1.11.0")
     debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
index d9f82e8..15def72 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTheme.kt
@@ -40,3 +40,5 @@
         }
     }
 }
+
+const val isSpaExpressiveEnabled = false
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
index aceb545..62af08e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -117,7 +117,7 @@
     val indication = LocalIndication.current
     val onChangeWithLog = wrapOnSwitchWithLog(onCheckedChange)
     val interactionSource = remember { MutableInteractionSource() }
-    val modifier = remember(checked, changeable) {
+    val modifier =
         if (checked != null && onChangeWithLog != null) {
             Modifier.toggleable(
                 value = checked,
@@ -128,7 +128,6 @@
                 onValueChange = onChangeWithLog,
             )
         } else Modifier
-    }
     BasePreference(
         title = title,
         summary = summary,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
index 2fac576..8619f31 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt
@@ -17,11 +17,18 @@
 package com.android.settingslib.spa.widget.ui
 
 import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material3.Icon
 import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import com.android.settingslib.spa.framework.compose.contentDescription
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
 import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog
 
 @Composable
@@ -39,13 +46,42 @@
             modifier = Modifier.contentDescription(contentDescription),
             enabled = changeable(),
             interactionSource = interactionSource,
-        )
+            thumbContent =
+                if (isSpaExpressiveEnabled) {
+                    if (checked) {
+                        {
+                            Icon(
+                                imageVector = Icons.Filled.Check,
+                                contentDescription = null,
+                                modifier = Modifier.size(SwitchDefaults.IconSize),
+                            )
+                        }
+                    } else {
+                        {
+                            Icon(
+                                imageVector = Icons.Filled.Close,
+                                contentDescription = null,
+                                modifier = Modifier.size(SwitchDefaults.IconSize),
+                            )
+                        }
+                    }
+                } else null)
     } else {
         Switch(
             checked = false,
             onCheckedChange = null,
             enabled = false,
             interactionSource = interactionSource,
+            thumbContent =
+            if (isSpaExpressiveEnabled) {
+                {
+                    Icon(
+                        imageVector = Icons.Filled.Close,
+                        contentDescription = null,
+                        modifier = Modifier.size(SwitchDefaults.IconSize),
+                    )
+                }
+            } else null
         )
     }
 }
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 0d124e8..34b597b 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -119,3 +119,23 @@
     purpose: PURPOSE_BUGFIX
   }
 }
+
+flag {
+    name: "audio_sharing_hysteresis_mode_fix"
+    namespace: "cross_device_experiences"
+    description: "Gates whether to enable fix for hysteresis mode"
+    bug: "355222285"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "member_device_lea_active_state_sync_fix"
+    namespace: "cross_device_experiences"
+    description: "Gates whether to enable fix for member device active state sync on lea profile"
+    bug: "364201289"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml
new file mode 100644
index 0000000..35517ea
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorAccentPrimary" />
+    <corners android:radius="12dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml
new file mode 100644
index 0000000..18696c6
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/audio_sharing_rounded_bg_ripple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:drawable="@drawable/audio_sharing_rounded_bg"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index e612229..9c75556 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -37,7 +37,7 @@
     <string name="wifi_security_wep" msgid="1413627788581122366">"WEP"</string>
     <string name="wifi_security_wpa" msgid="1072450904799930636">"WPA-persoonlik"</string>
     <string name="wifi_security_wpa2" msgid="4038267581230425543">"WPA2-persoonlik"</string>
-    <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-Personal"</string>
+    <string name="wifi_security_wpa_wpa2" msgid="946853615482465986">"WPA/WPA2-persoonlik"</string>
     <string name="wifi_security_eap" msgid="6179633834446852269">"WPA/WPA2/WPA3-onderneming"</string>
     <string name="wifi_security_eap_wpa" msgid="6189023812330549957">"WPA-onderneming"</string>
     <string name="wifi_security_eap_wpa_wpa2" msgid="1089879674896108216">"WPA/WPA2-onderneming"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Wys visuele terugvoer vir tikke"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Wys sleuteldrukke"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Wys visuele terugvoer vir fisieke sleuteldrukke"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Wys raakpaneelinvoer"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Skermoorlegger wat raakpaneelinvoerdata en herkende gebare vertoon"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Wys oppervlakopdaterings"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flits totale vensteroppervlakke wanneer dit opdateer"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Wys aansigopdaterings"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> oor tot vol"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laaiproses is onderbreek om battery te beskerm"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Laai tans"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Volgelaai teen <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Volgelaai teen <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en onthounotas"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Laat hierdie app toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die app op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie app geskeduleer is, nie werk nie."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"skedule, wekker, onthounota, horlosie"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Moenie Steur Nie"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Skakel aan"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Skakel Moenie steur nie aan"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 7556de6..093fbbb 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ለነካ ማድረጎች ምስላዊ ግብረመልስን አሳይ"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"የቁልፍ ጭነቶችን አሳይ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ለአካላዊ የቁልፍ ጭነቶች የሚታይ ግብረመልስን አሳይ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"የመዳሰሻ ሰሌዳ ግብዓት አሳይ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"የማያ ገፅ ተደራቢ የመዳሰሻ ሰሌዳ ግብዓት ውሂብ እና የታወቁ ምልክቶችን በማሳየት ላይ"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"የወለል ዝማኔዎችን አሳይ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"የመስኮት ወለሎች ሲዘምኑ መላ መስኮቱን አብለጭልጭ"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"የእይታ ዝማኔዎችን አሳይ"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g> ይቀራል"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስኪሞላ ድረስ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪን ለመጠበቅ ኃይል መሙላት በይቆይ ላይ"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ኃይል በመሙላት ላይ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - እስከ <xliff:g id="TIME">%3$s</xliff:g> ድረስ ይሞላል"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - እስከ <xliff:g id="TIME">%2$s</xliff:g> ሙሉ ለሙሉ ይሞላል"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ማንቂያዎች እና አስታዋሾች"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ይህ መተግበሪያ ማንቂያዎችን እንዲያቀናብር እና የጊዜ ትብነት ያላቸው እርምጃዎችን መርሐግብር እንዲያስይዝ ይፍቀዱለት። ይህ መተግበሪያው ከበስተጀርባ ማሄድ እንዲችል ያስችለዋል፣ ይህም የበለጠ ባትሪ ሊጠቀም ይችላል።\n\nይህ ፈቃድ ከጠፋ በዚህ መተግበሪያ መርሐግብር የተያዘላቸው ነባር ማንቂያዎች እና ጊዜ-ተኮር ክስተቶች አይሰሩም።"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"የጊዜ መርሐግብር፣ ማንቂያ፣ አስታዋሽ ሰዓት"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"አይረብሹ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"አብራ"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"አትረብሽን አብራ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 99abb2e..a1f4109 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -100,7 +100,7 @@
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"‏البلوتوث نشِط. مستوى الشحن في سماعة الرأس اليمنى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"‏مستوى شحن البطارية: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"‏مستوى الشحن في سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، مستوى الشحن في سماعة الرأس اليمنى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"‏مستوى الشحن في السمّاعة اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، السمّاعة اليمنى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"‏مستوى الشحن في سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"‏مستوى الشحن في سماعة الرأس اليمنى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"‏مستوى شحن البطارية في سماعة الرأس اليسرى: ‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"عرض التعليقات المرئية للنقرات"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"عرض الضغطات على المفاتيح"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"عرض الملاحظات المرئية للضغطات الفعلية على المفاتيح"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"عرض البيانات التي يتم إدخالها بلوحة اللمس"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"يعرض هذا الإعداد على الشاشة البيانات التي يتم إدخالها والإيماءات التي يتم استخدامها بواسطة لوحة اللمس."</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"عرض تحديثات السطح"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"وميض أسطح النوافذ بالكامل عندما يتم تحديثها"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"إظهار تحديثات العرض"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"يتبقّى <xliff:g id="TIME">%1$s</xliff:g> حتى اكتمال شحن البطارية"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقّى <xliff:g id="TIME">%2$s</xliff:g> حتى اكتمال شحن البطارية."</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"‫<xliff:g id="LEVEL">%1$s</xliff:g>: الشحن معلَّق لحماية البطارية"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g>: جارٍ الشحن"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‏‫‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - سيكتمل الشحن بحلول <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‏‫‎<xliff:g id="LEVEL">%1$s</xliff:g> - سيكتمل الشحن بحلول <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات المضبوطة والأحداث المستندة إلى الوقت المجدولة حاليًا في هذا التطبيق."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"وضع \"عدم الإزعاج\""</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 38600f4..019fb86 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"টিপিলে দৃশ্যায়িত ফীডবেক দিয়ক"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"কী টিপা দেখুৱাওক"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ভৌতিক কী টিপাৰ ভিজুৱেল প্ৰতিক্ৰিয়া দেখুৱাওক"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"টাচ্চপেডৰ ইনপুট দেখুৱাওক"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"টাচ্চপেডৰ ইনপুট ডেটা প্ৰদৰ্শন কৰা স্ক্ৰীনৰ অ’ভাৰলে’ আৰু চিনাক্ত কৰা নিৰ্দেশনা"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"পৃষ্ঠভাগৰ আপডে’ট দেখুৱাওক"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"আপডে’ট হওতে গোটেই ৱিণ্ড পৃষ্ঠসমূহ ফ্লাশ্ব কৰক"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"ভিউৰ আপডে’ট দেখুৱাওক"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"সম্পূৰ্ণ হ’বলৈ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সুৰক্ষিত কৰিবলৈ চাৰ্জিং স্থগিত ৰখা হৈছে"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ চাৰ্জ হৈ আছে"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>ৰ ভিতৰত সম্পূৰ্ণ হ’ব"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>ৰ ভিতৰত সম্পূৰ্ণৰূপে চাৰ্জ হ’ব"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"এই এপ্‌টোক এলাৰ্ম ছেট কৰিবলৈ আৰু সময় সংবেদনশীল কাৰ্যৰ সময়সূচী নিৰ্ধাৰণ কৰিবলৈ দিয়ক। ই এপ্‌টোক নেপথ্যত চলি থকাৰ অনুমতি দিয়ে যাৰ ফলত অধিক বেটাৰী ব্যৱহাৰ হয়।\n\nএই অনুমতিটো অফ কৰা থাকিলে, ইতিমধ্যে ছেট কৰা এলাৰ্ম আৰু এই এপ্‌টোৱে সময়সূচী নিৰ্ধাৰণ কৰা সময় ভিত্তিক অনুষ্ঠানসমূহে কাম নকৰা হ’ব।"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"সময়সূচী, এলাৰ্ম, ৰিমাইণ্ডাৰ, ঘড়ী"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"অসুবিধা নিদিব"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"অন কৰক"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"অসুবিধা নিদিব অন কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d41cf0e..7f17e79 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Toxunuşa vizual reaksiya verilsin"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Düyməyə basma göstərilsin"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Fiziki düymə basılmaları üçün vizual rəy göstərin"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Taçped daxiletməsini göstərin"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Taçped daxiletmə datasını və tanınan jestləri göstərən ekran örtüyü"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Səth yenilənməsi göstərilsin"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Pəncərə səthi təzələnəndə işıqlansın"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Baxış yenilənməsi göstərilsin"</string>
@@ -430,7 +432,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Manifest dəyərindən asılı olmayaraq tətbiqlərin xarici daşıyıcılarda saxlanmasına icazə verilsin"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Çoxpəncərəli rejimdə ölçü dəyişdirilməsi"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Manifest dəyərindən asılı olmayaraq çoxpəncərəli rejimdə pəncərə ölçüsünün dəyişdirilməsinə icazə verilsin"</string>
-    <string name="enable_freeform_support" msgid="7599125687603914253">"İxtiyari formada pəncərə yaradılsın"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Sərbəst ölçülü pəncərələri aktiv edin"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Masaüstü rezerv parolu"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Masaüstü tam rezervlər hazırda qorunmayıblar."</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Masaüstünün tam rezerv kopyalanması üçün parolu dəyişmək və ya silmək üçün basın"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tam şarj edilənədək <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj edilənədək <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyanı qorumaq üçün şarj gözlədilir"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj edilir"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> radələrinə qədər tam dolacaq"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> radələrinə qədər tam dolacaq"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Siqnallar və xatırlatmalar"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu tətbiqə siqnallar ayarlamağa və vaxta əsaslanan əməliyyatları planlaşdırmağa icazə verin. Bu, tətbiqin arxa fonda işləməsinə imkan verir ki, nəticədə daha çox enerji istifadə edilə bilər.\n\nBu icazə deaktiv olsa, bu tətbiq tərəfindən planlaşdırılan mövcud siqnallar və vaxta əsaslanan tədbirlər işləməyəcəkdir."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"cədvəl, siqnal, xatırlatma, saat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Narahat etməyin"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktiv edin"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Narahat Etməyin\" rejimini aktiv edin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index c576af5..25bf587 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Prikazuje vizuelne povratne informacije za dodire"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Prikazuj pritiske tastera"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Prikazuje povratne informacije za pritiske tastera"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Prikaži unos na tačpedu"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Element koji prekriva sadržaj ekrana prikazuje podatke iz unosa na tačpedu i prepoznaje pokrete"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja površine"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Osvetljava sve površine prozora kada se ažuriraju"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju da bi se zaštitila baterija"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsetnici"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite ovoj aplikaciji da podešava alarme i zakazuje vremenski osetljive radnje. To omogućava da aplikacija bude pokrenuta u pozadini, što može da troši više baterije.\n\nAko je ova dozvola isključena, postojeći alarmi i događaji zasnovani na vremenu zakazani pomoću ove aplikacije neće raditi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"zakazati, alarm, podsetnik, sat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite režim Ne uznemiravaj"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 9fd2af3..3a61980 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -164,7 +164,7 @@
     <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Не спалучана з прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g> з-за няправільнага PIN-кода або ключа доступу."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"Не магу размаўляць з прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Злучэнне адхілена прыладай <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
-    <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Камп\'ютар"</string>
+    <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Камп’ютар"</string>
     <string name="bluetooth_talkback_headset" msgid="3406852564400882682">"Гарнітура"</string>
     <string name="bluetooth_talkback_phone" msgid="868393783858123880">"Тэлефон"</string>
     <string name="bluetooth_talkback_imaging" msgid="8781682986822514331">"Прылада апрацоўкі відарысаў"</string>
@@ -343,10 +343,10 @@
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Перадача даных мабільнай сувязі заўсёды актыўная, нават калі актыўная сетка Wi‑Fi (для хуткага пераключэння паміж сеткамі)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Выкарыстоўваць апаратнае паскарэнне ў рэжыме мадэма пры наяўнасці"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"Дазволіць адладку па USB?"</string>
-    <string name="adb_warning_message" msgid="8145270656419669221">"Адладка па USB прызначана толькі для мэт распрацоўкі. Яна можа выкарыстоўвацца, каб капіраваць даныя паміж камп\'ютарам і прыладай, усталёўваць праграмы на прыладзе без папярэдняга апавяшчэння і чытаць даныя журнала."</string>
+    <string name="adb_warning_message" msgid="8145270656419669221">"Адладка па USB прызначана толькі для мэт распрацоўкі. Яна можа выкарыстоўвацца, каб капіраваць даныя паміж камп’ютарам і прыладай, усталёўваць праграмы на прыладзе без папярэдняга апавяшчэння і чытаць даныя журнала."</string>
     <string name="adbwifi_warning_title" msgid="727104571653031865">"Дазволіць адладку па Wi-Fi?"</string>
-    <string name="adbwifi_warning_message" msgid="8005936574322702388">"Адладка па Wi-Fi прызначана толькі для мэт распрацоўкі. Яна можа выкарыстоўвацца, каб капіраваць даныя паміж камп\'ютарам і прыладай, усталёўваць праграмы на прыладзе без апавяшчэння і чытаць даныя журнала."</string>
-    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Скасаваць доступ да адладкі па USB з усіх камп\'ютараў, на якiх вы уваходзiлi ў сiстэму?"</string>
+    <string name="adbwifi_warning_message" msgid="8005936574322702388">"Адладка па Wi-Fi прызначана толькі для мэт распрацоўкі. Яна можа выкарыстоўвацца, каб капіраваць даныя паміж камп’ютарам і прыладай, усталёўваць праграмы на прыладзе без апавяшчэння і чытаць даныя журнала."</string>
+    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Скасаваць доступ да адладкі па USB з усіх камп’ютараў, на якiх вы уваходзiлi ў сiстэму?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Дазволiць налады распрацоўшчыка?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Гэтыя налады прызначаны толькi для распрацоўшыкаў. Яны могуць выклікаць збоi прылад i ўсталяваных на iх прыкладанняў, а таксама перашкаджаць iх працы."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Праверце праграмы па USB"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Паказваць візуалізацыю дакрананняў"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Паказваць націсканні"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Паказваць візуальны водгук пры націсканні клавіш"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Паказваць увод з сэнсарнай панэлі"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Экранная накладка, якая паказвае ўвод даных з сэнсарнай панэлі і распазнаныя жэсты"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Абнаўленне паверхні"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Падсвяціць паверхню акна пры абнаўленні"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Паказаць абнаўленні"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – да поўнай зарадкі засталося: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарадка прыпынена, каб абараніць акумулятар"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зараджаецца"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Прылада зарадзіцца поўнасцю да <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Прылада зарадзіцца поўнасцю да <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будзільнікі і напаміны"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дазвольце гэтай праграме ўключаць будзільнікі і задаваць час дзеянняў. З такім дазволам праграма можа працаваць у фонавым рэжыме і ў выніку хутчэй разраджаць акумулятар.\n\nКалі вы не ўключыце гэты дазвол, існуючыя будзільнікі і запланаваны праграмай час падзей не будуць працаваць."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"расклад, будзільнік, напамін, гадзіннік"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбаваць"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Уключыць"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Уключэнне рэжыму \"Не турбаваць\""</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 462535b..5f0def5 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Показване на визуална обр. връзка за докосванията"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Видими натиск. на клавиши"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Визуална обратна връзка при натискане на клавиши"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Показване на взаимодействията със сензорния панел"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Екранно наслагване, което показва данните за взаимодействията със сензорния панел и разпознатите жестове"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Актуализации на повърхн."</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Примигв. на целите повърхности на прозорците при актуализирането им"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Актуализации на изгледите"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оставащо време до пълно зареждане: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зареждането е поставено на пауза с цел запазване на батерията"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарежда се"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – ще се зареди напълно до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – ще се зареди напълно до <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будилници и напомняния"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Разрешаване на това приложение да задава будилници и да насрочва действия, ограничени във времето. Това му позволява да работи на заден план, при което може да се използва повече батерия.\n\nАко разрешението е изключено, съществуващите будилници и събитията въз основа на времето, насрочени от приложението, няма да работят."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, будилник, напомняне, часовник"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не безпокойте"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включване"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включване на режима „Не безпокойте“"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index cd53b67..0c0b569 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"আলতো চাপ দিলে ভিজ্যুয়াল প্রতিক্রিয়া দেখান"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"প্রেস করা কী দেখুন"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ফিজিক্যাল কী প্রেস করা হলে ভিজুয়াল ফিডব্যাক দেখুন"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"টাচপ্যাড ইনপুট দেখুন"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"স্ক্রিন ওভারলে, টাচপ্যাড ইনপুট ডেটা ও পরিচিত জেসচার দেখাচ্ছে"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"সারফেস আপডেটগুলি দেখান"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"সম্পূর্ণ উইন্ডোর সারফেস আপডেট হয়ে গেলে সেটিকে ফ্ল্যাশ করুন"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"ভিউয়ের আপডেট দেখুন"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ ব্যাটারি পুরো চার্জ হয়ে যাবে"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারিকে সুরক্ষিত রাখতে চার্জিং হোল্ড করা হয়েছে"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - চার্জ করা হচ্ছে"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>-এর মধ্যে পুরো চার্জ হয়ে যাবে"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এর মধ্যে পুরো চার্জ হয়ে যাবে"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"অ্যালার্ম এবং সময়ের মধ্যে শেষ করতে হবে এমন অ্যাকশনের শিডিউল সেট করতে এই অ্যাপকে অনুমতি দিন। এর ফলে ব্যাকগ্রাউন্ডে অ্যাপ চলতে পারে, যার জন্য আরও ব্যাটারির চার্জ খরচ হতে পারে।\n\nএই অনুমতি বন্ধ করা থাকলে, আগে থেকে থাকা অ্যালার্ম এবং অ্যাপের মাধ্যমে শিডিউল করা সময় ভিত্তিক ইভেন্টের রিমাইন্ডার কাজ করবে না।"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"বিরক্ত করবে না"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 5e35f1a..ea4150f 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Prikaz vizuelnih povratnih informacija za dodire"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Prikaži pritiskanja tipki"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Prikaži vizuelne povr. inform. za pritiske tipki"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Prikazuj unos putem dodirne podloge"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Na preklapanju ekrana se prikazuju podaci uneseni putem dodirne podloge i prepoznati pokreti"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja za površinu"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Osvjetljava sve površine prozora kada se ažuriraju"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je na čekanju radi zaštite baterije"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Napunit će se do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu, a koje je ova aplikacija zakazala, neće funkcionirati."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne ometaj"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključi način rada Ne ometaj"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 18e8720..adfa6f2 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -100,7 +100,7 @@
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Actiu. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Esquerre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Dret: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Esquerre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostra la ubicació visual dels tocs"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostra les tecles premudes"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra avisos visuals en prémer tecles físiques"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostra l\'entrada del ratolí tàctil"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Superposició de pantalla que mostra les dades d\'entrada del ratolí tàctil i els gestos reconeguts"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Canvis de superfície"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Il·lumina superfícies de finestres en actualitzar-se"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Actualitzacions de visualitzacions"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>: la càrrega s\'ha posat en espera per protegir la bateria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està carregant"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Càrrega completa a les <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Càrrega completa a les <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes i recordatoris"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions a una hora determinada. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programació, alarma, recordatori, rellotge"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestis"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activa"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activa el mode No molestis"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7ca98f76..96d04a8 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Zobrazovat vizuální zpětnou vazbu pro klepnutí"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Zobrazit stisknutí klávesy"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Zobrazit vizuální odezvu při stisknutí fyzické klávesy"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Zobrazit vstup touchpadu"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Překryvná vrstva obrazovky zobrazující vstupní data touchpadu a rozpoznaná gesta"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Zobrazit obnovení obsahu"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Rozbliká obsah okna při aktualizaci"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ukazovat aktualizace zobrazení"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabití"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení je kvůli ochraně baterie pozastaveno"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Nabíjení"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Úplné nabití: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Úplné nabití: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a připomenutí"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Když tuto možnost povolíte, aplikace bude moci nastavovat budíky a plánovat akce závislé na čase. Aplikace poběží na pozadí, což může vést k vyšší spotřebě baterie.\n\nPokud toto oprávnění zůstane vypnuté, stávající budíky a události závislé na čase naplánované touto aplikací nebudou fungovat."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, připomenutí, hodiny"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nerušit"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnout"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapněte funkci Nerušit"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 5564722..c6a5a43 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Vis visuel feedback ved tryk"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Vis tastetryk"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Vis visuel feedback ved fysiske tastetryk"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Vis touchpladeinput"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Skærmoverlejring, der viser data om touchpladeinput og genkendte bevægelser"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Vis overfladeopdateringer"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fremhæv hele vinduesoverflader, når de opdateres"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Se visningsopdateringer"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fuldt opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – fuldt opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Opladningen er sat på pause for at beskytte batteriet"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – oplades"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Opladet senest kl. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fuldt opladet senest kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påmindelser"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillad, at denne app indstiller alarmer og planlægger tidsbestemte handlinger. Appen vil køre i baggrunden, hvor den muligvis bruger mere batteri.\n\nHvis denne tilladelse er deaktiveret, vil eksisterende alarmer og tidsbestemte handlinger, der er planlagt af denne app, ikke fungere."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planlæg, alarm, påmindelse, ur"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Forstyr ikke"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivér"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivér Forstyr ikke"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2fd683d..88db823 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Bei Fingertippen visuelles Feedback anzeigen"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Tastatureingaben anzeigen"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Visuelles Feedback für Tastatureingaben anzeigen"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Touchpadeingabe anzeigen"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Display-Overlay, das Touchpad-Eingabedaten und erkannte Touch-Gesten anzeigt"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Oberflächenaktualisierungen hervorheben"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Gesamte Fensteroberflächen blinken bei Aktualisierung"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Aktualisierungen von Ansichten hervorheben"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Voll in <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladevorgang zum Schutz des Akkus angehalten"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Wird geladen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Vollständig geladen in <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Vollständig geladen in <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dieser App erlauben, Wecker zu stellen und zeitgebundene Aktionen zu planen. Dadurch läuft die App im Hintergrund. Dies kann den Akkuverbrauch erhöhen. \n\nWenn diese Berechtigung deaktiviert ist, funktionieren bereits gestellte Wecker und zeitgebundene Ereignisse, die von dieser App geplant sind, nicht wie erwartet."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Bitte nicht stören"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 4fdea21..35734a9 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Εμφάνιση οπτικών σχολίων για πατήματα"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Εμφάν. πατημάτων πλήκτρων"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Εμφ. οπτικής ανάδρασης για πατήμ. φυσικών πλήκτρων"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Εμφάνιση εισόδου επιφάνειας αφής"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Επικάλυψη οθόνης που εμφανίζει δεδομένα εισόδου επιφάνειας αφής και κινήσεις που αναγνωρίζονται"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Εμφάνιση ενημερώσεων επιφάνειας"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Προβολή Flash ολόκλ. των επιφ παραθ. όταν ενημερ."</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Εμφάνιση ενημερ. προβολής"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για πλήρη φόρτιση"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένουν <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Η φόρτιση τέθηκε σε αναμονή για προστασία της μπαταρίας"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Φόρτιση"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Πλήρης φόρτιση στις <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Πλήρης φόρτιση στις <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτή την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτή την εφαρμογή δεν θα λειτουργούν."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"πρόγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Μην ενοχλείτε"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index bcffe2c..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Show touchpad input"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Screen overlay displaying touchpad input data and recognised gestures"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging on hold to protect battery"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index df54ac11..be5dfaa 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Show touchpad input"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Screen overlay displaying touchpad input data and recognized gestures"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging on hold to protect battery"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,7 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms &amp; reminders"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"Do Not Disturb"</string>
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index bcffe2c..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Show touchpad input"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Screen overlay displaying touchpad input data and recognised gestures"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging on hold to protect battery"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index bcffe2c..2d03437 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Show touchpad input"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Screen overlay displaying touchpad input data and recognised gestures"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> left until full"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> left until full"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging on hold to protect battery"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – charging"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Full by <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fully charged by <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarms and reminders"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.\n\nIf this permission is off, existing alarms and time-based events scheduled by this app won’t work."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schedule, alarm, reminder, clock"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Do Not Disturb"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Turn on"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Turn on Do Not Disturb"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index fc4c8a0..92af284 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎Show visual feedback for taps‎‏‎‎‏‎"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎Show key presses‎‏‎‎‏‎"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‎‎Show visual feedback for physical key presses‎‏‎‎‏‎"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎Show touchpad input‎‏‎‎‏‎"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‎Screen overlay displaying touchpad input data and recognized gestures‎‏‎‎‏‎"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎Show surface updates‎‏‎‎‏‎"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎Flash entire window surfaces when they update‎‏‎‎‏‎"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‎Show view updates‎‏‎‎‏‎"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‏‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="TIME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎ left until full‎‏‎‎‏‎"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging on hold to protect battery‎‏‎‎‏‎"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Charging‎‏‎‎‏‎"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - ‎‏‎‎‏‏‎<xliff:g id="STATUS">%2$s</xliff:g>‎‏‎‎‏‏‏‎ - Full by ‎‏‎‎‏‏‎<xliff:g id="TIME">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎‏‎‎‏‏‏‎ - Fully charged by ‎‏‎‎‏‏‎<xliff:g id="TIME">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
@@ -563,6 +564,7 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎Alarms &amp; reminders‎‏‎‎‏‎"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎‎‏‏‏‏‎Allow this app to set alarms and schedule time-sensitive actions. This lets the app run in the background, which may use more battery.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎If this permission is off, existing alarms and time-based events scheduled by this app won’t work.‎‏‎‎‏‎"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‎schedule, alarm, reminder, clock‎‏‎‎‏‎"</string>
+    <string name="zen_mode_do_not_disturb_name" msgid="6798711401734798283">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‎Do Not Disturb‎‏‎‎‏‎"</string>
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‏‏‎Do Not Disturb‎‏‎‎‏‎"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‎‏‎‎Turn on‎‏‎‎‏‎"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎Turn on Do Not Disturb‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index e146f49..ff69f64 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -135,7 +135,7 @@
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio en HD"</string>
     <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audífonos"</string>
-    <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"audio de bajo consumo"</string>
+    <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string>
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a audífonos"</string>
     <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado a LE Audio"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio multimedia"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Muestra la ubicación de las presiones en la pantalla"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Ver pulsaciones de teclas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Ver comentario visual para pulsaciones de teclas físicas"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada del panel táctil"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Pantalla superpuesta que muestra datos de entrada del panel táctil y de gestos reconocidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar actualizaciones"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Destello en superficie por actualización"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mostrar cambios de vista"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Se detuvo la carga para proteger la batería"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Cargando"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carga completa: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta app establezca alarmas y programe acciones para horarios específicos. De esta manera, la app puede ejecutarse en segundo plano, lo que podría aumentar el consumo de batería.\n\nSi se desactiva este permiso, no funcionarán las alarmas ni los eventos basados en el tiempo existentes que programe esta app."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"No interrumpir"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar No interrumpir"</string>
diff --git a/packages/SettingsLib/res/values-es/arrays.xml b/packages/SettingsLib/res/values-es/arrays.xml
index 1489e5f..b99219c 100644
--- a/packages/SettingsLib/res/values-es/arrays.xml
+++ b/packages/SettingsLib/res/values-es/arrays.xml
@@ -243,7 +243,7 @@
     <item msgid="8612549335720461635">"4K (seguro)"</item>
     <item msgid="7322156123728520872">"4K (mejorado)"</item>
     <item msgid="7735692090314849188">"4K (mejorado, seguro)"</item>
-    <item msgid="7346816300608639624">"720p, 1080p (pantalla doble)"</item>
+    <item msgid="7346816300608639624">"720p, 1080p (pantalla dual)"</item>
   </string-array>
   <string-array name="enable_opengl_traces_entries">
     <item msgid="4433736508877934305">"Desactivado"</item>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index fdfb844..e2d3589 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Muestra la ubicación de los toques en la pantalla"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Ver teclas pulsadas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Muestra respuestas visuales al pulsar teclas físicas"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada del panel táctil"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Superposición en pantalla que muestra los datos introducidos en el panel táctil y los gestos reconocidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar cambios de superficies"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Hace parpadear todas las superficies de la ventana cuando se actualizan"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver actualizaciones de vista"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> hasta la carga completa"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hasta la carga completa"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga pausada para proteger la batería"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Cargar"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carga completa a las <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa a las <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones que se llevan a cabo a una hora determinada. Esto hace que la aplicación pueda seguir activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivado, no funcionarán las alarmas ni los eventos que se activan a una hora determinada que programe esta aplicación."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"No molestar"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar el modo No molestar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index cff38eb..e4490a3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -127,7 +127,7 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string>
-    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Luba juurdepääs kontakt-le ja kõneaj-le"</string>
+    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Luba juurdepääs kontaktidele ja kõneajale"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Teavet kasutatakse kõne teadaannete ja muu jaoks"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Kuvab puudutuste visuaalse tagasiside"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Kuva klahvivajutused"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Kuva visuaalset tagasisidet füüsiliste klahvivajutuste kohta"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Kuva puuteplaadi sisend"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ekraani ülekate, mis kuvab puuteplaadi sisendandmeid ja tuvastatud liigutusi"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Näita pinna värskendusi"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Akna pinna värskendamiseks kirjuta kogu akna pind üle"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Kuva ekraanikuva värskendusi"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Täislaadimiseks kulub <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – täislaadimiseks kulub <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine on aku kaitsmiseks ootele pandud"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laadimine"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – täis kell <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – aku saab täis kell <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada ajakriitilisi toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Mitte segada"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string>
diff --git a/packages/SettingsLib/res/values-eu/arrays.xml b/packages/SettingsLib/res/values-eu/arrays.xml
index 00f43a3..6ed484c 100644
--- a/packages/SettingsLib/res/values-eu/arrays.xml
+++ b/packages/SettingsLib/res/values-eu/arrays.xml
@@ -27,7 +27,7 @@
     <item msgid="8356618438494652335">"Autentifikatzen…"</item>
     <item msgid="2837871868181677206">"IP helbidea lortzen…"</item>
     <item msgid="4613015005934755724">"Konektatuta"</item>
-    <item msgid="3763530049995655072">"Etenda"</item>
+    <item msgid="3763530049995655072">"Aldi baterako etenda"</item>
     <item msgid="7852381437933824454">"Deskonektatzen…"</item>
     <item msgid="5046795712175415059">"Deskonektatuta"</item>
     <item msgid="2473654476624070462">"Ezin izan da konektatu"</item>
@@ -41,7 +41,7 @@
     <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearekin autentifikatzen…"</item>
     <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarearen IP helbidea lortzen…"</item>
     <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> sarera konektatuta"</item>
-    <item msgid="7445993821842009653">"Etenda"</item>
+    <item msgid="7445993821842009653">"Aldi baterako etenda"</item>
     <item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> saretik deskonektatzen…"</item>
     <item msgid="699832486578171722">"Deskonektatuta"</item>
     <item msgid="522383512264986901">"Ezin izan da konektatu"</item>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 622055e..828831e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Erakutsi sakatutako elementuak"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Erakutsi tekla-sakatzeak"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Erakutsi sakatutako tekla fisikoak"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Erakutsi ukipen-paneleko sarrerak"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ukipen-paneleko sarreren datuak eta hautemandako keinuak bistaratzen dituen pantaila-gainjartzea"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Erakutsi azaleko aldaketak"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Distirarazi leiho osoen azalak haiek eguneratzean"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Erakutsi ikuspegi-aldaketak"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>: kargatze-prozesua zain dago bateria babesteko"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Kargatzen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Ordu honetan kargatuko da guztiz: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ordu honetan kargatuko da guztiz: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmak eta abisuak"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programazioa, alarma, abisua, erlojua"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ez molestatzeko modua"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktibatu"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktibatu ez molestatzeko modua"</string>
diff --git a/packages/SettingsLib/res/values-fa/arrays.xml b/packages/SettingsLib/res/values-fa/arrays.xml
index 5834982..d150538 100644
--- a/packages/SettingsLib/res/values-fa/arrays.xml
+++ b/packages/SettingsLib/res/values-fa/arrays.xml
@@ -243,7 +243,7 @@
     <item msgid="8612549335720461635">"‏4K (ایمن)"</item>
     <item msgid="7322156123728520872">"‏4K (ارتقا یافته)"</item>
     <item msgid="7735692090314849188">"‏4K (ارتقا یافته، ایمن)"</item>
-    <item msgid="7346816300608639624">"‫720p، ‫1080p ‫(Dual Screen)"</item>
+    <item msgid="7346816300608639624">"‏‫‫720p، ‫1080p ‫(صفحه‌نمایش دوگانه)"</item>
   </string-array>
   <string-array name="enable_opengl_traces_entries">
     <item msgid="4433736508877934305">"خالی"</item>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f7279b1..7858cc8 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"نمایش بازخورد تصویری برای ضربه‌ها"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"نمایش فشار کلیدها"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"نمایش بازخورد بصری برای فشار دادن کلیدهای فیزیکی"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"نمایش ورودی صفحه لمسی"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"رونهاد صفحه که داده‌های ورودی صفحه لمسی و اشاره‌های شناسایی‌شده را نمایش می‌دهد"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"نمایش به‌روزرسانی سطح"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"هنگام به‌روزرسانی سطوح پنجره همه فلش شوند"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"نمایش به‌روزرسانی‌های نما"</string>
@@ -427,10 +429,10 @@
     <string name="show_notification_channel_warnings" msgid="3448282400127597331">"نمایش هشدارهای کانال اعلان"</string>
     <string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"هنگامی که برنامه‌ای بدون وجود کانالی معتبر، اعلانی پست می‌کند، هشدار روی صفحه‌ای نمایش می‌دهد"</string>
     <string name="force_allow_on_external" msgid="9187902444231637880">"اجازه اجباری به برنامه‌های دستگاه ذخیره خارجی"</string>
-    <string name="force_allow_on_external_summary" msgid="8525425782530728238">"بدون توجه به مقادیر آشکار، هر برنامه‌ای را برای نوشتن در حافظه خارجی واجد شرایط می‌کند"</string>
+    <string name="force_allow_on_external_summary" msgid="8525425782530728238">"صرف‌نظر از مقادیر مانیفست، هر برنامه‌ای را برای نوشتن در حافظه خارجی واجد شرایط می‌کند"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"اجبار فعالیت‌ها به قابل تغییر اندازه بودن"</string>
-    <string name="force_resizable_activities_summary" msgid="2490382056981583062">"بدون توجه به مقادیر مانیفست، اندازه همه فعالیت‌ها برای حالت چند پنجره‌ای می‌تواند تغییر کند."</string>
-    <string name="enable_freeform_support" msgid="7599125687603914253">"فعال کردن پنجره‌های آزاد"</string>
+    <string name="force_resizable_activities_summary" msgid="2490382056981583062">"صرف‌نظر از مقادیر مانیفست، اندازه همه فعالیت‌ها را برای حالت چندپنجره‌ای قابل‌تغییر می‌کند."</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"فعال کردن پنجره قالب آزاد"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"گذرواژه پشتیبان‌گیری محلی"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"پشتیبان‌گیری کامل رایانه درحال حاضر محافظت نمی‌شود"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"برای تغییر یا حذف گذرواژه برای نسخه‌های پشتیبان کامل رایانه‌ای تک‌ضرب بزنید"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل باقی مانده است"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - برای محافظت از باتری، شارژ موقتاً متوقف شده است"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - درحال شارژ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - تا <xliff:g id="TIME">%3$s</xliff:g> کامل می‌شود"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‫<xliff:g id="LEVEL">%1$s</xliff:g> - شارژ تا <xliff:g id="TIME">%2$s</xliff:g> کامل می‌شود"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"زنگ‌های ساعت و یادآوری‌ها"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"به این برنامه اجازه می‌دهد زنگ ساعت تنظیم کند و کنش‌های حساس به زمان را زمان‌بندی کند. این تنظیم به برنامه اجازه می‌دهد در پس‌زمینه اجرا شود که ممکن است باتری بیشتری مصرف کند.\n\nاگر این اجازه خاموش باشد، زنگ‌های ساعت موجود و رویدادهای زمان‌محور که این برنامه زمان‌بندی کرده است کار نخواهند کرد."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"زمان‌بندی، زنگ ساعت، یادآوری، ساعت"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"مزاحم نشوید"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"روشن کردن"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"روشن کردن «مزاحم نشوید»"</string>
@@ -668,7 +671,7 @@
     <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string>
     <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ذخیره"</string>
     <string name="guest_exit_button" msgid="5774985819191803960">"خروج از حالت مهمان"</string>
-    <string name="guest_reset_button" msgid="2515069346223503479">"بازنشاندن جلسه مهمان"</string>
+    <string name="guest_reset_button" msgid="2515069346223503479">"بازنشانی جلسه مهمان"</string>
     <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"خروج مهمان"</string>
     <string name="guest_notification_ephemeral" msgid="7263252466950923871">"همه فعالیت‌ها هنگام خروج حذف خواهد شد"</string>
     <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"می‌توانید فعالیتتان را هنگام خروج ذخیره یا حذف کنید"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 03f6b0a..8ad343a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Anna visuaalista palautetta kosketuksesta"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Näytä näppäinpainallukset"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Näytä visuaalista palautetta näppäinpainalluksista"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Näytä kosketuslevyn syöte"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Näytön peitto, jossa näkyy kosketuslevyn syötedata ja tunnistetut eleet"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Näytä pintapäivitykset"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Väläytä koko ikkunoiden pinnat päivitettäessä"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Näytä näyttöpäivitykset"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Lataus on keskeytetty akun suojaamiseksi"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladataan"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Täynnä klo <xliff:g id="TIME">%3$s</xliff:g> mennessä"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladattu täyteen klo <xliff:g id="TIME">%2$s</xliff:g> mennessä"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Herätykset ja muistutukset"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Anna sovelluksen lisätä herätyksiä ja ajoittaa kiireellisiä tapahtumia. Näin sovellus voi toimia taustalla, mikä voi kuluttaa enemmän virtaa.\n\nIlman tätä lupaa sovelluksen ajoittamat herätykset ja aikaan perustuvat tapahtumat eivät toimi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajoitus, herätys, muistutus, kello"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Älä häiritse"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ota käyttöön"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Laita Älä häiritse ‑tila päälle"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 04bcb6c..2e3a6a7 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Afficher repère visuel pour éléments sélectionnés"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Afficher press. de touches"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Afficher retour visuel pour press. de touches phys."</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Afficher l\'entrée du pavé tactile"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Superposition de l\'écran affichant les données d\'entrée du pavé tactile et les gestes reconnus"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Afficher mises à jour surface"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Faire clignoter les surfaces à chaque mise à jour"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Afficher m. à j. affichage"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la recharge complète"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la recharge complète)"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – La recharge a été mise en pause pour protéger la pile"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge en cours…"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Recharge complète d\'ici <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Recharge complète d\'ici <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à créer des alarmes et à programmer des actions urgentes. Cela permet à l’appli de s\'exécuter en arrière-plan, ce qui peut nécessiter plus de pile.\n\nSi cette autorisation est désactivée, les alarmes existantes et les événements en temps réel programmés par cette appli ne fonctionneront pas."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"horaire, alarme, rappel, horloge"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 455fd23..4327708 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Afficher un indicateur visuel là où l\'utilisateur appuie sur l\'écran"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Afficher les pressions sur les touches"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Afficher un retour visuel des pressions sur les touches"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Afficher la saisie au pavé tactile"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Superposition d\'écran affichant les données de saisie au pavé tactile et les gestes reconnus"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mises à jour de la surface"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Faire clignoter les endroits où des mises à jour sont effectuées"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mises à jour de fenêtres"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Chargée à 100 %% dans <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Recharge en pause pour protéger la batterie"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - En charge"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Complètement chargé d\'ici <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Complètement chargé dans <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autoriser cette appli à définir des alarmes et à programmer des actions à certaines heures. Elle s\'exécutera alors en arrière-plan, ce qui peut solliciter davantage la batterie.\n\nSi l\'autorisation est désactivée, les alarmes existantes et les événements programmés par l\'appli ne fonctionneront pas."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"définir, alarme, rappel, horloge"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne pas déranger"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 7127ca8..9bcdc97 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostra a localización dos toques na pantalla"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas premidas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra información das teclas físicas premidas"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada do panel táctil"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Superposición na pantalla que mostra os datos de entrada do panel táctil e os xestos recoñecidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar cambios de superficie"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ilumina as superficies de ventás ao actualizarse"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mostrar actualizacións"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>. A carga púxose en pausa para protexer a batería"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (cargando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Completarase á/s <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa á/s <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Non molestar"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f89e1486..e402ad3 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -130,7 +130,7 @@
     <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"સંપર્કો અને કૉલ ઇતિહાસ ઍક્સેસ કરવા દો"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"માહિતીનો ઉપયોગ કૉલની ઘોષણાઓ અને વધુ બાબતો માટે કરવામાં આવશે"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
-    <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string>
+    <string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ મેસેજ"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ઑડિયો: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ઑડિયો"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ટૅપ માટે વિઝ્યુઅલ પ્રતિસાદ બતાવો"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"કી દબાવવાની ઘટના બતાવો"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"વાસ્તિવક રીતે કી દબાવવાના વિઝ્યુઅલ પ્રતિસાદ બતાવો"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ટચપૅડ ઇનપુટ બતાવો"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ટચપૅડ ઇનપુટનો ડેટા અને ઓળખેલા સંકેતો બતાવતું સ્ક્રીન ઓવરલે"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"સપાટી અપડેટ બતાવો"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"તે અપડેટ થાય ત્યારે સમગ્ર વિન્ડો સપાટી ફ્લેશ કરો"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"વ્યૂના અપડેટ બતાવો"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીને સુરક્ષિત રાખવા માટે, ચાર્જિંગ હોલ્ડ પર રાખવામાં આવ્યું છે"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> સુધીમાં સંપૂર્ણ ચાર્જ થશે"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> સુધીમાં સંપૂર્ણ ચાર્જ થશે"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ખલેલ પાડશો નહીં"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ચાલુ કરો"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ખલેલ પાડશો નહીં ચાલુ કરો"</string>
@@ -713,7 +716,7 @@
     <string name="accessibility_ethernet_disconnected" msgid="2832501530856497489">"ઇથરનેટ ડિસ્કનેક્ટ થયું."</string>
     <string name="accessibility_ethernet_connected" msgid="6175942685957461563">"ઇથરનેટ."</string>
     <string name="accessibility_no_calling" msgid="3540827068323895748">"કોઈ કૉલિંગ નહીં."</string>
-    <string name="physical_keyboard_title" msgid="4811935435315835220">"ભૌતિક કીબોર્ડ"</string>
+    <string name="physical_keyboard_title" msgid="4811935435315835220">"વાસ્તવિક કીબોર્ડ"</string>
     <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"કીબોર્ડ લેઆઉટ પસંદ કરો"</string>
     <string name="keyboard_layout_default_label" msgid="1997292217218546957">"ડિફૉલ્ટ"</string>
     <string name="turn_screen_on_title" msgid="2662312432042116026">"સ્ક્રીન ચાલુ કરવાનું નિયંત્રણ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index adf8b5f..2e484e4 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -116,7 +116,7 @@
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है). बायां हेडसेट: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, दायां हेडसेट: <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>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"कनेक्ट हो गया (ऑडियो शेयर करने की सुविधा काम करती है)"</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"कनेक्ट है (ऑडियो शेयर करने की सुविधा काम करती है)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"चालू है (सिर्फ़ मीडिया के लिए)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ऑडियो शेयर करने की सुविधा काम करती है"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"चालू है (सिर्फ़ मीडिया के लिए), सिर्फ़ बाएं कान की मशीन"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"टैप के लिए विज़ुअल फ़ीडबैक दिखाएं"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"दबाए गए बटन दिखाएं"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"दबाए गए बटन का विज़ुअल फ़ीडबैक दिखाएं"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"टचपैड इनपुट दिखाएं"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"स्क्रीन ओवरले में टचपैड इनपुट का डेटा और पहचाने गए जेस्चर दिख रहे हैं"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"सर्फ़ेस अपडेट दिखाएं"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट होने पर पूरे विंडो सर्फ़ेस फ़्लैश करें"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"जीपीयू व्यू के अपडेट दिखाएं"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में बैटरी पूरी चार्ज हो जाएगी"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी को सुरक्षित रखने के लिए, फ़ोन को चार्ज होने से रोक दिया गया है"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज हो रही है"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - बैटरी <xliff:g id="TIME">%3$s</xliff:g> में पूरी चार्ज हो जाएगी"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - बैटरी <xliff:g id="TIME">%2$s</xliff:g> में पूरी चार्ज हो जाएगी"</string>
@@ -509,7 +510,7 @@
     <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस चार्जिंग"</string>
     <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"चार्ज हो रहा है"</string>
     <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज नहीं हो रही है"</string>
-    <string name="battery_info_status_not_charging" msgid="1103084691314264664">"फ़ोन कनेक्ट हो गया, लेकिन चार्ज नहीं हो रहा है"</string>
+    <string name="battery_info_status_not_charging" msgid="1103084691314264664">"फ़ोन कनेक्ट है, लेकिन चार्ज नहीं हो रहा"</string>
     <string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string>
     <string name="battery_info_status_full_charged" msgid="3536054261505567948">"बैटरी पूरी चार्ज है"</string>
     <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"फ़ोन को चार्ज होने से रोक दिया गया है"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म और रिमाइंडर"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे बैटरी ज़्यादा खर्च होती है.\n\nऐप्लिकेशन को यह अनुमति न देने पर, इसकी मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्यूल, अलार्म, रिमाइंडर, घड़ी"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"परेशान न करें"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करें"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'परेशान न करें\' चालू करें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 3c20bf4..a1c9a61 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Prikazuju se vizualne povratne informacije za dodire"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Prikaži pritiske na tipke"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Prikaži vizualne povratne informacije za pritiske na tipke"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Prikaz unosa na dodirnu podlogu"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Na preklapanju na zaslonu prikazuju se podaci uneseni na dodirnoj podlozi i prepoznati pokreti"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja površine"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Sve površine prozora bljeskaju pri ažuriranju"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do napunjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje je pauzirano radi zaštite baterije"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – bit će pun do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite toj aplikaciji da postavlja alarme i zakazuje radnje u točno određeno vrijeme. To aplikaciji omogućuje da se izvodi u pozadini, pa je moguća dodatna potrošnja baterije.\n\nAko je to dopuštenje isključeno, postojeći alarmi i događaji zakazani putem te aplikacije neće funkcionirati."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne uznemiravaj"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite opciju Ne uznemiravaj."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fd938b9..5ea4fdb 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Koppintások vizuális visszajelzésének megjelenítése"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Gomblenyomások mutatása"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Fizikai gomblenyomások vizuális visszajelzéseinek mutatása"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Az érintőpados bevitel megjelenítése"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Képernyőfedvény érintőpados beviteli adatokkal és felismert kézmozdulatokkal"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Felületfrissítések megj."</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"A teljes ablakfelület villogjon frissítéskor."</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Frissítések megjelenítése"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttségig"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Az akkumulátor védelme érdekében a töltés szünetel"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Töltés…"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Teljes feltöltés eddig: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Teljes feltöltés eddig: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ébresztések és emlékeztetők"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lehetővé teszi ennek az alkalmazásnak, hogy ébresztéseket állítson be és időérzékeny feladatokat ütemezzen. Ezzel engedélyezi az alkalmazásnak, hogy a háttérben fusson, ami megnövekedett akkumulátorhasználattal járhat.\n\nHa ez az engedély ki van kapcsolva, az alkalmazás által beállított ébresztések és ütemezett időérzékeny események nem fognak működni."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ütemezés, ébresztés, emlékeztető, óra"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne zavarjanak"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bekapcsolás"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"A Ne zavarjanak mód bekapcsolása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 389aad2..f7b51a5 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Ցույց տալ հպումների տեսանելի արձագանքը"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Ցույց տալ սեղմումները"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Տեսողական արձագանք ստեղների սեղմումների համար"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Ցույց տալ հպահարթակի մուտքային տվյալները"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Էկրանի վրադրում, որում ցուցադրված են հպահարթակի մուտքագրման տվյալները և հայտնաբերված ժեստերը"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Ցույց տալ մակերեսի թարմացումները"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ցուցադրել թարմացումները"</string>
@@ -430,7 +432,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Թույլ է տալիս ցանկացած հավելված պահել արտաքին սարքում՝ մանիֆեստի արժեքներից անկախ"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Չափերի փոփոխում բազմապատուհան ռեժիմում"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Բոլոր ակտիվությունների չափերը բազմապատուհան ռեժիմի համար դարձնել փոփոխելի՝ մանիֆեստի արժեքներից անկախ:"</string>
-    <string name="enable_freeform_support" msgid="7599125687603914253">"Ակտիվացնել կամայական ձևի պատուհանները"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Թույլատրել կամայական ձևի պատուհանների ստեղծումը"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Աշխատասեղանի պահուստավորման գաղտնաբառ"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Աշխատասեղանի ամբողջական պահուստավորումները այժմ պաշտպանված չեն"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Հպեք՝ աշխատասեղանի ամբողջական պահուստավորման գաղտնաբառը փոխելու կամ հեռացնելու համար"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Լիցքավորումը դադարեցվել է՝ մարտկոցը պաշտպանելու համար"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — Լիցքավորում"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Ամբողջովին կլիցքավորվի մինչև <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ամբողջովին կլիցքավորվի մինչև <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Զարթուցիչներ և հիշեցումներ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Թույլատրել այս հավելվածին զարթուցիչներ կարգավորել և որոշակի ժամանակի համար գործողություններ պլանավորել։ Այդ դեպքում հավելվածն աշխատում է ֆոնային ռեժիմում, և արդյունքում մարտկոցի լիցքն ավելի արագ է սպառվում։\n\nԵթե այս թույլտվությունն անջատված է, հավելվածի կողմից կարգավորված զարթուցիչները և գործողությունների ժամանակացույցները չեն աշխատի։"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ժամանակացույց, զարթուցիչ, հիշեցում, ժամացույց"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Չանհանգստացնել"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Միացնել"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Միացրեք «Չանհանգստացնել» ռեժիմը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c6bd3e9..070c161 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -81,7 +81,7 @@
     <string name="speed_label_fast" msgid="2677719134596044051">"Cepat"</string>
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string>
-    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Sambungan terputus"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Memutus sambungan..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Menghubungkan…"</string>
@@ -112,7 +112,7 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktif (kiri dan kanan)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktif (hanya media). Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktif (hanya media). Baterai 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">"Terhubung (mendukung berbagi audio). Baterai<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Terhubung (mendukung berbagi audio). Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Terhubung (mendukung berbagi audio). Baterai 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">"Terhubung (mendukung berbagi audio). Kiri: Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Terhubung (mendukung berbagi audio). Kanan: Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Tampilkan efek visual untuk ketukan"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Tampilkan penekanan tombol"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Tampilkan respons visual untuk penekanan tombol fisik"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Tampilkan input touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Overlay layar menampilkan data input touchpad dan mengenali gestur"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Tampilkan pembaruan permukaan"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Buat seluruh permukaan jendela berkedip saat diperbarui"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Tampilkan pembaruan tampilan"</string>
@@ -430,7 +432,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"Membuat semua aplikasi dapat ditulis ke penyimpanan eksternal, terlepas dari nilai manifes"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"Paksa aktivitas agar ukurannya dapat diubah"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Membuat semua aktivitas dapat diubah ukurannya untuk banyak jendela, terlepas dari nilai manifes."</string>
-    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktifkan jendela berformat bebas"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"Aktifkan jendela berbentuk bebas"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"Sandi cadangan desktop"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"Saat ini cadangan desktop penuh tidak dilindungi"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"Ketuk guna mengubah atau menghapus sandi untuk cadangan lengkap desktop"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sampai penuh"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sampai penuh"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengisian daya dihentikan sementara untuk melindungi baterai"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengisi daya"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Penuh pukul <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Baterai terisi penuh dalam <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm &amp; pengingat"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Mengizinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadwal, alarm, pengingat, jam"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktifkan"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktifkan mode Jangan Ganggu"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 2575af0..5408b50 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Sýna snertingar myndrænt"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Sýna „Ýtt á lykil“"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Sýna myndsvörun fyrir „Ýtt á raunverulegan lykil“"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Sýna inntak snertiflatar"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Skjáyfirlögn sýnir gögn inntaks snertiflatar og þekktar bendingar"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Sýna yfirborðsuppfærslur"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Láta allt yfirborð glugga blikka við uppfærslu"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Sýna uppfærslur yfirlits"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> fram að fullri hleðslu"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> fram að fullri hleðslu"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Hleðsla í bið til að vernda rafhlöðuna"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Í hleðslu"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Fullt kl. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fullhlaðið kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Vekjarar og áminningar"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leyfa þessu forriti að stilla vekjara og áætla aðgerðir sem þurfa að eiga sér stað innan ákveðins tímaramma. Þetta leyfir forritinu að keyra í bakgrunninum sem getur notað meiri rafhlöðuorku.\n\nEf slökkt er á þessari heimild munu núverandi vekjarar og tímasettir viðburðir sem þetta forrit stillir ekki virka."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"áætlun, vekjari, áminning, klukka"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ónáðið ekki"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Kveikja"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Kveikja á „Ónáðið ekki“"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index deae4a5..10394f1 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostra feedback visivi per i tocchi"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostra pressioni tasti"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra feedback visivo per pressioni tasti fisici"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostra input touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"L\'overlay schermo mostra i dati di input del touchpad e i gesti riconosciuti"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Aggiornamenti superficie"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fai lampeggiare le superfici delle finestre quando si aggiornano"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Aggiornam. visualizzazione"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla ricarica completa"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ricarica in sospeso per proteggere la batteria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ In carica"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Ricarica completa entro <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batteria completamente carica entro <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Sveglie e promemoria"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Consenti a questa app di impostare sveglie e programmare azioni per orari specifici. L\'app potrà essere eseguita in background, comportando un consumo maggiore della batteria.\n\nSe questa autorizzazione viene disattivata, le sveglie esistenti e gli eventi basati sull\'orario programmati da questa app non funzioneranno."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programmare, sveglia, promemoria, orologio"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Non disturbare"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Attiva"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Attiva Non disturbare"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 310b046..fce947a 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -196,7 +196,7 @@
     <string name="launch_defaults_some" msgid="3631650616557252926">"הוגדרו כמה ברירות מחדל"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"לא הוגדרו ברירות מחדל"</string>
     <string name="tts_settings" msgid="8130616705989351312">"הגדרות טקסט לדיבור"</string>
-    <string name="tts_settings_title" msgid="7602210956640483039">"המרת טקסט לדיבור"</string>
+    <string name="tts_settings_title" msgid="7602210956640483039">"פלט של המרת טקסט לדיבור"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"קצב דיבור"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"המהירות שבה הטקסט נאמר"</string>
     <string name="tts_default_pitch_title" msgid="6988592215554485479">"גובה צליל"</string>
@@ -207,7 +207,7 @@
     <string name="tts_default_lang_summary" msgid="9042620014800063470">"הגדרת קול ספציפי לשפה עבור הטקסט הנאמר"</string>
     <string name="tts_play_example_title" msgid="1599468547216481684">"האזנה לדוגמה"</string>
     <string name="tts_play_example_summary" msgid="634044730710636383">"הפעלת הדגמה קצרה של סינתזת דיבור"</string>
-    <string name="tts_install_data_title" msgid="1829942496472751703">"התקנת נתוני קול"</string>
+    <string name="tts_install_data_title" msgid="1829942496472751703">"התקנת נתונים שנאספים בזיהוי קולי"</string>
     <string name="tts_install_data_summary" msgid="3608874324992243851">"התקנת הנתונים הקוליים הדרושים לסינתזת דיבור"</string>
     <string name="tts_engine_security_warning" msgid="3372432853837988146">"ייתכן שמנוע סינתזת דיבור זה יכול לאסוף את כל הטקסט המדובר, כולל נתונים אישיים כגון סיסמאות ומספרי כרטיסי אשראי. הוא מגיע מהמנוע <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g>. להפוך את השימוש במנוע סינתזת דיבור זה לפעיל?"</string>
     <string name="tts_engine_network_required" msgid="8722087649733906851">"שפה זו דורשת חיבור רשת פעיל עבור פלט טקסט לדיבור."</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"הצגת משוב ויזואלי להקשות"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"הצגת לחיצות המקשים"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"הצגת משוב חזותי עבור לחיצות פיזיות על מקשים"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"הצגת הקלט של לוח המגע"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"שכבת-על במסך מציגה את נתוני הקלט של לוח המגע ואת התנועות המזוהות"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"הצגת עדכונים על פני השטח"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"הבהוב של כל שטחי החלון כשהם מתעדכנים"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"הצגת עדכונים של התצוגה"</string>
@@ -430,7 +432,7 @@
     <string name="force_allow_on_external_summary" msgid="8525425782530728238">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
     <string name="force_resizable_activities" msgid="7143612144399959606">"אילוץ יכולת קביעת גודל של הפעילויות"</string>
     <string name="force_resizable_activities_summary" msgid="2490382056981583062">"מאפשר יכולת קביעת גודל של כל הפעילויות לריבוי חלונות, ללא התחשבות בערכי המניפסט."</string>
-    <string name="enable_freeform_support" msgid="7599125687603914253">"הפעלת האפשרות לשנות את הגודל והמיקום של החלונות"</string>
+    <string name="enable_freeform_support" msgid="7599125687603914253">"הפעלת מצב חופשי (חלונות צפים)"</string>
     <string name="local_backup_password_title" msgid="4631017948933578709">"סיסמת גיבוי שולחן העבודה"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"גיבויים מלאים בשולחן העבודה אינם מוגנים כעת"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"יש להקיש כדי לשנות או להסיר את הסיסמה לגיבויים מלאים בשולחן העבודה"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – הזמן הנותר לטעינה מלאה: <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – הטעינה הושהתה כדי להגן על הסוללה"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – בטעינה"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – טעינה מלאה עד <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"‫<xliff:g id="LEVEL">%1$s</xliff:g> – טעינה מלאה עד <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"שעונים מעוררים ותזכורות"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ההרשאה הזו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן פעולות דחופות. האפליקציה תוכל לפעול ברקע ובכך להגביר את צריכת הסוללה.\n\nאם ההרשאה מושבתת, ההתראות והאירועים מבוססי-הזמן שהוגדרו ותוזמנו על ידי האפליקציה לא יפעלו."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"תזמון, שעון מעורר, תזכורת, שעון"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"נא לא להפריע"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"הפעלה"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"הפעלת מצב נא לא להפריע"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4dc0580..41acfc0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"タップを視覚表示する"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"キーの押下を表示"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"物理キーが押下されたことを視覚的に表示"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"タッチパッド入力を表示する"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"タッチパッドの入力データと認識されたデータを画面オーバーレイで表示する"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"表示面の更新を通知"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新時にウィンドウの表示面全体を点滅させる"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"画面の更新を表示"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 完了まであと <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - バッテリーを保護するため、充電を一時停止しています"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>までに完了"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>までに充電完了"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"アラームとリマインダー"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"アラームの設定や時間ベースのアクション設定を、このアプリに許可します。これによりアプリがバックグラウンドで実行できるようになるため、バッテリーの使用量が増えることがあります。\n\nこの権限が OFF の場合、このアプリで設定された既存のアラームと時間ベースのイベントは機能しなくなります。"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"スケジュール, アラーム, リマインダー, 時計"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"サイレント モード"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ON にする"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"サイレント モードを ON にする"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index a196844..58a01a4 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -94,9 +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.format failed for translation -->
-    <!-- no translation found for bluetooth_active_battery_level (2685517576209066008) -->
-    <skip />
+    <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">"აქტიური. მარცხენა: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, მარჯვენა: <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,9 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"აქტიური (მხოლოდ მარცხენა)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"აქტიური (მხოლოდ მარჯვენა)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"აქტიური (მარცხენა და მარჯვენა)"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for bluetooth_active_media_only_battery_level (7772517511061834073) -->
-    <skip />
+    <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">"აქტიური (მხოლოდ მედია), მარცხენა: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, მარჯვენა:<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_untethered_lea_support" msgid="803110681688633362">"დაკავშირებული (აუდიოს გაზიარება მხარდაჭერილია). მარცხენა: ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, მარჯვენა: ბატარეა <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
@@ -384,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"შეხებებისთვის ვიზუალური უკუკავშირის ჩვენება"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"კლავიშების დაჭერის ჩვენება"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ვიზუალური გამოხმაურების ჩვენება კლავიშის დაჭერაზე"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"სენსორული პანელის შენატანის ჩვენება"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ეკრანის გადაფარვა, რომელიც აჩვენებს სენსორული პანელის შენატანების მონაცემებს და ამოცნობილ ჟსეტებს"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"ზედაპირის განახლებების ჩვენება"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ფანჯრის მთელი ზედაპირის აციმციმება მისი განახლებისას"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"განახლებების ჩვენება"</string>
@@ -499,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – დატენა შეჩერებულია ბატარეის დასაცავად"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – იტენება"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - სრულად დატენის დრო: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - სრულად დატენის დრო: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -567,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"მაღვიძარები და შეხსენებები"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ნებას რთავს ამ აპს, დააყენოს მაღვიძარები და დაგეგმოს დროზე დამოკიდებული მოქმედებები. ეს საშუალებას აძლევს აპს, იმუშაოს ფონურად, რამაც შეიძლება ბატარეის ხარჯი გაზარდოს.\n\nთუ ეს ნებართვა გამორთულია, ამ აპით დაგეგმილი მაღვიძარები და დროზე დამოკიდებული მოვლენები არ იმუშავებს."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"განრიგი, მაღვიძარა, შეხსენება, საათი"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"არ შემაწუხოთ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ჩართვა"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„არ შემაწუხოთ“ რეჟიმის ჩართვა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b0b71ef..485120d 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -112,11 +112,11 @@
     <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">"Істеп тұр (тек мультимедиа). Сол жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <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">"Қосылды (аудио бөлісуге мүмкіндік береді). Сол жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <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>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Қосылды (аудио бөлісуге мүмкіндік береді)."</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Жалғанды (аудио бөлісу мүмкіндігі бар)."</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Істеп тұр (тек мультимедиа)."</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Аудио бөлісуге мүмкіндік береді."</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Тек сол жақ істеп тұр (мультимедиа ғана)."</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Түрту қимылын экраннан көрсету"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Түйменің басылуын көрсету"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Түйменің басылуын экраннан көрсету"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Сенсорлық тақта кіріс деректерін көрсету"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Сенсорлық тақтаның кіріс деректері мен танылған қимылдар көрсетілген экран оверлейі"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Бедердің жаңарғанын көрсету"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Бедері жаңарғанда, терезені түгелдей жыпылықтату"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Көріністің жаңарғанын көрсету"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Толық зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды."</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>: батареяны қорғау үшін зарядтау кідіртілді."</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарядталып жатыр"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Зарядталып болады: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Толық заряд алуға қалған уақыт: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Оятқыштар мен еске салғыштар"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бұл қолданбаға оятқыштарды орнатуға және уақытқа байланысты әрекеттерді жоспарлауға рұқсат береді. Мұндайда қолданба фондық режимде жұмыс істейді, сондықтан батарея шығыны артуы мүмкін.\n\nБұл рұқсат өшірулі болса, осы қолданбада жоспарланған ағымдағы оятқыштар мен уақытқа байланысты іс-шаралар жұмыс істемейді."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"кесте, оятқыш, еске салғыш, сағат"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Мазаламау"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Қосу"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Мазаламау режимін қосу"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 577a2ae..7fbc509 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"បង្ហាញដានចុច នៅពេលចុច"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"បង្ហាញការចុចគ្រាប់ចុច"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"បង្ហាញរូបភាពប្រតិកម្មសម្រាប់ការចុចគ្រាប់ចុចរូបវន្ត"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"បង្ហាញការបញ្ចូលតាមផ្ទាំងប៉ះ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ការត្រួតគ្នា​លើ​អេក្រង់ដែលបង្ហាញទិន្នន័យបញ្ចូលតាមផ្ទាំងប៉ះ និងចលនាដែលស្គាល់"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"បង្ហាញ​បច្ចុប្បន្នភាព​ផ្ទៃ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ផ្ទៃ​វីនដូទាំង​មូល​បញ្ចេញពន្លឺ​នៅពេល​ធ្វើ​បច្ចុប្បន្នភាព"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"បង្ហាញ​បច្ចុប្បន្នភាពទិដ្ឋភាព"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបពេញ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅសល់ <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបពេញ"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងផ្អាកការសាកថ្ម ដើម្បីការពារថ្ម"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - កំពុងសាកថ្ម"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - ពេញនៅម៉ោង <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - សាកថ្មពេញនៅម៉ោង <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ម៉ោងរោទ៍ និង​ការរំលឹក"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"អនុញ្ញាតឱ្យ​កម្មវិធីនេះ​កំណត់ម៉ោងរោទ៍ និងកំណត់កាលវិភាគសកម្មភាពដែលតម្រូវឱ្យទាន់ពេលវេលា។ ការធ្វើបែបនេះអនុញ្ញាតឱ្យកម្មវិធីនេះដំណើរការនៅផ្ទៃខាងក្រោយ ដែលអាចប្រើថ្មកាន់តែច្រើន។\n\nប្រសិនបើបិទការអនុញ្ញាតនេះ ម៉ោងរោទ៍ដែលមានស្រាប់ និងព្រឹត្តិការណ៍ផ្អែកលើពេលវេលាដែលកំណត់ដោយកម្មវិធីនេះ​នឹងមិនដំណើរការទេ។"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"កាលវិភាគ ម៉ោងរោទ៍ ការរំលឹក នាឡិកា"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"កុំ​រំខាន"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"បើក"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"បើកមុខងារកុំរំខាន"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 69e8bff..85075c3 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ಟ್ಯಾಪ್‌ಗಳಿಗೆ ದೃಶ್ಯ ಪ್ರತಿಕ್ರಿಯೆ ತೋರಿಸು"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"ಕೀ ಪ್ರೆಸ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ಭೌತಿಕ ಕೀ ಪ್ರೆಸ್‌ಗಳ ವಿಷುವಲ್ ಪ್ರತಿಕ್ರಿಯೆಗಾಗಿ ನೋಡಿ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ಟಚ್‌ಪ್ಯಾಡ್ ಇನ್‌ಪುಟ್ ಅನ್ನು ತೋರಿಸಿ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ಟಚ್‌ಪ್ಯಾಡ್ ಇನ್‌ಪುಟ್ ಡೇಟಾ ಮತ್ತು ಗುರುತಿಸಲಾದ ಗೆಸ್ಚರ್‌ಗಳನ್ನು ಪ್ರದರ್ಶಿಸುವ ಸ್ಕ್ರೀನ್ ಓವರ್‌ಲೇ"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"ಸರ್ಫೇಸ್‌‌ ಅಪ್‌ಡೇಟ್ ತೋರಿಸಿ‌"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ಅಪ್‌ಡೇಟ್‌ ಆಗುವಾಗ ವಿಂಡೋದ ಸರ್ಫೇಸ್‌ ಫ್ಲ್ಯಾಶ್ ಆಗುತ್ತದೆ"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ\' ತೋರಿಸಿ"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> - ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ತಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯನ್ನು ರಕ್ಷಿಸಲು ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ಹೋಲ್ಡ್‌ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ವೇಳೆಗೆ ಭರ್ತಿಯಾಗುತ್ತದೆ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೇಳೆಗೆ ಸಂಪೂರ್ಣವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತದೆ"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್‌ಗಳು"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ಅಲಾರಂಗಳನ್ನು ಹೊಂದಿಸಲು ಮತ್ತು ಸಮಯ-ಸೂಕ್ಷ್ಮವಾದ ಕ್ರಿಯೆಗಳನ್ನು ನಿಗದಿಪಡಿಸಲು ಈ ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸಿ. ಇದು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗಲು ಆ್ಯಪ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಅದರಿಂದ ಹೆಚ್ಚು ಬ್ಯಾಟರಿ ಬಳಕೆಯಾಗಬಹುದು.\n\nಈ ಅನುಮತಿ ಆಫ್ ಆಗಿದ್ದರೆ, ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಲಾರಂಗಳು ಮತ್ತು ಈ ಆ್ಯಪ್ ನಿಗದಿಪಡಿಸಿದ ಸಮಯ-ಸೂಕ್ಷ್ಮ ಈವೆಂಟ್‌ಗಳು ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ವೇಳಾಪಟ್ಟಿ, ಅಲಾರಂ, ರಿಮೈಂಡರ್, ಗಡಿಯಾರ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ಆನ್ ಮಾಡಿ"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index af4f37b..480d78f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -116,7 +116,7 @@
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"연결되었습니다(오디오 공유 지원). 배터리는 왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽 <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>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"연결되었습니다(오디오 공유 지원)."</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"연결됨(오디오 공유 지원)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"사용 중(미디어 전용)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"오디오 공유 지원"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"사용 중(미디어 전용), 왼쪽만"</string>
@@ -127,7 +127,7 @@
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string>
-    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"연락처 및 통화 기록에 대한 액세스를 허용합니다."</string>
+    <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"연락처 및 통화 기록에 대한 액세스 허용"</string>
     <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"정보는 전화 알림 등에 사용됩니다."</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"인터넷 연결 공유"</string>
     <string name="bluetooth_profile_map" msgid="8907204701162107271">"문자 메시지"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"탭한 항목에 대해 시각적인 피드백 표시"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"키 누름 표시"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"물리적 키 누름에 관한 시각적 피드백을 표시합니다."</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"터치패드 입력 표시"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"터치패드 입력 데이터와 인식된 동작을 표시하는 화면 오버레이"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"표면 업데이트 표시"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"전체 창 화면이 업데이트되었을 때 플래시 처리"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"보기 업데이트 표시"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - 배터리 보호를 위해 충전 일시중지"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 충전 중"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>에 완전히 충전됨"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>에 완전히 충전됨"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"알람 및 리마인더"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"이 앱이 알람을 설정하고 시간 기반 작업을 예약할 수 있도록 허용합니다. 이렇게 하면 백그라운드에서 앱 실행이 허용되어 배터리 사용량이 증가할 수 있습니다.\n\n이 권한을 사용 중지하면 이 앱에서 예약한 기존의 알람 및 시간 기반 일정이 작동하지 않습니다."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"일정 예약, 알람, 리마인더, 시계"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"방해 금지 모드"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"사용 설정"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"방해 금지 모드 사용 설정"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 95d35f3..1667041 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -112,13 +112,13 @@
     <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_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>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Туташкан (чогуу угуу колдоого алынат)"</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>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Туташып турат (чогуу уксаңыз болот)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Активдүү (медиа үчүн гана)"</string>
-    <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Чогуу угуу колдоого алынат"</string>
+    <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Чогуу уксаңыз болот"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Активдүү (медиа үчүн гана), сол кулакчын гана"</string>
     <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"Активдүү (медиа үчүн гана), оң кулакчын гана"</string>
     <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"Активдүү (медиа үчүн гана), сол жана оң кулакчын"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Экрандын басылган жерлери көрүнүп турат"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Баскычтардын басылганын көрсөтүү"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Баскычтар басылганда визуалдык сигнал көрүнөт"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Сенсордук тактадан киргизилген маалыматты көрсөтүү"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Сенсордук тактадан киргизилген маалыматты жана таанылган жаңсоолорду көрсөткөн экран оверлейи"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Экран жаңыруусун көрсөтүү"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Экран жаңырганда анын үстү жарык болот"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Жаңыртууларды көрсөтүү"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталат"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталат"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны коргоо үчүн кубаттоо тындырылды"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Кубатталууда"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Саат <xliff:g id="TIME">%3$s</xliff:g> кубатталып бүтөт"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> чейин толук кубатталат"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ойготкучтар жана эстеткичтер"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бул колдонмого ойготкучтарды коюуга жана башка аракеттерди графикке киргизүүгө уруксат бересиз. Ушуну менен колдонмо фондо иштеп, батареяны көбүрөөк сарпташы мүмкүн.\n\nЭгер бул уруксат өчүрүлсө, колдонмодогу ойготкучтар жана графикке киргизилген башка аракеттер иштебейт."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, ойготкуч, эстеткич, саат"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Тынчымды алба"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Күйгүзүү"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Тынчымды алба\" режимин күйгүзүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 2bf96ad..2c6d6ba 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ສະແດງຄໍາຕິຊົມທາງຮູບພາບສຳລັບການແຕະ"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"ສະແດງການກົດປຸ່ມ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ສະແດງການຕອບສະໜອງທີ່ເປັນພາບສຳລັບການກົດປຸ່ມຈິງ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ສະແດງອິນພຸດຂອງແຜ່ນສໍາຜັດ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ການຊ້ອນໜ້າຈໍທີ່ສະແດງຂໍ້ມູນອິນພຸດຂອງແຜ່ນສໍາຜັດ ແລະ ທ່າທາງທີ່ຈຳແນກໄດ້"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"ສະແດງການອັບເດດພື້ນຜິວ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ກະພິບໜ້າຈໍທັງໜ້າເມື່ອມີການອັບເດດ"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"ສະແດງອັບເດດມຸມມອງ"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ຍັງເຫຼືອອີກ <xliff:g id="TIME">%1$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"ຍັງເຫຼືອອີກ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງຈະສາກເຕັມ"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຢຸດການສາກຊົ່ວຄາວເພື່ອປົກປ້ອງແບັດເຕີຣີ"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ກຳລັງສາກໄຟ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - ຈະເຕັມພາຍໃນ <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈະສາກເຕັມພາຍໃນ <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ອະນຸຍາດໃຫ້ແອັບນີ້ຕັ້ງໂມງປຸກ ແລະ ກຳນົດເວລາຄຳສັ່ງທີ່ເນັ້ນເລື່ອງເວລາເປັນສຳຄັນໄດ້. ນີ້ຈະເຮັດໃຫ້ແອັບເຮັດວຽກໄດ້ໃນພື້ນຫຼັງ, ເຊິ່ງອາດໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ.\n\nຫາກປິດການອະນຸຍາດນີ້ໄວ້, ໂມງປຸກທີ່ມີຢູ່ກ່ອນແລ້ວ ແລະ ເຫດການທີ່ອ້າງອີງເວລາທີ່ກຳນົດໄວ້ໂດຍແອັບນີ້ຈະບໍ່ສາມາດເຮັດວຽກໄດ້."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ກຳນົດເວລາ, ໂມງປຸກ, ການແຈ້ງເຕືອນ, ໂມງ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ຫ້າມລົບກວນ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ເປີດ"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ເປີດໂໝດຫ້າມລົບກວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 6363cdb..a6ef078 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Rodyti vaizdinius palietimų atsiliepimus"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Rodyt klavišų paspaudimus"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Rodyti fizinių klavišų paspaudimų vaizdinį atsiliepimą"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Rodyti jutiklinės dalies įvestį"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ekrano perdanga rodo jutiklinės dalies įvesties duomenis ir atpažįsta gestus"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Rodyti paviršiaus naujin."</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Naujinant mirginti visus langų paviršius"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Rodyti rodinių naujinius"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Liko <xliff:g id="TIME">%1$s</xliff:g>, kol bus visiškai įkrauta"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko <xliff:g id="TIME">%2$s</xliff:g>, kol bus visiškai įkrauta"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkrovimas pristabdytas, siekiant apsaugoti akumuliatorių"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – įkraunama"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – bus visiškai įkrauta <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – bus visiškai įkrauta <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signalai ir priminimai"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leiskite šiai programai nustatyti signalus ir suplanuoti veiksmus, kuriems svarbus laiko veiksnys. Dėl to programa gali veikti fone ir sunaudoti daugiau akumuliatoriaus energijos.\n\nJei šis leidimas išjungtas, šios programos suplanuoti esami signalai ir laiku pagrįsti įvykiai neveiks."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tvarkaraštis, signalas, priminimas, laikrodis"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Netrukdymo režimas"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Įjungti"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Netrukdymo režimo įjungimas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 2b82cdb..a1fef64 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Rādīt vizuālo reakciju pēc pieskārieniem"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Rādīt taustiņu nospiešanu"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Vizuāla reakcija uz fizisku taustiņu nospiešanu"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Rādīt skārienpaliktņa ievadi"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ekrāna pārklājums, kurā tiek rādīti skārienpaliktņa ievades dati un atpazītie žesti."</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Rādīt virsmas atjauninājumus WL: 294"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Atjaunināt visa loga virsmas, kad tās tiek atjauninātas WL: 294"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Rādīt skat. atjaunin."</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> — uzlāde apturēta, lai aizsargātu akumulatoru"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> — notiek uzlāde"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATUS">%2$s</xliff:g>. Tiks pilnībā uzlādēts līdz plkst. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> — tiks pilnībā uzlādēts līdz plkst. <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signāli un atgādinājumi"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Atļaujiet šai lietotnei iestatīt signālus un ieplānot darbības, kas jāveic konkrētā laikā. Tādējādi lietotne darbosies fonā un, iespējams, patērēs vairāk akumulatora enerģijas.\n\nJa šī atļauja nav piešķirta, esošie signāli un šīs lietotnes ieplānotie notikumi, kas jāizpilda konkrētā laikā, nedarbosies."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ieplānot, signāls, atgādinājums, pulkstenis"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Netraucēt"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ieslēgt"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Režīma “Netraucēt” ieslēgšana"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index b135afa..e22f67c 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Прикажувај визуелни повратни информации за допири"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Прикаж. притисн. копчиња"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Визуелно го прикажува притискањето на копчињата"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Прикажи влезен податок од допирната подлога"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Преклопување на екранот што прикажува влезни податоци од допирната подлога и познати движења"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Прикажи ажурир. површина"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Осветли површ. на прозорци при нивно ажурирање"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Покажувај ажурирања на приказот"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полна батерија"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полна батерија"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – полнењето е паузирано за да се заштити батеријата"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – се полни"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Ќе се наполни целосно до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ќе се наполни целосно до <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и потсетници"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволете ѝ на апликацијава да поставува аларми и да закажува дејства со временски рокови. Ова овозможува апликацијата да работи во заднина и така може повеќе да ја троши батеријата.\n\nАко дозволава е исклучена, нема да функционираат постојните аларми и настаните според време закажани од апликацијава."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"закажување, аларм, потсетник, часовник"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не вознемирувај"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Вклучи"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Исклучување на „Не вознемирувај“"</string>
@@ -604,7 +607,7 @@
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
     <string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
-    <string name="storage_category" msgid="2287342585424631813">"Капацитет"</string>
+    <string name="storage_category" msgid="2287342585424631813">"Простор"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"Споделени податоци"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"Прегледајте и изменете ги споделените податоци"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Нема споделени податоци за корисников."</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 5fa8961..0b6d3fb 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ടാപ്പുകൾക്ക് ദൃശ്യ ഫീഡ്ബാക്ക് കാണിക്കുക"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"കീ അമർത്തലുകൾ കാണിക്കുക"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ഫിസിക്കൽ കീ അമർത്തൽ വിഷ്വൽ ഫീഡ്‌ബാക്ക് കാണിക്കൂ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ടച്ച്‌പാഡ് ഇൻപുട്ട് കാണിക്കുക"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ടച്ച്‌പാഡ് ഇൻപുട്ട് ഡാറ്റയും അംഗീകൃത ജെസ്‌ച്ചറുകളും പ്രദർശിപ്പിക്കുന്ന സ്‌ക്രീൻ ഓവർലേ"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"സർഫേസ് അപ്‌ഡേറ്റ് കാണിക്കൂ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"അപ്‍ഡേറ്റ് പൂർത്തിയാകുമ്പോൾ മുഴുവൻ വിൻഡോ സർഫേസുകളും ഫ്ലാഷ് ചെയ്യുക"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'അപ്‌ഡേറ്റുകൾ കാണുക\' കാണിക്കുക"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"പൂർണ്ണമാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമാകാൻ <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി പരിരക്ഷിക്കാൻ ചാർജിംഗ് ഹോൾഡിലാണ്"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ചാർജ് ചെയ്യുന്നു"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>-നകം പൂർണ്ണമാകും"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-നകം പൂർണ്ണമായി ചാർജ് ചെയ്യും"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"അലാറങ്ങളും റിമെെൻഡറുകളും"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"അലാറങ്ങൾ സജ്ജീകരിക്കാനും സമയപ്രാധാന്യമുള്ള പ്രവർത്തനങ്ങൾ ഷെഡ്യൂൾ ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കുക. പശ്ചാത്തലത്തിൽ റൺ ചെയ്യാൻ ഇത് ഈ ആപ്പിന് അനുവാദം നൽകുന്നു, ഇതിന് കൂടുതൽ ബാറ്ററി ഉപയോഗിച്ചേക്കാം.\n\nഈ അനുമതി ഓഫാണെങ്കിൽ, ഈ ആപ്പ് നിലവിൽ ഷെഡ്യൂൾ ചെയ്‌ത അലാറങ്ങളും സമയാധിഷ്‌ഠിത ഇവന്റുകളും പ്രവർത്തിക്കില്ല."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ഷെഡ്യൂൾ, അലാറം, റിമെെൻഡർ, ക്ലോക്ക്"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ശല്യപ്പെടുത്തരുത്"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ഓണാക്കുക"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ശല്യപ്പെടുത്തരുത്\' ഓണാക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 388ea6f..1ce8b0f 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -47,7 +47,7 @@
     <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Хувийн"</string>
     <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Хувийн"</string>
     <string name="wifi_security_none_owe" msgid="5241745828327404101">"Хоосон/Сайжруулсан нээлт"</string>
-    <string name="wifi_security_owe" msgid="3343421403561657809">"Сайжруулсан нээлт"</string>
+    <string name="wifi_security_owe" msgid="3343421403561657809">"Сайжруулсан нээлттэй"</string>
     <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Байгууллага 192-бит"</string>
     <string name="wifi_remembered" msgid="3266709779723179188">"Хадгалагдсан"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"Салсан"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Товшилтын визуал хариу үйлдлийг харуулах"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Түлхүүрийн даралт харуул"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Биет түлхүүрийн даралтын визуал хариу үйлдлийг харуул"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Мэдрэгч самбарын оролтыг харуулах"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Дэлгэцэд давхарлах нь мэдрэгч самбарын оролтын өгөгдөл болон танигдсан зангааг үзүүлж байна"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Гадаргын шинэчлэлтүүдийг харуулах"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Шинэчлэгдэх үед цонхны гадаргыг бүхэлд нь анивчуулах"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Шинэчлэлт харахыг харуулах"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Дүүрэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - дүүрэх хүртэл <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг хамгаалахын тулд цэнэглэхийг хүлээлгэсэн"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Цэнэглэж байна"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> гэхэд дүүрнэ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> гэхэд бүрэн цэнэглэгдэнэ"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Сэрүүлэг, сануулагч"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Энэ аппад сэрүүлэг тавих болон хугацаанд мэдрэг үйлдлийн хуваарь гаргахыг зөвшөөрнө үү. Энэ нь аппад ард ажиллахыг зөвшөөрөх бөгөөд ингэснээр илүү их батарей ашиглаж магадгүй.\n\nХэрэв энэ зөвшөөрөл унтраалттай бол энэ аппын аль хэдийн тавьсан сэрүүлэг болон хуваарь гаргасан хугацаанд мэдрэг үйл явдал ажиллахгүй."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"хуваарь, сэрүүлэг, сануулагч, цаг"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Бүү саад бол"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Асаах"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Бүү саад бол горимыг асаах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 063b7d1..ad67ef2 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -47,7 +47,7 @@
     <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-वैयक्तिक"</string>
     <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-वैयक्तिक"</string>
     <string name="wifi_security_none_owe" msgid="5241745828327404101">"कोणतीही नाही/एन्हान्स्ड ओपन"</string>
-    <string name="wifi_security_owe" msgid="3343421403561657809">"एन्हान्स्ड ओपन"</string>
+    <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
     <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-एंटरप्राइझ 192-बिट"</string>
     <string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"टॅपसाठी व्हिज्युअल फीडबॅक दाखवा"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"की प्रेस दाखवा"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"प्रत्यक्ष की प्रेससाठी व्हिज्युअल फीडबॅक दाखवा"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"टचपॅड इनपुट दाखवा"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"टचपॅड इनपुट डेटा आणि ओळखीची जेश्चर दाखवणारी स्‍क्रीन ओव्‍हरले"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट दाखवा"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"संपूर्ण विंडो सर्फेस अपडेट होतात तेव्हा ते फ्‍लॅश करा"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"व्‍ह्यू अपडेट दाखवा"</string>
@@ -495,11 +497,10 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूर्ण चार्ज होण्यासाठी <xliff:g id="TIME">%1$s</xliff:g> शिल्लक आहेत"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> या वेळेत बॅटरी पूर्ण चार्ज होईल"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरीचे संरक्षण करण्यासाठी चार्जिंग थांबवले आहे"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ चार्ज होत आहे"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> पर्यंत पूर्ण होईल"</string>
-    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्णपणे चार्ज होईल"</string>
+    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरी <xliff:g id="TIME">%2$s</xliff:g> या वेळेत पूर्ण चार्ज होईल"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्णपणे चार्ज होईल"</string>
     <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण होईल"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म आणि रिमाइंडर"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"या ॲपला अलार्म सेट करण्याची किंवा वेळेनुसार संवेदनशील असलेल्या कृती शेड्युल करण्याची अनुमती द्या. हे ॲपला बॅकग्राउंडमध्ये रन होऊ देते, ज्यामुळे जास्त बॅटरी वापरली जाऊ शकते.\n\nही परवानगी बंद असल्यास, सध्याचे अलार्म आणि या ॲपद्वारे शेड्युल केलेले वेळेवर आधारित इव्हेंट काम करणार नाहीत."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्युल, अलार्म, रिमाइंडर, घड्याळ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"व्यत्यय आणू नका"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 48ed79d..5bb3ba0 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -47,7 +47,7 @@
     <string name="wifi_security_sae" msgid="3644520541721422843">"WPA3-Personal"</string>
     <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
     <string name="wifi_security_none_owe" msgid="5241745828327404101">"Keselamatan OWE Tiada/Dipertingkat"</string>
-    <string name="wifi_security_owe" msgid="3343421403561657809">"Keselamatan OWE Dipertingkat"</string>
+    <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
     <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-bit"</string>
     <string name="wifi_remembered" msgid="3266709779723179188">"Disimpan"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"Diputuskan sambungan"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Tunjukkan maklum balas visual untuk ketikan"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Tunjukkan tekanan kekunci"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Tunjukkan maklum balas visual untuk tekanan kekunci fizikal"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Tunjukkan input pad sentuh"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Tindanan skrin memaparkan data input pad sentuh dan gerak isyarat yang dikenali"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Tunjuk kemaskinian permukaan"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Denyar permukaan tetingkap apabila dikemas kini"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Tunjuk kemaskinian paparan"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> lagi sebelum penuh"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi sebelum penuh"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pengecasan ditunda untuk melindungi bateri"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mengecas"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Penuh pada <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Dicas sepenuhnya pada <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Penggera &amp; peringatan"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Benarkan apl ini menetapkan penggera dan menjadualkan tindakan yang sensitif masa. Hal ini membolehkan apl berjalan di latar, yang mungkin menggunakan lebih banyak bateri.\n\nJika kebenaran ini dimatikan, penggera sedia ada dan acara berdasarkan masa yang dijadualkan oleh apl ini tidak akan berfungsi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadual, penggera, peringatan, jam"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Jangan Ganggu"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Hidupkan"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Hidupkan Jangan Ganggu"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index d15a0be..7cce3d9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"တို့ခြင်းများအတွက် အမြင်ဖြင့် တုံ့ပြန်မှုပြသည်"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"ကီးနှိပ်မှုများ ပြပါ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ပကတိကီးနှိပ်မှုများအတွက် ရုပ်မြင်အကြံပြုချက် ပြပါ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"တာ့ချ်ပက် ထည့်သွင်းချက် ပြပါ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ဖန်သားပြင်တွင် ထပ်ဆင့်ပြသခြင်းသည် တာ့ချ်ပက်တွင် ထည့်သွင်းသော ဒေတာနှင့် သိရှိသော လက်ဟန်များကို ပြထားသည်"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"မျက်နှာပြင်အပ်ဒိတ်ပြရန်"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"အပ်ဒိတ်လုပ်စဉ် ဝင်းဒိုးမျက်နှာပြင်တွင် အချက်ပြရန်"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"မြင်ကွင်းအပ်ဒိတ်များ ပြခြင်း"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုသည်"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> လိုသည်"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီကာကွယ်ရန် အားသွင်းခြင်းကို ခဏရပ်ထားသည်"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားသွင်းနေသည်"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> တွင် အားပြည့်မည်"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> တွင် အားပြည့်မည်"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"နှိုးစက်နှင့် သတိပေးချက်များ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"နှိုးစက်သတ်မှတ်ရန်နှင့် အချိန်တိကျရန် လိုအပ်သည့် လုပ်ဆောင်ချက်များအတွက် အစီအစဉ်ဆွဲရန် ဤအက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက အက်ပ်ကို နောက်ခံတွင် လုပ်ဆောင်ခွင့်ပေးပြီး ဘက်ထရီပိုသုံးနိုင်သည်။\n\nဤခွင့်ပြုချက်ကို ပိတ်ထားပါက ဤအက်ပ်ဖြင့် အစီအစဉ်ဆွဲထားသော လက်ရှိနှိုးစက်နှင့် အချိန်သတ်မှတ်ထားသည့် အစီအစဉ်များ အလုပ်လုပ်တော့မည် မဟုတ်ပါ။"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"အချိန်ဇယား၊ နှိုးစက်၊ သတိပေးချက်၊ နာရီ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"မနှောင့်ယှက်ရ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ဖွင့်ရန်"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'မနှောင့်ယှက်ရ\' ဖွင့်ခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 8f5663a..c6fbea1 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Vis visuell tilbakemelding for trykk"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Vis tastetrykk"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Vis visuell tilbakemelding for fysiske tastetrykk"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Vis inndata fra styreflaten"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Skjermoverlegget viser inndata fra styreflaten og gjenkjente bevegelser"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Vis overflateoppdateringer"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fremhev hele vindusoverflater når de oppdateres"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Vis visningsoppdateringer"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Fulladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Ladingen er satt på vent for å beskytte batteriet"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – lader"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Fulladet innen <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Fulladet innen <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmer og påminnelser"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Gi denne appen tillatelse til å angi alarmer og planlegge tidssensitive handlinger. Dette gir appen tillatelse til å kjøre i bakgrunnen, noe som kan bruke mer batteri.\n\nHvis denne tillatelsen er av, fungerer ikke eksisterende alarmer og tidsbaserte hendelser som er planlagt av denne appen."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"tidsplan, alarm, påminnelse, klokke"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ikke forstyrr"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Slå på"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Slå på Ikke forstyrr"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index ddb58d5..5d18fd3 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ट्यापका लागि भिजुअल प्रतिक्रिया देखाउनुहोस्"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"थिचिएका कीहरू देखाइयून्"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"थिचिएका भौतिक कीसम्बन्धी भिजुअल प्रतिक्रिया देखाउनुहोस्"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"टचप्याड इन्पुट देखाउनुहोस्"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"स्क्रिनको ओभरलेमा टचप्याड इन्पुटसम्बन्धी डेटा र पहिचान गरिएका जेस्चरहरू देखाइँदै छन्"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट देखाउनुहोस्"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट हुँदा विन्डोका पूरै सतहमा देखाउनुहोस्"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"GPU भ्युको अपडेट देखाउनुहोस्"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"पूरा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> लाग्ने छ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ब्याट्री जोगाउन चार्जिङ होल्ड गरिएको छ"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्ज गरिँदै छ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> बजेसम्ममा पूरा चार्ज हुने छ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> बजेसम्ममा पूरा चार्ज हुने छ"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म तथा रिमाइन्डर"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"बाधा नपुऱ्याउनुहोस्"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"अन गर्नुहोस्"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई अन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index abf00f0..e457376 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Toon visuele feedback voor tikken"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Toetsaanslagen tonen"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Visuele feedback tonen voor fysieke toetsaanslagen"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Touchpadinvoer tonen"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Schermoverlay die gegevens van touchpadinvoer en herkende gebaren weergeeft"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Oppervlakupdates tonen"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash volledige vensteroppervlakken bij updates"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Weergave-updates tonen"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Vol over <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - vol over <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>: opladen is in de wacht gezet om de batterij te beschermen"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Opladen"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Vol om <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Volledig opgeladen om <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wekkers en herinneringen"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Sta toe dat deze app wekkers zet en tijdgevoelige acties plant. De app kan hierdoor op de achtergrond worden uitgevoerd, waardoor je misschien meer batterijlading verbruikt.\n\nAls dit recht uitstaat, werken door deze app geplande bestaande wekkers en tijdgebaseerde afspraken niet."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plannen, schema, wekker, alarm, herinnering, klok"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Niet storen"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aanzetten"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zet Niet storen aan."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 941a627..2f25cde5 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -48,7 +48,7 @@
     <string name="wifi_security_psk_sae" msgid="8135104122179904684">"WPA2/WPA3-Personal"</string>
     <string name="wifi_security_none_owe" msgid="5241745828327404101">"କିଛି ନାହିଁ/Enhanced Open"</string>
     <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string>
-    <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-ବିଟ୍"</string>
+    <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-Enterprise 192-ବିଟ"</string>
     <string name="wifi_remembered" msgid="3266709779723179188">"ସେଭ୍‌ ହୋଇଗଲା"</string>
     <string name="wifi_disconnected" msgid="7054450256284661757">"ବିଛିନ୍ନ କରାଯାଇଛି"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"ଅକ୍ଷମ ହୋଇଛି"</string>
@@ -112,13 +112,13 @@
     <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">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ)। ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ଡାହାଣ: <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">"କନେକ୍ଟ କରାଯାଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ)। ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ଡାହାଣ: <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>
+    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"କନେକ୍ଟ କରାଯାଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସପୋର୍ଟ କରେ)। ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ।"</string>
     <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"କନେକ୍ଟ କରାଯାଇଛି (ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ)"</string>
-    <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ଅଡିଓ ସେୟାରିଂକୁ ସମର୍ଥନ କରେ"</string>
+    <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ଅଡିଓ ସେୟାରିଂକୁ ସପୋର୍ଟ କରେ"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), କେବଳ ବାମ"</string>
     <string name="bluetooth_hearing_aid_media_only_right_active" msgid="3854140683042617230">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), କେବଳ ଡାହାଣ"</string>
     <string name="bluetooth_hearing_aid_media_only_left_and_right_active" msgid="1299913413062528417">"ସକ୍ରିୟ (କେବଳ ମିଡିଆ), ବାମ ଏବଂ ଡାହାଣ"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ଟାପ୍ ପାଇଁ ଭିଜୁଆଲ୍ ମତାମତ ଦେଖାନ୍ତୁ"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"ଦବାଯାଇଥିବା କୀ ଦେଖାନ୍ତୁ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ଦବାଯାଇଥିବା ଫିଜିକାଲ କୀ ପାଇଁ ଭିଜୁଆଲ ମତାମତ ଦେଖାନ୍ତୁ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ଟଚପେଡ ଇନପୁଟ ଦେଖାନ୍ତୁ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ଟଚପେଡ ଇନପୁଟ ଡାଟା ଏବଂ ଚିହ୍ନଟ କରାଯାଇଥିବା ଜେଶ୍ଚରକୁ ଡିସପ୍ଲେ କରୁଥିବା ସ୍କ୍ରିନ ଓଭରଲେ"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"ସର୍ଫେସ୍‌ ଅପଡେଟ୍‌ ଦେଖାନ୍ତୁ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ସମଗ୍ର ୱିଣ୍ଡୋ ପୃଷ୍ଠ ଅପଡେଟ୍‌ ହେବା ବେଳେ ସେଗୁଡ଼ିକ ଫ୍ଲାସ୍‌ କରନ୍ତୁ"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"ଭ୍ୟୁ ଅପଡେଟ୍‌ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପୂର୍ଣ୍ଣ ହେବାକୁ ଆଉ <xliff:g id="TIME">%2$s</xliff:g> ବାକି ଅଛି"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବେଟେରୀକୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ ଚାର୍ଜିଂ ହୋଲ୍ଡରେ ଅଛି"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ ଚାର୍ଜିଂ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ସୁଦ୍ଧା ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • <xliff:g id="TIME">%2$s</xliff:g> ସୁଦ୍ଧା ସମ୍ପୂର୍ଣ୍ଣ ଭାବେ ଚାର୍ଜ ହୋଇଯିବ"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ଏହି ଆପକୁ ଆଲାରାମ୍ ସେଟ୍ କରିବାକୁ ଏବଂ ସମୟ-ସମ୍ବେଦନଶୀଳ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଏହା ଆପକୁ ପୃଷ୍ଠପଟରେ ଚାଲିବାକୁ ଦେଇଥାଏ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ।\n\nଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଅଛି, ତେବେ ଏହି ଆପ୍ ଦ୍ୱାରା ସିଡୁଲ୍ କରାଯାଇଥିବା ପୂର୍ବରୁ ଥିବା ଆଲାରାମ୍ ଏବଂ ସମୟ-ଆଧାରିତ ଇଭେଣ୍ଟଗୁଡ଼ିକ କାମ କରିବ ନାହିଁ।"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 09fd778..b13ef1b 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"ਟੈਪਾਂ ਲਈ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਤੀਕਰਮ ਦਿਖਾਓ"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"ਕੁੰਜੀ ਦਬਾਉਣ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਦਿਖਾਓ"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"ਭੌਤਿਕ ਕੁੰਜੀ ਦਬਾਉਣ ਸੰਬੰਧੀ ਦ੍ਰਿਸ਼ਟੀਗਤ ਵਿਚਾਰ ਦਿਖਾਓ"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ਟੱਚਪੈਡ ਇਨਪੁੱਟ ਦਿਖਾਓ"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ਸਕ੍ਰੀਨ ਓਵਰਲੇ ਵਿੱਚ ਟੱਚਪੈਡ ਇਨਪੁੱਟ ਡਾਟਾ ਅਤੇ ਪਛਾਣੇ ਗਏ ਇਸ਼ਾਰੇ ਦਿਖਾਈ ਦੇ ਰਹੇ ਹਨ"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"ਸਰਫ਼ੇਸ ਅੱਪਡੇਟ ਦਿਖਾਓ"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"ਅੱਪਡੇਟ ਹੋਣ \'ਤੇ, ਸਮੁੱਚੀਆਂ ਵਿੰਡੋ ਸਰਫ਼ੇਸਾਂ ਫਲੈਸ਼ ਕਰੋ"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'ਅੱਪਡੇਟ ਦੇਖੋ\' ਨੂੰ ਦਿਖਾਓ"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਪੂਰੀ ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸੁਰੱਖਿਆ ਲਈ ਚਾਰਜਿੰਗ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> ਤੱਕ ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚੱਲਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7840cb0..3f90774 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Pokazuj potwierdzenie wizualne po dotknięciu"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Wyświetl naciśnięcia klawiszy"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Pokaż wizualną informację zwrotną po naciśnięciu fizycznego klawisza"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Pokazuj ruch na touchpadzie"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Nakładka ekranowa pokazująca dane wprowadzane na touchpadzie i rozpoznane gesty"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Pokazuj zmiany powierzchni"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Podświetlaj całe aktualizowane powierzchnie okien"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Pokazuj aktualizacje widoku"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – wstrzymano ładowanie, aby chronić baterię"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – ładowanie"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Bateria będzie pełna do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pełne naładowanie do <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmy i przypomnienia"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Zezwalaj tej aplikacji na ustawianie alarmów i planowanie działań, w przypadku których czas jest istotny. Aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tego uprawnienia, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"harmonogram, alarm, przypomnienie, zegar"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nie przeszkadzać"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Włącz"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Włącz tryb Nie przeszkadzać"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 9209d62..00375b0 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas pressionadas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual de teclas pressionadas"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada do touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Sobreposição de tela mostrando dados de entrada do touchpad e gestos reconhecidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Piscar superfícies de toda a janela ao atualizar"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de exibição"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 2cd6811..97d2848 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostrar toques em teclas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual (toques em teclas físicas)"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada do touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Sobreposição de ecrã a mostrar dados de entrada do touchpad e gestos reconhecidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Destacar a superfície da janela ao atualizar"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de vistas"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até à carga máxima"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até à carga máxima"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento em espera para proteger a bateria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – A carregar"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Completo à(s) <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Completamente carregado à(s) <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permita que a app defina alarmes e agende ações com um horário específico. Esta ação permite que a app seja executada em segundo plano, o que pode usar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"agendar, alarme, lembrete, relógio"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não incomodar"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o modo Não incomodar"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 9209d62..00375b0 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas pressionadas"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual de teclas pressionadas"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Mostrar entrada do touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Sobreposição de tela mostrando dados de entrada do touchpad e gestos reconhecidos"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Piscar superfícies de toda a janela ao atualizar"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de exibição"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> até a conclusão"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> até a conclusão"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carregamento suspenso para proteger a bateria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (carregando)"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Carregado até <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Totalmente carregado até <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes e lembretes"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarme, lembrete, relógio"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Não perturbe"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ativar"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ativar o Não perturbe"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index e33c02f..900851e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Afișează feedbackul vizual pentru atingeri"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Arată apăsările pe taste"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Arată feedback vizual pentru apăsările pe taste"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Afișează intrarea de la touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Suprapunere pe ecran care afișează datele de intrare de la touchpad și gesturile recunoscute"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Actualizări suprafețe"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Iluminarea întregii fereastre la actualizare"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Afiș. actualizări ecran"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> până la finalizare"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la finalizare"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Încărcarea s-a întrerupt pentru a proteja bateria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Se încarcă"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Timp rămas <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Complet încărcat în <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarme și mementouri"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programare, alarmă, memento, ceas"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Nu deranja"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activează"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activează Nu deranja"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 4de495a..db4be66 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Визуальный отклик при нажатии"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Показывать нажатия клавиш"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Показывать визуальный отклик при нажатии клавиш"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Показать входные данные сенсорной панели"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Экранный оверлей, на котором изображены входные данные сенсорной панели и распознанные жесты"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Показ. обнов. поверхности"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Подсвечивать поверхности окон при обновлении"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Показывать обнов. экрана"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g>, зарядка приостановлена для защиты батареи"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряжается"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g>. <xliff:g id="STATUS">%2$s</xliff:g> – завершится к <xliff:g id="TIME">%3$s</xliff:g>."</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – полностью зарядится к <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники и напоминания"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Вы можете разрешить этому приложению устанавливать будильники и планировать запуск действий в определенное время. В этом случае оно будет работать в фоновом режиме и быстрее расходовать заряд батареи.\n\nЕсли отключить это разрешение, текущие будильники и созданные приложением события перестанут запускаться."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"установить, будильник, напоминание, часы"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не беспокоить"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Включить"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Включите режим \"Не беспокоить\""</string>
@@ -624,7 +627,7 @@
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"Профиль с огр. доступом"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"Добавить пользователя?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"Если этим устройством пользуются сразу несколько человек, для каждого из них можно создать профиль – отдельное пространство с выбранными приложениями, обоями и т. д. Новый пользователь настраивает его сам.\n\nИз профиля можно поменять и общие настройки устройства, например сеть Wi-Fi.\n\nОбновлять общие приложения могут все, однако специальные возможности настраиваются индивидуально."</string>
-    <string name="user_add_user_message_short" msgid="3295959985795716166">"После создания профиля его потребуется настроить.\n\nЛюбой пользователь устройства может обновлять приложения для всех аккаунтов."</string>
+    <string name="user_add_user_message_short" msgid="3295959985795716166">"Новому пользователю потребуется настроить свой профиль.\n\nЛюбой пользователь может обновлять приложения для всех остальных."</string>
     <string name="user_grant_admin_title" msgid="5157031020083343984">"Назначить этого пользователя администратором?"</string>
     <string name="user_grant_admin_message" msgid="1673791931033486709">"У администраторов права шире, чем у других пользователей. Администратор может управлять аккаунтами всех пользователей, обновлять ПО на устройстве, менять и сбрасывать его настройки, просматривать установленные приложения, а также предоставлять и отзывать права администратора."</string>
     <string name="user_grant_admin_button" msgid="5441486731331725756">"Назначить администратором"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 9a4ca8e..d9df5eb 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"තට්ටු කිරීම් සඳහා දෘශ්‍ය ප්‍රතිපෝෂණ පෙන්වන්න"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"යතුරු එබීම් පෙන්වන්න"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"භෞතික යතුරු එබීම සඳහා දෘශ්‍ය ප්‍රතිපෝෂණය පෙන්වන්න"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ස්පර්ශ පුවරු ආදානය පෙන්වන්න"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"ස්පර්ශ පුවරු ආදාන දත්ත සහ හඳුනාගත් අභිනයන් පෙන්වන තිර උඩැතිරිය"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"පෘෂ්ඨ යාවත්කාලීන පෙන්වන්න"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"යාවත්කාලින වනවිට මුළු කවුළු තලයම දැල්වෙන්න"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"නැරඹීම් යාවත්කාලීන පෙන්වන්න"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"සම්පූර්ණ වීමට <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරියි"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - සම්පූර්ණ වීමට <xliff:g id="TIME">%2$s</xliff:g>ක් ඉතිරියි"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය ආරක්ෂා කිරීම සඳහා ආරෝපණය රඳවා තබා ඇත"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වේ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> හට පෙර සම්පූර්ණයි"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> හට පෙර සම්පූර්ණයෙන් ආරෝපණ වෙයි"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"එලාම සහ සිහිකැඳවීම්"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"එලාම සැකසීමට සහ කාල සංවේදී ක්‍රියා කාලසටහන්ගත කිරීමට මෙම යෙදුමට ඉඩ දෙන්න. මෙය පසුබිමේ ධාවනය වීමට යෙදුමට ඉඩ දෙයි, එය වැඩි බැටරිය වැඩියෙන් භාවිත කළ හැකිය.\n\nමෙම අවසරය ක්‍රියාවිරහිත නම්, මෙම යෙදුම මඟින් සැලසුම් කර ඇති තිබෙන එලාම සහ වේලාව පදනම් කර ගත් සිදුවීම් ක්‍රියා නොකරනු ඇත."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"කාල සටහන, එලාමය, සිහිකැඳවීම, ඔරලෝසුව"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"බාධා නොකරන්න"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ක්‍රියාත්මක කරන්න"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"බාධා නොකරන්න ක්‍රියාත්මක කරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 189e729..c690a19 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Vizuálne znázorňovať klepnutia"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Zobrazovať stlač. kláves."</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Zobr. vizuálnu spätnú väzbu fyz. stlač. klávesov"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Zobrazenie vstupu touchpadu"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Prekrývajúci prvok zobrazujúci údaje vstupu touchpadu a rozpoznané gestá"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Ukazovať obnovenia obsahu"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Rozblikať obsah okna pri aktualizácii"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ukazovať obnovenia zobrazenia"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> do úplného nabitia"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíjanie je pozastavené, aby sa chránila batéria"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – nabíja sa"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – čas do nabitia: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – čas do úplného nabitia: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Budíky a pripomenutia"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciou nebudú fungovať."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"plán, budík, pripomenutie, hodiny"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Režim bez vyrušení"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Zapnúť"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Zapnite režim bez vyrušení"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index a7367ad..7fa5ed1 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -112,7 +112,7 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivno (levo in desno)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktivno (samo predstavnost). Baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktivno (samo predstavnost), baterija – L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Povezano (podpira deljenje zvoka), baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Povezano (podpira deljenje zvoka), baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Povezano (podpira deljenje zvoka), baterija – L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Povezano (podpira deljenje zvoka). Levo – baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Povezano (podpira deljenje zvoka). Desno – baterija: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Prikaži vizualne povratne informacije za dotike."</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Pokaži pritiske tipk"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Pokaži vizualne povratne informacije za pritiske fizičnih tipk"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Pokaži vnos prek sledilne ploščice"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Prekrivanje zaslona, na katerem so prikazani podatki vnosa prek sledilne ploščice in prepoznane poteze"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Pokaži posodob. površine"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ob posodobitvi osveži celotne površine oken."</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži posodob. pogleda"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Še <xliff:g id="TIME">%1$s</xliff:g> do napolnjenosti"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – še <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Zaradi zaščite baterije je polnjenje na čakanju"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – polnjenje"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – popolnoma napolnjena do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – popolnoma napolnjena do <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi in opomniki"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tej aplikaciji dovolite nastavljanje alarmov in načrtovanje časovno občutljivih dejanj. S tem aplikaciji omogočite izvajanje v ozadju, kar bo morda povečalo porabo energije baterije.\n\nČe je to dovoljenje izklopljeno, obstoječi alarmi in časovno občutljivi dogodki, ki jih nastavi ta aplikacija, ne bodo delovali."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"načrtovanje, urnik, alarm, opomnik, ura"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ne moti"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vklopi"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vklop načina »Ne moti«"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 688a098..b64c8fd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Shfaq reagimet vizuale për trokitjet"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Shfaq shtypjet e tasteve"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Shfaq reagim vizual për shtypjet fizike të tasteve"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Shfaq hyrjen me bllokun me prekje"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Mbivendosja e ekranit që paraqet hyrjen me bllokun me prekje dhe gjestet e njohura"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Shfaq përditësimet e sipërfaqes"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ndriço të gjitha sipërfaqet e dritares kur ato të përditësohen"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Shfaq përditësimet e pamjes"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> derisa të mbushet"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi është vendosur në pritje për të mbrojtur baterinë"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Po karikohet"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Plot deri në <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikohet plotësisht deri në <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo mundëson që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet në bazë kohore të planifikuara nga ky aplikacion nuk do të funksionojnë."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Mos shqetëso"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 17f5c2a..f9001a8 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Приказује визуелне повратне информације за додире"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Приказуј притиске тастера"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Приказује повратне информације за притиске тастера"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Прикажи унос на тачпеду"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Елемент који прекрива садржај екрана приказује податке из уноса на тачпеду и препознаје покрете"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Прикажи ажурирања површине"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Осветљава све површине прозора када се ажурирају"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Прикажи ажурирања приказа"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до краја пуњења"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до краја пуњења"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – пуњење је на чекању да би се заштитила батерија"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Пуњење"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Потпуно напуњено до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Потпуно напуњено до <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Аларми и подсетници"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Омогућите овој апликацији да подешава аларме и заказује временски осетљиве радње. То омогућава да апликација буде покренута у позадини, што може да троши више батерије.\n\nАко је ова дозвола искључена, постојећи аларми и догађаји засновани на времену заказани помоћу ове апликације неће радити."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"заказати, аларм, подсетник, сат"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не узнемиравај"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Укључи"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Укључите режим Не узнемиравај"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c50d3c3..4ca4835 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -235,7 +235,7 @@
     <item msgid="6946761421234586000">"400 %"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Personlig"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Privat"</string>
     <string name="category_work" msgid="4014193632325996115">"Jobb"</string>
     <string name="category_private" msgid="4244892185452788977">"Privat"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klon"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Visa visuell feedback för tryck"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Visa tangenttryckningar"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Visa visuell feedback för tangenttryckningar"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Visa indata från styrplattan"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Skärmöverlagring som visar indata från styrplattan och identifierade rörelser"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Visa ytuppdateringar"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Hela fönstret blinkar vid uppdatering"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Visa visningsuppdatering"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> kvar tills fulladdat"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kvar tills fulladdat"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Laddningen har pausats för att skydda batteriet"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – laddas"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – fulladdad till <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladdad till <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm och påminnelser"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Tillåt att den här appen ställer in alarm och schemalägger tidskänsliga åtgärder. Om du tillåter detta kan appen köras i bakgrunden, vilket kan dra mer batteri.\n\nOm behörigheten är inaktiverad fungerar inte befintliga alarm och tidsbaserade händelser som schemalagts av den här appen."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"schema, alarm, påminnelse, klocka"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Stör ej"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivera"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivera Stör ej."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 0c71dd1..3198d0a 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -94,13 +94,13 @@
     <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Imeunganishwa (hamna simu), kiasi cha chaji ni <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">"Imeunganishwa (hamna kifaa cha sauti), kiasi cha chaji ni <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">"Imeunganishwa (hamna simu au kifaa cha sauti), kiasi cha chaji ni <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">"Inatumika. Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_active_battery_level" msgid="2685517576209066008">"Inatumika. Chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Inatumika. Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"Inatumika. Kushoto: chaji <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"Inatumika. Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Kushoto: chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Kushoto <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -112,11 +112,11 @@
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Inatumika (kushoto na kulia)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Inatumika (maudhui pekee). Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Inatumika (maudhui pekee), Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Chaji imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kushoto: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja). Kulia: chaji ya betri imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
-    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Imeunganishwa (inaweza kutumia kipengele cha kusikiliza pamoja)"</string>
+    <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Imeunganishwa (inaweza kusikiliza pamoja)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"Inatumika (maudhui pekee)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"Inaweza kutumia kipengele cha kusikiliza pamoja"</string>
     <string name="bluetooth_hearing_aid_media_only_left_active" msgid="1632152540901488645">"Inatumika (maudhui pekee), kushoto pekee"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Onyesha ishara za kuthibitisha unapogusa"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Onyesha mibofyo ya vitufe"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Onyesha muhtasari wa mibofyo halisi ya vitufe"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Onyesha uingizaji wa data kupitia padi ya kugusa"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Programu inayowekelewa juu ya nyingine inayoonyesha data iliyoingizwa kupitia padi ya kugusa na miguso inayotambulika"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Onyesha masasisho ya sehemu"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Angaza dirisha lote zitakaposasisha"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Onyesha taarifa za kuonekana"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> zimesalia ijae chaji"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Imesitisha kuchaji ili kulinda betri"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Inachaji"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Itajaa kufikia <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri itajaa chaji kufikia <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Kengele na vikumbusho"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Ruhusu programu hii iweke kengele na ratiba za vitendo vingine vinavyotegemea wakati. Hatua hii inairuhusu programu itumike chinichini, hali inayoweza kutumia chaji nyingi ya betri.\n\nIkiwa ruhusa hii itazimwa, kengele zilizopo na ratiba za vitendo vinavyotegemea wakati zilizowekwa na programu hii hazitafanya kazi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ratiba, kengele, kikumbusho, saa"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Usinisumbue"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Washa"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Washa kipengele cha Usinisumbue"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 2908b63..108ddce 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"தட்டல்களின் போது காட்சி அறிகுறிகளைக் காட்டும்"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"பட்டன் அழுத்தத்தை காட்டு"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"பட்டன் அழுத்தங்களைக் காட்சி மூலம் உறுதிப்படுத்தும்"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"டச்பேட் உள்ளீட்டைக் காட்டுதல்"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"டச்பேட் உள்ளீட்டுத் தரவையும் அங்கீகரிக்கப்பட்ட சைகைகளையும் திரை மேலடுக்குக் காட்டும்"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"மேலோட்ட புதுப்பிப்புகளைக் காட்டு"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"சாளரத்தின் பரப்புநிலைகள் புதுப்பிக்கப்படும்போது, அவற்றை முழுவதுமாகக் காட்டும்"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"வியூ அப்டேட்ஸைக் காட்டு"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"முழுவதும் சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழுவதும் சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - பேட்டரியைப் பாதுகாப்பதற்காகச் சார்ஜ் ஏற்றுவது நிறுத்தப்பட்டுள்ளது"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ சார்ஜாகிறது"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>க்கு முழுமையாகச் சார்ஜாகிவிடும்"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>க்கு முழுமையாகச் சார்ஜாகிவிடும்"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"அலாரங்கள் &amp; நினைவூட்டல்கள்"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"அலாரங்களை அமைக்கவும் குறிப்பிட்ட கால இடைவெளியில் செயல்களைத் திட்டமிடவும் இந்த ஆப்ஸை அனுமதிக்கும். இது ஆப்ஸ் பின்னணியில் இயங்குவதை அனுமதிக்கும், இதற்காக அதிக பேட்டரியைப் பயன்படுத்தக்கூடும்.\n\nஇந்த அனுமதி முடக்கப்பட்டிருந்தால் இந்த ஆப்ஸ் மூலம் திட்டமிடப்பட்ட ஏற்கெனவே அமைத்த அலாரங்களும் நேர அடிப்படையிலான நிகழ்வுகளும் வேலை செய்யாது."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"திட்டமிடல், அலாரம், நினைவூட்டல், கடிகாரம்"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"தொந்தரவு செய்யாதே"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ஆன் செய்"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"தொந்தரவு செய்ய வேண்டாம் என்பதை ஆன் செய்யும்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c188bdb..4811594 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -100,7 +100,7 @@
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"యాక్టివ్‌గా ఉంది. కుడి వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ."</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"బ్యాటరీ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ఎడమ వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, కుడివైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> బ్యాటరీ."</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ఎడమ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, కుడి: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> బ్యాటరీ."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ఎడమ వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"కుడి వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ఎడమ వైపు <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -115,7 +115,7 @@
     <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">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్‌కు సపోర్ట్ చేస్తుంది). ఎడమ వైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> బ్యాటరీ, కుడివైపు: <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>
+    <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"కనెక్ట్ అయింది (ఆడియో షేరింగ్‌కు సపోర్ట్ చేస్తుంది). కుడివైపు: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> బ్యాటరీ."</string>
     <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"కనెక్ట్ చేయబడింది (ఆడియో షేరింగ్‌కు సపోర్ట్ చేస్తుంది)"</string>
     <string name="bluetooth_active_media_only_no_battery_level" msgid="71106861912593126">"యాక్టివ్ (మీడియా మాత్రమే)"</string>
     <string name="bluetooth_saved_device_lea_support" msgid="7231323139968285768">"ఆడియో షేరింగ్‌కు సపోర్ట్ చేస్తుంది"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"నొక్కినప్పుడు దృశ్యపరమైన ప్రతిస్పందన చూపు"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"కీ ప్రెస్‌లను చూడండి"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"భౌతిక కీ ప్రెస్‌ల విజువల్ ఫీడ్‌బ్యాక్‌ను చూడండి"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"టచ్‌ప్యాడ్ ఇన్‌పుట్‌ను చూపండి"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"స్క్రీన్ ఓవర్‌లే టచ్‌ప్యాడ్ ఇన్‌పుట్ డేటాను, గుర్తించబడిన సంజ్ఞలను డిస్‌ప్లే చేస్తోంది"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"సర్ఫేస్‌ అప్‌డేట్లను చూపు"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"విండో సర్‌ఫేస్‌లన్నీ అప్‌డేట్‌ అయితే ఫ్లాష్ చేయి"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"వీక్షణ అప్‌డేట్‌లను చూపు"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీని రక్షించడానికి ఛార్జింగ్ హోల్డ్‌లో ఉంచబడింది"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవుతోంది"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g>కు పూర్తవుతుంది"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>కు పూర్తిగా ఛార్జ్ అవుతుంది"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు &amp; రిమైండర్‌లు"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, టైమ్-సెన్సిటివ్ చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్‌ను అనుమతించండి. ఇది యాప్‌ను బ్యాక్‌గ్రౌండ్‌లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసినటువంటి ఇప్పటికే ఉన్న అలారాలు, టైమ్-ఆధారిత ఈవెంట్‌లు పనిచేయవు."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"అంతరాయం కలిగించవద్దు"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b9510a8..b332154 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -95,12 +95,12 @@
     <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_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" msgid="4961338936672922617">"ใช้งานอยู่ แบตเตอรี่ข้างซ้าย: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ข้างขวา: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered_left" msgid="2895644748625343977">"ใช้งานอยู่ L: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered_right" msgid="7407517998880370179">"ใช้งานอยู่ R: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"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" msgid="1616774716076301755">"แบตเตอรี่ข้างซ้าย: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ข้างขวา: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ซ้าย: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ขวา: แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ฝั่งซ้าย <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -111,9 +111,9 @@
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"ใช้งานอยู่ (เฉพาะข้างขวา)"</string>
     <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_active_media_only_battery_level_untethered" msgid="7444753133664620926">"ใช้งานอยู่ (สื่อเท่านั้น) แบตเตอรี่ข้างซ้าย: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ข้างขวา: <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_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_lea_support" msgid="803110681688633362">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง) แบตเตอรี่ข้างซ้าย: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ข้างขวา: <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>
     <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"เชื่อมต่อแล้ว (รองรับการแชร์เสียง)"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"แสดงผลตอบสนองแบบภาพเมื่อแตะ"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"แสดงการกดแป้น"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"แสดงฟีดแบ็กที่เป็นภาพสำหรับการกดแป้นพิมพ์"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"แสดงอินพุตของทัชแพด"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"การซ้อนทับบนหน้าจอที่แสดงข้อมูลอินพุตของทัชแพดและท่าทางสัมผัสที่จดจำได้"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"แสดงการอัปเดตพื้นผิว"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"กะพริบหน้าต่างทั้งหมดเมื่อมีการอัปเดต"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"แสดงการอัปเดตมุมมอง"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - หยุดการชาร์จชั่วคราวเพื่อถนอมแบตเตอรี่"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ กำลังชาร์จ"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - จะเต็มภายใน <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - จะชาร์จเต็มภายใน <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"การปลุกและการช่วยเตือน"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"อนุญาตให้แอปนี้ตั้งปลุกและกำหนดเวลาการดำเนินการที่ต้องคำนึงถึงเวลาเป็นสำคัญ สิทธิ์นี้ช่วยให้แอปทำงานในเบื้องหลังได้ จึงอาจทำให้ใช้แบตเตอรี่มากขึ้น\n\nหากปิดใช้สิทธิ์นี้ การปลุกที่มีอยู่และกิจกรรมที่ต้องคำนึงถึงเวลาเป็นสำคัญซึ่งแอปนี้กำหนดเวลาไว้จะไม่ทำงาน"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"กำหนดเวลา การปลุก การช่วยเตือน นาฬิกา"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ห้ามรบกวน"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"เปิด"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"เปิด \"ห้ามรบกวน\""</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 32cc961..cf88980 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Ipakita ang visual na feedback para sa mga pag-tap"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Ipakita ang mga key press"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Ipakita: visual feedback ng mga physical key press"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Ipakita ang input ng touchpad"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Screen overlay na nagpapakita ng data ng input ng touchpad at mga natukoy na galaw"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Ipakita update sa surface"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"I-flash ang buong window surface kapag nag-update"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ipakita update ng view"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> na lang bago mapuno"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> na lang bago mapuno"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Naka-hold ang pag-charge para protektahan ang baterya"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Nagcha-charge"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Mapupuno sa <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Mafu-full charge sa <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Mga alarm at paalala"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Payagan ang app na ito na magtakda ng mga alarm at mag-iskedyul ng mga pagkilos na may limitadong oras. Papayagan nitong tumakbo ang app sa background, na posibleng gumamit ng mas maraming baterya.\n\nKung naka-off ang pahintulot na ito, hindi gagana ang mga kasalukuyang alarm at event na nakabatay sa oras na naiskedyul ng app na ito."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"iskedyul, alarm, paalala, orasan"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Huwag Istorbohin"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"I-on"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"I-on ang Huwag Istorbohin"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 6bfc1ee..6cdfa2e 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -111,9 +111,9 @@
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Etkin (yalnızca sağ taraf)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Etkin (sol ve sağ taraf)"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Etkin (yalnızca medya). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
-    <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Etkin (yalnızca medya), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi."</string>
-    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Bağlı (ses paylaşımını destekler), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
-    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Bağlı (ses paylaşımını destekler), Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi."</string>
+    <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Etkin (yalnızca medya). Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi."</string>
+    <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Bağlı (ses paylaşımını destekler). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
+    <string name="bluetooth_battery_level_untethered_lea_support" msgid="803110681688633362">"Bağlı (ses paylaşımını destekler). Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi."</string>
     <string name="bluetooth_battery_level_untethered_left_lea_support" msgid="7707464334346454950">"Bağlı (ses paylaşımını destekler). Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
     <string name="bluetooth_battery_level_untethered_right_lea_support" msgid="8941549024377771038">"Bağlı (ses paylaşımını destekler). Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
     <string name="bluetooth_no_battery_level_lea_support" msgid="5721725041048434075">"Bağlı (ses paylaşımını destekler)"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Dokunmalarda görsel geri bildirim göster"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Basılan tuşları göster"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Fiziksel tuşlara basınca görsel geri bildirim ver"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Dokunmatik alan girişini göster"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Dokunmatik alan girişi verilerini ve tanınan hareketleri gösteren ekran yer paylaşımı"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Yüzey güncellemelerini göster"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Güncellenirken tüm pencere yüzeylerini yakıp söndür"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Görünüm güncellemelerini göster"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Tamamen şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pili korumak için şarj beklemede"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmlar ve hatırlatıcılar"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"program, alarm, hatırlatıcı, saat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Rahatsız Etmeyin"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aç"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Rahatsız Etmeyin\'i açın"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 083a130..7abd4a8 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -95,12 +95,12 @@
     <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> під’єднано (без медіа), заряд акумулятора – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> під’єднано (без телефона й медіа), заряд акумулятора – <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">"Активне з’єднання. Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора."</string>
+    <string name="bluetooth_active_battery_level_untethered" msgid="4961338936672922617">"Активне з’єднання. Рівень заряду: лівий <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий <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>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
     <string name="tv_bluetooth_battery_level" msgid="8786353985605532846">"Заряд акумулятора: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора."</string>
+    <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Рівень заряду: лівий <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
     <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Ліва частина: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -111,7 +111,7 @@
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Активовано (лише правий)"</string>
     <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">"Активне з’єднання (лише для мультимедіа). Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора"</string>
+    <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Активне з’єднання (лише для мультимедіа). Рівень заряду: лівий <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <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_untethered_lea_support" msgid="803110681688633362">"Підключено (підтримує надсилання аудіо). Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <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>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Показувати візуальну реакцію на торкання"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Показувати натискання клавіш"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Показувати візуальний відгук на натискання клавіш"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Показувати дані, що вводяться на сенсорній панелі"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Дані, що вводяться на сенсорній панелі, і розпізнані жести відображатимуться поверх інших вікон"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Показ. оновлення поверхні"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Підсвічувати вікна повністю під час оновлення"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Показувати оновлення областей"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджання призупинено, щоб захистити акумулятор"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – заряджається"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Завершиться до <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Повністю зарядиться до <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будильники й нагадування"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дозволити цьому додатку налаштовувати будильники й створювати розклад дій. Додаток зможе працювати у фоновому режимі й використовувати більше заряду акумулятора.\n\nЯкщо вимкнути такий дозвіл, наявні будильники й дії, створені цим додатком, не працюватимуть."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"запланувати, будильник, нагадування, годинник"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Не турбувати"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Увімкнути"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Увімкнути режим \"Не турбувати\""</string>
@@ -604,7 +607,7 @@
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
     <string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
-    <string name="storage_category" msgid="2287342585424631813">"Пам\'ять"</string>
+    <string name="storage_category" msgid="2287342585424631813">"Сховище"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"Спільні дані"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"Переглянути й змінити спільні дані"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Немає спільних даних для цього користувача."</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 6508d51..bf7ac58 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"تھپتھپاہٹوں کیلئے بصری تاثرات دکھائیں"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"\'کلید کو دبانا\' دکھائیں"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"\'طبعی کلید کو دبانا\' کیلئے ویژوئل تاثرات دکھائیں"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"ٹچ پیڈ ان پٹ دکھائیں"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"اسکرین اوورلے ٹچ پیڈ ان پٹ ڈیٹا اور تسلیم شدہ اشاروں کو ظاہر کرتا ہے"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"سطح کے اپ ڈیٹس دکھائیں"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"اپ ڈیٹ ہونے پر ونڈو کی پوری سطحیں جھلملائیں"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"منظر کے اپ ڈیٹس دکھائیں"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"مکمل چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"مکمل چارج ہونے میں <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری کی حفاظت کرنے کے لیے چارجنگ ہولڈ پر ہے"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - چارج ہو رہی ہے"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> تک مکمل ہو جائے گی"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تک مکمل چارج ہو جائے گی"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"الارمز اور یاد دہانیاں"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"اس ایپ کو الارمز سیٹ کرنے اور وقت کے لحاظ سے حساس کارروائیوں کو شیڈول کرنے کی اجازت دیں۔ اس سے ایپ کو پس منظر میں چلنے کی اجازت ملتی ہے، جس میں زیادہ بیٹری استعمال ہو سکتی ہے۔\n\n اگر یہ اجازت آف ہے تو موجودہ الارمز اور اس ایپ کے ذریعے شیڈول کردہ وقت پر مبنی ایونٹس کام نہیں کریں گے۔"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"شیڈول، الارم، یاد دہانی، گھڑی"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"ڈسٹرب نہ کریں"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"آن کریں"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ڈسٹرب نہ کریں\' کو آن کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 738c815..2418053 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Ekranda bosilgan joylardagi nuqtalarni ko‘rsatish"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Tugma bosishlarini chiqarish"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Jismoniy tugmani bosishlar uchun vizual fikr-mulohazani chiqarish"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Sensorli panel axborotini chiqarish"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ekran ustida sensorli panelda kiritilgan axborotlar va tanilgan ishoralar chiqadi"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Yuza yangilanishlarini ko‘rsatish"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Yangilangandan so‘ng to‘liq oyna sirtlarini miltillatish"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Yangilash oynasini ochish"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"Toʻlishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – Toʻlishiga <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareyani himoyalash uchun quvvatlash toʻxtatildi"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quvvatlanmoqda"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Toʻladi: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Toʻliq quvvatlanadi: <xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Signal va eslatmalar"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu ilovaga signal oʻrnatish va vaqtga asoslangan amallarni rejalashtirishga ruxsat berish. Bunda ilovaga orqa fonda ishlashiga imkon beriladi, shu sababli batareya ortiqcha sarflanishi mumkin.\n\nAgar bu ruxsat oʻchirilsa, ushbu ilova tomonidan rejalashtirilgan mavjud signallar va vaqtga asoslangan tadbirlar ishlamaydi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"reja, signal, eslatma, soat"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Bezovta qilinmasin"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Yoqish"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bezovta qilinmasin rejimini yoqing"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 38b4e4f..15839b8 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -239,7 +239,7 @@
     <string name="category_work" msgid="4014193632325996115">"Công việc"</string>
     <string name="category_private" msgid="4244892185452788977">"Riêng tư"</string>
     <string name="category_clone" msgid="1554511758987195974">"Nhân bản"</string>
-    <string name="development_settings_title" msgid="140296922921597393">"Tùy chọn cho nhà phát triển"</string>
+    <string name="development_settings_title" msgid="140296922921597393">"Tuỳ chọn cho nhà phát triển"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Bật tùy chọn nhà phát triển"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Đặt tùy chọn cho phát triển ứng dụng"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"Tùy chọn dành cho nhà phát triển không khả dụng cho người dùng này"</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Hiển thị phản hồi trực quan cho các lần nhấn"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Hiện thao tác nhấn phím"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Hiện phản hồi trực quan cho thao tác nhấn phím vật lý"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Hiện phương thức nhập bằng bàn di chuột"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Lớp phủ màn hình hiển thị dữ liệu nhập bằng bàn di chuột và các cử chỉ được nhận dạng"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Hiện bản cập nhật giao diện"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Chuyển nhanh toàn bộ các giao diện cửa sổ khi các giao diện này cập nhật"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Hiện bản cập nhật chế độ xem"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> nữa là pin đầy"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang tạm ngưng sạc để bảo vệ pin"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang sạc"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Đến <xliff:g id="TIME">%3$s</xliff:g> pin sẽ đầy"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đến <xliff:g id="TIME">%2$s</xliff:g> pin sẽ đầy"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Chuông báo và lời nhắc"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Cho phép ứng dụng này đặt chuông báo và lên lịch các hành động cần chính xác về thời gian. Tùy chọn này cho phép ứng dụng chạy ở chế độ nền và có thể làm tiêu hao nhiều pin.\n\nNếu không cấp quyền này, các chuông báo và sự kiện theo thời gian do ứng dụng này lên lịch sẽ không hoạt động."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"lịch biểu, chuông báo, lời nhắc, đồng hồ"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Không làm phiền"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Bật"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Bật chế độ Không làm phiền"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 21cd458..a2fd4ea 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -81,7 +81,7 @@
     <string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
     <string name="speed_label_very_fast" msgid="8215718029533182439">"很快"</string>
     <string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string>
-    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
+    <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"已断开连接"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"正在断开连接..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"正在连接..."</string>
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"显示点按操作的视觉反馈"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"显示按键操作"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"显示实体按键操作的视觉反馈"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"显示触控板输入"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"一个屏幕叠加层,显示了触控板输入数据和已识别的手势"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"显示面 (surface) 更新"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"窗口中的面 (surface) 更新时全部闪烁"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"显示视图更新"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - 为保护电池,已暂停充电"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在充电"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> 前充满"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 前充满"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"勿扰模式"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 8ad35e6..bcf624e4 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"顯示輕按位置的視覺回應"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"顯示按鍵操作"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"顯示實際按鍵操作的視覺回應"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"顯示觸控板輸入內容"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"重疊式畫面正在顯示觸控板輸入的資料和已辨識的手勢"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"顯示表層更新"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新表層時閃動整個視窗表層"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"顯示畫面更新"</string>
@@ -493,10 +495,9 @@
     <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"剩餘電量時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"剩餘電量時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
-    <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充滿電"</string>
+    <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後完成充電"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充滿電"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - 在 <xliff:g id="TIME">%3$s</xliff:g>前充滿電"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • 在 <xliff:g id="TIME">%2$s</xliff:g>前充滿電"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許此應用程式設定鬧鐘及安排具時效性的操作。這讓應用程式在背景中執行,因此可能會較耗電。\n\n如果關閉此權限,此應用程式將不會在預定時間響起已設定的鬧鐘,亦不會就特定時間的活動傳送通知。"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"日程表, 鬧鐘, 提醒, 時鐘"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"請勿騷擾"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「請勿騷擾」模式"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 8265bba..58a19793 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"顯示觸控位置的視覺回應"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"顯示按鍵操作"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"顯示實際按鍵操作的視覺回饋"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"顯示觸控板輸入內容"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"在畫面重疊顯示觸控板輸入資料和辨識出的手勢"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"顯示表層更新"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新表層時閃爍顯示整個視窗表層"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"顯示畫面更新"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g>後充飽"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - 為保護電池,目前暫停充電"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電中"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> 前充飽"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 前充飽"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"鬧鐘和提醒"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允許這個應用程式設定鬧鐘及安排有時效性的動作。之後應用程式可以在背景執行,並可能耗用較多電量。\n\n如果關閉這項權限,這個應用程式設定的現有鬧鐘將不會響起,系統也無法在預定的時間發出活動提醒。"</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"時間表, 鬧鐘, 提醒, 時鐘"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"零打擾"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"開啟"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"開啟「零打擾」模式"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index b2f46b5..28b9b05 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -380,6 +380,8 @@
     <string name="show_touches_summary" msgid="3692861665994502193">"Bonisa izmpendulo ebukekayo ngamathebhu"</string>
     <string name="show_key_presses" msgid="6360141722735900214">"Bonisa ukucindezela ukhiye"</string>
     <string name="show_key_presses_summary" msgid="725387457373015024">"Bonisa impendulo ebonakalayo yokucindezela ukhiye obonakalayo"</string>
+    <string name="touchpad_visualizer" msgid="3707916068870825115">"Bonisa okokufaka kwephedi yokuthinta"</string>
+    <string name="touchpad_visualizer_summary" msgid="4056080018119847449">"Ukumbozwa kwesikrini okubonisa idatha yokufaka yephedi yokuthinta kanye nokuthinta okubonwayo"</string>
     <string name="show_screen_updates" msgid="2078782895825535494">"Buka izibuyekezo ezibonakalayo"</string>
     <string name="show_screen_updates_summary" msgid="2126932969682087406">"Khanyisa ukubonakala kwalo lonke iwindi uma libuyekezwa"</string>
     <string name="show_hw_screen_updates" msgid="2021286231267747506">"Bonisa izibuyekezo zokubuka"</string>
@@ -495,8 +497,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"<xliff:g id="TIME">%1$s</xliff:g> okusele kuze kugcwale"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> okusele kuze kugcwale"</string>
-    <!-- no translation found for power_charging_limited (4144004473976005214) -->
-    <skip />
+    <string name="power_charging_limited" msgid="4144004473976005214">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ukushaja kumisiwe ukuze kuvikelwe ibhethri"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"Iku-<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Iyashaja"</string>
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Igcwala ngo-<xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • Ishajwe ngokugcwele ngo-<xliff:g id="TIME">%2$s</xliff:g>"</string>
@@ -563,6 +564,8 @@
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ama-alamu nezikhumbuzi"</string>
     <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Vumela le app isethe ama-alamu futhi ushejule izenzo zesikhathi esizwelayo. Lokhu kuvumela i-app iqhubeke ngemuva okungasebenzisa ibhethri lakho eliningi.\n\nUma le mvume ivaliwe, ama-alamu asele nemicimbi esekelwe esikhathini ehlelwe yile app ngeke kusebenze."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ishejuli, i-alamu, isikhumbuzi, iwashi"</string>
+    <!-- no translation found for zen_mode_do_not_disturb_name (6798711401734798283) -->
+    <skip />
     <string name="zen_mode_settings_title" msgid="7374070457626419755">"Ungaphazamisi"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Vula"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Vula ukungaphazamisi"</string>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 68b81db..3c3de04 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -31,4 +31,14 @@
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
+
+    <!-- Edit User avatar explicit package name -->
+    <string name="config_avatar_picker_package" translatable="false">
+        com.android.avatarpicker
+    </string>
+
+    <!-- Edit User avatar explicit activity class -->
+    <string name="config_avatar_picker_class" translatable="false">
+        com.android.avatarpicker.ui.AvatarPickerActivity
+    </string>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 687c728..4d771c0 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1364,6 +1364,9 @@
     <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
     <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, clock</string>
 
+    <!-- Priority Modes: Name of the "manual" Do Not Disturb mode. [CHAR LIMIT=50] -->
+    <string name="zen_mode_do_not_disturb_name">Do Not Disturb</string>
+
     <!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
     <string name="zen_mode_settings_title">Do Not Disturb</string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index b02b0c4..1e58335 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -495,7 +495,7 @@
                 || packageName.equals(sServicesSystemSharedLibPackageName)
                 || packageName.equals(sSharedSystemSharedLibPackageName)
                 || packageName.equals(PrintManager.PRINT_SPOOLER_PACKAGE_NAME)
-                || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName()))
+                || (updateServiceV2() && packageName.equals(getDefaultWebViewPackageName(pm)))
                 || isDeviceProvisioningPackage(resources, packageName);
     }
 
@@ -511,7 +511,7 @@
 
     /** Fetch the package name of the default WebView provider. */
     @Nullable
-    private static String getDefaultWebViewPackageName() {
+    private static String getDefaultWebViewPackageName(PackageManager pm) {
         if (sDefaultWebViewPackageName != null) {
             return sDefaultWebViewPackageName;
         }
@@ -519,9 +519,10 @@
         WebViewProviderInfo provider = null;
 
         if (android.webkit.Flags.updateServiceIpcWrapper()) {
-            WebViewUpdateManager manager = WebViewUpdateManager.getInstance();
-            if (manager != null) {
-                provider = manager.getDefaultWebViewPackage();
+            if (pm.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
+                // WebViewUpdateManager.getInstance() will not return null on devices with
+                // FEATURE_WEBVIEW.
+                provider = WebViewUpdateManager.getInstance().getDefaultWebViewPackage();
             }
         } else {
             try {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index de60fdc2..616ab07 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1,5 +1,6 @@
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
 import static com.android.settingslib.widget.AdaptiveOutlineDrawable.ICON_TYPE_ADVANCED;
 
 import android.annotation.SuppressLint;
@@ -21,6 +22,8 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.provider.DeviceConfig;
@@ -41,11 +44,17 @@
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.settingslib.widget.AdaptiveOutlineDrawable;
 
+import com.google.common.collect.ImmutableSet;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 public class BluetoothUtils {
     private static final String TAG = "BluetoothUtils";
@@ -57,6 +66,9 @@
     public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled";
     private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
     private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
+    private static final Set<Integer> SA_PROFILES =
+            ImmutableSet.of(
+                    BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
 
     private static ErrorListener sErrorListener;
 
@@ -92,6 +104,22 @@
     /**
      * @param context to access resources from
      * @param cachedDevice to get class from
+     * @return pair containing the drawable and the description of the type of the device. The type
+     *     could either derived from metadata or CoD.
+     */
+    public static Pair<Drawable, String> getDerivedBtClassDrawableWithDescription(
+            Context context, CachedBluetoothDevice cachedDevice) {
+        return BluetoothUtils.isAdvancedUntetheredDevice(cachedDevice.getDevice())
+                ? new Pair<>(
+                        getBluetoothDrawable(
+                                context, com.android.internal.R.drawable.ic_bt_headphones_a2dp),
+                        context.getString(R.string.bluetooth_talkback_headphone))
+                : BluetoothUtils.getBtClassDrawableWithDescription(context, cachedDevice);
+    }
+
+    /**
+     * @param context to access resources from
+     * @param cachedDevice to get class from
      * @return pair containing the drawable and the description of the Bluetooth class of the
      *     device.
      */
@@ -553,6 +581,68 @@
         return isFilterMatched;
     }
 
+    /**
+     * Checks if a given `CachedBluetoothDevice` is available for audio sharing and being switch as
+     * active media device.
+     *
+     * <p>This method determines if the device meets the following criteria to be available:
+     *
+     * <ol>
+     *   <li>Audio sharing session is off.
+     *   <li>The device is one of the two connected devices on the LE Broadcast Assistant profile.
+     *   <li>The device is not currently active on the LE Audio profile.
+     *   <li>There is exactly one other device that is active on the LE Audio profile.
+     * </ol>
+     *
+     * @param cachedDevice The `CachedBluetoothDevice` to check.
+     * @param localBluetoothManager The `LocalBluetoothManager` instance, or null if unavailable.
+     * @return `true` if the device is available for audio sharing and settings as active, `false`
+     *     otherwise.
+     */
+    @WorkerThread
+    public static boolean isAvailableAudioSharingMediaBluetoothDevice(
+            CachedBluetoothDevice cachedDevice,
+            @Nullable LocalBluetoothManager localBluetoothManager) {
+        LocalBluetoothLeBroadcastAssistant assistantProfile =
+                Optional.ofNullable(localBluetoothManager)
+                        .map(LocalBluetoothManager::getProfileManager)
+                        .map(LocalBluetoothProfileManager::getLeAudioBroadcastAssistantProfile)
+                        .orElse(null);
+        LeAudioProfile leAudioProfile =
+                Optional.ofNullable(localBluetoothManager)
+                        .map(LocalBluetoothManager::getProfileManager)
+                        .map(LocalBluetoothProfileManager::getLeAudioProfile)
+                        .orElse(null);
+        CachedBluetoothDeviceManager deviceManager =
+                Optional.ofNullable(localBluetoothManager)
+                        .map(LocalBluetoothManager::getCachedDeviceManager)
+                        .orElse(null);
+        // If any of the profiles are null, or broadcast is already on, return false
+        if (assistantProfile == null
+                || leAudioProfile == null
+                || deviceManager == null
+                || isBroadcasting(localBluetoothManager)) {
+            return false;
+        }
+        Set<Integer> connectedGroupIds =
+                assistantProfile.getAllConnectedDevices().stream()
+                        .map(deviceManager::findDevice)
+                        .filter(Objects::nonNull)
+                        .map(CachedBluetoothDevice::getGroupId)
+                        .collect(Collectors.toSet());
+        Set<Integer> activeGroupIds =
+                leAudioProfile.getActiveDevices().stream()
+                        .map(deviceManager::findDevice)
+                        .filter(Objects::nonNull)
+                        .map(CachedBluetoothDevice::getGroupId)
+                        .collect(Collectors.toSet());
+        int groupId = cachedDevice.getGroupId();
+        return activeGroupIds.size() == 1
+                && !activeGroupIds.contains(groupId)
+                && connectedGroupIds.size() == 2
+                && connectedGroupIds.contains(groupId);
+    }
+
     /** Returns if the le audio sharing is enabled. */
     public static boolean isAudioSharingEnabled() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -631,12 +721,48 @@
         return !sourceList.isEmpty() && sourceList.stream().anyMatch(BluetoothUtils::isConnected);
     }
 
+    /**
+     * Check if {@link BluetoothDevice} has a active local broadcast source.
+     *
+     * @param device The bluetooth device to check.
+     * @param localBtManager The BT manager to provide BT functions.
+     * @return Whether the device has a active local broadcast source.
+     */
+    @WorkerThread
+    public static boolean hasActiveLocalBroadcastSourceForBtDevice(
+            @Nullable BluetoothDevice device, @Nullable LocalBluetoothManager localBtManager) {
+        LocalBluetoothLeBroadcastAssistant assistant =
+                localBtManager == null
+                        ? null
+                        : localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
+        LocalBluetoothLeBroadcast broadcast =
+                localBtManager == null
+                        ? null
+                        : localBtManager.getProfileManager().getLeAudioBroadcastProfile();
+        if (device == null || assistant == null || broadcast == null) {
+            Log.d(TAG, "Skip check hasActiveLocalBroadcastSourceForBtDevice due to arg is null");
+            return false;
+        }
+        List<BluetoothLeBroadcastReceiveState> sourceList = assistant.getAllSources(device);
+        int broadcastId = broadcast.getLatestBroadcastId();
+        return !sourceList.isEmpty()
+                && broadcastId != UNKNOWN_VALUE_PLACEHOLDER
+                && sourceList.stream().anyMatch(source -> isSourceMatched(source, broadcastId));
+    }
+
     /** Checks the connectivity status based on the provided broadcast receive state. */
     @WorkerThread
     public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
         return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0);
     }
 
+    /** Checks if the broadcast id is matched based on the provided broadcast receive state. */
+    @WorkerThread
+    public static boolean isSourceMatched(
+            @Nullable BluetoothLeBroadcastReceiveState state, int broadcastId) {
+        return state != null && state.getBroadcastId() == broadcastId;
+    }
+
     /**
      * Checks if the Bluetooth device is an available hearing device, which means: 1) currently
      * connected 2) is Hearing Aid 3) connected profile match hearing aid related profiles (e.g.
@@ -895,4 +1021,61 @@
         }
         return null;
     }
+
+    /**
+     * Gets {@link AudioDeviceAttributes} of bluetooth device for spatial audio. Returns null if
+     * it's not an audio device(no A2DP, LE Audio and Hearing Aid profile).
+     */
+    @Nullable
+    public static AudioDeviceAttributes getAudioDeviceAttributesForSpatialAudio(
+            CachedBluetoothDevice cachedDevice,
+            @AudioManager.AudioDeviceCategory int audioDeviceCategory) {
+        AudioDeviceAttributes saDevice = null;
+        for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) {
+            // pick first enabled profile that is compatible with spatial audio
+            if (SA_PROFILES.contains(profile.getProfileId())
+                    && profile.isEnabled(cachedDevice.getDevice())) {
+                switch (profile.getProfileId()) {
+                    case BluetoothProfile.A2DP:
+                        saDevice =
+                                new AudioDeviceAttributes(
+                                        AudioDeviceAttributes.ROLE_OUTPUT,
+                                        AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+                                        cachedDevice.getAddress());
+                        break;
+                    case BluetoothProfile.LE_AUDIO:
+                        if (audioDeviceCategory == AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER) {
+                            saDevice =
+                                    new AudioDeviceAttributes(
+                                            AudioDeviceAttributes.ROLE_OUTPUT,
+                                            AudioDeviceInfo.TYPE_BLE_SPEAKER,
+                                            cachedDevice.getAddress());
+                        } else {
+                            saDevice =
+                                    new AudioDeviceAttributes(
+                                            AudioDeviceAttributes.ROLE_OUTPUT,
+                                            AudioDeviceInfo.TYPE_BLE_HEADSET,
+                                            cachedDevice.getAddress());
+                        }
+
+                        break;
+                    case BluetoothProfile.HEARING_AID:
+                        saDevice =
+                                new AudioDeviceAttributes(
+                                        AudioDeviceAttributes.ROLE_OUTPUT,
+                                        AudioDeviceInfo.TYPE_HEARING_AID,
+                                        cachedDevice.getAddress());
+                        break;
+                    default:
+                        Log.i(
+                                TAG,
+                                "unrecognized profile for spatial audio: "
+                                        + profile.getProfileId());
+                        break;
+                }
+                break;
+            }
+        }
+        return saDevice;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 7124ed2..c6eb9fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -178,7 +178,7 @@
         }
         log("updateRelationshipOfGroupDevices: mCachedDevices list =" + mCachedDevices.toString());
 
-        // Get the preferred main device by getPreferredMainDeviceWithoutConectionState
+        // Get the preferred main device by getPreferredMainDeviceWithoutConnectionState
         List<CachedBluetoothDevice> groupDevicesList = getGroupDevicesFromAllOfDevicesList(groupId);
         CachedBluetoothDevice preferredMainDevice =
                 getPreferredMainDevice(groupId, groupDevicesList);
@@ -373,6 +373,7 @@
                 preferredMainDevice.addMemberDevice(deviceItem);
                 mCachedDevices.remove(deviceItem);
                 mBtManager.getEventManager().dispatchDeviceRemoved(deviceItem);
+                preferredMainDevice.refresh();
                 hasChanged = true;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 27fcdbe..6f2567b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -80,6 +80,7 @@
             "com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE";
     public static final String EXTRA_LE_AUDIO_SHARING_STATE = "BLUETOOTH_LE_AUDIO_SHARING_STATE";
     public static final String EXTRA_BLUETOOTH_DEVICE = "BLUETOOTH_DEVICE";
+    public static final String EXTRA_START_LE_AUDIO_SHARING = "START_LE_AUDIO_SHARING";
     public static final int BROADCAST_STATE_UNKNOWN = 0;
     public static final int BROADCAST_STATE_ON = 1;
     public static final int BROADCAST_STATE_OFF = 2;
@@ -100,7 +101,7 @@
     private static final int DEFAULT_CODE_MIN = 1000;
     // Order of this profile in device profiles list
     private static final int ORDINAL = 1;
-    private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
+    static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
     private static final Uri[] SETTINGS_URIS =
             new Uri[] {
                 Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_NAME),
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
index 91a99ae..24815fa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcastAssistantCallbackExt.kt
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.bluetooth
 
+import android.bluetooth.BluetoothAdapter
 import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothLeBroadcastAssistant
 import android.bluetooth.BluetoothLeBroadcastMetadata
@@ -81,5 +82,9 @@
             ConcurrentUtils.DIRECT_EXECUTOR,
             callback,
         )
-        awaitClose { unregisterServiceCallBack(callback) }
+        awaitClose {
+            if (BluetoothAdapter.getDefaultAdapter()?.isEnabled == true) {
+                unregisterServiceCallBack(callback)
+            }
+        }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java
new file mode 100644
index 0000000..e84a5d2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreference.java
@@ -0,0 +1,134 @@
+/*
+ * 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.bluetooth.devicesettings;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/** A data class representing a footer preference. */
+public class DeviceSettingFooterPreference extends DeviceSettingPreference implements Parcelable {
+
+    private final String mFooterText;
+    private final Bundle mExtras;
+
+    DeviceSettingFooterPreference(
+            @NonNull String footerText,
+            Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_FOOTER);
+        mFooterText = footerText;
+        mExtras = extras;
+    }
+
+    /** Read a {@link DeviceSettingFooterPreference} from {@link Parcel}. */
+    @NonNull
+    public static DeviceSettingFooterPreference readFromParcel(@NonNull Parcel in) {
+        String footerText = in.readString();
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new DeviceSettingFooterPreference(footerText, extras);
+    }
+
+    public static final Creator<DeviceSettingFooterPreference> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public DeviceSettingFooterPreference createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public DeviceSettingFooterPreference[] newArray(int size) {
+                    return new DeviceSettingFooterPreference[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(mFooterText);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link DeviceSettingFooterPreference}. */
+    public static final class Builder {
+        private String mFooterText = "";
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the footer text of the preference.
+         *
+         * @param footerText The footer text of the preference.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public DeviceSettingFooterPreference.Builder setFooterText(@NonNull String footerText) {
+            mFooterText = footerText;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public DeviceSettingFooterPreference.Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link DeviceSettingFooterPreference} object.
+         *
+         * @return Returns the built {@link DeviceSettingFooterPreference} object.
+         */
+        @NonNull
+        public DeviceSettingFooterPreference build() {
+            return new DeviceSettingFooterPreference(
+                    mFooterText, mExtras);
+        }
+    }
+
+    /**
+     * Gets the footer text of the preference.
+     *
+     * @return The footer text.
+     */
+    @NonNull
+    public String getFooterText() {
+        return mFooterText;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java
new file mode 100644
index 0000000..953e7cb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreference.java
@@ -0,0 +1,132 @@
+/*
+ * 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.bluetooth.devicesettings;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/** A data class representing a help button displayed on the top right corner of the page. */
+public class DeviceSettingHelpPreference extends DeviceSettingPreference implements Parcelable {
+
+    private final Intent mIntent;
+    private final Bundle mExtras;
+
+    DeviceSettingHelpPreference(@NonNull Intent intent, Bundle extras) {
+        super(DeviceSettingType.DEVICE_SETTING_TYPE_HELP);
+        mIntent = intent;
+        mExtras = extras;
+    }
+
+    /** Read a {@link DeviceSettingHelpPreference} from {@link Parcel}. */
+    @NonNull
+    public static DeviceSettingHelpPreference readFromParcel(@NonNull Parcel in) {
+        Intent intent = in.readParcelable(Intent.class.getClassLoader());
+        Bundle extras = in.readBundle(Bundle.class.getClassLoader());
+        return new DeviceSettingHelpPreference(intent, extras);
+    }
+
+    public static final Creator<DeviceSettingHelpPreference> CREATOR =
+            new Creator<>() {
+                @Override
+                @NonNull
+                public DeviceSettingHelpPreference createFromParcel(@NonNull Parcel in) {
+                    in.readInt();
+                    return readFromParcel(in);
+                }
+
+                @Override
+                @NonNull
+                public DeviceSettingHelpPreference[] newArray(int size) {
+                    return new DeviceSettingHelpPreference[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeParcelable(mIntent, flags);
+        dest.writeBundle(mExtras);
+    }
+
+    /** Builder class for {@link DeviceSettingHelpPreference}. */
+    public static final class Builder {
+        private Intent mIntent;
+        private Bundle mExtras = Bundle.EMPTY;
+
+        /**
+         * Sets the intent of the preference, should be an activity intent.
+         *
+         * @param intent The intent to launch when clicked.
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public DeviceSettingHelpPreference.Builder setIntent(@NonNull Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Sets the extras bundle.
+         *
+         * @return Returns the Builder object.
+         */
+        @NonNull
+        public DeviceSettingHelpPreference.Builder setExtras(@NonNull Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link DeviceSettingHelpPreference} object.
+         *
+         * @return Returns the built {@link DeviceSettingHelpPreference} object.
+         */
+        @NonNull
+        public DeviceSettingHelpPreference build() {
+            return new DeviceSettingHelpPreference(mIntent, mExtras);
+        }
+    }
+
+    /**
+     * Gets the intent to launch when clicked.
+     *
+     * @return The intent.
+     */
+    @NonNull
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Gets the extras Bundle.
+     *
+     * @return Returns a Bundle object.
+     */
+    @NonNull
+    public Bundle getExtras() {
+        return mExtras;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
index 20a0339..58dc8c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingId.java
@@ -108,6 +108,12 @@
     /** Device setting ID for device details footer. */
     int DEVICE_SETTING_ID_DEVICE_DETAILS_FOOTER = 19;
 
+    /** Device setting ID for spatial audio group. */
+    int DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE = 20;
+
+    /** Device setting ID for "More Settings" page. */
+    int DEVICE_SETTING_ID_MORE_SETTINGS = 21;
+
     /** Device setting ID for ANC. */
     int DEVICE_SETTING_ID_ANC = 1001;
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
index 9ee33b0..a0fe5d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingItem.kt
@@ -27,6 +27,7 @@
  * @property packageName The package name for service binding.
  * @property className The class name for service binding.
  * @property intentAction The intent action for service binding.
+ * @property preferenceKey The preference key if it's a built-in preference.
  * @property extras Extra bundle
  */
 data class DeviceSettingItem(
@@ -34,6 +35,7 @@
     val packageName: String,
     val className: String,
     val intentAction: String,
+    val preferenceKey: String? = null,
     val extras: Bundle = Bundle.EMPTY,
 ) : Parcelable {
 
@@ -45,6 +47,7 @@
             writeString(packageName)
             writeString(className)
             writeString(intentAction)
+            writeString(preferenceKey)
             writeBundle(extras)
         }
     }
@@ -60,6 +63,7 @@
                             packageName = readString() ?: "",
                             className = readString() ?: "",
                             intentAction = readString() ?: "",
+                            preferenceKey = readString() ?: "",
                             extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
                         )
                     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java
index 790939a..4b67ef7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingPreference.java
@@ -40,6 +40,8 @@
                 return ActionSwitchPreference.readFromParcel(in);
             case DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE:
                 return MultiTogglePreference.readFromParcel(in);
+            case DeviceSettingType.DEVICE_SETTING_TYPE_FOOTER:
+                return DeviceSettingFooterPreference.readFromParcel(in);
             default:
                 return UNKNOWN;
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java
index ee4d90f..ae3bf5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingType.java
@@ -27,6 +27,7 @@
             DeviceSettingType.DEVICE_SETTING_TYPE_UNKNOWN,
             DeviceSettingType.DEVICE_SETTING_TYPE_ACTION_SWITCH,
             DeviceSettingType.DEVICE_SETTING_TYPE_MULTI_TOGGLE,
+            DeviceSettingType.DEVICE_SETTING_TYPE_FOOTER,
         },
         open = true)
 public @interface DeviceSettingType {
@@ -38,4 +39,10 @@
 
     /** Device setting type is multi-toggle preference. */
     int DEVICE_SETTING_TYPE_MULTI_TOGGLE = 2;
+
+    /** Device setting type is footer preference. */
+    int DEVICE_SETTING_TYPE_FOOTER = 3;
+
+    /** Device setting type is "help" preference. */
+    int DEVICE_SETTING_TYPE_HELP = 4;
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt
index c8a2e9c..5656f38 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfig.kt
@@ -25,13 +25,13 @@
  *
  * @property mainContentItems The setting items to be shown in main page.
  * @property moreSettingsItems The setting items to be shown in more settings page.
- * @property moreSettingsFooter The footer in more settings page.
+ * @property moreSettingsHelpItem The help item displayed on the top right corner of the page.
  * @property extras Extra bundle
  */
 data class DeviceSettingsConfig(
     val mainContentItems: List<DeviceSettingItem>,
     val moreSettingsItems: List<DeviceSettingItem>,
-    val moreSettingsFooter: String,
+    val moreSettingsHelpItem: DeviceSettingItem?,
     val extras: Bundle = Bundle.EMPTY,
 ) : Parcelable {
 
@@ -41,7 +41,7 @@
         parcel.run {
             writeTypedList(mainContentItems)
             writeTypedList(moreSettingsItems)
-            writeString(moreSettingsFooter)
+            writeParcelable(moreSettingsHelpItem, flags)
             writeBundle(extras)
         }
     }
@@ -61,8 +61,9 @@
                                 arrayListOf<DeviceSettingItem>().also {
                                     readTypedList(it, DeviceSettingItem.CREATOR)
                                 },
-                            moreSettingsFooter = readString()!!,
-                            extras = readBundle((Bundle::class.java.classLoader))!!,
+                            moreSettingsHelpItem = readParcelable(
+                                DeviceSettingItem::class.java.classLoader
+                            )
                         )
                     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
new file mode 100644
index 0000000..1726036
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.bluetooth.devicesettings;
+
+parcelable DeviceSettingsProviderServiceStatus;
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.kt
new file mode 100644
index 0000000..977849e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatus.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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * A data class representing a device settings item in bluetooth device details config.
+ *
+ * @property enabled Whether the service is enabled.
+ * @property extras Extra bundle
+ */
+data class DeviceSettingsProviderServiceStatus(
+    val enabled: Boolean,
+    val extras: Bundle = Bundle.EMPTY,
+) : Parcelable {
+
+    override fun describeContents(): Int = 0
+
+    override fun writeToParcel(parcel: Parcel, flags: Int) {
+        parcel.run {
+            writeBoolean(enabled)
+            writeBundle(extras)
+        }
+    }
+
+    companion object {
+        @JvmField
+        val CREATOR: Parcelable.Creator<DeviceSettingsProviderServiceStatus> =
+            object : Parcelable.Creator<DeviceSettingsProviderServiceStatus> {
+                override fun createFromParcel(parcel: Parcel) =
+                    parcel.run {
+                        DeviceSettingsProviderServiceStatus(
+                            enabled = readBoolean(),
+                            extras = readBundle((Bundle::class.java.classLoader)) ?: Bundle.EMPTY,
+                        )
+                    }
+
+                override fun newArray(size: Int): Array<DeviceSettingsProviderServiceStatus?> {
+                    return arrayOfNulls(size)
+                }
+            }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
index d5efac9..1c0a1fd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/IDeviceSettingsProviderService.aidl
@@ -18,10 +18,12 @@
 
 import com.android.settingslib.bluetooth.devicesettings.DeviceInfo;
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState;
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus;
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener;
 
-oneway interface IDeviceSettingsProviderService {
-   void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
-   void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
-   void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
+interface IDeviceSettingsProviderService {
+   DeviceSettingsProviderServiceStatus getServiceStatus();
+   oneway void registerDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+   oneway void unregisterDeviceSettingsListener(in DeviceInfo device, in IDeviceSettingsListener callback);
+   oneway void updateDeviceSettings(in DeviceInfo device, in DeviceSettingState params);
 }
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.kt
new file mode 100644
index 0000000..25080bc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/model/ServiceConnectionStatus.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.bluetooth.devicesettings.data.model
+
+import android.os.IInterface
+
+/** Present a service connection status. */
+sealed interface ServiceConnectionStatus<out T : IInterface> {
+    /** Service is connecting. */
+    data object Connecting : ServiceConnectionStatus<Nothing>
+
+    /** Service is connected. */
+    data class Connected<T : IInterface>(val service: T) : ServiceConnectionStatus<T>
+
+    /** Service connection failed. */
+    data object Failed : ServiceConnectionStatus<Nothing>
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index 326bb31..457d6a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -18,16 +18,20 @@
 
 import android.bluetooth.BluetoothAdapter
 import android.content.Context
+import android.text.TextUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreference
 import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingFooterPreference
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingHelpPreference
 import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference
 import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
@@ -76,8 +80,7 @@
                             coroutineScope,
                             backgroundCoroutineContext,
                         )
-                }
-            )
+                })
 
     override suspend fun getDeviceSettingsConfig(
         cachedDevice: CachedBluetoothDevice
@@ -96,11 +99,15 @@
         DeviceSettingConfigModel(
             mainItems = mainContentItems.map { it.toModel() },
             moreSettingsItems = moreSettingsItems.map { it.toModel() },
-            moreSettingsPageFooter = moreSettingsFooter
-        )
+            moreSettingsHelpItem = moreSettingsHelpItem?.toModel(), )
 
-    private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel =
-        DeviceSettingConfigItemModel(settingId)
+    private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel {
+        return if (!TextUtils.isEmpty(preferenceKey)) {
+            DeviceSettingConfigItemModel.BuiltinItem(settingId, preferenceKey!!)
+        } else {
+            DeviceSettingConfigItemModel.AppProvidedItem(settingId)
+        }
+    }
 
     private fun DeviceSetting.toModel(
         cachedDevice: CachedBluetoothDevice,
@@ -113,7 +120,7 @@
                     id = settingId,
                     title = pref.title,
                     summary = pref.summary,
-                    icon = pref.icon,
+                    icon = pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) },
                     isAllowedChangingState = pref.isAllowedChangingState,
                     intent = pref.intent,
                     switchState =
@@ -146,8 +153,15 @@
                         }
                     },
                 )
+            is DeviceSettingFooterPreference -> DeviceSettingModel.FooterPreference(
+                cachedDevice = cachedDevice,
+                id = settingId, footerText = pref.footerText)
+            is DeviceSettingHelpPreference -> DeviceSettingModel.HelpPreference(
+                cachedDevice = cachedDevice,
+                id = settingId, intent = pref.intent)
             else -> DeviceSettingModel.Unknown(cachedDevice, settingId)
         }
 
-    private fun ToggleInfo.toModel(): ToggleModel = ToggleModel(label, icon)
+    private fun ToggleInfo.toModel(): ToggleModel =
+        ToggleModel(label, DeviceSettingIcon.BitmapIcon(icon))
 }
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 d6b2862..33beb06 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
@@ -22,7 +22,8 @@
 import android.content.Intent
 import android.content.ServiceConnection
 import android.os.IBinder
-import com.android.internal.util.ConcurrentUtils
+import android.os.IInterface
+import android.util.Log
 import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
@@ -34,27 +35,28 @@
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
+import com.android.settingslib.bluetooth.devicesettings.data.model.ServiceConnectionStatus
 import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicReference
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emitAll
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flatMapConcat
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -84,64 +86,132 @@
             }
     }
 
-    private var config = AtomicReference<DeviceSettingsConfig?>(null)
-    private var idToSetting = AtomicReference<Flow<Map<Int, DeviceSetting>>?>(null)
-
-    /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
-    suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? =
-        config.computeIfAbsent {
-            getConfigServiceBindingIntent(cachedDevice)
-                .flatMapLatest { getService(it) }
-                .map { it?.let { IDeviceSettingsConfigProviderService.Stub.asInterface(it) } }
-                .map {
-                    it?.getDeviceSettingsConfig(
-                        deviceInfo { setBluetoothAddress(cachedDevice.address) }
-                    )
+    private var isServiceEnabled =
+        coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+            val states = getSettingsProviderServices()?.values ?: return@async false
+            combine(states) { it.toList() }
+                .mapNotNull { allStatus ->
+                    if (allStatus.any { it is ServiceConnectionStatus.Failed }) {
+                        false
+                    } else if (allStatus.all { it is ServiceConnectionStatus.Connected }) {
+                        allStatus
+                            .filterIsInstance<
+                                ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+                            >()
+                            .all { it.service.serviceStatus?.enabled == true }
+                    } else {
+                        null
+                    }
                 }
                 .first()
         }
 
+    private var config =
+        coroutineScope.async(backgroundCoroutineContext, start = CoroutineStart.LAZY) {
+            val intent =
+                tryGetEndpointFromMetadata(cachedDevice)?.toIntent()
+                    ?: run {
+                        Log.i(TAG, "Unable to read device setting metadata from $cachedDevice")
+                        return@async null
+                    }
+            getService(intent, IDeviceSettingsConfigProviderService.Stub::asInterface)
+                .flatMapConcat {
+                    when (it) {
+                        is ServiceConnectionStatus.Connected ->
+                            flowOf(
+                                it.service.getDeviceSettingsConfig(
+                                    deviceInfo { setBluetoothAddress(cachedDevice.address) }
+                                )
+                            )
+                        ServiceConnectionStatus.Connecting -> flowOf()
+                        ServiceConnectionStatus.Failed -> flowOf(null)
+                    }
+                }
+                .first()
+        }
+
+    private val settingIdToItemMapping =
+        flow {
+                if (!isServiceEnabled.await()) {
+                    Log.w(TAG, "Service is disabled")
+                    return@flow
+                }
+                getSettingsProviderServices()
+                    ?.values
+                    ?.map {
+                        it.flatMapLatest { status ->
+                            when (status) {
+                                is ServiceConnectionStatus.Connected ->
+                                    getDeviceSettingsFromService(cachedDevice, status.service)
+                                else -> flowOf(emptyList())
+                            }
+                        }
+                    }
+                    ?.let { items -> combine(items) { it.toList().flatten() } }
+                    ?.map { items -> items.associateBy { it.settingId } }
+                    ?.let { emitAll(it) }
+            }
+            .shareIn(scope = coroutineScope, started = SharingStarted.WhileSubscribed(), replay = 1)
+
+    /** Gets [DeviceSettingsConfig] for the device, return null when failed. */
+    suspend fun getDeviceSettingsConfig(): DeviceSettingsConfig? {
+        if (!isServiceEnabled.await()) {
+            Log.w(TAG, "Service is disabled")
+            return null
+        }
+        return readConfig()
+    }
+
     /** Gets all device settings for the device. */
     fun getDeviceSettingList(): Flow<List<DeviceSetting>> =
-        getSettingIdToItemMapping().map { it.values.toList() }
+        settingIdToItemMapping.map { it.values.toList() }
 
     /** Gets the device settings with the ID for the device. */
     fun getDeviceSetting(@DeviceSettingId deviceSettingId: Int): Flow<DeviceSetting?> =
-        getSettingIdToItemMapping().map { it[deviceSettingId] }
+        settingIdToItemMapping.map { it[deviceSettingId] }
 
     /** Updates the device setting state for the device. */
     suspend fun updateDeviceSettings(
         @DeviceSettingId deviceSettingId: Int,
         deviceSettingPreferenceState: DeviceSettingPreferenceState,
     ) {
-        getDeviceSettingsConfig()?.let { config ->
+        if (!isServiceEnabled.await()) {
+            Log.w(TAG, "Service is disabled")
+            return
+        }
+        readConfig()?.let { config ->
             (config.mainContentItems + config.moreSettingsItems)
                 .find { it.settingId == deviceSettingId }
                 ?.let {
                     getSettingsProviderServices()
                         ?.get(EndPoint(it.packageName, it.className, it.intentAction))
-                        ?.filterNotNull()
+                        ?.filterIsInstance<
+                            ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+                        >()
                         ?.first()
                 }
+                ?.service
                 ?.updateDeviceSettings(
                     deviceInfo { setBluetoothAddress(cachedDevice.address) },
                     DeviceSettingState.Builder()
                         .setSettingId(deviceSettingId)
                         .setPreferenceState(deviceSettingPreferenceState)
-                        .build()
+                        .build(),
                 )
         }
     }
 
+    private suspend fun readConfig(): DeviceSettingsConfig? = config.await()
+
     private suspend fun getSettingsProviderServices():
-        Map<EndPoint, StateFlow<IDeviceSettingsProviderService?>>? =
-        getDeviceSettingsConfig()
+        Map<EndPoint, StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>>? =
+        readConfig()
             ?.let { config ->
                 (config.mainContentItems + config.moreSettingsItems).map {
                     EndPoint(
                         packageName = it.packageName,
                         className = it.className,
-                        intentAction = it.intentAction
+                        intentAction = it.intentAction,
                     )
                 }
             }
@@ -150,43 +220,22 @@
                 { it },
                 { endpoint ->
                     services.computeIfAbsent(endpoint) {
-                        getService(endpoint.toIntent())
-                            .map { service ->
-                                IDeviceSettingsProviderService.Stub.asInterface(service)
-                            }
-                            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), null)
+                        getService(
+                                endpoint.toIntent(),
+                                IDeviceSettingsProviderService.Stub::asInterface,
+                            )
+                            .stateIn(
+                                coroutineScope,
+                                SharingStarted.WhileSubscribed(),
+                                ServiceConnectionStatus.Connecting,
+                            )
                     }
-                }
+                },
             )
 
-    private fun getSettingIdToItemMapping(): Flow<Map<Int, DeviceSetting>> =
-        idToSetting.computeIfAbsent {
-            flow {
-                    getSettingsProviderServices()
-                        ?.values
-                        ?.map {
-                            it.flatMapLatest { service ->
-                                if (service != null) {
-                                    getDeviceSettingsFromService(cachedDevice, service)
-                                } else {
-                                    flowOf(emptyList())
-                                }
-                            }
-                        }
-                        ?.let { items -> combine(items) { it.toList().flatten() } }
-                        ?.map { items -> items.associateBy { it.settingId } }
-                        ?.let { emitAll(it) }
-                }
-                .shareIn(
-                    scope = coroutineScope,
-                    started = SharingStarted.WhileSubscribed(),
-                    replay = 1
-                )
-        }!!
-
     private fun getDeviceSettingsFromService(
         cachedDevice: CachedBluetoothDevice,
-        service: IDeviceSettingsProviderService
+        service: IDeviceSettingsProviderService,
     ): Flow<List<DeviceSetting>> {
         return callbackFlow {
                 val listener =
@@ -202,51 +251,28 @@
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList())
     }
 
-    private fun getService(intent: Intent): Flow<IBinder?> {
+    private fun <T : IInterface> getService(
+        intent: Intent,
+        transform: ((IBinder) -> T),
+    ): Flow<ServiceConnectionStatus<T>> {
         return callbackFlow {
             val serviceConnection =
                 object : ServiceConnection {
                     override fun onServiceConnected(name: ComponentName, service: IBinder) {
-                        launch { send(service) }
+                        launch { send(ServiceConnectionStatus.Connected(transform(service))) }
                     }
 
                     override fun onServiceDisconnected(name: ComponentName?) {
-                        launch { send(null) }
+                        launch { send(ServiceConnectionStatus.Connecting) }
                     }
                 }
             if (!context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)) {
-                launch { send(null) }
+                launch { send(ServiceConnectionStatus.Failed) }
             }
             awaitClose { context.unbindService(serviceConnection) }
         }
     }
 
-    private fun getConfigServiceBindingIntent(cachedDevice: CachedBluetoothDevice): Flow<Intent> {
-        return callbackFlow {
-                val listener =
-                    BluetoothAdapter.OnMetadataChangedListener { device, key, _ ->
-                        if (
-                            key == METADATA_FAST_PAIR_CUSTOMIZED_FIELDS &&
-                                cachedDevice.device == device
-                        ) {
-                            launch { tryGetEndpointFromMetadata(cachedDevice)?.let { send(it) } }
-                        }
-                    }
-                bluetoothAdaptor.addOnMetadataChangedListener(
-                    cachedDevice.device,
-                    ConcurrentUtils.DIRECT_EXECUTOR,
-                    listener,
-                )
-                awaitClose {
-                    bluetoothAdaptor.removeOnMetadataChangedListener(cachedDevice.device, listener)
-                }
-            }
-            .onStart { tryGetEndpointFromMetadata(cachedDevice)?.let { emit(it) } }
-            .distinctUntilChanged()
-            .map { it.toIntent() }
-            .flowOn(backgroundCoroutineContext)
-    }
-
     private suspend fun tryGetEndpointFromMetadata(cachedDevice: CachedBluetoothDevice): EndPoint? =
         withContext(backgroundCoroutineContext) {
             val packageName =
@@ -257,29 +283,31 @@
             val className =
                 BluetoothUtils.getFastPairCustomizedField(
                     cachedDevice.device,
-                    CONFIG_SERVICE_CLASS_NAME
+                    CONFIG_SERVICE_CLASS_NAME,
                 ) ?: return@withContext null
             val intentAction =
                 BluetoothUtils.getFastPairCustomizedField(
                     cachedDevice.device,
-                    CONFIG_SERVICE_INTENT_ACTION
+                    CONFIG_SERVICE_INTENT_ACTION,
                 ) ?: return@withContext null
             EndPoint(packageName, className, intentAction)
         }
 
-    private inline fun <T> AtomicReference<T?>.computeIfAbsent(producer: () -> T): T? =
-        get() ?: producer().let { compareAndExchange(null, it) ?: it }
-
     private inline fun deviceInfo(block: DeviceInfo.Builder.() -> Unit): DeviceInfo {
         return DeviceInfo.Builder().apply { block() }.build()
     }
 
     companion object {
+        const val TAG = "DeviceSettingSrvConn"
         const val METADATA_FAST_PAIR_CUSTOMIZED_FIELDS: Int = 25
         const val CONFIG_SERVICE_PACKAGE_NAME = "DEVICE_SETTINGS_CONFIG_PACKAGE_NAME"
         const val CONFIG_SERVICE_CLASS_NAME = "DEVICE_SETTINGS_CONFIG_CLASS"
         const val CONFIG_SERVICE_INTENT_ACTION = "DEVICE_SETTINGS_CONFIG_ACTION"
 
-        val services = ConcurrentHashMap<EndPoint, StateFlow<IDeviceSettingsProviderService?>>()
+        val services =
+            ConcurrentHashMap<
+                EndPoint,
+                StateFlow<ServiceConnectionStatus<IDeviceSettingsProviderService>>,
+            >()
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
index cd597ee..c1ac763 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
@@ -24,10 +24,24 @@
     val mainItems: List<DeviceSettingConfigItemModel>,
     /** Items need to be shown in device details more settings page. */
     val moreSettingsItems: List<DeviceSettingConfigItemModel>,
-    /** Footer text in more settings page. */
-    val moreSettingsPageFooter: String)
+    /**
+     * Help button which need to be shown on the top right corner of device details more settings
+     * page.
+     */
+    val moreSettingsHelpItem: DeviceSettingConfigItemModel?,
+)
 
 /** Models a device setting item in config. */
-data class DeviceSettingConfigItemModel(
-    @DeviceSettingId val settingId: Int,
-)
+sealed interface DeviceSettingConfigItemModel {
+    @DeviceSettingId val settingId: Int
+
+    /** A built-in item in Settings. */
+    data class BuiltinItem(
+        @DeviceSettingId override val settingId: Int,
+        val preferenceKey: String?
+    ) : DeviceSettingConfigItemModel
+
+    /** A remote item provided by other apps. */
+    data class AppProvidedItem(@DeviceSettingId override val settingId: Int) :
+        DeviceSettingConfigItemModel
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
index db78280..73648ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.Intent
 import android.graphics.Bitmap
+import androidx.annotation.DrawableRes
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
 
@@ -32,7 +33,7 @@
         @DeviceSettingId override val id: Int,
         val title: String,
         val summary: String? = null,
-        val icon: Bitmap? = null,
+        val icon: DeviceSettingIcon? = null,
         val intent: Intent? = null,
         val switchState: DeviceSettingStateModel.ActionSwitchPreferenceState? = null,
         val isAllowedChangingState: Boolean = true,
@@ -51,6 +52,20 @@
         val updateState: (DeviceSettingStateModel.MultiTogglePreferenceState) -> Unit
     ) : DeviceSettingModel
 
+    /** Models a footer preference. */
+    data class FooterPreference(
+        override val cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId override val id: Int,
+        val footerText: String,
+    ) : DeviceSettingModel
+
+    /** Models a help preference displayed on the top right corner of the fragment. */
+    data class HelpPreference(
+        override val cachedDevice: CachedBluetoothDevice,
+        @DeviceSettingId override val id: Int,
+        val intent: Intent,
+    ) : DeviceSettingModel
+
     /** Models an unknown preference. */
     data class Unknown(
         override val cachedDevice: CachedBluetoothDevice,
@@ -59,4 +74,12 @@
 }
 
 /** Models a toggle in [DeviceSettingModel.MultiTogglePreference]. */
-data class ToggleModel(val label: String, val icon: Bitmap)
+data class ToggleModel(val label: String, val icon: DeviceSettingIcon)
+
+/** Models an icon in device settings. */
+sealed interface DeviceSettingIcon {
+
+    data class BitmapIcon(val bitmap: Bitmap) : DeviceSettingIcon
+
+    data class ResourceIcon(@DrawableRes val resId: Int) : DeviceSettingIcon
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 4f2329b..47a08eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -136,12 +136,10 @@
             return true;
         }
 
-        if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
-            // App is subject to DevicePolicyManager.setUserControlDisabledPackages() policy.
-            final int userId = UserHandle.getUserId(uid);
-            if (mAppContext.getPackageManager().isPackageStateProtected(pkg, userId)) {
-                return true;
-            }
+        // App is subject to DevicePolicyManager.setUserControlDisabledPackages() policy.
+        final int userId = UserHandle.getUserId(uid);
+        if (mAppContext.getPackageManager().isPackageStateProtected(pkg, userId)) {
+            return true;
         }
 
         return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt
deleted file mode 100644
index 02d684d..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExt.kt
+++ /dev/null
@@ -1,100 +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.settingslib.media.data.repository
-
-import android.media.AudioManager
-import android.media.IVolumeController
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.buffer
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.launch
-
-/** Returns [AudioManager.setVolumeController] events as a [Flow] */
-fun AudioManager.volumeControllerEvents(): Flow<VolumeControllerEvent> =
-    callbackFlow {
-            volumeController =
-                object : IVolumeController.Stub() {
-                    override fun displaySafeVolumeWarning(flags: Int) {
-                        launch { send(VolumeControllerEvent.DisplaySafeVolumeWarning(flags)) }
-                    }
-
-                    override fun volumeChanged(streamType: Int, flags: Int) {
-                        launch { send(VolumeControllerEvent.VolumeChanged(streamType, flags)) }
-                    }
-
-                    override fun masterMuteChanged(flags: Int) {
-                        launch { send(VolumeControllerEvent.MasterMuteChanged(flags)) }
-                    }
-
-                    override fun setLayoutDirection(layoutDirection: Int) {
-                        launch { send(VolumeControllerEvent.SetLayoutDirection(layoutDirection)) }
-                    }
-
-                    override fun dismiss() {
-                        launch { send(VolumeControllerEvent.Dismiss) }
-                    }
-
-                    override fun setA11yMode(mode: Int) {
-                        launch { send(VolumeControllerEvent.SetA11yMode(mode)) }
-                    }
-
-                    override fun displayCsdWarning(
-                        csdWarning: Int,
-                        displayDurationMs: Int,
-                    ) {
-                        launch {
-                            send(
-                                VolumeControllerEvent.DisplayCsdWarning(
-                                    csdWarning,
-                                    displayDurationMs,
-                                )
-                            )
-                        }
-                    }
-                }
-            awaitClose { volumeController = null }
-        }
-        .buffer()
-
-/** Models events received via [IVolumeController] */
-sealed interface VolumeControllerEvent {
-
-    /** @see [IVolumeController.displaySafeVolumeWarning] */
-    data class DisplaySafeVolumeWarning(val flags: Int) : VolumeControllerEvent
-
-    /** @see [IVolumeController.volumeChanged] */
-    data class VolumeChanged(val streamType: Int, val flags: Int) : VolumeControllerEvent
-
-    /** @see [IVolumeController.masterMuteChanged] */
-    data class MasterMuteChanged(val flags: Int) : VolumeControllerEvent
-
-    /** @see [IVolumeController.setLayoutDirection] */
-    data class SetLayoutDirection(val layoutDirection: Int) : VolumeControllerEvent
-
-    /** @see [IVolumeController.setA11yMode] */
-    data class SetA11yMode(val mode: Int) : VolumeControllerEvent
-
-    /** @see [IVolumeController.displayCsdWarning] */
-    data class DisplayCsdWarning(
-        val csdWarning: Int,
-        val displayDurationMs: Int,
-    ) : VolumeControllerEvent
-
-    /** @see [IVolumeController.dismiss] */
-    data object Dismiss : VolumeControllerEvent
-}
\ No newline at end of file
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 e5d79a1..043219a 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
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.notification.data.repository
 
+import android.app.AutomaticZenRule
 import android.app.NotificationManager
 import android.provider.Settings
 import com.android.settingslib.notification.modes.TestModeBuilder
@@ -40,6 +41,8 @@
     override val modes: Flow<List<ZenMode>>
         get() = mutableModesFlow.asStateFlow()
 
+    override fun getModes(): List<ZenMode> = mutableModesFlow.value
+
     private val activeModesDurations = mutableMapOf<String, Duration?>()
 
     init {
@@ -58,8 +61,9 @@
         mutableModesFlow.value += zenModes
     }
 
-    fun addMode(id: String, active: Boolean = false) {
-        mutableModesFlow.value += newMode(id, active)
+    fun addMode(id: String, @AutomaticZenRule.Type type: Int = AutomaticZenRule.TYPE_UNKNOWN,
+        active: Boolean = false) {
+        mutableModesFlow.value += newMode(id, type, active)
     }
 
     fun removeMode(id: String) {
@@ -128,6 +132,6 @@
         )
     )
 
-private fun newMode(id: String, active: Boolean = false): ZenMode {
-    return TestModeBuilder().setId(id).setName("Mode $id").setActive(active).build()
+private fun newMode(id: String, @AutomaticZenRule.Type type: Int, active: Boolean): ZenMode {
+    return TestModeBuilder().setId(id).setName("Mode $id").setType(type).setActive(active).build()
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index 0ff7f84..7fdbcda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -59,6 +59,8 @@
     /** A list of all existing priority modes. */
     val modes: Flow<List<ZenMode>>
 
+    fun getModes(): List<ZenMode>
+
     fun activateMode(zenMode: ZenMode, duration: Duration? = null)
 
     fun deactivateMode(zenMode: ZenMode)
@@ -184,6 +186,15 @@
         }
     }
 
+    /**
+     * Gets the current list of [ZenMode] instances according to the backend.
+     *
+     * This is necessary, and cannot be supplanted by making [modes] a StateFlow, because it will be
+     * called whenever we know or suspect that [modes] may not have caught up to the latest data
+     * (such as right after a user switch).
+     */
+    override fun getModes(): List<ZenMode> = backend.modes
+
     override fun activateMode(zenMode: ZenMode, duration: Duration?) {
         backend.activateMode(zenMode, duration)
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
index f7492cf..8e0cdf8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/TestModeBuilder.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
 
@@ -24,11 +25,13 @@
 import android.content.ComponentName;
 import android.net.Uri;
 import android.service.notification.Condition;
+import android.service.notification.SystemZenRules;
 import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
 
 import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import java.util.Random;
@@ -40,13 +43,27 @@
     private ZenModeConfig.ZenRule mConfigZenRule;
 
     public static final ZenMode EXAMPLE = new TestModeBuilder().build();
-    public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY, true);
-    public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY, false);
 
-    public static ZenMode manualDnd(Uri conditionId, boolean isActive) {
+    public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY,
+            INTERRUPTION_FILTER_PRIORITY, true);
+
+    public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY,
+            INTERRUPTION_FILTER_PRIORITY, false);
+
+    @NonNull
+    public static ZenMode manualDnd(@NotificationManager.InterruptionFilter int filter,
+            boolean isActive) {
+        return manualDnd(Uri.EMPTY, filter, isActive);
+    }
+
+    private static ZenMode manualDnd(Uri conditionId,
+            @NotificationManager.InterruptionFilter int filter, boolean isActive) {
         return ZenMode.manualDndMode(
                 new AutomaticZenRule.Builder("Do Not Disturb", conditionId)
-                        .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+                        .setInterruptionFilter(filter)
+                        .setType(AutomaticZenRule.TYPE_OTHER)
+                        .setManualInvocationAllowed(true)
+                        .setPackage(SystemZenRules.PACKAGE_ANDROID)
                         .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
                         .build(),
                 isActive);
@@ -58,7 +75,7 @@
         mId = "rule_" + id;
         mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id))
                 .setPackage("some_package")
-                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
                 .build();
         mConfigZenRule = new ZenModeConfig.ZenRule();
@@ -134,7 +151,7 @@
             @NotificationManager.InterruptionFilter int interruptionFilter) {
         mRule.setInterruptionFilter(interruptionFilter);
         mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
-                interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+                interruptionFilter, INTERRUPTION_FILTER_PRIORITY);
         return this;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIcon.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIcon.java
new file mode 100644
index 0000000..001701e
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIcon.java
@@ -0,0 +1,48 @@
+/*
+ * 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.notification.modes;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Icon of a Zen Mode, already loaded from the owner's resources (if specified) or from a default.
+ */
+public record ZenIcon(@NonNull Key key, @NonNull Drawable drawable) {
+
+    /**
+     * Key of a Zen Mode Icon.
+     *
+     * <p>{@link #resPackage()} will be null if the resource belongs to the system, and thus can
+     * be loaded with any {@code Context}.
+     */
+    public record Key(@Nullable String resPackage, @DrawableRes int resId) {
+
+        public Key {
+            checkArgument(resId != 0, "Resource id must be valid");
+        }
+
+        static Key forSystemResource(@DrawableRes int resId) {
+            return new Key(null, resId);
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.java
new file mode 100644
index 0000000..79dabf0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconKeys.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.settingslib.notification.modes;
+
+import android.app.AutomaticZenRule;
+
+import com.android.internal.R;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Known icon keys for zen modes that lack a custom {@link AutomaticZenRule#getIconResId()}, based
+ * on their {@link ZenMode.Kind} and {@link ZenMode#getType}.
+ */
+class ZenIconKeys {
+
+    /** The icon for Do Not Disturb mode. */
+    static final ZenIcon.Key MANUAL_DND = ZenIcon.Key.forSystemResource(
+            R.drawable.ic_zen_mode_type_special_dnd);
+
+    /**
+     * The default icon for implicit modes (they can also have a specific icon, if the user has
+     * chosen one via Settings).
+     */
+    static final ZenIcon.Key IMPLICIT_MODE_DEFAULT = ZenIcon.Key.forSystemResource(
+            R.drawable.ic_zen_mode_type_special_dnd);
+
+    private static final ImmutableMap<Integer, ZenIcon.Key> TYPE_DEFAULTS = ImmutableMap.of(
+            AutomaticZenRule.TYPE_UNKNOWN,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown),
+            AutomaticZenRule.TYPE_OTHER,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_other),
+            AutomaticZenRule.TYPE_SCHEDULE_TIME,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_schedule_time),
+            AutomaticZenRule.TYPE_SCHEDULE_CALENDAR,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_schedule_calendar),
+            AutomaticZenRule.TYPE_BEDTIME,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_bedtime),
+            AutomaticZenRule.TYPE_DRIVING,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_driving),
+            AutomaticZenRule.TYPE_IMMERSIVE,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_immersive),
+            AutomaticZenRule.TYPE_THEATER,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_theater),
+            AutomaticZenRule.TYPE_MANAGED,
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_managed)
+    );
+
+    private static final ZenIcon.Key FOR_UNEXPECTED_TYPE =
+            ZenIcon.Key.forSystemResource(R.drawable.ic_zen_mode_type_unknown);
+
+    /** Default icon descriptors per mode {@link AutomaticZenRule.Type}. */
+    static ZenIcon.Key forType(@AutomaticZenRule.Type int ruleType) {
+        return TYPE_DEFAULTS.getOrDefault(ruleType, FOR_UNEXPECTED_TYPE);
+    }
+
+    private ZenIconKeys() { }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
index 271d5c4..43c6c50 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenIconLoader.java
@@ -16,28 +16,23 @@
 
 package com.android.settingslib.notification.modes;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.util.concurrent.Futures.immediateFuture;
+import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
-import android.app.AutomaticZenRule;
 import android.content.Context;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
-import android.service.notification.SystemZenRules;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.LruCache;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
+import androidx.annotation.Nullable;
 
 import com.google.common.util.concurrent.FluentFuture;
-import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.ListeningExecutorService;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -54,100 +49,92 @@
     @Nullable // Until first usage
     private static ZenIconLoader sInstance;
 
-    private final LruCache<String, Drawable> mCache;
+    private final LruCache<ZenIcon.Key, Drawable> mCache;
     private final ListeningExecutorService mBackgroundExecutor;
 
+    /** Obtains the singleton {@link ZenIconLoader}. */
     public static ZenIconLoader getInstance() {
         if (sInstance == null) {
-            sInstance = new ZenIconLoader();
+            sInstance = new ZenIconLoader(Executors.newFixedThreadPool(4));
         }
         return sInstance;
     }
 
-    private ZenIconLoader() {
-        this(Executors.newFixedThreadPool(4));
-    }
-
-    @VisibleForTesting
-    ZenIconLoader(ExecutorService backgroundExecutor) {
+    /**
+     * Constructs a ZenIconLoader with the specified {@code backgroundExecutor}.
+     *
+     * <p>ZenIconLoader <em>should be a singleton</em>, so this should only be used to instantiate
+     * and provide the singleton instance in a module. If the app doesn't support dependency
+     * injection, use {@link #getInstance} instead.
+     */
+    public ZenIconLoader(@NonNull ExecutorService backgroundExecutor) {
         mCache = new LruCache<>(50);
         mBackgroundExecutor =
                 MoreExecutors.listeningDecorator(backgroundExecutor);
     }
 
+    /**
+     * Loads the {@link Drawable} corresponding to a {@link ZenMode} in a background thread, and
+     * caches it for future calls.
+     *
+     * <p>The {@link ZenIcon#drawable()} will always correspond to the resource indicated by
+     * {@link ZenIcon#key()}. In turn, this will match the value of {@link ZenMode#getIconKey()}
+     * for the supplied mode -- except for the rare case where the mode has an apparently valid
+     * drawable resource id that we fail to load for some reason, thus needing a "fallback" icon.
+     */
     @NonNull
-    ListenableFuture<Drawable> getIcon(Context context, @NonNull AutomaticZenRule rule) {
-        if (rule.getIconResId() == 0) {
-            return Futures.immediateFuture(getFallbackIcon(context, rule.getType()));
-        }
+    public ListenableFuture<ZenIcon> getIcon(@NonNull Context context, @NonNull ZenMode mode) {
+        ZenIcon.Key key = mode.getIconKey();
 
-        return FluentFuture.from(loadIcon(context, rule.getPackageName(), rule.getIconResId()))
-                .transform(icon ->
-                                icon != null ? icon : getFallbackIcon(context, rule.getType()),
-                        MoreExecutors.directExecutor());
+        return FluentFuture.from(loadIcon(context, key, /* useMonochromeIfPresent= */ true))
+                .transformAsync(drawable ->
+                        drawable != null
+                            ? immediateFuture(new ZenIcon(key, drawable))
+                            : getFallbackIcon(context, mode),
+                mBackgroundExecutor);
+    }
+
+    private ListenableFuture<ZenIcon> getFallbackIcon(Context context, ZenMode mode) {
+        ZenIcon.Key key = ZenIconKeys.forType(mode.getType());
+        return FluentFuture.from(loadIcon(context, key, /* useMonochromeIfPresent= */ false))
+                .transform(drawable -> {
+                    checkNotNull(drawable, "Couldn't load DEFAULT icon for mode %s!", mode);
+                    return new ZenIcon(key, drawable);
+                },
+                directExecutor());
     }
 
     @NonNull
-    private ListenableFuture</* @Nullable */ Drawable> loadIcon(Context context, String pkg,
-            int iconResId) {
-        String cacheKey = pkg + ":" + iconResId;
+    private ListenableFuture</* @Nullable */ Drawable> loadIcon(Context context,
+            ZenIcon.Key key, boolean useMonochromeIfPresent) {
         synchronized (mCache) {
-            Drawable cachedValue = mCache.get(cacheKey);
+            Drawable cachedValue = mCache.get(key);
             if (cachedValue != null) {
                 return immediateFuture(cachedValue != MISSING ? cachedValue : null);
             }
         }
 
         return FluentFuture.from(mBackgroundExecutor.submit(() -> {
-            if (TextUtils.isEmpty(pkg) || SystemZenRules.PACKAGE_ANDROID.equals(pkg)) {
-                return context.getDrawable(iconResId);
+            if (TextUtils.isEmpty(key.resPackage())) {
+                return context.getDrawable(key.resId());
             } else {
-                Context appContext = context.createPackageContext(pkg, 0);
-                Drawable appDrawable = appContext.getDrawable(iconResId);
-                return getMonochromeIconIfPresent(appDrawable);
+                Context appContext = context.createPackageContext(key.resPackage(), 0);
+                Drawable appDrawable = appContext.getDrawable(key.resId());
+                return useMonochromeIfPresent
+                        ? getMonochromeIconIfPresent(appDrawable)
+                        : appDrawable;
             }
         })).catching(Exception.class, ex -> {
             // If we cannot resolve the icon, then store MISSING in the cache below, so
             // we don't try again.
-            Log.e(TAG, "Error while loading icon " + cacheKey, ex);
+            Log.e(TAG, "Error while loading mode icon " + key, ex);
             return null;
-        }, MoreExecutors.directExecutor()).transform(drawable -> {
+        }, directExecutor()).transform(drawable -> {
             synchronized (mCache) {
-                mCache.put(cacheKey, drawable != null ? drawable : MISSING);
+                mCache.put(key, drawable != null ? drawable : MISSING);
             }
             return drawable;
-        }, MoreExecutors.directExecutor());
-    }
-
-    private static Drawable getFallbackIcon(Context context, int ruleType) {
-        int iconResIdFromType = getIconResourceIdFromType(ruleType);
-        return requireNonNull(context.getDrawable(iconResIdFromType));
-    }
-
-    /** Return the default icon resource associated to a {@link AutomaticZenRule.Type} */
-    @DrawableRes
-    public static int getIconResourceIdFromType(@AutomaticZenRule.Type int ruleType) {
-        return switch (ruleType) {
-            case AutomaticZenRule.TYPE_UNKNOWN ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_unknown;
-            case AutomaticZenRule.TYPE_OTHER ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_other;
-            case AutomaticZenRule.TYPE_SCHEDULE_TIME ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_time;
-            case AutomaticZenRule.TYPE_SCHEDULE_CALENDAR ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar;
-            case AutomaticZenRule.TYPE_BEDTIME ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_bedtime;
-            case AutomaticZenRule.TYPE_DRIVING ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_driving;
-            case AutomaticZenRule.TYPE_IMMERSIVE ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_immersive;
-            case AutomaticZenRule.TYPE_THEATER ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_theater;
-            case AutomaticZenRule.TYPE_MANAGED ->
-                    com.android.internal.R.drawable.ic_zen_mode_type_managed;
-            default -> com.android.internal.R.drawable.ic_zen_mode_type_unknown;
-        };
+        }, directExecutor());
     }
 
     private static Drawable getMonochromeIconIfPresent(Drawable icon) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
index 2f4b2efe..7b2a284 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenMode.java
@@ -18,7 +18,9 @@
 
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
 import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
@@ -35,7 +37,6 @@
 import android.app.AutomaticZenRule;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -49,12 +50,10 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.settingslib.R;
-
 import com.google.common.base.Strings;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.collect.ImmutableList;
 
+import java.util.Comparator;
 import java.util.Objects;
 
 /**
@@ -70,22 +69,47 @@
     static final String MANUAL_DND_MODE_ID = ZenModeConfig.MANUAL_RULE_ID;
     static final String TEMP_NEW_MODE_ID = "temp_new_mode";
 
-    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
-    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
-            new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .allowAlarms(true)
-                    .allowMedia(true)
-                    .allowPriorityChannels(false)
-                    .build();
+    private static final Comparator<Integer> PRIORITIZED_TYPE_COMPARATOR = new Comparator<>() {
 
-    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
-    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
-            new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .hideAllVisualEffects()
-                    .allowPriorityChannels(false)
-                    .build();
+        private static final ImmutableList</* @AutomaticZenRule.Type */ Integer>
+                PRIORITIZED_TYPES = ImmutableList.of(
+                        AutomaticZenRule.TYPE_BEDTIME,
+                        AutomaticZenRule.TYPE_DRIVING);
+
+        @Override
+        public int compare(Integer first, Integer second) {
+            if (PRIORITIZED_TYPES.contains(first) && PRIORITIZED_TYPES.contains(second)) {
+                return PRIORITIZED_TYPES.indexOf(first) - PRIORITIZED_TYPES.indexOf(second);
+            } else if (PRIORITIZED_TYPES.contains(first)) {
+                return -1;
+            } else if (PRIORITIZED_TYPES.contains(second)) {
+                return 1;
+            } else {
+                return 0;
+            }
+        }
+    };
+
+    // Manual DND first, Bedtime/Driving, then alphabetically.
+    public static final Comparator<ZenMode> PRIORITIZING_COMPARATOR = Comparator
+            .comparing(ZenMode::isManualDnd).reversed()
+            .thenComparing(ZenMode::getType, PRIORITIZED_TYPE_COMPARATOR)
+            .thenComparing(ZenMode::getName);
+
+    public enum Kind {
+        /** A "normal" mode, created by apps or users via {@code addAutomaticZenRule()}. */
+        NORMAL,
+
+        /** The special, built-in "Do Not Disturb" mode. */
+        MANUAL_DND,
+
+        /**
+         * An implicit mode, automatically created and managed by the system on behalf of apps that
+         * call {@code setInterruptionFilter()} or {@code setNotificationPolicy()} (with some
+         * exceptions).
+         */
+        IMPLICIT,
+    }
 
     public enum Status {
         ENABLED,
@@ -96,8 +120,8 @@
 
     private final String mId;
     private final AutomaticZenRule mRule;
+    private final Kind mKind;
     private final Status mStatus;
-    private final boolean mIsManualDnd;
 
     /**
      * Initializes a {@link ZenMode}, mainly based on the information from the
@@ -107,9 +131,11 @@
      * active, or the reason it was disabled) are read from the {@link ZenModeConfig.ZenRule} --
      * see {@link #computeStatus}.
      */
-    public ZenMode(String id, @NonNull AutomaticZenRule rule,
+    ZenMode(String id, @NonNull AutomaticZenRule rule,
             @NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
-        this(id, rule, computeStatus(zenRuleExtraData), false);
+        this(id, rule,
+                ZenModeConfig.isImplicitRuleId(id) ? Kind.IMPLICIT : Kind.NORMAL,
+                computeStatus(zenRuleExtraData));
     }
 
     private static Status computeStatus(@NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
@@ -128,9 +154,12 @@
         }
     }
 
-    public static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
-        return new ZenMode(MANUAL_DND_MODE_ID, manualRule,
-                isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED, true);
+    static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
+        return new ZenMode(
+                MANUAL_DND_MODE_ID,
+                manualRule,
+                Kind.MANUAL_DND,
+                isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED);
     }
 
     /**
@@ -149,19 +178,19 @@
                 .setIconResId(iconResId)
                 .setManualInvocationAllowed(true)
                 .build();
-        return new ZenMode(TEMP_NEW_MODE_ID, rule, Status.ENABLED, false);
+        return new ZenMode(TEMP_NEW_MODE_ID, rule, Kind.NORMAL, Status.ENABLED);
     }
 
-    private ZenMode(String id, @NonNull AutomaticZenRule rule, Status status, boolean isManualDnd) {
+    private ZenMode(String id, @NonNull AutomaticZenRule rule, Kind kind, Status status) {
         mId = id;
         mRule = rule;
+        mKind = kind;
         mStatus = status;
-        mIsManualDnd = isManualDnd;
     }
 
     /** Creates a deep copy of this object. */
     public ZenMode copy() {
-        return new ZenMode(mId, new AutomaticZenRule.Builder(mRule).build(), mStatus, mIsManualDnd);
+        return new ZenMode(mId, new AutomaticZenRule.Builder(mRule).build(), mKind, mStatus);
     }
 
     @NonNull
@@ -220,15 +249,50 @@
         return getTriggerDescription();
     }
 
+    /**
+     * Returns the {@link ZenIcon.Key} corresponding to the icon resource for this mode. This can be
+     * either app-provided (via {@link AutomaticZenRule#setIconResId}, user-chosen (via the icon
+     * picker in Settings), or a default icon based on the mode {@link Kind} and {@link #getType}.
+     */
     @NonNull
-    public ListenableFuture<Drawable> getIcon(@NonNull Context context,
-            @NonNull ZenIconLoader iconLoader) {
-        if (mIsManualDnd) {
-            return Futures.immediateFuture(requireNonNull(
-                    context.getDrawable(R.drawable.ic_do_not_disturb_on_24dp)));
+    public ZenIcon.Key getIconKey() {
+        if (isManualDnd()) {
+            return ZenIconKeys.MANUAL_DND;
         }
+        if (mRule.getIconResId() != 0) {
+            if (isSystemOwned()) {
+                // System-owned rules can only have system icons.
+                return ZenIcon.Key.forSystemResource(mRule.getIconResId());
+            } else {
+                // Technically, the icon of an app-provided rule could be a system icon if the
+                // user chose one with the picker. However, we cannot know for sure.
+                return new ZenIcon.Key(mRule.getPackageName(), mRule.getIconResId());
+            }
+        } else {
+            // Using a default icon (which is always a system icon).
+            if (mKind == Kind.IMPLICIT) {
+                return ZenIconKeys.IMPLICIT_MODE_DEFAULT;
+            } else {
+                return ZenIconKeys.forType(getType());
+            }
+        }
+    }
 
-        return iconLoader.getIcon(context, mRule);
+    /** Returns the interruption filter of the mode. */
+    @NotificationManager.InterruptionFilter
+    public int getInterruptionFilter() {
+        return mRule.getInterruptionFilter();
+    }
+
+    /**
+     * Sets the interruption filter of the mode. This is valid for {@link AutomaticZenRule}-backed
+     * modes (and not manual DND).
+     */
+    public void setInterruptionFilter(@NotificationManager.InterruptionFilter int filter) {
+        if (isManualDnd() || !canEditPolicy()) {
+            throw new IllegalStateException("Cannot update interruption filter for mode " + this);
+        }
+        mRule.setInterruptionFilter(filter);
     }
 
     @NonNull
@@ -239,10 +303,12 @@
                 return requireNonNull(mRule.getZenPolicy());
 
             case NotificationManager.INTERRUPTION_FILTER_ALARMS:
-                return POLICY_INTERRUPTION_FILTER_ALARMS;
+                return new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy()).build()
+                        .overwrittenWith(ZenPolicy.getBasePolicyInterruptionFilterAlarms());
 
             case NotificationManager.INTERRUPTION_FILTER_NONE:
-                return POLICY_INTERRUPTION_FILTER_NONE;
+                return new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy()).build()
+                        .overwrittenWith(ZenPolicy.getBasePolicyInterruptionFilterNone());
 
             case NotificationManager.INTERRUPTION_FILTER_UNKNOWN:
             default:
@@ -259,6 +325,10 @@
      */
     @SuppressLint("WrongConstant")
     public void setPolicy(@NonNull ZenPolicy policy) {
+        if (!canEditPolicy()) {
+            throw new IllegalStateException("Cannot update ZenPolicy for mode " + this);
+        }
+
         ZenPolicy currentPolicy = getPolicy();
         if (currentPolicy.equals(policy)) {
             return;
@@ -275,6 +345,12 @@
         mRule.setZenPolicy(policy);
     }
 
+    /**
+     * Returns the {@link ZenDeviceEffects} of the mode.
+     *
+     * <p>This is never {@code null}; if the backing AutomaticZenRule doesn't have effects set then
+     * a default (empty) effects set is returned.
+     */
     @NonNull
     public ZenDeviceEffects getDeviceEffects() {
         return mRule.getDeviceEffects() != null
@@ -282,6 +358,15 @@
                 : new ZenDeviceEffects.Builder().build();
     }
 
+    /** Sets the {@link ZenDeviceEffects} of the mode. */
+    public void setDeviceEffects(@NonNull ZenDeviceEffects effects) {
+        checkNotNull(effects);
+        if (!canEditPolicy()) {
+            throw new IllegalStateException("Cannot update device effects for mode " + this);
+        }
+        mRule.setDeviceEffects(effects);
+    }
+
     public void setCustomModeConditionId(Context context, Uri conditionId) {
         checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
                 "Trying to change condition of non-system-owned rule %s (to %s)",
@@ -324,12 +409,30 @@
         return !isManualDnd();
     }
 
+    /**
+     * Whether the mode has an editable policy. Calling {@link #setPolicy},
+     * {@link #setDeviceEffects}, or {@link #setInterruptionFilter} is not valid for modes with a
+     * read-only policy.
+     */
+    public boolean canEditPolicy() {
+        // Cannot edit the policy of a temporarily active non-PRIORITY DND mode.
+        // Note that it's fine to edit the policy of an *AutomaticZenRule* with non-PRIORITY filter;
+        // the filter will we set to PRIORITY if you do.
+        return !isManualDndWithSpecialFilter();
+    }
+
     public boolean canBeDeleted() {
         return !isManualDnd();
     }
 
     public boolean isManualDnd() {
-        return mIsManualDnd;
+        return mKind == Kind.MANUAL_DND;
+    }
+
+    private boolean isManualDndWithSpecialFilter() {
+        return isManualDnd()
+                && (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALARMS
+                || mRule.getInterruptionFilter() == INTERRUPTION_FILTER_NONE);
     }
 
     /**
@@ -347,6 +450,18 @@
         return mRule.isEnabled();
     }
 
+    /**
+     * Enables or disables the mode.
+     *
+     * <p>The DND mode cannot be disabled; trying to do so will fail.
+     */
+    public void setEnabled(boolean enabled) {
+        if (isManualDnd()) {
+            throw new IllegalStateException("Cannot update enabled for manual DND mode " + this);
+        }
+        mRule.setEnabled(enabled);
+    }
+
     public boolean isActive() {
         return mStatus == Status.ENABLED_AND_ACTIVE;
     }
@@ -360,18 +475,18 @@
         return obj instanceof ZenMode other
                 && mId.equals(other.mId)
                 && mRule.equals(other.mRule)
-                && mStatus.equals(other.mStatus)
-                && mIsManualDnd == other.mIsManualDnd;
+                && mKind.equals(other.mKind)
+                && mStatus.equals(other.mStatus);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mId, mRule, mStatus, mIsManualDnd);
+        return Objects.hash(mId, mRule, mKind, mStatus);
     }
 
     @Override
     public String toString() {
-        return mId + " (" + mStatus + ") -> " + mRule;
+        return mId + " (" + mKind + ", " + mStatus + ") -> " + mRule;
     }
 
     @Override
@@ -383,8 +498,8 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mId);
         dest.writeParcelable(mRule, 0);
+        dest.writeString(mKind.name());
         dest.writeString(mStatus.name());
-        dest.writeBoolean(mIsManualDnd);
     }
 
     public static final Creator<ZenMode> CREATOR = new Creator<ZenMode>() {
@@ -394,8 +509,8 @@
                     in.readString(),
                     checkNotNull(in.readParcelable(AutomaticZenRule.class.getClassLoader(),
                             AutomaticZenRule.class)),
-                    Status.valueOf(in.readString()),
-                    in.readBoolean());
+                    Kind.valueOf(in.readString()),
+                    Status.valueOf(in.readString()));
         }
 
         @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
index 492828d..71e03c1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/modes/ZenModesBackend.java
@@ -16,6 +16,11 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.app.NotificationManager.zenModeToInterruptionFilter;
+import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
+
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AutomaticZenRule;
@@ -34,7 +39,6 @@
 
 import java.time.Duration;
 import java.util.ArrayList;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 
@@ -92,10 +96,7 @@
             }
         }
 
-        // Manual DND first, then alphabetically.
-        modes.sort(Comparator.comparing(ZenMode::isManualDnd).reversed()
-                .thenComparing(ZenMode::getName));
-
+        modes.sort(ZenMode.PRIORITIZING_COMPARATOR);
         return modes;
     }
 
@@ -116,14 +117,22 @@
 
     private ZenMode getManualDndMode(ZenModeConfig config) {
         ZenModeConfig.ZenRule manualRule = config.manualRule;
+
+        // If DND is currently on with an interruption filter other than PRIORITY, construct the
+        // rule with that. DND will be *non-editable* while in this state.
+        int dndInterruptionFilter = config.isManualActive()
+                ? zenModeToInterruptionFilter(manualRule.zenMode)
+                : INTERRUPTION_FILTER_PRIORITY;
+
         AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder(
-                mContext.getString(R.string.zen_mode_settings_title), manualRule.conditionId)
-                .setType(manualRule.type)
+                mContext.getString(R.string.zen_mode_do_not_disturb_name), manualRule.conditionId)
+                .setPackage(PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_OTHER)
                 .setZenPolicy(manualRule.zenPolicy)
                 .setDeviceEffects(manualRule.zenDeviceEffects)
-                .setManualInvocationAllowed(manualRule.allowManualInvocation)
+                .setManualInvocationAllowed(true)
                 .setConfigurationActivity(null) // No further settings
-                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
+                .setInterruptionFilter(dndInterruptionFilter)
                 .build();
 
         return ZenMode.manualDndMode(manualDndRule, config.isManualActive());
@@ -154,7 +163,7 @@
                 durationConditionId = ZenModeConfig.toTimeCondition(mContext,
                         (int) forDuration.toMinutes(), ActivityManager.getCurrentUser(), true).id;
             }
-            mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+            mNotificationManager.setZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                     durationConditionId, TAG, /* fromUser= */ true);
 
         } else {
@@ -174,7 +183,6 @@
             mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
                     /* fromUser= */ true);
         } else {
-            // TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
             mNotificationManager.setAutomaticZenRuleState(mode.getId(),
                     new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
                             Condition.SOURCE_USER_ACTION));
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
index 69c7410..d71b337 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java
@@ -181,14 +181,14 @@
      * admin status.
      */
     public Dialog createDialog(Activity activity,
-            ActivityStarter activityStarter, boolean isMultipleAdminEnabled,
+            @NonNull ActivityStarter activityStarter, boolean canCreateAdminUser,
             NewUserData successCallback, Runnable cancelCallback) {
         mActivity = activity;
         mCustomDialogHelper = new CustomDialogHelper(activity);
         mSuccessCallback = successCallback;
         mCancelCallback = cancelCallback;
         mActivityStarter = activityStarter;
-        addCustomViews(isMultipleAdminEnabled);
+        addCustomViews(canCreateAdminUser);
         mUserCreationDialog = mCustomDialogHelper.getDialog();
         updateLayout();
         mUserCreationDialog.setOnDismissListener(view -> finish());
@@ -197,19 +197,19 @@
         return mUserCreationDialog;
     }
 
-    private void addCustomViews(boolean isMultipleAdminEnabled) {
+    private void addCustomViews(boolean canCreateAdminUser) {
         addGrantAdminView();
         addUserInfoEditView();
         mCustomDialogHelper.setPositiveButton(R.string.next, view -> {
             mCurrentState++;
-            if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+            if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
                 mCurrentState++;
             }
             updateLayout();
         });
         mCustomDialogHelper.setNegativeButton(R.string.back, view -> {
             mCurrentState--;
-            if (mCurrentState == GRANT_ADMIN_DIALOG && !isMultipleAdminEnabled) {
+            if (mCurrentState == GRANT_ADMIN_DIALOG && !canCreateAdminUser) {
                 mCurrentState--;
             }
             updateLayout();
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
index 46f2290..c4c4ed8e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java
@@ -31,6 +31,7 @@
 import android.widget.EditText;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
@@ -126,9 +127,11 @@
      * @param activityStarter - ActivityStarter is called with appropriate intents and request
      *                        codes to take photo/choose photo/crop photo.
      */
-    public Dialog createDialog(Activity activity, ActivityStarter activityStarter,
-            @Nullable Drawable oldUserIcon, String defaultUserName,
-            BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) {
+    public @NonNull Dialog createDialog(@NonNull Activity activity,
+            @NonNull ActivityStarter activityStarter, @Nullable Drawable oldUserIcon,
+            @Nullable String defaultUserName,
+            @Nullable BiConsumer<String, Drawable> successCallback,
+            @Nullable Runnable cancelCallback) {
         LayoutInflater inflater = LayoutInflater.from(activity);
         View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null);
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
index cdc3f12..f38e91a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java
@@ -32,6 +32,7 @@
 
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.R;
 import com.android.settingslib.utils.ThreadUtils;
 
 import com.google.common.util.concurrent.FutureCallback;
@@ -132,6 +133,13 @@
         intent.addCategory(Intent.CATEGORY_DEFAULT);
         if (Flags.avatarSync()) {
             intent.putExtra(EXTRA_IS_USER_NEW, isUserNew);
+            // Fix vulnerability b/341688848 by explicitly set the class name of avatar picker.
+            if (Flags.fixAvatarCrossUserLeak()) {
+                final String packageName =
+                        mActivity.getString(R.string.config_avatar_picker_package);
+                final String className = mActivity.getString(R.string.config_avatar_picker_class);
+                intent.setClassName(packageName, className);
+            }
         } else {
             // SettingsLib is used by multiple apps therefore we need to know out of all apps
             // using settingsLib which one is the one we return value to.
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
deleted file mode 100644
index 0b71d25..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepository.kt
+++ /dev/null
@@ -1,113 +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.settingslib.view.accessibility.data.repository
-
-import android.view.accessibility.CaptioningManager
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.ProducerScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-interface CaptioningRepository {
-
-    /** The system audio caption enabled state. */
-    val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
-
-    /** The system audio caption UI enabled state. */
-    val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
-
-    /** Sets [isSystemAudioCaptioningEnabled]. */
-    suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean)
-}
-
-class CaptioningRepositoryImpl(
-    private val captioningManager: CaptioningManager,
-    private val backgroundCoroutineContext: CoroutineContext,
-    coroutineScope: CoroutineScope,
-) : CaptioningRepository {
-
-    private val captioningChanges: SharedFlow<CaptioningChange> =
-        callbackFlow {
-                val listener = CaptioningChangeProducingListener(this)
-                captioningManager.addCaptioningChangeListener(listener)
-                awaitClose { captioningManager.removeCaptioningChangeListener(listener) }
-            }
-            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
-
-    override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> =
-        captioningChanges
-            .filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class)
-            .map { it.isEnabled }
-            .onStart { emit(captioningManager.isSystemAudioCaptioningEnabled) }
-            .stateIn(
-                coroutineScope,
-                SharingStarted.WhileSubscribed(),
-                captioningManager.isSystemAudioCaptioningEnabled,
-            )
-
-    override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> =
-        captioningChanges
-            .filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class)
-            .map { it.isEnabled }
-            .onStart { emit(captioningManager.isSystemAudioCaptioningUiEnabled) }
-            .stateIn(
-                coroutineScope,
-                SharingStarted.WhileSubscribed(),
-                captioningManager.isSystemAudioCaptioningUiEnabled,
-            )
-
-    override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
-        withContext(backgroundCoroutineContext) {
-            captioningManager.isSystemAudioCaptioningEnabled = isEnabled
-        }
-    }
-
-    private sealed interface CaptioningChange {
-
-        data class IsSystemAudioCaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
-
-        data class IsSystemUICaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
-    }
-
-    private class CaptioningChangeProducingListener(
-        private val scope: ProducerScope<CaptioningChange>
-    ) : CaptioningManager.CaptioningChangeListener() {
-
-        override fun onSystemAudioCaptioningChanged(enabled: Boolean) {
-            emitChange(CaptioningChange.IsSystemAudioCaptioningEnabled(enabled))
-        }
-
-        override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) {
-            emitChange(CaptioningChange.IsSystemUICaptioningEnabled(enabled))
-        }
-
-        private fun emitChange(change: CaptioningChange) {
-            scope.launch { scope.send(change) }
-        }
-    }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.kt
deleted file mode 100644
index 858c8b3..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/view/accessibility/domain/interactor/CaptioningInteractor.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.settingslib.view.accessibility.domain.interactor
-
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
-import kotlinx.coroutines.flow.StateFlow
-
-class CaptioningInteractor(private val repository: CaptioningRepository) {
-
-    val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
-        get() = repository.isSystemAudioCaptioningEnabled
-
-    val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
-        get() = repository.isSystemAudioCaptioningUiEnabled
-
-    suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) =
-        repository.setIsSystemAudioCaptioningEnabled(enabled)
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.kt
new file mode 100644
index 0000000..0fe385b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/model/VolumeControllerEvent.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.settingslib.volume.data.model
+
+import android.media.IVolumeController
+
+/** Models events received via [IVolumeController] */
+sealed interface VolumeControllerEvent {
+
+    /** @see [IVolumeController.displaySafeVolumeWarning] */
+    data class DisplaySafeVolumeWarning(val flags: Int) : VolumeControllerEvent
+
+    /** @see [IVolumeController.volumeChanged] */
+    data class VolumeChanged(val streamType: Int, val flags: Int) : VolumeControllerEvent
+
+    /** @see [IVolumeController.masterMuteChanged] */
+    data class MasterMuteChanged(val flags: Int) : VolumeControllerEvent
+
+    /** @see [IVolumeController.setLayoutDirection] */
+    data class SetLayoutDirection(val layoutDirection: Int) : VolumeControllerEvent
+
+    /** @see [IVolumeController.setA11yMode] */
+    data class SetA11yMode(val mode: Int) : VolumeControllerEvent
+
+    /** @see [IVolumeController.displayCsdWarning] */
+    data class DisplayCsdWarning(
+        val csdWarning: Int,
+        val displayDurationMs: Int,
+    ) : VolumeControllerEvent
+
+    /** @see [IVolumeController.dismiss] */
+    data object Dismiss : VolumeControllerEvent
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index c88c4c9..3e2d832 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -22,9 +22,13 @@
 import android.media.AudioManager
 import android.media.AudioManager.AudioDeviceCategory
 import android.media.AudioManager.OnCommunicationDeviceChangedListener
+import android.media.IVolumeController
 import android.provider.Settings
+import android.util.Log
 import androidx.concurrent.futures.DirectExecutor
 import com.android.internal.util.ConcurrentUtils
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
+import com.android.settingslib.volume.shared.AudioLogger
 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
 import com.android.settingslib.volume.shared.model.AudioManagerEvent
 import com.android.settingslib.volume.shared.model.AudioStream
@@ -35,10 +39,13 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.callbackFlow
 import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterIsInstance
@@ -72,6 +79,11 @@
      */
     val communicationDevice: StateFlow<AudioDeviceInfo?>
 
+    /** Events from [AudioManager.setVolumeController] */
+    val volumeControllerEvents: Flow<VolumeControllerEvent>
+
+    fun init()
+
     /** State of the [AudioStream]. */
     fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel>
 
@@ -89,8 +101,9 @@
     suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
 
     /** Gets audio device category. */
-    @AudioDeviceCategory
-    suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
+    @AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int
+
+    suspend fun notifyVolumeControllerVisible(isVisible: Boolean)
 }
 
 class AudioRepositoryImpl(
@@ -99,9 +112,11 @@
     private val contentResolver: ContentResolver,
     private val backgroundCoroutineContext: CoroutineContext,
     private val coroutineScope: CoroutineScope,
-    private val logger: Logger,
+    private val logger: AudioLogger,
+    shouldUseVolumeController: Boolean,
 ) : AudioRepository {
 
+    private val volumeController = ProducingVolumeController()
     private val streamSettingNames: Map<AudioStream, String> =
         mapOf(
             AudioStream(AudioManager.STREAM_VOICE_CALL) to Settings.System.VOLUME_VOICE,
@@ -115,6 +130,13 @@
             AudioStream(AudioManager.STREAM_ASSISTANT) to Settings.System.VOLUME_ASSISTANT,
         )
 
+    override val volumeControllerEvents: Flow<VolumeControllerEvent> =
+        if (shouldUseVolumeController) {
+            volumeController.events
+        } else {
+            emptyFlow()
+        }
+
     override val mode: StateFlow<Int> =
         callbackFlow {
                 val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) }
@@ -158,6 +180,14 @@
                     audioManager.communicationDevice,
                 )
 
+    override fun init() {
+        try {
+            audioManager.volumeController = volumeController
+        } catch (error: SecurityException) {
+            Log.wtf("AudioManager", "Unable to set the volume controller", error)
+        }
+    }
+
     override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
         return merge(
                 audioManagerEventsReceiver.events.filter {
@@ -168,10 +198,12 @@
                     }
                 },
                 volumeSettingChanges(audioStream),
+                volumeControllerEvents.filter { it is VolumeControllerEvent.VolumeChanged },
             )
             .conflate()
             .map { getCurrentAudioStream(audioStream) }
             .onStart { emit(getCurrentAudioStream(audioStream)) }
+            .distinctUntilChanged()
             .onEach { logger.onVolumeUpdateReceived(audioStream, it) }
             .flowOn(backgroundCoroutineContext)
     }
@@ -227,6 +259,12 @@
         }
     }
 
+    override suspend fun notifyVolumeControllerVisible(isVisible: Boolean) {
+        withContext(backgroundCoroutineContext) {
+            audioManager.notifyVolumeControllerVisible(volumeController, isVisible)
+        }
+    }
+
     private fun getMinVolume(stream: AudioStream): Int =
         try {
             audioManager.getStreamMinVolume(stream.value)
@@ -251,11 +289,46 @@
             awaitClose { contentResolver.unregisterContentObserver(observer) }
         }
     }
+}
 
-    interface Logger {
+private class ProducingVolumeController : IVolumeController.Stub() {
 
-        fun onSetVolumeRequested(audioStream: AudioStream, volume: Int)
+    private val mutableEvents = MutableSharedFlow<VolumeControllerEvent>(extraBufferCapacity = 32)
+    val events = mutableEvents.asSharedFlow()
 
-        fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel)
+    override fun displaySafeVolumeWarning(flags: Int) {
+        mutableEvents.tryEmit(VolumeControllerEvent.DisplaySafeVolumeWarning(flags))
+    }
+
+    override fun volumeChanged(streamType: Int, flags: Int) {
+        mutableEvents.tryEmit(VolumeControllerEvent.VolumeChanged(streamType, flags))
+    }
+
+    override fun masterMuteChanged(flags: Int) {
+        mutableEvents.tryEmit(VolumeControllerEvent.MasterMuteChanged(flags))
+    }
+
+    override fun setLayoutDirection(layoutDirection: Int) {
+        mutableEvents.tryEmit(VolumeControllerEvent.SetLayoutDirection(layoutDirection))
+    }
+
+    override fun dismiss() {
+        mutableEvents.tryEmit(VolumeControllerEvent.Dismiss)
+    }
+
+    override fun setA11yMode(mode: Int) {
+        mutableEvents.tryEmit(VolumeControllerEvent.SetA11yMode(mode))
+    }
+
+    override fun displayCsdWarning(
+        csdWarning: Int,
+        displayDurationMs: Int,
+    ) {
+        mutableEvents.tryEmit(
+            VolumeControllerEvent.DisplayCsdWarning(
+                csdWarning,
+                displayDurationMs,
+            )
+        )
     }
 }
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 7a66335..2f8105a 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
@@ -34,6 +34,7 @@
 import com.android.settingslib.bluetooth.onSourceConnectedOrRemoved
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MAX
 import com.android.settingslib.volume.data.repository.AudioSharingRepository.Companion.AUDIO_SHARING_VOLUME_MIN
+import com.android.settingslib.volume.shared.AudioSharingLogger
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +51,7 @@
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.runningFold
 import kotlinx.coroutines.flow.stateIn
@@ -61,7 +63,7 @@
 /** Provides audio sharing functionality. */
 interface AudioSharingRepository {
     /** Whether the device is in audio sharing. */
-    val inAudioSharing: Flow<Boolean>
+    val inAudioSharing: StateFlow<Boolean>
 
     /** The primary headset groupId in audio sharing. */
     val primaryGroupId: StateFlow<Int>
@@ -90,6 +92,7 @@
     private val btManager: LocalBluetoothManager,
     private val coroutineScope: CoroutineScope,
     private val backgroundCoroutineContext: CoroutineContext,
+    private val logger: AudioSharingLogger
 ) : AudioSharingRepository {
     private val isAudioSharingProfilesReady: StateFlow<Boolean> =
         btManager.profileManager.onServiceStateChanged
@@ -98,17 +101,19 @@
             .flowOn(backgroundCoroutineContext)
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), false)
 
-    override val inAudioSharing: Flow<Boolean> =
+    override val inAudioSharing: StateFlow<Boolean> =
         isAudioSharingProfilesReady.flatMapLatest { ready ->
             if (ready) {
                 btManager.profileManager.leAudioBroadcastProfile.onBroadcastStartedOrStopped
                     .map { isBroadcasting() }
                     .onStart { emit(isBroadcasting()) }
+                    .onEach { logger.onAudioSharingStateChanged(it) }
                     .flowOn(backgroundCoroutineContext)
             } else {
                 flowOf(false)
             }
         }
+            .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), false)
 
     private val primaryChange: Flow<Unit> = callbackFlow {
         val callback =
@@ -156,6 +161,7 @@
                 .map { getSecondaryGroupId() },
             primaryGroupId.map { getSecondaryGroupId() })
             .onStart { emit(getSecondaryGroupId()) }
+            .onEach { logger.onSecondaryGroupIdChanged(it) }
             .flowOn(backgroundCoroutineContext)
             .stateIn(
                 coroutineScope,
@@ -202,6 +208,7 @@
                             acc
                         }
                     }
+                    .onEach { logger.onVolumeMapChanged(it) }
                     .flowOn(backgroundCoroutineContext)
             } else {
                 emptyFlow()
@@ -220,6 +227,7 @@
                     BluetoothUtils.getSecondaryDeviceForBroadcast(contentResolver, btManager)
                 if (cachedDevice != null) {
                     it.setDeviceVolume(cachedDevice.device, volume, /* isGroupOp= */ true)
+                    logger.onSetDeviceVolumeRequested(volume)
                 }
             }
         }
@@ -247,7 +255,7 @@
 }
 
 class AudioSharingRepositoryEmptyImpl : AudioSharingRepository {
-    override val inAudioSharing: Flow<Boolean> = flowOf(false)
+    override val inAudioSharing: StateFlow<Boolean> = MutableStateFlow(false)
     override val primaryGroupId: StateFlow<Int> =
         MutableStateFlow(BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
     override val secondaryGroupId: StateFlow<Int> =
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.kt
new file mode 100644
index 0000000..84f7fcb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioLogger.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.settingslib.volume.shared
+
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.android.settingslib.volume.shared.model.AudioStreamModel
+
+/** A log interface for audio streams volume events. */
+interface AudioLogger {
+    fun onSetVolumeRequested(audioStream: AudioStream, volume: Int)
+
+    fun onVolumeUpdateReceived(audioStream: AudioStream, model: AudioStreamModel)
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.kt
new file mode 100644
index 0000000..18a4c6d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioSharingLogger.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.settingslib.volume.shared
+
+/** A log interface for audio sharing volume events. */
+interface AudioSharingLogger {
+
+    fun onAudioSharingStateChanged(state: Boolean)
+
+    fun onSecondaryGroupIdChanged(groupId: Int)
+
+    fun onVolumeMapChanged(map: Map<Int, Int>)
+
+    fun onSetDeviceVolumeRequested(volume: Int)
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 33d23a3..48a1af6 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -72,3 +72,10 @@
     dxflags: ["--multi-dex"],
     manifest: "AndroidManifest.xml",
 }
+
+test_module_config {
+    name: "SettingsLibTests_settingslib_users",
+    base: "SettingsLibTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.settingslib.users."],
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExtTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExtTest.kt
deleted file mode 100644
index 83b612d..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/data/repository/AudioManagerVolumeControllerExtTest.kt
+++ /dev/null
@@ -1,95 +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.settingslib.media.data.repository
-
-import android.media.AudioManager
-import android.media.IVolumeController
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-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.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AudioManagerVolumeControllerExtTest {
-
-    private val testScope = TestScope()
-
-    @Captor private lateinit var volumeControllerCaptor: ArgumentCaptor<IVolumeController>
-    @Mock private lateinit var audioManager: AudioManager
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-    }
-
-    @Test
-    fun displaySafeVolumeWarning_emitsEvent() =
-        testEvent(VolumeControllerEvent.DisplaySafeVolumeWarning(1)) { displaySafeVolumeWarning(1) }
-
-    @Test
-    fun volumeChanged_emitsEvent() =
-        testEvent(VolumeControllerEvent.VolumeChanged(1, 2)) { volumeChanged(1, 2) }
-
-    @Test
-    fun masterMuteChanged_emitsEvent() =
-        testEvent(VolumeControllerEvent.MasterMuteChanged(1)) { masterMuteChanged(1) }
-
-    @Test
-    fun setLayoutDirection_emitsEvent() =
-        testEvent(VolumeControllerEvent.SetLayoutDirection(1)) { setLayoutDirection(1) }
-
-    @Test
-    fun setA11yMode_emitsEvent() =
-        testEvent(VolumeControllerEvent.SetA11yMode(1)) { setA11yMode(1) }
-
-    @Test
-    fun displayCsdWarning_emitsEvent() =
-        testEvent(VolumeControllerEvent.DisplayCsdWarning(1, 2)) { displayCsdWarning(1, 2) }
-
-    @Test fun dismiss_emitsEvent() = testEvent(VolumeControllerEvent.Dismiss) { dismiss() }
-
-    private fun testEvent(
-        expectedEvent: VolumeControllerEvent,
-        emit: IVolumeController.() -> Unit,
-    ) =
-        testScope.runTest {
-            var event: VolumeControllerEvent? = null
-            audioManager.volumeControllerEvents().onEach { event = it }.launchIn(backgroundScope)
-            runCurrent()
-            verify(audioManager).volumeController = volumeControllerCaptor.capture()
-
-            volumeControllerCaptor.value.emit()
-            runCurrent()
-
-            assertThat(event).isEqualTo(expectedEvent)
-        }
-}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt
deleted file mode 100644
index a5233e7..0000000
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/view/accessibility/data/repository/CaptioningRepositoryTest.kt
+++ /dev/null
@@ -1,113 +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.settingslib.view.accessibility.data.repository
-
-import android.view.accessibility.CaptioningManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-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.ArgumentCaptor
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@Suppress("UnspecifiedRegisterReceiverFlag")
-@RunWith(AndroidJUnit4::class)
-class CaptioningRepositoryTest {
-
-    @Captor
-    private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener>
-
-    @Mock private lateinit var captioningManager: CaptioningManager
-
-    private lateinit var underTest: CaptioningRepository
-
-    private val testScope = TestScope()
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-
-        underTest =
-            CaptioningRepositoryImpl(
-                captioningManager,
-                testScope.testScheduler,
-                testScope.backgroundScope
-            )
-    }
-
-    @Test
-    fun isSystemAudioCaptioningEnabled_change_repositoryEmits() {
-        testScope.runTest {
-            `when`(captioningManager.isEnabled).thenReturn(false)
-            val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>()
-            underTest.isSystemAudioCaptioningEnabled
-                .onEach { isSystemAudioCaptioningEnabled.add(it) }
-                .launchIn(backgroundScope)
-            runCurrent()
-
-            triggerOnSystemAudioCaptioningChange()
-            runCurrent()
-
-            assertThat(isSystemAudioCaptioningEnabled)
-                .containsExactlyElementsIn(listOf(false, true))
-                .inOrder()
-        }
-    }
-
-    @Test
-    fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() {
-        testScope.runTest {
-            `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false)
-            val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>()
-            underTest.isSystemAudioCaptioningUiEnabled
-                .onEach { isSystemAudioCaptioningUiEnabled.add(it) }
-                .launchIn(backgroundScope)
-            runCurrent()
-
-            triggerSystemAudioCaptioningUiChange()
-            runCurrent()
-
-            assertThat(isSystemAudioCaptioningUiEnabled)
-                .containsExactlyElementsIn(listOf(false, true))
-                .inOrder()
-        }
-    }
-
-    private fun triggerSystemAudioCaptioningUiChange(enabled: Boolean = true) {
-        verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
-        listenerCaptor.value.onSystemAudioCaptioningUiChanged(enabled)
-    }
-
-    private fun triggerOnSystemAudioCaptioningChange(enabled: Boolean = true) {
-        verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
-        listenerCaptor.value.onSystemAudioCaptioningChanged(enabled)
-    }
-}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 0e43acb..52e6391 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -44,6 +44,7 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
@@ -111,6 +112,7 @@
                 testScope.testScheduler,
                 testScope.backgroundScope,
                 logger,
+                true,
             )
     }
 
@@ -261,8 +263,8 @@
     @Test
     fun getBluetoothAudioDeviceCategory() {
         testScope.runTest {
-            `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78")).thenReturn(
-                AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
+            `when`(audioManager.getBluetoothAudioDeviceCategory("12:34:56:78"))
+                .thenReturn(AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES)
 
             val category = underTest.getBluetoothAudioDeviceCategory("12:34:56:78")
             runCurrent()
@@ -271,6 +273,27 @@
         }
     }
 
+    @Test
+    fun useVolumeControllerDisabled_setVolumeController_notCalled() {
+        testScope.runTest {
+            underTest =
+                AudioRepositoryImpl(
+                    eventsReceiver,
+                    audioManager,
+                    contentResolver,
+                    testScope.testScheduler,
+                    testScope.backgroundScope,
+                    logger,
+                    false,
+                )
+
+            underTest.volumeControllerEvents.launchIn(backgroundScope)
+            runCurrent()
+
+            verify(audioManager, never()).volumeController = any()
+        }
+    }
+
     private fun triggerConnectedDeviceChange(communicationDevice: AudioDeviceInfo?) {
         verify(audioManager)
             .addOnCommunicationDeviceChangedListener(
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryVolumeControllerEventsTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryVolumeControllerEventsTest.kt
new file mode 100644
index 0000000..f5c2f01
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryVolumeControllerEventsTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.volume.data.repository
+
+import android.content.ContentResolver
+import android.media.AudioManager
+import android.media.IVolumeController
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+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.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AudioRepositoryVolumeControllerEventsTest {
+
+    private val testScope = TestScope()
+
+    @Captor private lateinit var volumeControllerCaptor: ArgumentCaptor<IVolumeController>
+    @Mock private lateinit var audioManager: AudioManager
+    @Mock private lateinit var contentResolver: ContentResolver
+
+    private val logger = FakeAudioRepositoryLogger()
+    private val eventsReceiver = FakeAudioManagerEventsReceiver()
+
+    private lateinit var underTest: AudioRepository
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest =
+            AudioRepositoryImpl(
+                eventsReceiver,
+                audioManager,
+                contentResolver,
+                testScope.testScheduler,
+                testScope.backgroundScope,
+                logger,
+                true,
+            )
+    }
+
+    @Test
+    fun displaySafeVolumeWarning_emitsEvent() =
+        testEvent(VolumeControllerEvent.DisplaySafeVolumeWarning(1)) { displaySafeVolumeWarning(1) }
+
+    @Test
+    fun volumeChanged_emitsEvent() =
+        testEvent(VolumeControllerEvent.VolumeChanged(1, 2)) { volumeChanged(1, 2) }
+
+    @Test
+    fun masterMuteChanged_emitsEvent() =
+        testEvent(VolumeControllerEvent.MasterMuteChanged(1)) { masterMuteChanged(1) }
+
+    @Test
+    fun setLayoutDirection_emitsEvent() =
+        testEvent(VolumeControllerEvent.SetLayoutDirection(1)) { setLayoutDirection(1) }
+
+    @Test
+    fun setA11yMode_emitsEvent() =
+        testEvent(VolumeControllerEvent.SetA11yMode(1)) { setA11yMode(1) }
+
+    @Test
+    fun displayCsdWarning_emitsEvent() =
+        testEvent(VolumeControllerEvent.DisplayCsdWarning(1, 2)) { displayCsdWarning(1, 2) }
+
+    @Test fun dismiss_emitsEvent() = testEvent(VolumeControllerEvent.Dismiss) { dismiss() }
+
+    private fun testEvent(
+        expectedEvent: VolumeControllerEvent,
+        emit: IVolumeController.() -> Unit,
+    ) =
+        testScope.runTest {
+            var event: VolumeControllerEvent? = null
+            underTest.volumeControllerEvents.onEach { event = it }.launchIn(backgroundScope)
+            runCurrent()
+            verify(audioManager).volumeController = volumeControllerCaptor.capture()
+
+            volumeControllerCaptor.value.emit()
+            runCurrent()
+
+            assertThat(event).isEqualTo(expectedEvent)
+        }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
index 078f0c8..8c5a085 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioSharingRepositoryTest.kt
@@ -48,6 +48,7 @@
 import kotlinx.coroutines.test.TestScope
 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
@@ -101,7 +102,7 @@
 
     @Captor
     private lateinit var assistantCallbackCaptor:
-        ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback>
+            ArgumentCaptor<BluetoothLeBroadcastAssistant.Callback>
 
     @Captor private lateinit var btCallbackCaptor: ArgumentCaptor<BluetoothCallback>
 
@@ -110,6 +111,7 @@
     @Captor
     private lateinit var volumeCallbackCaptor: ArgumentCaptor<BluetoothVolumeControl.Callback>
 
+    private val logger = FakeAudioSharingRepositoryLogger()
     private val testScope = TestScope()
     private val context: Context = ApplicationProvider.getApplicationContext()
     @Spy private val contentResolver: ContentResolver = context.contentResolver
@@ -135,16 +137,23 @@
         Settings.Secure.putInt(
             contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-            TEST_GROUP_ID_INVALID)
+            TEST_GROUP_ID_INVALID
+        )
         underTest =
             AudioSharingRepositoryImpl(
                 contentResolver,
                 btManager,
                 testScope.backgroundScope,
                 testScope.testScheduler,
+                logger
             )
     }
 
+    @After
+    fun tearDown() {
+        logger.reset()
+    }
+
     @Test
     fun audioSharingStateChange_profileReady_emitValues() {
         testScope.runTest {
@@ -160,6 +169,13 @@
             runCurrent()
 
             Truth.assertThat(states).containsExactly(false, true, false, true)
+            Truth.assertThat(logger.logs)
+                .containsAtLeastElementsIn(
+                    listOf(
+                        "onAudioSharingStateChanged state=true",
+                        "onAudioSharingStateChanged state=false",
+                    )
+                ).inOrder()
         }
     }
 
@@ -187,7 +203,8 @@
             Truth.assertThat(groupIds)
                 .containsExactly(
                     TEST_GROUP_ID_INVALID,
-                    TEST_GROUP_ID2)
+                    TEST_GROUP_ID2
+                )
         }
     }
 
@@ -219,13 +236,16 @@
             triggerSourceAdded()
             runCurrent()
             triggerProfileConnectionChange(
-                BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+                BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+            )
             runCurrent()
             triggerProfileConnectionChange(
-                BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO)
+                BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO
+            )
             runCurrent()
             triggerProfileConnectionChange(
-                BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)
+                BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+            )
             runCurrent()
 
             Truth.assertThat(groupIds)
@@ -235,7 +255,16 @@
                     TEST_GROUP_ID1,
                     TEST_GROUP_ID_INVALID,
                     TEST_GROUP_ID2,
-                    TEST_GROUP_ID_INVALID)
+                    TEST_GROUP_ID_INVALID
+                )
+            Truth.assertThat(logger.logs)
+                .containsAtLeastElementsIn(
+                    listOf(
+                        "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID_INVALID",
+                        "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID2",
+                        "onSecondaryGroupIdChanged groupId=$TEST_GROUP_ID1",
+                    )
+                ).inOrder()
         }
     }
 
@@ -257,11 +286,22 @@
             verify(volumeControl).unregisterCallback(any())
             runCurrent()
 
+            val expectedMap1 = mapOf(TEST_GROUP_ID1 to TEST_VOLUME1)
+            val expectedMap2 = mapOf(TEST_GROUP_ID1 to TEST_VOLUME2)
             Truth.assertThat(volumeMaps)
                 .containsExactly(
                     emptyMap<Int, Int>(),
-                    mapOf(TEST_GROUP_ID1 to TEST_VOLUME1),
-                    mapOf(TEST_GROUP_ID1 to TEST_VOLUME2))
+                    expectedMap1,
+                    expectedMap2
+                )
+            Truth.assertThat(logger.logs)
+                .containsAtLeastElementsIn(
+                    listOf(
+                        "onVolumeMapChanged map={}",
+                        "onVolumeMapChanged map=$expectedMap1",
+                        "onVolumeMapChanged map=$expectedMap2",
+                    )
+                ).inOrder()
         }
     }
 
@@ -281,12 +321,19 @@
             Settings.Secure.putInt(
                 contentResolver,
                 BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-                TEST_GROUP_ID2)
+                TEST_GROUP_ID2
+            )
             `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
             underTest.setSecondaryVolume(TEST_VOLUME1)
 
             runCurrent()
             verify(volumeControl).setDeviceVolume(device1, TEST_VOLUME1, true)
+            Truth.assertThat(logger.logs)
+                .isEqualTo(
+                    listOf(
+                        "onSetVolumeRequested volume=$TEST_VOLUME1",
+                    )
+                )
         }
     }
 
@@ -313,7 +360,8 @@
         Settings.Secure.putInt(
             contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-            TEST_GROUP_ID1)
+            TEST_GROUP_ID1
+        )
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
         assistantCallbackCaptor.value.sourceAdded(device1, receiveState)
     }
@@ -324,7 +372,8 @@
         Settings.Secure.putInt(
             contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-            TEST_GROUP_ID1)
+            TEST_GROUP_ID1
+        )
         assistantCallbackCaptor.value.sourceRemoved(device2)
     }
 
@@ -334,7 +383,8 @@
         Settings.Secure.putInt(
             contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-            TEST_GROUP_ID1)
+            TEST_GROUP_ID1
+        )
         btCallbackCaptor.value.onProfileConnectionStateChanged(cachedDevice2, state, profile)
     }
 
@@ -343,12 +393,14 @@
             .registerContentObserver(
                 eq(Settings.Secure.getUriFor(BluetoothUtils.getPrimaryGroupIdUriForBroadcast())),
                 eq(false),
-                contentObserverCaptor.capture())
+                contentObserverCaptor.capture()
+            )
         `when`(assistant.allConnectedDevices).thenReturn(listOf(device1, device2))
         Settings.Secure.putInt(
             contentResolver,
             BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
-            TEST_GROUP_ID2)
+            TEST_GROUP_ID2
+        )
         contentObserverCaptor.value.primaryChanged()
     }
 
@@ -380,8 +432,9 @@
             onBroadcastStopped(TEST_REASON, TEST_BROADCAST_ID)
         }
         val sourceAdded:
-            BluetoothLeBroadcastAssistant.Callback.(
-                sink: BluetoothDevice, state: BluetoothLeBroadcastReceiveState) -> Unit =
+                BluetoothLeBroadcastAssistant.Callback.(
+                    sink: BluetoothDevice, state: BluetoothLeBroadcastReceiveState
+                ) -> Unit =
             { sink, state ->
                 onReceiveStateChanged(sink, TEST_SOURCE_ID, state)
             }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt
index 389bf53..bd573fb 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioRepositoryLogger.kt
@@ -16,10 +16,11 @@
 
 package com.android.settingslib.volume.data.repository
 
+import com.android.settingslib.volume.shared.AudioLogger
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 
-class FakeAudioRepositoryLogger : AudioRepositoryImpl.Logger {
+class FakeAudioRepositoryLogger : AudioLogger {
 
     private val mutableLogs: MutableList<String> = mutableListOf()
     val logs: List<String>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.kt
new file mode 100644
index 0000000..cc4cc8d
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/FakeAudioSharingRepositoryLogger.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.settingslib.volume.data.repository
+
+import com.android.settingslib.volume.shared.AudioSharingLogger
+import java.util.concurrent.CopyOnWriteArrayList
+
+class FakeAudioSharingRepositoryLogger : AudioSharingLogger {
+    private val mutableLogs = CopyOnWriteArrayList<String>()
+    val logs: List<String>
+        get() = mutableLogs.toList()
+
+    fun reset() {
+        mutableLogs.clear()
+    }
+
+    override fun onAudioSharingStateChanged(state: Boolean) {
+        mutableLogs.add("onAudioSharingStateChanged state=$state")
+    }
+
+    override fun onSecondaryGroupIdChanged(groupId: Int) {
+        mutableLogs.add("onSecondaryGroupIdChanged groupId=$groupId")
+    }
+
+    override fun onVolumeMapChanged(map: GroupIdToVolumes) {
+        mutableLogs.add("onVolumeMapChanged map=$map")
+    }
+
+    override fun onSetDeviceVolumeRequested(volume: Int) {
+        mutableLogs.add("onSetVolumeRequested volume=$volume")
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 75c40bf..f380e7f 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -56,6 +56,7 @@
         "flag-junit",
         "settingslib_media_flags_lib",
         "settingslib_illustrationpreference_flags_lib",
+        "settingslib_selectorwithwidgetpreference_flags_lib",
         "testng", // TODO: remove once JUnit on Android provides assertThrows
     ],
     java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 4551f1e..8eedb35 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
 import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -31,16 +33,20 @@
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.net.Uri;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings;
 import android.util.Pair;
 
+import com.android.internal.R;
 import com.android.settingslib.widget.AdaptiveIcon;
 
 import com.google.common.collect.ImmutableList;
@@ -70,10 +76,14 @@
     @Mock private BluetoothDevice mBluetoothDevice;
     @Mock private AudioManager mAudioManager;
     @Mock private PackageManager mPackageManager;
+    @Mock private LeAudioProfile mA2dpProfile;
+    @Mock private LeAudioProfile mLeAudioProfile;
+    @Mock private LeAudioProfile mHearingAid;
     @Mock private LocalBluetoothLeBroadcast mBroadcast;
     @Mock private LocalBluetoothProfileManager mProfileManager;
     @Mock private LocalBluetoothManager mLocalBluetoothManager;
     @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
+    @Mock private CachedBluetoothDeviceManager mDeviceManager;
     @Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
 
     private Context mContext;
@@ -88,6 +98,7 @@
                     + "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
     private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
     private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
+    private static final int TEST_BROADCAST_ID = 25;
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -98,8 +109,41 @@
         mContext = spy(RuntimeEnvironment.application);
         mSetFlagsRule.disableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
         when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
         when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+        when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
+        when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+        when(mHearingAid.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
+    }
+
+    @Test
+    public void
+            getDerivedBtClassDrawableWithDescription_isAdvancedUntetheredDevice_returnHeadset() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn(BOOL_METADATA.getBytes());
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        Pair<Drawable, String> pair =
+                BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+                        mContext, mCachedBluetoothDevice);
+
+        verify(mContext).getDrawable(R.drawable.ic_bt_headphones_a2dp);
+    }
+
+    @Test
+    public void
+            getDerivedBtClassDrawableWithDescription_notAdvancedUntetheredDevice_returnPhone() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+                .thenReturn("false".getBytes());
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getBtClass().getMajorDeviceClass())
+                .thenReturn(BluetoothClass.Device.Major.PHONE);
+        Pair<Drawable, String> pair =
+                BluetoothUtils.getDerivedBtClassDrawableWithDescription(
+                        mContext, mCachedBluetoothDevice);
+
+        verify(mContext).getDrawable(R.drawable.ic_phone);
     }
 
     @Test
@@ -658,6 +702,33 @@
     }
 
     @Test
+    public void testHasActiveLocalBroadcastSourceForBtDevice_hasActiveLocalSource() {
+        when(mBroadcast.getLatestBroadcastId()).thenReturn(TEST_BROADCAST_ID);
+        when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(TEST_BROADCAST_ID);
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+        assertThat(
+                        BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
+                .isTrue();
+    }
+
+    @Test
+    public void testHasActiveLocalBroadcastSourceForBtDevice_noActiveLocalSource() {
+        when(mLeBroadcastReceiveState.getBroadcastId()).thenReturn(UNKNOWN_VALUE_PLACEHOLDER);
+        List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+        sourceList.add(mLeBroadcastReceiveState);
+        when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(sourceList);
+
+        assertThat(
+                        BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(
+                                mBluetoothDevice, mLocalBluetoothManager))
+                .isFalse();
+    }
+
+    @Test
     public void isAvailableHearingDevice_isConnectedHearingAid_returnTure() {
         when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
@@ -756,4 +827,194 @@
                                 mContext.getContentResolver(), mLocalBluetoothManager))
                 .isEqualTo(mCachedBluetoothDevice);
     }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_nullProfiles() {
+        when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null);
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        mCachedBluetoothDevice, mLocalBluetoothManager);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_alreadyBroadcasting() {
+        when(mBroadcast.isEnabled(any())).thenReturn(true);
+
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        mCachedBluetoothDevice, mLocalBluetoothManager);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_availableDevice() {
+        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+        CachedBluetoothDevice cachedBluetoothDevice2 = mock(CachedBluetoothDevice.class);
+        when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
+
+        BluetoothDevice device1 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
+        BluetoothDevice device2 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
+
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1, device2));
+        when(mLeAudioProfile.getActiveDevices()).thenReturn(ImmutableList.of(device1));
+
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        cachedBluetoothDevice2, mLocalBluetoothManager);
+
+        assertThat(result).isTrue();
+    }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_alreadyActive() {
+        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+        CachedBluetoothDevice cachedBluetoothDevice2 = mock(CachedBluetoothDevice.class);
+        when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
+
+        BluetoothDevice device1 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
+        BluetoothDevice device2 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
+
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1, device2));
+        when(mLeAudioProfile.getActiveDevices()).thenReturn(ImmutableList.of(device1));
+
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        mCachedBluetoothDevice, mLocalBluetoothManager);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_notConnected() {
+        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+        CachedBluetoothDevice cachedBluetoothDevice2 = mock(CachedBluetoothDevice.class);
+        when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
+
+        BluetoothDevice device1 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
+        BluetoothDevice device2 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
+
+        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device2));
+        when(mLeAudioProfile.getActiveDevices()).thenReturn(ImmutableList.of(device1));
+
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        mCachedBluetoothDevice, mLocalBluetoothManager);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void testIsAvailableAudioSharingMediaBluetoothDevice_moreThanTwoConnected() {
+        when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+        CachedBluetoothDevice cachedBluetoothDevice2 = mock(CachedBluetoothDevice.class);
+        when(cachedBluetoothDevice2.getGroupId()).thenReturn(2);
+        CachedBluetoothDevice cachedBluetoothDevice3 = mock(CachedBluetoothDevice.class);
+        when(cachedBluetoothDevice3.getGroupId()).thenReturn(3);
+
+        BluetoothDevice device1 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device1)).thenReturn(mCachedBluetoothDevice);
+        BluetoothDevice device2 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device2)).thenReturn(cachedBluetoothDevice2);
+        BluetoothDevice device3 = mock(BluetoothDevice.class);
+        when(mDeviceManager.findDevice(device3)).thenReturn(cachedBluetoothDevice3);
+
+        when(mAssistant.getAllConnectedDevices())
+                .thenReturn(ImmutableList.of(device1, device2, device3));
+        when(mLeAudioProfile.getActiveDevices()).thenReturn(ImmutableList.of(device1));
+
+        boolean result =
+                isAvailableAudioSharingMediaBluetoothDevice(
+                        cachedBluetoothDevice2, mLocalBluetoothManager);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void getAudioDeviceAttributesForSpatialAudio_bleHeadset() {
+        String address = "11:22:33:44:55:66";
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
+        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+        AudioDeviceAttributes attr =
+                BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+                        mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+
+        assertThat(attr)
+                .isEqualTo(
+                        new AudioDeviceAttributes(
+                                AudioDeviceAttributes.ROLE_OUTPUT,
+                                AudioDeviceInfo.TYPE_BLE_HEADSET,
+                                address));
+    }
+
+    @Test
+    public void getAudioDeviceAttributesForSpatialAudio_bleSpeaker() {
+        String address = "11:22:33:44:55:66";
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
+        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+        AudioDeviceAttributes attr =
+                BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+                        mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER);
+
+        assertThat(attr)
+                .isEqualTo(
+                        new AudioDeviceAttributes(
+                                AudioDeviceAttributes.ROLE_OUTPUT,
+                                AudioDeviceInfo.TYPE_BLE_SPEAKER,
+                                address));
+    }
+
+    @Test
+    public void getAudioDeviceAttributesForSpatialAudio_a2dp() {
+        String address = "11:22:33:44:55:66";
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mA2dpProfile));
+        when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+        AudioDeviceAttributes attr =
+                BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+                        mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES);
+
+        assertThat(attr)
+                .isEqualTo(
+                        new AudioDeviceAttributes(
+                                AudioDeviceAttributes.ROLE_OUTPUT,
+                                AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+                                address));
+    }
+
+    @Test
+    public void getAudioDeviceAttributesForSpatialAudio_hearingAid() {
+        String address = "11:22:33:44:55:66";
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mHearingAid));
+        when(mHearingAid.isEnabled(mBluetoothDevice)).thenReturn(true);
+
+        AudioDeviceAttributes attr =
+                BluetoothUtils.getAudioDeviceAttributesForSpatialAudio(
+                        mCachedBluetoothDevice, AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID);
+
+        assertThat(attr)
+                .isEqualTo(
+                        new AudioDeviceAttributes(
+                                AudioDeviceAttributes.ROLE_OUTPUT,
+                                AudioDeviceInfo.TYPE_HEARING_AID,
+                                address));
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 7e7c76e..8cc9974 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -35,6 +35,7 @@
 import android.os.ParcelUuid;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -545,6 +546,7 @@
      * Test to verify OnDeviceUnpaired() for csip device unpair.
      */
     @Test
+    @Ignore("b/359066481")
     public void onDeviceUnpaired_unpairCsipSubDevice() {
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
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 f94f21f..698eb81 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
@@ -19,7 +19,9 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothClass;
@@ -352,4 +354,34 @@
         assertThat(mCachedDevice1.getMemberDevice()).contains(mCachedDevice3);
         assertThat(mCachedDevice1.getDevice()).isEqualTo(expectedMainBluetoothDevice);
     }
+
+    @Test
+    public void onProfileConnectionStateChangedIfProcessed_addMemberDevice_refreshUI() {
+        mCachedDevice3.setGroupId(GROUP1);
+
+        mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+                BluetoothProfile.STATE_CONNECTED);
+
+        verify(mCachedDevice1).refresh();
+    }
+
+    @Test
+    public void onProfileConnectionStateChangedIfProcessed_switchMainDevice_refreshUI() {
+        when(mDevice3.isConnected()).thenReturn(true);
+        when(mDevice2.isConnected()).thenReturn(false);
+        when(mDevice1.isConnected()).thenReturn(false);
+        mCachedDevice3.setGroupId(GROUP1);
+        mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+                BluetoothProfile.STATE_CONNECTED);
+
+        when(mDevice3.isConnected()).thenReturn(false);
+        mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice3,
+                BluetoothProfile.STATE_DISCONNECTED);
+        when(mDevice1.isConnected()).thenReturn(true);
+        mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
+                BluetoothProfile.STATE_CONNECTED);
+
+        verify(mCachedDevice3).switchMemberDeviceContent(mCachedDevice1);
+        verify(mCachedDevice3, atLeastOnce()).refresh();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS
new file mode 100644
index 0000000..134a56e
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/OWNERS
@@ -0,0 +1 @@
+include /packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreferenceTest.java
new file mode 100644
index 0000000..cc2f788
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingFooterPreferenceTest.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.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DeviceSettingFooterPreferenceTest {
+
+    @Test
+    public void getMethods() {
+        DeviceSettingFooterPreference preference =
+                new DeviceSettingFooterPreference.Builder()
+                        .setFooterText("footer_text")
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(preference.getFooterText()).isEqualTo("footer_text");
+        assertThat(preference.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        DeviceSettingFooterPreference preference =
+                new DeviceSettingFooterPreference.Builder()
+                        .setFooterText("footer_text")
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSettingFooterPreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getFooterText()).isEqualTo(preference.getFooterText());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private DeviceSettingFooterPreference writeAndRead(DeviceSettingFooterPreference preference) {
+        Parcel parcel = Parcel.obtain();
+        preference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        DeviceSettingFooterPreference fromParcel =
+                DeviceSettingFooterPreference.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java
new file mode 100644
index 0000000..5620ba3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingHelpPreferenceTest.java
@@ -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.settingslib.bluetooth.devicesettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DeviceSettingHelpPreferenceTest {
+
+    @Test
+    public void getMethods() {
+        Intent intent = new Intent();
+        DeviceSettingHelpPreference preference =
+                new DeviceSettingHelpPreference.Builder()
+                        .setIntent(intent)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        assertThat(preference.getIntent()).isSameInstanceAs(intent);
+        assertThat(preference.getExtras().getString("key1")).isEqualTo("value1");
+    }
+
+    @Test
+    public void parcelOperation() {
+        Intent intent = new Intent("intent_action");
+        DeviceSettingHelpPreference preference =
+                new DeviceSettingHelpPreference.Builder()
+                        .setIntent(intent)
+                        .setExtras(buildBundle("key1", "value1"))
+                        .build();
+
+        DeviceSettingHelpPreference fromParcel = writeAndRead(preference);
+
+        assertThat(fromParcel.getIntent().getAction())
+                .isEqualTo(preference.getIntent().getAction());
+        assertThat(fromParcel.getExtras().getString("key1"))
+                .isEqualTo(preference.getExtras().getString("key1"));
+    }
+
+    private Bundle buildBundle(String key, String value) {
+        Bundle bundle = new Bundle();
+        bundle.putString(key, value);
+        return bundle;
+    }
+
+    private DeviceSettingHelpPreference writeAndRead(DeviceSettingHelpPreference preference) {
+        Parcel parcel = Parcel.obtain();
+        preference.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        DeviceSettingHelpPreference fromParcel =
+                DeviceSettingHelpPreference.CREATOR.createFromParcel(parcel);
+        return fromParcel;
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt
index 2b29a6e..a0a2658 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsConfigTest.kt
@@ -37,9 +37,9 @@
                             "package_name_1",
                             "class_name_1",
                             "intent_action_1",
-                            Bundle()
-                        )
-                    ),
+                            null,
+                            Bundle(),
+                        )),
                 moreSettingsItems =
                     listOf(
                         DeviceSettingItem(
@@ -47,10 +47,17 @@
                             "package_name_2",
                             "class_name_2",
                             "intent_action_2",
-                            Bundle()
-                        )
-                    ),
-                moreSettingsFooter = "footer",
+                            null,
+                            Bundle(),
+                        )),
+                moreSettingsHelpItem = DeviceSettingItem(
+                    3,
+                    "package_name_2",
+                    "class_name_2",
+                    "intent_action_2",
+                    null,
+                    Bundle(),
+                ),
                 extras = Bundle().apply { putString("key1", "value1") },
             )
 
@@ -72,7 +79,10 @@
             .containsExactly("class_name_2")
         assertThat(fromParcel.moreSettingsItems.stream().map { it.intentAction }.toList())
             .containsExactly("intent_action_2")
-        assertThat(fromParcel.moreSettingsFooter).isEqualTo(config.moreSettingsFooter)
+        assertThat(fromParcel.moreSettingsHelpItem?.settingId).isEqualTo(3)
+        assertThat(fromParcel.moreSettingsHelpItem?.packageName).isEqualTo("package_name_2")
+        assertThat(fromParcel.moreSettingsHelpItem?.className).isEqualTo("class_name_2")
+        assertThat(fromParcel.moreSettingsHelpItem?.intentAction).isEqualTo("intent_action_2")
     }
 
     private fun writeAndRead(item: DeviceSettingsConfig): DeviceSettingsConfig {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.kt
new file mode 100644
index 0000000..aa22fac
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingsProviderServiceStatusTest.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.settingslib.bluetooth.devicesettings
+
+import android.os.Bundle
+import android.os.Parcel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DeviceSettingsProviderServiceStatusTest {
+
+    @Test
+    fun parcelOperation() {
+        val item =
+            DeviceSettingsProviderServiceStatus(
+                enabled = true,
+                extras = Bundle().apply { putString("key1", "value1") },
+            )
+
+        val fromParcel = writeAndRead(item)
+
+        assertThat(fromParcel.enabled).isEqualTo(item.enabled)
+        assertThat(fromParcel.extras.getString("key1")).isEqualTo(item.extras.getString("key1"))
+    }
+
+    private fun writeAndRead(
+        item: DeviceSettingsProviderServiceStatus
+    ): DeviceSettingsProviderServiceStatus {
+        val parcel = Parcel.obtain()
+        item.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+        return DeviceSettingsProviderServiceStatus.CREATOR.createFromParcel(parcel)
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index fee2394..ce155b5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -28,10 +28,12 @@
 import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreferenceState
 import com.android.settingslib.bluetooth.devicesettings.DeviceInfo
 import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingHelpPreference
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingState
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsProviderServiceStatus
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsConfigProviderService
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsListener
 import com.android.settingslib.bluetooth.devicesettings.IDeviceSettingsProviderService
@@ -40,15 +42,14 @@
 import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -57,12 +58,9 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.verify
@@ -83,9 +81,6 @@
     @Mock private lateinit var configService: IDeviceSettingsConfigProviderService.Stub
     @Mock private lateinit var settingProviderService1: IDeviceSettingsProviderService.Stub
     @Mock private lateinit var settingProviderService2: IDeviceSettingsProviderService.Stub
-    @Captor
-    private lateinit var metadataChangeCaptor:
-        ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
 
     private lateinit var underTest: DeviceSettingRepository
     private val testScope = TestScope()
@@ -96,9 +91,7 @@
         `when`(cachedDevice.address).thenReturn(BLUETOOTH_ADDRESS)
         `when`(
                 bluetoothDevice.getMetadata(
-                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
-                )
-            )
+                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
             .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
 
         `when`(configService.queryLocalInterface(anyString())).thenReturn(configService)
@@ -121,8 +114,7 @@
                     connection.onServiceConnected(
                         ComponentName(
                             SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
-                            SETTING_PROVIDER_SERVICE_CLASS_NAME_1
-                        ),
+                            SETTING_PROVIDER_SERVICE_CLASS_NAME_1),
                         settingProviderService1,
                     )
                 SETTING_PROVIDER_SERVICE_INTENT_ACTION_2 ->
@@ -154,6 +146,12 @@
     fun getDeviceSettingsConfig_withMetadata_success() {
         testScope.runTest {
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
 
             val config = underTest.getDeviceSettingsConfig(cachedDevice)
 
@@ -162,39 +160,40 @@
     }
 
     @Test
-    fun getDeviceSettingsConfig_waitMetadataChange_success() {
+    fun getDeviceSettingsConfig_noMetadata_returnNull() {
+        testScope.runTest {
+            `when`(
+                bluetoothDevice.getMetadata(
+                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn("".toByteArray())
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+
+            val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+            assertThat(config).isNull()
+        }
+    }
+
+    @Test
+    fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
         testScope.runTest {
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
-            `when`(
-                    bluetoothDevice.getMetadata(
-                        DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
-                    )
-                )
-                .thenReturn("".toByteArray())
-
-            var config: DeviceSettingConfigModel? = null
-            val job = launch { config = underTest.getDeviceSettingsConfig(cachedDevice) }
-            delay(1000)
-            verify(bluetoothAdapter)
-                .addOnMetadataChangedListener(
-                    eq(bluetoothDevice),
-                    any(),
-                    metadataChangeCaptor.capture()
-                )
-            metadataChangeCaptor.value.onMetadataChanged(
-                bluetoothDevice,
-                DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
-                BLUETOOTH_DEVICE_METADATA.toByteArray(),
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(false)
             )
-            `when`(
-                    bluetoothDevice.getMetadata(
-                        DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
-                    )
-                )
-                .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
 
-            job.join()
-            assertConfig(config!!, DEVICE_SETTING_CONFIG)
+            val config = underTest.getDeviceSettingsConfig(cachedDevice)
+
+            assertThat(config).isNull()
         }
     }
 
@@ -220,6 +219,12 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
             }
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -242,6 +247,12 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
             }
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -255,6 +266,34 @@
     }
 
     @Test
+    fun getDeviceSetting_helpPreference_success() {
+        testScope.runTest {
+            `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
+            `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
+                    input ->
+                input
+                    .getArgument<IDeviceSettingsListener>(1)
+                    .onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP))
+            }
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            var setting: DeviceSettingModel? = null
+
+            underTest
+                .getDeviceSetting(cachedDevice, DEVICE_SETTING_ID_HELP)
+                .onEach { setting = it }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            assertDeviceSetting(setting!!, DEVICE_SETTING_HELP)
+        }
+    }
+
+    @Test
     fun getDeviceSetting_noConfig_returnNull() {
         testScope.runTest {
             `when`(settingProviderService1.registerDeviceSettingsListener(any(), any())).then {
@@ -285,6 +324,12 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
             }
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -302,10 +347,8 @@
                     DeviceSettingState.Builder()
                         .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
                         .setPreferenceState(
-                            ActionSwitchPreferenceState.Builder().setChecked(false).build()
-                        )
-                        .build()
-                )
+                            ActionSwitchPreferenceState.Builder().setChecked(false).build())
+                        .build())
         }
     }
 
@@ -319,6 +362,12 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
             }
+            `when`(settingProviderService1.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
+            `when`(settingProviderService2.serviceStatus).thenReturn(
+                DeviceSettingsProviderServiceStatus(true)
+            )
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -336,10 +385,8 @@
                     DeviceSettingState.Builder()
                         .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_ANC)
                         .setPreferenceState(
-                            MultiTogglePreferenceState.Builder().setState(2).build()
-                        )
-                        .build()
-                )
+                            MultiTogglePreferenceState.Builder().setState(2).build())
+                        .build())
         }
     }
 
@@ -352,7 +399,8 @@
                 val pref = serviceResponse.preference as ActionSwitchPreference
                 assertThat(actual.title).isEqualTo(pref.title)
                 assertThat(actual.summary).isEqualTo(pref.summary)
-                assertThat(actual.icon).isEqualTo(pref.icon)
+                assertThat(actual.icon)
+                    .isEqualTo(pref.icon?.let { DeviceSettingIcon.BitmapIcon(it) })
                 assertThat(actual.isAllowedChangingState).isEqualTo(pref.isAllowedChangingState)
                 if (pref.hasSwitch()) {
                     assertThat(actual.switchState!!.checked).isEqualTo(pref.checked)
@@ -371,13 +419,20 @@
                     assertToggle(actual.toggles[i], pref.toggleInfos[i])
                 }
             }
+            is DeviceSettingModel.HelpPreference -> {
+                assertThat(serviceResponse.preference)
+                    .isInstanceOf(DeviceSettingHelpPreference::class.java)
+                val pref = serviceResponse.preference as DeviceSettingHelpPreference
+                assertThat(actual.intent).isSameInstanceAs(pref.intent)
+            }
             else -> {}
         }
     }
 
     private fun assertToggle(actual: ToggleModel, serviceResponse: ToggleInfo) {
         assertThat(actual.label).isEqualTo(serviceResponse.label)
-        assertThat(actual.icon).isEqualTo(serviceResponse.icon)
+        assertThat((actual.icon as DeviceSettingIcon.BitmapIcon).bitmap)
+            .isEqualTo(serviceResponse.icon)
     }
 
     private fun assertConfig(
@@ -392,7 +447,6 @@
         for (i in 0..<actual.moreSettingsItems.size) {
             assertConfigItem(actual.moreSettingsItems[i], serviceResponse.moreSettingsItems[i])
         }
-        assertThat(actual.moreSettingsPageFooter).isEqualTo(serviceResponse.moreSettingsFooter)
     }
 
     private fun assertConfigItem(
@@ -430,21 +484,25 @@
                 CONFIG_SERVICE_INTENT_ACTION +
                 "</DEVICE_SETTINGS_CONFIG_ACTION>"
         val DEVICE_INFO = DeviceInfo.Builder().setBluetoothAddress(BLUETOOTH_ADDRESS).build()
-
+        const val DEVICE_SETTING_ID_HELP = 12345
         val DEVICE_SETTING_ITEM_1 =
             DeviceSettingItem(
                 DeviceSettingId.DEVICE_SETTING_ID_HEADER,
                 SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
-                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1
-            )
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1)
         val DEVICE_SETTING_ITEM_2 =
             DeviceSettingItem(
                 DeviceSettingId.DEVICE_SETTING_ID_ANC,
                 SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
-                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2
-            )
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
+        val DEVICE_SETTING_HELP_ITEM =
+            DeviceSettingItem(
+                DEVICE_SETTING_ID_HELP,
+                SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
+                SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
         val DEVICE_SETTING_1 =
             DeviceSetting.Builder()
                 .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
@@ -453,8 +511,7 @@
                         .setTitle("title1")
                         .setHasSwitch(true)
                         .setAllowedChangingState(true)
-                        .build()
-                )
+                        .build())
                 .build()
         val DEVICE_SETTING_2 =
             DeviceSetting.Builder()
@@ -467,22 +524,23 @@
                             ToggleInfo.Builder()
                                 .setLabel("label1")
                                 .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
-                                .build()
-                        )
+                                .build())
                         .addToggleInfo(
                             ToggleInfo.Builder()
                                 .setLabel("label2")
                                 .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
-                                .build()
-                        )
-                        .build()
-                )
+                                .build())
+                        .build())
                 .build()
+        val DEVICE_SETTING_HELP = DeviceSetting.Builder()
+            .setSettingId(DEVICE_SETTING_ID_HELP)
+            .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build())
+            .build()
         val DEVICE_SETTING_CONFIG =
             DeviceSettingsConfig(
                 listOf(DEVICE_SETTING_ITEM_1),
                 listOf(DEVICE_SETTING_ITEM_2),
-                "footer"
+                DEVICE_SETTING_HELP_ITEM,
             )
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
index 194a0e2..4e54d8f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -40,6 +40,7 @@
 import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,6 +59,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowInteractionJankMonitor.class, SettingsJankMonitorTest.ShadowBuilder.class})
+@Ignore("b/359066481")
 public class SettingsJankMonitorTest {
     private static final String TEST_KEY = "key";
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 0d318c3..325bb2c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -36,6 +36,7 @@
 import com.android.settingslib.testutils.shadow.ShadowSmsApplication;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -86,6 +87,7 @@
         mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
     }
 
+    @Ignore("b/359066481")
     @Test
     public void testIsAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
@@ -160,6 +162,7 @@
         assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE, UID)).isTrue();
     }
 
+    @Ignore("b/359066481")
     @Test
     public void testIsSystemAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index 67c73b1..c136644 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -199,6 +199,15 @@
         }
     }
 
+    @EnableFlags(android.app.Flags.FLAG_MODES_UI)
+    @Test
+    fun getModes_returnsModes() {
+        val modesList = listOf(TestModeBuilder().setId("One").build())
+        `when`(zenModesBackend.modes).thenReturn(modesList)
+
+        assertThat(underTest.getModes()).isEqualTo(modesList)
+    }
+
     private fun triggerIntent(action: String, extras: Map<String, Parcelable>? = null) {
         verify(context).registerReceiver(receiverCaptor.capture(), any(), any(), any())
         val intent = Intent(action)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java
index 20461e3..6eb5f5b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenIconLoaderTest.java
@@ -16,17 +16,12 @@
 
 package com.android.settingslib.notification.modes;
 
-import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.AutomaticZenRule;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.service.notification.ZenPolicy;
+import android.service.notification.SystemZenRules;
 
-import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 
 import org.junit.Before;
@@ -48,44 +43,73 @@
     }
 
     @Test
-    public void getIcon_systemOwnedRuleWithIcon_loads() throws Exception {
-        AutomaticZenRule systemRule = newRuleBuilder()
-                .setPackage("android")
+    public void getIcon_systemOwnedModeWithIcon_loads() throws Exception {
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
                 .setIconResId(android.R.drawable.ic_media_play)
                 .build();
 
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, systemRule);
-        assertThat(loadFuture.isDone()).isTrue();
-        assertThat(loadFuture.get()).isNotNull();
+        ZenIcon icon = mLoader.getIcon(mContext, mode).get();
+
+        assertThat(icon.drawable()).isNotNull();
+        assertThat(icon.key().resPackage()).isNull();
+        assertThat(icon.key().resId()).isEqualTo(android.R.drawable.ic_media_play);
     }
 
     @Test
-    public void getIcon_ruleWithoutSpecificIcon_loadsFallback() throws Exception {
-        AutomaticZenRule rule = newRuleBuilder()
+    public void getIcon_modeWithoutSpecificIcon_loadsFallback() throws Exception {
+        ZenMode mode = new TestModeBuilder()
                 .setType(AutomaticZenRule.TYPE_DRIVING)
                 .setPackage("com.blah")
                 .build();
 
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
-        assertThat(loadFuture.isDone()).isTrue();
-        assertThat(loadFuture.get()).isNotNull();
+        ZenIcon icon = mLoader.getIcon(mContext, mode).get();
+
+        assertThat(icon.drawable()).isNotNull();
+        assertThat(icon.key().resPackage()).isNull();
+        assertThat(icon.key().resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_driving);
     }
 
     @Test
     public void getIcon_ruleWithAppIconWithLoadFailure_loadsFallback() throws Exception {
-        AutomaticZenRule rule = newRuleBuilder()
+        ZenMode mode = new TestModeBuilder()
                 .setType(AutomaticZenRule.TYPE_DRIVING)
                 .setPackage("com.blah")
                 .setIconResId(-123456)
                 .build();
 
-        ListenableFuture<Drawable> loadFuture = mLoader.getIcon(mContext, rule);
-        assertThat(loadFuture.get()).isNotNull();
+        ZenIcon icon = mLoader.getIcon(mContext, mode).get();
+
+        assertThat(icon.drawable()).isNotNull();
+        assertThat(icon.key().resPackage()).isNull();
+        assertThat(icon.key().resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_driving);
     }
 
-    private static AutomaticZenRule.Builder newRuleBuilder() {
-        return new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
-                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                .setZenPolicy(new ZenPolicy.Builder().build());
+    @Test
+    public void getIcon_cachesCustomIcons() throws Exception {
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setIconResId(android.R.drawable.ic_media_play)
+                .build();
+
+        ZenIcon iconOne = mLoader.getIcon(mContext, mode).get();
+        ZenIcon iconTwo = mLoader.getIcon(mContext, mode).get();
+
+        assertThat(iconOne.drawable()).isSameInstanceAs(iconTwo.drawable());
+    }
+
+    @Test
+    public void getIcon_cachesDefaultIcons() throws Exception {
+        ZenMode mode = new TestModeBuilder()
+                .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                .setType(AutomaticZenRule.TYPE_IMMERSIVE)
+                .build();
+
+        ZenIcon iconOne = mLoader.getIcon(mContext, mode).get();
+        ZenIcon iconTwo = mLoader.getIcon(mContext, mode).get();
+
+        assertThat(iconOne.drawable()).isSameInstanceAs(iconTwo.drawable());
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
index d9fdcc38..d08d91d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModeTest.java
@@ -16,18 +16,30 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.app.AutomaticZenRule.TYPE_BEDTIME;
+import static android.app.AutomaticZenRule.TYPE_DRIVING;
+import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.AutomaticZenRule.TYPE_OTHER;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_THEATER;
+import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.assertThrows;
+
 import android.app.AutomaticZenRule;
 import android.net.Uri;
 import android.os.Parcel;
 import android.service.notification.Condition;
 import android.service.notification.SystemZenRules;
+import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
 
@@ -37,6 +49,9 @@
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(RobolectricTestRunner.class)
 public class ZenModeTest {
 
@@ -45,28 +60,42 @@
     private static final AutomaticZenRule ZEN_RULE =
             new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
                     .setPackage("com.some.driving.thing")
-                    .setType(AutomaticZenRule.TYPE_DRIVING)
+                    .setType(TYPE_DRIVING)
                     .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                     .setZenPolicy(ZEN_POLICY)
                     .build();
 
+    private static final String IMPLICIT_RULE_ID = ZenModeConfig.implicitRuleId("some.package");
+    private static final AutomaticZenRule IMPLICIT_ZEN_RULE =
+            new AutomaticZenRule.Builder("Implicit", Uri.parse("implicit/some.package"))
+                    .setPackage("some.package")
+                    .setType(TYPE_OTHER)
+                    .build();
+
     @Test
-    public void testBasicMethods() {
+    public void testBasicMethods_mode() {
         ZenMode zenMode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, true));
 
         assertThat(zenMode.getId()).isEqualTo("id");
         assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
         assertThat(zenMode.isManualDnd()).isFalse();
         assertThat(zenMode.canEditNameAndIcon()).isTrue();
+        assertThat(zenMode.canEditPolicy()).isTrue();
         assertThat(zenMode.canBeDeleted()).isTrue();
         assertThat(zenMode.isActive()).isTrue();
+    }
 
-        ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false);
+    @Test
+    public void testBasicMethods_manualDnd() {
+        ZenMode manualMode = TestModeBuilder.MANUAL_DND_INACTIVE;
+
         assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID);
         assertThat(manualMode.isManualDnd()).isTrue();
         assertThat(manualMode.canEditNameAndIcon()).isFalse();
+        assertThat(manualMode.canEditPolicy()).isTrue();
         assertThat(manualMode.canBeDeleted()).isFalse();
         assertThat(manualMode.isActive()).isFalse();
+        assertThat(manualMode.getRule().getPackageName()).isEqualTo(PACKAGE_ANDROID);
     }
 
     @Test
@@ -184,7 +213,7 @@
         ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
 
         assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder()
+                new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
                         .disallowAllSounds()
                         .allowAlarms(true)
                         .allowMedia(true)
@@ -201,9 +230,8 @@
         ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));
 
         assertThat(zenMode.getPolicy()).isEqualTo(
-                new ZenPolicy.Builder()
+                new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
                         .disallowAllSounds()
-                        .hideAllVisualEffects()
                         .allowPriorityChannels(false)
                         .build());
     }
@@ -224,6 +252,99 @@
     }
 
     @Test
+    public void getInterruptionFilter_returnsFilter() {
+        ZenMode mode = new TestModeBuilder().setInterruptionFilter(
+                INTERRUPTION_FILTER_ALARMS).build();
+
+        assertThat(mode.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
+    }
+
+    @Test
+    public void setInterruptionFilter_setsFilter() {
+        ZenMode mode = new TestModeBuilder().setInterruptionFilter(
+                INTERRUPTION_FILTER_ALARMS).build();
+
+        mode.setInterruptionFilter(INTERRUPTION_FILTER_ALL);
+
+        assertThat(mode.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL);
+    }
+
+    @Test
+    public void setInterruptionFilter_manualDnd_throws() {
+        ZenMode manualDnd = TestModeBuilder.MANUAL_DND_INACTIVE;
+
+        assertThrows(IllegalStateException.class,
+                () -> manualDnd.setInterruptionFilter(INTERRUPTION_FILTER_ALL));
+    }
+
+    @Test
+    public void canEditPolicy_onlyFalseForSpecialDnd() {
+        assertThat(TestModeBuilder.EXAMPLE.canEditPolicy()).isTrue();
+        assertThat(TestModeBuilder.MANUAL_DND_ACTIVE.canEditPolicy()).isTrue();
+        assertThat(TestModeBuilder.MANUAL_DND_INACTIVE.canEditPolicy()).isTrue();
+
+        ZenMode dndWithAlarms = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, true);
+        assertThat(dndWithAlarms.canEditPolicy()).isFalse();
+        ZenMode dndWithNone = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
+        assertThat(dndWithNone.canEditPolicy()).isFalse();
+
+        // Note: Backend will never return an inactive manual mode with custom filter.
+        ZenMode badDndWithAlarms = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, false);
+        assertThat(badDndWithAlarms.canEditPolicy()).isFalse();
+        ZenMode badDndWithNone = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, false);
+        assertThat(badDndWithNone.canEditPolicy()).isFalse();
+    }
+
+    @Test
+    public void canEditPolicy_whenTrue_allowsSettingPolicyAndEffects() {
+        ZenMode normalDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_PRIORITY, true);
+
+        assertThat(normalDnd.canEditPolicy()).isTrue();
+
+        ZenPolicy somePolicy = new ZenPolicy.Builder().showBadges(true).build();
+        normalDnd.setPolicy(somePolicy);
+        assertThat(normalDnd.getPolicy()).isEqualTo(somePolicy);
+
+        ZenDeviceEffects someEffects = new ZenDeviceEffects.Builder()
+                .setShouldUseNightMode(true).build();
+        normalDnd.setDeviceEffects(someEffects);
+        assertThat(normalDnd.getDeviceEffects()).isEqualTo(someEffects);
+    }
+
+    @Test
+    public void canEditPolicy_whenFalse_preventsSettingFilterPolicyOrEffects() {
+        ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_ALARMS, true);
+
+        assertThat(specialDnd.canEditPolicy()).isFalse();
+        assertThrows(IllegalStateException.class,
+                () -> specialDnd.setPolicy(ZEN_POLICY));
+        assertThrows(IllegalStateException.class,
+                () -> specialDnd.setDeviceEffects(new ZenDeviceEffects.Builder().build()));
+    }
+
+    @Test
+    public void comparator_prioritizes() {
+        ZenMode manualDnd = TestModeBuilder.MANUAL_DND_INACTIVE;
+        ZenMode driving1 = new TestModeBuilder().setName("b1").setType(TYPE_DRIVING).build();
+        ZenMode driving2 = new TestModeBuilder().setName("b2").setType(TYPE_DRIVING).build();
+        ZenMode bedtime1 = new TestModeBuilder().setName("c1").setType(TYPE_BEDTIME).build();
+        ZenMode bedtime2 = new TestModeBuilder().setName("c2").setType(TYPE_BEDTIME).build();
+        ZenMode other = new TestModeBuilder().setName("a1").setType(TYPE_OTHER).build();
+        ZenMode immersive = new TestModeBuilder().setName("a2").setType(TYPE_IMMERSIVE).build();
+        ZenMode unknown = new TestModeBuilder().setName("a3").setType(TYPE_UNKNOWN).build();
+        ZenMode theater = new TestModeBuilder().setName("a4").setType(TYPE_THEATER).build();
+
+        ArrayList<ZenMode> list = new ArrayList<>(List.of(other, theater, bedtime1, unknown,
+                driving2, manualDnd, driving1, bedtime2, immersive));
+        list.sort(ZenMode.PRIORITIZING_COMPARATOR);
+
+        assertThat(list)
+                .containsExactly(manualDnd, bedtime1, bedtime2, driving1, driving2, other,
+                        immersive, unknown, theater)
+                .inOrder();
+    }
+
+    @Test
     public void writeToParcel_equals() {
         assertUnparceledIsEqualToOriginal("example",
                 new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, false)));
@@ -232,6 +353,104 @@
 
         assertUnparceledIsEqualToOriginal("custom_manual",
                 ZenMode.newCustomManual("New mode", R.drawable.ic_zen_mode_type_immersive));
+
+        assertUnparceledIsEqualToOriginal("implicit",
+                new ZenMode(IMPLICIT_RULE_ID, IMPLICIT_ZEN_RULE,
+                        zenConfigRuleFor(IMPLICIT_ZEN_RULE, false)));
+    }
+
+    @Test
+    public void getIconKey_normalModeWithCustomIcon_isCustomIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setType(TYPE_BEDTIME)
+                .setPackage("some.package")
+                .setIconResId(123)
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isEqualTo("some.package");
+        assertThat(iconKey.resId()).isEqualTo(123);
+    }
+
+    @Test
+    public void getIconKey_systemOwnedModeWithCustomIcon_isCustomIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setType(TYPE_SCHEDULE_CALENDAR)
+                .setPackage(PACKAGE_ANDROID)
+                .setIconResId(123)
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isNull();
+        assertThat(iconKey.resId()).isEqualTo(123);
+    }
+
+    @Test
+    public void getIconKey_implicitModeWithCustomIcon_isCustomIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setId(ZenModeConfig.implicitRuleId("some.package"))
+                .setPackage("some.package")
+                .setIconResId(123)
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isEqualTo("some.package");
+        assertThat(iconKey.resId()).isEqualTo(123);
+    }
+
+    @Test
+    public void getIconKey_manualDnd_isDndIcon() {
+        ZenIcon.Key iconKey = TestModeBuilder.MANUAL_DND_INACTIVE.getIconKey();
+
+        assertThat(iconKey.resPackage()).isNull();
+        assertThat(iconKey.resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_special_dnd);
+    }
+
+    @Test
+    public void getIconKey_normalModeWithoutCustomIcon_isModeTypeIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setType(TYPE_BEDTIME)
+                .setPackage("some.package")
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isNull();
+        assertThat(iconKey.resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_bedtime);
+    }
+
+    @Test
+    public void getIconKey_systemOwnedModeWithoutCustomIcon_isModeTypeIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setType(TYPE_SCHEDULE_CALENDAR)
+                .setPackage(PACKAGE_ANDROID)
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isNull();
+        assertThat(iconKey.resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar);
+    }
+
+    @Test
+    public void getIconKey_implicitModeWithoutCustomIcon_isDndIcon() {
+        ZenMode mode = new TestModeBuilder()
+                .setId(ZenModeConfig.implicitRuleId("some.package"))
+                .setPackage("some_package")
+                .setType(TYPE_BEDTIME) // Type should be ignored.
+                .build();
+
+        ZenIcon.Key iconKey = mode.getIconKey();
+
+        assertThat(iconKey.resPackage()).isNull();
+        assertThat(iconKey.resId()).isEqualTo(
+                com.android.internal.R.drawable.ic_zen_mode_type_special_dnd);
     }
 
     private static void assertUnparceledIsEqualToOriginal(String type, ZenMode original) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
index 00c7ae3..aae72b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/modes/ZenModesBackendTest.java
@@ -16,12 +16,14 @@
 
 package com.android.settingslib.notification.modes;
 
+import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
 import static android.service.notification.Condition.SOURCE_UNKNOWN;
 import static android.service.notification.Condition.STATE_FALSE;
 import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
 import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy;
 import static android.service.notification.ZenPolicy.STATE_ALLOW;
 
@@ -47,8 +49,6 @@
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenPolicy;
 
-import com.android.settingslib.R;
-
 import com.google.common.collect.ImmutableMap;
 
 import org.junit.Before;
@@ -123,7 +123,6 @@
         zenRule.id = id;
         zenRule.pkg = "package";
         zenRule.enabled = azr.isEnabled();
-        zenRule.snoozing = false;
         zenRule.conditionId = azr.getConditionId();
         zenRule.condition = new Condition(azr.getConditionId(), "",
                 active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
@@ -173,9 +172,8 @@
         // all modes exist, but none of them are currently active
         assertThat(modes).containsExactly(
                         ZenMode.manualDndMode(
-                                new AutomaticZenRule.Builder(
-                                        mContext.getString(R.string.zen_mode_settings_title),
-                                        Uri.EMPTY)
+                                new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+                                        .setPackage(PACKAGE_ANDROID)
                                         .setType(AutomaticZenRule.TYPE_OTHER)
                                         .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                                         .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
@@ -197,15 +195,59 @@
 
         ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
 
+        assertThat(mode).isNotNull();
         assertThat(mode).isEqualTo(
                 ZenMode.manualDndMode(
-                        new AutomaticZenRule.Builder(
-                                mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
+                        new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+                                .setPackage(PACKAGE_ANDROID)
                                 .setType(AutomaticZenRule.TYPE_OTHER)
                                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                                 .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
                                 .setManualInvocationAllowed(true)
                                 .build(), false));
+
+        assertThat(mode.isManualDnd()).isTrue();
+        assertThat(mode.isEnabled()).isTrue();
+        assertThat(mode.isActive()).isFalse();
+        assertThat(mode.canEditPolicy()).isTrue();
+        assertThat(mode.getPolicy()).isEqualTo(notificationPolicyToZenPolicy(dndPolicy));
+    }
+
+    @Test
+    public void getMode_dndWithOtherInterruptionFilter_returnsSpecialDndMode() {
+        ZenModeConfig config = configWithManualRule(new ZenModeConfig(), true);
+        config.manualRule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+        Policy dndPolicyForPriority = new Policy(Policy.PRIORITY_CATEGORY_ALARMS,
+                Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
+        config.applyNotificationPolicy(dndPolicyForPriority);
+        when(mNm.getZenModeConfig()).thenReturn(config);
+
+        ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);
+
+        assertThat(mode).isNotNull();
+        assertThat(mode).isEqualTo(
+                ZenMode.manualDndMode(
+                        new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY)
+                                .setPackage(PACKAGE_ANDROID)
+                                .setType(AutomaticZenRule.TYPE_OTHER)
+                                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                                .setZenPolicy(notificationPolicyToZenPolicy(dndPolicyForPriority))
+                                .setManualInvocationAllowed(true)
+                                .build(), true));
+
+        assertThat(mode.isManualDnd()).isTrue();
+        assertThat(mode.isEnabled()).isTrue();
+        assertThat(mode.isActive()).isTrue();
+
+        // Mode itself has a special fixed policy, different to the rule.
+        assertThat(mode.canEditPolicy()).isFalse();
+        assertThat(mode.getPolicy()).isEqualTo(
+                new ZenPolicy.Builder(ZenModeConfig.getDefaultZenPolicy())
+                        .disallowAllSounds()
+                        .allowAlarms(true)
+                        .allowMedia(true)
+                        .allowPriorityChannels(false)
+                        .build());
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 4f3b200..b549e3f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -26,6 +26,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
@@ -86,6 +87,7 @@
         assertThat(PowerUtil.roundTimeToNearestThreshold(-200, -75)).isEqualTo(225);
     }
 
+    @Ignore("b/359066481")
     @Test
     public void getTargetTimeShortString_lessThan15Minutes_returnsTimeShortStringWithoutRounded() {
         mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
@@ -100,6 +102,7 @@
         assertThat(actualTimeString).endsWith("14 PM");
     }
 
+    @Ignore("b/359066481")
     @Test
     public void getTargetTimeShortString_moreThan15Minutes_returnsTimeShortStringWithRounded() {
         mContext.getSystemService(AlarmManager.class).setTimeZone("UTC");
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
index f0f53d6..7f4bdae 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
@@ -44,6 +44,7 @@
 import com.android.settingslib.widget.preference.banner.R;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -53,6 +54,7 @@
 import org.robolectric.shadows.ShadowTouchDelegate;
 import org.robolectric.util.ReflectionHelpers;
 
+@Ignore("b/359066481")
 @RunWith(RobolectricTestRunner.class)
 public class BannerMessagePreferenceTest {
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
index 60885f1..243ce85 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/SelectorWithWidgetPreferenceTest.java
@@ -23,22 +23,31 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Application;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.TextView;
 
 import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settingslib.widget.preference.selector.R;
+import com.android.settingslib.widget.selectorwithwidgetpreference.flags.Flags;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 @RunWith(RobolectricTestRunner.class)
 public class SelectorWithWidgetPreferenceTest {
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     private Application mContext;
     private SelectorWithWidgetPreference mPreference;
 
@@ -55,7 +64,7 @@
 
     @Before
     public void setUp() {
-        mContext = RuntimeEnvironment.application;
+        mContext = ApplicationProvider.getApplicationContext();
         mPreference = new SelectorWithWidgetPreference(mContext);
 
         View view = LayoutInflater.from(mContext)
@@ -121,6 +130,60 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+    public void onBindViewHolder_titleMaxLinesSet_flagOff_titleMaxLinesMatchesDefault() {
+        final int titleMaxLines = 5;
+        AttributeSet attributeSet = Robolectric.buildAttributeSet()
+                .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
+                .build();
+        mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+        View view = LayoutInflater.from(mContext)
+                .inflate(mPreference.getLayoutResource(), null /* root */);
+        PreferenceViewHolder preferenceViewHolder =
+                PreferenceViewHolder.createInstanceForTests(view);
+
+        mPreference.onBindViewHolder(preferenceViewHolder);
+
+        TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+        assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+    public void onBindViewHolder_noTitleMaxLinesSet_titleMaxLinesMatchesDefault() {
+        AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+        mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+        View view = LayoutInflater.from(mContext)
+                .inflate(mPreference.getLayoutResource(), null /* root */);
+        PreferenceViewHolder preferenceViewHolder =
+                PreferenceViewHolder.createInstanceForTests(view);
+
+        mPreference.onBindViewHolder(preferenceViewHolder);
+
+        TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+        assertThat(title.getMaxLines()).isEqualTo(SelectorWithWidgetPreference.DEFAULT_MAX_LINES);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ALLOW_SET_TITLE_MAX_LINES)
+    public void onBindViewHolder_titleMaxLinesSet_titleMaxLinesUpdated() {
+        final int titleMaxLines = 5;
+        AttributeSet attributeSet = Robolectric.buildAttributeSet()
+                .addAttribute(R.attr.titleMaxLines, String.valueOf(titleMaxLines))
+                .build();
+        mPreference = new SelectorWithWidgetPreference(mContext, attributeSet);
+        View view = LayoutInflater.from(mContext)
+                .inflate(mPreference.getLayoutResource(), null /* root */);
+        PreferenceViewHolder preferenceViewHolder =
+                PreferenceViewHolder.createInstanceForTests(view);
+
+        mPreference.onBindViewHolder(preferenceViewHolder);
+
+        TextView title = (TextView) preferenceViewHolder.findViewById(android.R.id.title);
+        assertThat(title.getMaxLines()).isEqualTo(titleMaxLines);
+    }
+
+    @Test
     public void nullSummary_containerShouldBeGone() {
         mPreference.setSummary(null);
         View summaryContainer = new View(mContext);
diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp
index 75f8384..3e62b7b 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -32,6 +32,7 @@
         "unsupportedappusage",
     ],
     static_libs: [
+        "aconfig_device_paths_java",
         "aconfig_new_storage_flags_lib",
         "aconfigd_java_utils",
         "aconfig_demo_flags_java_lib",
diff --git a/packages/SettingsProvider/res/values-in/defaults.xml b/packages/SettingsProvider/res/values-in/defaults.xml
index 221c37f..1434b59 100644
--- a/packages/SettingsProvider/res/values-in/defaults.xml
+++ b/packages/SettingsProvider/res/values-in/defaults.xml
@@ -20,9 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for def_device_name_simple (9037785625140748221) -->
-    <skip />
+    <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
     <string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
     <string name="def_backup_manager_constants" msgid="75273734665044867"></string>
     <string name="def_backup_local_transport_parameters" msgid="303005414813191641"></string>
diff --git a/packages/SettingsProvider/res/values-ne/defaults.xml b/packages/SettingsProvider/res/values-ne/defaults.xml
index 221c37f..1434b59 100644
--- a/packages/SettingsProvider/res/values-ne/defaults.xml
+++ b/packages/SettingsProvider/res/values-ne/defaults.xml
@@ -20,9 +20,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for def_device_name_simple (9037785625140748221) -->
-    <skip />
+    <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
     <string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
     <string name="def_backup_manager_constants" msgid="75273734665044867"></string>
     <string name="def_backup_local_transport_parameters" msgid="303005414813191641"></string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 65937ea..f6e1057 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -340,7 +340,8 @@
                         new String[] {
                             String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_UNKNOWN),
                             String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_ANDROID),
-                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS)
+                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_IOS),
+                            String.valueOf(Global.Wearable.PAIRED_DEVICE_OS_TYPE_NONE)
                         }));
         VALIDATORS.put(
                 Global.Wearable.COMPANION_BLE_ROLE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index ec50323..e85ba45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -2173,8 +2173,7 @@
                                             (1 << AudioManager.STREAM_NOTIFICATION) |
                                             (1 << AudioManager.STREAM_SYSTEM) |
                                             (1 << AudioManager.STREAM_SYSTEM_ENFORCED);
-            if (!mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_voice_capable)) {
+            if (!getTelephonyManager().isVoiceCapable()) {
                 ringerModeAffectedStreams |= (1 << AudioManager.STREAM_MUSIC);
             }
             loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index 8b0772b..bfbf41d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -20,8 +20,10 @@
 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT;
 import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT;
 
-import android.aconfig.Aconfig.parsed_flag;
-import android.aconfig.Aconfig.parsed_flags;
+import android.aconfig.DeviceProtos;
+import android.aconfig.nano.Aconfig;
+import android.aconfig.nano.Aconfig.parsed_flag;
+import android.aconfig.nano.Aconfig.parsed_flags;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.content.AttributionSource;
@@ -42,7 +44,6 @@
 import android.provider.UpdatableDeviceConfigServiceReadiness;
 import android.util.Slog;
 
-import com.android.internal.pm.pkg.component.AconfigFlags;
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.File;
@@ -98,6 +99,12 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("SyncDisabledForTests: ");
+        MyShellCommand.getSyncDisabledForTests(pw, pw);
+
+        pw.print("Is mainline: ");
+        pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService());
+
         final IContentProvider iprovider = mProvider.getIContentProvider();
         pw.println("DeviceConfig flags:");
         for (String line : MyShellCommand.listAll(iprovider)) {
@@ -136,11 +143,8 @@
                     continue;
                 }
 
-                for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
-                    String namespace = flag.getNamespace();
-                    String packageName = flag.getPackage();
-                    String name = flag.getName();
-                    nameSet.add(namespace + "/" + packageName + "." + name);
+                for (parsed_flag flag : parsedFlags.parsedFlag) {
+                    nameSet.add(flag.namespace + "/" + flag.package_ + "." + flag.name);
                 }
             }
         } catch (IOException e) {
@@ -169,6 +173,7 @@
 
     static final class MyShellCommand extends ShellCommand {
         final SettingsProvider mProvider;
+        private HashMap<String, parsed_flag> mAconfigParsedFlags;
 
         enum CommandVerb {
             GET,
@@ -186,6 +191,62 @@
 
         MyShellCommand(SettingsProvider provider) {
             mProvider = provider;
+
+            if (Flags.checkRootAndReadOnly()) {
+                List<parsed_flag> parsedFlags;
+                try {
+                    parsedFlags = DeviceProtos.loadAndParseFlagProtos();
+                } catch (IOException e) {
+                    throw new IllegalStateException("failed to parse aconfig protos");
+                }
+
+                mAconfigParsedFlags = new HashMap();
+                for (parsed_flag flag : parsedFlags) {
+                    mAconfigParsedFlags.put(flag.package_ + "." + flag.name, flag);
+                }
+            }
+        }
+
+        /**
+         * Return true if a flag is aconfig.
+         */
+        private boolean isAconfigFlag(String name) {
+            return mAconfigParsedFlags.get(name) != null;
+        }
+
+        /**
+         * Return true if a flag is both aconfig and read-only.
+         *
+         * @return true if a flag is both aconfig and read-only
+         */
+        private boolean isReadOnly(String name) {
+            parsed_flag flag = mAconfigParsedFlags.get(name);
+            if (flag != null) {
+                if (flag.permission == Aconfig.READ_ONLY) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Return true if the calling process is root.
+         *
+         * @return true if a flag is aconfig, and the calling process is root
+         */
+        private boolean isRoot() {
+            return Binder.getCallingUid() == Process.ROOT_UID;
+        }
+
+        private static int getSyncDisabledForTests(PrintWriter pOut, PrintWriter pErr) {
+            int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
+            String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
+            if (syncDisabledModeString == null) {
+                pErr.println("Unknown mode: " + syncDisabledModeInt);
+                return -1;
+            }
+            pOut.println(syncDisabledModeString);
+            return 0;
         }
 
       public static HashMap<String, String> getAllFlags(IContentProvider provider) {
@@ -414,21 +475,71 @@
                     pout.println(DeviceConfig.getProperty(namespace, key));
                     break;
                 case PUT:
+                    if (Flags.checkRootAndReadOnly()) {
+                        if (isAconfigFlag(key)) {
+                            if (!isRoot()) {
+                                pout.println("Error: must be root to write aconfig flag");
+                                break;
+                            }
+
+                            if (isReadOnly(key)) {
+                                pout.println("Error: cannot write read-only flag");
+                                break;
+                            }
+                        }
+                    }
+
                     DeviceConfig.setProperty(namespace, key, value, makeDefault);
                     break;
                 case OVERRIDE:
-                    AconfigFlags.Permission permission =
-                            (new AconfigFlags()).getFlagPermission(key);
-                    if (permission == AconfigFlags.Permission.READ_ONLY) {
-                        pout.println("cannot override read-only flag " + key);
-                    } else {
-                        DeviceConfig.setLocalOverride(namespace, key, value);
+                    if (Flags.checkRootAndReadOnly()) {
+                        if (isAconfigFlag(key)) {
+                            if (!isRoot()) {
+                                pout.println("Error: must be root to write aconfig flag");
+                                break;
+                            }
+
+                            if (isReadOnly(key)) {
+                                pout.println("Error: cannot write read-only flag");
+                                break;
+                            }
+                        }
                     }
+
+                    DeviceConfig.setLocalOverride(namespace, key, value);
                     break;
                 case CLEAR_OVERRIDE:
+                    if (Flags.checkRootAndReadOnly()) {
+                        if (isAconfigFlag(key)) {
+                            if (!isRoot()) {
+                                pout.println("Error: must be root to write aconfig flag");
+                                break;
+                            }
+
+                            if (isReadOnly(key)) {
+                                pout.println("Error: cannot write read-only flag");
+                                break;
+                            }
+                        }
+                    }
+
                     DeviceConfig.clearLocalOverride(namespace, key);
                     break;
                 case DELETE:
+                    if (Flags.checkRootAndReadOnly()) {
+                        if (isAconfigFlag(key)) {
+                            if (!isRoot()) {
+                                pout.println("Error: must be root to write aconfig flag");
+                                break;
+                            }
+
+                            if (isReadOnly(key)) {
+                                pout.println("Error: cannot write read-only flag");
+                                break;
+                            }
+                        }
+                    }
+
                     pout.println(delete(iprovider, namespace, key)
                             ? "Successfully deleted " + key + " from " + namespace
                             : "Failed to delete " + key + " from " + namespace);
@@ -503,14 +614,7 @@
                     DeviceConfig.setSyncDisabledMode(syncDisabledModeArg);
                     break;
                 case GET_SYNC_DISABLED_FOR_TESTS:
-                    int syncDisabledModeInt = DeviceConfig.getSyncDisabledMode();
-                    String syncDisabledModeString = formatSyncDisabledMode(syncDisabledModeInt);
-                    if (syncDisabledModeString == null) {
-                        perr.println("Unknown mode: " + syncDisabledModeInt);
-                        return -1;
-                    }
-                    pout.println(syncDisabledModeString);
-                    break;
+                    return getSyncDisabledForTests(pout, perr);
                 default:
                     perr.println("Unspecified command");
                     return -1;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 30c4ee5..9ab853f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -690,7 +690,7 @@
         Cursor cursor = getContentResolver().query(Settings.Global.CONTENT_URI, PROJECTION, null,
                 null, null);
         try {
-            return extractRelevantValues(cursor, GlobalSettings.SETTINGS_TO_BACKUP);
+            return extractRelevantValues(cursor, getGlobalSettingsToBackup());
         } finally {
             cursor.close();
         }
@@ -1011,7 +1011,7 @@
                     Settings.System.LEGACY_RESTORE_SETTINGS);
             validators = SystemSettingsValidators.VALIDATORS;
         } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
-            whitelist = ArrayUtils.concat(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
+            whitelist = ArrayUtils.concat(String.class, getGlobalSettingsToBackup(),
                     Settings.Global.LEGACY_RESTORE_SETTINGS);
             validators = GlobalSettingsValidators.VALIDATORS;
         } else {
@@ -1021,6 +1021,17 @@
         return new SettingsBackupWhitelist(whitelist, validators);
     }
 
+    private String[] getGlobalSettingsToBackup() {
+        // On watches, we don't want to backup or restore 'bluetooth_on' setting, as setting it to
+        // false during restore would cause watch OOBE to fail due to bluetooth connection loss.
+        if (isWatch()) {
+            return ArrayUtils.removeElement(
+                    String.class, GlobalSettings.SETTINGS_TO_BACKUP, Settings.Global.BLUETOOTH_ON);
+        }
+
+        return GlobalSettings.SETTINGS_TO_BACKUP;
+    }
+
     private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
         String contentKey = Uri.withAppendedPath(areaUri, key).toString();
         return dynamicBlockList.contains(contentKey);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index b1e6d66..62401a1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -70,3 +70,24 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "check_root_and_read_only"
+    namespace: "core_experiments_team_internal"
+    description: "Check root and aconfig flag permissions in adb shell device_config commands."
+    bug: "342636474"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "sync_local_overrides_removal_new_storage"
+    namespace: "core_experiments_team_internal"
+    description: "When DeviceConfig overrides are deleted, delete new storage overrides too."
+    bug: "361643653"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index d4ca4a3..3a391505 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -26,6 +26,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
@@ -221,6 +222,21 @@
     }
 
     @Test
+    public void testOnRestore_bluetoothOnRestoredOnNonWearablesOnly() {
+        TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+        mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+        restoreGlobalSettings(generateBackupData(Map.of(Settings.Global.BLUETOOTH_ON, "0")));
+
+        var isWatch = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+        if (isWatch) {
+            assertFalse(settingsHelper.mWrittenValues.containsKey(Settings.Global.BLUETOOTH_ON));
+        } else {
+            assertEquals("0", settingsHelper.mWrittenValues.get(Settings.Global.BLUETOOTH_ON));
+        }
+    }
+
+    @Test
     @EnableFlags(Flags.FLAG_CONFIGURABLE_FONT_SCALE_DEFAULT)
     public void testFindClosestAllowedFontScale() {
         final String[] availableFontScales = new String[]{"0.5", "0.9", "1.0", "1.1", "1.5"};
@@ -266,6 +282,20 @@
         return buffer.array();
     }
 
+    private void restoreGlobalSettings(byte[] backupData) {
+        mAgentUnderTest.restoreSettings(
+                backupData,
+                /* pos= */ 0,
+                backupData.length,
+                Settings.Global.CONTENT_URI,
+                null,
+                null,
+                null,
+                R.array.restore_blocked_global_settings,
+                /* dynamicBlockList= */ Collections.emptySet(),
+                /* settingsToPreserve= */ Collections.emptySet());
+    }
+
     private byte[] generateUncorruptedHeader() throws IOException {
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             mAgentUnderTest.writeHeader(os);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0b5187c..720ec87 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -941,6 +941,10 @@
     <!-- Permission required for CTS test - FileIntegrityManagerTest -->
     <uses-permission android:name="android.permission.SETUP_FSVERITY" />
 
+    <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+    <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+    <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS" />
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/res/values-be/strings.xml b/packages/Shell/res/values-be/strings.xml
index b3d8a44..366db93 100644
--- a/packages/Shell/res/values-be/strings.xml
+++ b/packages/Shell/res/values-be/strings.xml
@@ -22,7 +22,7 @@
     <string name="bugreport_finished_title" msgid="4429132808670114081">"Справаздача <xliff:g id="ID">#%d</xliff:g> пра памылку зафіксавана"</string>
     <string name="bugreport_updating_title" msgid="4423539949559634214">"Дадаванне падрабязнасцей да справаздачы пра памылкі"</string>
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"Калі ласка, пачакайце..."</string>
-    <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Паведамленне пра памылку хутка з\'явіцца на тэлефоне"</string>
+    <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Паведамленне пра памылку хутка з’явіцца на тэлефоне"</string>
     <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Выберыце, каб абагуліць справаздачу пра памылку"</string>
     <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Дакраніцеся, каб абагуліць сваю справаздачу пра памылку"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Выберыце, каб абагуліць справаздачу пра памылку без здымка экрана, або чакайце атрымання здымка"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6d78705..d26a906 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -748,6 +748,7 @@
         "//frameworks/libs/systemui:motion_tool_lib",
         "//frameworks/libs/systemui:contextualeducationlib",
         "androidx.core_core-animation-testing",
+        "androidx.lifecycle_lifecycle-runtime-testing",
         "androidx.compose.ui_ui",
         "flag-junit",
         "ravenwood-junit",
@@ -789,6 +790,7 @@
         "SystemUI-tests-base",
         "androidx.test.uiautomator_uiautomator",
         "androidx.core_core-animation-testing",
+        "androidx.lifecycle_lifecycle-runtime-testing",
         "mockito-target-extended-minus-junit4",
         "mockito-kotlin-nodeps",
         "androidx.test.ext.junit",
@@ -836,14 +838,6 @@
     ],
     manifest: "tests/AndroidManifest-base.xml",
 
-    srcs: [
-        "src/**/*.kt",
-        "src/**/*.java",
-        "src/**/I*.aidl",
-        ":ReleaseJavaFiles",
-        "compose/features/src/**/*.kt",
-        "compose/facade/enabled/src/**/*.kt",
-    ],
     static_libs: [
         "//frameworks/libs/systemui:compilelib",
         "SystemUI-tests-base",
@@ -899,6 +893,7 @@
     ],
     static_libs: [
         "RoboTestLibraries",
+        "androidx.compose.runtime_runtime",
     ],
     libs: [
         "android.test.runner",
@@ -935,6 +930,7 @@
     ],
     static_libs: [
         "RoboTestLibraries",
+        "androidx.compose.runtime_runtime",
     ],
     libs: [
         "android.test.runner",
@@ -953,36 +949,37 @@
     strict_mode: false,
 }
 
-// Disable for now. TODO(b/356666754) Re-enable it
-// android_ravenwood_test {
-//     name: "SystemUiRavenTests",
-//     srcs: [
-//         ":SystemUI-tests-utils",
-//         ":SystemUI-tests-multivalent",
-//         // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable
-//         // use_resource_processor: true when better supported by soong
-//         ":SystemUIRobo-stub{.aapt.srcjar}",
-//     ],
-//     static_libs: [
-//         "SystemUI-core",
-//         "SystemUI-res",
-//         "SystemUI-tests-base",
-//         "androidx.test.uiautomator_uiautomator",
-//         "androidx.core_core-animation-testing",
-//         "androidx.test.ext.junit",
-//         "kosmos",
-//         "mockito-kotlin-nodeps",
-//     ],
-//     libs: [
-//         "android.test.runner",
-//         "android.test.base",
-//         "android.test.mock",
-//     ],
-//     auto_gen_config: true,
-//     plugins: [
-//         "dagger2-compiler",
-//     ],
-// }
+android_ravenwood_test {
+    name: "SystemUiRavenTests",
+    srcs: [
+        ":SystemUI-tests-utils",
+        ":SystemUI-tests-multivalent",
+        // TODO(b/294256649): pivot to using {.aapt.jar} and re-enable
+        // use_resource_processor: true when better supported by soong
+        ":SystemUIRobo-stub{.aapt.srcjar}",
+    ],
+    static_libs: [
+        "SystemUI-core",
+        "SystemUI-res",
+        "SystemUI-tests-base",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.core_core-animation-testing",
+        "androidx.test.ext.junit",
+        "kosmos",
+        "kotlin-test",
+        "mockito-kotlin-nodeps",
+        "androidx.compose.runtime_runtime",
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    auto_gen_config: true,
+    plugins: [
+        "dagger2-compiler",
+    ],
+}
 
 // Opt-out config for optimizing the SystemUI target using R8.
 // Disabled via `export SYSTEMUI_OPTIMIZE_JAVA=false`, or explicitly in Make via
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9f3c2bf..157af7d 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -92,6 +92,7 @@
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.MASTER_CLEAR" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.VIBRATE_SYSTEM_CONSTANTS" />
     <uses-permission android:name="android.permission.MANAGE_SENSOR_PRIVACY" />
     <uses-permission android:name="android.permission.OBSERVE_SENSOR_PRIVACY" />
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT" />
@@ -370,12 +371,19 @@
 
     <uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" />
 
+    <!-- Listen to keyboard shortcut events from input manager -->
+    <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" />
+
     <!-- To follow the grammatical gender preference -->
     <uses-permission android:name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER" />
 
     <!-- Listen to (dis-)connection of external displays and enable / disable them. -->
     <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
 
+    <!-- To be able to intercept meta key events, might need to be removed once b/358569822
+         is ready -->
+    <uses-permission android:name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
+
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
     <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -480,6 +488,19 @@
 
         <activity android:name=".touchpad.tutorial.ui.view.TouchpadTutorialActivity"
             android:exported="true"
+            android:showForAllUsers="true"
+            android:screenOrientation="userLandscape"
+            android:theme="@style/Theme.AppCompat.NoActionBar">
+            <intent-filter>
+                <action android:name="com.android.systemui.action.TOUCHPAD_TUTORIAL"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity"
+            android:exported="true"
+            android:showForAllUsers="true"
+            android:screenOrientation="userLandscape"
             android:theme="@style/Theme.AppCompat.NoActionBar">
             <intent-filter>
                 <action android:name="com.android.systemui.action.TOUCHPAD_KEYBOARD_TUTORIAL"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index c881e07..fb1f715 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -36,11 +36,12 @@
         "androidx.coordinatorlayout_coordinatorlayout",
         "androidx.core_core",
         "androidx.preference_preference",
-        "androidx.viewpager_viewpager",
+        "androidx.viewpager2_viewpager2",
         "com_android_systemui_flags_lib",
         "SettingsLibDisplayUtils",
         "SettingsLibSettingsTheme",
         "com_android_a11y_menu_flags_lib",
+        "//frameworks/libs/systemui:view_capture",
     ],
 
     optimize: {
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 462c90b..e1b6e63 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -22,7 +22,7 @@
       android:orientation="horizontal">
 
     <ImageButton
-        android:id="@+id/menu_prev_button"
+        android:id="@+id/menu_left_button"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_weight="1"
@@ -38,7 +38,7 @@
         android:background="?android:attr/listDivider"/>
 
     <ImageButton
-        android:id="@+id/menu_next_button"
+        android:id="@+id/menu_right_button"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_weight="1"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index fd9a9c6..3c73eca 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -3,8 +3,7 @@
     android:id="@+id/shortcutItem"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="@dimen/grid_item_padding"
-    android:paddingBottom="@dimen/grid_item_padding"
+    android:padding="@dimen/grid_item_padding"
     android:gravity="center">
 
   <ImageButton
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
index c198443..c0aa1b3 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml
@@ -2,7 +2,7 @@
 <GridView xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/gridview"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:horizontalSpacing="@dimen/a11ymenu_grid_layout_margin"
     android:listSelector="@android:color/transparent"
     android:numColumns="3"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
index 28a633e..adaa655 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml
@@ -1,27 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/row_width"
     android:layout_height="match_parent"
-    android:id="@+id/coordinatorLayout"
-    android:background="@drawable/view_background"
-    >
-  <LinearLayout
-      android:layout_width="@dimen/row_width"
-      android:layout_height="wrap_content"
-      android:orientation="vertical">
+    android:orientation="vertical"
+    android:background="@drawable/view_background">
 
-    <androidx.viewpager.widget.ViewPager
-        android:id="@+id/view_pager"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/table_margin_top"
-        android:paddingBottom="@dimen/a11ymenu_layout_margin"
-        android:paddingLeft="@dimen/a11ymenu_layout_margin"
-        android:paddingRight="@dimen/a11ymenu_layout_margin"
-        android:layout_gravity="center"
-        android:gravity="center"
-        />
+  <androidx.viewpager2.widget.ViewPager2
+      android:id="@+id/view_pager"
+      android:layout_width="match_parent"
+      android:layout_height="0dp"
+      android:layout_weight="1"
+      android:paddingTop="@dimen/table_margin_top"
+      android:paddingBottom="@dimen/a11ymenu_layout_margin"
+      android:gravity="center"
+      />
 
-    <include layout="@layout/footerlayout_switch_page"/>
-  </LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
+  <include layout="@layout/footerlayout_switch_page"/>
+</LinearLayout>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
index e60eac0..851c2c9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-fr-rCA/strings.xml
@@ -21,7 +21,7 @@
     <string name="previous_button_content_description" msgid="840869171117765966">"Aller à l\'écran précédent"</string>
     <string name="next_button_content_description" msgid="6810058269847364406">"Aller à l\'écran suivant"</string>
     <string name="accessibility_menu_description" msgid="4458354794093858297">"Le menu Accessibilité propose un grand espace à l\'écran à l\'aide duquel vous pouvez contrôler votre appareil. Utilisez-le pour verrouiller votre appareil, régler le volume et la luminosité, prendre des captures d\'écran et plus."</string>
-    <string name="accessibility_menu_summary" msgid="340071398148208130">"Contrôle l\'appareil à l\'aide d\'un menu de grande taille"</string>
+    <string name="accessibility_menu_summary" msgid="340071398148208130">"Contrôler l\'appareil à l\'aide d\'un menu de grande taille"</string>
     <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Paramètres du menu Accessibilité"</string>
     <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Boutons de grande taille"</string>
     <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Augmenter la taille des boutons du menu Accessibilité"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
index 7072b34..3f8671c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-iw/strings.xml
@@ -8,7 +8,7 @@
     <string name="a11y_settings_label" msgid="3977714687248445050">"הגדרות נגישות"</string>
     <string name="power_label" msgid="7699720321491287839">"הפעלה"</string>
     <string name="power_utterance" msgid="7444296686402104807">"אפשרויות הפעלה"</string>
-    <string name="recent_apps_label" msgid="6583276995616385847">"אפליקציות אחרונות"</string>
+    <string name="recent_apps_label" msgid="6583276995616385847">"אפליקציות שהיו בשימוש לאחרונה"</string>
     <string name="lockscreen_label" msgid="648347953557887087">"מסך נעילה"</string>
     <string name="quick_settings_label" msgid="2999117381487601865">"הגדרות מהירות"</string>
     <string name="notifications_label" msgid="6829741046963013567">"התראות"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
index ff8b632..0cc2f58 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rPT/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="accessibility_menu_service_name" msgid="730136711554740131">"menu Acessibilidade"</string>
+    <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu Acessibilidade"</string>
     <string name="accessibility_menu_intro" msgid="3164193281544042394">"O menu Acessibilidade disponibiliza um menu grande no ecrã para controlar o dispositivo. Pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de ecrã e muito mais."</string>
     <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string>
     <string name="assistant_utterance" msgid="65509599221141377">"Assistente"</string>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index c2cf6e1..c333a7a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.accessibility.accessibilitymenu.view;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.view.LayoutInflater;
 import android.view.TouchDelegate;
@@ -43,16 +42,14 @@
     private final int mLargeTextSize;
 
     private final AccessibilityMenuService mService;
-    private final LayoutInflater mInflater;
     private final List<A11yMenuShortcut> mShortcutDataList;
     private final ShortcutDrawableUtils mShortcutDrawableUtils;
 
     public A11yMenuAdapter(
             AccessibilityMenuService service,
-            Context displayContext, List<A11yMenuShortcut> shortcutDataList) {
+            List<A11yMenuShortcut> shortcutDataList) {
         this.mService = service;
         this.mShortcutDataList = shortcutDataList;
-        mInflater = LayoutInflater.from(displayContext);
 
         mShortcutDrawableUtils = new ShortcutDrawableUtils(service);
 
@@ -78,7 +75,8 @@
     @Override
     public View getView(int position, View convertView, ViewGroup parent) {
         if (convertView == null) {
-            convertView = mInflater.inflate(R.layout.grid_item, parent, false);
+            convertView = LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.grid_item, parent, false);
 
             configureShortcutSize(convertView,
                     A11yMenuPreferenceFragment.isLargeButtonsEnabled(mService));
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
index 20c63df..136a4ed 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java
@@ -16,7 +16,11 @@
 
 package com.android.systemui.accessibility.accessibilitymenu.view;
 
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+
+import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.text.TextUtils;
 import android.view.TouchDelegate;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -37,46 +41,73 @@
     public interface A11yMenuFooterCallBack {
 
         /** Calls back when user clicks the left button. */
-        void onLeftButtonClicked();
+        void onNextButtonClicked();
 
         /** Calls back when user clicks the right button. */
-        void onRightButtonClicked();
+        void onPreviousButtonClicked();
     }
 
     private final FooterButtonClickListener mFooterButtonClickListener;
 
-    private ImageButton mPreviousPageBtn;
-    private ImageButton mNextPageBtn;
+    private ImageButton mPageLeftBtn;
+    private ImageButton mPageRightBtn;
     private View mTopListDivider;
     private View mBottomListDivider;
     private final A11yMenuFooterCallBack mCallBack;
+    private final ViewGroup mMenuLayout;
+    private ViewGroup mFooterContainer;
+    private int mFooterContainerBaseHeight = 0;
+    private int mRightToLeftDirection = LAYOUT_DIRECTION_LTR;
 
     public A11yMenuFooter(ViewGroup menuLayout, A11yMenuFooterCallBack callBack) {
         this.mCallBack = callBack;
         mFooterButtonClickListener = new FooterButtonClickListener();
         configureFooterLayout(menuLayout);
+        mMenuLayout = menuLayout;
     }
 
     public @Nullable ImageButton getPreviousPageBtn() {
-        return mPreviousPageBtn;
+        return mRightToLeftDirection == LAYOUT_DIRECTION_LTR
+                ? mPageLeftBtn : mPageRightBtn;
     }
 
     public @Nullable ImageButton getNextPageBtn() {
-        return mNextPageBtn;
+        return mRightToLeftDirection == LAYOUT_DIRECTION_LTR
+                ? mPageRightBtn : mPageLeftBtn;
+    }
+
+    void adjustFooterToDensityScale(float densityScale) {
+        mFooterContainer.getLayoutParams().height =
+                (int) (mFooterContainerBaseHeight / densityScale);
+    }
+
+    int getHeight() {
+        return mFooterContainer.getLayoutParams().height;
+    }
+
+    /** Sets right to left direction of footer. */
+    public void updateRightToLeftDirection(Configuration configuration) {
+        mRightToLeftDirection = TextUtils.getLayoutDirectionFromLocale(
+                configuration.getLocales().get(0));
+        getPreviousPageBtn().setContentDescription(mMenuLayout.getResources().getString(
+                R.string.previous_button_content_description));
+        getNextPageBtn().setContentDescription(mMenuLayout.getResources().getString(
+                R.string.next_button_content_description));
     }
 
     private void configureFooterLayout(ViewGroup menuLayout) {
-        ViewGroup footerContainer = menuLayout.findViewById(R.id.footerlayout);
-        footerContainer.setVisibility(View.VISIBLE);
+        mFooterContainer = menuLayout.findViewById(R.id.footerlayout);
+        mFooterContainer.setVisibility(View.VISIBLE);
+        mFooterContainerBaseHeight = mFooterContainer.getLayoutParams().height;
 
-        mPreviousPageBtn = menuLayout.findViewById(R.id.menu_prev_button);
-        mNextPageBtn = menuLayout.findViewById(R.id.menu_next_button);
+        mPageLeftBtn = menuLayout.findViewById(R.id.menu_left_button);
+        mPageRightBtn = menuLayout.findViewById(R.id.menu_right_button);
         mTopListDivider = menuLayout.findViewById(R.id.top_listDivider);
         mBottomListDivider = menuLayout.findViewById(R.id.bottom_listDivider);
 
         // Registers listeners for footer buttons.
-        setListener(mPreviousPageBtn);
-        setListener(mNextPageBtn);
+        setListener(mPageLeftBtn);
+        setListener(mPageRightBtn);
 
         menuLayout
                 .getViewTreeObserver()
@@ -85,8 +116,8 @@
                             @Override
                             public void onGlobalLayout() {
                                 menuLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                                expandBtnTouchArea(mPreviousPageBtn, menuLayout);
-                                expandBtnTouchArea(mNextPageBtn, (View) mNextPageBtn.getParent());
+                                expandBtnTouchArea(mPageLeftBtn, menuLayout);
+                                expandBtnTouchArea(mPageRightBtn, (View) mPageRightBtn.getParent());
                             }
                         });
     }
@@ -115,10 +146,10 @@
     private class FooterButtonClickListener implements OnClickListener {
         @Override
         public void onClick(View view) {
-            if (view.getId() == R.id.menu_prev_button) {
-                mCallBack.onLeftButtonClicked();
-            } else if (view.getId() == R.id.menu_next_button) {
-                mCallBack.onRightButtonClicked();
+            if (view.getId() == getPreviousPageBtn().getId()) {
+                mCallBack.onPreviousButtonClicked();
+            } else if (view.getId() == getNextPageBtn().getId()) {
+                mCallBack.onNextButtonClicked();
             }
         }
     }
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 2e036e6..3db61a5 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -22,6 +22,8 @@
 import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
+import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
+
 import static java.lang.Math.max;
 
 import android.animation.Animator;
@@ -51,7 +53,9 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.UiContext;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
 import com.android.systemui.accessibility.accessibilitymenu.Flags;
 import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -101,7 +105,6 @@
     };
 
     private final AccessibilityMenuService mService;
-    private final WindowManager mWindowManager;
     private final DisplayManager mDisplayManager;
     private ViewGroup mLayout;
     private WindowManager.LayoutParams mLayoutParameter;
@@ -111,7 +114,6 @@
 
     public A11yMenuOverlayLayout(AccessibilityMenuService service) {
         mService = service;
-        mWindowManager = mService.getSystemService(WindowManager.class);
         mDisplayManager = mService.getSystemService(DisplayManager.class);
         configureLayout();
         mHandler = new Handler(Looper.getMainLooper());
@@ -134,8 +136,7 @@
         int lastVisibilityState = View.GONE;
         if (mLayout != null) {
             lastVisibilityState = mLayout.getVisibility();
-            mWindowManager.removeView(mLayout);
-            mLayout = null;
+            clearLayout();
         }
 
         if (mLayoutParameter == null) {
@@ -143,22 +144,30 @@
         }
 
         final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-        final Context context = mService.createDisplayContext(display).createWindowContext(
-                TYPE_ACCESSIBILITY_OVERLAY, null);
-        mLayout = new FrameLayout(context);
-        updateLayoutPosition();
-        inflateLayoutAndSetOnTouchListener(mLayout, context);
-        mA11yMenuViewPager = new A11yMenuViewPager(mService, context);
+        final Context uiContext = mService.createWindowContext(
+                display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null);
+        final ViewCaptureAwareWindowManager windowManager =
+                getViewCaptureAwareWindowManagerInstance(uiContext,
+                        com.android.systemui.Flags.enableViewCaptureTracing());
+        mLayout = new A11yMenuFrameLayout(uiContext);
+        updateLayoutPosition(uiContext);
+        inflateLayoutAndSetOnTouchListener(mLayout, uiContext);
+        mA11yMenuViewPager = new A11yMenuViewPager(mService);
         mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex);
-        mWindowManager.addView(mLayout, mLayoutParameter);
+        windowManager.addView(mLayout, mLayoutParameter);
         mLayout.setVisibility(lastVisibilityState);
+        mA11yMenuViewPager.updateFooterState();
 
         return mLayout;
     }
 
     public void clearLayout() {
         if (mLayout != null) {
-            mWindowManager.removeView(mLayout);
+            ViewCaptureAwareWindowManager windowManager = getViewCaptureAwareWindowManagerInstance(
+                    mLayout.getContext(), com.android.systemui.Flags.enableViewCaptureTracing());
+            if (windowManager != null) {
+                windowManager.removeView(mLayout);
+            }
             mLayout.setOnTouchListener(null);
             mLayout = null;
         }
@@ -169,8 +178,11 @@
         if (mLayout == null || mLayoutParameter == null) {
             return;
         }
-        updateLayoutPosition();
-        mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+        updateLayoutPosition(mLayout.getContext());
+        WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class);
+        if (windowManager != null) {
+            windowManager.updateViewLayout(mLayout, mLayoutParameter);
+        }
     }
 
     private void initLayoutParams() {
@@ -182,8 +194,8 @@
         mLayoutParameter.setTitle(mService.getString(R.string.accessibility_menu_service_name));
     }
 
-    private void inflateLayoutAndSetOnTouchListener(ViewGroup view, Context displayContext) {
-        LayoutInflater inflater = LayoutInflater.from(displayContext);
+    private void inflateLayoutAndSetOnTouchListener(ViewGroup view, @UiContext Context uiContext) {
+        LayoutInflater inflater = LayoutInflater.from(uiContext);
         inflater.inflate(R.layout.paged_menu, view);
         view.setOnTouchListener(mService);
     }
@@ -237,7 +249,11 @@
     }
 
     /** Updates a11y menu layout position by configuring layout params. */
-    private void updateLayoutPosition() {
+    private void updateLayoutPosition(@UiContext @NonNull Context uiContext) {
+        WindowManager windowManager = uiContext.getSystemService(WindowManager.class);
+        if (windowManager == null) {
+            return;
+        }
         final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         final Configuration configuration = mService.getResources().getConfiguration();
         final int orientation = configuration.orientation;
@@ -275,14 +291,13 @@
             mLayoutParameter.height = WindowManager.LayoutParams.WRAP_CONTENT;
             mLayout.setBackgroundResource(R.drawable.shadow_0deg);
         }
-
         // Adjusts the y position of a11y menu layout to make the layout not to overlap bottom
         // navigation bar window.
-        updateLayoutByWindowInsetsIfNeeded();
+        updateLayoutByWindowInsetsIfNeeded(windowManager);
         mLayout.setOnApplyWindowInsetsListener(
                 (view, insets) -> {
-                    if (updateLayoutByWindowInsetsIfNeeded()) {
-                        mWindowManager.updateViewLayout(mLayout, mLayoutParameter);
+                    if (updateLayoutByWindowInsetsIfNeeded(windowManager)) {
+                        windowManager.updateViewLayout(mLayout, mLayoutParameter);
                     }
                     return view.onApplyWindowInsets(insets);
                 });
@@ -294,9 +309,9 @@
      * This method adjusts the layout position and size to
      * make a11y menu not to overlap navigation bar window.
      */
-    private boolean updateLayoutByWindowInsetsIfNeeded() {
+    private boolean updateLayoutByWindowInsetsIfNeeded(@NonNull WindowManager windowManager) {
         boolean shouldUpdateLayout = false;
-        WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+        WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics();
         Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
                 WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
         int xOffset = max(windowInsets.left, windowInsets.right);
@@ -348,7 +363,17 @@
 
     /** Toggles a11y menu layout visibility. */
     public void toggleVisibility() {
-        mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE);
+        if (mLayout.getVisibility() == View.VISIBLE) {
+            mLayout.setVisibility(View.GONE);
+        } else {
+            if (Flags.hideRestrictedActions()) {
+                // Reconfigure the shortcut list in case the set of restricted actions has changed.
+                mA11yMenuViewPager.configureViewPagerAndFooter(
+                        mLayout, createShortcutList(), getPageIndex());
+                updateViewLayout();
+            }
+            mLayout.setVisibility(View.VISIBLE);
+        }
     }
 
     /** Shows hint text on a minimal Snackbar-like text view. */
@@ -383,4 +408,16 @@
                         }
                     }), timeoutDurationMs);
     }
+
+    private class A11yMenuFrameLayout extends FrameLayout {
+        A11yMenuFrameLayout(@UiContext @NonNull Context context) {
+            super(context);
+        }
+
+        @Override
+        public void dispatchConfigurationChanged(Configuration newConfig) {
+            super.dispatchConfigurationChanged(newConfig);
+            mA11yMenuViewPager.mA11yMenuFooter.updateRightToLeftDirection(newConfig);
+        }
+    }
 }
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index b969017..35f1248 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -29,7 +28,8 @@
 import android.view.WindowMetrics;
 import android.widget.GridView;
 
-import androidx.viewpager.widget.ViewPager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewpager2.widget.ViewPager2;
 
 import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
 import com.android.systemui.accessibility.accessibilitymenu.R;
@@ -133,9 +133,9 @@
      * The pager widget, which handles animation and allows swiping horizontally to access previous
      * and next gridView pages.
      */
-    protected ViewPager mViewPager;
+    protected ViewPager2 mViewPager;
 
-    private ViewPagerAdapter<GridView> mViewPagerAdapter;
+    private ViewPagerAdapter mViewPagerAdapter;
     private final List<GridView> mGridPageList = new ArrayList<>();
 
     /** The footer, which provides buttons to switch between pages */
@@ -147,12 +147,8 @@
     /** The container layout for a11y menu. */
     private ViewGroup mA11yMenuLayout;
 
-    /** Display context for inflating views. */
-    private Context mDisplayContext;
-
-    public A11yMenuViewPager(AccessibilityMenuService service, Context displayContext) {
+    public A11yMenuViewPager(AccessibilityMenuService service) {
         this.mService = service;
-        this.mDisplayContext = displayContext;
     }
 
     /**
@@ -168,7 +164,11 @@
         mA11yMenuShortcutList = shortcutDataList;
         initViewPager();
         initChildPage();
-        mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks);
+        if (mA11yMenuFooter == null) {
+            mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks);
+        }
+        mA11yMenuFooter.updateRightToLeftDirection(
+                a11yMenuLayout.getResources().getConfiguration());
         updateFooterState();
         registerOnGlobalLayoutListener();
         goToPage(pageIndex);
@@ -177,18 +177,12 @@
     /** Initializes viewPager and its adapter. */
     private void initViewPager() {
         mViewPager = mA11yMenuLayout.findViewById(R.id.view_pager);
-        mViewPagerAdapter = new ViewPagerAdapter<>();
+        mViewPagerAdapter = new ViewPagerAdapter(mService);
+        mViewPager.setOffscreenPageLimit(2);
         mViewPager.setAdapter(mViewPagerAdapter);
         mViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
-        mViewPager.addOnPageChangeListener(
-                new ViewPager.OnPageChangeListener() {
-                    @Override
-                    public void onPageScrollStateChanged(int state) {}
-
-                    @Override
-                    public void onPageScrolled(
-                            int position, float positionOffset, int positionOffsetPixels) {}
-
+        mViewPager.registerOnPageChangeCallback(
+                new ViewPager2.OnPageChangeCallback() {
                     @Override
                     public void onPageSelected(int position) {
                         updateFooterState();
@@ -206,31 +200,14 @@
             mGridPageList.clear();
         }
 
-        // Generate pages by calculating # of items per grid.
-        for (List<A11yMenuShortcut> page : GridViewParams.generateShortcutSubLists(
-                GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList)
-        ) {
-            addGridPage(page);
-        }
-
-        mViewPagerAdapter.set(mGridPageList);
-    }
-
-    private void addGridPage(List<A11yMenuShortcut> shortcutDataListInPage) {
-        LayoutInflater inflater = LayoutInflater.from(mDisplayContext);
-        View view = inflater.inflate(R.layout.grid_view, null);
-        GridView gridView = view.findViewById(R.id.gridview);
-        A11yMenuAdapter adapter = new A11yMenuAdapter(
-                mService, mDisplayContext, shortcutDataListInPage);
-        gridView.setNumColumns(GridViewParams.getGridColumnCount(mService));
-        gridView.setAdapter(adapter);
-        mGridPageList.add(gridView);
+        mViewPagerAdapter.set(GridViewParams.generateShortcutSubLists(
+                GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList));
     }
 
     /** Updates footer's state by index of current page in view pager. */
-    private void updateFooterState() {
+    public void updateFooterState() {
         int currentPage = mViewPager.getCurrentItem();
-        int lastPage = mViewPager.getAdapter().getCount() - 1;
+        int lastPage = mViewPager.getAdapter().getItemCount() - 1;
         mA11yMenuFooter.getPreviousPageBtn().setEnabled(currentPage > 0);
         mA11yMenuFooter.getNextPageBtn().setEnabled(currentPage < lastPage);
     }
@@ -239,7 +216,7 @@
         if (mViewPager == null) {
             return;
         }
-        if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getCount())) {
+        if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getItemCount())) {
             mViewPager.setCurrentItem(pageIndex);
         }
     }
@@ -259,11 +236,17 @@
                                     return;
                                 }
 
-                                if (mGridPageList.isEmpty()) {
+                                if (mViewPagerAdapter.getItemCount() == 0) {
                                     return;
                                 }
 
-                                GridView firstGridView = mGridPageList.get(0);
+                                RecyclerView.ViewHolder viewHolder =
+                                        ((RecyclerView) mViewPager.getChildAt(0))
+                                                .findViewHolderForAdapterPosition(0);
+                                if (viewHolder == null) {
+                                    return;
+                                }
+                                GridView firstGridView = (GridView) viewHolder.itemView;
                                 if (firstGridView == null
                                         || firstGridView.getChildAt(0) == null) {
                                     return;
@@ -306,31 +289,28 @@
             DisplayMetrics displayMetrics = mService.getResources().getDisplayMetrics();
             float densityScale = (float) displayMetrics.densityDpi
                     / DisplayMetrics.DENSITY_DEVICE_STABLE;
-            View footerLayout = mA11yMenuLayout.findViewById(R.id.footerlayout);
             // Keeps footer window height unchanged no matter the density is changed.
-            footerLayout.getLayoutParams().height =
-                    (int) (footerLayout.getLayoutParams().height / densityScale);
+            mA11yMenuFooter.adjustFooterToDensityScale(densityScale);
             // Adjust the view pager height for system bar and display cutout insets.
-            WindowManager windowManager = mService.getSystemService(WindowManager.class);
+            WindowManager windowManager = mA11yMenuLayout.getContext()
+                    .getSystemService(WindowManager.class);
             WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics();
             Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility(
                     WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
             viewPagerHeight =
                     windowMetric.getBounds().height()
-                            - footerLayout.getLayoutParams().height
+                            - mA11yMenuFooter.getHeight()
                             - windowInsets.bottom;
             // Sets vertical interval between grid items.
             int interval =
                     (viewPagerHeight - topMargin - defaultMargin
                             - (rowsInGridView * gridItemHeight))
                             / (rowsInGridView + 1);
-            for (GridView gridView : mGridPageList) {
-                gridView.setVerticalSpacing(interval);
-            }
+            mViewPagerAdapter.setVerticalSpacing(interval);
 
             // Sets padding to view pager.
             final int finalMarginTop = interval + topMargin;
-            mViewPager.setPadding(defaultMargin, finalMarginTop, defaultMargin, defaultMargin);
+            mViewPager.setPadding(0, finalMarginTop, 0, defaultMargin);
         }
         final ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
         layoutParams.height = viewPagerHeight;
@@ -341,7 +321,7 @@
     protected A11yMenuFooterCallBack mFooterCallbacks =
             new A11yMenuFooterCallBack() {
                 @Override
-                public void onLeftButtonClicked() {
+                public void onPreviousButtonClicked() {
                     // Moves to previous page.
                     int targetPage = mViewPager.getCurrentItem() - 1;
                     goToPage(targetPage);
@@ -349,7 +329,7 @@
                 }
 
                 @Override
-                public void onRightButtonClicked() {
+                public void onNextButtonClicked() {
                     // Moves to next page.
                     int targetPage = mViewPager.getCurrentItem() + 1;
                     goToPage(targetPage);
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
index 5670d72..4b14e51 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java
@@ -16,55 +16,73 @@
 
 package com.android.systemui.accessibility.accessibilitymenu.view;
 
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.GridView;
 
-import androidx.viewpager.widget.PagerAdapter;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
+import com.android.systemui.accessibility.accessibilitymenu.R;
+import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
 
 import java.util.List;
 
 /** The pager adapter, which provides the pages to the view pager widget. */
-class ViewPagerAdapter<T extends View> extends PagerAdapter {
+class ViewPagerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
-    /** The widget list in each page of view pager. */
-    private List<T> mWidgetList;
+    /** List of shortcuts, split into sub lists per page */
+    private List<List<A11yMenuShortcut>> mShortcutList;
+    private final AccessibilityMenuService mService;
+    private int mVerticalSpacing = 0;
 
-    ViewPagerAdapter() {}
+    ViewPagerAdapter(AccessibilityMenuService service) {
+        mService = service;
+    }
 
-    public void set(List<T> tList) {
-        mWidgetList = tList;
+    public void setVerticalSpacing(int spacing) {
+        if (mVerticalSpacing != spacing) {
+            mVerticalSpacing = spacing;
+            notifyDataSetChanged();
+        }
+    }
+
+    public void set(List<List<A11yMenuShortcut>> tList) {
+        mShortcutList = tList;
         notifyDataSetChanged();
     }
 
+    @NonNull
     @Override
-    public int getCount() {
-        if (mWidgetList == null) {
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+        View view = inflater.inflate(R.layout.grid_view, parent, false);
+        return new MenuViewHolder(view.findViewById(R.id.gridview));
+    }
+
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+        A11yMenuAdapter adapter = new A11yMenuAdapter(
+                mService, mShortcutList.get(position));
+        GridView gridView = (GridView) holder.itemView;
+        gridView.setNumColumns(A11yMenuViewPager.GridViewParams.getGridColumnCount(mService));
+        gridView.setAdapter(adapter);
+        gridView.setVerticalSpacing(mVerticalSpacing);
+    }
+
+    @Override
+    public int getItemCount() {
+        if (mShortcutList == null) {
             return 0;
         }
-        return mWidgetList.size();
+        return mShortcutList.size();
     }
 
-    @Override
-    public int getItemPosition(Object object) {
-        return POSITION_NONE;
-    }
-
-    @Override
-    public boolean isViewFromObject(View view, Object object) {
-        return view == object;
-    }
-
-    @Override
-    public Object instantiateItem(ViewGroup container, int position) {
-        if (mWidgetList == null) {
-            return null;
+    static class MenuViewHolder extends RecyclerView.ViewHolder {
+        MenuViewHolder(View itemView) {
+            super(itemView);
         }
-        container.addView(mWidgetList.get(position));
-        return mWidgetList.get(position);
     }
-
-    @Override
-    public void destroyItem(ViewGroup container, int position, Object object) {
-        container.removeView((View) object);
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
index d16617f..4ab771be 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/tests/src/com/android/systemui/accessibility/accessibilitymenu/tests/AccessibilityMenuServiceTest.java
@@ -542,8 +542,6 @@
         final Context context = sInstrumentation.getTargetContext();
         final UserManager userManager = context.getSystemService(UserManager.class);
         userManager.setUserRestriction(restriction, isRestricted);
-        // Re-enable the service for the restriction to take effect.
-        enableA11yMenuService(context);
     }
 
     private static void unlockSignal() throws IOException {
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 8860452..5251246 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -4,6 +4,16 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
+    name: "add_black_background_for_window_magnifier"
+    namespace: "accessibility"
+    description: "Set the background for SurfaceView in window magnification black."
+    bug: "299981434"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "create_windowless_window_magnifier"
     namespace: "accessibility"
     description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
diff --git a/packages/SystemUI/aconfig/biometrics_framework.aconfig b/packages/SystemUI/aconfig/biometrics_framework.aconfig
index e81d5d5..10d7352 100644
--- a/packages/SystemUI/aconfig/biometrics_framework.aconfig
+++ b/packages/SystemUI/aconfig/biometrics_framework.aconfig
@@ -4,8 +4,11 @@
 # NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
 
 flag {
-    name: "constraint_bp"
-    namespace: "biometrics_framework"
-    description: "Refactors Biometric Prompt to use a ConstraintLayout"
-    bug: "288175072"
+  name: "bp_icon_a11y"
+  namespace: "biometrics_framework"
+  description: "Fixes biometric prompt icon not working as button with a11y"
+  bug: "359423579"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e6fae7b..d1a59af 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -139,13 +139,6 @@
 }
 
 flag {
-    name: "notifications_heads_up_refactor"
-    namespace: "systemui"
-    description: "Use HeadsUpInteractor to feed HUN updates to the NSSL."
-    bug: "325936094"
-}
-
-flag {
    name: "notification_transparent_header_fix"
    namespace: "systemui"
    description: "fix the transparent group header issue for async header inflation."
@@ -156,17 +149,6 @@
 }
 
 flag {
-   name: "pss_app_selector_abrupt_exit_fix"
-   namespace: "systemui"
-   description: "Fixes the app selector abruptly disappearing without an animation, when the"
-        "selected task is the foreground task."
-   bug: "314385883"
-   metadata {
-        purpose: PURPOSE_BUGFIX
-   }
-}
-
-flag {
    name: "pss_app_selector_recents_split_screen"
    namespace: "systemui"
    description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -292,13 +274,6 @@
 }
 
 flag {
-    name: "qs_new_pipeline"
-    namespace: "systemui"
-    description: "Use the new pipeline for Quick Settings. Should have no behavior changes."
-    bug: "241772429"
-}
-
-flag {
    name: "qs_new_tiles"
    namespace: "systemui"
    description: "Use the new tiles in the Quick Settings. Should have no behavior changes."
@@ -388,6 +363,52 @@
 }
 
 flag {
+   name: "status_bar_signal_policy_refactor"
+   namespace: "systemui"
+   description: "Use a settings observer for airplane mode and make StatusBarSignalPolicy startable"
+   bug: "264539100"
+}
+
+flag {
+    name: "status_bar_swipe_over_chip"
+    namespace: "systemui"
+    description: "Allow users to swipe over the status bar chip to open the shade"
+    bug: "185897191"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "status_bar_always_check_underlying_networks"
+    namespace: "systemui"
+    description: "For status bar connectivity UI, always check underlying networks for wifi and "
+        "carrier merged information, regardless of the sepcified transport type"
+    bug: "352162710"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "status_bar_stop_updating_window_height"
+    namespace: "systemui"
+    description: "Don't have PhoneStatusBarView manually trigger an update of the height in "
+        "StatusBarWindowController"
+    bug: "360115167"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "status_bar_ron_chips"
+    namespace: "systemui"
+    description: "Show rich ongoing notifications as chips in the status bar"
+    bug: "361346412"
+}
+
+flag {
     name: "compose_bouncer"
     namespace: "systemui"
     description: "Use the new compose bouncer in SystemUI"
@@ -504,6 +525,16 @@
 }
 
 flag {
+    name: "status_bar_switch_to_spn_from_data_spn"
+    namespace: "systemui"
+    description: "Fix usage of the SPN broadcast extras"
+    bug: "350812372"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "haptic_volume_slider"
     namespace: "systemui"
     description: "Adds haptic feedback to the volume slider."
@@ -528,6 +559,26 @@
 }
 
 flag {
+    name: "clipboard_shared_transitions"
+    namespace: "systemui"
+    description: "Show shared transitions from clipboard"
+    bug: "360843770"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "clipboard_image_timeout"
+    namespace: "systemui"
+    description: "Wait for clipboard image to load before showing UI"
+    bug: "359864629"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "screenshot_action_dismiss_system_windows"
     namespace: "systemui"
     description: "Dismiss existing system windows when starting action from screenshot UI"
@@ -555,16 +606,6 @@
 }
 
 flag {
-    name: "screenshot_private_profile_behavior_fix"
-    namespace: "systemui"
-    description: "Private profile support for screenshots"
-    bug: "327613051"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "screenshot_save_image_exporter"
     namespace: "systemui"
     description: "Save all screenshots using ImageExporter"
@@ -596,10 +637,13 @@
 }
 
 flag {
-   name: "smartspace_remoteviews_rendering"
+   name: "smartspace_remoteviews_rendering_fix"
    namespace: "systemui"
    description: "Indicate Smartspace RemoteViews rendering"
    bug: "326292691"
+   metadata {
+     purpose: PURPOSE_BUGFIX
+   }
 }
 
 flag {
@@ -610,6 +654,13 @@
 }
 
 flag {
+    name: "smartspace_viewpager2"
+    namespace: "systemui"
+    description: "Use viewpager2 in Smartspace"
+    bug: "259566300"
+}
+
+flag {
    name: "pin_input_field_styled_focus_state"
    namespace: "systemui"
    description: "Enables styled focus states on pin input field if keyboard is connected"
@@ -989,6 +1040,26 @@
 }
 
 flag {
+  name: "communal_widget_trampoline_fix"
+  namespace: "systemui"
+  description: "fixes activity starts caused by non-activity trampolines from widgets."
+  bug: "350468769"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
+  name: "communal_edit_widgets_activity_finish_fix"
+  namespace: "systemui"
+  description: "finish edit widgets activity when stopping"
+  bug: "354725145"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "app_clips_backlinks"
   namespace: "systemui"
   description: "Enables Backlinks improvement feature in App Clips"
@@ -1026,6 +1097,27 @@
 }
 
 flag {
+  name: "media_controls_button_media3"
+  namespace: "systemui"
+  description: "Enable media action buttons updates using media3"
+  bug: "360196209"
+}
+
+flag {
+  name: "media_controls_drawables_reuse"
+  namespace: "systemui"
+  description: "Re-use created media drawables for media controls"
+  bug: "358402034"
+}
+
+flag {
+  name: "media_controls_posts_optimization"
+  namespace: "systemui"
+  description: "Ignore duplicate media notifications posted"
+  bug: "358645640"
+}
+
+flag {
   namespace: "systemui"
   name: "enable_view_capture_tracing"
   description: "Enables view capture tracing in System UI."
@@ -1046,16 +1138,6 @@
 }
 
 flag {
-  name: "glanceable_hub_back_gesture"
-  namespace: "systemui"
-  description: "Enables back gesture on the glanceable hub"
-  bug: "346331399"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "glanceable_hub_allow_keyguard_when_dreaming"
   namespace: "systemui"
   description: "Allows users to exit dream to keyguard with glanceable hub enabled"
@@ -1248,14 +1330,15 @@
 }
 
 flag {
-   name: "lockscreen_preview_renderer_create_on_main_thread"
+   name: "relock_with_power_button_immediately"
    namespace: "systemui"
-   description: "Force preview renderer to be created on the main thread"
-   bug: "343732179"
+   description: "UDFPS unlock followed by immediate power button push should relock"
+   bug: "343327511"
    metadata {
         purpose: PURPOSE_BUGFIX
    }
 }
+
 flag {
    name: "classic_flags_multi_user"
    namespace: "systemui"
@@ -1281,4 +1364,35 @@
   metadata {
     purpose: PURPOSE_BUGFIX
   }
-}
\ No newline at end of file
+}
+
+flag {
+    name: "notify_password_text_view_user_activity_in_background"
+    namespace: "systemui"
+    description: "Decide whether to notify the user activity in password text view, to power manager in the background thread."
+    bug: "346882515"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "face_message_defer_update"
+    namespace: "systemui"
+    description: "Only analyze the last n frames when determining whether to defer a face auth help message like low light"
+    bug: "351863611"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    name: "media_load_metadata_via_media_data_loader"
+    namespace: "systemui"
+    description: "Use MediaDataLoader for loading media metadata with better threading"
+    bug: "358350077"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
diff --git a/packages/SystemUI/animation/build.gradle b/packages/SystemUI/animation/build.gradle
index 939455f..16ba42f 100644
--- a/packages/SystemUI/animation/build.gradle
+++ b/packages/SystemUI/animation/build.gradle
@@ -10,13 +10,6 @@
         }
     }
 
-    compileSdk 33
-
-    defaultConfig {
-        minSdk 33
-        targetSdk 33
-    }
-
     lintOptions {
         abortOnError false
     }
@@ -24,10 +17,6 @@
     tasks.withType(JavaCompile) {
         options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
     }
-    kotlinOptions {
-        jvmTarget = '1.8'
-        freeCompilerArgs = ["-Xjvm-default=all"]
-    }
 }
 
 dependencies {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt
new file mode 100644
index 0000000..8f5cdbf
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetector.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UClass
+import org.jetbrains.uast.getParentOfType
+
+/**
+ * Checks if registerContentObserver/registerContentObserverAsUser/unregisterContentObserver is
+ * called on a ContentResolver (or subclasses), and directs the caller to using
+ * com.android.systemui.util.settings.SettingsProxy or its sub-classes.
+ */
+@Suppress("UnstableApiUsage")
+class RegisterContentObserverViaContentResolverDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableMethodNames(): List<String> {
+        return CONTENT_RESOLVER_METHOD_LIST
+    }
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        val classQualifiedName = node.getParentOfType(UClass::class.java)?.qualifiedName
+        if (classQualifiedName in CLASSNAME_ALLOWLIST) {
+            // Don't warn for class we want the developers to use.
+            return
+        }
+
+        val evaluator = context.evaluator
+        if (evaluator.isMemberInSubClassOf(method, "android.content.ContentResolver")) {
+            context.report(
+                issue = CONTENT_RESOLVER_ERROR,
+                location = context.getNameLocation(node),
+                message =
+                    "`ContentResolver.${method.name}()` should be replaced with " +
+                        "an appropriate interface API call, for eg. " +
+                        "`<SettingsProxy>/<UserSettingsProxy>.${method.name}()`"
+            )
+        }
+    }
+
+    companion object {
+        @JvmField
+        val CONTENT_RESOLVER_ERROR: Issue =
+            Issue.create(
+                id = "RegisterContentObserverViaContentResolver",
+                briefDescription =
+                    "Content observer registration done via `ContentResolver`" +
+                        "instead of `SettingsProxy or child interfaces.`",
+                // lint trims indents and converts \ to line continuations
+                explanation =
+                    """
+                        Use registerContentObserver/unregisterContentObserver methods in \
+                        `SettingsProxy`, `UserSettingsProxy` or `GlobalSettings` class instead of \
+                        using `ContentResolver.registerContentObserver` or \
+                        `ContentResolver.unregisterContentObserver`.""",
+                category = Category.PERFORMANCE,
+                priority = 10,
+                severity = Severity.ERROR,
+                implementation =
+                    Implementation(
+                        RegisterContentObserverViaContentResolverDetector::class.java,
+                        Scope.JAVA_FILE_SCOPE
+                    )
+            )
+
+        private val CLASSNAME_ALLOWLIST =
+            listOf(
+                "com.android.systemui.util.settings.SettingsProxy",
+                "com.android.systemui.util.settings.UserSettingsProxy",
+                "com.android.systemui.util.settings.GlobalSettings",
+                "com.android.systemui.util.settings.SecureSettings",
+                "com.android.systemui.util.settings.SystemSettings"
+            )
+
+        private val CONTENT_RESOLVER_METHOD_LIST =
+            listOf(
+                "registerContentObserver",
+                "registerContentObserverAsUser",
+                "unregisterContentObserver"
+            )
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index 5206b05..a1f4f55 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -46,7 +46,8 @@
                 DemotingTestWithoutBugDetector.ISSUE,
                 TestFunctionNameViolationDetector.ISSUE,
                 MissingApacheLicenseDetector.ISSUE,
-                RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING
+                RegisterContentObserverSyncViaSettingsProxyDetector.SYNC_WARNING,
+                RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR
             )
 
     override val api: Int
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
new file mode 100644
index 0000000..1d33bce
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/RegisterContentObserverViaContentResolverDetectorTest.kt
@@ -0,0 +1,207 @@
+/*
+ * 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.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class RegisterContentObserverViaContentResolverDetectorTest : SystemUILintDetectorTest() {
+
+    override fun getDetector(): Detector = RegisterContentObserverViaContentResolverDetector()
+
+    override fun getIssues(): List<Issue> =
+        listOf(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+
+    @Test
+    fun testRegisterContentObserver_throwError() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+
+                    public class TestClass {
+                        public void register(Context context) {
+                          context.getContentResolver().
+                            registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                                false, mSettingObserver);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserver() [RegisterContentObserverViaContentResolver]
+                        registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                        ~~~~~~~~~~~~~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                    .trimIndent()
+            )
+    }
+
+    @Test
+    fun testRegisterContentObserverForUser_throwError() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+
+                    public class TestClass {
+                        public void register(Context context) {
+                          context.getContentResolver().
+                            registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                                false, mSettingObserver);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:7: Error: ContentResolver.registerContentObserverAsUser() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.registerContentObserverAsUser() [RegisterContentObserverViaContentResolver]
+        registerContentObserverAsUser(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+            """
+                    .trimIndent()
+            )
+    }
+
+    @Test
+    fun testSuppressRegisterContentObserver() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+
+                    public class TestClass {
+                        @SuppressWarnings("RegisterContentObserverViaContentResolver")
+                        public void register(Context context) {
+                          context.getContentResolver().
+                            registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                                false, mSettingObserver);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testRegisterContentObserverInSettingsProxy_allowed() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package com.android.systemui.util.settings;
+                    import android.content.Context;
+
+                    public class SettingsProxy {
+                        public void register(Context context) {
+                          context.getContentResolver().
+                            registerContentObserver(Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                                false, mSettingObserver);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testNoopIfNoCall() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+
+                    public class SettingsProxy {
+                        public void register(Context context) {
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testUnRegisterContentObserver_throwError() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                    package test.pkg;
+                    import android.content.Context;
+
+                    public class TestClass {
+                        public void register(Context context) {
+                          context.getContentResolver().
+                            unregisterContentObserver(mSettingObserver);
+                        }
+                    }
+                """
+                    )
+                    .indented(),
+                *androidStubs
+            )
+            .issues(RegisterContentObserverViaContentResolverDetector.CONTENT_RESOLVER_ERROR)
+            .run()
+            .expect(
+                """
+                src/test/pkg/TestClass.java:7: Error: ContentResolver.unregisterContentObserver() should be replaced with an appropriate interface API call, for eg. <SettingsProxy>/<UserSettingsProxy>.unregisterContentObserver() [RegisterContentObserverViaContentResolver]
+                        unregisterContentObserver(mSettingObserver);
+                        ~~~~~~~~~~~~~~~~~~~~~~~~~
+                1 errors, 0 warnings
+            """
+                    .trimIndent()
+            )
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index a18b460..a5f8057 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -21,8 +21,7 @@
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.material3.ButtonColors
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.Icon
@@ -32,7 +31,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import com.android.compose.theme.LocalAndroidColorScheme
 
@@ -42,11 +40,10 @@
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     colors: ButtonColors = filledButtonColors(),
-    verticalPadding: Dp = DefaultPlatformButtonVerticalPadding,
     content: @Composable RowScope.() -> Unit,
 ) {
     androidx.compose.material3.Button(
-        modifier = modifier.padding(vertical = verticalPadding).height(36.dp),
+        modifier = modifier.heightIn(min = 36.dp),
         colors = colors,
         contentPadding = ButtonPaddings,
         onClick = onClick,
@@ -63,11 +60,10 @@
     enabled: Boolean = true,
     colors: ButtonColors = outlineButtonColors(),
     border: BorderStroke? = outlineButtonBorder(),
-    verticalPadding: Dp = DefaultPlatformButtonVerticalPadding,
     content: @Composable RowScope.() -> Unit,
 ) {
     androidx.compose.material3.OutlinedButton(
-        modifier = modifier.padding(vertical = verticalPadding).height(36.dp),
+        modifier = modifier.heightIn(min = 36.dp),
         enabled = enabled,
         colors = colors,
         border = border,
@@ -118,7 +114,6 @@
     }
 }
 
-private val DefaultPlatformButtonVerticalPadding = 6.dp
 private val ButtonPaddings = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
 
 @Composable
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
index 2b1268e..5b368df 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/BouncerSceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene
 
 import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
index 94b5db2..74ce4bb 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/CommunalSceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene
 
 import com.android.systemui.communal.ui.compose.CommunalScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
index bc3fef1..871ade9 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/GoneSceneModule.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.scene
 
-import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.ui.composable.GoneScene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 72965fb..bfeaf92 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.keyguard.ui.composable.LockscreenSceneBlueprintModule
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.kt
new file mode 100644
index 0000000..e55520a
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeOverlayModule.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.scene
+
+import com.android.systemui.notifications.ui.composable.NotificationsShadeOverlay
+import com.android.systemui.scene.ui.composable.Overlay
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface NotificationsShadeOverlayModule {
+
+    @Binds @IntoSet fun notificationsShade(overlay: NotificationsShadeOverlay): Overlay
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
index 9b736b8..c58df35 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/NotificationsShadeSceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene
 
 import com.android.systemui.notifications.ui.composable.NotificationsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
index ee1f525..d55210d 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsSceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene
 
 import com.android.systemui.qs.ui.composable.QuickSettingsScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.kt
new file mode 100644
index 0000000..bc4adf9
--- /dev/null
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeOverlayModule.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.scene
+
+import com.android.systemui.qs.ui.composable.QuickSettingsShadeOverlay
+import com.android.systemui.scene.ui.composable.Overlay
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+interface QuickSettingsShadeOverlayModule {
+
+    @Binds @IntoSet fun quickSettingsShade(overlay: QuickSettingsShadeOverlay): Overlay
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
index 3d7401d..5bb6ae4 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/QuickSettingsShadeSceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene
 
 import com.android.systemui.qs.ui.composable.QuickSettingsShadeScene
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
index c655d6b..186914f 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ShadeSceneModule.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.scene
 
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.shade.ui.composable.ShadeScene
 import dagger.Binds
 import dagger.Module
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 f655ac1..34eafde 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
@@ -95,13 +95,14 @@
 import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
 import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModel
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
 import com.android.systemui.bouncer.ui.viewmodel.MessageViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.common.shared.model.Text.Companion.loadText
 import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.fold.ui.composable.foldPosture
 import com.android.systemui.fold.ui.helper.FoldPosture
 import com.android.systemui.res.R
@@ -114,7 +115,7 @@
 
 @Composable
 fun BouncerContent(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
@@ -128,7 +129,7 @@
 @VisibleForTesting
 fun BouncerContent(
     layout: BouncerSceneLayout,
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier
 ) {
@@ -173,7 +174,7 @@
  */
 @Composable
 private fun StandardLayout(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     val isHeightExpanded =
@@ -235,7 +236,7 @@
  */
 @Composable
 private fun SplitLayout(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
@@ -259,8 +260,11 @@
                         viewModel = viewModel.message,
                         modifier = Modifier.align(Alignment.TopCenter),
                     )
-
-                    OutputArea(viewModel = viewModel, modifier = Modifier.align(Alignment.Center))
+                    OutputArea(
+                        viewModel = viewModel,
+                        modifier =
+                            Modifier.align(Alignment.Center).sysuiResTag("bouncer_text_entry")
+                    )
 
                     ActionArea(
                         viewModel = viewModel,
@@ -310,8 +314,11 @@
                         StatusMessage(
                             viewModel = viewModel.message,
                         )
-
-                        OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
+                        OutputArea(
+                            viewModel = viewModel,
+                            modifier =
+                                Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
+                        )
                     }
                 }
                 else -> Unit
@@ -326,7 +333,7 @@
  */
 @Composable
 private fun BesideUserSwitcherLayout(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     val layoutDirection = LocalLayoutDirection.current
@@ -417,10 +424,9 @@
                     StatusMessage(
                         viewModel = viewModel.message,
                     )
-
                     OutputArea(
                         viewModel = viewModel,
-                        modifier = Modifier.padding(top = 24.dp).testTag("OutputArea")
+                        modifier = Modifier.padding(top = 24.dp).sysuiResTag("bouncer_text_entry")
                     )
                 }
             },
@@ -461,7 +467,7 @@
 /** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
 @Composable
 private fun BelowUserSwitcherLayout(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     Column(
@@ -485,7 +491,6 @@
                 StatusMessage(
                     viewModel = viewModel.message,
                 )
-
                 OutputArea(viewModel = viewModel, modifier = Modifier.padding(top = 24.dp))
 
                 InputArea(
@@ -506,7 +511,7 @@
 
 @Composable
 private fun FoldAware(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     aboveFold: @Composable BoxScope.() -> Unit,
     belowFold: @Composable BoxScope.() -> Unit,
     modifier: Modifier = Modifier,
@@ -528,7 +533,7 @@
     // Update state whenever currentSceneKey has changed.
     LaunchedEffect(state, currentSceneKey) {
         if (currentSceneKey != state.transitionState.currentScene) {
-            state.setTargetScene(currentSceneKey, coroutineScope = this)
+            state.setTargetScene(currentSceneKey, animationScope = this)
         }
     }
 
@@ -649,22 +654,21 @@
  */
 @Composable
 private fun OutputArea(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
         viewModel.authMethodViewModel.collectAsStateWithLifecycle()
-
     when (val nonNullViewModel = authMethodViewModel) {
         is PinBouncerViewModel ->
             PinInputDisplay(
                 viewModel = nonNullViewModel,
-                modifier = modifier,
+                modifier = modifier.sysuiResTag("bouncer_text_entry")
             )
         is PasswordBouncerViewModel ->
             PasswordBouncer(
                 viewModel = nonNullViewModel,
-                modifier = modifier,
+                modifier = modifier.sysuiResTag("bouncer_text_entry")
             )
         else -> Unit
     }
@@ -677,7 +681,7 @@
  */
 @Composable
 private fun InputArea(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     pinButtonRowVerticalSpacing: Dp,
     centerPatternDotsVertically: Boolean,
     modifier: Modifier = Modifier,
@@ -706,7 +710,7 @@
 
 @Composable
 private fun ActionArea(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     val actionButton: BouncerActionButtonModel? by
@@ -774,7 +778,7 @@
 
 @Composable
 private fun Dialog(
-    bouncerViewModel: BouncerViewModel,
+    bouncerViewModel: BouncerSceneContentViewModel,
     dialogFactory: BouncerDialogFactory,
 ) {
     val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
@@ -803,7 +807,7 @@
 /** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
 @Composable
 private fun UserSwitcher(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     modifier: Modifier = Modifier,
 ) {
     if (!viewModel.isUserSwitcherVisible) {
@@ -884,7 +888,7 @@
 @Composable
 private fun UserSwitcherDropdownMenu(
     isExpanded: Boolean,
-    items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
+    items: List<BouncerSceneContentViewModel.UserSwitcherDropdownItemViewModel>,
     onDismissed: () -> Unit,
 ) {
     val context = LocalContext.current
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 9fd30b4..7fb88e8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -27,11 +27,14 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.bouncer.ui.viewmodel.BouncerUserActionsViewModel
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
@@ -51,23 +54,36 @@
 class BouncerScene
 @Inject
 constructor(
-    private val viewModel: BouncerViewModel,
+    private val actionsViewModelFactory: BouncerUserActionsViewModel.Factory,
+    private val contentViewModelFactory: BouncerSceneContentViewModel.Factory,
     private val dialogFactory: BouncerDialogFactory,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
     override val key = Scenes.Bouncer
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        viewModel.destinationScenes
+    private val actionsViewModel: BouncerUserActionsViewModel by lazy {
+        actionsViewModelFactory.create()
+    }
+
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+    override suspend fun onActivated(): Nothing {
+        actionsViewModel.activate()
+    }
 
     @Composable
     override fun SceneScope.Content(
         modifier: Modifier,
-    ) = BouncerScene(viewModel, dialogFactory, modifier)
+    ) =
+        BouncerScene(
+            viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() },
+            dialogFactory = dialogFactory,
+            modifier = modifier,
+        )
 }
 
 @Composable
 private fun SceneScope.BouncerScene(
-    viewModel: BouncerViewModel,
+    viewModel: BouncerSceneContentViewModel,
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
 ) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 069113b..163b355 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -511,7 +511,7 @@
 private const val SELECTED_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 1.5).toInt()
 private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
 private const val SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS = 750
-private const val LINE_STROKE_WIDTH_DP = DOT_DIAMETER_DP
+private const val LINE_STROKE_WIDTH_DP = 22
 private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = (DOT_DIAMETER_DP * 0.81f).toInt()
 private const val FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS = 50
 private const val FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS = 33
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 54f3969..480e4e4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -24,30 +24,31 @@
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.tween
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.focusable
-import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-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.draw.drawBehind
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.geometry.CornerRadius
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalView
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
@@ -63,9 +64,7 @@
 import com.android.systemui.res.R
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.DurationUnit
-import kotlinx.coroutines.async
 import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 
 /** Renders the PIN button pad. */
@@ -94,11 +93,15 @@
         }
     }
 
+    // set the focus, so adb can send the key events for testing.
+    val focusRequester = FocusRequester()
+    LaunchedEffect(Unit) { focusRequester.requestFocus() }
+
     VerticalGrid(
         columns = columns,
         verticalSpacing = verticalSpacing,
         horizontalSpacing = calculateHorizontalSpacingBetweenColumns(gridWidth = 300.dp),
-        modifier = modifier,
+        modifier = modifier.focusRequester(focusRequester)
     ) {
         repeat(9) { index ->
             DigitButton(
@@ -234,7 +237,9 @@
     onLongPressed: (() -> Unit)? = null,
     content: @Composable (contentColor: () -> Color) -> Unit,
 ) {
-    var isPressed: Boolean by remember { mutableStateOf(false) }
+    val interactionSource = remember { MutableInteractionSource() }
+    val isPressed by interactionSource.collectIsPressedAsState()
+    val indication = LocalIndication.current.takeUnless { isPressed }
 
     val view = LocalView.current
     LaunchedEffect(isPressed) {
@@ -281,8 +286,6 @@
             animationSpec = colorAnimationSpec
         )
 
-    val scope = rememberCoroutineScope()
-
     Box(
         contentAlignment = Alignment.Center,
         modifier =
@@ -297,24 +300,14 @@
                         cornerRadius = CornerRadius(cornerRadius.toPx()),
                     )
                 }
+                .clip(CircleShape)
                 .thenIf(isEnabled) {
-                    Modifier.pointerInput(Unit) {
-                        detectTapGestures(
-                            onPress = {
-                                scope.launch {
-                                    isPressed = true
-                                    val minDuration = async {
-                                        delay(pinButtonPressedDuration + pinButtonHoldTime)
-                                    }
-                                    tryAwaitRelease()
-                                    minDuration.await()
-                                    isPressed = false
-                                }
-                            },
-                            onTap = { onClicked() },
-                            onLongPress = onLongPressed?.let { { onLongPressed() } },
-                        )
-                    }
+                    Modifier.combinedClickable(
+                        interactionSource = interactionSource,
+                        indication = indication,
+                        onClick = onClicked,
+                        onLongClick = onLongPressed
+                    )
                 },
     ) {
         content(contentColor::value)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 465eade..ba885f7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -34,8 +34,10 @@
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
@@ -351,14 +353,17 @@
 
     @Composable
     fun Content(modifier: Modifier) {
-        Row(
-            modifier =
+
+        // Wrap PIN entry in a Box so it is visible to accessibility (even if empty).
+        Box(modifier = modifier.fillMaxWidth().wrapContentHeight()) {
+            Row(
                 modifier
                     .heightIn(min = shapeAnimations.shapeSize)
                     // Pins overflowing horizontally should still be shown as scrolling.
-                    .wrapContentSize(unbounded = true),
-        ) {
-            entries.forEach { entry -> key(entry.digit) { entry.Content() } }
+                    .wrapContentSize(unbounded = true)
+            ) {
+                entries.forEach { entry -> key(entry.digit) { entry.Content() } }
+            }
         }
     }
 
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 872bef2..a4dc8fc 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
@@ -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.animation.scene.Back
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
@@ -47,7 +46,6 @@
 import com.android.compose.animation.scene.transitions
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.internal.R.attr.focusable
-import com.android.systemui.Flags.glanceableHubBackGesture
 import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -198,15 +196,7 @@
             Box(modifier = Modifier.fillMaxSize())
         }
 
-        val userActions =
-            if (glanceableHubBackGesture()) {
-                mapOf(
-                    Swipe(SwipeDirection.End) to CommunalScenes.Blank,
-                    Back to CommunalScenes.Blank,
-                )
-            } else {
-                mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
-            }
+        val userActions = mapOf(Swipe(SwipeDirection.End) to CommunalScenes.Blank)
 
         scene(CommunalScenes.Communal, userActions = userActions) {
             CommunalScene(
@@ -226,7 +216,7 @@
 
 /** Scene containing the glanceable hub UI. */
 @Composable
-private fun SceneScope.CommunalScene(
+fun SceneScope.CommunalScene(
     backgroundType: CommunalBackgroundType,
     colors: CommunalColors,
     content: CommunalContent,
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 8c38253..6fca178 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
@@ -28,6 +28,7 @@
 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
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
 import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -46,6 +47,7 @@
     private val bottomAreaSection: BottomAreaSection,
     private val ambientStatusBarSection: AmbientStatusBarSection,
     private val communalPopupSection: CommunalPopupSection,
+    private val widgetSection: CommunalAppWidgetSection,
 ) {
 
     @Composable
@@ -63,6 +65,7 @@
                             viewModel = viewModel,
                             interactionHandler = interactionHandler,
                             dialogFactory = dialogFactory,
+                            widgetSection = widgetSection,
                             modifier = Modifier.element(Communal.Elements.Grid)
                         )
                     }
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 b65b471..bcbf933 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
@@ -19,10 +19,7 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.drawable.Icon
-import android.os.Bundle
 import android.util.SizeF
-import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
-import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
 import android.widget.FrameLayout
 import android.widget.RemoteViews
 import androidx.annotation.VisibleForTesting
@@ -68,6 +65,7 @@
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.foundation.lazy.grid.itemsIndexed
 import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.selection.selectable
@@ -105,7 +103,6 @@
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.geometry.CornerRadius
@@ -169,22 +166,23 @@
 import com.android.systemui.communal.ui.compose.extensions.detectLongPressGesture
 import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
 import com.android.systemui.communal.ui.compose.extensions.observeTaps
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
-import com.android.systemui.communal.util.DensityUtils.Companion.scalingAdjustment
 import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialogFactory
 import kotlinx.coroutines.launch
 
-@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CommunalHub(
     modifier: Modifier = Modifier,
     viewModel: BaseCommunalViewModel,
+    widgetSection: CommunalAppWidgetSection,
     interactionHandler: RemoteViews.InteractionHandler? = null,
     dialogFactory: SystemUIDialogFactory? = null,
     widgetConfigurator: WidgetConfigurator? = null,
@@ -199,7 +197,12 @@
 
     val gridState =
         rememberLazyGridState(viewModel.savedFirstScrollIndex, viewModel.savedFirstScrollOffset)
-    viewModel.clearPersistedScrollPosition()
+
+    LaunchedEffect(Unit) {
+        if (!viewModel.isEditMode) {
+            viewModel.clearPersistedScrollPosition()
+        }
+    }
 
     val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
     val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
@@ -222,7 +225,6 @@
     val windowMetrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
     val screenWidth = windowMetrics.bounds.width()
     val layoutDirection = LocalLayoutDirection.current
-
     if (viewModel.isEditMode) {
         ObserveNewWidgetAddedEffect(communalContent, gridState, viewModel)
     } else {
@@ -383,6 +385,7 @@
                             selectedKey = selectedKey,
                             widgetConfigurator = widgetConfigurator,
                             interactionHandler = interactionHandler,
+                            widgetSection = widgetSection,
                         )
                     }
                 }
@@ -479,8 +482,7 @@
 }
 
 val hubDimensions: Dimensions
-    @Composable
-    get() = Dimensions(LocalContext.current, LocalConfiguration.current, LocalDensity.current)
+    @Composable get() = Dimensions(LocalContext.current, LocalConfiguration.current)
 
 @Composable
 private fun DisclaimerBottomSheetContent(onButtonClicked: () -> Unit) {
@@ -546,23 +548,31 @@
     communalContent: List<CommunalContentModel>,
     gridState: LazyGridState,
 ) {
-    val coroutineScope = rememberCoroutineScope()
     val liveContentKeys = remember { mutableListOf<String>() }
+    var communalContentPending by remember { mutableStateOf(true) }
 
     LaunchedEffect(communalContent) {
+        // Do nothing until any communal content comes in
+        if (communalContentPending && communalContent.isEmpty()) {
+            return@LaunchedEffect
+        }
+
         val prevLiveContentKeys = liveContentKeys.toList()
+        val newLiveContentKeys = communalContent.filter { it.isLiveContent() }.map { it.key }
         liveContentKeys.clear()
-        liveContentKeys.addAll(communalContent.filter { it.isLiveContent() }.map { it.key })
+        liveContentKeys.addAll(newLiveContentKeys)
 
-        // Find the first updated content
+        // Do nothing on first communal content since we don't have a delta
+        if (communalContentPending) {
+            communalContentPending = false
+            return@LaunchedEffect
+        }
+
+        // Do nothing if there is no new live content
         val indexOfFirstUpdatedContent =
-            liveContentKeys.indexOfFirst { !prevLiveContentKeys.contains(it) }
-
-        // Scroll if current position is behind the first updated content
+            newLiveContentKeys.indexOfFirst { !prevLiveContentKeys.contains(it) }
         if (indexOfFirstUpdatedContent in 0 until gridState.firstVisibleItemIndex) {
-            // Launching with a scope to prevent the job from being canceled in the case of a
-            // recomposition during scrolling
-            coroutineScope.launch { gridState.animateScrollToItem(indexOfFirstUpdatedContent) }
+            gridState.scrollToItem(indexOfFirstUpdatedContent)
         }
     }
 }
@@ -632,6 +642,7 @@
     updateDragPositionForRemove: (offset: Offset) -> Boolean,
     widgetConfigurator: WidgetConfigurator?,
     interactionHandler: RemoteViews.InteractionHandler?,
+    widgetSection: CommunalAppWidgetSection,
 ) {
     var gridModifier =
         Modifier.align(Alignment.TopStart).onGloballyPositioned { setGridCoordinates(it) }
@@ -680,21 +691,20 @@
         horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
         verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
     ) {
-        items(
-            count = list.size,
-            key = { index -> list[index].key },
-            contentType = { index -> list[index].key },
-            span = { index -> GridItemSpan(list[index].size.span) },
-        ) { index ->
+        itemsIndexed(
+            items = list,
+            key = { _, item -> item.key },
+            contentType = { _, item -> item.key },
+            span = { _, item -> GridItemSpan(item.size.span) },
+        ) { index, item ->
             val size =
                 SizeF(
                     Dimensions.CardWidth.value,
-                    list[index].size.dp().value,
+                    item.size.dp().value,
                 )
             val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
             if (viewModel.isEditMode && dragDropState != null) {
-                val selected by
-                    remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
+                val selected = item.key == selectedKey.value
                 DraggableItem(
                     modifier =
                         if (dragDropState.draggingItemIndex == index) {
@@ -706,12 +716,12 @@
                         },
                     dragDropState = dragDropState,
                     selected = selected,
-                    enabled = list[index].isWidgetContent(),
+                    enabled = item.isWidgetContent(),
                     index = index,
                 ) { isDragging ->
                     CommunalContent(
                         modifier = cardModifier,
-                        model = list[index],
+                        model = item,
                         viewModel = viewModel,
                         size = size,
                         selected = selected && !isDragging,
@@ -719,18 +729,20 @@
                         index = index,
                         contentListState = contentListState,
                         interactionHandler = interactionHandler,
+                        widgetSection = widgetSection,
                     )
                 }
             } else {
                 CommunalContent(
-                    modifier = cardModifier.animateItem(),
-                    model = list[index],
+                    model = item,
                     viewModel = viewModel,
                     size = size,
                     selected = false,
+                    modifier = cardModifier.animateItem(),
                     index = index,
                     contentListState = contentListState,
                     interactionHandler = interactionHandler,
+                    widgetSection = widgetSection,
                 )
             }
         }
@@ -972,6 +984,7 @@
     index: Int,
     contentListState: ContentListState,
     interactionHandler: RemoteViews.InteractionHandler?,
+    widgetSection: CommunalAppWidgetSection,
 ) {
     when (model) {
         is CommunalContentModel.WidgetContent.Widget ->
@@ -983,7 +996,8 @@
                 widgetConfigurator,
                 modifier,
                 index,
-                contentListState
+                contentListState,
+                widgetSection,
             )
         is CommunalContentModel.WidgetPlaceholder -> HighlightedItem(modifier)
         is CommunalContentModel.WidgetContent.DisabledWidget ->
@@ -1116,9 +1130,9 @@
     modifier: Modifier = Modifier,
     index: Int,
     contentListState: ContentListState,
+    widgetSection: CommunalAppWidgetSection,
 ) {
     val context = LocalContext.current
-    val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
     val accessibilityLabel =
         remember(model, context) {
             model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -1152,7 +1166,7 @@
                 .then(selectableModifier)
                 .thenIf(!viewModel.isEditMode && !model.inQuietMode) {
                     Modifier.pointerInput(Unit) {
-                        observeTaps { viewModel.onTapWidget(model.componentName, model.priority) }
+                        observeTaps { viewModel.onTapWidget(model.componentName, model.rank) }
                     }
                 }
                 .thenIf(!viewModel.isEditMode && model.inQuietMode) {
@@ -1205,36 +1219,14 @@
                     }
                 }
     ) {
-        AndroidView(
-            modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
-            factory = { context ->
-                model.appWidgetHost
-                    .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
-                    .apply {
-                        updateAppWidgetSize(
-                            /* newOptions = */ Bundle(),
-                            /* minWidth = */ size.width.toInt(),
-                            /* minHeight = */ size.height.toInt(),
-                            /* maxWidth = */ size.width.toInt(),
-                            /* maxHeight = */ size.height.toInt(),
-                            /* ignorePadding = */ true
-                        )
-                        accessibilityDelegate = viewModel.widgetAccessibilityDelegate
-                    }
-            },
-            update = {
-                it.apply {
-                    importantForAccessibility =
-                        if (isFocusable) {
-                            IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                        } else {
-                            IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                        }
-                }
-            },
-            // For reusing composition in lazy lists.
-            onReset = {},
-        )
+        with(widgetSection) {
+            Widget(
+                viewModel = viewModel,
+                model = model,
+                size = size,
+                modifier = Modifier.fillMaxSize().allowGestures(allowed = !viewModel.isEditMode),
+            )
+        }
         if (
             viewModel is CommunalEditModeViewModel &&
                 model.reconfigurable &&
@@ -1306,8 +1298,9 @@
         modifier =
             modifier
                 .background(
-                    MaterialTheme.colorScheme.surfaceVariant,
-                    RoundedCornerShape(dimensionResource(system_app_widget_background_radius))
+                    color = MaterialTheme.colorScheme.surfaceVariant,
+                    shape =
+                        RoundedCornerShape(dimensionResource(system_app_widget_background_radius))
                 )
                 .clickable(
                     enabled = !viewModel.isEditMode,
@@ -1343,10 +1336,8 @@
     Column(
         modifier =
             modifier.background(
-                MaterialTheme.colorScheme.surfaceVariant,
-                RoundedCornerShape(
-                    dimensionResource(system_app_widget_background_radius) * scalingAdjustment
-                )
+                color = MaterialTheme.colorScheme.surfaceVariant,
+                shape = RoundedCornerShape(dimensionResource(system_app_widget_background_radius))
             ),
         verticalArrangement = Arrangement.Center,
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -1428,7 +1419,10 @@
                                     R.string.accessibility_action_label_close_communal_hub
                                 )
                             ) {
-                                viewModel.changeScene(CommunalScenes.Blank)
+                                viewModel.changeScene(
+                                    CommunalScenes.Blank,
+                                    "closed by accessibility"
+                                )
                                 true
                             },
                             CustomAccessibilityAction(
@@ -1529,21 +1523,24 @@
     fun toOffset(): Offset = Offset(start, top)
 }
 
-class Dimensions(val context: Context, val config: Configuration, val density: Density) {
+class Dimensions(val context: Context, val config: Configuration) {
     val GridTopSpacing: Dp
         get() {
-            if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
-                return 114.adjustedDp
-            } else {
-                val windowMetrics =
-                    WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
-                val screenHeight = with(density) { windowMetrics.bounds.height().adjustedDp }
-
-                return (screenHeight - CardHeightFull) / 2
-            }
+            val result =
+                if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+                    114.dp
+                } else {
+                    val windowMetrics =
+                        WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+                    val density = context.resources.displayMetrics.density
+                    val screenHeight = (windowMetrics.bounds.height() / density).dp
+                    ((screenHeight - CardHeightFull) / 2)
+                }
+            return result
         }
 
-    val GridHeight = CardHeightFull + GridTopSpacing
+    val GridHeight: Dp
+        get() = CardHeightFull + GridTopSpacing
 
     companion object {
         val CardHeightFull
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 5886d7d..e41a7df 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -17,19 +17,23 @@
 package com.android.systemui.communal.ui.compose
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
+import com.android.systemui.communal.util.CommunalColors
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
+import com.android.systemui.scene.ui.composable.Scene
 import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -40,21 +44,36 @@
 @Inject
 constructor(
     private val viewModel: CommunalViewModel,
-    private val dialogFactory: SystemUIDialogFactory,
-    private val interactionHandler: WidgetInteractionHandler,
-) : ComposableScene {
+    private val communalColors: CommunalColors,
+    private val communalContent: CommunalContent,
+) : ExclusiveActivatable(), Scene {
     override val key = Scenes.Communal
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        MutableStateFlow<Map<UserAction, UserActionResult>>(
+    override val userActions: Flow<Map<UserAction, UserActionResult>> =
+        MutableStateFlow(
                 mapOf(
-                    Swipe(SwipeDirection.End) to UserActionResult(Scenes.Lockscreen),
+                    Swipe(SwipeDirection.End) to Scenes.Lockscreen,
                 )
             )
             .asStateFlow()
 
+    override suspend fun onActivated(): Nothing {
+        awaitCancellation()
+    }
+
     @Composable
     override fun SceneScope.Content(modifier: Modifier) {
-        CommunalHub(modifier, viewModel, interactionHandler, dialogFactory)
+        val backgroundType by
+            viewModel.communalBackground.collectAsStateWithLifecycle(
+                initialValue = CommunalBackgroundType.ANIMATED
+            )
+
+        CommunalScene(
+            backgroundType = backgroundType,
+            colors = communalColors,
+            content = communalContent,
+            viewModel = viewModel,
+            modifier = modifier.horizontalNestedScrollToScene(),
+        )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
index 38a3474..1137357 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt
@@ -34,11 +34,11 @@
     return remember(communalContent) {
         ContentListState(
             communalContent,
-            { componentName, user, priority ->
+            { componentName, user, rank ->
                 viewModel.onAddWidget(
                     componentName,
                     user,
-                    priority,
+                    rank,
                     widgetConfigurator,
                 )
             },
@@ -56,10 +56,9 @@
 class ContentListState
 internal constructor(
     communalContent: List<CommunalContentModel>,
-    private val onAddWidget:
-        (componentName: ComponentName, user: UserHandle, priority: Int) -> Unit,
-    private val onDeleteWidget: (id: Int, componentName: ComponentName, priority: Int) -> Unit,
-    private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit,
+    private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
+    private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
+    private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
 ) {
     var list = communalContent.toMutableStateList()
         private set
@@ -74,7 +73,7 @@
         if (list[indexToRemove].isWidgetContent()) {
             val widget = list[indexToRemove] as CommunalContentModel.WidgetContent
             list.apply { removeAt(indexToRemove) }
-            onDeleteWidget(widget.appWidgetId, widget.componentName, widget.priority)
+            onDeleteWidget(widget.appWidgetId, widget.componentName, widget.rank)
         }
     }
 
@@ -94,24 +93,24 @@
         newItemUser: UserHandle? = null,
         newItemIndex: Int? = null
     ) {
-        // filters placeholder, but, maintains the indices of the widgets as if the placeholder was
-        // in the list. When persisted in DB, this leaves space for the new item (to be added) at
-        // the correct priority.
-        val widgetIdToPriorityMap: Map<Int, Int> =
+        // New widget added to the grid. Other widgets are shifted as needed at the database level.
+        if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
+            onAddWidget(newItemComponentName, newItemUser, /* rank= */ newItemIndex)
+            return
+        }
+
+        // No new widget, only reorder existing widgets.
+        val widgetIdToRankMap: Map<Int, Int> =
             list
                 .mapIndexedNotNull { index, item ->
                     if (item is CommunalContentModel.WidgetContent) {
-                        item.appWidgetId to list.size - index
+                        item.appWidgetId to index
                     } else {
                         null
                     }
                 }
                 .toMap()
-        // reorder and then add the new widget
-        onReorderWidgets(widgetIdToPriorityMap)
-        if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
-            onAddWidget(newItemComponentName, newItemUser, /* priority= */ list.size - newItemIndex)
-        }
+        onReorderWidgets(widgetIdToRankMap)
     }
 
     /** Returns true if the item at given index is editable. */
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
index 0c29394..f2f7c87 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/DragAndDropTargetState.kt
@@ -193,7 +193,7 @@
             val widgetExtra = event.maybeWidgetExtra() ?: return false
             val (componentName, user) = widgetExtra
             if (componentName != null && user != null) {
-                // Placeholder isn't removed yet to allow the setting the right priority for items
+                // Placeholder isn't removed yet to allow the setting the right rank for items
                 // before adding in the new item.
                 contentListState.onSaveList(
                     newItemComponentName = componentName,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
new file mode 100644
index 0000000..ecb3d8c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.modifiers.background
+import com.android.compose.modifiers.height
+import com.android.compose.modifiers.width
+import com.android.systemui.deviceentry.shared.model.BiometricMessage
+import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
+import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
+import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.keyguard.ui.binder.AlternateBouncerUdfpsViewBinder
+import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.res.R
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@Composable
+fun AlternateBouncer(
+    alternateBouncerDependencies: AlternateBouncerDependencies,
+    modifier: Modifier = Modifier,
+) {
+
+    val isVisible by
+        alternateBouncerDependencies.viewModel.isVisible.collectAsStateWithLifecycle(
+            initialValue = false
+        )
+
+    val udfpsIconLocation by
+        alternateBouncerDependencies.udfpsIconViewModel.iconLocation.collectAsStateWithLifecycle(
+            initialValue = null
+        )
+
+    AnimatedVisibility(
+        visible = isVisible,
+        enter = fadeIn(),
+        exit = fadeOut(),
+        modifier = modifier,
+    ) {
+        Box(
+            contentAlignment = Alignment.TopCenter,
+            modifier =
+                Modifier.background(color = Colors.AlternateBouncerBackgroundColor, alpha = { 1f })
+                    .pointerInput(Unit) {
+                        detectTapGestures(
+                            onTap = { alternateBouncerDependencies.viewModel.onTapped() }
+                        )
+                    },
+        ) {
+            StatusMessage(
+                viewModel = alternateBouncerDependencies.messageAreaViewModel,
+            )
+        }
+
+        udfpsIconLocation?.let { udfpsLocation ->
+            Box {
+                DeviceEntryIcon(
+                    viewModel = alternateBouncerDependencies.udfpsIconViewModel,
+                    modifier =
+                        Modifier.width { udfpsLocation.width }
+                            .height { udfpsLocation.height }
+                            .fillMaxHeight()
+                            .offset {
+                                IntOffset(
+                                    x = udfpsLocation.left,
+                                    y = udfpsLocation.top,
+                                )
+                            },
+                )
+            }
+
+            UdfpsA11yOverlay(
+                viewModel = alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel.get(),
+                modifier = Modifier.fillMaxHeight(),
+            )
+        }
+    }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun StatusMessage(
+    viewModel: AlternateBouncerMessageAreaViewModel,
+    modifier: Modifier = Modifier,
+) {
+    val message: BiometricMessage? by
+        viewModel.message.collectAsStateWithLifecycle(initialValue = null)
+
+    Crossfade(
+        targetState = message,
+        label = "Alternate Bouncer message",
+        animationSpec = tween(),
+        modifier = modifier,
+    ) { biometricMessage ->
+        biometricMessage?.let {
+            Text(
+                textAlign = TextAlign.Center,
+                text = it.message ?: "",
+                color = Colors.AlternateBouncerTextColor,
+                fontSize = 18.sp,
+                lineHeight = 24.sp,
+                overflow = TextOverflow.Ellipsis,
+                modifier = Modifier.padding(top = 92.dp),
+            )
+        }
+    }
+}
+
+@ExperimentalCoroutinesApi
+@Composable
+private fun DeviceEntryIcon(
+    viewModel: AlternateBouncerUdfpsIconViewModel,
+    modifier: Modifier = Modifier,
+) {
+    AndroidView(
+        modifier = modifier,
+        factory = { context ->
+            val view =
+                DeviceEntryIconView(context, null).apply {
+                    id = R.id.alternate_bouncer_udfps_icon_view
+                    contentDescription =
+                        context.resources.getString(R.string.accessibility_fingerprint_label)
+                }
+            AlternateBouncerUdfpsViewBinder.bind(view, viewModel)
+            view
+        },
+    )
+}
+
+/** TODO (b/353955910): Validate accessibility CUJs */
+@ExperimentalCoroutinesApi
+@Composable
+private fun UdfpsA11yOverlay(
+    viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel,
+    modifier: Modifier = Modifier,
+) {
+    AndroidView(
+        factory = { context ->
+            val view =
+                UdfpsAccessibilityOverlay(context).apply {
+                    id = R.id.alternate_bouncer_udfps_accessibility_overlay
+                }
+            UdfpsAccessibilityOverlayBinder.bind(view, viewModel)
+            view
+        },
+        modifier = modifier,
+    )
+}
+
+private object Colors {
+    val AlternateBouncerBackgroundColor: Color = Color.Black.copy(alpha = .66f)
+    val AlternateBouncerTextColor: Color = Color.White
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 672b8a7..dbe7538 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -50,7 +50,7 @@
     fun SceneScope.Content(
         modifier: Modifier = Modifier,
     ) {
-        val viewModel = rememberViewModel { viewModelFactory.create() }
+        val viewModel = rememberViewModel("LockscreenContent") { viewModelFactory.create() }
         val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle()
         if (!isContentVisible) {
             // If the content isn't supposed to be visible, show a large empty box as it's needed
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 7fe1b3e..c7c29f9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -22,12 +22,13 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneActionsViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenUserActionsViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -37,19 +38,18 @@
 class LockscreenScene
 @Inject
 constructor(
-    actionsViewModelFactory: LockscreenSceneActionsViewModel.Factory,
+    actionsViewModelFactory: LockscreenUserActionsViewModel.Factory,
     private val lockscreenContent: Lazy<LockscreenContent>,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
     override val key = Scenes.Lockscreen
 
-    private val actionsViewModel: LockscreenSceneActionsViewModel by lazy {
+    private val actionsViewModel: LockscreenUserActionsViewModel by lazy {
         actionsViewModelFactory.create()
     }
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        actionsViewModel.actions
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
 
-    override suspend fun activate() {
+    override suspend fun onActivated(): Nothing {
         actionsViewModel.activate()
     }
 
@@ -69,7 +69,7 @@
     lockscreenContent: Lazy<LockscreenContent>,
     modifier: Modifier = Modifier,
 ) {
-    animateSceneFloatAsState(
+    animateContentFloatAsState(
         value = QuickSettings.SharedValues.SquishinessValues.LockscreenSceneStarting,
         key = QuickSettings.SharedValues.TilesSquishiness,
     )
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index a3e0701..8d5189e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -73,9 +73,12 @@
         val isShadeLayoutWide by viewModel.isShadeLayoutWide.collectAsStateWithLifecycle()
         val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
         val areNotificationsVisible by
-            viewModel
-                .areNotificationsVisible(contentKey)
-                .collectAsStateWithLifecycle(initialValue = false)
+            viewModel.areNotificationsVisible().collectAsStateWithLifecycle(initialValue = false)
+        val isBypassEnabled by viewModel.isBypassEnabled.collectAsStateWithLifecycle()
+
+        if (isBypassEnabled) {
+            with(notificationSection) { HeadsUpNotifications() }
+        }
 
         LockscreenLongPress(
             viewModel = viewModel.touchHandling,
@@ -110,7 +113,7 @@
                                             }
                                 )
                             }
-                            if (isShadeLayoutWide) {
+                            if (isShadeLayoutWide && !isBypassEnabled) {
                                 with(notificationSection) {
                                     Notifications(
                                         areNotificationsVisible = areNotificationsVisible,
@@ -124,7 +127,7 @@
                                 }
                             }
                         }
-                        if (!isShadeLayoutWide) {
+                        if (!isShadeLayoutWide && !isBypassEnabled) {
                             with(notificationSection) {
                                 Notifications(
                                     areNotificationsVisible = areNotificationsVisible,
@@ -175,7 +178,7 @@
                 },
                 modifier = Modifier.fillMaxSize(),
             ) { measurables, constraints ->
-                check(measurables.size == 6)
+                check(measurables.size == 6) { "Expected 6 measurables, got: ${measurables.size}" }
                 val aboveLockIconMeasurable = measurables[0]
                 val lockIconMeasurable = measurables[1]
                 val belowLockIconMeasurable = measurables[2]
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index bcdb259..eae46e9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -111,7 +111,7 @@
                     1f
                 }
 
-            val dir = if (transition.toScene == splitShadeLargeClockScene) -1f else 1f
+            val dir = if (transition.toContent == splitShadeLargeClockScene) -1f else 1f
             val distance = dir * getClockCenteringDistance()
             val largeClock = checkNotNull(currentClock).largeClock
             largeClock.animations.onPositionUpdated(
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 4129c25..46cd58c 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
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.util.DisplayMetrics
-import android.view.View
 import android.view.WindowManager
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -47,7 +46,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
-import com.android.systemui.shade.NotificationPanelView
 import com.android.systemui.statusbar.VibratorHelper
 import dagger.Lazy
 import javax.inject.Inject
@@ -66,7 +64,6 @@
     private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
     private val falsingManager: Lazy<FalsingManager>,
     private val vibratorHelper: Lazy<VibratorHelper>,
-    private val notificationPanelView: NotificationPanelView,
 ) {
     @Composable
     fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
@@ -74,10 +71,6 @@
             return
         }
 
-        notificationPanelView.findViewById<View?>(R.id.lock_icon_view)?.let {
-            notificationPanelView.removeView(it)
-        }
-
         val context = LocalContext.current
 
         AndroidView(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index 4e117d6..18e1092 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -20,7 +20,6 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.unit.Dp
@@ -32,7 +31,9 @@
 import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.notifications.ui.composable.ConstrainedNotificationStack
+import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
 import com.android.systemui.shade.LargeScreenHeaderHelper
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -48,7 +49,7 @@
 @Inject
 constructor(
     private val stackScrollView: Lazy<NotificationScrollView>,
-    private val viewModel: NotificationsPlaceholderViewModel,
+    private val viewModelFactory: NotificationsPlaceholderViewModel.Factory,
     private val aodBurnInViewModel: AodBurnInViewModel,
     sharedNotificationContainer: SharedNotificationContainer,
     sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
@@ -78,6 +79,14 @@
         )
     }
 
+    @Composable
+    fun SceneScope.HeadsUpNotifications() {
+        SnoozeableHeadsUpNotificationSpace(
+            stackScrollView = stackScrollView.get(),
+            viewModel = rememberViewModel("HeadsUpNotifications") { viewModelFactory.create() },
+        )
+    }
+
     /**
      * @param burnInParams params to make this view adaptive to burn-in, `null` to disable burn-in
      *   adjustment
@@ -98,7 +107,7 @@
 
         ConstrainedNotificationStack(
             stackScrollView = stackScrollView.get(),
-            viewModel = viewModel,
+            viewModel = rememberViewModel("Notifications") { viewModelFactory.create() },
             modifier =
                 modifier
                     .fillMaxWidth()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 0eeb79b..97d89a2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -93,7 +93,7 @@
         // Update state whenever currentSceneKey has changed.
         LaunchedEffect(state, currentScene) {
             if (currentScene != state.transitionState.currentScene) {
-                state.setTargetScene(currentScene, coroutineScope = this)
+                state.setTargetScene(currentScene, animationScope = this)
             }
         }
 
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 26ab10b..5f7b1ad 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
@@ -24,9 +24,10 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.layout.layout
-import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.viewinterop.AndroidView
 import com.android.compose.animation.scene.MovableElementKey
 import com.android.compose.animation.scene.SceneScope
@@ -34,6 +35,7 @@
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.res.R
 import com.android.systemui.util.animation.MeasurementInput
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 object MediaCarousel {
     object Elements {
@@ -45,44 +47,55 @@
     }
 }
 
+@ExperimentalCoroutinesApi
 @Composable
 fun SceneScope.MediaCarousel(
     isVisible: Boolean,
     mediaHost: MediaHost,
     modifier: Modifier = Modifier,
     carouselController: MediaCarouselController,
+    offsetProvider: (() -> IntOffset)? = null,
 ) {
-    if (!isVisible) {
+    if (!isVisible || carouselController.isLockedAndHidden()) {
         return
     }
 
-    val density = LocalDensity.current
     val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
 
-    val layoutWidth = 0
-    val layoutHeight = with(density) { mediaHeight.toPx() }.toInt()
-
-    // Notify controller to size the carousel for the current space
-    mediaHost.measurementInput = MeasurementInput(layoutWidth, layoutHeight)
-    carouselController.setSceneContainerSize(layoutWidth, layoutHeight)
-
     MovableElement(
         key = MediaCarousel.Elements.Content,
-        modifier = modifier.height(mediaHeight).fillMaxWidth()
+        modifier = modifier.height(mediaHeight).fillMaxWidth(),
     ) {
         content {
             AndroidView(
                 modifier =
-                    Modifier.fillMaxSize().layout { measurable, constraints ->
-                        val placeable = measurable.measure(constraints)
+                    Modifier.fillMaxSize()
+                        .approachLayout(
+                            isMeasurementApproachInProgress = { offsetProvider != null },
+                            approachMeasure = { measurable, constraints ->
+                                val placeable = measurable.measure(constraints)
+                                layout(placeable.width, placeable.height) {
+                                    placeable.placeRelative(
+                                        offsetProvider?.invoke() ?: IntOffset.Zero
+                                    )
+                                }
+                            }
+                        )
+                        .layout { measurable, constraints ->
+                            val placeable = measurable.measure(constraints)
 
-                        // Notify controller to size the carousel for the current space
-                        mediaHost.measurementInput =
-                            MeasurementInput(placeable.width, placeable.height)
-                        carouselController.setSceneContainerSize(placeable.width, placeable.height)
+                            // Notify controller to size the carousel for the current space
+                            mediaHost.measurementInput =
+                                MeasurementInput(placeable.width, placeable.height)
+                            carouselController.setSceneContainerSize(
+                                placeable.width,
+                                placeable.height
+                            )
 
-                        layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
-                    },
+                            layout(placeable.width, placeable.height) {
+                                placeable.placeRelative(0, 0)
+                            }
+                        },
                 factory = { context ->
                     FrameLayout(context).apply {
                         layoutParams =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
index 3f04f37..5dccb68 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaContentPicker.kt
@@ -21,13 +21,12 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.SceneTransitionLayoutState
 import com.android.compose.animation.scene.StaticElementContentPicker
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.systemui.scene.shared.model.Scenes
 
 /** [ElementContentPicker] implementation for the media carousel object. */
 object MediaContentPicker : StaticElementContentPicker {
 
-    const val SHADE_FRACTION = 0.66f
     override val contents =
         setOf(
             Scenes.Lockscreen,
@@ -39,7 +38,7 @@
 
     override fun contentDuringTransition(
         element: ElementKey,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         fromContentZIndex: Float,
         toContentZIndex: Float
     ): ContentKey {
@@ -65,7 +64,7 @@
     }
 
     /** Returns true when the media should be laid on top of the rest for the given [transition]. */
-    fun shouldElevateMedia(transition: ContentState.Transition<*>): Boolean {
+    fun shouldElevateMedia(transition: TransitionState.Transition): Boolean {
         return transition.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
     }
 }
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 0bef05dc..a2beba8 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
@@ -68,7 +68,6 @@
 import androidx.compose.ui.layout.onPlaced
 import androidx.compose.ui.layout.onSizeChanged
 import androidx.compose.ui.layout.positionInWindow
-import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.Dp
@@ -82,7 +81,6 @@
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.thenIf
-import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
 import com.android.systemui.res.R
@@ -137,14 +135,16 @@
                 .notificationHeadsUpHeight(stackScrollView)
                 .debugBackground(viewModel, DEBUG_HUN_COLOR)
                 .onGloballyPositioned { coordinates: LayoutCoordinates ->
+                    val positionInWindow = coordinates.positionInWindow()
                     val boundsInWindow = coordinates.boundsInWindow()
                     debugLog(viewModel) {
                         "HUNS onGloballyPositioned:" +
                             " size=${coordinates.size}" +
                             " bounds=$boundsInWindow"
                     }
-                    // Note: boundsInWindow doesn't scroll off the screen
-                    stackScrollView.setHeadsUpTop(boundsInWindow.top)
+                    // Note: boundsInWindow doesn't scroll off the screen, so use positionInWindow
+                    // for top bound, which can scroll off screen while snoozing
+                    stackScrollView.setHeadsUpTop(positionInWindow.y)
                     stackScrollView.setHeadsUpBottom(boundsInWindow.bottom)
                 }
     )
@@ -159,16 +159,10 @@
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
 ) {
-    val context = LocalContext.current
-    val density = LocalDensity.current
-    val statusBarHeight = SystemBarUtils.getStatusBarHeight(context)
-    val headsUpPadding =
-        with(density) { dimensionResource(id = R.dimen.heads_up_status_bar_padding).roundToPx() }
-
     val isHeadsUp by viewModel.isHeadsUpOrAnimatingAway.collectAsStateWithLifecycle(false)
 
     var scrollOffset by remember { mutableFloatStateOf(0f) }
-    val minScrollOffset = -(statusBarHeight + headsUpPadding.toFloat())
+    val minScrollOffset = -(stackScrollView.getHeadsUpInset().toFloat())
     val maxScrollOffset = 0f
 
     val scrollableState = rememberScrollableState { delta ->
@@ -327,7 +321,9 @@
     val minScrimOffset: () -> Float = { minScrimTop - maxScrimTop() }
 
     // The height of the scrim visible on screen when it is in its resting (collapsed) state.
-    val minVisibleScrimHeight: () -> Float = { screenHeight - maxScrimTop() }
+    val minVisibleScrimHeight: () -> Float = {
+        screenHeight - maxScrimTop() - with(density) { navBarHeight.toPx() }
+    }
 
     // we are not scrolled to the top unless the scrim is at its maximum offset.
     LaunchedEffect(viewModel, scrimOffset) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
new file mode 100644
index 0000000..a22becc
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.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.notifications.ui.composable
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.scene.session.ui.composable.SaveableSession
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class NotificationsShadeOverlay
+@Inject
+constructor(
+    private val actionsViewModelFactory: NotificationsShadeOverlayActionsViewModel.Factory,
+    private val contentViewModelFactory: NotificationsShadeOverlayContentViewModel.Factory,
+    private val tintedIconManagerFactory: TintedIconManager.Factory,
+    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+    private val statusBarIconController: StatusBarIconController,
+    private val shadeSession: SaveableSession,
+    private val stackScrollView: Lazy<NotificationScrollView>,
+) : Overlay {
+
+    override val key = Overlays.NotificationsShade
+
+    private val actionsViewModel: NotificationsShadeOverlayActionsViewModel by lazy {
+        actionsViewModelFactory.create()
+    }
+
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+    override suspend fun activate(): Nothing {
+        actionsViewModel.activate()
+    }
+
+    @Composable
+    override fun ContentScope.Content(
+        modifier: Modifier,
+    ) {
+        val viewModel =
+            rememberViewModel("NotificationsShadeOverlay-viewModel") {
+                contentViewModelFactory.create()
+            }
+        val placeholderViewModel =
+            rememberViewModel("NotificationsShadeOverlay-notifPlaceholderViewModel") {
+                viewModel.notificationsPlaceholderViewModelFactory.create()
+            }
+
+        OverlayShade(
+            modifier = modifier,
+            onScrimClicked = viewModel::onScrimClicked,
+        ) {
+            Column {
+                ExpandedShadeHeader(
+                    viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+                    createTintedIconManager = tintedIconManagerFactory::create,
+                    createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+                    statusBarIconController = statusBarIconController,
+                    modifier = Modifier.padding(horizontal = 16.dp),
+                )
+
+                NotificationScrollingStack(
+                    shadeSession = shadeSession,
+                    stackScrollView = stackScrollView.get(),
+                    viewModel = placeholderViewModel,
+                    maxScrimTop = { 0f },
+                    shouldPunchHoleBehindScrim = false,
+                    shouldFillMaxSize = false,
+                    shouldReserveSpaceForNavBar = false,
+                    shadeMode = ShadeMode.Dual,
+                    modifier = Modifier.fillMaxWidth(),
+                )
+
+                // Communicates the bottom position of the drawable area within the shade to NSSL.
+                NotificationStackCutoffGuideline(
+                    stackScrollView = stackScrollView.get(),
+                    viewModel = placeholderViewModel,
+                )
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index 66be7bc..1f4cd04 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -20,33 +20,28 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneContentViewModel
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.OverlayShade
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
 import com.android.systemui.statusbar.phone.ui.TintedIconManager
 import dagger.Lazy
-import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
@@ -54,29 +49,25 @@
 class NotificationsShadeScene
 @Inject
 constructor(
-    private val contentViewModelFactory: NotificationsShadeSceneContentViewModel.Factory,
-    private val actionsViewModelFactory: NotificationsShadeSceneActionsViewModel.Factory,
-    private val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
+    private val actionsViewModelFactory: NotificationsShadeUserActionsViewModel.Factory,
     private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
-    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
     private val tintedIconManagerFactory: TintedIconManager.Factory,
     private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
     private val statusBarIconController: StatusBarIconController,
     private val shadeSession: SaveableSession,
     private val stackScrollView: Lazy<NotificationScrollView>,
-    private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
 
     override val key = Scenes.NotificationsShade
 
-    private val actionsViewModel: NotificationsShadeSceneActionsViewModel by lazy {
+    private val actionsViewModel: NotificationsShadeUserActionsViewModel by lazy {
         actionsViewModelFactory.create()
     }
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        actionsViewModel.actions
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
 
-    override suspend fun activate() {
+    override suspend fun onActivated(): Nothing {
         actionsViewModel.activate()
     }
 
@@ -84,13 +75,14 @@
     override fun SceneScope.Content(
         modifier: Modifier,
     ) {
-        val viewModel = rememberViewModel { contentViewModelFactory.create() }
-        val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
+        val notificationsPlaceholderViewModel =
+            rememberViewModel("NotificationsShadeScene") {
+                notificationsPlaceholderViewModelFactory.create()
+            }
 
         OverlayShade(
             modifier = modifier,
-            viewModelFactory = overlayShadeViewModelFactory,
-            lockscreenContent = lockscreenContent,
+            onScrimClicked = {},
         ) {
             Column {
                 ExpandedShadeHeader(
@@ -110,8 +102,6 @@
                     shouldFillMaxSize = false,
                     shouldReserveSpaceForNavBar = false,
                     shadeMode = ShadeMode.Dual,
-                    onEmptySpaceClick =
-                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                     modifier = Modifier.fillMaxWidth(),
                 )
 
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 f399436..671b012 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
@@ -98,29 +98,31 @@
                 else -> QSSceneAdapter.State.CLOSED
             }
         }
-        is TransitionState.Transition ->
+        is TransitionState.Transition.ChangeScene ->
             with(transitionState) {
                 when {
                     isSplitShade -> UnsquishingQS(squishiness)
                     fromScene == Scenes.Shade && toScene == Scenes.QuickSettings -> {
-                        Expanding(progress)
+                        Expanding { progress }
                     }
                     fromScene == Scenes.QuickSettings && toScene == Scenes.Shade -> {
-                        Collapsing(progress)
+                        Collapsing { progress }
                     }
-                    fromScene == Scenes.Shade || toScene == Scenes.Shade -> {
+                    fromContent == Scenes.Shade || toContent == Scenes.Shade -> {
                         UnsquishingQQS(squishiness)
                     }
-                    fromScene == Scenes.QuickSettings || toScene == Scenes.QuickSettings -> {
+                    fromContent == Scenes.QuickSettings || toContent == Scenes.QuickSettings -> {
                         QSSceneAdapter.State.QS
                     }
                     else ->
                         error(
-                            "Bad transition for QuickSettings: fromScene=$fromScene," +
-                                " toScene=$toScene"
+                            "Bad transition for QuickSettings: fromContent=$fromContent," +
+                                " toScene=$toContent"
                         )
                 }
             }
+        is TransitionState.Transition.OverlayTransition ->
+            TODO("b/359173565: Handle overlay transitions")
     }
 }
 
@@ -212,7 +214,8 @@
                             addView(view)
                         }
                     },
-                    // When the view changes (e.g. due to a theme change), this will be recomposed
+                    // When the view changes (e.g. due to a theme change), this will be
+                    // recomposed
                     // if needed and the new view will be attached to the FrameLayout here.
                     update = {
                         qsSceneAdapter.setState(state())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 8bba0f4..d34295e 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
@@ -81,6 +81,7 @@
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -92,11 +93,12 @@
 import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQS
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneContentViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsUserActionsViewModel
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
@@ -111,27 +113,37 @@
 import javax.inject.Inject
 import javax.inject.Named
 import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 /** The Quick Settings (AKA "QS") scene shows the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class QuickSettingsScene
 @Inject
 constructor(
     private val shadeSession: SaveableSession,
     private val notificationStackScrollView: Lazy<NotificationScrollView>,
-    private val viewModel: QuickSettingsSceneViewModel,
-    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+    private val actionsViewModelFactory: QuickSettingsUserActionsViewModel.Factory,
+    private val contentViewModelFactory: QuickSettingsSceneContentViewModel.Factory,
     private val tintedIconManagerFactory: TintedIconManager.Factory,
     private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
     private val statusBarIconController: StatusBarIconController,
     private val mediaCarouselController: MediaCarouselController,
     @Named(MediaModule.QS_PANEL) private val mediaHost: MediaHost,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
     override val key = Scenes.QuickSettings
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        viewModel.destinationScenes
+    private val actionsViewModel: QuickSettingsUserActionsViewModel by lazy {
+        actionsViewModelFactory.create()
+    }
+
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+    override suspend fun onActivated(): Nothing {
+        actionsViewModel.activate()
+    }
 
     @Composable
     override fun SceneScope.Content(
@@ -139,8 +151,11 @@
     ) {
         QuickSettingsScene(
             notificationStackScrollView = notificationStackScrollView.get(),
-            viewModel = viewModel,
-            notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
+            viewModelFactory = contentViewModelFactory,
+            notificationsPlaceholderViewModel =
+                rememberViewModel("QuickSettingsScene-notifPlaceholderViewModel") {
+                    notificationsPlaceholderViewModelFactory.create()
+                },
             createTintedIconManager = tintedIconManagerFactory::create,
             createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
             statusBarIconController = statusBarIconController,
@@ -155,7 +170,7 @@
 @Composable
 private fun SceneScope.QuickSettingsScene(
     notificationStackScrollView: NotificationScrollView,
-    viewModel: QuickSettingsSceneViewModel,
+    viewModelFactory: QuickSettingsSceneContentViewModel.Factory,
     notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
     createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager,
     createBatteryMeterViewController: (ViewGroup, StatusBarLocation) -> BatteryMeterViewController,
@@ -167,9 +182,11 @@
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
 
-    val brightnessMirrorViewModel = rememberViewModel {
-        viewModel.brightnessMirrorViewModelFactory.create()
-    }
+    val viewModel = rememberViewModel("QuickSettingsScene-viewModel") { viewModelFactory.create() }
+    val brightnessMirrorViewModel =
+        rememberViewModel("QuickSettingsScene-brightnessMirrorViewModel") {
+            viewModel.brightnessMirrorViewModelFactory.create()
+        }
     val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
@@ -241,7 +258,7 @@
         val isScrollable =
             when (val state = layoutState.transitionState) {
                 is TransitionState.Idle -> true
-                is TransitionState.Transition -> state.fromScene == Scenes.QuickSettings
+                is TransitionState.Transition -> state.fromContent == Scenes.QuickSettings
             }
 
         LaunchedEffect(isCustomizing, scrollState) {
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
new file mode 100644
index 0000000..f8d0588
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt
@@ -0,0 +1,165 @@
+/*
+ * 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.ui.composable
+
+import androidx.compose.animation.AnimatedContent
+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.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.rememberViewModel
+import com.android.systemui.qs.panels.ui.compose.EditMode
+import com.android.systemui.qs.panels.ui.compose.TileGrid
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayContentViewModel
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
+import com.android.systemui.shade.ui.composable.OverlayShade
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class QuickSettingsShadeOverlay
+@Inject
+constructor(
+    private val actionsViewModelFactory: QuickSettingsShadeOverlayActionsViewModel.Factory,
+    private val contentViewModelFactory: QuickSettingsShadeOverlayContentViewModel.Factory,
+    private val tintedIconManagerFactory: TintedIconManager.Factory,
+    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+    private val statusBarIconController: StatusBarIconController,
+) : Overlay {
+
+    override val key = Overlays.QuickSettingsShade
+
+    private val actionsViewModel: QuickSettingsShadeOverlayActionsViewModel by lazy {
+        actionsViewModelFactory.create()
+    }
+
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+    override suspend fun activate(): Nothing {
+        actionsViewModel.activate()
+    }
+
+    @Composable
+    override fun ContentScope.Content(
+        modifier: Modifier,
+    ) {
+        val viewModel =
+            rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() }
+
+        OverlayShade(
+            modifier = modifier,
+            onScrimClicked = viewModel::onScrimClicked,
+        ) {
+            Column {
+                ExpandedShadeHeader(
+                    viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+                    createTintedIconManager = tintedIconManagerFactory::create,
+                    createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+                    statusBarIconController = statusBarIconController,
+                    modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
+                )
+
+                ShadeBody(
+                    viewModel = viewModel.quickSettingsContainerViewModel,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun ShadeBody(
+    viewModel: QuickSettingsContainerViewModel,
+) {
+    val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
+
+    AnimatedContent(
+        targetState = isEditing,
+        transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) }
+    ) { editing ->
+        if (editing) {
+            EditMode(
+                viewModel = viewModel.editModeViewModel,
+                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
+            )
+        } else {
+            QuickSettingsLayout(
+                viewModel = viewModel,
+                modifier = Modifier.sysuiResTag("quick_settings_panel")
+            )
+        }
+    }
+}
+
+@Composable
+private fun QuickSettingsLayout(
+    viewModel: QuickSettingsContainerViewModel,
+    modifier: Modifier = Modifier,
+) {
+    Column(
+        verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
+        horizontalAlignment = Alignment.CenterHorizontally,
+        modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+    ) {
+        BrightnessSliderContainer(
+            viewModel = viewModel.brightnessSliderViewModel,
+            modifier =
+                Modifier.fillMaxWidth()
+                    .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+        )
+        TileGrid(
+            viewModel = viewModel.tileGridViewModel,
+            modifier =
+                Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+            viewModel.editModeViewModel::startEditing,
+        )
+    }
+}
+
+object QuickSettingsShade {
+
+    object Dimensions {
+        val Padding = 16.dp
+        val BrightnessSliderHeight = 64.dp
+        val GridMaxHeight = 800.dp
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index eea00c4..e27c7e2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -16,51 +16,26 @@
 
 package com.android.systemui.qs.ui.composable
 
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-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.layout.Arrangement
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.qs.panels.ui.compose.EditMode
-import com.android.systemui.qs.panels.ui.compose.TileGrid
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutEnter
-import com.android.systemui.qs.ui.composable.QuickSettingsShade.Transitions.QuickSettingsLayoutExit
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
-import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneActionsViewModel
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneContentViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeUserActionsViewModel
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
 import com.android.systemui.statusbar.phone.ui.TintedIconManager
-import dagger.Lazy
-import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 
@@ -68,33 +43,36 @@
 class QuickSettingsShadeScene
 @Inject
 constructor(
-    private val actionsViewModelFactory: QuickSettingsShadeSceneActionsViewModel.Factory,
+    private val actionsViewModelFactory: QuickSettingsShadeUserActionsViewModel.Factory,
     private val contentViewModelFactory: QuickSettingsShadeSceneContentViewModel.Factory,
-    private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
     private val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
     private val tintedIconManagerFactory: TintedIconManager.Factory,
     private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
     private val statusBarIconController: StatusBarIconController,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
 
     override val key = Scenes.QuickSettingsShade
 
-    private val actionsViewModel: QuickSettingsShadeSceneActionsViewModel by lazy {
+    private val actionsViewModel: QuickSettingsShadeUserActionsViewModel by lazy {
         actionsViewModelFactory.create()
     }
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        actionsViewModel.actions
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
+
+    override suspend fun onActivated(): Nothing {
+        actionsViewModel.activate()
+    }
 
     @Composable
     override fun SceneScope.Content(
         modifier: Modifier,
     ) {
-        val viewModel = rememberViewModel { contentViewModelFactory.create() }
+        val viewModel =
+            rememberViewModel("QuickSettingsShadeScene") { contentViewModelFactory.create() }
+
         OverlayShade(
-            viewModelFactory = viewModel.overlayShadeViewModelFactory,
-            lockscreenContent = lockscreenContent,
             modifier = modifier,
+            onScrimClicked = {},
         ) {
             Column {
                 ExpandedShadeHeader(
@@ -112,72 +90,3 @@
         }
     }
 }
-
-@Composable
-private fun ShadeBody(
-    viewModel: QuickSettingsContainerViewModel,
-) {
-    val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
-
-    AnimatedContent(
-        targetState = isEditing,
-        transitionSpec = { QuickSettingsLayoutEnter togetherWith QuickSettingsLayoutExit }
-    ) { editing ->
-        if (editing) {
-            EditMode(
-                viewModel = viewModel.editModeViewModel,
-                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
-            )
-        } else {
-            QuickSettingsLayout(
-                viewModel = viewModel,
-            )
-        }
-    }
-}
-
-@Composable
-private fun QuickSettingsLayout(
-    viewModel: QuickSettingsContainerViewModel,
-    modifier: Modifier = Modifier,
-) {
-    Column(
-        verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
-        horizontalAlignment = Alignment.CenterHorizontally,
-        modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
-    ) {
-        BrightnessSliderContainer(
-            viewModel = viewModel.brightnessSliderViewModel,
-            modifier =
-                Modifier.fillMaxWidth()
-                    .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
-        )
-        TileGrid(
-            viewModel = viewModel.tileGridViewModel,
-            modifier =
-                Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
-            viewModel.editModeViewModel::startEditing,
-        )
-        Button(
-            onClick = { viewModel.editModeViewModel.startEditing() },
-        ) {
-            Text("Edit mode")
-        }
-    }
-}
-
-object QuickSettingsShade {
-
-    object Dimensions {
-        val Padding = 16.dp
-        val BrightnessSliderHeight = 64.dp
-        val GridMaxHeight = 800.dp
-    }
-
-    object Transitions {
-        val QuickSettingsLayoutEnter: EnterTransition = fadeIn(tween(500))
-        val QuickSettingsLayoutExit: ExitTransition = fadeOut(tween(500))
-        val QuickSettingsEditorEnter: EnterTransition = fadeIn(tween(500))
-        val QuickSettingsEditorExit: ExitTransition = fadeOut(tween(500))
-    }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.kt
new file mode 100644
index 0000000..8fe6893
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ActionableContent.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.scene.ui.composable
+
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for content that can respond to user-actions. */
+interface ActionableContent {
+    /**
+     * The mapping between [UserAction] and destination [UserActionResult]s.
+     *
+     * When the scene framework detects a user action, if the current scene has a map entry for that
+     * user action, the framework starts a transition to the content specified in the map.
+     *
+     * Once the content is shown, the scene framework will read this property and set up a collector
+     * to watch for new mapping values. For each map entry, the scene framework will set up user
+     * input handling for its [UserAction] and, if such a user action is detected, initiate a
+     * transition to the specified [UserActionResult].
+     *
+     * Note that reading from this method does _not_ mean that any user action has occurred.
+     * Instead, the property is read before any user action/gesture is detected so that the
+     * framework can decide whether to set up gesture/input detectors/listeners in case user actions
+     * of the given types ever occur.
+     *
+     * A missing value for a specific [UserAction] means that the user action of the given type is
+     * not currently active in the top-most content (in z-index order) and should be ignored by the
+     * framework until the top-most content changes.
+     */
+    val userActions: Flow<Map<UserAction, UserActionResult>>
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
deleted file mode 100644
index 3da6a02..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
+++ /dev/null
@@ -1,27 +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.scene.ui.composable
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.compose.animation.scene.SceneScope
-import com.android.systemui.scene.shared.model.Scene
-
-/** Compose-capable extension of [Scene]. */
-interface ComposableScene : Scene {
-    @Composable fun SceneScope.Content(modifier: Modifier)
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index b0c3fb31..ae5dd8a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -23,15 +23,17 @@
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
-import com.android.compose.animation.scene.animateSceneDpAsState
-import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.compose.animation.scene.animateContentDpAsState
+import com.android.compose.animation.scene.animateContentFloatAsState
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.Default
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.viewmodel.GoneSceneActionsViewModel
+import com.android.systemui.scene.ui.viewmodel.GoneUserActionsViewModel
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import dagger.Lazy
@@ -47,17 +49,16 @@
 @Inject
 constructor(
     private val notificationStackScrolLView: Lazy<NotificationScrollView>,
-    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
-    private val viewModelFactory: GoneSceneActionsViewModel.Factory,
-) : ComposableScene {
+    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+    private val viewModelFactory: GoneUserActionsViewModel.Factory,
+) : ExclusiveActivatable(), Scene {
     override val key = Scenes.Gone
 
-    private val actionsViewModel: GoneSceneActionsViewModel by lazy { viewModelFactory.create() }
+    private val actionsViewModel: GoneUserActionsViewModel by lazy { viewModelFactory.create() }
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        actionsViewModel.actions
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
 
-    override suspend fun activate() {
+    override suspend fun onActivated(): Nothing {
         actionsViewModel.activate()
     }
 
@@ -65,15 +66,18 @@
     override fun SceneScope.Content(
         modifier: Modifier,
     ) {
-        animateSceneFloatAsState(
+        animateContentFloatAsState(
             value = QuickSettings.SharedValues.SquishinessValues.GoneSceneStarting,
             key = QuickSettings.SharedValues.TilesSquishiness,
         )
-        animateSceneDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
+        animateContentDpAsState(value = Default, key = MediaLandscapeTopOffset, canOverflow = false)
         Spacer(modifier.fillMaxSize())
         SnoozeableHeadsUpNotificationSpace(
             stackScrollView = notificationStackScrolLView.get(),
-            viewModel = notificationsPlaceholderViewModel
+            viewModel =
+                rememberViewModel("GoneScene") {
+                    notificationsPlaceholderViewModelFactory.create()
+                },
         )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.kt
new file mode 100644
index 0000000..609ce90
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Overlay.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.scene.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.lifecycle.Activatable
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/**
+ * Defines interface for classes that can describe an "overlay".
+ *
+ * In the scene framework, there can be multiple overlays in a single scene "container". The
+ * container takes care of rendering any current overlays and allowing overlays to be shown, hidden,
+ * or replaced based on a user action.
+ */
+interface Overlay : Activatable, ActionableContent {
+    /** Uniquely-identifying key for this overlay. The key must be unique within its container. */
+    val key: OverlayKey
+
+    /**
+     * The user actions supported by this overlay.
+     *
+     * @see [ActionableContent.userActions]
+     */
+    override val userActions: Flow<Map<UserAction, UserActionResult>>
+        get() = flowOf(mapOf(Back to UserActionResult.HideOverlay(key)))
+
+    @Composable fun ContentScope.Content(modifier: Modifier)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt
new file mode 100644
index 0000000..8d8ab8e
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.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.scene.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.lifecycle.Activatable
+
+/**
+ * Defines interface for classes that can describe a "scene".
+ *
+ * In the scene framework, there can be multiple scenes in a single scene "container". The container
+ * takes care of rendering the current scene and allowing scenes to be switched from one to another
+ * based on either user action (for example, swiping down while on the lock screen scene may switch
+ * to the shade scene).
+ */
+interface Scene : Activatable, ActionableContent {
+
+    /** Uniquely-identifying key for this scene. The key must be unique within its container. */
+    val key: SceneKey
+
+    @Composable fun SceneScope.Content(modifier: Modifier)
+}
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 d5874d1..3cb0d8a 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
@@ -16,6 +16,8 @@
 
 package com.android.systemui.scene.ui.composable
 
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material3.Text
@@ -28,7 +30,10 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayout
 import com.android.compose.animation.scene.UserAction
@@ -49,17 +54,23 @@
  * containers.
  *
  * @param viewModel The UI state holder for this container.
- * @param sceneByKey Mapping of [ComposableScene] by [SceneKey], ordered by z-order such that the
- *   last scene is rendered on top of all other scenes. It's critical that this map contains exactly
- *   and only the scenes on this container. In other words: (a) there should be no scene in this map
- *   that is not in the configuration for this container and (b) all scenes in the configuration
- *   must have entries in this map.
+ * @param sceneByKey Mapping of [Scene] by [SceneKey], ordered by z-order such that the last scene
+ *   is rendered on top of all other scenes. It's critical that this map contains exactly and only
+ *   the scenes on this container. In other words: (a) there should be no scene in this map that is
+ *   not in the configuration for this container and (b) all scenes in the configuration must have
+ *   entries in this map.
+ * @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the last
+ *   overlay is rendered on top of all other overlays. It's critical that this map contains exactly
+ *   and only the overlays on this container. In other words: (a) there should be no overlay in this
+ *   map that is not in the configuration for this container and (b) all overlays in the
+ *   configuration must have entries in this map.
  * @param modifier A modifier.
  */
 @Composable
 fun SceneContainer(
     viewModel: SceneContainerViewModel,
-    sceneByKey: Map<SceneKey, ComposableScene>,
+    sceneByKey: Map<SceneKey, Scene>,
+    overlayByKey: Map<OverlayKey, Overlay>,
     initialSceneKey: SceneKey,
     dataSourceDelegator: SceneDataSourceDelegator,
     modifier: Modifier = Modifier,
@@ -73,7 +84,6 @@
             enableInterruptions = false,
         )
     }
-    val currentSceneKey = state.transitionState.currentScene
 
     DisposableEffect(state) {
         val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
@@ -86,39 +96,67 @@
         onDispose { viewModel.setTransitionState(null) }
     }
 
-    val userActionsBySceneKey: MutableMap<SceneKey, Map<UserAction, UserActionResult>> = remember {
-        mutableStateMapOf()
-    }
-    LaunchedEffect(currentSceneKey) {
+    val actionableContentKey =
+        viewModel.getActionableContentKey(state.currentScene, state.currentOverlays, overlayByKey)
+    val userActionsByContentKey: MutableMap<ContentKey, Map<UserAction, UserActionResult>> =
+        remember {
+            mutableStateMapOf()
+        }
+    LaunchedEffect(actionableContentKey) {
         try {
-            sceneByKey[currentSceneKey]?.destinationScenes?.collectLatest { userActions ->
-                userActionsBySceneKey[currentSceneKey] = viewModel.resolveSceneFamilies(userActions)
+            val actionableContent: ActionableContent =
+                checkNotNull(
+                    overlayByKey[actionableContentKey] ?: sceneByKey[actionableContentKey]
+                ) {
+                    "invalid ContentKey: $actionableContentKey"
+                }
+            actionableContent.userActions.collectLatest { userActions ->
+                userActionsByContentKey[actionableContentKey] =
+                    viewModel.resolveSceneFamilies(userActions)
             }
         } finally {
-            userActionsBySceneKey[currentSceneKey] = emptyMap()
+            userActionsByContentKey[actionableContentKey] = emptyMap()
         }
     }
 
     Box(
-        modifier = Modifier.fillMaxSize(),
+        modifier =
+            Modifier.fillMaxSize().pointerInput(Unit) {
+                awaitEachGesture {
+                    awaitFirstDown(false)
+                    viewModel.onSceneContainerUserInputStarted()
+                }
+            },
     ) {
         SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
-            sceneByKey.forEach { (sceneKey, composableScene) ->
+            sceneByKey.forEach { (sceneKey, scene) ->
                 scene(
                     key = sceneKey,
-                    userActions = userActionsBySceneKey.getOrDefault(sceneKey, emptyMap())
+                    userActions = userActionsByContentKey.getOrDefault(sceneKey, emptyMap())
                 ) {
                     // Activate the scene.
-                    LaunchedEffect(composableScene) { composableScene.activate() }
+                    LaunchedEffect(scene) { scene.activate() }
 
                     // Render the scene.
-                    with(composableScene) {
+                    with(scene) {
                         [email protected](
                             modifier = Modifier.element(sceneKey.rootElementKey).fillMaxSize(),
                         )
                     }
                 }
             }
+            overlayByKey.forEach { (overlayKey, overlay) ->
+                overlay(
+                    key = overlayKey,
+                    userActions = userActionsByContentKey.getOrDefault(overlayKey, emptyMap())
+                ) {
+                    // Activate the overlay.
+                    LaunchedEffect(overlay) { overlay.activate() }
+
+                    // Render the overlay.
+                    with(overlay) { [email protected](Modifier) }
+                }
+            }
         }
 
         BottomRightCornerRibbon(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
index 8751ca7..5a350a6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt
@@ -1,15 +1,16 @@
 package com.android.systemui.scene.ui.composable
 
 import androidx.compose.foundation.gestures.Orientation
-import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.ProgressConverter
+import com.android.compose.animation.scene.TransitionKey
 import com.android.compose.animation.scene.transitions
 import com.android.systemui.bouncer.ui.composable.Bouncer
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
 import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
 import com.android.systemui.scene.ui.composable.transitions.bouncerToGoneTransition
+import com.android.systemui.scene.ui.composable.transitions.bouncerToLockscreenPreview
 import com.android.systemui.scene.ui.composable.transitions.goneToNotificationsShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.goneToQuickSettingsTransition
@@ -41,21 +42,14 @@
  */
 val SceneContainerTransitions = transitions {
 
+    // Overscroll progress starts linearly with some resistance (3f) and slowly approaches 0.2f
+    defaultOverscrollProgressConverter = ProgressConverter.tanh(maxProgress = 0.2f, tilt = 3f)
+
     // Scene transitions
 
     from(Scenes.Bouncer, to = Scenes.Gone) { bouncerToGoneTransition() }
-    from(Scenes.Gone, to = Scenes.NotificationsShade) {
-        goneToNotificationsShadeTransition(Edge.Top)
-    }
-    from(Scenes.Gone, to = Scenes.NotificationsShade, key = OpenBottomShade) {
-        goneToNotificationsShadeTransition(Edge.Bottom)
-    }
-    from(Scenes.Gone, to = Scenes.QuickSettingsShade) {
-        goneToQuickSettingsShadeTransition(Edge.Top)
-    }
-    from(Scenes.Gone, to = Scenes.QuickSettingsShade, key = OpenBottomShade) {
-        goneToQuickSettingsShadeTransition(Edge.Bottom)
-    }
+    from(Scenes.Gone, to = Scenes.NotificationsShade) { goneToNotificationsShadeTransition() }
+    from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() }
     from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
     from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
     from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
@@ -67,6 +61,14 @@
     }
 
     from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() }
+    from(
+        Scenes.Lockscreen,
+        to = Scenes.Bouncer,
+        key = TransitionKey.PredictiveBack,
+        reversePreview = { bouncerToLockscreenPreview() }
+    ) {
+        lockscreenToBouncerTransition()
+    }
     from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
     from(Scenes.Lockscreen, to = Scenes.NotificationsShade) {
         lockscreenToNotificationsShadeTransition()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index 4b4b7ed..6738b97 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -18,7 +18,9 @@
 
 package com.android.systemui.scene.ui.composable
 
+import androidx.compose.runtime.snapshotFlow
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.compose.animation.scene.observableTransitionState
@@ -52,6 +54,14 @@
                 initialValue = state.transitionState.currentScene,
             )
 
+    override val currentOverlays: StateFlow<Set<OverlayKey>> =
+        snapshotFlow { state.currentOverlays }
+            .stateIn(
+                scope = coroutineScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = emptySet(),
+            )
+
     override fun changeScene(
         toScene: SceneKey,
         transitionKey: TransitionKey?,
@@ -59,7 +69,7 @@
         state.setTargetScene(
             targetScene = toScene,
             transitionKey = transitionKey,
-            coroutineScope = coroutineScope,
+            animationScope = coroutineScope,
         )
     }
 
@@ -68,4 +78,29 @@
             scene = toScene,
         )
     }
+
+    override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        state.showOverlay(
+            overlay = overlay,
+            animationScope = coroutineScope,
+            transitionKey = transitionKey,
+        )
+    }
+
+    override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        state.hideOverlay(
+            overlay = overlay,
+            animationScope = coroutineScope,
+            transitionKey = transitionKey,
+        )
+    }
+
+    override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) {
+        state.replaceOverlay(
+            from = from,
+            to = to,
+            animationScope = coroutineScope,
+            transitionKey = transitionKey,
+        )
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
index fb41374..48ec198 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToNotificationsShadeTransition.kt
@@ -16,12 +16,10 @@
 
 package com.android.systemui.scene.ui.composable.transitions
 
-import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
 
 fun TransitionBuilder.goneToNotificationsShadeTransition(
-    edge: Edge = Edge.Top,
     durationScale: Double = 1.0,
 ) {
-    toNotificationsShadeTransition(edge, durationScale)
+    toNotificationsShadeTransition(durationScale)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
index a9da733..71fa6c9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromGoneToSplitShadeTransition.kt
@@ -25,7 +25,6 @@
 import com.android.compose.animation.scene.UserActionDistance
 import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaContentPicker
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.shade.ui.composable.Shade
@@ -54,13 +53,7 @@
     fractionRange(end = .33f) { fade(Shade.Elements.BackgroundScrim) }
 
     fractionRange(start = .33f) {
-        val qsTranslation =
-            ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
-        val qsExpansionDiff =
-            ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
-        translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
         fade(MediaCarousel.Elements.Content)
-
         fade(ShadeHeader.Elements.Clock)
         fade(ShadeHeader.Elements.CollapsedContentStart)
         fade(ShadeHeader.Elements.CollapsedContentEnd)
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 1fee874..ac54896 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,14 +1,27 @@
 package com.android.systemui.scene.ui.composable.transitions
 
+import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.tween
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.systemui.bouncer.ui.composable.Bouncer
 
+const val FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION = 0.5f
+
 fun TransitionBuilder.lockscreenToBouncerTransition() {
     spec = tween(durationMillis = 500)
 
     translate(Bouncer.Elements.Content, y = 300.dp)
-    fractionRange(end = 0.5f) { fade(Bouncer.Elements.Background) }
-    fractionRange(start = 0.5f) { fade(Bouncer.Elements.Content) }
+    fractionRange(end = FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION) {
+        fade(Bouncer.Elements.Background)
+    }
+    fractionRange(start = FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION) {
+        fade(Bouncer.Elements.Content)
+    }
+}
+
+fun TransitionBuilder.bouncerToLockscreenPreview() {
+    fractionRange(easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)) {
+        scaleDraw(Bouncer.Elements.Content, scaleY = 0.8f, scaleX = 0.8f)
+    }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
index 5401936..826a255 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -19,14 +19,19 @@
 import androidx.compose.animation.core.tween
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.communal.ui.compose.AllElements
+import com.android.systemui.communal.ui.compose.Communal
 import com.android.systemui.scene.shared.model.Scenes
 
 fun TransitionBuilder.lockscreenToCommunalTransition() {
-    spec = tween(durationMillis = 500)
+    spec = tween(durationMillis = 1000)
 
-    // Translate lockscreen to the left.
+    // Translate lockscreen to the start direction.
     translate(Scenes.Lockscreen.rootElementKey, Edge.Start)
 
-    // Translate communal from the right.
-    translate(Scenes.Communal.rootElementKey, Edge.End)
+    // Translate communal hub grid from the end direction.
+    translate(Communal.Elements.Grid, Edge.End)
+
+    // Fade all communal hub elements.
+    timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
index 05949b2..337f53a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToNotificationsShadeTransition.kt
@@ -19,12 +19,9 @@
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.spring
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.unit.IntSize
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.TransitionBuilder
 import com.android.compose.animation.scene.UserActionDistance
-import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.composable.Shade
@@ -32,11 +29,6 @@
 import kotlin.time.Duration.Companion.milliseconds
 
 fun TransitionBuilder.toNotificationsShadeTransition(
-    /**
-     * The edge where the shade will animate from. This is statically determined (i.e. doesn't
-     * change during runtime).
-     */
-    edge: Edge = Edge.Top,
     durationScale: Double = 1.0,
 ) {
     spec = tween(durationMillis = (DefaultDuration * durationScale).inWholeMilliseconds.toInt())
@@ -45,17 +37,11 @@
             stiffness = Spring.StiffnessMediumLow,
             visibilityThreshold = Shade.Dimensions.ScrimVisibilityThreshold,
         )
-    distance =
-        object : UserActionDistance {
-            override fun UserActionDistanceScope.absoluteDistance(
-                fromSceneSize: IntSize,
-                orientation: Orientation,
-            ): Float {
-                return fromSceneSize.height.toFloat() * 2 / 3f
-            }
-        }
+    distance = UserActionDistance { fromSceneSize, orientation ->
+        fromSceneSize.height.toFloat() * 2 / 3f
+    }
 
-    translate(OverlayShade.Elements.Panel, edge)
+    translate(OverlayShade.Elements.Panel, Edge.Top)
 
     fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
index 21dfc49..b677dff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/ToShadeTransition.kt
@@ -26,7 +26,6 @@
 import com.android.compose.animation.scene.UserActionDistance
 import com.android.compose.animation.scene.UserActionDistanceScope
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
-import com.android.systemui.media.controls.ui.composable.MediaContentPicker
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.scene.shared.model.Scenes
@@ -62,12 +61,9 @@
         fade(QuickSettings.Elements.FooterActions)
     }
 
-    val qsTranslation = ShadeHeader.Dimensions.CollapsedHeight * MediaContentPicker.SHADE_FRACTION
-    val qsExpansionDiff =
-        ShadeHeader.Dimensions.ExpandedHeight - ShadeHeader.Dimensions.CollapsedHeight
-
-    translate(QuickSettings.Elements.QuickQuickSettings, y = -qsTranslation)
-    translate(MediaCarousel.Elements.Content, y = -(qsExpansionDiff + qsTranslation))
+    val qsTranslation = -ShadeHeader.Dimensions.CollapsedHeight * 0.66f
+    translate(QuickSettings.Elements.QuickQuickSettings, y = qsTranslation)
+    translate(MediaCarousel.Elements.Content, y = qsTranslation)
     translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 445ffcb..8922224 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -40,56 +40,30 @@
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.systemui.keyguard.ui.composable.LockscreenContent
-import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
-import com.android.systemui.util.kotlin.getOrNull
-import dagger.Lazy
-import java.util.Optional
 
-/** The overlay shade renders a lightweight shade UI container on top of a background scene. */
+/** Renders a lightweight shade UI container, as an overlay. */
 @Composable
 fun SceneScope.OverlayShade(
-    viewModelFactory: OverlayShadeViewModel.Factory,
-    lockscreenContent: Lazy<Optional<LockscreenContent>>,
+    onScrimClicked: () -> Unit,
     modifier: Modifier = Modifier,
     content: @Composable () -> Unit,
 ) {
-    val viewModel = rememberViewModel { viewModelFactory.create() }
-    val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
-
     Box(modifier) {
-        if (backgroundScene == Scenes.Lockscreen) {
-            // Lockscreen content is optionally injected, because variants of System UI without a
-            // lockscreen cannot provide it.
-            val lockscreenContentOrNull = lockscreenContent.get().getOrNull()
-            lockscreenContentOrNull?.apply { Content(Modifier.fillMaxSize()) }
-        }
-
-        Scrim(onClicked = viewModel::onScrimClicked)
+        Scrim(onClicked = onScrimClicked)
 
         Box(
             modifier = Modifier.fillMaxSize().panelPadding(),
-            contentAlignment =
-                if (viewModel.panelAlignment == ShadeAlignment.Top) {
-                    Alignment.TopEnd
-                } else {
-                    Alignment.BottomEnd
-                },
+            contentAlignment = Alignment.TopEnd,
         ) {
             Panel(
                 modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 8c53740..05a0119 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -129,7 +129,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val viewModel = rememberViewModel { viewModelFactory.create() }
+    val viewModel = rememberViewModel("CollapsedShadeHeader") { viewModelFactory.create() }
     val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
@@ -287,7 +287,7 @@
     statusBarIconController: StatusBarIconController,
     modifier: Modifier = Modifier,
 ) {
-    val viewModel = rememberViewModel { viewModelFactory.create() }
+    val viewModel = rememberViewModel("ExpandedShadeHeader") { viewModelFactory.create() }
     val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
     if (isDisabled) {
         return
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt
new file mode 100644
index 0000000..e0b7909
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeMediaOffsetProvider.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.shade.ui.composable
+
+import androidx.compose.ui.unit.IntOffset
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+
+/**
+ * Provider for the extra offset for the Media section in the shade to accommodate for the squishing
+ * qs or qqs tiles.
+ */
+interface ShadeMediaOffsetProvider {
+
+    /** Returns current offset to be applied to the Media Carousel */
+    val offset: IntOffset
+
+    /**
+     * [ShadeMediaOffsetProvider] implementation for Quick Settings.
+     *
+     * [updateLayout] should represent an access to some state to trigger Compose to relayout to
+     * track [QSSceneAdapter] internal state changes during the transition.
+     */
+    class Qs(private val updateLayout: () -> Unit, private val qsSceneAdapter: QSSceneAdapter) :
+        ShadeMediaOffsetProvider {
+
+        override val offset: IntOffset
+            get() =
+                calculateQsOffset(
+                    updateLayout,
+                    qsSceneAdapter.qsHeight,
+                    qsSceneAdapter.squishedQsHeight
+                )
+    }
+
+    /**
+     * [ShadeMediaOffsetProvider] implementation for Quick Quick Settings.
+     *
+     * [updateLayout] should represent an access to some state to trigger Compose to relayout to
+     * track [QSSceneAdapter] internal state changes during the transition.
+     */
+    class Qqs(private val updateLayout: () -> Unit, private val qsSceneAdapter: QSSceneAdapter) :
+        ShadeMediaOffsetProvider {
+
+        override val offset: IntOffset
+            get() =
+                calculateQsOffset(
+                    updateLayout,
+                    qsSceneAdapter.qqsHeight,
+                    qsSceneAdapter.squishedQqsHeight
+                )
+    }
+
+    companion object {
+
+        protected fun calculateQsOffset(
+            updateLayout: () -> Unit,
+            qsHeight: Int,
+            qsSquishedHeight: Int
+        ): IntOffset {
+            updateLayout()
+            val distanceFromBottomToActualBottom = qsHeight - qsSquishedHeight
+            return IntOffset(0, -distanceFromBottomToActualBottom)
+        }
+    }
+}
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 0e3fcf4..df22264 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
@@ -30,7 +30,7 @@
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.WindowInsets
 import androidx.compose.foundation.layout.asPaddingValues
-import androidx.compose.foundation.layout.displayCutoutPadding
+import androidx.compose.foundation.layout.displayCutout
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -47,8 +47,9 @@
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.mutableIntStateOf
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.CompositingStrategy
@@ -56,9 +57,11 @@
 import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.zIndex
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -80,6 +83,7 @@
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.media.controls.ui.composable.MediaCarousel
 import com.android.systemui.media.controls.ui.composable.MediaContentPicker
@@ -88,37 +92,39 @@
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
+import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.COLLAPSED
+import com.android.systemui.media.controls.ui.view.MediaHostState.Companion.EXPANDED
 import com.android.systemui.media.dagger.MediaModule.QS_PANEL
 import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
 import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
 import com.android.systemui.notifications.ui.composable.NotificationStackCutoffGuideline
 import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
 import com.android.systemui.qs.ui.composable.BrightnessMirror
-import com.android.systemui.qs.ui.composable.QSMediaMeasurePolicy
 import com.android.systemui.qs.ui.composable.QuickSettings
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaLandscapeTopOffset
 import com.android.systemui.qs.ui.composable.QuickSettings.SharedValues.MediaOffset.InQQS
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneActionsViewModel
 import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeUserActionsViewModel
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
 import com.android.systemui.statusbar.phone.ui.TintedIconManager
+import com.android.systemui.util.Utils
 import dagger.Lazy
 import javax.inject.Inject
 import javax.inject.Named
 import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 
 object Shade {
     object Elements {
-        val MediaCarousel = ElementKey("ShadeMediaCarousel")
         val BackgroundScrim =
             ElementKey("ShadeBackgroundScrim", contentPicker = LowestZIndexContentPicker)
         val SplitShadeStartColumn = ElementKey("SplitShadeStartColumn")
@@ -141,35 +147,35 @@
 }
 
 /** The shade scene shows scrolling list of notifications and some of the quick setting tiles. */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class ShadeScene
 @Inject
 constructor(
     private val shadeSession: SaveableSession,
     private val notificationStackScrollView: Lazy<NotificationScrollView>,
-    private val actionsViewModelFactory: ShadeSceneActionsViewModel.Factory,
+    private val actionsViewModelFactory: ShadeUserActionsViewModel.Factory,
     private val contentViewModelFactory: ShadeSceneContentViewModel.Factory,
-    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+    private val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
     private val tintedIconManagerFactory: TintedIconManager.Factory,
     private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
     private val statusBarIconController: StatusBarIconController,
     private val mediaCarouselController: MediaCarouselController,
     @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
     @Named(QS_PANEL) private val qsMediaHost: MediaHost,
-) : ComposableScene {
+) : ExclusiveActivatable(), Scene {
 
     override val key = Scenes.Shade
 
-    private val actionsViewModel: ShadeSceneActionsViewModel by lazy {
+    private val actionsViewModel: ShadeUserActionsViewModel by lazy {
         actionsViewModelFactory.create()
     }
 
-    override suspend fun activate() {
+    override suspend fun onActivated(): Nothing {
         actionsViewModel.activate()
     }
 
-    override val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        actionsViewModel.actions
+    override val userActions: Flow<Map<UserAction, UserActionResult>> = actionsViewModel.actions
 
     @Composable
     override fun SceneScope.Content(
@@ -177,8 +183,12 @@
     ) =
         ShadeScene(
             notificationStackScrollView.get(),
-            viewModel = rememberViewModel { contentViewModelFactory.create() },
-            notificationsPlaceholderViewModel = notificationsPlaceholderViewModel,
+            viewModel =
+                rememberViewModel("ShadeScene-viewModel") { contentViewModelFactory.create() },
+            notificationsPlaceholderViewModel =
+                rememberViewModel("ShadeScene-notifPlaceholderViewModel") {
+                    notificationsPlaceholderViewModelFactory.create()
+                },
             createTintedIconManager = tintedIconManagerFactory::create,
             createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
             statusBarIconController = statusBarIconController,
@@ -260,8 +270,14 @@
     shadeSession: SaveableSession,
 ) {
     val cutoutLocation = LocalDisplayCutout.current.location
+    val cutoutInsets = WindowInsets.Companion.displayCutout
+    val isLandscape = LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+    val usingCollapsedLandscapeMedia =
+        Utils.useCollapsedMediaInLandscape(LocalContext.current.resources)
+    val isExpanded = !usingCollapsedLandscapeMedia || !isLandscape
+    mediaHost.expansion = if (isExpanded) EXPANDED else COLLAPSED
 
-    val maxNotifScrimTop = remember { mutableStateOf(0f) }
+    var maxNotifScrimTop by remember { mutableIntStateOf(0) }
     val tileSquishiness by
         animateSceneFloatAsState(
             value = 1f,
@@ -275,14 +291,39 @@
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
             layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
     // Media is visible and we are in landscape on a small height screen
-    val mediaInRow =
-        isMediaVisible &&
-            LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
+    val mediaInRow = isMediaVisible && isLandscape
     val mediaOffset by
         animateSceneDpAsState(value = InQQS, key = MediaLandscapeTopOffset, canOverflow = false)
 
     val navBarHeight = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding()
 
+    val mediaOffsetProvider = remember {
+        ShadeMediaOffsetProvider.Qqs(
+            { @Suppress("UNUSED_EXPRESSION") tileSquishiness },
+            viewModel.qsSceneAdapter,
+        )
+    }
+    val shadeHorizontalPadding =
+        dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
+    val shadeMeasurePolicy =
+        remember(mediaInRow) {
+            SingleShadeMeasurePolicy(
+                isMediaInRow = mediaInRow,
+                mediaOffset = { mediaOffset.roundToPx() },
+                onNotificationsTopChanged = { maxNotifScrimTop = it },
+                mediaZIndex = {
+                    if (MediaContentPicker.shouldElevateMedia(layoutState)) 1f else 0f
+                },
+                cutoutInsetsProvider = {
+                    if (cutoutLocation == CutoutLocation.CENTER) {
+                        null
+                    } else {
+                        cutoutInsets
+                    }
+                }
+            )
+        }
+
     Box(
         modifier =
             modifier.thenIf(shouldPunchHoleBehindScrim) {
@@ -299,101 +340,57 @@
                     .background(colorResource(R.color.shade_scrim_background_dark)),
         )
         Layout(
-            contents =
-                listOf(
-                    {
-                        Column(
-                            horizontalAlignment = Alignment.CenterHorizontally,
-                            modifier =
-                                Modifier.fillMaxWidth()
-                                    .thenIf(isEmptySpaceClickable) {
-                                        Modifier.clickable(
-                                            onClick = { viewModel.onEmptySpaceClicked() }
-                                        )
-                                    }
-                                    .thenIf(cutoutLocation != CutoutLocation.CENTER) {
-                                        Modifier.displayCutoutPadding()
-                                    },
-                        ) {
-                            CollapsedShadeHeader(
-                                viewModelFactory = viewModel.shadeHeaderViewModelFactory,
-                                createTintedIconManager = createTintedIconManager,
-                                createBatteryMeterViewController = createBatteryMeterViewController,
-                                statusBarIconController = statusBarIconController,
-                            )
-
-                            val content: @Composable () -> Unit = {
-                                Box(
-                                    Modifier.element(QuickSettings.Elements.QuickQuickSettings)
-                                        .layoutId(QSMediaMeasurePolicy.LayoutId.QS)
-                                ) {
-                                    QuickSettings(
-                                        viewModel.qsSceneAdapter,
-                                        { viewModel.qsSceneAdapter.qqsHeight },
-                                        isSplitShade = false,
-                                        squishiness = { tileSquishiness },
-                                    )
-                                }
-
-                                MediaCarousel(
-                                    isVisible = isMediaVisible,
-                                    mediaHost = mediaHost,
-                                    modifier =
-                                        Modifier.fillMaxWidth()
-                                            .layoutId(QSMediaMeasurePolicy.LayoutId.Media),
-                                    carouselController = mediaCarouselController,
-                                )
-                            }
-                            val landscapeQsMediaMeasurePolicy = remember {
-                                QSMediaMeasurePolicy(
-                                    { viewModel.qsSceneAdapter.qqsHeight },
-                                    { mediaOffset.roundToPx() },
-                                )
-                            }
-                            if (mediaInRow) {
-                                Layout(
-                                    content = content,
-                                    measurePolicy = landscapeQsMediaMeasurePolicy,
-                                )
-                            } else {
-                                content()
-                            }
-                        }
-                    },
-                    {
-                        NotificationScrollingStack(
-                            shadeSession = shadeSession,
-                            stackScrollView = notificationStackScrollView,
-                            viewModel = notificationsPlaceholderViewModel,
-                            maxScrimTop = { maxNotifScrimTop.value },
-                            shadeMode = ShadeMode.Single,
-                            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
-                            onEmptySpaceClick =
-                                viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
-                        )
-                    },
+            modifier =
+                Modifier.thenIf(isEmptySpaceClickable) {
+                    Modifier.clickable { viewModel.onEmptySpaceClicked() }
+                },
+            content = {
+                CollapsedShadeHeader(
+                    viewModelFactory = viewModel.shadeHeaderViewModelFactory,
+                    createTintedIconManager = createTintedIconManager,
+                    createBatteryMeterViewController = createBatteryMeterViewController,
+                    statusBarIconController = statusBarIconController,
+                    modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.ShadeHeader),
                 )
-        ) { measurables, constraints ->
-            check(measurables.size == 2)
-            check(measurables[0].size == 1)
-            check(measurables[1].size == 1)
 
-            val quickSettingsPlaceable = measurables[0][0].measure(constraints)
-            val notificationsPlaceable = measurables[1][0].measure(constraints)
+                Box(
+                    Modifier.element(QuickSettings.Elements.QuickQuickSettings)
+                        .layoutId(SingleShadeMeasurePolicy.LayoutId.QuickSettings)
+                        .padding(horizontal = shadeHorizontalPadding)
+                ) {
+                    QuickSettings(
+                        viewModel.qsSceneAdapter,
+                        { viewModel.qsSceneAdapter.qqsHeight },
+                        isSplitShade = false,
+                        squishiness = { tileSquishiness },
+                    )
+                }
 
-            maxNotifScrimTop.value = quickSettingsPlaceable.height.toFloat()
+                ShadeMediaCarousel(
+                    isVisible = isMediaVisible,
+                    isInRow = mediaInRow,
+                    mediaHost = mediaHost,
+                    mediaOffsetProvider = mediaOffsetProvider,
+                    carouselController = mediaCarouselController,
+                    modifier = Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Media),
+                )
 
-            layout(constraints.maxWidth, constraints.maxHeight) {
-                val qsZIndex =
-                    if (MediaContentPicker.shouldElevateMedia(layoutState)) {
-                        1f
-                    } else {
-                        0f
-                    }
-                quickSettingsPlaceable.placeRelative(x = 0, y = 0, zIndex = qsZIndex)
-                notificationsPlaceable.placeRelative(x = 0, y = maxNotifScrimTop.value.roundToInt())
-            }
-        }
+                NotificationScrollingStack(
+                    shadeSession = shadeSession,
+                    stackScrollView = notificationStackScrollView,
+                    viewModel = notificationsPlaceholderViewModel,
+                    maxScrimTop = { maxNotifScrimTop.toFloat() },
+                    shadeMode = ShadeMode.Single,
+                    shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
+                    onEmptySpaceClick =
+                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
+                    modifier =
+                        Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Notifications)
+                            .padding(horizontal = shadeHorizontalPadding),
+                )
+            },
+            measurePolicy = shadeMeasurePolicy,
+        )
         Box(
             modifier =
                 Modifier.align(Alignment.BottomCenter)
@@ -477,9 +474,10 @@
         }
     }
 
-    val brightnessMirrorViewModel = rememberViewModel {
-        viewModel.brightnessMirrorViewModelFactory.create()
-    }
+    val brightnessMirrorViewModel =
+        rememberViewModel("SplitShade-brightnessMirrorViewModel") {
+            viewModel.brightnessMirrorViewModelFactory.create()
+        }
     val brightnessMirrorShowing by brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
     val contentAlpha by
         animateFloatAsState(
@@ -497,6 +495,13 @@
 
     val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
 
+    val mediaOffsetProvider = remember {
+        ShadeMediaOffsetProvider.Qs(
+            { @Suppress("UNUSED_EXPRESSION") tileSquishiness },
+            viewModel.qsSceneAdapter,
+        )
+    }
+
     Box {
         Box(
             modifier =
@@ -570,11 +575,14 @@
                                     squishiness = { tileSquishiness },
                                 )
                             }
-                            MediaCarousel(
+
+                            ShadeMediaCarousel(
                                 isVisible = isMediaVisible,
+                                isInRow = false,
                                 mediaHost = mediaHost,
+                                mediaOffsetProvider = mediaOffsetProvider,
                                 modifier =
-                                    Modifier.fillMaxWidth().thenIf(
+                                    Modifier.thenIf(
                                         MediaContentPicker.shouldElevateMedia(layoutState)
                                     ) {
                                         Modifier.zIndex(1f)
@@ -608,7 +616,11 @@
                     modifier =
                         Modifier.weight(1f)
                             .fillMaxHeight()
-                            .padding(end = screenCornerRadius / 2f, bottom = navBarBottomHeight)
+                            .padding(
+                                end =
+                                    dimensionResource(R.dimen.notification_panel_margin_horizontal),
+                                bottom = navBarBottomHeight
+                            )
                             .then(brightnessMirrorShowingModifier)
                 )
             }
@@ -620,3 +632,26 @@
         )
     }
 }
+
+@Composable
+private fun SceneScope.ShadeMediaCarousel(
+    isVisible: Boolean,
+    isInRow: Boolean,
+    mediaHost: MediaHost,
+    carouselController: MediaCarouselController,
+    mediaOffsetProvider: ShadeMediaOffsetProvider,
+    modifier: Modifier = Modifier,
+) {
+    MediaCarousel(
+        modifier = modifier.fillMaxWidth(),
+        isVisible = isVisible,
+        mediaHost = mediaHost,
+        carouselController = carouselController,
+        offsetProvider =
+            if (isInRow || MediaContentPicker.shouldElevateMedia(layoutState)) {
+                null
+            } else {
+                { mediaOffsetProvider.offset }
+            }
+    )
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.kt
new file mode 100644
index 0000000..6275ac3
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/SingleShadeMeasurePolicy.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.shade.ui.composable
+
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasurePolicy
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.offset
+import androidx.compose.ui.util.fastFirst
+import androidx.compose.ui.util.fastFirstOrNull
+import com.android.systemui.shade.ui.composable.SingleShadeMeasurePolicy.LayoutId
+import kotlin.math.max
+
+/**
+ * Lays out elements from the [LayoutId] in the shade. This policy supports the case when the QS and
+ * UMO share the same row and when they should be one below another.
+ */
+class SingleShadeMeasurePolicy(
+    private val isMediaInRow: Boolean,
+    private val mediaOffset: MeasureScope.() -> Int,
+    private val onNotificationsTopChanged: (Int) -> Unit,
+    private val mediaZIndex: () -> Float,
+    private val cutoutInsetsProvider: () -> WindowInsets?,
+) : MeasurePolicy {
+
+    enum class LayoutId {
+        QuickSettings,
+        Media,
+        Notifications,
+        ShadeHeader,
+    }
+
+    override fun MeasureScope.measure(
+        measurables: List<Measurable>,
+        constraints: Constraints,
+    ): MeasureResult {
+        val cutoutInsets: WindowInsets? = cutoutInsetsProvider()
+        val constraintsWithCutout = applyCutout(constraints, cutoutInsets)
+        val insetsLeft = cutoutInsets?.getLeft(this, layoutDirection) ?: 0
+        val insetsTop = cutoutInsets?.getTop(this) ?: 0
+
+        val shadeHeaderPlaceable =
+            measurables
+                .fastFirst { it.layoutId == LayoutId.ShadeHeader }
+                .measure(constraintsWithCutout)
+        val mediaPlaceable =
+            measurables
+                .fastFirstOrNull { it.layoutId == LayoutId.Media }
+                ?.measure(applyMediaConstraints(constraintsWithCutout, isMediaInRow))
+        val quickSettingsPlaceable =
+            measurables
+                .fastFirst { it.layoutId == LayoutId.QuickSettings }
+                .measure(constraintsWithCutout)
+        val notificationsPlaceable =
+            measurables.fastFirst { it.layoutId == LayoutId.Notifications }.measure(constraints)
+
+        val notificationsTop =
+            calculateNotificationsTop(
+                statusBarHeaderPlaceable = shadeHeaderPlaceable,
+                quickSettingsPlaceable = quickSettingsPlaceable,
+                mediaPlaceable = mediaPlaceable,
+                insetsTop = insetsTop,
+                isMediaInRow = isMediaInRow,
+            )
+        onNotificationsTopChanged(notificationsTop)
+
+        return layout(constraints.maxWidth, constraints.maxHeight) {
+            shadeHeaderPlaceable.placeRelative(x = insetsLeft, y = insetsTop)
+            quickSettingsPlaceable.placeRelative(
+                x = insetsLeft,
+                y = insetsTop + shadeHeaderPlaceable.height,
+            )
+
+            if (isMediaInRow) {
+                mediaPlaceable?.placeRelative(
+                    x = insetsLeft + constraintsWithCutout.maxWidth / 2,
+                    y = mediaOffset() + insetsTop + shadeHeaderPlaceable.height,
+                    zIndex = mediaZIndex(),
+                )
+            } else {
+                mediaPlaceable?.placeRelative(
+                    x = insetsLeft,
+                    y = insetsTop + shadeHeaderPlaceable.height + quickSettingsPlaceable.height,
+                    zIndex = mediaZIndex(),
+                )
+            }
+
+            // Notifications don't need to accommodate for horizontal insets
+            notificationsPlaceable.placeRelative(x = 0, y = notificationsTop)
+        }
+    }
+
+    private fun calculateNotificationsTop(
+        statusBarHeaderPlaceable: Placeable,
+        quickSettingsPlaceable: Placeable,
+        mediaPlaceable: Placeable?,
+        insetsTop: Int,
+        isMediaInRow: Boolean,
+    ): Int {
+        val mediaHeight = mediaPlaceable?.height ?: 0
+        return insetsTop +
+            statusBarHeaderPlaceable.height +
+            if (isMediaInRow) {
+                max(quickSettingsPlaceable.height, mediaHeight)
+            } else {
+                quickSettingsPlaceable.height + mediaHeight
+            }
+    }
+
+    private fun applyMediaConstraints(
+        constraints: Constraints,
+        isMediaInRow: Boolean,
+    ): Constraints {
+        return if (isMediaInRow) {
+            constraints.copy(maxWidth = constraints.maxWidth / 2)
+        } else {
+            constraints
+        }
+    }
+
+    private fun MeasureScope.applyCutout(
+        constraints: Constraints,
+        cutoutInsets: WindowInsets?,
+    ): Constraints {
+        return if (cutoutInsets == null) {
+            constraints
+        } else {
+            val left = cutoutInsets.getLeft(this, layoutDirection)
+            val top = cutoutInsets.getTop(this)
+            val right = cutoutInsets.getRight(this, layoutDirection)
+            val bottom = cutoutInsets.getBottom(this)
+
+            constraints.offset(horizontal = -(left + right), vertical = -(top + bottom))
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index 1db96cf..c9b8013 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -92,7 +92,12 @@
                         true
                     }
                 },
-            color = MaterialTheme.colorScheme.surface,
+            color =
+                if (enabled) {
+                    MaterialTheme.colorScheme.surface
+                } else {
+                    MaterialTheme.colorScheme.surfaceContainerHighest
+                },
             shape = RoundedCornerShape(28.dp),
             onClick =
                 if (enabled) {
@@ -119,7 +124,7 @@
                 modifier = Modifier.basicMarquee(),
                 text = connectedDeviceViewModel.label.toString(),
                 style = MaterialTheme.typography.labelMedium,
-                color = MaterialTheme.colorScheme.onSurfaceVariant,
+                color = connectedDeviceViewModel.labelColor.toColor(),
                 maxLines = 1,
             )
             connectedDeviceViewModel.deviceName?.let {
@@ -127,7 +132,7 @@
                     modifier = Modifier.basicMarquee(),
                     text = it.toString(),
                     style = MaterialTheme.typography.titleMedium,
-                    color = MaterialTheme.colorScheme.onSurface,
+                    color = connectedDeviceViewModel.deviceNameColor.toColor(),
                     maxLines = 1,
                 )
             }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index 072e91a..d4f3b5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -23,7 +23,6 @@
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.core.tween
-import androidx.compose.animation.core.updateTransition
 import androidx.compose.animation.expandVertically
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
@@ -78,7 +77,6 @@
     modifier: Modifier = Modifier,
 ) {
     require(viewModels.isNotEmpty())
-    val transition = updateTransition(isExpanded, label = "CollapsableSliders")
     Column(modifier = modifier) {
         Box(
             modifier = Modifier.fillMaxWidth(),
@@ -106,8 +104,9 @@
                 sliderColors = sliderColors,
             )
         }
-        transition.AnimatedVisibility(
-            visible = { it || !isExpandable },
+        AnimatedVisibility(
+            visible = isExpanded || !isExpandable,
+            label = "CollapsableSliders",
             enter =
                 expandVertically(animationSpec = tween(durationMillis = EXPAND_DURATION_MILLIS)),
             exit =
@@ -120,23 +119,31 @@
                     for (index in 1..viewModels.lastIndex) {
                         val sliderViewModel: SliderViewModel = viewModels[index]
                         val sliderState by sliderViewModel.slider.collectAsStateWithLifecycle()
-                        transition.AnimatedVisibility(
-                            modifier = Modifier.padding(top = 16.dp),
-                            visible = { it || !isExpandable },
-                            enter = enterTransition(index = index, totalCount = viewModels.size),
-                            exit = exitTransition(index = index, totalCount = viewModels.size)
-                        ) {
-                            VolumeSlider(
-                                modifier = Modifier.fillMaxWidth(),
-                                state = sliderState,
-                                onValueChange = { newValue: Float ->
-                                    sliderViewModel.onValueChanged(sliderState, newValue)
-                                },
-                                onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
-                                onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
-                                sliderColors = sliderColors,
-                            )
-                        }
+
+                        VolumeSlider(
+                            modifier =
+                                Modifier.padding(top = 16.dp)
+                                    .fillMaxWidth()
+                                    .animateEnterExit(
+                                        enter =
+                                            enterTransition(
+                                                index = index,
+                                                totalCount = viewModels.size,
+                                            ),
+                                        exit =
+                                            exitTransition(
+                                                index = index,
+                                                totalCount = viewModels.size,
+                                            ),
+                                    ),
+                            state = sliderState,
+                            onValueChange = { newValue: Float ->
+                                sliderViewModel.onValueChanged(sliderState, newValue)
+                            },
+                            onValueChangeFinished = { sliderViewModel.onValueChangeFinished() },
+                            onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
+                            sliderColors = sliderColors,
+                        )
                     }
                 }
             }
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 9da2a1b..1cc0fb2 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
@@ -25,13 +25,13 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.paneTitle
 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,29 +43,30 @@
 private val padding = 24.dp
 
 @Composable
-@OptIn(ExperimentalComposeUiApi::class)
 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(null)
+    val components by viewModel.componentsLayout.collectAsStateWithLifecycle()
 
     with(VolumePanelComposeScope(state)) {
         components?.let { componentsState ->
-            Components(
-                componentsState,
-                modifier
-                    .sysuiResTag(VolumePanelTestTag)
-                    .semantics { paneTitle = accessibilityTitle }
-                    .padding(
-                        start = padding,
-                        top = padding,
-                        end = padding,
-                        bottom = 20.dp,
-                    )
-            )
+            PlatformTheme {
+                Components(
+                    componentsState,
+                    modifier
+                        .sysuiResTag(VolumePanelTestTag)
+                        .semantics { paneTitle = accessibilityTitle }
+                        .padding(
+                            start = padding,
+                            top = padding,
+                            end = padding,
+                            bottom = 20.dp,
+                        )
+                )
+            }
         }
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
new file mode 100644
index 0000000..d876606
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -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.compose.animation.scene
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.SpringSpec
+import com.android.compose.animation.scene.content.state.TransitionState
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+
+internal fun CoroutineScope.animateContent(
+    layoutState: MutableSceneTransitionLayoutStateImpl,
+    transition: TransitionState.Transition,
+    oneOffAnimation: OneOffAnimation,
+    targetProgress: Float,
+    chain: Boolean = true,
+): Job {
+    oneOffAnimation.onRun = {
+        // Animate the progress to its target value.
+        val animationSpec = transition.transformationSpec.progressSpec
+        val visibilityThreshold =
+            (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
+        val replacedTransition = transition.replacedTransition
+        val initialProgress = replacedTransition?.progress ?: 0f
+        val initialVelocity = replacedTransition?.progressVelocity ?: 0f
+        val animatable =
+            Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
+                oneOffAnimation.animatable = it
+            }
+
+        animatable.animateTo(targetProgress, animationSpec, initialVelocity)
+    }
+
+    return layoutState.startTransitionImmediately(animationScope = this, transition, chain)
+}
+
+internal class OneOffAnimation {
+    /**
+     * The animatable used to animate this transition.
+     *
+     * Note: This is lateinit because we need to first create this object so that
+     * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
+     * the transition, which is needed to initialize this Animatable.
+     */
+    lateinit var animatable: Animatable<Float, AnimationVector1D>
+
+    /** The runnable to run for this animation. */
+    lateinit var onRun: suspend () -> Unit
+
+    val progress: Float
+        get() = animatable.value
+
+    val progressVelocity: Float
+        get() = animatable.velocity
+
+    suspend fun run() {
+        onRun()
+    }
+
+    fun freezeAndAnimateToCurrentState() {
+        // Do nothing, the state of one-off animations never change and we directly animate to it.
+    }
+}
+
+// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
+// and screen density.
+internal const val ProgressVisibilityThreshold = 1e-3f
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.kt
new file mode 100644
index 0000000..28116cb
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateOverlay.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.compose.animation.scene
+
+import com.android.compose.animation.scene.content.state.TransitionState
+import kotlinx.coroutines.CoroutineScope
+
+/** Trigger a one-off transition to show or hide an overlay. */
+internal fun CoroutineScope.showOrHideOverlay(
+    layoutState: MutableSceneTransitionLayoutStateImpl,
+    overlay: OverlayKey,
+    fromOrToScene: SceneKey,
+    isShowing: Boolean,
+    transitionKey: TransitionKey?,
+    replacedTransition: TransitionState.Transition.ShowOrHideOverlay?,
+    reversed: Boolean,
+): TransitionState.Transition.ShowOrHideOverlay {
+    val targetProgress = if (reversed) 0f else 1f
+    val (fromContent, toContent) =
+        if (isShowing xor reversed) {
+            fromOrToScene to overlay
+        } else {
+            overlay to fromOrToScene
+        }
+
+    val oneOffAnimation = OneOffAnimation()
+    val transition =
+        OneOffShowOrHideOverlayTransition(
+            overlay = overlay,
+            fromOrToScene = fromOrToScene,
+            fromContent = fromContent,
+            toContent = toContent,
+            isEffectivelyShown = isShowing,
+            key = transitionKey,
+            replacedTransition = replacedTransition,
+            oneOffAnimation = oneOffAnimation,
+        )
+
+    animateContent(
+        layoutState = layoutState,
+        transition = transition,
+        oneOffAnimation = oneOffAnimation,
+        targetProgress = targetProgress,
+    )
+
+    return transition
+}
+
+/** Trigger a one-off transition to replace an overlay by another one. */
+internal fun CoroutineScope.replaceOverlay(
+    layoutState: MutableSceneTransitionLayoutStateImpl,
+    fromOverlay: OverlayKey,
+    toOverlay: OverlayKey,
+    transitionKey: TransitionKey?,
+    replacedTransition: TransitionState.Transition.ReplaceOverlay?,
+    reversed: Boolean,
+): TransitionState.Transition.ReplaceOverlay {
+    val targetProgress = if (reversed) 0f else 1f
+    val effectivelyShownOverlay = if (reversed) fromOverlay else toOverlay
+
+    val oneOffAnimation = OneOffAnimation()
+    val transition =
+        OneOffOverlayReplacingTransition(
+            fromOverlay = fromOverlay,
+            toOverlay = toOverlay,
+            effectivelyShownOverlay = effectivelyShownOverlay,
+            key = transitionKey,
+            replacedTransition = replacedTransition,
+            oneOffAnimation = oneOffAnimation,
+        )
+
+    animateContent(
+        layoutState = layoutState,
+        transition = transition,
+        oneOffAnimation = oneOffAnimation,
+        targetProgress = targetProgress,
+    )
+
+    return transition
+}
+
+private class OneOffShowOrHideOverlayTransition(
+    overlay: OverlayKey,
+    fromOrToScene: SceneKey,
+    fromContent: ContentKey,
+    toContent: ContentKey,
+    override val isEffectivelyShown: Boolean,
+    override val key: TransitionKey?,
+    replacedTransition: TransitionState.Transition?,
+    private val oneOffAnimation: OneOffAnimation,
+) :
+    TransitionState.Transition.ShowOrHideOverlay(
+        overlay,
+        fromOrToScene,
+        fromContent,
+        toContent,
+        replacedTransition,
+    ) {
+    override val progress: Float
+        get() = oneOffAnimation.progress
+
+    override val progressVelocity: Float
+        get() = oneOffAnimation.progressVelocity
+
+    override val isInitiatedByUserInput: Boolean = false
+    override val isUserInputOngoing: Boolean = false
+
+    override suspend fun run() {
+        oneOffAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        oneOffAnimation.freezeAndAnimateToCurrentState()
+    }
+}
+
+private class OneOffOverlayReplacingTransition(
+    fromOverlay: OverlayKey,
+    toOverlay: OverlayKey,
+    override val effectivelyShownOverlay: OverlayKey,
+    override val key: TransitionKey?,
+    replacedTransition: TransitionState.Transition?,
+    private val oneOffAnimation: OneOffAnimation,
+) : TransitionState.Transition.ReplaceOverlay(fromOverlay, toOverlay, replacedTransition) {
+    override val progress: Float
+        get() = oneOffAnimation.progress
+
+    override val progressVelocity: Float
+        get() = oneOffAnimation.progressVelocity
+
+    override val isInitiatedByUserInput: Boolean = false
+    override val isUserInputOngoing: Boolean = false
+
+    override suspend fun run() {
+        oneOffAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        oneOffAnimation.freezeAndAnimateToCurrentState()
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index e13ca391..b30f2b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -120,7 +120,7 @@
 }
 
 @Deprecated(
-    "Use animateSceneFloatAsState() instead",
+    "Use animateContentFloatAsState() instead",
     replaceWith = ReplaceWith("animateContentFloatAsState(value, key, canOverflow)")
 )
 @Composable
@@ -171,7 +171,7 @@
 }
 
 @Deprecated(
-    "Use animateSceneDpAsState() instead",
+    "Use animateContentDpAsState() instead",
     replaceWith = ReplaceWith("animateContentDpAsState(value, key, canOverflow)")
 )
 @Composable
@@ -393,45 +393,75 @@
         transition: TransitionState.Transition?,
     ): T? {
         if (transition == null) {
-            return sharedValue[layoutImpl.state.transitionState.currentScene]
+            return sharedValue[content]
+                ?: sharedValue[layoutImpl.state.transitionState.currentScene]
         }
 
-        val fromValue = sharedValue[transition.fromScene]
-        val toValue = sharedValue[transition.toScene]
-        return if (fromValue != null && toValue != null) {
-            if (fromValue == toValue) {
-                // Optimization: avoid reading progress if the values are the same, so we don't
-                // relayout/redraw for nothing.
-                fromValue
-            } else {
-                val overscrollSpec = transition.currentOverscrollSpec
-                val progress =
-                    when {
-                        overscrollSpec == null -> {
-                            if (canOverflow) transition.progress
-                            else transition.progress.fastCoerceIn(0f, 1f)
-                        }
-                        overscrollSpec.scene == transition.toScene -> 1f
-                        else -> 0f
-                    }
+        val fromValue = sharedValue[transition.fromContent]
+        val toValue = sharedValue[transition.toContent]
+        if (fromValue == null && toValue == null) {
+            return null
+        }
 
-                sharedValue.type.lerp(fromValue, toValue, progress)
+        if (fromValue != null && toValue != null) {
+            return interpolateSharedValue(fromValue, toValue, transition, sharedValue)
+        }
+
+        if (transition is TransitionState.Transition.ReplaceOverlay) {
+            val currentSceneValue = sharedValue[transition.currentScene]
+            if (currentSceneValue != null) {
+                return interpolateSharedValue(
+                    fromValue = fromValue ?: currentSceneValue,
+                    toValue = toValue ?: currentSceneValue,
+                    transition,
+                    sharedValue,
+                )
             }
-        } else fromValue ?: toValue
+        }
+
+        return fromValue ?: toValue
+    }
+
+    private fun interpolateSharedValue(
+        fromValue: T,
+        toValue: T,
+        transition: TransitionState.Transition,
+        sharedValue: SharedValue<T, *>,
+    ): T? {
+        if (fromValue == toValue) {
+            // Optimization: avoid reading progress if the values are the same, so we don't
+            // relayout/redraw for nothing.
+            return fromValue
+        }
+
+        val overscrollSpec = transition.currentOverscrollSpec
+        val progress =
+            when {
+                overscrollSpec == null -> {
+                    if (canOverflow) transition.progress
+                    else transition.progress.fastCoerceIn(0f, 1f)
+                }
+                overscrollSpec.content == transition.toContent -> 1f
+                else -> 0f
+            }
+
+        return sharedValue.type.lerp(fromValue, toValue, progress)
     }
 
     private fun transition(sharedValue: SharedValue<T, Delta>): TransitionState.Transition? {
         val targetValues = sharedValue.targetValues
         val transition =
             if (element != null) {
-                layoutImpl.elements[element]?.stateByContent?.let { sceneStates ->
-                    layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
-                        transition.fromScene in sceneStates || transition.toScene in sceneStates
-                    }
+                layoutImpl.elements[element]?.let { element ->
+                    elementState(
+                        layoutImpl.state.transitionStates,
+                        isInContent = { it in element.stateByContent },
+                    )
+                        as? TransitionState.Transition
                 }
             } else {
                 layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
-                    transition.fromScene in targetValues || transition.toScene in targetValues
+                    transition.fromContent in targetValues || transition.toContent in targetValues
                 }
             }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 1fc1f98..86be4a4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -16,15 +16,9 @@
 
 package com.android.compose.animation.scene
 
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.SpringSpec
 import com.android.compose.animation.scene.content.state.TransitionState
-import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
 
 /**
  * Transition to [target] using a canned animation. This function will try to be smart and take over
@@ -34,7 +28,7 @@
     layoutState: MutableSceneTransitionLayoutStateImpl,
     target: SceneKey,
     transitionKey: TransitionKey?,
-): TransitionState.Transition? {
+): Pair<TransitionState.Transition.ChangeScene, Job>? {
     val transitionState = layoutState.transitionState
     if (transitionState.currentScene == target) {
         // This can happen in 3 different situations, for which there isn't anything else to do:
@@ -49,16 +43,19 @@
     }
 
     return when (transitionState) {
-        is TransitionState.Idle -> {
-            animate(
+        is TransitionState.Idle,
+        is TransitionState.Transition.ShowOrHideOverlay,
+        is TransitionState.Transition.ReplaceOverlay -> {
+            animateToScene(
                 layoutState,
                 target,
                 transitionKey,
                 isInitiatedByUserInput = false,
+                fromScene = transitionState.currentScene,
                 replacedTransition = null,
             )
         }
-        is TransitionState.Transition -> {
+        is TransitionState.Transition.ChangeScene -> {
             val isInitiatedByUserInput = transitionState.isInitiatedByUserInput
 
             // A transition is currently running: first check whether `transition.toScene` or
@@ -68,51 +65,30 @@
             if (transitionState.toScene == target) {
                 // The user is currently swiping to [target] but didn't release their pointer yet:
                 // animate the progress to `1`.
-
                 check(transitionState.fromScene == transitionState.currentScene)
-                val progress = transitionState.progress
-                if ((1f - progress).absoluteValue < ProgressVisibilityThreshold) {
-                    // The transition is already finished (progress ~= 1): no need to animate. We
-                    // finish the current transition early to make sure that the current state
-                    // change is committed.
-                    layoutState.finishTransition(transitionState, target)
-                    null
-                } else {
-                    // The transition is in progress: start the canned animation at the same
-                    // progress as it was in.
-                    animate(
-                        layoutState,
-                        target,
-                        transitionKey,
-                        isInitiatedByUserInput,
-                        initialProgress = progress,
-                        initialVelocity = transitionState.progressVelocity,
-                        replacedTransition = transitionState,
-                    )
-                }
+
+                // The transition is in progress: start the canned animation at the same
+                // progress as it was in.
+                animateToScene(
+                    layoutState,
+                    target,
+                    transitionKey,
+                    isInitiatedByUserInput,
+                    replacedTransition = transitionState,
+                )
             } else if (transitionState.fromScene == target) {
                 // There is a transition from [target] to another scene: simply animate the same
                 // transition progress to `0`.
                 check(transitionState.toScene == transitionState.currentScene)
 
-                val progress = transitionState.progress
-                if (progress.absoluteValue < ProgressVisibilityThreshold) {
-                    // The transition is at progress ~= 0: no need to animate.We finish the current
-                    // transition early to make sure that the current state change is committed.
-                    layoutState.finishTransition(transitionState, target)
-                    null
-                } else {
-                    animate(
-                        layoutState,
-                        target,
-                        transitionKey,
-                        isInitiatedByUserInput,
-                        initialProgress = progress,
-                        initialVelocity = transitionState.progressVelocity,
-                        reversed = true,
-                        replacedTransition = transitionState,
-                    )
-                }
+                animateToScene(
+                    layoutState,
+                    target,
+                    transitionKey,
+                    isInitiatedByUserInput,
+                    reversed = true,
+                    replacedTransition = transitionState,
+                )
             } else {
                 // Generic interruption; the current transition is neither from or to [target].
                 val interruptionResult =
@@ -140,7 +116,7 @@
                     animateToScene(layoutState, animateFrom, transitionKey = null)
                 }
 
-                animate(
+                animateToScene(
                     layoutState,
                     target,
                     transitionKey,
@@ -154,103 +130,75 @@
     }
 }
 
-private fun CoroutineScope.animate(
+private fun CoroutineScope.animateToScene(
     layoutState: MutableSceneTransitionLayoutStateImpl,
     targetScene: SceneKey,
     transitionKey: TransitionKey?,
     isInitiatedByUserInput: Boolean,
     replacedTransition: TransitionState.Transition?,
-    initialProgress: Float = 0f,
-    initialVelocity: Float = 0f,
     reversed: Boolean = false,
     fromScene: SceneKey = layoutState.transitionState.currentScene,
     chain: Boolean = true,
-): TransitionState.Transition {
+): Pair<TransitionState.Transition.ChangeScene, Job> {
+    val oneOffAnimation = OneOffAnimation()
     val targetProgress = if (reversed) 0f else 1f
     val transition =
         if (reversed) {
-            OneOffTransition(
+            OneOffSceneTransition(
                 key = transitionKey,
                 fromScene = targetScene,
                 toScene = fromScene,
                 currentScene = targetScene,
                 isInitiatedByUserInput = isInitiatedByUserInput,
-                isUserInputOngoing = false,
                 replacedTransition = replacedTransition,
+                oneOffAnimation = oneOffAnimation,
             )
         } else {
-            OneOffTransition(
+            OneOffSceneTransition(
                 key = transitionKey,
                 fromScene = fromScene,
                 toScene = targetScene,
                 currentScene = targetScene,
                 isInitiatedByUserInput = isInitiatedByUserInput,
-                isUserInputOngoing = false,
                 replacedTransition = replacedTransition,
+                oneOffAnimation = oneOffAnimation,
             )
         }
 
-    // Change the current layout state to start this new transition. This will compute the
-    // TransformationSpec associated to this transition, which we need to initialize the Animatable
-    // that will actually animate it.
-    layoutState.startTransition(transition, chain)
+    val job =
+        animateContent(
+            layoutState = layoutState,
+            transition = transition,
+            oneOffAnimation = oneOffAnimation,
+            targetProgress = targetProgress,
+            chain = chain,
+        )
 
-    // The transition now contains the transformation spec that we should use to instantiate the
-    // Animatable.
-    val animationSpec = transition.transformationSpec.progressSpec
-    val visibilityThreshold =
-        (animationSpec as? SpringSpec)?.visibilityThreshold ?: ProgressVisibilityThreshold
-    val animatable =
-        Animatable(initialProgress, visibilityThreshold = visibilityThreshold).also {
-            transition.animatable = it
-        }
-
-    // Animate the progress to its target value.
-    // Important: We start atomically to make sure that we start the coroutine even if it is
-    // cancelled right after it is launched, so that finishTransition() is correctly called.
-    // Otherwise, this transition will never be stopped and we will never settle to Idle.
-    transition.job =
-        launch(start = CoroutineStart.ATOMIC) {
-            try {
-                animatable.animateTo(targetProgress, animationSpec, initialVelocity)
-            } finally {
-                layoutState.finishTransition(transition, targetScene)
-            }
-        }
-
-    return transition
+    return transition to job
 }
 
-private class OneOffTransition(
+private class OneOffSceneTransition(
     override val key: TransitionKey?,
     fromScene: SceneKey,
     toScene: SceneKey,
     override val currentScene: SceneKey,
     override val isInitiatedByUserInput: Boolean,
-    override val isUserInputOngoing: Boolean,
     replacedTransition: TransitionState.Transition?,
-) : TransitionState.Transition(fromScene, toScene, replacedTransition) {
-    /**
-     * The animatable used to animate this transition.
-     *
-     * Note: This is lateinit because we need to first create this Transition object so that
-     * [SceneTransitionLayoutState] can compute the transformations and animation spec associated to
-     * it, which is need to initialize this Animatable.
-     */
-    lateinit var animatable: Animatable<Float, AnimationVector1D>
-
-    /** The job that is animating [animatable]. */
-    lateinit var job: Job
-
+    private val oneOffAnimation: OneOffAnimation,
+) : TransitionState.Transition.ChangeScene(fromScene, toScene, replacedTransition) {
     override val progress: Float
-        get() = animatable.value
+        get() = oneOffAnimation.progress
 
     override val progressVelocity: Float
-        get() = animatable.velocity
+        get() = oneOffAnimation.progressVelocity
 
-    override fun finish(): Job = job
+    override val isUserInputOngoing: Boolean = false
+
+    override suspend fun run() {
+        oneOffAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        oneOffAnimation.freezeAndAnimateToCurrentState()
+    }
 }
-
-// TODO(b/290184746): Compute a good default visibility threshold that depends on the layout size
-// and screen density.
-internal const val ProgressVisibilityThreshold = 1e-3f
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 a43028a..24fef71 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
@@ -18,28 +18,16 @@
 
 package com.android.compose.animation.scene
 
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastCoerceIn
-import com.android.compose.animation.scene.content.Scene
-import com.android.compose.animation.scene.content.state.ContentState
-import com.android.compose.animation.scene.content.state.ContentState.HasOverscrollProperties.Companion.DistanceUnspecified
+import com.android.compose.animation.scene.content.Content
 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 kotlin.math.absoluteValue
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
 
 internal interface DraggableHandler {
     /**
@@ -64,17 +52,16 @@
     fun onDrag(delta: Float): Float
 
     /**
-     * Starts a transition to a target scene.
+     * Stop the current drag with the given [velocity].
      *
      * @return the consumed [velocity]
      */
-    fun onStop(velocity: Float, canChangeScene: Boolean): Float
+    fun onStop(velocity: Float, canChangeContent: Boolean): Float
 }
 
 internal class DraggableHandlerImpl(
     internal val layoutImpl: SceneTransitionLayoutImpl,
     internal val orientation: Orientation,
-    internal val coroutineScope: CoroutineScope,
 ) : DraggableHandler {
     internal val nestedScrollKey = Any()
     /** The [DraggableHandler] can only have one active [DragController] at a time. */
@@ -110,22 +97,25 @@
             return false
         }
 
-        val swipeTransition = dragController.swipeTransition
-
-        // Don't intercept a transition that is finishing.
-        if (swipeTransition.isFinishing) {
-            return false
-        }
+        val swipeAnimation = dragController.swipeAnimation
 
         // Only intercept the current transition if one of the 2 swipes results is also a transition
-        // between the same pair of scenes.
-        val fromScene = swipeTransition._currentScene
-        val swipes = computeSwipes(fromScene, startedPosition, pointersDown = 1)
-        val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromScene)
+        // between the same pair of contents.
+        val swipes = computeSwipes(startedPosition, pointersDown = 1)
+        val fromContent = layoutImpl.content(swipeAnimation.currentContent)
+        val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent)
+        val currentScene = layoutImpl.state.currentScene
+        val contentTransition = swipeAnimation.contentTransition
         return (upOrLeft != null &&
-            swipeTransition.isTransitioningBetween(fromScene.key, upOrLeft.toScene)) ||
+            contentTransition.isTransitioningBetween(
+                fromContent.key,
+                upOrLeft.toContent(currentScene)
+            )) ||
             (downOrRight != null &&
-                swipeTransition.isTransitioningBetween(fromScene.key, downOrRight.toScene))
+                contentTransition.isTransitioningBetween(
+                    fromContent.key,
+                    downOrRight.toContent(currentScene)
+                ))
     }
 
     override fun onDragStarted(
@@ -141,65 +131,68 @@
             }
 
             // This [transition] was already driving the animation: simply take over it.
-            // Stop animating and start from where the current offset.
-            oldDragController.swipeTransition.cancelOffsetAnimation()
+            // Stop animating and start from the current offset.
+            val oldSwipeAnimation = oldDragController.swipeAnimation
 
             // We need to recompute the swipe results since this is a new gesture, and the
             // fromScene.userActions may have changed.
             val swipes = oldDragController.swipes
-            swipes.updateSwipesResults(oldDragController.swipeTransition._fromScene)
+            swipes.updateSwipesResults(
+                fromContent = layoutImpl.content(oldSwipeAnimation.fromContent)
+            )
 
-            // A new gesture should always create a new SwipeTransition. This way there cannot be
+            // A new gesture should always create a new SwipeAnimation. This way there cannot be
             // different gestures controlling the same transition.
-            val swipeTransition = SwipeTransition(oldDragController.swipeTransition)
-            swipes.updateSwipesResults(fromScene = swipeTransition._fromScene)
-            return updateDragController(swipes, swipeTransition)
+            val swipeAnimation = createSwipeAnimation(oldSwipeAnimation)
+            return updateDragController(swipes, swipeAnimation)
         }
 
-        val transitionState = layoutImpl.state.transitionState
-        val fromScene = layoutImpl.scene(transitionState.currentScene)
-        val swipes = computeSwipes(fromScene, startedPosition, pointersDown)
+        val swipes = computeSwipes(startedPosition, pointersDown)
+        val fromContent = layoutImpl.contentForUserActions()
+
+        swipes.updateSwipesResults(fromContent)
         val result =
-            swipes.findUserActionResult(fromScene, overSlop, true)
-                // As we were unable to locate a valid target scene, the initial SwipeTransition
+            swipes.findUserActionResult(overSlop)
+                // As we were unable to locate a valid target scene, the initial SwipeAnimation
                 // cannot be defined. Consequently, a simple NoOp Controller will be returned.
                 ?: return NoOpDragController
 
-        return updateDragController(
-            swipes = swipes,
-            swipeTransition =
-                SwipeTransition(
-                    layoutImpl.state,
-                    coroutineScope,
-                    fromScene,
-                    result,
-                    swipes,
-                    layoutImpl,
-                    orientation,
-                )
-        )
+        val swipeAnimation = createSwipeAnimation(swipes, result)
+        return updateDragController(swipes, swipeAnimation)
     }
 
     private fun updateDragController(
         swipes: Swipes,
-        swipeTransition: SwipeTransition
-    ): DragController {
-        val newDragController = DragControllerImpl(this, swipes, swipeTransition)
-        newDragController.updateTransition(swipeTransition, force = true)
+        swipeAnimation: SwipeAnimation<*>
+    ): DragControllerImpl {
+        val newDragController = DragControllerImpl(this, swipes, swipeAnimation)
+        newDragController.updateTransition(swipeAnimation, force = true)
         dragController = newDragController
         return newDragController
     }
 
-    private fun computeSwipes(
-        fromScene: Scene,
-        startedPosition: Offset?,
-        pointersDown: Int
-    ): Swipes {
+    internal fun createSwipeAnimation(
+        swipes: Swipes,
+        result: UserActionResult,
+    ): SwipeAnimation<*> {
+        val upOrLeftResult = swipes.upOrLeftResult
+        val downOrRightResult = swipes.downOrRightResult
+        val isUpOrLeft =
+            when (result) {
+                upOrLeftResult -> true
+                downOrRightResult -> false
+                else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
+            }
+
+        return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
+    }
+
+    private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes {
         val fromSource =
             startedPosition?.let { position ->
                 layoutImpl.swipeSourceDetector
                     .source(
-                        fromScene.targetSize,
+                        layoutImpl.lastSize,
                         position.round(),
                         layoutImpl.density,
                         orientation,
@@ -255,242 +248,195 @@
 private class DragControllerImpl(
     private val draggableHandler: DraggableHandlerImpl,
     val swipes: Swipes,
-    var swipeTransition: SwipeTransition,
+    var swipeAnimation: SwipeAnimation<*>,
 ) : DragController {
     val layoutState = draggableHandler.layoutImpl.state
 
     /**
      * Whether this handle is active. If this returns false, calling [onDrag] and [onStop] will do
-     * nothing. We should have only one active controller at a time
+     * nothing.
      */
     val isDrivingTransition: Boolean
-        get() = layoutState.transitionState == swipeTransition
+        get() = layoutState.transitionState == swipeAnimation.contentTransition
 
     init {
         check(!isDrivingTransition) { "Multiple controllers with the same SwipeTransition" }
     }
 
-    fun updateTransition(newTransition: SwipeTransition, force: Boolean = false) {
-        if (isDrivingTransition || force) {
-            layoutState.startTransition(newTransition)
+    fun updateTransition(newTransition: SwipeAnimation<*>, force: Boolean = false) {
+        if (force || isDrivingTransition) {
+            layoutState.startTransitionImmediately(
+                animationScope = draggableHandler.layoutImpl.animationScope,
+                newTransition.contentTransition,
+                true
+            )
         }
 
-        swipeTransition = newTransition
+        swipeAnimation = newTransition
     }
 
     /**
      * We receive a [delta] that can be consumed to change the offset of the current
-     * [SwipeTransition].
+     * [SwipeAnimation].
      *
      * @return the consumed delta
      */
     override fun onDrag(delta: Float): Float {
-        if (delta == 0f || !isDrivingTransition || swipeTransition.isFinishing) {
+        return onDrag(delta, swipeAnimation)
+    }
+
+    private fun <T : ContentKey> onDrag(delta: Float, swipeAnimation: SwipeAnimation<T>): Float {
+        if (delta == 0f || !isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
             return 0f
         }
 
-        val toScene = swipeTransition._toScene
-        val distance = swipeTransition.distance()
-        val previousOffset = swipeTransition.dragOffset
+        val toContent = swipeAnimation.toContent
+        val distance = swipeAnimation.distance()
+        val previousOffset = swipeAnimation.dragOffset
         val desiredOffset = previousOffset + delta
 
         fun hasReachedToSceneUpOrLeft() =
             distance < 0 &&
                 desiredOffset <= distance &&
-                swipes.upOrLeftResult?.toScene == toScene.key
+                swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent
 
         fun hasReachedToSceneDownOrRight() =
             distance > 0 &&
                 desiredOffset >= distance &&
-                swipes.downOrRightResult?.toScene == toScene.key
+                swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent
 
-        // Considering accelerated swipe: Change fromScene in the case where the user quickly swiped
-        // multiple times in the same direction to accelerate the transition from A => B then B => C
+        // Considering accelerated swipe: Change fromContent in the case where the user quickly
+        // swiped multiple times in the same direction to accelerate the transition from A => B then
+        // B => C.
         //
         // TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging
         //  twice before B has been reached
-        val hasReachedToScene =
-            swipeTransition._currentScene == toScene &&
+        val hasReachedToContent =
+            swipeAnimation.currentContent == toContent &&
                 (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight())
 
-        val fromScene: Scene
+        val fromContent: ContentKey
         val currentTransitionOffset: Float
         val newOffset: Float
         val consumedDelta: Float
-        if (hasReachedToScene) {
-            // The new transition will start from the current toScene
-            fromScene = toScene
-            // The current transition is completed (we have reached the distance)
+        if (hasReachedToContent) {
+            // The new transition will start from the current toContent.
+            fromContent = toContent
+
+            // The current transition is completed (we have reached the full swipe distance).
             currentTransitionOffset = distance
-            // The next transition will start with the remaining offset
+
+            // The next transition will start with the remaining offset.
             newOffset = desiredOffset - distance
             consumedDelta = delta
         } else {
-            fromScene = swipeTransition._fromScene
-            val desiredProgress = swipeTransition.computeProgress(desiredOffset)
-            // note: the distance could be negative if fromScene is aboveOrLeft of toScene.
+            fromContent = swipeAnimation.fromContent
+            val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
+
+            // Note: the distance could be negative if fromContent is above or to the left of
+            // toContent.
             currentTransitionOffset =
                 when {
                     distance == DistanceUnspecified ||
-                        swipeTransition.isWithinProgressRange(desiredProgress) -> desiredOffset
+                        swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
+                        desiredOffset
                     distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
                     else -> desiredOffset.fastCoerceIn(distance, 0f)
                 }
+
             // If there is a new transition, we will use the same offset
             newOffset = currentTransitionOffset
             consumedDelta = newOffset - previousOffset
         }
 
-        swipeTransition.dragOffset = currentTransitionOffset
+        swipeAnimation.dragOffset = currentTransitionOffset
 
-        val result =
-            swipes.findUserActionResult(
-                fromScene = fromScene,
-                directionOffset = newOffset,
-                updateSwipesResults = hasReachedToScene
-            )
+        if (hasReachedToContent) {
+            swipes.updateSwipesResults(draggableHandler.layoutImpl.content(fromContent))
+        }
+        val result = swipes.findUserActionResult(directionOffset = newOffset)
 
         if (result == null) {
-            onStop(velocity = delta, canChangeScene = true)
+            onStop(velocity = delta, canChangeContent = true)
             return 0f
         }
 
         val needNewTransition =
-            hasReachedToScene ||
-                result.toScene != swipeTransition.toScene ||
-                result.transitionKey != swipeTransition.key
+            hasReachedToContent ||
+                result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
+                result.transitionKey != swipeAnimation.contentTransition.key
 
         if (needNewTransition) {
             // Make sure the current transition will finish to the right current scene.
-            swipeTransition._currentScene = fromScene
+            swipeAnimation.currentContent = fromContent
 
-            val newSwipeTransition =
-                SwipeTransition(
-                    layoutState = layoutState,
-                    coroutineScope = draggableHandler.coroutineScope,
-                    fromScene = fromScene,
-                    result = result,
-                    swipes = swipes,
-                    layoutImpl = draggableHandler.layoutImpl,
-                    orientation = draggableHandler.orientation,
-                )
-            newSwipeTransition.dragOffset = newOffset
-            updateTransition(newSwipeTransition)
+            val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result)
+            newSwipeAnimation.dragOffset = newOffset
+            updateTransition(newSwipeAnimation)
         }
 
         return consumedDelta
     }
 
-    override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
+    override fun onStop(velocity: Float, canChangeContent: Boolean): Float {
+        return onStop(velocity, canChangeContent, swipeAnimation)
+    }
+
+    private fun <T : ContentKey> onStop(
+        velocity: Float,
+        canChangeContent: Boolean,
+
+        // Important: Make sure that this has the same name as [this.swipeAnimation] so that all the
+        // code here references the current animation when [onDragStopped] is called, otherwise the
+        // callbacks (like onAnimationCompleted()) might incorrectly finish a new transition that
+        // replaced this one.
+        swipeAnimation: SwipeAnimation<T>,
+    ): Float {
         // The state was changed since the drag started; don't do anything.
-        if (!isDrivingTransition || swipeTransition.isFinishing) {
+        if (!isDrivingTransition || swipeAnimation.isAnimatingOffset()) {
             return 0f
         }
 
-        // Important: Make sure that all the code here references the current transition when
-        // [onDragStopped] is called, otherwise the callbacks (like onAnimationCompleted()) might
-        // incorrectly finish a new transition that replaced this one.
-        val swipeTransition = this.swipeTransition
-
-        fun animateTo(targetScene: Scene, targetOffset: Float) {
-            // If the effective current scene changed, it should be reflected right now in the
-            // current scene state, even before the settle animation is ongoing. That way all the
-            // swipeables and back handlers will be refreshed and the user can for instance quickly
-            // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then
-            // immediately go back B => A.
-            if (targetScene != swipeTransition._currentScene) {
-                swipeTransition._currentScene = targetScene
-            }
-
-            swipeTransition.animateOffset(
-                coroutineScope = draggableHandler.coroutineScope,
+        fun animateTo(targetContent: T) {
+            swipeAnimation.animateOffset(
                 initialVelocity = velocity,
-                targetOffset = targetOffset,
-                targetScene = targetScene.key,
+                targetContent = targetContent,
             )
         }
 
-        val fromScene = swipeTransition._fromScene
-        if (canChangeScene) {
-            // If we are halfway between two scenes, we check what the target will be based on the
+        val fromContent = swipeAnimation.fromContent
+        if (canChangeContent) {
+            // If we are halfway between two contents, we check what the target will be based on the
             // velocity and offset of the transition, then we launch the animation.
 
-            val toScene = swipeTransition._toScene
+            val toContent = swipeAnimation.toContent
 
-            // Compute the destination scene (and therefore offset) to settle in.
-            val offset = swipeTransition.dragOffset
-            val distance = swipeTransition.distance()
-            var targetScene: Scene
-            var targetOffset: Float
-            if (
-                distance != DistanceUnspecified &&
-                    shouldCommitSwipe(
-                        offset = offset,
-                        distance = distance,
-                        velocity = velocity,
-                        wasCommitted = swipeTransition._currentScene == toScene,
-                        requiresFullDistanceSwipe = swipeTransition.requiresFullDistanceSwipe,
-                    )
-            ) {
-                targetScene = toScene
-                targetOffset = distance
-            } else {
-                targetScene = fromScene
-                targetOffset = 0f
-            }
-
-            if (
-                targetScene != swipeTransition._currentScene &&
-                    !layoutState.canChangeScene(targetScene.key)
-            ) {
-                // We wanted to change to a new scene but we are not allowed to, so we animate back
-                // to the current scene.
-                targetScene = swipeTransition._currentScene
-                targetOffset =
-                    if (targetScene == fromScene) {
-                        0f
-                    } else {
-                        check(distance != DistanceUnspecified) {
-                            "distance is equal to $DistanceUnspecified"
-                        }
-                        distance
-                    }
-            }
-
-            animateTo(targetScene = targetScene, targetOffset = targetOffset)
-        } else {
-            // We are doing an overscroll animation between scenes. In this case, we can also start
-            // from the idle position.
-
-            val startFromIdlePosition = swipeTransition.dragOffset == 0f
-
-            if (startFromIdlePosition) {
-                // If there is a target scene, we start the overscroll animation.
-                val result = swipes.findUserActionResultStrict(velocity)
-                if (result == null) {
-                    // We will not animate
-                    swipeTransition.snapToScene(fromScene.key)
-                    return 0f
+            // Compute the destination content (and therefore offset) to settle in.
+            val offset = swipeAnimation.dragOffset
+            val distance = swipeAnimation.distance()
+            val targetContent =
+                if (
+                    distance != DistanceUnspecified &&
+                        shouldCommitSwipe(
+                            offset = offset,
+                            distance = distance,
+                            velocity = velocity,
+                            wasCommitted = swipeAnimation.currentContent == toContent,
+                            requiresFullDistanceSwipe = swipeAnimation.requiresFullDistanceSwipe,
+                        )
+                ) {
+                    toContent
+                } else {
+                    fromContent
                 }
 
-                val newSwipeTransition =
-                    SwipeTransition(
-                            layoutState = layoutState,
-                            coroutineScope = draggableHandler.coroutineScope,
-                            fromScene = fromScene,
-                            result = result,
-                            swipes = swipes,
-                            layoutImpl = draggableHandler.layoutImpl,
-                            orientation = draggableHandler.orientation,
-                        )
-                        .apply { _currentScene = swipeTransition._currentScene }
-
-                updateTransition(newSwipeTransition)
-                animateTo(targetScene = fromScene, targetOffset = 0f)
-            } else {
-                // We were between two scenes: animate to the initial scene.
-                animateTo(targetScene = fromScene, targetOffset = 0f)
+            animateTo(targetContent = targetContent)
+        } else {
+            // We are doing an overscroll preview animation between scenes.
+            check(fromContent == swipeAnimation.currentContent) {
+                "canChangeContent is false but currentContent != fromContent"
             }
+            animateTo(targetContent = fromContent)
         }
 
         // The onStop animation consumes any remaining velocity.
@@ -541,328 +487,8 @@
     }
 }
 
-private fun SwipeTransition(
-    layoutState: MutableSceneTransitionLayoutStateImpl,
-    coroutineScope: CoroutineScope,
-    fromScene: Scene,
-    result: UserActionResult,
-    swipes: Swipes,
-    layoutImpl: SceneTransitionLayoutImpl,
-    orientation: Orientation,
-): SwipeTransition {
-    val upOrLeftResult = swipes.upOrLeftResult
-    val downOrRightResult = swipes.downOrRightResult
-    val isUpOrLeft =
-        when (result) {
-            upOrLeftResult -> true
-            downOrRightResult -> false
-            else -> error("Unknown result $result ($upOrLeftResult $downOrRightResult)")
-        }
-
-    return SwipeTransition(
-        layoutImpl = layoutImpl,
-        layoutState = layoutState,
-        coroutineScope = coroutineScope,
-        key = result.transitionKey,
-        _fromScene = fromScene,
-        _toScene = layoutImpl.scene(result.toScene),
-        userActionDistanceScope = layoutImpl.userActionDistanceScope,
-        orientation = orientation,
-        isUpOrLeft = isUpOrLeft,
-        requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
-        replacedTransition = null,
-    )
-}
-
-private fun SwipeTransition(old: SwipeTransition): SwipeTransition {
-    return SwipeTransition(
-            layoutImpl = old.layoutImpl,
-            layoutState = old.layoutState,
-            coroutineScope = old.coroutineScope,
-            key = old.key,
-            _fromScene = old._fromScene,
-            _toScene = old._toScene,
-            userActionDistanceScope = old.userActionDistanceScope,
-            orientation = old.orientation,
-            isUpOrLeft = old.isUpOrLeft,
-            lastDistance = old.lastDistance,
-            requiresFullDistanceSwipe = old.requiresFullDistanceSwipe,
-            replacedTransition = old,
-        )
-        .apply {
-            _currentScene = old._currentScene
-            dragOffset = old.dragOffset
-        }
-}
-
-private class SwipeTransition(
-    val layoutImpl: SceneTransitionLayoutImpl,
-    val layoutState: MutableSceneTransitionLayoutStateImpl,
-    val coroutineScope: CoroutineScope,
-    override val key: TransitionKey?,
-    val _fromScene: Scene,
-    val _toScene: Scene,
-    val userActionDistanceScope: UserActionDistanceScope,
-    override val orientation: Orientation,
-    override val isUpOrLeft: Boolean,
-    val requiresFullDistanceSwipe: Boolean,
-    replacedTransition: SwipeTransition?,
-    var lastDistance: Float = DistanceUnspecified,
-) :
-    TransitionState.Transition(_fromScene.key, _toScene.key, replacedTransition),
-    ContentState.HasOverscrollProperties {
-    var _currentScene by mutableStateOf(_fromScene)
-    override val currentScene: SceneKey
-        get() = _currentScene.key
-
-    override val progress: Float
-        get() {
-            // Important: If we are going to return early because distance is equal to 0, we should
-            // still make sure we read the offset before returning so that the calling code still
-            // subscribes to the offset value.
-            val offset = offsetAnimation?.animatable?.value ?: dragOffset
-
-            return computeProgress(offset)
-        }
-
-    fun computeProgress(offset: Float): Float {
-        val distance = distance()
-        if (distance == DistanceUnspecified) {
-            return 0f
-        }
-        return offset / distance
-    }
-
-    override val progressVelocity: Float
-        get() {
-            val animatable = offsetAnimation?.animatable ?: return 0f
-            val distance = distance()
-            if (distance == DistanceUnspecified) {
-                return 0f
-            }
-
-            val velocityInDistanceUnit = animatable.velocity
-            return velocityInDistanceUnit / distance.absoluteValue
-        }
-
-    override val isInitiatedByUserInput = true
-
-    override var bouncingContent: SceneKey? = null
-
-    /** The current offset caused by the drag gesture. */
-    var dragOffset by mutableFloatStateOf(0f)
-
-    /** The offset animation that animates the offset once the user lifts their finger. */
-    private var offsetAnimation: OffsetAnimation? by mutableStateOf(null)
-
-    override val isUserInputOngoing: Boolean
-        get() = offsetAnimation == null
-
-    override val overscrollScope: OverscrollScope =
-        object : OverscrollScope {
-            override val density: Float
-                get() = layoutImpl.density.density
-
-            override val fontScale: Float
-                get() = layoutImpl.density.fontScale
-
-            override val absoluteDistance: Float
-                get() = distance().absoluteValue
-        }
-
-    /** Whether [TransitionState.Transition.finish] was called on this transition. */
-    var isFinishing = false
-        private set
-
-    /**
-     * The signed distance between [fromScene] and [toScene]. It is negative if [fromScene] is above
-     * or to the left of [toScene].
-     *
-     * Note that this distance can be equal to [DistanceUnspecified] during the first frame of a
-     * transition when the distance depends on the size or position of an element that is composed
-     * in the scene we are going to.
-     */
-    fun distance(): Float {
-        if (lastDistance != DistanceUnspecified) {
-            return lastDistance
-        }
-
-        val absoluteDistance =
-            with(transformationSpec.distance ?: DefaultSwipeDistance) {
-                userActionDistanceScope.absoluteDistance(
-                    _fromScene.targetSize,
-                    orientation,
-                )
-            }
-
-        if (absoluteDistance <= 0f) {
-            return DistanceUnspecified
-        }
-
-        val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
-        lastDistance = distance
-        return distance
-    }
-
-    /** Ends any previous [offsetAnimation] and runs the new [animation]. */
-    private fun startOffsetAnimation(animation: () -> OffsetAnimation): OffsetAnimation {
-        cancelOffsetAnimation()
-        return animation().also { offsetAnimation = it }
-    }
-
-    /** Cancel any ongoing offset animation. */
-    // TODO(b/317063114) This should be a suspended function to avoid multiple jobs running at
-    // the same time.
-    fun cancelOffsetAnimation() {
-        val animation = offsetAnimation ?: return
-        offsetAnimation = null
-
-        dragOffset = animation.animatable.value
-        animation.job.cancel()
-    }
-
-    fun animateOffset(
-        // TODO(b/317063114) The CoroutineScope should be removed.
-        coroutineScope: CoroutineScope,
-        initialVelocity: Float,
-        targetOffset: Float,
-        targetScene: SceneKey,
-    ): OffsetAnimation {
-        val initialProgress = progress
-        // Skip the animation if we have already reached the target scene and the overscroll does
-        // not animate anything.
-        val hasReachedTargetScene =
-            (targetScene == toScene && initialProgress >= 1f) ||
-                (targetScene == fromScene && initialProgress <= 0f)
-        val skipAnimation = hasReachedTargetScene && !isWithinProgressRange(initialProgress)
-
-        return startOffsetAnimation {
-            val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
-            val isTargetGreater = targetOffset > animatable.value
-            val startedWhenOvercrollingTargetScene =
-                if (targetScene == fromScene) initialProgress < 0f else initialProgress > 1f
-            val job =
-                coroutineScope
-                    // Important: We start atomically to make sure that we start the coroutine even
-                    // if it is cancelled right after it is launched, so that snapToScene() is
-                    // correctly called. Otherwise, this transition will never be stopped and we
-                    // will never settle to Idle.
-                    .launch(start = CoroutineStart.ATOMIC) {
-                        // TODO(b/327249191): Refactor the code so that we don't even launch a
-                        // coroutine if we don't need to animate.
-                        if (skipAnimation) {
-                            snapToScene(targetScene)
-                            cancelOffsetAnimation()
-                            dragOffset = targetOffset
-                            return@launch
-                        }
-
-                        try {
-                            val swipeSpec =
-                                transformationSpec.swipeSpec
-                                    ?: layoutState.transitions.defaultSwipeSpec
-                            animatable.animateTo(
-                                targetValue = targetOffset,
-                                animationSpec = swipeSpec,
-                                initialVelocity = initialVelocity,
-                            ) {
-                                if (bouncingContent == null) {
-                                    val isBouncing =
-                                        if (isTargetGreater) {
-                                            if (startedWhenOvercrollingTargetScene) {
-                                                value >= targetOffset
-                                            } else {
-                                                value > targetOffset
-                                            }
-                                        } else {
-                                            if (startedWhenOvercrollingTargetScene) {
-                                                value <= targetOffset
-                                            } else {
-                                                value < targetOffset
-                                            }
-                                        }
-
-                                    if (isBouncing) {
-                                        bouncingContent = targetScene
-
-                                        // Immediately stop this transition if we are bouncing on a
-                                        // scene that does not bounce.
-                                        if (!isWithinProgressRange(progress)) {
-                                            snapToScene(targetScene)
-                                        }
-                                    }
-                                }
-                            }
-                        } finally {
-                            snapToScene(targetScene)
-                        }
-                    }
-
-            OffsetAnimation(animatable, job)
-        }
-    }
-
-    fun snapToScene(scene: SceneKey) {
-        cancelOffsetAnimation()
-        layoutState.finishTransition(this, idleScene = scene)
-    }
-
-    override fun finish(): Job {
-        if (isFinishing) return requireNotNull(offsetAnimation).job
-        isFinishing = true
-
-        // If we were already animating the offset, simply return the job.
-        offsetAnimation?.let {
-            return it.job
-        }
-
-        // Animate to the current scene.
-        val targetScene = currentScene
-        val targetOffset =
-            if (targetScene == fromScene) {
-                0f
-            } else {
-                val distance = distance()
-                check(distance != DistanceUnspecified) {
-                    "targetScene != fromScene but distance is unspecified"
-                }
-                distance
-            }
-
-        val animation =
-            animateOffset(
-                coroutineScope = coroutineScope,
-                initialVelocity = 0f,
-                targetOffset = targetOffset,
-                targetScene = currentScene,
-            )
-        check(offsetAnimation == animation)
-        return animation.job
-    }
-
-    internal class OffsetAnimation(
-        /** The animatable used to animate the offset. */
-        val animatable: Animatable<Float, AnimationVector1D>,
-
-        /** The job in which [animatable] is animated. */
-        val job: Job,
-    )
-}
-
-private object DefaultSwipeDistance : UserActionDistance {
-    override fun UserActionDistanceScope.absoluteDistance(
-        fromSceneSize: IntSize,
-        orientation: Orientation,
-    ): Float {
-        return when (orientation) {
-            Orientation.Horizontal -> fromSceneSize.width
-            Orientation.Vertical -> fromSceneSize.height
-        }.toFloat()
-    }
-}
-
 /** The [Swipe] associated to a given fromScene, startedPosition and pointersDown. */
-private class Swipes(
+internal class Swipes(
     val upOrLeft: Swipe.Resolved?,
     val downOrRight: Swipe.Resolved?,
     val upOrLeftNoSource: Swipe.Resolved?,
@@ -872,8 +498,8 @@
     var upOrLeftResult: UserActionResult? = null
     var downOrRightResult: UserActionResult? = null
 
-    fun computeSwipesResults(fromScene: Scene): Pair<UserActionResult?, UserActionResult?> {
-        val userActions = fromScene.userActions
+    fun computeSwipesResults(fromContent: Content): Pair<UserActionResult?, UserActionResult?> {
+        val userActions = fromContent.userActions
         fun result(swipe: Swipe.Resolved?): UserActionResult? {
             return userActions[swipe ?: return null]
         }
@@ -883,39 +509,33 @@
         return upOrLeftResult to downOrRightResult
     }
 
-    fun updateSwipesResults(fromScene: Scene) {
-        val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromScene)
+    /**
+     * Update the swipes results.
+     *
+     * Usually we don't want to update them while doing a drag, because this could change the target
+     * content (jump cutting) to a different content, when some system state changed the targets the
+     * background. However, an update is needed any time we calculate the targets for a new
+     * fromContent.
+     */
+    fun updateSwipesResults(fromContent: Content) {
+        val (upOrLeftResult, downOrRightResult) = computeSwipesResults(fromContent)
 
         this.upOrLeftResult = upOrLeftResult
         this.downOrRightResult = downOrRightResult
     }
 
     /**
-     * Returns the [UserActionResult] from [fromScene] in the direction of [directionOffset].
+     * Returns the [UserActionResult] in the direction of [directionOffset].
      *
-     * @param fromScene the scene from which we look for the target
      * @param directionOffset signed float that indicates the direction. Positive is down or right
      *   negative is up or left.
-     * @param updateSwipesResults whether the target scenes should be updated to the current values
-     *   held in the Scenes map. Usually we don't want to update them while doing a drag, because
-     *   this could change the target scene (jump cutting) to a different scene, when some system
-     *   state changed the targets the background. However, an update is needed any time we
-     *   calculate the targets for a new fromScene.
      * @return null when there are no targets in either direction. If one direction is null and you
      *   drag into the null direction this function will return the opposite direction, assuming
      *   that the users intention is to start the drag into the other direction eventually. If
      *   [directionOffset] is 0f and both direction are available, it will default to
      *   [upOrLeftResult].
      */
-    fun findUserActionResult(
-        fromScene: Scene,
-        directionOffset: Float,
-        updateSwipesResults: Boolean,
-    ): UserActionResult? {
-        if (updateSwipesResults) {
-            updateSwipesResults(fromScene)
-        }
-
+    fun findUserActionResult(directionOffset: Float): UserActionResult? {
         return when {
             upOrLeftResult == null && downOrRightResult == null -> null
             (directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
@@ -923,18 +543,6 @@
             else -> downOrRightResult
         }
     }
-
-    /**
-     * A strict version of [findUserActionResult] that will return null when there is no Scene in
-     * [directionOffset] direction
-     */
-    fun findUserActionResultStrict(directionOffset: Float): UserActionResult? {
-        return when {
-            directionOffset > 0f -> upOrLeftResult
-            directionOffset < 0f -> downOrRightResult
-            else -> null
-        }
-    }
 }
 
 internal class NestedScrollHandlerImpl(
@@ -1050,10 +658,6 @@
 
                 val canStart =
                     when (behavior) {
-                        NestedScrollBehavior.DuringTransitionBetweenScenes -> {
-                            canChangeScene = false // unused: added for consistency
-                            false
-                        }
                         NestedScrollBehavior.EdgeNoPreview -> {
                             canChangeScene = isZeroOffset
                             isZeroOffset && hasNextScene(offsetAvailable)
@@ -1116,7 +720,7 @@
                 val controller = dragController ?: error("Should be called after onStart")
 
                 controller
-                    .onStop(velocity = velocityAvailable, canChangeScene = canChangeScene)
+                    .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
                     .also { dragController = null }
             },
         )
@@ -1134,5 +738,5 @@
 private object NoOpDragController : DragController {
     override fun onDrag(delta: Float) = 0f
 
-    override fun onStop(velocity: Float, canChangeScene: Boolean) = 0f
+    override fun onStop(velocity: Float, canChangeContent: Boolean) = 0f
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index ec7c77b..a076f22 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -46,10 +46,9 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.round
 import androidx.compose.ui.util.fastCoerceIn
-import androidx.compose.ui.util.fastLastOrNull
+import androidx.compose.ui.util.fastForEachReversed
 import androidx.compose.ui.util.lerp
 import com.android.compose.animation.scene.content.Content
-import com.android.compose.animation.scene.content.state.ContentState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.PropertyTransformation
 import com.android.compose.animation.scene.transformation.SharedElementTransformation
@@ -69,7 +68,7 @@
      * The last transition that was used when computing the state (size, position and alpha) of this
      * element in any content, or `null` if it was last laid out when idle.
      */
-    var lastTransition: ContentState.Transition<*>? = null
+    var lastTransition: TransitionState.Transition? = null
 
     /** Whether this element was ever drawn in a content. */
     var wasDrawnInAnyContent = false
@@ -146,30 +145,32 @@
     // layout/drawing.
     // TODO(b/341072461): Revert this and read the current transitions in ElementNode directly once
     // we can ensure that SceneTransitionLayoutImpl will compose new contents first.
-    val currentTransitions = layoutImpl.state.currentTransitions
-    return then(ElementModifier(layoutImpl, currentTransitions, content, key)).testTag(key.testTag)
+    val currentTransitionStates = layoutImpl.state.transitionStates
+    return then(ElementModifier(layoutImpl, currentTransitionStates, content, key))
+        .testTag(key.testTag)
 }
 
 /**
  * An element associated to [ElementNode]. Note that this element does not support updates as its
  * arguments should always be the same.
  */
-private data class ElementModifier(
-    private val layoutImpl: SceneTransitionLayoutImpl,
-    private val currentTransitions: List<TransitionState.Transition>,
-    private val content: Content,
-    private val key: ElementKey,
+internal data class ElementModifier(
+    internal val layoutImpl: SceneTransitionLayoutImpl,
+    private val currentTransitionStates: List<TransitionState>,
+    internal val content: Content,
+    internal val key: ElementKey,
 ) : ModifierNodeElement<ElementNode>() {
-    override fun create(): ElementNode = ElementNode(layoutImpl, currentTransitions, content, key)
+    override fun create(): ElementNode =
+        ElementNode(layoutImpl, currentTransitionStates, content, key)
 
     override fun update(node: ElementNode) {
-        node.update(layoutImpl, currentTransitions, content, key)
+        node.update(layoutImpl, currentTransitionStates, content, key)
     }
 }
 
 internal class ElementNode(
     private var layoutImpl: SceneTransitionLayoutImpl,
-    private var currentTransitions: List<TransitionState.Transition>,
+    private var currentTransitionStates: List<TransitionState>,
     private var content: Content,
     private var key: ElementKey,
 ) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode {
@@ -227,12 +228,12 @@
 
     fun update(
         layoutImpl: SceneTransitionLayoutImpl,
-        currentTransitions: List<TransitionState.Transition>,
+        currentTransitionStates: List<TransitionState>,
         content: Content,
         key: ElementKey,
     ) {
         check(layoutImpl == this.layoutImpl && content == this.content)
-        this.currentTransitions = currentTransitions
+        this.currentTransitionStates = currentTransitionStates
 
         removeNodeFromContentState()
 
@@ -288,31 +289,90 @@
         measurable: Measurable,
         constraints: Constraints,
     ): MeasureResult {
-        val transitions = currentTransitions
-        val transition = elementTransition(layoutImpl, element, transitions)
+        val elementState = elementState(layoutImpl, element, currentTransitionStates)
+        if (elementState == null) {
+            // If the element is not part of any transition, place it normally in its idle scene.
+            val currentState = currentTransitionStates.last()
+            val placeInThisContent =
+                elementContentWhenIdle(
+                    layoutImpl,
+                    currentState.currentScene,
+                    currentState.currentOverlays,
+                    isInContent = { it in element.stateByContent },
+                ) == content.key
+
+            return if (placeInThisContent) {
+                placeNormally(measurable, constraints)
+            } else {
+                doNotPlace(measurable, constraints)
+            }
+        }
+
+        val transition = elementState as? TransitionState.Transition
 
         // If this element is not supposed to be laid out now, either because it is not part of any
         // ongoing transition or the other content of its transition is overscrolling, then lay out
         // the element normally and don't place it.
-        val overscrollScene = transition?.currentOverscrollSpec?.scene
-        val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != content.key
-        val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null
-        if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) {
-            recursivelyClearPlacementValues()
-            stateInContent.lastSize = Element.SizeUnspecified
+        val overscrollContent = transition?.currentOverscrollSpec?.content
+        if (overscrollContent != null && overscrollContent != content.key) {
+            when (transition) {
+                is TransitionState.Transition.ChangeScene ->
+                    return doNotPlace(measurable, constraints)
 
-            val placeable = measurable.measure(constraints)
-            return layout(placeable.width, placeable.height) { /* Do not place */ }
+                // If we are overscrolling an overlay that does not contain an element that is in
+                // the current scene, place it in that scene otherwise the element won't be placed
+                // at all.
+                is TransitionState.Transition.ShowOrHideOverlay,
+                is TransitionState.Transition.ReplaceOverlay -> {
+                    if (
+                        content.key == transition.currentScene &&
+                            overscrollContent !in element.stateByContent
+                    ) {
+                        return placeNormally(measurable, constraints)
+                    } else {
+                        return doNotPlace(measurable, constraints)
+                    }
+                }
+            }
         }
 
         val placeable =
             measure(layoutImpl, element, transition, stateInContent, measurable, constraints)
         stateInContent.lastSize = placeable.size()
-        return layout(placeable.width, placeable.height) { place(transition, placeable) }
+        return layout(placeable.width, placeable.height) { place(elementState, placeable) }
+    }
+
+    private fun ApproachMeasureScope.doNotPlace(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        recursivelyClearPlacementValues()
+        stateInContent.lastSize = Element.SizeUnspecified
+
+        val placeable = measurable.measure(constraints)
+        return layout(placeable.width, placeable.height) { /* Do not place */ }
+    }
+
+    private fun ApproachMeasureScope.placeNormally(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        val placeable = measurable.measure(constraints)
+        stateInContent.lastSize = placeable.size()
+        return layout(placeable.width, placeable.height) {
+            coordinates?.let {
+                with(layoutImpl.lookaheadScope) {
+                    stateInContent.lastOffset =
+                        lookaheadScopeCoordinates.localPositionOf(it, Offset.Zero)
+                }
+            }
+
+            placeable.place(0, 0)
+        }
     }
 
     private fun Placeable.PlacementScope.place(
-        transition: ContentState.Transition<*>?,
+        elementState: TransitionState,
         placeable: Placeable,
     ) {
         with(layoutImpl.lookaheadScope) {
@@ -322,11 +382,12 @@
                 coordinates ?: error("Element ${element.key} does not have any coordinates")
 
             // No need to place the element in this content if we don't want to draw it anyways.
-            if (!shouldPlaceElement(layoutImpl, content.key, element, transition)) {
+            if (!shouldPlaceElement(layoutImpl, content.key, element, elementState)) {
                 recursivelyClearPlacementValues()
                 return
             }
 
+            val transition = elementState as? TransitionState.Transition
             val currentOffset = lookaheadScopeCoordinates.localPositionOf(coords, Offset.Zero)
             val targetOffset =
                 computeValue(
@@ -392,11 +453,15 @@
                         return@placeWithLayer
                     }
 
-                    val transition = elementTransition(layoutImpl, element, currentTransitions)
-                    if (!shouldPlaceElement(layoutImpl, content.key, element, transition)) {
+                    val elementState = elementState(layoutImpl, element, currentTransitionStates)
+                    if (
+                        elementState == null ||
+                            !shouldPlaceElement(layoutImpl, content.key, element, elementState)
+                    ) {
                         return@placeWithLayer
                     }
 
+                    val transition = elementState as? TransitionState.Transition
                     alpha = elementAlpha(layoutImpl, element, transition, stateInContent)
                     compositingStrategy = CompositingStrategy.ModulateAlpha
                 }
@@ -426,7 +491,9 @@
     override fun ContentDrawScope.draw() {
         element.wasDrawnInAnyContent = true
 
-        val transition = elementTransition(layoutImpl, element, currentTransitions)
+        val transition =
+            elementState(layoutImpl, element, currentTransitionStates)
+                as? TransitionState.Transition
         val drawScale = getDrawScale(layoutImpl, element, transition, stateInContent)
         if (drawScale == Scale.Default) {
             drawContent()
@@ -469,21 +536,15 @@
     }
 }
 
-/**
- * The transition that we should consider for [element]. This is the last transition where one of
- * its contents contains the element.
- */
-private fun elementTransition(
+/** The [TransitionState] that we should consider for [element]. */
+private fun elementState(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transitions: List<TransitionState.Transition>,
-): ContentState.Transition<*>? {
-    val transition =
-        transitions.fastLastOrNull { transition ->
-            transition.fromScene in element.stateByContent ||
-                transition.toScene in element.stateByContent
-        }
+    transitionStates: List<TransitionState>,
+): TransitionState? {
+    val state = elementState(transitionStates, isInContent = { it in element.stateByContent })
 
+    val transition = state as? TransitionState.Transition
     val previousTransition = element.lastTransition
     element.lastTransition = transition
 
@@ -498,14 +559,73 @@
         }
     }
 
-    return transition
+    return state
+}
+
+internal inline fun elementState(
+    transitionStates: List<TransitionState>,
+    isInContent: (ContentKey) -> Boolean,
+): TransitionState? {
+    val lastState = transitionStates.last()
+    if (lastState is TransitionState.Idle) {
+        check(transitionStates.size == 1)
+        return lastState
+    }
+
+    // Find the last transition with a content that contains the element.
+    transitionStates.fastForEachReversed { state ->
+        val transition = state as TransitionState.Transition
+        if (isInContent(transition.fromContent) || isInContent(transition.toContent)) {
+            return transition
+        }
+    }
+
+    return null
+}
+
+internal inline fun elementContentWhenIdle(
+    layoutImpl: SceneTransitionLayoutImpl,
+    idle: TransitionState.Idle,
+    isInContent: (ContentKey) -> Boolean,
+): ContentKey {
+    val currentScene = idle.currentScene
+    val overlays = idle.currentOverlays
+    return elementContentWhenIdle(layoutImpl, currentScene, overlays, isInContent)
+}
+
+private inline fun elementContentWhenIdle(
+    layoutImpl: SceneTransitionLayoutImpl,
+    currentScene: SceneKey,
+    overlays: Set<OverlayKey>,
+    isInContent: (ContentKey) -> Boolean,
+): ContentKey {
+    if (overlays.isEmpty()) {
+        return currentScene
+    }
+
+    // Find the overlay with highest zIndex that contains the element.
+    // TODO(b/353679003): Should we cache enabledOverlays into a List<> to avoid a lot of
+    // allocations here?
+    var currentOverlay: OverlayKey? = null
+    for (overlay in overlays) {
+        if (
+            isInContent(overlay) &&
+                (currentOverlay == null ||
+                    (layoutImpl.overlay(overlay).zIndex >
+                        layoutImpl.overlay(currentOverlay).zIndex))
+        ) {
+            currentOverlay = overlay
+        }
+    }
+
+    return currentOverlay ?: currentScene
 }
 
 private fun prepareInterruption(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: ContentState.Transition<*>,
-    previousTransition: ContentState.Transition<*>,
+    transition: TransitionState.Transition,
+    previousTransition: TransitionState.Transition,
 ) {
     if (transition.replacedTransition == previousTransition) {
         return
@@ -552,7 +672,7 @@
  */
 private fun reconcileStates(
     element: Element,
-    transition: ContentState.Transition<*>,
+    transition: TransitionState.Transition,
 ) {
     val fromContentState = element.stateByContent[transition.fromContent] ?: return
     val toContentState = element.stateByContent[transition.toContent] ?: return
@@ -621,7 +741,7 @@
  */
 private inline fun <T> computeInterruptedValue(
     layoutImpl: SceneTransitionLayoutImpl,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     value: T,
     unspecifiedValue: T,
     zeroValue: T,
@@ -668,7 +788,7 @@
 private inline fun <T> setPlacementInterruptionDelta(
     element: Element,
     stateInContent: Element.State,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     delta: T,
     setter: (Element.State, T) -> Unit,
 ) {
@@ -694,12 +814,20 @@
     layoutImpl: SceneTransitionLayoutImpl,
     content: ContentKey,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    elementState: TransitionState,
 ): Boolean {
-    // Always place the element if we are idle.
-    if (transition == null) {
-        return true
-    }
+    val transition =
+        when (elementState) {
+            is TransitionState.Idle -> {
+                return content ==
+                    elementContentWhenIdle(
+                        layoutImpl,
+                        elementState,
+                        isInContent = { it in element.stateByContent },
+                    )
+            }
+            is TransitionState.Transition -> elementState
+        }
 
     // Don't place the element in this content if this content is not part of the current element
     // transition.
@@ -725,47 +853,56 @@
         content,
         element.key,
         transition,
+        isInContent = { it in element.stateByContent },
     )
 }
 
-internal fun shouldPlaceOrComposeSharedElement(
+internal inline fun shouldPlaceOrComposeSharedElement(
     layoutImpl: SceneTransitionLayoutImpl,
     content: ContentKey,
     element: ElementKey,
-    transition: ContentState.Transition<*>,
+    transition: TransitionState.Transition,
+    isInContent: (ContentKey) -> Boolean,
 ): Boolean {
-    // If we are overscrolling, only place/compose the element in the overscrolling scene.
-    val overscrollScene = transition.currentOverscrollSpec?.scene
-    if (overscrollScene != null) {
-        return content == overscrollScene
+    val overscrollContent = transition.currentOverscrollSpec?.content
+    if (overscrollContent != null) {
+        return when (transition) {
+            // If we are overscrolling between scenes, only place/compose the element in the
+            // overscrolling scene.
+            is TransitionState.Transition.ChangeScene -> content == overscrollContent
+
+            // If we are overscrolling an overlay, place/compose the element if [content] is the
+            // overscrolling content or if [content] is the current scene and the overscrolling
+            // overlay does not contain the element.
+            is TransitionState.Transition.ReplaceOverlay,
+            is TransitionState.Transition.ShowOrHideOverlay ->
+                content == overscrollContent ||
+                    (content == transition.currentScene && !isInContent(overscrollContent))
+        }
     }
 
     val scenePicker = element.contentPicker
     val pickedScene =
-        when (transition) {
-            is TransitionState.Transition -> {
-                scenePicker.contentDuringTransition(
-                    element = element,
-                    transition = transition,
-                    fromContentZIndex = layoutImpl.scene(transition.fromScene).zIndex,
-                    toContentZIndex = layoutImpl.scene(transition.toScene).zIndex,
-                )
-            }
-        }
+        scenePicker.contentDuringTransition(
+            element = element,
+            transition = transition,
+            fromContentZIndex = layoutImpl.content(transition.fromContent).zIndex,
+            toContentZIndex = layoutImpl.content(transition.toContent).zIndex,
+        )
 
     return pickedScene == content
 }
 
 private fun isSharedElementEnabled(
     element: ElementKey,
-    transition: ContentState.Transition<*>,
+    transition: TransitionState.Transition,
 ): Boolean {
     return sharedElementTransformation(element, transition)?.enabled ?: true
 }
 
 internal fun sharedElementTransformation(
     element: ElementKey,
-    transition: ContentState.Transition<*>,
+    transition: TransitionState.Transition,
 ): SharedElementTransformation? {
     val transformationSpec = transition.transformationSpec
     val sharedInFromContent =
@@ -793,7 +930,7 @@
 private fun isElementOpaque(
     content: Content,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
 ): Boolean {
     if (transition == null) {
         return true
@@ -827,7 +964,7 @@
 private fun elementAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     stateInContent: Element.State,
 ): Float {
     val alpha =
@@ -858,7 +995,7 @@
 private fun interruptedAlpha(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     stateInContent: Element.State,
     alpha: Float,
 ): Float {
@@ -888,7 +1025,7 @@
 private fun measure(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     stateInContent: Element.State,
     measurable: Measurable,
     constraints: Constraints,
@@ -952,7 +1089,7 @@
 private fun ContentDrawScope.getDrawScale(
     layoutImpl: SceneTransitionLayoutImpl,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     stateInContent: Element.State,
 ): Scale {
     val scale =
@@ -1048,7 +1185,7 @@
     layoutImpl: SceneTransitionLayoutImpl,
     currentContentState: Element.State,
     element: Element,
-    transition: ContentState.Transition<*>?,
+    transition: TransitionState.Transition?,
     contentValue: (Element.State) -> T,
     transformation: (ElementTransformations) -> PropertyTransformation<T>?,
     currentValue: () -> T,
@@ -1076,9 +1213,9 @@
     }
 
     val currentContent = currentContentState.content
-    if (transition is ContentState.HasOverscrollProperties) {
+    if (transition is TransitionState.HasOverscrollProperties) {
         val overscroll = transition.currentOverscrollSpec
-        if (overscroll?.scene == currentContent) {
+        if (overscroll?.content == currentContent) {
             val elementSpec =
                 overscroll.transformationSpec.transformations(element.key, currentContent)
             val propertySpec = transformation(elementSpec) ?: return currentValue()
@@ -1104,9 +1241,12 @@
             // TODO(b/290184746): Make sure that we don't overflow transformations associated to a
             // range.
             val directionSign = if (transition.isUpOrLeft) -1 else 1
-            val isToContent = overscroll.scene == transition.toContent
+            val isToContent = overscroll.content == transition.toContent
             val linearProgress = transition.progress.let { if (isToContent) it - 1f else it }
-            val progress = directionSign * overscroll.progressConverter(linearProgress)
+            val progressConverter =
+                overscroll.progressConverter
+                    ?: layoutImpl.state.transitions.defaultProgressConverter
+            val progress = directionSign * progressConverter.convert(linearProgress)
             val rangeProgress = propertySpec.range?.progress(progress) ?: progress
 
             // Interpolate between the value at rest and the over scrolled value.
@@ -1120,17 +1260,30 @@
     // elements follow the finger direction.
     val isSharedElement = fromState != null && toState != null
     if (isSharedElement && isSharedElementEnabled(element.key, transition)) {
-        val start = contentValue(fromState!!)
-        val end = contentValue(toState!!)
+        return interpolateSharedElement(
+            transition = transition,
+            contentValue = contentValue,
+            fromState = fromState!!,
+            toState = toState!!,
+            isSpecified = isSpecified,
+            lerp = lerp,
+        )
+    }
 
-        // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
-        // nodes before the intermediate layout pass.
-        if (!isSpecified(start)) return end
-        if (!isSpecified(end)) return start
-
-        // Make sure we don't read progress if values are the same and we don't need to interpolate,
-        // so we don't invalidate the phase where this is read.
-        return if (start == end) start else lerp(start, end, transition.progress)
+    // If we are replacing an overlay and the element is both in a single overlay and in the current
+    // scene, interpolate the state of the element using the current scene as the other scene.
+    if (!isSharedElement && transition is TransitionState.Transition.ReplaceOverlay) {
+        val currentSceneState = element.stateByContent[transition.currentScene]
+        if (currentSceneState != null) {
+            return interpolateSharedElement(
+                transition = transition,
+                contentValue = contentValue,
+                fromState = fromState ?: currentSceneState,
+                toState = toState ?: currentSceneState,
+                isSpecified = isSpecified,
+                lerp = lerp,
+            )
+        }
     }
 
     // Get the transformed value, i.e. the target value at the beginning (for entering elements) or
@@ -1273,3 +1426,24 @@
         lerp(idleValue, targetValue, rangeProgress)
     }
 }
+
+private inline fun <T> interpolateSharedElement(
+    transition: TransitionState.Transition,
+    contentValue: (Element.State) -> T,
+    fromState: Element.State,
+    toState: Element.State,
+    isSpecified: (T) -> Boolean,
+    lerp: (T, T, Float) -> T
+): T {
+    val start = contentValue(fromState)
+    val end = contentValue(toState)
+
+    // TODO(b/316901148): Remove checks to isSpecified() once the lookahead pass runs for all
+    // nodes before the intermediate layout pass.
+    if (!isSpecified(start)) return end
+    if (!isSpecified(end)) return start
+
+    // Make sure we don't read progress if values are the same and we don't need to interpolate,
+    // so we don't invalidate the phase where this is read.
+    return if (start == end) start else lerp(start, end, transition.progress)
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
index bf70ca9..cb18c67 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/InterruptionHandler.kt
@@ -37,7 +37,7 @@
      * @see InterruptionResult
      */
     fun onInterruption(
-        interrupted: TransitionState.Transition,
+        interrupted: TransitionState.Transition.ChangeScene,
         newTargetScene: SceneKey,
     ): InterruptionResult?
 }
@@ -76,7 +76,7 @@
  */
 object DefaultInterruptionHandler : InterruptionHandler {
     override fun onInterruption(
-        interrupted: TransitionState.Transition,
+        interrupted: TransitionState.Transition.ChangeScene,
         newTargetScene: SceneKey,
     ): InterruptionResult {
         return InterruptionResult(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
index acb436e..ced177c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Key.kt
@@ -63,6 +63,18 @@
     }
 }
 
+/** Key for an overlay. */
+class OverlayKey(
+    debugName: String,
+    identity: Any = Object(),
+) : ContentKey(debugName, identity) {
+    override val testTag: String = "overlay:$debugName"
+
+    override fun toString(): String {
+        return "OverlayKey(debugName=$debugName)"
+    }
+}
+
 /** Key for an element. */
 open class ElementKey(
     debugName: String,
@@ -141,4 +153,15 @@
     override fun toString(): String {
         return "TransitionKey(debugName=$debugName)"
     }
+
+    companion object {
+        /**
+         * A special transition key indicating that the associated transition should be used for
+         * Predictive Back gestures.
+         *
+         * Use this key when defining a transition that you want to be specifically triggered when
+         * the user performs a Predictive Back gesture.
+         */
+        val PredictiveBack = TransitionKey("PredictiveBack")
+    }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index abecdd7..471ad3f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -26,7 +26,6 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.Layout
 import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.util.fastLastOrNull
 import com.android.compose.animation.scene.content.Content
 import com.android.compose.animation.scene.content.state.TransitionState
 
@@ -58,6 +57,13 @@
     modifier: Modifier,
     content: @Composable ElementScope<MovableElementContentScope>.() -> Unit,
 ) {
+    check(key.contentPicker.contents.contains(sceneOrOverlay.key)) {
+        val elementName = key.debugName
+        val contentName = sceneOrOverlay.key.debugName
+        "MovableElement $elementName was composed in content $contentName but the " +
+            "MovableElementKey($elementName).contentPicker.contents does not contain $contentName"
+    }
+
     Box(modifier.element(layoutImpl, sceneOrOverlay, key)) {
         val contentScope = sceneOrOverlay.scope
         val boxScope = this
@@ -153,13 +159,20 @@
             // size* as its movable content, i.e. the same *size when idle*. During transitions,
             // this size will be used to interpolate the transition size, during the intermediate
             // layout pass.
+            //
+            // Important: Like in Modifier.element(), we read the transition states during
+            // composition then pass them to Layout to make sure that composition sees new states
+            // before layout and drawing.
+            val transitionStates = layoutImpl.state.transitionStates
             Layout { _, _ ->
                 // No need to measure or place anything.
                 val size =
                     placeholderContentSize(
-                        layoutImpl,
-                        contentKey,
-                        layoutImpl.elements.getValue(element),
+                        layoutImpl = layoutImpl,
+                        content = contentKey,
+                        element = layoutImpl.elements.getValue(element),
+                        elementKey = element,
+                        transitionStates = transitionStates,
                     )
                 layout(size.width, size.height) {}
             }
@@ -172,28 +185,45 @@
     content: ContentKey,
     element: MovableElementKey,
 ): Boolean {
-    val transitions = layoutImpl.state.currentTransitions
-    if (transitions.isEmpty()) {
-        // If we are idle, there is only one [scene] that is composed so we can compose our
-        // movable content here. We still check that [scene] is equal to the current idle scene, to
-        // make sure we only compose it there.
-        return layoutImpl.state.transitionState.currentScene == content
+    return when (
+        val elementState = movableElementState(element, layoutImpl.state.transitionStates)
+    ) {
+        null -> false
+        is TransitionState.Idle ->
+            movableElementContentWhenIdle(layoutImpl, element, elementState) == content
+        is TransitionState.Transition -> {
+            // During transitions, always compose movable elements in the scene picked by their
+            // content picker.
+            val contents = element.contentPicker.contents
+            shouldPlaceOrComposeSharedElement(
+                layoutImpl,
+                content,
+                element,
+                elementState,
+                isInContent = { contents.contains(it) }
+            )
+        }
     }
+}
 
-    // The current transition for this element is the last transition in which either fromScene or
-    // toScene contains the element.
+private fun movableElementState(
+    element: MovableElementKey,
+    transitionStates: List<TransitionState>,
+): TransitionState? {
     val contents = element.contentPicker.contents
-    val transition =
-        transitions.fastLastOrNull { transition ->
-            transition.fromScene in contents || transition.toScene in contents
-        } ?: return false
+    return elementState(transitionStates, isInContent = { contents.contains(it) })
+}
 
-    // Always compose movable elements in the scene picked by their scene picker.
-    return shouldPlaceOrComposeSharedElement(
+private fun movableElementContentWhenIdle(
+    layoutImpl: SceneTransitionLayoutImpl,
+    element: MovableElementKey,
+    elementState: TransitionState.Idle,
+): ContentKey {
+    val contents = element.contentPicker.contents
+    return elementContentWhenIdle(
         layoutImpl,
-        content,
-        element,
-        transition,
+        elementState,
+        isInContent = { contents.contains(it) },
     )
 }
 
@@ -205,6 +235,8 @@
     layoutImpl: SceneTransitionLayoutImpl,
     content: ContentKey,
     element: Element,
+    elementKey: MovableElementKey,
+    transitionStates: List<TransitionState>,
 ): IntSize {
     // If the content of the movable element was already composed in this scene before, use that
     // target size.
@@ -213,19 +245,21 @@
         return targetValueInScene
     }
 
-    // This code is only run during transitions (otherwise the content would be composed and the
-    // placeholder would not), so it's ok to cast the state into a Transition directly.
-    val transition = layoutImpl.state.transitionState as TransitionState.Transition
+    // If the element content was already composed in the other overlay/scene, we use that
+    // target size assuming it doesn't change between scenes.
+    // TODO(b/317026105): Provide a way to give a hint size/content for cases where this is
+    // not true.
+    val otherContent =
+        when (val state = movableElementState(elementKey, transitionStates)) {
+            null -> return IntSize.Zero
+            is TransitionState.Idle -> movableElementContentWhenIdle(layoutImpl, elementKey, state)
+            is TransitionState.Transition ->
+                if (state.fromContent == content) state.toContent else state.fromContent
+        }
 
-    // If the content was already composed in the other scene, we use that target size assuming it
-    // doesn't change between scenes.
-    // TODO(b/317026105): Provide a way to give a hint size/content for cases where this is not
-    // true.
-    val otherScene =
-        if (transition.fromScene == content) transition.toScene else transition.fromScene
-    val targetValueInOtherScene = element.stateByContent[otherScene]?.targetSize
-    if (targetValueInOtherScene != null && targetValueInOtherScene != Element.SizeUnspecified) {
-        return targetValueInOtherScene
+    val targetValueInOtherContent = element.stateByContent[otherContent]?.targetSize
+    if (targetValueInOtherContent != null && targetValueInOtherContent != Element.SizeUnspecified) {
+        return targetValueInOtherContent
     }
 
     return IntSize.Zero
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 b329534..0d05f4e 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
@@ -56,7 +56,7 @@
 import com.android.compose.ui.util.SpaceVectorConverter
 import kotlin.coroutines.cancellation.CancellationException
 import kotlin.math.sign
-import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
 
@@ -81,6 +81,7 @@
     enabled: () -> Boolean,
     startDragImmediately: (startedPosition: Offset) -> Boolean,
     onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    onFirstPointerDown: () -> Unit = {},
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
     dispatcher: NestedScrollDispatcher,
 ): Modifier =
@@ -90,6 +91,7 @@
             enabled,
             startDragImmediately,
             onDragStarted,
+            onFirstPointerDown,
             swipeDetector,
             dispatcher,
         )
@@ -101,6 +103,7 @@
     private val startDragImmediately: (startedPosition: Offset) -> Boolean,
     private val onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    private val onFirstPointerDown: () -> Unit,
     private val swipeDetector: SwipeDetector,
     private val dispatcher: NestedScrollDispatcher,
 ) : ModifierNodeElement<MultiPointerDraggableNode>() {
@@ -110,6 +113,7 @@
             enabled = enabled,
             startDragImmediately = startDragImmediately,
             onDragStarted = onDragStarted,
+            onFirstPointerDown = onFirstPointerDown,
             swipeDetector = swipeDetector,
             dispatcher = dispatcher,
         )
@@ -119,6 +123,7 @@
         node.enabled = enabled
         node.startDragImmediately = startDragImmediately
         node.onDragStarted = onDragStarted
+        node.onFirstPointerDown = onFirstPointerDown
         node.swipeDetector = swipeDetector
     }
 }
@@ -129,6 +134,7 @@
     var startDragImmediately: (startedPosition: Offset) -> Boolean,
     var onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    var onFirstPointerDown: () -> Unit,
     var swipeDetector: SwipeDetector = DefaultSwipeDetector,
     private val dispatcher: NestedScrollDispatcher,
 ) :
@@ -137,8 +143,8 @@
     CompositionLocalConsumerModifierNode,
     ObserverModifierNode,
     SpaceVectorConverter {
-    private val pointerInputHandler: suspend PointerInputScope.() -> Unit = { pointerInput() }
-    private val delegate = delegate(SuspendingPointerInputModifierNode(pointerInputHandler))
+    private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
+    private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
     private val velocityTracker = VelocityTracker()
     private var previousEnabled: Boolean = false
 
@@ -147,7 +153,7 @@
             // Reset the pointer input whenever enabled changed.
             if (value != field) {
                 field = value
-                delegate.resetPointerInputHandler()
+                pointerInput.resetPointerInputHandler()
             }
         }
 
@@ -167,7 +173,7 @@
             if (value != field) {
                 field = value
                 converter = SpaceVectorConverter(value)
-                delegate.resetPointerInputHandler()
+                pointerInput.resetPointerInputHandler()
             }
         }
 
@@ -180,19 +186,26 @@
         observeReads {
             val newEnabled = enabled()
             if (newEnabled != previousEnabled) {
-                delegate.resetPointerInputHandler()
+                pointerInput.resetPointerInputHandler()
             }
             previousEnabled = newEnabled
         }
     }
 
-    override fun onCancelPointerInput() = delegate.onCancelPointerInput()
+    override fun onCancelPointerInput() {
+        pointerTracker.onCancelPointerInput()
+        pointerInput.onCancelPointerInput()
+    }
 
     override fun onPointerEvent(
         pointerEvent: PointerEvent,
         pass: PointerEventPass,
         bounds: IntSize
-    ) = delegate.onPointerEvent(pointerEvent, pass, bounds)
+    ) {
+        // The order is important here: the tracker is always called first.
+        pointerTracker.onPointerEvent(pointerEvent, pass, bounds)
+        pointerInput.onPointerEvent(pointerEvent, pass, bounds)
+    }
 
     private var startedPosition: Offset? = null
     private var pointersDown: Int = 0
@@ -205,80 +218,115 @@
         )
     }
 
+    private suspend fun PointerInputScope.pointerTracker() {
+        val currentContext = currentCoroutineContext()
+        awaitPointerEventScope {
+            var velocityPointerId: PointerId? = null
+            // Intercepts pointer inputs and exposes [PointersInfo], via
+            // [requireAncestorPointersInfoOwner], to our descendants.
+            while (currentContext.isActive) {
+                // During the Initial pass, we receive the event after our ancestors.
+                val changes = awaitPointerEvent(PointerEventPass.Initial).changes
+                pointersDown = changes.countDown()
+
+                when {
+                    // There are no more pointers down.
+                    pointersDown == 0 -> {
+                        startedPosition = null
+
+                        // This is the last pointer up
+                        velocityTracker.addPointerInputChange(changes.single())
+                    }
+
+                    // The first pointer down, startedPosition was not set.
+                    startedPosition == null -> {
+                        val firstPointerDown = changes.single()
+                        velocityPointerId = firstPointerDown.id
+                        velocityTracker.resetTracking()
+                        velocityTracker.addPointerInputChange(firstPointerDown)
+                        startedPosition = firstPointerDown.position
+                        if (enabled()) {
+                            onFirstPointerDown()
+                        }
+                    }
+
+                    // Changes with at least one pointer
+                    else -> {
+                        val pointerChange = changes.first()
+
+                        // Assuming that the list of changes doesn't have two changes with the same
+                        // id (PointerId), we can check:
+                        // - If the first change has `id` equals to `velocityPointerId` (this should
+                        //   always be true unless the pointer has been removed).
+                        // - If it does, we've found our change event (assuming there aren't any
+                        //   others changes with the same id in this PointerEvent - not checked).
+                        // - If it doesn't, we can check that the change with that id isn't in first
+                        //   place (which should never happen - this will crash).
+                        check(
+                            pointerChange.id == velocityPointerId ||
+                                !changes.fastAny { it.id == velocityPointerId }
+                        ) {
+                            "$velocityPointerId is present, but not the first: $changes"
+                        }
+
+                        // If the previous pointer has been removed, we use the first available
+                        // change to keep tracking the velocity.
+                        velocityPointerId = pointerChange.id
+
+                        velocityTracker.addPointerInputChange(pointerChange)
+                    }
+                }
+            }
+        }
+    }
+
     private suspend fun PointerInputScope.pointerInput() {
         if (!enabled()) {
             return
         }
 
-        coroutineScope {
-            launch {
-                // Intercepts pointer inputs and exposes [PointersInfo], via
-                // [requireAncestorPointersInfoOwner], to our descendants.
-                awaitPointerEventScope {
-                    while (isActive) {
-                        // During the Initial pass, we receive the event after our ancestors.
-                        val pointers = awaitPointerEvent(PointerEventPass.Initial).changes
-
-                        pointersDown = pointers.countDown()
-                        if (pointersDown == 0) {
-                            // There are no more pointers down
-                            startedPosition = null
-                        } else if (startedPosition == null) {
-                            startedPosition = pointers.first().position
-                        }
-                    }
-                }
-            }
-
-            // The order is important here: we want to make sure that the previous PointerEventScope
-            // is initialized first. This ensures that the following PointerEventScope doesn't
-            // receive more events than the first one.
-            launch {
-                awaitPointerEventScope {
-                    while (isActive) {
-                        try {
-                            detectDragGestures(
-                                orientation = orientation,
-                                startDragImmediately = startDragImmediately,
-                                onDragStart = { startedPosition, overSlop, pointersDown ->
-                                    velocityTracker.resetTracking()
-                                    onDragStarted(startedPosition, overSlop, pointersDown)
-                                },
-                                onDrag = { controller, change, amount ->
-                                    velocityTracker.addPointerInputChange(change)
-                                    dispatchScrollEvents(
-                                        availableOnPreScroll = amount,
-                                        onScroll = { controller.onDrag(it) },
-                                        source = NestedScrollSource.UserInput,
-                                    )
-                                },
-                                onDragEnd = { controller ->
-                                    startFlingGesture(
-                                        initialVelocity =
-                                            currentValueOf(LocalViewConfiguration)
-                                                .maximumFlingVelocity
-                                                .let {
-                                                    val maxVelocity = Velocity(it, it)
-                                                    velocityTracker.calculateVelocity(maxVelocity)
-                                                }
-                                                .toFloat(),
-                                        onFling = { controller.onStop(it, canChangeScene = true) }
-                                    )
-                                },
-                                onDragCancel = { controller ->
-                                    startFlingGesture(
-                                        initialVelocity = 0f,
-                                        onFling = { controller.onStop(it, canChangeScene = true) }
-                                    )
-                                },
-                                swipeDetector = swipeDetector,
+        val currentContext = currentCoroutineContext()
+        awaitPointerEventScope {
+            while (currentContext.isActive) {
+                try {
+                    detectDragGestures(
+                        orientation = orientation,
+                        startDragImmediately = startDragImmediately,
+                        onDragStart = { startedPosition, overSlop, pointersDown ->
+                            onDragStarted(startedPosition, overSlop, pointersDown)
+                        },
+                        onDrag = { controller, amount ->
+                            dispatchScrollEvents(
+                                availableOnPreScroll = amount,
+                                onScroll = { controller.onDrag(it) },
+                                source = NestedScrollSource.UserInput,
                             )
-                        } catch (exception: CancellationException) {
-                            // If the coroutine scope is active, we can just restart the drag cycle.
-                            if (!isActive) {
-                                throw exception
-                            }
-                        }
+                        },
+                        onDragEnd = { controller ->
+                            startFlingGesture(
+                                initialVelocity =
+                                    currentValueOf(LocalViewConfiguration)
+                                        .maximumFlingVelocity
+                                        .let {
+                                            val maxVelocity = Velocity(it, it)
+                                            velocityTracker.calculateVelocity(maxVelocity)
+                                        }
+                                        .toFloat(),
+                                onFling = { controller.onStop(it, canChangeContent = true) }
+                            )
+                        },
+                        onDragCancel = { controller ->
+                            startFlingGesture(
+                                initialVelocity = 0f,
+                                onFling = { controller.onStop(it, canChangeContent = true) }
+                            )
+                        },
+                        swipeDetector = swipeDetector,
+                    )
+                } catch (exception: CancellationException) {
+                    // If the coroutine scope is active, we can just restart the drag cycle.
+                    if (!currentContext.isActive) {
+                        throw exception
                     }
                 }
             }
@@ -393,7 +441,7 @@
         startDragImmediately: (startedPosition: Offset) -> Boolean,
         onDragStart:
             (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
-        onDrag: (controller: DragController, change: PointerInputChange, dragAmount: Float) -> Unit,
+        onDrag: (controller: DragController, dragAmount: Float) -> Unit,
         onDragEnd: (controller: DragController) -> Unit,
         onDragCancel: (controller: DragController) -> Unit,
         swipeDetector: SwipeDetector,
@@ -472,14 +520,14 @@
 
             val successful: Boolean
             try {
-                onDrag(controller, drag, overSlop)
+                onDrag(controller, overSlop)
 
                 successful =
                     drag(
                         initialPointerId = drag.id,
                         hasDragged = { it.positionChangeIgnoreConsumed().toFloat() != 0f },
                         onDrag = {
-                            onDrag(controller, it, it.positionChange().toFloat())
+                            onDrag(controller, it.positionChange().toFloat())
                             it.consume()
                         },
                         onIgnoredEvent = {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 945043d..8a0e462 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -35,13 +35,6 @@
  */
 enum class NestedScrollBehavior(val canStartOnPostFling: Boolean) {
     /**
-     * During scene transitions, if we are within
-     * [SceneTransitionLayoutImpl.transitionInterceptionThreshold], the [SceneTransitionLayout]
-     * consumes scroll events instead of the scrollable component.
-     */
-    DuringTransitionBetweenScenes(canStartOnPostFling = false),
-
-    /**
      * Overscroll will only be used by the [SceneTransitionLayout] to move to the next scene if the
      * gesture begins at the edge of the scrollable component (so that a scroll in that direction
      * can no longer be consumed). If the gesture is partially consumed by the scrollable component,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index ae5344f..8ae3a11 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -31,9 +31,6 @@
  *    layout or Compose drawing phases.
  * 2. [ObservableTransitionState] values are backed by Kotlin [Flow]s and can be collected by
  *    non-Compose code to observe value changes.
- * 3. [ObservableTransitionState.Transition.fromScene] and
- *    [ObservableTransitionState.Transition.toScene] will never be equal, while
- *    [TransitionState.Transition.fromScene] and [TransitionState.Transition.toScene] can be equal.
  */
 sealed interface ObservableTransitionState {
     /**
@@ -43,18 +40,33 @@
     fun currentScene(): Flow<SceneKey> {
         return when (this) {
             is Idle -> flowOf(currentScene)
-            is Transition -> currentScene
+            is Transition.ChangeScene -> currentScene
+            is Transition.ShowOrHideOverlay -> flowOf(currentScene)
+            is Transition.ReplaceOverlay -> flowOf(currentScene)
+        }
+    }
+
+    /** The current overlays. */
+    fun currentOverlays(): Flow<Set<OverlayKey>> {
+        return when (this) {
+            is Idle -> flowOf(currentOverlays)
+            is Transition -> currentOverlays
         }
     }
 
     /** No transition/animation is currently running. */
-    data class Idle(val currentScene: SceneKey) : ObservableTransitionState
+    data class Idle
+    @JvmOverloads
+    constructor(
+        val currentScene: SceneKey,
+        val currentOverlays: Set<OverlayKey> = emptySet(),
+    ) : ObservableTransitionState
 
     /** There is a transition animating between two scenes. */
-    class Transition(
-        val fromScene: SceneKey,
-        val toScene: SceneKey,
-        val currentScene: Flow<SceneKey>,
+    sealed class Transition(
+        val fromContent: ContentKey,
+        val toContent: ContentKey,
+        val currentOverlays: Flow<Set<OverlayKey>>,
         val progress: Flow<Float>,
 
         /**
@@ -74,25 +86,127 @@
          * the transition completes/settles.
          */
         val isUserInputOngoing: Flow<Boolean>,
+
+        /** Current progress of the preview part of the transition */
+        val previewProgress: Flow<Float>,
+
+        /** Whether the transition is currently in the preview stage or not */
+        val isInPreviewStage: Flow<Boolean>,
     ) : ObservableTransitionState {
         override fun toString(): String =
             """Transition
-                |(from=$fromScene,
-                | to=$toScene,
+                |(from=$fromContent,
+                | to=$toContent,
                 | isInitiatedByUserInput=$isInitiatedByUserInput,
                 | isUserInputOngoing=$isUserInputOngoing
                 |)"""
                 .trimMargin()
+
+        /** A transition animating between [fromScene] and [toScene]. */
+        class ChangeScene(
+            val fromScene: SceneKey,
+            val toScene: SceneKey,
+            val currentScene: Flow<SceneKey>,
+            currentOverlays: Set<OverlayKey>,
+            progress: Flow<Float>,
+            isInitiatedByUserInput: Boolean,
+            isUserInputOngoing: Flow<Boolean>,
+            previewProgress: Flow<Float>,
+            isInPreviewStage: Flow<Boolean>,
+        ) :
+            Transition(
+                fromScene,
+                toScene,
+                flowOf(currentOverlays),
+                progress,
+                isInitiatedByUserInput,
+                isUserInputOngoing,
+                previewProgress,
+                isInPreviewStage,
+            )
+
+        /** The [overlay] is either showing from [currentScene] or hiding into [currentScene]. */
+        class ShowOrHideOverlay(
+            val overlay: OverlayKey,
+            fromContent: ContentKey,
+            toContent: ContentKey,
+            val currentScene: SceneKey,
+            currentOverlays: Flow<Set<OverlayKey>>,
+            progress: Flow<Float>,
+            isInitiatedByUserInput: Boolean,
+            isUserInputOngoing: Flow<Boolean>,
+            previewProgress: Flow<Float>,
+            isInPreviewStage: Flow<Boolean>,
+        ) :
+            Transition(
+                fromContent,
+                toContent,
+                currentOverlays,
+                progress,
+                isInitiatedByUserInput,
+                isUserInputOngoing,
+                previewProgress,
+                isInPreviewStage,
+            )
+
+        /** We are transitioning from [fromOverlay] to [toOverlay]. */
+        class ReplaceOverlay(
+            val fromOverlay: OverlayKey,
+            val toOverlay: OverlayKey,
+            val currentScene: SceneKey,
+            currentOverlays: Flow<Set<OverlayKey>>,
+            progress: Flow<Float>,
+            isInitiatedByUserInput: Boolean,
+            isUserInputOngoing: Flow<Boolean>,
+            previewProgress: Flow<Float>,
+            isInPreviewStage: Flow<Boolean>,
+        ) :
+            Transition(
+                fromOverlay,
+                toOverlay,
+                currentOverlays,
+                progress,
+                isInitiatedByUserInput,
+                isUserInputOngoing,
+                previewProgress,
+                isInPreviewStage,
+            )
+
+        companion object {
+            operator fun invoke(
+                fromScene: SceneKey,
+                toScene: SceneKey,
+                currentScene: Flow<SceneKey>,
+                progress: Flow<Float>,
+                isInitiatedByUserInput: Boolean,
+                isUserInputOngoing: Flow<Boolean>,
+                previewProgress: Flow<Float> = flowOf(0f),
+                isInPreviewStage: Flow<Boolean> = flowOf(false),
+                currentOverlays: Set<OverlayKey> = emptySet(),
+            ): ChangeScene {
+                return ChangeScene(
+                    fromScene,
+                    toScene,
+                    currentScene,
+                    currentOverlays,
+                    progress,
+                    isInitiatedByUserInput,
+                    isUserInputOngoing,
+                    previewProgress,
+                    isInPreviewStage,
+                )
+            }
+        }
     }
 
-    fun isIdle(scene: SceneKey?): Boolean {
+    fun isIdle(scene: SceneKey? = null): Boolean {
         return this is Idle && (scene == null || this.currentScene == scene)
     }
 
-    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+    fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
         return this is Transition &&
-            (from == null || this.fromScene == from) &&
-            (to == null || this.toScene == to)
+            (from == null || this.fromContent == from) &&
+            (to == null || this.toContent == to)
     }
 }
 
@@ -104,15 +218,50 @@
 fun SceneTransitionLayoutState.observableTransitionState(): Flow<ObservableTransitionState> {
     return snapshotFlow {
             when (val state = transitionState) {
-                is TransitionState.Idle -> ObservableTransitionState.Idle(state.currentScene)
-                is TransitionState.Transition -> {
-                    ObservableTransitionState.Transition(
+                is TransitionState.Idle ->
+                    ObservableTransitionState.Idle(
+                        state.currentScene,
+                        state.currentOverlays,
+                    )
+                is TransitionState.Transition.ChangeScene -> {
+                    ObservableTransitionState.Transition.ChangeScene(
                         fromScene = state.fromScene,
                         toScene = state.toScene,
                         currentScene = snapshotFlow { state.currentScene },
+                        currentOverlays = state.currentOverlays,
                         progress = snapshotFlow { state.progress },
                         isInitiatedByUserInput = state.isInitiatedByUserInput,
                         isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+                        previewProgress = snapshotFlow { state.previewProgress },
+                        isInPreviewStage = snapshotFlow { state.isInPreviewStage },
+                    )
+                }
+                is TransitionState.Transition.ShowOrHideOverlay -> {
+                    check(state.fromOrToScene == state.currentScene)
+                    ObservableTransitionState.Transition.ShowOrHideOverlay(
+                        overlay = state.overlay,
+                        fromContent = state.fromContent,
+                        toContent = state.toContent,
+                        currentScene = state.currentScene,
+                        currentOverlays = snapshotFlow { state.currentOverlays },
+                        progress = snapshotFlow { state.progress },
+                        isInitiatedByUserInput = state.isInitiatedByUserInput,
+                        isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+                        previewProgress = snapshotFlow { state.previewProgress },
+                        isInPreviewStage = snapshotFlow { state.isInPreviewStage },
+                    )
+                }
+                is TransitionState.Transition.ReplaceOverlay -> {
+                    ObservableTransitionState.Transition.ReplaceOverlay(
+                        fromOverlay = state.fromOverlay,
+                        toOverlay = state.toOverlay,
+                        currentScene = state.currentScene,
+                        currentOverlays = snapshotFlow { state.currentOverlays },
+                        progress = snapshotFlow { state.progress },
+                        isInitiatedByUserInput = state.isInitiatedByUserInput,
+                        isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
+                        previewProgress = snapshotFlow { state.previewProgress },
+                        isInPreviewStage = snapshotFlow { state.isInPreviewStage },
                     )
                 }
             }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
index fe16ef751..8480d3a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/PredictiveBackHandler.kt
@@ -18,120 +18,71 @@
 
 import androidx.activity.BackEventCompat
 import androidx.activity.compose.PredictiveBackHandler
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.snap
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.setValue
-import com.android.compose.animation.scene.content.state.TransitionState
-import kotlin.coroutines.cancellation.CancellationException
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
+import com.android.compose.animation.scene.UserActionResult.ChangeScene
+import com.android.compose.animation.scene.UserActionResult.HideOverlay
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
+import com.android.compose.animation.scene.UserActionResult.ShowOverlay
+import com.android.compose.animation.scene.transition.animateProgress
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.map
 
 @Composable
 internal fun PredictiveBackHandler(
-    state: MutableSceneTransitionLayoutStateImpl,
-    coroutineScope: CoroutineScope,
-    targetSceneForBack: SceneKey? = null,
+    layoutImpl: SceneTransitionLayoutImpl,
+    result: UserActionResult?,
 ) {
     PredictiveBackHandler(
-        enabled = targetSceneForBack != null,
-    ) { progress: Flow<BackEventCompat> ->
-        val fromScene = state.transitionState.currentScene
-        if (targetSceneForBack == null || targetSceneForBack == fromScene) {
+        enabled = result != null,
+    ) { events: Flow<BackEventCompat> ->
+        if (result == null) {
             // Note: We have to collect progress otherwise PredictiveBackHandler will throw.
-            progress.first()
+            events.first()
             return@PredictiveBackHandler
         }
 
-        val transition =
-            PredictiveBackTransition(state, coroutineScope, fromScene, toScene = targetSceneForBack)
-        state.startTransition(transition)
-        try {
-            progress.collect { backEvent -> transition.dragProgress = backEvent.progress }
+        val animation =
+            createSwipeAnimation(
+                layoutImpl,
+                if (result.transitionKey != null) {
+                    result
+                } else {
+                    result.copy(transitionKey = TransitionKey.PredictiveBack)
+                },
+                isUpOrLeft = false,
+                // Note that the orientation does not matter here given that it's only used to
+                // compute the distance. In our case the distance is always 1f.
+                orientation = Orientation.Horizontal,
+                distance = 1f,
+            )
 
-            // Back gesture successful.
-            transition.animateTo(targetSceneForBack)
-        } catch (e: CancellationException) {
-            // Back gesture cancelled.
-            transition.animateTo(fromScene)
-        }
+        animateProgress(
+            state = layoutImpl.state,
+            animation = animation,
+            progress = events.map { it.progress },
+
+            // Use the transformationSpec.progressSpec. We will lazily access it later once the
+            // transition has been started, because at this point the transformation spec of the
+            // transition is not computed yet.
+            commitSpec = null,
+
+            // The predictive back APIs will automatically animate the progress for us in this case
+            // so there is no need to animate it.
+            cancelSpec = snap(),
+        )
     }
 }
 
-private class PredictiveBackTransition(
-    val state: MutableSceneTransitionLayoutStateImpl,
-    val coroutineScope: CoroutineScope,
-    fromScene: SceneKey,
-    toScene: SceneKey,
-) : TransitionState.Transition(fromScene, toScene) {
-    override var currentScene by mutableStateOf(fromScene)
-        private set
-
-    /** The animated progress once the gesture was committed or cancelled. */
-    private var progressAnimatable by mutableStateOf<Animatable<Float, AnimationVector1D>?>(null)
-    var dragProgress: Float by mutableFloatStateOf(0f)
-
-    override val previewProgress: Float
-        get() = dragProgress
-
-    override val previewProgressVelocity: Float
-        get() = 0f // Currently, velocity is not exposed by predictive back API
-
-    override val isInPreviewStage: Boolean
-        get() = progressAnimatable == null && previewTransformationSpec != null
-
-    override val progress: Float
-        get() = progressAnimatable?.value ?: previewTransformationSpec?.let { 0f } ?: dragProgress
-
-    override val progressVelocity: Float
-        get() = progressAnimatable?.velocity ?: 0f
-
-    override val isInitiatedByUserInput: Boolean
-        get() = true
-
-    override val isUserInputOngoing: Boolean
-        get() = progressAnimatable == null
-
-    private var animationJob: Job? = null
-
-    override fun finish(): Job = animateTo(currentScene)
-
-    fun animateTo(scene: SceneKey): Job {
-        check(scene == fromScene || scene == toScene)
-        animationJob?.let {
-            return it
-        }
-
-        if (scene != currentScene && state.transitionState == this && state.canChangeScene(scene)) {
-            currentScene = scene
-        }
-
-        val targetProgress =
-            when (currentScene) {
-                fromScene -> 0f
-                toScene -> 1f
-                else -> error("scene $currentScene should be either $fromScene or $toScene")
-            }
-        val startProgress = if (previewTransformationSpec != null) 0f else dragProgress
-        val animatable = Animatable(startProgress).also { progressAnimatable = it }
-
-        // Important: We start atomically to make sure that we start the coroutine even if it is
-        // cancelled right after it is launched, so that finishTransition() is correctly called.
-        return coroutineScope
-            .launch(start = CoroutineStart.ATOMIC) {
-                try {
-                    animatable.animateTo(targetProgress)
-                } finally {
-                    state.finishTransition(this@PredictiveBackTransition, scene)
-                }
-            }
-            .also { animationJob = it }
+private fun UserActionResult.copy(
+    transitionKey: TransitionKey? = this.transitionKey
+): UserActionResult {
+    return when (this) {
+        is ChangeScene -> copy(transitionKey = transitionKey)
+        is ShowOverlay -> copy(transitionKey = transitionKey)
+        is HideOverlay -> copy(transitionKey = transitionKey)
+        is ReplaceByOverlay -> copy(transitionKey = transitionKey)
     }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 65a7367..061613f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -49,7 +49,7 @@
  *   if any.
  * @param transitionInterceptionThreshold used during a scene transition. For the scene to be
  *   intercepted, the progress value must be above the threshold, and below (1 - threshold).
- * @param scenes the configuration of the different scenes of this layout.
+ * @param builder the configuration of the different scenes and overlays of this layout.
  */
 @Composable
 fun SceneTransitionLayout(
@@ -58,7 +58,7 @@
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
     @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0.05f,
-    scenes: SceneTransitionLayoutScope.() -> Unit,
+    builder: SceneTransitionLayoutScope.() -> Unit,
 ) {
     SceneTransitionLayoutForTesting(
         state,
@@ -67,7 +67,7 @@
         swipeDetector,
         transitionInterceptionThreshold,
         onLayoutImpl = null,
-        scenes,
+        builder,
     )
 }
 
@@ -86,6 +86,31 @@
         userActions: Map<UserAction, UserActionResult> = emptyMap(),
         content: @Composable ContentScope.() -> Unit,
     )
+
+    /**
+     * Add an overlay to this layout, identified by [key].
+     *
+     * Overlays are displayed above scenes and can be toggled using
+     * [MutableSceneTransitionLayoutState.showOverlay] and
+     * [MutableSceneTransitionLayoutState.hideOverlay].
+     *
+     * Overlays will have a maximum size that is the size of the layout without overlays, i.e. an
+     * overlay can be fillMaxSize() to match the layout size but it won't make the layout bigger.
+     *
+     * By default overlays are centered in their layout but they can be aligned differently using
+     * [alignment].
+     *
+     * Important: overlays must be defined after all scenes. Overlay order along the z-axis follows
+     * call order. Calling overlay(A) followed by overlay(B) will mean that overlay B renders
+     * after/above overlay A.
+     */
+    fun overlay(
+        key: OverlayKey,
+        userActions: Map<UserAction, UserActionResult> =
+            mapOf(Back to UserActionResult.HideOverlay(key)),
+        alignment: Alignment = Alignment.Center,
+        content: @Composable ContentScope.() -> Unit,
+    )
 }
 
 /**
@@ -239,7 +264,7 @@
     /**
      * Animate some value at the content level.
      *
-     * @param value the value of this shared value in the current scene.
+     * @param value the value of this shared value in the current content.
      * @param key the key of this shared value.
      * @param type the [SharedValueType] of this animated value.
      * @param canOverflow whether this value can overflow past the values it is interpolated
@@ -292,7 +317,7 @@
     /**
      * Animate some value associated to this element.
      *
-     * @param value the value of this shared value in the current scene.
+     * @param value the value of this shared value in the current content.
      * @param key the key of this shared value.
      * @param type the [SharedValueType] of this animated value.
      * @param canOverflow whether this value can overflow past the values it is interpolated
@@ -454,20 +479,95 @@
 }
 
 /** The result of performing a [UserAction]. */
-data class UserActionResult(
-    /** The scene we should be transitioning to during the [UserAction]. */
-    val toScene: SceneKey,
-
+sealed class UserActionResult(
     /** The key of the transition that should be used. */
-    val transitionKey: TransitionKey? = null,
+    open val transitionKey: TransitionKey? = null,
 
     /**
      * If `true`, the swipe will be committed and we will settle to [toScene] if only if the user
      * swiped at least the swipe distance, i.e. the transition progress was already equal to or
      * bigger than 100% when the user released their finger. `
      */
-    val requiresFullDistanceSwipe: Boolean = false,
-)
+    open val requiresFullDistanceSwipe: Boolean,
+) {
+    internal abstract fun toContent(currentScene: SceneKey): ContentKey
+
+    data class ChangeScene
+    internal constructor(
+        /** The scene we should be transitioning to during the [UserAction]. */
+        val toScene: SceneKey,
+        override val transitionKey: TransitionKey? = null,
+        override val requiresFullDistanceSwipe: Boolean = false,
+    ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
+        override fun toContent(currentScene: SceneKey): ContentKey = toScene
+    }
+
+    /** A [UserActionResult] that shows [overlay]. */
+    data class ShowOverlay(
+        val overlay: OverlayKey,
+        override val transitionKey: TransitionKey? = null,
+        override val requiresFullDistanceSwipe: Boolean = false,
+    ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
+        override fun toContent(currentScene: SceneKey): ContentKey = overlay
+    }
+
+    /** A [UserActionResult] that hides [overlay]. */
+    data class HideOverlay(
+        val overlay: OverlayKey,
+        override val transitionKey: TransitionKey? = null,
+        override val requiresFullDistanceSwipe: Boolean = false,
+    ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
+        override fun toContent(currentScene: SceneKey): ContentKey = currentScene
+    }
+
+    /**
+     * A [UserActionResult] that replaces the current overlay by [overlay].
+     *
+     * Note: This result can only be used for user actions of overlays and an exception will be
+     * thrown if it is used for a scene.
+     */
+    data class ReplaceByOverlay(
+        val overlay: OverlayKey,
+        override val transitionKey: TransitionKey? = null,
+        override val requiresFullDistanceSwipe: Boolean = false,
+    ) : UserActionResult(transitionKey, requiresFullDistanceSwipe) {
+        override fun toContent(currentScene: SceneKey): ContentKey = overlay
+    }
+
+    companion object {
+        /** A [UserActionResult] that changes the current scene to [toScene]. */
+        operator fun invoke(
+            /** The scene we should be transitioning to during the [UserAction]. */
+            toScene: SceneKey,
+
+            /** The key of the transition that should be used. */
+            transitionKey: TransitionKey? = null,
+
+            /**
+             * If `true`, the swipe will be committed if only if the user swiped at least the swipe
+             * distance, i.e. the transition progress was already equal to or bigger than 100% when
+             * the user released their finger.
+             */
+            requiresFullDistanceSwipe: Boolean = false,
+        ): UserActionResult = ChangeScene(toScene, transitionKey, requiresFullDistanceSwipe)
+
+        /** A [UserActionResult] that shows [toOverlay]. */
+        operator fun invoke(
+            /** The overlay we should be transitioning to during the [UserAction]. */
+            toOverlay: OverlayKey,
+
+            /** The key of the transition that should be used. */
+            transitionKey: TransitionKey? = null,
+
+            /**
+             * If `true`, the swipe will be committed if only if the user swiped at least the swipe
+             * distance, i.e. the transition progress was already equal to or bigger than 100% when
+             * the user released their finger.
+             */
+            requiresFullDistanceSwipe: Boolean = false,
+        ): UserActionResult = ShowOverlay(toOverlay, transitionKey, requiresFullDistanceSwipe)
+    }
+}
 
 fun interface UserActionDistance {
     /**
@@ -509,11 +609,11 @@
     swipeDetector: SwipeDetector = DefaultSwipeDetector,
     transitionInterceptionThreshold: Float = 0f,
     onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
-    scenes: SceneTransitionLayoutScope.() -> Unit,
+    builder: SceneTransitionLayoutScope.() -> Unit,
 ) {
     val density = LocalDensity.current
     val layoutDirection = LocalLayoutDirection.current
-    val coroutineScope = rememberCoroutineScope()
+    val animationScope = rememberCoroutineScope()
     val layoutImpl = remember {
         SceneTransitionLayoutImpl(
                 state = state as MutableSceneTransitionLayoutStateImpl,
@@ -521,15 +621,15 @@
                 layoutDirection = layoutDirection,
                 swipeSourceDetector = swipeSourceDetector,
                 transitionInterceptionThreshold = transitionInterceptionThreshold,
-                builder = scenes,
-                coroutineScope = coroutineScope,
+                builder = builder,
+                animationScope = animationScope,
             )
             .also { onLayoutImpl?.invoke(it) }
     }
 
     // TODO(b/317014852): Move this into the SideEffect {} again once STLImpl.scenes is not a
     // SnapshotStateMap anymore.
-    layoutImpl.updateScenes(scenes, layoutDirection)
+    layoutImpl.updateContents(builder, layoutDirection)
 
     SideEffect {
         if (state != layoutImpl.state) {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 062d553..f36c0fa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -16,12 +16,15 @@
 
 package com.android.compose.animation.scene
 
+import androidx.annotation.VisibleForTesting
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.Stable
 import androidx.compose.runtime.key
 import androidx.compose.runtime.snapshots.SnapshotStateMap
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.ApproachLayoutModifierNode
@@ -29,6 +32,7 @@
 import androidx.compose.ui.layout.LookaheadScope
 import androidx.compose.ui.layout.Measurable
 import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.node.LayoutAwareModifierNode
 import androidx.compose.ui.node.ModifierNodeElement
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
@@ -36,8 +40,11 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastForEachReversed
+import androidx.compose.ui.zIndex
 import com.android.compose.animation.scene.content.Content
+import com.android.compose.animation.scene.content.Overlay
 import com.android.compose.animation.scene.content.Scene
+import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.ui.util.lerp
 import kotlinx.coroutines.CoroutineScope
 
@@ -52,14 +59,30 @@
     internal var swipeSourceDetector: SwipeSourceDetector,
     internal var transitionInterceptionThreshold: Float,
     builder: SceneTransitionLayoutScope.() -> Unit,
-    internal val coroutineScope: CoroutineScope,
+
+    /**
+     * The scope that should be used by *animations started by this layout only*, i.e. animations
+     * triggered by gestures set up on this layout in [swipeToScene] or interruption decay
+     * animations.
+     */
+    internal val animationScope: CoroutineScope,
 ) {
     /**
      * The map of [Scene]s.
      *
      * TODO(b/317014852): Make this a normal MutableMap instead.
      */
-    internal val scenes = SnapshotStateMap<SceneKey, Scene>()
+    private val scenes = SnapshotStateMap<SceneKey, Scene>()
+
+    /**
+     * The map of [Overlays].
+     *
+     * Note: We lazily create this map to avoid instantiation an expensive SnapshotStateMap in the
+     * common case where there is no overlay in this layout.
+     */
+    private var _overlays: MutableMap<OverlayKey, Overlay>? = null
+    private val overlays
+        get() = _overlays ?: SnapshotStateMap<OverlayKey, Overlay>().also { _overlays = it }
 
     /**
      * The map of [Element]s.
@@ -117,24 +140,18 @@
     internal lateinit var lookaheadScope: LookaheadScope
         private set
 
+    internal var lastSize: IntSize = IntSize.Zero
+
     init {
-        updateScenes(builder, layoutDirection)
+        updateContents(builder, layoutDirection)
 
         // DraggableHandlerImpl must wait for the scenes to be initialized, in order to access the
         // current scene (required for SwipeTransition).
         horizontalDraggableHandler =
-            DraggableHandlerImpl(
-                layoutImpl = this,
-                orientation = Orientation.Horizontal,
-                coroutineScope = coroutineScope,
-            )
+            DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Horizontal)
 
         verticalDraggableHandler =
-            DraggableHandlerImpl(
-                layoutImpl = this,
-                orientation = Orientation.Vertical,
-                coroutineScope = coroutineScope,
-            )
+            DraggableHandlerImpl(layoutImpl = this, orientation = Orientation.Vertical)
 
         // Make sure that the state is created on the same thread (most probably the main thread)
         // than this STLImpl.
@@ -151,22 +168,54 @@
         return scenes[key] ?: error("Scene $key is not configured")
     }
 
+    internal fun sceneOrNull(key: SceneKey): Scene? = scenes[key]
+
+    internal fun overlay(key: OverlayKey): Overlay {
+        return overlays[key] ?: error("Overlay $key is not configured")
+    }
+
     internal fun content(key: ContentKey): Content {
         return when (key) {
             is SceneKey -> scene(key)
+            is OverlayKey -> overlay(key)
         }
     }
 
-    internal fun updateScenes(
+    internal fun contentForUserActions(): Content {
+        return findOverlayWithHighestZIndex() ?: scene(state.transitionState.currentScene)
+    }
+
+    private fun findOverlayWithHighestZIndex(): Overlay? {
+        val currentOverlays = state.transitionState.currentOverlays
+        if (currentOverlays.isEmpty()) {
+            return null
+        }
+
+        var overlay: Overlay? = null
+        currentOverlays.forEach { key ->
+            val previousZIndex = overlay?.zIndex
+            val candidate = overlay(key)
+            if (previousZIndex == null || candidate.zIndex > previousZIndex) {
+                overlay = candidate
+            }
+        }
+
+        return overlay
+    }
+
+    internal fun updateContents(
         builder: SceneTransitionLayoutScope.() -> Unit,
         layoutDirection: LayoutDirection,
     ) {
-        // Keep a reference of the current scenes. After processing [builder], the scenes that were
-        // not configured will be removed.
+        // Keep a reference of the current contents. After processing [builder], the contents that
+        // were not configured will be removed.
         val scenesToRemove = scenes.keys.toMutableSet()
+        val overlaysToRemove =
+            if (_overlays == null) mutableSetOf() else overlays.keys.toMutableSet()
 
         // The incrementing zIndex of each scene.
         var zIndex = 0f
+        var overlaysDefined = false
 
         object : SceneTransitionLayoutScope {
                 override fun scene(
@@ -174,10 +223,11 @@
                     userActions: Map<UserAction, UserActionResult>,
                     content: @Composable ContentScope.() -> Unit,
                 ) {
+                    require(!overlaysDefined) { "all scenes must be defined before overlays" }
+
                     scenesToRemove.remove(key)
 
-                    val resolvedUserActions =
-                        userActions.mapKeys { it.key.resolve(layoutDirection) }
+                    val resolvedUserActions = resolveUserActions(key, userActions, layoutDirection)
                     val scene = scenes[key]
                     if (scene != null) {
                         // Update an existing scene.
@@ -198,10 +248,84 @@
 
                     zIndex++
                 }
+
+                override fun overlay(
+                    key: OverlayKey,
+                    userActions: Map<UserAction, UserActionResult>,
+                    alignment: Alignment,
+                    content: @Composable (ContentScope.() -> Unit)
+                ) {
+                    overlaysDefined = true
+                    overlaysToRemove.remove(key)
+
+                    val overlay = overlays[key]
+                    val resolvedUserActions = resolveUserActions(key, userActions, layoutDirection)
+                    if (overlay != null) {
+                        // Update an existing overlay.
+                        overlay.content = content
+                        overlay.zIndex = zIndex
+                        overlay.userActions = resolvedUserActions
+                        overlay.alignment = alignment
+                    } else {
+                        // New overlay.
+                        overlays[key] =
+                            Overlay(
+                                key,
+                                this@SceneTransitionLayoutImpl,
+                                content,
+                                resolvedUserActions,
+                                zIndex,
+                                alignment,
+                            )
+                    }
+
+                    zIndex++
+                }
             }
             .builder()
 
         scenesToRemove.forEach { scenes.remove(it) }
+        overlaysToRemove.forEach { overlays.remove(it) }
+    }
+
+    private fun resolveUserActions(
+        key: ContentKey,
+        userActions: Map<UserAction, UserActionResult>,
+        layoutDirection: LayoutDirection
+    ): Map<UserAction.Resolved, UserActionResult> {
+        return userActions
+            .mapKeys { it.key.resolve(layoutDirection) }
+            .also { checkUserActions(key, it) }
+    }
+
+    private fun checkUserActions(
+        key: ContentKey,
+        userActions: Map<UserAction.Resolved, UserActionResult>,
+    ) {
+        userActions.forEach { (action, result) ->
+            fun details() = "Content $key, action $action, result $result."
+
+            when (result) {
+                is UserActionResult.ChangeScene -> {
+                    check(key != result.toScene) {
+                        error("Transition to the same scene is not supported. ${details()}")
+                    }
+                }
+                is UserActionResult.ReplaceByOverlay -> {
+                    check(key is OverlayKey) {
+                        "ReplaceByOverlay() can only be used for overlays, not scenes. ${details()}"
+                    }
+
+                    check(key != result.overlay) {
+                        "Transition to the same overlay is not supported. ${details()}"
+                    }
+                }
+                is UserActionResult.ShowOverlay,
+                is UserActionResult.HideOverlay -> {
+                    /* Always valid. */
+                }
+            }
+        }
     }
 
     @Composable
@@ -219,17 +343,21 @@
                 lookaheadScope = this
 
                 BackHandler()
-
-                scenesToCompose().fastForEach { scene -> key(scene.key) { scene.Content() } }
+                Scenes()
+                Overlays()
             }
         }
     }
 
     @Composable
     private fun BackHandler() {
-        val targetSceneForBack =
-            scene(state.transitionState.currentScene).userActions[Back.Resolved]?.toScene
-        PredictiveBackHandler(state, coroutineScope, targetSceneForBack)
+        val result = contentForUserActions().userActions[Back.Resolved]
+        PredictiveBackHandler(layoutImpl = this, result = result)
+    }
+
+    @Composable
+    private fun Scenes() {
+        scenesToCompose().fastForEach { scene -> key(scene.key) { scene.Content() } }
     }
 
     private fun scenesToCompose(): List<Scene> {
@@ -247,16 +375,81 @@
 
                 // Compose the new scene we are going to first.
                 transitions.fastForEachReversed { transition ->
-                    maybeAdd(transition.toScene)
-                    maybeAdd(transition.fromScene)
+                    when (transition) {
+                        is TransitionState.Transition.ChangeScene -> {
+                            maybeAdd(transition.toScene)
+                            maybeAdd(transition.fromScene)
+                        }
+                        is TransitionState.Transition.ShowOrHideOverlay ->
+                            maybeAdd(transition.fromOrToScene)
+                        is TransitionState.Transition.ReplaceOverlay -> {}
+                    }
                 }
+
+                // Make sure that the current scene is always composed.
+                maybeAdd(transitions.last().currentScene)
             }
         }
     }
 
-    internal fun setScenesTargetSizeForTest(size: IntSize) {
-        scenes.values.forEach { it.targetSize = size }
+    @Composable
+    private fun BoxScope.Overlays() {
+        val overlaysOrderedByZIndex = overlaysToComposeOrderedByZIndex()
+        if (overlaysOrderedByZIndex.isEmpty()) {
+            return
+        }
+
+        // We put the overlays inside a Box that is matching the layout size so that overlays are
+        // measured after all scenes and that their max size is the size of the layout without the
+        // overlays.
+        Box(Modifier.matchParentSize().zIndex(overlaysOrderedByZIndex.first().zIndex)) {
+            overlaysOrderedByZIndex.fastForEach { overlay ->
+                key(overlay.key) { overlay.Content(Modifier.align(overlay.alignment)) }
+            }
+        }
     }
+
+    private fun overlaysToComposeOrderedByZIndex(): List<Overlay> {
+        if (_overlays == null) return emptyList()
+
+        val transitions = state.currentTransitions
+        return if (transitions.isEmpty()) {
+                state.transitionState.currentOverlays.map { overlay(it) }
+            } else {
+                buildList {
+                    val visited = mutableSetOf<OverlayKey>()
+                    fun maybeAdd(key: OverlayKey) {
+                        if (visited.add(key)) {
+                            add(overlay(key))
+                        }
+                    }
+
+                    transitions.fastForEach { transition ->
+                        when (transition) {
+                            is TransitionState.Transition.ChangeScene -> {}
+                            is TransitionState.Transition.ShowOrHideOverlay ->
+                                maybeAdd(transition.overlay)
+                            is TransitionState.Transition.ReplaceOverlay -> {
+                                maybeAdd(transition.fromOverlay)
+                                maybeAdd(transition.toOverlay)
+                            }
+                        }
+                    }
+
+                    // Make sure that all current overlays are composed.
+                    transitions.last().currentOverlays.forEach { maybeAdd(it) }
+                }
+            }
+            .sortedBy { it.zIndex }
+    }
+
+    @VisibleForTesting
+    internal fun setContentsAndLayoutTargetSizeForTest(size: IntSize) {
+        lastSize = size
+        (scenes.values + overlays.values).forEach { it.targetSize = size }
+    }
+
+    internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
 }
 
 private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
@@ -269,7 +462,11 @@
 }
 
 private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
-    Modifier.Node(), ApproachLayoutModifierNode {
+    Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
+    override fun onRemeasured(size: IntSize) {
+        layoutImpl.lastSize = size
+    }
+
     override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
         return layoutImpl.state.isTransitioning()
     }
@@ -284,7 +481,8 @@
 
         val width: Int
         val height: Int
-        val transition = layoutImpl.state.currentTransition
+        val transition =
+            layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeScene
         if (transition == null) {
             width = placeable.width
             height = placeable.height
@@ -304,7 +502,7 @@
                 val progress =
                     when {
                         overscrollSpec == null -> transition.progress
-                        overscrollSpec.scene == transition.toScene -> 1f
+                        overscrollSpec.content == transition.toScene -> 1f
                         else -> 0f
                     }
 
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 a6c6a80..c2d5dd05 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
@@ -25,12 +25,15 @@
 import androidx.compose.ui.util.fastAll
 import androidx.compose.ui.util.fastFilter
 import androidx.compose.ui.util.fastForEach
-import com.android.compose.animation.scene.content.state.ContentState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transition.link.LinkedTransition
 import com.android.compose.animation.scene.transition.link.StateLink
 import kotlin.math.absoluteValue
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
 
 /**
  * The state of a [SceneTransitionLayout].
@@ -40,6 +43,21 @@
 @Stable
 sealed interface SceneTransitionLayoutState {
     /**
+     * The current effective scene. If a new transition is triggered, it will start from this scene.
+     */
+    val currentScene: SceneKey
+
+    /**
+     * The current set of overlays. This represents the set of overlays that will be visible on
+     * screen once all [currentTransitions] are finished.
+     *
+     * @see MutableSceneTransitionLayoutState.showOverlay
+     * @see MutableSceneTransitionLayoutState.hideOverlay
+     * @see MutableSceneTransitionLayoutState.replaceOverlay
+     */
+    val currentOverlays: Set<OverlayKey>
+
+    /**
      * The current [TransitionState]. All values read here are backed by the Snapshot system.
      *
      * To observe those values outside of Compose/the Snapshot system, use
@@ -67,12 +85,15 @@
 
     /**
      * Whether we are transitioning. If [from] or [to] is empty, we will also check that they match
-     * the scenes we are animating from and/or to.
+     * the contents we are animating from and/or to.
      */
-    fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean
+    fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean
 
-    /** Whether we are transitioning from [scene] to [other], or from [other] to [scene]. */
-    fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean
+    /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
+    fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean
+
+    /** Whether we are transitioning from or to [content]. */
+    fun isTransitioningFromOrTo(content: ContentKey): Boolean
 }
 
 /** A [SceneTransitionLayoutState] whose target scene can be imperatively set. */
@@ -91,27 +112,70 @@
      * If [targetScene] is different than the [currentScene][TransitionState.currentScene] of
      * [transitionState], then this will animate to [targetScene]. The associated
      * [TransitionState.Transition] will be returned and will be set as the current
-     * [transitionState] of this [MutableSceneTransitionLayoutState].
+     * [transitionState] of this [MutableSceneTransitionLayoutState]. The [Job] in which the
+     * transition runs will be returned, allowing you to easily [join][Job.join] or
+     * [cancel][Job.cancel] the animation.
      *
      * Note that because a non-null [TransitionState.Transition] is returned does not mean that the
      * transition will finish and that we will settle to [targetScene]. The returned transition
      * might still be interrupted, for instance by another call to [setTargetScene] or by a user
      * gesture.
      *
-     * If [this] [CoroutineScope] is cancelled during the transition and that the transition was
-     * still active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be
-     * set to `TransitionState.Idle(targetScene)`.
-     *
-     * TODO(b/318794193): Add APIs to await() and cancel() any [TransitionState.Transition].
+     * If [animationScope] is cancelled during the transition and that the transition was still
+     * active, then the [transitionState] of this [MutableSceneTransitionLayoutState] will be set to
+     * `TransitionState.Idle(targetScene)`.
      */
     fun setTargetScene(
         targetScene: SceneKey,
-        coroutineScope: CoroutineScope,
+        animationScope: CoroutineScope,
         transitionKey: TransitionKey? = null,
-    ): TransitionState.Transition?
+    ): Pair<TransitionState.Transition, Job>?
 
     /** Immediately snap to the given [scene]. */
-    fun snapToScene(scene: SceneKey)
+    fun snapToScene(
+        scene: SceneKey,
+        currentOverlays: Set<OverlayKey> = transitionState.currentOverlays,
+    )
+
+    /**
+     * Request to show [overlay] so that it animates in from [currentScene] and ends up being
+     * visible on screen.
+     *
+     * After this returns, this overlay will be included in [currentOverlays]. This does nothing if
+     * [overlay] is already in [currentOverlays].
+     */
+    fun showOverlay(
+        overlay: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey? = null,
+    )
+
+    /**
+     * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being
+     * visible on screen.
+     *
+     * After this returns, this overlay will not be included in [currentOverlays]. This does nothing
+     * if [overlay] is not in [currentOverlays].
+     */
+    fun hideOverlay(
+        overlay: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey? = null,
+    )
+
+    /**
+     * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up
+     * being visible.
+     *
+     * This throws if [from] is not currently in [currentOverlays] or if [to] is already in
+     * [currentOverlays].
+     */
+    fun replaceOverlay(
+        from: OverlayKey,
+        to: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey? = null,
+    )
 }
 
 /**
@@ -123,20 +187,34 @@
  *   commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns
  *   `true`, then the gesture will be committed and we will animate to the other scene. Otherwise,
  *   the gesture will be cancelled and we will animate back to the current scene.
+ * @param canShowOverlay whether we should commit a user action that will result in showing the
+ *   given overlay.
+ * @param canHideOverlay whether we should commit a user action that will result in hiding the given
+ *   overlay.
+ * @param canReplaceOverlay whether we should commit a user action that will result in replacing
+ *   `from` overlay by `to` overlay.
  * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
  *   [SceneTransitionLayoutState]s.
  */
 fun MutableSceneTransitionLayoutState(
     initialScene: SceneKey,
     transitions: SceneTransitions = SceneTransitions.Empty,
+    initialOverlays: Set<OverlayKey> = emptySet(),
     canChangeScene: (SceneKey) -> Boolean = { true },
+    canShowOverlay: (OverlayKey) -> Boolean = { true },
+    canHideOverlay: (OverlayKey) -> Boolean = { true },
+    canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
     stateLinks: List<StateLink> = emptyList(),
     enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
 ): MutableSceneTransitionLayoutState {
     return MutableSceneTransitionLayoutStateImpl(
         initialScene,
         transitions,
+        initialOverlays,
         canChangeScene,
+        canShowOverlay,
+        canHideOverlay,
+        canReplaceOverlay,
         stateLinks,
         enableInterruptions,
     )
@@ -146,7 +224,13 @@
 internal class MutableSceneTransitionLayoutStateImpl(
     initialScene: SceneKey,
     override var transitions: SceneTransitions = transitions {},
+    initialOverlays: Set<OverlayKey> = emptySet(),
     internal val canChangeScene: (SceneKey) -> Boolean = { true },
+    internal val canShowOverlay: (OverlayKey) -> Boolean = { true },
+    internal val canHideOverlay: (OverlayKey) -> Boolean = { true },
+    internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ ->
+        true
+    },
     private val stateLinks: List<StateLink> = emptyList(),
 
     // TODO(b/290930950): Remove this flag.
@@ -159,15 +243,18 @@
      * 1. A list with a single [TransitionState.Idle] element, when we are idle.
      * 2. A list with one or more [TransitionState.Transition], when we are transitioning.
      */
-    @VisibleForTesting
     internal var transitionStates: List<TransitionState> by
-        mutableStateOf(listOf(TransitionState.Idle(initialScene)))
+        mutableStateOf(listOf(TransitionState.Idle(initialScene, initialOverlays)))
         private set
 
-    override val transitionState: TransitionState
-        get() = transitionStates.last()
+    override val currentScene: SceneKey
+        get() = transitionState.currentScene
 
-    private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+    override val currentOverlays: Set<OverlayKey>
+        get() = transitionState.currentOverlays
+
+    override val transitionState: TransitionState
+        get() = transitionStates[transitionStates.lastIndex]
 
     override val currentTransitions: List<TransitionState.Transition>
         get() {
@@ -180,12 +267,8 @@
             }
         }
 
-    /**
-     * The mapping of transitions that are finished, i.e. for which [finishTransition] was called,
-     * to their idle scene.
-     */
-    @VisibleForTesting
-    internal val finishedTransitions = mutableMapOf<TransitionState.Transition, SceneKey>()
+    /** The transitions that are finished, i.e. for which [finishTransition] was called. */
+    @VisibleForTesting internal val finishedTransitions = mutableSetOf<TransitionState.Transition>()
 
     internal fun checkThread() {
         val current = Thread.currentThread()
@@ -201,24 +284,29 @@
         }
     }
 
-    override fun isTransitioning(from: SceneKey?, to: SceneKey?): Boolean {
+    override fun isTransitioning(from: ContentKey?, to: ContentKey?): Boolean {
         val transition = currentTransition ?: return false
         return transition.isTransitioning(from, to)
     }
 
-    override fun isTransitioningBetween(scene: SceneKey, other: SceneKey): Boolean {
+    override fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
         val transition = currentTransition ?: return false
-        return transition.isTransitioningBetween(scene, other)
+        return transition.isTransitioningBetween(content, other)
+    }
+
+    override fun isTransitioningFromOrTo(content: ContentKey): Boolean {
+        val transition = currentTransition ?: return false
+        return transition.isTransitioningFromOrTo(content)
     }
 
     override fun setTargetScene(
         targetScene: SceneKey,
-        coroutineScope: CoroutineScope,
+        animationScope: CoroutineScope,
         transitionKey: TransitionKey?,
-    ): TransitionState.Transition? {
+    ): Pair<TransitionState.Transition.ChangeScene, Job>? {
         checkThread()
 
-        return coroutineScope.animateToScene(
+        return animationScope.animateToScene(
             layoutState = this@MutableSceneTransitionLayoutStateImpl,
             target = targetScene,
             transitionKey = transitionKey,
@@ -226,44 +314,95 @@
     }
 
     /**
+     * Instantly start a [transition], running it in [animationScope].
+     *
+     * This call returns immediately and [transition] will be the [currentTransition] of this
+     * [MutableSceneTransitionLayoutState].
+     *
+     * @see startTransition
+     */
+    internal fun startTransitionImmediately(
+        animationScope: CoroutineScope,
+        transition: TransitionState.Transition,
+        chain: Boolean = true,
+    ): Job {
+        // Note that we start with UNDISPATCHED so that startTransition() is called directly and
+        // transition becomes the current [transitionState] right after this call.
+        return animationScope.launch(
+            start = CoroutineStart.UNDISPATCHED,
+        ) {
+            startTransition(transition, chain)
+        }
+    }
+
+    /**
      * Start a new [transition].
      *
      * If [chain] is `true`, then the transitions will simply be added to [currentTransitions] and
      * will run in parallel to the current transitions. If [chain] is `false`, then the list of
      * [currentTransitions] will be cleared and [transition] will be the only running transition.
      *
-     * Important: you *must* call [finishTransition] once the transition is finished.
+     * If any transition is currently ongoing, it will be interrupted and forced to animate to its
+     * current state.
+     *
+     * This method returns when [transition] is done running, i.e. when the call to
+     * [run][TransitionState.Transition.run] returns.
      */
-    internal fun startTransition(transition: TransitionState.Transition, chain: Boolean = true) {
+    internal suspend fun startTransition(
+        transition: TransitionState.Transition,
+        chain: Boolean = true,
+    ) {
         checkThread()
 
+        try {
+            // Keep a reference to the previous transition (if any).
+            val previousTransition = currentTransition
+
+            // Start the transition.
+            startTransitionInternal(transition, chain)
+
+            // Handle transition links.
+            previousTransition?.let { cancelActiveTransitionLinks(it) }
+            if (stateLinks.isNotEmpty()) {
+                coroutineScope { setupTransitionLinks(transition) }
+            }
+
+            // Run the transition until it is finished.
+            transition.run()
+        } finally {
+            finishTransition(transition)
+        }
+    }
+
+    private fun startTransitionInternal(transition: TransitionState.Transition, chain: Boolean) {
+        // Set the current scene and overlays on the transition.
+        val currentState = transitionState
+        transition.currentSceneWhenTransitionStarted = currentState.currentScene
+        transition.currentOverlaysWhenTransitionStarted = currentState.currentOverlays
+
         // Compute the [TransformationSpec] when the transition starts.
-        val fromScene = transition.fromScene
-        val toScene = transition.toScene
-        val orientation = (transition as? ContentState.HasOverscrollProperties)?.orientation
+        val fromContent = transition.fromContent
+        val toContent = transition.toContent
+        val orientation = (transition as? TransitionState.HasOverscrollProperties)?.orientation
 
         // Update the transition specs.
         transition.transformationSpec =
             transitions
-                .transitionSpec(fromScene, toScene, key = transition.key)
+                .transitionSpec(fromContent, toContent, key = transition.key)
                 .transformationSpec()
         transition.previewTransformationSpec =
             transitions
-                .transitionSpec(fromScene, toScene, key = transition.key)
+                .transitionSpec(fromContent, toContent, key = transition.key)
                 .previewTransformationSpec()
         if (orientation != null) {
             transition.updateOverscrollSpecs(
-                fromSpec = transitions.overscrollSpec(fromScene, orientation),
-                toSpec = transitions.overscrollSpec(toScene, orientation),
+                fromSpec = transitions.overscrollSpec(fromContent, orientation),
+                toSpec = transitions.overscrollSpec(toContent, orientation),
             )
         } else {
             transition.updateOverscrollSpecs(fromSpec = null, toSpec = null)
         }
 
-        // Handle transition links.
-        cancelActiveTransitionLinks()
-        setupTransitionLinks(transition)
-
         if (!enableInterruptions) {
             // Set the current transition.
             check(transitionStates.size == 1)
@@ -278,9 +417,8 @@
                 transitionStates = listOf(transition)
             }
             is TransitionState.Transition -> {
-                // Force the current transition to finish to currentScene. The transition will call
-                // [finishTransition] once it's finished.
-                currentState.finish()
+                // Force the current transition to finish to currentScene.
+                currentState.freezeAndAnimateToCurrentState()
 
                 val tooManyTransitions = transitionStates.size >= MAX_CONCURRENT_TRANSITIONS
                 val clearCurrentTransitions = !chain || tooManyTransitions
@@ -289,8 +427,7 @@
 
                     // Force finish all transitions.
                     while (currentTransitions.isNotEmpty()) {
-                        val transition = transitionStates[0] as TransitionState.Transition
-                        finishTransition(transition, transition.currentScene)
+                        finishTransition(transitionStates[0] as TransitionState.Transition)
                     }
 
                     // We finished all transitions, so we are now idle. We remove this state so that
@@ -319,8 +456,8 @@
                 appendLine("  Transitions (size=${transitionStates.size}):")
                 transitionStates.fastForEach { state ->
                     val transition = state as TransitionState.Transition
-                    val from = transition.fromScene
-                    val to = transition.toScene
+                    val from = transition.fromContent
+                    val to = transition.toContent
                     val indicator = if (finishedTransitions.contains(transition)) "x" else " "
                     appendLine("  [$indicator] $from => $to ($transition)")
                 }
@@ -328,18 +465,17 @@
         )
     }
 
-    private fun cancelActiveTransitionLinks() {
-        for ((link, linkedTransition) in activeTransitionLinks) {
-            link.target.finishTransition(linkedTransition, linkedTransition.currentScene)
+    private fun cancelActiveTransitionLinks(transition: TransitionState.Transition) {
+        transition.activeTransitionLinks.forEach { (link, linkedTransition) ->
+            link.target.finishTransition(linkedTransition)
         }
-        activeTransitionLinks.clear()
+        transition.activeTransitionLinks.clear()
     }
 
-    private fun setupTransitionLinks(transitionState: TransitionState) {
-        if (transitionState !is TransitionState.Transition) return
+    private fun CoroutineScope.setupTransitionLinks(transition: TransitionState.Transition) {
         stateLinks.fastForEach { stateLink ->
             val matchingLinks =
-                stateLink.transitionLinks.fastFilter { it.isMatchingLink(transitionState) }
+                stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) }
             if (matchingLinks.isEmpty()) return@fastForEach
             if (matchingLinks.size > 1) error("More than one link matched.")
 
@@ -350,34 +486,38 @@
 
             val linkedTransition =
                 LinkedTransition(
-                    originalTransition = transitionState,
+                    originalTransition = transition,
                     fromScene = targetCurrentScene,
                     toScene = matchingLink.targetTo,
                     key = matchingLink.targetTransitionKey,
                 )
 
-            stateLink.target.startTransition(linkedTransition)
-            activeTransitionLinks[stateLink] = linkedTransition
+            // Start with UNDISPATCHED so that startTransition is called directly and the new linked
+            // transition is observable directly.
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                stateLink.target.startTransition(linkedTransition)
+            }
+            transition.activeTransitionLinks[stateLink] = linkedTransition
         }
     }
 
     /**
-     * Notify that [transition] was finished and that we should settle to [idleScene]. This will do
-     * nothing if [transition] was interrupted since it was started.
+     * Notify that [transition] was finished and that it settled to its
+     * [currentScene][TransitionState.currentScene]. This will do nothing if [transition] was
+     * interrupted since it was started.
      */
-    internal fun finishTransition(transition: TransitionState.Transition, idleScene: SceneKey) {
+    private fun finishTransition(transition: TransitionState.Transition) {
         checkThread()
 
-        val existingIdleScene = finishedTransitions[transition]
-        if (existingIdleScene != null) {
+        if (finishedTransitions.contains(transition)) {
             // This transition was already finished.
-            check(idleScene == existingIdleScene) {
-                "Transition $transition was finished multiple times with different " +
-                    "idleScene ($existingIdleScene != $idleScene)"
-            }
             return
         }
 
+        // Make sure that this transition settles in case it was force finished, for instance by
+        // calling snapToScene().
+        transition.freezeAndAnimateToCurrentState()
+
         val transitionStates = this.transitionStates
         if (!transitionStates.contains(transition)) {
             // This transition was already removed from transitionStates.
@@ -386,15 +526,15 @@
 
         check(transitionStates.fastAll { it is TransitionState.Transition })
 
-        // Mark this transition as finished and save the scene it is settling at.
-        finishedTransitions[transition] = idleScene
+        // Mark this transition as finished.
+        finishedTransitions.add(transition)
 
         // Finish all linked transitions.
-        finishActiveTransitionLinks(idleScene)
+        finishActiveTransitionLinks(transition)
 
-        // Keep a reference to the idle scene of the last removed transition, in case we remove all
-        // transitions and should settle to Idle.
-        var lastRemovedIdleScene: SceneKey? = null
+        // Keep a reference to the last transition, in case we remove all transitions and should
+        // settle to Idle.
+        val lastTransition = transitionStates.last()
 
         // Remove all first n finished transitions.
         var i = 0
@@ -407,47 +547,42 @@
             }
 
             // Remove the transition from the set of finished transitions.
-            lastRemovedIdleScene = finishedTransitions.remove(t)
+            finishedTransitions.remove(t)
             i++
         }
 
         // If all transitions are finished, we are idle.
         if (i == nStates) {
             check(finishedTransitions.isEmpty())
-            this.transitionStates = listOf(TransitionState.Idle(checkNotNull(lastRemovedIdleScene)))
+            this.transitionStates =
+                listOf(
+                    TransitionState.Idle(
+                        lastTransition.currentScene,
+                        lastTransition.currentOverlays,
+                    )
+                )
         } else if (i > 0) {
             this.transitionStates = transitionStates.subList(fromIndex = i, toIndex = nStates)
         }
     }
 
-    override fun snapToScene(scene: SceneKey) {
+    override fun snapToScene(scene: SceneKey, currentOverlays: Set<OverlayKey>) {
         checkThread()
 
         // Force finish all transitions.
         while (currentTransitions.isNotEmpty()) {
-            val transition = transitionStates[0] as TransitionState.Transition
-            finishTransition(transition, transition.currentScene)
+            finishTransition(transitionStates[0] as TransitionState.Transition)
         }
 
         check(transitionStates.size == 1)
-        transitionStates = listOf(TransitionState.Idle(scene))
+        transitionStates = listOf(TransitionState.Idle(scene, currentOverlays))
     }
 
-    private fun finishActiveTransitionLinks(idleScene: SceneKey) {
-        val previousTransition = this.transitionState as? TransitionState.Transition ?: return
-        for ((link, linkedTransition) in activeTransitionLinks) {
-            if (previousTransition.fromScene == idleScene) {
-                // The transition ended by arriving at the fromScene, move link to Idle(fromScene).
-                link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
-            } else if (previousTransition.toScene == idleScene) {
-                // The transition ended by arriving at the toScene, move link to Idle(toScene).
-                link.target.finishTransition(linkedTransition, linkedTransition.toScene)
-            } else {
-                // The transition was interrupted by something else, we reset to initial state.
-                link.target.finishTransition(linkedTransition, linkedTransition.fromScene)
-            }
+    private fun finishActiveTransitionLinks(transition: TransitionState.Transition) {
+        for ((link, linkedTransition) in transition.activeTransitionLinks) {
+            link.target.finishTransition(linkedTransition)
         }
-        activeTransitionLinks.clear()
+        transition.activeTransitionLinks.clear()
     }
 
     /**
@@ -465,33 +600,154 @@
 
         fun isProgressCloseTo(value: Float) = (progress - value).absoluteValue <= threshold
 
-        fun finishAllTransitions(lastTransitionIdleScene: SceneKey) {
+        fun finishAllTransitions() {
             // Force finish all transitions.
             while (currentTransitions.isNotEmpty()) {
-                val transition = transitionStates[0] as TransitionState.Transition
-                val idleScene =
-                    if (transitionStates.size == 1) {
-                        lastTransitionIdleScene
-                    } else {
-                        transition.currentScene
-                    }
-
-                finishTransition(transition, idleScene)
+                finishTransition(transitionStates[0] as TransitionState.Transition)
             }
         }
 
-        return when {
-            isProgressCloseTo(0f) -> {
-                finishAllTransitions(transition.fromScene)
-                true
-            }
-            isProgressCloseTo(1f) -> {
-                finishAllTransitions(transition.toScene)
-                true
-            }
-            else -> false
+        val shouldSnap =
+            (isProgressCloseTo(0f) && transition.currentScene == transition.fromContent) ||
+                (isProgressCloseTo(1f) && transition.currentScene == transition.toContent)
+        return if (shouldSnap) {
+            finishAllTransitions()
+            true
+        } else {
+            false
         }
     }
+
+    override fun showOverlay(
+        overlay: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey?
+    ) {
+        checkThread()
+
+        // Overlay is already shown, do nothing.
+        val currentState = transitionState
+        if (overlay in currentState.currentOverlays) {
+            return
+        }
+
+        val fromScene = currentState.currentScene
+        fun animate(
+            replacedTransition: TransitionState.Transition.ShowOrHideOverlay? = null,
+            reversed: Boolean = false,
+        ) {
+            animationScope.showOrHideOverlay(
+                layoutState = this@MutableSceneTransitionLayoutStateImpl,
+                overlay = overlay,
+                fromOrToScene = fromScene,
+                isShowing = true,
+                transitionKey = transitionKey,
+                replacedTransition = replacedTransition,
+                reversed = reversed,
+            )
+        }
+
+        if (
+            currentState is TransitionState.Transition.ShowOrHideOverlay &&
+                currentState.overlay == overlay &&
+                currentState.fromOrToScene == fromScene
+        ) {
+            animate(
+                replacedTransition = currentState,
+                reversed = overlay == currentState.fromContent
+            )
+        } else {
+            animate()
+        }
+    }
+
+    override fun hideOverlay(
+        overlay: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey?
+    ) {
+        checkThread()
+
+        // Overlay is not shown, do nothing.
+        val currentState = transitionState
+        if (!currentState.currentOverlays.contains(overlay)) {
+            return
+        }
+
+        val toScene = currentState.currentScene
+        fun animate(
+            replacedTransition: TransitionState.Transition.ShowOrHideOverlay? = null,
+            reversed: Boolean = false,
+        ) {
+            animationScope.showOrHideOverlay(
+                layoutState = this@MutableSceneTransitionLayoutStateImpl,
+                overlay = overlay,
+                fromOrToScene = toScene,
+                isShowing = false,
+                transitionKey = transitionKey,
+                replacedTransition = replacedTransition,
+                reversed = reversed,
+            )
+        }
+
+        if (
+            currentState is TransitionState.Transition.ShowOrHideOverlay &&
+                currentState.overlay == overlay &&
+                currentState.fromOrToScene == toScene
+        ) {
+            animate(replacedTransition = currentState, reversed = overlay == currentState.toContent)
+        } else {
+            animate()
+        }
+    }
+
+    override fun replaceOverlay(
+        from: OverlayKey,
+        to: OverlayKey,
+        animationScope: CoroutineScope,
+        transitionKey: TransitionKey?
+    ) {
+        checkThread()
+
+        val currentState = transitionState
+        require(from != to) {
+            "replaceOverlay must be called with different overlays (from = to = ${from.debugName})"
+        }
+        require(from in currentState.currentOverlays) {
+            "Overlay ${from.debugName} is not shown so it can't be replaced by ${to.debugName}"
+        }
+        require(to !in currentState.currentOverlays) {
+            "Overlay ${to.debugName} is already shown so it can't replace ${from.debugName}"
+        }
+
+        fun animate(
+            replacedTransition: TransitionState.Transition.ReplaceOverlay? = null,
+            reversed: Boolean = false,
+        ) {
+            animationScope.replaceOverlay(
+                layoutState = this@MutableSceneTransitionLayoutStateImpl,
+                fromOverlay = if (reversed) to else from,
+                toOverlay = if (reversed) from else to,
+                transitionKey = transitionKey,
+                replacedTransition = replacedTransition,
+                reversed = reversed,
+            )
+        }
+
+        if (currentState is TransitionState.Transition.ReplaceOverlay) {
+            if (currentState.fromOverlay == from && currentState.toOverlay == to) {
+                animate(replacedTransition = currentState, reversed = false)
+                return
+            }
+
+            if (currentState.fromOverlay == to && currentState.toOverlay == from) {
+                animate(replacedTransition = currentState, reversed = true)
+                return
+            }
+        }
+
+        animate()
+    }
 }
 
 private const val TAG = "SceneTransitionLayoutState"
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 3ded1de..e65ed9b 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
@@ -45,19 +45,20 @@
     internal val transitionSpecs: List<TransitionSpecImpl>,
     internal val overscrollSpecs: List<OverscrollSpecImpl>,
     internal val interruptionHandler: InterruptionHandler,
+    internal val defaultProgressConverter: ProgressConverter,
 ) {
     private val transitionCache =
         mutableMapOf<
-            SceneKey,
-            MutableMap<SceneKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
+            ContentKey,
+            MutableMap<ContentKey, MutableMap<TransitionKey?, TransitionSpecImpl>>
         >()
 
     private val overscrollCache =
-        mutableMapOf<SceneKey, MutableMap<Orientation, OverscrollSpecImpl?>>()
+        mutableMapOf<ContentKey, MutableMap<Orientation, OverscrollSpecImpl?>>()
 
     internal fun transitionSpec(
-        from: SceneKey,
-        to: SceneKey,
+        from: ContentKey,
+        to: ContentKey,
         key: TransitionKey?,
     ): TransitionSpecImpl {
         return transitionCache
@@ -66,7 +67,11 @@
             .getOrPut(key) { findSpec(from, to, key) }
     }
 
-    private fun findSpec(from: SceneKey, to: SceneKey, key: TransitionKey?): TransitionSpecImpl {
+    private fun findSpec(
+        from: ContentKey,
+        to: ContentKey,
+        key: TransitionKey?
+    ): TransitionSpecImpl {
         val spec = transition(from, to, key) { it.from == from && it.to == to }
         if (spec != null) {
             return spec
@@ -85,15 +90,24 @@
             return relaxedSpec
         }
 
-        return transition(from, to, key) {
+        val relaxedReversed =
+            transition(from, to, key) {
                 (it.from == to && it.to == null) || (it.to == from && it.from == null)
             }
-            ?.reversed() ?: defaultTransition(from, to)
+        if (relaxedReversed != null) {
+            return relaxedReversed.reversed()
+        }
+
+        return if (key != null) {
+            findSpec(from, to, null)
+        } else {
+            defaultTransition(from, to)
+        }
     }
 
     private fun transition(
-        from: SceneKey,
-        to: SceneKey,
+        from: ContentKey,
+        to: ContentKey,
         key: TransitionKey?,
         filter: (TransitionSpecImpl) -> Boolean,
     ): TransitionSpecImpl? {
@@ -109,16 +123,16 @@
         return match
     }
 
-    private fun defaultTransition(from: SceneKey, to: SceneKey) =
+    private fun defaultTransition(from: ContentKey, to: ContentKey) =
         TransitionSpecImpl(key = null, from, to, null, null, TransformationSpec.EmptyProvider)
 
-    internal fun overscrollSpec(scene: SceneKey, orientation: Orientation): OverscrollSpecImpl? =
+    internal fun overscrollSpec(scene: ContentKey, orientation: Orientation): OverscrollSpecImpl? =
         overscrollCache
             .getOrPut(scene) { mutableMapOf() }
-            .getOrPut(orientation) { overscroll(scene, orientation) { it.scene == scene } }
+            .getOrPut(orientation) { overscroll(scene, orientation) { it.content == scene } }
 
     private fun overscroll(
-        scene: SceneKey,
+        scene: ContentKey,
         orientation: Orientation,
         filter: (OverscrollSpecImpl) -> Boolean,
     ): OverscrollSpecImpl? {
@@ -147,6 +161,7 @@
                 transitionSpecs = emptyList(),
                 overscrollSpecs = emptyList(),
                 interruptionHandler = DefaultInterruptionHandler,
+                defaultProgressConverter = ProgressConverter.Default,
             )
     }
 }
@@ -262,10 +277,10 @@
         previewTransformationSpec?.invoke()
 }
 
-/** The definition of the overscroll behavior of the [scene]. */
+/** The definition of the overscroll behavior of the [content]. */
 interface OverscrollSpec {
     /** The scene we are over scrolling. */
-    val scene: SceneKey
+    val content: ContentKey
 
     /** The orientation of this [OverscrollSpec]. */
     val orientation: Orientation
@@ -282,14 +297,14 @@
      * - 1, the user overscrolled by exactly the [OverscrollBuilder.distance].
      * - Greater than 1, the user overscrolled more than the [OverscrollBuilder.distance].
      */
-    val progressConverter: (Float) -> Float
+    val progressConverter: ProgressConverter?
 }
 
 internal class OverscrollSpecImpl(
-    override val scene: SceneKey,
+    override val content: ContentKey,
     override val orientation: Orientation,
     override val transformationSpec: TransformationSpecImpl,
-    override val progressConverter: (Float) -> Float,
+    override val progressConverter: ProgressConverter?,
 ) : OverscrollSpec
 
 /**
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
new file mode 100644
index 0000000..2a09a77
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -0,0 +1,647 @@
+/*
+ * 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.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+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 kotlin.math.absoluteValue
+import kotlinx.coroutines.CompletableDeferred
+
+internal fun createSwipeAnimation(
+    layoutState: MutableSceneTransitionLayoutStateImpl,
+    result: UserActionResult,
+    isUpOrLeft: Boolean,
+    orientation: Orientation,
+    distance: Float,
+): SwipeAnimation<*> {
+    return createSwipeAnimation(
+        layoutState,
+        result,
+        isUpOrLeft,
+        orientation,
+        distance = { distance },
+        contentForUserActions = {
+            error("Computing contentForUserActions requires a SceneTransitionLayoutImpl")
+        },
+    )
+}
+
+internal fun createSwipeAnimation(
+    layoutImpl: SceneTransitionLayoutImpl,
+    result: UserActionResult,
+    isUpOrLeft: Boolean,
+    orientation: Orientation,
+    distance: Float = DistanceUnspecified
+): SwipeAnimation<*> {
+    var lastDistance = distance
+
+    fun distance(animation: SwipeAnimation<*>): Float {
+        if (lastDistance != DistanceUnspecified) {
+            return lastDistance
+        }
+
+        val absoluteDistance =
+            with(animation.contentTransition.transformationSpec.distance ?: DefaultSwipeDistance) {
+                layoutImpl.userActionDistanceScope.absoluteDistance(
+                    layoutImpl.content(animation.fromContent).targetSize,
+                    orientation,
+                )
+            }
+
+        if (absoluteDistance <= 0f) {
+            return DistanceUnspecified
+        }
+
+        val distance = if (isUpOrLeft) -absoluteDistance else absoluteDistance
+        lastDistance = distance
+        return distance
+    }
+
+    return createSwipeAnimation(
+        layoutImpl.state,
+        result,
+        isUpOrLeft,
+        orientation,
+        distance = ::distance,
+        contentForUserActions = { layoutImpl.contentForUserActions().key },
+    )
+}
+
+private fun createSwipeAnimation(
+    layoutState: MutableSceneTransitionLayoutStateImpl,
+    result: UserActionResult,
+    isUpOrLeft: Boolean,
+    orientation: Orientation,
+    distance: (SwipeAnimation<*>) -> Float,
+    contentForUserActions: () -> ContentKey,
+): SwipeAnimation<*> {
+    fun <T : ContentKey> swipeAnimation(fromContent: T, toContent: T): SwipeAnimation<T> {
+        return SwipeAnimation(
+            layoutState = layoutState,
+            fromContent = fromContent,
+            toContent = toContent,
+            orientation = orientation,
+            isUpOrLeft = isUpOrLeft,
+            requiresFullDistanceSwipe = result.requiresFullDistanceSwipe,
+            distance = distance,
+        )
+    }
+
+    return when (result) {
+        is UserActionResult.ChangeScene -> {
+            val fromScene = layoutState.currentScene
+            val toScene = result.toScene
+            ChangeSceneSwipeTransition(
+                    layoutState = layoutState,
+                    swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = toScene),
+                    key = result.transitionKey,
+                    replacedTransition = null,
+                )
+                .swipeAnimation
+        }
+        is UserActionResult.ShowOverlay -> {
+            val fromScene = layoutState.currentScene
+            val overlay = result.overlay
+            ShowOrHideOverlaySwipeTransition(
+                    layoutState = layoutState,
+                    fromOrToScene = fromScene,
+                    overlay = overlay,
+                    swipeAnimation = swipeAnimation(fromContent = fromScene, toContent = overlay),
+                    key = result.transitionKey,
+                    replacedTransition = null,
+                )
+                .swipeAnimation
+        }
+        is UserActionResult.HideOverlay -> {
+            val toScene = layoutState.currentScene
+            val overlay = result.overlay
+            ShowOrHideOverlaySwipeTransition(
+                    layoutState = layoutState,
+                    fromOrToScene = toScene,
+                    overlay = overlay,
+                    swipeAnimation = swipeAnimation(fromContent = overlay, toContent = toScene),
+                    key = result.transitionKey,
+                    replacedTransition = null,
+                )
+                .swipeAnimation
+        }
+        is UserActionResult.ReplaceByOverlay -> {
+            val fromOverlay =
+                when (val contentForUserActions = contentForUserActions()) {
+                    is SceneKey ->
+                        error("ReplaceByOverlay can only be called when an overlay is shown")
+                    is OverlayKey -> contentForUserActions
+                }
+
+            val toOverlay = result.overlay
+            ReplaceOverlaySwipeTransition(
+                    layoutState = layoutState,
+                    swipeAnimation =
+                        swipeAnimation(fromContent = fromOverlay, toContent = toOverlay),
+                    key = result.transitionKey,
+                    replacedTransition = null,
+                )
+                .swipeAnimation
+        }
+    }
+}
+
+internal fun createSwipeAnimation(old: SwipeAnimation<*>): SwipeAnimation<*> {
+    return when (val transition = old.contentTransition) {
+        is TransitionState.Transition.ChangeScene -> {
+            ChangeSceneSwipeTransition(transition as ChangeSceneSwipeTransition).swipeAnimation
+        }
+        is TransitionState.Transition.ShowOrHideOverlay -> {
+            ShowOrHideOverlaySwipeTransition(transition as ShowOrHideOverlaySwipeTransition)
+                .swipeAnimation
+        }
+        is TransitionState.Transition.ReplaceOverlay -> {
+            ReplaceOverlaySwipeTransition(transition as ReplaceOverlaySwipeTransition)
+                .swipeAnimation
+        }
+    }
+}
+
+/** A helper class that contains the main logic for swipe transitions. */
+internal class SwipeAnimation<T : ContentKey>(
+    val layoutState: MutableSceneTransitionLayoutStateImpl,
+    val fromContent: T,
+    val toContent: T,
+    override val orientation: Orientation,
+    override val isUpOrLeft: Boolean,
+    val requiresFullDistanceSwipe: Boolean,
+    private val distance: (SwipeAnimation<T>) -> Float,
+    currentContent: T = fromContent,
+    dragOffset: Float = 0f,
+) : TransitionState.HasOverscrollProperties {
+    /** The [TransitionState.Transition] whose implementation delegates to this [SwipeAnimation]. */
+    lateinit var contentTransition: TransitionState.Transition
+
+    private var _currentContent by mutableStateOf(currentContent)
+    var currentContent: T
+        get() = _currentContent
+        set(value) {
+            check(!isAnimatingOffset()) {
+                "currentContent can not be changed once we are animating the offset"
+            }
+            _currentContent = value
+        }
+
+    val progress: Float
+        get() {
+            // Important: If we are going to return early because distance is equal to 0, we should
+            // still make sure we read the offset before returning so that the calling code still
+            // subscribes to the offset value.
+            val animatable = offsetAnimation
+            val offset =
+                when {
+                    isInPreviewStage -> 0f
+                    animatable != null -> animatable.value
+                    else -> dragOffset
+                }
+
+            return computeProgress(offset)
+        }
+
+    fun computeProgress(offset: Float): Float {
+        val distance = distance()
+        if (distance == DistanceUnspecified) {
+            return 0f
+        }
+        return offset / distance
+    }
+
+    val progressVelocity: Float
+        get() {
+            val animatable = offsetAnimation ?: return 0f
+            val distance = distance()
+            if (distance == DistanceUnspecified) {
+                return 0f
+            }
+
+            val velocityInDistanceUnit = animatable.velocity
+            return velocityInDistanceUnit / distance.absoluteValue
+        }
+
+    val previewProgress: Float
+        get() {
+            val offset =
+                if (isInPreviewStage) {
+                    offsetAnimation?.value ?: dragOffset
+                } else {
+                    dragOffset
+                }
+            return computeProgress(offset)
+        }
+
+    val previewProgressVelocity: Float
+        get() = 0f
+
+    val isInPreviewStage: Boolean
+        get() = contentTransition.previewTransformationSpec != null && currentContent == fromContent
+
+    override var bouncingContent: ContentKey? = null
+
+    /** The current offset caused by the drag gesture. */
+    var dragOffset by mutableFloatStateOf(dragOffset)
+
+    /** The offset animation that animates the offset once the user lifts their finger. */
+    private var offsetAnimation: Animatable<Float, AnimationVector1D>? by mutableStateOf(null)
+    private val offsetAnimationRunnable = CompletableDeferred<(suspend () -> Unit)?>()
+
+    val isUserInputOngoing: Boolean
+        get() = offsetAnimation == null
+
+    override val absoluteDistance: Float
+        get() = distance().absoluteValue
+
+    constructor(
+        other: SwipeAnimation<T>
+    ) : this(
+        layoutState = other.layoutState,
+        fromContent = other.fromContent,
+        toContent = other.toContent,
+        orientation = other.orientation,
+        isUpOrLeft = other.isUpOrLeft,
+        requiresFullDistanceSwipe = other.requiresFullDistanceSwipe,
+        distance = other.distance,
+        currentContent = other.currentContent,
+        dragOffset = other.offsetAnimation?.value ?: other.dragOffset,
+    )
+
+    suspend fun run() {
+        // This animation will first be driven by finger, then when the user lift their finger we
+        // start an animation to the target offset (progress = 1f or progress = 0f). We await() for
+        // offsetAnimationRunnable to be completed and then run it.
+        val runAnimation = offsetAnimationRunnable.await() ?: return
+        runAnimation()
+    }
+
+    /**
+     * The signed distance between [fromContent] and [toContent]. It is negative if [fromContent] is
+     * above or to the left of [toContent].
+     *
+     * Note that this distance can be equal to [DistanceUnspecified] during the first frame of a
+     * transition when the distance depends on the size or position of an element that is composed
+     * in the content we are going to.
+     */
+    fun distance(): Float = distance(this)
+
+    fun isAnimatingOffset(): Boolean = offsetAnimation != null
+
+    fun animateOffset(
+        initialVelocity: Float,
+        targetContent: T,
+        spec: AnimationSpec<Float>? = null,
+    ) {
+        check(!isAnimatingOffset()) { "SwipeAnimation.animateOffset() can only be called once" }
+
+        val initialProgress = progress
+        // Skip the animation if we have already reached the target content and the overscroll does
+        // not animate anything.
+        val hasReachedTargetContent =
+            (targetContent == toContent && initialProgress >= 1f) ||
+                (targetContent == fromContent && initialProgress <= 0f)
+        val skipAnimation =
+            hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
+
+        val targetContent =
+            if (targetContent != currentContent && !canChangeContent(targetContent)) {
+                currentContent
+            } else {
+                targetContent
+            }
+
+        val targetOffset =
+            if (targetContent == fromContent) {
+                0f
+            } else {
+                val distance = distance()
+                check(distance != DistanceUnspecified) {
+                    "distance is equal to $DistanceUnspecified"
+                }
+                distance
+            }
+
+        // If the effective current content changed, it should be reflected right now in the
+        // current state, even before the settle animation is ongoing. That way all the
+        // swipeables and back handlers will be refreshed and the user can for instance quickly
+        // swipe vertically from A => B then horizontally from B => C, or swipe from A => B then
+        // immediately go back B => A.
+        if (targetContent != currentContent) {
+            currentContent = targetContent
+        }
+
+        val startProgress =
+            if (contentTransition.previewTransformationSpec != null && targetContent == toContent) {
+                0f
+            } else {
+                dragOffset
+            }
+
+        val animatable =
+            Animatable(startProgress, OffsetVisibilityThreshold).also { offsetAnimation = it }
+
+        check(isAnimatingOffset())
+
+        // Note: we still create the animatable and set it on offsetAnimation even when
+        // skipAnimation is true, just so that isUserInputOngoing and isAnimatingOffset() are
+        // unchanged even despite this small skip-optimization (which is just an implementation
+        // detail).
+        if (skipAnimation) {
+            // Unblock the job.
+            offsetAnimationRunnable.complete(null)
+            return
+        }
+
+        val isTargetGreater = targetOffset > animatable.value
+        val startedWhenOvercrollingTargetContent =
+            if (targetContent == fromContent) initialProgress < 0f else initialProgress > 1f
+
+        val swipeSpec =
+            spec
+                ?: contentTransition.transformationSpec.swipeSpec
+                ?: layoutState.transitions.defaultSwipeSpec
+
+        offsetAnimationRunnable.complete {
+            try {
+                animatable.animateTo(
+                    targetValue = targetOffset,
+                    animationSpec = swipeSpec,
+                    initialVelocity = initialVelocity,
+                ) {
+                    if (bouncingContent == null) {
+                        val isBouncing =
+                            if (isTargetGreater) {
+                                if (startedWhenOvercrollingTargetContent) {
+                                    value >= targetOffset
+                                } else {
+                                    value > targetOffset
+                                }
+                            } else {
+                                if (startedWhenOvercrollingTargetContent) {
+                                    value <= targetOffset
+                                } else {
+                                    value < targetOffset
+                                }
+                            }
+
+                        if (isBouncing) {
+                            bouncingContent = targetContent
+
+                            // Immediately stop this transition if we are bouncing on a content that
+                            // does not bounce.
+                            if (!contentTransition.isWithinProgressRange(progress)) {
+                                throw SnapException()
+                            }
+                        }
+                    }
+                }
+            } catch (_: SnapException) {
+                /* Ignore. */
+            }
+        }
+    }
+
+    /** An exception thrown during the animation to stop it immediately. */
+    private class SnapException : Exception()
+
+    private fun canChangeContent(targetContent: ContentKey): Boolean {
+        return when (val transition = contentTransition) {
+            is TransitionState.Transition.ChangeScene ->
+                layoutState.canChangeScene(targetContent as SceneKey)
+            is TransitionState.Transition.ShowOrHideOverlay -> {
+                if (targetContent == transition.overlay) {
+                    layoutState.canShowOverlay(transition.overlay)
+                } else {
+                    layoutState.canHideOverlay(transition.overlay)
+                }
+            }
+            is TransitionState.Transition.ReplaceOverlay -> {
+                val to = targetContent as OverlayKey
+                val from =
+                    if (to == transition.toOverlay) transition.fromOverlay else transition.toOverlay
+                layoutState.canReplaceOverlay(from, to)
+            }
+        }
+    }
+
+    fun freezeAndAnimateToCurrentState() {
+        if (isAnimatingOffset()) return
+
+        animateOffset(initialVelocity = 0f, targetContent = currentContent)
+    }
+}
+
+private object DefaultSwipeDistance : UserActionDistance {
+    override fun UserActionDistanceScope.absoluteDistance(
+        fromSceneSize: IntSize,
+        orientation: Orientation,
+    ): Float {
+        return when (orientation) {
+            Orientation.Horizontal -> fromSceneSize.width
+            Orientation.Vertical -> fromSceneSize.height
+        }.toFloat()
+    }
+}
+
+private class ChangeSceneSwipeTransition(
+    val layoutState: MutableSceneTransitionLayoutStateImpl,
+    val swipeAnimation: SwipeAnimation<SceneKey>,
+    override val key: TransitionKey?,
+    replacedTransition: ChangeSceneSwipeTransition?,
+) :
+    TransitionState.Transition.ChangeScene(
+        swipeAnimation.fromContent,
+        swipeAnimation.toContent,
+        replacedTransition,
+    ),
+    TransitionState.HasOverscrollProperties by swipeAnimation {
+
+    constructor(
+        other: ChangeSceneSwipeTransition
+    ) : this(
+        layoutState = other.layoutState,
+        swipeAnimation = SwipeAnimation(other.swipeAnimation),
+        key = other.key,
+        replacedTransition = other,
+    )
+
+    init {
+        swipeAnimation.contentTransition = this
+    }
+
+    override val currentScene: SceneKey
+        get() = swipeAnimation.currentContent
+
+    override val progress: Float
+        get() = swipeAnimation.progress
+
+    override val progressVelocity: Float
+        get() = swipeAnimation.progressVelocity
+
+    override val previewProgress: Float
+        get() = swipeAnimation.previewProgress
+
+    override val previewProgressVelocity: Float
+        get() = swipeAnimation.previewProgressVelocity
+
+    override val isInPreviewStage: Boolean
+        get() = swipeAnimation.isInPreviewStage
+
+    override val isInitiatedByUserInput: Boolean = true
+
+    override val isUserInputOngoing: Boolean
+        get() = swipeAnimation.isUserInputOngoing
+
+    override suspend fun run() {
+        swipeAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        swipeAnimation.freezeAndAnimateToCurrentState()
+    }
+}
+
+private class ShowOrHideOverlaySwipeTransition(
+    val layoutState: MutableSceneTransitionLayoutStateImpl,
+    val swipeAnimation: SwipeAnimation<ContentKey>,
+    overlay: OverlayKey,
+    fromOrToScene: SceneKey,
+    override val key: TransitionKey?,
+    replacedTransition: ShowOrHideOverlaySwipeTransition?,
+) :
+    TransitionState.Transition.ShowOrHideOverlay(
+        overlay,
+        fromOrToScene,
+        swipeAnimation.fromContent,
+        swipeAnimation.toContent,
+        replacedTransition,
+    ),
+    TransitionState.HasOverscrollProperties by swipeAnimation {
+    constructor(
+        other: ShowOrHideOverlaySwipeTransition
+    ) : this(
+        layoutState = other.layoutState,
+        swipeAnimation = SwipeAnimation(other.swipeAnimation),
+        overlay = other.overlay,
+        fromOrToScene = other.fromOrToScene,
+        key = other.key,
+        replacedTransition = other,
+    )
+
+    init {
+        swipeAnimation.contentTransition = this
+    }
+
+    override val isEffectivelyShown: Boolean
+        get() = swipeAnimation.currentContent == overlay
+
+    override val progress: Float
+        get() = swipeAnimation.progress
+
+    override val progressVelocity: Float
+        get() = swipeAnimation.progressVelocity
+
+    override val previewProgress: Float
+        get() = swipeAnimation.previewProgress
+
+    override val previewProgressVelocity: Float
+        get() = swipeAnimation.previewProgressVelocity
+
+    override val isInPreviewStage: Boolean
+        get() = swipeAnimation.isInPreviewStage
+
+    override val isInitiatedByUserInput: Boolean = true
+
+    override val isUserInputOngoing: Boolean
+        get() = swipeAnimation.isUserInputOngoing
+
+    override suspend fun run() {
+        swipeAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        swipeAnimation.freezeAndAnimateToCurrentState()
+    }
+}
+
+private class ReplaceOverlaySwipeTransition(
+    val layoutState: MutableSceneTransitionLayoutStateImpl,
+    val swipeAnimation: SwipeAnimation<OverlayKey>,
+    override val key: TransitionKey?,
+    replacedTransition: ReplaceOverlaySwipeTransition?,
+) :
+    TransitionState.Transition.ReplaceOverlay(
+        swipeAnimation.fromContent,
+        swipeAnimation.toContent,
+        replacedTransition,
+    ),
+    TransitionState.HasOverscrollProperties by swipeAnimation {
+    constructor(
+        other: ReplaceOverlaySwipeTransition
+    ) : this(
+        layoutState = other.layoutState,
+        swipeAnimation = SwipeAnimation(other.swipeAnimation),
+        key = other.key,
+        replacedTransition = other,
+    )
+
+    init {
+        swipeAnimation.contentTransition = this
+    }
+
+    override val effectivelyShownOverlay: OverlayKey
+        get() = swipeAnimation.currentContent
+
+    override val progress: Float
+        get() = swipeAnimation.progress
+
+    override val progressVelocity: Float
+        get() = swipeAnimation.progressVelocity
+
+    override val previewProgress: Float
+        get() = swipeAnimation.previewProgress
+
+    override val previewProgressVelocity: Float
+        get() = swipeAnimation.previewProgressVelocity
+
+    override val isInPreviewStage: Boolean
+        get() = swipeAnimation.isInPreviewStage
+
+    override val isInitiatedByUserInput: Boolean = true
+
+    override val isUserInputOngoing: Boolean
+        get() = swipeAnimation.isUserInputOngoing
+
+    override suspend fun run() {
+        swipeAnimation.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        swipeAnimation.freezeAndAnimateToCurrentState()
+    }
+}
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 f062146..dc7eda5 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
@@ -31,7 +31,7 @@
 import androidx.compose.ui.node.TraversableNode
 import androidx.compose.ui.node.findNearestAncestor
 import androidx.compose.ui.unit.IntSize
-import com.android.compose.animation.scene.content.Scene
+import com.android.compose.animation.scene.content.Content
 
 /**
  * Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state.
@@ -67,6 +67,7 @@
                 enabled = ::enabled,
                 startDragImmediately = ::startDragImmediately,
                 onDragStarted = draggableHandler::onDragStarted,
+                onFirstPointerDown = ::onFirstPointerDown,
                 swipeDetector = swipeDetector,
                 dispatcher = dispatcher,
             )
@@ -101,6 +102,15 @@
         delegate(ScrollBehaviorOwnerNode(draggableHandler.nestedScrollKey, nestedScrollHandlerImpl))
     }
 
+    private fun onFirstPointerDown() {
+        // When we drag our finger across the screen, the NestedScrollConnection keeps track of all
+        // the scroll events until we lift our finger. However, in some cases, the connection might
+        // not receive the "up" event. This can lead to an incorrect initial state for the gesture.
+        // To prevent this issue, we can call the reset() method when the first finger touches the
+        // screen. This ensures that the NestedScrollConnection starts from a correct state.
+        nestedScrollHandlerImpl.connection.reset()
+    }
+
     override fun onDetach() {
         // Make sure we reset the scroll connection when this modifier is removed from composition
         nestedScrollHandlerImpl.connection.reset()
@@ -116,16 +126,15 @@
 
     private fun enabled(): Boolean {
         return draggableHandler.isDrivingTransition ||
-            currentScene().shouldEnableSwipes(multiPointerDraggableNode.orientation)
+            contentForSwipes().shouldEnableSwipes(multiPointerDraggableNode.orientation)
     }
 
-    private fun currentScene(): Scene {
-        val layoutImpl = draggableHandler.layoutImpl
-        return layoutImpl.scene(layoutImpl.state.transitionState.currentScene)
+    private fun contentForSwipes(): Content {
+        return draggableHandler.layoutImpl.contentForUserActions()
     }
 
     /** Whether swipe should be enabled in the given [orientation]. */
-    private fun Scene.shouldEnableSwipes(orientation: Orientation): Boolean {
+    private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
         return userActions.keys.any {
             it is Swipe.Resolved && it.direction.orientation == orientation
         }
@@ -143,7 +152,7 @@
                 Orientation.Vertical -> Orientation.Horizontal
                 Orientation.Horizontal -> Orientation.Vertical
             }
-        return currentScene().shouldEnableSwipes(oppositeOrientation)
+        return contentForSwipes().shouldEnableSwipes(oppositeOrientation)
     }
 }
 
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 a30b780..1f82e0b 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
@@ -17,13 +17,16 @@
 package com.android.compose.animation.scene
 
 import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
+import kotlin.math.tanh
 
 /** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */
 fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -47,6 +50,12 @@
     var interruptionHandler: InterruptionHandler
 
     /**
+     * Default [ProgressConverter] used during overscroll. It lets you change a linear progress into
+     * a function of your choice. Defaults to [ProgressConverter.Default].
+     */
+    var defaultOverscrollProgressConverter: ProgressConverter
+
+    /**
      * Define the default animation to be played when transitioning [to] the specified content, from
      * any content. For the animation specification to apply only when transitioning between two
      * specific contents, use [from] instead.
@@ -95,23 +104,23 @@
     ): TransitionSpec
 
     /**
-     * Define the animation to be played when the [scene] is overscrolled in the given
+     * Define the animation to be played when the [content] is overscrolled in the given
      * [orientation].
      *
      * The overscroll animation always starts from a progress of 0f, and reaches 1f when moving the
      * [distance] down/right, -1f when moving in the opposite direction.
      */
     fun overscroll(
-        scene: SceneKey,
+        content: ContentKey,
         orientation: Orientation,
         builder: OverscrollBuilder.() -> Unit,
     ): OverscrollSpec
 
     /**
-     * Prevents overscroll the [scene] in the given [orientation], allowing ancestors to eventually
-     * consume the remaining gesture.
+     * Prevents overscroll the [content] in the given [orientation], allowing ancestors to
+     * eventually consume the remaining gesture.
      */
-    fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec
+    fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec
 }
 
 interface BaseTransitionBuilder : PropertyTransformationBuilder {
@@ -140,6 +149,7 @@
     fun fractionRange(
         start: Float? = null,
         end: Float? = null,
+        easing: Easing = LinearEasing,
         builder: PropertyTransformationBuilder.() -> Unit,
     )
 }
@@ -182,6 +192,7 @@
     fun timestampRange(
         startMillis: Int? = null,
         endMillis: Int? = null,
+        easing: Easing = LinearEasing,
         builder: PropertyTransformationBuilder.() -> Unit,
     )
 
@@ -212,7 +223,7 @@
      * - 1, the user overscrolled by exactly the [distance].
      * - Greater than 1, the user overscrolled more than the [distance].
      */
-    var progressConverter: (Float) -> Float
+    var progressConverter: ProgressConverter?
 
     /** Translate the element(s) matching [matcher] by ([x], [y]) pixels. */
     fun translate(
@@ -241,9 +252,7 @@
 interface ElementContentPicker {
     /**
      * Return the content in which [element] should be drawn (when using `Modifier.element(key)`) or
-     * composed (when using `MovableElement(key)`) during the given [transition]. If this element
-     * should not be drawn or composed in neither [transition.fromContent] nor
-     * [transition.toContent], return `null`.
+     * composed (when using `MovableElement(key)`) during the given [transition].
      *
      * Important: For [MovableElements][ContentScope.MovableElement], this content picker will
      * *always* be used during transitions to decide whether we should compose that element in a
@@ -253,7 +262,7 @@
      */
     fun contentDuringTransition(
         element: ElementKey,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         fromContentZIndex: Float,
         toContentZIndex: Float,
     ): ContentKey
@@ -269,7 +278,7 @@
      */
     fun pickSingleContentIn(
         contents: Set<ContentKey>,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         element: ElementKey,
     ): ContentKey {
         val fromContent = transition.fromContent
@@ -322,7 +331,7 @@
 object HighestZIndexContentPicker : ElementContentPicker {
     override fun contentDuringTransition(
         element: ElementKey,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         fromContentZIndex: Float,
         toContentZIndex: Float
     ): ContentKey {
@@ -343,7 +352,7 @@
 
             override fun contentDuringTransition(
                 element: ElementKey,
-                transition: ContentState.Transition<*>,
+                transition: TransitionState.Transition,
                 fromContentZIndex: Float,
                 toContentZIndex: Float
             ): ContentKey {
@@ -364,7 +373,7 @@
 object LowestZIndexContentPicker : ElementContentPicker {
     override fun contentDuringTransition(
         element: ElementKey,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         fromContentZIndex: Float,
         toContentZIndex: Float
     ): ContentKey {
@@ -385,7 +394,7 @@
 
             override fun contentDuringTransition(
                 element: ElementKey,
-                transition: ContentState.Transition<*>,
+                transition: TransitionState.Transition,
                 fromContentZIndex: Float,
                 toContentZIndex: Float
             ): ContentKey {
@@ -419,7 +428,7 @@
 ) : StaticElementContentPicker {
     override fun contentDuringTransition(
         element: ElementKey,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         fromContentZIndex: Float,
         toContentZIndex: Float,
     ): ContentKey {
@@ -508,3 +517,35 @@
         anchorHeight: Boolean = true,
     )
 }
+
+/** This converter lets you change a linear progress into a function of your choice. */
+fun interface ProgressConverter {
+    fun convert(progress: Float): Float
+
+    companion object {
+        /** Starts linearly with some resistance and slowly approaches to 0.2f */
+        val Default = tanh(maxProgress = 0.2f, tilt = 3f)
+
+        /**
+         * The scroll stays linear, with [factor] you can control how much resistance there is.
+         *
+         * @param factor If you choose a value between 0f and 1f, the progress will grow more
+         *   slowly, like there's resistance. A value of 1f means there's no resistance.
+         */
+        fun linear(factor: Float = 1f) = ProgressConverter { it * factor }
+
+        /**
+         * This function starts linear and slowly approaches [maxProgress].
+         *
+         * See a [visual representation](https://www.desmos.com/calculator/usgvvf0z1u) of this
+         * function.
+         *
+         * @param maxProgress is the maximum progress value.
+         * @param tilt behaves similarly to the factor in the [linear] function, and allows you to
+         *   control how quickly you get to the [maxProgress].
+         */
+        fun tanh(maxProgress: Float, tilt: Float = 1f) = ProgressConverter {
+            maxProgress * tanh(x = it / (maxProgress * tilt))
+        }
+    }
+}
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 6515cb8..da4c8d8 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
@@ -18,6 +18,7 @@
 
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.DurationBasedAnimationSpec
+import androidx.compose.animation.core.Easing
 import androidx.compose.animation.core.Spring
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.VectorConverter
@@ -49,12 +50,14 @@
         impl.transitionSpecs,
         impl.transitionOverscrollSpecs,
         impl.interruptionHandler,
+        impl.defaultOverscrollProgressConverter,
     )
 }
 
 private class SceneTransitionsBuilderImpl : SceneTransitionsBuilder {
     override var defaultSwipeSpec: SpringSpec<Float> = SceneTransitions.DefaultSwipeSpec
     override var interruptionHandler: InterruptionHandler = DefaultInterruptionHandler
+    override var defaultOverscrollProgressConverter: ProgressConverter = ProgressConverter.Default
 
     val transitionSpecs = mutableListOf<TransitionSpecImpl>()
     val transitionOverscrollSpecs = mutableListOf<OverscrollSpecImpl>()
@@ -81,30 +84,30 @@
     }
 
     override fun overscroll(
-        scene: SceneKey,
+        content: ContentKey,
         orientation: Orientation,
         builder: OverscrollBuilder.() -> Unit
     ): OverscrollSpec {
         val impl = OverscrollBuilderImpl().apply(builder)
         check(impl.transformations.isNotEmpty()) {
             "This method does not allow empty transformations. " +
-                "Use overscrollDisabled($scene, $orientation) instead."
+                "Use overscrollDisabled($content, $orientation) instead."
         }
-        return overscrollSpec(scene, orientation, impl)
+        return overscrollSpec(content, orientation, impl)
     }
 
-    override fun overscrollDisabled(scene: SceneKey, orientation: Orientation): OverscrollSpec {
-        return overscrollSpec(scene, orientation, OverscrollBuilderImpl())
+    override fun overscrollDisabled(content: ContentKey, orientation: Orientation): OverscrollSpec {
+        return overscrollSpec(content, orientation, OverscrollBuilderImpl())
     }
 
     private fun overscrollSpec(
-        scene: SceneKey,
+        content: ContentKey,
         orientation: Orientation,
         impl: OverscrollBuilderImpl,
     ): OverscrollSpec {
         val spec =
             OverscrollSpecImpl(
-                scene = scene,
+                content = content,
                 orientation = orientation,
                 transformationSpec =
                     TransformationSpecImpl(
@@ -163,9 +166,10 @@
     override fun fractionRange(
         start: Float?,
         end: Float?,
+        easing: Easing,
         builder: PropertyTransformationBuilder.() -> Unit
     ) {
-        range = TransformationRange(start, end)
+        range = TransformationRange(start, end, easing)
         builder()
         range = null
     }
@@ -251,6 +255,7 @@
     override fun timestampRange(
         startMillis: Int?,
         endMillis: Int?,
+        easing: Easing,
         builder: PropertyTransformationBuilder.() -> Unit
     ) {
         if (startMillis != null && (startMillis < 0 || startMillis > durationMillis)) {
@@ -263,12 +268,12 @@
 
         val start = startMillis?.let { it.toFloat() / durationMillis }
         val end = endMillis?.let { it.toFloat() / durationMillis }
-        fractionRange(start, end, builder)
+        fractionRange(start, end, easing, builder)
     }
 }
 
 internal open class OverscrollBuilderImpl : BaseTransitionBuilderImpl(), OverscrollBuilder {
-    override var progressConverter: (Float) -> Float = { it }
+    override var progressConverter: ProgressConverter? = null
 
     override fun translate(
         matcher: ElementMatcher,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 0f66804..9851b32 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -35,7 +35,7 @@
     }
 
     override fun SceneKey.targetSize(): IntSize? {
-        return layoutImpl.scenes[this]?.targetSize.takeIf { it != IntSize.Zero }
+        return layoutImpl.sceneOrNull(this)?.targetSize.takeIf { it != IntSize.Zero }
     }
 }
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 6f608cb..59dd896 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -66,27 +66,7 @@
     var content by mutableStateOf(content)
     var zIndex by mutableFloatStateOf(zIndex)
     var targetSize by mutableStateOf(IntSize.Zero)
-
-    private var _userActions by mutableStateOf(checkValid(actions))
-    var userActions
-        get() = _userActions
-        set(value) {
-            _userActions = checkValid(value)
-        }
-
-    private fun checkValid(
-        userActions: Map<UserAction.Resolved, UserActionResult>
-    ): Map<UserAction.Resolved, UserActionResult> {
-        userActions.forEach { (action, result) ->
-            if (key == result.toScene) {
-                error(
-                    "Transition to the same content (scene/overlay) is not supported. Content " +
-                        "$key, action $action, result $result"
-                )
-            }
-        }
-        return userActions
-    }
+    var userActions by mutableStateOf(actions)
 
     @Composable
     fun Content(modifier: Modifier = Modifier) {
@@ -96,6 +76,8 @@
                 .approachLayout(
                     isMeasurementApproachInProgress = { layoutImpl.state.isTransitioning() }
                 ) { measurable, constraints ->
+                    // TODO(b/353679003): Use the ModifierNode API to set this *before* the approach
+                    // pass is started.
                     targetSize = lookaheadSize
                     val placeable = measurable.measure(constraints)
                     layout(placeable.width, placeable.height) { placeable.place(0, 0) }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.kt
new file mode 100644
index 0000000..ccec9e8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Overlay.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.compose.animation.scene.content
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+
+/** An overlay defined in a [SceneTransitionLayout]. */
+@Stable
+internal class Overlay(
+    override val key: OverlayKey,
+    layoutImpl: SceneTransitionLayoutImpl,
+    content: @Composable ContentScope.() -> Unit,
+    actions: Map<UserAction.Resolved, UserActionResult>,
+    zIndex: Float,
+    alignment: Alignment,
+) : Content(key, layoutImpl, content, actions, zIndex) {
+    var alignment by mutableStateOf(alignment)
+
+    override fun toString(): String {
+        return "Overlay(key=$key)"
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
deleted file mode 100644
index add3934..0000000
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/ContentState.kt
+++ /dev/null
@@ -1,237 +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.compose.animation.scene.content.state
-
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.animation.core.spring
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.runtime.Stable
-import com.android.compose.animation.scene.ContentKey
-import com.android.compose.animation.scene.OverscrollScope
-import com.android.compose.animation.scene.OverscrollSpecImpl
-import com.android.compose.animation.scene.ProgressVisibilityThreshold
-import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.TransformationSpec
-import com.android.compose.animation.scene.TransformationSpecImpl
-import com.android.compose.animation.scene.TransitionKey
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-
-/** The state associated to one or more contents. */
-@Stable
-sealed interface ContentState<out T : ContentKey> {
-    /** The [content] is idle, it does not animate. */
-    sealed class Idle<T : ContentKey>(val content: T) : ContentState<T>
-
-    /** The content is transitioning with another content. */
-    sealed class Transition<out T : ContentKey>(
-        val fromContent: T,
-        val toContent: T,
-        internal val replacedTransition: Transition<T>?,
-    ) : ContentState<T> {
-        /**
-         * The key of this transition. This should usually be null, but it can be specified to use a
-         * specific set of transformations associated to this transition.
-         */
-        open val key: TransitionKey? = null
-
-        /**
-         * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
-         * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
-         * when flinging quickly during a swipe gesture.
-         */
-        abstract val progress: Float
-
-        /** The current velocity of [progress], in progress units. */
-        abstract val progressVelocity: Float
-
-        /** Whether the transition was triggered by user input rather than being programmatic. */
-        abstract val isInitiatedByUserInput: Boolean
-
-        /** Whether user input is currently driving the transition. */
-        abstract val isUserInputOngoing: Boolean
-
-        /**
-         * The progress of the preview transition. This is usually in the `[0; 1]` range, but it can
-         * also be less than `0` or greater than `1` when using transitions with a spring
-         * AnimationSpec or when flinging quickly during a swipe gesture.
-         */
-        internal open val previewProgress: Float = 0f
-
-        /** The current velocity of [previewProgress], in progress units. */
-        internal open val previewProgressVelocity: Float = 0f
-
-        /** Whether the transition is currently in the preview stage */
-        internal open val isInPreviewStage: Boolean = false
-
-        /**
-         * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this
-         * transition.
-         *
-         * Important: These will be set exactly once, when this transition is
-         * [started][MutableSceneTransitionLayoutStateImpl.startTransition].
-         */
-        internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
-        internal var previewTransformationSpec: TransformationSpecImpl? = null
-        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
-                }
-            }
-
-        /**
-         * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
-         * jump of values when this transitions interrupts another one.
-         */
-        private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
-
-        init {
-            check(fromContent != toContent)
-            check(
-                replacedTransition == null ||
-                    (replacedTransition.fromContent == fromContent &&
-                        replacedTransition.toContent == toContent)
-            )
-        }
-
-        /**
-         * Force this transition to finish and animate to an [Idle] state.
-         *
-         * Important: Once this is called, the effective state of the transition should remain
-         * unchanged. For instance, in the case of a [TransitionState.Transition], its
-         * [currentScene][TransitionState.Transition.currentScene] should never change once [finish]
-         * is called.
-         *
-         * @return the [Job] that animates to the idle state. It can be used to wait until the
-         *   animation is complete or cancel it to snap the animation. Calling [finish] multiple
-         *   times will return the same [Job].
-         */
-        abstract fun finish(): Job
-
-        /**
-         * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
-         * match the contents we are animating from and/or to.
-         */
-        fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
-            return (from == null || fromContent == from) && (to == null || toContent == to)
-        }
-
-        /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
-        fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
-            return isTransitioning(from = content, to = other) ||
-                isTransitioning(from = other, to = content)
-        }
-
-        internal fun updateOverscrollSpecs(
-            fromSpec: OverscrollSpecImpl?,
-            toSpec: OverscrollSpecImpl?,
-        ) {
-            fromOverscrollSpec = fromSpec
-            toOverscrollSpec = toSpec
-        }
-
-        /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */
-        internal fun isWithinProgressRange(progress: Float): Boolean {
-            // If the properties are missing we assume that every [Transition] can overscroll
-            if (this !is HasOverscrollProperties) return true
-            // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet.
-            val specForCurrentScene =
-                when {
-                    progress <= 0f -> fromOverscrollSpec
-                    progress >= 1f -> toOverscrollSpec
-                    else -> null
-                } ?: return true
-
-            return specForCurrentScene.transformationSpec.transformations.isNotEmpty()
-        }
-
-        internal open fun interruptionProgress(
-            layoutImpl: SceneTransitionLayoutImpl,
-        ): Float {
-            if (!layoutImpl.state.enableInterruptions) {
-                return 0f
-            }
-
-            if (replacedTransition != null) {
-                return replacedTransition.interruptionProgress(layoutImpl)
-            }
-
-            fun create(): Animatable<Float, AnimationVector1D> {
-                val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
-                layoutImpl.coroutineScope.launch {
-                    val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec
-                    val progressSpec =
-                        spring(
-                            stiffness = swipeSpec.stiffness,
-                            dampingRatio = swipeSpec.dampingRatio,
-                            visibilityThreshold = ProgressVisibilityThreshold,
-                        )
-                    animatable.animateTo(0f, progressSpec)
-                }
-
-                return animatable
-            }
-
-            val animatable = interruptionDecay ?: create().also { interruptionDecay = it }
-            return animatable.value
-        }
-    }
-
-    interface HasOverscrollProperties {
-        /**
-         * The position of the [Transition.toContent].
-         *
-         * Used to understand the direction of the overscroll.
-         */
-        val isUpOrLeft: Boolean
-
-        /**
-         * The relative orientation between [Transition.fromContent] and [Transition.toContent].
-         *
-         * Used to understand the orientation of the overscroll.
-         */
-        val orientation: Orientation
-
-        /**
-         * Scope which can be used in the Overscroll DSL to define a transformation based on the
-         * distance between [Transition.fromContent] and [Transition.toContent].
-         */
-        val overscrollScope: OverscrollScope
-
-        /**
-         * The content (scene or overlay) around which the transition is currently bouncing. When
-         * not `null`, this transition is currently oscillating around this content and will soon
-         * settle to that content.
-         */
-        val bouncingContent: ContentKey?
-
-        companion object {
-            const val DistanceUnspecified = 0f
-        }
-    }
-}
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 77de22c..a47caaa 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
@@ -16,16 +16,33 @@
 
 package com.android.compose.animation.scene.content.state
 
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.OverscrollSpecImpl
+import com.android.compose.animation.scene.ProgressVisibilityThreshold
 import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.TransformationSpec
+import com.android.compose.animation.scene.TransformationSpecImpl
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.transition.link.LinkedTransition
+import com.android.compose.animation.scene.transition.link.StateLink
+import kotlinx.coroutines.launch
 
-/** The state associated to one or more scenes. */
-// TODO(b/353679003): Rename to SceneState.
+/** The state associated to a [SceneTransitionLayout] at some specific point in time. */
 @Stable
-sealed interface TransitionState : ContentState<SceneKey> {
+sealed interface TransitionState {
     /**
-     * The current effective scene. If a new transition was triggered, it would start from this
-     * scene.
+     * The current effective scene. If a new scene transition was triggered, it would start from
+     * this scene.
      *
      * For instance, when swiping from scene A to scene B, the [currentScene] is A when the swipe
      * gesture starts, but then if the user flings their finger and commits the transition to scene
@@ -34,20 +51,353 @@
      */
     val currentScene: SceneKey
 
+    /**
+     * The current set of overlays. This represents the set of overlays that will be visible on
+     * screen once all transitions are finished.
+     *
+     * @see MutableSceneTransitionLayoutState.showOverlay
+     * @see MutableSceneTransitionLayoutState.hideOverlay
+     * @see MutableSceneTransitionLayoutState.replaceOverlay
+     */
+    val currentOverlays: Set<OverlayKey>
+
     /** The scene [currentScene] is idle. */
     data class Idle(
         override val currentScene: SceneKey,
-    ) : TransitionState, ContentState.Idle<SceneKey>(currentScene)
+        override val currentOverlays: Set<OverlayKey> = emptySet(),
+    ) : TransitionState
 
-    /** There is a transition animating between [fromScene] and [toScene]. */
-    abstract class Transition(
-        /** The scene this transition is starting from. Can't be the same as toScene */
-        val fromScene: SceneKey,
+    sealed class Transition(
+        val fromContent: ContentKey,
+        val toContent: ContentKey,
+        val replacedTransition: Transition? = null,
+    ) : TransitionState {
+        /** A transition animating between [fromScene] and [toScene]. */
+        abstract class ChangeScene(
+            /** The scene this transition is starting from. Can't be the same as toScene */
+            val fromScene: SceneKey,
 
-        /** The scene this transition is going to. Can't be the same as fromScene */
-        val toScene: SceneKey,
+            /** The scene this transition is going to. Can't be the same as fromScene */
+            val toScene: SceneKey,
 
-        /** The transition that `this` transition is replacing, if any. */
-        replacedTransition: Transition? = null,
-    ) : TransitionState, ContentState.Transition<SceneKey>(fromScene, toScene, replacedTransition)
+            /** The transition that `this` transition is replacing, if any. */
+            replacedTransition: Transition? = null,
+        ) : Transition(fromScene, toScene, replacedTransition) {
+            final override val currentOverlays: Set<OverlayKey>
+                get() {
+                    // The set of overlays does not change in a [ChangeCurrentScene] transition.
+                    return currentOverlaysWhenTransitionStarted
+                }
+        }
+
+        /**
+         * A transition that is animating one or more overlays and for which [currentOverlays] will
+         * change over the course of the transition.
+         */
+        sealed class OverlayTransition(
+            fromContent: ContentKey,
+            toContent: ContentKey,
+            replacedTransition: Transition?,
+        ) : Transition(fromContent, toContent, replacedTransition) {
+            final override val currentScene: SceneKey
+                get() {
+                    // The current scene does not change during overlay transitions.
+                    return currentSceneWhenTransitionStarted
+                }
+
+            // Note: We use deriveStateOf() so that the computed set is cached and reused when the
+            // inputs of the computations don't change, to avoid recomputing and allocating a new
+            // set every time currentOverlays is called (which is every frame and for each element).
+            final override val currentOverlays: Set<OverlayKey> by derivedStateOf {
+                computeCurrentOverlays()
+            }
+
+            protected abstract fun computeCurrentOverlays(): Set<OverlayKey>
+        }
+
+        /** The [overlay] is either showing from [fromOrToScene] or hiding into [fromOrToScene]. */
+        abstract class ShowOrHideOverlay(
+            val overlay: OverlayKey,
+            val fromOrToScene: SceneKey,
+            fromContent: ContentKey,
+            toContent: ContentKey,
+            replacedTransition: Transition? = null,
+        ) : OverlayTransition(fromContent, toContent, replacedTransition) {
+            /**
+             * Whether [overlay] is effectively shown. For instance, this will be `false` when
+             * starting a swipe transition to show [overlay] and will be `true` only once the swipe
+             * transition is committed.
+             */
+            protected abstract val isEffectivelyShown: Boolean
+
+            init {
+                check(
+                    (fromContent == fromOrToScene && toContent == overlay) ||
+                        (fromContent == overlay && toContent == fromOrToScene)
+                )
+            }
+
+            final override fun computeCurrentOverlays(): Set<OverlayKey> {
+                return if (isEffectivelyShown) {
+                    currentOverlaysWhenTransitionStarted + overlay
+                } else {
+                    currentOverlaysWhenTransitionStarted - overlay
+                }
+            }
+        }
+
+        /** We are transitioning from [fromOverlay] to [toOverlay]. */
+        abstract class ReplaceOverlay(
+            val fromOverlay: OverlayKey,
+            val toOverlay: OverlayKey,
+            replacedTransition: Transition? = null,
+        ) :
+            OverlayTransition(
+                fromContent = fromOverlay,
+                toContent = toOverlay,
+                replacedTransition,
+            ) {
+            /**
+             * The current effective overlay, either [fromOverlay] or [toOverlay]. For instance,
+             * this will be [fromOverlay] when starting a swipe transition that replaces
+             * [fromOverlay] by [toOverlay] and will [toOverlay] once the swipe transition is
+             * committed.
+             */
+            protected abstract val effectivelyShownOverlay: OverlayKey
+
+            init {
+                check(fromOverlay != toOverlay)
+            }
+
+            final override fun computeCurrentOverlays(): Set<OverlayKey> {
+                return when (effectivelyShownOverlay) {
+                    fromOverlay ->
+                        computeCurrentOverlays(include = fromOverlay, exclude = toOverlay)
+                    toOverlay -> computeCurrentOverlays(include = toOverlay, exclude = fromOverlay)
+                    else ->
+                        error(
+                            "effectivelyShownOverlay=$effectivelyShownOverlay, should be " +
+                                "equal to fromOverlay=$fromOverlay or toOverlay=$toOverlay"
+                        )
+                }
+            }
+
+            private fun computeCurrentOverlays(
+                include: OverlayKey,
+                exclude: OverlayKey
+            ): Set<OverlayKey> {
+                return buildSet {
+                    addAll(currentOverlaysWhenTransitionStarted)
+                    remove(exclude)
+                    add(include)
+                }
+            }
+        }
+
+        /**
+         * The current scene and overlays observed right when this transition started. These are set
+         * when this transition is started in
+         * [com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl.startTransition].
+         */
+        internal lateinit var currentSceneWhenTransitionStarted: SceneKey
+        internal lateinit var currentOverlaysWhenTransitionStarted: Set<OverlayKey>
+
+        /**
+         * The key of this transition. This should usually be null, but it can be specified to use a
+         * specific set of transformations associated to this transition.
+         */
+        open val key: TransitionKey? = null
+
+        /**
+         * The progress of the transition. This is usually in the `[0; 1]` range, but it can also be
+         * less than `0` or greater than `1` when using transitions with a spring AnimationSpec or
+         * when flinging quickly during a swipe gesture.
+         */
+        abstract val progress: Float
+
+        /** The current velocity of [progress], in progress units. */
+        abstract val progressVelocity: Float
+
+        /** Whether the transition was triggered by user input rather than being programmatic. */
+        abstract val isInitiatedByUserInput: Boolean
+
+        /** Whether user input is currently driving the transition. */
+        abstract val isUserInputOngoing: Boolean
+
+        /**
+         * The progress of the preview transition. This is usually in the `[0; 1]` range, but it can
+         * also be less than `0` or greater than `1` when using transitions with a spring
+         * AnimationSpec or when flinging quickly during a swipe gesture.
+         */
+        internal open val previewProgress: Float = 0f
+
+        /** The current velocity of [previewProgress], in progress units. */
+        internal open val previewProgressVelocity: Float = 0f
+
+        /** Whether the transition is currently in the preview stage */
+        internal open val isInPreviewStage: Boolean = false
+
+        /**
+         * The current [TransformationSpecImpl] and [OverscrollSpecImpl] associated to this
+         * transition.
+         *
+         * Important: These will be set exactly once, when this transition is
+         * [started][MutableSceneTransitionLayoutStateImpl.startTransition].
+         */
+        internal var transformationSpec: TransformationSpecImpl = TransformationSpec.Empty
+        internal var previewTransformationSpec: TransformationSpecImpl? = null
+        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
+                }
+            }
+
+        /**
+         * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
+         * jump of values when this transitions interrupts another one.
+         */
+        private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null
+
+        /** The map of active links that connects this transition to other transitions. */
+        internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+
+        init {
+            check(fromContent != toContent)
+            check(
+                replacedTransition == null ||
+                    (replacedTransition.fromContent == fromContent &&
+                        replacedTransition.toContent == toContent)
+            )
+        }
+
+        /**
+         * Whether we are transitioning. If [from] or [to] is empty, we will also check that they
+         * match the contents we are animating from and/or to.
+         */
+        fun isTransitioning(from: ContentKey? = null, to: ContentKey? = null): Boolean {
+            return (from == null || fromContent == from) && (to == null || toContent == to)
+        }
+
+        /** Whether we are transitioning from [content] to [other], or from [other] to [content]. */
+        fun isTransitioningBetween(content: ContentKey, other: ContentKey): Boolean {
+            return isTransitioning(from = content, to = other) ||
+                isTransitioning(from = other, to = content)
+        }
+
+        /** Whether we are transitioning from or to [content]. */
+        fun isTransitioningFromOrTo(content: ContentKey): Boolean {
+            return fromContent == content || toContent == content
+        }
+
+        /** Run this transition and return once it is finished. */
+        internal abstract suspend fun run()
+
+        /**
+         * Freeze this transition state so that neither [currentScene] nor [currentOverlays] will
+         * change in the future, and animate the progress towards that state. For instance, a
+         * [Transition.ChangeScene] should animate the progress to 0f if its [currentScene] is equal
+         * to its [fromScene][Transition.ChangeScene.fromScene] or animate it to 1f if its equal to
+         * its [toScene][Transition.ChangeScene.toScene].
+         *
+         * This is called when this transition is interrupted (replaced) by another transition.
+         */
+        internal abstract fun freezeAndAnimateToCurrentState()
+
+        internal fun updateOverscrollSpecs(
+            fromSpec: OverscrollSpecImpl?,
+            toSpec: OverscrollSpecImpl?,
+        ) {
+            fromOverscrollSpec = fromSpec
+            toOverscrollSpec = toSpec
+        }
+
+        /** Returns if the [progress] value of this transition can go beyond range `[0; 1]` */
+        internal fun isWithinProgressRange(progress: Float): Boolean {
+            // If the properties are missing we assume that every [Transition] can overscroll
+            if (this !is HasOverscrollProperties) return true
+            // [OverscrollSpec] for the current scene, even if it hasn't started overscrolling yet.
+            val specForCurrentScene =
+                when {
+                    progress <= 0f -> fromOverscrollSpec
+                    progress >= 1f -> toOverscrollSpec
+                    else -> null
+                } ?: return true
+
+            return specForCurrentScene.transformationSpec.transformations.isNotEmpty()
+        }
+
+        internal open fun interruptionProgress(
+            layoutImpl: SceneTransitionLayoutImpl,
+        ): Float {
+            if (!layoutImpl.state.enableInterruptions) {
+                return 0f
+            }
+
+            if (replacedTransition != null) {
+                return replacedTransition.interruptionProgress(layoutImpl)
+            }
+
+            fun create(): Animatable<Float, AnimationVector1D> {
+                val animatable = Animatable(1f, visibilityThreshold = ProgressVisibilityThreshold)
+                layoutImpl.animationScope.launch {
+                    val swipeSpec = layoutImpl.state.transitions.defaultSwipeSpec
+                    val progressSpec =
+                        spring(
+                            stiffness = swipeSpec.stiffness,
+                            dampingRatio = swipeSpec.dampingRatio,
+                            visibilityThreshold = ProgressVisibilityThreshold,
+                        )
+                    animatable.animateTo(0f, progressSpec)
+                }
+
+                return animatable
+            }
+
+            val animatable = interruptionDecay ?: create().also { interruptionDecay = it }
+            return animatable.value
+        }
+    }
+
+    interface HasOverscrollProperties {
+        /**
+         * The position of the [Transition.toContent].
+         *
+         * Used to understand the direction of the overscroll.
+         */
+        val isUpOrLeft: Boolean
+
+        /**
+         * The relative orientation between [Transition.fromContent] and [Transition.toContent].
+         *
+         * Used to understand the orientation of the overscroll.
+         */
+        val orientation: Orientation
+
+        /**
+         * Return the absolute distance between fromScene and toScene, if available, otherwise
+         * [DistanceUnspecified].
+         */
+        val absoluteDistance: Float
+
+        /**
+         * The content (scene or overlay) around which the transition is currently bouncing. When
+         * not `null`, this transition is currently oscillating around this content and will soon
+         * settle to that content.
+         */
+        val bouncingContent: ContentKey?
+
+        companion object {
+            const val DistanceUnspecified = 0f
+        }
+    }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.kt
new file mode 100644
index 0000000..cade9bf
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/testing/ElementStateAccess.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.compose.animation.scene.testing
+
+import androidx.compose.ui.semantics.SemanticsNode
+import com.android.compose.animation.scene.Element
+import com.android.compose.animation.scene.Element.Companion.AlphaUnspecified
+import com.android.compose.animation.scene.ElementModifier
+import com.android.compose.animation.scene.Scale
+
+val SemanticsNode.lastAlphaForTesting: Float?
+    get() = elementState.lastAlpha.takeIf { it != AlphaUnspecified }
+
+val SemanticsNode.lastScaleForTesting: Scale?
+    get() = elementState.lastScale.takeIf { it != Scale.Unspecified }
+
+private val SemanticsNode.elementState: Element.State
+    get() {
+        val elementModifier =
+            layoutInfo
+                .getModifierInfo()
+                .map { it.modifier }
+                .filterIsInstance<ElementModifier>()
+                .firstOrNull()
+        requireNotNull(elementModifier) {
+            "No ElementModifier found. Did you use the Modifier.element(...)?"
+        }
+        return with(elementModifier) {
+            val element =
+                requireNotNull(layoutImpl.elements[key]) {
+                    "No element found in STL layout with key: ${key.testTag}"
+                }
+            requireNotNull(element.stateByContent[content.key]) {
+                "No state found for given content key: ${content.key.testTag}"
+            }
+        }
+    }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
index 538ce79..c5a3067c 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt
@@ -22,7 +22,7 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Anchor the size of an element to the size of another element. */
 internal class AnchoredSize(
@@ -36,7 +36,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
         fun anchorSizeIn(content: ContentKey): IntSize {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
index 258f541..05878c2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt
@@ -23,7 +23,7 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Anchor the translation of an element to another element. */
 internal class AnchoredTranslate(
@@ -35,7 +35,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
         fun throwException(content: ContentKey?): Nothing {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
index be8dac21..7f86479 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt
@@ -22,7 +22,7 @@
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.Scale
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /**
  * Scales the draw size of an element. Note this will only scale the draw inside of an element,
@@ -40,7 +40,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Scale,
     ): Scale {
         return Scale(scaleX, scaleY, pivot)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
index d72e43a..a32c7dd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt
@@ -22,7 +22,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Translate an element from an edge of the layout. */
 internal class EdgeTranslate(
@@ -35,7 +35,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Offset
     ): Offset {
         val sceneSize = layoutImpl.content(content).targetSize
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
index 92ae30f8..4528eef 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt
@@ -20,7 +20,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** Fade an element in or out. */
 internal class Fade(
@@ -31,7 +31,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Float
     ): Float {
         // Return the alpha value of [element] either when it starts fading in or when it finished
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
index e8515dc..5f3fdaf 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt
@@ -21,7 +21,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 import kotlin.math.roundToInt
 
 /**
@@ -38,7 +38,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: IntSize,
     ): IntSize {
         return IntSize(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
index 77ec891..505ad04 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt
@@ -16,6 +16,8 @@
 
 package com.android.compose.animation.scene.transformation
 
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.LinearEasing
 import androidx.compose.ui.util.fastCoerceAtLeast
 import androidx.compose.ui.util.fastCoerceAtMost
 import androidx.compose.ui.util.fastCoerceIn
@@ -23,7 +25,7 @@
 import com.android.compose.animation.scene.Element
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 /** A transformation applied to one or more elements during a transition. */
 sealed interface Transformation {
@@ -64,7 +66,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: T,
     ): T
 }
@@ -90,11 +92,13 @@
 data class TransformationRange(
     val start: Float,
     val end: Float,
+    val easing: Easing,
 ) {
     constructor(
         start: Float? = null,
-        end: Float? = null
-    ) : this(start ?: BoundUnspecified, end ?: BoundUnspecified)
+        end: Float? = null,
+        easing: Easing = LinearEasing,
+    ) : this(start ?: BoundUnspecified, end ?: BoundUnspecified, easing)
 
     init {
         require(!start.isSpecified() || (start in 0f..1f))
@@ -103,17 +107,20 @@
     }
 
     /** Reverse this range. */
-    fun reversed() = TransformationRange(start = reverseBound(end), end = reverseBound(start))
+    fun reversed() =
+        TransformationRange(start = reverseBound(end), end = reverseBound(start), easing = easing)
 
     /** Get the progress of this range given the global [transitionProgress]. */
     fun progress(transitionProgress: Float): Float {
-        return when {
-            start.isSpecified() && end.isSpecified() ->
-                ((transitionProgress - start) / (end - start)).fastCoerceIn(0f, 1f)
-            !start.isSpecified() && !end.isSpecified() -> transitionProgress
-            end.isSpecified() -> (transitionProgress / end).fastCoerceAtMost(1f)
-            else -> ((transitionProgress - start) / (1f - start)).fastCoerceAtLeast(0f)
-        }
+        val progress =
+            when {
+                start.isSpecified() && end.isSpecified() ->
+                    ((transitionProgress - start) / (end - start)).fastCoerceIn(0f, 1f)
+                !start.isSpecified() && !end.isSpecified() -> transitionProgress
+                end.isSpecified() -> (transitionProgress / end).fastCoerceAtMost(1f)
+                else -> ((transitionProgress - start) / (1f - start)).fastCoerceAtLeast(0f)
+            }
+        return easing.transform(progress)
     }
 
     private fun Float.isSpecified() = this != BoundUnspecified
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
index fab4ced..8f84586 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene.transformation
 
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.ContentKey
@@ -24,7 +25,7 @@
 import com.android.compose.animation.scene.ElementMatcher
 import com.android.compose.animation.scene.OverscrollScope
 import com.android.compose.animation.scene.SceneTransitionLayoutImpl
-import com.android.compose.animation.scene.content.state.ContentState
+import com.android.compose.animation.scene.content.state.TransitionState
 
 internal class Translate(
     override val matcher: ElementMatcher,
@@ -36,7 +37,7 @@
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
         return with(layoutImpl.density) {
@@ -53,22 +54,61 @@
     val x: OverscrollScope.() -> Float = { 0f },
     val y: OverscrollScope.() -> Float = { 0f },
 ) : PropertyTransformation<Offset> {
+    private val cachedOverscrollScope = CachedOverscrollScope()
+
     override fun transform(
         layoutImpl: SceneTransitionLayoutImpl,
         content: ContentKey,
         element: Element,
         stateInContent: Element.State,
-        transition: ContentState.Transition<*>,
+        transition: TransitionState.Transition,
         value: Offset,
     ): Offset {
         // As this object is created by OverscrollBuilderImpl and we retrieve the current
         // OverscrollSpec only when the transition implements HasOverscrollProperties, we can assume
         // that this method was invoked after performing this check.
-        val overscrollProperties = transition as ContentState.HasOverscrollProperties
+        val overscrollProperties = transition as TransitionState.HasOverscrollProperties
+        val overscrollScope =
+            cachedOverscrollScope.getFromCacheOrCompute(layoutImpl.density, overscrollProperties)
 
         return Offset(
-            x = value.x + overscrollProperties.overscrollScope.x(),
-            y = value.y + overscrollProperties.overscrollScope.y(),
+            x = value.x + overscrollScope.x(),
+            y = value.y + overscrollScope.y(),
         )
     }
 }
+
+/**
+ * A helper class to cache a [OverscrollScope] given a [Density] and
+ * [TransitionState.HasOverscrollProperties]. This helps avoid recreating a scope every frame
+ * whenever an overscroll transition is computed.
+ */
+private class CachedOverscrollScope() {
+    private var previousScope: OverscrollScope? = null
+    private var previousDensity: Density? = null
+    private var previousOverscrollProperties: TransitionState.HasOverscrollProperties? = null
+
+    fun getFromCacheOrCompute(
+        density: Density,
+        overscrollProperties: TransitionState.HasOverscrollProperties,
+    ): OverscrollScope {
+        if (
+            previousScope == null ||
+                density != previousDensity ||
+                previousOverscrollProperties != overscrollProperties
+        ) {
+            val scope =
+                object : OverscrollScope, Density by density {
+                    override val absoluteDistance: Float
+                        get() = overscrollProperties.absoluteDistance
+                }
+
+            previousScope = scope
+            previousDensity = density
+            previousOverscrollProperties = overscrollProperties
+            return scope
+        }
+
+        return checkNotNull(previousScope)
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
new file mode 100644
index 0000000..715d979
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/Seek.kt
@@ -0,0 +1,187 @@
+/*
+ * 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.transition
+
+import androidx.annotation.FloatRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.util.fastCoerceIn
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
+import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SwipeAnimation
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserActionResult
+import com.android.compose.animation.scene.createSwipeAnimation
+import kotlin.coroutines.cancellation.CancellationException
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+/**
+ * Seek to the given [scene] using [progress].
+ *
+ * This will start a transition from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene] to [scene], driven by the
+ * progress in [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToScene(
+    scene: SceneKey,
+    @FloatRange(0.0, 1.0) progress: Flow<Float>,
+    transitionKey: TransitionKey? = null,
+    animationSpec: AnimationSpec<Float>? = null,
+) {
+    require(scene != currentScene) {
+        "seekToScene($scene) has to be called with a different scene than the current scene"
+    }
+
+    seek(UserActionResult.ChangeScene(scene, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to show the given [overlay] using [progress].
+ *
+ * This will start a transition to show [overlay] from the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToShowOverlay(
+    overlay: OverlayKey,
+    @FloatRange(0.0, 1.0) progress: Flow<Float>,
+    transitionKey: TransitionKey? = null,
+    animationSpec: AnimationSpec<Float>? = null,
+) {
+    require(overlay in currentOverlays) {
+        "seekToShowOverlay($overlay) can be called only when the overlay is in currentOverlays"
+    }
+
+    seek(UserActionResult.ShowOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+/**
+ * Seek to hide the given [overlay] using [progress].
+ *
+ * This will start a transition to hide [overlay] to the
+ * [current scene][MutableSceneTransitionLayoutState.currentScene], driven by the progress in
+ * [progress]. Once [progress] stops emitting, we will animate progress to 1f (using
+ * [animationSpec]) if it stopped normally or to 0f if it stopped with a
+ * [kotlin.coroutines.cancellation.CancellationException].
+ */
+suspend fun MutableSceneTransitionLayoutState.seekToHideOverlay(
+    overlay: OverlayKey,
+    @FloatRange(0.0, 1.0) progress: Flow<Float>,
+    transitionKey: TransitionKey? = null,
+    animationSpec: AnimationSpec<Float>? = null,
+) {
+    require(overlay !in currentOverlays) {
+        "seekToHideOverlay($overlay) can be called only when the overlay is *not* in " +
+            "currentOverlays"
+    }
+
+    seek(UserActionResult.HideOverlay(overlay, transitionKey), progress, animationSpec)
+}
+
+private suspend fun MutableSceneTransitionLayoutState.seek(
+    result: UserActionResult,
+    progress: Flow<Float>,
+    animationSpec: AnimationSpec<Float>?,
+) {
+    val layoutState =
+        when (this) {
+            is MutableSceneTransitionLayoutStateImpl -> this
+        }
+
+    val swipeAnimation =
+        createSwipeAnimation(
+            layoutState = layoutState,
+            result = result,
+
+            // We are animating progress, so distance is always 1f.
+            distance = 1f,
+
+            // The orientation and isUpOrLeft don't matter here given that they are only used during
+            // overscroll, which is disabled for progress-based transitions.
+            orientation = Orientation.Horizontal,
+            isUpOrLeft = false,
+        )
+
+    animateProgress(
+        state = layoutState,
+        animation = swipeAnimation,
+        progress = progress,
+        commitSpec = animationSpec,
+        cancelSpec = animationSpec,
+    )
+}
+
+internal suspend fun <T : ContentKey> animateProgress(
+    state: MutableSceneTransitionLayoutStateImpl,
+    animation: SwipeAnimation<T>,
+    progress: Flow<Float>,
+    commitSpec: AnimationSpec<Float>?,
+    cancelSpec: AnimationSpec<Float>?,
+) {
+    fun animateOffset(targetContent: T, spec: AnimationSpec<Float>?) {
+        if (state.transitionState != animation.contentTransition || animation.isAnimatingOffset()) {
+            return
+        }
+
+        animation.animateOffset(
+            initialVelocity = 0f,
+            targetContent = targetContent,
+
+            // Important: we have to specify a spec that correctly animates *progress* (low
+            // visibility threshold) and not *offset* (higher visibility threshold).
+            spec = spec ?: animation.contentTransition.transformationSpec.progressSpec,
+        )
+    }
+
+    coroutineScope {
+        val collectionJob = launch {
+            try {
+                progress.collectLatest { progress ->
+                    // Progress based animation should never overscroll given that the
+                    // absoluteDistance exposed to overscroll builders is always 1f and will not
+                    // lead to any noticeable transformation.
+                    animation.dragOffset = progress.fastCoerceIn(0f, 1f)
+                }
+
+                // Transition committed.
+                animateOffset(animation.toContent, commitSpec)
+            } catch (e: CancellationException) {
+                // Transition cancelled.
+                animateOffset(animation.fromContent, cancelSpec)
+            }
+        }
+
+        // Start the transition.
+        state.startTransition(animation.contentTransition)
+
+        // The transition is done. Cancel the collection in case the transition was finished because
+        // it was interrupted by another transition.
+        if (collectionJob.isActive) {
+            collectionJob.cancel()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
index 23bcf10..42ba9ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt
@@ -19,7 +19,6 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.compose.animation.scene.content.state.TransitionState
-import kotlinx.coroutines.Job
 
 /** A linked transition which is driven by a [originalTransition]. */
 internal class LinkedTransition(
@@ -27,13 +26,13 @@
     fromScene: SceneKey,
     toScene: SceneKey,
     override val key: TransitionKey? = null,
-) : TransitionState.Transition(fromScene, toScene) {
+) : TransitionState.Transition.ChangeScene(fromScene, toScene) {
 
     override val currentScene: SceneKey
         get() {
             return when (originalTransition.currentScene) {
-                originalTransition.fromScene -> fromScene
-                originalTransition.toScene -> toScene
+                originalTransition.fromContent -> fromScene
+                originalTransition.toContent -> toScene
                 else -> error("Original currentScene is neither FromScene nor ToScene")
             }
         }
@@ -50,5 +49,11 @@
     override val progressVelocity: Float
         get() = originalTransition.progressVelocity
 
-    override fun finish(): Job = originalTransition.finish()
+    override suspend fun run() {
+        originalTransition.run()
+    }
+
+    override fun freezeAndAnimateToCurrentState() {
+        originalTransition.freezeAndAnimateToCurrentState()
+    }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
index c0c40dd..c830ca4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt
@@ -16,6 +16,7 @@
 
 package com.android.compose.animation.scene.transition.link
 
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.SceneTransitionLayoutState
@@ -35,8 +36,8 @@
      * target to `SceneA` from any current scene.
      */
     class TransitionLink(
-        val sourceFrom: SceneKey?,
-        val sourceTo: SceneKey?,
+        val sourceFrom: ContentKey?,
+        val sourceTo: ContentKey?,
         val targetFrom: SceneKey?,
         val targetTo: SceneKey,
         val targetTransitionKey: TransitionKey? = null,
@@ -49,14 +50,16 @@
                 error("From and To can't be the same")
         }
 
-        internal fun isMatchingLink(transition: TransitionState.Transition): Boolean {
-            return (sourceFrom == null || sourceFrom == transition.fromScene) &&
-                (sourceTo == null || sourceTo == transition.toScene)
+        internal fun isMatchingLink(
+            transition: TransitionState.Transition,
+        ): Boolean {
+            return (sourceFrom == null || sourceFrom == transition.fromContent) &&
+                (sourceTo == null || sourceTo == transition.toContent)
         }
 
-        internal fun targetIsInValidState(targetCurrentScene: SceneKey): Boolean {
-            return (targetFrom == null || targetFrom == targetCurrentScene) &&
-                targetTo != targetCurrentScene
+        internal fun targetIsInValidState(targetCurrentContent: ContentKey): Boolean {
+            return (targetFrom == null || targetFrom == targetCurrentContent) &&
+                targetTo != targetCurrentContent
         }
     }
 }
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 228f7ba..16fb533b 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
@@ -129,7 +129,11 @@
         return onPriorityStop(available)
     }
 
-    /** Method to call before destroying the object or to reset the initial state. */
+    /**
+     * Method to call before destroying the object or to reset the initial state.
+     *
+     * 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 = Velocity.Zero)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index 01895c9..a491349 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -39,7 +39,10 @@
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
 import com.android.compose.animation.scene.TestScenes.SceneD
+import com.android.compose.test.setContentAndCreateMainScope
+import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Rule
@@ -168,10 +171,7 @@
                 assertThat(lastValueInTo).isEqualTo(expectedValues)
             }
 
-            after {
-                assertThat(lastValueInFrom).isEqualTo(toValues)
-                assertThat(lastValueInTo).isEqualTo(toValues)
-            }
+            after { assertThat(lastValueInTo).isEqualTo(toValues) }
         }
     }
 
@@ -229,10 +229,7 @@
                 assertThat(lastValueInTo).isEqualTo(lerp(fromValues, toValues, fraction = 0.75f))
             }
 
-            after {
-                assertThat(lastValueInFrom).isEqualTo(toValues)
-                assertThat(lastValueInTo).isEqualTo(toValues)
-            }
+            after { assertThat(lastValueInTo).isEqualTo(toValues) }
         }
     }
 
@@ -288,10 +285,7 @@
                 assertThat(lastValueInTo).isEqualTo(expectedValues)
             }
 
-            after {
-                assertThat(lastValueInFrom).isEqualTo(toValues)
-                assertThat(lastValueInTo).isEqualTo(toValues)
-            }
+            after { assertThat(lastValueInTo).isEqualTo(toValues) }
         }
     }
 
@@ -415,30 +409,33 @@
             }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                // foo goes from 0f to 100f in A => B.
-                scene(SceneA) { animateFloat(0f, foo) }
-                scene(SceneB) { animateFloat(100f, foo) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    // foo goes from 0f to 100f in A => B.
+                    scene(SceneA) { animateFloat(0f, foo) }
+                    scene(SceneB) { animateFloat(100f, foo) }
 
-                // bar goes from 0f to 10f in C => D.
-                scene(SceneC) { animateFloat(0f, bar) }
-                scene(SceneD) { animateFloat(10f, bar) }
+                    // bar goes from 0f to 10f in C => D.
+                    scene(SceneC) { animateFloat(0f, bar) }
+                    scene(SceneD) { animateFloat(10f, bar) }
+                }
             }
-        }
 
-        rule.runOnUiThread {
-            // A => B is at 30%.
+        // A => B is at 30%.
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneA,
                     to = SceneB,
                     progress = { 0.3f },
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
+        }
 
-            // C => D is at 70%.
+        // C => D is at 70%.
+        scope.launch {
             state.startTransition(transition(from = SceneC, to = SceneD, progress = { 0.7f }))
         }
         rule.waitForIdle()
@@ -475,17 +472,18 @@
             }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneA) { animateFloat(0f, key) }
-                scene(SceneB) { animateFloat(100f, key) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { animateFloat(0f, key) }
+                    scene(SceneB) { animateFloat(100f, key) }
+                }
             }
-        }
 
         // Overscroll on A at -100%: value should be interpolated given that there is no overscroll
         // defined for scene A.
         var progress by mutableStateOf(-1f)
-        rule.runOnIdle {
+        scope.launch {
             state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
         }
         rule.waitForIdle()
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 dc5b2f7..79f82c9 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
@@ -28,10 +28,11 @@
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.compose.animation.scene.NestedScrollBehavior.DuringTransitionBetweenScenes
 import com.android.compose.animation.scene.NestedScrollBehavior.EdgeAlways
 import com.android.compose.animation.scene.NestedScrollBehavior.EdgeNoPreview
 import com.android.compose.animation.scene.NestedScrollBehavior.EdgeWithPreview
+import com.android.compose.animation.scene.TestOverlays.OverlayA
+import com.android.compose.animation.scene.TestOverlays.OverlayB
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
@@ -40,9 +41,9 @@
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.MonotonicClockTestScope
 import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.launch
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,7 +54,7 @@
 @RunWith(AndroidJUnit4::class)
 class DraggableHandlerTest {
     private class TestGestureScope(
-        private val testScope: MonotonicClockTestScope,
+        val testScope: MonotonicClockTestScope,
     ) {
         var canChangeScene: (SceneKey) -> Boolean = { true }
         val layoutState =
@@ -66,19 +67,19 @@
         var layoutDirection = LayoutDirection.Rtl
             set(value) {
                 field = value
-                layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+                layoutImpl.updateContents(scenesBuilder, layoutDirection)
             }
 
         var mutableUserActionsA = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
             set(value) {
                 field = value
-                layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+                layoutImpl.updateContents(scenesBuilder, layoutDirection)
             }
 
         var mutableUserActionsB = mapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
             set(value) {
                 field = value
-                layoutImpl.updateScenes(scenesBuilder, layoutDirection)
+                layoutImpl.updateContents(scenesBuilder, layoutDirection)
             }
 
         private val scenesBuilder: SceneTransitionLayoutScope.() -> Unit = {
@@ -104,6 +105,21 @@
             ) {
                 Text("SceneC")
             }
+            overlay(
+                key = OverlayA,
+                userActions =
+                    mapOf(
+                        Swipe.Up to UserActionResult.HideOverlay(OverlayA),
+                        Swipe.Down to UserActionResult.ReplaceByOverlay(OverlayB)
+                    ),
+            ) {
+                Text("OverlayA")
+            }
+            overlay(
+                key = OverlayB,
+            ) {
+                Text("OverlayB")
+            }
         }
 
         val transitionInterceptionThreshold = 0.05f
@@ -116,9 +132,12 @@
                     swipeSourceDetector = DefaultEdgeDetector,
                     transitionInterceptionThreshold = transitionInterceptionThreshold,
                     builder = scenesBuilder,
-                    coroutineScope = testScope,
+
+                    // Use testScope and not backgroundScope here because backgroundScope does not
+                    // work well with advanceUntilIdle(), which is used by some tests.
+                    animationScope = testScope,
                 )
-                .apply { setScenesTargetSizeForTest(LAYOUT_SIZE) }
+                .apply { setContentsAndLayoutTargetSizeForTest(LAYOUT_SIZE) }
 
         val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical)
         val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal)
@@ -181,13 +200,19 @@
             fromScene: SceneKey? = null,
             toScene: SceneKey? = null,
             progress: Float? = null,
+            previewProgress: Float? = null,
+            isInPreviewStage: Boolean? = null,
             isUserInputOngoing: Boolean? = null
         ): Transition {
-            val transition = assertThat(transitionState).isTransition()
+            val transition = assertThat(transitionState).isSceneTransition()
             currentScene?.let { assertThat(transition).hasCurrentScene(it) }
             fromScene?.let { assertThat(transition).hasFromScene(it) }
             toScene?.let { assertThat(transition).hasToScene(it) }
             progress?.let { assertThat(transition).hasProgress(it) }
+            previewProgress?.let { assertThat(transition).hasPreviewProgress(it) }
+            isInPreviewStage?.let {
+                assertThat(transition).run { if (it) isInPreviewStage() else isNotInPreviewStage() }
+            }
             isUserInputOngoing?.let { assertThat(transition).hasIsUserInputOngoing(it) }
             return transition
         }
@@ -285,8 +310,20 @@
         runMonotonicClockTest {
             val testGestureScope = TestGestureScope(testScope = this)
 
-            // run the test
-            testGestureScope.block()
+            try {
+                // Run the test.
+                testGestureScope.block()
+            } finally {
+                // Make sure we stop the last transition if it was not explicitly stopped, otherwise
+                // tests will time out after 10s given that the transitions are now started on the
+                // test scope. We don't use backgroundScope when starting the test transitions
+                // because coroutines started on the background scope don't work well with
+                // advanceUntilIdle(), which is used in a few tests.
+                if (testGestureScope.draggableHandler.isDrivingTransition) {
+                    (testGestureScope.layoutState.transitionState as Transition)
+                        .freezeAndAnimateToCurrentState()
+                }
+            }
         }
     }
 
@@ -322,6 +359,32 @@
     }
 
     @Test
+    fun onDragStoppedAfterDrag_velocityLowerThanThreshold_remainSameScene_previewAnimated() =
+        runGestureTest {
+            layoutState.transitions = transitions {
+                // set a preview for the transition
+                from(SceneA, to = SceneC, preview = {}) {}
+            }
+            val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
+            assertTransition(currentScene = SceneA)
+
+            dragController.onDragStopped(velocity = velocityThreshold - 0.01f)
+            runCurrent()
+
+            // verify that transition remains in preview stage and animates back to fromScene
+            assertTransition(
+                currentScene = SceneA,
+                isInPreviewStage = true,
+                previewProgress = 0.1f,
+                progress = 0f
+            )
+
+            // wait for the stop animation
+            advanceUntilIdle()
+            assertIdle(currentScene = SceneA)
+        }
+
+    @Test
     fun onDragStoppedAfterDrag_velocityAtLeastThreshold_goToNextScene() = runGestureTest {
         val dragController = onDragStarted(overSlop = down(fractionOfScreen = 0.1f))
         assertTransition(currentScene = SceneA)
@@ -460,13 +523,14 @@
     private fun TestGestureScope.navigateToSceneC() {
         assertIdle(currentScene = SceneA)
         val dragController = onDragStarted(overSlop = down(fractionOfScreen = 1f))
+        assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneC)
         dragController.onDragStopped(velocity = 0f)
         advanceUntilIdle()
         assertIdle(currentScene = SceneC)
     }
 
     @Test
-    fun onAccelaratedScroll_scrollToThirdScene() = runGestureTest {
+    fun onAcceleratedScroll_scrollToThirdScene() = runGestureTest {
         // Drag A -> B with progress 0.2
         val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
         assertTransition(
@@ -501,7 +565,7 @@
     }
 
     @Test
-    fun onAccelaratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest {
+    fun onAcceleratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest {
         val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
         dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.2f))
         dragController1.onDragStopped(velocity = -velocityThreshold)
@@ -730,13 +794,6 @@
     }
 
     @Test
-    fun flingAfterScroll_DuringTransitionBetweenScenes_doNothing() = runGestureTest {
-        flingAfterScroll(use = DuringTransitionBetweenScenes, idleAfterScroll = true)
-
-        assertIdle(currentScene = SceneA)
-    }
-
-    @Test
     fun flingAfterScroll_EdgeNoOverscroll_goToNextScene() = runGestureTest {
         flingAfterScroll(use = EdgeNoPreview, idleAfterScroll = false)
 
@@ -789,13 +846,6 @@
     }
 
     @Test
-    fun flingAfterScrollStartedInScene_DuringTransitionBetweenScenes_doNothing() = runGestureTest {
-        flingAfterScrollStartedInScene(use = DuringTransitionBetweenScenes, idleAfterScroll = true)
-
-        assertIdle(currentScene = SceneA)
-    }
-
-    @Test
     fun flingAfterScrollStartedInScene_EdgeNoOverscroll_doNothing() = runGestureTest {
         flingAfterScrollStartedInScene(use = EdgeNoPreview, idleAfterScroll = true)
 
@@ -937,7 +987,7 @@
     }
 
     @Test
-    fun finish() = runGestureTest {
+    fun freezeAndAnimateToCurrentState() = runGestureTest {
         // Start at scene C.
         navigateToSceneC()
 
@@ -949,35 +999,25 @@
         // The current transition can be intercepted.
         assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
 
-        // Finish the transition.
+        // Freeze the transition.
         val transition = transitionState as Transition
-        val job = transition.finish()
+        transition.freezeAndAnimateToCurrentState()
         assertTransition(isUserInputOngoing = false)
-
-        // The current transition can not be intercepted anymore.
-        assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isFalse()
-
-        // Calling finish() multiple times returns the same Job.
-        assertThat(transition.finish()).isSameInstanceAs(job)
-        assertThat(transition.finish()).isSameInstanceAs(job)
-        assertThat(transition.finish()).isSameInstanceAs(job)
-
-        // We can join the job to wait for the animation to end.
-        assertTransition()
-        job.join()
+        advanceUntilIdle()
         assertIdle(SceneC)
     }
 
     @Test
-    fun finish_cancelled() = runGestureTest {
-        // Swipe up from the middle to transition to scene B.
-        val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
-        onDragStarted(startedPosition = middle, overSlop = up(0.1f))
-        assertTransition(fromScene = SceneA, toScene = SceneB)
+    fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest {
+        assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
+        onDragStarted(overSlop = up(0.1f))
+        assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
 
-        // Finish the transition and cancel the returned job.
-        (transitionState as Transition).finish().cancelAndJoin()
-        assertIdle(SceneA)
+        layoutState.startTransitionImmediately(
+            animationScope = testScope.backgroundScope,
+            transition(SceneA, SceneB)
+        )
+        assertThat(draggableHandler.shouldImmediatelyIntercept(startedPosition = null)).isFalse()
     }
 
     @Test
@@ -1035,7 +1075,7 @@
 
         // We scrolled down, under scene C there is nothing, so we can use the overscroll spec
         assertThat(layoutState.currentTransition?.currentOverscrollSpec).isNotNull()
-        assertThat(layoutState.currentTransition?.currentOverscrollSpec?.scene).isEqualTo(SceneC)
+        assertThat(layoutState.currentTransition?.currentOverscrollSpec?.content).isEqualTo(SceneC)
         val transition = layoutState.currentTransition
         assertThat(transition).isNotNull()
         assertThat(transition!!.progress).isEqualTo(-0.1f)
@@ -1090,7 +1130,7 @@
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
 
         val dragController = onDragStarted(startedPosition = middle, overSlop = up(0.5f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0.5f)
@@ -1116,7 +1156,7 @@
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
 
         val dragController = onDragStarted(startedPosition = middle, overSlop = down(0.5f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneC)
         assertThat(transition).hasProgress(0.5f)
@@ -1142,7 +1182,7 @@
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
 
         val dragController = onDragStarted(startedPosition = middle, overSlop = up(1.5f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(1.5f)
@@ -1169,7 +1209,7 @@
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
 
         val dragController = onDragStarted(startedPosition = middle, overSlop = down(1.5f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneC)
         assertThat(transition).hasProgress(1.5f)
@@ -1197,7 +1237,7 @@
 
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
         val dragController = onDragStarted(startedPosition = middle, overSlop = down(1f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(-1f)
@@ -1225,7 +1265,7 @@
 
         val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)
         val dragController = onDragStarted(startedPosition = middle, overSlop = up(1f))
-        val transition = assertThat(transitionState).isTransition()
+        val transition = assertThat(transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneC)
         assertThat(transition).hasProgress(-1f)
@@ -1282,13 +1322,96 @@
     @Test
     fun interceptingTransitionReplacesCurrentTransition() = runGestureTest {
         val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f))
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         controller.onDragStopped(velocity = 0f)
 
         // Intercept the transition.
         onDragStartedImmediately()
-        val newTransition = assertThat(layoutState.transitionState).isTransition()
+        val newTransition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(newTransition).isNotSameInstanceAs(transition)
         assertThat(newTransition.replacedTransition).isSameInstanceAs(transition)
     }
+
+    @Test
+    fun showOverlay() = runGestureTest {
+        mutableUserActionsA = mapOf(Swipe.Down to UserActionResult.ShowOverlay(OverlayA))
+
+        // Initial state.
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(/* empty */ )
+
+        // Swipe down to show overlay A.
+        val controller = onDragStarted(overSlop = down(0.1f))
+        val transition = assertThat(layoutState.transitionState).isShowOrHideOverlayTransition()
+        assertThat(transition).hasCurrentScene(SceneA)
+        assertThat(transition).hasFromOrToScene(SceneA)
+        assertThat(transition).hasOverlay(OverlayA)
+        assertThat(transition).hasCurrentOverlays(/* empty, gesture not committed yet. */ )
+        assertThat(transition).hasProgress(0.1f)
+
+        // Commit the gesture. The overlay is instantly added in the set of current overlays.
+        controller.onDragStopped(velocityThreshold)
+        assertThat(transition).hasCurrentOverlays(OverlayA)
+        advanceUntilIdle()
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)
+    }
+
+    @Test
+    fun hideOverlay() = runGestureTest {
+        layoutState.showOverlay(OverlayA, animationScope = testScope)
+        advanceUntilIdle()
+
+        // Initial state.
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)
+
+        // Swipe up to hide overlay A.
+        val controller = onDragStarted(overSlop = up(0.1f))
+        val transition = assertThat(layoutState.transitionState).isShowOrHideOverlayTransition()
+        assertThat(transition).hasCurrentScene(SceneA)
+        assertThat(transition).hasFromOrToScene(SceneA)
+        assertThat(transition).hasOverlay(OverlayA)
+        assertThat(transition).hasCurrentOverlays(OverlayA)
+        assertThat(transition).hasProgress(0.1f)
+
+        // Commit the gesture. The overlay is instantly removed from the set of current overlays.
+        controller.onDragStopped(-velocityThreshold)
+        assertThat(transition).hasCurrentOverlays(/* empty */ )
+        advanceUntilIdle()
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(/* empty */ )
+    }
+
+    @Test
+    fun replaceOverlay() = runGestureTest {
+        layoutState.showOverlay(OverlayA, animationScope = testScope)
+        advanceUntilIdle()
+
+        // Initial state.
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA)
+
+        // Swipe down to replace overlay A by overlay B.
+        val controller = onDragStarted(overSlop = down(0.1f))
+        val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition()
+        assertThat(transition).hasCurrentScene(SceneA)
+        assertThat(transition).hasFromOverlay(OverlayA)
+        assertThat(transition).hasToOverlay(OverlayB)
+        assertThat(transition).hasCurrentOverlays(OverlayA)
+        assertThat(transition).hasProgress(0.1f)
+
+        // Commit the gesture. The overlays are instantly swapped in the set of current overlays.
+        controller.onDragStopped(velocityThreshold)
+        assertThat(transition).hasCurrentOverlays(OverlayB)
+        advanceUntilIdle()
+        assertThat(layoutState.transitionState).isIdle()
+        assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
+        assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB)
+    }
 }
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 75f44ff..60596de 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
@@ -71,10 +71,11 @@
 import com.android.compose.animation.scene.TestScenes.SceneC
 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 kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Ignore
 import org.junit.Rule
@@ -504,7 +505,7 @@
     }
 
     @Test
-    fun elementModifierNodeIsRecycledInLazyLayouts() = runTest {
+    fun elementModifierNodeIsRecycledInLazyLayouts() {
         val nPages = 2
         val pagerState = PagerState(currentPage = 0) { nPages }
         var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
@@ -630,18 +631,19 @@
                 )
             }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneA) { Box(Modifier.element(TestElements.Foo).size(20.dp)) }
-                scene(SceneB) {}
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { Box(Modifier.element(TestElements.Foo).size(20.dp)) }
+                    scene(SceneB) {}
+                }
             }
-        }
 
         // Pause the clock to block recompositions.
         rule.mainClock.autoAdvance = false
 
         // Change the current transition.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.5f }))
         }
 
@@ -724,6 +726,7 @@
                 layoutHeight = layoutHeight,
                 sceneTransitions = {
                     overscroll(SceneB, Orientation.Vertical) {
+                        progressConverter = ProgressConverter.linear()
                         // On overscroll 100% -> Foo should translate by overscrollTranslateY
                         translate(TestElements.Foo, y = overscrollTranslateY)
                     }
@@ -735,7 +738,7 @@
 
         val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
         fooElement.assertTopPositionInRootIsEqualTo(0.dp)
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).isNotNull()
         assertThat(transition).hasProgress(0.5f)
         assertThat(animatedFloat).isEqualTo(50f)
@@ -780,6 +783,7 @@
                     transitions =
                         transitions {
                             overscroll(SceneB, Orientation.Vertical) {
+                                progressConverter = ProgressConverter.linear()
                                 translate(TestElements.Foo, y = overscrollTranslateY)
                             }
                         }
@@ -822,7 +826,7 @@
             moveBy(Offset(0f, touchSlop + layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
         }
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasOverscrollSpec()
         assertThat(transition).hasProgress(-0.5f)
         fooElement.assertTopPositionInRootIsEqualTo(overscrollTranslateY * 0.5f)
@@ -905,7 +909,7 @@
             }
         }
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
         fooElement.assertTopPositionInRootIsEqualTo(translateY * 0.5f)
     }
@@ -921,6 +925,7 @@
                 layoutHeight = layoutHeight,
                 sceneTransitions = {
                     overscroll(SceneB, Orientation.Vertical) {
+                        progressConverter = ProgressConverter.linear()
                         // On overscroll 100% -> Foo should translate by layoutHeight
                         translate(TestElements.Foo, y = { absoluteDistance })
                     }
@@ -939,7 +944,7 @@
             moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
         }
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(animatedFloat).isEqualTo(100f)
 
         // Scroll 150% (100% scroll + 50% overscroll)
@@ -961,6 +966,97 @@
     }
 
     @Test
+    fun elementTransitionWithDistanceDuringOverscrollWithDefaultProgressConverter() {
+        val layoutWidth = 200.dp
+        val layoutHeight = 400.dp
+        var animatedFloat = 0f
+        val state =
+            setupOverscrollScenario(
+                layoutWidth = layoutWidth,
+                layoutHeight = layoutHeight,
+                sceneTransitions = {
+                    // Overscroll progress will be halved
+                    defaultOverscrollProgressConverter = ProgressConverter { it / 2f }
+
+                    overscroll(SceneB, Orientation.Vertical) {
+                        // On overscroll 100% -> Foo should translate by layoutHeight
+                        translate(TestElements.Foo, y = { absoluteDistance })
+                    }
+                },
+                firstScroll = 1f, // 100% scroll
+                animatedFloatRange = 0f..100f,
+                onAnimatedFloat = { animatedFloat = it },
+            )
+
+        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+        fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+        assertThat(animatedFloat).isEqualTo(100f)
+
+        rule.onRoot().performTouchInput {
+            // Scroll another 100%
+            moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+        }
+
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(animatedFloat).isEqualTo(100f)
+
+        // Scroll 200% (100% scroll + 100% overscroll)
+        assertThat(transition).hasProgress(2f)
+        assertThat(transition).hasOverscrollSpec()
+
+        // Overscroll progress is halved, we are at 50% of the overscroll progress.
+        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
+        assertThat(animatedFloat).isEqualTo(100f)
+    }
+
+    @Test
+    fun elementTransitionWithDistanceDuringOverscrollWithOverrideDefaultProgressConverter() {
+        val layoutWidth = 200.dp
+        val layoutHeight = 400.dp
+        var animatedFloat = 0f
+        val state =
+            setupOverscrollScenario(
+                layoutWidth = layoutWidth,
+                layoutHeight = layoutHeight,
+                sceneTransitions = {
+                    // Overscroll progress will be linear (by default)
+                    defaultOverscrollProgressConverter = ProgressConverter.linear()
+
+                    overscroll(SceneB, Orientation.Vertical) {
+                        // This override the defaultOverscrollProgressConverter
+                        // Overscroll progress will be halved
+                        progressConverter = ProgressConverter { it / 2f }
+                        // On overscroll 100% -> Foo should translate by layoutHeight
+                        translate(TestElements.Foo, y = { absoluteDistance })
+                    }
+                },
+                firstScroll = 1f, // 100% scroll
+                animatedFloatRange = 0f..100f,
+                onAnimatedFloat = { animatedFloat = it },
+            )
+
+        val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag)
+        fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+        assertThat(animatedFloat).isEqualTo(100f)
+
+        rule.onRoot().performTouchInput {
+            // Scroll another 100%
+            moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
+        }
+
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(animatedFloat).isEqualTo(100f)
+
+        // Scroll 200% (100% scroll + 100% overscroll)
+        assertThat(transition).hasProgress(2f)
+        assertThat(transition).hasOverscrollSpec()
+
+        // Overscroll progress is halved, we are at 50% of the overscroll progress.
+        fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 0.5f)
+        assertThat(animatedFloat).isEqualTo(100f)
+    }
+
+    @Test
     fun elementTransitionWithDistanceDuringOverscrollWithProgressConverter() {
         val layoutWidth = 200.dp
         val layoutHeight = 400.dp
@@ -972,7 +1068,7 @@
                 sceneTransitions = {
                     overscroll(SceneB, Orientation.Vertical) {
                         // Overscroll progress will be halved
-                        progressConverter = { it / 2f }
+                        progressConverter = ProgressConverter { it / 2f }
 
                         // On overscroll 100% -> Foo should translate by layoutHeight
                         translate(TestElements.Foo, y = { absoluteDistance })
@@ -992,7 +1088,7 @@
             moveBy(Offset(0f, layoutHeight.toPx()), delayMillis = 1_000)
         }
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(animatedFloat).isEqualTo(100f)
 
         // Scroll 200% (100% scroll + 100% overscroll)
@@ -1034,6 +1130,7 @@
                         )
 
                     overscroll(SceneB, Orientation.Vertical) {
+                        progressConverter = ProgressConverter.linear()
                         // On overscroll 100% -> Foo should translate by layoutHeight
                         translate(TestElements.Foo, y = { absoluteDistance })
                     }
@@ -1052,7 +1149,7 @@
             moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
         }
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
 
         // Scroll 150% (100% scroll + 50% overscroll)
         assertThat(transition).hasProgress(1.5f)
@@ -1069,7 +1166,7 @@
 
         assertThat(transition.progress).isLessThan(1f)
         assertThat(transition).hasOverscrollSpec()
-        assertThat(transition).hasBouncingScene(transition.toScene)
+        assertThat(transition).hasBouncingContent(transition.toContent)
         assertThat(animatedFloat).isEqualTo(100f)
     }
 
@@ -1152,13 +1249,15 @@
 
         val transitions = state.currentTransitions
         assertThat(transitions).hasSize(2)
-        assertThat(transitions[0]).hasFromScene(SceneA)
-        assertThat(transitions[0]).hasToScene(SceneB)
-        assertThat(transitions[0]).hasProgress(0f)
+        val firstTransition = assertThat(transitions[0]).isSceneTransition()
+        assertThat(firstTransition).hasFromScene(SceneA)
+        assertThat(firstTransition).hasToScene(SceneB)
+        assertThat(firstTransition).hasProgress(0f)
 
-        assertThat(transitions[1]).hasFromScene(SceneB)
-        assertThat(transitions[1]).hasToScene(SceneC)
-        assertThat(transitions[1]).hasProgress(0f)
+        val secondTransition = assertThat(transitions[1]).isSceneTransition()
+        assertThat(secondTransition).hasFromScene(SceneB)
+        assertThat(secondTransition).hasToScene(SceneC)
+        assertThat(secondTransition).hasProgress(0f)
 
         // First frame: both are at x = 0dp. For the whole transition, Foo is at y = 0dp and Bar is
         // at y = layoutSize - elementSoze = 100dp.
@@ -1199,7 +1298,7 @@
     }
 
     @Test
-    fun interruption() = runTest {
+    fun interruption() {
         // 4 frames of animation.
         val duration = 4 * 16
 
@@ -1239,37 +1338,41 @@
         val valueInC = 200f
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(
-                state,
-                Modifier.size(layoutSize),
-                onLayoutImpl = { layoutImpl = it },
-            ) {
-                // In scene A, Foo is aligned at the TopStart.
-                scene(SceneA) {
-                    Box(Modifier.fillMaxSize()) {
-                        Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(
+                    state,
+                    Modifier.size(layoutSize),
+                    onLayoutImpl = { layoutImpl = it },
+                ) {
+                    // In scene A, Foo is aligned at the TopStart.
+                    scene(SceneA) {
+                        Box(Modifier.fillMaxSize()) {
+                            Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+                        }
                     }
-                }
 
-                // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming
-                // from B. We put it before (below) scene B so that we can check that interruptions
-                // values and deltas are properly cleared once all transitions are done.
-                scene(SceneC) {
-                    Box(Modifier.fillMaxSize()) {
-                        Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
+                    // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when
+                    // coming
+                    // from B. We put it before (below) scene B so that we can check that
+                    // interruptions
+                    // values and deltas are properly cleared once all transitions are done.
+                    scene(SceneC) {
+                        Box(Modifier.fillMaxSize()) {
+                            Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
+                        }
                     }
-                }
 
-                // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming
-                // from A.
-                scene(SceneB) {
-                    Box(Modifier.fillMaxSize()) {
-                        Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+                    // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when
+                    // coming
+                    // from A.
+                    scene(SceneB) {
+                        Box(Modifier.fillMaxSize()) {
+                            Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+                        }
                     }
                 }
             }
-        }
 
         // The offset of Foo when idle in A, B or C.
         val offsetInA = DpOffset.Zero
@@ -1293,12 +1396,12 @@
                 from = SceneA,
                 to = SceneB,
                 progress = { aToBProgress },
-                onFinish = neverFinish(),
+                onFreezeAndAnimate = { /* never finish */ },
             )
         val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
         val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
         val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
-        rule.runOnUiThread { state.startTransition(aToB) }
+        scope.launch { state.startTransition(aToB) }
         rule
             .onNode(isElement(TestElements.Foo, SceneB))
             .assertSizeIsEqualTo(sizeInAToB)
@@ -1318,7 +1421,7 @@
                 progress = { bToCProgress },
                 interruptionProgress = { interruptionProgress },
             )
-        rule.runOnUiThread { state.startTransition(bToC) }
+        scope.launch { state.startTransition(bToC) }
 
         // The interruption deltas, which will be multiplied by the interruption progress then added
         // to the current transition offset and size.
@@ -1379,10 +1482,8 @@
             .assertSizeIsEqualTo(sizeInC)
 
         // Manually finish the transition.
-        rule.runOnUiThread {
-            state.finishTransition(aToB, SceneB)
-            state.finishTransition(bToC, SceneC)
-        }
+        aToB.finish()
+        bToC.finish()
         rule.waitForIdle()
         assertThat(state.transitionState).isIdle()
 
@@ -1401,7 +1502,7 @@
     }
 
     @Test
-    fun interruption_sharedTransitionDisabled() = runTest {
+    fun interruption_sharedTransitionDisabled() {
         // 4 frames of animation.
         val duration = 4 * 16
         val layoutSize = DpSize(200.dp, 100.dp)
@@ -1427,21 +1528,22 @@
             Box(modifier.element(TestElements.Foo).size(fooSize))
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state, Modifier.size(layoutSize)) {
-                scene(SceneA) {
-                    Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) }
-                }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+                    scene(SceneA) {
+                        Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopStart)) }
+                    }
 
-                scene(SceneB) {
-                    Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) }
-                }
+                    scene(SceneB) {
+                        Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.TopEnd)) }
+                    }
 
-                scene(SceneC) {
-                    Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) }
+                    scene(SceneC) {
+                        Box(Modifier.fillMaxSize()) { Foo(Modifier.align(Alignment.BottomEnd)) }
+                    }
                 }
             }
-        }
 
         // The offset of Foo when idle in A, B or C.
         val offsetInA = DpOffset.Zero
@@ -1450,7 +1552,12 @@
 
         // State is a transition A => B at 50% interrupted by B => C at 30%.
         val aToB =
-            transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
+            transition(
+                from = SceneA,
+                to = SceneB,
+                progress = { 0.5f },
+                onFreezeAndAnimate = { /* never finish */ },
+            )
         var bToCInterruptionProgress by mutableStateOf(1f)
         val bToC =
             transition(
@@ -1458,11 +1565,11 @@
                 to = SceneC,
                 progress = { 0.3f },
                 interruptionProgress = { bToCInterruptionProgress },
-                onFinish = neverFinish(),
+                onFreezeAndAnimate = { /* never finish */ },
             )
-        rule.runOnUiThread { state.startTransition(aToB) }
+        scope.launch { state.startTransition(aToB) }
         rule.waitForIdle()
-        rule.runOnUiThread { state.startTransition(bToC) }
+        scope.launch { state.startTransition(bToC) }
 
         // Foo is placed in both B and C given that the shared transition is disabled. In B, its
         // offset is impacted by the interruption but in C it is not.
@@ -1482,7 +1589,8 @@
 
         // Manually finish A => B so only B => C is remaining.
         bToCInterruptionProgress = 0f
-        rule.runOnUiThread { state.finishTransition(aToB, SceneB) }
+        aToB.finish()
+
         rule
             .onNode(isElement(TestElements.Foo, SceneB))
             .assertPositionInRootIsEqualTo(offsetInB.x, offsetInB.y)
@@ -1498,7 +1606,7 @@
                 progress = { 0.7f },
                 interruptionProgress = { 1f },
             )
-        rule.runOnUiThread { state.startTransition(bToA) }
+        scope.launch { state.startTransition(bToA) }
 
         // Foo should have the position it had in B right before the interruption.
         rule
@@ -1512,32 +1620,35 @@
         val state =
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
-                        SceneA,
-                        transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
-                    )
-                    .apply {
-                        startTransition(
-                            transition(
-                                from = SceneA,
-                                to = SceneB,
-                                progress = { -1f },
-                                orientation = Orientation.Horizontal
-                            )
-                        )
-                    }
+                    SceneA,
+                    transitions { overscrollDisabled(SceneA, Orientation.Horizontal) }
+                )
             }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(
-                state,
-                Modifier.size(100.dp),
-                onLayoutImpl = { layoutImpl = it },
-            ) {
-                scene(SceneA) {}
-                scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(
+                    state,
+                    Modifier.size(100.dp),
+                    onLayoutImpl = { layoutImpl = it },
+                ) {
+                    scene(SceneA) {}
+                    scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+                }
             }
+
+        scope.launch {
+            state.startTransition(
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { -1f },
+                    orientation = Orientation.Horizontal
+                )
+            )
         }
+        rule.waitForIdle()
 
         assertThat(layoutImpl.elements).containsKey(TestElements.Foo)
         val foo = layoutImpl.elements.getValue(TestElements.Foo)
@@ -1550,7 +1661,7 @@
     }
 
     @Test
-    fun lastAlphaIsNotSetByOutdatedLayer() = runTest {
+    fun lastAlphaIsNotSetByOutdatedLayer() {
         val state =
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
@@ -1560,23 +1671,24 @@
             }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
-                scene(SceneA) {}
-                scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
-                scene(SceneC) { Box(Modifier.element(TestElements.Foo)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                    scene(SceneA) {}
+                    scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+                    scene(SceneC) { Box(Modifier.element(TestElements.Foo)) }
+                }
             }
-        }
 
         // Start A => B at 0.5f.
         var aToBProgress by mutableStateOf(0.5f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneA,
                     to = SceneB,
                     progress = { aToBProgress },
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
         }
@@ -1595,7 +1707,7 @@
         assertThat(fooInB.lastAlpha).isEqualTo(0.7f)
 
         // Start B => C at 0.3f.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0.3f }))
         }
         rule.waitForIdle()
@@ -1623,16 +1735,17 @@
             }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
-                scene(SceneA) {}
-                scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                    scene(SceneA) {}
+                    scene(SceneB) { Box(Modifier.element(TestElements.Foo)) }
+                }
             }
-        }
 
         // Start A => B at 60%.
         var interruptionProgress by mutableStateOf(1f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneA,
@@ -1677,19 +1790,20 @@
             Box(Modifier.element(TestElements.Foo).size(10.dp))
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneA) { Foo() }
-                scene(SceneB) { Foo() }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { Foo() }
+                    scene(SceneB) { Foo() }
+                }
             }
-        }
 
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist()
 
         // A => B while overscrolling at scene B.
         var progress by mutableStateOf(2f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
         }
         rule.waitForIdle()
@@ -1730,19 +1844,20 @@
             MovableElement(key, modifier) { content { Text(text) } }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneA) { MovableFoo(text = fooInA) }
-                scene(SceneB) { MovableFoo(text = fooInB) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { MovableFoo(text = fooInA) }
+                    scene(SceneB) { MovableFoo(text = fooInB) }
+                }
             }
-        }
 
         rule.onNode(hasText(fooInA)).assertIsDisplayed()
         rule.onNode(hasText(fooInB)).assertDoesNotExist()
 
         // A => B while overscrolling at scene B.
         var progress by mutableStateOf(2f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
         }
         rule.waitForIdle()
@@ -1761,13 +1876,14 @@
     }
 
     @Test
-    fun interruptionThenOverscroll() = runTest {
+    fun interruptionThenOverscroll() {
         val state =
             rule.runOnUiThread {
                 MutableSceneTransitionLayoutStateImpl(
                     SceneA,
                     transitions {
                         overscroll(SceneB, Orientation.Vertical) {
+                            progressConverter = ProgressConverter.linear()
                             translate(TestElements.Foo, y = 15.dp)
                         }
                     }
@@ -1781,22 +1897,23 @@
             }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state, Modifier.size(200.dp)) {
-                scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
-                scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
-                scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                    scene(SceneA) { SceneWithFoo(offset = DpOffset.Zero) }
+                    scene(SceneB) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 0.dp)) }
+                    scene(SceneC) { SceneWithFoo(offset = DpOffset(x = 40.dp, y = 40.dp)) }
+                }
             }
-        }
 
         // Start A => B at 75%.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneA,
                     to = SceneB,
                     progress = { 0.75f },
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
         }
@@ -1809,7 +1926,7 @@
         // Interrupt A => B with B => C at 0%.
         var progress by mutableStateOf(0f)
         var interruptionProgress by mutableStateOf(1f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneB,
@@ -1817,7 +1934,7 @@
                     progress = { progress },
                     interruptionProgress = { interruptionProgress },
                     orientation = Orientation.Vertical,
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
         }
@@ -1865,12 +1982,13 @@
         }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
-                scene(SceneA) { NestedFooBar() }
-                scene(SceneB) { NestedFooBar() }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                    scene(SceneA) { NestedFooBar() }
+                    scene(SceneB) { NestedFooBar() }
+                }
             }
-        }
 
         // Idle on A: composed and placed only in B.
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed()
@@ -1899,7 +2017,7 @@
         assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified)
 
         // A => B: composed in both and placed only in B.
-        rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) }
+        scope.launch { state.startTransition(transition(from = SceneA, to = SceneB)) }
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed()
         rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed()
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed()
@@ -1926,7 +2044,7 @@
     }
 
     @Test
-    fun currentTransitionSceneIsUsedToComputeElementValues() = runTest {
+    fun currentTransitionSceneIsUsedToComputeElementValues() {
         val state =
             rule.runOnIdle {
                 MutableSceneTransitionLayoutStateImpl(
@@ -1946,23 +2064,31 @@
             }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state, Modifier.size(200.dp)) {
-                scene(SceneA) { Foo() }
-                scene(SceneB) {}
-                scene(SceneC) { Foo() }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                    scene(SceneA) { Foo() }
+                    scene(SceneB) {}
+                    scene(SceneC) { Foo() }
+                }
             }
-        }
 
         // We have 2 transitions:
         //  - A => B at 100%
         //  - B => C at 0%
         // So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its
         // size in B => C.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
-                transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish())
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { 1f },
+                    onFreezeAndAnimate = { /* never finish */ },
+                )
             )
+        }
+        scope.launch {
             state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f }))
         }
 
@@ -1971,7 +2097,7 @@
     }
 
     @Test
-    fun interruptionDeltasAreProperlyCleaned() = runTest {
+    fun interruptionDeltasAreProperlyCleaned() {
         val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
 
         @Composable
@@ -1981,18 +2107,24 @@
             }
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state, Modifier.size(200.dp)) {
-                scene(SceneA) { Foo(offset = 0.dp) }
-                scene(SceneB) { Foo(offset = 20.dp) }
-                scene(SceneC) { Foo(offset = 40.dp) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                    scene(SceneA) { Foo(offset = 0.dp) }
+                    scene(SceneB) { Foo(offset = 20.dp) }
+                    scene(SceneC) { Foo(offset = 40.dp) }
+                }
             }
-        }
 
         // Start A => B at 50%.
         val aToB =
-            transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
-        rule.runOnUiThread { state.startTransition(aToB) }
+            transition(
+                from = SceneA,
+                to = SceneB,
+                progress = { 0.5f },
+                onFreezeAndAnimate = { /* never finish */ },
+            )
+        scope.launch { state.startTransition(aToB) }
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
 
         // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the
@@ -2002,11 +2134,12 @@
             transition(
                 from = SceneB,
                 to = SceneC,
+                current = { SceneB },
                 progress = { 0f },
                 interruptionProgress = { interruptionProgress },
-                onFinish = neverFinish(),
+                onFreezeAndAnimate = { /* never finish */ },
             )
-        rule.runOnUiThread { state.startTransition(bToC) }
+        scope.launch { state.startTransition(bToC) }
         rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp)
 
         // Finish the interruption and leave the transition progress at 0f. We should be at the same
@@ -2017,9 +2150,9 @@
         // Finish both transitions but directly start a new one B => A with interruption progress
         // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been
         // correctly cleaned.
-        rule.runOnUiThread {
-            state.finishTransition(aToB, idleScene = SceneB)
-            state.finishTransition(bToC, idleScene = SceneB)
+        aToB.finish()
+        bToC.finish()
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneB,
@@ -2033,7 +2166,7 @@
     }
 
     @Test
-    fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest {
+    fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() {
         val state =
             rule.runOnIdle {
                 MutableSceneTransitionLayoutStateImpl(
@@ -2048,17 +2181,23 @@
         }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
-                scene(SceneA) { Foo() }
-                scene(SceneB) { Foo() }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                    scene(SceneA) { Foo() }
+                    scene(SceneB) { Foo() }
+                }
             }
-        }
 
         // Overscroll A => B on A.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
-                transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish())
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { -1f },
+                    onFreezeAndAnimate = { /* never finish */ },
+                )
             )
         }
         rule.waitForIdle()
@@ -2074,7 +2213,7 @@
     }
 
     @Test
-    fun transparentElementIsNotImpactingInterruption() = runTest {
+    fun transparentElementIsNotImpactingInterruption() {
         val state =
             rule.runOnIdle {
                 MutableSceneTransitionLayoutStateImpl(
@@ -2101,23 +2240,24 @@
             Box(modifier.element(TestElements.Foo).size(10.dp))
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
 
-                // Define A after B so that Foo is placed in A during A <=> B.
-                scene(SceneA) { Foo() }
+                    // Define A after B so that Foo is placed in A during A <=> B.
+                    scene(SceneA) { Foo() }
+                }
             }
-        }
 
         // Start A => B at 70%.
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneA,
                     to = SceneB,
                     progress = { 0.7f },
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
         }
@@ -2128,14 +2268,14 @@
         // Start B => A at 50% with interruptionProgress = 100%. Foo is placed in A and should still
         // be at (40dp, 60dp) given that it was fully transparent in A before the interruption.
         var interruptionProgress by mutableStateOf(1f)
-        rule.runOnUiThread {
+        scope.launch {
             state.startTransition(
                 transition(
                     from = SceneB,
                     to = SceneA,
                     progress = { 0.5f },
                     interruptionProgress = { interruptionProgress },
-                    onFinish = neverFinish(),
+                    onFreezeAndAnimate = { /* never finish */ },
                 )
             )
         }
@@ -2151,7 +2291,7 @@
     }
 
     @Test
-    fun replacedTransitionDoesNotTriggerInterruption() = runTest {
+    fun replacedTransitionDoesNotTriggerInterruption() {
         val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) }
 
         @Composable
@@ -2159,17 +2299,23 @@
             Box(modifier.element(TestElements.Foo).size(10.dp))
         }
 
-        rule.setContent {
-            SceneTransitionLayout(state) {
-                scene(SceneA) { Foo() }
-                scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) { Foo() }
+                    scene(SceneB) { Foo(Modifier.offset(40.dp, 60.dp)) }
+                }
             }
-        }
 
         // Start A => B at 50%.
         val aToB1 =
-            transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish())
-        rule.runOnUiThread { state.startTransition(aToB1) }
+            transition(
+                from = SceneA,
+                to = SceneB,
+                progress = { 0.5f },
+                onFreezeAndAnimate = { /* never finish */ },
+            )
+        scope.launch { state.startTransition(aToB1) }
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
 
@@ -2183,7 +2329,7 @@
                 interruptionProgress = { 1f },
                 replacedTransition = aToB1,
             )
-        rule.runOnUiThread { state.startTransition(aToB2) }
+        scope.launch { state.startTransition(aToB2) }
         rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsNotDisplayed()
         rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(40.dp, 60.dp)
     }
@@ -2329,12 +2475,13 @@
         }
 
         lateinit var layoutImpl: SceneTransitionLayoutImpl
-        rule.setContent {
-            SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
-                scene(from) { Box { exitingElements.forEach { Foo(it) } } }
-                scene(to) { Box { enteringElements.forEach { Foo(it) } } }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) {
+                    scene(from) { Box { exitingElements.forEach { Foo(it) } } }
+                    scene(to) { Box { enteringElements.forEach { Foo(it) } } }
+                }
             }
-        }
 
         val bToA =
             transition(
@@ -2344,7 +2491,7 @@
                 previewProgress = { previewProgress },
                 isInPreviewStage = { isInPreviewStage }
             )
-        rule.runOnUiThread { state.startTransition(bToA) }
+        scope.launch { state.startTransition(bToA) }
         rule.waitForIdle()
         return layoutImpl
     }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
index ca72181..bc929bd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/InterruptionHandlerTest.kt
@@ -25,9 +25,9 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
 import com.google.common.truth.Correspondence
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.launch
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -44,8 +44,8 @@
                 transitions { /* default interruption handler */ },
             )
 
-        state.setTargetScene(SceneB, coroutineScope = this)
-        state.setTargetScene(SceneC, coroutineScope = this)
+        state.setTargetScene(SceneB, animationScope = this)
+        state.setTargetScene(SceneC, animationScope = this)
 
         assertThat(state.currentTransitions)
             .comparingElementsUsing(FromToCurrentTriple)
@@ -69,7 +69,7 @@
                     interruptionHandler =
                         object : InterruptionHandler {
                             override fun onInterruption(
-                                interrupted: TransitionState.Transition,
+                                interrupted: TransitionState.Transition.ChangeScene,
                                 newTargetScene: SceneKey
                             ): InterruptionResult {
                                 return InterruptionResult(
@@ -81,8 +81,8 @@
                 },
             )
 
-        state.setTargetScene(SceneB, coroutineScope = this)
-        state.setTargetScene(SceneC, coroutineScope = this)
+        state.setTargetScene(SceneB, animationScope = this)
+        state.setTargetScene(SceneC, animationScope = this)
 
         assertThat(state.currentTransitions)
             .comparingElementsUsing(FromToCurrentTriple)
@@ -104,7 +104,7 @@
                     interruptionHandler =
                         object : InterruptionHandler {
                             override fun onInterruption(
-                                interrupted: TransitionState.Transition,
+                                interrupted: TransitionState.Transition.ChangeScene,
                                 newTargetScene: SceneKey
                             ): InterruptionResult {
                                 return InterruptionResult(
@@ -124,10 +124,10 @@
 
         // Animate to B and advance the transition a little bit so that progress > visibility
         // threshold and that reversing from B back to A won't immediately snap to A.
-        state.setTargetScene(SceneB, coroutineScope = this)
+        state.setTargetScene(SceneB, animationScope = this)
         testScheduler.advanceTimeBy(duration / 2L)
 
-        state.setTargetScene(SceneC, coroutineScope = this)
+        state.setTargetScene(SceneC, animationScope = this)
 
         assertThat(state.currentTransitions)
             .comparingElementsUsing(FromToCurrentTriple)
@@ -155,13 +155,21 @@
                 // Progress must be > visibility threshold otherwise we will directly snap to A.
                 progress = { 0.5f },
                 progressVelocity = { progressVelocity },
-                onFinish = { launch {} },
             )
-        state.startTransition(aToB)
+        state.startTransitionImmediately(animationScope = backgroundScope, aToB)
 
         // Animate back to A. The previous transition is reversed, i.e. it has the same (from, to)
         // pair, and its velocity is used when animating the progress back to 0.
-        val bToA = checkNotNull(state.setTargetScene(SceneA, coroutineScope = this))
+        val bToA =
+            checkNotNull(
+                    state.setTargetScene(
+                        SceneA,
+                        // We use testScope here and not backgroundScope because setTargetScene
+                        // needs the monotonic clock that is only available in the test scope.
+                        animationScope = this,
+                    )
+                )
+                .first
         testScheduler.runCurrent()
         assertThat(bToA).hasFromScene(SceneA)
         assertThat(bToA).hasToScene(SceneB)
@@ -181,13 +189,21 @@
                 to = SceneB,
                 current = { SceneA },
                 progressVelocity = { progressVelocity },
-                onFinish = { launch {} },
             )
-        state.startTransition(aToB)
+        state.startTransitionImmediately(animationScope = backgroundScope, aToB)
 
         // Animate to B. The previous transition is reversed, i.e. it has the same (from, to) pair,
         // and its velocity is used when animating the progress to 1.
-        val bToA = checkNotNull(state.setTargetScene(SceneB, coroutineScope = this))
+        val bToA =
+            checkNotNull(
+                    state.setTargetScene(
+                        SceneB,
+                        // We use testScope here and not backgroundScope because setTargetScene
+                        // needs the monotonic clock that is only available in the test scope.
+                        animationScope = this,
+                    )
+                )
+                .first
         testScheduler.runCurrent()
         assertThat(bToA).hasFromScene(SceneA)
         assertThat(bToA).hasToScene(SceneB)
@@ -198,7 +214,7 @@
     companion object {
         val FromToCurrentTriple =
             Correspondence.transforming(
-                { transition: TransitionState.Transition? ->
+                { transition: TransitionState.Transition.ChangeScene? ->
                     Triple(transition?.fromScene, transition?.toScene, transition?.currentScene)
                 },
                 "(from, to, current) triple"
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
index e1d0945..c8e7e65 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementContentPickerTest.kt
@@ -17,6 +17,7 @@
 package com.android.compose.animation.scene
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertThrows
 import org.junit.Test
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
index 520e759..e4879d9 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MovableElementTest.kt
@@ -45,7 +45,6 @@
 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.content.state.ContentState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.test.assertSizeIsEqualTo
@@ -107,7 +106,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = SceneA))
+                            hasParent(isElement(TestElements.Foo, content = SceneA))
                     )
                     .assertExists()
                     .assertIsNotDisplayed()
@@ -115,7 +114,7 @@
                 rule
                     .onNode(
                         hasText("count: 0") and
-                            hasParent(isElement(TestElements.Foo, scene = SceneB))
+                            hasParent(isElement(TestElements.Foo, content = SceneB))
                     )
                     .assertIsDisplayed()
                     .assertSizeIsEqualTo(75.dp, 75.dp)
@@ -160,11 +159,11 @@
 
                         override fun contentDuringTransition(
                             element: ElementKey,
-                            transition: ContentState.Transition<*>,
+                            transition: TransitionState.Transition,
                             fromContentZIndex: Float,
                             toContentZIndex: Float
                         ): ContentKey {
-                            transition as TransitionState.Transition
+                            transition as TransitionState.Transition.ChangeScene
                             assertThat(transition).hasFromScene(SceneA)
                             assertThat(transition).hasToScene(SceneB)
                             assertThat(fromContentZIndex).isEqualTo(0)
@@ -214,7 +213,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = SceneA))
+                            hasParent(isElement(TestElements.Foo, content = SceneA))
                     )
                     .assertIsDisplayed()
                     .assertSizeIsEqualTo(75.dp, 75.dp)
@@ -235,7 +234,7 @@
                 rule
                     .onNode(
                         hasText("count: 3") and
-                            hasParent(isElement(TestElements.Foo, scene = SceneB))
+                            hasParent(isElement(TestElements.Foo, content = SceneB))
                     )
                     .assertIsDisplayed()
 
@@ -325,7 +324,7 @@
     fun movableElementScopeExtendsBoxScope() {
         val key = MovableElementKey("Foo", contents = setOf(SceneA))
         rule.setContent {
-            TestContentScope {
+            TestContentScope(currentScene = SceneA) {
                 MovableElement(key, Modifier.size(200.dp)) {
                     content {
                         Box(Modifier.testTag("bottomEnd").align(Alignment.BottomEnd))
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 2d37a0d..bf192e7 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
@@ -38,12 +38,15 @@
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.TouchInjectionScope
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Velocity
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
+import kotlin.properties.Delegates
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.isActive
 import org.junit.Rule
@@ -68,7 +71,7 @@
             return delta
         }
 
-        override fun onStop(velocity: Float, canChangeScene: Boolean): Float {
+        override fun onStop(velocity: Float, canChangeContent: Boolean): Float {
             onStop.invoke(velocity)
             return velocity
         }
@@ -719,4 +722,88 @@
         assertThat(availableOnPreFling).isEqualTo(consumedOnDragStop)
         assertThat(availableOnPostFling).isEqualTo(0f)
     }
+
+    @Test
+    fun multiPointerOnStopVelocity() {
+        val size = 200f
+        val middle = Offset(size / 2f, size / 2f)
+
+        var stopped = false
+        var lastVelocity = -1f
+        var touchSlop = 0f
+        var density: Density by Delegates.notNull()
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            density = LocalDensity.current
+            Box(
+                Modifier.size(with(density) { Size(size, size).toDpSize() })
+                    .nestedScrollDispatcher()
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        onDragStarted = { _, _, _ ->
+                            SimpleDragController(
+                                onDrag = { /* do nothing */ },
+                                onStop = {
+                                    stopped = true
+                                    lastVelocity = it
+                                },
+                            )
+                        },
+                        dispatcher = defaultDispatcher,
+                    )
+            )
+        }
+
+        var eventMillis: Long by Delegates.notNull()
+        rule.onRoot().performTouchInput { eventMillis = eventPeriodMillis }
+
+        fun swipeGesture(block: TouchInjectionScope.() -> Unit) {
+            stopped = false
+            rule.onRoot().performTouchInput {
+                down(middle)
+                block()
+                up()
+            }
+            assertThat(stopped).isEqualTo(true)
+        }
+
+        val shortDistance = touchSlop / 2f
+        swipeGesture {
+            moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+            moveBy(delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+
+        val longDistance = touchSlop * 4f
+        swipeGesture {
+            moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            moveBy(delta = Offset(0f, longDistance), delayMillis = eventMillis)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((longDistance / eventMillis) * 1000f)
+
+        rule.onRoot().performTouchInput {
+            down(pointerId = 0, position = middle)
+            down(pointerId = 1, position = middle)
+            moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            moveBy(pointerId = 0, delta = Offset(0f, longDistance), delayMillis = eventMillis)
+            // The velocity should be:
+            // (longDistance / eventMillis) pixels/ms
+
+            // 1 pointer left, the second one
+            up(pointerId = 0)
+
+            // After a few events the velocity should be:
+            // (shortDistance / eventMillis) pixels/ms
+            repeat(10) {
+                moveBy(pointerId = 1, delta = Offset(0f, shortDistance), delayMillis = eventMillis)
+            }
+            up(pointerId = 1)
+        }
+        assertThat(lastVelocity).isGreaterThan(0f)
+        assertThat(lastVelocity).isWithin(1f).of((shortDistance / eventMillis) * 1000f)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 9ebc426..d58a0a3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -23,6 +23,9 @@
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalViewConfiguration
@@ -122,7 +125,7 @@
 
         scrollUp(percent = 0.5f)
         // STL will start a transition with the remaining scroll
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
 
         scrollUp(percent = 1f)
@@ -130,33 +133,6 @@
     }
 
     @Test
-    fun customizeStlNestedScrollBehavior_DuringTransitionBetweenScenes() {
-        var canScroll = true
-        val state = setup2ScenesAndScrollTouchSlop {
-            Modifier.verticalNestedScrollToScene(
-                    bottomBehavior = NestedScrollBehavior.DuringTransitionBetweenScenes
-                )
-                .scrollable(rememberScrollableState { if (canScroll) it else 0f }, Vertical)
-        }
-
-        scrollUp(percent = 0.5f)
-        assertThat(state.transitionState).isIdle()
-
-        // Reach the end of the scrollable element
-        canScroll = false
-        scrollUp(percent = 0.5f)
-        assertThat(state.transitionState).isIdle()
-
-        pointerUp()
-        assertThat(state.transitionState).isIdle()
-
-        // Start a new gesture
-        pointerDownAndScrollTouchSlop()
-        scrollUp(percent = 0.5f)
-        assertThat(state.transitionState).isIdle()
-    }
-
-    @Test
     fun customizeStlNestedScrollBehavior_EdgeNoPreview() {
         var canScroll = true
         val state = setup2ScenesAndScrollTouchSlop {
@@ -180,7 +156,7 @@
         // Start a new gesture
         pointerDownAndScrollTouchSlop()
         scrollUp(percent = 0.5f)
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
 
         pointerUp()
@@ -205,7 +181,7 @@
         // Reach the end of the scrollable element
         canScroll = false
         scrollUp(percent = 0.5f)
-        val transition1 = assertThat(state.transitionState).isTransition()
+        val transition1 = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition1).hasProgress(0.5f)
 
         pointerUp()
@@ -216,7 +192,7 @@
         // Start a new gesture
         pointerDownAndScrollTouchSlop()
         scrollUp(percent = 0.5f)
-        val transition2 = assertThat(state.transitionState).isTransition()
+        val transition2 = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition2).hasProgress(0.5f)
 
         pointerUp()
@@ -239,7 +215,7 @@
         // Reach the end of the scrollable element
         canScroll = false
         scrollUp(percent = 0.5f)
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
 
         pointerUp()
@@ -250,20 +226,59 @@
 
     @Test
     fun customizeStlNestedScrollBehavior_multipleRequests() {
+        var canScroll = true
         val state = setup2ScenesAndScrollTouchSlop {
             Modifier
                 // This verticalNestedScrollToScene is closer the STL (an ancestor node)
                 .verticalNestedScrollToScene(bottomBehavior = NestedScrollBehavior.EdgeAlways)
                 // Another verticalNestedScrollToScene modifier
-                .verticalNestedScrollToScene(
-                    bottomBehavior = NestedScrollBehavior.DuringTransitionBetweenScenes
-                )
-                .scrollable(rememberScrollableState { 0f }, Vertical)
+                .verticalNestedScrollToScene(bottomBehavior = NestedScrollBehavior.EdgeNoPreview)
+                .scrollable(rememberScrollableState { if (canScroll) it else 0f }, Vertical)
         }
 
         scrollUp(percent = 0.5f)
-        // EdgeAlways always consume the remaining scroll, DuringTransitionBetweenScenes does not.
-        val transition = assertThat(state.transitionState).isTransition()
+        assertThat(state.transitionState).isIdle()
+
+        // Reach the end of the scrollable element
+        canScroll = false
+
+        scrollUp(percent = 0.5f)
+        // EdgeAlways always consume the remaining scroll, EdgeNoPreview does not.
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
     }
+
+    @Test
+    fun resetScrollTracking_afterMissingPointerUpEvent() {
+        var canScroll = true
+        var hasScrollable by mutableStateOf(true)
+        val state = setup2ScenesAndScrollTouchSlop {
+            if (hasScrollable) {
+                Modifier.scrollable(rememberScrollableState { if (canScroll) it else 0f }, Vertical)
+            } else {
+                Modifier
+            }
+        }
+
+        // The gesture is consumed by the component in the scene.
+        scrollUp(percent = 0.2f)
+
+        // STL keeps track of the scroll consumed. The scene remains in Idle.
+        assertThat(state.transitionState).isIdle()
+
+        // The scrollable component disappears, and does not send the signal (pointer up) to reset
+        // the consumed amount.
+        hasScrollable = false
+        pointerUp()
+
+        // A new scrollable component appears and allows the scene to consume the scroll.
+        hasScrollable = true
+        canScroll = false
+        pointerDownAndScrollTouchSlop()
+        scrollUp(percent = 0.2f)
+
+        // STL can only start the transition if it has reset the amount of scroll consumed.
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(transition).hasProgress(0.2f)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
index f717301..f3161f3 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ObservableTransitionStateTest.kt
@@ -16,14 +16,20 @@
 
 package com.android.compose.animation.scene
 
+import androidx.activity.BackEventCompat
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshots.Snapshot
-import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
 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.test.transition
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.launch
@@ -36,7 +42,7 @@
 
 @RunWith(AndroidJUnit4::class)
 class ObservableTransitionStateTest {
-    @get:Rule val rule = createComposeRule()
+    @get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
 
     @Test
     fun testObservableTransitionState() = runTest {
@@ -84,16 +90,16 @@
             at(0) {
                 val state = observableState()
                 assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
-                assertThat((state as ObservableTransitionState.Transition).fromScene)
+                assertThat((state as ObservableTransitionState.Transition).fromContent)
                     .isEqualTo(SceneA)
-                assertThat(state.toScene).isEqualTo(SceneB)
+                assertThat(state.toContent).isEqualTo(SceneB)
                 assertThat(state.progress()).isEqualTo(0f)
             }
             at(TestTransitionDuration / 2) {
                 val state = observableState()
-                assertThat((state as ObservableTransitionState.Transition).fromScene)
+                assertThat((state as ObservableTransitionState.Transition).fromContent)
                     .isEqualTo(SceneA)
-                assertThat(state.toScene).isEqualTo(SceneB)
+                assertThat(state.toContent).isEqualTo(SceneB)
                 assertThat(state.progress()).isEqualTo(0.5f)
             }
             after {
@@ -134,7 +140,7 @@
         var transitionCurrentScene by mutableStateOf(SceneA)
         val transition =
             transition(from = SceneA, to = SceneB, current = { transitionCurrentScene })
-        state.startTransition(transition)
+        state.startTransitionImmediately(animationScope = backgroundScope, transition)
         assertThat(currentScene.value).isEqualTo(SceneA)
 
         // Change the transition current scene.
@@ -145,6 +151,82 @@
         assertThat(currentScene.value).isEqualTo(SceneA)
     }
 
+    @Test
+    fun testObservablePreviewTransitionState() = runTest {
+        val layoutState =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    transitions = transitions { from(SceneA, to = SceneB, preview = {}) }
+                )
+            }
+        rule.setContent {
+            SceneTransitionLayout(layoutState) {
+                scene(SceneA, mapOf(Back to SceneB)) { Box(Modifier.fillMaxSize()) }
+                scene(SceneB) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        var observableState: ObservableTransitionState? = null
+        backgroundScope.launch {
+            layoutState.observableTransitionState().collect { observableState = it }
+        }
+
+        fun observableState(): ObservableTransitionState {
+            runCurrent()
+            return observableState!!
+        }
+
+        fun ObservableTransitionState.Transition.previewProgress(): Float {
+            var lastProgress = -1f
+            backgroundScope.launch { previewProgress.collect { lastProgress = it } }
+            runCurrent()
+            return lastProgress
+        }
+
+        fun ObservableTransitionState.Transition.isInPreviewStage(): Boolean {
+            var lastIsInPreviewStage = false
+            backgroundScope.launch { isInPreviewStage.collect { lastIsInPreviewStage = it } }
+            runCurrent()
+            return lastIsInPreviewStage
+        }
+
+        // Start back.
+        val dispatcher = rule.activity.onBackPressedDispatcher
+        rule.runOnUiThread {
+            dispatcher.dispatchOnBackStarted(backEvent())
+            dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
+        }
+
+        var state = observableState()
+        assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
+        assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
+        assertThat(state.previewProgress()).isEqualTo(0.4f)
+        assertThat(state.isInPreviewStage()).isEqualTo(true)
+
+        // Cancel it.
+        rule.runOnUiThread { dispatcher.dispatchOnBackCancelled() }
+        rule.waitForIdle()
+        state = observableState()
+        assertThat(state).isInstanceOf(ObservableTransitionState.Idle::class.java)
+
+        // Start again and commit it.
+        rule.runOnUiThread {
+            dispatcher.dispatchOnBackStarted(backEvent())
+            dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
+            dispatcher.onBackPressed()
+        }
+        state = observableState()
+        assertThat(state).isInstanceOf(ObservableTransitionState.Transition::class.java)
+        assertThat((state as ObservableTransitionState.Transition).fromContent).isEqualTo(SceneA)
+        assertThat(state.previewProgress()).isEqualTo(0.4f)
+        assertThat(state.isInPreviewStage()).isEqualTo(false)
+
+        rule.waitForIdle()
+        state = observableState()
+        assertThat(state).isInstanceOf(ObservableTransitionState.Idle::class.java)
+    }
+
     // See http://shortn/_hj4Mhikmos for inspiration.
     private fun runTestWithSnapshots(testBody: suspend TestScope.() -> Unit) {
         val globalWriteObserverHandle =
@@ -159,4 +241,13 @@
             globalWriteObserverHandle.dispose()
         }
     }
+
+    private fun backEvent(progress: Float = 0f): BackEventCompat {
+        return BackEventCompat(
+            touchX = 0f,
+            touchY = 0f,
+            progress = progress,
+            swipeEdge = BackEventCompat.EDGE_LEFT,
+        )
+    }
 }
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
new file mode 100644
index 0000000..471362b
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/OverlayTest.kt
@@ -0,0 +1,693 @@
+/*
+ * 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.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
+import androidx.compose.ui.test.hasTestTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestOverlays.OverlayA
+import com.android.compose.animation.scene.TestOverlays.OverlayB
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.test.assertSizeIsEqualTo
+import com.android.compose.test.setContentAndCreateMainScope
+import com.android.compose.test.subjects.assertThat
+import com.android.compose.test.transition
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class OverlayTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Composable
+    private fun ContentScope.Foo(width: Dp = 100.dp, height: Dp = 100.dp) {
+        Box(Modifier.element(TestElements.Foo).size(width, height))
+    }
+
+    @Test
+    fun showThenHideOverlay() {
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+        lateinit var coroutineScope: CoroutineScope
+        rule.setContent {
+            coroutineScope = rememberCoroutineScope()
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } }
+                overlay(OverlayA) { Foo() }
+            }
+        }
+
+        // Initial state: overlay A is not shown, so Foo is displayed at the top left in scene A.
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+
+        // Show overlay A: Foo is now centered on screen and placed in overlay A. It is not placed
+        // in scene A.
+        rule.runOnUiThread { state.showOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // Hide overlay A: back to initial state, top-left in scene A.
+        rule.runOnUiThread { state.hideOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+    }
+
+    @Test
+    fun multipleOverlays() {
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+        lateinit var coroutineScope: CoroutineScope
+        rule.setContent {
+            coroutineScope = rememberCoroutineScope()
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } }
+                overlay(OverlayA) { Foo() }
+                overlay(OverlayB) { Foo() }
+            }
+        }
+
+        // Initial state.
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+        rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+
+        // Show overlay A.
+        rule.runOnUiThread { state.showOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+
+        // Replace overlay A by overlay B.
+        rule.runOnUiThread { state.replaceOverlay(OverlayA, OverlayB, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayB))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // Show overlay A: Foo is still placed in B because it has a higher zIndex, but it now
+        // exists in A as well.
+        rule.runOnUiThread { state.showOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayB))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // Hide overlay B.
+        rule.runOnUiThread { state.hideOverlay(OverlayB, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertExists()
+            .assertIsNotDisplayed()
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+
+        // Hide overlay A.
+        rule.runOnUiThread { state.hideOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(isElement(TestElements.Foo, content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+        rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+    }
+
+    @Test
+    fun movableElement() {
+        val key = MovableElementKey("MovableBar", contents = setOf(SceneA, OverlayA, OverlayB))
+        val elementChildTag = "elementChildTag"
+
+        fun elementChild(content: ContentKey) = hasTestTag(elementChildTag) and inContent(content)
+
+        @Composable
+        fun ContentScope.MovableBar() {
+            MovableElement(key, Modifier) {
+                content { Box(Modifier.testTag(elementChildTag).size(100.dp)) }
+            }
+        }
+
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+        lateinit var coroutineScope: CoroutineScope
+        rule.setContent {
+            coroutineScope = rememberCoroutineScope()
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) { MovableBar() } }
+                overlay(OverlayA) { MovableBar() }
+                overlay(OverlayB) { MovableBar() }
+            }
+        }
+
+        // Initial state.
+        rule
+            .onNode(elementChild(content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(elementChild(content = OverlayA)).assertDoesNotExist()
+        rule.onNode(elementChild(content = OverlayB)).assertDoesNotExist()
+
+        // Show overlay A: movable element child only exists (is only composed) in overlay A.
+        rule.runOnUiThread { state.showOverlay(OverlayA, coroutineScope) }
+        rule.onNode(elementChild(content = SceneA)).assertDoesNotExist()
+        rule
+            .onNode(elementChild(content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+        rule.onNode(elementChild(content = OverlayB)).assertDoesNotExist()
+
+        // Replace overlay A by overlay B: element child is only in overlay B.
+        rule.runOnUiThread { state.replaceOverlay(OverlayA, OverlayB, coroutineScope) }
+        rule.onNode(elementChild(content = SceneA)).assertDoesNotExist()
+        rule.onNode(elementChild(content = OverlayA)).assertDoesNotExist()
+        rule
+            .onNode(elementChild(content = OverlayB))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // Show overlay A: element child still only exists in overlay B because it has a higher
+        // zIndex.
+        rule.runOnUiThread { state.showOverlay(OverlayA, coroutineScope) }
+        rule.onNode(elementChild(content = SceneA)).assertDoesNotExist()
+        rule.onNode(elementChild(content = OverlayA)).assertDoesNotExist()
+        rule
+            .onNode(elementChild(content = OverlayB))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // Hide overlay B: element child is in overlay A.
+        rule.runOnUiThread { state.hideOverlay(OverlayB, coroutineScope) }
+        rule.onNode(elementChild(content = SceneA)).assertDoesNotExist()
+        rule
+            .onNode(elementChild(content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+        rule.onNode(elementChild(content = OverlayB)).assertDoesNotExist()
+
+        // Hide overlay A: element child is in scene A.
+        rule.runOnUiThread { state.hideOverlay(OverlayA, coroutineScope) }
+        rule
+            .onNode(elementChild(content = SceneA))
+            .assertIsDisplayed()
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+        rule.onNode(elementChild(content = OverlayA)).assertDoesNotExist()
+        rule.onNode(elementChild(content = OverlayB)).assertDoesNotExist()
+    }
+
+    @Test
+    fun overlayAlignment() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(SceneA, initialOverlays = setOf(OverlayA))
+            }
+        var alignment by mutableStateOf(Alignment.Center)
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } }
+                overlay(OverlayA, alignment = alignment) { Foo() }
+            }
+        }
+
+        // Initial state: 100x100dp centered in 200x200dp layout.
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(50.dp, 50.dp)
+
+        // BottomStart.
+        alignment = Alignment.BottomStart
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(0.dp, 100.dp)
+
+        // TopEnd.
+        alignment = Alignment.TopEnd
+        rule
+            .onNode(isElement(TestElements.Foo, content = OverlayA))
+            .assertSizeIsEqualTo(100.dp)
+            .assertPositionInRootIsEqualTo(100.dp, 0.dp)
+    }
+
+    @Test
+    fun overlayMaxSizeIsCurrentSceneSize() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(SceneA, initialOverlays = setOf(OverlayA))
+            }
+
+        val contentTag = "overlayContent"
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                scene(SceneA) { Box(Modifier.size(100.dp)) { Foo() } }
+                overlay(OverlayA) { Box(Modifier.testTag(contentTag).fillMaxSize()) }
+            }
+        }
+
+        // Max overlay size is the size of the layout without overlays, not the (max) possible size
+        // of the layout.
+        rule.onNodeWithTag(contentTag).assertSizeIsEqualTo(100.dp)
+    }
+
+    @Test
+    fun showAnimation() {
+        rule.testShowOverlayTransition(
+            fromSceneContent = {
+                Box(Modifier.size(width = 180.dp, height = 120.dp)) {
+                    Foo(width = 60.dp, height = 40.dp)
+                }
+            },
+            overlayContent = { Foo(width = 100.dp, height = 80.dp) },
+            transition = {
+                // 4 frames of animation
+                spec = tween(4 * 16, easing = LinearEasing)
+            },
+        ) {
+            // Foo moves from (0,0) with a size of 60x40dp to centered (in a 180x120dp Box) with a
+            // size of 100x80dp, so at (40,20).
+            before {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertSizeIsEqualTo(60.dp, 40.dp)
+                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+            }
+
+            at(16) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(70.dp, 50.dp)
+                    .assertPositionInRootIsEqualTo(10.dp, 5.dp)
+            }
+
+            at(32) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(80.dp, 60.dp)
+                    .assertPositionInRootIsEqualTo(20.dp, 10.dp)
+            }
+
+            at(48) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(90.dp, 70.dp)
+                    .assertPositionInRootIsEqualTo(30.dp, 15.dp)
+            }
+
+            after {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(100.dp, 80.dp)
+                    .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+            }
+        }
+    }
+
+    @Test
+    fun hideAnimation() {
+        rule.testHideOverlayTransition(
+            toSceneContent = {
+                Box(Modifier.size(width = 180.dp, height = 120.dp)) {
+                    Foo(width = 60.dp, height = 40.dp)
+                }
+            },
+            overlayContent = { Foo(width = 100.dp, height = 80.dp) },
+            transition = {
+                // 4 frames of animation
+                spec = tween(4 * 16, easing = LinearEasing)
+            },
+        ) {
+            // Foo moves from centered (in a 180x120dp Box) with a size of 100x80dp, so at (40,20),
+            // to (0,0) with a size of 60x40dp.
+            before {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(100.dp, 80.dp)
+                    .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+            }
+
+            at(16) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(90.dp, 70.dp)
+                    .assertPositionInRootIsEqualTo(30.dp, 15.dp)
+            }
+
+            at(32) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(80.dp, 60.dp)
+                    .assertPositionInRootIsEqualTo(20.dp, 10.dp)
+            }
+
+            at(48) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(70.dp, 50.dp)
+                    .assertPositionInRootIsEqualTo(10.dp, 5.dp)
+            }
+
+            after {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertSizeIsEqualTo(60.dp, 40.dp)
+                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+            }
+        }
+    }
+
+    @Test
+    fun replaceAnimation() {
+        rule.testReplaceOverlayTransition(
+            currentSceneContent = { Box(Modifier.size(width = 180.dp, height = 120.dp)) },
+            fromContent = { Foo(width = 60.dp, height = 40.dp) },
+            fromAlignment = Alignment.TopStart,
+            toContent = { Foo(width = 100.dp, height = 80.dp) },
+            transition = {
+                // 4 frames of animation
+                spec = tween(4 * 16, easing = LinearEasing)
+            },
+        ) {
+            // Foo moves from (0,0) with a size of 60x40dp to centered (in a 180x120dp Box) with a
+            // size of 100x80dp, so at (40,20).
+            before {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertSizeIsEqualTo(60.dp, 40.dp)
+                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+                rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+            }
+
+            at(16) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(70.dp, 50.dp)
+                    .assertPositionInRootIsEqualTo(10.dp, 5.dp)
+            }
+
+            at(32) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(80.dp, 60.dp)
+                    .assertPositionInRootIsEqualTo(20.dp, 10.dp)
+            }
+
+            at(48) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(90.dp, 70.dp)
+                    .assertPositionInRootIsEqualTo(30.dp, 15.dp)
+            }
+
+            after {
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(100.dp, 80.dp)
+                    .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+            }
+        }
+    }
+
+    @Test
+    fun replaceAnimation_elementInCurrentSceneAndOneOverlay() {
+        val sharedIntKey = ValueKey("sharedInt")
+        val sharedIntValueByContent = mutableMapOf<ContentKey, Int>()
+
+        @Composable
+        fun SceneScope.animateContentInt(targetValue: Int) {
+            val animatedValue = animateContentIntAsState(targetValue, sharedIntKey)
+            LaunchedEffect(animatedValue) {
+                try {
+                    snapshotFlow { animatedValue.value }
+                        .collect { sharedIntValueByContent[contentKey] = it }
+                } finally {
+                    sharedIntValueByContent.remove(contentKey)
+                }
+            }
+        }
+
+        rule.testReplaceOverlayTransition(
+            currentSceneContent = {
+                Box(Modifier.size(width = 180.dp, height = 120.dp)) {
+                    animateContentInt(targetValue = 1_000)
+                    Foo(width = 60.dp, height = 40.dp)
+                }
+            },
+            fromContent = {},
+            fromAlignment = Alignment.TopStart,
+            toContent = {
+                animateContentInt(targetValue = 2_000)
+                Foo(width = 100.dp, height = 80.dp)
+            },
+            transition = {
+                // 4 frames of animation
+                spec = tween(4 * 16, easing = LinearEasing)
+            },
+        ) {
+            // Foo moves from (0,0) with a size of 60x40dp to centered (in a 180x120dp Box) with a
+            // size of 100x80dp, so at (40,20).
+            //
+            // The animated Int goes from 1_000 to 2_000.
+            before {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertSizeIsEqualTo(60.dp, 40.dp)
+                    .assertPositionInRootIsEqualTo(0.dp, 0.dp)
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule.onNode(isElement(TestElements.Foo, content = OverlayB)).assertDoesNotExist()
+
+                assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_000)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayB)
+            }
+
+            at(16) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(70.dp, 50.dp)
+                    .assertPositionInRootIsEqualTo(10.dp, 5.dp)
+
+                assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_250)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+                assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_250)
+            }
+
+            at(32) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(80.dp, 60.dp)
+                    .assertPositionInRootIsEqualTo(20.dp, 10.dp)
+
+                assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_500)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+                assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_500)
+            }
+
+            at(48) {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(90.dp, 70.dp)
+                    .assertPositionInRootIsEqualTo(30.dp, 15.dp)
+
+                assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_750)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+                assertThat(sharedIntValueByContent).containsEntry(OverlayB, 1_750)
+            }
+
+            after {
+                rule
+                    .onNode(isElement(TestElements.Foo, content = SceneA))
+                    .assertExists()
+                    .assertIsNotDisplayed()
+                rule.onNode(isElement(TestElements.Foo, content = OverlayA)).assertDoesNotExist()
+                rule
+                    .onNode(isElement(TestElements.Foo, content = OverlayB))
+                    .assertSizeIsEqualTo(100.dp, 80.dp)
+                    .assertPositionInRootIsEqualTo(40.dp, 20.dp)
+
+                // Outside of transitions, the value is equal to the target value in each content.
+                assertThat(sharedIntValueByContent).containsEntry(SceneA, 1_000)
+                assertThat(sharedIntValueByContent).doesNotContainKey(OverlayA)
+                assertThat(sharedIntValueByContent).containsEntry(OverlayB, 2_000)
+            }
+        }
+    }
+
+    @Test
+    fun overscrollingOverlay_movableElementNotInOverlay() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutStateImpl(
+                    SceneA,
+                    transitions {
+                        // Make OverlayA overscrollable.
+                        overscroll(OverlayA, orientation = Orientation.Horizontal) {
+                            translate(ElementKey("elementThatDoesNotExist"), x = 10.dp)
+                        }
+                    }
+                )
+            }
+
+        val key = MovableElementKey("Foo", contents = setOf(SceneA))
+        val movableElementChildTag = "movableElementChildTag"
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state) {
+                    scene(SceneA) {
+                        MovableElement(key, Modifier) {
+                            content { Box(Modifier.testTag(movableElementChildTag).size(100.dp)) }
+                        }
+                    }
+                    overlay(OverlayA) { /* Does not contain the element. */ }
+                }
+            }
+
+        // Overscroll on Overlay A.
+        scope.launch { state.startTransition(transition(SceneA, OverlayA, progress = { 1.5f })) }
+        rule
+            .onNode(hasTestTag(movableElementChildTag) and 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/PredictiveBackHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
index 0eaecb0..9284ffd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/PredictiveBackHandlerTest.kt
@@ -18,12 +18,20 @@
 
 import androidx.activity.BackEventCompat
 import androidx.activity.ComponentActivity
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasTestTag
 import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestOverlays.OverlayA
+import com.android.compose.animation.scene.TestOverlays.OverlayB
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
@@ -59,7 +67,23 @@
 
     @Test
     fun testPredictiveBack() {
-        val layoutState = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+        val transitionFrames = 2
+        val layoutState =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    transitions =
+                        transitions {
+                            from(SceneA, to = SceneB) {
+                                spec =
+                                    tween(
+                                        durationMillis = transitionFrames * 16,
+                                        easing = LinearEasing
+                                    )
+                            }
+                        }
+                )
+            }
         rule.setContent {
             SceneTransitionLayout(layoutState) {
                 scene(SceneA, mapOf(Back to SceneB)) { Box(Modifier.fillMaxSize()) }
@@ -76,7 +100,7 @@
             dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
         }
 
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0.4f)
@@ -88,12 +112,27 @@
         assertThat(layoutState.transitionState).hasCurrentScene(SceneA)
         assertThat(layoutState.transitionState).isIdle()
 
+        rule.mainClock.autoAdvance = false
+
         // Start again and commit it.
         rule.runOnUiThread {
             dispatcher.dispatchOnBackStarted(backEvent())
             dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
             dispatcher.onBackPressed()
         }
+        rule.mainClock.advanceTimeByFrame()
+        rule.waitForIdle()
+        val transition2 = assertThat(layoutState.transitionState).isSceneTransition()
+        // verify that transition picks up progress from preview
+        assertThat(transition2).hasProgress(0.4f, tolerance = 0.0001f)
+
+        rule.mainClock.advanceTimeByFrame()
+        rule.waitForIdle()
+        // verify that transition is half way between preview-end-state (0.4f) and target-state (1f)
+        // after one frame
+        assertThat(transition2).hasProgress(0.7f, tolerance = 0.0001f)
+
+        rule.mainClock.autoAdvance = true
         rule.waitForIdle()
         assertThat(layoutState.transitionState).hasCurrentScene(SceneB)
         assertThat(layoutState.transitionState).isIdle()
@@ -124,7 +163,7 @@
             dispatcher.dispatchOnBackProgressed(backEvent(progress = 0.4f))
         }
 
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasPreviewProgress(0.4f)
@@ -178,13 +217,13 @@
         val dispatcher = rule.activity.onBackPressedDispatcher
         rule.runOnUiThread { dispatcher.dispatchOnBackStarted(backEvent()) }
 
-        val predictiveTransition = assertThat(layoutState.transitionState).isTransition()
+        val predictiveTransition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(predictiveTransition).hasFromScene(SceneA)
         assertThat(predictiveTransition).hasToScene(SceneB)
 
         // Start a new transition to C.
         rule.runOnUiThread { layoutState.setTargetScene(SceneC, coroutineScope) }
-        val newTransition = assertThat(layoutState.transitionState).isTransition()
+        val newTransition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(newTransition).hasFromScene(SceneA)
         assertThat(newTransition).hasToScene(SceneC)
 
@@ -198,6 +237,42 @@
         assertThat(canChangeSceneCalled).isFalse()
     }
 
+    @Test
+    fun backDismissesOverlayWithHighestZIndexByDefault() {
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    initialOverlays = setOf(OverlayA, OverlayB)
+                )
+            }
+
+        rule.setContent {
+            SceneTransitionLayout(state, Modifier.size(200.dp)) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) }
+                overlay(OverlayA) { Box(Modifier.fillMaxSize()) }
+                overlay(OverlayB) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        // Initial state.
+        rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed()
+        rule.onNode(hasTestTag(OverlayA.testTag)).assertIsDisplayed()
+        rule.onNode(hasTestTag(OverlayB.testTag)).assertIsDisplayed()
+
+        // Press back. This should hide overlay B because it has a higher zIndex than overlay A.
+        rule.runOnUiThread { rule.activity.onBackPressedDispatcher.onBackPressed() }
+        rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed()
+        rule.onNode(hasTestTag(OverlayA.testTag)).assertIsDisplayed()
+        rule.onNode(hasTestTag(OverlayB.testTag)).assertDoesNotExist()
+
+        // Press back again. This should hide overlay A.
+        rule.runOnUiThread { rule.activity.onBackPressedDispatcher.onBackPressed() }
+        rule.onNode(hasTestTag(SceneA.testTag)).assertIsDisplayed()
+        rule.onNode(hasTestTag(OverlayA.testTag)).assertDoesNotExist()
+        rule.onNode(hasTestTag(OverlayB.testTag)).assertDoesNotExist()
+    }
+
     private fun backEvent(progress: Float = 0f): BackEventCompat {
         return BackEventCompat(
             touchX = 0f,
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 6b417ee..d356c25 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
@@ -18,7 +18,9 @@
 
 import android.util.Log
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.animation.scene.TestScenes.SceneA
@@ -28,14 +30,18 @@
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.subjects.assertThat
 import com.android.compose.animation.scene.transition.link.StateLink
+import com.android.compose.animation.scene.transition.seekToScene
+import com.android.compose.test.MonotonicClockTestScope
+import com.android.compose.test.TestSceneTransition
 import com.android.compose.test.runMonotonicClockTest
+import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
+import kotlin.coroutines.cancellation.CancellationException
 import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancelAndJoin
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.consumeAsFlow
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.test.runTest
 import org.junit.Rule
 import org.junit.Test
@@ -56,9 +62,12 @@
     }
 
     @Test
-    fun isTransitioningTo_transition() {
+    fun isTransitioningTo_transition() = runTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(transition(from = SceneA, to = SceneB))
+        state.startTransitionImmediately(
+            animationScope = backgroundScope,
+            transition(from = SceneA, to = SceneB)
+        )
 
         assertThat(state.isTransitioning()).isTrue()
         assertThat(state.isTransitioning(from = SceneA)).isTrue()
@@ -71,17 +80,16 @@
     @Test
     fun setTargetScene_idleToSameScene() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutState(SceneA)
-        assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
+        assertThat(state.setTargetScene(SceneA, animationScope = this)).isNull()
     }
 
     @Test
     fun setTargetScene_idleToDifferentScene() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutState(SceneA)
-        val transition = state.setTargetScene(SceneB, coroutineScope = this)
-        assertThat(transition).isNotNull()
+        val (transition, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
         assertThat(state.transitionState).isEqualTo(transition)
 
-        transition!!.finish().join()
+        job.join()
         assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
     }
 
@@ -89,11 +97,10 @@
     fun setTargetScene_transitionToSameScene() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutState(SceneA)
 
-        val transition = state.setTargetScene(SceneB, coroutineScope = this)
-        assertThat(transition).isNotNull()
-        assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNull()
+        val (_, job) = checkNotNull(state.setTargetScene(SceneB, animationScope = this))
+        assertThat(state.setTargetScene(SceneB, animationScope = this)).isNull()
 
-        transition!!.finish().join()
+        job.join()
         assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
     }
 
@@ -101,32 +108,21 @@
     fun setTargetScene_transitionToDifferentScene() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutState(SceneA)
 
-        assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
-        val transition = state.setTargetScene(SceneC, coroutineScope = this)
-        assertThat(transition).isNotNull()
+        assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
+        val (_, job) = checkNotNull(state.setTargetScene(SceneC, animationScope = this))
 
-        transition!!.finish().join()
+        job.join()
         assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneC))
     }
 
     @Test
-    fun setTargetScene_transitionToOriginalScene() = runMonotonicClockTest {
-        val state = MutableSceneTransitionLayoutState(SceneA)
-        assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
-
-        // Progress is 0f, so we don't animate at all and directly snap back to A.
-        assertThat(state.setTargetScene(SceneA, coroutineScope = this)).isNull()
-        assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneA))
-    }
-
-    @Test
     fun setTargetScene_coroutineScopeCancelled() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutState(SceneA)
 
         lateinit var transition: TransitionState.Transition
         val job =
             launch(start = CoroutineStart.UNDISPATCHED) {
-                transition = state.setTargetScene(SceneB, coroutineScope = this)!!
+                transition = checkNotNull(state.setTargetScene(SceneB, animationScope = this)).first
             }
         assertThat(state.transitionState).isEqualTo(transition)
 
@@ -135,18 +131,6 @@
         assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB))
     }
 
-    @Test
-    fun transition_finishReturnsTheSameJobWhenCalledMultipleTimes() = runMonotonicClockTest {
-        val state = MutableSceneTransitionLayoutState(SceneA)
-        val transition = state.setTargetScene(SceneB, coroutineScope = this)
-        assertThat(transition).isNotNull()
-
-        val job = transition!!.finish()
-        assertThat(transition.finish()).isSameInstanceAs(job)
-        assertThat(transition.finish()).isSameInstanceAs(job)
-        assertThat(transition.finish()).isSameInstanceAs(job)
-    }
-
     private fun setupLinkedStates(
         parentInitialScene: SceneKey = SceneC,
         childInitialScene: SceneKey = SceneA,
@@ -171,22 +155,24 @@
     }
 
     @Test
-    fun linkedTransition_startsLinkAndFinishesLinkInToState() {
+    fun linkedTransition_startsLinkAndFinishesLinkInToState() = runTest {
         val (parentState, childState) = setupLinkedStates()
 
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
-        childState.finishTransition(childTransition, SceneB)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
     }
 
     @Test
-    fun linkedTransition_transitiveLink() {
+    fun linkedTransition_transitiveLink() = runTest {
         val parentParentState =
             MutableSceneTransitionLayoutState(SceneB) as MutableSceneTransitionLayoutStateImpl
         val parentLink =
@@ -212,25 +198,27 @@
 
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
         assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue()
 
-        childState.finishTransition(childTransition, SceneB)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
         assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
     }
 
     @Test
-    fun linkedTransition_linkProgressIsEqual() {
+    fun linkedTransition_linkProgressIsEqual() = runTest {
         val (parentState, childState) = setupLinkedStates()
 
         var progress = 0f
         val childTransition = transition(SceneA, SceneB, progress = { progress })
 
-        childState.startTransition(childTransition)
+        childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(parentState.currentTransition?.progress).isEqualTo(0f)
 
         progress = .5f
@@ -238,64 +226,48 @@
     }
 
     @Test
-    fun linkedTransition_reverseTransitionIsNotLinked() {
+    fun linkedTransition_reverseTransitionIsNotLinked() = runTest {
         val (parentState, childState) = setupLinkedStates()
 
-        val childTransition = transition(SceneB, SceneA)
+        val childTransition = transition(SceneB, SceneA, current = { SceneB })
 
-        childState.startTransition(childTransition)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue()
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
 
-        childState.finishTransition(childTransition, SceneB)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
     }
 
     @Test
-    fun linkedTransition_startsLinkAndFinishesLinkInFromState() {
+    fun linkedTransition_startsLinkAndFinishesLinkInFromState() = runTest {
         val (parentState, childState) = setupLinkedStates()
 
-        val childTransition = transition(SceneA, SceneB)
-        childState.startTransition(childTransition)
+        val childTransition = transition(SceneA, SceneB, current = { SceneA })
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
 
-        childState.finishTransition(childTransition, SceneA)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
     }
 
     @Test
-    fun linkedTransition_startsLinkAndFinishesLinkInUnknownState() {
-        val (parentState, childState) = setupLinkedStates()
-
-        val childTransition = transition(SceneA, SceneB)
-        childState.startTransition(childTransition)
-
-        childState.finishTransition(childTransition, SceneD)
-        assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
-        assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
-    }
-
-    @Test
     fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest {
         val (parentState, childState) = setupLinkedStates()
 
-        val childTransition =
-            transition(
-                SceneA,
-                SceneB,
-                onFinish = { launch { /* Do nothing. */ } },
-            )
-        val parentTransition =
-            transition(
-                SceneC,
-                SceneA,
-                onFinish = { launch { /* Do nothing. */ } },
-            )
-        childState.startTransition(childTransition)
-        parentState.startTransition(parentTransition)
+        val childTransition = transition(SceneA, SceneB)
+        val parentTransition = transition(SceneC, SceneA)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
+        parentState.startTransitionImmediately(animationScope = backgroundScope, parentTransition)
 
-        childState.finishTransition(childTransition, SceneB)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
         assertThat(parentState.transitionState).isEqualTo(parentTransition)
     }
@@ -317,11 +289,11 @@
             )
 
         // Default transition from A to B.
-        assertThat(state.setTargetScene(SceneB, coroutineScope = this)).isNotNull()
+        assertThat(state.setTargetScene(SceneB, animationScope = this)).isNotNull()
         assertThat(state.currentTransition?.transformationSpec?.transformations).hasSize(1)
 
         // Go back to A.
-        state.setTargetScene(SceneA, coroutineScope = this)
+        state.setTargetScene(SceneA, animationScope = this)
         testScheduler.advanceUntilIdle()
         assertThat(state.transitionState).isIdle()
         assertThat(state.transitionState).hasCurrentScene(SceneA)
@@ -330,7 +302,7 @@
         assertThat(
                 state.setTargetScene(
                     SceneB,
-                    coroutineScope = this,
+                    animationScope = this,
                     transitionKey = transitionkey,
                 )
             )
@@ -341,7 +313,10 @@
     @Test
     fun snapToIdleIfClose_snapToStart() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.2f }))
+        state.startTransitionImmediately(
+            animationScope = backgroundScope,
+            transition(from = SceneA, to = SceneB, current = { SceneA }, progress = { 0.2f })
+        )
         assertThat(state.isTransitioning()).isTrue()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -357,7 +332,10 @@
     @Test
     fun snapToIdleIfClose_snapToEnd() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
-        state.startTransition(transition(from = SceneA, to = SceneB, progress = { 0.8f }))
+        state.startTransitionImmediately(
+            animationScope = backgroundScope,
+            transition(from = SceneA, to = SceneB, progress = { 0.8f })
+        )
         assertThat(state.isTransitioning()).isTrue()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -374,18 +352,12 @@
     fun snapToIdleIfClose_multipleTransitions() = runMonotonicClockTest {
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
 
-        val aToB =
-            transition(
-                from = SceneA,
-                to = SceneB,
-                progress = { 0.5f },
-                onFinish = { launch { /* do nothing */ } },
-            )
-        state.startTransition(aToB)
+        val aToB = transition(from = SceneA, to = SceneB, progress = { 0.5f })
+        state.startTransitionImmediately(animationScope = backgroundScope, aToB)
         assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
 
         val bToC = transition(from = SceneB, to = SceneC, progress = { 0.8f })
-        state.startTransition(bToC)
+        state.startTransitionImmediately(animationScope = backgroundScope, bToC)
         assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
 
         // Ignore the request if the progress is not close to 0 or 1, using the threshold.
@@ -399,47 +371,77 @@
     }
 
     @Test
-    fun linkedTransition_fuzzyLinksAreMatchedAndStarted() {
+    fun snapToIdleIfClose_closeButNotCurrentScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutStateImpl(SceneA, SceneTransitions.Empty)
+        var progress by mutableStateOf(0f)
+        var currentScene by mutableStateOf(SceneB)
+        state.startTransitionImmediately(
+            animationScope = backgroundScope,
+            transition(
+                from = SceneA,
+                to = SceneB,
+                current = { currentScene },
+                progress = { progress }
+            )
+        )
+        assertThat(state.isTransitioning()).isTrue()
+
+        // Ignore the request if we are close to a scene that is not the current scene
+        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
+        assertThat(state.isTransitioning()).isTrue()
+
+        progress = 1f
+        currentScene = SceneA
+        assertThat(state.snapToIdleIfClose(threshold = 0.1f)).isFalse()
+        assertThat(state.isTransitioning()).isTrue()
+    }
+
+    @Test
+    fun linkedTransition_fuzzyLinksAreMatchedAndStarted() = runTest {
         val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD)
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
-        childState.finishTransition(childTransition, SceneB)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD))
     }
 
     @Test
-    fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() {
+    fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() = runTest {
         val (parentState, childState) =
             setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD)
 
-        val childTransition = transition(SceneA, SceneB)
+        val childTransition = transition(SceneA, SceneB, current = { SceneA })
 
-        childState.startTransition(childTransition)
+        val job =
+            childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue()
 
-        childState.finishTransition(childTransition, SceneA)
+        childTransition.finish()
+        job.join()
         assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA))
         assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC))
     }
 
     @Test
-    fun linkedTransition_fuzzyLinksAreNotMatched() {
+    fun linkedTransition_fuzzyLinksAreNotMatched() = runTest {
         val (parentState, childState) =
             setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD)
         val childTransition = transition(SceneA, SceneB)
 
-        childState.startTransition(childTransition)
+        childState.startTransitionImmediately(animationScope = backgroundScope, childTransition)
         assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue()
         assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse()
     }
 
-    private fun startOverscrollableTransistionFromAtoB(
+    private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB(
         progress: () -> Float,
         sceneTransitions: SceneTransitions,
     ): MutableSceneTransitionLayoutStateImpl {
@@ -448,7 +450,8 @@
                 SceneA,
                 sceneTransitions,
             )
-        state.startTransition(
+        state.startTransitionImmediately(
+            animationScope = backgroundScope,
             transition(
                 from = SceneA,
                 to = SceneB,
@@ -471,7 +474,7 @@
                         overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) }
                     }
             )
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasNoOverscrollSpec()
 
         // overscroll for SceneA is NOT defined
@@ -488,7 +491,7 @@
         // overscroll for SceneB is defined
         progress.value = 1.1f
         val overscrollSpec = assertThat(transition).hasOverscrollSpec()
-        assertThat(overscrollSpec.scene).isEqualTo(SceneB)
+        assertThat(overscrollSpec.content).isEqualTo(SceneB)
     }
 
     @Test
@@ -503,13 +506,13 @@
                     }
             )
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasNoOverscrollSpec()
 
         // overscroll for SceneA is defined
         progress.value = -0.1f
         val overscrollSpec = assertThat(transition).hasOverscrollSpec()
-        assertThat(overscrollSpec.scene).isEqualTo(SceneA)
+        assertThat(overscrollSpec.content).isEqualTo(SceneA)
 
         // scroll from SceneA to SceneB
         progress.value = 0.5f
@@ -532,7 +535,7 @@
                 sceneTransitions = transitions {}
             )
 
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasNoOverscrollSpec()
 
         // overscroll for SceneA is NOT defined
@@ -553,54 +556,54 @@
 
     @Test
     fun multipleTransitions() = runTest {
-        val finishingTransitions = mutableSetOf<TransitionState.Transition>()
-        fun onFinish(transition: TransitionState.Transition): Job {
-            // Instead of letting the transition finish, we put the transition in the
-            // finishingTransitions set so that we can verify that finish() is called when expected
-            // and then we call state STLState.finishTransition() ourselves.
-            finishingTransitions.add(transition)
+        val frozenTransitions = mutableSetOf<TestSceneTransition>()
+        fun onFreezeAndAnimate(transition: TestSceneTransition): () -> Unit {
+            // Instead of letting the transition finish when it is frozen, we put the transition in
+            // the frozenTransitions set so that we can verify that freezeAndAnimateToCurrentState()
+            // is called when expected and then we call finish() ourselves to finish the
+            // transitions.
+            frozenTransitions.add(transition)
 
-            return backgroundScope.launch {
-                // Try to acquire a locked mutex so that this code never completes.
-                Mutex(locked = true).withLock {}
-            }
+            return { /* do nothing */ }
         }
 
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
-        val aToB = transition(SceneA, SceneB, onFinish = ::onFinish)
-        val bToC = transition(SceneB, SceneC, onFinish = ::onFinish)
-        val cToA = transition(SceneC, SceneA, onFinish = ::onFinish)
+        val aToB = transition(SceneA, SceneB, onFreezeAndAnimate = ::onFreezeAndAnimate)
+        val bToC = transition(SceneB, SceneC, onFreezeAndAnimate = ::onFreezeAndAnimate)
+        val cToA = transition(SceneC, SceneA, onFreezeAndAnimate = ::onFreezeAndAnimate)
 
         // Starting state.
-        assertThat(finishingTransitions).isEmpty()
+        assertThat(frozenTransitions).isEmpty()
         assertThat(state.currentTransitions).isEmpty()
 
         // A => B.
-        state.startTransition(aToB)
-        assertThat(finishingTransitions).isEmpty()
+        val aToBJob = state.startTransitionImmediately(animationScope = backgroundScope, aToB)
+        assertThat(frozenTransitions).isEmpty()
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB).inOrder()
 
-        // B => C. This should automatically call finish() on aToB.
-        state.startTransition(bToC)
-        assertThat(finishingTransitions).containsExactly(aToB)
+        // B => C. This should automatically call freezeAndAnimateToCurrentState() on aToB.
+        val bToCJob = state.startTransitionImmediately(animationScope = backgroundScope, bToC)
+        assertThat(frozenTransitions).containsExactly(aToB)
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
 
-        // C => A. This should automatically call finish() on bToC.
-        state.startTransition(cToA)
-        assertThat(finishingTransitions).containsExactly(aToB, bToC)
+        // C => A. This should automatically call freezeAndAnimateToCurrentState() on bToC.
+        state.startTransitionImmediately(animationScope = backgroundScope, cToA)
+        assertThat(frozenTransitions).containsExactly(aToB, bToC)
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
 
         // Mark bToC as finished. The list of current transitions does not change because aToB is
         // still not marked as finished.
-        state.finishTransition(bToC, idleScene = bToC.currentScene)
-        assertThat(state.finishedTransitions).containsExactly(bToC, bToC.currentScene)
+        bToC.finish()
+        bToCJob.join()
+        assertThat(state.finishedTransitions).containsExactly(bToC)
         assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
 
         // Mark aToB as finished. This will remove both aToB and bToC from the list of transitions.
-        state.finishTransition(aToB, idleScene = aToB.currentScene)
+        aToB.finish()
+        aToBJob.join()
         assertThat(state.finishedTransitions).isEmpty()
         assertThat(state.currentTransitions).containsExactly(cToA).inOrder()
     }
@@ -610,8 +613,9 @@
         val state = MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
 
         fun startTransition() {
-            val transition = transition(SceneA, SceneB, onFinish = { launch { /* do nothing */ } })
-            state.startTransition(transition)
+            val transition =
+                transition(SceneA, SceneB, onFreezeAndAnimate = { launch { /* do nothing */ } })
+            state.startTransitionImmediately(animationScope = backgroundScope, transition)
         }
 
         var hasLoggedWtf = false
@@ -634,8 +638,8 @@
         val state = MutableSceneTransitionLayoutState(SceneA)
 
         // Transition to B.
-        state.setTargetScene(SceneB, coroutineScope = this)
-        val transition = assertThat(state.transitionState).isTransition()
+        state.setTargetScene(SceneB, animationScope = this)
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasCurrentScene(SceneB)
 
         // Snap to C.
@@ -643,4 +647,92 @@
         assertThat(state.transitionState).isIdle()
         assertThat(state.transitionState).hasCurrentScene(SceneC)
     }
+
+    @Test
+    fun snapToScene_freezesCurrentTransition() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutStateImpl(SceneA)
+
+        // Start a transition that is never finished. We don't use backgroundScope on purpose so
+        // that this test would fail if the transition was not frozen when snapping.
+        state.startTransitionImmediately(animationScope = this, transition(SceneA, SceneB))
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+
+        // Snap to C.
+        state.snapToScene(SceneC)
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneC)
+    }
+
+    @Test
+    fun seekToScene() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(SceneA)
+        val progress = Channel<Float>()
+
+        val job =
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                state.seekToScene(SceneB, progress.consumeAsFlow())
+            }
+
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasProgress(0f)
+
+        // Change progress.
+        progress.send(0.4f)
+        assertThat(transition).hasProgress(0.4f)
+
+        // Close the channel normally to confirm the transition.
+        progress.close()
+        job.join()
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneB)
+    }
+
+    @Test
+    fun seekToScene_cancelled() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(SceneA)
+        val progress = Channel<Float>()
+
+        val job =
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                state.seekToScene(SceneB, progress.consumeAsFlow())
+            }
+
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(transition).hasFromScene(SceneA)
+        assertThat(transition).hasToScene(SceneB)
+        assertThat(transition).hasProgress(0f)
+
+        // Change progress.
+        progress.send(0.4f)
+        assertThat(transition).hasProgress(0.4f)
+
+        // Close the channel with a CancellationException to cancel the transition.
+        progress.close(CancellationException())
+        job.join()
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneA)
+    }
+
+    @Test
+    fun seekToScene_interrupted() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(SceneA)
+        val progress = Channel<Float>()
+
+        val job =
+            launch(start = CoroutineStart.UNDISPATCHED) {
+                state.seekToScene(SceneB, progress.consumeAsFlow())
+            }
+
+        assertThat(state.transitionState).isSceneTransition()
+
+        // Start a new transition, interrupting the seek transition.
+        state.setTargetScene(SceneB, animationScope = this)
+
+        // The previous job is cancelled and does not infinitely collect the progress.
+        job.join()
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 84bcc28f..63ab04f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -55,10 +55,13 @@
 import com.android.compose.animation.scene.TestScenes.SceneC
 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.subjects.DpOffsetSubject
 import com.android.compose.test.subjects.assertThat
+import com.android.compose.test.transition
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
@@ -179,7 +182,7 @@
 
         // Change the current scene.
         currentScene = SceneB
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0f)
@@ -241,7 +244,7 @@
         // 100.dp. We pause at the middle of the transition, so it should now be 75.dp given that we
         // use a linear interpolator. Foo was at (x = layoutSize - 50dp, y = 0) in SceneA and is
         // going to (x = 0, y = 0), so the offset should now be half what it was.
-        var transition = assertThat(layoutState.transitionState).isTransition()
+        var transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
         sharedFoo.assertWidthIsEqualTo(75.dp)
         sharedFoo.assertHeightIsEqualTo(75.dp)
@@ -269,7 +272,7 @@
         val expectedSize = 100.dp + (150.dp - 100.dp) * interpolatedProgress
 
         sharedFoo = rule.onNode(isElement(TestElements.Foo, SceneC))
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(interpolatedProgress)
         sharedFoo.assertWidthIsEqualTo(expectedSize)
         sharedFoo.assertHeightIsEqualTo(expectedSize)
@@ -327,17 +330,18 @@
             }
 
         val layoutTag = "layout"
-        rule.setContent {
-            SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
-                scene(SceneA) { Box(Modifier.size(50.dp)) }
-                scene(SceneB) { Box(Modifier.size(70.dp)) }
+        val scope =
+            rule.setContentAndCreateMainScope {
+                SceneTransitionLayout(state, Modifier.testTag(layoutTag)) {
+                    scene(SceneA) { Box(Modifier.size(50.dp)) }
+                    scene(SceneB) { Box(Modifier.size(70.dp)) }
+                }
             }
-        }
 
         // Overscroll on A at -100%: size should be interpolated given that there is no overscroll
         // defined for scene A.
         var progress by mutableStateOf(-1f)
-        rule.runOnIdle {
+        scope.launch {
             state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress }))
         }
         rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp)
@@ -399,7 +403,7 @@
         rule.mainClock.advanceTimeBy(duration / 2)
         rule.waitForIdle()
 
-        var transition = assertThat(state.transitionState).isTransition()
+        var transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0.5f)
 
         // A and B are composed.
@@ -412,7 +416,7 @@
         rule.mainClock.advanceTimeByFrame()
         rule.waitForIdle()
 
-        transition = assertThat(state.transitionState).isTransition()
+        transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasProgress(0f)
 
         // A, B and C are composed.
@@ -500,4 +504,19 @@
         assertThat(keyInB).isEqualTo(SceneB)
         assertThat(keyInC).isEqualTo(SceneC)
     }
+
+    @Test
+    fun overlaysMapIsNotAllocatedWhenNoOverlayIsDefined() {
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(
+                remember { MutableSceneTransitionLayoutState(SceneA) },
+                onLayoutImpl = { layoutImpl = it },
+            ) {
+                scene(SceneA) { Box(Modifier.fillMaxSize()) }
+            }
+        }
+
+        assertThat(layoutImpl.overlaysOrNullForTest()).isNull()
+    }
 }
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 04a9380..e48cd817 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
@@ -155,7 +155,7 @@
 
         // We should be at a progress = 55dp / LayoutWidth given that we use the layout size in
         // the gesture axis as swipe distance.
-        var transition = assertThat(layoutState.transitionState).isTransition()
+        var transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasCurrentScene(SceneA)
@@ -165,7 +165,7 @@
         // Release the finger. We should now be animating back to A (currentScene = SceneA) given
         // that 55dp < positional threshold.
         rule.onRoot().performTouchInput { up() }
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasCurrentScene(SceneA)
@@ -185,7 +185,7 @@
         }
 
         // Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(TestScenes.SceneC)
         assertThat(transition).hasCurrentScene(SceneA)
@@ -195,7 +195,7 @@
         // Release the finger. We should now be animating to C (currentScene = SceneC) given
         // that 56dp >= positional threshold.
         rule.onRoot().performTouchInput { up() }
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(TestScenes.SceneC)
         assertThat(transition).hasCurrentScene(TestScenes.SceneC)
@@ -236,7 +236,7 @@
 
         // We should be animating back to A (currentScene = SceneA) given that 124 dp/s < velocity
         // threshold.
-        var transition = assertThat(layoutState.transitionState).isTransition()
+        var transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasCurrentScene(SceneA)
@@ -260,7 +260,7 @@
         }
 
         // We should be animating to C (currentScene = SceneC).
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(TestScenes.SceneC)
         assertThat(transition).hasCurrentScene(TestScenes.SceneC)
@@ -297,7 +297,7 @@
         }
 
         // We are transitioning to B because we used 2 fingers.
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(TestScenes.SceneC)
         assertThat(transition).hasToScene(SceneB)
 
@@ -332,7 +332,7 @@
         }
 
         // We are transitioning to B (and not A) because we started from the top edge.
-        var transition = assertThat(layoutState.transitionState).isTransition()
+        var transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(TestScenes.SceneC)
         assertThat(transition).hasToScene(SceneB)
 
@@ -350,7 +350,7 @@
         }
 
         // We are transitioning to B (and not A) because we started from the left edge.
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(TestScenes.SceneC)
         assertThat(transition).hasToScene(SceneB)
 
@@ -406,7 +406,7 @@
         }
 
         // We should be at 50%
-        val transition = assertThat(layoutState.transitionState).isTransition()
+        val transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).isNotNull()
         assertThat(transition).hasProgress(0.5f)
     }
@@ -427,7 +427,7 @@
         }
 
         // We should still correctly compute that we are swiping down to scene C.
-        var transition = assertThat(layoutState.transitionState).isTransition()
+        var transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasToScene(TestScenes.SceneC)
 
         // Release the finger, animating back to scene A.
@@ -443,7 +443,7 @@
         }
 
         // We should still correctly compute that we are swiping up to scene B.
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasToScene(SceneB)
 
         // Release the finger, animating back to scene A.
@@ -459,7 +459,7 @@
         }
 
         // We should still correctly compute that we are swiping down to scene B.
-        transition = assertThat(layoutState.transitionState).isTransition()
+        transition = assertThat(layoutState.transitionState).isSceneTransition()
         assertThat(transition).hasToScene(SceneB)
     }
 
@@ -597,7 +597,7 @@
         }
 
         rule.waitForIdle()
-        val transition = assertThat(state.transitionState).isTransition()
+        val transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0.5f, tolerance = 0.01f)
@@ -614,6 +614,7 @@
                         from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
 
                         overscroll(SceneB, Orientation.Vertical) {
+                            progressConverter = ProgressConverter.linear()
                             translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
                         }
                     }
@@ -686,7 +687,7 @@
         }
 
         // Scene B should come from the right (end) edge.
-        var transition = assertThat(state.transitionState).isTransition()
+        var transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         rule
@@ -707,7 +708,7 @@
         }
 
         // Scene C should come from the left (start) edge.
-        transition = assertThat(state.transitionState).isTransition()
+        transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneC)
         rule
@@ -761,7 +762,7 @@
         }
 
         // Scene C should come from the right (start) edge.
-        var transition = assertThat(state.transitionState).isTransition()
+        var transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneC)
         rule
@@ -782,7 +783,7 @@
         }
 
         // Scene C should come from the left (end) edge.
-        transition = assertThat(state.transitionState).isTransition()
+        transition = assertThat(state.transitionState).isSceneTransition()
         assertThat(transition).hasFromScene(SceneA)
         assertThat(transition).hasToScene(SceneB)
         rule
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
deleted file mode 100644
index 91bd7e1..0000000
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ /dev/null
@@ -1,107 +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.compose.animation.scene
-
-import androidx.compose.foundation.gestures.Orientation
-import com.android.compose.animation.scene.content.state.ContentState
-import com.android.compose.animation.scene.content.state.TransitionState
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.test.TestScope
-
-/** A utility to easily create a [TransitionState.Transition] in tests. */
-fun transition(
-    from: SceneKey,
-    to: SceneKey,
-    current: () -> SceneKey = { from },
-    progress: () -> Float = { 0f },
-    progressVelocity: () -> Float = { 0f },
-    previewProgress: () -> Float = { 0f },
-    previewProgressVelocity: () -> Float = { 0f },
-    isInPreviewStage: () -> Boolean = { false },
-    interruptionProgress: () -> Float = { 0f },
-    isInitiatedByUserInput: Boolean = false,
-    isUserInputOngoing: Boolean = false,
-    isUpOrLeft: Boolean = false,
-    bouncingContent: ContentKey? = null,
-    orientation: Orientation = Orientation.Horizontal,
-    onFinish: ((TransitionState.Transition) -> Job)? = null,
-    replacedTransition: TransitionState.Transition? = null,
-): TransitionState.Transition {
-    return object :
-        TransitionState.Transition(from, to, replacedTransition),
-        ContentState.HasOverscrollProperties {
-        override val currentScene: SceneKey
-            get() = current()
-
-        override val progress: Float
-            get() = progress()
-
-        override val progressVelocity: Float
-            get() = progressVelocity()
-
-        override val previewProgress: Float
-            get() = previewProgress()
-
-        override val previewProgressVelocity: Float
-            get() = previewProgressVelocity()
-
-        override val isInPreviewStage: Boolean
-            get() = isInPreviewStage()
-
-        override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
-        override val isUserInputOngoing: Boolean = isUserInputOngoing
-        override val isUpOrLeft: Boolean = isUpOrLeft
-        override val bouncingContent: ContentKey? = bouncingContent
-        override val orientation: Orientation = orientation
-        override val overscrollScope: OverscrollScope =
-            object : OverscrollScope {
-                override val density: Float = 1f
-                override val fontScale: Float = 1f
-                override val absoluteDistance = 0f
-            }
-
-        override fun finish(): Job {
-            val onFinish =
-                onFinish
-                    ?: error(
-                        "onFinish() must be provided if finish() is called on test transitions"
-                    )
-
-            return onFinish(this)
-        }
-
-        override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
-            return interruptionProgress()
-        }
-    }
-}
-
-/**
- * Return a onFinish lambda that can be used with [transition] so that the transition never
- * finishes. This allows to keep the transition in the current transitions list.
- */
-fun TestScope.neverFinish(): (TransitionState.Transition) -> Job {
-    return {
-        backgroundScope.launch {
-            // Try to acquire a locked mutex so that this code never completes.
-            Mutex(locked = true).withLock {}
-        }
-    }
-}
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 68240b5..f8068e6 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
@@ -16,6 +16,7 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.animation.core.CubicBezierEasing
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.animation.core.TweenSpec
 import androidx.compose.animation.core.spring
@@ -107,6 +108,13 @@
                 fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
                 fractionRange(start = 0.2f) { fade(TestElements.Foo) }
                 fractionRange(end = 0.9f) { fade(TestElements.Foo) }
+                fractionRange(
+                    start = 0.1f,
+                    end = 0.8f,
+                    easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+                ) {
+                    fade(TestElements.Foo)
+                }
             }
         }
 
@@ -118,6 +126,11 @@
                 TransformationRange(start = 0.1f, end = 0.8f),
                 TransformationRange(start = 0.2f, end = TransformationRange.BoundUnspecified),
                 TransformationRange(start = TransformationRange.BoundUnspecified, end = 0.9f),
+                TransformationRange(
+                    start = 0.1f,
+                    end = 0.8f,
+                    CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+                ),
             )
     }
 
@@ -130,6 +143,13 @@
                 timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
                 timestampRange(startMillis = 200) { fade(TestElements.Foo) }
                 timestampRange(endMillis = 400) { fade(TestElements.Foo) }
+                timestampRange(
+                    startMillis = 100,
+                    endMillis = 300,
+                    easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+                ) {
+                    fade(TestElements.Foo)
+                }
             }
         }
 
@@ -141,6 +161,11 @@
                 TransformationRange(start = 100 / 500f, end = 300 / 500f),
                 TransformationRange(start = 200 / 500f, end = TransformationRange.BoundUnspecified),
                 TransformationRange(start = TransformationRange.BoundUnspecified, end = 400 / 500f),
+                TransformationRange(
+                    start = 100 / 500f,
+                    end = 300 / 500f,
+                    easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+                ),
             )
     }
 
@@ -207,6 +232,47 @@
     }
 
     @Test
+    fun defaultPredictiveBack() {
+        val transitions = transitions {
+            from(
+                TestScenes.SceneA,
+                to = TestScenes.SceneB,
+                preview = { fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) } }
+            ) {
+                spec = tween(500)
+                fractionRange(start = 0.1f, end = 0.8f) { fade(TestElements.Foo) }
+                timestampRange(startMillis = 100, endMillis = 300) { fade(TestElements.Foo) }
+            }
+        }
+
+        // Verify that fetching the transitionSpec with the PredictiveBack key defaults to the above
+        // transition despite it not having the PredictiveBack key set.
+        val transitionSpec =
+            transitions.transitionSpec(
+                from = TestScenes.SceneA,
+                to = TestScenes.SceneB,
+                key = TransitionKey.PredictiveBack
+            )
+
+        val transformations = transitionSpec.transformationSpec().transformations
+
+        assertThat(transformations)
+            .comparingElementsUsing(TRANSFORMATION_RANGE)
+            .containsExactly(
+                TransformationRange(start = 0.1f, end = 0.8f),
+                TransformationRange(start = 100 / 500f, end = 300 / 500f),
+            )
+
+        val previewTransformations = transitionSpec.previewTransformationSpec()?.transformations
+
+        assertThat(previewTransformations)
+            .comparingElementsUsing(TRANSFORMATION_RANGE)
+            .containsExactly(
+                TransformationRange(start = 0.1f, end = 0.8f),
+            )
+    }
+
+    @Test
     fun springSpec() {
         val defaultSpec = spring<Float>(stiffness = 1f)
         val specFromAToC = spring<Float>(stiffness = 2f)
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
index a12ab78..44e0ba5 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/subjects/TransitionStateSubject.kt
@@ -16,9 +16,10 @@
 
 package com.android.compose.animation.scene.subjects
 
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.OverscrollSpec
 import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.content.state.ContentState
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.google.common.truth.Fact.simpleFact
 import com.google.common.truth.FailureMetadata
@@ -31,9 +32,25 @@
     return Truth.assertAbout(TransitionStateSubject.transitionStates()).that(state)
 }
 
-/** Assert on a [TransitionState.Transition]. */
-fun assertThat(transitions: TransitionState.Transition): TransitionSubject {
-    return Truth.assertAbout(TransitionSubject.transitions()).that(transitions)
+/** Assert on a [TransitionState.Transition.ChangeScene]. */
+fun assertThat(transition: TransitionState.Transition.ChangeScene): SceneTransitionSubject {
+    return Truth.assertAbout(SceneTransitionSubject.sceneTransitions()).that(transition)
+}
+
+/** Assert on a [TransitionState.Transition.ShowOrHideOverlay]. */
+fun assertThat(
+    transition: TransitionState.Transition.ShowOrHideOverlay,
+): ShowOrHideOverlayTransitionSubject {
+    return Truth.assertAbout(ShowOrHideOverlayTransitionSubject.showOrHideOverlayTransitions())
+        .that(transition)
+}
+
+/** Assert on a [TransitionState.Transition.ReplaceOverlay]. */
+fun assertThat(
+    transition: TransitionState.Transition.ReplaceOverlay,
+): ReplaceOverlayTransitionSubject {
+    return Truth.assertAbout(ReplaceOverlayTransitionSubject.replaceOverlayTransitions())
+        .that(transition)
 }
 
 class TransitionStateSubject
@@ -45,6 +62,10 @@
         check("currentScene").that(actual.currentScene).isEqualTo(sceneKey)
     }
 
+    fun hasCurrentOverlays(vararg overlays: OverlayKey) {
+        check("currentOverlays").that(actual.currentOverlays).containsExactlyElementsIn(overlays)
+    }
+
     fun isIdle(): TransitionState.Idle {
         if (actual !is TransitionState.Idle) {
             failWithActual(simpleFact("expected to be TransitionState.Idle"))
@@ -53,12 +74,32 @@
         return actual as TransitionState.Idle
     }
 
-    fun isTransition(): TransitionState.Transition {
-        if (actual !is TransitionState.Transition) {
-            failWithActual(simpleFact("expected to be TransitionState.Transition"))
+    fun isSceneTransition(): TransitionState.Transition.ChangeScene {
+        if (actual !is TransitionState.Transition.ChangeScene) {
+            failWithActual(
+                simpleFact("expected to be TransitionState.Transition.ChangeCurrentScene")
+            )
         }
 
-        return actual as TransitionState.Transition
+        return actual as TransitionState.Transition.ChangeScene
+    }
+
+    fun isShowOrHideOverlayTransition(): TransitionState.Transition.ShowOrHideOverlay {
+        if (actual !is TransitionState.Transition.ShowOrHideOverlay) {
+            failWithActual(
+                simpleFact("expected to be TransitionState.Transition.ShowOrHideOverlay")
+            )
+        }
+
+        return actual as TransitionState.Transition.ShowOrHideOverlay
+    }
+
+    fun isReplaceOverlayTransition(): TransitionState.Transition.ReplaceOverlay {
+        if (actual !is TransitionState.Transition.ReplaceOverlay) {
+            failWithActual(simpleFact("expected to be TransitionState.Transition.ReplaceOverlay"))
+        }
+
+        return actual as TransitionState.Transition.ReplaceOverlay
     }
 
     companion object {
@@ -68,21 +109,16 @@
     }
 }
 
-class TransitionSubject
-private constructor(
+abstract class BaseTransitionSubject<T : TransitionState.Transition>(
     metadata: FailureMetadata,
-    private val actual: TransitionState.Transition,
+    protected val actual: T,
 ) : Subject(metadata, actual) {
     fun hasCurrentScene(sceneKey: SceneKey) {
         check("currentScene").that(actual.currentScene).isEqualTo(sceneKey)
     }
 
-    fun hasFromScene(sceneKey: SceneKey) {
-        check("fromScene").that(actual.fromScene).isEqualTo(sceneKey)
-    }
-
-    fun hasToScene(sceneKey: SceneKey) {
-        check("toScene").that(actual.toScene).isEqualTo(sceneKey)
+    fun hasCurrentOverlays(vararg overlays: OverlayKey) {
+        check("currentOverlays").that(actual.currentOverlays).containsExactlyElementsIn(overlays)
     }
 
     fun hasProgress(progress: Float, tolerance: Float = 0f) {
@@ -132,19 +168,77 @@
         check("currentOverscrollSpec").that(actual.currentOverscrollSpec).isNull()
     }
 
-    fun hasBouncingScene(scene: SceneKey) {
-        if (actual !is ContentState.HasOverscrollProperties) {
+    fun hasBouncingContent(content: ContentKey) {
+        val actual = actual
+        if (actual !is TransitionState.HasOverscrollProperties) {
             failWithActual(simpleFact("expected to be ContentState.HasOverscrollProperties"))
         }
 
         check("bouncingContent")
-            .that((actual as ContentState.HasOverscrollProperties).bouncingContent)
-            .isEqualTo(scene)
+            .that((actual as TransitionState.HasOverscrollProperties).bouncingContent)
+            .isEqualTo(content)
+    }
+}
+
+class SceneTransitionSubject
+private constructor(
+    metadata: FailureMetadata,
+    actual: TransitionState.Transition.ChangeScene,
+) : BaseTransitionSubject<TransitionState.Transition.ChangeScene>(metadata, actual) {
+    fun hasFromScene(sceneKey: SceneKey) {
+        check("fromScene").that(actual.fromScene).isEqualTo(sceneKey)
+    }
+
+    fun hasToScene(sceneKey: SceneKey) {
+        check("toScene").that(actual.toScene).isEqualTo(sceneKey)
     }
 
     companion object {
-        fun transitions() = Factory { metadata, actual: TransitionState.Transition ->
-            TransitionSubject(metadata, actual)
-        }
+        fun sceneTransitions() =
+            Factory { metadata, actual: TransitionState.Transition.ChangeScene ->
+                SceneTransitionSubject(metadata, actual)
+            }
+    }
+}
+
+class ShowOrHideOverlayTransitionSubject
+private constructor(
+    metadata: FailureMetadata,
+    actual: TransitionState.Transition.ShowOrHideOverlay,
+) : BaseTransitionSubject<TransitionState.Transition.ShowOrHideOverlay>(metadata, actual) {
+    fun hasFromOrToScene(fromOrToScene: SceneKey) {
+        check("fromOrToScene").that(actual.fromOrToScene).isEqualTo(fromOrToScene)
+    }
+
+    fun hasOverlay(overlay: OverlayKey) {
+        check("overlay").that(actual.overlay).isEqualTo(overlay)
+    }
+
+    companion object {
+        fun showOrHideOverlayTransitions() =
+            Factory { metadata, actual: TransitionState.Transition.ShowOrHideOverlay ->
+                ShowOrHideOverlayTransitionSubject(metadata, actual)
+            }
+    }
+}
+
+class ReplaceOverlayTransitionSubject
+private constructor(
+    metadata: FailureMetadata,
+    actual: TransitionState.Transition.ReplaceOverlay,
+) : BaseTransitionSubject<TransitionState.Transition.ReplaceOverlay>(metadata, actual) {
+    fun hasFromOverlay(fromOverlay: OverlayKey) {
+        check("fromOverlay").that(actual.fromOverlay).isEqualTo(fromOverlay)
+    }
+
+    fun hasToOverlay(toOverlay: OverlayKey) {
+        check("toOverlay").that(actual.toOverlay).isEqualTo(toOverlay)
+    }
+
+    companion object {
+        fun replaceOverlayTransitions() =
+            Factory { metadata, actual: TransitionState.Transition.ReplaceOverlay ->
+                ReplaceOverlayTransitionSubject(metadata, actual)
+            }
     }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
new file mode 100644
index 0000000..e4a87ba
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.testing
+
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.Scale
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.testTransition
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ElementStateAccessTest {
+
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun testElementStateAccess() {
+        rule.testTransition(
+            fromSceneContent = { Box(Modifier.element(TestElements.Foo).size(50.dp)) },
+            toSceneContent = { Box(Modifier) },
+            transition = {
+                spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
+                fade(TestElements.Foo)
+                scaleDraw(TestElements.Foo, scaleX = 0f, scaleY = 0f)
+            },
+            fromScene = SceneA,
+            toScene = SceneB,
+        ) {
+            before {
+                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(1f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
+            }
+
+            at(32) {
+                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
+            }
+
+            at(64) {
+                val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+                assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
+                assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
+            }
+            after { onElement(TestElements.Foo).assertDoesNotExist() }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
index 46075c3..5bfc947 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/AnchoredTranslateTest.kt
@@ -27,7 +27,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.animation.scene.TestElements
 import com.android.compose.animation.scene.testTransition
-import com.android.compose.animation.scene.transition
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt
new file mode 100644
index 0000000..07901f2
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/transformation/EasingTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.compose.animation.scene.transformation
+
+import androidx.compose.animation.core.CubicBezierEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.unit.dp
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestElements
+import com.android.compose.animation.scene.testTransition
+import com.android.compose.test.assertSizeIsEqualTo
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class EasingTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun testFractionRangeEasing() {
+        val easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+        rule.testTransition(
+            fromSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Foo)) },
+            toSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Bar)) },
+            transition = {
+                // Scale during 4 frames.
+                spec = tween(16 * 4, easing = LinearEasing)
+                fractionRange(easing = easing) {
+                    scaleSize(TestElements.Foo, width = 0f, height = 0f)
+                    scaleSize(TestElements.Bar, width = 0f, height = 0f)
+                }
+            },
+        ) {
+            // Foo is entering, is 100dp x 100dp at rest and is scaled by (2, 0.5) during the
+            // transition so it starts at 200dp x 50dp.
+            before { onElement(TestElements.Bar).assertDoesNotExist() }
+            at(0) {
+                onElement(TestElements.Foo).assertSizeIsEqualTo(100.dp, 100.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(0.dp, 0.dp)
+            }
+            at(16) {
+                // 25% linear progress is mapped to 68.5% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(31.5.dp, 31.5.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(68.5.dp, 68.5.dp)
+            }
+            at(32) {
+                // 50% linear progress is mapped to 89.5% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(10.5.dp, 10.5.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(89.5.dp, 89.5.dp)
+            }
+            at(48) {
+                // 75% linear progress is mapped to 97.8% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(2.2.dp, 2.2.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(97.8.dp, 97.8.dp)
+            }
+            after {
+                onElement(TestElements.Foo).assertDoesNotExist()
+                onElement(TestElements.Bar).assertSizeIsEqualTo(100.dp, 100.dp)
+            }
+        }
+    }
+
+    @Test
+    fun testTimestampRangeEasing() {
+        val easing = CubicBezierEasing(0.1f, 0.1f, 0f, 1f)
+        rule.testTransition(
+            fromSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Foo)) },
+            toSceneContent = { Box(Modifier.size(100.dp).element(TestElements.Bar)) },
+            transition = {
+                // Scale during 4 frames.
+                spec = tween(16 * 4, easing = LinearEasing)
+                timestampRange(easing = easing) {
+                    scaleSize(TestElements.Foo, width = 0f, height = 0f)
+                    scaleSize(TestElements.Bar, width = 0f, height = 0f)
+                }
+            },
+        ) {
+            // Foo is entering, is 100dp x 100dp at rest and is scaled by (2, 0.5) during the
+            // transition so it starts at 200dp x 50dp.
+            before { onElement(TestElements.Bar).assertDoesNotExist() }
+            at(0) {
+                onElement(TestElements.Foo).assertSizeIsEqualTo(100.dp, 100.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(0.dp, 0.dp)
+            }
+            at(16) {
+                // 25% linear progress is mapped to 68.5% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(31.5.dp, 31.5.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(68.5.dp, 68.5.dp)
+            }
+            at(32) {
+                // 50% linear progress is mapped to 89.5% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(10.5.dp, 10.5.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(89.5.dp, 89.5.dp)
+            }
+            at(48) {
+                // 75% linear progress is mapped to 97.8% eased progress
+                onElement(TestElements.Foo).assertSizeIsEqualTo(2.2.dp, 2.2.dp)
+                onElement(TestElements.Bar).assertSizeIsEqualTo(97.8.dp, 97.8.dp)
+            }
+            after {
+                onElement(TestElements.Foo).assertDoesNotExist()
+                onElement(TestElements.Bar).assertSizeIsEqualTo(100.dp, 100.dp)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.kt
new file mode 100644
index 0000000..28a864f
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/SetContentAndCreateScope.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.compose.test
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+
+/**
+ * Set [content] as this rule's content and return a [CoroutineScope] bound to [Dispatchers.Main]
+ * and scoped to this rule.
+ */
+fun ComposeContentTestRule.setContentAndCreateMainScope(
+    content: @Composable () -> Unit,
+): CoroutineScope {
+    lateinit var coroutineScope: CoroutineScope
+    setContent {
+        coroutineScope = rememberCoroutineScope(getContext = { Dispatchers.Main })
+        content()
+    }
+    return coroutineScope
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
new file mode 100644
index 0000000..646cff8
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestOverlayTransition.kt
@@ -0,0 +1,112 @@
+/*
+ * 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.test
+
+import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.OverlayKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
+import kotlinx.coroutines.CompletableDeferred
+
+/** A [Transition.ShowOrHideOverlay] for tests that will be finished once [finish] is called. */
+abstract class TestOverlayTransition(
+    fromScene: SceneKey,
+    overlay: OverlayKey,
+    replacedTransition: Transition?,
+) :
+    Transition.ShowOrHideOverlay(
+        overlay = overlay,
+        fromOrToScene = fromScene,
+        fromContent = fromScene,
+        toContent = overlay,
+        replacedTransition = replacedTransition,
+    ) {
+    private val finishCompletable = CompletableDeferred<Unit>()
+
+    override suspend fun run() {
+        finishCompletable.await()
+    }
+
+    /** Finish this transition. */
+    fun finish() {
+        finishCompletable.complete(Unit)
+    }
+}
+
+/** A utility to easily create a [TestOverlayTransition] in tests. */
+fun transition(
+    fromScene: SceneKey,
+    overlay: OverlayKey,
+    isEffectivelyShown: () -> Boolean = { true },
+    progress: () -> Float = { 0f },
+    progressVelocity: () -> Float = { 0f },
+    previewProgress: () -> Float = { 0f },
+    previewProgressVelocity: () -> Float = { 0f },
+    isInPreviewStage: () -> Boolean = { false },
+    interruptionProgress: () -> Float = { 0f },
+    isInitiatedByUserInput: Boolean = false,
+    isUserInputOngoing: Boolean = false,
+    isUpOrLeft: Boolean = false,
+    bouncingContent: ContentKey? = null,
+    orientation: Orientation = Orientation.Horizontal,
+    onFreezeAndAnimate: ((TestOverlayTransition) -> Unit)? = null,
+    replacedTransition: Transition? = null,
+): TestOverlayTransition {
+    return object :
+        TestOverlayTransition(fromScene, overlay, replacedTransition),
+        TransitionState.HasOverscrollProperties {
+        override val isEffectivelyShown: Boolean
+            get() = isEffectivelyShown()
+
+        override val progress: Float
+            get() = progress()
+
+        override val progressVelocity: Float
+            get() = progressVelocity()
+
+        override val previewProgress: Float
+            get() = previewProgress()
+
+        override val previewProgressVelocity: Float
+            get() = previewProgressVelocity()
+
+        override val isInPreviewStage: Boolean
+            get() = isInPreviewStage()
+
+        override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
+        override val isUserInputOngoing: Boolean = isUserInputOngoing
+        override val isUpOrLeft: Boolean = isUpOrLeft
+        override val bouncingContent: ContentKey? = bouncingContent
+        override val orientation: Orientation = orientation
+        override val absoluteDistance = 0f
+
+        override fun freezeAndAnimateToCurrentState() {
+            if (onFreezeAndAnimate != null) {
+                onFreezeAndAnimate(this)
+            } else {
+                finish()
+            }
+        }
+
+        override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
+            return interruptionProgress()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
new file mode 100644
index 0000000..d24b895
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/TestSceneTransition.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.test
+
+import androidx.compose.foundation.gestures.Orientation
+import com.android.compose.animation.scene.ContentKey
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitionLayoutImpl
+import com.android.compose.animation.scene.content.state.TransitionState
+import com.android.compose.animation.scene.content.state.TransitionState.Transition
+import kotlinx.coroutines.CompletableDeferred
+
+/** A [Transition.ChangeScene] for tests that will be finished once [finish] is called. */
+abstract class TestSceneTransition(
+    fromScene: SceneKey,
+    toScene: SceneKey,
+    replacedTransition: Transition?,
+) : Transition.ChangeScene(fromScene, toScene, replacedTransition) {
+    private val finishCompletable = CompletableDeferred<Unit>()
+
+    override suspend fun run() {
+        finishCompletable.await()
+    }
+
+    /** Finish this transition. */
+    fun finish() {
+        finishCompletable.complete(Unit)
+    }
+}
+
+/** A utility to easily create a [TestSceneTransition] in tests. */
+fun transition(
+    from: SceneKey,
+    to: SceneKey,
+    current: () -> SceneKey = { to },
+    progress: () -> Float = { 0f },
+    progressVelocity: () -> Float = { 0f },
+    previewProgress: () -> Float = { 0f },
+    previewProgressVelocity: () -> Float = { 0f },
+    isInPreviewStage: () -> Boolean = { false },
+    interruptionProgress: () -> Float = { 0f },
+    isInitiatedByUserInput: Boolean = false,
+    isUserInputOngoing: Boolean = false,
+    isUpOrLeft: Boolean = false,
+    bouncingContent: ContentKey? = null,
+    orientation: Orientation = Orientation.Horizontal,
+    onFreezeAndAnimate: ((TestSceneTransition) -> Unit)? = null,
+    replacedTransition: Transition? = null,
+): TestSceneTransition {
+    return object :
+        TestSceneTransition(from, to, replacedTransition), TransitionState.HasOverscrollProperties {
+        override val currentScene: SceneKey
+            get() = current()
+
+        override val progress: Float
+            get() = progress()
+
+        override val progressVelocity: Float
+            get() = progressVelocity()
+
+        override val previewProgress: Float
+            get() = previewProgress()
+
+        override val previewProgressVelocity: Float
+            get() = previewProgressVelocity()
+
+        override val isInPreviewStage: Boolean
+            get() = isInPreviewStage()
+
+        override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
+        override val isUserInputOngoing: Boolean = isUserInputOngoing
+        override val isUpOrLeft: Boolean = isUpOrLeft
+        override val bouncingContent: ContentKey? = bouncingContent
+        override val orientation: Orientation = orientation
+        override val absoluteDistance = 0f
+
+        override fun freezeAndAnimateToCurrentState() {
+            if (onFreezeAndAnimate != null) {
+                onFreezeAndAnimate(this)
+            } else {
+                finish()
+            }
+        }
+
+        override fun interruptionProgress(layoutImpl: SceneTransitionLayoutImpl): Float {
+            return interruptionProgress()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
index 00adefb1..5cccfb1 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestContentScope.kt
@@ -26,9 +26,9 @@
 @Composable
 fun TestContentScope(
     modifier: Modifier = Modifier,
+    currentScene: SceneKey = remember { SceneKey("current") },
     content: @Composable ContentScope.() -> Unit,
 ) {
-    val currentScene = remember { SceneKey("current") }
     val state = remember { MutableSceneTransitionLayoutState(currentScene) }
     SceneTransitionLayout(state, modifier) { scene(currentScene, content = content) }
 }
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 6d063a0..22450d3 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
@@ -20,11 +20,16 @@
 import androidx.compose.ui.test.hasAnyAncestor
 import androidx.compose.ui.test.hasTestTag
 
-/** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */
-fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher {
-    return if (scene == null) {
+/** A [SemanticsMatcher] that matches [element], optionally restricted to content [content]. */
+fun isElement(element: ElementKey, content: ContentKey? = null): SemanticsMatcher {
+    return if (content == null) {
         hasTestTag(element.testTag)
     } else {
-        hasTestTag(element.testTag) and hasAnyAncestor(hasTestTag(scene.testTag))
+        hasTestTag(element.testTag) and inContent(content)
     }
 }
+
+/** A [SemanticsMatcher] that matches anything inside [content]. */
+fun inContent(content: ContentKey): SemanticsMatcher {
+    return hasAnyAncestor(hasTestTag(content.testTag))
+}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
index 7f26b98..25f9564 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestTransition.kt
@@ -16,9 +16,12 @@
 
 package com.android.compose.animation.scene
 
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.semantics.SemanticsNode
 import androidx.compose.ui.test.SemanticsNodeInteraction
@@ -115,6 +118,97 @@
     )
 }
 
+/** Test the transition when showing [overlay] from [fromScene]. */
+fun ComposeContentTestRule.testShowOverlayTransition(
+    fromSceneContent: @Composable ContentScope.() -> Unit,
+    overlayContent: @Composable ContentScope.() -> Unit,
+    transition: TransitionBuilder.() -> Unit,
+    fromScene: SceneKey = TestScenes.SceneA,
+    overlay: OverlayKey = TestOverlays.OverlayA,
+    builder: TransitionTestBuilder.() -> Unit,
+) {
+    testTransition(
+        state =
+            runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    fromScene,
+                    transitions = transitions { from(fromScene, overlay, builder = transition) },
+                )
+            },
+        transitionLayout = { state ->
+            SceneTransitionLayout(state) {
+                scene(fromScene) { fromSceneContent() }
+                overlay(overlay) { overlayContent() }
+            }
+        },
+        changeState = { state -> state.showOverlay(overlay, animationScope = this) },
+        builder = builder,
+    )
+}
+
+/** Test the transition when hiding [overlay] to [toScene]. */
+fun ComposeContentTestRule.testHideOverlayTransition(
+    toSceneContent: @Composable ContentScope.() -> Unit,
+    overlayContent: @Composable ContentScope.() -> Unit,
+    transition: TransitionBuilder.() -> Unit,
+    toScene: SceneKey = TestScenes.SceneA,
+    overlay: OverlayKey = TestOverlays.OverlayA,
+    builder: TransitionTestBuilder.() -> Unit,
+) {
+    testTransition(
+        state =
+            runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    toScene,
+                    initialOverlays = setOf(overlay),
+                    transitions = transitions { from(overlay, toScene, builder = transition) },
+                )
+            },
+        transitionLayout = { state ->
+            SceneTransitionLayout(state) {
+                scene(toScene) { toSceneContent() }
+                overlay(overlay) { overlayContent() }
+            }
+        },
+        changeState = { state -> state.hideOverlay(overlay, animationScope = this) },
+        builder = builder,
+    )
+}
+
+/** Test the transition when replace [from] to [to]. */
+fun ComposeContentTestRule.testReplaceOverlayTransition(
+    fromContent: @Composable ContentScope.() -> Unit,
+    toContent: @Composable ContentScope.() -> Unit,
+    transition: TransitionBuilder.() -> Unit,
+    currentSceneContent: @Composable ContentScope.() -> Unit = { Box(Modifier.fillMaxSize()) },
+    fromAlignment: Alignment = Alignment.Center,
+    toAlignment: Alignment = Alignment.Center,
+    from: OverlayKey = TestOverlays.OverlayA,
+    to: OverlayKey = TestOverlays.OverlayB,
+    currentScene: SceneKey = TestScenes.SceneA,
+    builder: TransitionTestBuilder.() -> Unit,
+) {
+    testTransition(
+        state =
+            runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    currentScene,
+                    initialOverlays = setOf(from),
+                    transitions = transitions { from(from, to, builder = transition) },
+                )
+            },
+        transitionLayout = { state ->
+            SceneTransitionLayout(state) {
+                scene(currentScene) { currentSceneContent() }
+                overlay(from, alignment = fromAlignment) { fromContent() }
+                overlay(to, alignment = toAlignment) { toContent() }
+            }
+        },
+        changeState = { state -> state.replaceOverlay(from, to, animationScope = this) },
+        builder = builder,
+    )
+}
+
 data class TransitionRecordingSpec(
     val recordBefore: Boolean = true,
     val recordAfter: Boolean = true,
@@ -152,7 +246,7 @@
         content = { play ->
             LaunchedEffect(play) {
                 if (play) {
-                    state.setTargetScene(toScene, coroutineScope = this)
+                    state.setTargetScene(toScene, animationScope = this)
                 }
             }
 
@@ -188,6 +282,21 @@
             "(${currentScene.debugName})"
     }
 
+    testTransition(
+        state = state,
+        changeState = { state -> state.setTargetScene(to, animationScope = this) },
+        transitionLayout = transitionLayout,
+        builder = builder,
+    )
+}
+
+/** Test the transition from [state] to [to]. */
+fun ComposeContentTestRule.testTransition(
+    state: MutableSceneTransitionLayoutState,
+    changeState: CoroutineScope.(MutableSceneTransitionLayoutState) -> Unit,
+    transitionLayout: @Composable (state: MutableSceneTransitionLayoutState) -> Unit,
+    builder: TransitionTestBuilder.() -> Unit,
+) {
     val test = transitionTest(builder)
     val assertionScope =
         object : TransitionTestAssertionScope {
@@ -213,7 +322,7 @@
     mainClock.autoAdvance = false
 
     // Change the current scene.
-    runOnUiThread { state.setTargetScene(to, coroutineScope) }
+    runOnUiThread { coroutineScope.changeState(state) }
     waitForIdle()
     mainClock.advanceTimeByFrame()
     waitForIdle()
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
index b83705a..f39dd67 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestValues.kt
@@ -21,7 +21,7 @@
 import androidx.compose.animation.core.snap
 import androidx.compose.animation.core.tween
 
-/** Scenes keys that can be reused by tests. */
+/** Scene keys that can be reused by tests. */
 object TestScenes {
     val SceneA = SceneKey("SceneA")
     val SceneB = SceneKey("SceneB")
@@ -29,6 +29,12 @@
     val SceneD = SceneKey("SceneD")
 }
 
+/** Overlay keys that can be reused by tests. */
+object TestOverlays {
+    val OverlayA = OverlayKey("OverlayA")
+    val OverlayB = OverlayKey("OverlayB")
+}
+
 /** Element keys that can be reused by tests. */
 object TestElements {
     val Foo = ElementKey("Foo")
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 9b725eb..954155d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -88,7 +88,6 @@
     val clockBuffers: ClockMessageBuffers? = null,
     val keepAllLoaded: Boolean,
     subTag: String,
-    var isTransitClockEnabled: Boolean = false,
     val assert: ThreadAssert = ThreadAssert(),
 ) {
     private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
@@ -188,10 +187,6 @@
                 var isClockListChanged = false
                 for (clock in plugin.getClocks()) {
                     val id = clock.clockId
-                    if (!isTransitClockEnabled && id == "DIGITAL_CLOCK_METRO") {
-                        continue
-                    }
-
                     val info =
                         availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
                             isClockListChanged = true
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS
new file mode 100644
index 0000000..f6f98e9
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 9d62e38..8721c78 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -182,9 +182,6 @@
         const val FLAG_NAME_WALLPAPER_PICKER_UI_FOR_AIWP = "wallpaper_picker_ui_for_aiwp"
 
         /** Flag denoting transit clock are enabled in wallpaper picker. */
-        const val FLAG_NAME_TRANSIT_CLOCK = "lockscreen_custom_transit_clock"
-
-        /** Flag denoting transit clock are enabled in wallpaper picker. */
         const val FLAG_NAME_PAGE_TRANSITIONS = "wallpaper_picker_page_transitions"
 
         /** Flag denoting adding apply button to wallpaper picker's grid preview page. */
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
index 362e23d..96d79df 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/notifications/data/repository/NotificationSettingsRepository.kt
@@ -71,7 +71,7 @@
             .stateIn(
                 scope = backgroundScope,
                 started = SharingStarted.Eagerly,
-                initialValue = false,
+                initialValue = true,
             )
 
     /** The default duration for DND mode when enabled. See [Settings.Secure.ZEN_DURATION]. */
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
index 08ee602..a3f40d4 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt
@@ -18,10 +18,17 @@
 package com.android.systemui.shared.quickaffordance.shared.model
 
 object KeyguardPreviewConstants {
+    const val MESSAGE_ID_DEFAULT_PREVIEW = 707
     const val MESSAGE_ID_HIDE_SMART_SPACE = 1111
-    const val KEY_HIDE_SMART_SPACE = "hide_smart_space"
+    const val MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED = 1988
     const val MESSAGE_ID_SLOT_SELECTED = 1337
-    const val KEY_SLOT_ID = "slot_id"
-    const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+    const val MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES = 214
+
+    const val KEY_HIDE_SMART_SPACE = "hide_smart_space"
     const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances"
+    const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id"
+    const val KEY_QUICK_AFFORDANCE_ID = "quick_affordance_id"
+    const val KEY_SLOT_ID = "slot_id"
+
+    const val KEYGUARD_QUICK_AFFORDANCE_ID_NONE = "none"
 }
diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md
index 2f50bbd..0ac15c5 100644
--- a/packages/SystemUI/docs/scene.md
+++ b/packages/SystemUI/docs/scene.md
@@ -121,11 +121,11 @@
 do so by defining their own scene. This section describes how to do that.
 
 Each scene is defined as an implementation of the
-[`ComposableScene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt)
+[`Scene`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/Scene.kt)
 interface, which has three parts: 1. The `key` property returns the
 [`SceneKey`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneKey.kt)
-that uniquely identifies that scene 2. The `destinationScenes` `Flow` returns
-the (potentially ever-changing) set of navigation edges to other scenes, based
+that uniquely identifies that scene 2. The `userActions` `Flow` returns
+the (potentially ever-changing) set of navigation edges to other content, based
 on user-actions, which is how the navigation graph is defined (see
 [the Scene navigation](#Scene-navigation) section for more) 3. The `Content`
 function which uses
@@ -138,10 +138,10 @@
 For example:
 
 ```kotlin
-@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : ComposableScene {
+@SysUISingleton class YourScene @Inject constructor( /* your dependencies here */ ) : Scene {
     override val key = SceneKey.YourScene
 
-    override val destinationScenes: StateFlow<Map<UserAction, SceneModel>> =
+    override val userActions: StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
             mapOf(
                 // This is where scene navigation is defined, more on that below.
diff --git a/packages/SystemUI/lint-baseline.xml b/packages/SystemUI/lint-baseline.xml
index b4c839f..7577147 100644
--- a/packages/SystemUI/lint-baseline.xml
+++ b/packages/SystemUI/lint-baseline.xml
@@ -32157,4 +32157,631 @@
             column="6"/>
     </issue>
 
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="                contentResolver.registerContentObserver("
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt"
+            line="154"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI,"
+        errorLine2="                     ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java"
+            line="164"
+            column="22"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="                resolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+            line="61"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="                awaitClose { resolver.unregisterContentObserver(observer) }"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/util/animation/data/repository/AnimationStatusRepository.kt"
+            line="67"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="                    mContentResolver.unregisterContentObserver(mSettingObserver);"
+        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+            line="123"
+            column="38"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContentResolver.unregisterContentObserver(mSettingObserver);"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+            line="180"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+            line="211"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java"
+            line="219"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        resolver.registerContentObserver("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+            line="46"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        resolver.registerContentObserver("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+            line="48"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        resolver.unregisterContentObserver(allowedObserver)"
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+            line="54"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        resolver.unregisterContentObserver(onObserver)"
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt"
+            line="55"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="                resolver.registerContentObserver(mQuickPickupGesture, false, this,"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+            line="511"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="                resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL);"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+            line="513"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="                resolver.registerContentObserver(mAlwaysOnEnabled, false, this,"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java"
+            line="514"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+            line="2470"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+            line="3077"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+            line="3168"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+            line="3957"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java"
+            line="3961"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        contentResolver.registerContentObserver("
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+            line="486"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        contentResolver.registerContentObserver("
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+            line="492"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        contentResolver.unregisterContentObserver(settingsObserver)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt"
+            line="543"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        contentResolver.registerContentObserver("
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+            line="82"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        contentResolver.unregisterContentObserver(settingsObserver)"
+        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenTargetFilter.kt"
+            line="100"
+            column="25"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContext.getContentResolver().unregisterContentObserver(mMenuTargetFeaturesContentObserver);"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+            line="275"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+            line="276"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java"
+            line="277"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+            line="200"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver(Global.getUriFor("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+            line="202"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContext.getContentResolver().unregisterContentObserver(mObserver);"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java"
+            line="212"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        context.contentResolver.registerContentObserver("
+        errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NaturalScrollingSettingObserver.kt"
+            line="54"
+            column="33"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+            line="253"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+            line="256"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+            line="259"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+            line="262"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContentResolver.unregisterContentObserver(mAssistContentObserver);"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java"
+            line="295"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+            line="395"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java"
+            line="400"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContentResolver.registerContentObserver("
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+            line="3675"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContentResolver.unregisterContentObserver(mSettingsChangeObserver);"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java"
+            line="4705"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        resolver.registerContentObserver(Settings.Global.getUriFor("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+            line="191"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        resolver.registerContentObserver("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+            line="205"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        resolver.registerContentObserver("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/power/PowerUI.java"
+            line="216"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="        mContext.getContentResolver().registerContentObserver("
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+            line="178"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);"
+        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java"
+            line="186"
+            column="39"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mContentResolver.registerContentObserver("
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+            line="83"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContentResolver.unregisterContentObserver(mContentObserver);"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/SecureSettingsContentObserver.java"
+            line="100"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+            line="219"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="        mContentResolver.unregisterContentObserver(mObserver);"
+        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+            line="241"
+            column="26"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);"
+        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java"
+            line="243"
+            column="30"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+            line="1235"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+            line="1236"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="            mContext.getContentResolver().unregisterContentObserver(this);"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java"
+            line="1240"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.unregisterContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.unregisterContentObserver()`"
+        errorLine1="                mResolver.unregisterContentObserver(this);"
+        errorLine2="                          ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+            line="377"
+            column="27"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mResolver.registerContentObserver("
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+            line="379"
+            column="23"/>
+    </issue>
+
+    <issue
+        id="RegisterContentObserverViaContentResolver"
+        message="`ContentResolver.registerContentObserver()` should be replaced with an appropriate interface API call, for eg.`&lt;SettingsProxy>/&lt;UserSettingsProxy>.registerContentObserver()`"
+        errorLine1="            mResolver.registerContentObserver("
+        errorLine2="                      ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java"
+            line="381"
+            column="23"/>
+    </issue>
+
 </issues>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 7707a60..062d351 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -29,6 +29,7 @@
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockscreenCredential
 import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor
+import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.flags.FakeFeatureFlags
@@ -56,7 +57,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import com.android.systemui.Flags as AconfigFlags
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -66,8 +66,7 @@
 class KeyguardPasswordViewControllerTest : SysuiTestCase() {
     @Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
     @Mock private lateinit var passwordEntry: EditText
-    private var passwordEntryLayoutParams =
-        ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0)
+    private var passwordEntryLayoutParams = ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0)
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
     @Mock lateinit var lockPatternUtils: LockPatternUtils
@@ -85,6 +84,7 @@
     private lateinit var mKeyguardMessageAreaController:
         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
     @Mock private lateinit var postureController: DevicePostureController
+    @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
     @Captor private lateinit var keyListenerArgumentCaptor: ArgumentCaptor<View.OnKeyListener>
 
     private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController
@@ -106,6 +106,8 @@
         whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
             .thenReturn(mock(ImageView::class.java))
         `when`(keyguardPasswordView.resources).thenReturn(context.resources)
+        // TODO(b/362362385): No need to mock keyguardPasswordView.context once this bug is fixed.
+        `when`(keyguardPasswordView.context).thenReturn(context)
         whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams)
         val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
         val fakeFeatureFlags = FakeFeatureFlags()
@@ -130,6 +132,8 @@
                 fakeFeatureFlags,
                 mSelectedUserInteractor,
                 keyguardKeyboardInteractor,
+                null,
+                mUserActivityNotifier
             )
     }
 
@@ -187,9 +191,11 @@
         verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
 
         val eventHandled =
-            keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+            keyListenerArgumentCaptor.value.onKey(
+                keyguardPasswordView,
                 KeyEvent.KEYCODE_SPACE,
-                KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE))
+                KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE)
+            )
 
         assertFalse("Unlock attempted.", eventHandled)
     }
@@ -204,9 +210,11 @@
         verify(passwordEntry).setOnKeyListener(keyListenerArgumentCaptor.capture())
 
         val eventHandled =
-            keyListenerArgumentCaptor.value.onKey(keyguardPasswordView,
+            keyListenerArgumentCaptor.value.onKey(
+                keyguardPasswordView,
                 KeyEvent.KEYCODE_ENTER,
-                KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER))
+                KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)
+            )
 
         assertTrue("Unlock not attempted.", eventHandled)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 0054d13..1076d90 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -87,6 +87,8 @@
     private View mOkButton;
     @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
+    @Mock
+    private UserActivityNotifier mUserActivityNotifier;
     private NumPadKey[] mButtons = new NumPadKey[]{};
 
     private KeyguardPinBasedInputViewController mKeyguardPinViewController;
@@ -117,7 +119,7 @@
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
                 mEmergencyButtonController, mFalsingCollector, featureFlags,
-                mSelectedUserInteractor, keyguardKeyboardInteractor) {
+                mSelectedUserInteractor, keyguardKeyboardInteractor, null, mUserActivityNotifier) {
             @Override
             public void onResume(int reason) {
                 super.onResume(reason);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index f7f69d3..e444db4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -18,10 +18,8 @@
 package com.android.keyguard
 
 import android.app.admin.DevicePolicyManager
-import android.app.admin.flags.Flags as DevicePolicyFlags
 import android.content.res.Configuration
 import android.media.AudioManager
-import android.platform.test.annotations.EnableFlags
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper.RunWithLooper
 import android.testing.TestableResources
@@ -56,6 +54,7 @@
 import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardDismissTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.Kosmos
@@ -153,6 +152,7 @@
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var postureController: DevicePostureController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
 
     @Captor
     private lateinit var swipeListenerArgumentCaptor:
@@ -238,6 +238,8 @@
                 featureFlags,
                 mSelectedUserInteractor,
                 keyguardKeyboardInteractor,
+                null,
+                mUserActivityNotifier
             )
 
         kosmos = testKosmos()
@@ -279,7 +281,7 @@
                 deviceProvisionedController,
                 faceAuthAccessibilityDelegate,
                 devicePolicyManager,
-                keyguardTransitionInteractor,
+                kosmos.keyguardDismissTransitionInteractor,
                 { primaryBouncerInteractor },
             ) {
                 deviceEntryInteractor
@@ -938,7 +940,6 @@
     }
 
     @Test
-    @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
     fun showAlmostAtWipeDialog_calledOnMainUser_setsCorrectUserType() {
         val mainUserId = 10
 
@@ -955,7 +956,6 @@
     }
 
     @Test
-    @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
     fun showAlmostAtWipeDialog_calledOnNonMainUser_setsCorrectUserType() {
         val secondaryUserId = 10
         val mainUserId = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/AccessibilityLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
new file mode 100644
index 0000000..c74d340
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.accessibility;
+
+import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.testing.TestableLooper;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IMagnificationConnectionCallback;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from
+ * {@link MagnificationImpl}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
+public class IMagnificationConnectionTest extends SysuiTestCase {
+
+    private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private CommandQueue mCommandQueue;
+    @Mock
+    private IMagnificationConnectionCallback mConnectionCallback;
+    @Mock
+    private WindowMagnificationController mWindowMagnificationController;
+    @Mock
+    private FullscreenMagnificationController mFullscreenMagnificationController;
+    @Mock
+    private MagnificationSettingsController mMagnificationSettingsController;
+    @Mock
+    private ModeSwitchesController mModeSwitchesController;
+    @Mock
+    private SysUiState mSysUiState;
+    @Mock
+    private IRemoteMagnificationAnimationCallback mAnimationCallback;
+    @Mock
+    private OverviewProxyService mOverviewProxyService;
+    @Mock
+    private SecureSettings mSecureSettings;
+    @Mock
+    private AccessibilityLogger mA11yLogger;
+    @Mock
+    private IWindowManager mIWindowManager;
+    @Mock
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+
+    private IMagnificationConnection mIMagnificationConnection;
+    private MagnificationImpl mMagnification;
+    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
+        doAnswer(invocation -> {
+            mIMagnificationConnection = invocation.getArgument(0);
+            return null;
+        }).when(mAccessibilityManager).setMagnificationConnection(
+                any(IMagnificationConnection.class));
+        mTestableLooper = TestableLooper.get(this);
+        assertNotNull(mTestableLooper);
+        mMagnification = new MagnificationImpl(getContext(),
+                mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
+                mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
+                mDisplayTracker, getContext().getSystemService(DisplayManager.class),
+                mA11yLogger, mIWindowManager, mAccessibilityManager,
+                mViewCaptureAwareWindowManager);
+        mMagnification.mWindowMagnificationControllerSupplier =
+                new FakeWindowMagnificationControllerSupplier(
+                        mContext.getSystemService(DisplayManager.class));
+        mMagnification.mFullscreenMagnificationControllerSupplier =
+                new FakeFullscreenMagnificationControllerSupplier(
+                        mContext.getSystemService(DisplayManager.class));
+        mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
+                mContext.getSystemService(DisplayManager.class));
+
+        mMagnification.requestMagnificationConnection(true);
+        assertNotNull(mIMagnificationConnection);
+        mIMagnificationConnection.setConnectionCallback(mConnectionCallback);
+    }
+
+    @Test
+    public void enableWindowMagnification_passThrough() throws RemoteException {
+        mIMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
+                Float.NaN, 0f, 0f, mAnimationCallback);
+        processAllPendingMessages();
+
+        verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f),
+                eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback));
+    }
+
+    @Test
+    public void onFullscreenMagnificationActivationChanged_passThrough() throws RemoteException {
+        mIMagnificationConnection.onFullscreenMagnificationActivationChanged(TEST_DISPLAY, true);
+        processAllPendingMessages();
+
+        verify(mFullscreenMagnificationController)
+                .onFullscreenMagnificationActivationChanged(eq(true));
+    }
+
+    @Test
+    public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
+        mIMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
+                mAnimationCallback);
+        processAllPendingMessages();
+
+        verify(mWindowMagnificationController).deleteWindowMagnification(
+                mAnimationCallback);
+    }
+
+    @Test
+    public void setScaleForWindowMagnification() throws RemoteException {
+        mIMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
+        processAllPendingMessages();
+
+        verify(mWindowMagnificationController).setScale(3.0f);
+    }
+
+    @Test
+    public void moveWindowMagnifier() throws RemoteException {
+        mIMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
+        processAllPendingMessages();
+
+        verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
+    }
+
+    @Test
+    public void moveWindowMagnifierToPosition() throws RemoteException {
+        mIMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY,
+                100f, 200f, mAnimationCallback);
+        processAllPendingMessages();
+
+        verify(mWindowMagnificationController).moveWindowMagnifierToPosition(
+                eq(100f), eq(200f), any(IRemoteMagnificationAnimationCallback.class));
+    }
+
+    @Test
+    public void showMagnificationButton_delayedShowButton() throws RemoteException {
+        // magnification settings panel should not be showing
+        assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+
+        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
+        // showMagnificationButton request to Magnification.
+        processAllPendingMessages();
+
+        // The delayed message would be processed after DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS.
+        // So call this processAllPendingMessages with a timeout to verify the showButton
+        // will be called.
+        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
+        processAllPendingMessages(timeout);
+        verify(mModeSwitchesController).showButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void showMagnificationButton_settingsPanelShowing_doNotShowButton()
+            throws RemoteException {
+        when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(true);
+
+        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
+        // showMagnificationButton request to Magnification.
+        processAllPendingMessages();
+
+        // The isMagnificationSettingsShowing will be checked after timeout, so
+        // process all message after a timeout here to verify the showButton will not be called.
+        processAllPendingMessages(DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100);
+        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void removeMagnificationButton() throws RemoteException {
+        mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
+        processAllPendingMessages();
+
+        verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
+    }
+
+    @Test
+    public void removeMagnificationButton_delayingShowButton_doNotShowButtonAfterTimeout()
+            throws RemoteException {
+        // magnification settings panel should not be showing
+        assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
+
+        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
+        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
+        // requests to Magnification.
+        processAllPendingMessages();
+
+        // Call this processAllPendingMessages with a timeout to ensure the delayed show button
+        // message should be removed and thus the showButton will not be called after timeout.
+        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
+        processAllPendingMessages(/* timeForwardMs= */ timeout);
+        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+    }
+
+    @Test
+    public void removeMagnificationSettingsPanel() throws RemoteException {
+        mIMagnificationConnection.removeMagnificationSettingsPanel(TEST_DISPLAY);
+        processAllPendingMessages();
+
+        verify(mMagnificationSettingsController).closeMagnificationSettings();
+    }
+
+    @Test
+    public void onUserMagnificationScaleChanged() throws RemoteException {
+        final int testUserId = 1;
+        final float testScale = 3.0f;
+        mIMagnificationConnection.onUserMagnificationScaleChanged(
+                testUserId, TEST_DISPLAY, testScale);
+        processAllPendingMessages();
+
+        assertTrue(mMagnification.mUsersScales.contains(testUserId));
+        assertEquals(mMagnification.mUsersScales.get(testUserId).get(TEST_DISPLAY),
+                (Float) testScale);
+        verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale));
+    }
+
+    private void processAllPendingMessages() {
+        processAllPendingMessages(/* timeForwardMs=*/ 0);
+    }
+
+    private void processAllPendingMessages(int timeForwardMs) {
+        if (timeForwardMs > 0) {
+            mTestableLooper.moveTimeForward(timeForwardMs);
+        }
+        mTestableLooper.processAllMessages();
+    }
+
+    private class FakeWindowMagnificationControllerSupplier extends
+            DisplayIdIndexSupplier<WindowMagnificationController> {
+
+        FakeWindowMagnificationControllerSupplier(DisplayManager displayManager) {
+            super(displayManager);
+        }
+
+        @Override
+        protected WindowMagnificationController createInstance(Display display) {
+            return mWindowMagnificationController;
+        }
+    }
+
+    private class FakeFullscreenMagnificationControllerSupplier extends
+            DisplayIdIndexSupplier<FullscreenMagnificationController> {
+
+        FakeFullscreenMagnificationControllerSupplier(DisplayManager displayManager) {
+            super(displayManager);
+        }
+
+        @Override
+        protected FullscreenMagnificationController createInstance(Display display) {
+            return mFullscreenMagnificationController;
+        }
+    }
+
+    private class FakeSettingsSupplier extends
+            DisplayIdIndexSupplier<MagnificationSettingsController> {
+
+        FakeSettingsSupplier(DisplayManager displayManager) {
+            super(displayManager);
+        }
+
+        @Override
+        protected MagnificationSettingsController createInstance(Display display) {
+            return mMagnificationSettingsController;
+        }
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MotionEventHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/MotionEventHelper.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MotionEventHelper.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/SystemActionsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/SystemActionsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/TestableWindowManager.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/TestableWindowManager.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
new file mode 100644
index 0000000..25696bf
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -0,0 +1,1524 @@
+/*
+ * 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.accessibility;
+
+import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.view.Choreographer.FrameCallback;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.WindowInsets.Type.systemGestures;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasItems;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsSecondArg;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.animation.ValueAnimator;
+import android.annotation.IdRes;
+import android.annotation.Nullable;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.RegionIterator;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+import android.text.TextUtils;
+import android.util.Size;
+import android.view.Display;
+import android.view.IWindowSession;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IRemoteMagnificationAnimationCallback;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.Flags;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.res.R;
+import com.android.systemui.settings.FakeDisplayTracker;
+import com.android.systemui.util.FakeSharedPreferences;
+import com.android.systemui.util.leak.ReferenceTestUtils;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.os.FakeHandler;
+
+import com.google.common.util.concurrent.AtomicDouble;
+
+import kotlin.Lazy;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@LargeTest
[email protected]
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
+public class WindowMagnificationControllerTest extends SysuiTestCase {
+
+    @Rule
+    // NOTE: pass 'null' to allow this test advances time on the main thread.
+    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(null);
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
+    @Mock
+    private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    @Mock
+    private MirrorWindowControl mMirrorWindowControl;
+    @Mock
+    private WindowMagnifierCallback mWindowMagnifierCallback;
+    @Mock
+    IRemoteMagnificationAnimationCallback mAnimationCallback;
+    @Mock
+    IRemoteMagnificationAnimationCallback mAnimationCallback2;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    @Mock
+    private SecureSettings mSecureSettings;
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
+
+    private long mWaitAnimationDuration;
+    private long mWaitBounceEffectDuration;
+
+    private Handler mHandler;
+    private TestableWindowManager mWindowManager;
+    private SysUiState mSysUiState;
+    private Resources mResources;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+    private WindowMagnificationController mWindowMagnificationController;
+    private Instrumentation mInstrumentation;
+    private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
+    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+
+    private IWindowSession mWindowSessionSpy;
+
+    private View mSpyView;
+    private View.OnTouchListener mTouchListener;
+    private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+    private KosmosJavaAdapter mKosmos;
+    private FakeSharedPreferences mSharedPreferences;
+
+    /**
+     *  return whether window magnification is supported for current test context.
+     */
+    private boolean isWindowModeSupported() {
+        return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(mContext);
+        mKosmos = new KosmosJavaAdapter(this);
+        mContext = Mockito.spy(getContext());
+        mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        mWindowManager = spy(new TestableWindowManager(wm));
+
+        mWindowSessionSpy = spy(WindowManagerGlobal.getWindowSession());
+
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        doAnswer(invocation -> {
+            FrameCallback callback = invocation.getArgument(0);
+            callback.doFrame(0);
+            return null;
+        }).when(mSfVsyncFrameProvider).postFrameCallback(
+                any(FrameCallback.class));
+        mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
+        mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
+        when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
+                returnsSecondArg());
+        when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
+                returnsSecondArg());
+
+        mResources = getContext().getOrCreateTestableResources().getResources();
+        // prevent the config orientation from undefined, which may cause config.diff method
+        // neglecting the orientation update.
+        if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) {
+            mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
+        }
+
+        // Using the animation duration in WindowMagnificationAnimationController for testing.
+        mWaitAnimationDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_longAnimTime);
+        // Using the bounce effect duration in WindowMagnificationController for testing.
+        mWaitBounceEffectDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_shortAnimTime);
+
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, mValueAnimator);
+        mSharedPreferences = new FakeSharedPreferences();
+        when(mContext.getSharedPreferences(
+                eq("window_magnification_preferences"), anyInt()))
+                .thenReturn(mSharedPreferences);
+        ViewCaptureAwareWindowManager viewCaptureAwareWindowManager = new
+                ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
+                /* isViewCaptureEnabled= */ false);
+        mWindowMagnificationController =
+                new WindowMagnificationController(
+                        mContext,
+                        mHandler,
+                        mWindowMagnificationAnimationController,
+                        mMirrorWindowControl,
+                        mTransaction,
+                        mWindowMagnifierCallback,
+                        mSysUiState,
+                        mSecureSettings,
+                        /* scvhSupplier= */ () -> null,
+                        mSfVsyncFrameProvider,
+                        /* globalWindowSessionSupplier= */ () -> mWindowSessionSpy,
+                        viewCaptureAwareWindowManager);
+
+        verify(mMirrorWindowControl).setWindowDelegate(
+                any(MirrorWindowControl.MirrorWindowDelegate.class));
+        mSpyView = Mockito.spy(new View(mContext));
+        doAnswer((invocation) -> {
+            mTouchListener = invocation.getArgument(0);
+            return null;
+        }).when(mSpyView).setOnTouchListener(
+                any(View.OnTouchListener.class));
+
+        // skip test if window magnification is not supported to prevent fail results. (b/279820875)
+        Assume.assumeTrue(isWindowModeSupported());
+    }
+
+    @After
+    public void tearDown() {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.deleteWindowMagnification();
+                });
+        mValueAnimator.cancel();
+    }
+
+    @Test
+    public void initWindowMagnificationController_checkAllowDiagonalScrollingWithSecureSettings() {
+        verify(mSecureSettings).getIntForUser(
+                eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
+                /* def */ eq(1), /* userHandle= */ anyInt());
+        assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled());
+    }
+
+    @Test
+    public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        verify(mMirrorWindowControl).showControl();
+        verify(mWindowMagnifierCallback,
+                timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeastOnce()).onWindowMagnifierBoundsChanged(
+                eq(mContext.getDisplayId()), any(Rect.class));
+    }
+
+    @Test
+    public void enableWindowMagnification_notifySourceBoundsChanged() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                        Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
+                        /* magnificationFrameOffsetRatioY= */ 0, null));
+
+        // Waits for the surface created
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
+                (eq(mContext.getDisplayId())), any());
+    }
+
+    @Test
+    public void enableWindowMagnification_disabled_notifySourceBoundsChanged() {
+        enableWindowMagnification_notifySourceBoundsChanged();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification(null));
+        Mockito.reset(mWindowMagnifierCallback);
+
+        enableWindowMagnification_notifySourceBoundsChanged();
+    }
+
+    @Test
+    public void enableWindowMagnification_withAnimation_schedulesFrame() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
+                    10, /* magnificationFrameOffsetRatioX= */ 0,
+                    /* magnificationFrameOffsetRatioY= */ 0,
+                    Mockito.mock(IRemoteMagnificationAnimationCallback.class));
+        });
+        advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
+
+        verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN, 0, 0, null);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifier(10, 10);
+        });
+
+        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
+                (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        assertEquals(mWindowMagnificationController.getCenterX(),
+                sourceBoundsCaptor.getValue().exactCenterX(), 0);
+        assertEquals(mWindowMagnificationController.getCenterY(),
+                sourceBoundsCaptor.getValue().exactCenterY(), 0);
+    }
+
+    @Test
+    public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        // Wait for Rects updated.
+        waitForIdleSync();
+
+        List<Rect> rects = mWindowManager.getAttachedView().getSystemGestureExclusionRects();
+        assertFalse(rects.isEmpty());
+    }
+
+    @Ignore("The default window size should be constrained after fixing b/288056772")
+    @Test
+    public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() {
+        final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
+        mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        final int halfScreenSize = screenSize / 2;
+        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+        // The frame size should be the half of smaller value of window height/width unless it
+        //exceed the max frame size.
+        assertTrue(params.width < halfScreenSize);
+        assertTrue(params.height < halfScreenSize);
+    }
+
+    @Test
+    public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN,
+                        Float.NaN));
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification());
+
+        verify(mMirrorWindowControl).destroyControl();
+        verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
+    }
+
+    @Test
+    public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+        setSystemGestureInsets();
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    bounds.bottom);
+        });
+        ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.deleteWindowMagnification();
+        });
+
+        verify(mMirrorWindowControl).destroyControl();
+        assertFalse(hasMagnificationOverlapFlag());
+    }
+
+    @Test
+    public void deleteWindowMagnification_notifySourceBoundsChanged() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN,
+                        Float.NaN));
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification());
+
+        // The first time is for notifying magnification enabled and the second time is for
+        // notifying magnification disabled.
+        verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged(
+                (eq(mContext.getDisplayId())), any());
+    }
+
+    @Test
+    public void moveMagnifier_schedulesFrame() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
+        });
+
+        verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
+    }
+
+    @Test
+    public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
+            throws RemoteException {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN, 0, 0, null);
+        });
+
+        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
+        final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifierToPosition(
+                    targetCenterX, targetCenterY, mAnimationCallback);
+        });
+        advanceTimeBy(mWaitAnimationDuration);
+
+        verify(mAnimationCallback, times(1)).onResult(eq(true));
+        verify(mAnimationCallback, never()).onResult(eq(false));
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        assertEquals(mWindowMagnificationController.getCenterX(),
+                sourceBoundsCaptor.getValue().exactCenterX(), 0);
+        assertEquals(mWindowMagnificationController.getCenterY(),
+                sourceBoundsCaptor.getValue().exactCenterY(), 0);
+        assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
+        assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
+    }
+
+    @Test
+    public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
+            throws RemoteException {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN, 0, 0, null);
+        });
+
+        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
+        final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifierToPosition(
+                    centerX + 10, centerY + 10, mAnimationCallback);
+            mWindowMagnificationController.moveWindowMagnifierToPosition(
+                    centerX + 20, centerY + 20, mAnimationCallback);
+            mWindowMagnificationController.moveWindowMagnifierToPosition(
+                    centerX + 30, centerY + 30, mAnimationCallback);
+            mWindowMagnificationController.moveWindowMagnifierToPosition(
+                    centerX + 40, centerY + 40, mAnimationCallback2);
+        });
+        advanceTimeBy(mWaitAnimationDuration);
+
+        // only the last one callback will return true
+        verify(mAnimationCallback2).onResult(eq(true));
+        // the others will return false
+        verify(mAnimationCallback, times(3)).onResult(eq(false));
+        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
+                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
+        assertEquals(mWindowMagnificationController.getCenterX(),
+                sourceBoundsCaptor.getValue().exactCenterX(), 0);
+        assertEquals(mWindowMagnificationController.getCenterY(),
+                sourceBoundsCaptor.getValue().exactCenterY(), 0);
+        assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
+        assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
+    }
+
+    @Test
+    public void setScale_enabled_expectedValueAndUpdateStateDescription() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(2.0f,
+                        Float.NaN, Float.NaN));
+
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
+
+        assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+        final View mirrorView = mWindowManager.getAttachedView();
+        assertNotNull(mirrorView);
+        assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
+    }
+
+    @Test
+    public void onConfigurationChanged_disabled_withoutException() {
+        Display display = Mockito.spy(mContext.getDisplay());
+        when(display.getRotation()).thenReturn(Surface.ROTATION_90);
+        when(mContext.getDisplay()).thenReturn(display);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+        });
+    }
+
+    @Test
+    public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
+        final int newRotation = simulateRotateTheDevice();
+        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+        final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
+        final float displayWidth = windowBounds.width();
+        final PointF magnifiedCenter = new PointF(center, center + 5f);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                    magnifiedCenter.x, magnifiedCenter.y);
+            // Get the center again in case the center we set is out of screen.
+            magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
+                    mWindowMagnificationController.getCenterY());
+        });
+        // Rotate the window clockwise 90 degree.
+        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+                windowBounds.right);
+        mWindowManager.setWindowBounds(windowBounds);
+
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
+                ActivityInfo.CONFIG_ORIENTATION));
+
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+        final PointF expectedCenter = new PointF(magnifiedCenter.y,
+                displayWidth - magnifiedCenter.x);
+        final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
+                mWindowMagnificationController.getCenterY());
+        assertEquals(expectedCenter, actualCenter);
+    }
+
+    @Test
+    public void onOrientationChanged_disabled_updateDisplayRotation() {
+        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
+        // Rotate the window clockwise 90 degree.
+        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
+                windowBounds.right);
+        mWindowManager.setWindowBounds(windowBounds);
+        final int newRotation = simulateRotateTheDevice();
+
+        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
+                ActivityInfo.CONFIG_ORIENTATION));
+
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+    }
+
+    @Test
+    public void onScreenSizeAndDensityChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
+        // The default position is at the center of the screen.
+        final float expectedRatio = 0.5f;
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        // Screen size and density change
+        mContext.getResources().getConfiguration().smallestScreenWidthDp =
+                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
+        final Rect testWindowBounds = new Rect(
+                mWindowManager.getCurrentWindowMetrics().getBounds());
+        testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+                testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+        mWindowManager.setWindowBounds(testWindowBounds);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+        });
+
+        // The ratio of center to window size should be the same.
+        assertEquals(expectedRatio,
+                mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
+                0);
+        assertEquals(expectedRatio,
+                mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
+                0);
+    }
+
+    @Test
+    public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
+        int newSmallestScreenWidthDp =
+                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
+        int windowFrameSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
+        mSharedPreferences
+                .edit()
+                .putString(String.valueOf(newSmallestScreenWidthDp),
+                        preferredWindowSize.toString())
+                .commit();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        // Change screen density and size to trigger restoring the preferred window size
+        mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
+        final Rect testWindowBounds = new Rect(
+                mWindowManager.getCurrentWindowMetrics().getBounds());
+        testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
+                testWindowBounds.right + 100, testWindowBounds.bottom + 100);
+        mWindowManager.setWindowBounds(testWindowBounds);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+        });
+
+        // wait for rect update
+        waitForIdleSync();
+        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+        // The width and height of the view include the magnification frame and the margins.
+        assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
+        assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
+    }
+
+    @Test
+    public void screenSizeIsChangedToLarge_enabled_defaultWindowSize() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
+        // Screen size and density change
+        mContext.getResources().getConfiguration().smallestScreenWidthDp =
+                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
+        mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
+        });
+
+        final int defaultWindowSize =
+                mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
+                        WindowMagnificationSettings.MagnificationSize.MEDIUM);
+        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
+
+        assertTrue(params.width == defaultWindowSize);
+        assertTrue(params.height == defaultWindowSize);
+    }
+
+    @Test
+    public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            Mockito.reset(mWindowManager);
+            Mockito.reset(mMirrorWindowControl);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+        });
+
+        verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+        verify(mWindowManager).removeView(any());
+        verify(mMirrorWindowControl).destroyControl();
+        verify(mWindowManager).addView(any(), any());
+        verify(mMirrorWindowControl).showControl();
+    }
+
+    @Test
+    public void onDensityChanged_disabled_updateDimensions() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
+        });
+
+        verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
+    }
+
+    @Test
+    public void initializeA11yNode_enabled_expectedValues() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
+                    Float.NaN);
+        });
+        final View mirrorView = mWindowManager.getAttachedView();
+        assertNotNull(mirrorView);
+        final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
+
+        mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
+
+        assertNotNull(nodeInfo.getContentDescription());
+        assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
+        assertThat(nodeInfo.getActionList(),
+                hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
+                        new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
+                        new AccessibilityAction(R.id.accessibility_action_move_right, null),
+                        new AccessibilityAction(R.id.accessibility_action_move_left, null),
+                        new AccessibilityAction(R.id.accessibility_action_move_down, null),
+                        new AccessibilityAction(R.id.accessibility_action_move_up, null)));
+    }
+
+    @Test
+    public void performA11yActions_visible_expectedResults() {
+        final int displayId = mContext.getDisplayId();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(1.5f, Float.NaN,
+                    Float.NaN);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        assertTrue(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
+        // Minimum scale is 1.0.
+        verify(mWindowMagnifierCallback).onPerformScaleAction(
+                eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
+
+        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
+        verify(mWindowMagnifierCallback).onPerformScaleAction(
+                eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
+
+        // TODO: Verify the final state when the mirror surface is visible.
+        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
+        assertTrue(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
+        assertTrue(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
+        assertTrue(
+                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+        verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
+
+        assertTrue(mirrorView.performAccessibilityAction(
+                AccessibilityAction.ACTION_CLICK.getId(), null));
+        verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
+    }
+
+    @Test
+    public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
+        final int displayId = mContext.getDisplayId();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
+                    Float.NaN);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
+
+        verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
+    }
+
+    @Test
+    public void windowMagnifierEditMode_performA11yClickAction_exitEditMode() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        View closeButton = getInternalView(R.id.close_button);
+        View bottomRightCorner = getInternalView(R.id.bottom_right_corner);
+        View bottomLeftCorner = getInternalView(R.id.bottom_left_corner);
+        View topRightCorner = getInternalView(R.id.top_right_corner);
+        View topLeftCorner = getInternalView(R.id.top_left_corner);
+
+        assertEquals(View.VISIBLE, closeButton.getVisibility());
+        assertEquals(View.VISIBLE, bottomRightCorner.getVisibility());
+        assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility());
+        assertEquals(View.VISIBLE, topRightCorner.getVisibility());
+        assertEquals(View.VISIBLE, topLeftCorner.getVisibility());
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(), null);
+
+        assertEquals(View.GONE, closeButton.getVisibility());
+        assertEquals(View.GONE, bottomRightCorner.getVisibility());
+        assertEquals(View.GONE, bottomLeftCorner.getVisibility());
+        assertEquals(View.GONE, topRightCorner.getVisibility());
+        assertEquals(View.GONE, topLeftCorner.getVisibility());
+    }
+
+    @Test
+    public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() {
+        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        final int startingWidth = (int) (windowBounds.width() * 0.8);
+        final int startingHeight = (int) (windowBounds.height() * 0.8);
+        final float changeWindowSizeAmount = mContext.getResources().getFraction(
+                R.fraction.magnification_resize_window_size_amount,
+                /* base= */ 1,
+                /* pbase= */ 1);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mirrorView.performAccessibilityAction(
+                            R.id.accessibility_action_increase_window_width, null);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+        // Window width includes the magnifier frame and the margin. Increasing the window size
+        // will be increasing the amount of the frame size only.
+        int newWindowWidth =
+                (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+                        + 2 * mirrorSurfaceMargin;
+        assertEquals(newWindowWidth, actualWindowWidth.get());
+        assertEquals(startingHeight, actualWindowHeight.get());
+    }
+
+    @Test
+    public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() {
+        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        final int startingWidth = (int) (windowBounds.width() * 0.8);
+        final int startingHeight = (int) (windowBounds.height() * 0.8);
+        final float changeWindowSizeAmount = mContext.getResources().getFraction(
+                R.fraction.magnification_resize_window_size_amount,
+                /* base= */ 1,
+                /* pbase= */ 1);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mirrorView.performAccessibilityAction(
+                            R.id.accessibility_action_increase_window_height, null);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+        // Window height includes the magnifier frame and the margin. Increasing the window size
+        // will be increasing the amount of the frame size only.
+        int newWindowHeight =
+                (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
+                        + 2 * mirrorSurfaceMargin;
+        assertEquals(startingWidth, actualWindowWidth.get());
+        assertEquals(newWindowHeight, actualWindowHeight.get());
+    }
+
+    @Test
+    public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() {
+        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        final int startingWidth = windowBounds.width();
+        final int startingHeight = windowBounds.height();
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AccessibilityNodeInfo accessibilityNodeInfo =
+                mirrorView.createAccessibilityNodeInfo();
+        assertFalse(accessibilityNodeInfo.getActionList().contains(
+                new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
+    }
+
+    @Test
+    public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() {
+        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        final int startingWidth = windowBounds.width();
+        final int startingHeight = windowBounds.height();
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AccessibilityNodeInfo accessibilityNodeInfo =
+                mirrorView.createAccessibilityNodeInfo();
+        assertFalse(accessibilityNodeInfo.getActionList().contains(
+                new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
+    }
+
+    @Test
+    public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() {
+        int mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int startingSize = (int) (mMinWindowSize * 1.1);
+        final float changeWindowSizeAmount = mContext.getResources().getFraction(
+                R.fraction.magnification_resize_window_size_amount,
+                /* base= */ 1,
+                /* pbase= */ 1);
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mirrorView.performAccessibilityAction(
+                            R.id.accessibility_action_decrease_window_width, null);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+        // Window width includes the magnifier frame and the margin. Decreasing the window size
+        // will be decreasing the amount of the frame size only.
+        int newWindowWidth =
+                (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+                        + 2 * mirrorSurfaceMargin;
+        assertEquals(newWindowWidth, actualWindowWidth.get());
+        assertEquals(startingSize, actualWindowHeight.get());
+    }
+
+    @Test
+    public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() {
+        int mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int startingSize = (int) (mMinWindowSize * 1.1);
+        final float changeWindowSizeAmount = mContext.getResources().getFraction(
+                R.fraction.magnification_resize_window_size_amount,
+                /* base= */ 1,
+                /* pbase= */ 1);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mirrorView.performAccessibilityAction(
+                            R.id.accessibility_action_decrease_window_height, null);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+        // Window height includes the magnifier frame and the margin. Decreasing the window size
+        // will be decreasing the amount of the frame size only.
+        int newWindowHeight =
+                (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
+                        + 2 * mirrorSurfaceMargin;
+        assertEquals(startingSize, actualWindowWidth.get());
+        assertEquals(newWindowHeight, actualWindowHeight.get());
+    }
+
+    @Test
+    public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() {
+        int mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int startingSize = mMinWindowSize;
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AccessibilityNodeInfo accessibilityNodeInfo =
+                mirrorView.createAccessibilityNodeInfo();
+        assertFalse(accessibilityNodeInfo.getActionList().contains(
+                new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
+    }
+
+    @Test
+    public void windowHeightIsMin_noDecreaseWindowHeightA11yAcyion() {
+        int mMinWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int startingSize = mMinWindowSize;
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+            mWindowMagnificationController.setEditMagnifierSizeMode(true);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+        final AccessibilityNodeInfo accessibilityNodeInfo =
+                mirrorView.createAccessibilityNodeInfo();
+        assertFalse(accessibilityNodeInfo.getActionList().contains(
+                new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
+    }
+
+    @Test
+    public void enableWindowMagnification_hasA11yWindowTitle() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        assertEquals(getContext().getResources().getString(
+                com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
+    }
+
+    @Test
+    public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(0.9f, Float.NaN,
+                    Float.NaN);
+        });
+
+        assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
+    }
+
+    @Test
+    public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
+        // the config orientation should not be undefined, since it would cause config.diff
+        // returning 0 and thus the orientation changed would not be detected
+        assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
+
+        final Configuration config = mResources.getConfiguration();
+        config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
+                : ORIENTATION_LANDSCAPE;
+        final int newRotation = simulateRotateTheDevice();
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        assertEquals(newRotation, mWindowMagnificationController.mRotation);
+    }
+
+    @Test
+    public void enableWindowMagnification_registerComponentCallback() {
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN,
+                        Float.NaN));
+
+        verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
+    }
+
+    @Test
+    public void onLocaleChanged_enabled_updateA11yWindowTitle() {
+        final String newA11yWindowTitle = "new a11y window title";
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        final TestableResources testableResources = getContext().getOrCreateTestableResources();
+        testableResources.addOverride(com.android.internal.R.string.android_system_label,
+                newA11yWindowTitle);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+        });
+
+        assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
+    }
+
+    @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
+    @Test
+    public void onSingleTap_enabled_scaleAnimates() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onSingleTap(mSpyView);
+        });
+
+        final View mirrorView = mWindowManager.getAttachedView();
+
+        final AtomicDouble maxScaleX = new AtomicDouble();
+        advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
+            // For some reason the fancy way doesn't compile...
+            // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+            final double oldMax = maxScaleX.get();
+            final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+            assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+        });
+
+        assertTrue(maxScaleX.get() > 1.0);
+    }
+
+    @Test
+    public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        setSystemGestureInsets();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
+        });
+
+        ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
+    }
+
+    @Test
+    public void moveWindowMagnificationToRightEdge_dragHandleMovesToLeftAndUpdatesTapExcludeRegion()
+            throws RemoteException {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        setSystemGestureInsets();
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.updateWindowMagnificationInternal(
+                            Float.NaN, Float.NaN, Float.NaN);
+                });
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
+                });
+
+        // Wait for Region updated.
+        waitForIdleSync();
+
+        final ArgumentCaptor<Region> tapExcludeRegionCapturer =
+                ArgumentCaptor.forClass(Region.class);
+        verify(mWindowSessionSpy, times(2))
+                .updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
+        Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
+        RegionIterator iterator = new RegionIterator(tapExcludeRegion);
+
+        final Rect topRect = new Rect();
+        final Rect bottomRect = new Rect();
+        assertTrue(iterator.next(topRect));
+        assertTrue(iterator.next(bottomRect));
+        assertFalse(iterator.next(new Rect()));
+
+        assertEquals(topRect.right, bottomRect.right);
+        assertNotEquals(topRect.left, bottomRect.left);
+    }
+
+    @Test
+    public void moveWindowMagnificationToLeftEdge_dragHandleMovesToRightAndUpdatesTapExcludeRegion()
+            throws RemoteException {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        setSystemGestureInsets();
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.updateWindowMagnificationInternal(
+                            Float.NaN, Float.NaN, Float.NaN);
+                });
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
+                });
+
+        // Wait for Region updated.
+        waitForIdleSync();
+
+        final ArgumentCaptor<Region> tapExcludeRegionCapturer =
+                ArgumentCaptor.forClass(Region.class);
+        verify(mWindowSessionSpy).updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
+        Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
+        RegionIterator iterator = new RegionIterator(tapExcludeRegion);
+
+        final Rect topRect = new Rect();
+        final Rect bottomRect = new Rect();
+        assertTrue(iterator.next(topRect));
+        assertTrue(iterator.next(bottomRect));
+        assertFalse(iterator.next(new Rect()));
+
+        assertEquals(topRect.left, bottomRect.left);
+        assertNotEquals(topRect.right, bottomRect.right);
+    }
+
+    @Test
+    public void setMinimumWindowSize_enabled_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final int  expectedWindowHeight = minimumWindowSize;
+        final int  expectedWindowWidth = minimumWindowSize;
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
+            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                    Float.NaN, Float.NaN);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
+                    minimumWindowSize - 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(minimumWindowSize, actualWindowHeight.get());
+        assertEquals(minimumWindowSize, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
+            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+        });
+
+        assertEquals(bounds.height(), actualWindowHeight.get());
+        assertEquals(bounds.width(), actualWindowWidth.get());
+    }
+
+    @Test
+    public void changeMagnificationSize_expectedWindowSize() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+
+        final float magnificationScaleLarge = 2.5f;
+        final int initSize = Math.min(bounds.width(), bounds.height()) / 3;
+        final int magnificationSize = (int) (initSize * magnificationScaleLarge)
+                - (int) (initSize * magnificationScaleLarge) % 2;
+
+        final int expectedWindowHeight = magnificationSize;
+        final int expectedWindowWidth = magnificationSize;
+
+        mInstrumentation.runOnMainSync(
+                () ->
+                        mWindowMagnificationController.updateWindowMagnificationInternal(
+                                Float.NaN, Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.changeMagnificationSize(
+                            WindowMagnificationSettings.MagnificationSize.LARGE);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        assertEquals(expectedWindowHeight, actualWindowHeight.get());
+        assertEquals(expectedWindowWidth, actualWindowWidth.get());
+    }
+
+    @Test
+    public void editModeOnDragCorner_resizesWindow() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+
+        final int startingSize = (int) (bounds.width() / 2);
+
+        mInstrumentation.runOnMainSync(
+                () ->
+                        mWindowMagnificationController.updateWindowMagnificationInternal(
+                                Float.NaN, Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+                    mWindowMagnificationController.setEditMagnifierSizeMode(true);
+                });
+
+        waitForIdleSync();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController
+                            .onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+
+        assertEquals(startingSize + 1, actualWindowHeight.get());
+        assertEquals(startingSize + 2, actualWindowWidth.get());
+    }
+
+    @Test
+    public void editModeOnDragEdge_resizesWindowInOnlyOneDirection() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+
+        final int startingSize = (int) (bounds.width() / 2f);
+
+        mInstrumentation.runOnMainSync(
+                () ->
+                        mWindowMagnificationController.updateWindowMagnificationInternal(
+                                Float.NaN, Float.NaN, Float.NaN));
+
+        final AtomicInteger actualWindowHeight = new AtomicInteger();
+        final AtomicInteger actualWindowWidth = new AtomicInteger();
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.setWindowSize(startingSize, startingSize);
+                    mWindowMagnificationController.setEditMagnifierSizeMode(true);
+                    mWindowMagnificationController
+                            .onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
+                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
+                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
+                });
+        assertEquals(startingSize + 1, actualWindowHeight.get());
+        assertEquals(startingSize, actualWindowWidth.get());
+    }
+
+    @Test
+    public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
+
+        final int minimumWindowSize = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
+                        Float.NaN, Float.NaN));
+
+        final AtomicInteger magnificationCenterX = new AtomicInteger();
+        final AtomicInteger magnificationCenterY = new AtomicInteger();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
+                    minimumWindowSize, bounds.right, bounds.bottom);
+            magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
+            magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
+        });
+
+        assertTrue(magnificationCenterX.get() < bounds.right);
+        assertTrue(magnificationCenterY.get() < bounds.bottom);
+    }
+
+    @Test
+    public void performSingleTap_DragHandle() {
+        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    mWindowMagnificationController.updateWindowMagnificationInternal(
+                            1.5f, bounds.centerX(), bounds.centerY());
+                });
+        View dragButton = getInternalView(R.id.drag_handle);
+
+        // Perform a single-tap
+        final long downTime = SystemClock.uptimeMillis();
+        dragButton.dispatchTouchEvent(
+                obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100));
+        dragButton.dispatchTouchEvent(
+                obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
+
+        verify(mWindowManager).addView(any(View.class), any());
+    }
+
+    private <T extends View> T getInternalView(@IdRes int idRes) {
+        View mirrorView = mWindowManager.getAttachedView();
+        T view = mirrorView.findViewById(idRes);
+        assertNotNull(view);
+        return view;
+    }
+
+    private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
+            float y) {
+        return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
+    }
+
+    private CharSequence getAccessibilityWindowTitle() {
+        final View mirrorView = mWindowManager.getAttachedView();
+        if (mirrorView == null) {
+            return null;
+        }
+        WindowManager.LayoutParams layoutParams =
+                (WindowManager.LayoutParams) mirrorView.getLayoutParams();
+        return layoutParams.accessibilityTitle;
+    }
+
+    private boolean hasMagnificationOverlapFlag() {
+        return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
+    }
+
+    private void setSystemGestureInsets() {
+        final WindowInsets testInsets = new WindowInsets.Builder()
+                .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
+                .build();
+        mWindowManager.setWindowInsets(testInsets);
+    }
+
+    private int updateMirrorSurfaceMarginDimension() {
+        return mContext.getResources().getDimensionPixelSize(
+                R.dimen.magnification_mirror_surface_margin);
+    }
+
+    @Surface.Rotation
+    private int simulateRotateTheDevice() {
+        final Display display = Mockito.spy(mContext.getDisplay());
+        final int currentRotation = display.getRotation();
+        final int newRotation = (currentRotation + 1) % 4;
+        when(display.getRotation()).thenReturn(newRotation);
+        when(mContext.getDisplay()).thenReturn(display);
+        return newRotation;
+    }
+
+    // advance time based on the device frame refresh rate
+    private void advanceTimeBy(long timeDelta) {
+        advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
+    }
+
+    // advance time based on the device frame refresh rate, and trigger runnable on each refresh
+    private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
+        final float frameRate = mContext.getDisplay().getRefreshRate();
+        final int timeSlot = (int) (1000 / frameRate);
+        int round = (int) Math.ceil((double) timeDelta / timeSlot);
+        for (; round >= 0; round--) {
+            mInstrumentation.runOnMainSync(() -> {
+                mAnimatorTestRule.advanceTimeBy(timeSlot);
+                if (runnableOnEachRefresh != null) {
+                    runnableOnEachRefresh.run();
+                }
+            });
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
index 460461a..176c3ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplTest.kt
@@ -22,10 +22,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Rule
@@ -38,13 +39,15 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AccessibilityQsShortcutsRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val secureSettings = kosmos.fakeSettings
+
     @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     // mocks
     @Mock private lateinit var a11yManager: AccessibilityManager
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-    private val secureSettings = FakeSettings()
 
     private val userA11yQsShortcutsRepositoryFactory =
         object : UserA11yQsShortcutsRepository.Factory {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/AccessibilityRepositoryTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt
new file mode 100644
index 0000000..dd85d9b
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryTest.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.accessibility.data.repository
+
+import android.view.accessibility.CaptioningManager
+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 kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+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.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@Suppress("UnspecifiedRegisterReceiverFlag")
+@RunWith(AndroidJUnit4::class)
+class CaptioningRepositoryTest : SysuiTestCase() {
+
+    @Captor
+    private lateinit var listenerCaptor: ArgumentCaptor<CaptioningManager.CaptioningChangeListener>
+
+    @Mock private lateinit var captioningManager: CaptioningManager
+
+    private lateinit var underTest: CaptioningRepository
+
+    private val testScope = TestScope()
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        underTest =
+            CaptioningRepositoryImpl(
+                captioningManager,
+                testScope.testScheduler,
+                testScope.backgroundScope
+            )
+    }
+
+    @Test
+    fun isSystemAudioCaptioningEnabled_change_repositoryEmits() {
+        testScope.runTest {
+            `when`(captioningManager.isEnabled).thenReturn(false)
+            val isSystemAudioCaptioningEnabled = mutableListOf<Boolean>()
+            underTest.isSystemAudioCaptioningEnabled
+                .onEach { isSystemAudioCaptioningEnabled.add(it) }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            triggerOnSystemAudioCaptioningChange()
+            runCurrent()
+
+            assertThat(isSystemAudioCaptioningEnabled)
+                .containsExactlyElementsIn(listOf(false, true))
+                .inOrder()
+        }
+    }
+
+    @Test
+    fun isSystemAudioCaptioningUiEnabled_change_repositoryEmits() {
+        testScope.runTest {
+            `when`(captioningManager.isSystemAudioCaptioningUiEnabled).thenReturn(false)
+            val isSystemAudioCaptioningUiEnabled = mutableListOf<Boolean>()
+            underTest.isSystemAudioCaptioningUiEnabled
+                .onEach { isSystemAudioCaptioningUiEnabled.add(it) }
+                .launchIn(backgroundScope)
+            runCurrent()
+
+            triggerSystemAudioCaptioningUiChange()
+            runCurrent()
+
+            assertThat(isSystemAudioCaptioningUiEnabled)
+                .containsExactlyElementsIn(listOf(false, true))
+                .inOrder()
+        }
+    }
+
+    private fun triggerSystemAudioCaptioningUiChange(enabled: Boolean = true) {
+        verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
+        listenerCaptor.value.onSystemAudioCaptioningUiChanged(enabled)
+    }
+
+    private fun triggerOnSystemAudioCaptioningChange(enabled: Boolean = true) {
+        verify(captioningManager).addCaptioningChangeListener(listenerCaptor.capture())
+        listenerCaptor.value.onSystemAudioCaptioningChanged(enabled)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
index 4e1f82c..801d359 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorCorrectionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -41,9 +42,10 @@
 
     private val testUser1 = UserHandle.of(1)!!
     private val testUser2 = UserHandle.of(2)!!
-    private val testDispatcher = StandardTestDispatcher()
-    private val scope = TestScope(testDispatcher)
-    private val settings: FakeSettings = FakeSettings()
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val scope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
 
     private lateinit var underTest: ColorCorrectionRepository
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
index b99dec4..2f457be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt
@@ -23,11 +23,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -41,9 +42,10 @@
 
     private val testUser1 = UserHandle.of(1)!!
     private val testUser2 = UserHandle.of(2)!!
-    private val testDispatcher = StandardTestDispatcher()
-    private val scope = TestScope(testDispatcher)
-    private val settings: FakeSettings = FakeSettings()
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val scope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
 
     private lateinit var underTest: ColorInversionRepository
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
index 5757f67..54dbed8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
@@ -26,7 +26,9 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.user.utils.UserScopedService
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
@@ -38,8 +40,6 @@
 import com.google.common.truth.Truth.assertThat
 import java.time.LocalTime
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -51,7 +51,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class NightDisplayRepositoryTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
+    private val kosmos = testKosmos()
     private val testUser = UserHandle.of(1)!!
     private val testStartTime = LocalTime.MIDNIGHT
     private val testEndTime = LocalTime.NOON
@@ -71,8 +71,8 @@
         }
     private val globalSettings = kosmos.fakeGlobalSettings
     private val secureSettings = kosmos.fakeSettings
-    private val testDispatcher = StandardTestDispatcher()
-    private val scope = TestScope(testDispatcher)
+    private val testDispatcher = kosmos.testDispatcher
+    private val scope = kosmos.testScope
     private val userScopedColorDisplayManager =
         mock<UserScopedService<ColorDisplayManager>> {
             whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
index 1378dac..729d356 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/OneHandedModeRepositoryImplTest.kt
@@ -22,11 +22,12 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -39,9 +40,10 @@
 
     private val testUser1 = UserHandle.of(1)!!
     private val testUser2 = UserHandle.of(2)!!
-    private val testDispatcher = StandardTestDispatcher()
-    private val scope = TestScope(testDispatcher)
-    private val settings: FakeSettings = FakeSettings()
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val scope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
 
     private val underTest: OneHandedModeRepository =
         OneHandedModeRepositoryImpl(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
index ce22e28..62f13f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/UserA11yQsShortcutsRepositoryTest.kt
@@ -21,10 +21,11 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -32,9 +33,10 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class UserA11yQsShortcutsRepositoryTest : SysuiTestCase() {
-    private val secureSettings = FakeSettings()
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val secureSettings = kosmos.fakeSettings
 
     private val underTest =
         UserA11yQsShortcutsRepository(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/extradim/ExtraDimDialogReceiverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
new file mode 100644
index 0000000..80de087
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+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.utils.TestUtils;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
+
+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;
+
+/** Tests for {@link DragToInteractAnimationController}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
[email protected]
+public class DragToInteractAnimationControllerTest extends SysuiTestCase {
+    private DragToInteractAnimationController mDragToInteractAnimationController;
+    private DragToInteractView mInteractView;
+    private DismissView mDismissView;
+
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
+    @Before
+    public void setUp() throws Exception {
+        final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
+        final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                mockSecureSettings);
+        final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
+                stubWindowManager);
+        final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
+                stubMenuViewAppearance, mockSecureSettings));
+        mInteractView = spy(new DragToInteractView(mContext));
+        mDismissView = spy(new DismissView(mContext));
+
+        if (Flags.floatingMenuDragToEdit()) {
+            mDragToInteractAnimationController = new DragToInteractAnimationController(
+                    mInteractView, stubMenuView);
+        } else {
+            mDragToInteractAnimationController = new DragToInteractAnimationController(
+                    mDismissView, stubMenuView);
+        }
+
+        mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
+            @Override
+            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject) {
+
+            }
+
+            @Override
+            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject, float velX, float velY,
+                    boolean wasFlungOut) {
+
+            }
+
+            @Override
+            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    @NonNull MagnetizedObject<?> draggedObject) {
+
+            }
+        });
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void showDismissView_success_old() {
+        mDragToInteractAnimationController.showInteractView(true);
+
+        verify(mDismissView).show();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void hideDismissView_success_old() {
+        mDragToInteractAnimationController.showInteractView(false);
+
+        verify(mDismissView).hide();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void showDismissView_success() {
+        mDragToInteractAnimationController.showInteractView(true);
+
+        verify(mInteractView).show();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void hideDismissView_success() {
+        mDragToInteractAnimationController.showInteractView(false);
+
+        verify(mInteractView).hide();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuEduTooltipViewTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
new file mode 100644
index 0000000..46f076a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static android.R.id.empty;
+import static android.view.View.OVER_SCROLL_NEVER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.systemui.Flags;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+import com.android.systemui.accessibility.utils.TestUtils;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.wm.shell.shared.bubbles.DismissView;
+
+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.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Tests for {@link MenuListViewTouchHandler}. */
+@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
+@SmallTest
+public class MenuListViewTouchHandlerTest extends SysuiTestCase {
+    private final List<AccessibilityTarget> mStubTargets = new ArrayList<>(
+            Collections.singletonList(mock(AccessibilityTarget.class)));
+    private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+    private MenuView mStubMenuView;
+    private MenuListViewTouchHandler mTouchHandler;
+    private MenuAnimationController mMenuAnimationController;
+    private DragToInteractAnimationController mDragToInteractAnimationController;
+    private RecyclerView mStubListView;
+    private DismissView mDismissView;
+    private DragToInteractView mInteractView;
+
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
+    @Before
+    public void setUp() throws Exception {
+        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        final SecureSettings secureSettings = TestUtils.mockSecureSettings();
+        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
+                secureSettings);
+        final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
+                windowManager);
+        mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
+                secureSettings);
+        mStubMenuView.setTranslationX(0);
+        mStubMenuView.setTranslationY(0);
+        mMenuAnimationController = spy(new MenuAnimationController(
+                mStubMenuView, stubMenuViewAppearance));
+        mInteractView = spy(new DragToInteractView(mContext));
+        mDismissView = spy(new DismissView(mContext));
+
+        if (Flags.floatingMenuDragToEdit()) {
+            mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
+                    mInteractView, mStubMenuView));
+        } else {
+            mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
+                    mDismissView, mStubMenuView));
+        }
+
+        mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
+                mDragToInteractAnimationController);
+        final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
+        mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
+        mStubListView.setAdapter(stubAdapter);
+    }
+
+    @Test
+    public void onActionDownEvent_shouldCancelAnimations() {
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+
+        verify(mMenuAnimationController).cancelAnimations();
+    }
+
+    @Test
+    public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
+        doReturn(empty).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
+                any(MotionEvent.class));
+        final int offset = 100;
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        final MotionEvent stubMoveEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+        mStubListView.setOverScrollMode(OVER_SCROLL_NEVER);
+
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+
+        assertThat(mStubMenuView.getTranslationX()).isEqualTo(offset);
+        assertThat(mStubMenuView.getTranslationY()).isEqualTo(offset);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void onActionMoveEvent_shouldShowDismissView() {
+        final int offset = 100;
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        final MotionEvent stubMoveEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+
+        verify(mDismissView).show();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
+    public void onActionMoveEvent_shouldShowInteractView() {
+        final int offset = 100;
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        final MotionEvent stubMoveEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+
+        verify(mInteractView).show();
+    }
+
+    @Test
+    public void dragAndDrop_shouldFlingMenuThenSpringToEdge() {
+        final int offset = 100;
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        final MotionEvent stubMoveEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+        final MotionEvent stubUpEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 5,
+                        MotionEvent.ACTION_UP, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
+
+        verify(mMenuAnimationController).flingMenuThenSpringToEdge(anyFloat(), anyFloat(),
+                anyFloat());
+    }
+
+    @Test
+    public void dragMenuOutOfBoundsAndDrop_moveToLeftEdge_shouldMoveToEdgeAndHide() {
+        final int offset = -100;
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        final MotionEvent stubMoveEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
+                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+        final MotionEvent stubUpEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 5,
+                        MotionEvent.ACTION_UP, mStubMenuView.getTranslationX() + offset,
+                        mStubMenuView.getTranslationY() + offset);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
+
+        verify(mMenuAnimationController).moveToEdgeAndHide();
+    }
+
+    @Test
+    public void receiveActionDownMotionEvent_verifyOnActionDownEnd() {
+        final Runnable onActionDownEnd = mock(Runnable.class);
+        mTouchHandler.setOnActionDownEndListener(onActionDownEnd);
+
+        final MotionEvent stubDownEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
+                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
+                        mStubMenuView.getTranslationY());
+        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
+
+        verify(onActionDownEnd).run();
+    }
+
+    @After
+    public void tearDown() {
+        mMotionEventHelper.recycleEvents();
+        mMenuAnimationController.mPositionAnimations.values().forEach(DynamicAnimation::cancel);
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
new file mode 100644
index 0000000..fcdeff9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.systemBars;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.util.settings.SecureSettings;
+
+import kotlin.Lazy;
+
+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;
+
+/** Tests for {@link MenuViewLayerController}. */
+@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
+@SmallTest
+public class MenuViewLayerControllerTest extends SysuiTestCase {
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private WindowManager mWindowManager;
+
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+
+    @Mock
+    private SecureSettings mSecureSettings;
+
+    @Mock
+    private WindowMetrics mWindowMetrics;
+
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
+
+    private MenuViewLayerController mMenuViewLayerController;
+
+    @Before
+    public void setUp() throws Exception {
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        final ViewCaptureAwareWindowManager viewCaptureAwareWm = new ViewCaptureAwareWindowManager(
+                mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false);
+        doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+                mWindowManager).getMaximumWindowMetrics();
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+        when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
+        when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
+        mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
+                viewCaptureAwareWm, mAccessibilityManager, mSecureSettings,
+                mock(NavigationModeController.class));
+    }
+
+    @Test
+    public void show_shouldAddViewToWindow() {
+        mMenuViewLayerController.show();
+
+        verify(mWindowManager).addView(any(View.class), any(ViewGroup.LayoutParams.class));
+    }
+
+    @Test
+    public void hide_menuIsShowing_removeViewFromWindow() {
+        mMenuViewLayerController.show();
+
+        mMenuViewLayerController.hide();
+
+        verify(mWindowManager).removeView(any(View.class));
+    }
+
+    private WindowInsets stubDisplayInsets() {
+        final int stubStatusBarHeight = 118;
+        final int stubNavigationBarHeight = 125;
+        return new WindowInsets.Builder()
+                .setVisible(systemBars() | displayCutout(), true)
+                .setInsets(systemBars() | displayCutout(),
+                        Insets.of(0, stubStatusBarHeight, 0, stubNavigationBarHeight))
+                .build();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/PositionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesCheckerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesListAdapterTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
new file mode 100644
index 0000000..2ac5d10
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsControllerTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.accessibility.hearingaid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptyList;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.HapClientProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.SysuiTestCase;
+
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/** Tests for {@link HearingDevicesPresetsController}. */
+@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesPresetsControllerTest extends SysuiTestCase {
+
+    private static final int TEST_PRESET_INDEX = 1;
+    private static final String TEST_PRESET_NAME = "test_preset";
+    private static final int TEST_HAP_GROUP_ID = 1;
+    private static final int TEST_REASON = 1024;
+
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private HapClientProfile mHapClientProfile;
+    @Mock
+    private CachedBluetoothDevice mCachedBluetoothDevice;
+    @Mock
+    private CachedBluetoothDevice mSubCachedBluetoothDevice;
+    @Mock
+    private BluetoothDevice mBluetoothDevice;
+    @Mock
+    private BluetoothDevice mSubBluetoothDevice;
+
+    @Mock
+    private HearingDevicesPresetsController.PresetCallback mCallback;
+
+    private HearingDevicesPresetsController mController;
+
+    @Before
+    public void setUp() {
+        when(mProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+        when(mHapClientProfile.isProfileReady()).thenReturn(true);
+        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+        when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
+        when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
+
+        mController = new HearingDevicesPresetsController(mProfileManager, mCallback);
+    }
+
+    @Test
+    public void onServiceConnected_callExpectedCallback() {
+        mController.onServiceConnected();
+
+        verify(mHapClientProfile).registerCallback(any(Executor.class),
+                any(BluetoothHapClient.Callback.class));
+        verify(mCallback).onPresetInfoUpdated(anyList(), anyInt());
+    }
+
+    @Test
+    public void getAllPresetInfo_setInvalidHearingDevice_getEmpty() {
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(emptyList());
+        mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+        BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+        when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+                List.of(hapPresetInfo));
+
+        assertThat(mController.getAllPresetInfo()).isEmpty();
+    }
+
+    @Test
+    public void getAllPresetInfo_containsNotAvailablePresetInfo_getEmpty() {
+        setValidHearingDeviceSupportHap();
+        BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(false);
+        when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+                List.of(hapPresetInfo));
+
+        assertThat(mController.getAllPresetInfo()).isEmpty();
+    }
+
+    @Test
+    public void getAllPresetInfo_containsOnePresetInfo_getOnePresetInfo() {
+        setValidHearingDeviceSupportHap();
+        BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+        when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+                List.of(hapPresetInfo));
+
+        assertThat(mController.getAllPresetInfo()).contains(hapPresetInfo);
+    }
+
+    @Test
+    public void getActivePresetIndex_getExpectedIndex() {
+        setValidHearingDeviceSupportHap();
+        when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+                TEST_PRESET_INDEX);
+
+        assertThat(mController.getActivePresetIndex()).isEqualTo(TEST_PRESET_INDEX);
+    }
+
+    @Test
+    public void onPresetSelected_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+        setValidHearingDeviceSupportHap();
+        BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+        when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+                List.of(hapPresetInfo));
+        when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+                TEST_PRESET_INDEX);
+
+        mController.onPresetSelected(mBluetoothDevice, TEST_PRESET_INDEX, TEST_REASON);
+
+        verify(mCallback).onPresetInfoUpdated(eq(List.of(hapPresetInfo)), eq(TEST_PRESET_INDEX));
+    }
+
+    @Test
+    public void onPresetInfoChanged_presetIndex_callOnPresetInfoUpdatedWithExpectedPresetIndex() {
+        setValidHearingDeviceSupportHap();
+        BluetoothHapPresetInfo hapPresetInfo = getHapPresetInfo(true);
+        when(mHapClientProfile.getAllPresetInfo(mBluetoothDevice)).thenReturn(
+                List.of(hapPresetInfo));
+        when(mHapClientProfile.getActivePresetIndex(mBluetoothDevice)).thenReturn(
+                TEST_PRESET_INDEX);
+
+        mController.onPresetInfoChanged(mBluetoothDevice, List.of(hapPresetInfo), TEST_REASON);
+
+        verify(mCallback).onPresetInfoUpdated(List.of(hapPresetInfo), TEST_PRESET_INDEX);
+    }
+
+    @Test
+    public void onPresetSelectionFailed_callOnPresetCommandFailed() {
+        setValidHearingDeviceSupportHap();
+
+        mController.onPresetSelectionFailed(mBluetoothDevice, TEST_REASON);
+
+        verify(mCallback).onPresetCommandFailed(TEST_REASON);
+    }
+
+    @Test
+    public void onSetPresetNameFailed_callOnPresetCommandFailed() {
+        setValidHearingDeviceSupportHap();
+
+        mController.onSetPresetNameFailed(mBluetoothDevice, TEST_REASON);
+
+        verify(mCallback).onPresetCommandFailed(TEST_REASON);
+    }
+
+    @Test
+    public void onPresetSelectionForGroupFailed_callSelectPresetIndividual() {
+        setValidHearingDeviceSupportHap();
+        mController.selectPreset(TEST_PRESET_INDEX);
+        Mockito.reset(mHapClientProfile);
+        when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+        mController.onPresetSelectionForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+
+        verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+        verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+    }
+
+    @Test
+    public void onSetPresetNameForGroupFailed_callOnPresetCommandFailed() {
+        setValidHearingDeviceSupportHap();
+
+        mController.onSetPresetNameForGroupFailed(TEST_HAP_GROUP_ID, TEST_REASON);
+
+        verify(mCallback).onPresetCommandFailed(TEST_REASON);
+    }
+
+    @Test
+    public void registerHapCallback_callHapRegisterCallback() {
+        mController.registerHapCallback();
+
+        verify(mHapClientProfile).registerCallback(any(Executor.class),
+                any(BluetoothHapClient.Callback.class));
+    }
+
+    @Test
+    public void unregisterHapCallback_callHapUnregisterCallback() {
+        mController.unregisterHapCallback();
+
+        verify(mHapClientProfile).unregisterCallback(any(BluetoothHapClient.Callback.class));
+    }
+
+    @Test
+    public void selectPreset_supportSynchronized_validGroupId_callSelectPresetForGroup() {
+        setValidHearingDeviceSupportHap();
+        when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+        when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+        mController.selectPreset(TEST_PRESET_INDEX);
+
+        verify(mHapClientProfile).selectPresetForGroup(TEST_HAP_GROUP_ID, TEST_PRESET_INDEX);
+    }
+
+    @Test
+    public void selectPreset_supportSynchronized_invalidGroupId_callSelectPresetIndividual() {
+        setValidHearingDeviceSupportHap();
+        when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(true);
+        when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(
+                BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+
+        mController.selectPreset(TEST_PRESET_INDEX);
+
+        verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+        verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+    }
+
+    @Test
+    public void selectPreset_notSupportSynchronized_validGroupId_callSelectPresetIndividual() {
+        setValidHearingDeviceSupportHap();
+        when(mHapClientProfile.supportsSynchronizedPresets(mBluetoothDevice)).thenReturn(false);
+        when(mHapClientProfile.getHapGroup(mBluetoothDevice)).thenReturn(TEST_HAP_GROUP_ID);
+
+        mController.selectPreset(TEST_PRESET_INDEX);
+
+        verify(mHapClientProfile).selectPreset(mBluetoothDevice, TEST_PRESET_INDEX);
+        verify(mHapClientProfile).selectPreset(mSubBluetoothDevice, TEST_PRESET_INDEX);
+    }
+
+    private BluetoothHapPresetInfo getHapPresetInfo(boolean available) {
+        BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
+        when(info.getName()).thenReturn(TEST_PRESET_NAME);
+        when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
+        when(info.isAvailable()).thenReturn(available);
+        return info;
+    }
+
+    private void setValidHearingDeviceSupportHap() {
+        LocalBluetoothProfile hapClientProfile = mock(HapClientProfile.class);
+        List<LocalBluetoothProfile> profiles = List.of(hapClientProfile);
+        when(mCachedBluetoothDevice.getProfiles()).thenReturn(profiles);
+
+        mController.setHearingDeviceIfSupportHap(mCachedBluetoothDevice);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/utils/TestUtils.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/utils/TestUtils.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
index 201ed00..43db5a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java
@@ -148,6 +148,7 @@
                 mKosmos.getWifiInteractor(),
                 mKosmos.getCommunalSceneInteractor(),
                 mLogBuffer);
+        mController.onInit();
     }
 
     @Test
@@ -517,6 +518,15 @@
         verify(mDreamOverlayStateController).setDreamOverlayStatusBarVisible(false);
     }
 
+    @Test
+    public void testStatusBarWindowStateControllerListenerLifecycle() {
+        ArgumentCaptor<StatusBarWindowStateListener> listenerCaptor =
+                ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
+        verify(mStatusBarWindowStateController).addListener(listenerCaptor.capture());
+        mController.destroy();
+        verify(mStatusBarWindowStateController).removeListener(eq(listenerCaptor.getValue()));
+    }
+
     private StatusBarWindowStateListener updateStatusBarWindowState(boolean show) {
         when(mStatusBarWindowStateController.windowIsShowing()).thenReturn(show);
         final ArgumentCaptor<StatusBarWindowStateListener>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
index d244482..58c3fec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -35,15 +37,14 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.ambient.touch.scrim.ScrimController;
 import com.android.systemui.ambient.touch.scrim.ScrimManager;
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeUserTracker;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -57,7 +58,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Collections;
 import java.util.Optional;
 
 @SmallTest
@@ -106,15 +106,13 @@
     UiEventLogger mUiEventLogger;
 
     @Mock
-    LockPatternUtils mLockPatternUtils;
-
-    @Mock
     ActivityStarter mActivityStarter;
 
     @Mock
     CommunalViewModel mCommunalViewModel;
 
-    FakeUserTracker mUserTracker;
+    @Mock
+    KeyguardInteractor mKeyguardInteractor;
 
     private static final float TOUCH_REGION = .3f;
     private static final float MIN_BOUNCER_HEIGHT = .05f;
@@ -130,7 +128,6 @@
     public void setup() {
         mKosmos = new KosmosJavaAdapter(this);
         MockitoAnnotations.initMocks(this);
-        mUserTracker = new FakeUserTracker();
         mTouchHandler = new BouncerSwipeTouchHandler(
                 mKosmos.getTestScope(),
                 mScrimManager,
@@ -138,24 +135,21 @@
                 mNotificationShadeWindowController,
                 mValueAnimatorCreator,
                 mVelocityTrackerFactory,
-                mLockPatternUtils,
-                mUserTracker,
                 mCommunalViewModel,
                 mFlingAnimationUtils,
                 mFlingAnimationUtilsClosing,
                 TOUCH_REGION,
                 MIN_BOUNCER_HEIGHT,
                 mUiEventLogger,
-                mActivityStarter);
+                mActivityStarter,
+                mKeyguardInteractor);
 
         when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
         when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
         when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
         when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
         when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
-        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true);
-
-        mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0);
+        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
     }
 
     /**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index b85e32b..9568167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -44,16 +46,15 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.ambient.touch.scrim.ScrimController;
 import com.android.systemui.ambient.touch.scrim.ScrimManager;
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeUserTracker;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -69,7 +70,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Collections;
 import java.util.Optional;
 
 @SmallTest
@@ -116,9 +116,6 @@
     UiEventLogger mUiEventLogger;
 
     @Mock
-    LockPatternUtils mLockPatternUtils;
-
-    @Mock
     ActivityStarter mActivityStarter;
 
     @Mock
@@ -127,11 +124,12 @@
     @Mock
     CommunalViewModel mCommunalViewModel;
 
+    @Mock
+    KeyguardInteractor mKeyguardInteractor;
+
     @Captor
     ArgumentCaptor<Rect> mRectCaptor;
 
-    FakeUserTracker mUserTracker;
-
     private static final float TOUCH_REGION = .3f;
     private static final int SCREEN_WIDTH_PX = 1024;
     private static final int SCREEN_HEIGHT_PX = 100;
@@ -148,7 +146,6 @@
     public void setup() {
         mKosmos = new KosmosJavaAdapter(this);
         MockitoAnnotations.initMocks(this);
-        mUserTracker = new FakeUserTracker();
         mTouchHandler = new BouncerSwipeTouchHandler(
                 mKosmos.getTestScope(),
                 mScrimManager,
@@ -156,24 +153,21 @@
                 mNotificationShadeWindowController,
                 mValueAnimatorCreator,
                 mVelocityTrackerFactory,
-                mLockPatternUtils,
-                mUserTracker,
                 mCommunalViewModel,
                 mFlingAnimationUtils,
                 mFlingAnimationUtilsClosing,
                 TOUCH_REGION,
                 MIN_BOUNCER_HEIGHT,
                 mUiEventLogger,
-                mActivityStarter);
+                mActivityStarter,
+                mKeyguardInteractor);
 
         when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
         when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
         when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
         when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE);
         when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS);
-        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true);
-
-        mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0);
+        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(false));
     }
 
     /**
@@ -391,7 +385,7 @@
      */
     @Test
     public void testSwipeUp_keyguardNotSecure_doesNotExpand() {
-        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true));
         mTouchHandler.onSessionStart(mTouchSession);
         ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
                 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
@@ -426,7 +420,7 @@
      */
     @Test
     public void testSwipeDown_keyguardNotSecure_doesNotExpand() {
-        when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false);
+        when(mKeyguardInteractor.isKeyguardDismissible()).thenReturn(MutableStateFlow(true));
         mTouchHandler.onSessionStart(mTouchSession);
         ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
                 ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/InputSessionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/ambient/touch/InputSessionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/InputSessionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/BackAnimationSpecTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/BackTransformationTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/back/BackTransformationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/BackTransformationTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/animation/back/OnBackAnimationCallbackExtensionTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/assist/ui/DisplayUtilsTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
index 1cd9d76..72163e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryTest.kt
@@ -31,9 +31,7 @@
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.whenever
@@ -58,14 +56,13 @@
 
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>
-    @Mock private lateinit var tableLogger: TableLogBuffer
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val clock = FakeSystemClock()
     private val userRepository = FakeUserRepository()
-    private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+    private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
 
     private lateinit var underTest: AuthenticationRepository
 
@@ -78,8 +75,6 @@
         userRepository.setUserInfos(USER_INFOS)
         runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
         whenever(getSecurityMode.apply(anyInt())).thenAnswer { currentSecurityMode }
-        mobileConnectionsRepository =
-            FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
 
         underTest =
             AuthenticationRepositoryImpl(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
index 0c5e726..080b48a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.authentication.domain.interactor
 
 import android.app.admin.DevicePolicyManager
-import android.app.admin.flags.Flags as DevicePolicyFlags
-import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
@@ -414,7 +412,6 @@
         }
 
     @Test
-    @EnableFlags(DevicePolicyFlags.FLAG_HEADLESS_SINGLE_USER_FIXES)
     fun upcomingWipe() =
         testScope.runTest {
             val upcomingWipe by collectLastValue(underTest.upcomingWipe)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/battery/AccessorizedBatteryDrawableTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatterySpecsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/battery/BatterySpecsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/battery/BatterySpecsTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 9b1d4ec..b7d99d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -79,6 +79,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCapture;
 import com.android.internal.R;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
@@ -96,6 +97,8 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
+import dagger.Lazy;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -148,8 +151,6 @@
     @Mock
     private WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock
-    private AuthDialogPanelInteractionDetector mPanelInteractionDetector;
-    @Mock
     private UserManager mUserManager;
     @Mock
     private LockPatternUtils mLockPatternUtils;
@@ -167,6 +168,8 @@
     private PromptViewModel mPromptViewModel;
     @Mock
     private UdfpsUtils mUdfpsUtils;
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
 
     @Captor
     private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
@@ -1059,11 +1062,11 @@
             super(context, null /* applicationCoroutineScope */,
                     mExecution, mCommandQueue, mActivityTaskManager, mWindowManager,
                     mFingerprintManager, mFaceManager, () -> mUdfpsController, mDisplayManager,
-                    mWakefulnessLifecycle, mPanelInteractionDetector, mUserManager,
-                    mLockPatternUtils, () -> mUdfpsLogger, () -> mLogContextInteractor,
-                    () -> mPromptSelectionInteractor, () -> mCredentialViewModel,
-                    () -> mPromptViewModel, mInteractionJankMonitor,
-                    mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper);
+                    mWakefulnessLifecycle, mUserManager, mLockPatternUtils, () -> mUdfpsLogger,
+                    () -> mLogContextInteractor, () -> mPromptSelectionInteractor,
+                    () -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
+                    mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper,
+                    mLazyViewCapture);
         }
 
         @Override
@@ -1071,7 +1074,6 @@
                 boolean requireConfirmation, int userId, int[] sensorIds,
                 String opPackageName, boolean skipIntro, long operationId, long requestId,
                 WakefulnessLifecycle wakefulnessLifecycle,
-                AuthDialogPanelInteractionDetector panelInteractionDetector,
                 UserManager userManager,
                 LockPatternUtils lockPatternUtils, PromptViewModel viewModel) {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/BiometricNotificationDialogFactoryTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceAuthAccessibilityDelegateTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
new file mode 100644
index 0000000..a36b0bc
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.hardware.biometrics.BiometricFaceConstants
+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.deviceentry.shared.model.HelpFaceAuthenticationStatus
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
[email protected]
+class FaceHelpMessageDebouncerTest : SysuiTestCase() {
+    private lateinit var underTest: FaceHelpMessageDebouncer
+    private val window = 9L
+    private val startWindow = 4L
+    private val shownFaceMessageFrequencyBoost = 2
+
+    @Before
+    fun setUp() {
+        underTest =
+            FaceHelpMessageDebouncer(
+                window = window,
+                startWindow = startWindow,
+                shownFaceMessageFrequencyBoost = shownFaceMessageFrequencyBoost,
+            )
+    }
+
+    @Test
+    fun getMessageBeforeStartWindow_null() {
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "testTooClose",
+                0
+            )
+        )
+        assertThat(underTest.getMessageToShow(0)).isNull()
+    }
+
+    @Test
+    fun getMessageAfterStartWindow() {
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+
+        assertThat(underTest.getMessageToShow(startWindow)?.msgId)
+            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE)
+        assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose")
+    }
+
+    @Test
+    fun getMessageAfterMessagesCleared_null() {
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.startNewFaceAuthSession(0)
+
+        assertThat(underTest.getMessageToShow(startWindow)).isNull()
+    }
+
+    @Test
+    fun messagesBeforeWindowRemoved() {
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                window - 1
+            )
+        )
+        val lastMessage =
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
+                "tooBright",
+                window
+            )
+        underTest.addMessage(lastMessage)
+
+        assertThat(underTest.getMessageToShow(window + 1)).isEqualTo(lastMessage)
+    }
+
+    @Test
+    fun getMessageTieGoesToMostRecent() {
+        for (i in 1..window step 2) {
+            underTest.addMessage(
+                HelpFaceAuthenticationStatus(
+                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                    "tooClose",
+                    i
+                )
+            )
+            underTest.addMessage(
+                HelpFaceAuthenticationStatus(
+                    BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
+                    "tooBright",
+                    i + 1
+                )
+            )
+        }
+
+        assertThat(underTest.getMessageToShow(window)?.msgId)
+            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT)
+        assertThat(underTest.getMessageToShow(window)?.msg).isEqualTo("tooBright")
+    }
+
+    @Test
+    fun boostCurrentlyShowingMessage() {
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
+                "tooBright",
+                0
+            )
+        )
+
+        val lastMessageShown = underTest.getMessageToShow(startWindow)
+        assertThat(lastMessageShown?.msgId)
+            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT)
+
+        for (i in 1..<shownFaceMessageFrequencyBoost) {
+            underTest.addMessage(
+                HelpFaceAuthenticationStatus(
+                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                    "tooClose",
+                    startWindow
+                )
+            )
+        }
+
+        // although technically there's a different msgId with a higher frequency count now, the
+        // shownFaceMessageFrequencyBoost causes the last message shown to get a "boost"
+        // to keep showing
+        assertThat(underTest.getMessageToShow(startWindow)).isEqualTo(lastMessageShown)
+    }
+
+    @Test
+    fun overcomeBoostedCurrentlyShowingMessage() {
+        // Comments are assuming shownFaceMessageFrequencyBoost = 2
+        // [B], weights: B=1
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
+                "tooBright",
+                0
+            )
+        )
+
+        // [B], showing messageB, weights: B=3
+        val messageB = underTest.getMessageToShow(startWindow)
+
+        // [B, C, C], showing messageB, weights: B=3, C=2
+        for (i in 1..shownFaceMessageFrequencyBoost) {
+            underTest.addMessage(
+                HelpFaceAuthenticationStatus(
+                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                    "tooClose",
+                    startWindow
+                )
+            )
+        }
+        // messageB is getting boosted to continue to show
+        assertThat(underTest.getMessageToShow(startWindow)).isEqualTo(messageB)
+
+        // receive one more FACE_ACQUIRED_TOO_CLOSE acquired info to pass the boost
+        // [C, C, C], showing messageB, weights: B=2, C=3
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                startWindow
+            )
+        )
+
+        // Now FACE_ACQUIRED_TOO_CLOSE has surpassed the boosted messageB frequency
+        // [C, C, C], showing messageC, weights: C=5
+        assertThat(underTest.getMessageToShow(startWindow)?.msgId)
+            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE)
+    }
+
+    @Test
+    fun messageMustMeetThreshold() {
+        underTest =
+            FaceHelpMessageDebouncer(
+                window = window,
+                startWindow = 0,
+                shownFaceMessageFrequencyBoost = 0,
+                threshold = .8f,
+            )
+
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
+                "tooBright",
+                0
+            )
+        )
+
+        // although tooClose message is the majority, it doesn't meet the 80% threshold
+        assertThat(underTest.getMessageToShow(startWindow)).isNull()
+
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+        underTest.addMessage(
+            HelpFaceAuthenticationStatus(
+                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
+                "tooClose",
+                0
+            )
+        )
+
+        // message shows once it meets the threshold
+        assertThat(underTest.getMessageToShow(startWindow)?.msgId)
+            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE)
+        assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
index b31f6f5..add7a7f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.biometrics
 
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.keyguard.logging.BiometricMessageDeferralLogger
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertNull
@@ -31,14 +35,29 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 @SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @android.platform.test.annotations.EnabledOnRavenwood
-class FaceHelpMessageDeferralTest : SysuiTestCase() {
+class FaceHelpMessageDeferralTest(flags: FlagsParameterization) : SysuiTestCase() {
     val threshold = .75f
     @Mock lateinit var logger: BiometricMessageDeferralLogger
     @Mock lateinit var dumpManager: DumpManager
+    val systemClock = FakeSystemClock()
+
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.allCombinationsOf(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
 
     @Before
     fun setUp() {
@@ -111,10 +130,11 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
     fun testReturnsMostFrequentDeferredMessage() {
         val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
 
-        // WHEN there's 80%of the messages are msgId=1 and 20% is msgId=2
+        // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2
         biometricMessageDeferral.processFrame(1)
         biometricMessageDeferral.processFrame(1)
         biometricMessageDeferral.processFrame(1)
@@ -124,7 +144,41 @@
         biometricMessageDeferral.processFrame(2)
         biometricMessageDeferral.updateMessage(2, "msgId-2")
 
-        // THEN the most frequent deferred message is that meets the threshold is returned
+        // THEN the most frequent deferred message that meets the threshold is returned
+        assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
+    fun testReturnsMostFrequentDeferredMessage_onlyAnalyzesLastNWindow() {
+        val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+        // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last
+        // N window only contains messages with msgId=2
+        repeat(80) { biometricMessageDeferral.processFrame(1) }
+        biometricMessageDeferral.updateMessage(1, "msgId-1")
+        systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L)
+        repeat(20) { biometricMessageDeferral.processFrame(2) }
+        biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+        // THEN the most frequent deferred message in the last N window (500L) is returned
+        assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_FACE_MESSAGE_DEFER_UPDATE)
+    fun testReturnsMostFrequentDeferredMessage_analyzesAllFrames() {
+        val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+        // WHEN there's 80% of the messages are msgId=1 and 20% is msgId=2, but the last
+        // N window only contains messages with msgId=2
+        repeat(80) { biometricMessageDeferral.processFrame(1) }
+        biometricMessageDeferral.updateMessage(1, "msgId-1")
+        systemClock.setElapsedRealtime(systemClock.elapsedRealtime() + 501L)
+        repeat(20) { biometricMessageDeferral.processFrame(2) }
+        biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+        // THEN the most frequent deferred message is returned
         assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
     }
 
@@ -213,14 +267,17 @@
     private fun createMsgDeferral(
         messagesToDefer: Set<Int>,
         acquiredInfoToIgnore: Set<Int> = emptySet(),
+        windowToAnalyzeLastNFrames: Long = 500L,
     ): BiometricMessageDeferral {
         return BiometricMessageDeferral(
-            messagesToDefer,
-            acquiredInfoToIgnore,
-            threshold,
-            logger,
-            dumpManager,
-            "0",
+            messagesToDefer = messagesToDefer,
+            acquiredInfoToIgnore = acquiredInfoToIgnore,
+            threshold = threshold,
+            windowToAnalyzeLastNFrames = windowToAnalyzeLastNFrames,
+            logBuffer = logger,
+            dumpManager = dumpManager,
+            id = "0",
+            systemClock = { systemClock },
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
deleted file mode 100644
index cd9189b..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapterTest.java
+++ /dev/null
@@ -1,141 +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 org.junit.Assert.assertEquals;
-
-import android.hardware.biometrics.ComponentInfoInternal;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.biometrics.SensorProperties;
-import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class UdfpsDialogMeasureAdapterTest extends SysuiTestCase {
-    @Test
-    public void testUdfpsBottomSpacerHeightForPortrait() {
-        final int displayHeightPx = 3000;
-        final int navbarHeightPx = 10;
-        final int dialogBottomMarginPx = 20;
-        final int buttonBarHeightPx = 100;
-        final int textIndicatorHeightPx = 200;
-
-        final int sensorLocationX = 540;
-        final int sensorLocationY = 1600;
-        final int sensorRadius = 100;
-
-        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
-        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
-                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                "00000001" /* serialNumber */, "" /* softwareVersion */));
-        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
-                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                "vendor/version/revision" /* softwareVersion */));
-
-        final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
-                0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
-                componentInfo,
-                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                true /* halControlsIllumination */,
-                true /* resetLockoutRequiresHardwareAuthToken */,
-                List.of(new SensorLocationInternal("" /* displayId */,
-                        sensorLocationX, sensorLocationY, sensorRadius)));
-
-        assertEquals(970,
-                UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForPortrait(
-                        props, displayHeightPx, textIndicatorHeightPx, buttonBarHeightPx,
-                        dialogBottomMarginPx, navbarHeightPx, 1.0f /* resolutionScale */
-                ));
-    }
-
-    @Test
-    public void testUdfpsBottomSpacerHeightForLandscape_whenMoreSpaceAboveIcon() {
-        final int titleHeightPx = 320;
-        final int subtitleHeightPx = 240;
-        final int descriptionHeightPx = 200;
-        final int topSpacerHeightPx = 550;
-        final int textIndicatorHeightPx = 190;
-        final int buttonBarHeightPx = 160;
-        final int navbarBottomInsetPx = 75;
-
-        assertEquals(885,
-                UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForLandscape(
-                        titleHeightPx, subtitleHeightPx, descriptionHeightPx, topSpacerHeightPx,
-                        textIndicatorHeightPx, buttonBarHeightPx, navbarBottomInsetPx));
-    }
-
-    @Test
-    public void testUdfpsBottomSpacerHeightForLandscape_whenMoreSpaceBelowIcon() {
-        final int titleHeightPx = 315;
-        final int subtitleHeightPx = 160;
-        final int descriptionHeightPx = 75;
-        final int topSpacerHeightPx = 220;
-        final int textIndicatorHeightPx = 290;
-        final int buttonBarHeightPx = 360;
-        final int navbarBottomInsetPx = 205;
-
-        assertEquals(-85,
-                UdfpsDialogMeasureAdapter.calculateBottomSpacerHeightForLandscape(
-                        titleHeightPx, subtitleHeightPx, descriptionHeightPx, topSpacerHeightPx,
-                        textIndicatorHeightPx, buttonBarHeightPx, navbarBottomInsetPx));
-    }
-
-    @Test
-    public void testUdfpsHorizontalSpacerWidthForLandscape() {
-        final int displayWidthPx = 3000;
-        final int dialogMarginPx = 20;
-        final int navbarHorizontalInsetPx = 75;
-
-        final int sensorLocationX = 540;
-        final int sensorLocationY = 1600;
-        final int sensorRadius = 100;
-
-        final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
-        componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
-                "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
-                "00000001" /* serialNumber */, "" /* softwareVersion */));
-        componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
-                "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
-                "vendor/version/revision" /* softwareVersion */));
-
-        final FingerprintSensorPropertiesInternal props = new FingerprintSensorPropertiesInternal(
-                0 /* sensorId */, SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */,
-                componentInfo,
-                FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
-                true /* halControlsIllumination */,
-                true /* resetLockoutRequiresHardwareAuthToken */,
-                List.of(new SensorLocationInternal("" /* displayId */,
-                        sensorLocationX, sensorLocationY, sensorRadius)));
-
-        assertEquals(1205,
-                UdfpsDialogMeasureAdapter.calculateHorizontalSpacerWidthForLandscape(
-                        props, displayWidthPx, dialogMarginPx, navbarHorizontalInsetPx,
-                        1.0f /* resolutionScale */));
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsUtilsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/DisplayStateRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FacePropertyRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/FingerprintRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/UdfpsOverlayInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/BoundingBoxOverlapDetectorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/EllipseOverlapDetectorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/NormalizedTouchDataTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/udfps/SinglePointerTouchProcessorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/DeviceEntryUdfpsTouchOverlayViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
new file mode 100644
index 0000000..0db7b62
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
@@ -0,0 +1,448 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.res.Configuration.UI_MODE_NIGHT_NO
+import android.content.res.Configuration.UI_MODE_NIGHT_YES
+import android.graphics.Color
+import android.graphics.Rect
+import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.display.DisplayManagerGlobal
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.WindowInsets
+import android.view.WindowMetrics
+import android.view.windowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.airbnb.lottie.model.KeyPath
+import com.android.keyguard.keyguardUpdateMonitor
+import com.android.settingslib.Utils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
+import com.android.systemui.biometrics.data.repository.biometricStatusRepository
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
+import com.android.systemui.biometrics.shared.model.AuthenticationReason
+import com.android.systemui.biometrics.shared.model.DisplayRotation
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.LottieCallback
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayStateRepository
+import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+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.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SideFpsOverlayViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    @Mock
+    private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
+    @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
+
+    private val contextDisplayInfo = DisplayInfo()
+
+    private val indicatorColor =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorPrimaryFixed
+        )
+    private val outerRimColor =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorPrimaryFixedDim
+        )
+    private val chevronFill =
+        Utils.getColorAttrDefaultColor(
+            context,
+            com.android.internal.R.attr.materialColorOnPrimaryFixed
+        )
+    private val color_blue400 =
+        context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400)
+
+    private var displayWidth: Int = 0
+    private var displayHeight: Int = 0
+    private var boundsWidth: Int = 0
+    private var boundsHeight: Int = 0
+
+    private lateinit var deviceConfig: DeviceConfig
+    private lateinit var sensorLocation: SensorLocationInternal
+
+    enum class DeviceConfig {
+        X_ALIGNED,
+        Y_ALIGNED,
+    }
+
+    @Before
+    fun setup() {
+        mContext = spy(mContext)
+
+        val resources = mContext.resources
+        whenever(mContext.display)
+            .thenReturn(
+                Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
+            )
+        kosmos.displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
+
+        whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
+            .thenReturn(MutableStateFlow(false))
+    }
+
+    @Test
+    fun updatesOverlayViewProperties_onDisplayRotationChange_xAlignedSensor() {
+        kosmos.testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewProperties by
+                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewProperties)
+
+            runCurrent()
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewProperties_onDisplayRotationChange_yAlignedSensor() {
+        kosmos.testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.Y_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewProperties by
+                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewProperties)
+
+            runCurrent()
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+
+            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
+            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() {
+        kosmos.testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.X_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewParams by
+                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewParams)
+
+            kosmos.sideFpsOverlayViewModel.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+            runCurrent()
+
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationX)
+            assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(0)
+            assertThat(overlayViewParams!!.y)
+                .isEqualTo(
+                    displayHeight - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+                )
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x)
+                .isEqualTo(
+                    displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
+                )
+            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth)
+            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX)
+        }
+    }
+
+    @Test
+    fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() {
+        kosmos.testScope.runTest {
+            setupTestConfiguration(
+                DeviceConfig.Y_ALIGNED,
+                rotation = DisplayRotation.ROTATION_0,
+                isInRearDisplayMode = false
+            )
+
+            val overlayViewParams by
+                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewParams)
+
+            kosmos.sideFpsOverlayViewModel.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
+            runCurrent()
+
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth)
+            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationY)
+            assertThat(overlayViewParams!!.y).isEqualTo(0)
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x).isEqualTo(0)
+            assertThat(overlayViewParams!!.y)
+                .isEqualTo(
+                    displayHeight - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+                )
+
+            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
+            assertThat(overlayViewParams).isNotNull()
+            assertThat(overlayViewParams!!.x)
+                .isEqualTo(
+                    displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
+                )
+            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight)
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForDeviceEntry() {
+        kosmos.testScope.runTest {
+            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
+
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.NotRunning
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = true,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".black", "**"), chevronFill))
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inDarkMode() {
+        kosmos.testScope.runTest {
+            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
+            setDarkMode(true)
+
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+        }
+    }
+
+    @Test
+    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inLightMode() {
+        kosmos.testScope.runTest {
+            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
+            setDarkMode(false)
+
+            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
+                AuthenticationReason.BiometricPromptAuthentication
+            )
+            kosmos.sideFpsProgressBarViewModel.setVisible(false)
+
+            updatePrimaryBouncer(
+                isShowing = false,
+                isAnimatingAway = false,
+                fpsDetectionRunning = true,
+                isUnlockingWithFpAllowed = true
+            )
+            runCurrent()
+
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
+            assertThat(lottieCallbacks)
+                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
+        }
+    }
+
+    private fun setDarkMode(inDarkMode: Boolean) {
+        val uiMode =
+            if (inDarkMode) {
+                UI_MODE_NIGHT_YES
+            } else {
+                UI_MODE_NIGHT_NO
+            }
+
+        mContext.resources.configuration.uiMode = uiMode
+    }
+
+    private fun updatePrimaryBouncer(
+        isShowing: Boolean,
+        isAnimatingAway: Boolean,
+        fpsDetectionRunning: Boolean,
+        isUnlockingWithFpAllowed: Boolean,
+    ) {
+        kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
+        kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
+        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
+        kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
+            primaryStartDisappearAnimation
+        )
+
+        whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
+            .thenReturn(fpsDetectionRunning)
+        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
+            .thenReturn(isUnlockingWithFpAllowed)
+        mContext.orCreateTestableResources.addOverride(
+            R.bool.config_show_sidefps_hint_on_bouncer,
+            true
+        )
+    }
+
+    private suspend fun TestScope.setupTestConfiguration(
+        deviceConfig: DeviceConfig,
+        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
+        isInRearDisplayMode: Boolean,
+    ) {
+        [email protected] = deviceConfig
+
+        when (deviceConfig) {
+            DeviceConfig.X_ALIGNED -> {
+                displayWidth = 3000
+                displayHeight = 1500
+                boundsWidth = 200
+                boundsHeight = 100
+                sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
+            }
+            DeviceConfig.Y_ALIGNED -> {
+                displayWidth = 2500
+                displayHeight = 2000
+                boundsWidth = 100
+                boundsHeight = 200
+                sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
+            }
+        }
+
+        whenever(kosmos.windowManager.maximumWindowMetrics)
+            .thenReturn(
+                WindowMetrics(
+                    Rect(0, 0, displayWidth, displayHeight),
+                    mock(WindowInsets::class.java),
+                )
+            )
+
+        contextDisplayInfo.uniqueId = DISPLAY_ID
+
+        kosmos.fingerprintPropertyRepository.setProperties(
+            sensorId = 1,
+            strength = SensorStrength.STRONG,
+            sensorType = FingerprintSensorType.POWER_BUTTON,
+            sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
+        )
+
+        kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
+        kosmos.displayStateRepository.setCurrentRotation(rotation)
+
+        kosmos.displayRepository.emitDisplayChangeEvent(0)
+        runCurrent()
+    }
+
+    companion object {
+        private const val DISPLAY_ID = "displayId"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothAutoOnInteractorTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.kt
new file mode 100644
index 0000000..969e26a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorKosmos.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.bluetooth.qsdialog
+
+import com.android.systemui.bluetooth.bluetoothAdapter
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import org.mockito.kotlin.mock
+
+val Kosmos.deviceItemInteractor: DeviceItemInteractor by
+    Kosmos.Fixture { mock<DeviceItemInteractor>() }
+
+val Kosmos.bluetoothDeviceMetadataInteractor by
+    Kosmos.Fixture {
+        BluetoothDeviceMetadataInteractor(
+            deviceItemInteractor,
+            bluetoothAdapter,
+            bluetoothTileDialogLogger,
+            fakeExecutor,
+            testDispatcher,
+        )
+    }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorTest.kt
new file mode 100644
index 0000000..f06b105
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractorTest.kt
@@ -0,0 +1,226 @@
+/*
+ * 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.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bluetooth.bluetoothAdapter
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+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.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.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+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.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
[email protected](setAsMainLooper = true)
+class BluetoothDeviceMetadataInteractorTest : SysuiTestCase() {
+    @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+    private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() }
+
+    private val deviceItemUpdate: MutableSharedFlow<List<DeviceItem>> = MutableSharedFlow()
+    @Mock private lateinit var cachedDevice1: CachedBluetoothDevice
+    @Mock private lateinit var bluetoothDevice1: BluetoothDevice
+    @Mock private lateinit var cachedDevice2: CachedBluetoothDevice
+    @Mock private lateinit var bluetoothDevice2: BluetoothDevice
+    @Captor
+    private lateinit var argumentCaptor: ArgumentCaptor<BluetoothAdapter.OnMetadataChangedListener>
+    private lateinit var interactor: BluetoothDeviceMetadataInteractor
+
+    @Before
+    fun setUp() {
+        with(kosmos) {
+            whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(deviceItemUpdate)
+
+            whenever(cachedDevice1.device).thenReturn(bluetoothDevice1)
+            whenever(cachedDevice1.name).thenReturn(DEVICE_NAME)
+            whenever(cachedDevice1.address).thenReturn(DEVICE_ADDRESS)
+            whenever(cachedDevice1.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+            whenever(bluetoothDevice1.address).thenReturn(DEVICE_ADDRESS)
+
+            whenever(cachedDevice2.device).thenReturn(bluetoothDevice2)
+            whenever(cachedDevice2.name).thenReturn(DEVICE_NAME)
+            whenever(cachedDevice2.address).thenReturn(DEVICE_ADDRESS)
+            whenever(cachedDevice2.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+            whenever(bluetoothDevice2.address).thenReturn(DEVICE_ADDRESS)
+
+            interactor = bluetoothDeviceMetadataInteractor
+        }
+    }
+
+    @Test
+    fun deviceItemUpdateEmpty_doNothing() {
+        with(kosmos) {
+            testScope.runTest {
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(emptyList())
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter, never()).addOnMetadataChangedListener(any(), any(), any())
+                verify(bluetoothAdapter, never()).removeOnMetadataChangedListener(any(), any())
+            }
+        }
+    }
+
+    @Test
+    fun deviceItemUpdate_registerListener() {
+        with(kosmos) {
+            testScope.runTest {
+                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(listOf(deviceItem))
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter)
+                    .addOnMetadataChangedListener(eq(bluetoothDevice1), any(), any())
+                verify(bluetoothAdapter, never()).removeOnMetadataChangedListener(any(), any())
+            }
+        }
+    }
+
+    @Test
+    fun deviceItemUpdate_sameDeviceItems_registerListenerOnce() {
+        with(kosmos) {
+            testScope.runTest {
+                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(listOf(deviceItem))
+                deviceItemUpdate.emit(listOf(deviceItem))
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter)
+                    .addOnMetadataChangedListener(eq(bluetoothDevice1), any(), any())
+                verify(bluetoothAdapter, never()).removeOnMetadataChangedListener(any(), any())
+            }
+        }
+    }
+
+    @Test
+    fun deviceItemUpdate_differentDeviceItems_unregisterOldAndRegisterNew() {
+        with(kosmos) {
+            testScope.runTest {
+                val deviceItem1 = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
+                val deviceItem2 = AvailableMediaDeviceItemFactory().create(context, cachedDevice2)
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(listOf(deviceItem1))
+                deviceItemUpdate.emit(listOf(deviceItem1, deviceItem2))
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter, times(2))
+                    .addOnMetadataChangedListener(eq(bluetoothDevice1), any(), any())
+                verify(bluetoothAdapter)
+                    .addOnMetadataChangedListener(eq(bluetoothDevice2), any(), any())
+                verify(bluetoothAdapter)
+                    .removeOnMetadataChangedListener(eq(bluetoothDevice1), any())
+            }
+        }
+    }
+
+    @Test
+    fun metadataUpdate_triggerCallback_emit() {
+        with(kosmos) {
+            testScope.runTest {
+                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(listOf(deviceItem))
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter)
+                    .addOnMetadataChangedListener(
+                        eq(bluetoothDevice1),
+                        any(),
+                        argumentCaptor.capture()
+                    )
+
+                val listener = argumentCaptor.value
+                listener.onMetadataChanged(
+                    bluetoothDevice1,
+                    BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
+                    ByteArray(0)
+                )
+                assertThat(update).isEqualTo(Unit)
+            }
+        }
+    }
+
+    @Test
+    fun metadataUpdate_triggerCallbackNonBatteryKey_doNothing() {
+        with(kosmos) {
+            testScope.runTest {
+                val deviceItem = AvailableMediaDeviceItemFactory().create(context, cachedDevice1)
+                val update by collectLastValue(interactor.metadataUpdate)
+                deviceItemUpdate.emit(listOf(deviceItem))
+                runCurrent()
+
+                assertThat(update).isNull()
+                verify(bluetoothAdapter)
+                    .addOnMetadataChangedListener(
+                        eq(bluetoothDevice1),
+                        any(),
+                        argumentCaptor.capture()
+                    )
+
+                val listener = argumentCaptor.value
+                listener.onMetadataChanged(
+                    bluetoothDevice1,
+                    BluetoothDevice.METADATA_MODEL_NAME,
+                    ByteArray(0)
+                )
+
+                assertThat(update).isNull()
+            }
+        }
+    }
+
+    companion object {
+        private const val DEVICE_NAME = "DeviceName"
+        private const val CONNECTION_SUMMARY = "ConnectionSummary"
+        private const val DEVICE_ADDRESS = "04:52:C7:0B:D8:3C"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index d850f17..65236f0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -30,8 +30,6 @@
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags.REFACTOR_GETCURRENTUSER
-import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
@@ -90,8 +88,6 @@
             .thenReturn(needsEmergencyAffordance)
         whenever(telecomManager.isInCall).thenReturn(false)
 
-        kosmos.fakeFeatureFlagsClassic.set(REFACTOR_GETCURRENTUSER, true)
-
         kosmos.fakeTelephonyRepository.setHasTelephonyRadio(true)
 
         kosmos.telecomManager = telecomManager
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index cfe0bec..65c9b72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.bouncer.domain.interactor
 
 import android.content.pm.UserInfo
+import android.hardware.biometrics.BiometricFaceConstants
+import android.hardware.biometrics.BiometricSourceType
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -25,6 +27,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Pattern
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FaceSensorInfo
@@ -36,7 +39,6 @@
 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
@@ -58,7 +60,9 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -77,6 +81,8 @@
     @Mock private lateinit var securityModel: KeyguardSecurityModel
     @Mock private lateinit var countDownTimerUtil: CountDownTimerUtil
     @Mock private lateinit var systemPropertiesHelper: SystemPropertiesHelper
+    @Captor
+    private lateinit var keyguardUpdateMonitorCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
 
     private lateinit var underTest: BouncerMessageInteractor
 
@@ -107,8 +113,6 @@
                 systemPropertiesHelper = systemPropertiesHelper,
                 primaryBouncerInteractor = kosmos.primaryBouncerInteractor,
                 facePropertyRepository = kosmos.fakeFacePropertyRepository,
-                deviceEntryFingerprintAuthInteractor = kosmos.deviceEntryFingerprintAuthInteractor,
-                faceAuthRepository = kosmos.fakeDeviceEntryFaceAuthRepository,
                 securityModel = securityModel,
                 deviceEntryBiometricsAllowedInteractor =
                     kosmos.deviceEntryBiometricsAllowedInteractor,
@@ -207,6 +211,52 @@
         }
 
     @Test
+    fun resetMessageBackToDefault_faceAuthRestarts() =
+        testScope.runTest {
+            init()
+            captureKeyguardUpdateMonitorCallback()
+            val bouncerMessage by collectLastValue(underTest.bouncerMessage)
+
+            underTest.setFaceAcquisitionMessage("not empty")
+
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
+
+            keyguardUpdateMonitorCaptor.value.onBiometricAcquired(
+                BiometricSourceType.FACE,
+                BiometricFaceConstants.FACE_ACQUIRED_START
+            )
+
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(bouncerMessage?.secondaryMessage?.message).isNull()
+        }
+
+    @Test
+    fun faceRestartDoesNotResetFingerprintMessage() =
+        testScope.runTest {
+            init()
+            captureKeyguardUpdateMonitorCallback()
+            val bouncerMessage by collectLastValue(underTest.bouncerMessage)
+
+            underTest.setFingerprintAcquisitionMessage("not empty")
+
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
+
+            keyguardUpdateMonitorCaptor.value.onBiometricAcquired(
+                BiometricSourceType.FACE,
+                BiometricFaceConstants.FACE_ACQUIRED_START
+            )
+
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(bouncerMessage?.secondaryMessage?.message).isEqualTo("not empty")
+        }
+
+    @Test
     fun setFingerprintMessage_propagateValue() =
         testScope.runTest {
             init()
@@ -284,6 +334,32 @@
         }
 
     @Test
+    fun faceLockoutThenFaceFailure_doesNotUpdateMessage() =
+        testScope.runTest {
+            init()
+            captureKeyguardUpdateMonitorCallback()
+            val bouncerMessage by collectLastValue(underTest.bouncerMessage)
+
+            kosmos.fakeBiometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+            kosmos.fakeDeviceEntryFaceAuthRepository.setLockedOut(true)
+            runCurrent()
+
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(secondaryResMessage(bouncerMessage))
+                .isEqualTo("Can’t unlock with face. Too many attempts.")
+
+            // WHEN face failure comes in during lockout
+            keyguardUpdateMonitorCaptor.value.onBiometricAuthFailed(BiometricSourceType.FACE)
+
+            // THEN lockout message does NOT update to face failure message
+            assertThat(primaryResMessage(bouncerMessage))
+                .isEqualTo("Unlock with PIN or fingerprint")
+            assertThat(secondaryResMessage(bouncerMessage))
+                .isEqualTo("Can’t unlock with face. Too many attempts.")
+        }
+
+    @Test
     fun onFaceLockoutStateChange_whenFaceIsNotEnrolled_isANoop() =
         testScope.runTest {
             init()
@@ -576,6 +652,10 @@
         }
     }
 
+    private fun captureKeyguardUpdateMonitorCallback() {
+        verify(updateMonitor).registerCallback(keyguardUpdateMonitorCaptor.capture())
+    }
+
     companion object {
         private const val PRIMARY_USER_ID = 0
         private val PRIMARY_USER =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
index c9fa671..deef652 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -22,14 +22,14 @@
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.coroutines.collectLastValue
 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.flow.MutableStateFlow
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -39,17 +39,16 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
-    private val underTest by lazy {
-        PinBouncerViewModel(
-            applicationContext = context,
-            viewModelScope = testScope.backgroundScope,
-            interactor = bouncerInteractor,
+    private val underTest =
+        kosmos.pinBouncerViewModelFactory.create(
             isInputEnabled = MutableStateFlow(true),
-            simBouncerInteractor = kosmos.simBouncerInteractor,
-            authenticationMethod = AuthenticationMethodModel.Pin,
             onIntentionalUserInput = {},
+            authenticationMethod = AuthenticationMethodModel.Pin,
         )
+
+    @Before
+    fun setUp() {
+        underTest.activateIn(testScope)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
index 4f5d0e5..8c8faee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelTest.kt
@@ -19,9 +19,11 @@
 import android.content.pm.UserInfo
 import android.hardware.biometrics.BiometricFaceConstants
 import android.hardware.fingerprint.FingerprintManager
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
@@ -35,7 +37,6 @@
 import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.shared.model.SensorStrength
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.shared.flag.fakeComposeBouncerFlags
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
@@ -52,6 +53,7 @@
 import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
 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.user.data.repository.fakeUserRepository
@@ -70,6 +72,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_COMPOSE_BOUNCER)
 class BouncerMessageViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
@@ -81,12 +84,12 @@
     @Before
     fun setUp() {
         kosmos.fakeUserRepository.setUserInfos(listOf(PRIMARY_USER))
-        kosmos.fakeComposeBouncerFlags.composeBouncerEnabled = true
         overrideResource(
             R.array.config_face_acquire_device_entry_ignorelist,
             intArrayOf(ignoreHelpMessageId)
         )
         underTest = kosmos.bouncerMessageViewModel
+        underTest.activateIn(testScope)
         overrideResource(R.string.kg_trust_agent_disabled, "Trust agent is unavailable")
         kosmos.fakeSystemPropertiesHelper.set(
             DeviceUnlockedInteractor.SYS_BOOT_REASON_PROP,
@@ -428,6 +431,22 @@
                 .isEqualTo("Can’t unlock with face. Too many attempts.")
         }
 
+    @Test
+    fun startLockdownCountdown_onActivated() =
+        testScope.runTest {
+            val bouncerMessage by collectLastValue(underTest.message)
+            val lockoutSeconds = 200 * 1000 // 200 second lockout
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            kosmos.fakeAuthenticationRepository.reportLockoutStarted(lockoutSeconds)
+            runCurrent()
+
+            assertThat(bouncerMessage?.text).isEqualTo("Try again in 200 seconds.")
+            advanceTimeBy(100.seconds)
+            assertThat(bouncerMessage?.text).isEqualTo("Try again in 100 seconds.")
+            advanceTimeBy(101.seconds)
+            assertThat(bouncerMessage?.text).isEqualTo("Enter PIN")
+        }
+
     private fun TestScope.verifyMessagesForAuthFlags(
         vararg authFlagToMessagePair: Pair<Int, Pair<String, String?>>
     ) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
new file mode 100644
index 0000000..9bddcd2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
+import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.currentTime
+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)
+@EnableSceneContainer
+class BouncerSceneContentViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private lateinit var underTest: BouncerSceneContentViewModel
+
+    @Before
+    fun setUp() {
+        kosmos.sceneContainerStartable.start()
+        underTest = kosmos.bouncerSceneContentViewModel
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
+        testScope.runTest {
+            var authMethodViewModel: AuthMethodBouncerViewModel? = null
+
+            authMethodsToTest().forEach { authMethod ->
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+                val job =
+                    underTest.authMethodViewModel.onEach { authMethodViewModel = it }.launchIn(this)
+                runCurrent()
+
+                if (authMethod.isSecure) {
+                    assertWithMessage("View-model unexpectedly null for auth method $authMethod")
+                        .that(authMethodViewModel)
+                        .isNotNull()
+                } else {
+                    assertWithMessage(
+                            "View-model unexpectedly non-null for auth method $authMethod"
+                        )
+                        .that(authMethodViewModel)
+                        .isNull()
+                }
+
+                job.cancel()
+            }
+        }
+
+    @Test
+    fun authMethodChanged_doesNotReuseInstances() =
+        testScope.runTest {
+            val seen = mutableMapOf<AuthenticationMethodModel, AuthMethodBouncerViewModel>()
+            val authMethodViewModel: AuthMethodBouncerViewModel? by
+                collectLastValue(underTest.authMethodViewModel)
+
+            // First pass, populate our "seen" map:
+            authMethodsToTest().forEach { authMethod ->
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+                authMethodViewModel?.let { seen[authMethod] = it }
+            }
+
+            // Second pass, assert same instances are not reused:
+            authMethodsToTest().forEach { authMethod ->
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+                authMethodViewModel?.let {
+                    assertThat(it.authenticationMethod).isEqualTo(authMethod)
+                    assertThat(it).isNotSameInstanceAs(seen[authMethod])
+                }
+            }
+        }
+
+    @Test
+    fun authMethodUnchanged_reusesInstances() =
+        testScope.runTest {
+            authMethodsToTest().forEach { authMethod ->
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+                val firstInstance: AuthMethodBouncerViewModel? =
+                    collectLastValue(underTest.authMethodViewModel).invoke()
+
+                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+                val secondInstance: AuthMethodBouncerViewModel? =
+                    collectLastValue(underTest.authMethodViewModel).invoke()
+
+                firstInstance?.let { assertThat(it.authenticationMethod).isEqualTo(authMethod) }
+                assertThat(secondInstance).isSameInstanceAs(firstInstance)
+            }
+        }
+
+    @Test
+    fun authMethodsToTest_returnsCompleteSampleOfAllAuthMethodTypes() {
+        assertThat(authMethodsToTest().map { it::class }.toSet())
+            .isEqualTo(AuthenticationMethodModel::class.sealedSubclasses.toSet())
+    }
+
+    @Test
+    fun isInputEnabled() =
+        testScope.runTest {
+            val isInputEnabled by
+                collectLastValue(
+                    underTest.authMethodViewModel.flatMapLatest { authViewModel ->
+                        authViewModel?.isInputEnabled ?: emptyFlow()
+                    }
+                )
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            assertThat(isInputEnabled).isTrue()
+
+            repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
+                kosmos.bouncerInteractor.authenticate(WRONG_PIN)
+            }
+            assertThat(isInputEnabled).isFalse()
+
+            val lockoutEndMs = kosmos.authenticationInteractor.lockoutEndTimestamp ?: 0
+            advanceTimeBy(lockoutEndMs - testScope.currentTime)
+            assertThat(isInputEnabled).isTrue()
+        }
+
+    @Test
+    fun isSideBySideSupported() =
+        testScope.runTest {
+            val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            assertThat(isSideBySideSupported).isTrue()
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
+            assertThat(isSideBySideSupported).isTrue()
+
+            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            assertThat(isSideBySideSupported).isTrue()
+
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
+            assertThat(isSideBySideSupported).isFalse()
+        }
+
+    @Test
+    fun isFoldSplitRequired() =
+        testScope.runTest {
+            val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
+            assertThat(isFoldSplitRequired).isTrue()
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
+            assertThat(isFoldSplitRequired).isFalse()
+
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pattern)
+            assertThat(isFoldSplitRequired).isTrue()
+        }
+
+    private fun authMethodsToTest(): List<AuthenticationMethodModel> {
+        return listOf(None, Pin, Password, Pattern, Sim)
+    }
+
+    companion object {
+        private val WRONG_PIN = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
new file mode 100644
index 0000000..f58bbc3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModelTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.fakeSceneDataSource
+import com.android.systemui.testKosmos
+import com.android.systemui.truth.containsEntriesExactly
+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)
+@EnableSceneContainer
+class BouncerUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private lateinit var underTest: BouncerUserActionsViewModel
+
+    @Before
+    fun setUp() {
+        kosmos.sceneContainerStartable.start()
+        underTest = kosmos.bouncerUserActionsViewModel
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun actions() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings)
+            runCurrent()
+
+            kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer)
+            runCurrent()
+
+            assertThat(actions)
+                .containsEntriesExactly(
+                    Back to UserActionResult(Scenes.QuickSettings),
+                    Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
+                )
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
deleted file mode 100644
index ccddc9c..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ /dev/null
@@ -1,228 +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.bouncer.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
-import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.domain.interactor.authenticationInteractor
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.None
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Password
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pattern
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.startable.sceneContainerStartable
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.fakeSceneDataSource
-import com.android.systemui.testKosmos
-import com.android.systemui.truth.containsEntriesExactly
-import com.google.common.truth.Truth.assertThat
-import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.emptyFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.currentTime
-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)
-@EnableSceneContainer
-class BouncerViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private lateinit var underTest: BouncerViewModel
-
-    @Before
-    fun setUp() {
-        kosmos.sceneContainerStartable.start()
-        underTest = kosmos.bouncerViewModel
-    }
-
-    @Test
-    fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
-        testScope.runTest {
-            var authMethodViewModel: AuthMethodBouncerViewModel? = null
-
-            authMethodsToTest().forEach { authMethod ->
-                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-                val job =
-                    underTest.authMethodViewModel.onEach { authMethodViewModel = it }.launchIn(this)
-                runCurrent()
-
-                if (authMethod.isSecure) {
-                    assertWithMessage("View-model unexpectedly null for auth method $authMethod")
-                        .that(authMethodViewModel)
-                        .isNotNull()
-                } else {
-                    assertWithMessage(
-                            "View-model unexpectedly non-null for auth method $authMethod"
-                        )
-                        .that(authMethodViewModel)
-                        .isNull()
-                }
-
-                job.cancel()
-            }
-        }
-
-    @Test
-    fun authMethodChanged_doesNotReuseInstances() =
-        testScope.runTest {
-            val seen = mutableMapOf<AuthenticationMethodModel, AuthMethodBouncerViewModel>()
-            val authMethodViewModel: AuthMethodBouncerViewModel? by
-                collectLastValue(underTest.authMethodViewModel)
-
-            // First pass, populate our "seen" map:
-            authMethodsToTest().forEach { authMethod ->
-                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-                authMethodViewModel?.let { seen[authMethod] = it }
-            }
-
-            // Second pass, assert same instances are not reused:
-            authMethodsToTest().forEach { authMethod ->
-                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-                authMethodViewModel?.let {
-                    assertThat(it.authenticationMethod).isEqualTo(authMethod)
-                    assertThat(it).isNotSameInstanceAs(seen[authMethod])
-                }
-            }
-        }
-
-    @Test
-    fun authMethodUnchanged_reusesInstances() =
-        testScope.runTest {
-            authMethodsToTest().forEach { authMethod ->
-                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-                val firstInstance: AuthMethodBouncerViewModel? =
-                    collectLastValue(underTest.authMethodViewModel).invoke()
-
-                kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-                val secondInstance: AuthMethodBouncerViewModel? =
-                    collectLastValue(underTest.authMethodViewModel).invoke()
-
-                firstInstance?.let { assertThat(it.authenticationMethod).isEqualTo(authMethod) }
-                assertThat(secondInstance).isSameInstanceAs(firstInstance)
-            }
-        }
-
-    @Test
-    fun authMethodsToTest_returnsCompleteSampleOfAllAuthMethodTypes() {
-        assertThat(authMethodsToTest().map { it::class }.toSet())
-            .isEqualTo(AuthenticationMethodModel::class.sealedSubclasses.toSet())
-    }
-
-    @Test
-    fun isInputEnabled() =
-        testScope.runTest {
-            val isInputEnabled by
-                collectLastValue(
-                    underTest.authMethodViewModel.flatMapLatest { authViewModel ->
-                        authViewModel?.isInputEnabled ?: emptyFlow()
-                    }
-                )
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isInputEnabled).isTrue()
-
-            repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
-                kosmos.bouncerInteractor.authenticate(WRONG_PIN)
-            }
-            assertThat(isInputEnabled).isFalse()
-
-            val lockoutEndMs = kosmos.authenticationInteractor.lockoutEndTimestamp ?: 0
-            advanceTimeBy(lockoutEndMs - testScope.currentTime)
-            assertThat(isInputEnabled).isTrue()
-        }
-
-    @Test
-    fun isSideBySideSupported() =
-        testScope.runTest {
-            val isSideBySideSupported by collectLastValue(underTest.isSideBySideSupported)
-            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isSideBySideSupported).isTrue()
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
-            assertThat(isSideBySideSupported).isTrue()
-
-            kosmos.fakeFeatureFlagsClassic.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isSideBySideSupported).isTrue()
-
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
-            assertThat(isSideBySideSupported).isFalse()
-        }
-
-    @Test
-    fun isFoldSplitRequired() =
-        testScope.runTest {
-            val isFoldSplitRequired by collectLastValue(underTest.isFoldSplitRequired)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pin)
-            assertThat(isFoldSplitRequired).isTrue()
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Password)
-            assertThat(isFoldSplitRequired).isFalse()
-
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(Pattern)
-            assertThat(isFoldSplitRequired).isTrue()
-        }
-
-    @Test
-    fun destinationScenes() =
-        testScope.runTest {
-            val destinationScenes by collectLastValue(underTest.destinationScenes)
-            kosmos.fakeSceneDataSource.changeScene(Scenes.QuickSettings)
-            runCurrent()
-
-            kosmos.fakeSceneDataSource.changeScene(Scenes.Bouncer)
-            runCurrent()
-
-            assertThat(destinationScenes)
-                .containsEntriesExactly(
-                    Back to UserActionResult(Scenes.QuickSettings),
-                    Swipe(SwipeDirection.Down) to UserActionResult(Scenes.QuickSettings),
-                )
-        }
-
-    private fun authMethodsToTest(): List<AuthenticationMethodModel> {
-        return listOf(None, Pin, Password, Pattern, Sim)
-    }
-
-    companion object {
-        private val WRONG_PIN = FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index a09189e..492543f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.inputmethod.data.repository.fakeInputMethodRepository
 import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -44,7 +45,6 @@
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
@@ -68,12 +68,8 @@
     private val isInputEnabled = MutableStateFlow(true)
 
     private val underTest =
-        PasswordBouncerViewModel(
-            viewModelScope = testScope.backgroundScope,
-            isInputEnabled = isInputEnabled.asStateFlow(),
-            interactor = bouncerInteractor,
-            inputMethodInteractor = inputMethodInteractor,
-            selectedUserInteractor = selectedUserInteractor,
+        kosmos.passwordBouncerViewModelFactory.create(
+            isInputEnabled = isInputEnabled,
             onIntentionalUserInput = {},
         )
 
@@ -81,6 +77,7 @@
     fun setUp() {
         overrideResource(R.string.keyguard_enter_your_password, ENTER_YOUR_PASSWORD)
         overrideResource(R.string.kg_wrong_password, WRONG_PASSWORD)
+        underTest.activateIn(testScope)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index 14d3634..7c773a9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -26,9 +26,9 @@
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate as Point
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 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.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -54,17 +54,12 @@
     private val testScope = kosmos.testScope
     private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
-    private val bouncerViewModel by lazy { kosmos.bouncerViewModel }
-    private val underTest by lazy {
-        PatternBouncerViewModel(
-            applicationContext = context,
-            viewModelScope = testScope.backgroundScope,
-            interactor = bouncerInteractor,
+    private val bouncerViewModel by lazy { kosmos.bouncerSceneContentViewModel }
+    private val underTest =
+        kosmos.patternBouncerViewModelFactory.create(
             isInputEnabled = MutableStateFlow(true).asStateFlow(),
             onIntentionalUserInput = {},
         )
-    }
 
     private val containerSize = 90 // px
     private val dotSize = 30 // px
@@ -73,6 +68,7 @@
     fun setUp() {
         overrideResource(R.string.keyguard_enter_your_pattern, ENTER_YOUR_PATTERN)
         overrideResource(R.string.kg_wrong_pattern, WRONG_PATTERN)
+        underTest.activateIn(testScope)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 89bafb9..8d82e97 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -31,10 +31,9 @@
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.data.repository.fakeSimBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 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.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -44,7 +43,6 @@
 import kotlin.random.nextInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
@@ -62,24 +60,18 @@
     private val testScope = kosmos.testScope
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
-    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
-    private lateinit var underTest: PinBouncerViewModel
+    private val underTest =
+        kosmos.pinBouncerViewModelFactory.create(
+            isInputEnabled = MutableStateFlow(true),
+            onIntentionalUserInput = {},
+            authenticationMethod = AuthenticationMethodModel.Pin,
+        )
 
     @Before
     fun setUp() {
-        underTest =
-            PinBouncerViewModel(
-                applicationContext = context,
-                viewModelScope = testScope.backgroundScope,
-                interactor = bouncerInteractor,
-                isInputEnabled = MutableStateFlow(true).asStateFlow(),
-                simBouncerInteractor = kosmos.simBouncerInteractor,
-                authenticationMethod = AuthenticationMethodModel.Pin,
-                onIntentionalUserInput = {},
-            )
-
         overrideResource(R.string.keyguard_enter_your_pin, ENTER_YOUR_PIN)
         overrideResource(R.string.kg_wrong_pin, WRONG_PIN)
+        underTest.activateIn(testScope)
     }
 
     @Test
@@ -96,14 +88,10 @@
     fun simBouncerViewModel_simAreaIsVisible() =
         testScope.runTest {
             val underTest =
-                PinBouncerViewModel(
-                    applicationContext = context,
-                    viewModelScope = testScope.backgroundScope,
-                    interactor = bouncerInteractor,
-                    isInputEnabled = MutableStateFlow(true).asStateFlow(),
-                    simBouncerInteractor = kosmos.simBouncerInteractor,
-                    authenticationMethod = AuthenticationMethodModel.Sim,
+                kosmos.pinBouncerViewModelFactory.create(
+                    isInputEnabled = MutableStateFlow(true),
                     onIntentionalUserInput = {},
+                    authenticationMethod = AuthenticationMethodModel.Sim,
                 )
 
             assertThat(underTest.isSimAreaVisible).isTrue()
@@ -125,14 +113,10 @@
     fun simBouncerViewModel_autoConfirmEnabled_hintedPinLengthIsNull() =
         testScope.runTest {
             val underTest =
-                PinBouncerViewModel(
-                    applicationContext = context,
-                    viewModelScope = testScope.backgroundScope,
-                    interactor = bouncerInteractor,
-                    isInputEnabled = MutableStateFlow(true).asStateFlow(),
-                    simBouncerInteractor = kosmos.simBouncerInteractor,
-                    authenticationMethod = AuthenticationMethodModel.Sim,
+                kosmos.pinBouncerViewModelFactory.create(
+                    isInputEnabled = MutableStateFlow(true),
                     onIntentionalUserInput = {},
+                    authenticationMethod = AuthenticationMethodModel.Pin,
                 )
             kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
             val hintedPinLength by collectLastValue(underTest.hintedPinLength)
@@ -355,6 +339,7 @@
                 AuthenticationMethodModel.Pin
             )
             kosmos.fakeAuthenticationRepository.setAutoConfirmFeatureEnabled(true)
+            runCurrent()
 
             underTest.onPinButtonClicked(1)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/camera/CameraIntentsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/camera/CameraIntentsTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
new file mode 100644
index 0000000..53d82d7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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.classifier;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.view.MotionEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+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;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BrightLineFalsingManagerTest extends SysuiTestCase {
+    private BrightLineFalsingManager mBrightLineFalsingManager;
+    @Mock
+    private FalsingDataProvider mFalsingDataProvider;
+    private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+    private final Set<FalsingClassifier> mClassifiers = new HashSet<>();
+    @Mock
+    private SingleTapClassifier mSingleTapClassifier;
+    @Mock
+    private LongTapClassifier mLongTapClassifier;
+    @Mock
+    private DoubleTapClassifier mDoubleTapClassifier;
+    @Mock
+    private FalsingClassifier mClassifierA;
+    private final List<MotionEvent> mMotionEventList = new ArrayList<>();
+    @Mock
+    private HistoryTracker mHistoryTracker;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+    @Captor
+    private ArgumentCaptor<FalsingDataProvider.SessionListener> mSessionListenerArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<HistoryTracker.BeliefListener> mBeliefListenerArgumentCaptor;
+
+    private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
+    private final FalsingClassifier.Result mFalsedResult =
+            FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble()))
+                .thenReturn(mFalsedResult);
+        when(mSingleTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
+        when(mLongTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
+        when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
+                .thenReturn(mFalsedResult);
+        mClassifiers.add(mClassifierA);
+        when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
+        mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
+                mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
+                mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
+                mAccessibilityManager, false);
+    }
+
+    @Test
+    public void testA11yDisablesGesture() {
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+    }
+
+    @Test
+    public void testA11yDisablesTap() {
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
+    }
+
+
+    @Test
+    public void testA11yDisablesLongTap() {
+        assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isTrue();
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isFalse();
+    }
+
+    @Test
+    public void testA11yDisablesDoubleTap() {
+        assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue();
+        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
+    }
+
+    @Test
+    public void testIsProxNear_noProxEvents_defaultsToFalse() {
+        assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
+    }
+
+    @Test
+    public void testIsProxNear_receivesNearEvent() {
+        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
+            @Override
+            public boolean getCovered() {
+                return true;
+            }
+
+            @Override
+            public long getTimestampNs() {
+                return 0;
+            }
+        });
+        assertThat(mBrightLineFalsingManager.isProximityNear()).isTrue();
+    }
+
+    @Test
+    public void testIsProxNear_receivesNearAndThenFarEvent() {
+        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
+            @Override
+            public boolean getCovered() {
+                return true;
+            }
+
+            @Override
+            public long getTimestampNs() {
+                return 0;
+            }
+        });
+        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
+            @Override
+            public boolean getCovered() {
+                return false;
+            }
+
+            @Override
+            public long getTimestampNs() {
+                return 5;
+            }
+        });
+        assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
+    }
+
+    @Test
+    public void testA11yAction() {
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
+        when(mFalsingDataProvider.isA11yAction()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
+    }
+
+    @Test
+    public void testSkipUnfolded() {
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        when(mFalsingDataProvider.isUnfolded()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+    }
+
+    @Test
+    public void testTrackpadGesture() {
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+        when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
+        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+    }
+
+    @Test
+    public void testAddAndRemoveFalsingBeliefListener() {
+        verify(mHistoryTracker, never()).addBeliefListener(any());
+
+        // Session started
+        final FalsingDataProvider.SessionListener sessionListener = captureSessionListener();
+        sessionListener.onSessionStarted();
+
+        // Verify belief listener added when session started
+        verify(mHistoryTracker).addBeliefListener(mBeliefListenerArgumentCaptor.capture());
+        verify(mHistoryTracker, never()).removeBeliefListener(any());
+
+        // Session ended
+        sessionListener.onSessionEnded();
+
+        // Verify belief listener removed when session ended
+        verify(mHistoryTracker).removeBeliefListener(mBeliefListenerArgumentCaptor.getValue());
+    }
+
+    private FalsingDataProvider.SessionListener captureSessionListener() {
+        verify(mFalsingDataProvider).addSessionListener(mSessionListenerArgumentCaptor.capture());
+        return mSessionListenerArgumentCaptor.getValue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/DiagonalClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingA11yDelegateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/HistoryTrackerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/HistoryTrackerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ProximityClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/ProximityClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/TimeLimitedInputEventBufferTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/TypeClassifierTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/TypeClassifierTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardImageLoaderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayUtilsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/common/coroutine/CoroutineResultTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageInstallerMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageInstallerMonitorTest.kt
index 5556b04..4c908dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageInstallerMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/data/repository/PackageInstallerMonitorTest.kt
@@ -140,6 +140,41 @@
         }
 
     @Test
+    fun installSessions_ignoreNullPackageNameSessions() =
+        testScope.runTest {
+            val nullPackageSession =
+                SessionInfo().apply {
+                    sessionId = 1
+                    appPackageName = null
+                    appIcon = icon1
+                }
+            val wellFormedSession =
+                SessionInfo().apply {
+                    sessionId = 2
+                    appPackageName = "pkg_name"
+                    appIcon = icon2
+                }
+
+            defaultSessions = listOf(nullPackageSession, wellFormedSession)
+
+            whenever(packageInstaller.allSessions).thenReturn(defaultSessions)
+            whenever(packageInstaller.getSessionInfo(1)).thenReturn(nullPackageSession)
+            whenever(packageInstaller.getSessionInfo(2)).thenReturn(wellFormedSession)
+
+            val packageInstallerMonitor =
+                PackageInstallerMonitor(
+                    handler,
+                    kosmos.applicationCoroutineScope,
+                    logcatLogBuffer("PackageInstallerRepositoryImplTest"),
+                    packageInstaller,
+                )
+
+            val sessions by
+                testScope.collectLastValue(packageInstallerMonitor.installSessionsForPrimaryUser)
+            assertThat(sessions?.size).isEqualTo(1)
+        }
+
+    @Test
     fun installSessions_newSessionsAreAdded() =
         testScope.runTest {
             val installSessions by collectLastValue(underTest.installSessionsForPrimaryUser)
@@ -177,7 +212,7 @@
                 }
 
             // Session 1 finished successfully
-            callback.onFinished(1, /* success = */ true)
+            callback.onFinished(1, /* success= */ true)
             runCurrent()
 
             // Verify flow updated with session 1 removed
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryTest.kt
new file mode 100644
index 0000000..81cdce4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryTest.kt
@@ -0,0 +1,254 @@
+/*
+ * 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.common.usagestats.data.repository
+
+import android.app.usage.UsageEvents
+import android.app.usage.UsageEventsQuery
+import android.app.usage.UsageStatsManager
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+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
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UsageStatsRepositoryTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val fakeUsageStatsManager = FakeUsageStatsManager()
+
+    private val usageStatsManager =
+        mock<UsageStatsManager> {
+            on { queryEvents(any()) } doAnswer
+                { inv ->
+                    val query = inv.getArgument(0) as UsageEventsQuery
+                    fakeUsageStatsManager.queryEvents(query)
+                }
+        }
+
+    private val underTest by lazy {
+        UsageStatsRepositoryImpl(
+            bgContext = kosmos.backgroundCoroutineContext,
+            usageStatsManager = usageStatsManager,
+        )
+    }
+
+    @Test
+    fun testQueryWithBeginAndEndTime() =
+        testScope.runTest {
+            with(fakeUsageStatsManager) {
+                // This event is outside the queried time, and therefore should
+                // not be returned.
+                addEvent(
+                    type = UsageEvents.Event.ACTIVITY_RESUMED,
+                    timestamp = 5,
+                    instanceId = 1,
+                )
+                addEvent(
+                    type = UsageEvents.Event.ACTIVITY_PAUSED,
+                    timestamp = 10,
+                    instanceId = 1,
+                )
+                addEvent(
+                    type = UsageEvents.Event.ACTIVITY_STOPPED,
+                    timestamp = 20,
+                    instanceId = 2,
+                )
+                // This event is outside the queried time, and therefore should
+                // not be returned.
+                addEvent(
+                    type = UsageEvents.Event.ACTIVITY_DESTROYED,
+                    timestamp = 50,
+                    instanceId = 2,
+                )
+            }
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        UsageStatsQuery(MAIN_USER, startTime = 10, endTime = 50),
+                    ),
+                )
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 1,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.PAUSED,
+                        timestamp = 10,
+                    ),
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.STOPPED,
+                        timestamp = 20,
+                    ),
+                )
+        }
+
+    @Test
+    fun testQueryForDifferentUsers() =
+        testScope.runTest {
+            with(fakeUsageStatsManager) {
+                addEvent(
+                    user = MAIN_USER,
+                    type = UsageEvents.Event.ACTIVITY_PAUSED,
+                    timestamp = 10,
+                    instanceId = 1,
+                )
+                addEvent(
+                    user = SECONDARY_USER,
+                    type = UsageEvents.Event.ACTIVITY_RESUMED,
+                    timestamp = 11,
+                    instanceId = 2,
+                )
+            }
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        UsageStatsQuery(MAIN_USER, startTime = 10, endTime = 15),
+                    ),
+                )
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 1,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.PAUSED,
+                        timestamp = 10,
+                    ),
+                )
+        }
+
+    @Test
+    fun testQueryForSpecificPackages() =
+        testScope.runTest {
+            with(fakeUsageStatsManager) {
+                addEvent(
+                    packageName = DEFAULT_PACKAGE,
+                    type = UsageEvents.Event.ACTIVITY_PAUSED,
+                    timestamp = 10,
+                    instanceId = 1,
+                )
+                addEvent(
+                    packageName = OTHER_PACKAGE,
+                    type = UsageEvents.Event.ACTIVITY_RESUMED,
+                    timestamp = 11,
+                    instanceId = 2,
+                )
+            }
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        UsageStatsQuery(
+                            MAIN_USER,
+                            startTime = 10,
+                            endTime = 10000,
+                            packageNames = listOf(OTHER_PACKAGE),
+                        ),
+                    ),
+                )
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = OTHER_PACKAGE,
+                        lifecycle = Lifecycle.RESUMED,
+                        timestamp = 11,
+                    ),
+                )
+        }
+
+    @Test
+    fun testNonActivityEvent() =
+        testScope.runTest {
+            with(fakeUsageStatsManager) {
+                addEvent(
+                    type = UsageEvents.Event.CHOOSER_ACTION,
+                    timestamp = 10,
+                    instanceId = 1,
+                )
+            }
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        UsageStatsQuery(
+                            MAIN_USER,
+                            startTime = 1,
+                            endTime = 20,
+                        ),
+                    ),
+                )
+                .isEmpty()
+        }
+
+    private class FakeUsageStatsManager() {
+        private val events = mutableMapOf<Int, MutableList<UsageEvents.Event>>()
+
+        fun queryEvents(query: UsageEventsQuery): UsageEvents {
+            val results =
+                events
+                    .getOrDefault(query.userId, emptyList())
+                    .filter { event ->
+                        query.packageNames.isEmpty() ||
+                            query.packageNames.contains(event.packageName)
+                    }
+                    .filter { event ->
+                        event.timeStamp in query.beginTimeMillis until query.endTimeMillis
+                    }
+                    .filter { event ->
+                        query.eventTypes.isEmpty() || query.eventTypes.contains(event.eventType)
+                    }
+            return UsageEvents(results, emptyArray())
+        }
+
+        fun addEvent(
+            type: Int,
+            instanceId: Int = 0,
+            user: UserHandle = MAIN_USER,
+            packageName: String = DEFAULT_PACKAGE,
+            timestamp: Long,
+        ) {
+            events
+                .getOrPut(user.identifier) { mutableListOf() }
+                .add(
+                    UsageEvents.Event(type, timestamp).apply {
+                        mPackage = packageName
+                        mInstanceId = instanceId
+                    }
+                )
+        }
+    }
+
+    private companion object {
+        const val DEFAULT_PACKAGE = "pkg.default"
+        const val OTHER_PACKAGE = "pkg.other"
+        val MAIN_USER: UserHandle = UserHandle.of(0)
+        val SECONDARY_USER: UserHandle = UserHandle.of(1)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorTest.kt
new file mode 100644
index 0000000..af45588
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorTest.kt
@@ -0,0 +1,269 @@
+/*
+ * 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.common.usagestats.domain.interactor
+
+import android.annotation.CurrentTimeMillisLong
+import android.app.usage.UsageEvents
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.usagestats.data.repository.fakeUsageStatsRepository
+import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
+import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
+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 UsageStatsInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val userTracker = kosmos.fakeUserTracker
+    private val systemClock = kosmos.fakeSystemClock
+    private val repository = kosmos.fakeUsageStatsRepository
+
+    private val underTest = kosmos.usageStatsInteractor
+
+    @Before
+    fun setUp() {
+        userTracker.set(listOf(MAIN_USER, SECONDARY_USER), 0)
+    }
+
+    @Test
+    fun testQueryWithBeginAndEndTime() =
+        testScope.runTest {
+            // This event is outside the queried time, and therefore should
+            // not be returned.
+            addEvent(
+                instanceId = 1,
+                type = UsageEvents.Event.ACTIVITY_RESUMED,
+                timestamp = 5,
+            )
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_PAUSED,
+                timestamp = 10,
+                instanceId = 1,
+            )
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_STOPPED,
+                timestamp = 20,
+                instanceId = 2,
+            )
+            // This event is outside the queried time, and therefore should
+            // not be returned.
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_DESTROYED,
+                timestamp = 50,
+                instanceId = 2,
+            )
+
+            assertThat(underTest.queryActivityEvents(startTime = 10, endTime = 50))
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 1,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.PAUSED,
+                        timestamp = 10,
+                    ),
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.STOPPED,
+                        timestamp = 20,
+                    ),
+                )
+        }
+
+    @Test
+    fun testQueryForDifferentUsers() =
+        testScope.runTest {
+            addEvent(
+                user = MAIN_USER.userHandle,
+                type = UsageEvents.Event.ACTIVITY_PAUSED,
+                timestamp = 10,
+                instanceId = 1,
+            )
+            addEvent(
+                user = SECONDARY_USER.userHandle,
+                type = UsageEvents.Event.ACTIVITY_RESUMED,
+                timestamp = 11,
+                instanceId = 2,
+            )
+
+            assertThat(underTest.queryActivityEvents(startTime = 10, endTime = 15))
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 1,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.PAUSED,
+                        timestamp = 10,
+                    ),
+                )
+        }
+
+    @Test
+    fun testQueryWithUserSpecified() =
+        testScope.runTest {
+            addEvent(
+                user = MAIN_USER.userHandle,
+                type = UsageEvents.Event.ACTIVITY_PAUSED,
+                timestamp = 10,
+                instanceId = 1,
+            )
+            addEvent(
+                user = SECONDARY_USER.userHandle,
+                type = UsageEvents.Event.ACTIVITY_RESUMED,
+                timestamp = 11,
+                instanceId = 2,
+            )
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        startTime = 10,
+                        endTime = 15,
+                        userHandle = SECONDARY_USER.userHandle,
+                    ),
+                )
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.RESUMED,
+                        timestamp = 11,
+                    ),
+                )
+        }
+
+    @Test
+    fun testQueryForSpecificPackages() =
+        testScope.runTest {
+            addEvent(
+                packageName = DEFAULT_PACKAGE,
+                type = UsageEvents.Event.ACTIVITY_PAUSED,
+                timestamp = 10,
+                instanceId = 1,
+            )
+            addEvent(
+                packageName = OTHER_PACKAGE,
+                type = UsageEvents.Event.ACTIVITY_RESUMED,
+                timestamp = 11,
+                instanceId = 2,
+            )
+
+            assertThat(
+                    underTest.queryActivityEvents(
+                        startTime = 10,
+                        endTime = 10000,
+                        packageNames = listOf(OTHER_PACKAGE),
+                    ),
+                )
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = OTHER_PACKAGE,
+                        lifecycle = Lifecycle.RESUMED,
+                        timestamp = 11,
+                    ),
+                )
+        }
+
+    @Test
+    fun testNonActivityEvent() =
+        testScope.runTest {
+            addEvent(
+                type = UsageEvents.Event.CHOOSER_ACTION,
+                timestamp = 10,
+                instanceId = 1,
+            )
+
+            assertThat(underTest.queryActivityEvents(startTime = 1, endTime = 20)).isEmpty()
+        }
+
+    @Test
+    fun testNoEndTimeSpecified() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(30)
+
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_PAUSED,
+                timestamp = 10,
+                instanceId = 1,
+            )
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_STOPPED,
+                timestamp = 20,
+                instanceId = 2,
+            )
+            // This event is outside the queried time, and therefore should
+            // not be returned.
+            addEvent(
+                type = UsageEvents.Event.ACTIVITY_DESTROYED,
+                timestamp = 50,
+                instanceId = 2,
+            )
+
+            assertThat(underTest.queryActivityEvents(startTime = 1))
+                .containsExactly(
+                    ActivityEventModel(
+                        instanceId = 1,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.PAUSED,
+                        timestamp = 10,
+                    ),
+                    ActivityEventModel(
+                        instanceId = 2,
+                        packageName = DEFAULT_PACKAGE,
+                        lifecycle = Lifecycle.STOPPED,
+                        timestamp = 20,
+                    ),
+                )
+        }
+
+    private fun addEvent(
+        instanceId: Int,
+        user: UserHandle = MAIN_USER.userHandle,
+        packageName: String = DEFAULT_PACKAGE,
+        @UsageEvents.Event.EventType type: Int,
+        @CurrentTimeMillisLong timestamp: Long,
+    ) {
+        repository.addEvent(
+            instanceId = instanceId,
+            user = user,
+            packageName = packageName,
+            type = type,
+            timestamp = timestamp,
+        )
+    }
+
+    private companion object {
+        const val DEFAULT_PACKAGE = "pkg.default"
+        const val OTHER_PACKAGE = "pkg.other"
+        val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+        val SECONDARY_USER: UserInfo = UserInfo(10, "secondary", 0)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
index 76920e4..3b0057d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalDreamStartableTest.kt
@@ -81,6 +81,7 @@
     @Test
     fun startDreamWhenTransitioningToHub() =
         testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
             keyguardRepository.setDreaming(false)
             powerRepository.setScreenPowerState(ScreenPowerState.SCREEN_ON)
             whenever(dreamManager.canStartDreaming(/* isScreenOn= */ true)).thenReturn(true)
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 9ccf99b..70529cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -112,7 +112,7 @@
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 communalSceneInteractor.setEditModeState(EditModeState.STARTING)
@@ -133,7 +133,7 @@
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 communalSceneInteractor.setIsLaunchingWidget(true)
@@ -154,7 +154,7 @@
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 communalSceneInteractor.setIsLaunchingWidget(false)
@@ -174,7 +174,7 @@
         with(kosmos) {
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 communalInteractor.setEditModeOpen(true)
@@ -213,7 +213,7 @@
             testScope.runTest {
                 whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(false)
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 updateDocked(true)
@@ -233,7 +233,7 @@
             testScope.runTest {
                 whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(true)
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 updateDocked(true)
@@ -270,7 +270,7 @@
         with(kosmos) {
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -292,7 +292,7 @@
         with(kosmos) {
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -320,7 +320,7 @@
     fun dockingOnLockscreen_forcesCommunal() =
         with(kosmos) {
             testScope.runTest {
-                communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
                 // device is docked while on the lockscreen
@@ -342,7 +342,7 @@
     fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
         with(kosmos) {
             testScope.runTest {
-                communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
                 // device is docked while on the lockscreen
@@ -374,7 +374,7 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -391,7 +391,7 @@
             testScope.runTest {
                 // Device is not dreaming and on communal.
                 updateDreaming(false)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 // Scene stays as Communal
                 advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
@@ -406,7 +406,7 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -429,7 +429,7 @@
             testScope.runTest {
                 // Device is on communal, but not dreaming.
                 updateDreaming(false)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -450,7 +450,7 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is on communal.
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 // Device stays on the hub after the timeout since we're not dreaming.
                 advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
@@ -471,7 +471,7 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -500,7 +500,7 @@
 
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
 
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
@@ -520,7 +520,7 @@
         with(kosmos) {
             testScope.runTest {
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
-                communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
                 assertThat(scene).isEqualTo(CommunalScenes.Blank)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
new file mode 100644
index 0000000..edc8c83
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.data.backup
+
+import androidx.room.Room
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.db.CommunalDatabase
+import com.android.systemui.communal.data.db.CommunalWidgetDao
+import com.android.systemui.communal.nano.CommunalHubState
+import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.google.common.truth.Correspondence
+import com.google.common.truth.Truth.assertThat
+import java.io.FileNotFoundException
+import java.nio.charset.Charset
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalBackupUtilsTest : SysuiTestCase() {
+    @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule()
+
+    private lateinit var database: CommunalDatabase
+    private lateinit var dao: CommunalWidgetDao
+    private lateinit var underTest: CommunalBackupUtils
+
+    @Before
+    fun setup() {
+        database =
+            Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
+                .allowMainThreadQueries()
+                .build()
+        CommunalDatabase.setInstance(database)
+
+        dao = database.communalWidgetDao()
+        underTest = CommunalBackupUtils(context)
+    }
+
+    @After
+    fun teardown() {
+        database.close()
+        underTest.clear()
+    }
+
+    @Test
+    fun getCommunalHubState_returnsExpectedWidgets() {
+        // Set up database
+        val expectedWidgets =
+            listOf(
+                FakeWidgetMetadata(
+                    widgetId = 11,
+                    componentName = "com.android.fakePackage1/fakeWidget1",
+                    rank = 0,
+                    userSerialNumber = 0,
+                ),
+                FakeWidgetMetadata(
+                    widgetId = 12,
+                    componentName = "com.android.fakePackage2/fakeWidget2",
+                    rank = 1,
+                    userSerialNumber = 0,
+                ),
+                FakeWidgetMetadata(
+                    widgetId = 13,
+                    componentName = "com.android.fakePackage3/fakeWidget3",
+                    rank = 2,
+                    userSerialNumber = 10,
+                ),
+            )
+        expectedWidgets.forEach {
+            dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber)
+        }
+
+        // Get communal hub state
+        val state = underTest.getCommunalHubState()
+        val actualWidgets = state.widgets.toList()
+
+        // Verify the state contains widgets as expected
+        assertThat(actualWidgets)
+            .comparingElementsUsing(represents)
+            .containsExactlyElementsIn(expectedWidgets)
+    }
+
+    @Test
+    fun write_existingContentIsOverwritten() {
+        // Write old data
+        val dataToWrite = "I am old data. Erase me."
+        underTest.writeBytesToDisk(dataToWrite.toByteArray(Charset.defaultCharset()))
+
+        // Verify old data written
+        var dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset())
+        assertThat(dataRead).isEqualTo(dataToWrite)
+
+        // Write new data
+        val newDataToWrite = "I am new data."
+        underTest.writeBytesToDisk(newDataToWrite.toByteArray(Charset.defaultCharset()))
+
+        // Verify new data overwrites old
+        dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset())
+        assertThat(dataRead).isEqualTo(newDataToWrite)
+    }
+
+    @Test(expected = FileNotFoundException::class)
+    fun clear_returnsTrueWhenFileDeleted() {
+        // Write bytes to disk
+        underTest.writeBytesToDisk(byteArrayOf(1, 2, 3))
+
+        assertThat(underTest.clear()).isTrue()
+
+        // Verify a read after that throws a FileNotFoundException
+        underTest.readBytesFromDisk()
+    }
+
+    @Test
+    fun clear_returnsFalseWhenFileDoesNotExist() {
+        assertThat(underTest.clear()).isFalse()
+    }
+
+    @Test
+    fun fileExists() {
+        assertThat(underTest.fileExists()).isFalse()
+
+        underTest.writeBytesToDisk(byteArrayOf(1, 2, 3))
+        assertThat(underTest.fileExists()).isTrue()
+
+        underTest.clear()
+        assertThat(underTest.fileExists()).isFalse()
+    }
+
+    data class FakeWidgetMetadata(
+        val widgetId: Int,
+        val componentName: String,
+        val rank: Int,
+        val userSerialNumber: Int,
+    )
+
+    companion object {
+        /**
+         * A comparator for whether a [CommunalHubState.CommunalWidgetItem] represents a
+         * [FakeWidgetMetadata]
+         */
+        val represents: Correspondence<CommunalHubState.CommunalWidgetItem, FakeWidgetMetadata> =
+            Correspondence.from(
+                { actual, expected ->
+                    actual?.widgetId == expected?.widgetId &&
+                        actual?.componentName == expected?.componentName &&
+                        actual?.rank == expected?.rank &&
+                        actual?.userSerialNumber == expected?.userSerialNumber
+                },
+                "represents",
+            )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
new file mode 100644
index 0000000..d4d966a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.data.db
+
+import android.content.ComponentName
+import androidx.room.Room
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.nano.CommunalHubState
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.google.common.truth.Truth.assertThat
+import java.io.IOException
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+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.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalWidgetDaoTest : SysuiTestCase() {
+    @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule()
+
+    private lateinit var db: CommunalDatabase
+    private lateinit var communalWidgetDao: CommunalWidgetDao
+
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+
+    @Before
+    @Throws(IOException::class)
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        db =
+            Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
+                .allowMainThreadQueries()
+                .build()
+        communalWidgetDao = db.communalWidgetDao()
+    }
+
+    @After
+    @Throws(IOException::class)
+    fun teardown() {
+        db.close()
+    }
+
+    @Test
+    fun addWidget_readValueInDb() =
+        testScope.runTest {
+            val (widgetId, provider, rank, userSerialNumber) = widgetInfo1
+            communalWidgetDao.addWidget(
+                widgetId = widgetId,
+                provider = provider,
+                rank = rank,
+                userSerialNumber = userSerialNumber,
+            )
+            val entry = communalWidgetDao.getWidgetByIdNow(id = 1)
+            assertThat(entry).isEqualTo(communalWidgetItemEntry1)
+        }
+
+    @Test
+    fun deleteWidget_notInDb_returnsFalse() =
+        testScope.runTest {
+            val (widgetId, provider, rank, userSerialNumber) = widgetInfo1
+            communalWidgetDao.addWidget(
+                widgetId = widgetId,
+                provider = provider,
+                rank = rank,
+                userSerialNumber = userSerialNumber,
+            )
+            assertThat(communalWidgetDao.deleteWidgetById(widgetId = 123)).isFalse()
+        }
+
+    @Test
+    fun addWidget_emitsActiveWidgetsInDb(): Unit =
+        testScope.runTest {
+            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+            widgetsToAdd.forEach {
+                val (widgetId, provider, rank, userSerialNumber) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    rank = rank,
+                    userSerialNumber = userSerialNumber
+                )
+            }
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1,
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2
+                )
+        }
+
+    @Test
+    fun addWidget_rankNotSpecified_widgetAddedAtTheEnd(): Unit =
+        testScope.runTest {
+            val widgets by collectLastValue(communalWidgetDao.getWidgets())
+
+            // Verify database is empty
+            assertThat(widgets).isEmpty()
+
+            // Add widgets one by one without specifying rank
+            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2, widgetInfo3)
+            widgetsToAdd.forEach {
+                val (widgetId, provider, _, userSerialNumber) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    userSerialNumber = userSerialNumber
+                )
+            }
+
+            // Verify new each widget is added at the end
+            assertThat(widgets)
+                .containsExactly(
+                    communalItemRankEntry1,
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry3,
+                    communalWidgetItemEntry3,
+                )
+        }
+
+    @Test
+    fun deleteWidget_emitsActiveWidgetsInDb() =
+        testScope.runTest {
+            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+            widgetsToAdd.forEach {
+                val (widgetId, provider, rank, userSerialNumber) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    rank = rank,
+                    userSerialNumber = userSerialNumber,
+                )
+            }
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1,
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2
+                )
+
+            communalWidgetDao.deleteWidgetById(communalWidgetItemEntry1.widgetId)
+            assertThat(widgets()).containsExactly(communalItemRankEntry2, communalWidgetItemEntry2)
+        }
+
+    @Test
+    fun reorderWidget_emitsWidgetsInNewOrder() =
+        testScope.runTest {
+            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+            widgetsToAdd.forEach {
+                val (widgetId, provider, rank, userSerialNumber) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    rank = rank,
+                    userSerialNumber = userSerialNumber,
+                )
+            }
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1,
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2,
+                )
+                .inOrder()
+
+            // swapped ranks
+            val widgetIdsToRankMap = mapOf(widgetInfo1.widgetId to 1, widgetInfo2.widgetId to 0)
+            communalWidgetDao.updateWidgetOrder(widgetIdsToRankMap)
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry2.copy(rank = 0),
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry1.copy(rank = 1),
+                    communalWidgetItemEntry1,
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun addNewWidgetWithReorder_emitsWidgetsInNewOrder() =
+        testScope.runTest {
+            val existingWidgets = listOf(widgetInfo1, widgetInfo2, widgetInfo3)
+            val widgets = collectLastValue(communalWidgetDao.getWidgets())
+
+            existingWidgets.forEach {
+                val (widgetId, provider, rank, userSerialNumber) = it
+                communalWidgetDao.addWidget(
+                    widgetId = widgetId,
+                    provider = provider,
+                    rank = rank,
+                    userSerialNumber = userSerialNumber,
+                )
+            }
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1,
+                    communalWidgetItemEntry1,
+                    communalItemRankEntry2,
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry3,
+                    communalWidgetItemEntry3,
+                )
+                .inOrder()
+
+            // add a new widget at rank 1.
+            communalWidgetDao.addWidget(
+                widgetId = 4,
+                provider = ComponentName("pk_name", "cls_name_4"),
+                rank = 1,
+                userSerialNumber = 0,
+            )
+
+            val newRankEntry = CommunalItemRank(uid = 4L, rank = 1)
+            val newWidgetEntry =
+                CommunalWidgetItem(
+                    uid = 4L,
+                    widgetId = 4,
+                    componentName = "pk_name/cls_name_4",
+                    itemId = 4L,
+                    userSerialNumber = 0,
+                )
+            assertThat(widgets())
+                .containsExactly(
+                    communalItemRankEntry1.copy(rank = 0),
+                    communalWidgetItemEntry1,
+                    newRankEntry,
+                    newWidgetEntry,
+                    communalItemRankEntry2.copy(rank = 2),
+                    communalWidgetItemEntry2,
+                    communalItemRankEntry3.copy(rank = 3),
+                    communalWidgetItemEntry3,
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun restoreCommunalHubState() =
+        testScope.runTest {
+            // Set up db
+            listOf(widgetInfo1, widgetInfo2, widgetInfo3).forEach { addWidget(it) }
+
+            // Restore db to fake state
+            communalWidgetDao.restoreCommunalHubState(fakeState)
+
+            // Verify db matches new state
+            val expected = mutableMapOf<CommunalItemRank, CommunalWidgetItem>()
+            fakeState.widgets.forEachIndexed { index, fakeWidget ->
+                // Auto-generated uid continues after the initial 3 widgets and starts at 4
+                val uid = index + 4L
+                val rank = CommunalItemRank(uid = uid, rank = fakeWidget.rank)
+                val widget =
+                    CommunalWidgetItem(
+                        uid = uid,
+                        widgetId = fakeWidget.widgetId,
+                        componentName = fakeWidget.componentName,
+                        itemId = rank.uid,
+                        userSerialNumber = fakeWidget.userSerialNumber,
+                    )
+                expected[rank] = widget
+            }
+            val widgets by collectLastValue(communalWidgetDao.getWidgets())
+            assertThat(widgets).containsExactlyEntriesIn(expected)
+        }
+
+    private fun addWidget(metadata: FakeWidgetMetadata, rank: Int? = null) {
+        communalWidgetDao.addWidget(
+            widgetId = metadata.widgetId,
+            provider = metadata.provider,
+            rank = rank ?: metadata.rank,
+            userSerialNumber = metadata.userSerialNumber,
+        )
+    }
+
+    data class FakeWidgetMetadata(
+        val widgetId: Int,
+        val provider: ComponentName,
+        val rank: Int,
+        val userSerialNumber: Int,
+    )
+
+    companion object {
+        val widgetInfo1 =
+            FakeWidgetMetadata(
+                widgetId = 1,
+                provider = ComponentName("pk_name", "cls_name_1"),
+                rank = 0,
+                userSerialNumber = 0,
+            )
+        val widgetInfo2 =
+            FakeWidgetMetadata(
+                widgetId = 2,
+                provider = ComponentName("pk_name", "cls_name_2"),
+                rank = 1,
+                userSerialNumber = 0,
+            )
+        val widgetInfo3 =
+            FakeWidgetMetadata(
+                widgetId = 3,
+                provider = ComponentName("pk_name", "cls_name_3"),
+                rank = 2,
+                userSerialNumber = 10,
+            )
+        val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.rank)
+        val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.rank)
+        val communalItemRankEntry3 = CommunalItemRank(uid = 3L, rank = widgetInfo3.rank)
+        val communalWidgetItemEntry1 =
+            CommunalWidgetItem(
+                uid = 1L,
+                widgetId = widgetInfo1.widgetId,
+                componentName = widgetInfo1.provider.flattenToString(),
+                itemId = communalItemRankEntry1.uid,
+                userSerialNumber = widgetInfo1.userSerialNumber,
+            )
+        val communalWidgetItemEntry2 =
+            CommunalWidgetItem(
+                uid = 2L,
+                widgetId = widgetInfo2.widgetId,
+                componentName = widgetInfo2.provider.flattenToString(),
+                itemId = communalItemRankEntry2.uid,
+                userSerialNumber = widgetInfo2.userSerialNumber,
+            )
+        val communalWidgetItemEntry3 =
+            CommunalWidgetItem(
+                uid = 3L,
+                widgetId = widgetInfo3.widgetId,
+                componentName = widgetInfo3.provider.flattenToString(),
+                itemId = communalItemRankEntry3.uid,
+                userSerialNumber = widgetInfo3.userSerialNumber,
+            )
+        val fakeState =
+            CommunalHubState().apply {
+                widgets =
+                    listOf(
+                            CommunalHubState.CommunalWidgetItem().apply {
+                                widgetId = 1
+                                componentName = "pk_name/fake_widget_1"
+                                rank = 1
+                                userSerialNumber = 0
+                            },
+                            CommunalHubState.CommunalWidgetItem().apply {
+                                widgetId = 2
+                                componentName = "pk_name/fake_widget_2"
+                                rank = 2
+                                userSerialNumber = 10
+                            },
+                        )
+                        .toTypedArray()
+            }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
index ad2c42f..eba395b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt
@@ -115,21 +115,21 @@
                 .addWidget(
                     widgetId = 0,
                     componentName = defaultWidgets[0],
-                    priority = 3,
+                    rank = 0,
                     userSerialNumber = 0,
                 )
             verify(communalWidgetDao)
                 .addWidget(
                     widgetId = 1,
                     componentName = defaultWidgets[1],
-                    priority = 2,
+                    rank = 1,
                     userSerialNumber = 0,
                 )
             verify(communalWidgetDao)
                 .addWidget(
                     widgetId = 2,
                     componentName = defaultWidgets[2],
-                    priority = 1,
+                    rank = 2,
                     userSerialNumber = 0,
                 )
         }
@@ -150,7 +150,7 @@
                 .addWidget(
                     widgetId = anyInt(),
                     componentName = any(),
-                    priority = anyInt(),
+                    rank = anyInt(),
                     userSerialNumber = anyInt(),
                 )
         }
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 dd28022..dd5ad17 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
@@ -67,7 +67,7 @@
         testScope.runTest {
             val mediaModel = collectLastValue(underTest.mediaModel)
             runCurrent()
-            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
         }
 
     @Test
@@ -81,16 +81,16 @@
             // Initial value is false.
             val mediaModel = collectLastValue(underTest.mediaModel)
             runCurrent()
-            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
 
             // Change to media available and notify the listener.
-            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
+            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
             whenever(mediaData.createdTimestampMillis).thenReturn(1234L)
             mediaDataListenerCaptor.firstValue.onMediaDataLoaded("key", null, mediaData)
             runCurrent()
 
             // Media active now returns true.
-            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isTrue()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
             assertThat(mediaModel()?.createdTimestampMillis).isEqualTo(1234L)
         }
 
@@ -103,20 +103,20 @@
             verify(mediaDataManager).addListener(mediaDataListenerCaptor.capture())
 
             // Change to media available and notify the listener.
-            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(true)
+            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(true)
             mediaDataListenerCaptor.firstValue.onMediaDataLoaded("key", null, mediaData)
             runCurrent()
 
             // Media active now returns true.
             val mediaModel = collectLastValue(underTest.mediaModel)
-            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isTrue()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isTrue()
 
             // Change to media unavailable and notify the listener.
-            whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
+            whenever(mediaDataManager.hasAnyMediaOrRecommendation()).thenReturn(false)
             mediaDataListenerCaptor.firstValue.onMediaDataRemoved("key", false)
             runCurrent()
 
             // Media active now returns false.
-            assertThat(mediaModel()?.hasActiveMediaOrRecommendation).isFalse()
+            assertThat(mediaModel()?.hasAnyMediaOrRecommendation).isFalse()
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
index d251585..1a426d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepositoryImplTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
 import com.android.systemui.testKosmos
 import com.android.systemui.util.time.fakeSystemClock
@@ -67,6 +68,7 @@
                 smartspaceController,
                 fakeExecutor,
                 systemClock,
+                logcatLogBuffer("CommunalSmartspaceRepositoryImplTest"),
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
index c37b33e..ae1c496 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalTutorialRepositoryImplTest.kt
@@ -29,7 +29,7 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -45,8 +45,7 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-
-    private lateinit var secureSettings: FakeSettings
+    private val secureSettings = kosmos.fakeSettings
     private lateinit var userRepository: FakeUserRepository
 
     private lateinit var underTest: CommunalTutorialRepositoryImpl
@@ -55,7 +54,6 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        secureSettings = FakeSettings()
         userRepository = FakeUserRepository()
         val listOfUserInfo = listOf(MAIN_USER_INFO)
         userRepository.setUserInfos(listOfUserInfo)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
index ca81838..980a5ec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt
@@ -154,7 +154,7 @@
                     CommunalWidgetContentModel.Available(
                         appWidgetId = communalWidgetItemEntry.widgetId,
                         providerInfo = providerInfoA,
-                        priority = communalItemRankEntry.rank,
+                        rank = communalItemRankEntry.rank,
                     )
                 )
 
@@ -190,12 +190,12 @@
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
-                        priority = 1,
+                        rank = 1,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
-                        priority = 2,
+                        rank = 2,
                     ),
                 )
         }
@@ -225,12 +225,12 @@
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
-                        priority = 1,
+                        rank = 1,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
-                        priority = 2,
+                        rank = 2,
                     ),
                 )
 
@@ -248,12 +248,12 @@
                         appWidgetId = 1,
                         // Verify that provider info updated
                         providerInfo = providerInfoC,
-                        priority = 1,
+                        rank = 1,
                     ),
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 2,
                         providerInfo = providerInfoB,
-                        priority = 2,
+                        rank = 2,
                     ),
                 )
         }
@@ -263,7 +263,7 @@
         testScope.runTest {
             val provider = ComponentName("pkg_name", "cls_name")
             val id = 1
-            val priority = 1
+            val rank = 1
             whenever(communalWidgetHost.getAppWidgetInfo(id))
                 .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
             whenever(
@@ -273,12 +273,11 @@
                     )
                 )
                 .thenReturn(id)
-            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorSuccess)
+            underTest.addWidget(provider, mainUser, rank, kosmos.widgetConfiguratorSuccess)
             runCurrent()
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
-            verify(communalWidgetDao)
-                .addWidget(id, provider, priority, testUserSerialNumber(mainUser))
+            verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser))
 
             // Verify backup requested
             verify(backupManager).dataChanged()
@@ -289,7 +288,7 @@
         testScope.runTest {
             val provider = ComponentName("pkg_name", "cls_name")
             val id = 1
-            val priority = 1
+            val rank = 1
             whenever(communalWidgetHost.getAppWidgetInfo(id))
                 .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
             whenever(
@@ -299,7 +298,7 @@
                     )
                 )
                 .thenReturn(id)
-            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorFail)
+            underTest.addWidget(provider, mainUser, rank, kosmos.widgetConfiguratorFail)
             runCurrent()
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
@@ -316,7 +315,7 @@
         testScope.runTest {
             val provider = ComponentName("pkg_name", "cls_name")
             val id = 1
-            val priority = 1
+            val rank = 1
             whenever(communalWidgetHost.getAppWidgetInfo(id))
                 .thenReturn(PROVIDER_INFO_REQUIRES_CONFIGURATION)
             whenever(
@@ -326,7 +325,7 @@
                     )
                 )
                 .thenReturn(id)
-            underTest.addWidget(provider, mainUser, priority) {
+            underTest.addWidget(provider, mainUser, rank) {
                 throw IllegalStateException("some error")
             }
             runCurrent()
@@ -345,7 +344,7 @@
         testScope.runTest {
             val provider = ComponentName("pkg_name", "cls_name")
             val id = 1
-            val priority = 1
+            val rank = 1
             whenever(communalWidgetHost.getAppWidgetInfo(id))
                 .thenReturn(PROVIDER_INFO_CONFIGURATION_OPTIONAL)
             whenever(
@@ -355,12 +354,11 @@
                     )
                 )
                 .thenReturn(id)
-            underTest.addWidget(provider, mainUser, priority, kosmos.widgetConfiguratorFail)
+            underTest.addWidget(provider, mainUser, rank, kosmos.widgetConfiguratorFail)
             runCurrent()
 
             verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser)
-            verify(communalWidgetDao)
-                .addWidget(id, provider, priority, testUserSerialNumber(mainUser))
+            verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser))
 
             // Verify backup requested
             verify(backupManager).dataChanged()
@@ -399,11 +397,11 @@
     @Test
     fun reorderWidgets_queryDb() =
         testScope.runTest {
-            val widgetIdToPriorityMap = mapOf(104 to 1, 103 to 2, 101 to 3)
-            underTest.updateWidgetOrder(widgetIdToPriorityMap)
+            val widgetIdToRankMap = mapOf(104 to 1, 103 to 2, 101 to 3)
+            underTest.updateWidgetOrder(widgetIdToRankMap)
             runCurrent()
 
-            verify(communalWidgetDao).updateWidgetOrder(widgetIdToPriorityMap)
+            verify(communalWidgetDao).updateWidgetOrder(widgetIdToRankMap)
 
             // Verify backup requested
             verify(backupManager).dataChanged()
@@ -691,11 +689,11 @@
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
-                        priority = 1,
+                        rank = 1,
                     ),
                     CommunalWidgetContentModel.Pending(
                         appWidgetId = 2,
-                        priority = 2,
+                        rank = 2,
                         componentName = ComponentName("pk_2", "cls_2"),
                         icon = fakeIcon,
                         user = mainUser,
@@ -730,7 +728,7 @@
                 .containsExactly(
                     CommunalWidgetContentModel.Pending(
                         appWidgetId = 1,
-                        priority = 1,
+                        rank = 1,
                         componentName = ComponentName("pk_1", "cls_1"),
                         icon = fakeIcon,
                         user = mainUser,
@@ -750,7 +748,7 @@
                     CommunalWidgetContentModel.Available(
                         appWidgetId = 1,
                         providerInfo = providerInfoA,
-                        priority = 1,
+                        rank = 1,
                     ),
                 )
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 0242c2d..777ddab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -68,15 +68,16 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 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.nullable
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.leaks.FakeManagedProfileController
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -122,6 +123,7 @@
     private lateinit var userTracker: FakeUserTracker
     private lateinit var activityStarter: ActivityStarter
     private lateinit var userManager: UserManager
+    private lateinit var managedProfileController: FakeManagedProfileController
 
     private lateinit var underTest: CommunalInteractor
 
@@ -143,6 +145,7 @@
         userTracker = kosmos.fakeUserTracker
         activityStarter = kosmos.activityStarter
         userManager = kosmos.userManager
+        managedProfileController = kosmos.fakeManagedProfileController
 
         whenever(mainUser.isMain).thenReturn(true)
         whenever(secondaryUser.isMain).thenReturn(false)
@@ -352,7 +355,7 @@
 
             smartspaceRepository.setTimers(targets)
 
-            val smartspaceContent by collectLastValue(underTest.getOngoingContent(true))
+            val smartspaceContent by collectLastValue(underTest.ongoingContent)
             assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
             for (index in 0 until totalTargets) {
                 assertThat(smartspaceContent?.get(index)?.size).isEqualTo(expectedSizes[index])
@@ -368,7 +371,7 @@
             // Media is playing.
             mediaRepository.mediaActive()
 
-            val umoContent by collectLastValue(underTest.getOngoingContent(true))
+            val umoContent by collectLastValue(underTest.ongoingContent)
 
             assertThat(umoContent?.size).isEqualTo(1)
             assertThat(umoContent?.get(0)).isInstanceOf(CommunalContentModel.Umo::class.java)
@@ -376,20 +379,6 @@
         }
 
     @Test
-    fun umo_mediaPlaying_doNotShowUmo() =
-        testScope.run {
-            // Tutorial completed.
-            tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
-
-            // Media is playing.
-            mediaRepository.mediaActive()
-
-            val umoContent by collectLastValue(underTest.getOngoingContent(false))
-
-            assertThat(umoContent?.size).isEqualTo(0)
-        }
-
-    @Test
     fun ongoing_shouldOrderAndSizeByTimestamp() =
         testScope.runTest {
             // Keyguard showing, and tutorial completed.
@@ -412,7 +401,7 @@
             val timer3 = smartspaceTimer("timer3", timestamp = 4L)
             smartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
 
-            val ongoingContent by collectLastValue(underTest.getOngoingContent(true))
+            val ongoingContent by collectLastValue(underTest.ongoingContent)
             assertThat(ongoingContent?.size).isEqualTo(4)
             assertThat(ongoingContent?.get(0)?.key)
                 .isEqualTo(CommunalContentModel.KEY.smartspace("timer3"))
@@ -482,7 +471,7 @@
         testScope.runTest {
             val targetScene = CommunalScenes.Communal
 
-            underTest.changeScene(targetScene)
+            underTest.changeScene(targetScene, "test")
 
             val desiredScene = collectLastValue(communalRepository.currentScene)
             runCurrent()
@@ -635,7 +624,7 @@
             runCurrent()
             assertThat(isCommunalShowing()).isEqualTo(false)
 
-            underTest.changeScene(CommunalScenes.Communal)
+            underTest.changeScene(CommunalScenes.Communal, "test")
 
             isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
             runCurrent()
@@ -659,12 +648,12 @@
             assertThat(isCommunalShowing).isFalse()
 
             // Verify scene changes (without the flag) to communal sets the value to true
-            underTest.changeScene(CommunalScenes.Communal)
+            underTest.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             assertThat(isCommunalShowing).isTrue()
 
             // Verify scene changes (without the flag) to blank sets the value back to false
-            underTest.changeScene(CommunalScenes.Blank)
+            underTest.changeScene(CommunalScenes.Blank, "test")
             runCurrent()
             assertThat(isCommunalShowing).isFalse()
         }
@@ -679,7 +668,7 @@
             assertThat(isCommunalShowing).isFalse()
 
             // Verify scene changes without the flag doesn't have any impact
-            underTest.changeScene(CommunalScenes.Communal)
+            underTest.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             assertThat(isCommunalShowing).isFalse()
 
@@ -791,14 +780,6 @@
         }
 
     @Test
-    fun showWidgetEditor_withPreselectedKey_startsActivity() =
-        testScope.runTest {
-            val widgetKey = CommunalContentModel.KEY.widget(123)
-            underTest.showWidgetEditor(preselectedKey = widgetKey)
-            verify(editWidgetsActivityStarter).startActivity(widgetKey)
-        }
-
-    @Test
     fun showWidgetEditor_openWidgetPickerOnStart_startsActivity() =
         testScope.runTest {
             underTest.showWidgetEditor(shouldOpenWidgetPickerOnStart = true)
@@ -1039,6 +1020,22 @@
             assertThat(showCommunalFromOccluded).isTrue()
         }
 
+    @Test
+    fun showCommunalFromOccluded_enteredOccludedFromDreaming() =
+        testScope.runTest {
+            kosmos.setCommunalAvailable(true)
+            val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
+            assertThat(showCommunalFromOccluded).isFalse()
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.OCCLUDED,
+                testScope
+            )
+
+            assertThat(showCommunalFromOccluded).isTrue()
+        }
+
     private fun smartspaceTimer(id: String, timestamp: Long = 0L): CommunalSmartspaceTimer {
         return CommunalSmartspaceTimer(
             smartspaceTargetId = id,
@@ -1066,6 +1063,24 @@
             assertThat(disclaimerDismissed).isFalse()
         }
 
+    @Test
+    fun settingSelectedKey_flowUpdated() {
+        testScope.runTest {
+            val key = "test"
+            val selectedKey by collectLastValue(underTest.selectedKey)
+            underTest.setSelectedKey(key)
+            assertThat(selectedKey).isEqualTo(key)
+        }
+    }
+
+    @Test
+    fun unpauseWorkProfileEnablesWorkMode() =
+        testScope.runTest {
+            underTest.unpauseWorkProfile()
+
+            assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+        }
+
     private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
         whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
             .thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
index 43293c7..dfb75ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor.OnSceneAboutToChangeListener
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.EditModeState
@@ -36,6 +37,11 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -53,17 +59,47 @@
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
 
-            underTest.changeScene(CommunalScenes.Communal)
+            underTest.changeScene(CommunalScenes.Communal, "test")
             assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
         }
 
     @Test
+    fun changeScene_callsSceneStateProcessor() =
+        testScope.runTest {
+            val callback: OnSceneAboutToChangeListener = mock()
+            underTest.registerSceneStateProcessor(callback)
+
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+            verify(callback, never()).onSceneAboutToChange(any(), anyOrNull())
+
+            underTest.changeScene(CommunalScenes.Communal, "test")
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+            verify(callback).onSceneAboutToChange(CommunalScenes.Communal, null)
+        }
+
+    @Test
+    fun changeScene_doesNotCallSceneStateProcessorForDuplicateState() =
+        testScope.runTest {
+            val callback: OnSceneAboutToChangeListener = mock()
+            underTest.registerSceneStateProcessor(callback)
+
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+            underTest.changeScene(CommunalScenes.Blank, "test")
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+            verify(callback, never()).onSceneAboutToChange(any(), anyOrNull())
+        }
+
+    @Test
     fun snapToScene() =
         testScope.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
             assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
 
-            underTest.snapToScene(CommunalScenes.Communal)
+            underTest.snapToScene(CommunalScenes.Communal, "test")
             assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
         }
 
@@ -75,6 +111,7 @@
             assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
             underTest.snapToScene(
                 CommunalScenes.Communal,
+                "test",
                 ActivityTransitionAnimator.TIMINGS.totalDuration
             )
             assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
@@ -86,7 +123,7 @@
     fun changeSceneForActivityStartOnDismissKeyguard() =
         testScope.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
-            underTest.snapToScene(CommunalScenes.Communal)
+            underTest.snapToScene(CommunalScenes.Communal, "test")
             assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
 
             underTest.changeSceneForActivityStartOnDismissKeyguard()
@@ -97,7 +134,7 @@
     fun changeSceneForActivityStartOnDismissKeyguard_willNotChangeScene_forEditModeActivity() =
         testScope.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
-            underTest.snapToScene(CommunalScenes.Communal)
+            underTest.snapToScene(CommunalScenes.Communal, "test")
             assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
 
             underTest.setEditModeState(EditModeState.STARTING)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
index d6712f0..2ba4bf9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractorTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -53,6 +54,7 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -78,6 +80,7 @@
 
     private val underTest by lazy { kosmos.communalSceneTransitionInteractor }
     private val keyguardTransitionRepository by lazy { kosmos.realKeyguardTransitionRepository }
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
 
     private val ownerName = CommunalSceneTransitionInteractor::class.java.simpleName
     private val progress = MutableSharedFlow<Float>()
@@ -789,4 +792,129 @@
                     )
                 )
         }
+
+    /** Verifies that we correctly transition to GONE after keyguard goes away */
+    @Test
+    fun transition_to_blank_after_unlock_should_go_to_gone() =
+        testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
+            sceneTransitions.value = Idle(CommunalScenes.Communal)
+
+            val currentStep by collectLastValue(keyguardTransitionRepository.transitions)
+
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    )
+                )
+
+            // Keyguard starts exiting after a while, then fully exits after some time.
+            advanceTimeBy(1.seconds)
+            keyguardRepository.setKeyguardGoingAway(true)
+            advanceTimeBy(2.seconds)
+            keyguardRepository.setKeyguardGoingAway(false)
+            keyguardRepository.setKeyguardShowing(false)
+            runCurrent()
+
+            // We snap to the blank scene as a result of keyguard going away.
+            sceneTransitions.value = Idle(CommunalScenes.Blank)
+
+            assertThat(currentStep)
+                .isEqualTo(
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = GONE,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    )
+                )
+        }
+
+    /**
+     * KTF: LOCKSCREEN -> ALTERNATE_BOUNCER starts but then STL: GLANCEABLE_HUB -> BLANK interrupts.
+     *
+     * Verifies that we correctly cancel the previous KTF state before starting the glanceable hub
+     * transition.
+     */
+    @Test
+    fun transition_to_blank_after_ktf_started_another_transition() =
+        testScope.runTest {
+            // Another transition has already started to the alternate bouncer.
+            keyguardTransitionRepository.startTransition(
+                TransitionInfo(
+                    from = LOCKSCREEN,
+                    to = ALTERNATE_BOUNCER,
+                    animator = null,
+                    ownerName = "external",
+                    modeOnCanceled = TransitionModeOnCanceled.RESET
+                ),
+            )
+
+            val allSteps by collectValues(keyguardTransitionRepository.transitions)
+            // Keep track of existing size to drop any pre-existing steps that we don't
+            // care about.
+            val numToDrop = allSteps.size
+
+            sceneTransitions.value = hubToBlank
+            runCurrent()
+            progress.emit(0.4f)
+            runCurrent()
+            // We land on blank.
+            sceneTransitions.value = Idle(CommunalScenes.Blank)
+
+            // We should cancel the previous ALTERNATE_BOUNCER transition and transition back
+            // to the GLANCEABLE_HUB before we can transition away from it.
+            assertThat(allSteps.drop(numToDrop))
+                .containsExactly(
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = ALTERNATE_BOUNCER,
+                        transitionState = CANCELED,
+                        value = 0f,
+                        ownerName = "external",
+                    ),
+                    TransitionStep(
+                        from = ALTERNATE_BOUNCER,
+                        to = GLANCEABLE_HUB,
+                        transitionState = STARTED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    ),
+                    TransitionStep(
+                        from = ALTERNATE_BOUNCER,
+                        to = GLANCEABLE_HUB,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    ),
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = LOCKSCREEN,
+                        transitionState = STARTED,
+                        value = 0f,
+                        ownerName = ownerName,
+                    ),
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = LOCKSCREEN,
+                        transitionState = RUNNING,
+                        value = 0.4f,
+                        ownerName = ownerName,
+                    ),
+                    TransitionStep(
+                        from = GLANCEABLE_HUB,
+                        to = LOCKSCREEN,
+                        transitionState = FINISHED,
+                        value = 1f,
+                        ownerName = ownerName,
+                    ),
+                )
+                .inOrder()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
index 3a23e14..0bfcd24 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractorTest.kt
@@ -36,11 +36,15 @@
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+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 CommunalTutorialInteractorTest : SysuiTestCase() {
@@ -50,14 +54,14 @@
     private lateinit var underTest: CommunalTutorialInteractor
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var communalTutorialRepository: FakeCommunalTutorialRepository
-    private lateinit var communalInteractor: CommunalInteractor
+    private lateinit var communalSceneInteractor: CommunalSceneInteractor
     private lateinit var userRepository: FakeUserRepository
 
     @Before
     fun setUp() {
         keyguardRepository = kosmos.fakeKeyguardRepository
         communalTutorialRepository = kosmos.fakeCommunalTutorialRepository
-        communalInteractor = kosmos.communalInteractor
+        communalSceneInteractor = kosmos.communalSceneInteractor
         userRepository = kosmos.fakeUserRepository
 
         kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
@@ -158,7 +162,7 @@
             kosmos.setCommunalAvailable(true)
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_NOT_STARTED)
 
-            communalInteractor.changeScene(CommunalScenes.Blank)
+            communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
 
             assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_NOT_STARTED)
         }
@@ -171,7 +175,7 @@
             goToCommunal()
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_STARTED)
 
-            communalInteractor.changeScene(CommunalScenes.Blank)
+            communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
 
             assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
         }
@@ -184,13 +188,14 @@
             goToCommunal()
             communalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
 
-            communalInteractor.changeScene(CommunalScenes.Blank)
+            communalSceneInteractor.changeScene(CommunalScenes.Blank, "test")
 
             assertThat(tutorialSettingState).isEqualTo(HUB_MODE_TUTORIAL_COMPLETED)
         }
 
-    private suspend fun goToCommunal() {
+    private suspend fun TestScope.goToCommunal() {
         kosmos.setCommunalAvailable(true)
-        communalInteractor.changeScene(CommunalScenes.Communal)
+        communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
+        runCurrent()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
new file mode 100644
index 0000000..b3ffc71
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorTest.kt
@@ -0,0 +1,220 @@
+/*
+ * 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.domain.interactor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.usage.UsageEvents
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.usagestats.data.repository.fakeUsageStatsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.plugins.activityStarter
+import com.android.systemui.settings.fakeUserTracker
+import com.android.systemui.shared.system.taskStackChangeListeners
+import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.currentTime
+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.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetTrampolineInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val activityStarter = kosmos.activityStarter
+    private val usageStatsRepository = kosmos.fakeUsageStatsRepository
+    private val taskStackChangeListeners = kosmos.taskStackChangeListeners
+    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val userTracker = kosmos.fakeUserTracker
+    private val systemClock = kosmos.fakeSystemClock
+
+    private val underTest = kosmos.widgetTrampolineInteractor
+
+    @Before
+    fun setUp() {
+        userTracker.set(listOf(MAIN_USER), 0)
+        systemClock.setCurrentTimeMillis(testScope.currentTime)
+    }
+
+    @Test
+    fun testNewTaskStartsWhileOnHub_triggersUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+            moveTaskToFront()
+
+            verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testNewTaskStartsAfterExitingHub_doesNotTriggerUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            transition(from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.LOCKSCREEN)
+            moveTaskToFront()
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testNewTaskStartsAfterTimeout_doesNotTriggerUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            advanceTime(2.seconds)
+            moveTaskToFront()
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testActivityResumedWhileOnHub_triggersUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+            advanceTime(1.seconds)
+
+            verify(activityStarter).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testActivityResumedAfterExitingHub_doesNotTriggerUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            transition(from = KeyguardState.GLANCEABLE_HUB, to = KeyguardState.LOCKSCREEN)
+            addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+            advanceTime(1.seconds)
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testActivityDestroyed_doesNotTriggerUnlock() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            addActivityEvent(UsageEvents.Event.ACTIVITY_DESTROYED)
+            advanceTime(1.seconds)
+
+            verify(activityStarter, never()).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    @Test
+    fun testMultipleActivityEvents_triggersUnlockOnlyOnce() =
+        testScope.runTest {
+            transition(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GLANCEABLE_HUB)
+            backgroundScope.launch { underTest.waitForActivityStartAndDismissKeyguard() }
+            runCurrent()
+
+            addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+            advanceTime(10.milliseconds)
+            addActivityEvent(UsageEvents.Event.ACTIVITY_RESUMED)
+            advanceTime(1.seconds)
+
+            verify(activityStarter, times(1)).dismissKeyguardThenExecute(any(), anyOrNull(), any())
+        }
+
+    private fun TestScope.advanceTime(duration: Duration) {
+        systemClock.advanceTime(duration.inWholeMilliseconds)
+        advanceTimeBy(duration)
+    }
+
+    private fun TestScope.addActivityEvent(type: Int) {
+        usageStatsRepository.addEvent(
+            instanceId = 1,
+            user = MAIN_USER.userHandle,
+            packageName = "pkg.test",
+            timestamp = systemClock.currentTimeMillis(),
+            type = type,
+        )
+        runCurrent()
+    }
+
+    private fun TestScope.moveTaskToFront() {
+        taskStackChangeListeners.listenerImpl.onTaskMovedToFront(mock<RunningTaskInfo>())
+        runCurrent()
+    }
+
+    private suspend fun TestScope.transition(from: KeyguardState, to: KeyguardState) {
+        keyguardTransitionRepository.sendTransitionSteps(
+            listOf(
+                TransitionStep(
+                    from = from,
+                    to = to,
+                    value = 0.1f,
+                    transitionState = TransitionState.STARTED,
+                    ownerName = "test",
+                ),
+                TransitionStep(
+                    from = from,
+                    to = to,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                    ownerName = "test",
+                ),
+            ),
+            testScope
+        )
+        runCurrent()
+    }
+
+    private companion object {
+        val MAIN_USER: UserInfo = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
+    }
+}
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 57ce9de..179ba22 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
@@ -20,9 +20,7 @@
 import android.content.ActivityNotFoundException
 import android.content.ComponentName
 import android.content.Intent
-import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
 import android.content.pm.UserInfo
 import android.provider.Settings
 import android.view.accessibility.AccessibilityEvent
@@ -72,7 +70,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Mockito
 import org.mockito.Mockito.never
@@ -141,6 +138,7 @@
                 context,
                 accessibilityManager,
                 packageManager,
+                WIDGET_PICKER_PACKAGE_NAME,
             )
     }
 
@@ -150,8 +148,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
-            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
+            widgetRepository.addWidget(appWidgetId = 0, rank = 30)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 20)
 
             // Smartspace available.
             smartspaceRepository.setTimers(
@@ -212,8 +210,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
-            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
+            widgetRepository.addWidget(appWidgetId = 0, rank = 30)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 20)
 
             val communalContent by collectLastValue(underTest.communalContent)
 
@@ -227,7 +225,7 @@
             underTest.onDeleteWidget(
                 id = 0,
                 componentName = ComponentName("test_package", "test_class"),
-                priority = 30,
+                rank = 30,
             )
 
             // Only one widget and CTA tile remain.
@@ -259,18 +257,8 @@
     @Test
     fun onOpenWidgetPicker_launchesWidgetPickerActivity() {
         testScope.runTest {
-            whenever(packageManager.resolveActivity(any(), anyInt())).then {
-                ResolveInfo().apply {
-                    activityInfo = ActivityInfo().apply { packageName = WIDGET_PICKER_PACKAGE_NAME }
-                }
-            }
-
             val success =
-                underTest.onOpenWidgetPicker(
-                    testableResources.resources,
-                    packageManager,
-                    activityResultLauncher
-                )
+                underTest.onOpenWidgetPicker(testableResources.resources, activityResultLauncher)
 
             verify(activityResultLauncher).launch(any())
             assertTrue(success)
@@ -278,38 +266,14 @@
     }
 
     @Test
-    fun onOpenWidgetPicker_launcherActivityNotResolved_doesNotLaunchWidgetPickerActivity() {
-        testScope.runTest {
-            whenever(packageManager.resolveActivity(any(), anyInt())).thenReturn(null)
-
-            val success =
-                underTest.onOpenWidgetPicker(
-                    testableResources.resources,
-                    packageManager,
-                    activityResultLauncher
-                )
-
-            verify(activityResultLauncher, never()).launch(any())
-            assertFalse(success)
-        }
-    }
-
-    @Test
     fun onOpenWidgetPicker_activityLaunchThrowsException_failure() {
         testScope.runTest {
-            whenever(packageManager.resolveActivity(any(), anyInt())).then {
-                ResolveInfo().apply {
-                    activityInfo = ActivityInfo().apply { packageName = WIDGET_PICKER_PACKAGE_NAME }
-                }
-            }
-
             whenever(activityResultLauncher.launch(any()))
                 .thenThrow(ActivityNotFoundException::class.java)
 
             val success =
                 underTest.onOpenWidgetPicker(
                     testableResources.resources,
-                    packageManager,
                     activityResultLauncher,
                 )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index cc945d6..780d357 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -97,11 +97,11 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.spy
-import org.mockito.kotlin.times
 import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
@@ -160,24 +160,27 @@
 
         communalInteractor = spy(kosmos.communalInteractor)
 
-        underTest =
-            CommunalViewModel(
-                kosmos.testDispatcher,
-                testScope,
-                kosmos.testScope.backgroundScope,
-                context.resources,
-                kosmos.keyguardTransitionInteractor,
-                kosmos.keyguardInteractor,
-                mock<KeyguardIndicationController>(),
-                kosmos.communalSceneInteractor,
-                communalInteractor,
-                kosmos.communalSettingsInteractor,
-                kosmos.communalTutorialInteractor,
-                kosmos.shadeInteractor,
-                mediaHost,
-                logcatLogBuffer("CommunalViewModelTest"),
-                metricsLogger,
-            )
+        underTest = createViewModel()
+    }
+
+    private fun createViewModel(): CommunalViewModel {
+        return CommunalViewModel(
+            kosmos.testDispatcher,
+            testScope,
+            kosmos.testScope.backgroundScope,
+            context.resources,
+            kosmos.keyguardTransitionInteractor,
+            kosmos.keyguardInteractor,
+            mock<KeyguardIndicationController>(),
+            kosmos.communalSceneInteractor,
+            communalInteractor,
+            kosmos.communalSettingsInteractor,
+            kosmos.communalTutorialInteractor,
+            kosmos.shadeInteractor,
+            mediaHost,
+            logcatLogBuffer("CommunalViewModelTest"),
+            metricsLogger,
+        )
     }
 
     @Test
@@ -213,8 +216,8 @@
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
             // Widgets available.
-            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
-            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
+            widgetRepository.addWidget(appWidgetId = 0, rank = 30)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 20)
 
             // Smartspace available.
             smartspaceRepository.setTimers(
@@ -303,7 +306,7 @@
         testScope.runTest {
             tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
 
-            widgetRepository.addWidget(appWidgetId = 1, priority = 1)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 1)
             mediaRepository.mediaInactive()
             smartspaceRepository.setTimers(emptyList())
 
@@ -660,8 +663,8 @@
             )
 
             // Widgets available
-            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
-            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
+            widgetRepository.addWidget(appWidgetId = 0, rank = 30)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 20)
 
             // Then hub shows widgets and the CTA tile
             assertThat(communalContent).hasSize(3)
@@ -716,8 +719,8 @@
             )
 
             // And widgets available
-            widgetRepository.addWidget(appWidgetId = 0, priority = 30)
-            widgetRepository.addWidget(appWidgetId = 1, priority = 20)
+            widgetRepository.addWidget(appWidgetId = 0, rank = 30)
+            widgetRepository.addWidget(appWidgetId = 1, rank = 20)
 
             // Then emits widgets and the CTA tile
             assertThat(communalContent).hasSize(3)
@@ -755,7 +758,7 @@
 
             // updateViewVisibility is called when the flow is collected.
             assertThat(communalContent).isNotNull()
-            verify(mediaHost).updateViewVisibility()
+            verify(mediaHost, atLeastOnce()).updateViewVisibility()
         }
 
     @Test
@@ -770,7 +773,7 @@
 
     @Test
     fun onTapWidget_logEvent() {
-        underTest.onTapWidget(ComponentName("test_pkg", "test_cls"), priority = 10)
+        underTest.onTapWidget(ComponentName("test_pkg", "test_cls"), rank = 10)
         verify(metricsLogger).logTapWidget("test_pkg/test_cls", rank = 10)
     }
 
@@ -785,6 +788,21 @@
             assertThat(touchAvailable).isTrue()
         }
 
+    @Test
+    fun selectedKey_changeAffectsAllInstances() =
+        testScope.runTest {
+            val model1 = createViewModel()
+            val selectedKey1 by collectLastValue(model1.selectedKey)
+            val model2 = createViewModel()
+            val selectedKey2 by collectLastValue(model2.selectedKey)
+
+            val key = "test"
+            model1.setSelectedKey(key)
+
+            assertThat(selectedKey1).isEqualTo(key)
+            assertThat(selectedKey2).isEqualTo(key)
+        }
+
     private suspend fun setIsMainUser(isMainUser: Boolean) {
         val user = if (isMainUser) MAIN_USER_INFO else SECONDARY_USER_INFO
         with(userRepository) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
index e36fd75..a052b07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorControllerTest.kt
@@ -76,7 +76,7 @@
                 val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
                 communalSceneInteractor.setIsLaunchingWidget(true)
                 assertTrue(launching!!)
@@ -103,7 +103,7 @@
                 val launching by collectLastValue(communalSceneInteractor.isLaunchingWidget)
                 val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
                 Truth.assertThat(scene).isEqualTo(CommunalScenes.Communal)
                 communalSceneInteractor.setIsLaunchingWidget(true)
                 assertTrue(launching!!)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
index 50fdb31..3ba8625 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityControllerTest.kt
@@ -30,28 +30,63 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @ExperimentalCoroutinesApi
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EditWidgetsActivityControllerTest : SysuiTestCase() {
     @Test
-    fun activityLifecycle_stoppedWhenNotWaitingForResult() {
+    fun activityLifecycle_finishedWhenNotWaitingForResult() {
         val activity = mock<Activity>()
-        val controller = EditWidgetsActivity.ActivityController(activity)
+        val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
 
         val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
         verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
 
+        controller.setActivityFullyVisible(true)
         callbackCapture.lastValue.onActivityStopped(activity)
 
         verify(activity).finish()
     }
 
     @Test
-    fun activityLifecycle_notStoppedWhenNotWaitingForResult() {
+    fun activityLifecycle_notFinishedWhenOnStartCalledAfterOnStop() {
         val activity = mock<Activity>()
-        val controller = EditWidgetsActivity.ActivityController(activity)
+
+        val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
+
+        val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+        verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+        controller.setActivityFullyVisible(false)
+        callbackCapture.lastValue.onActivityStopped(activity)
+        callbackCapture.lastValue.onActivityStarted(activity)
+
+        verify(activity, never()).finish()
+    }
+
+    @Test
+    fun activityLifecycle_notFinishedDuringConfigurationChange() {
+        val activity = mock<Activity>()
+
+        val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
+
+        val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
+        verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
+
+        controller.setActivityFullyVisible(true)
+        whenever(activity.isChangingConfigurations).thenReturn(true)
+        callbackCapture.lastValue.onActivityStopped(activity)
+        callbackCapture.lastValue.onActivityStarted(activity)
+
+        verify(activity, never()).finish()
+    }
+
+    @Test
+    fun activityLifecycle_notFinishedWhenWaitingForResult() {
+        val activity = mock<Activity>()
+        val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
 
         val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
         verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
@@ -63,15 +98,16 @@
     }
 
     @Test
-    fun activityLifecycle_stoppedAfterResultReturned() {
+    fun activityLifecycle_finishedAfterResultReturned() {
         val activity = mock<Activity>()
-        val controller = EditWidgetsActivity.ActivityController(activity)
+        val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
 
         val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
         verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
 
         controller.onWaitingForResult(true)
         controller.onWaitingForResult(false)
+        controller.setActivityFullyVisible(true)
         callbackCapture.lastValue.onActivityStopped(activity)
 
         verify(activity).finish()
@@ -83,7 +119,7 @@
         val bundle = Bundle(1)
 
         run {
-            val controller = EditWidgetsActivity.ActivityController(activity)
+            val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
             val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
             verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
 
@@ -94,7 +130,7 @@
         clearInvocations(activity)
 
         run {
-            val controller = EditWidgetsActivity.ActivityController(activity)
+            val controller = EditWidgetsActivity.ActivityControllerImpl(activity)
             val callbackCapture = argumentCaptor<ActivityLifecycleCallbacks>()
             verify(activity).registerActivityLifecycleCallbacks(callbackCapture.capture())
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt
index 5b629b9..48b42d5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarterTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.testKosmos
@@ -62,7 +61,7 @@
     fun activityLaunch_intentIsWellFormed() {
         with(kosmos) {
             testScope.runTest {
-                underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = true)
+                underTest.startActivity(shouldOpenWidgetPickerOnStart = true)
 
                 val captor = argumentCaptor<Intent>()
                 verify(activityStarter)
@@ -71,8 +70,6 @@
                 assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0)
                 assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK)
                     .isNotEqualTo(0)
-                assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY))
-                    .isEqualTo(TEST_PRESELECTED_KEY)
                 assertThat(
                         captor.lastValue.extras?.getBoolean(
                             EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START
@@ -80,7 +77,7 @@
                     )
                     .isEqualTo(true)
 
-                underTest.startActivity(TEST_PRESELECTED_KEY, shouldOpenWidgetPickerOnStart = false)
+                underTest.startActivity(shouldOpenWidgetPickerOnStart = false)
 
                 verify(activityStarter, times(2))
                     .startActivityDismissingKeyguard(captor.capture(), eq(true), eq(true), any())
@@ -88,8 +85,6 @@
                 assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_NEW_TASK).isNotEqualTo(0)
                 assertThat(captor.lastValue.flags and Intent.FLAG_ACTIVITY_CLEAR_TASK)
                     .isNotEqualTo(0)
-                assertThat(captor.lastValue.extras?.getString(EXTRA_PRESELECTED_KEY))
-                    .isEqualTo(TEST_PRESELECTED_KEY)
                 assertThat(
                         captor.lastValue.extras?.getBoolean(
                             EditWidgetsActivity.EXTRA_OPEN_WIDGET_PICKER_ON_START
@@ -99,8 +94,4 @@
             }
         }
     }
-
-    companion object {
-        const val TEST_PRESELECTED_KEY = "test-key"
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index 023de52..9c308a60 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -25,13 +25,19 @@
 import androidx.core.util.component2
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.widgetTrampolineInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.testKosmos
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -39,12 +45,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.isNull
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.refEq
 import org.mockito.kotlin.verify
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WidgetInteractionHandlerTest : SysuiTestCase() {
@@ -67,9 +75,13 @@
         with(kosmos) {
             underTest =
                 WidgetInteractionHandler(
+                    applicationScope = applicationCoroutineScope,
+                    uiBackgroundContext = backgroundCoroutineContext,
                     activityStarter = activityStarter,
                     communalSceneInteractor = communalSceneInteractor,
+                    keyguardUpdateMonitor = keyguardUpdateMonitor,
                     logBuffer = logcatLogBuffer(),
+                    widgetTrampolineInteractor = widgetTrampolineInteractor,
                 )
         }
     }
@@ -91,16 +103,21 @@
                 // Verify that we set the state correctly
                 assertTrue(launching!!)
                 // Verify that we pass in a non-null Communal animation controller
+
+                val callbackCaptor = argumentCaptor<Runnable>()
                 verify(activityStarter)
                     .startPendingIntentMaybeDismissingKeyguard(
                         /* intent = */ eq(testIntent),
                         /* dismissShade = */ eq(false),
-                        /* intentSentUiThreadCallback = */ isNull(),
+                        /* intentSentUiThreadCallback = */ callbackCaptor.capture(),
                         /* animationController = */ any<CommunalTransitionAnimatorController>(),
                         /* fillInIntent = */ refEq(fillInIntent),
                         /* extraOptions = */ refEq(activityOptions.toBundle()),
                         /* customMessage */ isNull(),
                     )
+                callbackCaptor.firstValue.run()
+                runCurrent()
+                verify(keyguardUpdateMonitor).awakenFromDream()
             }
         }
     }
@@ -119,7 +136,7 @@
             .startPendingIntentMaybeDismissingKeyguard(
                 /* intent = */ eq(testIntent),
                 /* dismissShade = */ eq(false),
-                /* intentSentUiThreadCallback = */ isNull(),
+                /* intentSentUiThreadCallback = */ any(),
                 /* animationController = */ isNull(),
                 /* fillInIntent = */ refEq(fillInIntent),
                 /* extraOptions = */ refEq(activityOptions.toBundle()),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationHostViewControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationLayoutEngineTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationLayoutParamsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationTypesUpdaterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationUtilsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationUtilsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationUtilsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/ComplicationViewModelTransformerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamClockTimeComplicationTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/DreamMediaEntryComplicationTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/complication/SmartSpaceComplicationTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/CustomIconCacheTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/CustomIconCacheTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/PackageUpdateMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/AllModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/AllModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/AppAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/AppAdapterTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
new file mode 100644
index 0000000..f1782e8
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -0,0 +1,720 @@
+/*
+ * 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.controls.management
+
+import android.Manifest
+import android.content.ComponentName
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.os.Bundle
+import android.os.UserHandle
+import android.service.controls.ControlsProviderService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.applications.ServiceListing
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.res.R
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.ActivityTaskManagerProxy
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argThat
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+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.ArgumentMatcher
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.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.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ControlsListingControllerImplTest : SysuiTestCase() {
+
+    companion object {
+        private const val FLAGS =
+            PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+    }
+
+    @Mock private lateinit var mockSL: ServiceListing
+    @Mock private lateinit var mockCallback: ControlsListingController.ControlsListingCallback
+    @Mock private lateinit var mockCallbackOther: ControlsListingController.ControlsListingCallback
+    @Mock(stubOnly = true) private lateinit var userTracker: UserTracker
+    @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var featureFlags: FeatureFlags
+    @Mock private lateinit var activityTaskManagerProxy: ActivityTaskManagerProxy
+
+    private var componentName = ComponentName("pkg", "class1")
+    private var activityName = ComponentName("pkg", "activity")
+
+    private val executor = FakeExecutor(FakeSystemClock())
+
+    private lateinit var controller: ControlsListingControllerImpl
+
+    private var serviceListingCallbackCaptor =
+        ArgumentCaptor.forClass(ServiceListing.Callback::class.java)
+
+    private val user = mContext.userId
+    private val otherUser = user + 1
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(userTracker.userId).thenReturn(user)
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
+        `when`(userTracker.userContext).thenReturn(context)
+        // Return disabled by default
+        `when`(packageManager.getComponentEnabledSetting(any()))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
+        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(true)
+        mContext.setMockPackageManager(packageManager)
+
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf(componentName.packageName)
+        )
+
+        val wrapper =
+            object : ContextWrapper(mContext) {
+                override fun createContextAsUser(user: UserHandle, flags: Int): Context {
+                    return baseContext
+                }
+            }
+
+        controller =
+            ControlsListingControllerImpl(
+                wrapper,
+                executor,
+                { mockSL },
+                userTracker,
+                activityTaskManagerProxy,
+                dumpManager,
+                featureFlags
+            )
+        verify(mockSL).addCallback(capture(serviceListingCallbackCaptor))
+    }
+
+    @After
+    fun tearDown() {
+        executor.advanceClockToLast()
+        executor.runAllReady()
+    }
+
+    @Test
+    fun testInitialStateListening() {
+        verify(mockSL).setListening(true)
+        verify(mockSL).reload()
+    }
+
+    @Test
+    fun testImmediateListingReload_doesNotCrash() {
+        val exec = Executor { it.run() }
+        val mockServiceListing = mock(ServiceListing::class.java)
+        var callback: ServiceListing.Callback? = null
+        `when`(mockServiceListing.addCallback(any<ServiceListing.Callback>())).then {
+            callback = it.getArgument(0)
+            Unit
+        }
+        `when`(mockServiceListing.reload()).then {
+            callback?.onServicesReloaded(listOf(ServiceInfo(componentName)))
+        }
+        ControlsListingControllerImpl(
+            mContext,
+            exec,
+            { mockServiceListing },
+            userTracker,
+            activityTaskManagerProxy,
+            dumpManager,
+            featureFlags
+        )
+    }
+
+    @Test
+    fun testStartsOnUser() {
+        assertEquals(user, controller.currentUserId)
+    }
+
+    @Test
+    fun testCallbackCalledWhenAdded() {
+        controller.addCallback(mockCallback)
+        executor.runAllReady()
+        verify(mockCallback).onServicesUpdated(any())
+        reset(mockCallback)
+
+        controller.addCallback(mockCallbackOther)
+        executor.runAllReady()
+        verify(mockCallbackOther).onServicesUpdated(any())
+        verify(mockCallback, never()).onServicesUpdated(any())
+    }
+
+    @Test
+    fun testCallbackGetsList() {
+        val list = listOf(ServiceInfo(componentName))
+        controller.addCallback(mockCallback)
+        controller.addCallback(mockCallbackOther)
+
+        @Suppress("unchecked_cast")
+        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+            ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<ControlsServiceInfo>>
+
+        executor.runAllReady()
+        reset(mockCallback)
+        reset(mockCallbackOther)
+
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+        verify(mockCallback).onServicesUpdated(capture(captor))
+        assertEquals(1, captor.value.size)
+        assertEquals(componentName.flattenToString(), captor.value[0].key)
+
+        verify(mockCallbackOther).onServicesUpdated(capture(captor))
+        assertEquals(1, captor.value.size)
+        assertEquals(componentName.flattenToString(), captor.value[0].key)
+    }
+
+    @Test
+    fun testChangeUser() {
+        controller.changeUser(UserHandle.of(otherUser))
+        executor.runAllReady()
+        assertEquals(otherUser, controller.currentUserId)
+
+        val inOrder = inOrder(mockSL)
+        inOrder.verify(mockSL).setListening(false)
+        inOrder.verify(mockSL).addCallback(any()) // We add a callback because we replaced the SL
+        inOrder.verify(mockSL).setListening(true)
+        inOrder.verify(mockSL).reload()
+    }
+
+    @Test
+    fun testChangeUserSendsCorrectServiceUpdate() {
+        val serviceInfo = ServiceInfo(componentName)
+
+        val list = listOf(serviceInfo)
+        controller.addCallback(mockCallback)
+
+        @Suppress("unchecked_cast")
+        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+            ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<ControlsServiceInfo>>
+        executor.runAllReady()
+        reset(mockCallback)
+
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+        verify(mockCallback).onServicesUpdated(capture(captor))
+        assertEquals(1, captor.value.size)
+
+        reset(mockCallback)
+        reset(mockSL)
+
+        val updatedList = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(updatedList)
+        controller.changeUser(UserHandle.of(otherUser))
+        executor.runAllReady()
+        assertEquals(otherUser, controller.currentUserId)
+
+        // this event should was triggered just before the user change, and should
+        // be ignored
+        verify(mockCallback, never()).onServicesUpdated(any())
+
+        serviceListingCallbackCaptor.value.onServicesReloaded(emptyList<ServiceInfo>())
+        executor.runAllReady()
+
+        verify(mockCallback).onServicesUpdated(capture(captor))
+        assertEquals(0, captor.value.size)
+    }
+
+    @Test
+    fun test_nullPanelActivity() {
+        val list = listOf(ServiceInfo(componentName))
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testNoActivity_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityWithoutPermission_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        setUpQueryResult(listOf(ActivityInfo(activityName)))
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityPermissionNotExported_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        setUpQueryResult(
+            listOf(ActivityInfo(activityName, permission = Manifest.permission.BIND_CONTROLS))
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityDisabled_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityEnabled_correctPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityDefaultEnabled_correctPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    enabled = true,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityDefaultDisabled_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    enabled = false,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testActivityDifferentPackage_nullPanel() {
+        val serviceInfo = ServiceInfo(componentName, ComponentName("other_package", "cls"))
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    enabled = true,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testPackageNotPreferred_correctPanel() {
+        mContext.orCreateTestableResources.addOverride(
+            R.array.config_controlsPreferredPackages,
+            arrayOf<String>()
+        )
+
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun testListingsNotModifiedByCallback() {
+        // This test checks that if the list passed to the callback is modified, it has no effect
+        // in the resulting services
+        val list = mutableListOf<ServiceInfo>()
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        list.add(ServiceInfo(ComponentName("a", "b")))
+        executor.runAllReady()
+
+        assertTrue(controller.getCurrentServices().isEmpty())
+    }
+
+    @Test
+    fun testForceReloadQueriesPackageManager() {
+        val user = 10
+        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
+
+        controller.forceReload()
+        verify(packageManager)
+            .queryIntentServicesAsUser(
+                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                argThat(
+                    FlagsMatcher(
+                        PackageManager.GET_META_DATA.toLong() or
+                            PackageManager.GET_SERVICES.toLong() or
+                            PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                            PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                    )
+                ),
+                eq(UserHandle.of(user))
+            )
+    }
+
+    @Test
+    fun testForceReloadUpdatesList() {
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(
+                packageManager.queryIntentServicesAsUser(
+                    argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
+                    argThat(
+                        FlagsMatcher(
+                            PackageManager.GET_META_DATA.toLong() or
+                                PackageManager.GET_SERVICES.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
+                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
+                        )
+                    ),
+                    any<UserHandle>()
+                )
+            )
+            .thenReturn(listOf(resolveInfo))
+
+        controller.forceReload()
+
+        val services = controller.getCurrentServices()
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+    @Test
+    fun testForceReloadCallsListeners() {
+        controller.addCallback(mockCallback)
+        executor.runAllReady()
+
+        @Suppress("unchecked_cast")
+        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
+            ArgumentCaptor.forClass(List::class.java) as ArgumentCaptor<List<ControlsServiceInfo>>
+
+        val resolveInfo = ResolveInfo()
+        resolveInfo.serviceInfo = ServiceInfo(componentName)
+
+        `when`(
+                packageManager.queryIntentServicesAsUser(
+                    any(),
+                    any<PackageManager.ResolveInfoFlags>(),
+                    any<UserHandle>()
+                )
+            )
+            .thenReturn(listOf(resolveInfo))
+
+        reset(mockCallback)
+        controller.forceReload()
+
+        verify(mockCallback).onServicesUpdated(capture(captor))
+
+        val services = captor.value
+
+        assertThat(services.size).isEqualTo(1)
+        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
+    }
+
+    @Test
+    fun testNoPanelIfMultiWindowNotSupported() {
+        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(false)
+
+        val serviceInfo = ServiceInfo(componentName, activityName)
+
+        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
+            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+
+        setUpQueryResult(
+            listOf(
+                ActivityInfo(
+                    activityName,
+                    enabled = true,
+                    exported = true,
+                    permission = Manifest.permission.BIND_CONTROLS
+                )
+            )
+        )
+
+        val list = listOf(serviceInfo)
+        serviceListingCallbackCaptor.value.onServicesReloaded(list)
+
+        executor.runAllReady()
+
+        assertNull(controller.getCurrentServices()[0].panelActivity)
+    }
+
+    @Test
+    fun dumpAndAddRemoveCallback_willNotThrowConcurrentModificationException() {
+        val repeat = 100
+        controller.addCallback(mockCallback) // 1 extra callback increases the duration of iteration
+
+        // the goal of these two barriers is to make the modify and iterate run concurrently
+        val startSignal = CountDownLatch(2)
+        val doneSignal = CountDownLatch(2)
+        val modifyRunnable = Runnable {
+            for (i in 1..repeat) {
+                controller.addCallback(mockCallbackOther)
+                executor.runAllReady()
+                controller.removeCallback(mockCallbackOther)
+                executor.runAllReady()
+            }
+        }
+        val printWriter = mock<PrintWriter>()
+        val arr = arrayOf<String>()
+        val iterateRunnable = Runnable {
+            for (i in 1..repeat) {
+                controller.dump(printWriter, arr)
+            }
+        }
+
+        val workerThread = Thread(Worker(startSignal, doneSignal, modifyRunnable))
+        workerThread.start()
+        val workerThreadOther = Thread(Worker(startSignal, doneSignal, iterateRunnable))
+        workerThreadOther.start()
+        doneSignal.await()
+        workerThread.interrupt()
+        workerThreadOther.interrupt()
+    }
+
+    class Worker : Runnable {
+        private val startSignal: CountDownLatch
+        private val doneSignal: CountDownLatch
+        private val runnable: Runnable
+
+        constructor(start: CountDownLatch, done: CountDownLatch, run: Runnable) {
+            startSignal = start
+            doneSignal = done
+            runnable = run
+        }
+
+        override fun run() {
+            try {
+                startSignal.countDown()
+                startSignal.await()
+                runnable.run()
+                doneSignal.countDown()
+            } catch (ex: InterruptedException) {
+                return
+            }
+        }
+    }
+
+    private fun ServiceInfo(
+        componentName: ComponentName,
+        panelActivityComponentName: ComponentName? = null
+    ): ServiceInfo {
+        return ServiceInfo().apply {
+            packageName = componentName.packageName
+            name = componentName.className
+            panelActivityComponentName?.let {
+                metaData =
+                    Bundle().apply {
+                        putString(
+                            ControlsProviderService.META_DATA_PANEL_ACTIVITY,
+                            it.flattenToShortString()
+                        )
+                    }
+            }
+        }
+    }
+
+    private fun ActivityInfo(
+        componentName: ComponentName,
+        exported: Boolean = false,
+        enabled: Boolean = true,
+        permission: String? = null
+    ): ActivityInfo {
+        return ActivityInfo().apply {
+            packageName = componentName.packageName
+            name = componentName.className
+            this.permission = permission
+            this.exported = exported
+            this.enabled = enabled
+        }
+    }
+
+    private fun setUpQueryResult(infos: List<ActivityInfo>) {
+        `when`(
+                packageManager.queryIntentActivitiesAsUser(
+                    argThat(IntentMatcherComponent(activityName)),
+                    argThat(FlagsMatcher(FLAGS)),
+                    eq(UserHandle.of(user))
+                )
+            )
+            .thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
+    }
+
+    private class IntentMatcherComponent(private val componentName: ComponentName) :
+        ArgumentMatcher<Intent> {
+        override fun matches(argument: Intent?): Boolean {
+            return argument?.component == componentName
+        }
+    }
+
+    private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> {
+        override fun matches(argument: Intent?): Boolean {
+            return argument?.action == action
+        }
+    }
+
+    private class FlagsMatcher(private val flags: Long) :
+        ArgumentMatcher<PackageManager.ResolveInfoFlags> {
+        override fun matches(argument: PackageManager.ResolveInfoFlags?): Boolean {
+            return flags == argument?.value
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/PanelConfirmationDialogFactoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/StartActivityData.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/StartActivityData.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/panels/SelectedComponentRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/settings/ControlsSettingsRepositoryImplTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TemperatureControlBehaviorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/TestableControlsActivity.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TestableControlsActivity.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/TestableControlsActivity.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/TestableControlsActivity.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/controls/ui/ToggleRangeTemplateTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/coroutines/FlowTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/coroutines/FlowTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 91259a6..71abed7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -34,6 +34,7 @@
 import android.os.CancellationSignal
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.internal.logging.InstanceId.fakeInstanceId
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.Flags as AConfigFlags
@@ -55,6 +56,7 @@
 import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.keyguard.data.repository.BiometricType
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -73,10 +75,13 @@
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.log.SessionTracker
 import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+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.phone.KeyguardBypassController
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.SelectionStatus
@@ -85,11 +90,12 @@
 import com.android.systemui.util.mockito.captureMany
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
@@ -136,12 +142,12 @@
 
     @Captor
     private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
-    private val testDispatcher = kosmos.testDispatcher
+    private val testDispatcher by lazy { kosmos.testDispatcher }
 
-    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
-    private val testScope = kosmos.testScope
-    private val fakeUserRepository = kosmos.fakeUserRepository
-    private val fakeExecutor = kosmos.fakeExecutor
+    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+    private val testScope by lazy { kosmos.testScope }
+    private val fakeUserRepository by lazy { kosmos.fakeUserRepository }
+    private val fakeExecutor by lazy { kosmos.fakeExecutor }
     private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
     private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
     private lateinit var authRunning: FlowValue<Boolean?>
@@ -149,18 +155,19 @@
     private lateinit var lockedOut: FlowValue<Boolean?>
     private lateinit var canFaceAuthRun: FlowValue<Boolean?>
     private lateinit var authenticated: FlowValue<Boolean?>
-    private val biometricSettingsRepository = kosmos.fakeBiometricSettingsRepository
-    private val deviceEntryFingerprintAuthRepository =
+    private val biometricSettingsRepository by lazy { kosmos.fakeBiometricSettingsRepository }
+    private val deviceEntryFingerprintAuthRepository by lazy {
         kosmos.fakeDeviceEntryFingerprintAuthRepository
-    private val trustRepository = kosmos.fakeTrustRepository
-    private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val powerInteractor = kosmos.powerInteractor
-    private val keyguardInteractor = kosmos.keyguardInteractor
-    private val alternateBouncerInteractor = kosmos.alternateBouncerInteractor
-    private val displayStateInteractor = kosmos.displayStateInteractor
-    private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
-    private val displayRepository = kosmos.displayRepository
-    private val keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor
+    }
+    private val trustRepository by lazy { kosmos.fakeTrustRepository }
+    private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+    private val powerInteractor by lazy { kosmos.powerInteractor }
+    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
+    private val alternateBouncerInteractor by lazy { kosmos.alternateBouncerInteractor }
+    private val displayStateInteractor by lazy { kosmos.displayStateInteractor }
+    private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
+    private val displayRepository by lazy { kosmos.displayRepository }
+    private val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor }
     private lateinit var featureFlags: FakeFeatureFlags
 
     private var wasAuthCancelled = false
@@ -180,34 +187,19 @@
         whenever(bypassController.bypassEnabled).thenReturn(true)
         underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController)
 
-        mSetFlagsRule.disableFlags(
-            AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
-        )
+        if (!SceneContainerFlag.isEnabled) {
+            mSetFlagsRule.disableFlags(
+                AConfigFlags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+            )
+        }
     }
 
     private fun createDeviceEntryFaceAuthRepositoryImpl(
         fmOverride: FaceManager? = faceManager,
         bypassControllerOverride: KeyguardBypassController? = bypassController
     ): DeviceEntryFaceAuthRepositoryImpl {
-        val systemClock = FakeSystemClock()
-        val faceAuthBuffer =
-            TableLogBuffer(
-                10,
-                "face auth",
-                systemClock,
-                mock(),
-                testDispatcher,
-                testScope.backgroundScope
-            )
-        val faceDetectBuffer =
-            TableLogBuffer(
-                10,
-                "face detect",
-                systemClock,
-                mock(),
-                testDispatcher,
-                testScope.backgroundScope
-            )
+        val faceAuthBuffer = logcatTableLogBuffer(kosmos, "face auth")
+        val faceDetectBuffer = logcatTableLogBuffer(kosmos, "face detect")
 
         return DeviceEntryFaceAuthRepositoryImpl(
             mContext,
@@ -227,6 +219,7 @@
             powerInteractor,
             keyguardInteractor,
             alternateBouncerInteractor,
+            { kosmos.sceneInteractor },
             faceDetectBuffer,
             faceAuthBuffer,
             keyguardTransitionInteractor,
@@ -547,6 +540,39 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun withSceneContainerEnabled_authenticateDoesNotRunWhenKeyguardIsGoingAway() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth(sceneContainerEnabled = true) {
+                kosmos.sceneInteractor.setTransitionState(
+                    MutableStateFlow(
+                        ObservableTransitionState.Transition(
+                            fromScene = Scenes.Bouncer,
+                            toScene = Scenes.Gone,
+                            currentScene = flowOf(Scenes.Bouncer),
+                            progress = MutableStateFlow(0.2f),
+                            isInitiatedByUserInput = true,
+                            isUserInputOngoing = flowOf(false),
+                        )
+                    )
+                )
+                runCurrent()
+            }
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun withSceneContainerEnabled_authenticateDoesNotRunWhenLockscreenIsGone() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth(sceneContainerEnabled = true) {
+                kosmos.sceneInteractor.setTransitionState(
+                    MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
+                )
+                runCurrent()
+            }
+        }
+
+    @Test
     fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() =
         testScope.runTest {
             testGatingCheckForFaceAuth {
@@ -595,6 +621,31 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun withSceneContainer_authenticateRunsWhenSecureCameraIsActiveIfBouncerIsShowing() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled = true)
+            bouncerRepository.setAlternateVisible(false)
+
+            // launch secure camera
+            keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+            keyguardRepository.setKeyguardOccluded(true)
+            kosmos.sceneInteractor.snapToScene(Scenes.Lockscreen, "for-test")
+            runCurrent()
+            assertThat(canFaceAuthRun()).isFalse()
+
+            // but bouncer is shown after that.
+            kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "for-test")
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(ObservableTransitionState.Idle(Scenes.Bouncer))
+            )
+            runCurrent()
+
+            assertThat(canFaceAuthRun()).isTrue()
+        }
+
+    @Test
     fun authenticateDoesNotRunOnUnsupportedPosture() =
         testScope.runTest {
             testGatingCheckForFaceAuth {
@@ -841,6 +892,41 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun withSceneContainer_faceDetectDoesNotRunWhenKeyguardGoingAway() =
+        testScope.runTest {
+            testGatingCheckForDetect(sceneContainerEnabled = true) {
+                kosmos.sceneInteractor.setTransitionState(
+                    MutableStateFlow(
+                        ObservableTransitionState.Transition(
+                            fromScene = Scenes.Bouncer,
+                            toScene = Scenes.Gone,
+                            currentScene = flowOf(Scenes.Bouncer),
+                            progress = MutableStateFlow(0.2f),
+                            isInitiatedByUserInput = true,
+                            isUserInputOngoing = flowOf(false),
+                        )
+                    )
+                )
+
+                runCurrent()
+            }
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun withSceneContainer_faceDetectDoesNotRunWhenLockscreenIsGone() =
+        testScope.runTest {
+            testGatingCheckForDetect(sceneContainerEnabled = true) {
+                kosmos.sceneInteractor.setTransitionState(
+                    MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
+                )
+
+                runCurrent()
+            }
+        }
+
+    @Test
     fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() =
         testScope.runTest {
             testGatingCheckForDetect {
@@ -1052,10 +1138,11 @@
         }
 
     private suspend fun TestScope.testGatingCheckForFaceAuth(
+        sceneContainerEnabled: Boolean = false,
         gatingCheckModifier: suspend () -> Unit
     ) {
         initCollectors()
-        allPreconditionsToRunFaceAuthAreTrue()
+        allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled)
 
         gatingCheckModifier()
         runCurrent()
@@ -1069,7 +1156,7 @@
         faceAuthenticateIsNotCalled()
 
         // flip the gating check back on.
-        allPreconditionsToRunFaceAuthAreTrue()
+        allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled)
         assertThat(underTest.canRunFaceAuth.value).isTrue()
 
         faceAuthenticateIsCalled()
@@ -1094,10 +1181,11 @@
     }
 
     private suspend fun TestScope.testGatingCheckForDetect(
+        sceneContainerEnabled: Boolean = false,
         gatingCheckModifier: suspend () -> Unit
     ) {
         initCollectors()
-        allPreconditionsToRunFaceAuthAreTrue()
+        allPreconditionsToRunFaceAuthAreTrue(sceneContainerEnabled)
 
         // This will stop face auth from running but is required to be false for detect.
         biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
@@ -1145,12 +1233,25 @@
         cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
     }
 
-    private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
+    private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue(
+        sceneContainerEnabled: Boolean = false
+    ) {
         fakeExecutor.runAllReady()
         verify(faceManager, atLeastOnce())
             .addLockoutResetCallback(faceLockoutResetCallback.capture())
         trustRepository.setCurrentUserTrusted(false)
-        keyguardRepository.setKeyguardGoingAway(false)
+        if (sceneContainerEnabled) {
+            // Keyguard is not going away
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(KeyguardState.OFF, KeyguardState.LOCKSCREEN, value = 1.0f),
+                validateStep = false
+            )
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(ObservableTransitionState.Idle(Scenes.Lockscreen))
+            )
+        } else {
+            keyguardRepository.setKeyguardGoingAway(false)
+        }
         powerInteractor.setAwakeForTest()
         biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
         biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 88ba041..296a0fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -88,21 +88,14 @@
     }
 
     @Test
-    fun testWakeUpSetsExitAnimationsRunning() {
-        controller.wakeUp()
-
-        verify(stateController).setExitAnimationsRunning(true)
-    }
-
-    @Test
-    fun testWakeUpAfterStartWillCancel() {
+    fun testOnWakeUpAfterStartWillCancel() {
         val mockStartAnimator: AnimatorSet = mock()
 
         controller.startEntryAnimations(false, animatorBuilder = { mockStartAnimator })
 
         verify(mockStartAnimator, never()).cancel()
 
-        controller.wakeUp()
+        controller.onWakeUp()
 
         // Verify that we cancelled the start animator in favor of the exit
         // animator.
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 7a86e57..7dd7174 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.dreams
 
+import android.app.WindowConfiguration
 import android.content.ComponentName
 import android.content.Intent
 import android.os.RemoteException
@@ -65,11 +66,11 @@
 import com.android.systemui.keyguard.gesture.domain.gestureInteractor
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+import com.android.systemui.navigationbar.gestural.domain.TaskInfo
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
 import com.android.systemui.testKosmos
 import com.android.systemui.touch.TouchInsetManager
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -85,11 +86,18 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.isNull
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
+import org.mockito.kotlin.firstValue
+import org.mockito.kotlin.mock
 import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -115,8 +123,6 @@
 
     @Mock lateinit var mComplicationComponentFactory: ComplicationComponent.Factory
 
-    @Mock lateinit var mComplicationComponent: ComplicationComponent
-
     @Mock lateinit var mComplicationHostViewController: ComplicationHostViewController
 
     @Mock lateinit var mComplicationVisibilityController: ComplicationLayoutEngine
@@ -125,20 +131,12 @@
     lateinit var mDreamComplicationComponentFactory:
         com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
 
-    @Mock
-    lateinit var mDreamComplicationComponent:
-        com.android.systemui.dreams.complication.dagger.ComplicationComponent
-
     @Mock lateinit var mHideComplicationTouchHandler: HideComplicationTouchHandler
 
     @Mock lateinit var mDreamOverlayComponentFactory: DreamOverlayComponent.Factory
 
-    @Mock lateinit var mDreamOverlayComponent: DreamOverlayComponent
-
     @Mock lateinit var mAmbientTouchComponentFactory: AmbientTouchComponent.Factory
 
-    @Mock lateinit var mAmbientTouchComponent: AmbientTouchComponent
-
     @Mock lateinit var mDreamOverlayContainerView: DreamOverlayContainerView
 
     @Mock lateinit var mDreamOverlayContainerViewController: DreamOverlayContainerViewController
@@ -170,10 +168,83 @@
     private lateinit var communalRepository: FakeCommunalSceneRepository
     private var viewCaptureSpy = spy(ViewCaptureFactory.getInstance(context))
     private lateinit var gestureInteractor: GestureInteractor
+    private lateinit var environmentComponents: EnvironmentComponents
 
     @Captor var mViewCaptor: ArgumentCaptor<View>? = null
     private lateinit var mService: DreamOverlayService
 
+    private class EnvironmentComponents(
+        val dreamsComplicationComponent:
+            com.android.systemui.dreams.complication.dagger.ComplicationComponent,
+        val dreamOverlayComponent: DreamOverlayComponent,
+        val complicationComponent: ComplicationComponent,
+        val ambientTouchComponent: AmbientTouchComponent,
+    ) {
+        fun clearInvocations() {
+            clearInvocations(
+                dreamsComplicationComponent,
+                dreamOverlayComponent,
+                complicationComponent,
+                ambientTouchComponent
+            )
+        }
+
+        fun verifyNoMoreInteractions() {
+            Mockito.verifyNoMoreInteractions(
+                dreamsComplicationComponent,
+                dreamOverlayComponent,
+                complicationComponent,
+                ambientTouchComponent
+            )
+        }
+    }
+
+    private fun setupComponentFactories(
+        dreamComplicationComponentFactory:
+            com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory,
+        dreamOverlayComponentFactory: DreamOverlayComponent.Factory,
+        complicationComponentFactory: ComplicationComponent.Factory,
+        ambientTouchComponentFactory: AmbientTouchComponent.Factory
+    ): EnvironmentComponents {
+        val dreamOverlayComponent = mock<DreamOverlayComponent>()
+        whenever(dreamOverlayComponent.getDreamOverlayContainerViewController())
+            .thenReturn(mDreamOverlayContainerViewController)
+
+        val complicationComponent = mock<ComplicationComponent>()
+        whenever(complicationComponent.getComplicationHostViewController())
+            .thenReturn(mComplicationHostViewController)
+        whenever(mLifecycleOwner.registry).thenReturn(lifecycleRegistry)
+
+        mCommunalInteractor = Mockito.spy(kosmos.communalInteractor)
+
+        whenever(complicationComponentFactory.create(any(), any(), any(), any()))
+            .thenReturn(complicationComponent)
+        whenever(complicationComponent.getVisibilityController())
+            .thenReturn(mComplicationVisibilityController)
+
+        val dreamComplicationComponent =
+            mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent>()
+        whenever(dreamComplicationComponent.getHideComplicationTouchHandler())
+            .thenReturn(mHideComplicationTouchHandler)
+        whenever(dreamComplicationComponentFactory.create(any(), any()))
+            .thenReturn(dreamComplicationComponent)
+
+        whenever(dreamOverlayComponentFactory.create(any(), any(), any()))
+            .thenReturn(dreamOverlayComponent)
+
+        val ambientTouchComponent = mock<AmbientTouchComponent>()
+        whenever(ambientTouchComponentFactory.create(any(), any(), any()))
+            .thenReturn(ambientTouchComponent)
+        whenever(ambientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
+
+        return EnvironmentComponents(
+            dreamComplicationComponent,
+            dreamOverlayComponent,
+            complicationComponent,
+            ambientTouchComponent
+        )
+    }
+
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
@@ -183,27 +254,14 @@
         communalRepository = kosmos.fakeCommunalSceneRepository
         gestureInteractor = spy(kosmos.gestureInteractor)
 
-        whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
-            .thenReturn(mDreamOverlayContainerViewController)
-        whenever(mComplicationComponent.getComplicationHostViewController())
-            .thenReturn(mComplicationHostViewController)
-        whenever(mLifecycleOwner.registry).thenReturn(lifecycleRegistry)
+        environmentComponents =
+            setupComponentFactories(
+                mDreamComplicationComponentFactory,
+                mDreamOverlayComponentFactory,
+                mComplicationComponentFactory,
+                mAmbientTouchComponentFactory
+            )
 
-        mCommunalInteractor = Mockito.spy(kosmos.communalInteractor)
-
-        whenever(mComplicationComponentFactory.create(any(), any(), any(), any()))
-            .thenReturn(mComplicationComponent)
-        whenever(mComplicationComponent.getVisibilityController())
-            .thenReturn(mComplicationVisibilityController)
-        whenever(mDreamComplicationComponent.getHideComplicationTouchHandler())
-            .thenReturn(mHideComplicationTouchHandler)
-        whenever(mDreamComplicationComponentFactory.create(any(), any()))
-            .thenReturn(mDreamComplicationComponent)
-        whenever(mDreamOverlayComponentFactory.create(any(), any(), any()))
-            .thenReturn(mDreamOverlayComponent)
-        whenever(mAmbientTouchComponentFactory.create(any(), any()))
-            .thenReturn(mAmbientTouchComponent)
-        whenever(mAmbientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor)
         whenever(mDreamOverlayContainerViewController.containerView)
             .thenReturn(mDreamOverlayContainerView)
         whenever(mScrimManager.getCurrentController()).thenReturn(mScrimController)
@@ -264,6 +322,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -281,6 +340,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -300,6 +360,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -322,6 +383,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -340,6 +402,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -355,6 +418,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -362,6 +426,47 @@
     }
 
     @Test
+    fun testDeferredResetRespondsToAnimationEnd() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*isPreview*/,
+            true /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        whenever(mStateController.areExitAnimationsRunning()).thenReturn(true)
+        clearInvocations(mStateController, mTouchMonitor)
+
+        // Starting a dream will cause it to end first.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*isPreview*/,
+            true /*shouldShowComplication*/
+        )
+
+        mMainExecutor.runAllReady()
+
+        verifyZeroInteractions(mTouchMonitor)
+
+        val captor = ArgumentCaptor.forClass(DreamOverlayStateController.Callback::class.java)
+        verify(mStateController).addCallback(captor.capture())
+
+        whenever(mStateController.areExitAnimationsRunning()).thenReturn(false)
+
+        captor.firstValue.onStateChanged()
+
+        // Should only be called once since it should be null during the second reset.
+        verify(mTouchMonitor).destroy()
+    }
+
+    @Test
     fun testLowLightSetByStartDream() {
         val client = client
 
@@ -370,6 +475,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -386,6 +492,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(),
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -402,6 +509,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -436,6 +544,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         // Immediately end the dream.
@@ -467,6 +576,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -486,6 +596,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             LOW_LIGHT_COMPONENT.flattenToString(),
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -537,6 +648,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -560,6 +672,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -570,9 +683,8 @@
 
         // Assert that the overlay is not showing complications.
         assertThat(mService.shouldShowComplications()).isFalse()
-        Mockito.clearInvocations(mDreamOverlayComponent)
-        Mockito.clearInvocations(mAmbientTouchComponent)
-        Mockito.clearInvocations(mWindowManager)
+        environmentComponents.clearInvocations()
+        clearInvocations(mWindowManager)
 
         // New dream starting with dream complications showing. Note that when a new dream is
         // binding to the dream overlay service, it receives the same instance of IBinder as the
@@ -581,6 +693,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -594,8 +707,8 @@
 
         // Verify that new instances of overlay container view controller and overlay touch monitor
         // are created.
-        verify(mDreamOverlayComponent).getDreamOverlayContainerViewController()
-        verify(mAmbientTouchComponent).getTouchMonitor()
+        verify(environmentComponents.dreamOverlayComponent).getDreamOverlayContainerViewController()
+        verify(environmentComponents.ambientTouchComponent).getTouchMonitor()
 
         // Verify DreamOverlayContainerViewController is destroyed.
         verify(mDreamOverlayContainerViewController).destroy()
@@ -610,18 +723,19 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
         mService.onWakeUp()
-        verify(mDreamOverlayContainerViewController).wakeUp()
+        verify(mDreamOverlayContainerViewController).onWakeUp()
         verify(mDreamOverlayCallbackController).onWakeUp()
     }
 
     @Test
     fun testWakeUpBeforeStartDoesNothing() {
         mService.onWakeUp()
-        verify(mDreamOverlayContainerViewController, Mockito.never()).wakeUp()
+        verify(mDreamOverlayContainerViewController, Mockito.never()).onWakeUp()
     }
 
     @Test
@@ -633,6 +747,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -658,6 +773,7 @@
                 mWindowParams,
                 mDreamOverlayCallback,
                 DREAM_COMPONENT,
+                false /*isPreview*/,
                 false /*shouldShowComplication*/
             )
             mMainExecutor.runAllReady()
@@ -669,7 +785,7 @@
             runCurrent()
             verify(mDreamOverlayCallback).onRedirectWake(true)
             client.onWakeRequested()
-            verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), isNull())
+            verify(mCommunalInteractor).changeScene(eq(CommunalScenes.Communal), any(), isNull())
             verify(mUiEventLogger).log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START)
         }
 
@@ -683,6 +799,7 @@
                 mWindowParams,
                 mDreamOverlayCallback,
                 DREAM_COMPONENT,
+                false /*isPreview*/,
                 false /*shouldShowComplication*/
             )
             // Set communal available, verify that overlay callback is informed.
@@ -711,6 +828,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -731,6 +849,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -750,6 +869,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             true /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -773,6 +893,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -803,6 +924,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         testScope.runCurrent()
@@ -819,6 +941,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -842,6 +965,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -873,6 +997,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -904,6 +1029,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -939,6 +1065,7 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
@@ -965,7 +1092,7 @@
     }
 
     @Test
-    fun testDreamActivityGesturesBlockedOnStart() {
+    fun testDreamActivityGesturesBlockedWhenDreaming() {
         val client = client
 
         // Inform the overlay service of dream starting.
@@ -973,36 +1100,141 @@
             mWindowParams,
             mDreamOverlayCallback,
             DREAM_COMPONENT,
+            false /*isPreview*/,
             false /*shouldShowComplication*/
         )
         mMainExecutor.runAllReady()
-        val captor = argumentCaptor<ComponentName>()
+
+        val matcherCaptor = argumentCaptor<TaskMatcher>()
         verify(gestureInteractor)
-            .addGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
-        assertThat(captor.firstValue.packageName)
-            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
-    }
+            .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global))
+        val matcher = matcherCaptor.firstValue
 
-    @Test
-    fun testDreamActivityGesturesUnblockedOnEnd() {
-        val client = client
-
-        // Inform the overlay service of dream starting.
-        client.startDream(
-            mWindowParams,
-            mDreamOverlayCallback,
-            DREAM_COMPONENT,
-            false /*shouldShowComplication*/
-        )
-        mMainExecutor.runAllReady()
+        val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM)
+        assertThat(matcher.matches(dreamTaskInfo)).isTrue()
 
         client.endDream()
         mMainExecutor.runAllReady()
-        val captor = argumentCaptor<ComponentName>()
+
         verify(gestureInteractor)
-            .removeGestureBlockedActivity(captor.capture(), eq(GestureInteractor.Scope.Global))
-        assertThat(captor.firstValue.packageName)
-            .isEqualTo(ComponentName.unflattenFromString(DREAM_COMPONENT)?.packageName)
+            .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global))
+    }
+
+    @Test
+    fun testDreamActivityGesturesNotBlockedWhenPreview() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            true /*isPreview*/,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        verify(gestureInteractor, never())
+            .addGestureBlockedMatcher(any(), eq(GestureInteractor.Scope.Global))
+    }
+
+    @Test
+    fun testDreamActivityGesturesNotBlockedWhenNotificationShadeShowing() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*isPreview*/,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        val matcherCaptor = argumentCaptor<TaskMatcher>()
+        verify(gestureInteractor)
+            .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global))
+        val matcher = matcherCaptor.firstValue
+
+        val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM)
+        assertThat(matcher.matches(dreamTaskInfo)).isTrue()
+
+        val callbackCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
+
+        // Notification shade opens.
+        callbackCaptor.value.onShadeExpandedChanged(true)
+        mMainExecutor.runAllReady()
+
+        verify(gestureInteractor)
+            .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global))
+    }
+
+    @Test
+    fun testDreamActivityGesturesNotBlockedDreamEndedBeforeKeyguardStateChanged() {
+        val client = client
+
+        // Inform the overlay service of dream starting.
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*isPreview*/,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+
+        val matcherCaptor = argumentCaptor<TaskMatcher>()
+        verify(gestureInteractor)
+            .addGestureBlockedMatcher(matcherCaptor.capture(), eq(GestureInteractor.Scope.Global))
+        val matcher = matcherCaptor.firstValue
+
+        val dreamTaskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_DREAM)
+        assertThat(matcher.matches(dreamTaskInfo)).isTrue()
+
+        client.endDream()
+        mMainExecutor.runAllReady()
+        clearInvocations(gestureInteractor)
+
+        val callbackCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(mKeyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
+
+        // Notification shade opens.
+        callbackCaptor.value.onShadeExpandedChanged(true)
+        mMainExecutor.runAllReady()
+
+        verify(gestureInteractor)
+            .removeGestureBlockedMatcher(eq(matcher), eq(GestureInteractor.Scope.Global))
+    }
+
+    @Test
+    fun testComponentsRecreatedBetweenDreams() {
+        clearInvocations(
+            mDreamComplicationComponentFactory,
+            mDreamOverlayComponentFactory,
+            mComplicationComponentFactory,
+            mAmbientTouchComponentFactory
+        )
+
+        mService.onEndDream()
+
+        setupComponentFactories(
+            mDreamComplicationComponentFactory,
+            mDreamOverlayComponentFactory,
+            mComplicationComponentFactory,
+            mAmbientTouchComponentFactory
+        )
+
+        client.startDream(
+            mWindowParams,
+            mDreamOverlayCallback,
+            DREAM_COMPONENT,
+            false /*isPreview*/,
+            false /*shouldShowComplication*/
+        )
+        mMainExecutor.runAllReady()
+        environmentComponents.verifyNoMoreInteractions()
     }
 
     internal class FakeLifecycleRegistry(provider: LifecycleOwner) : LifecycleRegistry(provider) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
index 5dd6c22..50b727c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.SysuiTestableContext
 import com.android.systemui.contextualeducation.GestureType.BACK
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
@@ -51,6 +52,7 @@
     }
 
     private val testUserId = 1111
+    private val secondTestUserId = 1112
 
     // For deleting any test files created after the test
     @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
@@ -73,12 +75,21 @@
             assertThat(model?.signalCount).isEqualTo(1)
 
             // User is changed.
-            underTest.setUser(1112)
+            underTest.setUser(secondTestUserId)
             // Assert count is 0 after user is changed.
             assertThat(model?.signalCount).isEqualTo(0)
         }
 
     @Test
+    fun changeUserIdForNewUser() =
+        testScope.runTest {
+            val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
+            assertThat(model?.userId).isEqualTo(testUserId)
+            underTest.setUser(secondTestUserId)
+            assertThat(model?.userId).isEqualTo(secondTestUserId)
+        }
+
+    @Test
     fun dataChangedOnUpdate() =
         testScope.runTest {
             val newModel =
@@ -88,12 +99,26 @@
                     lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(),
                     lastEducationTime = kosmos.fakeEduClock.instant(),
                     usageSessionStartTime = kosmos.fakeEduClock.instant(),
+                    userId = testUserId
                 )
             underTest.updateGestureEduModel(BACK) { newModel }
             val model by collectLastValue(underTest.readGestureEduModelFlow(BACK))
             assertThat(model).isEqualTo(newModel)
         }
 
+    @Test
+    fun eduDeviceConnectionTimeDataChangedOnUpdate() =
+        testScope.runTest {
+            val newModel =
+                EduDeviceConnectionTime(
+                    keyboardFirstConnectionTime = kosmos.fakeEduClock.instant(),
+                    touchpadFirstConnectionTime = kosmos.fakeEduClock.instant(),
+                )
+            underTest.updateEduDeviceConnectionTime { newModel }
+            val model by collectLastValue(underTest.readEduDeviceConnectionTime())
+            assertThat(model).isEqualTo(newModel)
+        }
+
     /** Test context which allows overriding getFilesDir path */
     private class TestContext(context: Context, private val folder: File) :
         SysuiTestableContext(context) {
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 6867089..25c5336 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
@@ -16,38 +16,61 @@
 
 package com.android.systemui.education.domain.interactor
 
+import android.content.pm.UserInfo
+import android.hardware.input.InputManager
+import android.hardware.input.KeyGestureEvent
+import android.view.KeyEvent
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.contextualeducation.GestureType
+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.coroutines.collectValues
 import com.android.systemui.education.data.model.GestureEduModel
 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.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.android.systemui.user.data.repository.fakeUserRepository
 import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
 import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.test.TestScope
 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.ArgumentCaptor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
[email protected]
 class KeyboardTouchpadEduInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val contextualEduInteractor = kosmos.contextualEducationInteractor
+    private val touchpadRepository = kosmos.touchpadRepository
+    private val keyboardRepository = kosmos.keyboardRepository
+    private val userRepository = kosmos.fakeUserRepository
+
     private val underTest: KeyboardTouchpadEduInteractor = kosmos.keyboardTouchpadEduInteractor
     private val eduClock = kosmos.fakeEduClock
+    private val minDurationForNextEdu =
+        KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
 
     @Before
     fun setup() {
         underTest.start()
+        contextualEduInteractor.start()
+        userRepository.setUserInfos(USER_INFOS)
     }
 
     @Test
@@ -67,14 +90,16 @@
         }
 
     @Test
-    @kotlinx.coroutines.ExperimentalCoroutinesApi
     fun newEducationNotificationOn2ndEducation() =
         testScope.runTest {
             val model by collectLastValue(underTest.educationTriggered)
             triggerMaxEducationSignals(BACK)
             // runCurrent() to trigger 1st education
             runCurrent()
+
+            eduClock.offset(minDurationForNextEdu)
             triggerMaxEducationSignals(BACK)
+
             assertThat(model?.educationUiType).isEqualTo(EducationUiType.Notification)
         }
 
@@ -96,6 +121,39 @@
         }
 
     @Test
+    fun no2ndEducationBeforeMinEduIntervalReached() =
+        testScope.runTest {
+            val models by collectValues(underTest.educationTriggered)
+            triggerMaxEducationSignals(BACK)
+            runCurrent()
+
+            // Offset a duration that is less than the required education interval
+            eduClock.offset(1.seconds)
+            triggerMaxEducationSignals(BACK)
+            runCurrent()
+
+            assertThat(models.filterNotNull().size).isEqualTo(1)
+        }
+
+    @Test
+    fun noNewEducationInfoAfterMaxEducationCountReached() =
+        testScope.runTest {
+            val models by collectValues(underTest.educationTriggered)
+            // Trigger 2 educations
+            triggerMaxEducationSignals(BACK)
+            runCurrent()
+            eduClock.offset(minDurationForNextEdu)
+            triggerMaxEducationSignals(BACK)
+            runCurrent()
+
+            // Try triggering 3rd education
+            eduClock.offset(minDurationForNextEdu)
+            triggerMaxEducationSignals(BACK)
+
+            assertThat(models.filterNotNull().size).isEqualTo(2)
+        }
+
+    @Test
     fun startNewUsageSessionWhen2ndSignalReceivedAfterSessionDeadline() =
         testScope.runTest {
             val model by
@@ -109,15 +167,136 @@
                 .isEqualTo(
                     GestureEduModel(
                         signalCount = 1,
-                        usageSessionStartTime = secondSignalReceivedTime
+                        usageSessionStartTime = secondSignalReceivedTime,
+                        userId = 0
                     )
                 )
         }
 
+    @Test
+    fun newTouchpadConnectionTimeOnFirstTouchpadConnected() =
+        testScope.runTest {
+            setIsAnyTouchpadConnected(true)
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.touchpadFirstConnectionTime).isEqualTo(eduClock.instant())
+        }
+
+    @Test
+    fun unchangedTouchpadConnectionTimeOnSecondConnection() =
+        testScope.runTest {
+            val firstConnectionTime = eduClock.instant()
+            setIsAnyTouchpadConnected(true)
+            setIsAnyTouchpadConnected(false)
+
+            eduClock.offset(1.hours)
+            setIsAnyTouchpadConnected(true)
+
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.touchpadFirstConnectionTime).isEqualTo(firstConnectionTime)
+        }
+
+    @Test
+    fun newTouchpadConnectionTimeOnUserChanged() =
+        testScope.runTest {
+            // Touchpad connected for user 0
+            setIsAnyTouchpadConnected(true)
+
+            // Change user
+            eduClock.offset(1.hours)
+            val newUserFirstConnectionTime = eduClock.instant()
+            userRepository.setSelectedUserInfo(USER_INFOS[0])
+            runCurrent()
+
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.touchpadFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+        }
+
+    @Test
+    fun newKeyboardConnectionTimeOnKeyboardConnected() =
+        testScope.runTest {
+            setIsAnyKeyboardConnected(true)
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.keyboardFirstConnectionTime).isEqualTo(eduClock.instant())
+        }
+
+    @Test
+    fun unchangedKeyboardConnectionTimeOnSecondConnection() =
+        testScope.runTest {
+            val firstConnectionTime = eduClock.instant()
+            setIsAnyKeyboardConnected(true)
+            setIsAnyKeyboardConnected(false)
+
+            eduClock.offset(1.hours)
+            setIsAnyKeyboardConnected(true)
+
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.keyboardFirstConnectionTime).isEqualTo(firstConnectionTime)
+        }
+
+    @Test
+    fun newKeyboardConnectionTimeOnUserChanged() =
+        testScope.runTest {
+            // Keyboard connected for user 0
+            setIsAnyKeyboardConnected(true)
+
+            // Change user
+            eduClock.offset(1.hours)
+            val newUserFirstConnectionTime = eduClock.instant()
+            userRepository.setSelectedUserInfo(USER_INFOS[0])
+            runCurrent()
+
+            val model = contextualEduInteractor.getEduDeviceConnectionTime()
+            assertThat(model.keyboardFirstConnectionTime).isEqualTo(newUserFirstConnectionTime)
+        }
+
+    @Test
+    fun updateShortcutTimeOnKeyboardShortcutTriggered() =
+        testScope.runTest {
+            // runCurrent() to trigger inputManager#registerKeyGestureEventListener in the
+            // interactor
+            runCurrent()
+            val listenerCaptor =
+                ArgumentCaptor.forClass(InputManager.KeyGestureEventListener::class.java)
+            verify(kosmos.mockEduInputManager)
+                .registerKeyGestureEventListener(any(), listenerCaptor.capture())
+
+            val allAppsKeyGestureEvent =
+                KeyGestureEvent(
+                    /* deviceId= */ 1,
+                    IntArray(0),
+                    KeyEvent.META_META_ON,
+                    KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS
+                )
+            listenerCaptor.value.onKeyGestureEvent(allAppsKeyGestureEvent)
+
+            val model by
+                collectLastValue(
+                    kosmos.contextualEducationRepository.readGestureEduModelFlow(ALL_APPS)
+                )
+            assertThat(model?.lastShortcutTriggeredTime).isEqualTo(eduClock.instant())
+        }
+
     private suspend fun triggerMaxEducationSignals(gestureType: GestureType) {
         // Increment max number of signal to try triggering education
         for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
             contextualEduInteractor.incrementSignalCount(gestureType)
         }
     }
+
+    private fun TestScope.setIsAnyTouchpadConnected(isConnected: Boolean) {
+        touchpadRepository.setIsAnyTouchpadConnected(isConnected)
+        runCurrent()
+    }
+
+    private fun TestScope.setIsAnyKeyboardConnected(isConnected: Boolean) {
+        keyboardRepository.setIsAnyKeyboardConnected(isConnected)
+        runCurrent()
+    }
+
+    companion object {
+        private val USER_INFOS =
+            listOf(
+                UserInfo(101, "Second User", 0),
+            )
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
new file mode 100644
index 0000000..c4ac585
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.ui.view
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.content.applicationContext
+import android.widget.Toast
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.domain.interactor.contextualEducationInteractor
+import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+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 kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.test.TestScope
+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.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+class ContextualEduUiCoordinatorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val interactor = kosmos.contextualEducationInteractor
+    private val eduClock = kosmos.fakeEduClock
+    private val minDurationForNextEdu =
+        KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds
+    private lateinit var underTest: ContextualEduUiCoordinator
+    @Mock private lateinit var toast: Toast
+    @Mock private lateinit var notificationManager: NotificationManager
+    @get:Rule val mockitoRule = MockitoJUnit.rule()
+    private var toastContent = ""
+
+    @Before
+    fun setUp() {
+        val viewModel =
+            ContextualEduViewModel(
+                kosmos.applicationContext.resources,
+                kosmos.keyboardTouchpadEduInteractor
+            )
+        underTest =
+            ContextualEduUiCoordinator(
+                kosmos.applicationCoroutineScope,
+                viewModel,
+                kosmos.applicationContext,
+                notificationManager
+            ) { content ->
+                toastContent = content
+                toast
+            }
+        underTest.start()
+        kosmos.keyboardTouchpadEduInteractor.start()
+    }
+
+    @Test
+    fun showToastOnNewEdu() =
+        testScope.runTest {
+            triggerEducation(BACK)
+            verify(toast).show()
+        }
+
+    @Test
+    fun showNotificationOn2ndEdu() =
+        testScope.runTest {
+            triggerEducation(BACK)
+            eduClock.offset(minDurationForNextEdu)
+            triggerEducation(BACK)
+            verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any())
+        }
+
+    @Test
+    fun verifyBackEduToastContent() =
+        testScope.runTest {
+            triggerEducation(BACK)
+            assertThat(toastContent).isEqualTo(context.getString(R.string.back_edu_toast_content))
+        }
+
+    @Test
+    fun verifyBackEduNotificationContent() =
+        testScope.runTest {
+            val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+            triggerEducation(BACK)
+
+            eduClock.offset(minDurationForNextEdu)
+            triggerEducation(BACK)
+
+            verify(notificationManager)
+                .notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any())
+            verifyNotificationContent(
+                R.string.back_edu_notification_title,
+                R.string.back_edu_notification_content,
+                notificationCaptor.value
+            )
+        }
+
+    private fun verifyNotificationContent(
+        titleResId: Int,
+        contentResId: Int,
+        notification: Notification
+    ) {
+        val expectedContent = context.getString(contentResId)
+        val expectedTitle = context.getString(titleResId)
+        val actualContent = notification.getString(Notification.EXTRA_TEXT)
+        val actualTitle = notification.getString(Notification.EXTRA_TITLE)
+        assertThat(actualContent).isEqualTo(expectedContent)
+        assertThat(actualTitle).isEqualTo(expectedTitle)
+    }
+
+    private fun Notification.getString(key: String): String =
+        this.extras?.getCharSequence(key).toString()
+
+    private suspend fun TestScope.triggerEducation(gestureType: GestureType) {
+        for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+            interactor.incrementSignalCount(gestureType)
+        }
+        runCurrent()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
index 91d37cf..a764256d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/data/GestureRepositoryTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.gesture.data
 
+import android.app.WindowConfiguration
 import android.content.ComponentName
 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.navigationbar.gestural.data.respository.GestureRepositoryImpl
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -40,14 +42,36 @@
     @Test
     fun addRemoveComponentToBlock_updatesBlockedComponentSet() =
         testScope.runTest {
-            val component = mock<ComponentName>()
+            val matcher = TaskMatcher.TopActivityComponent(mock<ComponentName>())
 
-            underTest.addGestureBlockedActivity(component)
-            val addedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
-            assertThat(addedBlockedComponents).contains(component)
+            kotlin.run {
+                underTest.addGestureBlockedMatcher(matcher)
+                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
+                assertThat(blockedMatchers).contains(matcher)
+            }
 
-            underTest.removeGestureBlockedActivity(component)
-            val removedBlockedComponents by collectLastValue(underTest.gestureBlockedActivities)
-            assertThat(removedBlockedComponents).isEmpty()
+            kotlin.run {
+                underTest.removeGestureBlockedMatcher(matcher)
+                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
+                assertThat(blockedMatchers).doesNotContain(matcher)
+            }
+        }
+
+    @Test
+    fun addRemoveActivityTypeToBlock_updatesBlockedActivityTypesSet() =
+        testScope.runTest {
+            val matcher = TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_STANDARD)
+
+            kotlin.run {
+                underTest.addGestureBlockedMatcher(matcher)
+                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
+                assertThat(blockedMatchers).contains(matcher)
+            }
+
+            kotlin.run {
+                underTest.removeGestureBlockedMatcher(matcher)
+                val blockedMatchers by collectLastValue(underTest.gestureBlockedMatchers)
+                assertThat(blockedMatchers).doesNotContain(matcher)
+            }
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
index bc142e6..0ce0d93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/GestureInteractorTest.kt
@@ -16,120 +16,138 @@
 
 package com.android.systemui.gesture.domain
 
+import android.app.ActivityManager
 import android.content.ComponentName
 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.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigationbar.gestural.data.gestureRepository
 import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
+import com.android.systemui.shared.system.activityManagerWrapper
+import com.android.systemui.shared.system.taskStackChangeListeners
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.resetMain
-import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
-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.MockitoRule
-import org.mockito.kotlin.any
-import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
-import org.mockito.kotlin.verify
+import org.mockito.kotlin.spy
 import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class GestureInteractorTest : SysuiTestCase() {
     @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+    private val kosmos = testKosmos()
 
-    val dispatcher = StandardTestDispatcher()
+    val dispatcher = kosmos.testDispatcher
+    val repository = spy(kosmos.gestureRepository)
     val testScope = TestScope(dispatcher)
 
-    @Mock private lateinit var gestureRepository: GestureRepository
+    private val underTest by lazy { createInteractor() }
 
-    private val underTest by lazy {
-        GestureInteractor(gestureRepository, testScope.backgroundScope)
+    private fun createInteractor(): GestureInteractor {
+        return GestureInteractor(
+            repository,
+            dispatcher,
+            kosmos.backgroundCoroutineContext,
+            testScope,
+            kosmos.activityManagerWrapper,
+            kosmos.taskStackChangeListeners
+        )
     }
 
-    @Before
-    fun setup() {
-        Dispatchers.setMain(dispatcher)
-        whenever(gestureRepository.gestureBlockedActivities).thenReturn(MutableStateFlow(setOf()))
-    }
+    private fun setTopActivity(componentName: ComponentName) {
+        val task = mock<ActivityManager.RunningTaskInfo>()
+        task.topActivity = componentName
+        whenever(kosmos.activityManagerWrapper.runningTask).thenReturn(task)
 
-    @After
-    fun tearDown() {
-        Dispatchers.resetMain()
+        kosmos.taskStackChangeListeners.listenerImpl.onTaskStackChanged()
     }
 
     @Test
     fun addBlockedActivity_testCombination() =
         testScope.runTest {
             val globalComponent = mock<ComponentName>()
-            whenever(gestureRepository.gestureBlockedActivities)
-                .thenReturn(MutableStateFlow(setOf(globalComponent)))
+            repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent))
+
             val localComponent = mock<ComponentName>()
-            underTest.addGestureBlockedActivity(localComponent, GestureInteractor.Scope.Local)
-            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
-            testScope.runCurrent()
-            verify(gestureRepository, never()).addGestureBlockedActivity(any())
-            assertThat(lastSeen).hasSize(2)
-            assertThat(lastSeen).containsExactly(globalComponent, localComponent)
+
+            val blocked by collectLastValue(underTest.topActivityBlocked)
+
+            underTest.addGestureBlockedMatcher(
+                TaskMatcher.TopActivityComponent(localComponent),
+                GestureInteractor.Scope.Local
+            )
+
+            assertThat(blocked).isFalse()
+
+            setTopActivity(localComponent)
+
+            assertThat(blocked).isTrue()
+        }
+
+    @Test
+    fun initialization_testEmit() =
+        testScope.runTest {
+            val globalComponent = mock<ComponentName>()
+            repository.addGestureBlockedMatcher(TaskMatcher.TopActivityComponent(globalComponent))
+            setTopActivity(globalComponent)
+
+            val interactor = createInteractor()
+
+            val blocked by collectLastValue(interactor.topActivityBlocked)
+            assertThat(blocked).isTrue()
         }
 
     @Test
     fun addBlockedActivityLocally_onlyAffectsLocalInteractor() =
         testScope.runTest {
-            val component = mock<ComponentName>()
-            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
-            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
-            testScope.runCurrent()
-            verify(gestureRepository, never()).addGestureBlockedActivity(any())
-            assertThat(lastSeen).contains(component)
+            val interactor1 = createInteractor()
+            val interactor1Blocked by collectLastValue(interactor1.topActivityBlocked)
+            val interactor2 = createInteractor()
+            val interactor2Blocked by collectLastValue(interactor2.topActivityBlocked)
+
+            val localComponent = mock<ComponentName>()
+
+            interactor1.addGestureBlockedMatcher(
+                TaskMatcher.TopActivityComponent(localComponent),
+                GestureInteractor.Scope.Local
+            )
+            setTopActivity(localComponent)
+
+            assertThat(interactor1Blocked).isTrue()
+            assertThat(interactor2Blocked).isFalse()
         }
 
     @Test
-    fun removeBlockedActivityLocally_onlyAffectsLocalInteractor() =
+    fun matchingBlockers_separatelyManaged() =
         testScope.runTest {
-            val component = mock<ComponentName>()
-            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Local)
-            val lastSeen by collectLastValue(underTest.gestureBlockedActivities)
-            testScope.runCurrent()
-            underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Local)
-            testScope.runCurrent()
-            verify(gestureRepository, never()).removeGestureBlockedActivity(any())
-            assertThat(lastSeen).isEmpty()
-        }
+            val interactor = createInteractor()
+            val interactorBlocked by collectLastValue(interactor.topActivityBlocked)
 
-    @Test
-    fun addBlockedActivity_invokesRepository() =
-        testScope.runTest {
-            val component = mock<ComponentName>()
-            underTest.addGestureBlockedActivity(component, GestureInteractor.Scope.Global)
-            runCurrent()
-            val captor = argumentCaptor<ComponentName>()
-            verify(gestureRepository).addGestureBlockedActivity(captor.capture())
-            assertThat(captor.firstValue).isEqualTo(component)
-        }
+            val localComponent = mock<ComponentName>()
 
-    @Test
-    fun removeBlockedActivity_invokesRepository() =
-        testScope.runTest {
-            val component = mock<ComponentName>()
-            underTest.removeGestureBlockedActivity(component, GestureInteractor.Scope.Global)
-            runCurrent()
-            val captor = argumentCaptor<ComponentName>()
-            verify(gestureRepository).removeGestureBlockedActivity(captor.capture())
-            assertThat(captor.firstValue).isEqualTo(component)
+            val matcher1 = TaskMatcher.TopActivityComponent(localComponent)
+            val matcher2 = TaskMatcher.TopActivityComponent(localComponent)
+
+            interactor.addGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local)
+            interactor.addGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local)
+            setTopActivity(localComponent)
+            assertThat(interactorBlocked).isTrue()
+
+            interactor.removeGestureBlockedMatcher(matcher1, GestureInteractor.Scope.Local)
+            assertThat(interactorBlocked).isTrue()
+
+            interactor.removeGestureBlockedMatcher(matcher2, GestureInteractor.Scope.Local)
+            assertThat(interactorBlocked).isFalse()
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt
new file mode 100644
index 0000000..a246270
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/gesture/domain/TaskMatcherTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.gesture.domain
+
+import android.app.WindowConfiguration
+import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.navigationbar.gestural.domain.TaskInfo
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TaskMatcherTest : SysuiTestCase() {
+    @Test
+    fun activityMatcher_matchesComponentName() {
+        val componentName = ComponentName.unflattenFromString("com.foo/.bar")!!
+        val matcher = TaskMatcher.TopActivityComponent(componentName)
+
+        val taskInfo = TaskInfo(componentName, WindowConfiguration.ACTIVITY_TYPE_STANDARD)
+        assertThat(matcher.matches(taskInfo)).isTrue()
+    }
+
+    @Test
+    fun activityMatcher_doesNotMatchComponentName() {
+        val componentName = ComponentName.unflattenFromString("com.foo/.bar")!!
+        val matcher = TaskMatcher.TopActivityComponent(componentName)
+
+        val taskInfo =
+            TaskInfo(
+                ComponentName.unflattenFromString("com.bar/.baz"),
+                WindowConfiguration.ACTIVITY_TYPE_STANDARD
+            )
+        assertThat(matcher.matches(taskInfo)).isFalse()
+    }
+
+    @Test
+    fun activityMatcher_matchesActivityType() {
+        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
+        val matcher = TaskMatcher.TopActivityType(activityType)
+
+        val taskInfo = TaskInfo(mock<ComponentName>(), activityType)
+        assertThat(matcher.matches(taskInfo)).isTrue()
+    }
+
+    @Test
+    fun activityMatcher_doesNotMatchEmptyActivityType() {
+        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
+        val matcher = TaskMatcher.TopActivityType(activityType)
+
+        val taskInfo = TaskInfo(null, activityType)
+        assertThat(matcher.matches(taskInfo)).isFalse()
+    }
+
+    @Test
+    fun activityMatcher_doesNotMatchActivityType() {
+        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
+        val matcher = TaskMatcher.TopActivityType(activityType)
+
+        val taskInfo = TaskInfo(mock<ComponentName>(), WindowConfiguration.ACTIVITY_TYPE_STANDARD)
+        assertThat(matcher.matches(taskInfo)).isFalse()
+    }
+
+    @Test
+    fun activityMatcher_equivalentMatchersAreNotEqual() {
+        val activityType = WindowConfiguration.ACTIVITY_TYPE_HOME
+        val matcher1 = TaskMatcher.TopActivityType(activityType)
+        val matcher2 = TaskMatcher.TopActivityType(activityType)
+
+        assertThat(matcher1).isNotEqualTo(matcher2)
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index fd4ed38..686b518 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -23,7 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ActivityTransitionAnimator
-import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.haptics.fakeVibratorHelper
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.qs.qsTileFactory
@@ -50,7 +50,7 @@
 
     @Rule @JvmField val mMockitoRule: MockitoRule = MockitoJUnit.rule()
     private val kosmos = testKosmos()
-    private val vibratorHelper = kosmos.vibratorHelper
+    private val vibratorHelper = kosmos.fakeVibratorHelper
     private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
     @Mock private lateinit var callback: QSLongPressEffect.Callback
     @Mock private lateinit var controller: ActivityTransitionAnimator.Controller
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
index bbfaf6f..6c3c7ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -31,19 +31,19 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.ZenModeController
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
@@ -63,27 +63,24 @@
 @RunWith(AndroidJUnit4::class)
 class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
+
     @Mock private lateinit var zenModeController: ZenModeController
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var conditionUri: Uri
     @Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
     @Captor private lateinit var spyZenMode: ArgumentCaptor<Int>
     @Captor private lateinit var spyConditionId: ArgumentCaptor<Uri?>
-    private lateinit var settings: FakeSettings
 
     private lateinit var underTest: DoNotDisturbQuickAffordanceConfig
-    private lateinit var testDispatcher: TestDispatcher
-    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
-
-        settings = FakeSettings()
-
         underTest =
             DoNotDisturbQuickAffordanceConfig(
                 context,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb23..0145f17 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -23,19 +23,19 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -51,14 +51,15 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceLegacySettingSyncerTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.unconfinedTestDispatcher
+    private val testScope = kosmos.unconfinedTestScope
+    private val settings = kosmos.unconfinedDispatcherFakeSettings
+
     @Mock private lateinit var sharedPrefs: FakeSharedPreferences
 
     private lateinit var underTest: KeyguardQuickAffordanceLegacySettingSyncer
-
-    private lateinit var testScope: TestScope
-    private lateinit var testDispatcher: TestDispatcher
     private lateinit var selectionManager: KeyguardQuickAffordanceLocalUserSelectionManager
-    private lateinit var settings: FakeSettings
 
     @Before
     fun setUp() {
@@ -73,8 +74,6 @@
         whenever(resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)).thenReturn(true)
         whenever(context.resources).thenReturn(resources)
 
-        testDispatcher = UnconfinedTestDispatcher()
-        testScope = TestScope(testDispatcher)
         selectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
@@ -92,7 +91,6 @@
                 userTracker = FakeUserTracker(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
-        settings = FakeSettings()
         settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0)
         settings.putInt(Settings.Secure.LOCKSCREEN_SHOW_WALLET, 0)
         settings.putInt(Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index c5ba02d..4e429c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -46,11 +46,10 @@
 import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT
 import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT
 import com.android.systemui.keyguard.shared.model.DevicePosture
-import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
@@ -81,6 +80,8 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4::class)
 class BiometricSettingsRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: BiometricSettingsRepository
 
     @Mock private lateinit var authController: AuthController
@@ -88,7 +89,6 @@
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var biometricManager: BiometricManager
-    @Mock private lateinit var tableLogger: TableLogBuffer
     @Captor
     private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
     @Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@@ -99,7 +99,7 @@
     private lateinit var devicePostureRepository: FakeDevicePostureRepository
     private lateinit var facePropertyRepository: FakeFacePropertyRepository
     private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
-    private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
+    private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
 
     private lateinit var testDispatcher: TestDispatcher
     private lateinit var testScope: TestScope
@@ -115,8 +115,6 @@
         devicePostureRepository = FakeDevicePostureRepository()
         facePropertyRepository = FakeFacePropertyRepository()
         fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
-        mobileConnectionsRepository =
-            FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogger)
     }
 
     private suspend fun createBiometricSettingsRepository() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 3a28471..9bcc19d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -369,4 +369,18 @@
             invokeOnCallback { it.onStrongAuthStateChanged(0) }
             assertThat(shouldUpdateIndicatorVisibility).isTrue()
         }
+
+    @Test
+    fun isLockedOut_initialStateFalse() =
+        testScope.runTest {
+            whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+            assertThat(underTest.isLockedOut.value).isEqualTo(false)
+        }
+
+    @Test
+    fun isLockedOut_initialStateTrue() =
+        testScope.runTest {
+            whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
+            assertThat(underTest.isLockedOut.value).isEqualTo(true)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 8e109b4..1582e47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -31,20 +31,21 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.testKosmos
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
@@ -58,6 +59,11 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
+
     private lateinit var underTest: KeyguardQuickAffordanceRepository
 
     private lateinit var config1: FakeKeyguardQuickAffordanceConfig
@@ -65,7 +71,6 @@
     private lateinit var userTracker: FakeUserTracker
     private lateinit var client1: FakeCustomizationProviderClient
     private lateinit var client2: FakeCustomizationProviderClient
-    private lateinit var testScope: TestScope
 
     @Before
     fun setUp() {
@@ -73,8 +78,6 @@
         context.resources.configuration.setLayoutDirection(Locale.US)
         config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
         config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
-        val testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
         userTracker = FakeUserTracker()
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
@@ -128,7 +131,7 @@
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = testDispatcher,
-                        secureSettings = FakeSettings(),
+                        secureSettings = settings,
                         selectionsManager = localUserSelectionManager,
                     ),
                 configs = setOf(config1, config2),
@@ -308,6 +311,15 @@
             )
         }
 
+    @Test
+    fun getConfig() =
+        testScope.runTest {
+            assertThat(underTest.getConfig(FakeCustomizationProviderClient.AFFORDANCE_1))
+                .isEqualTo(config1)
+            assertThat(underTest.getConfig(FakeCustomizationProviderClient.AFFORDANCE_2))
+                .isEqualTo(config2)
+        }
+
     private fun assertSelections(
         observed: Map<String, List<KeyguardQuickAffordanceConfig>>?,
         expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 3b8ffcd..17e3006 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -116,7 +116,7 @@
             reset(transitionRepository)
 
             // Authentication results in calling startDismissKeyguardTransition.
-            kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+            kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition()
             runCurrent()
 
             assertThat(transitionRepository)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
index 6eb9862..33f3cd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt
@@ -39,12 +39,15 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
@@ -53,6 +56,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.testKosmos
 import junit.framework.Assert.assertEquals
@@ -166,6 +170,10 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
         testScope.runTest {
+            val isGone by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -175,7 +183,7 @@
             runCurrent()
 
             // Make sure we're GONE.
-            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+            assertEquals(true, isGone)
 
             // Get part way to AOD.
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -204,6 +212,10 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
         testScope.runTest {
+            val isGone by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -213,7 +225,7 @@
             runCurrent()
 
             // Make sure we're GONE.
-            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+            assertEquals(true, isGone)
 
             // Get all the way to AOD
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -239,6 +251,10 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
         testScope.runTest {
+            val isLockscreen by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -248,10 +264,7 @@
             runCurrent()
 
             // Make sure we're in LOCKSCREEN.
-            assertEquals(
-                KeyguardState.LOCKSCREEN,
-                kosmos.keyguardTransitionInteractor.getFinishedState()
-            )
+            assertEquals(true, isLockscreen)
 
             // Get part way to AOD.
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -314,7 +327,7 @@
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardShowing(false)
             kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
-            kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition()
+            kosmos.keyguardDismissTransitionInteractor.startDismissKeyguardTransition()
             powerInteractor.setAwakeForTest()
             advanceTimeBy(100) // account for debouncing
 
@@ -327,6 +340,10 @@
     @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetectedInAod_fromGone() =
         testScope.runTest {
+            val isGone by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.AOD,
@@ -336,7 +353,7 @@
             runCurrent()
 
             // Make sure we're GONE.
-            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+            assertEquals(true, isGone)
 
             // Start going to AOD on first button push
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 783e3b5..c18deb1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -48,12 +48,15 @@
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -62,6 +65,7 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -150,13 +154,13 @@
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun testTransitionToLockscreen_onPowerButtonPress_canDream_glanceableHubAvailable() =
+    fun testTransitionToLockscreen_onWake_canDream_glanceableHubAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
             kosmos.setCommunalAvailable(true)
             runCurrent()
 
-            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
+            powerInteractor.setAwakeForTest()
             runCurrent()
 
             // If dreaming is possible and communal is available, then we should transition to
@@ -170,14 +174,14 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR, FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun testTransitionToLockscreen_onPowerButtonPress_canDream_ktfRefactor() =
+    fun testTransitionToLockscreen_onWake_canDream_ktfRefactor() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
             kosmos.setCommunalAvailable(true)
             runCurrent()
             clearInvocations(kosmos.fakeCommunalSceneRepository)
 
-            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
+            powerInteractor.setAwakeForTest()
             runCurrent()
 
             // If dreaming is possible and communal is available, then we should transition to
@@ -188,13 +192,13 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToLockscreen_onPowerButtonPress_canNotDream_glanceableHubAvailable() =
+    fun testTransitionToLockscreen_onWake_canNotDream_glanceableHubAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(false)
             kosmos.setCommunalAvailable(true)
             runCurrent()
 
-            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
+            powerInteractor.setAwakeForTest()
             runCurrent()
 
             // If dreaming is NOT possible but communal is available, then we should transition to
@@ -208,13 +212,13 @@
 
     @Test
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToLockscreen_onPowerButtonPress_canNDream_glanceableHubNotAvailable() =
+    fun testTransitionToLockscreen_onWake_canNDream_glanceableHubNotAvailable() =
         testScope.runTest {
             whenever(kosmos.dreamManager.canStartDreaming(anyBoolean())).thenReturn(true)
             kosmos.setCommunalAvailable(false)
             runCurrent()
 
-            powerInteractor.setAwakeForTest(reason = PowerManager.WAKE_REASON_POWER_BUTTON)
+            powerInteractor.setAwakeForTest()
             runCurrent()
 
             // If dreaming is possible but communal is NOT available, then we should transition to
@@ -316,6 +320,10 @@
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromGone() =
         testScope.runTest {
+            val isGone by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.DOZING,
@@ -325,7 +333,7 @@
             runCurrent()
 
             // Make sure we're GONE.
-            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+            assertEquals(true, isGone)
 
             // Get part way to AOD.
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -355,6 +363,10 @@
     @Suppress("ktlint:standard:max-line-length")
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
         testScope.runTest {
+            val isGone by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Gone, GONE)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.DOZING,
@@ -364,7 +376,7 @@
             runCurrent()
 
             // Make sure we're GONE.
-            assertEquals(KeyguardState.GONE, kosmos.keyguardTransitionInteractor.getFinishedState())
+            assertEquals(true, isGone)
 
             // Get all the way to AOD
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
@@ -390,6 +402,10 @@
     @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromLockscreen() =
         testScope.runTest {
+            val isLockscreen by
+                collectLastValue(
+                    kosmos.keyguardTransitionInteractor.isFinishedIn(Scenes.Lockscreen, LOCKSCREEN)
+                )
             powerInteractor.setAwakeForTest()
             transitionRepository.sendTransitionSteps(
                 from = KeyguardState.DOZING,
@@ -399,10 +415,7 @@
             runCurrent()
 
             // Make sure we're in LOCKSCREEN.
-            assertEquals(
-                KeyguardState.LOCKSCREEN,
-                kosmos.keyguardTransitionInteractor.getFinishedState()
-            )
+            assertEquals(true, isLockscreen)
 
             // Get part way to AOD.
             powerInteractor.onStartedGoingToSleep(PowerManager.GO_TO_SLEEP_REASON_MIN)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 75c0d3b..a8bb2b0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -61,7 +61,7 @@
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -80,6 +80,10 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
+
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var userTracker: UserTracker
@@ -90,11 +94,8 @@
     @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
     @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
 
-    private val kosmos = testKosmos()
-
     private lateinit var underTest: KeyguardQuickAffordanceInteractor
 
-    private val testScope = kosmos.testScope
     private lateinit var repository: FakeKeyguardRepository
     private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
     private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
@@ -170,7 +171,7 @@
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = kosmos.testDispatcher,
-                        secureSettings = FakeSettings(),
+                        secureSettings = settings,
                         selectionsManager = localUserSelectionManager,
                     ),
                 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
@@ -426,6 +427,64 @@
         }
 
     @Test
+    fun quickAffordanceAlwaysVisible_withNonNullOverrideKeyguardQuickAffordanceId() =
+        testScope.runTest {
+            quickAccessWallet.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                    activationState = ActivationState.Active,
+                )
+            )
+            homeControls.setState(
+                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                    icon = ICON,
+                    activationState = ActivationState.Active,
+                )
+            )
+
+            // The default case
+            val collectedValue =
+                collectLastValue(
+                    underTest.quickAffordanceAlwaysVisible(
+                        KeyguardQuickAffordancePosition.BOTTOM_START,
+                    )
+                )
+            assertThat(collectedValue())
+                .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
+            val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
+            assertThat(visibleModel.configKey)
+                .isEqualTo(
+                    "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${homeControls.key}"
+                )
+            assertThat(visibleModel.icon).isEqualTo(ICON)
+            assertThat(visibleModel.icon.contentDescription)
+                .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
+            assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active)
+
+            // With override
+            val collectedValueWithOverride =
+                collectLastValue(
+                    underTest.quickAffordanceAlwaysVisible(
+                        position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                        overrideQuickAffordanceId =
+                            BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
+                    )
+                )
+            assertThat(collectedValueWithOverride())
+                .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
+            val visibleModelWithOverride =
+                collectedValueWithOverride() as KeyguardQuickAffordanceModel.Visible
+            assertThat(visibleModelWithOverride.configKey)
+                .isEqualTo(
+                    "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${quickAccessWallet.key}"
+                )
+            assertThat(visibleModelWithOverride.icon).isEqualTo(ICON)
+            assertThat(visibleModelWithOverride.icon.contentDescription)
+                .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
+            assertThat(visibleModelWithOverride.activationState).isEqualTo(ActivationState.Active)
+        }
+
+    @Test
     fun select() =
         testScope.runTest {
             overrideResource(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
index 2b2c121..aee72de 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractorTest.kt
@@ -25,8 +25,11 @@
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -260,6 +263,23 @@
         }
 
     @Test
+    fun triggersFaceAuthWhenLockscreenIsClicked() =
+        testScope.runTest {
+            collectLastValue(underTest.isMenuVisible)
+            runCurrent()
+            kosmos.fakeDeviceEntryFaceAuthRepository.canRunFaceAuth.value = true
+
+            underTest.onClick(100.0f, 100.0f)
+            runCurrent()
+
+            val runningAuthRequest =
+                kosmos.fakeDeviceEntryFaceAuthRepository.runningAuthRequest.value
+            assertThat(runningAuthRequest?.first)
+                .isEqualTo(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED)
+            assertThat(runningAuthRequest?.second).isEqualTo(true)
+        }
+
+    @Test
     fun showMenu_leaveLockscreen_returnToLockscreen_menuNotVisible() =
         testScope.runTest {
             val isMenuVisible by collectLastValue(underTest.isMenuVisible)
@@ -302,6 +322,7 @@
                 broadcastDispatcher = fakeBroadcastDispatcher,
                 accessibilityManager = kosmos.accessibilityManagerWrapper,
                 pulsingGestureListener = kosmos.pulsingGestureListener,
+                faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor,
             )
         setUpState()
     }
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 03647b9..12039c1 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
@@ -90,52 +90,6 @@
         }
 
     @Test
-    fun finishedKeyguardStateTests() =
-        testScope.runTest {
-            val finishedSteps by collectValues(underTest.finishedKeyguardState)
-            runCurrent()
-            val steps = mutableListOf<TransitionStep>()
-
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
-            steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
-            steps.forEach {
-                repository.sendTransitionStep(it)
-                runCurrent()
-            }
-
-            assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD))
-        }
-
-    @Test
-    fun startedKeyguardStateTests() =
-        testScope.runTest {
-            val startedStates by collectValues(underTest.startedKeyguardState)
-            runCurrent()
-            val steps = mutableListOf<TransitionStep>()
-
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0f, STARTED))
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 0.5f, RUNNING))
-            steps.add(TransitionStep(AOD, PRIMARY_BOUNCER, 1f, FINISHED))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0f, STARTED))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 0.9f, RUNNING))
-            steps.add(TransitionStep(PRIMARY_BOUNCER, AOD, 1f, FINISHED))
-            steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
-
-            steps.forEach {
-                repository.sendTransitionStep(it)
-                runCurrent()
-            }
-
-            assertThat(startedStates).isEqualTo(listOf(LOCKSCREEN, PRIMARY_BOUNCER, AOD, GONE))
-        }
-
-    @Test
     fun startedKeyguardTransitionStepTests() =
         testScope.runTest {
             val startedSteps by collectValues(underTest.startedKeyguardTransitionStep)
@@ -205,6 +159,38 @@
         }
 
     @Test
+    fun transitionValue_badTransitionResetsTransitionValue() =
+        testScope.runTest {
+            resetTransitionValueReplayCache(setOf(AOD, DOZING, LOCKSCREEN))
+            val transitionValues by collectValues(underTest.transitionValue(state = DOZING))
+
+            val toSteps =
+                listOf(
+                    TransitionStep(AOD, DOZING, 0f, STARTED),
+                    TransitionStep(AOD, DOZING, 0.5f, RUNNING),
+                )
+            toSteps.forEach {
+                repository.sendTransitionStep(it)
+                runCurrent()
+            }
+
+            // This is an intentionally bad sequence that will leave the transitionValue for
+            // DOZING in a bad place, since no CANCELED will be issued for DOZING
+            val fromSteps =
+                listOf(
+                    TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
+                    TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
+                    TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
+                )
+            fromSteps.forEach {
+                repository.sendTransitionStep(it)
+                runCurrent()
+            }
+
+            assertThat(transitionValues).isEqualTo(listOf(0f, 0.5f, 0f))
+        }
+
+    @Test
     fun transitionValue_canceled_toAnotherState() =
         testScope.runTest {
             resetTransitionValueReplayCache(setOf(AOD, GONE, LOCKSCREEN))
@@ -1174,95 +1160,6 @@
         }
 
     @Test
-    fun finishedKeyguardState_emitsAgainIfCancelledAndReversed() =
-        testScope.runTest {
-            val finishedStates by collectValues(underTest.finishedKeyguardState)
-
-            // We default FINISHED in LOCKSCREEN.
-            assertEquals(listOf(LOCKSCREEN), finishedStates)
-
-            sendSteps(
-                TransitionStep(LOCKSCREEN, AOD, 0f, STARTED),
-                TransitionStep(LOCKSCREEN, AOD, 0.5f, RUNNING),
-                TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED),
-            )
-
-            // We're FINISHED in AOD.
-            assertEquals(
-                listOf(
-                    LOCKSCREEN,
-                    AOD,
-                ),
-                finishedStates
-            )
-
-            // Transition back to LOCKSCREEN.
-            sendSteps(
-                TransitionStep(AOD, LOCKSCREEN, 0f, STARTED),
-                TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING),
-                TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED),
-            )
-
-            // We're FINISHED in LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    LOCKSCREEN,
-                    AOD,
-                    LOCKSCREEN,
-                ),
-                finishedStates
-            )
-
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 0f, STARTED),
-                TransitionStep(LOCKSCREEN, GONE, 0.5f, RUNNING),
-            )
-
-            // We've STARTED a transition to GONE but not yet finished it so we're still FINISHED in
-            // LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    LOCKSCREEN,
-                    AOD,
-                    LOCKSCREEN,
-                ),
-                finishedStates
-            )
-
-            sendSteps(
-                TransitionStep(LOCKSCREEN, GONE, 0.6f, CANCELED),
-            )
-
-            // We've CANCELED a transition to GONE, we're still FINISHED in LOCKSCREEN.
-            assertEquals(
-                listOf(
-                    LOCKSCREEN,
-                    AOD,
-                    LOCKSCREEN,
-                ),
-                finishedStates
-            )
-
-            sendSteps(
-                TransitionStep(GONE, LOCKSCREEN, 0.6f, STARTED),
-                TransitionStep(GONE, LOCKSCREEN, 0.9f, RUNNING),
-                TransitionStep(GONE, LOCKSCREEN, 1f, FINISHED),
-            )
-
-            // Expect another emission of LOCKSCREEN, as we have FINISHED a second transition to
-            // LOCKSCREEN after the cancellation.
-            assertEquals(
-                listOf(
-                    LOCKSCREEN,
-                    AOD,
-                    LOCKSCREEN,
-                    LOCKSCREEN,
-                ),
-                finishedStates
-            )
-        }
-
-    @Test
     fun testCurrentState() =
         testScope.runTest {
             val currentStates by collectValues(underTest.currentKeyguardState)
@@ -1497,9 +1394,11 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
             val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            sendSteps(sendStep1)
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
             val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
             val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
-            sendSteps(sendStep1, sendStep2, sendStep3)
+            sendSteps(sendStep2, sendStep3)
 
             assertEquals(listOf(sendStep1, sendStep2), currentStates)
             assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted)
@@ -1513,6 +1412,7 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
             val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
             val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
             sendSteps(sendStep1, sendStep2, sendStep3)
@@ -1529,6 +1429,7 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
             val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
             val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
             sendSteps(sendStep1, sendStep2, sendStep3)
@@ -1546,6 +1447,7 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
             val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+            kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
             val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
             val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
             val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
@@ -1564,10 +1466,12 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Lockscreen, Scenes.Gone))
             val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            sendSteps(sendStep1)
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
             val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
             val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
             val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
-            sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+            sendSteps(sendStep2, sendStep3, sendStep4)
 
             assertEquals(listOf(sendStep1, sendStep2), currentStates)
             assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped)
@@ -1581,10 +1485,12 @@
 
             kosmos.setSceneTransition(Transition(Scenes.Gone, Scenes.Lockscreen))
             val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+            sendSteps(sendStep1)
+            kosmos.setSceneTransition(Idle(Scenes.Gone))
             val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
             val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
             val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
-            sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+            sendSteps(sendStep2, sendStep3, sendStep4)
 
             assertEquals(listOf<TransitionStep>(), currentStatesMapped)
         }
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 8c1e8de..5a6f2be 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
@@ -60,6 +60,7 @@
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancelChildren
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -845,7 +846,7 @@
             runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.GONE)
 
             // WHEN the glanceable hub is shown
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
 
             assertThat(transitionRepository)
@@ -1004,7 +1005,7 @@
     fun alternateBouncerToGlanceableHub() =
         testScope.runTest {
             // GIVEN the device is idle on the glanceable hub
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             clearInvocations(transitionRepository)
 
@@ -1123,7 +1124,7 @@
     fun primaryBouncerToGlanceableHub() =
         testScope.runTest {
             // GIVEN the device is idle on the glanceable hub
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
 
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1157,7 +1158,7 @@
             advanceTimeBy(600L)
 
             // GIVEN the device is idle on the glanceable hub
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
 
             // GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1967,47 +1968,6 @@
 
     @Test
     @DisableSceneContainer
-    @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToLockscreen_communalKtfRefactor() =
-        testScope.runTest {
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
-            runCurrent()
-            clearInvocations(transitionRepository)
-
-            // WHEN a transition away from glanceable hub starts
-            val currentScene = CommunalScenes.Communal
-            val targetScene = CommunalScenes.Blank
-
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalSceneInteractor.setTransitionState(transitionState)
-            progress.value = .1f
-            runCurrent()
-
-            assertThat(transitionRepository)
-                .startedTransition(
-                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
-                    from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.LOCKSCREEN,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
-                )
-
-            coroutineContext.cancelChildren()
-        }
-
-    @Test
-    @DisableSceneContainer
     @DisableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToDozing() =
         testScope.runTest {
@@ -2035,7 +1995,7 @@
     fun glanceableHubToDozing_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             clearInvocations(transitionRepository)
 
@@ -2135,8 +2095,16 @@
     @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
     fun glanceableHubToOccluded_communalKtfRefactor() =
         testScope.runTest {
+            // GIVEN device is not dreaming
+            powerInteractor.setAwakeForTest()
+            keyguardRepository.setDreaming(false)
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            advanceTimeBy(600.milliseconds)
+
             // GIVEN a prior transition has run to GLANCEABLE_HUB
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             clearInvocations(transitionRepository)
 
@@ -2184,7 +2152,7 @@
     fun glanceableHubToGone_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN a prior transition has run to GLANCEABLE_HUB
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
             clearInvocations(transitionRepository)
 
@@ -2251,48 +2219,48 @@
         }
 
     @Test
-    @DisableSceneContainer
+    @BrokenWithSceneContainer(339465026)
     @EnableFlags(FLAG_COMMUNAL_SCENE_KTF_REFACTOR)
-    fun glanceableHubToDreaming_communalKtfRefactor() =
+    fun glanceableHubToOccludedDoesNotTriggerWhenDreamStateChanges_communalKtfRefactor() =
         testScope.runTest {
             // GIVEN that we are dreaming and not dozing
             powerInteractor.setAwakeForTest()
             keyguardRepository.setDreaming(true)
-            keyguardRepository.setDreamingWithOverlay(true)
+            keyguardRepository.setKeyguardOccluded(true)
             keyguardRepository.setDozeTransitionModel(
                 DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
             )
-            advanceTimeBy(600L)
-
-            // GIVEN a prior transition has run to GLANCEABLE_HUB
-            communalSceneInteractor.changeScene(CommunalScenes.Communal)
-            runCurrent()
+            advanceTimeBy(700.milliseconds)
             clearInvocations(transitionRepository)
 
-            // WHEN a transition away from glanceable hub starts
-            val currentScene = CommunalScenes.Communal
-            val targetScene = CommunalScenes.Blank
-
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = currentScene,
-                        toScene = targetScene,
-                        currentScene = flowOf(targetScene),
-                        progress = flowOf(0f, 0.1f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            communalSceneInteractor.setTransitionState(transitionState)
+            // GIVEN a prior transition has run to GLANCEABLE_HUB
+            communalSceneInteractor.changeScene(CommunalScenes.Communal, "test")
             runCurrent()
+            assertThat(transitionRepository)
+                .startedTransition(
+                    ownerName = CommunalSceneTransitionInteractor::class.simpleName,
+                    from = KeyguardState.DREAMING,
+                    to = KeyguardState.GLANCEABLE_HUB,
+                )
+            clearInvocations(transitionRepository)
+
+            // WHEN dream ends but we are still occluded
+            keyguardRepository.setDreaming(false)
+            runCurrent()
+            assertThat(transitionRepository).noTransitionsStarted()
+
+            // Simulate occlusion signal changing due to dream terminating and then occluding again
+            // due to a new activity starting a couple milliseconds later.
+            keyguardRepository.setKeyguardOccluded(false)
+            advanceTimeBy(10.milliseconds)
+            keyguardRepository.setKeyguardOccluded(true)
+            advanceTimeBy(200.milliseconds)
 
             assertThat(transitionRepository)
                 .startedTransition(
                     ownerName = CommunalSceneTransitionInteractor::class.simpleName,
                     from = KeyguardState.GLANCEABLE_HUB,
-                    to = KeyguardState.DREAMING,
-                    animatorAssertion = { it.isNull() }, // transition should be manually animated
+                    to = KeyguardState.OCCLUDED,
                 )
 
             coroutineContext.cancelChildren()
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 3e0a1f3..073ed61 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,18 +19,20 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.fakeLightRevealScrimRepository
+import com.android.systemui.keyguard.data.repository.DEFAULT_REVEAL_EFFECT
 import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+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.LightRevealEffect
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertEquals
 import org.junit.Test
@@ -64,35 +66,45 @@
 
     @Test
     fun lightRevealEffect_doesNotChangeDuringKeyguardTransition() =
-        runTest(UnconfinedTestDispatcher()) {
-            val values = mutableListOf<LightRevealEffect>()
-            val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+        kosmos.testScope.runTest {
+            val values by collectValues(underTest.lightRevealEffect)
+            runCurrent()
+            assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values)
 
             fakeLightRevealScrimRepository.setRevealEffect(reveal1)
-
+            runCurrent()
             // The reveal effect shouldn't emit anything until a keyguard transition starts.
-            assertEquals(values.size, 0)
+            assertEquals(listOf(DEFAULT_REVEAL_EFFECT), values)
 
             // Once it starts, it should emit reveal1.
             fakeKeyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(transitionState = TransitionState.STARTED)
+                TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED)
             )
-            assertEquals(values, listOf(reveal1))
+            runCurrent()
+            assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values)
 
             // Until the next transition starts, reveal2 should not be emitted.
             fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+            runCurrent()
             fakeKeyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(transitionState = TransitionState.RUNNING)
+                TransitionStep(
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.RUNNING
+                )
             )
+            runCurrent()
             fakeKeyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(transitionState = TransitionState.FINISHED)
+                TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.FINISHED)
             )
-            assertEquals(values, listOf(reveal1))
+            runCurrent()
+            assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1), values)
             fakeKeyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(transitionState = TransitionState.STARTED)
+                TransitionStep(
+                    to = KeyguardState.LOCKSCREEN,
+                    transitionState = TransitionState.STARTED
+                )
             )
-            assertEquals(values, listOf(reveal1, reveal2))
-
-            job.cancel()
+            runCurrent()
+            assertEquals(listOf(DEFAULT_REVEAL_EFFECT, reveal1, reveal2), values)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
index 4a10d80..8e4876d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
@@ -42,6 +42,28 @@
     val underTest = kosmos.dozingToLockscreenTransitionViewModel
 
     @Test
+    fun lockscreenAlpha() =
+        testScope.runTest {
+            val lockscreenAlpha by collectValues(underTest.lockscreenAlpha)
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(1f))
+            lockscreenAlpha.forEach { assertThat(it).isEqualTo(1f) }
+        }
+
+    @Test
+    fun shortcutsAlpha() =
+        testScope.runTest {
+            val shortcutsAlpha by collectValues(underTest.shortcutsAlpha)
+            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(1f))
+            assertThat(shortcutsAlpha[0]).isEqualTo(0f)
+            assertThat(shortcutsAlpha[1]).isEqualTo(1f)
+        }
+
+    @Test
     fun deviceEntryParentViewShows() =
         testScope.runTest {
             val deviceEntryParentViewAlpha by collectValues(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 6dbe94b..3b2b12c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -360,6 +360,26 @@
         }
 
     @Test
+    @DisableSceneContainer
+    fun alpha_transitionBetweenHubAndDream_isZero() =
+        testScope.runTest {
+            val alpha by collectLastValue(underTest.alpha(viewState))
+
+            // Default value check
+            assertThat(alpha).isEqualTo(1f)
+
+            // Start transitioning between DREAM and HUB but don't finish.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+                throughTransitionState = TransitionState.STARTED,
+            )
+
+            assertThat(alpha).isEqualTo(0f)
+        }
+
+    @Test
     @EnableSceneContainer
     fun alpha_transitionToHub_isZero_scene_container() =
         testScope.runTest {
@@ -369,8 +389,8 @@
                 ObservableTransitionState.Transition(
                     fromScene = Scenes.Lockscreen,
                     toScene = Scenes.Communal,
-                    emptyFlow(),
-                    emptyFlow(),
+                    flowOf(Scenes.Communal),
+                    flowOf(0.5f),
                     false,
                     emptyFlow()
                 )
@@ -485,6 +505,45 @@
         }
 
     @Test
+    @DisableSceneContainer
+    fun alphaFromShadeExpansion_doesNotEmitWhenLockscreenToDreamTransitionRunning() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                testScope,
+            )
+
+            val alpha by collectLastValue(underTest.alpha(viewState))
+            shadeTestUtil.setQsExpansion(0f)
+
+            assertThat(alpha).isEqualTo(1f)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.DREAMING,
+                        transitionState = TransitionState.STARTED,
+                        value = 0f,
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.DREAMING,
+                        transitionState = TransitionState.RUNNING,
+                        value = 0.1f,
+                    ),
+                ),
+                testScope,
+            )
+
+            val alphaBeforeExpansion = alpha
+            shadeTestUtil.setQsExpansion(0.5f)
+            // Alpha should remain unchanged instead of being affected by expansion.
+            assertThat(alpha).isEqualTo(alphaBeforeExpansion)
+        }
+
+    @Test
     fun alpha_shadeClosedOverLockscreen_isOne() =
         testScope.runTest {
             val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index 8236eec..6f7e9d3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -128,8 +128,7 @@
     fun areNotificationsVisible_splitShadeTrue_true() =
         with(kosmos) {
             testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                 shadeRepository.setShadeLayoutWide(true)
                 fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
 
@@ -142,36 +141,7 @@
     fun areNotificationsVisible_dualShadeWideOnLockscreen_true() =
         with(kosmos) {
             testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
-                shadeRepository.setShadeLayoutWide(true)
-                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
-
-                assertThat(areNotificationsVisible).isTrue()
-            }
-        }
-
-    @Test
-    @EnableFlags(DualShade.FLAG_NAME)
-    fun areNotificationsVisible_dualShadeWideOnNotificationsShade_false() =
-        with(kosmos) {
-            testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.NotificationsShade))
-                shadeRepository.setShadeLayoutWide(true)
-                fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
-
-                assertThat(areNotificationsVisible).isFalse()
-            }
-        }
-
-    @Test
-    @EnableFlags(DualShade.FLAG_NAME)
-    fun areNotificationsVisible_dualShadeWideOnQuickSettingsShade_true() =
-        with(kosmos) {
-            testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.QuickSettingsShade))
+                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                 shadeRepository.setShadeLayoutWide(true)
                 fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
 
@@ -184,8 +154,7 @@
     fun areNotificationsVisible_withSmallClock_true() =
         with(kosmos) {
             testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                 fakeKeyguardClockRepository.setClockSize(ClockSize.SMALL)
                 assertThat(areNotificationsVisible).isTrue()
             }
@@ -196,8 +165,7 @@
     fun areNotificationsVisible_withLargeClock_false() =
         with(kosmos) {
             testScope.runTest {
-                val areNotificationsVisible by
-                    collectLastValue(underTest.areNotificationsVisible(Scenes.Lockscreen))
+                val areNotificationsVisible by collectLastValue(underTest.areNotificationsVisible())
                 fakeKeyguardClockRepository.setClockSize(ClockSize.LARGE)
                 assertThat(areNotificationsVisible).isFalse()
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt
deleted file mode 100644
index b3ea03e..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,265 +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.keyguard.ui.viewmodel
-
-import android.platform.test.annotations.EnableFlags
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.TransitionKey
-import com.android.systemui.Flags
-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.communal.domain.interactor.communalInteractor
-import com.android.systemui.communal.domain.interactor.setCommunalAvailable
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.power.data.repository.fakePowerRepository
-import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlin.math.pow
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runTest
-import org.junit.BeforeClass
-import org.junit.Test
-import org.junit.runner.RunWith
-import platform.test.runner.parameterized.Parameter
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-@RunWithLooper
-@EnableSceneContainer
-class LockscreenSceneActionsViewModelTest : SysuiTestCase() {
-
-    companion object {
-        private const val parameterCount = 6
-
-        @Parameters(
-            name =
-                "canSwipeToEnter={0}, downWithTwoPointers={1}, downFromEdge={2}," +
-                    " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}"
-        )
-        @JvmStatic
-        fun combinations() = buildList {
-            repeat(2f.pow(parameterCount).toInt()) { combination ->
-                add(
-                    arrayOf(
-                            /* canSwipeToEnter= */ combination and 1 != 0,
-                            /* downWithTwoPointers= */ combination and 2 != 0,
-                            /* downFromEdge= */ combination and 4 != 0,
-                            /* isSingleShade= */ combination and 8 != 0,
-                            /* isCommunalAvailable= */ combination and 16 != 0,
-                            /* isShadeTouchable= */ combination and 32 != 0,
-                        )
-                        .also { check(it.size == parameterCount) }
-                )
-            }
-        }
-
-        @JvmStatic
-        @BeforeClass
-        fun setUp() {
-            val combinationStrings =
-                combinations().map { array ->
-                    check(array.size == parameterCount)
-                    buildString {
-                        ((parameterCount - 1) downTo 0).forEach { index ->
-                            append("${array[index]}")
-                            if (index > 0) {
-                                append(",")
-                            }
-                        }
-                    }
-                }
-            val uniqueCombinations = combinationStrings.toSet()
-            assertThat(combinationStrings).hasSize(uniqueCombinations.size)
-        }
-
-        private fun expectedDownDestination(
-            downFromEdge: Boolean,
-            isSingleShade: Boolean,
-            isShadeTouchable: Boolean,
-        ): SceneKey? {
-            return when {
-                !isShadeTouchable -> null
-                downFromEdge && isSingleShade -> Scenes.QuickSettings
-                else -> Scenes.Shade
-            }
-        }
-
-        private fun expectedDownTransitionKey(
-            isSingleShade: Boolean,
-            isShadeTouchable: Boolean,
-        ): TransitionKey? {
-            return when {
-                !isShadeTouchable -> null
-                !isSingleShade -> TransitionKeys.ToSplitShade
-                else -> null
-            }
-        }
-
-        private fun expectedUpDestination(
-            canSwipeToEnter: Boolean,
-            isShadeTouchable: Boolean,
-        ): SceneKey? {
-            return when {
-                !isShadeTouchable -> null
-                canSwipeToEnter -> Scenes.Gone
-                else -> Scenes.Bouncer
-            }
-        }
-
-        private fun expectedLeftDestination(
-            isCommunalAvailable: Boolean,
-            isShadeTouchable: Boolean,
-        ): SceneKey? {
-            return when {
-                !isShadeTouchable -> null
-                isCommunalAvailable -> Scenes.Communal
-                else -> null
-            }
-        }
-    }
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-
-    @JvmField @Parameter(0) var canSwipeToEnter: Boolean = false
-    @JvmField @Parameter(1) var downWithTwoPointers: Boolean = false
-    @JvmField @Parameter(2) var downFromEdge: Boolean = false
-    @JvmField @Parameter(3) var isSingleShade: Boolean = true
-    @JvmField @Parameter(4) var isCommunalAvailable: Boolean = false
-    @JvmField @Parameter(5) var isShadeTouchable: Boolean = false
-
-    private val underTest by lazy { createLockscreenSceneViewModel() }
-
-    @Test
-    @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
-    fun destinationScenes() =
-        testScope.runTest {
-            underTest.activateIn(this)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                if (canSwipeToEnter) {
-                    AuthenticationMethodModel.None
-                } else {
-                    AuthenticationMethodModel.Pin
-                }
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            kosmos.shadeRepository.setShadeLayoutWide(!isSingleShade)
-            kosmos.setCommunalAvailable(isCommunalAvailable)
-            kosmos.fakePowerRepository.updateWakefulness(
-                rawState =
-                    if (isShadeTouchable) {
-                        WakefulnessState.AWAKE
-                    } else {
-                        WakefulnessState.ASLEEP
-                    },
-            )
-
-            val destinationScenes by collectLastValue(underTest.actions)
-            val downDestination =
-                destinationScenes?.get(
-                    Swipe(
-                        SwipeDirection.Down,
-                        fromSource = Edge.Top.takeIf { downFromEdge },
-                        pointerCount = if (downWithTwoPointers) 2 else 1,
-                    )
-                )
-            val downScene by
-                collectLastValue(
-                    downDestination?.let {
-                        kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene)
-                    } ?: flowOf(null)
-                )
-            assertThat(downScene)
-                .isEqualTo(
-                    expectedDownDestination(
-                        downFromEdge = downFromEdge,
-                        isSingleShade = isSingleShade,
-                        isShadeTouchable = isShadeTouchable,
-                    )
-                )
-
-            assertThat(downDestination?.transitionKey)
-                .isEqualTo(
-                    expectedDownTransitionKey(
-                        isSingleShade = isSingleShade,
-                        isShadeTouchable = isShadeTouchable,
-                    )
-                )
-
-            val upScene by
-                collectLastValue(
-                    destinationScenes?.get(Swipe(SwipeDirection.Up))?.toScene?.let { scene ->
-                        kosmos.sceneInteractor.resolveSceneFamily(scene)
-                    } ?: flowOf(null)
-                )
-
-            assertThat(upScene)
-                .isEqualTo(
-                    expectedUpDestination(
-                        canSwipeToEnter = canSwipeToEnter,
-                        isShadeTouchable = isShadeTouchable,
-                    )
-                )
-
-            val leftScene by
-                collectLastValue(
-                    destinationScenes?.get(Swipe(SwipeDirection.Left))?.toScene?.let { scene ->
-                        kosmos.sceneInteractor.resolveSceneFamily(scene)
-                    } ?: flowOf(null)
-                )
-
-            assertThat(leftScene)
-                .isEqualTo(
-                    expectedLeftDestination(
-                        isCommunalAvailable = isCommunalAvailable,
-                        isShadeTouchable = isShadeTouchable,
-                    )
-                )
-        }
-
-    private fun createLockscreenSceneViewModel(): LockscreenSceneActionsViewModel {
-        return LockscreenSceneActionsViewModel(
-            deviceEntryInteractor = kosmos.deviceEntryInteractor,
-            communalInteractor = kosmos.communalInteractor,
-            shadeInteractor = kosmos.shadeInteractor,
-        )
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
index 86b3f33..0a0ded7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.testKosmos
 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
@@ -85,26 +86,22 @@
         testScope.runTest {
             fingerprintPropertyRepository.supportsUdfps()
             biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false)
+
             val values by collectValues(underTest.deviceEntryParentViewAlpha)
+            runCurrent()
 
             keyguardTransitionRepository.sendTransitionSteps(
-                listOf(
-                    step(0f, TransitionState.STARTED),
-                    step(0f),
-                    step(0.1f),
-                    step(0.2f),
-                    step(0.3f),
-                    step(1f),
-                ),
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.DOZING,
                 testScope,
             )
 
-            values.forEach { assertThat(it).isEqualTo(0f) }
+            assertThat(values[0]).isEqualTo(1f)
+            assertThat(values[1]).isEqualTo(0f)
         }
 
-
     @Test
-    fun lockscreenAlphaFadesOutAndFinishesVisible() =
+    fun lockscreenAlphaDoesNotFadeOut() =
         testScope.runTest {
             val alpha by collectValues(underTest.lockscreenAlpha)
             keyguardTransitionRepository.sendTransitionSteps(
@@ -113,31 +110,7 @@
                 testScope,
             )
 
-            assertThat(alpha[0]).isEqualTo(1f)
-            // Halfway through, it will have faded out
-            assertThat(alpha[1]).isEqualTo(0f)
-            // FINISHED alpha should be visible, to support pulsing
-            assertThat(alpha[2]).isEqualTo(1f)
-        }
-
-    @Test
-    fun deviceEntryBackgroundViewDisappear() =
-        testScope.runTest {
-            val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)
-
-            keyguardTransitionRepository.sendTransitionSteps(
-                listOf(
-                    step(0f, TransitionState.STARTED),
-                    step(0f),
-                    step(0.1f),
-                    step(0.2f),
-                    step(0.3f),
-                    step(1f),
-                ),
-                testScope,
-            )
-
-            values.forEach { assertThat(it).isEqualTo(0f) }
+            alpha.forEach { assertThat(it).isEqualTo(1f) }
         }
 
     private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
new file mode 100644
index 0000000..4253c29
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt
@@ -0,0 +1,268 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.TransitionKey
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.Flags
+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.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.math.pow
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@RunWithLooper
+@EnableSceneContainer
+class LockscreenUserActionsViewModelTest : SysuiTestCase() {
+
+    companion object {
+        private const val parameterCount = 6
+
+        @Parameters(
+            name =
+                "canSwipeToEnter={0}, downWithTwoPointers={1}, downFromEdge={2}," +
+                    " isSingleShade={3}, isCommunalAvailable={4}, isShadeTouchable={5}"
+        )
+        @JvmStatic
+        fun combinations() = buildList {
+            repeat(2f.pow(parameterCount).toInt()) { combination ->
+                add(
+                    arrayOf(
+                            /* canSwipeToEnter= */ combination and 1 != 0,
+                            /* downWithTwoPointers= */ combination and 2 != 0,
+                            /* downFromEdge= */ combination and 4 != 0,
+                            /* isSingleShade= */ combination and 8 != 0,
+                            /* isCommunalAvailable= */ combination and 16 != 0,
+                            /* isShadeTouchable= */ combination and 32 != 0,
+                        )
+                        .also { check(it.size == parameterCount) }
+                )
+            }
+        }
+
+        @JvmStatic
+        @BeforeClass
+        fun setUp() {
+            val combinationStrings =
+                combinations().map { array ->
+                    check(array.size == parameterCount)
+                    buildString {
+                        ((parameterCount - 1) downTo 0).forEach { index ->
+                            append("${array[index]}")
+                            if (index > 0) {
+                                append(",")
+                            }
+                        }
+                    }
+                }
+            val uniqueCombinations = combinationStrings.toSet()
+            assertThat(combinationStrings).hasSize(uniqueCombinations.size)
+        }
+
+        private fun expectedDownDestination(
+            downFromEdge: Boolean,
+            isSingleShade: Boolean,
+            isShadeTouchable: Boolean,
+        ): SceneKey? {
+            return when {
+                !isShadeTouchable -> null
+                downFromEdge && isSingleShade -> Scenes.QuickSettings
+                else -> Scenes.Shade
+            }
+        }
+
+        private fun expectedDownTransitionKey(
+            isSingleShade: Boolean,
+            isShadeTouchable: Boolean,
+        ): TransitionKey? {
+            return when {
+                !isShadeTouchable -> null
+                !isSingleShade -> TransitionKeys.ToSplitShade
+                else -> null
+            }
+        }
+
+        private fun expectedUpDestination(
+            canSwipeToEnter: Boolean,
+            isShadeTouchable: Boolean,
+        ): SceneKey? {
+            return when {
+                !isShadeTouchable -> null
+                canSwipeToEnter -> Scenes.Gone
+                else -> Scenes.Bouncer
+            }
+        }
+
+        private fun expectedLeftDestination(
+            isCommunalAvailable: Boolean,
+            isShadeTouchable: Boolean,
+        ): SceneKey? {
+            return when {
+                !isShadeTouchable -> null
+                isCommunalAvailable -> Scenes.Communal
+                else -> null
+            }
+        }
+    }
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+
+    @JvmField @Parameter(0) var canSwipeToEnter: Boolean = false
+    @JvmField @Parameter(1) var downWithTwoPointers: Boolean = false
+    @JvmField @Parameter(2) var downFromEdge: Boolean = false
+    @JvmField @Parameter(3) var isSingleShade: Boolean = true
+    @JvmField @Parameter(4) var isCommunalAvailable: Boolean = false
+    @JvmField @Parameter(5) var isShadeTouchable: Boolean = false
+
+    private val underTest by lazy { createLockscreenSceneViewModel() }
+
+    @Test
+    @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+    fun userActions() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                if (canSwipeToEnter) {
+                    AuthenticationMethodModel.None
+                } else {
+                    AuthenticationMethodModel.Pin
+                }
+            )
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+            kosmos.shadeRepository.setShadeLayoutWide(!isSingleShade)
+            kosmos.setCommunalAvailable(isCommunalAvailable)
+            kosmos.fakePowerRepository.updateWakefulness(
+                rawState =
+                    if (isShadeTouchable) {
+                        WakefulnessState.AWAKE
+                    } else {
+                        WakefulnessState.ASLEEP
+                    },
+            )
+
+            val userActions by collectLastValue(underTest.actions)
+            val downDestination =
+                userActions?.get(
+                    Swipe(
+                        SwipeDirection.Down,
+                        fromSource = Edge.Top.takeIf { downFromEdge },
+                        pointerCount = if (downWithTwoPointers) 2 else 1,
+                    )
+                ) as? UserActionResult.ChangeScene
+            val downScene by
+                collectLastValue(
+                    downDestination?.let {
+                        kosmos.sceneInteractor.resolveSceneFamily(downDestination.toScene)
+                    } ?: flowOf(null)
+                )
+            assertThat(downScene)
+                .isEqualTo(
+                    expectedDownDestination(
+                        downFromEdge = downFromEdge,
+                        isSingleShade = isSingleShade,
+                        isShadeTouchable = isShadeTouchable,
+                    )
+                )
+
+            assertThat(downDestination?.transitionKey)
+                .isEqualTo(
+                    expectedDownTransitionKey(
+                        isSingleShade = isSingleShade,
+                        isShadeTouchable = isShadeTouchable,
+                    )
+                )
+
+            val upScene by
+                collectLastValue(
+                    (userActions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene?.let {
+                        scene ->
+                        kosmos.sceneInteractor.resolveSceneFamily(scene)
+                    } ?: flowOf(null)
+                )
+
+            assertThat(upScene)
+                .isEqualTo(
+                    expectedUpDestination(
+                        canSwipeToEnter = canSwipeToEnter,
+                        isShadeTouchable = isShadeTouchable,
+                    )
+                )
+
+            val leftScene by
+                collectLastValue(
+                    (userActions?.get(Swipe.Left) as? UserActionResult.ChangeScene)?.toScene?.let {
+                        scene ->
+                        kosmos.sceneInteractor.resolveSceneFamily(scene)
+                    } ?: flowOf(null)
+                )
+
+            assertThat(leftScene)
+                .isEqualTo(
+                    expectedLeftDestination(
+                        isCommunalAvailable = isCommunalAvailable,
+                        isShadeTouchable = isShadeTouchable,
+                    )
+                )
+        }
+
+    private fun createLockscreenSceneViewModel(): LockscreenUserActionsViewModel {
+        return LockscreenUserActionsViewModel(
+            deviceEntryInteractor = kosmos.deviceEntryInteractor,
+            communalInteractor = kosmos.communalInteractor,
+            shadeInteractor = kosmos.shadeInteractor,
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
index 409c551..5ec566b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
+import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -49,6 +50,24 @@
     val underTest = kosmos.primaryBouncerToLockscreenTransitionViewModel
 
     @Test
+    fun lockscreenAlphaStartsFromViewStateAccessorAlpha() =
+        testScope.runTest {
+            val viewState = ViewStateAccessor(alpha = { 0.5f })
+            val alpha by collectLastValue(underTest.lockscreenAlpha(viewState))
+
+            keyguardTransitionRepository.sendTransitionStep(step(0f, TransitionState.STARTED))
+
+            keyguardTransitionRepository.sendTransitionStep(step(0f))
+            assertThat(alpha).isEqualTo(0.5f)
+
+            keyguardTransitionRepository.sendTransitionStep(step(0.5f))
+            assertThat(alpha).isIn(Range.open(0.5f, 1f))
+
+            keyguardTransitionRepository.sendTransitionStep(step(1f))
+            assertThat(alpha).isEqualTo(1f)
+        }
+
+    @Test
     fun deviceEntryParentViewAlpha() =
         testScope.runTest {
             val deviceEntryParentViewAlpha by collectLastValue(underTest.deviceEntryParentViewAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt
deleted file mode 100644
index 67517a2..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ActivatableTest.kt
+++ /dev/null
@@ -1,76 +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.lifecycle
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.test.junit4.createComposeRule
-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.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ActivatableTest : SysuiTestCase() {
-
-    @get:Rule val composeRule = createComposeRule()
-
-    @Test
-    fun rememberActivated() {
-        val keepAliveMutable = mutableStateOf(true)
-        var isActive = false
-        composeRule.setContent {
-            val keepAlive by keepAliveMutable
-            if (keepAlive) {
-                rememberActivated {
-                    FakeActivatable(
-                        onActivation = { isActive = true },
-                        onDeactivation = { isActive = false },
-                    )
-                }
-            }
-        }
-        assertThat(isActive).isTrue()
-    }
-
-    @Test
-    fun rememberActivated_leavingTheComposition() {
-        val keepAliveMutable = mutableStateOf(true)
-        var isActive = false
-        composeRule.setContent {
-            val keepAlive by keepAliveMutable
-            if (keepAlive) {
-                rememberActivated {
-                    FakeActivatable(
-                        onActivation = { isActive = true },
-                        onDeactivation = { isActive = false },
-                    )
-                }
-            }
-        }
-
-        // Tear down the composable.
-        composeRule.runOnUiThread { keepAliveMutable.value = false }
-        composeRule.waitForIdle()
-
-        assertThat(isActive).isFalse()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ExclusiveActivatableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ExclusiveActivatableTest.kt
new file mode 100644
index 0000000..81b9180
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/ExclusiveActivatableTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.lifecycle
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+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.Job
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ExclusiveActivatableTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = FakeActivatable()
+
+    @Test
+    fun activate() =
+        testScope.runTest {
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+        }
+
+    @Test
+    fun activate_andCancel() =
+        testScope.runTest {
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            val job = Job()
+            underTest.activateIn(testScope, context = job)
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+        }
+
+    @Test
+    fun activate_afterCancellation() =
+        testScope.runTest {
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            val job = Job()
+            underTest.activateIn(testScope, context = job)
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            job.cancel()
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(2)
+            assertThat(underTest.cancellationCount).isEqualTo(1)
+        }
+
+    @Test(expected = IllegalStateException::class)
+    fun activate_whileActive_throws() =
+        testScope.runTest {
+            assertThat(underTest.activationCount).isEqualTo(0)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+            assertThat(underTest.activationCount).isEqualTo(1)
+            assertThat(underTest.cancellationCount).isEqualTo(0)
+
+            underTest.activateIn(testScope)
+            runCurrent()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.kt
new file mode 100644
index 0000000..ec6045c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/HydratorTest.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.
+ */
+
+package com.android.systemui.lifecycle
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material3.Text
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertTextEquals
+import androidx.compose.ui.test.hasTestTag
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HydratorTest : SysuiTestCase() {
+
+    @get:Rule val composeRule = createComposeRule()
+
+    @Test
+    fun hydratedStateOf() {
+        val keepAliveMutable = mutableStateOf(true)
+        val upstreamStateFlow = MutableStateFlow(true)
+        val upstreamFlow = upstreamStateFlow.map { !it }
+        composeRule.setContent {
+            val keepAlive by keepAliveMutable
+            if (keepAlive) {
+                val viewModel =
+                    rememberViewModel("test") {
+                        FakeSysUiViewModel(
+                            upstreamFlow = upstreamFlow,
+                            upstreamStateFlow = upstreamStateFlow,
+                        )
+                    }
+
+                Column {
+                    Text(
+                        "upstreamStateFlow=${viewModel.stateBackedByStateFlow}",
+                        Modifier.testTag("upstreamStateFlow")
+                    )
+                    Text(
+                        "upstreamFlow=${viewModel.stateBackedByFlow}",
+                        Modifier.testTag("upstreamFlow")
+                    )
+                }
+            }
+        }
+
+        composeRule.waitForIdle()
+        composeRule
+            .onNode(hasTestTag("upstreamStateFlow"))
+            .assertTextEquals("upstreamStateFlow=true")
+        composeRule.onNode(hasTestTag("upstreamFlow")).assertTextEquals("upstreamFlow=false")
+
+        composeRule.runOnUiThread { upstreamStateFlow.value = false }
+        composeRule.waitForIdle()
+        composeRule
+            .onNode(hasTestTag("upstreamStateFlow"))
+            .assertTextEquals("upstreamStateFlow=false")
+        composeRule.onNode(hasTestTag("upstreamFlow")).assertTextEquals("upstreamFlow=true")
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt
deleted file mode 100644
index 9484821..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SafeActivatableTest.kt
+++ /dev/null
@@ -1,121 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.lifecycle
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-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.Job
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SafeActivatableTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val underTest = FakeActivatable()
-
-    @Test
-    fun activate() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-        }
-
-    @Test
-    fun activate_andCancel() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            val job = Job()
-            underTest.activateIn(testScope, context = job)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            job.cancel()
-            runCurrent()
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-        }
-
-    @Test
-    fun activate_afterCancellation() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            val job = Job()
-            underTest.activateIn(testScope, context = job)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            job.cancel()
-            runCurrent()
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(2)
-            assertThat(underTest.cancellationCount).isEqualTo(1)
-        }
-
-    @Test(expected = IllegalStateException::class)
-    fun activate_whileActive_throws() =
-        testScope.runTest {
-            assertThat(underTest.isActive).isFalse()
-            assertThat(underTest.activationCount).isEqualTo(0)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-            assertThat(underTest.activationCount).isEqualTo(1)
-            assertThat(underTest.cancellationCount).isEqualTo(0)
-
-            underTest.activateIn(testScope)
-            runCurrent()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
deleted file mode 100644
index d1f908d..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
+++ /dev/null
@@ -1,113 +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.lifecycle
-
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.test.junit4.createComposeRule
-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.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SysUiViewModelTest : SysuiTestCase() {
-
-    @get:Rule val composeRule = createComposeRule()
-
-    @Test
-    fun rememberActivated() {
-        val keepAliveMutable = mutableStateOf(true)
-        var isActive = false
-        composeRule.setContent {
-            val keepAlive by keepAliveMutable
-            if (keepAlive) {
-                rememberViewModel {
-                    FakeSysUiViewModel(
-                        onActivation = { isActive = true },
-                        onDeactivation = { isActive = false },
-                    )
-                }
-            }
-        }
-        assertThat(isActive).isTrue()
-    }
-
-    @Test
-    fun rememberActivated_withKey() {
-        val keyMutable = mutableStateOf(1)
-        var isActive1 = false
-        var isActive2 = false
-        composeRule.setContent {
-            val key by keyMutable
-            rememberViewModel(key) {
-                when (key) {
-                    1 ->
-                        FakeSysUiViewModel(
-                            onActivation = { isActive1 = true },
-                            onDeactivation = { isActive1 = false },
-                        )
-                    2 ->
-                        FakeSysUiViewModel(
-                            onActivation = { isActive2 = true },
-                            onDeactivation = { isActive2 = false },
-                        )
-                    else -> error("unsupported key $key")
-                }
-            }
-        }
-        assertThat(isActive1).isTrue()
-        assertThat(isActive2).isFalse()
-
-        composeRule.runOnUiThread { keyMutable.value = 2 }
-        composeRule.waitForIdle()
-        assertThat(isActive1).isFalse()
-        assertThat(isActive2).isTrue()
-
-        composeRule.runOnUiThread { keyMutable.value = 1 }
-        composeRule.waitForIdle()
-        assertThat(isActive1).isTrue()
-        assertThat(isActive2).isFalse()
-    }
-
-    @Test
-    fun rememberActivated_leavingTheComposition() {
-        val keepAliveMutable = mutableStateOf(true)
-        var isActive = false
-        composeRule.setContent {
-            val keepAlive by keepAliveMutable
-            if (keepAlive) {
-                rememberViewModel {
-                    FakeSysUiViewModel(
-                        onActivation = { isActive = true },
-                        onDeactivation = { isActive = false },
-                    )
-                }
-            }
-        }
-
-        // Tear down the composable.
-        composeRule.runOnUiThread { keepAliveMutable.value = false }
-        composeRule.waitForIdle()
-
-        assertThat(isActive).isFalse()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
new file mode 100644
index 0000000..c1dcf37
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderTest.kt
@@ -0,0 +1,397 @@
+/**
+ * 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.media.controls.domain.pipeline
+
+import android.app.Notification
+import android.app.Notification.MediaStyle
+import android.app.PendingIntent
+import android.app.statusBarManager
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.media.AudioAttributes
+import android.media.MediaDescription
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+import android.service.notification.StatusBarNotification
+import androidx.media.utils.MediaConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.graphics.imageLoader
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.mediaFlags
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.SbnBuilder
+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
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+private const val KEY = "KEY"
+private const val PACKAGE_NAME = "com.example.app"
+private const val SYSTEM_PACKAGE_NAME = "com.android.systemui"
+private const val APP_NAME = "SystemUI"
+private const val SESSION_ARTIST = "artist"
+private const val SESSION_TITLE = "title"
+private const val SESSION_EMPTY_TITLE = ""
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MediaDataLoaderTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val testDispatcher = kosmos.testDispatcher
+    private val statusBarManager = kosmos.statusBarManager
+    private val mediaController = mock<MediaController>()
+    private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
+    private val mediaFlags = kosmos.mediaFlags
+    private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
+    private val session = MediaSession(context, "MediaDataLoaderTestSession")
+    private val metadataBuilder =
+        MediaMetadata.Builder().apply {
+            putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
+            putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
+        }
+
+    private val underTest: MediaDataLoader =
+        MediaDataLoader(
+            context,
+            testDispatcher,
+            testScope,
+            kosmos.activityStarter,
+            mediaControllerFactory,
+            mediaFlags,
+            kosmos.imageLoader,
+            statusBarManager
+        )
+
+    @Before
+    fun setUp() {
+        mediaControllerFactory.setControllerForToken(session.sessionToken, mediaController)
+    }
+
+    @Test
+    fun loadMediaData_returnsMediaData() =
+        testScope.runTest {
+            val song = "THIS_IS_A_SONG"
+            val artist = "THIS_IS_AN_ARTIST"
+            val albumArt = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
+
+            whenever(mediaController.playbackState)
+                .thenReturn(
+                    PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 12, 1.0f).build()
+                )
+            whenever(mediaController.playbackInfo)
+                .thenReturn(
+                    MediaController.PlaybackInfo(
+                        MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
+                        0,
+                        0,
+                        0,
+                        AudioAttributes.Builder().build(),
+                        null
+                    )
+                )
+            whenever(mediaController.metadata)
+                .thenReturn(
+                    metadataBuilder
+                        .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, song)
+                        .putString(MediaMetadata.METADATA_KEY_ARTIST, artist)
+                        .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, albumArt)
+                        .putLong(
+                            MediaConstants.METADATA_KEY_IS_EXPLICIT,
+                            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+                        )
+                        .build()
+                )
+
+            val result = underTest.loadMediaData(KEY, createMediaNotification())
+            assertThat(result).isNotNull()
+            assertThat(result?.appIcon).isNotNull()
+            assertThat(result?.appIcon?.resId).isEqualTo(android.R.drawable.ic_media_pause)
+            assertThat(result?.artist).isEqualTo(artist)
+            assertThat(result?.song).isEqualTo(song)
+            assertThat(result?.artworkIcon).isNotNull()
+            assertThat(result?.artworkIcon?.bitmap?.width).isEqualTo(albumArt.width)
+            assertThat(result?.artworkIcon?.bitmap?.height).isEqualTo(albumArt.height)
+            assertThat(result?.token).isEqualTo(session.sessionToken)
+            assertThat(result?.device).isNull()
+            assertThat(result?.playbackLocation).isEqualTo(MediaData.PLAYBACK_LOCAL)
+            assertThat(result?.isPlaying).isTrue()
+            assertThat(result?.isExplicit).isTrue()
+            assertThat(result?.resumeAction).isNull()
+            assertThat(result?.resumeProgress).isNull()
+        }
+
+    @Test
+    fun loadMediaDataForResumption_returnsMediaData() =
+        testScope.runTest {
+            fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
+
+            val song = "THIS_IS_A_SONG"
+            val artist = "THIS_IS_AN_ARTIST"
+            val albumArt = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
+
+            val extras = Bundle()
+            extras.putInt(
+                MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
+                MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
+            )
+            extras.putDouble(MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.3)
+            extras.putLong(
+                MediaConstants.METADATA_KEY_IS_EXPLICIT,
+                MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+            )
+
+            val description =
+                MediaDescription.Builder()
+                    .setTitle(song)
+                    .setSubtitle(artist)
+                    .setIconBitmap(albumArt)
+                    .setExtras(extras)
+                    .build()
+
+            val intent =
+                PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_IMMUTABLE)
+
+            val result =
+                underTest.loadMediaDataForResumption(
+                    0,
+                    description,
+                    Runnable {},
+                    null,
+                    session.sessionToken,
+                    APP_NAME,
+                    intent,
+                    PACKAGE_NAME
+                )
+            assertThat(result).isNotNull()
+            assertThat(result?.appName).isEqualTo(APP_NAME)
+            assertThat(result?.song).isEqualTo(song)
+            assertThat(result?.artist).isEqualTo(artist)
+            assertThat(result?.artworkIcon).isNotNull()
+            assertThat(result?.artworkIcon?.bitmap?.width).isEqualTo(100)
+            assertThat(result?.artworkIcon?.bitmap?.height).isEqualTo(100)
+            assertThat(result?.token).isEqualTo(session.sessionToken)
+            assertThat(result?.clickIntent).isEqualTo(intent)
+            assertThat(result?.isExplicit).isTrue()
+            assertThat(result?.resumeProgress).isEqualTo(0.3)
+        }
+
+    @Test
+    fun loadMediaData_songNameFallbacks() =
+        testScope.runTest {
+            // Check ordering of Song resolution:
+            // DISPLAY_TITLE > TITLE > notification TITLE > notification TITLE_BIG
+
+            // DISPLAY_TITLE
+            whenever(mediaController.metadata)
+                .thenReturn(
+                    MediaMetadata.Builder()
+                        .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, "title1")
+                        .putString(MediaMetadata.METADATA_KEY_TITLE, "title2")
+                        .build()
+                )
+            val result1 = underTest.loadMediaData(KEY, createMediaNotification())
+            assertThat(result1?.song).isEqualTo("title1")
+
+            // TITLE
+            whenever(mediaController.metadata)
+                .thenReturn(
+                    MediaMetadata.Builder()
+                        .putString(MediaMetadata.METADATA_KEY_TITLE, "title2")
+                        .build()
+                )
+            val result2 = underTest.loadMediaData(KEY, createMediaNotification())
+            assertThat(result2?.song).isEqualTo("title2")
+
+            // notification TITLE
+            val notif =
+                SbnBuilder().run {
+                    setPkg(PACKAGE_NAME)
+                    modifyNotification(context).also {
+                        it.setSmallIcon(android.R.drawable.ic_media_pause)
+                        it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                        it.setContentTitle("notiftitle")
+                    }
+                    build()
+                }
+            whenever(mediaController.metadata).thenReturn(MediaMetadata.Builder().build())
+            val result3 = underTest.loadMediaData(KEY, notif)
+            assertThat(result3?.song).isEqualTo("notiftitle")
+
+            // Final fallback
+            whenever(mediaController.metadata).thenReturn(MediaMetadata.Builder().build())
+            val result4 = underTest.loadMediaData(KEY, createMediaNotification())
+            assertThat(result4?.song)
+                .isEqualTo(context.getString(R.string.controls_media_empty_title, result4?.appName))
+        }
+
+    @Test
+    fun loadMediaData_emptyTitle_hasPlaceholder() =
+        testScope.runTest {
+            val packageManager = mock<PackageManager>()
+            context.setMockPackageManager(packageManager)
+            whenever(packageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
+            whenever(mediaController.metadata)
+                .thenReturn(
+                    metadataBuilder
+                        .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
+                        .build()
+                )
+
+            val result = underTest.loadMediaData(KEY, createMediaNotification())
+
+            val placeholderTitle = context.getString(R.string.controls_media_empty_title, APP_NAME)
+            assertThat(result).isNotNull()
+            assertThat(result?.song).isEqualTo(placeholderTitle)
+        }
+
+    @Test
+    fun loadMediaData_emptyMetadata_usesNotificationTitle() =
+        testScope.runTest {
+            val packageManager = mock<PackageManager>()
+            context.setMockPackageManager(packageManager)
+            whenever(packageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
+            whenever(mediaController.metadata)
+                .thenReturn(
+                    metadataBuilder
+                        .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
+                        .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
+                        .build()
+                )
+            val mediaNotification =
+                SbnBuilder().run {
+                    setPkg(PACKAGE_NAME)
+                    modifyNotification(context).also {
+                        it.setSmallIcon(android.R.drawable.ic_media_pause)
+                        it.setContentTitle(SESSION_TITLE)
+                        it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                    }
+                    build()
+                }
+
+            val result = underTest.loadMediaData(KEY, mediaNotification)
+
+            assertThat(result).isNotNull()
+            assertThat(result?.song).isEqualTo(SESSION_TITLE)
+        }
+
+    @Test
+    fun loadMediaData_badArtwork_isNotUsed() =
+        testScope.runTest {
+            val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
+            val mediaNotification =
+                SbnBuilder().run {
+                    setPkg(PACKAGE_NAME)
+                    modifyNotification(context).also {
+                        it.setSmallIcon(android.R.drawable.ic_media_pause)
+                        it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                        it.setLargeIcon(artwork)
+                    }
+                    build()
+                }
+
+            val result = underTest.loadMediaData(KEY, mediaNotification)
+
+            assertThat(result).isNotNull()
+        }
+
+    @Test
+    fun loadMediaData_invalidTokenNoCrash() =
+        testScope.runTest {
+            val bundle = Bundle()
+            // wrong data type
+            bundle.putParcelable(Notification.EXTRA_MEDIA_SESSION, Bundle())
+            val rcn =
+                SbnBuilder().run {
+                    setPkg(SYSTEM_PACKAGE_NAME)
+                    modifyNotification(context).also {
+                        it.setSmallIcon(android.R.drawable.ic_media_pause)
+                        it.addExtras(bundle)
+                        it.setStyle(
+                            MediaStyle().apply { setRemotePlaybackInfo("Remote device", 0, null) }
+                        )
+                    }
+                    build()
+                }
+
+            val result = underTest.loadMediaData(KEY, rcn)
+            assertThat(result).isNull()
+        }
+
+    @Test
+    fun testLoadMediaDataInBg_invalidMediaRemoteIntentNoCrash() =
+        testScope.runTest {
+            val bundle = Bundle()
+            // wrong data type
+            bundle.putParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT, Bundle())
+            val rcn =
+                SbnBuilder().run {
+                    setPkg(SYSTEM_PACKAGE_NAME)
+                    modifyNotification(context).also {
+                        it.setSmallIcon(android.R.drawable.ic_media_pause)
+                        it.addExtras(bundle)
+                        it.setStyle(
+                            MediaStyle().apply {
+                                setMediaSession(session.sessionToken)
+                                setRemotePlaybackInfo("Remote device", 0, null)
+                            }
+                        )
+                    }
+                    build()
+                }
+
+            val result = underTest.loadMediaData(KEY, rcn)
+            assertThat(result).isNotNull()
+        }
+
+    private fun createMediaNotification(
+        mediaSession: MediaSession? = session,
+        applicationInfo: ApplicationInfo? = null
+    ): StatusBarNotification =
+        SbnBuilder().run {
+            setPkg(PACKAGE_NAME)
+            modifyNotification(context).also {
+                it.setSmallIcon(android.R.drawable.ic_media_pause)
+                it.setStyle(MediaStyle().apply { setMediaSession(mediaSession?.sessionToken) })
+                if (applicationInfo != null) {
+                    val bundle = Bundle()
+                    bundle.putParcelable(
+                        Notification.EXTRA_BUILDER_APPLICATION_INFO,
+                        applicationInfo
+                    )
+                    it.addExtras(bundle)
+                }
+            }
+            build()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
new file mode 100644
index 0000000..f6865f13
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notifications.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayActionsViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = kosmos.notificationsShadeOverlayActionsViewModel
+
+    @Test
+    fun upTransitionSceneKey_hidesShade() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+                .isEqualTo(Overlays.NotificationsShade)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+        }
+
+    @Test
+    fun back_hidesShade() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+                .isEqualTo(Overlays.NotificationsShade)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..88a1df1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.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.systemui.notifications.ui.viewmodel
+
+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.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayContentViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+
+    private val underTest = kosmos.notificationsShadeOverlayContentViewModel
+
+    @Test
+    fun onScrimClicked_hidesShade() =
+        testScope.runTest {
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            sceneInteractor.showOverlay(
+                overlay = Overlays.NotificationsShade,
+                loggingReason = "test",
+            )
+            assertThat(currentOverlays).contains(Overlays.NotificationsShade)
+
+            underTest.onScrimClicked()
+
+            assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
deleted file mode 100644
index 8f925d5..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,182 +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.notifications.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Swipe
-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.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.shade.ui.viewmodel.notificationsShadeSceneActionsViewModel
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
[email protected]
-@EnableSceneContainer
-class NotificationsShadeSceneActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
-    private val underTest by lazy { kosmos.notificationsShadeSceneActionsViewModel }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_lockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
-                .isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceUnlocked_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            unlockDevice()
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
-        testScope.runTest {
-            kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Up)).isNull()
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
-                .isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
-        testScope.runTest {
-            kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
-            val actions by collectLastValue(underTest.actions)
-            lockDevice()
-            unlockDevice()
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Up)).isNull()
-            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
-                .isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor // force the lazy; this will kick off StateFlows
-            runCurrent()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-            underTest.activateIn(this)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
-        }
-
-    private fun TestScope.lockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
-        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-        runCurrent()
-    }
-
-    private fun TestScope.unlockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-            SuccessFingerprintAuthenticationStatus(0, true)
-        )
-        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-        sceneInteractor.changeScene(Scenes.Gone, "reason")
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.kt
new file mode 100644
index 0000000..46b02e92
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModelTest.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.notifications.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+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.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.ui.viewmodel.notificationsShadeUserActionsViewModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class NotificationsShadeUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
+
+    private val underTest by lazy { kosmos.notificationsShadeUserActionsViewModel }
+
+    @Test
+    fun upTransitionSceneKey_deviceLocked_lockscreen() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            lockDevice()
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+                .isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            lockDevice()
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_deviceUnlocked_gone() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            lockDevice()
+            unlockDevice()
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+            assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value)
+                .isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
+        testScope.runTest {
+            val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
+            val actions by collectLastValue(underTest.actions)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+            sceneInteractor // force the lazy; this will kick off StateFlows
+            runCurrent()
+            sceneInteractor.changeScene(Scenes.Gone, "reason")
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(kosmos.homeSceneFamilyResolver.resolvedScene.value).isEqualTo(Scenes.Gone)
+        }
+
+    private fun TestScope.lockDevice() {
+        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
+
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+        runCurrent()
+    }
+
+    private fun TestScope.unlockDevice() {
+        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
+
+        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+            SuccessFingerprintAuthenticationStatus(0, true)
+        )
+        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+        sceneInteractor.changeScene(Scenes.Gone, "reason")
+        runCurrent()
+    }
+}
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
new file mode 100644
index 0000000..7203b61
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelTest.kt
@@ -0,0 +1,213 @@
+/*
+ * 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.composefragment.viewmodel
+
+import android.app.StatusBarManager
+import android.content.testableContext
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.testing.TestLifecycleOwner
+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.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.fgsManagerController
+import com.android.systemui.res.R
+import com.android.systemui.shade.largeScreenHeaderHelper
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestResult
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+class QSFragmentComposeViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
+    private val lifecycleOwner =
+        TestLifecycleOwner(
+            initialState = Lifecycle.State.CREATED,
+            coroutineDispatcher = kosmos.testDispatcher,
+        )
+
+    private val underTest by lazy {
+        kosmos.qsFragmentComposeViewModelFactory.create(lifecycleOwner.lifecycleScope)
+    }
+
+    @Before
+    fun setUp() {
+        Dispatchers.setMain(kosmos.testDispatcher)
+    }
+
+    @After
+    fun teardown() {
+        Dispatchers.resetMain()
+    }
+
+    // For now the state changes at 0.5f expansion. This will change once we implement animation
+    // (and this test will fail)
+    @Test
+    fun qsExpansionValueChanges_correctExpansionState() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                val expansionState by collectLastValue(underTest.expansionState)
+
+                underTest.qsExpansionValue = 0f
+                assertThat(expansionState)
+                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
+
+                underTest.qsExpansionValue = 0.3f
+                assertThat(expansionState)
+                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)
+
+                underTest.qsExpansionValue = 0.7f
+                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+
+                underTest.qsExpansionValue = 1f
+                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
+            }
+        }
+
+    @Test
+    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)
+            }
+        }
+
+    @Test
+    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)
+                    .isEqualTo(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
+            }
+        }
+
+    @Test
+    fun footerActionsControllerInit() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                underTest
+                runCurrent()
+                assertThat(fgsManagerController.initialized).isTrue()
+            }
+        }
+
+    @Test
+    fun statusBarState_followsController() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                val statusBarState by collectLastValue(underTest.statusBarState)
+                runCurrent()
+
+                sysuiStatusBarStateController.setState(StatusBarState.SHADE)
+                assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
+
+                sysuiStatusBarStateController.setState(StatusBarState.KEYGUARD)
+                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+                sysuiStatusBarStateController.setState(StatusBarState.SHADE_LOCKED)
+                assertThat(statusBarState).isEqualTo(StatusBarState.SHADE_LOCKED)
+            }
+        }
+
+    @Test
+    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)
+
+                sysuiStatusBarStateController.setUpcomingState(StatusBarState.KEYGUARD)
+                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+                sysuiStatusBarStateController.setUpcomingState(StatusBarState.SHADE)
+                assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+            }
+        }
+
+    @Test
+    fun qsEnabled_followsRepository() =
+        with(kosmos) {
+            testScope.testWithinLifecycle {
+                val qsEnabled by collectLastValue(underTest.qsEnabled)
+
+                fakeDisableFlagsRepository.disableFlags.value =
+                    DisableFlagsModel(disable2 = QS_DISABLE_FLAG)
+
+                assertThat(qsEnabled).isFalse()
+
+                fakeDisableFlagsRepository.disableFlags.value = DisableFlagsModel()
+
+                assertThat(qsEnabled).isTrue()
+            }
+        }
+
+    private inline fun TestScope.testWithinLifecycle(
+        crossinline block: suspend TestScope.() -> TestResult
+    ): TestResult {
+        return runTest {
+            lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
+            block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
+        }
+    }
+
+    companion object {
+        private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
index 661d4b0..e58cf15 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
 import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
 import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -40,15 +41,16 @@
         testKosmos().apply {
             defaultLargeTilesRepository =
                 object : DefaultLargeTilesRepository {
-                    override val defaultLargeTiles: Set<TileSpec> = setOf(TileSpec.create("large"))
+                    override val defaultLargeTiles: Set<TileSpec> = setOf(largeTile)
                 }
+            currentTilesInteractor.setTiles(listOf(largeTile, smallTile))
         }
     private val underTest = with(kosmos) { iconTilesInteractor }
 
     @Test
     fun isIconTile_returnsCorrectValue() {
-        assertThat(underTest.isIconTile(TileSpec.create("large"))).isFalse()
-        assertThat(underTest.isIconTile(TileSpec.create("small"))).isTrue()
+        assertThat(underTest.isIconTile(largeTile)).isFalse()
+        assertThat(underTest.isIconTile(smallTile)).isTrue()
     }
 
     @OptIn(ExperimentalCoroutinesApi::class)
@@ -56,14 +58,21 @@
     fun isIconTile_updatesFromSharedPreferences() =
         with(kosmos) {
             testScope.runTest {
-                // Assert that new tile defaults to icon
-                assertThat(underTest.isIconTile(TileSpec.create("newTile"))).isTrue()
+                val spec = TileSpec.create("newTile")
 
-                qsPreferencesRepository.setLargeTilesSpecs(setOf(TileSpec.create("newTile")))
+                // Assert that new tile defaults to icon
+                assertThat(underTest.isIconTile(spec)).isTrue()
+
+                // Add the tile
+                currentTilesInteractor.addTile(spec)
+                runCurrent()
+
+                // Resize it to large
+                qsPreferencesRepository.setLargeTilesSpecs(setOf(spec))
                 runCurrent()
 
                 // Assert that the new tile was added to the large tiles set
-                assertThat(underTest.isIconTile(TileSpec.create("newTile"))).isFalse()
+                assertThat(underTest.isIconTile(spec)).isFalse()
             }
         }
 
@@ -72,21 +81,57 @@
     fun resize_updatesSharedPreferences() =
         with(kosmos) {
             testScope.runTest {
-                qsPreferencesRepository.setLargeTilesSpecs(setOf())
-                runCurrent()
-
                 val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
-                val spec = TileSpec.create("large")
-
-                // Assert that the tile is added to the large tiles after resizing
-                underTest.resize(spec)
                 runCurrent()
-                assertThat(latest).contains(spec)
 
                 // Assert that the tile is removed from the large tiles after resizing
-                underTest.resize(spec)
+                underTest.resize(largeTile)
                 runCurrent()
-                assertThat(latest).doesNotContain(spec)
+                assertThat(latest).doesNotContain(largeTile)
+
+                // Assert that the tile is added to the large tiles after resizing
+                underTest.resize(largeTile)
+                runCurrent()
+                assertThat(latest).contains(largeTile)
             }
         }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun removingTile_updatesSharedPreferences() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
+                runCurrent()
+
+                // Remove the large tile from the current tiles
+                currentTilesInteractor.removeTiles(listOf(largeTile))
+                runCurrent()
+
+                // Assert that it resized to small
+                assertThat(latest).doesNotContain(largeTile)
+            }
+        }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun resizingNonCurrentTile_doesNothing() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(qsPreferencesRepository.largeTilesSpecs)
+                val newTile = TileSpec.create("newTile")
+
+                // Remove the large tile from the current tiles
+                underTest.resize(newTile)
+                runCurrent()
+
+                // Assert that it's still small
+                assertThat(latest).doesNotContain(newTile)
+            }
+        }
+
+    private companion object {
+        private val largeTile = TileSpec.create("large")
+        private val smallTile = TileSpec.create("small")
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
deleted file mode 100644
index b2f5765..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt
+++ /dev/null
@@ -1,123 +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.compose
-
-import androidx.compose.runtime.mutableStateOf
-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.common.shared.model.Text
-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.viewmodel.EditTileViewModel
-import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DragAndDropStateTest : SysuiTestCase() {
-    private val listState = EditTileListState(TestEditTiles)
-    private val underTest = DragAndDropState(mutableStateOf(null), listState)
-
-    @Test
-    fun isMoving_returnsCorrectValue() {
-        // Asserts no tiles is moving
-        TestEditTiles.forEach { assertThat(underTest.isMoving(it.tile.tileSpec)).isFalse() }
-
-        // Start the drag movement
-        underTest.onStarted(TestEditTiles[0])
-
-        // Assert that the correct tile is marked as moving
-        TestEditTiles.forEach {
-            assertThat(underTest.isMoving(it.tile.tileSpec))
-                .isEqualTo(TestEditTiles[0].tile.tileSpec == it.tile.tileSpec)
-        }
-    }
-
-    @Test
-    fun onMoved_updatesList() {
-        // Start the drag movement
-        underTest.onStarted(TestEditTiles[0])
-
-        // Move the tile to the end of the list
-        underTest.onMoved(listState.tiles[5].tile.tileSpec)
-        assertThat(underTest.currentPosition()).isEqualTo(5)
-
-        // Move the tile to the middle of the list
-        underTest.onMoved(listState.tiles[2].tile.tileSpec)
-        assertThat(underTest.currentPosition()).isEqualTo(2)
-    }
-
-    @Test
-    fun onDrop_resetsMovingTile() {
-        // Start the drag movement
-        underTest.onStarted(TestEditTiles[0])
-
-        // Move the tile to the end of the list
-        underTest.onMoved(listState.tiles[5].tile.tileSpec)
-
-        // Drop the tile
-        underTest.onDrop()
-
-        // Asserts no tiles is moving
-        TestEditTiles.forEach { assertThat(underTest.isMoving(it.tile.tileSpec)).isFalse() }
-    }
-
-    @Test
-    fun onMoveOutOfBounds_removeMovingTileFromCurrentList() {
-        // Start the drag movement
-        underTest.onStarted(TestEditTiles[0])
-
-        // Move the tile outside of the list
-        underTest.movedOutOfBounds()
-
-        // Asserts the moving tile is not current
-        assertThat(
-                listState.tiles.firstOrNull { it.tile.tileSpec == TestEditTiles[0].tile.tileSpec }
-            )
-            .isNull()
-    }
-
-    companion object {
-        private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> {
-            return SizedTileImpl(
-                EditTileViewModel(
-                    tileSpec = TileSpec.create(tileSpec),
-                    icon = Icon.Resource(0, null),
-                    label = Text.Loaded("unused"),
-                    appName = null,
-                    isCurrent = true,
-                    availableEditActions = emptySet(),
-                ),
-                1,
-            )
-        }
-
-        private val TestEditTiles =
-            listOf(
-                createEditTile("tileA"),
-                createEditTile("tileB"),
-                createEditTile("tileC"),
-                createEditTile("tileD"),
-                createEditTile("tileE"),
-                createEditTile("tileF"),
-            )
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index a3a6a33..7f01fad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -23,6 +23,9 @@
 import com.android.systemui.common.shared.model.Text
 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.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.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.google.common.truth.Truth.assertThat
@@ -32,80 +35,130 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EditTileListStateTest : SysuiTestCase() {
-    val underTest = EditTileListState(TestEditTiles)
+    private val underTest = EditTileListState(TestEditTiles, 4)
 
     @Test
-    fun movingNonExistentTile_tileAdded() {
-        val newTile = createEditTile("other_tile", false)
-        underTest.move(newTile, TestEditTiles[0].tile.tileSpec)
-
-        assertThat(underTest.tiles[0]).isEqualTo(newTile)
-        assertThat(underTest.tiles.subList(1, underTest.tiles.size))
-            .containsExactly(*TestEditTiles.toTypedArray())
+    fun noDrag_listUnchanged() {
+        underTest.tiles.forEach { assertThat(it).isNotInstanceOf(SpacerGridCell::class.java) }
+        assertThat(underTest.tiles.map { (it as TileGridCell).tile.tileSpec })
+            .containsExactly(*TestEditTiles.map { it.tile.tileSpec }.toTypedArray())
     }
 
     @Test
-    fun movingTileToNonExistentTarget_listUnchanged() {
-        underTest.move(TestEditTiles[0], TileSpec.create("other_tile"))
+    fun startDrag_listHasSpacers() {
+        underTest.onStarted(TestEditTiles[0])
 
-        assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
+        // [ a ] [ b ] [ c ] [ X ]
+        // [ Large D ] [ e ] [ X ]
+        assertThat(underTest.tiles.toStrings())
+            .isEqualTo(listOf("a", "b", "c", "spacer", "d", "e", "spacer"))
+        assertThat(underTest.isMoving(TestEditTiles[0].tile.tileSpec)).isTrue()
+        assertThat(underTest.dragInProgress).isTrue()
     }
 
     @Test
-    fun movingTileToItself_listUnchanged() {
-        underTest.move(TestEditTiles[0], TestEditTiles[0].tile.tileSpec)
+    fun moveDrag_listChanges() {
+        underTest.onStarted(TestEditTiles[4])
+        underTest.onMoved(3, false)
 
-        assertThat(underTest.tiles).containsExactly(*TestEditTiles.toTypedArray())
+        // Tile E goes to index 3
+        // [ a ] [ b ] [ c ] [ e ]
+        // [ Large D ] [ X ] [ X ]
+        assertThat(underTest.tiles.toStrings())
+            .isEqualTo(listOf("a", "b", "c", "e", "d", "spacer", "spacer"))
     }
 
     @Test
-    fun movingTileToSameSection_listUpdates() {
-        // Move tile at index 0 to index 1. Tile 0 should remain current.
-        underTest.move(TestEditTiles[0], TestEditTiles[1].tile.tileSpec)
+    fun moveDragOnSidesOfLargeTile_listChanges() {
+        val draggedCell = TestEditTiles[4]
 
-        // Assert the tiles 0 and 1 have changed places.
-        assertThat(underTest.tiles[0]).isEqualTo(TestEditTiles[1])
-        assertThat(underTest.tiles[1]).isEqualTo(TestEditTiles[0])
+        underTest.onStarted(draggedCell)
+        underTest.onMoved(4, true)
 
-        // Assert the rest of the list is unchanged
-        assertThat(underTest.tiles.subList(2, 5))
-            .containsExactly(*TestEditTiles.subList(2, 5).toTypedArray())
+        // Tile E goes to the right side of tile D, list is unchanged
+        // [ a ] [ b ] [ c ] [ X ]
+        // [ Large D ] [ e ] [ X ]
+        assertThat(underTest.tiles.toStrings())
+            .isEqualTo(listOf("a", "b", "c", "spacer", "d", "e", "spacer"))
+
+        underTest.onMoved(4, false)
+
+        // Tile E goes to the left side of tile D, they swap positions
+        // [ a ] [ b ] [ c ] [ e ]
+        // [ Large D ] [ X ] [ X ]
+        assertThat(underTest.tiles.toStrings())
+            .isEqualTo(listOf("a", "b", "c", "e", "d", "spacer", "spacer"))
     }
 
-    fun removingTile_listUpdates() {
-        // Remove tile at index 0
-        underTest.remove(TestEditTiles[0].tile.tileSpec)
+    @Test
+    fun moveNewTile_tileIsAdded() {
+        val newTile = createEditTile("newTile", 2)
 
-        // Assert the tile was removed
-        assertThat(underTest.tiles).containsExactly(*TestEditTiles.subList(1, 6).toTypedArray())
+        underTest.onStarted(newTile)
+        underTest.onMoved(5, false)
+
+        // New tile goes to index 5
+        // [ a ] [ b ] [ c ] [ X ]
+        // [ Large D ] [ newTile ]
+        // [ e ] [ X ] [ X ] [ X ]
+        assertThat(underTest.tiles.toStrings())
+            .isEqualTo(
+                listOf("a", "b", "c", "spacer", "d", "newTile", "e", "spacer", "spacer", "spacer")
+            )
+    }
+
+    @Test
+    fun droppedNewTile_spacersDisappear() {
+        underTest.onStarted(TestEditTiles[0])
+        underTest.onDrop()
+
+        assertThat(underTest.tiles.toStrings()).isEqualTo(listOf("a", "b", "c", "d", "e"))
+        assertThat(underTest.isMoving(TestEditTiles[0].tile.tileSpec)).isFalse()
+        assertThat(underTest.dragInProgress).isFalse()
+    }
+
+    @Test
+    fun movedTileOutOfBounds_tileDisappears() {
+        underTest.onStarted(TestEditTiles[0])
+        underTest.movedOutOfBounds()
+
+        assertThat(underTest.tiles.toStrings()).doesNotContain(TestEditTiles[0].tile.tileSpec.spec)
+    }
+
+    private fun List<GridCell>.toStrings(): List<String> {
+        return map {
+            if (it is TileGridCell) {
+                it.tile.tileSpec.spec
+            } else {
+                "spacer"
+            }
+        }
     }
 
     companion object {
-        private fun createEditTile(
-            tileSpec: String,
-            isCurrent: Boolean
-        ): SizedTile<EditTileViewModel> {
+        private fun createEditTile(tileSpec: String, width: Int): SizedTile<EditTileViewModel> {
             return SizedTileImpl(
                 EditTileViewModel(
                     tileSpec = TileSpec.create(tileSpec),
                     icon = Icon.Resource(0, null),
                     label = Text.Loaded("unused"),
                     appName = null,
-                    isCurrent = isCurrent,
+                    isCurrent = true,
                     availableEditActions = emptySet(),
                 ),
-                1,
+                width,
             )
         }
 
+        // [ a ] [ b ] [ c ]
+        // [ Large D ] [ e ] [ f ]
         private val TestEditTiles =
             listOf(
-                createEditTile("tileA", true),
-                createEditTile("tileB", true),
-                createEditTile("tileC", true),
-                createEditTile("tileD", false),
-                createEditTile("tileE", false),
-                createEditTile("tileF", false),
+                createEditTile("a", 1),
+                createEditTile("b", 1),
+                createEditTile("c", 1),
+                createEditTile("d", 2),
+                createEditTile("e", 1),
             )
     }
 }
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 56156a8..ef85302 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
@@ -24,8 +24,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
-import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
+import com.android.systemui.qs.panels.domain.interactor.qsPreferencesInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.res.R
@@ -56,11 +55,9 @@
 
     private val kosmos =
         testKosmos().apply {
-            defaultLargeTilesRepository =
-                object : DefaultLargeTilesRepository {
-                    override val defaultLargeTiles: Set<TileSpec> =
-                        tiles.filter { it.spec.startsWith(PREFIX_LARGE) }.toSet()
-                }
+            qsPreferencesInteractor.setLargeTilesSpecs(
+                tiles.filter { it.spec.startsWith(PREFIX_LARGE) }.toSet()
+            )
         }
 
     private val underTest = kosmos.quickQuickSettingsViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
index d153e9d..5619022 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/AutoAddableSettingTest.kt
@@ -21,14 +21,15 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.pipeline.domain.model.AutoAddSignal
 import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -38,10 +39,11 @@
 @RunWith(AndroidJUnit4::class)
 class AutoAddableSettingTest : SysuiTestCase() {
 
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val secureSettings = kosmos.fakeSettings
 
-    private val secureSettings = FakeSettings()
     private val underTest =
         AutoAddableSetting(
             secureSettings,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 3146318..8995f46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -24,7 +24,6 @@
 import android.service.quicksettings.Tile
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
 import com.android.systemui.Flags.FLAG_QS_NEW_TILES
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -107,7 +106,6 @@
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
-        mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
         mSetFlagsRule.enableFlags(FLAG_QS_NEW_TILES)
 
         userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
index e8ad038..00c7204 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt
@@ -20,7 +20,6 @@
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -98,8 +97,6 @@
 
     @Before
     fun setUp() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
         with(kosmos) {
             restoreReconciliationInteractor.start()
             autoAddInteractor.init(kosmos.currentTilesInteractor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
index dffd0d7..6bcaea4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/WorkProfileAutoAddedAfterRestoreTest.kt
@@ -20,7 +20,6 @@
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
-import com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
@@ -59,6 +58,7 @@
     // Getter here so it can change when there is a managed profile.
     private val workTileAvailable: Boolean
         get() = hasManagedProfile()
+
     private val currentUser: Int
         get() = kosmos.userTracker.userId
 
@@ -67,8 +67,6 @@
 
     @Before
     fun setUp() {
-        mSetFlagsRule.enableFlags(FLAG_QS_NEW_PIPELINE)
-
         kosmos.qsTileFactory = FakeQSFactory(::tileCreator)
         kosmos.restoreReconciliationInteractor.start()
         kosmos.autoAddInteractor.init(kosmos.currentTilesInteractor)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
new file mode 100644
index 0000000..5a73fe2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.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.qs.tiles.impl
+
+import android.graphics.drawable.TestStubDrawable
+import android.service.quicksettings.Tile
+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.airplane.domain.AirplaneModeMapper
+import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
+import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AirplaneModeMapperTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
+
+    private lateinit var mapper: AirplaneModeMapper
+
+    @Before
+    fun setup() {
+        mapper =
+            AirplaneModeMapper(
+                context.orCreateTestableResources
+                    .apply {
+                        addOverride(R.drawable.qs_airplane_icon_off, TestStubDrawable())
+                        addOverride(R.drawable.qs_airplane_icon_on, TestStubDrawable())
+                    }
+                    .resources,
+                context.theme,
+            )
+    }
+
+    @Test
+    fun enabledModel_mapsCorrectly() {
+        val inputModel = AirplaneModeTileModel(true)
+
+        val outputState = mapper.map(airplaneModeConfig, inputModel)
+
+        val expectedState =
+            createAirplaneModeState(
+                QSTileState.ActivationState.ACTIVE,
+                context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_ACTIVE],
+                R.drawable.qs_airplane_icon_on
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    @Test
+    fun disabledModel_mapsCorrectly() {
+        val inputModel = AirplaneModeTileModel(false)
+
+        val outputState = mapper.map(airplaneModeConfig, inputModel)
+
+        val expectedState =
+            createAirplaneModeState(
+                QSTileState.ActivationState.INACTIVE,
+                context.resources.getStringArray(R.array.tile_states_airplane)[Tile.STATE_INACTIVE],
+                R.drawable.qs_airplane_icon_off
+            )
+        QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+    }
+
+    private fun createAirplaneModeState(
+        activationState: QSTileState.ActivationState,
+        secondaryLabel: String,
+        iconRes: Int
+    ): QSTileState {
+        val label = context.getString(R.string.airplane_mode)
+        return QSTileState(
+            { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+            iconRes,
+            label,
+            activationState,
+            secondaryLabel,
+            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            label,
+            null,
+            QSTileState.SideViewIcon.None,
+            QSTileState.EnabledState.ENABLED,
+            Switch::class.qualifiedName
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
index 79fcc92..d27e810 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
@@ -29,8 +29,9 @@
 import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -40,8 +41,9 @@
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class AirplaneModeTileUserActionInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
-    private val mobileConnectionsRepository = FakeMobileConnectionsRepository()
+    private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
     private val connectivityRepository = FakeConnectivityRepository()
     private val airplaneModeRepository = FakeAirplaneModeRepository()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index dfc004a..c9869bdb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -280,9 +280,12 @@
         }
 
     @Test
-    fun isActiveFollowsPackageManagerAdapter() =
+    fun isActiveFollowsPackageManagerAdapter_user0() =
         with(kosmos) {
             testScope.runTest {
+                packageManagerAdapterFacade.setExclusiveForUser(0)
+
+                underTest.updateWithDefaults(UserHandle.of(0), TEST_DEFAULTS_1, true)
                 packageManagerAdapterFacade.setIsActive(false)
                 assertThat(underTest.isTileActive()).isFalse()
 
@@ -295,6 +298,7 @@
     fun isToggleableFollowsPackageManagerAdapter() =
         with(kosmos) {
             testScope.runTest {
+                underTest.updateWithDefaults(UserHandle.of(0), TEST_DEFAULTS_1, true)
                 packageManagerAdapterFacade.setIsToggleable(false)
                 assertThat(underTest.isTileToggleable()).isFalse()
 
@@ -303,6 +307,66 @@
             }
         }
 
+    @Test
+    fun isActiveFollowsPackageManagerAdapter_user10_withAdapterForUser10() =
+        with(kosmos) {
+            testScope.runTest {
+                packageManagerAdapterFacade.setExclusiveForUser(10)
+
+                underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true)
+                packageManagerAdapterFacade.setIsActive(false)
+                assertThat(underTest.isTileActive()).isFalse()
+
+                packageManagerAdapterFacade.setIsActive(true)
+                assertThat(underTest.isTileActive()).isTrue()
+            }
+        }
+
+    @Test
+    fun isToggleableFollowsPackageManagerAdapter_user10_withAdapterForUser10() =
+        with(kosmos) {
+            testScope.runTest {
+                packageManagerAdapterFacade.setExclusiveForUser(10)
+
+                underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true)
+                packageManagerAdapterFacade.setIsToggleable(false)
+                assertThat(underTest.isTileToggleable()).isFalse()
+
+                packageManagerAdapterFacade.setIsToggleable(true)
+                assertThat(underTest.isTileToggleable()).isTrue()
+            }
+        }
+
+    @Test
+    fun isActiveDoesntFollowPackageManagerAdapter_user10() =
+        with(kosmos) {
+            testScope.runTest {
+                packageManagerAdapterFacade.setExclusiveForUser(0)
+
+                underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true)
+                packageManagerAdapterFacade.setIsActive(false)
+                assertThat(underTest.isTileActive()).isFalse()
+
+                packageManagerAdapterFacade.setIsActive(true)
+                assertThat(underTest.isTileActive()).isFalse()
+            }
+        }
+
+    @Test
+    fun isToggleableDoesntFollowPackageManagerAdapter_user10() =
+        with(kosmos) {
+            testScope.runTest {
+                packageManagerAdapterFacade.setExclusiveForUser(0)
+
+                underTest.updateWithDefaults(UserHandle.of(10), TEST_DEFAULTS_1, true)
+                packageManagerAdapterFacade.setIsToggleable(false)
+                assertThat(underTest.isTileToggleable()).isFalse()
+
+                packageManagerAdapterFacade.setIsToggleable(true)
+                assertThat(underTest.isTileToggleable()).isFalse()
+            }
+        }
+
     private companion object {
 
         val TEST_COMPONENT = ComponentName("test.pkg", "test.cls")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
index b4ff565..f1d08c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.IUriGrantsManager
 import android.content.ComponentName
+import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.graphics.drawable.TestStubDrawable
@@ -51,11 +52,13 @@
 class CustomTileMapperTest : SysuiTestCase() {
 
     private val uriGrantsManager: IUriGrantsManager = mock {}
+    private val mockContext =
+        mock<Context> { whenever(createContextAsUser(any(), any())).thenReturn(context) }
     private val kosmos =
         testKosmos().apply { customTileSpec = TileSpec.Companion.create(TEST_COMPONENT) }
     private val underTest by lazy {
         CustomTileMapper(
-            context = mock { whenever(createContextAsUser(any(), any())).thenReturn(context) },
+            context = mockContext,
             uriGrantsManager = uriGrantsManager,
         )
     }
@@ -164,7 +167,7 @@
                     )
                 val expected =
                     createTileState(
-                        activationState = QSTileState.ActivationState.INACTIVE,
+                        activationState = QSTileState.ActivationState.UNAVAILABLE,
                         icon = DEFAULT_DRAWABLE,
                     )
 
@@ -173,7 +176,7 @@
         }
 
     @Test
-    fun failedToLoadIconTileIsInactive() =
+    fun failedToLoadIconTileIsUnavailable() =
         with(kosmos) {
             testScope.runTest {
                 val actual =
@@ -187,13 +190,32 @@
                 val expected =
                     createTileState(
                         icon = null,
-                        activationState = QSTileState.ActivationState.INACTIVE,
+                        activationState = QSTileState.ActivationState.UNAVAILABLE,
                     )
 
                 assertThat(actual).isEqualTo(expected)
             }
         }
 
+    @Test
+    fun nullUserContextDoesNotCauseExceptionReturnsNullIconAndUnavailableState() =
+        with(kosmos) {
+            testScope.runTest {
+                // map() will catch this exception
+                whenever(mockContext.createContextAsUser(any(), any()))
+                    .thenThrow(IllegalStateException("Unable to create userContext"))
+
+                val actual = underTest.map(customTileQsTileConfig, createModel())
+
+                val expected =
+                    createTileState(
+                        icon = null,
+                        activationState = QSTileState.ActivationState.UNAVAILABLE,
+                    )
+                assertThat(actual).isEqualTo(expected)
+            }
+        }
+
     private fun Kosmos.createModel(
         tileState: Int = Tile.STATE_ACTIVE,
         tileIcon: Icon = createIcon(DRAWABLE, false),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
index c44836a..620e90d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
@@ -117,7 +117,11 @@
             label,
             activationState,
             secondaryLabel,
-            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+            setOf(
+                QSTileState.UserAction.CLICK,
+                QSTileState.UserAction.TOGGLE_CLICK,
+                QSTileState.UserAction.LONG_CLICK
+            ),
             contentDescription,
             null,
             QSTileState.SideViewIcon.Chevron,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 13d6411..aaad0fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.res.R
@@ -86,7 +86,7 @@
     private val wifiInteractor =
         WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
 
-    private val tableLogBuffer: TableLogBuffer = mock()
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "InternetTileDataInteractorTest")
     private val carrierConfigTracker: CarrierConfigTracker = mock()
 
     private val mobileConnectionsRepository =
@@ -185,7 +185,6 @@
 
             val networkModel =
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 4,
                     ssid = "test ssid",
                 )
@@ -204,7 +203,7 @@
 
             val actualIcon = latest?.icon
             assertThat(actualIcon).isEqualTo(expectedIcon)
-            assertThat(latest?.iconId).isNull()
+            assertThat(latest?.iconId).isEqualTo(WifiIcons.WIFI_NO_INTERNET_ICONS[4])
             assertThat(latest?.contentDescription.loadContentDescription(context))
                 .isEqualTo("$internet,test ssid")
             val expectedSd = wifiIcon.contentDescription
@@ -221,7 +220,6 @@
 
             val networkModel =
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 4,
                     ssid = "test ssid",
                     hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -443,15 +441,15 @@
      * on the mentioned context. Since that context does not have a looper assigned to it, the
      * handler instantiation will throw a RuntimeException.
      *
-     * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception
-     * So either we should make Robolectric behvase similar to the device test, or change this
-     * test to look for a different signal than the exception, when run by Robolectric. For now
-     * we just assume the test is not Robolectric.
+     * TODO(b/338068066): Robolectric behavior differs in that it does not throw the exception So
+     *   either we should make Robolectric behave similar to the device test, or change this test to
+     *   look for a different signal than the exception, when run by Robolectric. For now we just
+     *   assume the test is not Robolectric.
      */
     @Test(expected = java.lang.RuntimeException::class)
     fun mobileDefault_usesNetworkNameAndIcon_throwsRunTimeException() =
         testScope.runTest {
-            assumeFalse(isRobolectricTest());
+            assumeFalse(isRobolectricTest())
 
             collectLastValue(underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest)))
 
@@ -546,7 +544,6 @@
     private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
         val networkModel =
             WifiNetworkModel.Active(
-                networkId = 1,
                 level = 4,
                 ssid = "test ssid",
                 hotspotDeviceType = hotspot,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index e1f3d97..52c476e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import com.android.systemui.util.mockito.mock
@@ -40,6 +41,8 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.times
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @EnabledOnRavenwood
@@ -51,17 +54,20 @@
     private lateinit var underTest: InternetTileUserActionInteractor
 
     @Mock private lateinit var internetDialogManager: InternetDialogManager
+    @Mock private lateinit var wifiStateWorker: WifiStateWorker
     @Mock private lateinit var controller: AccessPointController
 
     @Before
     fun setup() {
         internetDialogManager = mock<InternetDialogManager>()
+        wifiStateWorker = mock<WifiStateWorker>()
         controller = mock<AccessPointController>()
 
         underTest =
             InternetTileUserActionInteractor(
                 kosmos.testScope.coroutineContext,
                 internetDialogManager,
+                wifiStateWorker,
                 controller,
                 inputHandler,
             )
@@ -110,4 +116,24 @@
                 Truth.assertThat(it.intent.action).isEqualTo(Settings.ACTION_WIFI_SETTINGS)
             }
         }
+
+    @Test
+    fun handleSecondaryClickWhenWifiOn() =
+        kosmos.testScope.runTest {
+            whenever(wifiStateWorker.isWifiEnabled).thenReturn(true)
+
+            underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Active()))
+
+            verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false)
+        }
+
+    @Test
+    fun handleSecondaryClickWhenWifiOff() =
+        kosmos.testScope.runTest {
+            whenever(wifiStateWorker.isWifiEnabled).thenReturn(false)
+
+            underTest.handleInput(QSTileInputTestKtx.toggleClick(InternetTileModel.Inactive()))
+
+            verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 69b8ee1..91d8e2a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -16,19 +16,25 @@
 
 package com.android.systemui.qs.tiles.impl.modes.domain.interactor
 
+import android.app.AutomaticZenRule
 import android.app.Flags
+import android.graphics.drawable.TestStubDrawable
 import android.os.UserHandle
 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.internal.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 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.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -36,6 +42,7 @@
 import kotlinx.coroutines.flow.toCollection
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -48,7 +55,16 @@
     private val dispatcher = kosmos.testDispatcher
     private val zenModeRepository = kosmos.fakeZenModeRepository
 
-    private val underTest = ModesTileDataInteractor(zenModeRepository, dispatcher)
+    private val underTest = ModesTileDataInteractor(context, kosmos.zenModeInteractor, dispatcher)
+
+    @Before
+    fun setUp() {
+        context.orCreateTestableResources.apply {
+            addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE)
+            addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
+            addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE)
+        }
+    }
 
     @EnableFlags(Flags.FLAG_MODES_UI)
     @Test
@@ -110,8 +126,130 @@
             assertThat(dataList.map { it.activeModes }.last()).isEmpty()
         }
 
-    private companion object {
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun tileData_iconsFlagEnabled_changesIconWhenActiveModesChange() =
+        testScope.runTest {
+            val tileData by
+                collectLastValue(
+                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+                )
 
+            // Tile starts with the generic Modes icon.
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+            // Add an inactive mode -> Still modes icon
+            zenModeRepository.addMode(id = "Mode", active = false)
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+            // Add an active mode: icon should be the mode icon. No iconResId, because we don't
+            // really know that it's a system icon.
+            zenModeRepository.addMode(
+                id = "Bedtime",
+                type = AutomaticZenRule.TYPE_BEDTIME,
+                active = true
+            )
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
+            assertThat(tileData?.iconResId).isNull()
+
+            // Add another, less-prioritized mode: icon should remain the first mode icon
+            zenModeRepository.addMode(
+                id = "Driving",
+                type = AutomaticZenRule.TYPE_DRIVING,
+                active = true
+            )
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
+            assertThat(tileData?.iconResId).isNull()
+
+            // Deactivate more important mode: icon should be the less important, still active mode
+            zenModeRepository.deactivateMode("Bedtime")
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(DRIVING_ICON)
+            assertThat(tileData?.iconResId).isNull()
+
+            // Deactivate remaining mode: back to the default modes icon
+            zenModeRepository.deactivateMode("Driving")
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
+    fun tileData_iconsFlagDisabled_hasPriorityModesIcon() =
+        testScope.runTest {
+            val tileData by
+                collectLastValue(
+                    underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))
+                )
+
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+            // Activate a Mode -> Icon doesn't change.
+            zenModeRepository.addMode(id = "Mode", active = true)
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+
+            zenModeRepository.deactivateMode(id = "Mode")
+            runCurrent()
+            assertThat(tileData?.icon).isEqualTo(MODES_ICON)
+            assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
+        }
+
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    @Test
+    fun getCurrentTileModel_returnsActiveModes() = runTest {
+        var tileData = underTest.getCurrentTileModel()
+        assertThat(tileData.isActivated).isFalse()
+        assertThat(tileData.activeModes).isEmpty()
+
+        // Add active mode
+        zenModeRepository.addMode(id = "One", active = true)
+        tileData = underTest.getCurrentTileModel()
+        assertThat(tileData.isActivated).isTrue()
+        assertThat(tileData.activeModes).containsExactly("Mode One")
+
+        // Add an inactive mode: state hasn't changed
+        zenModeRepository.addMode(id = "Two", active = false)
+        tileData = underTest.getCurrentTileModel()
+        assertThat(tileData.isActivated).isTrue()
+        assertThat(tileData.activeModes).containsExactly("Mode One")
+
+        // Add another active mode
+        zenModeRepository.addMode(id = "Three", active = true)
+        tileData = underTest.getCurrentTileModel()
+        assertThat(tileData.isActivated).isTrue()
+        assertThat(tileData.activeModes).containsExactly("Mode One", "Mode Three").inOrder()
+
+        // Remove a mode and deactivate the other
+        zenModeRepository.removeMode("One")
+        zenModeRepository.deactivateMode("Three")
+        tileData = underTest.getCurrentTileModel()
+        assertThat(tileData.isActivated).isFalse()
+        assertThat(tileData.activeModes).isEmpty()
+    }
+
+    private companion object {
         val TEST_USER = UserHandle.of(1)!!
+
+        val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
+
+        val MODES_DRAWABLE = TestStubDrawable("modes_icon")
+        val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
+        val DRIVING_DRAWABLE = TestStubDrawable("driving")
+
+        val MODES_ICON = MODES_DRAWABLE.asIcon()
+        val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon()
+        val DRIVING_ICON = DRIVING_DRAWABLE.asIcon()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 4b75649..cd58127 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.qs.tiles.impl.modes.domain.interactor
 
+import android.graphics.drawable.TestStubDrawable
 import android.platform.test.annotations.EnableFlags
 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.animation.Expandable
+import com.android.systemui.common.shared.model.asIcon
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
@@ -54,10 +56,7 @@
     fun handleClick_active() = runTest {
         val expandable = mock<Expandable>()
         underTest.handleInput(
-            QSTileInputTestKtx.click(
-                data = ModesTileModel(true, listOf("DND")),
-                expandable = expandable
-            )
+            QSTileInputTestKtx.click(data = modelOf(true, listOf("DND")), expandable = expandable)
         )
 
         verify(mockDialogDelegate).showDialog(eq(expandable))
@@ -67,10 +66,7 @@
     fun handleClick_inactive() = runTest {
         val expandable = mock<Expandable>()
         underTest.handleInput(
-            QSTileInputTestKtx.click(
-                data = ModesTileModel(false, emptyList()),
-                expandable = expandable
-            )
+            QSTileInputTestKtx.click(data = modelOf(false, emptyList()), expandable = expandable)
         )
 
         verify(mockDialogDelegate).showDialog(eq(expandable))
@@ -78,7 +74,7 @@
 
     @Test
     fun handleLongClick_active() = runTest {
-        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(true, listOf("DND"))))
+        underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(true, listOf("DND"))))
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
             assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
@@ -87,10 +83,14 @@
 
     @Test
     fun handleLongClick_inactive() = runTest {
-        underTest.handleInput(QSTileInputTestKtx.longClick(ModesTileModel(false, emptyList())))
+        underTest.handleInput(QSTileInputTestKtx.longClick(modelOf(false, emptyList())))
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
             assertThat(it.intent.action).isEqualTo(Settings.ACTION_ZEN_MODE_SETTINGS)
         }
     }
+
+    private fun modelOf(isActivated: Boolean, activeModeNames: List<String>): ModesTileModel {
+        return ModesTileModel(isActivated, activeModeNames, TestStubDrawable("icon").asIcon(), 123)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index dd9711e..f7bdcb8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -16,10 +16,14 @@
 
 package com.android.systemui.qs.tiles.impl.modes.ui
 
+import android.app.Flags
 import android.graphics.drawable.TestStubDrawable
+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.shared.model.asIcon
 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
 import com.android.systemui.qs.tiles.viewmodel.QSTileState
@@ -31,6 +35,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
+@EnableFlags(Flags.FLAG_MODES_UI)
 class ModesTileMapperTest : SysuiTestCase() {
     val config =
         QSTileConfigTestBuilder.build {
@@ -54,35 +59,88 @@
 
     @Test
     fun inactiveState() {
-        val model = ModesTileModel(isActivated = false, activeModes = emptyList())
+        val icon = TestStubDrawable("res123").asIcon()
+        val model =
+            ModesTileModel(
+                isActivated = false,
+                activeModes = emptyList(),
+                icon = icon,
+            )
 
         val state = underTest.map(config, model)
 
         assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.INACTIVE)
-        assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_off)
+        assertThat(state.icon()).isEqualTo(icon)
         assertThat(state.secondaryLabel).isEqualTo("No active modes")
     }
 
     @Test
     fun activeState_oneMode() {
-        val model = ModesTileModel(isActivated = true, activeModes = listOf("DND"))
+        val icon = TestStubDrawable("res123").asIcon()
+        val model =
+            ModesTileModel(
+                isActivated = true,
+                activeModes = listOf("DND"),
+                icon = icon,
+            )
 
         val state = underTest.map(config, model)
 
         assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
-        assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.icon()).isEqualTo(icon)
         assertThat(state.secondaryLabel).isEqualTo("DND is active")
     }
 
     @Test
     fun activeState_multipleModes() {
+        val icon = TestStubDrawable("res123").asIcon()
         val model =
-            ModesTileModel(isActivated = true, activeModes = listOf("Mode 1", "Mode 2", "Mode 3"))
+            ModesTileModel(
+                isActivated = true,
+                activeModes = listOf("Mode 1", "Mode 2", "Mode 3"),
+                icon = icon,
+            )
 
         val state = underTest.map(config, model)
 
         assertThat(state.activationState).isEqualTo(QSTileState.ActivationState.ACTIVE)
-        assertThat(state.iconRes).isEqualTo(R.drawable.qs_dnd_icon_on)
+        assertThat(state.icon()).isEqualTo(icon)
         assertThat(state.secondaryLabel).isEqualTo("3 modes are active")
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI_ICONS)
+    fun state_withEnabledFlag_noIconResId() {
+        val icon = TestStubDrawable("res123").asIcon()
+        val model =
+            ModesTileModel(
+                isActivated = false,
+                activeModes = emptyList(),
+                icon = icon,
+                iconResId = 123 // Should not be populated, but is ignored even if present
+            )
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.icon()).isEqualTo(icon)
+        assertThat(state.iconRes).isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
+    fun state_withDisabledFlag_includesIconResId() {
+        val icon = TestStubDrawable("res123").asIcon()
+        val model =
+            ModesTileModel(
+                isActivated = false,
+                activeModes = emptyList(),
+                icon = icon,
+                iconResId = 123
+            )
+
+        val state = underTest.map(config, model)
+
+        assertThat(state.icon()).isEqualTo(icon)
+        assertThat(state.iconRes).isEqualTo(123)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index d472d98..22913f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -255,7 +255,7 @@
             runCurrent()
             clearInvocations(qsImpl!!)
 
-            underTest.setState(QSSceneAdapter.State.Expanding(progress))
+            underTest.setState(QSSceneAdapter.State.Expanding { progress })
             with(qsImpl!!) {
                 verify(this).setQsVisible(true)
                 verify(this, never())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
index 63ce67c..41b5988 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
@@ -20,7 +20,12 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.ui.adapter.ExpandingSubject.Companion.assertThatExpanding
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Subject.Factory
+import com.google.common.truth.Truth.assertAbout
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -32,33 +37,59 @@
 
     @Test
     fun expanding_squishiness1() {
-        assertThat(QSSceneAdapter.State.Expanding(0.3f).squishiness()).isEqualTo(1f)
+        assertThat(QSSceneAdapter.State.Expanding { 0.3f }.squishiness()).isEqualTo(1f)
     }
 
     @Test
     fun expandingSpecialValues() {
-        assertThat(QSSceneAdapter.State.QQS).isEqualTo(QSSceneAdapter.State.Expanding(0f))
-        assertThat(QSSceneAdapter.State.QS).isEqualTo(QSSceneAdapter.State.Expanding(1f))
+        assertThatExpanding(QSSceneAdapter.State.QQS)
+            .isEqualTo(QSSceneAdapter.State.Expanding { 0f })
+        assertThatExpanding(QSSceneAdapter.State.QS)
+            .isEqualTo(QSSceneAdapter.State.Expanding { 1f })
     }
 
     @Test
     fun collapsing() {
         val collapsingProgress = 0.3f
-        assertThat(Collapsing(collapsingProgress))
-            .isEqualTo(QSSceneAdapter.State.Expanding(1 - collapsingProgress))
+        assertThatExpanding(Collapsing { collapsingProgress })
+            .isEqualTo(QSSceneAdapter.State.Expanding { 1 - collapsingProgress })
     }
 
     @Test
     fun unsquishingQQS_expansionSameAsQQS() {
         val squishiness = 0.6f
-        assertThat(QSSceneAdapter.State.UnsquishingQQS { squishiness }.expansion)
-            .isEqualTo(QSSceneAdapter.State.QQS.expansion)
+        assertThat(QSSceneAdapter.State.UnsquishingQQS { squishiness }.expansion())
+            .isEqualTo(QSSceneAdapter.State.QQS.expansion())
     }
 
     @Test
     fun unsquishingQS_expansionSameAsQS() {
         val squishiness = 0.6f
-        assertThat(QSSceneAdapter.State.UnsquishingQS { squishiness }.expansion)
-            .isEqualTo(QSSceneAdapter.State.QS.expansion)
+        assertThat(QSSceneAdapter.State.UnsquishingQS { squishiness }.expansion())
+            .isEqualTo(QSSceneAdapter.State.QS.expansion())
+    }
+}
+
+private class ExpandingSubject(
+    metadata: FailureMetadata,
+    private val actual: QSSceneAdapter.State.Expanding?
+) : Subject(metadata, actual) {
+    fun isEqualTo(expected: QSSceneAdapter.State.Expanding) {
+        isNotNull()
+        check("expansion()")
+            .that(actual?.expansion?.invoke())
+            .isEqualTo(expected.expansion.invoke())
+    }
+
+    companion object {
+        fun expanding(): Factory<ExpandingSubject, QSSceneAdapter.State.Expanding> {
+            return Factory { metadata: FailureMetadata, actual: QSSceneAdapter.State.Expanding? ->
+                ExpandingSubject(metadata, actual)
+            }
+        }
+
+        fun assertThatExpanding(actual: QSSceneAdapter.State.Expanding): ExpandingSubject {
+            return assertAbout(expanding()).that(actual)
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
new file mode 100644
index 0000000..424afe1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModelTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import android.platform.test.annotations.DisableFlags
+import android.testing.TestableLooper.RunWithLooper
+import androidx.lifecycle.LifecycleOwner
+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.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper
+@EnableSceneContainer
+@DisableFlags(com.android.systemui.Flags.FLAG_DUAL_SHADE)
+class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
+    private val footerActionsViewModel = mock<FooterActionsViewModel>()
+    private val footerActionsViewModelFactory =
+        mock<FooterActionsViewModel.Factory> {
+            whenever(create(any<LifecycleOwner>())).thenReturn(footerActionsViewModel)
+        }
+    private val footerActionsController = mock<FooterActionsController>()
+
+    private val sceneContainerStartable = kosmos.sceneContainerStartable
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val shadeInteractor by lazy { kosmos.shadeInteractor }
+
+    private lateinit var underTest: QuickSettingsSceneContentViewModel
+
+    @Before
+    fun setUp() {
+        kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
+
+        sceneContainerStartable.start()
+        underTest =
+            QuickSettingsSceneContentViewModel(
+                brightnessMirrorViewModelFactory = kosmos.brightnessMirrorViewModelFactory,
+                shadeHeaderViewModelFactory = kosmos.shadeHeaderViewModelFactory,
+                qsSceneAdapter = qsFlexiglassAdapter,
+                footerActionsViewModelFactory = footerActionsViewModelFactory,
+                footerActionsController = footerActionsController,
+                mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
+                shadeInteractor = shadeInteractor,
+                sceneInteractor = sceneInteractor,
+            )
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun gettingViewModelInitializesControllerOnlyOnce() {
+        underTest.getFooterActionsViewModel(mock())
+        underTest.getFooterActionsViewModel(mock())
+
+        verify(footerActionsController, times(1)).init()
+    }
+
+    @Test
+    fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
+        testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
+            val userMedia = MediaData(active = true)
+
+            assertThat(isMediaVisible).isFalse()
+
+            kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+
+            assertThat(isMediaVisible).isTrue()
+
+            kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
+
+            assertThat(isMediaVisible).isFalse()
+        }
+
+    @Test
+    fun addInactiveMedia_mediaVisibilityIsUpdated() =
+        testScope.runTest {
+            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
+            val userMedia = MediaData(active = false)
+
+            assertThat(isMediaVisible).isFalse()
+
+            kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+
+            assertThat(isMediaVisible).isTrue()
+        }
+
+    @Test
+    fun shadeModeChange_switchToShadeScene() =
+        testScope.runTest {
+            val scene by collectLastValue(sceneInteractor.currentScene)
+
+            // switch to split shade
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            runCurrent()
+
+            assertThat(scene).isEqualTo(Scenes.Shade)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
deleted file mode 100644
index 0363808..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ /dev/null
@@ -1,306 +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.qs.ui.viewmodel
-
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserActionResult
-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.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags
-import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.controls.data.repository.mediaFilterRepository
-import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
-import com.android.systemui.media.controls.shared.model.MediaData
-import com.android.systemui.qs.FooterActionsController
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
-import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.sceneBackInteractor
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.domain.startable.sceneContainerStartable
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
-import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
-import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@RunWithLooper
-@EnableSceneContainer
-class QuickSettingsSceneViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
-    private val footerActionsViewModel = mock<FooterActionsViewModel>()
-    private val footerActionsViewModelFactory =
-        mock<FooterActionsViewModel.Factory> {
-            whenever(create(any())).thenReturn(footerActionsViewModel)
-        }
-    private val footerActionsController = mock<FooterActionsController>()
-
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val sceneBackInteractor = kosmos.sceneBackInteractor
-    private val sceneContainerStartable = kosmos.sceneContainerStartable
-
-    private lateinit var underTest: QuickSettingsSceneViewModel
-
-    @Before
-    fun setUp() {
-        kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
-
-        sceneContainerStartable.start()
-        underTest =
-            QuickSettingsSceneViewModel(
-                brightnessMirrorViewModelFactory = kosmos.brightnessMirrorViewModelFactory,
-                shadeHeaderViewModelFactory = kosmos.shadeHeaderViewModelFactory,
-                qsSceneAdapter = qsFlexiglassAdapter,
-                footerActionsViewModelFactory = footerActionsViewModelFactory,
-                footerActionsController = footerActionsController,
-                sceneBackInteractor = sceneBackInteractor,
-                mediaCarouselInteractor = kosmos.mediaCarouselInteractor,
-            )
-    }
-
-    @Test
-    fun destinations_whenNotCustomizing_unlocked() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            val destinations by collectLastValue(underTest.destinationScenes)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            qsFlexiglassAdapter.setCustomizing(false)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pin
-            )
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
-            assertThat(destinations)
-                .isEqualTo(
-                    mapOf(
-                        Back to UserActionResult(Scenes.Shade),
-                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
-                        Swipe(
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ) to UserActionResult(SceneFamilies.Home)
-                    )
-                )
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            qsFlexiglassAdapter.setCustomizing(false)
-            val destinations by collectLastValue(underTest.destinationScenes)
-
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            val backScene by collectLastValue(sceneBackInteractor.backScene)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
-            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
-            assertThat(backScene).isEqualTo(Scenes.Lockscreen)
-
-            assertThat(destinations)
-                .isEqualTo(
-                    mapOf(
-                        Back to UserActionResult(Scenes.Lockscreen),
-                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Lockscreen),
-                        Swipe(
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ) to UserActionResult(SceneFamilies.Home)
-                    )
-                )
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen_butLockscreenDisabled() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            qsFlexiglassAdapter.setCustomizing(false)
-            val destinations by collectLastValue(underTest.destinationScenes)
-
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            val backScene by collectLastValue(sceneBackInteractor.backScene)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
-
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-
-            assertThat(currentScene).isEqualTo(Scenes.Gone)
-            assertThat(backScene).isNull()
-            assertThat(destinations)
-                .isEqualTo(
-                    mapOf(
-                        Back to UserActionResult(Scenes.Shade),
-                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
-                        Swipe(
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ) to UserActionResult(SceneFamilies.Home)
-                    )
-                )
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            val destinations by collectLastValue(underTest.destinationScenes)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            qsFlexiglassAdapter.setCustomizing(false)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-
-            assertThat(destinations)
-                .isEqualTo(
-                    mapOf(
-                        Back to UserActionResult(Scenes.Shade),
-                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
-                        Swipe(
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ) to UserActionResult(SceneFamilies.Home)
-                    )
-                )
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun destinations_whenCustomizing_noDestinations() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            val destinations by collectLastValue(underTest.destinationScenes)
-            qsFlexiglassAdapter.setCustomizing(true)
-
-            assertThat(destinations).isEmpty()
-        }
-
-    @Test
-    fun destinations_whenNotCustomizing_inSplitShade_unlocked() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, true)
-            val destinations by collectLastValue(underTest.destinationScenes)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            qsFlexiglassAdapter.setCustomizing(false)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pin
-            )
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-
-            assertThat(destinations)
-                .isEqualTo(
-                    mapOf(
-                        Back to UserActionResult(Scenes.Shade),
-                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
-                        Swipe(
-                            fromSource = Edge.Bottom,
-                            direction = SwipeDirection.Up,
-                        ) to UserActionResult(SceneFamilies.Home)
-                    )
-                )
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun destinations_whenCustomizing_inSplitShade_noDestinations() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, true)
-            val destinations by collectLastValue(underTest.destinationScenes)
-            qsFlexiglassAdapter.setCustomizing(true)
-
-            assertThat(destinations).isEmpty()
-        }
-
-    @Test
-    fun gettingViewModelInitializesControllerOnlyOnce() {
-        underTest.getFooterActionsViewModel(mock())
-        underTest.getFooterActionsViewModel(mock())
-
-        verify(footerActionsController, times(1)).init()
-    }
-
-    @Test
-    fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
-        testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
-            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
-            val userMedia = MediaData(active = true)
-
-            assertThat(isMediaVisible).isFalse()
-
-            kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
-
-            assertThat(isMediaVisible).isTrue()
-
-            kosmos.mediaFilterRepository.removeSelectedUserMediaEntry(userMedia.instanceId)
-
-            assertThat(isMediaVisible).isFalse()
-        }
-
-    @Test
-    fun addInactiveMedia_mediaVisibilityIsUpdated() =
-        testScope.runTest {
-            kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
-            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
-            val userMedia = MediaData(active = false)
-
-            assertThat(isMediaVisible).isFalse()
-
-            kosmos.mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
-
-            assertThat(isMediaVisible).isTrue()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
new file mode 100644
index 0000000..762941d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.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.qs.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = kosmos.quickSettingsShadeOverlayActionsViewModel
+
+    @Test
+    fun upTransitionSceneKey_hidesShade() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.HideOverlay)?.overlay)
+                .isEqualTo(Overlays.QuickSettingsShade)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+        }
+
+    @Test
+    fun back_hidesShade() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(this)
+
+            assertThat((actions?.get(Back) as? UserActionResult.HideOverlay)?.overlay)
+                .isEqualTo(Overlays.QuickSettingsShade)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt
new file mode 100644
index 0000000..abd1e2c
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.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.ui.viewmodel
+
+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.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+
+    private val underTest = kosmos.quickSettingsShadeOverlayContentViewModel
+
+    @Test
+    fun onScrimClicked_hidesShade() =
+        testScope.runTest {
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            sceneInteractor.showOverlay(
+                overlay = Overlays.QuickSettingsShade,
+                loggingReason = "test",
+            )
+            assertThat(currentOverlays).contains(Overlays.QuickSettingsShade)
+
+            underTest.onScrimClicked()
+
+            assertThat(currentOverlays).doesNotContain(Overlays.QuickSettingsShade)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
deleted file mode 100644
index 647fdf6..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,203 +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.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-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.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-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)
[email protected]
-@EnableSceneContainer
-class QuickSettingsShadeSceneActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
-
-    private val underTest by lazy { kosmos.quickSettingsShadeSceneActionsViewModel }
-
-    @Before
-    fun setUp() {
-        underTest.activateIn(testScope)
-    }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_lockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceUnlocked_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-            unlockDevice()
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Down)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun downTransitionSceneKey_deviceLocked_bottomAligned_lockscreen() =
-        testScope.runTest {
-            kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-
-            assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Up)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun downTransitionSceneKey_deviceUnlocked_bottomAligned_gone() =
-        testScope.runTest {
-            kosmos.fakeShadeRepository.setDualShadeAlignedToBottom(true)
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            lockDevice()
-            unlockDevice()
-
-            assertThat(actions?.get(Swipe.Down)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(actions?.get(Swipe.Up)).isNull()
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            runCurrent()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-
-            assertThat(actions?.get(Swipe.Up)?.toScene).isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun backTransitionSceneKey_notEditing_Home() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-
-            assertThat(actions?.get(Back)?.toScene).isEqualTo(SceneFamilies.Home)
-        }
-
-    @Test
-    fun backTransition_editing_noDestination() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            kosmos.editModeViewModel.startEditing()
-
-            assertThat(actions!!).isNotEmpty()
-            assertThat(actions?.get(Back)).isNull()
-        }
-
-    private fun TestScope.lockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
-        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-        runCurrent()
-    }
-
-    private fun TestScope.unlockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-            SuccessFingerprintAuthenticationStatus(0, true)
-        )
-        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-        sceneInteractor.changeScene(Scenes.Gone, "reason")
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.kt
new file mode 100644
index 0000000..32772d2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelTest.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.qs.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
+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.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+@EnableSceneContainer
+class QuickSettingsShadeUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
+
+    private val underTest by lazy { kosmos.quickSettingsShadeUserActionsViewModel }
+
+    @Test
+    fun upTransitionSceneKey_deviceLocked_lockscreen() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            lockDevice()
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_deviceLocked_keyguardDisabled_gone() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            lockDevice()
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_deviceUnlocked_gone() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            lockDevice()
+            unlockDevice()
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(actions?.get(Swipe.Down)).isNull()
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            runCurrent()
+            sceneInteractor.changeScene(Scenes.Gone, "reason")
+
+            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun backTransitionSceneKey_notEditing_Home() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+
+            assertThat((actions?.get(Back) as? UserActionResult.ChangeScene)?.toScene)
+                .isEqualTo(SceneFamilies.Home)
+        }
+
+    @Test
+    fun backTransition_editing_noDestination() =
+        testScope.runTest {
+            underTest.activateIn(this)
+            val actions by collectLastValue(underTest.actions)
+            kosmos.editModeViewModel.startEditing()
+
+            assertThat(actions!!).isNotEmpty()
+            assertThat(actions?.get(Back)).isNull()
+        }
+
+    private fun TestScope.lockDevice() {
+        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
+
+        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
+        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
+        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+        runCurrent()
+    }
+
+    private fun TestScope.unlockDevice() {
+        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
+
+        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+            SuccessFingerprintAuthenticationStatus(0, true)
+        )
+        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
+        sceneInteractor.changeScene(Scenes.Gone, "reason")
+        runCurrent()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
new file mode 100644
index 0000000..6986cf8e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModelTest.kt
@@ -0,0 +1,246 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
+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.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
+import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.sceneBackInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.domain.startable.sceneContainerStartable
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
+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)
+@RunWithLooper
+@EnableSceneContainer
+class QuickSettingsUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
+
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val sceneBackInteractor = kosmos.sceneBackInteractor
+    private val sceneContainerStartable = kosmos.sceneContainerStartable
+
+    private lateinit var underTest: QuickSettingsUserActionsViewModel
+
+    @Before
+    fun setUp() {
+        kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
+
+        sceneContainerStartable.start()
+        underTest =
+            QuickSettingsUserActionsViewModel(
+                qsSceneAdapter = qsFlexiglassAdapter,
+                sceneBackInteractor = sceneBackInteractor,
+            )
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun destinations_whenNotCustomizing_unlocked() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            qsFlexiglassAdapter.setCustomizing(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            assertThat(actions)
+                .isEqualTo(
+                    mapOf(
+                        Back to UserActionResult(Scenes.Shade),
+                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+                        Swipe(
+                            fromSource = Edge.Bottom,
+                            direction = SwipeDirection.Up,
+                        ) to UserActionResult(SceneFamilies.Home)
+                    )
+                )
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            qsFlexiglassAdapter.setCustomizing(false)
+            val actions by collectLastValue(underTest.actions)
+
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val backScene by collectLastValue(sceneBackInteractor.backScene)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+            assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
+            assertThat(backScene).isEqualTo(Scenes.Lockscreen)
+
+            assertThat(actions)
+                .isEqualTo(
+                    mapOf(
+                        Back to UserActionResult(Scenes.Lockscreen),
+                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Lockscreen),
+                        Swipe(
+                            fromSource = Edge.Bottom,
+                            direction = SwipeDirection.Up,
+                        ) to UserActionResult(SceneFamilies.Home)
+                    )
+                )
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun destinations_whenNotCustomizing_withPreviousSceneLockscreen_butLockscreenDisabled() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            qsFlexiglassAdapter.setCustomizing(false)
+            val actions by collectLastValue(underTest.actions)
+
+            val currentScene by collectLastValue(sceneInteractor.currentScene)
+            val backScene by collectLastValue(sceneBackInteractor.backScene)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
+
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(backScene).isNull()
+            assertThat(actions)
+                .isEqualTo(
+                    mapOf(
+                        Back to UserActionResult(Scenes.Shade),
+                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+                        Swipe(
+                            fromSource = Edge.Bottom,
+                            direction = SwipeDirection.Up,
+                        ) to UserActionResult(SceneFamilies.Home)
+                    )
+                )
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun destinations_whenNotCustomizing_authMethodSwipe_lockscreenNotDismissed() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            qsFlexiglassAdapter.setCustomizing(false)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+
+            assertThat(actions)
+                .isEqualTo(
+                    mapOf(
+                        Back to UserActionResult(Scenes.Shade),
+                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+                        Swipe(
+                            fromSource = Edge.Bottom,
+                            direction = SwipeDirection.Up,
+                        ) to UserActionResult(SceneFamilies.Home)
+                    )
+                )
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun destinations_whenCustomizing_noDestinations() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            val actions by collectLastValue(underTest.actions)
+            qsFlexiglassAdapter.setCustomizing(true)
+
+            assertThat(actions).isEmpty()
+        }
+
+    @Test
+    fun destinations_whenNotCustomizing_inSplitShade_unlocked() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, true)
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            qsFlexiglassAdapter.setCustomizing(false)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            assertThat(actions)
+                .isEqualTo(
+                    mapOf(
+                        Back to UserActionResult(Scenes.Shade),
+                        Swipe(SwipeDirection.Up) to UserActionResult(Scenes.Shade),
+                        Swipe(
+                            fromSource = Edge.Bottom,
+                            direction = SwipeDirection.Up,
+                        ) to UserActionResult(SceneFamilies.Home)
+                    )
+                )
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun destinations_whenCustomizing_inSplitShade_noDestinations() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, true)
+            val actions by collectLastValue(underTest.actions)
+            qsFlexiglassAdapter.setCustomizing(true)
+
+            assertThat(actions).isEmpty()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 66e45ab..2d42c42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -18,7 +18,6 @@
 
 package com.android.systemui.scene
 
-import android.telecom.TelecomManager
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -26,35 +25,30 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserActionResult
 import com.android.internal.R
-import com.android.internal.util.EmergencyAffordanceManager
 import com.android.internal.util.emergencyAffordanceManager
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
-import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
-import com.android.systemui.classifier.domain.interactor.falsingInteractor
-import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModel
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneActionsViewModel
+import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel
+import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
@@ -62,18 +56,14 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneActionsViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeSceneContentViewModel
-import com.android.systemui.shade.ui.viewmodel.shadeSceneActionsViewModel
 import com.android.systemui.shade.ui.viewmodel.shadeSceneContentViewModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.shade.ui.viewmodel.shadeUserActionsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
-import com.android.telecom.telecomManager
+import com.android.telecom.mockTelecomManager
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -81,14 +71,12 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.TestScope
 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.verify
-import org.mockito.MockitoAnnotations
 
 /**
  * Integration test cases for the Scene Framework.
@@ -114,190 +102,131 @@
 @RunWithLooper
 @EnableSceneContainer
 class SceneFrameworkIntegrationTest : SysuiTestCase() {
-
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
-    private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
-    private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
-    private val communalInteractor by lazy { kosmos.communalInteractor }
-
-    private val transitionState by lazy {
-        MutableStateFlow<ObservableTransitionState>(
-            ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
-        )
-    }
-    private val sceneContainerViewModel by lazy {
-        SceneContainerViewModel(
-                sceneInteractor = sceneInteractor,
-                falsingInteractor = kosmos.falsingInteractor,
-                powerInteractor = kosmos.powerInteractor,
-            )
-            .apply { setTransitionState(transitionState) }
-    }
-
-    private lateinit var mobileConnectionsRepository: FakeMobileConnectionsRepository
-    private lateinit var bouncerActionButtonInteractor: BouncerActionButtonInteractor
-    private lateinit var bouncerViewModel: BouncerViewModel
-
-    private val lockscreenSceneActionsViewModel by lazy {
-        LockscreenSceneActionsViewModel(
-            deviceEntryInteractor = deviceEntryInteractor,
-            communalInteractor = communalInteractor,
-            shadeInteractor = kosmos.shadeInteractor,
-        )
-    }
-
-    private lateinit var shadeSceneContentViewModel: ShadeSceneContentViewModel
-    private lateinit var shadeSceneActionsViewModel: ShadeSceneActionsViewModel
-
-    private val powerInteractor by lazy { kosmos.powerInteractor }
-
     private var bouncerSceneJob: Job? = null
 
-    private val qsFlexiglassAdapter = kosmos.fakeQSSceneAdapter
-
-    private lateinit var emergencyAffordanceManager: EmergencyAffordanceManager
-    private lateinit var telecomManager: TelecomManager
-    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
-
     @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
+    fun setUp() =
+        kosmos.run {
+            overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
+            whenever(mockTelecomManager.isInCall).thenReturn(false)
+            whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
 
-        overrideResource(R.bool.config_enable_emergency_call_while_sim_locked, true)
-        telecomManager = checkNotNull(kosmos.telecomManager)
-        whenever(telecomManager.isInCall).thenReturn(false)
-        emergencyAffordanceManager = kosmos.emergencyAffordanceManager
-        whenever(emergencyAffordanceManager.needsEmergencyAffordance()).thenReturn(true)
+            fakeFeatureFlagsClassic.apply { set(Flags.NEW_NETWORK_SLICE_UI, false) }
 
-        kosmos.fakeFeatureFlagsClassic.apply {
-            set(Flags.NEW_NETWORK_SLICE_UI, false)
-            set(Flags.REFACTOR_GETCURRENTUSER, true)
+            fakeMobileConnectionsRepository.isAnySimSecure.value = false
+
+            fakeTelephonyRepository.apply {
+                setHasTelephonyRadio(true)
+                setCallState(TelephonyManager.CALL_STATE_IDLE)
+                setIsInCall(false)
+            }
+
+            sceneContainerStartable.start()
+
+            lockscreenUserActionsViewModel.activateIn(testScope)
+            shadeSceneContentViewModel.activateIn(testScope)
+            shadeUserActionsViewModel.activateIn(testScope)
+            bouncerSceneContentViewModel.activateIn(testScope)
+            sceneContainerViewModel.activateIn(testScope)
+
+            assertWithMessage("Initial scene key mismatch!")
+                .that(sceneContainerViewModel.currentScene.value)
+                .isEqualTo(sceneContainerConfig.initialSceneKey)
+            assertWithMessage("Initial scene container visibility mismatch!")
+                .that(sceneContainerViewModel.isVisible)
+                .isTrue()
         }
 
-        mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
-        mobileConnectionsRepository.isAnySimSecure.value = false
-
-        kosmos.fakeTelephonyRepository.apply {
-            setHasTelephonyRadio(true)
-            setCallState(TelephonyManager.CALL_STATE_IDLE)
-            setIsInCall(false)
-        }
-
-        bouncerActionButtonInteractor = kosmos.bouncerActionButtonInteractor
-        bouncerViewModel = kosmos.bouncerViewModel
-
-        shadeSceneContentViewModel = kosmos.shadeSceneContentViewModel
-        shadeSceneActionsViewModel = kosmos.shadeSceneActionsViewModel
-
-        val startable = kosmos.sceneContainerStartable
-        startable.start()
-
-        lockscreenSceneActionsViewModel.activateIn(testScope)
-        shadeSceneContentViewModel.activateIn(testScope)
-        shadeSceneActionsViewModel.activateIn(testScope)
-
-        assertWithMessage("Initial scene key mismatch!")
-            .that(sceneContainerViewModel.currentScene.value)
-            .isEqualTo(sceneContainerConfig.initialSceneKey)
-        assertWithMessage("Initial scene container visibility mismatch!")
-            .that(sceneContainerViewModel.isVisible.value)
-            .isTrue()
-    }
-
     @Test
-    fun startsInLockscreenScene() = testScope.runTest { assertCurrentScene(Scenes.Lockscreen) }
+    fun startsInLockscreenScene() =
+        testScope.runTest { kosmos.assertCurrentScene(Scenes.Lockscreen) }
 
     @Test
     fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
         testScope.runTest {
-            emulateUserDrivenTransition(Scenes.Bouncer)
+            kosmos.emulateUserDrivenTransition(Scenes.Bouncer)
 
-            fakeSceneDataSource.pause()
-            enterPin()
-            emulatePendingTransitionProgress(
-                expectedVisible = false,
-            )
-            assertCurrentScene(Scenes.Gone)
+            kosmos.fakeSceneDataSource.pause()
+            kosmos.enterPin()
+            kosmos.emulatePendingTransitionProgress(expectedVisible = false)
+            kosmos.assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreen_enterCorrectPin_unlocksDevice() =
         testScope.runTest {
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            emulateUserDrivenTransition(
+            kosmos.emulateUserDrivenTransition(
                 to = upDestinationSceneKey,
             )
 
-            fakeSceneDataSource.pause()
-            enterPin()
-            emulatePendingTransitionProgress(
-                expectedVisible = false,
-            )
-            assertCurrentScene(Scenes.Gone)
+            kosmos.fakeSceneDataSource.pause()
+            kosmos.enterPin()
+            kosmos.emulatePendingTransitionProgress(expectedVisible = false)
+            kosmos.assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreen_withAuthMethodSwipe_dismissesLockscreen() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
 
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
-            emulateUserDrivenTransition(
-                to = upDestinationSceneKey,
-            )
+            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
         }
 
     @Test
     fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
         testScope.runTest {
-            val actions by collectLastValue(shadeSceneActionsViewModel.actions)
+            val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
             val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
             // Emulate a user swipe to the shade scene.
-            emulateUserDrivenTransition(to = Scenes.Shade)
-            assertCurrentScene(Scenes.Shade)
+            kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
+            kosmos.assertCurrentScene(Scenes.Shade)
 
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
             assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-            emulateUserDrivenTransition(
-                to = homeScene,
-            )
+            kosmos.emulateUserDrivenTransition(to = homeScene)
         }
 
     @Test
     fun swipeUpOnShadeScene_withAuthMethodSwipe_lockscreenDismissed_goesToGone() =
         testScope.runTest {
-            val actions by collectLastValue(shadeSceneActionsViewModel.actions)
-            val canSwipeToEnter by collectLastValue(deviceEntryInteractor.canSwipeToEnter)
+            val actions by collectLastValue(kosmos.shadeUserActionsViewModel.actions)
+            val canSwipeToEnter by collectLastValue(kosmos.deviceEntryInteractor.canSwipeToEnter)
             val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
 
-            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
 
             assertThat(canSwipeToEnter).isTrue()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
             // Emulate a user swipe to dismiss the lockscreen.
-            emulateUserDrivenTransition(to = Scenes.Gone)
-            assertCurrentScene(Scenes.Gone)
+            kosmos.emulateUserDrivenTransition(to = Scenes.Gone)
+            kosmos.assertCurrentScene(Scenes.Gone)
 
             // Emulate a user swipe to the shade scene.
-            emulateUserDrivenTransition(to = Scenes.Shade)
-            assertCurrentScene(Scenes.Shade)
+            kosmos.emulateUserDrivenTransition(to = Scenes.Shade)
+            kosmos.assertCurrentScene(Scenes.Shade)
 
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(SceneFamilies.Home)
             assertThat(homeScene).isEqualTo(Scenes.Gone)
-            emulateUserDrivenTransition(
+            kosmos.emulateUserDrivenTransition(
                 to = homeScene,
             )
         }
@@ -305,99 +234,113 @@
     @Test
     fun withAuthMethodNone_deviceWakeUp_skipsLockscreen() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
-            putDeviceToSleep(instantlyLockDevice = false)
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = false)
+            kosmos.putDeviceToSleep(instantlyLockDevice = false)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
-            wakeUpDevice()
-            assertCurrentScene(Scenes.Gone)
+            kosmos.wakeUpDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun withAuthMethodSwipe_deviceWakeUp_doesNotSkipLockscreen() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
-            putDeviceToSleep(instantlyLockDevice = false)
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None, enableLockscreen = true)
+            kosmos.putDeviceToSleep(instantlyLockDevice = false)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
-            wakeUpDevice()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.wakeUpDevice()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun lockDeviceLocksDevice() =
+        testScope.runTest {
+            kosmos.unlockDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
+
+            kosmos.lockDevice()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun deviceGoesToSleep_switchesToLockscreen() =
         testScope.runTest {
-            unlockDevice()
-            assertCurrentScene(Scenes.Gone)
+            kosmos.unlockDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
 
-            putDeviceToSleep()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.putDeviceToSleep()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun deviceGoesToSleep_wakeUp_unlock() =
         testScope.runTest {
-            unlockDevice()
-            assertCurrentScene(Scenes.Gone)
-            putDeviceToSleep()
-            assertCurrentScene(Scenes.Lockscreen)
-            wakeUpDevice()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.unlockDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
+            kosmos.putDeviceToSleep()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
+            kosmos.wakeUpDevice()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
-            unlockDevice()
-            assertCurrentScene(Scenes.Gone)
+            kosmos.unlockDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun swipeUpOnLockscreenWhileUnlocked_dismissesLockscreen() =
         testScope.runTest {
-            unlockDevice()
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            kosmos.unlockDevice()
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Gone)
         }
 
     @Test
     fun deviceGoesToSleep_withLockTimeout_staysOnLockscreen() =
         testScope.runTest {
-            unlockDevice()
-            assertCurrentScene(Scenes.Gone)
-            putDeviceToSleep(instantlyLockDevice = false)
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.unlockDevice()
+            kosmos.assertCurrentScene(Scenes.Gone)
+            kosmos.putDeviceToSleep(instantlyLockDevice = false)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
 
             // Pretend like the timeout elapsed and now lock the device.
-            lockDevice()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.lockDevice()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun dismissingIme_whileOnPasswordBouncer_navigatesToLockscreen() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.Password)
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            emulateUserDrivenTransition(
+            kosmos.emulateUserDrivenTransition(
                 to = upDestinationSceneKey,
             )
 
-            fakeSceneDataSource.pause()
-            dismissIme()
+            kosmos.fakeSceneDataSource.pause()
+            kosmos.dismissIme()
 
-            emulatePendingTransitionProgress()
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.emulatePendingTransitionProgress()
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
         }
 
     @Test
     fun bouncerActionButtonClick_opensEmergencyServicesDialer() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.Password)
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            emulateUserDrivenTransition(to = upDestinationSceneKey)
+            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
 
-            val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+            val bouncerActionButton by
+                collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
             assertWithMessage("Bouncer action button not visible")
                 .that(bouncerActionButton)
                 .isNotNull()
@@ -410,53 +353,55 @@
     @Test
     fun bouncerActionButtonClick_duringCall_returnsToCall() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.Password)
-            startPhoneCall()
-            val actions by collectLastValue(lockscreenSceneActionsViewModel.actions)
-            val upDestinationSceneKey = actions?.get(Swipe.Up)?.toScene
+            kosmos.setAuthMethod(AuthenticationMethodModel.Password)
+            kosmos.startPhoneCall()
+            val actions by collectLastValue(kosmos.lockscreenUserActionsViewModel.actions)
+            val upDestinationSceneKey =
+                (actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene
             assertThat(upDestinationSceneKey).isEqualTo(Scenes.Bouncer)
-            emulateUserDrivenTransition(to = upDestinationSceneKey)
+            kosmos.emulateUserDrivenTransition(to = upDestinationSceneKey)
 
-            val bouncerActionButton by collectLastValue(bouncerViewModel.actionButton)
+            val bouncerActionButton by
+                collectLastValue(kosmos.bouncerSceneContentViewModel.actionButton)
             assertWithMessage("Bouncer action button not visible during call")
                 .that(bouncerActionButton)
                 .isNotNull()
             bouncerActionButton?.onClick?.invoke()
             runCurrent()
 
-            verify(telecomManager).showInCallScreen(any())
+            verify(kosmos.mockTelecomManager).showInCallScreen(any())
         }
 
     @Test
     fun showBouncer_whenLockedSimIntroduced() =
         testScope.runTest {
-            setAuthMethod(AuthenticationMethodModel.None)
-            introduceLockedSim()
-            assertCurrentScene(Scenes.Bouncer)
+            kosmos.setAuthMethod(AuthenticationMethodModel.None)
+            kosmos.introduceLockedSim()
+            kosmos.assertCurrentScene(Scenes.Bouncer)
         }
 
     @Test
     fun goesToGone_whenSimUnlocked_whileDeviceUnlocked() =
         testScope.runTest {
-            fakeSceneDataSource.pause()
-            introduceLockedSim()
-            emulatePendingTransitionProgress(expectedVisible = true)
-            enterSimPin(
+            kosmos.fakeSceneDataSource.pause()
+            kosmos.introduceLockedSim()
+            kosmos.emulatePendingTransitionProgress(expectedVisible = true)
+            kosmos.enterSimPin(
                 authMethodAfterSimUnlock = AuthenticationMethodModel.None,
                 enableLockscreen = false
             )
 
-            assertCurrentScene(Scenes.Gone)
+            kosmos.assertCurrentScene(Scenes.Gone)
         }
 
     @Test
     fun showLockscreen_whenSimUnlocked_whileDeviceLocked() =
         testScope.runTest {
-            fakeSceneDataSource.pause()
-            introduceLockedSim()
-            emulatePendingTransitionProgress(expectedVisible = true)
-            enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
-            assertCurrentScene(Scenes.Lockscreen)
+            kosmos.fakeSceneDataSource.pause()
+            kosmos.introduceLockedSim()
+            kosmos.emulatePendingTransitionProgress(expectedVisible = true)
+            kosmos.enterSimPin(authMethodAfterSimUnlock = AuthenticationMethodModel.Pin)
+            kosmos.assertCurrentScene(Scenes.Lockscreen)
         }
 
     /**
@@ -464,8 +409,8 @@
      *
      * Note that this doesn't assert what the current scene is in the UI.
      */
-    private fun TestScope.assertCurrentScene(expected: SceneKey) {
-        runCurrent()
+    private fun Kosmos.assertCurrentScene(expected: SceneKey) {
+        testScope.runCurrent()
         assertWithMessage("Current scene mismatch!")
             .that(sceneContainerViewModel.currentScene.value)
             .isEqualTo(expected)
@@ -477,15 +422,17 @@
      * This can be different than the value in [SceneContainerViewModel.currentScene], by design, as
      * the UI must gradually transition between scenes.
      */
-    private fun getCurrentSceneInUi(): SceneKey {
+    private fun Kosmos.getCurrentSceneInUi(): SceneKey {
         return when (val state = transitionState.value) {
             is ObservableTransitionState.Idle -> state.currentScene
-            is ObservableTransitionState.Transition -> state.fromScene
+            is ObservableTransitionState.Transition.ChangeScene -> state.fromScene
+            is ObservableTransitionState.Transition.ShowOrHideOverlay -> state.currentScene
+            is ObservableTransitionState.Transition.ReplaceOverlay -> state.currentScene
         }
     }
 
     /** Updates the current authentication method and related states in the data layer. */
-    private fun TestScope.setAuthMethod(
+    private fun Kosmos.setAuthMethod(
         authMethod: AuthenticationMethodModel,
         enableLockscreen: Boolean = true
     ) {
@@ -497,20 +444,20 @@
         // Set the lockscreen enabled bit _before_ set the auth method as the code picks up on the
         // lockscreen enabled bit _after_ the auth method is changed and the lockscreen enabled bit
         // is not an observable that can trigger a new evaluation.
-        kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
-        runCurrent()
+        fakeDeviceEntryRepository.setLockscreenEnabled(enableLockscreen)
+        fakeAuthenticationRepository.setAuthenticationMethod(authMethod)
+        testScope.runCurrent()
     }
 
     /** Emulates a phone call in progress. */
-    private fun TestScope.startPhoneCall() {
-        whenever(telecomManager.isInCall).thenReturn(true)
-        kosmos.fakeTelephonyRepository.apply {
+    private fun Kosmos.startPhoneCall() {
+        whenever(mockTelecomManager.isInCall).thenReturn(true)
+        fakeTelephonyRepository.apply {
             setHasTelephonyRadio(true)
             setIsInCall(true)
             setCallState(TelephonyManager.CALL_STATE_OFFHOOK)
         }
-        runCurrent()
+        testScope.runCurrent()
     }
 
     /**
@@ -520,15 +467,12 @@
      *
      * In order to use this, the [fakeSceneDataSource] must be paused before this method is called.
      */
-    private fun TestScope.emulatePendingTransitionProgress(
-        expectedVisible: Boolean = true,
-    ) {
-        val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
+    private fun Kosmos.emulatePendingTransitionProgress(expectedVisible: Boolean = true) {
         assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
-            .that(fakeSceneDataSource.isPaused)
+            .that(kosmos.fakeSceneDataSource.isPaused)
             .isTrue()
 
-        val to = fakeSceneDataSource.pendingScene ?: return
+        val to = kosmos.fakeSceneDataSource.pendingScene ?: return
         val from = getCurrentSceneInUi()
 
         if (to == from) {
@@ -546,29 +490,29 @@
                 isInitiatedByUserInput = false,
                 isUserInputOngoing = flowOf(false),
             )
-        runCurrent()
+        testScope.runCurrent()
 
         // Report progress of transition.
         while (progressFlow.value < 1f) {
             progressFlow.value += 0.2f
-            runCurrent()
+            testScope.runCurrent()
         }
 
         // End the transition and report the change.
         transitionState.value = ObservableTransitionState.Idle(to)
 
-        fakeSceneDataSource.unpause(force = true)
-        runCurrent()
+        kosmos.fakeSceneDataSource.unpause(force = true)
+        testScope.runCurrent()
 
         assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
-            .that(isVisible)
+            .that(sceneContainerViewModel.isVisible)
             .isEqualTo(expectedVisible)
         assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
 
         bouncerSceneJob =
             if (to == Scenes.Bouncer) {
                 testScope.backgroundScope.launch {
-                    bouncerViewModel.authMethodViewModel.collect {
+                    bouncerSceneContentViewModel.authMethodViewModel.collect {
                         // Do nothing. Need this to turn this otherwise cold flow, hot.
                     }
                 }
@@ -576,7 +520,7 @@
                 bouncerSceneJob?.cancel()
                 null
             }
-        runCurrent()
+        testScope.runCurrent()
     }
 
     /**
@@ -588,12 +532,10 @@
      *
      * @param to The scene to transition to.
      */
-    private fun TestScope.emulateUserDrivenTransition(
-        to: SceneKey?,
-    ) {
+    private fun Kosmos.emulateUserDrivenTransition(to: SceneKey?) {
         checkNotNull(to)
 
-        fakeSceneDataSource.pause()
+        kosmos.fakeSceneDataSource.pause()
         sceneInteractor.changeScene(to, "reason")
 
         emulatePendingTransitionProgress(
@@ -608,17 +550,17 @@
      *
      * Not to be confused with [putDeviceToSleep], which may also instantly lock the device.
      */
-    private suspend fun TestScope.lockDevice() {
+    private suspend fun Kosmos.lockDevice() {
         val authMethod = authenticationInteractor.getAuthenticationMethod()
         assertWithMessage("The authentication method of $authMethod is not secure, cannot lock!")
             .that(authMethod.isSecure)
             .isTrue()
-
-        runCurrent()
+        sceneInteractor.changeScene(Scenes.Lockscreen, "")
+        testScope.runCurrent()
     }
 
     /** Unlocks the device by entering the correct PIN. Ends up in the Gone scene. */
-    private fun TestScope.unlockDevice() {
+    private fun Kosmos.unlockDevice() {
         assertWithMessage("Cannot unlock a device that's already unlocked!")
             .that(deviceEntryInteractor.isUnlocked.value)
             .isFalse()
@@ -640,11 +582,12 @@
      *
      * Does not assert that the device is locked or unlocked.
      */
-    private fun TestScope.enterPin() {
+    private fun Kosmos.enterPin() {
         assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
             .that(getCurrentSceneInUi())
             .isEqualTo(Scenes.Bouncer)
-        val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
+        val authMethodViewModel by
+            testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
         assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
             .that(authMethodViewModel)
             .isInstanceOf(PinBouncerViewModel::class.java)
@@ -654,7 +597,7 @@
             pinBouncerViewModel.onPinButtonClicked(digit)
         }
         pinBouncerViewModel.onAuthenticateButtonClicked()
-        runCurrent()
+        testScope.runCurrent()
     }
 
     /**
@@ -665,14 +608,15 @@
      *
      * Does not assert that the device is locked or unlocked.
      */
-    private fun TestScope.enterSimPin(
+    private fun Kosmos.enterSimPin(
         authMethodAfterSimUnlock: AuthenticationMethodModel = AuthenticationMethodModel.None,
         enableLockscreen: Boolean = true,
     ) {
         assertWithMessage("Cannot enter PIN when not on the Bouncer scene!")
             .that(getCurrentSceneInUi())
             .isEqualTo(Scenes.Bouncer)
-        val authMethodViewModel by collectLastValue(bouncerViewModel.authMethodViewModel)
+        val authMethodViewModel by
+            testScope.collectLastValue(bouncerSceneContentViewModel.authMethodViewModel)
         assertWithMessage("Cannot enter PIN when not using a PIN authentication method!")
             .that(authMethodViewModel)
             .isInstanceOf(PinBouncerViewModel::class.java)
@@ -682,26 +626,26 @@
             pinBouncerViewModel.onPinButtonClicked(digit)
         }
         pinBouncerViewModel.onAuthenticateButtonClicked()
-        kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = false
-        runCurrent()
+        fakeMobileConnectionsRepository.isAnySimSecure.value = false
+        testScope.runCurrent()
 
         setAuthMethod(authMethodAfterSimUnlock, enableLockscreen)
-        runCurrent()
+        testScope.runCurrent()
     }
 
     /** Changes device wakefulness state from asleep to awake, going through intermediary states. */
-    private fun TestScope.wakeUpDevice() {
+    private fun Kosmos.wakeUpDevice() {
         val wakefulnessModel = powerInteractor.detailedWakefulness.value
         assertWithMessage("Cannot wake up device as it's already awake!")
             .that(wakefulnessModel.isAwake())
             .isFalse()
 
         powerInteractor.setAwakeForTest()
-        runCurrent()
+        testScope.runCurrent()
     }
 
     /** Changes device wakefulness state from awake to asleep, going through intermediary states. */
-    private suspend fun TestScope.putDeviceToSleep(
+    private suspend fun Kosmos.putDeviceToSleep(
         instantlyLockDevice: Boolean = true,
     ) {
         val wakefulnessModel = powerInteractor.detailedWakefulness.value
@@ -710,7 +654,7 @@
             .isTrue()
 
         powerInteractor.setAsleepForTest()
-        runCurrent()
+        testScope.runCurrent()
 
         if (instantlyLockDevice) {
             lockDevice()
@@ -718,16 +662,16 @@
     }
 
     /** Emulates the dismissal of the IME (soft keyboard). */
-    private fun TestScope.dismissIme() {
-        (bouncerViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
+    private fun Kosmos.dismissIme() {
+        (bouncerSceneContentViewModel.authMethodViewModel.value as? PasswordBouncerViewModel)?.let {
             it.onImeDismissed()
-            runCurrent()
+            testScope.runCurrent()
         }
     }
 
-    private fun TestScope.introduceLockedSim() {
+    private fun Kosmos.introduceLockedSim() {
         setAuthMethod(AuthenticationMethodModel.Sim)
-        kosmos.fakeMobileConnectionsRepository.isAnySimSecure.value = true
-        runCurrent()
+        fakeMobileConnectionsRepository.isAnySimSecure.value = true
+        testScope.runCurrent()
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index df30c4b..227b3a6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.overlayKeys
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.sceneKeys
 import com.android.systemui.scene.shared.model.Scenes
@@ -46,19 +47,9 @@
     private val testScope = kosmos.testScope
 
     @Test
-    fun allSceneKeys() {
+    fun allContentKeys() {
         val underTest = kosmos.sceneContainerRepository
-        assertThat(underTest.allSceneKeys())
-            .isEqualTo(
-                listOf(
-                    Scenes.QuickSettings,
-                    Scenes.Shade,
-                    Scenes.Lockscreen,
-                    Scenes.Bouncer,
-                    Scenes.Gone,
-                    Scenes.Communal,
-                )
-            )
+        assertThat(underTest.allContentKeys).isEqualTo(kosmos.sceneKeys + kosmos.overlayKeys)
     }
 
     @Test
@@ -75,6 +66,18 @@
             assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
         }
 
+    // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined
+    //  them.
+    @Test
+    fun currentOverlays() =
+        testScope.runTest {
+            val underTest = kosmos.sceneContainerRepository
+            val currentOverlays by collectLastValue(underTest.currentOverlays)
+            assertThat(currentOverlays).isEmpty()
+
+            // TODO(b/356596436): When we have a first overlay, add it here and assert contains.
+        }
+
     @Test(expected = IllegalStateException::class)
     fun changeScene_noSuchSceneInContainer_throws() {
         kosmos.sceneKeys = listOf(Scenes.QuickSettings, Scenes.Lockscreen)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index e3a69a9..4a7d8b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.scene.data.repository.sceneContainerRepository
 import com.android.systemui.scene.data.repository.setSceneTransition
 import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.overlayKeys
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.sceneKeys
 import com.android.systemui.scene.shared.model.SceneFamilies
@@ -72,9 +73,11 @@
         kosmos.keyguardEnabledInteractor
     }
 
+    // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined
+    //  them.
     @Test
-    fun allSceneKeys() {
-        assertThat(underTest.allSceneKeys()).isEqualTo(kosmos.sceneKeys)
+    fun allContentKeys() {
+        assertThat(underTest.allContentKeys).isEqualTo(kosmos.sceneKeys + kosmos.overlayKeys)
     }
 
     @Test
@@ -401,10 +404,10 @@
             underTest.setVisible(false, "reason")
             val isVisible by collectLastValue(underTest.isVisible)
             assertThat(isVisible).isFalse()
-            underTest.onRemoteUserInteractionStarted("reason")
+            underTest.onRemoteUserInputStarted("reason")
             assertThat(isVisible).isTrue()
 
-            underTest.onUserInteractionFinished()
+            underTest.onUserInputFinished()
 
             assertThat(isVisible).isFalse()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index f26c39d0b..ec79cc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -19,6 +19,7 @@
 package com.android.systemui.scene.domain.startable
 
 import android.app.StatusBarManager
+import android.hardware.face.FaceManager
 import android.os.PowerManager
 import android.view.Display
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -28,10 +29,12 @@
 import com.android.internal.logging.uiEventLoggerFake
 import com.android.internal.policy.IKeyguardDismissCallback
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
 import com.android.systemui.classifier.FalsingCollector
@@ -40,8 +43,14 @@
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.haptics.vibratorHelper
+import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
@@ -52,13 +61,17 @@
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.scenetransition.lockscreenSceneTransitionInteractor
+import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
 import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.power.data.repository.powerRepository
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
@@ -70,6 +83,7 @@
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.shared.system.QuickStepContract
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
@@ -86,6 +100,7 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -93,6 +108,8 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyBoolean
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
@@ -106,11 +123,14 @@
 
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
+    private val deviceEntryHapticsInteractor by lazy { kosmos.deviceEntryHapticsInteractor }
     private val sceneInteractor by lazy { kosmos.sceneInteractor }
     private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
     private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
+    private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
     private val sysUiState = kosmos.sysUiState
     private val falsingCollector = mock<FalsingCollector>().also { kosmos.falsingCollector = it }
+    private val vibratorHelper = mock<VibratorHelper>().also { kosmos.vibratorHelper = it }
     private val fakeSceneDataSource = kosmos.fakeSceneDataSource
     private val windowController = kosmos.notificationShadeWindowController
     private val centralSurfaces = kosmos.centralSurfaces
@@ -228,6 +248,32 @@
         }
 
     @Test
+    fun hydrateVisibility_basedOnAlternateBouncer() =
+        testScope.runTest {
+            val isVisible by collectLastValue(sceneInteractor.isVisible)
+            prepareState(
+                isDeviceUnlocked = false,
+                initialSceneKey = Scenes.Lockscreen,
+            )
+
+            underTest.start()
+            assertThat(isVisible).isTrue()
+
+            // WHEN the device is occluded,
+            kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+                true,
+                mock()
+            )
+            // THEN scenes are not visible
+            assertThat(isVisible).isFalse()
+
+            // WHEN the alternate bouncer is visible
+            kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true)
+            // THEN scenes visible
+            assertThat(isVisible).isTrue()
+        }
+
+    @Test
     fun startsInLockscreenScene() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -275,6 +321,67 @@
         }
 
     @Test
+    fun switchFromLockscreenToGoneAndHideAltBouncerWhenDeviceUnlocked() =
+        testScope.runTest {
+            val alternateBouncerVisible by
+                collectLastValue(bouncerRepository.alternateBouncerVisible)
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+
+            bouncerRepository.setAlternateVisible(true)
+            assertThat(alternateBouncerVisible).isTrue()
+
+            prepareState(
+                authenticationMethod = AuthenticationMethodModel.Pin,
+                isDeviceUnlocked = false,
+                initialSceneKey = Scenes.Lockscreen,
+            )
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            underTest.start()
+
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+            assertThat(alternateBouncerVisible).isFalse()
+        }
+
+    @Test
+    fun stayOnCurrentSceneAndHideAltBouncerWhenDeviceUnlocked_whenLeaveOpenShade() =
+        testScope.runTest {
+            val alternateBouncerVisible by
+                collectLastValue(bouncerRepository.alternateBouncerVisible)
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+
+            kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open
+            bouncerRepository.setAlternateVisible(true)
+            assertThat(alternateBouncerVisible).isTrue()
+
+            val transitionState =
+                prepareState(
+                    authenticationMethod = AuthenticationMethodModel.Pin,
+                    isDeviceUnlocked = false,
+                    initialSceneKey = Scenes.Lockscreen,
+                )
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            underTest.start()
+            runCurrent()
+
+            sceneInteractor.changeScene(Scenes.QuickSettings, "switching to qs for test")
+            transitionState.value = ObservableTransitionState.Idle(Scenes.QuickSettings)
+            runCurrent()
+            assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+
+            assertThat(currentSceneKey).isEqualTo(Scenes.QuickSettings)
+            assertThat(alternateBouncerVisible).isFalse()
+        }
+
+    @Test
     fun switchFromBouncerToQuickSettingsWhenDeviceUnlocked_whenLeaveOpenShade() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -419,6 +526,33 @@
         }
 
     @Test
+    fun hideAlternateBouncerAndNotifyDismissCancelledWhenDeviceSleeps() =
+        testScope.runTest {
+            val alternateBouncerVisible by
+                collectLastValue(bouncerRepository.alternateBouncerVisible)
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            prepareState(
+                isDeviceUnlocked = false,
+                initialSceneKey = Scenes.Shade,
+            )
+            assertThat(currentSceneKey).isEqualTo(Scenes.Shade)
+            bouncerRepository.setAlternateVisible(true)
+            underTest.start()
+
+            // run all pending dismiss succeeded/cancelled calls from setup:
+            kosmos.fakeExecutor.runAllReady()
+
+            val dismissCallback: IKeyguardDismissCallback = mock()
+            kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+            powerInteractor.setAsleepForTest()
+            runCurrent()
+            kosmos.fakeExecutor.runAllReady()
+
+            assertThat(alternateBouncerVisible).isFalse()
+            verify(dismissCallback).onDismissCancelled()
+        }
+
+    @Test
     fun switchToLockscreenWhenDeviceSleepsLocked() =
         testScope.runTest {
             val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -437,8 +571,7 @@
     fun switchToAOD_whenAvailable_whenDeviceSleepsLocked() =
         testScope.runTest {
             kosmos.lockscreenSceneTransitionInteractor.start()
-            val asleepState by
-                collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState)
+            val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
             val currentTransitionInfo by
                 collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
             val transitionState =
@@ -470,8 +603,7 @@
     fun switchToDozing_whenAodUnavailable_whenDeviceSleepsLocked() =
         testScope.runTest {
             kosmos.lockscreenSceneTransitionInteractor.start()
-            val asleepState by
-                collectLastValue(kosmos.keyguardTransitionInteractor.asleepKeyguardState)
+            val asleepState by collectLastValue(kosmos.keyguardInteractor.asleepKeyguardState)
             val currentTransitionInfo by
                 collectLastValue(kosmos.keyguardTransitionRepository.currentTransitionInfoInternal)
             val transitionState =
@@ -522,6 +654,194 @@
         }
 
     @Test
+    fun playSuccessHaptics_onSuccessfulLockscreenAuth_udfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playSuccessHaptic by
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+            setupBiometricAuth(hasUdfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            unlockWithFingerprintAuth()
+
+            assertThat(playSuccessHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper)
+                .vibrateAuthSuccess(
+                    "SceneContainerStartable, $currentSceneKey device-entry::success"
+                )
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+            updateFingerprintAuthStatus(isSuccess = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun playSuccessHaptics_onSuccessfulLockscreenAuth_sfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playSuccessHaptic by
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+            setupBiometricAuth(hasSfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            allowHapticsOnSfps()
+            unlockWithFingerprintAuth()
+
+            assertThat(playSuccessHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper)
+                .vibrateAuthSuccess(
+                    "SceneContainerStartable, $currentSceneKey device-entry::success"
+                )
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+            updateFingerprintAuthStatus(isSuccess = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun playErrorHaptics_onFailedLockscreenAuth_udfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+            setupBiometricAuth(hasUdfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            updateFingerprintAuthStatus(isSuccess = false)
+
+            assertThat(playErrorHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper)
+                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+        }
+
+    @Test
+    fun playErrorHaptics_onFailedLockscreenAuth_sfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+            setupBiometricAuth(hasSfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            updateFingerprintAuthStatus(isSuccess = false)
+
+            assertThat(playErrorHaptic).isNotNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper)
+                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+        }
+
+    @Test
+    fun skipsSuccessHaptics_whenPowerButtonDown_sfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playSuccessHaptic by
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+            setupBiometricAuth(hasSfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            allowHapticsOnSfps(isPowerButtonDown = true)
+            unlockWithFingerprintAuth()
+
+            assertThat(playSuccessHaptic).isNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper, never())
+                .vibrateAuthSuccess(
+                    "SceneContainerStartable, $currentSceneKey device-entry::success"
+                )
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+            updateFingerprintAuthStatus(isSuccess = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun skipsSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playSuccessHaptic by
+                collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+            setupBiometricAuth(hasSfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            allowHapticsOnSfps(lastPowerPress = 50)
+            unlockWithFingerprintAuth()
+
+            assertThat(playSuccessHaptic).isNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper, never())
+                .vibrateAuthSuccess(
+                    "SceneContainerStartable, $currentSceneKey device-entry::success"
+                )
+            verify(vibratorHelper, never()).vibrateAuthError(anyString())
+
+            updateFingerprintAuthStatus(isSuccess = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun skipsErrorHaptics_whenPowerButtonDown_sfps() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+            setupBiometricAuth(hasSfps = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+            updateFingerprintAuthStatus(isSuccess = false)
+
+            assertThat(playErrorHaptic).isNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper, never())
+                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+        }
+
+    @Test
+    fun skipsFaceErrorHaptics_nonSfps_coEx() =
+        testScope.runTest {
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+            setupBiometricAuth(hasUdfps = true, hasFace = true)
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+            underTest.start()
+            updateFaceAuthStatus(isSuccess = false)
+
+            assertThat(playErrorHaptic).isNull()
+            assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+            verify(vibratorHelper, never())
+                .vibrateAuthError("SceneContainerStartable, $currentSceneKey device-entry::error")
+            verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
+        }
+
+    @Test
     fun hydrateSystemUiState() =
         testScope.runTest {
             val transitionStateFlow = prepareState()
@@ -1080,41 +1400,6 @@
         }
 
     @Test
-    fun hydrateWindowController_setBouncerShowing() =
-        testScope.runTest {
-            underTest.start()
-            val notificationShadeWindowController = kosmos.notificationShadeWindowController
-            val transitionStateFlow = prepareState(initialSceneKey = Scenes.Lockscreen)
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
-            verify(notificationShadeWindowController, never()).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)
-
-            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(false)
-
-            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
-            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-            assertThat(currentScene).isEqualTo(Scenes.Gone)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
-            emulateSceneTransition(transitionStateFlow, Scenes.Lockscreen)
-            verify(notificationShadeWindowController, times(1)).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-
-            emulateSceneTransition(transitionStateFlow, Scenes.Bouncer)
-            verify(notificationShadeWindowController, times(2)).setBouncerShowing(true)
-            verify(notificationShadeWindowController, times(2)).setBouncerShowing(false)
-        }
-
-    @Test
     fun hydrateWindowController_setKeyguardOccluded() =
         testScope.runTest {
             underTest.start()
@@ -1556,19 +1841,28 @@
         }
 
     @Test
-    fun notifyKeyguardDismissCallbacks_whenUnlocking_onDismissSucceeded() =
+    fun notifyKeyguardDismissCallbacks_whenUnlockingFromBouncer_onDismissSucceeded() =
         testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            prepareState()
+            val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+            prepareState(
+                authenticationMethod = AuthenticationMethodModel.Pin,
+                isDeviceUnlocked = false,
+                initialSceneKey = Scenes.Bouncer,
+            )
+            assertThat(currentSceneKey).isEqualTo(Scenes.Bouncer)
             underTest.start()
+
+            // run all pending dismiss succeeded/cancelled calls from setup:
+            runCurrent()
+            kosmos.fakeExecutor.runAllReady()
+
             val dismissCallback: IKeyguardDismissCallback = mock()
             kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
 
-            // Switch to bouncer and unlock device:
-            sceneInteractor.changeScene(Scenes.Bouncer, "")
-            assertThat(currentScene).isEqualTo(Scenes.Bouncer)
-            kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
-            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
             kosmos.fakeExecutor.runAllReady()
 
             verify(dismissCallback).onDismissSucceeded()
@@ -1577,19 +1871,26 @@
     @Test
     fun notifyKeyguardDismissCallbacks_whenLeavingBouncer_onDismissCancelled() =
         testScope.runTest {
+            val isUnlocked by collectLastValue(kosmos.deviceEntryInteractor.isUnlocked)
             val currentScene by collectLastValue(sceneInteractor.currentScene)
             prepareState()
             underTest.start()
+
+            // run all pending dismiss succeeded/cancelled calls from setup:
+            kosmos.fakeExecutor.runAllReady()
+
             val dismissCallback: IKeyguardDismissCallback = mock()
             kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
 
             // Switch to bouncer:
             sceneInteractor.changeScene(Scenes.Bouncer, "")
             assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+            runCurrent()
 
-            // Return to lockscreen:
+            // Return to lockscreen when isUnlocked=false:
             sceneInteractor.changeScene(Scenes.Lockscreen, "")
             assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isUnlocked).isFalse()
             runCurrent()
             kosmos.fakeExecutor.runAllReady()
 
@@ -1748,4 +2049,92 @@
         FakeHeadsUpRowRepository(key = key, elementKey = Any()).apply {
             this.isPinned.value = isPinned
         }
+
+    private fun setFingerprintSensorType(fingerprintSensorType: FingerprintSensorType) {
+        kosmos.fingerprintPropertyRepository.setProperties(
+            sensorId = 0,
+            strength = SensorStrength.STRONG,
+            sensorType = fingerprintSensorType,
+            sensorLocations = mapOf(),
+        )
+        kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+    }
+
+    private fun setFaceEnrolled() {
+        kosmos.biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true)
+    }
+
+    private fun TestScope.allowHapticsOnSfps(
+        isPowerButtonDown: Boolean = false,
+        lastPowerPress: Long = 10000
+    ) {
+        kosmos.fakeKeyEventRepository.setPowerButtonDown(isPowerButtonDown)
+
+        kosmos.powerRepository.updateWakefulness(
+            WakefulnessState.AWAKE,
+            WakeSleepReason.POWER_BUTTON,
+            WakeSleepReason.POWER_BUTTON,
+            powerButtonLaunchGestureTriggered = false,
+        )
+
+        advanceTimeBy(lastPowerPress)
+        runCurrent()
+    }
+
+    private fun unlockWithFingerprintAuth() {
+        kosmos.fakeKeyguardRepository.setBiometricUnlockSource(
+            BiometricUnlockSource.FINGERPRINT_SENSOR
+        )
+        kosmos.fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockMode.UNLOCK_COLLAPSING)
+    }
+
+    private fun TestScope.setupBiometricAuth(
+        hasSfps: Boolean = false,
+        hasUdfps: Boolean = false,
+        hasFace: Boolean = false
+    ) {
+        if (hasSfps) {
+            setFingerprintSensorType(FingerprintSensorType.POWER_BUTTON)
+        }
+
+        if (hasUdfps) {
+            setFingerprintSensorType(FingerprintSensorType.UDFPS_ULTRASONIC)
+        }
+
+        if (hasFace) {
+            setFaceEnrolled()
+        }
+
+        prepareState(
+            authenticationMethod = AuthenticationMethodModel.Pin,
+            isDeviceUnlocked = false,
+            initialSceneKey = Scenes.Lockscreen,
+        )
+    }
+
+    private fun updateFingerprintAuthStatus(isSuccess: Boolean) {
+        if (isSuccess) {
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+        } else {
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                FailFingerprintAuthenticationStatus
+            )
+        }
+    }
+
+    private fun updateFaceAuthStatus(isSuccess: Boolean) {
+        if (isSuccess) {
+            kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+                SuccessFaceAuthenticationStatus(
+                    successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java)
+                )
+            )
+        } else {
+            kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus(
+                FailedFaceAuthenticationStatus()
+            )
+        }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
index 32c0172..2720c57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegatorTest.kt
@@ -37,6 +37,8 @@
     private val initialSceneKey = kosmos.initialSceneKey
     private val fakeSceneDataSource = kosmos.fakeSceneDataSource
 
+    // TODO(b/356596436): Add tests for showing, hiding, and replacing overlays after we've defined
+    //  them.
     private val underTest = kosmos.sceneDataSourceDelegator
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt
deleted file mode 100644
index b526275..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.scene.ui.viewmodel
-
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.testKosmos
-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)
[email protected]
-@EnableSceneContainer
-class GoneSceneActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val shadeRepository by lazy { kosmos.shadeRepository }
-    private lateinit var underTest: GoneSceneActionsViewModel
-
-    @Before
-    fun setUp() {
-        underTest =
-            GoneSceneActionsViewModel(
-                shadeInteractor = kosmos.shadeInteractor,
-            )
-        underTest.activateIn(testScope)
-    }
-
-    @Test
-    fun downTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
-        testScope.runTest {
-            val destinationScenes by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
-            runCurrent()
-
-            assertThat(destinationScenes?.get(Swipe(SwipeDirection.Down))?.transitionKey)
-                .isEqualTo(ToSplitShade)
-        }
-
-    @Test
-    fun downTransitionKey_splitShadeDisabled_isNull() =
-        testScope.runTest {
-            val destinationScenes by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(false)
-            runCurrent()
-
-            assertThat(destinationScenes?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
new file mode 100644
index 0000000..03106ec
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.ui.viewmodel
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.testKosmos
+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)
[email protected]
+@EnableSceneContainer
+class GoneUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val shadeRepository by lazy { kosmos.shadeRepository }
+    private lateinit var underTest: GoneUserActionsViewModel
+
+    @Before
+    fun setUp() {
+        underTest =
+            GoneUserActionsViewModel(
+                shadeInteractor = kosmos.shadeInteractor,
+            )
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun downTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
+        testScope.runTest {
+            val userActions by collectLastValue(underTest.actions)
+            shadeRepository.setShadeLayoutWide(true)
+            runCurrent()
+
+            assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey)
+                .isEqualTo(ToSplitShade)
+        }
+
+    @Test
+    fun downTransitionKey_splitShadeDisabled_isNull() =
+        testScope.runTest {
+            val userActions by collectLastValue(underTest.actions)
+            shadeRepository.setShadeLayoutWide(false)
+            runCurrent()
+
+            assertThat(userActions?.get(Swipe(SwipeDirection.Down))?.transitionKey).isNull()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
deleted file mode 100644
index 206d3ac..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,133 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.scene.ui.viewmodel
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-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.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SceneActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val underTest = FakeSceneActionsViewModel()
-
-    @Test
-    fun actions_emptyBeforeActivation() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-
-            assertThat(underTest.isActive).isFalse()
-            assertThat(actions).isEmpty()
-        }
-
-    @Test
-    fun actions_emptyBeforeFirstValue() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            underTest.activateIn(testScope)
-            runCurrent()
-
-            assertThat(underTest.isActive).isTrue()
-            assertThat(actions).isEmpty()
-        }
-
-    @Test
-    fun actions() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            underTest.activateIn(testScope)
-            runCurrent()
-            assertThat(underTest.isActive).isTrue()
-
-            val expected1 =
-                mapOf(
-                    Back to UserActionResult(toScene = Scenes.Gone),
-                    Swipe(SwipeDirection.Up) to UserActionResult(toScene = Scenes.Shade)
-                )
-            underTest.upstream.value = expected1
-            runCurrent()
-            assertThat(actions).isEqualTo(expected1)
-
-            val expected2 =
-                mapOf(
-                    Back to UserActionResult(toScene = Scenes.Lockscreen),
-                    Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
-                )
-            underTest.upstream.value = expected2
-            runCurrent()
-            assertThat(actions).isEqualTo(expected2)
-        }
-
-    @Test
-    fun actions_emptyAfterCancellation() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val job = Job()
-            underTest.activateIn(testScope, job)
-            runCurrent()
-
-            val expected =
-                mapOf(
-                    Back to UserActionResult(toScene = Scenes.Lockscreen),
-                    Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
-                )
-            underTest.upstream.value = expected
-            runCurrent()
-            assertThat(actions).isEqualTo(expected)
-
-            job.cancel()
-            runCurrent()
-            assertThat(underTest.isActive).isFalse()
-            assertThat(actions).isEmpty()
-        }
-
-    private class FakeSceneActionsViewModel : SceneActionsViewModel() {
-
-        val upstream = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
-
-        override suspend fun hydrateActions(
-            setActions: (Map<UserAction, UserActionResult>) -> Unit,
-        ) {
-            upstream.collectLatest { setActions(it) }
-        }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index ea95aab..4b132c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.scene.ui.viewmodel
 
 import android.view.MotionEvent
@@ -25,11 +27,14 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.fakeOverlaysByKeys
 import com.android.systemui.scene.sceneContainerConfig
-import com.android.systemui.scene.sceneKeys
+import com.android.systemui.scene.shared.logger.sceneLogger
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.shared.model.fakeSceneDataSource
 import com.android.systemui.testKosmos
@@ -37,12 +42,15 @@
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @EnableSceneContainer
@@ -57,6 +65,9 @@
 
     private lateinit var underTest: SceneContainerViewModel
 
+    private lateinit var activationJob: Job
+    private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
+
     @Before
     fun setUp() {
         underTest =
@@ -64,28 +75,46 @@
                 sceneInteractor = sceneInteractor,
                 falsingInteractor = kosmos.falsingInteractor,
                 powerInteractor = kosmos.powerInteractor,
+                logger = kosmos.sceneLogger,
+                motionEventHandlerReceiver = { motionEventHandler ->
+                    [email protected] = motionEventHandler
+                },
             )
+        activationJob = Job()
+        underTest.activateIn(testScope, activationJob)
     }
 
     @Test
+    fun activate_setsMotionEventHandler() =
+        testScope.runTest {
+            runCurrent()
+            assertThat(motionEventHandler).isNotNull()
+        }
+
+    @Test
+    fun deactivate_clearsMotionEventHandler() =
+        testScope.runTest {
+            activationJob.cancel()
+            runCurrent()
+
+            assertThat(motionEventHandler).isNull()
+        }
+
+    @Test
     fun isVisible() =
         testScope.runTest {
-            val isVisible by collectLastValue(underTest.isVisible)
-            assertThat(isVisible).isTrue()
+            assertThat(underTest.isVisible).isTrue()
 
             sceneInteractor.setVisible(false, "reason")
-            assertThat(isVisible).isFalse()
+            runCurrent()
+            assertThat(underTest.isVisible).isFalse()
 
             sceneInteractor.setVisible(true, "reason")
-            assertThat(isVisible).isTrue()
+            runCurrent()
+            assertThat(underTest.isVisible).isTrue()
         }
 
     @Test
-    fun allSceneKeys() {
-        assertThat(underTest.allSceneKeys).isEqualTo(kosmos.sceneKeys)
-    }
-
-    @Test
     fun sceneTransition() =
         testScope.runTest {
             val currentScene by collectLastValue(underTest.currentScene)
@@ -203,15 +232,59 @@
     fun remoteUserInteraction_keepsContainerVisible() =
         testScope.runTest {
             sceneInteractor.setVisible(false, "reason")
-            val isVisible by collectLastValue(underTest.isVisible)
-            assertThat(isVisible).isFalse()
-            sceneInteractor.onRemoteUserInteractionStarted("reason")
-            assertThat(isVisible).isTrue()
+            runCurrent()
+            assertThat(underTest.isVisible).isFalse()
+            sceneInteractor.onRemoteUserInputStarted("reason")
+            runCurrent()
+            assertThat(underTest.isVisible).isTrue()
 
             underTest.onMotionEvent(
                 mock { whenever(actionMasked).thenReturn(MotionEvent.ACTION_UP) }
             )
+            runCurrent()
 
-            assertThat(isVisible).isFalse()
+            assertThat(underTest.isVisible).isFalse()
+        }
+
+    @Test
+    fun getActionableContentKey_noOverlays_returnsCurrentScene() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays).isEmpty()
+
+            val actionableContentKey =
+                underTest.getActionableContentKey(
+                    currentScene = checkNotNull(currentScene),
+                    currentOverlays = checkNotNull(currentOverlays),
+                    overlayByKey = kosmos.fakeOverlaysByKeys,
+                )
+
+            assertThat(actionableContentKey).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun getActionableContentKey_multipleOverlays_returnsTopOverlay() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+            fakeSceneDataSource.showOverlay(Overlays.QuickSettingsShade)
+            fakeSceneDataSource.showOverlay(Overlays.NotificationsShade)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(currentOverlays)
+                .containsExactly(
+                    Overlays.QuickSettingsShade,
+                    Overlays.NotificationsShade,
+                )
+
+            val actionableContentKey =
+                underTest.getActionableContentKey(
+                    currentScene = checkNotNull(currentScene),
+                    currentOverlays = checkNotNull(currentOverlays),
+                    overlayByKey = kosmos.fakeOverlaysByKeys,
+                )
+
+            assertThat(actionableContentKey).isEqualTo(Overlays.QuickSettingsShade)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.kt
new file mode 100644
index 0000000..972afb5
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModelTest.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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+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.scene.shared.model.Scenes
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = FakeUserActionsViewModel()
+
+    @Test
+    fun actions_emptyBeforeActivation() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+
+            assertThat(actions).isEmpty()
+        }
+
+    @Test
+    fun actions_emptyBeforeFirstValue() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(testScope)
+            runCurrent()
+
+            assertThat(actions).isEmpty()
+        }
+
+    @Test
+    fun actions() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            underTest.activateIn(testScope)
+            runCurrent()
+
+            val expected1 =
+                mapOf(
+                    Back to UserActionResult(toScene = Scenes.Gone),
+                    Swipe(SwipeDirection.Up) to UserActionResult(toScene = Scenes.Shade)
+                )
+            underTest.upstream.value = expected1
+            runCurrent()
+            assertThat(actions).isEqualTo(expected1)
+
+            val expected2 =
+                mapOf(
+                    Back to UserActionResult(toScene = Scenes.Lockscreen),
+                    Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+                )
+            underTest.upstream.value = expected2
+            runCurrent()
+            assertThat(actions).isEqualTo(expected2)
+        }
+
+    @Test
+    fun actions_emptyAfterCancellation() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val job = Job()
+            underTest.activateIn(testScope, job)
+            runCurrent()
+
+            val expected =
+                mapOf(
+                    Back to UserActionResult(toScene = Scenes.Lockscreen),
+                    Swipe(SwipeDirection.Down) to UserActionResult(toScene = Scenes.Shade)
+                )
+            underTest.upstream.value = expected
+            runCurrent()
+            assertThat(actions).isEqualTo(expected)
+
+            job.cancel()
+            runCurrent()
+            assertThat(actions).isEmpty()
+        }
+
+    private class FakeUserActionsViewModel : UserActionsViewModel() {
+
+        val upstream = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
+
+        override suspend fun hydrateActions(
+            setActions: (Map<UserAction, UserActionResult>) -> Unit,
+        ) {
+            upstream.collect { setActions(it) }
+        }
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
deleted file mode 100644
index fadb1d7..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
+++ /dev/null
@@ -1,573 +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.shade.domain.interactor
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
-import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@EnableSceneContainer
-class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val configurationRepository = kosmos.fakeConfigurationRepository
-    private val keyguardRepository = kosmos.fakeKeyguardRepository
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val shadeTestUtil = kosmos.shadeTestUtil
-
-    private val underTest = kosmos.shadeInteractorSceneContainerImpl
-
-    @Test
-    fun qsExpansionWhenInSplitShadeAndQsExpanded() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.qsExpansion)
-
-            // WHEN split shade is enabled and QS is expanded
-            shadeTestUtil.setSplitShade(true)
-            configurationRepository.onAnyConfigurationChange()
-            runCurrent()
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.QuickSettings,
-                        toScene = Scenes.Shade,
-                        currentScene = flowOf(Scenes.Shade),
-                        progress = MutableStateFlow(.3f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-
-            // THEN legacy shade expansion is passed through
-            Truth.assertThat(actual).isEqualTo(.3f)
-        }
-
-    @Test
-    fun qsExpansionWhenNotInSplitShadeAndQsExpanded() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.qsExpansion)
-
-            // WHEN split shade is not enabled and QS is expanded
-            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            shadeTestUtil.setSplitShade(false)
-            configurationRepository.onAnyConfigurationChange()
-            runCurrent()
-            val progress = MutableStateFlow(.3f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.QuickSettings,
-                        toScene = Scenes.Shade,
-                        currentScene = flowOf(Scenes.Shade),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN shade expansion is zero
-            Truth.assertThat(actual).isEqualTo(.7f)
-        }
-
-    @Test
-    fun qsFullscreen_falseWhenTransitioning() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.isQsFullscreen)
-
-            // WHEN scene transition active
-            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.QuickSettings,
-                        toScene = Scenes.Shade,
-                        currentScene = flowOf(Scenes.Shade),
-                        progress = MutableStateFlow(.3f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN QS is not fullscreen
-            Truth.assertThat(actual).isFalse()
-        }
-
-    @Test
-    fun qsFullscreen_falseWhenIdleNotQS() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.isQsFullscreen)
-
-            // WHEN Idle but not on QuickSettings scene
-            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(Scenes.Shade)
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN QS is not fullscreen
-            Truth.assertThat(actual).isFalse()
-        }
-
-    @Test
-    fun qsFullscreen_trueWhenIdleQS() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.isQsFullscreen)
-
-            // WHEN Idle on QuickSettings scene
-            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(Scenes.QuickSettings)
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN QS is fullscreen
-            Truth.assertThat(actual).isTrue()
-        }
-
-    @Test
-    fun lockscreenShadeExpansion_idle_onScene() =
-        testScope.runTest {
-            // GIVEN an expansion flow based on transitions to and from a scene
-            val key = Scenes.Shade
-            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
-            val expansionAmount by collectLastValue(expansion)
-
-            // WHEN transition state is idle on the scene
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN expansion is 1
-            Truth.assertThat(expansionAmount).isEqualTo(1f)
-        }
-
-    @Test
-    fun lockscreenShadeExpansion_idle_onDifferentScene() =
-        testScope.runTest {
-            // GIVEN an expansion flow based on transitions to and from a scene
-            val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.Shade)
-            val expansionAmount by collectLastValue(expansion)
-
-            // WHEN transition state is idle on a different scene
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Idle(Scenes.Lockscreen)
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN expansion is 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-        }
-
-    @Test
-    fun lockscreenShadeExpansion_transitioning_toScene() =
-        testScope.runTest {
-            // GIVEN an expansion flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
-            val expansionAmount by collectLastValue(expansion)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Lockscreen,
-                        toScene = key,
-                        currentScene = flowOf(key),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN expansion is 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN expansion matches the progress
-            Truth.assertThat(expansionAmount).isEqualTo(.4f)
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN expansion is 1
-            Truth.assertThat(expansionAmount).isEqualTo(1f)
-        }
-
-    @Test
-    fun lockscreenShadeExpansion_transitioning_fromScene() =
-        testScope.runTest {
-            // GIVEN an expansion flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
-            val expansionAmount by collectLastValue(expansion)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = key,
-                        toScene = Scenes.Lockscreen,
-                        currentScene = flowOf(Scenes.Lockscreen),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN expansion is 1
-            Truth.assertThat(expansionAmount).isEqualTo(1f)
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN expansion reflects the progress
-            Truth.assertThat(expansionAmount).isEqualTo(.6f)
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN expansion is 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-        }
-
-    fun isQsBypassingShade_goneToQs() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.isQsBypassingShade)
-
-            // WHEN transitioning from QS directly to Gone
-            configurationRepository.onAnyConfigurationChange()
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Gone,
-                        toScene = Scenes.QuickSettings,
-                        currentScene = flowOf(Scenes.QuickSettings),
-                        progress = MutableStateFlow(.1f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN qs is bypassing shade
-            Truth.assertThat(actual).isTrue()
-        }
-
-    fun isQsBypassingShade_shadeToQs() =
-        testScope.runTest {
-            val actual by collectLastValue(underTest.isQsBypassingShade)
-
-            // WHEN transitioning from QS to Shade
-            configurationRepository.onAnyConfigurationChange()
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Shade,
-                        toScene = Scenes.QuickSettings,
-                        currentScene = flowOf(Scenes.QuickSettings),
-                        progress = MutableStateFlow(.1f),
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-            runCurrent()
-
-            // THEN qs is not bypassing shade
-            Truth.assertThat(actual).isFalse()
-        }
-
-    @Test
-    fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
-        testScope.runTest {
-            // GIVEN an expansion flow based on transitions to and from a scene
-            val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
-            val expansionAmount by collectLastValue(expansion)
-
-            // WHEN transition state is starting to between different scenes
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Lockscreen,
-                        toScene = Scenes.Shade,
-                        currentScene = flowOf(Scenes.Shade),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN expansion is 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-
-            // WHEN transition state is partially complete
-            progress.value = .4f
-
-            // THEN expansion is still 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN expansion is still 0
-            Truth.assertThat(expansionAmount).isEqualTo(0f)
-        }
-
-    @Test
-    fun userInteracting_idle() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val key = Scenes.Shade
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is idle
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-        }
-
-    @Test
-    fun userInteracting_transitioning_toScene_programmatic() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Lockscreen,
-                        toScene = key,
-                        currentScene = flowOf(key),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-        }
-
-    @Test
-    fun userInteracting_transitioning_toScene_userInputDriven() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Lockscreen,
-                        toScene = key,
-                        currentScene = flowOf(key),
-                        progress = progress,
-                        isInitiatedByUserInput = true,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-        }
-
-    @Test
-    fun userInteracting_transitioning_fromScene_programmatic() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = key,
-                        toScene = Scenes.Lockscreen,
-                        currentScene = flowOf(Scenes.Lockscreen),
-                        progress = progress,
-                        isInitiatedByUserInput = false,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN interacting is false
-            Truth.assertThat(interacting).isFalse()
-        }
-
-    @Test
-    fun userInteracting_transitioning_fromScene_userInputDriven() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val key = Scenes.QuickSettings
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is starting to move to the scene
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = key,
-                        toScene = Scenes.Lockscreen,
-                        currentScene = flowOf(Scenes.Lockscreen),
-                        progress = progress,
-                        isInitiatedByUserInput = true,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-
-            // WHEN transition state is partially to the scene
-            progress.value = .4f
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-
-            // WHEN transition completes
-            progress.value = 1f
-
-            // THEN interacting is true
-            Truth.assertThat(interacting).isTrue()
-        }
-
-    @Test
-    fun userInteracting_transitioning_toAndFromDifferentScenes() =
-        testScope.runTest {
-            // GIVEN an interacting flow based on transitions to and from a scene
-            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, Scenes.Shade)
-            val interacting by collectLastValue(interactingFlow)
-
-            // WHEN transition state is starting to between different scenes
-            val progress = MutableStateFlow(0f)
-            val transitionState =
-                MutableStateFlow<ObservableTransitionState>(
-                    ObservableTransitionState.Transition(
-                        fromScene = Scenes.Lockscreen,
-                        toScene = Scenes.QuickSettings,
-                        currentScene = flowOf(Scenes.QuickSettings),
-                        progress = MutableStateFlow(0f),
-                        isInitiatedByUserInput = true,
-                        isUserInputOngoing = flowOf(false),
-                    )
-                )
-            sceneInteractor.setTransitionState(transitionState)
-
-            // THEN interacting is false
-            assertThat(interacting).isFalse()
-        }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
index 6a88664..f5022b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModelTest.kt
@@ -16,16 +16,28 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 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.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 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.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -83,4 +95,155 @@
             )
             assertThat(isKeyguardOccluded).isFalse()
         }
+
+    @Test
+    fun transitionFromOccludedToDreamingTransitionRemainsTrue() =
+        testScope.runTest {
+            val isKeyguardOccluded by collectLastValue(underTest.isKeyguardOccluded)
+            assertThat(isKeyguardOccluded).isFalse()
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.DREAMING,
+                        value = 0f,
+                        transitionState = TransitionState.STARTED,
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.LOCKSCREEN,
+                        to = KeyguardState.DREAMING,
+                        value = 0.5f,
+                        transitionState = TransitionState.RUNNING,
+                    ),
+                ),
+                testScope,
+            )
+            assertThat(isKeyguardOccluded).isFalse()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.DREAMING,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                ),
+            )
+            assertThat(isKeyguardOccluded).isTrue()
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    TransitionStep(
+                        from = KeyguardState.DREAMING,
+                        to = KeyguardState.OCCLUDED,
+                        value = 0f,
+                        transitionState = TransitionState.STARTED,
+                    ),
+                    TransitionStep(
+                        from = KeyguardState.DREAMING,
+                        to = KeyguardState.OCCLUDED,
+                        value = 0.5f,
+                        transitionState = TransitionState.RUNNING,
+                    ),
+                ),
+                testScope,
+            )
+            assertThat(isKeyguardOccluded).isTrue()
+
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.DREAMING,
+                    to = KeyguardState.OCCLUDED,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                ),
+            )
+            assertThat(isKeyguardOccluded).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun withSceneContainer_bouncerShowing_providesTheCorrectState() =
+        testScope.runTest {
+            val bouncerShowing by collectLastValue(underTest.isBouncerShowing)
+
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.Lockscreen)
+                )
+            kosmos.sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+            assertThat(bouncerShowing).isFalse()
+
+            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+            runCurrent()
+            assertThat(bouncerShowing).isTrue()
+        }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
+    fun withComposeBouncer_bouncerShowing_providesTheCorrectState() =
+        testScope.runTest {
+            val bouncerShowing by collectLastValue(underTest.isBouncerShowing)
+
+            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = false)
+            runCurrent()
+            assertThat(bouncerShowing).isFalse()
+
+            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+            runCurrent()
+            assertThat(bouncerShowing).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun withSceneContainer_doesBouncerRequireIme_providesTheCorrectState() =
+        testScope.runTest {
+            val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.Bouncer)
+                )
+            kosmos.sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+            assertThat(bouncerRequiresIme).isFalse()
+
+            // go back to lockscreen
+            transitionState.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+            runCurrent()
+
+            // change auth method
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Password
+            )
+            // go back to bouncer
+            transitionState.value = ObservableTransitionState.Idle(Scenes.Bouncer)
+            runCurrent()
+            assertThat(bouncerRequiresIme).isTrue()
+        }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_COMPOSE_BOUNCER)
+    fun withComposeBouncer_doesBouncerRequireIme_providesTheCorrectState() =
+        testScope.runTest {
+            val bouncerRequiresIme by collectLastValue(underTest.doesBouncerRequireIme)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+            runCurrent()
+            assertThat(bouncerRequiresIme).isFalse()
+
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Password
+            )
+            kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(isShowing = true)
+            runCurrent()
+            assertThat(bouncerRequiresIme).isFalse()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
deleted file mode 100644
index 3f087b4..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelTest.kt
+++ /dev/null
@@ -1,170 +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.shade.ui.viewmodel
-
-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.authentication.data.repository.fakeAuthenticationRepository
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-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)
[email protected]
-@EnableSceneContainer
-class OverlayShadeViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor = kosmos.sceneInteractor
-    private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
-
-    private val underTest = kosmos.overlayShadeViewModel
-
-    @Before
-    fun setUp() {
-        underTest.activateIn(testScope)
-    }
-
-    @Test
-    fun backgroundScene_deviceLocked_lockscreen() =
-        testScope.runTest {
-            val backgroundScene by collectLastValue(underTest.backgroundScene)
-
-            lockDevice()
-
-            assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun backgroundScene_deviceUnlocked_gone() =
-        testScope.runTest {
-            val backgroundScene by collectLastValue(underTest.backgroundScene)
-
-            lockDevice()
-            unlockDevice()
-
-            assertThat(backgroundScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun backgroundScene_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val backgroundScene by collectLastValue(underTest.backgroundScene)
-            val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-            runCurrent()
-
-            assertThat(backgroundScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun backgroundScene_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val backgroundScene by collectLastValue(underTest.backgroundScene)
-            val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-            runCurrent()
-
-            assertThat(backgroundScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun onScrimClicked_onLockscreen_goesToLockscreen() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            lockDevice()
-            sceneInteractor.changeScene(Scenes.Bouncer, "reason")
-            runCurrent()
-            assertThat(currentScene).isNotEqualTo(Scenes.Lockscreen)
-
-            underTest.onScrimClicked()
-
-            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun onScrimClicked_deviceWasEntered_goesToGone() =
-        testScope.runTest {
-            val currentScene by collectLastValue(sceneInteractor.currentScene)
-            val backgroundScene by collectLastValue(underTest.backgroundScene)
-
-            lockDevice()
-            unlockDevice()
-            sceneInteractor.changeScene(Scenes.QuickSettings, "reason")
-            runCurrent()
-            assertThat(backgroundScene).isEqualTo(Scenes.Gone)
-            assertThat(currentScene).isNotEqualTo(Scenes.Gone)
-
-            underTest.onScrimClicked()
-
-            assertThat(currentScene).isEqualTo(Scenes.Gone)
-        }
-
-    private fun TestScope.lockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeAuthenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
-        assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
-        sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-        runCurrent()
-    }
-
-    private fun TestScope.unlockDevice() {
-        val deviceUnlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus)
-
-        kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-            SuccessFingerprintAuthenticationStatus(0, true)
-        )
-        assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
-        sceneInteractor.changeScene(Scenes.Gone, "reason")
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt
deleted file mode 100644
index 06a02c6..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelTest.kt
+++ /dev/null
@@ -1,237 +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.shade.ui.viewmodel
-
-import android.platform.test.annotations.DisableFlags
-import android.testing.TestableLooper
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-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.coroutines.collectLastValue
-import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
-import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
-import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.lifecycle.activateIn
-import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
-import com.android.systemui.res.R
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.startable.shadeStartable
-import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.testKosmos
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-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)
[email protected]
-@EnableSceneContainer
-@DisableFlags(DualShade.FLAG_NAME)
-class ShadeSceneActionsViewModelTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val sceneInteractor by lazy { kosmos.sceneInteractor }
-    private val shadeRepository by lazy { kosmos.shadeRepository }
-    private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
-
-    private val underTest: ShadeSceneActionsViewModel by lazy { kosmos.shadeSceneActionsViewModel }
-
-    @Before
-    fun setUp() {
-        underTest.activateIn(testScope)
-    }
-
-    @Test
-    fun upTransitionSceneKey_deviceLocked_lockScreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pin
-            )
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_deviceUnlocked_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pin
-            )
-            setDeviceEntered(true)
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_keyguardDisabled_gone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.Pin
-            )
-            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
-        }
-
-    @Test
-    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
-            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
-            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
-                AuthenticationMethodModel.None
-            )
-            runCurrent()
-            sceneInteractor.changeScene(Scenes.Gone, "reason")
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.toScene)
-                .isEqualTo(SceneFamilies.Home)
-            assertThat(homeScene).isEqualTo(Scenes.Gone)
-        }
-
-    @Test
-    fun upTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(true)
-            runCurrent()
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey)
-                .isEqualTo(ToSplitShade)
-        }
-
-    @Test
-    fun upTransitionKey_splitShadeDisable_isNull() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-            shadeRepository.setShadeLayoutWide(false)
-            runCurrent()
-
-            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey).isNull()
-        }
-
-    @Test
-    fun downTransitionSceneKey_inSplitShade_null() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, true)
-            kosmos.shadeStartable.start()
-            val actions by collectLastValue(underTest.actions)
-            assertThat(actions?.get(Swipe(SwipeDirection.Down))?.toScene).isNull()
-        }
-
-    @Test
-    fun downTransitionSceneKey_notSplitShade_quickSettings() =
-        testScope.runTest {
-            overrideResource(R.bool.config_use_split_notification_shade, false)
-            kosmos.shadeStartable.start()
-            val actions by collectLastValue(underTest.actions)
-            assertThat(actions?.get(Swipe(SwipeDirection.Down))?.toScene)
-                .isEqualTo(Scenes.QuickSettings)
-        }
-
-    @Test
-    fun upTransitionSceneKey_customizing_noTransition() =
-        testScope.runTest {
-            val actions by collectLastValue(underTest.actions)
-
-            qsSceneAdapter.setCustomizing(true)
-            assertThat(
-                    actions!!.keys.filterIsInstance<Swipe>().filter {
-                        it.direction == SwipeDirection.Up
-                    }
-                )
-                .isEmpty()
-        }
-
-    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
-        if (isEntered) {
-            // Unlock the device marking the device has entered.
-            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
-                SuccessFingerprintAuthenticationStatus(0, true)
-            )
-            runCurrent()
-        }
-        setScene(
-            if (isEntered) {
-                Scenes.Gone
-            } else {
-                Scenes.Lockscreen
-            }
-        )
-        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
-    }
-
-    private fun TestScope.setScene(key: SceneKey) {
-        sceneInteractor.changeScene(key, "test")
-        sceneInteractor.setTransitionState(
-            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
-        )
-        runCurrent()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
new file mode 100644
index 0000000..9f3e126e
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelTest.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import android.platform.test.annotations.DisableFlags
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserActionResult
+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.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
+import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.resolver.homeSceneFamilyResolver
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.domain.startable.shadeStartable
+import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+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)
[email protected]
+@EnableSceneContainer
+@DisableFlags(DualShade.FLAG_NAME)
+class ShadeUserActionsViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sceneInteractor by lazy { kosmos.sceneInteractor }
+    private val shadeRepository by lazy { kosmos.shadeRepository }
+    private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
+
+    private val underTest: ShadeUserActionsViewModel by lazy { kosmos.shadeUserActionsViewModel }
+
+    @Before
+    fun setUp() {
+        underTest.activateIn(testScope)
+    }
+
+    @Test
+    fun upTransitionSceneKey_deviceLocked_lockScreen() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_deviceUnlocked_gone() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            setDeviceEntered(true)
+
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_keyguardDisabled_gone() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.Pin
+            )
+            kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenNotDismissed_goesToLockscreen() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
+
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Lockscreen)
+        }
+
+    @Test
+    fun upTransitionSceneKey_authMethodSwipe_lockscreenDismissed_goesToGone() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            val homeScene by collectLastValue(kosmos.homeSceneFamilyResolver.resolvedScene)
+            kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(true)
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            runCurrent()
+            sceneInteractor.changeScene(Scenes.Gone, "reason")
+
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Up)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(SceneFamilies.Home)
+            assertThat(homeScene).isEqualTo(Scenes.Gone)
+        }
+
+    @Test
+    fun upTransitionKey_splitShadeEnabled_isGoneToSplitShade() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            shadeRepository.setShadeLayoutWide(true)
+            runCurrent()
+
+            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey)
+                .isEqualTo(ToSplitShade)
+        }
+
+    @Test
+    fun upTransitionKey_splitShadeDisable_isNull() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+            shadeRepository.setShadeLayoutWide(false)
+            runCurrent()
+
+            assertThat(actions?.get(Swipe(SwipeDirection.Up))?.transitionKey).isNull()
+        }
+
+    @Test
+    fun downTransitionSceneKey_inSplitShade_null() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, true)
+            kosmos.shadeStartable.start()
+            val actions by collectLastValue(underTest.actions)
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isNull()
+        }
+
+    @Test
+    fun downTransitionSceneKey_notSplitShade_quickSettings() =
+        testScope.runTest {
+            overrideResource(R.bool.config_use_split_notification_shade, false)
+            kosmos.shadeStartable.start()
+            val actions by collectLastValue(underTest.actions)
+            assertThat(
+                    (actions?.get(Swipe(SwipeDirection.Down)) as? UserActionResult.ChangeScene)
+                        ?.toScene
+                )
+                .isEqualTo(Scenes.QuickSettings)
+        }
+
+    @Test
+    fun upTransitionSceneKey_customizing_noTransition() =
+        testScope.runTest {
+            val actions by collectLastValue(underTest.actions)
+
+            qsSceneAdapter.setCustomizing(true)
+            assertThat(
+                    actions!!.keys.filterIsInstance<Swipe>().filter {
+                        it.direction == SwipeDirection.Up
+                    }
+                )
+                .isEmpty()
+        }
+
+    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+        if (isEntered) {
+            // Unlock the device marking the device has entered.
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+        }
+        setScene(
+            if (isEntered) {
+                Scenes.Gone
+            } else {
+                Scenes.Lockscreen
+            }
+        )
+        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+    }
+
+    private fun TestScope.setScene(key: SceneKey) {
+        sceneInteractor.changeScene(key, "test")
+        sceneInteractor.setTransitionState(
+            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+        )
+        runCurrent()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
index eac86e5..ce9b3be 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/smartspace/CommunalSmartspaceControllerTest.kt
@@ -23,7 +23,6 @@
 import android.os.Handler
 import android.testing.TestableLooper
 import android.view.View
-import android.widget.FrameLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -46,6 +45,8 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.never
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -67,12 +68,9 @@
 
     @Mock private lateinit var session: SmartspaceSession
 
-    private lateinit var controller: CommunalSmartspaceController
+    private val preconditionListenerCaptor = argumentCaptor<SmartspacePrecondition.Listener>()
 
-    // TODO(b/272811280): Remove usage of real view
-    private val fakeParent by lazy {
-        FrameLayout(context)
-    }
+    private lateinit var controller: CommunalSmartspaceController
 
     /**
      * A class which implements SmartspaceView and extends View. This is mocked to provide the right
@@ -155,6 +153,26 @@
         verify(session).close()
     }
 
+    /** Ensures smartspace session begins when precondition is met if there is any listener. */
+    @Test
+    fun testConnectOnPreconditionMet() {
+        // Precondition not met
+        `when`(precondition.conditionsMet()).thenReturn(false)
+        controller.addListener(listener)
+
+        // Verify session not created because precondition not met
+        verify(smartspaceManager, never()).createSmartspaceSession(any())
+
+        // Precondition met
+        `when`(precondition.conditionsMet()).thenReturn(true)
+        verify(precondition).addListener(preconditionListenerCaptor.capture())
+        val preconditionListener = preconditionListenerCaptor.firstValue
+        preconditionListener.onCriteriaChanged()
+
+        // Verify session created
+        verify(smartspaceManager).createSmartspaceSession(any())
+    }
+
     /**
      * Ensures session is closed and weather plugin unregisters the notifier when weather smartspace
      * view is detached.
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 355669bd..f72a2e8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -43,6 +43,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.scene.data.repository.Idle
 import com.android.systemui.scene.data.repository.setTransition
+import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -112,6 +113,7 @@
                     { kosmos.sceneInteractor },
                     { kosmos.sceneContainerOcclusionInteractor },
                     { kosmos.keyguardClockInteractor },
+                    { kosmos.sceneBackInteractor },
                 ) {
                 override fun createDarkAnimator(): ObjectAnimator {
                     return mockDarkAnimator
@@ -320,12 +322,23 @@
 
             assertThat(deviceUnlockStatus!!.isUnlocked).isTrue()
 
-            kosmos.sceneInteractor.changeScene(toScene = Scenes.Gone, loggingReason = "reason")
+            kosmos.sceneInteractor.changeScene(
+                toScene = Scenes.Lockscreen,
+                loggingReason = "reason"
+            )
             runCurrent()
-            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
 
             // Call start to begin hydrating based on the scene framework:
             underTest.start()
+            runCurrent()
+
+            assertThat(statusBarState).isEqualTo(StatusBarState.KEYGUARD)
+
+            kosmos.sceneInteractor.changeScene(toScene = Scenes.Gone, loggingReason = "reason")
+            runCurrent()
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(statusBarState).isEqualTo(StatusBarState.SHADE)
 
             kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "reason")
             runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 75ecb2c..beba162 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -133,7 +133,8 @@
                 mVisibilityLocationProvider,
                 mVisualStabilityProvider,
                 mWakefulnessLifecycle,
-                mKosmos.getCommunalInteractor(),
+                mKosmos.getCommunalSceneInteractor(),
+                mKosmos.getShadeInteractor(),
                 mKosmos.getKeyguardTransitionInteractor(),
                 mLogger);
         mCoordinator.attach(mNotifPipeline);
@@ -561,11 +562,12 @@
 
     @Test
     public void testCommunalShowingWillNotSuppressReordering() {
-        // GIVEN panel is expanded and communal is showing
+        // GIVEN panel is expanded, communal is showing, and QS is collapsed
         setPulsing(false);
         setFullyDozed(false);
         setSleepy(false);
         setPanelExpanded(true);
+        setQsExpanded(false);
         setCommunalShowing(true);
 
         // Reordering should be allowed
@@ -573,6 +575,20 @@
     }
 
     @Test
+    public void testQsExpandedOverCommunalWillSuppressReordering() {
+        // GIVEN panel is expanded and communal is showing, but QS is expanded
+        setPulsing(false);
+        setFullyDozed(false);
+        setSleepy(false);
+        setPanelExpanded(true);
+        setQsExpanded(true);
+        setCommunalShowing(true);
+
+        // Reordering should not be allowed
+        assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry));
+    }
+
+    @Test
     public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() {
         // GIVEN visual stability is being maintained b/c panel is expanded
         setPulsing(false);
@@ -631,7 +647,12 @@
                         new ObservableTransitionState.Idle(
                                 isShowing ? CommunalScenes.Communal : CommunalScenes.Blank)
                 );
-        mKosmos.getCommunalRepository().setTransitionState(showingFlow);
+        mKosmos.getCommunalSceneInteractor().setTransitionState(showingFlow);
+        mTestScope.getTestScheduler().runCurrent();
+    }
+
+    private void setQsExpanded(boolean isExpanded) {
+        mKosmos.getShadeRepository().setQsExpansion(isExpanded ? 1.0f : 0.0f);
         mTestScope.getTestScheduler().runCurrent();
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
index 14134cc..cea8857 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractorTest.kt
@@ -18,11 +18,11 @@
 
 package com.android.systemui.statusbar.notification.domain.interactor
 
-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.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -31,7 +31,6 @@
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
 import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.testKosmos
@@ -44,7 +43,7 @@
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
-@EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+@EnableSceneContainer
 class HeadsUpNotificationInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.kt
new file mode 100644
index 0000000..a310ef4
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelTest.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:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.notification.row.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.notification.row.data.repository.fakeNotificationRowRepository
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
+import com.android.systemui.statusbar.notification.row.shared.IconModel
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@EnableFlags(RichOngoingNotificationFlag.FLAG_NAME)
+class EnRouteViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val repository = kosmos.fakeNotificationRowRepository
+
+    private var contentModel: EnRouteContentModel?
+        get() = repository.richOngoingContentModel.value as? EnRouteContentModel
+        set(value) {
+            repository.richOngoingContentModel.value = value
+        }
+
+    private lateinit var underTest: EnRouteViewModel
+
+    @Before
+    fun setup() {
+        underTest = kosmos.getEnRouteViewModel(repository)
+    }
+
+    @Test
+    fun viewModelShowsContent() =
+        testScope.runTest {
+            val title by collectLastValue(underTest.title)
+            val text by collectLastValue(underTest.text)
+            contentModel =
+                exampleEnRouteContent(
+                    title = "Example EnRoute Title",
+                    text = "Example EnRoute Text",
+                )
+            assertThat(title).isEqualTo("Example EnRoute Title")
+            assertThat(text).isEqualTo("Example EnRoute Text")
+        }
+
+    private fun exampleEnRouteContent(
+        icon: IconModel = mock(),
+        title: CharSequence = "example text",
+        text: CharSequence = "example title",
+    ) =
+        EnRouteContentModel(
+            smallIcon = icon,
+            title = title,
+            text = text,
+        )
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
index 1356e93..06a2c5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
@@ -84,4 +85,48 @@
                 )
             )
         }
+
+    @Test
+    fun shouldCloseGuts_userInputOngoing_currentGestureInGuts() =
+        testScope.runTest {
+            val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+            kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+            underTest.setCurrentGestureInGuts(true)
+
+            assertThat(shouldCloseGuts).isFalse()
+        }
+
+    @Test
+    fun shouldCloseGuts_userInputOngoing_currentGestureNotInGuts() =
+        testScope.runTest {
+            val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+            kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+            underTest.setCurrentGestureInGuts(false)
+
+            assertThat(shouldCloseGuts).isTrue()
+        }
+
+    @Test
+    fun shouldCloseGuts_userInputNotOngoing_currentGestureInGuts() =
+        testScope.runTest {
+            val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+            kosmos.sceneInteractor.onUserInputFinished()
+            underTest.setCurrentGestureInGuts(true)
+
+            assertThat(shouldCloseGuts).isFalse()
+        }
+
+    @Test
+    fun shouldCloseGuts_userInputNotOngoing_currentGestureNotInGuts() =
+        testScope.runTest {
+            val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+            kosmos.sceneInteractor.onUserInputFinished()
+            underTest.setCurrentGestureInGuts(false)
+
+            assertThat(shouldCloseGuts).isFalse()
+        }
 }
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 f96cf10..840aa92 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
@@ -26,6 +26,7 @@
 import com.android.settingslib.notification.data.repository.updateNotificationPolicy
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -41,7 +42,6 @@
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
@@ -535,7 +535,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun pinnedHeadsUpRows_filtersForPinnedItems() =
         testScope.runTest {
             val pinnedHeadsUpRows by collectLastValue(underTest.pinnedHeadsUpRows)
@@ -576,7 +576,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun hasPinnedHeadsUpRows_true() =
         testScope.runTest {
             val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow)
@@ -591,7 +591,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun hasPinnedHeadsUpRows_false() =
         testScope.runTest {
             val hasPinnedHeadsUpRow by collectLastValue(underTest.hasPinnedHeadsUpRow)
@@ -606,7 +606,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun topHeadsUpRow_emptyList_null() =
         testScope.runTest {
             val topHeadsUpRow by collectLastValue(underTest.topHeadsUpRow)
@@ -618,7 +618,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun headsUpAnimationsEnabled_true() =
         testScope.runTest {
             val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
@@ -631,7 +631,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun headsUpAnimationsEnabled_keyguardShowing_true() =
         testScope.runTest {
             val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 733cac9..3f97f0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -42,10 +42,14 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.KeyguardState
+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.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -56,6 +60,10 @@
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.Transition
+import com.android.systemui.scene.data.repository.setTransition
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.fakeShadeRepository
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
@@ -66,6 +74,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
@@ -295,34 +304,47 @@
 
             // Start transitioning to glanceable hub
             val progress = 0.6f
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = 0f,
-                )
+            kosmos.setTransition(
+                sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.STARTED,
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                        value = 0f,
+                    )
             )
+
             runCurrent()
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.RUNNING,
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = progress,
-                )
+            kosmos.setTransition(
+                sceneTransition =
+                    Transition(
+                        from = Scenes.Lockscreen,
+                        to = Scenes.Communal,
+                        progress = flowOf(progress)
+                    ),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.RUNNING,
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                        value = progress,
+                    )
             )
+
             runCurrent()
             assertThat(alpha).isIn(Range.closed(0f, 1f))
 
             // Finish transition to glanceable hub
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.FINISHED,
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = 1f,
-                )
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.FINISHED,
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                        value = 1f,
+                    )
             )
             assertThat(alpha).isEqualTo(0f)
 
@@ -348,35 +370,46 @@
 
             // Start transitioning to glanceable hub
             val progress = 0.6f
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.STARTED,
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = 0f,
-                )
+            kosmos.setTransition(
+                sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.STARTED,
+                        from = DREAMING,
+                        to = GLANCEABLE_HUB,
+                        value = 0f,
+                    )
             )
             runCurrent()
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.RUNNING,
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = progress,
-                )
+            kosmos.setTransition(
+                sceneTransition =
+                    Transition(
+                        from = Scenes.Lockscreen,
+                        to = Scenes.Communal,
+                        progress = flowOf(progress)
+                    ),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.RUNNING,
+                        from = DREAMING,
+                        to = GLANCEABLE_HUB,
+                        value = progress,
+                    )
             )
             runCurrent()
             // Keep notifications hidden during the transition from dream to hub
             assertThat(alpha).isEqualTo(0)
 
             // Finish transition to glanceable hub
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    transitionState = TransitionState.FINISHED,
-                    from = KeyguardState.DREAMING,
-                    to = KeyguardState.GLANCEABLE_HUB,
-                    value = 1f,
-                )
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        transitionState = TransitionState.FINISHED,
+                        from = DREAMING,
+                        to = GLANCEABLE_HUB,
+                        value = 1f,
+                    )
             )
             assertThat(alpha).isEqualTo(0f)
         }
@@ -400,35 +433,47 @@
         testScope.runTest {
             val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GONE,
-                testScope,
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Gone),
+                stateTransition = TransitionStep(from = LOCKSCREEN, to = GONE)
             )
             assertThat(isOnLockscreen).isFalse()
 
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition = TransitionStep(from = GONE, to = LOCKSCREEN)
+            )
+            assertThat(isOnLockscreen).isTrue()
             // While progressing from lockscreen, should still be true
-            keyguardTransitionRepository.sendTransitionStep(
-                TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GONE,
-                    value = 0.8f,
-                    transitionState = TransitionState.RUNNING
-                )
+            kosmos.setTransition(
+                sceneTransition = Transition(from = Scenes.Lockscreen, to = Scenes.Gone),
+                stateTransition =
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = GONE,
+                        value = 0.8f,
+                        transitionState = TransitionState.RUNNING
+                    )
             )
             assertThat(isOnLockscreen).isTrue()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.GONE,
-                to = KeyguardState.LOCKSCREEN,
-                testScope,
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition =
+                    TransitionStep(
+                        from = GONE,
+                        to = LOCKSCREEN,
+                    )
             )
             assertThat(isOnLockscreen).isTrue()
 
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.PRIMARY_BOUNCER,
-                testScope,
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Bouncer),
+                stateTransition =
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = PRIMARY_BOUNCER,
+                    )
             )
             assertThat(isOnLockscreen).isTrue()
         }
@@ -442,8 +487,8 @@
             shadeTestUtil.setLockscreenShadeExpansion(0f)
             shadeTestUtil.setQsExpansion(0f)
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.OCCLUDED,
+                from = LOCKSCREEN,
+                to = OCCLUDED,
                 testScope,
             )
             assertThat(isOnLockscreenWithoutShade).isFalse()
@@ -480,11 +525,15 @@
             assertThat(isOnGlanceableHubWithoutShade).isFalse()
 
             // Move to glanceable hub
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GLANCEABLE_HUB,
-                testScope = this
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                    )
             )
+
             assertThat(isOnGlanceableHubWithoutShade).isTrue()
 
             // While state is GLANCEABLE_HUB, validate variations of both shade and qs expansion
@@ -502,6 +551,14 @@
 
             shadeTestUtil.setQsExpansion(0f)
             shadeTestUtil.setLockscreenShadeExpansion(0f)
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Communal),
+                stateTransition =
+                    TransitionStep(
+                        from = LOCKSCREEN,
+                        to = GLANCEABLE_HUB,
+                    )
+            )
             assertThat(isOnGlanceableHubWithoutShade).isTrue()
         }
 
@@ -808,8 +865,8 @@
             // GONE transition gets to 90% complete
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GONE,
+                    from = LOCKSCREEN,
+                    to = GONE,
                     transitionState = TransitionState.STARTED,
                     value = 0f,
                 )
@@ -817,8 +874,8 @@
             runCurrent()
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.GONE,
+                    from = LOCKSCREEN,
+                    to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
                 )
@@ -843,8 +900,8 @@
             // OCCLUDED transition gets to 90% complete
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
+                    from = LOCKSCREEN,
+                    to = OCCLUDED,
                     transitionState = TransitionState.STARTED,
                     value = 0f,
                 )
@@ -852,8 +909,8 @@
             runCurrent()
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.LOCKSCREEN,
-                    to = KeyguardState.OCCLUDED,
+                    from = LOCKSCREEN,
+                    to = OCCLUDED,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
                 )
@@ -877,8 +934,8 @@
             showLockscreen()
 
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GONE,
+                from = LOCKSCREEN,
+                to = GONE,
                 testScope
             )
             keyguardRepository.setStatusBarState(StatusBarState.SHADE)
@@ -922,8 +979,8 @@
 
             // ... then user hits power to go to AOD
             keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.AOD,
+                from = LOCKSCREEN,
+                to = AOD,
                 testScope,
             )
             // ... followed by a shade collapse
@@ -945,7 +1002,7 @@
             // PRIMARY_BOUNCER->GONE transition is started
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.STARTED,
                     value = 0f,
@@ -956,7 +1013,7 @@
             // PRIMARY_BOUNCER->GONE transition running
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.1f,
@@ -967,7 +1024,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
@@ -979,7 +1036,7 @@
             hideCommunalScene()
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.FINISHED,
                     value = 1f
@@ -1003,7 +1060,7 @@
             // PRIMARY_BOUNCER->GONE transition is started
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.STARTED,
                 )
@@ -1013,7 +1070,7 @@
             // PRIMARY_BOUNCER->GONE transition running
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.1f,
@@ -1024,7 +1081,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
@@ -1035,7 +1092,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.PRIMARY_BOUNCER,
+                    from = PRIMARY_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.FINISHED,
                     value = 1f
@@ -1058,7 +1115,7 @@
             // ALTERNATE_BOUNCER->GONE transition is started
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.STARTED,
                     value = 0f,
@@ -1069,7 +1126,7 @@
             // ALTERNATE_BOUNCER->GONE transition running
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.1f,
@@ -1080,7 +1137,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
@@ -1092,7 +1149,7 @@
             hideCommunalScene()
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.FINISHED,
                     value = 1f
@@ -1116,7 +1173,7 @@
             // ALTERNATE_BOUNCER->GONE transition is started
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.STARTED,
                 )
@@ -1126,7 +1183,7 @@
             // ALTERNATE_BOUNCER->GONE transition running
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.1f,
@@ -1137,7 +1194,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.RUNNING,
                     value = 0.9f,
@@ -1148,7 +1205,7 @@
 
             keyguardTransitionRepository.sendTransitionStep(
                 TransitionStep(
-                    from = KeyguardState.ALTERNATE_BOUNCER,
+                    from = ALTERNATE_BOUNCER,
                     to = GONE,
                     transitionState = TransitionState.FINISHED,
                     value = 1f
@@ -1165,8 +1222,8 @@
         keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.AOD,
-            to = KeyguardState.LOCKSCREEN,
+            from = AOD,
+            to = LOCKSCREEN,
             testScope,
         )
     }
@@ -1178,8 +1235,8 @@
         keyguardRepository.setDreaming(true)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.LOCKSCREEN,
-            to = KeyguardState.DREAMING,
+            from = LOCKSCREEN,
+            to = DREAMING,
             testScope,
         )
     }
@@ -1191,8 +1248,8 @@
         keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.AOD,
-            to = KeyguardState.LOCKSCREEN,
+            from = AOD,
+            to = LOCKSCREEN,
             testScope,
         )
     }
@@ -1204,8 +1261,8 @@
         keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.AOD,
-            to = KeyguardState.LOCKSCREEN,
+            from = AOD,
+            to = LOCKSCREEN,
             testScope,
         )
     }
@@ -1219,8 +1276,8 @@
         kosmos.keyguardBouncerRepository.setPrimaryShow(true)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.GLANCEABLE_HUB,
-            to = KeyguardState.PRIMARY_BOUNCER,
+            from = GLANCEABLE_HUB,
+            to = PRIMARY_BOUNCER,
             testScope,
         )
     }
@@ -1234,8 +1291,8 @@
         kosmos.keyguardBouncerRepository.setPrimaryShow(false)
         runCurrent()
         keyguardTransitionRepository.sendTransitionSteps(
-            from = KeyguardState.GLANCEABLE_HUB,
-            to = KeyguardState.ALTERNATE_BOUNCER,
+            from = GLANCEABLE_HUB,
+            to = ALTERNATE_BOUNCER,
             testScope,
         )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
index a6fdd03..b5dbc3f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt
@@ -22,8 +22,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
-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.statusbar.connectivity.WifiPickerTrackerFactory
@@ -73,11 +71,6 @@
     private val demoModelFlow = MutableStateFlow<FakeWifiEventModel?>(null)
 
     private val mainExecutor = FakeExecutor(FakeSystemClock())
-    private val featureFlags =
-        FakeFeatureFlagsClassic().also {
-            it.set(Flags.INSTANT_TETHER, true)
-            it.set(Flags.WIFI_SECONDARY_NETWORKS, true)
-        }
 
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
@@ -93,7 +86,6 @@
 
         realImpl =
             WifiRepositoryImpl(
-                featureFlags,
                 testScope.backgroundScope,
                 mainExecutor,
                 testDispatcher,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 84c728c..b9ca8fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -93,7 +93,7 @@
     fun ssid_carrierMergedNetwork_outputsNull() =
         testScope.runTest {
             wifiRepository.setWifiNetwork(
-                WifiNetworkModel.CarrierMerged(networkId = 1, subscriptionId = 2, level = 1)
+                WifiNetworkModel.CarrierMerged(subscriptionId = 2, level = 1)
             )
 
             var latest: String? = "default"
@@ -106,53 +106,10 @@
         }
 
     @Test
-    fun ssid_isPasspointAccessPoint_outputsPasspointName() =
-        testScope.runTest {
-            wifiRepository.setWifiNetwork(
-                WifiNetworkModel.Active(
-                    networkId = 1,
-                    level = 1,
-                    isPasspointAccessPoint = true,
-                    passpointProviderFriendlyName = "friendly",
-                )
-            )
-
-            var latest: String? = null
-            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
-            runCurrent()
-
-            assertThat(latest).isEqualTo("friendly")
-
-            job.cancel()
-        }
-
-    @Test
-    fun ssid_isOnlineSignUpForPasspoint_outputsPasspointName() =
-        testScope.runTest {
-            wifiRepository.setWifiNetwork(
-                WifiNetworkModel.Active(
-                    networkId = 1,
-                    level = 1,
-                    isOnlineSignUpForPasspointAccessPoint = true,
-                    passpointProviderFriendlyName = "friendly",
-                )
-            )
-
-            var latest: String? = null
-            val job = underTest.ssid.onEach { latest = it }.launchIn(this)
-            runCurrent()
-
-            assertThat(latest).isEqualTo("friendly")
-
-            job.cancel()
-        }
-
-    @Test
     fun ssid_unknownSsid_outputsNull() =
         testScope.runTest {
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 1,
                     ssid = WifiManager.UNKNOWN_SSID,
                 )
@@ -172,7 +129,6 @@
         testScope.runTest {
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 1,
                     ssid = "MyAwesomeWifiNetwork",
                 )
@@ -234,11 +190,9 @@
         testScope.runTest {
             val wifiNetwork =
                 WifiNetworkModel.Active(
-                    networkId = 45,
                     isValidated = true,
                     level = 3,
                     ssid = "AB",
-                    passpointProviderFriendlyName = "friendly"
                 )
             wifiRepository.setWifiNetwork(wifiNetwork)
 
@@ -346,7 +300,6 @@
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.Active(
                     ssid = "ssid 2",
-                    networkId = 1,
                     level = 2,
                 )
             )
@@ -367,7 +320,6 @@
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.Active(
                     ssid = "ssid 2",
-                    networkId = 1,
                     level = 2,
                 )
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 1237347..72a45b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -25,14 +25,14 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.connectivity.WifiIcons
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.TestScope
@@ -58,10 +59,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class WifiViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: WifiViewModel
 
-    @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "WifiViewModelTest")
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -86,7 +88,7 @@
                 AirplaneModeInteractor(
                     airplaneModeRepository,
                     connectivityRepository,
-                    FakeMobileConnectionsRepository(),
+                    kosmos.fakeMobileConnectionsRepository,
                 ),
                 tableLogBuffer,
                 testScope.backgroundScope,
@@ -113,9 +115,7 @@
             val latestKeyguard by collectLastValue(keyguard.wifiIcon)
             val latestQs by collectLastValue(qs.wifiIcon)
 
-            wifiRepository.setWifiNetwork(
-                WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1)
-            )
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 1))
 
             assertThat(latestHome).isInstanceOf(WifiIcon.Visible::class.java)
             assertThat(latestHome).isEqualTo(latestKeyguard)
@@ -130,7 +130,6 @@
             // Even WHEN the network has a valid hotspot type
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.Active(
-                    NETWORK_ID,
                     isValidated = true,
                     level = 1,
                     hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
@@ -192,9 +191,7 @@
             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
             createAndSetViewModel()
 
-            wifiRepository.setWifiNetwork(
-                WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
-            )
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(ssid = null, level = 1))
 
             val activityIn by collectLastValue(underTest.isActivityInViewVisible)
             val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -217,9 +214,7 @@
             whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(true)
             createAndSetViewModel()
 
-            wifiRepository.setWifiNetwork(
-                WifiNetworkModel.Active(NETWORK_ID, ssid = null, level = 1)
-            )
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(ssid = null, level = 1))
 
             val activityIn by collectLastValue(underTest.isActivityInViewVisible)
             val activityOut by collectLastValue(underTest.isActivityOutViewVisible)
@@ -468,8 +463,6 @@
     }
 
     companion object {
-        private const val NETWORK_ID = 2
-        private val ACTIVE_VALID_WIFI_NETWORK =
-            WifiNetworkModel.Active(NETWORK_ID, ssid = "AB", level = 1)
+        private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(ssid = "AB", level = 1)
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 9005ae3..89aa670 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -241,7 +241,7 @@
         alm.showNotification(entry);
 
         final boolean removedImmediately = alm.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false);
+                entry.getKey(), /* releaseImmediately = */ false, "removeDeferred");
         assertFalse(removedImmediately);
         assertTrue(alm.isHeadsUpEntry(entry.getKey()));
     }
@@ -254,7 +254,7 @@
         alm.showNotification(entry);
 
         final boolean removedImmediately = alm.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ true);
+                entry.getKey(), /* releaseImmediately = */ true, "forceRemove");
         assertTrue(removedImmediately);
         assertFalse(alm.isHeadsUpEntry(entry.getKey()));
     }
@@ -430,7 +430,7 @@
         hum.showNotification(entry);
 
         final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false);
+                entry.getKey(), /* releaseImmediately = */ false, "beforeMinimumDisplayTime");
         assertFalse(removedImmediately);
         assertTrue(hum.isHeadsUpEntry(entry.getKey()));
 
@@ -452,7 +452,7 @@
         assertTrue(hum.isHeadsUpEntry(entry.getKey()));
 
         final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ false);
+                entry.getKey(), /* releaseImmediately = */ false, "afterMinimumDisplayTime");
         assertTrue(removedImmediately);
         assertFalse(hum.isHeadsUpEntry(entry.getKey()));
     }
@@ -466,7 +466,7 @@
         hum.showNotification(entry);
 
         final boolean removedImmediately = hum.removeNotification(
-                entry.getKey(), /* releaseImmediately = */ true);
+                entry.getKey(), /* releaseImmediately = */ true, "afterMinimumDisplayTime");
         assertTrue(removedImmediately);
         assertFalse(hum.isHeadsUpEntry(entry.getKey()));
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
index 7a6838a..469a7bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt
@@ -23,6 +23,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -36,7 +37,6 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -179,8 +179,8 @@
         mContext
             .getOrCreateTestableResources()
             .addOverride(R.integer.ambient_notification_extension_time, 500)
-        mAvalancheController = AvalancheController(dumpManager, mUiEventLogger,
-                mHeadsUpManagerLogger, mBgHandler)
+        mAvalancheController =
+            AvalancheController(dumpManager, mUiEventLogger, mHeadsUpManagerLogger, mBgHandler)
     }
 
     @Test
@@ -200,7 +200,12 @@
         hmp.addSwipedOutNotification(entry.key)
 
         // Remove should succeed because the notification is swiped out
-        val removedImmediately = hmp.removeNotification(entry.key, /* releaseImmediately= */ false)
+        val removedImmediately =
+            hmp.removeNotification(
+                entry.key,
+                /* releaseImmediately= */ false,
+                /* reason= */ "swipe out"
+            )
         Assert.assertTrue(removedImmediately)
         Assert.assertFalse(hmp.isHeadsUpEntry(entry.key))
     }
@@ -464,9 +469,9 @@
         @get:Parameters(name = "{0}")
         val flags: List<FlagsParameterization>
             get() = buildList {
-                addAll(FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME))
                 addAll(
-                    FlagsParameterization.allCombinationsOf(NotificationsHeadsUpRefactor.FLAG_NAME)
+                    FlagsParameterization.allCombinationsOf(NotificationThrottleHun.FLAG_NAME)
+                        .andSceneContainer()
                 )
             }
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
index 69207ba..3efabd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/TestableHeadsUpManager.java
@@ -100,7 +100,7 @@
 
     @Override
     public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
-            boolean animate) {
+            boolean animate, @NonNull String reason) {
         throw new UnsupportedOperationException();
     }
 
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 11504aa..fb32855 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
@@ -16,13 +16,16 @@
 
 package com.android.systemui.statusbar.policy.domain.interactor
 
+import android.app.AutomaticZenRule
 import android.app.NotificationManager.Policy
 import android.provider.Settings
 import android.provider.Settings.Secure.ZEN_DURATION
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
+import android.service.notification.SystemZenRules
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.R
 import com.android.settingslib.notification.data.repository.updateNotificationPolicy
 import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.systemui.SysuiTestCase
@@ -217,4 +220,121 @@
             assertThat(zenModeRepository.getModeActiveDuration(manualDnd.id))
                 .isEqualTo(Duration.ofMinutes(60))
         }
+
+    @Test
+    fun activeModes_computesMainActiveMode() =
+        testScope.runTest {
+            val activeModes by collectLastValue(underTest.activeModes)
+
+            zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME)
+            zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER)
+
+            runCurrent()
+            assertThat(activeModes?.modeNames).hasSize(0)
+            assertThat(activeModes?.mainMode).isNull()
+
+            zenModeRepository.activateMode("Other")
+            runCurrent()
+            assertThat(activeModes?.modeNames).containsExactly("Mode Other")
+            assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Other")
+
+            zenModeRepository.activateMode("Bedtime")
+            runCurrent()
+            assertThat(activeModes?.modeNames)
+                .containsExactly("Mode Bedtime", "Mode Other")
+                .inOrder()
+            assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime")
+
+            zenModeRepository.deactivateMode("Other")
+            runCurrent()
+            assertThat(activeModes?.modeNames).containsExactly("Mode Bedtime")
+            assertThat(activeModes?.mainMode?.name).isEqualTo("Mode Bedtime")
+
+            zenModeRepository.deactivateMode("Bedtime")
+            runCurrent()
+            assertThat(activeModes?.modeNames).hasSize(0)
+            assertThat(activeModes?.mainMode).isNull()
+        }
+
+    @Test
+    fun getActiveModes_computesMainActiveMode() = runTest {
+        zenModeRepository.addMode(id = "Bedtime", type = AutomaticZenRule.TYPE_BEDTIME)
+        zenModeRepository.addMode(id = "Other", type = AutomaticZenRule.TYPE_OTHER)
+
+        var activeModes = underTest.getActiveModes()
+        assertThat(activeModes.modeNames).hasSize(0)
+        assertThat(activeModes.mainMode).isNull()
+
+        zenModeRepository.activateMode("Other")
+        activeModes = underTest.getActiveModes()
+        assertThat(activeModes.modeNames).containsExactly("Mode Other")
+        assertThat(activeModes.mainMode?.name).isEqualTo("Mode Other")
+
+        zenModeRepository.activateMode("Bedtime")
+        activeModes = underTest.getActiveModes()
+        assertThat(activeModes.modeNames).containsExactly("Mode Bedtime", "Mode Other").inOrder()
+        assertThat(activeModes.mainMode?.name).isEqualTo("Mode Bedtime")
+
+        zenModeRepository.deactivateMode("Other")
+        activeModes = underTest.getActiveModes()
+        assertThat(activeModes.modeNames).containsExactly("Mode Bedtime")
+        assertThat(activeModes.mainMode?.name).isEqualTo("Mode Bedtime")
+
+        zenModeRepository.deactivateMode("Bedtime")
+        activeModes = underTest.getActiveModes()
+        assertThat(activeModes.modeNames).hasSize(0)
+        assertThat(activeModes.mainMode).isNull()
+    }
+
+    @Test
+    fun mainActiveMode_flows() =
+        testScope.runTest {
+            val mainActiveMode by collectLastValue(underTest.mainActiveMode)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setId("Bedtime")
+                        .setName("Mode Bedtime")
+                        .setType(AutomaticZenRule.TYPE_BEDTIME)
+                        .setActive(false)
+                        .setPackage(mContext.packageName)
+                        .setIconResId(R.drawable.ic_zen_mode_type_bedtime)
+                        .build(),
+                    TestModeBuilder()
+                        .setId("Other")
+                        .setName("Mode Other")
+                        .setType(AutomaticZenRule.TYPE_OTHER)
+                        .setActive(false)
+                        .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                        .setIconResId(R.drawable.ic_zen_mode_type_other)
+                        .build(),
+                )
+            )
+
+            runCurrent()
+            assertThat(mainActiveMode).isNull()
+
+            zenModeRepository.activateMode("Other")
+            runCurrent()
+            assertThat(mainActiveMode?.name).isEqualTo("Mode Other")
+            assertThat(mainActiveMode?.icon?.key?.resId)
+                .isEqualTo(R.drawable.ic_zen_mode_type_other)
+
+            zenModeRepository.activateMode("Bedtime")
+            runCurrent()
+            assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
+            assertThat(mainActiveMode?.icon?.key?.resId)
+                .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+
+            zenModeRepository.deactivateMode("Other")
+            runCurrent()
+            assertThat(mainActiveMode?.name).isEqualTo("Mode Bedtime")
+            assertThat(mainActiveMode?.icon?.key?.resId)
+                .isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
+
+            zenModeRepository.deactivateMode("Bedtime")
+            runCurrent()
+            assertThat(mainActiveMode).isNull()
+        }
 }
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 bcad7e7b..a0f6431 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
@@ -23,6 +23,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.settingslib.notification.modes.ZenMode
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testDispatcher
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.mockModesDialogEventLogger
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,6 +42,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.clearInvocations
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 
 @SmallTest
@@ -50,9 +53,16 @@
     private val repository = kosmos.fakeZenModeRepository
     private val interactor = kosmos.zenModeInteractor
     private val mockDialogDelegate = kosmos.mockModesDialogDelegate
+    private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger
 
     private val underTest =
-        ModesDialogViewModel(context, interactor, kosmos.testDispatcher, mockDialogDelegate)
+        ModesDialogViewModel(
+            context,
+            interactor,
+            kosmos.testDispatcher,
+            mockDialogDelegate,
+            mockDialogEventLogger
+        )
 
     @Test
     fun tiles_filtersOutUserDisabledModes() =
@@ -142,7 +152,7 @@
             }
             with(tiles?.elementAt(1)!!) {
                 assertThat(this.text).isEqualTo("Active with manual")
-                assertThat(this.subtext).isEqualTo("trigger description")
+                assertThat(this.subtext).isEqualTo("On • trigger description")
                 assertThat(this.enabled).isEqualTo(true)
             }
             with(tiles?.elementAt(2)!!) {
@@ -264,6 +274,139 @@
         }
 
     @Test
+    fun tiles_calculatesSubtext() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("With description, inactive")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When the going gets tough")
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("With description, active")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When in Rome")
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("With description, needs setup")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When you find yourself in a hole")
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, inactive")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, active")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, needs setup")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles!!).hasSize(6)
+            assertThat(tiles!![0].subtext).isEqualTo("When the going gets tough")
+            assertThat(tiles!![1].subtext).isEqualTo("On • When in Rome")
+            assertThat(tiles!![2].subtext).isEqualTo("Set up")
+            assertThat(tiles!![3].subtext).isEqualTo("Off")
+            assertThat(tiles!![4].subtext).isEqualTo("On")
+            assertThat(tiles!![5].subtext).isEqualTo("Set up")
+        }
+
+    @Test
+    fun tiles_populatesFieldsForAccessibility() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("With description, inactive")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When the going gets tough")
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("With description, active")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When in Rome")
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("With description, needs setup")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription("When you find yourself in a hole")
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, inactive")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, active")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Without description, needs setup")
+                        .setManualInvocationAllowed(true)
+                        .setTriggerDescription(null)
+                        .setEnabled(false, /* byUser= */ false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles!!).hasSize(6)
+            with(tiles?.elementAt(0)!!) {
+                assertThat(this.stateDescription).isEqualTo("Off")
+                assertThat(this.subtextDescription).isEqualTo("When the going gets tough")
+            }
+            with(tiles?.elementAt(1)!!) {
+                assertThat(this.stateDescription).isEqualTo("On")
+                assertThat(this.subtextDescription).isEqualTo("When in Rome")
+            }
+            with(tiles?.elementAt(2)!!) {
+                assertThat(this.stateDescription).isEqualTo("Off")
+                assertThat(this.subtextDescription).isEqualTo("Set up")
+            }
+            with(tiles?.elementAt(3)!!) {
+                assertThat(this.stateDescription).isEqualTo("Off")
+                assertThat(this.subtextDescription).isEmpty()
+            }
+            with(tiles?.elementAt(4)!!) {
+                assertThat(this.stateDescription).isEqualTo("On")
+                assertThat(this.subtextDescription).isEmpty()
+            }
+            with(tiles?.elementAt(5)!!) {
+                assertThat(this.stateDescription).isEqualTo("Off")
+                assertThat(this.subtextDescription).isEqualTo("Set up")
+            }
+
+            // All tiles have the same long click info
+            tiles!!.forEach { assertThat(it.onLongClickLabel).isEqualTo("Open settings") }
+        }
+
+    @Test
     fun onClick_togglesTileState() =
         testScope.runTest {
             val tiles by collectLastValue(underTest.tiles)
@@ -432,4 +575,84 @@
             assertThat(intent.extras?.getString(Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID))
                 .isEqualTo("B")
         }
+
+    @Test
+    fun onClick_logsOnOffEvents() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder.MANUAL_DND_ACTIVE,
+                    TestModeBuilder()
+                        .setId("id1")
+                        .setName("Inactive Mode One")
+                        .setActive(false)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setId("id2")
+                        .setName("Active Non-Invokable Mode Two") // but can be turned off by tile
+                        .setActive(true)
+                        .setManualInvocationAllowed(false)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(3)
+
+            // Trigger onClick for each tile in sequence
+            tiles?.forEach { it.onClick.invoke() }
+            runCurrent()
+
+            val onModeCaptor = argumentCaptor<ZenMode>()
+            val offModeCaptor = argumentCaptor<ZenMode>()
+
+            // manual mode and mode 2 should have turned off
+            verify(mockDialogEventLogger, times(2)).logModeOff(offModeCaptor.capture())
+            val off0 = offModeCaptor.firstValue
+            assertThat(off0.isManualDnd).isTrue()
+
+            val off1 = offModeCaptor.secondValue
+            assertThat(off1.id).isEqualTo("id2")
+
+            // should also have logged turning mode 1 on
+            verify(mockDialogEventLogger).logModeOn(onModeCaptor.capture())
+            val on = onModeCaptor.lastValue
+            assertThat(on.id).isEqualTo("id1")
+        }
+
+    @Test
+    fun onLongClick_logsSettingsEvents() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles)
+
+            repository.addModes(
+                listOf(
+                    TestModeBuilder.MANUAL_DND_ACTIVE,
+                    TestModeBuilder()
+                        .setId("id1")
+                        .setName("Inactive Mode One")
+                        .setActive(false)
+                        .setManualInvocationAllowed(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(tiles?.size).isEqualTo(2)
+            val modeCaptor = argumentCaptor<ZenMode>()
+
+            // long click manual DND and then automatic mode
+            tiles?.forEach { it.onLongClick.invoke() }
+            runCurrent()
+
+            verify(mockDialogEventLogger, times(2)).logModeSettings(modeCaptor.capture())
+            val manualMode = modeCaptor.firstValue
+            assertThat(manualMode.isManualDnd).isTrue()
+
+            val automaticMode = modeCaptor.lastValue
+            assertThat(automaticMode.id).isEqualTo("id1")
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index 7385a47..7c55f7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -32,7 +32,6 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
 import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.data.repository.audioSharingRepository
 import com.android.systemui.volume.domain.model.AudioOutputDevice
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.localMediaRepository
@@ -222,32 +221,4 @@
 
         val testIcon = TestStubDrawable()
     }
-
-    @Test
-    fun inAudioSharing_returnTrue() {
-        with(kosmos) {
-            testScope.runTest {
-                audioSharingRepository.setInAudioSharing(true)
-
-                val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
-                runCurrent()
-
-                assertThat(inAudioSharing).isTrue()
-            }
-        }
-    }
-
-    @Test
-    fun notInAudioSharing_returnFalse() {
-        with(kosmos) {
-            testScope.runTest {
-                audioSharingRepository.setInAudioSharing(false)
-
-                val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
-                runCurrent()
-
-                assertThat(inAudioSharing).isFalse()
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
index a1fcfcd..c9d147b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractorTest.kt
@@ -49,6 +49,24 @@
     }
 
     @Test
+    fun handleInAudioSharingChange() {
+        with(kosmos) {
+            testScope.runTest {
+                with(audioSharingRepository) { setInAudioSharing(true) }
+                val inAudioSharing by collectLastValue(underTest.isInAudioSharing)
+                runCurrent()
+
+                Truth.assertThat(inAudioSharing).isEqualTo(true)
+
+                with(audioSharingRepository) { setInAudioSharing(false) }
+                runCurrent()
+
+                Truth.assertThat(inAudioSharing).isEqualTo(false)
+            }
+        }
+    }
+
+    @Test
     fun handlePrimaryGroupChange_nullVolume() {
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
index 4cf924a..cb6dc19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt
@@ -20,11 +20,12 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.captioningRepository
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.testKosmos
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
-import com.android.systemui.view.accessibility.data.repository.captioningRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -49,7 +50,7 @@
                 CaptioningViewModel(
                     context,
                     captioningInteractor,
-                    testScope.backgroundScope,
+                    applicationCoroutineScope,
                     uiEventLogger,
                 )
             }
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 0f56d0b..fa7f37c 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
@@ -90,8 +90,9 @@
                 assertThat(model)
                     .isEqualTo(
                         MediaOutputComponentModel.Calling(
-                            AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
-                            false,
+                            device = AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+                            isInAudioSharing = false,
+                            canOpenAudioSwitcher = false,
                         )
                     )
             }
@@ -101,6 +102,9 @@
     fun hasSession_stateIs_MediaSession() =
         with(kosmos) {
             testScope.runTest {
+                localMediaRepository.updateCurrentConnectedDevice(
+                    TestMediaDevicesFactory.builtInMediaDevice()
+                )
                 mediaControllerRepository.setActiveSessions(listOf(localMediaController))
 
                 val model by collectLastValue(underTest.mediaOutputModel.filterData())
@@ -113,6 +117,7 @@
                     assertThat(device)
                         .isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
                     assertThat(isInAudioSharing).isFalse()
+                    assertThat(canOpenAudioSwitcher).isTrue()
                 }
             }
         }
@@ -129,8 +134,9 @@
                 assertThat(model)
                     .isEqualTo(
                         MediaOutputComponentModel.Idle(
-                            AudioOutputDevice.BuiltIn("built_in_media", testIcon),
-                            true,
+                            device = AudioOutputDevice.BuiltIn("built_in_media", testIcon),
+                            isInAudioSharing = true,
+                            canOpenAudioSwitcher = false,
                         )
                     )
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
index ab184ab..f232d52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorImplTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.volume.panel.domain.unavailableCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
 import com.google.common.truth.Truth.assertThat
 import javax.inject.Provider
 import kotlinx.coroutines.test.runTest
@@ -49,6 +50,7 @@
                     enabledComponents,
                     { defaultCriteria },
                     testScope.backgroundScope,
+                    volumePanelLogger,
                     criteriaByKey,
                 )
             }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
index 420b955..fe6c741 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelTest.kt
@@ -24,21 +24,31 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.configurationController
 import com.android.systemui.statusbar.policy.fakeConfigurationController
 import com.android.systemui.testKosmos
+import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
 import com.android.systemui.volume.panel.data.repository.volumePanelGlobalStateRepository
 import com.android.systemui.volume.panel.domain.interactor.criteriaByKey
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
 import com.android.systemui.volume.panel.domain.unavailableCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import com.android.systemui.volume.panel.shared.model.mockVolumePanelUiComponentProvider
 import com.android.systemui.volume.panel.ui.composable.componentByKey
 import com.android.systemui.volume.panel.ui.layout.DefaultComponentsLayoutManager
 import com.android.systemui.volume.panel.ui.layout.componentsLayoutManager
+import com.android.systemui.volume.shared.volumePanelLogger
 import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
 import javax.inject.Provider
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
@@ -55,6 +65,7 @@
             volumePanelGlobalStateRepository.updateVolumePanelState { it.copy(isVisible = true) }
         }
 
+    private val realDumpManager = DumpManager()
     private val testableResources = context.orCreateTestableResources
 
     private lateinit var underTest: VolumePanelViewModel
@@ -124,6 +135,64 @@
     }
 
     @Test
+    fun testDumpableRegister_unregister() =
+        with(kosmos) {
+            testScope.runTest {
+                val job = launch {
+                    applicationCoroutineScope = this
+                    underTest = createViewModel()
+
+                    runCurrent()
+
+                    assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME })
+                        .isTrue()
+                }
+
+                runCurrent()
+                job.cancel()
+
+                assertThat(realDumpManager.getDumpables().any { it.name == DUMPABLE_NAME }).isTrue()
+            }
+        }
+
+    @Test
+    fun testDumpingState() =
+        test({
+            testableResources.addOverride(R.bool.volume_panel_is_large_screen, false)
+            testableResources.overrideConfiguration(
+                Configuration().apply { orientation = Configuration.ORIENTATION_PORTRAIT }
+            )
+            componentByKey =
+                mapOf(
+                    COMPONENT_1 to mockVolumePanelUiComponentProvider,
+                    COMPONENT_2 to mockVolumePanelUiComponentProvider,
+                    BOTTOM_BAR to mockVolumePanelUiComponentProvider,
+                )
+            criteriaByKey = mapOf(COMPONENT_2 to Provider { unavailableCriteria })
+        }) {
+            testScope.runTest {
+                runCurrent()
+
+                StringWriter().use {
+                    underTest.dump(PrintWriter(it), emptyArray())
+
+                    assertThat(it.buffer.toString())
+                        .isEqualTo(
+                            "volumePanelState=" +
+                                "VolumePanelState(orientation=1, isLargeScreen=false)\n" +
+                                "componentsLayout=( " +
+                                "headerComponents= " +
+                                "contentComponents=" +
+                                "test_component:1:visible=true, " +
+                                "test_component:2:visible=false " +
+                                "footerComponents= " +
+                                "bottomBarComponent=test_bottom_bar:visible=true )\n"
+                        )
+                }
+            }
+        }
+
+    @Test
     fun dismissBroadcast_dismissesPanel() = test {
         testScope.runTest {
             runCurrent() // run the flows to let allow the receiver to be registered
@@ -140,11 +209,26 @@
     private fun test(setup: Kosmos.() -> Unit = {}, test: Kosmos.() -> Unit) =
         with(kosmos) {
             setup()
-            underTest = volumePanelViewModel
+            underTest = createViewModel()
+
             test()
         }
 
+    private fun Kosmos.createViewModel(): VolumePanelViewModel =
+        VolumePanelViewModel(
+            context.orCreateTestableResources.resources,
+            applicationCoroutineScope,
+            volumePanelComponentFactory,
+            configurationController,
+            broadcastDispatcher,
+            realDumpManager,
+            volumePanelLogger,
+            volumePanelGlobalStateInteractor,
+        )
+
     private companion object {
+        const val DUMPABLE_NAME = "VolumePanelViewModel"
+
         const val BOTTOM_BAR: VolumePanelComponentKey = "test_bottom_bar"
         const val COMPONENT_1: VolumePanelComponentKey = "test_component:1"
         const val COMPONENT_2: VolumePanelComponentKey = "test_component:2"
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
index 509f022..84f39af 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceConfigPlugin.kt
@@ -21,4 +21,6 @@
 interface BcSmartspaceConfigPlugin {
     /** Gets default date/weather disabled status. */
     val isDefaultDateWeatherDisabled: Boolean
+    /** Gets if Smartspace should use ViewPager2 */
+    val isViewPager2Enabled: Boolean
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
index 789a473..f920b18 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/WeatherData.kt
@@ -4,6 +4,7 @@
 import android.util.Log
 import android.view.View
 import androidx.annotation.VisibleForTesting
+import androidx.core.text.util.LocalePreferences
 
 typealias WeatherTouchAction = (View) -> Unit
 
@@ -54,12 +55,35 @@
             }
         }
 
-        private fun readIntFromBundle(extras: Bundle, key: String): Int? =
+        private fun readIntFromBundle(extras: Bundle, key: String): Int? {
             try {
-                extras.getString(key)?.toInt()
+                return extras.getString(key)?.toInt()
             } catch (e: Exception) {
-                null
+                return null
             }
+        }
+
+        fun getPlaceholderWeatherData(): WeatherData {
+            return getPlaceholderWeatherData(
+                LocalePreferences.getTemperatureUnit() == LocalePreferences.TemperatureUnit.CELSIUS
+            )
+        }
+
+        private const val DESCRIPTION_PLACEHODLER = ""
+        private const val TEMPERATURE_FAHRENHEIT_PLACEHOLDER = 58
+        private const val TEMPERATURE_CELSIUS_PLACEHOLDER = 21
+        private val WEATHERICON_PLACEHOLDER = WeatherData.WeatherStateIcon.MOSTLY_SUNNY
+
+        fun getPlaceholderWeatherData(useCelsius: Boolean): WeatherData {
+            return WeatherData(
+                description = DESCRIPTION_PLACEHODLER,
+                state = WEATHERICON_PLACEHOLDER,
+                temperature =
+                    if (useCelsius) TEMPERATURE_CELSIUS_PLACEHOLDER
+                    else TEMPERATURE_FAHRENHEIT_PLACEHOLDER,
+                useCelsius = useCelsius,
+            )
+        }
     }
 
     // Values for WeatherStateIcon must stay in sync with go/g3-WeatherStateIcon
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index d13c750..be44dee 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -169,6 +169,7 @@
         public boolean isTransient = false;
         public String expandedAccessibilityClassName;
         public boolean handlesLongClick = true;
+        public boolean handlesSecondaryClick = false;
         @Nullable
         public Drawable sideViewCustomDrawable;
         public String spec;
@@ -212,6 +213,7 @@
                     || !Objects.equals(other.isTransient, isTransient)
                     || !Objects.equals(other.dualTarget, dualTarget)
                     || !Objects.equals(other.handlesLongClick, handlesLongClick)
+                    || !Objects.equals(other.handlesSecondaryClick, handlesSecondaryClick)
                     || !Objects.equals(other.sideViewCustomDrawable, sideViewCustomDrawable);
             other.spec = spec;
             other.icon = icon;
@@ -227,6 +229,7 @@
             other.dualTarget = dualTarget;
             other.isTransient = isTransient;
             other.handlesLongClick = handlesLongClick;
+            other.handlesSecondaryClick = handlesSecondaryClick;
             other.sideViewCustomDrawable = sideViewCustomDrawable;
             return changed;
         }
@@ -252,6 +255,7 @@
             sb.append(",disabledByPolicy=").append(disabledByPolicy);
             sb.append(",dualTarget=").append(dualTarget);
             sb.append(",isTransient=").append(isTransient);
+            sb.append(",handlesSecondaryClick=").append(handlesSecondaryClick);
             sb.append(",state=").append(state);
             sb.append(",sideViewCustomDrawable=").append(sideViewCustomDrawable);
             return sb.append(']');
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 57fd9ea..d069c01 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -71,7 +71,7 @@
     <string name="kg_prompt_after_dpm_lock" msgid="6002804765868345917">"لمزيد من الأمان، تم قفل الجهاز وفقًا لسياسة العمل."</string>
     <string name="kg_prompt_after_user_lockdown_pin" msgid="5374732179740050373">"يجب إدخال رقم التعريف الشخصي بعد إلغاء الفتح الذكي."</string>
     <string name="kg_prompt_after_user_lockdown_password" msgid="9097968458291129795">"يجب إدخال كلمة المرور بعد إلغاء الفتح الذكي."</string>
-    <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"يجب رسم النقش بعد إلغاء التأمين."</string>
+    <string name="kg_prompt_after_user_lockdown_pattern" msgid="215072203613597906">"يجب رسم النقش بعد إلغاء الفتح الذكي."</string>
     <string name="kg_prompt_unattended_update" msgid="4366635751738712452">"سيتم تثبيت التحديث عندما لا يكون الجهاز قيد الاستخدام."</string>
     <string name="kg_prompt_pin_auth_timeout" msgid="5868644725126275245">"‏يجب تعزيز الأمان. لم يُستخدَم رقم PIN لبعض الوقت."</string>
     <string name="kg_prompt_password_auth_timeout" msgid="5809110458491920871">"يجب تعزيز الأمان. لم تستخدَم كلمة المرور لبعض الوقت."</string>
diff --git a/packages/SystemUI/res-keyguard/values-as/strings.xml b/packages/SystemUI/res-keyguard/values-as/strings.xml
index 4ed7e27..8ac43e1 100644
--- a/packages/SystemUI/res-keyguard/values-as/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-as/strings.xml
@@ -27,7 +27,7 @@
     <string name="keyguard_enter_your_password" msgid="7225626204122735501">"আপোনাৰ পাছৱর্ড দিয়ক"</string>
     <string name="keyguard_enter_password" msgid="6483623792371009758">"পাছৱৰ্ড দিয়ক"</string>
     <string name="keyguard_sim_error_message_short" msgid="633630844240494070">"ব্যৱহাৰৰ অযোগ্য ছিম কাৰ্ড"</string>
-    <string name="keyguard_charged" msgid="5478247181205188995">"চ্চার্জ কৰা হ’ল"</string>
+    <string name="keyguard_charged" msgid="5478247181205188995">"চাৰ্জ কৰা হ’ল"</string>
     <string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • বেতাঁৰৰ জৰিয়তে চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in_dock" msgid="2122073051904360987">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চ্চার্জ কৰি থকা হৈছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml
index fd90d08..cf2057c 100644
--- a/packages/SystemUI/res-keyguard/values-ru/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml
@@ -57,7 +57,7 @@
     <string name="kg_wrong_pin" msgid="4160978845968732624">"Неверный PIN-код"</string>
     <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"Неверный PIN-код."</string>
     <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"Повторите попытку или используйте отпечаток пальца."</string>
-    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Отпечаток не распознан."</string>
+    <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Отпечаток не распознан"</string>
     <string name="bouncer_face_not_recognized" msgid="1666128054475597485">"Лицо не распознано."</string>
     <string name="kg_bio_try_again_or_pin" msgid="4752168242723808390">"Повторите попытку или введите PIN-код."</string>
     <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"Повторите попытку или введите пароль."</string>
diff --git a/packages/SystemUI/res-product/values-af/strings.xml b/packages/SystemUI/res-product/values-af/strings.xml
index c1a6803..e49c890 100644
--- a/packages/SystemUI/res-product/values-af/strings.xml
+++ b/packages/SystemUI/res-product/values-af/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Jou foon sal outomaties probeer afkoel. Jy kan steeds jou foon gebruik, maar dit sal dalk stadiger werk.\n\nJou foon sal normaal werk nadat dit afgekoel het."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Jou toestel sal outomaties probeer afkoel. Jy kan steeds jou toestel gebruik, maar dit sal dalk stadiger werk.\n\nJou toestel sal normaal werk nadat dit afgekoel het."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Jou tablet sal outomaties probeer afkoel. Jy kan steeds jou tablet gebruik, maar dit sal dalk stadiger werk.\n\nJou tablet sal normaal werk nadat dit afgekoel het."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie aan die kant van die tablet."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie aan die kant van die toestel."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie aan die kant van die foon."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie op die rand van die tablet."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie op die rand van die toestel."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Die vingerafdruksensor is op die aan/af-skakelaar. Dit is die plat knoppie langs die verhewe volumeknoppie op die rand van die foon."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Ontsluit jou foon vir meer opsies"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Ontsluit jou tablet vir meer opsies"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Ontsluit jou toestel vir meer opsies"</string>
diff --git a/packages/SystemUI/res-product/values-am/strings.xml b/packages/SystemUI/res-product/values-am/strings.xml
index b8b2df8..49e5d52 100644
--- a/packages/SystemUI/res-product/values-am/strings.xml
+++ b/packages/SystemUI/res-product/values-am/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"የእርስዎ ስልክ በራስ-ሰር ለመቀዝቀዝ ይሞክራል። አሁንም ስልክዎን መጠቀም ይችላሉ ነገር ግን ቀትፋፋ ሆኖ ሊያሄድ ይችላል።\n\nአንዴ ስልክዎ ከቀዘቀዘ በኋላ በመደበኛነት ያሄዳል።"</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"መሣሪያዎ በራስ-ሰር ለመቀዝቀዝ ይሞክራል። አሁንም መሣሪያዎን መጠቀም ይችላሉ ነገር ግን ቀርፋፋ ሆኖ ሊያሄድ ይችላል።\n\nአንዴ መሣሪያዎ ከቀዘቀዘ በኋላ በመደበኛነት ያሄዳል"</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"ጡባዊዎ በራስ-ሰር ለመቀዝቀዝ ይሞክራል። አሁንም ጡባዊዎን መጠቀም ይችላሉ ነገር ግን ቀርፋፋ ሆኖ ሊያሄድ ይችላል።\n\nአንዴ ጡባዊዎ ከቀዘቀዘ በኋላ በመደበኛነት ያሄዳል።"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በጡባዊው ጫፍ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በመሣሪያው ጫፍ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በስልኩ ጫፍ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በጡባዊው በማንኛውም ጠርዝ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በመሣሪያው በማንኛውም ጠርዝ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"የጣት አሻራ ዳሳሹ የማብሪያ/ማጥፊያ ቁልፉ ላይ ነው። በስልኩ በማንኛውም ጠርዝ ላይ ከፍ ካለው የድምፅ አዝራር ቀጥሎ ያለው ጠፍጣፋ አዝራር ነው።"</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"ለተጨማሪ አማራጮች የእርስዎን ስልክ ይክፈቱ"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"ለተጨማሪ አማራጮች የእርስዎን ጡባዊ ይክፈቱ"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"ለተጨማሪ አማራጮች የእርስዎን መሣሪያ ይክፈቱ"</string>
diff --git a/packages/SystemUI/res-product/values-ar/strings.xml b/packages/SystemUI/res-product/values-ar/strings.xml
index 0ddb911..d365ef2 100644
--- a/packages/SystemUI/res-product/values-ar/strings.xml
+++ b/packages/SystemUI/res-product/values-ar/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"سيحاول الهاتف تخفيض درجة حرارته تلقائيًا. سيظل بإمكانك استخدام هاتفك، ولكنه قد يعمل بشكل أبطأ.\n\nبعد أن تنخفض درجة حرارة الهاتف، سيستعيد سرعته المعتادة."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"سيحاول جهازك تخفيض درجة حرارته تلقائيًا. سيظل بإمكانك استخدام جهازك، ولكنه قد يعمل بشكل أبطأ.\n\nبعد أن تنخفض درجة حرارة الجهاز، سيستعيد سرعته المعتادة."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"سيحاول جهازك اللوحي تخفيض درجة حرارته تلقائيًا. سيظل بإمكانك استخدام جهازك اللوحي، ولكنه قد يعمل بشكل أبطأ.\n\nبعد أن تنخفض درجة حرارة الجهاز اللوحي، سيستعيد سرعته المعتادة."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"توجد أداة استشعار بصمة الإصبع على زر التشغيل. زر التشغيل هو الزر المسطّح بجانب زرَّي التحكّم بمستوى الصوت البارزَين في الجزء الجانبي من الجهاز اللوحي."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"توجد أداة استشعار بصمة الإصبع على زر التشغيل. زر التشغيل هو الزر المسطّح بجانب زرَّي التحكّم بمستوى الصوت البارزَين في إحدى حواف الجهاز اللوحي."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"توجد أداة استشعار بصمة الإصبع على زر التشغيل. زر التشغيل هو الزر المسطّح بجانب زرَّي التحكّم بمستوى الصوت البارزَين في الجزء الجانبي من الجهاز."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"توجد أداة استشعار بصمة الإصبع على زر التشغيل. زر التشغيل هو الزر المسطّح بجانب زرَّي التحكّم بمستوى الصوت البارزَين في الجزء الجانبي من الهاتف."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"توجد أداة استشعار بصمة الإصبع على زر التشغيل. زر التشغيل هو الزر المسطّح بجانب زرَّي التحكّم بمستوى الصوت البارزَين في إحدى حواف الهاتف."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"يمكنك فتح قفل هاتفك للوصول إلى مزيد من الخيارات."</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"يمكنك فتح قفل جهازك اللوحي للوصول إلى مزيد من الخيارات."</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"يمكنك فتح قفل جهازك للوصول إلى مزيد من الخيارات."</string>
diff --git a/packages/SystemUI/res-product/values-be/strings.xml b/packages/SystemUI/res-product/values-be/strings.xml
index f9ef0d5..9b2658e 100644
--- a/packages/SystemUI/res-product/values-be/strings.xml
+++ b/packages/SystemUI/res-product/values-be/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Ваш тэлефон будзе астываць аўтаматычна. Вы можаце і далей ім карыстацца, але ён можа працаваць больш павольна.\n\nПасля таго як тэлефон астыне, ён будзе працаваць у звычайным рэжыме."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Ваша прылада будзе астываць аўтаматычна. Вы можаце і далей ёй карыстацца, але яна можа працаваць больш павольна.\n\nПасля таго як прылада астыне, яна будзе працаваць у звычайным рэжыме."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Ваш планшэт будзе астываць аўтаматычна. Вы можаце і далей ім карыстацца, але ён можа працаваць больш павольна.\n\nПасля таго як планшэт астыне, ён будзе працаваць у звычайным рэжыме."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сканер адбіткаў пальцаў знаходзіцца на кнопцы сілкавання. Гэта плоская кнопка побач з выпуклай кнопкай гучнасці на бакавой грані планшэта."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сканер адбіткаў пальцаў знаходзіцца на кнопцы сілкавання. Гэта плоская кнопка побач з выпуклай кнопкай гучнасці на адной з бакавых паверхняў планшэта."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Сканер адбіткаў пальцаў знаходзіцца на кнопцы сілкавання. Гэта плоская кнопка побач з выпуклай кнопкай гучнасці на бакавой грані прылады."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сканер адбіткаў пальцаў знаходзіцца на кнопцы сілкавання. Гэта плоская кнопка побач з выпуклай кнопкай гучнасці на бакавой грані тэлефона."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сканер адбіткаў пальцаў знаходзіцца на кнопцы сілкавання. Гэта плоская кнопка побач з выпуклай кнопкай гучнасці на адной з бакавых паверхняў тэлефона."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Каб адкрыць іншыя параметры, разблакіруйце тэлефон"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Каб адкрыць іншыя параметры, разблакіруйце планшэт"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Каб адкрыць іншыя параметры, разблакіруйце прыладу"</string>
diff --git a/packages/SystemUI/res-product/values-bg/strings.xml b/packages/SystemUI/res-product/values-bg/strings.xml
index 40140c4..039ece7 100644
--- a/packages/SystemUI/res-product/values-bg/strings.xml
+++ b/packages/SystemUI/res-product/values-bg/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Телефонът ви автоматично ще направи опит да се охлади. Пак можете да го използвате, но той може да работи по-бавно.\n\nСлед като се охлади, ще работи нормално."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Устройството ви автоматично ще направи опит да се охлади. Пак можете да го използвате, но то може да работи по-бавно.\n\nСлед като се охлади, ще работи нормално."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Таблетът ви автоматично ще направи опит да се охлади. Пак можете да го използвате, но той може да работи по-бавно.\n\nСлед като се охлади, ще работи нормално."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сензорът за отпечатъци се намира върху бутона за захранване. Този бутон е плосък и е разположен на ръба на таблета до повдигнатия бутон за силата на звука."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сензорът за отпечатъци се намира върху бутона за захранване. Този бутон е плосък и е разположен отстрани на таблета до повдигнатия бутон за силата на звука."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Сензорът за отпечатъци се намира върху бутона за захранване. Този бутон е плосък и е разположен на ръба на устройството до повдигнатия бутон за силата на звука."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сензорът за отпечатъци се намира върху бутона за захранване. Този бутон е плосък и е разположен на ръба на телефона до повдигнатия бутон за силата на звука."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сензорът за отпечатъци се намира върху бутона за захранване. Този бутон е плосък и е разположен отстрани на телефона до повдигнатия бутон за силата на звука."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Отключете телефона си за още опции"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Отключете таблета си за още опции"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Отключете устройството си за още опции"</string>
diff --git a/packages/SystemUI/res-product/values-ca/strings.xml b/packages/SystemUI/res-product/values-ca/strings.xml
index 4b84a6b..cfec9b2 100644
--- a/packages/SystemUI/res-product/values-ca/strings.xml
+++ b/packages/SystemUI/res-product/values-ca/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"El telèfon provarà de refredar-se automàticament. Podràs continuar utilitzant-lo, però és possible que funcioni més lentament.\n\nUn cop s\'hagi refredat, funcionarà amb normalitat."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"El dispositiu provarà de refredar-se automàticament. Pots continuar utilitzant-lo, però és possible que funcioni més lentament.\n\nUn cop s\'hagi refredat, funcionarà amb normalitat."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"La tauleta provarà de refredar-se automàticament. Pots continuar utilitzant-la, però és possible que funcioni més lentament.\n\nUn cop s\'hagi refredat, funcionarà amb normalitat."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu al lateral de la tauleta."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu al lateral del dispositiu."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu al lateral del telèfon."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu als costats de la tauleta."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu als costats del dispositiu."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"El sensor d\'empremtes digitals es troba al botó d\'engegada. És el botó pla situat al costat del botó de volum amb relleu als costats del telèfon."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloqueja el teu telèfon per veure més opcions"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloqueja la teva tauleta per veure més opcions"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloqueja el teu dispositiu per veure més opcions"</string>
diff --git a/packages/SystemUI/res-product/values-de/strings.xml b/packages/SystemUI/res-product/values-de/strings.xml
index 80389a4..acf27a8d 100644
--- a/packages/SystemUI/res-product/values-de/strings.xml
+++ b/packages/SystemUI/res-product/values-de/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Dein Smartphone kühlt sich automatisch ab. Du kannst es weiterhin nutzen, aber es reagiert möglicherweise langsamer.\n\nSobald dein Smartphone abgekühlt ist, funktioniert es wieder normal."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Dein Gerät kühlt sich automatisch ab. Du kannst es weiterhin nutzen, aber es reagiert möglicherweise langsamer.\n\nSobald dein Gerät abgekühlt ist, funktioniert es wieder normal."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Dein Tablet kühlt sich automatisch ab. Du kannst es weiterhin nutzen, aber es reagiert möglicherweise langsamer.\n\nSobald dein Tablet abgekühlt ist, funktioniert es wieder normal."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste am Rand des Tablets."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste am Rand des Geräts."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste am Rand des Smartphones."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste an einer der Seiten des Tablets."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste an einer der Seiten des Geräts."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Der Fingerabdrucksensor befindet sich auf der Ein-/Aus-Taste. Das ist die flache Taste neben der erhöhten Lautstärketaste an einer der Seiten des Smartphones."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Entsperre dein Smartphone für weitere Optionen"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Entsperre dein Tablet für weitere Optionen"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Entsperre dein Gerät für weitere Optionen"</string>
diff --git a/packages/SystemUI/res-product/values-es/strings.xml b/packages/SystemUI/res-product/values-es/strings.xml
index eb7d849..90ebe96 100644
--- a/packages/SystemUI/res-product/values-es/strings.xml
+++ b/packages/SystemUI/res-product/values-es/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"El teléfono intentará enfriarse automáticamente. Puedes seguir usándolo, pero es posible que funcione más lento.\n\nUna vez que el teléfono se haya enfriado, funcionará con normalidad."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"El dispositivo intentará enfriarse automáticamente. Puedes seguir usándolo, pero es posible que funcione más lento.\n\nUna vez que el dispositivo se haya enfriado, funcionará con normalidad."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"La tablet intentará enfriarse automáticamente. Puedes seguir usándola, pero es posible que funcione más lenta.\n\nUna vez que la tablet se haya enfriado, funcionará con normalidad."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral de la tablet."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral del dispositivo."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el lateral del teléfono."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el borde de la tablet."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el borde del dispositivo."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"El sensor de huellas digitales está en el botón de encendido. Es el botón plano situado junto al botón de volumen con relieve en el borde del teléfono."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desbloquea el teléfono para ver más opciones"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desbloquea el tablet para ver más opciones"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desbloquea el dispositivo para ver más opciones"</string>
diff --git a/packages/SystemUI/res-product/values-et/strings.xml b/packages/SystemUI/res-product/values-et/strings.xml
index 8cd4ae6..be1e084 100644
--- a/packages/SystemUI/res-product/values-et/strings.xml
+++ b/packages/SystemUI/res-product/values-et/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Teie telefon proovib automaatselt maha jahtuda. Saate telefoni ikka kasutada, kuid see võib olla aeglasem.\n\nKui telefon on jahtunud, töötab see tavapäraselt."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Teie seade proovib automaatselt maha jahtuda. Saate seadet ikka kasutada, kuid see võib olla aeglasem.\n\nKui seade on jahtunud, töötab see tavapäraselt."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Teie tahvelarvuti proovib automaatselt maha jahtuda. Saate tahvelarvutit ikka kasutada, kuid see võib olla aeglasem.\n\nKui tahvelarvuti on jahtunud, töötab see tavapäraselt."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Sõrmejäljeandur asub toitenupul. See on tahvelarvuti küljel helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Sõrmejäljeandur asub toitenupul. See on seadme küljel helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Sõrmejäljeandur asub toitenupul. See on telefoni küljel helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Sõrmejäljeandur asub toitenupul. See on tahvelarvuti serval helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Sõrmejäljeandur asub toitenupul. See on seadme serval helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Sõrmejäljeandur asub toitenupul. See on telefoni serval helitugevuse kõrgendatud nupu kõrval olev lame nupp."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Lisavalikute nägemiseks avage oma telefon"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Lisavalikute nägemiseks avage oma tahvelarvuti"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Lisavalikute nägemiseks avage oma seade"</string>
diff --git a/packages/SystemUI/res-product/values-eu/strings.xml b/packages/SystemUI/res-product/values-eu/strings.xml
index 032811c..abd3f39 100644
--- a/packages/SystemUI/res-product/values-eu/strings.xml
+++ b/packages/SystemUI/res-product/values-eu/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Telefonoa automatikoki saiatuko da hozten. Hoztu bitartean, erabiltzen jarrai dezakezu, baina baliteke mantsoago funtzionatzea.\n\nHozten denean, ohi bezala funtzionatuko du."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Gailua automatikoki saiatuko da hozten. Hoztu bitartean, erabiltzen jarrai dezakezu, baina baliteke mantsoago funtzionatzea.\n\nHozten denean, ohi bezala funtzionatuko du."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Tableta automatikoki saiatuko da hozten. Hoztu bitartean, erabiltzen jarrai dezakezu, baina baliteke mantsoago funtzionatzea.\n\nHozten denean, ohi bezala funtzionatuko du."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Hatz-marken sentsorea etengailuan dago. Tabletaren ertzeko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Hatz-marken sentsorea etengailuan dago. Gailuaren ertzeko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Hatz-marken sentsorea etengailuan dago. Telefonoaren ertzeko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Hatz-marken sentsorea etengailuan dago. Tabletaren ertzetako bateko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Hatz-marken sentsorea etengailuan dago. Gailuaren ertzetako bateko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Hatz-marken sentsorea etengailuan dago. Telefonoaren ertzetako bateko bolumen-botoi goratuaren ondoan dagoen botoi laua da."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Desblokeatu telefonoa aukera gehiago ikusteko"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Desblokeatu tableta aukera gehiago ikusteko"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Desblokeatu gailua aukera gehiago ikusteko"</string>
diff --git a/packages/SystemUI/res-product/values-fr-rCA/strings.xml b/packages/SystemUI/res-product/values-fr-rCA/strings.xml
index 9ff7ff8..eec07a5 100644
--- a/packages/SystemUI/res-product/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-product/values-fr-rCA/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Votre téléphone va se refroidir automatiquement. Vous pouvez toujours l\'utiliser, mais il risque d\'être plus lent.\n\nUne fois refroidi, il va fonctionner normalement."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Votre téléphone va se refroidir automatiquement. Vous pouvez toujours utiliser votre téléphone, mais il risque d\'être plus lent.\n\nUne fois refroidi, il fonctionnera normalement."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Votre tablette va se refroidir automatiquement. Vous pouvez toujours utiliser votre tablette, mais elle risque d\'être plus lente.\n\nUne fois refroidie, elle va fonctionner normalement."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur le bord de la tablette."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur le bord de l\'appareil."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur le bord du téléphone."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur l\'un des bords de la tablette."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur l\'un des bords de l\'appareil."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Le capteur d\'empreintes digitales est situé sur l\'interrupteur. Il s\'agit du bouton plat situé à côté du bouton de volume surélevé, sur l\'un des bords du téléphone."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Déverrouillez votre téléphone pour afficher davantage d\'options"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Déverrouillez votre tablette pour afficher davantage d\'options"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Déverrouillez votre appareil pour afficher davantage d\'options"</string>
diff --git a/packages/SystemUI/res-product/values-hu/strings.xml b/packages/SystemUI/res-product/values-hu/strings.xml
index 34f20a0..97feff8 100644
--- a/packages/SystemUI/res-product/values-hu/strings.xml
+++ b/packages/SystemUI/res-product/values-hu/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"A telefon automatikusan megpróbál lehűlni. Továbbra is tudja használni a telefont, de elképzelhető, hogy működése lelassul.\n\nAmint a telefon lehűl, újra a szokásos módon működik majd."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Az eszköz automatikusan megpróbál lehűlni. Továbbra is tudja használni, de elképzelhető, hogy működése lelassul.\n\nAmint az eszköz lehűl, újra a szokásos módon működik majd."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"A táblagép automatikusan megpróbál lehűlni. Továbbra is tudja használni, de elképzelhető, hogy működése lelassul.\n\nAmint a táblagép lehűl, újra a szokásos módon működik majd."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb a táblagép szélén."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb az eszköz szélén."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb a telefon szélén."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb a táblagép valamelyik oldalán."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb az eszköz valamelyik oldalán."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Az ujjlenyomat-érzékelő a bekapcsológombon található. Ez a kiemelkedő hangerőgomb melletti lapos gomb a telefon valamelyik oldalán."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"További lehetőségekért oldja fel a telefont"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"További lehetőségekért oldja fel a táblagépet"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"További lehetőségekért oldja fel az eszközt"</string>
diff --git a/packages/SystemUI/res-product/values-hy/strings.xml b/packages/SystemUI/res-product/values-hy/strings.xml
index f527eab..8e4c75a 100644
--- a/packages/SystemUI/res-product/values-hy/strings.xml
+++ b/packages/SystemUI/res-product/values-hy/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Ձեր հեռախոսն ավտոմատ կերպով կփորձի hովանալ։ Կարող եք շարունակել օգտագործել հեռախոսը, սակայն հնարավոր է, որ այն ավելի դանդաղ աշխատի։\n\nՀովանալուց հետո հեռախոսը կաշխատի կանոնավոր կերպով։"</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Ձեր սարքը ավտոմատ կերպով կփորձի hովանալ։ Կարող եք շարունակել օգտագործել սարքը, սակայն հնարավոր է, որ այն ավելի դանդաղ աշխատի:\n\nՀովանալուց հետո սարքը կաշխատի կանոնավոր կերպով։"</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Ձեր պլանշետը ավտոմատ կերպով կփորձի hովանալ։ Կարող եք շարունակել օգտագործել պլանշետը, սակայն հնարավոր է, որ այն ավելի դանդաղ աշխատի:\n\nՀովանալուց հետո պլանշետը կաշխատի կանոնավոր կերպով։"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ պլանշետի կողային մասում։"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ սարքի կողային մասում։"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ հեռախոսի կողային մասում։"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ պլանշետի եզրին։"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ սարքի եզրին։"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Մատնահետքերի սկաները սնուցման կոճակի վրա է։ Այն հարթ կոճակ է ձայնի ուժգնության ուռուցիկ կոճակի կողքին՝ հեռախոսի եզրին։"</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Ապակողպեք ձեր հեռախոսը՝ լրացուցիչ կարգավորումները տեսնելու համար"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Ապակողպեք ձեր պլանշետը՝ լրացուցիչ կարգավորումները տեսնելու համար"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Ապակողպեք ձեր սարքը՝ լրացուցիչ կարգավորումները տեսնելու համար"</string>
diff --git a/packages/SystemUI/res-product/values-is/strings.xml b/packages/SystemUI/res-product/values-is/strings.xml
index 0f3f71cb..a39dd2da 100644
--- a/packages/SystemUI/res-product/values-is/strings.xml
+++ b/packages/SystemUI/res-product/values-is/strings.xml
@@ -58,7 +58,7 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Síminn reynir að kæla sig sjálfkrafa. Þú getur áfram notað símann en hann gæti verið hægvirkari.\n\nÞegar síminn hefur kælt sig mun hann virka eðlilega."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Tækið reynir að kæla sig sjálfkrafa. Þú getur áfram notað tækið en það gæti verið hægvirkara.\n\nÞegar tækið hefur kælt sig mun það virka eðlilega."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Spjaldtölvan reynir að kæla sig sjálfkrafa. Þú getur áfram notað spjaldtölvuna en hún gæti verið hægvirkari.\n\nÞegar spjaldtölvan hefur kælt sig mun hún virka eðlilega."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Fingrafaralesarinn er á aflrofanum. Það er flati hnappurinn við hliðina á upphleypta hljóðstyrkshnappnum á hlið spjaldtölvunnar."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Fingrafaralesarinn er á aflrofanum. Það er flati hnappurinn við hliðina á upphleypta hljóðstyrkshnappnum á brún spjaldtölvunnar."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Fingrafaralesarinn er á aflrofanum. Það er flati hnappurinn við hliðina á upphleypta hljóðstyrkshnappnum á hlið tækisins."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Fingrafaralesarinn er á aflrofanum. Það er flati hnappurinn við hliðina á upphleypta hljóðstyrkshnappnum á hlið símans."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Taktu símann úr lás til að fá fleiri valkosti"</string>
diff --git a/packages/SystemUI/res-product/values-iw/strings.xml b/packages/SystemUI/res-product/values-iw/strings.xml
index 9365dd9..5049d10 100644
--- a/packages/SystemUI/res-product/values-iw/strings.xml
+++ b/packages/SystemUI/res-product/values-iw/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"קירור הטלפון ייעשה באופן אוטומטי. אפשר עדיין להשתמש בטלפון, אבל ייתכן שהוא יפעל לאט יותר.\n\nהטלפון יחזור לפעול כרגיל לאחר שיתקרר."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"קירור המכשיר ייעשה באופן אוטומטי. אפשר עדיין להשתמש במכשיר אבל ייתכן שהוא יפעל לאט יותר.\n\nהמכשיר יחזור לפעול כרגיל לאחר שיתקרר."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"קירור הטאבלט ייעשה באופן אוטומטי. אפשר עדיין להשתמש בטאבלט אבל ייתכן שהוא יפעל לאט יותר.\n\nהטאבלט יחזור לפעול כרגיל לאחר שיתקרר."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"חיישן טביעות האצבע נמצא על לחצן ההפעלה. זה הלחצן השטוח ליד הלחצן הבולט של עוצמת הקול בשולי הטאבלט."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"חיישן טביעות האצבע נמצא על לחצן ההפעלה. זה הלחצן השטוח ליד הלחצן הבולט של עוצמת הקול בקצה הטאבלט."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"חיישן טביעות האצבע נמצא על לחצן ההפעלה. זה הלחצן השטוח ליד הלחצן הבולט של עוצמת הקול בשולי המכשיר."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"חיישן טביעות האצבע נמצא על לחצן ההפעלה. זה הלחצן השטוח ליד הלחצן הבולט של עוצמת הקול בשולי הטלפון."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"חיישן טביעות האצבע נמצא על לחצן ההפעלה. זה הלחצן השטוח ליד הלחצן הבולט של עוצמת הקול בקצה הטלפון."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"לאפשרויות נוספות, יש לבטל את נעילת הטלפון"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"לאפשרויות נוספות, יש לבטל את נעילת הטאבלט"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"לאפשרויות נוספות, יש לבטל את נעילת המכשיר"</string>
diff --git a/packages/SystemUI/res-product/values-ja/strings.xml b/packages/SystemUI/res-product/values-ja/strings.xml
index 9d054c9..cd7f1c1 100644
--- a/packages/SystemUI/res-product/values-ja/strings.xml
+++ b/packages/SystemUI/res-product/values-ja/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"スマートフォンは自動的にクールダウンを行います。その間もスマートフォンを使用できますが、動作が遅くなる可能性があります。\n\nクールダウンが完了すると、通常どおり動作します。"</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"デバイスは自動的にクールダウンを行います。その間もデバイスを使用できますが、動作が遅くなる可能性があります。\n\nクールダウンが完了すると、通常どおり動作します。"</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"タブレットは自動的にクールダウンを行います。その間もタブレットを使用できますが、動作が遅くなる可能性があります。\n\nクールダウンが完了すると、通常どおり動作します。"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"指紋認証センサーは電源ボタンに内蔵されています。タブレット側面のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"指紋認証センサーは電源ボタンに内蔵されています。デバイス側面のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"指紋認証センサーは電源ボタンに内蔵されています。スマートフォン側面のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"指紋認証センサーは電源ボタンに内蔵されています。タブレット縁のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"指紋認証センサーは電源ボタンに内蔵されています。デバイス縁のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"指紋認証センサーは電源ボタンに内蔵されています。スマートフォン縁のボタンのうち、音量ボタンの横にあるフラットなボタンです。"</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"スマートフォンのロックを解除してその他のオプションを表示する"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"タブレットのロックを解除してその他のオプションを表示する"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"デバイスのロックを解除してその他のオプションを表示する"</string>
diff --git a/packages/SystemUI/res-product/values-ko/strings.xml b/packages/SystemUI/res-product/values-ko/strings.xml
index cb4a620..c894120 100644
--- a/packages/SystemUI/res-product/values-ko/strings.xml
+++ b/packages/SystemUI/res-product/values-ko/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"휴대전화가 자동으로 온도를 낮추려고 시도합니다. 휴대전화를 계속 사용할 수는 있지만 작동이 느려질 수도 있습니다.\n\n휴대전화 온도가 낮아지면 정상적으로 작동됩니다."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"기기가 자동으로 온도를 낮추려고 시도합니다. 기기를 계속 사용할 수는 있지만 작동이 느려질 수도 있습니다.\n\n기기 온도가 낮아지면 정상적으로 작동됩니다."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"태블릿이 자동으로 온도를 낮추려고 시도합니다. 태블릿을 계속 사용할 수 있지만 작동이 느려질 수도 있습니다.\n\n태블릿 온도가 낮아지면 정상적으로 작동합니다."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"지문 센서는 전원 버튼에 있습니다. 태블릿 옆면에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"지문 센서는 전원 버튼에 있습니다. 기기 옆면에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"지문 센서는 전원 버튼에 있습니다. 휴대전화 옆면에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"지문 센서는 전원 버튼에 있습니다. 태블릿 테두리에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"지문 센서는 전원 버튼에 있습니다. 기기 테두리에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"지문 센서는 전원 버튼에 있습니다. 휴대전화 테두리에 있는 튀어나온 볼륨 버튼 옆의 평평한 버튼입니다."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"더 많은 옵션을 확인하려면 휴대전화를 잠금 해제하세요."</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"더 많은 옵션을 확인하려면 태블릿을 잠금 해제하세요."</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"더 많은 옵션을 확인하려면 기기를 잠금 해제하세요."</string>
diff --git a/packages/SystemUI/res-product/values-ky/strings.xml b/packages/SystemUI/res-product/values-ky/strings.xml
index 8bd066f0..11e7f6f 100644
--- a/packages/SystemUI/res-product/values-ky/strings.xml
+++ b/packages/SystemUI/res-product/values-ky/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Телефонуңуз автоматтык түрдө сууйт. Аны колдоно берсеңиз болот, бирок ал жайыраак иштеп калат.\n\nТелефонуңуз суугандан кийин адаттагыдай эле иштеп баштайт."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Түзмөгүңүз автоматтык түрдө сууйт. Аны колдоно берсеңиз болот, бирок ал жайыраак иштеп калат.\n\nТүзмөгүңүз суугандан кийин адаттагыдай эле иштеп баштайт."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Планшетиңиз автоматтык түрдө сууйт. Аны колдоно берсеңиз болот, бирок ал жайыраак иштеп калат.\n\nПланшетиңиз суугандан кийин адаттагыдай эле иштеп баштайт."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Манжа изинин сенсору кубат баскычында жайгашкан. Бул планшеттин четиндеги үндү катуулатуу/акырындатуу баскычынын (көтөрүлгөн) жанындагы жалпак баскыч."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Манжа изинин сенсору кубат баскычында жайгашкан. Бул планшеттин кырындагы үндү катуулатуу/акырындатуу баскычынын (көтөрүлгөн) жанындагы жалпак баскыч."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Манжа изинин сенсору кубат баскычында жайгашкан. Бул түзмөктүн четиндеги үндү катуулатуу/акырындатуу баскычынын (көтөрүлгөн) жанындагы жалпак баскыч."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Манжа изинин сенсору кубат баскычында жайгашкан. Бул телефондун четиндеги үндү катуулатуу/акырындатуу баскычынын (көтөрүлгөн) жанындагы жалпак баскыч."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Манжа изинин сенсору кубат баскычында жайгашкан. Бул телефондун кырындагы үндү катуулатуу/акырындатуу баскычынын (көтөрүлгөн) жанындагы жалпак баскыч."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Дагы башка параметрлерди көрүү үчүн телефонуңуздун кулпусун ачыңыз"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Дагы башка параметрлерди көрүү үчүн планшетиңиздин кулпусун ачыңыз"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Дагы башка параметрлерди көрүү үчүн түзмөгүңүздүн кулпусун ачыңыз"</string>
diff --git a/packages/SystemUI/res-product/values-ml/strings.xml b/packages/SystemUI/res-product/values-ml/strings.xml
index 55cfd06..0fc494c 100644
--- a/packages/SystemUI/res-product/values-ml/strings.xml
+++ b/packages/SystemUI/res-product/values-ml/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"നിങ്ങളുടെ ഫോൺ സ്വയമേവ തണുക്കാൻ ശ്രമിക്കും. നിങ്ങൾക്ക് അപ്പോഴും ഫോൺ ഉപയോഗിക്കാമെങ്കിലും അതിന്റെ പ്രവർത്തനം മന്ദഗതിയിലായിരിക്കാം.\n\nതണുത്തുകഴിഞ്ഞാൽ ഫോൺ സാധാരണപോലെ പ്രവർത്തിക്കും."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"നിങ്ങളുടെ ഉപകരണം സ്വയമേവ തണുക്കാൻ ശ്രമിക്കും. നിങ്ങൾക്ക് അപ്പോഴും ഉപകരണം ഉപയോഗിക്കാമെങ്കിലും അതിന്റെ പ്രവർത്തനം മന്ദഗതിയിലായിരിക്കാം.\n\nതണുത്തുകഴിഞ്ഞാൽ ഉപകരണം സാധാരണപോലെ പ്രവർത്തിക്കും."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"നിങ്ങളുടെ ടാബ്‌ലെറ്റ് സ്വയമേവ തണുക്കാൻ ശ്രമിക്കും. നിങ്ങൾക്ക് അപ്പോഴും ടാബ്‌ലെറ്റ് ഉപയോഗിക്കാമെങ്കിലും അതിന്റെ പ്രവർത്തനം മന്ദഗതിയിലായിരിക്കാം.\n\nതണുത്തുകഴിഞ്ഞാൽ ടാബ്‌ലെറ്റ് സാധാരണപോലെ പ്രവർത്തിക്കും."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ടാബ്‌ലെറ്റിന്റെ അറ്റത്ത് ഉയർന്ന് നിൽക്കുന്ന ശബ്ദ ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ഉപകരണത്തിന്റെ അറ്റത്ത് ഉയർന്ന് നിൽക്കുന്ന ശബ്ദ ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ഫോണിന്റെ അറ്റത്ത് ഉയർന്ന് നിൽക്കുന്ന ശബ്ദ ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ടാബ്‌ലെറ്റിന്റെ അരികിൽ ഉയർന്ന് നിൽക്കുന്ന വോളിയം ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ഉപകരണത്തിന്റെ അരികുഭാഗത്ത് ഉയർന്ന് നിൽക്കുന്ന വോളിയം ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"പവർ ബട്ടണിലാണ് ഫിംഗർപ്രിന്റ് സെൻസർ ഉള്ളത്. ഫോണിന്റെ അരികുഭാഗത്ത് ഉയർന്ന് നിൽക്കുന്ന വോളിയം ബട്ടണിന്റെ അടുത്തുള്ള പരന്ന ബട്ടൺ ആണ് ഇത്."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് നിങ്ങളുടെ ഫോൺ അൺലോക്ക് ചെയ്യുക"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് നിങ്ങളുടെ ടാബ്‌ലെറ്റ് അൺലോക്ക് ചെയ്യുക"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് നിങ്ങളുടെ ഉപകരണം അൺലോക്ക് ചെയ്യുക"</string>
diff --git a/packages/SystemUI/res-product/values-nb/strings.xml b/packages/SystemUI/res-product/values-nb/strings.xml
index 533a9b8..aaa0a03 100644
--- a/packages/SystemUI/res-product/values-nb/strings.xml
+++ b/packages/SystemUI/res-product/values-nb/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Telefonen prøver automatisk å kjøle seg ned. Du kan fremdeles bruke den, men den kjører muligens saktere.\n\nNår telefonen har kjølt seg ned, kjører den som normalt."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Enheten prøver automatisk å kjøle seg ned. Du kan fremdeles bruke den, men den kjører muligens saktere.\n\nNår enheten har kjølt seg ned, kjører den som normalt."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Nettbrettet prøver automatisk å kjøle seg ned. Du kan fremdeles bruke det, men det kjører muligens saktere.\n\nNår nettbrettet har kjølt seg ned, kjører det som normalt."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Fingeravtrykkssensoren er på av/på-knappen. Det er den flate knappen ved siden av den hevede volumknappen på siden av nettbrettet."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Fingeravtrykkssensoren er på av/på-knappen. Det er den flate knappen ved siden av den hevede volumknappen på nettbrettets kant."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Fingeravtrykkssensoren er på av/på-knappen. Det er den flate knappen ved siden av den hevede volumknappen på siden av enheten."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Fingeravtrykkssensoren er på av/på-knappen. Det er den flate knappen ved siden av den hevede volumknappen på siden av telefonen."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Fingeravtrykkssensoren er på av/på-knappen. Det er den flate knappen ved siden av den hevede volumknappen på telefonens kant."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Lås opp telefonen din for å få flere alternativer"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Lås opp nettbrettet ditt for å få flere alternativer"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Lås opp enheten din for å få flere alternativer"</string>
diff --git a/packages/SystemUI/res-product/values-nl/strings.xml b/packages/SystemUI/res-product/values-nl/strings.xml
index abdc7ee..f4b6eea 100644
--- a/packages/SystemUI/res-product/values-nl/strings.xml
+++ b/packages/SystemUI/res-product/values-nl/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Je telefoon probeert automatisch af te koelen. Je kunt je telefoon nog steeds gebruiken, maar deze kan langzamer werken.\n\nZodra de telefoon is afgekoeld, werkt deze weer normaal."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Je apparaat probeert automatisch af te koelen. Je kunt je apparaat nog steeds gebruiken, maar het kan langzamer werken.\n\nZodra het apparaat is afgekoeld, werkt het weer normaal."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Je tablet probeert automatisch af te koelen. Je kunt je tablet nog steeds gebruiken, maar deze kan langzamer werken.\n\nZodra de tablet is afgekoeld, werkt deze weer normaal."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de zijkant van de tablet."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de zijkant van het apparaat."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de zijkant van de telefoon."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de rand van de tablet."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de rand van het apparaat."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Je vindt de vingerafdruksensor op de aan/uit-knop. Het is de platte knop naast de verhoogde volumeknop aan de rand van de telefoon."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Ontgrendel je telefoon voor meer opties"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Ontgrendel je tablet voor meer opties"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Ontgrendel je apparaat voor meer opties"</string>
diff --git a/packages/SystemUI/res-product/values-pl/strings.xml b/packages/SystemUI/res-product/values-pl/strings.xml
index 286a242..4e5ad3f 100644
--- a/packages/SystemUI/res-product/values-pl/strings.xml
+++ b/packages/SystemUI/res-product/values-pl/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Telefon automatycznie podejmie próbę obniżenia temperatury. Możesz go wciąż używać, ale może działać wolniej.\n\nGdy temperatura się obniży, telefon będzie działać normalnie."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Urządzenie automatycznie podejmie próbę obniżenia temperatury. Możesz go wciąż używać, ale może działać wolniej.\n\nGdy temperatura się obniży, urządzenie będzie działać normalnie."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Tablet automatycznie podejmie próbę obniżenia temperatury. Możesz go wciąż używać, ale może działać wolniej.\n\nGdy temperatura się obniży, tablet będzie działać normalnie."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na krawędzi tabletu."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na krawędzi urządzenia."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na krawędzi telefonu."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na jednej z krawędzi tabletu."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na jednej z krawędzi urządzenia."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Czytnik linii papilarnych znajduje się na przycisku zasilania. To płaski przycisk przy uniesionym przycisku głośności na jednej z krawędzi telefonu."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Odblokuj telefon, by wyświetlić więcej opcji"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Odblokuj tablet, by wyświetlić więcej opcji"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Odblokuj urządzenie, by wyświetlić więcej opcji"</string>
diff --git a/packages/SystemUI/res-product/values-sk/strings.xml b/packages/SystemUI/res-product/values-sk/strings.xml
index 8e1bb39..fbf5ee7 100644
--- a/packages/SystemUI/res-product/values-sk/strings.xml
+++ b/packages/SystemUI/res-product/values-sk/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Váš telefón sa automaticky pokúsi schladiť. Môžete ho naďalej používať, ale môže fungovať pomalšie.\n\nPo poklese teploty bude fungovať ako obvykle."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Vaše zariadenie sa automaticky pokúsi schladiť. Môžete ho naďalej používať, ale môže fungovať pomalšie.\n\nPo poklese teploty bude fungovať ako obvykle."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Váš tablet sa automaticky pokúsi schladiť. Môžete ho naďalej používať, ale môže fungovať pomalšie.\n\nPo poklese teploty bude fungovať ako obvykle."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na okraji tabletu."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na okraji zariadenia."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na okraji telefónu."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na boku tabletu."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na boku zariadenia."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Senzor odtlačkov prstov je na vypínači. Je to ploché tlačidlo vedľa vypuklého tlačidla hlasitosti na boku telefónu."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Ak chcete zobraziť ďalšie možnosti, odomknite telefón"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Ak chcete zobraziť ďalšie možnosti, odomknite tablet"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Ak chcete zobraziť ďalšie možnosti, odomknite zariadenie"</string>
diff --git a/packages/SystemUI/res-product/values-sq/strings.xml b/packages/SystemUI/res-product/values-sq/strings.xml
index f7421c1..c0e93c4 100644
--- a/packages/SystemUI/res-product/values-sq/strings.xml
+++ b/packages/SystemUI/res-product/values-sq/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Telefoni yt do të përpiqet automatikisht të ftohet. Mund të vazhdosh ta përdorësh telefonin, por ai mund të funksionojë më ngadalë.\n\nPasi telefoni të jetë ftohur, ai do të funksionojë normalisht."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Pajisja jote do të përpiqet automatikisht të ftohet. Mund të vazhdosh ta përdorësh pajisjen, por ajo mund të funksionojë më ngadalë.\n\nPasi pajisja të jetë ftohur, ajo do të funksionojë normalisht."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Tableti yt do të përpiqet automatikisht të ftohet. Mund të vazhdosh ta përdorësh tabletin, por ai mund të funksionojë më ngadalë.\n\nPasi tableti të jetë ftohur, ai do të funksionojë normalisht."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në anë të tabletit."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në anë të pajisjes."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në anë të telefonit."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në skaj të tabletit."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në skaj të pajisjes."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Sensori i gjurmës së gishtit është në butonin e energjisë. Ai është butoni i rrafshët pranë butonit të ngritur të volumit në skaj të telefonit."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Shkyçe telefonin për më shumë opsione"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Shkyçe tabletin për më shumë opsione"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Shkyçe pajisjen për më shumë opsione"</string>
diff --git a/packages/SystemUI/res-product/values-uk/strings.xml b/packages/SystemUI/res-product/values-uk/strings.xml
index e0aff9e8..24d5cc9 100644
--- a/packages/SystemUI/res-product/values-uk/strings.xml
+++ b/packages/SystemUI/res-product/values-uk/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Ваш телефон автоматично спробує охолодитися. Ви можете й далі користуватися ним, але він може працювати повільніше.\n\nКоли телефон охолоне, він знову працюватиме як зазвичай."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Ваш пристрій автоматично спробує охолодитися. Ви можете й далі користуватися ним, але він може працювати повільніше.\n\nКоли пристрій охолоне, він знову працюватиме як зазвичай."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Ваш планшет автоматично спробує охолодитися. Ви можете й далі користуватися ним, але він може працювати повільніше.\n\nКоли планшет охолоне, він знову працюватиме як зазвичай."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній крайці планшета."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній крайці пристрою."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній крайці телефона."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній поверхні планшета."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній поверхні пристрою."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Сканер відбитків пальців розташовано на кнопці живлення. Це плоска кнопка поруч із випуклою кнопкою гучності на бічній поверхні телефона."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Розблокуйте телефон, щоб переглянути інші параметри"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Розблокуйте планшет, щоб переглянути інші параметри"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Розблокуйте пристрій, щоб переглянути інші параметри"</string>
diff --git a/packages/SystemUI/res-product/values-zu/strings.xml b/packages/SystemUI/res-product/values-zu/strings.xml
index 89d4264..6374f1f 100644
--- a/packages/SystemUI/res-product/values-zu/strings.xml
+++ b/packages/SystemUI/res-product/values-zu/strings.xml
@@ -58,9 +58,9 @@
     <string name="high_temp_dialog_message" product="default" msgid="4272882413847595625">"Ifoni yakho izozama ngokuzenzakalela ukuphola. Ungasasebenzisa ifoni yakho, kodwa ingasebenza ngokungasheshi.\n\nUma ifoni yakho isipholile, izosebenza ngokuvamile."</string>
     <string name="high_temp_dialog_message" product="device" msgid="263861943935989046">"Idivayisi yakho izozama ukupholisa ngokuzenzekelayo. Usengasebenzisa idivayisi yakho, kodwa ingase isebenze ngokunensayo.\n\nLapho idivayisi yakho isipholile, izosebenza ngokuvamile."</string>
     <string name="high_temp_dialog_message" product="tablet" msgid="5613713326841935537">"Ithebulethi yakho izozama ukupholisa. Usengasebenzisa ithebulethi yakho, kodwa ingase isebenze ngokunensayo.\n\nLapho ithebulethi yakho isipholile, izosebenza ngokuvamile."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Inzwa yesigxivizo somunwe esenkinobhweni yamandla. Inkinobho eyisicaba eduze kwenkinobho yevolumu ephakanyisiwe emaphethelweni wethebulethi."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet" msgid="3726972508570143945">"Inzwa yesigxivizo somunwe esenkinobhweni yamandla. Inkinobho eyisicaba eduze kwenkinobho yevolumu ephakanyisiwe onqenqemeni lwethebulethi."</string>
     <string name="security_settings_sfps_enroll_find_sensor_message" product="device" msgid="2929467060295094725">"Inzwa yesigxivizo somunwe esenkinobhweni yamandla. Inkinobho eyisicaba eduze kwenkinobho yevolumu ephakanyisiwe emaphethelweni edivayisi."</string>
-    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Inzwa yesigxivizo somunwe esenkinobhweni yamandla. Inkinobho eyisicaba eduze kwenkinobho yevolumu ephakanyisiwe emaphethelweni efoni."</string>
+    <string name="security_settings_sfps_enroll_find_sensor_message" product="default" msgid="8582726566542997639">"Inzwa yesigxivizo somunwe esenkinobhweni yamandla. Inkinobho eyisicaba eduze kwenkinobho yevolumu eliphakanyisiwe onqenqemeni lwefoni."</string>
     <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Vula ifoni yakho ukuthola okunye okungakhethwa"</string>
     <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Vula ithebulethi yakho ukuthola okunye okungakhethwa"</string>
     <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Vula idivayisi yakho ukuthola okunye okungakhethwa"</string>
diff --git a/packages/SystemUI/res/color/audio_sharing_btn_text_color.xml b/packages/SystemUI/res/color/audio_sharing_btn_text_color.xml
new file mode 100644
index 0000000..b72835e
--- /dev/null
+++ b/packages/SystemUI/res/color/audio_sharing_btn_text_color.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:state_activated="true"
+        android:color="@color/qs_dialog_btn_filled_text_color" />
+    <item android:color="@color/qs_dialog_btn_outline_text" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/brightness_slider_track.xml b/packages/SystemUI/res/color/brightness_slider_track.xml
deleted file mode 100644
index 6028769..0000000
--- a/packages/SystemUI/res/color/brightness_slider_track.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
-  -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@android:color/system_neutral2_500" android:lStar="40" />
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/arrow_pointing_down.xml b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
index be39683..ca573c7 100644
--- a/packages/SystemUI/res/drawable/arrow_pointing_down.xml
+++ b/packages/SystemUI/res/drawable/arrow_pointing_down.xml
@@ -19,7 +19,7 @@
     android:height="24dp"
     android:viewportWidth="24.0"
     android:viewportHeight="24.0"
-    android:tint="?attr/colorControlNormal">
+    android:tint="?android:attr/textColorPrimary">
     <path
         android:fillColor="@android:color/white"
         android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
diff --git a/packages/SystemUI/res/drawable/audio_sharing_btn_background.xml b/packages/SystemUI/res/drawable/audio_sharing_btn_background.xml
new file mode 100644
index 0000000..310e095
--- /dev/null
+++ b/packages/SystemUI/res/drawable/audio_sharing_btn_background.xml
@@ -0,0 +1,21 @@
+<?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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:drawable="@drawable/qs_dialog_btn_filled" />
+    <item android:drawable="@drawable/qs_dialog_btn_outline" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_bar.xml b/packages/SystemUI/res/drawable/brightness_bar.xml
index 2afe164..3d1c1fb 100644
--- a/packages/SystemUI/res/drawable/brightness_bar.xml
+++ b/packages/SystemUI/res/drawable/brightness_bar.xml
@@ -21,7 +21,7 @@
         android:viewportHeight="48">
 <path
     android:pathData="M2,22L302,22A2,2 0,0 1,304 24L304,24A2,2 0,0 1,302 26L2,26A2,2 0,0 1,0 24L0,24A2,2 0,0 1,2 22z"
-    android:fillColor="@color/brightness_slider_track"/>
+    android:fillColor="?androidprv:attr/customColorShadeInactive"/>
 <path
     android:pathData="M24,0L205.71,0A24,24 0,0 1,229.71 24L229.71,24A24,24 0,0 1,205.71 48L24,48A24,24 0,0 1,0 24L0,24A24,24 0,0 1,24 0z"
     android:fillColor="?attr/shadeActive"/>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index cae9d6b..ec15b10 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -15,6 +15,7 @@
   ~ limitations under the License.
   -->
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
             android:paddingMode="stack" >
     <item android:id="@android:id/background"
         android:gravity="center_vertical|fill_horizontal">
@@ -24,7 +25,7 @@
             <shape>
                 <size android:height="@dimen/rounded_slider_track_width" />
                 <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
-                <solid android:color="@color/brightness_slider_track" />
+                <solid android:color="?androidprv:attr/customColorShadeInactive" />
             </shape>
         </inset>
     </item>
diff --git a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
index e06bfdc..368fe82 100644
--- a/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_button_bar_systemui.xml
@@ -52,7 +52,7 @@
         <Button
             android:id="@android:id/button1"
             style="?android:attr/buttonBarPositiveButtonStyle"
-            android:layout_marginStart="8dp"
+            android:layout_marginStart="@dimen/dialog_button_side_margin"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" />
     </com.android.internal.widget.ButtonBarLayout>
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index d7b94ec..7b7c96cb 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -82,6 +82,23 @@
         app:layout_constraintStart_toEndOf="@id/backlinks_include_data"
         app:layout_constraintTop_toTopOf="parent" />
 
+    <TextView
+        android:id="@+id/backlinks_cross_profile_error"
+        android:layout_width="wrap_content"
+        android:layout_height="48dp"
+        android:layout_marginStart="8dp"
+        android:drawablePadding="4dp"
+        android:drawableStart="@drawable/ic_info_outline"
+        android:drawableTint="?androidprv:attr/materialColorOnBackground"
+        android:gravity="center"
+        android:paddingHorizontal="8dp"
+        android:text="@string/backlinks_cross_profile_error"
+        android:textColor="?androidprv:attr/materialColorOnBackground"
+        android:visibility="gone"
+        app:layout_constraintBottom_toTopOf="@id/preview"
+        app:layout_constraintStart_toEndOf="@id/backlinks_data"
+        app:layout_constraintTop_toTopOf="parent" />
+
     <ImageView
         android:id="@+id/preview"
         android:layout_width="0px"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
deleted file mode 100644
index ff89ed9..0000000
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ /dev/null
@@ -1,208 +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.
-  -->
-<com.android.systemui.biometrics.ui.BiometricPromptLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/contents"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <ImageView
-        android:id="@+id/logo"
-        android:layout_width="@dimen/biometric_auth_icon_size"
-        android:layout_height="@dimen/biometric_auth_icon_size"
-        android:layout_gravity="center"
-        android:scaleType="fitXY"/>
-
-    <TextView
-        android:id="@+id/logo_description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="@integer/biometric_dialog_text_gravity"
-        android:singleLine="true"
-        android:marqueeRepeatLimit="1"
-        android:ellipsize="marquee"/>
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="@integer/biometric_dialog_text_gravity"
-        android:singleLine="true"
-        android:marqueeRepeatLimit="1"
-        android:ellipsize="marquee"
-        style="@style/TextAppearance.AuthCredential.OldTitle"/>
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="@integer/biometric_dialog_text_gravity"
-        android:singleLine="true"
-        android:marqueeRepeatLimit="1"
-        android:ellipsize="marquee"
-        style="@style/TextAppearance.AuthCredential.OldSubtitle"/>
-
-    <TextView
-        android:id="@+id/description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="@integer/biometric_dialog_text_gravity"
-        android:scrollbars ="vertical"
-        android:importantForAccessibility="no"
-        style="@style/TextAppearance.AuthCredential.OldDescription"/>
-
-    <Space
-        android:id="@+id/space_above_content"
-        android:layout_width="match_parent"
-        android:layout_height="24dp"
-        android:visibility="gone" />
-
-    <LinearLayout
-        android:id="@+id/customized_view_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:fadeScrollbars="false"
-        android:gravity="center_vertical"
-        android:orientation="vertical"
-        android:scrollbars="vertical"
-        android:visibility="gone" />
-
-    <Space android:id="@+id/space_above_icon"
-        android:layout_width="match_parent"
-        android:layout_height="48dp" />
-
-    <FrameLayout
-        android:id="@+id/biometric_icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center">
-
-        <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
-            android:id="@+id/biometric_icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:contentDescription="@null"
-            android:scaleType="fitXY" />
-
-        <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
-            android:id="@+id/biometric_icon_overlay"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:contentDescription="@null"
-            android:scaleType="fitXY" />
-    </FrameLayout>
-
-    <!-- For sensors such as UDFPS, this view is used during custom measurement/layout to add extra
-         padding so that the biometric icon is always in the right physical position. -->
-    <Space android:id="@+id/space_below_icon"
-        android:layout_width="match_parent"
-        android:layout_height="12dp" />
-
-    <TextView
-        android:id="@+id/indicator"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingHorizontal="24dp"
-        android:textSize="12sp"
-        android:gravity="center_horizontal"
-        android:accessibilityLiveRegion="polite"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:marqueeRepeatLimit="marquee_forever"
-        android:scrollHorizontally="true"
-        android:fadingEdge="horizontal"
-        android:textColor="@color/biometric_dialog_gray"/>
-
-    <LinearLayout
-        android:id="@+id/button_bar"
-        android:layout_width="match_parent"
-        android:layout_height="88dp"
-        style="?android:attr/buttonBarStyle"
-        android:orientation="horizontal"
-        android:paddingTop="24dp">
-
-        <Space android:id="@+id/leftSpacer"
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:visibility="visible" />
-
-        <!-- Negative Button, reserved for app -->
-        <Button android:id="@+id/button_negative"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-            android:layout_gravity="center_vertical"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
-            android:visibility="gone"/>
-        <!-- Cancel Button, replaces negative button when biometric is accepted -->
-        <Button android:id="@+id/button_cancel"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-            android:layout_gravity="center_vertical"
-            android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
-            android:text="@string/cancel"
-            android:visibility="gone"/>
-        <!-- "Use Credential" Button, replaces if device credential is allowed -->
-        <Button android:id="@+id/button_use_credential"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
-            android:layout_gravity="center_vertical"
-            android:maxWidth="@dimen/biometric_dialog_button_negative_max_width"
-            android:visibility="gone"/>
-
-        <Space android:id="@+id/middleSpacer"
-            android:layout_width="0dp"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:visibility="visible"/>
-
-        <!-- Positive Button -->
-        <Button android:id="@+id/button_confirm"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@*android:style/Widget.DeviceDefault.Button.Colored"
-            android:layout_gravity="center_vertical"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:maxWidth="@dimen/biometric_dialog_button_positive_max_width"
-            android:text="@string/biometric_dialog_confirm"
-            android:visibility="gone"/>
-        <!-- Try Again Button -->
-        <Button android:id="@+id/button_try_again"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            style="@*android:style/Widget.DeviceDefault.Button.Colored"
-            android:layout_gravity="center_vertical"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:maxWidth="@dimen/biometric_dialog_button_positive_max_width"
-            android:text="@string/biometric_dialog_try_again"
-            android:visibility="gone"/>
-
-        <Space android:id="@+id/rightSpacer"
-            android:layout_width="8dp"
-            android:layout_height="match_parent"
-            android:visibility="visible" />
-    </LinearLayout>
-
-</com.android.systemui.biometrics.ui.BiometricPromptLayout>
diff --git a/packages/SystemUI/res/layout/bluetooth_device_item.xml b/packages/SystemUI/res/layout/bluetooth_device_item.xml
index 4535f67..b9314c7 100644
--- a/packages/SystemUI/res/layout/bluetooth_device_item.xml
+++ b/packages/SystemUI/res/layout/bluetooth_device_item.xml
@@ -41,6 +41,7 @@
         android:textDirection="locale"
         android:textAlignment="gravity"
         android:paddingStart="20dp"
+        android:paddingEnd="10dp"
         android:paddingTop="15dp"
         android:maxLines="1"
         android:ellipsize="end"
@@ -56,6 +57,7 @@
         android:id="@+id/bluetooth_device_summary"
         style="@style/BluetoothTileDialog.DeviceSummary"
         android:paddingStart="20dp"
+        android:paddingEnd="10dp"
         android:paddingBottom="15dp"
         android:maxLines="1"
         android:ellipsize="end"
@@ -73,11 +75,20 @@
         android:orientation="vertical"/>
 
     <View
+        android:id="@+id/divider"
+        android:layout_width="1dp"
+        android:layout_height="38dp"
+        app:layout_constraintStart_toEndOf="@+id/guideline"
+        app:layout_constraintEnd_toStartOf="@+id/gear_icon"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent" />
+
+    <View
         android:id="@+id/gear_icon"
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:contentDescription="@string/accessibility_bluetooth_device_settings_gear"
-        app:layout_constraintStart_toEndOf="@+id/guideline"
+        app:layout_constraintStart_toEndOf="@+id/divider"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent" />
diff --git a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
index 27b8006..b4eaa40 100644
--- a/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/bluetooth_tile_dialog.xml
@@ -258,7 +258,7 @@
 
             <Button
                 android:id="@+id/audio_sharing_button"
-                style="@style/Widget.Dialog.Button.BorderButton"
+                style="@style/BluetoothTileDialog.AudioSharingButton"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="9dp"
@@ -270,7 +270,6 @@
                 android:text="@string/quick_settings_bluetooth_audio_sharing_button"
                 android:drawableStart="@drawable/ic_bt_le_audio_sharing_18dp"
                 android:drawablePadding="10dp"
-                android:drawableTint="?android:attr/textColorPrimary"
                 app:layout_constrainedWidth="true"
                 app:layout_constraintHorizontal_bias="0"
                 app:layout_constraintEnd_toStartOf="@+id/done_button"
diff --git a/packages/SystemUI/res/layout/media_projection_app_selector.xml b/packages/SystemUI/res/layout/media_projection_app_selector.xml
index b77f78d..3b6a64f 100644
--- a/packages/SystemUI/res/layout/media_projection_app_selector.xml
+++ b/packages/SystemUI/res/layout/media_projection_app_selector.xml
@@ -37,6 +37,7 @@
         android:background="@*android:drawable/bottomsheet_background">
 
         <ImageView
+            android:id="@+id/media_projection_app_selector_icon"
             android:layout_width="@dimen/media_projection_app_selector_icon_size"
             android:layout_height="@dimen/media_projection_app_selector_icon_size"
             android:layout_marginTop="@*android:dimen/chooser_edge_margin_normal"
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
new file mode 100644
index 0000000..59cfecc
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/status_bar_latest_event_content"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:minHeight="@*android:dimen/notification_headerless_min_height"
+    android:tag="enroute"
+    >
+
+    <include layout="@*android:layout/notification_template_material_base" />
+
+</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 154397d..690a89a 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -17,7 +17,6 @@
      the chip. -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/ongoing_activity_chip"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:layout_gravity="center_vertical|start"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index aa083ad..0533c7e 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -64,30 +64,27 @@
             android:layout_height="wrap_content"
             android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
             style="@style/TextAppearance.Dialog.Body.Message"
-            android:gravity="start"/>
+            android:gravity="start"
+            android:textAlignment="gravity"/>
 
         <!-- Buttons -->
         <com.android.internal.widget.ButtonBarLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:layout_marginTop="@dimen/screenrecord_buttons_margin_top">
+            android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"
+            android:gravity="end">
             <Button
                 android:id="@android:id/button2"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_weight="0"
                 android:text="@string/cancel"
                 style="@style/Widget.Dialog.Button.BorderButton" />
-            <Space
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"/>
             <Button
                 android:id="@android:id/button1"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_weight="0"
+                android:layout_marginStart="@dimen/dialog_button_side_margin"
                 android:text="@string/screenrecord_continue"
                 style="@style/Widget.Dialog.Button" />
         </com.android.internal.widget.ButtonBarLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 4247c7e..541aebe 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -66,7 +66,7 @@
             <FrameLayout
                 android:id="@+id/status_bar_start_side_content"
                 android:layout_width="wrap_content"
-                android:layout_height="match_parent"
+                android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical|start"
                 android:clipChildren="false">
 
@@ -99,7 +99,10 @@
                         android:gravity="center_vertical|start"
                     />
 
-                    <include layout="@layout/ongoing_activity_chip" />
+                    <include layout="@layout/ongoing_activity_chip"
+                        android:id="@+id/ongoing_activity_chip_primary"/>
+
+                    <!-- TODO(b/364653005): Add a second activity chip. -->
 
                     <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
                         android:id="@+id/notification_icon_area"
diff --git a/packages/SystemUI/res/raw/action_key_edu.json b/packages/SystemUI/res/raw/action_key_edu.json
new file mode 100644
index 0000000..014d837
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_edu.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":181,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-EDU","ddd":0,"assets":[{"id":"comp_0","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":34,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":46,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.33],"y":[0.52]},"t":124,"s":[0]},{"i":{"x":[0.64],"y":[1]},"o":{"x":[0.36],"y":[0]},"t":127,"s":[100]},{"i":{"x":[0.64],"y":[0.48]},"o":{"x":[0.36],"y":[0]},"t":130,"s":[100]},{"t":136,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.072,"y":0.635},"o":{"x":0.424,"y":0.112},"t":27,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":39,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":57,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.07,"y":0.63},"o":{"x":0.42,"y":0.11},"t":117,"s":[40,39.79,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":1},"o":{"x":0.313,"y":0.131},"t":129,"s":[40,49.21,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[40,39.79,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"AllApps_Tray_themed","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-8.859,0]},"a":{"a":0,"k":[277,256.562,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,330.391]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 4 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,281.172]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 3 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,231.953]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 2 App - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Safety app","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[400.047,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Settings","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[350.828,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"SoundCloud","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[301.609,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Starbucks","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252.391,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Snapchat","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[203.172,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[29.531,29.531]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Spotify","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[153.953,182.734]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Row 1 App - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-129.938,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[275.625,25.594]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"560x52","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":4,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-151.594,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[15.75,1.969]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":118.125},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"handle","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.1,"y":1},"o":{"x":0.05,"y":0.7},"t":45,"s":[277,516,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.214,"y":0.214},"t":75,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":1},"o":{"x":0.3,"y":0},"t":135,"s":[277,265.422,0],"to":[0,0,0],"ti":[0,0,0]},{"t":147,"s":[277,516,0]}]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[316.969,320.906]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":13.78},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"all apps","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[50]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[50]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":51,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[70],"t":135,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[100],"t":144,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"BlankButton","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"actionKey_themed","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"matte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.32549020648,0.270588248968,0.164705887437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"AllApps_Tray_themed","tt":1,"tp":5,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,282,0]},"a":{"a":0,"k":[277,282,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":554,"h":564,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":451,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":180,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":451,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ No newline at end of file
diff --git a/packages/SystemUI/res/raw/action_key_success.json b/packages/SystemUI/res/raw/action_key_success.json
new file mode 100644
index 0000000..cae7344
--- /dev/null
+++ b/packages/SystemUI/res/raw/action_key_success.json
@@ -0,0 +1 @@
+{"v":"5.12.1","fr":60,"ip":0,"op":50,"w":554,"h":564,"nm":"Trackpad-JSON_ActionKey-Success","ddd":0,"assets":[{"id":"comp_0","nm":"TrackpadAK_Success_Checkmark","fr":60,"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"Check Rotate","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":2,"s":[-16]},{"t":20,"s":[6]}]},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[95.049,95.049,100]}},"ao":0,"ip":0,"op":228,"st":-72,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"Bounce","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.12],"y":[1]},"o":{"x":[0.44],"y":[0]},"t":12,"s":[0]},{"t":36,"s":[-6]}]},"p":{"a":0,"k":[81,127,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.263,0.263,0.833],"y":[1.126,1.126,1]},"o":{"x":[0.05,0.05,0.05],"y":[0.958,0.958,0]},"t":1,"s":[80,80,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.45,0.45,0.167],"y":[0.325,0.325,0]},"t":20,"s":[105,105,100]},{"t":36,"s":[100,100,100]}]}},"ao":0,"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","parent":1,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":-0.289},"p":{"a":0,"k":[14.364,-33.591,0]},"a":{"a":0,"k":[-0.125,0,0]},"s":{"a":0,"k":[104.744,104.744,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-1.401,-0.007],[-10.033,11.235]],"o":[[5.954,7.288],[1.401,0.007],[0,0]],"v":[[-28.591,4.149],[-10.73,26.013],[31.482,-21.255]],"c":false}},"nm":"Path 1","hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.22],"y":[1]},"o":{"x":[0.001],"y":[0.149]},"t":10,"s":[29]},{"t":27,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":11},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false}],"ip":5,"op":44,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[95,95,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.275,0.275,0.21],"y":[1.102,1.102,1]},"o":{"x":[0.037,0.037,0.05],"y":[0.476,0.476,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.252,0.252,0.47],"y":[0.159,0.159,0]},"t":16,"s":[120,120,100]},{"t":28,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.1,0.1],"y":[1,1]},"o":{"x":[0.32,0.32],"y":[0.11,0.11]},"t":16,"s":[148,148]},{"t":28,"s":[136,136]}]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":88},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Checkbox - Widget","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_1","nm":"actionKey_themed-static","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","parent":3,"sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[0.288,-0.035,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.579,-0.158],[0.605,0],[1.21,1.21],[0,1.684],[-1.184,1.184],[-1.684,0],[-1.21,-1.21],[0,-1.71],[0.184,-0.553],[0.316,-0.474],[0,0],[0,0]],"o":[[-0.474,0.316],[-0.553,0.158],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.184],[0,0.605],[-0.158,0.553],[0,0],[0,0],[0,0]],"v":[[10.241,12.155],[8.663,12.866],[6.926,13.103],[2.585,11.287],[0.809,6.946],[2.585,2.605],[6.926,0.789],[11.307,2.605],[13.122,6.946],[12.846,8.682],[12.136,10.222],[16.911,14.997],[15.017,16.891]],"c":true}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.184],[-1.684,0],[-1.184,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.736],[1.21,-1.21],[1.736,0],[1.21,1.184],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,11.327],[-16.911,6.985],[-15.096,2.605],[-10.754,0.789],[-6.374,2.605],[-4.558,6.985],[-6.374,11.327],[-10.754,13.142]],"c":true}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[-8.268,9.432],[-7.242,6.985],[-8.268,4.499],[-10.754,3.473],[-13.201,4.499],[-14.188,6.985],[-13.201,9.432],[-10.754,10.419]],"c":true}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.658],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.658],[0.973,0]],"v":[[9.413,9.432],[10.439,6.985],[9.413,4.499],[6.926,3.473],[4.479,4.499],[3.453,6.985],[4.479,9.432],[6.926,10.419]],"c":true}},"nm":"Path 4","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.184,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.736,0],[1.21,1.21],[0,1.684],[-1.184,1.21],[-1.684,0]],"v":[[-15.096,-6.354],[-16.911,-10.695],[-15.096,-15.076],[-10.754,-16.891],[-6.374,-15.076],[-4.558,-10.695],[-6.374,-6.354],[-10.754,-4.539]],"c":true}},"nm":"Path 5","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0],[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0]],"o":[[-1.21,-1.21],[0,-1.71],[1.21,-1.21],[1.71,0],[1.21,1.21],[0,1.684],[-1.21,1.21],[-1.684,0]],"v":[[2.585,-6.354],[0.77,-10.695],[2.585,-15.076],[6.926,-16.891],[11.307,-15.076],[13.122,-10.695],[11.307,-6.354],[6.926,-4.539]],"c":true}},"nm":"Path 6","hd":false},{"ind":6,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.658,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.658,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[-8.268,-8.248],[-7.242,-10.695],[-8.268,-13.182],[-10.754,-14.208],[-13.201,-13.182],[-14.188,-10.695],[-13.201,-8.248],[-10.754,-7.222]],"c":true}},"nm":"Path 7","hd":false},{"ind":7,"ty":"sh","ks":{"a":0,"k":{"i":[[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0],[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0]],"o":[[0.684,-0.684],[0,-0.973],[-0.684,-0.684],[-0.947,0],[-0.684,0.684],[0,0.947],[0.684,0.684],[0.973,0]],"v":[[9.413,-8.248],[10.439,-10.695],[9.413,-13.182],[6.926,-14.208],[4.479,-13.182],[3.453,-10.695],[4.479,-8.248],[6.926,-7.222]],"c":true}},"nm":"Path 8","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098045468,0.101960785687,0.015686275437,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"icon","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".primaryFixedDim","cl":"primaryFixedDim","parent":3,"sr":1,"ks":{"o":{"a":0,"k":0},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,0,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.92549020052,0.752941191196,0.423529416323,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_2","nm":"BlankButton","fr":60,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,39.79,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shortcut symbols","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":80},"r":{"a":0,"k":0},"p":{"a":0,"k":[40,49.21,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[80,79.581]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":14.032},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.364705890417,0.258823543787,0,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"shadow","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]},{"id":"comp_3","nm":"AK_LofiLauncher","fr":60,"pfr":1,"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Scale Down","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":51,"s":[70]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":135,"s":[70]},{"t":144,"s":[100]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[252,157.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":1,"k":[{"i":{"x":[0.1,0.1,0.1],"y":[1,1,1]},"o":{"x":[0.153,0.153,0.153],"y":[0.074,0.074,0]},"t":45,"s":[100,100,100]},{"i":{"x":[0.841,0.841,0.841],"y":[1,1,1]},"o":{"x":[0.161,0.161,0.161],"y":[0,0,0]},"t":75,"s":[95,95,100]},{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.2,0.2,0.2],"y":[0,0,0]},"t":135,"s":[95,95,100]},{"t":165,"s":[100,100,100]}]}},"ao":0,"ef":[{"ty":5,"nm":"Void","np":19,"mn":"Pseudo/250958","ix":1,"en":1,"ef":[{"ty":0,"nm":"Width","mn":"Pseudo/250958-0001","ix":1,"v":{"a":0,"k":100}},{"ty":0,"nm":"Height","mn":"Pseudo/250958-0002","ix":2,"v":{"a":0,"k":100}},{"ty":0,"nm":"Offset X","mn":"Pseudo/250958-0003","ix":3,"v":{"a":0,"k":0}},{"ty":0,"nm":"Offset Y","mn":"Pseudo/250958-0004","ix":4,"v":{"a":0,"k":0}},{"ty":0,"nm":"Roundness","mn":"Pseudo/250958-0005","ix":5,"v":{"a":0,"k":0}},{"ty":6,"nm":"About","mn":"Pseudo/250958-0006","ix":6,"v":0},{"ty":6,"nm":"Plague of null layers.","mn":"Pseudo/250958-0007","ix":7,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0008","ix":8,"v":0},{"ty":6,"nm":"Following projects","mn":"Pseudo/250958-0009","ix":9,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0010","ix":10,"v":0},{"ty":6,"nm":"through time.","mn":"Pseudo/250958-0011","ix":11,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0012","ix":12,"v":0},{"ty":6,"nm":"Be free of the past.","mn":"Pseudo/250958-0013","ix":13,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0014","ix":14,"v":0},{"ty":6,"nm":"Copyright 2023 Battle Axe Inc","mn":"Pseudo/250958-0015","ix":15,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0016","ix":16,"v":0},{"ty":6,"nm":"Void","mn":"Pseudo/250958-0017","ix":17,"v":0}]}],"shapes":[],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,117.5,0]},"a":{"a":0,"k":[252,275,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[300,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"hotseat - 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[168,20]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":15},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[132,275]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"qsb","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[20.144,20.144],[20.144,-20.144],[0,0],[-20.144,-20.144],[-20.144,20.144],[0,0]],"o":[[-20.144,-20.144],[0,0],[-20.144,20.144],[20.144,20.144],[0,0],[20.144,-20.144]],"v":[[44.892,-44.892],[-28.057,-44.892],[-44.892,-28.057],[-44.892,44.892],[28.057,44.892],[44.892,28.057]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[108,152.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets weather","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.782,-2.684],[0,0],[2.63,-0.033],[0,0],[2.807,-4.716],[0,0],[2.263,-1.343],[0,0],[0.066,-5.485],[0,0],[1.292,-2.295],[0,0],[-2.683,-4.784],[0,0],[-0.033,-2.63],[0,0],[-4.716,-2.807],[0,0],[-1.338,-2.263],[0,0],[-5.483,-0.066],[0,0],[-2.296,-1.292],[0,0],[-4.782,2.683],[0,0],[-2.63,0.033],[0,0],[-2.807,4.716],[0,0],[-2.263,1.338],[0,0],[-0.066,5.483],[0,0],[-1.292,2.295],[0,0],[2.683,4.784],[0,0],[0.033,2.631],[0,0],[4.716,2.801],[0,0],[1.338,2.262],[0,0],[5.483,0.068],[0,0],[2.296,1.287]],"o":[[-4.782,-2.684],[0,0],[-2.296,1.287],[0,0],[-5.483,0.068],[0,0],[-1.338,2.262],[0,0],[-4.716,2.801],[0,0],[-0.033,2.631],[0,0],[-2.683,4.784],[0,0],[1.292,2.295],[0,0],[0.066,5.483],[0,0],[2.263,1.338],[0,0],[2.807,4.716],[0,0],[2.63,0.033],[0,0],[4.782,2.683],[0,0],[2.296,-1.292],[0,0],[5.483,-0.066],[0,0],[1.338,-2.263],[0,0],[4.716,-2.807],[0,0],[0.033,-2.63],[0,0],[2.683,-4.784],[0,0],[-1.292,-2.295],[0,0],[-0.066,-5.485],[0,0],[-2.263,-1.343],[0,0],[-2.807,-4.716],[0,0],[-2.63,-0.033],[0,0]],"v":[[7.7,-57.989],[-7.7,-57.989],[-11.019,-56.128],[-18.523,-54.117],[-22.327,-54.07],[-35.668,-46.369],[-37.609,-43.1],[-43.099,-37.605],[-46.372,-35.663],[-54.072,-22.324],[-54.118,-18.522],[-56.132,-11.016],[-57.988,-7.7],[-57.988,7.703],[-56.132,11.019],[-54.118,18.524],[-54.072,22.328],[-46.372,35.669],[-43.099,37.611],[-37.609,43.101],[-35.668,46.373],[-22.327,54.074],[-18.523,54.12],[-11.019,56.133],[-7.7,57.99],[7.7,57.99],[11.019,56.133],[18.523,54.12],[22.327,54.074],[35.668,46.373],[37.609,43.101],[43.099,37.611],[46.372,35.669],[54.072,22.328],[54.118,18.524],[56.132,11.019],[57.988,7.703],[57.988,-7.7],[56.132,-11.016],[54.118,-18.522],[54.072,-22.324],[46.372,-35.663],[43.099,-37.605],[37.609,-43.1],[35.668,-46.369],[22.327,-54.07],[18.523,-54.117],[11.019,-56.128]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[396,104.003]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"widgets clock","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","parent":1,"sr":1,"ks":{"o":{"k":[{"s":[100],"t":45,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[80],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[0,-29.497,0]},"a":{"a":0,"k":[252,128.003,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[444,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 7","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[348,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 6","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,200.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 5","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,128.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 4","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[252,56.002]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 3","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[156,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 2","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24]},"p":{"a":0,"k":[0,0]},"nm":"Ellipse Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"apps","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[60,56.004]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"app - 1","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"TrackpadAK_Success_Checkmark","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,198.5,0]},"a":{"a":0,"k":[95,95,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":190,"h":190,"ip":6,"op":50,"st":6,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".onSecondaryFixed","cl":"onSecondaryFixed","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":389,"s":[100]},{"t":392,"s":[0]}]},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.145098039216,0.101960784314,0.01568627451,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":0,"nm":"actionKey_themed-static","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[325,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[421,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[229,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":0,"nm":"BlankButton","refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[133,455.5,0]},"a":{"a":0,"k":[40,44.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":80,"h":89,"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":0,"nm":"AK_LofiLauncher","refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[252,157.5,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"w":504,"h":315,"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".onSecondaryFixedVariant","cl":"onSecondaryFixedVariant","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"ef":[{"ty":5,"nm":"Global Position","np":4,"mn":"Pseudo/88900","ix":1,"en":1,"ef":[{"ty":10,"nm":"Master Parent","mn":"Pseudo/88900-0001","ix":1,"v":{"a":0,"k":2}},{"ty":3,"nm":"Global Position","mn":"Pseudo/88900-0002","ix":2,"v":{"k":[{"s":[277,197.5],"t":0,"i":{"x":1,"y":1},"o":{"x":0,"y":0}},{"s":[277,197.5],"t":49,"i":{"x":1,"y":1},"o":{"x":0,"y":0}}]}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.325490196078,0.270588235294,0.164705882353,1]},"o":{"a":0,"k":50},"r":1,"bm":0,"nm":"Fill 1","hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980401039,0.768627464771,0.627451002598,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"illustrations: action key","bm":0,"hd":false}],"ip":0,"op":600,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".secondaryFixedDim","cl":"secondaryFixedDim","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":0,"k":0},"p":{"a":0,"k":[277,197.5,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[504,315]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":28},"nm":"Rectangle Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":14},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"op","nm":"Stroke align: Outside","a":{"k":[{"s":[7],"t":0,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}},{"s":[7],"t":49,"i":{"x":[1],"y":[1]},"o":{"x":[0],"y":[0]}}]},"lj":1,"ml":{"a":0,"k":4},"hd":false},{"ty":"fl","c":{"a":0,"k":[0.850980392157,0.76862745098,0.627450980392,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"frame","bm":0,"hd":false}],"ip":0,"op":50,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}}
\ 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 8c685ff..2a669de 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en ander oop apps het hierdie skermskoot bespeur."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Voeg by nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Sluit skakel in"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Skermopnemer"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Verwerk tans skermopname"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Begin opneem?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Terwyl jy opneem, het Android toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Terwyl jy ’n app opneem, het Android toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Begin opneem"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Neem jou skerm op?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Neem een app op"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Neem hele skerm op"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wanneer jy jou hele skerm opneem, word enigiets wat op jou skerm wys, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wanneer jy ’n app opneem, word enigiets wat in daardie app gewys of gespeel word, opgeneem. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Neem skerm op"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Kies app om op te neem"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Neem oudio op"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Toesteloudio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Klank vanaf jou toestel, soos musiek, oproepe en luitone"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Stuur"</string>
     <string name="cancel" msgid="1089011503403416730">"Kanselleer"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Applogo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bevestig"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Probeer weer"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tik om stawing te kanselleer"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Sluimerskerm"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Moenie Steur Nie"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitmodusse"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen saamgebinde toestelle beskikbaar nie"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om ’n toestel te koppel of ontkoppel"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Oudiodeling"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tik om oor te skakel of oudio te deel"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Intydse Onderskrifte"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Intydse Onderskrifte"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Maak Instellings oop"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander toestel"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Wissel oorsig"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitmodusse"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellings"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Stel op"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pasmaak"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Legstukke op sluitskerm"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk is by sluitskerm gevoeg"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swiep links om die gemeenskaplike tutoriaal te begin"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pasmaak"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Maak toe"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sal toegang hê tot al die inligting wat op jou skerm sigbaar is of op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto’s, boodskappe en oudio wat jy speel."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Begin opneem of uitsaai?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Die diens wat hierdie funksie verskaf, sal toegang hê tot al die inligting wat op jou skerm sigbaar is of op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto’s, boodskappe en oudio wat jy speel."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Hele skerm"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"’n Enkele app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Deel of neem ’n app op"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Begin opneem of uitsaai met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Wanneer jy deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Wanneer jy ’n app deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Begin"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deel of neem ’n app op"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Deel jou skerm met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deel een app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deel hele skerm"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wanneer jy jou hele skerm deel, is enigiets op jou skerm sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wanneer jy ’n app deel, is enigiets wat in die app wys of speel, sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deel skerm"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> het hierdie opsie gedeaktiveer"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Begin uitsaai?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Wanneer jy uitsaai, het Android toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Wanneer jy ’n app uitsaai, het Android toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Begin uitsaai"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Kies app om te deel"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Saai jou skerm uit?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Saai een app uit"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Saai hele skerm uit"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Wanneer jy jou hele skerm uitsaai, is enigiets op jou skerm sigbaar. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Wanneer jy ’n app uitsaai, is enigiets wat in die app wys of speel, sigbaar. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Saai skerm uit"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Kies app om uit te saai"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Begin deel?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Wanneer jy deel, opneem of uitsaai, het Android toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Wanneer jy ’n app deel, opneem of uitsaai, het Android toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Begin"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Volgende"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Deling word onderbreek wanneer jy apps wissel"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Deel eerder hierdie app"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Skakel terug"</string>
@@ -594,13 +606,13 @@
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA-sertifikate"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Bekyk beleide"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Bekyk kontroles"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nJou IT-admin kan instellings, korporatiewe toegang, programme, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"Hierdie toestel behoort aan <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nJou IT-admin kan instellings, korporatiewe toegang, apps, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> kan dalk toegang kry tot data wat met hierdie toestel geassosieer word, programme bestuur, en hierdie toestel se instellings verander.\n\nKontak <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> as jy enige vrae het."</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"Hierdie toestel behoort aan jou organisasie.\n\nJou IT-admin kan instellings, korporatiewe toegang, programme, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"Hierdie toestel behoort aan jou organisasie.\n\nJou IT-admin kan instellings, korporatiewe toegang, apps, data wat met jou toestel geassosieer word, en jou toestel se ligginginligting monitor en bestuur.\n\nKontak jou IT-admin vir meer inligting."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Jou organisasie het \'n sertifikaatoutoriteit op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Jou organisasie het \'n sertifikaatoutoriteit in jou werkprofiel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"\'n Sertifikaatoutoriteit is op hierdie toestel geïnstalleer. Jou veilige netwerkverkeer kan gemonitor of gewysig word."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Jou administrateur het netwerkloginskrywing aangeskakel, wat verkeer op jou toestel monitor."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Jou admin het netwerkloginskrywing aangeskakel, wat verkeer op jou toestel monitor."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Jou administrateur het netwerkloglêers aangeskakel wat verkeer in jou werkprofiel monitor, maar nie in jou persoonlike profiel nie."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"Hierdie toestel is deur <xliff:g id="VPN_APP">%1$s</xliff:g> aan die internet gekoppel. Die VPN-verskaffer kan jou netwerkaktiwiteit sien, insluitend jou e-posse en blaaierdata."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"Hierdie toestel is deur <xliff:g id="VPN_APP">%1$s</xliff:g> aan die internet gekoppel. Jou IT-admin kan jou netwerkaktiwiteit sien, insluitend jou e-posse en blaaierdata."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goeie toestand"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding is beskikbaar"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliet-SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Noodoproepe of SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Pret vir party mense, maar nie vir almal nie"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Stelsel-UI-ontvanger gee jou ekstra maniere om die Android-gebruikerkoppelvlak in te stel en te pasmaak. Hierdie eksperimentele kenmerke kan in toekomstige uitreikings verander, breek of verdwyn. Gaan versigtig voort."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toeganklikheid"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortpadsleutels"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen soekresultate nie"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Teruggebaar"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Tuisgebaar"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handelingsleutel"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Uitstekende werk!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Raakpaneel wat drie vingers wat regs en links beweeg wys"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Toestelskerm wat animasie vir teruggebaar wys"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Swiep enige plek op die raakpaneel links of regs met drie vingers om terug te gaan.\n\nJy kan ook die kortpadsleutelhandeling + Esc hiervoor gebruik."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Knap gedaan!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Swiep enige tyd van die onderkant van jou skerm af op met drie vingers om na jou tuisskerm toe te gaan."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Mooi so!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Jy het die Gaan na Tuisskerm-gebaar voltooi."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Handelingsleutel"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Druk die handelingsleutel op jou sleutelbord om toegang tot jou apps te kry."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Geluk!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Jy het die Handelingsleutel-gebaar voltooi.\n\nHandeling + / wys al die kortpaaie wat vir jou beskikbaar is."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Swiep op met drie vingers op die raakpaneel om terug na die tuisskerm te gaan"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Swiep op en hou met drie vingers op die raakpaneel om onlangse apps te bekyk"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Druk die handelingsleutel op jou sleutelbord om al jou apps te bekyk"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Gewysig"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Ontsluit om te kyk"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Gebruik jou raakpaneel om terug te gaan"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swiep links of regs met drie vingers. Tik om meer gebare te leer."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Gebruik jou raakpaneel om na die tuisskerm toe te gaan"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swiep op en hou met drie vingers. Tik om meer gebare te leer."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gebruik jou sleutelbord om alle apps te bekyk"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk enige tyd die handelingsleutel. Tik om meer gebare te leer."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ekstra donker is nou deel van die helderheidbalk"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Jy kan nou die skerm ekstra donker maak deur die helderheidvlak vanaf die bokant van jou skerm nog laer te maak.\n\nDit werk die beste wanneer jy in ’n donker omgewing is."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Verwyder kortpad vir ekstra donker"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Kortpad vir ekstra donker is verwyder. Gebruik die gewone helderheidbalk om jou helderheid te verlaag."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 26e5c1e5..f7f9763 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> እና ሌሎች ክፍት መተግበሪያዎች ይህን ቅጽበታዊ ገፅ ዕይታ ለይተዋል።"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ወደ ማስታወሻ አክል"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"አገናኝ አካትት"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"የማያ መቅረጫ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገፅ ቀረጻን በማሰናዳት ላይ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገፅ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"መቅረጽ ይጀመር?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"እርስዎ ሲቀርጹ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"እርስዎ አንድን መተግበሪያ ሲቀርጹ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"መቅረጽ ጀምር"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ማያ ገፅዎን ይቀዳሉ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"አንድ መተግበሪያ ቅዳ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"መላው ማያ ገፅን ቅረጽ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"መላው ማያ ገፅዎን በሚቀዱበት ጊዜ፣ በማያ ገፅዎ ላይ የሚታየው ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"መተግበሪያን ሲቀዱ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ይቀዳል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ማያ ገፅን ቅረጽ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ለመቅዳት መተግበሪያ ይምረጡ"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ኦዲዮን ቅረጽ"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"የመሣሪያ ኦዲዮ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"እንደ ሙዚቃ፣ ጥሪዎች እና የጥሪ ቅላጼዎች ያሉ የመሣሪያዎ ድምፅ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ላክ"</string>
     <string name="cancel" msgid="1089011503403416730">"ይቅር"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"የመተግበሪያ ዓርማ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"አረጋግጥ"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"እንደገና ይሞክሩ"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ማረጋገጥን ለመሰረዝ መታ ያድርጉ"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"የማያ ገፅ ማቆያ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ኤተርኔት"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"አትረብሽ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ቅድሚያ ሁነታዎች"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ብሉቱዝ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ምንም የተጣመሩ መሣሪያዎች አይገኝም"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"መሣሪያን ለማገናኘት ወይም ግንኙነቱን ለማቋረጥ መታ ያድርጉ"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"የድምጽ ማጋራት"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ኦዲዮ ለመቀየር ወይም ለማጋራት መታ ያድርጉ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ተቀምጧል"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ግንኙነትን አቋርጥ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ያግብሩ"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"የቀጥታ ስርጭት መግለጫ ጽሁፍ"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"የቀጥታ መግለጫ ጽሑፍ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ቅንብሮችን ክፈት"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ሌላ መሣሪያ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"አጠቃላይ እይታን ቀያይር"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ቅድሚያ ሁነታዎች"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ተከናውኗል"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ቅንብሮች"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"አዋቅር"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ከማንቂያዎች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"አብጅ"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ምግብሮች በማያ ገጽ ቁልፍ ላይ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር ወደ ማያ ገፅ ቁልፍ ታክሏል"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"የጋራ አጋዥ ሥልጠናውን ለመጀመር ወደ ግራ ያንሸራትቱ።"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"አብጅ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"አሰናብት"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በእርስዎ ማያ ገጽ ላይ ለሚታየው ወይም በሚቀረጽበት ወይም cast በሚደረግበት ጊዜ በእርስዎ መሣሪያ ላይ ለሚጫወተው ሁሉም መረጃ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚያጫውቱትን ኦዲዮ የመሳሰለ መረጃን ያካትታል።"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"መቅረጽ ወይም cast ማድረግ ይጀመር?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ይህን ተግባር የሚያቀርበው አገልግሎት በእርስዎ ማያ ገጽ ላይ ለሚታየው ወይም በሚቀረጽበት ወይም cast በሚደረግበት ጊዜ በእርስዎ መሣሪያ ላይ ለሚጫወተው ሁሉም መረጃ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚያጫውቱትን ኦዲዮ የመሳሰለ መረጃን ያካትታል።"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"መላው ማያ ገጽ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"አንድ ነጠላ መተግበሪያ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"መተግበሪያን ያጋሩ ወይም ይቅረጹ"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"በ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> መቅረጽ ወይም cast ማድረግ ይጀመር?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"እርስዎ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"አንድን መተግበሪያ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በዚያ መተግበሪያ ላይ ለሚታይ ወይም ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ጀምር"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"መተግበሪያን ያጋሩ ወይም ይቅረጹ"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ማያ ገፅዎን ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ያጋራሉ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"አንድ መተግበሪያ ያጋሩ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"መላውን ማያ ገፅ ያጋሩ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"እርስዎ ሙሉ ማያ ገፅዎን ሲያጋሩ በማያ ገፅዎ ላይ ያለው ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"መተግበሪያን ሲያጋሩ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ማያ ገፅ አጋራ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ይህን አማራጭ አሰናክሏል"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"cast ማድረግ ይጀምር?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"እርስዎ cast በሚያደርጉበት ጊዜ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"አንድን መተግበሪያ cast ሲያደርጉ Android በዚያ መተግበሪያ ላይ ለሚታይ ወይም ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ ይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"cast ማድረግ ይጀምር"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ለማጋራት መተግበሪያ ይምረጡ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ማያ ገፅዎ cast ይደረግ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"አንድ መተግበሪያ cast ያድርጉ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"መላውን ማያ ገፅ cast ያድርጉ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"እርስዎ ሙሉ ማያ ገፅዎን cast ሲያደርጉ በማያ ገፅዎ ላይ ያለው ማንኛውም ነገር ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"መተግበሪያን cast ሲያደርጉ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ማያ ገፅ Cast አድርግ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"cast ለማድረግ መተግበሪያ ይምረጡ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ማጋራት ይጀምር?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"እርስዎ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"እርስዎ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ጀምር"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ቀጣይ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"መተግበሪያዎችን በሚቀያይሩበት ጊዜ ለአፍታ ማቆሞችን ማጋራት"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"በምትኩ ይህን መተግበሪያ ያጋሩ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ተመልሰው ይቀይሩ"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ሳተላይት፣ ጥሩ ግንኙነት"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ሳተላይት፣ ግንኙነት አለ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ሳተላይት ኤስኦኤስ"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"የአደጋ ጥሪዎች ወይም ኤስኦኤስ"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"የስራ መገለጫ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ለአንዳንዶች አስደሳች ቢሆንም ለሁሉም አይደለም"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"የስርዓት በይነገጽ መቃኛ የAndroid ተጠቃሚ በይነገጹን የሚነካኩበት እና የሚያበጁበት ተጨማሪ መንገዶች ይሰጠዎታል። እነዚህ የሙከራ ባህሪዎች ወደፊት በሚኖሩ ልቀቶች ላይ ሊለወጡ፣ ሊሰበሩ ወይም ሊጠፉ ይችላሉ። ከጥንቃቄ ጋር ወደፊት ይቀጥሉ።"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ተደራሽነት"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"የቁልፍ ሰሌዳ አቋራጮች"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ምንም የፍለጋ ውጤቶች የሉም"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"የተመለስ ምልክት"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"የቤት ምልክት"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"የተግባር ቁልፍ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ጥሩ ሠርተዋል!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ሦስት ጣቶች ወደቀኝ እና ግራ ሲንቀሳቀሱ የሚያሳይ የመዳሰሻ ሰሌዳ"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"የኋላ ምልክት እነማ የሚያሳይ የመሣሪያ ማያ ገፅ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ወደኋላ ለመመለስ የመዳሰሻ ሰሌዳው ላይ የትኛውም ቦታ በሦስት ጣቶች ወደግራ ወይም ወደቀኝ ያንሸራትቱ።\n\nእንዲሁም የቁልፍ ሰሌዳ አቋራጭ + ESC ለዚህ መጠቀም ይችላሉ።"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ጥሩ ሠርተዋል!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ወደኋላ የመመለስ ምልክትን አጠናቅቀዋል።"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ወደ መነሻ ሂድ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"በማንኛውም ጊዜ ወደ መነሻ ማያ ገፅዎ ለመሄድ ከማያ ገፅዎ ታች በሦስት ጣቶች ወደላይ ያሸብልሉ።"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"አሪፍ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል።"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"የተግባር ቁልፍ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"መተግበሪያዎችዎን ለመድረስ በቁልፍ ሰሌዳዎ ላይ የእርምጃ ቁልፉን ይጫኑ።"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"እንኳን ደስ አለዎት!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"የተግባር ቁልፍ ምልክቱን አጠናቅቀዋል።\n\nእርምጃ + / ለእርስዎ ተገኚ የሆኑትን አቋራጮች በሙሉ ያሳያል።"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ወደ መነሻ ለመሄድ የመዳሰሻ ሰሌዳው ላይ በሦስት ጣቶች ወደላይ ያንሸራትቱ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"የቅርብ ጊዜ መተግበሪያዎችን ለማየት የመዳሰሻ ሰሌዳው ላይ በሦስት ጣቶች ወደላይ ያንሸራትቱ እና ይያዙ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ሁሉንም መተግበሪያዎችዎን ለማየት በቁልፍ ሰሌዳዎ ላይ ያለውን የተግባር ቁልፍ ይጫኑ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ጽሁፍ ተቀይሯል"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ለመመልከት ይክፈቱ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ለመመለስ የመዳሰሻ ሰሌዳዎን ይጠቀሙ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ሦስት ጣቶችን በመጠቀም ወደ ግራ ወይም ወደ ቀኝ ያንሸራትቱ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ወደ መነሻ ለመመለስ የመዳሰሻ ሰሌዳዎን ይጠቀሙ"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ሁሉንም መተግበሪያዎች ለማየት የቁልፍ ሰሌዳዎን ይጠቀሙ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"በማንኛውም ጊዜ የተግባር ቁልፍን ይጫኑ። ምልክቶችን የበለጠ ለማወቅ መታ ያድርጉ።"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ተጨማሪ ደብዛዛ አሁን የብሩህነት አሞሌ ክፍል ነው"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"አሁን ከማያ ገፅዎ በላይ የብሩህነት ደረጃውን ይበልጥ በመቀነስ ማያ ገፁን ተጨማሪ ደብዛዛ ማድረግ ይችላሉ።\n\nይህ በጨለማ አካባቢ ውስጥ ሲሆኑ በተሻለ ይሠራል።"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ተጨማሪ ደብዛዛ አቋራጭን አስወግድ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"የተጨማሪ ደብዛዛ አቋራጭን ያስወግዱ። የእርስዎን ብሩሃማነት ለመቀነስ መደበኛ የብሩሃማነት አሞሌውን ይጠቀሙ።"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 3b65cd8..5d6b908 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" والتطبيقات المفتوحة الأخرى لقطة الشاشة هذه."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"إضافة إلى الملاحظة"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"تضمين الرابط"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"‫<xliff:g id="APPNAME">%1$s</xliff:g> ‏<xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"مسجّل الشاشة"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"جارٍ معالجة تسجيل الشاشة"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"هل تريد بدء التسجيل؟"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"‏أثناء التسجيل، يمكن لنظام Android الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"‏أثناء تسجيل محتوى تطبيق، يمكن لنظام Android الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"بدء التسجيل"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"هل تريد تسجيل محتوى الشاشة؟"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل محتوى تطبيق واحد"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"تسجيل محتوى الشاشة بالكامل"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"أثناء تسجيل محتوى الشاشة بالكامل، يتم تسجيل كل المحتوى المعروض على شاشتك. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"أثناء تسجيل محتوى تطبيق، يتم تسجيل أي محتوى يتم عرضه أو تشغيله في ذلك التطبيق. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"تسجيل محتوى الشاشة"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"اختيار تطبيق لتسجيل محتواه"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"تسجيل الصوت"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"صوت الجهاز"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"الصوت من جهازك، مثلاً الموسيقى والمكالمات ونغمات الرنين"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"إرسال"</string>
     <string name="cancel" msgid="1089011503403416730">"إلغاء"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"شعار التطبيق"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"تأكيد"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"إعادة المحاولة"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"انقر لإلغاء المصادقة."</string>
@@ -247,7 +249,7 @@
     <string name="data_connection_roaming" msgid="375650836665414797">"التجوال"</string>
     <string name="cell_data_off" msgid="4886198950247099526">"غير مفعّلة"</string>
     <string name="accessibility_airplane_mode" msgid="1899529214045998505">"وضع الطيران."</string>
-    <string name="accessibility_vpn_on" msgid="8037549696057288731">"‏الشبكة الافتراضية الخاصة (VPN) قيد التفعيل."</string>
+    <string name="accessibility_vpn_on" msgid="8037549696057288731">"‏شبكة VPN قيد التفعيل."</string>
     <string name="accessibility_battery_level" msgid="5143715405241138822">"مستوى البطارية <xliff:g id="NUMBER">%d</xliff:g> في المائة."</string>
     <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"نسبة شحن البطارية <xliff:g id="PERCENTAGE">%1$d</xliff:g> في المئة، <xliff:g id="TIME">%2$s</xliff:g>."</string>
     <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"جارٍ شحن البطارية، <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
@@ -290,15 +292,17 @@
     <string name="start_dreams" msgid="9131802557946276718">"شاشة الاستراحة"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"عدم الإزعاج"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"الأوضاع ذات الأولوية"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"لا يتوفر أي أجهزة مقترنة"</string>
-    <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"انقر لربط جهاز أو إلغاء ربطه"</string>
+    <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"انقر للاتصال بجهاز أو قطع الاتصال به"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"إقران جهاز جديد"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"عرض الكل"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"مشاركة الصوت"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"انقر لتبديل مصدر الصوت أو مشاركته"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"إلغاء الربط"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"تفعيل"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string>
     <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_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>
@@ -382,13 +387,13 @@
     <string name="qs_record_issue_start" msgid="2979831312582567056">"بدء"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"إيقاف"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"تقرير خطأ"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ما هو الجانب الذي تأثّر في تجربة استخدام الجهاز؟"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"بأي جانب من تجربة استخدام الجهاز تتعلّق المشكلة؟"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"اختيار نوع المشكلة"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"تسجيل الشاشة"</string>
     <string name="performance" msgid="6552785217174378320">"الأداء"</string>
     <string name="user_interface" msgid="3712869377953950887">"واجهة المستخدم"</string>
-    <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string>
-    <string name="custom" msgid="3337456985275158299">"مخصّص"</string>
+    <string name="thermal" msgid="6758074791325414831">"ارتفاع حرارة الجهاز"</string>
+    <string name="custom" msgid="3337456985275158299">"الإعدادات المخصّصة"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"إعدادات التتبع المخصصة"</string>
     <string name="restore_default" msgid="5259420807486239755">"استعادة الإعدادات التلقائية"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"النسخ النصي التلقائي"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"النسخ النصي التلقائي"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"فتح الإعدادات"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"جهاز آخر"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تبديل \"النظرة العامة\""</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"الأوضاع ذات الأولوية"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"تم"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"الإعدادات"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"إعداد"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"تخصيص"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن ببطء • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • جارٍ الشحن • ستمتلئ البطارية خلال <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"التطبيقات المصغّرة على شاشة القفل"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"تمت إضافة تطبيق \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" المصغّر إلى شاشة القفل"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"مرِّر سريعًا لليمين لبدء الدليل التوجيهي العام."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"تخصيص"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"إغلاق"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"سيتمكن تطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" من الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك أثناء التسجيل أو البثّ. ويشمل ذلك معلومات، مثل كلمات المرور وتفاصيل الدفع والصور والرسائل والمقاطع الصوتية التي تشغِّلها."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"هل تريد بدء التسجيل أو البثّ؟"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ستتمكن الخدمة التي تقدّم هذه الوظيفة من الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك أثناء التسجيل أو البثّ. ويشمل ذلك معلومات، مثل كلمات المرور وتفاصيل الدفع والصور والرسائل والمقاطع الصوتية التي تشغِّلها."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"الشاشة بالكامل"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"تطبيق واحد"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"مشاركة أو تسجيل محتوى تطبيق محدّد"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"هل تريد بدء التسجيل أو البثّ باستخدام \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"؟"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"أثناء المشاركة أو التسجيل أو البثّ، يمكن لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهاز، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"أثناء مشاركة محتوى تطبيق أو تسجيله أو بثّه، يمكن لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"بدء"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"مشاركة محتوى تطبيق أو تسجيله"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"هل تريد مشاركة الشاشة مع تطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"؟"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"مشاركة تطبيق واحد"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"مشاركة الشاشة بأكملها"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"أثناء مشاركة محتوى الشاشة بالكامل، سيكون كل المحتوى المعروض على شاشتك مرئيًا لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"أثناء مشاركة محتوى تطبيق، سيكون كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق مرئيًا لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"مشاركة الشاشة"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"تم إيقاف هذا الخيار من خلال تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"هل تريد بدء البثّ؟"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‏أثناء البثّ، يمكن لنظام Android الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏أثناء بثّ محتوى تطبيق، يمكن لنظام Android الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"بدء البثّ"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"اختيار تطبيق لمشاركة محتواه"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"هل تريد بث محتوى الشاشة؟"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"بث محتوى تطبيق واحد"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"بث محتوى الشاشة بالكامل"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"أثناء بث محتوى الشاشة بالكامل، سيكون كل المحتوى المعروض على شاشتك مرئيًا. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"أثناء بث محتوى تطبيق، سيكون كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق مرئيًا. لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"بث محتوى الشاشة"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"اختيار تطبيق لبث محتواه"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"هل تريد بدء المشاركة؟"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‏أثناء المشاركة أو التسجيل أو البثّ، يمكن لنظام Android الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‏أثناء مشاركة محتوى تطبيق أو تسجيله أو بثّه، يمكن لنظام Android الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"بدء"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"التالي"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"تتوقف المشاركة مؤقتًا عند التبديل بين التطبيقات."</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"مشاركة هذا التطبيق بدلاً من ذلك"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"الرجوع"</string>
@@ -577,19 +589,19 @@
     <string name="quick_settings_disclosure_named_management_named_vpn" msgid="2169227918166358741">"ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال <xliff:g id="VPN_APP">%2$s</xliff:g>."</string>
     <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"هذا الجهاز يخص مؤسستك."</string>
     <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
-    <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"‏ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
-    <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"‏ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string>
+    <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"‏ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات شبكة VPN."</string>
+    <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"‏ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات شبكة VPN."</string>
     <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في ملف العمل"</string>
     <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملف العمل الخاص بك"</string>
     <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة ملف العمل مرئية لمشرف تكنولوجيا المعلومات."</string>
     <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string>
-    <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"‏هذا الجهاز متّصل بالإنترنت من خلال خدمات الشبكات الافتراضية الخاصة (VPN)."</string>
+    <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"‏هذا الجهاز متّصل بالإنترنت من خلال خدمات شبكات VPN."</string>
     <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
     <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"تطبيقاتك الشخصية متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
     <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"هذا الجهاز متّصل بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string>
     <string name="monitoring_title_financed_device" msgid="3659962357973919387">"توفر مؤسسة \"<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>\" هذا الجهاز"</string>
     <string name="monitoring_title_device_owned" msgid="7029691083837606324">"إدارة الأجهزة"</string>
-    <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"‏شبكة افتراضية خاصة (VPN)"</string>
+    <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"‏شبكة VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"تسجيل بيانات الشبكة"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"‏شهادات CA"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"عرض السياسات"</string>
@@ -605,12 +617,12 @@
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"‏هذا الجهاز متّصل بالإنترنت من خلال \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". ويمكن لمقدِّم شبكة VPN الاطّلاع على أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفّح."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"هذا الجهاز متّصل بالإنترنت من خلال \"<xliff:g id="VPN_APP">%1$s</xliff:g>\". تجدر الإشارة إلى أنّ أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح، مرئية لمشرف تكنولوجيا المعلومات في مؤسستك."</string>
     <string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"هذا الجهاز متّصل بالإنترنت من خلال <xliff:g id="VPN_APP_0">%1$s</xliff:g> و<xliff:g id="VPN_APP_1">%2$s</xliff:g>. يمكن لمشرف تكنولوجيا المعلومات رؤية أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"‏تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. يمكن لمشرف تكنولوجيا المعلومات ومزوّد خدمة الشبكة الافتراضية الخاصة (VPN) رؤية أنشطة الشبكة في تطبيقات العمل، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"‏تطبيقاتك الشخصية متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. تظهر أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح، لمزوّد خدمة الشبكة الافتراضية الخاصة (VPN)."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"‏تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. يمكن لمشرف تكنولوجيا المعلومات ومزوّد خدمة شبكة VPN رؤية أنشطة الشبكة في تطبيقات العمل، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"‏تطبيقاتك الشخصية متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>. تظهر أنشطة الشبكة، بما في ذلك الرسائل الإلكترونية وبيانات التصفُّح، لمزوّد خدمة شبكة VPN."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"‏فتح إعدادات شبكة VPN"</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"يتولّى أحد الوالدين إدارة هذا الجهاز. يمكن للوالدين عرض وإدارة معلوماتك، مثلاً التطبيقات التي تستخدمها وموقعك الجغرافي ووقت النظر إلى الشاشة."</string>
-    <string name="legacy_vpn_name" msgid="4174223520162559145">"شبكة افتراضية خاصة"</string>
+    <string name="legacy_vpn_name" msgid="4174223520162559145">"‏شبكة VPN"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"‏فتح القفل باستمرار بواسطة TrustAgent"</string>
     <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"تم قفل الجهاز بسبب إجراء العديد من محاولات المصادقة"</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"تم قفل الجهاز\nتعذّرت المصادقة"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"اتصالات الطوارئ بالقمر الصناعي"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"مكالمات الطوارئ أو ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string>
@@ -841,7 +854,7 @@
     <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"الإدخال"</string>
     <string name="input_switch_input_language_next" msgid="3782155659868227855">"التبديل إلى اللغة التالية"</string>
     <string name="input_switch_input_language_previous" msgid="6043341362202336623">"التبديل إلى اللغة السابقة"</string>
-    <string name="input_access_emoji" msgid="8105642858900406351">"الوصول إلى الرموز التعبيرية"</string>
+    <string name="input_access_emoji" msgid="8105642858900406351">"الوصول إلى رموز الإيموجي"</string>
     <string name="input_access_voice_typing" msgid="7291201476395326141">"الوصول إلى ميزة \"الكتابة بالصوت\""</string>
     <string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"التطبيقات"</string>
     <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"‏مساعد Google"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"تسهيل الاستخدام"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"اختصارات لوحة المفاتيح"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ما مِن نتائج بحث"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"إيماءة الرجوع"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"إيماءة الانتقال إلى الشاشة الرئيسية"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"مفتاح الإجراء"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"أحسنت."</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"لوحة لمس تعرض ثلاثة أصابع تتحرك يمينًا ويسارًا"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"شاشة جهاز تعرض صورة متحركة لإيماءة الرجوع"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏للرجوع، مرِّر سريعًا لليمين أو لليسار باستخدام ثلاثة أصابع في أي مكان على لوحة اللمس.\n\nيمكنك أيضًا الرجوع باستخدام اختصار لوحة المفاتيح \"مفتاح الإجراء + ESC\"."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"أحسنت"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"لقد أكملت التدريب على إيماءة الرجوع."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"الانتقال إلى الشاشة الرئيسية"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"للانتقال إلى الشاشة الرئيسية في أي وقت، مرِّر سريعًا من أسفل الشاشة إلى أعلاها بثلاثة أصابع."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"أحسنت"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"لقد أكملت التدريب على إيماءة الانتقال إلى الشاشة الرئيسية."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"مفتاح الإجراء"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"للوصول إلى التطبيقات، اضغط على مفتاح الإجراء في لوحة المفاتيح."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"تهانينا!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"لقد أكملت التدريب على إيماءة مفتاح الإجراء.\n\nيؤدي الضغط على مفتاح الإجراء + / إلى عرض جميع الاختصارات المتاحة."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"للانتقال إلى الشاشة الرئيسية، مرِّر سريعًا للأعلى على لوحة اللمس باستخدام 3 أصابع"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"لعرض التطبيقات المستخدَمة مؤخرًا، مرِّر سريعًا للأعلى مع استمرار الضغط على لوحة اللمس باستخدام 3 أصابع"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"لعرض جميع التطبيقات، اضغط على مفتاح الإجراء في لوحة المفاتيح"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"إشعار مخفي"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"افتح القفل لعرض المعلومات"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"استخدِم لوحة اللمس للرجوع"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"مرِّر سريعًا لليمين أو لليسار باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"استخدِم لوحة اللمس للانتقال إلى الشاشة الرئيسية"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"مرِّر سريعًا للأعلى مع استمرار الضغط باستخدام 3 أصابع. انقر للتعرّف على المزيد من الإيماءات."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"استخدِم لوحة المفاتيح لعرض جميع التطبيقات"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"اضغط على مفتاح الإجراء في أي وقت. انقر للتعرّف على المزيد من الإيماءات."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ميزة \"زيادة تعتيم الشاشة\" أصبحت الآن ضمن شريط مستوى السطوع"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"يمكنك الآن زيادة تعتيم الشاشة عن طريق خفض مستوى السطوع بشكل أكبر من أعلى الشاشة.\n\nيُعد هذا الخيار مناسبًا عندما تكون في مكان مظلم."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"إزالة اختصار \"زيادة تعتيم الشاشة\""</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"تمت إزالة اختصار \"زيادة تعتيم الشاشة\". لخفض مستوى سطوع شاشتك، استخدِم شريط مستوى السطوع العادي."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 576e8ad..2e8a552 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> আৰু আন খোলা এপ্‌সমূহে এই স্ক্ৰীনশ্বটটো চিনাক্ত কৰিছে।"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"টোকাত যোগ দিয়ক"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"লিংক অন্তৰ্ভুক্ত কৰক"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রীন ৰেকৰ্ডিঙৰ প্ৰক্ৰিয়াকৰণ হৈ আছে"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ৰেকৰ্ড কৰা আৰম্ভ কৰিবনে?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"আপুনি ৰেকৰ্ড কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"আপুনি এটা এপ্ ৰেকৰ্ড কৰাৰ সময়ত সেইটো এপত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ৰেকৰ্ডিং আৰম্ভ কৰক"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপোনাৰ স্ক্ৰীনখন ৰেকৰ্ড কৰিবনে?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"এটা এপ্ ৰেকৰ্ড কৰক"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"গোটেই স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপুনি গোটেই স্ক্ৰীনখন ৰেকৰ্ডিং কৰিলে, আপোনাৰ স্ক্ৰীনখনত দেখুওৱা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপুনি কোনো এপ্ ৰেকৰ্ড কৰিলে, সেই এপত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু ৰেকৰ্ড কৰা হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্ৰীনখন ৰেকৰ্ড কৰক"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ৰেকৰ্ড কৰিবলৈ এপ্‌ বাছনি কৰক"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"অডিঅ’ ৰেকৰ্ড কৰক"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ডিভাইচৰ অডিঅ’"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"সংগীত, কল আৰু ৰিংট’নসমূহৰ দৰে আপোনাৰ ডিভাইচৰ পৰা কেপচাৰ কৰিব পৰা ধ্বনি"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পঠিয়াওক"</string>
     <string name="cancel" msgid="1089011503403416730">"বাতিল কৰক"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"এপৰ ল’গ’"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"নিশ্চিত কৰক"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"আকৌ চেষ্টা কৰক"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ বাতিল কৰিবলৈ টিপক"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"স্ক্ৰীন ছেভাৰ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ইথাৰনেট"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"অসুবিধা নিদিব"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"অগ্ৰাধিকাৰপ্ৰাপ্ত ম’ড"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ব্লুটুথ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"কোনো যোৰা লগোৱা ডিভাইচ উপলব্ধ নহয়।"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ডিভাইচ সংযোগ কৰিবলৈ অথবা সংযোগ বিচ্ছিন্ন কৰিবলৈ টিপক"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"অডিঅ’ শ্বেয়াৰ কৰা"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"অডিঅ’ সলনি কৰিবলৈ বা শ্বেয়াৰ কৰিলৈ টিপক"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ছেভ কৰা হৈছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"সংযোগ বিচ্ছিন্ন কৰক"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"সক্ৰিয় কৰক"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"লাইভ কেপশ্বন"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ কেপশ্বন"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
@@ -429,10 +434,12 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ছেটিং খোলক"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইচ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"অৱলোকন ট’গল কৰক"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"অগ্ৰাধিকাৰপ্ৰাপ্ত ম’ডসমূহ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"কৰা হ’ল"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ছেটিং"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"ছেট আপ কৰক"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ছেটিঙত পৰিচালনা কৰক"</string>
@@ -475,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • লাহে লাহে চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চাৰ্জ হৈ আছে • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ত সম্পূৰ্ণ হ’ব"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"লক স্ক্ৰীনত ৱিজেট"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট লক স্ক্ৰীনত যোগ দিয়া হৈছে"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"সম্প্ৰদায় সম্পৰ্কীয় নিৰ্দেশনা আৰম্ভ কৰিবলৈ বাওঁফালে ছোৱাইপ কৰক"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"কাষ্টমাইজ কৰক"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"অগ্ৰাহ্য কৰক"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"এই স্পেচটোত আপোনাৰ ৱিজেটসমূহ যোগ দিয়ক, আঁতৰাওক আৰু সেইসমূহৰ ক্ৰম সলনি কৰক"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"এই স্পে’চটোত আপোনাৰ ৱিজেটসমূহ যোগ দিয়ক, আঁতৰাওক আৰু সেইসমূহৰ ক্ৰম সলনি কৰক"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"অধিক ৱিজেট যোগ দিয়ক"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"ৱিজেট কাষ্টমাইজ কৰিবলৈ দীঘলীয়াকৈ টিপক"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"ৱিজেট কাষ্টমাইজ কৰক"</string>
@@ -528,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>এ আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকৰ্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে’ কৰা আটাইবোৰ তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, ফট’, বাৰ্তাসমূহ আৰু আপুনি প্লে’ কৰা অডিঅ’ৰ দৰে তথ্য অন্তৰ্ভুক্ত হয়।"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ৰেকৰ্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"এই সুবিধাটো প্ৰদান কৰা সেৱাটোৱে আপোনাৰ স্ক্ৰীনত দৃশ্যমান হোৱা অথবা ৰেকৰ্ডিং অথবা কাষ্টিঙৰ সময়ত আপোনাৰ ডিভাইচত প্লে’ কৰা আটাইবোৰ তথ্যলৈ এক্সেছ পাব। এইটোত পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, ফট’, বাৰ্তাসমূহ আৰু আপুনি প্লে’ কৰা অডিঅ’ৰ দৰে তথ্য অন্তৰ্ভুক্ত হয়।"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"সম্পূৰ্ণ স্ক্ৰীন"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"এটা একক এপ্"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"এটা এপ্ শ্বেয়াৰ অথবা ৰেকৰ্ড কৰক"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ জৰিয়তে ৰেকৰ্ডিং অথবা কাষ্টিং আৰম্ভ কৰিবনে?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, সেইটো এপত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"আৰম্ভ কৰক"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"এটা এপ্ শ্বেয়াৰ অথবা ৰেকৰ্ড কৰক"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ সৈতে আপোনাৰ স্ক্ৰীন শ্বেয়াৰ কৰিবনে?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"এটা এপ্‌ শ্বেয়াৰ কৰক"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰক"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপুনি আপোনাৰ গোটেই স্ক্ৰীনখন শ্বেয়াৰ কৰি থাকোঁতে, আপোনাৰ স্ক্ৰীনত থকা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"আপুনি কোনো এপ্‌ শ্বেয়াৰ কৰি থাকোঁতে সেই এপ্‌টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্ৰীন শ্বেয়াৰ কৰক"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ এই বিকল্পটো অক্ষম কৰিছে"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"কাষ্ট কৰিবলৈ আৰম্ভ কৰিবনে?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"আপুনি কাষ্ট কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"আপুনি এটা এপ্ কাষ্ট কৰাৰ সময়ত সেইটো এপত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"কাষ্ট কৰিবলৈ আৰম্ভ কৰক"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শ্বেয়াৰ কৰিবলৈ এপ্ বাছনি কৰক"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপোনাৰ স্ক্ৰীনখন কাষ্ট কৰিবনে?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"এটা এপ্‌ কাষ্ট কৰক"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"গোটেই স্ক্ৰীনখন কাষ্ট কৰক"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"যেতিয়া আপুনি গোটেই স্ক্ৰীনখন কাষ্ট কৰি থাকে, তেতিয়া আপোনাৰ স্ক্ৰীনত থকা যিকোনো বস্তু দৃশ্যমান হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"যেতিয়া আপুনি কোনো এপ্‌ কাষ্ট কৰি থাকে, তেতিয়া সেই এপ্‌টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু দৃশ্যমান হয়। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"স্ক্ৰীন কাষ্ট কৰক"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"কাষ্ট কৰিবলৈ এপ্ বাছনি কৰক"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"শ্বেয়াৰ কৰিবলৈ আৰম্ভ কৰিবনে?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, সেইটো এপত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"আৰম্ভ কৰক"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"পৰৱৰ্তী"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"আপুনি এপ্‌ সলনি কৰিলে শ্বেয়াৰ কৰাটো পজ হয়"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"তাৰ সলনি এই এপ্‌টো শ্বেয়াৰ কৰক"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"উভতি যাওক"</string>
@@ -711,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"উপগ্ৰহ, ভাল সংযোগ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"উপগ্ৰহ, সংযোগ উপলব্ধ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"উপগ্ৰহ SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"জৰুৰীকালীন কল বা SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ’ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
@@ -1373,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"সাধ্য সুবিধা"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"কীব’ৰ্ডৰ শ্বৰ্টকাট"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"সন্ধানৰ কোনো ফলাফল নাই"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"উভতি যাওক নিৰ্দেশ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"কাৰ্য কী"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"বঢ়িয়া!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"তিনিটা আঙুলি সোঁ আৰু বাওঁফালে লৰচৰ কৰা দেখুওৱা টাচ্চপেড"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"পিছফালৰ নিৰ্দেশৰ এনিমেশ্বন দেখুওৱা ডিভাইচ স্ক্ৰীন"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"উভতি যাবলৈ, টাচ্চপেডৰ যিকোনো স্থানত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁ বা সোঁফালে ছোৱাইপ কৰক।\n\nইয়াৰ বাবে আপুনি কীব’ৰ্ড শ্বৰ্টকাট কাৰ্য + ESC ব্যৱহাৰ কৰিবও পাৰে।"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"বঢ়িয়া!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"আপুনি উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"গৃহ পৃষ্ঠালৈ যাওক"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"যিকোনো সময়তে আপোনাৰ গৃহ স্ক্ৰীনলৈ যাবলৈ, আপোনাৰ স্ক্ৰীনখনৰ একেবাৰে তলৰ পৰা ওপৰলৈ তিনিটা আঙুলিৰে ছোৱাইপ কৰক।"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"সুন্দৰ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"আপুনি গৃহ স্ক্ৰীনলৈ উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"কাৰ্য কী"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"আপোনাৰ এপ্‌সমূহ এক্সেছ কৰিবলৈ আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক।"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"অভিনন্দন!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"আপুনি কাৰ্য কীৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।\n\nকাৰ্য + /এ আপোনাৰ বাবে উপলব্ধ আটাইবোৰ শ্বৰ্টকাট দেখুৱায়।"</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>
@@ -1397,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"গৃহপৃষ্ঠালৈ যাওক, টাচ্চপেডত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"শেহতীয়া এপ্‌সমূহ চাবলৈ টাচ্চপেডখনত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"আপোনাৰ আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ডৰ কাৰ্য কীটোত টিপক"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"সম্পাদনা কৰা হৈছে"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"চাবলৈ আনলক কৰক"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"উভতি যাবলৈ আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰক"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁফাললৈ বা সোঁফাললৈ ছোৱাইপ কৰক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"গৃহপৃষ্ঠালৈ যাবলৈ আপোনাৰ টাচ্চপেড ব্যৱহাৰ কৰক"</string>
@@ -1405,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"আটাইবোৰ এপ্‌ চাবলৈ আপোনাৰ কীব’ৰ্ড ব্যৱহাৰ কৰক"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যিকোনো সময়তে কাৰ্য কীটোত টিপক। অধিক নিৰ্দেশ শিকিবলৈ টিপক।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"এক্সট্ৰা ডিম এতিয়া উজ্জ্বলতা বাৰৰ অংশ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"আপুনি এতিয়া আপোনাৰ স্ক্ৰীনৰ একেবাৰে ওপৰৰ পৰা উজ্জ্বলতাৰ স্তৰ আৰু অধিক হ্ৰাস কৰি স্ক্ৰীনখন এক্সট্ৰা ডিম কৰিব পাৰে।\n\nআপুনি অন্ধকাৰ পৰিৱেশত থাকিলে এই সুবিধাটোৱে আটাইতকৈ ভাল কাম কৰে।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"এক্সট্ৰা ডিম শ্বৰ্টকাট আঁতৰাওক"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"এক্সট্ৰা ডিম শ্বৰ্টকাট আঁতৰোৱা হৈছে। আপোনাৰ উজ্জ্বলতা হ্ৰাস কৰিবলৈ, নিয়মীয়া উজ্জ্বলতা বাৰ ব্যৱহাৰ কৰক।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index d6abf07..b011ca64 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> və digər açıq tətbiqlər bu skrinşotu aşkarladı."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Qeydə əlavə edin"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Keçid daxil edin"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekran yazıcısı"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Qeydəalma başladılsın?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Qeydə aldıqda Android-in ekranda görünən və ya cihazda oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Tətbiq qeydə aldıqda Android-in həmin tətbiqdə göstərilən və ya oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Qeydə almağa başlayın"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran qeydə alınsın?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir tətbiqi qeydə alın"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Bütün ekranı qeydə alın"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Bütün ekranı qeydə alarkən ekranda göstərilən bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Tətbiq qeydə aldıqda həmin tətbiqdə göstərilən və ya işə salınan bütün kontent qeydə alınır. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı qeydə alın"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Qeydə almaq üçün tətbiq seçin"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio yazın"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Cihaz audiosu"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Cihazınızdan gələn musiqi, zənglər və zəng melodiyaları kimi səslər"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Göndərin"</string>
     <string name="cancel" msgid="1089011503403416730">"Ləğv edin"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Tətbiq loqosu"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Təsdiq"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Yenidən cəhd edin"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Doğrulanmanı ləğv etmək üçün toxunun"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran qoruyucu"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Narahat etməyin"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritet rejimləri"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Heç bir cütlənmiş cihaz əlçatan deyil"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toxunaraq cihaza qoşulun, yaxud əlaqəni ayırın"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-u açın"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio paylaşma"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dəyişmək və ya audio paylaşmaq üçün toxunun"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Avtomatik subtitrlər"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarları açın"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Digər cihaz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"İcmala Keçin"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritet rejimləri"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hazırdır"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ayarlayın"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Siqnallar 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>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Fərdiləşdirin"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Asta şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj edilir • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> sonra dolacaq"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Kilid ekranındakı vidcetlər"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti kilid ekranına əlavə edildi"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"İcma təlimatını başlatmaq üçün sola sürüşdürün"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Fərdiləşdirin"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Bağlayın"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> qeydəalma və ya yayım zamanı ekranda görünən, yaxud cihazda oxudulan məlumatlara giriş edə biləcək. Bura parol, ödəniş detalları, foto, mesaj və oxudulan audio kimi məlumatlar daxildir."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Qeydəalma və ya yayım başladılsın?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu funksiyanı təmin edən xidmətin qeydəalma və ya yayım zamanı ekranda görünən, yaxud cihazda oxudulan məlumatlara girişi olacaq. Bura parol, ödəniş detalları, foto, mesaj və oxudulan audio kimi məlumatlar daxildir."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Bütün ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Tək tətbiq"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Tətbiq paylaşın və ya qeydə alın"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilə qeydəalma və ya yayım başladılsın?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Paylaşım, qeydəalma və ya yayım zamanı <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ekranda görünən, yaxud cihazda oxudulan məlumatlara giriş edə bilir. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Tətbiq paylaşdıqda, qeydə aldıqda və ya yayımladıqda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> həmin tətbiqdə göstərilən, yaxud oxudulan məlumatlara giriş edə bilir. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Başlayın"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Tətbiq paylaşın və ya qeydə alın"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekran <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilə paylaşılsın?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bir tətbiq paylaşın"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bütün ekranı paylaşın"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Bütün ekranı paylaşdığınız zaman ekrandakı hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Tətbiq paylaşdığınız zaman həmin tətbiqdə göstərilən və ya işə salınan hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaşın"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçimi deaktiv edib"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Yayım başladılsın?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Yayım zamanı Android-in ekranda görünən, yaxud cihazda oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Tətbiq yayımladıqda Android-in həmin tətbiqdə göstərilən və ya oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Yayıma başlayın"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşmaq üçün tətbiq seçin"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekran yayımlansın?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bir tətbiqi yayımlayın"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Bütün ekranı yayımlayın"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Bütün ekranı yayımladıqda ekrandakı hər şey görünür. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Tətbiq yayımladıqda həmin tətbiqdə göstərilən və ya işə salınan hər şey görünür. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranı yayımlayın"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Yayımlamaq üçün tətbiq seçin"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Paylaşım başladılsın?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Paylaşım, qeydəalma və ya yayım zamanı Android-in ekranda görünən, yaxud cihazda oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Tətbiq paylaşdıqda, qeydə aldıqda və ya yayımladıqda Android-in həmin tətbiqdə göstərilən, yaxud oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Başlayın"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Növbəti"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Tətbiqləri dəyişdikdə paylaşım durdurulur"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Bu tətbiqi paylaşın"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Geri dəyişin"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Peyk, bağlantı yaxşıdır"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Peyk, bağlantı var"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Təcili peyk bağlantısı"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Təcili zənglər və ya SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Hamı üçün deyil, bəziləri üçün əyləncəli"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android istifadəçi interfeysini dəyişdirmək və fərdiləşdirmək üçün Sizə ekstra yollar təklif edir."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Xüsusi imkanlar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatura qısayolları"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Axtarış nəticəsi yoxdur"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri jesti"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Əsas ekran jesti"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Əməliyyat düyməsi"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Əla!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Üç barmağın sağa və sola hərəkət etdiyini göstərən taçped"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri jesti üçün animasiya göstərən cihaz ekranı"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Geri getmək üçün taçpeddə istənilən yerdə üç barmaqla sola və ya sağa çəkin.\n\nBunun üçün Action + ESC klaviatura qısayolundan da istifadə edə bilərsiniz."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Ə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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"İstənilən vaxt ana ekrana keçmək üçün ekranın aşağısından üç barmağınızla yuxarı çəkin."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Əla!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Əsas ekrana keçid jestini tamamladınız."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Fəaliyyət açarı"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Tətbiqlərə daxil olmaq üçün klaviaturada fəaliyyət açarını basın."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Təbriklər!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Fəaliyyət açarı jestini tamamladınız.\n\nFəaliyyət + / əlçatan bütün qısayolları göstərir."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Ana səhifəyə keçmək üçün taçpeddə üç barmağınızla yuxarı çəkin"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Son tətbiqlərə baxmaq üçün taçpeddə üç barmağınızla yuxarı çəkib saxlayın"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Bütün tətbiqlərə baxmaq üçün klaviaturada fəaliyyət açarını basın"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Çıxarılıb"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Baxmaq üçün kiliddən çıxarın"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Geri qayıtmaq üçün taçped istifadə edin"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Üç barmaqla sola və ya sağa çəkin. Daha çox jest öyrənmək üçün toxunun."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Ana səhifəyə keçmək üçün taçped istifadə edin"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç barmaq ilə yuxarı çəkib saxlayın. Daha çox jest öyrənmək üçün toxunun."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bütün tətbiqlərə baxmaq üçün klaviatura istifadə edin"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstənilən vaxt fəaliyyət açarını basın. Daha çox jest öyrənmək üçün toxunun."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Əlavə qaraltma artıq parlaqlıq panelinin bir hissəsidir"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"İndi ekranın yuxarısında parlaqlıq səviyyəsini daha da aşağı salaraq ekranı əlavə qaralda bilərsiniz.\n\nTünd mühitdə olduqda nəticə yaxşı olur."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Əlavə qaraltma qısayolunu silin"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Əlavə qaraltma qısayolu silindi. Parlaqlığı aşağı salmaq üçün adi parlaqlıq panelindən istifadə edin."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 307dcc0..c581fd5 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj u belešku"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Uvrsti link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Snimač ekrana"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađujemo video snimka ekrana"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Želite da započnete snimanje?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju dok snimate. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Kada snimate aplikaciju, Android ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Započni snimanje"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite da snimite ekran?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimi jednu aplikaciju"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimi ceo ekran"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate ceo ekran, snima se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snima se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimi ekran"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Odaberite aplikaciju koju želite da snimite"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snimaj zvuk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk uređaja"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk sa uređaja, na primer, muzika, pozivi i melodije zvona"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
     <string name="cancel" msgid="1089011503403416730">"Otkaži"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip aplikacije"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Probaj ponovo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Dodirnite da biste otkazali potvrdu identiteta"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni režimi"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nije dostupan nijedan upareni uređaj"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Deljenje zvuka"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite da biste prebacili ili delili zvuk"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Titl uživo"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titl uživo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Podešavanja"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključi/isključi pregled"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni režimi"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Podešavanja"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Podesi"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas uznemiravati zvukovi i vibracije osim za alarme. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Vidžeti na zaključanom ekranu"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je dodat na zaključani ekran"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulevo da biste započeli zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodite"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i preuredite vidžete u ovom prostoru"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i preuredite vidžete ovde"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Dugi pritisak za prilagođavanje vidžeta"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodi vidžete"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Želite da počnete snimanje ili prebacivanje?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Ceo ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Jedna aplikacija"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Delite ili snimite aplikaciju"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Želite da počnete snimanje ili prebacivanje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kada delite, snimate ili prebacujete, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kada delite, snimate ili prebacujete aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Delite ili snimite aplikaciju"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite da delite ekran sa aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deli jednu aplikaciju"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli ceo ekran"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada delite ceo ekran, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada delite aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli ekran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila ovu opciju"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite da započnete prebacivanje?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kada prebacujete, Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kada prebacujete aplikaciju, Android ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Započni prebacivanje"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju koju želite da delite"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite da prebacite ekran?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prebaci jednu aplikaciju"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prebaci ceo ekran"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada prebacujete ceo ekran, vidi se sve što je na njemu. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada prebacujete aplikaciju, vidi se sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prebacivanje ekrana"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju koju želite da prebacite"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite da počnete da delite?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kada delite, snimate ili prebacujete, Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kada delite, snimate ili prebacujete aplikaciju, Android ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, i audio i video sadržaj."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Pokreni"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Dalje"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Deljenje se zaustavlja kada menjate aplikacije"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Deli ovu aplikaciju"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Vrati"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, veza je dobra"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć preko satelita"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili hitna pomoć"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Tjuner za korisnički interfejs sistema vam pruža dodatne načine za podešavanje i prilagođavanje Android korisničkog interfejsa. Ove eksperimentalne funkcije mogu da se promene, otkažu ili nestanu u budućim izdanjima. Budite oprezni."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tasterske prečice"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretrage"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za vraćanje"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za početnu stranicu"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Taster radnji"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Odlično!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tačped sa prikazom tri prsta koji se pomeraju udesno i ulevo"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran uređaja sa prikazom animacije pokreta za nazad"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da biste se vratili, prevucite ulevo sa tri prsta bilo gde na tačpedu.\n\nMožete da koristite i tastersku prečicu Alt + ESC za ovo."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Odlično!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da biste otišli na početni ekran u bilo kom trenutku, prevucite nagore od dna ekrana pomoću tri prsta."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Svaka čast!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dovršili ste pokret za povratak na početnu stranicu."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Taster radnji"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da biste pristupili aplikacijama, pritisnite taster radnji na tastaturi."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Dovršili ste pokret pomoću tastera radnji.\n\nRadnja + / prikazuje sve prečice koje su vam dostupne."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Da biste otišli na početni ekran, prevucite nagore sa tri prsta na tačpedu"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Da biste pregledali nedavne aplikacije, prevucite nagore i zadržite sa tri prsta na tačpedu"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Da biste pogledali sve aplikacije, pritisnite taster radnji na tastaturi"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redigovano"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Otključajte za prikaz"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite tačped da biste se vratili"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulevo ili udesno sa tri prsta. Dodirnite da biste videli više pokreta."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite tačped da biste otišli na početni ekran"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite sa tri prsta. Dodirnite da biste videli više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da biste pregledali sve aplikacije"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite taster radnji u bilo kom trenutku. Dodirnite da biste videli više pokreta."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Dodatno zatamnjivanje je sada deo trake za osvetljenost"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Sada možete dodatno da zatamnite ekran smanjivanjem nivoa osvetljenosti pri vrhu ekrana. \n\nOvo najbolje funkcioniše kada ste u tamnom okruženju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ukloni prečicu za dodatno zatamnjivanje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Uklonjena je prečica za dodatno zatamnjivanje. Da biste smanjili osvetljenost, koristite uobičajenu traku za osvetljenost."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 0580d7e..31854f6 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -50,8 +50,8 @@
     <string name="always_use_device" msgid="210535878779644679">"Заўсёды адкрываць <xliff:g id="APPLICATION">%1$s</xliff:g>, калі падключана прылада <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="1977225429341838444">"Заўсёды адкрываць <xliff:g id="APPLICATION">%1$s</xliff:g>, калі падключаны аксесуар <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
     <string name="usb_debugging_title" msgid="8274884945238642726">"Дазволіць адладку па USB?"</string>
-    <string name="usb_debugging_message" msgid="5794616114463921773">"Адбiтак ключа RSA на гэтым камп\'ютары:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
-    <string name="usb_debugging_always" msgid="4003121804294739548">"Заўсёды дазваляць з гэтага камп\'ютара"</string>
+    <string name="usb_debugging_message" msgid="5794616114463921773">"Адбiтак ключа RSA на гэтым камп’ютары:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+    <string name="usb_debugging_always" msgid="4003121804294739548">"Заўсёды дазваляць з гэтага камп’ютара"</string>
     <string name="usb_debugging_allow" msgid="1722643858015321328">"Дазволіць"</string>
     <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Адладка па USB забаронена"</string>
     <string name="usb_debugging_secondary_user_message" msgid="1888835696965417845">"Карыстальнік, які зараз увайшоў у гэтую прыладу, не можа ўключыць адладку цераз USB. Каб выкарыстоўваць гэтую функцыю, пераключыцеся на адміністратара."</string>
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> і іншыя адкрытыя праграмы выявілі гэты здымак экрана."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Дадаць у нататку"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Дадаць спасылку"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Запіс экрана"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Апрацоўваецца запіс экрана"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Пачаць запіс?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Калі адбываецца запіс, Android мае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Калі адбываецца запіс змесціва праграмы, Android мае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Пачаць запіс"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Запісаць экран?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Запісаць адну праграму"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Запісаць змесціва ўсяго экрана"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Пры запісе ўсяго экрана запісваецца ўсё, што паказваецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Пры запісе праграмы запісваецца ўсё, што паказваецца або прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запісаць экран"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Выберыце праграму для запісу"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Запісаць аўдыя"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Аўдыя з прылады"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Гук на вашай прыладзе, напрыклад музыка, выклікі і рынгтоны"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Адправіць"</string>
     <string name="cancel" msgid="1089011503403416730">"Скасаваць"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Лагатып праграмы"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Пацвердзіць"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Паўтарыць спробу"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Націсніце, каб скасаваць аўтэнтыфікацыю"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Экранная застаўка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбаваць"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Прыярытэтныя рэжымы"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма даступных спалучаных прылад"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Націсніце, каб падключыць або адключыць прыладу"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Абагульванне аўдыя"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Націсніце, каб пераключыць або абагуліць аўдыя"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Захавана"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"адключыць"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"актываваць"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Аўтаматычныя субцітры"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Аўтаматычныя субцітры"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Адкрыць налады"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Іншая прылада"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Уключыць/выключыць агляд"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Прыярытэтныя рэжымы"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Гатова"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налады"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Наладзіць"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Дапасаваць"</string>
@@ -478,14 +482,13 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе павольная зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ідзе зарадка • Поўны зарад праз <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Віджэты на экране блакіроўкі"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" дададзены на экран блакіроўкі"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Правядзіце пальцам па экране ўлева, каб азнаёміцца з дапаможнікам"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Наладзіць"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрыць"</string>
     <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Дадаць ці выдаліць віджэты ў гэтай вобласці або змяніць іх парадак"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Дадаць іншыя віджэты"</string>
-    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Доўга націскайце, каб наладзіць віджэты"</string>
+    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Націсніце і ўтрымлівайце, каб наладзіць віджэты"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Наладзіць віджэты"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"Разблакіруйце, каб наладзіць віджэты"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Значок праграмы для адключанага віджэта"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Падчас запісу ці трансляцыі праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" будзе мець доступ да ўсёй інфармацыі, адлюстраванай на экране вашай прылады, ці той, якая праз яе прайграецца. Гэтая інфармацыя ўключае паролі, звесткі пра аплату, фота, паведамленні і аўдыя, якое вы прайграяце."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Пачаць запіс або трансляцыю?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Падчас запісу ці трансляцыі служба, якая забяспечвае работу гэтай функцыі, будзе мець доступ да ўсёй інфармацыі, адлюстраванай на экране вашай прылады, ці той, якая праз яе прайграецца. Гэтая інфармацыя ўключае паролі, плацежных рэквізітаў, фота, паведамленні і аўдыя, якое вы прайграяце."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Увесь экран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Адна праграма"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Абагульванне або запіс праграмы"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Пачаць запіс або трансляцыю з дапамогай праграмы \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Калі пачынаецца абагульванне, запіс ці трансляцыя, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> атрымлівае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Калі пачынаецца абагульванне, запіс ці трансляцыя змесціва праграмы, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> атрымлівае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Пачаць"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Абагульванне або запіс праграмы"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Абагуліць экран з праграмай \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Абагуліць адну праграму"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Абагуліць увесь экран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Калі вы абагульваеце ўвесь экран, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што адбываецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Калі вы абагульваеце праграму, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Абагуліць экран"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" адключыла гэты параметр"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Пачаць трансляцыю?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Калі адбываецца трансляцыя, Android мае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Калі адбываецца трансляцыя змесціва праграмы, Android мае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Пачаць трансляцыю"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выберыце праграму для абагульвання"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Уключыць трансляцыю экрана?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Трансліраваць адну праграму"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Трансліраваць увесь экран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Калі вы трансліруеце ўвесь экран, бачна ўсё, што адбываецца на экране. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Калі вы трансліруеце праграму, бачна ўсё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Трансліраваць экран"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Выберыце праграму для трансляцыі"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Пачаць абагульванне?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Калі адбываецца абагульванне, запіс ці трансляцыя, Android мае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Калі адбываецца абагульванне, запіс ці трансляцыя змесціва праграмы, Android мае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Пачаць"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Далей"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Пры пераключэнні праграм абагульванне прыпыняецца"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Абагуліць гэту праграму"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Пераключыцца назад"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спадарожнікавая сувязь, добрае падключэнне"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спадарожнікавая сувязь, падключэнне даступнае"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Экстраннае спадарожнікавае падключэнне"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Экстранныя выклікі або экстраннае спадарожнікавае падключэнне"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Працоўны профіль"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Цікава для некаторых, але не для ўсіх"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Наладка сістэмнага інтэрфейсу карыстальніка дае вам дадатковыя спосабы наладжвання і дапасоўвання карыстальніцкага інтэрфейсу Android. Гэтыя эксперыментальныя функцыі могуць змяніцца, перастаць працаваць або знікнуць у будучых версіях. Карыстайцеся з асцярожнасцю."</string>
@@ -741,10 +754,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Стан:&lt;/b&gt; Пераведзена ў рэжым \"Без гуку\""</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;Стан:&lt;/b&gt; Ацэнена як важнае"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;Стан:&lt;/b&gt; Ацэнена як няважнае"</string>
-    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"З\'яўляецца ўверсе раздзела размоў і паказвае на экране блакіроўкі відарыс профілю"</string>
-    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"З\'яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое паказвае на экране блакіроўкі відарыс профілю"</string>
-    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"З\'яўляецца ўверсе раздзела размоў, перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string>
-    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З\'яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string>
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"З’яўляецца ўверсе раздзела размоў і паказвае на экране блакіроўкі відарыс профілю"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"З’яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое паказвае на экране блакіроўкі відарыс профілю"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"З’яўляецца ўверсе раздзела размоў, перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"З’яўляецца ўверсе раздзела размоў як усплывальнае апавяшчэнне, якое перарывае рэжым \"Не турбаваць\" і паказвае на экране блакіроўкі відарыс профілю"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Прыярытэт"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> не падтрымлівае функцыі размовы"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Гэтыя апавяшчэнні нельга змяніць."</string>
@@ -1056,7 +1069,7 @@
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string>
     <string name="accessibility_floating_button_undo" msgid="511112888715708241">"Адрабіць"</string>
     <string name="accessibility_floating_button_hidden_notification_title" msgid="4115036997406994799">"Кнопка спецыяльных магчымасцей схавана"</string>
-    <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Калі вы хочаце, каб яна з\'явілася, націсніце на апавяшчэнне"</string>
+    <string name="accessibility_floating_button_hidden_notification_text" msgid="1457021647040915658">"Калі вы хочаце, каб яна з’явілася, націсніце на апавяшчэнне"</string>
     <string name="accessibility_floating_button_undo_message_label_text" msgid="9017658016426242640">"Выдалены ярлык <xliff:g id="FEATURE_NAME">%s</xliff:g>"</string>
     <string name="accessibility_floating_button_undo_message_number_text" msgid="4909270290725226075">"{count,plural, =1{Выдалены # ярлык}one{Выдалены # ярлык}few{Выдалена # ярлыкі}many{Выдалена # ярлыкоў}other{Выдалена # ярлыка}}"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string>
@@ -1210,7 +1223,7 @@
     <string name="game_status" msgid="1340694320630973259">"Ідзе гульня"</string>
     <string name="empty_user_name" msgid="3389155775773578300">"Сябры"</string>
     <string name="empty_status" msgid="5938893404951307749">"Паразмаўляем у чаце!"</string>
-    <string name="status_before_loading" msgid="1500477307859631381">"Неўзабаве з\'явіцца змесціва"</string>
+    <string name="status_before_loading" msgid="1500477307859631381">"Неўзабаве з’явіцца змесціва"</string>
     <string name="missed_call" msgid="4228016077700161689">"Прапушчаны выклік"</string>
     <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
     <string name="people_tile_description" msgid="8154966188085545556">"Глядзець нядаўнія паведамленні, прапушчаныя выклікі і абнаўленні стану"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Спецыяльныя магчымасці"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Спалучэнні клавіш"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма вынікаў пошуку"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жэст для вяртання на папярэдні экран"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жэст для вяртання на галоўны экран"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавіша дзеяння"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Цудоўна!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Паказваецца, як на сэнсарнай панэлі тры пальцы рухаюцца ўправа і ўлева"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"На экране прылады паказваецца анімацыя жэста \"Назад\""</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Каб вярнуцца, правядзіце трыма пальцамі ўлева ці ўправа ў любым месцы сэнсарнай панэлі.\n\nТаксама можна выкарыстоўваць спалучэнне \"клавіша дзеяння + ESC\"."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Выдатная праца!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы навучыліся рабіць жэст вяртання."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На галоўны экран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Каб у любы момант перайсці на галоўны экран, правядзіце па экране трыма пальцамі знізу ўверх."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Выдатна!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Вы навучыліся рабіць жэст для пераходу на галоўны экран."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавіша дзеяння"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Каб атрымаць доступ да праграм, націсніце клавішу дзеяння на клавіятуры."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Віншуем!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Вы навучыліся рабіць жэст з клавішай дзеяння.\n\nКалі націснуць клавішу дзеяння разам з \"/\", будуць паказаны ўсе даступныя спалучэнні клавіш."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Каб вярнуцца на галоўны экран, правядзіце па сэнсарнай панэлі трыма пальцамі ўверх"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Для прагляду нядаўніх праграм правядзіце па сэнсарнай панэлі трыма пальцамі ўверх і затрымайцеся"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Каб праглядзець усе праграмы, націсніце на клавішу дзеяння на клавіятуры"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Схавана"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Разблакіруйце экран, каб праглядзець"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Выкарыстайце сэнсарную панэль для вяртання"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Правядзіце ўлева ці ўправа трыма пальцамі. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Выкарыстайце сэнсарную панэль для вяртання на галоўны экран"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Правядзіце трыма пальцамі ўверх і затрымайце пальцы. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Выкарыстайце клавіятуру для прагляду ўсіх праграм"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Можна націснуць на клавішу дзеяння ў любы момант. Націсніце, каб азнаёміцца з іншымі жэстамі."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Цяпер кіраваць дадатковым памяншэннем яркасці можна на панэлі яркасці"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Цяпер вы можаце дадаткова зацямніць экран, яшчэ больш панізіўшы ўзровень яркасці праз налады ўверсе экрана.\n\nГэта функцыя працуе лепш за ўсё ў цёмным асяроддзі."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Выдаліць хуткую каманду для дадатковага памяншэння яркасці"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Хуткая каманда для дадатковага памяншэння яркасці выдалена. Каб паменшыць яркасць, выкарыстоўвайце звычайную панэль яркасці."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index dfa9598..64ecfd3 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и други отворени приложения установиха заснемането на тази екранна снимка."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Добавяне към бележката"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Включване на връзката"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Запис на екрана"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Записът на екрана се обработва"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Искате ли да стартирате записване?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Когато записвате, Android има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Докато записвате дадено приложение, Android има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Стартиране на записването"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се записва ли екранът?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записване на едно приложение"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записване на целия екран"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Когато записвате целия си екран, се записва всичко, което се показва на него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Когато записвате приложение, се записва всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записване на екрана"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Изберете приложение за записване"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Записване на звук"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Звук от устройството"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук от устройството ви, като например музика, обаждания и мелодии"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Изпращане"</string>
     <string name="cancel" msgid="1089011503403416730">"Отказ"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Лого на приложението"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потвърждаване"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Нов опит"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Докоснете, за да анулирате удостоверяването"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Скрийнсейвър"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не безпокойте"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Няма налични сдвоени устройства"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Докоснете, за да свържете устройство или да прекъснете връзката му"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Споделяне на звука"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Докоснете, за да превключите или споделите аудио"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Запазено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекратяване на връзката"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активиране"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string>
     <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_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>
@@ -387,8 +392,8 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string>
     <string name="performance" msgid="6552785217174378320">"Ефективност"</string>
     <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string>
-    <string name="thermal" msgid="6758074791325414831">"Термално"</string>
-    <string name="custom" msgid="3337456985275158299">"Персонализирано"</string>
+    <string name="thermal" msgid="6758074791325414831">"Температура"</string>
+    <string name="custom" msgid="3337456985275158299">"Персонализиране"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Настройки за персонализираната следа"</string>
     <string name="restore_default" msgid="5259420807486239755">"Възстановяване на стандартната настройка"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Надписи на живо"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Надписи на живо"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Към настройките"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Друго устройство"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Превключване на общия преглед"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Настройване"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници. Ще чувате обаче всичко, което изберете пуснете, включително музика, видеоклипове и игри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Персонализиране"</string>
@@ -478,10 +482,9 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се бавно • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарежда се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до пълно зареждане"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Приспособления на заключения екран"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Приспособлението <xliff:g id="WIDGET_NAME">%1$s</xliff:g> бе добавено на заключения екран"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Прекарайте пръст наляво, за да стартирате общия урок"</string>
-    <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Персонализиране"</string>
+    <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Персонализи­ране"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Отхвърляне"</string>
     <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Добавяйте, премахвайте и пренареждайте приспособленията си в тази област"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавете още приспособления"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, подробности за начини на плащане, снимки, съобщения и възпроизвеждано аудио."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Искате ли да стартирате записване или предаване?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услугата, предоставяща тази функция, ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, подробности за начини на плащане, снимки, съобщения и възпроизвеждано аудио."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Цял екран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Едно приложение"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Споделяне или записване на приложение"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Искате ли да стартирате записване или предаване чрез <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Когато споделяте, записвате или предавате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Когато споделяте, записвате или предавате дадено приложение, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Стартиране"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Споделяне или записване на приложение"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Да се сподели ли екранът ви с(ъс) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Споделяне на едно приложение"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделяне на целия екран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Когато споделяте целия си екран, всичко, което се показва на него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Когато споделяте приложение, всичко, което се показва или възпроизвежда в него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Споделяне на екрана"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> деактивира тази опция"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Искате ли да стартирате предаване?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Когато предавате, Android има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Когато предавате дадено приложение, Android има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Стартиране на предаването"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете приложение за споделяне"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Искате ли да предавате екрана си?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Предаване на едно приложение"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Предаване на целия екран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Когато предавате целия екран, всичко, което се показва на него, е видимо. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Когато предавате дадено приложение, всичко, което се показва или възпроизвежда в него, е видимо. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Предаване на екрана"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Изберете приложение за предаване"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Искате ли да стартирате споделяне?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Когато споделяте, записвате или предавате, Android има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Когато споделяте, записвате или предавате дадено приложение, Android има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Стартиране"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Напред"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Споделянето се поставя на пауза, когато превключвате приложенията"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Вместо това споделете приложението"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Превключване обратно"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, добра връзка"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, налице е връзка"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS чрез сателит"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Спешни обаждания или SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Потребителски профил в Work"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Забавно – но не за всички"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Тунерът на системния потребителски интерфейс ви предоставя допълнителни възможности за прецизиране и персонализиране на практическата работа с Android. Тези експериментални функции може да се променят, повредят или да изчезнат в бъдещите версии. Действайте внимателно."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Достъпност"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Клавишни комбинации"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Няма резултати от търсенето"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест за връщане назад"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест за преминаване към началния екран"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавиш за действия"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Отлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Сензорен панел, върху който три пръста се движат надясно и наляво"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран на устройство, показващ анимация за жеста за връщане назад"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"За да се върнете назад, прекарайте три пръста наляво или надясно по сензорния панел.\n\nЗа целта можете също да използвате комбинацията с клавиша за действия + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Отлично!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Изпълнихте жеста за връщане назад."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Към началния екран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"За да преминете към началния екран по всяко време, прекарайте три пръста нагоре от долната част на екрана."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Чудесно!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Изпълнихте жеста за преминаване към началния екран."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавиш за действия"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"За да осъществите достъп до приложенията, натиснете клавиша за действия на клавиатурата си."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Поздравления!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Изпълнихте жеста с клавиша за действия.\n\nНатискането на клавиша за действия и / показва всички налични клавишни комбинации."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"За да преминете към началния екран, плъзнете нагоре по сензорния панел с три пръста"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"За да прегледате скорошните приложения, плъзнете нагоре по сензорния панел с три пръста и задръжте"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"За да прегледате всичките си приложения, натиснете клавиша за действия на клавиатурата си"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Скрито"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Отключете за преглед"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Използвайте сензорния панел, за да се върнете назад"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Плъзнете три пръста наляво или надясно. Докоснете, за да научите повече жестове."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Използвайте сензорния панел, за да преминете към началния екран"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Плъзнете нагоре с три пръста и задръжте. Докоснете, за да научите повече жестове."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Използвайте клавиатурата, за да прегледате всички приложения"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Натиснете клавиша за действия по всяко време. Докоснете, за да научите повече жестове."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Функцията за допълнителнително затъмняване вече е част от лентата за яркостта"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Вече можете да затъмнявате екрана допълнително, като намалявате яркостта още повече от лентата в горната му част.\n\nТова е най-полезно, когато сте на тъмно място."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Премахване на прекия път за допълнително затъмняване"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Прекият път за допълнително затъмняване е премахнат. За да намалите яркостта, използвайте обикновената лента за управлението ѝ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 3f6c269..f62ee9e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> এবং খোলা থাকা অন্য অ্যাপ এই স্ক্রিনশট শনাক্ত করেছে।"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"নোটে যোগ করুন"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"লিঙ্ক যোগ করুন"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"স্ক্রিন রেকর্ডার"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"স্ক্রিন রেকর্ডিং প্রসেস হচ্ছে"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"রেকর্ডিং শুরু করবেন?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"আপনি রেকর্ড করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"আপনি কোনও অ্যাপের মাধ্যমে রেকর্ড করার সময়, ওই অ্যাপে দেখানো বা চালানো হয় এমন সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো জিনিস সম্পর্কে সতর্ক থাকুন।"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"রেকর্ড করা শুরু করুন"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপনার স্ক্রিন রেকর্ড করবেন?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"একটি অ্যাপ রেকর্ড করুন"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"সম্পূর্ণ স্ক্রিন রেকর্ড করুন"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"আপনার সম্পূর্ণ স্ক্রিন রেকর্ড করার সময়, আপনার স্ক্রিনে দেখানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"আপনি কোনও অ্যাপ রেকর্ড করার সময়, সেই অ্যাপে দেখানো বা চালানো সব কিছু রেকর্ড করা হয়। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"স্ক্রিন রেকর্ড করুন"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"রেকর্ড করার জন্য অ্যাপ বেছে নিন"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"অডিও রেকর্ড করুন"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ডিভাইস অডিও"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"মিউজিক, কল এবং রিংটোনগুলির মতো আপনার ডিভাইস থেকে সাউন্ড"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"পাঠান"</string>
     <string name="cancel" msgid="1089011503403416730">"বাতিল করুন"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"অ্যাপের লোগো"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"কনফার্ম করুন"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"আবার চেষ্টা করুন"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"যাচাইকরণ বাতিল করতে ট্যাপ করুন"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"স্ক্রিন সেভার"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ইথারনেট"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"বিরক্ত করবে না"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"প্রায়োরিটি মোড"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ব্লুটুথ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"চেনা কোনও ডিভাইস নেই"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"কোনও ডিভাইস কানেক্ট বা ডিসকানেক্ট করতে ট্যাপ করুন"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"অডিও শেয়ারিং"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"অডিও শেয়ার বা পরিবর্তন করতে ট্যাপ করুন"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"লাইভ ক্যাপশন"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ ক্যাপশন"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"সেটিংস খুলুন"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"অন্য ডিভাইস"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"\'এক নজরে\' বৈশিষ্ট্যটি চালু বা বন্ধ করুন"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"প্রায়োরিটি মোড"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"হয়ে গেছে"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"সেটিংস"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"সেট-আপ করুন"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"অ্যালার্ম ছাড়া অন্য কোনও আওয়াজ বা ভাইব্রেশন হবে না। তবে সঙ্গীত, ভিডিও, এবং গেম সহ আপনি যা কিছু চালাবেন তার আওয়াজ শুনতে পাবেন।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"কাস্টমাইজ করুন"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জ হচ্ছে • পুরো চার্জ হতে আরও <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> সময় লাগবে"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"লক স্ক্রিনে উইজেট"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট লক স্ক্রিনে যোগ করা হয়েছে"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"কমিউনিটি টিউটোরিয়াল চালু করতে বাঁদিকে সোয়াইপ করুন"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"কাস্টমাইজ করুন"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"বাতিল করুন"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"উইজেট যোগ করুন"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"হয়ে গেছে"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"উইজেট যোগ করুন"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"নিজের ট্যাবলেট আনলক বা করেই আপনার প্রিয় অ্যাপ উইজেটে দ্রুত অ্যাক্সেস পান।"</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"নিজের ট্যাবলেট আনলক না করেই আপনার প্রিয় অ্যাপ উইজেটে দ্রুত অ্যাক্সেস পান।"</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"লক স্ক্রিনে যেকোনও উইজেটকে অনুমতি দেবেন?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"সেটিংস খুলুন"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"অফিসের অ্যাপ আনপজ করতে চান?"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"আপনার ট্যাবলেট লক থাকলেও যেকোনও ব্যক্তি লক স্ক্রিনে উইজেট দেখতে পাবেন।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"উইজেট বাদ দিন"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"লক স্ক্রিন উইজেট"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, এমনকি আপনার ট্যাবলেট লক থাকাকালীন যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"উইজেট ব্যবহার করে কোনও অ্যাপ খুলতে, আপনাকে নিজের পরিচয় যাচাই করতে হবে। এছাড়াও, মনে রাখবেন, আপনার ট্যাবলেট লক থাকলেও যেকেউ তা দেখতে পারবেন। কিছু উইজেট আপনার লক স্ক্রিনের উদ্দেশ্যে তৈরি করা হয়নি এবং এখানে যোগ করা নিরাপদ নাও হতে পারে।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"বুঝেছি"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"পুলডাউন মেনু"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"রেকর্ড বা কাস্ট করার সময় স্ক্রিনে বা ডিভাইসে দৃশ্যমান সব তথ্য <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> অ্যাক্সেস করতে পারবে। এর মধ্যে আপনার পাসওয়ার্ড, পেমেন্টের বিবরণ, ফটো, মেসেজ এবং আপনার চালানো অডিও সম্পর্কিত তথ্য রয়েছে।"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"রেকর্ডিং বা কাস্টিং শুরু করতে চান?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"যে পরিষেবা এই ফাংশন প্রদান করছে, সেটি রেকর্ড বা কাস্ট করার সময় আপনার স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো হয়েছে এমন সব তথ্য অ্যাক্সেস করতে পারবে। এর মধ্যে আপনার পাসওয়ার্ড, পেমেন্টের বিবরণ, ফটো, মেসেজ এবং আপনার চালানো অডিও সম্পর্কিত তথ্য রয়েছে।"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"পুরো স্ক্রিন"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"একটি অ্যাপ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"অ্যাপ শেয়ার বা রেকর্ড করুন"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ব্যবহার করে রেকর্ডিং বা কাস্টিং শুরু করবেন?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"আপনি শেয়ার, রেকর্ড বা কাস্ট করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সব কিছুই <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"আপনি কোনও অ্যাপ শেয়ার, রেকর্ড বা কাস্ট করার সময়, সেই অ্যাপে দেখা যায় বা চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"শুরু করুন"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"কোনও অ্যাপ শেয়ার বা রেকর্ড করুন"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-এর সাথে আপনার স্ক্রিন শেয়ার করবেন?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"একটি অ্যাপ শেয়ার করুন"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"সম্পূর্ণ স্ক্রিন শেয়ার করুন"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"আপনার সম্পূর্ণ স্ক্রিন শেয়ার করার সময়, স্ক্রিনে থাকা সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"কোনও অ্যাপ শেয়ার করার সময়, সেই অ্যাপে দেখা ও চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্রিন শেয়ার করুন"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> এই বিকল্পটি বন্ধ করে দিয়েছে"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"কাস্ট করা শুরু করবেন?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"আপনি কাস্ট করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"আপনি কোনও অ্যাপ কাস্ট করার সময়, ওই অ্যাপে দেখানো বা চালানো হয় এমন সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"কাস্ট করা শুরু করুন"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শেয়ার করার জন্য অ্যাপ বেছে নিন"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপনার স্ক্রিন কাস্ট করুন?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"একটি অ্যাপ কাস্ট করুন"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"সম্পূর্ণ স্ক্রিন কাস্ট করুন"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"আপনি সম্পূর্ণ স্ক্রিন কাস্ট করলে, আপনার স্ক্রিনে থাকা সব কিছুই দেখা যাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"আপনি কোনও অ্যাপ কাস্ট করলে, ওই অ্যাপে কিছু দেখানো বা চালানো হলে তা দেখা যাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"স্ক্রিন কাস্ট করুন"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"কাস্ট করার জন্য অ্যাপ বেছে নিন"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"শেয়ার করা শুরু করবেন?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"আপনি শেয়ার, রেকর্ড বা কাস্ট করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সব কিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"আপনি কোনও অ্যাপ শেয়ার, রেকর্ড বা কাস্ট করার সময়, সেই অ্যাপে দেখা যায় বা চালানো হয় এমন সব কিছু Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"শুরু করুন"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"পরবর্তী"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"আপনি অ্যাপ পাল্টানোর সময় শেয়ারিং পজ করা হয়"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"এর পরিবর্তে এই অ্যাপ শেয়ার করুন"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"আবার পাল্টান"</string>
@@ -588,7 +600,7 @@
     <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এর মাধ্যমে আপনার ব্যক্তিগত অ্যাপ ইন্টারনেটের সাথে কানেক্ট করা আছে"</string>
     <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এর মাধ্যমে এই ডিভাইস ইন্টারনেটের সাথে কানেক্ট করা আছে"</string>
     <string name="monitoring_title_financed_device" msgid="3659962357973919387">"এই ডিভাইস <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> দিয়েছে"</string>
-    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"ডিভাইসের পরিচালনা"</string>
+    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"ডিভাইস ম্যানেজমেন্ট"</string>
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"নেটওয়ার্ক লগিং"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA সার্টিফিকেট"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"স্যাটেলাইট, ভালো কানেকশন"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"স্যাটেলাইট, কানেকশন উপলভ্য আছে"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"স্যাটেলাইট SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"জরুরি কল বা SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"কাজের প্রোফাইল"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"কিছু ব্যক্তির জন্য মজাদার কিন্তু সকলের জন্য নয়"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"এই পরীক্ষামূলক বৈশিষ্ট্যগুলি ভবিষ্যতের সংস্করণগুলির মধ্যে পরিবর্তিত, বিভাজিত এবং অদৃশ্য হয়ে যেতে পারে৷ সাবধানতার সাথে এগিয়ে যান৷ সিস্টেম UI টিউনার আপনাকে Android ব্যবহারকারী ইন্টারফেসের সূক্ষ্ম সমন্বয় এবং কাস্টমাইজ করার অতিরিক্ত উপায়গুলি প্রদান করে৷"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"অ্যাক্সেসিবিলিটি"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"কীবোর্ড শর্টকাট"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"কোনও সার্চ ফলাফল নেই"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ফিরে যাওয়ার জেসচার"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"হোমপেজে যাওয়ার জেসচার"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"অ্যাকশন কী"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হয়ে গেছে"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"অসাধারণ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ফিরে যান"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"টাচপ্যাডে, তিনটি আঙুল ডান ও বাঁদিকে সরানো দেখানো হচ্ছে"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ডিভাইসের স্ক্রিনে ফিরে যাওয়ার জেসচারের অ্যানিমেশন দেখানো হচ্ছে"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ফিরে যেতে, টাচপ্যাডে যেকোনও জায়গায় তিনটি আঙুল দিয়ে বাঁদিক বা ডানদিকে সোয়াইপ করুন।\n\nএছাড়া, এটির জন্য আপনি কীবোর্ড শর্টকাট অ্যাকশন + ESC বোতাম প্রেস করতে পারবেন।"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"অসাধারণ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"জেসচার ব্যবহার করে কীভাবে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন।"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"হোমে যান"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"যেকোনও সময়ে আপনার হোম স্ক্রিনে যেতে, আপনার স্ক্রিনের একদম নিচের থেকে তিনটি আঙুল দিয়ে উপরের দিকে সোয়াইপ করুন।"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"সাবাস!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"জেসচার ব্যবহার করে কীভাবে হোমে ফিরে যাওয়া যায় সেই সম্পর্কে আপনি জেনেছেন।"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"অ্যাকশন কী"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"আপনার অ্যাপ অ্যাক্সেস করতে, কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"অভিনন্দন!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"অ্যাকশন কী জেসচার সম্পর্কে আপনি জেনেছেন।\n\nঅ্যাকশন + / প্রেস করলে আপনার কাছে উপলভ্য থাকা সব শর্টকাট দেখতে পাবেন।"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"হোমে যেতে, টাচপ্যাডে তিনটি আঙুল দিয়ে উপরের দিকে সোয়াইপ করুন"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"সম্প্রতি ব্যবহার করা অ্যাপ দেখতে, টাচপ্যাডে তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"আপনার সব অ্যাপ দেখতে, কীবোর্ডে অ্যাকশন কী প্রেস করুন"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"রিড্যাক্ট করা হয়েছে"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"দেখার জন্য আনলক করুন"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ফিরে যেতে টাচপ্যাড ব্যবহার করুন"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"তিনটি আঙুলের ব্যবহার করে ডান বা বাঁদিকে সোয়াইপ করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"হোমে যেতে টাচপ্যাড ব্যবহার করুন"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"তিনটি আঙুল ব্যবহার করে উপরের দিকে সোয়াইপ করে ধরে রাখুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"সব অ্যাপ দেখতে আপনার কীবোর্ড ব্যবহার করুন"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"যেকোনও সময় অ্যাকশন কী প্রেস করুন। আরও জেসচার সম্পর্কে জানতে ট্যাপ করুন।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচার এখন ব্রাইটনেস বারের একটি অংশ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"আপনি এখন স্ক্রিনের উপর থেকে ব্রাইটনেস লেভেল কমিয়েও, স্ক্রিন অতিরিক্ত কম ব্রাইট করতে পারবেন।\n\nআপনি অন্ধকার পরিবেশে থাকলে এটি সবথেকে ভালো কাজ করে।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরান"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরানো হয়েছে। আপনার ব্রাইটনেস কম করতে, সাধারণ ব্রাইটনেস বার ব্যবহার করুন।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 2bc13c0..51416ef 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije su otkrile ovaj snimak ekrana."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj u bilješku"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Uključi link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Snimač ekrana"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrađivanje snimka ekrana"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Pokrenuti snimanje?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Dok snimate, Android ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Dok snimate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Pokreni snimanje"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Snimati ekran?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimaj jednu aplikaciju"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimaj cijeli ekran"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kada snimate cijeli ekran, snimat će se sve što se prikazuje na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kada snimate aplikaciju, snimat će se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimaj ekran"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Odaberite aplikaciju koju želite snimati"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snimanje zvuka"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk na uređaju"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk s vašeg uređaja, npr. muzika, pozivi i melodije zvona"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
     <string name="cancel" msgid="1089011503403416730">"Otkaži"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip aplikacije"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Pokušaj ponovo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Dodirnite da otkažete autentifikaciju"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar ekrana"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne ometaj"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini rada"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nema dostupnih uparenih uređaja"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da povežete ili prekinete povezanost uređaja"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Dijeljenje zvuka"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite da uključite ili dijelite zvučni zapis"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
     <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_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>
@@ -378,8 +383,8 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje ekrana"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Započnite"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavite"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite problem"</string>
-    <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori Postavke"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Drugi uređaj"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pregled uključivanja/isključivanja"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini rada"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Postavite"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas ometati zvukovi i vibracije, osim alarma. I dalje ćete čuti sve što izaberete za reprodukciju, uključujući muziku, videozapise i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Vidžeti na zaključanom ekranu"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je dodan na zaključani ekran"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prevucite ulijevo da pokrenete zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodite"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i promijenite raspored vidžeta u ovom prostoru"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Dodajte, uklonite i preuredite vidžete u prostoru"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Dodajte još vidžeta"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pritisnite i zadržite da prilagodite vidžete"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prilagodite vidžete"</string>
@@ -491,7 +494,7 @@
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Ikona aplikacije za onemogućeni vidžet"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"Ikona aplikacije za vidžet koji se instalira"</string>
     <string name="edit_widget" msgid="9030848101135393954">"Uredite vidžet"</string>
-    <string name="button_to_remove_widget" msgid="3948204829181214098">"Uklanjanje"</string>
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Ukloni"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Dodajte vidžet"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Gotovo"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Dodajte vidžet"</string>
@@ -509,7 +512,7 @@
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Vidžeti na zaključanom ekranu"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Svi mogu pregledati vidžete na zaključanom ekranu, čak i ako je tablet zaključan."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"poništavanje odabira vidžeta"</string>
-    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti zaključanog ekrana"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Vidžeti na zaključanom ekranu"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Da otvorite aplikaciju pomoću vidžeta, morat ćete potvrditi identitet. Također imajte na umu da ih svako može pregledati, čak i ako je tablet zaključan. Neki vidžeti možda nisu namijenjeni za vaš zaključani ekran i njihovo dodavanje ovdje možda nije sigurno."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Razumijem"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje su vidljive na ekranu ili koje se reproduciraju s uređaja tokom snimanja ili emitiranja. To uključuje informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Pokrenuti snimanje ili emitiranje?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje su vidljive na ekranu ili koje se reproduciraju s uređaja tokom snimanja ili emitiranja. To uključuje informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Cijeli ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Jedna aplikacija"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Dijelite ili snimite aplikaciju"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Pokrenuti snimanje ili emitiranje uz aplikaciju <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kada dijelite, snimate ili emitirate, aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kada dijelite, snimate ili emitirate aplikaciju, aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dijelite ili snimajte aplikaciju"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Dijeliti ekran s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dijeli jednu aplikaciju"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeli cijeli ekran"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli ekran, sve što je na ekranu će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeli ekran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila tu opciju"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Pokrenuti emitiranje?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kada emitirate, Android ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kada emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Pokreni emitiranje"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju koju želite dijeliti"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Emitirati ekran?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiraj jednu aplikaciju"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitiraj cijeli ekran"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada emitirate cijeli ekran, vidljivo je sve što je na ekranu. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada emitirate aplikaciju, vidljivo je sve što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiraj ekran"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju koju želite emitirati"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Pokrenuti dijeljenje?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kada dijelite, snimate ili emitirate, Android ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kada dijelite, snimate ili emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Pokreni"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Naprijed"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Dijeljenje se pauzira kada promijenite aplikaciju"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Umjesto toga, dijeli aplikaciju"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Vrati se"</string>
@@ -669,7 +681,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Prostorni zvuk"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Isključi"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Hitna pomoć putem satelita"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili pomoć"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Radni profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Podešavač za korisnički interfejs sistema vam omogućava dodatne načine da podesite i prilagodite Androidov interfejs. Ove eksperimentalne funkcije se u budućim verzijama mogu mijenjati, kvariti ili nestati. Budite oprezni."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Prečice tastature"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za povratak na početni ekran"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tipka radnji"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sjajno!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se pomjeraju desno-lijevo"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran uređaja pokazuje animaciju pokreta unazad"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da se vratite, prevucite ulijevo ili udesno s tri prsta bilo gdje na dodirnoj podlozi.\n\nZa ovo možete koristiti i radnju za prečicu i Esc na tastaturi."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Sjajno!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da odete na početni ekran bilo kada, prevucite s dna ekrana nagore s tri prsta."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Lijepo!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Savladali ste pokret za odlazak na početni ekran."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka radnji"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da pristupite aplikacijama, pritisnite tipku radnji na tastaturi."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Savladali ste pokret za tipku radnji.\n\nRadnja + / prikazuje sve prečice koje su vam dostupne."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Da se vratite na početnu stranicu, prevucite nagore s tri prsta na dodirnoj podlozi"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Da pregledate nedavne aplikacije, prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Da pregledate sve aplikacije, pritisnite tipku radnji na tastaturi"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redigovano"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Otključajte da pregledate"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Koristite dodirnu podlogu da se vratite"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Prevucite ulijevo ili udesno s tri prsta. Dodirnite da saznate za više pokreta."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Koristite dodirnu podlogu da se vratite na početnu stranicu"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prevucite nagore i zadržite s tri prsta. Dodirnite da saznate za više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Koristite tastaturu da pregledate sve aplikacije"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku radnji bilo kada. Dodirnite da saznate za više pokreta."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Dodatno zatamnjenje je sada dio trake za osvijetljenost"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Sada možete dodatno zatamniti ekran daljnjim smanjenjem nivoa osvijetljenosti s vrha ekrana.\n\nOvo najbolje funkcionira u tamnom okruženju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ukloni prečicu za dodatno zatamnjenje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Prečica za dodatno zatamnjenje je uklonjena. Da smanjite osvijetljenost, koristite običnu traku za osvijetljenost."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index a9ab9fc..182fe02 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i altres aplicacions obertes han detectat aquesta captura de pantalla."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Afegeix a una nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inclou l\'enllaç"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Gravació de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Vols iniciar una gravació?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Mentre graves, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Mentre graves una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Inicia una gravació"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vols gravar la pantalla?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grava tota la pantalla"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quan graves tota la pantalla, es grava tot el que es mostra en pantalla. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quan graves una aplicació, es grava tot el que es mostra o es reprodueix en aquesta aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grava la pantalla"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Tria una aplicació per gravar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envia"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancel·la"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip de l\'aplicació"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirma"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Torna-ho a provar"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toca per cancel·lar l\'autenticació"</string>
@@ -290,15 +292,17 @@
     <string name="start_dreams" msgid="9131802557946276718">"Estalvi de pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestis"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaris"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hi ha dispositius vinculats  disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca per connectar o desconnectar un dispositiu"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Vincula un dispositiu nou"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostra-ho tot"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza\'l"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza el Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartició d\'àudio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca per canviar o compartir l\'àudio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtítols instantanis"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítols instantanis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Obre Configuració"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Un altre dispositiu"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activa o desactiva Aplicacions recents"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaris"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fet"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuració"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configura"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalitza"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets a la pantalla de bloqueig"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"El widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> s\'ha afegit a la pantalla de bloqueig"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Llisca cap a l\'esquerra per iniciar el tutorial de la comunitat"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalitza"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignora"</string>
@@ -507,7 +510,7 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"suprimeix el widget"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"col·loca el widget seleccionat"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets de la pantalla de bloqueig"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure widgets a la pantalla de bloqueig, fins i tot amb la tauleta bloquejada."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Tothom pot veure els widgets de la teva pantalla de bloqueig, fins i tot quan la tauleta està bloquejada."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desselecciona el widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de la pantalla de bloqueig"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Per obrir una aplicació utilitzant un widget, necessitaràs verificar la teva identitat. També has de tenir en compte que qualsevol persona pot veure els widgets, fins i tot quan la tauleta està bloquejada. És possible que alguns widgets no estiguin pensats per a la pantalla de bloqueig i que no sigui segur afegir-los-hi."</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tindrà accés a tota la informació que es veu en pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio que reprodueixis."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vols començar a gravar o emetre contingut?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servei que ofereix aquesta funció tindrà accés a tota la informació visible a la teva pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara les contrasenyes, les dades de pagament, les fotos, els missatges i àudio que reprodueixis."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tota la pantalla"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Una sola aplicació"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Comparteix o grava una aplicació"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vols començar a gravar o emetre contingut amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quan comparteixes, graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quan comparteixes, graves o emets una aplicació, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Inicia"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Comparteix o grava una aplicació"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vols compartir la pantalla amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Comparteix una aplicació"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Comparteix tota la pantalla"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quan comparteixes tota la pantalla, qualsevol cosa que es mostra en pantalla és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Comparteix la pantalla"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha desactivat aquesta opció"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vols iniciar una emissió?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quan emets contingut, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quan emets una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Inicia una emissió"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Tria una aplicació per compartir"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emet tota la pantalla"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que es mostra en pantalla és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emet la pantalla"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Tria una aplicació per emetre"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vols començar a compartir?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quan comparteixes, graves o emets contingut, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quan comparteixes, graves o emets una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Inicia"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Següent"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"La compartició es posa en pausa quan canvies d\'aplicació"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Comparteix aquesta aplicació"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Torna a canviar"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satèl·lit, bona connexió"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satèl·lit, connexió disponible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS per satèl·lit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Trucades d\'emergència o SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de treball"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversió per a uns quants, però no per a tothom"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilitat"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tecles de drecera"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hi ha cap resultat de la cerca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest Enrere"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest Inici"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla d\'acció"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Ben fet!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Ratolí tàctil que mostra tres dits que es mouen cap a la dreta i cap a l\'esquerra"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla del dispositiu que mostra l\'animació del gest per anar enrere"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Per tornar enrere, llisca cap a l\'esquerra o cap a la dreta amb tres dits en qualsevol lloc del ratolí tàctil.\n\nTambé pots utilitzar les tecles d\'accions de drecera+Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Ben fet!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Per anar a la pantalla d\'inici en qualsevol moment, fes lliscar tres dits cap amunt des de la part inferior de la pantalla."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Molt bé!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Has completat el gest per anar a la pantalla d\'inici."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla d\'acció"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Per accedir a les aplicacions, prem la tecla d\'acció al teclat."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Enhorabona!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Has completat el gest de la tecla d\'acció.\n\nTecla d\'acció+/ mostra totes les dreceres que tens disponibles."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Per anar a la pantalla d\'inici, llisca tres dits cap amunt al ratolí tàctil"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Per veure les aplicacions recents, llisca cap amunt amb tres dits i mantén premut al ratolí tàctil"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per veure totes les aplicacions, prem la tecla d\'acció al teclat"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Emmascarat"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloqueja per veure"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Utilitza el ratolí tàctil per tornar enrere"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Fes lliscar tres dits cap a l\'esquerra o cap a la dreta. Toca per aprendre més gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilitza el ratolí tàctil per anar a la pantalla d\'inici"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Llisca cap amunt amb tres dits i mantén premut. Toca per aprendre més gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilitza el teclat per veure totes les aplicacions"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prem la tecla d\'acció en qualsevol moment. Toca per aprendre més gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Atenuació extra ara forma part de la barra de brillantor"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Ara pots atenuar encara més la pantalla abaixant-ne el nivell de brillantor des de la part superior.\n\nFunciona millor si et trobes en un lloc fosc."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Suprimeix la drecera d\'atenuació extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"S\'ha suprimit la drecera d\'atenuació extra. Per abaixar la brillantor, utilitza la barra de brillantor normal."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 194057a..cebb175 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a ostatní otevřené aplikace objevily tento snímek obrazovky."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Přidat do poznámky"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Zahrnout odkaz"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Nahrávání obrazovky"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Záznam obrazovky se zpracovává"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Začít nahrávat?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Během nahrávání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Dejte proto pozor na hesla, platební údaje, zprávy, fotografie, zvukové záznamy nebo videa."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Během nahrávání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s informacemi, jako jsou hesla, platební údaje, zprávy, fotky, zvukové záznamy nebo videa."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Začít nahrávat"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pořídit nahrávku obrazovky?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrát jednu aplikaci"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrát celou obrazovku"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Při nahrávání celé obrazovky se zaznamenává veškerý obsah na obrazovce. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Při nahrávání aplikace se zaznamenává všechno, co se v dané obrazovce zobrazuje nebo přehrává. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vyberte aplikaci, ze které chcete pořídit nahrávku"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávat zvuk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zařízení"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk ze zařízení, například hudba, hovory a vyzvánění"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odeslat"</string>
     <string name="cancel" msgid="1089011503403416730">"Zrušit"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo aplikace"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdit"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Zkusit znovu"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Klepnutím zrušíte ověření"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Spořič obrazovky"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nerušit"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nejsou dostupná žádná spárovaná zařízení"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím zařízení připojíte nebo odpojíte"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Používat Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Sdílení zvuku"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Klepnutím přepnete nebo nasdílíte zvuk"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string>
     <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_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>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string>
     <string name="performance" msgid="6552785217174378320">"Výkon"</string>
     <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string>
-    <string name="thermal" msgid="6758074791325414831">"Termovize"</string>
+    <string name="thermal" msgid="6758074791325414831">"Teplota"</string>
     <string name="custom" msgid="3337456985275158299">"Vlastní"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Vlastní nastavení trasování"</string>
     <string name="restore_default" msgid="5259420807486239755">"Obnovit výchozí"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Okamžité titulky"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okamžité titulky"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otevřít nastavení"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Další zařízení"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Přepnout přehled"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavení"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavit"</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í režimy}=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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Přizpůsobit"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgety na obrazovce uzamčení"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Na obrazovku uzamčení byl přidán widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Přejetím doleva spustíte komunitní výukový program"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Přizpůsobit"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zavřít"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mít přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze za řízení při nahrávání nebo odesílání. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Začít nahrávat nebo odesílat?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Služba, která tuto funkci poskytuje, bude mít při nahrávání nebo odesílání přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze zařízení. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Celá obrazovka"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Jedna aplikace"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Sdílení nebo nahrání aplikace"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Začít nahrávat nebo odesílat pomocí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Během sdílení, nahrávání nebo odesílání má <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Během sdílení, nahrávání nebo odesílání aplikace má <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začít"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Sdílení nebo nahrání aplikace"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Sdílet obrazovku s aplikací <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Sdílet jednu aplikaci"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Sdílet celou obrazovku"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Při sdílení celé obrazovky vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Sdílet obrazovku"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> tuto možnost zakázala"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Začít odesílat?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Během odesílání má Android přístup ke všemu, co je viditelné na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Během odesílání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s informacemi, jako jsou hesla, platební údaje, zprávy, fotky, zvukové záznamy a videa."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Začít odesílat"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikaci ke sdílení"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odeslat obrazovku?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odeslat jednu aplikaci"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Odeslat celou obrazovku"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Při odesílání celé obrazovky je vidět vše, co se na obrazovce nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Při odesílání aplikace je vidět vše, co se v aplikaci nachází nebo děje. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Odesílání obrazovky"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vyberte aplikaci k odesílání"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Začít sdílet?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Během sdílení, nahrávání nebo odesílání má Android přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Během sdílení, nahrávání nebo odesílání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s informacemi, jako jsou hesla, platební údaje, zprávy, fotky, zvukové záznamy a videa."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Začít"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Další"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Když přepnete aplikace, sdílení se pozastaví"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Sdílet aplikaci"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Přepnout zpět"</string>
@@ -565,8 +577,8 @@
     <string name="media_projection_action_text" msgid="3634906766918186440">"Spustit"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Žádná oznámení"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Žádná nová oznámení"</string>
-    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Je zapnuté zeslabení oznámení"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Když dostanete příliš mnoho oznámení najednou, až na dvě minuty se sníží hlasitost zařízení a oznámení se omezí."</string>
+    <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Oznámení jsou zeslabená"</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Když máte moc oznámení najednou, až na dvě minuty se sníží hlasitost zařízení a oznámení se omezí."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Vypnout"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Starší oznámení se zobrazí po odemknutí"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Toto zařízení spravuje rodič"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobré připojení"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, připojení je k dispozici"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS přes satelit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Tísňová volání nebo SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovní profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Zábava, která není pro každého"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Nástroj na ladění uživatelského rozhraní systému vám nabízí další způsoby, jak si vyladit a přizpůsobit uživatelské rozhraní Android. Tyto experimentální funkce mohou v dalších verzích chybět, nefungovat nebo být změněny. Postupujte proto prosím opatrně."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Přístupnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové zkratky"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žádné výsledky hledání"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto zpět"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto domů"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Akční klávesa"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Výborně!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad se třemi prsty, které se pohybují doprava a doleva"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Obrazovka zařízení s animaci gesta k vrácení zpět"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pokud se chcete vrátit zpět, stačí kdekoli na touchpadu přejet třemi prsty doleva nebo doprava.\n\nMůžete také použít klávesovou zkratku Akce + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Výborně!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Na plochu přejdete kdykoli přejetím třemi prsty ze spodní části obrazovky nahoru."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Skvělé!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dokončili jste gesto pro přechod na plochu."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Akční klávesa"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Přístup k aplikacím získáte stisknutím akční klávesy na klávesnici."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulujeme!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Dokončili jste gesto akční klávesy.\n\nAkce + / zobrazí všechny dostupné zkratky."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Pokud se chcete vrátit domů, přejeďte po touchpadu třemi prsty nahoru"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pokud chcete zobrazit poslední aplikace, přejeďte na touchpadu třemi prsty nahoru a podržte je"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pokud chcete zobrazit všechny aplikace, stiskněte na klávesnici akční klávesu"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Odstraněno"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"K zobrazení je potřeba zařízení odemknout"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Návrat zpět pomocí touchpadu"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Přejeďte třemi prsty doleva nebo doprava. Další gesta zjistíte klepnutím."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Návrat domů pomocí touchpadu"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Přejeďte třemi prsty nahoru a podržte je. Další gesta zjistíte klepnutím."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazení všech aplikací pomocí klávesnice"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kdykoli stiskněte akční klávesu. Další gesta zjistíte klepnutím."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Na sloupci jasu lze nově nastavit velmi tmavou obrazovku"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Obrazovku můžete v horní části nastavit jako velmi tmavou tím, že ještě víc snížíte jas.\n\nNejlépe to funguje ve tmavém prostředí."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Odstranit zkratku pro velmi tmavou obrazovku"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Zkratka pro velmi tmavou obrazovku byla odstraněna. Jas můžete snížit pomocí standardního sloupce jasu."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ba1babf..974083e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åbne apps har registreret dette screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Føj til note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inkluder link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Skærmoptagelse"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skærmoptagelse"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Vil du starte optagelsen?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Mens du optager, har Android adgang til alt, der er synligt på din skærm, eller som afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Mens du optager en app, har Android adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Start optagelse"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du optage din skærm?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Optag én app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Optag hele skærmen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du optager hele skærmen, bliver alt det, der vises på skærmen, optaget. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du optager en app, optages alt det, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Optag skærm"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vælg den app, der skal optages"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Optag lyd"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Enhedslyd"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Lyd fra din enhed såsom musik, opkald og ringetoner"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Annuller"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Applogo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekræft"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Prøv igen"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tryk for at annullere godkendelsen"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Pauseskærm"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Forstyr ikke"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Tilstande med prioritet"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Der er ingen tilgængelige parrede enheder"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryk for at oprette eller afbryde forbindelse til en enhed"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Lyddeling"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tryk for at skifte eller dele lyd"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Livetekstning"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstning"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åbn Indstillinger"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Anden enhed"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå Oversigt til/fra"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tilstande med prioritet"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Udfør"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Indstillinger"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfigurer"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tilpas"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets på låseskærmen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widgetten <xliff:g id="WIDGET_NAME">%1$s</xliff:g> er føjet til låseskærmen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Stryg mod venstre for at starte den fælles vejledning"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tilpas"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Luk"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vil du begynde at optage eller caste?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Tjenesten, der tilbyder denne funktion, får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Hele skærmen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Én app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Del eller optag en app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vil du begynde at optage eller caste via <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Når du deler, optager eller caster, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> adgang til alt, der er synligt på din skærm eller afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Når du deler, optager eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Del eller optag en app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vil du dele din skærm med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Del én app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skærmen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skærmen, er alt på din skærm synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt, der vises eller afspilles i den pågældende app, synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skærm"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktiveret denne valgmulighed"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vil du begynde at caste?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Når du caster, har Android adgang til alt, der vises på din skærm eller afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Når du caster en app, har Android adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Start casting"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vælg den app, du vil dele fra"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste din skærm?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast hele skærmen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Når du caster hele din skærm, er alt på skærmen synligt. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Når du caster en app, er alt, der vises eller afspilles i appen, synligt. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast skærm"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vælg den app, du vil caste fra"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vil du begynde at dele?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Når du deler, optager eller caster, har Android adgang til alt, der er synligt på din skærm eller afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Når du deler, optager eller caster en app, har Android adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Start"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Næste"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Delingen sættes på pause, når du skifter apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Del denne app i stedet"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Skift tilbage"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit – god forbindelse"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit – forbindelsen er tilgængelig"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-meldinger via satellit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nødopkald eller SOS-meldinger via satellit"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbejdsprofil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Sjovt for nogle, men ikke for alle"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner giver dig flere muligheder for at justere og tilpasse Android-brugerfladen. Disse eksperimentelle funktioner kan ændres, gå i stykker eller forsvinde i fremtidige udgivelser. Vær forsigtig, hvis du fortsætter."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hjælpefunktioner"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastaturgenveje"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Der er ingen søgeresultater"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bevægelse for at gå tilbage"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bevægelse for at gå til startskærm"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handlingstast"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sådan!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchplade viser tre fingre, der bevæger sig til højre og venstre"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Enhedsskærm, der viser en animation, som demonstrerer, hvordan man går tilbage"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Du kan gå tilbage ved at stryge mod venstre eller højre med tre fingre et vilkårligt sted på touchpladen.\n\nDu kan også bruge tastaturgenvejen Alt + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Flot!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Du kan til enhver tid stryge opad med tre fingre fra bunden af skærmen, hvis du vil gå til startskærmen."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Sådan!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du har fuldført bevægelsen for Gå til startskærmen."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Handlingstast"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Du kan tilgå alle dine apps ved at trykke på handlingstasten på dit tastatur."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tillykke!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Du har fuldført bevægelsen for handlingstasten.\n\nHandling + / viser alle de tilgængelige genveje."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Du kan gå til startskærmen ved at stryge opad med tre fingre på touchpladen"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Du kan se nyligt brugte apps ved at stryge opad og holde tre fingre nede på touchpladen"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Du kan se alle dine apps ved at trykke på handlingstasten på dit tastatur"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Skjult"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Lås op for at se"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Brug din touchplade til at gå tilbage"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Stryg til venstre eller højre med tre fingre. Tryk for at lære flere bevægelser."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Brug din touchplade til at gå til startskærmen"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Stryg opad, og hold tre fingre nede. Tryk for at lære flere bevægelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Brug dit tastatur til at se alle apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryk på handlingstasten når som helst. Tryk for at lære flere bevægelser."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ekstra dæmpet belysning er nu en del af lysstyrkebjælken"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Du kan nu dæmpe skærmens belysning ekstra meget ved at reducere lysstyrken endnu mere fra toppen af skærmen.\n\nDette fungerer bedst i mørke omgivelser."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Fjern genvejen til ekstra dæmpet belysning"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Genvejen til ekstra dæmpet belysning er fjernet. Brug den almindelige lysstyrkebjælke til at reducere lysstyrken."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 62ede78..11dd46e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> und andere geöffnete Apps haben diesen Screenshot erkannt."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Zu Notiz hinzufügen"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Link einschließen"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Bildschirmaufzeichnung"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Bildschirmaufzeichnung…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Aufnahme starten?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Während der Aufnahme hat Android Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Während der Aufnahme einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Aufnahme starten"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Bildschirm aufnehmen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Einzelne App aufnehmen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gesamten Bildschirm aufnehmen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Wenn du den gesamten Bildschirm aufnimmst, ist in der Aufnahme alles zu sehen, was auf dem Bildschirm angezeigt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Wenn du eine App aufnimmst, ist in der Aufnahme alles zu sehen, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Bildschirm aufnehmen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"App zum Aufnehmen auswählen"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio aufnehmen"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio des Geräts"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Audioinhalte auf deinem Gerät, wie Musik, Anrufe und Klingeltöne"</string>
@@ -128,24 +132,24 @@
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fehler beim Start der Bildschirmaufzeichnung"</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Aufzeichnung beenden?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Du zeichnest momentan deinen gesamten Bildschirm auf"</string>
-    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du zeichnest momentan Inhalte der <xliff:g id="APP_NAME">%1$s</xliff:g> auf"</string>
-    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Aufnahme beenden"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Du zeichnest momentan Inhalte der App <xliff:g id="APP_NAME">%1$s</xliff:g> auf"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Aufzeichnung beenden"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Bildschirm wird geteilt"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Bildschirmfreigabe beenden?"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du teilst momentan deinen gesamten Bildschirm mit der <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Du teilst momentan deinen gesamten Bildschirm mit der App <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Du teilst momentan deinen gesamten Bildschirm mit einer App"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du teilst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Du teilst momentan die App <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Du teilst momentan Inhalte einer App"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Freigabe beenden"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Bildschirm wird übertragen"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Übertragung abbrechen?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du überträgst momentan deinen gesamten Bildschirm auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du überträgst momentan deinen gesamten Bildschirm auf ein Gerät in der Nähe"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du überträgst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf das Gerät „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du überträgst momentan Inhalte der <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf ein Gerät in der Nähe"</string>
-    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du überträgst momentan Inhalte auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
-    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du überträgst momentan Inhalte auf ein Gerät in der Nähe"</string>
-    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Streaming beenden"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Streaming beenden?"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Du streamst momentan deinen gesamten Bildschirm auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Du streamst momentan deinen gesamten Bildschirm auf ein Gerät in der Nähe"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Du streamst momentan die App <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf das Gerät „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Du streamst momentan Inhalte der App <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> auf ein Gerät in der Nähe"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Du streamst momentan Inhalte auf das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Du streamst momentan auf ein Gerät in der Nähe"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Streamen beenden"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Schließen"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Problem aufzeichnen"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Aufzeichnung wird verarbeitet"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senden"</string>
     <string name="cancel" msgid="1089011503403416730">"Abbrechen"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App-Logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bestätigen"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Noch einmal versuchen"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Zum Abbrechen der Authentifizierung tippen"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Bildschirmschoner"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bitte nicht stören"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritätsmodi"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Keine gekoppelten Geräte verfügbar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Zum Verbinden oder Trennen eines Geräts tippen"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audiofreigabe"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Zum Wechseln oder Teilen des Audiostreams tippen"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Bildschirmaufzeichnung"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Beenden"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufzeichnen"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Automatische Untertitel"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatische Untertitel"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Einstellungen öffnen"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Sonstiges Gerät"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Übersicht ein-/ausblenden"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritätsmodi"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fertig"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Einstellungen"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Einrichten"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Klingeltöne und die Vibration werden deaktiviert, außer für Weckrufe. Du hörst jedoch weiterhin Sound, wenn du dir Musik anhörst, Videos ansiehst oder Spiele spielst."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Anpassen"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird langsam geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wird geladen • Voll in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets auf dem Sperrbildschirm"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ zum Sperrbildschirm hinzugefügt"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Wische nach links, um das gemeinsame Tutorial zu starten"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Anpassen"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Schließen"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Die <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Aufnahme oder Stream starten?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Gesamter Bildschirm"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Einzelne App"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"App teilen oder aufnehmen"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Aufnehmen oder Streamen mit der <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> App starten?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Beim Teilen, Aufnehmen oder Streamen hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Beim Teilen, Aufnehmen oder Streamen einer App hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Starten"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"App teilen oder aufnehmen"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bildschirm mit <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> teilen?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Eine App teilen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Gesamten Bildschirm teilen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Wenn du den gesamten Bildschirm teilst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles auf dem Bildschirm sichtbar. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wenn du eine App streamst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bildschirm teilen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> hat diese Option deaktiviert"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Stream starten?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Beim Streamen hat Android Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Beim Streamen einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Stream starten"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App zum Teilen auswählen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Bildschirm streamen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eine App streamen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Gesamten Bildschirm streamen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Wenn du den gesamten Bildschirm streamst, ist alles auf dem Bildschirm sichtbar. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Wenn du eine App streamst, ist alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Bildschirm streamen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"App zum Streamen auswählen"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Teilen starten?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Beim Teilen, Aufnehmen oder Streamen hat Android Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Beim Teilen, Aufnehmen oder Streamen einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Starten"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Weiter"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Das Teilen wird pausiert, wenn du zwischen Apps wechselst"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Stattdessen diese App teilen"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Zurückwechseln"</string>
@@ -625,7 +637,7 @@
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Einstellungen"</string>
     <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatische Untertitel"</string>
     <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Auf verträglichere Lautstärke eingestellt"</string>
-    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörerlautstärke war länger als empfohlen hoch eingestellt"</string>
+    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Die Kopfhörer sind schon länger als empfohlen auf hohe Lautstärke eingestellt"</string>
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Die Kopfhörerlautstärke hat für diese Woche das Sicherheitslimit überschritten"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Weiterhören"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Leiser stellen"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, Verbindung gut"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, Verbindung verfügbar"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Notruf über Satellit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Notrufe oder SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Arbeitsprofil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Für einige ein Vergnügen, aber nicht für alle"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Mit System UI Tuner erhältst du zusätzliche Möglichkeiten, die Android-Benutzeroberfläche anzupassen. Achtung: Diese Testfunktionen können sich ändern, abstürzen oder in zukünftigen Versionen verschwinden."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Keine Suchergebnisse"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Touch-Geste „Zurück“"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Touch-Geste „Startbildschirm“"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Aktionstaste"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Gut gemacht!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mit drei Fingern, die sich nach links und rechts bewegen"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Bildschirm zeigt eine Animation der Touch-Geste „Zurück“"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Wenn du zurückgehen möchtest, wische an einer beliebigen Stelle des Touchpads mit drei Fingern nach links oder rechts.\n\nDu kannst stattdessen auch die Tastenkombination „Aktion“ + „ESC“ verwenden."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Gut gemacht!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Du kannst jederzeit zum Startbildschirm gehen, indem du mit drei Fingern vom unteren Displayrand nach oben wischst."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Sehr gut!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du hast den Schritt für die „Startbildschirm“-Geste abgeschlossen."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Aktionstaste"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Wenn du auf deine Apps zugreifen möchtest, drücke auf der Tastatur die Aktionstaste."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Glückwunsch!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Du hast den Schritt für die „Aktionstaste“-Geste abgeschlossen.\n\nAktion + / zeigt alle verfügbaren Tastenkombinationen."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Wenn du den Startbildschirm aufrufen möchtest, wische auf dem Touchpad mit drei Fingern nach oben"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Um zuletzt verwendete Apps aufzurufen, wische mit 3 Fingern nach oben und halte das Touchpad gedrückt"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Wenn du alle deine Apps aufrufen möchtest, drücke auf der Tastatur die Aktionstaste"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Entfernt"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Zum Ansehen entsperren"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Über das Touchpad zurückgehen"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Wische mit drei Fingern nach links oder rechts. Tippe für mehr Infos zu Touch-Gesten."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Über das Touchpad zum Startbildschirm zurückkehren"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Wische mit 3 Fingern nach oben und halte das Touchpad gedrückt. Tippe für mehr Infos zu Touch-Gesten."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Über die Tastatur alle Apps aufrufen"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Du kannst jederzeit die Aktionstaste drücken. Tippe für mehr Infos zu Touch-Gesten."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"„Extradunkel“ jetzt auf Helligkeitsleiste verfügbar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Du kannst das Display jetzt extradunkel machen, indem du die Helligkeit vom oberen Displayrand aus noch weiter senkst.\n\nDas funktioniert in dunklen Umgebungen am besten."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Verknüpfung für „Extradunkel“ entfernen"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Verknüpfung für „Extradunkel“ wurde entfernt. Wenn du die Helligkeit reduzieren möchtest, verwende einfach die normale Helligkeitsleiste."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 5025f6d..433137c 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> και άλλες ανοικτές εφαρμογές εντόπισαν το στιγμιότυπο οθόνης."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Προσθήκη σε σημείωση"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Συμπερίληψη συνδέσμου"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Εγγραφή οθόνης"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Επεξεργασία εγγραφής οθόνης"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Έναρξη εγγραφής;"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Όταν κάνετε εγγραφή, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Όταν κάνετε εγγραφή μιας εφαρμογής, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Έναρξη εγγραφής"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Να γίνει εγγραφή της οθόνης σας;"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Εγγραφή μίας εφαρμογής"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Εγγραφή ολόκληρης της οθόνης"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Όταν κάνετε εγγραφή ολόκληρης της οθόνη σας, καταγράφεται οτιδήποτε εμφανίζεται σε αυτήν. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Όταν κάνετε εγγραφή μιας εφαρμογής, καταγράφεται οτιδήποτε εμφανίζεται ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Εγγραφή οθόνης"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Επιλέξτε εφαρμογή για εγγραφή"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Ηχογράφηση"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Ήχος συσκευής"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Ήχος από τη συσκευή σας, όπως μουσική, κλήσεις και ήχοι κλήσης"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Αποστολή"</string>
     <string name="cancel" msgid="1089011503403416730">"Ακύρωση"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Λογότυπο εφαρμογής"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Επιβεβαίωση"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Δοκιμάστε ξανά"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Πατήστε για ακύρωση του ελέγχου ταυτότητας"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Προφύλαξη οθόνης"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Μην ενοχλείτε"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Λειτουργίες προτεραιότητας"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Δεν υπάρχουν διαθέσιμες συσκευές σε σύζευξη"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Πατήστε για σύνδεση ή αποσύνδεση μιας συσκευής"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Κοινή χρήση ήχου"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Πατήστε για εναλλαγή ή κοινή χρήση ήχου"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Αποθηκεύτηκε"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"αποσύνδεση"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ενεργοποίηση"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Εγγραφή οθόνης"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Έναρξη"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Διακοπή"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλήματος"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλ."</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Διακοπή"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Αναφορά σφάλματος"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Ζωντανοί υπότιτλοι"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ζωντανοί υπότιτλοι"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Άνοιγμα Ρυθμίσεων"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Άλλη συσκευή"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Εναλλαγή επισκόπησης"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Λειτουργίες προτεραιότητας"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Τέλος"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ρυθμίσεις"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ρύθμιση"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Προσαρμογή"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Γραφικά στοιχεία στην οθόνη κλειδώματος"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Το γραφικό στοιχείο <xliff:g id="WIDGET_NAME">%1$s</xliff:g> προστέθηκε στην οθόνη κλειδώματος"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Σύρετε προς τα αριστερά για να ξεκινήσετε τον κοινόχρηστο οδηγό"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Προσαρμογή"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Παράβλεψη"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Έναρξη εγγραφής ή μετάδοσης;"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Η υπηρεσία που παρέχει αυτήν τη λειτουργία θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Ολόκληρη την οθόνη"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Μία εφαρμογή"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Κοινή χρήση ή εγγραφή εφαρμογής"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Έναρξη εγγραφής ή μετάδοσης με <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>;"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση μιας εφαρμογής, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Έναρξη"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Κοινή χρήση ή εγγραφή εφαρμογής"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Κοινή χρήση της οθόνης με την εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>;"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Κοινή χρήση μίας εφαρμογής"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Κοινή χρήση ολόκληρης της οθόνης"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Όταν μοιράζεστε ολόκληρη την οθόνη, οτιδήποτε εμφανίζεται στην οθόνη σας είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Όταν μοιράζεστε μια εφαρμογή, οτιδήποτε εμφανίζεται ή αναπαράγεται σε αυτή την εφαρμογή, είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Κοινή χρήση οθόνης"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απενεργοποίησε αυτή την επιλογή"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Έναρξη μετάδοσης;"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Όταν κάνετε μετάδοση, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Όταν κάνετε μετάδοση μιας εφαρμογής, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Έναρξη μετάδοσης"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Επιλογή εφαρμογής για κοινή χρήση"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Να γίνει μετάδοση της οθόνης σας;"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Μετάδοση μίας εφαρμογής"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Μετάδοση ολόκληρης της οθόνης"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Όταν κάνετε μετάδοση ολόκληρης της οθόνης, όλο το περιεχόμενο της οθόνης είναι ορατό. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Όταν κάνετε μετάδοση μιας εφαρμογής, όλο το περιεχόμενο που εμφανίζεται ή αναπαράγεται στην εφαρμογή είναι ορατό. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Μετάδοση οθόνης"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Επιλογή εφαρμογής για μετάδοση"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Έναρξη κοινοποίησης περιεχομένου;"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση μιας εφαρμογής, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Έναρξη"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Επόμενο"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Η κοινοποίηση τίθεται σε παύση κατά την εναλλαγή μεταξύ εφαρμογών"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Εναλλακτικά, κοινοποιήστε την εφαρμογή"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Επιστροφή"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Δορυφορική, καλή σύνδεση"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Δορυφορική, διαθέσιμη σύνδεση"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Δορυφορικό SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Κλήσεις έκτακτης ανάγκης ή SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Προφίλ εργασίας"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Διασκέδαση για ορισμένους, αλλά όχι για όλους"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Το System UI Tuner σάς προσφέρει επιπλέον τρόπους για να τροποποιήσετε και να προσαρμόσετε τη διεπαφή χρήστη Android. Αυτές οι πειραματικές λειτουργίες ενδέχεται να τροποποιηθούν, να παρουσιάσουν σφάλματα ή να καταργηθούν σε μελλοντικές εκδόσεις. Συνεχίστε με προσοχή."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Προσβασιμότητα"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Συντομεύσεις πληκτρολογίου"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Κανένα αποτέλεσμα αναζήτησης"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Κίνηση επιστροφής"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Κίνηση μετάβασης στην αρχική οθόνη"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Πλήκτρο ενέργειας"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Μπράβο!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Επιφάνεια αφής στην οποία εμφανίζονται τρία δάχτυλα να κινούνται δεξιά και αριστερά"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Οθόνη συσκευής που εμφανίζει μια κινούμενη εικόνα σχετικά με την κίνηση επιστροφής"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Για να επιστρέψετε, σύρετε προς τα αριστερά ή προς τα δεξιά χρησιμοποιώντας τρία δάχτυλα σε οποιοδήποτε σημείο της επιφάνειας αφής.\n\nΜπορείτε επίσης να χρησιμοποιήσετε τη συντόμευση πληκτρολογίου Action + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Μπράβο!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ολοκληρώσατε την κίνηση επιστροφής."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Αρχική"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Για μετάβαση στην αρχική οθόνη ανά πάσα στιγμή, σύρετε προς τα επάνω με τρία δάχτυλα από το κάτω μέρος της οθόνης."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Ωραία!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Πλήκτρο ενέργειας"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Για να αποκτήσετε πρόσβαση στις εφαρμογές σας, πατήστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Συγχαρητήρια!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Ολοκληρώσατε την κίνηση του κουμπιού ενέργειας.\n\nΗ ενέργεια + / εμφανίζει όλες τις διαθέσιμες συντομεύσεις."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Για μετάβαση στην αρχική οθόνη, σύρετε προς τα επάνω στην επιφάνεια αφής με τρία δάχτυλα"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Για πρόσφατες εφαρμογές, σαρώστε προς τα πάνω με τρία δάχτυλα και κρατήστε τα στην επιφάνεια αφής"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Για να δείτε όλες τις εφαρμογές, πιέστε το πλήκτρο ενέργειας στο πληκτρολόγιό σας"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Αποκρύφτηκε"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Ξεκλείδωμα για προβολή"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Χρήση της επιφάνειας αφής για επιστροφή"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Σύρετε προς τα αριστερά ή τα δεξιά με τρία δάχτυλα. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Χρήση της επιφάνειας αφής για μετάβαση στην αρχική οθόνη"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Σύρετε προς τα πάνω με τρία δάχτυλα και μην τα σηκώσετε. Πατήστε για να δείτε περισσότερες κινήσεις."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Χρήση του πληκτρολογίου για προβολή όλων των εφαρμογών"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Πιέστε το πλήκτρο ενέργειας ανά πάσα στιγμή. Πατήστε για να μάθετε περισσότερες κινήσεις."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Η επιπλέον μείωση φωτεινότητας είναι τώρα μέρος της γραμμής φωτεινότητας"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Τώρα μπορείτε να μειώσετε επιπλέον τη φωτεινότητα της οθόνης, χαμηλώνοντας το επίπεδο φωτεινότητας ακόμα περισσότερο από το επάνω μέρος της οθόνης.\n\nΑυτό λειτουργεί καλύτερα όταν βρίσκεστε σε σκοτεινό περιβάλλον."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Κατάργηση συντόμευσης επιπλέον μείωσης φωτεινότητας"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Η συντόμευση της επιπλέον μείωσης φωτεινότητας καταργήθηκε. Για να χαμηλώσετε τη φωτεινότητα, χρησιμοποιήστε την κανονική γραμμή φωτεινότητας."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index f2ba704..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Start recording?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Start recording"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choose app to record"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancel"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tap to cancel authentication"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to lock screen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Start recording or casting?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Entire screen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"A single app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Share or record an app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. Be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Start casting"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choose app to cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Start sharing?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"When you’re sharing, recording or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"When you’re sharing, recording or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Start"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Next"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Sharing pauses when you switch apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Share this app instead"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Switch back"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Great work!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"You completed the action key gesture.\n\nAction + / shows all the shortcuts that you have available."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redacted"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Unlock to view"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extra dim is now part of the brightness bar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.\n\nThis works best when you\'re in a dark environment."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remove extra dim shortcut"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Extra dim shortcut removed. To lower your brightness, use the regular brightness bar."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 5680cb7..3d4c31f 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Screen Recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Start Recording?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Start recording"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choose app to record"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls, and ringtones"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancel"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tap to cancel authentication"</string>
@@ -290,7 +292,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+    <string name="quick_settings_modes_label" msgid="879156359479504244">"Modes"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -299,6 +301,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -307,6 +310,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
     <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_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>
@@ -400,7 +404,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -429,10 +433,11 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open Settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Modes"</string>
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string>
@@ -527,22 +532,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Start recording or casting?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Entire screen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"A single app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Share or record an app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording, or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording, or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you’re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Start casting"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you’re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choose app to cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Start sharing?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"When you’re sharing, recording, or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"When you’re sharing, recording, or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Start"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Next"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Sharing pauses when you switch apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Share this app instead"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Switch back"</string>
@@ -710,6 +724,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customize the Android user interface. These experimental features may change, break, or disappear in future releases. Proceed with caution."</string>
@@ -1372,21 +1387,32 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigate using your keyboard"</string>
+    <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"Learn keyboards shortcuts"</string>
+    <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Navigate using your touchpad"</string>
+    <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>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great job!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing three fingers moving right and left"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + ESC for this."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Great job!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"You completed the action key gesture.\n\nAction + / shows all the shortcuts you have available."</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>
@@ -1396,6 +1422,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redacted"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Unlock to view"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
@@ -1404,4 +1432,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extra dim is now part of the brightness bar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.\n\nThis works best when you\'re in a dark environment."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remove extra dim shortcut"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Extra dim shortcut removed. To lower your brightness, use the regular brightness bar."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index f2ba704..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Start recording?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Start recording"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choose app to record"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancel"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tap to cancel authentication"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to lock screen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Start recording or casting?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Entire screen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"A single app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Share or record an app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. Be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Start casting"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choose app to cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Start sharing?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"When you’re sharing, recording or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"When you’re sharing, recording or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Start"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Next"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Sharing pauses when you switch apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Share this app instead"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Switch back"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Great work!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"You completed the action key gesture.\n\nAction + / shows all the shortcuts that you have available."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redacted"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Unlock to view"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extra dim is now part of the brightness bar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.\n\nThis works best when you\'re in a dark environment."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remove extra dim shortcut"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Extra dim shortcut removed. To lower your brightness, use the regular brightness bar."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index f2ba704..cb9dfe5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> and other open apps detected this screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Add to note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Include link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Screen recorder"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Start recording?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Start recording"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Record your screen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Record one app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Record entire screen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"When you\'re recording your entire screen, anything displayed on your screen is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"When you\'re recording an app, anything displayed or played in that app is recorded. So, be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Record screen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choose app to record"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancel"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirm"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Try again"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tap to cancel authentication"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Do Not Disturb"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Priority modes"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No paired devices available"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tap to connect or disconnect a device"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio sharing"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tap to switch or share audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Open settings"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Other device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Toggle Overview"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Priority modes"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Done"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Settings"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Set up"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"You won\'t be disturbed by sounds and vibrations, except from alarms. You\'ll still hear anything you choose to play including music, videos and games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Customise"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging slowly • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Charging • Full in <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets on lock screen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to lock screen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe left to start the communal tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Customise"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dismiss"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Start recording or casting?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages and audio that you play."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Entire screen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"A single app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Share or record an app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Share or record an app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Share your screen with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Share one app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Share entire screen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"When you\'re sharing your entire screen, anything on your screen is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. Be careful with things like passwords, payment details, messages, photos, audio and video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Start casting"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast entire screen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"When you\'re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"When you\'re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast screen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choose app to cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Start sharing?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"When you’re sharing, recording or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"When you’re sharing, recording or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Start"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Next"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Sharing pauses when you switch apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Share this app instead"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Switch back"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, good connection"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, connection available"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Emergency calls or SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work profile"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Fun for some but not for all"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner gives you extra ways to tweak and customise the Android user interface. These experimental features may change, break or disappear in future releases. Proceed with caution."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Keyboard shortcuts"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Back gesture"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Home gesture"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Great work!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad showing 3 fingers moving right and left"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Device screen showing animation for back gesture"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"To go back, swipe left or right using three fingers anywhere on the touchpad.\n\nYou can also use the keyboard shortcut Action + Esc for this."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Great work!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"To go to your home screen at any time, swipe up with three fingers from the bottom of your screen."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Nice!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"You completed the go home gesture."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"To access your apps, press the action key on your keyboard."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Congratulations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"You completed the action key gesture.\n\nAction + / shows all the shortcuts that you have available."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"To go home, swipe up with three fingers on the touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"To view recent apps, swipe up and hold with three fingers on the touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"To view all your apps, press the action key on your keyboard"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redacted"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Unlock to view"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use your touchpad to go back"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe left or right using three fingers. Tap to learn more gestures."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use your touchpad to go home"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe up and hold using three fingers. Tap to learn more gestures."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use your keyboard to view all apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Press the action key at any time. Tap to learn more gestures."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extra dim is now part of the brightness bar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.\n\nThis works best when you\'re in a dark environment."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remove extra dim shortcut"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Extra dim shortcut removed. To lower your brightness, use the regular brightness bar."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index f99ab9e..eb94d59 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and other open apps detected this screenshot.‎‏‎‎‏‎"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎Add to note‎‏‎‎‏‎"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‎Include link‎‏‎‎‏‎"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APPNAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‎‎Screen Recorder‎‏‎‎‏‎"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎Processing screen recording‎‏‎‎‏‎"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‎Ongoing notification for a screen record session‎‏‎‎‏‎"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‏‎Start Recording?‎‏‎‎‏‎"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‎‏‎While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎Start recording‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎Record your screen?‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎Record one app‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‎Record entire screen‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎Record screen‎‏‎‎‏‎"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎Choose app to record‎‏‎‎‏‎"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‎‏‏‎‏‎Record audio‎‏‎‎‏‎"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‎Device audio‎‏‎‎‏‎"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‎Sound from your device, like music, calls, and ringtones‎‏‎‎‏‎"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎Send‎‏‎‎‏‎"</string>
     <string name="cancel" msgid="1089011503403416730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‎‏‎‎Cancel‎‏‎‎‏‎"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎App logo‎‏‎‎‏‎"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎Confirm‎‏‎‎‏‎"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎Try again‎‏‎‎‏‎"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎Tap to cancel authentication‎‏‎‎‏‎"</string>
@@ -290,7 +292,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‏‏‎‏‏‏‎‎Screen saver‎‏‎‎‏‎"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎Ethernet‎‏‎‎‏‎"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‏‎‎Do Not Disturb‎‏‎‎‏‎"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‏‎‏‎Priority modes‎‏‎‎‏‎"</string>
+    <string name="quick_settings_modes_label" msgid="879156359479504244">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‎Modes‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‎Bluetooth‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‎‏‎‎‏‎‎No paired devices available‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‏‎‏‏‏‎‎Tap to connect or disconnect a device‎‏‎‎‏‎"</string>
@@ -299,6 +301,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎Use Bluetooth‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎Connected‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‎‎Audio Sharing‎‏‎‎‏‎"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‎‎‎Tap to switch or share audio‎‏‎‎‏‎"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎‏‎Saved‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎disconnect‎‏‎‎‏‎"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‎activate‎‏‎‎‏‎"</string>
@@ -307,6 +310,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‏‏‎Bluetooth will turn on tomorrow morning‎‏‎‎‏‎"</string>
     <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_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>
@@ -400,7 +404,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎Click to pair new device‎‏‎‎‏‎"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎Couldn\'t update preset‎‏‎‎‏‎"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎Preset‎‏‎‎‏‎"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎Live Caption‎‏‎‎‏‎"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‎Live Caption‎‏‎‎‏‎"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎Unblock device microphone?‎‏‎‎‏‎"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎Unblock device camera?‎‏‎‎‏‎"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎Unblock device camera and microphone?‎‏‎‎‏‎"</string>
@@ -429,10 +433,11 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎Open Settings‎‏‎‎‏‎"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎Other device‎‏‎‎‏‎"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‎‏‏‎Toggle Overview‎‏‎‎‏‎"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‎Priority modes‎‏‎‎‏‎"</string>
+    <string name="zen_modes_dialog_title" msgid="8854640808100096934">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‎Modes‎‏‎‎‏‎"</string>
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎Done‎‏‎‎‏‎"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‎Settings‎‏‎‎‏‎"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎Set up‎‏‎‎‏‎"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎Manage in settings‎‏‎‎‏‎"</string>
@@ -527,22 +532,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎ will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.‎‏‎‎‏‎"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎Start recording or casting?‎‏‎‎‏‎"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.‎‏‎‎‏‎"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎Entire screen‎‏‎‎‏‎"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‏‎A single app‎‏‎‎‏‎"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎Share or record an app‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎Start recording or casting with ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‎‏‎When you’re sharing, recording, or casting, ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎ has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‏‎When you’re sharing, recording, or casting an app, ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎ has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎Start‎‏‎‎‏‎"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎Share or record an app‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‎Share your screen with ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎Share one app‎‏‎‎‏‎"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎Share entire screen‎‏‎‎‏‎"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎When you’re sharing your entire screen, anything on your screen is visible to ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎When you’re sharing an app, anything shown or played in that app is visible to ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‎Share screen‎‏‎‎‏‎"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has disabled this option‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‏‎Start casting?‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‎‏‏‎‏‎‎Start casting‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎Choose app to share‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎Cast your screen?‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎Cast one app‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎Cast entire screen‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎When you’re casting your entire screen, anything on your screen is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎Cast screen‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎Choose app to cast‎‏‎‎‏‎"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎Start sharing?‎‏‎‎‏‎"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‏‎When you’re sharing, recording, or casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎When you’re sharing, recording, or casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.‎‏‎‎‏‎"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎Start‎‏‎‎‏‎"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎Next‎‏‎‎‏‎"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎Sharing pauses when you switch apps‎‏‎‎‏‎"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎Share this app instead‎‏‎‎‏‎"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎Switch back‎‏‎‎‏‎"</string>
@@ -710,6 +724,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎Satellite, good connection‎‏‎‎‏‎"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎Satellite, connection available‎‏‎‎‏‎"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‎Satellite SOS‎‏‎‎‏‎"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎Emergency calls or SOS‎‏‎‎‏‎"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎Work profile‎‏‎‎‏‎"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎Fun for some but not for all‎‏‎‎‏‎"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎System UI Tuner gives you extra ways to tweak and customize the Android user interface. These experimental features may change, break, or disappear in future releases. Proceed with caution.‎‏‎‎‏‎"</string>
@@ -1372,21 +1387,32 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‎Accessibility‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎Keyboard shortcuts‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎Search shortcuts‎‏‎‎‏‎"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‎‎‎‎‎‎No search results‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‎Collapse icon‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‎Expand icon‎‏‎‎‏‎"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‎‎‎or‎‏‎‎‏‎"</string>
+    <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎Navigate using your keyboard‎‏‎‎‏‎"</string>
+    <string name="launch_keyboard_tutorial_notification_content" msgid="2880339951512757918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎Learn keyboards shortcuts‎‏‎‎‏‎"</string>
+    <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎Navigate using your touchpad‎‏‎‎‏‎"</string>
+    <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>
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎Back gesture‎‏‎‎‏‎"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎Home gesture‎‏‎‎‏‎"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎Action key‎‏‎‎‏‎"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‏‎Done‎‏‎‎‏‎"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎Great job!‎‏‎‎‏‎"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎Go back‎‏‎‎‏‎"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎Touchpad showing three fingers moving right and left‎‏‎‎‏‎"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎Device screen showing animation for back gesture‎‏‎‎‏‎"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎To go back, swipe left or right using three fingers anywhere on the touchpad.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎You can also use the keyboard shortcut Action + ESC for this.‎‏‎‎‏‎"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎Great job!‎‏‎‎‏‎"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎To go to your home screen at any time, swipe up with three fingers from the bottom of your screen.‎‏‎‎‏‎"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎Nice!‎‏‎‎‏‎"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎You completed the go home gesture.‎‏‎‎‏‎"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‏‏‏‎Action key‎‏‎‎‏‎"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎To access your apps, press the action key on your keyboard.‎‏‎‎‏‎"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‏‎Congratulations!‎‏‎‎‏‎"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎You completed the action key gesture.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Action + / shows all the shortcuts you have available.‎‏‎‎‏‎"</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>
@@ -1396,6 +1422,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‏‎To go home, swipe up with three fingers on the touchpad‎‏‎‎‏‎"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‎To view recent apps, swipe up and hold with three fingers on the touchpad‎‏‎‎‏‎"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‎To view all your apps, press the action key on your keyboard‎‏‎‎‏‎"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎Redacted‎‏‎‎‏‎"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎Unlock to view‎‏‎‎‏‎"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‎‎Use your touchpad to go back‎‏‎‎‏‎"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‎Swipe left or right using three fingers. Tap to learn more gestures.‎‏‎‎‏‎"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‎Use your touchpad to go home‎‏‎‎‏‎"</string>
@@ -1404,4 +1432,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎Swipe up and hold using three fingers. Tap to learn more gestures.‎‏‎‎‏‎"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‏‏‎Use your keyboard to view all apps‎‏‎‎‏‎"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‏‎Press the action key at any time. Tap to learn more gestures.‎‏‎‎‏‎"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‏‎‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎Extra dim is now part of the brightness bar‎‏‎‎‏‎"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎You can now make the screen extra dim by lowering the brightness level even further from the top of your screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎This works best when you\'re in a dark environment.‎‏‎‎‏‎"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎Remove extra dim shortcut‎‏‎‎‏‎"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎Extra dim shortcut removed. To lower your brightness, use the regular brightness bar.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 31344fa..2af8bd1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras apps en ejecución detectaron que tomaste una captura de pantalla."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Agregar a la nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluir vínculo"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Grabadora de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación constante para una sesión de grabación de pantalla"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"¿Quieres comenzar a grabar?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Cuando grabes contenido, Android podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Cuando grabes una app, Android podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar grabación"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabes toda la pantalla, se grabará todo lo que se muestre en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabes una app, se registrará todo lo que se muestre o reproduzca en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Elige una app para grabar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grabar audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio del dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sonidos del dispositivo, como música, llamadas y tonos"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotipo de la app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Volver a intentarlo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Presiona para cancelar la autenticación"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No interrumpir"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridad"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos sincronizados disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Presiona para conectar o desconectar un dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Uso compartido de audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Presiona para cambiar o compartir el audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtitulado instantáneo"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitulado instantáneo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ocultar o mostrar Recientes"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridad"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Listo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas. Podrás escuchar el contenido que reproduzcas, como música, videos y juegos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets en la pantalla de bloqueo"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Se agregó el widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a la pantalla de bloqueo"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza el dedo a la izquierda para iniciar el instructivo comunal"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Descartar"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en el dispositivo durante una grabación o transmisión. Se incluyen contraseñas, detalles de pagos, fotos, mensajes y el audio que reproduzcas."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"¿Quieres comenzar a grabar o transmitir contenido?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servicio que brinda esta función tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en el dispositivo durante una grabación o transmisión. Se incluyen contraseñas, detalles de pagos, fotos, mensajes y audio que reproduzcas."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Pantalla completa"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Una sola app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Comparte o graba una app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"¿Quieres iniciar una grabación o transmisión con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cuando compartas, grabes o transmitas contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartas, grabes o transmitas una app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Comparte o graba una app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"¿Quieres compartir pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir una app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir pantalla completa"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes la pantalla completa, todo lo que se muestre es visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una app, todo lo que se muestre o reproduzca en ella será visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> inhabilitó esta opción"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a transmitir contenido?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando transmitas contenido, Android podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando transmitas una app, Android podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Empezar"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elige la app para compartir"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Quieres transmitir la pantalla?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir una app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir pantalla entera"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando transmitas la pantalla entera, todo lo que se muestre es visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando transmitas una app, todo lo que se muestre o reproduzcas en ella será visible. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir pantalla"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Elige la app para transmitir"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"¿Quieres empezar a compartir?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Cuando compartas, grabes o transmitas contenido, Android podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Cuando compartas, grabes o transmitas una app, Android podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Iniciar"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Siguiente"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"El uso compartido se detiene al cambiar de app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Compartir esta app en su lugar"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Cambiar de cuenta"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Llamadas de emergencia o SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunas personas"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"El sintonizador de IU del sistema te brinda más formas para editar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, dejar de funcionar o no incluirse en futuras versiones. Procede con precaución."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"La búsqueda no arrojó resultados"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto atrás"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para ir a la pantalla principal"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"¡Bien hecho!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil en el que aparecen tres dedos que se mueven hacia la derecha y la izquierda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla de un dispositivo en la que aparece una animación del gesto atrás"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para volver, desliza tres dedos hacia la derecha o la izquierda en cualquier lugar del panel táctil.\n\nPara completar esta acción, también puedes usar la combinación de teclas Action + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"¡Bien hecho!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para ir a la pantalla principal en cualquier momento, desliza hacia arriba desde la parte inferior de la pantalla con tres dedos."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"¡Muy bien!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Completaste el gesto para ir al inicio."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de acción"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acceder a las apps, presiona la tecla de acción en el teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"¡Felicitaciones!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Completaste el gesto de la tecla de acción.\n\nSi presionas las teclas Acción + /, se muestran todas las combinaciones de teclas disponibles."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir a la pantalla principal, desliza hacia arriba con tres dedos en el panel táctil"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver las apps recientes, desliza hacia arriba con tres dedos y mantén presionado el panel táctil"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas las apps, presiona la tecla de acción en el teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Oculto"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquea el dispositivo para ver"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa el panel táctil para ir hacia atrás"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Desliza hacia la izquierda o la derecha con tres dedos. Presiona para aprender más gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa el panel táctil para ir a la pantalla principal"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba con tres dedos y mantenlos presionados. Presiona para aprender más gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Presiona la tecla de acción en cualquier momento. Presiona para aprender más gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"La atenuación extra ahora es parte de la barra de brillo"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Ahora puedes bajar el nivel del brillo desde la parte superior de la pantalla para atenuarla aún más.\n\nEsto funciona mejor si estás en un ambiente oscuro."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Quitar la combinación de teclas de atenuación extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Se quitó el atajo de atenuación extra. Para bajar el brillo, usa la barra de brillo normal."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 3aff0fa..2e823ed 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> y otras aplicaciones abiertas han detectado esta captura de pantalla."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Añadir a nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluir enlace"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Grabación de pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando grabación de pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación continua de una sesión de grabación de la pantalla"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"¿Empezar a grabar?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Mientras grabas contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Mientras grabas una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Empezar a grabar"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"¿Grabar la pantalla?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una aplicación"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabar toda la pantalla"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cuando grabas toda la pantalla, se graba todo lo que se muestre en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cuando grabas una aplicación, se graba todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabar pantalla"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Elegir una aplicación para grabar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grabar audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio del dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sonido de tu dispositivo, como música, llamadas y tonos de llamada"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotipo de la aplicación"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Reintentar"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toca para cancelar la autenticación"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Salvapantallas"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"No molestar"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritarios"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"No hay dispositivos vinculados disponibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar o desconectar un dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartir audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca para cambiar o compartir el audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string>
     <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_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>
@@ -378,18 +383,18 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Grabar pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Detener"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string>
-    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe errores"</string>
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string>
     <string name="performance" msgid="6552785217174378320">"Rendimiento"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string>
     <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
-    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de rastreo personalizados"</string>
+    <string name="custom" msgid="3337456985275158299">"Config. personalizada"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Ajustes de traza personalizados"</string>
     <string name="restore_default" msgid="5259420807486239755">"Restaurar ajustes predeterminados"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos automáticos"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos automáticos"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Ajustes"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Otro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Mostrar u ocultar aplicaciones recientes"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritarios"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hecho"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ajustes"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"No te molestarán los sonidos ni las vibraciones, excepto las alarmas. Seguirás escuchando el contenido que quieras reproducir, como música, vídeos y juegos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Carga completa en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets en la pantalla de bloqueo"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> añadido a la pantalla de bloqueo"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Desliza hacia la izquierda para iniciar el tutorial de la comunidad"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Cerrar"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluidos contraseñas, detalles de pagos, fotos, mensajes y audio que reproduzcas."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"¿Empezar a grabar o enviar contenido?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestre en la pantalla o se reproduzca en el dispositivo mientras grabas o envías contenido, incluidos contraseñas, detalles de pagos, fotos, mensajes y audio que reproduzcas."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Toda la pantalla"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Una sola aplicación"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Compartir o grabar una aplicación"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"¿Empezar a grabar o enviar contenido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cuando compartes, grabas o envías contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartes, grabas o envías una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Empezar"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartir o grabar una aplicación"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"¿Compartir tu pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir una aplicación"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda la pantalla"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Cuando compartes toda tu pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que se muestra o reproduce en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando envías contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando envías una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Empezar a enviar"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elegir una aplicación para compartir"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Enviar tu pantalla?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar solo una aplicación"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Enviar toda la pantalla"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cuando envías toda tu pantalla, se ve todo lo que hay en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cuando envías una aplicación, se ve todo lo que se muestre o reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Enviar pantalla"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Elegir una aplicación para enviar"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"¿Empezar a compartir?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Cuando compartes, grabas o envías contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Cuando compartes, grabas o envías una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Empezar"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Siguiente"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"El uso compartido se detiene al cambiar de aplicación"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Compartir esta aplicación"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Volver"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, buena conexión"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión disponible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Llamadas de emergencia o SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabajo"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión solo para algunos"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"El configurador de UI del sistema te ofrece otras formas de modificar y personalizar la interfaz de usuario de Android. Estas funciones experimentales pueden cambiar, fallar o desaparecer en futuras versiones. Te recomendamos que tengas cuidado."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidad"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Combinaciones de teclas"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No hay resultados de búsqueda"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para volver"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para ir al inicio"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hecho"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"¡Bien hecho!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil con tres dedos moviéndose hacia la derecha y la izquierda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla del dispositivo que muestra una animación del gesto para volver atrás"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para volver, desliza con tres dedos hacia la izquierda o la derecha en cualquier parte del panel táctil.\n\nTambién puedes hacerlo con la combinación de teclas asignada + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"¡Bien hecho!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para ir a la pantalla de inicio en cualquier momento, desliza hacia arriba con tres dedos desde la parte inferior de la pantalla."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"¡Muy bien!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Has completado el gesto para ir a la pantalla de inicio."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de acción"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acceder a tus aplicaciones, pulsa la tecla de acción de tu teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"¡Enhorabuena!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Has completado el gesto de la tecla de acción.\n\nLa combinación de teclas Acción + / muestra todas las combinaciones de teclas que tienes disponibles."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir a la pantalla de inicio, desliza hacia arriba con tres dedos en el panel táctil"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver las aplicaciones recientes, desliza hacia arriba y mantén pulsado el panel táctil con tres dedos"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas tus aplicaciones, pulsa la tecla de acción de tu teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Oculta"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquea para ver"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa el panel táctil para volver atrás"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Desliza hacia la izquierda o hacia la derecha con tres dedos. Toca para aprender a usar más gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa el panel táctil para ir a la pantalla de inicio"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Desliza hacia arriba y mantén pulsado con tres dedos. Toca para aprender a usar más gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa el teclado para ver todas las aplicaciones"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pulsa la tecla de acción en cualquier momento. Toca para aprender a usar más gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"La atenuación extra ahora forma parte de la barra de brillo"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Ahora puedes atenuar aún más tu pantalla reduciendo el nivel de brillo desde la parte superior.\n\nFunciona mejor cuando estás en un lugar con poca luz."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Eliminar acceso directo a la atenuación extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Acceso directo a la atenuación extra eliminado. Para reducir el brillo, usa la barra de brillo normal."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 5b38ff6..09f461f 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja muud avatud rakendused tuvastasid selle ekraanipildi."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lisa märkmesse"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Kaasa link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekraanisalvesti"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekraanisalvestuse töötlemine"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Kas alustada salvestamist?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Salvestamise ajal on Androidil juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Rakenduse salvestamise ajal on Androidil juurdepääs kõigele, mis on selles rakenduses nähtaval või mida selles esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Alusta salvestamist"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Kas salvestada ekraanikuvast video?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ühe rakenduse salvestamine"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Kogu ekraanikuva salvestamine"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kui salvestate kogu ekraani, salvestatakse kõik ekraanil kuvatud andmed. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kui salvestate rakendust, salvestatakse kõik, mida selles rakenduses näidatakse või esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekraanikuva jäädvustamine"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Vali salvestamiseks rakendus"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Salvesta heli"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Seadme heli"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Seadmest pärinev heli, nt muusika, kõned ja helinad"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Saada"</string>
     <string name="cancel" msgid="1089011503403416730">"Tühista"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Rakenduse logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Kinnita"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Proovi uuesti"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Puudutage autentimise tühistamiseks"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekraanisäästja"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mitte segada"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteetsed režiimid"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ühtegi seotud seadet pole saadaval"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Puudutage seadme ühendamiseks või ühenduse katkestamiseks"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Heli jagamine"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Puudutage helile lülitumiseks või selle jagamiseks"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Reaalajas subtiitrid"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Reaalajas subtiitrid"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ava menüü Seaded"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu seade"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Lehe Ülevaade sisse- ja väljalülitamine"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteetsed režiimid"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Seaded"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Seadistamine"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Helid ja värinad ei sega teid. Kuulete siiski äratusi. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Kohanda"</string>
@@ -478,15 +482,14 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Aeglane laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laadimine • Täis <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> pärast"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Lukustuskuva vidinad"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g> lisati lukustuskuvale"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ühise õpetuse käivitamiseks pühkige vasakule"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Kohandage"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Loobuge"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisage ja eemaldage selles ruumis oma vidinaid ning muutke nende järjestust"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisage ja eemaldage vidinaid ning muutke nende järjestust"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisage rohkem vidinaid"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Vajutage pikalt vidinate kohandamiseks"</string>
-    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Kohanda vidinaid"</string>
+    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Vidinate kohandamine"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"Avage vidinate kohandamiseks"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Keelatud vidina rakenduseikoon"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"Installitava vidina rakenduseikoon"</string>
@@ -495,13 +498,13 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Lisa vidin"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Valmis"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Vidinate lisamine"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Pääsege kiiresti juurde rakenduse lemmikvidinatele ilma tahvelarvutit avamata."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Kiire juurdepääs rakenduse lemmikvidinatele ilma tahvelarvutit lukust avamata."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Kas lubada lukustuskuval kõik vidinad?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Ava seaded"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Kas lõpetada töörakenduste peatamine?"</string>
     <string name="work_mode_turn_on" msgid="907813741770247267">"Lõpeta peatamine"</string>
     <string name="accessibility_action_label_close_communal_hub" msgid="6790396569621032333">"Lukustuskuva vidinate sulgemine"</string>
-    <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Kohanda vidinaid"</string>
+    <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"Vidinate kohandamine"</string>
     <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"Lukustuskuva vidinad"</string>
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"vidina valimine"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"eemaldage vidin"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Rakendus <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Kas alustada salvestamist või ülekannet?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Seda funktsiooni pakkuv teenus saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Kogu ekraanikuva"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Üks rakendus"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Rakenduse jagamine või salvestamine"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Kas alustada rakendusega <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> salvestamist või ülekannet?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kui jagate, salvestate või kannate üle, on rakendusel <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kui jagate, salvestate või kannate rakendust üle, on rakendusel <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> juurdepääs kõigele, mida selles rakenduses kuvatakse või esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Alusta"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Rakenduse jagamine või salvestamine"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Kas jagada teie ekraani rakendusega <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Jaga ühte rakendust"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaga kogu ekraani"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kogu ekraanikuva jagamisel on kogu sellel kuvatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Rakenduse jagamisel on kogu rakenduses kuvatav või esitatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaga ekraani"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on selle valiku keelanud"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Kas alustada ülekandmist?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kui kannate üle, on Androidil juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Rakenduse ülekandmise ajal on Androidil juurdepääs kõigele, mis on selles rakenduses nähtaval või mida selles esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Alusta ülekandmist"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vali jagamiseks rakendus"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Kas kanda ekraanikuva üle?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Ühe rakenduse ülekandmine"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Kogu ekraanikuva ülekandmine"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kogu ekraanikuva ülekandmisel on kogu sellel kuvatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Rakenduse ülekandmisel on kogu rakenduses kuvatav või esitatav sisu nähtav. Seega olge ettevaatlik näiteks paroolide, makseteabe, sõnumite, fotode ning heli ja videoga."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Kanna üle ekraanikuva"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Vali ülekandmiseks rakendus"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Kas alustada jagamist?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kui jagate, salvestate või kannate üle, on Androidil juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kui jagate, salvestate või kannate rakendust üle, on Androidil juurdepääs kõigele, mida selles rakenduses kuvatakse või esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Alusta"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Järgmine"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Rakenduste vahetamisel jagamine peatatakse"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Jaga hoopis seda rakendust"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Lülitu tagasi"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliit, hea ühendus"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliit, ühendus on saadaval"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelliit-SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hädaabikõned või SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Tööprofiil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Kõik ei pruugi sellest rõõmu tunda"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Süsteemi kasutajaliidese tuuner pakub täiendavaid võimalusi Androidi kasutajaliidese muutmiseks ja kohandamiseks. Need katselised funktsioonid võivad muutuda, rikki minna või tulevastest versioonidest kaduda. Olge jätkamisel ettevaatlik."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Juurdepääsetavus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klaviatuuri otseteed"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Otsingutulemused puuduvad"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tagasiliikumisliigutus"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Avakuvale liikumise liigutus"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Toiminguklahv"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Väga hea!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Puuteplaat kolme paremale ja vasakule liikuva sõrmega"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Seadme ekraanil näidatakse tagasiliigutuse animatsiooni"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Tagasiliikumiseks pühkige puuteplaadil kolme sõrmega vasakule või paremale.\n\nSamuti saate selle jaoks kasutada klaviatuuri otseteed toiminguklahv + paoklahv."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Väga hea!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Mis tahes ajal avakuvale liikumiseks pühkige kolme sõrmega ekraanikuva allosast üles."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Hästi tehtud!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Tegite avakuvale minemise liigutuse."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Toiminguklahv"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Rakendustele juurdepääsemiseks vajutage klaviatuuril toiminguklahvi."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Õnnitleme!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Tegite toiminguklahvi liigutuse.\n\nKombinatsiooni toiminguklahv + / vajutamisel kuvatakse kõik saadaolevad otseteed."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Avakuvale liikumiseks pühkige puuteplaadil kolme sõrmega üles"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Hiljutiste rakenduste kuvamiseks pühkige puuteplaadil kolme sõrmega üles ja hoidke sõrmi puuteplaadil"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Kõigi oma rakenduste kuvamiseks vajutage klaviatuuril toiminguklahvi"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Peidetud"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Vaatamiseks avage"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Puuteplaadi kasutamine tagasiliikumiseks"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Pühkige kolme sõrmega vasakule või paremale. Puudutage žestide kohta lisateabe saamiseks."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Puuteplaadi kasutamine avakuvale liikumiseks"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pühkige kolme sõrmega üles ja hoidke sõrmi plaadil. Puudutage žestide kohta lisateabe saamiseks."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatuuri kasutamine kõigi rakenduste kuvamiseks"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Vajutage soovitud ajal toiminguklahvi. Puudutage žestide kohta lisateabe saamiseks."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Funktsioon „Eriti tume“ on nüüd osa ereduse reguleerimise ribast"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Nüüd saate muuta ekraani eriti tumedaks, vähendades ereduse taset ekraani ülaosast veelgi rohkem.\n\nSee toimib kõige paremini hämaras keskkonnas."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Eemalda funktsiooni „Eriti tume“ otsetee"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Funktsiooni „Eriti tume“ otsetee eemaldati. Kasutage ereduse vähendamiseks tavapärast ereduse reguleerimise riba."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 6471c3b..329b6e2 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> aplikazioak eta irekitako beste aplikazio batzuek pantaila-argazkia hauteman dute."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Gehitu oharrean"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Sartu esteka"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Pantaila-grabagailua"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pantaila-grabaketa prozesatzen"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Grabatzen hasi nahi duzu?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Grabatzen duzunean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Aplikazio bat grabatzen duzunean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Hasi grabatzen"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Pantaila grabatu nahi duzu?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabatu aplikazio bat"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Grabatu pantaila osoa"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pantaila osoa grabatzen ari zarenean, pantailan agertzen den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Aplikazio bat grabatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia grabatzen da. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Grabatu pantaila"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Aukeratu zer aplikazio grabatu nahi duzun"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Grabatu audioa"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Gailuaren audioa"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Gailuko soinuak; adibidez, musika, deiak eta tonuak"</string>
@@ -126,7 +130,7 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Sakatu ikusteko"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Errore bat gertatu da pantaila-grabaketa gordetzean"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore bat gertatu da pantaila grabatzen hastean"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Grabaketa gelditu nahi duzu?"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Grabatzeari utzi nahi diozu?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Pantaila osoa grabatzen ari zara"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"<xliff:g id="APP_NAME">%1$s</xliff:g> grabatzen ari zara"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Utzi grabatzeari"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Bidali"</string>
     <string name="cancel" msgid="1089011503403416730">"Utzi"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Aplikazioaren logotipoa"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Berretsi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Saiatu berriro"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Sakatu hau autentifikazioa bertan behera uzteko"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Pantaila-babeslea"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ez molestatzeko modua"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Lehentasunezko moduak"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetootha"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ez dago parekatutako gailurik erabilgarri"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Sakatu hau gailu bat konektatu edo deskonektatzeko"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audioa partekatzea"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Audioa aldatu edo partekatzeko, sakatu hau"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Pantaila-grabaketa"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Hasi"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Gelditu"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabatu arazoa"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Akatsen txostena"</string>
@@ -389,7 +394,7 @@
     <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string>
     <string name="thermal" msgid="6758074791325414831">"Termikoa"</string>
     <string name="custom" msgid="3337456985275158299">"Pertsonalizatua"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Arrasto pertsonalizatuen ezarpenak"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Arrastoaren ezarpen pertsonalizatuak"</string>
     <string name="restore_default" msgid="5259420807486239755">"Leheneratu balio lehenetsia"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Istanteko azpitituluak"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Istanteko azpitituluak"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ireki Ezarpenak"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Beste gailu bat"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aldatu ikuspegi orokorra"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Lehentasunezko moduak"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Eginda"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ezarpenak"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfiguratu"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak joko ditu. Hala ere, zuk erreproduzitutako guztia entzun ahal izango duzu, besteak beste, musika, bideoak eta jokoak."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pertsonalizatu"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Pantaila blokeatuko widgetak"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta gehitu da pantaila blokeatuan"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Tutorial komuna hasteko, pasatu hatza ezkerrera"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pertsonalizatu"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Baztertu"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Zerbait grabatzen edo igortzen duzunean, pantailan ikusgai dagoen edo gailuak erreproduzitzen duen informazio guztia atzi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak sartzen dira informazio horretan."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Grabatzen edo igortzen hasi nahi duzu?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Zerbait grabatzen edo igortzen duzunean, pantailan ikusgai dagoen edo gailuak erreproduzitzen duen informazio guztia erabili ahalko du funtzio hori eskaintzen duen zerbitzuak. Pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak sartzen dira informazio horretan."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Pantaila osoa"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Aplikazio bakar bat"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Partekatu edo grabatu aplikazio bat"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioarekin grabatzen edo igortzen hasi nahi duzu?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Edukia partekatzen, grabatzen edo igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Aplikazio bat partekatzen, grabatzen edo igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Hasi"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partekatu edo grabatu aplikazio bat"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioarekin pantaila partekatu nahi duzu?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Aplikazio bat partekatu"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Pantaila osoa partekatu"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pantaila osoa partekatzen ari zarenean, pantailan duzun guztia ikus dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Aplikazio bat partekatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia ikusi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partekatu pantaila"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak aukera desgaitu du"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Igortzen hasi nahi duzu?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Edukia igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Aplikazio bat igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Hasi igortzen"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Aukeratu zer aplikazio partekatu nahi duzun"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Pantaila igorri nahi duzu?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Igorri aplikazio bat"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Igorri pantaila osoa"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pantaila osoa igortzen ari zarenean, pantailan duzun guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Aplikazio bat igortzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia dago ikusgai. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Igorri pantaila"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Aukeratu zer aplikazio igorri nahi duzun"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Partekatzen hasi nahi duzu?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Edukia partekatzen, grabatzen edo igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Aplikazio bat partekatzen, grabatzen edo igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Hasi"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Hurrengoa"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Partekatzea pausatu egiten da aplikazioz aldatzean"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Partekatu aplikazioa"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Itzuli"</string>
@@ -612,7 +624,7 @@
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Zure gurasoak kudeatzen du gailua. Zure gurasoak gailuko informazioa ikusi eta kudea dezake; besteak beste, zer aplikazio erabiltzen dituzun, zure kokapena zein den eta pantaila aurrean zenbat eta noiz egoten zaren."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPNa"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent bidez desblokeatuta"</string>
-    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Blokeatu egin da gailua. Autentifikatze-saiakera gehiegi egin dira."</string>
+    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Blokeatu egin da gailua. Autentifikatzeko saiakera gehiegi egin dira."</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Gailua blokeatuta dago\nEzin izan da autentifikatu"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="accessibility_volume_settings" msgid="1458961116951564784">"Soinuaren ezarpenak"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelitea, konexio ona"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelitea, konexioa erabilgarri"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelite bidezko SOS komunikazioa"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Larrialdi-deiak edo SOS komunikazioa"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Laneko profila"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Dibertsioa batzuentzat, baina ez guztientzat"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Sistemaren erabiltzaile-interfazearen konfiguratzaileak Android erabiltzaile-interfazea moldatzeko eta pertsonalizatzeko modu gehiago eskaintzen dizkizu. Baliteke eginbide esperimental horiek hurrengo kaleratzeetan aldatuta, etenda edo desagertuta egotea. Kontuz erabili."</string>
@@ -970,7 +983,7 @@
     <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> - <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g> (<xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>)"</string>
     <string name="wifi_is_off" msgid="5389597396308001471">"Wifi-konexioa desaktibatuta dago"</string>
-    <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth bidezko konexioa desaktibatuta dago"</string>
+    <string name="bt_is_off" msgid="7436344904889461591">"Bluetootha desaktibatuta dago"</string>
     <string name="dnd_is_off" msgid="3185706903793094463">"Ez molestatzeko modua desaktibatuta dago"</string>
     <string name="dnd_is_on" msgid="7009368176361546279">"Aktibatuta dago ez molestatzeko modua"</string>
     <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"Ez molestatzeko modua aktibatu du arau automatiko batek (<xliff:g id="ID_1">%s</xliff:g>)."</string>
@@ -1189,7 +1202,7 @@
     <string name="select_conversation_text" msgid="3376048251434956013">"Sakatu elkarrizketa bat orri nagusian gehitzeko"</string>
     <string name="no_conversations_text" msgid="5354115541282395015">"Azkenaldiko elkarrizketak agertuko dira hemen"</string>
     <string name="priority_conversations" msgid="3967482288896653039">"Lehentasunezko elkarrizketak"</string>
-    <string name="recent_conversations" msgid="8531874684782574622">"Azken elkarrizketak"</string>
+    <string name="recent_conversations" msgid="8531874684782574622">"Azkenaldiko elkarrizketak"</string>
     <string name="days_timestamp" msgid="5821854736213214331">"Duela <xliff:g id="DURATION">%1$s</xliff:g> egun"</string>
     <string name="one_week_timestamp" msgid="4925600765473875590">"Duela astebete"</string>
     <string name="two_weeks_timestamp" msgid="9111801081871962155">"Duela bi aste"</string>
@@ -1213,7 +1226,7 @@
     <string name="status_before_loading" msgid="1500477307859631381">"Laster agertuko da edukia"</string>
     <string name="missed_call" msgid="4228016077700161689">"Dei galdua"</string>
     <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
-    <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azken mezuak, dei galduak eta egoerei buruzko informazio eguneratua"</string>
+    <string name="people_tile_description" msgid="8154966188085545556">"Ikusi azkenaldiko mezuak, dei galduak eta egoerei buruzko informazio eguneratua"</string>
     <string name="people_tile_title" msgid="6589377493334871272">"Elkarrizketa"</string>
     <string name="paused_by_dnd" msgid="7856941866433556428">"Ez molestatzeko moduak pausatu du"</string>
     <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak mezu bat bidali du: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erabilerraztasuna"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Lasterbideak"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ez dago bilaketa-emaitzarik"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Atzera egiteko keinua"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Orri nagusira joateko keinua"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Ekintza-tekla"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bikain!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"3 hatz ukipen-panel baten gainean eskuinera eta ezkerrera mugitzen erakusten duen irudia"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Gailuaren pantailan atzera egiteko keinuaren animazioa erakusten duen irudia"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Atzera egiteko, pasatu 3 hatz ezkerrera edo eskuinera ukipen-panelean.\n\nEkintza + Ihes lasterbidea ere erabil dezakezu horretarako."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Bikain!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Orri nagusira joateko, pasatu 3 hatz pantailaren behealdetik gora."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Ederki!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ikasi duzu hasierako pantailara joateko keinua."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Ekintza-tekla"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Aplikazioak atzitzeko, sakatu teklatuko ekintza-tekla."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Zorionak!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Ekintza-teklaren keinua egin duzu.\n\nEkintza + / sakatuz gero, erabilgarri dituzun lasterbide guztiak ikusiko dituzu."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Hasierako pantailara joateko, pasatu 3 hatz ukipen-pantailan"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Azkenaldiko aplikazioak ikusteko, pasatu 3 hatz ukipen-panelean gora eta eduki sakatuta"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Aplikazio guztiak ikusteko, sakatu teklatuko ekintza-tekla"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Desitxuratuta"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desblokeatu ikusteko"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Erabili ukipen-panela atzera egiteko"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Pasatu 3 hatz ezkerrera edo eskuinera. Sakatu keinu gehiago ikasteko."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela hasierako pantailara joateko"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasatu 3 hatz gora eta eduki sakatuta. Sakatu keinu gehiago ikasteko."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Erabili teklatua aplikazio guztiak ikusteko"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Sakatu ekintza-tekla edonoiz. Sakatu keinu gehiago ikasteko."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Orain, argitasun-barran agertzen da Are ilunago"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Orain, pantaila are ilunago jar dezakezu, pantailaren goialdetik argitasun-maila are gehiago jaitsita.\n\nIngurune ilun batean zaudenean funtzionatzen du ondoen."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Kendu Are ilunago eginbidearen lasterbidea"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Kendu da Are ilunago eginbidearen lasterbidea. Argitasuna murrizteko, erabili argitasun-barra arrunta."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f4b9d9e..f845027 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> و سایر برنامه‌های باز این نماگرفت را تشخیص دادند."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"افزودن به یادداشت"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"اضافه کردن پیوند"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"‫<xliff:g id="APPNAME">%1$s</xliff:g> ‏<xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ضبط‌کن صفحه‌نمایش"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"درحال پردازش ضبط صفحه‌نمایش"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحه‌نمایش"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ضبط شروع شود؟"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"‏درحین ضبط کردن، Android به همه محتوایی که در صفحه‌تان نمایان است یا در دستگاهتان پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"‏درحین ضبط کردن برنامه، Android به همه محتوایی که در آن برنامه نمایان است یا پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"شروع ضبط"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"صفحه‌نمایش ضبط شود؟"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ضبط یک برنامه"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ضبط کل صفحه‌نمایش"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"وقتی کل صفحه‌نمایش را ضبط می‌کنید، هر چیزی که در صفحه‌نمایش نشان داده شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"وقتی برنامه‌ای را ضبط می‌کنید، هر چیزی که در آن برنامه نشان داده شود یا پخش شود ضبط خواهد شد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ضبط صفحه‌نمایش"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"برنامه‌ای را برای ضبط انتخاب کنید"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ضبط صدا"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"صدای دریافتی از دستگاه"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"صدای دریافتی از دستگاه، مثل موسیقی، تماس، و آهنگ زنگ"</string>
@@ -130,8 +134,8 @@
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"اکنون درحال ضبط کل صفحه‌نمایشتان هستید"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"اکنون درحال ضبط <xliff:g id="APP_NAME">%1$s</xliff:g> هستید"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"توقف ضبط"</string>
-    <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"درحال هم‌رسانی صفحه‌نمایش"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"صفحه هم‌رسانی متوقف شود؟"</string>
+    <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"درحال هم‌رسانی صفحه"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"هم‌رسانی صفحه متوقف شود؟"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> هستید"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"اکنون درحال هم‌رسانی کل صفحه‌نمایشتان با یک برنامه هستید"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"اکنون درحال هم‌رسانی <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> هستید"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ارسال"</string>
     <string name="cancel" msgid="1089011503403416730">"لغو"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"نشان‌واره برنامه"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"تأیید"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"امتحان مجدد"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستی‌آزمایی تک‌ضرب بزنید"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"محافظ صفحه"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"اترنت"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"مزاحم نشوید"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"حالت‌های اولویت‌دار"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوتوث"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"هیچ دستگاه مرتبط شده‌ای موجود نیست"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"برای اتصال یا قطع اتصال دستگاه، تک‌ضرب بزنید"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"اشتراک صدا"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"برای فعال کردن و هم‌رسانی صدا، تک‌ضرب بزنید"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ذخیره‌شده"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"قطع اتصال"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کردن"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string>
     <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_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>
@@ -321,7 +326,7 @@
     <string name="quick_settings_mic_label" msgid="8392773746295266375">"دسترسی به میکروفون"</string>
     <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"دردسترس"</string>
     <string name="quick_settings_camera_mic_blocked" msgid="4710884905006788281">"مسدود"</string>
-    <string name="quick_settings_media_device_label" msgid="8034019242363789941">"دستگاه رسانه"</string>
+    <string name="quick_settings_media_device_label" msgid="8034019242363789941">"دستگاه رسانه‌ای"</string>
     <string name="quick_settings_user_title" msgid="8673045967216204537">"کاربر"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
     <string name="quick_settings_internet_label" msgid="6603068555872455463">"اینترنت"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیش‌تنظیم به‌روزرسانی نشد"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیش‌تنظیم"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس ناشنوایان زنده"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس ناشنوایان زنده"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"باز کردن تنظیمات"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"دستگاه دیگر"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"حالت‌های اولویت‌دار"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"تمام"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"تنظیمات"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"راه‌اندازی"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"به‌جز هشدارها، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش می‌کنید می‌شنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"سفارشی کردن"</string>
@@ -477,40 +481,39 @@
     <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن سریع • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ شدن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
-    <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ابزارک‌ها در صفحه قفل"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ابزاره‌ها در صفحه قفل"</string>
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"ابزاره <xliff:g id="WIDGET_NAME">%1$s</xliff:g> به صفحه قفل اضافه شد"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"برای شروع آموزش گام‌به‌گام عمومی، تند به‌چپ بکشید"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"سفارشی‌سازی"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"بستن"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"افزودن، برداشتن، و تغییر ترتیب ابزارک‌ها در این فضا"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"افزودن، برداشتن، و تغییر ترتیب ابزاره‌ها در این فضا"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"افزودن ابزارک‌های بیشتر"</string>
-    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"برای سفارشی‌سازی ابزارک‌ها، فشار طولانی دهید"</string>
-    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"سفارشی‌سازی ابزارک‌ها"</string>
+    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"برای سفارشی‌سازی ابزاره‌ها، فشار طولانی دهید"</string>
+    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"سفارشی‌سازی ابزاره‌ها"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"برای سفارشی‌سازی ابزاره‌ها، قفل دستگاه را باز کنید"</string>
-    <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"نماد برنامه برای ابزارک غیرفعال"</string>
-    <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"نماد برنامه مربوط به ابزارکی که درحال نصب است"</string>
-    <string name="edit_widget" msgid="9030848101135393954">"ویرایش ابزارک"</string>
+    <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"نماد برنامه برای ابزاره غیرفعال"</string>
+    <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"نماد برنامه مربوط به ابزاره‌ای که درحال نصب شدن است"</string>
+    <string name="edit_widget" msgid="9030848101135393954">"ویرایش ابزاره"</string>
     <string name="button_to_remove_widget" msgid="3948204829181214098">"برداشتن"</string>
-    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"افزودن ابزارک"</string>
+    <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"افزودن ابزاره"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"تمام"</string>
-    <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"افزودن ابزارک‌ها"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"بدون باز کردن قفل رایانه لوحی، به ابزارک برنامه‌های دلخواهتان فوراً دسترسی پیدا کنید."</string>
-    <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"هر نوع ابزارکی در صفحه قفل مجاز شود؟"</string>
+    <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"افزودن ابزاره"</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"بدون باز کردن قفل رایانه لوحی، به ابزاره برنامه‌های دلخواهتان فوراً دسترسی پیدا کنید."</string>
+    <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"هر نوع ابزاره‌ای در صفحه قفل مجاز باشد؟"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"باز کردن تنظیمات"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"مکث برنامه‌های کاری لغو شود؟"</string>
     <string name="work_mode_turn_on" msgid="907813741770247267">"لغو مکث"</string>
-    <string name="accessibility_action_label_close_communal_hub" msgid="6790396569621032333">"بستن ابزارک‌ها در صفحه قفل"</string>
-    <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"سفارشی‌سازی ابزارک‌ها"</string>
-    <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ابزارک‌ها در صفحه قفل"</string>
-    <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"انتخاب ابزارک"</string>
-    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"برداشتن ابزارک"</string>
-    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"جای‌گذاری ابزارک انتخاب‌شده"</string>
+    <string name="accessibility_action_label_close_communal_hub" msgid="6790396569621032333">"بستن ابزاره‌ها در صفحه قفل"</string>
+    <string name="accessibility_action_label_edit_widgets" msgid="3821868581348322346">"سفارشی‌سازی ابزاره‌ها"</string>
+    <string name="accessibility_content_description_for_communal_hub" msgid="1670220840599380118">"ابزاره‌ها در صفحه قفل"</string>
+    <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"انتخاب ابزاره"</string>
+    <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"برداشتن ابزاره"</string>
+    <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"جای‌گذاری ابزاره انتخاب‌شده"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"ابزاره‌های صفحه قفل"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"همه می‌توانند ابزاره‌ها را در صفحه قفل شما ببینند، حتی اگر رایانه لوحی قفل باشد."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"لغو انتخاب ابزاره"</string>
-    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزارک‌های صفحه قفل"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزارک، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزارک‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ابزاره‌های صفحه قفل"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"برای باز کردن برنامه بااستفاده از ابزاره، باید هویت خودتان را به‌تأیید برسانید. همچنین، به‌خاطر داشته باشید که همه می‌توانند آن‌ها را مشاهده کنند، حتی وقتی رایانه لوحی‌تان قفل است. برخی‌از ابزاره‌ها ممکن است برای صفحه قفل درنظر گرفته نشده باشند و ممکن است اضافه کردن آن‌ها در اینجا ناامن باشد."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"متوجه‌ام"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"منوی پایین‌پر"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه اطلاعاتی که روی صفحه‌نمایش قابل‌مشاهد است و هنگام ضبط کردن یا پخش محتوا از دستگاهتان پخش می‌شود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژه‌ها، جزئیات پرداخت، عکس‌ها، پیام‌ها، و صداهایی که پخش می‌کنید می‌شود."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ضبط یا پخش محتوا شروع شود؟"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"سرویس ارائه‌دهنده این عملکرد به همه اطلاعاتی که روی صفحه‌نمایش قابل‌مشاهد است و هنگام ضبط کردن یا پخش محتوا از دستگاهتان پخش می‌شود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژه‌ها، جزئیات پرداخت، عکس‌ها، پیام‌ها، و صداهایی که پخش می‌کنید می‌شود."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"کل صفحه"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"یک برنامه"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"هم‌رسانی یا ضبط برنامه"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"ضبط یا پخش محتوا با <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> شروع شود؟"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"وقتی درحال هم‌رسانی، ضبط، یا پخش محتوا هستید، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه محتوایی که در صفحه‌تان نمایان است یا در دستگاهتان پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"وقتی درحال هم‌رسانی، ضبط، یا پخش محتوای برنامه‌ای هستید، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه محتوایی که در آن برنامه نمایان است یا پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"شروع"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"هم‌رسانی یا ضبط برنامه"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"صفحه‌نمایش با <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> هم‌رسانی شود؟"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"هم‌رسانی کردن یک برنامه"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"هم‌رسانی کردن کل صفحه‌نمایش"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"وقتی کل صفحه‌نمایش را هم‌رسانی می‌کنید، هر چیزی که روی صفحه‌نمایش شما وجود داشته باشد برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابل‌مشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"وقتی برنامه‌ای را هم‌رسانی می‌کنید، هر چیزی که در آن برنامه نمایش داده شود یا پخش شود برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابل‌مشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"هم‌رسانی صفحه‌نمایش"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>این گزینه را غیرفعال کرده است"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"پخش محتوا شروع شود؟"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‏وقتی محتوا پخش می‌کنید، Android به همه محتوایی که در صفحه‌تان نمایان است یا در دستگاهتان پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏وقتی محتوای برنامه‌ای را پخش می‌کنید، Android به همه محتوایی که در آن برنامه نمایان است یا پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"شروع پخش محتوا"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"برنامه‌ای را برای هم‌رسانی انتخاب کنید"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"محتوای صفحه‌نمایش شما پخش شود؟"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"پخش کردن محتوای یک برنامه"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"پخش کردن محتوای کل صفحه"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"وقتی محتوای کل صفحه‌نمایش را پخش می‌کنید، هر چیزی که روی صفحه‌نمایش شما وجود دارد قابل‌مشاهده است. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"وقتی محتوای برنامه‌ای را پخش می‌کنید، هر چیزی که در آن برنامه پخش می‌شود قابل‌مشاهده است. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"پخش محتوای صفحه‌نمایش"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"برنامه‌ای را برای پخش محتوا انتخاب کنید"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"هم‌رسانی شروع شود؟"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‏وقتی درحال هم‌رسانی، ضبط، یا پخش محتوا هستید، Android به همه محتوایی که در صفحه‌تان نمایان است یا در دستگاهتان پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‏وقتی درحال هم‌رسانی، ضبط، یا پخش محتوای برنامه‌ای هستید، Android به همه محتوایی که در آن برنامه نمایان است یا پخش می‌شود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"شروع"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"بعدی"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"وقتی برنامه‌ها را تغییر می‌دهید، هم‌رسانی متوقف می‌شود"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"درعوض هم‌رسانی این برنامه"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"تغییر به حالت قبل"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ماهواره، اتصال خوب است"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ماهواره، اتصال دردسترس است"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"درخواست کمک ماهواره‌ای"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"تماس اضطراری یا درخواست کمک اضطراری"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"نمایه کاری"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"برای بعضی افراد سرگرم‌کننده است اما نه برای همه"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏«تنظیم‌کننده واسط کاربری سیستم» روش‌های بیشتری برای تنظیم دقیق و سفارشی کردن واسط کاربری Android در اختیار شما قرار می‌دهد. ممکن است این ویژگی‌های آزمایشی تغییر کنند، خراب شوند یا در نسخه‌های آینده جود نداشته باشند. با احتیاط ادامه دهید."</string>
@@ -1287,7 +1300,7 @@
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="230135078402003532">"یادداشت‌برداری"</string>
     <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"یادداشت‌برداری، <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
-    <string name="audio_sharing_description" msgid="8849060142768870004">"هم‌رسانی صدا"</string>
+    <string name="audio_sharing_description" msgid="8849060142768870004">"درحال هم‌رسانی صوتی"</string>
     <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همه‌فرستی"</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همه‌فرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string>
     <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همه‌فرستی کنید یا خروجی را تغییر دهید، همه‌فرستی کنونی متوقف خواهد شد"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"دسترس‌پذیری"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"میان‌برهای صفحه‌کلید"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میان‌برها"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"نتیجه‌ای برای جستجو پیدا نشد"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"اشاره برگشت"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"اشاره صفحه اصلی"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"دکمه کنش"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"عالی است!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"صفحه لمسی که سه انگشت را درحال حرکت به‌سمت راست و چپ نشان می‌دهد"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"صفحه‌نمایش دستگاه درحال نمایش پویانمایی مربوط به اشاره برگشتن"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"برای برگشتن، در هر جایی از صفحه لمسی، با سه انگشت تند به‌چپ یا راست بکشید.\n\nبرای این کار می‌توانید از میان‌بر صفحه‌کلید «کنش + گریز» هم استفاده کنید."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"عالی بود!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"اشاره برگشت را تکمیل کردید."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"رفتن به صفحه اصلی"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"برای رفتن به صفحه اصلی در هرزمانی، با سه انگشت از پایین صفحه‌نمایش تند به‌بالا بکشید."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"آفرین!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"اشاره رفتن به صفحه اصلی را تکمیل کردید."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"دکمه کنش"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"برای دسترسی به برنامه‌هایتان، دکمه کنش در صفحه‌کلید را فشار دهید."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"تبریک!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"اشاره دکمه کنش را تکمیل کردید.\n\n«کنش» + / همه میان‌برهای دردسترس را نمایش می‌دهد."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"برای رفتن به صفحه اصلی، در صفحه لمسی با سه انگشت تند به‌بالا بکشید"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"برای مشاهده برنامه‌های اخیر، در صفحه لمسی با سه انگشت تند به‌بالا بکشید و نگه دارید"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"برای مشاهده همه برنامه‌ها، دکمه کنش در صفحه‌کلید را فشار دهید"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"محوشده"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"برای مشاهده، قفل را باز کنید"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"برای برگشتن از صفحه لمسی استفاده کنید"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"با سه انگشت تند به‌چپ یا راست بکشید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"برای رفتن به صفحه اصلی از صفحه لمسی استفاده کنید"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"با سه انگشت تند به‌بالا بکشید و نگه دارید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"برای مشاهده همه برنامه‌ها، از صفحه‌کلید استفاده کنید"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"در هرزمانی دکمه کنش را فشار دهید. برای آشنایی با اشاره‌های بیشتر، تک‌ضرب بزنید."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"«بسیار کم‌نور» اکنون بخشی از نوار روشنایی است"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ازاین‌پس می‌توانید با پایین‌تر آوردن سطح روشنایی از بالای صفحه‌نمایش، صفحه‌نمایش را بسیار کم‌نور کنید.\n\nاین ویژگی زمانی بهترین عملکرد را دارد که در محیطی تاریک باشید."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"حذف میان‌بر «بسیار کم‌نور»"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"میان‌بر «بسیار کم‌نور» حذف شد. برای کم کردن روشنایی، از نوار معمول روشنایی استفاده کنید."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 79962f0..b9afcd5 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ja jotkin muut sovellukset havaitsivat tämän kuvakaappauksen."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lisää muistiinpanoon"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Lisää linkki"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Näytön tallentaja"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Näytön tallennusta käsitellään"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Aloitetaanko tallennus?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Kun tallennat, Android saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Kun striimaat sovellusta, Android saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, audiota tai videoita."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Aloita tallennus"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Tallennetaanko näytön toimintaa?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Tallenna yhdestä sovelluksesta"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tallenna koko näyttö"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kun tallennat koko näyttöä, kaikki näytöllä näkyvä sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kun tallennat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö tallennetaan. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Tallenna näyttö"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Valitse tallennettava sovellus"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Tallenna audiota"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Laitteen audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Musiikki, puhelut, soittoäänet ja muut äänet laitteesta"</string>
@@ -150,7 +154,7 @@
     <string name="issuerecord_title" msgid="286627115110121849">"Ongelman tallentaja"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Käsittely: Ongelman tallennus"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Ongelmankeräykseen liittyvä ilmoitus taustalla jatkuvasta toiminnasta"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tallennusongelma"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Tallennetaan ongelmaa"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Jaa"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"Ongelman tallennus tallennettiin"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Näytä napauttamalla"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Lähetä"</string>
     <string name="cancel" msgid="1089011503403416730">"Peru"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Sovelluksen logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Vahvista"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Yritä uudelleen"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Peru todennus napauttamalla"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Näytönsäästäjä"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Älä häiritse"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteettitilat"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Laitepareja ei ole käytettävissä"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Muodosta yhteys laitteeseen tai katkaise yhteys napauttamalla"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audionjako"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Vaihda tai jaa audiota napauttamalla"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Livetekstitys"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstitys"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Avaa Asetukset"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Muu laite"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Näytä/piilota viimeisimmät"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteettitilat"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Valmis"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Asetukset"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ota käyttöön"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Muokkaa"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu hitaasti • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Latautuu • Täynnä <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> päästä"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgetit lukitusnäytöllä"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ‑widget lisätty lukitusnäyttöön"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aloita yhteisöesittely pyyhkäisemällä vasemmalle"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Muokkaa"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hylkää"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisää, poista ja järjestä widgetejäsi uudelleen tässä tilassa"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Lisää, poista ja järjestä widgettejäsi uudelleen tässä tilassa"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Lisää widgetejä"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Yksilöi widgetit pitkällä painalluksella"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Muokkaa widgettejä"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Aloitetaanko tallentaminen tai striimaus?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Ominaisuuden tarjoavalla palvelulla on pääsy kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Koko näyttö"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Yksittäinen sovellus"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Jaa sovellus tai tallenna sen sisältöä"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Haluatko, että <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aloittaa tallennuksen tai striimauksen?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kun jaat, tallennat tai striimaat, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kun jaat, tallennat tai striimaat sovellusta, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Aloita"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Jaa sovellus tai tallenna sen sisältöä"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Saako <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> nähdä näyttösi?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Jaa yksi sovellus"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Jaa koko näyttö"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kun jaat koko näytön, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sen sisälllön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kun jaat sovelluksen, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sovelluksessa näkyvän tai toistetun sisällön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaa näyttö"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on poistanut vaihtoehdon käytöstä"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Aloitetaanko striimaus?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kun striimaat, Android saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kun striimaat sovellusta, Android saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Aloita striimaus"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Valitse jaettava sovellus"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Striimataanko näyttö?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Striimaa yksi sovellus"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Striimaa koko näyttö"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kun striimaat koko näyttöä, kaikki näytön sisältö on näkyvillä. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kun striimaat sovellusta, kaikki sovelluksessa näkyvä tai toistettu sisältö on näkyvillä. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Striimaa näyttö"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Valitse striimattava sovellus"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Aloitetaanko jakaminen?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kun jaat, tallennat tai striimaat, Android saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kun jaat, tallennat tai striimaat, Android saa pääsyn kaikkeen sovelluksella näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Aloita"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Seuraava"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Jakaminen keskeytyy, kun vaihdat sovelluksia"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Jaa tämä sovellus"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Vaihda takaisin"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliitti, hyvä yhteys"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliitti, yhteys saatavilla"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hätäpuhelut tai Satellite SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Työprofiili"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Ei sovellu kaikkien käyttöön"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner antaa lisämahdollisuuksia Android-käyttöliittymän muokkaamiseen. Nämä kokeelliset ominaisuudet voivat muuttua, lakata toimimasta tai kadota milloin tahansa. Jatka omalla vastuullasi."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Saavutettavuus"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pikanäppäimet"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ei hakutuloksia"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Takaisin-ele"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Etusivu-ele"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Toimintonäppäin"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Hienoa!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Kosketuslevy, jolla kolme sormea liikkuu oikealle ja vasemmalle"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Laitteen näyttö, jolla näkyy animaatio takaisin-eleestä"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Jos haluat siirtyä takaisin, pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella.\n\nVoit myös käyttää pikanäppäinyhdistelmää toimintonäppäin + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Voit siirtyä aloitusnäytölle milloin tahansa pyyhkäisemällä ylös näytön alareunasta kolmella sormella."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Hienoa!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Olet oppinut aloitusnäytölle palaamiseleen."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Toimintonäppäin"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Voit käyttää sovelluksia painamalla näppäimistön toimintonäppäintä."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Onnittelut!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Olet oppinut toimintonäppäineleen.\n\nToiminto + / tuo esiin kaikki käytettävissä olevat pikakomennot."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Jos haluat siirtyä aloitussivulle, pyyhkäise kosketuslevyllä ylös kolmella sormella"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Näet äskeiset sovellukset, kun pyyhkäiset ylös ja pidät kosketuslevyä painettuna kolmella sormella."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Jos haluat nähdä kaikki sovellukset, paina näppäimistön toimintonäppäintä"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Sensuroitu"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Avaa lukitus ja katso tiedot"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Takaisin siirtyminen kosketuslevyn avulla"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Pyyhkäise vasemmalle tai oikealle kolmella sormella. Lue lisää eleistä napauttamalla."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Aloitusnäytölle siirtyminen kosketuslevyn avulla"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella. Lue lisää eleistä napauttamalla."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kaikkien sovellusten näkeminen näppäimistön avulla"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Voit painaa toimintonäppäintä milloin tahansa. Lue lisää eleistä napauttamalla."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Erittäin himmeä on nyt osa kirkkauspalkkia"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Voit nyt tehdä näytöstä erittäin himmeän vähentämällä kirkkautta vieläkin enemmän näytön yläreunasta.\n\nTämä toimii parhaiten pimeässä ympäristössä."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Poista erittäin himmeä ‑pikakomento"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Erittäin himmeä ‑pikakomento poistettu. Voit vähentää kirkkautta tavallisesta kirkkauspalkista."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index f34ce98..07e131e 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applis ouvertes ont détecté cette capture d\'écran."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ajouter à une note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inclure le lien"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Enregistreur d\'écran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Commencer l\'enregistrement?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Pendant l\'enregistrement, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Lorsque vous enregistrez une appli, Android a accès à tout ce qui est visible ou lu sur cette appli. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Commencer l\'enregistrement"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer votre écran?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer l\'écran entier"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'affiche sur votre écran est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans cette appli est enregistré. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choisir l\'appli à enregistrer"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Enregistrer des fichiers audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio de l\'appareil"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons de l\'appareil comme la musique, les appels et les sonneries"</string>
@@ -123,7 +127,7 @@
     <string name="screenrecord_stop_label" msgid="72699670052087989">"Arrêter"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Partager"</string>
     <string name="screenrecord_save_title" msgid="1886652605520893850">"Enregistrement sauvegardé"</string>
-    <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez pour afficher"</string>
+    <string name="screenrecord_save_text" msgid="3008973099800840163">"Touchez ceci pour afficher"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Erreur d\'enregistrement de l\'écran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Arrêter l\'enregistrement?"</string>
@@ -152,8 +156,8 @@
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification continue pour une session de collecte d\'un problème"</string>
     <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Enregistrement du problème"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"L\'enregistrement du problème a été enregistré"</string>
-    <string name="issuerecord_save_text" msgid="1205985304551521495">"Touchez ici pour afficher l\'enregistrement"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"Le problème a été enregistré"</string>
+    <string name="issuerecord_save_text" msgid="1205985304551521495">"Touchez ceci pour afficher l\'enregistrement"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Erreur lors de l\'enregistrement du problème"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Erreur lors du démarrage de l\'enregistrement du problème"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Affichage plein écran"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
     <string name="cancel" msgid="1089011503403416730">"Annuler"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo de l\'appli"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmer"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Réessayer"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Touchez ici pour annuler l\'authentification"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Écran de veille"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun des appareils associés n\'est disponible"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Touchez pour connecter ou déconnecter un appareil"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partage audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Touchez pour passer d\'un appareil à l\'autre ou pour partager le contenu audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string>
@@ -306,7 +310,8 @@
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string>
     <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_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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistrement d\'écran"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bogue"</string>
@@ -387,9 +392,9 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string>
     <string name="performance" msgid="6552785217174378320">"Performance"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string>
-    <string name="thermal" msgid="6758074791325414831">"Thermique"</string>
-    <string name="custom" msgid="3337456985275158299">"Personnalisé"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres personnalisés de la trace"</string>
+    <string name="thermal" msgid="6758074791325414831">"Chaleur"</string>
+    <string name="custom" msgid="3337456985275158299">"Type personnalisé"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Paramètres de traçage personnalisés"</string>
     <string name="restore_default" msgid="5259420807486239755">"Restaurer la valeur par défaut"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Sous-titres instantanés"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Basculer l\'aperçu"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurer"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par les sons et les vibrations, sauf pour les alarmes. Vous entendrez tout ce que vous choisissez d\'écouter, y compris la musique, les vidéos et les jeux."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
@@ -478,14 +482,13 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"En recharge lente : <xliff:g id="PERCENTAGE">%2$s</xliff:g> • Terminée <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge en cours… • Se terminera dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets sur l\'écran de verrouillage"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Ajout du widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> à l\'écran de verrouillage"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer l\'écran vers la gauche pour démarrer le tutoriel communautaire"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personnaliser"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Fermer"</string>
     <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ajouter, retirer et réorganiser vos widgets dans cet espace"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ajouter plus de widgets"</string>
-    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Maintenez le doigt pour personnaliser les widgets"</string>
+    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Appuyez longuement pour personnaliser les widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personnaliser les widgets"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"Déverrouiller pour personnaliser des widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icône d\'appli pour un widget désactivé"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"N\'importe qui peut voir les widgets sur votre écran de verrouillage, même si votre tablette est verrouillée."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"désélectionner le widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets de l\'écran de verrouillage"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut les voir, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage et il pourrait être dangereux de les ajouter ici."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pour ouvrir une appli à l\'aide d\'un widget, vous devrez confirmer votre identité. En outre, gardez à l\'esprit que tout le monde peut voir les widgets, même lorsque votre tablette est verrouillée. Certains widgets n\'ont peut-être pas été conçus pour votre écran de verrouillage, et il pourrait être dangereux de les ajouter ici."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu déroulant"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information qui est visible sur votre écran ou lue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et les contenus audio que vous faites jouer."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Commencer à enregistrer ou à diffuser?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou lu sur votre appareil pendant que vous enregistrez ou diffusez. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Écran entier"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Une seule appli"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Partager ou enregistrer une appli"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Lorsque vous partagez, enregistrez ou diffusez, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lorsque vous partagez, enregistrez ou diffusez une appli, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur cette appli. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Commencer"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partager ou enregistrer une appli"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partager votre écran avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partager une appli"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager l\'intégralité de l\'écran"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui s\'y affiche ou s\'y joue est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Commencer la diffusion?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Lorsque vous diffusez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lorsque vous diffusez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur cette appli. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Commencer la diffusion"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Diffuser votre écran?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Diffuser une appli"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Diffuser l\'intégralité de l\'écran"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous diffusez l\'intégralité de votre écran, tout ce qui s\'y trouve est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous diffusez une appli, tout ce qui s\'y affiche ou s\'y joue est visible. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Diffuser l\'écran"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choisir l\'appli à diffuser"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Commencer à partager?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Lorsque vous partagez, enregistrez ou diffusez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Lorsque vous partagez, enregistrez ou diffusez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur cette appli. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Commencer"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Suivant"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Le partage s\'interrompt lorsque vous changez d\'appli"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Partager plutôt cette appli"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Revenir en arrière"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite accessible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Appels d\'urgence ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur d\'Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -1266,7 +1279,7 @@
     <string name="clipboard_edit_text_description" msgid="805254383912962103">"Modifier le texte copié"</string>
     <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Modifier l\'image copiée"</string>
     <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Envoyer à un appareil à proximité"</string>
-    <string name="clipboard_text_hidden" msgid="7926899867471812305">"Touchez pour afficher"</string>
+    <string name="clipboard_text_hidden" msgid="7926899867471812305">"Touchez ceci pour afficher"</string>
     <string name="clipboard_text_copied" msgid="5100836834278976679">"Texte copié"</string>
     <string name="clipboard_image_copied" msgid="3793365360174328722">"Image copiée"</string>
     <string name="clipboard_content_copied" msgid="144452398567828145">"Contenu copié"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis-clavier"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste de retour"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste d\'accès à l\'écran d\'accueil"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Touche d\'action"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bon travail!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pavé tactile montrant trois doigts se déplaçant à droite et à gauche"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Écran de l\'appareil montrant l\'animation pour le geste de retour"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pour revenir en arrière, balayez vers la gauche ou la droite en utilisant trois doigts n\'importe où sur le pavé tactile.\n\nVous pouvez également utiliser le raccourci clavier Action+Échap."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Bon travail!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pour accéder à votre écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut avec trois doigts."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bien!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Vous avez appris le geste de retour à l\'écran d\'accueil."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Touche d\'action"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pour accéder à vos applis, appuyez sur la touche d\'action de votre clavier."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Félicitations!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Vous avez terminé le geste de la touche d\'action.\n\nAction + / affiche tous les raccourcis dont vous disposez."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Pour accéder à l\'écran d\'accueil, balayez l\'écran vers le haut avec trois doigts sur le pavé tactile"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pour afficher les applis récentes, balayez l\'écran vers le haut avec trois doigts sur le pavé tactile et maintenez-les en place"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pour afficher toutes vos applis, appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Supprimé"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Déverrouiller pour afficher"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Utiliser votre pavé tactile pour revenir en arrière"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Balayez vers la gauche ou vers la droite avec trois doigts. Touchez pour apprendre d\'autres gestes."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilisez votre pavé tactile pour accéder à l\'écran d\'accueil"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez l\'écran vers le haut avec trois doigts et maintenez-les en place. Touchez pour apprendre d\'autres gestes."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utiliser votre clavier pour afficher toutes les applis"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Touchez pour apprendre d\'autres gestes."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"La réduction supplémentaire de la luminosité fait désormais partie de la barre de luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Vous pouvez désormais rendre l\'écran encore plus sombre en réduisant davantage le niveau de luminosité à partir du haut de l\'écran.\n\nCela fonctionne mieux lorsque vous êtes dans un environnement sombre."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Retirer le raccourci de réduction supplémentaire de la luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Le raccourci de réduction supplémentaire de la luminosité à été retiré. Pour réduire la luminosité, utilisez la barre de luminosité habituelle."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index ad63e9e..9d3bbd7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applis ouvertes ont détecté cette capture d\'écran."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ajouter à la note"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inclure le lien"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Enregistreur d\'écran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Enregistrement de l\'écran…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement de l\'écran"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Commencer à enregistrer ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Lorsque vous enregistrez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Lorsque vous enregistrez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Lancer l\'enregistrement"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Enregistrer l\'écran ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Enregistrer tout l\'écran"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Lorsque vous enregistrez l\'intégralité de votre écran, tout ce qui s\'y affiche est enregistré. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos, et les contenus audio et vidéo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Lorsque vous enregistrez une appli, tout ce qui est affiché ou lu dans celle-ci est enregistré. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Enregistrer l\'écran"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Choisir l\'appli à enregistrer"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Enregistrer l\'audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio de l\'appareil"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Son provenant de l\'appareil (musique, appels, sonneries, etc.)"</string>
@@ -150,7 +154,7 @@
     <string name="issuerecord_title" msgid="286627115110121849">"Enregistreur de problèmes"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Enregistrement du problème"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Notification d\'activité en cours concernant la session d\'enregistrement d\'un problème"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problème d\'enregistrement"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Enregistrement du problème"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Partager"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"Problème enregistré"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Appuyez pour afficher"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Envoyer"</string>
     <string name="cancel" msgid="1089011503403416730">"Annuler"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo de l\'appli"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmer"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Réessayer"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Appuyer pour annuler l\'authentification"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Économiseur d\'écran"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne pas déranger"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modes prioritaires"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Aucun appareil associé disponible."</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Appuyez pour connecter ou déconnecter un appareil"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partage audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Appuyez pour activer ou partager l\'audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Enregistr. écran"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Démarrer"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Arrêter"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Enreg. le problème"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Lancer"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Sous-titres instantanés"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer la caméra de l\'appareil ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ouvrir les paramètres"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Autre appareil"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activer/Désactiver l\'écran Récents"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modes prioritaires"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"OK"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Paramètres"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurer"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Vous ne serez pas dérangé par des sons ou des vibrations, hormis ceux des alarmes. Vous entendrez encore les sons que vous choisirez de jouer, comme la musique, les vidéos et les jeux."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personnaliser"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Temps restant : <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets sur l\'écran de verrouillage"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran de verrouillage"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Balayer vers la gauche pour démarrer le tutoriel collectif"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personnaliser"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Fermer"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil pendant un enregistrement ou une diffusion de contenu. Il peut s\'agir de mots de passe, détails de mode de paiement, photos, messages ou encore contenus audio lus."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Commencer à enregistrer ou à caster ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Le service qui fournit cette fonction aura accès à toutes les infos visibles sur votre écran ou lues depuis votre appareil pendant un enregistrement ou une diffusion de contenu. Il peut s\'agir de mots de passe, détails de mode de paiement, photos, messages ou encore contenus audio lus."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tout l\'écran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Uniquement l\'appli"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Partager ou enregistrer une appli"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Commencer à enregistrer ou à caster avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Lorsque vous partagez, enregistrez ou castez, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lorsque vous partagez, enregistrez ou castez une appli, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Commencer"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partager ou enregistrer une appli"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partager votre écran avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partager une appli"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partager tout l\'écran"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Lorsque vous partagez tout votre écran, l\'ensemble de son contenu est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui est affiché ou lu dans celle-ci est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Commencer à caster ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Lorsque vous castez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lorsque vous castez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Commencer à caster"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Caster votre écran ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Caster une appli"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Caster tout l\'écran"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Lorsque vous castez tout votre écran, l\'ensemble de son contenu est visible. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Lorsque vous castez une appli, tout ce qui est affiché ou lu dans celle-ci est visible. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Caster l\'écran"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Choisir l\'appli à caster"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Commencer à partager ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Lorsque vous partagez, enregistrez ou castez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Lorsque vous partagez, enregistrez ou castez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Commencer"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Suivant"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Le partage est suspendu pendant le changement d\'application"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Partager cette application à la place"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Revenir"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Bonne connexion satellite"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Connexion satellite disponible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS par satellite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Appels d\'urgence ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil professionnel"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Divertissant pour certains, mais pas pour tous"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vous propose de nouvelles manières d\'adapter et de personnaliser l\'interface utilisateur Android. Ces fonctionnalités expérimentales peuvent être modifiées, cesser de fonctionner ou disparaître dans les versions futures. À utiliser avec prudence."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilité"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Raccourcis clavier"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Aucun résultat de recherche"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geste Retour"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Geste Accueil"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Touche d\'action"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"OK"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bravo !"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Retour"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pavé tactile sur lequel trois doigts glissent vers la droite, puis vers la gauche"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Écran d\'un appareil affichant l\'animation du geste Retour"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pour revenir en arrière, balayez vers la gauche ou vers la droite avec trois doigts n\'importe où sur le pavé tactile.\n\nVous pouvez aussi utiliser le raccourci clavier Action+Échap pour cela."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pour accéder à l\'écran d\'accueil à tout moment, balayez l\'écran du bas vers le haut avec trois doigts."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bravo !"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Vous avez appris le geste pour revenir à l\'écran d\'accueil."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Touche d\'action"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pour accéder à vos applis, appuyez sur la touche d\'action de votre clavier."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Félicitations !"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Vous avez appris le geste permettant d\'utiliser la touche d\'action.\n\nAction+/ affiche tous les raccourcis disponibles."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Pour revenir à l\'accueil, balayez vers le haut avec trois doigts sur le pavé tactile"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Pour afficher les applis récentes, balayez vers le haut avec trois doigts sur le pavé tactile et maintenez-les."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Pour afficher toutes vos applis, appuyez sur la touche d\'action de votre clavier"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Masqué"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Déverrouiller pour afficher"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Utilisez votre pavé tactile pour revenir en arrière"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Balayez vers la gauche ou la droite en utilisant trois doigts. Appuyez pour apprendre d\'autres gestes."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Utilisez votre pavé tactile pour revenir à l\'accueil"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Balayez vers le haut en utilisant trois doigts et maintenez. Appuyez pour apprendre d\'autres gestes."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Utilisez votre clavier pour afficher toutes les applis"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Appuyez sur la touche d\'action à tout moment. Appuyez pour apprendre d\'autres gestes."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Luminosité ultra-réduite fait désormais partie de la barre de luminosité"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Désormais, vous pouvez rendre l\'écran encore plus sombre en abaissant davantage le niveau de luminosité en haut de l\'écran.\n\nCela fonctionne mieux lorsque vous êtes dans un environnement sombre."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Supprimer le raccourci Luminosité ultra-réduite"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Raccourci Luminosité ultra-réduite supprimé. Pour diminuer la luminosité, utilisez la barre de luminosité habituelle."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 913e589..6281570 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outras aplicacións abertas detectaron esta captura de pantalla."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Engadir a unha nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluír ligazón"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Gravadora da pantalla"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Procesando gravación pantalla"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificación de actividade en curso sobre unha sesión de gravación de pantalla"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Queres iniciar a gravación?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Durante a gravación, Android ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Durante a gravación dunha aplicación, Android ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar gravación"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Queres gravar a túa pantalla?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar unha aplicación"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar pantalla completa"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Cando gravas a pantalla completa, recóllese todo o que se mostra nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Cando gravas unha aplicación, recóllese todo o que se mostra ou reproduce nela. Recomendámosche que teñas coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar pantalla"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Escoller unha aplicación para gravar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio do dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Son do dispositivo (por exemplo, música, chamadas e tons de chamada)"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotipo da aplicación"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar de novo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toca para cancelar a autenticación"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protector pantalla"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non molestar"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridade"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Non hai dispositivos vinculados dispoñibles"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toca para conectar ou desconectar un dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio compartido"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toca para cambiar ou compartir o audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Gravar pantalla"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Iniciar"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Deter"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Gravar problema"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de erros"</string>
@@ -388,7 +393,7 @@
     <string name="performance" msgid="6552785217174378320">"Rendemento"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string>
     <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
-    <string name="custom" msgid="3337456985275158299">"Personalizada"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configuración de rastrexo personalizada"</string>
     <string name="restore_default" msgid="5259420807486239755">"Restaurar configuración predeterminada"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtítulos instantáneos"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos instantáneos"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir Configuración"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Activar/desactivar Visión xeral"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Feito"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configuración"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,14 +482,13 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na pantalla de bloqueo"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Engadiuse o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> á pantalla de bloqueo"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Pasa o dedo cara á esquerda para iniciar o titorial comunitario"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Pechar"</string>
     <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Engadir, quitar e reordenar widgets neste espazo"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Engadir máis widgets"</string>
-    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Pulsación longa para personalizar os widgets"</string>
+    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Mantén premido para personalizar os widgets"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizar widgets"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"Desbloquea o dispositivo para personalizar os widgets"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Icona da aplicación de widget desactivado"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acceso a toda a información visible na pantalla ou reproducida desde o teu dispositivo mentres graves ou emitas contido. Isto inclúe datos como contrasinais, detalles de pago, fotos, mensaxes e o audio que reproduzas."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Queres iniciar a gravación ou a emisión?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O servizo que proporciona esta función terá acceso a toda a información visible na pantalla ou reproducida desde o teu dispositivo mentres graves ou emitas contido. Isto inclúe datos como contrasinais, detalles de pago, fotos, mensaxes e o audio que reproduzas."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Pantalla completa"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Unha soa aplicación"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Compartir ou gravar unha aplicación"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Queres comezar a gravar ou emitir contido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cando compartes, gravas ou emites contido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cando compartes, gravas ou emites unha aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartir ou gravar unha aplicación"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Queres compartir a pantalla con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartir unha aplicación"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartir toda a pantalla"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza nela. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza ou se reproduza nesa aplicación. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> desactivou esta opción"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Queres comezar a emitir contido?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cando emites contido, Android ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cando emites unha aplicación, Android ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Comezar emisión"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escoller unha aplicación para compartir"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Queres emitir a túa pantalla?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitir unha aplicación"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitir pantalla completa"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Cando emites a pantalla enteira, pódese ver todo o que haxa nela. Xa que logo, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Cando emites unha aplicación, pódese ver todo o que se mostre ou reproduza nela. Xa que logo, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitir pantalla"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Escoller unha aplicación para emitir"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Queres comezar a compartir contido?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Cando compartes, gravas ou emites contido, Android ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Cando compartes, gravas ou emites unha aplicación, Android ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Iniciar"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Seguinte"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Cando cambias de aplicación, ponse en pausa o uso compartido"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Compartir esta aplicación"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Volver"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa conexión"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexión dispoñible"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS por satélite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emerxencia ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de traballo"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversión só para algúns"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"O configurador da IU do sistema ofréceche formas adicionais de modificar e personalizar a interface de usuario de Android. Estas funcións experimentais poden cambiar, interromperse ou desaparecer en futuras versións. Continúa con precaución."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atallos de teclado"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Non hai resultados de busca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Xesto para volver"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Xesto para ir ao inicio"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de acción"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Moi ben!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Panel táctil que mostra tres dedos movéndose á dereita e á esquerda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Pantalla do dispositivo que mostra unha animación do xesto de retroceso"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para retroceder, pasa tres dedos cara á esquerda ou cara á dereita en calquera parte do panel táctil.\n\nTamén podes usar o atallo de teclado Acción + Escape."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Moi ben!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para ir á pantalla de inicio, pasa tres dedos cara arriba desde a parte inferior da pantalla."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Excelente!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Completaches o xesto de ir ao inicio."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de acción"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acceder ás aplicacións, preme a tecla de acción do teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Completaches o xesto da tecla de acción.\n\nAcción + / mostra todos os atallos que tes á túa disposición."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Para ir ao inicio, pasa tres dedos cara arriba no panel táctil"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver as aplicacións recentes, pasa tres dedos cara arriba no panel táctil e mantenos premidos"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas as aplicacións, preme a tecla de acción do teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Contido ocultado"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquea o dispositivo para ver o contido"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa o panel táctil para volver"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Pasa tres dedos cara á esquerda ou cara á dereita. Toca para obter máis información sobre os xestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa o panel táctil para volver ao inicio"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasa tres dedos cara arriba e mantenos premidos. Toca para obter máis información sobre os xestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa o teclado para ver todas as aplicacións"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Preme a tecla de acción cando queiras. Toca para obter máis información sobre os xestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"A atenuación extra agora está incluída na barra de brillo"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Agora podes aumentar a atenuación da pantalla: só tes que baixar o nivel de brillo aínda máis desde a parte superior.\n\nEsta opción funciona mellor se estás nun ambiente escuro."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Quitar atallo de atenuación extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Quitouse o atallo de atenuación extra. Para reducir o brillo, usa a barra de brillo normal."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 8fd8b3e..2be88f2 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> અને કામ કરતી અન્ય ઍપ દ્વારા આ સ્ક્રીનશૉટ લેવાયાની ભાળ મેળવવામાં આવી."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"નોંધમાં ઉમેરો"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"લિંક શામેલ કરો"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"સ્ક્રીન રેકોર્ડર"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"સ્ક્રીન રેકૉર્ડિંગ ચાલુ છે"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"રેકોર્ડિંગ શરૂ કરીએ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"જ્યારે તમે રેકોર્ડ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી હોય કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી કોઈપણ વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"જ્યારે તમે કોઈ ઍપ રેકોર્ડ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"રેકોર્ડિંગ શરૂ કરો"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"તમારી સ્ક્રીન રેકોર્ડ કરીએ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"એક ઍપ રેકોર્ડ કરો"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"પૂર્ણ સ્ક્રીન રેકોર્ડ કરો"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"જ્યારે તમે તમારી પૂર્ણ સ્ક્રીન રેકોર્ડ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર બતાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"જ્યારે તમે કોઈ ઍપને રેકોર્ડ કરી રહ્યાં હો, ત્યારે એ ઍપમાં બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ રેકોર્ડ કરવામાં આવે છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"સ્ક્રીન રેકોર્ડ કરો"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"રેકોર્ડ કરવા માટે ઍપ પસંદ કરો"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ઑડિયો રેકોર્ડ કરો"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ડિવાઇસનો ઑડિયો"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"મ્યુઝિક, કૉલ અને રિંગટોન જેવા તમારા ડિવાઇસના સાઉન્ડ"</string>
@@ -138,7 +142,7 @@
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"તમે હાલમાં ઍપ શેર કરી રહ્યાં છો"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"શેર કરવાનું રોકો"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"સ્ક્રીન કાસ્ટ કરી રહ્યાં છીએ"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"શું કાસ્ટ કરવાનું બંધ કરીએ?"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"કાસ્ટ કરવાનું રોકીએ?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"તમે હાલમાં તમારી પૂર્ણ સ્ક્રીન <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર કાસ્ટ કરી રહ્યાં છો"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"તમે હાલમાં તમારી પૂર્ણ સ્ક્રીન નજીકના ડિવાઇસ પર કાસ્ટ કરી રહ્યાં છો"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"તમે હાલમાં <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>ને <xliff:g id="DEVICE_NAME">%2$s</xliff:g> પર કાસ્ટ કરી રહ્યાં છો"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"મોકલો"</string>
     <string name="cancel" msgid="1089011503403416730">"રદ કરો"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ઍપનો લોગો"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"કન્ફર્મ કરો"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ફરી પ્રયાસ કરો"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"પ્રમાણીકરણ રદ કરવા માટે ટૅપ કરો"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"સ્ક્રીન સેવર"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ઇથરનેટ"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ખલેલ પાડશો નહીં"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"પ્રાધાન્યતાના મોડ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"બ્લૂટૂથ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"કોઈ જોડી કરેલ ઉપકરણો ઉપલબ્ધ નથી"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"કોઈ ડિવાઇસ કનેક્ટ કરવા કે ડિસ્કનેક્ટ કરવા માટે ટૅપ કરો"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ઑડિયો શેરિંગ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ઑડિયો પર સ્વિચ કરવા કે તેને શેર કરવા માટે બે વાર ટૅપ કરો"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"સાચવેલું"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ડિસ્કનેક્ટ કરો"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"સક્રિય કરો"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string>
     <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_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>
@@ -375,7 +380,7 @@
     <string name="quick_settings_nfc_label" msgid="1054317416221168085">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"NFC અક્ષમ કરેલ છે"</string>
     <string name="quick_settings_nfc_on" msgid="1004976611203202230">"NFC સક્ષમ કરેલ છે"</string>
-    <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકૉર્ડ"</string>
+    <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"સ્ક્રીન રેકોર્ડ"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"શરૂ કરો"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"રોકો"</string>
     <string name="qs_record_issue_label" msgid="8166290137285529059">"રેકોર્ડિંગમાં સમસ્યા"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"લાઇવ કૅપ્શન"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"લાઇવ કૅપ્શન"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"સેટિંગ ખોલો"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"અન્ય ડિવાઇસ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ઝલકને ટૉગલ કરો"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"પ્રાધાન્યતાના મોડ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"થઈ ગયું"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"સેટિંગ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"સેટઅપ કરો"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"અલાર્મ સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"કસ્ટમાઇઝ કરો"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ધીમેથી ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં ચાર્જ થઈ જશે"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ચાર્જ થઈ રહ્યું છે • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>માં પૂરું ચાર્જ થઈ જશે"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"લૉક સ્ક્રીન પર વિજેટ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"લૉક સ્ક્રીન પર <xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ ઉમેરવામાં આવ્યું"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"કૉમ્યુનલ ટ્યૂટૉરિઅલ શરૂ કરવા માટે ડાબે સ્વાઇપ કરો"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"કસ્ટમાઇઝ કરો"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"છોડી દો"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"આ સ્પેસમાં તમારા વિજેટ ઉમેરો, તેને કાઢી નાખો અને ફરી તેને ક્રમમાં ગોઠવો"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"આ સ્પેસમાં તમારા વિજેટ ઉમેરો, કાઢી નાખો અને ફરીથી ક્રમમાં ગોઠવો"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"વધુ વિજેટ ઉમેરો"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"વિજેટ કસ્ટમાઇઝ કરવા માટે થોડીવાર દબાવી રાખો"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"વિજેટ કસ્ટમાઇઝ કરો"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"રેકોર્ડ અથવા કાસ્ટ કરતી વખતે, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. આમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, મેસેજ અને તમે વગાડો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"શું રેકોર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"રેકોર્ડ અથવા કાસ્ટ કરતી વખતે, આ સુવિધા આપતી સેવાને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. જેમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, મેસેજ અને તમે વગાડો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"પૂર્ણ સ્ક્રીન"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"કોઈ એક ઍપ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"કોઈ ઍપ શેર કરો અથવા રેકોર્ડ કરો"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> વડે રેકોર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"જ્યારે તમે શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી હોય કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"જ્યારે તમે કોઈ ઍપ શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"શરૂ કરો"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"કોઈ ઍપ શેર કરો અથવા રેકોર્ડ કરો"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> સાથે તમારી સ્ક્રીન શેર કરીએ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"કોઈ ઍપ શેર કરો"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"સંપૂર્ણ સ્ક્રીન શેર કરો"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"જ્યારે તમે તમારી સંપૂર્ણ સ્ક્રીનને શેર કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પરની કોઈપણ વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"જ્યારે તમે કોઈ ઍપને શેર કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"સ્ક્રીન શેર કરો"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા આ વિકલ્પ બંધ કરવામાં આવ્યો છે"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"જ્યારે તમે કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી બધી વસ્તુઓનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"જ્યારે તમે ઍપને કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"કાસ્ટ કરવાનું શરૂ કરો"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"શેર કરવા માટે ઍપ પસંદ કરો"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"તમારી સ્ક્રીનને કાસ્ટ કરીએ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"એક ઍપને કાસ્ટ કરો"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"સંપૂર્ણ સ્ક્રીનને કાસ્ટ કરો"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"જ્યારે તમે તમારી સંપૂર્ણ સ્ક્રીનને કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પરની કોઈપણ વસ્તુ દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"જ્યારે તમે ઍપને કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"સ્ક્રીનને કાસ્ટ કરો"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"કાસ્ટ કરવા માટે ઍપ પસંદ કરો"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"શેર કરવાનું શરૂ કરીએ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"જ્યારે તમે શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી હોય કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"જ્યારે તમે કોઈ ઍપ શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"શરૂ કરો"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"આગળ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"તમે ઍપ સ્વિચ કરો ત્યારે શેરિંગ થોભી જાય છે"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"તેના બદલે આ ઍપ શેર કરો"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"પાછળ સ્વિચ કરો"</string>
@@ -594,13 +606,13 @@
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA પ્રમાણપત્રો"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"પૉલિસીઓ જુઓ"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"નિયંત્રણો જુઓ"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"આ ડિવાઇસ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ની માલિકીનું છે.\n\nતમારા IT ઍડમિન સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની લોકેશનની માહિતીને મૉનિટર તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT ઍડમિનનો સંપર્ક કરો."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> આ ડિવાઇસ સાથે સંકળાયેલો ડેટા ઍક્સેસ કરી શકશે અને ઍપ મેનેજ કરી શકશે તેમજ આ ડિવાઇસના સેટિંગ બદલી શકશે.\n\nજો તમને કોઈ પ્રશ્ન હોય, તો <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>નો સંપર્ક કરો."</string>
     <string name="monitoring_description_management" msgid="4308879039175729014">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે.\n\nતમારા IT વ્યવસ્થાપક સેટિંગ, કૉર્પોરેટ ઍક્સેસ, ઍપ, તમારા ડિવાઇસ સાથે સંકળાયેલો ડેટા અને તમારા ડિવાઇસની સ્થાન માહિતીનું નિરીક્ષણ તેમજ તેને મેનેજ કરી શકે છે.\n\nવધુ માહિતી માટે, તમારા IT વ્યવસ્થાપકનો સંપર્ક કરો."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"તમારી સંસ્થાએ આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"તમારી સંસ્થાએ તમારી કાર્ય પ્રોફાઇલમાં પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કર્યું છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"આ ઉપકરણ પર પ્રમાણપત્ર સત્તાધિકારી ઇન્સ્ટૉલ કરેલ છે. તમારા સુરક્ષિત નેટવર્ક ટ્રાફિકનું નિયમન થઈ શકે છે અથવા તેમાં ફેરફાર કરવામાં આવી શકે છે."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગિંગ ચાલુ કર્યું છે, જે તમારા ઉપકરણ પર નેટવર્ક ટ્રાફિકનું નિયમન કરે છે."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"તમારા ઍડમિને નેટવર્ક લૉગિંગ ચાલુ કર્યું છે, જે તમારા ડિવાઇસ પર નેટવર્ક ટ્રાફિક મૉનિટર કરે છે."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"તમારા વ્યવસ્થાપકે નેટવર્ક લૉગ ઇન ચાલુ કર્યું છે, જે તમારી વ્યક્તિગત પ્રોફાઇલમાં નહીં, પરંતુ ઑફિસની પ્રોફાઇલમાં ટ્રાફિકનું નિરીક્ષણ કરે છે."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"આ ડિવાઇસ <xliff:g id="VPN_APP">%1$s</xliff:g> મારફતે ઇન્ટરનેટ સાથે કનેક્ટેડ છે. ઇમેઇલ અને બ્રાઉઝિંગ ડેટા સહિતની તમારી નેટવર્ક પ્રવૃત્તિને VPN પ્રદાતા જોઈ શકે છે."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"આ ડિવાઇસ <xliff:g id="VPN_APP">%1$s</xliff:g> મારફતે ઇન્ટરનેટ સાથે કનેક્ટેડ છે. ઇમેઇલ અને બ્રાઉઝિંગ ડેટા સહિતની તમારી નેટવર્ક પ્રવૃત્તિ, તમારા IT ઍડમિન જોઈ શકે છે."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"સૅટલાઇટ, સારું કનેક્શન"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"સૅટલાઇટ, કનેક્શન ઉપલબ્ધ છે"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ઇમર્જન્સી સૅટલાઇટ સહાય"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ઇમર્જન્સી કૉલ અથવા SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ઑફિસની પ્રોફાઇલ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"કેટલાક માટે મજા પરંતુ બધા માટે નહીં"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"સિસ્ટમ UI ટ્યૂનર તમને Android વપરાશકર્તા ઇન્ટરફેસને ટ્વીક અને કસ્ટમાઇઝ કરવાની વધારાની રીતો આપે છે. ભાવિ રીલિઝેસમાં આ પ્રાયોગિક સુવિધાઓ બદલાઈ, ભંગ અથવા અદૃશ્ય થઈ શકે છે. સાવધાની સાથે આગળ વધો."</string>
@@ -1216,7 +1229,7 @@
     <string name="people_tile_description" msgid="8154966188085545556">"તાજેતરના મેસેજ, ચૂકી ગયેલા કૉલ અને સ્ટેટસ અપડેટ જુઓ"</string>
     <string name="people_tile_title" msgid="6589377493334871272">"વાતચીત"</string>
     <string name="paused_by_dnd" msgid="7856941866433556428">"\'ખલેલ પાડશો નહીં\'ની સુવિધા દ્વારા થોભાવેલું"</string>
-    <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ સંદેશ મોકલવામાં આવ્યો: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
+    <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ મેસેજ મોકલવામાં આવ્યો: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
     <string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ છબી મોકલવામાં આવી"</string>
     <string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા નવી સ્ટેટસ અપડેટ પોસ્ટ કરવામાં આવી: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
     <string name="person_available" msgid="2318599327472755472">"ઉપલબ્ધ છે"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ઍક્સેસિબિલિટી"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"કીબોર્ડ શૉર્ટકટ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"કોઈ શોધ પરિણામો નથી"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"પાછળ જવાનો સંકેત"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"હોમ સ્ક્રીન પર જવાનો સંકેત"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ઍક્શન કી"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ખૂબ સરસ કામ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"જમણી અને ડાબી તરફ ખસી રહેલી ત્રણ આંગળીઓ બતાવતું ટચપૅડ"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"પાછા જવા માટેના સંકેત માટેનું ઍનિમેશન બતાવતી ડિવાઇસ સ્ક્રીન"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"પાછા જવા માટે, ટચપૅડ પર ગમે ત્યાં ત્રણ આંગળી વડે ડાબે અથવા જમણે સ્વાઇપ કરો.\n\nઆના માટે તમે કીબોર્ડ શૉર્ટકટ Action + ESCનો ઉપયોગ કરી શકો છો."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ખૂબ સરસ કામ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"હોમ પર જાઓ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"કોઈપણ સમયે તમારી હોમ સ્ક્રીન પર જવા માટે, ત્રણ આંગળી વડે તમારી સ્ક્રીનની સૌથી નીચેની બાજુએથી ઉપરની તરફ સ્વાઇપ કરો."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"સરસ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો છે."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ઍક્શન કી"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"તમારી ઍપ ઍક્સેસ કરવા માટે, તમારા કીબોર્ડ પરની ઍક્શન કી દબાવો."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"અભિનંદન!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"તમે ઍક્શન કીનો સંકેત પૂર્ણ કર્યો છે.\n\nઍક્શન કી + /ને દબાવવાથી, તમારી પાસે ઉપલબ્ધ હોય તે બધા શૉર્ટકટ જોવા મળે છે."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"હોમ સ્ક્રીન પર જવા માટે, ટચપૅડ પર ત્રણ આંગળીઓ વડે ઉપર સ્વાઇપ કરો"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"તાજેતરની ઍપ જોવા માટે, ટચપૅડ પર ત્રણ આંગળીઓ વડે ઉપર સ્વાઇપ કરો અને દબાવી રાખો"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"તમારી બધી ઍપ જોવા માટે, તમારા કીબોર્ડ પર ઍક્શન કી દબાવો"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"બદલાવેલું"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"જોવા માટે અનલૉક કરો"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"પાછા જવા માટે તમારા ટચપૅડનો ઉપયોગ કરો"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ત્રણ આંગળીનો ઉપયોગ કરીને ડાબે અથવા જમણે સ્વાઇપ કરો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"હોમ સ્ક્રીન પર જવા માટે તમારા ટચપૅડનો ઉપયોગ કરો"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"બધી ઍપ જોવા માટે તમારા કીબોર્ડનો ઉપયોગ કરો"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"કોઈપણ સમયે ઍક્શન કી દબાવો. સંકેતો વિશે વધુ જાણવા માટે ટૅપ કરો."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"બ્રાઇટનેસ બાર હવે એક્સ્ટ્રા ડિમનો ભાગ છે"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"તમે હવે તમારી સ્ક્રીનના સૌથી ઉપરના ભાગમાંથી બ્રાઇટનેસ લેવલને હજી પણ ઘટાડીને સ્ક્રીનને એક્સ્ટ્રા ડિમ બનાવી શકો છો.\n\nતમે ડાર્ક વાતાવરણમાં હો, ત્યારે આ શ્રેષ્ઠ રીતે કામ કરે છે."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"એક્સ્ટ્રા ડિમ શૉર્ટકટ કાઢી નાખો"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"એક્સ્ટ્રા ડિમ શૉર્ટકટ કાઢી નાખ્યો. તમારી બ્રાઇટનેસ ઘટાડવા માટે, નિયમિત બ્રાઇટનેસ બારનો ઉપયોગ કરો."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 6d96376..7c267bf 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> और खुले हुए अन्य ऐप्लिकेशन को इस स्क्रीनशॉट का पता चला है."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"नोट में जोड़ें"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"लिंक जोड़ें"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"स्क्रीन रिकॉर्डर"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रिकॉर्डिंग को प्रोसेस किया जा रहा है"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"क्या रिकॉर्डिंग शुरू करनी है?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"रिकॉर्ड करते समय, Android के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"किसी ऐप्लिकेशन को रिकॉर्ड करते समय, Android के पास उस ऐप्लिकेशन पर दिख रहे कॉन्टेंट या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"रिकॉर्ड करें"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन रिकॉर्ड करनी है?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन की रिकॉर्डिंग करें"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरी स्क्रीन रिकॉर्ड करें"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"पूरी स्क्रीन रिकॉर्ड करते समय, स्क्रीन पर दिखने वाली हर चीज़ रिकॉर्ड की जाती है. इसलिए पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज,  डिवाइस पर चल रहे ऑडियो और वीडियो, और फ़ोटो जैसी चीज़ों को लेकर सावधानी बरतें."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"किसी ऐप्लिकेशन को रिकॉर्ड करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी रिकॉर्ड होता है. इसलिए, रिकॉर्ड करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रिकॉर्ड करें"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रिकॉर्ड करने के लिए ऐप्लिकेशन चुनें"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ऑडियो रिकॉर्ड करें"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"डिवाइस ऑडियो"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"आपके डिवाइस से आने वाली आवाज़ जैसे कि संगीत, कॉल, और रिंगटोन"</string>
@@ -126,19 +130,19 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"देखने के लिए टैप करें"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"स्क्रीन रिकॉर्डिंग सेव करते समय गड़बड़ी हुई"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन को रिकॉर्ड करने में गड़बड़ी आ रही है"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"क्या रिकॉर्डिंग बंद करनी है?"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"रिकॉर्ड करना बंद करना है?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"फ़िलहाल, पूरी स्क्रीन रिकॉर्ड की जा रही है"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"फ़िलहाल, <xliff:g id="APP_NAME">%1$s</xliff:g> की रिकॉर्डिंग की जा रही है"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"रिकॉर्ड करना बंद करें"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"स्क्रीन शेयर की जा रही है"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"क्या स्क्रीन शेयरिंग बंद करनी है?"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"स्क्रीन शेयर करना बंद करना है?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"फ़िलहाल, <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> पर पूरी स्क्रीन शेयर की जा रही है"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"फ़िलहाल, किसी ऐप्लिकेशन पर पूरी स्क्रीन शेयर की जा रही है"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> शेयर किया जा रहा है"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"फ़िलहाल, कोई ऐप्लिकेशन शेयर किया जा रहा है"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"शेयर करना बंद करें"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"स्क्रीन कास्ट की जा रही है"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"क्या कास्टिंग बंद करनी है?"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"कास्ट करना बंद करना है?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"फ़िलहाल, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर पूरी स्क्रीन कास्ट की जा रही है"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"फ़िलहाल, आस-पास मौजूद किसी डिवाइस पर पूरी स्क्रीन कास्ट की जा रही है"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"फ़िलहाल, <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> पर कास्ट किया जा रहा है"</string>
@@ -147,10 +151,10 @@
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"फ़िलहाल, आस-पास मौजूद किसी डिवाइस पर कास्ट किया जा रहा है"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"कास्ट करना बंद करें"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"बंद करें"</string>
-    <string name="issuerecord_title" msgid="286627115110121849">"समस्या का डेटा सेव करने वाला टूल"</string>
+    <string name="issuerecord_title" msgid="286627115110121849">"समस्या रिकॉर्ड करने वाला टूल"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"समस्या का डेटा प्रोसेस हो रहा"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"समस्या का डेटा इकट्ठा करने के लिए बैकग्राउंड में जारी गतिविधि की सूचना"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या का डेटा इकट्ठा किया जा रहा है"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"समस्या रिकॉर्ड की जा रही है"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"शेयर करें"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"समस्या का डेटा सेव किया गया"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"देखने के लिए टैप करें"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"भेजें"</string>
     <string name="cancel" msgid="1089011503403416730">"रद्द करें"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ऐप्लिकेशन का लोगो"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"पुष्टि करें"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"फिर से कोशिश करें"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"पुष्टि की प्रक्रिया रद्द करने के लिए टैप करें"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रीन सेवर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ईथरनेट"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"परेशान न करें"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"अहम मोड"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लूटूथ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"कोई भी युग्मित डिवाइस उपलब्ध नहीं"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"किसी डिवाइस को कनेक्ट या डिसकनेक्ट करने के लिए टैप करें"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ऑडियो शेयर करने की सुविधा"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ऑडियो को स्विच या शेयर करने के लिए टैप करें"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव किया गया"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिसकनेक्ट करें"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"चालू करें"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string>
     <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_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>
@@ -389,7 +394,7 @@
     <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string>
     <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
     <string name="custom" msgid="3337456985275158299">"कस्टम"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"कस्टम ट्रेस सेटिंग"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ट्रेस करने से जुड़ी कस्टम सेटिंग"</string>
     <string name="restore_default" msgid="5259420807486239755">"डिफ़ॉल्ट सेटिंग वापस लाएं"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"लाइव कैप्शन"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव कैप्शन"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको माइक्रोफ़ोन का ऐक्सेस अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको कैमरे का ऐक्सेस अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग खोलें"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"अन्य डिवाइस"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"खास जानकारी टॉगल करें"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"अहम मोड"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"हो गया"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"सेट अप करें"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"आपको अलार्म छोड़कर दूसरी आवाज़ों और कंपनों से परेशान नहीं किया जाएगा. आपको अब भी संगीत, वीडियो और गेम सहित वह सब कुछ सुनाई देगा जो आपने चलाने के लिए चुना है."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"अपनी पसंद के मुताबिक बनाएं"</string>
@@ -478,15 +482,14 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लॉक स्क्रीन पर विजेट"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट, लॉक स्क्रीन में जोड़े गए"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्यूनिटी ट्यूटोरियल शुरू करने के लिए, बाईं ओर स्वाइप करें"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"पसंद के मुताबिक बनाएं"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"खारिज करें"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"इस स्पेस में विजेट जोड़ें, हटाएं, और उन्हें फिर से क्रम में लगाएं"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"इस स्पेस में विजेट जोड़ें, हटाएं, और उन्हें नए क्रम में लगाएं"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"ज़्यादा विजेट जोड़ें"</string>
-    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट पसंद के मुताबिक बनाने के लिए उसे दबाकर रखें"</string>
-    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट अपनी पसंद के मुताबिक बनाएं"</string>
+    <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"विजेट को मनमुताबिक बनाने के लिए उसे दबाकर रखें"</string>
+    <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"विजेट को अपनी पसंद के मुताबिक बनाएं"</string>
     <string name="unlock_reason_to_customize_widgets" msgid="5011909432460546033">"विजेट को पसंद के मुताबिक बनाने के लिए, डिवाइस अनलॉक करें"</string>
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"बंद किए गए विजेट के लिए ऐप्लिकेशन आइकॉन"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"इंस्टॉल हो रहे विजेट के लिए ऐप्लिकेशन आइकॉन"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"टैबलेट लॉक होने के बावजूद, कोई भी व्यक्ति इसकी लॉक स्क्रीन पर विजेट देख सकता है."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"विजेट से चुने हुए का निशान हटाएं"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"लॉक स्क्रीन विजेट"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि टैबलेट के लॉक होने पर भी कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"किसी विजेट से कोई ऐप्लिकेशन खोलने के लिए, आपको अपनी पहचान की पुष्टि करनी होगी. ध्यान रखें कि आपके टैबलेट के लॉक होने पर भी, कोई व्यक्ति विजेट देख सकता है. ऐसा हो सकता है कि कुछ विजेट, लॉक स्क्रीन पर दिखाने के लिए न बने हों. इन्हें लॉक स्क्रीन पर जोड़ना असुरक्षित हो सकता है."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ठीक है"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"पुलडाउन मेन्यू"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास आपकी स्क्रीन पर दिख रही जानकारी या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. जैसे, पासवर्ड, पेमेंट के तरीके की जानकारी, फ़ोटो, मैसेज, और डिवाइस पर चल रहा ऑडियो."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"क्या मीडिया रिकॉर्ड या कास्ट करना है?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"रिकॉर्ड या कास्ट करते समय, इस सुविधा को उपलब्ध कराने वाली सेवा के पास आपकी स्क्रीन पर दिख रही जानकारी या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. जैसे, पासवर्ड, पेमेंट के तरीके की जानकारी, फ़ोटो, मैसेज, और डिवाइस पर चल रहा ऑडियो."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"पूरी स्क्रीन"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"सिर्फ़ एक ऐप्लिकेशन"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ऐप्लिकेशन शेयर करें या उसकी रिकॉर्डिंग करें"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"क्या <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> का इस्तेमाल करके रिकॉर्ड या कास्ट करना है?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"किसी ऐप्लिकेशन को शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास उस ऐप्लिकेशन पर दिख रहे कॉन्टेंट या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"शुरू करें"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ऐप्लिकेशन शेयर करें या उसकी रिकॉर्डिंग करें"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"क्या आपको <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर अपनी स्क्रीन शेयर करनी है?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एक ऐप्लिकेशन शेयर करें"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरी स्क्रीन शेयर करें"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"जब पूरी स्क्रीन शेयर की जाती है, तो आपकी स्क्रीन पर दिख रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"जब कोई ऐप्लिकेशन शेयर किया जाता है, तो उस ऐप्लिकेशन में दिख रहा या चलाया जा रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेयर करें"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ने इस विकल्प को बंद कर दिया है"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"क्या कास्टिंग शुरू करनी है?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"कास्ट करते समय, Android के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"जब किसी ऐप्लिकेशन को कास्ट किया जाता है, तब उस पर दिख रहे कॉन्टेंट या चल रहे हर मीडिया का ऐक्सेस Android के पास होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर आपको सावधानी बरतनी चाहिए."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"कास्ट करें"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेयर करने के लिए ऐप्लिकेशन चुनें"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"क्या स्क्रीन को कास्ट करना है?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक ऐप्लिकेशन को कास्ट करें"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"पूरी स्क्रीन को कास्ट करें"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"पूरी स्क्रीन को कास्ट करने के दौरान, उस पर दिख रहा कॉन्टेंट दूसरी स्क्रीन पर भी दिखता है. इसलिए, कास्ट करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"किसी ऐप्लिकेशन को कास्ट करने के दौरान, उस पर दिख रहा कॉन्टेंट या चल रहा मीडिया दूसरी स्क्रीन पर भी दिखता है. इसलिए, कास्ट करते समय पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रीन कास्ट करें"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट करने के लिए ऐप्लिकेशन चुनें"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"क्या मीडिया शेयर करना है?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"शेयर, रिकॉर्ड या कास्ट करते समय, Android के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"किसी ऐप्लिकेशन को शेयर, रिकॉर्ड या कास्ट करते समय, Android के पास उस ऐप्लिकेशन पर दिख रहे कॉन्टेंट या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"शुरू करें"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"आगे बढ़ें"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ऐप्लिकेशन स्विच करते समय शेयर करने की प्रोसेस रुक जाती है"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"इसके बजाय, यह ऐप्लिकेशन शेयर करें"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"वापस जाएं"</string>
@@ -624,8 +636,8 @@
     <string name="sound_settings" msgid="8874581353127418308">"आवाज़ और वाइब्रेशन"</string>
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"सेटिंग"</string>
     <string name="volume_panel_captioning_title" msgid="5984936949147684357">"लाइव कैप्शन"</string>
-    <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट कर दिया गया है"</string>
-    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय से देर तक ज़्यादा रही"</string>
+    <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"आवाज़ को कम करके, सुरक्षित लेवल पर सेट किया गया"</string>
+    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"हेडफ़ोन की आवाज़ सुझाए गए समय के बाद भी ज़्यादा रही"</string>
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"इस हफ़्ते के लिए हेडफ़ोन की आवाज़, सुझाई गई सीमा से ज़्यादा हो गई है"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"सुनना जारी रखें"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"आवाज़ कम करें"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सैटलाइट कनेक्शन अच्छा है"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सैटलाइट कनेक्शन उपलब्ध है"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"सैटलाइट एसओएस"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आपातकालीन कॉल या एसओएस"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"वर्क प्रोफ़ाइल"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"कुछ के लिए मज़ेदार लेकिन सबके लिए नहीं"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम यूज़र इंटरफ़ेस (यूआई) ट्यूनर, आपको Android यूज़र इंटरफ़ेस में सुधार लाने और उसे अपनी पसंद के हिसाब से बदलने के कुछ और तरीके देता है. प्रयोग के तौर पर इस्तेमाल हो रहीं ये सुविधाएं आगे चल कर रिलीज़ की जा सकती हैं, रोकी जा सकती हैं या दिखाई देना बंद हो सकती हैं. सावधानी से आगे बढ़ें."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सुलभता"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"खोज का कोई नतीजा नहीं मिला"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"पीछे जाने का जेस्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम स्क्रीन पर जाने का जेस्चर"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ऐक्शन बटन"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"बहुत बढ़िया!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"टचपैड पर तीन उंगलियों को दाईं और बाईं तरफ़ ले जाया जा रहा है"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"डिवाइस की स्क्रीन पर, पिछले पेज पर जाने के लिए हाथ के जेस्चर का ऐनिमेशन दिख रहा है"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"वापस जाने के लिए, टचपैड पर कहीं भी तीन उंगलियों से दाईं या बाईं ओर स्वाइप करें.\n\nइसके अलावा, ऐसा करने के लिए Action + ESC बटन का भी इस्तेमाल किया जा सकता है."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"बहुत बढ़िया!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"अब आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके, पिछली स्क्रीन पर वापस कैसे जाएं."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होम स्क्रीन पर जाएं"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"किसी भी समय फ़ोन की होम स्क्रीन पर जाने के लिए, तीन उंगलियों से फ़ोन पर सबसे नीचे से ऊपर की ओर स्वाइप करें."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"बढ़िया!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"आपने जान लिया कि हाथ का जेस्चर इस्तेमाल करके, होम स्क्रीन पर कैसे जाएं."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ऐक्शन बटन"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"अपने ऐप्लिकेशन ऐक्सेस करने के लिए, कीबोर्ड पर ऐक्शन बटन दबाएं."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"बधाई हो!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"आपने ऐक्शन बटन का जेस्चर इस्तेमाल करने की प्रोसेस पूरी कर ली है.\n\nऐक्शन बटन और / को साथ में दबाने पर, आपके पास उपलब्ध सभी शॉर्टकट दिखते हैं."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"होम पर जाने के लिए, अपने डिवाइस के टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"हाल ही में इस्तेमाल हुए ऐप देखने के लिए, टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करके दबाकर रखें"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड पर ऐक्शन बटन दबाएं"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"जानकारी छिपाने के लिए सूचना में बदलाव किया गया"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"देखने के लिए डिवाइस अनलॉक करें"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"वापस जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन उंगलियों से बाईं या दाईं ओर स्वाइप करें. जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"होम पर जाने के लिए, अपने डिवाइस के टचपैड का इस्तेमाल करें"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन उंगलियों से ऊपर की ओर स्वाइप करें और दबाकर रखें. जेस्चर की ज़्यादा जानकारी पाने के लिए टैप करें."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सभी ऐप्लिकेशन देखने के लिए, कीबोर्ड का इस्तेमाल करें"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"किसी भी समय ऐक्शन बटन दबाएं. हाथ के जेस्चर के बारे में ज़्यादा जानने के लिए टैप करें."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा, अब ब्राइटनेस बार का हिस्सा है"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"अब स्क्रीन के सबसे ऊपरी हिस्से से, स्क्रीन की रोशनी सामान्य लेवल से और कम की जा सकती है.\n\nयह सुविधा, अंधेरे वाली जगह पर बेहतर तरीके से काम करती है."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा का शॉर्टकट हटाएं"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा का शॉर्टकट हटा दिया गया. स्क्रीन की रोशनी कम करने के लिए, ब्राइटनेस बार का इस्तेमाल करें."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 1d5051c..bdf6e13 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> i druge otvorene aplikacije otkrile su ovu snimku zaslona."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj bilješci"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Uključi vezu"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Snimač zaslona"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obrada snimanja zaslona"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Želite li pokrenuti snimanje?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Dok snimate, Android ima pristup svemu što je vidljivo na zaslonu ili se reproducira na uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Dok snimate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Pokreni snimanje"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite li snimati zaslon?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snimanje jedne aplikacije"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snimanje cijelog zaslona"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kad snimate cijeli zaslon, snima se sve što se prikazuje na zaslonu. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kad snimate aplikaciju, snima se sve što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimanje zaslona"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Odaberite aplikaciju za snimanje"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snimanje zvuka"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk na uređaju"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk s vašeg uređaja, poput glazbe, poziva i melodija zvona"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošalji"</string>
     <string name="cancel" msgid="1089011503403416730">"Odustani"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip aplikacije"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Pokušaj ponovo"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Dodirnite da biste otkazali autentifikaciju"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Čuvar zaslona"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne uznemiravaj"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetni načini"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Upareni uređaji nisu dostupni"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dodirnite da biste povezali uređaj ili prekinuli vezu s njim"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Zajedničko slušanje"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dodirnite za prebacivanje ili dijeljenje zvuka"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string>
     <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_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>
@@ -378,16 +383,16 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Snimanje zaslona"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Početak"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zaustavi"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Zabilježite poteškoću"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite poteškoću"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokreni"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavi"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o programskim pogreškama"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji je dio doživljaja na uređaju to utjecalo?"</string>
-    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu poteškoće"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string>
     <string name="performance" msgid="6552785217174378320">"Izvedba"</string>
     <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string>
-    <string name="thermal" msgid="6758074791325414831">"Termalno"</string>
+    <string name="thermal" msgid="6758074791325414831">"Pregrijavanje"</string>
     <string name="custom" msgid="3337456985275158299">"Prilagođeno"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Postavke prilagođenog praćenja"</string>
     <string name="restore_default" msgid="5259420807486239755">"Vrati na zadano"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Automatski titlovi"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvori postavke"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ostali uređaji"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Uključivanje/isključivanje pregleda"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetni načini"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotovo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Postavke"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Postavi"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Neće vas ometati zvukovi i vibracije, osim alarma. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgeti na zaključanom zaslonu"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> dodan je na zaključani zaslon"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Prijeđite prstom ulijevo da biste pokrenuli zajednički vodič"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodi"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Odbaci"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Želite li pokrenuti snimanje ili emitiranje?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Usluga koja pruža ovu funkciju imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Cijeli zaslon"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Jedna aplikacija"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Dijeljenje ili snimanje aplikacije"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Želite li započeti snimanje ili emitiranje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kad dijelite, snimate ili emitirate, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što je vidljivo na zaslonu ili se reproducira na uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kad dijelite, snimate ili emitirate aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dijeljenje ili snimanje pomoću aplikacije"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite li dijeliti zaslon s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dijeljenje jedne aplikacije"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dijeljenje cijelog zaslona"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kada dijelite cijeli zaslon, sve na zaslonu bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeljenje zaslona"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućila je ovu opciju"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite li pokrenuti emitiranje?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kad emitirate, Android ima pristup svemu što je vidljivo na zaslonu ili se reproducira na uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kad emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Pokreni emitiranje"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju za dijeljenje"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite li emitirati zaslon?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiranje jedne aplikacije"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emitiranje cijelog zaslona"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kada emitirate cijeli zaslon, sve na zaslonu bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kada emitirate aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emitiranje zaslona"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Odaberite aplikaciju za emitiranje"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite li pokrenuti dijeljenje?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kad dijelite, snimate ili emitirate, Android ima pristup svemu što je vidljivo na zaslonu ili se reproducira na uređaju. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kad dijelite, snimate ili emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na zaporke, podatke o plaćanju, poruke, fotografije te audio i videozapise."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Pokreni"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Dalje"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Dijeljenje pauza tijekom prebacivanja aplikacija"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Umjesto toga podijelite ovu aplikaciju"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Promjena računa"</string>
@@ -625,7 +637,7 @@
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Postavke"</string>
     <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Automatski titlovi"</string>
     <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Glasnoća je stišana na sigurniju razinu"</string>
-    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Glasnoća u slušalicama pojačana je dulje nego što se preporučuje"</string>
+    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Zvuk u slušalicama bio je preglasan dulje nego što se preporučuje"</string>
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Glasnoća slušalica premašila je sigurno ograničenje za ovaj tjedan"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Nastavi slušati"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Stišaj"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra veza"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, veza je dostupna"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS putem satelita"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Hitni pozivi ili SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Poslovni profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Zabava za neke, ali ne za sve"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Ugađanje korisničkog sučelja sustava pruža vam dodatne načine za prilagodbu korisničkog sučelja Androida. Te se eksperimentalne značajke mogu promijeniti, prekinuti ili nestati u budućim izdanjima. Nastavite uz oprez."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pristupačnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tipkovni prečaci"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nema rezultata pretraživanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Pokret za povratak"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pokret za otvaranje početnog zaslona"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tipka za radnju"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Sjajno!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Dodirna podloga prikazuje tri prsta koji se kreću udesno i ulijevo"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Na zaslonu uređaja prikazuje se animacija za pokret za povratak"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Da biste se vratili natrag, s tri prsta prijeđite ulijevo ili udesno bilo gdje na dodirnoj podlozi.\n\nZa to možete upotrijebiti i tipku za radnju tipkovnog prečaca + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Sjajno!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Da biste u bilo kojem trenutku otvorili početni zaslon, trima prstima prijeđite prema gore od dna zaslona."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Odlično!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Izvršili ste pokret za otvaranje početnog zaslona."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka za radnju"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Da biste pristupili svojim aplikacijama, pritisnite tipku za radnje na tipkovnici."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Izvršili ste pokret tipke za radnju.\n\nRadnja + / prikazuje sve prečace koji su vam dostupni."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Da biste se vratili na početni zaslon, prijeđite prema gore trima prstima na dodirnoj podlozi."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Za prikaz nedavnih aplikacija prijeđite prema gore trima prstima i zadržite pritisak na dodirnoj podlozi"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Za prikaz svojih svih aplikacija pritisnite tipku za radnju na tipkovnici"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redaktirano"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Otključajte za prikaz"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Upotrijebite dodirnu podlogu za povratak"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Prijeđite ulijevo ili udesno trima prstima. Dodirnite da biste naučili više pokreta."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Uz pomoć dodirne podloge vratite se na početni zaslon"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Prijeđite prema gore trima prstima i zadržite pritisak. Dodirnite da biste naučili više pokreta."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Upotrijebite tipkovnicu za prikaz svih aplikacija"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pritisnite tipku za radnju u bilo kojem trenutku. Dodirnite da biste naučili više pokreta."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Dodatno zatamnjenje sada je dio trake za svjetlinu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Zaslon možete dodatno zatamniti daljnjim smanjivanjem razine svjetline na vrhu zaslona.\n\nTo najbolje funkcionira kada ste u tamnom okruženju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ukloni prečac za dodatno zatamnjenje"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Prečac za dodatno zatamnjenje je uklonjen. Da biste smanjili svjetlinu, upotrijebite regularnu traku za svjetlinu."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 0604064..773d9ba 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> és más nyitott alkalmazások észlelték ezt a képernyőképet."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Hozzáadás jegyzethez"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Linkkel együtt"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Képernyőrögzítő"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Képernyőrögzítés feldolgozása"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Folyamatban lévő értesítés képernyőrögzítési munkamenethez"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Elindítja a felvételt?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Rögzítés közben az Android a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Alkalmazás rögzítése közben az Android az adott alkalmazásban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Felvétel indítása"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rögzíti a képernyőt?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Egyetlen alkalmazás rögzítése"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Teljes képernyő rögzítése"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"A teljes képernyő rögzítése esetén a képernyőn megjelenő minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Alkalmazás rögzítésekor az adott alkalmazásban megjelenített vagy lejátszott minden tartalom rögzítésre kerül. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Képernyő rögzítése"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Válassza ki a rögzíteni kívánt alkalmazást"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Hang rögzítése"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Eszköz hangja"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Az eszköz által lejátszott hangok, például zeneszámok, hívások és csengőhangok"</string>
@@ -127,7 +131,7 @@
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hiba történt a képernyőrögzítés mentése során"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hiba a képernyőrögzítés indításakor"</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Leállítja a felvételt?"</string>
-    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Jelenleg a teljes képernyőről készít felvételt"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Jelenleg a teljes képernyőről felvételt készít"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Jelenleg a következőről készít felvételt: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Felvétel leállítása"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Képernyő megosztása…"</string>
@@ -150,7 +154,7 @@
     <string name="issuerecord_title" msgid="286627115110121849">"Problémafelvevő"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Problémafelvétel feldolgozása…"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Folyamatban lévő értesítés egy problémagyűjtési munkamenethez"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Problémafelvétel folyamatban…"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Probléma rögzítése folyamatban…"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Megosztás"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"Problémafelvétel mentve"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Koppintson a megtekintéshez"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Küldés"</string>
     <string name="cancel" msgid="1089011503403416730">"Mégse"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Alkalmazás emblémája"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Megerősítés"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Újrapróbálkozás"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Koppintson a hitelesítés visszavonásához"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Képernyővédő"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne zavarjanak"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritási módok"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nem áll rendelkezésre párosított eszköz"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Koppintson egy eszköz csatlakoztatásához vagy leválasztásához"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Hang megosztása"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Koppintással átkapcsolhatja vagy megoszthatja a hangot"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Élő feliratozás"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Élő feliratozás"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszköz mikrofonjának letiltását?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszköz kamerájának letiltását?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszköz kamerájának és mikrofonjának letiltását?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Beállítások megnyitása"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Más eszköz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Áttekintés be- és kikapcsolása"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritási módok"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kész"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Beállítások"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Beállítás"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Az ébresztéseken kívül nem fogja Önt más hang és 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>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Személyre szabás"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Modulok a lezárási képernyőn"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul felvéve a lezárási képernyőre"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Csúsztasson gyorsan balra a közösségi útmutató elindításához"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Személyre szabás"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Elvetés"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, vagy amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés során. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, a fotók, az üzenetek és a lejátszott audiotartalmak."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Biztosan elkezdi a rögzítést vagy az átküldést?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"A funkciót biztosító szolgáltatás hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, illetve amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés közben. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, a fotók, az üzenetek és a lejátszott audiotartalmak."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Teljes képernyő"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Egyetlen alkalmazás"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Alkalmazás megosztása vagy rögzítése"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Elkezdi a rögzítést vagy az átküldést a következővel: <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Amikor Ön megosztást, rögzítést vagy átküldést végez, a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Amikor Ön megoszt, rögzít vagy átküld egy alkalmazást, a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> az adott appban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Indítás"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Alkalmazás megosztása vagy rögzítése"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Megosztja a képernyőjét a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alkalmazással?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Egyetlen alkalmazás megosztása"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"A teljes képernyő megosztása"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"A teljes képernyő megosztása esetén a képernyő teljes tartalma látható lesz a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Alkalmazás megosztása közben az adott appban megjelenített vagy lejátszott minden tartalom látható a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Képernyő megosztása"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> letiltotta ezt a beállítást"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Elindítja az átküldést?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Amikor Ön átküldést végez, az Android a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Alkalmazás átküldése közben az Android az adott appban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Az átküldés indítása"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Válassza ki a megosztani kívánt alkalmazást"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Átküldi a képernyőt?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Egyetlen app átküldése"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Teljes képernyő átküldése"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"A teljes képernyő átküldése esetén a képernyő teljes tartalma látható. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Alkalmazás átküldése közben az adott appban megjelenített vagy lejátszott minden tartalom látható. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Képernyőtartalom átküldése"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Válassza ki az átküldeni kívánt alkalmazást"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Megkezdi a megosztást?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Amikor Ön megosztást, rögzítést vagy átküldést végez, az Android a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Amikor Ön megoszt, rögzít vagy átküld egy alkalmazást, az Android az adott alkalmazásban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Indítás"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Következő"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"A megosztás szünetel alkalmazásváltáskor"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Inkább ennek az appnak a megosztása"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Visszaváltás"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Műhold, jó kapcsolat"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Műhold, van rendelkezésre álló kapcsolat"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Műholdas SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Segélyhívás vagy SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Munkaprofil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Egyeseknek tetszik, másoknak nem"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"A Kezelőfelület-hangoló az Android felhasználói felületének szerkesztéséhez és testreszabásához nyújt további megoldásokat. Ezek a kísérleti funkciók változhatnak vagy megsérülhetnek a későbbi kiadásokban, illetve eltűnhetnek azokból. Körültekintően járjon el."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kisegítő lehetőségek"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Billentyűparancsok"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nincsenek keresési találatok"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Vissza kézmozdulat"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Kezdőképernyő kézmozdulat"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Műveletbillentyű"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Kiváló!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Három, jobbra és balra mozgó ujjat ábrázoló érintőpad"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"A Vissza kézmozdulat animációját megjelenítő eszközképernyő"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"A visszalépéshez csúsztasson három ujjal balra vagy a jobbra az érintőpadon.\n\nEnnek végrehajtásához használhatja az Action + Esc billentyűparancsot is."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Kiváló!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ha bármikor vissza szeretne térni a kezdőképernyőre, csúsztassa gyorsan felfelé három ujját a képernyő aljáról."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Remek!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Műveletbillentyű"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Az alkalmazásokhoz való hozzáféréshez nyomja meg a billentyűzet műveletbillentyűjét."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulálunk!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Teljesítette a műveletbillentyű kézmozdulatát.\n\nA Művelet + / billentyűkombinációval megjelenítheti az összes használható billentyűparancsot."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"A kezdőképernyőre való ugráshoz csúsztasson felfelé három ujjal az érintőpadon."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"A legutóbbi appokért csúsztasson lefelé három ujjal az érintőpadon, és tartsa lenyomva ujjait."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Az összes alkalmazás megtekintéséhez nyomja meg a billentyűzet műveletbillentyűjét."</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Törölve"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Oldja fel a megtekintéshez"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"A visszalépéshez használja az érintőpadot"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Csúsztatasson gyorsan három ujjal balra vagy jobbra. Koppintson a további kézmozdulatokért."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"A kezdőképernyő megnyitásához használja az érintőpadot"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Gyúsztason felfelé három ujjal, és tartsa lenyomva az ujjait. Koppintson a további kézmozdulatokért."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"A billentyűzet használatával valamennyi alkalmazás megtekinthető"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"A műveletbillentyű bármikor használható. Koppintson a további kézmozdulatokért."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Az extrasötét funkció mostantól része a fényerő-beállítási sávnak"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"A képernyő tetején mostantól extrasötétre állíthatja a képernyőt, amivel a korábbinál még jobban csökkentheti a fényerőt.\n\nA funkció sötét környezetben használható a legjobban."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Az extrasötét funkció gyorsparancsának eltávolítása"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Eltávolította az extrasötét funkció gyorsparancsát. A fényerő csökkentéséhez használja a fényerő-beállítási sávot."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 6f32125..6a64c5d 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>-ն ու բացված այլ հավելվածներ հայտնաբերել են այս սքրինշոթը։"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ավելացնել նշմանը"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Ներառել հղումը"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Էկրանի տեսագրում"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Էկրանի տեսագրության մշակում"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Սկսե՞լ տեսագրումը"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Երբ դուք տեսագրում եք էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին և նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Երբ դուք տեսագրում եք որևէ հավելվածի էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Սկսել տեսագրումը"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Տեսագրե՞լ ձեր էկրանը"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Տեսագրել մեկ հավելված"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Տեսագրել ամբողջ էկրանը"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Երբ դուք տեսագրում եք ամբողջ էկրանը, էկրանին ցուցադրվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Երբ դուք որևէ հավելված եք տեսագրում, հավելվածում ցուցադրվող կամ նվագարկվող ամեն ինչ տեսագրվում է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Տեսագրել էկրանը"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Հավելվածի ընտրություն՝ տեսագրելու համար"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Ձայնագրել"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Սարքի ձայները"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Ձեր սարքի ձայները, օրինակ՝ երաժշտությունը, զանգերն ու զանգերանգները"</string>
@@ -123,12 +127,12 @@
     <string name="screenrecord_stop_label" msgid="72699670052087989">"Կանգնեցնել"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Կիսվել"</string>
     <string name="screenrecord_save_title" msgid="1886652605520893850">"Էկրանի տեսագրությունը պահվեց"</string>
-    <string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք՝ դիտելու համար"</string>
+    <string name="screenrecord_save_text" msgid="3008973099800840163">"Հպեք դիտելու համար"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Չհաջողվեց պահել էկրանի տեսագրությունը"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Չհաջողվեց սկսել տեսագրումը"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Կանգնեցնե՞լ ձայնագրումը"</string>
-    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Դուք ներկայումս ձայնագրում եք ձեր ամբողջ էկրանը"</string>
-    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Դուք ներկայումս ձայնագրում եք <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Կանգնեցնե՞լ տեսագրումը"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Դուք ներկայումս տեսագրում եք ձեր ամբողջ էկրանը"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Դուք ներկայումս տեսագրում եք <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Կանգնեցնել տեսագրումը"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Միացված է էկրանի ցուցադրումը"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Դադարեցնե՞լ էկրանի ցուցադրումը"</string>
@@ -145,7 +149,7 @@
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Դուք ներկայումս հեռարձակում եք <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> հավելվածը մոտակա սարքին"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Դուք ներկայումս հեռարձակում եք <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքին"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Դուք ներկայումս հեռարձակում եք մոտակա սարքին"</string>
-    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Դադարեցնել հեռարձակումը"</string>
+    <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Կանգնեցնել հեռարձակումը"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Փակել"</string>
     <string name="issuerecord_title" msgid="286627115110121849">"Խնդիրների տեսագրիչ"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Մշակում ենք տեսագրությունը"</string>
@@ -153,7 +157,7 @@
     <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Տեսագրում ենք խնդիրը"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Կիսվել"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"Տեսագրությունը պահվեց"</string>
-    <string name="issuerecord_save_text" msgid="1205985304551521495">"Հպեք՝ դիտելու համար"</string>
+    <string name="issuerecord_save_text" msgid="1205985304551521495">"Հպեք դիտելու համար"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Չհաջողվեց պահել տեսագրությունը"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Չհաջողվեց սկսել տեսագրումը"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"Լիաէկրան դիտակերպ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ուղարկել"</string>
     <string name="cancel" msgid="1089011503403416730">"Չեղարկել"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Հավելվածի լոգո"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Հաստատել"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Նորից փորձել"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Հպեք՝ նույնականացումը չեղարկելու համար"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Էկրանապահ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Չանհանգստացնել"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Կարևոր ռեժիմներ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Զուգակցված սարքեր չկան"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Հպեք՝ սարք միացնելու կամ անջատելու համար"</string>
@@ -299,14 +302,16 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Աուդիոյի փոխանցում"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Հպեք՝ աուդիոն փոխանջատելու կամ դրանով կիսվելու համար"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Պահված է"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"անջատել"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ակտիվացնել"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Ավտոմատ միացնել վաղը"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string>
-    <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Փոխանցել աուդիո"</string>
+    <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Էկրանի տեսագրում"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Սկսել"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Կանգնեցնել"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Ձայնագրել"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Խնդրի տեսագրում"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Սկսել"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Կանգնեցնել"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Հաղորդում սխալի մասին"</string>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string>
     <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string>
     <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string>
-    <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string>
+    <string name="thermal" msgid="6758074791325414831">"Տաքացում"</string>
     <string name="custom" msgid="3337456985275158299">"Հատուկ"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Հետագծման հատուկ կարգավորումներ"</string>
     <string name="restore_default" msgid="5259420807486239755">"Վերականգնել կանխադրված կարգավորումները"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Կենդանի ենթագրեր"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Կենդանի ենթագրեր"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Բացել կարգավորումները"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Այլ սարք"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Միացնել/անջատել համատեսքը"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Կարևոր ռեժիմներ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Պատրաստ է"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Կարգավորումներ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Կարգավորել"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ձայները և թրթռոցները, բացի զարթուցիչներից, չեն անհանգստացնի ձեզ: Դուք կլսեք ձեր ընտրածի նվագարկումը, այդ թվում՝ երաժշտություն, տեսանյութեր և խաղեր:"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Հարմարեցնել"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Դանդաղ լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Լիցքավորում • Մնացել է <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Վիջեթներ կողպէկրանին"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"«<xliff:g id="WIDGET_NAME">%1$s</xliff:g>» վիջեթն ավելացվեց կողպէկրանին"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Թերթեք ձախ՝ ուղեցույցը գործարկելու համար"</string>
-    <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Անհատականացնել"</string>
+    <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Կարգավորել"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Փակել"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ավելացնել վիջեթներ, ինչպես նաև հեռացնել և վերադասավորել դրանք այս տարածքում"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Ավելացրեք, հեռացրեք և դասավորեք վիջեթները այս տարածքում"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Ավելացնել վիջեթներ"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Երկար սեղմեք՝ վիջեթները հարմարեցնելու համար"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Հարմարեցնել վիջեթները"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Ավելացնել վիջեթ"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Պատրաստ է"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Ավելացնել վիջեթներ"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Արագ բացեք հավելվածների ձեր սիրելի վիջեթները առանց ապակողպելու պլանշետը։"</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Հեշտությամբ օգտվեք ձեր սիրելի հավելվածներից, նույնիսկ երբ պլանշետը կողպված է։"</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Թույլատրե՞լ վիջեթների ցուցադրումը կողպէկրանին"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Բացել կարգավորումները"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Վերսկսե՞լ աշխ. հավելվածները"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Տեսագրման և հեռարձակման ընթացքում <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի կլինեն ձեր սարքի էկրանին ցուցադրվող տեղեկությունները և ձեր սարքով նվագարկվող նյութերը։ Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, գաղտնաբառերը, վճարային տվյալները, լուսանկարները, հաղորդագրությունները և նվագարկվող աուդիո ֆայլերը։"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Սկսե՞լ տեսագրումը կամ հեռարձակումը"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Տեսագրման և հեռարձակման ընթացքում ծառայությունների մատակարարին հասանելի կլինեն ձեր սարքի էկրանին ցուցադրվող տեղեկությունները և ձեր սարքով նվագարկվող նյութերը։ Սա ներառում է այնպիսի տեղեկություններ, ինչպիսիք են, օրինակ, գաղտնաբառերը, վճարային տվյալները, լուսանկարները, հաղորդագրությունները և նվագարկվող աուդիո ֆայլերը։"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Ամբողջ էկրանը"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Մեկ հավելված"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Հավելվածի էկրանի ցուցադրում կամ տեսագրում"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Սկսե՞լ տեսագրումը կամ հեռարձակումը <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածով"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք էկրանը, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին և նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք որևէ հավելվածի էկրանը, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի է լինում այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Սկսել"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Հավելվածի էկրանի ցուցադրում կամ տեսագրում"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ցուցադրե՞լ ձեր էկրանը <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածով"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ցուցադրել մեկ հավելված"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ցուցադրել ամբողջ էկրանը"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Երբ ցուցադրում եք ամբողջ էկրանը, տեսանելի կլինի այն ամենը, ինչ ձեր էկրանին տեսանելի է <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Երբ դուք որևէ հավելված եք հեռարձակում, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ցուցադրել էկրանը"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ն անջատել է այս ընտրանքը"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Սկսե՞լ հեռարձակումը"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Երբ դուք հեռարձակում եք էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին կամ նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Երբ դուք տեսագրում եք որևէ հավելվածի էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Սկսել հեռարձակումը"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Հավելվածի ընտրություն՝ կիսվելու համար"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Հեռարձակե՞լ ձեր էկրանը"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Հեռարձակել մեկ հավելված"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Հեռարձակել ամբողջ էկրանը"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Երբ հեռարձակում եք ամբողջ էկրանը, տեսանելի կլինի այն ամենը, ինչ ձեր էկրանին է։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Երբ դուք որևէ հավելված եք հեռարձակում, տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Հեռարձակել էկրանը"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Հավելվածի ընտրություն՝ հեռարձակելու համար"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Սկսե՞լ էկրանի ցուցադրումը"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին և նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք որևէ հավելվածի էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ ցուցադրվում է կամ նվագարկվում այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Սկսել"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Առաջ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Երբ անցում եք կատարում մեկ այլ հավելվածի, ընթացիկ հավելվածի համատեղ օգտագործումը դադարեցվում է"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Փոխարենը կիսվել այս հավելվածով"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Վերադառնալ"</string>
@@ -661,7 +673,7 @@
     <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string>
     <string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</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_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s։ Հպեք՝ թրթռոցը միացնելու համար։"</string>
     <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s։ Հպեք՝ ձայնը անջատելու համար։"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Արբանյակային լավ կապ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Հասանելի է արբանյակային կապ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Շտապ կանչեր կամ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Աշխատանքային պրոֆիլ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Զվարճանք մեկ՝ որոշակի մարդու համար"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Համակարգի ՕՄ-ի կարգավորիչը հնարավորություն է տալիս հարմարեցնել Android-ի օգտատիրոջ միջերեսը: Այս փորձնական գործառույթները կարող են հետագա թողարկումների մեջ փոփոխվել, խափանվել կամ ընդհանրապես չհայտնվել: Եթե շարունակում եք, զգուշացեք:"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Հատուկ գործառույթներ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Ստեղնային դյուրանցումներ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Որոնման արդյունքներ չկան"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"«Հետ» ժեստ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Հիմնական էկրան անցնելու ժեստ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Գործողության ստեղն"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Պատրաստ է"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Կեցցե՛ք"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Հետ գնալ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Երեք մատները աջ ու ձախ են շարժվում հպահարթակի վրա"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Սարքի էկրանին ցուցադրվում է շարժանկար՝ հետ գնալու ժեստի համար"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Հետ գնալու համար հպահարթակի վրա երեք մատով սահեցրեք ձախ կամ աջ։\n\nԻնչպես նաև կարող եք օգտագործել ստեղնային դյուրանցման գործողությունը + Esc։"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Կեցցե՛ք"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Դուք սովորեցիք հետ գնալու ժեստը։"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Անցում հիմնական էկրան"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Հիմնական էկրան վերադառնալու համար երեք մատը էկրանի ներքևից սահեցրեք վերև։"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Գերազանց է"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Դուք սովորեցիք հիմնական էկրան անցնելու ժեստը։"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Գործողության ստեղն"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Բոլոր հավելվածներն օգտագործելու համար սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Շնորհավո՛ր"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Դուք սովորեցիք գործողության ստեղնի ժեստը։\n\nԳործողություն + / ժեստը ցույց է տալիս ձեզ հասանելի բոլոր դյուրանցումները։"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Հիմնական էկրան անցնելու համար հպահարթակի վրա երեք մատը սահեցրեք ձախ կամ աջ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Վերջերս օգտագործված հավելվածները դիտելու համար երեք մատը սահեցրեք վերև և սեղմած պահեք"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Բոլոր հավելվածները դիտելու համար սեղմեք գործողության ստեղնը ստեղնաշարի վրա"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Կոդավորված"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Ապակողպել դիտելու համար"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Օգտագործեք ձեր հպահարթակը՝ վերադառնալու համար"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Երեք մատը սահեցրեք ձախ կամ աջ։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Օգտագործեք ձեր հպահարթակը՝ հիմնական էկրան անցնելու համար"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Երեք մատը սահեցրեք վերև և սեղմած պահեք։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Օգտագործեք ձեր ստեղնաշարը՝ բոլոր հավելվածները դիտելու համար"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ցանկացած ժամանակ սեղմեք գործողության ստեղնը։ Հպեք՝ ավելի շատ ժեստերի ծանոթանալու համար։"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Հավելյալ խամրեցումն այժմ պայծառության գոտում է"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Էկրանը հավելյալ խամրեցնելու համար բացեք կարգավորումները էկրանի վերևի մասից։\n\nԽորհուրդ ենք տալիս օգտագործել այս գործառույթը, երբ շուրջը մութ է։"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Հեռացնել հավելյալ խամրեցման դյուրանցումը"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Հավելյալ խամրեցման դյուրանցումը հեռացվեց։ Պայծառության մակարդակը նվազեցնելու համար օգտագործեք պայծառության գոտին։"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 873372b..8c58012 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan aplikasi terbuka lainnya mendeteksi screenshot ini."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Tambahkan ke catatan"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Sertakan link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Perekam Layar"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses perekaman layar"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Mulai Merekam?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Saat Anda merekam, Android akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Saat Anda merekam aplikasi, Android akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Mulai merekam"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekam layar Anda?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekam satu aplikasi"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekam seluruh layar"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Saat Anda merekam seluruh layar, semua hal yang ditampilkan di layar akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Jika Anda merekam aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan direkam. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekam layar"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Pilih aplikasi yang akan direkam"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Rekam audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio perangkat"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Suara dari perangkat Anda, seperti musik, panggilan, dan nada dering"</string>
@@ -120,7 +124,7 @@
     <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Merekam layar"</string>
     <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Merekam layar dan audio"</string>
     <string name="screenrecord_taps_label" msgid="1595690528298857649">"Tampilkan lokasi sentuhan pada layar"</string>
-    <string name="screenrecord_stop_label" msgid="72699670052087989">"Stop"</string>
+    <string name="screenrecord_stop_label" msgid="72699670052087989">"Berhenti"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Bagikan"</string>
     <string name="screenrecord_save_title" msgid="1886652605520893850">"Rekaman layar disimpan"</string>
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Ketuk untuk melihat"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Kirim"</string>
     <string name="cancel" msgid="1089011503403416730">"Batal"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo aplikasi"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Konfirmasi"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Coba lagi"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Ketuk untuk membatalkan autentikasi"</string>
@@ -290,23 +292,26 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Jangan Ganggu"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mode prioritas"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Perangkat yang disandingkan tak tersedia"</string>
-    <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketuk untuk memulai atau menghentikan koneksi perangkat"</string>
+    <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketuk untuk mulai atau berhenti menghubungkan perangkat"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Sambungkan perangkat baru"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string>
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Berbagi Audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ketuk untuk beralih atau berbagi audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
-    <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string>
+    <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"berhenti hubungkan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"Aktifkan otomatis besok"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string>
     <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_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>
@@ -378,16 +383,16 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Perekam layar"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mulai"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Mencatat Masalah"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekam Masalah"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Mulai"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Berhenti"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Bug"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hal apa yang terpengaruh?"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Apa jenis masalah yang Anda alami pada perangkat?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string>
     <string name="performance" msgid="6552785217174378320">"Performa"</string>
     <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string>
-    <string name="thermal" msgid="6758074791325414831">"Termal"</string>
+    <string name="thermal" msgid="6758074791325414831">"Panas"</string>
     <string name="custom" msgid="3337456985275158299">"Kustom"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Setelan Rekaman Aktivitas Kustom"</string>
     <string name="restore_default" msgid="5259420807486239755">"Pulihkan Default"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Teks Otomatis"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Teks Otomatis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Setelan"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Perangkat lainnya"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktifkan Ringkasan"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mode prioritas"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setelan"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Siapkan"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sesuaikan"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya dengan lambat • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengisi daya • Penuh dalam waktu <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget di layar kunci"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan ke layar kunci"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Geser ke kiri untuk memulai tutorial komunal"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sesuaikan"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Tutup"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua informasi yang terlihat di layar atau diputar dari perangkat saat merekam atau melakukan transmisi. Ini mencakup informasi seperti sandi, detail pembayaran, foto, pesan, dan audio yang Anda putar."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Mulai merekam atau melakukan transmisi?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Layanan yang menyediakan fungsi ini akan memiliki akses ke semua informasi yang terlihat di layar atau diputar dari perangkat saat merekam atau melakukan transmisi. Ini mencakup informasi seperti sandi, detail pembayaran, foto, pesan, dan audio yang Anda putar."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Seluruh layar"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Satu aplikasi"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Bagikan atau rekam aplikasi"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Mulai merekam atau mentransmisikan dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Jika Anda membagikan, merekam, atau mentransmisikan, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Jika Anda membagikan, merekam, atau mentransmisikan suatu aplikasi, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio dan video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Mulai"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Bagikan atau rekam aplikasi"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bagikan layar dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bagikan satu aplikasi"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bagikan seluruh layar"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"JIka Anda membagikan seluruh layar, semua hal yang ada di layar Anda akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Jika Anda membagikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bagikan layar"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah menonaktifkan opsi ini"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Mulai transmisi?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Jika Anda melakukan transmisi, Android akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Jika Anda mentransmisikan aplikasi, Android akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Mulai transmisi"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih aplikasi yang akan dibagikan"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmisikan layar?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmisikan satu aplikasi"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmisikan seluruh layar"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"JIka Anda mentransmisikan seluruh layar, semua hal yang ada di layar Anda akan terlihat. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Jika Anda mentransmisikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Layar Cast"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Pilih aplikasi yang akan ditransmisikan"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Mulai berbagi?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Jika Anda membagikan, merekam, atau mentransmisikan, Android akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Jika Anda membagikan, merekam, atau mentransmisikan suatu aplikasi, Android akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Mulai"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Berikutnya"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Berbagi akan dijeda saat Anda beralih aplikasi"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Bagikan aplikasi ini"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Beralih kembali"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, koneksi baik"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, koneksi tersedia"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Panggilan darurat atau SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Tidak semua orang menganggapnya baik"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Penyetel Antarmuka Pengguna Sistem memberikan cara tambahan untuk mengubah dan menyesuaikan antarmuka pengguna Android. Fitur eksperimental ini dapat berubah, rusak, atau menghilang dalam rilis di masa mendatang. Lanjutkan dengan hati-hati."</string>
@@ -782,7 +795,7 @@
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
     <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
     <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Play/Pause"</string>
-    <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Stop"</string>
+    <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Berhenti"</string>
     <string name="keyboard_key_media_next" msgid="8502476691227914952">"Next"</string>
     <string name="keyboard_key_media_previous" msgid="5637875709190955351">"Previous"</string>
     <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Rewind"</string>
@@ -948,8 +961,8 @@
     <string name="tuner_circle" msgid="5270591778160525693">"Lingkaran"</string>
     <string name="tuner_plus" msgid="4130366441154416484">"Plus"</string>
     <string name="tuner_minus" msgid="5258518368944598545">"Minus"</string>
-    <string name="tuner_left" msgid="5758862558405684490">"Kiri"</string>
-    <string name="tuner_right" msgid="8247571132790812149">"Kanan"</string>
+    <string name="tuner_left" msgid="5758862558405684490">"Kiri (L)"</string>
+    <string name="tuner_right" msgid="8247571132790812149">"Kanan (R)"</string>
     <string name="tuner_menu" msgid="363690665924769420">"Menu"</string>
     <string name="tuner_app" msgid="6949280415826686972">"Aplikasi <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="notification_channel_alerts" msgid="3385787053375150046">"Notifikasi"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tidak ada hasil penelusuran"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestur kembali"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestur layar utama"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tombol tindakan"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bagus!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad menampilkan tiga jari yang bergerak ke kanan dan ke kiri"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Layar perangkat menampilkan animasi untuk gestur kembali"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Untuk kembali, geser ke kiri atau kanan menggunakan tiga jari di touchpad.\n\nAnda juga dapat menggunakan pintasan keyboard Action + ECS untuk melakukannya."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Untuk membuka layar utama kapan saja, geser ke atas menggunakan tiga jari dari bawah layar Anda."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bagus!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Anda telah menyelesaikan gestur buka layar utama."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tombol tindakan"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Untuk mengakses aplikasi, tekan tombol tindakan di keyboard."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Selamat!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Anda telah menyelesaikan gestur tombol tindakan.\n\nTindakan + / akan menampilkan semua pintasan yang Anda miliki."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk membuka layar utama, geser ke atas menggunakan tiga jari di touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Untuk melihat aplikasi terkini, geser ke atas dan tahan menggunakan tiga jari di touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua aplikasi, tekan tombol tindakan di keyboard"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Disamarkan"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Buka kunci untuk melihat"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Gunakan touchpad untuk kembali"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Geser ke kiri atau kanan menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Gunakan touchpad untuk membuka layar utama"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Geser ke atas dan tahan menggunakan tiga jari. Ketuk untuk mempelajari gestur lainnya."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan keyboard untuk melihat semua aplikasi"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan tombol tindakan kapan saja. Ketuk untuk mempelajari gestur lainnya."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ekstra redup kini menjadi bagian dari panel kecerahan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Anda kini dapat membuat layar menjadi ekstra redup dengan menurunkan tingkat kecerahan lebih banyak lagi dari bagian atas layar.\n\nFitur ini berfungsi optimal saat Anda berada di tempat yang gelap."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Hapus pintasan ekstra redup"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Pintasan ekstra redup dihapus. Untuk menurunkan kecerahan, gunakan panel kecerahan biasa."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 50145b0..6b3b6732 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og önnur opin forrit greindu skjámyndina."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Bæta við glósu"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Hafa tengil með"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Skjáupptaka"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Vinnur úr skjáupptöku"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Hefja upptöku?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Þegar þú tekur upp hefur Android aðgang að öllu sem sést á skjánum eða spilast í tækinu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Þegar þú tekur upp forrit hefur Android aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Hefja upptöku"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Viltu taka upp skjáinn?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Taka upp eitt forrit"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Taka upp allan skjáinn"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Þegar þú tekur upp allan skjáinn verður allt sem er sýnilegt á skjánum tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Þegar þú tekur upp forrit verður allt sem er sýnilegt eða spilað í forritinu tekið upp. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Taka upp skjá"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Velja forrit til að taka upp"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Taka upp hljóð"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Hljóð tækis"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Hljóð úr tækinu á borð við tónlist, símtöl og hringitóna"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Senda"</string>
     <string name="cancel" msgid="1089011503403416730">"Hætta við"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Lógó forrits"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Staðfesta"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Reyna aftur"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Ýttu til að hætta við auðkenningu"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skjávari"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ónáðið ekki"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Forgangsstillingar"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Engin pöruð tæki til staðar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ýttu til að tengja eða aftengja tæki"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Hljóði deilt"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ýttu til að skipta um eða deila hljóði"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Skjátextar í rauntíma"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Skjátextar í rauntíma"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Opna stillingar"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annað tæki"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kveikja/slökkva á yfirliti"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Forgangsstillingar"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Lokið"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Stillingar"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Setja upp"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sérsníða"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Græjur á lásskjá"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Græjunni „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ var bætt við lásskjá"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Strjúktu til vinstri til að hefja samfélagsleiðsögnina"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sérsníða"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hunsa"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Bæta græju við"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Lokið"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Bæta við græjum"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Fáðu skjótan aðgang að eftirlætis forritagræjunum án þess að taka spjaldtölvuna úr lás."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Fáðu skjótan aðgang að eftirlætisforritagræjunum án þess að taka spjaldtölvuna úr lás."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Leyfa allar græjur á lásskjá?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Opna stillingar"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Ljúka hléi vinnuforrita?"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mun hafa aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða vörpun er í gangi. Þar á meðal eru upplýsingar á borð við aðgangsorð, greiðsluupplýsingar, myndir, skilaboð og hljóð sem þú spilar."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Viltu hefja upptöku eða vörpun?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Þjónustan sem býður upp á þennan eiginleika fær aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða vörpun er í gangi, þar á meðal aðgangsorði, greiðsluupplýsingum, myndum, skilaboðum og hljóðefni sem þú spilar."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Allur skjárinn"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Eitt forrit"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Deila eða taka upp forrit"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Viltu hefja upptöku eða vörpun með <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Þegar þú deilir, tekur upp eða varpar hefur<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aðgang að öllu sem sést á skjánum eða spilast í tækinu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Þegar þú deilir, tekur upp eða varpar forriti hefur <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Byrja"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deila eða taka upp forrit"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Deila skjánum með <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deila einu forriti"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deila öllum skjánum"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Þegar þú deilir öllum skjánum verður allt á skjánum sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Þegar þú deilir forriti er allt sem sést eða er spilað í því forriti sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og vídeó."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deila skjá"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> slökkti á þessum valkosti"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Byrja að varpa?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Þegar þú varpar hefur Android aðgang að öllu sem er sýnilegt á skjánum hjá þér eða spilast í tækinu þínu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Þegar þú varpar forriti hefur Android aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Byrja að varpa"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velja forrit til að deila"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Varpa skjánum?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Varpa einu forriti"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Varpa öllum skjánum"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Þegar þú varpar öllum skjánum þá er allt á skjánum sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Þegar þú varpar forriti er allt sem sést eða er spilað í því forriti sýnilegt. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Senda út skjá"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Velja forrit til að varpa"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Byrja að deila?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Þegar þú deilir, tekur upp eða varpar hefur Android aðgang að öllu sem sést á skjánum eða spilast í tækinu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Þegar þú deilir, tekur upp eða varpar forriti hefur Android aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Byrja"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Áfram"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Hlé er gert á samnýtingu þegar þú skiptir um forrit"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Deila frekar þessu forriti"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Skipta til baka"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Gervihnöttur, góð tenging"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Gervihnöttur, tenging tiltæk"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Gervihnattar-SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Neyðarsímtöl eða SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Vinnusnið"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Þetta er ekki allra"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Fínstillingar kerfisviðmóts gera þér kleift að fínstilla og sérsníða notendaviðmót Android. Þessir tilraunaeiginleikar geta breyst, bilað eða horfið í síðari útgáfum. Gakktu því hægt um gleðinnar dyr."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aðgengi"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Flýtilyklar"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Engar leitarniðurstöður"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Bending til að fara til baka"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Bending til að fara á upphafsskjá"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Aðgerðalykill"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Vel gert!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Snertiflötur sem sýnir þrjá fingur færast til hægri og vinstri"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skjár tækis sem sýnir hreyfimynd fyrir bendinguna „til baka“."</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Strjúktu til vinstri eða hægri með þremur fingrum hvar sem er á snertifletinum til að fara til baka.\n\nÞú getur einnig notað flýtileiðaraðgerðina + ESC til að gera þetta."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Vel gert!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Strjúktu upp frá neðri brún skjásins með þremur fingrum til að opna heimaskjáinn."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Flott!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Þú laukst við að kynna þér bendinguna „heim“."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Aðgerðalykill"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ýttu á aðgerðalykilinn á lyklaborðinu til að opna forritin þín."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Til hamingju!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Þú laukst við að kynna þér bendinguna „aðgerðalykill“.\n\nAðgerðalykill + / sýnir þér alla flýtilykla sem eru í boði."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Strjúktu upp með þremur fingrum á snertifletinum til að fara á heimaskjá"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Strjúktu upp og haltu með þremur fingrum á snertifletinum til að sjá nýleg forrit"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ýttu á aðgerðalykilinn á lyklaborðinu til að sjá öll forritin þín"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Ritskoðað"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Taktu úr lás til að skoða"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Notaðu snertiflötinn til að fara til baka"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Strjúktu til vinstri eða hægri með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Notaðu snertiflötinn til að fara á heimaskjá"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Strjúktu upp og haltu með þremur fingrum. Ýttu til að læra fleiri bendingar."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Notaðu lyklaborðið til að sjá öll forrit"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Ýttu hvenær sem er á aðgerðalykilinn. Ýttu til að læra fleiri bendingar."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Nú er stillingin „mjög dökkt“ hluti af birtustigsstikunni"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Nú geturðu gert skjáinn mjög dökkan með því að lækka birtustigið enn frekar efst á skjánum.\n\nÞetta virkar best þegar umhverfi þitt er mjög dimmt."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Fjarlægja flýtilykil á mjög dökka stillingu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Flýtilykill á mjög dökka stillingu fjarlægður. Notaðu hefðbundnu birtustigsstikuna til að lækka birtustigið."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it-feminine/strings.xml b/packages/SystemUI/res/values-it-feminine/strings.xml
index 99b9361..e8dbc40 100644
--- a/packages/SystemUI/res/values-it-feminine/strings.xml
+++ b/packages/SystemUI/res/values-it-feminine/strings.xml
@@ -21,5 +21,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbata da suoni e vibrazioni, 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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbata da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
-    <string name="empty_user_name" msgid="3389155775773578300">"Amiche"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it-masculine/strings.xml b/packages/SystemUI/res/values-it-masculine/strings.xml
index 5e78889..5a76304 100644
--- a/packages/SystemUI/res/values-it-masculine/strings.xml
+++ b/packages/SystemUI/res/values-it-masculine/strings.xml
@@ -21,5 +21,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbato da suoni e vibrazioni, 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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbato da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
-    <string name="empty_user_name" msgid="3389155775773578300">"Amici"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it-neuter/strings.xml b/packages/SystemUI/res/values-it-neuter/strings.xml
index 0e1c227..09237f5 100644
--- a/packages/SystemUI/res/values-it-neuter/strings.xml
+++ b/packages/SystemUI/res/values-it-neuter/strings.xml
@@ -21,5 +21,4 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Non verrai disturbatə da suoni e vibrazioni, 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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Non verrai disturbatə da suoni e vibrazioni, ad eccezione delle sveglie. Potrai comunque sentire qualunque cosa decidi di riprodurre, inclusi video, musica e giochi."</string>
-    <string name="empty_user_name" msgid="3389155775773578300">"Amicɜ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 502678e..958d67b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -19,7 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4811759950673118541">"UI sistema"</string>
+    <string name="app_label" msgid="4811759950673118541">"UI di sistema"</string>
     <string name="battery_low_title" msgid="5319680173344341779">"Attivare il risparmio energetico?"</string>
     <string name="battery_low_description" msgid="3282977755476423966">"Batteria rimanente: <xliff:g id="PERCENTAGE">%s</xliff:g>. Il risparmio energetico attiva il tema scuro, limita l\'attività in background e ritarda le notifiche."</string>
     <string name="battery_low_intro" msgid="5148725009653088790">"Il risparmio energetico attiva il tema scuro, limita l\'attività in background e ritarda le notifiche."</string>
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e altre app aperte hanno rilevato questo screenshot."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Aggiungi alla nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Includi link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Registrazione dello schermo"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Elaborazione registrazione…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniziare a registrare?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Quando registri, Android ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Quando registri un\'app, Android ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Avvia registrazione"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Registrare lo schermo?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Registra un\'app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Registra l\'intero schermo"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando registri l\'intero schermo, tutto ciò che viene mostrato sullo schermo viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando registri un\'app, tutto ciò che viene mostrato o riprodotto al suo interno viene registrato. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Registra lo schermo"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Scegli l\'app da registrare"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Registra audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio del dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Suoni del dispositivo, come musica, chiamate e suonerie"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Invia"</string>
     <string name="cancel" msgid="1089011503403416730">"Annulla"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo dell\'app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Conferma"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Riprova"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tocca per annullare l\'autenticazione"</string>
@@ -227,7 +229,7 @@
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Configura lo Sblocco con il Volto"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Per riconfigurare lo Sblocco con il Volto, l\'attuale modello del volto verrà eliminato.\n\nDovrai riconfigurare questa funzionalità per usare il volto per sbloccare lo smartphone."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Impossibile configurare lo Sblocco con il Volto. Vai alle Impostazioni e riprova."</string>
-    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Tocca il sensore di impronte"</string>
+    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Tocca il sensore di impronte digitali"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Premi l\'icona Sblocca per continuare"</string>
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"Volto non riconosciuto. Usa l\'impronta."</string>
     <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -250,9 +252,7 @@
     <string name="accessibility_vpn_on" msgid="8037549696057288731">"VPN attiva."</string>
     <string name="accessibility_battery_level" msgid="5143715405241138822">"Batteria: <xliff:g id="NUMBER">%d</xliff:g> percento."</string>
     <string name="accessibility_battery_level_with_estimate" msgid="6548654589315074529">"Batteria rimanente: <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for accessibility_battery_level_charging (8892191177774027364) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Batteria in carica, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%."</string>
     <string name="accessibility_battery_level_charging_paused" msgid="3560711496775146763">"Batteria a <xliff:g id="PERCENTAGE">%d</xliff:g> per cento; ricarica in pausa per proteggere la batteria."</string>
     <string name="accessibility_battery_level_charging_paused_with_estimate" msgid="2223541217743647858">"Batteria a <xliff:g id="PERCENTAGE">%1$d</xliff:g> per cento, <xliff:g id="TIME">%2$s</xliff:g>; ricarica in pausa per proteggere la batteria."</string>
     <string name="accessibility_overflow_action" msgid="8555835828182509104">"Visualizza tutte le notifiche"</string>
@@ -292,15 +292,17 @@
     <string name="start_dreams" msgid="9131802557946276718">"Salvaschermo"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Non disturbare"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalità priorità"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nessun dispositivo accoppiato disponibile"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tocca per connettere o disconnettere un dispositivo"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Accoppia nuovo dispositivo"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Visualizza tutti"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa Bluetooth"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa il Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Condivisione audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tocca per cambiare o condividere l\'audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string>
@@ -309,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string>
     <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_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>
@@ -384,14 +387,14 @@
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza con il dispositivo?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Registrazione schermo"</string>
     <string name="performance" msgid="6552785217174378320">"Prestazioni"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfaccia utente"</string>
     <string name="thermal" msgid="6758074791325414831">"Termico"</string>
-    <string name="custom" msgid="3337456985275158299">"Personalizzate"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni monitoraggio personalizzate"</string>
+    <string name="custom" msgid="3337456985275158299">"Personalizzata"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Impostazioni di traccia personalizzate"</string>
     <string name="restore_default" msgid="5259420807486239755">"Ripristina predefinite"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string>
@@ -402,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Sottotitoli in tempo reale"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sottotitoli in tempo reale"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string>
@@ -431,10 +434,12 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Apri Impostazioni"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Altro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Attiva/disattiva la panoramica"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalità priorità"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Fine"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Impostazioni"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Configura"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestisci nelle impostazioni"</string>
@@ -477,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget su schermata di blocco"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> aggiunto alla schermata di blocco"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Scorri a sinistra per iniziare il tutorial della community"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizza"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Chiudi"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Aggiungi, rimuovi e riordina i tuoi widget in questo spazio"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Aggiungi, rimuovi e riordina i widget in questo spazio"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Aggiungi altri widget"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Premi a lungo per personalizzare i widget"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Personalizza widget"</string>
@@ -530,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vuoi avviare la registrazione o la trasmissione?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Il servizio che offre questa funzione avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Schermo intero"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Una sola app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Condividi o registra un\'app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vuoi avviare la registrazione o la trasmissione con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando condividi, registri o trasmetti, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando condividi, registri o trasmetti un\'app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Inizia"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Condividi o registra un\'app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Condividere lo schermo con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Condividi un\'app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Condividi schermo intero"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando condividi lo schermo intero, tutto ciò che è nella schermata è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando condividi un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Condividi schermo"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha disattivato questa opzione"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Iniziare a trasmettere?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando trasmetti, Android ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando trasmetti un\'app, Android ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Inizia a trasmettere"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Scegli l\'app da condividere"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Trasmettere lo schermo?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Trasmetti un\'app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Trasmetti schermo intero"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando trasmetti lo schermo intero, tutto ciò che è nella schermata è visibile. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando trasmetti un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Trasmetti schermo"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Scegli l\'app da trasmettere"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Iniziare a condividere?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quando condividi, registri o trasmetti, Android ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quando condividi, registri o trasmetti un\'app, Android ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Inizia"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Avanti"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"La condivisione viene messa in pausa quando cambi app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Condividi invece questa app"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Torna indietro"</string>
@@ -713,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitare, connessione buona"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitare, connessione disponibile"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satellitare"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chiamate di emergenza o SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profilo di lavoro"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Il divertimento riservato a pochi eletti"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"L\'Ottimizzatore UI di sistema mette a disposizione altri metodi per modificare e personalizzare l\'interfaccia utente di Android. Queste funzioni sperimentali potrebbero cambiare, interrompersi o scomparire nelle versioni successive. Procedi con cautela."</string>
@@ -1207,7 +1221,7 @@
     <string name="video_status" msgid="4548544654316843225">"Visione in corso"</string>
     <string name="audio_status" msgid="4237055636967709208">"Ascolto in corso"</string>
     <string name="game_status" msgid="1340694320630973259">"Gioco in corso"</string>
-    <string name="empty_user_name" msgid="3389155775773578300">"Persone amiche"</string>
+    <string name="empty_user_name" msgid="3389155775773578300">"Amico"</string>
     <string name="empty_status" msgid="5938893404951307749">"Chattiamo stasera."</string>
     <string name="status_before_loading" msgid="1500477307859631381">"I contenuti verranno mostrati a breve"</string>
     <string name="missed_call" msgid="4228016077700161689">"Chiamata persa"</string>
@@ -1375,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibilità"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nessun risultato di ricerca"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto Indietro"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto Home"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tasto azione"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fine"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Ottimo lavoro"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Indietro"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad che mostra tre dita che si muovono verso destra e sinistra"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Schermata del dispositivo che mostra l\'animazione del gesto per tornare indietro"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Per tornare indietro, scorri verso sinistra o verso destra utilizzando tre dita in un punto qualsiasi del touchpad.\n\nPuoi usare anche la scorciatoia da tastiera Action + Esc per farlo."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Ottimo lavoro."</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Per andare alla schermata Home, scorri verso l\'alto con tre dita dalla parte inferiore dello schermo."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bene!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Hai completato il gesto Vai alla schermata Home."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasto azione"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Per accedere alle tue app, premi il tasto azione sulla tastiera."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Complimenti!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Hai completato il gesto del tasto azione.\n\nAzione + / mostra tutte le scorciatoie disponibili."</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>
@@ -1399,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Per andare alla schermata Home, scorri verso l\'alto con tre dita sul touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Per visualizzare le app recenti, scorri verso l\'alto e tieni premuto con tre dita sul touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Per visualizzare tutte le tue app, premi il tasto azione sulla tastiera"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Oscurata"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Sblocca per visualizzare"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Usa il touchpad per tornare indietro"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Scorri verso sinistra o destra con tre dita. Tocca per scoprire altri gesti."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Usa il touchpad per andare alla schermata Home"</string>
@@ -1407,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Scorri verso l\'alto e tieni premuto con tre dita. Tocca per scoprire altri gesti."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Usa la tastiera per visualizzare tutte le app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Premi il tasto azione in qualsiasi momento. Tocca per scoprire altri gesti."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ora l\'attenuazione extra è nella barra della luminosità"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Ora puoi usare l\'attenuazione extra per lo schermo abbassando il livello di luminosità ancora di più dalla parte superiore della schermata.\n\nQuesta funzionalità opera in modo ottimale quando ti trovi in un ambiente buio."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Rimuovi scorciatoia attenuazione extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Scorciatoia attenuazione extra rimossa. Per diminuire la luminosità, usa la normale barra della luminosità."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 698d2ad..1161fa0 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> ואפליקציות פתוחות נוספות זיהו את צילום המסך הזה."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"הוספה לפתק"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"הכנסת הקישור"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"מקליט המסך"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"מתבצע עיבוד של הקלטת מסך"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"להתחיל את ההקלטה?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"‏בזמן ההקלטה, תהיה ל-Android גישה לכל מה שמופיע במסך שלך או מופעל במכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"‏בזמן הקלטה של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"התחלת ההקלטה"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"להקליט את המסך?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"הקלטה של אפליקציה אחת"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"הקלטה של כל המסך"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"כשמקליטים את כל המסך, כל מה שמופיע במסך מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"כשמקליטים אפליקציה, כל מה שרואים או מפעילים בה מוקלט. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"הקלטת המסך"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"בחירת אפליקציה להקלטה"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"הקלטת אודיו"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"אודיו מהמכשיר"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"צלילים מהמכשיר, כמו מוזיקה, שיחות ורינגטונים"</string>
@@ -147,12 +151,12 @@
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"‏מתבצעת כרגע פעולת Cast למכשיר בקרבת מקום"</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"‏הפסקת ה-Cast"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"סגירה"</string>
-    <string name="issuerecord_title" msgid="286627115110121849">"בעיה במכשיר ההקלטה"</string>
+    <string name="issuerecord_title" msgid="286627115110121849">"תיעוד של בעיה"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"מתבצע עיבוד של בעיית ההקלטה"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"התראה מתמשכת לסשן איסוף הבעיה"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"בעיית הקלטה"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"הבעיה בתהליך הקלטה"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"שיתוף"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"בעיית ההקלטה נשמרה"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"הקלטת הבעיה נשמרה"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"אפשר להקיש כדי להציג"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"שגיאה בשמירה של בעיית ההקלטה"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"שגיאה בהפעלה של בעיית ההקלטה"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"שליחה"</string>
     <string name="cancel" msgid="1089011503403416730">"ביטול"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"לוגו של האפליקציה"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"אישור"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ניסיון נוסף"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"יש להקיש כדי לבטל את האימות"</string>
@@ -217,17 +219,17 @@
     <string name="biometric_re_enroll_dialog_cancel" msgid="93760939407091417">"לא עכשיו"</string>
     <string name="biometric_re_enroll_notification_content" msgid="8685925877186288180">"הפעולה הזו נדרשת כדי לשפר את האבטחה והביצועים"</string>
     <string name="fingerprint_re_enroll_notification_title" msgid="4539432429683916604">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
-    <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"ביטול הנעילה בטביעת אצבע"</string>
+    <string name="fingerprint_re_enroll_notification_name" msgid="630798657797645704">"פתיחה בטביעת אצבע"</string>
     <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"הגדרת \'ביטול הנעילה בטביעת אצבע\'"</string>
     <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"כדי להגדיר שוב את התכונה \'ביטול הנעילה בטביעת אצבע\', עליך למחוק את התבניות והמודלים הנוכחיים של טביעת האצבע.\n\nאחרי המחיקה יהיה צורך להגדיר שוב את \'ביטול הנעילה בטביעת אצבע\' כדי להשתמש בטביעת האצבע לביטול הנעילה של הטלפון ולאמת את זהותך."</string>
     <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"כדי להגדיר שוב את התכונה \'ביטול הנעילה בטביעת אצבע\', עליך למחוק את התבניות והמודל הנוכחיים של טביעת האצבע.\n\nאחרי המחיקה יהיה צורך להגדיר שוב את \'ביטול הנעילה בטביעת אצבע\' כדי להשתמש בטביעת האצבע לביטול הנעילה של הטלפון ולאמת את זהותך."</string>
     <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"לא ניתן להגדיר ביטול נעילה בטביעת אצבע. יש לעבור להגדרות כדי לנסות שוב."</string>
     <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"הגדרה חוזרת של \'פתיחה ע\"י זיהוי הפנים\'"</string>
-    <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"פתיחה ע\"י זיהוי הפנים"</string>
+    <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"פתיחה בזיהוי פנים"</string>
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"להגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"כדי להגדיר שוב את התכונה \'פתיחה ע\"י זיהוי הפנים\', עליך למחוק את התבנית הנוכחית לזיהוי הפנים.\n\nיהיה צורך להגדיר את התכונה הזו שוב כדי להשתמש בזיהוי הפנים לביטול הנעילה של הטלפון."</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"לא ניתן להגדיר פתיחה ע\"י זיהוי הפנים. צריך לעבור להגדרות כדי לנסות שוב."</string>
-    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"יש לגעת בחיישן טביעות האצבע"</string>
+    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"צריך לגעת בחיישן טביעות האצבע"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"להמשך יש ללחוץ על סמל ביטול הנעילה"</string>
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"הפנים לא זוהו. צריך להשתמש בטביעת אצבע במקום."</string>
     <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"שומר מסך"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"אתרנט"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"נא לא להפריע"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"מצבי עדיפות"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"אין מכשירים מותאמים זמינים"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"אפשר להקיש כדי להתחבר למכשיר או להתנתק ממנו"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"‏שימוש ב-Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"שיתוף אודיו"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"צריך להקיש כדי להחליף מצב או לשתף אודיו"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"‏חיבור ה-Bluetooth יופעל מחר בבוקר"</string>
     <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_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>
@@ -378,8 +383,8 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"הקלטת המסך"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"התחלה"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"עצירה"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד הבעיה"</string>
-    <string name="qs_record_issue_start" msgid="2979831312582567056">"התחלה"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד בעיה"</string>
+    <string name="qs_record_issue_start" msgid="2979831312582567056">"קדימה"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"עצירה"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"דיווח על באג"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"איזה חלק בחוויית השימוש שלך במכשיר הושפע?"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"כתוביות מיידיות"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"כתוביות מיידיות"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"פתיחת ההגדרות"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"מכשיר אחר"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"החלפת מצב של מסכים אחרונים"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"מצבי עדיפות"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"סיום"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"הגדרות"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"הגדרה"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"התאמה אישית"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה איטית • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • בטעינה • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> עד לסיום"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ווידג\'טים במסך הנעילה"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g> נוסף למסך הנעילה"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"אפשר להחליק שמאלה כדי להפעיל את המדריך המשותף"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"התאמה אישית"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"סגירה"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"הוספת ווידג\'ט"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"סיום"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"הוספת ווידג\'טים"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"קבלת גישה מהירה לווידג\'טים של האפליקציות המועדפות עליך בלי לבטל את נעילת הטאבלט."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"קבלת גישה מהירה לווידג\'טים של אפליקציות בלי לבטל את נעילת הטאבלט."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"לאפשר להציג כל ווידג\'ט במסך הנעילה?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"לפתיחת ההגדרות"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"להפעיל את האפליקציות לעבודה?"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"‏לאפליקציית <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל המידע הגלוי במסך שלך ולכל תוכן שמופעל במכשיר שלך בזמן הקלטה או הפעלת Cast. המידע הזה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"‏להתחיל הקלטה או הפעלת Cast?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"‏לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או הפעלת Cast – כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"כל המסך"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"אפליקציה אחת"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"שיתוף או הקלטה של אפליקציה"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"‏להתחיל להקליט או להפעיל Cast דרך <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"‏בזמן שיתוף, הקלטה או הפעלת Cast תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"‏בזמן שיתוף, הקלטה או הפעלת Cast של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"התחלה"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"שיתוף או הקלטה של אפליקציה"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"לשתף את המסך שלך עם <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"שיתוף של אפליקציה אחת"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"שיתוף כל המסך"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"כשמשתפים את כל המסך, כל מה שמופיע בו יהיה גלוי ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"כשמשתפים אפליקציה, כל מה שרואים או מפעילים בה יהיה גלוי ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"שיתוף המסך"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"‏להפעיל Cast?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‏בזמן הפעלת Cast, תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏בזמן Cast מאפליקציה, תהיה ל-Android גישה לכל מה שמופיע באפליקציה ולכל מדיה שפועלת בה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"‏הפעלת Cast"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"בחירת האפליקציה לשיתוף"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"‏להפעיל Cast של המסך?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"‏הפעלת Cast של אפליקציה אחת"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"‏הפעלת Cast של כל המסך"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"‏כשמפעילים Cast של כל המסך, כל מה שמופיע בו יהיה גלוי לצופים. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"‏כשמפעילים Cast של כל אפליקציה, כל מה שמופיע או מופעל בה יהיה גלוי לצופים. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"‏הפעלת Cast של המסך"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"‏בחירת אפליקציה להפעלת Cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"להתחיל את השיתוף?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‏בזמן שיתוף, הקלטה או הפעלת Cast תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‏בזמן שיתוף, הקלטה או הפעלת Cast של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"התחלה"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"הבא"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"השיתוף מושהה כשמחליפים אפליקציות"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"שיתוף של האפליקציה הזו במקום"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"חזרה למשימה הקודמת"</string>
@@ -592,15 +604,15 @@
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"רישום התנועה ברשת"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"‏אישורי CA"</string>
-    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"הצגת מדיניות"</string>
+    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"צפייה במדיניות"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"לצפייה באמצעי בקרת ההורים"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"‏המכשיר הזה שייך לארגון <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"ל-<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> תהיה אפשרות לגשת לנתונים המשויכים למכשיר הזה, לנהל אפליקציות ולשנות את הגדרות המכשיר.\n\nאם יש לך שאלות, ניתן ליצור קשר עם <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"‏המכשיר הזה שייך לארגון שלך.\n\nמנהל ה-IT יכול לנטר ולנהל הגדרות, גישה ארגונית, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, יש לפנות למנהל ה-IT."</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"‏המכשיר הזה שייך לארגון שלך.\n\nהאדמין ב-IT יכול לנטר ולנהל הגדרות, הרשאות גישה לארגון, אפליקציות, נתונים המשויכים למכשיר ומידע על מיקום המכשיר.\n\nלמידע נוסף, אפשר לפנות לאדמין ב-IT."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"הארגון שלך התקין רשות אישורים במכשיר. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"הארגון שלך התקין רשות אישורים בפרופיל העבודה. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"במכשיר זה מותקנת רשות אישורים. ניתן לעקוב אחר התנועה ברשת המאובטחת או לשנות אותה."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"מנהל המערכת הפעיל את התכונה \'רישום התנועה ברשת\', שמנטרת את תנועת הנתונים במכשיר."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"האדמין הפעיל את התכונה \'רישום התנועה ברשת\', שמנטרת את תנועת הנתונים במכשיר."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"מנהל המערכת הפעיל את תכונת רישום התנועה ברשת, שמנטרת את תנועת הנתונים בפרופיל העבודה, אבל לא בפרופיל האישי."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"‏המכשיר הזה מחובר לאינטרנט דרך <xliff:g id="VPN_APP">%1$s</xliff:g>. הפעילויות שלך ברשת, כולל האימיילים ונתוני הגלישה, גלויות לספק ה-VPN."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"‏המכשיר הזה מחובר לאינטרנט דרך <xliff:g id="VPN_APP">%1$s</xliff:g>. הפעילויות שלך ברשת, כולל האימיילים ונתוני הגלישה, גלויות לאדמין ב-IT."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"לוויין, חיבור באיכות טובה"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"לוויין, יש חיבור זמין"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"תקשורת לוויינית למצב חירום"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"‏שיחות חירום או SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏התכונה System UI Tuner מספקת לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, לא לעבוד כראוי או להיעלם בגרסאות עתידיות. יש להמשיך בזהירות."</string>
@@ -990,7 +1003,7 @@
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- תהיה לה אפשרות לקרוא מידע מאפליקציית <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="slice_permission_text_2" msgid="6758906940360746983">"- תהיה לה יכולת לנקוט פעולה בתוך <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"יש לאשר לאפליקציית <xliff:g id="APP">%1$s</xliff:g> להראות חלקים מכל אפליציה שהיא"</string>
-    <string name="slice_permission_allow" msgid="6340449521277951123">"אני רוצה לאשר"</string>
+    <string name="slice_permission_allow" msgid="6340449521277951123">"אישור"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"אני לא מרשה"</string>
     <string name="auto_saver_title" msgid="6873691178754086596">"יש להקיש כדי לתזמן את מצב החיסכון בסוללה"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"מומלץ להפעיל את התכונה כשיש סבירות גבוהה שהסוללה תתרוקן"</string>
@@ -1368,7 +1381,7 @@
     <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"הגדרות המערכת"</string>
     <string name="shortcut_helper_category_system_apps" msgid="6001757545472556810">"אפליקציות מערכת"</string>
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"ריבוי משימות"</string>
-    <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"אפליקציות אחרונות"</string>
+    <string name="shortcutHelper_category_recent_apps" msgid="7918731953612377145">"אפליקציות שהיו בשימוש לאחרונה"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"מסך מפוצל"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"קלט"</string>
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"קיצורי דרך של אפליקציות"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"נגישות"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"מקשי קיצור"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"אין תוצאות חיפוש"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"תנועת חזרה"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"תנועת חזרה למסך הבית"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"מקש הפעולה"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"מעולה!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"לוח מגע שמראה שלוש אצבעות זזות ימינה ושמאלה"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"מסך מכשיר שמראה אנימציה לתנועה אחורה"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏כדי לחזור אחורה, מחליקים שמאלה או ימינה עם שלוש אצבעות בכל מקום על לוח המגע.\n\nאפשר לבצע את הפעולה הזו גם באמצעות קיצור הדרך לפעולה + מקש ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"מעולה!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"השלמת את התנועה \'הקודם\'."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר לדף הבית"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"כדי לעבור למסך הבית בכל שלב, צריך להחליק למעלה עם שלוש אצבעות מהחלק התחתון של המסך."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"איזה יופי!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"השלמת את תנועת המעבר למסך הבית."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"מקש הפעולה"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"כדי לגשת לאפליקציות, מקישים על מקש הפעולה במקלדת."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"כל הכבוד!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"השלמת את התנועה של מקש הפעולה.\n\nלחיצה על מקש הפעולה + מקש / מציגה את כל מקשי הקיצור הזמינים."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"כדי לעבור למסך הבית, מחליקים למעלה עם שלוש אצבעות על לוח המגע"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"כדי לראות את האפליקציות האחרונות, מחליקים למעלה עם שלוש אצבעות על לוח המגע ולוחצים לחיצה ארוכה"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"כדי לראות את כל האפליקציות, מקישים על מקש הפעולה במקלדת"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"מצונזר"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"צריך לבטל את הנעילה כדי לראות"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"איך להשתמש בלוח המגע כדי לחזור אחורה"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"מחליקים ימינה או שמאלה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"איך להשתמש בלוח המגע כדי לעבור למסך הבית"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"איך להשתמש במקלדת כדי לראות את כל האפליקציות"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"בכל שלב אפשר ללחוץ על מקש הפעולה. ניתן להקיש כדי לקבל מידע נוסף על התנועות."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"התכונה \'מעומעם במיוחד\' נוספה לסרגל הבהירות"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"עכשיו אפשר להפוך את המסך למעומעם במיוחד באמצעות הפחתה נוספת של רמת הבהירות דרך החלק העליון במסך.\n\nהפעולה הזו עובדת הכי טוב בסביבה חשוכה."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"הסרה של קיצור הדרך לתכונה \'מעומעם במיוחד\'"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"קיצור הדרך לתכונה \'מעומעם במיוחד\' הוסר. כדי להפחית את הבהירות, אפשר להשתמש בסרגל הבהירות הרגיל."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7e09b94..6436cdd 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> とその他の開いているアプリがこのスクリーンショットを検出しました。"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"メモに追加"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"リンクを含める"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"スクリーン レコーダー"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"画面の録画を処理しています"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"録画を開始しますか?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"録画中は、表示や再生される内容に Android がアクセスできます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"アプリの録画中は、そのアプリで表示または再生される内容に Android がアクセスできます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"録画を開始"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"画面全体を録画"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"画面全体を録画すると、画面に表示されるものがすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"アプリを録画すると、そのアプリで表示または再生される内容がすべて録画されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"画面を録画"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"録画するアプリを選択"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"録音"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"デバイスの音声"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"デバイスからの音(音楽、通話、着信音など)"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"送信"</string>
     <string name="cancel" msgid="1089011503403416730">"キャンセル"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"アプリのロゴ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"再試行"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"タップすると認証をキャンセルします"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"スクリーン セーバー"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"イーサネット"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"サイレント モード"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先モード"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ペア設定されたデバイスがありません"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"タップしてデバイスを接続または接続解除します"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音声の共有"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"タップして音声の切り替えや共有を行えます"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"保存済み"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"接続を解除"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"有効化"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"自動字幕起こし"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"自動字幕起こし"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string>
@@ -429,10 +434,12 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"設定を開く"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"その他のデバイス"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"概要を切り替え"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先モード"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完了"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"設定で管理"</string>
@@ -475,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • フル充電まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ロック画面のウィジェット"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ウィジェットをロック画面に追加しました"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"左にスワイプすると、コミュニティ チュートリアルが開始します"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"カスタマイズ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"閉じる"</string>
@@ -528,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> は、録画中またはキャスト中に画面上に表示または再生される情報のすべてにアクセスできるようになります。これには、パスワード、お支払いの詳細、写真、メッセージ、音声などが含まれます。"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"録画やキャストを開始しますか?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"この機能を提供するサービスは、録画中またはキャスト中に画面上に表示または再生される情報のすべてにアクセスできるようになります。これには、パスワード、お支払いの詳細、写真、メッセージ、音声などが含まれます。"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"画面全体"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"1 つのアプリ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"アプリの共有または録画"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> で録画やキャストを開始しますか?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"共有、録画、キャスト中は、画面に表示される内容やデバイスで再生される内容に <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"アプリの共有、録画、キャスト中は、そのアプリで表示または再生される内容に <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"アプリの共有または録画"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> と画面を共有しますか?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"1 個のアプリを共有"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"画面全体を共有"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"画面全体を共有すると、画面に表示される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"アプリを共有すると、そのアプリで表示または再生される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"画面を共有"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> がこのオプションを無効にしています"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"キャストを開始しますか?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"キャスト中は、画面に表示される内容やデバイスで再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"アプリのキャスト中は、そのアプリで表示または再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"キャストを開始"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"共有するアプリを選択"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"画面をキャストしますか?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 つのアプリをキャスト"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"画面全体をキャスト"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"画面全体をキャストすると、画面に表示される内容がすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"アプリをキャストすると、そのアプリで表示または再生される内容がすべて公開されます。パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"画面のキャスト"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"キャストするアプリを選択"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"共有を開始しますか?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"共有、録画、キャスト中は、画面に表示される内容やデバイスで再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"アプリの共有、録画、キャスト中は、そのアプリで表示または再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"開始"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"次へ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"アプリを切り替えるときに共有を一時停止します"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"代わりにこのアプリを共有する"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"切り替える"</string>
@@ -711,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛生、接続状態良好"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛生、接続利用可能"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"衛星 SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急通報または SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"仕事用プロファイル"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"一部の方のみお楽しみいただける限定公開ツール"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"システムUI調整ツールでは、Androidユーザーインターフェースの調整やカスタマイズを行えます。これらの試験運用機能は今後のリリースで変更となったり、中止となったり、削除されたりする可能性がありますのでご注意ください。"</string>
@@ -1308,7 +1324,7 @@
     <string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"アプリを選択"</string>
     <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"ショートカットの長押しが必要です"</string>
     <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"キャンセル"</string>
-    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"画面を切り替えましょう"</string>
+    <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"画面を切り替える"</string>
     <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"スマートフォンを開いてください"</string>
     <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"画面を切り替えますか?"</string>
     <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"高解像度で撮るには背面カメラを使用してください"</string>
@@ -1373,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ユーザー補助"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"検索結果がありません"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"「戻る」ジェスチャー"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"「ホーム」ジェスチャー"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"アクションキー"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完了"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"よくできました。"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"戻る"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"タッチパッドで 3 本の指を左右に動かしている様子"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"デバイスの画面で「戻る」ジェスチャーのアニメーションが表示されている様子"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"戻るには、3 本の指でタッチパッドを左右にスワイプします。\n\nキーボード ショートカットのアクション + ESC キーを使用して、この操作を行うこともできます。"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"お疲れさまでした。"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"「戻る」操作を学習しました。"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ホームに移動"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"3 本の指で画面を下から上にスワイプすると、ホーム画面にいつでも移動できます。"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"お疲れさまでした。"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"「ホームに移動」操作を学習しました。"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"アクションキー"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"アプリにアクセスするには、キーボードのアクションキーを押します。"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"お疲れさまでした。"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"アクションキー操作を学習しました。\n\nアクションキーと + キーを同時に押すと、利用可能なショートカットがすべて表示されます。"</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>
@@ -1397,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ホームに移動するには、3 本の指でタッチパッドを上にスワイプします"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"最近使ったアプリを表示するには、3 本の指でタッチパッドを上にスワイプして長押しします"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"すべてのアプリを表示するには、キーボードのアクションキーを押してください"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"削除済み"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"表示するにはロックを解除してください"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"タッチパッドを使用して、前の画面に戻る"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"3 本の指で左または右にスワイプします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"タッチパッドを使用して、ホームに移動する"</string>
@@ -1405,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"3 本の指で上にスワイプして長押しします。ジェスチャーの詳細を確認するにはタップしてください。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"キーボードを使用して、すべてのアプリを表示する"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"アクションキーを押せばいつでも機能します。ジェスチャーの詳細を確認するにはタップしてください。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"「さらに輝度を下げる」機能が明るさのバーの追加されました"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"画面の上部で明るさを大幅に低く設定することで、画面の輝度をさらに下げられるようになりました。\n\nこの設定は暗い場所での操作に最適です。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"「さらに輝度を下げる」のショートカットを削除する"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"「さらに輝度を下げる」のショートカットを削除しました。明るさを下げるには、通常の明るさのバーを使用してください。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 4d0e90c..7d52760 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>-მა და სხვა გახსნილმა აპებმა აღმოაჩინეს ეკრანის ეს ანაბეჭდი."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"დაამატეთ შენიშვნა"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ბმულის დართვა"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ეკრანის ჩამწერი"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ეკრანის ჩანაწერი მუშავდება"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"დაიწყოს ჩაწერა?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"სანამ აპის ჩაწერას ახორციელებთ, Android-ს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენი მოწყობილობის მეშვეობით. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"სანამ აპს იწერთ, Android-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან რასაც უკრავთ აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ჩაწერის დაწყება"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"გსურთ თქვენი ეკრანის ჩაწერა?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ერთი აპის ჩაწერა"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"მთლიანი ეკრანის ჩაწერა"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"მთლიანი ეკრანის ჩაწერისას ჩაიწერება ყველაფერი, რაც თქვენს ეკრანზე გამოჩნდება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"აპის ჩაწერისას ჩაიწერება ყველაფერი, რაც ამ აპში გამოჩნდება ან დაიკვრება. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ეკრანის ჩაწერა"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ჩაწერისთვის აპის არჩევა"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"აუდიოს ჩაწერა"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"მოწყობილობის აუდიო"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ხმა თქვენი მოწყობილობიდან, როგორიც არის მუსიკა, საუბარი და ზარები"</string>
@@ -132,7 +136,7 @@
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"ჩაწერის შეწყვეტა"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"მიმდინარეობს ეკრანის გაზიარება"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"გსურთ ეკრანის გაზიარების შეწყვეტა?"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-ს"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>-ს"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"თქვენ ამჟამად უზიარებთ თქვენს მთლიან ეკრანს აპს"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"თქვენ ამჟამად აზიარებთ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>-ს"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"თქვენ ამჟამად აზიარებთ აპს"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"გაგზავნა"</string>
     <string name="cancel" msgid="1089011503403416730">"გაუქმება"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"აპის ლოგო"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"დადასტურება"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ხელახლა ცდა"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"შეეხეთ ავტორიზაციის გასაუქმებლად"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ეკრანმზოგი"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ეთერნეტი"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"არ შემაწუხოთ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"პრიორიტეტული რეჟიმები"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"დაწყვილებული მოწყობილობები მიუწვდომელია"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"შეეხეთ მოწყობილობის დასაკავშირებლად ან გასათიშად"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"აუდიოს გაზიარება"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"შეეხეთ აუდიოს გადასართავად ან გასაზიარებლად"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"შენახული"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"კავშირის გაწყვეტა"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"გააქტიურება"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ავტოსუბტიტრები"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"პარამეტრების გახსნა"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"სხვა მოწყობილობა"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"მიმოხილვის გადართვა"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"პრიორიტეტული რეჟიმები"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"მზადაა"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"პარამეტრები"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"დაყენება"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"მაღვიძარების გარდა, არავითარი ხმა და ვიბრაცია არ შეგაწუხებთ. თქვენ მაინც შეძლებთ სასურველი კონტენტის, მაგალითად, მუსიკის, ვიდეოებისა და თამაშების აუდიოს მოსმენა."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"მორგება"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ნელა იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • იტენება • სრულ დატენვამდე <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ვიჯეტები ჩაკეტილ ეკრანზე"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტი დაემატა ჩაკეტილ ეკრანს"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"გადაფურცლეთ მარცხნივ, რათა დაიწყოთ საერთო სახელმძღვანელო"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"მორგება"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"უარყოფა"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს ექნება წვდომა ყველა ინფორმაციაზე, რომელიც თქვენს ეკრანზე გამოჩნდება ან თქვენს მოწყობილობაზე დაიკვრება ჩაწერის ან ტრანსლირების განმავლობაში. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიც არის პაროლები, გადახდის დეტალები, ფოტოები, შეტყობინებები და თქვენ მიერ დაკრული აუდიო."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"დაიწყოს ჩაწერა ან ტრანსლირება?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ამ ფუნქციის მომწოდებელ სერვისს ექნება წვდომა ყველა ინფორმაციაზე, რომელიც თქვენს ეკრანზე გამოჩნდება ან თქვენს მოწყობილობაზე დაიკვრება ჩაწერის ან ტრანსლირების განმავლობაში. აღნიშნული მოიცავს ისეთ ინფორმაციას, როგორიც არის პაროლები, გადახდის დეტალები, ფოტოები, შეტყობინებები და თქვენ მიერ დაკრული აუდიო."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"მთელი ეკრანი"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ერთი აპი"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"გააზიარეთ ან ჩაწერეთ აპი"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"დაიწყოს ჩაწერა ან ტრანსლირება <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ით?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"გაზიარების, ჩაწერის ან ტრანსლირების დროს, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან იკვრება აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"დაწყება"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"აპის გაზიარება ან ჩაწერა"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"გსურთ თქვენი ეკრანის <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-თან გაზიარება?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ერთი აპის გაზიარება"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"მთლიანი ეკრანის გაზიარება"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"მთლიანი ეკრანის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც თქვენს ეკრანზეა. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"აპის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც ჩანს ან უკრავს ამ აპში. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ეკრანის გაზიარება"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-მა გათიშა ეს ვარიანტი"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"გსურთ ტრანსლირების დაწყება?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"როდესაც თქვენ ტრანსლირებთ, ამ აპს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"როდესაც აპს ტრანსლირებთ, Android-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან იკვრება აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ტრანსლირების დაწყება"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"გაზიარებისთვის აპის არჩევა"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"გსურთ თქვენი ეკრანის ტრანსლირება?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ერთი აპის ტრანსლირება"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"მთლიანი ეკრანის ტრანსლირება"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"თქვენი მთლიანი ეკრანის ტრანსლირების დროს, რაც თქვენს ეკრანზეა ყველაფერი ჩანს. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"აპის ტრანსლირების დროს ყველაფერი ჩანს, რაც იკვრება ან გამოსახულია აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ეკრანის ტრანსლირება"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ტრანსლირებისთვის აპის არჩევა"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"გსურთ გაზიარების დაწყება?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"გაზიარებისას, ჩაწერისას ან ტრანსლირებისას, Android-ს წვდომა აქვს ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას, Android-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან იკვრება აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"დაწყება"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"შემდეგი"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"აპების გადართვისას გაზიარება პაუზდება"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"სანაცვლოდ ამ აპის გაზიარება"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"უკან გადართვა"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"კარგი სატელიტური კავშირი"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ხელმისაწვდომია სატელიტური კავშირი"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"სატელიტური SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"გადაუდებელი ზარი ან SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"სამსახურის პროფილი"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ზოგისთვის გასართობია, მაგრამ არა ყველასთვის"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"სისტემის UI ტუნერი გაძლევთ დამატებით გზებს Android-ის სამომხმარებლო ინტერფეისის პარამეტრების დაყენებისთვის. ეს ექსპერიმენტული მახასიათებლები შეიძლება შეიცვალოს, შეწყდეს ან გაქრეს მომავალ ვერსიებში. სიფრთხილით გააგრძელეთ."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"მისაწვდომობა"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"კლავიატურის მალსახმობები"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ძიების შედეგები არ არის"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"უკან დაბრუნების ჟესტი"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"მთავარ ეკრანზე გადასვლის ჟესტი"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"მოქმედების კლავიში"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"მზადაა"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"შესანიშნავია!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"უკან დაბრუნება"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"სენსორული პანელი, რომელიც აჩვენებს მარჯვენა და მარცხენა მიმართულებით მოძრავ სამ თითს"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"მოწყობილობის ეკრანი, რომელიც აჩვენებს უკან დაბრუნების ჟესტის ანიმაციას"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"უკან დასაბრუნებლად სენსორულ პანელზე გადაფურცლეთ მარცხნივ ან მარჯვნივ სამი თითის გამოყენებით ნებისმიერ ადგილას.\n\nამისთვის თქვენ ასევე შეგიძლიათ გამოიყენოთ კლავიატურის მალსახმობის მოქმედება + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"შესანიშნავია!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"თქვენ შეასრულეთ უკან დაბრუნების ჟესტი."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"მთავარზე გადასვლა"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"თქვენს მთავარ ეკრანზე ნებისმიერ დროს გადასასვლელად გადაფურცლეთ ეკრანის ქვემოდან ზემოთ სამი თითით."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"მშვენიერია!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"თქვენ შეასრულეთ მთავარ ეკრანზე დაბრუნების ჟესტი."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"მოქმედების კლავიში"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"აპებზე წვდომისთვის დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"გილოცავთ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"თქვენ შეასრულეთ მოქმედების კლავიშის ჟესტი.\n\nქმედება + / აჩვენებს თქვენთვის ხელმისაწვდომ ყველა მალსახმობს."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"მთავარ გვერდზე გადასასვლელად სენსორულ პანელზე გადაფურცლეთ ზემოთ სამი თითით"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ბოლო აპების სანახავად სენსორულ პანელზე სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ყველა აპის სანახავად დააჭირეთ მოქმედების კლავიშს თქვენს კლავიატურაზე"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ტექსტს ადევს ცენზურა"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"განბლოკვა სანახავად"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"უკან დასაბრუნებლად გამოიყენეთ სენსორული პანელი"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"გადაფურცლეთ მარცხნივ ან მარჯვნივ სამი თითით. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"მთავარ გვერდზე გადასასვლელად გამოიყენეთ სენსორული პანელი"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"სამი თითით გადაფურცლეთ ზემოთ და მოიცადეთ. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ყველა აპის სანახავად გამოიყენეთ თქვენი კლავიატურა"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ნებისმიერ დროს დააჭირეთ მოქმედების კლავიშს. შეეხეთ მეტი ჟესტის შესასწავლად."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"დამატებითი დაბინდვის ფუქნცია ახლა განთავსებულია სიკაშკაშის პანელზე"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ახლა თქვენ შეგიძლიათ დამატებით დაბინდოთ ეკრანი მის ზედა ნაწილში სიკაშკაშის დონის კიდევ უფრო შემცირების გზით.\n\nეს ყველაზე უკეთ ბნელ გარემოში ყოფნისას მუშაობს."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"დამატებითი დაბინდვის მალსახმობის ამოშლა"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"დამატებითი დაბინდვის მალსახმობი ამოშლილია. სიკაშკაშის შესამცირებლად გამოიყენეთ სიკაშკაშის ჩვეულებრივი პანელი."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 9d84c73..7b32db1 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> және басқа да ашық қолданбалар осы скриншотты анықтады."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ескертпеге қосу"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Сілтеме қосу"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Экран жазғыш"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экран жазғыш бейнесін өңдеу"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Жазу басталсын ба?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Жазу кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Қолданбаны жазу кезінде Android жүйесі қолданбада көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Жазуды бастау"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Қолданба экранын жазасыз ба?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бір қолданба экранын жазу"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүкіл экранды жазу"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүкіл экранды жазған кезде, онда көрінетін барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Қолданбаны жазған кезде, онда көрінетін не ойнатылатын барлық нәрсе жазылады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жазу"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Жазатын қолданба экранын таңдау"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио жазу"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Құрылғыдан шығатын дыбыс"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Музыка, қоңыраулар және рингтондар сияқты құрылғыдан шығатын дыбыс"</string>
@@ -147,12 +151,12 @@
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Қазір маңайдағы құрылғыға трансляциялап жатырсыз."</string>
     <string name="cast_to_other_device_stop_dialog_button" msgid="6420183747435521834">"Трансляцияны тоқтату"</string>
     <string name="close_dialog_button" msgid="4749497706540104133">"Жабу"</string>
-    <string name="issuerecord_title" msgid="286627115110121849">"Мәселені жазу құралы"</string>
+    <string name="issuerecord_title" msgid="286627115110121849">"Ақау жазу құралы"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Мәселе жазбасы өңделіп жатыр"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Мәселе туралы дерек жинау сеансына арналған ағымдағы хабарландыру"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Мәселе жазылып жатыр"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ақау жазылып жатыр"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлісу"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"Мәселе жазбасы сақталды"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"Ақау жазбасы сақталды."</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Көру үшін түртіңіз."</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Мәселе жазбасын сақтау кезінде қате шықты."</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Мәселені жазуды бастау кезінде қате шықты."</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жіберу"</string>
     <string name="cancel" msgid="1089011503403416730">"Бас тарту"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Қолданба логотипі"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Растау"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Қайта көру"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Аутентификациядан бас тарту үшін түртіңіз."</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Скринсейвер"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Мазаламау"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Басымдық режимдері"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жұптасқан құрылғылар жоқ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Құрылғыны жалғау не ажырату үшін түртіңіз."</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Аудио бөлісу"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудионы бөлісу немесе ауыстыру үшін түртіңіз."</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сақталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажырату"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"іске қосу"</string>
@@ -306,7 +310,8 @@
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string>
     <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_sharing" msgid="3069309588231072128">"Аудио беріліп жатыр"</string>
+    <string name="quick_settings_bluetooth_audio_sharing_button_accessibility" msgid="7604615019302091708">"аудио бөлісу параметрлерін енгізу"</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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерді ашу"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Басқа құрылғы"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Шолуды қосу/өшіру"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Басымдық режимдері"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Дайын"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Реттеу"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Дабылдардан басқа ешқандай дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойындар сияқты ойнатылатын контентті ести алатын боласыз."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Реттеу"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Баяу зарядталуда • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядталып жатыр. • Толуына <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> қалды."</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Құлыптаулы экрандағы виджеттер"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"\"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" виджеті құлып экранына қосылды."</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ортақ оқулықты ашу үшін солға қарай сырғытыңыз."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Бейімдеу"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Жабу"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Осы бөлмеде виджеттер қосыңыз, оларды өшіріңіз және ретін өзгертіңіз."</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Осында виджеттерді қосып, оларды реттеуге болады."</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Басқа виджеттер қосыңыз."</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Виджеттерді бейімдеу үшін ұзақ басып тұрыңыз."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Виджеттерді реттеу"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Жазу немесе трансляциялау басталсын ба?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Осы функцияны ұсынатын қызмет экранда көрсетілетін немесе жазу не трансляциялау кезінде құрылғыда ойнатылған барлық ақпаратты пайдалана алады. Бұған құпия сөздер, төлем туралы мәліметтер, суреттер, хабарлар және ойнатылатын аудио сияқты ақпарат кіреді."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Бүкіл экран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Жалғыз қолданба"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Қолданба экранын бөлісу не жазу"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> арқылы жазу немесе трансляциялау басталсын ба?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Бөлісу, жазу не трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Қолданба экранын бөлісу, жазу не трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> онда көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Бастау"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Қолданба экранын бөлісу не жазу"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Экранды <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасымен бөлісу керек пе?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Бір қолданбаны бөлісу"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Бүкіл экранды бөлісу"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүкіл экранды бөліскен кезде, ондағы барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Қолданбаны бөліскен кезде, онда көрінетін не ойнатылатын барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлісу"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы осы опцияны өшірді."</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Трансляциялау басталсын ба?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Трансляциялау кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Қолданба экранын трансляциялау кезінде Android қолданбада көрінетін не ойнатылатын барлық контентті пайдалана алады. Сондықтан құпия сөздер, төлем туралы мәлімет, хабарлар, фотосуреттер, дыбыстар мен бейнелер сияқты ақпаратқа сақ болыңыз."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Трансляциялауды бастау"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлісетін қолданба экранын таңдау"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды трансляциялау керек пе?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бір қолданба экранын трансляциялау"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүкіл экранды трансляциялау"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүкіл экранды трансляциялаған кезде экранда барлық нәрсе көрсетіледі. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Қолданба экранын трансляциялаған кезде қолданбадағы барлық контент көрсетіледі. Сондықтан құпия сөздерге, төлем туралы мәліметке, хабарларға, фотосуреттерге, аудиоконтент пен бейнелерге сақ болыңыз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Экранды трансляциялау"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Трансляциялайтын қолданба экранын таңдау"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Бөлісу басталсын ба?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Бөлісу, жазу не трансляциялау кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Қолданба экранын бөлісу, жазу не трансляциялау кезінде Android жүйесі онда көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Бастау"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Келесі"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Қолданба ауыстырғанда, бөлісу кідіртіледі."</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Орнына осы қолданбаны бөлісу"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Қайта ауысу"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Жерсерік, байланыс жақсы."</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Жерсерік, байланыс бар."</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Құтқару қызметіне қоңырау шалу немесе SOS сигналын жіберу"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Жұмыс профилі"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Кейбіреулерге қызық, бірақ барлығына емес"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Жүйелік пайдаланушылық интерфейс тюнері Android пайдаланушылық интерфейсін реттеудің қосымша жолдарын береді. Бұл эксперименттік мүмкіндіктер болашақ шығарылымдарда өзгеруі, бұзылуы немесе жоғалуы мүмкін. Сақтықпен жалғастырыңыз."</string>
@@ -1287,7 +1300,7 @@
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="230135078402003532">"Ескертпе жазу"</string>
     <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ескертпе жазу, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string>
-    <string name="audio_sharing_description" msgid="8849060142768870004">"Аудио бөлісу"</string>
+    <string name="audio_sharing_description" msgid="8849060142768870004">"Аудио беріліп жатыр"</string>
     <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string>
     <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Іздеу нәтижелері жоқ."</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артқа қайтару қимылы"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Негізгі бетке қайтару қимылы"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Әрекет пернесі"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Жарайсыз!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Оңға және солға жылжитын үш саусақты көрсетіп тұрған сенсорлық тақта."</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Артқа қайту қимылын көрсетіп тұрған құрылғы экраны."</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Артқа қайту үшін сенсорлық тақтаның кез келген жерін үш саусақпен солға не оңға сырғытыңыз.\n\nСондай-ақ Action + ESC перне тіркесімін пайдалануға болады."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Жарайсыз!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Артқа қайту қимылын аяқтадыңыз."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Негізгі экранға өту"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Негізгі экранға кез келген уақытта өту үшін экранның төменгі жағынан жоғары қарай үш саусағыңызбен сырғытыңыз."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Жақсы нәтиже!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Негізгі экранға қайту қимылын аяқтадыңыз."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Әрекет пернесі"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Қолданбаларыңызға кіру үшін пернетақтадағы әрекет пернесін басыңыз."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Құттықтаймыз!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Әрекет пернесі қимылын аяқтадыңыз.\n\n\"+ /\" әрекеті сіз үшін қолжетімді барлық таңбашаны көрсетеді."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Сенсорлық сақтада үш саусақпен жоғары сырғытсаңыз, негізгі бетке өтесіз."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Сенсорлық тақтада үш саусақпен жоғары сырғытып, басып тұрсаңыз, соңғы ашылған қолданбаларды көресіз."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Пернетақтада әрекет пернесін басып, барлық қолданбаны көре аласыз."</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Жасырылған"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Көру үшін құлыпты ашыңыз."</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Артқа қайту үшін сенсорлық тақтаны қолданыңыз"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Үш саусақпен солға не оңға сырғытыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Негізгі бетке өту үшін сенсорлық тақтаны қолданыңыз"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үш саусақпен жоғары сырғытып, басып тұрыңыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Барлық қолданбаны көру үшін пернетақтаны қолданыңыз"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Әрекет пернесін кез келген уақытта баса аласыз. Басқа қимылдарды үйрену үшін түртіңіз."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Экранды қарайту функциясын енді жарықтық панелінің бөлшегі болады"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Енді экранның жоғарғы бөлігінде жарықтық деңгейін түсіру арқылы экранды одан сайын қарайтуға болады.\n\nБұл мүмкіндіктің артықшылығын қараңғы жерде көруге болады."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Экранды қарайту жылдам пәрменін өшіру"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Экранды қарайту жылдам пәрмені өшірілді. Жарықтықты азайту үшін әдеттегі жарықтық панелін пайдаланыңыз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index b84b6b6..81664ca 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> និងកម្មវិធីដែលបើក​ផ្សេងទៀតបានរកឃើញ​រូបថតអេក្រង់នេះ។"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"បញ្ចូលទៅក្នុងកំណត់ចំណាំ"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"រួមបញ្ចូល​តំណ"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"មុខងារថត​វីដេអូអេក្រង់"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"កំពុង​ដំណើរការ​ការថតអេក្រង់"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹង​ដែល​កំពុង​ដំណើរការ​សម្រាប់​រយៈពេលប្រើ​ការថត​សកម្មភាព​អេក្រង់"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ចាប់ផ្តើម​ថត​ឬ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"នៅពេលអ្នក​កំពុងថត, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើ​អេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"នៅពេលអ្នក​កំពុងថតកម្មវិធី, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ចាប់ផ្តើមថត"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ថត​អេក្រង់​របស់អ្នកឬ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ថត​កម្មវិធី​ទោល"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ថតអេក្រង់ទាំងមូល"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"នៅពេល​អ្នកកំពុង​ថតអេក្រង់​ទាំងមូល​របស់អ្នក អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ​នៅលើ​អេក្រង់​របស់អ្នក​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"នៅពេលអ្នក​កំពុង​ថតកម្មវិធី​ណាមួយ អ្វីគ្រប់យ៉ាង​ដែលបង្ហាញ ឬចាក់​នៅក្នុង​កម្មវិធីនោះ​ត្រូវបាន​ថត។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ថត​អេក្រង់"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ជ្រើសរើស​កម្មវិធី​ដើម្បីថត"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ថត​សំឡេង"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"សំឡេង​ឧបករណ៍"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"សំឡេង​ពី​ឧបករណ៍​របស់អ្នក​ដូចជា តន្ត្រី ការហៅទូរសព្ទ និងសំឡេងរោទ៍​ជាដើម"</string>
@@ -134,9 +138,9 @@
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"ឈប់បង្ហាញអេក្រង់ឬ?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈ <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នកតាមរយៈកម្មវិធីមួយ"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញ<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញ <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"បច្ចុប្បន្ន អ្នកកំពុងបង្ហាញកម្មវិធីមួយ"</string>
-    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ឈប់​ចែក​រំលែក"</string>
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"ឈប់​បង្ហាញ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"កំពុង​បញ្ជូន​អេក្រង់"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"ឈប់បញ្ជូនឬ?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"បច្ចុប្បន្ន អ្នកកំពុងបញ្ជូនអេក្រង់ទាំងមូលរបស់អ្នកទៅ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ផ្ញើ"</string>
     <string name="cancel" msgid="1089011503403416730">"បោះបង់"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"និមិត្តសញ្ញាកម្មវិធី"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"បញ្ជាក់"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ព្យាយាម​ម្ដង​ទៀត"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ចុចដើម្បីបោះបង់​ការផ្ទៀងផ្ទាត់"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ធាតុរក្សាអេក្រង់"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"អ៊ីសឺរណិត"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"កុំ​រំខាន"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"មុខងារអាទិភាព"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ប៊្លូធូស"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"មិន​មាន​ឧបករណ៍​ផ្គូផ្គង​ដែល​អាច​ប្រើ​បាន"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ចុចដើម្បីភ្ជាប់ ឬផ្ដាច់ឧបករណ៍"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ការស្ដាប់សំឡេងរួមគ្នា"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ចុចដើម្បីប្ដូរ ឬចែករំលែកសំឡេង"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"បាន​រក្សាទុក"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ផ្ដាច់"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"បើកដំណើរការ"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាច​ប្ដូរ​ការកំណត់ជាមុន​បានទេ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់​មីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់​កាមេរ៉ា​របស់ឧបករណ៍ឬ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់​កាមេរ៉ា និងមីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"បើកការកំណត់"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ឧបករណ៍ផ្សេងទៀត"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"បិទ/បើក​ទិដ្ឋភាពរួម"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"មុខងារអាទិភាព"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"រួចរាល់"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ការកំណត់"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"រៀបចំ"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"សំឡេង និងរំញ័រនឹងមិន​រំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ប្ដូរតាមបំណង"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្ម​យឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ធាតុ​ក្រាហ្វិកនៅលើអេក្រង់ចាក់សោ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"បានបញ្ចូល​ធាតុ​ក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ទៅ​អេក្រង់​ចាក់សោ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"អូសទៅឆ្វេង ដើម្បីចាប់ផ្ដើមមេរៀនសហគមន៍"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ប្ដូរតាមបំណង"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ច្រានចោល"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> នឹងមានសិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែលអាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬភ្ជាប់។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ចាប់ផ្ដើម​ថត ឬភ្ជាប់​មែនទេ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"សេវាកម្មដែលផ្ដល់មុខងារនេះ​នឹងមានសិទ្ធិ​ចូលប្រើ​ព័ត៌មាន​ទាំងអស់​ដែលអាច​មើលឃើញ​នៅលើ​អេក្រង់​របស់អ្នក ឬចាក់​ពីឧបករណ៍​របស់អ្នក នៅពេល​កំពុង​ថត ឬភ្ជាប់។ ព័ត៌មាន​នេះមាន​ដូចជា ពាក្យសម្ងាត់ ព័ត៌មាន​លម្អិត​អំពីការទូទាត់​ប្រាក់ រូបថត សារ និង​សំឡេង​ដែល​អ្នកចាក់​ជាដើម។"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"អេក្រង់ទាំងមូល"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"កម្មវិធីតែមួយ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ចែករំលែក ឬថតកម្មវិធី"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"ចាប់ផ្ដើម​ថត ឬភ្ជាប់​ដោយប្រើ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់កម្មវិធី, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ចាប់ផ្ដើម"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ចែករំលែក ឬថតកម្មវិធី"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"បង្ហាញអេក្រង់របស់អ្នកជាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ឬ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"បង្ហាញកម្មវិធី​មួយ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"បង្ហាញអេក្រង់​ទាំង​មូល"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"នៅពេលអ្នកបង្ហាញអេក្រង់ទាំងមូលរបស់អ្នក <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងនៅលើអេក្រង់របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"នៅពេលអ្នកបង្ហាញកម្មវិធីណាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"បង្ហាញ​អេក្រង់"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> បានបិទជម្រើសនេះ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ចាប់ផ្តើមភ្ជាប់ឬ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"នៅពេលអ្នក​កំពុងភ្ជាប់, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើ​អេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"នៅពេលអ្នក​កំពុងភ្ជាប់កម្មវិធី, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ចាប់ផ្តើមភ្ជាប់"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ជ្រើសរើស​កម្មវិធី​ដើម្បី​ចែករំលែក"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"បញ្ជូនអេក្រង់របស់អ្នកឬ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"បញ្ជូនកម្មវិធីមួយ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"បញ្ជូនអេក្រង់ទាំងមូល"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"នៅពេលអ្នកបញ្ជូនអេក្រង់ទាំងមូលរបស់អ្នក អ្នកផ្សេងមើលឃើញអ្វីគ្រប់យ៉ាងនៅលើអេក្រង់របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"នៅពេលអ្នកបញ្ជូនកម្មវិធីណាមួយ អ្នកផ្សេងមើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"បញ្ជូនអេក្រង់"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ជ្រើសរើស​កម្មវិធី​ដើម្បី​បញ្ជូន"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ចាប់ផ្ដើម​ចែករំលែកឬ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់កម្មវិធី, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ចាប់ផ្ដើម"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"បន្ទាប់"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ការចែករំលែក​ផ្អាក នៅពេលដែល​អ្នកប្ដូរ​កម្មវិធី"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ចែករំលែក​កម្មវិធីនេះ​ជំនួសវិញ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ប្ដូរទៅវិញ"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ផ្កាយរណប មានការតភ្ជាប់ល្អ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ផ្កាយរណប អាចតភ្ជាប់បាន"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ការប្រកាសអាសន្នតាមផ្កាយរណប"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់ ឬ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"កម្រងព័ត៌មានការងារ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ល្អសម្រាប់អ្នកប្រើមួយចំនួន តែមិនសម្រាប់គ្រប់គ្នាទេ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"កម្មវិធីសម្រួល UI ប្រព័ន្ធផ្តល់ជូនអ្នកនូវមធ្យោបាយបន្ថែមទៀតដើម្បីកែសម្រួល និងប្តូរចំណុចប្រទាក់អ្នកប្រើ Android តាមបំណង។ លក្ខណៈពិសេសសាកល្បងនេះអាចនឹងផ្លាស់ប្តូរ បំបែក ឬបាត់បង់បន្ទាប់ពីការចេញផ្សាយនាពេលអនាគត។ សូមបន្តដោយប្រុងប្រយ័ត្ន។"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ភាពងាយស្រួល"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ផ្លូវកាត់​ក្ដារ​ចុច"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវ​កាត់ការស្វែងរក"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"គ្មាន​លទ្ធផល​ស្វែងរក​ទេ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ចលនាថយក្រោយ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ចលនាទៅទំព័រដើម"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"គ្រាប់ចុចសកម្មភាព"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ធ្វើបានល្អ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយ​ក្រោយ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ផ្ទាំងប៉ះដែលបង្ហាញម្រាមដៃបីដែលផ្លាស់ទីទៅស្ដាំ និងឆ្វេង"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"អេក្រង់ឧបករណ៍ដែលបង្ហាញរូបមានចលនាសម្រាប់ចលនាថយក្រោយ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ដើម្បីថយក្រោយ សូមអូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​​ម្រាមដៃបីនៅត្រង់ណាក៏បានលើផ្ទាំងប៉ះ។\n\nអ្នកក៏អាចប្រើសកម្មភាពផ្លូវកាត់ក្ដារចុច + ESC សម្រាប់ការធ្វើបែបនេះ។"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ធ្វើបានល្អ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"អ្នក​បានបញ្ចប់​ចលនា​ថយក្រោយ​ហើយ។"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ទៅទំព័រដើម"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ដើម្បីចូលទៅអេក្រង់ដើមរបស់អ្នកនៅពេលណាក៏បាន សូមអូសឡើងលើដោយប្រើម្រាមដៃបីពីផ្នែកខាងក្រោមនៃអេក្រង់របស់អ្នក។"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ល្អ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"អ្នក​បានបញ្ចប់​ចលនា​ចូលទៅកាន់​ទំព័រដើម​ហើយ។"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"គ្រាប់ចុចសកម្មភាព"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ដើម្បីចូលប្រើប្រាស់កម្មវិធីរបស់អ្នក សូមចុចគ្រាប់ចុចសកម្មភាពនៅលើក្ដារចុចរបស់អ្នក។"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"សូមអបអរសាទរ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"អ្នកបានបញ្ចប់មេរៀនអំពីចលនាសម្រាប់គ្រាប់ចុចសកម្មភាព។\n\nគ្រាប់ចុចសកម្មភាព + / បង្ហាញផ្លូវកាត់ទាំងអស់ដែលអ្នកអាចប្រើបាន។"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ដើម្បីចូលទៅទំព័រដើម សូមអូសឡើងលើដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ដើម្បីមើលកម្មវិធីថ្មីៗ សូមអូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ដើម្បីមើលកម្មវិធីទាំងអស់របស់អ្នក សូមចុចគ្រាប់ចុចសកម្មភាពនៅលើក្ដារចុចរបស់អ្នក"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"បាន​កែ​លម្អ​ពាក្យពេចន៍"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ដោះសោដើម្បីមើល"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ប្រើផ្ទាំងប៉ះរបស់អ្នក ដើម្បីថយក្រោយ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"អូសទៅឆ្វេង ឬស្ដាំដោយប្រើ​ម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ប្រើផ្ទាំងប៉ះរបស់អ្នក ដើម្បីចូលទៅទំព័រដើម"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបី។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ប្រើក្ដារចុចរបស់អ្នក ដើម្បីមើលកម្មវិធីទាំងអស់"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ចុចគ្រាប់ចុចសកម្មភាពនៅពេលណាក៏បាន។ ចុច ដើម្បីស្វែងយល់បន្ថែមអំពីចលនា។"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ឥឡូវនេះ មុខងារងងឹតខ្លាំងក្លាយជាផ្នែកមួយនៃរបារពន្លឺ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ឥឡូវនេះ អ្នកអាចធ្វើឱ្យអេក្រង់ងងឹតខ្លាំងបានដោយបន្ថយកម្រិតពន្លឺបន្ថែមទៀតដោយចូលទៅកាន់ផ្នែកខាងលើនៃអេក្រង់របស់អ្នក។\n\nការធ្វើបែបនេះទទួលបានលទ្ធផលប្រសើរបំផុត ពេលអ្នកស្ថិតនៅកន្លែងងងឹត។"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ដកផ្លូវ​កាត់មុខងារងងឹតខ្លាំងចេញ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"ផ្លូវ​កាត់មុខងារងងឹតខ្លាំងត្រូវបានដកចេញ។ ដើម្បីបន្ថយពន្លឺរបស់អ្នក សូមប្រើរបារពន្លឺធម្មតា។"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 8ad74af..e1b119c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -45,7 +45,7 @@
     <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಅನ್ನು ನಿಯಂತ್ರಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?\nಈ ಆ್ಯಪ್‌ಗೆ ರೆಕಾರ್ಡ್ ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿಲ್ಲ, ಆದರೆ ಈ USB ಸಾಧನದ ಮೂಲಕ ಆಡಿಯೊವನ್ನು ಸೆರೆಹಿಡಿಯಬಹುದು."</string>
     <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯುವುದೇ?"</string>
     <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ಆಪ್‌ಗಳು USB ಪರಿಕರದಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವುದಿಲ್ಲ. ಆ ಬಗ್ಗೆ <xliff:g id="URL">%1$s</xliff:g> ನಲ್ಲಿ ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
-    <string name="title_usb_accessory" msgid="1236358027511638648">"USB ಪರಿಕರ"</string>
+    <string name="title_usb_accessory" msgid="1236358027511638648">"USB ಆ್ಯಕ್ಸೆಸರಿ"</string>
     <string name="label_view" msgid="6815442985276363364">"ವೀಕ್ಷಿಸು"</string>
     <string name="always_use_device" msgid="210535878779644679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="always_use_accessory" msgid="1977225429341838444">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ಸಂಪರ್ಕಗೊಂಡಾಗ ಯಾವಾಗಲೂ <xliff:g id="APPLICATION">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಿರಿ"</string>
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ಹಾಗೂ ತೆರೆದಿರುವ ಇತರ ಆ್ಯಪ್‌ಗಳು ಈ ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ಪತ್ತೆಹಚ್ಚಿವೆ."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ಟಿಪ್ಪಣಿಗೆ ಸೇರಿಸಿ"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ಲಿಂಕ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್‌ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ನೋಟಿಫಿಕೇಶನ್"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭಿಸಬೇಕೇ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ನೀವು ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬೇಕೇ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ನೀವು ರೆಕಾರ್ಡ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ ಗೋಚರಿಸುವ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಎಲ್ಲವನ್ನೂ ರೆಕಾರ್ಡ್ ಮಾಡಲಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ರೆಕಾರ್ಡ್ ಮಾಡಲು ಆ್ಯಪ್‌ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ಆಡಿಯೋ ರೆಕಾರ್ಡ್‌ ಮಾಡಿ"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ಸಾಧನದ ಆಡಿಯೋ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ನಿಮ್ಮ ಸಾಧನದ ಧ್ವನಿ ಉದಾ: ಸಂಗೀತ, ಕರೆಗಳು ಮತ್ತು ರಿಂಗ್‌ಟೋನ್‌ಗಳು"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ಕಳುಹಿಸಿ"</string>
     <string name="cancel" msgid="1089011503403416730">"ರದ್ದುಮಾಡಿ"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ಆ್ಯಪ್ ಲೋಗೋ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ದೃಢೀಕರಿಸಿ"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ದೃಢೀಕರಣವನ್ನು ರದ್ದುಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ಸ್ಕ್ರೀನ್ ಸೇವರ್"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ಇಥರ್ನೆಟ್"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ಆದ್ಯತೆಯ ಮೋಡ್‌ಗಳು"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ಬ್ಲೂಟೂತ್‌"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ಯಾವುದೇ ಜೋಡಿಸಲಾದ ಸಾಧನಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ಸಾಧನವನ್ನು ಕನೆಕ್ಟ್ ಅಥವಾ ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
@@ -299,14 +302,16 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ಆಡಿಯೊವನ್ನು ಬದಲಾಯಿಸಲು ಅಥವಾ ಹಂಚಿಕೊಳ್ಳಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ಡಿಸ್‌ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"ನಾಳೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"ನಾಳೆ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಆಗಲಿ"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್‌"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್‍ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ಅನ್ಯ ಸಾಧನ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ಟಾಗಲ್ ನ ಅವಲೋಕನ"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ಆದ್ಯತೆಯ ಮೋಡ್‌ಗಳು"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ಮುಗಿದಿದೆ"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ಸೆಟಪ್ ಮಾಡಿ"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ಅಲಾರಾಂಗಳನ್ನು ಹೊರತುಪಡಿಸಿ, ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್‌ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ಕಸ್ಟಮೈಸ್ ಮಾಡು"</string>
@@ -465,7 +469,7 @@
     <string name="phone_hint" msgid="6682125338461375925">"ಫೋನ್‌ಗಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="voice_hint" msgid="7476017460191291417">"ಧ್ವನಿ ಸಹಾಯಕ್ಕಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="camera_hint" msgid="4519495795000658637">"ಕ್ಯಾಮರಾಗಾಗಿ ಐಕಾನ್‌ನಿಂದ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
-    <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"ಒಟ್ಟು ಮೌನ. ಇದು ಪರದೆ ರೀಡರ್ ಅನ್ನು ಮೌನವಾಗಿರಿಸುತ್ತದೆ."</string>
+    <string name="interruption_level_none_with_warning" msgid="8394434073508145437">"ಒಟ್ಟು ಮೌನ. ಇದು ಸ್ಕ್ರೀನ್ ರೀಡರ್ ಅನ್ನು ಮೌನವಾಗಿರಿಸುತ್ತದೆ."</string>
     <string name="interruption_level_none" msgid="219484038314193379">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string>
     <string name="interruption_level_priority" msgid="661294280016622209">"ಆದ್ಯತೆ ಮಾತ್ರ"</string>
     <string name="interruption_level_alarms" msgid="2457850481335846959">"ಅಲಾರಮ್‌ಗಳು ಮಾತ್ರ"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ಸಮಯದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ದಲ್ಲಿ ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳು"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"ಲಾಕ್ ಸ್ಕ್ರೀನ್‌ಗೆ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ಸಮುದಾಯದ ಟ್ಯುಟೋರಿಯಲ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ವಜಾಗೊಳಿಸಿ"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ ಸ್ಕ್ರೀನ್‌ ಮೇಲೆ ಕಾಣಿಸುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಹೊಂದಿರುತ್ತದೆ. ಇದು ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ನೀವು ಪ್ಲೇ ಮಾಡುವ ಆಡಿಯೊದಂತಹ ಮಾಹಿತಿಯನ್ನೂ ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ರೆಕಾರ್ಡಿಂಗ್ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸಬೇಕೇ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ಈ ಕಾರ್ಯವನ್ನು ಒದಗಿಸುವ ಸೇವೆಗಳು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಗೋಚರಿಸುವ ಅಥವಾ ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಎಲ್ಲಾ ಮಾಹಿತಿಗಳಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತವೆ. ಇದು ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಫೋಟೋಗಳು, ಸಂದೇಶಗಳು ಮತ್ತು ನೀವು ಪ್ಲೇ ಮಾಡುವ ಆಡಿಯೊದಂತಹ ಮಾಹಿತಿಯನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ಒಂದೇ ಆ್ಯಪ್"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ ಅಥವಾ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಮೂಲಕ ರೆಕಾರ್ಡಿಂಗ್, ಕ್ಯಾಸ್ಟ್ ಮಾಡುವುದನ್ನು ಪ್ರಾರಂಭಿಸಬೇಕೇ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ ಅಥವಾ ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ನೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಬೇಕೇ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ಒಂದು ಆ್ಯಪ್‌ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ನಲ್ಲಿರುವ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ಸ್ಕ್ರೀನ್‌ ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಈ ಆಯ್ಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದೆ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ಕ್ಯಾಸ್ಟ್ ಮಾಡಲು ಪ್ರಾರಂಭಿಸಬೇಕೆ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ನೀವು ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ಕ್ಯಾಸ್ಟ್ ಮಾಡಲು ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ಹಂಚಿಕೊಳ್ಳಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡಬೇಕೆ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡಿ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡಿ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ನಿಮ್ಮ ಸಂಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನೀವು ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಏನಾದರೂ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿರುವುದು ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ಬಿತ್ತರಿಸಿ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ಬಿತ್ತರಿಸಲು ಆ್ಯಪ್‌ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ಹಂಚಿಕೊಳ್ಳಲು ಪ್ರಾರಂಭಿಸಬೇಕೇ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ಪ್ರಾರಂಭಿಸಿ"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ಮುಂದಿನದು"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ನೀವು ಆ್ಯಪ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಿದಾಗ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗುತ್ತದೆ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ಬದಲಿಗೆ ಈ ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳಿ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ಮರಳಿ ಬದಲಿಸಿ"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ಸ್ಯಾಟಲೈಟ್‌, ಕನೆಕ್ಷನ್ ಉತ್ತಮವಾಗಿದೆ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ಸ್ಯಾಟಲೈಟ್, ಕನೆಕ್ಷನ್ ಲಭ್ಯವಿದೆ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ಸ್ಯಾಟಲೈಟ್ SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ತುರ್ತು ಕರೆಗಳು ಅಥವಾ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ಕೆಲವರಿಗೆ ಮೋಜು ಆಗಿದೆ ಎಲ್ಲರಿಗೆ ಇಲ್ಲ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"ಸಿಸ್ಟಂ UI ಟ್ಯೂನರ್ ನಿಮಗೆ Android ಬಳಕೆದಾರ ಅಂತರಸಂಪರ್ಕವನ್ನು ಸರಿಪಡಿಸಲು ಮತ್ತು ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹೆಚ್ಚುವರಿ ಮಾರ್ಗಗಳನ್ನು ನೀಡುತ್ತದೆ. ಈ ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯಗಳು ಭವಿಷ್ಯದ ಬಿಡುಗಡೆಗಳಲ್ಲಿ ಬದಲಾಗಬಹುದು, ವಿರಾಮವಾಗಬಹುದು ಅಥವಾ ಕಾಣಿಸಿಕೊಳ್ಳದಿರಬಹುದು. ಎಚ್ಚರಿಕೆಯಿಂದ ಮುಂದುವರಿಯಿರಿ."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ಯಾವುದೇ ಹುಡುಕಾಟ ಫಲಿತಾಂಶಗಳಿಲ್ಲ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ಹಿಂಬದಿ ಗೆಸ್ಚರ್"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ಹೋಮ್ ಗೆಸ್ಚರ್"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ಆ್ಯಕ್ಷನ್‌ ಕೀ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ಭೇಷ್!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ಮೂರು ಬೆರಳುಗಳು ಬಲಕ್ಕೆ ಮತ್ತು ಎಡಕ್ಕೆ ಚಲಿಸುತ್ತಿರುವುದನ್ನು ತೋರಿಸುತ್ತಿರುವ ಟಚ್‌ಪ್ಯಾಡ್"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್‌ಗೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯನಿಮೇಶನ್ ಅನ್ನು ತೋರಿಸುತ್ತಿರುವ ಸಾಧನದ ಸ್ಕ್ರೀನ್"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ಹಿಂತಿರುಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಎಲ್ಲಿಯಾದರೂ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ.\n\nಇದಕ್ಕಾಗಿ ನೀವು ಕೀಬೋರ್ಡ್ ಶಾರ್ಟ್‌ಕಟ್ Action + ESC ಅನ್ನು ಸಹ ಬಳಸಬಹುದು."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ಭೇಷ್!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್‌ಗೆ ಹೋಗಲು, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಕೆಳಗಿನಿಂದ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ಭೇಷ್!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ಆ್ಯಕ್ಷನ್‌ ಕೀ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ನಿಮ್ಮ ಆ್ಯಪ್‌ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು, ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿರುವ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ಅಭಿನಂದನೆಗಳು!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ನೀವು ಆ್ಯಕ್ಷನ್‌ ಕೀ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ.\n\nನಿಮಗೆ ಲಭ್ಯವಿರುವ ಎಲ್ಲಾ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಆ್ಯಕ್ಷನ್‌ + / ತೋರಿಸುತ್ತದೆ."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ಹೋಮ್‌ಗೆ ಹೋಗಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್‌ಗಳನ್ನು ನೋಡಲು, ಟಚ್‌ಪ್ಯಾಡ್‌ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಹಾಗೂ ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ನಿಮ್ಮ ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು, ನಿಮ್ಮ ಕೀಬೋರ್ಡ್‌ನಲ್ಲಿರುವ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ಅರ್ಥಬದ್ಧವಾಗಿಸಲಾಗಿದೆ"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ನೋಡಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ಹಿಂತಿರುಗಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಎಡಕ್ಕೆ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ಹೋಮ್‌ಗೆ ಹೋಗಲು ನಿಮ್ಮ ಟಚ್‌ಪ್ಯಾಡ್ ಅನ್ನು ಬಳಸಿ"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ಮೂರು ಬೆರಳುಗಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಹಾಗೂ ಹೋಲ್ಡ್ ಮಾಡಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ಯಾವಾಗ ಬೇಕಾದರೂ ಆ್ಯಕ್ಷನ್‌ ಕೀಯನ್ನು ಒತ್ತಿರಿ. ಇನ್ನಷ್ಟು ಗೆಸ್ಚರ್‌ಗಳನ್ನು ತಿಳಿಯಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಈಗ ಬ್ರೈಟ್‌ನೆಸ್ ಬಾರ್‌ನ ಭಾಗವಾಗಿದೆ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲ್ಭಾಗದಿಂದ ಬ್ರೈಟ್‌ನೆಸ್ ಮಟ್ಟವನ್ನು ಇನ್ನಷ್ಟು ಕಡಿಮೆ ಮಾಡುವ ಮೂಲಕ ನೀವು ಈಗ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಇನ್ನಷ್ಟು ಮಬ್ಬುಗೊಳಿಸಬಹುದು.\n\nನೀವು ಕತ್ತಲೆಯ ವಾತಾವರಣದಲ್ಲಿರುವಾಗ ಇದು ಉತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"ಇನ್ನಷ್ಟು ಮಬ್ಬು ಶಾರ್ಟ್‌ಕಟ್ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ. ನಿಮ್ಮ ಬ್ರೈಟ್‌ನೆಸ್ ಅನ್ನು ಕಡಿಮೆ ಮಾಡಲು, ಸಾಮಾನ್ಯ ಬ್ರೈಟ್‌ನೆಸ್ ಬಾರ್ ಬಳಸಿ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b904fa8..27aa785 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 및 기타 공개 앱에서 이 스크린샷을 감지했습니다."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"메모에 추가"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"링크 포함"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g><xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"화면 녹화"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"화면 녹화 처리 중"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"녹화를 시작하시겠습니까?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"녹화 중에는 Android가 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"앱을 녹화할 때 Android가 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"녹화 시작"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"화면을 녹화하시겠습니까?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"단일 앱 녹화"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"전체 화면 녹화"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"전체 화면을 녹화하면 화면에 표시되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"앱을 녹화하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 녹화됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"화면 녹화"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"녹화할 앱 선택"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"오디오 녹음"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"기기 오디오"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"음악, 통화, 벨소리와 같이 기기에서 나는 소리"</string>
@@ -128,20 +132,20 @@
     <string name="screenrecord_start_error" msgid="2200660692479682368">"화면 녹화 시작 중 오류 발생"</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"녹화를 중지하시겠습니까?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"현재 전체 화면을 녹화 중입니다."</string>
-    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g>의 콘텐츠를 녹화 중입니다"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g>의 콘텐츠를 녹화 중입니다."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"녹화 중지"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"화면 공유 중"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"화면 공유를 중지하시겠습니까?"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"현재 전체 화면을 <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>과 공유 중입니다"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"현재 전체 화면을 <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> 앱과 공유 중입니다."</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"현재 전체 화면을 앱과 공유 중입니다"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 공유 중입니다"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 공유 중입니다."</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"현재 앱을 공유 중입니다"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"공유 중지"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"화면 전송 중"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"전송을 중지할까요?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"현재 전체 화면을 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"현재 전체 화면을 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 기기로 전송 중입니다."</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"현재 전체 화면을 근처 기기로 전송 중입니다."</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 <xliff:g id="DEVICE_NAME">%2$s</xliff:g>로 전송 중입니다."</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 기기로 전송 중입니다."</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"현재 <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>의 콘텐츠를 근처 기기로 전송 중입니다."</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"현재 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>로 전송 중입니다."</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"현재 근처 기기로 전송 중입니다."</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"보내기"</string>
     <string name="cancel" msgid="1089011503403416730">"취소"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"앱 로고"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"확인"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"다시 시도하세요."</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"탭하여 인증 취소"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"화면 보호기"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"이더넷"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"방해 금지 모드"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"우선순위 모드"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"블루투스"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"페어링된 기기가 없습니다"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"기기를 연결 또는 연결 해제하려면 탭하세요"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"오디오 공유"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"오디오를 전환하거나 공유하려면 탭하세요"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"저장됨"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"연결 해제"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"실행"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"실시간 자막"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"실시간 자막"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"설정 열기"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"기타 기기"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"최근 사용 버튼 전환"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"우선순위 모드"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"완료"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"설정"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"설정"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"알람을 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"맞춤설정"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 저속 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 충전 중 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> 후 충전 완료"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"잠금 화면의 위젯"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯이 잠금 화면에 추가됨"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"공동 튜토리얼을 시작하려면 왼쪽으로 스와이프하세요"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"맞춤설정"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"닫기"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"이 공간에서 위젯 추가, 삭제 또는 다시 정렬"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"여기에서 위젯을 추가, 삭제 또는 다시 정렬하세요"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"더 많은 위젯 추가"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"위젯을 맞춤설정하려면 길게 누르기"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"위젯 맞춤설정"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱이 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 사용자가 재생하는 오디오 등의 정보가 포함됩니다."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"녹화 또는 전송을 시작하시겠습니까?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"이 기능을 제공하는 서비스는 녹화 또는 전송 중에 화면에 표시되거나 기기에서 재생되는 모든 정보에 액세스할 수 있습니다. 여기에는 비밀번호, 결제 세부정보, 사진, 메시지, 사용자가 재생하는 오디오 등의 정보가 포함됩니다."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"전체 화면"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"단일 앱"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"앱 공유 또는 녹화"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱으로 녹화 또는 전송을 시작하시겠습니까?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"공유, 녹화 또는 전송 중에 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱이 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"앱을 공유, 녹화 또는 전송할 때는 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱이 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"시작"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"앱 공유 또는 녹화"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"화면을 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱과 공유하시겠습니까?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"앱 하나 공유"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"전체 화면 공유"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"전체 화면을 공유하면 화면에 있는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"앱을 공유하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"화면 공유"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 이 옵션을 사용 중지했습니다."</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"전송을 시작하시겠습니까?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"전송 중에는 Android가 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"앱을 전송할 때 Android가 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"전송 시작하기"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"공유할 앱 선택"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"화면을 전송하시겠습니까?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"앱 1개 전송"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"전체 화면 전송"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"전체 화면을 전송하면 화면이 있는 모든 항목이 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"앱을 전송하면 해당 앱에 표시되거나 재생되는 모든 항목이 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"화면 전송"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"전송할 앱 선택"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"공유를 시작하시겠습니까?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"공유, 녹화 또는 전송 중에 Android가 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"앱을 공유, 녹화 또는 전송할 때는 Android가 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"시작"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"다음"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"앱을 전환할 때 공유를 일시중지합니다."</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"대신 이 앱 공유"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"다시 전환"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"위성, 연결 상태 양호"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"위성, 연결 가능"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"위성 긴급 SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"긴급 전화 또는 SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"직장 프로필"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"마음에 들지 않을 수도 있음"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"시스템 UI 튜너를 사용하면 Android 사용자 인터페이스를 변경 및 맞춤설정할 수 있습니다. 이러한 실험실 기능은 향후 출시 버전에서는 변경되거나 다운되거나 사라질 수 있습니다. 신중하게 진행하시기 바랍니다."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"검색 결과 없음"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"뒤로 동작"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"홈 동작"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"작업 키"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"아주 좋습니다."</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"터치패드에서 세 손가락을 좌우로 움직이는 모습"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"뒤로 동작 애니메이션을 보여 주는 기기 화면"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"돌아가려면 세 손가락을 사용해 터치패드의 아무 곳이나 왼쪽 또는 오른쪽으로 스와이프합니다.\n\n키보드 단축키 Action + ESC를 사용할 수도 있습니다."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"아주 좋습니다"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"돌아가기 동작을 완료했습니다."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"홈으로 이동"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"언제든지 홈 화면으로 이동하려면 세 손가락으로 화면 하단에서 위로 스와이프하세요."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"좋습니다"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"홈으로 이동 동작을 완료했습니다."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"작업 키"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"앱에 액세스하려면 키보드의 작업 키를 누르세요."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"축하합니다"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"작업 키 동작을 완료했습니다.\n\n작업 키 + /를 누르면 사용 가능한 모든 단축키가 표시됩니다."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"홈으로 이동하려면 터치패드에서 세 손가락을 사용해 위로 스와이프합니다"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"최근 앱을 보려면 터치패드에서 세 손가락으로 위로 스와이프한 후 잠시 기다리세요"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"모든 앱을 보려면 키보드의 작업 키를 누르세요"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"수정됨"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"잠금 해제하여 보기"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"터치패드를 사용하여 돌아가기"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"세 손가락을 사용해 왼쪽 또는 오른쪽으로 스와이프하세요. 더 많은 동작을 알아보려면 탭하세요."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"터치패드를 사용하여 홈으로 이동"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"세 손가락을 사용해 위로 스와이프한 다음 잠시 기다리세요. 더 많은 동작을 알아보려면 탭하세요."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"키보드를 사용하여 모든 앱 보기"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"언제든지 작업 키를 누릅니다. 더 많은 동작을 알아보려면 탭하세요."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"이제 \'더 어둡게\' 기능이 밝기 막대에 추가되었습니다"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"이제 화면 상단에서 밝기 수준을 더 낮춰 화면을 더 어둡게 만들 수 있습니다\n\n이 기능은 어두운 환경에서 가장 잘 작동합니다."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"\'더 어둡게\' 단축키 삭제"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"\'더 어둡게\' 단축키가 삭제되었습니다. 밝기를 낮추려면 일반 밝기 막대를 사용하세요."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 7cea137..306baa7 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> жана ачылып турган башка колдонмолор ушул скриншотту аныктады."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Кыска жазууга кошуу"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Шилтеме кошуу"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Экрандан видео жаздырып алуу"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Экрандан жаздырылып алынган видео иштетилүүдө"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Жаздырып баштайсызбы?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Жаздырып жатканыңызда Android экраныңызда көрүнүп жана түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Колдонмону жаздырып жатканда Android анда көрүнүп же ойноп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Жаздырып баштоо"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтүндөй экранды жаздыруу"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Бүтүндөй экранды жаздырганда, андагы нерселердин баары видеого түшүп калат. Андыктан этият болуп, сырсөздөр, төлөм ыкмалары, билдирүүлөр, сүрөттөр, аудио жана видео материалдар сыяктуу купуя нерселерди көрсөтүп албаңыз."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Колдонмону жаздырганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер жаздырылат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Экранды жаздыруу"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Жаздыруу үчүн колдонмо тандоо"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио жаздыруу"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Түзмөктөгү аудиолор"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Музыка, чалуулар жана шыңгырлар сыяктуу түзмөгүңүздөгү добуштар"</string>
@@ -131,17 +135,17 @@
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Учурда <xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосун жаздырып жатасыз"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Жаздырууну токтотуу"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Экран бөлүшүлүүдө"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлүшүү токтотулсунбу?"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Экранды бөлүшүүнү токтотосузбу?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Учурда бүтүндөй экраныңызды <xliff:g id="HOST_APP_NAME">%1$s</xliff:g> менен бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Учурда бүтүндөй экраныңызды колдонмо менен бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Учурда <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> колдонмосун бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Учурда колдонмону бөлүшүп жатасыз"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Бөлүшүүнү токтотуу"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Тышкы экранга чыгарылууда"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Тышкы экранга чыгаруу токтотулсунбу?"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Тышкы экранга чыгарууну токтотосузбу?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Учурда бүтүндөй экраныңызды <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө чыгарып жатасыз"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Учурда бүтүндөй экраныңызды жакын жердеги түзмөккө чыгарып жатасыз"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Учурда <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> контентин <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнө чыгарып жатасыз"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Учурда <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> колдонмосун <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнө чыгарып жатасыз"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> контентин жакын жердеги түзмөккө чыгарып жатасыз"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Учурда <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө чыгарып жатасыз"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Учурда жакын жердеги түзмөккө чыгарып жатасыз"</string>
@@ -150,9 +154,9 @@
     <string name="issuerecord_title" msgid="286627115110121849">"Маселе жаздыргыч"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Маселе жаздырылууда"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Маселе тууралуу маалымат чогултулуп жатканы жөнүндө учурдагы билдирме"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Жаздыруу маселеси"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Маселе жазылууда"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Бөлүшүү"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"Жаздырылган маселе сакталды"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"Маселе жаздырылды"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Көрүү үчүн таптаңыз"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Жаздырылган маселе сакталган жок"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Башталбай койду"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Жөнөтүү"</string>
     <string name="cancel" msgid="1089011503403416730">"Баш тартуу"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Колдонмонун логотиби"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Ырастоо"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Кайталоо"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Аныктыгын текшерүүнү жокко чыгаруу үчүн таптаңыз"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Көшөгө"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Тынчымды алба"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Маанилүүлүк режимдери"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Жупташкан түзмөктөр жок"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Түзмөктү туташтыруу же ажыратуу үчүн таптаңыз"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Чогуу угуу"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудиону которуштуруу же бөлүшүү үчүн таптаңыз"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сакталды"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Ыкчам коштомо жазуулар"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ыкчам коштомо жазуулар"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Параметрлерди ачуу"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Башка түзмөк"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Назар режимин өчүрүү/күйгүзүү"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Маанилүүлүк режимдери"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Бүттү"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Параметрлер"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Тууралоо"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ойготкучтардан башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Ыңгайлаштыруу"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Жай кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Кубатталууда • Толгонго чейин <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> калды"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Кулпуланган экрандагы виджеттер"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети кулпу экранына кошулду"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Жалпы үйрөткүчтү иштетүү үчүн солго сүрүңүз"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Ыңгайлаштыруу"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Жабуу"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Виджет кошуу"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Бүттү"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Виджеттерди кошуу"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Тандалма колдонмолордун виджеттерин планшеттин кулпусун ачпастан эле, ыкчам колдонуңуз."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Планшет кулпуланып турса да, жакшы көргөн колдонмолордун виджеттери көрүнүп турат."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Бардык виджеттер кулпуланган экранда көрсөтүлсүнбү?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Параметрлерди ачуу"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Жумуш колдонмолорун иштетесизби?"</string>
@@ -507,10 +510,10 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"виджетти алып салуу"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"тандалган виджетти жайгаштыруу"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Кулпуланган экрандагы виджеттер"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Планшетиңиз кулпуланган болсо да, кулпуланган экраныңыздан виджеттерди бардыгы көрө алат."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Кулпуланган планшетте баарына көрүнүп турат."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"виджетти тандоодон чыгаруу"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Кулпуланган экрандагы виджеттер"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн бул сиз экениңизди ырасташыңыз керек. Аларды планшетиңиз кулпуланып турса да, баары көрө аларын эске алыңыз. Айрым виджеттер кулпуланган экранда колдонууга арналган эмес жана аларды бул жерге кошуу кооптуу болушу мүмкүн."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Колдонмону виджет аркылуу ачуу үчүн өзүңүздү ырасташыңыз керек. Алар кулпуланган планшетиңизде да көрүнүп турат. Кээ бир виджеттерди кулпуланган экранда колдоно албайсыз, андыктан аларды ал жерге кошпой эле койгонуңуз оң."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Түшүндүм"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Колдонуучуну которуу"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ылдый түшүүчү меню"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Жаздырып же тышкы экранга чыгарып жатканда <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Буга сырсөздөр, төлөмдүн чоо-жайы, сүрөттөр, билдирүүлөр жана ойнотулган аудио кирет."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Жаздырып же тышкы экранга чыгарып жатканда кызмат көрсөтүүчү экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Буга сырсөздөр, төлөмдүн чоо-жайы, сүрөттөр, билдирүүлөр жана ойнотулган аудио кирет."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Бүтүндөй экран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Жалгыз колдонмо"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Колдонмону бөлүшүү же жаздыруу"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу аркылуу жаздырып же тышкы экранга чыгарып баштайсызбы?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Бөлүшүп, жаздырып же тышкы экранга чыгарып жатканда <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Колдонмону бөлүшүп, жаздырып же тышкы экранга чыгарганда <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу анда көрүнүп же ойноп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Баштоо"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Колдонмону бөлүшүү же жаздыруу"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Экранды <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> менен бөлүшөсүзбү?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Бир колдонмону бөлүшүү"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Толук экранды бөлүшүү"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Бүтүндөй экранды бөлүшкөндө андагы бардык нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Колдонмону бөлүшкөндө ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлүшүү"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> бул параметрди өчүрүп койду"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Тышкы экранга чыгарып баштайсызбы?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Тышкы экранга чыгарганда Android экраныңызда көрүнүп жана түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Колдонмону тышкы экранга чыгарганда Android анда көрүнүп же ойноп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Тышкы экранга чыгарып баштоо"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлүшүү үчүн колдонмо тандоо"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды тышкы экранга чыгарасызбы?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону тышкы экранга чыгаруу"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Бүтүндөй экранды тышкы экранга чыгаруу"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Бүтүндөй экранды тышкы экранга чыгарганда андагы бардык нерселер көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселерди көрсөтүп албаңыз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Колдонмону тышкы экранга чыгарганда ал колдонмодо көрсөтүлүп же ойнотулуп жаткан нерселер көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видео сыяктуу нерселерди көрсөтүп албаңыз."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Тышкы экранга чыгаруу"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Тышкы экранга чыгаруу үчүн колдонмо тандоо"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Бөлүшүү башталсынбы?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Бөлүшүп, жаздырып же тышкы экранга чыгарып жатканда Android экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Колдонмону бөлүшүп, жаздырып же тышкы экранга чыгарганда Android анда көрүнүп же ойноп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Баштоо"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Кийинки"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Колдонмо которулганда бөлүшүү тындырылат"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Анын ордуна бул колдонмону бөлүшүү"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Кайра которулуу"</string>
@@ -592,7 +604,7 @@
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"Тармактын таржымалы"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Тастыктоочу борбордун тастыктамасы"</string>
-    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Саясаттарды карап көрүү"</string>
+    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Эрежелерди карап көрүү"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Башкаруу элементтерин көрүү"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"Бул түзмөк <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> уюмуна таандык.\n\nАдминистраторуңуз бул түзмөктөгү параметрлерди, корпоративдик ресурстарды пайдалануу мүмкүнчүлүгүн берген параметрлерди жана колдонмолорду, түзмөгүңүзгө байланыштуу маалыматтарды (мисалы, түзмөгүңүздүн жайгашкан жери сыяктуу) көзөмөлдөп башкара алат.\n\nТолугураак маалымат алуу үчүн IT администраторуңузга кайрылыңыз."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> бул түзмөк менен байланышкан маалыматты көрүп, колдонмолорду башкарып, анын параметрлерин өзгөртө алат.\n\nЭгер суроолоруңуз болсо, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> уюмуна кайрылыңыз."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутник, байланыш жакшы"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Спутник, байланыш бар"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутник SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Шашылыш чалуулар же SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Жумуш профили"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Баарына эле жага бербейт"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner Android колдонуучу интерфейсин жөнгө салып жана ыңгайлаштыруунун кошумча ыкмаларын сунуштайт. Бул сынамык функциялар кийинки чыгарылыштарда өзгөрүлүп, бузулуп же жоголуп кетиши мүмкүн. Абайлап колдонуңуз."</string>
@@ -1287,7 +1300,7 @@
     <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string>
     <string name="note_task_button_label" msgid="230135078402003532">"Эскертме жазуу"</string>
     <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Эскертме жазуу (<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>)"</string>
-    <string name="audio_sharing_description" msgid="8849060142768870004">"Аудиону бөлүшүү"</string>
+    <string name="audio_sharing_description" msgid="8849060142768870004">"Чогуу угуу"</string>
     <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string>
     <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Атайын мүмкүнчүлүктөр"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Ыкчам баскычтар"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Эч нерсе табылган жок"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Артка кайтуу жаңсоосу"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Башкы бетке өтүү жаңсоосу"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Аракет баскычы"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Азаматсыз!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Үч манжанын оңго жана солго жылып жатканы көрсөтүлгөн сенсордук такта"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Артка жаңсоосунун анимациясы көрсөтүлгөн түзмөктүн экраны"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Кайтуу үчүн сенсордук тактанын каалаган жерин үч манжаңыз менен солго же оңго сүрүңүз.\n\nОшондой эле Action + ESC баскычтарынын айкалышын колдоно аласыз."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Азаматсыз!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"\"Артка\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Башкы бетке өтүү"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Каалаган убакта башкы экранга өтүү үчүн экранды үч манжаңыз менен ылдыйдан жогору карай сүрүңүз."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Сонун!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Аракет баскычы"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Бардык колдонмолоруңузду көрүү үчүн баскычтобуңуздагы аракет баскычын басыңыз"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Куттуктайбыз!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Аракет баскычынын жаңсоосун аткардыңыз.\n\n+ / аракети бардык жеткиликтүү ыкчам баскычтарды көрсөтөт."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Башкы бетке өтүү үчүн сенсордук тактаны үч манжаңыз менен өйдө сүрүңүз"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Акыркы колдонмолорду көрүү үчүн сенсордук тактаны үч манжаңыз менен өйдө сүрүп, кармап туруңуз"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Бардык колдонмолоруңузду көрүү үчүн баскычтобуңуздагы аракет баскычын басыңыз"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Жашырылды"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Көрүү үчүн кулпусун ачыңыз"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Артка кайтуу үчүн сенсордук тактаны колдонуңуз"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Үч манжаңыз менен солго же оңго сүрүңүз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Башкы бетке өтүү үчүн сенсордук тактаны колдонуңуз"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Үч манжаңыз менен өйдө сүрүп, кармап туруңуз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бардык колдонмолорду көрүү үчүн баскычтобуңузду колдонуңуз"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Каалаганда аракет баскычын басыңыз. Башка жаңсоолорду үйрөнүү үчүн таптаңыз."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Кошумча караңгылатуу эми жарыктык тилкесинде жайгашкан"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Эми экраныңыздын өйдө жагынан жарыктыктын деңгээлин азайтып, экранды кошумча караңгылата аласыз.\n\nМуну караңгы жерде турганыңызда колдонуу сунушталат."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Кошумча караңгылатуу ыкчам баскычын өчүрүү"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Кошумча караңгылатуу ыкчам баскычы өчүрүлдү. Жарыктыкты азайтуу үчүн кадимки жарыктык тилкесин колдонуңуз."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 2c490ac..75f579c 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ແລະ ແອັບອື່ນໆທີ່ເປີດຢູ່ກວດພົບຮູບໜ້າຈໍນີ້."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ເພີ່ມໃສ່ບັນທຶກ"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ຮວມລິ້ງ"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ກຳລັງປະມວນຜົນການບັນທຶກໜ້າຈໍ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ເລີ່ມການບັນທຶກບໍ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ໃນຂະນະທີ່ທ່ານກຳລັງບັນທຶກ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ໃນຂະນະທີ່ທ່ານກຳລັງບັນທຶກແອັບ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ເລີ່ມການບັນທຶກ"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ບັນທຶກໜ້າຈໍຂອງທ່ານບໍ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ບັນທຶກແອັບດຽວ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ບັນທຶກໝົດໜ້າຈໍ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ເມື່ອທ່ານບັນທຶກໝົດໜ້າຈໍຂອງທ່ານ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງຢູ່ໜ້າຈໍຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ເມື່ອທ່ານບັນທຶກແອັບ, ລະບົບຈະບັນທຶກທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ໃນແອັບນັ້ນ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ບັນທຶກໜ້າຈໍ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ເລືອກແອັບທີ່ຈະບັນທຶກ"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ບັນທຶກສຽງ"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ສຽງອຸປະກອນ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ສຽງຈາກອຸປະກອນຂອງທ່ານ ເຊັ່ນ: ສຽງເພງ, ສຽງລົມໂທລະສັບ ແລະ ສຽງຣິງໂທນ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ສົ່ງ"</string>
     <string name="cancel" msgid="1089011503403416730">"ຍົກເລີກ"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ໂລໂກ້ແອັບ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ຢືນຢັນ"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ລອງໃໝ່"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ແຕະເພື່ອຍົກເລີກການກວດສອບຄວາມຖືກຕ້ອງ"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ພາບພັກໜ້າຈໍ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ຫ້າມລົບກວນ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ໂໝດຄວາມສຳຄັນ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ບໍ່​ມີ​ອຸ​ປະ​ກອນ​ທີ່​ສາ​ມາດ​ຈັບ​ຄູ່​ໄດ້"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ແຕະເພື່ອເຊື່ອມຕໍ່ ຫຼື ຕັດການເຊື່ອມຕໍ່ອຸປະກອນ"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ການແບ່ງປັນສຽງ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ແຕະເພື່ອສະຫຼັບ ຫຼື ແບ່ງປັນສຽງ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ບັນທຶກແລ້ວ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ຕັດການເຊື່ອມຕໍ່"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ເປີດນຳໃຊ້"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"ຄຳບັນຍາຍສົດ"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ຄຳບັນຍາຍສົດ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸ​ປະ​ກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸ​ປະ​ກອນບໍ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ເປີດການຕັ້ງຄ່າ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ອຸປະກອນອື່ນໆ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ສະຫຼັບພາບຮວມ"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ໂໝດຄວາມສຳຄັນ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ແລ້ວໆ"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ການຕັ້ງຄ່າ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ຕັ້ງຄ່າ"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ທ່ານຈະບໍ່ໄດ້ຮັບການລົບກວນຈາກສຽງ ແລະ ການສັ່ນເຕືອນ, ຍົກເວັ້ນໃນເວລາໂມງປຸກດັງ. ທ່ານອາດຍັງຄົງໄດ້ຍິນຫາກທ່ານເລືອກຫຼິ້ນເພງ, ວິດີໂອ ແລະ ເກມ."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ປັບແຕ່ງ"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ວິດເຈັດຢູ່ໜ້າຈໍລັອກ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"ເພີ່ມວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ໃສ່ໜ້າຈໍລັອກແລ້ວ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ປັດຊ້າຍເພື່ອເລີ່ມບົດແນະນຳສ່ວນກາງ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ປັບແຕ່ງ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ປ່ອຍ​ໄປ"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະມີສິດເຂົ້າເຖິງຂໍ້ມູນທັງໝົດທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຈາກອຸປະກອນຂອງທ່ານໃນຂະນະທີ່ບັນທຶກ ຫຼື ສົ່ງສັນຍານ. ເຊິ່ງຈະຮວມທັງຂໍ້ມູນຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຮູບພາບ, ຂໍ້ຄວາມ ແລະ ສຽງທີ່ທ່ານຫຼິ້ນ."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ເລີ່ມການບັນທຶກ ຫຼື ການສົ່ງສັນຍານບໍ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ບໍລິການທີ່ມີຟັງຊັນນີ້ຈະມີສິດເຂົ້າເຖິງຂໍ້ມູນທັງໝົດທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຈາກອຸປະກອນຂອງທ່ານໃນຂະນະທີ່ບັນທຶກ ຫຼື ສົ່ງສັນຍານ. ເຊິ່ງຈະຮວມທັງຂໍ້ມູນຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຮູບພາບ, ຂໍ້ຄວາມ ແລະ ສຽງທີ່ທ່ານຫຼິ້ນ."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ທັງໝົດໜ້າຈໍ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ແອັບດຽວ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ແບ່ງປັນ ຫຼື ບັນທຶກແອັບ"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"ເລີ່ມການບັນທຶກ ຫຼື ການສົ່ງສັນຍານດ້ວຍ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ເລີຍບໍ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານແອັບ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ເລີ່ມ"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ແບ່ງປັນ ຫຼື ບັນທຶກແອັບ"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ແບ່ງປັນໜ້າຈໍຂອງທ່ານກັບ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ບໍ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ແບ່ງປັນແອັບດຽວ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ແບ່ງປັນໜ້າຈໍທັງໝົດ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ເມື່ອທ່ານແບ່ງປັນໜ້າຈໍທັງໝົດຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ຢູ່ໜ້າຈໍຂອງທ່ານໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ເມື່ອທ່ານແບ່ງປັນແອັບຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ແບ່ງປັນໜ້າຈໍ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ປິດການນຳໃຊ້ຕົວເລືອກນີ້ແລ້ວ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ເລີ່ມການສົ່ງສັນຍານບໍ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ເມື່ອທ່ານກຳລັງສົ່ງສັນຍານ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ເມື່ອທ່ານກຳລັງສົ່ງສັນຍານແອັບ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ເລີ່ມການສົ່ງສັນຍານ"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ເລືອກແອັບທີ່ຈະແບ່ງປັນ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານບໍ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ສົ່ງສັນຍານແອັບ 1 ລາຍການ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ສົ່ງສັນຍານໜ້າຈໍທັງໝົດ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ເມື່ອທ່ານສົ່ງສັນຍານໜ້າຈໍທັງໝົດຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ຢູ່ໜ້າຈໍຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ເມື່ອທ່ານສົ່ງສັນຍານແອັບ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ສົ່ງສັນຍານໜ້າຈໍ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ເລືອກແອັບທີ່ຈະສົ່ງສັນຍານ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ເລີ່ມການແບ່ງປັນບໍ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານແອັບ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ເລີ່ມ"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ຕໍ່ໄປ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ການແບ່ງປັນຈະຢຸດຊົ່ວຄາວເມື່ອທ່ານປ່ຽນແອັບ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ແບ່ງປັນແອັບນີ້ແທນ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ປ່ຽນກັບຄືນ"</string>
@@ -596,7 +608,7 @@
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"ເບິ່ງການຄວບຄຸມ"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"ອຸປະກອນນີ້ເປັນຂອງ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານສາມາດເຝົ້າຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ສິດເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານ ແລະ ຂໍ້ມູນສະຖານທີ່ອຸປະກອນຂອງທ່ານໄດ້.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານ."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> ອາດສາມາດເຂົ້າເຖິງຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນນີ້, ຈັດການແອັບ ແລະ ປ່ຽນການຕັ້ງຄ່າອຸປະກອນນີ້ໄດ້.\n\nຫາກທ່ານມີຄຳຖາມ, ກະລຸນາຕິດຕໍ່ <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ.\n\nຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານສາມາດເຝົ້າຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ສິດເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານ ແລະ ຂໍ້ມູນສະຖານທີ່ອຸປະກອນຂອງທ່ານໄດ້.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານ."</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"ອຸປະກອນນີ້ເປັນຂອງອົງກອນທ່ານ.\n\nຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານສາມາດເຝົ້າຕິດຕາມ ແລະ ຈັດການການຕັ້ງຄ່າ, ສິດເຂົ້າເຖິງອົງກອນ, ແອັບ, ຂໍ້ມູນທີ່ເຊື່ອມໂຍງກັບອຸປະກອນຂອງທ່ານ ແລະ ຂໍ້ມູນສະຖານທີ່ອຸປະກອນຂອງທ່ານໄດ້.\n\nສຳລັບຂໍ້ມູນເພີ່ມເຕີມ, ກະລຸນາຕິດຕໍ່ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານ."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ອົງກອນຂອງທ່ານຕິດຕັ້ງອຳນາດໃບຮັບຮອງໄວ້ໃນໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ມີອຳນາດໃບຮັບຮອງຕິດຕັ້ງຢູ່ໃນອຸປະກອນນີ້. ທຣາບຟິກເຄືອຂ່າຍທີ່ເຂົ້າລະຫັດໄວ້ຂອງທ່ານອາດຖືກຕິດຕາມ ຫຼື ແກ້ໄຂໄດ້."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ດາວທຽມ, ການເຊື່ອມຕໍ່ດີ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ດາວທຽມ, ການເຊື່ອມຕໍ່ທີ່ພ້ອມນຳໃຊ້"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ດາວທຽມ"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ໂທສຸກເສີນ ຫຼື SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"​ໂປຣ​ໄຟລ໌​ບ່ອນ​ເຮັດ​ວຽກ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ມ່ວນຊື່ນສຳລັບບາງຄົນ ແຕ່ບໍ່ແມ່ນສຳລັບທຸກຄົນ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner ໃຫ້ທ່ານມີວິທີພິເສດຕື່ມອີກໃນການປັບປ່ຽນ ແລະຕົບແຕ່ງສ່ວນຕໍ່ປະສານຜູ້ໃຊ້ຂອງ Android. ຄຸນສົມບັດທົດລອງໃຊ້ເຫຼົ່ານີ້ອາດຈະປ່ຽນແປງ, ຢຸດເຊົາ ຫຼືຫາຍໄປໃນການວາງຈຳໜ່າຍໃນອະນາຄົດ. ຈົ່ງດຳເນີນຕໍ່ດ້ວຍຄວາມລະມັດລະວັງ."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ການຊ່ວຍເຂົ້າເຖິງ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ຄີລັດ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ບໍ່ມີຜົນການຊອກຫາ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ທ່າທາງສຳລັບກັບຄືນ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ທ່າທາງສຳລັບໜ້າຫຼັກ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ປຸ່ມຄຳສັ່ງ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ແລ້ວໆ"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ດີຫຼາຍ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ກັບຄືນ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ແຜ່ນສໍາຜັດສະແດງພາບ 3 ນິ້ວເລື່ອນໄປທາງຂວາ ແລະ ຊ້າຍ"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ໜ້າຈໍອຸປະກອນສະແດງພາບເຄື່ອນໄຫວຂອງທ່າທາງກັບຄືນ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ເພື່ອກັບຄືນ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາບ່ອນໃດກໍໄດ້ເທິງແຜ່ນສຳຜັດ.\n\nທ່ານຍັງສາມາດໃຊ້ຄຳສັ່ງຄີລັດ + ESC ສຳລັບການດຳເນີນການນີ້ໄດ້ນຳ."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ເກັ່ງຫຼາຍ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ທ່ານໃຊ້ທ່າທາງກັບຄືນສຳເລັດແລ້ວ."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ໄປຫາໜ້າຫຼັກ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ເພື່ອໄປຫາໜ້າຫຼັກຂອງທ່ານຕອນໃດກໍໄດ້, ໃຫ້ປັດຂຶ້ນດ້ວຍສາມນິ້ວຈາກລຸ່ມສຸດຂອງໜ້າຈໍຂອງທ່ານ."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ດີຫຼາຍ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ທ່ານໃຊ້ທ່າທາງໄປໜ້າຫຼັກສຳເລັດແລ້ວ."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ປຸ່ມຄຳສັ່ງ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ເພື່ອເຂົ້າເຖິງແອັບ, ໃຫ້ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ຂໍສະແດງຄວາມຍິນດີ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ທ່ານໃຊ້ທ່າທາງປຸ່ມຄຳສັ່ງສໍາເລັດແລ້ວ.\n\nຄໍາສັ່ງ + / ຈະສະແດງທາງລັດທັງໝົດທີ່ທ່ານມີ."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ເພື່ອໄປຫາໜ້າຫຼັກ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຂຶ້ນເທິງແຜ່ນສໍາຜັດ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ເພື່ອເບິ່ງແອັບຫຼ້າສຸດ, ໃຫ້ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ ແລ້ວຄ້າງໄວ້ເທິງແຜ່ນສໍາຜັດ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ເພື່ອເບິ່ງແອັບທັງໝົດຂອງທ່ານ, ໃຫ້ກົດປຸ່ມຄຳສັ່ງຢູ່ແປ້ນພິມຂອງທ່ານ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ປົກປິດໄວ້ແລ້ວ"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ປົດລັອກເພື່ອເບິ່ງ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ໃຊ້ແຜ່ນສໍາຜັດຂອງທ່ານເພື່ອກັບຄືນ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ໃຊ້ 3 ນິ້ວປັດຊ້າຍ ຫຼື ຂວາ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ໃຊ້ແຜ່ນສໍາຜັດຂອງທ່ານເພື່ອໄປຫາໜ້າຫຼັກ"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ໃຊ້ 3 ນິ້ວປັດຂຶ້ນ ແລ້ວຄ້າງໄວ້. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ໃຊ້ແປ້ນພິມຂອງທ່ານເພື່ອເບິ່ງແອັບທັງໝົດ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ກົດປຸ່ມຄຳສັ່ງໄດ້ທຸກເວລາ. ແຕະເພື່ອສຶກສາທ່າທາງເພີ່ມເຕີມ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ຕອນນີ້ການຫຼຸດແສງເປັນພິເສດເປັນສ່ວນໜຶ່ງຂອງແຖບຄວາມສະຫວ່າງແລ້ວ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ຕອນນີ້ທ່ານສາມາດເຮັດໃຫ້ໜ້າຈໍມືດລົງເປັນພິເສດໄດ້ໂດຍການຫຼຸດລະດັບຄວາມສະຫວ່າງລົງໃຫ້ຫຼາຍຂຶ້ນຈາກເທິງສຸດຂອງໜ້າຈໍຂອງທ່ານ.\n\nຄຸນສົມບັດນີ້ຈະເຮັດວຽກໄດ້ດີທີ່ສຸດເມື່ອທ່ານຢູ່ໃນສະພາບແວດລ້ອມທີ່ມືດ."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ລຶບທາງລັດທີ່ຫຼຸດແສງເປັນພິເສດອອກ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"ລຶບທາງລັດທີ່ຫຼຸດແສງເປັນພິເສດອອກແລ້ວ. ເພື່ອຫຼຸດຄວາມສະຫວ່າງຂອງທ່ານລົງ, ໃຫ້ໃຊ້ແຖບຄວາມສະຫວ່າງປົກກະຕິ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6b97dc9..61516fe 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ ir kitos atidarytos programos aptiko šią ekrano kopiją."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pridėti prie užrašo"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Įtraukti nuorodą"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekrano vaizdo įrašytuvas"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Apdorojam. ekrano vaizdo įraš."</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Pradėti įrašymą?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Kai įrašote, „Android“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Kai įrašote programą, „Android“ gali pasiekti viską, kas rodoma ar leidžiama toje programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Pradėti įrašymą"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Įrašyti ekraną?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Įrašyti vieną programą"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Įrašyti visą ekraną"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kai įrašote visą ekraną, įrašomas visas ekrane rodomas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kai įrašote programą, įrašomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Įrašyti ekraną"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Norimos įrašyti programos pasirinkimas"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Įrašyti garsą"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Įrenginio garsas"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Garsas iš jūsų įrenginio, pvz., muzika, skambučiai ir skambėjimo tonai"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Siųsti"</string>
     <string name="cancel" msgid="1089011503403416730">"Atšaukti"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Programos logotipas"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Patvirtinkite"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Bandyti dar kartą"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Palieskite, jei norite atšaukti autentifikavimą"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekrano užsklanda"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternetas"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Netrukdymo režimas"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteto režimai"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nėra pasiekiamų susietų įrenginių"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Palieskite, kad prijungtumėte ar atjungtumėte įrenginį"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Garso įrašų bendrinimas"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Palieskite, jei norite perjungti arba bendrinti garsą"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtitrai realiuoju laiku"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrai realiuoju laiku"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atidaryti nustatymus"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Kitas įrenginys"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Perjungti apžvalgą"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteto režimai"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Atlikta"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nustatymai"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nustatyti"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jūsų netrikdys garsai ir vibravimas, išskyrus signalus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tinkinti"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Valdikliai užrakinimo ekrane"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Valdiklis „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ pridėtas prie užrakinimo ekrano"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Perbraukite kairėn, paleistumėte bendruomenės mokomąją medžiagą"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tinkinti"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Atsisakyti"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Pradėti įrašyti ar perduoti turinį?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Šią funkciją teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Visas ekranas"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Viena programa"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Programos bendrinimas ar įrašymas"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Pradėti įrašyti ar perduoti turinį naudojant „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kai bendrinate, įrašote ar perduodate turinį, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kai bendrinate, įrašote ar perduodate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ gali pasiekti viską, kas rodoma ar leidžiama programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pradėti"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Programos bendrinimas ar įrašymas"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Bendrinti ekraną su „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bendrinti vieną programą"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Bendrinti visą ekraną"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kai bendrinate visą ekraną, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas ekrano turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kai bendrinate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bendrinti ekraną"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Programoje „<xliff:g id="APP_NAME">%1$s</xliff:g>“ ši parinktis išjungta"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Pradėti perdavimą?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kai perduodate turinį, „Android“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kai perduodate programą, „Android“ gali pasiekti viską, kas rodoma ar leidžiama toje programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Pradėti perdavimą"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Norimos bendrinti programos pasirinkimas"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Perduoti ekraną?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Perduoti vieną programą"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Perduoti visą ekraną"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kai perduodate visą ekraną, matomas visas ekrano turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kai perduodate programą, matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Perduoti ekraną"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Norimos perduoti programos pasirinkimas"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Pradėti bendrinti?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kai bendrinate, įrašote ar perduodate turinį, „Android“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kai bendrinate, įrašote ar perduodate programą, „Android“ gali pasiekti viską, kas rodoma ar leidžiama programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Pradėti"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Kitas"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Bendrinimas pristabdomas, kai perjungiate programas"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Bendrinti šią programą"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Perjungti atgal"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Palydovas, geras ryšys"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Palydovas, pasiekiamas ryšys"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Prisijungimas prie palydovo kritiniu atveju"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Skambučiai pagalbos numeriu arba pagalbos iškvietimas kritiniu atveju"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Darbo profilis"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Smagu, bet ne visada"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Sistemos naudotojo sąsajos derinimo priemonė suteikia papildomų galimybių pagerinti ir tinkinti „Android“ naudotojo sąsają. Šios eksperimentinės funkcijos gali pasikeisti, nutrūkti ar išnykti iš būsimų laidų. Tęskite atsargiai."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pritaikomumas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Spartieji klavišai"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nėra jokių paieškos rezultatų"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Grįžimo atgal gestas"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Pagrindinio ekrano gestas"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Veiksmų klavišas"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Puiku!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Jutiklinė dalis, kurioje rodomi trys dešinėn ir kairėn judantys pirštai"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Įrenginio ekranas, kuriame rodoma grįžimo gesto animacija"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Jei norite grįžti, perbraukite kairėn arba dešinėn trimis pirštais bet kurioje jutiklinės dalies vietoje.\n\nTaip pat galite naudoti šį spartųjį klavišą: veiksmų klavišas + klavišas „Esc“."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Puiku!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Jei norite bet kada pasiekti pagrindinį ekraną, perbraukite aukštyn trim pirštais iš ekrano apačios."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Šaunu!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Atlikote perėjimo į pagrindinį ekraną gestą."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Veiksmų klavišas"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Jei norite pasiekti programas, paspauskite klaviatūros veiksmų klavišą."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Sveikiname!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Atlikote veiksmų klavišo gestą.\n\nVeiksmas + / rodo visus pasiekiamus sparčiuosius klavišus."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Jei norite eiti į pagrindinį ekraną, jutiklinėje dalyje perbraukite aukštyn trimis pirštais"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Peržiūrėkite naujausias programas, jutiklinėje dalyje perbraukę aukštyn trimis pirštais ir palaikę"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Jei norite peržiūrėti visas programas, paspauskite klaviatūros veiksmų klavišą"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Paslėpta"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Atrakinkite, kad peržiūrėtumėte"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Naudokite klaviatūrą, kad grįžtumėte atgal"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Perbraukite į kairę ar dešinę trimis pirštais. Palieskite, kad sužinotumėte daugiau gestų."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Naudokite jutiklinę dalį, jei norite eiti į pagrindinį ekraną"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Perbraukite aukštyn trimis pirštais ir palaikykite. Palieskite, kad sužinotumėte daugiau gestų."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Naudokite klaviatūrą, kad peržiūrėtumėte visas programas"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bet kuriuo metu paspauskite veiksmų klavišą. Palieskite, kad sužinotumėte daugiau gestų."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Funkcija „Itin blanku“ dabar yra ryškumo juostos dalis"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Dabar galite padaryti ekraną itin blankų, dar labiau sumažindami ryškumo lygį nuo ekrano viršaus.\n\nŠi funkcija geriausiai veikia, kai esate tamsioje aplinkoje."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Pašalinti funkcijos „Itin blanku“ spartųjį klavišą"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Funkcijos „Itin blanku“ spartusis klavišas pašalintas. Jei norite sumažinti ryškumą, naudokite įprastą ryškumo juostą."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index f191a28..1b48d3f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> un citas atvērtas lietotnes konstatēja, ka tika veikts ekrānuzņēmums."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pievienot piezīmei"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Iekļaut saiti"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekrāna ierakstītājs"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekrāna ieraksta apstrāde"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Aktīvs paziņojums par ekrāna ierakstīšanas sesiju"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Vai sākt ierakstīšanu?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Ierakstīšanas laikā Android var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Lietotnes ierakstīšanas laikā Android var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu rīkojieties piesardzīgi."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Sākt ierakstīšanu"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vai ierakstīt ekrānu?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ierakstīt vienu lietotni"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ierakstīt visu ekrānu"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ierakstot visu ekrānu, viss, kas redzams ekrānā, tiek ierakstīts. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ierakstot lietotni, tiek ierakstīts viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ierakstīt ekrānu"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Lietotnes izvēlēšanās ierakstīšanai"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Ierakstīt audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Ierīces audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Skaņa no jūsu ierīces, piemēram, mūzika, sarunas un zvana signāli"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Sūtīt"</string>
     <string name="cancel" msgid="1089011503403416730">"Atcelt"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Lietotnes logotips"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Apstiprināt"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Mēģināt vēlreiz"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Pieskarieties, lai atceltu autentifikāciju."</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekrānsaudzētājs"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Tīkls Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režīms “Netraucēt”"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritātes režīmi"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nav pieejama neviena pārī savienota ierīce."</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Lai pievienotu vai atvienotu kādu ierīci, pieskarieties."</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio kopīgošana"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Pieskarieties, lai pārslēgtu vai kopīgotu audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtitri reāllaikā"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitri reāllaikā"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Atvērt iestatījumus"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Cita ierīce"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Pārskata pārslēgšana"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritātes režīmi"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gatavs"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Iestatījumi"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Iestatīt"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Jūs netraucēs skaņas un vibrācija, izņemot signālus. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Pielāgot"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lēnā uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Notiek uzlāde • Laiks līdz pilnai uzlādei: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Logrīki bloķēšanas ekrānā"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Logrīks <xliff:g id="WIDGET_NAME">%1$s</xliff:g> pievienots bloķēšanas ekrānam"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Velciet pa kreisi, lai palaistu kopienas pamācību."</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Pielāgot"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Nerādīt"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> iegūs piekļuvi visai informācijai, kas ierakstīšanas vai apraides laikā tiks rādīta jūsu ekrānā vai atskaņota jūsu ierīcē. Atļauja attiecas uz tādu informāciju kā paroles, maksājumu informācija, fotoattēli, ziņojumi un jūsu atskaņotais audio saturs."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vai vēlaties sākt ierakstīšanu vai apraidi?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Pakalpojums, kas nodrošina šo funkciju, iegūs piekļuvi visai informācijai, kas ierakstīšanas vai apraides laikā tiks rādīta jūsu ekrānā vai atskaņota jūsu ierīcē. Atļauja attiecas uz tādu informāciju kā paroles, maksājumu informācija, fotoattēli, ziņojumi un jūsu atskaņotais audio saturs."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Viss ekrāns"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Viena lietotne"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Lietotnes kopīgošana vai ierakstīšana"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vai vēlaties sākt ierakstīšanu vai apraidi, izmantojot lietotni <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kopīgošanas, ierakstīšanas vai apraides laikā <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lietotnes kopīgošanas, ierakstīšanas vai apraides laikā <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Sākt"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Lietotnes kopīgošana vai ierakstīšana"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vai kopīgot ekrānu ar lietotni <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Kopīgot vienu lietotni"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kopīgot visu ekrānu"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kopīgojot visu ekrānu, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss ekrāna saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kopīgojot lietotni, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss kopīgotajā lietotnē parādītais vai atskaņotais saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kopīgot ekrānu"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> tika atspējota šī opcija"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vai sākt apraidi?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Apraides laikā Android var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lietotnes apraides laikā Android var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Sākt apraidi"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Lietotnes izvēlēšanās kopīgošanai"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vai apraidīt ekrānu?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Apraidīt vienu lietotni"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Apraidīt visu ekrānu"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Apraidot visu ekrānu, ir redzams viss ekrāna saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Apraidot lietotni, ir redzams viss attiecīgajā lietotnē rādītais vai atskaņotais. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Apraidīt ekrānu"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Lietotnes izvēlēšanās apraide"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vai sākt kopīgošanu?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kopīgošanas, ierakstīšanas vai apraides laikā Android var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Lietotnes kopīgošanas, ierakstīšanas vai apraides laikā Android var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Sākt"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Tālāk"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Pārslēdzot lietotnes, tiek pārtraukta kopīgošana."</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Tā vietā kopīgot šo lietotni"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Pārslēgties uz iepriekšējo"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelīts, labs savienojums"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelīts, ir pieejams savienojums"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelīta SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Ārkārtas izsaukumi vai ārkārtas zvani"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Darba profils"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Jautri dažiem, bet ne visiem"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Sistēmas saskarnes regulators sniedz papildu veidus, kā mainīt un pielāgot Android lietotāja saskarni. Nākamajās versijās šīs eksperimentālās funkcijas var tikt mainītas, bojātas vai to darbība var tikt pārtraukta. Turpinot esiet uzmanīgs."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Pieejamība"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Īsinājumtaustiņi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nav meklēšanas rezultātu"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Žests pāriešanai atpakaļ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Žests pāriešanai uz sākumu"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Darbību taustiņš"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Lieliski!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Attēls ar skārienpaliktni, uz kura trīs pirksti kustas pa labi un pa kreisi"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ierīces ekrāns, kurā redzama animācija ar žestu pāriešanai atpakaļ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Lai atgrieztos, ar trīs pirkstiem velciet pa kreisi vai pa labi jebkurā vietā uz skārienpaliktņa.\n\nVarat arī izmantot šim nolūkam īsinājumtaustiņus: darbību taustiņu + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Lai jebkurā brīdī pārietu uz sākuma ekrānu, ar trim pirkstiem velciet augšup no ekrāna apakšdaļas."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Lieliski!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Darbību taustiņš"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Lai piekļūtu savām lietotnēm, tastatūrā nospiediet darbību taustiņu."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Apsveicam!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Jūs pabeidzāt darbību taustiņa žesta apmācību.\n\nNospiežot darbību taustiņu un taustiņu “/”, tiks parādīti visi pieejamie īsinājumtaustiņi."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Lai pārietu uz sākuma ekrāna, ar trīs pirkstiem uz skārienpaliktņa velciet augšup."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Lai skatītu nesenās lietotnes, ar trīs pirkstiem uz skārienpaliktņa velciet augšup un turiet."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Lai skatītu visas savas lietotnes, tastatūrā nospiediet darbību taustiņu."</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Rediģēts"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Lai skatītu, atbloķējiet"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Atgriešanās, izmantojot skārienpaliktni"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Ar trīs pirkstiem velciet pa kreisi vai pa labi. Lai apgūtu citus žestus, pieskarieties šeit."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Pāriešana uz sākuma ekrānu, izmantojot skārienpaliktni"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Ar trīs pirkstiem velciet augšup un turiet. Lai apgūtu citus žestus, pieskarieties šeit."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Visu lietotņu skatīšana, izmantojot tastatūru"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Jebkurā laikā varat nospiest darbību taustiņu. Lai apgūtu citus žestus, pieskarieties šeit."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Papildu aptumšošana tagad ir iekļauta spilgtuma joslā"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Tagad varat veikt ekrāna papildu aptumšošanu, vēl vairāk samazinot spilgtumu ekrāna augšdaļā.\n\nTas darbojas vislabāk, ja esat tumšā vietā."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Noņemt papildu aptumšošanas saīsni"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Papildu aptumšošanas saīsne ir noņemta. Lai samazinātu spilgtumu, izmantojiet parasto spilgtuma joslu."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 68f9a9d..1c853df 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и други отворени апликации ја открија оваа слика од екранот."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додај во белешка"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Опфати линк"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Снимач на екран"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Се обработува снимка од екран"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Да се започне со снимање?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Додека снимате, Android има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Додека снимате апликација, Android има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Започни со снимање"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање на една апликација"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Снимање на целиот екран"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Додека го снимате целиот екран, сѐ што е прикажано на екранот се снима. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Додека снимате апликација, може да се сними сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со лозинките, деталите за плаќање, пораките, фотографиите и аудиото и видеото."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Снимај го екранот"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Изберете апликација за снимање"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Снимај аудио"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Аудио од уредот"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук од вашиот уред, како на пр., музика, повици и мелодии"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Испрати"</string>
     <string name="cancel" msgid="1089011503403416730">"Откажи"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Лого на апликацијата"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потврди"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Обиди се повторно"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Допрете за да ја откажете проверката"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Штедач на екран"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не вознемирувај"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нема достапни спарени уреди"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Допрете за да воспоставите или да прекинете врска со уред"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Споделување аудио"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Допрете за да префрлите или споделите аудио"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Зачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекини врска"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирај"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string>
     <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_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>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string>
     <string name="performance" msgid="6552785217174378320">"Изведба"</string>
     <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string>
-    <string name="thermal" msgid="6758074791325414831">"Термално"</string>
+    <string name="thermal" msgid="6758074791325414831">"Прегревање"</string>
     <string name="custom" msgid="3337456985275158299">"Приспособено"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Приспособени поставки за следење"</string>
     <string name="restore_default" msgid="5259420807486239755">"Врати на стандардно"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Автоматски титлови"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматски титлови"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отворете „Поставки“"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Друг уред"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Вклучи/исклучи преглед"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Поставки"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Поставете"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Нема да ве вознемируваат звуци и вибрации, освен од аларми. Сѐ уште ќе слушате сѐ што ќе изберете да пуштите, како музика, видеа и игри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Приспособи"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виџети на заклучен екран"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g> е додаден на заклучениот екран"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Повлечете налево за да го започнете заедничкото упатство"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Приспособете"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Отфрли"</string>
@@ -491,7 +494,7 @@
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"Икона за апликација за оневозможен виџет"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"Икона за апликација за виџет што се инсталира"</string>
     <string name="edit_widget" msgid="9030848101135393954">"Изменување виџети"</string>
-    <string name="button_to_remove_widget" msgid="3948204829181214098">"Отстранува"</string>
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"Отстрани"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Додајте виџет"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Додајте виџети"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова опфаќа податоци како лозинките, деталите за плаќање, фотографиите, пораките и аудиото што го пуштате."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Да почне снимање или емитување?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услугата што ја обезбедува функцијава ќе има пристап до сите податоци што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува податоци како лозинките, деталите за плаќање, фотографиите, пораките и аудиото што го пуштате."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Цел екран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Една апликација"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Споделување или снимање апликација"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Да почне снимање или емитување со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Кога споделувате, снимате или емитувате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Кога споделувате, снимате или емитувате апликација, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Започни"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Споделете или снимете апликација"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Да се сподели вашиот екран со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Споделете една апликација"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Споделете го целиот екран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Додека го споделувате целиот екран, сè на екранот е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Додека споделувате апликација, сѐ што се прикажува или пушта на таа апликација е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Сподели екран"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ја оневозможи опцијава"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Да се започне со емитување?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Кога емитувате, Android има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Додека емитувате апликација, Android има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со лозинки, детали за плаќање, пораки фотографии и аудио и видео."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Започни со емитување"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете апликација за споделување"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Да се емитува вашиот екран?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Емитувајте една апликација"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Емитувајте го целиот екран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Додека го емитувате целиот екран, може да се види сè што е на екранот. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Додека емитувате апликација, може да се види сѐ што се прикажува или пушта во таа апликација. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Емитувај го екранот"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Изберете апликација за емитување"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Да се започне со споделување?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Кога споделувате, снимате или емитувате, Android има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Кога споделувате, снимате или емитувате апликација, Android има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Започни"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Следно"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Споделувањето се паузира кога се префрлате на друга апликација"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Наместо тоа, споделете ја апликацијава"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Враќање"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Добра сателитска врска"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Достапна е сателитска врска"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Сателитски SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Итни повици или SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Работен профил"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Забава за некои, но не за сите"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Адаптерот на УИ на системот ви дава дополнителни начини за дотерување и приспособување на корисничкиот интерфејс на Android. Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string>
@@ -957,7 +970,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">"Инстант апликации"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Пристапност"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Кратенки од тастатура"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултати од пребарување"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Движење за назад"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Движење за почетен екран"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Копче за дејство"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Одлично сторено!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Допирната подлога покажува три прста што се движат десно и лево"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екранот на уредот покажува анимација за движење назад"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"За да се вратите назад, повлечете налево или надесно со три прста каде било на допирната подлога.\n\nЗа ова може да ја користите и кратенката од тастатурата Action + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Одлично сторено!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Го научивте движењето за враќање назад."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Одете на почетниот екран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"За да одите на вашиот почетен екран кога сакате, повлечете нагоре со три прсти од дното на екранот."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Одлично!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Го научивте движењето за враќање на почетниот екран."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Копче за дејство"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"За да пристапите до апликациите, притиснете го копчето за дејство на тастатурата."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Честитки!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Го научивте движењето со копчето за дејство.\n\nДејство + / ги прикажува сите кратенки што ги имате на располагање."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"За да одите на почетниот екран, повлечете нагоре со три прста на допирната подлога"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"За да ги видите скорешните апликации, повлечете нагоре и задржете со три прста на допирната подлога"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Притиснете го копчето за дејство на тастатурата за да ги видите сите апликации"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Редактирано"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Отклучете за приказ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Користете ја допирната подлога за да се вратите назад"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Повлечете налево или надесно со три прста. Допрете за да научите повеќе движења."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Користете ја допирната подлога за да одите на почетниот екран"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Повлечете нагоре и задржете со три прста. Допрете за да научите повеќе движења."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користете ја тастатурата за да ги видите сите апликации"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притиснете го копчето за дејство кога сакате. Допрете за да научите повеќе движења."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Отсега „Дополнително затемнување“ е дел од лентата за осветленост"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Отсега може да го затемнувате екранот дополнително со намалување на нивото на осветленост од горниот дел на екранот.\n\nОва функционира најдобро кога сте во темна средина."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Отстрани ја кратенката за „Дополнително затемнување“"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Кратенката за „Дополнително затемнување“ е отстранета. Користете ја стандардната лента за осветленост за да ја намалите осветленоста."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 8711604..ef63d67 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> എന്ന ആപ്പും തുറന്നിരിക്കുന്ന മറ്റ് ആപ്പും ഈ സ്ക്രീൻഷോട്ട് തിരിച്ചറിഞ്ഞു."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"കുറിപ്പിലേക്ക് ചേർക്കുക"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ലിങ്ക് ഉൾപ്പെടുത്തുക"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"സ്ക്രീൻ റെക്കോർഡർ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"സ്ക്രീൻ റെക്കോർഡിംഗ് പ്രോസസുചെയ്യുന്നു"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"റെക്കോർഡ് ചെയ്യൽ ആരംഭിക്കണോ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"റെക്കോർഡ് ചെയ്യുമ്പോൾ, Android-ന് സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"നിങ്ങൾ ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുമ്പോൾ, Android-ന് ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"റെക്കോർഡ് ചെയ്യൽ ആരംഭിക്കുക"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"നിങ്ങളുടെ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യണോ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"നിങ്ങളുടെ സ്ക്രീൻ പൂർണ്ണമായി റെക്കോർഡ് ചെയ്യുമ്പോൾ, സ്ക്രീനിൽ ദൃശ്യമാകുന്ന എല്ലാം റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"നിങ്ങളുടെ ആപ്പ് റെക്കോർഡ് ചെയ്യുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും റെക്കോർഡ് ചെയ്യപ്പെടും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"സ്ക്രീൻ റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"റെക്കോർഡ് ചെയ്യാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ഓഡിയോ റെക്കോർഡ് ചെയ്യുക"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ഉപകരണത്തിന്റെ ഓഡിയോ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"സംഗീതം, കോളുകൾ, റിംഗ്‌ടോണുകൾ എന്നിവപോലെ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്നുള്ള ശബ്ദം"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"അയയ്ക്കുക"</string>
     <string name="cancel" msgid="1089011503403416730">"റദ്ദാക്കുക"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ആപ്പ് ലോഗോ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"സ്ഥിരീകരിക്കുക"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"വീണ്ടും ശ്രമിക്കുക"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"പരിശോധിച്ചുറപ്പിക്കൽ റദ്ദാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"സ്ക്രീൻ സേവർ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ഇതർനെറ്റ്"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ശല്യപ്പെടുത്തരുത്"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"മുൻഗണനാ മോഡുകൾ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ജോടിയാക്കിയ ഉപകരണങ്ങളൊന്നും ലഭ്യമല്ല"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ഒരു ഉപകരണം കണക്റ്റ് ചെയ്യാനോ വിച്ഛേദിക്കാനോ ടാപ്പ് ചെയ്യുക"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്‌റ്റ് ചെയ്‌തു"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ഓഡിയോ പങ്കിടൽ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ഓഡിയോ മാറാനോ പങ്കിടാനോ ടാപ്പ് ചെയ്യുക"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"സംരക്ഷിച്ചു"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"വിച്ഛേദിക്കുക"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"സജീവമാക്കുക"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"തത്സമയ ക്യാപ്‌ഷനുകൾ"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"തത്സമയ ക്യാപ്ഷൻ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ക്രമീകരണം തുറക്കുക"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"മറ്റ് ഉപകരണം"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"അവലോകനം മാറ്റുക"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"മുൻഗണനാ മോഡുകൾ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ശരി"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ക്രമീകരണം"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"സജ്ജീകരിക്കുക"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"അലാറങ്ങളിൽ നിന്നുള്ള ശബ്‌ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് ‌തുടർന്നും കേൾക്കാൻ കഴിയും."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • പതുക്കെ ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ചാർജ് ചെയ്യുന്നു • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-ൽ പൂർത്തിയാകും"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ലോക്ക് സ്‌ക്രീനിൽ വിജറ്റുകൾ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"ലോക്ക് സ്‌ക്രീനിലേക്ക് <xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ് ചേർത്തു"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"കമ്മ്യൂണൽ ട്യൂട്ടോറിയൽ ആരംഭിക്കാൻ ഇടത്തോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ഡിസ്‌മിസ് ചെയ്യുക"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"ഈ സ്‌പെയ്‌സിൽ നിങ്ങളുടെ വിജറ്റുകൾ ചേർക്കുക, നീക്കം ചെയ്യുക, പുനഃക്രമീകരിക്കുക"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"വിജറ്റുകൾ ചേർക്കുക, നീക്കം ചെയ്യുക, പുനഃക്രമീകരിക്കുക"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"കൂടുതൽ വിജറ്റുകൾ ചേർക്കുക"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കാൻ ദീർഘനേരം അമർത്തുക"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"വിജറ്റുകൾ ഇഷ്ടാനുസൃതമാക്കുക"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്‌റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആക്‌സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, പാസ്‌വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"റെക്കോർഡ് ചെയ്യുമ്പോഴോ കാസ്‌റ്റ് ചെയ്യുമ്പോഴോ നിങ്ങളുടെ ഉപകരണത്തിൽ നിന്ന് പ്ലേ ചെയ്യുന്നതോ നിങ്ങളുടെ സ്‌ക്രീനിൽ ദൃശ്യമാകുന്നതോ ആയ എല്ലാ വിവരങ്ങളിലേക്കും ഈ ഫംഗ്‌ഷൻ ലഭ്യമാക്കുന്ന സേവനത്തിന് ആക്‌സസ് ഉണ്ടായിരിക്കും. നിങ്ങൾ പ്ലേ ചെയ്യുന്ന ഓഡിയോ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, പാസ്‌വേഡുകൾ എന്നിവ പോലുള്ള വിവരങ്ങൾ ഇതിൽ ഉൾപ്പെടുന്നു."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"മുഴുവൻ സ്‌ക്രീൻ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ഒറ്റ ആപ്പ്"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ഒരു ആപ്പ് പങ്കിടുക അല്ലെങ്കിൽ റെക്കോർഡ് ചെയ്യുക"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ഉപയോഗിച്ച് റെക്കോർഡ് ചെയ്യൽ അല്ലെങ്കിൽ കാസ്‌റ്റ് ചെയ്യൽ ആരംഭിക്കണോ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ഒരു ആപ്പ് പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആ ആപ്പിൽ കാണിക്കുന്ന അല്ലെങ്കിൽ പ്ലേ ചെയ്യുന്ന എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ആരംഭിക്കുക"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ഒരു ആപ്പ് പങ്കിടുക അല്ലെങ്കിൽ റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"നിങ്ങളുടെ സ്ക്രീൻ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതുമായി പങ്കിടണോ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ആപ്പ് പങ്കിടുക"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"സ്ക്രീൻ പൂർണമായും പങ്കിടുക"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"നിങ്ങളുടെ സ്ക്രീൻ മുഴുവനായി പങ്കിടുമ്പോൾ, സ്ക്രീനിലെ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"നിങ്ങളുടെ ആപ്പ് പങ്കിടുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"സ്‌ക്രീൻ പങ്കിടുക"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഈ ഓപ്‌ഷൻ പ്രവർത്തനരഹിതമാക്കി"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"കാസ്റ്റ് ചെയ്യാൻ ആരംഭിക്കണോ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"നിങ്ങൾ കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"നിങ്ങൾ ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"കാസ്റ്റ് ചെയ്യാൻ ആരംഭിക്കുക"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"പങ്കിടാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"നിങ്ങളുടെ സ്ക്രീൻ കാസ്റ്റ് ചെയ്യണോ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുക"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"മുഴുവൻ സ്‌ക്രീനും കാസ്റ്റ് ചെയ്യുക"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"നിങ്ങളുടെ മുഴുവൻ സ്ക്രീനും കാസ്റ്റ് ചെയ്യുമ്പോൾ, സ്ക്രീനിലെ എല്ലാ കാര്യങ്ങളും ദൃശ്യമാകും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധിക്കുക."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"നിങ്ങൾ ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും ദൃശ്യമാകും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധിക്കുക."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"സ്ക്രീൻ കാസ്‌റ്റ് ചെയ്യുക"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"കാസ്റ്റ് ചെയ്യാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"പങ്കിടൽ ആരംഭിക്കണോ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ഒരു ആപ്പ് പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് ആ ആപ്പിൽ കാണിക്കുന്ന അല്ലെങ്കിൽ പ്ലേ ചെയ്യുന്ന എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ആരംഭിക്കുക"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"അടുത്തത്"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"നിങ്ങൾ ആപ്പുകൾ മാറുമ്പോൾ പങ്കിടൽ താൽക്കാലികമായി നിർത്തും"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"പകരം ഈ ആപ്പ് പങ്കിടുക"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"മടങ്ങുക"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"സാറ്റലൈറ്റ്, മികച്ച കണക്ഷൻ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"സാറ്റലൈറ്റ്, കണക്ഷൻ ലഭ്യമാണ്"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"സാറ്റലൈറ്റ് SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"എമർജൻസി കോൾ അല്ലെങ്കിൽ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ചിലർക്ക് വിനോദം, എന്നാൽ എല്ലാവർക്കുമില്ല"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Android ഉപയോക്തൃ ഇന്റർഫേസ് ആവശ്യമുള്ള രീതിയിൽ മാറ്റുന്നതിനും ഇഷ്ടാനുസൃതമാക്കുന്നതിനും സിസ്റ്റം UI ട്യൂണർ നിങ്ങൾക്ക് അധിക വഴികൾ നൽകുന്നു. ഭാവി റിലീസുകളിൽ ഈ പരീക്ഷണാത്മക ഫീച്ചറുകൾ മാറ്റുകയോ നിർത്തുകയോ അപ്രത്യക്ഷമാവുകയോ ചെയ്തേക്കാം. ശ്രദ്ധയോടെ മുന്നോട്ടുപോകുക."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ഉപയോഗസഹായി"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"കീബോഡ് കുറുക്കുവഴികൾ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"തിരയൽ ഫലങ്ങളൊന്നുമില്ല"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"\'മടങ്ങുക\' ജെസ്ച്ചർ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ഹോം ജെസ്‌ച്ചർ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ആക്ഷൻ കീ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"കൊള്ളാം!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"വലത്തേക്കും ഇടത്തേക്കും ചലിക്കുന്ന മൂന്ന് വിരലുകൾ കാണിക്കുന്ന ടച്ച്പാഡ്"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ബാക്ക് ജെസ്ച്ചറിനായി ആനിമേഷൻ കാണിക്കുന്ന ഉപകരണ സ്‌ക്രീൻ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"തിരികെ പോകാൻ, ടച്ച്പാഡിൽ എവിടെയെങ്കിലും മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക.\n\nഇതിന് Action + ESC കീബോഡ് കുറുക്കുവഴികളും നിങ്ങൾക്ക് ഉപയോഗിക്കാം."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"കൊള്ളാം!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ഹോമിലേക്ക് പോകൂ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ഏതുസമയത്തും ഹോം സ്ക്രീനിലേക്ക് പോകാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് സ്ക്രീനിന്റെ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യൂ."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"കൊള്ളാം!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ഹോമിലേക്ക് പോകുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action കീ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"നിങ്ങളുടെ ആപ്പുകൾ ആക്‌സസ് ചെയ്യാൻ, നിങ്ങളുടെ കീബോർഡിലെ Action കീ അമർത്തുക."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"അഭിനന്ദനങ്ങൾ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"നിങ്ങൾ Action കീ ജെസ്ച്ചർ പൂർത്തിയാക്കി.\n\nനിങ്ങൾക്ക് ലഭ്യമാകുന്ന എല്ലാ കുറുക്കുവഴികളും Action + / കാണിക്കുന്നു."</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>
@@ -1400,12 +1430,18 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ഹോമിലേക്ക് പോകാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ടച്ച്‌പാഡിൽ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ കാണാൻ, മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് ടച്ച്‌പാഡിൽ സ്വൈപ്പ് ചെയ്ത് പിടിക്കുക"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"എല്ലാ ആപ്പുകളും കാണാൻ, നിങ്ങളുടെ കീബോർഡിലെ ആക്‌ഷൻ കീ അമർത്തുക"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"മറച്ചത്"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"കാണാൻ, അൺലോക്ക് ചെയ്യുക"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"തിരികെ പോകാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
-    <string name="back_edu_notification_content" msgid="2497557451540954068">"മൂന്ന് വിരലുകളാൽ ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പുചെയ്യൂ. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="back_edu_notification_content" msgid="2497557451540954068">"മൂന്ന് വിരലുകൾ കൊണ്ട് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യൂ. കൂടുതൽ ജെസ്ച്ചറുകളറിയാൻ ടാപ്പ് ചെയ്യൂ."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ഹോമിലേക്ക് പോകാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
     <string name="home_edu_notification_content" msgid="6631697734535766588">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="overview_edu_notification_title" msgid="1265824157319562406">"അടുത്തിടെ ഉപയോഗിച്ച ആപ്പുകൾ കാണാൻ നിങ്ങളുടെ ടച്ച്‌പാഡ് ഉപയോഗിക്കുക"</string>
-    <string name="overview_edu_notification_content" msgid="3578204677648432500">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="overview_edu_notification_content" msgid="3578204677648432500">"മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്‌ത് പിടിക്കുക. കൂടുതൽ ജെസ്ച്ചറുകളറിയാൻ ടാപ്പ് ചെയ്യൂ."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"എല്ലാ ആപ്പുകളും കാണാൻ നിങ്ങളുടെ കീബോർഡ് ഉപയോഗിക്കുക"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ഏതുസമയത്തും ആക്ഷൻ കീ അമർത്തുക. കൂടുതൽ ജെസ്ച്ചറുകൾ മനസ്സിലാക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"കൂടുതൽ ഡിം ചെയ്യൽ, ഇപ്പോൾ തെളിച്ചം ബാറിന്റെ ഭാഗമാണ്"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"മുകളിൽ നിന്ന് തെളിച്ചം കുറയ്ക്കുന്നതിലൂടെ നിങ്ങൾക്ക് ഇപ്പോൾ സ്‌ക്രീൻ കൂടുതൽ മങ്ങിക്കാൻ കഴിയും.\n\nനിങ്ങൾ ഇരുണ്ട മുറിയിലായിരിക്കുമ്പോൾ ഇത് മികച്ച രീതിയിൽ പ്രവർത്തിക്കുന്നു."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"കൂടുതൽ ഡിം ചെയ്യൽ കുറുക്കുവഴി നീക്കം ചെയ്യുക"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"കൂടുതൽ ഡിം ചെയ്യാനുള്ള കുറുക്കുവഴി നീക്കം ചെയ്തു. തെളിച്ചം കുറയ്ക്കാൻ, സാധാരണ \'തെളിച്ചം ബാർ\' ഉപയോഗിക്കുക."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 95b4e3f..ff8c52b 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> болон бусад нээлттэй апп энэ дэлгэцийн агшныг илрүүлсэн."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Тэмдэглэлд нэмэх"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Холбоосыг оруулах"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Дэлгэцийн үйлдэл бичигч"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Дэлгэц бичлэг боловсруулж байна"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Бичиж эхлэх үү?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Таныг бичиж байх үед Android нь таны дэлгэцэд харуулсан эсвэл төхөөрөмжид тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Таныг апп бичиж байх үед Android тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Бичиж эхлэх"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Дэлгэцээ бичих үү?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Нэг аппыг бичих"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Бүтэн дэлгэцийг бичих"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Таныг бүтэн дэлгэцээ бичиж байхад дэлгэц дээр тань харуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Таныг апп бичиж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйлийг бичдэг. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Дэлгэцийг бичих"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Бичих апп сонгох"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Аудио бичих"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Төхөөрөмжийн аудио"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Хөгжим, дуудлага болон хонхны ая зэрэг таны төхөөрөмжийн дуу"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Илгээх"</string>
     <string name="cancel" msgid="1089011503403416730">"Цуцлах"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Аппын лого"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Баталгаажуулах"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Дахин оролдох"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Нотолгоог цуцлахын тулд товшино уу"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Дэлгэц амраагч"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Этернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Бүү саад бол"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Чухал байдлаар нь ангилах горим"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Хослуулсан төхөөрөмж байхгүй"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Төхөөрөмжийг холбох эсвэл салгахын тулд товшино уу"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Аудио хуваалцах"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Аудиог сэлгэх эсвэл хуваалцахын тулд товшино уу"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Хадгалсан"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"салгах"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"идэвхжүүлэх"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Шууд тайлбар"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Шууд тайлбар"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Тохиргоог нээх"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Бусад төхөөрөмж"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Тоймыг асаах/унтраах"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Чухал байдлаар нь ангилах горим"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Болсон"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Тохиргоо"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Тохируулах"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Танд сэрүүлгээс бусад дуу, чичиргээ саад болохгүй. Та хөгжим, видео, тоглоом зэрэг тоглуулахыг хүссэн бүх зүйлээ сонсох боломжтой хэвээр байна."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Тохируулах"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Түгжээтэй дэлгэц дээрх виджетүүд"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетийг түгжээтэй дэлгэцэд нэмсэн"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Нийтийн практик хичээлийг эхлүүлэхийн тулд зүүн тийш шударна уу"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Өөрчлөх"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Хаах"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэцэд харуулсан эсвэл таны төхөөрөмжөөс тоглуулсан бүх мэдээлэлд хандах эрхтэй байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг, мессеж болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Энэ функцийг олгож буй үйлчилгээ нь бичлэг хийж эсвэл дамжуулж байх үед таны дэлгэцэд харуулсан эсвэл төхөөрөмжөөс тань тоглуулсан бүх мэдээлэлд хандах эрхтэй. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Бүтэн дэлгэц"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Нэг апп"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Апп хуваалцах эсвэл бичих"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-р бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Таныг хуваалцаж, бичиж эсвэл дамжуулж байх үед <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь таны дэлгэцэд харагдаж буй зүйл эсвэл төхөөрөмжид тань тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Таныг хуваалцаж, бичлэг хийж эсвэл апп дамжуулж байх үед <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио болон видео зэрэг бусад зүйлд болгоомжтой хандаарай."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Эхлүүлэх"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Апп хуваалцах эсвэл бичих"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Дэлгэцээ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай хуваалцах уу?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Нэг апп хуваалцах"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дэлгэцийг бүтнээр нь хуваалцах"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Таныг дэлгэцээ бүхэлд нь хуваалцаж байхад дэлгэц дээр тань байгаа аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Таныг апп хуваалцаж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дэлгэцийг хуваалцах"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> энэ сонголтыг идэвхгүй болгосон"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Дамжуулж эхлэх үү?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Таныг дамжуулж байх үед Android таны дэлгэцэд харагдаж буй эсвэл төхөөрөмжид тань тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Таныг апп дамжуулж байх үед Android тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Дамжуулж эхлэх"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Хуваалцах апп сонгох"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Дэлгэцээ дамжуулах уу?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Нэг апп дамжуулах"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Дэлгэцийг бүхэлд нь дамжуулах"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Та дэлгэцээ бүхэлд нь дамжуулах үед дэлгэц дээр тань байгаа аливаа зүйл харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Та апп дамжуулах үед тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Дэлгэц дамжуулах"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Дамжуулах апп сонгох"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Хуваалцаж эхлэх үү?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Таныг хуваалцаж, бичлэг хийж эсвэл дамжуулж байх үед Android таны дэлгэцэд харуулсан эсвэл төхөөрөмжид тань тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Таныг хуваалцаж, бичлэг хийж эсвэл дамжуулж байх үед Android тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио болон видео зэрэг бусад зүйлд болгоомжтой хандаарай."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Эхлүүлэх"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Дараах"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Таныг аппууд сэлгэх үед хуваалцахыг түр зогсооно"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Оронд нь энэ аппыг хуваалцах"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Буцаж сэлгэх"</string>
@@ -566,7 +578,7 @@
     <string name="empty_shade_text" msgid="8935967157319717412">"Мэдэгдэл байхгүй"</string>
     <string name="no_unseen_notif_text" msgid="395512586119868682">"Шинэ мэдэгдэл алга"</string>
     <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"Мэдэгдлийн хөргөлт асаалттай байна"</string>
-    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Таныг хэт олон мэдэгдэл нэг дор авахад таны төхөөрөмжийн дууны түвшин болон сэрэмжлүүлэг/дохиог 2 хүртэлх минутын турш автоматаар багасгадаг."</string>
+    <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Таныг хэт олон мэдэгдэл нэг дор авахад таны төхөөрөмжийн дууны түвшин болон дохиог 2 хүртэлх минутын турш автоматаар багасгадаг."</string>
     <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Унтраах"</string>
     <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Хуучин мэдэгдлийг харах бол түгжээг тайл"</string>
     <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Энэ төхөөрөмжийг таны эцэг эх удирддаг"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хиймэл дагуул, холболт сайн байна"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Хиймэл дагуул, холболт боломжтой"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хиймэл дагуул SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Яаралтай дуудлага эсвэл SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Ажлын профайл"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Зарим хүнд хөгжилтэй байж болох ч бүх хүнд тийм биш"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Системийн UI Tохируулагч нь Android хэрэглэгчийн интерфэйсийг тааруулах, өөрчлөх нэмэлт аргыг зааж өгөх болно. Эдгээр туршилтын тохиргоо нь цаашид өөрчлөгдөх, эвдрэх, алга болох магадлалтай. Үйлдлийг болгоомжтой хийнэ үү."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Хандалт"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Товчлуурын шууд холбоос"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ямар ч хайлтын илэрц байхгүй"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Буцах зангаа"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Үндсэн нүүрний зангаа"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Тусгай товчлуур"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Болсон"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Үнэхээр сайн ажиллалаа!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Буцах"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Гурван хуруу баруун болон зүүн тийш хөдөлж буйг харуулсан мэдрэгч самбар"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Буцах зангаанд зориулсан анимацийг харуулсан төхөөрөмжийн дэлгэц"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Буцахын тулд мэдрэгч самбар дээр гурван хуруугаар хүссэн газраа зүүн эсвэл баруун тийш шударна уу.\n\nТа мөн үүнийг хийхэд Action + ESC товчлуурын шууд холбоосыг ашиглах боломжтой."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Үнэхээр сайн ажиллалаа!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Та буцах зангааг гүйцэтгэлээ."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Үндсэн нүүр лүү очих"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Үндсэн нүүр лүүгээ хүссэн үедээ очихын тулд дэлгэцийнхээ доод талаас гурван хуруугаараа дээш шударна уу."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Янзтай!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Та үндсэн нүүр лүү очих зангааг гүйцэтгэлээ."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Тусгай товчлуур"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Аппууддаа хандахын тулд гар дээр тань байх тусгай товчлуурыг дарна уу."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Баяр хүргэе!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Та тусгай товчлуурын зангааг гүйцэтгэлээ.\n\nТусгай товчлуур болох + / нь танд боломжтой бүх товчлолыг харуулна."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Нүүр хуудас руу очихын тулд мэдрэгч самбар дээр гурван хуруугаараа дээш шударна уу"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Саяхны аппуудыг харахын тулд мэдрэгч самбар дээр гурван хуруугаараа дээш шудраад, удаан дарна уу"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Бүх аппаа харахын тулд гар дээр тань байх тусгай товчлуурыг дарна уу"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Хассан"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Харахын тулд түгжээг тайлна уу"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Буцахын тулд мэдрэгч самбараа ашиглах"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Гурван хуруугаараа зүүн эсвэл баруун тийш шударна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Нүүр хуудас руу очихын тулд мэдрэгч самбараа ашиглах"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Гурван хуруугаа ашиглан дээш шудраад, удаан дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Бүх аппыг харахын тулд гараа ашиглах"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Тусгай товчлуурыг хүссэн үедээ дарна уу. Илүү олон зангаа сурахын тулд товшино уу."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Хэт бүүдгэр онцлог одоо гэрэлтүүлгийн самбарын нэг хэсэг боллоо"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Та одоо дэлгэцийнхээ дээд талаас гэрэлтүүлгийн түвшнийг бүр илүү багасгаснаар дэлгэцийг хэт бүүдгэр болгох боломжтой.\n\nЭнэ нь таныг харанхуй орчинд байхад хамгийн сайн ажилладаг."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Хэт бүүдгэр онцлогийн товчлолыг хасах"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Хэт бүүдгэр онцлогийн товчлолыг хассан. Гэрэлтүүлгээ багасгахын тулд энгийн гэрэлтүүлгийн самбарыг ашиглана уу."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index e3510ad..76515d2 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> आणि उघडलेल्या इतर अ‍ॅप्सनी हा स्क्रीनशॉट डिटेक्ट केला."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"टीप जोडा"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"लिंकचा समावेश करा"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"स्क्रीन रेकॉर्डर"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रीन रेकॉर्डिंग प्रोसेस सुरू"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"रेकॉर्डिंग सुरू करायचे आहे का?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"तुम्ही रेकॉर्ड करत असताना, Android ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"तुम्ही एखादे ॲप रेकॉर्ड करत असताना, Android ला त्या ॲपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा ॲक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"रेकॉर्डिंग सुरू करा"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तुमची स्क्रीन रेकॉर्ड करायची आहे का?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूर्ण स्क्रीन रेकॉर्ड करा"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तुम्ही तुमची पूर्ण स्क्रीन रेकॉर्ड करता, तेव्हा तुमच्या स्क्रीनवर दाखवलेली कोणतीही गोष्टी रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तुम्ही अ‍ॅप रेकॉर्ड करता, तेव्हा त्या अ‍ॅपमध्ये दाखवलेली किंवा प्ले केलेली कोणतीही गोष्ट रेकॉर्ड केली जाते. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रीन रेकॉर्ड करा"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रेकॉर्ड करण्यासाठी अ‍ॅप निवडा"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ऑडिओ रेकॉर्ड करा"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"डिव्हाइस ऑडिओ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"तुमच्या डिव्हाइसवरील आवाज, जसे की संगीत, कॉल आणि रिंगटोन"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पाठवा"</string>
     <string name="cancel" msgid="1089011503403416730">"रद्द करा"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"अ‍ॅप लोगो"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कन्फर्म करा"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"पुन्हा प्रयत्न करा"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रीन सेव्हर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"इथरनेट"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"व्यत्यय आणू नका"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"प्राधान्य मोड"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लूटूथ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"कोणतेही जोडलेले डिव्हाइसेस उपलब्ध नाहीत"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"डिव्हाइस कनेक्ट किंवा डिस्कनेक्ट करण्यासाठी टॅप करा"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्‍लूटूथ वापरा"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ऑडिओ शेअरिंग"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"व्हिडिओवर स्विच करण्यासाठी टॅप करा किंवा शेअर करा"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेव्ह केले"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट करा"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ॲक्टिव्हेट करा"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string>
     <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_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>
@@ -389,7 +394,7 @@
     <string name="user_interface" msgid="3712869377953950887">"यूझर इंटरफेस"</string>
     <string name="thermal" msgid="6758074791325414831">"थर्मल"</string>
     <string name="custom" msgid="3337456985275158299">"कस्टम"</string>
-    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"मागाच्या कस्टम सेटिंग्ज"</string>
+    <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"ट्रेससाठी कस्टम सेटिंग्ज"</string>
     <string name="restore_default" msgid="5259420807486239755">"डीफॉल्ट रिस्टोअर करा"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"लाइव्ह कॅप्शन"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव्ह कॅप्शन"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिंग्ज उघडा"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"इतर डिव्हाइस"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"अवलोकन टॉगल करा."</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"प्राधान्य मोड"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"पूर्ण झाले"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिंग्ज"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"सेट करा"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"अलार्म व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"सानुकूलित करा"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • हळू चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज होत आहे • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मध्ये पूर्ण होईल"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लॉक स्क्रीनवरील विजेट"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट लॉक स्‍क्रीनवर जोडले आहे"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"सामुदायिक ट्यूटोरियल सुरू करण्यासाठी डावीकडे स्वाइप करा"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"कस्टमाइझ करा"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"डिसमिस करा"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला तुमच्या स्क्रीनवर दाखवलेल्या अथवा तुमच्या डिव्हाइसवर प्ले केलेल्या सर्व माहितीचा अ‍ॅक्सेस असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले करत असलेला ऑडिओ यासारख्या माहितीचा समावेश आहे."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"रेकॉर्ड किंवा कास्ट करणे सुरू करायचे आहे का ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"रेकॉर्ड किंवा कास्ट करत असताना, हे कार्य पुरवणाऱ्या सेवेला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या सर्व माहितीचा अ‍ॅक्सेस असेल. यामध्ये पासवर्ड, पेमेंट तपशील, फोटो, मेसेज आणि तुम्ही प्ले करत असलेला ऑडिओ यासारख्या माहितीचा समावेश आहे."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"संपूर्ण स्क्रीन"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"एक अ‍ॅप"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"अ‍ॅप शेअर किंवा रेकॉर्ड करा"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> वापरून रेकॉर्ड किंवा कास्ट करणे सुरू करायचे आहे का?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"तुम्ही शेअर, रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"तुम्ही एखादे अ‍ॅप शेअर, रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला त्या अ‍ॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"सुरुवात करा"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"अ‍ॅप शेअर किंवा रेकॉर्ड करा"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"तुमची स्क्रीन <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> सह शेअर करायची आहे का?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एक अ‍ॅप शेअर करा"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"संपूर्ण स्क्रीन शेअर करा"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तुम्ही तुमची संपूर्ण स्क्रीन कास्ट करता, तेव्हा तुमच्या स्क्रीनवरील कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तुम्ही अ‍ॅप शेअर करता, तेव्हा त्या अ‍ॅपमध्ये दाखवल्या किंवा प्ले होणाऱ्या कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेअर करा"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा पर्याय बंद केला आहे"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"कास्ट करणे सुरू करायचे आहे का?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"तुम्ही कास्ट करत असताना, Android ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"तुम्ही एखादे अ‍ॅप कास्ट करत असताना, Android ला त्या अ‍ॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"कास्ट करणे सुरू करा"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेअर करण्यासाठी अ‍ॅप निवडा"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"तुमची स्क्रीन कास्ट करायची आहे का?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक अ‍ॅप कास्ट करा"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"संपूर्ण स्क्रीन कास्ट करा"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"तुम्ही तुमची संपूर्ण स्क्रीन कास्ट करता, तेव्हा तुमच्या स्क्रीनवरील सर्व गोष्टी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"तुम्ही अ‍ॅप कास्ट करता, तेव्हा त्या अ‍ॅपमध्ये दाखवल्या किंवा प्ले होणाऱ्या सर्व गोष्टी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रीन कास्‍ट करा"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट करण्यासाठी ॲप निवडा"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"शेअर करणे सुरू करायचे आहे का?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"तुम्ही शेअर, रेकॉर्ड किंवा कास्ट करत असताना, Android ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"तुम्ही एखादे अ‍ॅप शेअर, रेकॉर्ड किंवा कास्ट करत असताना, Android ला त्या अ‍ॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अ‍ॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"सुरुवात करा"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"पुढील"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"तुम्ही ॲप्स स्विच करता, तेव्हा शेअरिंग थांबते"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"त्याऐवजी हे अ‍ॅप शेअर करा"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"परत स्विच करा"</string>
@@ -600,7 +612,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"आपल्या संस्थेने या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"आपल्या संस्थेने आपल्या कार्य प्रोफाइलवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"या डिव्हाइसवर प्रमाणपत्र अधिकार इंस्टॉल केला आहे. आपल्या सुरक्षित नेटवर्क रहदारीचे परीक्षण केले जाऊ शकते किंवा ती सुधारली जाऊ शकते."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"आपल्या प्रशासकाने नेटवर्क लॉगिंग सुरू केले आहे, जे आपल्या डिव्हाइसवरील रहदारीचे परीक्षण करते."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"तुमच्या ॲडमिनने नेटवर्क लॉगिंग सुरू केले आहे, जे तुमच्या डिव्हाइसवरील रहदारीचे परीक्षण करते."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"तुमच्या ॲडमिनने नेटवर्क लॉग इन सुरू केले आहे, जे तुमच्या कार्य प्रोफाइलमधील रहदारीचे निरीक्षण करत असले तरी तुमच्या वैयक्तिक प्रोफाइलमधील रहदारीचे निरीक्षण करत नाही."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"हे डिव्हाइस <xliff:g id="VPN_APP">%1$s</xliff:g> द्वारे इंटरनेटशी कनेक्ट केलेले आहे. ईमेल आणि ब्राउझिंग डेटाच्या समावेशासह, तुमची नेटवर्क अ‍ॅक्टिव्हिटी तुमच्या VPN पुरवठादाराला दिसते."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"हे डिव्हाइस <xliff:g id="VPN_APP">%1$s</xliff:g> द्वारे इंटरनेटशी कनेक्ट केलेले आहे. ईमेल आणि ब्राउझिंग डेटाच्या समावेशासह, तुमची नेटवर्क अ‍ॅक्टिव्हिटी तुमच्या आयटी ॲडमिनला दिसते."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"सॅटेलाइट, चांगले कनेक्शन"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"सॅटेलाइट, कनेक्शन उपलब्ध"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"सॅटेलाइट SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आणीबाणी कॉल किंवा SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाईल"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"सर्वांसाठी नाही तर काहींसाठी मजेदार असू शकते"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनर आपल्‍याला Android यूझर इंटरफेस ट्विक आणि कस्टमाइझ करण्‍याचे अनेक प्रकार देते. ही प्रयोगात्मक वैशिष्‍ट्ये बदलू शकतात, खंडित होऊ शकतात किंवा भविष्‍यातील रिलीज मध्‍ये कदाचित दिसणार नाहीत. सावधगिरी बाळगून पुढे सुरू ठेवा."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"अ‍ॅक्सेसिबिलिटी"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"कीबोर्ड शॉर्टकट"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कोणतेही शोध परिणाम नाहीत"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"मागे जा जेश्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेश्चर"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"अ‍ॅक्शन की"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"उत्तम कामगिरी!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"तीन बोट उजवीकडे आणि डावीकडे हलताना दाखवणारे टचपॅड"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"मागील जेश्चरसाठी अ‍ॅनिमेशन दाखवणारी डिव्हाइस स्क्रीन"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"मागे जाण्यासाठी, तीन बोटांनी टचपॅडवर कुठेही डावीकडे किंवा उजवीकडे स्वाइप करा.\n\nतुम्ही यासाठी Action + ESC हा कीबोर्ड शॉर्टकटदेखील वापरू शकता."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"उत्तम कामगिरी!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तुम्ही गो बॅक जेश्चर पूर्ण केले."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमवर जा"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"कधीही तुमच्या होम स्क्रीनवर जाण्यासाठी, तीन बोटांनी तुमच्या स्क्रीनच्या तळापासून स्वाइप करा."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"छान!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"तुम्ही गो होम जेश्चर पूर्ण केले आहे."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"अ‍ॅक्शन की"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"तुमची ॲप्स अ‍ॅक्सेस करण्यासाठी, तुमच्या कीबोर्डवरील अ‍ॅक्शन की प्रेस करा."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"अभिनंदन!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"तुम्ही अ‍ॅक्शन की जेश्चर पूर्ण केले आहे.\n\nकृती + / हे तुमच्याकडे उपलब्ध असलेले सर्व शॉर्टकट दाखवते."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"होमवर जाण्यासाठी, टचपॅडवर तीन बोटांनी वरती स्वाइप करा"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"अलीकडील ॲप्स पाहण्यासाठी, टचपॅडवर तीन बोटांनी वरती स्वाइप करा आणि धरून ठेवा"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"तुमची सर्व ॲप्स पाहण्यासाठी, तुमच्या कीबोर्डवरील अ‍ॅक्शन की प्रेस करा"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"रिडॅक्ट केलेले"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"पाहण्यासाठी अनलॉक करा"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"मागे जाण्यासाठी तुमचा टचपॅड वापरा"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"तीन बोटांनी डावीकडे किंवा उजवीकडे स्वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"होमवर जाण्यासाठी तुमचा टचपॅड वापरा"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तीन बोटांनी वरती आणि खाली स्वाइप करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सर्व ॲप्स पाहण्यासाठी तुमचा कीबोर्ड वापरा"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"अ‍ॅक्शन की कधीही प्रेस करा. आणखी जेश्चर जाणून घेण्यासाठी टॅप करा."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"आणखी डिम हे आता ब्राइटनेस बारचा भाग आहे"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"तुम्ही आता तुमच्या स्क्रीनच्या सर्वात वरून ब्राइटनेसची पातळी आणखी कमी करून स्क्रीनला आणखी डिम करू शकता.\n\nतुम्ही गडद वातावरणात असता, तेव्हा हे सर्वोत्तम कार्य करते."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"आणखी डिमचा शॉर्टकट काढून टाका"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"आणखी डिमचा शॉर्टकट काढून टाकला आहे. तुमचा ब्राइटनेस कमी करण्यासाठी, नेहमीचा ब्राइटनेस बार वापरा."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 0e91cb4..181b0c7 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan apl lain yang dibuka telah mengesan tangkapan skrin ini."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Tambahkan pada nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Sertakan pautan"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Perakam Skrin"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Memproses rakaman skrin"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Mulakan Rakaman?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Semasa anda merakam, Android boleh mengakses apa-apa sahaja yang kelihatan pada skrin atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati apabila memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Semasa anda merakam apl, Android boleh mengakses apa-apa sahaja yang ditunjukkan atau dimainkan pada apl itu. Oleh hal yang demikian, berhati-hati ketika memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Mulakan rakaman"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rakam skrin anda?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rakam satu apl"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rakam seluruh skrin"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Apabila anda merakam seluruh skrin anda, apa-apa sahaja yang dipaparkan pada skrin anda akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Apabila anda merakam apl, apa-apa sahaja yang dipaparkan atau dimainkan dalam apl tersebut akan dirakam. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rakam skrin"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Pilih apl untuk merakam"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Rakam audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio peranti"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Bunyi daripada peranti anda, seperti muzik, panggilan dan nada dering"</string>
@@ -138,7 +142,7 @@
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Anda sedang berkongsi apl"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Hentikan perkongsian"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Menghantar skrin"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Berhenti menghantar?"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Hentikan penghantaran?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Anda sedang menghantar seluruh skrin anda kepada <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Anda sedang menghantar seluruh skrin anda kepada peranti berdekatan"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Anda sedang menghantar <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> kepada <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Hantar"</string>
     <string name="cancel" msgid="1089011503403416730">"Batal"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo apl"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Sahkan"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Cuba lagi"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Ketik untuk membatalkan pengesahan"</string>
@@ -290,7 +292,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Penyelamat skrin"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Jangan Ganggu"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mod keutamaan"</string>
+    <string name="quick_settings_modes_label" msgid="879156359479504244">"Mod"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Tiada peranti berpasangan tersedia"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Ketik untuk menyambungkan atau memutuskan sambungan peranti"</string>
@@ -299,6 +301,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Perkongsian Audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Ketik untuk menukar atau berkongsi audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string>
@@ -307,6 +310,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string>
     <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_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>
@@ -378,7 +382,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rakam skrin"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Mula"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Berhenti"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekodkan Masalah"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Rakam Masalah"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Mula"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Hentikan"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Laporan Pepijat"</string>
@@ -400,7 +404,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Sari Kata Langsung"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sari Kata Langsung"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
@@ -429,10 +433,11 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buka Tetapan"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Peranti lain"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Togol Ikhtisar"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mod keutamaan"</string>
+    <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mod"</string>
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Selesai"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Tetapan"</string>
     <string name="zen_mode_on" msgid="9085304934016242591">"Hidup"</string>
+    <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Pada • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Mati"</string>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"Sediakan"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Urus dalam tetapan"</string>
@@ -475,8 +480,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas dengan perlahan • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mengecas • Penuh dalam masa <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widget pada skrin kunci"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan pada skrin kunci"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Leret ke kiri untuk memulakan tutorial umum"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Sesuaikan"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ketepikan"</string>
@@ -492,7 +496,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Tambahkan widget"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Selesai"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Tambahkan widget"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Dapatkan akses pantas kepada widget apl kegemaran anda tanpa membuka kunci tablet anda."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Dapatkan akses pantas kepada widget apl kegemaran anda tanpa perlu membuka kunci tablet."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Benarkan sebarang widget pada skrin kunci?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Buka tetapan"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Nyahjeda apl kerja?"</string>
@@ -528,22 +532,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan dapat mengakses semua maklumat yang kelihatan pada skrin anda atau yang dimainkan daripada peranti anda semasa merakam atau membuat penghantaran. Maklumat ini termasuk kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Mulakan rakaman atau penghantaran?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Perkhidmatan yang menyediakan fungsi ini boleh mengakses semua maklumat yang boleh dilihat pada skrin anda atau dimainkan daripada peranti anda semasa membuat rakaman atau penghantaran. Maklumat ini termasuk kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Seluruh skrin"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Satu apl"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Kongsi atau rakam apl"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Mulakan rakaman atau penghantaran dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Apabila anda membuat perkongsian, rakaman atau penghantaran, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> boleh mengakses apa-apa sahaja yang boleh dilihat pada skrin anda atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Apabila anda berkongsi, merakam atau menghantar apl, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> boleh mengakses apa-apa sahaja yang ditunjukan atau dimainkan pada apl tersebut. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Mula"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Kongsi atau rakam apl"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Kongsi skrin anda dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Kongsi satu apl"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Kongsi seluruh skrin"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Apabila anda berkongsi seluruh skrin anda, apa-apa sahaja kandungan pada skrin anda boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Apabila anda berkongsi apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan pada apl boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kongsi skrin"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah melumpuhkan pilihan ini"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Mulakan penghantaran?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Apabila anda membuat penghantaran, Android boleh mengakses apa-apa sahaja yang kelihatan pada skrin atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati apabila memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Apabila anda menghantar apl, Android boleh mengakses apa-apa sahaja yang ditunjukkan atau dimainkan pada apl itu. Oleh hal yang demikian, berhati-hati apabila memasukkan kata laluan, butiran pembayaran, mesej, foto, audio dan video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Mulakan penghantaran"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih apl untuk dikongsi"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Hantar skrin anda?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Hantar satu apl"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Hantar keseluruhan skrin"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Apabila anda menghantar seluruh skrin anda, apa-apa sahaja kandungan pada skrin anda boleh dilihat. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Apabila anda menghantar apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan pada apl itu boleh dilihat. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Hantar skrin"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Pilih apl untuk membuat penghantaran"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Mulakan perkongsian?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Apabila anda membuat perkongsian, rakaman atau penghantaran, Android boleh mengakses apa-apa sahaja yang boleh dilihat pada skrin anda atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Apabila anda berkongsi, merakam atau menghantar apl, Android boleh mengakses apa-apa sahaja yang ditunjukan atau dimainkan pada apl tersebut. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Mula"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Seterusnya"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Perkongsian dijeda apabila anda menukar apl"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Sebaliknya kongsi apl ini"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Tukar kembali"</string>
@@ -711,6 +724,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, sambungan yang baik"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, sambungan tersedia"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via Satelit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Panggilan kecemasan atau SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil kerja"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Menarik untuk sesetengah orang tetapi bukan untuk semua"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Penala UI Sistem memberi anda cara tambahan untuk mengolah dan menyesuaikan antara muka Android. Ciri eksperimen ini boleh berubah, rosak atau hilang dalam keluaran masa hadapan. Teruskan dengan berhati-hati."</string>
@@ -1373,21 +1387,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Kebolehaksesan"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan papan kekunci"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Tiada hasil carian"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gerak isyarat kembali"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gerak isyarat pergi ke laman utama"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Kekunci tindakan"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Syabas!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Pad sentuh menunjukkan tiga jari bergerak ke kanan dan kiri"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrin peranti menunjukkan animasi untuk gerak isyarat kembali"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Untuk kembali, leret ke kiri atau ke kanan menggunakan tiga jari di mana-mana sahaja pada pad sentuh.\n\nAnda juga boleh menggunakan pintasan papan kekunci Action + ESC untuk kembali."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Syabas!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Untuk mengakses skrin utama anda pada bila-bila masa, leret ke atas menggunakan tiga jari daripada bahagian bawah skrin anda."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bagus!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Anda telah melengkapkan gerak isyarat akses laman utama."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Kekunci tindakan"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Untuk mengakses semua apl anda, tekan kekunci tindakan pada papan kekunci anda."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tahniah!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Anda telah melengkapkan gerak isyarat kekunci tindakan.\n\nTindakan + / menunjukkan semua pintasan anda yang tersedia."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string>
@@ -1397,6 +1428,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Untuk mengakses laman utama, leret ke atas dengan tiga jari pada pad sentuh"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Untuk melihat apl terbaharu, leret ke atas dan tahan dengan tiga jari pada pad sentuh"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Untuk melihat semua apl anda, tekan kekunci tindakan pada papan kekunci anda"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Disunting"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Buka kunci untuk melihat"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Gunakan pad sentuh anda untuk kembali"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Leret ke kiri atau ke kanan dengan tiga jari. Ketik dan ketahui lebih lanjut tentang gerak isyarat."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Gunakan pad sentuh untuk mengakses laman utama"</string>
@@ -1405,4 +1438,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Leret ke atas, tahan dengan tiga jari. Ketik untuk mengetahui lebih lanjut tentang gerak isyarat."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gunakan papan kekunci anda untuk melihat semua apl"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tekan kekunci tindakan pada bila-bila masa. Ketik dan ketahui lebih lanjut tentang gerak isyarat."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Kini ciri amat malap merupakan sebahagian daripada bar kecerahan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Kini anda boleh menjadikan skrin amat malap dengan merendahkan tahap kecerahan lebih jauh daripada bahagian atas skrin anda.\n\nCiri ini berfungsi paling baik apabila anda berada dalam persekitaran yang gelap."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Alih keluar pintasan amat malap"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Pintasan amat malap dialih keluar. Untuk mengurangkan kecerahan anda, gunakan bar kecerahan biasa."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index d7b20ef..afc57ac 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> နှင့် အခြားဖွင့်ထားသော အက်ပ်များက ဤဖန်သားပြင်ဓာတ်ပုံကို တွေ့ရှိသည်။"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"မှတ်စုတွင် ထည့်ရန်"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"လင့်ခ်ထည့်သွင်းရန်"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ဖန်သားပြင်ရိုက်ကူးစက်"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"စကရင်ရိုက်ကူးမှု အပြီးသတ်နေသည်"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ရုပ်သံဖမ်းခြင်း စတင်မလား။"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ရုပ်သံဖမ်းနေစဉ် Android သည် သင့်ဖန်သားပြင်တွင် မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"အက်ပ်တစ်ခုကို ရုပ်သံဖမ်းနေစဉ် Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ရုပ်သံ စဖမ်းရန်"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ဖန်သားပြင်ကို ရိုက်ကူးမလား။"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"အက်ပ်တစ်ခုကို ရိုက်ကူးရန်"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ဖန်သားပြင်တစ်ခုလုံးကို ရိုက်ကူးရန်"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"သင့်ဖန်သားပြင်တစ်ခုလုံး ရိုက်ကူးနေချိန်တွင် ဖန်သားပြင်တွင် ပြထားသည့် အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"အက်ပ်ကို ရိုက်ကူးနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို ရိုက်ကူးသည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ဖန်သားပြင်ကို ရိုက်ကူးရန်"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ရိုက်ကူးရန် အက်ပ်ရွေးခြင်း"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"အသံဖမ်းရန်"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"စက်ပစ္စည်းအသံ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"သီချင်း၊ ဖုန်းခေါ်ဆိုမှုနှင့် ဖုန်းမြည်သံကဲ့သို့ သင့်စက်ပစ္စည်းမှ အသံ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ပို့ရန်"</string>
     <string name="cancel" msgid="1089011503403416730">"မလုပ်တော့"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"အက်ပ်လိုဂို"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"အတည်ပြုရန်"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ထပ်စမ်းကြည့်ရန်"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"အထောက်အထားစိစစ်ခြင်းကို ပယ်ဖျက်ရန် တို့ပါ"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"စခရင်နားချိန်ပုံ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"အီသာနက်"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"မနှောင့်ယှက်ရ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ဦးစားပေးမုဒ်"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ဘလူးတုသ်"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ချိတ်တွဲထားသည့် ကိရိယာများ မရှိ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"စက်ကို ချိတ်ဆက်ရန် (သို့) ချိတ်ဆက်မှုဖြုတ်ရန် တို့ပါ"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"အော်ဒီယို မျှဝေခြင်း"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"အသံ ပြောင်းရန်/မျှဝေရန် တို့ပါ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"သိမ်းထားသည်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ချိတ်ဆက်မှုဖြုတ်ရန်"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"စသုံးရန်"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"တိုက်ရိုက်စာတန်း"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"တိုက်ရိုက်စာတန်း"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
@@ -429,10 +434,12 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ဆက်တင်များဖွင့်ရန်"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"အခြားစက်ပစ္စည်း"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ဖွင့်၊ ပိတ် အနှစ်ချုပ်"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ဦးစားပေးမုဒ်"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ပြီးပြီ"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ဆက်တင်များ"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"စနစ်ထည့်သွင်းရန်"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string>
@@ -475,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • နှေးကွေးစွာ အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • အားသွင်းနေသည် • အားပြည့်ရန် <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> လိုသည်"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"လော့ခ်မျက်နှာပြင်ရှိ ဝိဂျက်များ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ကို လော့ခ်မျက်နှာပြင်တွင် ထည့်ထားသည်"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"အများသုံးရှင်းလင်းပို့ချချက် စတင်ရန် ဘယ်သို့ပွတ်ဆွဲပါ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"စိတ်ကြိုက်လုပ်ရန်"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ပယ်ရန်"</string>
@@ -528,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ရုပ်သံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ရုပ်သံဖမ်းခြင်း (သို့) ကာစ်လုပ်ခြင်း စမလား။"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ဤလုပ်ဆောင်ချက်ကို ပေးအပ်သည့် ဝန်ဆောင်မှုသည် ရုပ်သံဖမ်းနေစဉ် (သို့) ကာစ်လုပ်နေစဉ် သင့်မျက်နှာပြင်တွင် မြင်ရသော (သို့) သင့်စက်တွင် ဖွင့်ထားသော အချက်အလက်မှန်သမျှကို သုံးနိုင်ပါမည်။ ၎င်းတွင် စကားဝှက်များ၊ ငွေပေးချေမှုအသေးစိတ်များ၊ ဓာတ်ပုံများ၊ မက်ဆေ့ဂျ်များနှင့် သင်ဖွင့်သည့်အသံကဲ့သို့သော အချက်အလက်များ ပါဝင်သည်။"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ဖန်သားပြင်တစ်ခုလုံး"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"အက်ပ်တစ်ခုတွင်"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"အက်ပ် မျှဝေခြင်း (သို့) ရိုက်ကူးခြင်း"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> နှင့် ရုပ်သံဖမ်းခြင်း သို့မဟုတ် ကာစ်လုပ်ခြင်း စတင်မလား။"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် သင့်ဖန်သားပြင်ရှိ မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"အက်ပ်တစ်ခုဖြင့် မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"စတင်ရန်"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"အက်ပ် မျှဝေခြင်း (သို့) ရိုက်ကူးခြင်း"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"သင့်စခရင်ကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> နှင့် မျှဝေမလား။"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"အက်ပ်တစ်ခု မျှဝေရန်"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"စခရင်တစ်ခုလုံး မျှဝေရန်"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"သင့်စခရင်တစ်ခုလုံးကို မျှဝေနေချိန်တွင် စခရင်ပေါ်ရှိ အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"အက်ပ်ကို မျှဝေနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"စခရင် မျှဝေရန်"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် ဤရွေးစရာကို ပိတ်ထားသည်"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ကာစ်လုပ်ခြင်း စမလား။"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ကာစ်လုပ်သည့်အခါ Android သည် သင့်ဖန်သားပြင်ရှိ မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"အက်ပ်တစ်ခုကို ကာစ်လုပ်သည့်အခါ Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ကာစ်လုပ်ခြင်း စရန်"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"မျှဝေရန် အက်ပ်ရွေးခြင်း"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"သင့်ဖန်သားပြင်ကို ကာစ်လုပ်မလား။"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"အက်ပ်တစ်ခုကို ကာစ်လုပ်ရန်"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်ရန်"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"သင့်ဖန်သားပြင်တစ်ခုလုံးကို ကာစ်လုပ်သည့်အခါ ဖန်သားပြင်ပေါ်ရှိ အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"အက်ပ်တစ်ခုကို ကာစ်လုပ်သည့်အခါ ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"စခရင် ကာစ်လုပ်ရန်"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ကာစ်လုပ်ရန် အက်ပ်ရွေးခြင်း"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"စတင်မျှဝေမလား။"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ Android သည် သင့်ဖန်သားပြင်တွင် မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"အက်ပ်တစ်ခုဖြင့် မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့အရာများကို ဂရုစိုက်ပါ။"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"စတင်ရန်"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ရှေ့သို့"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"အက်ပ်ပြောင်းချိန်တွင် မျှဝေခြင်းကို ခဏရပ်သည်"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"၎င်းအစား ဤအက်ပ်ကို မျှဝေရန်"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ပြန်ပြောင်းရန်"</string>
@@ -711,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ကောင်းသည်"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ဂြိုဟ်တု၊ ချိတ်ဆက်မှု ရနိုင်သည်"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"အရေးပေါ်ဖုန်းခေါ်ခြင်း (သို့) SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"အလုပ် ပရိုဖိုင်"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"အချို့သူများ အတွက် ပျော်စရာ ဖြစ်ပေမဲ့ အားလုံး အတွက် မဟုတ်ပါ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"စနစ် UI Tuner က သင့်အတွက် Android အသုံးပြုသူ အင်တာဖေ့စ်ကို ပြောင်းရန်နှင့် စိတ်ကြိုက်ပြုလုပ်ရန် နည်းလမ်း အပိုများကို သင့်အတွက် စီစဉ်ပေးသည်။ အနာဂတ်ဗားရှင်းများတွင် ဤစမ်းသပ်အင်္ဂါရပ်များမှာ ပြောင်းလဲ၊ ပျက်စီး သို့မဟုတ် ပျောက်ကွယ်သွားနိုင်သည်။ သတိဖြင့် ရှေ့ဆက်ပါ။"</string>
@@ -1373,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"အများသုံးနိုင်မှု"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"လက်ကွက်ဖြတ်လမ်းများ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ရှာဖွေမှုရလဒ် မရှိပါ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"နောက်သို့ လက်ဟန်"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ပင်မစာမျက်နှာ လက်ဟန်"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"လုပ်ဆောင်ချက်ကီး"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"တော်ပါပေသည်။"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"တာ့ချ်ပက်တွင် ဘယ်ညာရွှေ့နေသော လက်သုံးချောင်းကို ပြထားသည်"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"စက်စခရင်တွင် နောက်သို့လက်ဟန်အတွက် လှုပ်ရှားသက်ဝင်ပုံကို ပြထားသည်"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"နောက်ပြန်သွားရန် တာ့ချ်ပက်ပေါ်ရှိ မည်သည့်နေရာ၌မဆို လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။\n\n၎င်းအတွက် လက်ကွက်ဖြတ်လမ်း Action + ESC ကိုလည်း သုံးနိုင်သည်။"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"တော်ပါပေသည်။"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"နောက်သို့လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ပင်မစာမျက်နှာသို့ သွားရန်"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ပင်မစာမျက်နှာသို့ အချိန်မရွေးသွားရန် စခရင်အောက်ခြေမှ အပေါ်သို့ လက်သုံးချောင်းဖြင့် ပွတ်ဆွဲပါ။"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ကောင်းသည်။"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ။"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"လုပ်ဆောင်ချက်ကီး"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"သင့်အက်ပ်များသုံးရန် ကီးဘုတ်ပေါ်ရှိ လုပ်ဆောင်ချက်ကီးကို နှိပ်ပါ။"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ဂုဏ်ယူပါသည်။"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"လုပ်ဆောင်ချက်ကီး လက်ဟန် အပြီးသတ်လိုက်ပါပြီ။\n\nလုပ်ဆောင်ချက် + / သည် ရရှိနိုင်သော ဖြတ်လမ်းအားလုံးကို ပြသည်။"</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>
@@ -1397,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ပင်မစာမျက်နှာသို့ သွားရန် တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"လတ်တလောအက်ပ်များကို ကြည့်ရန် တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"သင့်အက်ပ်အားလုံးကြည့်ရန် ကီးဘုတ်ပေါ်ရှိ လုပ်ဆောင်ချက်ကီးကို နှိပ်ပါ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"အစားထိုးထားသည်"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ကြည့်ရန် ဖွင့်ပါ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"နောက်ပြန်သွားရန် သင့်တာ့ချ်ပက်ကို သုံးပါ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ပင်မစာမျက်နှာသို့ သွားရန် သင့်တာ့ချ်ပက်ကို သုံးပါ"</string>
@@ -1405,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"အက်ပ်အားလုံးကြည့်ရန် သင့်ကီးဘုတ်ကို သုံးပါ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"လုပ်ဆောင်ချက်ကီးကို အချိန်မရွေးနှိပ်ပါ။ လက်ဟန်များ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ပိုမှိန်ခြင်းသည် တောက်ပမှုဘားတွင် ပါဝင်လာပြီ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"သင့်စခရင်ထိပ်ဆုံး၌ပင် တောက်ပမှုအဆင့်လျှော့ချခြင်းဖြင့် စခရင်ကို ပိုမှိန်အောင် လုပ်နိုင်ပါပြီ။\n\nသင်သည် မှောင်သောပတ်ဝန်းကျင်၌ရှိချိန် ၎င်းက အကောင်းဆုံးအလုပ်လုပ်သည်။"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ပိုမှိန်ခြင်း ဖြတ်လမ်း ဖယ်ရှားရန်"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"ပိုမှိန်ခြင်း ဖြတ်လမ်းကို ဖယ်ရှားလိုက်ပြီ။ တောက်ပမှုလျှော့ရန် ပုံမှန် တောက်ပမှုဘားကို အသုံးပြုပါ။"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 07b979f..d222f31 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> og andre åpne apper har registrert dette skjermbildet."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Legg til i notat"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inkluder linken"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Skjermopptak"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandler skjermopptaket"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Vil du begynne å ta opp?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Når du tar opp noe, har Android tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Når du tar opp en app, har Android tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Begynn å ta opp"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vil du ta opp skjermen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ta opp én app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ta opp hele skjermen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Når du tar opp hele skjermen, blir alt som vises på skjermen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Når du tar opp en app, blir alt som vises eller spilles av i appen, tatt opp. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ta opp skjermen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Velg app å ta opp"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Spill inn lyd"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Enhetslyd"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Lyd fra enheten, f.eks. musikk, samtaler og ringelyder"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Send"</string>
     <string name="cancel" msgid="1089011503403416730">"Avbryt"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Applogo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekreft"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Prøv på nytt"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Trykk for å avbryte autentiseringen"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skjermsparer"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ikke forstyrr"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioritetsmoduser"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ingen sammenkoblede enheter er tilgjengelige"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trykk for å koble en enhet til eller fra"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Lyddeling"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Trykk for å bytte eller dele lyd"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Direkteteksting"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Direkteteksting"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Åpne Innstillinger"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annen enhet"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Slå oversikten av eller på"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioritetsmoduser"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Ferdig"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Innstillinger"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfigurer"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tilpass"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Moduler på låseskjermen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen er lagt til på låseskjermen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Sveip til venstre for å starte fellesveiledningen"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tilpass"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Lukk"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får tilgang til all informasjon som vises på skjermen eller spilles av fra enheten når du tar opp eller caster noe. Dette inkluderer informasjon som passord, betalingsopplysninger, bilder, meldinger og lyd du spiller av."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vil du begynne å ta opp eller caste?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Tjenesten som leverer denne funksjonen, får tilgang til all informasjon som vises på skjermen eller spilles av fra enheten mens du tar opp eller caster noe. Dette inkluderer informasjon som passord, betalingsopplysninger, bilder, meldinger og lyd du spiller av."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Hele skjermen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Én app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Del eller ta opp en app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vil du begynne å ta opp eller caste med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Når du deler, tar opp eller caster noe, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Når du deler, tar opp eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Begynn"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Del eller ta opp en app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vil du dele skjermen med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Del én app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Del hele skjermen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Når du deler hele skjermen, er alt på skjermen synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt som vises eller spilles av i appen, synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skjermen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktivert dette alternativet"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vil du begynne å caste?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Når du caster, har Android tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Når du caster en app, har Android tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Begynn å caste"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velg app å dele"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste skjermen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Cast hele skjermen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Når du caster hele skjermen, er alt på skjermen synlig. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Når du caster en app, er alt som vises eller spilles av i appen, synlig. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Cast skjermen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Velg app å caste"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vil du begynne å dele?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Når du deler, tar opp eller caster noe, har Android tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Når du deler, tar opp eller caster en app, har Android tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Begynn"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Neste"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Delingen settes på pause når du bytter app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Del denne appen i stedet"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Bytt tilbake"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellitt – god tilkobling"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellitt – tilkobling tilgjengelig"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-alarm via satellitt"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nødanrop eller SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Work-profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Gøy for noen – ikke for alle"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Med System UI Tuner har du flere måter å justere og tilpasse Android-brukergrensesnittet på. Disse eksperimentelle funksjonene kan endres, avbrytes eller fjernes i fremtidige utgivelser. Fortsett med forbehold."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tilgjengelighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Hurtigtaster"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ingen søkeresultater"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tilbakebevegelse"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Startskjermbevegelse"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Handlingstast"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bra jobbet!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"En styreflate med tre fingre som beveger seg til høyre og venstre"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Enhetsskjerm med animasjonen for tilbakebevegelse"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"For å gå tilbake, sveip mot høyre eller venstre med tre fingre hvor som helst på styreflaten.\n\nDu kan også gjøre dette med hurtigtasten Action + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Bra jobbet!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"For å gå til startskjermen, sveip opp med tre fingre fra bunnen av skjermen når som helst."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bra!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du har fullført bevegelsen for å gå til startskjermen."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Handlingstast"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"For å åpne appene dine, trykk på handlingstasten på tastaturet."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulerer!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Du har fullført bevegelsen for handlingstasten.\n\nHandling + / viser alle tilgjengelige snarveier."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"For å gå til startsiden, sveip opp med tre fingre på styreflaten"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"For å se nylige apper, sveip opp og hold med tre fingre på styreflaten"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"For å se alle appene dine, trykk på handlingstasten på tastaturet"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Fjernet"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Lås opp for å se"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Bruk styreflaten for å gå tilbake"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Sveip til venstre eller høyre med tre fingre. Trykk for å lære flere bevegelser."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Bruk styreflaten for å gå til startsiden"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Sveip opp og hold med tre fingre. Trykk for å lære flere bevegelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Bruk tastaturet for å se alle apper"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Trykk på handlingstasten når som helst. Trykk for å lære flere bevegelser."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Nå er ekstra dimmet en del av lysstyrkeraden"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Nå kan du gjøre skjermen ekstra dimmet ved å redusere lysstyrkenivået enda mer fra toppen av skjermen.\n\nDette fungerer best i mørke omgivelser."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Fjern hurtigtasten for ekstra dimmet"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Hurtigtasten for ekstra dimmet er fjernet. For å redusere lysstyrken kan du bruke den vanlige lysstyrkeraden."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 67843fd..8af52a1 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> र खुला रहेका अन्य एपहरूले यो स्क्रिनसट भेट्टाएका छन्।"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"नोटमा सेभ गर्नुहोस्"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"लिंक समावेश गर्नुहोस्"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"स्क्रिन रेकर्डर"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"स्क्रिन रेकर्डिङको प्रक्रिया अघि बढाइँदै"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"रेकर्ड गर्न थाल्ने हो?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"तपाईंले रेकर्ड गर्दै गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"तपाईंले कुनै एप रेकर्ड गर्दै गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"रेकर्ड गर्न थाल्नुहोस्"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तपाईंको स्क्रिन रेकर्ड गर्ने हो?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एउटा एप रेकर्ड गर्नुहोस्"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"पूरै स्क्रिन रेकर्ड गर्नुहोस्"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"तपाईंले आफ्नो पूरै स्क्रिन रेकर्ड गरिरहेका बेला तपाईंको स्क्रिनमा देखाइने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"तपाईंले यो एप रेकर्ड गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री रेकर्ड गरिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"स्क्रिन रेकर्ड गर्नुहोस्"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"रेकर्ड गर्न छनौट गर्नुहोस्"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"अडियो रेकर्ड गर्नुहोस्"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"डिभाइसको अडियो"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"तपाईंको डिभाइसका सङ्गीत, कल र रिङटोन जस्ता साउन्ड"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"पठाउनुहोस्"</string>
     <string name="cancel" msgid="1089011503403416730">"रद्द गर्नुहोस्"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"एपको लोगो"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"पुष्टि गर्नुहोस्"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"फेरि प्रयास गर्नुहोस्"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"प्रमाणीकरण रद्द गर्न ट्याप गर्नुहोस्"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"महत्त्वपूर्ण मोडहरू"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ब्लुटुथ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"जोडी उपकरणहरू उपलब्ध छैन"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"कुनै डिभाइस कनेक्ट गर्न वा डिस्कनेक्ट गर्न ट्याप गर्नुहोस्"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"अडियो सेयरिङ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"अडियो बदल्न वा सेयर गर्न ट्याप गर्नुहोस्"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"सेभ गरिएको छ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"डिस्कनेक्ट गर्नुहोस्"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"एक्टिभेट गर्नुहोस्"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"लाइभ क्याप्सन"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइभ क्याप्सन"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"सेटिङ खोल्नुहोस्"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"अर्को डिभाइड"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"महत्त्वपूर्ण मोडहरू"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"सम्पन्न भयो"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"सेटिङ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"सेटअप गर्नुहोस्"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"तपाईंलाई अलार्महरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">" कस्टम बनाउनुहोस्"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • बिस्तारै चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा पूरै चार्ज हुन्छ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हुँदै छ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> मा फुल चार्ज हुने छ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"लक स्क्रिनमा भएका विजेटहरू"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"लक स्क्रिनमा <xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट हालिएको छ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"कम्युनल ट्युटोरियल सुरु गर्न बायाँतिर स्वाइप गर्नुहोस्"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"कस्टमाइज गर्नुहोस्"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"खारेज गर्नुहोस्"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"रेकर्ड वा कास्ट गर्न थाल्ने हो?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"यो फङ्सन प्रदान गर्ने सेवाले रेकर्ड वा कास्ट गर्दै गर्दा तपाईंको स्क्रिनमा देखिने सबै जानकारी अथवा तपाईंको डिभाइसबाट प्ले गरिने सबै सामग्री हेर्न तथा प्रयोग गर्न सक्छ। यसअन्तर्गत पासवर्ड, भुक्तानीसम्बन्धी विवरण, फोटो, म्यासेज र तपाईंले प्ले गर्ने अडियो जस्ता कुराहरू समावेश हुन्छन्।"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"पूरा स्क्रिन"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"एकल एप"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"कुनै एप सेयर वा रेकर्ड गर्नुहोस्"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा कास्ट गर्न थाल्ने हो?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"तपाईंले कुनै एप सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"सुरु गर्नुहोस्"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"कुनै एप सेयर वा रेकर्ड गर्नुहोस्"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"स्क्रिन <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> सँग सेयर गर्ने हो?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"एउटा एप सेयर गर्नुहोस्"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"पूरै स्क्रिन सेयर गर्नुहोस्"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"तपाईंले पूरै स्क्रिन सेयर गरिरहेका बेला तपाईंको स्क्रिनमा देखिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तपाईंले यो एप सेयर गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रिन सेयर गर्नुहोस्"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले यो विकल्प अफ गर्नुभएको छ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"कास्ट गर्न थाल्ने हो?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"तपाईंले कास्ट गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"तपाईंले कुनै एप कास्ट गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"कास्ट गर्न थाल्नुहोस्"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"सेयर गर्न छनौट गर्नुहोस्"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"स्क्रिन कास्ट गर्ने हो?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एउटा एप कास्ट गर्नुहोस्"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"पूरै स्क्रिन कास्ट गर्नुहोस्"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"तपाईंले पूरै स्क्रिन कास्ट गरिरहेका बेला तपाईंको स्क्रिनमा देखिने सबै सामग्री अर्को स्क्रिनमा पनि देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"तपाईंले यो एप कास्ट गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री अर्को स्क्रिनमा पनि देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"स्क्रिन कास्ट गर्नुहोस्"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"कास्ट गर्न एप छनौट गर्नुहोस्"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"सेयर गर्न थाल्ने हो?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"तपाईंले कुनै एप सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"सुरु गर्नुहोस्"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"अर्को"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"तपाईंले एपहरू बदल्दा सेयर गर्ने प्रक्रिया पज हुन्छ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"यसको साटो यो एप सेयर गर्नुहोस्"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"पछाडि जानुहोस्"</string>
@@ -600,7 +612,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापित गऱ्यो। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"तपाईंको संगठनले तपाईंको कार्य प्रोफाइलमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरेको छ। तपाईंको सुरक्षित नेटवर्क ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"यस डिभाइसमा एउटा प्रमाणपत्र सम्बन्धी अख्तियार सुविधा स्थापना गरिएको छ। तपाईंको सुरक्षित नेटवर्कको ट्राफिकको अनुगमन वा परिमार्जन हुनसक्छ।"</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"तपाईंका प्रशासकले तपाईंको डिभाइसमा ट्राफिकको अनुगमन गर्ने नेटवर्क लग गर्ने प्रक्रियालाई सक्रिय गर्नुभएको छ।"</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"तपाईंको एडमिनले तपाईंको डिभाइसमा ट्राफिकको अनुगमन गर्ने नेटवर्क लगिङ अन गर्नुभएको छ।"</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"तपाईंका एड्मिनले \'नेटवर्क लगिङ\' सुविधा अन गर्नुभएको छ। यो सुविधाले तपाईंको कार्य प्रोफाइलको ट्राफिक अनुगमन गर्छ तर व्यक्तिगत प्रोफाइलको ट्राफिक भने अनुगमन गर्दैन।"</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"यो डिभाइस <xliff:g id="VPN_APP">%1$s</xliff:g> मार्फत इन्टरनेटमा कनेक्ट गरिएको छ। तपाईंको VPN प्रदायकले इमेल र ब्राउजिङ डेटालगायतका नेटवर्कसम्बन्धी गतिविधि हेर्न सक्छ।"</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"यो डिभाइस <xliff:g id="VPN_APP">%1$s</xliff:g> मार्फत इन्टरनेटमा कनेक्ट गरिएको छ। तपाईंका IT एड्मिन इमेल र ब्राउजिङ डेटालगायतका नेटवर्कसम्बन्धी गतिविधि हेर्न सक्नुहुन्छ।"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"स्याटलाइट, राम्रो कनेक्सन"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"स्याटलाइट, कनेक्सन उपलब्ध छ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"स्याटलाइट SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"आपत्कालीन कल वा SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"कार्य प्रोफाइल"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"केहीका लागि रमाइलो हुन्छ तर सबैका लागि होइन"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"सिस्टम UI ट्युनरले तपाईँलाई Android प्रयोगकर्ता इन्टरफेस  कस्टम गर्न र ट्विक गर्न थप तरिकाहरू प्रदान गर्छ। यी प्रयोगात्मक सुविधाहरू भावी विमोचनमा परिवर्तन हुन, बिग्रिन वा हराउन सक्ने छन्। सावधानीपूर्वक अगाडि बढ्नुहोस्।"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"सर्वसुलभता"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"किबोर्डका सर्टकटहरू"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"कुनै पनि खोज परिणाम भेटिएन"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ब्याक जेस्चर"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"होम जेस्चर"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"एक्सन की"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"अद्भुत!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"तिन वटा औँला दायाँ र बायाँ सारेको देखाइएको टचप्याड"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"पछाडि जाने जेस्चरको एनिमेसन देखाइएको डिभाइसको स्क्रिन"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"पछाडि जान तीन वटा औँलाले टचप्याडमा कतै छोएर बायाँ वा दायाँतिर स्वाइप गर्नुहोस्।\n\nतपाईं यसका लागि किबोर्डको सर्टकट \"Action + ESC\" पनि प्रयोग गर्न सक्नुहुन्छ।"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"अद्भुत!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमपेजमा जानुहोस्"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"जुनसुकै बेला आफ्नो होम स्क्रिनमा जान स्क्रिनको फेदबाट तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्।"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"राम्रो!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो।"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"एक्सन की"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"आफ्ना एपहरू एक्सेस गर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्।"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"बधाई छ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"तपाईंले एक्सन की थिचेर गरिने जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो।\n\nएक्सन + / थिच्नुभयो भने उपलब्ध सबै सर्टकटहरू देखिन्छन्।"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"होममा जान तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"आफूले हालसालै चलाएका एपहरू हेर्न तिन वटा औँलाले टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"आफ्ना सबै एपहरू हेर्न आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"जानकारी लुकाउन सम्पादन गरिएको"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"हेर्नका लागि अनलक गर्नुहोस्"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"पछाडि जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"तिन वटा औँला प्रयोग गरी बायाँ वा दायाँतिर स्वाइप गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"होममा जान आफ्नो टचप्याड प्रयोग गर्नुहोस्"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"तिन वटा औँला प्रयोग गरी माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"सबै एपहरू हेर्न आफ्नो किबोर्ड प्रयोग गर्नुहोस्"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"जुनसुकै बेला एक्सन की थिच्नुहोस्। थप जेस्चर प्रयोग गर्ने तरिका सिक्न ट्याप गर्नुहोस्।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"\"अझै मधुरो\" सुविधा अब ब्राइटनेस बारमा समावेश गरिएको छ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"तपाईं अब आफ्नो स्क्रिनको सिरानबाट चमकको स्तर घटाएर आफ्नो स्क्रिन अझै मधुरो बनाउन सक्नुहुन्छ।\n\nतपाईं अँध्यारो ठाउँमा भएका बेला यो सुविधाले अझ राम्रोसँग काम गर्छ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"\"अझै मधुरो\" सर्टकट हटाउनुहोस्"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"\"अझै मधुरो\" सर्टकट हटाइएको छ। स्क्रिनको चमक घटाउन \"रेगुलर ब्राइटनेस बार\" प्रयोग गर्नुहोस्।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 21f1cfb..c1eff5f 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -16,7 +16,11 @@
 
   NOTE: You might also want to edit: core/res/res/values-night/*.xml
   -->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <!-- The dark background color behind the shade -->
+    <color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_dark</color>
+
     <!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.)
     It's fine to override this color since at that point the shade was dark. -->
     <color name="notification_legacy_background_color">@color/GM2_grey_900</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index d259a3d..24a4835 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> en andere geopende apps hebben dit screenshot waargenomen."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Toevoegen aan notitie"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Link opnemen"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Schermopname"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Schermopname verwerken"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Opname starten?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Terwijl je aan het opnemen bent, heeft Android toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Terwijl je een app aan het opnemen bent, heeft Android toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Opname starten"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Je scherm opnemen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Eén app opnemen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Hele scherm opnemen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Als je je hele scherm opneemt, wordt alles opgenomen wat op je scherm wordt getoond. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Als je een app opneemt, wordt alles opgenomen wat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Scherm opnemen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"App kiezen om op te nemen"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio opnemen"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio van apparaat"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Geluid van je apparaat, zoals muziek, gesprekken en ringtones"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Verzenden"</string>
     <string name="cancel" msgid="1089011503403416730">"Annuleren"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"App-logo"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bevestigen"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Opnieuw proberen"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tik om de verificatie te annuleren"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Niet storen"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriteitsmodi"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Geen gekoppelde apparaten beschikbaar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tik om een apparaat te verbinden of de verbinding te verbreken"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio delen"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tik om audio te schakelen of te delen"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Schermopname"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starten"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppen"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem opnemen"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bugrapport"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live ondertiteling"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live ondertiteling"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Microfoon van apparaat niet meer blokkeren?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Apparaatcamera niet meer blokkeren?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Instellingen openen"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Ander apparaat"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Overzicht aan- of uitzetten"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriteitsmodi"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klaar"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Instellingen"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Instellen"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Aanpassen"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets op het vergrendelscherm"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toegevoegd aan vergrendelscherm"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swipe naar links om de communitytutorial te starten"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Aanpassen"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Sluiten"</string>
@@ -507,10 +510,10 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"widget verwijderen"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"geselecteerde widget plaatsen"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgets op het vergrendelscherm"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Iedereen kan widgets op je vergrendelscherm bekijken, ook als je tablet is vergrendeld."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Iedereen kan widgets op je vergrendelscherm bekijken, ook als je tablet vergrendeld is."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"widget deselecteren"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets op het vergrendelscherm"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je laten verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Als je een app wilt openen met een widget, moet je verifiëren dat jij het bent. Houd er ook rekening mee dat iedereen ze kan bekijken, ook als je tablet vergrendeld is. Bepaalde widgets zijn misschien niet bedoeld voor je vergrendelscherm en kunnen hier niet veilig worden toegevoegd."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"OK"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"pull-downmenu"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> krijgt toegang tot alle informatie die zichtbaar is op je scherm of die wordt afgespeeld vanaf je apparaat tijdens het opnemen of casten. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Opnemen of casten starten?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"De service die deze functie levert, krijgt tijdens het opnemen of casten toegang tot alle informatie die zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Volledig scherm"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Eén app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"App delen of opnemen"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Opnemen of casten starten met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Als je deelt, opneemt of cast, heeft <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Als je deelt, opneemt of cast, heeft <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Starten"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"App delen of opnemen"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Je scherm delen met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Eén app delen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Hele scherm delen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Als je het hele scherm deelt, is alles op je scherm zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Als je een app deelt, is alles dat wordt getoond of afgespeeld in die app zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Scherm delen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Voor <xliff:g id="APP_NAME">%1$s</xliff:g> staat deze optie uit"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Casten starten?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Als je cast, heeft Android toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Als je een app cast, heeft Android toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Casten starten"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App kiezen om te delen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Je scherm casten?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eén app casten"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Hele scherm casten"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Als je het hele scherm cast, is alles op je scherm zichtbaar. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Als je een app cast, is alles zichtbaar dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Scherm casten"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"App kiezen om te casten"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Delen starten?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Als je deelt, opneemt of cast, heeft Android toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Als je deelt, opneemt of cast, heeft Android toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Starten"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Volgende"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Delen wordt onderbroken als je schakelt tussen apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"In plaats daarvan deze app delen"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Terugschakelen"</string>
@@ -625,7 +637,7 @@
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Instellingen"</string>
     <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Live ondertiteling"</string>
     <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Volume verlaagd naar een veiliger niveau"</string>
-    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Het hoofdtelefoonvolume is langer dan de aanbevolen tijd hoog geweest"</string>
+    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Het koptelefoonvolume is langer dan de aanbevolen tijd hoog geweest"</string>
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Het hoofdtelefoonvolume overschrijdt de veiligheidslimiet voor deze week"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Blijven luisteren"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Volume omlaag"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelliet, goede verbinding"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelliet, verbinding beschikbaar"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satelliet"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Noodoproepen of SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Werkprofiel"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Leuk voor sommige gebruikers, maar niet voor iedereen"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Met Systeem-UI-tuner beschikt u over extra manieren om de Android-gebruikersinterface aan te passen. Deze experimentele functies kunnen veranderen, vastlopen of verdwijnen in toekomstige releases. Ga voorzichtig verder."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Toegankelijkheid"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Sneltoetsen"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Geen zoekresultaten"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gebaar voor terug"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gebaar voor startscherm"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Actietoets"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Goed werk!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad met 3 vingers die naar rechts en links bewegen"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Apparaatscherm met animatie voor teruggebaar"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Als je wilt teruggaan, swipe je met 3 vingers naar links of rechts op de touchpad.\n\nJe kunt hiervoor ook de sneltoets Actie + ESC gebruiken."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Goed bezig!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Swipe met 3 vingers omhoog vanaf de onderkant van het scherm om naar het startscherm te gaan."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Mooi zo!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Je weet nu hoe je het gebaar Naar startscherm maakt."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Actietoets"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Als je toegang tot je apps wilt krijgen, druk je op de actietoets op je toetsenbord."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gefeliciteerd!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Je hebt het gebaar voor de actietoets uitgevoerd.\n\nActie + / toont alle beschikbare sneltoetsen."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Als je naar het startscherm wilt gaan, swipe je met 3 vingers omhoog op de touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Als je recente apps wilt bekijken, swipe je met 3 vingers omhoog op de touchpad en houd je vast"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Als je alle apps wilt bekijken, druk je op de actietoets op je toetsenbord"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Verborgen"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Ontgrendelen om te bekijken"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Je touchpad gebruiken om terug te gaan"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swipe met 3 vingers naar links of rechts. Tik voor meer gebaren."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Je touchpad gebruiken om naar het startscherm te gaan"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swipe met 3 vingers omhoog en houd vast. Tik voor meer gebaren."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Je toetsenbord gebruiken om alle apps te bekijken"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Druk op de actietoets wanneer je wilt. Tik voor meer gebaren."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extra dimmen maakt nu deel uit van de helderheidsbalk"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Je kunt het scherm nu extra dimmen door het helderheidsniveau nog verder te verlagen vanaf de bovenkant van het scherm.\n\nDit werkt het beste als je in een donkere omgeving bent."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Snelkoppeling voor extra dimmen verwijderen"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Snelkoppeling voor extra dimmen verwijderd. Als je de helderheid wilt verlagen, gebruik je de gewone helderheidsbalk."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 11681c9..67537c2 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ଏବଂ ଅନ୍ୟ ଓପନ ଆପ୍ସ ଏହି ସ୍କ୍ରିନସଟକୁ ଚିହ୍ନଟ କରିଛି।"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ନୋଟରେ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ଲିଙ୍କକୁ ଅନ୍ତର୍ଭୁକ୍ତ କରନ୍ତୁ"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ସ୍କ୍ରିନ ରେକର୍ଡର"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ସ୍କ୍ରିନ ରେକର୍ଡିଂର ପ୍ରକ୍ରିୟାକରଣ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରି‍ନ୍‍ ରେକର୍ଡ୍‍ ସେସନ୍‍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବେ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ଆପଣ ରେକର୍ଡ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ଆପଣ ଏକ ଆପ ରେକର୍ଡ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ରେକର୍ଡିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରିବେ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ଗୋଟିଏ ଆପ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ଆପଣ ଆପଣଙ୍କର ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ରେକର୍ଡ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ଆପଣ ଏକ ଆପ ରେକର୍ଡ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ରେକର୍ଡ ହୋଇଥାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ସ୍କ୍ରିନ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ରେକର୍ଡ କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ଅଡିଓ ରେକର୍ଡ କରନ୍ତୁ"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ଡିଭାଇସ ଅଡିଓ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ମ୍ୟୁଜିକ, କଲ ଏବଂ ରିଂଟୋନଗୁଡ଼ିକ ପରି ଆପଣଙ୍କ ଡିଭାଇସରୁ ସାଉଣ୍ଡ"</string>
@@ -153,7 +157,7 @@
     <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ରେକର୍ଡିଂରେ ସମସ୍ୟା"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"ସେୟାର କରନ୍ତୁ"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"ସମସ୍ୟାର ରେକର୍ଡିଂକୁ ସେଭ କରାଯାଇଛି"</string>
-    <string name="issuerecord_save_text" msgid="1205985304551521495">"ଦେଖିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
+    <string name="issuerecord_save_text" msgid="1205985304551521495">"ଭ୍ୟୁ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ସମସ୍ୟାର ରେକର୍ଡିଂ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ସମସ୍ୟାର ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="immersive_cling_title" msgid="8372056499315585941">"ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନରେ ଦେଖିବା"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ପଠାନ୍ତୁ"</string>
     <string name="cancel" msgid="1089011503403416730">"ବାତିଲ କରନ୍ତୁ"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ଆପ ଲୋଗୋ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ପ୍ରାମାଣିକତା ବାତିଲ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
@@ -227,7 +229,7 @@
     <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"ଫେସ ଅନଲକ ସେଟ ଅପ କରନ୍ତୁ"</string>
     <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"ଫେସ ଅନଲକ ପୁଣି ସେଟ ଅପ କରିବାକୁ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଫେସ ମଡେଲ ଡିଲିଟ ହୋଇଯିବ।\n\nଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବା ପାଇଁ ଆପଣଙ୍କ ଫେସ ବ୍ୟବହାର କରିବାକୁ ଆପଣଙ୍କୁ ଏହି ଫିଚର ପୁଣି ସେଟ ଅପ କରିବାକୁ ହେବ।"</string>
     <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"ଫେସ ଅନଲକ ସେଟ ଅପ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରିବା ପାଇଁ ସେଟିଂସକୁ ଯାଆନ୍ତୁ।"</string>
-    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ଟିପଚିହ୍ନ ସେନସର୍‌କୁ ଛୁଅଁନ୍ତୁ"</string>
+    <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"ଟିପଚିହ୍ନ ସେନସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ"</string>
     <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"ଜାରି ରଖିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ"</string>
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5542430577183894219">"ଫେସ ଚିହ୍ନଟ କରାଯାଇନାହିଁ। ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
     <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ସ୍କ୍ରିନ୍‌ ସେଭର୍‌"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ଇଥରନେଟ୍‌"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ପ୍ରାଥମିକତା ମୋଡ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ବ୍ଲୁଟୁଥ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ପେୟାର୍‍ ହୋଇଥିବା କୌଣସି ଡିଭାଇସ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ଏକ ଡିଭାଇସ କନେକ୍ଟ କିମ୍ବା ଡିସକନେକ୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ଅଡିଓ ସେୟାରିଂ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ଅଡିଓ ସୁଇଚ କିମ୍ବା ସେୟାର କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ସେଭ କରାଯାଇଛି"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ଚାଲୁ କରନ୍ତୁ"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string>
     <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_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>
@@ -381,7 +386,7 @@
     <string name="qs_record_issue_label" msgid="8166290137285529059">"ସମସ୍ୟାର ରେକର୍ଡ କରନ୍ତୁ"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ବଗ୍‌ ରିପୋର୍ଟ୍‌"</string>
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ବଗ ରିପୋର୍ଟ"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"ଲାଇଭ କେପ୍ସନ"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ଲାଇଭ କେପ୍ସନ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ଅନ୍ୟ ଡିଭାଇସ୍"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀକୁ ଟୋଗଲ୍ କରନ୍ତୁ"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ପ୍ରାଥମିକତା ମୋଡ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ହୋଇଗଲା"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ସେଟିଂସ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ସେଟ ଅପ କରନ୍ତୁ"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ଆଲାର୍ମ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"କଷ୍ଟମାଇଜ୍‌ କରନ୍ତୁ"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ଲକ ସ୍କ୍ରିନରେ ଥିବା ୱିଜେଟଗୁଡ଼ିକ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"ଲକ ସ୍କ୍ରିନରେ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ ଯୋଗ କରାଯାଇଛି"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"କମ୍ୟୁନାଲ ଟ୍ୟୁଟୋରିଆଲ ଆରମ୍ଭ କରିବା ପାଇଁ ବାମକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ଖାରଜ କରନ୍ତୁ"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"ରେକର୍ଡ ବା କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ଡିଭାଇସରୁ ପ୍ଲେ ହେଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସମସ୍ତ ସୂଚନାକୁ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ର ଆକ୍ସେସ ରହିବ। ଏଥିରେ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ଫଟୋ, ମେସେଜ ଏବଂ ଆପଣ ପ୍ଲେ କରୁଥିବା ଅଡିଓ ପରି ସୂଚନା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି।"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ରେକର୍ଡିଂ ବା କାଷ୍ଟିଂ ଆରମ୍ଭ କରିବେ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ରେକର୍ଡ ବା କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ଡିଭାଇସରୁ ପ୍ଲେ ହେଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା ସମସ୍ତ ସୂଚନାକୁ ଏହି ଫଙ୍କସନ ପ୍ରଦାନ କରୁଥିବା ସେବାର ଆକ୍ସେସ ରହିବ। ଏଥିରେ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ଫଟୋ, ମେସେଜ ଏବଂ ଆପଣ ପ୍ଲେ କରୁଥିବା ଅଡିଓ ପରି ସୂଚନା ଅନ୍ତର୍ଭୁକ୍ତ ଅଛି।"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ଏକ ସିଙ୍ଗଲ ଆପ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ଏକ ଆପକୁ ସେୟାର କିମ୍ବା ରେକର୍ଡ କରନ୍ତୁ"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ସହ ରେକର୍ଡିଂ ବା କାଷ୍ଟିଂ ଆରମ୍ଭ କରିବେ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ଆପଣ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ଆପଣ ଏକ ଆପ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ଏକ ଆପକୁ ସେୟାର କିମ୍ବା ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ସହ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ସେୟାର କରନ୍ତୁ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ଗୋଟିଏ ଆପ ସେୟାର କରନ୍ତୁ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ସେୟାର କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ଆପଣ ଏକ ଆପ ସେୟାର କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଏହି ବିକଳ୍ପକୁ ଅକ୍ଷମ କରିଛି"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"କାଷ୍ଟିଂ ଆରମ୍ଭ କରିବେ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ଆପଣ କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ଆପଣ ଏକ ଆପ କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପାଇଁ ସତର୍କ ରୁହନ୍ତୁ।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"କାଷ୍ଟିଂ ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ସେୟାର କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବେ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ଗୋଟିଏ ଆପକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ଆପଣ ଆପଣଙ୍କ ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବା ସମୟରେ ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ଆପଣ ଏକ ଆପ କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"କାଷ୍ଟ ସ୍କ୍ରିନ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"କାଷ୍ଟ କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ସେୟାରିଂ ଆରମ୍ଭ କରିବେ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ଆପଣ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ଆପଣ ଏକ ଆପ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ପରବର୍ତ୍ତୀ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ଆପଣ ଆପ୍ସ ସୁଇଚ କଲେ ସେୟାରିଂ ବିରତ ହୁଏ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ଏହା ପରିବର୍ତ୍ତେ ଏହି ଆପ ସେୟାର କରନ୍ତୁ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ପୁଣି ସୁଇଚ କରନ୍ତୁ"</string>
@@ -592,7 +604,7 @@
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"ନେଟୱାର୍କ ଲଗିଂ"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA ସର୍ଟିଫିକେଟ୍‌"</string>
-    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"ପଲିସୀ ଦେଖନ୍ତୁ"</string>
+    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"ନୀତିଗୁଡ଼ିକୁ ଭ୍ୟୁ କରନ୍ତୁ"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"ଏହି ଡିଭାଇସଟି <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>ର ଅଟେ।\n\nଆପଣଙ୍କ IT ଆଡମିନ ସେଟିଂସ, କର୍ପୋରେଟ ଆକ୍ସେସ, ଆପ୍ସ, ଆପଣଙ୍କ ଡିଭାଇସ ସହ ସମ୍ବନ୍ଧିତ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେସନ ସୂଚନାକୁ ନିରୀକ୍ଷଣ ଏବଂ ପରିଚାଳନା କରିପାରିବେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> ଏହି ଡିଭାଇସ ସହ ସମ୍ବନ୍ଧିତ ଡାଟା ଆକ୍ସେସ କରିବା, ଆପଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା ଏବଂ ଏହି ଡିଭାଇସର ସେଟିଂସ ବଦଳାଇବାକୁ ସକ୍ଷମ ହୋଇପାରେ।\n\nଯଦି ଆପଣଙ୍କର କିଛି ପ୍ରଶ୍ନ ଅଛି, ତେବେ <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string>
@@ -600,7 +612,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ଏହି ଡିଭାଇସରେ ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ଆପଣଙ୍କ ଆଡମିନ୍‍ ନେଟୱର୍କ ଲଗଇନ୍‍ କରିବା ଅନ୍‍ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରେ।"</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"ଆପଣଙ୍କ ଆଡମିନ ନେଟୱାର୍କ ଲଗିଂ ଚାଲୁ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ ନୀରିକ୍ଷଣ କରେ।"</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"ଆପଣଙ୍କ ଆଡମିନ୍ ନେଟୱାର୍କ ଲଗିଂ ଚାଲୁ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ୱାର୍କ ପ୍ରୋଫାଇଲରେ ଟ୍ରାଫିକ୍ ନିରୀକ୍ଷଣ କରେ କିନ୍ତୁ ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲରେ ନୁହେଁ।"</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"ଏହି ଡିଭାଇସ <xliff:g id="VPN_APP">%1$s</xliff:g> ମାଧ୍ୟମରେ ଇଣ୍ଟରନେଟ ସହ କନେକ୍ଟ ହୋଇଛି। ଇମେଲ ଏବଂ ବ୍ରାଉଜିଂ ଡାଟା ସମେତ, ଆପଣଙ୍କ ନେଟୱାର୍କ କାର୍ଯ୍ୟକଳାପ VPN ପ୍ରଦାନକାରୀଙ୍କୁ ଦେଖାଯାଉଛି।"</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"ଏହି ଡିଭାଇସ <xliff:g id="VPN_APP">%1$s</xliff:g> ମାଧ୍ୟମରେ ଇଣ୍ଟରନେଟ ସହ କନେକ୍ଟ ଅଛି। ଇମେଲ ଏବଂ ବ୍ରାଉଜିଂ ଡାଟା ସମେତ, ଆପଣଙ୍କ ନେଟୱାର୍କ କାର୍ଯ୍ୟକଳାପ ଆପଣଙ୍କର IT ଆଡମିନଙ୍କୁ ଦୃଶ୍ୟମାନ ହୋଇଥାଏ।"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ସାଟେଲାଇଟ, ଭଲ କନେକ୍ସନ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ସାଟେଲାଇଟ, କନେକ୍ସନ ଉପଲବ୍ଧ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ସେଟେଲାଇଟ SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ଜରୁରୀକାଳୀନ କଲ କିମ୍ବା SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"କେତେକଙ୍କ ପାଇଁ ମଜାଦାର, କିନ୍ତୁ ସମସ୍ତଙ୍କ ପାଇଁ ନୁହେଁ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Android ୟୁଜର୍‍ ଇଣ୍ଟରଫେସ୍‍ ବଦଳାଇବାକୁ ତଥା ନିଜ ପସନ୍ଦ ଅନୁଯାୟୀ କରିବାକୁ ସିଷ୍ଟମ୍‍ UI ଟ୍ୟୁନର୍‍ ଆପଣଙ୍କୁ ଅତିରିକ୍ତ ଉପାୟ ପ୍ରଦାନ କରେ। ଏହି ପରୀକ୍ଷାମୂଳକ ସୁବିଧାମାନ ବଦଳିପାରେ, ଭାଙ୍ଗିପାରେ କିମ୍ବା ଭବିଷ୍ୟତର ରିଲିଜ୍‌ଗୁଡ଼ିକରେ ନଦେଖାଯାଇପାରେ। ସତର୍କତାର ସହ ଆଗକୁ ବଢ଼ନ୍ତୁ।"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ଆକ୍ସେସିବିଲିଟୀ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"କୀବୋର୍ଡ ସର୍ଟକଟ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"କୌଣସି ସର୍ଚ୍ଚ ଫଳାଫଳ ନାହିଁ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ବେକ ଜେଶ୍ଚର"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ହୋମ ଜେଶ୍ଚର"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ଆକ୍ସନ କୀ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ବଢ଼ିଆ କାମ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ଡାହାଣ ଏବଂ ବାମକୁ ତିନି ଆଙ୍ଗୁଠି ମୁଭ କରୁଥିବା ଟଚପେଡ ଦେଖାଉଛି"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ଡିଭାଇସ ସ୍କ୍ରିନ ବେକ ଜେଶ୍ଚର ପାଇଁ ଆନିମେସନ ଦେଖାଉଛି"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ପଛକୁ ଫେରିବା ପାଇଁ ଯେ କୌଣସି ସ୍ଥାନରେ ତିନି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ।\n\nଏଥିପାଇଁ ଆପଣ କୀବୋର୍ଡ ସର୍ଟକଟ ଆକ୍ସନ + ESC ମଧ୍ୟ ବ୍ୟବହାର କରିପାରିବେ।"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ବଢ଼ିଆ କାମ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ହୋମକୁ ଯାଆନ୍ତୁ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ଯେ କୌଣସି ସମୟରେ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ସ୍କିନର ତଳୁ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ।"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ବଢ଼ିଆ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ଆକ୍ସନ କୀ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ଆପଣଙ୍କ ଆପ୍ସ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣଙ୍କର କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ।"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ଅଭିନନ୍ଦନ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ଆପଣ ଆକ୍ସନ କୀ ଜେଶ୍ଚରକୁ ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।\n\nଆକ୍ସନ + / ଆପଣଙ୍କ ପାଖରେ ଉପଲବ୍ଧ ଥିବା ସମସ୍ତ ସଟକର୍ଟକୁ ଦେଖାଇଥାଏ।"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ହୋମକୁ ଯିବା ପାଇଁ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରିବାକୁ, ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ଆପଣଙ୍କ ସମସ୍ତ ଆପ୍ସ ଭ୍ୟୁ କରିବା ପାଇଁ ଆପଣଙ୍କ କୀବୋର୍ଡରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ଲୁଚା ଯାଇଥିବା"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ଭ୍ୟୁ କରିବାକୁ ଅନଲକ କରନ୍ତୁ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ପଛକୁ ଫେରିବା ପାଇଁ ଆପଣଙ୍କ ଟଚପେଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ତିନୋଟି ଆଙ୍ଗୁଠିରେ ବାମ ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ହୋମକୁ ଯିବା ପାଇଁ ଆପଣଙ୍କ ଟଚପେଡ ବ୍ୟବହାର କରନ୍ତୁ"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ସମସ୍ତ ଆପ୍ସ ଭ୍ୟୁ କରିବା ପାଇଁ ଆପଣଙ୍କ କୀବୋର୍ଡକୁ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ଯେ କୌଣସି ସମୟରେ ଆକ୍ସନ କୀ\'କୁ ଦବାନ୍ତୁ। ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଅଧିକ ଜାଣିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ଅତିରିକ୍ତ ଡିମ ବର୍ତ୍ତମାନ ଉଜ୍ଜ୍ୱଳତା ବାରର ଅଂଶ ଅଟେ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ବର୍ତ୍ତମାନ ଆପଣ ଆପଣଙ୍କ ସ୍କ୍ରିନର ଶୀର୍ଷରୁ ଉଜ୍ଜ୍ୱଳତାର ଲେଭେଲ ହ୍ରାସ କରି ସ୍କ୍ରିନକୁ ଅତିରିକ୍ତ ଡିମ କରିପାରିବେ।\n\nଆପଣ ଏକ ଡାର୍କ ପରିବେଶରେ ଥିଲେ ଏହା ସବୁଠାରୁ ଭଲ କାମ କରେ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"ଅତିରିକ୍ତ ଡିମ ସର୍ଟକଟକୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"ଅତିରିକ୍ତ ଡିମର ସର୍ଟକଟ କାଢ଼ି ଦିଆଯାଇଛି। ଆପଣଙ୍କ ଉଜ୍ଜ୍ୱଳତା ହ୍ରାସ କରିବା ପାଇଁ ନିୟମିତ ଉଜ୍ଜ୍ୱଳତା ବାର ବ୍ୟବହାର କରନ୍ତୁ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index ef642ff..1927d45 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅਤੇ ਹੋਰ ਖੁੱਲ੍ਹੀਆਂ ਐਪਾਂ ਨੂੰ ਇਸ ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਪਤਾ ਲੱਗਿਆ ਹੈ।"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ਨੋਟ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"ਲਿੰਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਜਾਰੀ ਹੈ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ਕੀ ਰਿਕਾਰਡਿੰਗ ਸ਼ੁਰੂ ਕਰਨੀ ਹੈ?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਰਿਕਾਰਡਿੰਗ ਕਰਨ ਵੇਲੇ, Android ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਣ ਵਾਲੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਰਿਕਾਰਡਿੰਗ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ਰਿਕਾਰਡਿੰਗ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ਜਦੋਂ ਤੁਸੀਂ ਆਪਣੀ ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਨਾਲ ਹੀ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ਜਦੋਂ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹੁੰਦੇ ਹੋ, ਤਾਂ ਉਸ ਐਪ ਵਿੱਚ ਦਿਖਾਈ ਜਾਂ ਚਲਾਈ ਜਾ ਰਹੀ ਹਰ ਚੀਜ਼ ਨੂੰ ਰਿਕਾਰਡ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ਰਿਕਾਰਡ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ਆਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"ਡੀਵਾਈਸ ਆਡੀਓ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਧੁਨੀ, ਜਿਵੇਂ ਕਿ ਸੰਗੀਤ, ਕਾਲਾਂ ਅਤੇ ਰਿੰਗਟੋਨਾਂ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ਭੇਜੋ"</string>
     <string name="cancel" msgid="1089011503403416730">"ਰੱਦ ਕਰੋ"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ਐਪ ਲੋਗੋ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ਤਸਦੀਕ ਕਰੋ"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ਪ੍ਰਮਾਣੀਕਰਨ ਰੱਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ਈਥਰਨੈਟ"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ਤਰਜੀਹ ਮੋਡ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ਬਲੂਟੁੱਥ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ਕੋਈ ਜੋੜਾਬੱਧ ਕੀਤੀਆਂ ਡੀਵਾਈਸਾਂ ਉਪਲਬਧ ਨਹੀਂ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"ਡੀਵਾਈਸ ਨੂੰ ਕਨੈਕਟ ਜਾਂ ਡਿਸਕਨੈਕਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ਆਡੀਓ ਨੂੰ ਸਵਿੱਚ ਜਾਂ ਸਾਂਝਾ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ਕਿਰਿਆਸ਼ੀਲ ਕਰੋ"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ਹੋਰ ਡੀਵਾਈਸ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"ਰੂਪ-ਰੇਖਾ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ਤਰਜੀਹ ਮੋਡ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ਹੋ ਗਿਆ"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ਸੈਟਿੰਗਾਂ"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ਸੈੱਟਅੱਪ ਕਰੋ"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ਵਿੱਚ ਪੂਰਾ ਚਾਰਜ ਹੋਵੇਗਾ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ, ਲਾਕ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ਭਾਈਚਾਰਕ ਟਿਊਟੋਰੀਅਲ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ਖਾਰਜ ਕਰੋ"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"ਕੋਈ ਵੀ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ।"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"ਵਿਜੇਟ ਨੂੰ ਅਣਚੁਣਿਆ ਕਰੋ"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"ਲਾਕ ਸਕ੍ਰੀਨ ਵਿਜੇਟ"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡੀ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"ਵਿਜੇਟ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਐਪ ਖੋਲ੍ਹਣ ਲਈ, ਤੁਹਾਨੂੰ ਇਹ ਪੁਸ਼ਟੀ ਕਰਨ ਦੀ ਲੋੜ ਪਵੇਗੀ ਕਿ ਇਹ ਤੁਸੀਂ ਹੀ ਹੋ। ਨਾਲ ਹੀ, ਇਹ ਵੀ ਧਿਆਨ ਵਿੱਚ ਰੱਖੋ ਕਿ ਕੋਈ ਵੀ ਉਨ੍ਹਾਂ ਨੂੰ ਦੇਖ ਸਕਦਾ ਹੈ, ਭਾਵੇਂ ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਲਾਕ ਹੋਵੇ। ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਕੁਝ ਵਿਜੇਟ ਤੁਹਾਡੀ ਲਾਕ ਸਕ੍ਰੀਨ ਲਈ ਨਾ ਬਣੇ ਹੋਣ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਇੱਥੇ ਸ਼ਾਮਲ ਕਰਨਾ ਅਸੁਰੱਖਿਅਤ ਹੋਵੇ।"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"ਸਮਝ ਲਿਆ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ਵਰਤੋਂਕਾਰ ਸਵਿੱਚ ਕਰੋ"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"ਪੁੱਲਡਾਊਨ ਮੀਨੂ"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਬਾਕੀ ਸਾਰੀ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ ਜੋ ਕਿ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਣਯੋਗ ਹੈ ਜਾਂ ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨ ਵੇਲੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਂਦੀ ਹੈ। ਇਸ ਵਿੱਚ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਫ਼ੋਟੋਆਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਤੁਹਾਡੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਆਡੀਓ ਦੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੈ।"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ਕੀ ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰਨਾ ਹੈ?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ਇਸ ਫੰਕਸ਼ਨ ਦੇ ਸੇਵਾ ਪ੍ਰਦਾਨਕ ਕੋਲ ਬਾਕੀ ਸਾਰੀ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ ਜੋ ਕਿ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ ਜਾਂ ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨ ਵੇਲੇ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਂਦੀ ਹੈ। ਇਸ ਵਿੱਚ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਫ਼ੋਟੋਆਂ, ਸੁਨੇਹਿਆਂ ਅਤੇ ਤੁਹਾਡੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਆਡੀਓ ਦੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੈ।"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ਪੂਰੀ ਸਕ੍ਰੀਨ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ਇਕਹਿਰੀ ਐਪ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰੋ ਜਾਂ ਰਿਕਾਰਡ ਕਰੋ"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"ਕੀ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨਾਲ ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰਨਾ ਹੈ?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਣ ਵਾਲੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡ ਕਰਨ, ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰੋ ਜਾਂ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"ਕੀ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨਾਲ ਆਪਣੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰਨੀ ਹੈ?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ਇੱਕ ਐਪ ਸਾਂਝੀ ਕਰੋ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖ ਰਹੀ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> \'ਤੇ ਵੀ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੇ ਇਸ ਵਿਕਲਪ ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ਕੀ ਕਾਸਟ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰਨਾ ਹੈ?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਣ ਵਾਲੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਿਸੇ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ਕਾਸਟ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ਸਾਂਝਾ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨਾ ਹੈ?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ਇੱਕ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨ ਦੌਰਾਨ, ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖ ਰਹੀ ਹਰੇਕ ਚੀਜ਼ ਦੂਸਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵੀ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ਕਿਸੇ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਸਾਰੇ ਲੋਕਾਂ ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"ਸਕ੍ਰੀਨ ਕਾਸਟ ਕਰੋ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ਕਾਸਟ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"ਕੀ ਸਾਂਝਾਕਰਨ ਸ਼ੁਰੂ ਕਰਨਾ ਹੈ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡ ਕਰਨ, ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਦੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡ ਕਰਨ, ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ਸ਼ੁਰੂ ਕਰੋ"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ਅੱਗੇ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ਐਪਾਂ ਸਵਿੱਚ ਕਰਨ \'ਤੇ ਸਾਂਝਾਕਰਨ ਰੁਕ ਜਾਂਦਾ ਹੈ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ਇਸਦੀ ਬਜਾਏ ਇਹ ਐਪ ਸਾਂਝੀ ਕਰੋ"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ਵਾਪਸ ਸਵਿੱਚ ਕਰੋ"</string>
@@ -612,7 +624,7 @@
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਵੱਲੋਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। ਤੁਹਾਡੇ ਮਾਂ-ਪਿਓ ਤੁਹਾਡੀਆਂ ਐਪਾਂ ਦੀ ਵਰਤੋਂ, ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਅਤੇ ਤੁਹਾਡੇ ਸਕ੍ਰੀਨ ਸਮੇਂ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਦੇਖ ਅਤੇ ਉਸਦਾ ਪ੍ਰਬੰਧਨ ਕਰ ਸਕਦੇ ਹਨ।"</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"ਟਰੱਸਟ-ਏਜੰਟ ਵੱਲੋਂ ਅਣਲਾਕ ਰੱਖਿਆ ਗਿਆ"</string>
-    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ਡੀਵਾਈਸ ਲਾਕ ਕੀਤਾ ਗਿਆ ਸੀ, ਬਹੁਤ ਸਾਰੀਆਂ ਪ੍ਰਮਾਣੀਕਰਨ ਕੋਸ਼ਿਸ਼ਾਂ ਕੀਤੀਆਂ ਗਈਆਂ"</string>
+    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"ਬਹੁਤ ਵਾਰੀ ਪ੍ਰਮਾਣੀਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ ਕਰਨ ਕਰਕੇ ਡੀਵਾਈਸ ਲਾਕ ਹੋ ਗਿਆ ਸੀ"</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"ਡੀਵਾਈਸ ਲਾਕ ਹੈ\nਪ੍ਰਮਾਣੀਕਰਨ ਅਸਫਲ ਰਿਹਾ"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="accessibility_volume_settings" msgid="1458961116951564784">"ਧੁਨੀ ਸੈਟਿੰਗਾਂ"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਵਧੀਆ ਹੈ"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ਸੈਟੇਲਾਈਟ, ਕਨੈਕਸ਼ਨ ਉਪਲਬਧ ਹੈ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ਸੈਟੇਲਾਈਟ SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ਐਮਰਜੈਂਸੀ ਕਾਲਾਂ ਜਾਂ ਸਹਾਇਤਾ"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"ਕੁਝ ਵਾਸਤੇ ਤਾਂ ਮਜ਼ੇਦਾਰ ਹੈ ਲੇਕਿਨ ਸਾਰਿਆਂ ਵਾਸਤੇ ਨਹੀਂ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"ਸਿਸਟਮ UI ਟਿਊਨਰ ਤੁਹਾਨੂੰ Android ਵਰਤੋਂਕਾਰ ਇੰਟਰਫ਼ੇਸ ਤਬਦੀਲ ਕਰਨ ਅਤੇ ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਵਾਧੂ ਤਰੀਕੇ ਦਿੰਦਾ ਹੈ। ਇਹ ਪ੍ਰਯੋਗਾਤਮਿਕ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਭਵਿੱਖ ਦੀ ਰੀਲੀਜ਼ ਵਿੱਚ ਬਦਲ ਸਕਦੀਆਂ ਹਨ, ਟੁੱਟ ਸਕਦੀਆਂ ਹਨ, ਜਾਂ ਅਲੋਪ ਹੋ ਸਕਦੀਆਂ ਹਨ। ਸਾਵਧਾਨੀ ਨਾਲ ਅੱਗੇ ਵੱਧੋ।"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ਪਹੁੰਚਯੋਗਤਾ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ਕੋਈ ਖੋਜ ਨਤੀਜਾ ਨਹੀਂ ਮਿਲਿਆ"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ਪਿੱਛੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ਹੋਮ \'ਤੇ ਜਾਣ ਦਾ ਇਸ਼ਾਰਾ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ਕਾਰਵਾਈ ਕੁੰਜੀ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"ਬਹੁਤ ਵਧੀਆ!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨੂੰ ਸੱਜੇ ਅਤੇ ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਲਿਜਾਂਦੇ ਹੋਏ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"ਡੀਵਾਈਸ ਦੀ ਸਕ੍ਰੀਨ \'ਤੇ ਪਿੱਛੇ ਜਾਣ ਵਾਲੇ ਇਸ਼ਾਰੇ ਲਈ ਐਨੀਮੇਸ਼ਨ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ਵਾਪਸ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਕਿਤੇ ਵੀ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।\n\nਤੁਸੀਂ ਇਸ ਲਈ ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ Action + ESC ਦੀ ਵਰਤੋਂ ਵੀ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"ਬਹੁਤ ਵਧੀਆ!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ਹੋਮ \'ਤੇ ਜਾਓ"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਜਾਣ ਲਈ, ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ।"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ਵਧੀਆ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ਕਾਰਵਾਈ ਕੁੰਜੀ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ਆਪਣੀਆਂ ਐਪਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਲਈ, ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ।"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ਵਧਾਈਆਂ!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ਤੁਸੀਂ \'ਕਾਰਵਾਈ ਕੁੰਜੀ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।\n\nਕਾਰਵਾਈ ਬਟਨ ਅਤੇ / ਨੂੰ ਇਕੱਠੇ ਦਬਾਉਣ \'ਤੇ, ਤੁਹਾਡੇ ਕੋਲ ਉਪਲਬਧ ਸਾਰੇ ਸ਼ਾਰਟਕੱਟ ਦਿਖਦੇ ਹਨ।"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ਹਾਲੀਆ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ, ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ਆਪਣੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ, ਆਪਣੇ ਕੀ-ਬੋਰਡ \'ਤੇ ਕਾਰਵਾਈ ਕੁੰਜੀ ਨੂੰ ਦਬਾਓ"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ਅਸਪਸ਼ਟ ਬਣਾਇਆ ਗਿਆ"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ਪਿੱਛੇ ਜਾਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ਹੋਮ \'ਤੇ ਜਾਣ ਲਈ ਆਪਣੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ਸਾਰੀਆਂ ਐਪਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਵਰਤੋ"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ਕਿਸੇ ਵੀ ਸਮੇਂ ਕਾਰਵਾਈ ਕੁੰਜੀ ਦਬਾਓ। ਹੋਰ ਇਸ਼ਾਰਿਆਂ ਨੂੰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਹੁਣ ਚਮਕ ਪੱਟੀ ਦਾ ਹਿੱਸਾ ਹੈ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ਤੁਸੀਂ ਹੁਣ ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੇ ਸਿਖਰ ਤੋਂ ਚਕਮ ਦੇ ਪੱਧਰ ਨੂੰ ਹੋਰ ਵੀ ਘੱਟ ਕਰ ਕੇ ਸਕ੍ਰੀਨ ਦੀ ਚਮਕ ਨੂੰ ਜ਼ਿਆਦਾ ਘੱਟ ਕਰ ਸਕਦੇ ਹੋ।\n\nਇਹ ਉਦੋਂ ਬਿਹਤਰੀਨ ਕੰਮ ਕਰਦੀ ਹੈ, ਜਦੋਂ ਤੁਸੀਂ ਹਨੇਰੇ ਵਿੱਚ ਹੁੰਦੇ ਹੋ।"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਸ਼ਾਰਟਕੱਟ ਹਟਾਓ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"\'ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ\' ਸ਼ਾਰਟਕੱਟ ਹਟਾਇਆ ਗਿਆ। ਆਪਣੀ ਸਕ੍ਰੀਨ ਦੀ ਚਕਮ ਨੂੰ ਘੱਟ ਕਰਨ ਲਈ, ਨਿਯਮਿਤ ਚਮਕ ਪੱਟੀ ਦੀ ਵਰਤੋਂ ਕਰੋ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d0e1e11..1b27dba 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -19,7 +19,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_label" msgid="4811759950673118541">"UI systemu"</string>
+    <string name="app_label" msgid="4811759950673118541">"Interfejs systemu"</string>
     <string name="battery_low_title" msgid="5319680173344341779">"Włączyć Oszczędzanie baterii?"</string>
     <string name="battery_low_description" msgid="3282977755476423966">"Masz już tylko <xliff:g id="PERCENTAGE">%s</xliff:g> baterii. Oszczędzanie baterii uruchamia ciemny motyw, ogranicza aktywność w tle i opóźnia powiadomienia."</string>
     <string name="battery_low_intro" msgid="5148725009653088790">"Oszczędzanie baterii uruchamia ciemny motyw, ogranicza aktywność w tle i opóźnia powiadomienia."</string>
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> i inne aplikacje wykryły ten zrzut ekranu."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj do notatek"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Dołącz link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Nagrywanie ekranu"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Przetwarzam nagrywanie ekranu"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Rozpocząć nagrywanie?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Podczas nagrywania Android ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Podczas nagrywania treści z aplikacji Android ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Zacznij nagrywać"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Nagrywać ekran?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nagrywaj jedną aplikację"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nagrywaj cały ekran"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kiedy nagrywasz cały ekran, nagrane zostanie wszystko, co jest na nim widoczne. Dlatego uważaj na hasła, dane do płatności, wiadomości, zdjęcia, nagrania audio czy filmy."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kiedy nagrywasz aplikację, wszystko, co jest w niej wyświetlane lub odtwarzane, zostaje nagrane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nagrywaj ekran"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Wybieranie aplikacji do nagrywania"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nagrywaj dźwięk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Dźwięki z urządzenia"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Dźwięki odtwarzane na urządzeniu, na przykład muzyka, połączenia i dzwonki"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Wyślij"</string>
     <string name="cancel" msgid="1089011503403416730">"Anuluj"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo aplikacji"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potwierdź"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Spróbuj jeszcze raz"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Kliknij, by anulować uwierzytelnianie"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Wygaszacz ekranu"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nie przeszkadzać"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Tryby priorytetowe"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Brak dostępnych sparowanych urządzeń"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Kliknij, aby podłączyć lub odłączyć urządzenie"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Używaj Bluetootha"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Udostępnianie dźwięku"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Kliknij, aby przełączyć lub udostępnić dźwięk"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string>
     <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_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>
@@ -375,20 +380,20 @@
     <string name="quick_settings_nfc_label" msgid="1054317416221168085">"Komunikacja NFC"</string>
     <string name="quick_settings_nfc_off" msgid="3465000058515424663">"Komunikacja NFC jest wyłączona"</string>
     <string name="quick_settings_nfc_on" msgid="1004976611203202230">"Komunikacja NFC jest włączona"</string>
-    <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagrywanie ekranu"</string>
+    <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Nagraj ekran"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Rozpocznij"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Zatrzymaj"</string>
     <string name="qs_record_issue_label" msgid="8166290137285529059">"Zarejestruj problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Rozpocznij"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zatrzymaj"</string>
-    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport o błędzie"</string>
+    <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zgłoś błąd"</string>
     <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Którego aspektu korzystania z urządzenia dotyczył problem?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Wybierz typ problemu"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Nagrywanie ekranu"</string>
     <string name="performance" msgid="6552785217174378320">"Wydajność"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interfejs"</string>
-    <string name="thermal" msgid="6758074791325414831">"Termografia"</string>
-    <string name="custom" msgid="3337456985275158299">"Niestandardowe"</string>
+    <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
+    <string name="custom" msgid="3337456985275158299">"Niestandardowy"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Niestandardowe ustawienia śladu"</string>
     <string name="restore_default" msgid="5259420807486239755">"Przywróć wartości domyślne"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Napisy na żywo"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Napisy na żywo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otwórz Ustawienia"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Inne urządzenie"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Przełącz Przegląd"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Tryby priorytetowe"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gotowe"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ustawienia"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Skonfiguruj"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Dostosuj"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widżety na ekranie blokady"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu blokady"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Aby uruchomić wspólny samouczek, przeciągnij palcem w lewo"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Dostosuj"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zamknij"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Podczas nagrywania i przesyłania aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> będzie mieć dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Rozpocząć nagrywanie lub przesyłanie?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Podczas nagrywania i przesyłania usługa udostępniająca tę funkcję będzie miała dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Cały ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Pojedyncza aplikacja"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Udostępnianie i nagrywanie aplikacji"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Rozpocząć nagrywanie lub przesyłanie za pomocą aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Rozpocznij"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Udostępnianie i nagrywanie za pomocą aplikacji"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Udostępnić ekran aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Udostępnij jedną aplikację"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Udostępnij cały ekran"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kiedy udostępniasz treści z całego ekranu, aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do całości obrazu z wyświetlacza. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kiedy udostępniasz obraz z aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, widoczne jest wszystko to, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Udostępnij ekran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ma wyłączoną tę opcję"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Rozpocząć przesyłanie?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Podczas przesyłania, Android ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Podczas przesyłania treści z aplikacji Android ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Rozpocznij przesyłanie"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Wybieranie aplikacji do udostępniania"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Włączyć przesyłanie treści wyświetlanych na ekranie?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Przesyłanie obrazu z jednej aplikacji"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Przesyłanie obrazu z całego ekranu"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kiedy przesyłasz treści z całego ekranu, widoczny jest cały obraz z wyświetlacza. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kiedy przesyłasz obraz z aplikacji, widoczne jest wszystko to, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prześlij ekran"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Wybieranie aplikacji do przesyłania"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Rozpocząć udostępnianie?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Podczas udostępniania, nagrywania lub przesyłania treści Android ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Podczas udostępniania, nagrywania lub przesyłania treści Android ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, dźwięku i filmów."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Rozpocznij"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Dalej"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Udostępnianie zostanie wstrzymane, gdy przełączysz aplikacje"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Udostępnij tę aplikację"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Wróć"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelita – połączenie dobre"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelita – połączenie dostępne"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satelitarne połączenie alarmowe"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Połączenia alarmowe lub SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil służbowy"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Dobra zabawa, ale nie dla każdego"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Kalibrator System UI udostępnia dodatkowe sposoby dostrajania i dostosowywania interfejsu Androida. Te eksperymentalne funkcje mogą się zmienić, popsuć lub zniknąć w przyszłych wersjach. Zachowaj ostrożność."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ułatwienia dostępu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Skróty klawiszowe"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Brak wyników wyszukiwania"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gest przejścia wstecz"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gest przejścia na ekran główny"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Klawisz działania"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Świetnie!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"3 palce na touchpadzie poruszające się w prawo i w lewo"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekran urządzenia z animacją gestu cofania"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Aby przejść wstecz, przesuń 3 palcami w lewo lub w prawo w dowolnym miejscu touchpada.\n\nMożesz też użyć do tego skrótu klawiszowego Action + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Brawo!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Aby w dowolnym momencie wyświetlić ekran główny, przesuń od dołu ekranu w górę 3 palcami."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Super!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Gest przechodzenia na ekran główny został opanowany."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Klawisz działania"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Aby uzyskać dostęp do aplikacji, naciśnij klawisz działania na klawiaturze."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Gratulacje!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Gest klawisza działania został opanowany.\n\nKlawisz działania + / pokazuje wszystkie dostępne skróty."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Aby przejść do ekranu głównego, przesuń w górę za pomocą 3 palców na touchpadzie"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Aby wyświetlić ostatnie aplikacje, przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Aby wyświetlić wszystkie swoje aplikacje, naciśnij klawisz działania na klawiaturze"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Usunięto"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Odblokuj, aby zobaczyć"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Przechodzenie wstecz za pomocą touchpada"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Przesuń w prawo lub lewo za pomocą 3 palców. Kliknij, aby poznać więcej gestów."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Przechodzenie do ekranu głównego za pomocą touchpada"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Przesuń w górę za pomocą 3 palców i przytrzymaj. Kliknij, aby poznać więcej gestów."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Wyświetlanie wszystkich aplikacji za pomocą klawiatury"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Naciśnij klawisz działania w dowolnym momencie. Kliknij, aby poznać więcej gestów."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Dodatkowe przyciemnienie jest teraz częścią paska jasności"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Możesz teraz dodatkowo przyciemnić ekran, jeszcze bardziej zmniejszając poziom jasności u góry ekranu.\n\nTa funkcja sprawdza się najlepiej, gdy jesteś w ciemnym otoczeniu."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Usuń skrót do dodatkowego przyciemnienia"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Skrót do dodatkowego przyciemnienia został usunięty. Aby zmniejszyć jasność, użyj standardowego paska jasności."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index e74f578..d5856af 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Incluir anotação"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Gravador de tela"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniciar gravação?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Durante a gravação, o Android tem acesso às informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Enquanto você grava um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar gravação"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Escolha um app para gravar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar áudio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Áudio do dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons do dispositivo, como música, chamadas e toques"</string>
@@ -139,9 +143,9 @@
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Você está transmitindo a tela inteira para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Você está transmitindo a tela inteira para o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Você está transmitindo a tela inteira para um dispositivo por perto"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para o <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para o dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para um dispositivo por perto"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Você está transmitindo para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Você está transmitindo para um dispositivo por perto"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotipo do app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toque para cancelar a autenticação"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou compartilhar o áudio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
     <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_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>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
-    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
     <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
     <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Legenda instantânea"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Você não será perturbado por sons e vibrações, exceto alarmes. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na tela de bloqueio"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"O widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> foi adicionado à tela de bloqueio"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dispensar"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Adicionar widgets"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Tenha acesso rápido aos widgets de seus apps favoritos sem desbloquear o tablet."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Acesse os widgets dos seus apps favoritos sem desbloquear o tablet."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Permitir qualquer widget na tela de bloqueio?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Abrir as configurações"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Reativar apps de trabalho?"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisará confirmar sua identidade. Além disso, não se esqueça que qualquer pessoa pode ver os widgets, mesmo quando o tablet está bloqueado. Alguns widgets podem não ter sido criados para ficar na tela de bloqueio e fazer isso talvez não seja seguro."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações na tela ou reproduzidas no dispositivo, como gravações ou transmissões. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudios que você tocar."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudios."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tela cheia"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Um único app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Compartilhar ou gravar um app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando você compartilha, grava ou transmite a tela, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Início"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartilhe ou grave um app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Compartilhar a tela com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartilhar um app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmissão?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando você transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando você transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Começar a transmissão"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir tela inteira"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando você está transmitindo a tela inteira, tudo nela fica visível. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando você transmite um app, todas as informações visíveis ou abertas nele ficam visíveis. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir tela"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Escolha um app para transmitir"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Começar a compartilhar?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quando você compartilha, grava ou transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quando você compartilha, grava ou transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Início"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Próxima"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"O compartilhamento é pausado na troca de apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Compartilhar este app"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Voltar"</string>
@@ -649,7 +661,7 @@
     <string name="stream_ring" msgid="7550670036738697526">"Toques"</string>
     <string name="stream_music" msgid="2188224742361847580">"Mídia"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Alarme"</string>
-    <string name="stream_notification" msgid="7930294049046243939">"Notificação"</string>
+    <string name="stream_notification" msgid="7930294049046243939">"Notificações"</string>
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"Multifrequência de dois tons"</string>
     <string name="stream_accessibility" msgid="3873610336741987152">"Acessibilidade"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad.\n\nVocê também pode usar o atalho de teclado Ação + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Muito bem!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para acessar sua tela inicial a qualquer momento, deslize de baixo para cima na tela com três dedos."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Legal!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Você concluiu o gesto para acessar a tela inicial."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acessar os apps, pressione a tecla de ação no teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Você concluiu o gesto da tecla de ação.\n\nA tecla de ação + / mostra todos os atalhos disponíveis."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Encoberto"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquear para visualizar"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para voltar"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize para a esquerda ou direita usando três dedos. Toque para aprender outros gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para acessar a tela inicial"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"O recurso Escurecer a tela agora faz parte da barra de brilho"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Agora, na parte de cima, é possível usar o recurso Escurecer a tela, que diminui ainda mais o nível de brilho.\n\nIsso funciona melhor quando você está em um ambiente escuro."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remover atalho de Escurecer a tela"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Atalho de Escurecer a tela removido. Use a barra normal para diminuir o brilho."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a21f475..6589b0f 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"A app <xliff:g id="APPNAME">%1$s</xliff:g> e outras apps abertas detetaram esta captura de ecrã."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Adicionar a uma nota"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Gravador de ecrã"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"A processar a gravação de ecrã"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação persistente de uma sessão de gravação de ecrã"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniciar a gravação?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Enquanto está a gravar, o Android tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Enquanto está a gravar uma app, o Android tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Começar gravação"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar o ecrã?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar uma app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar o ecrã inteiro"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando está a gravar o ecrã inteiro, tudo o que é apresentado no ecrã é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando está a gravar uma app, tudo o que é apresentado ou reproduzido nessa app é gravado. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar ecrã"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Escolha uma app para gravar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar áudio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Áudio do dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"O som do dispositivo, como música, chamadas e toques."</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logótipo da app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toque para cancelar a autenticação."</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Proteção ecrã"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não incomodar"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos de prioridade"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Sem dispositivos sincronizados disponíveis"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para associar ou desassociar um dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Partilha de áudio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou partilhar o áudio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string>
     <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_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>
@@ -382,7 +387,7 @@
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório de erro"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte do dispositivo foi afetada?"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que experiência com o dispositivo foi afetada?"</string>
     <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecione o tipo de problema"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de ecrã"</string>
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Legendas instantâneas"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legendas instantâneas"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmara do dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Quer desbloquear a câmara e o microfone?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir definições"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Ativar/desativar Vista geral"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos de prioridade"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluir"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Definições"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Não é incomodado por sons e vibrações, exceto de alarmes. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -459,7 +463,7 @@
     <string name="keyguard_retry" msgid="886802522584053523">"Deslize rapidamente para cima para tentar novamente."</string>
     <string name="accesssibility_keyguard_retry" msgid="8880238862712870676">"Deslize para cima para tentar o Desbloqueio facial novamente"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para utilizar o NFC"</string>
-    <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua entidade."</string>
+    <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização."</string>
     <string name="do_disclosure_with_name" msgid="2091641464065004091">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
     <string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"Este dispositivo foi fornecido por <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
     <string name="phone_hint" msgid="6682125338461375925">"Deslize rapid. a partir do ícone para aceder ao telemóvel"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets no ecrã de bloqueio"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado ao ecrã de bloqueio"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize rapidamente para a esquerda para iniciar o tutorial coletivo"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignorar"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"A app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vai ter acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou a transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Começar a gravar ou a transmitir?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que fornece esta função vai ter acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou a transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Ecrã inteiro"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Só uma app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Partilhe ou grave uma app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Começar a gravar ou a transmitir com a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando está a partilhar, gravar ou transmitir, a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando está a partilhar, gravar ou transmitir uma app, a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Partilhe ou grave uma app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Partilhar o seu ecrã com a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Partilhar uma app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Partilhar ecrã inteiro"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando está a partilhar o ecrã inteiro, tudo o que estiver no ecrã é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando está a partilhar uma app, tudo o que é mostrado ou reproduzido nessa app é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partilhar ecrã"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou esta opção"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmitir?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando está a transmitir conteúdo, o Android tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando está a transmitir uma app, o Android tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Começar a transmitir"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha uma app para partilhar"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir o ecrã?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir uma app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir ecrã inteiro"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando está a transmitir todo o ecrã, tudo o que estiver no seu ecrã é visível. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando está a transmitir uma app, tudo o que é mostrado ou reproduzido nessa app fica visível. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir ecrã"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Escolha uma app para transmitir"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Começar a partilhar?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quando está a partilhar, gravar ou transmitir conteúdo, o Android tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quando está a partilhar, gravar ou transmitir uma app, o Android tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Iniciar"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Seguinte"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"A partilha é pausada quando muda de app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Partilhar antes esta app"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Voltar"</string>
@@ -575,7 +587,7 @@
     <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"Este dispositivo foi fornecido pela <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
     <string name="quick_settings_disclosure_management_named_vpn" msgid="4137564460025113168">"Este dispositivo pertence à sua organização e está ligado à Internet através da app <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="quick_settings_disclosure_named_management_named_vpn" msgid="2169227918166358741">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está ligado à Internet através da app <xliff:g id="VPN_APP">%2$s</xliff:g>"</string>
-    <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence à sua entidade."</string>
+    <string name="quick_settings_disclosure_management" msgid="5515296598440684962">"Este dispositivo pertence à sua organização."</string>
     <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string>
     <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"Este dispositivo pertence à sua organização e está ligado à Internet através de VPNs"</string>
     <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"Este dispositivo pertence à organização <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> e está ligado à Internet através de VPNs"</string>
@@ -588,15 +600,15 @@
     <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"As suas apps pessoais estão ligadas à Internet através da app <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"Este dispositivo está ligado à Internet através da app <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="monitoring_title_financed_device" msgid="3659962357973919387">"Este dispositivo foi fornecido pela <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>."</string>
-    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestão de dispositivos"</string>
+    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"Gestão do dispositivo"</string>
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"Registos de rede"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Certificados da AC"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Ver Políticas"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Ver controlos"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para obter mais informações."</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"Este dispositivo pertence à entidade <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para mais informações."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"A <xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> pode conseguir aceder aos dados associados a este dispositivo, gerir apps e alterar as definições do mesmo.\n\nSe tiver perguntas, contacte a <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence à sua entidade.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para obter mais informações."</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"Este dispositivo pertence à sua organização.\n\nO administrador de TI pode monitorizar e gerir as definições, o acesso empresarial, as apps, os dados associados ao dispositivo e as informações de localização do mesmo.\n\nContacte o administrador de TI para mais informações."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"A sua entidade instalou uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"A sua entidade instalou uma autoridade de certificação no seu perfil de trabalho. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Está instalada uma autoridade de certificação neste dispositivo. O tráfego da sua rede segura pode ser monitorizado ou alterado."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, boa ligação"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, ligação disponível"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satélite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"O Sintonizador da interface do sistema disponibiliza-lhe formas adicionais ajustar e personalizar a interface do utilizador do Android. Estas funcionalidades experimentais podem ser alteradas, deixar de funcionar ou desaparecer em versões futuras. Prossiga com cuidado."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos de teclado"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado da pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto para retroceder"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto para aceder ao ecrã principal"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad a mostrar três dedos a moverem-se para a direita e esquerda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecrã de dispositivo a mostrar uma animação do gesto para retroceder"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para retroceder, deslize rapidamente para a esquerda ou direita com 3 dedos em qualquer parte do touchpad.\n\nPara o fazer, também pode usar o atalho de teclado Ação + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Muito bem!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para aceder ao ecrã principal em qualquer altura, deslize rapidamente com 3 dedos de baixo para cima no ecrã."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Boa!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Concluiu o gesto para aceder ao ecrã principal."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para aceder às suas apps, prima a tecla de ação no teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Concluiu o gesto da tecla de ação.\n\nA ação + / mostra todos os atalhos que tem disponíveis."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Para aceder ao ecrã principal, deslize rapidamente para cima com 3 dedos no touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para ver as apps recentes, deslize rapidamente para cima e mantenha premido com 3 dedos no touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todas as suas apps, prima a tecla de ação no teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Revisto"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloqueie para ver"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para retroceder"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize rapidamente para a esquerda ou direita com 3 dedos. Toque para aprender mais gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para aceder ao ecrã principal"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize rapidamente para cima e mantenha premido com 3 dedos. Toque para aprender mais gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todas as apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Prima a tecla de ação em qualquer altura. Toque para aprender mais gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Agora, o escurecimento extra faz parte da barra do brilho"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Agora, pode tornar o ecrã ainda mais escuro reduzindo ainda mais o nível de brilho a partir da parte superior do ecrã.\n\nIsto funciona melhor quando está num ambiente escuro."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remover atalho do escurecimento extra"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Atalho do escurecimento extra removido. Para reduzir o brilho, use a barra do brilho normal."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index e74f578..d5856af 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> e outros apps abertos detectaram essa captura de tela."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Incluir anotação"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Incluir link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Gravador de tela"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processando gravação de tela"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificação contínua para uma sessão de gravação de tela"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Iniciar gravação?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Durante a gravação, o Android tem acesso às informações na tela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Enquanto você grava um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Iniciar gravação"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Gravar a tela?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Gravar a tela toda"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Quando você grava a tela toda, tudo o que aparece nela é registrado. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Quando você grava um app, todas as informações visíveis ou abertas nele ficam registradas. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Gravar a tela"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Escolha um app para gravar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Gravar áudio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Áudio do dispositivo"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons do dispositivo, como música, chamadas e toques"</string>
@@ -139,9 +143,9 @@
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Interromper compartilhamento"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Transmitindo a tela"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Parar transmissão?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Você está transmitindo a tela inteira para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Você está transmitindo a tela inteira para o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Você está transmitindo a tela inteira para um dispositivo por perto"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para o <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para o dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Você está transmitindo o app <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> para um dispositivo por perto"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"Você está transmitindo para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"Você está transmitindo para um dispositivo por perto"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Enviar"</string>
     <string name="cancel" msgid="1089011503403416730">"Cancelar"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotipo do app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmar"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tentar novamente"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Toque para cancelar a autenticação"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Protetor de tela"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Não perturbe"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modos prioritários"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Não há dispositivos pareados disponíveis"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toque para conectar ou desconectar um dispositivo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Compartilhamento de áudio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Toque para mudar ou compartilhar o áudio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string>
     <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_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>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string>
     <string name="performance" msgid="6552785217174378320">"Desempenho"</string>
     <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string>
-    <string name="thermal" msgid="6758074791325414831">"Térmico"</string>
+    <string name="thermal" msgid="6758074791325414831">"Temperatura"</string>
     <string name="custom" msgid="3337456985275158299">"Personalizado"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string>
     <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Legenda instantânea"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Abrir as Configurações"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Outro dispositivo"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Alternar Visão geral"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modos prioritários"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Concluído"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Configurações"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurar"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Você não será perturbado por sons e vibrações, exceto alarmes. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizar"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgets na tela de bloqueio"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"O widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> foi adicionado à tela de bloqueio"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Deslize para a esquerda para iniciar o tutorial compartilhado"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizar"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Dispensar"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Adicionar widget"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Concluído"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Adicionar widgets"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Tenha acesso rápido aos widgets de seus apps favoritos sem desbloquear o tablet."</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Acesse os widgets dos seus apps favoritos sem desbloquear o tablet."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Permitir qualquer widget na tela de bloqueio?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Abrir as configurações"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Reativar apps de trabalho?"</string>
@@ -510,7 +513,7 @@
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Todos podem ver os widgets na tela de bloqueio, mesmo com o tablet bloqueado."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"desmarcar widget"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgets da tela de bloqueio"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisará confirmar sua identidade. Além disso, não se esqueça que qualquer pessoa pode ver os widgets, mesmo quando o tablet está bloqueado. Alguns widgets podem não ter sido criados para ficar na tela de bloqueio e fazer isso talvez não seja seguro."</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Para abrir um app usando um widget, você precisa confirmar sua identidade. E não se esqueça que qualquer pessoa pode ver os widgets, mesmo com o tablet bloqueado. Além disso, alguns apps não foram criados para a tela de bloqueio, é melhor manter a segurança."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"Entendi"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"menu suspenso"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações na tela ou reproduzidas no dispositivo, como gravações ou transmissões. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudios que você tocar."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou reproduzidas durante uma gravação ou transmissão. Isso inclui senhas, detalhes de pagamento, fotos, mensagens e áudios."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tela cheia"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Um único app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Compartilhar ou gravar um app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando você compartilha, grava ou transmite a tela, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Início"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Compartilhe ou grave um app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Compartilhar a tela com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Compartilhar um app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Compartilhar a tela inteira"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Quando você compartilha a tela inteira, tudo nela fica visível para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmissão?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando você transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando você transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Começar a transmissão"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmitir tela inteira"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quando você está transmitindo a tela inteira, tudo nela fica visível. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quando você transmite um app, todas as informações visíveis ou abertas nele ficam visíveis. Por isso, tenha cuidado com senhas, detalhes da forma de pagamento, mensagens, fotos, áudios e vídeos."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmitir tela"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Escolha um app para transmitir"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Começar a compartilhar?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Quando você compartilha, grava ou transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Quando você compartilha, grava ou transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Início"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Próxima"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"O compartilhamento é pausado na troca de apps"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Compartilhar este app"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Voltar"</string>
@@ -649,7 +661,7 @@
     <string name="stream_ring" msgid="7550670036738697526">"Toques"</string>
     <string name="stream_music" msgid="2188224742361847580">"Mídia"</string>
     <string name="stream_alarm" msgid="16058075093011694">"Alarme"</string>
-    <string name="stream_notification" msgid="7930294049046243939">"Notificação"</string>
+    <string name="stream_notification" msgid="7930294049046243939">"Notificações"</string>
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"Bluetooth"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"Multifrequência de dois tons"</string>
     <string name="stream_accessibility" msgid="3873610336741987152">"Acessibilidade"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satélite, conexão boa"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satélite, conexão disponível"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS via satélite"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Chamadas de emergência ou SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Perfil de trabalho"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diversão para alguns, mas não para todos"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"O sintonizador System UI fornece maneiras adicionais de ajustar e personalizar a interface do usuário do Android. Esses recursos experimentais podem mudar, falhar ou desaparecer nas versões futuras. Prossiga com cuidado."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Acessibilidade"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Atalhos do teclado"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Nenhum resultado de pesquisa"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto de volta"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto de início"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tecla de ação"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Muito bem!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad mostrando 3 dedos deslizando para a direita e esquerda"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Tela do dispositivo mostrando a animação do gesto de volta"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para voltar, deslize para a esquerda ou direita usando 3 dedos em qualquer lugar do touchpad.\n\nVocê também pode usar o atalho de teclado Ação + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Muito bem!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para acessar sua tela inicial a qualquer momento, deslize de baixo para cima na tela com três dedos."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Legal!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Você concluiu o gesto para acessar a tela inicial."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tecla de ação"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para acessar os apps, pressione a tecla de ação no teclado."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Parabéns!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Você concluiu o gesto da tecla de ação.\n\nA tecla de ação + / mostra todos os atalhos disponíveis."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Se quiser acessar a tela inicial, deslize para cima com três dedos no touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Se quiser ver os apps recentes, deslize para cima e pressione com três dedos no touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para ver todos os apps, pressione a tecla de ação no teclado"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Encoberto"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Desbloquear para visualizar"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Use o touchpad para voltar"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Deslize para a esquerda ou direita usando três dedos. Toque para aprender outros gestos."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Use o touchpad para acessar a tela inicial"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Deslize para cima e pressione com três dedos. Toque para aprender outros gestos."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Use o teclado para ver todos os apps"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pressione a tecla de ação a qualquer momento. Toque para aprender outros gestos."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"O recurso Escurecer a tela agora faz parte da barra de brilho"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Agora, na parte de cima, é possível usar o recurso Escurecer a tela, que diminui ainda mais o nível de brilho.\n\nIsso funciona melhor quando você está em um ambiente escuro."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Remover atalho de Escurecer a tela"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Atalho de Escurecer a tela removido. Use a barra normal para diminuir o brilho."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d0895ad..59136e0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> și alte aplicații deschise au detectat această captură de ecran."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Adaugă în notă"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Include linkul"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Recorder pentru ecran"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Se procesează înregistrarea"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificare în curs pentru o sesiune de înregistrare a ecranului"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Începi înregistrarea?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Când înregistrezi, Android are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Când înregistrezi o aplicație, Android are acces la orice se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Începe înregistrarea"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Înregistrezi ecranul?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Înregistrează o aplicație"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Înregistrează tot ecranul"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Când înregistrezi întregul ecran, se înregistrează tot ce apare pe ecran. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Când înregistrezi o aplicație, se înregistrează tot ce se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Înregistrează ecranul"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Alege aplicația de înregistrat"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Înregistrează audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Conținutul audio de la dispozitiv"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sunetul de la dispozitiv, precum muzică, apeluri și tonuri de apel"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimite"</string>
     <string name="cancel" msgid="1089011503403416730">"Anulează"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Sigla aplicației"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmă"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Încearcă din nou"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Atinge pentru a anula autentificarea"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screensaver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Nu deranja"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Moduri cu prioritate"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Niciun dispozitiv conectat disponibil"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Atinge pentru a conecta sau deconecta un dispozitiv"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Permiterea accesului la audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Atinge pentru a comuta sau a permite accesul la conținutul audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Subtitrări live"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrări live"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Deschide Setări"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Moduri cu prioritate"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Gata"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Setări"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Configurează"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizează"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgeturi pe ecranul de blocare"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget adăugat pe ecranul de blocare"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Glisează spre stânga pentru a începe tutorialul pentru comunitate"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizează"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Respinge"</string>
@@ -507,7 +510,7 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"elimină widgetul"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"plasează widgetul selectat"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Widgeturi pe ecranul de blocare"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Oricine vede widgeturile pe ecranul de blocare, chiar dacă tableta e blocată"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Oricine poate vedea widgeturile pe ecranul de blocare, chiar cu tableta blocată"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"deselectează widgetul"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Widgeturi pe ecranul de blocare"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Pentru a deschide o aplicație folosind un widget, va trebui să-ți confirmi identitatea. În plus, reține că oricine poate să vadă widgeturile, chiar dacă tableta este blocată. Este posibil ca unele widgeturi să nu fi fost create pentru ecranul de blocare și poate fi nesigur să le adaugi aici."</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrezi sau proiectezi. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redai."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Începi să înregistrezi sau să proiectezi?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Serviciul care oferă această funcție va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrezi sau proiectezi. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redai."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tot ecranul"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"O singură aplicație"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Distribuie o aplicație sau înregistreaz-o"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Începi să înregistrezi sau să proiectezi cu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Când permiți accesul, înregistrezi sau proiectezi, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Când permiți accesul, înregistrezi sau proiectezi o aplicație, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> are acces la orice se afișează pe ecran sau se redă în aplicație. Prin urmare, ai grijă cu informații cum ar fi parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Începe"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Permite accesul la o aplicație sau înregistreaz-o"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Permiți accesul <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> la ecran?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Permite accesul la o aplicație"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Permite accesul la tot ecranul"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Când permiți accesul la tot ecranul, tot conținutul de pe ecran este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Când permiți accesul la o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Permite accesul la ecran"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a dezactivat această opțiune"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Începi să proiectezi?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Când proiectezi, Android are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Când proiectezi o aplicație, Android are acces la orice se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Începe să proiectezi conținut"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Alege aplicația de trimis"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Proiectezi ecranul?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Proiectează o aplicație"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Proiectează tot ecranul"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Când proiectezi tot ecranul, tot conținutul de pe ecran este vizibil. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Când proiectezi o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Proiectează ecranul"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Alege aplicația de proiectat"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Începi să permiți accesul?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Când permiți accesul, înregistrezi sau proiectezi, Android are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Când permiți accesul, înregistrezi sau proiectezi o aplicație, Android are acces la orice se afișează pe ecran sau se redă în aplicație. Prin urmare, ai grijă cu informații cum ar fi parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Începe"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Înainte"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Trimiterea se oprește când comuți între aplicații"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Trimite această aplicație"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Revino"</string>
@@ -581,7 +593,7 @@
     <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și este conectat la internet prin rețele VPN."</string>
     <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"E posibil ca organizația ta să monitorizeze traficul de rețea în profilul de serviciu"</string>
     <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"E posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul tău de serviciu"</string>
-    <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"Adminul IT poate vedea profilul de serviciu"</string>
+    <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"Administratorul IT poate vedea profilul de serviciu"</string>
     <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Este posibil ca rețeaua să fie monitorizată"</string>
     <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"Acest dispozitiv este conectat la internet prin rețele VPN."</string>
     <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"Aplicațiile pentru lucru sunt conectate la internet prin <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, conexiune bună"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, conexiune disponibilă"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prin satelit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Apeluri de urgență sau SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accesibilitate"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Comenzi rapide de la tastatură"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Niciun rezultat al căutării"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gestul Înapoi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gestul Ecran de pornire"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tastă de acțiuni"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Excelent!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad cu trei degete care se mișcă spre dreapta și spre stânga"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ecran de dispozitiv cu o animație pentru gestul Înapoi"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Pentru a reveni, glisează spre stânga sau spre dreapta cu trei degete oriunde pe touchpad.\n\nPoți folosi și comanda rapidă de la tastatură Action + ESC pentru aceasta."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Excelent!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Pentru a accesa oricând ecranul de pornire, glisează în sus cu trei degete din partea de jos a ecranului"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bravo!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ai finalizat gestul „accesează ecranul de pornire”."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasta de acțiuni"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Pentru a accesa aplicațiile, apasă tasta de acțiuni de pe tastatură."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Felicitări!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Ai finalizat gestul cu tasta de acțiuni.\n\nAcțiune + / afișează toate comenzile rapide disponibile."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Ca să accesezi pagina de pornire, glisează în sus cu trei degete pe touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ca să vezi aplicațiile recente, glisează în sus și ține apăsat cu trei degete pe touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ca să vezi toate aplicațiile, apasă tasta de acțiuni de pe tastatură"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Ascunsă"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Deblochează pentru a afișa"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Folosește-ți touchpadul ca să revii"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Glisează la stânga sau la dreapta cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Folosește-ți touchpadul ca să accesezi pagina de pornire"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Glisează în sus și ține apăsat cu trei degete. Atinge ca să înveți mai multe gesturi."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Folosește-ți tastatura ca să vezi toate aplicațiile"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Apasă oricând tasta de acțiuni. Atinge ca să înveți mai multe gesturi."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Luminozitatea redusă suplimentar face acum parte din bara de luminozitate"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Poți reduce suplimentar luminozitatea ecranului dacă scazi nivelul de luminozitate din partea de sus a ecranului.\n\nAcest lucru funcționează cel mai bine într-un mediu întunecat."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Elimină comanda rapidă de luminozitate redusă suplimentar"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"S-a eliminat comanda rapidă de luminozitate redusă suplimentar. Ca să reduci luminozitatea, folosește bara obișnuită de luminozitate."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 3811185..de43676 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" и другие запущенные продукты обнаружили создание скриншота."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Добавить в заметку"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Добавить ссылку"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Запись видео с экрана"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обработка записи с экрана…"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Начать запись?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Когда вы записываете видео с экрана, Android получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Когда вы записываете экран приложения, система Android получает доступ ко всему, что видно или воспроизводится в нем. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Начать запись"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Начать запись экрана?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записывать одно приложение"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записывать весь экран"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Во время записи всего экрана все данные и действия, которые на нем показываются, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Во время записи приложения все данные и действия, которые показываются в его окне, попадают на видео. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Запись экрана"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Выбор приложения для записи экрана"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Записывать аудио"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Звук с устройства"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук с вашего устройства, например музыка, звонки и рингтоны"</string>
@@ -127,19 +131,19 @@
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Не удалось сохранить запись видео с экрана."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не удалось начать запись видео с экрана."</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Остановить запись?"</string>
-    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Вы записываете свой экран."</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Вы записываете весь экран."</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Вы записываете экран приложения \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Остановить запись"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Демонстрация экрана"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Закрыть совместный доступ к экрану?"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Прекратить показ экрана?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Вы демонстрируете свой экран в приложении \"<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>\"."</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Вы демонстрируете свой экран в приложении."</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Вы демонстрируете экран приложения \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\"."</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Вы демонстрируете экран приложения."</string>
-    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Закрыть доступ"</string>
+    <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Прекратить показ"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Трансляция экрана"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"Прекратить трансляцию?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Вы транслируете свой экран на устройство \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"Вы транслируете весь экран на устройство \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"Вы транслируете свой экран на устройство поблизости."</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"Вы транслируете приложение \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" на устройство \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\"."</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"Вы транслируете приложение \"<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>\" на устройство поблизости."</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Отправить"</string>
     <string name="cancel" msgid="1089011503403416730">"Отмена"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Логотип приложения"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Подтвердить"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Повторить попытку"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Нажмите, чтобы отменить аутентификацию"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не беспокоить"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режимы приоритета"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Нет доступных сопряженных устройств"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Нажмите, чтобы подключить или отключить устройство."</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Отправка аудио"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Нажмите, чтобы переключить аудио или поделиться им"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сохранено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"отключить"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активировать"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Автосубтитры"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматические субтитры"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Открыть настройки"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Другое устройство"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Переключить режим обзора"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режимы приоритета"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Настройки"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Настроить"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Настроить"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Медленная зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Зарядка • Осталось <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виджеты на заблокированном экране"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" добавлен на заблокированный экран."</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Чтобы ознакомиться с руководством, проведите по экрану влево"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Настроить"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрыть"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Добавление, удаление и упорядочивание виджетов в этом пространстве"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Здесь можно добавить, удалить и переместить виджеты"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Добавить виджеты"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Нажмите и удерживайте, чтобы настроить виджеты."</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Настроить виджеты"</string>
@@ -495,7 +498,7 @@
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Добавить виджет"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Готово"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Добавить виджеты"</string>
-    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Быстрый доступ к виджетам любимых приложений, даже когда планшет заблокирован"</string>
+    <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Добавьте виджеты любимых приложений на заблокированный экран, чтобы их всегда можно было быстро посмотреть."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Разрешить добавлять любые виджеты на заблокированный экран?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Открыть настройки"</string>
     <string name="work_mode_off_title" msgid="5794818421357835873">"Включить рабочие приложения?"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Во время записи или трансляции у приложения \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" будет доступ ко всему, что видно или воспроизводится на устройстве, в том числе к паролям, сведениям о способах оплаты, фотографиям, сообщениям и аудио."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Начать запись или трансляцию?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Во время записи или трансляции у сервиса, предоставляющего эту функцию, будет доступ ко всему, что видно или воспроизводится на устройстве, включая пароли, сведения о способах оплаты, фотографии, сообщения и аудио."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Весь экран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Отдельное приложение"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Демонстрация или запись экрана приложения"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Начать запись или трансляцию через приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Когда вы демонстрируете, транслируете экран или записываете видео с него, приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Когда вы демонстрируете, записываете или транслируете экран приложения, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> получает доступ ко всему, что видно или воспроизводится в этом приложении. Будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Начать"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Демонстрация или запись экрана приложения"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Показать экран приложению \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Показать приложение"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показать весь экран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"При показе экрана целиком все, что на нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"При показе приложения все, что в нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показать экран"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" отключило эту возможность"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Начать трансляцию?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Во время трансляции система Android получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Когда вы транслируете экран приложения, система Android получает доступ ко всему, что видно или воспроизводится в нем. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Начать трансляцию"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выбор приложения для демонстрации"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Начать трансляцию экрана?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслировать одно приложение"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Транслировать весь экран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Во время трансляции будет видно все, что происходит на экране. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Во время трансляции будет видно все, что происходит в выбранном приложении. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Транслировать экран"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Выбор приложения для трансляции"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Начать показ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Когда вы демонстрируете, транслируете экран или записываете видео с него, система Android получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Когда вы демонстрируете, записываете или транслируете экран приложения, система Android получает доступ ко всему, что видно или воспроизводится в нем. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Начать"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Далее"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Общий доступ прерывается при переключении приложений"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Открыть доступ к этому приложению"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Переключиться обратно"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Спутниковая связь, хорошее качество соединения"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступно соединение по спутниковой связи"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Спутниковый SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Экстренные вызовы или спутниковый SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Рабочий профиль"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Внимание!"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner позволяет настраивать интерфейс устройства Android по вашему вкусу. В будущем эта экспериментальная функция может измениться, перестать работать или исчезнуть."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Специальные возможности"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ничего не найдено"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"назад\""</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест \"на главный экран\""</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавиша действия"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Отлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Три пальца двигаются вправо и влево по сенсорной панели"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"На экране устройства показана анимация для жеста \"Назад\""</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Чтобы вернуться назад, проведите по сенсорной панели тремя пальцами влево или вправо.\n\nВы также можете нажать клавишу действия + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Отлично!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы выполнили жест для перехода назад."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На главный экран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Чтобы перейти на главный экран, проведите снизу вверх тремя пальцами."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Неплохо!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Вы выполнили жест для перехода на главный экран."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавиша действия"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Чтобы перейти к приложениям, нажмите клавишу действия на клавиатуре."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Готово!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Вы выполнили жест с клавишей действия.\n\nЧтобы посмотреть доступные сочетания, нажмите клавишу действия и \"/\"."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Чтобы перейти на главный экран, проведите по сенсорной панели тремя пальцами вверх."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Чтобы увидеть недавние приложения, проведите по сенсорной панели тремя пальцами вверх и удерживайте."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Чтобы открыть список всех своих приложений, нажмите клавишу действия."</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Скрыто"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Разблокируйте экран, чтобы посмотреть."</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Используйте сенсорную панель, чтобы возвращаться назад"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Для этого проведите тремя пальцами влево или вправо. Нажмите, чтобы посмотреть другие жесты."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Используйте сенсорную панель, чтобы переходить на главный экран"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Для этого проведите тремя пальцами вверх и удерживайте. Нажмите, чтобы посмотреть другие жесты."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Открывайте список всех приложений с помощью клавиатуры"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Для этого можно использовать клавишу действия. Нажмите, чтобы посмотреть другие жесты."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Дополнительно уменьшать яркость теперь можно через стандартный ползунок"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Чтобы дополнительно понизить яркость экрана, откройте настройки в его верхней части.\n\nРекомендуем использовать эту функцию, когда вокруг темно."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Удалить быструю команду для дополнительного уменьшения яркости"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Быстрая команда для дополнительного уменьшения яркости удалена. Чтобы изменить уровень яркости, воспользуйтесь стандартным ползунком яркости."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 46b82e3..8d6cd09 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> සහ අනෙකුත් විවෘත යෙදුම් මෙම තිර රුව අනාවරණය කර ගෙන ඇත."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"සටහනට එක් කරන්න"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"සබැඳිය ඇතුළත් කරන්න"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"තිර රෙකෝඩරය"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"තිර පටිගත කිරීම සකසමින්"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"පටිගත කිරීම ආරම්භ කරන්න ද?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ඔබ පටිගත කරන අතරේ, Android හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ඔබ යෙදුමක් පටිගත කරන අතරතුර, Android හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"පටිගත කිරීම අරඹන්න"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ඔබේ තිරය පටිගත කරන්න ද?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"එක් යෙදුමක් පටිගත කරන්න"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"සම්පූර්ණ තිරය පටිගත කරන්න"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ඔබ ඔබේ සම්පූර්ණ තිරය පටිගත කරන විට, ඔබේ තිරයේ පෙන්වන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ඔබ යෙදුමක් පටිගත කරන විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් වාර්තා වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"තිරය පටිගත කරන්න"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"පටිගත කිරීමට යෙදුම තෝරන්න"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ඕඩියෝ පටිගත කරන්න"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"උපාංග ඕඩියෝ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"සංගීතය, ඇමතුම් සහ නාද රිද්ම වැනි ඔබේ උපාංගය වෙතින් ශබ්ද"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"යවන්න"</string>
     <string name="cancel" msgid="1089011503403416730">"අවලංගු කරන්න"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"යෙදුම් ලාංඡනය"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"තහවුරු කරන්න"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"නැවත උත්සාහ කරන්න"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"සත්‍යාපනය අවලංගු කිරීමට තට්ටු කරන්න"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"තිර සුරැකුම"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ඊතර නෙට්"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"බාධා නොකරන්න"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ප්‍රමුඛතා ප්‍රකාර"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"බ්ලූටූත්"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"යුගල කළ උපාංග නොතිබේ"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"උපාංගයක් සම්බන්ධ කිරීමට හෝ විසන්ධි කිරීමට තට්ටු කරන්න"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ශ්‍රව්‍ය බෙදා ගැනීම"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ශ්‍රව්‍ය මාරු කිරීමට හෝ බෙදා ගැනීමට තට්ටු කරන්න"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"සුරැකිණි"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"විසන්ධි කරන්න"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"සක්‍රිය කරන්න"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්‍රීය වෙයි"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"සජීවී සිරස්තල"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"සැකසීම් විවෘත කරන්න"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"වෙනත් උපාංගය"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"දළ විශ්ලේෂණය ටොගල කරන්න"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ප්‍රමුඛතා ප්‍රකාර"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"නිමයි"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"සැකසීම්"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"පිහිටුවන්න"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"එලාම හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්‍රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"අභිරුචිකරණය"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • සෙමින් ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ආරෝපණය වෙමින් • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>කින් සම්පූර්ණ වේ"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"අගුළු තිරයෙහි විජට්"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට් අගුළු තිරයට එක් කරන ලදි"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"පොදු නිබන්ධනය ආරම්භ කිරීමට වමට ස්වයිප් කරන්න"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"අභිරුචිකරණය කරන්න"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"අස් කරන්න"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට ඔබේ තිරයේ පෙනෙන හෝ පටිගත කිරීමේ දී හෝ විකාශනය කිරීමේ දී ඔබේ උපාංගයේ වාදනය වන සියලු තොරතුරු වෙත ප්‍රවේශය ඇත. මෙයට මුරපද, ගෙවීම් විස්තර, ඡායාරූප, පණිවුඩ, සහ ඔබ වාදනය කරන ශ්‍රව්‍ය වැනි තොරතුරු ඇතුළත් වේ."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"පටිගත කිරීම හෝ විකාශය කිරීම ආරම්භ කරන්න ද?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"මෙම කාර්යය සපයන සේවාවට තිරයේ පෙනෙන හෝ පටිගත කිරීමේ දී හෝ විකාශනය කිරීමේ දී ඔබේ උපාංගයේ වාදනය වන සියලු තොරතුරු වෙත ප්‍රවේශය ඇත. මෙයට මුරපද, ගෙවීම් විස්තර, ඡායාරූප, පණිවුඩ, සහ ඔබ වාදනය කරන ශ්‍රව්‍ය වැනි තොරතුරු ඇතුළත් වේ."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"සම්පූර්ණ තිරය"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"තනි යෙදුමක්"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"යෙදුමක් බෙදා ගන්න හෝ පටිගත කරන්න"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> සමග පටිගත කිරීම හෝ විකාශය කිරීම ආරම්භ කරන්න ද?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ඔබ බෙදා ගන්නා විට, පටිගත කරන විට, හෝ විකාශනය කරන විට, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ඔබ යෙදුමක් බෙදා ගන්නා විට, පටිගත කරන විට හෝ විකාශය කරන විට, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"අරඹන්න"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"යෙදුමක් බෙදා ගන්න හෝ පටිගත කරන්න"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> සමග ඔබේ තිරය බෙදා ගන්න ද?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"එක් යෙදුමක් බෙදා ගන්න"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"සම්පූර්ණ තිරය බෙදා ගන්න"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"ඔබ ඔබේ සම්පූර්ණ තිරය බෙදා ගන්නා විට, ඔබේ තිරයේ ඇති ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්‍යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ඔබ යෙදුමක් බෙදා ගන්නා විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්‍යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"තිරය බෙදා ගන්න"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> මෙම විකල්පය අබල කර ඇත"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"විකාශය ආරම්භ කරන්න ද?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ඔබ විකාශය කරන විට, Android හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ඔබ යෙදුමක් විකාශය කරන විට, Android හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"විකාශය ආරම්භ කරන්න"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"බෙදා ගැනීමට යෙදුම තෝරන්න"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ඔබේ තිරය විකාශය කරන්න ද?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"එක් යෙදුමක් විකාශය කරන්න"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"සමස්ත තිරය විකාශය කරන්න"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"ඔබ ඔබේ සම්පූර්ණ තිරය විකාශය කරන විට, ඔබේ තිරයේ ඇති ඕනෑම දෙයක් දෘශ්‍යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ඔබ යෙදුමක් විකාශය කරන විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් දෘශ්‍යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"විකාශ තිරය"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"විකාශය කිරීමට යෙදුම තෝරන්න"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"බෙදා ගැනීම ආරම්භ කරන්න ද?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"ඔබ බෙදා ගන්නා විට, පටිගත කරන විට, හෝ විකාශය කරන විට, Android හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"ඔබ යෙදුමක් බෙදා ගන්නා විට, පටිගත කරන විට හෝ විකාශය කරන විට, Android හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්‍රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"අරඹන්න"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"මීළඟ"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"ඔබ යෙදුම් මාරු කරන විට බෙදා ගැනීම විරාම වේ"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"ඒ වෙනුවට මෙම යෙදුම බෙදා ගන්න"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"ආපසු මාරු වන්න"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"චන්ද්‍රිකාව, හොඳ සම්බන්ධතාවයක්"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"චන්ද්‍රිකාව, සම්බන්ධතාවය තිබේ"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"චන්ද්‍රිකා SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"හදිසි ඇමතුම් හෝ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"කාර්යාල පැතිකඩ"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"සමහරක් දේවල් වලට විනෝදයි, නමුත් සියල්ලටම නොවේ"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"පද්ධති UI සුසරකය ඔබට Android පරිශීලක අතුරු මුහුණත වෙනස් කිරීමට හෝ අභිරුචිකරණය කිරීමට අමතර ක්‍රම ලබා දේ. මෙම පර්යේෂණාත්මක අංග ඉදිරි නිකුත් වීම් වල වෙනස් වීමට, වැඩ නොකිරීමට, හෝ නැතිවීමට හැක. ප්‍රවේශමෙන් ඉදිරියට යන්න."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ප්‍රවේශ්‍යතාව"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"යතුරු පුවරු කෙටි මං"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"සෙවීම් ප්‍රතිඵල නැත"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ආපසු අභිනය"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"නිවෙස් අභිනය"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ක්‍රියා යතුර"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"අනර්ඝ වැඩක්!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ඇඟිලි තුනක් දකුණට සහ වමට චලනය වන බව පෙන්වන ස්පර්ශක පුවරුව"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"පසුපස අභිනය සඳහා සජීවිකරණය පෙන්වන උපාංග තිරය"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"ආපසු යාමට, ස්පර්ශ පුවරුවවේ ඕනෑම තැනක ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න.\n\nඔබට මේ සඳහා යතුරු පුවරු කෙටිමං ක්‍රියාව + ESC ද භාවිත කළ හැක."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"අනර්ඝ වැඩක්!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"මුල් පිටුවට යන්න"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ඕනෑම වේලාවක ඔබේ මුල් තිරයට යාමට, ඔබේ තිරයේ පහළ සිට ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"කදිමයි!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කළා."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ක්‍රියා යතුර"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ඔබේ යෙදුම් වෙත ප්‍රවේශ වීමට, ඔබේ යතුරු පුවරුවෙහි ක්‍රියා යතුර ඔබන්න."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"සුබ පැතුම්!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ඔබ ක්‍රියා යතුරු අභිනය සම්පූර්ණ කළා.\n\nක්‍රියාව + / ඔබට ලබා ගත හැකි සියලු කෙටිමං පෙන්වයි."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"මුල් පිටුවට යාමට, ස්පර්ශ පුවරුව මත ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"මෑත යෙදුම් බැලීමට, ඉහළට ස්වයිප් කර ස්පර්ශ පුවරුව මත ඇඟිලි තුනකින් අල්ලාගෙන සිටින්න"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"ඔබේ සියලුම යෙදුම් බැලීමට, ඔබේ යතුරුපුවරුවේ ක්‍රියාකාරී යතුර ඔබන්න"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"නැවත සකස් කරන ලද"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"බැලීමට අගුළු හරින්න"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ආපසු යාමට ඔබේ ස්පර්ශ පුවරුව භාවිත කරන්න"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"මුල් පිටුවට යාමට ඔබේ ස්පර්ශ පුවරුව භාවිත කරන්න"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ඇඟිලි තුනක් භාවිතයෙන් ඉහළට ස්වයිප් කර අල්ලාගෙන සිටින්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"සියලුම යෙදුම් බැලීමට ඔබේ යතුරු පුවරුව භාවිත කරන්න"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ඕනෑම අවස්ථාවක ක්‍රියාකාරී යතුර ඔබන්න. තව ඉංගිත දැන ගැනීමට තට්ටු කරන්න."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"තවත් අඳුර දැන් දීප්ත තීරුවේ කොටසකි"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ඔබේ තිරයේ ඉහළ සිට දීප්තියේ මට්ටම තවත් අඩු කිරීමෙන් ඔබට දැන් තිරය තවත් අඳුරු කළ හැක.\n\nඔබ අඳුරු පරිසරයක සිටින විට මෙය වඩාත් හොඳින් ක්‍රියා කරයි."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"තවත් අඳුරු කෙටිමඟ ඉවත් කරන්න"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"තවත් අඳුරු කෙටිමඟ ඉවත් කරන ලදි. ඔබේ දීප්තිය අඩු කිරීමට, සාමාන්‍ය දීප්ත තීරුව භාවිත කරන්න."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b607739..7159285 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> a ďalšie otvorené aplikácie zaznamenali túto snímku obrazovky."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Pridať do poznámky"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Zahrnúť odkaz"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Rekordér obrazovky"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Spracúva sa záznam obrazovky"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Zobrazuje sa upozornenie týkajúce sa relácie záznamu obrazovky"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Chcete spustiť nahrávanie?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Počas nahrávania bude mať Android prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Počas nahrávania v aplikácii bude mať Android prístup k všetkému, čo sa v danej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Spustiť nahrávanie"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Chcete nahrávať obrazovku?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrávať jednu aplikáciu"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Nahrávať celú obrazovku"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri nahrávaní celej obrazovky sa zaznamená všetko, čo sa na nej zobrazuje. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri nahrávaní aplikácie sa zaznamená všetko, čo sa v nej zobrazuje alebo prehráva. Preto venujte pozornosť položkám, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrávať obrazovku"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Výber aplikácie na nahrávanie"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Nahrávať zvuk"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvuk zariadenia"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvuk zo zariadenia, napríklad hudba, hovory a tóny zvonenia"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Odoslať"</string>
     <string name="cancel" msgid="1089011503403416730">"Zrušiť"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo aplikácie"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potvrdiť"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Skúsiť znova"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Klepnutím zrušíte overenie"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Šetrič obrazovky"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Režim bez vyrušení"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Režimy priority"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nie sú k dispozícii žiadne spárované zariadenia"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Klepnutím pripojíte alebo odpojíte zariadenie"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Používať Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Zdieľanie zvuku"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Klepnutím prepnete alebo budete zdieľať zvuk"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Rekordér obrazovky"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Začať"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Ukončiť"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Nahrať problém"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenať problém"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Začať"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zastavte"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hlásenie chyby"</string>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string>
     <string name="performance" msgid="6552785217174378320">"Výkon"</string>
     <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string>
-    <string name="thermal" msgid="6758074791325414831">"Termálne"</string>
+    <string name="thermal" msgid="6758074791325414831">"Teplota"</string>
     <string name="custom" msgid="3337456985275158299">"Vlastné"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavenia vlastnej stopy"</string>
     <string name="restore_default" msgid="5259420807486239755">"Obnoviť predvolené"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Živý prepis"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Živý prepis"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Otvoriť Nastavenia"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Iné zariadenie"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Prepnúť prehľad"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Režimy priority"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Hotovo"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavenia"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavenie"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prispôsobiť"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Miniaplikácie na uzamknutej obrazovke"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g> bola pridaná na uzamknutú obrazovku"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Potiahnutím doľava spustite komunitný návod"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prispôsobiť"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Zavrieť"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Pridávajte aj odstraňujte miniaplikácie a meňte ich poradie v tomto priestore"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Tu pridávajte, odstraňujte a presúvajte miniaplikácie"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Pridať ďalšie miniaplikácie"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Miniaplikácie prispôsobíte dlhým stlačením"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Prispôsobiť miniaplikácie"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, ako sú heslá, platobné údaje, fotky, správy a prehrávaný zvuk."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Chcete spustiť nahrávanie alebo prenos?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Služba poskytujúca túto funkciu bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, ako sú heslá, platobné údaje, fotky, správy a prehrávaný zvuk."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Celá obrazovka"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Jedna aplikácia"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Vyberte aplikáciu, ktorú chcete zdieľať alebo nahrávať"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Chcete spustiť nahrávanie alebo prenos s aktivovaným povolením <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Počas zdieľania, nahrávania alebo prenosu bude mať <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Počas zdieľania, nahrávania alebo prenosu v aplikácii bude mať <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému, čo sa v danej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začať"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Aplikácia na zdieľanie alebo nahrávanie"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Chcete zdieľať obrazovku s aplikáciou <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Zdieľať jednu aplikáciu"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Zdieľať celú obrazovku"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri zdieľaní celej obrazovky vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa na nej zobrazuje. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri zdieľaní aplikácie vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa v zdieľanej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Zdieľať obrazovku"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> túto možnosť zakázala"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Chcete spustiť prenos?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Počas prenosu bude mať Android prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Počas prenosu v aplikácii bude mať Android prístup k všetkému, čo sa v danej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Spustiť prenos"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Výber aplikácie na zdieľanie"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Chcete prenášať obrazovku?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prenášať jednu aplikáciu"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Prenášať celú obrazovku"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pri prenášaní celej obrazovky je viditeľný všetok obsah na obrazovke. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Pri prenášaní aplikácie je viditeľný všetok obsah zobrazený alebo prehrávaný v tejto aplikácii. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Prenášať obrazovku"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Výber aplikácie na prenos"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Chcete spustiť zdieľanie?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Počas zdieľania, nahrávania alebo prenosu bude mať Android prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Počas zdieľania, nahrávania alebo prenosu v aplikácii bude mať Android prístup k všetkému zobrazovanému alebo prehrávaného obsahu v danej aplikácii. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Začať"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Ďalej"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Zdieľanie sa pozastaví, keď prepnete aplikácie"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Zdieľať radšej túto aplikáciu"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Prepnúť späť"</string>
@@ -588,7 +600,7 @@
     <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"Vaše osobné aplikácie sú k internetu pripojené prostredníctvom aplikácie <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"Toto zariadenie je k internetu pripojené prostredníctvom aplikácie <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="monitoring_title_financed_device" msgid="3659962357973919387">"Toto zariadenie poskytuje <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
-    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"Správa zariadení"</string>
+    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"Správa zariadenia"</string>
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"Zapisovanie do denníka siete"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Certifikáty CA"</string>
@@ -600,7 +612,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizácia nainštalovala pre toto zariadenie certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizácia nainštalovala pre váš pracovný profil certifikačnú autoritu. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"V tomto zariadení je nainštalovaná certifikačná autorita. Zabezpečená sieťová premávka môže byť sledovaná či upravená."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku na vašom zariadení."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje sieťovú premávku na vašom zariadení."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Správca aktivoval zapisovanie do denníka siete, ktoré sleduje premávku vo vašom pracovnom profile, ale nie osobnom."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"Toto zariadenie je pripojené na internet prostredníctvom aplikácie <xliff:g id="VPN_APP">%1$s</xliff:g>. Vaša sieťová aktivita, ako sú e‑maily a dáta prehliadania, je viditeľná pre poskytovateľa siete VPN."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"Toto zariadenie je k internetu pripojené prostredníctvom aplikácie <xliff:g id="VPN_APP">%1$s</xliff:g>. Vašu aktivitu v sieti vrátane e‑mailov a dát prehliadania vidí váš správca IT."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobrá kvalita pripojenia"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, pripojenie je k dispozícii"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Pomoc cez satelit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Tiesňové volania alebo pomoc v tiesni"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Pracovný profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Pri používaní tuneru postupujte opatrne"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Tuner používateľského rozhrania systému poskytujte ďalšie spôsoby ladenia a prispôsobenia používateľského rozhrania Android. Tieto experimentálne funkcie sa môžu v budúcich verziách zmeniť, ich poskytovanie môže byť prerušené alebo môžu byť odstránené. Pokračujte opatrne."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostupnosť"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klávesové skratky"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Žiadne výsledky vyhľadávania"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gesto prechodu späť"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gesto prechodu domov"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Akčný kláves"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Skvelé!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tri prsty na touchpade pohybujúce sa doprava a doľava"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Obrazovka zariadenia, na ktorej je animácia gesta späť"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ak chcete prejsť späť, potiahnite kdekoľvek na touchpade troma prstami doľava alebo doprava.\n\nMôžete použiť aj klávesovú skratku, teda akčný kláves + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Skvelé!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Na plochu môžete kedykoľvek prejsť potiahnutím troma prstami zdola obrazovky."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Výborne!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Dokončili ste gesto na prechod na plochu."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Akčný kláves"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ak chcete získať prístup k aplikáciám, stlačte na klávesnici akčný kláves."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Blahoželáme!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Dokončili ste gesto akčného klávesa.\n\nStlačením kombinácie akčný kláves + / zobrazíte všetky skratky, ktoré máte k dispozícii"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Ak sa chcete vrátiť na plochu, potiahnite po touchpade troma prstami nahor."</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ak si chcete zobraziť nedávne aplikácie, potiahnite po touchpade troma prstami nahor a pridržte ich."</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ak si chcete zobraziť všetky aplikácie, stlačte na klávesnici akčný kláves"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Zamaskované"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Zobrazíte odomknutím"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Prechádzajte späť pomocou touchpadu"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Potiahnite troma prstami doľava alebo doprava. Viac o gestách sa dozviete klepnutím."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Vráťte sa na plochu pomocou touchpadu"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Potiahnite troma prstami nahor a pridržte ich. Viac o gestách sa dozviete klepnutím."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Zobrazte si všetky aplikácie pomocou klávesnice"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Akčný kláves môžete stlačiť kedykoľvek. Viac o gestách sa dozviete klepnutím."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Mimoriadne stmavenie je teraz súčasťou posúvača na úpravu jasu"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Teraz môžete obrazovku mimoriadne stmaviť ešte ďalším znížením úrovne jasu v hornej časti obrazovky.\n\nNajlepšie to funguje v tmavom prostredí."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Odstrániť skratku mimoriadneho stmavenia"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Skratka mimoriadneho stmavenia bola odstránená. Ak chcete znížiť jas, použite bežný posúvač jasu."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 69f63b9..f2c466e 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> in druge odprte aplikacije so zaznale ta posnetek zaslona."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Dodaj v zapisek"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Vključi povezavo"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Snemalnik zaslona"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Obdelava videoposnetka zaslona"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Želite začeti snemati?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Med snemanjem ima Android dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Med snemanjem aplikacije ima Android dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Začni snemanje"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Želite posneti zaslon?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Snemanje ene aplikacije"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Snemanje celotnega zaslona"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Pri snemanju celotnega zaslona se posname vse, kar je prikazano na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Pri snemanju aplikacije se posname vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snemanje zaslona"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Izbira aplikacije za snemanje"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Snemanje zvoka"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Zvok v napravi"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Zvoki v napravi, kot so glasba, klici in toni zvonjenja."</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Pošlji"</string>
     <string name="cancel" msgid="1089011503403416730">"Prekliči"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logotip aplikacije"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Potrdite"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Poskusi znova"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Če želite preklicati preverjanje pristnosti, se dotaknite"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ohranjeval. zaslona"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ne moti"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prednostni načini"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Na voljo ni nobene seznanjene naprave"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Dotaknite se za vzpostavitev ali prekinitev povezave z napravo"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Deljenje zvoka"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Dotaknite se za preklop ali deljenje zvoka"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string>
     <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_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>
@@ -387,7 +392,7 @@
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string>
     <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string>
     <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string>
-    <string name="thermal" msgid="6758074791325414831">"Toplotno"</string>
+    <string name="thermal" msgid="6758074791325414831">"Toplota"</string>
     <string name="custom" msgid="3337456985275158299">"Po meri"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Nastavitve sledi po meri"</string>
     <string name="restore_default" msgid="5259420807486239755">"Obnovi privzeto"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Samodejni podnapisi"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Samodejni podnapisi"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Odpri nastavitve"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Druga naprava"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Vklop/izklop pregleda"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prednostni načini"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Končano"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Nastavitve"</string>
     <string name="zen_mode_on" msgid="9085304934016242591">"Vklopljeno"</string>
+    <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Vklopljeno • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string>
     <string name="zen_mode_off" msgid="1736604456618147306">"Izklopljeno"</string>
-    <!-- no translation found for zen_mode_set_up (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Nastavitev"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljanje v nastavitvah"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ni aktivnih načinov}=1{Način {mode} je aktiven}one{# način je aktiven}two{# načina sta aktivna}few{# načini so aktivni}other{# načinov je aktivnih}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Ne bodo vas motili zvoki ali vibriranje, razen v primeru alarmov, opomnikov, dogodkov in klicateljev, ki jih določite. Še vedno pa boste slišali vse, kar se boste odločili predvajati, vključno z glasbo, videoposnetki in igrami."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ne bodo vas motili zvoki ali vibriranje, razen v primeru alarmov. Še vedno pa boste slišali vse, kar se boste odločili predvajati, vključno z glasbo, videoposnetki in igrami."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Prilagodi"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Počasno polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Polnjenje • Napolnjeno čez <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Pripomočki na zaklenjenem zaslonu"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Pripomoček <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je bil dodan na zaklenjeni zaslon"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Povlecite levo, da zaženete vadnico za skupnost"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Prilagodi"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Opusti"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bo imela dostop do vseh podatkov, ki so med snemanjem ali predvajanjem prikazani na vašem zaslonu ali se predvajajo iz vaše naprave. To vključuje podatke, kot so gesla, podrobnosti o plačilu, fotografije, sporočila in zvok, ki ga predvajate."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Želite začeti snemati ali predvajati?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Storitev, ki zagotavlja to funkcijo, bo imela dostop do vseh podatkov, ki so med snemanjem ali predvajanjem prikazani na vašem zaslonu ali se predvajajo iz vaše naprave. To vključuje podatke, kot so gesla, podrobnosti o plačilu, fotografije, sporočila in zvok, ki ga predvajate."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Celoten zaslon"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Posamezna aplikacija"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Deljenje ali snemanje aplikacije"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Želite začeti snemati ali predvajati z aplikacijo <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Pri deljenju, snemanju ali predvajanju ima aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Pri deljenju, snemanju ali predvajanju aplikacije ima aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začni"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Deljenje ali snemanje aplikacije"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Želite deliti zaslon z aplikacijo <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Deli eno aplikacijo"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Deli celoten zaslon"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Pri deljenju celotnega zaslona je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri deljenju aplikacije je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli zaslon"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočila to možnost"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite začeti predvajati?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Pri predvajanju ima Android dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Pri predvajanju aplikacije ima Android dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Začni predvajanje"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Izbira aplikacije za deljenje"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite predvajati vsebino zaslona?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Predvajanje vsebine ene aplikacije"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Predvajanje vsebine celotnega zaslona"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Pri predvajanju vsebine celotnega zaslona je vidno vse na zaslonu. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Pri predvajanju vsebine aplikacije je vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Predvajanje zaslona"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Izbira aplikacije za predvajanje"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Želite začeti deliti?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Pri deljenju, snemanju ali predvajanju ima Android dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Pri deljenju, snemanju ali predvajanju aplikacije ima Android dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Začni"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Naprej"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Deljenje se začasno zaustavi ob preklopu aplikacije"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Delite to aplikacijo"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Preklopite nazaj"</string>
@@ -669,7 +681,7 @@
     <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Prostorski zvok"</string>
     <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izklopljeno"</string>
     <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string>
-    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje položaja glave"</string>
+    <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje premikov glave"</string>
     <string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string>
     <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string>
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satelit, dobra povezava"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satelit, povezava je na voljo"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS prek satelita"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Klici v sili ali SOS prek satelita"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Delovni profil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Zabavno za nekatere, a ne za vse"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Uglaševalnik uporabniškega vmesnika sistema vam omogoča dodatne načine za spreminjanje in prilagajanje uporabniškega vmesnika Android. Te poskusne funkcije lahko v prihodnjih izdajah kadar koli izginejo, se spremenijo ali pokvarijo. Bodite previdni."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Dostopnost"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Bližnjične tipke"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ni rezultatov iskanja"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Poteza za pomik nazaj"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Poteza za začetni zaslon"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Gumb za dejanje"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Odlično!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sledilna ploščica s tremi prsti, ki se premikajo desno in levo"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Zaslon naprave z animacijo, ki prikazuje potezo za pomik nazaj"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Za pomik nazaj povlecite levo ali desno s tremi prsti kjer koli na sledilni ploščici.\n\nUporabite lahko tudi bližnjični tipki Action + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Odlično!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Izvedli ste potezo za pomik nazaj."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Pomik na začetni zaslon"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Za pomik na začetni zaslon lahko kadar koli s tremi prsti povlečete navzgor z dna zaslona."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Odlično!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Izvedli ste potezo za pomik na začetni zaslon."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tipka za dejanja"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Za dostop do aplikacij pritisnite tipko za dejanja na tipkovnici."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Čestitamo!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Izvedli ste potezo za tipko za dejanja.\n\nČe hkrati pritisnete tipko za dejanja in poševnico naprej »/«, bodo prikazane vse razpoložljive bližnjice."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Za pomik na začetni zaslon povlecite s tremi prsti navzgor po sledilni ploščici"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Za ogled nedavnih aplikacij povlecite s tremi prsti navzgor po sledilni ploščici in pridržite"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Za ogled vseh aplikacij pritisnite tipko za dejanja na tipkovnici"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Zakrito"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Odklenite za ogled"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Uporaba sledilne ploščice za pomik nazaj"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"S tremi prsti povlecite levo ali desno. Dotaknite se, če želite spoznati več potez."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Uporaba sledilne ploščice za pomik na začetni zaslon"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"S tremi prsti povlecite navzgor in pridržite. Dotaknite se, če želite spoznati več potez."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Uporaba tipkovnice za prikaz vseh aplikacij"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Kadar koli pritisnite tipko za dejanja. Dotaknite se, če želite spoznati več potez."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Funkcija Zelo zatemnjeno je zdaj del vrstice za uravnavanje svetlosti"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Zdaj lahko zelo zatemnite zaslon tako, da na vrhu zaslona dodatno zmanjšate raven svetlosti.\n\nTa funkcija najbolje deluje v temnem okolju."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Odstrani bližnjico do funkcije Zelo zatemnjeno"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Bližnjica do funkcije Zelo zatemnjeno je odstranjena. Če želite zmanjšati svetlost, uporabite običajno vrstico za uravnavanje svetlosti."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b26c7d8..70ea421 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dhe aplikacionet e tjera të hapura zbuluan këtë pamje ekrani."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Shto te shënimi"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Përfshi lidhjen"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Regjistruesi i ekranit"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Regjistrimi i ekranit po përpunohet"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Njoftim i vazhdueshëm për një seancë regjistrimi të ekranit"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Të niset regjistrimi?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Kur ti regjistron, Android ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Kur ti regjistron një aplikacion, Android ka qasje te çdo gjë e dukshme ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesës, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Nis regjistrimin"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Të regjistrohet ekrani?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Regjistro një aplikacion"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Regjistro të gjithë ekranin"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kur regjistron të gjithë ekranin, regjistrohet çdo gjë e shfaqur në ekranin tënd. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kur regjistron një aplikacion, regjistrohet çdo gjë që shfaqet ose luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Regjistro ekranin"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Zgjidh aplikacionin për të regjistruar"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Regjistro audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audioja e pajisjes"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Tingulli nga pajisja, si muzika, telefonatat dhe tonet e ziles"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Dërgo"</string>
     <string name="cancel" msgid="1089011503403416730">"Anulo"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logoja e aplikacionit"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Konfirmo"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Provo përsëri"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Trokit për të anuluar vërtetimin"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Mbrojtësi i ekranit"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Eternet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Mos shqetëso"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Modalitetet e përparësisë"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth-i"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Nuk ofrohet për përdorim asnjë pajisje e çiftuar"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Trokit për të lidhur ose shkëputur një pajisje"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ndarja e audios"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Trokit për të ndërruar ose ndarë audion"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Titra në çast"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titrat në çast"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Hap \"Cilësimet\""</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Pajisje tjetër"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Kalo te përmbledhja"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Modalitetet e përparësisë"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"U krye"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cilësimet"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Konfiguro"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Nuk do të shqetësohesh nga tingujt dhe dridhjet, përveç alarmeve. Do të vazhdosh të dëgjosh çdo gjë që zgjedh të luash duke përfshirë muzikën, videot dhe lojërat."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizo"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Miniaplikacionet në ekranin e kyçjes"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Miniaplikacioni i <xliff:g id="WIDGET_NAME">%1$s</xliff:g> u shtua tek ekrani i kyçjes"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Rrëshqit shpejt majtas për të filluar udhëzuesin e përbashkët"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Personalizo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Hiq"</string>
@@ -506,10 +509,10 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"zgjidh miniaplikacionin"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"hiq miniaplikacionin"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"vendos miniaplikacionin e zgjedhur"</string>
-    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Kyç miniaplikacionet e ekranit"</string>
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"Çdo person mund të shikojë miniaplikacionet në ekranin tënd të kyçjes, edhe nëse tableti është i kyçur."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"anulo zgjedhjen e miniaplikacionit"</string>
-    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Kyç miniaplikacionet e ekranit"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Miniaplikacionet në ekranin e kyçjes"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Për të hapur një aplikacion duke përdorur një miniaplikacion, do të duhet të verifikosh që je ti. Ki parasysh gjithashtu që çdo person mund t\'i shikojë, edhe kur tableti yt është i kyçur. Disa miniaplikacione mund të mos jenë planifikuar për ekranin tënd të kyçjes dhe mund të mos jetë e sigurt t\'i shtosh këtu."</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"E kuptova"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet, si dhe audion që luan ti."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Të niset regjistrimi ose transmetimi?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione, si p.sh.: fjalëkalimet, detajet e pagesave, fotografitë, mesazhet, si dhe audion që luan ti."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Të gjithë ekranin"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Vetëm një aplikacion"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Ndaj ose regjistro një aplikacion"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Të niset regjistrimi ose transmetimi me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kur ti ndan, regjistron ose transmeton, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kur ti ndan, regjistron ose transmeton një aplikacion, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Nis"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Ndaj ose regjistro një aplikacion"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Të ndahet ekrani yt me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ndaj një aplikacion"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ndaj të gjithë ekranin"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kur ti ndan të gjithë ekranin, çdo gjë në ekranin tënd është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kur ti ndan një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ndaj ekranin"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> e ka çaktivizuar këtë opsion"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Të niset transmetimi?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kur ti transmeton, Android ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kur ti transmeton një aplikacion, Android ka qasje te çdo gjë e dukshme ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesës, mesazhet, fotografitë, si dhe audion dhe videon."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Nis transmetimin"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Zgjidh aplikacionin për të ndarë"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Të transmetohet ekrani yt?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmeto një aplikacion"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Transmeto të gjithë ekranin"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kur ti transmeton të gjithë ekranin, çdo gjë në ekranin tënd është e dukshme. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kur ti transmeton një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Transmeto ekranin"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Zgjidh aplikacionin për të transmetuar"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Të niset ndarja?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kur ti ndan, regjistron ose transmeton, Android ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kur ti ndan, regjistron ose transmeton një aplikacion, Android ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Nis"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Para"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Ndarja vendoset në pauzë kur ndërron aplikacionet"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Ndaj më mirë këtë aplikacion"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Kthehu përsëri"</string>
@@ -594,9 +606,9 @@
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Certifikatat CA"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Shiko politikat"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Shiko kontrollet"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"Kjo pajisje i përket <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes sate.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> mund të arrijë të qaset te të dhënat e lidhura me këtë pajisje, të menaxhojë aplikacionet dhe të ndryshojë cilësimet e kësaj pajisjeje.\n\nNëse ke pyetje, kontakto me <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
-    <string name="monitoring_description_management" msgid="4308879039175729014">"Kjo pajisje i përket organizatës sate.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes tënde.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
+    <string name="monitoring_description_management" msgid="4308879039175729014">"Kjo pajisje i përket organizatës sate.\n\nAdministratori i teknologjisë së informacionit mund të monitorojë dhe menaxhojë cilësimet, qasjen e korporatës, aplikacionet, të dhënat e lidhura me pajisjen tënde, si dhe informacionet e vendndodhjes së pajisjes sate.\n\nPër më shumë informacione, kontakto me administratorin e teknologjisë së informacionit."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizata jote instaloi një autoritet certifikate në këtë pajisje. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizata jote instaloi një autoritet certifikate në profilin tënd të punës. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Në këtë pajisje është instaluar një autoritet certifikate. Trafiku i rrjetit tënd të sigurt mund të monitorohet ose modifikohet."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sateliti. Lidhje e mirë"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sateliti. Ofrohet lidhje"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS satelitor"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Telefonatat e urgjencës ose SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profili i punës"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Argëtim për disa, por jo për të gjithë!"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit të jep mënyra shtesë për të tërhequr dhe personalizuar ndërfaqen Android të përdoruesit. Këto funksione eksperimentale mund të ndryshojnë, prishen ose zhduken në versionet e ardhshme. Vazhdo me kujdes."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qasshmëria"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Shkurtoret e tastierës"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Asnjë rezultat kërkimi"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Gjesti i kthimit prapa"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Gjesti për të shkuar tek ekrani bazë"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Tasti i veprimit"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"U krye"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Punë e shkëlqyer!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kthehu prapa"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Blloku me prekje që tregon tre gishta që lëvizin djathtas dhe majtas"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Ekrani i pajisjes që tregon një animacion për gjestin e kthimit prapa"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Për t\'u kthyer, rrëshqit shpejt majtas ose djathtas duke përdorur tri gishta kudo në bllokun me prekje.\n\nPër ta bërë këtë, mund të përdorësh gjithashtu shkurtoren e tastierës \"Action + ESC\"."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Punë e shkëlqyer!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Për të shkuar tek ekrani bazë në çdo kohë, rrëshqit shpejt lart me tre gishta nga fundi i ekranit."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bukur!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"E ke përfunduar gjestin e kalimit tek ekrani bazë."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Tasti i veprimit"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Për t\'u qasur në aplikacionet e tua, shtyp tastin e veprimit në tastierë."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Urime!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Ke përfunduar gjestin e tastit të veprimit.\n\nVeprimi + / shfaq të gjitha shkurtoret që janë të disponueshme për ty."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Për të shkuar tek ekrani bazë, rrëshqit shpejt lart me tre gishta në bllokun me prekje"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Për aplikacionet e fundit, rrëshqit shpejt lart dhe mbaj shtypur me tre gishta në bllokun me prekje"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Për të shikuar të gjitha aplikacionet, shtyp tastin e veprimit në tastierë"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Redaktuar"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Shkyçe për ta parë"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Përdor bllokun me prekje për t\'u kthyer prapa"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Rrëshqit shpejt majtas ose djathtas duke përdorur tre gishta. Trokit për të mësuar më shumë gjeste."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Përdor bllokun me prekje për të shkuar tek ekrani bazë"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Rrëshqit shpejt lart dhe mbaj shtypur me tre gishta. Trokit për të mësuar më shumë gjeste."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Përdor tastierën për të shikuar të gjitha aplikacionet"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Shtyp tastin e veprimit në çdo kohë. Trokit për të mësuar më shumë gjeste."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Modaliteti \"Shumë më i zbehtë\" tani është pjesë e shiritit të ndriçimit"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Tani mund ta bësh ekranin shumë më të zbehtë duke e ulur nivelin e ndriçimit edhe më tej nga kreu i ekranit.\n\nKjo funksionon më mirë kur je në një mjedis të errët."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Hiq shkurtoren e modalitetit \"Shumë më i zbehtë\""</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Shkurtorja e modalitetit \"Shumë më i zbehtë\" u hoq. Për të ulur ndriçimin, përdor shiritin e zakonshëm të ndriçimit."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 7acfbf5..6db372d 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> и друге отворене апликације су откриле овај снимак екрана."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додај у белешку"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Уврсти линк"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Снимач екрана"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обрађујемо видео снимка екрана"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Желите да започнете снимање?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају док снимате. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Када снимате апликацију, Android има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Започни снимање"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Желите да снимите екран?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Сними једну апликацију"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Сними цео екран"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Када снимате цео екран, снима се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Када снимате апликацију, снима се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Сними екран"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Одаберите апликацију коју желите да снимите"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Снимај звук"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Звук уређаја"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук са уређаја, на пример, музика, позиви и мелодије звона"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Пошаљи"</string>
     <string name="cancel" msgid="1089011503403416730">"Откажи"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Логотип апликације"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Потврди"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Пробај поново"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Додирните да бисте отказали потврду идентитета"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Чувар екрана"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Етернет"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не узнемиравај"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Приоритетни режими"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Није доступан ниједан упарени уређај"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Додирните да бисте повезали уређај или прекинули везу"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Дељење звука"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Додирните да бисте пребацили или делили звук"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Сачувано"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"прекините везу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активирајте"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Титл уживо"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Титл уживо"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Отвори Подешавања"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Други уређај"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Укључи/искључи преглед"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Приоритетни режими"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Подешавања"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Подеси"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Неће вас узнемиравати звукови и вибрације осим за аларме. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Прилагоди"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Виџети на закључаном екрану"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> је додат на закључани екран"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Превуците улево да бисте започели заједнички водич"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Прилагодите"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Одбаци"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додајте, уклоните и преуредите виџете у овом простору"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додајте, уклоните и преуредите виџете овде"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додајте још виџета"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Дуги притисак за прилагођавање виџета"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Прилагоди виџете"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Желите да почнете снимање или пребацивање?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Услуга која пружа ову функцију ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Цео екран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Једна апликација"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Делите или снимите апликацију"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Желите да почнете снимање или пребацивање помоћу апликације <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Када делите, снимате или пребацујете, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Када делите, снимате или пребацујете апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Покрени"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Делите или снимите апликацију"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Желите да делите екран са апликацијом <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Дели једну апликацију"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Дели цео екран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Када делите цео екран, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Када делите апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дели екран"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућила ову опцију"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Желите да започнете пребацивање?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Када пребацујете, Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Када пребацујете апликацију, Android има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Започни пребацивање"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Одаберите апликацију коју желите да делите"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Желите да пребаците екран?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Пребаци једну апликацију"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Пребаци цео екран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Када пребацујете цео екран, види се све што је на њему. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Када пребацујете апликацију, види се сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Пребацивање екрана"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Одаберите апликацију коју желите да пребаците"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Желите да почнете да делите?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Када делите, снимате или пребацујете, Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Када делите, снимате или пребацујете апликацију, Android има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато пазите на лозинке, информације о плаћању, поруке, слике, и аудио и видео садржај."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Покрени"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Даље"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Дељење се зауставља када мењате апликације"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Дели ову апликацију"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Врати"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Сателит, веза је добра"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Сателит, веза је доступна"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Хитна помоћ преко сателита"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Хитни позиви или хитна помоћ"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Пословни профил"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Забава за неке, али не за све"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Тјунер за кориснички интерфејс система вам пружа додатне начине за подешавање и прилагођавање Android корисничког интерфејса. Ове експерименталне функције могу да се промене, откажу или нестану у будућим издањима. Будите опрезни."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Приступачност"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Тастерске пречице"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нема резултата претраге"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Покрет за враћање"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Покрет за почетну страницу"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Тастер радњи"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Одлично!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Тачпед са приказом три прста који се померају удесно и улево"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран уређаја са приказом анимације покрета за назад"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Да бисте се вратили, превуците улево са три прста било где на тачпеду.\n\nМожете да користите и тастерску пречицу Alt + ESC за ово."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Одлично!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Довршили сте покрет за повратак."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Иди на почетни екран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Да бисте отишли на почетни екран у било ком тренутку, превуците нагоре од дна екрана помоћу три прста."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Свака част!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Довршили сте покрет за повратак на почетну страницу."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Тастер радњи"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Да бисте приступили апликацијама, притисните тастер радњи на тастатури."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Честитамо!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Довршили сте покрет помоћу тастера радњи.\n\nРадња + / приказује све пречице које су вам доступне."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Да бисте отишли на почетни екран, превуците нагоре са три прста на тачпеду"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Да бисте прегледали недавне апликације, превуците нагоре и задржите са три прста на тачпеду"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Да бисте погледали све апликације, притисните тастер радњи на тастатури"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Редиговано"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Откључајте за приказ"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Користите тачпед да бисте се вратили"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Превуците улево или удесно са три прста. Додирните да бисте видели више покрета."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Користите тачпед да бисте отишли на почетни екран"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Превуците нагоре и задржите са три прста. Додирните да бисте видели више покрета."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Користите тастатуру да бисте прегледали све апликације"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Притисните тастер радњи у било ком тренутку. Додирните да бисте видели више покрета."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Додатно затамњивање је сада део траке за осветљеност"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Сада можете додатно да затамните екран смањивањем нивоа осветљености при врху екрана. \n\nОво најбоље функционише када сте у тамном окружењу."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Уклони пречицу за додатно затамњивање"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Уклоњена је пречица за додатно затамњивање. Да бисте смањили осветљеност, користите уобичајену траку за осветљеност."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b632dde..225e3b3 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> och andra öppna appar identifierade skärmbilden."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Lägg till i anteckning"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Inkludera länk"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Skärminspelare"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Behandlar skärminspelning"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Vill du starta inspelningen?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"När du spelar in har Android åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"När du spelar in en app har Android åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Börja spela in"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Vill du spela in det som visas på skärmen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Spela in en app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Spela in hela skärmen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"När du spelar in hela skärmen spelas allt som visas på skärmen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"När du spelar in en app spelas allt som visas eller spelas upp i appen in. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Spela in skärmen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Välj en app att spela in"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Spela in ljud"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Ljud på enheten"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Ljud från enheten, till exempel musik, samtal och ringsignaler"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Skicka"</string>
     <string name="cancel" msgid="1089011503403416730">"Avbryt"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Appens logotyp"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Bekräfta"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Försök igen"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tryck för att avbryta autentiseringen"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Skärmsläckare"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Stör ej"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Prioriterade lägen"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Det finns inga kopplade enheter tillgängliga"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Tryck för att ansluta eller koppla från en enhet"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ljuddelning"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Tryck för att byta eller dela ljud"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Skärminspelning"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Starta"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Stoppa"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Anmäl problem"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Felrapport"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Live Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Öppna Inställningar"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Annan enhet"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Aktivera och inaktivera översikten"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Prioriterade lägen"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Klar"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Inställningar"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ställ in"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Hantera i inställningarna"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Inga aktiva lägen}=1{{mode} är aktivt}other{# lägen är aktiva}}"</string>
     <string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir inte störd av ljud och vibrationer, förutom från alarm, påminnelser, händelser och specifika samtal. Ljudet är fortfarande på för sådant du väljer att spela upp, till exempel musik, videor och spel."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Du blir inte störd av ljud och vibrationer, förutom från alarm. Ljudet är fortfarande på för sådant du väljer att spela upp, till exempel musik, videor och spel."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Anpassa"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Widgetar på låsskärmen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Widgeten <xliff:g id="WIDGET_NAME">%1$s</xliff:g> har lagts till på låsskärmen"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Svep åt vänster för att börja med gruppguiden"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Anpassa"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Ignorera"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar till exempel lösenord, betalningsuppgifter, foton, meddelanden och ljud som du spelar upp."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Vill du börja spela in eller casta?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Den tjänst som tillhandahåller funktionen får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar till exempel lösenord, betalningsuppgifter, foton, meddelanden och ljud som du spelar upp."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Hela skärmen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"En enda app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Dela eller spela in en app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Vill du börja spela in eller casta med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"När du delar, spelar in eller castar har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"När du delar, spelar in eller castar en app har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Börja"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Dela eller spela in en app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Vill du dela skärmen med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Dela en app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Dela hela skärmen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"När du delar hela skärmen är allt på skärmen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"När du delar en app är allt som visas eller spelas upp i appen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dela skärmen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverat alternativet"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vill du börja casta?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"När du castar har Android åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"När du castar en app har Android åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Börja casta"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Välj en app att dela"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vill du casta skärmen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Casta en app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Casta hela skärmen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"När du castar hela skärmen är allt på skärmen synligt. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"När du castar en app är allt som visas eller spelas i den appen synligt. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Casta skärmen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Välj en app att casta"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vill du börja dela?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"När du delar, spelar in eller castar har Android åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"När du delar, spelar in eller castar en app har Android åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Börja"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Nästa"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Delningen pausas när du byter appar"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Dela den här appen i stället"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Byt tillbaka"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellit, bra anslutning"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellit, anslutning tillgänglig"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS-larm via satellit"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Nödsamtal eller SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Jobbprofil"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Kul för vissa, inte för alla"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Du kan använda inställningarna för systemgränssnitt för att justera användargränssnittet i Android. Dessa experimentfunktioner kan när som helst ändras, sluta fungera eller försvinna. Använd med försiktighet."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Tillgänglighet"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Kortkommandon"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Inga sökresultat"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Tillbaka-rörelse"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Rörelse för att öppna startskärmen"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Åtgärdstangent"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klar"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Bra jobbat!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tillbaka"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Tre fingrar rör sig åt höger och vänster på en styrplatta"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"En enhetsskärm visar en animation för rörelsen Tillbaka"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Gå tillbaka genom att svepa åt vänster eller höger med tre fingrar var som helst på styrplattan.\n\nDu kan även använda kortkommandot Åtgärd + Esc."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Bra jobbat!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du är klar med rörelsen för att gå tillbaka."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Återvänd till startskärmen"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Öppna startskärmen när som helst genom att svepa uppåt med tre fingrar från skärmens nederkant."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Bra!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Du är klar med rörelsen för att öppna startskärmen."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Åtgärdstangent"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Tryck på åtgärdstangenten på tangentbordet för att komma åt dina appar."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Grattis!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Du är klar med rörelsen med åtgärdstangenten.\n\nÅtgärd + / visar alla tillgängliga genvägar."</string>
     <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string>
     <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string>
     <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Svep uppåt på styrplattan med tre fingrar för att gå till startskärmen"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Svep uppåt på styrplattan med tre fingrar och håll kvar för att se nyligen använda appar"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Tryck på åtgärdstangenten på tangentbordet för att se alla appar"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Anonymiserad"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Lås upp för att visa"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Använd styrplattan för att gå tillbaka"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Svep åt vänster eller höger med tre fingrar. Tryck för att lära dig fler rörelser."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Använd styrplattan för att gå till startskärmen"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Svep uppåt med tre fingrar och håll kvar. Tryck för att lära dig fler rörelser."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Använd tangentbordet för att se alla appar"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Tryck på åtgärdstangenten när som helst. Tryck för att lära dig fler rörelser."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Extradimmat är nu en del av fältet för ljusstyrka"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Nu kan du göra skärmen extradimmad genom att sänka ljusstyrkan ännu mer från överst på skärmen.\n\nDetta fungerar bäst när omgivningen är mörk."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ta bort kortkommandot för extradimmat"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Kortkommandot för extradimmat har tagits bort. Använd det vanliga fältet för ljusstyrka om du vill sänka ljusstyrkan."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d3e4e5d..9fa8e55 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> na zingine zinazotumika zimetambua picha hii ya skrini."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ongeza kwenye dokezo"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Jumuisha kiungo"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g> <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Kinasa Skrini"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Inachakata rekodi ya skrini"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Ungependa kuanza kurekodi?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Unaporekodi, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Unaporekodi programu, Android inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Anza kurekodi"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ungependa kurekodi skrini yako?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekodi programu moja"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekodi skrini nzima"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Unaporekodi skrini yako nzima, chochote kinachoonyeshwa kwenye skrini yako kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Unaporekodi programu, chochote kinachoonyeshwa au kuchezwa kwenye programu hiyo kitarekodiwa. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekodi skrini"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Chagua programu ya kurekodi"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Rekodi sauti"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Sauti ya kifaa"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sauti kutoka kwenye kifaa chako, kama vile muziki, simu na milio ya simu"</string>
@@ -121,7 +125,7 @@
     <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Inarekodi skrini na sauti"</string>
     <string name="screenrecord_taps_label" msgid="1595690528298857649">"Onyesha sehemu za kugusa kwenye skrini"</string>
     <string name="screenrecord_stop_label" msgid="72699670052087989">"Acha"</string>
-    <string name="screenrecord_share_label" msgid="5025590804030086930">"Shiriki"</string>
+    <string name="screenrecord_share_label" msgid="5025590804030086930">"Tuma"</string>
     <string name="screenrecord_save_title" msgid="1886652605520893850">"Imehifadhi rekodi ya skrini"</string>
     <string name="screenrecord_save_text" msgid="3008973099800840163">"Gusa ili uangalie"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Hitilafu imetokea wakati wa kuhifadhi rekodi ya skrini"</string>
@@ -134,7 +138,7 @@
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Ungependa kuacha kuonyesha skrini?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Kwa sasa unatuma maudhui yaliyo katika skrini yako nzima kwenye programu"</string>
-    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kwa sasa unatumia <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g> pamoja na wengine"</string>
+    <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Kwa sasa unaonyesha <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Kwa sasa unatumia programu pamoja na wengine"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Acha kuonyesha skrini"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"Inatuma skrini"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Tuma"</string>
     <string name="cancel" msgid="1089011503403416730">"Ghairi"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Nembo ya programu"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Thibitisha"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Jaribu tena"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Gusa ili ughairi uthibitishaji"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Taswira ya skrini"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Usinisumbue"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Hali za kipaumbele"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Hakuna vifaa vilivyooanishwa vinavyopatikana"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Gusa ili uunganishe au utenganishe kifaa"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Kusikiliza Pamoja"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Gusa ili ubadilishe sauti au usikilize pamoja na wengine"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Manukuu Papo Hapo"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Manukuu Papo Hapo"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuacha kuzuia kamera ya kifaa?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Fungua Mipangilio"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Kifaa kingine"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Washa Muhtasari"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Hali za kipaumbele"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Nimemaliza"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mipangilio"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Weka mipangilio"</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 za kutumika}=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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Badilisha upendavyo"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji polepole • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Inachaji • Itajaa baada ya <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Wijeti kwenye skrini iliyofungwa"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g> imewekwa kwenye skrini iliyofungwa"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Telezesha kidole kushoto ili uanze mafunzo ya pamoja"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Weka mapendeleo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Funga"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> itaweza kufikia maelezo yote yanayoonekana kwenye skrini yako au yanayochezwa kwenye kifaa chako wakati wa kurekodi au kutuma. Hii ni pamoja na maelezo kama vile manenosiri, maelezo ya malipo, picha, ujumbe na sauti unayocheza."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Ungependa kuanza kurekodi au kutuma?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Huduma inayotoa utendaji huu itaweza kufikia maelezo yote yanayoonekana kwenye skrini yako au yanayochezwa kwenye kifaa chako wakati wa kurekodi au kutuma. Hii ni pamoja na maelezo kama vile manenosiri, maelezo ya malipo, picha, ujumbe na sauti unayocheza."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Skrini nzima"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Programu moja"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Shiriki au rekodi programu"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Ungependa kuanza kurekodi au kutuma ukitumia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Unaposhiriki, kurekodi au kutuma, programu ya <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Unaposhiriki, kurekodi au kutuma programu, programu, programu ya <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Anza"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Kurekodi au kuruhusu programu ifikiwe"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ungependa kuruhusu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ifikie skrini yako?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Ruhusu ufikiaji wa programu moja"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Ruhusu ufikiaji wa skrini nzima"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Unaporuhusu ufikiaji wa skrini nzima, chochote kilicho katika skrini yako kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Unaporuhusu ufikiaji wa programu, chochote kinachoonyeshwa au kuchezwa katika programu hiyo kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ruhusu ufikiaji wa skrini"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezima chaguo hili"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Ungependa kuanza kutuma?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Unapotuma, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Unapotuma programu, Android inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Anza kutuma"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Kuchagua programu utakayoruhusu ifikiwe"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ungependa kutuma maudhui yaliyo katika skrini yako?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Tuma maudhui ya programu moja"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Tuma maudhui katika skrini nzima"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Unapotuma maudhui katika skrini yako nzima, chochote kilicho kwenye skrini yako kitaonekana. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Unapotuma maudhui ya programu moja, chochote kinachoonekana au kucheza katika programu hiyo kitaonekana. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Tuma maudhui yaliyo kwenye skrini"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Kuchagua programu utakayotumia kutuma maudhui"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Ungependa kuanza kushiriki?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Unaposhiriki, kurekodi au kutuma, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Unaposhiriki, kurekodi au kutuma programu, Android inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Anza"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Endelea"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Itasitisha kushiriki unapobadilisha programu"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Shiriki programu hii badala yake"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Rejea"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Setilaiti, muunganisho thabiti"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Setilaiti, muunganisho unapatikana"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Msaada kupitia Setilaiti"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Simu za dharura"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Wasifu wa kazini"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Kinafurahisha kwa baadhi ya watu lakini si wote"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Kirekebishi cha kiolesura cha mfumo kinakupa njia zaidi za kugeuza na kubadilisha kiolesura cha Android ili kikufae. Vipengele hivi vya majaribio vinaweza kubadilika, kuharibika au kupotea katika matoleo ya siku zijazo. Endelea kwa uangalifu."</string>
@@ -1376,30 +1389,49 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ufikivu"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mikato ya kibodi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hamna matokeo ya utafutaji"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ishara ya kurudi nyuma"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Mguso wa kurudi kwenye skrini ya kwanza"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Kitufe cha vitendo"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Kazi nzuri!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Padi ya kugusa inayoonyesha vidole vitatu vikisonga kulia na kushoto"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Skrini ya kifaa inayoonyesha uhuishaji wa mguso wa nyuma"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Telezesha vidole vitatu kushoto au kulia mahali popote kwenye padi ya kugusa ili urudi nyuma.\n\nUnaweza pia kutumia mikato ya kibodi ya Kitendo pamoja na ESC kutekeleza kitendo hiki."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Kazi nzuri!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ili uende kwenye skrini ya kwanza wakati wowote, telezesha vidole vitatu juu kutoka sehemu ya chini ya skrini yako."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Safi!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Umeweka ishara ya kwenda kwenye skrini ya kwanza."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Kitufe cha vitendo"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Bonyeza kitufe cha vitendo kwenye kibodi yako ili ufikie programu zako."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Hongera!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Umekamilisha ishara ya kitufe cha vitendo.\n\nKitendo + / huonyesha njia zote za mkato zinazopatikana."</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>
     <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani vikiwa taswira ya skrini"</string>
     <string name="volume_undo_action" msgid="5815519725211877114">"Tendua"</string>
-    <string name="back_edu_toast_content" msgid="4530314597378982956">"Telezesha vidole vitatu kutoka kushoto au kulia kwenye padi ya kugusa ili urudi nyuma"</string>
+    <string name="back_edu_toast_content" msgid="4530314597378982956">"Telezesha vidole vitatu kushoto au kulia kwenye padi ya kugusa ili urudi nyuma"</string>
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Telezesha vidole vitatu juu kwenye padi ya kugusa ili urudi kwenye skrini ya kwanza"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Telezesha vidole vitatu juu na ushikilie kwenye padi ya kugusa ili uangalie programu za hivi majuzi"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Bonyeza kitufe cha vitendo kwenye kibodi yako ili uangalie programu zako zote"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Maandishi yameondolewa"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Fungua ili uone"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Kutumia padi yako ya kugusa ili kurudi nyuma"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Telezesha vidole vitatu kulia au kushoto. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Kutumia padi yako ya kugusa ili kurudi kwenye skrini ya kwanza"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Telezesha vidole vitatu juu na ushikilie. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Kutumia kibodi yako kuangalia programu zote"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Bonyeza kitufe cha vitendo wakati wowote. Gusa ili upate maelezo kuhusu miguso zaidi."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Kipunguza mwangaza zaidi sasa ni sehemu ya upau wa mwangaza"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Sasa unaweza kupunguza mwangaza zaidi kwa kupunguza kabisa kiwango cha mwangaza katika sehemu ya juu ya skrini yako.\n\nMipangilio hii hufanya kazi vyema zaidi ukiwa katika mazingira yenye giza."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ondoa njia ya mkato ya kipunguza mwangaza zaidi"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Njia ya mkato ya kipunguza mwangaza zaidi imeondolewa. Tumia upau wa kawaida wa mwangaza ili upunguze mwangaza wako."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 6cef518..7acd1e8 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"இந்த ஸ்கிரீன்ஷாட்டை <xliff:g id="APPNAME">%1$s</xliff:g> மற்றும் திறந்திருக்கும் பிற ஆப்ஸ் கண்டறிந்துள்ளன."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"குறிப்பில் சேர்"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"இணைப்பைச் சேர்"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"ஸ்கிரீன் ரெக்கார்டர்"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"ஸ்க்ரீன் ரெக்கார்டிங் செயலாக்கப்படுகிறது"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ரெக்கார்டு செய்யத் தொடங்கவா?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"நீங்கள் ரெக்கார்டு செய்யும்போது உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ஓர் ஆப்ஸை நீங்கள் ரெக்கார்டு செய்யும்போது அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ரெக்கார்டு செய்யத் தொடங்கு"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"உங்கள் திரையை ரெக்கார்டு செய்யவா?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ஓர் ஆப்ஸை ரெக்கார்டு செய்தல்"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"முழுத் திரையை ரெக்கார்டு செய்தல்"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"முழுத் திரையை நீங்கள் ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ஓர் ஆப்ஸை ரெக்கார்டு செய்யும்போது அதில் காட்டப்படும் அல்லது பிளே செய்யப்படும் அனைத்தும் ரெக்கார்டு செய்யப்படும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"திரையை ரெக்கார்டு செய்"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ரெக்கார்டு செய்ய ஆப்ஸைத் தேர்வுசெய்தல்"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ஆடியோவை ரெக்கார்டு செய்"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"சாதன ஆடியோ"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"இசை, அழைப்புகள், ரிங்டோன்கள் போன்ற உங்கள் சாதனத்திலிருந்து வரும் ஒலி"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"அனுப்பு"</string>
     <string name="cancel" msgid="1089011503403416730">"ரத்துசெய்"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ஆப்ஸ் லோகோ"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"உறுதிப்படுத்துக"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"மீண்டும் முயல்க"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"பயோமெட்ரிக் அடையாளத்தை ரத்துசெய்ய தட்டவும்"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ஸ்கிரீன் சேவர்"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ஈதர்நெட்"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"தொந்தரவு செய்ய வேண்டாம்"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"முன்னுரிமைப் பயன்முறைகள்"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"புளூடூத்"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"இணைக்கப்பட்ட சாதனங்கள் இல்லை"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"சாதனத்தை இணைக்க/துண்டிக்க தட்டவும்"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ஆடியோ பகிர்வு"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ஆடியோவை மாற்ற அல்லது பகிர, தட்டவும்"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"சேமிக்கப்பட்டது"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"இணைப்பு நீக்கும்"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"செயல்படுத்தும்"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"உடனடி வசன உரை"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"உடனடி வசனம்"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"அமைப்புகளைத் திற"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"பிற சாதனம்"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"மேலோட்டப் பார்வையை நிலைமாற்று"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"முன்னுரிமைப் பயன்முறைகள்"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"முடிந்தது"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"அமைப்புகள்"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"அமையுங்கள்"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"அலாரங்களைத் தவிர்த்து, பிற ஒலிகள் மற்றும் அதிர்வுகளின் தொந்தரவு இருக்காது. எனினும், நீங்கள் எதையேனும் (இசை, வீடியோக்கள், கேம்ஸ் போன்றவை) ஒலிக்கும்படி தேர்ந்தெடுத்திருந்தால், அவை வழக்கம் போல் ஒலிக்கும்."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"பிரத்தியேகமாக்கு"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • மெதுவாக சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுதும் சார்ஜாகும்"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • சார்ஜாகிறது • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> இல் முழுவதும் சார்ஜாகும்"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"பூட்டுத் திரையில் விட்ஜெட்கள்"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"பூட்டுத் திரையில் <xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட் சேர்க்கப்பட்டது"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"சமூகப் பயிற்சியைத் தொடங்க இடதுபுறம் ஸ்வைப் செய்யுங்கள்"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"பிரத்தியேகமாக்குங்கள்"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"மூடுக"</string>
@@ -491,7 +494,7 @@
     <string name="icon_description_for_disabled_widget" msgid="4693151565003206943">"முடக்கப்பட்ட விட்ஜெட்டுக்கான ஆப்ஸ் ஐகான்"</string>
     <string name="icon_description_for_pending_widget" msgid="8413816401868001755">"நிறுவப்படும் விட்ஜெட்டுக்கான ஆப்ஸ் ஐகான்"</string>
     <string name="edit_widget" msgid="9030848101135393954">"விட்ஜெட்டைத் திருத்து"</string>
-    <string name="button_to_remove_widget" msgid="3948204829181214098">"அகற்றும்"</string>
+    <string name="button_to_remove_widget" msgid="3948204829181214098">"அகற்று"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"விட்ஜெட்டைச் சேர்"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"முடிந்தது"</string>
     <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"விட்ஜெட்களைச் சேர்"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது உங்கள் சாதனத்திலிருந்து பிளே செய்யப்படுகின்ற அனைத்துத் தகவல்களுக்குமான அணுகலை <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸ் கொண்டிருக்கும். கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், படங்கள், மெசேஜ்கள், நீங்கள் பிளே செய்யும் ஆடியோ போன்றவை இதிலடங்கும்."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ரெக்கார்டு செய்ய அல்லது அலைபரப்பத் தொடங்கவா?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்திலிருந்து பிளே செய்யப்படுகின்ற அனைத்துத் தகவல்களையும் இந்தச் செயல்பாட்டை வழங்கும் சேவையால் அணுக முடியும். கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், படங்கள், மெசேஜ்கள், நீங்கள் பிளே செய்யும் ஆடியோ போன்றவை இதிலடங்கும்."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"முழுத்திரையையும்"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"ஓர் ஆப்ஸ்"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ஆப்ஸைப் பகிர்தல் அல்லது ரெக்கார்டு செய்தல்"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸ் மூலம் ரெக்கார்டு செய்ய அல்லது அலைபரப்பத் தொடங்கவா?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"நீங்கள் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸால் அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"நீங்கள் ஓர் ஆப்ஸைப் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸால் அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"தொடங்கு"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ஆப்ஸைப் பகிர்தல் அல்லது ரெக்கார்டு செய்தல்"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> உடன் திரையைப் பகிரவா?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ஓர் ஆப்ஸைப் பகிர்"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"முழுத் திரையையும் பகிர்"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"உங்கள் முழுத்திரையையும் பகிரும்போது, திரையில் உள்ள அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ஓர் ஆப்ஸைப் பகிரும்போது, அதில் காட்டப்படும்/பிளே செய்யப்படும் அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"திரையைப் பகிர்"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இந்த விருப்பத்தை முடக்கியுள்ளது"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"அலைபரப்பைத் தொடங்கவா?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"நீங்கள் அலைபரப்பும்போது உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ஓர் ஆப்ஸை நீங்கள் அலைபரப்பும்போது அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"அலைபரப்பைத் தொடங்கு"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"பகிர ஆப்ஸைத் தேர்வுசெய்தல்"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"உங்கள் திரையை அலைபரப்ப வேண்டுமா?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ஓர் ஆப்ஸை அலைபரப்பு"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"முழுத்திரையையும் அலைபரப்பு"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"உங்கள் முழுத்திரையையும் அலைபரப்பும்போது திரையில் உள்ள அனைத்தையும் பார்க்க முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"ஓர் ஆப்ஸை அலைபரப்பும்போது அதில் காட்டப்படுகின்ற அல்லது அதில் பிளே செய்யப்படுகின்ற அனைத்தையும் பார்க்க முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"திரையை அலைபரப்பு"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"அலைபரப்ப ஆப்ஸைத் தேர்வுசெய்தல்"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"பகிர்தலைத் தொடங்கவா?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"நீங்கள் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"நீங்கள் ஓர் ஆப்ஸைப் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"தொடங்கு"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"அடுத்து"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"பகிர்தல் ஆப்ஸிற்கு இடையே மாறும்போது இடைநிறுத்தப்படும்"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"அதற்குப் பதிலாக இந்த ஆப்ஸைப் பகிர்"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"முந்தையதற்கு மாறு"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"சாட்டிலைட், நிலையான இணைப்பு"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"சாட்டிலைட், இணைப்பு கிடைக்கிறது"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"சாட்டிலைட் SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"அவசர அழைப்புகள் அல்லது SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"பணிக் கணக்கு"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"சில வேடிக்கையாக இருந்தாலும் கவனம் தேவை"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner, Android பயனர் இடைமுகத்தை மாற்றவும் தனிப்பயனாக்கவும் கூடுதல் வழிகளை வழங்குகிறது. இந்தப் பரிசோதனைக்குரிய அம்சங்கள் எதிர்கால வெளியீடுகளில் மாற்றப்படலாம், இடைநிறுத்தப்படலாம் அல்லது தோன்றாமல் போகலாம். கவனத்துடன் தொடரவும்."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"மாற்றுத்திறன் வசதி"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"கீபோர்டு ஷார்ட்கட்கள்"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"தேடல் முடிவுகள் இல்லை"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"பின்செல்வதற்கான சைகை"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"முகப்பிற்குச் செல்வதற்கான சைகை"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ஆக்ஷன் பட்டன்"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"முடிந்தது"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"அருமை!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"பின்செல்"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"மூன்று விரல்கள் வலது மற்றும் இடதுபுறம் நகர்வதை டச்பேட் காட்டுகிறது"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"பின்செல்லும் சைகைக்கான அனிமேஷனை சாதனத்தின் திரை காட்டுகிறது"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"பின்செல்ல, உங்கள் டச்பேடில் எங்கு வேண்டுமானாலும் இடது அல்லது வலதுபுறமாக மூன்று விரல்களால் ஸ்வைப் செய்யவும்.\n\nஇதற்கு நீங்கள் கீபோர்டு ஷார்ட்கட் செயல்பாடுகள் + Esc பட்டனையும் பயன்படுத்தலாம்."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"அருமை!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"பின்செல்வதற்கான சைகையை நிறைவுசெய்துவிட்டீர்கள்."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"முகப்பிற்குச் செல்"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"எப்போது வேண்டுமானாலும் உங்கள் முகப்புத் திரைக்குச் செல்ல, மூன்று விரல்களால் திரையின் கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்யவும்."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"அற்புதம்!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"முகப்புக்குச் செல்வதற்கான சைகையை நிறைவுசெய்துவிட்டீர்கள்."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ஆக்‌ஷன் பட்டன்"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"ஆப்ஸை அணுக உங்கள் கீபோர்டில் உள்ள ஆக்‌ஷன் பட்டனை அழுத்துங்கள்."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"வாழ்த்துகள்!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"ஆக்‌ஷன் பட்டன் சைகையை நிறைவுசெய்துவிட்டீர்கள்.\n\nAction + / உங்களுக்குக் கிடைக்கக்கூடிய எல்லா ஷார்ட்கட்களையும் காட்டும்."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"முகப்புக்குச் செல்ல, உங்கள் டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"சமீபத்திய ஆப்ஸைப் பார்க்க, டச்பேடில் மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும்"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"அனைத்து ஆப்ஸையும் பார்க்க, உங்கள் கீபோர்டில் உள்ள ஆக்ஷன் பட்டனை அழுத்தவும்"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"அர்த்தம் புரியாதபடி திருத்தப்பட்டது"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"பார்ப்பதற்கு அன்லாக் செய்யவும்"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"பின்செல்ல, உங்கள் டச்பேடைப் பயன்படுத்துங்கள்"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"மூன்று விரல்களால் இடது அல்லது வலதுபுறம் ஸ்வைப் செய்யவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"முகப்புக்குச் செல்ல, உங்கள் டச்பேடைப் பயன்படுத்துங்கள்"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"மூன்று விரல்களால் மேல்நோக்கி ஸ்வைப் செய்து பிடிக்கவும். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"அனைத்து ஆப்ஸையும் பார்க்க உங்கள் கீபோர்டைப் பயன்படுத்துங்கள்"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"எப்போது வேண்டுமானாலும் ஆக்ஷன் பட்டனை அழுத்தலாம். சைகைகள் குறித்து மேலும் அறிய தட்டவும்."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"\'மிகக் குறைவான வெளிச்சம்\' அம்சம் இப்போது ஒளிர்வுப் பட்டியின் ஒரு பகுதியாகும்"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"இப்போது உங்கள் திரையின் மேற்பகுதியில் ஒளிர்வு அளவைக் குறைப்பதன் மூலம் திரையை மிகக் குறைவான வெளிச்சத்திற்குக் கொண்டு வரலாம்.\n\nஇருட்டான சூழலில் இருக்கும்போது இது சிறப்பாகச் செயல்படும்."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"மிகக் குறைவான வெளிச்சத்திற்கான ஷார்ட்கட்டை அகற்று"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"மிகக் குறைவான வெளிச்சத்திற்கான ஷார்ட்கட் அகற்றப்பட்டது. உங்கள் ஒளிர்வைக் குறைக்க, வழக்கமான ஒளிர்வுப் பட்டியைப் பயன்படுத்துங்கள்."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index e9cb6eb..5cba2ef 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g>, ఇతర ఓపెన్ యాప్‌లు ఈ స్క్రీన్‌షాట్‌ను గుర్తించాయి."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"గమనికకు జోడించండి"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"లింక్‌ను చేర్చండి"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"స్క్రీన్ రికార్డర్"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్‌గోయింగ్ నోటిఫికేషన్"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"రికార్డింగ్‌ను ప్రారంభించాలా?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"మీరు రికార్డ్ చేసేటప్పుడు, మీ స్క్రీన్‌పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"మీరు ఏదైనా యాప్‌ను రికార్డ్ చేసేటప్పుడు, ఆ యాప్‌లో చూపబడిన లేదా ప్లే అవుతున్న దేనినైనా Android యాక్సెస్ చేయగలదు. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"రికార్డింగ్‌ను ప్రారంభించండి"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"మీ స్క్రీన్‌ను రికార్డ్ చేయాలా?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ఒక యాప్‌ను రికార్డ్ చేయండి"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"ఫుల్ స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"మీ ఫుల్ స్క్రీన్‌ను మీరు రికార్డ్ చేసేటప్పుడు, మీ స్క్రీన్‌పై కనిపించేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"మీరు యాప్‌ను రికార్డ్ చేసేటప్పుడు, సంబంధిత యాప్‌లో కనిపించేవన్నీ లేదా ప్లే అయ్యేవన్నీ రికార్డ్ అవుతాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"రికార్డ్ చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ఆడియోను రికార్డ్ చేయండి"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"పరికరం ఆడియో"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే మ్యూజిక్, కాల్స్‌, రింగ్‌టోన్‌ల వంటి ధ్వనులు"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"పంపండి"</string>
     <string name="cancel" msgid="1089011503403416730">"రద్దు చేయండి"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"యాప్ లోగో"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"నిర్ధారించండి"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"మళ్లీ ట్రై చేయండి"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ప్రామాణీకరణను రద్దు చేయడానికి నొక్కండి"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"స్క్రీన్ సేవర్"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ఈథర్‌నెట్"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"అంతరాయం కలిగించవద్దు"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ముఖ్యమైన ఫైల్స్ మోడ్స్"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"బ్లూటూత్"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"జత చేసిన పరికరాలు ఏవీ అందుబాటులో లేవు"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"పరికరాన్ని కనెక్ట్ చేయడానికి లేదా డిస్‌కనెక్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"ఆడియో షేరింగ్"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"ఆడియోను మార్చడానికి లేదా షేర్ చేయడానికి ట్యాప్ చేయండి"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"సేవ్ అయ్యింది"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"డిస్‌కనెక్ట్ చేయండి"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"యాక్టివేట్ చేయండి"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string>
     <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_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>
@@ -390,7 +395,7 @@
     <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string>
     <string name="custom" msgid="3337456985275158299">"అనుకూలంగా మార్చుకోండి"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"కస్టమ్ ట్రేస్ సెట్టింగ్‌లు"</string>
-    <string name="restore_default" msgid="5259420807486239755">"డిఫాల్ట్‌ను రీస్టోర్ చేయండి"</string>
+    <string name="restore_default" msgid="5259420807486239755">"ఆటోమేటిక్ సెట్టింగ్‌లకు రీస్టోర్ చేయండి"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string>
     <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string>
     <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్‌ను అప్‌డేట్ చేయడం సాధ్యపడలేదు"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"లైవ్ క్యాప్షన్"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"లైవ్ క్యాప్షన్"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్‌లను అన్‌బ్లాక్ చేయమంటారా?"</string>
@@ -429,10 +434,12 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"సెట్టింగ్‌లను తెరవండి"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"ఇతర పరికరం"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"స్థూలదృష్టిని టోగుల్ చేయి"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ముఖ్యమైన ఫైళ్ల మోడ్స్"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"పూర్తయింది"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"సెట్టింగ్‌లు"</string>
     <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>
     <string name="zen_mode_set_up" msgid="7457957033034460064">"సెటప్ చేయండి"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"సెట్టింగ్‌లలో మేనేజ్ చేయండి"</string>
@@ -475,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తి ఛార్జ్"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ఛార్జ్ అవుతోంది • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>లో పూర్తిగా ఛార్జ్ అవుతుంది"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"లాక్ స్క్రీన్‌లో విడ్జెట్‌లు"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్, లాక్ స్క్రీన్‌కు జోడించబడింది"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"కమ్యూనల్ ట్యుటోరియల్‌ను ప్రారంభించడానికి ఎడమ వైపునకు స్వైప్ చేయండి"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"అనుకూలంగా మార్చండి"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"విస్మరించండి"</string>
@@ -528,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్‌పై కనిపించే సమాచారం లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏదైనా మీడియాకు సంబంధించిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్‌లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్‌పై చూపబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్‌ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్‌లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ఫుల్-స్క్రీన్"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"సింగిల్ యాప్"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"యాప్‌ను షేర్ చేయండి లేదా రికార్డ్ చేయండి"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"మీరు షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్‌పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌కు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"మీరు ఏదైనా యాప్‌ను షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, ఆ యాప్‌లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌కు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ప్రారంభించండి"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"యాప్‌ను షేర్ చేయండి లేదా రికార్డ్ చేయండి"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"మీ స్క్రీన్‌ను <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌తో షేర్ చేయండి?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ఒక యాప్‌ను షేర్ చేయండి"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"మొత్తం స్క్రీన్‌ను షేర్ చేయండి"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"మీ మొత్తం స్క్రీన్‌ను మీరు షేర్ చేసేటప్పుడు, మీ స్క్రీన్‌పై ఉన్నవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌కు కనిపిస్తాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"మీరు యాప్‌ను షేర్ చేసేటప్పుడు, సంబంధిత యాప్‌లో కనిపించేవి లేదా ప్లే అయ్యేవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌కు కనిపిస్తాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"స్క్రీన్‌ను షేర్ చేయండి"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఈ ఆప్షన్‌ను డిజేబుల్ చేసింది"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ప్రసారాన్ని ప్రారంభించాలా?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"మీరు ప్రసారం చేసేటప్పుడు, మీ స్క్రీన్‌పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"మీరు ఏదైనా యాప్‌ను ప్రసారం చేసేటప్పుడు, ఆ యాప్‌లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"ప్రసారాన్ని ప్రారంభించండి"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"షేర్ చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"మీ స్క్రీన్‌ను ప్రసారం చేయాలా?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ఒక యాప్‌ను ప్రసారం చేయండి"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"మొత్తం స్క్రీన్‌ను ప్రసారం చేయండి"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"మీ స్క్రీన్‌ను మీరు ప్రసారం చేసేటప్పుడు, మీ స్క్రీన్‌పై ఉన్నవన్నీ కనిపిస్తాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"మీరు యాప్‌ను ప్రసారం చేసేటప్పుడు, సంబంధిత యాప్‌లో చూపబడేవన్నీ లేదా ప్లే అయ్యేవన్నీ కనిపిస్తాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"స్క్రీన్‌ను ప్రసారం చేయండి"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"ప్రసారం చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"షేర్ చేయడాన్ని ప్రారంభించాలా?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"మీరు షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్‌పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"మీరు ఏదైనా యాప్‌ను షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, ఆ యాప్‌లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ప్రారంభించండి"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"తర్వాత"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"మీరు యాప్‌లను మార్చినప్పుడు షేరింగ్ పాజ్ చేయబడుతుంది"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"బదులుగా ఈ యాప్‌ను షేర్ చేయండి"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"మునుపటి దానికి స్విచ్ అవ్వండి"</string>
@@ -585,11 +600,11 @@
     <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="451254750289172191">"మీ వ్యక్తిగత యాప్‌లు <xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా ఇంటర్నెట్‌కు కనెక్ట్ చేయబడ్డాయి"</string>
     <string name="quick_settings_disclosure_named_vpn" msgid="6191822916936028208">"ఈ పరికరం <xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా ఇంటర్నెట్‌కు కనెక్ట్ చేయబడింది"</string>
     <string name="monitoring_title_financed_device" msgid="3659962357973919387">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ద్వారా అందించబడింది"</string>
-    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"పరికర నిర్వహణ"</string>
+    <string name="monitoring_title_device_owned" msgid="7029691083837606324">"డివైజ్ మేనేజ్‌మెంట్"</string>
     <string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
     <string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"నెట్‌వర్క్ లాగింగ్‌"</string>
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA ప్రమాణపత్రాలు"</string>
-    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను చూడండి"</string>
+    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"పాలసీలను చూడండి"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"నియంత్రణలను చూడండి"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది.\n\nసెట్టింగ్‌లను, కార్పొరేట్ యాక్సెస్‌ను, యాప్‌లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్‌ను సంప్రదించండి."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>, ఈ పరికరంతో అనుబంధించబడిన డేటాను యాక్సెస్ చేయవచ్చు, యాప్‌లను మేనేజ్ చేయవచ్చు అలాగే ఈ పరికరాల సెట్టింగ్‌లను మార్చవచ్చు.\n\nమీకు ఏవైనా సందేహాలు ఉంటే, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ను కాంటాక్ట్ చేయండి."</string>
@@ -597,7 +612,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ఈ పరికరంలో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్‌లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేశారు."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ అడ్మిన్, నెట్‌వర్క్ లాగింగ్‌ను ఆన్ చేశారు. ఇది మీ డివైజ్‌లో ట్రాఫిక్‌ను మానిటర్ చేస్తుంది."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"మీ అడ్మిన్ నెట్‌వర్క్ లాగింగ్‌ను ఆన్ చేశారు, ఇది మీ వర్క్ ప్రొఫైల్‌లోని ట్రాఫిక్‌ను పర్యవేక్షిస్తుంది కానీ మీ వ్యక్తిగత ప్రొఫైల్‌లో కాదు."</string>
     <string name="monitoring_description_named_vpn" msgid="8220190039787149671">"ఈ పరికరం <xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా ఇంటర్నెట్‌కు కనెక్ట్ చేయబడింది. ఈమెయిళ్లు, బ్రౌజింగ్ డేటాతో సహా మీ నెట్‌వర్క్ యాక్టివిటీ VPN ప్రొవైడర్‌కు కనిపిస్తుంది."</string>
     <string name="monitoring_description_managed_device_named_vpn" msgid="7693648349547785255">"ఈ పరికరం <xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా ఇంటర్నెట్‌కు కనెక్ట్ చేయబడింది. ఈమెయిళ్లు, బ్రౌజింగ్ డేటాతో సహా మీ నెట్‌వర్క్ యాక్టివిటీ మీ IT అడ్మిన్‌కు కనిపిస్తుంది."</string>
@@ -711,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"శాటిలైట్, కనెక్షన్ బాగుంది"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"శాటిలైట్, కనెక్షన్ అందుబాటులో ఉంది"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"ఎమర్జెన్సీ శాటిలైట్ సహాయం"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"ఎమర్జెన్సీ కాల్స్ లేదా SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"ఆఫీస్ ప్రొఫైల్‌"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"కొందరికి సరదాగా ఉంటుంది కానీ అందరికీ అలాగే ఉండదు"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"సిస్టమ్ UI ట్యూనర్ Android వినియోగదారు ఇంటర్‌ఫేస్‌ను మెరుగుపరచడానికి మరియు అనుకూలంగా మార్చడానికి మీకు మరిన్ని మార్గాలను అందిస్తుంది. ఈ ప్రయోగాత్మక లక్షణాలు భవిష్యత్తు విడుదలల్లో మార్పుకు లోనవ్వచ్చు, తాత్కాలికంగా లేదా పూర్తిగా నిలిపివేయవచ్చు. జాగ్రత్తగా కొనసాగండి."</string>
@@ -1373,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"యాక్సెసిబిలిటీ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"కీబోర్డ్ షార్ట్‌కట్‌లు"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్‌కట్‌లు"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"సెర్చ్ ఫలితాలు ఏవీ లేవు"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"వెనుకకు పంపే సంజ్ఞ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"హోమ్‌కు పంపే సంజ్ఞ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"యాక్షన్ కీ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"విజయవంతమైంది!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"మూడు వేళ్లు కుడి, ఎడమకు కదులుతున్నట్లు చూపే టచ్‌ప్యాడ్"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"వెనుక సంజ్ఞ కోసం యానిమేషన్‌ను చూపుతున్న పరికర స్క్రీన్"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"వెనుకకు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో ఎక్కడైనా మూడు వేళ్లను ఉపయోగించి ఎడమ లేదా కుడి వైపునకు స్వైప్ చేయండి.\n\nమీరు దీని కోసం + ESC కీబోర్డ్ షార్ట్‌కట్ యాక్షన్‌ను కూడా ఉపయోగించవచ్చు."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"చక్కగా పూర్తి చేశారు!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"తిరిగి వెనుకకు వెళ్ళడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"మొదటి ట్యాబ్‌కు వెళ్లండి"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ఏ సమయంలోనైనా మీ మొదటి స్క్రీన్‌కు వెళ్లడానికి, మీ స్క్రీన్ కింది నుండి మూడు వేళ్లతో పైకి స్వైప్ చేయండి."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"సూపర్!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"మొదటి స్క్రీన్‌కు వెళ్ళడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్‌ను మీరు పూర్తి చేశారు."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"యాక్షన్ కీ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"మీ యాప్‌లను యాక్సెస్ చేయడానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"అభినందనలు!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"మీరు \'యాక్షన్ కీ\' సంజ్ఞను పూర్తి చేశారు.\n\nAction + / నొక్కితే, మీకు అందుబాటులో ఉండే షార్ట్‌కట్‌లన్నీ కనిపిస్తాయి."</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>
@@ -1397,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"హోమ్‌కు వెళ్లడానికి, టచ్‌ప్యాడ్‌లో మీ మూడు వెళ్లతో పైకి స్వైప్ చేయండి"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"ఇటీవలి యాప్‌లను చూడటానికి, టచ్‌ప్యాడ్‌లో మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"మీ యాప్‌లన్నింటినీ చూడటానికి, మీ కీబోర్డ్‌లో యాక్షన్ కీని నొక్కండి"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"దాచిపెట్టినది"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"చూడటానికి అన్‌లాక్ చేయండి"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"వెనుకకు వెళ్లడానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"మూడు వేళ్లతో ఎడమ / కుడి వైపునకు స్వైప్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"హోమ్‌కు వెళ్లడానికి మీ టచ్‌ప్యాడ్‌ను ఉపయోగించండి"</string>
@@ -1405,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"మూడు వేళ్లతో పైకి స్వైప్ చేసి, హోల్డ్ చేయండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"యాప్‌లన్నింటినీ చూడటానికి మీ కీబోర్డ్‌ను ఉపయోగించండి"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"ఏ సమయంలోనైనా యాక్షన్ కీని నొక్కండి. మరిన్ని సంజ్ఞల గురించి తెలుసుకోవడానికి ట్యాప్ చేయండి."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"కాంతిని మరింత డిమ్ చేసే ఫీచర్ ఇప్పుడు బ్రైట్‌నెస్ బార్‌లో ఒక భాగం"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"మీరు ఇప్పుడు మీ స్క్రీన్ పైభాగం నుండి బ్రైట్‌నెస్ స్థాయిని తగ్గించడం ద్వారా కూడా స్క్రీన్ కాంతిని మరింత డిమ్ చేయవచ్చు.\n\nమీరు డార్క్ ఎన్విరాన్‌మెంట్‌లో ఉన్నప్పుడు కూడా ఇది బాగా పని చేస్తుంది."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"కాంతిని మరింత డిమ్ చేసే షార్ట్‌కట్‌ను తీసివేయండి"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"కాంతిని మరింత డిమ్ చేసే షార్ట్‌కట్ తీసివేయబడింది. మీ బ్రైట్‌నెస్‌ను తగ్గించడానికి, సాధారణ బ్రైట్‌నెస్ బార్‌ను ఉపయోగించండి."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0d131ac..905ab98 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> และแอปอื่นๆ ที่เปิดอยู่ตรวจพบภาพหน้าจอนี้"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"เพิ่มลงในโน้ต"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"รวมลิงก์"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"โปรแกรมบันทึกหน้าจอ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"เริ่มบันทึกเลยไหม"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"ขณะกำลังบันทึก Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"ขณะกำลังบันทึกแอป Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"เริ่มบันทึก"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"บันทึกหน้าจอไหม"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"บันทึกแอปเดียว"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"บันทึกทั้งหน้าจอ"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"ขณะบันทึกทั้งหน้าจอ ระบบจะบันทึกทุกสิ่งที่แสดงอยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"ขณะบันทึกแอป ระบบจะบันทึกทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"บันทึกหน้าจอ"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"เลือกแอปที่จะบันทึก"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"บันทึกเสียง"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"เสียงจากอุปกรณ์"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"เสียงจากอุปกรณ์ เช่น เพลง การโทร และเสียงเรียกเข้า"</string>
@@ -152,7 +156,7 @@
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการรวบรวมปัญหา"</string>
     <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"กำลังบันทึกปัญหา"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"แชร์"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"บันทึกไฟล์บันทึกปัญหาแล้ว"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"จัดเก็บไฟล์บันทึกปัญหาแล้ว"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"แตะเพื่อดู"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"เกิดข้อผิดพลาดในการบันทึกไฟล์บันทึกปัญหา"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"เกิดข้อผิดพลาดในการเริ่มบันทึกปัญหา"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ส่ง"</string>
     <string name="cancel" msgid="1089011503403416730">"ยกเลิก"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"โลโก้แอป"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ยืนยัน"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ลองอีกครั้ง"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"แตะเพื่อยกเลิกการตรวจสอบสิทธิ์"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"ภาพพักหน้าจอ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"โหมดสำคัญ"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"บลูทูธ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ไม่มีอุปกรณ์ที่จับคู่ที่สามารถใช้ได้"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"แตะเพื่อเชื่อมต่อหรือยกเลิกการเชื่อมต่ออุปกรณ์"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"การแชร์เสียง"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"แตะเพื่อสลับหรือแชร์เสียง"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"บันทึกแล้ว"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"คำบรรยายสด"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"คำบรรยายสด"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"เปิดการตั้งค่า"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"อุปกรณ์อื่น"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"สลับภาพรวม"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"โหมดสำคัญ"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"เสร็จสิ้น"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"การตั้งค่า"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"ตั้งค่า"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"กำหนดค่า"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จอย่างช้าๆ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • กำลังชาร์จ • จะเต็มในอีก <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"วิดเจ็ตในหน้าจอล็อก"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"เพิ่มวิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ในหน้าจอล็อกแล้ว"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"ปัดไปทางซ้ายเพื่อเริ่มบทแนะนำส่วนกลาง"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"ปรับแต่ง"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"ปิด"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมีสิทธิ์เข้าถึงข้อมูลทั้งหมดที่ปรากฏบนหน้าจอหรือเปิดจากอุปกรณ์ของคุณขณะบันทึกหรือแคสต์ ซึ่งรวมถึงข้อมูลอย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน รูปภาพ ข้อความ และเสียงที่คุณเล่น"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"เริ่มบันทึกหรือแคสต์เลยไหม"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"บริการที่มีฟังก์ชันนี้จะมีสิทธิ์เข้าถึงข้อมูลทั้งหมดที่ปรากฏบนหน้าจอหรือเปิดจากอุปกรณ์ของคุณขณะบันทึกหรือแคสต์ ซึ่งรวมถึงข้อมูลอย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน รูปภาพ ข้อความ และเสียงที่คุณเล่น"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"ทั้งหน้าจอ"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"แอปเดียว"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"แชร์หรือบันทึกแอป"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"เริ่มบันทึกหรือแคสต์ด้วย <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> เลยไหม"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"เมื่อกำลังแชร์ บันทึก หรือแคสต์ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"เมื่อกำลังแชร์ บันทึก หรือแคสต์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"เริ่ม"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"แชร์หรือบันทึกแอป"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"แชร์หน้าจอกับ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ไหม"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"แชร์แอปเดียว"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"แชร์ทั้งหน้าจอ"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"เมื่อกำลังแชร์ทั้งหน้าจอ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่อยู่บนหน้าจอ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"เมื่อกำลังแชร์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"แชร์หน้าจอ"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ปิดใช้ตัวเลือกนี้"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"เริ่มแคสต์เลยไหม"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"เมื่อกำลังแคสต์ Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"เมื่อกำลังแคสต์แอป Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"เริ่มแคสต์"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"เลือกแอปที่จะแชร์"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"แคสต์หน้าจอของคุณไหม"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"แคสต์แอปเดียว"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"แคสต์ทั้งหน้าจอ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"เมื่อกำลังแคสต์ทั้งหน้าจอ ทุกสิ่งที่อยู่บนหน้าจอจะมองเห็นได้ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"เมื่อกำลังแคสต์แอป ทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าวจะมองเห็นได้ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"แคสต์หน้าจอ"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"เลือกแอปที่จะแคสต์"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"เริ่มแชร์เลยไหม"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"เมื่อกำลังแชร์ บันทึก หรือแคสต์ Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"เมื่อกำลังแชร์ บันทึก หรือแคสต์แอป Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"เริ่ม"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"ถัดไป"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"การแชร์จะหยุดชั่วคราวเมื่อสลับแอป"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"แชร์แอปนี้แทน"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"เปลี่ยนกลับ"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"ดาวเทียม, การเชื่อมต่อดี"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"ดาวเทียม, การเชื่อมต่อที่พร้อมใช้งาน"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"SOS ดาวเทียม"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"การโทรฉุกเฉินหรือ SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"โปรไฟล์งาน"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"เพลิดเพลินกับบางส่วนแต่ไม่ใช่ทั้งหมด"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"ตัวรับสัญญาณ UI ระบบช่วยให้คุณมีวิธีพิเศษในการปรับแต่งและกำหนดค่าส่วนติดต่อผู้ใช้ Android ฟีเจอร์รุ่นทดลองเหล่านี้อาจมีการเปลี่ยนแปลง ขัดข้อง หรือหายไปในเวอร์ชันอนาคต โปรดดำเนินการด้วยความระมัดระวัง"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"การช่วยเหลือพิเศษ"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"แป้นพิมพ์ลัด"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"ไม่พบผลการค้นหา"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"ท่าทางสัมผัสสำหรับย้อนกลับ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ท่าทางสัมผัสสำหรับหน้าแรก"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ปุ่มดำเนินการ"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"เก่งมาก"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ทัชแพดแสดงภาพ 3 นิ้วเลื่อนไปทางขวาและซ้าย"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"หน้าจออุปกรณ์แสดงภาพเคลื่อนไหวของท่าทางสัมผัสย้อนกลับ"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"หากต้องการย้อนกลับ ให้ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาที่ใดก็ได้บนทัชแพด\n\nหรือใช้การดำเนินการแป้นพิมพ์ลัด + ESC ได้เช่นเดียวกัน"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"เก่งมาก"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"ใช้ 3 นิ้วปัดขึ้นจากด้านล่างของหน้าจอเพื่อไปที่หน้าจอหลักได้ทุกเมื่อ"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"ดีมาก"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ปุ่มดำเนินการ"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"หากต้องการเข้าถึงแอป ให้กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"ยินดีด้วย"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"คุณทำท่าทางสัมผัสสำหรับปุ่มดำเนินการเสร็จแล้ว\n\nการดำเนินการ + / จะแสดงแป้นพิมพ์ลัดทั้งหมดที่คุณมี"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"หากต้องการไปที่หน้าแรก ให้ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"หากต้องการดูแอปล่าสุด ให้ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"หากต้องการดูแอปทั้งหมด ให้กดปุ่มดำเนินการบนแป้นพิมพ์"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"ปกปิดไว้"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"ปลดล็อกเพื่อดู"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"ใช้ทัชแพดเพื่อย้อนกลับ"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวา แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ใช้ทัชแพดเพื่อไปยังหน้าแรก"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"ใช้แป้นพิมพ์เพื่อดูแอปทั้งหมด"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"กดปุ่มดำเนินการได้ทุกเมื่อ แตะเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับท่าทางสัมผัสต่างๆ"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"ตอนนี้การหรี่แสงเพิ่มเติมเป็นส่วนหนึ่งของแถบความสว่างแล้ว"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"ตอนนี้คุณสามารถหรี่แสงหน้าจอเพิ่มเติมได้โดยลดระดับความสว่างจากด้านบนของหน้าจอมากขึ้น\n\nฟีเจอร์นี้จะทำงานได้ดีเมื่อคุณอยู่ในที่มืด"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"นำทางลัดหรี่แสงเพิ่มเติมออก"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"นำทางลัดหรี่แสงเพิ่มเติมออกแล้ว หากต้องการลดความสว่าง ให้ใช้แถบความสว่างปกติ"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f836aca..5e9d720 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"Na-detect ng <xliff:g id="APPNAME">%1$s</xliff:g> at ng iba pang bukas na app ang screenshot na ito."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Idagdag sa tala"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Isama ang link"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Recorder ng Screen"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Pinoproseso screen recording"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Kasalukuyang notification para sa session ng pag-record ng screen"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Simulan ang Pag-record?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Habang nagre-record ka, may access ang Android sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Habang nagre-record ka ng app, may access ang Android sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Simulang mag-record"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"I-record ang iyong screen?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Mag-record ng isang app"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"I-record ang buong screen"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Kapag nire-record mo ang iyong buong screen, nire-record ang anumang ipinapakita sa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Kapag nagre-record ka ng app, nire-record ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"I-record ang screen"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Pumili ng app na ire-record"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Mag-record ng audio"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio ng device"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Tunog mula sa iyong device, gaya ng musika, mga tawag, at ringtone"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Ipadala"</string>
     <string name="cancel" msgid="1089011503403416730">"Kanselahin"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Logo ng app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Kumpirmahin"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Subukang muli"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"I-tap para kanselahin ang pag-authenticate"</string>
@@ -290,7 +292,7 @@
     <string name="start_dreams" msgid="9131802557946276718">"Screen saver"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Huwag Istorbohin"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Mga mode ng priyoridad"</string>
+    <string name="quick_settings_modes_label" msgid="879156359479504244">"Mga Mode"</string>
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Walang available na mga magkapares na device"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Mag-tap para magkonekta o magdiskonekta ng device"</string>
@@ -299,6 +301,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Pag-share ng Audio"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"I-tap para lumipat o magbahagi ng audio"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string>
@@ -307,6 +310,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string>
     <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_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>
@@ -400,7 +404,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Instant Caption"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Instant Caption"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
@@ -429,17 +433,15 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Buksan ang Mga Setting"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Iba pang device"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"I-toggle ang Overview"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Mga priyoridad na mode"</string>
+    <string name="zen_modes_dialog_title" msgid="8854640808100096934">"Mga Mode"</string>
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tapos na"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Mga Setting"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"I-set up"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban sa mga alarm. Maririnig mo pa rin ang anumang pipiliin mong i-play kabilang ang mga musika, video, at laro."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"I-customize"</string>
@@ -478,8 +480,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mabagal na nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nagcha-charge • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> na lang para mapuno"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Mga widget sa lock screen"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Naidagdag sa lock screen ang widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Mag-swipe pakaliwa para simulan ang communal na tutorial"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"I-customize"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"I-dismiss"</string>
@@ -531,22 +532,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Magkakaroon ng access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa lahat ng impormasyong nakikita sa iyong screen o pine-play mula sa device mo habang nagre-record o nagka-cast. Kasama rito ang impormasyong tulad ng mga password, detalye ng pagbabayad, larawan, mensahe, at audio na pine-play mo."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Magsimulang mag-record o mag-cast?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Ang serbisyong nagbibigay ng function na ito ay magkakaroon ng access sa lahat ng impormasyong nakikita sa iyong screen o pine-play mula sa device mo habang nagre-record o nagka-cast. Kasama rito ang impormasyong tulad ng mga password, detalye ng pagbabayad, larawan, mensahe, at audio na pine-play mo."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Buong screen"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Isang app"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"I-share o i-record ang isang app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Simulang mag-record o mag-cast gamit ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kapag nagbabahagi, nagre-record, o nagka-cast ka, may access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kapag nagbabahagi, nagre-record, o nagka-cast ka ng app, may access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Simulan"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Magbahagi o mag-record ng app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ibahagi ang iyong screen sa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Mag-share ng isang app"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"I-share ang buong screen"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Kapag ibinahagi mo ang iyong buong screen, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong nasa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kapag nagbabahagi ka ng app, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ibahagi ang screen"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Na-disable ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang opsyong ito"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Simulan ang pag-cast?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kapag nagka-cast ka, may access ang Android sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kapag nagka-cast ka ng app, may access ang Android sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Simulan ang pag-cast"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pumili ng app na ibabahagi"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"I-cast ang iyong screen?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Mag-cast ng isang app"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"I-cast ang buong screen"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Kapag na-cast mo ang iyong buong screen, makikita ang anumang nasa screen mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Kapag nagka-cast ka ng app, makikita ang anumang ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"I-cast ang screen"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Pumili ng app na ika-cast"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Simulan ang pagbabahagi?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Kapag nagbabahagi, nagre-record, o nagka-cast ka, may access ang Android sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Kapag nagbabahagi, nagre-record, o nagka-cast ka ng app, may access ang Android sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Simulan"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Susunod"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Magpo-pause ang pagbabahagi kapag lumipat ka ng app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Ibahagi na lang ang app na ito"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Bumalik sa dati"</string>
@@ -625,7 +635,7 @@
     <string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Mga Setting"</string>
     <string name="volume_panel_captioning_title" msgid="5984936949147684357">"Instant Caption"</string>
     <string name="csd_lowered_title" product="default" msgid="2464112924151691129">"Ibinaba sa mas ligtas na level ang volume"</string>
-    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Naging malakas ang volume ng headphones nang mas matagal sa inirerekomenda"</string>
+    <string name="csd_system_lowered_text" product="default" msgid="1250251883692996888">"Malakas ang volume ng headphones nang mas matagal sa inirerekomenda"</string>
     <string name="csd_500_system_lowered_text" product="default" msgid="7414943302186884124">"Lampas na sa ligtas na limitasyon para sa linggong ito ang volume ng headphone"</string>
     <string name="csd_button_keep_listening" product="default" msgid="4093794049149286784">"Magpatuloy sa pakikinig"</string>
     <string name="csd_button_lower_volume" product="default" msgid="5347210412376264579">"Hinaan"</string>
@@ -714,6 +724,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Satellite, malakas ang koneksyon"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Satellite, may koneksyon"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Satellite SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Mga emergency na tawag o SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Profile sa trabaho"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Masaya para sa ilan ngunit hindi para sa lahat"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Nagbibigay sa iyo ang Tuner ng System UI ng mga karagdagang paraan upang baguhin at i-customize ang user interface ng Android. Ang mga pang-eksperimentong feature na ito ay maaaring magbago, masira o mawala sa mga pagpapalabas sa hinaharap. Magpatuloy nang may pag-iingat."</string>
@@ -1376,21 +1387,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Accessibility"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Mga keyboard shortcut"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Walang resulta ng paghahanap"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Galaw para bumalik"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Galaw para sa Home"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Action key"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Magaling!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Touchpad na nagpapakita ng tatlong daliring gumagalaw pakanan at pakaliwa"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Screen ng device na nagpapakita ng animation para sa galaw sa pagbalik"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Para bumalik, mag-swipe pakaliwa o pakanan gamit ang tatlong daliri saanman sa touchpad.\n\nPuwede mo ring gamitin ang keyboard shortcut na Action + ESC para dito."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Para pumunta sa iyong home screen anumang oras, mag-swipe pataas gamit ang tatlong daliri mula sa ibaba ng screen. mo."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Magaling!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Nakumpleto mo na ang galaw para pumunta sa home."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Action key"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Para ma-access ang iyong mga app, pindutin ang action key sa keyboard mo."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Binabati kita!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Nakumpleto mo na ang galaw ng action key.\n\nIpinapakita ng Action + / ang lahat ng shortcut na available sa iyo."</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>
@@ -1400,6 +1428,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Para pumunta sa home, mag-swipe pataas gamit ang tatlong daliri sa touchpad"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Para tingnan ang kamakailang app, mag-swipe pataas at i-hold gamit ang tatlong daliri sa touchpad"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Para tingnan ang lahat ng iyong app, pindutin ang action key sa keyboard mo"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Na-redact"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"I-unlock para tingnan"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Gamitin ang iyong touchpad para bumalik"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Mag-swipe pakaliwa o pakanan gamit ang tatlong daliri. I-tap para matuto pa tungkol sa mga galaw."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Gamitin ang touchpad mo para pumunta sa home"</string>
@@ -1408,4 +1438,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Mag-swipe pataas at i-hold gamit ang tatlong daliri. I-tap para matuto pa tungkol sa mga galaw."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Gamitin ang iyong keyboard para tingnan ang lahat ng app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Pindutin ang action key kahit kailan. I-tap para matuto pa tungkol sa mga galaw."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Bahagi na ng brightness bar ang extra dim"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Puwede mo nang gawing extra dim ang screen sa pamamagitan ng pagpapababa ng level ng liwanag nang higit pa mula sa itaas ng iyong screen.\n\nPinakamahusay itong gumagana kapag nasa madilim na kapaligiran ka."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Alisin ang shortcut ng extra dim"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Inalis ang shortcut ng extra dim. Para bawasan ang liwanag, gamitin ang karaniwang bar ng liwanag."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index b30df3d..db49402 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> ve diğer açık uygulamalar bu ekran görüntüsünü algıladı."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Nota ekle"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Bağlantıyı dahil et"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekran Kaydedicisi"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran kaydı işleniyor"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Kayıt başlatılsın mı?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Kayıt özelliğini kullandığınızda Android, ekranınızda gösterilen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Bir uygulamayı kaydettiğinizde Android, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Kaydı başlat"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekranınız kaydedilsin mi?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bir uygulamayı kaydet"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Tüm ekranı kaydedin"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Tüm ekranınızı kaydettiğinizde ekranınızda gösterilen her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Bir uygulamayı kaydettiğinizde o uygulamada gösterilen veya oynatılan her şey kaydedilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranı kaydet"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Kaydedilecek uygulamayı seçin"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Ses kaydet"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Cihaz sesi"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Müzik, aramalar, zil sesleri gibi cihazınızdan sesler"</string>
@@ -152,7 +156,7 @@
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Sorun toplama oturumuyla ilgili devam eden görev bildirimi"</string>
     <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Kayıt sorunu"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Paylaş"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"Sorun kaydı saklandı"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"Sorun kaydı kaydedildi"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Görüntülemek için dokunun"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"Sorun kaydı saklanırken hata oluştu"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"Sorun kaydı başlatılırken hata oluştu"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gönder"</string>
     <string name="cancel" msgid="1089011503403416730">"İptal"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Uygulama logosu"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Onayla"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Tekrar dene"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Kimlik doğrulama işlemini iptal etmek için dokunun"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran koruyucu"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Rahatsız Etmeyin"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Öncelik modları"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Kullanılabilir eşlenmiş cihaz yok"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Cihaz bağlamak veya cihazın bağlantısını kesmek için dokunun"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ses Paylaşımı"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Geçiş yapmak veya ses paylaşmak için dokunun"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Canlı Altyazı"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Ayarlar\'ı aç"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Diğer cihaz"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Genel bakışı aç/kapat"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Öncelik modları"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Bitti"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Ayarlar"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Ayarla"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Alarmlar 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>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Özelleştir"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Yavaş şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Şarj oluyor • Dolmasına <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> kaldı"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Kilit ekranındaki widget\'lar"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı kilit ekranına eklendi"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Ortak eğitimi başlatmak için sola kaydırın"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Özelleştir"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Kapat"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Bu alanda widget\'larınızı ekleyin, kaldırın ve yeniden sıralayın"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Burada widget\'larınızı ekleyin, kaldırın ve düzenleyin"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Daha fazla widget ekle"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Widget\'ları özelleştirmek için uzun basın"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Widget\'ları özelleştir"</string>
@@ -494,7 +497,7 @@
     <string name="button_to_remove_widget" msgid="3948204829181214098">"Kaldır"</string>
     <string name="hub_mode_add_widget_button_text" msgid="4831464661209971729">"Widget ekle"</string>
     <string name="hub_mode_editing_exit_button_text" msgid="3704686734192264771">"Bitti"</string>
-    <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Widget ekleme"</string>
+    <string name="label_for_button_in_empty_state_cta" msgid="7314975555382055823">"Widget ekle"</string>
     <string name="title_for_empty_state_cta" msgid="6161654421223450530">"Tabletinizin kilidini açmadan favori uygulama widget\'larınıza hızlıca erişin."</string>
     <string name="dialog_title_to_allow_any_widget" msgid="1004820948962675644">"Kilit ekranında tüm widget\'lara izin verilsin mi?"</string>
     <string name="button_text_to_open_settings" msgid="1987729256950941628">"Ayarları açın"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ekranınızda görünen veya kayıt ya da yayın sırasında cihazınızdan oynatılan tüm bilgilere erişecektir. Bu bilgiler arasında şifreler, ödeme detayları, fotoğraflar, mesajlar ve çaldığınız sesler gibi bilgiler yer alır."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Kaydetme veya yayınlama başlatılsın mı?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu işlevi sağlayan hizmet, ekranınızda görünen veya kayıt ya da yayın sırasında cihazınızdan oynatılan tüm bilgilere erişecektir. Bu bilgiler arasında şifreler, ödeme detayları, fotoğraflar, mesajlar ve çaldığınız sesler gibi bilgiler yer alır."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Tüm ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Tek bir uygulama"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Uygulamayı paylaşın veya kaydedin"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ile kayıt veya yayınlama başlatılsın mı?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Paylaşma, kaydetme ve yayınlama özelliklerini kullandığınızda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ekranınızda görünen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Bir uygulamayı paylaştığınızda, kaydettiğinizde veya yayınladığınızda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Başlat"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Uygulamayı paylaşın veya kaydedin"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekranınız <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> uygulamasıyla paylaşılsın mı?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Tek bir uygulamayı paylaş"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Tüm ekranı paylaş"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, tüm ekranınızı paylaştığınızda ekranınızdaki her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, bir uygulamayı paylaştığınızda o uygulamada gösterilen veya oynatılan her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaş"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçeneği devre dışı bıraktı"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Yayın başlatılsın mı?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Yayınlama özelliğini kullandığınızda Android, ekranınızda görünen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Bir uygulamayı yayınladığınızda Android, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Yayını başlat"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşılacak uygulamayı seçin"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekranınız yayınlansın mı?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 uygulamayı yayınla"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Tüm ekranı yayınla"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Tüm ekranınızı yayınladığınızda ekranınızdaki her şey görünür. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Bir uygulamayı yayınladığınızda o uygulamada gösterilen veya oynatılan her şey görünür. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranı yayınla"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Yayınlanacak uygulamayı seçin"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Paylaşma başlatılsın mı?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Paylaşma, kaydetme veya yayınlama özelliğini kullandığınızda Android, ekranınızda gösterilen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Bir uygulamayı paylaştığınızda, kaydettiğinizde veya yayınladığınızda Android, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Başlat"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Sonraki"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Uygulama değiştirdiğinizde paylaşım duraklatılır"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Bunun yerine bu uygulamayı paylaşın"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Geri dön"</string>
@@ -612,7 +624,7 @@
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Bu cihaz ebeveyniniz tarafından yönetiliyor. Kullandığınız uygulamalar, konumunuz ve ekran başında kalma süreniz gibi bilgiler ebeveyniniz tarafından görülüp yönetilebilir."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"TrustAgent tarafından kilit açık tutuldu"</string>
-    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Cihaz kilitlendi. Çok fazla sayıda kimlik doğrulama denemesi yapıldı."</string>
+    <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"Çok fazla kimlik doğrulama denemesi yapıldığından cihaz kilitlendi."</string>
     <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"Cihaz kilitli\nKimlik doğrulama yapılamadı"</string>
     <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="accessibility_volume_settings" msgid="1458961116951564784">"Ses ayarları"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Uydu, bağlantı güçlü"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Uydu, bağlantı mevcut"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Acil Uydu Bağlantısı"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Acil durum aramaları veya acil yardım"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"İş profili"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Bazıları için eğlenceliyken diğerleri için olmayabilir"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Sistem Kullanıcı Arayüzü Ayarlayıcı, Android kullanıcı arayüzünde değişiklikler yapmanız ve arayüzü özelleştirmeniz için ekstra yollar sağlar. Bu deneysel özellikler değişebilir, bozulabilir veya gelecekteki sürümlerde yer almayabilir. Dikkatli bir şekilde devam edin."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Erişilebilirlik"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Klavye kısayolları"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Arama sonucu yok"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Geri hareketi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ana sayfa hareketi"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Eylem tuşu"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Bitti"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Tebrikler!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri dön"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sağa ve sola hareket eden üç parmağın gösterildiği dokunmatik alan"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Geri hareketi animasyonunu gösteren cihaz ekranı"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Geri dönmek için dokunmatik alanın herhangi bir yerinde üç parmağınızla sola veya sağa kaydırın.\n\nDilerseniz işlem düğmesi + Esc klavye kısayolunu kullanarak da geri dönebilirsiniz."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Tebrikler!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"İstediğiniz zaman ana ekrana gitmek için üç parmağınızla ekranınızın altından yukarı doğru kaydırın."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Güzel!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ana ekrana git hareketini tamamladınız."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Eylem tuşu"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Uygulamalarınıza erişmek için klavyenizdeki eylem tuşuna basın."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tebrikler!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Eylem tuşu hareketini tamamladınız.\n\nKullanabileceğiniz tüm kısayolları görmek için eylem + / tuşuna basın."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Ana sayfaya gitmek için dokunmatik alanda üç parmağınızla yukarı kaydırın"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Son uygulamaları görüntülemek için dokunmatik alanda üç parmağınızla yukarı kaydırıp basılı tutun"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Tüm uygulamalarınızı görüntülemek için klavyenizdeki eylem tuşuna basın"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Çıkartıldı"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Görüntülemek için kilidi açın"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Geri dönmek için dokunmatik alanınızı kullanın"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Üç parmağınızla sola veya sağa kaydırın. Daha fazla hareket öğrenmek için dokunun."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Ana sayfaya gitmek için dokunmatik alanınızı kullanın"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Üç parmağınızla yukarı kaydırıp basılı tutun. Daha fazla hareket öğrenmek için dokunun."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Tüm uygulamaları görüntülemek için klavyenizi kullanın"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"İstediğiniz zaman eylem tuşuna basın. Daha fazla hareket öğrenmek için dokunun."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ekstra loş özelliği, parlaklık çubuğuna eklendi"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Artık ekranınızın üst kısmından parlaklık seviyesini daha da düşürerek ekranı ekstra loş hale getirebilirsiniz.\n\nBu özellik, karanlık ortamdayken en iyi sonucu verir."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Ekstra loş kısayolunu kaldır"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Ekstra loş kısayolu kaldırıldı. Parlaklık seviyesini düşürmek için normal parlaklık çubuğunu kullanın."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7e72f7e..6d881df 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> та інші відкриті додатки виявили цей знімок екрана."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Додати до примітки"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Додати посилання"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Запис відео з екрана"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Обробка записування екрана"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Почати записування?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Коли ви записуєте вміст екрана, ОС Android отримує доступ до всього, що відображається на ньому або відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Коли ви записуєте додаток, ОС Android отримує доступ до всього, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Почати записування"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Записувати весь екран"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Коли ви записуєте вміст усього екрана, на відео потрапляє все, що на ньому відображається. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Коли ви записуєте додаток, на відео потрапляє все, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Записувати вміст екрана"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Виберіть додаток для запису"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Записувати звук"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Звук із пристрою"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Звук із пристрою, зокрема музика, виклики й сигнали дзвінка"</string>
@@ -127,13 +131,13 @@
     <string name="screenrecord_save_error" msgid="5862648532560118815">"Не вдалося зберегти запис відео з екрана"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не вдалося почати запис екрана"</string>
     <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"Зупинити запис?"</string>
-    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Ви зараз записуєте відео з усього екрана"</string>
-    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ви зараз записуєте відео з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"Ви зараз записуєте вміст усього екрана"</string>
+    <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"Ви зараз записуєте вміст екрана додатка <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"Зупинити запис"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"Показ екрана"</string>
     <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"Зупинити показ екрана?"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ви зараз ділитеся вмістом усього екрана з додатком <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
-    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ви зараз ділитеся вмістом усього екрана з додатком"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"Ви зараз показуєте вміст усього екрана в додатку <xliff:g id="HOST_APP_NAME">%1$s</xliff:g>"</string>
+    <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"Ви зараз показуєте вміст усього екрана в додатку."</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"Ви зараз показуєте вікно додатка <xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"Ви зараз показуєте вікно додатка"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"Зупинити показ"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Надіслати"</string>
     <string name="cancel" msgid="1089011503403416730">"Скасувати"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Логотип додатка"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Підтвердити"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Повторити спробу"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Натисніть, щоб скасувати автентифікацію"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Заставка"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Не турбувати"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Режими пріоритету"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Немає спарених пристроїв"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Натисніть, щоб під’єднати або від’єднати пристрій"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Надсилання аудіо"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Натисніть, щоб змінити режим або надіслати аудіо"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Збережено"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"від’єднати"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"активувати"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Запис екрана"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Почати"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Зупинити"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис помилки"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис проблеми"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Почати"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зупинити"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Звіт про помилку"</string>
@@ -388,7 +393,7 @@
     <string name="performance" msgid="6552785217174378320">"Продуктивність"</string>
     <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string>
     <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string>
-    <string name="custom" msgid="3337456985275158299">"Власні"</string>
+    <string name="custom" msgid="3337456985275158299">"Указати"</string>
     <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Власні налаштування трасування"</string>
     <string name="restore_default" msgid="5259420807486239755">"Відновити налаштування за умовчанням"</string>
     <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Живі субтитри"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Живі субтитри"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Відкрити налаштування"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Інший пристрій"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Увімкнути або вимкнути огляд"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Режими пріоритету"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Готово"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Налаштування"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Налаштувати"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ви отримуватиме звукові та вібросигнали лише для будильників. Однак ви чутимете все, що захочете відтворити, зокрема музику, відео й ігри."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Налаштувати"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Повільне заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Заряджання • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до повного заряду"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Віджети на заблокованому екрані"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Віджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" додано на заблокований екран"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Проведіть пальцем уліво, щоб відкрити спільний навчальний посібник"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Налаштувати"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Закрити"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додати, вилучити чи впорядкувати віджети в цьому просторі"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Додати, вилучити чи перемістити віджети"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Додати більше віджетів"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Утримуйте, щоб налаштувати віджети"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Налаштувати віджети"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"Додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> матиме доступ до всієї інформації, яка з’являється на екрані або відтворюється на пристрої під час запису чи трансляції. Це, зокрема, паролі, платіжна інформація, фотографії, повідомлення і аудіофайли."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Почати запис або трансляцію?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Сервіс, що надає цю функцію, матиме доступ до всієї інформації, яка з’являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, платіжної інформації, фотографій, повідомлень і аудіофайлів."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Увесь екран"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Окремий додаток"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Показувати або записувати додаток"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Почати запис або трансляцію за допомогою додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Коли ви показуєте, записуєте або транслюєте екран, додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримує доступ до всього, що відображається на екрані чи відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Коли ви показуєте, записуєте або транслюєте додаток, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримує доступ до всього, що відображається або відтворюється в цьому додатку. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Почати"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Показувати або записувати додаток"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Показати екран для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Показати один додаток"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Показати весь екран"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Коли ви показуєте весь екран, для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> стає видимим увесь контент на ньому. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Коли ви показуєте додаток, для додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> стає видимим увесь контент, що відображається або відтворюється в ньому. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показати екран"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> вимкнув цю опцію"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Почати трансляцію?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Під час трансляції ОС Android отримує доступ до всього, що відображається на екрані чи відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Коли ви транслюєте додаток, ОС Android отримує доступ до всього, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Почати трансляцію"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Виберіть додаток, яким хочете поділитися"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Транслювати екран?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслювати один додаток"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Транслювати весь екран"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Коли ви транслюєте весь екран, видимим стає весь контент на ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Коли ви транслюєте додаток, видимим стає весь контент, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Транслювати екран"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Виберіть додаток для трансляції"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Почати показ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Коли ви показуєте, записуєте або транслюєте екран, ОС Android отримує доступ до всього, що відображається на ньому чи відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Коли ви показуєте, записуєте або транслюєте додаток, ОС Android отримує доступ до всього, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Почати"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Далі"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Спільний доступ до додатка призупиняється, коли ви переходите в інший додаток"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Поділитися цим додатком"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Повернутися"</string>
@@ -594,7 +606,7 @@
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Сертифікати центру сертифікації"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Переглянути правила"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Переглянути засоби контролю"</string>
-    <string name="monitoring_description_named_management" msgid="505833016545056036">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\".\n\nIT-адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані пристрою та інформацію про його місцезнаходження.\n\nЩоб дізнатися більше, зв\'яжіться з IT-адміністратором."</string>
+    <string name="monitoring_description_named_management" msgid="505833016545056036">"Цей пристрій належить організації \"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>\".\n\nСистемний адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані пристрою і інформацію про його місцезнаходження.\n\nЩоб дізнатися більше, зв’яжіться з адміністратором."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"Компанія \"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>\" має доступ до даних, пов\'язаних із цим пристроєм, а також може змінювати його налаштування та керувати додатками.\n\nЯкщо у вас є запитання, зв\'яжіться з компанією \"<xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>\"."</string>
     <string name="monitoring_description_management" msgid="4308879039175729014">"Цей пристрій належить вашій організації.\n\nІТ-адміністратор може відстежувати й контролювати налаштування, корпоративний доступ, додатки, дані пристрою та інформацію про його місцезнаходження.\n\nЩоб дізнатися більше, зв\'яжіться з ІТ-адміністратором."</string>
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Адміністратор організації встановив центр сертифікації на цьому пристрої. Захищений мережевий трафік може відстежуватися або змінюватися."</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Хороше з’єднання із супутником"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Доступне з’єднання із супутником"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Супутниковий сигнал SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Екстрені виклики або сигнал SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Робочий профіль"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Це цікаво, але будьте обачні"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner пропонує нові способи налаштувати та персоналізувати інтерфейс користувача Android. Ці експериментальні функції можуть змінюватися, не працювати чи зникати в майбутніх версіях. Будьте обачні."</string>
@@ -957,7 +970,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">"Додатки з миттєвим запуском"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Доступність"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Комбінації клавіш"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Нічого не знайдено"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Жест \"Назад\""</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Жест переходу на головний екран"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Клавіша дії"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Чудово!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Сенсорна панель із зображенням трьох пальців, що рухаються вправо й уліво"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Екран пристрою, на якому показано анімацію щодо жесту \"Назад\""</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Щоб перейти назад, проведіть трьома пальцями вліво або вправо по сенсорній панелі.\n\nТакож можна скористатися комбінацією \"клавіша дії\" + ESC."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Чудово!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ви виконали жест \"Назад\"."</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Перейти на головний екран"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Щоб будь-коли перейти на головний екран, проведіть трьома пальцями вгору від нижнього краю екрана."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Чудово!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ви виконали жест переходу на головний екран."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Клавіша дії"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Щоб переглянути додатки, натисніть клавішу дії на клавіатурі."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Вітаємо!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Ви виконали жест клавіші дії.\n\nНатисніть клавішу дії + /, щоб переглянути всі доступні комбінації клавіш."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Щоб перейти на головний екран, проведіть трьома пальцями вгору по сенсорній панелі"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Щоб переглянути останні додатки, проведіть трьома пальцями вгору по сенсорній панелі й утримуйте їх"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Щоб переглянути всі додатки, натисніть клавішу дії на клавіатурі"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Замасковано"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Розблокуйте, щоб переглянути"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Щоб повернутися, використовуйте сенсорну панель"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Проведіть трьома пальцями вліво чи вправо. Натисніть, щоб дізнатися про інші жести."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Щоб перейти на головний екран, використовуйте сенсорну панель"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Проведіть трьома пальцями вгору й утримуйте їх на екрані. Натисніть, щоб дізнатися про інші жести."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Щоб переглянути всі додатки, використовуйте клавіатуру"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Будь-коли натисніть клавішу дії. Натисніть, щоб дізнатися про інші жести."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Тепер на панелі регулювання яскравості є функція додаткового зменшення яскравості"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Тепер ви можете зробити екран ще темнішим, додатково зменшуючи рівень яскравості вгорі екрана.\n\nНайкраще ця функція працює, коли ви перебуваєте в темному місці."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Видалити комбінацію клавіш для додаткового зменшення яскравості"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Комбінацію клавіш для додаткового зменшення яскравості видалено. Щоб зменшити яскравість, використовуйте стандартну панель регулювання."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 94586b7..628d660 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"‫<xliff:g id="APPNAME">%1$s</xliff:g> اور دیگر کھلی ایپس نے اس اسکرین شاٹ کا پتا لگایا۔"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"نوٹ میں شامل کریں"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"لنک شامل کریں"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"اسکرین ریکارڈر"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"سکرین ریکارڈنگ پروسیس ہورہی ہے"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"ریکارڈنگ شروع کریں؟"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"‏جب آپ ریکارڈنگ کر رہے ہوتے ہیں تو Android کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"‏جب آپ کسی ایپ کو ریکارڈ کر رہے ہوتے ہیں تو Android کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"ریکارڈنگ شروع کریں"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"آپ کی اسکرین ریکارڈ کریں؟"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ایک ایپ ریکارڈ کریں"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"پوری اسکرین کو ریکارڈ کریں"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"جب آپ اپنی پوری اسکرین کو ریکارڈ کر رہے ہوتے ہیں تو آپ کی اسکرین پر دکھائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"جب آپ کسی ایپ کو ریکارڈ کر رہے ہوتے ہیں تو اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز ریکارڈ کی جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"اسکرین ریکارڈ کریں"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"ریکارڈ کرنے کیلئے ایپ منتخب کریں"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"آڈیو ریکارڈ کریں"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"آلہ کا آڈیو"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"آپ کے آلے سے آواز، جیسے موسیقی، کالز اور رِنگ ٹونز"</string>
@@ -150,9 +154,9 @@
     <string name="issuerecord_title" msgid="286627115110121849">"ایشو ریکارڈر"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"ایشو ریکارڈنگ پروسیس ہو رہی ہے"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"ایشو کلیکشن سیشن کے لیے جاری اطلاع"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"ریکارڈنگ ایشو"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"مسئلہ ریکارڈ ہو رہا ہے"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"اشتراک کریں"</string>
-    <string name="issuerecord_save_title" msgid="4161043023696751591">"ایشو ریکارڈنگ محفوظ ہو گئی"</string>
+    <string name="issuerecord_save_title" msgid="4161043023696751591">"مسئلے کی ریکارڈنگ محفوظ ہو گئی"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"دیکھنے کیلئے تھپتھپائیں"</string>
     <string name="issuerecord_save_error" msgid="6913040083446722726">"ایشو ریکارڈنگ محفوظ کرنے میں خرابی"</string>
     <string name="issuerecord_start_error" msgid="3402782952722871190">"ایشو ریکارڈنگ شروع کرنے میں خرابی"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"بھیجیں"</string>
     <string name="cancel" msgid="1089011503403416730">"منسوخ کريں"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"ایپ لوگو"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"تصدیق کریں"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"دوبارہ کوشش کریں"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"تصدیق کو منسوخ کرنے کے لیے تھپتھپائیں"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"اسکرین سیور"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"ایتھرنیٹ"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ڈسٹرب نہ کریں"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"ترجیحی وضع"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"بلوٹوتھ"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"کوئی جوڑا بنائے ہوئے آلات دستیاب نہیں ہیں"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"کسی آلے کو منسلک یا غیر منسلک کرنے کے لیے تھپتھپائیں"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"بلوٹوتھ استعمال کریں"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"آڈیو کا اشتراک"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"آڈیو پر سوئچ کرنے یا اس کا اشتراک کرنے کے لیے تھپتھپائیں"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"لائیو کیپشن"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"لائیو کیپشن"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"ترتیبات کھولیں"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"دوسرا آلہ"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"مجموعی جائزہ ٹوگل کریں"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"ترجیحی وضع"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"ہو گیا"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"ترتیبات"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"سیٹ اپ کریں"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"الارمز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"حسب ضرورت بنائیں"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • آہستہ چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • چارج ہو رہا ہے • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> میں مکمل"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"مقفل اسکرین پر ویجیٹس"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"مقفل اسکرین میں <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ شامل کیا گیا"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"کمیونل ٹیوٹوریل شروع کرنے کے لیے بائیں سوائپ کریں"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"حسب ضرورت بنائیں"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"برخاست کریں"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو اس تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر نظر آتی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائی گئی ہے۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات، اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"اس فنکشن فراہم کرنے والی سروس کو اس تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر نظر آتی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائی گئی ہے۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"پوری اسکرین"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"واحد ایپ"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"ایپ کا اشتراک کرنا یا ریکارڈ کرنا"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کا استعمال کر کے ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"جب آپ اشتراک، ریکارڈنگ یا کاسٹ کر رہے ہوتے ہیں تو <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"جب آپ اشتراک، ریکارڈنگ یا کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"شروع کریں"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"ایپ کا اشتراک یا ریکارڈ کریں"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کے ساتھ اپنی اسکرین کا اشتراک کریں؟"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"ایک ایپ کا اشتراک کریں"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"پوری اسکرین کا اشتراک کریں"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"آپ کے اپنی پوری اسکرین کا اشتراک کرنے پر آپ کی اسکرین پر ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہوجاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"آپ کے کسی ایپ کا اشتراک کرنے پر اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"اسکرین کا اشتراک کریں"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> نے اس اختیار کو غیر فعال کر دیا ہے"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"کاسٹ کرنا شروع کریں؟"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"‏جب آپ کاسٹ کر رہے ہوتے ہیں، تو Android کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"‏جب آپ کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو Android کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"کاسٹ کرنا شروع کریں"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"اشتراک کرنے کیلئے ایپ منتخب کریں"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"اپنی اسکرین کاسٹ کریں؟"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ایک ایپ کاسٹ کریں"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"پوری اسکرین کاسٹ کریں"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"جب آپ اپنی پوری اسکرین کاسٹ کر رہے ہوتے ہیں تو آپ کی اسکرین پر ہر چیز مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"جب آپ کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"اسکرین کاسٹ کریں"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"کاسٹ کرنے کیلئے ایپ منتخب کریں"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"اشتراک کرنا شروع کریں؟"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"‏جب آپ اشتراک، ریکارڈنگ یا کاسٹ کر رہے ہوتے ہیں تو Android کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"‏جب آپ اشتراک، ریکارڈنگ یا کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو Android کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"شروع کریں"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"اگلا"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"آپ کے ایپس سوئچ کرنے پر اشتراک موقوف ہو جاتا ہے"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"اس کے بجائے اس ایپ کا اشتراک کریں"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"واپس سوئچ کریں"</string>
@@ -679,12 +691,8 @@
     <string name="volume_panel_enter_media_output_settings" msgid="8824244246272552669">"آؤٹ پٹ کی ترتیبات درج کریں"</string>
     <string name="volume_panel_expanded_sliders" msgid="1885750987768506271">"والیوم سلائیڈرز کو پھیلا دیا گیا"</string>
     <string name="volume_panel_collapsed_sliders" msgid="1413383759434791450">"والیوم سلائیڈرز سکیڑا گیا"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for volume_panel_hint_mute (2153922288568199077) -->
-    <skip />
-    <!-- String.format failed for translation -->
-    <!-- no translation found for volume_panel_hint_unmute (4831850937582282340) -->
-    <skip />
+    <string name="volume_panel_hint_mute" msgid="2153922288568199077">"‏%s خاموش کریں"</string>
+    <string name="volume_panel_hint_unmute" msgid="4831850937582282340">"‏%s غیر خاموش کریں"</string>
     <string name="volume_panel_hint_muted" msgid="1124844870181285320">"خاموش کردہ"</string>
     <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"وائبریٹ"</string>
     <string name="media_output_label_title" msgid="872824698593182505">"<xliff:g id="LABEL">%s</xliff:g> پر چل رہی ہے"</string>
@@ -718,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"سیٹلائٹ، کنکشن اچھا ہے"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"سیٹلائٹ، کنکشن دستیاب ہے"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"‏سیٹلائٹ SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"‏ایمرجنسی کالز یا SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"دفتری پروفائل"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"کچھ کیلئے دلچسپ لیکن سبھی کیلئے نہیں"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏سسٹم UI ٹیونر Android صارف انٹر فیس میں ردوبدل کرنے اور اسے حسب ضرورت بنانے کیلئے آپ کو اضافی طریقے دیتا ہے۔ یہ تجرباتی خصوصیات مستقبل کی ریلیزز میں تبدیل ہو سکتی، رک سکتی یا غائب ہو سکتی ہیں۔ احتیاط کے ساتھ آگے بڑھیں۔"</string>
@@ -1380,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ایکسیسبیلٹی"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"کی بورڈ شارٹ کٹس"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"تلاش کا کوئی نتیجہ نہیں ہے"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"پیچھے جانے کا اشارہ"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"ہوم کا اشارہ"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"ایکشن کلید"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"بہترین!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"ٹچ پیڈ دائیں اور بائیں حرکت کرتی ہوئی تین انگلیاں دکھا رہا ہے"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"آلہ کی اسکرین پیچھے جانے کے اشارے کے لیے اینیمیشن دکھا رہی ہے"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"‏واپس جانے کے لیے، ٹچ پیڈ پر کہیں بھی تین انگلیوں کی مدد سے دائیں یا بائیں سوائپ کریں۔\n\nآپ اس کیلئے کی بورڈ شارٹ کٹ ایکشن + Esc کا بھی استعمال کر سکتے ہیں۔"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"بہترین!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"آپ نے واپس جائیں اشارے کو مکمل کر لیا۔"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ہوم پر جائیں"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"کسی بھی وقت اپنی ہوم اسکرین پر جانے کے لیے، تین انگلیوں کی مدد سے اپنی اسکرین کے نیچے سے اوپر کی طرف سوائپ کریں۔"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"عمدہ!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا۔"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"ایکشن کلید"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"اپنی ایپس تک رسائی حاصل کرنے کے لیے، اپنے کی بورڈ پر ایکشن کلید کو دبائیں۔"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"مبارکباد!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"آپ نے ایکشن کلید کا اشارہ مکمل کر لیا۔\n\nایکشن + / دبانے سے آپ کے دستیاب تمام شارٹ کٹس دکھائی دیں گے۔"</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>
@@ -1404,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"ہوم پر جانے کے لیے، ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر سوائپ کریں"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"حالیہ ایپس دیکھنے کے لیے، ٹچ پیڈ پر تین انگلیوں سے اوپر سوائپ کریں اور دبائے رکھیں"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"اپنی سبھی ایپس دیکھنے کے لیے، اپنے کی بورڈ پر ایکشن کلید دبائیں"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"چھپانے کیلئے تبدیل کردہ"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"دیکھنے کے لیے غیر مقفل کریں"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"واپس جانے کے لیے اپنے ٹچ پیڈ کا استعمال کریں"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"تین انگلیوں سے دائیں یا بائیں طرف سوائپ کریں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"ہوم پر جانے کے لیے اپنے ٹچ پیڈ کا استعمال کریں"</string>
@@ -1412,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"تین انگلیوں سے اوپر کی طرف سوائپ کریں اور دبائے رکھیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"سبھی ایپس دیکھنے کے لیے اپنے کی بورڈ کا استعمال کریں"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"کسی بھی وقت ایکشن کلید دبائیں۔ مزید اشارے جاننے کے لیے تھپتھپائیں۔"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"اضافی دھندلا اب چمک بار کا حصہ ہے"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"آپ اپنی اسکرین کے اوپری حصے سے چمکیلے پن لیول کو مزید کم کر کے اپنی اسکرین کو اضافی دھندلی بنا سکتے ہیں۔\n\nجب آپ تاریک ماحول میں ہوتے ہیں تو یہ بہتر کام کرتا ہے۔"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"اضافی دھندلا شارٹ کٹ کو ہٹائیں"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"اضافی دھندلا شارٹ کٹ کو ہٹا دیا گیا۔ اپنا چمکیلا پن کم کرنے کیلئے، ریگولر چمک بار کا استعمال کریں"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 3f60776..f0988ed 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> va boshqa ochiq ilovalar skrinshot olinganini aniqladi."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Qaydga qoʻshish"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Havolani kiritish"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g>, <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Ekranni yozib olish"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran yozib olinmoqda"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Yozib olish boshlansinmi?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Yozib olish vaqtida Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Ilovani yozib olayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Yozib olish"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ekran yozib olinsinmi?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Bitta ilovani yozib olish"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Butun ekranni yozib olish"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Butun ekranni yozib olishda ekranda koʻrsatilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Ilovani yozib olishda ilova koʻrsatilgan yoki ijro etilgan barcha axborotlar yozib olinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ekranni yozib olish"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Yozib olinadigan ilovani tanlash"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio yozib olish"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Qurilmadagi audio"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Qurilmangizdagi musiqa, chaqiruvlar va ringtonlar kabi ovozlar"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Yuborish"</string>
     <string name="cancel" msgid="1089011503403416730">"Bekor qilish"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Ilova logotipi"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"OK"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Qayta urinish"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Tekshiruvni bekor qilish uchun bosing"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Ekran lavhasi"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Bezovta qilinmasin"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Muhim rejimlar"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Ulangan qurilmalar topilmadi"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Qurilma ulash yoki uzish uchun tegining"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulangan"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio ulashuvi"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Audioni almashtirish yoki ulashish uchun bosing"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string>
     <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_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>
@@ -378,7 +383,7 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ekran yozuvi"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Boshlash"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Toʻxtatish"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Nosozlikni yozib olish"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Xatoliklar hisoboti"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Jonli izoh"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Jonli izoh"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Sozlamalarni ochish"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Boshqa qurilma"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Umumiy nazar rejimini almashtirish"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Muhim rejimlar"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Tayyor"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Sozlamalar"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Sozlash"</string>
+    <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Sozlamalarda boshqarish"</string>
+    <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hech qanday rejim faol emas}=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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Sozlash"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sekin quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Quvvat olmoqda • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> qoldi"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Ekran qulfidagi vidjetlar"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Ekran qulfiga <xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjeti qoʻshildi"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Qoʻllanma bilan tanishish uchun chapga suring"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Moslash"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Yopish"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Yozib olish yoki translatsiya boshlansinmi?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Bu funksiyani taʼminlovchi xizmat ekranda chiqqan yoki yozib olish va translatsiya vaqtida ijro etilgan barcha axborotlarga ruxsat oladi. Bu axborotlar parollar, toʻlov tafsilotlari, rasmlar, xabarlar va ijro etilgan audiolardan iborat boʻlishi mumkin."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Butun ekran"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Bitta ilova"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Ilovada ulashish yoki yozib olish"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> orqali yozib olish yoki translatsiya boshlansinmi?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Ulashish, yozib olish va translatsiya qilish vaqtida <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Ulashish, yozib olish va translatsiya qilish vaqtida <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Boshlash"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Ilovani ulashish yoki yozib olish"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Ekraningiz <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bilan ulashilsinmi?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Bitta ilovani namoyish qilish"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Butun ekranni namoyish qilish"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Butun ekranni namoyish qilayotganingizda, ekrandagi barcha narsalaringiz <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Ilovani namoyish qilayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranni namoyish qilish"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu sozlamani faolsizlantirgan"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Translatsiya boshlansinmi?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Translatsiya qilayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Translatsiya qilayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Translatsiyani boshlang"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Ulashiladigan ilovani tanlash"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekraningiz uzatilsinmi?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bitta ilovani uzatish"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Butun ekranni uzatish"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Butun ekran uzatilayotganda, ekrandagi hamma narsa koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Ilovani uzatayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Ekranni translatsiya qilish"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Translatsiya qilinadigan ilovani tanlash"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Ulashuv boshlansinmi?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Ulashish, yozib olish va translatsiya qilish vaqtida Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Ilovani ulashish, yozib olish yoki translatsiya qilayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Boshlash"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Keyingisi"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Ilovalarni almashtirsangiz, ulashuv toʻxtatiladi"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Bu ilovani ulashish"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Ortga qaytarish"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Sputnik, aloqa sifati yaxshi"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Sputnik, aloqa mavjud"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Sputnik SOS"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Favqulodda chaqiruvlar yoki SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Ish profili"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Diqqat!"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner yordamida siz Android foydalanuvchi interfeysini tuzatish va o‘zingizga moslashtirishingiz mumkin. Ushbu tajribaviy funksiyalar o‘zgarishi, buzilishi yoki keyingi versiyalarda olib tashlanishi mumkin. Ehtiyot bo‘lib davom eting."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Qulayliklar"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tezkor tugmalar"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Hech narsa topilmadi"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Orqaga qaytish ishorasi"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Asosiy ekran ishorasi"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Amal tugmasi"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Barakalla!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Sensorli panelda uchta barmoq chapga va oʻngga harakatlanishi"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Qurilma ekranidagi ortga qaytish ishorasi animatsiyasi"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ortga qaytish uchun sensorli panelda uchta barmoqni chapga yoki oʻngga suring.\n\nBuning uchun Action + ESC tezkor tugmalaridan ham foydalanishingiz mumkin."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Barakalla!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Istalgan vaqtda bosh ekranga oʻtish uchun ekranning pastidan uchta barmoq bilan tepaga suring."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Yaxshi!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Bosh ekranni ochish ishorasi darsini tamomladingiz."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Amal tugmasi"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ilovalarga kirish uchun klaviaturadagi amal tugmasini bosing"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Tabriklaymiz!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Amal tugmasi ishorasi darsini tamomladingiz.\n\nAmal + / bosilsa, mavjud buyruqlar koʻrsatiladi."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Bosh ekranga qaytish uchun sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Oxirgi ilovalarni koʻrish uchun sensorli panelda uchta barmoq bilan tepaga surib, bosib turing"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Barcha ishoralarni koʻrish uchun klaviaturadagi amal tugmasini bosing"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Chiqarildi"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Koʻrish uchun qulfdan chiqaring"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Sensorli panel orqali orqaga qaytish"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Uchta barmoq bilan chapga yoki oʻngga suring. Boshqa ishoralar bilan tanishish uchun bosing."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Sensorli panel orqali bosh ekranga qaytish"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Uchta barmoq bilan tepaga surib, bosib turing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Klaviatura orqali barcha ilovalarni koʻrish"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Amal tugmasini istalganda bosing. Boshqa ishoralar bilan tanishish uchun bosing."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Juda xira endi yorqinlik panelida joylashgan"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Endi yorqinlik darajasini ekranning yuqori qismidan yanada pasaytirish orqali ekranni yanada xiralashtirishingiz mumkin.\n\nBu qorongʻi muhitda eng yaxshi ishlaydi."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Juda xira yorligʻini olib tashlash"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Juda xira yorligʻi olib tashlandi. Yorqinlikni pasaytirish uchun oddiy yorqinlik panelidan foydalaning."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index dcf8051..6ad5db4 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> và các ứng dụng đang mở khác đã phát hiện thấy ảnh chụp màn hình này."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Thêm vào ghi chú"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Thêm đường liên kết"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Trình ghi màn hình"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Đang xử lý video ghi màn hình"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Thông báo đang diễn ra về phiên ghi màn hình"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Bắt đầu ghi?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Khi bạn ghi, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Khi bạn ghi một ứng dụng, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Bắt đầu ghi"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Ghi màn hình?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ghi một ứng dụng"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Ghi toàn màn hình"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Khi bạn ghi toàn màn hình, mọi nội dung trên màn hình của bạn đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Khi bạn ghi một ứng dụng, mọi nội dung xuất hiện hoặc phát trong ứng dụng đó sẽ đều được ghi. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Ghi màn hình"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Chọn ứng dụng để ghi"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Ghi âm"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Âm thanh trên thiết bị"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Âm thanh trên thiết bị, chẳng hạn như nhạc, cuộc gọi và nhạc chuông"</string>
@@ -150,7 +154,7 @@
     <string name="issuerecord_title" msgid="286627115110121849">"Trình ghi sự cố"</string>
     <string name="issuerecord_background_processing_label" msgid="1666840264959336876">"Đang xử lý bản ghi sự cố"</string>
     <string name="issuerecord_channel_description" msgid="6142326363431474632">"Thông báo hiển thị liên tục cho một phiên thu thập sự cố"</string>
-    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Ghi sự cố"</string>
+    <string name="issuerecord_ongoing_screen_only" msgid="6248206059935015722">"Đang ghi sự cố"</string>
     <string name="issuerecord_share_label" msgid="3992657993619876199">"Chia sẻ"</string>
     <string name="issuerecord_save_title" msgid="4161043023696751591">"Đã lưu bản ghi sự cố"</string>
     <string name="issuerecord_save_text" msgid="1205985304551521495">"Nhấn để xem"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Gửi"</string>
     <string name="cancel" msgid="1089011503403416730">"Hủy"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Biểu trưng của ứng dụng"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Xác nhận"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Thử lại"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Nhấn để hủy quá trình xác thực"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Trình bảo vệ m.hình"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Không làm phiền"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Chế độ ưu tiên"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Không có thiết bị nào được ghép nối"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Nhấn để kết nối/ngắt kết nối với một thiết bị"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Chia sẻ âm thanh"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Nhấn để chuyển hoặc chia sẻ âm thanh"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string>
     <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_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>
@@ -378,12 +383,12 @@
     <string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Ghi màn hình"</string>
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Bắt đầu"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Dừng"</string>
-    <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string>
+    <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi sự cố"</string>
     <string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string>
     <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string>
     <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Báo cáo lỗi"</string>
-    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string>
-    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string>
+    <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại sự cố gì khi dùng thiết bị?"</string>
+    <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại sự cố"</string>
     <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string>
     <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string>
     <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Phụ đề trực tiếp"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Phụ đề trực tiếp"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn camera của thiết bị?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Mở phần Cài đặt"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Thiết bị khác"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Bật/tắt chế độ xem Tổng quan"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Chế độ ưu tiên"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Xong"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Cài đặt"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Thiết lập"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"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. 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>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Tùy chỉnh"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc chậm • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Đang sạc • Sẽ đầy sau <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Các tiện ích trên màn hình khoá"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Đã thêm tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g> vào màn hình khoá"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Vuốt sang trái để bắt đầu xem hướng dẫn chung"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Tuỳ chỉnh"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Đóng"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Thêm, xoá và sắp xếp lại các tiện ích trong không gian này."</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"Thêm, xoá và sắp xếp tiện ích trong không gian này."</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"Thêm tiện ích khác"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"Nhấn và giữ để tuỳ chỉnh tiện ích"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"Tuỳ chỉnh tiện ích"</string>
@@ -507,7 +510,7 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"xoá tiện ích"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"đặt tiện ích đã chọn vào vị trí"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"Tiện ích trên màn hình khoá"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"Ai cũng thấy được các tiện ích trên màn hình khoá, dù bạn đã khoá máy tính bảng."</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"Ai cũng thấy được tiện ích trên màn hình khoá, kể cả khi khoá máy tính bảng."</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"bỏ chọn tiện ích"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"Tiện ích trên màn hình khoá"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"Để dùng tiện ích mở một ứng dụng, bạn cần xác minh danh tính của mình. Ngoài ra, hãy lưu ý rằng bất kỳ ai cũng có thể xem các tiện ích này, ngay cả khi máy tính bảng của bạn được khoá. Một số tiện ích có thể không dành cho màn hình khoá và không an toàn khi thêm vào đây."</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào tất cả thông tin xuất hiện trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi âm/ghi hình hoặc truyền, bao gồm cả thông tin như mật khẩu, thông tin thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Bắt đầu ghi hoặc truyền?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Dịch vụ cung cấp chức năng này có quyền truy cập vào tất cả thông tin xuất hiện trên màn hình của bạn hoặc phát trên thiết bị của bạn trong khi ghi hoặc truyền, bao gồm cả thông tin như mật khẩu, thông tin thanh toán, ảnh, tin nhắn và âm thanh mà bạn phát."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Toàn màn hình"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Một ứng dụng"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Chia sẻ hoặc ghi màn hình ứng dụng"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Bắt đầu ghi hoặc truyền bằng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Khi bạn chia sẻ, ghi hoặc truyền, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Khi bạn chia sẻ, ghi hoặc truyền ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ các thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Bắt đầu"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Chia sẻ hoặc ghi một ứng dụng"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Chia sẻ màn hình của bạn với <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Chia sẻ một ứng dụng"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Chia sẻ toàn bộ màn hình"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Khi bạn chia sẻ toàn bộ màn hình, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung trên màn hình của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Khi bạn chia sẻ một ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung hiển thị hoặc phát trong ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Chia sẻ màn hình"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã tắt lựa chọn này"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Bắt đầu truyền?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Khi bạn truyền, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Khi bạn truyền một ứng dụng, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ các thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Bắt đầu truyền"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Chọn ứng dụng để chia sẻ"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Truyền màn hình?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Truyền một ứng dụng"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Truyền toàn bộ màn hình"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Khi bạn truyền toàn bộ màn hình thì người khác sẽ thấy được mọi nội dung trên màn hình của bạn. Vì vậy, hãy thận trọng đối với những thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Khi bạn truyền một ứng dụng, thì người khác sẽ thấy được mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng đối với những thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Màn hình truyền"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Chọn ứng dụng để truyền"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Bắt đầu chia sẻ?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Khi bạn chia sẻ, ghi hoặc truyền, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Khi bạn chia sẻ, ghi hoặc truyền ứng dụng, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ các thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Bắt đầu"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Tiếp theo"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Quá trình chia sẻ sẽ tạm dừng khi bạn chuyển đổi ứng dụng"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Chia sẻ ứng dụng này"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Chuyển trở lại"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Kết nối vệ tinh tốt"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Hiện có kết nối vệ tinh"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Liên lạc khẩn cấp qua vệ tinh"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Cuộc gọi khẩn cấp hoặc SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Hồ sơ công việc"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Thú vị đối với một số người nhưng không phải tất cả"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Bộ điều hướng giao diện người dùng hệ thống cung cấp thêm cho bạn những cách chỉnh sửa và tùy chỉnh giao diện người dùng Android. Những tính năng thử nghiệm này có thể thay đổi, hỏng hoặc biến mất trong các phiên bản tương lai. Hãy thận trọng khi tiếp tục."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Hỗ trợ tiếp cận"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Phím tắt"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Không có kết quả tìm kiếm nào"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Cử chỉ quay lại"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Cử chỉ chuyển đến màn hình chính"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Phím hành động"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Tuyệt vời!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Minh hoạ thao tác di chuyển sang phải và sang trái bằng 3 ngón tay trên bàn di chuột"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Màn hình thiết bị hiện ảnh động minh hoạ cử chỉ quay lại"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Để quay lại, hãy dùng 3 ngón tay vuốt sang trái hoặc sang phải ở vị trí bất kỳ trên bàn di chuột.\n\nBạn cũng có thể dùng phím tắt Hành động + ESC cho thao tác này."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Để chuyển đến màn hình chính bất cứ lúc nào, hãy dùng 3 ngón tay vuốt lên từ cuối màn hình lên."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Tốt lắm!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Bạn đã thực hiện xong cử chỉ chuyển đến màn hình chính."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Phím hành động"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Để truy cập vào các ứng dụng của bạn, hãy nhấn phím hành động trên bàn phím."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Xin chúc mừng!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Bạn đã thực hiện xong cử chỉ nhấn phím hành động.\n\nThao tác + / sẽ hiển thị tất cả phím tắt bạn hiện có."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Để trở về màn hình chính, hãy dùng 3 ngón tay vuốt lên trên bàn di chuột"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Để xem các ứng dụng gần đây, hãy dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Để xem tất cả ứng dụng của bạn, hãy nhấn phím hành động trên bàn phím"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Bị loại bỏ"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Mở khoá để xem"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Dùng bàn di chuột để quay lại"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Dùng 3 ngón tay vuốt sang trái hoặc sang phải. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Dùng bàn di chuột để chuyển đến màn hình chính"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Dùng 3 ngón tay vuốt lên và giữ. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sử dụng bàn phím để xem tất cả ứng dụng"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Nhấn phím hành động bất cứ lúc nào. Hãy nhấn để tìm hiểu các cử chỉ khác."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Chế độ siêu tối hiện đã có trên thanh độ sáng"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Giờ đây, bạn có thể đặt màn hình ở chế độ siêu tối bằng cách giảm thêm độ sáng từ đầu màn hình.\n\nChế độ này hoạt động hiệu quả nhất khi bạn ở trong một môi trường tối."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Xoá lối tắt của chế độ siêu tối"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Đã xoá lối tắt của chế độ siêu tối. Để giảm độ sáng, hãy dùng thanh độ sáng như thông thường."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 6162da8..35f8851 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 及其他打开的应用检测到此屏幕截图。"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"添加到备注中"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"包括链接"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"屏幕录制器"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"要开始录制吗?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"在录制内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"在录制某个应用时,Android 可以访问此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"开始录制"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"录制整个屏幕"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"录制整个屏幕时,屏幕上显示的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"录制单个应用时,该应用中显示或播放的所有内容均会被录制。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"录制屏幕"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"选择要录制的应用"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"录制音频"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"设备音频"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"设备发出的声音,例如音乐、通话和铃声"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"发送"</string>
     <string name="cancel" msgid="1089011503403416730">"取消"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"应用徽标"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"确认"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"重试"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"点按即可取消身份验证"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"屏保"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"有线网络"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"勿扰"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"优先模式"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"蓝牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"没有可用的配对设备"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"点按即可连接设备或断开设备连接"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"启用蓝牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音频分享"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"点按即可切换或分享音频"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已保存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"断开连接"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"启用"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"实时字幕"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"实时字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"打开“设置”"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他设备"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切换概览"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"优先模式"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"设置"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"设置"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"您将不会受到声音和振动的打扰(闹钟提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自定义"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在慢速充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 正在充电 • 将于 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>后充满"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"锁定屏幕上的微件"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件已添加至锁定屏幕"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑动即可启动公共教程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自定义"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"关闭"</string>
@@ -507,9 +510,9 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除微件"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所选微件"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"锁屏微件"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可以查看锁屏上的微件,即使平板电脑已锁定。"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可以查看锁屏上的微件,平板电脑处于锁定状态时也是如此。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消选中微件"</string>
-    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁定的屏幕中的微件"</string>
+    <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"锁屏微件"</string>
     <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"若要使用微件打开应用,您需要验证是您本人在操作。另外请注意,任何人都可以查看此类微件,即使您的平板电脑已锁定。有些微件可能不适合显示在锁定的屏幕中,因此添加到这里可能不安全。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切换用户"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"在录制或投放内容时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>将可访问屏幕上显示或设备中播放的所有信息,其中包括密码、付款信息、照片、消息及播放的音频等信息。"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"要开始录制或投放内容吗?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"在录制或投放内容时,提供此功能的服务将可访问屏幕上显示或设备中播放的所有信息,其中包括密码、付款信息、照片、消息及播放的音频等信息。"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"整个屏幕"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"单个应用"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"分享或录制应用"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"要开始使用<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>录制或投放吗?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"在分享、录制或投放内容时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"在分享、录制或投放内容时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可以访问通过此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"开始"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或录制应用"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要与“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”共享屏幕吗?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"共享一个应用"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"共享整个屏幕"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"当您共享整个屏幕时,屏幕上的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"当您共享一个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"共享屏幕"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已停用此选项"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"开始投放?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"在投放内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"在投放某个应用时,Android 可以访问此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"开始投放"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"选择要分享的应用"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"投放您的屏幕?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放单个应用"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整个屏幕"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"投放整个屏幕时,屏幕上的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"投放单个应用时,该应用显示或播放的所有内容均公开可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放屏幕"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"选择要投放的应用"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"开始分享?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"在分享内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"在分享、录制或投放内容时,Android 可以访问通过此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"开始"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"下一步"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"切换应用后,分享会暂停"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"改为分享此应用"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"切换回去"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"卫星,连接质量良好"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"卫星,可连接"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"卫星紧急呼救"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"紧急呼叫或紧急求救"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作资料"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"并不适合所有用户"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"系统界面调节工具可让您以更多方式调整及定制 Android 界面。在日后推出的版本中,这些实验性功能可能会变更、失效或消失。操作时请务必谨慎。"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"无障碍功能"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"无搜索结果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手势"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主屏幕手势"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作按键"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太棒了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"触控板,其中显示了三根手指右移和左移"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"设备屏幕,其中显示了返回手势的动画"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"如要返回,请使用三根手指在触控板上的任意位置左滑或右滑。\n\n您也可以使用键盘快捷操作键 + ESC 键进行返回。"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"太棒了!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您完成了“返回”手势教程。"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"若要随时进入主屏幕,请用三根手指从屏幕的底部向上滑动。"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"很好!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"您完成了“前往主屏幕”手势教程。"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作按键"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要访问您的应用,请按下键盘上的快捷操作按键。"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"您完成了“快捷操作按键”手势教程。\n\n按下快捷操作按键 + / 可显示所有可用快捷键。"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"如要前往主屏幕,请用三根手指在触控板上向上滑动"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近用过的应用,请用三根手指在触控板上向上滑动并按住"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有应用,请按下键盘上的快捷操作按键"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"已隐去"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"解锁即可查看"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"使用触控板返回"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"用三根手指向左或向右滑动。点按即可了解更多手势。"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"使用触控板前往主屏幕"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三根手指向上滑动并按住。点按即可了解更多手势。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用键盘查看所有应用"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"您可随时按下快捷操作按键。点按即可了解更多手势。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"“极暗”功能现已在亮度条中"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"现在,您可从屏幕顶部进一步调低亮度,将屏幕调成极暗。\n\n此功能在昏暗环境中效果最佳。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"移除“极暗”快捷方式"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"已移除“极暗”快捷方式。如要调低亮度,请使用常规亮度条。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 9c1049b..359b3cc 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> 和其他開啟的應用程式偵測到此螢幕截圖。"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"新增至筆記"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"加入連結"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"螢幕錄影機"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在處理螢幕錄影內容"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"要開始錄影嗎?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"當你錄影時,Android 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"當你錄影應用程式時,Android 可存取在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"開始錄影"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄影螢幕畫面嗎?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄影一個應用程式"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄影整個螢幕畫面"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"當你錄影整個螢幕畫面時,系統會錄影螢幕畫面上顯示的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄影應用程式時,系統會錄影該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄影螢幕畫面"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"選擇要錄影的應用程式"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"錄音"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"裝置音訊"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"裝置播放的音效,例如音樂、通話和鈴聲"</string>
@@ -139,9 +143,9 @@
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放螢幕"</string>
     <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
-    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"你正在投放整個螢幕畫面至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"你正在將整個螢幕畫面投放到「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"你正在投放整個螢幕畫面至一部附近的裝置"</string>
-    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"你正在投放「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」至「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string>
+    <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"你正在將「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」投放到「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app" msgid="8616103075630934513">"你正在投放「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」至一部附近的裝置"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic_with_device" msgid="9213582497852420203">"你正在投放內容至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="cast_to_other_device_stop_dialog_message_generic" msgid="4100272100480415076">"你正在投放內容至一部附近的裝置"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
     <string name="cancel" msgid="1089011503403416730">"取消"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"應用程式標誌"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"請再試一次"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"輕按即可取消驗證"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"以太網"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"請勿騷擾"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕按即可連結或解除連結裝置"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音訊分享功能"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"輕按即可切換或分享音訊"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"解除連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟動"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string>
     <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_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>
@@ -348,7 +353,7 @@
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"正在開啟…"</string>
     <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"數據節省模式已開啟"</string>
     <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# 部裝置}other{# 部裝置}}"</string>
-    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"電筒"</string>
+    <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"手電筒"</string>
     <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"相機使用中"</string>
     <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"流動數據"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"數據用量"</string>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換概覽"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"你不會受到聲音和震動騷擾 (鬧鐘除外)。當你選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自訂"</string>
@@ -474,16 +478,15 @@
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"僅限\n鬧鐘"</string>
     <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 無線充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
     <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
-    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 快速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後完成充電"</string>
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充滿電"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"上鎖畫面上的小工具"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>小工具加咗去上鎖畫面"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可開始共用教學課程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自訂"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"關閉"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"增、移除小工具,以及調整小工具在此空間中的位置"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"在這個空間新增或移除小工具,以及調整小工具的位置"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
@@ -507,10 +510,10 @@
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
     <string name="communal_widget_picker_title" msgid="1953369090475731663">"上鎖畫面小工具"</string>
-    <string name="communal_widget_picker_description" msgid="490515450110487871">"任何人都可查看上鎖畫面的小工具,即使平板電腦已上鎖亦然。"</string>
+    <string name="communal_widget_picker_description" msgid="490515450110487871">"無論平板電腦的螢幕是否已上鎖,任何人都可以看到上鎖畫面小工具。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消揀小工具"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"上鎖畫面小工具"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,即使平板電腦已鎖定,所有人還是能查看小工具。部分小工具可能不適用於上鎖畫面,而且新增至這裡後可能會有安全疑慮。"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,所有人都能查看小工具,即使平板電腦已鎖定亦然。部分小工具可能不適用於上鎖畫面,新增至這裡可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"知道了"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"當你錄影或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」將可存取畫面上顯示的任何資料或裝置播放的任何內容,包括密碼、付款資料、相片、訊息和播放的音訊等。"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"要開始錄影或投放嗎?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"在錄影或投放時,此功能的服務供應商可存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款資料、相片、訊息和播放的語音等資料。"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"整個螢幕畫面"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"單一應用程式"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"分享或錄影應用程式"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」錄影或投放嗎?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"當你分享、錄影或投放時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"當你分享、錄影或投放應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取顯示在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或錄影應用程式"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要與「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」分享螢幕畫面嗎?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"分享一個應用程式"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個螢幕畫面"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個螢幕畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到你畫面上的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享螢幕畫面"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"要開始投放嗎?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"當你投放時,Android 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"當你投放應用程式時,Android 可存取在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"開始投放"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放螢幕嗎?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整個螢幕畫面"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"當你投放整個螢幕畫面時,其他人可看到你畫面上的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"當你投放應用程式時,其他人可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放螢幕畫面"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"選擇要投放的應用程式"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"要開始分享嗎?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"當你分享、錄影或投放時,Android 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"當你分享、錄影或投放應用程式時,Android 可存取顯示在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"開始"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"下一步"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"切換應用程式時,系統會暫停分享"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"改為分享此應用程式"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"切換回先前的應用程式"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線質素好"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可以連線"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連接"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急電話或 SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作設定檔"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"這只是測試版本,並不包含完整功能"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"使用者介面調諧器讓你以更多方法修改和自訂 Android 使用者介面。但請小心,這些實驗功能可能會在日後發佈時更改、分拆或消失。"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙功能"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"沒有相符的搜尋結果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返去手勢"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作鍵"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太好了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"觸控板上有三隻手指左右移動"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"裝置畫面顯示返回手勢動畫"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"用三隻手指在觸控板上任何一處向左或向右滑動即可返回。\n\n你也可使用鍵盤快速鍵 Action 鍵 + Esc 鍵執行此操作。"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"太好了!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"只要用三隻手指從螢幕底部向上滑動,隨時可以返回主畫面。"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"做得好!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"你已完成「返回主畫面」手勢的教學課程。"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作鍵"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要存取應用程式,請在鍵盤上按下快捷操作鍵。"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"你已完成「快捷操作鍵」手勢的教學課程。\n\n按下動作 + / 鍵即可顯示所有可使用的快速鍵。"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"如要返回主畫面,請用三隻手指在觸控板上向上滑動"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近使用的應用程式,請用三隻手指在觸控板上向上滑動並按住"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有應用程式,請在鍵盤上按下快捷操作鍵"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"已剔除"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"解鎖即可查看"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"使用觸控板返回"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"用三隻手指向左或向右滑動。輕按即可瞭解更多手勢。"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"使用觸控板返回主畫面"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三隻手指向上滑動並按住。輕按即可瞭解更多手勢。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"隨時按下快捷操作鍵。輕按即可瞭解更多手勢。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"亮度列現已加入超暗功能"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"而家喺螢幕頂部進一步校低亮度,就可以令螢幕變得超暗\n\n呢個功能喺陰暗環境之下嘅效果最好"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"移除超暗功能快速鍵"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"超暗功能快速鍵已移除。如要降低亮度,請使用一般的亮度列。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1ac8ec4..63b19d6b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"「<xliff:g id="APPNAME">%1$s</xliff:g>」和其他開啟的應用程式偵測到這張螢幕截圖。"</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"新增至記事本"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"包含連結"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"螢幕錄影器"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"處理螢幕錄影內容"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"要開始錄影嗎?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"你在錄影時,Android 可存取畫面上的所有資訊或裝置播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音等內容。"</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"當你錄製應用程式畫面時,Android 可存取應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音等內容。"</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"開始錄影"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄製畫面嗎?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄製單一應用程式"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"錄製整個畫面"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"錄製整個畫面時,系統會錄下畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"當你錄製應用程式畫面時,系統會錄下該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"錄製畫面"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"選擇要錄製的應用程式"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"錄音"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"裝置音訊"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"來自裝置的音訊,例如音樂、通話和鈴聲等等"</string>
@@ -126,19 +130,19 @@
     <string name="screenrecord_save_text" msgid="3008973099800840163">"輕觸即可查看"</string>
     <string name="screenrecord_save_error" msgid="5862648532560118815">"儲存螢幕錄影內容時發生錯誤"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄製螢幕畫面時發生錯誤"</string>
-    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"要停止錄製畫面嗎?"</string>
+    <string name="screenrecord_stop_dialog_title" msgid="8716193661764511095">"停止錄製?"</string>
     <string name="screenrecord_stop_dialog_message" msgid="6262768207331626817">"目前正在錄製整個螢幕的畫面"</string>
     <string name="screenrecord_stop_dialog_message_specific_app" msgid="5995770227684523244">"目前正在錄製「<xliff:g id="APP_NAME">%1$s</xliff:g>」的畫面"</string>
-    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄音"</string>
+    <string name="screenrecord_stop_dialog_button" msgid="2883812564938194350">"停止錄製"</string>
     <string name="share_to_app_chip_accessibility_label" msgid="4210256229976947065">"正在分享畫面"</string>
-    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"要停止分享畫面嗎?"</string>
+    <string name="share_to_app_stop_dialog_title" msgid="9212915050910250438">"停止分享?"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen_with_host_app" msgid="522823522115375414">"目前正在與「<xliff:g id="HOST_APP_NAME">%1$s</xliff:g>」分享整個畫面"</string>
     <string name="share_to_app_stop_dialog_message_entire_screen" msgid="5090115386271179270">"目前正在與某個應用程式分享整個畫面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_specific" msgid="5923772039347985172">"目前正在分享「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」的畫面"</string>
     <string name="share_to_app_stop_dialog_message_single_app_generic" msgid="6681016774654578261">"目前正在分享應用程式畫面"</string>
     <string name="share_to_app_stop_dialog_button" msgid="6334056916284230217">"停止分享"</string>
     <string name="cast_screen_to_other_device_chip_accessibility_label" msgid="4687917476203009885">"正在投放畫面"</string>
-    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"要停止投放嗎?"</string>
+    <string name="cast_to_other_device_stop_dialog_title" msgid="7836517190930357326">"停止投放?"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen_with_device" msgid="1474703115926205251">"目前正在將整個畫面投放到「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」"</string>
     <string name="cast_to_other_device_stop_dialog_message_entire_screen" msgid="8419219169553867625">"目前正在將整個畫面投放到鄰近裝置"</string>
     <string name="cast_to_other_device_stop_dialog_message_specific_app_with_device" msgid="2715934698604085519">"目前正在將「<xliff:g id="APP_BEING_SHARED_NAME">%1$s</xliff:g>」投放到「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"傳送"</string>
     <string name="cancel" msgid="1089011503403416730">"取消"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"應用程式標誌"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"確認"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"再試一次"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"輕觸即可取消驗證"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"螢幕保護程式"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"乙太網路"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"零打擾"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"優先模式"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"藍牙"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"找不到配對的裝置"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"輕觸即可連結/取消連結裝置"</string>
@@ -299,14 +302,16 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"音訊分享"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"輕觸即可切換或分享音訊"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"已儲存"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="3345758139235739006">"明天自動開啟"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都會使用藍牙技術"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"開啟「設定」"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"其他裝置"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"切換總覽"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"優先模式"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"完成"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"設定"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"設定"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"裝置不會發出音效或震動造成干擾,但是會保留鬧鐘響鈴。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"自訂"</string>
@@ -478,12 +482,11 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 慢速充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 將於 <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>後充飽"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"螢幕鎖定畫面上的小工具"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具已新增到螢幕鎖定畫面"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"向左滑動即可啟動通用教學課程"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"自訂"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"關閉"</string>
-    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"新增、移除小工具,以及調整小工具在這個空間中的位置"</string>
+    <string name="cta_label_to_edit_widget" msgid="6496885074209203756">"新增和移除小工具,及調整小工具在此空間的位置"</string>
     <string name="cta_label_to_open_widget_picker" msgid="3874946756976360699">"新增更多小工具"</string>
     <string name="popup_on_dismiss_cta_tile_text" msgid="8292501780996070019">"長按即可自訂小工具"</string>
     <string name="button_to_configure_widgets_text" msgid="4191862850185256901">"自訂小工具"</string>
@@ -506,11 +509,11 @@
     <string name="accessibility_action_label_select_widget" msgid="8897281501387398191">"選取小工具"</string>
     <string name="accessibility_action_label_remove_widget" msgid="3373779447448758070">"移除小工具"</string>
     <string name="accessibility_action_label_place_widget" msgid="1914197458644168978">"放置所選小工具"</string>
-    <string name="communal_widget_picker_title" msgid="1953369090475731663">"螢幕鎖定畫面小工具"</string>
+    <string name="communal_widget_picker_title" msgid="1953369090475731663">"螢幕鎖定小工具"</string>
     <string name="communal_widget_picker_description" msgid="490515450110487871">"即使平板電腦已鎖定,所有人仍可查看螢幕鎖定畫面上的小工具。"</string>
     <string name="accessibility_action_label_unselect_widget" msgid="1041811747619468698">"取消選取小工具"</string>
     <string name="communal_widgets_disclaimer_title" msgid="1150954395585308868">"螢幕鎖定小工具"</string>
-    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,系統會要求你驗證身分。請注意,即使平板電腦已鎖定,所有人還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,而且新增到這裡可能有安全疑慮。"</string>
+    <string name="communal_widgets_disclaimer_text" msgid="1423545475160506349">"如要使用小工具開啟應用程式,需先驗證身分。請留意,即使平板電腦已鎖定,所有人都還是能查看小工具。某些小工具可能不適用於螢幕鎖定畫面,新增到此可能會有安全疑慮。"</string>
     <string name="communal_widgets_disclaimer_button" msgid="4423059765740780753">"我知道了"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"切換使用者"</string>
     <string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"下拉式選單"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」將可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款資料、相片、訊息和你播放的音訊等資訊。"</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"要開始錄製或投放內容嗎?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"當你錄製或投放內容時,提供這項功能的服務將可存取畫面上顯示的任何資訊或裝置播放的任何內容,包括密碼、付款資料、相片、訊息和你播放的音訊等資訊。"</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"整個螢幕畫面"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"單一應用程式"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"分享或錄製應用程式"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"要開始使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」錄製或投放內容嗎?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"當你分享、錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上顯示的任何資訊或裝置播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"當你分享、錄製或投放應用程式內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取應用程式中顯示的任何資訊或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"分享或錄製應用程式"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」分享畫面嗎?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"分享單一應用程式的畫面"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"分享整個畫面"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"當你分享整個畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享畫面"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"要開始投放嗎?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"投放時,Android 可存取畫面上顯示的任何資訊或裝置播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音等內容。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"當你投放應用程式時,Android 可存取應用程式顯示的任何資訊或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音等內容。"</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"開始投放"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放畫面嗎?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"投放整個畫面"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"當你投放整個畫面時,畫面上的所有內容都會顯示出來。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"當你投放應用程式畫面時,該應用程式呈現或播放的所有內容都會顯示出來。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"投放螢幕"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"選擇要投放的應用程式"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"要開始分享嗎?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"當你分享、錄製或投放內容時,Android 將可存取畫面上顯示的任何資訊或裝置播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"當你分享、錄製或投放內容時,Android 可存取應用程式中顯示的任何資訊或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"開始"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"繼續"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"切換應用程式時暫停分享"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"改為分享這個應用程式"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"切回"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"衛星,連線品質良好"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"衛星,可連線"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"緊急衛星連線"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"緊急電話或緊急求救"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"工作資料夾"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"有趣與否,見仁見智"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"系統使用者介面調整精靈可讓你透過其他方式,調整及自訂 Android 使用者介面。這些實驗性功能隨著版本更新可能會變更、損壞或消失,執行時請務必謹慎。"</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"無障礙"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"鍵盤快速鍵"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"找不到相符的搜尋結果"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"返回手勢"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"主畫面手勢"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"快捷操作鍵"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"太棒了!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"動畫顯示三指正在觸控板上向左右移動"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"裝置畫面顯示返回手勢的動畫"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"如要返回,請在觸控板的任何位置上用三指向左或向右滑動。\n\n使用快捷操作鍵 + ESC 鍵 (鍵盤快速鍵) 也可以返回。"</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"太棒了!"</string>
+    <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string>
+    <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"用 3 指從螢幕底部向上滑動,就能隨時返回主畫面。"</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"太棒了!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"你已完成「返回主畫面」手勢的教學課程。"</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"快捷操作鍵"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"如要存取應用程式,請按下鍵盤上的快捷操作鍵。"</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"恭喜!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"你已完成「快捷操作鍵」手勢的教學課程。\n\n按下快捷操作鍵 + / 鍵,就能顯示所有可用的快速鍵。"</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"如要前往主畫面,請在觸控板上用三指向上滑動"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"如要查看最近使用的應用程式,請在觸控板上向上滑動並按住"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"如要查看所有應用程式,請按下鍵盤上的快捷操作鍵"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"已遮蓋"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"解鎖即可查看"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"使用觸控板返回"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"用三指向左或向右滑動。輕觸即可進一步瞭解手勢。"</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"使用觸控板前往主畫面"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"用三指向上滑動並按住。輕觸即可進一步瞭解手勢。"</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"使用鍵盤查看所有應用程式"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"你隨時可以按下快捷操作鍵。輕觸即可進一步瞭解手勢。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"「超暗」已移到亮度列"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"現在只要在螢幕頂端將亮度設定調得更低,就能讓螢幕變得更暗。\n\n這項設定最適合在昏暗環境下使用。"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"移除「超暗」捷徑"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"「超暗」捷徑已移除。如要調低亮度,請使用一般的亮度列。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 3a34c47..048cb01 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -104,13 +104,17 @@
     <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"I-<xliff:g id="APPNAME">%1$s</xliff:g> namanye ama-app avuliwe athole lesi sithombe-skrini."</string>
     <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Engeza kunothi"</string>
     <string name="backlinks_include_link" msgid="4562093591148248158">"Faka ilinki"</string>
+    <string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>"</string>
     <string name="screenrecord_title" msgid="4257171601439507792">"Okokuqopha iskrini"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Icubungula okokuqopha iskrini"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</string>
-    <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Qala Ukurekhoda?"</string>
-    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Ngenkathi urekhoda, i-Android inokufinyelela kunoma yini ebonakalayo esikrinini sakho noma edlalwa kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Ngenkathi urekhoda i-app, i-Android inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Qala ukurekhoda"</string>
+    <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Rekhoda isikrini sakho?"</string>
+    <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Rekhoda i-app eyodwa"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="2794896384693120020">"Rekhoda sonke isikrini"</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Uma urekhoda sonke isikrini sakho, noma yini evela esikrinini iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="screenrecord_permission_dialog_warning_single_app" msgid="3738199712880063924">"Uma urekhoda i-app, noma yini evezwa noma edlala kuleyo app iyarekhodwa. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Rekhoda isikrini"</string>
+    <string name="screenrecord_app_selector_title" msgid="3854492366333954736">"Khetha i-app yokurekhoda"</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"Rekhoda umsindo"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Umsindo wedivayisi"</string>
     <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Umsindo ophuma kudivayisi yakho, njengomculo, amakholi, namathoni okukhala"</string>
@@ -176,8 +180,6 @@
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Thumela"</string>
     <string name="cancel" msgid="1089011503403416730">"Khansela"</string>
     <string name="biometric_dialog_logo" msgid="7681107853070774595">"Ilogo ye-app"</string>
-  <string-array name="biometric_dialog_package_names_for_logo_with_overrides">
-  </string-array>
     <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Qinisekisa"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Zama futhi"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Thepha ukuze ukhansele ukufakazela ubuqiniso"</string>
@@ -290,7 +292,8 @@
     <string name="start_dreams" msgid="9131802557946276718">"Isigciniskrini"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"I-Ethernet"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"Ungaphazamisi"</string>
-    <string name="quick_settings_modes_label" msgid="5407025818652750501">"Amamodi okubalulekile"</string>
+    <!-- no translation found for quick_settings_modes_label (879156359479504244) -->
+    <skip />
     <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"I-Bluetooth"</string>
     <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"Awekho amadivayisi abhanqiwe atholakalayo"</string>
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Thepha ukuze uxhumae noma ungaxhumi idivaysi"</string>
@@ -299,6 +302,7 @@
     <string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa iBluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ukwabelana Ngokuqoshiwe"</string>
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active" msgid="3227408556754456024">"Thepha ukuze ushintshe noma wabelane ngokulalelwayo"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
@@ -307,6 +311,7 @@
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string>
     <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_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>
@@ -400,7 +405,7 @@
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string>
-    <string name="live_caption_title" msgid="8916875614623730005">"Amagama-ncazo abukhoma"</string>
+    <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okushuthwe Bukhoma"</string>
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
@@ -429,17 +434,16 @@
     <string name="sensor_privacy_dialog_open_settings" msgid="5635865896053011859">"Vula Amasethingi"</string>
     <string name="media_seamless_other_device" msgid="4654849800789196737">"Enye idivayisi"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Guqula ukubuka konke"</string>
-    <string name="zen_modes_dialog_title" msgid="4159138230418567383">"Amamodi okubalulekile"</string>
+    <!-- no translation found for zen_modes_dialog_title (8854640808100096934) -->
+    <skip />
     <string name="zen_modes_dialog_done" msgid="6654130880256438950">"Kwenziwe"</string>
     <string name="zen_modes_dialog_settings" msgid="2310248023728936697">"Amasethingi"</string>
     <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 (7457957033034460064) -->
-    <skip />
-    <!-- no translation found for zen_mode_no_manual_invocation (1769975741344633672) -->
-    <skip />
-    <!-- no translation found for zen_mode_active_modes (1625850411578488856) -->
-    <skip />
+    <string name="zen_mode_set_up" msgid="7457957033034460064">"Setha"</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>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"Ngeke uze uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"Enza ngendlela oyifisayo"</string>
@@ -478,8 +482,7 @@
     <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ishaja kancane • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Iyashaja • Izogcwala ngo-<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_action_open_communal_hub" msgid="3081702792413787849">"Amawijethi ekukhiyeni isikrini"</string>
-    <!-- no translation found for accessibility_announcement_communal_widget_added (6911593106099328271) -->
-    <skip />
+    <string name="accessibility_announcement_communal_widget_added" msgid="6911593106099328271">"Iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ifakwe esikrinini sokukhiya"</string>
     <string name="communal_tutorial_indicator_text" msgid="4503010353591430123">"Swayiphela kwesokunxele ukuze uqale okokufundisa komphakathi"</string>
     <string name="cta_tile_button_to_open_widget_editor" msgid="3871562362382963878">"Enza ngendlela oyifisayo"</string>
     <string name="cta_tile_button_to_dismiss" msgid="3377597875997861754">"Chitha"</string>
@@ -531,22 +534,31 @@
     <string name="media_projection_dialog_warning" msgid="1303664408388363598">"I-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> izothola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma idlalwe kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
     <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Qala ukurekhoda noma ukusakaza?"</string>
     <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Isevisi enikezela ngalo msebenzi izothola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma oludlalwa kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
-    <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Sonke isikrini"</string>
-    <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"I-app eyodwa"</string>
-    <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Yabelana noma rekhoda i-app"</string>
-    <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Qala ukurekhoda noma ukusakaza nge-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Uma wabelana, urekhoda, noma usakaza, i-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inokufinyelela kunoma yini ebonakalayo kusikrini sakho noma edlalwa kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Uma wabelana, ukurekhoda, noma ukusakaza ku-app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezfana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Qala"</string>
+    <string name="screen_share_generic_app_selector_title" msgid="8331515850599218288">"Yabelana noma urekhode i-app"</string>
+    <string name="media_projection_entry_app_permission_dialog_title" msgid="4613857256721708062">"Yabelana ngesikrini sakho ne-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
+    <string name="screen_share_permission_dialog_option_single_app" msgid="2974054871681567314">"Yabelana nge-app eyodwa"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_single_app (2308901434964846084) -->
+    <skip />
+    <string name="screen_share_permission_dialog_option_entire_screen" msgid="4493174362775038997">"Yabelana ngesikrini sonke"</string>
+    <!-- no translation found for media_projection_entry_app_permission_dialog_option_text_entire_screen (5100078808078139706) -->
+    <skip />
+    <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="5504288438067851086">"Uma wabelana ngesikrini sakho sonke, noma yini esesikrinini sakho ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Uma wabelana nge-app, noma yini eboniswayo noma edlalwayo kuleyo app ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Yabelana ngesikrini"</string>
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ivale le nketho"</string>
-    <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Qala ukusakaza?"</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Uma usakaza, i-Android inokufinyelela kunoma yini ebonakalayo kusikrini sakho noma edlalwa kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Uma usakaza i-app, i-Android inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
-    <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Qala ukusakaza"</string>
+    <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Khetha i-app yokwabelana"</string>
+    <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Sakaza isikrini sakho?"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Sakaza i-app eyodwa"</string>
+    <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Sakaza isikrini sonke"</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Uma usakaza isikrini sakho sonke, noma yini esesikrinini sakho iyabonakala. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Uma usakaza i-app, noma yini ekhonjiswe noma edlalwe kuleyo-app iyabonakala. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
+    <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Isikrini sokusakaza"</string>
+    <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Khetha i-app yokusakaza"</string>
     <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Qala ukwabelana?"</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Uma wabelana, ukurekhoda, noma ukusakaza, i-Android inokufinyelela kunoma yini ebonakala esikrinini sakho noma okudlalwayo kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
     <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Uma wabelana, ukurekhoda, noma ukusakaza ku-app, i-Android inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezfana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
     <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Qala"</string>
+    <string name="media_projection_entry_generic_permission_dialog_continue_single_app" msgid="5920814988611877051">"Okulandelayo"</string>
     <string name="media_projection_task_switcher_text" msgid="590885489897412359">"Ukwabelana kuyaphumula uma ushintsha ama-app"</string>
     <string name="media_projection_task_switcher_action_switch" msgid="8682258717291921123">"Yabelana ngale-app esikhundleni salokho"</string>
     <string name="media_projection_task_switcher_action_back" msgid="5324164224147845282">"Shintshela emuva"</string>
@@ -714,6 +726,7 @@
     <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"Isethelayithi, uxhumano oluhle"</string>
     <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"Isethelayithi, uxhumano luyatholakala"</string>
     <string name="satellite_connected_carrier_text" msgid="118524195198532589">"Isethelayithi yokuxhumana ngezimo eziphuthumayo"</string>
+    <string name="satellite_emergency_only_carrier_text" msgid="828510231597991206">"Ikholi ephuthumayo noma i-SOS"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"Iphrofayela yomsebenzi"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"Kuyajabulisa kwabanye kodwa hhayi bonke"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Isishuni se-UI sesistimu sikunika izindlela ezingeziwe zokuhlobisa nokwenza ngezifiso isixhumanisi sokubona se-Android. Lezi zici zesilingo zingashintsha, zephuke, noma zinyamalale ekukhishweni kwangakusasa. Qhubeka ngokuqaphela."</string>
@@ -1376,21 +1389,38 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Ukufinyeleleka"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Izinqamuleli zekhibhodi"</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string>
+    <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"Ayikho imiphumela yosesho"</string>
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string>
     <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string>
+    <!-- no translation found for launch_keyboard_tutorial_notification_title (8849933155160522519) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_tutorial_notification_content (2880339951512757918) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_title (2243780062772196901) -->
+    <skip />
+    <!-- no translation found for launch_touchpad_tutorial_notification_content (7931085031240753226) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_title (1940023776496198762) -->
+    <skip />
+    <!-- no translation found for launch_keyboard_touchpad_tutorial_notification_content (1780725168171929365) -->
+    <skip />
     <string name="touchpad_tutorial_back_gesture_button" msgid="2746834288077265946">"Ukunyakazisa umzimba kwangemuva"</string>
     <string name="touchpad_tutorial_home_gesture_button" msgid="7640544867625955304">"Ukunyakazisa umzimba kwasekhaya"</string>
     <string name="touchpad_tutorial_action_key_button" msgid="3220074511852927267">"Inkinobho yokufinyelela"</string>
     <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string>
-    <string name="touchpad_tutorial_gesture_done" msgid="4784438360736821255">"Umsebenzi omuhle!"</string>
     <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string>
-    <!-- no translation found for touchpad_back_gesture_guidance (6263750214998421587) -->
-    <skip />
-    <!-- no translation found for touchpad_back_gesture_finished (5353616006999726249) -->
-    <skip />
-    <string name="touchpad_back_gesture_animation_content_description" msgid="2646107450922379918">"Iphedi yokuthinta ebonisa iminwe emithathu iya kwesokudla nakwesokunxele"</string>
-    <string name="touchpad_back_gesture_screen_animation_content_description" msgid="4036267494237748710">"Isikrini sedivayisi esibonisa opopayi bokuthinta kwasemuva"</string>
+    <string name="touchpad_back_gesture_guidance" msgid="6263750214998421587">"Ukuze ubuyele emuva, swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu noma yikuphi ephedini yokuthinta.\n\nUngasebenzisa nesinqamuleli sekhibhodi Isenzo + ESC kulokhu."</string>
+    <string name="touchpad_back_gesture_success_title" msgid="7240576648330612171">"Umsebenzi omuhle!"</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>
+    <string name="touchpad_home_gesture_guidance" msgid="3043931356096731966">"Ukuze uye esikrinini sakho sasekhaya nganoma isiphi isikhathi, swayipha uye phezulu ngeminwe emithathu usuka phansi esikrinini sakho."</string>
+    <string name="touchpad_home_gesture_success_title" msgid="3778407003948209795">"Kuhle!"</string>
+    <string name="touchpad_home_gesture_success_body" msgid="2404031094918807067">"Ukuqedile ukuthinta kokuya ekhaya."</string>
+    <string name="tutorial_action_key_title" msgid="2659466586996495447">"Inkinobho yokufinyelela"</string>
+    <string name="tutorial_action_key_guidance" msgid="5718948664616999196">"Ukuze ufinyelele ama-app wakho, cindezela inkinobho yokufinyelela kukhibhodi yakho."</string>
+    <string name="tutorial_action_key_success_title" msgid="466467860120112933">"Halala!"</string>
+    <string name="tutorial_action_key_success_body" msgid="7201991081652850430">"Uqedele ukuthinta inkinobho yokufinyelela.\n\nIsenzo +/ sibonisa zonke izinqamuleli onazo."</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>
@@ -1400,6 +1430,8 @@
     <string name="home_edu_toast_content" msgid="3381071147871955415">"Ukuze uye ekhaya, swayiphela phezulu ngeminwe emithathu ephedini yokuthinta"</string>
     <string name="overview_edu_toast_content" msgid="5797030644017804518">"Ukuze ubuke ama-app akamuva, swayiphela phezulu bese ubambe ngeminwe emithathu ephedini yokuthinta"</string>
     <string name="all_apps_edu_toast_content" msgid="8807496014667211562">"Ukuze ubuke wonke ama-app wakho, cindezela inkinobho yokufinyelela kukhibhodi yakho"</string>
+    <string name="redacted_notification_single_line_title" msgid="212019960919261670">"Kwenziwe iredact"</string>
+    <string name="redacted_notification_single_line_text" msgid="8684166405005242945">"Vula ukuze ubuke"</string>
     <string name="back_edu_notification_title" msgid="5624780717751357278">"Sebenzisa iphedi yokuthinta ukuze ubuyele emuva"</string>
     <string name="back_edu_notification_content" msgid="2497557451540954068">"Swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
     <string name="home_edu_notification_title" msgid="6097902076909654045">"Sebenzisa iphedi yokuthinta ukuya ekhaya"</string>
@@ -1408,4 +1440,8 @@
     <string name="overview_edu_notification_content" msgid="3578204677648432500">"Swayiphela phezulu bese uyabamba usebenzisa iminwe emithathu. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
     <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Sebenzisa ikhibhodi yakho ukubuka wonke ama-app"</string>
     <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Cindezela inkinobho yokufinyelela noma kunini. Thepha ukuze ufunde kabanzi ngokunyakazisa umzimba."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="4369307638184799742">"Ukufiphala okwengeziwe manje sekuyingxenye yebha yokukhanya"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="7513137763024327538">"Manje ungenza isikrini sifiphale ngokwengeziwe ngokwehlisa izinga lokukhanya nakakhulu kusukela phezulu kwesikrini sakho.\n\nLokhu kusebenza kahle kakhulu uma usendaweni emnyama."</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="1782147201534669800">"Susa isinqamuleli esifiphele esengeziwe"</string>
+    <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="4070696910424515757">"Isinqamuleli esifiphele ngokwengeziwe sikhishiwe. Ukuze wehlise ukukhanya kwakho, sebenzisa ibha yokukhanya evamile."</string>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8cf0fb2..a375264 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -29,7 +29,7 @@
     <color name="status_bar_icons_hover_color_dark">#38000000</color> <!-- 22% black -->
 
     <!-- The dark background color behind the shade -->
-    <color name="shade_scrim_background_dark">@*android:color/black</color>
+    <color name="shade_scrim_background_dark">@androidprv:color/system_under_surface_light</color>
 
     <!-- The color of the background in the separated list of the Global Actions menu -->
     <color name="global_actions_separated_background">#F5F5F5</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f0c8894..38ef0e9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -727,6 +727,10 @@
         .75
     </item>
 
+    <!-- The last x ms of face acquired info messages to analyze to determine
+         whether to show a deferred face auth help message. -->
+    <integer name="config_face_help_msgs_defer_analyze_timeframe">500</integer>
+
     <!-- Which face help messages to surface when fingerprint is also enrolled.
          Message ids correspond with the acquired ids in BiometricFaceConstants -->
     <integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
@@ -1048,9 +1052,17 @@
     <!-- The width of the shortcut helper container, as a fraction of the screen's width. -->
     <item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">1.0</item>
 
-    <!-- Only applicable for dual shade - Allow Notifications/QS shade to anchor to the bottom. -->
-    <bool name="config_dualShadeAlignedToBottom">false</bool>
-
     <!-- List of packages for which we want to use activity info (instead of application info) for biometric prompt logo. Empty for AOSP. [DO NOT TRANSLATE] -->
     <string-array name="config_useActivityLogoForBiometricPrompt" translatable="false"/>
+
+    <!--
+    Whether to enable the desktop specific feature set.
+
+    Refrain from using this from code that needs to make decisions
+    regarding the size or density of the display.
+
+    Variant owners and OEMs should override this to true when they want to
+    enable the desktop specific features.
+    -->
+    <bool name="config_enableDesktopFeatureSet">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e5750d2..141d035 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1751,6 +1751,7 @@
 
     <!-- System UI Dialog -->
     <dimen name="dialog_title_text_size">24sp</dimen>
+    <dimen name="dialog_button_side_margin">8dp</dimen>
 
     <!-- Internet panel related dimensions -->
     <dimen name="internet_dialog_list_max_height">662dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e4f900d..20e70e0 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -278,4 +278,12 @@
     <!-- Id for the udfps accessibility overlay -->
     <item type="id" name="udfps_accessibility_overlay" />
     <item type="id" name="udfps_accessibility_overlay_top_guideline" />
+
+    <!-- Ids for communal hub widgets -->
+    <item type="id" name="communal_widget_disposable_tag"/>
+
+    <!-- snapshot view-binding IDs -->
+    <item type="id" name="snapshot_view_binding" />
+    <item type="id" name="snapshot_view_binding_root" />
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a5fd5b9..1fb1dad 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -269,7 +269,12 @@
     <string name="screenshot_detected_multiple_template"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> and other open apps detected this screenshot.</string>
     <!-- Add to note button used in App Clips flow to return the saved screenshot image to notes app. [CHAR LIMIT=NONE] -->
     <string name="app_clips_save_add_to_note">Add to note</string>
+    <!-- A check box used in App Clips flow to return the captured backlink of the screenshotted app to notes app. [CHAR LIMIT=NONE] -->
     <string name="backlinks_include_link">Include link</string>
+    <!-- A label for backlinks app that is used if there are multiple backlinks with same app name. [CHAR LIMIT=NONE] -->
+    <string name="backlinks_duplicate_label_format"><xliff:g id="appName" example="Google Chrome">%1$s</xliff:g> <xliff:g id="frequencyCount" example="(1)">(%2$d)</xliff:g></string>
+    <!-- An error message to inform user that capturing backlink from cross profile apps is not possible. [CHAR LIMIT=NONE] -->
+    <string name="backlinks_cross_profile_error">Links can\'t be added from other profiles</string>
 
     <!-- Notification title displayed for screen recording [CHAR LIMIT=50]-->
     <string name="screenrecord_title">Screen Recorder</string>
@@ -280,13 +285,19 @@
 
     <!-- For updated Screen Recording permission dialog (i.e. with PSS)-->
     <!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]-->
-    <string name="screenrecord_permission_dialog_title">Start Recording?</string>
+    <string name="screenrecord_permission_dialog_title">Record your screen?</string>
+    <!-- Screen recording permission option for recording just a single app [CHAR LIMIT=50] -->
+    <string name="screenrecord_permission_dialog_option_text_single_app">Record one app</string>
+    <!-- Screen recording permission option for recording the whole screen [CHAR LIMIT=50] -->
+    <string name="screenrecord_permission_dialog_option_text_entire_screen">Record entire screen</string>
     <!-- Message reminding the user that sensitive information may be captured during a full screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
-    <string name="screenrecord_permission_dialog_warning_entire_screen">While you’re recording, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+    <string name="screenrecord_permission_dialog_warning_entire_screen">When you’re recording your entire screen, anything shown on your screen is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- Message reminding the user that sensitive information may be captured during a single app screen recording for the updated dialog that includes partial screen sharing option [CHAR_LIMIT=350]-->
-    <string name="screenrecord_permission_dialog_warning_single_app">While you’re recording an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
-    <!-- Button to start a screen recording in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]-->
-    <string name="screenrecord_permission_dialog_continue">Start recording</string>
+    <string name="screenrecord_permission_dialog_warning_single_app">When you’re recording an app, anything shown or played in that app is recorded. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
+    <!-- Button to start a screen recording of the entire screen in the updated screen record dialog that allows to select an app to record [CHAR LIMIT=50]-->
+    <string name="screenrecord_permission_dialog_continue_entire_screen">Record screen</string>
+    <!-- Title of the activity that allows to select an app to screen record [CHAR LIMIT=70] -->
+    <string name="screenrecord_app_selector_title">Choose app to record</string>
 
     <!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]-->
     <string name="screenrecord_audio_label">Record audio</string>
@@ -714,8 +725,8 @@
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
     <!-- QuickSettings: Do not disturb - Alarms only [CHAR LIMIT=NONE] -->
     <!-- QuickSettings: Do not disturb - Total silence [CHAR LIMIT=NONE] -->
-    <!-- QuickSettings: Priority modes [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_modes_label">Priority modes</string>
+    <!-- QuickSettings: Modes [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_modes_label">Modes</string>
     <!-- QuickSettings: Bluetooth [CHAR LIMIT=NONE] -->
     <string name="quick_settings_bluetooth_label">Bluetooth</string>
     <!-- QuickSettings: Bluetooth (Multiple) [CHAR LIMIT=NONE] -->
@@ -734,6 +745,8 @@
     <string name="quick_settings_bluetooth_device_connected">Connected</string>
     <!-- QuickSettings: Bluetooth dialog device in audio sharing default summary [CHAR LIMIT=50]-->
     <string name="quick_settings_bluetooth_device_audio_sharing">Audio Sharing</string>
+    <!-- QuickSettings: Bluetooth dialog device summary for devices that are capable of audio sharing and switching to active[CHAR LIMIT=NONE]-->
+    <string name="quick_settings_bluetooth_device_audio_sharing_or_switch_active">Tap to switch or share audio</string>
     <!-- QuickSettings: Bluetooth dialog device saved default summary [CHAR LIMIT=NONE]-->
     <string name="quick_settings_bluetooth_device_saved">Saved</string>
     <!-- QuickSettings: Accessibility label to disconnect a device [CHAR LIMIT=NONE]-->
@@ -750,6 +763,8 @@
     <string name="quick_settings_bluetooth_audio_sharing_button">Share audio</string>
     <!-- QuickSettings: Bluetooth dialog audio sharing button text when sharing audio [CHAR LIMIT=50]-->
     <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 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>
@@ -1086,25 +1101,28 @@
     <!-- QuickStep: Accessibility to toggle overview [CHAR LIMIT=40] -->
     <string name="quick_step_accessibility_toggle_overview">Toggle Overview</string>
 
-    <!-- Priority modes dialog title [CHAR LIMIT=35] -->
-    <string name="zen_modes_dialog_title">Priority modes</string>
+    <!-- Modes dialog title [CHAR LIMIT=35] -->
+    <string name="zen_modes_dialog_title">Modes</string>
 
-    <!-- Priority modes dialog confirmation button [CHAR LIMIT=15] -->
+    <!-- Modes dialog confirmation button [CHAR LIMIT=15] -->
     <string name="zen_modes_dialog_done">Done</string>
 
-    <!-- Priority modes dialog settings shortcut button [CHAR LIMIT=15] -->
+    <!-- Modes dialog settings shortcut button [CHAR LIMIT=15] -->
     <string name="zen_modes_dialog_settings">Settings</string>
 
-    <!-- Priority modes: label for an active mode [CHAR LIMIT=35] -->
+    <!-- Modes: label for an active mode [CHAR LIMIT=35] -->
     <string name="zen_mode_on">On</string>
 
-    <!-- Priority modes: label for an inactive mode [CHAR LIMIT=35] -->
+    <!-- Modes: label for an active mode, with details [CHAR LIMIT=10] -->
+    <string name="zen_mode_on_with_details">On • <xliff:g id="trigger_description" example="Mon-Fri, 23:00-7:00">%1$s</xliff:g></string>
+
+    <!-- Modes: label for an inactive mode [CHAR LIMIT=35] -->
     <string name="zen_mode_off">Off</string>
 
-    <!-- Priority modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
+    <!-- Modes: label for a mode that needs to be set up [CHAR LIMIT=35] -->
     <string name="zen_mode_set_up">Set up</string>
 
-    <!-- Priority modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
+    <!-- Modes: label for a mode that cannot be manually turned on [CHAR LIMIT=35] -->
     <string name="zen_mode_no_manual_invocation">Manage in settings</string>
 
     <string name="zen_mode_active_modes">
@@ -1358,21 +1376,21 @@
     <!-- Media projection permission dialog warning text for system services. [CHAR LIMIT=NONE] -->
     <string name="media_projection_sys_service_dialog_warning">The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.</string>
 
-    <!-- Permission dropdown option for sharing or recording the whole screen. [CHAR LIMIT=30] -->
-    <string name="screen_share_permission_dialog_option_entire_screen">Entire screen</string>
-    <!-- Permission dropdown option for sharing or recording single app. [CHAR LIMIT=30] -->
-    <string name="screen_share_permission_dialog_option_single_app">A single app</string>
-    <!-- Title of the dialog that allows to select an app to share or record [CHAR LIMIT=NONE] -->
-    <string name="screen_share_permission_app_selector_title">Share or record an app</string>
+    <!-- Title of the activity that allows users to select an app to share or record [CHAR LIMIT=NONE] -->
+    <string name="screen_share_generic_app_selector_title">Share or record an app</string>
 
     <!-- Media projection that launched from 1P/3P apps -->
     <!-- 1P/3P app media projection permission dialog title. [CHAR LIMIT=NONE] -->
     <string name="media_projection_entry_app_permission_dialog_title">Share your screen with <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g>?</string>
 
     <!-- 1P/3P app media projection permission option for capturing just a single app [CHAR LIMIT=50] -->
-    <string name="media_projection_entry_app_permission_dialog_option_text_single_app">Share one app</string>
+    <string name="screen_share_permission_dialog_option_single_app">Share one app</string>
+    <!-- CTS tests rely on the `screen_share_permission_dialog_option_single_app` resource name, so just point the updated resource name to the old resource name. -->
+    <string name="media_projection_entry_app_permission_dialog_option_text_single_app">@string/screen_share_permission_dialog_option_single_app</string>
     <!-- 1P/3P app media projection permission option for capturing the whole screen [CHAR LIMIT=50] -->
-    <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen">Share entire screen</string>
+    <string name="screen_share_permission_dialog_option_entire_screen">Share entire screen</string>
+    <!-- CTS tests rely on the `screen_share_permission_dialog_option_entire_screen` resource name, so just point the updated resource name to the old resource name. -->
+    <string name="media_projection_entry_app_permission_dialog_option_text_entire_screen">@string/screen_share_permission_dialog_option_entire_screen</string>
     <!-- 1P/3P app media projection permission warning for capturing the whole screen. [CHAR LIMIT=350] -->
     <string name="media_projection_entry_app_permission_dialog_warning_entire_screen">When you’re sharing your entire screen, anything on your screen is visible to <xliff:g id="app_seeking_permission" example="Meet">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- 1P/3P app media projection permission warning for capturing an app. [CHAR LIMIT=350] -->
@@ -1381,6 +1399,8 @@
     <string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
     <!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
     <string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+    <!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
+    <string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
 
     <!-- Casting that launched by SysUI (i.e. when there is no app name) -->
     <!-- System casting media projection permission dialog title. [CHAR LIMIT=100] -->
@@ -1395,6 +1415,8 @@
     <string name="media_projection_entry_cast_permission_dialog_warning_single_app">When you’re casting an app, anything shown or played in that app is visible. So be careful with things like passwords, payment details, messages, photos, and audio and video.</string>
     <!-- System casting media projection permission button to continue for SysUI casting. [CHAR LIMIT=60] -->
     <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen">Cast screen</string>
+    <!-- Title of the activity that allows users to select an app to cast [CHAR LIMIT=70] -->
+    <string name="media_projection_entry_cast_app_selector_title">Choose app to cast</string>
 
     <!-- Other sharing (not recording nor casting) that launched by SysUI (currently not in use) -->
     <!-- System sharing media projection permission dialog title. [CHAR LIMIT=100] -->
@@ -1858,6 +1880,8 @@
 
     <!-- Text displayed indicating that the user is connected to a satellite signal. -->
     <string name="satellite_connected_carrier_text">Satellite SOS</string>
+    <!-- Text displayed indicating that the user might be able to use satellite SOS. -->
+    <string name="satellite_emergency_only_carrier_text">Emergency calls or SOS</string>
 
     <!-- Accessibility label for managed profile icon (not shown on screen) [CHAR LIMIT=NONE] -->
     <string name="accessibility_managed_profile">Work profile</string>
@@ -3679,6 +3703,22 @@
           -->
     <string name="shortcut_helper_key_combinations_or_separator">or</string>
 
+    <!-- Keyboard touchpad tutorial scheduler-->
+    <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_keyboard_tutorial_notification_title">Navigate using your keyboard</string>
+    <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_keyboard_tutorial_notification_content">Learn keyboards shortcuts</string>
+
+    <!-- Notification title for launching touchpad tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_touchpad_tutorial_notification_title">Navigate using your touchpad</string>
+    <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_touchpad_tutorial_notification_content">Learn touchpad gestures</string>
+
+    <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_keyboard_touchpad_tutorial_notification_title">Navigate using your keyboard and touchpad</string>
+    <!-- Notification text for launching keyboard tutorial [CHAR_LIMIT=100] -->
+    <string name="launch_keyboard_touchpad_tutorial_notification_content">Learn touchpad gestures, keyboards shortcuts, and more</string>
+
     <!-- TOUCHPAD TUTORIAL-->
     <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] -->
     <string name="touchpad_tutorial_back_gesture_button">Back gesture</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7fa7088..a02c354 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -505,14 +505,14 @@
         <item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
         <item name="outline">?androidprv:attr/materialColorOutline</item>
 
-        <item name="shadeActive">@color/material_dynamic_primary90</item>
-        <item name="onShadeActive">@color/material_dynamic_primary10</item>
-        <item name="onShadeActiveVariant">@color/material_dynamic_primary30</item>
-        <item name="shadeInactive">@color/material_dynamic_neutral20</item>
-        <item name="onShadeInactive">@color/material_dynamic_neutral90</item>
-        <item name="onShadeInactiveVariant">@color/material_dynamic_neutral_variant80</item>
-        <item name="shadeDisabled">@color/shade_disabled</item>
-        <item name="underSurface">@color/material_dynamic_neutral0</item>
+        <item name="shadeActive">?androidprv:attr/customColorShadeActive</item>
+        <item name="onShadeActive">?androidprv:attr/customColorOnShadeActive</item>
+        <item name="onShadeActiveVariant">?androidprv:attr/customColorOnShadeActiveVariant</item>
+        <item name="shadeInactive">?androidprv:attr/customColorShadeInactive</item>
+        <item name="onShadeInactive">?androidprv:attr/customColorOnShadeInactive</item>
+        <item name="onShadeInactiveVariant">?androidprv:attr/customColorOnShadeInactiveVariant</item>
+        <item name="shadeDisabled">?androidprv:attr/customColorShadeDisabled</item>
+        <item name="underSurface">?androidprv:attr/customColorUnderSurface</item>
         <item name="android:itemTextAppearance">@style/Control.MenuItem</item>
     </style>
 
@@ -1455,6 +1455,12 @@
         <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryContainer</item>
     </style>
 
+    <style name="BluetoothTileDialog.AudioSharingButton" parent="Widget.Dialog.Button">
+        <item name="android:background">@drawable/audio_sharing_btn_background</item>
+        <item name="android:textColor">@color/audio_sharing_btn_text_color</item>
+        <item name="android:drawableTint">@color/audio_sharing_btn_text_color</item>
+    </style>
+
     <style name="BroadcastDialog">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/3.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/3.json
new file mode 100644
index 0000000..fe996b7
--- /dev/null
+++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/3.json
@@ -0,0 +1,81 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 3,
+    "identityHash": "02e2da2d36e6955200edd5fb49e63c72",
+    "entities": [
+      {
+        "tableName": "communal_widget_table",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1)",
+        "fields": [
+          {
+            "fieldPath": "uid",
+            "columnName": "uid",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "widgetId",
+            "columnName": "widget_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "componentName",
+            "columnName": "component_name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "itemId",
+            "columnName": "item_id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userSerialNumber",
+            "columnName": "user_serial_number",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "-1"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "uid"
+          ]
+        }
+      },
+      {
+        "tableName": "communal_item_rank_table",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)",
+        "fields": [
+          {
+            "fieldPath": "uid",
+            "columnName": "uid",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "rank",
+            "columnName": "rank",
+            "affinity": "INTEGER",
+            "notNull": true,
+            "defaultValue": "0"
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "uid"
+          ]
+        }
+      }
+    ],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '02e2da2d36e6955200edd5fb49e63c72')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index fbe1399..8f55961 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -48,7 +48,7 @@
         "src/**/*.kt",
         "src/**/*.aidl",
         ":wm_shell-aidls",
-        ":wm_shell_util-sources",
+        ":wm_shell-shared-aidls",
     ],
     static_libs: [
         "BiometricsSharedLib",
@@ -69,6 +69,7 @@
         "dagger2",
         "jsr330",
         "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
+        "//frameworks/libs/systemui:msdl",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 12d881b..870e6e6 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.biometrics
 
 import android.Manifest
+import android.app.ActivityTaskManager
 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC
 import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
@@ -35,6 +36,7 @@
 import android.hardware.biometrics.SensorPropertiesInternal
 import android.os.UserManager
 import android.util.DisplayMetrics
+import android.util.Log
 import android.view.ViewGroup
 import android.view.WindowInsets
 import android.view.WindowManager
@@ -45,6 +47,8 @@
 import com.android.systemui.biometrics.shared.model.PromptKind
 
 object Utils {
+    private const val TAG = "SysUIBiometricUtils"
+
     /** Base set of layout flags for fingerprint overlay widgets. */
     const val FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS =
         (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
@@ -148,4 +152,43 @@
         draw(canvas)
         return bitmap
     }
+
+    @JvmStatic
+    fun String.ellipsize(cutOffLength: Int) =
+        if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...")
+
+    // LINT.IfChange
+    @JvmStatic
+    /**
+     * Checks if a client package is running in the background or it's a system app.
+     *
+     * @param clientPackage The name of the package to be checked.
+     * @param clientClassNameIfItIsConfirmDeviceCredentialActivity The class name of
+     *   ConfirmDeviceCredentialActivity.
+     * @return Whether the client package is running in background
+     */
+    fun ActivityTaskManager.isSystemAppOrInBackground(
+        context: Context,
+        clientPackage: String,
+        clientClassNameIfItIsConfirmDeviceCredentialActivity: String?
+    ): Boolean {
+        Log.v(TAG, "Checking if the authenticating is in background, clientPackage:$clientPackage")
+        val tasks = getTasks(Int.MAX_VALUE)
+        if (tasks == null || tasks.isEmpty()) {
+            Log.w(TAG, "No running tasks reported")
+            return false
+        }
+
+        val topActivity = tasks[0].topActivity
+        val isSystemApp = isSystem(context, clientPackage)
+        val topPackageEqualsToClient = topActivity!!.packageName == clientPackage
+        val isClientConfirmDeviceCredentialActivity =
+            clientClassNameIfItIsConfirmDeviceCredentialActivity != null
+        // b/339532378: If it's ConfirmDeviceCredentialActivity, we need to check further on
+        // class name.
+        return !(isSystemApp || topPackageEqualsToClient) ||
+            (isClientConfirmDeviceCredentialActivity &&
+                topActivity.className != clientClassNameIfItIsConfirmDeviceCredentialActivity)
+    }
+    // LINT.ThenChange(frameworks/base/services/core/java/com/android/server/biometrics/Utils.java)
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
deleted file mode 100644
index 9999f08..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ /dev/null
@@ -1,359 +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.shared.navigationbar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import android.annotation.TargetApi;
-import android.graphics.Rect;
-import android.os.Build;
-import android.os.Handler;
-import android.view.CompositionSamplingListener;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.ViewRootImpl;
-import android.view.ViewTreeObserver;
-
-import androidx.annotation.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.util.concurrent.Executor;
-
-/**
- * A helper class to sample regions on the screen and inspect its luminosity.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
-        View.OnLayoutChangeListener {
-
-    // Luminance threshold to determine black/white contrast for the navigation affordances.
-    // Passing the threshold of this luminance value will make the button black otherwise white
-    private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f;
-    // Luminance change threshold that allows applying new value if difference was exceeded
-    private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f;
-
-    private final Handler mHandler = new Handler();
-    private final View mSampledView;
-
-    private final CompositionSamplingListener mSamplingListener;
-
-    /**
-     * The requested sampling bounds that we want to sample from
-     */
-    private final Rect mSamplingRequestBounds = new Rect();
-
-    /**
-     * The sampling bounds that are currently registered.
-     */
-    private final Rect mRegisteredSamplingBounds = new Rect();
-    private final SamplingCallback mCallback;
-    private final Executor mBackgroundExecutor;
-    private final SysuiCompositionSamplingListener mCompositionSamplingListener;
-    private boolean mSamplingEnabled = false;
-    private boolean mSamplingListenerRegistered = false;
-
-    private float mLastMedianLuma;
-    private float mCurrentMedianLuma;
-    private boolean mWaitingOnDraw;
-    private boolean mIsDestroyed;
-
-    private boolean mFirstSamplingAfterStart;
-    private boolean mWindowVisible;
-    private boolean mWindowHasBlurs;
-    private SurfaceControl mRegisteredStopLayer = null;
-    // A copy of mRegisteredStopLayer where we own the life cycle and can access from a bg thread.
-    private SurfaceControl mWrappedStopLayer = null;
-    private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() {
-        @Override
-        public void onDraw() {
-            // We need to post the remove runnable, since it's not allowed to remove in onDraw
-            mHandler.post(mRemoveDrawRunnable);
-            RegionSamplingHelper.this.onDraw();
-        }
-    };
-    private Runnable mRemoveDrawRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mSampledView.getViewTreeObserver().removeOnDrawListener(mUpdateOnDraw);
-        }
-    };
-
-    /**
-     * @deprecated Pass a main executor.
-     */
-    public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
-            Executor backgroundExecutor) {
-        this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(),
-                backgroundExecutor);
-    }
-
-    public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
-            Executor mainExecutor, Executor backgroundExecutor) {
-        this(sampledView, samplingCallback, mainExecutor,
-                backgroundExecutor, new SysuiCompositionSamplingListener());
-    }
-
-    @VisibleForTesting
-    RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
-            Executor mainExecutor, Executor backgroundExecutor,
-            SysuiCompositionSamplingListener compositionSamplingListener) {
-        mBackgroundExecutor = backgroundExecutor;
-        mCompositionSamplingListener = compositionSamplingListener;
-        mSamplingListener = new CompositionSamplingListener(mainExecutor) {
-            @Override
-            public void onSampleCollected(float medianLuma) {
-                if (mSamplingEnabled) {
-                    updateMedianLuma(medianLuma);
-                }
-            }
-        };
-        mSampledView = sampledView;
-        mSampledView.addOnAttachStateChangeListener(this);
-        mSampledView.addOnLayoutChangeListener(this);
-
-        mCallback = samplingCallback;
-    }
-
-    /**
-     * Make callback accessible
-     */
-    @VisibleForTesting
-    public SamplingCallback getCallback() {
-        return mCallback;
-    }
-
-    private void onDraw() {
-        if (mWaitingOnDraw) {
-            mWaitingOnDraw = false;
-            updateSamplingListener();
-        }
-    }
-
-    public void start(Rect initialSamplingBounds) {
-        if (!mCallback.isSamplingEnabled()) {
-            return;
-        }
-        if (initialSamplingBounds != null) {
-            mSamplingRequestBounds.set(initialSamplingBounds);
-        }
-        mSamplingEnabled = true;
-        // make sure we notify once
-        mLastMedianLuma = -1;
-        mFirstSamplingAfterStart = true;
-        updateSamplingListener();
-    }
-
-    public void stop() {
-        mSamplingEnabled = false;
-        updateSamplingListener();
-    }
-
-    public void stopAndDestroy() {
-        stop();
-        mBackgroundExecutor.execute(mSamplingListener::destroy);
-        mIsDestroyed = true;
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View view) {
-        updateSamplingListener();
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View view) {
-        stopAndDestroy();
-    }
-
-    @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        updateSamplingRect();
-    }
-
-    private void updateSamplingListener() {
-        boolean isSamplingEnabled = mSamplingEnabled
-                && !mSamplingRequestBounds.isEmpty()
-                && mWindowVisible
-                && !mWindowHasBlurs
-                && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
-        if (isSamplingEnabled) {
-            ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
-            SurfaceControl stopLayerControl = null;
-            if (viewRootImpl != null) {
-                 stopLayerControl = viewRootImpl.getSurfaceControl();
-            }
-            if (stopLayerControl == null || !stopLayerControl.isValid()) {
-                if (!mWaitingOnDraw) {
-                    mWaitingOnDraw = true;
-                    // The view might be attached but we haven't drawn yet, so wait until the
-                    // next draw to update the listener again with the stop layer, such that our
-                    // own drawing doesn't affect the sampling.
-                    if (mHandler.hasCallbacks(mRemoveDrawRunnable)) {
-                        mHandler.removeCallbacks(mRemoveDrawRunnable);
-                    } else {
-                        mSampledView.getViewTreeObserver().addOnDrawListener(mUpdateOnDraw);
-                    }
-                }
-                // If there's no valid surface, let's just sample without a stop layer, so we
-                // don't have to delay
-                stopLayerControl = null;
-            }
-            if (!mSamplingRequestBounds.equals(mRegisteredSamplingBounds)
-                    || mRegisteredStopLayer != stopLayerControl) {
-                // We only want to re-register if something actually changed
-                unregisterSamplingListener();
-                mSamplingListenerRegistered = true;
-                SurfaceControl wrappedStopLayer = wrap(stopLayerControl);
-
-                // pass this to background thread to avoid empty Rect race condition
-                final Rect boundsCopy = new Rect(mSamplingRequestBounds);
-
-                mBackgroundExecutor.execute(() -> {
-                    if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) {
-                        return;
-                    }
-                    mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
-                            wrappedStopLayer, boundsCopy);
-                });
-                mRegisteredSamplingBounds.set(mSamplingRequestBounds);
-                mRegisteredStopLayer = stopLayerControl;
-                mWrappedStopLayer = wrappedStopLayer;
-            }
-            mFirstSamplingAfterStart = false;
-        } else {
-            unregisterSamplingListener();
-        }
-    }
-
-    @VisibleForTesting
-    protected SurfaceControl wrap(SurfaceControl stopLayerControl) {
-        return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl,
-                "regionSampling");
-    }
-
-    private void unregisterSamplingListener() {
-        if (mSamplingListenerRegistered) {
-            mSamplingListenerRegistered = false;
-            SurfaceControl wrappedStopLayer = mWrappedStopLayer;
-            mRegisteredStopLayer = null;
-            mWrappedStopLayer = null;
-            mRegisteredSamplingBounds.setEmpty();
-            mBackgroundExecutor.execute(() -> {
-                mCompositionSamplingListener.unregister(mSamplingListener);
-                if (wrappedStopLayer != null && wrappedStopLayer.isValid()) {
-                    wrappedStopLayer.release();
-                }
-            });
-        }
-    }
-
-    private void updateMedianLuma(float medianLuma) {
-        mCurrentMedianLuma = medianLuma;
-
-        // If the difference between the new luma and the current luma is larger than threshold
-        // then apply the current luma, this is to prevent small changes causing colors to flicker
-        if (Math.abs(mCurrentMedianLuma - mLastMedianLuma)
-                > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) {
-            mCallback.onRegionDarknessChanged(
-                    medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */);
-            mLastMedianLuma = medianLuma;
-        }
-    }
-
-    public void updateSamplingRect() {
-        Rect sampledRegion = mCallback.getSampledRegion(mSampledView);
-        if (!mSamplingRequestBounds.equals(sampledRegion)) {
-            mSamplingRequestBounds.set(sampledRegion);
-            updateSamplingListener();
-        }
-    }
-
-    public void setWindowVisible(boolean visible) {
-        mWindowVisible = visible;
-        updateSamplingListener();
-    }
-
-    /**
-     * If we're blurring the shade window.
-     */
-    public void setWindowHasBlurs(boolean hasBlurs) {
-        mWindowHasBlurs = hasBlurs;
-        updateSamplingListener();
-    }
-
-    public void dump(PrintWriter pw) {
-        dump("", pw);
-    }
-
-    public void dump(String prefix, PrintWriter pw) {
-        pw.println(prefix + "RegionSamplingHelper:");
-        pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow());
-        pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow()
-                ? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
-                : "notAttached"));
-        pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled);
-        pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered);
-        pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds);
-        pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
-        pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma);
-        pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma);
-        pw.println(prefix + "\tmWindowVisible: " + mWindowVisible);
-        pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs);
-        pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw);
-        pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer);
-        pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer);
-        pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed);
-    }
-
-    public interface SamplingCallback {
-        /**
-         * Called when the darkness of the sampled region changes
-         * @param isRegionDark true if the sampled luminance is below the luminance threshold
-         */
-        void onRegionDarknessChanged(boolean isRegionDark);
-
-        /**
-         * Get the sampled region of interest from the sampled view
-         * @param sampledView The view that this helper is attached to for convenience
-         * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid
-         * sampling in this frame
-         */
-        Rect getSampledRegion(View sampledView);
-
-        /**
-         * @return if sampling should be enabled in the current configuration
-         */
-        default boolean isSamplingEnabled() {
-            return true;
-        }
-    }
-
-    @VisibleForTesting
-    public static class SysuiCompositionSamplingListener {
-        public void register(CompositionSamplingListener listener,
-                int displayId, SurfaceControl stopLayer, Rect samplingArea) {
-            CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea);
-        }
-
-        /**
-         * Unregisters a sampling listener.
-         */
-        public void unregister(CompositionSamplingListener listener) {
-            CompositionSamplingListener.unregister(listener);
-        }
-    }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 5d804cc..6209ed8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -172,5 +172,10 @@
      */
     oneway void onImeSwitcherLongPress() = 57;
 
-    // Next id = 58
+    /**
+     * Updates contextual education stats when target gesture type is triggered.
+     */
+    oneway void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) = 58;
+
+    // Next id = 59
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index edf855f..7ec977a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -19,8 +19,8 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
-import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
@@ -218,6 +218,7 @@
     @ViewDebug.ExportedProperty(category="recents")
     public String title;
     @ViewDebug.ExportedProperty(category="recents")
+    @Nullable
     public String titleDescription;
     @ViewDebug.ExportedProperty(category="recents")
     public int colorPrimary;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 68d2eb3..41ad437 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -26,6 +26,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
@@ -105,8 +106,8 @@
      * @return updated set of flags from InputMethodService based off {@param oldHints}
      *          Leaves original hints unmodified
      */
-    public static int calculateBackDispositionHints(int oldHints, int backDisposition,
-            boolean imeShown, boolean showImeSwitcher) {
+    public static int calculateBackDispositionHints(int oldHints,
+            @BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) {
         int hints = oldHints;
         switch (backDisposition) {
             case InputMethodService.BACK_DISPOSITION_DEFAULT:
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 317201d..f358ba2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -125,6 +125,7 @@
                 taskbarMarginLeft, taskbarMarginBottom, floatingRotationButtonPositionLeft);
 
         final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
+        mKeyButtonView.setDiameter(diameter);
         mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
                 taskbarMarginBottom));
     }
@@ -195,6 +196,7 @@
     public void updateIcon(int lightIconColor, int darkIconColor) {
         mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
                 .getDrawable(mRotationButtonController.getIconResId());
+        mAnimatedDrawable.setBounds(0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
         mKeyButtonView.setImageDrawable(mAnimatedDrawable);
         mKeyButtonView.setColors(lightIconColor, darkIconColor);
     }
@@ -248,8 +250,14 @@
             updateDimensionResources();
 
             if (mIsShowing) {
+                updateIcon(mRotationButtonController.getLightIconColor(),
+                        mRotationButtonController.getDarkIconColor());
                 final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
                 mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+                if (mAnimatedDrawable != null) {
+                    mAnimatedDrawable.reset();
+                    mAnimatedDrawable.start();
+                }
             }
         }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index 2145166..75412f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -37,6 +37,7 @@
     private static final float BACKGROUND_ALPHA = 0.92f;
 
     private KeyButtonRipple mRipple;
+    private int mDiameter;
     private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
 
     private final Configuration mLastConfiguration;
@@ -93,10 +94,25 @@
         mRipple.setDarkIntensity(darkIntensity);
     }
 
+    /**
+     * Sets the view's diameter.
+     *
+     * @param diameter the diameter value for the view
+     */
+    void setDiameter(int diameter) {
+        mDiameter = diameter;
+    }
+
     @Override
     public void draw(Canvas canvas) {
         int d = Math.min(getWidth(), getHeight());
         canvas.drawOval(0, 0, d, d, mOvalBgPaint);
         super.draw(canvas);
     }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(mDiameter, mDiameter);
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 3019fe7..ed9ba7a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -35,9 +35,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -45,9 +43,6 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
-import android.view.RemoteAnimationTarget;
 import android.window.TaskSnapshot;
 
 import com.android.internal.app.IVoiceInteractionManagerService;
@@ -55,7 +50,6 @@
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
 import java.util.List;
-import java.util.function.Consumer;
 
 public class ActivityManagerWrapper {
 
@@ -190,69 +184,13 @@
     }
 
     /**
-     * Starts the recents activity. The caller should manage the thread on which this is called.
+     * Preloads the recents activity. The caller should manage the thread on which this is called.
      */
-    public void startRecentsActivity(Intent intent, long eventTime,
-            final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
-        boolean result = startRecentsActivity(intent, eventTime, animationHandler);
-        if (resultCallback != null && resultCallbackHandler != null) {
-            resultCallbackHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    resultCallback.accept(result);
-                }
-            });
-        }
-    }
-
-    /**
-     * Starts the recents activity. The caller should manage the thread on which this is called.
-     */
-    public boolean startRecentsActivity(
-            Intent intent, long eventTime, RecentsAnimationListener animationHandler) {
+    public void preloadRecentsActivity(Intent intent) {
         try {
-            IRecentsAnimationRunner runner = null;
-            if (animationHandler != null) {
-                runner = new IRecentsAnimationRunner.Stub() {
-                    @Override
-                    public void onAnimationStart(IRecentsAnimationController controller,
-                            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
-                            Rect homeContentInsets, Rect minimizedHomeBounds,
-                            Bundle extras) {
-                        final RecentsAnimationControllerCompat controllerCompat =
-                                new RecentsAnimationControllerCompat(controller);
-                        animationHandler.onAnimationStart(controllerCompat, apps,
-                                wallpapers, homeContentInsets, minimizedHomeBounds, extras);
-                    }
-
-                    @Override
-                    public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
-                        animationHandler.onAnimationCanceled(
-                                ThumbnailData.wrap(taskIds, taskSnapshots));
-                    }
-
-                    @Override
-                    public void onTasksAppeared(RemoteAnimationTarget[] apps) {
-                        animationHandler.onTasksAppeared(apps);
-                    }
-                };
-            }
-            getService().startRecentsActivity(intent, eventTime, runner);
-            return true;
+            getService().preloadRecentsActivity(intent);
         } catch (Exception e) {
-            return false;
-        }
-    }
-
-    /**
-     * Cancels the remote recents animation started from {@link #startRecentsActivity}.
-     */
-    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
-        try {
-            getService().cancelRecentsAnimation(restoreHomeRootTaskPosition);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to cancel recents animation", e);
+            Log.w(TAG, "Failed to preload recents activity", e);
         }
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4ef1f93..121577e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -342,8 +342,7 @@
         // the keyguard)
         if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
                 || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0
-                || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0
-                || (sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) {
+                || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) {
             return false;
         }
         if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.kt
new file mode 100644
index 0000000..efa13c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AuthInteractionProperties.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.keyguard
+
+import android.os.VibrationAttributes
+import com.google.android.msdl.domain.InteractionProperties
+
+/**
+ * This class represents the set of [InteractionProperties] that only hold [VibrationAttributes] for
+ * the case of user authentication.
+ */
+data class AuthInteractionProperties(
+    override val vibrationAttributes: VibrationAttributes =
+        VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST)
+) : InteractionProperties
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 5dcf161..c1eae2e 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -477,6 +477,12 @@
         smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
     }
 
+    fun setFallbackWeatherData(data: WeatherData) {
+        if (weatherData != null) return
+        weatherData = data
+        clock?.run { events.onWeatherDataChanged(data) }
+    }
+
     /**
      * Sets this clock as showing in a secondary display.
      *
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 7733841..5e36539 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.msdlFeedback;
 
 import android.annotation.SuppressLint;
 import android.app.ActivityOptions;
@@ -46,6 +47,9 @@
 import com.android.systemui.util.EmergencyDialerConstants;
 import com.android.systemui.util.ViewController;
 
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -67,6 +71,7 @@
     private final Executor mMainExecutor;
     private final Executor mBackgroundExecutor;
     private final SelectedUserInteractor mSelectedUserInteractor;
+    private final MSDLPlayer mMSDLPlayer;
 
     private final KeyguardUpdateMonitorCallback mInfoCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -99,7 +104,8 @@
             MetricsLogger metricsLogger,
             LockPatternUtils lockPatternUtils,
             Executor mainExecutor, Executor backgroundExecutor,
-            SelectedUserInteractor selectedUserInteractor) {
+            SelectedUserInteractor selectedUserInteractor,
+            MSDLPlayer msdlPlayer) {
         super(view);
         mConfigurationController = configurationController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -112,6 +118,7 @@
         mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
         mSelectedUserInteractor = selectedUserInteractor;
+        mMSDLPlayer = msdlPlayer;
     }
 
     @Override
@@ -165,6 +172,9 @@
     @SuppressLint("MissingPermission")
     public void takeEmergencyCallAction() {
         mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL);
+        if (msdlFeedback()) {
+            mMSDLPlayer.playToken(MSDLToken.KEYPRESS_RETURN, null);
+        }
         if (mPowerManager != null) {
             mPowerManager.userActivity(SystemClock.uptimeMillis(), true);
         }
@@ -221,6 +231,7 @@
         private final Executor mMainExecutor;
         private final Executor mBackgroundExecutor;
         private final SelectedUserInteractor mSelectedUserInteractor;
+        private final MSDLPlayer mMSDLPlayer;
 
         @Inject
         public Factory(ConfigurationController configurationController,
@@ -233,7 +244,8 @@
                 LockPatternUtils lockPatternUtils,
                 @Main Executor mainExecutor,
                 @Background Executor backgroundExecutor,
-                SelectedUserInteractor selectedUserInteractor) {
+                SelectedUserInteractor selectedUserInteractor,
+                MSDLPlayer msdlPlayer) {
 
             mConfigurationController = configurationController;
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -246,6 +258,7 @@
             mMainExecutor = mainExecutor;
             mBackgroundExecutor = backgroundExecutor;
             mSelectedUserInteractor = selectedUserInteractor;
+            mMSDLPlayer = msdlPlayer;
         }
 
         /** Construct an {@link com.android.keyguard.EmergencyButtonController}. */
@@ -253,7 +266,7 @@
             return new EmergencyButtonController(view, mConfigurationController,
                     mKeyguardUpdateMonitor, mPowerManager, mActivityTaskManager, mShadeController,
                     mTelecomManager, mMetricsLogger, mLockPatternUtils, mMainExecutor,
-                    mBackgroundExecutor, mSelectedUserInteractor);
+                    mBackgroundExecutor, mSelectedUserInteractor, mMSDLPlayer);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index dad4400..28f1381 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -19,7 +19,10 @@
 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
 import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
+import static com.android.systemui.Flags.msdlFeedback;
+import static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
 
+import android.annotation.Nullable;
 import android.content.res.ColorStateList;
 import android.os.AsyncTask;
 import android.os.CountDownTimer;
@@ -40,6 +43,9 @@
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -50,11 +56,14 @@
     private final LatencyTracker mLatencyTracker;
     private final FalsingCollector mFalsingCollector;
     private final EmergencyButtonController mEmergencyButtonController;
+    private final UserActivityNotifier mUserActivityNotifier;
     private CountDownTimer mCountdownTimer;
     private boolean mDismissing;
     protected AsyncTask<?, ?, ?> mPendingLockCheck;
     protected boolean mResumed;
     protected boolean mLockedOut;
+    @Nullable
+    protected MSDLPlayer mMSDLPlayer;
 
     private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
         // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
@@ -81,7 +90,9 @@
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController,
-            FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
+            FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, securityMode, keyguardSecurityCallback, emergencyButtonController,
                 messageAreaControllerFactory, featureFlags, selectedUserInteractor);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -89,6 +100,8 @@
         mLatencyTracker = latencyTracker;
         mFalsingCollector = falsingCollector;
         mEmergencyButtonController = emergencyButtonController;
+        mMSDLPlayer = msdlPlayer;
+        mUserActivityNotifier = userActivityNotifier;
     }
 
     abstract void resetState();
@@ -178,6 +191,7 @@
     void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
         boolean dismissKeyguard = mSelectedUserInteractor.getSelectedUserId() == userId;
         if (matched) {
+            playAuthenticationHaptics(/* unlock= */true);
             getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
             if (dismissKeyguard) {
                 mDismissing = true;
@@ -185,6 +199,7 @@
                 getKeyguardSecurityCallback().dismiss(true, userId, getSecurityMode());
             }
         } else {
+            playAuthenticationHaptics(/* unlock= */false);
             mView.resetPasswordText(true /* animate */, false /* announce deletion if no match */);
             if (isValidPassword) {
                 getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
@@ -201,6 +216,18 @@
         }
     }
 
+    private void playAuthenticationHaptics(boolean unlock) {
+        if (!msdlFeedback() || mMSDLPlayer == null) return;
+
+        MSDLToken token;
+        if (unlock) {
+            token = MSDLToken.UNLOCK;
+        } else {
+            token = MSDLToken.FAILURE;
+        }
+        mMSDLPlayer.playToken(token, mAuthInteractionProperties);
+    }
+
     protected void startErrorAnimation() { /* no-op */ }
 
     protected void verifyPasswordAndUnlock() {
@@ -280,6 +307,9 @@
         getKeyguardSecurityCallback().userActivity();
         getKeyguardSecurityCallback().onUserInput();
         mMessageAreaController.setMessage("");
+        if (notifyPasswordTextViewUserActivityInBackground()) {
+            mUserActivityNotifier.notifyUserActivity();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index db14a0f..dd84bc6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -45,6 +45,9 @@
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import com.google.android.msdl.domain.InteractionProperties;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 import javax.inject.Inject;
 
 /** Controller for a {@link KeyguardSecurityView}. */
@@ -63,6 +66,8 @@
     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {};
     private final FeatureFlags mFeatureFlags;
     protected final SelectedUserInteractor mSelectedUserInteractor;
+    protected final InteractionProperties mAuthInteractionProperties =
+            new AuthInteractionProperties();
 
     protected KeyguardInputViewController(T view, SecurityMode securityMode,
             KeyguardSecurityCallback keyguardSecurityCallback,
@@ -214,6 +219,8 @@
         private final SelectedUserInteractor mSelectedUserInteractor;
         private final UiEventLogger mUiEventLogger;
         private final KeyguardKeyboardInteractor mKeyguardKeyboardInteractor;
+        private final MSDLPlayer mMSDLPlayer;
+        private final UserActivityNotifier mUserActivityNotifier;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -228,7 +235,9 @@
                 KeyguardViewController keyguardViewController,
                 FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
                 UiEventLogger uiEventLogger,
-                KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+                KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+                MSDLPlayer msdlPlayer,
+                UserActivityNotifier userActivityNotifier) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -246,6 +255,8 @@
             mSelectedUserInteractor = selectedUserInteractor;
             mUiEventLogger = uiEventLogger;
             mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
+            mMSDLPlayer = msdlPlayer;
+            mUserActivityNotifier = userActivityNotifier;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -268,29 +279,29 @@
                         mInputMethodManager, emergencyButtonController, mMainExecutor, mResources,
                         mFalsingCollector, mKeyguardViewController,
                         mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
-                        mKeyguardKeyboardInteractor);
+                        mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
             } else if (keyguardInputView instanceof KeyguardPINView) {
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
                         mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
-                        mUiEventLogger, mKeyguardKeyboardInteractor
-                );
+                        mUiEventLogger, mKeyguardKeyboardInteractor, mMSDLPlayer,
+                        mUserActivityNotifier);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
                         emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
-                        mKeyguardKeyboardInteractor);
+                        mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
                         emergencyButtonController, mFeatureFlags, mSelectedUserInteractor,
-                        mKeyguardKeyboardInteractor
+                        mKeyguardKeyboardInteractor, mMSDLPlayer, mUserActivityNotifier
                 );
             }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 490ad5c..905fa09 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -19,9 +19,13 @@
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -52,6 +56,8 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import com.google.android.msdl.domain.MSDLPlayer;
+
 import java.util.List;
 
 public class KeyguardPasswordViewController
@@ -131,10 +137,13 @@
             DevicePostureController postureController,
             FeatureFlags featureFlags,
             SelectedUserInteractor selectedUserInteractor,
-            KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+            KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
-                emergencyButtonController, featureFlags, selectedUserInteractor);
+                emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+                userActivityNotifier);
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mInputMethodManager = inputMethodManager;
         mPostureController = postureController;
@@ -170,8 +179,33 @@
         mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
         mPasswordEntry.setOnKeyListener(mKeyListener);
         mPasswordEntry.addTextChangedListener(mTextWatcher);
+
         // Poke the wakelock any time the text is selected or modified
-        mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
+        // TODO(b/362362385): Revert to the previous onClickListener implementation once this bug is
+        //  fixed.
+        mPasswordEntry.setOnClickListener(new View.OnClickListener() {
+
+            private final boolean mAutomotiveAndVisibleBackgroundUsers =
+                    isAutomotiveAndVisibleBackgroundUsers();
+
+            @Override
+            public void onClick(View v) {
+                if (mAutomotiveAndVisibleBackgroundUsers) {
+                    mInputMethodManager.restartInput(v);
+                }
+                mKeyguardSecurityCallback.userActivity();
+            }
+
+            private boolean isAutomotiveAndVisibleBackgroundUsers() {
+                final Context context = getContext();
+                return context.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_AUTOMOTIVE)
+                        && UserManager.isVisibleBackgroundUsersEnabled()
+                        && context.getResources().getBoolean(
+                        android.R.bool.config_perDisplayFocusEnabled);
+            }
+        });
+
         mSwitchImeButton.setOnClickListener(v -> {
             mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
             // Do not show auxiliary subtypes in password lock screen.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 0f61233..f575cf2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -16,9 +16,11 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.Flags.msdlFeedback;
 import static com.android.systemui.Flags.pinInputFieldStyledFocusState;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
+import android.annotation.Nullable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.StateListDrawable;
@@ -40,6 +42,9 @@
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
         extends KeyguardAbsKeyInputViewController<T> {
 
@@ -77,10 +82,13 @@
             FalsingCollector falsingCollector,
             FeatureFlags featureFlags,
             SelectedUserInteractor selectedUserInteractor,
-            KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+            KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, falsingCollector,
-                emergencyButtonController, featureFlags, selectedUserInteractor);
+                emergencyButtonController, featureFlags, selectedUserInteractor, msdlPlayer,
+                userActivityNotifier);
         mLiftToActivateListener = liftToActivateListener;
         mFalsingCollector = falsingCollector;
         mKeyguardKeyboardInteractor = keyguardKeyboardInteractor;
@@ -102,12 +110,22 @@
                 return false;
             });
             button.setAnimationEnabled(showAnimations);
+            button.setMSDLPlayer(mMSDLPlayer);
         }
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
         mPasswordEntry.setUserActivityListener(this::onUserInput);
 
         View deleteButton = mView.findViewById(R.id.delete_button);
-        deleteButton.setOnTouchListener(mActionButtonTouchListener);
+        if (msdlFeedback()) {
+            deleteButton.setOnTouchListener((View view, MotionEvent event) -> {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN && mMSDLPlayer != null) {
+                    mMSDLPlayer.playToken(MSDLToken.KEYPRESS_DELETE, null);
+                }
+                return false;
+            });
+        } else {
+            deleteButton.setOnTouchListener(mActionButtonTouchListener);
+        }
         deleteButton.setOnClickListener(v -> {
             // check for time-based lockouts
             if (mPasswordEntry.isEnabled()) {
@@ -119,13 +137,19 @@
             if (mPasswordEntry.isEnabled()) {
                 mView.resetPasswordText(true /* animate */, true /* announce */);
             }
-            mView.doHapticKeyClick();
+            if (msdlFeedback() && mMSDLPlayer != null) {
+                mMSDLPlayer.playToken(MSDLToken.LONG_PRESS, null);
+            } else {
+                mView.doHapticKeyClick();
+            }
             return true;
         });
 
         View okButton = mView.findViewById(R.id.key_enter);
         if (okButton != null) {
-            okButton.setOnTouchListener(mActionButtonTouchListener);
+            if (!msdlFeedback()) {
+                okButton.setOnTouchListener(mActionButtonTouchListener);
+            }
             okButton.setOnClickListener(v -> {
                 if (mPasswordEntry.isEnabled()) {
                     verifyPasswordAndUnlock();
@@ -177,6 +201,7 @@
 
         for (NumPadKey button : mView.getButtons()) {
             button.setOnTouchListener(null);
+            button.setMSDLPlayer(null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index f4cda02..3b5bf1a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
 
+import android.annotation.Nullable;
 import android.view.View;
 
 import com.android.internal.logging.UiEvent;
@@ -33,6 +34,8 @@
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.domain.MSDLPlayer;
+
 public class KeyguardPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardPINView> {
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -61,11 +64,13 @@
             FalsingCollector falsingCollector,
             DevicePostureController postureController, FeatureFlags featureFlags,
             SelectedUserInteractor selectedUserInteractor, UiEventLogger uiEventLogger,
-            KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+            KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor);
+                keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mPostureController = postureController;
         mLockPatternUtils = lockPatternUtils;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index bf905db..7efe2dd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -344,7 +344,7 @@
                         R.dimen.keyguard_security_container_padding_top), getPaddingRight(),
                 getPaddingBottom());
         setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.materialColorSurface));
+                com.android.internal.R.attr.materialColorSurfaceDim));
     }
 
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
@@ -808,7 +808,7 @@
     void reloadColors() {
         mViewMode.reloadColors();
         setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(),
-                com.android.internal.R.attr.materialColorSurface));
+                com.android.internal.R.attr.materialColorSurfaceDim));
     }
 
     /** Handles density or font scale changes. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index afd42cb..2d28a18 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -35,7 +35,6 @@
 
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
-import android.app.admin.flags.Flags;
 import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -81,7 +80,7 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -134,7 +133,7 @@
     private final DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private final BouncerMessageInteractor mBouncerMessageInteractor;
     private int mTranslationY;
-    private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor;
     private final DevicePolicyManager mDevicePolicyManager;
     // Whether the volume keys should be handled by keyguard. If true, then
     // they will be handled here for specific media types such as music, otherwise
@@ -321,7 +320,7 @@
             }
 
             if (KeyguardWmStateRefactor.isEnabled()) {
-                mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+                mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition(
                         "KeyguardSecurityContainerController#finish");
             }
         }
@@ -458,7 +457,7 @@
             DeviceProvisionedController deviceProvisionedController,
             FaceAuthAccessibilityDelegate faceAuthAccessibilityDelegate,
             DevicePolicyManager devicePolicyManager,
-            KeyguardTransitionInteractor keyguardTransitionInteractor,
+            KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
             Lazy<PrimaryBouncerInteractor> primaryBouncerInteractor,
             Provider<DeviceEntryInteractor> deviceEntryInteractor
     ) {
@@ -490,7 +489,7 @@
         mSelectedUserInteractor = selectedUserInteractor;
         mDeviceEntryInteractor = deviceEntryInteractor;
         mJavaAdapter = javaAdapter;
-        mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor;
         mDeviceProvisionedController = deviceProvisionedController;
         mPrimaryBouncerInteractor = primaryBouncerInteractor;
         mDevicePolicyManager = devicePolicyManager;
@@ -1140,12 +1139,7 @@
             int remainingBeforeWipe, int failedAttempts) {
         int userType = USER_TYPE_PRIMARY;
         if (expiringUserId == userId) {
-            int primaryUser = UserHandle.USER_SYSTEM;
-            if (Flags.headlessSingleUserFixes()) {
-                if (mainUserId != null) {
-                    primaryUser = mainUserId;
-                }
-            }
+            int primaryUser = mainUserId != null ? mainUserId : UserHandle.USER_SYSTEM;
             // TODO: http://b/23522538
             if (expiringUserId != primaryUser) {
                 userType = USER_TYPE_SECONDARY_USER;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 3ef3418..47fe2b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
 import android.app.Dialog;
@@ -48,6 +49,8 @@
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.domain.MSDLPlayer;
+
 public class KeyguardSimPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
     public static final String TAG = "KeyguardSimPinView";
@@ -95,11 +98,13 @@
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
             SelectedUserInteractor selectedUserInteractor,
-            KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+            KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor);
+                keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 46225c7..c688acb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -43,6 +44,8 @@
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.domain.MSDLPlayer;
+
 public class KeyguardSimPukViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -92,11 +95,13 @@
             TelephonyManager telephonyManager, FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController, FeatureFlags featureFlags,
             SelectedUserInteractor selectedUserInteractor,
-            KeyguardKeyboardInteractor keyguardKeyboardInteractor) {
+            KeyguardKeyboardInteractor keyguardKeyboardInteractor,
+            @Nullable MSDLPlayer msdlPlayer,
+            UserActivityNotifier userActivityNotifier) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor,
-                keyguardKeyboardInteractor);
+                keyguardKeyboardInteractor, msdlPlayer, userActivityNotifier);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 0da252d..f731186 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -104,6 +104,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.compose.animation.scene.ObservableTransitionState;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
@@ -119,6 +120,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -140,6 +142,9 @@
 import com.android.systemui.plugins.clocks.WeatherData;
 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.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -150,6 +155,7 @@
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import dalvik.annotation.optimization.NeverCompile;
 
@@ -279,6 +285,9 @@
     private final UserTracker mUserTracker;
     private final KeyguardUpdateMonitorLogger mLogger;
     private final boolean mIsSystemUser;
+    private final Provider<JavaAdapter> mJavaAdapter;
+    private final Provider<SceneInteractor> mSceneInteractor;
+    private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
     private final AuthController mAuthController;
     private final UiEventLogger mUiEventLogger;
     private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -474,22 +483,6 @@
     @VisibleForTesting
     SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
 
-    private static int sCurrentUser;
-
-    @Deprecated
-    public synchronized static void setCurrentUser(int currentUser) {
-        sCurrentUser = currentUser;
-    }
-
-    /**
-     * @deprecated This can potentially return unexpected values in a multi user scenario
-     * as this state is managed by another component. Consider using {@link SelectedUserInteractor}.
-     */
-    @Deprecated
-    public synchronized static int getCurrentUser() {
-        return sCurrentUser;
-    }
-
     @Override
     public void onTrustChanged(boolean enabled, boolean newlyUnlocked, int userId, int flags,
             List<String> trustGrantedMessages) {
@@ -563,7 +556,7 @@
      */
     private boolean shouldDismissKeyguardOnTrustGrantedWithCurrentUser(TrustGrantFlags flags) {
         final boolean isBouncerShowing =
-                mPrimaryBouncerIsOrWillBeShowing || mAlternateBouncerShowing;
+                isPrimaryBouncerShowingOrWillBeShowing() || isAlternateBouncerShowing();
         return (flags.isInitiatedByUser() || flags.dismissKeyguardRequested())
                 && (mDeviceInteractive || flags.temporaryAndRenewable())
                 && (isBouncerShowing || flags.dismissKeyguardRequested());
@@ -960,7 +953,7 @@
             mHandler.removeCallbacks(mFpCancelNotReceived);
         }
         try {
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+            final int userId = mSelectedUserInteractor.getSelectedUserId();
             if (userId != authUserId) {
                 mLogger.logFingerprintAuthForWrongUser(authUserId);
                 return;
@@ -1170,8 +1163,8 @@
         Assert.isMainThread();
         String reason =
                 mKeyguardBypassController.canBypass() ? "bypass"
-                        : mAlternateBouncerShowing ? "alternateBouncer"
-                                : mPrimaryBouncerFullyShown ? "bouncer"
+                        : isAlternateBouncerShowing() ? "alternateBouncer"
+                                : isPrimaryBouncerFullyShown() ? "bouncer"
                                         : "udfpsFpDown";
         requestActiveUnlock(
                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.BIOMETRIC_FAIL,
@@ -1211,7 +1204,7 @@
             mLogger.d("Aborted successful auth because device is going to sleep.");
             return;
         }
-        final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+        final int userId = mSelectedUserInteractor.getSelectedUserId();
         if (userId != authUserId) {
             mLogger.logFaceAuthForWrongUser(authUserId);
             return;
@@ -2169,7 +2162,10 @@
             Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
             TaskStackChangeListeners taskStackChangeListeners,
             SelectedUserInteractor selectedUserInteractor,
-            IActivityTaskManager activityTaskManagerService) {
+            IActivityTaskManager activityTaskManagerService,
+            Provider<AlternateBouncerInteractor> alternateBouncerInteractor,
+            Provider<JavaAdapter> javaAdapter,
+            Provider<SceneInteractor> sceneInteractor) {
         mContext = context;
         mSubscriptionManager = subscriptionManager;
         mUserTracker = userTracker;
@@ -2214,6 +2210,9 @@
 
         mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
         mIsSystemUser = mUserManager.isSystemUser();
+        mAlternateBouncerInteractor = alternateBouncerInteractor;
+        mJavaAdapter = javaAdapter;
+        mSceneInteractor = sceneInteractor;
 
         mHandler = new Handler(mainLooper) {
             @Override
@@ -2447,7 +2446,7 @@
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
 
         mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
-        int user = mSelectedUserInteractor.getSelectedUserId(true);
+        int user = mSelectedUserInteractor.getSelectedUserId();
         boolean isUserUnlocked = mUserManager.isUserUnlocked(user);
         mLogger.logUserUnlockedInitialState(user, isUserUnlocked);
         mUserIsUnlocked.put(user, isUserUnlocked);
@@ -2470,6 +2469,30 @@
         mContext.getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(Settings.System.TIME_12_24),
                 false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
+
+        if (SceneContainerFlag.isEnabled()) {
+            mJavaAdapter.get().alwaysCollectFlow(
+                    mAlternateBouncerInteractor.get().isVisible(),
+                    this::onAlternateBouncerVisibilityChange);
+            mJavaAdapter.get().alwaysCollectFlow(
+                    mSceneInteractor.get().getTransitionState(),
+                    this::onTransitionStateChanged
+            );
+        }
+    }
+
+    @VisibleForTesting
+    void onAlternateBouncerVisibilityChange(boolean isAlternateBouncerVisible) {
+        setAlternateBouncerShowing(isAlternateBouncerVisible);
+    }
+
+
+    @VisibleForTesting
+    void onTransitionStateChanged(ObservableTransitionState transitionState) {
+        int primaryBouncerFullyShown = isPrimaryBouncerFullyShown(transitionState) ? 1 : 0;
+        int primaryBouncerIsOrWillBeShowing =
+                  isPrimaryBouncerShowingOrWillBeShowing(transitionState) ? 1 : 0;
+        handlePrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing, primaryBouncerFullyShown);
     }
 
     private void initializeSimState() {
@@ -2717,8 +2740,8 @@
         requestActiveUnlock(
                 requestOrigin,
                 extraReason, canFaceBypass
-                        || mAlternateBouncerShowing
-                        || mPrimaryBouncerFullyShown
+                        || isAlternateBouncerShowing()
+                        || isPrimaryBouncerFullyShown()
                         || mAuthController.isUdfpsFingerDown());
     }
 
@@ -2739,7 +2762,7 @@
      */
     public void setAlternateBouncerShowing(boolean showing) {
         mAlternateBouncerShowing = showing;
-        if (mAlternateBouncerShowing) {
+        if (isAlternateBouncerShowing()) {
             requestActiveUnlock(
                     ActiveUnlockConfig.ActiveUnlockRequestOrigin.UNLOCK_INTENT,
                     "alternateBouncer");
@@ -2747,6 +2770,45 @@
         updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
     }
 
+    private boolean isAlternateBouncerShowing() {
+        if (SceneContainerFlag.isEnabled()) {
+            return mAlternateBouncerInteractor.get().isVisibleState();
+        } else {
+            return mAlternateBouncerShowing;
+        }
+    }
+
+    private boolean isPrimaryBouncerShowingOrWillBeShowing() {
+        if (SceneContainerFlag.isEnabled()) {
+            return isPrimaryBouncerShowingOrWillBeShowing(
+                    mSceneInteractor.get().getTransitionState().getValue());
+        } else {
+            return mPrimaryBouncerIsOrWillBeShowing;
+        }
+    }
+
+    private boolean isPrimaryBouncerFullyShown() {
+        if (SceneContainerFlag.isEnabled()) {
+            return isPrimaryBouncerFullyShown(
+                    mSceneInteractor.get().getTransitionState().getValue());
+        } else {
+            return mPrimaryBouncerFullyShown;
+        }
+    }
+
+    private boolean isPrimaryBouncerShowingOrWillBeShowing(
+            ObservableTransitionState transitionState
+    ) {
+        SceneContainerFlag.assertInNewMode();
+        return isPrimaryBouncerFullyShown(transitionState)
+                || transitionState.isTransitioning(null, Scenes.Bouncer);
+    }
+
+    private boolean isPrimaryBouncerFullyShown(ObservableTransitionState transitionState) {
+        SceneContainerFlag.assertInNewMode();
+        return transitionState.isIdle(Scenes.Bouncer);
+    }
+
     /**
      * If the current state of the device allows for triggering active unlock. This does not
      * include active unlock availability.
@@ -2762,7 +2824,7 @@
     private boolean shouldTriggerActiveUnlock(boolean shouldLog) {
         // Triggers:
         final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
-        final boolean awakeKeyguard = mPrimaryBouncerFullyShown || mAlternateBouncerShowing
+        final boolean awakeKeyguard = isPrimaryBouncerFullyShown() || isAlternateBouncerShowing()
                 || (isKeyguardVisible() && !mGoingToSleep
                 && mStatusBarState != StatusBarState.SHADE_LOCKED);
 
@@ -2830,14 +2892,14 @@
         final boolean shouldListenKeyguardState =
                 isKeyguardVisible()
                         || !mDeviceInteractive
-                        || (mPrimaryBouncerIsOrWillBeShowing && !mKeyguardGoingAway)
+                        || (isPrimaryBouncerShowingOrWillBeShowing() && !mKeyguardGoingAway)
                         || mGoingToSleep
                         || shouldListenForFingerprintAssistant
                         || (mKeyguardOccluded && mIsDreaming)
                         || (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing
                         && (mOccludingAppRequestingFp
                         || isUdfps
-                        || mAlternateBouncerShowing
+                        || isAlternateBouncerShowing()
                         || mAllowFingerprintOnCurrentOccludingActivity
                 )
             );
@@ -2856,7 +2918,7 @@
                         && !isUserInLockdown(user);
         final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
         final boolean shouldListenBouncerState =
-                !strongerAuthRequired || !mPrimaryBouncerIsOrWillBeShowing;
+                !strongerAuthRequired || !isPrimaryBouncerShowingOrWillBeShowing();
 
         final boolean shouldListenUdfpsState = !isUdfps
                 || (!userCanSkipBouncer
@@ -2872,10 +2934,10 @@
                     user,
                     shouldListen,
                     mAllowFingerprintOnCurrentOccludingActivity,
-                    mAlternateBouncerShowing,
+                    isAlternateBouncerShowing(),
                     biometricEnabledForUser,
                     mBiometricPromptShowing,
-                    mPrimaryBouncerIsOrWillBeShowing,
+                    isPrimaryBouncerShowingOrWillBeShowing(),
                     userCanSkipBouncer,
                     mCredentialAttempted,
                     mDeviceInteractive,
@@ -3614,6 +3676,7 @@
      */
     public void sendPrimaryBouncerChanged(boolean primaryBouncerIsOrWillBeShowing,
             boolean primaryBouncerFullyShown) {
+        SceneContainerFlag.assertInLegacyMode();
         mLogger.logSendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
                 primaryBouncerFullyShown);
         Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED);
@@ -3748,7 +3811,8 @@
         if (!mSimDatas.containsKey(subId)) {
             refreshSimState(subId, SubscriptionManager.getSlotIndex(subId));
         }
-        return mSimDatas.get(subId).slotId;
+        SimData simData = mSimDatas.get(subId);
+        return simData != null ? simData.slotId : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     }
 
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@@ -4002,7 +4066,7 @@
             pw.println("    " + subId + "=" + mServiceStates.get(subId));
         }
         if (isFingerprintSupported()) {
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+            final int userId = mSelectedUserInteractor.getSelectedUserId();
             final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
             BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
             pw.println("  Fingerprint state (user=" + userId + ")");
@@ -4031,10 +4095,10 @@
             if (isUdfpsSupported()) {
                 pw.println("        udfpsEnrolled=" + isUdfpsEnrolled());
                 pw.println("        shouldListenForUdfps=" + shouldListenForFingerprint(true));
-                pw.println("        mPrimaryBouncerIsOrWillBeShowing="
-                        + mPrimaryBouncerIsOrWillBeShowing);
+                pw.println("        isPrimaryBouncerShowingOrWillBeShowing="
+                        + isPrimaryBouncerShowingOrWillBeShowing());
                 pw.println("        mStatusBarState=" + StatusBarState.toString(mStatusBarState));
-                pw.println("        mAlternateBouncerShowing=" + mAlternateBouncerShowing);
+                pw.println("        isAlternateBouncerShowing=" + isAlternateBouncerShowing());
             } else if (isSfpsSupported()) {
                 pw.println("        sfpsEnrolled=" + isSfpsEnrolled());
                 pw.println("        shouldListenForSfps=" + shouldListenForFingerprint(false));
@@ -4045,7 +4109,7 @@
                     mFingerprintListenBuffer.toList()
             ).printTableData(pw);
         } else if (mFpm != null && mFingerprintSensorProperties.isEmpty()) {
-            final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+            final int userId = mSelectedUserInteractor.getSelectedUserId();
             pw.println("  Fingerprint state (user=" + userId + ")");
             pw.println("    mFingerprintSensorProperties.isEmpty="
                     + mFingerprintSensorProperties.isEmpty());
@@ -4058,7 +4122,7 @@
                     mFingerprintListenBuffer.toList()
             ).printTableData(pw);
         }
-        final int userId = mSelectedUserInteractor.getSelectedUserId(true);
+        final int userId = mSelectedUserInteractor.getSelectedUserId();
         final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
         pw.println("    authSinceBoot="
                 + getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index dcfa775..4fb80de 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -15,6 +15,7 @@
  */
 package com.android.keyguard;
 
+import static com.android.systemui.Flags.msdlFeedback;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY;
 
 import android.content.Context;
@@ -38,6 +39,9 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.res.R;
 
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.MSDLPlayer;
+
 /**
  * Viewgroup for the bouncer numpad button, specifically for digits.
  */
@@ -57,6 +61,8 @@
     @Nullable
     private NumPadAnimator mAnimator;
     private int mOrientation;
+    @Nullable
+    private MSDLPlayer mMSDLPlayer;
 
     private View.OnClickListener mListener = new View.OnClickListener() {
         @Override
@@ -221,8 +227,12 @@
 
     // Cause a VIRTUAL_KEY vibration
     public void doHapticKeyClick() {
-        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        if (msdlFeedback() && mMSDLPlayer != null) {
+            mMSDLPlayer.playToken(MSDLToken.KEYPRESS_STANDARD, null);
+        } else {
+            performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+        }
     }
 
     @Override
@@ -244,4 +254,8 @@
         super.onInitializeAccessibilityNodeInfo(info);
         info.setTextEntryKey(true);
     }
+
+    public void setMSDLPlayer(@Nullable MSDLPlayer player) {
+        mMSDLPlayer = player;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 85f8b48..0c4bc0e 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.Flags.notifyPasswordTextViewUserActivityInBackground;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -257,7 +259,9 @@
 
     @Override
     protected void onUserActivity() {
-        mPM.userActivity(SystemClock.uptimeMillis(), false);
+        if (!notifyPasswordTextViewUserActivityInBackground()) {
+            mPM.userActivity(SystemClock.uptimeMillis(), false);
+        }
         super.onUserActivity();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.kt
new file mode 100644
index 0000000..9b1ddb74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/UserActivityNotifier.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.keyguard
+
+import android.os.PowerManager
+import android.os.SystemClock
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/** Wrapper class for notifying the system about user activity in the background. */
+class UserActivityNotifier
+@Inject
+constructor(
+    @UiBackground private val uiBgExecutor: Executor,
+    private val powerManager: PowerManager
+) {
+
+    fun notifyUserActivity() {
+        uiBgExecutor.execute {
+            powerManager.userActivity(
+                SystemClock.uptimeMillis(),
+                PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                0
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 781f6dd..831543d 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -74,7 +74,6 @@
                 clockBuffers,
                 /* keepAllLoaded = */ false,
                 /* subTag = */ "System",
-                /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK),
                 new ThreadAssert());
         registry.registerListeners();
         return registry;
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
index c11cf55..7dbf013 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardQuickAffordancesLogger.kt
@@ -16,6 +16,7 @@
 
 package com.android.keyguard.logging
 
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.dagger.KeyguardQuickAffordancesLog
@@ -63,6 +64,15 @@
         )
     }
 
+    fun logUpdate(viewModel: KeyguardQuickAffordanceViewModel) {
+        buffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = viewModel.toString() },
+            { "QuickAffordance updated: $str1" }
+        )
+    }
+
     private fun String.decode(): Pair<String, String> {
         val splitUp = this.split(DELIMITER)
         return Pair(splitUp[0], splitUp[1])
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index baf8f5a..a301155 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,7 +49,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -140,7 +139,6 @@
     @Inject Lazy<SysUiState> mSysUiStateFlagsContainer;
     @Inject Lazy<CommandQueue> mCommandQueue;
     @Inject Lazy<UiEventLogger> mUiEventLogger;
-    @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
     @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
     @Inject Lazy<NotificationSectionsManager> mNotificationSectionsManagerLazy;
     @Inject Lazy<ScreenOffAnimationController> mScreenOffAnimationController;
@@ -186,7 +184,6 @@
         mProviders.put(CommandQueue.class, mCommandQueue::get);
         mProviders.put(UiEventLogger.class, mUiEventLogger::get);
         mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
-        mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
         mProviders.put(NotificationSectionsManager.class, mNotificationSectionsManagerLazy::get);
         mProviders.put(ScreenOffAnimationController.class, mScreenOffAnimationController::get);
         mProviders.put(AmbientState.class, mAmbientStateLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e055e7c..9b5d5b6e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -66,6 +66,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.Utils;
 import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
@@ -168,7 +169,7 @@
     ViewGroup mScreenDecorHwcWindow;
     @VisibleForTesting
     ScreenDecorHwcLayer mScreenDecorHwcLayer;
-    private WindowManager mWindowManager;
+    private ViewCaptureAwareWindowManager mWindowManager;
     private int mRotation;
     private UserSettingObserver mColorInversionSetting;
     @Nullable
@@ -338,7 +339,8 @@
             ScreenDecorationsLogger logger,
             FacePropertyRepository facePropertyRepository,
             JavaAdapter javaAdapter,
-            CameraProtectionLoader cameraProtectionLoader) {
+            CameraProtectionLoader cameraProtectionLoader,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         mContext = context;
         mSecureSettings = secureSettings;
         mCommandRegistry = commandRegistry;
@@ -353,6 +355,7 @@
         mLogger = logger;
         mFacePropertyRepository = facePropertyRepository;
         mJavaAdapter = javaAdapter;
+        mWindowManager = viewCaptureAwareWindowManager;
     }
 
     private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
@@ -484,7 +487,6 @@
 
     private void startOnScreenDecorationsThread() {
         Trace.beginSection("ScreenDecorations#startOnScreenDecorationsThread");
-        mWindowManager = mContext.getSystemService(WindowManager.class);
         mContext.getDisplay().getDisplayInfo(mDisplayInfo);
         mRotation = mDisplayInfo.rotation;
         mDisplaySize.x = mDisplayInfo.getNaturalWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 63ad41a..13cd2c5 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -53,6 +53,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 import com.android.wm.shell.shared.animation.PhysicsAnimator;
 import com.android.wm.shell.shared.animation.PhysicsAnimator.SpringConfig;
@@ -63,13 +64,6 @@
 public class SwipeHelper implements Gefingerpoken, Dumpable {
     static final String TAG = "com.android.systemui.SwipeHelper";
     private static final boolean DEBUG_INVALIDATE = false;
-    private static final boolean CONSTRAIN_SWIPE = true;
-    private static final boolean FADE_OUT_DURING_SWIPE = true;
-    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
-    public static final int X = 0;
-    public static final int Y = 1;
-
     private static final float SWIPE_ESCAPE_VELOCITY = 500f; // dp/sec
     private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
     private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
@@ -171,10 +165,6 @@
         mPagingTouchSlop = pagingTouchSlop;
     }
 
-    public void setDisableHardwareLayers(boolean disableHwLayers) {
-        mDisableHwLayers = disableHwLayers;
-    }
-
     private float getPos(MotionEvent ev) {
         return ev.getX();
     }
@@ -253,13 +243,14 @@
             float translation) {
         float swipeProgress = getSwipeProgressForOffset(animView, translation);
         if (!mCallback.updateSwipeProgress(animView, dismissable, swipeProgress)) {
-            if (FADE_OUT_DURING_SWIPE && dismissable) {
-                if (!mDisableHwLayers) {
-                    if (swipeProgress != 0f && swipeProgress != 1f) {
-                        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                    } else {
-                        animView.setLayerType(View.LAYER_TYPE_NONE, null);
-                    }
+            if (dismissable
+                    || (NotificationContentAlphaOptimization.isEnabled() && translation == 0)) {
+                // We need to reset the content alpha even when the view is not dismissible (eg.
+                //  when Guts is visible)
+                if (swipeProgress != 0f && swipeProgress != 1f) {
+                    animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                } else {
+                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
                 }
                 updateSwipeProgressAlpha(animView, getSwipeAlpha(swipeProgress));
             }
@@ -436,9 +427,7 @@
             duration = fixedDuration;
         }
 
-        if (!mDisableHwLayers) {
-            animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        }
+        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -493,9 +482,7 @@
                 if (endAction != null) {
                     endAction.accept(mCancelled);
                 }
-                if (!mDisableHwLayers) {
-                    animView.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
+                animView.setLayerType(View.LAYER_TYPE_NONE, null);
                 onDismissChildWithAnimationFinished();
             }
         });
@@ -612,7 +599,11 @@
      * view is being animated to dismiss or snap.
      */
     public void onTranslationUpdate(View animView, float value, boolean canBeDismissed) {
-        updateSwipeProgressFromOffset(animView, canBeDismissed, value);
+        updateSwipeProgressFromOffset(
+                animView,
+                /* dismissable= */ canBeDismissed,
+                /* translation= */ value
+        );
     }
 
     private void snapChildInstantly(final View view) {
@@ -689,7 +680,7 @@
                     } else {
                         // don't let items that can't be dismissed be dragged more than
                         // maxScrollDistance
-                        if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
+                        if (!mCallback.canChildBeDismissedInDirection(
                                 mTouchedView,
                                 delta > 0)) {
                             float size = getSize(mTouchedView);
@@ -761,8 +752,7 @@
 
     protected boolean swipedFarEnough() {
         float translation = getTranslation(mTouchedView);
-        return DISMISS_IF_SWIPED_FAR_ENOUGH
-                && Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
+        return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
                 mTouchedView);
     }
 
@@ -822,9 +812,18 @@
     }
 
     public void forceResetSwipeState(@NonNull View view) {
-        if (view.getTranslationX() == 0) return;
+        if (view.getTranslationX() == 0
+                && (!NotificationContentAlphaOptimization.isEnabled() || view.getAlpha() == 1f)
+        ) {
+            // Don't do anything when translation is 0 and alpha is 1
+            return;
+        }
         setTranslation(view, 0);
-        updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0);
+        updateSwipeProgressFromOffset(
+                view,
+                /* dismissable= */ true,
+                /* translation= */ 0
+        );
     }
 
     /** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */
@@ -893,7 +892,6 @@
         pw.append("mTranslation=").println(mTranslation);
         pw.append("mCanCurrViewBeDimissed=").println(mCanCurrViewBeDimissed);
         pw.append("mMenuRowIntercepting=").println(mMenuRowIntercepting);
-        pw.append("mDisableHwLayers=").println(mDisableHwLayers);
         pw.append("mDismissPendingMap: ").println(mDismissPendingMap.size());
         if (!mDismissPendingMap.isEmpty()) {
             mDismissPendingMap.forEach((view, animator) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
new file mode 100644
index 0000000..c944878
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserver.java
@@ -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.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.settings.UserTracker;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for tracking the current accessibility gesture list.
+ *
+ * @see Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS
+ */
+@MainThread
+@SysUISingleton
+public class AccessibilityGestureTargetsObserver extends
+        SecureSettingsContentObserver<AccessibilityGestureTargetsObserver.TargetsChangedListener> {
+
+    /** Listener for accessibility gesture targets changes. */
+    public interface TargetsChangedListener {
+
+        /**
+         * Called when accessibility gesture targets changes.
+         *
+         * @param targets Current content of {@link Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS}
+         */
+        void onAccessibilityGestureTargetsChanged(String targets);
+    }
+
+    @Inject
+    public AccessibilityGestureTargetsObserver(Context context, UserTracker userTracker) {
+        super(context, userTracker, Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS);
+    }
+
+    @Override
+    void onValueChanged(TargetsChangedListener listener, String value) {
+        listener.onAccessibilityGestureTargetsChanged(value);
+    }
+
+    /** Returns the current string from settings key
+     *  {@link Settings.Secure#ACCESSIBILITY_GESTURE_TARGETS}. */
+    @Nullable
+    public String getCurrentAccessibilityGestureTargets() {
+        return getSettingsValue();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
index 394f8dd..04afd86 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/FullscreenMagnificationController.java
@@ -408,6 +408,10 @@
         if (!isActivated()) {
             return;
         }
+        if (!(mFullscreenBorder.getBackground() instanceof GradientDrawable)) {
+            // Wear doesn't use the same magnification border background. So early return here.
+            return;
+        }
 
         float cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
         GradientDrawable backgroundDrawable = (GradientDrawable) mFullscreenBorder.getBackground();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index d81a686..f8b445b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -100,17 +100,20 @@
         private final WindowMagnifierCallback mWindowMagnifierCallback;
         private final SysUiState mSysUiState;
         private final SecureSettings mSecureSettings;
+        private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
 
         WindowMagnificationControllerSupplier(Context context, Handler handler,
                 WindowMagnifierCallback windowMagnifierCallback,
                 DisplayManager displayManager, SysUiState sysUiState,
-                SecureSettings secureSettings) {
+                SecureSettings secureSettings,
+                ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
             super(displayManager);
             mContext = context;
             mHandler = handler;
             mWindowMagnifierCallback = windowMagnifierCallback;
             mSysUiState = sysUiState;
             mSecureSettings = secureSettings;
+            mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
         }
 
         @Override
@@ -137,7 +140,8 @@
                     mSecureSettings,
                     scvhSupplier,
                     new SfVsyncFrameCallbackProvider(),
-                    WindowManagerGlobal::getWindowSession);
+                    WindowManagerGlobal::getWindowSession,
+                    mViewCaptureAwareWindowManager);
         }
     }
 
@@ -267,7 +271,7 @@
         mA11yLogger = a11yLogger;
         mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
                 mHandler, mWindowMagnifierCallback,
-                displayManager, sysUiState, secureSettings);
+                displayManager, sysUiState, secureSettings, viewCaptureAwareWindowManager);
         mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
                 context, displayManager, mHandler, mExecutor, iWindowManager);
         mMagnificationSettingsSupplier = new SettingsSupplier(context,
@@ -415,17 +419,13 @@
     @Override
     @MainThread
     public void showMagnificationButton(int displayId, int magnificationMode) {
-        if (Flags.delayShowMagnificationButton()) {
-            if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
-                return;
-            }
-            mHandler.sendMessageDelayed(
-                    mHandler.obtainMessage(
-                            MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
-                    DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
-        } else {
-            showMagnificationButtonInternal(displayId, magnificationMode);
+        if (mHandler.hasMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL)) {
+            return;
         }
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(
+                        MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL, displayId, magnificationMode),
+                DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS);
     }
 
     @MainThread
@@ -441,9 +441,7 @@
     @Override
     @MainThread
     public void removeMagnificationButton(int displayId) {
-        if (Flags.delayShowMagnificationButton()) {
-            mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
-        }
+        mHandler.removeMessages(MSG_SHOW_MAGNIFICATION_BUTTON_INTERNAL);
         mModeSwitchesController.removeButton(displayId);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 3828f9f..0883a06 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -80,6 +80,7 @@
 import androidx.annotation.UiThread;
 import androidx.core.math.MathUtils;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.accessibility.common.MagnificationConstants;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -112,6 +113,7 @@
             MagnificationConstants.SCALE_MAX_VALUE);
     private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
     private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f;
+    private static final float[] COLOR_BLACK_ARRAY = {0f, 0f, 0f};
     private final SparseArray<Float> mMagnificationSizeScaleOptions = new SparseArray<>();
 
     private final Context mContext;
@@ -125,6 +127,7 @@
     private final SurfaceControl.Transaction mTransaction;
 
     private final WindowManager mWm;
+    private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
 
     private float mScale;
     private int mSettingsButtonIndex = MagnificationSize.DEFAULT;
@@ -257,7 +260,8 @@
             SecureSettings secureSettings,
             Supplier<SurfaceControlViewHost> scvhSupplier,
             SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
-            Supplier<IWindowSession> globalWindowSessionSupplier) {
+            Supplier<IWindowSession> globalWindowSessionSupplier,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         mContext = context;
         mHandler = handler;
         mAnimationController = animationController;
@@ -279,6 +283,7 @@
 
         mWm = context.getSystemService(WindowManager.class);
         mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
+        mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
 
         mResources = mContext.getResources();
         mScale = secureSettings.getFloatForUser(
@@ -509,7 +514,7 @@
             mHandler.removeCallbacks(mMirrorViewRunnable);
             mMirrorView.removeOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
             if (!Flags.createWindowlessWindowMagnifier()) {
-                mWm.removeView(mMirrorView);
+                mViewCaptureAwareWindowManager.removeView(mMirrorView);
             }
             mMirrorView = null;
         }
@@ -721,7 +726,7 @@
             return v.onApplyWindowInsets(insets);
         });
 
-        mWm.addView(mMirrorView, params);
+        mViewCaptureAwareWindowManager.addView(mMirrorView, params);
 
         SurfaceHolder holder = mMirrorSurfaceView.getHolder();
         holder.addCallback(this);
@@ -1019,6 +1024,11 @@
         if (!mMirrorSurface.isValid()) {
             return;
         }
+        // Set the surface of the SurfaceView to black to avoid users seeing the contents below the
+        // magnifier when the mirrored surface has an alpha less than 1.
+        if (Flags.addBlackBackgroundForWindowMagnifier()) {
+            mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY);
+        }
         mTransaction.show(mMirrorSurface)
                 .reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
         modifyWindowMagnification(false);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 9b6501e..2f0ca6e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -482,6 +482,10 @@
                 } else { // mode = ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
                     mEditButton.setVisibility(View.VISIBLE);
                     mAllowDiagonalScrollingView.setVisibility(View.VISIBLE);
+                    if (Flags.saveAndRestoreMagnificationSettingsButtons()) {
+                        selectedButtonIndex =
+                                windowMagnificationFrameSizePrefs.getIndexForCurrentDensity();
+                    }
                 }
                 break;
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.kt
new file mode 100644
index 0000000..bf749d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/CaptioningRepository.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.accessibility.data.repository
+
+import android.view.accessibility.CaptioningManager
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.filterIsInstance
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+interface CaptioningRepository {
+
+    /** The system audio caption enabled state. */
+    val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
+
+    /** The system audio caption UI enabled state. */
+    val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
+
+    /** Sets [isSystemAudioCaptioningEnabled]. */
+    suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean)
+}
+
+class CaptioningRepositoryImpl(
+    private val captioningManager: CaptioningManager,
+    private val backgroundCoroutineContext: CoroutineContext,
+    coroutineScope: CoroutineScope,
+) : CaptioningRepository {
+
+    private val captioningChanges: SharedFlow<CaptioningChange> =
+        callbackFlow {
+                val listener = CaptioningChangeProducingListener(this)
+                captioningManager.addCaptioningChangeListener(listener)
+                awaitClose { captioningManager.removeCaptioningChangeListener(listener) }
+            }
+            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
+
+    override val isSystemAudioCaptioningEnabled: StateFlow<Boolean> =
+        captioningChanges
+            .filterIsInstance(CaptioningChange.IsSystemAudioCaptioningEnabled::class)
+            .map { it.isEnabled }
+            .onStart { emit(captioningManager.isSystemAudioCaptioningEnabled) }
+            .stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                captioningManager.isSystemAudioCaptioningEnabled,
+            )
+
+    override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean> =
+        captioningChanges
+            .filterIsInstance(CaptioningChange.IsSystemUICaptioningEnabled::class)
+            .map { it.isEnabled }
+            .onStart { emit(captioningManager.isSystemAudioCaptioningUiEnabled) }
+            .stateIn(
+                coroutineScope,
+                SharingStarted.WhileSubscribed(),
+                captioningManager.isSystemAudioCaptioningUiEnabled,
+            )
+
+    override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
+        withContext(backgroundCoroutineContext) {
+            captioningManager.isSystemAudioCaptioningEnabled = isEnabled
+        }
+    }
+
+    private sealed interface CaptioningChange {
+
+        data class IsSystemAudioCaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
+
+        data class IsSystemUICaptioningEnabled(val isEnabled: Boolean) : CaptioningChange
+    }
+
+    private class CaptioningChangeProducingListener(
+        private val scope: ProducerScope<CaptioningChange>
+    ) : CaptioningManager.CaptioningChangeListener() {
+
+        override fun onSystemAudioCaptioningChanged(enabled: Boolean) {
+            emitChange(CaptioningChange.IsSystemAudioCaptioningEnabled(enabled))
+        }
+
+        override fun onSystemAudioCaptioningUiChanged(enabled: Boolean) {
+            emitChange(CaptioningChange.IsSystemUICaptioningEnabled(enabled))
+        }
+
+        private fun emitChange(change: CaptioningChange) {
+            scope.launch { scope.send(change) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.kt
new file mode 100644
index 0000000..1d493c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractor.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.accessibility.domain.interactor
+
+import com.android.systemui.accessibility.data.repository.CaptioningRepository
+import kotlinx.coroutines.flow.StateFlow
+
+class CaptioningInteractor(private val repository: CaptioningRepository) {
+
+    val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
+        get() = repository.isSystemAudioCaptioningEnabled
+
+    val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
+        get() = repository.isSystemAudioCaptioningUiEnabled
+
+    suspend fun setIsSystemAudioCaptioningEnabled(enabled: Boolean) =
+        repository.setIsSystemAudioCaptioningEnabled(enabled)
+}
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 5924149..275147e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -21,14 +21,17 @@
 
 import android.content.Context;
 import android.hardware.display.DisplayManager;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.view.Display;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
 
 import androidx.annotation.MainThread;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -36,6 +39,7 @@
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -54,16 +58,21 @@
 
     private Context mContext;
     private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private final DisplayManager mDisplayManager;
     private final AccessibilityManager mAccessibilityManager;
 
     private final SecureSettings mSecureSettings;
     private final DisplayTracker mDisplayTracker;
+    private final NavigationModeController mNavigationModeController;
     @VisibleForTesting
     IAccessibilityFloatingMenu mFloatingMenu;
     private int mBtnMode;
     private String mBtnTargets;
     private boolean mIsKeyguardVisible;
+    private boolean mIsUserInInitialization;
+    @VisibleForTesting
+    Handler mHandler;
 
     @VisibleForTesting
     final KeyguardUpdateMonitorCallback mKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@@ -82,30 +91,30 @@
         @Override
         public void onUserSwitching(int userId) {
             destroyFloatingMenu();
-        }
-
-        @Override
-        public void onUserSwitchComplete(int userId) {
-            mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
-            mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
-            mBtnTargets =
-                    mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
-            handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets);
+            mIsUserInInitialization = true;
         }
     };
 
+    @VisibleForTesting
+    final UserInitializationCompleteCallback mUserInitializationCompleteCallback =
+            new UserInitializationCompleteCallback();
+
     @Inject
     public AccessibilityFloatingMenuController(Context context,
             WindowManager windowManager,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             DisplayManager displayManager,
             AccessibilityManager accessibilityManager,
             AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecureSettings secureSettings,
-            DisplayTracker displayTracker) {
+            DisplayTracker displayTracker,
+            NavigationModeController navigationModeController,
+            Handler handler) {
         mContext = context;
         mWindowManager = windowManager;
+        mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
         mDisplayManager = displayManager;
         mAccessibilityManager = accessibilityManager;
         mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
@@ -113,6 +122,8 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mSecureSettings = secureSettings;
         mDisplayTracker = displayTracker;
+        mNavigationModeController = navigationModeController;
+        mHandler = handler;
 
         mIsKeyguardVisible = false;
     }
@@ -151,6 +162,8 @@
         mAccessibilityButtonModeObserver.addListener(this);
         mAccessibilityButtonTargetsObserver.addListener(this);
         mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
+        mAccessibilityManager.registerUserInitializationCompleteCallback(
+                mUserInitializationCompleteCallback);
     }
 
     /**
@@ -164,7 +177,7 @@
      */
     private void handleFloatingMenuVisibility(boolean keyguardVisible,
             @AccessibilityButtonMode int mode, String targets) {
-        if (keyguardVisible) {
+        if (keyguardVisible || mIsUserInInitialization) {
             destroyFloatingMenu();
             return;
         }
@@ -187,7 +200,8 @@
             final Context windowContext = mContext.createWindowContext(defaultDisplay,
                     TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
             mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
-                    mAccessibilityManager, mSecureSettings);
+                    mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
+                    mNavigationModeController);
         }
 
         mFloatingMenu.show();
@@ -201,4 +215,18 @@
         mFloatingMenu.hide();
         mFloatingMenu = null;
     }
+
+    class UserInitializationCompleteCallback
+            extends IUserInitializationCompleteCallback.Stub {
+        @Override
+        public void onUserInitializationComplete(int userId) {
+            mIsUserInInitialization = false;
+            mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+            mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+            mBtnTargets =
+                    mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
+            mHandler.post(
+                    () -> handleFloatingMenuVisibility(mIsKeyguardVisible, mBtnMode, mBtnTargets));
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
index 7fd72ec..708f7f1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationController.java
@@ -30,9 +30,9 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Flags;
-import com.android.wm.shell.common.bubbles.DismissCircleView;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.shared.bubbles.DismissCircleView;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import java.util.Map;
 import java.util.Objects;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
index c1b3962..13c1a45 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -39,9 +39,9 @@
 import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
 import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
 import com.android.wm.shell.R
-import com.android.wm.shell.common.bubbles.DismissCircleView
-import com.android.wm.shell.common.bubbles.DismissView
 import com.android.wm.shell.shared.animation.PhysicsAnimator
+import com.android.wm.shell.shared.bubbles.DismissCircleView
+import com.android.wm.shell.shared.bubbles.DismissView
 
 /**
  * View that handles interactions between DismissCircleView and BubbleStackView.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 27ded74..7a674e2 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -77,11 +77,12 @@
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.Flags;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.res.R;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.wm.shell.bubbles.DismissViewUtils;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.shared.bubbles.DismissView;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -142,6 +143,8 @@
     private boolean mIsNotificationShown;
     private Optional<MenuEduTooltipView> mEduTooltipView = Optional.empty();
     private BroadcastReceiver mNotificationActionReceiver;
+    private NavigationModeController mNavigationModeController;
+    private NavigationModeController.ModeChangedListener mNavigationModeChangedListender;
 
     @IntDef({
             LayerIndex.MENU_VIEW,
@@ -220,7 +223,8 @@
             MenuViewModel menuViewModel,
             MenuViewAppearance menuViewAppearance, MenuView menuView,
             IAccessibilityFloatingMenu floatingMenu,
-            SecureSettings secureSettings) {
+            SecureSettings secureSettings,
+            NavigationModeController navigationModeController) {
         super(context);
 
         // Simplifies the translation positioning and animations
@@ -253,6 +257,8 @@
         mNotificationFactory = new MenuNotificationFactory(context);
         mNotificationManager = context.getSystemService(NotificationManager.class);
         mStatusBarManager = context.getSystemService(StatusBarManager.class);
+        mNavigationModeController = navigationModeController;
+        mNavigationModeChangedListender = (mode -> mMenuView.onPositionChanged());
 
         if (Flags.floatingMenuDragToEdit()) {
             mDragToInteractAnimationController = new DragToInteractAnimationController(
@@ -381,6 +387,7 @@
                 mMigrationTooltipObserver);
         mMessageView.setUndoListener(view -> undo());
         getContext().registerComponentCallbacks(this);
+        mNavigationModeController.addListener(mNavigationModeChangedListender);
     }
 
     @Override
@@ -396,6 +403,7 @@
                 mMigrationTooltipObserver);
         mHandler.removeCallbacksAndMessages(/* token= */ null);
         getContext().unregisterComponentCallbacks(this);
+        mNavigationModeController.removeListener(mNavigationModeChangedListender);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 6b1240b..cb96e78 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -23,6 +23,8 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.util.settings.SecureSettings;
 
 /**
@@ -30,13 +32,15 @@
  * of {@link IAccessibilityFloatingMenu}.
  */
 class MenuViewLayerController implements IAccessibilityFloatingMenu {
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final MenuViewLayer mMenuViewLayer;
     private boolean mIsShowing;
 
     MenuViewLayerController(Context context, WindowManager windowManager,
-            AccessibilityManager accessibilityManager, SecureSettings secureSettings) {
-        mWindowManager = windowManager;
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+            AccessibilityManager accessibilityManager, SecureSettings secureSettings,
+            NavigationModeController navigationModeController) {
+        mWindowManager = viewCaptureAwareWindowManager;
 
         MenuViewModel menuViewModel = new MenuViewModel(
                 context, accessibilityManager, secureSettings);
@@ -47,7 +51,8 @@
                 menuViewAppearance,
                 new MenuView(context, menuViewModel, menuViewAppearance, secureSettings),
                 this,
-                secureSettings);
+                secureSettings,
+                navigationModeController);
     }
 
     @Override
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 b6fe0df..d08653c3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -228,7 +228,7 @@
         mHearingDeviceItemList = getHearingDevicesList();
         if (mPresetsController != null) {
             activeHearingDevice = getActiveHearingDevice(mHearingDeviceItemList);
-            mPresetsController.setActiveHearingDevice(activeHearingDevice);
+            mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
         } else {
             activeHearingDevice = null;
         }
@@ -336,7 +336,7 @@
         }
         final CachedBluetoothDevice activeHearingDevice = getActiveHearingDevice(
                 mHearingDeviceItemList);
-        mPresetsController.setActiveHearingDevice(activeHearingDevice);
+        mPresetsController.setHearingDeviceIfSupportHap(activeHearingDevice);
 
         mPresetInfoAdapter = new ArrayAdapter<>(dialog.getContext(),
                 R.layout.hearing_devices_preset_spinner_selected,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
index f81124e..aa95fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesPresetsController.java
@@ -113,7 +113,7 @@
 
     @Override
     public void onPresetSelectionForGroupFailed(int hapGroupId, int reason) {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return;
         }
         if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -137,7 +137,7 @@
 
     @Override
     public void onSetPresetNameForGroupFailed(int hapGroupId, int reason) {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return;
         }
         if (hapGroupId == mHapClientProfile.getHapGroup(mActiveHearingDevice.getDevice())) {
@@ -177,22 +177,33 @@
     }
 
     /**
-     * Sets the hearing device for this controller to control the preset.
+     * Sets the hearing device for this controller to control the preset if it supports
+     * {@link HapClientProfile}.
      *
      * @param activeHearingDevice the {@link CachedBluetoothDevice} need to be hearing aid device
+     *                            and support {@link HapClientProfile}.
      */
-    public void setActiveHearingDevice(CachedBluetoothDevice activeHearingDevice) {
-        mActiveHearingDevice = activeHearingDevice;
+    public void setHearingDeviceIfSupportHap(CachedBluetoothDevice activeHearingDevice) {
+        if (mHapClientProfile == null || activeHearingDevice == null) {
+            mActiveHearingDevice = null;
+            return;
+        }
+        if (activeHearingDevice.getProfiles().stream().anyMatch(
+                profile -> profile instanceof HapClientProfile)) {
+            mActiveHearingDevice = activeHearingDevice;
+        } else {
+            mActiveHearingDevice = null;
+        }
     }
 
     /**
      * Selects the currently active preset for {@code mActiveHearingDevice} individual device or
-     * the device group accoridng to whether it supports synchronized presets or not.
+     * the device group according to whether it supports synchronized presets or not.
      *
      * @param presetIndex an index of one of the available presets
      */
     public void selectPreset(int presetIndex) {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return;
         }
         mSelectedPresetIndex = presetIndex;
@@ -217,7 +228,7 @@
      * @return a list of all known preset info
      */
     public List<BluetoothHapPresetInfo> getAllPresetInfo() {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return emptyList();
         }
         return mHapClientProfile.getAllPresetInfo(mActiveHearingDevice.getDevice()).stream().filter(
@@ -230,14 +241,14 @@
      * @return active preset index
      */
     public int getActivePresetIndex() {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
         }
         return mHapClientProfile.getActivePresetIndex(mActiveHearingDevice.getDevice());
     }
 
     private void selectPresetSynchronously(int groupId, int presetIndex) {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return;
         }
         if (DEBUG) {
@@ -250,7 +261,7 @@
     }
 
     private void selectPresetIndependently(int presetIndex) {
-        if (mActiveHearingDevice == null) {
+        if (mActiveHearingDevice == null || mHapClientProfile == null) {
             return;
         }
         if (DEBUG) {
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 03f282e..bb80396 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -120,42 +120,42 @@
     @IntoMap
     @StringKey(COLOR_CORRECTION_TILE_SPEC)
     fun provideColorCorrectionAvailabilityInteractor(
-            impl: ColorCorrectionTileDataInteractor
+        impl: ColorCorrectionTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     @Binds
     @IntoMap
     @StringKey(COLOR_INVERSION_TILE_SPEC)
     fun provideColorInversionAvailabilityInteractor(
-            impl: ColorCorrectionTileDataInteractor
+        impl: ColorCorrectionTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     @Binds
     @IntoMap
     @StringKey(FONT_SCALING_TILE_SPEC)
     fun provideFontScalingAvailabilityInteractor(
-            impl: FontScalingTileDataInteractor
+        impl: FontScalingTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     @Binds
     @IntoMap
     @StringKey(REDUCE_BRIGHTNESS_TILE_SPEC)
     fun provideReduceBrightnessAvailabilityInteractor(
-            impl: ReduceBrightColorsTileDataInteractor
+        impl: ReduceBrightColorsTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     @Binds
     @IntoMap
     @StringKey(ONE_HANDED_TILE_SPEC)
     fun provideOneHandedAvailabilityInteractor(
-            impl: OneHandedModeTileDataInteractor
+        impl: OneHandedModeTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     @Binds
     @IntoMap
     @StringKey(NIGHT_DISPLAY_TILE_SPEC)
     fun provideNightDisplayAvailabilityInteractor(
-            impl: NightDisplayTileDataInteractor
+        impl: NightDisplayTileDataInteractor
     ): QSTileAvailabilityInteractor
 
     companion object {
@@ -165,6 +165,7 @@
         const val REDUCE_BRIGHTNESS_TILE_SPEC = "reduce_brightness"
         const val ONE_HANDED_TILE_SPEC = "onehanded"
         const val NIGHT_DISPLAY_TILE_SPEC = "night"
+        const val HEARING_DEVICES_TILE_SPEC = "hearing_devices"
 
         @Provides
         @IntoMap
@@ -273,6 +274,20 @@
                 instanceId = uiEventLogger.getNewInstanceId(),
             )
 
+        @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(),
+            )
+
         /**
          * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
          * behind a flag.
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
index b0314d8..476d54b 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/dagger/AmbientModule.kt
@@ -32,5 +32,6 @@
 interface AmbientModule {
     companion object {
         const val TOUCH_HANDLERS = "touch_handlers"
+        const val LOGGING_NAME = "logging_name"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
index abdc333..04595a2 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.time.DateFormatUtil;
 
@@ -127,6 +128,9 @@
     private final DreamOverlayStatusBarItemsProvider.Callback mStatusBarItemsProviderCallback =
             this::onStatusBarItemsChanged;
 
+    private final StatusBarWindowStateListener mStatusBarWindowStateListener =
+            this::onSystemStatusBarStateChanged;
+
     @Inject
     public AmbientStatusBarViewController(
             AmbientStatusBarView view,
@@ -161,10 +165,22 @@
         mWifiInteractor = wifiInteractor;
         mCommunalSceneInteractor = communalSceneInteractor;
         mLogger = new DreamLogger(logBuffer, TAG);
+    }
+
+    @Override
+    protected void onInit() {
+        super.onInit();
 
         // Register to receive show/hide updates for the system status bar. Our custom status bar
         // needs to hide when the system status bar is showing to ovoid overlapping status bars.
-        statusBarWindowStateController.addListener(this::onSystemStatusBarStateChanged);
+        mStatusBarWindowStateController.addListener(mStatusBarWindowStateListener);
+    }
+
+    @Override
+    public void destroy() {
+        mStatusBarWindowStateController.removeListener(mStatusBarWindowStateListener);
+
+        super.destroy();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index a093f58..fd06bb1 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -29,7 +29,6 @@
 import androidx.annotation.VisibleForTesting
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.Flags
 import com.android.systemui.ambient.touch.TouchHandler.TouchSession
 import com.android.systemui.ambient.touch.dagger.BouncerSwipeModule
@@ -37,8 +36,8 @@
 import com.android.systemui.ambient.touch.scrim.ScrimManager
 import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.ShadeExpansionChangeEvent
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -63,8 +62,6 @@
     private val notificationShadeWindowController: NotificationShadeWindowController,
     private val valueAnimatorCreator: ValueAnimatorCreator,
     private val velocityTrackerFactory: VelocityTrackerFactory,
-    private val lockPatternUtils: LockPatternUtils,
-    private val userTracker: UserTracker,
     private val communalViewModel: CommunalViewModel,
     @param:Named(BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
     private val flingAnimationUtils: FlingAnimationUtils,
@@ -75,7 +72,8 @@
     @param:Named(BouncerSwipeModule.MIN_BOUNCER_ZONE_SCREEN_PERCENTAGE)
     private val minBouncerZoneScreenPercentage: Float,
     private val uiEventLogger: UiEventLogger,
-    private val activityStarter: ActivityStarter
+    private val activityStarter: ActivityStarter,
+    private val keyguardInteractor: KeyguardInteractor,
 ) : TouchHandler {
     /** An interface for creating ValueAnimators. */
     interface ValueAnimatorCreator {
@@ -148,7 +146,7 @@
 
                     // If scrolling up and keyguard is not locked, dismiss both keyguard and the
                     // dream since there's no bouncer to show.
-                    if (y > e2.y && !lockPatternUtils.isSecure(userTracker.userId)) {
+                    if (y > e2.y && keyguardInteractor.isKeyguardDismissible.value) {
                         activityStarter.executeRunnableDismissingKeyguard(
                             { centralSurfaces.get().awakenDreams() },
                             /* cancelAction= */ null,
@@ -331,8 +329,8 @@
             return
         }
 
-        // Don't set expansion if the user doesn't have a pin/password set.
-        if (!lockPatternUtils.isSecure(userTracker.userId)) {
+        // Don't set expansion if keyguard is dismissible (i.e. unlocked).
+        if (keyguardInteractor.isKeyguardDismissible.value) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 1be6f9e..0898134 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import static com.android.systemui.ambient.dagger.AmbientModule.LOGGING_NAME;
 import static com.android.systemui.shared.Flags.bouncerAreaExclusion;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
@@ -44,6 +45,9 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.Logger;
+import com.android.systemui.log.dagger.CommunalTouchLog;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.util.display.DisplayHelper;
 
@@ -62,6 +66,8 @@
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
+import javax.inject.Named;
+
 
 /**
  * {@link TouchMonitor} is responsible for monitoring touches and gestures over the
@@ -73,6 +79,7 @@
     // This executor is used to protect {@code mActiveTouchSessions} from being modified
     // concurrently. Any operation that adds or removes values should use this executor.
     public String TAG = "DreamOverlayTouchMonitor";
+    private final Logger mLogger;
     private final Executor mMainExecutor;
     private final Executor mBackgroundExecutor;
 
@@ -116,6 +123,11 @@
             TouchSessionImpl touchSessionImpl) {
         return CallbackToFutureAdapter.getFuture(completer -> {
             mMainExecutor.execute(() -> {
+                mLogger.i(msg -> "Session popped, hashCode: " + msg.getInt1(), msg -> {
+                    msg.setInt1(touchSessionImpl.hashCode());
+                    return kotlin.Unit.INSTANCE;
+                });
+
                 if (mActiveTouchSessions.remove(touchSessionImpl)) {
                     touchSessionImpl.onRemoved();
 
@@ -269,6 +281,7 @@
      * When invoked, instantiates a new {@link InputSession} to monitor touch events.
      */
     private void startMonitoring() {
+        mLogger.i("startMonitoring(): monitoring started");
         stopMonitoring(true);
 
         if (bouncerAreaExclusion()) {
@@ -322,6 +335,12 @@
         }
 
         if (!mActiveTouchSessions.isEmpty() && !force) {
+            mLogger.i(msg -> "stopMonitoring(): waiting for sessions to end: " + msg.getStr1(),
+                    msg -> {
+                        msg.setStr1(mActiveTouchSessions.stream().map(Object::hashCode).map(
+                                Object::toString).collect(Collectors.joining(",")));
+                        return kotlin.Unit.INSTANCE;
+                    });
             mStopMonitoringPending = true;
             return;
         }
@@ -341,6 +360,8 @@
         mCurrentInputSession.dispose();
         mCurrentInputSession = null;
         mStopMonitoringPending = false;
+
+        mLogger.i("stopMonitoring(): monitoring finished");
     }
 
 
@@ -405,12 +426,29 @@
                         // created so the
                         // final session is correct.
                         sessionMap.forEach((dreamTouchHandler, touchSession)
-                                -> dreamTouchHandler.onSessionStart(touchSession));
+                                -> {
+                            if (ev instanceof MotionEvent motionEvent) {
+                                int x = Math.round(motionEvent.getX());
+                                int y = Math.round(motionEvent.getY());
+                                mLogger.i(
+                                        msg -> "Session start, handler: " + msg.getStr1() + ", x: "
+                                                + msg.getLong1() + ", y: " + msg.getLong2()
+                                                + ", hashCode: " + msg.getInt1(), msg -> {
+                                            msg.setStr1(
+                                                    dreamTouchHandler.getClass().getSimpleName());
+                                            msg.setLong1(x);
+                                            msg.setLong2(y);
+                                            msg.setInt1(touchSession.hashCode());
+                                            return kotlin.Unit.INSTANCE;
+                                        });
+                            }
+                            dreamTouchHandler.onSessionStart(touchSession);
+                        });
                     }
 
                     // Find active sessions and invoke on InputEvent.
                     mActiveTouchSessions.stream()
-                            .map(touchSessionStack -> touchSessionStack.getEventListeners())
+                            .map(TouchSessionImpl::getEventListeners)
                             .flatMap(Collection::stream)
                             .forEach(inputEventListener -> inputEventListener.onInputEvent(ev));
                 }
@@ -526,6 +564,8 @@
      *                            returned.
      * @param handlers            This set represents the {@link TouchHandler} instances that will
      *                            participate in touch handling.
+     * @param loggingName         Identifying string for this {@link TouchMonitor} that will be used
+     *                            when logging to {@link CommunalTouchLog}.
      */
     @Inject
     public TouchMonitor(
@@ -537,7 +577,9 @@
             ConfigurationInteractor configurationInteractor,
             Set<TouchHandler> handlers,
             IWindowManager windowManagerService,
-            @DisplayId int displayId) {
+            @DisplayId int displayId,
+            @Named(LOGGING_NAME) String loggingName,
+            @CommunalTouchLog LogBuffer logBuffer) {
         mDisplayId = displayId;
         mHandlers = handlers;
         mInputSessionFactory = inputSessionFactory;
@@ -547,6 +589,7 @@
         mDisplayHelper = displayHelper;
         mWindowManagerService = windowManagerService;
         mConfigurationInteractor = configurationInteractor;
+        mLogger = new Logger(logBuffer, loggingName + ":TouchMonitor");
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
index 390e53b..ba552c3 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/AmbientTouchComponent.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.ambient.touch.dagger
 
 import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.ambient.dagger.AmbientModule.Companion.LOGGING_NAME
 import com.android.systemui.ambient.dagger.AmbientModule.Companion.TOUCH_HANDLERS
 import com.android.systemui.ambient.touch.TouchHandler
 import com.android.systemui.ambient.touch.TouchMonitor
@@ -36,7 +37,8 @@
             @BindsInstance lifecycleOwner: LifecycleOwner,
             @BindsInstance
             @Named(TOUCH_HANDLERS)
-            touchHandlers: Set<@JvmSuppressWildcards TouchHandler>
+            touchHandlers: Set<@JvmSuppressWildcards TouchHandler>,
+            @BindsInstance @Named(LOGGING_NAME) loggingName: String,
         ): AmbientTouchComponent
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index a5c5bec..f4e2b82 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -33,6 +33,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.app.animation.Interpolators;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.res.R;
 
 /**
@@ -40,16 +41,17 @@
  */
 public class AssistDisclosure {
     private final Context mContext;
-    private final WindowManager mWm;
+    private final ViewCaptureAwareWindowManager mWm;
     private final Handler mHandler;
 
     private AssistDisclosureView mView;
     private boolean mViewAdded;
 
-    public AssistDisclosure(Context context, Handler handler) {
+    public AssistDisclosure(Context context, Handler handler,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         mContext = context;
         mHandler = handler;
-        mWm = mContext.getSystemService(WindowManager.class);
+        mWm = viewCaptureAwareWindowManager;
     }
 
     public void postShow() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index a67dcdb..939d96e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -25,6 +25,7 @@
 import android.service.voice.VoiceInteractionSession;
 import android.util.Log;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVisualQueryDetectionAttentionListener;
 import com.android.internal.app.IVisualQueryRecognitionStatusListener;
@@ -195,12 +196,13 @@
             SecureSettings secureSettings,
             SelectedUserInteractor selectedUserInteractor,
             ActivityManager activityManager,
-            AssistInteractor interactor) {
+            AssistInteractor interactor,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         mContext = context;
         mDeviceProvisionedController = controller;
         mCommandQueue = commandQueue;
         mAssistUtils = assistUtils;
-        mAssistDisclosure = new AssistDisclosure(context, uiHandler);
+        mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager);
         mOverviewProxyService = overviewProxyService;
         mPhoneStateMonitor = phoneStateMonitor;
         mAssistLogger = assistLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
index 468737d..732a90d 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt
@@ -32,11 +32,11 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Pin
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel.Sim
 import com.android.systemui.authentication.shared.model.AuthenticationResultModel
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.onSubscriberAdded
@@ -254,7 +254,7 @@
     override val hasLockoutOccurred: StateFlow<Boolean> = _hasLockoutOccurred.asStateFlow()
 
     init {
-        if (SceneContainerFlag.isEnabled) {
+        if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
             // Hydrate failedAuthenticationAttempts initially and whenever the selected user
             // changes.
             applicationScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index fcba425..3080e19 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.authentication.domain.interactor
 
-import android.app.admin.flags.Flags
 import android.os.UserHandle
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockPatternView
@@ -289,12 +288,7 @@
     private suspend fun getWipeTarget(): WipeTarget {
         // Check which profile has the strictest policy for failed authentication attempts.
         val userToBeWiped = repository.getProfileWithMinFailedUnlockAttemptsForWipe()
-        val primaryUser =
-            if (Flags.headlessSingleUserFixes()) {
-                selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
-            } else {
-                UserHandle.USER_SYSTEM
-            }
+        val primaryUser = selectedUserInteractor.getMainUserId() ?: UserHandle.USER_SYSTEM
         return when (userToBeWiped) {
             selectedUserInteractor.getSelectedUserId() ->
                 if (userToBeWiped == primaryUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9521be1..970fdea 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,11 +17,10 @@
 package com.android.systemui.biometrics;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
-import static com.android.systemui.Flags.constraintBp;
+import static com.android.systemui.Flags.enableViewCaptureTracing;
 
 import android.animation.Animator;
 import android.annotation.IntDef;
@@ -30,8 +29,6 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.hardware.biometrics.BiometricAuthenticator.Modality;
 import android.hardware.biometrics.BiometricConstants;
@@ -41,17 +38,11 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Binder;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.UserManager;
 import android.util.Log;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -60,13 +51,14 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.ScrollView;
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedDispatcher;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import com.android.app.animation.Interpolators;
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternUtils;
@@ -74,7 +66,6 @@
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor;
 import com.android.systemui.biometrics.shared.model.BiometricModalities;
 import com.android.systemui.biometrics.shared.model.PromptKind;
-import com.android.systemui.biometrics.ui.BiometricPromptLayout;
 import com.android.systemui.biometrics.ui.CredentialView;
 import com.android.systemui.biometrics.ui.binder.BiometricViewBinder;
 import com.android.systemui.biometrics.ui.binder.BiometricViewSizeBinder;
@@ -87,6 +78,8 @@
 import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import kotlin.Lazy;
+
 import kotlinx.coroutines.CoroutineScope;
 
 import java.io.PrintWriter;
@@ -111,7 +104,6 @@
 
     private static final int ANIMATION_DURATION_SHOW_MS = 250;
     private static final int ANIMATION_DURATION_AWAY_MS = 350;
-    private static final int ANIMATE_CREDENTIAL_START_DELAY_MS = 300;
 
     private static final int STATE_UNKNOWN = 0;
     private static final int STATE_ANIMATING_IN = 1;
@@ -136,13 +128,11 @@
 
     private final Config mConfig;
     private final int mEffectiveUserId;
-    private final Handler mHandler;
     private final IBinder mWindowToken = new Binder();
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final Interpolator mLinearOutSlowIn;
     private final LockPatternUtils mLockPatternUtils;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
-    private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
     private final InteractionJankMonitor mInteractionJankMonitor;
     private final CoroutineScope mApplicationCoroutineScope;
 
@@ -159,10 +149,7 @@
     private final AuthPanelController mPanelController;
     private final ViewGroup mLayout;
     private final ImageView mBackgroundView;
-    private final ScrollView mBiometricScrollView;
     private final View mPanelView;
-    private final List<FingerprintSensorPropertiesInternal> mFpProps;
-    private final List<FaceSensorPropertiesInternal> mFaceProps;
     private final float mTranslationY;
     @VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
     private final Set<Integer> mFailedModalities = new HashSet<Integer>();
@@ -229,13 +216,7 @@
         @Override
         public void onUseDeviceCredential() {
             mConfig.mCallback.onDeviceCredentialPressed(getRequestId());
-            if (constraintBp()) {
-                addCredentialView(false /* animatePanel */, true /* animateContents */);
-            } else {
-                mHandler.postDelayed(() -> {
-                    addCredentialView(false /* animatePanel */, true /* animateContents */);
-                }, mConfig.mSkipAnimation ? 0 : ANIMATE_CREDENTIAL_START_DELAY_MS);
-            }
+            addCredentialView(false /* animatePanel */, true /* animateContents */);
 
             // TODO(b/313469218): Remove Config
             mConfig.mPromptInfo.setAuthenticators(Authenticators.DEVICE_CREDENTIAL);
@@ -303,47 +284,24 @@
             @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
             @Nullable List<FaceSensorPropertiesInternal> faceProps,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
-            @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
-            @NonNull UserManager userManager,
-            @NonNull LockPatternUtils lockPatternUtils,
-            @NonNull InteractionJankMonitor jankMonitor,
-            @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractor,
-            @NonNull PromptViewModel promptViewModel,
-            @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
-            @NonNull @Background DelayableExecutor bgExecutor,
-            @NonNull VibratorHelper vibratorHelper) {
-        this(config, applicationCoroutineScope, fpProps, faceProps,
-                wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
-                jankMonitor, promptSelectorInteractor, promptViewModel,
-                credentialViewModelProvider, new Handler(Looper.getMainLooper()), bgExecutor,
-                vibratorHelper);
-    }
-
-    @VisibleForTesting
-    AuthContainerView(@NonNull Config config,
-            @NonNull CoroutineScope applicationCoroutineScope,
-            @Nullable List<FingerprintSensorPropertiesInternal> fpProps,
-            @Nullable List<FaceSensorPropertiesInternal> faceProps,
-            @NonNull WakefulnessLifecycle wakefulnessLifecycle,
-            @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull InteractionJankMonitor jankMonitor,
             @NonNull Provider<PromptSelectorInteractor> promptSelectorInteractorProvider,
             @NonNull PromptViewModel promptViewModel,
             @NonNull Provider<CredentialViewModel> credentialViewModelProvider,
-            @NonNull Handler mainHandler,
             @NonNull @Background DelayableExecutor bgExecutor,
-            @NonNull VibratorHelper vibratorHelper) {
+            @NonNull VibratorHelper vibratorHelper,
+            Lazy<ViewCapture> lazyViewCapture) {
         super(config.mContext);
 
         mConfig = config;
         mLockPatternUtils = lockPatternUtils;
         mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId);
-        mHandler = mainHandler;
-        mWindowManager = mContext.getSystemService(WindowManager.class);
+        WindowManager wm = getContext().getSystemService(WindowManager.class);
+        mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
+                enableViewCaptureTracing());
         mWakefulnessLifecycle = wakefulnessLifecycle;
-        mPanelInteractionDetector = panelInteractionDetector;
         mApplicationCoroutineScope = applicationCoroutineScope;
 
         mPromptViewModel = promptViewModel;
@@ -352,8 +310,6 @@
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mBiometricCallback = new BiometricCallback();
 
-        mFpProps = fpProps;
-        mFaceProps = faceProps;
         final BiometricModalities biometricModalities = new BiometricModalities(
                 Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
                 Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
@@ -367,7 +323,7 @@
 
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         final PromptKind kind = mPromptViewModel.getPromptKind().getValue();
-        if (constraintBp() && kind.isBiometric()) {
+        if (kind.isBiometric()) {
             if (kind.isTwoPaneLandscapeBiometric()) {
                 mLayout = (ConstraintLayout) layoutInflater.inflate(
                         R.layout.biometric_prompt_two_pane_layout, this, false /* attachToRoot */);
@@ -379,26 +335,16 @@
             mLayout = (FrameLayout) layoutInflater.inflate(
                     R.layout.auth_container_view, this, false /* attachToRoot */);
         }
-        mBiometricScrollView = mLayout.findViewById(R.id.biometric_scrollview);
         addView(mLayout);
         mBackgroundView = mLayout.findViewById(R.id.background);
 
         mPanelView = mLayout.findViewById(R.id.panel);
-        if (!constraintBp()) {
-            final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
-                    android.R.attr.colorBackgroundFloating});
-            mPanelView.setBackgroundColor(ta.getColor(0, Color.WHITE));
-            ta.recycle();
-        }
         mPanelController = new AuthPanelController(mContext, mPanelView);
         mBackgroundExecutor = bgExecutor;
         mInteractionJankMonitor = jankMonitor;
         mCredentialViewModelProvider = credentialViewModelProvider;
 
-        showPrompt(config, layoutInflater, promptViewModel,
-                Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
-                Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds),
-                vibratorHelper);
+        showPrompt(promptViewModel, vibratorHelper);
 
         // TODO: De-dupe the logic with AuthCredentialPasswordView
         setOnKeyListener((v, keyCode, event) -> {
@@ -415,52 +361,25 @@
         requestFocus();
     }
 
-    private void showPrompt(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
-            @NonNull PromptViewModel viewModel,
-            @Nullable FingerprintSensorPropertiesInternal fpProps,
-            @Nullable FaceSensorPropertiesInternal faceProps,
-            @NonNull VibratorHelper vibratorHelper
-    ) {
+    private void showPrompt(@NonNull PromptViewModel viewModel,
+            @NonNull VibratorHelper vibratorHelper) {
         if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
-            addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
+            addBiometricView(viewModel, vibratorHelper);
         } else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
-            if (constraintBp()) {
-                addCredentialView(true, false);
-            }
+            addCredentialView(true, false);
         } else {
             mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
         }
     }
 
-    private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
-            @NonNull PromptViewModel viewModel,
-            @Nullable FingerprintSensorPropertiesInternal fpProps,
-            @Nullable FaceSensorPropertiesInternal faceProps,
+    private void addBiometricView(@NonNull PromptViewModel viewModel,
             @NonNull VibratorHelper vibratorHelper) {
-
-        if (constraintBp()) {
-            mBiometricView = BiometricViewBinder.bind(mLayout, viewModel, null,
-                    // TODO(b/201510778): This uses the wrong timeout in some cases
-                    getJankListener(mLayout, TRANSIT,
-                            BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
-                    mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
-                    vibratorHelper);
-        } else {
-            final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
-                    R.layout.biometric_prompt_layout, null, false);
-            mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
-                    // TODO(b/201510778): This uses the wrong timeout in some cases
-                    getJankListener(view, TRANSIT,
-                            BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
-                    mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
-                    vibratorHelper);
-
-            // TODO(b/251476085): migrate these dependencies
-            if (fpProps != null && fpProps.isAnyUdfpsType()) {
-                view.setUdfpsAdapter(new UdfpsDialogMeasureAdapter(view, fpProps),
-                        config.mScaleProvider);
-            }
-        }
+        mBiometricView = BiometricViewBinder.bind(mLayout, viewModel,
+                // TODO(b/201510778): This uses the wrong timeout in some cases
+                getJankListener(mLayout, TRANSIT,
+                        BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
+                mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
+                vibratorHelper);
     }
 
     @VisibleForTesting
@@ -524,9 +443,6 @@
 
     @Override
     public void onOrientationChanged() {
-        if (!constraintBp()) {
-            updatePositionByCapability(true /* invalidate */);
-        }
     }
 
     @Override
@@ -538,23 +454,6 @@
         }
 
         mWakefulnessLifecycle.addObserver(this);
-        if (constraintBp()) {
-            // Do nothing on attachment with constraintLayout
-        } else if (mPromptViewModel.getPromptKind().getValue().isBiometric()) {
-            mBiometricScrollView.addView(mBiometricView.asView());
-        } else if (mPromptViewModel.getPromptKind().getValue().isCredential()) {
-            addCredentialView(true /* animatePanel */, false /* animateContents */);
-        } else {
-            throw new IllegalStateException("Unknown configuration: "
-                    + mConfig.mPromptInfo.getAuthenticators());
-        }
-
-        if (!constraintBp()) {
-            mPanelInteractionDetector.enable(
-                    () -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
-            updatePositionByCapability(false /* invalidate */);
-        }
-
         if (mConfig.mSkipIntro) {
             mContainerState = STATE_SHOWING;
         } else {
@@ -618,120 +517,8 @@
         };
     }
 
-    private void updatePositionByCapability(boolean forceInvalidate) {
-        final FingerprintSensorPropertiesInternal fpProp = Utils.findFirstSensorProperties(
-                mFpProps, mConfig.mSensorIds);
-        final FaceSensorPropertiesInternal faceProp = Utils.findFirstSensorProperties(
-                mFaceProps, mConfig.mSensorIds);
-        if (fpProp != null && fpProp.isAnyUdfpsType()) {
-            maybeUpdatePositionForUdfps(forceInvalidate /* invalidate */);
-        }
-        if (faceProp != null && mBiometricView != null && mBiometricView.isFaceOnly()) {
-            alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
-        }
-        if (fpProp != null && fpProp.sensorType == TYPE_POWER_BUTTON) {
-            alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
-        }
-    }
-
-    private static boolean shouldUpdatePositionForUdfps(@NonNull View view) {
-        if (view instanceof BiometricPromptLayout) {
-            // this will force the prompt to align itself on the edge of the screen
-            // instead of centering (temporary workaround to prevent small implicit view
-            // from breaking due to the way gravity / margins are set in the legacy
-            // AuthPanelController
-            return true;
-        }
-
-        return false;
-    }
-
-    private boolean maybeUpdatePositionForUdfps(boolean invalidate) {
-        final Display display = getDisplay();
-        if (display == null) {
-            return false;
-        }
-
-        final DisplayInfo cachedDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(cachedDisplayInfo);
-        if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
-            return false;
-        }
-
-        final int displayRotation = cachedDisplayInfo.rotation;
-        switch (displayRotation) {
-            case Surface.ROTATION_0:
-                mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
-                setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-                break;
-
-            case Surface.ROTATION_90:
-                mPanelController.setPosition(AuthPanelController.POSITION_RIGHT);
-                setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT);
-                break;
-
-            case Surface.ROTATION_270:
-                mPanelController.setPosition(AuthPanelController.POSITION_LEFT);
-                setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
-                break;
-
-            case Surface.ROTATION_180:
-            default:
-                Log.e(TAG, "Unsupported display rotation: " + displayRotation);
-                mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
-                setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-                break;
-        }
-
-        if (invalidate) {
-            mPanelView.invalidateOutline();
-        }
-
-        return true;
-    }
-
-    private boolean alwaysUpdatePositionAtScreenBottom(boolean invalidate) {
-        final Display display = getDisplay();
-        if (display == null) {
-            return false;
-        }
-        if (mBiometricView == null || !shouldUpdatePositionForUdfps(mBiometricView.asView())) {
-            return false;
-        }
-
-        final int displayRotation = display.getRotation();
-        switch (displayRotation) {
-            case Surface.ROTATION_0:
-            case Surface.ROTATION_90:
-            case Surface.ROTATION_270:
-            case Surface.ROTATION_180:
-                mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
-                setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-                break;
-            default:
-                Log.e(TAG, "Unsupported display rotation: " + displayRotation);
-                mPanelController.setPosition(AuthPanelController.POSITION_BOTTOM);
-                setScrollViewGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
-                break;
-        }
-
-        if (invalidate) {
-            mPanelView.invalidateOutline();
-        }
-
-        return true;
-    }
-
-    private void setScrollViewGravity(int gravity) {
-        final FrameLayout.LayoutParams params =
-                (FrameLayout.LayoutParams) mBiometricScrollView.getLayoutParams();
-        params.gravity = gravity;
-        mBiometricScrollView.setLayoutParams(params);
-    }
-
     @Override
     public void onDetachedFromWindow() {
-        mPanelInteractionDetector.disable();
         OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
         if (dispatcher != null) {
             findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
@@ -834,6 +621,11 @@
     }
 
     @Override
+    public String getClassNameIfItIsConfirmDeviceCredentialActivity() {
+        return  mConfig.mPromptInfo.getClassNameIfItIsConfirmDeviceCredentialActivity();
+    }
+
+    @Override
     public long getRequestId() {
         return mConfig.mRequestId;
     }
@@ -878,7 +670,7 @@
 
         final Runnable endActionRunnable = () -> {
             setVisibility(View.INVISIBLE);
-            if (Flags.customBiometricPrompt() && constraintBp()) {
+            if (Flags.customBiometricPrompt()) {
                 // TODO(b/288175645): resetPrompt calls should be lifecycle aware
                 mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index b466f31..097ab72 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -20,9 +20,10 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
 
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
@@ -62,6 +63,7 @@
 import android.view.MotionEvent;
 import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCapture;
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
@@ -173,7 +175,6 @@
     @NonNull private final SparseBooleanArray mSfpsEnrolledForUser;
     @NonNull private final SensorPrivacyManager mSensorPrivacyManager;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
-    private final AuthDialogPanelInteractionDetector mPanelInteractionDetector;
     private boolean mAllFingerprintAuthenticatorsRegistered;
     @NonNull private final UserManager mUserManager;
     @NonNull private final LockPatternUtils mLockPatternUtils;
@@ -183,11 +184,13 @@
     private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
     @NonNull private final VibratorHelper mVibratorHelper;
 
+    private final kotlin.Lazy<ViewCapture> mLazyViewCapture;
+
     @VisibleForTesting
     final TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
-            if (!isOwnerInForeground()) {
+            if (isOwnerInBackground()) {
                 mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground);
             }
         }
@@ -227,21 +230,20 @@
         }
     }
 
-    private boolean isOwnerInForeground() {
+    private boolean isOwnerInBackground() {
         if (mCurrentDialog != null) {
             final String clientPackage = mCurrentDialog.getOpPackageName();
-            final List<ActivityManager.RunningTaskInfo> runningTasks =
-                    mActivityTaskManager.getTasks(1);
-            if (!runningTasks.isEmpty()) {
-                final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                if (!topPackage.contentEquals(clientPackage)
-                        && !Utils.isSystem(mContext, clientPackage)) {
-                    Log.w(TAG, "Evicting client due to: " + topPackage);
-                    return false;
-                }
+            final String clientClassNameIfItIsConfirmDeviceCredentialActivity =
+                    mCurrentDialog.getClassNameIfItIsConfirmDeviceCredentialActivity();
+            final boolean isInBackground = Utils.isSystemAppOrInBackground(mActivityTaskManager,
+                    mContext, clientPackage,
+                    clientClassNameIfItIsConfirmDeviceCredentialActivity);
+            if (isInBackground) {
+                Log.w(TAG, "Evicting client due to top activity is not : " + clientPackage);
             }
+            return isInBackground;
         }
-        return true;
+        return false;
     }
 
     private void cancelIfOwnerIsNotInForeground() {
@@ -728,7 +730,6 @@
             Provider<UdfpsController> udfpsControllerFactory,
             @NonNull DisplayManager displayManager,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
-            @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull Lazy<UdfpsLogger> udfpsLogger,
@@ -740,7 +741,8 @@
             @Main Handler handler,
             @Background DelayableExecutor bgExecutor,
             @NonNull UdfpsUtils udfpsUtils,
-            @NonNull VibratorHelper vibratorHelper) {
+            @NonNull VibratorHelper vibratorHelper,
+            Lazy<ViewCapture> daggerLazyViewCapture) {
         mContext = context;
         mExecution = execution;
         mUserManager = userManager;
@@ -779,7 +781,6 @@
                 });
 
         mWakefulnessLifecycle = wakefulnessLifecycle;
-        mPanelInteractionDetector = panelInteractionDetector;
 
         mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null;
 
@@ -790,6 +791,8 @@
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+
+        mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
     }
 
     // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@@ -1229,7 +1232,6 @@
                 operationId,
                 requestId,
                 mWakefulnessLifecycle,
-                mPanelInteractionDetector,
                 mUserManager,
                 mLockPatternUtils,
                 viewModel);
@@ -1259,9 +1261,9 @@
         }
         mCurrentDialog = newDialog;
 
-        // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+        // TODO(b/353597496): We should check whether |allowBackgroundAuthentication| should be
         //  removed.
-        if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) {
+        if (!promptInfo.isAllowBackgroundAuthentication() && isOwnerInBackground()) {
             cancelIfOwnerIsNotInForeground();
         } else {
             mCurrentDialog.show(mWindowManager);
@@ -1306,7 +1308,6 @@
             PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds,
             String opPackageName, boolean skipIntro, long operationId, long requestId,
             @NonNull WakefulnessLifecycle wakefulnessLifecycle,
-            @NonNull AuthDialogPanelInteractionDetector panelInteractionDetector,
             @NonNull UserManager userManager,
             @NonNull LockPatternUtils lockPatternUtils,
             @NonNull PromptViewModel viewModel) {
@@ -1323,9 +1324,10 @@
         config.mSensorIds = sensorIds;
         config.mScaleProvider = this::getScaleFactor;
         return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
-                wakefulnessLifecycle, panelInteractionDetector, userManager, lockPatternUtils,
+                wakefulnessLifecycle, userManager, lockPatternUtils,
                 mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
-                mCredentialViewModelProvider, bgExecutor, mVibratorHelper);
+                mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
+                mLazyViewCapture);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index 3fd488c..8611916 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -94,6 +94,12 @@
      */
     String getOpPackageName();
 
+    /**
+     * Get the class name of ConfirmDeviceCredentialActivity. Returns null if the direct caller is
+     * not ConfirmDeviceCredentialActivity.
+     */
+    String getClassNameIfItIsConfirmDeviceCredentialActivity();
+
     /** The requestId of the underlying operation within the framework. */
     long getRequestId();
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
deleted file mode 100644
index 04c2351..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.annotation.MainThread
-import android.util.Log
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import dagger.Lazy
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-
-class AuthDialogPanelInteractionDetector
-@Inject
-constructor(
-    @Application private val scope: CoroutineScope,
-    private val shadeInteractorLazy: Lazy<ShadeInteractor>,
-) {
-    private var shadeExpansionCollectorJob: Job? = null
-
-    @MainThread
-    fun enable(onShadeInteraction: Runnable) {
-        if (shadeExpansionCollectorJob != null) {
-            Log.e(TAG, "Already enabled")
-            return
-        }
-        //TODO(b/313957306) delete this check
-        if (shadeInteractorLazy.get().isUserInteracting.value) {
-            // Workaround for b/311266890. This flow is in an error state that breaks this.
-            Log.e(TAG, "isUserInteracting already true, skipping enable")
-            return
-        }
-        shadeExpansionCollectorJob =
-            scope.launch {
-                Log.i(TAG, "Enable detector")
-                // wait for it to emit true once
-                shadeInteractorLazy.get().isUserInteracting.first { it }
-                Log.i(TAG, "Detector detected shade interaction")
-                onShadeInteraction.run()
-            }
-        shadeExpansionCollectorJob?.invokeOnCompletion { shadeExpansionCollectorJob = null }
-    }
-
-    @MainThread
-    fun disable() {
-        Log.i(TAG, "Disable detector")
-        shadeExpansionCollectorJob?.cancel()
-    }
-}
-
-private const val TAG = "AuthDialogPanelInteractionDetector"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
index 1685f49..4731ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDebouncer.kt
@@ -25,11 +25,13 @@
  * - startWindow: Window of time on start required before showing the first help message
  * - shownFaceMessageFrequencyBoost: Frequency boost given to messages that are currently shown to
  *   the user
+ * - threshold: minimum percentage of frames a message must appear in order to show it
  */
 class FaceHelpMessageDebouncer(
     private val window: Long = DEFAULT_WINDOW_MS,
     private val startWindow: Long = window,
     private val shownFaceMessageFrequencyBoost: Int = 4,
+    private val threshold: Float = 0f,
 ) {
     private val TAG = "FaceHelpMessageDebouncer"
     private var startTime = 0L
@@ -56,7 +58,7 @@
         }
     }
 
-    private fun getMostFrequentHelpMessage(): HelpFaceAuthenticationStatus? {
+    private fun getMostFrequentHelpMessageSurpassingThreshold(): HelpFaceAuthenticationStatus? {
         // freqMap: msgId => frequency
         val freqMap = helpFaceAuthStatuses.groupingBy { it.msgId }.eachCount().toMutableMap()
 
@@ -83,7 +85,25 @@
                     }
                 }
                 ?.key
-        return helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency }
+
+        if (msgIdWithHighestFrequency == null) {
+            return null
+        }
+
+        val freq =
+            if (msgIdWithHighestFrequency == lastMessageIdShown) {
+                    freqMap[msgIdWithHighestFrequency]!! - shownFaceMessageFrequencyBoost
+                } else {
+                    freqMap[msgIdWithHighestFrequency]!!
+                }
+                .toFloat()
+
+        return if ((freq / helpFaceAuthStatuses.size.toFloat()) >= threshold) {
+            helpFaceAuthStatuses.findLast { it.msgId == msgIdWithHighestFrequency }
+        } else {
+            Log.v(TAG, "most frequent helpFaceAuthStatus didn't make the threshold: $threshold")
+            null
+        }
     }
 
     fun addMessage(helpFaceAuthStatus: HelpFaceAuthenticationStatus) {
@@ -98,14 +118,15 @@
             return null
         }
         removeOldMessages(atTimestamp)
-        val messageToShow = getMostFrequentHelpMessage()
+        val messageToShow = getMostFrequentHelpMessageSurpassingThreshold()
         if (lastMessageIdShown != messageToShow?.msgId) {
             Log.v(
                 TAG,
                 "showMessage previousLastMessageId=$lastMessageIdShown" +
                     "\n\tmessageToShow=$messageToShow " +
                     "\n\thelpFaceAuthStatusesSize=${helpFaceAuthStatuses.size}" +
-                    "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses"
+                    "\n\thelpFaceAuthStatuses=$helpFaceAuthStatuses" +
+                    "\n\tthreshold=$threshold"
             )
             lastMessageIdShown = messageToShow?.msgId
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
index 90d06fb..d382ada 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -17,14 +17,19 @@
 package com.android.systemui.biometrics
 
 import android.content.res.Resources
+import android.os.SystemClock.elapsedRealtime
 import com.android.keyguard.logging.BiometricMessageDeferralLogger
 import com.android.systemui.Dumpable
+import com.android.systemui.Flags.faceMessageDeferUpdate
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.dagger.BiometricLog
 import com.android.systemui.res.R
+import com.android.systemui.util.time.SystemClock
+import dagger.Lazy
 import java.io.PrintWriter
 import java.util.Objects
 import java.util.UUID
@@ -36,7 +41,8 @@
 constructor(
     @Main private val resources: Resources,
     @BiometricLog private val logBuffer: LogBuffer,
-    private val dumpManager: DumpManager
+    private val dumpManager: DumpManager,
+    private val systemClock: Lazy<SystemClock>,
 ) {
     fun create(): FaceHelpMessageDeferral {
         val id = UUID.randomUUID().toString()
@@ -45,6 +51,7 @@
             logBuffer = BiometricMessageDeferralLogger(logBuffer, "FaceHelpMessageDeferral[$id]"),
             dumpManager = dumpManager,
             id = id,
+            systemClock,
         )
     }
 }
@@ -58,14 +65,17 @@
     logBuffer: BiometricMessageDeferralLogger,
     dumpManager: DumpManager,
     val id: String,
+    val systemClock: Lazy<SystemClock>,
 ) :
     BiometricMessageDeferral(
         resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
         resources.getIntArray(R.array.config_face_help_msgs_ignore).toHashSet(),
         resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
+        resources.getInteger(R.integer.config_face_help_msgs_defer_analyze_timeframe).toLong(),
         logBuffer,
         dumpManager,
         id,
+        systemClock,
     )
 
 /**
@@ -77,10 +87,24 @@
     private val messagesToDefer: Set<Int>,
     private val acquiredInfoToIgnore: Set<Int>,
     private val threshold: Float,
+    private val windowToAnalyzeLastNFrames: Long,
     private val logBuffer: BiometricMessageDeferralLogger,
     dumpManager: DumpManager,
     id: String,
+    private val systemClock: Lazy<SystemClock>,
 ) : Dumpable {
+
+    private val faceHelpMessageDebouncer: FaceHelpMessageDebouncer? =
+        if (faceMessageDeferUpdate()) {
+            FaceHelpMessageDebouncer(
+                window = windowToAnalyzeLastNFrames,
+                startWindow = 0L,
+                shownFaceMessageFrequencyBoost = 0,
+                threshold = threshold,
+            )
+        } else {
+            null
+        }
     private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
     private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
     private var mostFrequentAcquiredInfoToDefer: Int? = null
@@ -97,13 +121,20 @@
         pw.println("messagesToDefer=$messagesToDefer")
         pw.println("totalFrames=$totalFrames")
         pw.println("threshold=$threshold")
+        pw.println("faceMessageDeferUpdateFlagEnabled=${faceMessageDeferUpdate()}")
+        if (faceMessageDeferUpdate()) {
+            pw.println("windowToAnalyzeLastNFrames(ms)=$windowToAnalyzeLastNFrames")
+        }
     }
 
     /** Reset all saved counts. */
     fun reset() {
         totalFrames = 0
-        mostFrequentAcquiredInfoToDefer = null
-        acquiredInfoToFrequency.clear()
+        if (!faceMessageDeferUpdate()) {
+            mostFrequentAcquiredInfoToDefer = null
+            acquiredInfoToFrequency.clear()
+        }
+
         acquiredInfoToHelpString.clear()
         logBuffer.reset()
     }
@@ -137,24 +168,48 @@
             logBuffer.logFrameIgnored(acquiredInfo)
             return
         }
-
         totalFrames++
 
-        val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
-        acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
-        if (
-            messagesToDefer.contains(acquiredInfo) &&
-                (mostFrequentAcquiredInfoToDefer == null ||
-                    newAcquiredInfoCount >
-                        acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
-        ) {
-            mostFrequentAcquiredInfoToDefer = acquiredInfo
+        if (faceMessageDeferUpdate()) {
+            faceHelpMessageDebouncer?.let {
+                val helpFaceAuthStatus =
+                    HelpFaceAuthenticationStatus(
+                        msgId = acquiredInfo,
+                        msg = null,
+                        systemClock.get().elapsedRealtime()
+                    )
+                if (totalFrames == 1) { // first frame
+                    it.startNewFaceAuthSession(helpFaceAuthStatus.createdAt)
+                }
+                it.addMessage(helpFaceAuthStatus)
+            }
+        } else {
+            val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
+            acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
+            if (
+                messagesToDefer.contains(acquiredInfo) &&
+                    (mostFrequentAcquiredInfoToDefer == null ||
+                        newAcquiredInfoCount >
+                            acquiredInfoToFrequency.getOrDefault(
+                                mostFrequentAcquiredInfoToDefer!!,
+                                0
+                            ))
+            ) {
+                mostFrequentAcquiredInfoToDefer = acquiredInfo
+            }
         }
 
         logBuffer.logFrameProcessed(
             acquiredInfo,
             totalFrames,
-            mostFrequentAcquiredInfoToDefer?.toString()
+            if (faceMessageDeferUpdate()) {
+                faceHelpMessageDebouncer
+                    ?.getMessageToShow(systemClock.get().elapsedRealtime())
+                    ?.msgId
+                    .toString()
+            } else {
+                mostFrequentAcquiredInfoToDefer?.toString()
+            }
         )
     }
 
@@ -166,9 +221,16 @@
      *   [threshold] percentage.
      */
     fun getDeferredMessage(): CharSequence? {
-        mostFrequentAcquiredInfoToDefer?.let {
-            if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
-                return acquiredInfoToHelpString[it]
+        if (faceMessageDeferUpdate()) {
+            faceHelpMessageDebouncer?.let {
+                val helpFaceAuthStatus = it.getMessageToShow(systemClock.get().elapsedRealtime())
+                return acquiredInfoToHelpString[helpFaceAuthStatus?.msgId]
+            }
+        } else {
+            mostFrequentAcquiredInfoToDefer?.let {
+                if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
+                    return acquiredInfoToHelpString[it]
+                }
             }
         }
         return null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
deleted file mode 100644
index 02eae9ced..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDialogMeasureAdapter.java
+++ /dev/null
@@ -1,386 +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.annotation.IdRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
-import android.os.Build;
-import android.util.Log;
-import android.view.Surface;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-import android.widget.FrameLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.res.R;
-
-/**
- * Adapter that remeasures an auth dialog view to ensure that it matches the location of a physical
- * under-display fingerprint sensor (UDFPS).
- */
-public class UdfpsDialogMeasureAdapter {
-    private static final String TAG = "UdfpsDialogMeasurementAdapter";
-    private static final boolean DEBUG = Build.IS_USERDEBUG || Build.IS_ENG;
-
-    @NonNull private final ViewGroup mView;
-    @NonNull private final FingerprintSensorPropertiesInternal mSensorProps;
-    @Nullable private WindowManager mWindowManager;
-    private int mBottomSpacerHeight;
-
-    public UdfpsDialogMeasureAdapter(
-            @NonNull ViewGroup view, @NonNull FingerprintSensorPropertiesInternal sensorProps) {
-        mView = view;
-        mSensorProps = sensorProps;
-        mWindowManager = mView.getContext().getSystemService(WindowManager.class);
-    }
-
-    @NonNull
-    FingerprintSensorPropertiesInternal getSensorProps() {
-        return mSensorProps;
-    }
-
-    @NonNull
-    public AuthDialog.LayoutParams onMeasureInternal(
-            int width, int height, @NonNull AuthDialog.LayoutParams layoutParams,
-            float scaleFactor) {
-
-        final int displayRotation = mView.getDisplay().getRotation();
-        switch (displayRotation) {
-            case Surface.ROTATION_0:
-                return onMeasureInternalPortrait(width, height, scaleFactor);
-            case Surface.ROTATION_90:
-            case Surface.ROTATION_270:
-                return onMeasureInternalLandscape(width, height, scaleFactor);
-            default:
-                Log.e(TAG, "Unsupported display rotation: " + displayRotation);
-                return layoutParams;
-        }
-    }
-
-    /**
-     * @return the actual (and possibly negative) bottom spacer height. If negative, this indicates
-     * that the UDFPS sensor is too low. Our current xml and custom measurement logic is very hard
-     * too cleanly support this case. So, let's have the onLayout code translate the sensor location
-     * instead.
-     */
-    public int getBottomSpacerHeight() {
-        return mBottomSpacerHeight;
-    }
-
-    /**
-     * @return sensor diameter size as scaleFactor
-     */
-    public int getSensorDiameter(float scaleFactor) {
-        return (int) (scaleFactor * mSensorProps.getLocation().sensorRadius * 2);
-    }
-
-    @NonNull
-    private AuthDialog.LayoutParams onMeasureInternalPortrait(int width, int height,
-            float scaleFactor) {
-        final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
-
-        // Figure out where the bottom of the sensor anim should be.
-        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
-        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
-        final int dialogMargin = getDialogMarginPx();
-        final int displayHeight = getMaximumWindowBounds(windowMetrics).height();
-        final Insets navbarInsets = getNavbarInsets(windowMetrics);
-        mBottomSpacerHeight = calculateBottomSpacerHeightForPortrait(
-                mSensorProps, displayHeight, textIndicatorHeight, buttonBarHeight,
-                dialogMargin, navbarInsets.bottom, scaleFactor);
-
-        // Go through each of the children and do the custom measurement.
-        int totalHeight = 0;
-        final int numChildren = mView.getChildCount();
-        final int sensorDiameter = getSensorDiameter(scaleFactor);
-        for (int i = 0; i < numChildren; i++) {
-            final View child = mView.getChildAt(i);
-            if (child.getId() == R.id.biometric_icon_frame) {
-                final FrameLayout iconFrame = (FrameLayout) child;
-                final View icon = iconFrame.getChildAt(0);
-                // Create a frame that's exactly the height of the sensor circle.
-                iconFrame.measure(
-                        MeasureSpec.makeMeasureSpec(
-                                child.getLayoutParams().width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
-
-                // Ensure that the icon is never larger than the sensor.
-                icon.measure(
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
-            } else if (child.getId() == R.id.space_above_icon
-                    || child.getId() == R.id.space_above_content
-                    || child.getId() == R.id.button_bar) {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(
-                                child.getLayoutParams().height, MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.space_below_icon) {
-                // Set the spacer height so the fingerprint icon is on the physical sensor area
-                final int clampedSpacerHeight = Math.max(mBottomSpacerHeight, 0);
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(clampedSpacerHeight, MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.description
-                    || child.getId() == R.id.customized_view_container) {
-                //skip description view and compute later
-                continue;
-            } else if (child.getId() == R.id.logo) {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
-                                MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
-                                MeasureSpec.EXACTLY));
-            } else {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            }
-
-            if (child.getVisibility() != View.GONE) {
-                totalHeight += child.getMeasuredHeight();
-            }
-        }
-
-        //re-calculate the height of body content
-        View description = mView.findViewById(R.id.description);
-        View contentView = mView.findViewById(R.id.customized_view_container);
-        if (description != null && description.getVisibility() != View.GONE) {
-            totalHeight += measureDescription(description, displayHeight, width, totalHeight);
-        } else if (contentView != null && contentView.getVisibility() != View.GONE) {
-            totalHeight += measureDescription(contentView, displayHeight, width, totalHeight);
-        }
-
-        return new AuthDialog.LayoutParams(width, totalHeight);
-    }
-
-    private int measureDescription(View bodyContent, int displayHeight, int currWidth,
-                                   int currHeight) {
-        int newHeight = bodyContent.getMeasuredHeight() + currHeight;
-        int limit = (int) (displayHeight * 0.75);
-        if (newHeight > limit) {
-            bodyContent.measure(
-                    MeasureSpec.makeMeasureSpec(currWidth, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(limit - currHeight, MeasureSpec.EXACTLY));
-        }
-        return bodyContent.getMeasuredHeight();
-    }
-
-    @NonNull
-    private AuthDialog.LayoutParams onMeasureInternalLandscape(int width, int height,
-            float scaleFactor) {
-        final WindowMetrics windowMetrics = mWindowManager.getMaximumWindowMetrics();
-
-        // Find the spacer height needed to vertically align the icon with the sensor.
-        final int titleHeight = getViewHeightPx(R.id.title);
-        final int subtitleHeight = getViewHeightPx(R.id.subtitle);
-        final int descriptionHeight = getViewHeightPx(R.id.description);
-        final int topSpacerHeight = getViewHeightPx(R.id.space_above_icon);
-        final int textIndicatorHeight = getViewHeightPx(R.id.indicator);
-        final int buttonBarHeight = getViewHeightPx(R.id.button_bar);
-
-        final Insets navbarInsets = getNavbarInsets(windowMetrics);
-        final int bottomSpacerHeight = calculateBottomSpacerHeightForLandscape(titleHeight,
-                subtitleHeight, descriptionHeight, topSpacerHeight, textIndicatorHeight,
-                buttonBarHeight, navbarInsets.bottom);
-
-        // Find the spacer width needed to horizontally align the icon with the sensor.
-        final int displayWidth = getMaximumWindowBounds(windowMetrics).width();
-        final int dialogMargin = getDialogMarginPx();
-        final int horizontalInset = navbarInsets.left + navbarInsets.right;
-        final int horizontalSpacerWidth = calculateHorizontalSpacerWidthForLandscape(
-                mSensorProps, displayWidth, dialogMargin, horizontalInset, scaleFactor);
-
-        final int sensorDiameter = getSensorDiameter(scaleFactor);
-        final int remeasuredWidth = sensorDiameter + 2 * horizontalSpacerWidth;
-
-        int remeasuredHeight = 0;
-        final int numChildren = mView.getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            final View child = mView.getChildAt(i);
-            if (child.getId() == R.id.biometric_icon_frame) {
-                final FrameLayout iconFrame = (FrameLayout) child;
-                final View icon = iconFrame.getChildAt(0);
-                // Create a frame that's exactly the height of the sensor circle.
-                iconFrame.measure(
-                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.EXACTLY));
-
-                // Ensure that the icon is never larger than the sensor.
-                icon.measure(
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST),
-                        MeasureSpec.makeMeasureSpec(sensorDiameter, MeasureSpec.AT_MOST));
-            } else if (child.getId() == R.id.space_above_icon) {
-                // Adjust the width and height of the top spacer if necessary.
-                final int newTopSpacerHeight = child.getLayoutParams().height
-                        - Math.min(bottomSpacerHeight, 0);
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(newTopSpacerHeight, MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.button_bar) {
-                // Adjust the width of the button bar while preserving its height.
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(
-                                child.getLayoutParams().height, MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.space_below_icon) {
-                // Adjust the bottom spacer height to align the fingerprint icon with the sensor.
-                final int newBottomSpacerHeight = Math.max(bottomSpacerHeight, 0);
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(newBottomSpacerHeight, MeasureSpec.EXACTLY));
-            } else {
-                // Use the remeasured width for all other child views.
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(remeasuredWidth, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            }
-
-            if (child.getVisibility() != View.GONE) {
-                remeasuredHeight += child.getMeasuredHeight();
-            }
-        }
-
-        return new AuthDialog.LayoutParams(remeasuredWidth, remeasuredHeight);
-    }
-
-    private int getViewHeightPx(@IdRes int viewId) {
-        final View view = mView.findViewById(viewId);
-        return view != null && view.getVisibility() != View.GONE ? view.getMeasuredHeight() : 0;
-    }
-
-    private int getDialogMarginPx() {
-        return mView.getResources().getDimensionPixelSize(R.dimen.biometric_dialog_border_padding);
-    }
-
-    @NonNull
-    private static Insets getNavbarInsets(@Nullable WindowMetrics windowMetrics) {
-        return windowMetrics != null
-                ? windowMetrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars())
-                : Insets.NONE;
-    }
-
-    @NonNull
-    private static Rect getMaximumWindowBounds(@Nullable WindowMetrics windowMetrics) {
-        return windowMetrics != null ? windowMetrics.getBounds() : new Rect();
-    }
-
-    /**
-     * For devices in portrait orientation where the sensor is too high up, calculates the amount of
-     * padding necessary to center the biometric icon within the sensor's physical location.
-     */
-    @VisibleForTesting
-    static int calculateBottomSpacerHeightForPortrait(
-            @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayHeightPx,
-            int textIndicatorHeightPx, int buttonBarHeightPx, int dialogMarginPx,
-            int navbarBottomInsetPx, float scaleFactor) {
-        final SensorLocationInternal location = sensorProperties.getLocation();
-        final int sensorDistanceFromBottom = displayHeightPx
-                - (int) (scaleFactor * location.sensorLocationY)
-                - (int) (scaleFactor * location.sensorRadius);
-
-        final int spacerHeight = sensorDistanceFromBottom
-                - textIndicatorHeightPx
-                - buttonBarHeightPx
-                - dialogMarginPx
-                - navbarBottomInsetPx;
-
-        if (DEBUG) {
-            Log.d(TAG, "Display height: " + displayHeightPx
-                    + ", Distance from bottom: " + sensorDistanceFromBottom
-                    + ", Bottom margin: " + dialogMarginPx
-                    + ", Navbar bottom inset: " + navbarBottomInsetPx
-                    + ", Bottom spacer height (portrait): " + spacerHeight
-                    + ", Scale Factor: " + scaleFactor);
-        }
-
-        return spacerHeight;
-    }
-
-    /**
-     * For devices in landscape orientation where the sensor is too high up, calculates the amount
-     * of padding necessary to center the biometric icon within the sensor's physical location.
-     */
-    @VisibleForTesting
-    static int calculateBottomSpacerHeightForLandscape(int titleHeightPx, int subtitleHeightPx,
-            int descriptionHeightPx, int topSpacerHeightPx, int textIndicatorHeightPx,
-            int buttonBarHeightPx, int navbarBottomInsetPx) {
-
-        final int dialogHeightAboveIcon = titleHeightPx
-                + subtitleHeightPx
-                + descriptionHeightPx
-                + topSpacerHeightPx;
-
-        final int dialogHeightBelowIcon = textIndicatorHeightPx + buttonBarHeightPx;
-
-        final int bottomSpacerHeight = dialogHeightAboveIcon
-                - dialogHeightBelowIcon
-                - navbarBottomInsetPx;
-
-        if (DEBUG) {
-            Log.d(TAG, "Title height: " + titleHeightPx
-                    + ", Subtitle height: " + subtitleHeightPx
-                    + ", Description height: " + descriptionHeightPx
-                    + ", Top spacer height: " + topSpacerHeightPx
-                    + ", Text indicator height: " + textIndicatorHeightPx
-                    + ", Button bar height: " + buttonBarHeightPx
-                    + ", Navbar bottom inset: " + navbarBottomInsetPx
-                    + ", Bottom spacer height (landscape): " + bottomSpacerHeight);
-        }
-
-        return bottomSpacerHeight;
-    }
-
-    /**
-     * For devices in landscape orientation where the sensor is too left/right, calculates the
-     * amount of padding necessary to center the biometric icon within the sensor's physical
-     * location.
-     */
-    @VisibleForTesting
-    static int calculateHorizontalSpacerWidthForLandscape(
-            @NonNull FingerprintSensorPropertiesInternal sensorProperties, int displayWidthPx,
-            int dialogMarginPx, int navbarHorizontalInsetPx, float scaleFactor) {
-        final SensorLocationInternal location = sensorProperties.getLocation();
-        final int sensorDistanceFromEdge = displayWidthPx
-                - (int) (scaleFactor * location.sensorLocationY)
-                - (int) (scaleFactor * location.sensorRadius);
-
-        final int horizontalPadding = sensorDistanceFromEdge
-                - dialogMarginPx
-                - navbarHorizontalInsetPx;
-
-        if (DEBUG) {
-            Log.d(TAG, "Display width: " + displayWidthPx
-                    + ", Distance from edge: " + sensorDistanceFromEdge
-                    + ", Dialog margin: " + dialogMarginPx
-                    + ", Navbar horizontal inset: " + navbarHorizontalInsetPx
-                    + ", Horizontal spacer width (landscape): " + horizontalPadding
-                    + ", Scale Factor: " + scaleFactor);
-        }
-
-        return horizontalPadding;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index 5e2b5ff..6da5e42 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -188,7 +188,6 @@
         val hasCredentialViewShown = promptKind.value.isCredential()
         val showBpForCredential =
             Flags.customBiometricPrompt() &&
-                com.android.systemui.Flags.constraintBp() &&
                 !Utils.isBiometricAllowed(promptInfo) &&
                 isDeviceCredentialAllowed(promptInfo) &&
                 promptInfo.contentView != null &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 348b423..695707d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -44,7 +44,7 @@
         val logoDescription: String? = info.logoDescription
         val negativeButtonText: String = info.negativeButtonText?.toString() ?: ""
         val componentNameForConfirmDeviceCredentialActivity: ComponentName? =
-            info.componentNameForConfirmDeviceCredentialActivity
+            info.realCallerForConfirmDeviceCredentialActivity
         val allowBackgroundAuthentication = info.isAllowBackgroundAuthentication
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
deleted file mode 100644
index b450896..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricPromptLayout.java
+++ /dev/null
@@ -1,188 +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.ui;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Insets;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthDialog;
-import com.android.systemui.biometrics.UdfpsDialogMeasureAdapter;
-import com.android.systemui.res.R;
-
-import kotlin.Pair;
-
-/**
- * Contains the Biometric views (title, subtitle, icon, buttons, etc.).
- *
- * TODO(b/251476085): get the udfps junk out of here, at a minimum. Likely can be replaced with a
- * normal LinearLayout.
- */
-public class BiometricPromptLayout extends LinearLayout {
-
-    private static final String TAG = "BiometricPromptLayout";
-
-    @NonNull
-    private final WindowManager mWindowManager;
-    @Nullable
-    private AuthController.ScaleFactorProvider mScaleFactorProvider;
-    @Nullable
-    private UdfpsDialogMeasureAdapter mUdfpsAdapter;
-
-    private final boolean mUseCustomBpSize;
-    private final int mCustomBpWidth;
-    private final int mCustomBpHeight;
-
-    public BiometricPromptLayout(Context context) {
-        this(context, null);
-    }
-
-    public BiometricPromptLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mWindowManager = context.getSystemService(WindowManager.class);
-
-        mUseCustomBpSize = getResources().getBoolean(R.bool.use_custom_bp_size);
-        mCustomBpWidth = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_width);
-        mCustomBpHeight = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_height);
-    }
-
-    @Deprecated
-    public void setUdfpsAdapter(@NonNull UdfpsDialogMeasureAdapter adapter,
-            @NonNull AuthController.ScaleFactorProvider scaleProvider) {
-        mUdfpsAdapter = adapter;
-        mScaleFactorProvider = scaleProvider != null ? scaleProvider : () -> 1.0f;
-    }
-
-    @Deprecated
-    public boolean isUdfps() {
-        return mUdfpsAdapter != null;
-    }
-
-    @Deprecated
-    public Pair<Integer, Integer> getUpdatedFingerprintAffordanceSize() {
-        if (mUdfpsAdapter != null) {
-            final int sensorDiameter = mUdfpsAdapter.getSensorDiameter(
-                    mScaleFactorProvider.provide());
-            return new Pair(sensorDiameter, sensorDiameter);
-        }
-        return null;
-    }
-
-    @NonNull
-    private AuthDialog.LayoutParams onMeasureInternal(int width, int height) {
-        int totalHeight = 0;
-        final int numChildren = getChildCount();
-        for (int i = 0; i < numChildren; i++) {
-            final View child = getChildAt(i);
-
-            if (child.getId() == R.id.space_above_icon
-                    || child.getId() == R.id.space_above_content
-                    || child.getId() == R.id.space_below_icon
-                    || child.getId() == R.id.button_bar) {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
-                                MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.biometric_icon_frame) {
-                final View iconView = findViewById(R.id.biometric_icon);
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().width,
-                                MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(iconView.getLayoutParams().height,
-                                MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.logo) {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().width,
-                                MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(child.getLayoutParams().height,
-                                MeasureSpec.EXACTLY));
-            } else if (child.getId() == R.id.biometric_icon) {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            } else {
-                child.measure(
-                        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            }
-
-            if (child.getVisibility() != View.GONE) {
-                totalHeight += child.getMeasuredHeight();
-            }
-        }
-
-        final AuthDialog.LayoutParams params = new AuthDialog.LayoutParams(width, totalHeight);
-        if (mUdfpsAdapter != null) {
-            return mUdfpsAdapter.onMeasureInternal(width, height, params,
-                    (mScaleFactorProvider != null) ? mScaleFactorProvider.provide() : 1.0f);
-        } else {
-            return params;
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
-
-        if (mUseCustomBpSize) {
-            width = mCustomBpWidth;
-            height = mCustomBpHeight;
-        } else {
-            width = Math.min(width, height);
-        }
-
-        // add nav bar insets since the parent AuthContainerView
-        // uses LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
-        final Insets insets = mWindowManager.getMaximumWindowMetrics().getWindowInsets()
-                .getInsets(WindowInsets.Type.navigationBars());
-        final AuthDialog.LayoutParams params = onMeasureInternal(width, height);
-        setMeasuredDimension(params.mMediumWidth + insets.left + insets.right,
-                params.mMediumHeight + insets.bottom);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-
-        if (mUdfpsAdapter != null) {
-            // Move the UDFPS icon and indicator text if necessary. This probably only needs to
-            // happen for devices where the UDFPS sensor is too low.
-            // TODO(b/201510778): Update this logic to support cases where the sensor or text
-            // overlap the button bar area.
-            final float bottomSpacerHeight = mUdfpsAdapter.getBottomSpacerHeight();
-            Log.w(TAG, "bottomSpacerHeight: " + bottomSpacerHeight);
-            if (bottomSpacerHeight < 0) {
-                final FrameLayout iconFrame = findViewById(R.id.biometric_icon_frame);
-                iconFrame.setTranslationY(-bottomSpacerHeight);
-                final TextView indicator = findViewById(R.id.indicator);
-                indicator.setTranslationY(-bottomSpacerHeight);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index 7ccac03..0ad83ec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -38,15 +38,17 @@
 import android.widget.Space
 import android.widget.TextView
 import com.android.settingslib.Utils
-import com.android.systemui.biometrics.ui.BiometricPromptLayout
+import com.android.systemui.biometrics.Utils.ellipsize
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import kotlin.math.ceil
 
 private const val TAG = "BiometricCustomizedViewBinder"
 
-/** Sub-binder for [BiometricPromptLayout.customized_view_container]. */
+/** Sub-binder for Biometric Prompt Customized View */
 object BiometricCustomizedViewBinder {
+    const val MAX_DESCRIPTION_CHARACTER_NUMBER = 225
+
     fun bind(
         customizedViewContainer: LinearLayout,
         contentView: PromptContentView?,
@@ -91,7 +93,8 @@
 
     val descriptionView = contentView.requireViewById<TextView>(R.id.customized_view_description)
     if (!description.isNullOrEmpty()) {
-        descriptionView.text = description
+        descriptionView.text =
+            description.ellipsize(BiometricCustomizedViewBinder.MAX_DESCRIPTION_CHARACTER_NUMBER)
     } else {
         descriptionView.visibility = View.GONE
     }
@@ -219,13 +222,14 @@
         inflater.inflate(R.layout.biometric_prompt_content_row_item_text_view, null) as TextView
     val lp = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1f)
     textView.layoutParams = lp
+    val maxCharNumber = PromptVerticalListContentView.getMaxEachItemCharacterNumber()
 
     when (this) {
         is PromptContentItemPlainText -> {
-            textView.text = text
+            textView.text = text.ellipsize(maxCharNumber)
         }
         is PromptContentItemBulletedText -> {
-            val bulletedText = SpannableString(text)
+            val bulletedText = SpannableString(text.ellipsize(maxCharNumber))
             val span =
                 BulletSpan(
                     getListItemBulletGapWidth(resources),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 43ba097..0b440ad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -45,17 +45,17 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
 import com.airbnb.lottie.LottieCompositionFactory
-import com.android.systemui.Flags.constraintBp
-import com.android.systemui.biometrics.AuthPanelController
+import com.android.systemui.Flags.bpIconA11y
+import com.android.systemui.biometrics.Utils.ellipsize
 import com.android.systemui.biometrics.shared.model.BiometricModalities
 import com.android.systemui.biometrics.shared.model.BiometricModality
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.biometrics.shared.model.asBiometricModality
-import com.android.systemui.biometrics.ui.BiometricPromptLayout
 import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode
 import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
 import com.android.systemui.biometrics.ui.viewmodel.PromptSize
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
+import com.android.systemui.common.ui.view.onTouchListener
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.VibratorHelper
@@ -67,33 +67,23 @@
 import kotlinx.coroutines.launch
 
 private const val TAG = "BiometricViewBinder"
-private const val MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30
 
 /** Top-most view binder for BiometricPrompt views. */
 object BiometricViewBinder {
+    const val MAX_LOGO_DESCRIPTION_CHARACTER_NUMBER = 30
 
-    /** Binds a [BiometricPromptLayout] to a [PromptViewModel]. */
+    /** Binds a Biometric Prompt View to a [PromptViewModel]. */
     @SuppressLint("ClickableViewAccessibility")
     @JvmStatic
     fun bind(
         view: View,
         viewModel: PromptViewModel,
-        panelViewController: AuthPanelController?,
         jankListener: BiometricJankListener,
         backgroundView: View,
         legacyCallback: Spaghetti.Callback,
         applicationScope: CoroutineScope,
         vibratorHelper: VibratorHelper,
     ): Spaghetti {
-        /**
-         * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
-         * accounts for iconView size, to prevent prompt resizing being visible to the user.
-         *
-         * TODO(b/288175072): May be able to remove this once constraint layout is implemented
-         */
-        if (!constraintBp()) {
-            view.visibility = View.INVISIBLE
-        }
         val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
 
         val textColorError =
@@ -104,9 +94,7 @@
                 R.style.TextAppearance_AuthCredential_Indicator,
                 intArrayOf(android.R.attr.textColor)
             )
-        val textColorHint =
-            if (constraintBp()) attributes.getColor(0, 0)
-            else view.resources.getColor(R.color.biometric_dialog_gray, view.context.theme)
+        val textColorHint = attributes.getColor(0, 0)
         attributes.recycle()
 
         val logoView = view.requireViewById<ImageView>(R.id.logo)
@@ -116,12 +104,7 @@
         val descriptionView = view.requireViewById<TextView>(R.id.description)
         val customizedViewContainer =
             view.requireViewById<LinearLayout>(R.id.customized_view_container)
-        val udfpsGuidanceView =
-            if (constraintBp()) {
-                view.requireViewById<View>(R.id.panel)
-            } else {
-                backgroundView
-            }
+        val udfpsGuidanceView = view.requireViewById<View>(R.id.panel)
 
         // set selected to enable marquee unless a screen reader is enabled
         titleView.isSelected =
@@ -130,14 +113,6 @@
             !accessibilityManager.isEnabled || !accessibilityManager.isTouchExplorationEnabled
 
         val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
-
-        val iconSizeOverride =
-            if (constraintBp()) {
-                null
-            } else {
-                (view as BiometricPromptLayout).updatedFingerprintAffordanceSize
-            }
-
         val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator)
 
         // Negative-side (left) buttons
@@ -213,7 +188,7 @@
             subtitleView.text = viewModel.subtitle.first()
             descriptionView.text = viewModel.description.first()
 
-            if (Flags.customBiometricPrompt() && constraintBp()) {
+            if (Flags.customBiometricPrompt()) {
                 BiometricCustomizedViewBinder.bind(
                     customizedViewContainer,
                     viewModel.contentView.first(),
@@ -250,22 +225,6 @@
                             descriptionView,
                             customizedViewContainer,
                         ),
-                    viewsToFadeInOnSizeChange =
-                        listOf(
-                            logoView,
-                            logoDescriptionView,
-                            titleView,
-                            subtitleView,
-                            descriptionView,
-                            customizedViewContainer,
-                            indicatorMessageView,
-                            negativeButton,
-                            cancelButton,
-                            retryButton,
-                            confirmationButton,
-                            credentialFallbackButton,
-                        ),
-                    panelViewController = panelViewController,
                     jankListener = jankListener,
                 )
             }
@@ -275,7 +234,6 @@
                     if (!showWithoutIcon) {
                         PromptIconViewBinder.bind(
                             iconView,
-                            iconSizeOverride,
                             viewModel,
                         )
                     }
@@ -329,20 +287,6 @@
                     }
                 }
 
-                // set padding
-                launch {
-                    viewModel.promptPadding.collect { promptPadding ->
-                        if (!constraintBp()) {
-                            view.setPadding(
-                                promptPadding.left,
-                                promptPadding.top,
-                                promptPadding.right,
-                                promptPadding.bottom
-                            )
-                        }
-                    }
-                }
-
                 // configure & hide/disable buttons
                 launch {
                     viewModel.credentialKind
@@ -388,17 +332,31 @@
 
                 // reuse the icon as a confirm button
                 launch {
-                    viewModel.isIconConfirmButton
-                        .map { isPending ->
-                            when {
-                                isPending && modalities.hasFaceAndFingerprint ->
-                                    View.OnTouchListener { _: View, event: MotionEvent ->
-                                        viewModel.onOverlayTouch(event)
-                                    }
-                                else -> null
+                    if (bpIconA11y()) {
+                        viewModel.isIconConfirmButton.collect { isButton ->
+                            if (isButton) {
+                                iconView.onTouchListener { _: View, event: MotionEvent ->
+                                    viewModel.onOverlayTouch(event)
+                                }
+                                iconView.setOnClickListener { viewModel.confirmAuthenticated() }
+                            } else {
+                                iconView.setOnTouchListener(null)
+                                iconView.setOnClickListener(null)
                             }
                         }
-                        .collect { onTouch -> iconView.setOnTouchListener(onTouch) }
+                    } else {
+                        viewModel.isIconConfirmButton
+                            .map { isPending ->
+                                when {
+                                    isPending && modalities.hasFaceAndFingerprint ->
+                                        View.OnTouchListener { _: View, event: MotionEvent ->
+                                            viewModel.onOverlayTouch(event)
+                                        }
+                                    else -> null
+                                }
+                            }
+                            .collect { onTouch -> iconView.setOnTouchListener(onTouch) }
+                    }
                 }
 
                 // dismiss prompt when authenticated and confirmed
@@ -416,7 +374,8 @@
                             // Allow icon to be used as confirmation button with udfps and a11y
                             // enabled
                             if (
-                                accessibilityManager.isTouchExplorationEnabled &&
+                                !bpIconA11y() &&
+                                    accessibilityManager.isTouchExplorationEnabled &&
                                     modalities.hasUdfps
                             ) {
                                 iconView.setOnClickListener { viewModel.confirmAuthenticated() }
@@ -546,24 +505,6 @@
         fun onAuthenticatedAndConfirmed()
     }
 
-    @Deprecated("TODO(b/330788871): remove after replacing AuthContainerView")
-    enum class BiometricState {
-        /** Authentication hardware idle. */
-        STATE_IDLE,
-        /** UI animating in, authentication hardware active. */
-        STATE_AUTHENTICATING_ANIMATING_IN,
-        /** UI animated in, authentication hardware active. */
-        STATE_AUTHENTICATING,
-        /** UI animated in, authentication hardware active. */
-        STATE_HELP,
-        /** Hard error, e.g. ERROR_TIMEOUT. Authentication hardware idle. */
-        STATE_ERROR,
-        /** Authenticated, waiting for user confirmation. Authentication hardware idle. */
-        STATE_PENDING_CONFIRMATION,
-        /** Authenticated, dialog animating away soon. */
-        STATE_AUTHENTICATED,
-    }
-
     private var lifecycleScope: CoroutineScope? = null
     private var modalities: BiometricModalities = BiometricModalities()
     private var legacyCallback: Callback? = null
@@ -699,15 +640,8 @@
         }
 
     fun startTransitionToCredentialUI(isError: Boolean) {
-        if (!constraintBp()) {
-            applicationScope.launch {
-                viewModel.onSwitchToCredential()
-                legacyCallback?.onUseDeviceCredential()
-            }
-        } else {
-            viewModel.onSwitchToCredential()
-            legacyCallback?.onUseDeviceCredential()
-        }
+        viewModel.onSwitchToCredential()
+        legacyCallback?.onUseDeviceCredential()
     }
 
     fun cancelAnimation() {
@@ -727,9 +661,6 @@
         else -> ""
     }
 
-private fun String.ellipsize(cutOffLength: Int) =
-    if (length <= cutOffLength) this else replaceRange(cutOffLength, length, "...")
-
 private fun Boolean.asVisibleOrGone(): Int = if (this) View.VISIBLE else View.GONE
 
 private fun Boolean.asVisibleOrHidden(): Int = if (this) View.VISIBLE else View.INVISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index b9ec2de..85c3ae3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -38,10 +38,7 @@
 import androidx.constraintlayout.widget.Guideline
 import androidx.core.animation.addListener
 import androidx.core.view.doOnLayout
-import androidx.core.view.isGone
 import androidx.lifecycle.lifecycleScope
-import com.android.systemui.Flags.constraintBp
-import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.ui.viewmodel.PromptPosition
 import com.android.systemui.biometrics.ui.viewmodel.PromptSize
@@ -49,7 +46,6 @@
 import com.android.systemui.biometrics.ui.viewmodel.isLarge
 import com.android.systemui.biometrics.ui.viewmodel.isLeft
 import com.android.systemui.biometrics.ui.viewmodel.isMedium
-import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
 import com.android.systemui.biometrics.ui.viewmodel.isSmall
 import com.android.systemui.biometrics.ui.viewmodel.isTop
 import com.android.systemui.lifecycle.repeatWhenAttached
@@ -71,8 +67,6 @@
         view: View,
         viewModel: PromptViewModel,
         viewsToHideWhenSmall: List<View>,
-        viewsToFadeInOnSizeChange: List<View>,
-        panelViewController: AuthPanelController?,
         jankListener: BiometricJankListener,
     ) {
         val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java))
@@ -92,553 +86,366 @@
             }
         }
 
-        if (constraintBp()) {
-            val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
-            val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
-            val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
-            val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
+        val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
+        val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
+        val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
+        val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
 
-            val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
-            val panelView = view.requireViewById<View>(R.id.panel)
-            val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
-            val pxToDp =
-                TypedValue.applyDimension(
-                    TypedValue.COMPLEX_UNIT_DIP,
-                    1f,
-                    view.resources.displayMetrics
-                )
-            val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
+        val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
+        val panelView = view.requireViewById<View>(R.id.panel)
+        val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
+        val pxToDp =
+            TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP,
+                1f,
+                view.resources.displayMetrics
+            )
+        val cornerRadiusPx = (pxToDp * cornerRadius).toInt()
 
-            var currentSize: PromptSize? = null
-            var currentPosition: PromptPosition = PromptPosition.Bottom
-            panelView.outlineProvider =
-                object : ViewOutlineProvider() {
-                    override fun getOutline(view: View, outline: Outline) {
-                        when (currentPosition) {
-                            PromptPosition.Right -> {
-                                outline.setRoundRect(
-                                    0,
-                                    0,
-                                    view.width + cornerRadiusPx,
-                                    view.height,
-                                    cornerRadiusPx.toFloat()
-                                )
-                            }
-                            PromptPosition.Left -> {
-                                outline.setRoundRect(
-                                    -cornerRadiusPx,
-                                    0,
-                                    view.width,
-                                    view.height,
-                                    cornerRadiusPx.toFloat()
-                                )
-                            }
-                            PromptPosition.Bottom,
-                            PromptPosition.Top -> {
-                                outline.setRoundRect(
-                                    0,
-                                    0,
-                                    view.width,
-                                    view.height + cornerRadiusPx,
-                                    cornerRadiusPx.toFloat()
-                                )
-                            }
-                        }
-                    }
-                }
-
-            // ConstraintSets for animating between prompt sizes
-            val mediumConstraintSet = ConstraintSet()
-            mediumConstraintSet.clone(view as ConstraintLayout)
-
-            val smallConstraintSet = ConstraintSet()
-            smallConstraintSet.clone(mediumConstraintSet)
-
-            val largeConstraintSet = ConstraintSet()
-            largeConstraintSet.clone(mediumConstraintSet)
-            largeConstraintSet.constrainMaxWidth(R.id.panel, 0)
-            largeConstraintSet.setGuidelineBegin(R.id.leftGuideline, 0)
-            largeConstraintSet.setGuidelineEnd(R.id.rightGuideline, 0)
-
-            // TODO: Investigate better way to handle 180 rotations
-            val flipConstraintSet = ConstraintSet()
-
-            view.doOnLayout {
-                fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
-                    viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
-                    largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
-                    largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
-                    largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
-
-                    if (hideSensorIcon) {
-                        smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
-                        smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
-                        mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
-                        mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
-                    }
-                }
-
-                view.repeatWhenAttached {
-                    lifecycleScope.launch {
-                        viewModel.iconPosition.collect { position ->
-                            if (position != Rect()) {
-                                val iconParams =
-                                    iconHolderView.layoutParams as ConstraintLayout.LayoutParams
-
-                                if (position.left != 0) {
-                                    iconParams.endToEnd = ConstraintSet.UNSET
-                                    iconParams.leftMargin = position.left
-                                    mediumConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT
-                                    )
-                                    mediumConstraintSet.connect(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT,
-                                        ConstraintSet.PARENT_ID,
-                                        ConstraintSet.LEFT
-                                    )
-                                    mediumConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT,
-                                        position.left
-                                    )
-                                    smallConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT
-                                    )
-                                    smallConstraintSet.connect(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT,
-                                        ConstraintSet.PARENT_ID,
-                                        ConstraintSet.LEFT
-                                    )
-                                    smallConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT,
-                                        position.left
-                                    )
-                                }
-                                if (position.top != 0) {
-                                    iconParams.bottomToBottom = ConstraintSet.UNSET
-                                    iconParams.topMargin = position.top
-                                    mediumConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.BOTTOM
-                                    )
-                                    mediumConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.TOP,
-                                        position.top
-                                    )
-                                    smallConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.BOTTOM
-                                    )
-                                    smallConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.TOP,
-                                        position.top
-                                    )
-                                }
-                                if (position.right != 0) {
-                                    iconParams.startToStart = ConstraintSet.UNSET
-                                    iconParams.rightMargin = position.right
-                                    mediumConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT
-                                    )
-                                    mediumConstraintSet.connect(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT,
-                                        ConstraintSet.PARENT_ID,
-                                        ConstraintSet.RIGHT
-                                    )
-                                    mediumConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT,
-                                        position.right
-                                    )
-                                    smallConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.LEFT
-                                    )
-                                    smallConstraintSet.connect(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT,
-                                        ConstraintSet.PARENT_ID,
-                                        ConstraintSet.RIGHT
-                                    )
-                                    smallConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.RIGHT,
-                                        position.right
-                                    )
-                                }
-                                if (position.bottom != 0) {
-                                    iconParams.topToTop = ConstraintSet.UNSET
-                                    iconParams.bottomMargin = position.bottom
-                                    mediumConstraintSet.clear(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.TOP
-                                    )
-                                    mediumConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.BOTTOM,
-                                        position.bottom
-                                    )
-                                    smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
-                                    smallConstraintSet.setMargin(
-                                        R.id.biometric_icon,
-                                        ConstraintSet.BOTTOM,
-                                        position.bottom
-                                    )
-                                }
-                                iconHolderView.layoutParams = iconParams
-                            }
-                        }
-                    }
-
-                    lifecycleScope.launch {
-                        viewModel.iconSize.collect { iconSize ->
-                            iconHolderView.layoutParams.width = iconSize.first
-                            iconHolderView.layoutParams.height = iconSize.second
-                            mediumConstraintSet.constrainWidth(R.id.biometric_icon, iconSize.first)
-                            mediumConstraintSet.constrainHeight(
-                                R.id.biometric_icon,
-                                iconSize.second
+        var currentSize: PromptSize? = null
+        var currentPosition: PromptPosition = PromptPosition.Bottom
+        panelView.outlineProvider =
+            object : ViewOutlineProvider() {
+                override fun getOutline(view: View, outline: Outline) {
+                    when (currentPosition) {
+                        PromptPosition.Right -> {
+                            outline.setRoundRect(
+                                0,
+                                0,
+                                view.width + cornerRadiusPx,
+                                view.height,
+                                cornerRadiusPx.toFloat()
                             )
                         }
-                    }
-
-                    lifecycleScope.launch {
-                        viewModel.guidelineBounds.collect { bounds ->
-                            val bottomInset =
-                                windowManager.maximumWindowMetrics.windowInsets
-                                    .getInsets(WindowInsets.Type.navigationBars())
-                                    .bottom
-                            mediumConstraintSet.setGuidelineEnd(R.id.bottomGuideline, bottomInset)
-
-                            if (bounds.left >= 0) {
-                                mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
-                                smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
-                            } else if (bounds.left < 0) {
-                                mediumConstraintSet.setGuidelineEnd(
-                                    leftGuideline.id,
-                                    abs(bounds.left)
-                                )
-                                smallConstraintSet.setGuidelineEnd(
-                                    leftGuideline.id,
-                                    abs(bounds.left)
-                                )
-                            }
-
-                            if (bounds.right >= 0) {
-                                mediumConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
-                                smallConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
-                            } else if (bounds.right < 0) {
-                                mediumConstraintSet.setGuidelineBegin(
-                                    rightGuideline.id,
-                                    abs(bounds.right)
-                                )
-                                smallConstraintSet.setGuidelineBegin(
-                                    rightGuideline.id,
-                                    abs(bounds.right)
-                                )
-                            }
-
-                            if (bounds.top >= 0) {
-                                mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
-                                smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
-                            } else if (bounds.top < 0) {
-                                mediumConstraintSet.setGuidelineEnd(
-                                    topGuideline.id,
-                                    abs(bounds.top)
-                                )
-                                smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
-                            }
-
-                            if (midGuideline != null) {
-                                val left =
-                                    if (bounds.left >= 0) {
-                                        abs(bounds.left)
-                                    } else {
-                                        view.width - abs(bounds.left)
-                                    }
-                                val right =
-                                    if (bounds.right >= 0) {
-                                        view.width - abs(bounds.right)
-                                    } else {
-                                        abs(bounds.right)
-                                    }
-                                val mid = (left + right) / 2
-                                mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
-                            }
+                        PromptPosition.Left -> {
+                            outline.setRoundRect(
+                                -cornerRadiusPx,
+                                0,
+                                view.width,
+                                view.height,
+                                cornerRadiusPx.toFloat()
+                            )
                         }
-                    }
-
-                    lifecycleScope.launch {
-                        combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
-                            (hideSensorIcon, size) ->
-                            setVisibilities(hideSensorIcon, size)
-                        }
-                    }
-
-                    lifecycleScope.launch {
-                        combine(viewModel.position, viewModel.size, ::Pair).collect {
-                            (position, size) ->
-                            if (position.isLeft) {
-                                if (size.isSmall) {
-                                    flipConstraintSet.clone(smallConstraintSet)
-                                } else {
-                                    flipConstraintSet.clone(mediumConstraintSet)
-                                }
-
-                                // Move all content to other panel
-                                flipConstraintSet.connect(
-                                    R.id.scrollView,
-                                    ConstraintSet.LEFT,
-                                    R.id.midGuideline,
-                                    ConstraintSet.LEFT
-                                )
-                                flipConstraintSet.connect(
-                                    R.id.scrollView,
-                                    ConstraintSet.RIGHT,
-                                    R.id.rightGuideline,
-                                    ConstraintSet.RIGHT
-                                )
-                            } else if (position.isTop) {
-                                // Top position is only used for 180 rotation Udfps
-                                // Requires repositioning due to sensor location at top of screen
-                                mediumConstraintSet.connect(
-                                    R.id.scrollView,
-                                    ConstraintSet.TOP,
-                                    R.id.indicator,
-                                    ConstraintSet.BOTTOM
-                                )
-                                mediumConstraintSet.connect(
-                                    R.id.scrollView,
-                                    ConstraintSet.BOTTOM,
-                                    R.id.button_bar,
-                                    ConstraintSet.TOP
-                                )
-                                mediumConstraintSet.connect(
-                                    R.id.panel,
-                                    ConstraintSet.TOP,
-                                    R.id.biometric_icon,
-                                    ConstraintSet.TOP
-                                )
-                                mediumConstraintSet.setMargin(
-                                    R.id.panel,
-                                    ConstraintSet.TOP,
-                                    (-24 * pxToDp).toInt()
-                                )
-                                mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
-                            }
-
-                            when {
-                                size.isSmall -> {
-                                    if (position.isLeft) {
-                                        flipConstraintSet.applyTo(view)
-                                    } else {
-                                        smallConstraintSet.applyTo(view)
-                                    }
-                                }
-                                size.isMedium && currentSize.isSmall -> {
-                                    val autoTransition = AutoTransition()
-                                    autoTransition.setDuration(
-                                        ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
-                                    )
-
-                                    TransitionManager.beginDelayedTransition(view, autoTransition)
-
-                                    if (position.isLeft) {
-                                        flipConstraintSet.applyTo(view)
-                                    } else {
-                                        mediumConstraintSet.applyTo(view)
-                                    }
-                                }
-                                size.isMedium -> {
-                                    if (position.isLeft) {
-                                        flipConstraintSet.applyTo(view)
-                                    } else {
-                                        mediumConstraintSet.applyTo(view)
-                                    }
-                                }
-                                size.isLarge && currentSize.isMedium -> {
-                                    val autoTransition = AutoTransition()
-                                    autoTransition.setDuration(
-                                        ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
-                                    )
-
-                                    TransitionManager.beginDelayedTransition(view, autoTransition)
-                                    largeConstraintSet.applyTo(view)
-                                }
-                            }
-
-                            currentSize = size
-                            currentPosition = position
-                            notifyAccessibilityChanged()
-
-                            panelView.invalidateOutline()
-                            view.invalidate()
-                            view.requestLayout()
+                        PromptPosition.Bottom,
+                        PromptPosition.Top -> {
+                            outline.setRoundRect(
+                                0,
+                                0,
+                                view.width,
+                                view.height + cornerRadiusPx,
+                                cornerRadiusPx.toFloat()
+                            )
                         }
                     }
                 }
             }
-        } else if (panelViewController != null) {
-            val iconHolderView = view.requireViewById<View>(R.id.biometric_icon_frame)
-            val iconPadding = view.resources.getDimension(R.dimen.biometric_dialog_icon_padding)
-            val fullSizeYOffset =
-                view.resources.getDimension(
-                    R.dimen.biometric_dialog_medium_to_large_translation_offset
-                )
 
-            // cache the original position of the icon view (as done in legacy view)
-            // this must happen before any size changes can be made
-            view.doOnLayout {
-                // TODO(b/251476085): this old way of positioning has proven itself unreliable
-                // remove this and associated thing like (UdfpsDialogMeasureAdapter) and
-                // pin to the physical sensor
-                val iconHolderOriginalY = iconHolderView.y
+        // ConstraintSets for animating between prompt sizes
+        val mediumConstraintSet = ConstraintSet()
+        mediumConstraintSet.clone(view as ConstraintLayout)
 
-                // bind to prompt
-                // TODO(b/251476085): migrate the legacy panel controller and simplify this
-                view.repeatWhenAttached {
-                    var currentSize: PromptSize? = null
-                    lifecycleScope.launch {
-                        /**
-                         * View is only set visible in BiometricViewSizeBinder once PromptSize is
-                         * determined that accounts for iconView size, to prevent prompt resizing
-                         * being visible to the user.
-                         *
-                         * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
-                         *   layout is implemented
-                         */
-                        combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
-                            (isIconViewLoaded, size) ->
-                            if (!isIconViewLoaded) {
-                                return@collect
+        val smallConstraintSet = ConstraintSet()
+        smallConstraintSet.clone(mediumConstraintSet)
+
+        val largeConstraintSet = ConstraintSet()
+        largeConstraintSet.clone(mediumConstraintSet)
+        largeConstraintSet.constrainMaxWidth(R.id.panel, 0)
+        largeConstraintSet.setGuidelineBegin(R.id.leftGuideline, 0)
+        largeConstraintSet.setGuidelineEnd(R.id.rightGuideline, 0)
+
+        // TODO: Investigate better way to handle 180 rotations
+        val flipConstraintSet = ConstraintSet()
+
+        view.doOnLayout {
+            fun setVisibilities(hideSensorIcon: Boolean, size: PromptSize) {
+                viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
+                largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+                largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+                largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
+
+                if (hideSensorIcon) {
+                    smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                    smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+                    smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
+                    mediumConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+                    mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+                    mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
+                }
+            }
+
+            view.repeatWhenAttached {
+                lifecycleScope.launch {
+                    viewModel.iconPosition.collect { position ->
+                        if (position != Rect()) {
+                            val iconParams =
+                                iconHolderView.layoutParams as ConstraintLayout.LayoutParams
+
+                            if (position.left != 0) {
+                                iconParams.endToEnd = ConstraintSet.UNSET
+                                iconParams.leftMargin = position.left
+                                mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.RIGHT)
+                                mediumConstraintSet.connect(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.LEFT,
+                                    ConstraintSet.PARENT_ID,
+                                    ConstraintSet.LEFT
+                                )
+                                mediumConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.LEFT,
+                                    position.left
+                                )
+                                smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.RIGHT)
+                                smallConstraintSet.connect(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.LEFT,
+                                    ConstraintSet.PARENT_ID,
+                                    ConstraintSet.LEFT
+                                )
+                                smallConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.LEFT,
+                                    position.left
+                                )
                             }
-
-                            // prepare for animated size transitions
-                            for (v in viewsToHideWhenSmall) {
-                                v.showContentOrHide(forceHide = size.isSmall)
+                            if (position.top != 0) {
+                                iconParams.bottomToBottom = ConstraintSet.UNSET
+                                iconParams.topMargin = position.top
+                                mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.BOTTOM)
+                                mediumConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.TOP,
+                                    position.top
+                                )
+                                smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.BOTTOM)
+                                smallConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.TOP,
+                                    position.top
+                                )
                             }
-
-                            if (viewModel.hideSensorIcon.first()) {
-                                iconHolderView.visibility = View.GONE
+                            if (position.right != 0) {
+                                iconParams.startToStart = ConstraintSet.UNSET
+                                iconParams.rightMargin = position.right
+                                mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.LEFT)
+                                mediumConstraintSet.connect(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.RIGHT,
+                                    ConstraintSet.PARENT_ID,
+                                    ConstraintSet.RIGHT
+                                )
+                                mediumConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.RIGHT,
+                                    position.right
+                                )
+                                smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.LEFT)
+                                smallConstraintSet.connect(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.RIGHT,
+                                    ConstraintSet.PARENT_ID,
+                                    ConstraintSet.RIGHT
+                                )
+                                smallConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.RIGHT,
+                                    position.right
+                                )
                             }
-
-                            if (currentSize == null && size.isSmall) {
-                                iconHolderView.alpha = 0f
+                            if (position.bottom != 0) {
+                                iconParams.topToTop = ConstraintSet.UNSET
+                                iconParams.bottomMargin = position.bottom
+                                mediumConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
+                                mediumConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.BOTTOM,
+                                    position.bottom
+                                )
+                                smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.TOP)
+                                smallConstraintSet.setMargin(
+                                    R.id.biometric_icon,
+                                    ConstraintSet.BOTTOM,
+                                    position.bottom
+                                )
                             }
-                            if ((currentSize.isSmall && size.isMedium) || size.isSmall) {
-                                viewsToFadeInOnSizeChange.forEach { it.alpha = 0f }
-                            }
+                            iconHolderView.layoutParams = iconParams
+                        }
+                    }
+                }
 
-                            // propagate size changes to legacy panel controller and animate
-                            // transitions
-                            view.doOnLayout {
-                                val width = view.measuredWidth
-                                val height = view.measuredHeight
+                lifecycleScope.launch {
+                    viewModel.iconSize.collect { iconSize ->
+                        iconHolderView.layoutParams.width = iconSize.first
+                        iconHolderView.layoutParams.height = iconSize.second
+                        mediumConstraintSet.constrainWidth(R.id.biometric_icon, iconSize.first)
+                        mediumConstraintSet.constrainHeight(R.id.biometric_icon, iconSize.second)
+                    }
+                }
 
-                                when {
-                                    size.isSmall -> {
-                                        iconHolderView.alpha = 1f
-                                        val bottomInset =
-                                            windowManager.maximumWindowMetrics.windowInsets
-                                                .getInsets(WindowInsets.Type.navigationBars())
-                                                .bottom
-                                        iconHolderView.y =
-                                            if (view.isLandscape()) {
-                                                (view.height -
-                                                    iconHolderView.height -
-                                                    bottomInset) / 2f
-                                            } else {
-                                                view.height -
-                                                    iconHolderView.height -
-                                                    iconPadding -
-                                                    bottomInset
-                                            }
-                                        val newHeight =
-                                            iconHolderView.height + (2 * iconPadding.toInt()) -
-                                                iconHolderView.paddingTop -
-                                                iconHolderView.paddingBottom
-                                        panelViewController.updateForContentDimensions(
-                                            width,
-                                            newHeight + bottomInset,
-                                            0, /* animateDurationMs */
-                                        )
-                                    }
-                                    size.isMedium && currentSize.isSmall -> {
-                                        val duration = ANIMATE_SMALL_TO_MEDIUM_DURATION_MS
-                                        panelViewController.updateForContentDimensions(
-                                            width,
-                                            height,
-                                            duration,
-                                        )
-                                        startMonitoredAnimation(
-                                            listOf(
-                                                iconHolderView.asVerticalAnimator(
-                                                    duration = duration.toLong(),
-                                                    toY =
-                                                        iconHolderOriginalY -
-                                                            viewsToHideWhenSmall
-                                                                .filter { it.isGone }
-                                                                .sumOf { it.height },
-                                                ),
-                                                viewsToFadeInOnSizeChange.asFadeInAnimator(
-                                                    duration = duration.toLong(),
-                                                    delay = duration.toLong(),
-                                                ),
-                                            )
-                                        )
-                                    }
-                                    size.isMedium && currentSize.isNullOrNotSmall -> {
-                                        panelViewController.updateForContentDimensions(
-                                            width,
-                                            height,
-                                            0, /* animateDurationMs */
-                                        )
-                                    }
-                                    size.isLarge -> {
-                                        val duration = ANIMATE_MEDIUM_TO_LARGE_DURATION_MS
-                                        panelViewController.setUseFullScreen(true)
-                                        panelViewController.updateForContentDimensions(
-                                            panelViewController.containerWidth,
-                                            panelViewController.containerHeight,
-                                            duration,
-                                        )
+                lifecycleScope.launch {
+                    viewModel.guidelineBounds.collect { bounds ->
+                        val bottomInset =
+                            windowManager.maximumWindowMetrics.windowInsets
+                                .getInsets(WindowInsets.Type.navigationBars())
+                                .bottom
+                        mediumConstraintSet.setGuidelineEnd(R.id.bottomGuideline, bottomInset)
 
-                                        startMonitoredAnimation(
-                                            listOf(
-                                                view.asVerticalAnimator(
-                                                    duration.toLong() * 2 / 3,
-                                                    toY = view.y - fullSizeYOffset
-                                                ),
-                                                listOf(view)
-                                                    .asFadeInAnimator(
-                                                        duration = duration.toLong() / 2,
-                                                        delay = duration.toLong(),
-                                                    ),
-                                            )
-                                        )
-                                        // TODO(b/251476085): clean up (copied from legacy)
-                                        if (view.isAttachedToWindow) {
-                                            val parent = view.parent as? ViewGroup
-                                            parent?.removeView(view)
-                                        }
-                                    }
+                        if (bounds.left >= 0) {
+                            mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+                            smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
+                        } else if (bounds.left < 0) {
+                            mediumConstraintSet.setGuidelineEnd(leftGuideline.id, abs(bounds.left))
+                            smallConstraintSet.setGuidelineEnd(leftGuideline.id, abs(bounds.left))
+                        }
+
+                        if (bounds.right >= 0) {
+                            mediumConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+                            smallConstraintSet.setGuidelineEnd(rightGuideline.id, bounds.right)
+                        } else if (bounds.right < 0) {
+                            mediumConstraintSet.setGuidelineBegin(
+                                rightGuideline.id,
+                                abs(bounds.right)
+                            )
+                            smallConstraintSet.setGuidelineBegin(
+                                rightGuideline.id,
+                                abs(bounds.right)
+                            )
+                        }
+
+                        if (bounds.top >= 0) {
+                            mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+                            smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+                        } else if (bounds.top < 0) {
+                            mediumConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
+                            smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
+                        }
+
+                        if (midGuideline != null) {
+                            val left =
+                                if (bounds.left >= 0) {
+                                    abs(bounds.left)
+                                } else {
+                                    view.width - abs(bounds.left)
                                 }
+                            val right =
+                                if (bounds.right >= 0) {
+                                    view.width - abs(bounds.right)
+                                } else {
+                                    abs(bounds.right)
+                                }
+                            val mid = (left + right) / 2
+                            mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid)
+                        }
+                    }
+                }
 
-                                currentSize = size
-                                view.visibility = View.VISIBLE
-                                viewModel.setIsIconViewLoaded(false)
-                                notifyAccessibilityChanged()
+                lifecycleScope.launch {
+                    combine(viewModel.hideSensorIcon, viewModel.size, ::Pair).collect {
+                        (hideSensorIcon, size) ->
+                        setVisibilities(hideSensorIcon, size)
+                    }
+                }
+
+                lifecycleScope.launch {
+                    combine(viewModel.position, viewModel.size, ::Pair).collect { (position, size)
+                        ->
+                        if (position.isLeft) {
+                            if (size.isSmall) {
+                                flipConstraintSet.clone(smallConstraintSet)
+                            } else {
+                                flipConstraintSet.clone(mediumConstraintSet)
+                            }
+
+                            // Move all content to other panel
+                            flipConstraintSet.connect(
+                                R.id.scrollView,
+                                ConstraintSet.LEFT,
+                                R.id.midGuideline,
+                                ConstraintSet.LEFT
+                            )
+                            flipConstraintSet.connect(
+                                R.id.scrollView,
+                                ConstraintSet.RIGHT,
+                                R.id.rightGuideline,
+                                ConstraintSet.RIGHT
+                            )
+                        } else if (position.isTop) {
+                            // Top position is only used for 180 rotation Udfps
+                            // Requires repositioning due to sensor location at top of screen
+                            mediumConstraintSet.connect(
+                                R.id.scrollView,
+                                ConstraintSet.TOP,
+                                R.id.indicator,
+                                ConstraintSet.BOTTOM
+                            )
+                            mediumConstraintSet.connect(
+                                R.id.scrollView,
+                                ConstraintSet.BOTTOM,
+                                R.id.button_bar,
+                                ConstraintSet.TOP
+                            )
+                            mediumConstraintSet.connect(
+                                R.id.panel,
+                                ConstraintSet.TOP,
+                                R.id.biometric_icon,
+                                ConstraintSet.TOP
+                            )
+                            mediumConstraintSet.setMargin(
+                                R.id.panel,
+                                ConstraintSet.TOP,
+                                (-24 * pxToDp).toInt()
+                            )
+                            mediumConstraintSet.setVerticalBias(R.id.scrollView, 0f)
+                        }
+
+                        when {
+                            size.isSmall -> {
+                                if (position.isLeft) {
+                                    flipConstraintSet.applyTo(view)
+                                } else {
+                                    smallConstraintSet.applyTo(view)
+                                }
+                            }
+                            size.isMedium && currentSize.isSmall -> {
+                                val autoTransition = AutoTransition()
+                                autoTransition.setDuration(
+                                    ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
+                                )
+
+                                TransitionManager.beginDelayedTransition(view, autoTransition)
+
+                                if (position.isLeft) {
+                                    flipConstraintSet.applyTo(view)
+                                } else {
+                                    mediumConstraintSet.applyTo(view)
+                                }
+                            }
+                            size.isMedium -> {
+                                if (position.isLeft) {
+                                    flipConstraintSet.applyTo(view)
+                                } else {
+                                    mediumConstraintSet.applyTo(view)
+                                }
+                            }
+                            size.isLarge && currentSize.isMedium -> {
+                                val autoTransition = AutoTransition()
+                                autoTransition.setDuration(
+                                    ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+                                )
+
+                                TransitionManager.beginDelayedTransition(view, autoTransition)
+                                largeConstraintSet.applyTo(view)
                             }
                         }
+
+                        currentSize = size
+                        currentPosition = position
+                        notifyAccessibilityChanged()
+
+                        panelView.invalidateOutline()
+                        view.invalidate()
+                        view.requestLayout()
                     }
                 }
             }
@@ -646,17 +453,6 @@
     }
 }
 
-private fun View.isLandscape(): Boolean {
-    val r = context.display.rotation
-    return if (
-        context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)
-    ) {
-        r == Surface.ROTATION_0 || r == Surface.ROTATION_180
-    } else {
-        r == Surface.ROTATION_90 || r == Surface.ROTATION_270
-    }
-}
-
 private fun View.showContentOrHide(forceHide: Boolean = false) {
     val isTextViewWithBlankText = this is TextView && this.text.isBlank()
     val isImageViewWithoutImage = this is ImageView && this.drawable == null
@@ -667,26 +463,3 @@
             View.VISIBLE
         }
 }
-
-private fun View.asVerticalAnimator(
-    duration: Long,
-    toY: Float,
-    fromY: Float = this.y
-): ValueAnimator {
-    val animator = ValueAnimator.ofFloat(fromY, toY)
-    animator.duration = duration
-    animator.addUpdateListener { y = it.animatedValue as Float }
-    return animator
-}
-
-private fun List<View>.asFadeInAnimator(duration: Long, delay: Long): ValueAnimator {
-    forEach { it.alpha = 0f }
-    val animator = ValueAnimator.ofFloat(0f, 1f)
-    animator.duration = duration
-    animator.startDelay = delay
-    animator.addUpdateListener {
-        val alpha = it.animatedValue as Float
-        forEach { view -> view.alpha = alpha }
-    }
-    return animator
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
index 18e2a56..49f4b05 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialViewBinder.kt
@@ -10,7 +10,6 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.animation.Interpolators
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.AuthPanelController
 import com.android.systemui.biometrics.ui.CredentialPasswordView
 import com.android.systemui.biometrics.ui.CredentialPatternView
@@ -82,7 +81,7 @@
 
                         subtitleView.textOrHide = header.subtitle
                         descriptionView.textOrHide = header.description
-                        if (Flags.customBiometricPrompt() && constraintBp()) {
+                        if (Flags.customBiometricPrompt()) {
                             BiometricCustomizedViewBinder.bind(
                                 customizedViewContainer,
                                 header.contentView,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index 9e4aaaa..eab3b26 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -22,9 +22,7 @@
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
-import com.airbnb.lottie.LottieOnCompositionLoadedListener
 import com.android.settingslib.widget.LottieColorUtils
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
 import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
@@ -44,55 +42,12 @@
     @JvmStatic
     fun bind(
         iconView: LottieAnimationView,
-        iconViewLayoutParamSizeOverride: Pair<Int, Int>?,
         promptViewModel: PromptViewModel
     ) {
         val viewModel = promptViewModel.iconViewModel
         iconView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 viewModel.onConfigurationChanged(iconView.context.resources.configuration)
-                if (iconViewLayoutParamSizeOverride != null) {
-                    iconView.layoutParams.width = iconViewLayoutParamSizeOverride.first
-                    iconView.layoutParams.height = iconViewLayoutParamSizeOverride.second
-                }
-
-                if (!constraintBp()) {
-                    launch {
-                        var lottieOnCompositionLoadedListener: LottieOnCompositionLoadedListener? =
-                            null
-
-                        viewModel.iconSize.collect { iconSize ->
-                            /**
-                             * When we bind the BiometricPrompt View and ViewModel in
-                             * [BiometricViewBinder], the view is set invisible and
-                             * [isIconViewLoaded] is set to false. We configure the iconView with a
-                             * LottieOnCompositionLoadedListener that sets [isIconViewLoaded] to
-                             * true, in order to wait for the iconView to load before determining
-                             * the prompt size, and prevent any prompt resizing from being visible
-                             * to the user.
-                             *
-                             * TODO(b/288175072): May be able to remove this once constraint layout
-                             *   is unflagged
-                             */
-                            if (lottieOnCompositionLoadedListener != null) {
-                                iconView.removeLottieOnCompositionLoadedListener(
-                                    lottieOnCompositionLoadedListener!!
-                                )
-                            }
-                            lottieOnCompositionLoadedListener = LottieOnCompositionLoadedListener {
-                                promptViewModel.setIsIconViewLoaded(true)
-                            }
-                            iconView.addLottieOnCompositionLoadedListener(
-                                lottieOnCompositionLoadedListener!!
-                            )
-
-                            if (iconViewLayoutParamSizeOverride == null) {
-                                iconView.layoutParams.width = iconSize.first
-                                iconView.layoutParams.height = iconSize.second
-                            }
-                        }
-                    }
-                }
 
                 launch {
                     viewModel.iconAsset
@@ -154,7 +109,7 @@
     setAnimation(asset)
     if (animatingFromSfpsAuthenticating(asset)) {
         // Skipping to error / success / unlock segment of animation
-        setMinFrame(151)
+        setMinFrame(158)
     } else {
         frame = 0
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 31af126..761c3da 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -6,7 +6,6 @@
 import android.hardware.biometrics.PromptContentView
 import android.text.InputType
 import com.android.internal.widget.LockPatternView
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.CredentialStatus
 import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
@@ -39,7 +38,7 @@
             credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
             credentialInteractor.showTitleOnly
         ) { request, showTitleOnly ->
-            val flagEnabled = customBiometricPrompt() && constraintBp()
+            val flagEnabled = customBiometricPrompt()
             val showTitleOnlyForCredential = showTitleOnly && flagEnabled
             BiometricPromptHeaderViewModelImpl(
                 request,
@@ -82,8 +81,8 @@
     val errorMessage: Flow<String> =
         combine(credentialInteractor.verificationError, credentialInteractor.prompt) { error, p ->
             when (error) {
-                is CredentialStatus.Fail.Error -> error.error
-                        ?: applicationContext.asBadCredentialErrorMessage(p)
+                is CredentialStatus.Fail.Error ->
+                    error.error ?: applicationContext.asBadCredentialErrorMessage(p)
                 is CredentialStatus.Fail.Throttled -> error.error
                 null -> ""
             }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 214420d..4c2fe07 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -36,7 +36,6 @@
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.UdfpsUtils
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.Utils.isSystem
@@ -470,7 +469,7 @@
         promptSelectorInteractor.prompt
             .map {
                 when {
-                    !(customBiometricPrompt() && constraintBp()) || it == null -> Pair(null, "")
+                    !(customBiometricPrompt()) || it == null -> Pair(null, "")
                     else -> context.getUserBadgedLogoInfo(it, iconProvider, activityTaskManager)
                 }
             }
@@ -487,7 +486,7 @@
     /** Custom content view for the prompt. */
     val contentView: Flow<PromptContentView?> =
         promptSelectorInteractor.prompt
-            .map { if (customBiometricPrompt() && constraintBp()) it?.contentView else null }
+            .map { if (customBiometricPrompt()) it?.contentView else null }
             .distinctUntilChanged()
 
     private val originalDescription =
@@ -942,16 +941,16 @@
     private fun vibrateOnSuccess() {
         _hapticsToPlay.value =
             HapticsToPlay(
-                HapticFeedbackConstants.CONFIRM,
-                HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+                HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+                null,
             )
     }
 
     private fun vibrateOnError() {
         _hapticsToPlay.value =
             HapticsToPlay(
-                HapticFeedbackConstants.REJECT,
-                HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
+                HapticFeedbackConstants.BIOMETRIC_REJECT,
+                null,
             )
     }
 
@@ -1045,7 +1044,7 @@
     val packageName =
         when {
             componentNameForLogo != null -> componentNameForLogo.packageName
-            // TODO(b/339532378): We should check whether |allowBackgroundAuthentication| should be
+            // TODO(b/353597496): We should check whether |allowBackgroundAuthentication| should be
             // removed.
             // This is being consistent with the check in [AuthController.showDialog()].
             allowBackgroundAuthentication || isSystem(context, opPackageName) -> opPackageName
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
index 19ea007..c2a4ee3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModel.kt
@@ -28,7 +28,6 @@
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
 import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
 import com.airbnb.lottie.model.KeyPath
-import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.Utils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.SideFpsSensorInteractor
@@ -152,21 +151,6 @@
             ->
             val topLeft = Point(sensorLocation.left, sensorLocation.top)
 
-            if (!constraintBp()) {
-                if (sensorLocation.isSensorVerticalInDefaultOrientation) {
-                    if (displayRotation == DisplayRotation.ROTATION_0) {
-                        topLeft.x -= bounds!!.width()
-                    } else if (displayRotation == DisplayRotation.ROTATION_270) {
-                        topLeft.y -= bounds!!.height()
-                    }
-                } else {
-                    if (displayRotation == DisplayRotation.ROTATION_180) {
-                        topLeft.y -= bounds!!.height()
-                    } else if (displayRotation == DisplayRotation.ROTATION_270) {
-                        topLeft.x -= bounds!!.width()
-                    }
-                }
-            }
             defaultOverlayViewParams.apply {
                 x = topLeft.x
                 y = topLeft.y
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 e44f054..817f2d7 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/AudioSharingInteractor.kt
@@ -34,7 +34,9 @@
 
 internal sealed class AudioSharingButtonState {
     object Gone : AudioSharingButtonState()
-    data class Visible(@StringRes val resId: Int) : AudioSharingButtonState()
+
+    data class Visible(@StringRes val resId: Int, val isActive: Boolean) :
+        AudioSharingButtonState()
 }
 
 /** Holds business logic for the audio sharing state. */
@@ -73,7 +75,8 @@
             // Show sharing audio when broadcasting
             BluetoothUtils.isBroadcasting(localBluetoothManager) ->
                 AudioSharingButtonState.Visible(
-                    R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+                    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 {
@@ -85,7 +88,8 @@
             // 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
+                    R.string.quick_settings_bluetooth_audio_sharing_button,
+                    isActive = false
                 )
             else -> AudioSharingButtonState.Gone
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt
new file mode 100644
index 0000000..d69e416
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothDeviceMetadataInteractor.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.merge
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class BluetoothDeviceMetadataInteractor
+@Inject
+constructor(
+    deviceItemInteractor: DeviceItemInteractor,
+    private val bluetoothAdapter: BluetoothAdapter?,
+    private val logger: BluetoothTileDialogLogger,
+    @Background private val executor: Executor,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    private fun metadataUpdateForDevice(bluetoothDevice: BluetoothDevice): Flow<Unit> =
+        conflatedCallbackFlow {
+            val metadataChangedListener =
+                BluetoothAdapter.OnMetadataChangedListener { device, key, value ->
+                    when (key) {
+                        BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
+                        BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY,
+                        BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY,
+                        BluetoothDevice.METADATA_MAIN_BATTERY -> {
+                            trySendWithFailureLogging(Unit, TAG, "onMetadataChanged")
+                            logger.logBatteryChanged(device.address, key, value)
+                        }
+                    }
+                }
+            bluetoothAdapter?.addOnMetadataChangedListener(
+                bluetoothDevice,
+                executor,
+                metadataChangedListener
+            )
+            awaitClose {
+                bluetoothAdapter?.removeOnMetadataChangedListener(
+                    bluetoothDevice,
+                    metadataChangedListener
+                )
+            }
+        }
+
+    val metadataUpdate: Flow<Unit> =
+        deviceItemInteractor.deviceItemUpdate
+            .distinctUntilChangedBy { it.bluetoothDevices }
+            .flatMapLatest { items ->
+                items.bluetoothDevices.map { device -> metadataUpdateForDevice(device) }.merge()
+            }
+            .flowOn(backgroundDispatcher)
+
+    private companion object {
+        private const val TAG = "BluetoothDeviceMetadataInteractor"
+        private val List<DeviceItem>.bluetoothDevices: Set<BluetoothDevice>
+            get() =
+                flatMapTo(mutableSetOf()) { item ->
+                    listOf(item.cachedBluetoothDevice.device) +
+                        item.cachedBluetoothDevice.memberDevice.map { it.device }
+                }
+    }
+}
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 f5b9a05..7deea73 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -129,8 +129,26 @@
         getPairNewDeviceButton(dialog).setOnClickListener {
             bluetoothTileDialogCallback.onPairNewDeviceClicked(it)
         }
-        getAudioSharingButtonView(dialog).setOnClickListener {
-            bluetoothTileDialogCallback.onAudioSharingButtonClicked(it)
+        getAudioSharingButtonView(dialog).apply {
+            setOnClickListener { bluetoothTileDialogCallback.onAudioSharingButtonClicked(it) }
+            accessibilityDelegate =
+                object : AccessibilityDelegate() {
+                    override fun onInitializeAccessibilityNodeInfo(
+                        host: View,
+                        info: AccessibilityNodeInfo
+                    ) {
+                        super.onInitializeAccessibilityNodeInfo(host, info)
+                        info.addAction(
+                            AccessibilityAction(
+                                AccessibilityAction.ACTION_CLICK.id,
+                                context.getString(
+                                    R.string
+                                        .quick_settings_bluetooth_audio_sharing_button_accessibility
+                                )
+                            )
+                        )
+                    }
+                }
         }
         getScrollViewContent(dialog).apply {
             minimumHeight =
@@ -212,11 +230,13 @@
     internal fun onAudioSharingButtonUpdated(
         dialog: SystemUIDialog,
         visibility: Int,
-        label: String?
+        label: String?,
+        isActive: Boolean
     ) {
         getAudioSharingButtonView(dialog).apply {
             this.visibility = visibility
             label?.let { text = it }
+            this.isActivated = isActive
         }
     }
 
@@ -370,6 +390,7 @@
             private val iconView = view.requireViewById<ImageView>(R.id.bluetooth_device_icon)
             private val iconGear = view.requireViewById<ImageView>(R.id.gear_icon_image)
             private val gearView = view.requireViewById<View>(R.id.gear_icon)
+            private val divider = view.requireViewById<View>(R.id.divider)
 
             internal fun bind(
                 item: DeviceItem,
@@ -402,6 +423,8 @@
 
                     iconGear.apply { drawable?.let { it.mutate()?.setTint(tintColor) } }
 
+                    divider.setBackgroundColor(tintColor)
+
                     // update text styles
                     nameView.setTextAppearance(
                         if (item.isActive) R.style.BluetoothTileDialog_DeviceName_Active
@@ -440,7 +463,6 @@
 
     internal companion object {
         const val MIN_HEIGHT_CHANGE_INTERVAL_MS = 800L
-        const val MAX_DEVICE_ITEM_ENTRY = 3
         const val ACTION_BLUETOOTH_DEVICE_DETAILS =
             "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"
         const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
index 72312b8..06116f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt
@@ -90,6 +90,18 @@
             { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
         )
 
+    fun logBatteryChanged(address: String, key: Int, value: ByteArray?) =
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = address
+                int1 = key
+                str2 = value?.toString() ?: ""
+            },
+            { "BatteryChanged. address=$str1 key=$int1 value=$str2" }
+        )
+
     fun logDeviceFetch(status: JobStatus, trigger: DeviceFetchTrigger, duration: Long) =
         logBuffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt
index 6e51915..56b79d1 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogRepository.kt
@@ -24,7 +24,7 @@
 
 /** Repository to get CachedBluetoothDevices for the Bluetooth Dialog. */
 @SysUISingleton
-internal class BluetoothTileDialogRepository
+class BluetoothTileDialogRepository
 @Inject
 constructor(
     private val localBluetoothManager: LocalBluetoothManager?,
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 4a358c0..bdd4c16 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt
@@ -31,6 +31,8 @@
     @UiEvent(doc = "Saved clicked to connect") SAVED_DEVICE_CONNECT(1500),
     @UiEvent(doc = "Active device clicked to disconnect") ACTIVE_DEVICE_DISCONNECT(1507),
     @UiEvent(doc = "Audio sharing device clicked, do nothing") AUDIO_SHARING_DEVICE_CLICKED(1699),
+    @UiEvent(doc = "Available audio sharing device clicked, do nothing")
+    AVAILABLE_AUDIO_SHARING_DEVICE_CLICKED(1880),
     @UiEvent(doc = "Connected other device clicked to disconnect")
     CONNECTED_OTHER_DEVICE_DISCONNECT(1508),
     @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617),
@@ -44,8 +46,14 @@
         doc = "Not broadcasting, having one connected, another saved LE audio device is clicked"
     )
     LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719),
+    @Deprecated(
+        "Use case no longer needed",
+        ReplaceWith("LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED")
+    )
     @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked")
-    LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720);
+    LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720),
+    @UiEvent(doc = "Not broadcasting, having two connected, the active LE audio devices is clicked")
+    LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED(1881);
 
     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 eaddc42..a8f7fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModel.kt
@@ -29,6 +29,7 @@
 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.systemui.Prefs
 import com.android.systemui.animation.DialogCuj
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -37,7 +38,6 @@
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_BLUETOOTH_DEVICE_DETAILS
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PAIR_NEW_DEVICE
 import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.ACTION_PREVIOUSLY_CONNECTED_DEVICE
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogDelegate.Companion.MAX_DEVICE_ITEM_ENTRY
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -50,8 +50,10 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.channels.produce
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -66,6 +68,7 @@
     private val bluetoothStateInteractor: BluetoothStateInteractor,
     private val bluetoothAutoOnInteractor: BluetoothAutoOnInteractor,
     private val audioSharingInteractor: AudioSharingInteractor,
+    private val bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val activityStarter: ActivityStarter,
     private val uiEventLogger: UiEventLogger,
@@ -104,8 +107,7 @@
                     )
                 controller?.let {
                     dialogTransitionAnimator.show(dialog, it, animateBackgroundBoundsChange = true)
-                }
-                    ?: dialog.show()
+                } ?: dialog.show()
 
                 updateDeviceItemJob = launch {
                     deviceItemInteractor.updateDeviceItems(context, DeviceFetchTrigger.FIRST_LOAD)
@@ -113,15 +115,17 @@
 
                 // deviceItemUpdate is emitted when device item list is done fetching, update UI and
                 // stop the progress bar.
-                deviceItemInteractor.deviceItemUpdate
-                    .onEach {
+                combine(
+                        deviceItemInteractor.deviceItemUpdate,
+                        deviceItemInteractor.showSeeAllUpdate
+                    ) { deviceItem, showSeeAll ->
                         updateDialogUiJob?.cancel()
                         updateDialogUiJob = launch {
                             dialogDelegate.apply {
                                 onDeviceItemUpdated(
                                     dialog,
-                                    it.take(MAX_DEVICE_ITEM_ENTRY),
-                                    showSeeAll = it.size > MAX_DEVICE_ITEM_ENTRY,
+                                    deviceItem,
+                                    showSeeAll,
                                     showPairNewDevice =
                                         bluetoothStateInteractor.isBluetoothEnabled()
                                 )
@@ -132,8 +136,11 @@
                     .launchIn(this)
 
                 // deviceItemUpdateRequest is emitted when a bluetooth callback is called, re-fetch
-                // the device item list and animiate the progress bar.
-                deviceItemInteractor.deviceItemUpdateRequest
+                // the device item list and animate the progress bar.
+                merge(
+                        deviceItemInteractor.deviceItemUpdateRequest,
+                        bluetoothDeviceMetadataInteractor.metadataUpdate
+                    )
                     .onEach {
                         dialogDelegate.animateProgressBar(dialog, true)
                         updateDeviceItemJob?.cancel()
@@ -149,14 +156,23 @@
                 if (BluetoothUtils.isAudioSharingEnabled()) {
                     audioSharingInteractor.audioSharingButtonStateUpdate
                         .onEach {
-                            if (it is AudioSharingButtonState.Visible) {
-                                dialogDelegate.onAudioSharingButtonUpdated(
-                                    dialog,
-                                    VISIBLE,
-                                    context.getString(it.resId)
-                                )
-                            } else {
-                                dialogDelegate.onAudioSharingButtonUpdated(dialog, GONE, null)
+                            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)
@@ -256,7 +272,7 @@
         val intent =
             Intent(ACTION_BLUETOOTH_DEVICE_DETAILS).apply {
                 putExtra(
-                    ":settings:show_fragment_args",
+                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
                     Bundle().apply {
                         putString("device_address", deviceItem.cachedBluetoothDevice.address)
                     }
@@ -277,7 +293,16 @@
 
     override fun onAudioSharingButtonClicked(view: View) {
         uiEventLogger.log(BluetoothTileDialogUiEvent.BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED)
-        startSettingsActivity(Intent(ACTION_AUDIO_SHARING), view)
+        val intent =
+            Intent(ACTION_AUDIO_SHARING).apply {
+                putExtra(
+                    EXTRA_SHOW_FRAGMENT_ARGUMENTS,
+                    Bundle().apply {
+                        putBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING, true)
+                    }
+                )
+            }
+        startSettingsActivity(intent, view)
     }
 
     private fun cancelJob() {
@@ -305,6 +330,8 @@
     companion object {
         private const val INTERACTION_JANK_TAG = "bluetooth_tile_dialog"
         private const val CONTENT_HEIGHT_PREF_KEY = Prefs.Key.BLUETOOTH_TILE_DIALOG_CONTENT_HEIGHT
+        private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"
+
         private fun getSubtitleResId(isBluetoothEnabled: Boolean) =
             if (isBluetoothEnabled) R.string.quick_settings_bluetooth_tile_subtitle
             else R.string.bt_is_off
@@ -336,7 +363,10 @@
 
 interface BluetoothTileDialogCallback {
     fun onDeviceItemGearClicked(deviceItem: DeviceItem, view: View)
+
     fun onSeeAllClicked(view: View)
+
     fun onPairNewDeviceClicked(view: View)
+
     fun onAudioSharingButtonClicked(view: View)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
index a78130f..2ba4c73 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItem.kt
@@ -38,6 +38,7 @@
 enum class DeviceItemType {
     ACTIVE_MEDIA_BLUETOOTH_DEVICE,
     AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
+    AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
     AVAILABLE_MEDIA_BLUETOOTH_DEVICE,
     CONNECTED_BLUETOOTH_DEVICE,
     SAVED_BLUETOOTH_DEVICE,
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 9d82e76..f1894d3 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt
@@ -67,7 +67,7 @@
                     backgroundDispatcher,
                     logger
                 ),
-                NotSharingClickedConnected(
+                NotSharingClickedActive(
                     leAudioProfile,
                     assistantProfile,
                     backgroundDispatcher,
@@ -106,6 +106,12 @@
                     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)
@@ -238,14 +244,14 @@
             BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED
     }
 
-    private class NotSharingClickedConnected(
+    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 any connected LE audio
-        // devices
+        // 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 =
@@ -259,7 +265,7 @@
                                         logger
                                     )
                                     .size == 2 &&
-                                deviceItem.isActiveOrConnectedLeAudioSupported
+                                deviceItem.isActiveLeAudioSupported
                         }
                     } ?: false
 
@@ -275,7 +281,7 @@
         }
 
         override suspend fun getClickUiEvent(deviceItem: DeviceItem) =
-            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED
+            BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_ACTIVE_LE_DEVICE_CLICKED
     }
 
     private companion object {
@@ -290,10 +296,8 @@
         val DeviceItem.isNotConnectedLeAudioSupported: Boolean
             get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported
 
-        val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean
-            get() =
-                (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE ||
-                    type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported
+        val DeviceItem.isActiveLeAudioSupported: Boolean
+            get() = type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE && isLeAudioSupported
 
         val DeviceItem.isMediaDevice: Boolean
             get() =
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 d7893db..7280489 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemFactory.kt
@@ -38,7 +38,7 @@
     R.string.accessibility_quick_settings_bluetooth_device_tap_to_disconnect
 
 /** Factories to create different types of Bluetooth device items from CachedBluetoothDevice. */
-internal abstract class DeviceItemFactory {
+abstract class DeviceItemFactory {
     abstract fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
@@ -125,6 +125,37 @@
     }
 }
 
+internal class AvailableAudioSharingMediaDeviceItemFactory(
+    private val localBluetoothManager: LocalBluetoothManager?
+) : AvailableMediaDeviceItemFactory() {
+    override fun isFilterMatched(
+        context: Context,
+        cachedDevice: CachedBluetoothDevice,
+        audioManager: AudioManager
+    ): Boolean {
+        return BluetoothUtils.isAudioSharingEnabled() &&
+            super.isFilterMatched(context, cachedDevice, audioManager) &&
+            BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(
+                cachedDevice,
+                localBluetoothManager
+            )
+    }
+
+    override fun create(context: Context, cachedDevice: CachedBluetoothDevice): DeviceItem {
+        return createDeviceItem(
+            context,
+            cachedDevice,
+            DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
+            context.getString(
+                R.string.quick_settings_bluetooth_device_audio_sharing_or_switch_active
+            ),
+            if (cachedDevice.isBusy) backgroundOffBusy else backgroundOff,
+            "",
+            isActive = false
+        )
+    }
+}
+
 internal class ActiveHearingDeviceItemFactory : ActiveMediaDeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
@@ -136,7 +167,7 @@
     }
 }
 
-internal open class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
+open class AvailableMediaDeviceItemFactory : DeviceItemFactory() {
     override fun isFilterMatched(
         context: Context,
         cachedDevice: CachedBluetoothDevice,
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 1526cd9..9114eca 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemInteractor.kt
@@ -34,16 +34,18 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.isActive
 import kotlinx.coroutines.withContext
 
 /** Holds business logic for the Bluetooth Dialog after clicking on the Bluetooth QS tile. */
 @SysUISingleton
-internal class DeviceItemInteractor
+class DeviceItemInteractor
 @Inject
 constructor(
     private val bluetoothTileDialogRepository: BluetoothTileDialogRepository,
@@ -58,9 +60,13 @@
 
     private val mutableDeviceItemUpdate: MutableSharedFlow<List<DeviceItem>> =
         MutableSharedFlow(extraBufferCapacity = 1)
-    internal val deviceItemUpdate
+    val deviceItemUpdate
         get() = mutableDeviceItemUpdate.asSharedFlow()
 
+    private val mutableShowSeeAllUpdate: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    internal val showSeeAllUpdate
+        get() = mutableShowSeeAllUpdate.asStateFlow()
+
     internal val deviceItemUpdateRequest: SharedFlow<Unit> =
         conflatedCallbackFlow {
                 val listener =
@@ -112,6 +118,7 @@
         listOf(
             ActiveMediaDeviceItemFactory(),
             AudioSharingMediaDeviceItemFactory(localBluetoothManager),
+            AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager),
             AvailableMediaDeviceItemFactory(),
             ConnectedDeviceItemFactory(),
             SavedDeviceItemFactory()
@@ -121,6 +128,7 @@
         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,
@@ -139,7 +147,8 @@
                     .sort(displayPriority, bluetoothAdapter?.mostRecentlyConnectedDevices)
             // Only emit when the job is not cancelled
             if (isActive) {
-                mutableDeviceItemUpdate.tryEmit(deviceItems)
+                mutableDeviceItemUpdate.tryEmit(deviceItems.take(MAX_DEVICE_ITEM_ENTRY))
+                mutableShowSeeAllUpdate.tryEmit(deviceItems.size > MAX_DEVICE_ITEM_ENTRY)
                 logger.logDeviceFetch(
                     JobStatus.FINISHED,
                     trigger,
@@ -177,5 +186,6 @@
 
     companion object {
         private const val TAG = "DeviceItemInteractor"
+        private const val MAX_DEVICE_ITEM_ENTRY = 3
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
index 094dc0a..e939f37 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.data.repository
 
+import android.hardware.biometrics.BiometricSourceType
 import com.android.systemui.bouncer.shared.model.BouncerMessageModel
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
@@ -26,7 +27,12 @@
 interface BouncerMessageRepository {
     val bouncerMessage: Flow<BouncerMessageModel>
 
-    fun setMessage(message: BouncerMessageModel)
+    fun setMessage(message: BouncerMessageModel, source: BiometricSourceType? = null)
+
+    /**
+     * Return the source of the current [bouncerMessage] if it was set from a biometric. Else, null.
+     */
+    fun getMessageSource(): BiometricSourceType?
 }
 
 @SysUISingleton
@@ -34,8 +40,14 @@
 
     private val _bouncerMessage = MutableStateFlow(BouncerMessageModel())
     override val bouncerMessage: Flow<BouncerMessageModel> = _bouncerMessage
+    private var messageSource: BiometricSourceType? = null
 
-    override fun setMessage(message: BouncerMessageModel) {
+    override fun setMessage(message: BouncerMessageModel, source: BiometricSourceType?) {
         _bouncerMessage.value = message
+        messageSource = source
+    }
+
+    override fun getMessageSource(): BiometricSourceType? {
+        return messageSource
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index 8e12646..d125c36 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
+import android.hardware.biometrics.BiometricFaceConstants
 import android.hardware.biometrics.BiometricSourceType
 import android.os.CountDownTimer
 import com.android.keyguard.KeyguardSecurityModel
@@ -32,9 +33,7 @@
 import com.android.systemui.bouncer.shared.model.Message
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.data.repository.DeviceEntryFaceAuthRepository
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor
 import com.android.systemui.flags.SystemPropertiesHelper
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
@@ -74,8 +73,6 @@
     primaryBouncerInteractor: PrimaryBouncerInteractor,
     @Application private val applicationScope: CoroutineScope,
     private val facePropertyRepository: FacePropertyRepository,
-    private val deviceEntryFingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
-    faceAuthRepository: DeviceEntryFaceAuthRepository,
     private val securityModel: KeyguardSecurityModel,
     deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
 ) {
@@ -96,6 +93,17 @@
     private val kumCallback =
         object : KeyguardUpdateMonitorCallback() {
             override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
+                // Only show the biometric failure messages if the biometric is NOT locked out.
+                // If the biometric is locked out, rely on the lock out message to show
+                // the lockout message & don't override it with the failure message.
+                if (
+                    (biometricSourceType == BiometricSourceType.FACE &&
+                        deviceEntryBiometricsAllowedInteractor.isFaceLockedOut.value) ||
+                        (biometricSourceType == BiometricSourceType.FINGERPRINT &&
+                            deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut.value)
+                ) {
+                    return
+                }
                 repository.setMessage(
                     when (biometricSourceType) {
                         BiometricSourceType.FINGERPRINT ->
@@ -115,7 +123,8 @@
                                     isFingerprintAuthCurrentlyAllowedOnBouncer.value
                                 )
                                 .toMessage()
-                    }
+                    },
+                    biometricSourceType,
                 )
             }
 
@@ -123,7 +132,12 @@
                 biometricSourceType: BiometricSourceType?,
                 acquireInfo: Int
             ) {
-                super.onBiometricAcquired(biometricSourceType, acquireInfo)
+                if (
+                    repository.getMessageSource() == BiometricSourceType.FACE &&
+                        acquireInfo == BiometricFaceConstants.FACE_ACQUIRED_START
+                ) {
+                    repository.setMessage(defaultMessage)
+                }
             }
 
             override fun onBiometricAuthenticated(
@@ -131,7 +145,7 @@
                 biometricSourceType: BiometricSourceType?,
                 isStrongBiometric: Boolean
             ) {
-                repository.setMessage(defaultMessage)
+                repository.setMessage(defaultMessage, biometricSourceType)
             }
         }
 
@@ -152,8 +166,8 @@
                 biometricSettingsRepository.authenticationFlags,
                 trustRepository.isCurrentUserTrustManaged,
                 isAnyBiometricsEnabledAndEnrolled,
-                deviceEntryFingerprintAuthInteractor.isLockedOut,
-                faceAuthRepository.isLockedOut,
+                deviceEntryBiometricsAllowedInteractor.isFingerprintLockedOut,
+                deviceEntryBiometricsAllowedInteractor.isFaceLockedOut,
                 isFingerprintAuthCurrentlyAllowedOnBouncer,
                 ::Septuple
             )
@@ -291,7 +305,8 @@
                 currentSecurityMode,
                 value,
                 isFingerprintAuthCurrentlyAllowedOnBouncer.value
-            )
+            ),
+            BiometricSourceType.FINGERPRINT,
         )
     }
 
@@ -302,7 +317,8 @@
                 currentSecurityMode,
                 value,
                 isFingerprintAuthCurrentlyAllowedOnBouncer.value
-            )
+            ),
+            BiometricSourceType.FACE,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
index 62ef365..a1111f6 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlags.kt
@@ -17,18 +17,17 @@
 package com.android.systemui.bouncer.shared.flag
 
 import com.android.systemui.Flags
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import dagger.Module
-import dagger.Provides
 
-interface ComposeBouncerFlags {
+object ComposeBouncerFlags {
 
     /**
      * Returns `true` if the Compose bouncer is enabled or if the scene container framework is
      * enabled; `false` otherwise.
      */
-    fun isComposeBouncerOrSceneContainerEnabled(): Boolean
+    fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
+        return SceneContainerFlag.isEnabled || Flags.composeBouncer()
+    }
 
     /**
      * Returns `true` if only compose bouncer is enabled and scene container framework is not
@@ -39,30 +38,7 @@
             "that includes compose bouncer in legacy keyguard.",
         replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
     )
-    fun isOnlyComposeBouncerEnabled(): Boolean
-}
-
-class ComposeBouncerFlagsImpl() : ComposeBouncerFlags {
-
-    override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
-        return SceneContainerFlag.isEnabled || Flags.composeBouncer()
-    }
-
-    @Deprecated(
-        "Avoid using this, this is meant to be used only by the glue code " +
-            "that includes compose bouncer in legacy keyguard.",
-        replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
-    )
-    override fun isOnlyComposeBouncerEnabled(): Boolean {
+    fun isOnlyComposeBouncerEnabled(): Boolean {
         return !SceneContainerFlag.isEnabled && Flags.composeBouncer()
     }
 }
-
-@Module
-object ComposeBouncerFlagsModule {
-    @Provides
-    @SysUISingleton
-    fun impl(): ComposeBouncerFlags {
-        return ComposeBouncerFlagsImpl()
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
index aebc50f..3410782 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
@@ -18,8 +18,6 @@
 
 import android.app.AlertDialog
 import android.content.Context
-import com.android.systemui.bouncer.ui.viewmodel.BouncerMessageViewModelModule
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModelModule
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -27,13 +25,7 @@
 import dagger.Module
 import dagger.Provides
 
-@Module(
-    includes =
-        [
-            BouncerViewModelModule::class,
-            BouncerMessageViewModelModule::class,
-        ],
-)
+@Module
 interface BouncerViewModule {
     /** Binds BouncerView to BouncerViewImpl and makes it injectable. */
     @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
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 78811a9..49dadce 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
@@ -2,14 +2,12 @@
 
 import android.view.ViewGroup
 import com.android.keyguard.KeyguardMessageAreaController
-import com.android.keyguard.ViewMediatorCallback
 import com.android.keyguard.dagger.KeyguardBouncerComponent
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 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.BouncerViewModel
+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.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
@@ -39,12 +37,9 @@
 data class ComposeBouncerDependencies
 @Inject
 constructor(
-    val legacyInteractor: PrimaryBouncerInteractor,
-    val viewModel: BouncerViewModel,
+    val viewModelFactory: BouncerSceneContentViewModel.Factory,
     val dialogFactory: BouncerDialogFactory,
-    val authenticationInteractor: AuthenticationInteractor,
-    val viewMediatorCallback: ViewMediatorCallback?,
-    val selectedUserInteractor: SelectedUserInteractor,
+    val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
 )
 
 /**
@@ -55,21 +50,17 @@
 class BouncerViewBinder
 @Inject
 constructor(
-    private val composeBouncerFlags: ComposeBouncerFlags,
     private val legacyBouncerDependencies: Lazy<LegacyBouncerDependencies>,
     private val composeBouncerDependencies: Lazy<ComposeBouncerDependencies>,
 ) {
     fun bind(view: ViewGroup) {
-        if (composeBouncerFlags.isOnlyComposeBouncerEnabled()) {
+        if (ComposeBouncerFlags.isOnlyComposeBouncerEnabled()) {
             val deps = composeBouncerDependencies.get()
             ComposeBouncerViewBinder.bind(
                 view,
-                deps.legacyInteractor,
-                deps.viewModel,
+                deps.viewModelFactory,
                 deps.dialogFactory,
-                deps.authenticationInteractor,
-                deps.selectedUserInteractor,
-                deps.viewMediatorCallback,
+                deps.bouncerContainerViewModelFactory,
             )
         } else {
             val deps = legacyBouncerDependencies.get()
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 eaca276..b5e54d5 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
@@ -5,90 +5,55 @@
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.ui.platform.ComposeView
-import androidx.core.view.isVisible
+import androidx.core.view.isGone
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.compose.theme.PlatformTheme
-import com.android.keyguard.ViewMediatorCallback
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
-import com.android.systemui.bouncer.ui.composable.BouncerContent
-import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+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.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import kotlinx.coroutines.awaitCancellation
 
 /** View binder responsible for binding the compose version of the bouncer. */
 object ComposeBouncerViewBinder {
     fun bind(
         view: ViewGroup,
-        legacyInteractor: PrimaryBouncerInteractor,
-        viewModel: BouncerViewModel,
+        viewModelFactory: BouncerSceneContentViewModel.Factory,
         dialogFactory: BouncerDialogFactory,
-        authenticationInteractor: AuthenticationInteractor,
-        selectedUserInteractor: SelectedUserInteractor,
-        viewMediatorCallback: ViewMediatorCallback?,
+        bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
     ) {
-        view.addView(
-            ComposeView(view.context).apply {
-                repeatWhenAttached {
-                    repeatOnLifecycle(Lifecycle.State.CREATED) {
-                        setViewTreeOnBackPressedDispatcherOwner(
-                            object : OnBackPressedDispatcherOwner {
-                                override val onBackPressedDispatcher =
-                                    OnBackPressedDispatcher().apply {
-                                        setOnBackInvokedDispatcher(
-                                            view.viewRootImpl.onBackInvokedDispatcher
-                                        )
-                                    }
-
-                                override val lifecycle: Lifecycle =
-                                    [email protected]
-                            }
-                        )
-                        setContent { PlatformTheme { BouncerContent(viewModel, dialogFactory) } }
-                    }
-                }
-            }
-        )
-
         view.repeatWhenAttached {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                launch {
-                    legacyInteractor.isShowing.collectLatest { bouncerShowing ->
-                        view.isVisible = bouncerShowing
-                    }
-                }
-
-                launch {
-                    authenticationInteractor.onAuthenticationResult.collectLatest {
-                        authenticationSucceeded ->
-                        if (authenticationSucceeded) {
-                            // Some dismiss actions require that keyguard be dismissed right away or
-                            // deferred until something else later on dismisses keyguard (eg. end of
-                            // a hide animation).
-                            val deferKeyguardDone =
-                                legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
-                            legacyInteractor.setDismissAction(null, null)
-
-                            viewMediatorCallback?.let {
-                                val selectedUserId = selectedUserInteractor.getSelectedUserId()
-                                if (deferKeyguardDone == true) {
-                                    it.keyguardDonePending(selectedUserId)
-                                } else {
-                                    it.keyguardDone(selectedUserId)
+            view.viewModel(
+                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                factory = { bouncerContainerViewModelFactory.create() },
+                traceName = "ComposeBouncerViewBinder",
+            ) { viewModel ->
+                try {
+                    view.setViewTreeOnBackPressedDispatcherOwner(
+                        object : OnBackPressedDispatcherOwner {
+                            override val onBackPressedDispatcher =
+                                OnBackPressedDispatcher().apply {
+                                    setOnBackInvokedDispatcher(
+                                        view.viewRootImpl.onBackInvokedDispatcher
+                                    )
                                 }
-                            }
+
+                            override val lifecycle: Lifecycle = [email protected]
                         }
-                    }
-                }
-                launch {
-                    legacyInteractor.startingDisappearAnimation.collectLatest {
-                        it.run()
-                        legacyInteractor.hide()
-                    }
+                    )
+
+                    view.addView(
+                        ComposeView(view.context).apply {
+                            setContent { BouncerContainer(viewModelFactory, dialogFactory) }
+                        }
+                    )
+                    view.setSnapshotBinding { view.isGone = !viewModel.isVisible }
+                    awaitCancellation()
+                } finally {
+                    view.removeAllViews()
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.kt
new file mode 100644
index 0000000..c05dcd5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/composable/BouncerContainer.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.bouncer.ui.composable
+
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.bouncer.ui.BouncerDialogFactory
+import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.rememberViewModel
+
+/** Container that includes the compose bouncer and is meant to be included in legacy keyguard. */
+@Composable
+fun BouncerContainer(
+    viewModelFactory: BouncerSceneContentViewModel.Factory,
+    dialogFactory: BouncerDialogFactory,
+) {
+    PlatformTheme {
+        val backgroundColor = MaterialTheme.colorScheme.surface
+
+        val bouncerViewModel = rememberViewModel("BouncerContainer") { viewModelFactory.create() }
+        Box {
+            Canvas(Modifier.fillMaxSize()) { drawRect(color = backgroundColor) }
+
+            // Separate the bouncer content into a reusable composable that
+            // doesn't have any SceneScope
+            // dependencies
+            BouncerContent(
+                bouncerViewModel,
+                dialogFactory,
+                Modifier.sysuiResTag(Bouncer.TestTags.Root).fillMaxSize()
+            )
+        }
+    }
+}
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 4fbf735..c67b354 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,17 +17,19 @@
 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
-import kotlinx.coroutines.CoroutineScope
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.receiveAsFlow
 
 sealed class AuthMethodBouncerViewModel(
-    protected val viewModelScope: CoroutineScope,
     protected val interactor: BouncerInteractor,
 
     /**
@@ -37,7 +39,10 @@
      * being able to attempt to unlock the device.
      */
     val isInputEnabled: StateFlow<Boolean>,
-) {
+
+    /** Name to use for performance tracing purposes. */
+    val traceName: String,
+) : ExclusiveActivatable() {
 
     private val _animateFailure = MutableStateFlow(false)
     /**
@@ -57,6 +62,30 @@
      */
     @get:StringRes abstract val lockoutMessageId: Int
 
+    private val authenticationRequests = Channel<AuthenticationRequest>(Channel.BUFFERED)
+
+    override suspend fun onActivated(): Nothing {
+        authenticationRequests.receiveAsFlow().collectLatest { request ->
+            if (!isInputEnabled.value) {
+                return@collectLatest
+            }
+
+            val authenticationResult =
+                interactor.authenticate(
+                    input = request.input,
+                    tryAutoConfirm = request.useAutoConfirm,
+                )
+
+            if (authenticationResult == AuthenticationResult.SKIPPED && request.useAutoConfirm) {
+                return@collectLatest
+            }
+
+            _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
+            clearInput()
+        }
+        awaitCancellation()
+    }
+
     /**
      * Notifies that the UI has been hidden from the user (after any transitions have completed).
      */
@@ -92,14 +121,11 @@
         input: List<Any> = getInput(),
         useAutoConfirm: Boolean = false,
     ) {
-        viewModelScope.launch {
-            val authenticationResult = interactor.authenticate(input, useAutoConfirm)
-            if (authenticationResult == AuthenticationResult.SKIPPED && useAutoConfirm) {
-                return@launch
-            }
-            _animateFailure.value = authenticationResult != AuthenticationResult.SUCCEEDED
-
-            clearInput()
-        }
+        authenticationRequests.trySend(AuthenticationRequest(input, useAutoConfirm))
     }
+
+    private data class AuthenticationRequest(
+        val input: List<Any>,
+        val useAutoConfirm: Boolean,
+    )
 }
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
new file mode 100644
index 0000000..d223657
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -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.systemui.bouncer.ui.viewmodel
+
+import androidx.compose.runtime.getValue
+import com.android.keyguard.ViewMediatorCallback
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+class BouncerContainerViewModel
+@AssistedInject
+constructor(
+    private val legacyInteractor: PrimaryBouncerInteractor,
+    private val authenticationInteractor: AuthenticationInteractor,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val viewMediatorCallback: ViewMediatorCallback?,
+) : ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("BouncerContainerViewModel")
+
+    val isVisible: Boolean by
+        hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch {
+                authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded ->
+                    if (authenticationSucceeded) {
+                        // Some dismiss actions require that keyguard be dismissed right away or
+                        // deferred until something else later on dismisses keyguard (eg. end of
+                        // a hide animation).
+                        val deferKeyguardDone =
+                            legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
+                        legacyInteractor.setDismissAction(null, null)
+
+                        viewMediatorCallback?.let {
+                            val selectedUserId = selectedUserInteractor.getSelectedUserId()
+                            if (deferKeyguardDone == true) {
+                                it.keyguardDonePending(selectedUserId)
+                            } else {
+                                it.keyguardDone(selectedUserId)
+                            }
+                        }
+                    }
+                }
+            }
+
+            launch {
+                legacyInteractor.startingDisappearAnimation.collect {
+                    it.run()
+                    legacyInteractor.hide()
+                }
+            }
+
+            hydrator.activate()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): BouncerContainerViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
index 31479f1..c383b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModel.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.bouncer.shared.model.BouncerMessageStrings
 import com.android.systemui.bouncer.shared.model.primaryMessage
 import com.android.systemui.bouncer.shared.model.secondaryMessage
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
@@ -39,19 +38,20 @@
 import com.android.systemui.deviceentry.shared.model.FaceTimeoutMessage
 import com.android.systemui.deviceentry.shared.model.FingerprintFailureMessage
 import com.android.systemui.deviceentry.shared.model.FingerprintLockoutMessage
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.res.R.string.kg_too_many_failed_attempts_countdown
 import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.user.ui.viewmodel.UserViewModel
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.time.SystemClock
-import dagger.Module
-import dagger.Provides
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlin.math.ceil
 import kotlin.math.max
 import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -65,20 +65,20 @@
 
 /** Holds UI state for the 2-line status message shown on the bouncer. */
 @OptIn(ExperimentalCoroutinesApi::class)
-class BouncerMessageViewModel(
+class BouncerMessageViewModel
+@AssistedInject
+constructor(
     @Application private val applicationContext: Context,
-    @Application private val applicationScope: CoroutineScope,
     private val bouncerInteractor: BouncerInteractor,
     private val simBouncerInteractor: SimBouncerInteractor,
     private val authenticationInteractor: AuthenticationInteractor,
-    selectedUser: Flow<UserViewModel>,
+    private val userSwitcherViewModel: UserSwitcherViewModel,
     private val clock: SystemClock,
     private val biometricMessageInteractor: BiometricMessageInteractor,
     private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
     private val deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
-    flags: ComposeBouncerFlags,
-) {
+) : ExclusiveActivatable() {
     /**
      * A message shown when the user has attempted the wrong credential too many times and now must
      * wait a while before attempting to authenticate again.
@@ -94,6 +94,26 @@
     /** The user-facing message to show in the bouncer. */
     val message: MutableStateFlow<MessageViewModel?> = MutableStateFlow(null)
 
+    override suspend fun onActivated(): Nothing {
+        if (!ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
+            return awaitCancellation()
+        }
+
+        coroutineScope {
+            launch {
+                // Update the lockout countdown whenever the selected user is switched.
+                userSwitcherViewModel.selectedUser.collect { startLockoutCountdown() }
+            }
+
+            launch { defaultBouncerMessageInitializer() }
+            launch { listenForSimBouncerEvents() }
+            launch { listenForBouncerEvents() }
+            launch { listenForFaceMessages() }
+            launch { listenForFingerprintMessages() }
+            awaitCancellation()
+        }
+    }
+
     /** Initializes the bouncer message to default whenever it is shown. */
     fun onShown() {
         showDefaultMessage()
@@ -108,173 +128,164 @@
 
     private var lockoutCountdownJob: Job? = null
 
-    private fun defaultBouncerMessageInitializer() {
-        applicationScope.launch {
-            resetToDefault.emit(Unit)
-            authenticationInteractor.authenticationMethod
-                .flatMapLatest { authMethod ->
-                    if (authMethod == AuthenticationMethodModel.Sim) {
-                        resetToDefault.map {
-                            MessageViewModel(simBouncerInteractor.getDefaultMessage())
-                        }
-                    } else if (authMethod.isSecure) {
-                        combine(
-                            deviceUnlockedInteractor.deviceEntryRestrictionReason,
-                            lockoutMessage,
-                            deviceEntryBiometricsAllowedInteractor
-                                .isFingerprintCurrentlyAllowedOnBouncer,
-                            resetToDefault,
-                        ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
-                            lockoutMsg
-                                ?: deviceEntryRestrictedReason.toMessage(
-                                    authMethod,
-                                    isFpAllowedInBouncer
-                                )
-                        }
-                    } else {
-                        emptyFlow()
+    private suspend fun defaultBouncerMessageInitializer() {
+        resetToDefault.emit(Unit)
+        authenticationInteractor.authenticationMethod
+            .flatMapLatest { authMethod ->
+                if (authMethod == AuthenticationMethodModel.Sim) {
+                    resetToDefault.map {
+                        MessageViewModel(simBouncerInteractor.getDefaultMessage())
                     }
+                } else if (authMethod.isSecure) {
+                    combine(
+                        deviceUnlockedInteractor.deviceEntryRestrictionReason,
+                        lockoutMessage,
+                        deviceEntryBiometricsAllowedInteractor
+                            .isFingerprintCurrentlyAllowedOnBouncer,
+                        resetToDefault,
+                    ) { deviceEntryRestrictedReason, lockoutMsg, isFpAllowedInBouncer, _ ->
+                        lockoutMsg
+                            ?: deviceEntryRestrictedReason.toMessage(
+                                authMethod,
+                                isFpAllowedInBouncer
+                            )
+                    }
+                } else {
+                    emptyFlow()
                 }
-                .collectLatest { messageViewModel -> message.value = messageViewModel }
-        }
+            }
+            .collect { messageViewModel -> message.value = messageViewModel }
     }
 
-    private fun listenForSimBouncerEvents() {
+    private suspend fun listenForSimBouncerEvents() {
         // Listen for any events from the SIM bouncer and update the message shown on the bouncer.
-        applicationScope.launch {
-            authenticationInteractor.authenticationMethod
-                .flatMapLatest { authMethod ->
-                    if (authMethod == AuthenticationMethodModel.Sim) {
-                        simBouncerInteractor.bouncerMessageChanged.map { simMsg ->
-                            simMsg?.let { MessageViewModel(it) }
-                        }
-                    } else {
-                        emptyFlow()
+        authenticationInteractor.authenticationMethod
+            .flatMapLatest { authMethod ->
+                if (authMethod == AuthenticationMethodModel.Sim) {
+                    simBouncerInteractor.bouncerMessageChanged.map { simMsg ->
+                        simMsg?.let { MessageViewModel(it) }
                     }
+                } else {
+                    emptyFlow()
                 }
-                .collectLatest {
-                    if (it != null) {
-                        message.value = it
-                    } else {
-                        resetToDefault.emit(Unit)
-                    }
+            }
+            .collect {
+                if (it != null) {
+                    message.value = it
+                } else {
+                    resetToDefault.emit(Unit)
                 }
-        }
+            }
     }
 
-    private fun listenForFaceMessages() {
+    private suspend fun listenForFaceMessages() {
         // Listen for any events from face authentication and update the message shown on the
         // bouncer.
-        applicationScope.launch {
-            biometricMessageInteractor.faceMessage
-                .sample(
-                    authenticationInteractor.authenticationMethod,
-                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
-                )
-                .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
-                    val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
-                    val defaultPrimaryMessage =
-                        BouncerMessageStrings.defaultMessage(
-                                authMethod,
-                                fingerprintAllowedOnBouncer
+        biometricMessageInteractor.faceMessage
+            .sample(
+                authenticationInteractor.authenticationMethod,
+                deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer,
+            )
+            .collectLatest { (faceMessage, authMethod, fingerprintAllowedOnBouncer) ->
+                val isFaceAuthStrong = faceAuthInteractor.isFaceAuthStrong()
+                val defaultPrimaryMessage =
+                    BouncerMessageStrings.defaultMessage(authMethod, fingerprintAllowedOnBouncer)
+                        .primaryMessage
+                        .toResString()
+                message.value =
+                    when (faceMessage) {
+                        is FaceTimeoutMessage ->
+                            MessageViewModel(
+                                text = defaultPrimaryMessage,
+                                secondaryText = faceMessage.message,
+                                isUpdateAnimated = true
                             )
-                            .primaryMessage
-                            .toResString()
-                    message.value =
-                        when (faceMessage) {
-                            is FaceTimeoutMessage ->
-                                MessageViewModel(
-                                    text = defaultPrimaryMessage,
-                                    secondaryText = faceMessage.message,
-                                    isUpdateAnimated = true
-                                )
-                            is FaceLockoutMessage ->
-                                if (isFaceAuthStrong)
-                                    BouncerMessageStrings.class3AuthLockedOut(authMethod)
-                                        .toMessage()
-                                else
-                                    BouncerMessageStrings.faceLockedOut(
-                                            authMethod,
-                                            fingerprintAllowedOnBouncer
-                                        )
-                                        .toMessage()
-                            is FaceFailureMessage ->
-                                BouncerMessageStrings.incorrectFaceInput(
+                        is FaceLockoutMessage ->
+                            if (isFaceAuthStrong)
+                                BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
+                            else
+                                BouncerMessageStrings.faceLockedOut(
                                         authMethod,
                                         fingerprintAllowedOnBouncer
                                     )
                                     .toMessage()
-                            else ->
-                                MessageViewModel(
-                                    text = defaultPrimaryMessage,
-                                    secondaryText = faceMessage.message,
-                                    isUpdateAnimated = false
+                        is FaceFailureMessage ->
+                            BouncerMessageStrings.incorrectFaceInput(
+                                    authMethod,
+                                    fingerprintAllowedOnBouncer
                                 )
-                        }
-                    delay(MESSAGE_DURATION)
-                    resetToDefault.emit(Unit)
-                }
-        }
-    }
-
-    private fun listenForFingerprintMessages() {
-        applicationScope.launch {
-            // Listen for any events from fingerprint authentication and update the message shown
-            // on the bouncer.
-            biometricMessageInteractor.fingerprintMessage
-                .sample(
-                    authenticationInteractor.authenticationMethod,
-                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
-                )
-                .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
-                    val defaultPrimaryMessage =
-                        BouncerMessageStrings.defaultMessage(authMethod, isFingerprintAllowed)
-                            .primaryMessage
-                            .toResString()
-                    message.value =
-                        when (fingerprintMessage) {
-                            is FingerprintLockoutMessage ->
-                                BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
-                            is FingerprintFailureMessage ->
-                                BouncerMessageStrings.incorrectFingerprintInput(authMethod)
-                                    .toMessage()
-                            else ->
-                                MessageViewModel(
-                                    text = defaultPrimaryMessage,
-                                    secondaryText = fingerprintMessage.message,
-                                    isUpdateAnimated = false
-                                )
-                        }
-                    delay(MESSAGE_DURATION)
-                    resetToDefault.emit(Unit)
-                }
-        }
-    }
-
-    private fun listenForBouncerEvents() {
-        // Keeps the lockout message up-to-date.
-        applicationScope.launch {
-            bouncerInteractor.onLockoutStarted.collect { startLockoutCountdown() }
-        }
-
-        // Listens to relevant bouncer events
-        applicationScope.launch {
-            bouncerInteractor.onIncorrectBouncerInput
-                .sample(
-                    authenticationInteractor.authenticationMethod,
-                    deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
-                )
-                .collectLatest { (_, authMethod, isFingerprintAllowed) ->
-                    message.emit(
-                        BouncerMessageStrings.incorrectSecurityInput(
-                                authMethod,
-                                isFingerprintAllowed
+                                .toMessage()
+                        else ->
+                            MessageViewModel(
+                                text = defaultPrimaryMessage,
+                                secondaryText = faceMessage.message,
+                                isUpdateAnimated = false
                             )
-                            .toMessage()
+                    }
+                delay(MESSAGE_DURATION)
+                resetToDefault.emit(Unit)
+            }
+    }
+
+    private suspend fun listenForFingerprintMessages() {
+        // Listen for any events from fingerprint authentication and update the message shown
+        // on the bouncer.
+        biometricMessageInteractor.fingerprintMessage
+            .sample(
+                authenticationInteractor.authenticationMethod,
+                deviceEntryBiometricsAllowedInteractor.isFingerprintCurrentlyAllowedOnBouncer
+            )
+            .collectLatest { (fingerprintMessage, authMethod, isFingerprintAllowed) ->
+                val defaultPrimaryMessage =
+                    BouncerMessageStrings.defaultMessage(authMethod, isFingerprintAllowed)
+                        .primaryMessage
+                        .toResString()
+                message.value =
+                    when (fingerprintMessage) {
+                        is FingerprintLockoutMessage ->
+                            BouncerMessageStrings.class3AuthLockedOut(authMethod).toMessage()
+                        is FingerprintFailureMessage ->
+                            BouncerMessageStrings.incorrectFingerprintInput(authMethod).toMessage()
+                        else ->
+                            MessageViewModel(
+                                text = defaultPrimaryMessage,
+                                secondaryText = fingerprintMessage.message,
+                                isUpdateAnimated = false
+                            )
+                    }
+                delay(MESSAGE_DURATION)
+                resetToDefault.emit(Unit)
+            }
+    }
+
+    private suspend fun listenForBouncerEvents() {
+        coroutineScope {
+            // Keeps the lockout message up-to-date.
+            launch { bouncerInteractor.onLockoutStarted.collect { startLockoutCountdown() } }
+
+            // Start already active lockdown if it exists
+            launch { startLockoutCountdown() }
+
+            // Listens to relevant bouncer events
+            launch {
+                bouncerInteractor.onIncorrectBouncerInput
+                    .sample(
+                        authenticationInteractor.authenticationMethod,
+                        deviceEntryBiometricsAllowedInteractor
+                            .isFingerprintCurrentlyAllowedOnBouncer
                     )
-                    delay(MESSAGE_DURATION)
-                    resetToDefault.emit(Unit)
-                }
+                    .collectLatest { (_, authMethod, isFingerprintAllowed) ->
+                        message.emit(
+                            BouncerMessageStrings.incorrectSecurityInput(
+                                    authMethod,
+                                    isFingerprintAllowed
+                                )
+                                .toMessage()
+                        )
+                        delay(MESSAGE_DURATION)
+                        resetToDefault.emit(Unit)
+                    }
+            }
         }
     }
 
@@ -323,10 +334,10 @@
     }
 
     /** Shows the countdown message and refreshes it every second. */
-    private fun startLockoutCountdown() {
+    private suspend fun startLockoutCountdown() {
         lockoutCountdownJob?.cancel()
-        lockoutCountdownJob =
-            applicationScope.launch {
+        lockoutCountdownJob = coroutineScope {
+            launch {
                 authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
                     do {
                         val remainingSeconds = remainingLockoutSeconds()
@@ -352,6 +363,7 @@
                     lockoutCountdownJob = null
                 }
             }
+        }
     }
 
     private fun remainingLockoutSeconds(): Int {
@@ -365,20 +377,9 @@
 
     private fun Int.toResString(): String = applicationContext.getString(this)
 
-    init {
-        if (flags.isComposeBouncerOrSceneContainerEnabled()) {
-            applicationScope.launch {
-                // Update the lockout countdown whenever the selected user is switched.
-                selectedUser.collect { startLockoutCountdown() }
-            }
-
-            defaultBouncerMessageInitializer()
-
-            listenForSimBouncerEvents()
-            listenForBouncerEvents()
-            listenForFaceMessages()
-            listenForFingerprintMessages()
-        }
+    @AssistedFactory
+    interface Factory {
+        fun create(): BouncerMessageViewModel
     }
 
     companion object {
@@ -398,40 +399,3 @@
      */
     val isUpdateAnimated: Boolean = true,
 )
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@Module
-object BouncerMessageViewModelModule {
-
-    @Provides
-    @SysUISingleton
-    fun viewModel(
-        @Application applicationContext: Context,
-        @Application applicationScope: CoroutineScope,
-        bouncerInteractor: BouncerInteractor,
-        simBouncerInteractor: SimBouncerInteractor,
-        authenticationInteractor: AuthenticationInteractor,
-        clock: SystemClock,
-        biometricMessageInteractor: BiometricMessageInteractor,
-        faceAuthInteractor: DeviceEntryFaceAuthInteractor,
-        deviceUnlockedInteractor: DeviceUnlockedInteractor,
-        deviceEntryBiometricsAllowedInteractor: DeviceEntryBiometricsAllowedInteractor,
-        flags: ComposeBouncerFlags,
-        userSwitcherViewModel: UserSwitcherViewModel,
-    ): BouncerMessageViewModel {
-        return BouncerMessageViewModel(
-            applicationContext = applicationContext,
-            applicationScope = applicationScope,
-            bouncerInteractor = bouncerInteractor,
-            simBouncerInteractor = simBouncerInteractor,
-            authenticationInteractor = authenticationInteractor,
-            clock = clock,
-            biometricMessageInteractor = biometricMessageInteractor,
-            faceAuthInteractor = faceAuthInteractor,
-            deviceUnlockedInteractor = deviceUnlockedInteractor,
-            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
-            flags = flags,
-            selectedUser = userSwitcherViewModel.selectedUser,
-        )
-    }
-}
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
new file mode 100644
index 0000000..0aada06
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources
+import android.content.Context
+import android.graphics.Bitmap
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.type
+import androidx.core.graphics.drawable.toBitmap
+import com.android.app.tracing.coroutines.traceCoroutine
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
+import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/** Models UI state for the content of the bouncer scene. */
+class BouncerSceneContentViewModel
+@AssistedInject
+constructor(
+    @Application private val applicationContext: Context,
+    private val bouncerInteractor: BouncerInteractor,
+    private val authenticationInteractor: AuthenticationInteractor,
+    private val devicePolicyManager: DevicePolicyManager,
+    private val bouncerMessageViewModelFactory: BouncerMessageViewModel.Factory,
+    private val userSwitcher: UserSwitcherViewModel,
+    private val actionButtonInteractor: BouncerActionButtonInteractor,
+    private val pinViewModelFactory: PinBouncerViewModel.Factory,
+    private val patternViewModelFactory: PatternBouncerViewModel.Factory,
+    private val passwordViewModelFactory: PasswordBouncerViewModel.Factory,
+) : ExclusiveActivatable() {
+    private val _selectedUserImage = MutableStateFlow<Bitmap?>(null)
+    val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow()
+
+    val message: BouncerMessageViewModel by lazy { bouncerMessageViewModelFactory.create() }
+
+    private val _userSwitcherDropdown =
+        MutableStateFlow<List<UserSwitcherDropdownItemViewModel>>(emptyList())
+    val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
+        _userSwitcherDropdown.asStateFlow()
+
+    val isUserSwitcherVisible: Boolean
+        get() = bouncerInteractor.isUserSwitcherVisible
+
+    /** View-model for the current UI, based on the current authentication method. */
+    private val _authMethodViewModel = MutableStateFlow<AuthMethodBouncerViewModel?>(null)
+    val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
+        _authMethodViewModel.asStateFlow()
+
+    /**
+     * A message for a dialog to show when the user has attempted the wrong credential too many
+     * times and now must wait a while before attempting again.
+     *
+     * If `null`, the lockout dialog should not be shown.
+     */
+    private val lockoutDialogMessage = MutableStateFlow<String?>(null)
+
+    /**
+     * A message for a dialog to show when the user has attempted the wrong credential too many
+     * times and their user/profile/device data is at risk of being wiped due to a Device Manager
+     * policy.
+     *
+     * If `null`, the wipe dialog should not be shown.
+     */
+    private val wipeDialogMessage = MutableStateFlow<String?>(null)
+
+    private val _dialogViewModel = MutableStateFlow<DialogViewModel?>(createDialogViewModel())
+    /**
+     * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
+     *
+     * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
+     * dismisses this dialog.
+     */
+    val dialogViewModel: StateFlow<DialogViewModel?> = _dialogViewModel.asStateFlow()
+
+    private val _actionButton = MutableStateFlow<BouncerActionButtonModel?>(null)
+    /**
+     * The bouncer action button (Return to Call / Emergency Call). If `null`, the button should not
+     * be shown.
+     */
+    val actionButton: StateFlow<BouncerActionButtonModel?> = _actionButton.asStateFlow()
+
+    private val _isSideBySideSupported =
+        MutableStateFlow(isSideBySideSupported(authMethodViewModel.value))
+    /**
+     * Whether the "side-by-side" layout is supported.
+     *
+     * When presented on its own, without a user switcher (e.g. not on communal devices like
+     * tablets, for example), some authentication method UIs don't do well if they're shown in the
+     * side-by-side layout; these need to be shown with the standard layout so they can take up as
+     * much width as possible.
+     */
+    val isSideBySideSupported: StateFlow<Boolean> = _isSideBySideSupported.asStateFlow()
+
+    private val _isFoldSplitRequired =
+        MutableStateFlow(isFoldSplitRequired(authMethodViewModel.value))
+    /**
+     * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
+     * is required.
+     */
+    val isFoldSplitRequired: StateFlow<Boolean> = _isFoldSplitRequired.asStateFlow()
+
+    private val _isInputEnabled =
+        MutableStateFlow(authenticationInteractor.lockoutEndTimestamp == null)
+    private val isInputEnabled: StateFlow<Boolean> = _isInputEnabled.asStateFlow()
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { message.activate() }
+            launch {
+                authenticationInteractor.authenticationMethod
+                    .map(::getChildViewModel)
+                    .collectLatest { childViewModelOrNull ->
+                        _authMethodViewModel.value = childViewModelOrNull
+                        childViewModelOrNull?.let { traceCoroutine(it.traceName) { it.activate() } }
+                    }
+            }
+
+            launch {
+                authenticationInteractor.upcomingWipe.collect { wipeModel ->
+                    wipeDialogMessage.value = wipeModel?.message
+                }
+            }
+
+            launch {
+                userSwitcher.selectedUser
+                    .map { it.image.toBitmap() }
+                    .collect { _selectedUserImage.value = it }
+            }
+
+            launch {
+                combine(
+                        userSwitcher.users,
+                        userSwitcher.menu,
+                    ) { users, actions ->
+                        users.map { user ->
+                            UserSwitcherDropdownItemViewModel(
+                                icon = Icon.Loaded(user.image, contentDescription = null),
+                                text = user.name,
+                                onClick = user.onClicked ?: {},
+                            )
+                        } +
+                            actions.map { action ->
+                                UserSwitcherDropdownItemViewModel(
+                                    icon =
+                                        Icon.Resource(
+                                            action.iconResourceId,
+                                            contentDescription = null
+                                        ),
+                                    text = Text.Resource(action.textResourceId),
+                                    onClick = action.onClicked,
+                                )
+                            }
+                    }
+                    .collect { _userSwitcherDropdown.value = it }
+            }
+
+            launch {
+                combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
+                    .collect { _dialogViewModel.value = it }
+            }
+
+            launch { actionButtonInteractor.actionButton.collect { _actionButton.value = it } }
+
+            launch {
+                authMethodViewModel
+                    .map { authMethod -> isSideBySideSupported(authMethod) }
+                    .collect { _isSideBySideSupported.value = it }
+            }
+
+            launch {
+                authMethodViewModel
+                    .map { authMethod -> isFoldSplitRequired(authMethod) }
+                    .collect { _isFoldSplitRequired.value = it }
+            }
+
+            launch {
+                message.isLockoutMessagePresent
+                    .map { lockoutMessagePresent -> !lockoutMessagePresent }
+                    .collect { _isInputEnabled.value = it }
+            }
+
+            awaitCancellation()
+        }
+    }
+
+    private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
+        return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
+    }
+
+    private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
+        return authMethod !is PasswordBouncerViewModel
+    }
+
+    private fun getChildViewModel(
+        authenticationMethod: AuthenticationMethodModel,
+    ): AuthMethodBouncerViewModel? {
+        // If the current child view-model matches the authentication method, reuse it instead of
+        // creating a new instance.
+        val childViewModel = authMethodViewModel.value
+        if (authenticationMethod == childViewModel?.authenticationMethod) {
+            return childViewModel
+        }
+
+        return when (authenticationMethod) {
+            is AuthenticationMethodModel.Pin ->
+                pinViewModelFactory.create(
+                    authenticationMethod = authenticationMethod,
+                    onIntentionalUserInput = ::onIntentionalUserInput,
+                    isInputEnabled = isInputEnabled,
+                )
+            is AuthenticationMethodModel.Sim ->
+                pinViewModelFactory.create(
+                    authenticationMethod = authenticationMethod,
+                    onIntentionalUserInput = ::onIntentionalUserInput,
+                    isInputEnabled = isInputEnabled,
+                )
+            is AuthenticationMethodModel.Password ->
+                passwordViewModelFactory.create(
+                    onIntentionalUserInput = ::onIntentionalUserInput,
+                    isInputEnabled = isInputEnabled,
+                )
+            is AuthenticationMethodModel.Pattern ->
+                patternViewModelFactory.create(
+                    onIntentionalUserInput = ::onIntentionalUserInput,
+                    isInputEnabled = isInputEnabled,
+                )
+            else -> null
+        }
+    }
+
+    private fun onIntentionalUserInput() {
+        message.showDefaultMessage()
+        bouncerInteractor.onIntentionalUserInput()
+    }
+
+    /**
+     * @return A message warning the user that the user/profile/device will be wiped upon a further
+     *   [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
+     */
+    private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
+        val message =
+            applicationContext.getString(
+                wipeTarget.messageIdForAlmostWipe,
+                failedAttempts,
+                remainingAttempts,
+            )
+        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+            devicePolicyManager.resources.getString(
+                DevicePolicyResources.Strings.SystemUi
+                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
+                { message },
+                failedAttempts,
+                remainingAttempts,
+            ) ?: message
+        } else {
+            message
+        }
+    }
+
+    /**
+     * @return A message informing the user that their user/profile/device will be wiped promptly.
+     */
+    private fun AuthenticationWipeModel.getWipeMessage(): String {
+        val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
+        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
+            devicePolicyManager.resources.getString(
+                DevicePolicyResources.Strings.SystemUi
+                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
+                { message },
+                failedAttempts,
+            ) ?: message
+        } else {
+            message
+        }
+    }
+
+    private val AuthenticationWipeModel.message: String
+        get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
+
+    private fun createDialogViewModel(): DialogViewModel? {
+        val wipeText = wipeDialogMessage.value
+        val lockoutText = lockoutDialogMessage.value
+        return when {
+            // The wipe dialog takes priority over the lockout dialog.
+            wipeText != null ->
+                DialogViewModel(
+                    text = wipeText,
+                    onDismiss = { wipeDialogMessage.value = null },
+                )
+            lockoutText != null ->
+                DialogViewModel(
+                    text = lockoutText,
+                    onDismiss = { lockoutDialogMessage.value = null },
+                )
+            else -> null // No dialog to show.
+        }
+    }
+
+    /**
+     * Notifies that a key event has occurred.
+     *
+     * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+     */
+    fun onKeyEvent(keyEvent: KeyEvent): Boolean {
+        return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
+            keyEvent.type,
+            keyEvent.nativeKeyEvent.keyCode
+        ) ?: false
+    }
+
+    data class DialogViewModel(
+        val text: String,
+
+        /** Callback to run after the dialog has been dismissed by the user. */
+        val onDismiss: () -> Unit,
+    )
+
+    data class UserSwitcherDropdownItemViewModel(
+        val icon: Icon,
+        val text: Text,
+        val onClick: () -> Unit,
+    )
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): BouncerSceneContentViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
new file mode 100644
index 0000000..4fe6fc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerUserActionsViewModel.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.bouncer.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models UI state for user actions that can lead to navigation to other scenes when showing the
+ * bouncer scene.
+ */
+class BouncerUserActionsViewModel
+@AssistedInject
+constructor(
+    private val bouncerInteractor: BouncerInteractor,
+) : UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        bouncerInteractor.dismissDestination
+            .map { prevScene ->
+                mapOf(
+                    Back to UserActionResult(prevScene),
+                    Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
+                )
+            }
+            .collect { actions -> setActions(actions) }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): BouncerUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
deleted file mode 100644
index e2089bb..0000000
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ /dev/null
@@ -1,439 +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.bouncer.ui.viewmodel
-
-import android.app.admin.DevicePolicyManager
-import android.app.admin.DevicePolicyResources
-import android.content.Context
-import android.graphics.Bitmap
-import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.type
-import androidx.core.graphics.drawable.toBitmap
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.authentication.shared.model.AuthenticationWipeModel
-import com.android.systemui.bouncer.domain.interactor.BouncerActionButtonInteractor
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
-import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
-import com.android.systemui.common.shared.model.Icon
-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.dagger.qualifiers.Main
-import com.android.systemui.inputmethod.domain.interactor.InputMethodInteractor
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.user.ui.viewmodel.UserActionViewModel
-import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.user.ui.viewmodel.UserViewModel
-import dagger.Module
-import dagger.Provides
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-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
-import kotlinx.coroutines.job
-import kotlinx.coroutines.launch
-
-/** Holds UI state and handles user input on bouncer UIs. */
-class BouncerViewModel(
-    @Application private val applicationContext: Context,
-    @Deprecated("TODO(b/354270224): remove this. Injecting CoroutineScope to view-models is banned")
-    @Application
-    private val applicationScope: CoroutineScope,
-    @Main private val mainDispatcher: CoroutineDispatcher,
-    private val bouncerInteractor: BouncerInteractor,
-    private val inputMethodInteractor: InputMethodInteractor,
-    private val simBouncerInteractor: SimBouncerInteractor,
-    private val authenticationInteractor: AuthenticationInteractor,
-    private val selectedUserInteractor: SelectedUserInteractor,
-    private val devicePolicyManager: DevicePolicyManager,
-    bouncerMessageViewModel: BouncerMessageViewModel,
-    flags: ComposeBouncerFlags,
-    selectedUser: Flow<UserViewModel>,
-    users: Flow<List<UserViewModel>>,
-    userSwitcherMenu: Flow<List<UserActionViewModel>>,
-    actionButton: Flow<BouncerActionButtonModel?>,
-) {
-    val selectedUserImage: StateFlow<Bitmap?> =
-        selectedUser
-            .map { it.image.toBitmap() }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = null,
-            )
-
-    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        bouncerInteractor.dismissDestination.map { prevScene ->
-            mapOf(
-                Back to UserActionResult(prevScene),
-                Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
-            )
-        }
-
-    val message: BouncerMessageViewModel = bouncerMessageViewModel
-
-    val userSwitcherDropdown: StateFlow<List<UserSwitcherDropdownItemViewModel>> =
-        combine(
-                users,
-                userSwitcherMenu,
-            ) { users, actions ->
-                users.map { user ->
-                    UserSwitcherDropdownItemViewModel(
-                        icon = Icon.Loaded(user.image, contentDescription = null),
-                        text = user.name,
-                        onClick = user.onClicked ?: {},
-                    )
-                } +
-                    actions.map { action ->
-                        UserSwitcherDropdownItemViewModel(
-                            icon = Icon.Resource(action.iconResourceId, contentDescription = null),
-                            text = Text.Resource(action.textResourceId),
-                            onClick = action.onClicked,
-                        )
-                    }
-            }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = emptyList(),
-            )
-
-    val isUserSwitcherVisible: Boolean
-        get() = bouncerInteractor.isUserSwitcherVisible
-
-    // Handle to the scope of the child ViewModel (stored in [authMethod]).
-    private var childViewModelScope: CoroutineScope? = null
-
-    /** View-model for the current UI, based on the current authentication method. */
-    val authMethodViewModel: StateFlow<AuthMethodBouncerViewModel?> =
-        authenticationInteractor.authenticationMethod
-            .map(::getChildViewModel)
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = null,
-            )
-
-    /**
-     * A message for a dialog to show when the user has attempted the wrong credential too many
-     * times and now must wait a while before attempting again.
-     *
-     * If `null`, the lockout dialog should not be shown.
-     */
-    private val lockoutDialogMessage = MutableStateFlow<String?>(null)
-
-    /**
-     * A message for a dialog to show when the user has attempted the wrong credential too many
-     * times and their user/profile/device data is at risk of being wiped due to a Device Manager
-     * policy.
-     *
-     * If `null`, the wipe dialog should not be shown.
-     */
-    private val wipeDialogMessage = MutableStateFlow<String?>(null)
-
-    /**
-     * Models the dialog to be shown to the user, or `null` if no dialog should be shown.
-     *
-     * Once the dialog is shown, the UI should call [DialogViewModel.onDismiss] when the user
-     * dismisses this dialog.
-     */
-    val dialogViewModel: StateFlow<DialogViewModel?> =
-        combine(wipeDialogMessage, lockoutDialogMessage) { _, _ -> createDialogViewModel() }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = createDialogViewModel(),
-            )
-
-    /**
-     * The bouncer action button (Return to Call / Emergency Call). If `null`, the button should not
-     * be shown.
-     */
-    val actionButton: StateFlow<BouncerActionButtonModel?> =
-        actionButton.stateIn(
-            scope = applicationScope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = null
-        )
-
-    /**
-     * Whether the "side-by-side" layout is supported.
-     *
-     * When presented on its own, without a user switcher (e.g. not on communal devices like
-     * tablets, for example), some authentication method UIs don't do well if they're shown in the
-     * side-by-side layout; these need to be shown with the standard layout so they can take up as
-     * much width as possible.
-     */
-    val isSideBySideSupported: StateFlow<Boolean> =
-        authMethodViewModel
-            .map { authMethod -> isSideBySideSupported(authMethod) }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = isSideBySideSupported(authMethodViewModel.value),
-            )
-
-    /**
-     * Whether the splitting the UI around the fold seam (where the hinge is on a foldable device)
-     * is required.
-     */
-    val isFoldSplitRequired: StateFlow<Boolean> =
-        authMethodViewModel
-            .map { authMethod -> isFoldSplitRequired(authMethod) }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = isFoldSplitRequired(authMethodViewModel.value),
-            )
-
-    private val isInputEnabled: StateFlow<Boolean> =
-        bouncerMessageViewModel.isLockoutMessagePresent
-            .map { lockoutMessagePresent -> !lockoutMessagePresent }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = authenticationInteractor.lockoutEndTimestamp == null,
-            )
-
-    init {
-        if (flags.isComposeBouncerOrSceneContainerEnabled()) {
-            // Keeps the upcoming wipe dialog up-to-date.
-            applicationScope.launch {
-                authenticationInteractor.upcomingWipe.collect { wipeModel ->
-                    wipeDialogMessage.value = wipeModel?.message
-                }
-            }
-        }
-    }
-
-    private fun isSideBySideSupported(authMethod: AuthMethodBouncerViewModel?): Boolean {
-        return isUserSwitcherVisible || authMethod !is PasswordBouncerViewModel
-    }
-
-    private fun isFoldSplitRequired(authMethod: AuthMethodBouncerViewModel?): Boolean {
-        return authMethod !is PasswordBouncerViewModel
-    }
-
-    private fun getChildViewModel(
-        authenticationMethod: AuthenticationMethodModel,
-    ): AuthMethodBouncerViewModel? {
-        // If the current child view-model matches the authentication method, reuse it instead of
-        // creating a new instance.
-        val childViewModel = authMethodViewModel.value
-        if (authenticationMethod == childViewModel?.authenticationMethod) {
-            return childViewModel
-        }
-
-        childViewModelScope?.cancel()
-        val newViewModelScope = createChildCoroutineScope(applicationScope)
-        childViewModelScope = newViewModelScope
-        return when (authenticationMethod) {
-            is AuthenticationMethodModel.Pin ->
-                PinBouncerViewModel(
-                    applicationContext = applicationContext,
-                    viewModelScope = newViewModelScope,
-                    interactor = bouncerInteractor,
-                    isInputEnabled = isInputEnabled,
-                    simBouncerInteractor = simBouncerInteractor,
-                    authenticationMethod = authenticationMethod,
-                    onIntentionalUserInput = ::onIntentionalUserInput
-                )
-            is AuthenticationMethodModel.Sim ->
-                PinBouncerViewModel(
-                    applicationContext = applicationContext,
-                    viewModelScope = newViewModelScope,
-                    interactor = bouncerInteractor,
-                    isInputEnabled = isInputEnabled,
-                    simBouncerInteractor = simBouncerInteractor,
-                    authenticationMethod = authenticationMethod,
-                    onIntentionalUserInput = ::onIntentionalUserInput
-                )
-            is AuthenticationMethodModel.Password ->
-                PasswordBouncerViewModel(
-                    viewModelScope = newViewModelScope,
-                    isInputEnabled = isInputEnabled,
-                    interactor = bouncerInteractor,
-                    inputMethodInteractor = inputMethodInteractor,
-                    selectedUserInteractor = selectedUserInteractor,
-                    onIntentionalUserInput = ::onIntentionalUserInput
-                )
-            is AuthenticationMethodModel.Pattern ->
-                PatternBouncerViewModel(
-                    applicationContext = applicationContext,
-                    viewModelScope = newViewModelScope,
-                    interactor = bouncerInteractor,
-                    isInputEnabled = isInputEnabled,
-                    onIntentionalUserInput = ::onIntentionalUserInput
-                )
-            else -> null
-        }
-    }
-
-    private fun onIntentionalUserInput() {
-        message.showDefaultMessage()
-        bouncerInteractor.onIntentionalUserInput()
-    }
-
-    private fun createChildCoroutineScope(parentScope: CoroutineScope): CoroutineScope {
-        return CoroutineScope(
-            SupervisorJob(parent = parentScope.coroutineContext.job) + mainDispatcher
-        )
-    }
-
-    /**
-     * @return A message warning the user that the user/profile/device will be wiped upon a further
-     *   [AuthenticationWipeModel.remainingAttempts] unsuccessful authentication attempts.
-     */
-    private fun AuthenticationWipeModel.getAlmostAtWipeMessage(): String {
-        val message =
-            applicationContext.getString(
-                wipeTarget.messageIdForAlmostWipe,
-                failedAttempts,
-                remainingAttempts,
-            )
-        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
-            devicePolicyManager.resources.getString(
-                DevicePolicyResources.Strings.SystemUi
-                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ALMOST_ERASING_PROFILE,
-                { message },
-                failedAttempts,
-                remainingAttempts,
-            ) ?: message
-        } else {
-            message
-        }
-    }
-
-    /**
-     * @return A message informing the user that their user/profile/device will be wiped promptly.
-     */
-    private fun AuthenticationWipeModel.getWipeMessage(): String {
-        val message = applicationContext.getString(wipeTarget.messageIdForWipe, failedAttempts)
-        return if (wipeTarget == AuthenticationWipeModel.WipeTarget.ManagedProfile) {
-            devicePolicyManager.resources.getString(
-                DevicePolicyResources.Strings.SystemUi
-                    .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
-                { message },
-                failedAttempts,
-            ) ?: message
-        } else {
-            message
-        }
-    }
-
-    private val AuthenticationWipeModel.message: String
-        get() = if (remainingAttempts > 0) getAlmostAtWipeMessage() else getWipeMessage()
-
-    private fun createDialogViewModel(): DialogViewModel? {
-        val wipeText = wipeDialogMessage.value
-        val lockoutText = lockoutDialogMessage.value
-        return when {
-            // The wipe dialog takes priority over the lockout dialog.
-            wipeText != null ->
-                DialogViewModel(
-                    text = wipeText,
-                    onDismiss = { wipeDialogMessage.value = null },
-                )
-            lockoutText != null ->
-                DialogViewModel(
-                    text = lockoutText,
-                    onDismiss = { lockoutDialogMessage.value = null },
-                )
-            else -> null // No dialog to show.
-        }
-    }
-
-    /**
-     * Notifies that a key event has occurred.
-     *
-     * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
-     */
-    fun onKeyEvent(keyEvent: KeyEvent): Boolean {
-        return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
-            keyEvent.type,
-            keyEvent.nativeKeyEvent.keyCode
-        ) ?: false
-    }
-
-    data class DialogViewModel(
-        val text: String,
-
-        /** Callback to run after the dialog has been dismissed by the user. */
-        val onDismiss: () -> Unit,
-    )
-
-    data class UserSwitcherDropdownItemViewModel(
-        val icon: Icon,
-        val text: Text,
-        val onClick: () -> Unit,
-    )
-}
-
-@Module
-object BouncerViewModelModule {
-
-    @Provides
-    @SysUISingleton
-    fun viewModel(
-        @Application applicationContext: Context,
-        @Application applicationScope: CoroutineScope,
-        @Main mainDispatcher: CoroutineDispatcher,
-        bouncerInteractor: BouncerInteractor,
-        imeInteractor: InputMethodInteractor,
-        simBouncerInteractor: SimBouncerInteractor,
-        actionButtonInteractor: BouncerActionButtonInteractor,
-        authenticationInteractor: AuthenticationInteractor,
-        selectedUserInteractor: SelectedUserInteractor,
-        flags: ComposeBouncerFlags,
-        userSwitcherViewModel: UserSwitcherViewModel,
-        devicePolicyManager: DevicePolicyManager,
-        bouncerMessageViewModel: BouncerMessageViewModel,
-    ): BouncerViewModel {
-        return BouncerViewModel(
-            applicationContext = applicationContext,
-            applicationScope = applicationScope,
-            mainDispatcher = mainDispatcher,
-            bouncerInteractor = bouncerInteractor,
-            inputMethodInteractor = imeInteractor,
-            simBouncerInteractor = simBouncerInteractor,
-            authenticationInteractor = authenticationInteractor,
-            selectedUserInteractor = selectedUserInteractor,
-            devicePolicyManager = devicePolicyManager,
-            bouncerMessageViewModel = bouncerMessageViewModel,
-            flags = flags,
-            selectedUser = userSwitcherViewModel.selectedUser,
-            users = userSwitcherViewModel.users,
-            userSwitcherMenu = userSwitcherViewModel.menu,
-            actionButton = actionButtonInteractor.actionButton,
-        )
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index 052fb6b..2493cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -23,31 +23,36 @@
 import com.android.systemui.res.R
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.onSubscriberAdded
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
 
 /** Holds UI state and handles user input for the password bouncer UI. */
-class PasswordBouncerViewModel(
-    viewModelScope: CoroutineScope,
-    isInputEnabled: StateFlow<Boolean>,
+class PasswordBouncerViewModel
+@AssistedInject
+constructor(
     interactor: BouncerInteractor,
-    private val onIntentionalUserInput: () -> Unit,
     private val inputMethodInteractor: InputMethodInteractor,
     private val selectedUserInteractor: SelectedUserInteractor,
+    @Assisted isInputEnabled: StateFlow<Boolean>,
+    @Assisted private val onIntentionalUserInput: () -> Unit,
 ) :
     AuthMethodBouncerViewModel(
-        viewModelScope = viewModelScope,
         interactor = interactor,
         isInputEnabled = isInputEnabled,
+        traceName = "PasswordBouncerViewModel",
     ) {
 
     private val _password = MutableStateFlow("")
@@ -59,28 +64,69 @@
 
     override val lockoutMessageId = R.string.kg_too_many_failed_password_attempts_dialog_message
 
+    private val _isImeSwitcherButtonVisible = MutableStateFlow(false)
     /** Informs the UI whether the input method switcher button should be visible. */
-    val isImeSwitcherButtonVisible: StateFlow<Boolean> = imeSwitcherRefreshingFlow()
+    val isImeSwitcherButtonVisible: StateFlow<Boolean> = _isImeSwitcherButtonVisible.asStateFlow()
 
     /** Whether the text field element currently has focus. */
     private val isTextFieldFocused = MutableStateFlow(false)
 
+    private val _isTextFieldFocusRequested =
+        MutableStateFlow(isInputEnabled.value && !isTextFieldFocused.value)
     /** Whether the UI should request focus on the text field element. */
-    val isTextFieldFocusRequested =
-        combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus -> hasInput && !hasFocus }
-            .stateIn(
-                scope = viewModelScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = isInputEnabled.value && !isTextFieldFocused.value,
-            )
+    val isTextFieldFocusRequested = _isTextFieldFocusRequested.asStateFlow()
 
+    private val _selectedUserId = MutableStateFlow(selectedUserInteractor.getSelectedUserId())
     /** The ID of the currently-selected user. */
-    val selectedUserId: StateFlow<Int> =
-        selectedUserInteractor.selectedUser.stateIn(
-            scope = viewModelScope,
-            started = SharingStarted.WhileSubscribed(),
-            initialValue = selectedUserInteractor.getSelectedUserId(),
-        )
+    val selectedUserId: StateFlow<Int> = _selectedUserId.asStateFlow()
+
+    private val requests = Channel<Request>(Channel.BUFFERED)
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { super.onActivated() }
+            launch {
+                requests.receiveAsFlow().collect { request ->
+                    when (request) {
+                        is OnImeSwitcherButtonClicked -> {
+                            inputMethodInteractor.showInputMethodPicker(
+                                displayId = request.displayId,
+                                showAuxiliarySubtypes = false,
+                            )
+                        }
+                        is OnImeDismissed -> {
+                            interactor.onImeHiddenByUser()
+                        }
+                    }
+                }
+            }
+            launch {
+                combine(isInputEnabled, isTextFieldFocused) { hasInput, hasFocus ->
+                        hasInput && !hasFocus
+                    }
+                    .collect { _isTextFieldFocusRequested.value = it }
+            }
+            launch { selectedUserInteractor.selectedUser.collect { _selectedUserId.value = it } }
+            launch {
+                // Re-fetch the currently-enabled IMEs whenever the selected user changes, and
+                // whenever
+                // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
+                combine(
+                        // InputMethodManagerService sometimes takes some time to update its
+                        // internal
+                        // state when the selected user changes. As a workaround, delay fetching the
+                        // IME
+                        // info.
+                        selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
+                        _isImeSwitcherButtonVisible.onSubscriberAdded()
+                    ) { selectedUserId, _ ->
+                        inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
+                    }
+                    .collect { _isImeSwitcherButtonVisible.value = it }
+            }
+            awaitCancellation()
+        }
+    }
 
     override fun onHidden() {
         super.onHidden()
@@ -106,9 +152,7 @@
 
     /** Notifies that the user clicked the button to change the input method. */
     fun onImeSwitcherButtonClicked(displayId: Int) {
-        viewModelScope.launch {
-            inputMethodInteractor.showInputMethodPicker(displayId, showAuxiliarySubtypes = false)
-        }
+        requests.trySend(OnImeSwitcherButtonClicked(displayId))
     }
 
     /** Notifies that the user has pressed the key for attempting to authenticate the password. */
@@ -120,7 +164,7 @@
 
     /** Notifies that the user has dismissed the software keyboard (IME). */
     fun onImeDismissed() {
-        viewModelScope.launch { interactor.onImeHiddenByUser() }
+        requests.trySend(OnImeDismissed)
     }
 
     /** Notifies that the password text field has gained or lost focus. */
@@ -128,34 +172,21 @@
         isTextFieldFocused.value = isFocused
     }
 
-    /**
-     * Whether the input method switcher button should be displayed in the password bouncer UI. The
-     * value may be stale at the moment of subscription to this flow, but it is guaranteed to be
-     * shortly updated with a fresh value.
-     *
-     * Note: Each added subscription triggers an IPC call in the background, so this should only be
-     * subscribed to by the UI once in its lifecycle (i.e. when the bouncer is shown).
-     */
-    private fun imeSwitcherRefreshingFlow(): StateFlow<Boolean> {
-        val isImeSwitcherButtonVisible = MutableStateFlow(value = false)
-        viewModelScope.launch {
-            // Re-fetch the currently-enabled IMEs whenever the selected user changes, and whenever
-            // the UI subscribes to the `isImeSwitcherButtonVisible` flow.
-            combine(
-                    // InputMethodManagerService sometimes takes some time to update its internal
-                    // state when the selected user changes. As a workaround, delay fetching the IME
-                    // info.
-                    selectedUserInteractor.selectedUser.onEach { delay(DELAY_TO_FETCH_IMES) },
-                    isImeSwitcherButtonVisible.onSubscriberAdded()
-                ) { selectedUserId, _ ->
-                    inputMethodInteractor.hasMultipleEnabledImesOrSubtypes(selectedUserId)
-                }
-                .collect { isImeSwitcherButtonVisible.value = it }
-        }
-        return isImeSwitcherButtonVisible.asStateFlow()
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+        ): PasswordBouncerViewModel
     }
 
     companion object {
         @VisibleForTesting val DELAY_TO_FETCH_IMES = 300.milliseconds
     }
+
+    private sealed interface Request
+
+    private data class OnImeSwitcherButtonClicked(val displayId: Int) : Request
+
+    private data object OnImeDismissed : Request
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index a401600..0a866b4 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -22,30 +22,35 @@
 import com.android.systemui.authentication.shared.model.AuthenticationPatternCoordinate
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.res.R
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlin.math.max
 import kotlin.math.min
 import kotlin.math.pow
 import kotlin.math.sqrt
-import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
 
 /** Holds UI state and handles user input for the pattern bouncer UI. */
-class PatternBouncerViewModel(
+class PatternBouncerViewModel
+@AssistedInject
+constructor(
     private val applicationContext: Context,
-    viewModelScope: CoroutineScope,
     interactor: BouncerInteractor,
-    isInputEnabled: StateFlow<Boolean>,
-    private val onIntentionalUserInput: () -> Unit,
+    @Assisted isInputEnabled: StateFlow<Boolean>,
+    @Assisted private val onIntentionalUserInput: () -> Unit,
 ) :
     AuthMethodBouncerViewModel(
-        viewModelScope = viewModelScope,
         interactor = interactor,
         isInputEnabled = isInputEnabled,
+        traceName = "PatternBouncerViewModel",
     ) {
 
     /** The number of columns in the dot grid. */
@@ -54,17 +59,10 @@
     /** The number of rows in the dot grid. */
     val rowCount = 3
 
-    private val _selectedDots = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
-
+    private val selectedDotSet = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
+    private val selectedDotList = MutableStateFlow(selectedDotSet.value.toList())
     /** The dots that were selected by the user, in the order of selection. */
-    val selectedDots: StateFlow<List<PatternDotViewModel>> =
-        _selectedDots
-            .map { it.toList() }
-            .stateIn(
-                scope = viewModelScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = emptyList(),
-            )
+    val selectedDots: StateFlow<List<PatternDotViewModel>> = selectedDotList.asStateFlow()
 
     private val _currentDot = MutableStateFlow<PatternDotViewModel?>(null)
 
@@ -83,6 +81,16 @@
 
     override val lockoutMessageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message
 
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { super.onActivated() }
+            launch {
+                selectedDotSet.map { it.toList() }.collect { selectedDotList.value = it.toList() }
+            }
+            awaitCancellation()
+        }
+    }
+
     /** Notifies that the user has started a drag gesture across the dot grid. */
     fun onDragStart() {
         onIntentionalUserInput()
@@ -120,7 +128,7 @@
         }
 
         val hitDot = dots.value.firstOrNull { dot -> dot.x == dotColumn && dot.y == dotRow }
-        if (hitDot != null && !_selectedDots.value.contains(hitDot)) {
+        if (hitDot != null && !selectedDotSet.value.contains(hitDot)) {
             val skippedOverDots =
                 currentDot.value?.let { previousDot ->
                     buildList {
@@ -145,12 +153,11 @@
                                 )
                         }
                     }
-                }
-                    ?: emptyList()
+                } ?: emptyList()
 
-            _selectedDots.value =
+            selectedDotSet.value =
                 linkedSetOf<PatternDotViewModel>().apply {
-                    addAll(_selectedDots.value)
+                    addAll(selectedDotSet.value)
                     addAll(skippedOverDots)
                     add(hitDot)
                 }
@@ -173,11 +180,11 @@
     override fun clearInput() {
         _dots.value = defaultDots()
         _currentDot.value = null
-        _selectedDots.value = linkedSetOf()
+        selectedDotSet.value = linkedSetOf()
     }
 
     override fun getInput(): List<Any> {
-        return _selectedDots.value.map(PatternDotViewModel::toCoordinate)
+        return selectedDotSet.value.map(PatternDotViewModel::toCoordinate)
     }
 
     private fun defaultDots(): List<PatternDotViewModel> {
@@ -205,6 +212,14 @@
         max(min(outValue.float, 1f), MIN_DOT_HIT_FACTOR)
     }
 
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+        ): PatternBouncerViewModel
+    }
+
     companion object {
         private const val MIN_DOT_HIT_FACTOR = 0.2f
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index aa447ff..df6ca9b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -32,31 +32,37 @@
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
 import com.android.systemui.res.R
-import kotlinx.coroutines.CoroutineScope
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.receiveAsFlow
 import kotlinx.coroutines.launch
 
 /** Holds UI state and handles user input for the PIN code bouncer UI. */
-class PinBouncerViewModel(
+class PinBouncerViewModel
+@AssistedInject
+constructor(
     applicationContext: Context,
-    viewModelScope: CoroutineScope,
     interactor: BouncerInteractor,
-    isInputEnabled: StateFlow<Boolean>,
-    private val onIntentionalUserInput: () -> Unit,
     private val simBouncerInteractor: SimBouncerInteractor,
-    authenticationMethod: AuthenticationMethodModel,
+    @Assisted isInputEnabled: StateFlow<Boolean>,
+    @Assisted private val onIntentionalUserInput: () -> Unit,
+    @Assisted override val authenticationMethod: AuthenticationMethodModel,
 ) :
     AuthMethodBouncerViewModel(
-        viewModelScope = viewModelScope,
         interactor = interactor,
         isInputEnabled = isInputEnabled,
+        traceName = "PinBouncerViewModel",
     ) {
     /**
      * Whether the sim-related UI in the pin view is showing.
@@ -73,69 +79,90 @@
     /** Currently entered pin keys. */
     val pinInput: StateFlow<PinInputViewModel> = mutablePinInput
 
+    private val _hintedPinLength = MutableStateFlow<Int?>(null)
     /** The length of the PIN for which we should show a hint. */
-    val hintedPinLength: StateFlow<Int?> =
-        if (isSimAreaVisible) {
-                flowOf(null)
-            } else {
-                interactor.hintedPinLength
-            }
-            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
+    val hintedPinLength: StateFlow<Int?> = _hintedPinLength.asStateFlow()
 
+    private val _backspaceButtonAppearance = MutableStateFlow(ActionButtonAppearance.Hidden)
     /** Appearance of the backspace button. */
     val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
-        combine(
-                mutablePinInput,
-                interactor.isAutoConfirmEnabled,
-            ) { mutablePinEntries, isAutoConfirmEnabled ->
-                computeBackspaceButtonAppearance(
-                    pinInput = mutablePinEntries,
-                    isAutoConfirmEnabled = isAutoConfirmEnabled,
-                )
-            }
-            .stateIn(
-                scope = viewModelScope,
-                // Make sure this is kept as WhileSubscribed or we can run into a bug where the
-                // downstream continues to receive old/stale/cached values.
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = ActionButtonAppearance.Hidden,
-            )
+        _backspaceButtonAppearance.asStateFlow()
 
+    private val _confirmButtonAppearance = MutableStateFlow(ActionButtonAppearance.Hidden)
     /** Appearance of the confirm button. */
     val confirmButtonAppearance: StateFlow<ActionButtonAppearance> =
-        interactor.isAutoConfirmEnabled
-            .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
-            .stateIn(
-                scope = viewModelScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = ActionButtonAppearance.Hidden,
-            )
-
-    override val authenticationMethod: AuthenticationMethodModel = authenticationMethod
+        _confirmButtonAppearance.asStateFlow()
 
     override val lockoutMessageId = R.string.kg_too_many_failed_pin_attempts_dialog_message
 
-    init {
-        viewModelScope.launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
+    private val requests = Channel<Request>(Channel.BUFFERED)
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch { super.onActivated() }
+            launch {
+                requests.receiveAsFlow().collect { request ->
+                    when (request) {
+                        is OnErrorDialogDismissed -> {
+                            simBouncerInteractor.onErrorDialogDismissed()
+                        }
+                        is OnAuthenticateButtonClickedForSim -> {
+                            isSimUnlockingDialogVisible.value = true
+                            simBouncerInteractor.verifySim(getInput())
+                            isSimUnlockingDialogVisible.value = false
+                            clearInput()
+                        }
+                    }
+                }
+            }
+            launch { simBouncerInteractor.subId.collect { onResetSimFlow() } }
+            launch {
+                if (isSimAreaVisible) {
+                        flowOf(null)
+                    } else {
+                        interactor.hintedPinLength
+                    }
+                    .collect { _hintedPinLength.value = it }
+            }
+            launch {
+                combine(
+                        mutablePinInput,
+                        interactor.isAutoConfirmEnabled,
+                    ) { mutablePinEntries, isAutoConfirmEnabled ->
+                        computeBackspaceButtonAppearance(
+                            pinInput = mutablePinEntries,
+                            isAutoConfirmEnabled = isAutoConfirmEnabled,
+                        )
+                    }
+                    .collect { _backspaceButtonAppearance.value = it }
+            }
+            launch {
+                interactor.isAutoConfirmEnabled
+                    .map { if (it) ActionButtonAppearance.Hidden else ActionButtonAppearance.Shown }
+                    .collect { _confirmButtonAppearance.value = it }
+            }
+            launch {
+                interactor.isPinEnhancedPrivacyEnabled
+                    .map { !it }
+                    .collect { _isDigitButtonAnimationEnabled.value = it }
+            }
+            awaitCancellation()
+        }
     }
 
     /** Notifies that the user dismissed the sim pin error dialog. */
     fun onErrorDialogDismissed() {
-        viewModelScope.launch { simBouncerInteractor.onErrorDialogDismissed() }
+        requests.trySend(OnErrorDialogDismissed)
     }
 
+    private val _isDigitButtonAnimationEnabled =
+        MutableStateFlow(!interactor.isPinEnhancedPrivacyEnabled.value)
     /**
      * Whether the digit buttons should be animated when touched. Note that this doesn't affect the
      * delete or enter buttons; those should always animate.
      */
     val isDigitButtonAnimationEnabled: StateFlow<Boolean> =
-        interactor.isPinEnhancedPrivacyEnabled
-            .map { !it }
-            .stateIn(
-                scope = viewModelScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = !interactor.isPinEnhancedPrivacyEnabled.value,
-            )
+        _isDigitButtonAnimationEnabled.asStateFlow()
 
     /** Notifies that the user clicked on a PIN button with the given digit value. */
     fun onPinButtonClicked(input: Int) {
@@ -163,19 +190,14 @@
     /** Notifies that the user clicked the "enter" button. */
     fun onAuthenticateButtonClicked() {
         if (authenticationMethod == AuthenticationMethodModel.Sim) {
-            viewModelScope.launch {
-                isSimUnlockingDialogVisible.value = true
-                simBouncerInteractor.verifySim(getInput())
-                isSimUnlockingDialogVisible.value = false
-                clearInput()
-            }
+            requests.trySend(OnAuthenticateButtonClickedForSim)
         } else {
             tryAuthenticate(useAutoConfirm = false)
         }
     }
 
     fun onDisableEsimButtonClicked() {
-        viewModelScope.launch { simBouncerInteractor.disableEsim() }
+        simBouncerInteractor.disableEsim()
     }
 
     /** Resets the sim screen and shows a default message. */
@@ -242,6 +264,21 @@
             else -> false
         }
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+            authenticationMethod: AuthenticationMethodModel,
+        ): PinBouncerViewModel
+    }
+
+    private sealed interface Request
+
+    private data object OnErrorDialogDismissed : Request
+
+    private data object OnAuthenticateButtonClickedForSim : Request
 }
 
 /** Appearance of pin-pad action buttons. */
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 6757edb..b2d02ed 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -120,7 +120,7 @@
                         Intent.FLAG_ACTIVITY_NEW_TASK,
                         null,
                         activityOptions.toBundle(),
-                        selectedUserInteractor.getSelectedUserId(true),
+                        selectedUserInteractor.getSelectedUserId(),
                     )
                 } catch (e: RemoteException) {
                     Log.w("CameraGestureHelper", "Unable to start camera activity", e)
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 718ef51..7d518f4 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -23,19 +23,20 @@
 import android.view.Surface
 import android.view.View
 import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.Utils
-import com.android.systemui.res.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
-import com.android.systemui.surfaceeffects.ripple.RippleView
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.surfaceeffects.ripple.RippleView
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -57,6 +58,7 @@
     featureFlags: FeatureFlags,
     private val context: Context,
     private val windowManager: WindowManager,
+    private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
     private val systemClock: SystemClock,
     private val uiEventLogger: UiEventLogger
 ) {
@@ -161,12 +163,12 @@
             override fun onViewAttachedToWindow(view: View) {
                 layoutRipple()
                 rippleView.startRipple(Runnable {
-                    windowManager.removeView(rippleView)
+                    viewCaptureAwareWindowManager.removeView(rippleView)
                 })
                 rippleView.removeOnAttachStateChangeListener(this)
             }
         })
-        windowManager.addView(rippleView, windowLayoutParams)
+        viewCaptureAwareWindowManager.addView(rippleView, windowLayoutParams)
         uiEventLogger.log(WiredChargingRippleEvent.CHARGING_RIPPLE_PLAYED)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 3808ab7..e5e9c46 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -28,6 +28,7 @@
 import android.view.Gravity;
 import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
@@ -59,10 +60,11 @@
      */
     private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
             int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
-            RippleShape rippleShape, UiEventLogger uiEventLogger) {
+            RippleShape rippleShape, UiEventLogger uiEventLogger,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
                 transmittingBatteryLevel, batteryLevel, callback, isDozing,
-                rippleShape, uiEventLogger);
+                rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
     }
 
     /**
@@ -73,9 +75,11 @@
     public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
             @Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
             Callback callback, boolean isDozing, RippleShape rippleShape,
-            UiEventLogger uiEventLogger) {
+            UiEventLogger uiEventLogger,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
-                batteryLevel, callback, isDozing, rippleShape, uiEventLogger);
+                batteryLevel, callback, isDozing, rippleShape, uiEventLogger,
+                viewCaptureAwareWindowManager);
     }
 
     /**
@@ -83,10 +87,11 @@
      * battery level without charging number shown.
      */
     public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
-            @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger) {
+            @NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
         return makeWirelessChargingAnimation(context, null,
                 UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
-                rippleShape, uiEventLogger);
+                rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
     }
 
     /**
@@ -118,17 +123,19 @@
         private int mGravity;
         private WirelessChargingLayout mView;
         private WirelessChargingLayout mNextView;
-        private WindowManager mWM;
+        private ViewCaptureAwareWindowManager mWM;
         private Callback mCallback;
 
         public WirelessChargingView(Context context, @Nullable Looper looper,
                 int transmittingBatteryLevel, int batteryLevel, Callback callback,
-                boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger) {
+                boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger,
+                ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
             mCallback = callback;
             mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
                     isDozing, rippleShape);
             mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
             mUiEventLogger = uiEventLogger;
+            mWM = viewCaptureAwareWindowManager;
 
             final WindowManager.LayoutParams params = mParams;
             params.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -200,7 +207,6 @@
                 if (context == null) {
                     context = mView.getContext();
                 }
-                mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
                 mParams.packageName = packageName;
                 mParams.hideTimeoutMilliseconds = DURATION;
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index d2caefd..e1ba93c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -76,7 +76,6 @@
     private final boolean mTestHarness;
     private final MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
-    private FeatureFlags mFeatureFlags;
     private static final Queue<String> RECENT_INFO_LOG =
             new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
     private static final Queue<DebugSwipeRecord> RECENT_SWIPES =
@@ -186,8 +185,7 @@
             DoubleTapClassifier doubleTapClassifier, HistoryTracker historyTracker,
             KeyguardStateController keyguardStateController,
             AccessibilityManager accessibilityManager,
-            @TestHarness boolean testHarness,
-            FeatureFlags featureFlags) {
+            @TestHarness boolean testHarness) {
         mDataProvider = falsingDataProvider;
         mMetricsLogger = metricsLogger;
         mClassifiers = classifiers;
@@ -198,7 +196,6 @@
         mKeyguardStateController = keyguardStateController;
         mAccessibilityManager = accessibilityManager;
         mTestHarness = testHarness;
-        mFeatureFlags = featureFlags;
 
         mDataProvider.addSessionListener(mSessionListener);
         mDataProvider.addGestureCompleteListener(mGestureFinalizedListener);
@@ -399,8 +396,7 @@
                 || mDataProvider.isA11yAction()
                 || mDataProvider.isFromTrackpad()
                 || mDataProvider.isFromKeyboard()
-                || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED)
-                    && mDataProvider.isUnfolded());
+                || mDataProvider.isUnfolded();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 04c6fa9..aabfbd1 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -19,6 +19,8 @@
 import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
 
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS;
+import static com.android.systemui.Flags.clipboardImageTimeout;
+import static com.android.systemui.Flags.clipboardSharedTransitions;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISSED_OTHER;
@@ -32,8 +34,6 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT;
-import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
-import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -207,7 +207,7 @@
         mClipboardUtils = clipboardUtils;
         mBgExecutor = bgExecutor;
 
-        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+        if (clipboardSharedTransitions()) {
             mView.setCallbacks(this);
         } else {
             mView.setCallbacks(mClipboardCallbacks);
@@ -220,7 +220,7 @@
         });
 
         mTimeoutHandler.setOnTimeoutRunnable(() -> {
-            if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+            if (clipboardSharedTransitions()) {
                 finish(CLIPBOARD_OVERLAY_TIMED_OUT);
             } else {
                 mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
@@ -232,7 +232,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                    if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+                    if (clipboardSharedTransitions()) {
                         finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     } else {
                         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
@@ -248,7 +248,7 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (SCREENSHOT_ACTION.equals(intent.getAction())) {
-                    if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+                    if (clipboardSharedTransitions()) {
                         finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
                     } else {
                         mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
@@ -288,7 +288,7 @@
         boolean shouldAnimate = !model.dataMatches(mClipboardModel) || wasExiting;
         mClipboardModel = model;
         mClipboardLogger.setClipSource(mClipboardModel.getSource());
-        if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) {
+        if (clipboardImageTimeout()) {
             if (shouldAnimate) {
                 reset();
                 mClipboardLogger.setClipSource(mClipboardModel.getSource());
@@ -452,7 +452,7 @@
                     mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED);
                     mIsMinimized = false;
                 }
-                if (mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT)) {
+                if (clipboardImageTimeout()) {
                     setExpandedView(() -> animateIn());
                 } else {
                     setExpandedView();
@@ -481,7 +481,7 @@
                 remoteAction.ifPresent(action -> {
                     mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN);
                     mView.post(() -> mView.setActionChip(action, () -> {
-                        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+                        if (clipboardSharedTransitions()) {
                             finish(CLIPBOARD_OVERLAY_ACTION_TAPPED);
                         } else {
                             mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
@@ -522,13 +522,13 @@
                 mInputMonitor.getInputChannel(), Looper.getMainLooper()) {
             @Override
             public void onInputEvent(InputEvent event) {
-                if ((!mFeatureFlags.isEnabled(CLIPBOARD_IMAGE_TIMEOUT) || mShowingUi)
+                if ((!clipboardImageTimeout() || mShowingUi)
                         && event instanceof MotionEvent) {
                     MotionEvent motionEvent = (MotionEvent) event;
                     if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
                         if (!mView.isInTouchRegion(
                                 (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
-                            if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+                            if (clipboardSharedTransitions()) {
                                 finish(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
                             } else {
                                 mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
@@ -575,6 +575,10 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
+                // check again after animation to see if we should still be minimized
+                if (mIsMinimized && !shouldShowMinimized(mWindow.getWindowInsets())) {
+                    animateFromMinimized();
+                }
                 if (mOnUiUpdate != null) {
                     mOnUiUpdate.run();
                 }
@@ -690,14 +694,14 @@
 
     @Override
     public void onDismissButtonTapped() {
-        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+        if (clipboardSharedTransitions()) {
             finish(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
         }
     }
 
     @Override
     public void onRemoteCopyButtonTapped() {
-        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+        if (clipboardSharedTransitions()) {
             finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED,
                     IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext));
         }
@@ -705,7 +709,7 @@
 
     @Override
     public void onShareButtonTapped() {
-        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+        if (clipboardSharedTransitions()) {
             if (mClipboardModel.getType() != ClipboardModel.Type.OTHER) {
                 finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED,
                         IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext));
@@ -715,7 +719,7 @@
 
     @Override
     public void onPreviewTapped() {
-        if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) {
+        if (clipboardSharedTransitions()) {
             switch (mClipboardModel.getType()) {
                 case TEXT:
                     finish(CLIPBOARD_OVERLAY_EDIT_TAPPED,
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
index 0dc6fda..dc3b50c 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
@@ -27,6 +27,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.policy.PhoneWindow;
 import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
 import com.android.systemui.screenshot.FloatingWindowUtil;
@@ -44,6 +45,7 @@
 
     private final Context mContext;
     private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
 
     private boolean mKeyboardVisible;
@@ -52,7 +54,9 @@
     private Runnable mOnOrientationChangeListener;
 
     @Inject
-    ClipboardOverlayWindow(@OverlayWindowContext Context context) {
+    ClipboardOverlayWindow(@OverlayWindowContext Context context,
+            @OverlayWindowContext ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+            @OverlayWindowContext WindowManager windowManager) {
         super(context);
         mContext = context;
         mOrientation = mContext.getResources().getConfiguration().orientation;
@@ -61,10 +65,11 @@
         requestFeature(Window.FEATURE_NO_TITLE);
         requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
         setBackgroundDrawableResource(android.R.color.transparent);
-        mWindowManager = mContext.getSystemService(WindowManager.class);
+        mWindowManager = windowManager;
+        mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
         mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
         mWindowLayoutParams.setTitle("ClipboardOverlay");
-        setWindowManager(mWindowManager, null, null);
+        setWindowManager(windowManager, null, null);
         setWindowFocusable(false);
     }
 
@@ -81,10 +86,12 @@
 
         attach();
         withWindowAttached(() -> {
-            WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+            WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics()
+                    .getWindowInsets();
             mKeyboardVisible = currentInsets.isVisible(WindowInsets.Type.ime());
             peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
-                WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+                WindowInsets insets = mWindowManager.getCurrentWindowMetrics()
+                        .getWindowInsets();
                 boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
                 if (keyboardVisible != mKeyboardVisible) {
                     mKeyboardVisible = keyboardVisible;
@@ -105,7 +112,7 @@
     void remove() {
         final View decorView = peekDecorView();
         if (decorView != null && decorView.isAttachedToWindow()) {
-            mWindowManager.removeViewImmediate(decorView);
+            mViewCaptureAwareWindowManager.removeViewImmediate(decorView);
         }
     }
 
@@ -139,7 +146,7 @@
         if (decorView.isAttachedToWindow()) {
             return;
         }
-        mWindowManager.addView(decorView, mWindowLayoutParams);
+        mViewCaptureAwareWindowManager.addView(decorView, mWindowLayoutParams);
         decorView.requestApplyInsets();
     }
 
@@ -160,7 +167,7 @@
         }
         final View decorView = peekDecorView();
         if (decorView != null && decorView.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
+            mViewCaptureAwareWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index a43447f..aae21b9 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -66,7 +66,8 @@
                     @Override
                     public WindowInsets onApplyWindowInsets(@NonNull View view,
                             @NonNull WindowInsets windowInsets) {
-                        Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars());
+                        Insets insets = windowInsets.getInsets(
+                                WindowInsets.Type.systemBars() | WindowInsets.Type.ime());
                         ViewGroup.MarginLayoutParams layoutParams =
                                 (ViewGroup.MarginLayoutParams) view.getLayoutParams();
                         layoutParams.leftMargin = insets.left;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index ff9fba4..307a07f 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -18,17 +18,24 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.view.Display;
 import android.view.LayoutInflater;
+import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.DisplayTracker;
 
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -61,6 +68,28 @@
                 R.layout.clipboard_overlay, null);
     }
 
+    /**
+     *
+     */
+    @Provides
+    @OverlayWindowContext
+    static WindowManager provideWindowManager(@OverlayWindowContext Context context) {
+        return context.getSystemService(WindowManager.class);
+    }
+
+    /**
+     *
+     */
+    @Provides
+    @OverlayWindowContext
+    static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
+            @OverlayWindowContext WindowManager windowManager,
+            Lazy<ViewCapture> daggerLazyViewCapture) {
+        return new ViewCaptureAwareWindowManager(windowManager,
+                /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
+                /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+    }
+
     @Qualifier
     @Documented
     @Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageInstallerMonitor.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageInstallerMonitor.kt
index 46db346..208adc2 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageInstallerMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/PackageInstallerMonitor.kt
@@ -18,6 +18,7 @@
 
 import android.content.pm.PackageInstaller
 import android.os.Handler
+import android.text.TextUtils
 import com.android.internal.annotations.GuardedBy
 import com.android.systemui.common.shared.model.PackageInstallSession
 import com.android.systemui.dagger.SysUISingleton
@@ -63,6 +64,7 @@
                         synchronized(sessions) {
                             sessions.putAll(
                                 packageInstaller.allSessions
+                                    .filter { !TextUtils.isEmpty(it.appPackageName) }
                                     .map { session -> session.toModel() }
                                     .associateBy { it.sessionId }
                             )
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 3cdb573..aef5f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -38,5 +38,5 @@
 }
 
 /** Creates [Icon.Loaded] for a given drawable with an optional [contentDescription]. */
-fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon =
+fun Drawable.asIcon(contentDescription: ContentDescription? = null): Icon.Loaded =
     Icon.Loaded(this, contentDescription)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
index 578389b..13f6bba 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt
@@ -23,35 +23,25 @@
 import androidx.annotation.DimenRes
 import androidx.annotation.LayoutRes
 import com.android.settingslib.Utils
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.onDensityOrFontScaleChanged
 import com.android.systemui.statusbar.policy.onThemeChanged
 import com.android.systemui.util.kotlin.emitOnStart
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 
-/** Configuration-aware-state-tracking utilities. */
-class ConfigurationState
-@Inject
-constructor(
-    private val configurationController: ConfigurationController,
-    @Application private val context: Context,
-    private val layoutInflater: LayoutInflater,
-) {
+interface ConfigurationState {
     /**
      * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
      * configuration.
      *
      * @see android.content.res.Resources.getDimensionPixelSize
      */
-    fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
-        return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
-            context.resources.getDimensionPixelSize(id)
-        }
-    }
+    fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int>
 
     /**
      * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
@@ -59,22 +49,14 @@
      *
      * @see android.content.res.Resources.getDimensionPixelSize
      */
-    fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
-        return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
-            context.resources.getDimensionPixelOffset(id)
-        }
-    }
+    fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int>
 
     /**
      * Returns a [Flow] that emits a color that is kept in sync with the device theme.
      *
      * @see Utils.getColorAttrDefaultColor
      */
-    fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
-        return configurationController.onThemeChanged.emitOnStart().map {
-            Utils.getColorAttrDefaultColor(context, id, defaultValue)
-        }
-    }
+    fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int>
 
     /**
      * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
@@ -87,6 +69,65 @@
         @LayoutRes id: Int,
         root: ViewGroup?,
         attachToRoot: Boolean,
+    ): Flow<T>
+}
+
+/** Configuration-aware-state-tracking utilities. */
+class ConfigurationStateImpl
+@AssistedInject
+constructor(
+    @Assisted private val configurationController: ConfigurationController,
+    @Assisted private val context: Context,
+) : ConfigurationState {
+
+    private val layoutInflater = LayoutInflater.from(context)
+
+    /**
+     * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+     * configuration.
+     *
+     * @see android.content.res.Resources.getDimensionPixelSize
+     */
+    override fun getDimensionPixelSize(@DimenRes id: Int): Flow<Int> {
+        return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+            context.resources.getDimensionPixelSize(id)
+        }
+    }
+
+    /**
+     * Returns a [Flow] that emits a dimension pixel size that is kept in sync with the device
+     * configuration.
+     *
+     * @see android.content.res.Resources.getDimensionPixelSize
+     */
+    override fun getDimensionPixelOffset(@DimenRes id: Int): Flow<Int> {
+        return configurationController.onDensityOrFontScaleChanged.emitOnStart().map {
+            context.resources.getDimensionPixelOffset(id)
+        }
+    }
+
+    /**
+     * Returns a [Flow] that emits a color that is kept in sync with the device theme.
+     *
+     * @see Utils.getColorAttrDefaultColor
+     */
+    override fun getColorAttr(@AttrRes id: Int, @ColorInt defaultValue: Int): Flow<Int> {
+        return configurationController.onThemeChanged.emitOnStart().map {
+            Utils.getColorAttrDefaultColor(context, id, defaultValue)
+        }
+    }
+
+    /**
+     * Returns a [Flow] that emits a [View] that is re-inflated as necessary to remain in sync with
+     * the device configuration.
+     *
+     * @see LayoutInflater.inflate
+     */
+    @Suppress("UNCHECKED_CAST")
+    override fun <T : View> inflateLayout(
+        @LayoutRes id: Int,
+        root: ViewGroup?,
+        attachToRoot: Boolean,
     ): Flow<T> {
         // TODO(b/305930747): This may lead to duplicate invocations if both flows emit, find a
         //  solution to only emit one event.
@@ -97,4 +138,16 @@
             .emitOnStart()
             .map { layoutInflater.inflate(id, root, attachToRoot) as T }
     }
+
+    @AssistedFactory
+    interface Factory {
+        /**
+         * Creates a configurationState for a given context. The [configurationController] is
+         * supposed to give config events specific for that context.
+         */
+        fun create(
+            context: Context,
+            configurationController: ConfigurationController
+        ): ConfigurationStateImpl
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.kt
new file mode 100644
index 0000000..b36da3b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationStateModule.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.systemui.common.ui
+
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
+
+/**
+ * Annotates elements that provide information from the global configuration.
+ *
+ * The global configuration is the one associted with the main display. Secondary displays will
+ * apply override to the global configuration. Elements annotated with this shouldn't be used for
+ * secondary displays.
+ */
+@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalConfig
+
+@Module
+interface ConfigurationStateModule {
+
+    /**
+     * Deprecated: [ConfigurationState] should be injected only with the correct annotation. For
+     * now, without annotation the global config associated state is provided.
+     */
+    @Binds
+    fun provideGlobalConfigurationState(
+        @GlobalConfig configurationState: ConfigurationState
+    ): ConfigurationState
+
+    companion object {
+        @SysUISingleton
+        @Provides
+        @GlobalConfig
+        fun provideGlobalConfigurationState(
+            configStateFactory: ConfigurationStateImpl.Factory,
+            configurationController: ConfigurationController,
+            @Application context: Context,
+        ): ConfigurationState {
+            return configStateFactory.create(context, configurationController)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/CommonUsageStatsDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/CommonUsageStatsDataLayerModule.kt
new file mode 100644
index 0000000..3faa0dd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/CommonUsageStatsDataLayerModule.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.common.usagestats.data
+
+import com.android.systemui.common.usagestats.data.repository.UsageStatsRepository
+import com.android.systemui.common.usagestats.data.repository.UsageStatsRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class CommonUsageStatsDataLayerModule {
+    @Binds
+    abstract fun bindUsageStatsRepository(impl: UsageStatsRepositoryImpl): UsageStatsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/model/UsageStatsQuery.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/model/UsageStatsQuery.kt
new file mode 100644
index 0000000..270498c5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/model/UsageStatsQuery.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.common.usagestats.data.model
+
+import android.annotation.CurrentTimeMillisLong
+import android.app.usage.UsageStatsManager
+import android.os.UserHandle
+import com.android.systemui.util.time.SystemClock
+
+/** Models a query which can be made to [UsageStatsManager] */
+data class UsageStatsQuery(
+    /** Specifies the user for the query. */
+    val user: UserHandle,
+    /**
+     * The inclusive beginning of the range of events to include. Defined in unix time, see
+     * [SystemClock.currentTimeMillis]
+     */
+    @CurrentTimeMillisLong val startTime: Long,
+    /**
+     * The exclusive end of the range of events to include. Defined in unix time, see
+     * [SystemClock.currentTimeMillis]
+     */
+    @CurrentTimeMillisLong val endTime: Long,
+    /**
+     * The list of package names to be included in the query. If empty, events for all packages will
+     * be queried.
+     */
+    val packageNames: List<String> = emptyList(),
+)
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
new file mode 100644
index 0000000..e3f1174
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.common.usagestats.data.repository
+
+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.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
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+
+/** Repository for querying UsageStatsManager */
+interface UsageStatsRepository {
+    /** Query activity events. */
+    suspend fun queryActivityEvents(query: UsageStatsQuery): List<ActivityEventModel>
+}
+
+@SysUISingleton
+class UsageStatsRepositoryImpl
+@Inject
+constructor(
+    @Background private val bgContext: CoroutineContext,
+    private val usageStatsManager: UsageStatsManager,
+) : UsageStatsRepository {
+    private companion object {
+        const val TAG = "UsageStatsRepository"
+    }
+
+    override suspend fun queryActivityEvents(query: UsageStatsQuery): List<ActivityEventModel> =
+        withContext("$TAG#queryActivityEvents", bgContext) {
+            val systemQuery: UsageEventsQuery =
+                UsageEventsQuery.Builder(query.startTime, query.endTime)
+                    .apply {
+                        setUserId(query.user.identifier)
+                        setEventTypes(
+                            UsageEvents.Event.ACTIVITY_RESUMED,
+                            UsageEvents.Event.ACTIVITY_PAUSED,
+                            UsageEvents.Event.ACTIVITY_STOPPED,
+                            UsageEvents.Event.ACTIVITY_DESTROYED,
+                        )
+                        if (query.packageNames.isNotEmpty()) {
+                            setPackageNames(*query.packageNames.toTypedArray())
+                        }
+                    }
+                    .build()
+
+            val events: UsageEvents? = usageStatsManager.queryEvents(systemQuery)
+
+            buildList {
+                events.forEachEvent { event ->
+                    val lifecycle =
+                        when (event.eventType) {
+                            UsageEvents.Event.ACTIVITY_RESUMED -> Lifecycle.RESUMED
+                            UsageEvents.Event.ACTIVITY_PAUSED -> Lifecycle.PAUSED
+                            UsageEvents.Event.ACTIVITY_STOPPED -> Lifecycle.STOPPED
+                            UsageEvents.Event.ACTIVITY_DESTROYED -> Lifecycle.DESTROYED
+                            else -> Lifecycle.UNKNOWN
+                        }
+
+                    add(
+                        ActivityEventModel(
+                            instanceId = event.instanceId,
+                            packageName = event.packageName,
+                            lifecycle = lifecycle,
+                            timestamp = event.timeStamp,
+                        )
+                    )
+                }
+            }
+        }
+}
+
+private inline fun UsageEvents?.forEachEvent(action: (UsageEvents.Event) -> Unit) {
+    this ?: return
+    val event = UsageEvents.Event()
+    while (getNextEvent(event)) {
+        action(event)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/domain/UsageStatsInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/domain/UsageStatsInteractor.kt
new file mode 100644
index 0000000..81848e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/domain/UsageStatsInteractor.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.common.usagestats.domain
+
+import android.annotation.CurrentTimeMillisLong
+import android.os.UserHandle
+import com.android.systemui.common.usagestats.data.model.UsageStatsQuery
+import com.android.systemui.common.usagestats.data.repository.UsageStatsRepository
+import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+
+@SysUISingleton
+class UsageStatsInteractor
+@Inject
+constructor(
+    private val userTracker: UserTracker,
+    private val repository: UsageStatsRepository,
+    private val systemClock: SystemClock,
+) {
+    suspend fun queryActivityEvents(
+        @CurrentTimeMillisLong startTime: Long,
+        @CurrentTimeMillisLong endTime: Long = systemClock.currentTimeMillis(),
+        userHandle: UserHandle = UserHandle.CURRENT,
+        packageNames: List<String> = emptyList(),
+    ): List<ActivityEventModel> {
+        val user =
+            if (userHandle == UserHandle.CURRENT) {
+                userTracker.userHandle
+            } else {
+                userHandle
+            }
+
+        return repository.queryActivityEvents(
+            UsageStatsQuery(
+                startTime = startTime,
+                endTime = endTime,
+                user = user,
+                packageNames = packageNames,
+            )
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/shared/model/ActivityEventModel.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/shared/model/ActivityEventModel.kt
new file mode 100644
index 0000000..9ef33fa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/shared/model/ActivityEventModel.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.common.usagestats.shared.model
+
+import android.annotation.CurrentTimeMillisLong
+import android.app.Activity
+import com.android.systemui.util.time.SystemClock
+
+/** Represents [Activity] lifecycle events. */
+data class ActivityEventModel(
+    /** Uniquely identifies an activity. */
+    val instanceId: Int,
+    /** The package name of the source of this event. */
+    val packageName: String,
+    /** The lifecycle change which this event represents. */
+    val lifecycle: Lifecycle,
+    /** The timestamp of the event. Defined in unix time, see [SystemClock.currentTimeMillis] */
+    @CurrentTimeMillisLong val timestamp: Long,
+) {
+    enum class Lifecycle {
+        UNKNOWN,
+        RESUMED,
+        PAUSED,
+        STOPPED,
+        DESTROYED
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 0582cc2..c69cea4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -32,11 +32,14 @@
 import com.android.systemui.keyguard.shared.model.filterState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 
 /**
@@ -54,6 +57,18 @@
     private val dreamManager: DreamManager,
     @Background private val bgScope: CoroutineScope,
 ) : CoreStartable {
+    /** Flow that emits when the dream should be started underneath the glanceable hub. */
+    val startDream =
+        allOf(
+                keyguardTransitionInteractor
+                    .transitionValue(Scenes.Communal, KeyguardState.GLANCEABLE_HUB)
+                    .map { it == 1f },
+                not(keyguardInteractor.isDreaming),
+                // TODO(b/362830856): Remove this workaround.
+                keyguardInteractor.isKeyguardShowing,
+            )
+            .filter { it }
+
     @SuppressLint("MissingPermission")
     override fun start() {
         if (!communalSettingsInteractor.isCommunalFlagEnabled()) {
@@ -72,17 +87,10 @@
 
         // Restart the dream underneath the hub in order to support the ability to swipe
         // away the hub to enter the dream.
-        keyguardTransitionInteractor
-            .transition(
-                edge = Edge.create(to = Scenes.Communal),
-                edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GLANCEABLE_HUB)
-            )
-            .filterState(TransitionState.FINISHED)
+        startDream
             .sampleFilter(powerInteractor.isAwake) { isAwake ->
-                dreamManager.canStartDreaming(isAwake)
+                !glanceableHubAllowKeyguardWhenDreaming() && dreamManager.canStartDreaming(isAwake)
             }
-            .sampleFilter(keyguardInteractor.isDreaming) { isDreaming -> !isDreaming }
-            .filter { !glanceableHubAllowKeyguardWhenDreaming() }
             .onEach { dreamManager.startDream() }
             .launchIn(bgScope)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index b7c02ea..6e01393 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -112,7 +112,11 @@
                         communalSceneInteractor.editModeState.value == EditModeState.STARTING ||
                             communalSceneInteractor.isLaunchingWidget.value
                     if (!delaySceneTransition) {
-                        communalSceneInteractor.changeScene(nextScene, nextTransition)
+                        communalSceneInteractor.changeScene(
+                            newScene = nextScene,
+                            loggingReason = "KTF syncing",
+                            transitionKey = nextTransition,
+                        )
                     }
                 }
                 .launchIn(applicationScope)
@@ -176,7 +180,10 @@
                     if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
                         // If dreaming starts after timeout has expired, ex. if dream restarts under
                         // the hub, just close the hub immediately.
-                        communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                        communalSceneInteractor.changeScene(
+                            CommunalScenes.Blank,
+                            "dream started after timeout",
+                        )
                     }
                 }
         }
@@ -201,7 +208,10 @@
                 bgScope.launch {
                     delay(screenTimeout.milliseconds)
                     if (isDreaming) {
-                        communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Blank,
+                            loggingReason = "hub timeout",
+                        )
                     }
                     timeoutJob = null
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index ba2b7bf..a33e0ac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.dagger
 
 import android.content.Context
+import android.content.res.Resources
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.data.backup.CommunalBackupUtils
 import com.android.systemui.communal.data.db.CommunalDatabaseModule
@@ -38,6 +39,8 @@
 import com.android.systemui.communal.widgets.EditWidgetsActivityStarterImpl
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSource
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
@@ -90,6 +93,7 @@
 
     companion object {
         const val LOGGABLE_PREFIXES = "loggable_prefixes"
+        const val LAUNCHER_PACKAGE = "launcher_package"
 
         @Provides
         @Communal
@@ -126,5 +130,12 @@
                 .getStringArray(com.android.internal.R.array.config_loggable_dream_prefixes)
                 .toList()
         }
+
+        /** The package name of the launcher */
+        @Provides
+        @Named(LAUNCHER_PACKAGE)
+        fun provideLauncherPackage(@Main resources: Resources): String {
+            return resources.getString(R.string.launcher_overlayable_package)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
index dff6352..8f1854f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt
@@ -26,7 +26,7 @@
 import androidx.sqlite.db.SupportSQLiteDatabase
 import com.android.systemui.res.R
 
-@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 2)
+@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 3)
 abstract class CommunalDatabase : RoomDatabase() {
     abstract fun communalWidgetDao(): CommunalWidgetDao
 
@@ -55,7 +55,7 @@
                             context.resources.getString(R.string.config_communalDatabase)
                         )
                         .also { builder ->
-                            builder.addMigrations(MIGRATION_1_2)
+                            builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                             builder.fallbackToDestructiveMigration(dropAllTables = true)
                             callback?.let { callback -> builder.addCallback(callback) }
                         }
@@ -87,5 +87,21 @@
                     )
                 }
             }
+
+        /**
+         * This migration reverses the ranks. For example, if the ranks are 2, 1, 0, then after the
+         * migration they will be 0, 1, 2.
+         */
+        @VisibleForTesting
+        val MIGRATION_2_3 =
+            object : Migration(2, 3) {
+                override fun migrate(db: SupportSQLiteDatabase) {
+                    Log.i(TAG, "Migrating from version 2 to 3")
+                    db.execSQL(
+                        "UPDATE communal_item_rank_table " +
+                            "SET rank = (SELECT MAX(rank) FROM communal_item_rank_table) - rank"
+                    )
+                }
+            }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
index 933a25a..93b86bd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt
@@ -97,7 +97,7 @@
                             .addWidget(
                                 widgetId = id,
                                 componentName = name,
-                                priority = defaultWidgets.size - index,
+                                rank = index,
                                 userSerialNumber = userSerialNumber,
                             )
                     }
@@ -132,10 +132,17 @@
     @Query(
         "SELECT * FROM communal_widget_table JOIN communal_item_rank_table " +
             "ON communal_item_rank_table.uid = communal_widget_table.item_id " +
-            "ORDER BY communal_item_rank_table.rank DESC"
+            "ORDER BY communal_item_rank_table.rank ASC"
     )
     fun getWidgets(): Flow<Map<CommunalItemRank, CommunalWidgetItem>>
 
+    @Query(
+        "SELECT * FROM communal_widget_table JOIN communal_item_rank_table " +
+            "ON communal_item_rank_table.uid = communal_widget_table.item_id " +
+            "ORDER BY communal_item_rank_table.rank ASC"
+    )
+    fun getWidgetsNow(): Map<CommunalItemRank, CommunalWidgetItem>
+
     @Query("SELECT * FROM communal_widget_table WHERE widget_id = :id")
     fun getWidgetByIdNow(id: Int): CommunalWidgetItem?
 
@@ -167,11 +174,11 @@
     @Query("DELETE FROM communal_item_rank_table") fun clearCommunalItemRankTable()
 
     @Transaction
-    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
-        widgetIdToPriorityMap.forEach { (id, priority) ->
+    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {
+        widgetIdToRankMap.forEach { (id, rank) ->
             val widget = getWidgetByIdNow(id)
             if (widget != null) {
-                updateItemRank(widget.itemId, priority)
+                updateItemRank(widget.itemId, rank)
             }
         }
     }
@@ -180,13 +187,13 @@
     fun addWidget(
         widgetId: Int,
         provider: ComponentName,
-        priority: Int,
+        rank: Int? = null,
         userSerialNumber: Int,
     ): Long {
         return addWidget(
             widgetId = widgetId,
             componentName = provider.flattenToString(),
-            priority = priority,
+            rank = rank,
             userSerialNumber = userSerialNumber,
         )
     }
@@ -195,13 +202,27 @@
     fun addWidget(
         widgetId: Int,
         componentName: String,
-        priority: Int,
+        rank: Int? = null,
         userSerialNumber: Int,
     ): Long {
+        val widgets = getWidgetsNow()
+
+        // If rank is not specified, rank it last by finding the current maximum rank and increment
+        // by 1. If the new widget is the first widget, set the rank to 0.
+        val newRank = rank ?: widgets.keys.maxOfOrNull { it.rank + 1 } ?: 0
+
+        // Shift widgets after [rank], unless widget is added at the end.
+        if (rank != null) {
+            widgets.forEach { (rankEntry, widgetEntry) ->
+                if (rankEntry.rank < newRank) return@forEach
+                updateItemRank(widgetEntry.itemId, rankEntry.rank + 1)
+            }
+        }
+
         return insertWidget(
             widgetId = widgetId,
             componentName = componentName,
-            itemId = insertItemRank(priority),
+            itemId = insertItemRank(newRank),
             userSerialNumber = userSerialNumber,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
index 33edb80..c46f0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalMediaModel.kt
@@ -21,21 +21,21 @@
 
 /** Data model of media on the communal hub. */
 data class CommunalMediaModel(
-    val hasActiveMediaOrRecommendation: Boolean,
+    val hasAnyMediaOrRecommendation: Boolean,
     val createdTimestampMillis: Long = 0L,
 ) : Diffable<CommunalMediaModel> {
     companion object {
         val INACTIVE =
             CommunalMediaModel(
-                hasActiveMediaOrRecommendation = false,
+                hasAnyMediaOrRecommendation = false,
             )
     }
 
     override fun logDiffs(prevVal: CommunalMediaModel, row: TableRowLogger) {
-        if (hasActiveMediaOrRecommendation != prevVal.hasActiveMediaOrRecommendation) {
+        if (hasAnyMediaOrRecommendation != prevVal.hasAnyMediaOrRecommendation) {
             row.logChange(
                 columnName = "isMediaActive",
-                value = hasActiveMediaOrRecommendation,
+                value = hasAnyMediaOrRecommendation,
             )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index fe9154c..882991aa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -80,10 +80,10 @@
     }
 
     private fun updateMediaModel(data: MediaData? = null) {
-        if (mediaDataManager.hasActiveMediaOrRecommendation()) {
+        if (mediaDataManager.hasAnyMediaOrRecommendation()) {
             _mediaModel.value =
                 CommunalMediaModel(
-                    hasActiveMediaOrRecommendation = true,
+                    hasAnyMediaOrRecommendation = true,
                     createdTimestampMillis = data?.createdTimestampMillis ?: 0L,
                 )
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
index 86241a5..f77dd58 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSmartspaceRepository.kt
@@ -24,6 +24,9 @@
 import com.android.systemui.communal.smartspace.CommunalSmartspaceController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.util.time.SystemClock
 import java.util.concurrent.Executor
@@ -49,8 +52,11 @@
     private val communalSmartspaceController: CommunalSmartspaceController,
     @Main private val uiExecutor: Executor,
     private val systemClock: SystemClock,
+    @CommunalLog logBuffer: LogBuffer,
 ) : CommunalSmartspaceRepository, BcSmartspaceDataPlugin.SmartspaceTargetListener {
 
+    private val logger = Logger(logBuffer, "CommunalSmartspaceRepository")
+
     private val _timers: MutableStateFlow<List<CommunalSmartspaceTimer>> =
         MutableStateFlow(emptyList())
     override val timers: Flow<List<CommunalSmartspaceTimer>> = _timers
@@ -87,6 +93,8 @@
                     remoteViews = target.remoteViews!!,
                 )
             }
+
+        logger.d({ "Smartspace timers updated: $str1" }) { str1 = _timers.value.toString() }
     }
 
     override fun startListening() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
index ad0bfc7..6cdd9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt
@@ -57,12 +57,17 @@
     /** A flow of information about active communal widgets stored in database. */
     val communalWidgets: Flow<List<CommunalWidgetContentModel>>
 
-    /** Add a widget at the specified position in the app widget service and the database. */
+    /**
+     * Add a widget in the app widget service and the database.
+     *
+     * @param rank The rank of the widget determines its position in the grid. 0 is first place, 1
+     *   is second, etc. If rank is not specified, widget is added at the end.
+     */
     fun addWidget(
         provider: ComponentName,
         user: UserHandle,
-        priority: Int,
-        configurator: WidgetConfigurator? = null
+        rank: Int?,
+        configurator: WidgetConfigurator? = null,
     ) {}
 
     /**
@@ -75,9 +80,9 @@
     /**
      * Update the order of widgets in the database.
      *
-     * @param widgetIdToPriorityMap mapping of the widget ids to the priority of the widget.
+     * @param widgetIdToRankMap mapping of the widget ids to the rank of the widget.
      */
-    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {}
+    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {}
 
     /**
      * Restores the database by reading a state file from disk and updating the widget ids according
@@ -121,7 +126,7 @@
                 CommunalWidgetEntry(
                     appWidgetId = widget.widgetId,
                     componentName = widget.componentName,
-                    priority = rank.rank,
+                    rank = rank.rank,
                     providerInfo = providers[widget.widgetId]
                 )
             }
@@ -151,8 +156,8 @@
     override fun addWidget(
         provider: ComponentName,
         user: UserHandle,
-        priority: Int,
-        configurator: WidgetConfigurator?
+        rank: Int?,
+        configurator: WidgetConfigurator?,
     ) {
         bgScope.launch {
             val id = communalWidgetHost.allocateIdAndBindWidget(provider, user)
@@ -190,14 +195,14 @@
                 communalWidgetDao.addWidget(
                     widgetId = id,
                     provider = provider,
-                    priority = priority,
+                    rank = rank,
                     userSerialNumber = userManager.getUserSerialNumber(user.identifier),
                 )
                 backupManager.dataChanged()
             } else {
                 appWidgetHost.deleteAppWidgetId(id)
             }
-            logger.i("Added widget ${provider.flattenToString()} at position $priority.")
+            logger.i("Added widget ${provider.flattenToString()} at position $rank.")
         }
     }
 
@@ -211,11 +216,11 @@
         }
     }
 
-    override fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) {
+    override fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) {
         bgScope.launch {
-            communalWidgetDao.updateWidgetOrder(widgetIdToPriorityMap)
+            communalWidgetDao.updateWidgetOrder(widgetIdToRankMap)
             logger.i({ "Updated the order of widget list with ids: $str1." }) {
-                str1 = widgetIdToPriorityMap.toString()
+                str1 = widgetIdToRankMap.toString()
             }
             backupManager.dataChanged()
         }
@@ -342,7 +347,7 @@
                 addWidget(
                     provider = ComponentName.unflattenFromString(widget.componentName)!!,
                     user = newUser,
-                    priority = widget.rank,
+                    rank = widget.rank,
                 )
             }
 
@@ -377,7 +382,7 @@
         return CommunalWidgetContentModel.Available(
             appWidgetId = entry.appWidgetId,
             providerInfo = entry.providerInfo!!,
-            priority = entry.priority,
+            rank = entry.rank,
         )
     }
 
@@ -394,7 +399,7 @@
             return CommunalWidgetContentModel.Available(
                 appWidgetId = entry.appWidgetId,
                 providerInfo = entry.providerInfo!!,
-                priority = entry.priority,
+                rank = entry.rank,
             )
         }
 
@@ -403,7 +408,7 @@
         return if (componentName != null && session != null) {
             CommunalWidgetContentModel.Pending(
                 appWidgetId = entry.appWidgetId,
-                priority = entry.priority,
+                rank = entry.rank,
                 componentName = componentName,
                 icon = session.icon,
                 user = session.user,
@@ -416,7 +421,7 @@
     private data class CommunalWidgetEntry(
         val appWidgetId: Int,
         val componentName: String,
-        val priority: Int,
+        val rank: Int,
         var providerInfo: AppWidgetProviderInfo? = null,
     )
 }
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 6aaaf3d..b570e14 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
@@ -61,6 +61,7 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.phone.ManagedProfileController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.emitOnStart
@@ -116,6 +117,7 @@
     sceneInteractor: SceneInteractor,
     @CommunalLog logBuffer: LogBuffer,
     @CommunalTableLog tableLogBuffer: TableLogBuffer,
+    private val managedProfileController: ManagedProfileController
 ) {
     private val logger = Logger(logBuffer, "CommunalInteractor")
 
@@ -140,6 +142,10 @@
      */
     val editActivityShowing: StateFlow<Boolean> = _editActivityShowing.asStateFlow()
 
+    private val _selectedKey: MutableStateFlow<String?> = MutableStateFlow(null)
+
+    val selectedKey: StateFlow<String?> = _selectedKey.asStateFlow()
+
     /** Whether communal features are enabled. */
     val isCommunalEnabled: StateFlow<Boolean> = communalSettingsInteractor.isCommunalEnabled
 
@@ -179,12 +185,20 @@
         }
     }
 
+    fun setSelectedKey(key: String?) {
+        _selectedKey.value = key
+    }
+
     /** Whether to show communal when exiting the occluded state. */
     val showCommunalFromOccluded: Flow<Boolean> =
         keyguardTransitionInteractor.startedKeyguardTransitionStep
             .filter { step -> step.to == KeyguardState.OCCLUDED }
             .combine(isCommunalAvailable, ::Pair)
-            .map { (step, available) -> available && step.from == KeyguardState.GLANCEABLE_HUB }
+            .map { (step, available) ->
+                available &&
+                    (step.from == KeyguardState.GLANCEABLE_HUB ||
+                        step.from == KeyguardState.DREAMING)
+            }
             .flowOn(bgDispatcher)
             .stateIn(
                 scope = applicationScope,
@@ -325,8 +339,11 @@
     @Deprecated(
         "Use com.android.systemui.communal.domain.interactor.CommunalSceneInteractor instead"
     )
-    fun changeScene(newScene: SceneKey, transitionKey: TransitionKey? = null) =
-        communalSceneInteractor.changeScene(newScene, transitionKey)
+    fun changeScene(
+        newScene: SceneKey,
+        loggingReason: String,
+        transitionKey: TransitionKey? = null
+    ) = communalSceneInteractor.changeScene(newScene, loggingReason, transitionKey)
 
     fun setEditModeOpen(isOpen: Boolean) {
         _editModeOpen.value = isOpen
@@ -338,11 +355,10 @@
 
     /** Show the widget editor Activity. */
     fun showWidgetEditor(
-        preselectedKey: String? = null,
         shouldOpenWidgetPickerOnStart: Boolean = false,
     ) {
         communalSceneInteractor.setEditModeState(EditModeState.STARTING)
-        editWidgetsActivityStarter.startActivity(preselectedKey, shouldOpenWidgetPickerOnStart)
+        editWidgetsActivityStarter.startActivity(shouldOpenWidgetPickerOnStart)
     }
 
     /**
@@ -360,13 +376,16 @@
     /** Dismiss the CTA tile from the hub in view mode. */
     suspend fun dismissCtaTile() = communalPrefsInteractor.setCtaDismissed()
 
-    /** Add a widget at the specified position. */
+    /**
+     * Add a widget at the specified rank. If rank is not provided, the widget will be added at the
+     * end.
+     */
     fun addWidget(
         componentName: ComponentName,
         user: UserHandle,
-        priority: Int,
+        rank: Int? = null,
         configurator: WidgetConfigurator?,
-    ) = widgetRepository.addWidget(componentName, user, priority, configurator)
+    ) = widgetRepository.addWidget(componentName, user, rank, configurator)
 
     /**
      * Delete a widget by id. Called when user deletes a widget from the hub or a widget is
@@ -377,19 +396,14 @@
     /**
      * Reorder the widgets.
      *
-     * @param widgetIdToPriorityMap mapping of the widget ids to their new priorities.
+     * @param widgetIdToRankMap mapping of the widget ids to their new priorities.
      */
-    fun updateWidgetOrder(widgetIdToPriorityMap: Map<Int, Int>) =
-        widgetRepository.updateWidgetOrder(widgetIdToPriorityMap)
+    fun updateWidgetOrder(widgetIdToRankMap: Map<Int, Int>) =
+        widgetRepository.updateWidgetOrder(widgetIdToRankMap)
 
     /** Request to unpause work profile that is currently in quiet mode. */
     fun unpauseWorkProfile() {
-        userTracker.userProfiles
-            .find { it.isManagedProfile }
-            ?.userHandle
-            ?.let { userHandle ->
-                userManager.requestQuietModeEnabled(/* enableQuietMode */ false, userHandle)
-            }
+        managedProfileController.setWorkModeEnabled(true)
     }
 
     /** Returns true if work profile is in quiet mode (disabled) for user handle. */
@@ -433,7 +447,7 @@
                     is CommunalWidgetContentModel.Available -> {
                         WidgetContent.Widget(
                             appWidgetId = widget.appWidgetId,
-                            priority = widget.priority,
+                            rank = widget.rank,
                             providerInfo = widget.providerInfo,
                             appWidgetHost = appWidgetHost,
                             inQuietMode = isQuietModeEnabled(widget.providerInfo.profile)
@@ -442,7 +456,7 @@
                     is CommunalWidgetContentModel.Pending -> {
                         WidgetContent.PendingWidget(
                             appWidgetId = widget.appWidgetId,
-                            priority = widget.priority,
+                            rank = widget.rank,
                             componentName = widget.componentName,
                             icon = widget.icon,
                         )
@@ -493,7 +507,7 @@
      * A flow of ongoing content, including smartspace timers and umo, ordered by creation time and
      * sized dynamically.
      */
-    fun getOngoingContent(mediaHostVisible: Boolean): Flow<List<CommunalContentModel.Ongoing>> =
+    val ongoingContent: Flow<List<CommunalContentModel.Ongoing>> =
         combine(smartspaceRepository.timers, mediaRepository.mediaModel) { timers, media ->
                 val ongoingContent = mutableListOf<CommunalContentModel.Ongoing>()
 
@@ -509,7 +523,7 @@
                 )
 
                 // Add UMO
-                if (mediaHostVisible && media.hasActiveMediaOrRecommendation) {
+                if (media.hasAnyMediaOrRecommendation) {
                     ongoingContent.add(
                         CommunalContentModel.Umo(
                             createdTimestampMillis = media.createdTimestampMillis,
@@ -597,11 +611,6 @@
         _firstVisibleItemOffset = firstVisibleItemOffset
     }
 
-    fun resetScrollPosition() {
-        _firstVisibleItemIndex = 0
-        _firstVisibleItemOffset = 0
-    }
-
     val firstVisibleItemIndex: Int
         get() = _firstVisibleItemIndex
 
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 e45a695..ac496f0 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
@@ -22,6 +22,7 @@
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.communal.data.repository.CommunalSceneRepository
 import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.shared.log.CommunalSceneLogger
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.communal.shared.model.EditModeState
@@ -29,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.pairwiseBy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,8 +44,8 @@
 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
-import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
@@ -51,7 +53,8 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val communalSceneRepository: CommunalSceneRepository,
+    private val repository: CommunalSceneRepository,
+    private val logger: CommunalSceneLogger,
 ) {
     private val _isLaunchingWidget = MutableStateFlow(false)
 
@@ -80,25 +83,41 @@
      */
     fun changeScene(
         newScene: SceneKey,
+        loggingReason: String,
         transitionKey: TransitionKey? = null,
         keyguardState: KeyguardState? = null,
     ) {
-        applicationScope.launch {
+        applicationScope.launch("$TAG#changeScene") {
+            if (currentScene.value == newScene) return@launch
+            logger.logSceneChangeRequested(
+                from = currentScene.value,
+                to = newScene,
+                reason = loggingReason,
+                isInstant = false,
+            )
             notifyListeners(newScene, keyguardState)
-            communalSceneRepository.changeScene(newScene, transitionKey)
+            repository.changeScene(newScene, transitionKey)
         }
     }
 
     /** Immediately snaps to the new scene. */
     fun snapToScene(
         newScene: SceneKey,
+        loggingReason: String,
         delayMillis: Long = 0,
         keyguardState: KeyguardState? = null
     ) {
         applicationScope.launch("$TAG#snapToScene") {
             delay(delayMillis)
+            if (currentScene.value == newScene) return@launch
+            logger.logSceneChangeRequested(
+                from = currentScene.value,
+                to = newScene,
+                reason = loggingReason,
+                isInstant = true,
+            )
             notifyListeners(newScene, keyguardState)
-            communalSceneRepository.snapToScene(newScene)
+            repository.snapToScene(newScene)
         }
     }
 
@@ -113,13 +132,30 @@
         if (_editModeState.value == EditModeState.STARTING) {
             return
         }
-        changeScene(CommunalScenes.Blank, CommunalTransitionKeys.SimpleFade)
+        changeScene(
+            CommunalScenes.Blank,
+            "activity start dismissing keyguard",
+            CommunalTransitionKeys.SimpleFade,
+        )
     }
 
     /**
      * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene].
      */
-    val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene
+    val currentScene: StateFlow<SceneKey> =
+        repository.currentScene
+            .pairwiseBy(initialValue = repository.currentScene.value) { from, to ->
+                logger.logSceneChangeCommitted(
+                    from = from,
+                    to = to,
+                )
+                to
+            }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = repository.currentScene.value,
+            )
 
     private val _editModeState = MutableStateFlow<EditModeState?>(null)
     /**
@@ -134,7 +170,13 @@
 
     /** Transition state of the hub mode. */
     val transitionState: StateFlow<ObservableTransitionState> =
-        communalSceneRepository.transitionState
+        repository.transitionState
+            .onEach { logger.logSceneTransition(it) }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = repository.transitionState.value,
+            )
 
     /**
      * Updates the transition state of the hub [SceneTransitionLayout].
@@ -142,7 +184,7 @@
      * Note that you must call is with `null` when the UI is done or risk a memory leak.
      */
     fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
-        communalSceneRepository.setTransitionState(transitionState)
+        repository.setTransitionState(transitionState)
     }
 
     /** Returns a flow that tracks the progress of transitions to the given scene from 0-1. */
@@ -153,7 +195,7 @@
                     is ObservableTransitionState.Idle ->
                         flowOf(CommunalTransitionProgressModel.Idle(state.currentScene))
                     is ObservableTransitionState.Transition ->
-                        if (state.toScene == targetScene) {
+                        if (state.toContent == targetScene) {
                             state.progress.map {
                                 CommunalTransitionProgressModel.Transition(
                                     // Clamp the progress values between 0 and 1 as actual progress
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index c780aac..c7538bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -41,6 +41,8 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
@@ -85,25 +87,29 @@
      */
     private val nextKeyguardStateInternal =
         combine(
-            keyguardInteractor.isAbleToDream,
-            keyguardInteractor.isKeyguardOccluded,
-            keyguardInteractor.isKeyguardGoingAway,
-        ) { dreaming, occluded, keyguardGoingAway ->
-            if (keyguardGoingAway) {
-                KeyguardState.GONE
-            } else if (occluded && !dreaming) {
-                KeyguardState.OCCLUDED
-            } else if (dreaming) {
-                KeyguardState.DREAMING
-            } else {
-                KeyguardState.LOCKSCREEN
+                keyguardInteractor.isAbleToDream,
+                keyguardInteractor.isKeyguardOccluded,
+                keyguardInteractor.isKeyguardGoingAway,
+                keyguardInteractor.isKeyguardShowing,
+            ) { dreaming, occluded, keyguardGoingAway, keyguardShowing ->
+                if (keyguardGoingAway) {
+                    KeyguardState.GONE
+                } else if (occluded && !dreaming) {
+                    KeyguardState.OCCLUDED
+                } else if (dreaming) {
+                    KeyguardState.DREAMING
+                } else if (keyguardShowing) {
+                    KeyguardState.LOCKSCREEN
+                } else {
+                    null
+                }
             }
-        }
+            .filterNotNull()
 
     private val nextKeyguardState: StateFlow<KeyguardState> =
         combine(
                 repository.nextLockscreenTargetState,
-                nextKeyguardStateInternal,
+                nextKeyguardStateInternal.onStart { emit(KeyguardState.LOCKSCREEN) },
             ) { override, nextState ->
                 override ?: nextState
             }
@@ -155,7 +161,7 @@
         if (
             prevTransition is ObservableTransitionState.Transition &&
                 currentTransitionId != null &&
-                idle.currentScene == prevTransition.toScene
+                idle.currentScene == prevTransition.toContent
         ) {
             finishCurrentTransition()
         } else {
@@ -176,6 +182,7 @@
     }
 
     private suspend fun finishCurrentTransition() {
+        if (currentTransitionId == null) return
         internalTransitionInteractor.updateTransition(
             currentTransitionId!!,
             1f,
@@ -212,24 +219,22 @@
         prevTransition: ObservableTransitionState,
         transition: ObservableTransitionState.Transition
     ) {
-        if (prevTransition.isTransitioning(from = transition.fromScene, to = transition.toScene)) {
+        if (
+            prevTransition.isTransitioning(from = transition.fromContent, to = transition.toContent)
+        ) {
             // This is a new transition, but exactly the same as the previous state. Skip resetting
             // KTF for this case and just collect the new progress instead.
             collectProgress(transition)
-        } else if (transition.toScene == CommunalScenes.Communal) {
-            if (currentTransitionId != null) {
-                if (currentToState == KeyguardState.GLANCEABLE_HUB) {
-                    transitionKtfTo(transitionInteractor.getStartedFromState())
-                }
+        } else if (transition.toContent == CommunalScenes.Communal) {
+            if (currentToState == KeyguardState.GLANCEABLE_HUB) {
+                transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
             }
             startTransitionToGlanceableHub()
             collectProgress(transition)
-        } else if (transition.toScene == CommunalScenes.Blank) {
-            if (currentTransitionId != null) {
-                // Another transition started before this one is completed. Transition to the
-                // GLANCEABLE_HUB state so that we can properly transition away from it.
-                transitionKtfTo(KeyguardState.GLANCEABLE_HUB)
-            }
+        } else if (transition.toContent == CommunalScenes.Blank) {
+            // Another transition started before this one is completed. Transition to the
+            // GLANCEABLE_HUB state so that we can properly transition away from it.
+            transitionKtfTo(KeyguardState.GLANCEABLE_HUB)
             startTransitionFromGlanceableHub()
             collectProgress(transition)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
new file mode 100644
index 0000000..7453368
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractor.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.domain.interactor
+
+import android.app.ActivityManager
+import com.android.systemui.common.usagestats.domain.UsageStatsInteractor
+import com.android.systemui.common.usagestats.shared.model.ActivityEventModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.system.TaskStackChangeListener
+import com.android.systemui.shared.system.TaskStackChangeListeners
+import com.android.systemui.util.kotlin.race
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.takeWhile
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withTimeout
+
+/**
+ * Detects activity starts that occur while the communal hub is showing, within a short delay of a
+ * widget interaction occurring. Used for detecting non-activity trampolines which otherwise would
+ * not prompt the user for authentication.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class WidgetTrampolineInteractor
+@Inject
+constructor(
+    private val activityStarter: ActivityStarter,
+    private val systemClock: SystemClock,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val taskStackChangeListeners: TaskStackChangeListeners,
+    private val usageStatsInteractor: UsageStatsInteractor,
+    @CommunalLog logBuffer: LogBuffer,
+) {
+    private companion object {
+        const val TAG = "WidgetTrampolineInteractor"
+    }
+
+    private val logger = Logger(logBuffer, TAG)
+
+    /** Waits for a new task to be moved to the foreground. */
+    private suspend fun waitForNewForegroundTask() = suspendCancellableCoroutine { cont ->
+        val listener =
+            object : TaskStackChangeListener {
+                override fun onTaskMovedToFront(taskInfo: ActivityManager.RunningTaskInfo) {
+                    if (!cont.isCompleted) {
+                        cont.resume(Unit, null)
+                    }
+                }
+            }
+        taskStackChangeListeners.registerTaskStackListener(listener)
+        cont.invokeOnCancellation { taskStackChangeListeners.unregisterTaskStackListener(listener) }
+    }
+
+    /**
+     * Waits for an activity to enter a [ActivityEventModel.Lifecycle.RESUMED] state by periodically
+     * polling the system to see if any activities have started.
+     */
+    private suspend fun waitForActivityStartByPolling(startTime: Long): Boolean {
+        while (true) {
+            val events = usageStatsInteractor.queryActivityEvents(startTime = startTime)
+            if (events.any { event -> event.lifecycle == ActivityEventModel.Lifecycle.RESUMED }) {
+                return true
+            } else {
+                // Poll again in the future to check if an activity started.
+                delay(200.milliseconds)
+            }
+        }
+    }
+
+    /** Waits for a transition away from the hub to occur. */
+    private suspend fun waitForTransitionAwayFromHub() {
+        keyguardTransitionInteractor
+            .isFinishedIn(Scenes.Communal, KeyguardState.GLANCEABLE_HUB)
+            .takeWhile { it }
+            .collect {}
+    }
+
+    private suspend fun waitForActivityStartWhileOnHub(): Boolean {
+        val startTime = systemClock.currentTimeMillis()
+        return try {
+            return withTimeout(1.seconds) {
+                race(
+                    {
+                        waitForNewForegroundTask()
+                        true
+                    },
+                    { waitForActivityStartByPolling(startTime) },
+                    {
+                        waitForTransitionAwayFromHub()
+                        false
+                    },
+                )
+            }
+        } catch (e: TimeoutCancellationException) {
+            false
+        }
+    }
+
+    /**
+     * Checks if an activity starts while on the glanceable hub and dismisses the keyguard if it
+     * does. This can detect activities started due to broadcast trampolines from widgets.
+     */
+    suspend fun waitForActivityStartAndDismissKeyguard() {
+        if (waitForActivityStartWhileOnHub()) {
+            logger.d("Detected trampoline, requesting unlock")
+            activityStarter.dismissKeyguardThenExecute(
+                /* action= */ { false },
+                /* cancel= */ null,
+                /* afterKeyguardGone= */ false
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
index 73c6ce3..4c821d4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt
@@ -47,12 +47,12 @@
 
     sealed interface WidgetContent : CommunalContentModel {
         val appWidgetId: Int
-        val priority: Int
+        val rank: Int
         val componentName: ComponentName
 
         data class Widget(
             override val appWidgetId: Int,
-            override val priority: Int,
+            override val rank: Int,
             val providerInfo: AppWidgetProviderInfo,
             val appWidgetHost: CommunalAppWidgetHost,
             val inQuietMode: Boolean,
@@ -71,7 +71,7 @@
 
         data class DisabledWidget(
             override val appWidgetId: Int,
-            override val priority: Int,
+            override val rank: Int,
             val providerInfo: AppWidgetProviderInfo
         ) : WidgetContent {
             override val key = KEY.disabledWidget(appWidgetId)
@@ -85,7 +85,7 @@
 
         data class PendingWidget(
             override val appWidgetId: Int,
-            override val priority: Int,
+            override val rank: Int,
             override val componentName: ComponentName,
             val icon: Bitmap? = null,
         ) : WidgetContent {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
index 2352841f..1def5a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/log/CommunalLoggerStartable.kt
@@ -126,13 +126,13 @@
 /** Whether currently transitioning from another scene to communal. */
 private fun ObservableTransitionState.isSwipingToCommunal(): Boolean {
     return this is ObservableTransitionState.Transition &&
-        toScene == CommunalScenes.Communal &&
+        toContent == CommunalScenes.Communal &&
         isInitiatedByUserInput
 }
 
 /** Whether currently transitioning from communal to another scene. */
 private fun ObservableTransitionState.isSwipingFromCommunal(): Boolean {
     return this is ObservableTransitionState.Transition &&
-        fromScene == CommunalScenes.Communal &&
+        fromContent == CommunalScenes.Communal &&
         isInitiatedByUserInput
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
index 9ce8cf7..7cfad60 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt
@@ -31,7 +31,7 @@
     private val statsLogProxy: StatsLogProxy,
 ) {
     /** Logs an add widget event for metrics. No-op if widget is not loggable. */
-    fun logAddWidget(componentName: String, rank: Int) {
+    fun logAddWidget(componentName: String, rank: Int?) {
         if (!componentName.isLoggable()) {
             return
         }
@@ -39,7 +39,7 @@
         statsLogProxy.writeCommunalHubWidgetEventReported(
             SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD,
             componentName,
-            rank,
+            rank ?: -1,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
new file mode 100644
index 0000000..83f31e5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalSceneLogger.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.shared.log
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.CommunalLog
+import javax.inject.Inject
+
+class CommunalSceneLogger @Inject constructor(@CommunalLog private val logBuffer: LogBuffer) {
+
+    fun logSceneChangeRequested(
+        from: SceneKey,
+        to: SceneKey,
+        reason: String,
+        isInstant: Boolean,
+    ) {
+        logBuffer.log(
+            tag = TAG,
+            level = LogLevel.INFO,
+            messageInitializer = {
+                str1 = from.toString()
+                str2 = to.toString()
+                str3 = reason
+                bool1 = isInstant
+            },
+            messagePrinter = {
+                buildString {
+                    append("Scene change requested: $str1 → $str2")
+                    if (isInstant) {
+                        append(" (instant)")
+                    }
+                    append(", reason: $str3")
+                }
+            },
+        )
+    }
+
+    fun logSceneChangeCommitted(
+        from: SceneKey,
+        to: SceneKey,
+    ) {
+        logBuffer.log(
+            tag = TAG,
+            level = LogLevel.INFO,
+            messageInitializer = {
+                str1 = from.toString()
+                str2 = to.toString()
+            },
+            messagePrinter = { "Scene change committed: $str1 → $str2" },
+        )
+    }
+
+    fun logSceneTransition(transitionState: ObservableTransitionState) {
+        when (transitionState) {
+            is ObservableTransitionState.Transition -> {
+                logBuffer.log(
+                    tag = TAG,
+                    level = LogLevel.INFO,
+                    messageInitializer = {
+                        str1 = transitionState.fromContent.toString()
+                        str2 = transitionState.toContent.toString()
+                    },
+                    messagePrinter = { "Scene transition started: $str1 → $str2" },
+                )
+            }
+            is ObservableTransitionState.Idle -> {
+                logBuffer.log(
+                    tag = TAG,
+                    level = LogLevel.INFO,
+                    messageInitializer = { str1 = transitionState.currentScene.toString() },
+                    messagePrinter = { "Scene transition idle on: $str1" },
+                )
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "CommunalSceneLogger"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
index 7cddb72..63b1a14 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt
@@ -24,19 +24,19 @@
 /** Encapsulates data for a communal widget. */
 sealed interface CommunalWidgetContentModel {
     val appWidgetId: Int
-    val priority: Int
+    val rank: Int
 
     /** Widget is ready to display */
     data class Available(
         override val appWidgetId: Int,
         val providerInfo: AppWidgetProviderInfo,
-        override val priority: Int,
+        override val rank: Int,
     ) : CommunalWidgetContentModel
 
     /** Widget is pending installation */
     data class Pending(
         override val appWidgetId: Int,
-        override val priority: Int,
+        override val rank: Int,
         val componentName: ComponentName,
         val icon: Bitmap?,
         val user: UserHandle,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
index 80db535..012c844 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/CommunalSmartspaceController.kt
@@ -65,6 +65,12 @@
     var preconditionListener =
         object : SmartspacePrecondition.Listener {
             override fun onCriteriaChanged() {
+                if (session == null && hasActiveSessionListeners()) {
+                    Log.d(TAG, "Precondition criteria changed. Attempting to connect session.")
+                    connectSession()
+                    return
+                }
+
                 reloadSmartspace()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
index c4edcac..99e3232 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/smartspace/SmartspaceInteractionHandler.kt
@@ -48,7 +48,17 @@
         InteractionHandlerDelegate(
             communalSceneInteractor,
             findViewToAnimate = { view -> view is SmartspaceAppWidgetHostView },
-            intentStarter = this::startIntent,
+            intentStarter =
+                object : InteractionHandlerDelegate.IntentStarter {
+                    override fun startActivity(
+                        intent: PendingIntent,
+                        fillInIntent: Intent,
+                        activityOptions: ActivityOptions,
+                        controller: ActivityTransitionAnimator.Controller?
+                    ): Boolean {
+                        return startIntent(intent, fillInIntent, activityOptions, controller)
+                    }
+                },
             logger = Logger(logBuffer, TAG),
         )
 
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
new file mode 100644
index 0000000..5f421fd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt
@@ -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.systemui.communal.ui.binder
+
+import android.content.Context
+import android.util.SizeF
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.view.doOnLayout
+import com.android.app.tracing.coroutines.launch
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.util.WidgetViewFactory
+import com.android.systemui.util.kotlin.DisposableHandles
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+
+object CommunalAppWidgetHostViewBinder {
+    private const val TAG = "CommunalAppWidgetHostViewBinder"
+
+    fun bind(
+        context: Context,
+        applicationScope: CoroutineScope,
+        container: FrameLayout,
+        model: CommunalContentModel.WidgetContent.Widget,
+        size: SizeF,
+        factory: WidgetViewFactory,
+    ): DisposableHandle {
+        val disposables = DisposableHandles()
+
+        val loadingJob =
+            applicationScope.launch("$TAG#createWidgetView") {
+                val widget = factory.createWidget(context, model, size)
+                waitForLayout(container)
+                container.post { container.setView(widget) }
+            }
+
+        disposables += DisposableHandle { loadingJob.cancel() }
+        disposables += DisposableHandle { container.removeAllViews() }
+
+        return disposables
+    }
+
+    private suspend fun waitForLayout(container: FrameLayout) = suspendCoroutine { cont ->
+        container.doOnLayout { cont.resume(Unit) }
+    }
+}
+
+private fun ViewGroup.setView(view: View) {
+    if (view.parent == this) {
+        return
+    }
+    (view.parent as? ViewGroup)?.removeView(view)
+    addView(view)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
new file mode 100644
index 0000000..56b769e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/CommunalAppWidgetSection.kt
@@ -0,0 +1,105 @@
+/*
+ * 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.view.layout.sections
+
+import android.util.SizeF
+import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
+import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+import android.widget.FrameLayout
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.ui.binder.CommunalAppWidgetHostViewBinder
+import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
+import com.android.systemui.communal.util.WidgetViewFactory
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+
+class CommunalAppWidgetSection
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val factory: WidgetViewFactory,
+) {
+
+    private companion object {
+        val DISPOSABLE_TAG = R.id.communal_widget_disposable_tag
+    }
+
+    @Composable
+    fun Widget(
+        viewModel: BaseCommunalViewModel,
+        model: CommunalContentModel.WidgetContent.Widget,
+        size: SizeF,
+        modifier: Modifier = Modifier,
+    ) {
+        val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
+
+        AndroidView(
+            factory = { context ->
+                FrameLayout(context).apply {
+                    layoutParams =
+                        FrameLayout.LayoutParams(
+                            FrameLayout.LayoutParams.MATCH_PARENT,
+                            FrameLayout.LayoutParams.MATCH_PARENT,
+                        )
+
+                    // Need to attach the disposable handle to the view here instead of storing
+                    // the state in the composable in order to properly support lazy lists. In a
+                    // lazy list, when the composable is no longer in view - it will exit
+                    // composition and any state inside the composable will be lost. However,
+                    // the View instance will be re-used. Therefore we can store data on the view
+                    // in order to preserve it.
+                    setTag(
+                        DISPOSABLE_TAG,
+                        CommunalAppWidgetHostViewBinder.bind(
+                            context = context,
+                            container = this,
+                            model = model,
+                            size = size,
+                            factory = factory,
+                            applicationScope = applicationScope,
+                        )
+                    )
+
+                    accessibilityDelegate = viewModel.widgetAccessibilityDelegate
+                }
+            },
+            update = { container ->
+                container.importantForAccessibility =
+                    if (isFocusable) {
+                        IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                    } else {
+                        IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                    }
+            },
+            onRelease = { view ->
+                val disposable = (view.getTag(DISPOSABLE_TAG) as? DisposableHandle)
+                disposable?.dispose()
+            },
+            modifier = modifier,
+            // For reusing composition in lazy lists.
+            onReset = {},
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index d1a5a4b..0929d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -55,11 +55,8 @@
     /** Whether widgets are currently being re-ordered. */
     open val reorderingWidgets: StateFlow<Boolean> = MutableStateFlow(false)
 
-    private val _selectedKey: MutableStateFlow<String?> = MutableStateFlow(null)
-
     /** The key of the currently selected item, or null if no item selected. */
-    val selectedKey: StateFlow<String?>
-        get() = _selectedKey
+    val selectedKey: StateFlow<String?> = communalInteractor.selectedKey
 
     private val _isTouchConsumed: MutableStateFlow<Boolean> = MutableStateFlow(false)
 
@@ -106,10 +103,11 @@
      */
     fun changeScene(
         scene: SceneKey,
+        loggingReason: String,
         transitionKey: TransitionKey? = null,
         keyguardState: KeyguardState? = null
     ) {
-        communalSceneInteractor.changeScene(scene, transitionKey, keyguardState)
+        communalSceneInteractor.changeScene(scene, loggingReason, transitionKey, keyguardState)
     }
 
     fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state)
@@ -152,7 +150,7 @@
     open fun onAddWidget(
         componentName: ComponentName,
         user: UserHandle,
-        priority: Int,
+        rank: Int? = null,
         configurator: WidgetConfigurator? = null,
     ) {}
 
@@ -160,23 +158,23 @@
     open fun onDeleteWidget(
         id: Int,
         componentName: ComponentName,
-        priority: Int,
+        rank: Int,
     ) {}
 
     /** Called as the UI detects a tap event on the widget. */
     open fun onTapWidget(
         componentName: ComponentName,
-        priority: Int,
+        rank: Int,
     ) {}
 
     /**
      * Called as the UI requests reordering widgets.
      *
-     * @param widgetIdToPriorityMap mapping of the widget ids to its priority. When re-ordering to
-     *   add a new item in the middle, provide the priorities of existing widgets as if the new item
-     *   existed, and then, call [onAddWidget] to add the new item at intended order.
+     * @param widgetIdToRankMap mapping of the widget ids to its rank. When re-ordering to add a new
+     *   item in the middle, provide the priorities of existing widgets as if the new item existed,
+     *   and then, call [onAddWidget] to add the new item at intended order.
      */
-    open fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) {}
+    open fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) {}
 
     /** Called as the UI requests opening the widget editor with an optional preselected widget. */
     open fun onOpenWidgetEditor(
@@ -225,7 +223,7 @@
 
     /** Set the key of the currently selected item */
     fun setSelectedKey(key: String?) {
-        _selectedKey.value = key
+        communalInteractor.setSelectedKey(key)
     }
 
     /** Invoked once touches inside the lazy grid are consumed */
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 1a86c71..65f0679 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
@@ -29,6 +29,7 @@
 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
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -81,6 +82,7 @@
     @Application private val context: Context,
     private val accessibilityManager: AccessibilityManager,
     private val packageManager: PackageManager,
+    @Named(LAUNCHER_PACKAGE) private val launcherPackage: String,
 ) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
 
     private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
@@ -125,24 +127,24 @@
     override fun onAddWidget(
         componentName: ComponentName,
         user: UserHandle,
-        priority: Int,
+        rank: Int?,
         configurator: WidgetConfigurator?
     ) {
-        communalInteractor.addWidget(componentName, user, priority, configurator)
-        metricsLogger.logAddWidget(componentName.flattenToString(), priority)
+        communalInteractor.addWidget(componentName, user, rank, configurator)
+        metricsLogger.logAddWidget(componentName.flattenToString(), rank)
     }
 
     override fun onDeleteWidget(
         id: Int,
         componentName: ComponentName,
-        priority: Int,
+        rank: Int,
     ) {
         communalInteractor.deleteWidget(id)
-        metricsLogger.logRemoveWidget(componentName.flattenToString(), priority)
+        metricsLogger.logRemoveWidget(componentName.flattenToString(), rank)
     }
 
-    override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
-        communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
+    override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) =
+        communalInteractor.updateWidgetOrder(widgetIdToRankMap)
 
     override fun onReorderWidgetStart() {
         // Clear selection status
@@ -185,7 +187,6 @@
     /** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
     suspend fun onOpenWidgetPicker(
         resources: Resources,
-        packageManager: PackageManager,
         activityLauncher: ActivityResultLauncher<Intent>
     ): Boolean =
         withContext(backgroundDispatcher) {
@@ -196,7 +197,7 @@
                 ) {
                     it.providerInfo
                 }
-            getWidgetPickerActivityIntent(resources, packageManager, excludeList)?.let {
+            getWidgetPickerActivityIntent(resources, excludeList)?.let {
                 try {
                     activityLauncher.launch(it)
                     return@withContext true
@@ -209,18 +210,10 @@
 
     private fun getWidgetPickerActivityIntent(
         resources: Resources,
-        packageManager: PackageManager,
         excludeList: ArrayList<AppWidgetProviderInfo>
     ): Intent? {
-        val packageName =
-            getLauncherPackageName(packageManager)
-                ?: run {
-                    Log.e(TAG, "Couldn't resolve launcher package name")
-                    return@getWidgetPickerActivityIntent null
-                }
-
         return Intent(Intent.ACTION_PICK).apply {
-            setPackage(packageName)
+            setPackage(launcherPackage)
             putExtra(
                 EXTRA_DESIRED_WIDGET_WIDTH,
                 resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
@@ -247,16 +240,6 @@
         }
     }
 
-    private fun getLauncherPackageName(packageManager: PackageManager): String? {
-        return packageManager
-            .resolveActivity(
-                Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) },
-                PackageManager.MATCH_DEFAULT_ONLY
-            )
-            ?.activityInfo
-            ?.packageName
-    }
-
     /** Sets whether edit mode is currently open */
     fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
index bbd8596..6239373 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalTransitionViewModel.kt
@@ -67,7 +67,10 @@
      * transition.
      */
     fun snapToCommunal() {
-        communalSceneInteractor.snapToScene(CommunalScenes.Communal)
+        communalSceneInteractor.snapToScene(
+            newScene = CommunalScenes.Communal,
+            loggingReason = "transition view model",
+        )
     }
 
     // Show UMO on glanceable hub immediately on transition into glanceable hub
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index b06cf3f..d69ba1b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -99,7 +99,7 @@
 
     private val logger = Logger(logBuffer, "CommunalViewModel")
 
-    private val _isMediaHostVisible =
+    private val isMediaHostVisible =
         conflatedCallbackFlow {
                 val callback = { visible: Boolean ->
                     trySend(visible)
@@ -117,12 +117,26 @@
                 mediaHost.updateViewVisibility()
                 emit(mediaHost.visible)
             }
+            .distinctUntilChanged()
             .onEach { logger.d({ "_isMediaHostVisible: $bool1" }) { bool1 = it } }
             .flowOn(mainDispatcher)
 
     /** Communal content saved from the previous emission when the flow is active (not "frozen"). */
     private var frozenCommunalContent: List<CommunalContentModel>? = null
 
+    private val ongoingContent =
+        combine(
+            isMediaHostVisible,
+            communalInteractor.ongoingContent.onEach { mediaHost.updateViewVisibility() }
+        ) { mediaVisible, ongoingContent ->
+            if (mediaVisible) {
+                ongoingContent
+            } else {
+                // Media is not visible, don't show UMO
+                ongoingContent.filterNot { it is CommunalContentModel.Umo }
+            }
+        }
+
     @OptIn(ExperimentalCoroutinesApi::class)
     private val latestCommunalContent: Flow<List<CommunalContentModel>> =
         tutorialInteractor.isTutorialAvailable
@@ -130,8 +144,6 @@
                 if (isTutorialMode) {
                     return@flatMapLatest flowOf(communalInteractor.tutorialContent)
                 }
-                val ongoingContent =
-                    _isMediaHostVisible.flatMapLatest { communalInteractor.getOngoingContent(it) }
                 combine(
                     ongoingContent,
                     communalInteractor.widgetContent,
@@ -252,8 +264,9 @@
         with(mediaHost) {
             expansion = MediaHostState.EXPANDED
             expandedMatchesParentHeight = true
-            showsOnlyActiveMedia = true
+            showsOnlyActiveMedia = false
             falsingProtectionNeeded = false
+            disablePagination = true
             init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
         }
     }
@@ -262,7 +275,7 @@
         shouldOpenWidgetPickerOnStart: Boolean,
     ) {
         persistScrollPosition()
-        communalInteractor.showWidgetEditor(selectedKey.value, shouldOpenWidgetPickerOnStart)
+        communalInteractor.showWidgetEditor(shouldOpenWidgetPickerOnStart)
     }
 
     override fun onDismissCtaTile() {
@@ -272,8 +285,8 @@
         }
     }
 
-    override fun onTapWidget(componentName: ComponentName, priority: Int) {
-        metricsLogger.logTapWidget(componentName.flattenToString(), priority)
+    override fun onTapWidget(componentName: ComponentName, rank: Int) {
+        metricsLogger.logTapWidget(componentName.flattenToString(), rank)
     }
 
     fun onClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
index d2029d5..5e21afa 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/InteractionHandlerDelegate.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityOptions
 import android.app.PendingIntent
 import android.content.Intent
+import android.util.Pair as UtilPair
 import android.view.View
 import android.widget.RemoteViews
 import androidx.core.util.component1
@@ -36,14 +37,28 @@
     private val logger: Logger,
 ) : RemoteViews.InteractionHandler {
 
-    /** Responsible for starting the pending intent for launching activities. */
-    fun interface IntentStarter {
-        fun startPendingIntent(
+    interface IntentStarter {
+        /** Responsible for starting the pending intent for launching activities. */
+        fun startActivity(
             intent: PendingIntent,
             fillInIntent: Intent,
             activityOptions: ActivityOptions,
             controller: ActivityTransitionAnimator.Controller?,
         ): Boolean
+
+        /** Responsible for starting the pending intent for non-activity launches. */
+        fun startPendingIntent(
+            view: View,
+            pendingIntent: PendingIntent,
+            fillInIntent: Intent,
+            activityOptions: ActivityOptions,
+        ): Boolean {
+            return RemoteViews.startPendingIntent(
+                view,
+                pendingIntent,
+                UtilPair(fillInIntent, activityOptions),
+            )
+        }
     }
 
     override fun onInteraction(
@@ -55,7 +70,7 @@
             str1 = pendingIntent.toLoggingString()
             str2 = pendingIntent.creatorPackage
         }
-        val launchOptions = response.getLaunchOptions(view)
+        val (fillInIntent, activityOptions) = response.getLaunchOptions(view)
         return when {
             pendingIntent.isActivity -> {
                 // Forward the fill-in intent and activity options retrieved from the response
@@ -67,15 +82,15 @@
                         communalSceneInteractor.setIsLaunchingWidget(true)
                         CommunalTransitionAnimatorController(it, communalSceneInteractor)
                     }
-                val (fillInIntent, activityOptions) = launchOptions
-                intentStarter.startPendingIntent(
+                intentStarter.startActivity(
                     pendingIntent,
                     fillInIntent,
                     activityOptions,
                     animationController
                 )
             }
-            else -> RemoteViews.startPendingIntent(view, pendingIntent, launchOptions)
+            else ->
+                intentStarter.startPendingIntent(view, pendingIntent, fillInIntent, activityOptions)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt
new file mode 100644
index 0000000..0e39a99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.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.communal.util
+
+import android.content.Context
+import android.os.Bundle
+import android.util.SizeF
+import com.android.app.tracing.coroutines.withContext
+import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
+import com.android.systemui.dagger.qualifiers.UiBackground
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+
+/** Factory for creating [CommunalAppWidgetHostView] in a background thread. */
+class WidgetViewFactory
+@Inject
+constructor(
+    @UiBackground private val uiBgContext: CoroutineContext,
+    private val appWidgetHost: CommunalAppWidgetHost,
+) {
+    suspend fun createWidget(
+        context: Context,
+        model: CommunalContentModel.WidgetContent.Widget,
+        size: SizeF,
+    ): CommunalAppWidgetHostView =
+        withContext("$TAG#createWidget", uiBgContext) {
+            appWidgetHost
+                .createViewForCommunal(context, model.appWidgetId, model.providerInfo)
+                .apply {
+                    updateAppWidgetSize(
+                        /* newOptions = */ Bundle(),
+                        /* minWidth = */ size.width.toInt(),
+                        /* minHeight = */ size.height.toInt(),
+                        /* maxWidth = */ size.width.toInt(),
+                        /* maxHeight = */ size.height.toInt(),
+                        /* ignorePadding = */ true,
+                    )
+                }
+        }
+
+    private companion object {
+        const val TAG = "WidgetViewFactory"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
index 0844462..e7cedc6 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalTransitionAnimatorController.kt
@@ -42,6 +42,7 @@
         // TODO(b/330672236): move this to onTransitionAnimationEnd() without the delay.
         communalSceneInteractor.snapToScene(
             CommunalScenes.Blank,
+            "CommunalTransitionAnimatorController",
             ActivityTransitionAnimator.TIMINGS.totalDuration
         )
     }
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 2bcbc9a..d84dc209 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -37,11 +37,13 @@
 import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.logging.UiEventLogger
+import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
 import com.android.systemui.communal.shared.log.CommunalUiEvent
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.communal.shared.model.EditModeState
 import com.android.systemui.communal.ui.compose.CommunalHub
+import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
 import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
 import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -60,12 +62,12 @@
     private var windowManagerService: IWindowManager? = null,
     private val uiEventLogger: UiEventLogger,
     private val widgetConfiguratorFactory: WidgetConfigurationController.Factory,
+    private val widgetSection: CommunalAppWidgetSection,
     @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_PRESELECTED_KEY = "preselected_key"
         const val EXTRA_OPEN_WIDGET_PICKER_ON_START = "open_widget_picker_on_start"
     }
 
@@ -73,12 +75,34 @@
      * [ActivityController] handles closing the activity in the case it is backgrounded without
      * waiting for an activity result
      */
-    class ActivityController(activity: Activity) {
+    interface ActivityController {
+        /**
+         * Invoked when waiting for an activity result changes, either initiating such wait or
+         * finishing due to the return of a result.
+         */
+        fun onWaitingForResult(waitingForResult: Boolean) {}
+
+        /** Set the visibility of the activity under control. */
+        fun setActivityFullyVisible(fullyVisible: Boolean) {}
+    }
+
+    /**
+     * A nop ActivityController to be use when the communalEditWidgetsActivityFinishFix flag is
+     * false.
+     */
+    class NopActivityController : ActivityController
+
+    /**
+     * A functional ActivityController to be used when the communalEditWidgetsActivityFinishFix flag
+     * is true.
+     */
+    class ActivityControllerImpl(activity: Activity) : ActivityController {
         companion object {
             private const val STATE_EXTRA_IS_WAITING_FOR_RESULT = "extra_is_waiting_for_result"
         }
 
-        private var waitingForResult: Boolean = false
+        private var waitingForResult = false
+        private var activityFullyVisible = false
 
         init {
             activity.registerActivityLifecycleCallbacks(
@@ -105,10 +129,14 @@
                     }
 
                     override fun onActivityStopped(activity: Activity) {
-                        // If we're not backgrounded due to waiting for a resul (either widget
-                        // selection
-                        // or configuration), finish activity.
-                        if (!waitingForResult) {
+                        // If we're not backgrounded due to waiting for a result (either widget
+                        // selection or configuration), and we are fully visible, then finish the
+                        // activity.
+                        if (
+                            !waitingForResult &&
+                                activityFullyVisible &&
+                                !activity.isChangingConfigurations
+                        ) {
                             activity.finish()
                         }
                     }
@@ -124,13 +152,13 @@
             )
         }
 
-        /**
-         * Invoked when waiting for an activity result changes, either initiating such wait or
-         * finishing due to the return of a result.
-         */
-        fun onWaitingForResult(waitingForResult: Boolean) {
+        override fun onWaitingForResult(waitingForResult: Boolean) {
             this.waitingForResult = waitingForResult
         }
+
+        override fun setActivityFullyVisible(fullyVisible: Boolean) {
+            activityFullyVisible = fullyVisible
+        }
     }
 
     private val logger = Logger(logBuffer, "EditWidgetsActivity")
@@ -139,7 +167,9 @@
 
     private var shouldOpenWidgetPickerOnStart = false
 
-    private val activityController: ActivityController = ActivityController(this)
+    private val activityController: ActivityController =
+        if (communalEditWidgetsActivityFinishFix()) ActivityControllerImpl(this)
+        else NopActivityController()
 
     private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
         registerForActivityResult(StartActivityForResult()) { result ->
@@ -156,11 +186,11 @@
                         if (!isPendingWidgetDrag) {
                             val (componentName, user) = getWidgetExtraFromIntent(intent)
                             if (componentName != null && user != null) {
+                                // Add widget at the end.
                                 communalViewModel.onAddWidget(
                                     componentName,
                                     user,
-                                    0,
-                                    widgetConfigurator
+                                    configurator = widgetConfigurator,
                                 )
                             } else {
                                 run { Log.w(TAG, "No AppWidgetProviderInfo found in result.") }
@@ -178,20 +208,19 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
         listenForTransitionAndChangeScene()
 
+        activityController.setActivityFullyVisible(false)
         communalViewModel.setEditModeOpen(true)
 
         val windowInsetsController = window.decorView.windowInsetsController
         windowInsetsController?.hide(WindowInsets.Type.systemBars())
         window.setDecorFitsSystemWindows(false)
 
-        val preselectedKey = intent.getStringExtra(EXTRA_PRESELECTED_KEY)
         shouldOpenWidgetPickerOnStart =
             intent.getBooleanExtra(EXTRA_OPEN_WIDGET_PICKER_ON_START, false)
 
-        communalViewModel.setSelectedKey(preselectedKey)
-
         setContent {
             PlatformTheme {
                 Box(
@@ -204,6 +233,7 @@
                         onOpenWidgetPicker = ::onOpenWidgetPicker,
                         widgetConfigurator = widgetConfigurator,
                         onEditDone = ::onEditDone,
+                        widgetSection = widgetSection,
                     )
                 }
             }
@@ -215,15 +245,19 @@
         lifecycleScope.launch {
             communalViewModel.canShowEditMode.collect {
                 communalViewModel.changeScene(
-                    CommunalScenes.Blank,
-                    CommunalTransitionKeys.ToEditMode,
-                    KeyguardState.GONE,
+                    scene = CommunalScenes.Blank,
+                    loggingReason = "edit mode opening",
+                    transitionKey = CommunalTransitionKeys.ToEditMode,
+                    keyguardState = KeyguardState.GONE,
                 )
                 // wait till transitioned to Blank scene, then animate in communal content in
                 // edit mode
                 communalViewModel.currentScene.first { it == CommunalScenes.Blank }
                 communalViewModel.setEditModeState(EditModeState.SHOWING)
 
+                // Inform the ActivityController that we are now fully visible.
+                activityController.setActivityFullyVisible(true)
+
                 // Show the widget picker, if necessary, after the edit activity has animated in.
                 // Waiting until after the activity has appeared avoids transitions issues.
                 if (shouldOpenWidgetPickerOnStart) {
@@ -236,11 +270,7 @@
 
     private fun onOpenWidgetPicker() {
         lifecycleScope.launch {
-            communalViewModel.onOpenWidgetPicker(
-                resources,
-                packageManager,
-                addWidgetActivityLauncher
-            )
+            communalViewModel.onOpenWidgetPicker(resources, addWidgetActivityLauncher)
         }
     }
 
@@ -249,8 +279,9 @@
             communalViewModel.cleanupEditModeState()
 
             communalViewModel.changeScene(
-                CommunalScenes.Communal,
-                CommunalTransitionKeys.FromEditMode
+                scene = CommunalScenes.Communal,
+                loggingReason = "edit mode closing",
+                transitionKey = CommunalTransitionKeys.FromEditMode
             )
 
             // Wait for the current scene to be idle on communal.
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
index af87f09..63121a8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivityStarter.kt
@@ -19,7 +19,6 @@
 import android.content.Context
 import android.content.Intent
 import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_OPEN_WIDGET_PICKER_ON_START
-import com.android.systemui.communal.widgets.EditWidgetsActivity.Companion.EXTRA_PRESELECTED_KEY
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
@@ -27,7 +26,6 @@
 
 interface EditWidgetsActivityStarter {
     fun startActivity(
-        preselectedKey: String? = null,
         shouldOpenWidgetPickerOnStart: Boolean = false,
     )
 }
@@ -39,12 +37,11 @@
     private val activityStarter: ActivityStarter,
 ) : EditWidgetsActivityStarter {
 
-    override fun startActivity(preselectedKey: String?, shouldOpenWidgetPickerOnStart: Boolean) {
+    override fun startActivity(shouldOpenWidgetPickerOnStart: Boolean) {
         activityStarter.startActivityDismissingKeyguard(
             Intent(applicationContext, EditWidgetsActivity::class.java)
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
                 .apply {
-                    preselectedKey?.let { putExtra(EXTRA_PRESELECTED_KEY, preselectedKey) }
                     putExtra(EXTRA_OPEN_WIDGET_PICKER_ON_START, shouldOpenWidgetPickerOnStart)
                 },
             /* onlyProvisioned = */ true,
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 0eeb506..542b988 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -21,22 +21,35 @@
 import android.content.Intent
 import android.view.View
 import android.widget.RemoteViews
+import com.android.app.tracing.coroutines.launch
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.communalWidgetTrampolineFix
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.domain.interactor.WidgetTrampolineInteractor
 import com.android.systemui.communal.util.InteractionHandlerDelegate
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.UiBackground
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.Logger
 import com.android.systemui.log.dagger.CommunalLog
 import com.android.systemui.plugins.ActivityStarter
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 
 @SysUISingleton
 class WidgetInteractionHandler
 @Inject
 constructor(
+    @Application private val applicationScope: CoroutineScope,
+    @UiBackground private val uiBackgroundContext: CoroutineContext,
     private val activityStarter: ActivityStarter,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     communalSceneInteractor: CommunalSceneInteractor,
+    private val widgetTrampolineInteractor: WidgetTrampolineInteractor,
     @CommunalLog val logBuffer: LogBuffer,
 ) : RemoteViews.InteractionHandler {
 
@@ -48,7 +61,52 @@
         InteractionHandlerDelegate(
             communalSceneInteractor,
             findViewToAnimate = { view -> view is CommunalAppWidgetHostView },
-            intentStarter = this::startIntent,
+            intentStarter =
+                object : InteractionHandlerDelegate.IntentStarter {
+                    private var job: Job? = null
+
+                    override fun startActivity(
+                        intent: PendingIntent,
+                        fillInIntent: Intent,
+                        activityOptions: ActivityOptions,
+                        controller: ActivityTransitionAnimator.Controller?
+                    ): Boolean {
+                        cancelTrampolineMonitoring()
+                        return startActivityIntent(
+                            intent,
+                            fillInIntent,
+                            activityOptions,
+                            controller
+                        )
+                    }
+
+                    override fun startPendingIntent(
+                        view: View,
+                        pendingIntent: PendingIntent,
+                        fillInIntent: Intent,
+                        activityOptions: ActivityOptions
+                    ): Boolean {
+                        cancelTrampolineMonitoring()
+                        if (communalWidgetTrampolineFix()) {
+                            job =
+                                applicationScope.launch("$TAG#monitorForActivityStart") {
+                                    widgetTrampolineInteractor
+                                        .waitForActivityStartAndDismissKeyguard()
+                                }
+                        }
+                        return super.startPendingIntent(
+                            view,
+                            pendingIntent,
+                            fillInIntent,
+                            activityOptions
+                        )
+                    }
+
+                    private fun cancelTrampolineMonitoring() {
+                        job?.cancel()
+                        job = null
+                    }
+                },
             logger = Logger(logBuffer, TAG),
         )
 
@@ -58,7 +116,7 @@
         response: RemoteViews.RemoteResponse
     ): Boolean = delegate.onInteraction(view, pendingIntent, response)
 
-    private fun startIntent(
+    private fun startActivityIntent(
         pendingIntent: PendingIntent,
         fillInIntent: Intent,
         extraOptions: ActivityOptions,
@@ -67,7 +125,14 @@
         activityStarter.startPendingIntentMaybeDismissingKeyguard(
             pendingIntent,
             /* dismissShade = */ false,
-            /* intentSentUiThreadCallback = */ null,
+            {
+                applicationScope.launch("$TAG#awakenFromDream", uiBackgroundContext) {
+                    // This activity could have started while the device is dreaming, in which case
+                    // the dream would occlude the activity. In order to show the newly started
+                    // activity, we wake from the dream.
+                    keyguardUpdateMonitor.awakenFromDream()
+                }
+            },
             controller,
             fillInIntent,
             extraOptions.toBundle(),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 74e1dc0..a5f29aa 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -38,31 +38,35 @@
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.indentIfPossible
 import java.io.PrintWriter
+import java.util.concurrent.CopyOnWriteArraySet
 import java.util.concurrent.Executor
 import java.util.concurrent.atomic.AtomicInteger
 import javax.inject.Inject
 
 private fun createServiceListing(context: Context): ServiceListing {
-    return ServiceListing.Builder(context).apply {
-        setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
-        setPermission("android.permission.BIND_CONTROLS")
-        setNoun("Controls Provider")
-        setSetting("controls_providers")
-        setTag("controls_providers")
-        setAddDeviceLockedFlags(true)
-    }.build()
+    return ServiceListing.Builder(context)
+        .apply {
+            setIntentAction(ControlsProviderService.SERVICE_CONTROLS)
+            setPermission("android.permission.BIND_CONTROLS")
+            setNoun("Controls Provider")
+            setSetting("controls_providers")
+            setTag("controls_providers")
+            setAddDeviceLockedFlags(true)
+        }
+        .build()
 }
 
 /**
  * Provides a listing of components to be used as ControlsServiceProvider.
  *
  * This controller keeps track of components that satisfy:
- *
  * * Has an intent-filter responding to [ControlsProviderService.CONTROLS_ACTION]
  * * Has the bind permission `android.permission.BIND_CONTROLS`
  */
 @SysUISingleton
-class ControlsListingControllerImpl @VisibleForTesting constructor(
+class ControlsListingControllerImpl
+@VisibleForTesting
+constructor(
     private val context: Context,
     @Background private val backgroundExecutor: Executor,
     private val serviceListingBuilder: (Context) -> ServiceListing,
@@ -74,12 +78,12 @@
 
     @Inject
     constructor(
-            context: Context,
-            @Background executor: Executor,
-            userTracker: UserTracker,
-            activityTaskManagerProxy: ActivityTaskManagerProxy,
-            dumpManager: DumpManager,
-            featureFlags: FeatureFlags
+        context: Context,
+        @Background executor: Executor,
+        userTracker: UserTracker,
+        activityTaskManagerProxy: ActivityTaskManagerProxy,
+        dumpManager: DumpManager,
+        featureFlags: FeatureFlags
     ) : this(
         context,
         executor,
@@ -92,7 +96,7 @@
 
     private var serviceListing = serviceListingBuilder(context)
     // All operations in background thread
-    private val callbacks = mutableSetOf<ControlsListingController.ControlsListingCallback>()
+    private val callbacks = CopyOnWriteArraySet<ControlsListingController.ControlsListingCallback>()
 
     companion object {
         private const val TAG = "ControlsListingControllerImpl"
@@ -104,15 +108,17 @@
     override var currentUserId = userTracker.userId
         private set
 
-    private val serviceListingCallback = ServiceListing.Callback { list ->
-        Log.d(TAG, "ServiceConfig reloaded, count: ${list.size}")
-        val newServices = list.map { ControlsServiceInfo(userTracker.userContext, it) }
-        // After here, `list` is not captured, so we don't risk modifying it outside of the callback
-        backgroundExecutor.execute {
-            if (userChangeInProgress.get() > 0) return@execute
-            updateServices(newServices)
+    private val serviceListingCallback =
+        ServiceListing.Callback { list ->
+            Log.d(TAG, "ServiceConfig reloaded, count: ${list.size}")
+            val newServices = list.map { ControlsServiceInfo(userTracker.userContext, it) }
+            // After here, `list` is not captured, so we don't risk modifying it outside of the
+            // callback
+            backgroundExecutor.execute {
+                if (userChangeInProgress.get() > 0) return@execute
+                updateServices(newServices)
+            }
         }
-    }
 
     init {
         Log.d(TAG, "Initializing")
@@ -124,15 +130,12 @@
 
     private fun updateServices(newServices: List<ControlsServiceInfo>) {
         if (activityTaskManagerProxy.supportsMultiWindow(context)) {
-            newServices.forEach {
-                it.resolvePanelActivity() }
+            newServices.forEach { it.resolvePanelActivity() }
         }
 
         if (newServices != availableServices) {
             availableServices = newServices
-            callbacks.forEach {
-                it.onServicesUpdated(getCurrentServices())
-            }
+            callbacks.forEach { it.onServicesUpdated(getCurrentServices()) }
         }
     }
 
@@ -155,8 +158,8 @@
     /**
      * Adds a callback to this controller.
      *
-     * The callback will be notified after it is added as well as any time that the valid
-     * components change.
+     * The callback will be notified after it is added as well as any time that the valid components
+     * change.
      *
      * @param listener a callback to be notified
      */
@@ -188,26 +191,29 @@
     }
 
     /**
-     * @return a list of components that satisfy the requirements to be a
-     *         [ControlsProviderService]
+     * @return a list of components that satisfy the requirements to be a [ControlsProviderService]
      */
     override fun getCurrentServices(): List<ControlsServiceInfo> =
-            availableServices.map(ControlsServiceInfo::copy)
+        availableServices.map(ControlsServiceInfo::copy)
 
     @WorkerThread
     override fun forceReload() {
         val packageManager = context.packageManager
         val intent = Intent(ControlsProviderService.SERVICE_CONTROLS)
         val user = userTracker.userHandle
-        val flags = PackageManager.GET_SERVICES or
+        val flags =
+            PackageManager.GET_SERVICES or
                 PackageManager.GET_META_DATA or
                 PackageManager.MATCH_DIRECT_BOOT_UNAWARE or
                 PackageManager.MATCH_DIRECT_BOOT_AWARE
-        val services = packageManager.queryIntentServicesAsUser(
-                intent,
-                PackageManager.ResolveInfoFlags.of(flags.toLong()),
-                user
-        ).map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) }
+        val services =
+            packageManager
+                .queryIntentServicesAsUser(
+                    intent,
+                    PackageManager.ResolveInfoFlags.of(flags.toLong()),
+                    user
+                )
+                .map { ControlsServiceInfo(userTracker.userContext, it.serviceInfo) }
         updateServices(services)
     }
 
@@ -218,8 +224,7 @@
      * @return a label as returned by [CandidateInfo.loadLabel] or `null`.
      */
     override fun getAppLabel(name: ComponentName): CharSequence? {
-        return availableServices.firstOrNull { it.componentName == name }
-                ?.loadLabel()
+        return availableServices.firstOrNull { it.componentName == name }?.loadLabel()
     }
 
     override fun dump(writer: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 2ea27b7..21a704d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -42,6 +42,7 @@
 import android.app.role.RoleManager;
 import android.app.smartspace.SmartspaceManager;
 import android.app.trust.TrustManager;
+import android.app.usage.UsageStatsManager;
 import android.appwidget.AppWidgetManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothManager;
@@ -544,6 +545,13 @@
         return context.getSystemService(UiModeManager.class);
     }
 
+    /** */
+    @Provides
+    @Singleton
+    static UsageStatsManager provideUsageStatsManager(Context context) {
+        return context.getSystemService(UsageStatsManager.class);
+    }
+
     @Provides
     @Main
     static Resources provideResources(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3273111..79f4568 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -33,6 +33,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
+import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialModule;
 import com.android.systemui.keyboard.shortcut.ShortcutHelperModule;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
 import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
@@ -77,7 +78,7 @@
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
 import com.android.systemui.toast.ToastModule;
-import com.android.systemui.touchpad.tutorial.TouchpadKeyboardTutorialModule;
+import com.android.systemui.touchpad.tutorial.TouchpadTutorialModule;
 import com.android.systemui.unfold.SysUIUnfoldStartableModule;
 import com.android.systemui.unfold.UnfoldTransitionModule;
 import com.android.systemui.util.kotlin.SysUICoroutinesModule;
@@ -122,6 +123,7 @@
         KeyboardShortcutsModule.class,
         KeyguardBlueprintModule.class,
         KeyguardSectionsModule.class,
+        KeyboardTouchpadTutorialModule.class,
         MediaModule.class,
         MediaMuteAwaitConnectionCli.StartableModule.class,
         MultiUserUtilsModule.class,
@@ -142,7 +144,7 @@
         SysUIUnfoldStartableModule.class,
         UnfoldTransitionModule.Startables.class,
         ToastModule.class,
-        TouchpadKeyboardTutorialModule.class,
+        TouchpadTutorialModule.class,
         VolumeModule.class,
         WallpaperModule.class,
         ShortcutHelperModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 1dd3722..3fe6669 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -21,6 +21,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SystemUIAppComponentFactoryBase;
+import com.android.systemui.common.ui.GlobalConfig;
 import com.android.systemui.dagger.qualifiers.PerUser;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -127,6 +128,7 @@
      * Creates a ContextComponentHelper.
      */
     @SysUISingleton
+    @GlobalConfig
     ConfigurationController getConfigurationController();
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index b0f2c18..cbea876 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -258,13 +258,6 @@
 
     @Binds
     @IntoMap
-    @ClassKey(KeyboardTouchpadTutorialCoreStartable::class)
-    abstract fun bindKeyboardTouchpadTutorialCoreStartable(
-        listener: KeyboardTouchpadTutorialCoreStartable
-    ): CoreStartable
-
-    @Binds
-    @IntoMap
     @ClassKey(PhysicalKeyboardCoreStartable::class)
     abstract fun bindKeyboardCoreStartable(listener: PhysicalKeyboardCoreStartable): CoreStartable
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 25b6b14..b55108d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -48,6 +48,8 @@
 import com.android.systemui.classifier.FalsingModule;
 import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule;
 import com.android.systemui.common.data.CommonDataLayerModule;
+import com.android.systemui.common.ui.ConfigurationStateModule;
+import com.android.systemui.common.usagestats.data.CommonUsageStatsDataLayerModule;
 import com.android.systemui.communal.dagger.CommunalModule;
 import com.android.systemui.complication.dagger.ComplicationComponent;
 import com.android.systemui.controls.dagger.ControlsModule;
@@ -65,6 +67,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.FlagDependenciesModule;
 import com.android.systemui.flags.FlagsModule;
+import com.android.systemui.haptics.msdl.dagger.MSDLModule;
 import com.android.systemui.inputmethod.InputMethodModule;
 import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
@@ -205,6 +208,8 @@
         ClockRegistryModule.class,
         CommunalModule.class,
         CommonDataLayerModule.class,
+        ConfigurationStateModule.class,
+        CommonUsageStatsDataLayerModule.class,
         ConfigurationControllerModule.class,
         ConnectivityModule.class,
         ControlsModule.class,
@@ -229,6 +234,7 @@
         MediaProjectionTaskSwitcherModule.class,
         MediaRouterModule.class,
         MotionToolModule.class,
+        MSDLModule.class,
         PeopleHubModule.class,
         PeopleModule.class,
         PluginModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 9460eaf..37c6e17 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -57,7 +57,10 @@
 import com.android.systemui.log.SessionTracker
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.power.domain.interactor.PowerInteractor
+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.scene.shared.model.Scenes.Bouncer
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
@@ -159,6 +162,7 @@
     private val powerInteractor: PowerInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    private val sceneInteractor: dagger.Lazy<SceneInteractor>,
     @FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
     @FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
@@ -385,7 +389,18 @@
                 biometricSettingsRepository.isFaceAuthEnrolledAndEnabled,
                 "isFaceAuthEnrolledAndEnabled"
             ),
-            Pair(keyguardRepository.isKeyguardGoingAway.isFalse(), "keyguardNotGoingAway"),
+            Pair(
+                if (SceneContainerFlag.isEnabled) {
+                    sceneInteractor
+                        .get()
+                        .transitionState
+                        .map { it.isTransitioning(to = Scenes.Gone) || it.isIdle(Scenes.Gone) }
+                        .isFalse()
+                } else {
+                    keyguardRepository.isKeyguardGoingAway.isFalse()
+                },
+                "keyguardNotGoingAway"
+            ),
             Pair(
                 keyguardTransitionInteractor
                     .isInTransitionWhere(toStatePredicate = KeyguardState::deviceIsAsleepInState)
@@ -397,7 +412,11 @@
                     .isFalse()
                     .or(
                         alternateBouncerInteractor.isVisible.or(
-                            keyguardInteractor.primaryBouncerShowing
+                            if (SceneContainerFlag.isEnabled) {
+                                sceneInteractor.get().transitionState.map { it.isIdle(Bouncer) }
+                            } else {
+                                keyguardInteractor.primaryBouncerShowing
+                            }
                         )
                     ),
                 "secureCameraNotActiveOrAnyBouncerIsShowing"
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
index 79b176c..7aee12f 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricsAllowedInteractor.kt
@@ -22,6 +22,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
@@ -44,18 +45,30 @@
     biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
     facePropertyRepository: FacePropertyRepository,
 ) {
+    /**
+     * Whether face is locked out due to too many failed face attempts. This currently includes
+     * whether face is not allowed based on other biometric lockouts; however does not include if
+     * face isn't allowed due to other strong or primary authentication requirements.
+     */
+    val isFaceLockedOut: StateFlow<Boolean> = deviceEntryFaceAuthInteractor.isLockedOut
 
     private val isStrongFaceAuth: Flow<Boolean> =
         facePropertyRepository.sensorInfo.map { it?.strength == SensorStrength.STRONG }
 
     private val isStrongFaceAuthLockedOut: Flow<Boolean> =
-        combine(isStrongFaceAuth, deviceEntryFaceAuthInteractor.isLockedOut) {
-            isStrongFaceAuth,
-            isFaceAuthLockedOut ->
+        combine(isStrongFaceAuth, isFaceLockedOut) { isStrongFaceAuth, isFaceAuthLockedOut ->
             isStrongFaceAuth && isFaceAuthLockedOut
         }
 
     /**
+     * Whether fingerprint is locked out due to too many failed fingerprint attempts. This does NOT
+     * include whether fingerprint is not allowed based on other biometric lockouts nor if
+     * fingerprint isn't allowed due to other strong or primary authentication requirements.
+     */
+    val isFingerprintLockedOut: StateFlow<Boolean> =
+        deviceEntryFingerprintAuthInteractor.isLockedOut
+
+    /**
      * Whether fingerprint authentication is currently allowed for the user. This is true if the
      * user has fingerprint auth enabled, enrolled, it is not disabled by any security timeouts by
      * [com.android.systemui.keyguard.shared.model.AuthenticationFlags], not locked out due to too
@@ -64,7 +77,7 @@
      */
     val isFingerprintAuthCurrentlyAllowed: Flow<Boolean> =
         combine(
-            deviceEntryFingerprintAuthInteractor.isLockedOut,
+            isFingerprintLockedOut,
             biometricSettingsInteractor.fingerprintAuthCurrentlyAllowed,
             isStrongFaceAuthLockedOut,
         ) { fpLockedOut, fpAllowedBySettings, strongAuthFaceAuthLockedOut ->
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index dff391a..cdd2b05 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -60,14 +60,23 @@
     fun unregisterListener(listener: FaceAuthenticationListener)
 
     fun onUdfpsSensorTouched()
+
     fun onAssistantTriggeredOnLockScreen()
+
     fun onDeviceLifted()
-    fun onQsExpansionStared()
+
+    fun onShadeExpansionStarted()
+
     fun onNotificationPanelClicked()
+
     fun onSwipeUpOnBouncer()
+
     fun onPrimaryBouncerUserInput()
+
     fun onAccessibilityAction()
+
     fun onWalletLaunched()
+
     fun onDeviceUnfolded()
 
     /** Whether face auth is considered class 3 */
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index 969f53f..5c058fe 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -54,7 +54,7 @@
     val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
         repository.authenticationStatus
 
-    val isLockedOut: Flow<Boolean> = repository.isLockedOut
+    val isLockedOut: StateFlow<Boolean> = repository.isLockedOut
 
     val fingerprintFailure: Flow<FailFingerprintAuthenticationStatus> =
         repository.authenticationStatus.filterIsInstance<FailFingerprintAuthenticationStatus>()
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 9b95ac4..7018f9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -16,12 +16,14 @@
 
 package com.android.systemui.deviceentry.domain.interactor
 
+import com.android.internal.policy.IKeyguardDismissCallback
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
+import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.utils.coroutines.flow.mapLatestConflated
@@ -56,6 +58,7 @@
     private val sceneInteractor: SceneInteractor,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    private val dismissCallbackRegistry: DismissCallbackRegistry,
 ) {
     /**
      * Whether the device is unlocked.
@@ -119,7 +122,7 @@
      * Note: `true` doesn't mean the lockscreen is visible. It may be occluded or covered by other
      * UI.
      */
-    val canSwipeToEnter: StateFlow<Boolean?> =
+    val canSwipeToEnter: StateFlow<Boolean?> by lazy {
         combine(
                 authenticationInteractor.authenticationMethod.map {
                     it == AuthenticationMethodModel.None
@@ -142,12 +145,21 @@
                 // from upstream data sources.
                 initialValue = null,
             )
+    }
 
     /**
      * Attempt to enter the device and dismiss the lockscreen. If authentication is required to
      * unlock the device it will transition to bouncer.
+     *
+     * @param callback An optional callback to invoke when the attempt succeeds, fails, or is
+     *   canceled
      */
-    fun attemptDeviceEntry() {
+    @JvmOverloads
+    fun attemptDeviceEntry(
+        callback: IKeyguardDismissCallback? = null,
+    ) {
+        callback?.let { dismissCallbackRegistry.addCallback(it) }
+
         // TODO (b/307768356),
         //       1. Check if the device is already authenticated by trust agent/passive biometrics
         //       2. Show SPFS/UDFPS bouncer if it is available AlternateBouncerInteractor.show
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
index de5d0aa..9b8c2b1 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -47,6 +47,7 @@
     override fun isFaceAuthEnabledAndEnrolled(): Boolean = false
 
     override fun isFaceAuthStrong(): Boolean = false
+
     override fun start() = Unit
 
     override fun registerListener(listener: FaceAuthenticationListener) {}
@@ -59,13 +60,17 @@
 
     override fun onDeviceLifted() {}
 
-    override fun onQsExpansionStared() {}
+    override fun onShadeExpansionStarted() {}
 
     override fun onNotificationPanelClicked() {}
 
     override fun onSwipeUpOnBouncer() {}
+
     override fun onPrimaryBouncerUserInput() {}
+
     override fun onAccessibilityAction() {}
+
     override fun onWalletLaunched() = Unit
+
     override fun onDeviceUnfolded() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index c536d6b..3b5d5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -46,6 +46,9 @@
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.power.domain.interactor.PowerInteractor
 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 com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
@@ -57,6 +60,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
@@ -90,6 +94,7 @@
     private val powerInteractor: PowerInteractor,
     private val biometricSettingsRepository: BiometricSettingsRepository,
     private val trustManager: TrustManager,
+    private val sceneInteractor: Lazy<SceneInteractor>,
     deviceEntryFaceAuthStatusInteractor: DeviceEntryFaceAuthStatusInteractor,
 ) : DeviceEntryFaceAuthInteractor {
 
@@ -103,9 +108,7 @@
         keyguardUpdateMonitor.setFaceAuthInteractor(this)
         observeFaceAuthStateUpdates()
         faceAuthenticationLogger.interactorStarted()
-        primaryBouncerInteractor
-            .get()
-            .isShowing
+        isBouncerVisible
             .whenItFlipsToTrue()
             .onEach {
                 faceAuthenticationLogger.bouncerVisibilityChanged()
@@ -181,19 +184,23 @@
         // auth so that the switched user can unlock the device with face auth.
         userRepository.selectedUser
             .pairwise()
-            .onEach { (previous, curr) ->
+            .filter { (previous, curr) ->
                 val wasSwitching = previous.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
                 val isSwitching = curr.selectionStatus == SelectionStatus.SELECTION_IN_PROGRESS
-                if (wasSwitching && !isSwitching) {
-                    resetLockedOutState(curr.userInfo.id)
-                    yield()
-                    runFaceAuth(
-                        FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
-                        // Fallback to detection if bouncer is not showing so that we can detect a
-                        // face and then show the bouncer to the user if face auth can't run
-                        fallbackToDetect = !primaryBouncerInteractor.get().isBouncerShowing()
-                    )
-                }
+                // User switching was in progress and is complete now.
+                wasSwitching && !isSwitching
+            }
+            .map { (_, curr) -> curr.userInfo.id }
+            .sample(isBouncerVisible, ::Pair)
+            .onEach { (userId, isBouncerCurrentlyVisible) ->
+                resetLockedOutState(userId)
+                yield()
+                runFaceAuth(
+                    FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING,
+                    // Fallback to detection if bouncer is not showing so that we can detect a
+                    // face and then show the bouncer to the user if face auth can't run
+                    fallbackToDetect = !isBouncerCurrentlyVisible
+                )
             }
             .launchIn(applicationScope)
 
@@ -208,6 +215,24 @@
                 }
             }
             .launchIn(applicationScope)
+
+        if (SceneContainerFlag.isEnabled) {
+            sceneInteractor
+                .get()
+                .transitionState
+                .filter { it.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade) }
+                .distinctUntilChanged()
+                .onEach { onShadeExpansionStarted() }
+                .launchIn(applicationScope)
+        }
+    }
+
+    private val isBouncerVisible: Flow<Boolean> by lazy {
+        if (SceneContainerFlag.isEnabled) {
+            sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) }
+        } else {
+            primaryBouncerInteractor.get().isShowing
+        }
     }
 
     private suspend fun resetLockedOutState(currentUserId: Int) {
@@ -225,8 +250,8 @@
         runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, true)
     }
 
-    override fun onQsExpansionStared() {
-        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true)
+    override fun onShadeExpansionStarted() {
+        runFaceAuth(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false)
     }
 
     override fun onDeviceLifted() {
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 40e2f17..1f5878b 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
@@ -48,6 +48,7 @@
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.scan
 import kotlinx.coroutines.flow.shareIn
@@ -184,6 +185,9 @@
         if (Flags.enableEfficientDisplayRepository()) {
             enabledDisplayIds
                 .mapElementsLazily { displayId -> getDisplay(displayId) }
+                .onEach {
+                    if (it.isEmpty()) Log.wtf(TAG, "No enabled displays. This should never happen.")
+                }
                 .flowOn(backgroundCoroutineDispatcher)
                 .debugLog("enabledDisplays")
                 .stateIn(
@@ -194,7 +198,8 @@
                     // performance concerns.
                     // Ultimately, this is a trade-off between a one-time UI thread binder call and
                     // the constant overhead of sharedFlows.
-                    initialValue = getDisplays())
+                    initialValue = getDisplays()
+                )
         } else {
             oldEnabledDisplays
         }
@@ -380,9 +385,8 @@
             val resultSet: Set<V>
         )
 
-        val initialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
-
-        return this.scan(initialState) { state, currentSet ->
+        val emptyInitialState = State(emptySet<T>(), emptyMap(), emptySet<V>())
+        return this.scan(emptyInitialState) { state, currentSet ->
                 if (currentSet == state.previousSet) {
                     state
                 } else {
@@ -397,6 +401,7 @@
                     State(currentSet, newMap, resultSet)
                 }
             }
+            .filter { it != emptyInitialState }
             .map { it.resultSet }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index e4b290d..15a3cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.display.domain.interactor
 
 import android.companion.virtual.VirtualDeviceManager
-import android.companion.virtual.flags.Flags
 import android.view.Display
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -155,8 +154,7 @@
     }
 
     private fun isVirtualDeviceOwnedMirrorDisplay(display: Display): Boolean {
-        return Flags.interactiveScreenMirror() &&
-            virtualDeviceManager != null &&
+        return virtualDeviceManager != null &&
             virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(display.displayId)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
index 0b33614..6fd4d33 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeBrightnessHostForwarder.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.doze;
 
-import java.util.concurrent.Executor;
-
 /**
  * Forwards the currently used brightness to {@link DozeHost}.
  */
@@ -25,9 +23,8 @@
 
     private final DozeHost mHost;
 
-    public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host,
-            Executor bgExecutor) {
-        super(wrappedService, bgExecutor);
+    public DozeBrightnessHostForwarder(DozeMachine.Service wrappedService, DozeHost host) {
+        super(wrappedService);
         mHost = host;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 5bfcc97..cdcb03e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -253,9 +253,11 @@
     /**
      * Appends display state changed event to the logs
      * @param displayState new DozeMachine state
+     * @param afterRequest whether the request has successfully been sent else false for it's
+     *                        about to be requested
      */
-    public void traceDisplayState(int displayState) {
-        mLogger.logDisplayStateChanged(displayState);
+    public void traceDisplayState(int displayState, boolean afterRequest) {
+        mLogger.logDisplayStateChanged(displayState, afterRequest);
     }
 
     /**
@@ -402,18 +404,22 @@
     /**
      * Appends new AOD screen brightness to logs
      * @param brightness display brightness setting between 1 and 255
+     * @param afterRequest whether the request has successfully been sent else false for it's
+     *                        about to be requested
      */
-    public void traceDozeScreenBrightness(int brightness) {
-        mLogger.logDozeScreenBrightness(brightness);
+    public void traceDozeScreenBrightness(int brightness, boolean afterRequest) {
+        mLogger.logDozeScreenBrightness(brightness, afterRequest);
     }
 
     /**
      * Appends new AOD screen brightness to logs
      * @param brightness display brightness setting between {@link PowerManager#BRIGHTNESS_MIN} and
      *                   {@link PowerManager#BRIGHTNESS_MAX}
+     * @param afterRequest whether the request has successfully been sent else false for it's
+     *                        about to be requested
      */
-    public void traceDozeScreenBrightnessFloat(float brightness) {
-        mLogger.logDozeScreenBrightnessFloat(brightness);
+    public void traceDozeScreenBrightnessFloat(float brightness, boolean afterRequest) {
+        mLogger.logDozeScreenBrightnessFloat(brightness, afterRequest);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index a31dbec..7128731 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -32,23 +32,18 @@
 import javax.inject.Inject
 
 /** Interface for logging messages to the [DozeLog]. */
-class DozeLogger @Inject constructor(
-    @DozeLog private val buffer: LogBuffer
-) {
+class DozeLogger @Inject constructor(@DozeLog private val buffer: LogBuffer) {
     fun logPickupWakeup(isWithinVibrationThreshold: Boolean) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = isWithinVibrationThreshold
-        }, {
-            "PickupWakeup withinVibrationThreshold=$bool1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = isWithinVibrationThreshold },
+            { "PickupWakeup withinVibrationThreshold=$bool1" }
+        )
     }
 
     fun logPulseStart(@Reason reason: Int) {
-        buffer.log(TAG, INFO, {
-            int1 = reason
-        }, {
-            "Pulse start, reason=${reasonToString(int1)}"
-        })
+        buffer.log(TAG, INFO, { int1 = reason }, { "Pulse start, reason=${reasonToString(int1)}" })
     }
 
     fun logPulseFinish() {
@@ -60,52 +55,51 @@
     }
 
     fun logDozing(isDozing: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isDozing
-        }, {
-            "Dozing=$bool1"
-        })
+        buffer.log(TAG, INFO, { bool1 = isDozing }, { "Dozing=$bool1" })
     }
 
     fun logDozingChanged(isDozing: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isDozing
-        }, {
-            "Dozing changed dozing=$bool1"
-        })
+        buffer.log(TAG, INFO, { bool1 = isDozing }, { "Dozing changed dozing=$bool1" })
     }
 
     fun logPowerSaveChanged(powerSaveActive: Boolean, nextState: DozeMachine.State) {
-        buffer.log(TAG, INFO, {
-            bool1 = powerSaveActive
-            str1 = nextState.name
-        }, {
-            "Power save active=$bool1 nextState=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                bool1 = powerSaveActive
+                str1 = nextState.name
+            },
+            { "Power save active=$bool1 nextState=$str1" }
+        )
     }
 
     fun logAlwaysOnSuppressedChange(isAodSuppressed: Boolean, nextState: DozeMachine.State) {
-        buffer.log(TAG, INFO, {
-            bool1 = isAodSuppressed
-            str1 = nextState.name
-        }, {
-            "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                bool1 = isAodSuppressed
+                str1 = nextState.name
+            },
+            { "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1" }
+        )
     }
 
-    fun logFling(
-        expand: Boolean,
-        aboveThreshold: Boolean,
-        screenOnFromTouch: Boolean
-    ) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = expand
-            bool2 = aboveThreshold
-            bool4 = screenOnFromTouch
-        }, {
-            "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
-                "screenOnFromTouch=$bool4"
-        })
+    fun logFling(expand: Boolean, aboveThreshold: Boolean, screenOnFromTouch: Boolean) {
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = expand
+                bool2 = aboveThreshold
+                bool4 = screenOnFromTouch
+            },
+            {
+                "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " +
+                    "screenOnFromTouch=$bool4"
+            }
+        )
     }
 
     fun logEmergencyCall() {
@@ -113,280 +107,314 @@
     }
 
     fun logKeyguardBouncerChanged(isShowing: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isShowing
-        }, {
-            "Keyguard bouncer changed, showing=$bool1"
-        })
+        buffer.log(TAG, INFO, { bool1 = isShowing }, { "Keyguard bouncer changed, showing=$bool1" })
     }
 
     fun logScreenOn(isPulsing: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isPulsing
-        }, {
-            "Screen on, pulsing=$bool1"
-        })
+        buffer.log(TAG, INFO, { bool1 = isPulsing }, { "Screen on, pulsing=$bool1" })
     }
 
     fun logScreenOff(why: Int) {
-        buffer.log(TAG, INFO, {
-            int1 = why
-        }, {
-            "Screen off, why=$int1"
-        })
+        buffer.log(TAG, INFO, { int1 = why }, { "Screen off, why=$int1" })
     }
 
     fun logMissedTick(delay: String) {
-        buffer.log(TAG, ERROR, {
-            str1 = delay
-        }, {
-            "Missed AOD time tick by $str1"
-        })
+        buffer.log(TAG, ERROR, { str1 = delay }, { "Missed AOD time tick by $str1" })
     }
 
     fun logTimeTickScheduled(whenAt: Long, triggerAt: Long) {
-        buffer.log(TAG, DEBUG, {
-            long1 = whenAt
-            long2 = triggerAt
-        }, {
-            "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
-                "triggerAt=${DATE_FORMAT.format(Date(long2))}"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                long1 = whenAt
+                long2 = triggerAt
+            },
+            {
+                "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " +
+                    "triggerAt=${DATE_FORMAT.format(Date(long2))}"
+            }
+        )
     }
 
     fun logKeyguardVisibilityChange(isVisible: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isVisible
-        }, {
-            "Keyguard visibility change, isVisible=$bool1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { bool1 = isVisible },
+            { "Keyguard visibility change, isVisible=$bool1" }
+        )
     }
 
     fun logPendingUnscheduleTimeTick(isPending: Boolean, isTimeTickScheduled: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = isPending
-            bool2 = isTimeTickScheduled
-        }, {
-            "Pending unschedule time tick, isPending=$bool1, isTimeTickScheduled:$bool2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                bool1 = isPending
+                bool2 = isTimeTickScheduled
+            },
+            { "Pending unschedule time tick, isPending=$bool1, isTimeTickScheduled:$bool2" }
+        )
     }
 
     fun logDozeStateChanged(state: DozeMachine.State) {
-        buffer.log(TAG, INFO, {
-            str1 = state.name
-        }, {
-            "Doze state changed to $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = state.name }, { "Doze state changed to $str1" })
     }
 
     fun logStateChangedSent(state: DozeMachine.State) {
-        buffer.log(TAG, INFO, {
-            str1 = state.name
-        }, {
-            "Doze state sent to all DozeMachineParts stateSent=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = state.name },
+            { "Doze state sent to all DozeMachineParts stateSent=$str1" }
+        )
     }
 
     fun logDisplayStateDelayedByUdfps(delayedDisplayState: Int) {
-        buffer.log(TAG, INFO, {
-            str1 = Display.stateToString(delayedDisplayState)
-        }, {
-            "Delaying display state change to: $str1 due to UDFPS activity"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = Display.stateToString(delayedDisplayState) },
+            { "Delaying display state change to: $str1 due to UDFPS activity" }
+        )
     }
 
-    fun logDisplayStateChanged(displayState: Int) {
-        buffer.log(TAG, INFO, {
-            str1 = Display.stateToString(displayState)
-        }, {
-            "Display state changed to $str1"
-        })
+    fun logDisplayStateChanged(displayState: Int, afterRequest: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = Display.stateToString(displayState)
+                bool1 = afterRequest
+            },
+            { "Display state ${if (bool1) "changed" else "requested"} to $str1" }
+        )
     }
 
     fun logWakeDisplay(isAwake: Boolean, @Reason reason: Int) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = isAwake
-            int1 = reason
-        }, {
-            "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isAwake
+                int1 = reason
+            },
+            { "Display wakefulness changed, isAwake=$bool1, reason=${reasonToString(int1)}" }
+        )
     }
 
     fun logProximityResult(isNear: Boolean, millis: Long, @Reason reason: Int) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = isNear
-            long1 = millis
-            int1 = reason
-        }, {
-            "Proximity result reason=${reasonToString(int1)} near=$bool1 millis=$long1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isNear
+                long1 = millis
+                int1 = reason
+            },
+            { "Proximity result reason=${reasonToString(int1)} near=$bool1 millis=$long1" }
+        )
     }
 
     fun logPostureChanged(posture: Int, partUpdated: String) {
-        buffer.log(TAG, INFO, {
-            int1 = posture
-            str1 = partUpdated
-        }, {
-            "Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" +
-                " partUpdated=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                int1 = posture
+                str1 = partUpdated
+            },
+            {
+                "Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" +
+                    " partUpdated=$str1"
+            }
+        )
     }
 
     /**
-     * Log why a pulse was dropped and the current doze machine state. The state can be null
-     * if the DozeMachine is the middle of transitioning between states.
+     * Log why a pulse was dropped and the current doze machine state. The state can be null if the
+     * DozeMachine is the middle of transitioning between states.
      */
     fun logPulseDropped(from: String, state: DozeMachine.State?) {
-        buffer.log(TAG, INFO, {
-            str1 = from
-            str2 = state?.name
-        }, {
-            "Pulse dropped, cannot pulse from=$str1 state=$str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = from
+                str2 = state?.name
+            },
+            { "Pulse dropped, cannot pulse from=$str1 state=$str2" }
+        )
     }
 
     fun logSensorEventDropped(sensorEvent: Int, reason: String) {
-        buffer.log(TAG, INFO, {
-            int1 = sensorEvent
-            str1 = reason
-        }, {
-            "SensorEvent [$int1] dropped, reason=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                int1 = sensorEvent
+                str1 = reason
+            },
+            { "SensorEvent [$int1] dropped, reason=$str1" }
+        )
     }
 
     fun logPulseEvent(pulseEvent: String, dozing: Boolean, pulseReason: String) {
-        buffer.log(TAG, DEBUG, {
-            str1 = pulseEvent
-            bool1 = dozing
-            str2 = pulseReason
-        }, {
-            "Pulse-$str1 dozing=$bool1 pulseReason=$str2"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            {
+                str1 = pulseEvent
+                bool1 = dozing
+                str2 = pulseReason
+            },
+            { "Pulse-$str1 dozing=$bool1 pulseReason=$str2" }
+        )
     }
 
     fun logPulseDropped(reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = reason
-        }, {
-            "Pulse dropped, why=$str1"
-        })
+        buffer.log(TAG, INFO, { str1 = reason }, { "Pulse dropped, why=$str1" })
     }
 
     fun logPulseTouchDisabledByProx(disabled: Boolean) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = disabled
-        }, {
-            "Pulse touch modified by prox, disabled=$bool1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = disabled },
+            { "Pulse touch modified by prox, disabled=$bool1" }
+        )
     }
 
     fun logSensorTriggered(@Reason reason: Int) {
-        buffer.log(TAG, DEBUG, {
-            int1 = reason
-        }, {
-            "Sensor triggered, type=${reasonToString(int1)}"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { int1 = reason },
+            { "Sensor triggered, type=${reasonToString(int1)}" }
+        )
     }
 
     fun logAlwaysOnSuppressed(state: DozeMachine.State, reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = state.name
-            str2 = reason
-        }, {
-            "Always-on state suppressed, suppressed state=$str1 reason=$str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = state.name
+                str2 = reason
+            },
+            { "Always-on state suppressed, suppressed state=$str1 reason=$str2" }
+        )
     }
 
     fun logImmediatelyEndDoze(reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = reason
-        }, {
-            "Doze immediately ended due to $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = reason }, { "Doze immediately ended due to $str1" })
     }
 
-    fun logDozeScreenBrightness(brightness: Int) {
-        buffer.log(TAG, INFO, {
-            int1 = brightness
-        }, {
-            "Doze screen brightness set (int), brightness=$int1"
-        })
+    fun logDozeScreenBrightness(brightness: Int, afterRequest: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                int1 = brightness
+                bool1 = afterRequest
+            },
+            {
+                "Doze screen brightness ${if (bool1) "set" else "requested"}" +
+                    " (int), brightness=$int1"
+            }
+        )
     }
 
-    fun logDozeScreenBrightnessFloat(brightness: Float) {
-        buffer.log(TAG, INFO, {
-            double1 = brightness.toDouble()
-        }, {
-            "Doze screen brightness set (float), brightness=$double1"
-        })
+    fun logDozeScreenBrightnessFloat(brightness: Float, afterRequest: Boolean) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                double1 = brightness.toDouble()
+                bool1 = afterRequest
+            },
+            {
+                "Doze screen brightness ${if (bool1) "set" else "requested"}" +
+                    " (float), brightness=$double1"
+            }
+        )
     }
 
     fun logSetAodDimmingScrim(scrimOpacity: Long) {
-        buffer.log(TAG, INFO, {
-            long1 = scrimOpacity
-        }, {
-            "Doze aod dimming scrim opacity set, opacity=$long1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { long1 = scrimOpacity },
+            { "Doze aod dimming scrim opacity set, opacity=$long1" }
+        )
     }
 
     fun logCarModeEnded() {
-        buffer.log(TAG, INFO, {}, {
-            "Doze car mode ended"
-        })
+        buffer.log(TAG, INFO, {}, { "Doze car mode ended" })
     }
 
     fun logCarModeStarted() {
-        buffer.log(TAG, INFO, {}, {
-            "Doze car mode started"
-        })
+        buffer.log(TAG, INFO, {}, { "Doze car mode started" })
     }
 
     fun logSensorRegisterAttempt(sensorInfo: String, successfulRegistration: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = sensorInfo
-            bool1 = successfulRegistration
-        }, {
-            "Register sensor. Success=$bool1 sensor=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = sensorInfo
+                bool1 = successfulRegistration
+            },
+            { "Register sensor. Success=$bool1 sensor=$str1" }
+        )
     }
 
     fun logSensorUnregisterAttempt(sensorInfo: String, successfulUnregister: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = sensorInfo
-            bool1 = successfulUnregister
-        }, {
-            "Unregister sensor. Success=$bool1 sensor=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = sensorInfo
+                bool1 = successfulUnregister
+            },
+            { "Unregister sensor. Success=$bool1 sensor=$str1" }
+        )
     }
 
     fun logSensorUnregisterAttempt(
-            sensorInfo: String,
-            successfulUnregister: Boolean,
-            reason: String
+        sensorInfo: String,
+        successfulUnregister: Boolean,
+        reason: String
     ) {
-        buffer.log(TAG, INFO, {
-            str1 = sensorInfo
-            bool1 = successfulUnregister
-            str2 = reason
-        }, {
-            "Unregister sensor. reason=$str2. Success=$bool1 sensor=$str1"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = sensorInfo
+                bool1 = successfulUnregister
+                str2 = reason
+            },
+            { "Unregister sensor. reason=$str2. Success=$bool1 sensor=$str1" }
+        )
     }
 
     fun logSkipSensorRegistration(sensor: String) {
-        buffer.log(TAG, DEBUG, {
-            str1 = sensor
-        }, {
-            "Skipping sensor registration because its already registered. sensor=$str1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { str1 = sensor },
+            { "Skipping sensor registration because its already registered. sensor=$str1" }
+        )
     }
 
     fun logSetIgnoreTouchWhilePulsing(ignoreTouchWhilePulsing: Boolean) {
-        buffer.log(TAG, DEBUG, {
-            bool1 = ignoreTouchWhilePulsing
-        }, {
-            "Prox changed while pulsing. setIgnoreTouchWhilePulsing=$bool1"
-        })
+        buffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = ignoreTouchWhilePulsing },
+            { "Prox changed while pulsing. setIgnoreTouchWhilePulsing=$bool1" }
+        )
     }
 
     fun log(@CompileTimeConstant msg: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 8198ef4..e02e3fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -39,7 +39,6 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -232,7 +231,6 @@
     }
 
     void onScreenState(int state) {
-        mDozeLog.traceDisplayState(state);
         for (Part part : mParts) {
             part.onScreenState(state);
         }
@@ -516,11 +514,9 @@
 
         class Delegate implements Service {
             private final Service mDelegate;
-            private final Executor mBgExecutor;
 
-            public Delegate(Service delegate, Executor bgExecutor) {
+            public Delegate(Service delegate) {
                 mDelegate = delegate;
-                mBgExecutor = bgExecutor;
             }
 
             @Override
@@ -540,16 +536,12 @@
 
             @Override
             public void setDozeScreenBrightness(int brightness) {
-                mBgExecutor.execute(() -> {
-                    mDelegate.setDozeScreenBrightness(brightness);
-                });
+                mDelegate.setDozeScreenBrightness(brightness);
             }
 
             @Override
             public void setDozeScreenBrightnessFloat(float brightness) {
-                mBgExecutor.execute(() -> {
-                    mDelegate.setDozeScreenBrightnessFloat(brightness);
-                });
+                mDelegate.setDozeScreenBrightnessFloat(brightness);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
index 8d44472..25c2c39 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenStatePreventingAdapter.java
@@ -22,16 +22,14 @@
 
 import com.android.systemui.statusbar.phone.DozeParameters;
 
-import java.util.concurrent.Executor;
-
 /**
  * Prevents usage of doze screen states on devices that don't support them.
  */
 public class DozeScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
 
     @VisibleForTesting
-    DozeScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
-        super(inner, bgExecutor);
+    DozeScreenStatePreventingAdapter(DozeMachine.Service inner) {
+        super(inner);
     }
 
     @Override
@@ -49,8 +47,8 @@
      * return a new instance of {@link DozeScreenStatePreventingAdapter} wrapping {@code inner}.
      */
     public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
-            DozeParameters params, Executor bgExecutor) {
-        return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner, bgExecutor) : inner;
+            DozeParameters params) {
+        return isNeeded(params) ? new DozeScreenStatePreventingAdapter(inner) : inner;
     }
 
     private static boolean isNeeded(DozeParameters params) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index e07b5c2..21922ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -256,7 +256,7 @@
                         Settings.Secure.DOZE_WAKE_DISPLAY_GESTURE,
                         mConfig.wakeScreenGestureAvailable()
                           && mConfig.alwaysOnEnabled(
-                                  mSelectedUserInteractor.getSelectedUserId(true)),
+                                  mSelectedUserInteractor.getSelectedUserId()),
                         DozeLog.REASON_SENSOR_WAKE_UP_PRESENCE,
                         false /* reports touch coordinates */,
                         false /* touchscreen */
@@ -297,7 +297,7 @@
 
     private boolean udfpsLongPressConfigured() {
         return mUdfpsEnrolled
-                && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId(true))
+                && (mConfig.alwaysOnEnabled(mSelectedUserInteractor.getSelectedUserId())
                 || mScreenOffUdfpsEnabled);
     }
 
@@ -477,7 +477,7 @@
     private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange, Collection<Uri> uris, int flags, int userId) {
-            if (userId != mSelectedUserInteractor.getSelectedUserId(true)) {
+            if (userId != mSelectedUserInteractor.getSelectedUserId()) {
                 return;
             }
             for (TriggerSensor s : mTriggerSensors) {
@@ -703,13 +703,13 @@
         }
 
         protected boolean enabledBySetting() {
-            if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId(true))) {
+            if (!mConfig.enabled(mSelectedUserInteractor.getSelectedUserId())) {
                 return false;
             } else if (TextUtils.isEmpty(mSetting)) {
                 return true;
             }
             return mSecureSettings.getIntForUser(mSetting, mSettingDefault ? 1 : 0,
-                    mSelectedUserInteractor.getSelectedUserId(true)) != 0;
+                    mSelectedUserInteractor.getSelectedUserId()) != 0;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index ba38ab0..2e372ff 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -23,6 +23,7 @@
 import android.service.dreams.DreamService;
 import android.util.Log;
 
+import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.plugins.DozeServicePlugin;
 import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
@@ -31,6 +32,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -43,9 +45,14 @@
     private DozeMachine mDozeMachine;
     private DozeServicePlugin mDozePlugin;
     private PluginManager mPluginManager;
+    private DozeLog mDozeLog;
+    private Executor mBgExecutor;
 
     @Inject
-    public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager) {
+    public DozeService(DozeComponent.Builder dozeComponentBuilder, PluginManager pluginManager,
+            DozeLog dozeLog, @UiBackground Executor bgExecutor) {
+        mDozeLog = dozeLog;
+        mBgExecutor = bgExecutor;
         mDozeComponentBuilder = dozeComponentBuilder;
         setDebug(DEBUG);
         mPluginManager = pluginManager;
@@ -143,9 +150,29 @@
 
     @Override
     public void setDozeScreenState(int state) {
+        mDozeLog.traceDisplayState(state, /* afterRequest */ false);
         super.setDozeScreenState(state);
+        mDozeLog.traceDisplayState(state, /* afterRequest */ true);
         if (mDozeMachine != null) {
             mDozeMachine.onScreenState(state);
         }
     }
+
+    @Override
+    public void setDozeScreenBrightness(int brightness) {
+        mBgExecutor.execute(() -> {
+            mDozeLog.traceDozeScreenBrightness(brightness, /* afterRequest */ false);
+            super.setDozeScreenBrightness(brightness);
+            mDozeLog.traceDozeScreenBrightness(brightness, /* afterRequest */ true);
+        });
+    }
+
+    @Override
+    public void setDozeScreenBrightnessFloat(float brightness) {
+        mBgExecutor.execute(() -> {
+            mDozeLog.traceDozeScreenBrightnessFloat(brightness, /* afterRequest */ false);
+            super.setDozeScreenBrightnessFloat(brightness);
+            mDozeLog.traceDozeScreenBrightnessFloat(brightness, /* afterRequest */ true);
+        });
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
index f7773f1..cfc952d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapter.java
@@ -22,16 +22,14 @@
 
 import com.android.systemui.statusbar.phone.DozeParameters;
 
-import java.util.concurrent.Executor;
-
 /**
  * Prevents usage of doze screen states on devices that don't support them.
  */
 public class DozeSuspendScreenStatePreventingAdapter extends DozeMachine.Service.Delegate {
 
     @VisibleForTesting
-    DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner, Executor bgExecutor) {
-        super(inner, bgExecutor);
+    DozeSuspendScreenStatePreventingAdapter(DozeMachine.Service inner) {
+        super(inner);
     }
 
     @Override
@@ -47,8 +45,8 @@
      * return a new instance of {@link DozeSuspendScreenStatePreventingAdapter} wrapping {@code inner}.
      */
     public static DozeMachine.Service wrapIfNeeded(DozeMachine.Service inner,
-            DozeParameters params, Executor bgExecutor) {
-        return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner, bgExecutor)
+            DozeParameters params) {
+        return isNeeded(params) ? new DozeSuspendScreenStatePreventingAdapter(inner)
                 : inner;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 4a9f741..dd08d32 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -251,7 +251,7 @@
             return;
         }
         mNotificationPulseTime = SystemClock.elapsedRealtime();
-        if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId(true))) {
+        if (!mConfig.pulseOnNotificationEnabled(mSelectedUserInteractor.getSelectedUserId())) {
             runIfNotNull(onPulseSuppressedListener);
             mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 8c3de4b..f383a04 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.hardware.Sensor;
 
-import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.doze.DozeAuthRemover;
 import com.android.systemui.doze.DozeBrightnessHostForwarder;
 import com.android.systemui.doze.DozeDockHandler;
@@ -51,7 +50,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
-import java.util.concurrent.Executor;
 
 /** Dagger module for use with {@link com.android.systemui.doze.dagger.DozeComponent}. */
 @Module
@@ -60,13 +58,13 @@
     @DozeScope
     @WrappedService
     static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService,
-            DozeHost dozeHost, DozeParameters dozeParameters, @UiBackground Executor bgExecutor) {
+            DozeHost dozeHost, DozeParameters dozeParameters) {
         DozeMachine.Service wrappedService = dozeMachineService;
-        wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost, bgExecutor);
+        wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost);
         wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded(
-                wrappedService, dozeParameters, bgExecutor);
+                wrappedService, dozeParameters);
         wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(
-                wrappedService, dozeParameters, bgExecutor);
+                wrappedService, dozeParameters);
 
         return wrappedService;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 24ac542..9051745 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -255,10 +255,8 @@
         return mAnimator as AnimatorSet
     }
 
-    /** Starts the dream content and dream overlay exit animations. */
-    fun wakeUp() {
+    fun onWakeUp() {
         cancelAnimations()
-        mOverlayStateController.setExitAnimationsRunning(true)
     }
 
     /** Cancels the dream content and dream overlay animations, if they're currently running. */
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index bf6d266..3dd2561 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -381,16 +381,17 @@
     }
 
     /**
-     * Handle the dream waking up and run any necessary animations.
+     * Handle the dream waking up.
      */
-    public void wakeUp() {
+    public void onWakeUp() {
+        // TODO(b/361872929): clean up this bool as it doesn't do anything anymore
         // When swiping causes wakeup, do not run any animations as the dream should exit as soon
         // as possible.
         if (mWakingUpFromSwipe) {
             return;
         }
 
-        mDreamOverlayAnimationsController.wakeUp();
+        mDreamOverlayAnimationsController.onWakeUp();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 4b9e5a0..113e0011 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -24,12 +24,11 @@
 import static com.android.systemui.dreams.dagger.DreamModule.HOME_CONTROL_PANEL_DREAM_COMPONENT;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
+import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.graphics.drawable.ColorDrawable;
-import android.service.dreams.DreamActivity;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -60,12 +59,12 @@
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.communal.shared.log.CommunalUiEvent;
 import com.android.systemui.communal.shared.model.CommunalScenes;
-import com.android.systemui.complication.Complication;
 import com.android.systemui.complication.dagger.ComplicationComponent;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
 import com.android.systemui.shade.ShadeExpansionChangeEvent;
 import com.android.systemui.touch.TouchInsetManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -90,6 +89,8 @@
         LifecycleOwner {
     private static final String TAG = "DreamOverlayService";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final TaskMatcher DREAM_TYPE_MATCHER =
+            new TaskMatcher.TopActivityType(WindowConfiguration.ACTIVITY_TYPE_DREAM);
 
     // The Context is used to construct the hosting constraint layout and child overlay views.
     private final Context mContext;
@@ -133,16 +134,14 @@
      */
     private boolean mBouncerShowing = false;
 
-    private final ComplicationComponent mComplicationComponent;
+    private final com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
+            mDreamComplicationComponentFactory;
+    private final ComplicationComponent.Factory mComplicationComponentFactory;
+    private final DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
+    private final AmbientTouchComponent.Factory mAmbientTouchComponentFactory;
 
-    private final AmbientTouchComponent mAmbientTouchComponent;
-
-    private final com.android.systemui.dreams.complication.dagger.ComplicationComponent
-            mDreamComplicationComponent;
-
-    private final DreamOverlayComponent mDreamOverlayComponent;
-
-    private ComponentName mCurrentBlockedGestureDreamActivityComponent;
+    private final TouchInsetManager mTouchInsetManager;
+    private final LifecycleOwner mLifecycleOwner;
 
     private final ArrayList<Job> mFlows = new ArrayList<>();
 
@@ -186,6 +185,7 @@
                         mShadeExpanded = expanded;
 
                         updateLifecycleStateLocked();
+                        updateGestureBlockingLocked();
                     });
                 }
             };
@@ -216,20 +216,127 @@
                 mBouncerShowing = bouncerShowing;
 
                 updateLifecycleStateLocked();
+                updateGestureBlockingLocked();
             });
         }
     };
 
-    private final DreamOverlayStateController.Callback mExitAnimationFinishedCallback =
-            new DreamOverlayStateController.Callback() {
-                @Override
-                public void onStateChanged() {
-                    if (!mStateController.areExitAnimationsRunning()) {
-                        mStateController.removeCallback(mExitAnimationFinishedCallback);
-                        resetCurrentDreamOverlayLocked();
+    /**
+     * {@link ResetHandler} protects resetting {@link DreamOverlayService} by making sure reset
+     * requests are processed before subsequent actions proceed. Requests themselves are also
+     * ordered between each other as well to ensure actions are correctly sequenced.
+     */
+    private final class ResetHandler {
+        @FunctionalInterface
+        interface Callback {
+            void onComplete();
+        }
+
+        private record Info(Callback callback, String source) {}
+
+        private final ArrayList<Info> mPendingCallbacks = new ArrayList<>();
+
+        DreamOverlayStateController.Callback mStateCallback =
+                new DreamOverlayStateController.Callback() {
+                    @Override
+                    public void onStateChanged() {
+                        process(true);
                     }
+                };
+
+        /**
+         * Called from places where there is no need to wait for the reset to complete. This still
+         * will defer the reset until it is okay to reset and also sequences the request with
+         * others.
+         */
+        public void reset(String source) {
+            reset(() -> {}, source);
+        }
+
+        /**
+         * Invoked to request a reset with a callback that will fire after reset if it is deferred.
+         *
+         * @return {@code true} if the reset happened immediately, {@code false} if it was deferred
+         * and will fire later, invoking the callback.
+         */
+        public boolean reset(Callback callback, String source) {
+            // Always add listener pre-emptively
+            if (mPendingCallbacks.isEmpty()) {
+                mStateController.addCallback(mStateCallback);
+            }
+
+            final Info info = new Info(callback, source);
+            mPendingCallbacks.add(info);
+            process(false);
+
+            boolean processed = !mPendingCallbacks.contains(info);
+
+            if (!processed) {
+                Log.d(TAG, "delayed resetting from: " + source);
+            }
+
+            return processed;
+        }
+
+        private void resetInternal() {
+            // This ensures the container view of the current dream is removed before
+            // the controller is potentially reset.
+            removeContainerViewFromParentLocked();
+
+            if (mStarted && mWindow != null) {
+                try {
+                    mWindow.clearContentView();
+                    mWindowManager.removeView(mWindow.getDecorView());
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, "Error removing decor view when resetting overlay", e);
                 }
-            };
+            }
+
+            mStateController.setOverlayActive(false);
+            mStateController.setLowLightActive(false);
+            mStateController.setEntryAnimationsFinished(false);
+
+            if (mDreamOverlayContainerViewController != null) {
+                mDreamOverlayContainerViewController.destroy();
+                mDreamOverlayContainerViewController = null;
+            }
+
+            if (mTouchMonitor != null) {
+                mTouchMonitor.destroy();
+                mTouchMonitor = null;
+            }
+
+            mWindow = null;
+
+            // Always unregister the any set DreamActivity from being blocked from gestures.
+            mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+                    GestureInteractor.Scope.Global);
+
+            mStarted = false;
+        }
+
+        private boolean canReset() {
+            return !mStateController.areExitAnimationsRunning();
+        }
+
+        private void process(boolean fromDelayedCallback) {
+            while (canReset() && !mPendingCallbacks.isEmpty()) {
+                final Info callbackInfo = mPendingCallbacks.removeFirst();
+                resetInternal();
+                callbackInfo.callback.onComplete();
+
+                if (fromDelayedCallback) {
+                    Log.d(TAG, "reset overlay (delayed) for " + callbackInfo.source);
+                }
+            }
+
+            if (mPendingCallbacks.isEmpty()) {
+                mStateController.removeCallback(mStateCallback);
+            }
+        }
+    }
+
+    private final ResetHandler mResetHandler = new ResetHandler();
 
     private final DreamOverlayStateController mStateController;
 
@@ -291,26 +398,17 @@
         mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
         mStateController = stateController;
         mUiEventLogger = uiEventLogger;
+        mComplicationComponentFactory = complicationComponentFactory;
+        mDreamComplicationComponentFactory = dreamComplicationComponentFactory;
         mDreamOverlayCallbackController = dreamOverlayCallbackController;
         mWindowTitle = windowTitle;
         mCommunalInteractor = communalInteractor;
         mSystemDialogsCloser = systemDialogsCloser;
         mGestureInteractor = gestureInteractor;
-
-        final ViewModelStore viewModelStore = new ViewModelStore();
-        final Complication.Host host =
-                () -> mExecutor.execute(DreamOverlayService.this::requestExit);
-
-        mComplicationComponent = complicationComponentFactory.create(lifecycleOwner, host,
-                viewModelStore, touchInsetManager);
-        mDreamComplicationComponent = dreamComplicationComponentFactory.create(
-                mComplicationComponent.getVisibilityController(), touchInsetManager);
-        mDreamOverlayComponent = dreamOverlayComponentFactory.create(lifecycleOwner,
-                mComplicationComponent.getComplicationHostViewController(), touchInsetManager);
-        mAmbientTouchComponent = ambientTouchComponentFactory.create(lifecycleOwner,
-                new HashSet<>(Arrays.asList(
-                        mDreamComplicationComponent.getHideComplicationTouchHandler(),
-                        mDreamOverlayComponent.getCommunalTouchHandler())));
+        mDreamOverlayComponentFactory = dreamOverlayComponentFactory;
+        mAmbientTouchComponentFactory = ambientTouchComponentFactory;
+        mTouchInsetManager = touchInsetManager;
+        mLifecycleOwner = lifecycleOwner;
         mLifecycleRegistry = lifecycleOwner.getRegistry();
 
         mExecutor.execute(() -> setLifecycleStateLocked(Lifecycle.State.CREATED));
@@ -352,10 +450,8 @@
 
         mExecutor.execute(() -> {
             setLifecycleStateLocked(Lifecycle.State.DESTROYED);
-
-            resetCurrentDreamOverlayLocked();
-
             mDestroyed = true;
+            mResetHandler.reset("destroying");
         });
 
         mDispatcher.onServicePreSuperOnDestroy();
@@ -364,6 +460,23 @@
 
     @Override
     public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
+        final ComplicationComponent complicationComponent = mComplicationComponentFactory.create(
+                mLifecycleOwner,
+                () -> mExecutor.execute(DreamOverlayService.this::requestExit),
+                new ViewModelStore(), mTouchInsetManager);
+        final com.android.systemui.dreams.complication.dagger.ComplicationComponent
+                dreamComplicationComponent = mDreamComplicationComponentFactory.create(
+                complicationComponent.getVisibilityController(), mTouchInsetManager);
+
+        final DreamOverlayComponent dreamOverlayComponent = mDreamOverlayComponentFactory.create(
+                mLifecycleOwner, complicationComponent.getComplicationHostViewController(),
+                mTouchInsetManager);
+        final AmbientTouchComponent ambientTouchComponent = mAmbientTouchComponentFactory.create(
+                mLifecycleOwner,
+                new HashSet<>(Arrays.asList(
+                        dreamComplicationComponent.getHideComplicationTouchHandler(),
+                        dreamOverlayComponent.getCommunalTouchHandler())), TAG);
+
         setLifecycleStateLocked(Lifecycle.State.STARTED);
 
         mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
@@ -378,19 +491,22 @@
             // Reset the current dream overlay before starting a new one. This can happen
             // when two dreams overlap (briefly, for a smoother dream transition) and both
             // dreams are bound to the dream overlay service.
-            resetCurrentDreamOverlayLocked();
+            if (!mResetHandler.reset(() -> onStartDream(layoutParams),
+                    "starting with dream already started")) {
+                return;
+            }
         }
 
         mDreamOverlayContainerViewController =
-                mDreamOverlayComponent.getDreamOverlayContainerViewController();
-        mTouchMonitor = mAmbientTouchComponent.getTouchMonitor();
+                dreamOverlayComponent.getDreamOverlayContainerViewController();
+        mTouchMonitor = ambientTouchComponent.getTouchMonitor();
         mTouchMonitor.init();
 
         mStateController.setShouldShowComplications(shouldShowComplications());
 
         // If we are not able to add the overlay window, reset the overlay.
         if (!addOverlayWindowLocked(layoutParams)) {
-            resetCurrentDreamOverlayLocked();
+            mResetHandler.reset("couldn't add window while starting");
             return;
         }
 
@@ -411,7 +527,7 @@
         mStarted = true;
 
         updateRedirectWakeup();
-        updateBlockedGestureDreamActivityComponent();
+        updateGestureBlockingLocked();
     }
 
     private void updateRedirectWakeup() {
@@ -422,27 +538,30 @@
         redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming());
     }
 
-    private void updateBlockedGestureDreamActivityComponent() {
-        // TODO(b/343815446): We should not be crafting this ActivityInfo ourselves. It should be
-        // in a common place, Such as DreamActivity itself.
-        final ActivityInfo info = new ActivityInfo();
-        info.name = DreamActivity.class.getName();
-        info.packageName = getDreamComponent().getPackageName();
-        mCurrentBlockedGestureDreamActivityComponent = info.getComponentName();
-
-        mGestureInteractor.addGestureBlockedActivity(mCurrentBlockedGestureDreamActivityComponent,
-                GestureInteractor.Scope.Global);
-    }
-
     @Override
     public void onEndDream() {
-        resetCurrentDreamOverlayLocked();
+        mResetHandler.reset("ending dream");
     }
 
     @Override
     public void onWakeRequested() {
         mUiEventLogger.log(CommunalUiEvent.DREAM_TO_COMMUNAL_HUB_DREAM_AWAKE_START);
-        mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+        mCommunalInteractor.changeScene(CommunalScenes.Communal,
+                "dream wake requested",
+                null);
+    }
+
+    private void updateGestureBlockingLocked() {
+        final boolean shouldBlock = mStarted && !mShadeExpanded && !mBouncerShowing
+                && !isDreamInPreviewMode();
+
+        if (shouldBlock) {
+            mGestureInteractor.addGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+                    GestureInteractor.Scope.Global);
+        } else {
+            mGestureInteractor.removeGestureBlockedMatcher(DREAM_TYPE_MATCHER,
+                    GestureInteractor.Scope.Global);
+        }
     }
 
     private Lifecycle.State getLifecycleStateLocked() {
@@ -470,7 +589,7 @@
     public void onWakeUp() {
         if (mDreamOverlayContainerViewController != null) {
             mDreamOverlayCallbackController.onWakeUp();
-            mDreamOverlayContainerViewController.wakeUp();
+            mDreamOverlayContainerViewController.onWakeUp();
         }
     }
 
@@ -493,7 +612,7 @@
         mSystemDialogsCloser.closeSystemDialogs();
 
         // Hide glanceable hub (this is a nop if glanceable hub is not open).
-        mCommunalInteractor.changeScene(CommunalScenes.Blank, null);
+        mCommunalInteractor.changeScene(CommunalScenes.Blank, "dream come to front", null);
     }
 
     /**
@@ -550,6 +669,10 @@
     }
 
     private void removeContainerViewFromParentLocked() {
+        if (mDreamOverlayContainerViewController == null) {
+            return;
+        }
+
         View containerView = mDreamOverlayContainerViewController.getContainerView();
         if (containerView == null) {
             return;
@@ -561,45 +684,4 @@
         Log.w(TAG, "Removing dream overlay container view parent!");
         parentView.removeView(containerView);
     }
-
-    private void resetCurrentDreamOverlayLocked() {
-        if (mStateController.areExitAnimationsRunning()) {
-            mStateController.addCallback(mExitAnimationFinishedCallback);
-            return;
-        }
-
-        if (mStarted && mWindow != null) {
-            try {
-                mWindow.clearContentView();
-                mWindowManager.removeView(mWindow.getDecorView());
-            } catch (IllegalArgumentException e) {
-                Log.e(TAG, "Error removing decor view when resetting overlay", e);
-            }
-        }
-
-        mStateController.setOverlayActive(false);
-        mStateController.setLowLightActive(false);
-        mStateController.setEntryAnimationsFinished(false);
-
-        if (mDreamOverlayContainerViewController != null) {
-            mDreamOverlayContainerViewController.destroy();
-            mDreamOverlayContainerViewController = null;
-        }
-
-        if (mTouchMonitor != null) {
-            mTouchMonitor.destroy();
-            mTouchMonitor = null;
-        }
-
-        mWindow = null;
-
-        // Always unregister the any set DreamActivity from being blocked from gestures.
-        if (mCurrentBlockedGestureDreamActivityComponent != null) {
-            mGestureInteractor.removeGestureBlockedActivity(
-                    mCurrentBlockedGestureDreamActivityComponent, GestureInteractor.Scope.Global);
-            mCurrentBlockedGestureDreamActivityComponent = null;
-        }
-
-        mStarted = false;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
index befd822..d547de2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/TaskFragmentComponent.kt
@@ -43,6 +43,8 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
 
 typealias FragmentInfoCallback = (TaskFragmentInfo) -> Unit
 
@@ -68,14 +70,18 @@
     }
 
     private val fragmentToken = Binder()
-    private val organizer: TaskFragmentOrganizer =
-        object : TaskFragmentOrganizer(executor) {
 
-                override fun onTransactionReady(transaction: TaskFragmentTransaction) {
-                    handleTransactionReady(transaction)
-                }
-            }
-            .apply { registerOrganizer(true /* isSystemOrganizer */) }
+    class Organizer(val component: WeakReference<TaskFragmentComponent>, executor: Executor) :
+        TaskFragmentOrganizer(executor) {
+        override fun onTransactionReady(transaction: TaskFragmentTransaction) {
+            component.get()?.handleTransactionReady(transaction)
+        }
+    }
+
+    private val organizer: TaskFragmentOrganizer =
+        Organizer(WeakReference(this), executor).apply {
+            registerOrganizer(true /* isSystemOrganizer */)
+        }
 
     private fun handleTransactionReady(transaction: TaskFragmentTransaction) {
         val resultT = WindowContainerTransaction()
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 4b07f78..5c0335a6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -20,9 +20,9 @@
 import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
 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
@@ -51,6 +51,7 @@
     fromGlanceableHubTransitionInteractor: GlanceableHubToDreamingTransitionViewModel,
     toGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel,
     private val toLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+    private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
     private val communalInteractor: CommunalInteractor,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val userTracker: UserTracker,
@@ -61,11 +62,9 @@
         val showGlanceableHub =
             communalInteractor.isCommunalEnabled.value &&
                 !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
-        if (showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()) {
-            communalInteractor.changeScene(CommunalScenes.Communal)
-        } else {
-            toLockscreenTransitionViewModel.startTransition()
-        }
+        fromDreamingTransitionInteractor.startToLockscreenOrGlanceableHubTransition(
+            showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()
+        )
     }
 
     val dreamOverlayTranslationX: Flow<Float> =
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 096556f..7e2c9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -26,6 +26,7 @@
 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
 import dagger.Module
@@ -74,7 +75,7 @@
                 implLazy.get()
             } else {
                 // No-op implementation when the flag is disabled.
-                return NoOpContextualEducationInteractor
+                return NoOpCoreStartable
             }
         }
 
@@ -91,6 +92,8 @@
         }
 
         @Provides
+        @IntoMap
+        @ClassKey(KeyboardTouchpadEduInteractor::class)
         fun provideKeyboardTouchpadEduInteractor(
             implLazy: Lazy<KeyboardTouchpadEduInteractor>
         ): CoreStartable {
@@ -98,22 +101,32 @@
                 implLazy.get()
             } else {
                 // No-op implementation when the flag is disabled.
-                return NoOpKeyboardTouchpadEduInteractor
+                return NoOpCoreStartable
+            }
+        }
+
+        @Provides
+        @IntoMap
+        @ClassKey(ContextualEduUiCoordinator::class)
+        fun provideContextualEduUiCoordinator(
+            implLazy: Lazy<ContextualEduUiCoordinator>
+        ): CoreStartable {
+            return if (Flags.keyboardTouchpadContextualEducation()) {
+                implLazy.get()
+            } else {
+                // No-op implementation when the flag is disabled.
+                return NoOpCoreStartable
             }
         }
     }
+}
 
-    private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
-        override fun incrementSignalCount(gestureType: GestureType) {}
+private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
+    override fun incrementSignalCount(gestureType: GestureType) {}
 
-        override fun updateShortcutTriggerTime(gestureType: GestureType) {}
-    }
+    override fun updateShortcutTriggerTime(gestureType: GestureType) {}
+}
 
-    private object NoOpContextualEducationInteractor : CoreStartable {
-        override fun start() {}
-    }
-
-    private object NoOpKeyboardTouchpadEduInteractor : CoreStartable {
-        override fun start() {}
-    }
+private object NoOpCoreStartable : CoreStartable {
+    override fun start() {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/model/EduDeviceConnectionTime.kt b/packages/SystemUI/src/com/android/systemui/education/data/model/EduDeviceConnectionTime.kt
new file mode 100644
index 0000000..8682848
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/data/model/EduDeviceConnectionTime.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.data.model
+
+import java.time.Instant
+
+data class EduDeviceConnectionTime(
+    val keyboardFirstConnectionTime: Instant? = null,
+    val touchpadFirstConnectionTime: Instant? = null
+)
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt b/packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt
index a171f87..1daaa11 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt
@@ -28,4 +28,5 @@
     val lastShortcutTriggeredTime: Instant? = null,
     val usageSessionStartTime: Instant? = null,
     val lastEducationTime: Instant? = null,
+    val userId: Int
 )
diff --git a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
index 7c3d6338..01f838f 100644
--- a/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt
@@ -29,10 +29,12 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.education.dagger.ContextualEducationModule.EduDataStoreScope
+import com.android.systemui.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import java.time.Instant
 import javax.inject.Inject
 import javax.inject.Provider
+import kotlin.properties.Delegates.notNull
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.Flow
@@ -52,10 +54,16 @@
 
     fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel>
 
+    fun readEduDeviceConnectionTime(): Flow<EduDeviceConnectionTime>
+
     suspend fun updateGestureEduModel(
         gestureType: GestureType,
         transform: (GestureEduModel) -> GestureEduModel
     )
+
+    suspend fun updateEduDeviceConnectionTime(
+        transform: (EduDeviceConnectionTime) -> EduDeviceConnectionTime
+    )
 }
 
 /**
@@ -75,10 +83,14 @@
         const val LAST_SHORTCUT_TRIGGERED_TIME_SUFFIX = "_LAST_SHORTCUT_TRIGGERED_TIME"
         const val USAGE_SESSION_START_TIME_SUFFIX = "_USAGE_SESSION_START_TIME"
         const val LAST_EDUCATION_TIME_SUFFIX = "_LAST_EDUCATION_TIME"
+        const val KEYBOARD_FIRST_CONNECTION_TIME = "KEYBOARD_FIRST_CONNECTION_TIME"
+        const val TOUCHPAD_FIRST_CONNECTION_TIME = "TOUCHPAD_FIRST_CONNECTION_TIME"
 
         const val DATASTORE_DIR = "education/USER%s_ContextualEducation"
     }
 
+    private var userId by notNull<Int>()
+
     private var dataStoreScope: CoroutineScope? = null
 
     private val datastore = MutableStateFlow<DataStore<Preferences>?>(null)
@@ -89,6 +101,7 @@
     override fun setUser(userId: Int) {
         dataStoreScope?.cancel()
         val newDsScope = dataStoreScopeProvider.get()
+        this.userId = userId
         datastore.value =
             PreferenceDataStoreFactory.create(
                 produceFile = {
@@ -123,6 +136,7 @@
                 preferences[getLastEducationTimeKey(gestureType)]?.let {
                     Instant.ofEpochSecond(it)
                 },
+            userId = userId
         )
     }
 
@@ -153,6 +167,37 @@
         }
     }
 
+    override fun readEduDeviceConnectionTime(): Flow<EduDeviceConnectionTime> =
+        prefData.map { preferences -> getEduDeviceConnectionTime(preferences) }
+
+    override suspend fun updateEduDeviceConnectionTime(
+        transform: (EduDeviceConnectionTime) -> EduDeviceConnectionTime
+    ) {
+        datastore.filterNotNull().first().edit { preferences ->
+            val currentModel = getEduDeviceConnectionTime(preferences)
+            val updatedModel = transform(currentModel)
+            setInstant(
+                preferences,
+                updatedModel.keyboardFirstConnectionTime,
+                getKeyboardFirstConnectionTimeKey()
+            )
+            setInstant(
+                preferences,
+                updatedModel.touchpadFirstConnectionTime,
+                getTouchpadFirstConnectionTimeKey()
+            )
+        }
+    }
+
+    private fun getEduDeviceConnectionTime(preferences: Preferences): EduDeviceConnectionTime {
+        return EduDeviceConnectionTime(
+            keyboardFirstConnectionTime =
+                preferences[getKeyboardFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) },
+            touchpadFirstConnectionTime =
+                preferences[getTouchpadFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) }
+        )
+    }
+
     private fun getSignalCountKey(gestureType: GestureType): Preferences.Key<Int> =
         intPreferencesKey(gestureType.name + SIGNAL_COUNT_SUFFIX)
 
@@ -168,6 +213,12 @@
     private fun getLastEducationTimeKey(gestureType: GestureType): Preferences.Key<Long> =
         longPreferencesKey(gestureType.name + LAST_EDUCATION_TIME_SUFFIX)
 
+    private fun getKeyboardFirstConnectionTimeKey(): Preferences.Key<Long> =
+        longPreferencesKey(KEYBOARD_FIRST_CONNECTION_TIME)
+
+    private fun getTouchpadFirstConnectionTimeKey(): Preferences.Key<Long> =
+        longPreferencesKey(TOUCHPAD_FIRST_CONNECTION_TIME)
+
     private fun setInstant(
         preferences: MutablePreferences,
         instant: Instant?,
diff --git a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
index db5c386..10be26e 100644
--- a/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt
@@ -22,6 +22,7 @@
 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.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import com.android.systemui.education.data.repository.ContextualEducationRepository
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -32,6 +33,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
 
@@ -67,6 +69,10 @@
             .flowOn(backgroundDispatcher)
     }
 
+    suspend fun getEduDeviceConnectionTime(): EduDeviceConnectionTime {
+        return repository.readEduDeviceConnectionTime().first()
+    }
+
     suspend fun incrementSignalCount(gestureType: GestureType) {
         repository.updateGestureEduModel(gestureType) {
             it.copy(
@@ -100,4 +106,16 @@
             it.copy(usageSessionStartTime = clock.instant(), signalCount = 1)
         }
     }
+
+    suspend fun updateKeyboardFirstConnectionTime() {
+        repository.updateEduDeviceConnectionTime {
+            it.copy(keyboardFirstConnectionTime = clock.instant())
+        }
+    }
+
+    suspend fun updateTouchpadFirstConnectionTime() {
+        repository.updateEduDeviceConnectionTime {
+            it.copy(touchpadFirstConnectionTime = clock.instant())
+        }
+    }
 }
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 3a3fb8c..43855d9 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
@@ -16,7 +16,14 @@
 
 package com.android.systemui.education.domain.interactor
 
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyGestureEventListener
+import android.hardware.input.KeyGestureEvent
+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.contextualeducation.GestureType.BACK
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -24,10 +31,18 @@
 import com.android.systemui.education.data.model.GestureEduModel
 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.utils.coroutines.flow.conflatedCallbackFlow
 import java.time.Clock
+import java.util.concurrent.Executor
 import javax.inject.Inject
-import kotlin.time.Duration.Companion.hours
+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.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.launch
@@ -39,35 +54,112 @@
 constructor(
     @Background private val backgroundScope: CoroutineScope,
     private val contextualEducationInteractor: ContextualEducationInteractor,
+    private val userInputDeviceRepository: UserInputDeviceRepository,
+    private val inputManager: InputManager,
     @EduClock private val clock: Clock,
 ) : CoreStartable {
 
     companion object {
+        const val TAG = "KeyboardTouchpadEduInteractor"
         const val MAX_SIGNAL_COUNT: Int = 2
-        val usageSessionDuration = 72.hours
+        const val MAX_EDUCATION_SHOW_COUNT: Int = 2
+        val usageSessionDuration =
+            getDurationForConfig("persist.contextual_edu.usage_session_sec", 3.days)
+        val minIntervalBetweenEdu =
+            getDurationForConfig("persist.contextual_edu.edu_interval_sec", 7.days)
+
+        private fun getDurationForConfig(
+            systemPropertyKey: String,
+            defaultDuration: Duration
+        ): Duration =
+            SystemProperties.getLong(
+                    systemPropertyKey,
+                    /* defaultValue= */ defaultDuration.inWholeSeconds
+                )
+                .toDuration(DurationUnit.SECONDS)
     }
 
     private val _educationTriggered = MutableStateFlow<EducationInfo?>(null)
     val educationTriggered = _educationTriggered.asStateFlow()
 
+    private val keyboardShortcutTriggered: Flow<GestureType> = conflatedCallbackFlow {
+        val listener = KeyGestureEventListener { event ->
+            // Only store keyboard shortcut time for gestures providing keyboard education
+            val shortcutType =
+                when (event.keyGestureType) {
+                    KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS -> ALL_APPS
+                    else -> null
+                }
+
+            if (shortcutType != null) {
+                trySendWithFailureLogging(shortcutType, TAG)
+            }
+        }
+
+        inputManager.registerKeyGestureEventListener(Executor(Runnable::run), listener)
+        awaitClose { inputManager.unregisterKeyGestureEventListener(listener) }
+    }
+
     override fun start() {
         backgroundScope.launch {
             contextualEducationInteractor.backGestureModelFlow.collect {
                 if (isUsageSessionExpired(it)) {
                     contextualEducationInteractor.startNewUsageSession(BACK)
                 } else if (isEducationNeeded(it)) {
-                    _educationTriggered.value = EducationInfo(BACK, getEduType(it))
+                    _educationTriggered.value = EducationInfo(BACK, getEduType(it), it.userId)
                     contextualEducationInteractor.updateOnEduTriggered(BACK)
                 }
             }
         }
+
+        backgroundScope.launch {
+            userInputDeviceRepository.isAnyTouchpadConnectedForUser.collect {
+                if (
+                    it.isConnected &&
+                        contextualEducationInteractor
+                            .getEduDeviceConnectionTime()
+                            .touchpadFirstConnectionTime == null
+                ) {
+                    contextualEducationInteractor.updateTouchpadFirstConnectionTime()
+                }
+            }
+        }
+
+        backgroundScope.launch {
+            userInputDeviceRepository.isAnyKeyboardConnectedForUser.collect {
+                if (
+                    it.isConnected &&
+                        contextualEducationInteractor
+                            .getEduDeviceConnectionTime()
+                            .keyboardFirstConnectionTime == null
+                ) {
+                    contextualEducationInteractor.updateKeyboardFirstConnectionTime()
+                }
+            }
+        }
+
+        backgroundScope.launch {
+            keyboardShortcutTriggered.collect {
+                contextualEducationInteractor.updateShortcutTriggerTime(it)
+            }
+        }
     }
 
     private fun isEducationNeeded(model: GestureEduModel): Boolean {
-        // Todo: b/354884305 - add complete education logic to show education in correct scenarios
+        val lessThanMaxEduCount = model.educationShownCount < MAX_EDUCATION_SHOW_COUNT
         val noShortcutTriggered = model.lastShortcutTriggeredTime == null
         val signalCountReached = model.signalCount >= MAX_SIGNAL_COUNT
-        return noShortcutTriggered && signalCountReached
+        val isPreviousEduOlderThanMinInterval =
+            if (model.educationShownCount == 1) {
+                model.lastEducationTime
+                    ?.plusSeconds(minIntervalBetweenEdu.inWholeSeconds)
+                    ?.isBefore(clock.instant()) ?: true
+            } else true
+
+        return lessThanMaxEduCount &&
+            noShortcutTriggered &&
+            signalCountReached &&
+            isPreviousEduOlderThanMinInterval
     }
 
     private fun isUsageSessionExpired(model: GestureEduModel): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt b/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt
index d92fb9b..27c41cff 100644
--- a/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/shared/model/EducationInfo.kt
@@ -22,7 +22,11 @@
  * Model for education triggered. [gestureType] indicates what gesture it is trying to educate about
  * and [educationUiType] is how we educate user in the UI
  */
-data class EducationInfo(val gestureType: GestureType, val educationUiType: EducationUiType)
+data class EducationInfo(
+    val gestureType: GestureType,
+    val educationUiType: EducationUiType,
+    val userId: Int
+)
 
 enum class EducationUiType {
     Toast,
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
new file mode 100644
index 0000000..e62b26b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.ui.view
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.UserHandle
+import android.widget.Toast
+import androidx.core.app.NotificationCompat
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.education.ui.viewmodel.ContextualEduNotificationViewModel
+import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A class to show contextual education on UI based on the edu produced from
+ * [ContextualEduViewModel]
+ */
+@SysUISingleton
+class ContextualEduUiCoordinator
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val viewModel: ContextualEduViewModel,
+    private val context: Context,
+    private val notificationManager: NotificationManager,
+    private val createToast: (String) -> Toast
+) : CoreStartable {
+
+    companion object {
+        private const val CHANNEL_ID = "ContextualEduNotificationChannel"
+        private const val TAG = "ContextualEduUiCoordinator"
+        private const val NOTIFICATION_ID = 1000
+    }
+
+    @Inject
+    constructor(
+        @Application applicationScope: CoroutineScope,
+        context: Context,
+        viewModel: ContextualEduViewModel,
+        notificationManager: NotificationManager,
+    ) : this(
+        applicationScope,
+        viewModel,
+        context,
+        notificationManager,
+        createToast = { message -> Toast.makeText(context, message, Toast.LENGTH_LONG) }
+    )
+
+    override fun start() {
+        createEduNotificationChannel()
+        applicationScope.launch {
+            viewModel.eduContent.collect { contentModel ->
+                when (contentModel) {
+                    is ContextualEduToastViewModel -> showToast(contentModel)
+                    is ContextualEduNotificationViewModel -> showNotification(contentModel)
+                }
+            }
+        }
+    }
+
+    private fun createEduNotificationChannel() {
+        val channel =
+            NotificationChannel(
+                CHANNEL_ID,
+                context.getString(com.android.internal.R.string.android_system_label),
+                // Make it as silent notification
+                NotificationManager.IMPORTANCE_LOW
+            )
+        notificationManager.createNotificationChannel(channel)
+    }
+
+    private fun showToast(model: ContextualEduToastViewModel) {
+        val toast = createToast(model.message)
+        toast.show()
+    }
+
+    private fun showNotification(model: ContextualEduNotificationViewModel) {
+        // Replace "System UI" app name with "Android System"
+        val extras = Bundle()
+        extras.putString(
+            Notification.EXTRA_SUBSTITUTE_APP_NAME,
+            context.getString(com.android.internal.R.string.android_system_label)
+        )
+
+        val notification =
+            NotificationCompat.Builder(context, CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_settings)
+                .setContentTitle(model.title)
+                .setContentText(model.message)
+                .setContentIntent(createPendingIntent())
+                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+                .setAutoCancel(true)
+                .addExtras(extras)
+                .build()
+        notificationManager.notifyAsUser(
+            TAG,
+            NOTIFICATION_ID,
+            notification,
+            UserHandle.of(model.userId)
+        )
+    }
+
+    private fun createPendingIntent(): PendingIntent {
+        val intent =
+            Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
+                addCategory(Intent.CATEGORY_DEFAULT)
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            }
+        return PendingIntent.getActivity(
+            context,
+            /* requestCode= */ 0,
+            intent,
+            PendingIntent.FLAG_IMMUTABLE
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
new file mode 100644
index 0000000..632b250
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.ui.viewmodel
+
+sealed class ContextualEduContentViewModel(open val userId: Int)
+
+data class ContextualEduNotificationViewModel(
+    val title: String,
+    val message: String,
+    override val userId: Int
+) : ContextualEduContentViewModel(userId)
+
+data class ContextualEduToastViewModel(val message: String, override val userId: Int) :
+    ContextualEduContentViewModel(userId)
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
new file mode 100644
index 0000000..cd4a8ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.res.Resources
+import com.android.systemui.contextualeducation.GestureType.ALL_APPS
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.contextualeducation.GestureType.HOME
+import com.android.systemui.contextualeducation.GestureType.OVERVIEW
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.shared.model.EducationInfo
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ContextualEduViewModel
+@Inject
+constructor(@Main private val resources: Resources, interactor: KeyboardTouchpadEduInteractor) {
+    val eduContent: Flow<ContextualEduContentViewModel> =
+        interactor.educationTriggered.filterNotNull().map {
+            if (it.educationUiType == EducationUiType.Notification) {
+                ContextualEduNotificationViewModel(getEduTitle(it), getEduContent(it), it.userId)
+            } else {
+                ContextualEduToastViewModel(getEduContent(it), it.userId)
+            }
+        }
+
+    private fun getEduContent(educationInfo: EducationInfo): String {
+        val resourceId =
+            if (educationInfo.educationUiType == EducationUiType.Notification) {
+                when (educationInfo.gestureType) {
+                    BACK -> R.string.back_edu_notification_content
+                    HOME -> R.string.home_edu_notification_content
+                    OVERVIEW -> R.string.overview_edu_notification_content
+                    ALL_APPS -> R.string.all_apps_edu_notification_content
+                }
+            } else {
+                when (educationInfo.gestureType) {
+                    BACK -> R.string.back_edu_toast_content
+                    HOME -> R.string.home_edu_toast_content
+                    OVERVIEW -> R.string.overview_edu_toast_content
+                    ALL_APPS -> R.string.all_apps_edu_toast_content
+                }
+            }
+
+        return resources.getString(resourceId)
+    }
+
+    private fun getEduTitle(educationInfo: EducationInfo): String {
+        val resourceId =
+            when (educationInfo.gestureType) {
+                BACK -> R.string.back_edu_notification_title
+                HOME -> R.string.home_edu_notification_title
+                OVERVIEW -> R.string.overview_edu_notification_title
+                ALL_APPS -> R.string.all_apps_edu_notification_title
+            }
+
+        return resources.getString(resourceId)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index cd0b3f9..6318dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -41,7 +41,6 @@
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
 import javax.inject.Inject
 
@@ -59,7 +58,6 @@
         NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
         PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
         NotificationMinimalismPrototype.token dependsOn NotificationThrottleHun.token
-        NotificationsHeadsUpRefactor.token dependsOn NotificationThrottleHun.token
 
         // SceneContainer dependencies
         SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 8990505..95cd9eb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -55,19 +55,13 @@
     // TODO(b/254512624): Tracking Bug
     @JvmField
     val NOTIFICATION_DRAG_TO_CONTENTS =
-        resourceBooleanFlag(
-            R.bool.config_notificationToContents,
-            "notification_drag_to_contents"
-        )
+        resourceBooleanFlag(R.bool.config_notificationToContents, "notification_drag_to_contents")
 
     // TODO(b/280783617): Tracking Bug
     @Keep
     @JvmField
     val BUILDER_EXTRAS_OVERRIDE =
-        sysPropBooleanFlag(
-            "persist.sysui.notification.builder_extras_override",
-            default = true
-        )
+        sysPropBooleanFlag("persist.sysui.notification.builder_extras_override", default = true)
 
     // 200 - keyguard/lockscreen
     // ** Flag retired **
@@ -81,10 +75,7 @@
     // TODO(b/254512676): Tracking Bug
     @JvmField
     val LOCKSCREEN_CUSTOM_CLOCKS =
-        resourceBooleanFlag(
-            R.bool.config_enableLockScreenCustomClocks,
-            "lockscreen_custom_clocks"
-        )
+        resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks")
 
     /**
      * Whether the clock on a wide lock screen should use the new "stepping" animation for moving
@@ -99,10 +90,6 @@
     // TODO(b/255607168): Tracking Bug
     @JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
 
-    // TODO(b/305984787):
-    @JvmField
-    val REFACTOR_GETCURRENTUSER = unreleasedFlag("refactor_getcurrentuser", teamfood = true)
-
     /** Flag to control the revamp of keyguard biometrics progress animation */
     // TODO(b/244313043): Tracking bug
     @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
@@ -111,9 +98,6 @@
     @JvmField
     val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
 
-    // TODO(b/262859270): Tracking Bug
-    @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag("falsing_off_for_unfolded")
-
     /** Enables code to show contextual loyalty cards in wallet entrypoints */
     // TODO(b/294110497): Tracking Bug
     @JvmField
@@ -125,13 +109,11 @@
 
     /** Whether the long-press gesture to open wallpaper picker is enabled. */
     // TODO(b/266242192): Tracking Bug
-    @JvmField
-    val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
+    @JvmField val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
 
     /** Inflate and bind views upon emitting a blueprint value . */
     // TODO(b/297365780): Tracking Bug
-    @JvmField
-    val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
+    @JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
 
     /** Enables UI updates for AI wallpapers in the wallpaper picker. */
     // TODO(b/267722622): Tracking Bug
@@ -145,16 +127,12 @@
     /** Add "Apply" button to wall paper picker's grid preview page. */
     // TODO(b/294866904): Tracking bug.
     @JvmField
-    val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
-            unreleasedFlag("wallpaper_picker_grid_apply_button")
+    val WALLPAPER_PICKER_GRID_APPLY_BUTTON = unreleasedFlag("wallpaper_picker_grid_apply_button")
 
     /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
     // TODO(b/286563884): Tracking bug
     @JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
 
-    // TODO(b/287268101): Tracking bug.
-    @JvmField val TRANSIT_CLOCK = releasedFlag("lockscreen_custom_transit_clock")
-
     /** Enables preview loading animation in the wallpaper picker. */
     // TODO(b/274443705): Tracking Bug
     @JvmField
@@ -193,10 +171,7 @@
     // TODO(b/254512383): Tracking Bug
     @JvmField
     val FULL_SCREEN_USER_SWITCHER =
-        resourceBooleanFlag(
-            R.bool.config_enableFullscreenUserSwitcher,
-            "full_screen_user_switcher"
-        )
+        resourceBooleanFlag(R.bool.config_enableFullscreenUserSwitcher, "full_screen_user_switcher")
 
     // TODO(b/244064524): Tracking Bug
     @JvmField val QS_SECONDARY_DATA_SUB_INFO = releasedFlag("qs_secondary_data_sub_info")
@@ -215,26 +190,18 @@
     @JvmField val NEW_NETWORK_SLICE_UI = releasedFlag("new_network_slice_ui")
 
     // TODO(b/311222557): Tracking bug
-    val ROAMING_INDICATOR_VIA_DISPLAY_INFO =
-        releasedFlag("roaming_indicator_via_display_info")
+    val ROAMING_INDICATOR_VIA_DISPLAY_INFO = releasedFlag("roaming_indicator_via_display_info")
 
     // TODO(b/308138154): Tracking bug
     val FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS =
         releasedFlag("filter_provisioning_network_subscriptions")
 
     // TODO(b/293863612): Tracking Bug
-    @JvmField val INCOMPATIBLE_CHARGING_BATTERY_ICON =
-        releasedFlag("incompatible_charging_battery_icon")
-
-    // TODO(b/293585143): Tracking Bug
-    val INSTANT_TETHER = releasedFlag("instant_tether")
-
-    // TODO(b/294588085): Tracking Bug
-    val WIFI_SECONDARY_NETWORKS = releasedFlag("wifi_secondary_networks")
+    @JvmField
+    val INCOMPATIBLE_CHARGING_BATTERY_ICON = releasedFlag("incompatible_charging_battery_icon")
 
     // TODO(b/290676905): Tracking Bug
-    val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
-        releasedFlag("new_shade_carrier_group_mobile_icons")
+    val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS = releasedFlag("new_shade_carrier_group_mobile_icons")
 
     // 800 - general visual/theme
     @JvmField val MONET = resourceBooleanFlag(R.bool.flag_monet, "monet")
@@ -257,9 +224,6 @@
     // TODO(b/254512697): Tracking Bug
     val MEDIA_TAP_TO_TRANSFER = releasedFlag("media_tap_to_transfer")
 
-    // TODO(b/254512502): Tracking Bug
-    val MEDIA_SESSION_ACTIONS = unreleasedFlag("media_session_actions")
-
     // TODO(b/254512654): Tracking Bug
     @JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag("dream_media_complication")
 
@@ -283,8 +247,7 @@
 
     // TODO(b/273509374): Tracking Bug
     @JvmField
-    val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS =
-        releasedFlag("always_show_home_controls_on_dreams")
+    val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = releasedFlag("always_show_home_controls_on_dreams")
 
     // 1100 - windowing
     @Keep
@@ -307,9 +270,7 @@
         )
 
     // TODO(b/293252410) : Tracking Bug
-    @JvmField
-    val LOCKSCREEN_ENABLE_LANDSCAPE =
-            unreleasedFlag("lockscreen.enable_landscape")
+    @JvmField val LOCKSCREEN_ENABLE_LANDSCAPE = unreleasedFlag("lockscreen.enable_landscape")
 
     // 1200 - predictive back
     @Keep
@@ -330,8 +291,7 @@
     val QUICK_TAP_IN_PCC = releasedFlag("quick_tap_in_pcc")
 
     // TODO(b/261979569): Tracking Bug
-    val QUICK_TAP_FLOW_FRAMEWORK =
-        unreleasedFlag("quick_tap_flow_framework", teamfood = false)
+    val QUICK_TAP_FLOW_FRAMEWORK = unreleasedFlag("quick_tap_flow_framework", teamfood = false)
 
     // 1500 - chooser aka sharesheet
 
@@ -340,10 +300,6 @@
     // TODO(b/278714186) Tracking Bug
     @JvmField
     val CLIPBOARD_IMAGE_TIMEOUT = unreleasedFlag("clipboard_image_timeout", teamfood = true)
-    // TODO(b/279405451): Tracking Bug
-    @JvmField
-    val CLIPBOARD_SHARED_TRANSITIONS =
-            unreleasedFlag("clipboard_shared_transitions", teamfood = true)
 
     // 1900
     @JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
@@ -371,14 +327,12 @@
     // TODO(b/265764985): Tracking Bug
     @Keep
     @JvmField
-    val ENABLE_DARK_VIGNETTE_WHEN_FOLDING =
-        unreleasedFlag("enable_dark_vignette_when_folding")
+    val ENABLE_DARK_VIGNETTE_WHEN_FOLDING = unreleasedFlag("enable_dark_vignette_when_folding")
 
     // TODO(b/265764985): Tracking Bug
     @Keep
     @JvmField
-    val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS =
-        unreleasedFlag("enable_unfold_status_bar_animations")
+    val ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS = unreleasedFlag("enable_unfold_status_bar_animations")
 
     // TODO(b/316157842): Tracking Bug
     // Adds extra delay to notifications measure
@@ -422,28 +376,26 @@
         unreleasedFlag("bigpicture_notification_lazy_loading")
 
     // TODO(b/283740863): Tracking Bug
-    @JvmField
-    val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
+    @JvmField val ENABLE_NEW_PRIVACY_DIALOG = releasedFlag("enable_new_privacy_dialog")
 
     // TODO(b/302144438): Tracking Bug
-    @JvmField val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
-            unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
+    @JvmField
+    val DECOUPLE_REMOTE_INPUT_DELEGATE_AND_CALLBACK_UPDATE =
+        unreleasedFlag("decouple_remote_input_delegate_and_callback_update")
 
     /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */
     @JvmField
     val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
 
     /** Enable the share wifi button in Quick Settings internet dialog. */
-    @JvmField
-    val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
+    @JvmField val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
 
     /** Enable showing a dialog when clicking on Quick Settings bluetooth tile. */
-    @JvmField
-    val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
+    @JvmField val BLUETOOTH_QS_TILE_DIALOG = releasedFlag("bluetooth_qs_tile_dialog")
 
     // TODO(b/300995746): Tracking Bug
     /** A resource flag for whether the communal service is enabled. */
     @JvmField
-    val COMMUNAL_SERVICE_ENABLED = resourceBooleanFlag(R.bool.config_communalServiceEnabled,
-        "communal_service_enabled")
+    val COMMUNAL_SERVICE_ENABLED =
+        resourceBooleanFlag(R.bool.config_communalServiceEnabled, "communal_service_enabled")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
index 567bf70..ca43871 100644
--- a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -35,6 +35,7 @@
 import android.util.Log
 import android.util.Size
 import androidx.core.content.res.ResourcesCompat
+import com.android.app.tracing.traceSection
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -162,20 +163,21 @@
         @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
-    ): Bitmap? {
-        return try {
-            ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
-                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
-                decoder.allocator = allocator
+    ): Bitmap? =
+        traceSection("ImageLoader#loadBitmap") {
+            return try {
+                ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+                    configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                    decoder.allocator = allocator
+                }
+            } catch (e: IOException) {
+                Log.w(TAG, "Failed to load source $source", e)
+                return null
+            } catch (e: DecodeException) {
+                Log.w(TAG, "Failed to decode source $source", e)
+                return null
             }
-        } catch (e: IOException) {
-            Log.w(TAG, "Failed to load source $source", e)
-            return null
-        } catch (e: DecodeException) {
-            Log.w(TAG, "Failed to decode source $source", e)
-            return null
         }
-    }
 
     /**
      * Loads passed [Source] on a background thread and returns the [Drawable].
@@ -253,28 +255,31 @@
         @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
-    ): Drawable? {
-        return try {
-            loadDrawableSync(
-                toImageDecoderSource(source, defaultContext),
-                maxWidth,
-                maxHeight,
-                allocator
-            )
-                ?:
-                // If we have a resource, retry fallback using the "normal" Resource loading system.
-                // This will come into effect in cases like trying to load AnimatedVectorDrawable.
-                if (source is Res) {
-                    val context = source.context ?: defaultContext
-                    ResourcesCompat.getDrawable(context.resources, source.resId, context.theme)
-                } else {
-                    null
-                }
-        } catch (e: NotFoundException) {
-            Log.w(TAG, "Couldn't load resource $source", e)
-            null
+    ): Drawable? =
+        traceSection("ImageLoader#loadDrawable") {
+            return try {
+                loadDrawableSync(
+                    toImageDecoderSource(source, defaultContext),
+                    maxWidth,
+                    maxHeight,
+                    allocator
+                )
+                    ?:
+                    // If we have a resource, retry fallback using the "normal" Resource loading
+                    // system.
+                    // This will come into effect in cases like trying to load
+                    // AnimatedVectorDrawable.
+                    if (source is Res) {
+                        val context = source.context ?: defaultContext
+                        ResourcesCompat.getDrawable(context.resources, source.resId, context.theme)
+                    } else {
+                        null
+                    }
+            } catch (e: NotFoundException) {
+                Log.w(TAG, "Couldn't load resource $source", e)
+                null
+            }
         }
-    }
 
     /**
      * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
@@ -297,20 +302,21 @@
         @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
-    ): Drawable? {
-        return try {
-            ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
-                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
-                decoder.allocator = allocator
+    ): Drawable? =
+        traceSection("ImageLoader#loadDrawable") {
+            return try {
+                ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
+                    configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                    decoder.allocator = allocator
+                }
+            } catch (e: IOException) {
+                Log.w(TAG, "Failed to load source $source", e)
+                return null
+            } catch (e: DecodeException) {
+                Log.w(TAG, "Failed to decode source $source", e)
+                return null
             }
-        } catch (e: IOException) {
-            Log.w(TAG, "Failed to load source $source", e)
-            return null
-        } catch (e: DecodeException) {
-            Log.w(TAG, "Failed to decode source $source", e)
-            return null
         }
-    }
 
     /** Loads icon drawable while attempting to size restrict the drawable. */
     @WorkerThread
@@ -320,55 +326,59 @@
         @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
         allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
-    ): Drawable? {
-        return when (icon.type) {
-            Icon.TYPE_URI,
-            Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
-                val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
-                loadDrawableSync(source, maxWidth, maxHeight, allocator)
-            }
-            Icon.TYPE_RESOURCE -> {
-                val resources = resolveResourcesForIcon(context, icon)
-                resources?.let {
+    ): Drawable? =
+        traceSection("ImageLoader#loadDrawable") {
+            return when (icon.type) {
+                Icon.TYPE_URI,
+                Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+                    val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+                    loadDrawableSync(source, maxWidth, maxHeight, allocator)
+                }
+                Icon.TYPE_RESOURCE -> {
+                    val resources = resolveResourcesForIcon(context, icon)
+                    resources?.let {
+                        loadDrawableSync(
+                            ImageDecoder.createSource(it, icon.resId),
+                            maxWidth,
+                            maxHeight,
+                            allocator
+                        )
+                    }
+                        // Fallback to non-ImageDecoder load if the attempt failed (e.g. the
+                        // resource
+                        // is a Vector drawable which ImageDecoder doesn't support.)
+                        ?: loadIconDrawable(icon, context)
+                }
+                Icon.TYPE_BITMAP -> {
+                    BitmapDrawable(context.resources, icon.bitmap)
+                }
+                Icon.TYPE_ADAPTIVE_BITMAP -> {
+                    AdaptiveIconDrawable(null, BitmapDrawable(context.resources, icon.bitmap))
+                }
+                Icon.TYPE_DATA -> {
                     loadDrawableSync(
-                        ImageDecoder.createSource(it, icon.resId),
+                        ImageDecoder.createSource(icon.dataBytes, icon.dataOffset, icon.dataLength),
                         maxWidth,
                         maxHeight,
                         allocator
                     )
                 }
-                // Fallback to non-ImageDecoder load if the attempt failed (e.g. the resource
-                // is a Vector drawable which ImageDecoder doesn't support.)
-                ?: loadIconDrawable(icon, context)
+                else -> {
+                    // We don't recognize this icon, just fallback.
+                    loadIconDrawable(icon, context)
+                }
+            }?.let { drawable ->
+                // Icons carry tint which we need to propagate down to a Drawable.
+                tintDrawable(icon, drawable)
+                drawable
             }
-            Icon.TYPE_BITMAP -> {
-                BitmapDrawable(context.resources, icon.bitmap)
-            }
-            Icon.TYPE_ADAPTIVE_BITMAP -> {
-                AdaptiveIconDrawable(null, BitmapDrawable(context.resources, icon.bitmap))
-            }
-            Icon.TYPE_DATA -> {
-                loadDrawableSync(
-                    ImageDecoder.createSource(icon.dataBytes, icon.dataOffset, icon.dataLength),
-                    maxWidth,
-                    maxHeight,
-                    allocator
-                )
-            }
-            else -> {
-                // We don't recognize this icon, just fallback.
-                loadIconDrawable(icon, context)
-            }
-        }?.let { drawable ->
-            // Icons carry tint which we need to propagate down to a Drawable.
-            tintDrawable(icon, drawable)
-            drawable
         }
-    }
 
     @WorkerThread
     fun loadIconDrawable(icon: Icon, context: Context): Drawable? {
-        icon.loadDrawable(context)?.let { return it }
+        icon.loadDrawable(context)?.let {
+            return it
+        }
 
         Log.w(TAG, "Failed to load drawable for $icon")
         return null
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
new file mode 100644
index 0000000..5ea96b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.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.haptics.msdl.dagger
+
+import android.content.Context
+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
+
+@Module
+object MSDLModule {
+    @Provides
+    @SysUISingleton
+    fun provideMSDLPlayer(@Application context: Context): MSDLPlayer =
+        MSDLPlayer.createPlayer(context)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/data/model/UserDeviceConnectionStatus.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/data/model/UserDeviceConnectionStatus.kt
new file mode 100644
index 0000000..1a22d3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/data/model/UserDeviceConnectionStatus.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.inputdevice.data.model
+
+data class UserDeviceConnectionStatus(val isConnected: Boolean, val userId: Int)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepository.kt
new file mode 100644
index 0000000..b8e73a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepository.kt
@@ -0,0 +1,64 @@
+/*
+ * 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.inputdevice.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/**
+ * Allow listening keyboard and touchpad device connection changes for current user. It emits new
+ * value when user is changed.
+ */
+@SysUISingleton
+class UserInputDeviceRepository
+@Inject
+constructor(
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    keyboardRepository: KeyboardRepository,
+    touchpadRepository: TouchpadRepository,
+    userRepository: UserRepository,
+) {
+    private val selectedUserId =
+        userRepository.selectedUser
+            .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
+            .map { it.userInfo.id }
+
+    val isAnyKeyboardConnectedForUser =
+        keyboardRepository.isAnyKeyboardConnected
+            .combine(selectedUserId) { isAnyKeyboardConnected, userId ->
+                UserDeviceConnectionStatus(isAnyKeyboardConnected, userId)
+            }
+            .flowOn(backgroundDispatcher)
+
+    val isAnyTouchpadConnectedForUser =
+        touchpadRepository.isAnyTouchpadConnected
+            .combine(selectedUserId) { isAnyTouchpadConnected, userId ->
+                UserDeviceConnectionStatus(isAnyTouchpadConnected, userId)
+            }
+            .flowOn(backgroundDispatcher)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.kt
new file mode 100644
index 0000000..9525174
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialLogger.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.inputdevice.tutorial
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.InputDeviceTutorialLog
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen
+import com.google.errorprone.annotations.CompileTimeConstant
+import javax.inject.Inject
+
+private const val TAG = "InputDeviceTutorial"
+
+class InputDeviceTutorialLogger
+@Inject
+constructor(@InputDeviceTutorialLog private val buffer: LogBuffer) {
+
+    fun log(@CompileTimeConstant s: String) {
+        buffer.log(TAG, LogLevel.INFO, message = s)
+    }
+
+    fun logGoingToScreen(screen: Screen, context: TutorialContext) {
+        buffer.log(
+            TAG,
+            LogLevel.INFO,
+            {
+                str1 = screen.toString()
+                str2 = context.string
+            },
+            { "Emitting new screen $str1 in $str2" }
+        )
+    }
+
+    fun logCloseTutorial(context: TutorialContext) {
+        buffer.log(TAG, LogLevel.INFO, { str1 = context.string }, { "Closing $str1" })
+    }
+
+    enum class TutorialContext(val string: String) {
+        KEYBOARD_TOUCHPAD_TUTORIAL("keyboard touchpad tutorial"),
+        TOUCHPAD_TUTORIAL("touchpad tutorial"),
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
index e8e1dd4..7ecacdc 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialCoreStartable.kt
@@ -18,20 +18,20 @@
 
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
 import com.android.systemui.shared.Flags.newTouchpadGesturesTutorial
 import dagger.Lazy
 import javax.inject.Inject
 
-/** A [CoreStartable] to launch a scheduler for keyboard and touchpad education */
+/** A [CoreStartable] to launch a scheduler for keyboard and touchpad tutorial notification */
 @SysUISingleton
 class KeyboardTouchpadTutorialCoreStartable
 @Inject
-constructor(private val tutorialSchedulerInteractor: Lazy<TutorialSchedulerInteractor>) :
+constructor(private val tutorialNotificationCoordinator: Lazy<TutorialNotificationCoordinator>) :
     CoreStartable {
     override fun start() {
         if (newTouchpadGesturesTutorial()) {
-            tutorialSchedulerInteractor.get().start()
+            tutorialNotificationCoordinator.get().start()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialModule.kt
new file mode 100644
index 0000000..8e6cb07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/KeyboardTouchpadTutorialModule.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.inputdevice.tutorial
+
+import android.app.Activity
+import com.android.systemui.CoreStartable
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import dagger.Binds
+import dagger.BindsOptionalOf
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+interface KeyboardTouchpadTutorialModule {
+
+    @Binds
+    @IntoMap
+    @ClassKey(KeyboardTouchpadTutorialCoreStartable::class)
+    fun bindKeyboardTouchpadTutorialCoreStartable(
+        listener: KeyboardTouchpadTutorialCoreStartable
+    ): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(KeyboardTouchpadTutorialActivity::class)
+    fun activity(impl: KeyboardTouchpadTutorialActivity): Activity
+
+    // TouchpadModule dependencies below
+    // all should be optional to not introduce touchpad dependency in all sysui variants
+
+    @BindsOptionalOf fun touchpadScreensProvider(): TouchpadTutorialScreensProvider
+
+    @BindsOptionalOf fun touchpadGesturesInteractor(): TouchpadGesturesInteractor
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt
new file mode 100644
index 0000000..bd3e771
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/TouchpadTutorialScreensProvider.kt
@@ -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 com.android.systemui.inputdevice.tutorial
+
+import androidx.compose.runtime.Composable
+
+interface TouchpadTutorialScreensProvider {
+
+    @Composable fun BackGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit)
+
+    @Composable fun HomeGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
index cfe64e2..1dbe83a 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt
@@ -16,12 +16,23 @@
 
 package com.android.systemui.inputdevice.tutorial.data.model
 
-data class TutorialSchedulerInfo(
-    val keyboard: DeviceSchedulerInfo = DeviceSchedulerInfo(),
-    val touchpad: DeviceSchedulerInfo = DeviceSchedulerInfo()
-)
+import java.time.Instant
 
-data class DeviceSchedulerInfo(var isLaunched: Boolean = false, var connectTime: Long? = null) {
+data class DeviceSchedulerInfo(
+    var launchTime: Instant? = null,
+    var firstConnectionTime: Instant? = null
+) {
+    constructor(
+        launchTimeSec: Long?,
+        firstConnectionTimeSec: Long?
+    ) : this(
+        launchTimeSec?.let { Instant.ofEpochSecond(it) },
+        firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) }
+    )
+
     val wasEverConnected: Boolean
-        get() = connectTime != null
+        get() = firstConnectionTime != null
+
+    val isLaunched: Boolean
+        get() = launchTime != null
 }
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
index 31ff018..d8d4bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/repository/TutorialSchedulerRepository.kt
@@ -17,63 +17,87 @@
 package com.android.systemui.inputdevice.tutorial.data.repository
 
 import android.content.Context
+import androidx.annotation.VisibleForTesting
 import androidx.datastore.core.DataStore
 import androidx.datastore.preferences.core.Preferences
-import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.edit
 import androidx.datastore.preferences.core.longPreferencesKey
 import androidx.datastore.preferences.preferencesDataStore
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
-import com.android.systemui.inputdevice.tutorial.data.model.TutorialSchedulerInfo
+import java.time.Instant
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 
 @SysUISingleton
-class TutorialSchedulerRepository
-@Inject
-constructor(@Application private val applicationContext: Context) {
+class TutorialSchedulerRepository(
+    private val applicationContext: Context,
+    backgroundScope: CoroutineScope,
+    dataStoreName: String
+) {
+    @Inject
+    constructor(
+        @Application applicationContext: Context,
+        @Background backgroundScope: CoroutineScope
+    ) : this(applicationContext, backgroundScope, dataStoreName = DATASTORE_NAME)
 
     private val Context.dataStore: DataStore<Preferences> by
-        preferencesDataStore(name = DATASTORE_NAME)
+        preferencesDataStore(name = dataStoreName, scope = backgroundScope)
 
-    suspend fun loadData(): TutorialSchedulerInfo {
+    suspend fun isLaunched(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isLaunched
+
+    suspend fun launchTime(deviceType: DeviceType): Instant? = loadData()[deviceType]!!.launchTime
+
+    suspend fun wasEverConnected(deviceType: DeviceType): Boolean =
+        loadData()[deviceType]!!.wasEverConnected
+
+    suspend fun firstConnectionTime(deviceType: DeviceType): Instant? =
+        loadData()[deviceType]!!.firstConnectionTime
+
+    private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> {
         return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
     }
 
-    suspend fun updateConnectTime(device: DeviceType, time: Long) {
-        applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time }
+    suspend fun updateFirstConnectionTime(device: DeviceType, time: Instant) {
+        applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time.epochSecond }
     }
 
-    suspend fun updateLaunch(device: DeviceType) {
-        applicationContext.dataStore.edit { pref -> pref[getLaunchedKey(device)] = true }
+    suspend fun updateLaunchTime(device: DeviceType, time: Instant) {
+        applicationContext.dataStore.edit { pref -> pref[getLaunchKey(device)] = time.epochSecond }
     }
 
-    private fun getSchedulerInfo(pref: Preferences): TutorialSchedulerInfo {
-        return TutorialSchedulerInfo(
-            keyboard = getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
-            touchpad = getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
+    private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
+        return mapOf(
+            DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
+            DeviceType.TOUCHPAD to getDeviceSchedulerInfo(pref, DeviceType.TOUCHPAD)
         )
     }
 
     private fun getDeviceSchedulerInfo(pref: Preferences, device: DeviceType): DeviceSchedulerInfo {
-        val isLaunched = pref[getLaunchedKey(device)] ?: false
-        val connectionTime = pref[getConnectKey(device)] ?: null
-        return DeviceSchedulerInfo(isLaunched, connectionTime)
+        val launchTime = pref[getLaunchKey(device)]
+        val connectionTime = pref[getConnectKey(device)]
+        return DeviceSchedulerInfo(launchTime, connectionTime)
     }
 
-    private fun getLaunchedKey(device: DeviceType) =
-        booleanPreferencesKey(device.name + IS_LAUNCHED_SUFFIX)
+    private fun getLaunchKey(device: DeviceType) =
+        longPreferencesKey(device.name + LAUNCH_TIME_SUFFIX)
 
     private fun getConnectKey(device: DeviceType) =
         longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
 
+    @VisibleForTesting
+    suspend fun clearDataStore() {
+        applicationContext.dataStore.edit { it.clear() }
+    }
+
     companion object {
         const val DATASTORE_NAME = "TutorialScheduler"
-        const val IS_LAUNCHED_SUFFIX = "_IS_LAUNCHED"
-        const val CONNECT_TIME_SUFFIX = "_CONNECTED_TIME"
+        const val LAUNCH_TIME_SUFFIX = "_LAUNCH_TIME"
+        const val CONNECT_TIME_SUFFIX = "_CONNECT_TIME"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.kt
new file mode 100644
index 0000000..3f1f68a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/KeyboardTouchpadConnectionInteractor.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.inputdevice.tutorial.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+@SysUISingleton
+class KeyboardTouchpadConnectionInteractor
+@Inject
+constructor(
+    keyboardRepository: KeyboardRepository,
+    touchpadRepository: TouchpadRepository,
+) {
+
+    val connectionState: Flow<ConnectionState> =
+        combine(
+            keyboardRepository.isAnyKeyboardConnected,
+            touchpadRepository.isAnyTouchpadConnected
+        ) { keyboardConnected, touchpadConnected ->
+            ConnectionState(keyboardConnected, touchpadConnected)
+        }
+}
+
+data class ConnectionState(val keyboardConnected: Boolean, val touchpadConnected: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
index 05e1044..cfc913f 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt
@@ -16,98 +16,118 @@
 
 package com.android.systemui.inputdevice.tutorial.domain.interactor
 
-import android.content.Context
-import android.content.Intent
+import android.os.SystemProperties
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.inputdevice.tutorial.data.model.DeviceSchedulerInfo
 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.keyboard.data.repository.KeyboardRepository
 import com.android.systemui.touchpad.data.repository.TouchpadRepository
 import java.time.Duration
 import java.time.Instant
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.toKotlinDuration
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
 
 /**
- * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], then launch the
- * tutorial as soon as there's a connected device
+ * When the first time a keyboard or touchpad is connected, wait for [LAUNCH_DELAY], and as soon as
+ * there's a connected device, show a notification to launch the tutorial.
  */
 @SysUISingleton
 class TutorialSchedulerInteractor
 @Inject
 constructor(
-    @Application private val context: Context,
-    @Application private val applicationScope: CoroutineScope,
-    private val keyboardRepository: KeyboardRepository,
-    private val touchpadRepository: TouchpadRepository,
-    private val tutorialSchedulerRepository: TutorialSchedulerRepository
+    keyboardRepository: KeyboardRepository,
+    touchpadRepository: TouchpadRepository,
+    private val repo: TutorialSchedulerRepository
 ) {
-    fun start() {
-        applicationScope.launch {
-            val info = tutorialSchedulerRepository.loadData()
-            if (!info.keyboard.isLaunched) {
-                applicationScope.launch {
-                    schedule(
-                        keyboardRepository.isAnyKeyboardConnected,
-                        info.keyboard,
-                        DeviceType.KEYBOARD
-                    )
-                }
-            }
-            if (!info.touchpad.isLaunched) {
-                applicationScope.launch {
-                    schedule(
-                        touchpadRepository.isAnyTouchpadConnected,
-                        info.touchpad,
-                        DeviceType.TOUCHPAD
-                    )
-                }
-            }
+    private val isAnyDeviceConnected =
+        mapOf(
+            KEYBOARD to keyboardRepository.isAnyKeyboardConnected,
+            TOUCHPAD to touchpadRepository.isAnyTouchpadConnected
+        )
+
+    private val touchpadScheduleFlow = flow {
+        if (!repo.isLaunched(TOUCHPAD)) {
+            schedule(TOUCHPAD)
+            emit(TOUCHPAD)
         }
     }
 
-    private suspend fun schedule(
-        isAnyDeviceConnected: Flow<Boolean>,
-        info: DeviceSchedulerInfo,
-        deviceType: DeviceType
-    ) {
-        if (!info.wasEverConnected) {
-            waitForDeviceConnection(isAnyDeviceConnected)
-            info.connectTime = Instant.now().toEpochMilli()
-            tutorialSchedulerRepository.updateConnectTime(deviceType, info.connectTime!!)
+    private val keyboardScheduleFlow = flow {
+        if (!repo.isLaunched(KEYBOARD)) {
+            schedule(KEYBOARD)
+            emit(KEYBOARD)
         }
-        delay(remainingTimeMillis(info.connectTime!!))
-        waitForDeviceConnection(isAnyDeviceConnected)
-        info.isLaunched = true
-        tutorialSchedulerRepository.updateLaunch(deviceType)
-        launchTutorial()
     }
 
-    private suspend fun waitForDeviceConnection(isAnyDeviceConnected: Flow<Boolean>): Boolean {
-        return isAnyDeviceConnected.filter { it }.first()
+    private suspend fun schedule(deviceType: DeviceType) {
+        if (!repo.wasEverConnected(deviceType)) {
+            waitForDeviceConnection(deviceType)
+            repo.updateFirstConnectionTime(deviceType, Instant.now())
+        }
+        delay(remainingTime(start = repo.firstConnectionTime(deviceType)!!))
+        waitForDeviceConnection(deviceType)
     }
 
-    private fun launchTutorial() {
-        val intent = Intent(TUTORIAL_ACTION)
-        intent.addCategory(Intent.CATEGORY_DEFAULT)
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-        context.startActivity(intent)
+    private suspend fun waitForDeviceConnection(deviceType: DeviceType) =
+        isAnyDeviceConnected[deviceType]!!.filter { it }.first()
+
+    // Merging two flows ensures that tutorial is launched consecutively to avoid race condition
+    val tutorials: Flow<TutorialType> =
+        merge(touchpadScheduleFlow, keyboardScheduleFlow).map {
+            val tutorialType = resolveTutorialType(it)
+
+            // TODO: notifying time is not oobe launching time - move these updates into oobe
+            if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
+                repo.updateLaunchTime(KEYBOARD, Instant.now())
+            if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
+                repo.updateLaunchTime(TOUCHPAD, Instant.now())
+
+            tutorialType
+        }
+
+    private suspend fun resolveTutorialType(deviceType: DeviceType): TutorialType {
+        // Resolve the type of tutorial depending on which device are connected when the tutorial is
+        // launched. E.g. when the keyboard is connected for [LAUNCH_DELAY], both keyboard and
+        // touchpad are connected, we launch the tutorial for both.
+        if (repo.isLaunched(deviceType)) return TutorialType.NONE
+        val otherDevice = if (deviceType == KEYBOARD) TOUCHPAD else KEYBOARD
+        val isOtherDeviceConnected = isAnyDeviceConnected[otherDevice]!!.first()
+        if (!repo.isLaunched(otherDevice) && isOtherDeviceConnected) return TutorialType.BOTH
+        return if (deviceType == KEYBOARD) TutorialType.KEYBOARD else TutorialType.TOUCHPAD
     }
 
-    private fun remainingTimeMillis(start: Long): Long {
-        val elapsed = Instant.now().toEpochMilli() - start
-        return LAUNCH_DELAY - elapsed
+    private fun remainingTime(start: Instant): kotlin.time.Duration {
+        val elapsed = Duration.between(start, Instant.now())
+        return LAUNCH_DELAY.minus(elapsed).toKotlinDuration()
     }
 
     companion object {
-        const val TUTORIAL_ACTION = "com.android.systemui.action.TOUCHPAD_TUTORIAL"
-        private val LAUNCH_DELAY = Duration.ofHours(72).toMillis()
+        const val TAG = "TutorialSchedulerInteractor"
+        private val DEFAULT_LAUNCH_DELAY_SEC = 72.hours.inWholeSeconds
+        private val LAUNCH_DELAY: Duration
+            get() =
+                Duration.ofSeconds(
+                    SystemProperties.getLong(
+                        "persist.peripheral_tutorial_delay_sec",
+                        DEFAULT_LAUNCH_DELAY_SEC
+                    )
+                )
+    }
+
+    enum class TutorialType {
+        KEYBOARD,
+        TOUCHPAD,
+        BOTH,
+        NONE
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
new file mode 100644
index 0000000..5d9dda3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt
@@ -0,0 +1,146 @@
+/*
+ * 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
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.core.app.NotificationCompat
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.TAG
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_BOTH
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/** When the scheduler is due, show a notification to launch tutorial */
+@SysUISingleton
+class TutorialNotificationCoordinator
+@Inject
+constructor(
+    @Background private val backgroundScope: CoroutineScope,
+    @Application private val context: Context,
+    private val tutorialSchedulerInteractor: TutorialSchedulerInteractor,
+    private val notificationManager: NotificationManager
+) {
+    fun start() {
+        backgroundScope.launch {
+            tutorialSchedulerInteractor.tutorials.collect { showNotification(it) }
+        }
+    }
+
+    // By sharing the same tag and id, we update the content of existing notification instead of
+    // creating multiple notifications
+    private fun showNotification(tutorialType: TutorialType) {
+        if (tutorialType == TutorialType.NONE) return
+
+        if (notificationManager.getNotificationChannel(CHANNEL_ID) == null)
+            createNotificationChannel()
+
+        // Replace "System UI" app name with "Android System"
+        val extras = Bundle()
+        extras.putString(
+            Notification.EXTRA_SUBSTITUTE_APP_NAME,
+            context.getString(com.android.internal.R.string.android_system_label)
+        )
+
+        val info = getNotificationInfo(tutorialType)!!
+        val notification =
+            NotificationCompat.Builder(context, CHANNEL_ID)
+                .setSmallIcon(R.drawable.ic_settings)
+                .setContentTitle(info.title)
+                .setContentText(info.text)
+                .setContentIntent(createPendingIntent(info.type))
+                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+                .setAutoCancel(true)
+                .addExtras(extras)
+                .build()
+
+        notificationManager.notify(TAG, NOTIFICATION_ID, notification)
+    }
+
+    private fun createNotificationChannel() {
+        val channel =
+            NotificationChannel(
+                CHANNEL_ID,
+                context.getString(com.android.internal.R.string.android_system_label),
+                NotificationManager.IMPORTANCE_DEFAULT
+            )
+        notificationManager.createNotificationChannel(channel)
+    }
+
+    private fun createPendingIntent(tutorialType: String): PendingIntent {
+        val intent =
+            Intent(context, KeyboardTouchpadTutorialActivity::class.java).apply {
+                putExtra(INTENT_TUTORIAL_TYPE_KEY, tutorialType)
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            }
+        return PendingIntent.getActivity(
+            context,
+            /* requestCode= */ 0,
+            intent,
+            PendingIntent.FLAG_IMMUTABLE
+        )
+    }
+
+    private data class NotificationInfo(val title: String, val text: String, val type: String)
+
+    private fun getNotificationInfo(tutorialType: TutorialType): NotificationInfo? =
+        when (tutorialType) {
+            TutorialType.KEYBOARD ->
+                NotificationInfo(
+                    context.getString(R.string.launch_keyboard_tutorial_notification_title),
+                    context.getString(R.string.launch_keyboard_tutorial_notification_content),
+                    INTENT_TUTORIAL_TYPE_KEYBOARD
+                )
+            TutorialType.TOUCHPAD ->
+                NotificationInfo(
+                    context.getString(R.string.launch_touchpad_tutorial_notification_title),
+                    context.getString(R.string.launch_touchpad_tutorial_notification_content),
+                    INTENT_TUTORIAL_TYPE_TOUCHPAD
+                )
+            TutorialType.BOTH ->
+                NotificationInfo(
+                    context.getString(
+                        R.string.launch_keyboard_touchpad_tutorial_notification_title
+                    ),
+                    context.getString(
+                        R.string.launch_keyboard_touchpad_tutorial_notification_content
+                    ),
+                    INTENT_TUTORIAL_TYPE_BOTH
+                )
+            TutorialType.NONE -> null
+        }
+
+    companion object {
+        private const val CHANNEL_ID = "TutorialSchedulerNotificationChannel"
+        private const val NOTIFICATION_ID = 5566
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.kt
new file mode 100644
index 0000000..1aa5ee0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionKeyTutorialScreen.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.inputdevice.tutorial.ui.composable
+
+import androidx.activity.compose.BackHandler
+import androidx.compose.foundation.focusable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
+import com.airbnb.lottie.compose.rememberLottieDynamicProperties
+import com.android.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+import com.android.systemui.res.R
+
+@Composable
+fun ActionKeyTutorialScreen(
+    onDoneButtonClicked: () -> Unit,
+    onBack: () -> Unit,
+) {
+    BackHandler(onBack = onBack)
+    val screenConfig = buildScreenConfig()
+    var actionState by remember { mutableStateOf(NOT_STARTED) }
+    val focusRequester = remember { FocusRequester() }
+    Box(
+        modifier =
+            Modifier.fillMaxSize()
+                .onKeyEvent { keyEvent: KeyEvent ->
+                    if (keyEvent.key == Key.MetaLeft && keyEvent.type == KeyEventType.KeyUp) {
+                        actionState = FINISHED
+                    }
+                    true
+                }
+                .focusRequester(focusRequester)
+                .focusable()
+    ) {
+        ActionTutorialContent(actionState, onDoneButtonClicked, screenConfig)
+    }
+    LaunchedEffect(Unit) {
+        // we need to request focus on main container so it can handle all key events immediately
+        // when it's open. Otherwise user needs to press non-modifier key before modifier key can
+        // be handled as nothing is focused
+        focusRequester.requestFocus()
+    }
+}
+
+@Composable
+private fun buildScreenConfig() =
+    TutorialScreenConfig(
+        colors = rememberScreenColors(),
+        strings =
+            TutorialScreenConfig.Strings(
+                titleResId = R.string.tutorial_action_key_title,
+                bodyResId = R.string.tutorial_action_key_guidance,
+                titleSuccessResId = R.string.tutorial_action_key_success_title,
+                bodySuccessResId = R.string.tutorial_action_key_success_body
+            ),
+        animations =
+            TutorialScreenConfig.Animations(
+                educationResId = R.raw.action_key_edu,
+                successResId = R.raw.action_key_success
+            )
+    )
+
+@Composable
+private fun rememberScreenColors(): TutorialScreenConfig.Colors {
+    val primaryFixedDim = LocalAndroidColorScheme.current.primaryFixedDim
+    val secondaryFixedDim = LocalAndroidColorScheme.current.secondaryFixedDim
+    val onSecondaryFixed = LocalAndroidColorScheme.current.onSecondaryFixed
+    val onSecondaryFixedVariant = LocalAndroidColorScheme.current.onSecondaryFixedVariant
+    val dynamicProperties =
+        rememberLottieDynamicProperties(
+            rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
+            rememberColorFilterProperty(".secondaryFixedDim", secondaryFixedDim),
+            rememberColorFilterProperty(".onSecondaryFixed", onSecondaryFixed),
+            rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant)
+        )
+    val screenColors =
+        remember(dynamicProperties) {
+            TutorialScreenConfig.Colors(
+                background = onSecondaryFixed,
+                title = secondaryFixedDim,
+                animationColors = dynamicProperties,
+            )
+        }
+    return screenColors
+}
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
new file mode 100644
index 0000000..b271356
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/ActionTutorialContent.kt
@@ -0,0 +1,229 @@
+/*
+ * 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.annotation.RawRes
+import androidx.annotation.StringRes
+import androidx.compose.animation.AnimatedContent
+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
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+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.compose.modifiers.background
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.FINISHED
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.IN_PROGRESS
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NOT_STARTED
+
+enum class TutorialActionState {
+    NOT_STARTED,
+    IN_PROGRESS,
+    FINISHED
+}
+
+@Composable
+fun ActionTutorialContent(
+    actionState: TutorialActionState,
+    onDoneButtonClicked: () -> Unit,
+    config: TutorialScreenConfig
+) {
+    Column(
+        verticalArrangement = Arrangement.Center,
+        modifier =
+            Modifier.fillMaxSize()
+                .background(config.colors.background)
+                .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
+    ) {
+        Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
+            TutorialDescription(
+                titleTextId =
+                    if (actionState == FINISHED) config.strings.titleSuccessResId
+                    else config.strings.titleResId,
+                titleColor = config.colors.title,
+                bodyTextId =
+                    if (actionState == FINISHED) config.strings.bodySuccessResId
+                    else config.strings.bodyResId,
+                modifier = Modifier.weight(1f)
+            )
+            Spacer(modifier = Modifier.width(76.dp))
+            TutorialAnimation(
+                actionState,
+                config,
+                modifier = Modifier.weight(1f).padding(top = 8.dp)
+            )
+        }
+        DoneButton(onDoneButtonClicked = onDoneButtonClicked)
+    }
+}
+
+@Composable
+fun TutorialDescription(
+    @StringRes titleTextId: Int,
+    titleColor: Color,
+    @StringRes bodyTextId: Int,
+    modifier: Modifier = Modifier
+) {
+    Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
+        Text(
+            text = stringResource(id = titleTextId),
+            style = MaterialTheme.typography.displayLarge,
+            color = titleColor
+        )
+        Spacer(modifier = Modifier.height(16.dp))
+        Text(
+            text = stringResource(id = bodyTextId),
+            style = MaterialTheme.typography.bodyLarge,
+            color = Color.White
+        )
+    }
+}
+
+@Composable
+fun TutorialAnimation(
+    actionState: TutorialActionState,
+    config: TutorialScreenConfig,
+    modifier: Modifier = Modifier
+) {
+    Box(modifier = modifier.fillMaxWidth()) {
+        AnimatedContent(
+            targetState = actionState,
+            transitionSpec = {
+                if (initialState == NOT_STARTED) {
+                    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) {
+                NOT_STARTED ->
+                    EducationAnimation(
+                        config.animations.educationResId,
+                        config.colors.animationColors
+                    )
+                IN_PROGRESS ->
+                    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/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.kt
new file mode 100644
index 0000000..01ad585
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialComponents.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 androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import com.android.systemui.res.R
+
+@Composable
+fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
+    Row(
+        horizontalArrangement = Arrangement.End,
+        verticalAlignment = Alignment.CenterVertically,
+        modifier = modifier.fillMaxWidth()
+    ) {
+        Button(onClick = onDoneButtonClicked) {
+            Text(stringResource(R.string.touchpad_tutorial_done_button))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.kt
new file mode 100644
index 0000000..55e5f2d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/composable/TutorialScreenConfig.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.inputdevice.tutorial.ui.composable
+
+import androidx.annotation.RawRes
+import androidx.annotation.StringRes
+import androidx.compose.ui.graphics.Color
+import com.airbnb.lottie.compose.LottieDynamicProperties
+
+data class TutorialScreenConfig(
+    val colors: Colors,
+    val strings: Strings,
+    val animations: Animations
+) {
+
+    data class Colors(
+        val background: Color,
+        val title: Color,
+        val animationColors: LottieDynamicProperties
+    )
+
+    data class Strings(
+        @StringRes val titleResId: Int,
+        @StringRes val bodyResId: Int,
+        @StringRes val titleSuccessResId: Int,
+        @StringRes val bodySuccessResId: Int,
+    )
+
+    data class Animations(
+        @RawRes val educationResId: Int,
+        @RawRes val successResId: Int,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt
new file mode 100644
index 0000000..1adc285
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.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.inputdevice.tutorial.ui.view
+
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.Lifecycle.State.STARTED
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.KeyboardTouchpadTutorialViewModel.Factory.ViewModelFactoryAssistedProvider
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
+import java.util.Optional
+import javax.inject.Inject
+import kotlinx.coroutines.launch
+
+/**
+ * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that
+ * either of them are actually not connected when this is launched
+ */
+class KeyboardTouchpadTutorialActivity
+@Inject
+constructor(
+    private val viewModelFactoryAssistedProvider: ViewModelFactoryAssistedProvider,
+    private val touchpadTutorialScreensProvider: Optional<TouchpadTutorialScreensProvider>,
+) : ComponentActivity() {
+
+    companion object {
+        const val INTENT_TUTORIAL_TYPE_KEY = "tutorial_type"
+        const val INTENT_TUTORIAL_TYPE_TOUCHPAD = "touchpad"
+        const val INTENT_TUTORIAL_TYPE_KEYBOARD = "keyboard"
+        const val INTENT_TUTORIAL_TYPE_BOTH = "both"
+    }
+
+    private val vm by
+        viewModels<KeyboardTouchpadTutorialViewModel>(
+            factoryProducer = {
+                viewModelFactoryAssistedProvider.create(touchpadTutorialScreensProvider.isPresent)
+            }
+        )
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        enableEdgeToEdge()
+        // required to handle 3+ fingers on touchpad
+        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
+        lifecycle.addObserver(vm)
+        lifecycleScope.launch {
+            vm.closeActivity.collect { finish ->
+                if (finish) {
+                    finish()
+                }
+            }
+        }
+        setContent {
+            PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) }
+        }
+    }
+}
+
+@Composable
+fun KeyboardTouchpadTutorialContainer(
+    vm: KeyboardTouchpadTutorialViewModel,
+    touchpadScreens: Optional<TouchpadTutorialScreensProvider>,
+) {
+    val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED)
+    when (activeScreen) {
+        BACK_GESTURE ->
+            touchpadScreens
+                .get()
+                .BackGesture(onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack)
+        HOME_GESTURE ->
+            touchpadScreens
+                .get()
+                .HomeGesture(onDoneButtonClicked = vm::onDoneButtonClicked, onBack = vm::onBack)
+        ACTION_KEY ->
+            ActionKeyTutorialScreen(
+                onDoneButtonClicked = vm::onDoneButtonClicked,
+                onBack = vm::onBack
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
new file mode 100644
index 0000000..315c102
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModel.kt
@@ -0,0 +1,205 @@
+/*
+ * 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.viewmodel
+
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.systemui.inputdevice.tutorial.domain.interactor.ConnectionState
+import com.android.systemui.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.RequiredHardware.TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.Optional
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.runningFold
+import kotlinx.coroutines.launch
+
+class KeyboardTouchpadTutorialViewModel(
+    private val gesturesInteractor: Optional<TouchpadGesturesInteractor>,
+    private val keyboardTouchpadConnectionInteractor: KeyboardTouchpadConnectionInteractor,
+    private val hasTouchpadTutorialScreens: Boolean,
+    handle: SavedStateHandle
+) : ViewModel(), DefaultLifecycleObserver {
+
+    private fun startingScreen(handle: SavedStateHandle): Screen {
+        val tutorialType: String? = handle[INTENT_TUTORIAL_TYPE_KEY]
+        return if (tutorialType == INTENT_TUTORIAL_TYPE_KEYBOARD) ACTION_KEY else BACK_GESTURE
+    }
+
+    private val _screen = MutableStateFlow(startingScreen(handle))
+    val screen: Flow<Screen> = _screen.filter { it.canBeShown() }
+
+    private val _closeActivity: MutableStateFlow<Boolean> = MutableStateFlow(false)
+    val closeActivity: StateFlow<Boolean> = _closeActivity
+
+    private val screensBackStack = ArrayDeque(listOf(_screen.value))
+
+    private var connectionState: ConnectionState =
+        ConnectionState(keyboardConnected = false, touchpadConnected = false)
+
+    init {
+        viewModelScope.launch {
+            keyboardTouchpadConnectionInteractor.connectionState.collect { connectionState = it }
+        }
+
+        viewModelScope.launch {
+            screen
+                .runningFold<Screen, Pair<Screen?, Screen?>>(null to null) {
+                    previousScreensPair,
+                    currentScreen ->
+                    previousScreensPair.second to currentScreen
+                }
+                .collect { (previousScreen, currentScreen) ->
+                    // ignore first empty emission
+                    if (currentScreen != null) {
+                        setupDeviceState(previousScreen, currentScreen)
+                    }
+                }
+        }
+
+        viewModelScope.launch {
+            // close activity if screen requires touchpad but we don't have it. This can only happen
+            // when current sysui build doesn't contain touchpad module dependency
+            _screen.filterNot { it.canBeShown() }.collect { _closeActivity.value = true }
+        }
+    }
+
+    override fun onCleared() {
+        // this shouldn't be needed as onTutorialInvisible should already clear device state but
+        // it'd be really bad if we'd block gestures/shortcuts after leaving tutorial so just to be
+        // extra sure...
+        clearDeviceStateForScreen(_screen.value)
+    }
+
+    override fun onStart(owner: LifecycleOwner) {
+        setupDeviceState(previousScreen = null, currentScreen = _screen.value)
+    }
+
+    override fun onStop(owner: LifecycleOwner) {
+        clearDeviceStateForScreen(_screen.value)
+    }
+
+    fun onDoneButtonClicked() {
+        var nextScreen = _screen.value.next()
+        while (nextScreen != null) {
+            if (requiredHardwarePresent(nextScreen)) {
+                break
+            }
+            nextScreen = nextScreen.next()
+        }
+        if (nextScreen == null) {
+            _closeActivity.value = true
+        } else {
+            _screen.value = nextScreen
+            screensBackStack.add(nextScreen)
+        }
+    }
+
+    private fun Screen.canBeShown() = requiredHardware != TOUCHPAD || hasTouchpadTutorialScreens
+
+    private fun setupDeviceState(previousScreen: Screen?, currentScreen: Screen) {
+        if (previousScreen?.requiredHardware == currentScreen.requiredHardware) return
+        previousScreen?.let { clearDeviceStateForScreen(it) }
+        when (currentScreen.requiredHardware) {
+            TOUCHPAD -> gesturesInteractor.get().disableGestures()
+            KEYBOARD -> {} // TODO(b/358587037) disabled keyboard shortcuts
+        }
+    }
+
+    private fun clearDeviceStateForScreen(screen: Screen) {
+        when (screen.requiredHardware) {
+            TOUCHPAD -> gesturesInteractor.get().enableGestures()
+            KEYBOARD -> {} // TODO(b/358587037) enable keyboard shortcuts
+        }
+    }
+
+    private fun requiredHardwarePresent(screen: Screen): Boolean =
+        when (screen.requiredHardware) {
+            KEYBOARD -> connectionState.keyboardConnected
+            TOUCHPAD -> connectionState.touchpadConnected
+        }
+
+    fun onBack() {
+        if (screensBackStack.size <= 1) {
+            _closeActivity.value = true
+        } else {
+            screensBackStack.removeLast()
+            _screen.value = screensBackStack.last()
+        }
+    }
+
+    class Factory
+    @AssistedInject
+    constructor(
+        private val gesturesInteractor: Optional<TouchpadGesturesInteractor>,
+        private val keyboardTouchpadConnected: KeyboardTouchpadConnectionInteractor,
+        @Assisted private val hasTouchpadTutorialScreens: Boolean,
+    ) : AbstractSavedStateViewModelFactory() {
+
+        @AssistedFactory
+        fun interface ViewModelFactoryAssistedProvider {
+            fun create(@Assisted hasTouchpadTutorialScreens: Boolean): Factory
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        override fun <T : ViewModel> create(
+            key: String,
+            modelClass: Class<T>,
+            handle: SavedStateHandle
+        ): T =
+            KeyboardTouchpadTutorialViewModel(
+                gesturesInteractor,
+                keyboardTouchpadConnected,
+                hasTouchpadTutorialScreens,
+                handle
+            )
+                as T
+    }
+}
+
+enum class RequiredHardware {
+    TOUCHPAD,
+    KEYBOARD
+}
+
+enum class Screen(val requiredHardware: RequiredHardware) {
+    BACK_GESTURE(requiredHardware = TOUCHPAD),
+    HOME_GESTURE(requiredHardware = TOUCHPAD),
+    ACTION_KEY(requiredHardware = KEYBOARD);
+
+    fun next(): Screen? =
+        when (this) {
+            BACK_GESTURE -> HOME_GESTURE
+            HOME_GESTURE -> ACTION_KEY
+            ACTION_KEY -> null
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index b654307..a20dfa5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.inputdevice.data.repository.InputDeviceRepository
 import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceAdded
-import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceChange
 import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.DeviceRemoved
 import com.android.systemui.inputdevice.data.repository.InputDeviceRepository.FreshStart
 import com.android.systemui.keyboard.data.model.Keyboard
@@ -78,24 +77,16 @@
     inputDeviceRepository: InputDeviceRepository
 ) : KeyboardRepository {
 
-    private val keyboardsChange: Flow<Pair<Collection<Int>, DeviceChange>> =
-        inputDeviceRepository.deviceChange
-            .map { (ids, change) -> ids.filter { id -> isPhysicalFullKeyboard(id) } to change }
-            .filter { (_, change) ->
-                when (change) {
-                    FreshStart -> true
-                    is DeviceAdded -> isPhysicalFullKeyboard(change.deviceId)
-                    is DeviceRemoved -> isPhysicalFullKeyboard(change.deviceId)
-                }
-            }
-
     @FlowPreview
     override val newlyConnectedKeyboard: Flow<Keyboard> =
-        keyboardsChange
+        inputDeviceRepository.deviceChange
             .flatMapConcat { (devices, operation) ->
                 when (operation) {
-                    FreshStart -> devices.asFlow()
-                    is DeviceAdded -> flowOf(operation.deviceId)
+                    FreshStart -> devices.filter { id -> isPhysicalFullKeyboard(id) }.asFlow()
+                    is DeviceAdded -> {
+                        if (isPhysicalFullKeyboard(operation.deviceId)) flowOf(operation.deviceId)
+                        else emptyFlow()
+                    }
                     is DeviceRemoved -> emptyFlow()
                 }
             }
@@ -103,8 +94,8 @@
             .flowOn(backgroundDispatcher)
 
     override val isAnyKeyboardConnected: Flow<Boolean> =
-        keyboardsChange
-            .map { (devices, _) -> devices.isNotEmpty() }
+        inputDeviceRepository.deviceChange
+            .map { (ids, _) -> ids.any { id -> isPhysicalFullKeyboard(id) } }
             .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 19b46e3..04aa04d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -63,7 +63,11 @@
                 if (categories.isEmpty()) {
                     ShortcutsUiState.Inactive
                 } else {
-                    val filteredCategories = filterCategoriesBySearchQuery(query, categories)
+                    /* temporarily hiding launcher shortcut categories until b/327141011
+                     * is completed. */
+                    val categoriesWithLauncherExcluded = excludeLauncherApp(categories)
+                    val filteredCategories =
+                        filterCategoriesBySearchQuery(query, categoriesWithLauncherExcluded)
                     ShortcutsUiState.Active(
                         searchQuery = query,
                         shortcutCategories = filteredCategories,
@@ -77,15 +81,27 @@
                 initialValue = ShortcutsUiState.Inactive
             )
 
+    private suspend fun excludeLauncherApp(
+        categories: List<ShortcutCategory>
+    ): List<ShortcutCategory> {
+        val launcherAppCategory =
+            categories.firstOrNull { it.type is CurrentApp && isLauncherApp(it.type.packageName) }
+        return if (launcherAppCategory != null) {
+            categories - launcherAppCategory
+        } else {
+            categories
+        }
+    }
+
     private suspend fun getDefaultSelectedCategory(
         categories: List<ShortcutCategory>
     ): ShortcutCategoryType? {
         val currentAppShortcuts =
-            categories.firstOrNull { it.type is CurrentApp && !isAppLauncher(it.type.packageName) }
+            categories.firstOrNull { it.type is CurrentApp && !isLauncherApp(it.type.packageName) }
         return currentAppShortcuts?.type ?: categories.firstOrNull()?.type
     }
 
-    private suspend fun isAppLauncher(packageName: String): Boolean {
+    private suspend fun isLauncherApp(packageName: String): Boolean {
         return withContext(backgroundDispatcher) {
             roleManager
                 .getRoleHoldersAsUser(RoleManager.ROLE_HOME, userTracker.userHandle)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 871d046..1bc91ca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -35,8 +35,6 @@
 import static android.view.WindowManager.TransitionOldType;
 import static android.view.WindowManager.TransitionType;
 
-import static com.android.systemui.Flags.refactorGetCurrentUser;
-
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -78,7 +76,9 @@
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
@@ -123,6 +123,7 @@
     private final PowerInteractor mPowerInteractor;
     private final KeyguardInteractor mKeyguardInteractor;
     private final Lazy<SceneInteractor> mSceneInteractorLazy;
+    private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractorLazy;
     private final Executor mMainExecutor;
     private final Lazy<KeyguardStateCallbackStartable> mKeyguardStateCallbackStartableLazy;
 
@@ -319,6 +320,7 @@
     private final WindowManagerOcclusionManager mWmOcclusionManager;
     private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
     private final KeyguardWakeDirectlyToGoneInteractor mKeyguardWakeDirectlyToGoneInteractor;
+    private final KeyguardDismissInteractor mKeyguardDismissInteractor;
     private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
         @Override
         public FoldGracePeriodProvider get() {
@@ -346,7 +348,9 @@
             KeyguardInteractor keyguardInteractor,
             KeyguardEnabledInteractor keyguardEnabledInteractor,
             Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
-            KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor) {
+            KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
+            KeyguardDismissInteractor keyguardDismissInteractor,
+            Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy) {
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -359,6 +363,7 @@
         mSceneInteractorLazy = sceneInteractorLazy;
         mMainExecutor = mainExecutor;
         mKeyguardStateCallbackStartableLazy = keyguardStateCallbackStartableLazy;
+        mDeviceEntryInteractorLazy = deviceEntryInteractorLazy;
 
         if (KeyguardWmStateRefactor.isEnabled()) {
             WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -375,6 +380,7 @@
         mWmOcclusionManager = windowManagerOcclusionManager;
         mKeyguardEnabledInteractor = keyguardEnabledInteractor;
         mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
+        mKeyguardDismissInteractor = keyguardDismissInteractor;
     }
 
     @Override
@@ -482,7 +488,13 @@
         public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
             trace("dismiss message=" + message);
             checkPermission();
-            mKeyguardViewMediator.dismiss(callback, message);
+            if (SceneContainerFlag.isEnabled()) {
+                mDeviceEntryInteractorLazy.get().attemptDeviceEntry(callback);
+            } else if (KeyguardWmStateRefactor.isEnabled()) {
+                mKeyguardDismissInteractor.dismissKeyguardWithCallback(callback);
+            } else {
+                mKeyguardViewMediator.dismiss(callback, message);
+            }
         }
 
         @Override // Binder interface
@@ -672,9 +684,6 @@
         public void setCurrentUser(int userId) {
             trace("Deprecated/NOT USED: setCurrentUser userId=" + userId);
             checkPermission();
-            if (!refactorGetCurrentUser()) {
-                mKeyguardViewMediator.setCurrentUser(userId);
-            }
         }
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index ba533ce..362e016c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -72,6 +72,7 @@
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import com.google.android.msdl.domain.MSDLPlayer
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
@@ -112,6 +113,7 @@
     private val keyguardViewMediator: KeyguardViewMediator,
     private val deviceEntryUnlockTrackerViewBinder: Optional<DeviceEntryUnlockTrackerViewBinder>,
     @Main private val mainDispatcher: CoroutineDispatcher,
+    private val msdlPlayer: MSDLPlayer,
 ) : CoreStartable {
 
     private var rootViewHandle: DisposableHandle? = null
@@ -219,6 +221,7 @@
                 falsingManager,
                 keyguardViewMediator,
                 mainDispatcher,
+                msdlPlayer,
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3f9c98d..d38c952 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -41,7 +41,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
-import static com.android.systemui.Flags.refactorGetCurrentUser;
+import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
 import static com.android.systemui.Flags.translucentOccludingActivityFix;
 import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
 
@@ -477,6 +477,7 @@
     private boolean mUnlockingAndWakingFromDream = false;
     private boolean mHideAnimationRun = false;
     private boolean mHideAnimationRunning = false;
+    private boolean mIsKeyguardExitAnimationCanceled = false;
 
     private SoundPool mLockSounds;
     private int mLockSoundId;
@@ -624,11 +625,9 @@
 
         @Override
         public void onUserSwitching(int userId) {
-            if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
+            Log.d(TAG, String.format("onUserSwitching %d", userId));
             synchronized (KeyguardViewMediator.this) {
-                if (refactorGetCurrentUser()) {
-                    notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
-                }
+                notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(userId));
                 resetKeyguardDonePendingLocked();
                 dismiss(null /* callback */, null /* message */);
                 adjustStatusBarLocked();
@@ -637,7 +636,7 @@
 
         @Override
         public void onUserSwitchComplete(int userId) {
-            if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
+            Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
             // We are calling dismiss again and with a delay as there are race conditions
             // in some scenarios caused by async layout listeners
             mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500);
@@ -1578,20 +1577,17 @@
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
-        if (!refactorGetCurrentUser()) {
-            KeyguardUpdateMonitor.setCurrentUser(mUserTracker.getUserId());
-        }
-
         // Assume keyguard is showing (unless it's disabled) until we know for sure, unless Keyguard
         // is disabled.
         if (isKeyguardServiceEnabled()) {
             setShowingLocked(!shouldWaitForProvisioning()
                     && !mLockPatternUtils.isLockScreenDisabled(
                             mSelectedUserInteractor.getSelectedUserId()),
-                    true /* forceCallbacks */);
+                    true /* forceCallbacks */, "setupLocked - keyguard service enabled");
         } else {
             // The system's keyguard is disabled or missing.
-            setShowingLocked(false /* showing */, true /* forceCallbacks */);
+            setShowingLocked(false /* showing */, true /* forceCallbacks */,
+                    "setupLocked - keyguard service disabled");
         }
 
         mKeyguardTransitions.register(
@@ -2334,6 +2330,12 @@
                     Log.e(TAG, "doKeyguard: we're still showing, but going away. Re-show the "
                             + "keyguard rather than short-circuiting and resetting.");
                 } else {
+                    // We're removing "reset" in the refactor - "resetting" the views will happen
+                    // as a reaction to the root cause of the "reset" signal.
+                    if (KeyguardWmStateRefactor.isEnabled()) {
+                        return;
+                    }
+
                     // It's already showing, and we're not trying to show it while the screen is
                     // off. We can simply reset all of the views, but don't hide the bouncer in case
                     // the user is currently interacting with it.
@@ -2537,19 +2539,6 @@
     }
 
     /**
-     * Update the newUserId. Call while holding WindowManagerService lock.
-     * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing.
-     *
-     * @param newUserId The id of the incoming user.
-     */
-    public void setCurrentUser(int newUserId) {
-        KeyguardUpdateMonitor.setCurrentUser(newUserId);
-        synchronized (this) {
-            notifyTrustedChangedLocked(mUpdateMonitor.getUserHasTrust(newUserId));
-        }
-    }
-
-    /**
      * This broadcast receiver should be registered with the SystemUI permission.
      */
     private final BroadcastReceiver mDelayedLockBroadcastReceiver = new BroadcastReceiver() {
@@ -2827,9 +2816,10 @@
         playSound(mTrustedSoundId);
     }
 
-    private void updateActivityLockScreenState(boolean showing, boolean aodShowing) {
+    private void updateActivityLockScreenState(boolean showing, boolean aodShowing, String reason) {
         mUiBgExecutor.execute(() -> {
-            Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ")");
+            Log.d(TAG, "updateActivityLockScreenState(" + showing + ", " + aodShowing + ", "
+                    + reason + ")");
 
             if (KeyguardWmStateRefactor.isEnabled()) {
                 // Handled in WmLockscreenVisibilityManager if flag is enabled.
@@ -2889,7 +2879,7 @@
 
             // Force if we're showing in the middle of unlocking, to ensure we end up in the
             // correct state.
-            setShowingLocked(true, hidingOrGoingAway /* force */);
+            setShowingLocked(true, hidingOrGoingAway /* force */, "handleShowInner");
             mHiding = false;
 
             if (!KeyguardWmStateRefactor.isEnabled()) {
@@ -3061,15 +3051,14 @@
                 mHiding = true;
                 mKeyguardGoingAwayRunnable.run();
             } else {
-                Log.d(TAG, "Hiding keyguard while occluded. Just hide the keyguard view and exit.");
-
                 if (!KeyguardWmStateRefactor.isEnabled()) {
                     mKeyguardViewControllerLazy.get().hide(
                             mSystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
                             mHideAnimation.getDuration());
                 }
 
-                onKeyguardExitFinished();
+                onKeyguardExitFinished("Hiding keyguard while occluded. Just hide the keyguard "
+                        + "view and exit.");
             }
 
             // It's possible that the device was unlocked (via BOUNCER or Fingerprint) while
@@ -3100,6 +3089,7 @@
         Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
         synchronized (KeyguardViewMediator.this) {
+            mIsKeyguardExitAnimationCanceled = false;
             // Tell ActivityManager that we canceled the keyguard animation if
             // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard,
             // unless we're animating the surface behind the keyguard and will be hiding the
@@ -3119,7 +3109,8 @@
                         Slog.w(TAG, "Failed to call onAnimationFinished", e);
                     }
                 }
-                setShowingLocked(mShowing, true /* force */);
+                setShowingLocked(mShowing, true /* force */,
+                        "handleStartKeyguardExitAnimation - canceled");
                 return;
             }
             mHiding = false;
@@ -3143,9 +3134,11 @@
                                         Slog.w(TAG, "Failed to call onAnimationFinished", e);
                                     }
                                 }
-                                onKeyguardExitFinished();
-                                mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
-                                        0 /* fadeoutDuration */);
+                                if (!mIsKeyguardExitAnimationCanceled) {
+                                    onKeyguardExitFinished("onRemoteAnimationFinished");
+                                    mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
+                                            0 /* fadeoutDuration */);
+                                }
                                 mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
                             }
 
@@ -3282,12 +3275,12 @@
                     anim.start();
                 });
 
-                onKeyguardExitFinished();
+                onKeyguardExitFinished("remote animation disabled");
             }
         }
     }
 
-    private void onKeyguardExitFinished() {
+    private void onKeyguardExitFinished(String reason) {
         if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
         // only play "unlock" noises if not on a call (since the incall UI
         // disables the keyguard)
@@ -3295,7 +3288,7 @@
             playSounds(false);
         }
 
-        setShowingLocked(false);
+        setShowingLocked(false, "onKeyguardExitFinished: " + reason);
         mWakeAndUnlocking = false;
         mDismissCallbackRegistry.notifyDismissSucceeded();
         resetKeyguardDonePendingLocked();
@@ -3343,6 +3336,9 @@
             // A lock is pending, meaning the keyguard exit animation was cancelled because we're
             // re-locking. We should just end the surface-behind animation without exiting the
             // keyguard. The pending lock will be handled by onFinishedGoingToSleep().
+            if (relockWithPowerButtonImmediately()) {
+                mIsKeyguardExitAnimationCanceled = true;
+            }
             finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
             maybeHandlePendingLock();
         } else {
@@ -3391,12 +3387,13 @@
                 doKeyguardLocked(null);
                 finishSurfaceBehindRemoteAnimation(true /* showKeyguard */);
                 // Ensure WM is notified that we made a decision to show
-                setShowingLocked(true /* showing */, true /* force */);
+                setShowingLocked(true /* showing */, true /* force */,
+                        "exitKeyguardAndFinishSurfaceBehindRemoteAnimation - relocked");
 
                 return;
             }
 
-            onKeyguardExitFinished();
+            onKeyguardExitFinished("exitKeyguardAndFinishSurfaceBehindRemoteAnimation");
 
             if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
                 Log.d(TAG, "onKeyguardExitRemoteAnimationFinished"
@@ -3453,7 +3450,7 @@
         mSurfaceBehindRemoteAnimationRequested = false;
         mKeyguardStateController.notifyKeyguardGoingAway(false);
         if (mShowing) {
-            setShowingLocked(true, true);
+            setShowingLocked(true, true, "hideSurfaceBehindKeyguard");
         }
     }
 
@@ -3536,7 +3533,7 @@
                     try {
                         mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
                                 mContext.getPackageName(),
-                                mSelectedUserInteractor.getSelectedUserId(true));
+                                mSelectedUserInteractor.getSelectedUserId());
                     } catch (RemoteException e) {
                         Log.d(TAG, "Failed to force clear flags", e);
                     }
@@ -3571,12 +3568,16 @@
                     }
                     return;
                 }
-                try {
-                    mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
-                            mContext.getPackageName(),
-                            mSelectedUserInteractor.getSelectedUserId(true));
-                } catch (RemoteException e) {
-                    Log.d(TAG, "Failed to set disable flags: " + flags, e);
+
+                // Handled in StatusBarDisableFlagsInteractor.
+                if (!KeyguardWmStateRefactor.isEnabled()) {
+                    try {
+                        mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+                                mContext.getPackageName(),
+                                mSelectedUserInteractor.getSelectedUserId());
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Failed to set disable flags: " + flags, e);
+                    }
                 }
             }
         }
@@ -3799,7 +3800,7 @@
         // update lock screen state in ATMS here, otherwise ATMS tries to resume activities when
         // enabling doze state.
         if (mShowing || !mPendingLock || !mDozeParameters.canControlUnlockedScreenOff()) {
-            setShowingLocked(mShowing);
+            setShowingLocked(mShowing, "setDozing");
         }
     }
 
@@ -3809,7 +3810,7 @@
         // is 1f), then show the activity lock screen.
         if (mAnimatingScreenOff && mDozing && linear == 1f) {
             mAnimatingScreenOff = false;
-            setShowingLocked(mShowing, true);
+            setShowingLocked(mShowing, true, "onDozeAmountChanged");
         }
     }
 
@@ -3847,11 +3848,11 @@
         }
     }
 
-    void setShowingLocked(boolean showing) {
-        setShowingLocked(showing, false /* forceCallbacks */);
+    void setShowingLocked(boolean showing, String reason) {
+        setShowingLocked(showing, false /* forceCallbacks */, reason);
     }
 
-    private void setShowingLocked(boolean showing, boolean forceCallbacks) {
+    private void setShowingLocked(boolean showing, boolean forceCallbacks, String reason) {
         final boolean aodShowing = mDozing && !mWakeAndUnlocking;
         final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks;
         final boolean updateActivityLockScreenState = showing != mShowing
@@ -3862,9 +3863,8 @@
             notifyDefaultDisplayCallbacks(showing);
         }
         if (updateActivityLockScreenState) {
-            updateActivityLockScreenState(showing, aodShowing);
+            updateActivityLockScreenState(showing, aodShowing, reason);
         }
-
     }
 
     private void notifyDefaultDisplayCallbacks(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
new file mode 100644
index 0000000..208a17c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
@@ -0,0 +1,12 @@
+set noparent
+
+# Bug component: 78010
+
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 180afb2..e89594e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -23,7 +23,7 @@
 import android.view.WindowManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
 import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.util.concurrent.Executor
@@ -41,7 +41,7 @@
     private val activityTaskManagerService: IActivityTaskManager,
     private val keyguardStateController: KeyguardStateController,
     private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
-    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
 ) {
 
     /**
@@ -148,7 +148,7 @@
         // a transition to GONE. This transition needs to start even if we're not provided an app
         // animation target - it's possible the app is destroyed on creation, etc. but we'll still
         // be unlocking.
-        keyguardTransitionInteractor.startDismissKeyguardTransition(
+        keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
             reason = "Going away remote animation started"
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index a43bfd3..8a3d017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -63,6 +63,7 @@
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger;
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl;
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.process.ProcessWrapper;
@@ -111,6 +112,7 @@
             DeviceEntryIconTransitionModule.class,
             FalsingModule.class,
             KeyguardDataQuickAffordanceModule.class,
+            KeyguardQuickAffordancesCombinedViewModelModule.class,
             KeyguardRepositoryModule.class,
             DeviceEntryFaceAuthModule.class,
             KeyguardDisplayModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index b1589da..e68d799 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -39,7 +39,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.SharingStarted.Companion.Eagerly
 import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.buffer
@@ -53,7 +53,7 @@
 /** Encapsulates state about device entry fingerprint auth mechanism. */
 interface DeviceEntryFingerprintAuthRepository {
     /** Whether the device entry fingerprint auth is locked out. */
-    val isLockedOut: Flow<Boolean>
+    val isLockedOut: StateFlow<Boolean>
 
     /**
      * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
@@ -127,7 +127,7 @@
         else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
     }
 
-    override val isLockedOut: Flow<Boolean> =
+    override val isLockedOut: StateFlow<Boolean> by lazy {
         conflatedCallbackFlow {
                 val sendLockoutUpdate =
                     fun() {
@@ -151,7 +151,12 @@
                 sendLockoutUpdate()
                 awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
             }
-            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
+            .stateIn(
+                scope,
+                started = Eagerly,
+                initialValue = keyguardUpdateMonitor.isFingerprintLockedOut
+            )
+    }
 
     override val isRunning: Flow<Boolean>
         get() =
@@ -309,6 +314,7 @@
                         ) {
                             sendShouldUpdateIndicatorVisibility(true)
                         }
+
                         override fun onStrongAuthStateChanged(userId: Int) {
                             sendShouldUpdateIndicatorVisibility(true)
                         }
@@ -318,7 +324,7 @@
                 awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
             }
             .flowOn(mainDispatcher)
-            .shareIn(scope, started = SharingStarted.WhileSubscribed(), replay = 1)
+            .shareIn(scope, started = WhileSubscribed(), replay = 1)
 
     companion object {
         const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
index 7087752..ec52055 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepository.kt
@@ -101,7 +101,7 @@
         secureSettings
             .observerFlow(
                 names = arrayOf(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
-                userId = UserHandle.USER_SYSTEM,
+                userId = UserHandle.USER_ALL,
             )
             .onStart { emit(Unit) } // Forces an initial update.
             .map { withContext(backgroundDispatcher) { getClockSize() } }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
index 698328e..d49550e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt
@@ -22,7 +22,6 @@
 import android.os.UserHandle
 import android.util.LayoutDirection
 import com.android.systemui.Dumpable
-import com.android.systemui.res.R
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
@@ -35,6 +34,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -61,10 +61,13 @@
     private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager,
     private val userTracker: UserTracker,
     legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer,
-    private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
+    configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>,
     dumpManager: DumpManager,
     userHandle: UserHandle,
 ) {
+    // Configs for all keyguard quick affordances, mapped by the quick affordance ID as key
+    private val configsByAffordanceId: Map<String, KeyguardQuickAffordanceConfig> =
+        configs.associateBy { it.key }
     private val userId: Flow<Int> =
         ConflatedCallbackFlow.conflatedCallbackFlow {
             val callback =
@@ -126,7 +129,7 @@
      */
     fun getCurrentSelections(slotId: String): List<KeyguardQuickAffordanceConfig> {
         val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList())
-        return configs.filter { selections.contains(it.key) }
+        return configsByAffordanceId.values.filter { selections.contains(it.key) }
     }
 
     /**
@@ -159,7 +162,7 @@
      */
     suspend fun getAffordancePickerRepresentations():
         List<KeyguardQuickAffordancePickerRepresentation> {
-        return configs
+        return configsByAffordanceId.values
             .associateWith { config -> config.getPickerScreenState() }
             .filterNot { (_, pickerState) ->
                 pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
@@ -226,6 +229,11 @@
         }
     }
 
+    /** Get the config of a quick affordance. */
+    fun getConfig(quickAffordanceId: String): KeyguardQuickAffordanceConfig? {
+        return configsByAffordanceId[quickAffordanceId]
+    }
+
     private inner class Dumpster : Dumpable {
         override fun dump(pw: PrintWriter, args: Array<out String>) {
             val slotPickerRepresentations = getSlotPickerRepresentations()
@@ -246,7 +254,7 @@
                 pw.println("    $slotId$selectionText (capacity = $capacity)")
             }
             pw.println("Available affordances on device:")
-            configs.forEach { config ->
+            configsByAffordanceId.values.forEach { config ->
                 pw.println("    ${config.key} (\"${config.pickerName()}\")")
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 81b0064..49303e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -91,11 +91,11 @@
      * the z-order (which is not really above the system UI window, but rather - the lock-screen
      * becomes invisible to reveal the "occluding activity").
      */
-    val isKeyguardShowing: Flow<Boolean>
+    val isKeyguardShowing: StateFlow<Boolean>
 
     /** Is an activity showing over the keyguard? */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
-    val isKeyguardOccluded: Flow<Boolean>
+    val isKeyguardOccluded: StateFlow<Boolean>
 
     /**
      * Whether the device is locked or unlocked right now. This is true when keyguard has been
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
index a1e4af5..b67fd4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.content.Context
+import android.os.UserHandle
 import android.provider.Settings
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
@@ -37,6 +38,7 @@
 interface KeyguardSmartspaceRepository {
     val bcSmartspaceVisibility: StateFlow<Int>
     val isWeatherEnabled: StateFlow<Boolean>
+
     fun setBcSmartspaceVisibility(visibility: Int)
 }
 
@@ -55,7 +57,7 @@
         secureSettings
             .observerFlow(
                 names = arrayOf(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED),
-                userId = userTracker.userId,
+                userId = UserHandle.USER_ALL,
             )
             .onStart { emit(Unit) }
             .map { getLockscreenWeatherEnabled() }
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 de60c11..797a4ec 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
@@ -285,7 +285,7 @@
         state: TransitionState
     ) {
         if (updateTransitionId != transitionId) {
-            Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+            Log.e(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index ae830ee..e4b0f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -30,12 +30,11 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -49,7 +48,6 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 
-@ExperimentalCoroutinesApi
 @SysUISingleton
 class FromAlternateBouncerTransitionInteractor
 @Inject
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 2a8bb47..4cf9ec8 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
@@ -36,8 +36,6 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
 
 @SysUISingleton
@@ -73,15 +71,11 @@
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
-    private val canDismissLockscreen: Flow<Boolean> =
-        combine(
-            keyguardInteractor.isKeyguardShowing,
-            keyguardInteractor.isKeyguardDismissible,
-            keyguardInteractor.biometricUnlockState,
-        ) { isKeyguardShowing, isKeyguardDismissible, biometricUnlockState ->
-            (isWakeAndUnlock(biometricUnlockState.mode) ||
-                (!isKeyguardShowing && isKeyguardDismissible))
-        }
+    private fun canDismissLockscreen(): Boolean {
+        return isWakeAndUnlock(keyguardInteractor.biometricUnlockState.value.mode) ||
+            (!keyguardInteractor.isKeyguardShowing.value &&
+                keyguardInteractor.isKeyguardDismissible.value)
+    }
 
     /**
      * Listen for the signal that we're waking up and figure what state we need to transition to.
@@ -95,23 +89,19 @@
                 .filterRelevantKeyguardStateAnd { wakefulness -> wakefulness.isAwake() }
                 .debounce(50L)
                 .sample(
-                    startedKeyguardTransitionStep,
-                    keyguardInteractor.biometricUnlockState,
-                    keyguardInteractor.primaryBouncerShowing,
-                    keyguardInteractor.isKeyguardOccluded,
-                    canDismissLockscreen,
+                    transitionInteractor.startedKeyguardTransitionStep,
                     wakeToGoneInteractor.canWakeDirectlyToGone,
                 )
                 .collect {
                     (
                         _,
                         startedStep,
-                        biometricUnlockState,
-                        primaryBouncerShowing,
-                        isKeyguardOccludedLegacy,
-                        canDismissLockscreen,
                         canWakeDirectlyToGone,
                     ) ->
+                    val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
+                    val biometricUnlockMode = keyguardInteractor.biometricUnlockState.value.mode
+                    val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
+
                     if (!maybeHandleInsecurePowerGesture()) {
                         val shouldTransitionToLockscreen =
                             if (KeyguardWmStateRefactor.isEnabled) {
@@ -121,12 +111,10 @@
                                 // completes.
                                 !maybeStartTransitionToOccludedOrInsecureCamera { state, reason ->
                                     startTransitionTo(state, ownerReason = reason)
-                                } &&
-                                    !isWakeAndUnlock(biometricUnlockState.mode) &&
-                                    !primaryBouncerShowing
+                                } && !isWakeAndUnlock(biometricUnlockMode) && !primaryBouncerShowing
                             } else {
                                 !isKeyguardOccludedLegacy &&
-                                    !isWakeAndUnlock(biometricUnlockState.mode) &&
+                                    !isWakeAndUnlock(biometricUnlockMode) &&
                                     !primaryBouncerShowing
                             }
 
@@ -136,11 +124,11 @@
                             !KeyguardWmStateRefactor.isEnabled && isKeyguardOccludedLegacy
 
                         val shouldTransitionToGone =
-                            (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) ||
+                            (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen()) ||
                                 (KeyguardWmStateRefactor.isEnabled && canWakeDirectlyToGone)
 
                         if (shouldTransitionToGone) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
+                            // TODO(b/360368320): Adapt for scene framework
                             if (SceneContainerFlag.isEnabled) return@collect
                             startTransitionTo(
                                 toState = KeyguardState.GONE,
@@ -198,7 +186,6 @@
      * PRIMARY_BOUNCER.
      */
     private fun listenForAodToPrimaryBouncer() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch("$TAG#listenForAodToPrimaryBouncer") {
             keyguardInteractor.primaryBouncerShowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 61446c1..80a0cee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.shared.model.WakeSleepReason
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
@@ -42,8 +41,6 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.launch
 
@@ -83,13 +80,10 @@
         listenForTransitionToCamera(scope, keyguardInteractor)
     }
 
-    private val canTransitionToGoneOnWake: Flow<Boolean> =
-        combine(
-            keyguardInteractor.isKeyguardShowing,
-            keyguardInteractor.isKeyguardDismissible,
-        ) { isKeyguardShowing, isKeyguardDismissible ->
-            isKeyguardDismissible && !isKeyguardShowing
-        }
+    private fun canDismissLockscreen(): Boolean {
+        return !keyguardInteractor.isKeyguardShowing.value &&
+            keyguardInteractor.isKeyguardDismissible.value
+    }
 
     private fun listenForDozingToGoneViaBiometrics() {
         if (KeyguardWmStateRefactor.isEnabled) {
@@ -112,7 +106,7 @@
                     ) ->
                     if (isWakeAndUnlock(biometricUnlockState.mode)) {
                         if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
+                            // TODO(b/360368320): Adapt for scene framework
                         } else {
                             startTransitionTo(
                                 KeyguardState.GONE,
@@ -135,57 +129,35 @@
                 .debounce(50L)
                 .filterRelevantKeyguardStateAnd { isAwake -> isAwake }
                 .sample(
-                    keyguardInteractor.isKeyguardOccluded,
                     communalInteractor.isCommunalAvailable,
                     communalSceneInteractor.isIdleOnCommunal,
-                    canTransitionToGoneOnWake,
-                    keyguardInteractor.primaryBouncerShowing,
                 )
-                .collect {
-                    (
-                        _,
-                        occluded,
-                        isCommunalAvailable,
-                        isIdleOnCommunal,
-                        canTransitionToGoneOnWake,
-                        primaryBouncerShowing) ->
+                .collect { (_, isCommunalAvailable, isIdleOnCommunal) ->
+                    val isKeyguardOccludedLegacy = keyguardInteractor.isKeyguardOccluded.value
+                    val primaryBouncerShowing = keyguardInteractor.primaryBouncerShowing.value
+
                     if (!deviceEntryInteractor.isLockscreenEnabled()) {
-                        if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
-                        } else {
+                        if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GONE)
                         }
-                    } else if (canTransitionToGoneOnWake) {
-                        if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
-                        } else {
+                    } else if (canDismissLockscreen()) {
+                        if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GONE)
                         }
                     } else if (primaryBouncerShowing) {
-                        if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
-                        } else {
+                        if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
                         }
-                    } else if (occluded) {
+                    } else if (isKeyguardOccludedLegacy) {
                         startTransitionTo(KeyguardState.OCCLUDED)
                     } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
-                        if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
-                        } else {
+                        if (!SceneContainerFlag.isEnabled) {
                             startTransitionTo(KeyguardState.GLANCEABLE_HUB)
                         }
-                    } else if (
-                        powerInteractor.detailedWakefulness.value.lastWakeReason ==
-                            WakeSleepReason.POWER_BUTTON &&
-                            isCommunalAvailable &&
-                            dreamManager.canStartDreaming(true)
-                    ) {
+                    } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
                         // This case handles tapping the power button to transition through
                         // dream -> off -> hub.
-                        if (SceneContainerFlag.isEnabled) {
-                            // TODO(b/336576536): Check if adaptation for scene framework is needed
-                        } else {
+                        if (!SceneContainerFlag.isEnabled) {
                             transitionToGlanceableHub()
                         }
                     } else {
@@ -228,47 +200,28 @@
                             !isWakeAndUnlock(biometricUnlockState.mode)
                     ) {
                         if (canWakeDirectlyToGone) {
-                            if (SceneContainerFlag.isEnabled) {
-                                // TODO(b/336576536): Check if adaptation for scene framework is
-                                // needed
-                            } else {
+                            if (!SceneContainerFlag.isEnabled) {
                                 startTransitionTo(
                                     KeyguardState.GONE,
                                     ownerReason = "waking from dozing"
                                 )
                             }
                         } else if (primaryBouncerShowing) {
-                            if (SceneContainerFlag.isEnabled) {
-                                // TODO(b/336576536): Check if adaptation for scene framework is
-                                // needed
-                            } else {
+                            if (!SceneContainerFlag.isEnabled) {
                                 startTransitionTo(
                                     KeyguardState.PRIMARY_BOUNCER,
                                     ownerReason = "waking from dozing"
                                 )
                             }
                         } else if (isIdleOnCommunal && !communalSceneKtfRefactor()) {
-                            if (SceneContainerFlag.isEnabled) {
-                                // TODO(b/336576536): Check if adaptation for scene framework is
-                                // needed
-                            } else {
+                            if (!SceneContainerFlag.isEnabled) {
                                 startTransitionTo(
                                     KeyguardState.GLANCEABLE_HUB,
                                     ownerReason = "waking from dozing"
                                 )
                             }
-                        } else if (
-                            powerInteractor.detailedWakefulness.value.lastWakeReason ==
-                                WakeSleepReason.POWER_BUTTON &&
-                                isCommunalAvailable &&
-                                dreamManager.canStartDreaming(true)
-                        ) {
-                            // This case handles tapping the power button to transition through
-                            // dream -> off -> hub.
-                            if (SceneContainerFlag.isEnabled) {
-                                // TODO(b/336576536): Check if adaptation for scene framework is
-                                // needed
-                            } else {
+                        } else if (isCommunalAvailable && dreamManager.canStartDreaming(true)) {
+                            if (!SceneContainerFlag.isEnabled) {
                                 transitionToGlanceableHub()
                             }
                         } else {
@@ -285,9 +238,10 @@
     private suspend fun transitionToGlanceableHub() {
         if (communalSceneKtfRefactor()) {
             communalSceneInteractor.changeScene(
-                CommunalScenes.Communal,
+                newScene = CommunalScenes.Communal,
+                loggingReason = "from dozing to hub",
                 // Immediately show the hub when transitioning from dozing to hub.
-                CommunalTransitionKeys.Immediately,
+                transitionKey = CommunalTransitionKeys.Immediately,
             )
         } else {
             startTransitionTo(KeyguardState.GLANCEABLE_HUB)
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 17c1e82..2434b29 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,9 @@
 import com.android.app.animation.Interpolators
 import com.android.app.tracing.coroutines.launch
 import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
@@ -32,7 +34,6 @@
 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.Utils.Companion.sample as sampleCombine
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
@@ -59,6 +60,7 @@
     @Main mainDispatcher: CoroutineDispatcher,
     keyguardInteractor: KeyguardInteractor,
     private val glanceableHubTransitions: GlanceableHubTransitions,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -115,7 +117,7 @@
         if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
-                .sample(startedKeyguardTransitionStep, ::Pair)
+                .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
                 .collect { pair ->
                     val (isBouncerShowing, lastStartedTransitionStep) = pair
                     if (
@@ -127,17 +129,24 @@
         }
     }
 
-    fun startToLockscreenTransition() {
+    fun startToLockscreenOrGlanceableHubTransition(openHub: Boolean) {
         scope.launch {
             if (
-                transitionInteractor.startedKeyguardState.replayCache.last() ==
+                transitionInteractor.startedKeyguardTransitionStep.value.to ==
                     KeyguardState.DREAMING
             ) {
                 if (powerInteractor.detailedWakefulness.value.isAwake()) {
-                    startTransitionTo(
-                        KeyguardState.LOCKSCREEN,
-                        ownerReason = "Dream has ended and device is awake"
-                    )
+                    if (openHub) {
+                        communalSceneInteractor.changeScene(
+                            newScene = CommunalScenes.Communal,
+                            loggingReason = "FromDreamingTransitionInteractor",
+                        )
+                    } else {
+                        startTransitionTo(
+                            KeyguardState.LOCKSCREEN,
+                            ownerReason = "Dream has ended and device is awake"
+                        )
+                    }
                 }
             }
         }
@@ -208,15 +217,15 @@
 
         scope.launch {
             keyguardInteractor.isAbleToDream
-                .sampleCombine(
-                    keyguardInteractor.isKeyguardShowing,
-                    keyguardInteractor.isKeyguardDismissible,
-                )
-                .filterRelevantKeyguardStateAnd {
-                    (isDreaming, isKeyguardShowing, isKeyguardDismissible) ->
-                    !isDreaming && isKeyguardDismissible && !isKeyguardShowing
+                .filterRelevantKeyguardStateAnd { isDreaming -> !isDreaming }
+                .collect {
+                    if (
+                        keyguardInteractor.isKeyguardDismissible.value &&
+                            !keyguardInteractor.isKeyguardShowing.value
+                    ) {
+                        startTransitionTo(KeyguardState.GONE)
+                    }
                 }
-                .collect { startTransitionTo(KeyguardState.GONE) }
         }
     }
 
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 befcc9e..199caa1 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
@@ -37,16 +37,21 @@
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.noneOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
+@OptIn(FlowPreview::class)
 @SysUISingleton
 class FromGlanceableHubTransitionInteractor
 @Inject
@@ -159,6 +164,7 @@
                     if (communalSceneKtfRefactor()) {
                         communalSceneInteractor.changeScene(
                             newScene = CommunalScenes.Blank,
+                            loggingReason = "hub to dozing",
                             transitionKey = CommunalTransitionKeys.Immediately,
                             keyguardState = KeyguardState.DOZING,
                         )
@@ -182,6 +188,7 @@
                             if (communalSceneKtfRefactor()) {
                                 communalSceneInteractor.changeScene(
                                     newScene = CommunalScenes.Blank,
+                                    loggingReason = "hub to occluded (KeyguardWmStateRefactor)",
                                     transitionKey = CommunalTransitionKeys.SimpleFade,
                                     keyguardState = state,
                                 )
@@ -194,23 +201,31 @@
             }
         } else if (communalSceneKtfRefactor()) {
             scope.launch {
-                allOf(
+                combine(
                         keyguardInteractor.isKeyguardOccluded,
-                        noneOf(
-                            // Dream is a special-case of occluded, so filter out the dreaming
-                            // case here.
-                            keyguardInteractor.isDreaming,
-                            // When launching activities from widgets on the hub, we have a
-                            // custom occlusion animation.
-                            communalSceneInteractor.isLaunchingWidget,
-                        ),
+                        keyguardInteractor.isAbleToDream
+                            // Debounce the dreaming signal since there is a race condition between
+                            // the occluded and dreaming signals. We therefore add a small delay
+                            // to give enough time for occluded to flip to false when the dream
+                            // ends, to avoid transitioning to OCCLUDED erroneously when exiting
+                            // the dream.
+                            .debounce(100.milliseconds),
+                        ::Pair
                     )
-                    .filterRelevantKeyguardStateAnd { isOccludedAndNotDreamingNorLaunchingWidget ->
-                        isOccludedAndNotDreamingNorLaunchingWidget
+                    .sampleFilter(
+                        // When launching activities from widgets on the hub, we have a
+                        // custom occlusion animation.
+                        communalSceneInteractor.isLaunchingWidget,
+                    ) { launchingWidget ->
+                        !launchingWidget
+                    }
+                    .filterRelevantKeyguardStateAnd { (isOccluded, isDreaming) ->
+                        isOccluded && !isDreaming
                     }
                     .collect { _ ->
                         communalSceneInteractor.changeScene(
                             newScene = CommunalScenes.Blank,
+                            loggingReason = "hub to occluded",
                             transitionKey = CommunalTransitionKeys.SimpleFade,
                             keyguardState = KeyguardState.OCCLUDED,
                         )
@@ -228,7 +243,6 @@
     }
 
     private fun listenForHubToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (communalSceneKtfRefactor()) {
             scope.launch {
@@ -254,6 +268,7 @@
                         } else {
                             communalSceneInteractor.changeScene(
                                 newScene = CommunalScenes.Blank,
+                                loggingReason = "hub to gone",
                                 transitionKey = CommunalTransitionKeys.SimpleFade,
                                 keyguardState = KeyguardState.GONE
                             )
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 5dc020f..228e01e 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
@@ -134,16 +134,12 @@
                 .filterRelevantKeyguardState()
                 .sampleCombine(
                     internalTransitionInteractor.currentTransitionInfoInternal,
-                    finishedKeyguardState,
+                    transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
                     keyguardInteractor.isActiveDreamLockscreenHosted,
                 )
                 .collect {
-                    (
-                        isAbleToDream,
-                        transitionInfo,
-                        finishedKeyguardState,
-                        isActiveDreamLockscreenHosted) ->
-                    val isOnLockscreen = finishedKeyguardState == KeyguardState.LOCKSCREEN
+                    (isAbleToDream, transitionInfo, isOnLockscreen, isActiveDreamLockscreenHosted)
+                    ->
                     val isTransitionInterruptible =
                         transitionInfo.to == KeyguardState.LOCKSCREEN &&
                             !invalidFromStates.contains(transitionInfo.from)
@@ -189,7 +185,7 @@
         scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
             shadeRepository.legacyShadeExpansion
                 .sampleCombine(
-                    startedKeyguardTransitionStep,
+                    transitionInteractor.startedKeyguardTransitionStep,
                     internalTransitionInteractor.currentTransitionInfoInternal,
                     keyguardInteractor.statusBarState,
                     keyguardInteractor.isKeyguardDismissible,
@@ -276,7 +272,6 @@
     }
 
     private fun listenForLockscreenToGone() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) return
         scope.launch("$TAG#listenForLockscreenToGone") {
@@ -292,7 +287,6 @@
     }
 
     private fun listenForLockscreenToGoneDragging() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         if (KeyguardWmStateRefactor.isEnabled) {
             // When the refactor is enabled, we no longer use isKeyguardGoingAway.
@@ -336,7 +330,7 @@
             listenForSleepTransition(
                 modeOnCanceledFromStartedStep = { startedStep ->
                     if (
-                        transitionInteractor.asleepKeyguardState.value == KeyguardState.AOD &&
+                        keyguardInteractor.asleepKeyguardState.value == KeyguardState.AOD &&
                             startedStep.from == KeyguardState.AOD
                     ) {
                         TransitionModeOnCanceled.REVERSE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index aea57ce..0343786 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -76,7 +76,6 @@
     }
 
     private fun listenForOccludedToPrimaryBouncer() {
-        // TODO(b/336576536): Check if adaptation for scene framework is needed
         if (SceneContainerFlag.isEnabled) return
         scope.launch {
             keyguardInteractor.primaryBouncerShowing
@@ -135,7 +134,7 @@
         }
     }
 
-    private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
+    private suspend fun startTransitionToLockscreenOrHub(
         isIdleOnCommunal: Boolean,
         showCommunalFromOccluded: Boolean,
         dreamFromOccluded: Boolean,
@@ -146,8 +145,9 @@
             if (SceneContainerFlag.isEnabled) return
             if (communalSceneKtfRefactor()) {
                 communalSceneInteractor.changeScene(
-                    CommunalScenes.Communal,
-                    CommunalTransitionKeys.SimpleFade
+                    newScene = CommunalScenes.Communal,
+                    loggingReason = "occluded to hub",
+                    transitionKey = CommunalTransitionKeys.SimpleFade
                 )
             } else {
                 startTransitionTo(KeyguardState.GLANCEABLE_HUB)
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 2823b93..52323a5 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
@@ -35,7 +35,7 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineDispatcher
@@ -175,7 +175,10 @@
                 !communalSceneInteractor.isLaunchingWidget.value &&
                 communalSceneInteractor.editModeState.value == null
         ) {
-            communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+            communalSceneInteractor.snapToScene(
+                newScene = CommunalScenes.Blank,
+                loggingReason = "FromPrimaryBouncerTransitionInteractor",
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index f9ab1bb..bde0f56 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -62,16 +62,16 @@
         communalInteractor
             .transitionProgressToScene(toScene)
             .sample(
-                transitionInteractor.startedKeyguardState,
+                transitionInteractor.startedKeyguardTransitionStep,
                 ::Pair,
             )
-            .collect { (transitionProgress, lastStartedState) ->
+            .collect { (transitionProgress, lastStartedStep) ->
                 val id = transitionId
                 if (id == null) {
                     // No transition started.
                     if (
                         transitionProgress is CommunalTransitionProgressModel.Transition &&
-                            lastStartedState == fromState
+                            lastStartedStep.to == fromState
                     ) {
                         transitionId =
                             transitionRepository.startTransition(
@@ -84,7 +84,7 @@
                             )
                     }
                 } else {
-                    if (lastStartedState != toState) {
+                    if (lastStartedStep.to != toState) {
                         return@collect
                     }
                     // An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index 1c445a7..7801c00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -17,15 +17,16 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.domain.resolver.NotifShadeSceneFamilyResolver
 import com.android.systemui.scene.domain.resolver.QuickSettingsSceneFamilyResolver
@@ -61,6 +62,8 @@
     deviceEntryInteractor: DeviceEntryInteractor,
     quickSettingsSceneFamilyResolver: QuickSettingsSceneFamilyResolver,
     notifShadeSceneFamilyResolver: NotifShadeSceneFamilyResolver,
+    powerInteractor: PowerInteractor,
+    alternateBouncerInteractor: AlternateBouncerInteractor,
 ) {
     val dismissAction: Flow<DismissAction> = repository.dismissAction
 
@@ -124,10 +127,12 @@
                     scene = Scenes.Bouncer,
                     stateWithoutSceneContainer = PRIMARY_BOUNCER
                 ),
-                transitionInteractor.isFinishedIn(state = ALTERNATE_BOUNCER),
+                alternateBouncerInteractor.isVisible,
                 isOnShadeWhileUnlocked,
-            ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked ->
-                !isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked
+                powerInteractor.isAsleep,
+            ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked, isAsleep ->
+                (!isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked) ||
+                    isAsleep
             }
             .filter { it }
             .sampleFilter(dismissAction) { it !is DismissAction.None }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index 628e912..d7e6bdb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -16,9 +16,13 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.internal.policy.IKeyguardDismissCallback
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 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.dagger.qualifiers.Main
+import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.TrustRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
@@ -28,23 +32,30 @@
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /** Encapsulates business logic for requesting the keyguard to dismiss/finish/done. */
 @SysUISingleton
 class KeyguardDismissInteractor
 @Inject
 constructor(
-    trustRepository: TrustRepository,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    @Application private val scope: CoroutineScope,
     private val keyguardRepository: KeyguardRepository,
-    primaryBouncerInteractor: PrimaryBouncerInteractor,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
+    private val selectedUserInteractor: SelectedUserInteractor,
+    private val dismissCallbackRegistry: DismissCallbackRegistry,
+    trustRepository: TrustRepository,
     alternateBouncerInteractor: AlternateBouncerInteractor,
     powerInteractor: PowerInteractor,
-    private val selectedUserInteractor: SelectedUserInteractor,
 ) {
     /*
      * Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -127,4 +138,29 @@
     suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
         keyguardRepository.setKeyguardDone(keyguardDoneTiming)
     }
+
+    /**
+     * Dismiss the keyguard (or show the bouncer) and invoke the provided callback once dismissed.
+     *
+     * TODO(b/358412565): Support dismiss messages.
+     */
+    fun dismissKeyguardWithCallback(
+        callback: IKeyguardDismissCallback?,
+    ) {
+        scope.launch {
+            withContext(mainDispatcher) {
+                if (callback != null) {
+                    dismissCallbackRegistry.addCallback(callback)
+                }
+
+                // This will either show the bouncer, or dismiss the keyguard if insecure.
+                // We currently need to request showing the primary bouncer in order to start a
+                // transition to PRIMARY_BOUNCER. Once we refactor that so that starting the
+                // transition is what causes the bouncer to show, we can remove this entire method,
+                // and simply ask KeyguardTransitionInteractor to transition to a bouncer state or
+                // dismiss keyguard.
+                primaryBouncerInteractor.show(true)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
new file mode 100644
index 0000000..c19bbbc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.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.keyguard.domain.interactor
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+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.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import javax.inject.Inject
+
+@SysUISingleton
+class KeyguardDismissTransitionInteractor
+@Inject
+constructor(
+    private val repository: KeyguardTransitionRepository,
+    private val fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor,
+    private val fromPrimaryBouncerTransitionInteractor: FromPrimaryBouncerTransitionInteractor,
+    private val fromAodTransitionInteractor: FromAodTransitionInteractor,
+    private val fromAlternateBouncerTransitionInteractor: FromAlternateBouncerTransitionInteractor,
+    private val fromDozingTransitionInteractor: FromDozingTransitionInteractor,
+    private val fromOccludedTransitionInteractor: FromOccludedTransitionInteractor,
+) {
+
+    /**
+     * Called to start a transition that will ultimately dismiss the keyguard from the current
+     * state.
+     *
+     * This is called exclusively by sources that can authoritatively say we should be unlocked,
+     * including KeyguardSecurityContainerController and WindowManager.
+     */
+    fun startDismissKeyguardTransition(reason: String = "") {
+        if (SceneContainerFlag.isEnabled) return
+        Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
+        when (val startedState = repository.currentTransitionInfoInternal.value.to) {
+            LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
+            PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
+            ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
+            AOD -> fromAodTransitionInteractor.dismissAod()
+            DOZING -> fromDozingTransitionInteractor.dismissFromDozing()
+            KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.dismissFromOccluded()
+            KeyguardState.GONE ->
+                Log.i(
+                    TAG,
+                    "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
+                )
+            else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
+        }
+    }
+
+    companion object {
+        private val TAG = KeyguardDismissTransitionInteractor::class.simpleName
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 4aef808..44aafab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -48,7 +48,7 @@
     @Application scope: CoroutineScope,
     val repository: KeyguardRepository,
     val biometricSettingsRepository: BiometricSettingsRepository,
-    transitionInteractor: KeyguardTransitionInteractor,
+    keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor,
     internalTransitionInteractor: InternalKeyguardTransitionInteractor,
 ) {
 
@@ -94,7 +94,9 @@
                 showKeyguardWhenReenabled
                     .filter { shouldDismiss -> shouldDismiss }
                     .collect {
-                        transitionInteractor.startDismissKeyguardTransition("keyguard disabled")
+                        keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+                            "keyguard disabled"
+                        )
                     }
             }
         }
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 0df989e..f6f0cc5 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
@@ -19,6 +19,7 @@
 
 import android.app.StatusBarManager
 import android.graphics.Point
+import android.util.Log
 import android.util.MathUtils
 import com.android.app.animation.Interpolators
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
@@ -35,9 +36,12 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.res.R
@@ -87,6 +91,7 @@
     sceneInteractorProvider: Provider<SceneInteractor>,
     private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>,
     private val fromLockscreenTransitionInteractor: Provider<FromLockscreenTransitionInteractor>,
+    private val fromOccludedTransitionInteractor: Provider<FromOccludedTransitionInteractor>,
     sharedNotificationContainerInteractor: Provider<SharedNotificationContainerInteractor>,
     @Application applicationScope: CoroutineScope,
 ) {
@@ -216,14 +221,14 @@
 
     /** Whether the keyguard is showing or not. */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState")
-    val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+    val isKeyguardShowing: StateFlow<Boolean> = repository.isKeyguardShowing
 
     /** Whether the keyguard is dismissible or not. */
     val isKeyguardDismissible: StateFlow<Boolean> = repository.isKeyguardDismissible
 
     /** Whether the keyguard is occluded (covered by an activity). */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.OCCLUDED")
-    val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
+    val isKeyguardOccluded: StateFlow<Boolean> = repository.isKeyguardOccluded
 
     /** Whether the keyguard is going away. */
     @Deprecated("Use KeyguardTransitionInteractor + KeyguardState.GONE")
@@ -253,7 +258,7 @@
     val ambientIndicationVisible: Flow<Boolean> = repository.ambientIndicationVisible.asStateFlow()
 
     /** Whether the primary bouncer is showing or not. */
-    @JvmField val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
+    @JvmField val primaryBouncerShowing: StateFlow<Boolean> = bouncerRepository.primaryBouncerShow
 
     /** Whether the alternate bouncer is showing or not. */
     val alternateBouncerShowing: Flow<Boolean> =
@@ -274,7 +279,7 @@
     val statusBarState: Flow<StatusBarState> = repository.statusBarState
 
     /** Observable for [BiometricUnlockModel] when biometrics are used to unlock the device. */
-    val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+    val biometricUnlockState: StateFlow<BiometricUnlockModel> = repository.biometricUnlockState
 
     /** Keyguard is present and is not occluded. */
     val isKeyguardVisible: Flow<Boolean> =
@@ -406,6 +411,12 @@
         }
     }
 
+    /** Which keyguard state to use when the device goes to sleep. */
+    val asleepKeyguardState: StateFlow<KeyguardState> =
+        repository.isAodAvailable
+            .map { aodAvailable -> if (aodAvailable) AOD else DOZING }
+            .stateIn(applicationScope, SharingStarted.Eagerly, DOZING)
+
     /**
      * Whether the primary authentication is required for the given user due to lockdown or
      * encryption after reboot.
@@ -484,7 +495,11 @@
 
     /** Temporary shim, until [KeyguardWmStateRefactor] is enabled */
     fun dismissKeyguard() {
-        fromLockscreenTransitionInteractor.get().dismissKeyguard()
+        when (keyguardTransitionInteractor.transitionState.value.to) {
+            LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
+            OCCLUDED -> fromOccludedTransitionInteractor.get().dismissFromOccluded()
+            else -> Log.v(TAG, "Keyguard was dismissed, no direct transition call needed")
+        }
     }
 
     fun onCameraLaunchDetected(source: Int) {
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 31236a4..2c3b481 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
@@ -53,6 +53,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEYGUARD_QUICK_AFFORDANCE_ID_NONE
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
@@ -118,7 +119,8 @@
                             is ObservableTransitionState.Idle ->
                                 it.currentScene == Scenes.Lockscreen
                             is ObservableTransitionState.Transition ->
-                                it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen
+                                it.fromContent == Scenes.Lockscreen ||
+                                    it.toContent == Scenes.Lockscreen
                         }
                     }
                     .distinctUntilChanged()
@@ -142,14 +144,18 @@
      *
      * This is useful for experiences like the lock screen preview mode, where the affordances must
      * always be visible.
+     *
+     * @param overrideQuickAffordanceId If null, return the currently-set quick affordance;
+     *   otherwise, override and return the correspondent [KeyguardQuickAffordanceModel].
      */
     suspend fun quickAffordanceAlwaysVisible(
         position: KeyguardQuickAffordancePosition,
+        overrideQuickAffordanceId: String? = null,
     ): Flow<KeyguardQuickAffordanceModel> {
         return if (isFeatureDisabledByDevicePolicy()) {
             flowOf(KeyguardQuickAffordanceModel.Hidden)
         } else {
-            quickAffordanceInternal(position)
+            quickAffordanceInternal(position, overrideQuickAffordanceId)
         }
     }
 
@@ -299,12 +305,24 @@
     }
 
     private fun quickAffordanceInternal(
-        position: KeyguardQuickAffordancePosition
+        position: KeyguardQuickAffordancePosition,
+        overrideAffordanceId: String? = null,
     ): Flow<KeyguardQuickAffordanceModel> =
         repository
             .get()
             .selections
-            .map { it[position.toSlotId()] ?: emptyList() }
+            .map { selections ->
+                val overrideQuickAffordanceConfigs =
+                    overrideAffordanceId?.let {
+                        if (it == KEYGUARD_QUICK_AFFORDANCE_ID_NONE) {
+                            emptyList()
+                        } else {
+                            val config = repository.get().getConfig(it)
+                            listOfNotNull(config)
+                        }
+                    }
+                overrideQuickAffordanceConfigs ?: selections[position.toSlotId()] ?: emptyList()
+            }
             .flatMapLatest { configs -> combinedConfigs(position, configs) }
 
     private fun combinedConfigs(
@@ -433,10 +451,6 @@
                 value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_UI_FOR_AIWP)
             ),
             KeyguardPickerFlag(
-                name = Contract.FlagsTable.FLAG_NAME_TRANSIT_CLOCK,
-                value = featureFlags.isEnabled(Flags.TRANSIT_CLOCK)
-            ),
-            KeyguardPickerFlag(
                 name = Contract.FlagsTable.FLAG_NAME_PAGE_TRANSITIONS,
                 value = featureFlags.isEnabled(Flags.WALLPAPER_PICKER_PAGE_TRANSITIONS)
             ),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
index cd49c6a..505c749d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTouchHandlingInteractor.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -47,7 +48,6 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -67,14 +67,13 @@
     broadcastDispatcher: BroadcastDispatcher,
     private val accessibilityManager: AccessibilityManagerWrapper,
     private val pulsingGestureListener: PulsingGestureListener,
+    private val faceAuthInteractor: DeviceEntryFaceAuthInteractor,
 ) {
     /** Whether the long-press handling feature should be enabled. */
     val isLongPressHandlingEnabled: StateFlow<Boolean> =
         if (isFeatureEnabled()) {
                 combine(
-                    transitionInteractor.finishedKeyguardState.map {
-                        it == KeyguardState.LOCKSCREEN
-                    },
+                    transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
                     repository.isQuickSettingsVisible,
                 ) { isFullyTransitionedToLockScreen, isQuickSettingsVisible ->
                     isFullyTransitionedToLockScreen && !isQuickSettingsVisible
@@ -129,7 +128,8 @@
         }
     }
 
-    /** Notifies that the user has long-pressed on the lock screen.
+    /**
+     * Notifies that the user has long-pressed on the lock screen.
      *
      * @param isA11yAction: Whether the action was performed as an a11y action
      */
@@ -174,6 +174,7 @@
     /** Notifies that the lockscreen has been clicked at position [x], [y]. */
     fun onClick(x: Float, y: Float) {
         pulsingGestureListener.onSingleTapUp(x, y)
+        faceAuthInteractor.onNotificationPanelClicked()
     }
 
     /** Notifies that the lockscreen has been double clicked. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index 2ebd9e8..b218300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -71,7 +71,7 @@
                 )
             } else {
                 if (SceneContainerFlag.isEnabled) {
-                    // TODO(b/336576536): Some part of the transition implemented for flag off is
+                    // TODO(b/360372242): Some part of the transition implemented for flag off is
                     //  missing here. There are two things achieved with this:
                     //  1. Keyguard is hidden when the setup wizard is shown. This part is already
                     //     implemented in scene container by disabling visibility instead of going
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 31b0bf7..d9c48fa 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
@@ -28,6 +28,7 @@
     private val interactors: Set<TransitionInteractor>,
     private val auditLogger: KeyguardTransitionAuditLogger,
     private val bootInteractor: KeyguardTransitionBootInteractor,
+    private val statusBarDisableFlagsInteractor: StatusBarDisableFlagsInteractor,
 ) : CoreStartable {
 
     override fun start() {
@@ -53,6 +54,7 @@
         }
         auditLogger.start()
         bootInteractor.start()
+        statusBarDisableFlagsInteractor.start()
     }
 
     companion object {
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 efdae62..e19b72e 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
@@ -22,21 +22,17 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
-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.DOZING
-import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
 import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 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.util.kotlin.WithPrev
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -47,7 +43,6 @@
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.buffer
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
@@ -56,7 +51,6 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.flow.transform
 import kotlinx.coroutines.launch
 
 /** Encapsulates business-logic related to the keyguard transitions. */
@@ -66,16 +60,7 @@
 @Inject
 constructor(
     @Application val scope: CoroutineScope,
-    private val keyguardRepository: KeyguardRepository,
     private val repository: KeyguardTransitionRepository,
-    private val fromLockscreenTransitionInteractor: dagger.Lazy<FromLockscreenTransitionInteractor>,
-    private val fromPrimaryBouncerTransitionInteractor:
-        dagger.Lazy<FromPrimaryBouncerTransitionInteractor>,
-    private val fromAodTransitionInteractor: dagger.Lazy<FromAodTransitionInteractor>,
-    private val fromAlternateBouncerTransitionInteractor:
-        dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
-    private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
-    private val fromOccludedTransitionInteractor: dagger.Lazy<FromOccludedTransitionInteractor>,
     private val sceneInteractor: SceneInteractor,
 ) {
     private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
@@ -104,6 +89,18 @@
     val transitionState: StateFlow<TransitionStep> =
         transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep())
 
+    private val sceneTransitionPair =
+        sceneInteractor.transitionState
+            .pairwise()
+            .stateIn(
+                scope,
+                SharingStarted.Eagerly,
+                WithPrev(
+                    sceneInteractor.transitionState.value,
+                    sceneInteractor.transitionState.value
+                )
+            )
+
     /**
      * A pair of the most recent STARTED step, and the transition step immediately preceding it. The
      * transition framework enforces that the previous step is either a CANCELED or FINISHED step,
@@ -126,8 +123,10 @@
             repository.transitions
                 .filter { it.transitionState != TransitionState.CANCELED }
                 .collect { step ->
-                    getTransitionValueFlow(step.from).emit(1f - step.value)
-                    getTransitionValueFlow(step.to).emit(step.value)
+                    val value =
+                        if (step.transitionState == TransitionState.FINISHED) 1f else step.value
+                    getTransitionValueFlow(step.from).emit(1f - value)
+                    getTransitionValueFlow(step.to).emit(value)
                 }
         }
 
@@ -162,10 +161,35 @@
                 }
             }
         }
+
+        // Safety: When any transition is FINISHED, ensure all other transitionValue flows other
+        // than the FINISHED state are reset to a value of 0f. There have been rare but severe
+        // bugs that get the device stuck in a bad state when these are not properly reset.
+        scope.launch {
+            repository.transitions
+                .filter { it.transitionState == TransitionState.FINISHED }
+                .collect {
+                    for (state in KeyguardState.entries) {
+                        if (state != it.to) {
+                            val flow = getTransitionValueFlow(state)
+                            val replayCache = flow.replayCache
+                            if (!replayCache.isEmpty() && replayCache.last() != 0f) {
+                                flow.emit(0f)
+                            }
+                        }
+                    }
+                }
+        }
     }
 
-    fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {
-        return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer)
+    fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> {
+        return transition(
+            if (SceneContainerFlag.isEnabled || edgeWithoutSceneContainer == null) {
+                edge
+            } else {
+                edgeWithoutSceneContainer
+            }
+        )
     }
 
     /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */
@@ -183,7 +207,7 @@
             }
 
         return if (SceneContainerFlag.isEnabled) {
-            flow.filter {
+            flow.filter { step ->
                 val fromScene =
                     when (edge) {
                         is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
@@ -200,8 +224,23 @@
 
                 fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
 
-                return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
+                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
@@ -231,10 +270,10 @@
     }
 
     fun transitionValue(
-        scene: SceneKey,
+        scene: SceneKey? = null,
         stateWithoutSceneContainer: KeyguardState,
     ): Flow<Float> {
-        return if (SceneContainerFlag.isEnabled) {
+        return if (SceneContainerFlag.isEnabled && scene != null) {
             sceneInteractor.transitionProgress(scene)
         } else {
             transitionValue(stateWithoutSceneContainer)
@@ -258,73 +297,10 @@
     }
 
     /** The last [TransitionStep] with a [TransitionState] of STARTED */
-    val startedKeyguardTransitionStep: Flow<TransitionStep> =
-        repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
-
-    /** The destination state of the last [TransitionState.STARTED] transition. */
-    @SuppressLint("SharedFlowCreation")
-    val startedKeyguardState: SharedFlow<KeyguardState> =
-        startedKeyguardTransitionStep
-            .map { step -> step.to }
-            .buffer(2, BufferOverflow.DROP_OLDEST)
-            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
-    /** The from state of the last [TransitionState.STARTED] transition. */
-    // TODO: is it performant to have several SharedFlows side by side instead of one?
-    @SuppressLint("SharedFlowCreation")
-    val startedKeyguardFromState: SharedFlow<KeyguardState> =
-        startedKeyguardTransitionStep
-            .map { step -> step.from }
-            .buffer(2, BufferOverflow.DROP_OLDEST)
-            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
-
-    /** Which keyguard state to use when the device goes to sleep. */
-    val asleepKeyguardState: StateFlow<KeyguardState> =
-        keyguardRepository.isAodAvailable
-            .map { aodAvailable -> if (aodAvailable) AOD else DOZING }
-            .stateIn(scope, SharingStarted.Eagerly, DOZING)
-
-    /**
-     * The last [KeyguardState] to which we [TransitionState.FINISHED] a transition.
-     *
-     * WARNING: This will NOT emit a value if a transition is CANCELED, and will also not emit a
-     * value when a subsequent transition is STARTED. It will *only* emit once we have finally
-     * FINISHED in a state. This can have unintuitive implications.
-     *
-     * For example, if we're transitioning from GONE -> DOZING, and that transition is CANCELED in
-     * favor of a DOZING -> LOCKSCREEN transition, the FINISHED state is still GONE, and will remain
-     * GONE throughout the DOZING -> LOCKSCREEN transition until the DOZING -> LOCKSCREEN transition
-     * finishes (at which point we'll be FINISHED in LOCKSCREEN).
-     *
-     * Since there's no real limit to how many consecutive transitions can be canceled, it's even
-     * possible for the FINISHED state to be the same as the STARTED state while still
-     * transitioning.
-     *
-     * For example:
-     * 1. We're finished in GONE.
-     * 2. The user presses the power button, starting a GONE -> DOZING transition. We're still
-     *    FINISHED in GONE.
-     * 3. The user changes their mind, pressing the power button to wake up; this starts a DOZING ->
-     *    LOCKSCREEN transition. We're still FINISHED in GONE.
-     * 4. The user quickly swipes away the lockscreen prior to DOZING -> LOCKSCREEN finishing; this
-     *    starts a LOCKSCREEN -> GONE transition. We're still FINISHED in GONE, but we've also
-     *    STARTED a transition *to* GONE.
-     * 5. We'll emit KeyguardState.GONE again once the transition finishes.
-     *
-     * If you just need to know when we eventually settle into a state, this flow is likely
-     * sufficient. However, if you're having issues with state *during* transitions started after
-     * one or more canceled transitions, you probably need to use [currentKeyguardState].
-     */
-    @SuppressLint("SharedFlowCreation")
-    val finishedKeyguardState: SharedFlow<KeyguardState> =
+    val startedKeyguardTransitionStep: StateFlow<TransitionStep> =
         repository.transitions
-            .transform { step ->
-                if (step.transitionState == TransitionState.FINISHED) {
-                    emit(step.to)
-                }
-            }
-            .buffer(2, BufferOverflow.DROP_OLDEST)
-            .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+            .filter { step -> step.transitionState == TransitionState.STARTED }
+            .stateIn(scope, SharingStarted.Eagerly, TransitionStep())
 
     /**
      * The [KeyguardState] we're currently in.
@@ -390,8 +366,7 @@
                     it.from
                 }
             }
-            .distinctUntilChanged()
-            .stateIn(scope, SharingStarted.Eagerly, KeyguardState.OFF)
+            .stateIn(scope, SharingStarted.Eagerly, OFF)
 
     val isInTransition =
         combine(
@@ -403,33 +378,6 @@
         }
 
     /**
-     * Called to start a transition that will ultimately dismiss the keyguard from the current
-     * state.
-     *
-     * This is called exclusively by sources that can authoritatively say we should be unlocked,
-     * including KeyguardSecurityContainerController and WindowManager.
-     */
-    fun startDismissKeyguardTransition(reason: String = "") {
-        if (SceneContainerFlag.isEnabled) return
-        Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
-        when (val startedState = repository.currentTransitionInfoInternal.value.to) {
-            LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
-            PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
-            ALTERNATE_BOUNCER ->
-                fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
-            AOD -> fromAodTransitionInteractor.get().dismissAod()
-            DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
-            KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.get().dismissFromOccluded()
-            KeyguardState.GONE ->
-                Log.i(
-                    TAG,
-                    "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
-                )
-            else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
-        }
-    }
-
-    /**
      * Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
      * completed it.
      *
@@ -487,12 +435,13 @@
 
     fun isFinishedIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> {
         return if (SceneContainerFlag.isEnabled) {
-            sceneInteractor.transitionState
-                .map { it.isIdle(scene) || it.isTransitioning(from = scene) }
-                .distinctUntilChanged()
-        } else {
-            isFinishedIn(stateWithoutSceneContainer)
-        }
+                sceneInteractor.transitionState.map {
+                    it.isIdle(scene) || it.isTransitioning(from = scene)
+                }
+            } else {
+                isFinishedIn(stateWithoutSceneContainer)
+            }
+            .distinctUntilChanged()
     }
 
     /** Whether we've FINISHED a transition to a state */
@@ -501,17 +450,26 @@
         return finishedKeyguardState.map { it == state }.distinctUntilChanged()
     }
 
+    fun isCurrentlyIn(scene: SceneKey, stateWithoutSceneContainer: KeyguardState): Flow<Boolean> {
+        return if (SceneContainerFlag.isEnabled) {
+                // In STL there is no difference between finished/currentState
+                isFinishedIn(scene, stateWithoutSceneContainer)
+            } else {
+                stateWithoutSceneContainer.checkValidState()
+                currentKeyguardState.map { it == stateWithoutSceneContainer }
+            }
+            .distinctUntilChanged()
+    }
+
     fun getCurrentState(): KeyguardState {
         return currentKeyguardState.replayCache.last()
     }
 
-    fun getStartedFromState(): KeyguardState {
-        return startedKeyguardFromState.replayCache.last()
-    }
-
-    fun getFinishedState(): KeyguardState {
-        return finishedKeyguardState.replayCache.last()
-    }
+    private val finishedKeyguardState: StateFlow<KeyguardState> =
+        repository.transitions
+            .filter { it.transitionState == TransitionState.FINISHED }
+            .map { it.to }
+            .stateIn(scope, SharingStarted.Eagerly, OFF)
 
     companion object {
         private val TAG = KeyguardTransitionInteractor::class.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
index f0bf402..9b8d9ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAwakeInState
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.settings.SecureSettings
@@ -181,14 +182,20 @@
         scope.launch {
             powerInteractor.detailedWakefulness
                 .distinctUntilChangedBy { it.isAwake() }
-                .sample(transitionInteractor.currentKeyguardState, ::Pair)
-                .collect { (wakefulness, currentState) ->
+                .sample(
+                    transitionInteractor.isCurrentlyIn(
+                        Scenes.Gone,
+                        stateWithoutSceneContainer = KeyguardState.GONE
+                    ),
+                    ::Pair
+                )
+                .collect { (wakefulness, finishedInGone) ->
                     // Save isAwake for use in onDreamingStarted/onDreamingStopped.
                     [email protected] = wakefulness.isAwake()
 
                     // If we're sleeping from GONE, check the timeout and lock instantly settings.
                     // These are not relevant if we're coming from non-GONE states.
-                    if (!isAwake && currentState == KeyguardState.GONE) {
+                    if (!isAwake && finishedInGone) {
                         val lockTimeoutDuration = getCanIgnoreAuthAndReturnToGoneDuration()
 
                         // If the screen timed out and went to sleep, and the lock timeout is > 0ms,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
new file mode 100644
index 0000000..e00e33d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.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.keyguard.domain.interactor
+
+import android.annotation.SuppressLint
+import android.app.StatusBarManager
+import android.content.Context
+import android.os.Binder
+import android.os.IBinder
+import android.os.RemoteException
+import android.provider.DeviceConfig
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.CoreStartable
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.deviceconfig.domain.interactor.DeviceConfigInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.navigation.domain.interactor.NavigationInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * Logic around StatusBarService#disableForUser, which is used to disable the home and recents
+ * button in certain device states.
+ *
+ * TODO(b/362313975): Remove post-Flexiglass, this duplicates StatusBarStartable logic.
+ */
+@SysUISingleton
+class StatusBarDisableFlagsInteractor
+@Inject
+constructor(
+    @Application private val scope: CoroutineScope,
+    @Application private val applicationContext: Context,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+    private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor,
+    private val statusBarService: IStatusBarService,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    selectedUserInteractor: SelectedUserInteractor,
+    deviceConfigInteractor: DeviceConfigInteractor,
+    navigationInteractor: NavigationInteractor,
+    authenticationInteractor: AuthenticationInteractor,
+    powerInteractor: PowerInteractor,
+) : CoreStartable {
+
+    private val disableToken: IBinder = Binder()
+
+    private val disableFlagsForUserId =
+        combine(
+                selectedUserInteractor.selectedUser,
+                keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
+                deviceConfigInteractor.property(
+                    namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
+                    name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+                    default = true,
+                ),
+                navigationInteractor.isGesturalMode,
+                authenticationInteractor.authenticationMethod,
+                powerInteractor.detailedWakefulness,
+            ) { values ->
+                val selectedUserId = values[0] as Int
+                val startedState = values[1] as KeyguardState
+                val isShowHomeOverLockscreen = values[2] as Boolean
+                val isGesturalMode = values[3] as Boolean
+                val authenticationMethod = values[4] as AuthenticationMethodModel
+                val wakefulnessModel = values[5] as WakefulnessModel
+                val isOccluded = startedState == KeyguardState.OCCLUDED
+
+                val hideHomeAndRecentsForBouncer =
+                    startedState == KeyguardState.PRIMARY_BOUNCER ||
+                        startedState == KeyguardState.ALTERNATE_BOUNCER
+                val isKeyguardShowing = startedState != KeyguardState.GONE
+                val isPowerGestureIntercepted =
+                    with(wakefulnessModel) {
+                        isAwake() &&
+                            powerButtonLaunchGestureTriggered &&
+                            lastSleepReason == WakeSleepReason.POWER_BUTTON
+                    }
+
+                var flags = StatusBarManager.DISABLE_NONE
+
+                if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
+                    if (!isShowHomeOverLockscreen || !isGesturalMode) {
+                        flags = flags or StatusBarManager.DISABLE_HOME
+                    }
+                    flags = flags or StatusBarManager.DISABLE_RECENT
+                }
+
+                if (
+                    isPowerGestureIntercepted &&
+                        isOccluded &&
+                        authenticationMethod.isSecure &&
+                        deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+                ) {
+                    flags = flags or StatusBarManager.DISABLE_RECENT
+                }
+
+                selectedUserId to flags
+            }
+            .distinctUntilChanged()
+
+    @SuppressLint("WrongConstant", "NonInjectedService")
+    override fun start() {
+        if (!KeyguardWmStateRefactor.isEnabled) {
+            return
+        }
+
+        scope.launch {
+            disableFlagsForUserId.collect { (selectedUserId, flags) ->
+                if (applicationContext.getSystemService(Context.STATUS_BAR_SERVICE) == null) {
+                    return@collect
+                }
+
+                withContext(backgroundDispatcher) {
+                    try {
+                        statusBarService.disableForUser(
+                            flags,
+                            disableToken,
+                            applicationContext.packageName,
+                            selectedUserId,
+                        )
+                    } catch (e: RemoteException) {
+                        e.printStackTrace()
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
index 906d586..e404f27 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SwipeToDismissInteractor.kt
@@ -22,12 +22,12 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.util.kotlin.Utils.Companion.sample
+import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
-import javax.inject.Inject
 
 /**
  * Handles logic around the swipe to dismiss gesture, where the user swipes up on the dismissable
@@ -53,15 +53,15 @@
     val dismissFling =
         shadeRepository.currentFling
             .sample(
-                transitionInteractor.startedKeyguardState,
+                transitionInteractor.startedKeyguardTransitionStep,
                 keyguardInteractor.isKeyguardDismissible,
                 keyguardInteractor.statusBarState,
             )
-            .filter { (flingInfo, startedState, keyguardDismissable, statusBarState) ->
+            .filter { (flingInfo, startedStep, keyguardDismissable, statusBarState) ->
                 flingInfo != null &&
-                        !flingInfo.expand &&
-                        statusBarState != StatusBarState.SHADE_LOCKED &&
-                        startedState == KeyguardState.LOCKSCREEN &&
+                    !flingInfo.expand &&
+                    statusBarState != StatusBarState.SHADE_LOCKED &&
+                    startedStep.to == KeyguardState.LOCKSCREEN &&
                     keyguardDismissable
             }
             .map { (flingInfo, _) -> flingInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index d06ee64..ba12e93 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -32,7 +32,6 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
@@ -62,17 +61,6 @@
 
     abstract fun start()
 
-    /* Use background dispatcher for all [KeyguardTransitionInteractor] flows. Necessary because
-     * the [sample] utility internally runs a collect on the Unconfined dispatcher, resulting
-     * in continuations on the main thread. We don't want that for classes that inherit from this.
-     */
-    val startedKeyguardTransitionStep =
-        transitionInteractor.startedKeyguardTransitionStep.flowOn(bgDispatcher)
-    // The following are MutableSharedFlows, and do not require flowOn
-    val startedKeyguardState = transitionInteractor.startedKeyguardState
-    val finishedKeyguardState = transitionInteractor.finishedKeyguardState
-    val currentKeyguardState = transitionInteractor.currentKeyguardState
-
     suspend fun startTransitionTo(
         toState: KeyguardState,
         animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
@@ -92,17 +80,6 @@
                     " $fromState. This should never happen - check currentTransitionInfoInternal" +
                     " or use filterRelevantKeyguardState before starting transitions."
             )
-
-            if (fromState == transitionInteractor.finishedKeyguardState.replayCache.last()) {
-                Log.e(
-                    name,
-                    "This transition would not have been ignored prior to ag/26681239, since we " +
-                        "are FINISHED in $fromState (but have since started another transition). " +
-                        "If ignoring this transition has caused a regression, fix it by ensuring " +
-                        "that transitions are exclusively started from the most recently started " +
-                        "state."
-                )
-            }
             return null
         }
 
@@ -207,11 +184,11 @@
         powerInteractor.isAsleep
             .filter { isAsleep -> isAsleep }
             .filterRelevantKeyguardState()
-            .sample(startedKeyguardTransitionStep)
+            .sample(transitionInteractor.startedKeyguardTransitionStep)
             .map(modeOnCanceledFromStartedStep)
             .collect { modeOnCanceled ->
                 startTransitionTo(
-                    toState = transitionInteractor.asleepKeyguardState.value,
+                    toState = keyguardInteractor.asleepKeyguardState.value,
                     modeOnCanceled = modeOnCanceled,
                     ownerReason = "Sleep transition triggered"
                 )
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 25b2b7c..a09cd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -63,10 +63,13 @@
 ) {
     private val defaultSurfaceBehindVisibility =
         combine(
-            transitionInteractor.finishedKeyguardState,
+            transitionInteractor.isFinishedIn(
+                scene = Scenes.Gone,
+                stateWithoutSceneContainer = KeyguardState.GONE
+            ),
             wakeToGoneInteractor.canWakeDirectlyToGone,
-        ) { finishedState, canWakeDirectlyToGone ->
-            isSurfaceVisible(finishedState) || canWakeDirectlyToGone
+        ) { isOnGone, canWakeDirectlyToGone ->
+            isOnGone || canWakeDirectlyToGone
         }
 
     /**
@@ -124,8 +127,8 @@
                     when (transitionState) {
                         is ObservableTransitionState.Transition ->
                             when {
-                                transitionState.fromScene == Scenes.Lockscreen &&
-                                    transitionState.toScene == Scenes.Gone ->
+                                transitionState.fromContent == Scenes.Lockscreen &&
+                                    transitionState.toContent == Scenes.Gone ->
                                     sceneInteractor
                                         .get()
                                         .isTransitionUserInputOngoing
@@ -136,8 +139,8 @@
                                                 flowOf(true)
                                             }
                                         }
-                                transitionState.fromScene == Scenes.Bouncer &&
-                                    transitionState.toScene == Scenes.Gone ->
+                                transitionState.fromContent == Scenes.Bouncer &&
+                                    transitionState.toContent == Scenes.Gone ->
                                     transitionState.progress.map { progress ->
                                         progress >
                                             FromPrimaryBouncerTransitionInteractor
@@ -196,18 +199,20 @@
                         edge = Edge.create(to = Scenes.Gone),
                         edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
                     ),
-                    transitionInteractor.finishedKeyguardState,
+                    transitionInteractor.isFinishedIn(
+                        scene = Scenes.Gone,
+                        stateWithoutSceneContainer = KeyguardState.GONE
+                    ),
                     surfaceBehindInteractor.isAnimatingSurface,
                     notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
-                ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+                ) { isInTransitionToGone, isOnGone, isAnimatingSurface, notifLaunchRunning ->
                     // Using the animation if we're animating it directly, or if the
                     // ActivityLaunchAnimator is in the process of animating it.
                     val animationsRunning = isAnimatingSurface || notifLaunchRunning
                     // We may still be animating the surface after the keyguard is fully GONE, since
                     // some animations (like the translation spring) are not tied directly to the
                     // transition step amount.
-                    isInTransitionToGone ||
-                        (finishedState == KeyguardState.GONE && animationsRunning)
+                    isInTransitionToGone || (isOnGone && animationsRunning)
                 }
                 .distinctUntilChanged()
         }
@@ -248,7 +253,7 @@
                         // transition. Same for waking directly to gone, due to the lockscreen being
                         // disabled or because the device was woken back up before the lock timeout
                         // duration elapsed.
-                        KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+                        false
                     } else if (canWakeDirectlyToGone) {
                         // Never show the lockscreen if we can wake directly to GONE. This means
                         // that the lock timeout has not yet elapsed, or the keyguard is disabled.
@@ -274,8 +279,7 @@
                         // *not* play the going away animation or related animations.
                         false
                     } else {
-                        // Otherwise, use the visibility of the current state.
-                        KeyguardState.lockscreenVisibleInState(currentState)
+                        currentState != KeyguardState.GONE
                     }
                 }
                 .distinctUntilChanged()
@@ -302,10 +306,4 @@
                     !BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode)
             }
             .distinctUntilChanged()
-
-    companion object {
-        fun isSurfaceVisible(state: KeyguardState): Boolean {
-            return !KeyguardState.lockscreenVisibleInState(state)
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index b850095..f3bb829 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -111,12 +111,12 @@
         if (currentTransitionId == null) return
         if (prevTransition !is ObservableTransitionState.Transition) return
 
-        if (idle.currentScene == prevTransition.toScene) {
+        if (idle.currentScene == prevTransition.toContent) {
             finishCurrentTransition()
         } else {
             val targetState =
                 if (idle.currentScene == Scenes.Lockscreen) {
-                    transitionInteractor.getStartedFromState()
+                    transitionInteractor.startedKeyguardTransitionStep.value.from
                 } else {
                     UNDEFINED
                 }
@@ -150,17 +150,17 @@
     }
 
     private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
-        if (transition.fromScene == Scenes.Lockscreen) {
+        if (transition.fromContent == Scenes.Lockscreen) {
             if (currentTransitionId != null) {
                 val currentToState =
                     internalTransitionInteractor.currentTransitionInfoInternal.value.to
                 if (currentToState == UNDEFINED) {
-                    transitionKtfTo(transitionInteractor.getStartedFromState())
+                    transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
                 }
             }
             startTransitionFromLockscreen()
             collectProgress(transition)
-        } else if (transition.toScene == Scenes.Lockscreen) {
+        } else if (transition.toContent == Scenes.Lockscreen) {
             if (currentTransitionId != null) {
                 transitionKtfTo(UNDEFINED)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 24db3c2..080ddfd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -156,12 +156,6 @@
 
     companion object {
 
-        /** Whether the lockscreen is visible when we're FINISHED in the given state. */
-        fun lockscreenVisibleInState(state: KeyguardState): Boolean {
-            // TODO(b/349784682): Transform deprecated states for Flexiglass
-            return state != GONE
-        }
-
         /**
          * Whether the device is awake ([PowerInteractor.isAwake]) when we're FINISHED in the given
          * keyguard state.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
index 2581b59..1bbe8438 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt
@@ -16,7 +16,8 @@
 
 package com.android.systemui.keyguard.shared.quickaffordance
 
-import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
 
 /** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
 enum class KeyguardQuickAffordancePosition {
@@ -25,8 +26,19 @@
 
     fun toSlotId(): String {
         return when (this) {
-            BOTTOM_START -> KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
-            BOTTOM_END -> KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END
+            BOTTOM_START -> SLOT_ID_BOTTOM_START
+            BOTTOM_END -> SLOT_ID_BOTTOM_END
         }
     }
+
+    companion object {
+
+        /** If the slot ID does not match any string, return null. */
+        fun parseKeyguardQuickAffordancePosition(slotId: String): KeyguardQuickAffordancePosition? =
+            when (slotId) {
+                SLOT_ID_BOTTOM_START -> BOTTOM_START
+                SLOT_ID_BOTTOM_END -> BOTTOM_END
+                else -> null
+            }
+    }
 }
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 9dc77d3..fb97191 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
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @ExperimentalCoroutinesApi
@@ -52,7 +53,11 @@
                     }
                 }
 
-                launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+                if (SceneContainerFlag.isEnabled) {
+                    view.alpha = 1f
+                } else {
+                    launch("$TAG#viewModel.alpha") { viewModel.alpha.collect { view.alpha = it } }
+                }
             }
         }
 
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 a250b22..91a7f7f 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
@@ -37,13 +37,13 @@
 import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder
 import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay
 import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scrim.ScrimView
 import dagger.Lazy
 import javax.inject.Inject
@@ -67,7 +67,6 @@
     private val alternateBouncerDependencies: Lazy<AlternateBouncerDependencies>,
     private val windowManager: Lazy<WindowManager>,
     private val layoutInflater: Lazy<LayoutInflater>,
-    private val dismissCallbackRegistry: DismissCallbackRegistry,
 ) : CoreStartable {
     private val layoutParams: WindowManager.LayoutParams
         get() =
@@ -95,9 +94,10 @@
     private var alternateBouncerView: ConstraintLayout? = null
 
     override fun start() {
-        if (!DeviceEntryUdfpsRefactor.isEnabled) {
+        if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) {
             return
         }
+
         applicationScope.launch("$TAG#alternateBouncerWindowViewModel") {
             alternateBouncerWindowViewModel.get().alternateBouncerWindowRequired.collect {
                 addAlternateBouncerWindowView ->
@@ -110,7 +110,7 @@
                     bind(alternateBouncerView!!, alternateBouncerDependencies.get())
                 } else {
                     removeViewFromWindowManager()
-                    alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
+                    alternateBouncerDependencies.get().viewModel.onRemovedFromWindow()
                 }
             }
         }
@@ -144,7 +144,7 @@
     private val onAttachAddBackGestureHandler =
         object : View.OnAttachStateChangeListener {
             private val onBackInvokedCallback: OnBackInvokedCallback = OnBackInvokedCallback {
-                onBackRequested()
+                alternateBouncerDependencies.get().viewModel.onBackRequested()
             }
 
             override fun onViewAttachedToWindow(view: View) {
@@ -161,14 +161,12 @@
                     .findOnBackInvokedDispatcher()
                     ?.unregisterOnBackInvokedCallback(onBackInvokedCallback)
             }
-
-            fun onBackRequested() {
-                alternateBouncerDependencies.get().viewModel.hideAlternateBouncer()
-                dismissCallbackRegistry.notifyDismissCancelled()
-            }
         }
 
     private fun addViewToWindowManager() {
+        if (SceneContainerFlag.isEnabled) {
+            return
+        }
         if (alternateBouncerView != null) {
             return
         }
@@ -190,6 +188,7 @@
         if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) {
             return
         }
+
         optionallyAddUdfpsViews(
             view = view,
             udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
@@ -202,12 +201,13 @@
             viewModel = alternateBouncerDependencies.messageAreaViewModel,
         )
 
-        val scrim = view.requireViewById(R.id.alternate_bouncer_scrim) as ScrimView
+        val scrim: ScrimView = view.requireViewById(R.id.alternate_bouncer_scrim)
         val viewModel = alternateBouncerDependencies.viewModel
         val swipeUpAnywhereGestureHandler =
             alternateBouncerDependencies.swipeUpAnywhereGestureHandler
         val tapGestureDetector = alternateBouncerDependencies.tapGestureDetector
-        view.repeatWhenAttached { alternateBouncerViewContainer ->
+
+        view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch("$TAG#viewModel.registerForDismissGestures") {
                         viewModel.registerForDismissGestures.collect { registerForDismissGestures ->
@@ -216,11 +216,11 @@
                                     swipeTag
                                 ) { _ ->
                                     alternateBouncerDependencies.powerInteractor.onUserTouch()
-                                    viewModel.showPrimaryBouncer()
+                                    viewModel.onTapped()
                                 }
                                 tapGestureDetector.addOnGestureDetectedCallback(tapTag) { _ ->
                                     alternateBouncerDependencies.powerInteractor.onUserTouch()
-                                    viewModel.showPrimaryBouncer()
+                                    viewModel.onTapped()
                                 }
                             } else {
                                 swipeUpAnywhereGestureHandler.removeOnGestureDetectedCallback(
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 bec8f3d..f1b9cba 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
@@ -93,7 +93,7 @@
                                         blueprint.applyConstraints(this)
                                     }
 
-                                logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+                                logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
                                 cs.applyTo(constraintLayout)
                             }
                         }
@@ -115,7 +115,7 @@
                                     clone(constraintLayout)
                                     blueprint.applyConstraints(this)
                                 }
-                            logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
+                            logAlphaVisibilityScaleOfAppliedConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
                         }
                     }
@@ -124,7 +124,7 @@
         }
     }
 
-    private fun logAlphaVisibilityOfAppliedConstraintSet(
+    private fun logAlphaVisibilityScaleOfAppliedConstraintSet(
         cs: ConstraintSet,
         viewModel: KeyguardClockViewModel
     ) {
@@ -136,12 +136,15 @@
         Log.i(
             TAG,
             "applyCsToSmallClock: vis=${cs.getVisibility(smallClockViewId)} " +
-                "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha}"
+                "alpha=${cs.getConstraint(smallClockViewId).propertySet.alpha} " +
+                "scale=${cs.getConstraint(smallClockViewId).transform.scaleX} "
         )
         Log.i(
             TAG,
             "applyCsToLargeClock: vis=${cs.getVisibility(largeClockViewId)} " +
-                "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha}"
+                "alpha=${cs.getConstraint(largeClockViewId).propertySet.alpha} " +
+                "scale=${cs.getConstraint(largeClockViewId).transform.scaleX} " +
+                "pivotX=${cs.getConstraint(largeClockViewId).transform.transformPivotX} "
         )
         Log.i(
             TAG,
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 830ef3b..76d3389 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
@@ -26,8 +26,8 @@
 import com.android.systemui.common.ui.view.LongPressHandlingView
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.res.R
 
 object KeyguardLongPressViewBinder {
     /**
@@ -46,7 +46,6 @@
         onSingleTap: () -> Unit,
         falsingManager: FalsingManager,
     ) {
-        view.contentDescription = view.resources.getString(R.string.accessibility_desc_lock_screen)
         view.accessibilityHintLongPressAction =
             AccessibilityNodeInfo.AccessibilityAction(
                 AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
@@ -54,8 +53,15 @@
             )
         view.listener =
             object : LongPressHandlingView.Listener {
-                override fun onLongPressDetected(view: View, x: Int, y: Int, isA11yAction: Boolean) {
-                    if (!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+                override fun onLongPressDetected(
+                    view: View,
+                    x: Int,
+                    y: Int,
+                    isA11yAction: Boolean
+                ) {
+                    if (
+                        !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
+                    ) {
                         return
                     }
 
@@ -76,6 +82,12 @@
                 launch("$TAG#viewModel.isLongPressHandlingEnabled") {
                     viewModel.isLongPressHandlingEnabled.collect { isEnabled ->
                         view.setLongPressHandlingEnabled(isEnabled)
+                        view.contentDescription =
+                            if (isEnabled) {
+                                view.resources.getString(R.string.accessibility_desc_lock_screen)
+                            } else {
+                                null
+                            }
                     }
                 }
             }
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 15e6b1d..27dd18d 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
@@ -92,9 +92,9 @@
         val button = view as ImageView
         val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
         val disposableHandle =
-            view.repeatWhenAttached(mainImmediateDispatcher) {
+            view.repeatWhenAttached {
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    launch("$TAG#viewModel") {
+                    launch {
                         viewModel.collect { buttonModel ->
                             updateButton(
                                 view = button,
@@ -104,7 +104,7 @@
                         }
                     }
 
-                    launch("$TAG#updateButtonAlpha") {
+                    launch {
                         updateButtonAlpha(
                             view = button,
                             viewModel = viewModel,
@@ -112,7 +112,7 @@
                         )
                     }
 
-                    launch("$TAG#configurationBasedDimensions") {
+                    launch {
                         configurationBasedDimensions.collect { dimensions ->
                             button.updateLayoutParams<ViewGroup.LayoutParams> {
                                 width = dimensions.buttonSizePx.width
@@ -141,6 +141,7 @@
         viewModel: KeyguardQuickAffordanceViewModel,
         messageDisplayer: (Int) -> Unit,
     ) {
+        logger.logUpdate(viewModel)
         if (!viewModel.isVisible) {
             view.isInvisible = true
             return
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 aab5b9b..5bb7b64 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
@@ -40,6 +40,8 @@
 import com.android.app.tracing.coroutines.launch
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags.msdlFeedback
 import com.android.systemui.Flags.newAodTransition
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
@@ -79,6 +81,8 @@
 import com.android.systemui.util.ui.isAnimating
 import com.android.systemui.util.ui.stopAnimating
 import com.android.systemui.util.ui.value
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
 import kotlin.math.min
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
@@ -112,6 +116,7 @@
         falsingManager: FalsingManager?,
         keyguardViewMediator: KeyguardViewMediator?,
         mainImmediateDispatcher: CoroutineDispatcher,
+        msdlPlayer: MSDLPlayer?,
     ): DisposableHandle {
         val disposables = DisposableHandles()
         val childViews = mutableMapOf<Int, View>()
@@ -152,6 +157,62 @@
                             }
                         }
                     }
+
+                    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
+                                }
+                            }
+                        }
+                    }
+
+                    if (MigrateClocksToBlueprint.isEnabled) {
+                        launch("$TAG#translationY") {
+                            // When translation happens in burnInLayer, it won't be weather clock
+                            // large clock isn't added to burnInLayer due to its scale transition
+                            // so we also need to add translation to it here
+                            // same as translationX
+                            viewModel.translationY.collect { y ->
+                                childViews[burnInLayerId]?.translationY = y
+                                childViews[largeClockId]?.translationY = y
+                                childViews[aodNotificationIconContainerId]?.translationY = y
+                            }
+                        }
+
+                        launch("$TAG#translationX") {
+                            viewModel.translationX.collect { state ->
+                                val px = state.value ?: return@collect
+                                when {
+                                    state.isToOrFrom(KeyguardState.AOD) -> {
+                                        // Large Clock is not translated in the x direction
+                                        childViews[burnInLayerId]?.translationX = px
+                                        childViews[aodNotificationIconContainerId]?.translationX =
+                                            px
+                                    }
+                                    state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
+                                        for ((key, childView) in childViews.entries) {
+                                            when (key) {
+                                                indicationArea,
+                                                startButton,
+                                                endButton,
+                                                lockIcon,
+                                                deviceEntryIcon -> {
+                                                    // Do not move these views
+                                                }
+                                                else -> childView.translationX = px
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
                 }
             }
         disposables +=
@@ -188,20 +249,6 @@
                         }
                     }
 
-                    if (
-                        KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled
-                    ) {
-                        launch {
-                            viewModel.alpha(viewState).collect { alpha ->
-                                view.alpha = alpha
-                                if (KeyguardBottomAreaRefactor.isEnabled) {
-                                    childViews[statusViewId]?.alpha = alpha
-                                    childViews[burnInLayerId]?.alpha = alpha
-                                }
-                            }
-                        }
-                    }
-
                     if (MigrateClocksToBlueprint.isEnabled) {
                         launch {
                             viewModel.burnInLayerVisibility.collect { visibility ->
@@ -222,46 +269,6 @@
                         }
 
                         launch {
-                            // When translation happens in burnInLayer, it won't be weather clock
-                            // large clock isn't added to burnInLayer due to its scale transition
-                            // so we also need to add translation to it here
-                            // same as translationX
-                            viewModel.translationY.collect { y ->
-                                childViews[burnInLayerId]?.translationY = y
-                                childViews[largeClockId]?.translationY = y
-                                childViews[aodNotificationIconContainerId]?.translationY = y
-                            }
-                        }
-
-                        launch {
-                            viewModel.translationX.collect { state ->
-                                val px = state.value ?: return@collect
-                                when {
-                                    state.isToOrFrom(KeyguardState.AOD) -> {
-                                        // Large Clock is not translated in the x direction
-                                        childViews[burnInLayerId]?.translationX = px
-                                        childViews[aodNotificationIconContainerId]?.translationX =
-                                            px
-                                    }
-                                    state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
-                                        for ((key, childView) in childViews.entries) {
-                                            when (key) {
-                                                indicationArea,
-                                                startButton,
-                                                endButton,
-                                                lockIcon,
-                                                deviceEntryIcon -> {
-                                                    // Do not move these views
-                                                }
-                                                else -> childView.translationX = px
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-
-                        launch {
                             viewModel.scale.collect { scaleViewModel ->
                                 if (scaleViewModel.scaleClockOnly) {
                                     // For clocks except weather clock, we have scale transition
@@ -349,21 +356,33 @@
                     if (deviceEntryHapticsInteractor != null && vibratorHelper != null) {
                         launch {
                             deviceEntryHapticsInteractor.playSuccessHaptic.collect {
-                                vibratorHelper.performHapticFeedback(
-                                    view,
-                                    HapticFeedbackConstants.CONFIRM,
-                                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
-                                )
+                                if (msdlFeedback()) {
+                                    msdlPlayer?.playToken(
+                                        MSDLToken.UNLOCK,
+                                        authInteractionProperties
+                                    )
+                                } else {
+                                    vibratorHelper.performHapticFeedback(
+                                        view,
+                                        HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+                                    )
+                                }
                             }
                         }
 
                         launch {
                             deviceEntryHapticsInteractor.playErrorHaptic.collect {
-                                vibratorHelper.performHapticFeedback(
-                                    view,
-                                    HapticFeedbackConstants.REJECT,
-                                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING,
-                                )
+                                if (msdlFeedback()) {
+                                    msdlPlayer?.playToken(
+                                        MSDLToken.FAILURE,
+                                        authInteractionProperties
+                                    )
+                                } else {
+                                    vibratorHelper.performHapticFeedback(
+                                        view,
+                                        HapticFeedbackConstants.BIOMETRIC_REJECT,
+                                    )
+                                }
                             }
                         }
                     }
@@ -632,6 +651,7 @@
     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()
 
     private const val ID = "occluding_app_device_entry_unlock_msg"
     private const val AOD_ICONS_APPEAR_DURATION: Long = 200
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
index fb6efd3..3b36762 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindParamsApplier.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.keyguard.TAG
 import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
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 6031ef6..f581a2e 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
@@ -79,6 +79,7 @@
 import com.android.systemui.monet.ColorScheme
 import com.android.systemui.monet.Style
 import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shared.clocks.ClockRegistry
@@ -188,6 +189,7 @@
     init {
         coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job())
         disposables += DisposableHandle { coroutineScope.cancel() }
+        clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData())
 
         if (KeyguardBottomAreaRefactor.isEnabled) {
             quickAffordancesCombinedViewModel.enablePreviewMode(
@@ -275,6 +277,15 @@
         }
     }
 
+    fun onStartCustomizingQuickAffordances(
+        initiallySelectedSlotId: String?,
+    ) {
+        quickAffordancesCombinedViewModel.enablePreviewMode(
+            initiallySelectedSlotId = initiallySelectedSlotId,
+            shouldHighlightSelectedAffordance = true,
+        )
+    }
+
     fun onSlotSelected(slotId: String) {
         if (KeyguardBottomAreaRefactor.isEnabled) {
             quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId)
@@ -283,6 +294,21 @@
         }
     }
 
+    fun onPreviewQuickAffordanceSelected(slotId: String, quickAffordanceId: String) {
+        quickAffordancesCombinedViewModel.onPreviewQuickAffordanceSelected(
+            slotId,
+            quickAffordanceId,
+        )
+    }
+
+    fun onDefaultPreview() {
+        quickAffordancesCombinedViewModel.onClearPreviewQuickAffordances()
+        quickAffordancesCombinedViewModel.enablePreviewMode(
+            initiallySelectedSlotId = null,
+            shouldHighlightSelectedAffordance = false,
+        )
+    }
+
     fun destroy() {
         isDestroyed = true
         lockscreenSmartspaceController.disconnect()
@@ -392,6 +418,7 @@
                     null, // falsing manager not required for preview mode
                     null, // keyguard view mediator is not required for preview mode
                     mainDispatcher,
+                    null,
                 )
         }
         rootView.addView(
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 0532ee2..075a1d2 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
@@ -26,12 +26,20 @@
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.app.tracing.coroutines.runBlocking
-import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
+import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_QUICK_AFFORDANCE_ID
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_SLOT_ID
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_DEFAULT_PREVIEW
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED
+import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES
 import com.android.systemui.util.kotlin.logD
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -58,11 +66,7 @@
         var observer: PreviewLifecycleObserver? = null
         return try {
             val renderer =
-                if (Flags.lockscreenPreviewRendererCreateOnMainThread()) {
-                    runBlocking ("$TAG#previewRendererFactory.create", mainDispatcher) {
-                        previewRendererFactory.create(request)
-                    }
-                } else {
+                runBlocking("$TAG#previewRendererFactory.create", mainDispatcher) {
                     previewRendererFactory.create(request)
                 }
 
@@ -157,16 +161,35 @@
         }
 
         when (message.what) {
-            KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
-                message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
+            MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES -> {
+                checkNotNull(renderer)
+                    .onStartCustomizingQuickAffordances(
+                        initiallySelectedSlotId =
+                            message.data.getString(KEY_INITIALLY_SELECTED_SLOT_ID)
+                                ?: SLOT_ID_BOTTOM_START
+                    )
+            }
+            MESSAGE_ID_SLOT_SELECTED -> {
+                message.data.getString(KEY_SLOT_ID)?.let { slotId ->
                     checkNotNull(renderer).onSlotSelected(slotId = slotId)
                 }
             }
-            KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> {
-                checkNotNull(renderer)
-                    .hideSmartspace(
-                        message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
-                    )
+            MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED -> {
+                val slotId = message.data.getString(KEY_SLOT_ID)
+                val quickAffordanceId = message.data.getString(KEY_QUICK_AFFORDANCE_ID)
+                if (slotId != null && quickAffordanceId != null) {
+                    checkNotNull(renderer)
+                        .onPreviewQuickAffordanceSelected(
+                            slotId = slotId,
+                            quickAffordanceId = quickAffordanceId,
+                        )
+                }
+            }
+            MESSAGE_ID_DEFAULT_PREVIEW -> {
+                checkNotNull(renderer).onDefaultPreview()
+            }
+            MESSAGE_ID_HIDE_SMART_SPACE -> {
+                checkNotNull(renderer).hideSmartspace(message.data.getBoolean(KEY_HIDE_SMART_SPACE))
             }
             else -> checkNotNull(onDestroy).invoke(this)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
index 0032c2f..e2ad4635 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/DeviceEntryIconTransitionModule.kt
@@ -46,6 +46,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToDozingTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToGlanceableHubTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewModel
@@ -205,6 +206,12 @@
 
     @Binds
     @IntoSet
+    abstract fun occludedToDozing(
+        impl: OccludedToDozingTransitionViewModel
+    ): DeviceEntryIconTransition
+
+    @Binds
+    @IntoSet
     abstract fun occludedToLockscreen(
         impl: OccludedToLockscreenTransitionViewModel
     ): DeviceEntryIconTransition
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
index a4137ac..8861c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutSection.kt
@@ -4,10 +4,10 @@
 import android.widget.ImageView
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.content.res.ResourcesCompat
-import com.android.systemui.res.R
 import com.android.systemui.animation.view.LaunchableImageView
 import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.res.R
 
 abstract class BaseShortcutSection : KeyguardSection() {
     protected var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
@@ -15,7 +15,9 @@
 
     override fun removeViews(constraintLayout: ConstraintLayout) {
         leftShortcutHandle?.destroy()
+        leftShortcutHandle = null
         rightShortcutHandle?.destroy()
+        rightShortcutHandle = null
         constraintLayout.removeView(R.id.start_button)
         constraintLayout.removeView(R.id.end_button)
     }
@@ -75,6 +77,7 @@
             }
         constraintLayout.addView(view)
     }
+
     /**
      * Defines equality as same class.
      *
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 c8fe55d..be6b0eb 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
@@ -59,6 +59,16 @@
     alpha: Float,
 ) = views.forEach { view -> this.setAlpha(view.id, alpha) }
 
+internal fun ConstraintSet.setScaleX(
+    views: Iterable<View>,
+    alpha: Float,
+) = views.forEach { view -> this.setScaleX(view.id, alpha) }
+
+internal fun ConstraintSet.setScaleY(
+    views: Iterable<View>,
+    alpha: Float,
+) = views.forEach { view -> this.setScaleY(view.id, alpha) }
+
 @SysUISingleton
 class ClockSection
 @Inject
@@ -125,6 +135,9 @@
             setAlpha(getNonTargetClockFace(clock).views, 0F)
             if (!keyguardClockViewModel.isLargeClockVisible.value) {
                 connect(sharedR.id.bc_smartspace_view, TOP, sharedR.id.date_smartspace_view, BOTTOM)
+            } else {
+                setScaleX(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
+                setScaleY(getTargetClockFace(clock).views, rootViewModel.burnInModel.value.scale)
             }
         }
     }
@@ -205,6 +218,9 @@
             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)
+
+            // Explicitly clear pivot to force recalculate pivot instead of using legacy value
+            setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN)
         }
 
         constrainWeatherClockDateIconsBarrier(constraints)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index e558033..6c6e14c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -33,16 +33,19 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.KeyguardIndicationController
 import dagger.Lazy
 import javax.inject.Inject
+import javax.inject.Named
 
 class DefaultShortcutsSection
 @Inject
 constructor(
     @Main private val resources: Resources,
+    @Named(LOCKSCREEN_INSTANCE)
     private val keyguardQuickAffordancesCombinedViewModel:
         KeyguardQuickAffordancesCombinedViewModel,
     private val keyguardRootViewModel: KeyguardRootViewModel,
@@ -76,6 +79,7 @@
 
     override fun bindData(constraintLayout: ConstraintLayout) {
         if (KeyguardBottomAreaRefactor.isEnabled) {
+            leftShortcutHandle?.destroy()
             leftShortcutHandle =
                 keyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.start_button),
@@ -84,6 +88,7 @@
                 ) {
                     indicationController.showTransientIndication(it)
                 }
+            rightShortcutHandle?.destroy()
             rightShortcutHandle =
                 keyguardQuickAffordanceViewBinder.bind(
                     constraintLayout.requireViewById(R.id.end_button),
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 55fc718..99160f8 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
@@ -127,6 +127,7 @@
 
             // migrate addSmartspaceView from KeyguardClockSwitchController
             constrainHeight(sharedR.id.bc_smartspace_view, ConstraintSet.WRAP_CONTENT)
+            constrainWidth(sharedR.id.bc_smartspace_view, ConstraintSet.MATCH_CONSTRAINT)
             connect(
                 sharedR.id.bc_smartspace_view,
                 ConstraintSet.START,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
index 34c1436..38f5d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
@@ -50,5 +50,9 @@
             }
             .distinctUntilChanged()
 
-    fun openCommunalHub() = communalInteractor.changeScene(CommunalScenes.Communal)
+    fun openCommunalHub() =
+        communalInteractor.changeScene(
+            newScene = CommunalScenes.Communal,
+            loggingReason = "accessibility",
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index df0b3dc..4908dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shared.recents.utilities.Utilities.clamp
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,6 +51,7 @@
     private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
     val alpha: Flow<Float> =
         alternateBouncerViewModel.transitionToAlternateBouncerProgress.map {
+            SceneContainerFlag.assertInLegacyMode()
             clamp(it * 2f, 0f, 1f)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index 470f17b..b5d9e2a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -18,15 +18,21 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.graphics.Color
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
 
 @ExperimentalCoroutinesApi
 class AlternateBouncerViewModel
@@ -34,12 +40,19 @@
 constructor(
     private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    private val dismissCallbackRegistry: DismissCallbackRegistry,
+    alternateBouncerInteractor: Lazy<AlternateBouncerInteractor>,
+    private val primaryBouncerInteractor: PrimaryBouncerInteractor,
 ) {
     // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be:
     private val alternateBouncerScrimAlpha = .66f
 
+    /** Reports the alternate bouncer visible state if the scene container flag is enabled. */
+    val isVisible: Flow<Boolean> =
+        alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.assertInNewMode() }
+
     /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
-    val transitionToAlternateBouncerProgress =
+    val transitionToAlternateBouncerProgress: Flow<Float> =
         keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER)
 
     /** An observable for the scrim alpha. */
@@ -51,11 +64,17 @@
     val registerForDismissGestures: Flow<Boolean> =
         transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()
 
-    fun showPrimaryBouncer() {
+    fun onTapped() {
         statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
     }
 
-    fun hideAlternateBouncer() {
+    fun onRemovedFromWindow() {
         statusBarKeyguardViewManager.hideAlternateBouncer(false)
     }
+
+    fun onBackRequested() {
+        statusBarKeyguardViewManager.hideAlternateBouncer(false)
+        dismissCallbackRegistry.notifyDismissCancelled()
+        primaryBouncerInteractor.setDismissAction(null, null)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 6f8389f..a021de4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -43,21 +43,21 @@
     val deviceEntryIconViewModel: DeviceEntryIconViewModel,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     configurationInteractor: ConfigurationInteractor,
-    lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
-    aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
-    goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
-    primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
-    occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
-    occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
-    dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
     alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
-    goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
-    goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
-    primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
-    lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
-    dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
     alternateBouncerToDozingTransitionViewModel: AlternateBouncerToDozingTransitionViewModel,
+    aodToLockscreenTransitionViewModel: AodToLockscreenTransitionViewModel,
+    dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
     dreamingToAodTransitionViewModel: DreamingToAodTransitionViewModel,
+    dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
+    goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+    goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+    goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel,
+    lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+    occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+    occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel,
+    occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
+    primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
+    primaryBouncerToDozingTransitionViewModel: PrimaryBouncerToDozingTransitionViewModel,
     primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel,
 ) {
     val color: Flow<Int> =
@@ -97,12 +97,12 @@
                         goneToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         goneToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         primaryBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
-                        lockscreenToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         dozingToLockscreenTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         alternateBouncerToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         dreamingToAodTransitionViewModel.deviceEntryBackgroundViewAlpha,
                         primaryBouncerToLockscreenTransitionViewModel
                             .deviceEntryBackgroundViewAlpha,
+                        occludedToDozingTransitionViewModel.deviceEntryBackgroundViewAlpha,
                     )
                     .merge()
                     .onStart {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
index 06b76b3..87c32a5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt
@@ -53,10 +53,10 @@
 ) {
     private val isShowingAodOrDozing: Flow<Boolean> =
         combine(
-            transitionInteractor.startedKeyguardState,
+            transitionInteractor.startedKeyguardTransitionStep,
             transitionInteractor.transitionValue(KeyguardState.DOZING),
-        ) { startedKeyguardState, dozingTransitionValue ->
-            startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
+        ) { startedKeyguardStep, dozingTransitionValue ->
+            startedKeyguardStep.to == KeyguardState.AOD || dozingTransitionValue == 1f
         }
 
     private fun getColor(usingBackgroundProtection: Boolean): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 5ce1b5e..d3bb4f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -83,8 +83,8 @@
     private val intEvaluator = IntEvaluator()
     private val floatEvaluator = FloatEvaluator()
     private val showingAlternateBouncer: Flow<Boolean> =
-        transitionInteractor.startedKeyguardState.map { keyguardState ->
-            keyguardState == KeyguardState.ALTERNATE_BOUNCER
+        transitionInteractor.startedKeyguardTransitionStep.map { keyguardStep ->
+            keyguardStep.to == KeyguardState.ALTERNATE_BOUNCER
         }
     private val qsProgress: Flow<Float> = shadeInteractor.qsExpansion.onStart { emit(0f) }
     private val shadeExpansion: Flow<Float> = shadeInteractor.shadeExpansion.onStart { emit(0f) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index a460d51..9d8a7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -51,7 +51,8 @@
             onCancel = { 0f },
         )
 
-    val lockscreenAlpha: Flow<Float> = shortcutsAlpha
+    // Show immediately to avoid what can appear to be a flicker on device wakeup
+    val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f)
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index b5ec7a6..10605b2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,6 @@
 
 import com.android.app.animation.Interpolators.EMPHASIZED
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -39,10 +38,8 @@
 class DreamingToLockscreenTransitionViewModel
 @Inject
 constructor(
-    private val fromDreamingTransitionInteractor: FromDreamingTransitionInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
 ) : DeviceEntryIconTransition {
-    fun startTransition() = fromDreamingTransitionInteractor.startToLockscreenTransition()
 
     private val transitionAnimation =
         animationFlow.setup(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 609b571d..ceae1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import javax.inject.Inject
+import javax.inject.Named
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -50,6 +51,7 @@
     keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel,
     private val burnInHelperWrapper: BurnInHelperWrapper,
     burnInInteractor: BurnInInteractor,
+    @Named(KeyguardQuickAffordancesCombinedViewModelModule.Companion.LOCKSCREEN_INSTANCE)
     shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
     configurationInteractor: ConfigurationInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
index 0a84886..6579ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.content.Context
+import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 import com.android.systemui.res.R
@@ -81,7 +82,7 @@
                 getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
             } else {
                 getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
-                    getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard) +
+                    SystemBarUtils.getStatusBarHeight(context) +
                     getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
             }
         }
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 2426f97..7e13d22 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
@@ -28,7 +28,9 @@
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags
 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -83,9 +85,7 @@
     private val previewMode = MutableStateFlow(PreviewMode())
 
     private val showingLockscreen: Flow<Boolean> =
-        transitionInteractor.finishedKeyguardState.map { keyguardState ->
-            keyguardState == KeyguardState.LOCKSCREEN
-        }
+        transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN)
 
     /** The only time the expansion is important is while lockscreen is actively displayed */
     private val shadeExpansionAlpha =
@@ -164,13 +164,50 @@
             .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD }
             .distinctUntilChanged()
 
+    private val previewAffordances =
+        MutableStateFlow<Map<KeyguardQuickAffordancePosition, String>>(emptyMap())
+
     /** An observable for the view-model of the "start button" quick affordance. */
     val startButton: Flow<KeyguardQuickAffordanceViewModel> =
-        button(KeyguardQuickAffordancePosition.BOTTOM_START)
+        if (Flags.newCustomizationPickerUi()) {
+            previewAffordances.flatMapLatestConflated {
+                button(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                    overrideQuickAffordanceId = it[KeyguardQuickAffordancePosition.BOTTOM_START],
+                )
+            }
+        } else {
+            button(KeyguardQuickAffordancePosition.BOTTOM_START)
+        }
+        .stateIn(
+            scope = applicationScope,
+            started = SharingStarted.WhileSubscribed(),
+            initialValue =
+                KeyguardQuickAffordanceViewModel(
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()
+                ),
+        )
 
     /** An observable for the view-model of the "end button" quick affordance. */
     val endButton: Flow<KeyguardQuickAffordanceViewModel> =
-        button(KeyguardQuickAffordancePosition.BOTTOM_END)
+        if (Flags.newCustomizationPickerUi()) {
+            previewAffordances.flatMapLatestConflated {
+                button(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_END,
+                    overrideQuickAffordanceId = it[KeyguardQuickAffordancePosition.BOTTOM_END],
+                )
+            }
+        } else {
+            button(KeyguardQuickAffordancePosition.BOTTOM_END)
+        }
+        .stateIn(
+            scope = applicationScope,
+            started = SharingStarted.WhileSubscribed(),
+            initialValue =
+                KeyguardQuickAffordanceViewModel(
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId()
+                ),
+        )
 
     /**
      * Notifies that a slot with the given ID has been selected in the preview experience that is
@@ -183,6 +220,28 @@
     }
 
     /**
+     * Notifies to preview an affordance at a given slot ID. This is ignored for the real lock
+     * screen experience.
+     */
+    fun onPreviewQuickAffordanceSelected(slotId: String, affordanceId: String) {
+        val position =
+            KeyguardQuickAffordancePosition.parseKeyguardQuickAffordancePosition(slotId) ?: return
+        previewAffordances.value =
+            previewAffordances.value.toMutableMap().let {
+                it[position] = affordanceId
+                HashMap(it)
+            }
+    }
+
+    /**
+     * Notifies to clear up the preview affordances map. This is ignored for the real lock screen
+     * experience.
+     */
+    fun onClearPreviewQuickAffordances() {
+        previewAffordances.value = emptyMap()
+    }
+
+    /**
      * Puts this view-model in "preview mode", which means it's being used for UI that is rendering
      * the lock screen preview in wallpaper picker / settings and not the real experience on the
      * lock screen.
@@ -207,14 +266,16 @@
     }
 
     private fun button(
-        position: KeyguardQuickAffordancePosition
+        position: KeyguardQuickAffordancePosition,
+        overrideQuickAffordanceId: String? = null,
     ): Flow<KeyguardQuickAffordanceViewModel> {
         return previewMode
             .flatMapLatest { previewMode ->
                 combine(
                         if (previewMode.isInPreviewMode) {
                             quickAffordanceInteractor.quickAffordanceAlwaysVisible(
-                                position = position
+                                position = position,
+                                overrideQuickAffordanceId = overrideQuickAffordanceId,
                             )
                         } else {
                             quickAffordanceInteractor.quickAffordance(position = position)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.kt
new file mode 100644
index 0000000..fceacc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelModule.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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+import javax.inject.Named
+
+@Module
+interface KeyguardQuickAffordancesCombinedViewModelModule {
+    companion object {
+        const val LOCKSCREEN_INSTANCE = "lockscreen_instance"
+    }
+
+    @SysUISingleton
+    @Binds
+    @Named(LOCKSCREEN_INSTANCE)
+    fun provideKeyguardQuickAffordancesCombinedViewModel(
+        model: KeyguardQuickAffordancesCombinedViewModel
+    ): KeyguardQuickAffordancesCombinedViewModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 050ef6f..eaa61a1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -33,15 +33,16 @@
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.ui.StateToValue
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ui.viewmodel.NotificationShadeWindowModel
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
@@ -83,6 +84,7 @@
     private val communalInteractor: CommunalInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val notificationsKeyguardInteractor: NotificationsKeyguardInteractor,
+    notificationShadeWindowModel: NotificationShadeWindowModel,
     private val alternateBouncerToAodTransitionViewModel: AlternateBouncerToAodTransitionViewModel,
     private val alternateBouncerToGoneTransitionViewModel:
         AlternateBouncerToGoneTransitionViewModel,
@@ -129,8 +131,8 @@
     val burnInModel = _burnInModel.asStateFlow()
 
     val burnInLayerVisibility: Flow<Int> =
-        keyguardTransitionInteractor.startedKeyguardState
-            .filter { it == AOD || it == LOCKSCREEN }
+        keyguardTransitionInteractor.startedKeyguardTransitionStep
+            .filter { it.to == AOD || it.to == LOCKSCREEN }
             .map { VISIBLE }
 
     val goneToAodTransition =
@@ -159,30 +161,26 @@
 
     private val alphaOnShadeExpansion: Flow<Float> =
         combineTransform(
-                keyguardTransitionInteractor.isInTransition(
-                    edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
-                    edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
-                ),
-                keyguardTransitionInteractor.isInTransition(
-                    edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
-                    edgeWithoutSceneContainer =
-                        Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
+                anyOf(
+                    keyguardTransitionInteractor.isInTransition(
+                        edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+                        edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
+                    ),
+                    keyguardTransitionInteractor.isInTransition(
+                        edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
+                        edgeWithoutSceneContainer =
+                            Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
+                    ),
+                    keyguardTransitionInteractor.isInTransition(
+                        Edge.create(from = LOCKSCREEN, to = DREAMING)
+                    ),
                 ),
                 isOnLockscreen,
                 shadeInteractor.qsExpansion,
                 shadeInteractor.shadeExpansion,
-            ) {
-                lockscreenToGoneTransitionRunning,
-                primaryBouncerToLockscreenTransitionRunning,
-                isOnLockscreen,
-                qsExpansion,
-                shadeExpansion ->
+            ) { disabledTransitionRunning, isOnLockscreen, qsExpansion, shadeExpansion ->
                 // Fade out quickly as the shade expands
-                if (
-                    isOnLockscreen &&
-                        !lockscreenToGoneTransitionRunning &&
-                        !primaryBouncerToLockscreenTransitionRunning
-                ) {
+                if (isOnLockscreen && !disabledTransitionRunning) {
                     val alpha =
                         1f -
                             MathUtils.constrainedMap(
@@ -198,29 +196,18 @@
             .distinctUntilChanged()
 
     /**
-     * Keyguard should not show while the communal hub is fully visible. This check is added since
-     * at the moment, closing the notification shade will cause the keyguard alpha to be set back to
-     * 1. Also ensure keyguard is never visible when GONE.
+     * Keyguard should not show if fully transitioned into a hidden keyguard state or if
+     * transitioning between hidden states.
      */
     private val hideKeyguard: Flow<Boolean> =
-        combine(
-                communalInteractor.isIdleOnCommunal,
-                keyguardTransitionInteractor
-                    .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
-                    .map { it == 1f }
-                    .onStart { emit(false) },
-                keyguardTransitionInteractor
-                    .transitionValue(OCCLUDED)
-                    .map { it == 1f }
-                    .onStart { emit(false) },
-                keyguardTransitionInteractor
-                    .transitionValue(KeyguardState.DREAMING)
-                    .map { it == 1f }
-                    .onStart { emit(false) },
-            ) { isIdleOnCommunal, isGone, isOccluded, isDreaming ->
-                isIdleOnCommunal || isGone || isOccluded || isDreaming
-            }
-            .distinctUntilChanged()
+        anyOf(
+            notificationShadeWindowModel.isKeyguardOccluded,
+            communalInteractor.isIdleOnCommunal,
+            keyguardTransitionInteractor
+                .transitionValue(scene = Scenes.Gone, stateWithoutSceneContainer = GONE)
+                .map { it == 1f }
+                .onStart { emit(false) },
+        )
 
     /** Last point that the root view was tapped */
     val lastRootViewTapPosition: Flow<Point?> = keyguardInteractor.lastRootViewTapPosition
@@ -270,7 +257,7 @@
                         occludedToLockscreenTransitionViewModel.lockscreenAlpha,
                         primaryBouncerToAodTransitionViewModel.lockscreenAlpha,
                         primaryBouncerToGoneTransitionViewModel.lockscreenAlpha,
-                        primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
+                        primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                     )
                     .onStart { emit(1f) }
             ) { hideKeyguard, alpha ->
@@ -326,16 +313,17 @@
                     .transitionValue(LOCKSCREEN)
                     .map { it > 0f }
                     .onStart { emit(false) },
-                keyguardTransitionInteractor.finishedKeyguardState.map {
-                    KeyguardState.lockscreenVisibleInState(it)
-                },
+                keyguardTransitionInteractor.isFinishedIn(
+                    scene = Scenes.Gone,
+                    stateWithoutSceneContainer = GONE
+                ),
                 deviceEntryInteractor.isBypassEnabled,
                 areNotifsFullyHiddenAnimated(),
                 isPulseExpandingAnimated(),
             ) { flows ->
                 val goneToAodTransitionRunning = flows[0] as Boolean
                 val isOnLockscreen = flows[1] as Boolean
-                val onKeyguard = flows[2] as Boolean
+                val isOnGone = flows[2] as Boolean
                 val isBypassEnabled = flows[3] as Boolean
                 val notifsFullyHidden = flows[4] as AnimatedValue<Boolean>
                 val pulseExpanding = flows[5] as AnimatedValue<Boolean>
@@ -345,8 +333,7 @@
                     // animation is playing, in which case we want them to be visible if we're
                     // animating in the AOD UI and will be switching to KEYGUARD shortly.
                     goneToAodTransitionRunning ||
-                        (!onKeyguard &&
-                            !screenOffAnimationController.shouldShowAodIconsWhenShade()) ->
+                        (isOnGone && !screenOffAnimationController.shouldShowAodIconsWhenShade()) ->
                         AnimatedValue.NotAnimating(false)
                     else ->
                         zip(notifsFullyHidden, pulseExpanding) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 59cb6e5..75b1b04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -17,31 +17,29 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.content.res.Resources
-import com.android.compose.animation.scene.ContentKey
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.biometrics.AuthController
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.shared.model.ClockSize
-import com.android.systemui.lifecycle.SysUiViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
-import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -56,7 +54,8 @@
     private val shadeInteractor: ShadeInteractor,
     private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
     private val occlusionInteractor: SceneContainerOcclusionInteractor,
-) : SysUiViewModel() {
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+) : ExclusiveActivatable() {
     @VisibleForTesting val clockSize = clockInteractor.clockSize
 
     val isUdfpsVisible: Boolean
@@ -72,7 +71,11 @@
     /** Whether the content of the scene UI should be shown. */
     val isContentVisible: StateFlow<Boolean> = _isContentVisible.asStateFlow()
 
-    override suspend fun onActivated() {
+    /** @see DeviceEntryInteractor.isBypassEnabled */
+    val isBypassEnabled: StateFlow<Boolean>
+        get() = deviceEntryInteractor.isBypassEnabled
+
+    override suspend fun onActivated(): Nothing {
         coroutineScope {
             launch {
                 combine(
@@ -84,28 +87,21 @@
                             end = end,
                         )
                     }
-                    .collectLatest { _unfoldTranslations.value = it }
+                    .collect { _unfoldTranslations.value = it }
             }
 
             launch {
                 occlusionInteractor.isOccludingActivityShown
                     .map { !it }
-                    .collectLatest { _isContentVisible.value = it }
+                    .collect { _isContentVisible.value = it }
             }
+
+            awaitCancellation()
         }
     }
 
-    /**
-     * Returns a flow that indicates whether lockscreen notifications should be rendered in the
-     * given [contentKey].
-     */
-    fun areNotificationsVisible(contentKey: ContentKey): Flow<Boolean> {
-        // `Scenes.NotificationsShade` renders its own separate notifications stack, so when it's
-        // open we avoid rendering the lockscreen notifications stack.
-        if (contentKey == Scenes.NotificationsShade) {
-            return flowOf(false)
-        }
-
+    /** Returns a flow that indicates whether lockscreen notifications should be rendered. */
+    fun areNotificationsVisible(): Flow<Boolean> {
         return combine(
             clockSize,
             shadeInteractor.isShadeLayoutWide,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
deleted file mode 100644
index 7383f57..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneActionsViewModel.kt
+++ /dev/null
@@ -1,125 +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.keyguard.ui.viewmodel
-
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.communal.domain.interactor.CommunalInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
-import com.android.systemui.util.kotlin.filterValuesNotNull
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-
-/** Models UI state and handles user input for the lockscreen scene. */
-class LockscreenSceneActionsViewModel
-@AssistedInject
-constructor(
-    private val deviceEntryInteractor: DeviceEntryInteractor,
-    private val communalInteractor: CommunalInteractor,
-    private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        shadeInteractor.isShadeTouchable
-            .flatMapLatest { isShadeTouchable ->
-                if (!isShadeTouchable) {
-                    flowOf(emptyMap())
-                } else {
-                    combine(
-                        deviceEntryInteractor.isUnlocked,
-                        communalInteractor.isCommunalAvailable,
-                        shadeInteractor.shadeMode,
-                    ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
-                        val notifShadeSceneKey =
-                            UserActionResult(
-                                toScene = SceneFamilies.NotifShade,
-                                transitionKey =
-                                    ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
-                            )
-
-                        mapOf(
-                                Swipe.Left to
-                                    UserActionResult(Scenes.Communal).takeIf {
-                                        isCommunalAvailable
-                                    },
-                                Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
-
-                                // Swiping down from the top edge goes to QS (or shade if in split
-                                // shade mode).
-                                swipeDownFromTop(pointerCount = 1) to
-                                    if (shadeMode is ShadeMode.Single) {
-                                        UserActionResult(Scenes.QuickSettings)
-                                    } else {
-                                        notifShadeSceneKey
-                                    },
-
-                                // TODO(b/338577208): Remove once we add Dual Shade invocation zones
-                                swipeDownFromTop(pointerCount = 2) to
-                                    UserActionResult(
-                                        toScene = SceneFamilies.QuickSettings,
-                                        transitionKey =
-                                            ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                                    ),
-
-                                // Swiping down, not from the edge, always navigates to the notif
-                                // shade scene.
-                                swipeDown(pointerCount = 1) to notifShadeSceneKey,
-                                swipeDown(pointerCount = 2) to notifShadeSceneKey,
-                            )
-                            .filterValuesNotNull()
-                    }
-                }
-            }
-            .collectLatest { setActions(it) }
-    }
-
-    private fun swipeDownFromTop(pointerCount: Int): Swipe {
-        return Swipe(
-            SwipeDirection.Down,
-            fromSource = Edge.Top,
-            pointerCount = pointerCount,
-        )
-    }
-
-    private fun swipeDown(pointerCount: Int): Swipe {
-        return Swipe(
-            SwipeDirection.Down,
-            pointerCount = pointerCount,
-        )
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): LockscreenSceneActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index 27a1f7a..d3eefca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -45,13 +45,7 @@
             edge = Edge.create(from = LOCKSCREEN, to = DOZING),
         )
 
-    val lockscreenAlpha: Flow<Float> =
-        transitionAnimation.sharedFlow(
-            duration = 250.milliseconds,
-            onStep = { 1 - it },
-            onFinish = { 1f },
-            onCancel = { 1f },
-        )
+    val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(1f)
 
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
@@ -61,18 +55,16 @@
             onCancel = { 1f },
         )
 
-    val deviceEntryBackgroundViewAlpha: Flow<Float> =
-        transitionAnimation.immediatelyTransitionTo(0f)
-
     override val deviceEntryParentViewAlpha: Flow<Float> =
         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
             isUdfpsEnrolledAndEnabled ->
-            transitionAnimation.immediatelyTransitionTo(
-                if (isUdfpsEnrolledAndEnabled) {
-                    1f
-                } else {
-                    0f
-                }
-            )
+            if (isUdfpsEnrolledAndEnabled) {
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                transitionAnimation.sharedFlow(
+                    duration = 250.milliseconds,
+                    onStep = { 1f - it },
+                )
+            }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 1314e88..6adf3e9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -67,7 +67,7 @@
         var leaveShadeOpen = false
 
         return transitionAnimation.sharedFlow(
-            duration = 200.milliseconds,
+            duration = 80.milliseconds,
             onStart = {
                 leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
                 startAlpha = viewState.alpha()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index e64c614..c0b9efaa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -23,7 +23,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.transitions.FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,10 +53,18 @@
                 edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
             )
 
+    private val alphaForAnimationStep: (Float) -> Float =
+        when {
+            SceneContainerFlag.isEnabled -> { step ->
+                    1f - Math.min((step / FROM_LOCK_SCREEN_TO_BOUNCER_FADE_FRACTION), 1f)
+                }
+            else -> { step -> 1f - step }
+        }
+
     val shortcutsAlpha: Flow<Float> =
         transitionAnimation.sharedFlow(
             duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
-            onStep = { 1f - it }
+            onStep = alphaForAnimationStep
         )
 
     val lockscreenAlpha: Flow<Float> = shortcutsAlpha
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
new file mode 100644
index 0000000..dd47678
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModel.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.util.kotlin.filterValuesNotNull
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+
+/** Models UI state and handles user input for the lockscreen scene. */
+class LockscreenUserActionsViewModel
+@AssistedInject
+constructor(
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val communalInteractor: CommunalInteractor,
+    private val shadeInteractor: ShadeInteractor,
+) : UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        shadeInteractor.isShadeTouchable
+            .flatMapLatest { isShadeTouchable ->
+                if (!isShadeTouchable) {
+                    flowOf(emptyMap())
+                } else {
+                    combine(
+                        deviceEntryInteractor.isUnlocked,
+                        communalInteractor.isCommunalAvailable,
+                        shadeInteractor.shadeMode,
+                    ) { isDeviceUnlocked, isCommunalAvailable, shadeMode ->
+                        val notifShadeSceneKey =
+                            UserActionResult(
+                                toScene = SceneFamilies.NotifShade,
+                                transitionKey =
+                                    ToSplitShade.takeIf { shadeMode is ShadeMode.Split },
+                            )
+
+                        mapOf(
+                                Swipe.Left to
+                                    UserActionResult(Scenes.Communal).takeIf {
+                                        isCommunalAvailable
+                                    },
+                                Swipe.Up to if (isDeviceUnlocked) Scenes.Gone else Scenes.Bouncer,
+
+                                // Swiping down from the top edge goes to QS (or shade if in split
+                                // shade mode).
+                                swipeDownFromTop(pointerCount = 1) to
+                                    if (shadeMode is ShadeMode.Single) {
+                                        UserActionResult(Scenes.QuickSettings)
+                                    } else {
+                                        notifShadeSceneKey
+                                    },
+
+                                // TODO(b/338577208): Remove once we add Dual Shade invocation zones
+                                swipeDownFromTop(pointerCount = 2) to
+                                    UserActionResult(
+                                        toScene = SceneFamilies.QuickSettings,
+                                        transitionKey =
+                                            ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                                    ),
+
+                                // Swiping down, not from the edge, always navigates to the notif
+                                // shade scene.
+                                swipeDown(pointerCount = 1) to notifShadeSceneKey,
+                                swipeDown(pointerCount = 2) to notifShadeSceneKey,
+                            )
+                            .filterValuesNotNull()
+                    }
+                }
+            }
+            .collect { setActions(it) }
+    }
+
+    private fun swipeDownFromTop(pointerCount: Int): Swipe {
+        return Swipe(
+            SwipeDirection.Down,
+            fromSource = Edge.Top,
+            pointerCount = pointerCount,
+        )
+    }
+
+    private fun swipeDown(pointerCount: Int): Swipe {
+        return Swipe(
+            SwipeDirection.Down,
+            pointerCount = pointerCount,
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): LockscreenUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index af01930..4fb2b9b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -17,15 +17,19 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.flatMapLatest
 
 /**
  * Breaks down OCCLUDED->DOZING transition into discrete steps for corresponding views to consume.
@@ -35,8 +39,9 @@
 class OccludedToDozingTransitionViewModel
 @Inject
 constructor(
+    deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
     animationFlow: KeyguardTransitionAnimationFlow,
-) {
+) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
@@ -50,4 +55,17 @@
             duration = 250.milliseconds,
             onStep = { it },
         )
+
+    val deviceEntryBackgroundViewAlpha: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
+
+    override val deviceEntryParentViewAlpha: Flow<Float> =
+        deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
+            ->
+            if (udfpsEnrolledAndEnabled) {
+                transitionAnimation.immediatelyTransitionTo(1f)
+            } else {
+                emptyFlow()
+            }
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 7511101..d29f512 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.util.MathUtils
 import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
@@ -58,7 +59,14 @@
             onStep = { it }
         )
 
-    val lockscreenAlpha: Flow<Float> = shortcutsAlpha
+    fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+        var currentAlpha = 0f
+        return transitionAnimation.sharedFlow(
+            duration = 250.milliseconds,
+            onStart = { currentAlpha = viewState.alpha() },
+            onStep = { MathUtils.lerp(currentAlpha, 1f, it) },
+        )
+    }
 
     val deviceEntryBackgroundViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
index e45d537..708b408 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/ShadeDependentFlows.kt
@@ -34,7 +34,9 @@
 ) {
     /** When the last keyguard state transition started, was the shade fully expanded? */
     private val lastStartedTransitionHadShadeFullyExpanded: Flow<Boolean> =
-        transitionInteractor.startedKeyguardState.sample(shadeInteractor.isAnyFullyExpanded)
+        transitionInteractor.startedKeyguardTransitionStep.sample(
+            shadeInteractor.isAnyFullyExpanded
+        )
 
     /**
      * Decide which flow to use depending on the shade expansion state at the start of the last
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt
index ebb0ea62..c1768a4 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Activatable.kt
@@ -19,6 +19,7 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
+import com.android.app.tracing.coroutines.traceCoroutine
 
 /** Defines interface for classes that can be activated to do coroutine work. */
 interface Activatable {
@@ -57,7 +58,7 @@
      * }
      * ```
      */
-    suspend fun activate()
+    suspend fun activate(): Nothing
 }
 
 /**
@@ -66,13 +67,19 @@
  *
  * If the [key] changes, the old [Activatable] is deactivated and a new one will be instantiated,
  * activated, and returned.
+ *
+ * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label
+ * that's unique enough and easy enough to find in code search; this should help correlate
+ * performance findings with actual code. One recommendation: prefer whole string literals instead
+ * of some complex concatenation or templating scheme.
  */
 @Composable
 fun <T : Activatable> rememberActivated(
+    traceName: String,
     key: Any = Unit,
     factory: () -> T,
 ): T {
     val instance = remember(key) { factory() }
-    LaunchedEffect(instance) { instance.activate() }
+    LaunchedEffect(instance) { traceCoroutine(traceName) { instance.activate() } }
     return instance
 }
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/ExclusiveActivatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/ExclusiveActivatable.kt
new file mode 100644
index 0000000..0837398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/ExclusiveActivatable.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.lifecycle
+
+import java.util.concurrent.atomic.AtomicBoolean
+
+/**
+ * A base [Activatable] that can only be activated by a single owner (hence "exclusive"). A previous
+ * call to [activate] must be canceled before a new call to [activate] can be made. Trying to call
+ * [activate] while already active will result in a runtime error.
+ */
+abstract class ExclusiveActivatable : Activatable {
+
+    private val _isActive = AtomicBoolean(false)
+
+    protected var isActive: Boolean
+        get() = _isActive.get()
+        private set(value) {
+            _isActive.set(value)
+        }
+
+    final override suspend fun activate(): Nothing {
+        val allowed = _isActive.compareAndSet(false, true)
+        check(allowed) { "Cannot activate an already active ExclusiveActivatable!" }
+
+        try {
+            onActivated()
+        } finally {
+            isActive = false
+        }
+    }
+
+    /**
+     * Notifies that the [Activatable] has been activated.
+     *
+     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
+     * its state fresh and/or perform side-effects.
+     *
+     * The method suspends and doesn't return until all work required by the object is finished. In
+     * most cases, it's expected for the work to remain ongoing forever so this method will forever
+     * suspend its caller until the coroutine that called it is canceled.
+     *
+     * Implementations could follow this pattern:
+     * ```kotlin
+     * override suspend fun onActivated(): Nothing {
+     *     coroutineScope {
+     *         launch { ... }
+     *         launch { ... }
+     *         launch { ... }
+     *         awaitCancellation()
+     *     }
+     * }
+     * ```
+     *
+     * @see activate
+     */
+    protected abstract suspend fun onActivated(): Nothing
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt
new file mode 100644
index 0000000..df1394b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.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 com.android.systemui.lifecycle
+
+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.traceCoroutine
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Keeps snapshot/Compose [State]s up-to-date.
+ *
+ * ```kotlin
+ * val hydrator = Hydrator()
+ * val state: Int by hydrator.hydratedStateOf(upstreamFlow)
+ *
+ * override suspend fun activate(): Nothing {
+ *     hydrator.activate()
+ * }
+ * ```
+ */
+class Hydrator(
+    /**
+     * A name for performance tracing purposes.
+     *
+     * Please use a short string literal that's easy to find in code search. Try to avoid
+     * concatenation or templating.
+     */
+    private val traceName: String,
+) : ExclusiveActivatable() {
+
+    private val children = mutableListOf<NamedActivatable>()
+
+    /**
+     * Returns a snapshot [State] that's kept up-to-date as long as its owner is active.
+     *
+     * @param traceName Used for coroutine performance tracing purposes. Please try to use a label
+     *   that's unique enough and easy enough to find in code search; this should help correlate
+     *   performance findings with actual code. One recommendation: prefer whole string literals
+     *   instead of some complex concatenation or templating scheme.
+     * @param source The upstream [StateFlow] to collect from; values emitted to it will be
+     *   automatically set on the returned [State].
+     */
+    @StateFactoryMarker
+    fun <T> hydratedStateOf(
+        traceName: String,
+        source: StateFlow<T>,
+    ): State<T> {
+        return hydratedStateOf(
+            traceName = traceName,
+            initialValue = source.value,
+            source = source,
+        )
+    }
+
+    /**
+     * Returns a snapshot [State] that's kept up-to-date as long as its owner is active.
+     *
+     * @param traceName Used for coroutine performance tracing purposes. Please try to use a label
+     *   that's unique enough and easy enough to find in code search; this should help correlate
+     *   performance findings with actual code. One recommendation: prefer whole string literals
+     *   instead of some complex concatenation or templating scheme. Use `null` to disable
+     *   performance tracing for this state.
+     * @param initialValue The first value to place on the [State]
+     * @param source The upstream [Flow] to collect from; values emitted to it will be automatically
+     *   set on the returned [State].
+     */
+    @StateFactoryMarker
+    fun <T> hydratedStateOf(
+        traceName: String?,
+        initialValue: T,
+        source: Flow<T>,
+    ): State<T> {
+        check(!isActive) { "Cannot call hydratedStateOf after Hydrator is already active." }
+
+        val mutableState = mutableStateOf(initialValue)
+        children.add(
+            NamedActivatable(
+                traceName = traceName,
+                activatable =
+                    object : ExclusiveActivatable() {
+                        override suspend fun onActivated(): Nothing {
+                            source.collect { mutableState.value = it }
+                            awaitCancellation()
+                        }
+                    },
+            )
+        )
+        return mutableState
+    }
+
+    override suspend fun onActivated() = coroutineScope {
+        traceCoroutine(traceName) {
+            children.forEach { child ->
+                if (child.traceName != null) {
+                    launch(spanName = child.traceName) { child.activatable.activate() }
+                } else {
+                    launch { child.activatable.activate() }
+                }
+            }
+            awaitCancellation()
+        }
+    }
+
+    private data class NamedActivatable(
+        val traceName: String?,
+        val activatable: Activatable,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 661da6d..c2b5d98 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -227,13 +227,33 @@
 }
 
 /**
+ * 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
+ * at [state]), automatically canceling the work when the window is no longer at least at that
+ * state.
+ *
+ * [block] may be run multiple times, running once per every time this` [View]'s Window's
+ * [WindowLifecycleState] becomes at least at [state].
+ */
+suspend fun View.repeatOnWindowLifecycle(
+    state: WindowLifecycleState,
+    block: suspend CoroutineScope.() -> Unit,
+): Nothing {
+    when (state) {
+        WindowLifecycleState.ATTACHED -> repeatWhenAttachedToWindow(block)
+        WindowLifecycleState.VISIBLE -> repeatWhenWindowIsVisible(block)
+        WindowLifecycleState.FOCUSED -> repeatWhenWindowHasFocus(block)
+    }
+}
+
+/**
  * Runs the given [block] every time the [View] becomes attached (or immediately after calling this
  * function, if the view was already attached), automatically canceling the work when the view
  * becomes detached.
  *
  * Only use from the main thread.
  *
- * The [block] may be run multiple times, running once per every time the view is attached.
+ * [block] may be run multiple times, running once per every time the view is attached.
  */
 @MainThread
 suspend fun View.repeatWhenAttachedToWindow(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -249,7 +269,7 @@
  *
  * Only use from the main thread.
  *
- * The [block] may be run multiple times, running once per every time the window becomes visible.
+ * [block] may be run multiple times, running once per every time the window becomes visible.
  */
 @MainThread
 suspend fun View.repeatWhenWindowIsVisible(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -265,7 +285,7 @@
  *
  * Only use from the main thread.
  *
- * The [block] may be run multiple times, running once per every time the window is focused.
+ * [block] may be run multiple times, running once per every time the window is focused.
  */
 @MainThread
 suspend fun View.repeatWhenWindowHasFocus(block: suspend CoroutineScope.() -> Unit): Nothing {
@@ -274,6 +294,21 @@
     awaitCancellation() // satisfies return type of Nothing
 }
 
+/** Lifecycle states for a [View]'s interaction with a [android.view.Window]. */
+enum class WindowLifecycleState {
+    /** Indicates that the [View] is attached to a [android.view.Window]. */
+    ATTACHED,
+    /**
+     * Indicates that the [View] is attached to a [android.view.Window], and the window is visible.
+     */
+    VISIBLE,
+    /**
+     * Indicates that the [View] is attached to a [android.view.Window], and the window is visible
+     * and focused.
+     */
+    FOCUSED
+}
+
 private val View.isAttached
     get() = conflatedCallbackFlow {
         val onAttachListener =
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt
deleted file mode 100644
index f080a42..0000000
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SafeActivatable.kt
+++ /dev/null
@@ -1,72 +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.lifecycle
-
-import java.util.concurrent.atomic.AtomicBoolean
-
-/**
- * An [Activatable] that can be concurrently activated by no more than one owner.
- *
- * A previous call to [activate] must be canceled before a new call to [activate] can be made.
- * Trying to call [activate] while already active will fail with an error.
- */
-abstract class SafeActivatable : Activatable {
-
-    private val _isActive = AtomicBoolean(false)
-
-    var isActive: Boolean
-        get() = _isActive.get()
-        private set(value) {
-            _isActive.set(value)
-        }
-
-    final override suspend fun activate() {
-        val allowed = _isActive.compareAndSet(false, true)
-        check(allowed) { "Cannot activate an already active activatable!" }
-
-        try {
-            onActivated()
-        } finally {
-            isActive = false
-        }
-    }
-
-    /**
-     * Notifies that the [Activatable] has been activated.
-     *
-     * Serves as an entrypoint to kick off coroutine work that the object requires in order to keep
-     * its state fresh and/or perform side-effects.
-     *
-     * The method suspends and doesn't return until all work required by the object is finished. In
-     * most cases, it's expected for the work to remain ongoing forever so this method will forever
-     * suspend its caller until the coroutine that called it is canceled.
-     *
-     * Implementations could follow this pattern:
-     * ```kotlin
-     * override suspend fun onActivated() {
-     *     coroutineScope {
-     *         launch { ... }
-     *         launch { ... }
-     *         launch { ... }
-     *     }
-     * }
-     * ```
-     *
-     * @see activate
-     */
-    protected abstract suspend fun onActivated()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SnapshotViewBinding.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SnapshotViewBinding.kt
new file mode 100644
index 0000000..91ba614
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SnapshotViewBinding.kt
@@ -0,0 +1,298 @@
+/*
+ * 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.lifecycle
+
+import android.os.Handler
+import android.os.Looper
+import android.view.Choreographer
+import android.view.View
+import androidx.collection.MutableScatterSet
+import androidx.compose.runtime.snapshots.Snapshot
+import androidx.compose.runtime.snapshots.SnapshotStateObserver
+import androidx.core.os.HandlerCompat
+import com.android.systemui.res.R
+
+/**
+ * [SnapshotViewBindingRoot] is installed on the root view of an attached view hierarchy and
+ * coordinates all [SnapshotViewBinding]s for the window.
+ *
+ * This class is not thread-safe. It should only be accessed from the thread corresponding to the UI
+ * thread referenced by the [handler] and [choreographer] constructor parameters. These two
+ * parameters must refer to the same UI thread.
+ *
+ * Lazily created and installed on a root attached view by [bindingRoot].
+ */
+private class SnapshotViewBindingRoot(
+    private val handler: Handler,
+    private val choreographer: Choreographer
+) {
+    /** Multiplexer for all snapshot state observations; see [start] and [stop] */
+    private val observer = SnapshotStateObserver { task ->
+        if (Looper.myLooper() === handler.looper) task() else handler.post(task)
+    }
+
+    /** `true` if a [Choreographer] frame is currently scheduled */
+    private var isFrameScheduled = false
+
+    /**
+     * Unordered set of [SnapshotViewBinding]s that have been invalidated and are awaiting handling
+     * by an upcoming frame.
+     */
+    private val invalidatedBindings = MutableScatterSet<SnapshotViewBinding>()
+
+    /**
+     * Callback for [SnapshotStateObserver.observeReads] allocated once for the life of the
+     * [SnapshotViewBindingRoot] and reused to avoid extra allocations during frame operations.
+     */
+    private val onBindingChanged: (SnapshotViewBinding) -> Unit = {
+        invalidatedBindings += it
+        if (!isFrameScheduled) {
+            choreographer.postFrameCallback(frameCallback)
+            isFrameScheduled = true
+        }
+    }
+
+    /** Callback for [Choreographer.postFrameCallback] */
+    private val frameCallback =
+        Choreographer.FrameCallback {
+            try {
+                bindInvalidatedBindings()
+            } finally {
+                isFrameScheduled = false
+            }
+        }
+
+    /**
+     * Perform binding of all [SnapshotViewBinding]s in [invalidatedBindings] within a single
+     * mutable snapshot. The snapshot will be committed if no exceptions are thrown from any
+     * binding's `onError` handler.
+     */
+    private fun bindInvalidatedBindings() {
+        Snapshot.withMutableSnapshot {
+            // removeIf is used here to perform a forEach where each element is removed
+            // as the invalid bindings are traversed. If a performBindOf throws we want
+            // the rest of the unhandled invalidations to remain.
+            invalidatedBindings.removeIf { binding ->
+                performBindOf(binding)
+                true
+            }
+        }
+    }
+
+    /**
+     * Perform the view binding for [binding] while observing its snapshot reads. Once this method
+     * is called for a [binding] this [SnapshotViewBindingRoot] may retain hard references back to
+     * [binding] via [observer], [invalidatedBindings] or both. Use [forgetBinding] to drop these
+     * references once a [SnapshotViewBinding] is no longer relevant.
+     *
+     * This method should only be called after [start] has been called and before [stop] has been
+     * called; failing to obey this constraint may result in lingering hard references to [binding]
+     * or missed invalidations in response to snapshot state that was changed prior to [start] being
+     * called.
+     */
+    fun performBindOf(binding: SnapshotViewBinding) {
+        try {
+            observer.observeReads(binding, onBindingChanged, binding.performBind)
+        } catch (error: Throwable) {
+            // Note: it is valid (and the default) for this call to re-throw the error
+            binding.onError(error)
+        }
+    }
+
+    /**
+     * Forget about [binding], dropping all observed tracking and invalidation state. After calling
+     * this method it is safe to abandon [binding] to the garbage collector.
+     */
+    fun forgetBinding(binding: SnapshotViewBinding) {
+        observer.clear(binding)
+        invalidatedBindings.remove(binding)
+    }
+
+    /**
+     * Start tracking snapshot commits that may affect [SnapshotViewBinding]s passed to
+     * [performBindOf] calls. Call this method before invoking [performBindOf].
+     *
+     * Once this method has been called, [stop] must be called prior to abandoning this
+     * [SnapshotViewBindingRoot] to the garbage collector, as a hard reference to it will be
+     * retained by the snapshot system until [stop] is invoked.
+     */
+    fun start() {
+        observer.start()
+    }
+
+    /**
+     * Stop tracking snapshot commits that may affect [SnapshotViewBinding]s that have been passed
+     * to [performBindOf], cancel any pending [choreographer] frame callback, and forget all
+     * [invalidatedBindings].
+     *
+     * Call [stop] prior to abandoning this [SnapshotViewBindingRoot] to the garbage collector.
+     *
+     * Calling [start] again after [stop] will begin tracking invalidations again, but any
+     * [SnapshotViewBinding]s must be re-bound using [performBindOf] after the [start] call returns.
+     */
+    fun stop() {
+        observer.stop()
+        choreographer.removeFrameCallback(frameCallback)
+        isFrameScheduled = false
+        invalidatedBindings.clear()
+    }
+}
+
+/**
+ * Return the [SnapshotViewBindingRoot] for this [View], lazily creating it if it does not yet
+ * exist. This [View] must be currently attached to a window and this property should only be
+ * accessed from this [View]'s UI thread.
+ *
+ * The [SnapshotViewBindingRoot] will be [started][SnapshotViewBindingRoot.start] before this
+ * property get returns, making it safe to call [SnapshotViewBindingRoot.performBindOf] for the
+ * [bindingRoot] of an attached [View].
+ *
+ * When the [View] becomes attached to a window the [SnapshotViewBindingRoot] will automatically be
+ * [started][SnapshotViewBindingRoot.start]. When it becomes detached from its window it will
+ * automatically be [stopped][SnapshotViewBindingRoot.stop].
+ *
+ * This should generally only be called on the [View] returned by [View.getRootView] for an attached
+ * [View].
+ */
+private val View.bindingRoot: SnapshotViewBindingRoot
+    get() {
+        val tag = getTag(R.id.snapshot_view_binding_root) as? SnapshotViewBindingRoot
+        if (tag != null) return tag
+        val newRoot =
+            SnapshotViewBindingRoot(
+                // Use an async handler for processing invalidations; this ensures invalidations
+                // are tracked for the upcoming frame and not the next frame.
+                handler =
+                    HandlerCompat.createAsync(
+                        handler?.looper ?: error("$this is not attached to a window")
+                    ),
+                choreographer = Choreographer.getInstance()
+            )
+        setTag(R.id.snapshot_view_binding_root, newRoot)
+        addOnAttachStateChangeListener(
+            object : View.OnAttachStateChangeListener {
+                override fun onViewAttachedToWindow(view: View) {
+                    newRoot.start()
+                }
+
+                override fun onViewDetachedFromWindow(view: View) {
+                    newRoot.stop()
+                }
+            }
+        )
+        if (isAttachedToWindow) newRoot.start()
+        return newRoot
+    }
+
+/**
+ * A single [SnapshotViewBinding] set on a [View] by [setSnapshotBinding]. The [SnapshotViewBinding]
+ * is responsible for invoking [SnapshotViewBindingRoot.performBindOf] when the associated [View]
+ * becomes attached to a window in order to register it for invalidation tracking and rebinding as
+ * relevant snapshot state changes. When the [View] becomes detached the binding will invoke
+ * [SnapshotViewBindingRoot.forgetBinding] for itself.
+ */
+private class SnapshotViewBinding(
+    val performBind: () -> Unit,
+    val onError: (Throwable) -> Unit,
+) : View.OnAttachStateChangeListener {
+
+    override fun onViewAttachedToWindow(view: View) {
+        Snapshot.withMutableSnapshot { view.rootView.bindingRoot.performBindOf(this) }
+    }
+
+    override fun onViewDetachedFromWindow(view: View) {
+        view.rootView.bindingRoot.forgetBinding(this)
+    }
+}
+
+/**
+ * Set binding logic for this [View] that will be re-invoked for UI frames where relevant [Snapshot]
+ * state has changed. This can be especially useful for codebases with mixed usage of both Views and
+ * [Jetpack Compose](https://d.android.com/compose), enabling the same patterns of snapshot-backed
+ * state management when using either UI toolkit.
+ *
+ * In the following example the sender name and message text of a message item view will be kept up
+ * to date with the snapshot-backed `model.senderName` and `model.messageText` properties:
+ * ```
+ * val view = layoutInflater.inflate(R.layout.single_message, parent, false)
+ * val senderNameView = view.findViewById<TextView>(R.id.sender_name)
+ * val messageTextView = view.findViewById<TextView>(R.id.message_text)
+ * view.setSnapshotBinding {
+ *     senderNameView.text = model.senderName
+ *     messageTextView.text = model.messageText
+ * }
+ * ```
+ *
+ * Snapshot binding may also be used in concert with
+ * [View binding](https://developer.android.com/topic/libraries/view-binding):
+ * ```
+ * val binding = SingleMessageBinding.inflate(layoutInflater)
+ * binding.root.setSnapshotBinding {
+ *     binding.senderName.text = model.senderName
+ *     binding.messageText.text = model.messageText
+ * }
+ * ```
+ *
+ * When a snapshot binding is set [performBind] will be invoked immediately before
+ * [setSnapshotBinding] returns if this [View] is currently attached to a window. If the view is not
+ * currently attached, [performBind] will be invoked when the view becomes attached to a window.
+ *
+ * If a snapshot commit changes state accessed by [performBind] changes while the view remains
+ * attached to its window and the snapshot binding is not replaced or [cleared][clearBinding], the
+ * binding will be considered _invalidated,_ a rebinding will be scheduled for the upcoming UI
+ * frame, and [performBind] will be re-executed prior to the layout and draw phases for the frame.
+ * [performBind] will only be re-executed **once** for any given UI frame provided that
+ * [setSnapshotBinding] is not called again.
+ *
+ * [performBind] is always invoked from a [mutable snapshot][Snapshot.takeMutableSnapshot], ensuring
+ * atomic consistency of all snapshot state reads within it. **All** rebinding performed for
+ * invalidations of bindings within the same window for a given UI frame are performed within the
+ * **same** snapshot, ensuring that same atomic consistency of snapshot state for **all** snapshot
+ * bindings within the same window.
+ *
+ * As [performBind] is invoked for rebinding as part of the UI frame itself, [performBind]
+ * implementations should be both fast and idempotent to avoid delaying the UI frame.
+ *
+ * There are no mutual ordering guarantees between separate snapshot bindings; the [performBind] of
+ * separate snapshot bindings may be executed in any order. Similarly, no ordering guarantees exist
+ * between snapshot binding rebinding and Jetpack Compose recomposition. Snapshot bindings and
+ * Compose UIs both should obey
+ * [unidirectional data flow](https://developer.android.com/topic/architecture/ui-layer#udf)
+ * principles, consuming state from mutual single sources of truth and avoid consuming state
+ * produced by the rebinding or recomposition of other UI components.
+ */
+fun View.setSnapshotBinding(onError: (Throwable) -> Unit = { throw it }, performBind: () -> Unit) {
+    clearBinding()
+    val newBinding = SnapshotViewBinding(performBind, onError)
+    setTag(R.id.snapshot_view_binding, newBinding)
+    addOnAttachStateChangeListener(newBinding)
+    if (isAttachedToWindow) newBinding.onViewAttachedToWindow(this)
+}
+
+/**
+ * Remove a snapshot binding that was set by [setSnapshotBinding]. It is not necessary to call this
+ * function before abandoning a [View] with a snapshot binding to the garbage collector.
+ */
+fun View.clearBinding() {
+    val oldBinding = getTag(R.id.snapshot_view_binding) as? SnapshotViewBinding
+    if (oldBinding != null) {
+        removeOnAttachStateChangeListener(oldBinding)
+        if (isAttachedToWindow) {
+            oldBinding.onViewDetachedFromWindow(this)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
index 0af5fea..508b04e 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/SysUiViewModel.kt
@@ -16,29 +16,58 @@
 
 package com.android.systemui.lifecycle
 
+import android.view.View
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.remember
-
-/** Base class for all System UI view-models. */
-abstract class SysUiViewModel : SafeActivatable() {
-
-    override suspend fun onActivated() = Unit
-}
+import com.android.app.tracing.coroutines.traceCoroutine
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /**
- * Returns a remembered [SysUiViewModel] of the type [T] that's automatically kept active until this
- * composable leaves the composition.
- *
- * If the [key] changes, the old [SysUiViewModel] is deactivated and a new one will be instantiated,
+ * Returns a remembered view-model of the type [T]. If the returned instance is also an
+ * [Activatable], it's automatically kept active until this composable leaves the composition; if
+ * the [key] changes, the old view-model is deactivated and a new one will be instantiated,
  * activated, and returned.
+ *
+ * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label
+ * that's unique enough and easy enough to find in code search; this should help correlate
+ * performance findings with actual code. One recommendation: prefer whole string literals instead
+ * of some complex concatenation or templating scheme.
  */
 @Composable
-fun <T : SysUiViewModel> rememberViewModel(
+fun <T> rememberViewModel(
+    traceName: String,
     key: Any = Unit,
     factory: () -> T,
 ): T {
     val instance = remember(key) { factory() }
-    LaunchedEffect(instance) { instance.activate() }
+    if (instance is Activatable) {
+        LaunchedEffect(instance) { traceCoroutine(traceName) { instance.activate() } }
+    }
     return instance
 }
+
+/**
+ * Invokes [block] in a new coroutine with a new view-model that is automatically activated whenever
+ * `this` [View]'s Window's [WindowLifecycleState] is at least at [minWindowLifecycleState], and is
+ * automatically canceled once that is no longer the case.
+ *
+ * The [traceName] is used for coroutine performance tracing purposes. Please try to use a label
+ * that's unique enough and easy enough to find in code search; this should help correlate
+ * performance findings with actual code. One recommendation: prefer whole string literals instead
+ * of some complex concatenation or templating scheme.
+ */
+suspend fun <T> View.viewModel(
+    traceName: String,
+    minWindowLifecycleState: WindowLifecycleState,
+    factory: () -> T,
+    block: suspend CoroutineScope.(T) -> Unit,
+): Nothing =
+    repeatOnWindowLifecycle(minWindowLifecycleState) {
+        val instance = factory()
+        if (instance is Activatable) {
+            launch { traceCoroutine(traceName) { instance.activate() } }
+        }
+        block(instance)
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt
new file mode 100644
index 0000000..b0abdb7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/CommunalTouchLog.kt
@@ -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.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for communal touch-handling logging. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class CommunalTouchLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/InputDeviceTutorialLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/InputDeviceTutorialLog.kt
new file mode 100644
index 0000000..a788279
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/InputDeviceTutorialLog.kt
@@ -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.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for input device tutorial. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class InputDeviceTutorialLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 40bb8e1..19906fd 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -574,7 +574,7 @@
     @SysUISingleton
     @KeyguardQuickAffordancesLog
     public static LogBuffer provideKeyguardQuickAffordancesLogBuffer(LogBufferFactory factory) {
-        return factory.create("KeyguardQuickAffordancesLog", 25);
+        return factory.create("KeyguardQuickAffordancesLog", 100);
     }
 
     /**
@@ -618,6 +618,16 @@
     }
 
     /**
+     * Provides a {@link LogBuffer} for communal touch-handling logs.
+     */
+    @Provides
+    @SysUISingleton
+    @CommunalTouchLog
+    public static LogBuffer provideCommunalTouchLogBuffer(LogBufferFactory factory) {
+        return factory.create("CommunalTouchLog", 250);
+    }
+
+    /**
      * Provides a {@link TableLogBuffer} for communal-related logs.
      */
     @Provides
@@ -660,6 +670,14 @@
         return factory.create("KeyboardLog", 50);
     }
 
+    /** Provides a {@link LogBuffer} for the input devices tutorial. */
+    @Provides
+    @SysUISingleton
+    @InputDeviceTutorialLog
+    public static LogBuffer provideInputDeviceTutorialLogBuffer(LogBufferFactory factory) {
+        return factory.create("InputDeviceTutorialLog", 50);
+    }
+
     /** Provides a {@link LogBuffer} for {@link PackageChangeRepository} */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 2089cce5..89a599a 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -16,19 +16,17 @@
 
 package com.android.systemui.log.table
 
+import android.annotation.SuppressLint
 import android.icu.text.SimpleDateFormat
 import android.os.Trace
 import com.android.systemui.Dumpable
 import com.android.systemui.common.buffer.RingBuffer
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.LogcatEchoTracker
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.plugins.log.TableLogBufferBase
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import java.util.Locale
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
 
 /**
  * A logger that logs changes in table format.
@@ -75,13 +73,12 @@
  *
  * @param maxSize the maximum size of the buffer. Must be > 0.
  */
+@SuppressLint("DumpableNotRegistered") // Registered as dumpable in [TableLogBufferFactory]
 class TableLogBuffer(
     maxSize: Int,
     private val name: String,
     private val systemClock: SystemClock,
     private val logcatEchoTracker: LogcatEchoTracker,
-    @Background private val bgDispatcher: CoroutineDispatcher,
-    private val coroutineScope: CoroutineScope,
     private val localLogcat: LogProxy = LogProxyDefault(),
 ) : Dumpable, TableLogBufferBase {
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
index ff523ae1..425e674e 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt
@@ -17,15 +17,11 @@
 package com.android.systemui.log.table
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.LogBufferHelper.Companion.adjustMaxSize
 import com.android.systemui.log.LogcatEchoTracker
 import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
 
 @SysUISingleton
 class TableLogBufferFactory
@@ -34,8 +30,6 @@
     private val dumpManager: DumpManager,
     private val systemClock: SystemClock,
     private val logcatEchoTracker: LogcatEchoTracker,
-    @Background private val bgDispatcher: CoroutineDispatcher,
-    @Application private val coroutineScope: CoroutineScope,
 ) {
     private val existingBuffers = mutableMapOf<String, TableLogBuffer>()
 
@@ -58,8 +52,6 @@
                 name,
                 systemClock,
                 logcatEchoTracker,
-                bgDispatcher,
-                coroutineScope,
             )
         dumpManager.registerTableLogBuffer(name, tableBuffer)
         return tableBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
index 9c29bab..ed5080d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -51,9 +51,8 @@
         fun providesMediaDataManager(
             legacyProvider: Provider<LegacyMediaDataManagerImpl>,
             newProvider: Provider<MediaCarouselInteractor>,
-            mediaFlags: MediaFlags,
         ): MediaDataManager {
-            return if (mediaFlags.isSceneContainerEnabled()) {
+            return if (SceneContainerFlag.isEnabled) {
                 newProvider.get()
             } else {
                 legacyProvider.get()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 143d66b..24c57be 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -16,9 +16,8 @@
 
 package com.android.systemui.media.controls.domain.pipeline
 
+import android.annotation.MainThread
 import android.annotation.SuppressLint
-import android.app.ActivityOptions
-import android.app.BroadcastOptions
 import android.app.Notification
 import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
 import android.app.PendingIntent
@@ -39,7 +38,6 @@
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.graphics.ImageDecoder
-import android.graphics.drawable.Animatable
 import android.graphics.drawable.Icon
 import android.media.MediaDescription
 import android.media.MediaMetadata
@@ -62,8 +60,10 @@
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.Dumpable
+import com.android.systemui.Flags
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
@@ -86,7 +86,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.tuner.TunerService
@@ -97,8 +96,13 @@
 import com.android.systemui.util.time.SystemClock
 import java.io.IOException
 import java.io.PrintWriter
+import java.util.Collections
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 // URI fields to try loading album art from
 private val ART_URIS =
@@ -167,8 +171,11 @@
 class LegacyMediaDataManagerImpl(
     private val context: Context,
     @Background private val backgroundExecutor: Executor,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
     @Main private val uiExecutor: Executor,
     @Main private val foregroundExecutor: DelayableExecutor,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    @Application private val applicationScope: CoroutineScope,
     private val mediaControllerFactory: MediaControllerFactory,
     private val broadcastDispatcher: BroadcastDispatcher,
     dumpManager: DumpManager,
@@ -188,6 +195,7 @@
     private val logger: MediaUiEventLogger,
     private val smartspaceManager: SmartspaceManager?,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
 ) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener, MediaDataManager {
 
     companion object {
@@ -219,7 +227,12 @@
     // listeners are listeners that depend on MediaDataManager.
     // TODO(b/159539991#comment5): Move internal listeners to separate package.
     private val internalListeners: MutableSet<MediaDataManager.Listener> = mutableSetOf()
-    private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
+    private val mediaEntries: MutableMap<String, MediaData> =
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            Collections.synchronizedMap(LinkedHashMap())
+        } else {
+            LinkedHashMap()
+        }
     // There should ONLY be at most one Smartspace media recommendation.
     var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
     @Keep private var smartspaceSession: SmartspaceSession? = null
@@ -245,8 +258,11 @@
     constructor(
         context: Context,
         threadFactory: ThreadFactory,
+        @Background backgroundDispatcher: CoroutineDispatcher,
         @Main uiExecutor: Executor,
         @Main foregroundExecutor: DelayableExecutor,
+        @Main mainDispatcher: CoroutineDispatcher,
+        @Application applicationScope: CoroutineScope,
         mediaControllerFactory: MediaControllerFactory,
         dumpManager: DumpManager,
         broadcastDispatcher: BroadcastDispatcher,
@@ -264,13 +280,17 @@
         logger: MediaUiEventLogger,
         smartspaceManager: SmartspaceManager?,
         keyguardUpdateMonitor: KeyguardUpdateMonitor,
+        mediaDataLoader: dagger.Lazy<MediaDataLoader>,
     ) : this(
         context,
         // Loading bitmap for UMO background can take longer time, so it cannot run on the default
         // background thread. Use a custom thread for media.
         threadFactory.buildExecutorOnNewThread(TAG),
+        backgroundDispatcher,
         uiExecutor,
         foregroundExecutor,
+        mainDispatcher,
+        applicationScope,
         mediaControllerFactory,
         broadcastDispatcher,
         dumpManager,
@@ -290,6 +310,7 @@
         logger,
         smartspaceManager,
         keyguardUpdateMonitor,
+        mediaDataLoader,
     )
 
     private val appChangeReceiver =
@@ -464,16 +485,31 @@
             logSingleVsMultipleMediaAdded(appUid, packageName, instanceId)
             logger.logResumeMediaAdded(appUid, packageName, instanceId)
         }
-        backgroundExecutor.execute {
-            loadMediaDataInBgForResumption(
-                userId,
-                desc,
-                action,
-                token,
-                appName,
-                appIntent,
-                packageName
-            )
+
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            applicationScope.launch {
+                loadMediaDataForResumption(
+                    userId,
+                    desc,
+                    action,
+                    token,
+                    appName,
+                    appIntent,
+                    packageName
+                )
+            }
+        } else {
+            backgroundExecutor.execute {
+                loadMediaDataInBgForResumption(
+                    userId,
+                    desc,
+                    action,
+                    token,
+                    appName,
+                    appIntent,
+                    packageName
+                )
+            }
         }
     }
 
@@ -498,9 +534,90 @@
         oldKey: String?,
         isNewlyActiveEntry: Boolean = false,
     ) {
-        backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            applicationScope.launch {
+                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry)
+            }
+        } else {
+            backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+        }
     }
 
+    private suspend fun loadMediaDataWithLoader(
+        key: String,
+        sbn: StatusBarNotification,
+        oldKey: String?,
+        isNewlyActiveEntry: Boolean = false,
+    ) =
+        withContext(backgroundDispatcher) {
+            val lastActive = systemClock.elapsedRealtime()
+            val result = mediaDataLoader.get().loadMediaData(key, sbn)
+            if (result == null) {
+                Log.d(TAG, "No result from loadMediaData")
+                return@withContext
+            }
+
+            val currentEntry = mediaEntries[key]
+            val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
+            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+            val resumeAction: Runnable? = currentEntry?.resumeAction
+            val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
+            val active = currentEntry?.active ?: true
+
+            // We need to log the correct media added.
+            if (isNewlyActiveEntry) {
+                logSingleVsMultipleMediaAdded(result.appUid, sbn.packageName, instanceId)
+                logger.logActiveMediaAdded(
+                    result.appUid,
+                    sbn.packageName,
+                    instanceId,
+                    result.playbackLocation
+                )
+            } else if (result.playbackLocation != currentEntry?.playbackLocation) {
+                logger.logPlaybackLocationChange(
+                    result.appUid,
+                    sbn.packageName,
+                    instanceId,
+                    result.playbackLocation
+                )
+            }
+
+            withContext(mainDispatcher) {
+                onMediaDataLoaded(
+                    key,
+                    oldKey,
+                    MediaData(
+                        userId = sbn.normalizedUserId,
+                        initialized = true,
+                        app = result.appName,
+                        appIcon = result.appIcon,
+                        artist = result.artist,
+                        song = result.song,
+                        artwork = result.artworkIcon,
+                        actions = result.actionIcons,
+                        actionsToShowInCompact = result.actionsToShowInCompact,
+                        semanticActions = result.semanticActions,
+                        packageName = sbn.packageName,
+                        token = result.token,
+                        clickIntent = result.clickIntent,
+                        device = result.device,
+                        active = active,
+                        resumeAction = resumeAction,
+                        playbackLocation = result.playbackLocation,
+                        notificationKey = key,
+                        hasCheckedForResume = hasCheckedForResume,
+                        isPlaying = result.isPlaying,
+                        isClearable = !sbn.isOngoing,
+                        lastActive = lastActive,
+                        createdTimestampMillis = createdTimestampMillis,
+                        instanceId = instanceId,
+                        appUid = result.appUid,
+                        isExplicit = result.isExplicit,
+                    )
+                )
+            }
+        }
+
     /** Add a listener for changes in this class */
     override fun addListener(listener: MediaDataManager.Listener) {
         // mediaDataFilter is the current end of the internal pipeline. Register external
@@ -697,6 +814,75 @@
         notifySmartspaceMediaDataLoaded(smartspaceMediaData.targetId, smartspaceMediaData)
     }
 
+    private suspend fun loadMediaDataForResumption(
+        userId: Int,
+        desc: MediaDescription,
+        resumeAction: Runnable,
+        token: MediaSession.Token,
+        appName: String,
+        appIntent: PendingIntent,
+        packageName: String
+    ) =
+        withContext(backgroundDispatcher) {
+            val lastActive = systemClock.elapsedRealtime()
+            val currentEntry = mediaEntries[packageName]
+            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+            val result =
+                mediaDataLoader
+                    .get()
+                    .loadMediaDataForResumption(
+                        userId,
+                        desc,
+                        resumeAction,
+                        currentEntry,
+                        token,
+                        appName,
+                        appIntent,
+                        packageName
+                    )
+            if (result == null || desc.title.isNullOrBlank()) {
+                Log.d(TAG, "No MediaData result for resumption")
+                mediaEntries.remove(packageName)
+                return@withContext
+            }
+
+            val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
+            withContext(mainDispatcher) {
+                onMediaDataLoaded(
+                    packageName,
+                    null,
+                    MediaData(
+                        userId = userId,
+                        initialized = true,
+                        app = result.appName,
+                        appIcon = null,
+                        artist = result.artist,
+                        song = result.song,
+                        artwork = result.artworkIcon,
+                        actions = result.actionIcons,
+                        actionsToShowInCompact = result.actionsToShowInCompact,
+                        semanticActions = result.semanticActions,
+                        packageName = packageName,
+                        token = result.token,
+                        clickIntent = result.clickIntent,
+                        device = result.device,
+                        active = false,
+                        resumeAction = resumeAction,
+                        resumption = true,
+                        notificationKey = packageName,
+                        hasCheckedForResume = true,
+                        lastActive = lastActive,
+                        createdTimestampMillis = createdTimestampMillis,
+                        instanceId = instanceId,
+                        appUid = result.appUid,
+                        isExplicit = result.isExplicit,
+                        resumeProgress = result.resumeProgress,
+                    )
+                )
+            }
+        }
+
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun loadMediaDataInBgForResumption(
         userId: Int,
         desc: MediaDescription,
@@ -780,6 +966,7 @@
         }
     }
 
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     fun loadMediaDataInBg(
         key: String,
         sbn: StatusBarNotification,
@@ -802,8 +989,7 @@
             notif.extras.getParcelable(
                 Notification.EXTRA_BUILDER_APPLICATION_INFO,
                 ApplicationInfo::class.java
-            )
-                ?: getAppInfoFromPackage(sbn.packageName)
+            ) ?: getAppInfoFromPackage(sbn.packageName)
 
         // App name
         val appName = getAppName(sbn, appInfo)
@@ -894,7 +1080,7 @@
         var actionsToShowCollapsed: List<Int> = emptyList()
         val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
         if (semanticActions == null) {
-            val actions = createActionsFromNotification(sbn)
+            val actions = createActionsFromNotification(context, activityStarter, sbn)
             actionIcons = actions.first
             actionsToShowCollapsed = actions.second
         }
@@ -975,6 +1161,7 @@
         }
     }
 
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun getAppInfoFromPackage(packageName: String): ApplicationInfo? {
         try {
             return context.packageManager.getApplicationInfo(packageName, 0)
@@ -984,6 +1171,7 @@
         return null
     }
 
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun getAppName(sbn: StatusBarNotification, appInfo: ApplicationInfo?): String {
         val name = sbn.notification.extras.getString(EXTRA_SUBSTITUTE_APP_NAME)
         if (name != null) {
@@ -997,264 +1185,19 @@
         }
     }
 
-    /** Generate action buttons based on notification actions */
-    private fun createActionsFromNotification(
-        sbn: StatusBarNotification
-    ): Pair<List<MediaAction>, List<Int>> {
-        val notif = sbn.notification
-        val actionIcons: MutableList<MediaAction> = ArrayList()
-        val actions = notif.actions
-        var actionsToShowCollapsed =
-            notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList()
-                ?: mutableListOf()
-        if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
-            Log.e(
-                TAG,
-                "Too many compact actions for ${sbn.key}," +
-                    "limiting to first $MAX_COMPACT_ACTIONS"
-            )
-            actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
-        }
-
-        if (actions != null) {
-            for ((index, action) in actions.withIndex()) {
-                if (index == MAX_NOTIFICATION_ACTIONS) {
-                    Log.w(
-                        TAG,
-                        "Too many notification actions for ${sbn.key}," +
-                            " limiting to first $MAX_NOTIFICATION_ACTIONS"
-                    )
-                    break
-                }
-                if (action.getIcon() == null) {
-                    if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
-                    actionsToShowCollapsed.remove(index)
-                    continue
-                }
-                val runnable =
-                    if (action.actionIntent != null) {
-                        Runnable {
-                            if (action.actionIntent.isActivity) {
-                                activityStarter.startPendingIntentDismissingKeyguard(
-                                    action.actionIntent
-                                )
-                            } else if (action.isAuthenticationRequired()) {
-                                activityStarter.dismissKeyguardThenExecute(
-                                    {
-                                        var result = sendPendingIntent(action.actionIntent)
-                                        result
-                                    },
-                                    {},
-                                    true
-                                )
-                            } else {
-                                sendPendingIntent(action.actionIntent)
-                            }
-                        }
-                    } else {
-                        null
-                    }
-                val mediaActionIcon =
-                    if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
-                            Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
-                        } else {
-                            action.getIcon()
-                        }
-                        .setTint(themeText)
-                        .loadDrawable(context)
-                val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null)
-                actionIcons.add(mediaAction)
-            }
-        }
-        return Pair(actionIcons, actionsToShowCollapsed)
-    }
-
-    /**
-     * Generates action button info for this media session based on the PlaybackState
-     *
-     * @param packageName Package name for the media app
-     * @param controller MediaController for the current session
-     * @return a Pair consisting of a list of media actions, and a list of ints representing which
-     *
-     * ```
-     *      of those actions should be shown in the compact player
-     * ```
-     */
     private fun createActionsFromState(
         packageName: String,
         controller: MediaController,
         user: UserHandle
     ): MediaButton? {
-        val state = controller.playbackState
-        if (state == null || !mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
+        if (!mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
             return null
         }
-
-        // First, check for standard actions
-        val playOrPause =
-            if (isConnectingState(state.state)) {
-                // Spinner needs to be animating to render anything. Start it here.
-                val drawable =
-                    context.getDrawable(com.android.internal.R.drawable.progress_small_material)
-                (drawable as Animatable).start()
-                MediaAction(
-                    drawable,
-                    null, // no action to perform when clicked
-                    context.getString(R.string.controls_media_button_connecting),
-                    context.getDrawable(R.drawable.ic_media_connecting_container),
-                    // Specify a rebind id to prevent the spinner from restarting on later binds.
-                    com.android.internal.R.drawable.progress_small_material
-                )
-            } else if (isPlayingState(state.state)) {
-                getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE)
-            } else {
-                getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY)
-            }
-        val prevButton =
-            getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_PREVIOUS)
-        val nextButton =
-            getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_NEXT)
-
-        // Then, create a way to build any custom actions that will be needed
-        val customActions =
-            state.customActions
-                .asSequence()
-                .filterNotNull()
-                .map { getCustomAction(state, packageName, controller, it) }
-                .iterator()
-        fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null
-
-        // Finally, assign the remaining button slots: play/pause A B C D
-        // A = previous, else custom action (if not reserved)
-        // B = next, else custom action (if not reserved)
-        // C and D are always custom actions
-        val reservePrev =
-            controller.extras?.getBoolean(
-                MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
-            ) == true
-        val reserveNext =
-            controller.extras?.getBoolean(
-                MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
-            ) == true
-
-        val prevOrCustom =
-            if (prevButton != null) {
-                prevButton
-            } else if (!reservePrev) {
-                nextCustomAction()
-            } else {
-                null
-            }
-
-        val nextOrCustom =
-            if (nextButton != null) {
-                nextButton
-            } else if (!reserveNext) {
-                nextCustomAction()
-            } else {
-                null
-            }
-
-        return MediaButton(
-            playOrPause,
-            nextOrCustom,
-            prevOrCustom,
-            nextCustomAction(),
-            nextCustomAction(),
-            reserveNext,
-            reservePrev
-        )
-    }
-
-    /**
-     * Create a [MediaAction] for a given action and media session
-     *
-     * @param controller MediaController for the session
-     * @param stateActions The actions included with the session's [PlaybackState]
-     * @param action A [PlaybackState.Actions] value representing what action to generate. One of:
-     * ```
-     *      [PlaybackState.ACTION_PLAY]
-     *      [PlaybackState.ACTION_PAUSE]
-     *      [PlaybackState.ACTION_SKIP_TO_PREVIOUS]
-     *      [PlaybackState.ACTION_SKIP_TO_NEXT]
-     * @return
-     * ```
-     *
-     * A [MediaAction] with correct values set, or null if the state doesn't support it
-     */
-    private fun getStandardAction(
-        controller: MediaController,
-        stateActions: Long,
-        @PlaybackState.Actions action: Long
-    ): MediaAction? {
-        if (!includesAction(stateActions, action)) {
-            return null
-        }
-
-        return when (action) {
-            PlaybackState.ACTION_PLAY -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_play),
-                    { controller.transportControls.play() },
-                    context.getString(R.string.controls_media_button_play),
-                    context.getDrawable(R.drawable.ic_media_play_container)
-                )
-            }
-            PlaybackState.ACTION_PAUSE -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_pause),
-                    { controller.transportControls.pause() },
-                    context.getString(R.string.controls_media_button_pause),
-                    context.getDrawable(R.drawable.ic_media_pause_container)
-                )
-            }
-            PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_prev),
-                    { controller.transportControls.skipToPrevious() },
-                    context.getString(R.string.controls_media_button_prev),
-                    null
-                )
-            }
-            PlaybackState.ACTION_SKIP_TO_NEXT -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_next),
-                    { controller.transportControls.skipToNext() },
-                    context.getString(R.string.controls_media_button_next),
-                    null
-                )
-            }
-            else -> null
-        }
-    }
-
-    /** Check whether the actions from a [PlaybackState] include a specific action */
-    private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Long): Boolean {
-        if (
-            (action == PlaybackState.ACTION_PLAY || action == PlaybackState.ACTION_PAUSE) &&
-                (stateActions and PlaybackState.ACTION_PLAY_PAUSE > 0L)
-        ) {
-            return true
-        }
-        return (stateActions and action != 0L)
-    }
-
-    /** Get a [MediaAction] representing a [PlaybackState.CustomAction] */
-    private fun getCustomAction(
-        state: PlaybackState,
-        packageName: String,
-        controller: MediaController,
-        customAction: PlaybackState.CustomAction
-    ): MediaAction {
-        return MediaAction(
-            Icon.createWithResource(packageName, customAction.icon).loadDrawable(context),
-            { controller.transportControls.sendCustomAction(customAction, customAction.extras) },
-            customAction.name,
-            null
-        )
+        return createActionsFromState(context, packageName, controller)
     }
 
     /** Load a bitmap from the various Art metadata URIs */
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? {
         for (uri in ART_URIS) {
             val uriString = metadata.getString(uri)
@@ -1269,21 +1212,6 @@
         return null
     }
 
-    private fun sendPendingIntent(intent: PendingIntent): Boolean {
-        return try {
-            val options = BroadcastOptions.makeBasic()
-            options.setInteractive(true)
-            options.setPendingIntentBackgroundActivityStartMode(
-                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
-            )
-            intent.send(options.toBundle())
-            true
-        } catch (e: PendingIntent.CanceledException) {
-            Log.d(TAG, "Intent canceled", e)
-            false
-        }
-    }
-
     /** Returns a bitmap if the user can access the given URI, else null */
     private fun loadBitmapFromUriForUser(
         uri: Uri,
@@ -1364,6 +1292,7 @@
         )
     }
 
+    @MainThread
     fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) =
         traceSection("MediaDataManager#onMediaDataLoaded") {
             Assert.isMainThread()
@@ -1619,6 +1548,7 @@
      * - If resumption is disabled, we only want to show active players
      */
     override fun hasAnyMedia() = mediaDataFilter.hasAnyMedia()
+
     override fun isRecommendationActive() = smartspaceMediaData.isActive
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
new file mode 100644
index 0000000..bcf748e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaActions.kt
@@ -0,0 +1,312 @@
+/*
+ * 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.media.controls.domain.pipeline
+
+import android.app.ActivityOptions
+import android.app.BroadcastOptions
+import android.app.Notification
+import android.app.PendingIntent
+import android.content.Context
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.Icon
+import android.media.session.MediaController
+import android.media.session.PlaybackState
+import android.service.notification.StatusBarNotification
+import android.util.Log
+import androidx.media.utils.MediaConstants
+import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_COMPACT_ACTIONS
+import com.android.systemui.media.controls.domain.pipeline.LegacyMediaDataManagerImpl.Companion.MAX_NOTIFICATION_ACTIONS
+import com.android.systemui.media.controls.shared.MediaControlDrawables
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
+import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
+import com.android.systemui.util.kotlin.logI
+
+private const val TAG = "MediaActions"
+
+/**
+ * Generates action button info for this media session based on the PlaybackState
+ *
+ * @param packageName Package name for the media app
+ * @param controller MediaController for the current session
+ * @return a Pair consisting of a list of media actions, and a list of ints representing which of
+ *   those actions should be shown in the compact player
+ */
+fun createActionsFromState(
+    context: Context,
+    packageName: String,
+    controller: MediaController,
+): MediaButton? {
+    val state = controller.playbackState ?: return null
+    // First, check for standard actions
+    val playOrPause =
+        if (isConnectingState(state.state)) {
+            // Spinner needs to be animating to render anything. Start it here.
+            val drawable =
+                context.getDrawable(com.android.internal.R.drawable.progress_small_material)
+            (drawable as Animatable).start()
+            MediaAction(
+                drawable,
+                null, // no action to perform when clicked
+                context.getString(R.string.controls_media_button_connecting),
+                context.getDrawable(R.drawable.ic_media_connecting_container),
+                // Specify a rebind id to prevent the spinner from restarting on later binds.
+                com.android.internal.R.drawable.progress_small_material
+            )
+        } else if (isPlayingState(state.state)) {
+            getStandardAction(context, controller, state.actions, PlaybackState.ACTION_PAUSE)
+        } else {
+            getStandardAction(context, controller, state.actions, PlaybackState.ACTION_PLAY)
+        }
+    val prevButton =
+        getStandardAction(context, controller, state.actions, PlaybackState.ACTION_SKIP_TO_PREVIOUS)
+    val nextButton =
+        getStandardAction(context, controller, state.actions, PlaybackState.ACTION_SKIP_TO_NEXT)
+
+    // Then, create a way to build any custom actions that will be needed
+    val customActions =
+        state.customActions
+            .asSequence()
+            .filterNotNull()
+            .map { getCustomAction(context, packageName, controller, it) }
+            .iterator()
+    fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null
+
+    // Finally, assign the remaining button slots: play/pause A B C D
+    // A = previous, else custom action (if not reserved)
+    // B = next, else custom action (if not reserved)
+    // C and D are always custom actions
+    val reservePrev =
+        controller.extras?.getBoolean(
+            MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
+        ) == true
+    val reserveNext =
+        controller.extras?.getBoolean(
+            MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
+        ) == true
+
+    val prevOrCustom =
+        if (prevButton != null) {
+            prevButton
+        } else if (!reservePrev) {
+            nextCustomAction()
+        } else {
+            null
+        }
+
+    val nextOrCustom =
+        if (nextButton != null) {
+            nextButton
+        } else if (!reserveNext) {
+            nextCustomAction()
+        } else {
+            null
+        }
+
+    return MediaButton(
+        playOrPause,
+        nextOrCustom,
+        prevOrCustom,
+        nextCustomAction(),
+        nextCustomAction(),
+        reserveNext,
+        reservePrev
+    )
+}
+
+/**
+ * Create a [MediaAction] for a given action and media session
+ *
+ * @param controller MediaController for the session
+ * @param stateActions The actions included with the session's [PlaybackState]
+ * @param action A [PlaybackState.Actions] value representing what action to generate. One of:
+ *   [PlaybackState.ACTION_PLAY] [PlaybackState.ACTION_PAUSE]
+ *   [PlaybackState.ACTION_SKIP_TO_PREVIOUS] [PlaybackState.ACTION_SKIP_TO_NEXT]
+ * @return A [MediaAction] with correct values set, or null if the state doesn't support it
+ */
+private fun getStandardAction(
+    context: Context,
+    controller: MediaController,
+    stateActions: Long,
+    @PlaybackState.Actions action: Long
+): MediaAction? {
+    if (!includesAction(stateActions, action)) {
+        return null
+    }
+
+    return when (action) {
+        PlaybackState.ACTION_PLAY -> {
+            MediaAction(
+                context.getDrawable(R.drawable.ic_media_play),
+                { controller.transportControls.play() },
+                context.getString(R.string.controls_media_button_play),
+                context.getDrawable(R.drawable.ic_media_play_container)
+            )
+        }
+        PlaybackState.ACTION_PAUSE -> {
+            MediaAction(
+                context.getDrawable(R.drawable.ic_media_pause),
+                { controller.transportControls.pause() },
+                context.getString(R.string.controls_media_button_pause),
+                context.getDrawable(R.drawable.ic_media_pause_container)
+            )
+        }
+        PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
+            MediaAction(
+                MediaControlDrawables.getPrevIcon(context),
+                { controller.transportControls.skipToPrevious() },
+                context.getString(R.string.controls_media_button_prev),
+                null
+            )
+        }
+        PlaybackState.ACTION_SKIP_TO_NEXT -> {
+            MediaAction(
+                MediaControlDrawables.getNextIcon(context),
+                { controller.transportControls.skipToNext() },
+                context.getString(R.string.controls_media_button_next),
+                null
+            )
+        }
+        else -> null
+    }
+}
+
+/** Get a [MediaAction] representing a [PlaybackState.CustomAction] */
+private fun getCustomAction(
+    context: Context,
+    packageName: String,
+    controller: MediaController,
+    customAction: PlaybackState.CustomAction
+): MediaAction {
+    return MediaAction(
+        Icon.createWithResource(packageName, customAction.icon).loadDrawable(context),
+        { controller.transportControls.sendCustomAction(customAction, customAction.extras) },
+        customAction.name,
+        null
+    )
+}
+
+/** Check whether the actions from a [PlaybackState] include a specific action */
+private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Long): Boolean {
+    if (
+        (action == PlaybackState.ACTION_PLAY || action == PlaybackState.ACTION_PAUSE) &&
+            (stateActions and PlaybackState.ACTION_PLAY_PAUSE > 0L)
+    ) {
+        return true
+    }
+    return (stateActions and action != 0L)
+}
+
+/** Generate action buttons based on notification actions */
+fun createActionsFromNotification(
+    context: Context,
+    activityStarter: ActivityStarter,
+    sbn: StatusBarNotification
+): Pair<List<MediaAction>, List<Int>> {
+    val notif = sbn.notification
+    val actionIcons: MutableList<MediaAction> = ArrayList()
+    val actions = notif.actions
+    var actionsToShowCollapsed =
+        notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList()
+            ?: mutableListOf()
+    if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
+        Log.e(
+            TAG,
+            "Too many compact actions for ${sbn.key}, limiting to first $MAX_COMPACT_ACTIONS"
+        )
+        actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
+    }
+
+    actions?.let {
+        if (it.size > MAX_NOTIFICATION_ACTIONS) {
+            Log.w(
+                TAG,
+                "Too many notification actions for ${sbn.key}, " +
+                    "limiting to first $MAX_NOTIFICATION_ACTIONS"
+            )
+        }
+
+        for ((index, action) in it.take(MAX_NOTIFICATION_ACTIONS).withIndex()) {
+            if (action.getIcon() == null) {
+                logI(TAG) { "No icon for action $index ${action.title}" }
+                actionsToShowCollapsed.remove(index)
+                continue
+            }
+
+            val runnable =
+                action.actionIntent?.let { actionIntent ->
+                    Runnable {
+                        when {
+                            actionIntent.isActivity ->
+                                activityStarter.startPendingIntentDismissingKeyguard(
+                                    action.actionIntent
+                                )
+                            action.isAuthenticationRequired ->
+                                activityStarter.dismissKeyguardThenExecute(
+                                    { sendPendingIntent(action.actionIntent) },
+                                    {},
+                                    true
+                                )
+                            else -> sendPendingIntent(actionIntent)
+                        }
+                    }
+                }
+
+            val themeText =
+                com.android.settingslib.Utils.getColorAttr(
+                        context,
+                        com.android.internal.R.attr.textColorPrimary
+                    )
+                    .defaultColor
+
+            val mediaActionIcon =
+                when (action.getIcon().type) {
+                        Icon.TYPE_RESOURCE ->
+                            Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
+                        else -> action.getIcon()
+                    }
+                    .setTint(themeText)
+                    .loadDrawable(context)
+
+            val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null)
+            actionIcons.add(mediaAction)
+        }
+    }
+    return Pair(actionIcons, actionsToShowCollapsed)
+}
+
+private fun sendPendingIntent(intent: PendingIntent): Boolean {
+    return try {
+        intent.send(
+            BroadcastOptions.makeBasic()
+                .apply {
+                    setInteractive(true)
+                    setPendingIntentBackgroundActivityStartMode(
+                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+                    )
+                }
+                .toBundle()
+        )
+        true
+    } catch (e: PendingIntent.CanceledException) {
+        Log.d(TAG, "Intent canceled", e)
+        false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
new file mode 100644
index 0000000..f9fef8e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoader.kt
@@ -0,0 +1,530 @@
+/*
+ * 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.media.controls.domain.pipeline
+
+import android.annotation.WorkerThread
+import android.app.Notification
+import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
+import android.app.PendingIntent
+import android.app.StatusBarManager
+import android.app.UriGrantsManager
+import android.content.ContentProvider
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.drawable.Icon
+import android.media.MediaDescription
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.net.Uri
+import android.os.Process
+import android.os.UserHandle
+import android.service.notification.StatusBarNotification
+import android.support.v4.media.MediaMetadataCompat
+import android.text.TextUtils
+import android.util.Log
+import android.util.Pair
+import androidx.media.utils.MediaConstants
+import com.android.app.tracing.coroutines.traceCoroutine
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.media.controls.shared.model.MediaAction
+import com.android.systemui.media.controls.shared.model.MediaButton
+import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.media.controls.shared.model.MediaDeviceData
+import com.android.systemui.media.controls.util.MediaControllerFactory
+import com.android.systemui.media.controls.util.MediaDataUtils
+import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
+import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import com.android.systemui.util.kotlin.logD
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+import kotlin.coroutines.coroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.ensureActive
+
+/** Loads media information from media style [StatusBarNotification] classes. */
+@SysUISingleton
+class MediaDataLoader
+@Inject
+constructor(
+    @Application val context: Context,
+    @Main val mainDispatcher: CoroutineDispatcher,
+    @Background val backgroundScope: CoroutineScope,
+    private val activityStarter: ActivityStarter,
+    private val mediaControllerFactory: MediaControllerFactory,
+    private val mediaFlags: MediaFlags,
+    private val imageLoader: ImageLoader,
+    private val statusBarManager: StatusBarManager,
+) {
+    private val mediaProcessingJobs = ConcurrentHashMap<JobKey, Job>()
+
+    private val artworkWidth: Int =
+        context.resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize
+        )
+    private val artworkHeight: Int =
+        context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
+
+    private val themeText =
+        com.android.settingslib.Utils.getColorAttr(
+                context,
+                com.android.internal.R.attr.textColorPrimary
+            )
+            .defaultColor
+
+    /**
+     * Loads media data for a given [StatusBarNotification]. It does the loading on the background
+     * thread.
+     *
+     * Returns a [MediaDataLoaderResult] if loaded data or `null` if loading failed. The method
+     * suspends until loading has completed or failed.
+     *
+     * If a new [loadMediaData] is issued while existing load is in progress, the existing (old)
+     * load will be cancelled.
+     */
+    suspend fun loadMediaData(key: String, sbn: StatusBarNotification): MediaDataLoaderResult? {
+        logD(TAG) { "Loading media data for $key..." }
+        val jobKey = JobKey(key, sbn)
+        val loadMediaJob = backgroundScope.async { loadMediaDataInBackground(key, sbn) }
+        loadMediaJob.invokeOnCompletion { mediaProcessingJobs.remove(jobKey) }
+        val existingJob = mediaProcessingJobs.put(jobKey, loadMediaJob)
+        existingJob?.cancel("New processing job incoming.")
+        return loadMediaJob.await()
+    }
+
+    /** Loads media data, should be called from [backgroundScope]. */
+    @WorkerThread
+    private suspend fun loadMediaDataInBackground(
+        key: String,
+        sbn: StatusBarNotification,
+    ): MediaDataLoaderResult? =
+        traceCoroutine("MediaDataLoader#loadMediaData") {
+            val token =
+                sbn.notification.extras.getParcelable(
+                    Notification.EXTRA_MEDIA_SESSION,
+                    MediaSession.Token::class.java
+                )
+            if (token == null) {
+                Log.i(TAG, "Token was null, not loading media info")
+                return null
+            }
+            val mediaController = mediaControllerFactory.create(token)
+            val metadata = mediaController.metadata
+            val notification: Notification = sbn.notification
+
+            val appInfo =
+                notification.extras.getParcelable(
+                    Notification.EXTRA_BUILDER_APPLICATION_INFO,
+                    ApplicationInfo::class.java
+                ) ?: getAppInfoFromPackage(sbn.packageName)
+
+            // App name
+            val appName = getAppName(sbn, appInfo)
+
+            // Song name
+            var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
+            if (song.isNullOrBlank()) {
+                song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
+            }
+            if (song.isNullOrBlank()) {
+                song = HybridGroupManager.resolveTitle(notification)
+            }
+            if (song.isNullOrBlank()) {
+                // For apps that don't include a title, log and add a placeholder
+                song = context.getString(R.string.controls_media_empty_title, appName)
+                try {
+                    statusBarManager.logBlankMediaTitle(sbn.packageName, sbn.user.identifier)
+                } catch (e: RuntimeException) {
+                    Log.e(TAG, "Error reporting blank media title for package ${sbn.packageName}")
+                }
+            }
+
+            // Don't attempt to load bitmaps if the job was cancelled.
+            coroutineContext.ensureActive()
+
+            // Album art
+            var artworkBitmap = metadata?.let { loadBitmapFromUri(it) }
+            if (artworkBitmap == null) {
+                artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
+            }
+            if (artworkBitmap == null) {
+                artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
+            }
+            val artworkIcon =
+                if (artworkBitmap == null) {
+                    notification.getLargeIcon()
+                } else {
+                    Icon.createWithBitmap(artworkBitmap)
+                }
+
+            // Don't continue if we were cancelled during slow bitmap load.
+            coroutineContext.ensureActive()
+
+            // App Icon
+            val smallIcon = sbn.notification.smallIcon
+
+            // Explicit Indicator
+            val isExplicit =
+                MediaMetadataCompat.fromMediaMetadata(metadata)
+                    ?.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT) ==
+                    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+
+            // Artist name
+            var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
+            if (artist.isNullOrBlank()) {
+                artist = HybridGroupManager.resolveText(notification)
+            }
+
+            // Device name (used for remote cast notifications)
+            val device: MediaDeviceData? = getDeviceInfoForRemoteCast(key, sbn)
+
+            // Control buttons
+            // If flag is enabled and controller has a PlaybackState, create actions from session
+            // info
+            // Otherwise, use the notification actions
+            var actionIcons: List<MediaAction> = emptyList()
+            var actionsToShowCollapsed: List<Int> = emptyList()
+            val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
+            logD(TAG) { "Semantic actions: $semanticActions" }
+            if (semanticActions == null) {
+                val actions = createActionsFromNotification(context, activityStarter, sbn)
+                actionIcons = actions.first
+                actionsToShowCollapsed = actions.second
+                logD(TAG) { "[!!] Semantic actions: $semanticActions" }
+            }
+
+            val playbackLocation = getPlaybackLocation(sbn, mediaController)
+            val isPlaying = mediaController.playbackState?.let { isPlayingState(it.state) }
+
+            val appUid = appInfo?.uid ?: Process.INVALID_UID
+            return MediaDataLoaderResult(
+                appName = appName,
+                appIcon = smallIcon,
+                artist = artist,
+                song = song,
+                artworkIcon = artworkIcon,
+                actionIcons = actionIcons,
+                actionsToShowInCompact = actionsToShowCollapsed,
+                semanticActions = semanticActions,
+                token = token,
+                clickIntent = notification.contentIntent,
+                device = device,
+                playbackLocation = playbackLocation,
+                isPlaying = isPlaying,
+                appUid = appUid,
+                isExplicit = isExplicit
+            )
+        }
+
+    /**
+     * Loads media data in background for a given set of resumption parameters. The method suspends
+     * until loading is complete or fails.
+     *
+     * Returns a [MediaDataLoaderResult] if loaded data or `null` if loading failed.
+     */
+    suspend fun loadMediaDataForResumption(
+        userId: Int,
+        desc: MediaDescription,
+        resumeAction: Runnable,
+        currentEntry: MediaData?,
+        token: MediaSession.Token,
+        appName: String,
+        appIntent: PendingIntent,
+        packageName: String
+    ): MediaDataLoaderResult? {
+        val mediaData =
+            backgroundScope.async {
+                loadMediaDataForResumptionInBackground(
+                    userId,
+                    desc,
+                    resumeAction,
+                    currentEntry,
+                    token,
+                    appName,
+                    appIntent,
+                    packageName
+                )
+            }
+        return mediaData.await()
+    }
+
+    /** Loads media data for resumption, should be called from [backgroundScope]. */
+    @WorkerThread
+    private suspend fun loadMediaDataForResumptionInBackground(
+        userId: Int,
+        desc: MediaDescription,
+        resumeAction: Runnable,
+        currentEntry: MediaData?,
+        token: MediaSession.Token,
+        appName: String,
+        appIntent: PendingIntent,
+        packageName: String
+    ): MediaDataLoaderResult? =
+        traceCoroutine("MediaDataLoader#loadMediaDataForResumption") {
+            if (desc.title.isNullOrBlank()) {
+                Log.e(TAG, "Description incomplete")
+                return null
+            }
+
+            logD(TAG) { "adding track for $userId from browser: $desc" }
+
+            val appUid = currentEntry?.appUid ?: Process.INVALID_UID
+
+            // Album art
+            var artworkBitmap = desc.iconBitmap
+            if (artworkBitmap == null && desc.iconUri != null) {
+                artworkBitmap =
+                    loadBitmapFromUriForUser(desc.iconUri!!, userId, appUid, packageName)
+            }
+            val artworkIcon =
+                if (artworkBitmap != null) {
+                    Icon.createWithBitmap(artworkBitmap)
+                } else {
+                    null
+                }
+
+            val isExplicit =
+                desc.extras?.getLong(MediaConstants.METADATA_KEY_IS_EXPLICIT) ==
+                    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT
+
+            val progress =
+                if (mediaFlags.isResumeProgressEnabled()) {
+                    MediaDataUtils.getDescriptionProgress(desc.extras)
+                } else null
+
+            val mediaAction = getResumeMediaAction(resumeAction)
+            return MediaDataLoaderResult(
+                appName = appName,
+                appIcon = null,
+                artist = desc.subtitle,
+                song = desc.title,
+                artworkIcon = artworkIcon,
+                actionIcons = listOf(mediaAction),
+                actionsToShowInCompact = listOf(0),
+                semanticActions = MediaButton(playOrPause = mediaAction),
+                token = token,
+                clickIntent = appIntent,
+                device = null,
+                playbackLocation = 0,
+                isPlaying = null,
+                appUid = appUid,
+                isExplicit = isExplicit,
+                resumeAction = resumeAction,
+                resumeProgress = progress
+            )
+        }
+
+    private fun createActionsFromState(
+        packageName: String,
+        controller: MediaController,
+        user: UserHandle
+    ): MediaButton? {
+        if (!mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
+            return null
+        }
+
+        return createActionsFromState(context, packageName, controller)
+    }
+
+    private fun getPlaybackLocation(sbn: StatusBarNotification, mediaController: MediaController) =
+        when {
+            isRemoteCastNotification(sbn) -> MediaData.PLAYBACK_CAST_REMOTE
+            mediaController.playbackInfo?.playbackType ==
+                MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL -> MediaData.PLAYBACK_LOCAL
+            else -> MediaData.PLAYBACK_CAST_LOCAL
+        }
+
+    /**
+     * Returns [MediaDeviceData] if the [StatusBarNotification] is a remote cast notification.
+     * `null` otherwise.
+     */
+    private fun getDeviceInfoForRemoteCast(
+        key: String,
+        sbn: StatusBarNotification
+    ): MediaDeviceData? {
+        val extras = sbn.notification.extras
+        val deviceName = extras.getCharSequence(Notification.EXTRA_MEDIA_REMOTE_DEVICE, null)
+        val deviceIcon = extras.getInt(Notification.EXTRA_MEDIA_REMOTE_ICON, -1)
+        val deviceIntent =
+            extras.getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT, PendingIntent::class.java)
+        logD(TAG) { "$key is RCN for $deviceName" }
+
+        if (deviceName != null && deviceIcon > -1) {
+            // Name and icon must be present, but intent may be null
+            val enabled = deviceIntent != null && deviceIntent.isActivity
+            val deviceDrawable =
+                Icon.createWithResource(sbn.packageName, deviceIcon)
+                    .loadDrawable(sbn.getPackageContext(context))
+            return MediaDeviceData(
+                enabled,
+                deviceDrawable,
+                deviceName,
+                deviceIntent,
+                showBroadcastButton = false
+            )
+        }
+        return null
+    }
+
+    private fun getAppInfoFromPackage(packageName: String): ApplicationInfo? {
+        try {
+            return context.packageManager.getApplicationInfo(packageName, 0)
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.w(TAG, "Could not get app info for $packageName", e)
+            return null
+        }
+    }
+
+    private fun getAppName(sbn: StatusBarNotification, appInfo: ApplicationInfo?): String {
+        val name = sbn.notification.extras.getString(EXTRA_SUBSTITUTE_APP_NAME)
+        return when {
+            name != null -> name
+            appInfo != null -> context.packageManager.getApplicationLabel(appInfo).toString()
+            else -> sbn.packageName
+        }
+    }
+
+    /** Load a bitmap from the various Art metadata URIs */
+    private suspend fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? {
+        for (uri in ART_URIS) {
+            val uriString = metadata.getString(uri)
+            if (!TextUtils.isEmpty(uriString)) {
+                val albumArt = loadBitmapFromUri(Uri.parse(uriString))
+                // If we got cancelled during slow album art load, cancel the rest of
+                // the process.
+                coroutineContext.ensureActive()
+                if (albumArt != null) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "loaded art from $uri")
+                    }
+                    return albumArt
+                }
+            }
+        }
+        return null
+    }
+
+    private suspend fun loadBitmapFromUri(uri: Uri): Bitmap? {
+        // ImageDecoder requires a scheme of the following types
+        if (
+            uri.scheme !in
+                listOf(
+                    ContentResolver.SCHEME_CONTENT,
+                    ContentResolver.SCHEME_ANDROID_RESOURCE,
+                    ContentResolver.SCHEME_FILE
+                )
+        ) {
+            Log.w(TAG, "Invalid album art uri $uri")
+            return null
+        }
+
+        val source = ImageLoader.Uri(uri)
+        return imageLoader.loadBitmap(
+            source,
+            artworkWidth,
+            artworkHeight,
+            allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+        )
+    }
+
+    private suspend fun loadBitmapFromUriForUser(
+        uri: Uri,
+        userId: Int,
+        appUid: Int,
+        packageName: String
+    ): Bitmap? {
+        try {
+            val ugm = UriGrantsManager.getService()
+            ugm.checkGrantUriPermission_ignoreNonSystem(
+                appUid,
+                packageName,
+                ContentProvider.getUriWithoutUserId(uri),
+                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                ContentProvider.getUserIdFromUri(uri, userId)
+            )
+            return loadBitmapFromUri(uri)
+        } catch (e: SecurityException) {
+            Log.e(TAG, "Failed to get URI permission: $e")
+        }
+        return null
+    }
+
+    /** Check whether this notification is an RCN */
+    private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean =
+        sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
+
+    private fun getResumeMediaAction(action: Runnable): MediaAction {
+        return MediaAction(
+            Icon.createWithResource(context, R.drawable.ic_media_play)
+                .setTint(themeText)
+                .loadDrawable(context),
+            action,
+            context.getString(R.string.controls_media_resume),
+            context.getDrawable(R.drawable.ic_media_play_container)
+        )
+    }
+
+    private data class JobKey(val key: String, val sbn: StatusBarNotification) :
+        Pair<String, StatusBarNotification>(key, sbn)
+
+    companion object {
+        private const val TAG = "MediaDataLoader"
+        private val ART_URIS =
+            arrayOf(
+                MediaMetadata.METADATA_KEY_ALBUM_ART_URI,
+                MediaMetadata.METADATA_KEY_ART_URI,
+                MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI
+            )
+    }
+
+    /** Returned data from loader. */
+    data class MediaDataLoaderResult(
+        val appName: String?,
+        val appIcon: Icon?,
+        val artist: CharSequence?,
+        val song: CharSequence?,
+        val artworkIcon: Icon?,
+        val actionIcons: List<MediaAction>,
+        val actionsToShowInCompact: List<Int>,
+        val semanticActions: MediaButton?,
+        val token: MediaSession.Token?,
+        val clickIntent: PendingIntent?,
+        val device: MediaDeviceData?,
+        val playbackLocation: Int,
+        val isPlaying: Boolean?,
+        val appUid: Int,
+        val isExplicit: Boolean,
+        val resumeAction: Runnable? = null,
+        val resumeProgress: Double? = null
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index adcfba7..4555810 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -16,9 +16,8 @@
 
 package com.android.systemui.media.controls.domain.pipeline
 
+import android.annotation.MainThread
 import android.annotation.SuppressLint
-import android.app.ActivityOptions
-import android.app.BroadcastOptions
 import android.app.Notification
 import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
 import android.app.PendingIntent
@@ -39,7 +38,6 @@
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
 import android.graphics.ImageDecoder
-import android.graphics.drawable.Animatable
 import android.graphics.drawable.Icon
 import android.media.MediaDescription
 import android.media.MediaMetadata
@@ -47,7 +45,6 @@
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
 import android.net.Uri
-import android.os.Handler
 import android.os.Parcelable
 import android.os.Process
 import android.os.UserHandle
@@ -63,6 +60,7 @@
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -90,7 +88,7 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.res.R
-import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
@@ -135,7 +133,7 @@
     @Background private val backgroundExecutor: Executor,
     @Main private val uiExecutor: Executor,
     @Main private val foregroundExecutor: DelayableExecutor,
-    @Main private val handler: Handler,
+    @Main private val mainDispatcher: CoroutineDispatcher,
     private val mediaControllerFactory: MediaControllerFactory,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val dumpManager: DumpManager,
@@ -150,6 +148,7 @@
     private val smartspaceManager: SmartspaceManager?,
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val mediaDataRepository: MediaDataRepository,
+    private val mediaDataLoader: dagger.Lazy<MediaDataLoader>,
 ) : CoreStartable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
 
     companion object {
@@ -215,7 +214,7 @@
         threadFactory: ThreadFactory,
         @Main uiExecutor: Executor,
         @Main foregroundExecutor: DelayableExecutor,
-        @Main handler: Handler,
+        @Main mainDispatcher: CoroutineDispatcher,
         mediaControllerFactory: MediaControllerFactory,
         dumpManager: DumpManager,
         broadcastDispatcher: BroadcastDispatcher,
@@ -228,6 +227,7 @@
         smartspaceManager: SmartspaceManager?,
         keyguardUpdateMonitor: KeyguardUpdateMonitor,
         mediaDataRepository: MediaDataRepository,
+        mediaDataLoader: dagger.Lazy<MediaDataLoader>,
     ) : this(
         context,
         applicationScope,
@@ -237,7 +237,7 @@
         threadFactory.buildExecutorOnNewThread(TAG),
         uiExecutor,
         foregroundExecutor,
-        handler,
+        mainDispatcher,
         mediaControllerFactory,
         broadcastDispatcher,
         dumpManager,
@@ -252,6 +252,7 @@
         smartspaceManager,
         keyguardUpdateMonitor,
         mediaDataRepository,
+        mediaDataLoader,
     )
 
     private val appChangeReceiver =
@@ -271,7 +272,7 @@
         }
 
     override fun start() {
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             return
         }
 
@@ -434,16 +435,30 @@
             logSingleVsMultipleMediaAdded(appUid, packageName, instanceId)
             logger.logResumeMediaAdded(appUid, packageName, instanceId)
         }
-        backgroundExecutor.execute {
-            loadMediaDataInBgForResumption(
-                userId,
-                desc,
-                action,
-                token,
-                appName,
-                appIntent,
-                packageName
-            )
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            applicationScope.launch {
+                loadMediaDataForResumption(
+                    userId,
+                    desc,
+                    action,
+                    token,
+                    appName,
+                    appIntent,
+                    packageName
+                )
+            }
+        } else {
+            backgroundExecutor.execute {
+                loadMediaDataInBgForResumption(
+                    userId,
+                    desc,
+                    action,
+                    token,
+                    appName,
+                    appIntent,
+                    packageName
+                )
+            }
         }
     }
 
@@ -469,7 +484,13 @@
         oldKey: String?,
         isNewlyActiveEntry: Boolean = false,
     ) {
-        backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            applicationScope.launch {
+                loadMediaDataWithLoader(key, sbn, oldKey, isNewlyActiveEntry)
+            }
+        } else {
+            backgroundExecutor.execute { loadMediaDataInBg(key, sbn, oldKey, isNewlyActiveEntry) }
+        }
     }
 
     /** Add a listener for internal events. */
@@ -644,6 +665,75 @@
         }
     }
 
+    private suspend fun loadMediaDataForResumption(
+        userId: Int,
+        desc: MediaDescription,
+        resumeAction: Runnable,
+        token: MediaSession.Token,
+        appName: String,
+        appIntent: PendingIntent,
+        packageName: String
+    ) =
+        withContext(backgroundDispatcher) {
+            val lastActive = systemClock.elapsedRealtime()
+            val currentEntry = mediaDataRepository.mediaEntries.value[packageName]
+            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+            val result =
+                mediaDataLoader
+                    .get()
+                    .loadMediaDataForResumption(
+                        userId,
+                        desc,
+                        resumeAction,
+                        currentEntry,
+                        token,
+                        appName,
+                        appIntent,
+                        packageName
+                    )
+            if (result == null || desc.title.isNullOrBlank()) {
+                Log.d(TAG, "No MediaData result for resumption")
+                mediaDataRepository.removeMediaEntry(packageName)
+                return@withContext
+            }
+
+            val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
+            withContext(mainDispatcher) {
+                onMediaDataLoaded(
+                    packageName,
+                    null,
+                    MediaData(
+                        userId = userId,
+                        initialized = true,
+                        app = result.appName,
+                        appIcon = null,
+                        artist = result.artist,
+                        song = result.song,
+                        artwork = result.artworkIcon,
+                        actions = result.actionIcons,
+                        actionsToShowInCompact = result.actionsToShowInCompact,
+                        semanticActions = result.semanticActions,
+                        packageName = packageName,
+                        token = result.token,
+                        clickIntent = result.clickIntent,
+                        device = result.device,
+                        active = false,
+                        resumeAction = resumeAction,
+                        resumption = true,
+                        notificationKey = packageName,
+                        hasCheckedForResume = true,
+                        lastActive = lastActive,
+                        createdTimestampMillis = createdTimestampMillis,
+                        instanceId = instanceId,
+                        appUid = result.appUid,
+                        isExplicit = result.isExplicit,
+                        resumeProgress = result.resumeProgress,
+                    )
+                )
+            }
+        }
+
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun loadMediaDataInBgForResumption(
         userId: Int,
         desc: MediaDescription,
@@ -728,6 +818,82 @@
         }
     }
 
+    private suspend fun loadMediaDataWithLoader(
+        key: String,
+        sbn: StatusBarNotification,
+        oldKey: String?,
+        isNewlyActiveEntry: Boolean = false,
+    ) =
+        withContext(backgroundDispatcher) {
+            val lastActive = systemClock.elapsedRealtime()
+            val result = mediaDataLoader.get().loadMediaData(key, sbn)
+            if (result == null) {
+                Log.d(TAG, "No result from loadMediaData")
+                return@withContext
+            }
+
+            val currentEntry = mediaDataRepository.mediaEntries.value[key]
+            val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
+            val createdTimestampMillis = currentEntry?.createdTimestampMillis ?: 0L
+            val resumeAction: Runnable? = currentEntry?.resumeAction
+            val hasCheckedForResume = currentEntry?.hasCheckedForResume == true
+            val active = currentEntry?.active ?: true
+
+            // We need to log the correct media added.
+            if (isNewlyActiveEntry) {
+                logSingleVsMultipleMediaAdded(result.appUid, sbn.packageName, instanceId)
+                logger.logActiveMediaAdded(
+                    result.appUid,
+                    sbn.packageName,
+                    instanceId,
+                    result.playbackLocation
+                )
+            } else if (result.playbackLocation != currentEntry?.playbackLocation) {
+                logger.logPlaybackLocationChange(
+                    result.appUid,
+                    sbn.packageName,
+                    instanceId,
+                    result.playbackLocation
+                )
+            }
+
+            withContext(mainDispatcher) {
+                onMediaDataLoaded(
+                    key,
+                    oldKey,
+                    MediaData(
+                        userId = sbn.normalizedUserId,
+                        initialized = true,
+                        app = result.appName,
+                        appIcon = result.appIcon,
+                        artist = result.artist,
+                        song = result.song,
+                        artwork = result.artworkIcon,
+                        actions = result.actionIcons,
+                        actionsToShowInCompact = result.actionsToShowInCompact,
+                        semanticActions = result.semanticActions,
+                        packageName = sbn.packageName,
+                        token = result.token,
+                        clickIntent = result.clickIntent,
+                        device = result.device,
+                        active = active,
+                        resumeAction = resumeAction,
+                        playbackLocation = result.playbackLocation,
+                        notificationKey = key,
+                        hasCheckedForResume = hasCheckedForResume,
+                        isPlaying = result.isPlaying,
+                        isClearable = !sbn.isOngoing,
+                        lastActive = lastActive,
+                        createdTimestampMillis = createdTimestampMillis,
+                        instanceId = instanceId,
+                        appUid = result.appUid,
+                        isExplicit = result.isExplicit,
+                    )
+                )
+            }
+        }
+
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     fun loadMediaDataInBg(
         key: String,
         sbn: StatusBarNotification,
@@ -841,7 +1007,7 @@
         var actionsToShowCollapsed: List<Int> = emptyList()
         val semanticActions = createActionsFromState(sbn.packageName, mediaController, sbn.user)
         if (semanticActions == null) {
-            val actions = createActionsFromNotification(sbn)
+            val actions = createActionsFromNotification(context, activityStarter, sbn)
             actionIcons = actions.first
             actionsToShowCollapsed = actions.second
         }
@@ -924,6 +1090,7 @@
         }
     }
 
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun getAppInfoFromPackage(packageName: String): ApplicationInfo? {
         try {
             return context.packageManager.getApplicationInfo(packageName, 0)
@@ -933,6 +1100,7 @@
         return null
     }
 
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun getAppName(sbn: StatusBarNotification, appInfo: ApplicationInfo?): String {
         val name = sbn.notification.extras.getString(EXTRA_SUBSTITUTE_APP_NAME)
         if (name != null) {
@@ -946,78 +1114,6 @@
         }
     }
 
-    /** Generate action buttons based on notification actions */
-    private fun createActionsFromNotification(
-        sbn: StatusBarNotification
-    ): Pair<List<MediaAction>, List<Int>> {
-        val notif = sbn.notification
-        val actionIcons: MutableList<MediaAction> = ArrayList()
-        val actions = notif.actions
-        var actionsToShowCollapsed =
-            notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList()
-                ?: mutableListOf()
-        if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
-            Log.e(
-                TAG,
-                "Too many compact actions for ${sbn.key}," +
-                    "limiting to first $MAX_COMPACT_ACTIONS"
-            )
-            actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
-        }
-
-        if (actions != null) {
-            for ((index, action) in actions.withIndex()) {
-                if (index == MAX_NOTIFICATION_ACTIONS) {
-                    Log.w(
-                        TAG,
-                        "Too many notification actions for ${sbn.key}," +
-                            " limiting to first $MAX_NOTIFICATION_ACTIONS"
-                    )
-                    break
-                }
-                if (action.getIcon() == null) {
-                    if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
-                    actionsToShowCollapsed.remove(index)
-                    continue
-                }
-                val runnable =
-                    if (action.actionIntent != null) {
-                        Runnable {
-                            if (action.actionIntent.isActivity) {
-                                activityStarter.startPendingIntentDismissingKeyguard(
-                                    action.actionIntent
-                                )
-                            } else if (action.isAuthenticationRequired()) {
-                                activityStarter.dismissKeyguardThenExecute(
-                                    {
-                                        var result = sendPendingIntent(action.actionIntent)
-                                        result
-                                    },
-                                    {},
-                                    true
-                                )
-                            } else {
-                                sendPendingIntent(action.actionIntent)
-                            }
-                        }
-                    } else {
-                        null
-                    }
-                val mediaActionIcon =
-                    if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
-                            Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
-                        } else {
-                            action.getIcon()
-                        }
-                        .setTint(themeText)
-                        .loadDrawable(context)
-                val mediaAction = MediaAction(mediaActionIcon, runnable, action.title, null)
-                actionIcons.add(mediaAction)
-            }
-        }
-        return Pair(actionIcons, actionsToShowCollapsed)
-    }
-
     /**
      * Generates action button info for this media session based on the PlaybackState
      *
@@ -1034,175 +1130,14 @@
         controller: MediaController,
         user: UserHandle
     ): MediaButton? {
-        val state = controller.playbackState
-        if (state == null || !mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
+        if (!mediaFlags.areMediaSessionActionsEnabled(packageName, user)) {
             return null
         }
-
-        // First, check for standard actions
-        val playOrPause =
-            if (isConnectingState(state.state)) {
-                // Spinner needs to be animating to render anything. Start it here.
-                val drawable =
-                    context.getDrawable(com.android.internal.R.drawable.progress_small_material)
-                (drawable as Animatable).start()
-                MediaAction(
-                    drawable,
-                    null, // no action to perform when clicked
-                    context.getString(R.string.controls_media_button_connecting),
-                    context.getDrawable(R.drawable.ic_media_connecting_container),
-                    // Specify a rebind id to prevent the spinner from restarting on later binds.
-                    com.android.internal.R.drawable.progress_small_material
-                )
-            } else if (isPlayingState(state.state)) {
-                getStandardAction(controller, state.actions, PlaybackState.ACTION_PAUSE)
-            } else {
-                getStandardAction(controller, state.actions, PlaybackState.ACTION_PLAY)
-            }
-        val prevButton =
-            getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_PREVIOUS)
-        val nextButton =
-            getStandardAction(controller, state.actions, PlaybackState.ACTION_SKIP_TO_NEXT)
-
-        // Then, create a way to build any custom actions that will be needed
-        val customActions =
-            state.customActions
-                .asSequence()
-                .filterNotNull()
-                .map { getCustomAction(packageName, controller, it) }
-                .iterator()
-        fun nextCustomAction() = if (customActions.hasNext()) customActions.next() else null
-
-        // Finally, assign the remaining button slots: play/pause A B C D
-        // A = previous, else custom action (if not reserved)
-        // B = next, else custom action (if not reserved)
-        // C and D are always custom actions
-        val reservePrev =
-            controller.extras?.getBoolean(
-                MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
-            ) == true
-        val reserveNext =
-            controller.extras?.getBoolean(
-                MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
-            ) == true
-
-        val prevOrCustom =
-            if (prevButton != null) {
-                prevButton
-            } else if (!reservePrev) {
-                nextCustomAction()
-            } else {
-                null
-            }
-
-        val nextOrCustom =
-            if (nextButton != null) {
-                nextButton
-            } else if (!reserveNext) {
-                nextCustomAction()
-            } else {
-                null
-            }
-
-        return MediaButton(
-            playOrPause,
-            nextOrCustom,
-            prevOrCustom,
-            nextCustomAction(),
-            nextCustomAction(),
-            reserveNext,
-            reservePrev
-        )
-    }
-
-    /**
-     * Create a [MediaAction] for a given action and media session
-     *
-     * @param controller MediaController for the session
-     * @param stateActions The actions included with the session's [PlaybackState]
-     * @param action A [PlaybackState.Actions] value representing what action to generate. One of:
-     * ```
-     *      [PlaybackState.ACTION_PLAY]
-     *      [PlaybackState.ACTION_PAUSE]
-     *      [PlaybackState.ACTION_SKIP_TO_PREVIOUS]
-     *      [PlaybackState.ACTION_SKIP_TO_NEXT]
-     * @return
-     * ```
-     *
-     * A [MediaAction] with correct values set, or null if the state doesn't support it
-     */
-    private fun getStandardAction(
-        controller: MediaController,
-        stateActions: Long,
-        @PlaybackState.Actions action: Long
-    ): MediaAction? {
-        if (!includesAction(stateActions, action)) {
-            return null
-        }
-
-        return when (action) {
-            PlaybackState.ACTION_PLAY -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_play),
-                    { controller.transportControls.play() },
-                    context.getString(R.string.controls_media_button_play),
-                    context.getDrawable(R.drawable.ic_media_play_container)
-                )
-            }
-            PlaybackState.ACTION_PAUSE -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_pause),
-                    { controller.transportControls.pause() },
-                    context.getString(R.string.controls_media_button_pause),
-                    context.getDrawable(R.drawable.ic_media_pause_container)
-                )
-            }
-            PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_prev),
-                    { controller.transportControls.skipToPrevious() },
-                    context.getString(R.string.controls_media_button_prev),
-                    null
-                )
-            }
-            PlaybackState.ACTION_SKIP_TO_NEXT -> {
-                MediaAction(
-                    context.getDrawable(R.drawable.ic_media_next),
-                    { controller.transportControls.skipToNext() },
-                    context.getString(R.string.controls_media_button_next),
-                    null
-                )
-            }
-            else -> null
-        }
-    }
-
-    /** Check whether the actions from a [PlaybackState] include a specific action */
-    private fun includesAction(stateActions: Long, @PlaybackState.Actions action: Long): Boolean {
-        if (
-            (action == PlaybackState.ACTION_PLAY || action == PlaybackState.ACTION_PAUSE) &&
-                (stateActions and PlaybackState.ACTION_PLAY_PAUSE > 0L)
-        ) {
-            return true
-        }
-        return (stateActions and action != 0L)
-    }
-
-    /** Get a [MediaAction] representing a [PlaybackState.CustomAction] */
-    private fun getCustomAction(
-        packageName: String,
-        controller: MediaController,
-        customAction: PlaybackState.CustomAction
-    ): MediaAction {
-        return MediaAction(
-            Icon.createWithResource(packageName, customAction.icon).loadDrawable(context),
-            { controller.transportControls.sendCustomAction(customAction, customAction.extras) },
-            customAction.name,
-            null
-        )
+        return createActionsFromState(context, packageName, controller)
     }
 
     /** Load a bitmap from the various Art metadata URIs */
+    @Deprecated("Cleanup when media_load_metadata_via_media_data_loader is cleaned up")
     private fun loadBitmapFromUri(metadata: MediaMetadata): Bitmap? {
         for (uri in ART_URIS) {
             val uriString = metadata.getString(uri)
@@ -1217,21 +1152,6 @@
         return null
     }
 
-    private fun sendPendingIntent(intent: PendingIntent): Boolean {
-        return try {
-            val options = BroadcastOptions.makeBasic()
-            options.setInteractive(true)
-            options.setPendingIntentBackgroundActivityStartMode(
-                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
-            )
-            intent.send(options.toBundle())
-            true
-        } catch (e: PendingIntent.CanceledException) {
-            Log.d(TAG, "Intent canceled", e)
-            false
-        }
-    }
-
     /** Returns a bitmap if the user can access the given URI, else null */
     private fun loadBitmapFromUriForUser(
         uri: Uri,
@@ -1312,6 +1232,7 @@
         )
     }
 
+    @MainThread
     fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) =
         traceSection("MediaDataProcessor#onMediaDataLoaded") {
             Assert.isMainThread()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index eab0d48..a193f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -39,6 +39,7 @@
 import com.android.settingslib.media.flags.Flags
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.media.controls.shared.MediaControlDrawables
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.shared.model.MediaDeviceData
 import com.android.systemui.media.controls.util.LocalMediaManagerFactory
@@ -142,6 +143,7 @@
     interface Listener {
         /** Called when the route has changed for a given notification. */
         fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
+
         /** Called when the notification was removed. */
         fun onKeyRemoved(key: String, userInitiated: Boolean)
     }
@@ -159,6 +161,7 @@
 
         val token
             get() = controller?.sessionToken
+
         private var started = false
         private var playbackType = PLAYBACK_TYPE_UNKNOWN
         private var playbackVolumeControlId: String? = null
@@ -170,6 +173,7 @@
                     fgExecutor.execute { processDevice(key, oldKey, value) }
                 }
             }
+
         // A device that is not yet connected but is expected to connect imminently. Because it's
         // expected to connect imminently, it should be displayed as the current device.
         private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null
@@ -354,12 +358,12 @@
 
                     activeDevice =
                         routingSession?.let {
-                            val icon = if (it.selectedRoutes.size > 1) {
-                                context.getDrawable(
-                                        com.android.settingslib.R.drawable.ic_media_group_device)
-                            } else {
-                                connectedDevice?.icon // Single route. We don't change the icon.
-                            }
+                            val icon =
+                                if (it.selectedRoutes.size > 1) {
+                                    MediaControlDrawables.getGroupDevice(context)
+                                } else {
+                                    connectedDevice?.icon // Single route. We don't change the icon.
+                                }
                             // For a remote session, always use the current device from
                             // LocalMediaManager. Override with routing session information if
                             // available:
@@ -367,14 +371,16 @@
                             //   - Icon: To show the group icon if there's more than one selected
                             //           route.
                             connectedDevice?.copy(
-                                    name = it.name ?: connectedDevice.name,
-                                    icon = icon)
-                        } ?: MediaDeviceData(
-                            enabled = false,
-                            icon = context.getDrawable(R.drawable.ic_media_home_devices),
-                            name = context.getString(R.string.media_seamless_other_device),
-                            showBroadcastButton = false
-                        )
+                                name = it.name ?: connectedDevice.name,
+                                icon = icon
+                            )
+                        }
+                            ?: MediaDeviceData(
+                                enabled = false,
+                                icon = MediaControlDrawables.getHomeDevices(context),
+                                name = context.getString(R.string.media_seamless_other_device),
+                                showBroadcastButton = false
+                            )
                 } else {
                     // Prefer SASS if available when playback is local.
                     activeDevice = getSassDevice() ?: connectedDevice
@@ -434,10 +440,7 @@
             return if (enableLeAudioSharing()) {
                 MediaDeviceData(
                     enabled = false,
-                    icon =
-                        context.getDrawable(
-                            com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
-                        ),
+                    icon = MediaControlDrawables.getLeAudioSharing(context),
                     name = context.getString(R.string.audio_sharing_description),
                     intent = null,
                     showBroadcastButton = false
@@ -445,13 +448,14 @@
             } else {
                 MediaDeviceData(
                     enabled = true,
-                    icon = context.getDrawable(R.drawable.settings_input_antenna),
+                    icon = MediaControlDrawables.getAntenna(context),
                     name = broadcastDescription,
                     intent = null,
                     showBroadcastButton = true
                 )
             }
         }
+
         /** Return a display name for the current device / route, or null if not possible */
         private fun getDeviceName(
             device: MediaDevice?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 9d7160c..270ab72 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -105,7 +105,7 @@
     val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
 
     override fun start() {
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.kt
new file mode 100644
index 0000000..95ca11c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/MediaControlDrawables.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.
+ */
+
+package com.android.systemui.media.controls.shared
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import com.android.systemui.Flags.mediaControlsDrawablesReuse
+import com.android.systemui.res.R
+
+object MediaControlDrawables {
+
+    // Prev button.
+    private var prevIcon: Drawable? = null
+    // Next button.
+    private var nextIcon: Drawable? = null
+    // Output switcher drawables.
+    private var leAudioSharing: Drawable? = null
+    private var antenna: Drawable? = null
+    private var groupDevice: Drawable? = null
+    private var homeDevices: Drawable? = null
+
+    fun getNextIcon(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(R.drawable.ic_media_next)
+        }
+        return nextIcon ?: context.getDrawable(R.drawable.ic_media_next).also { nextIcon = it }
+    }
+
+    fun getPrevIcon(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(R.drawable.ic_media_prev)
+        }
+        return prevIcon ?: context.getDrawable(R.drawable.ic_media_prev).also { prevIcon = it }
+    }
+
+    fun getLeAudioSharing(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
+        }
+        return leAudioSharing
+            ?: context.getDrawable(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing).also {
+                leAudioSharing = it
+            }
+    }
+
+    fun getAntenna(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(R.drawable.settings_input_antenna)
+        }
+        return antenna
+            ?: context.getDrawable(R.drawable.settings_input_antenna).also { antenna = it }
+    }
+
+    fun getGroupDevice(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(com.android.settingslib.R.drawable.ic_media_group_device)
+        }
+        return groupDevice
+            ?: context.getDrawable(com.android.settingslib.R.drawable.ic_media_group_device).also {
+                groupDevice = it
+            }
+    }
+
+    fun getHomeDevices(context: Context): Drawable? {
+        if (!mediaControlsDrawablesReuse()) {
+            return context.getDrawable(R.drawable.ic_media_home_devices)
+        }
+        return homeDevices
+            ?: context.getDrawable(R.drawable.ic_media_home_devices).also { homeDevices = it }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 62759a4..6373fed 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -54,7 +54,6 @@
 import com.android.systemui.media.controls.ui.viewmodel.MediaOutputSwitcherViewModel
 import com.android.systemui.media.controls.ui.viewmodel.MediaPlayerViewModel
 import com.android.systemui.media.controls.util.MediaDataUtils
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.monet.ColorScheme
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
@@ -76,7 +75,6 @@
         falsingManager: FalsingManager,
         @Background backgroundDispatcher: CoroutineDispatcher,
         @Main mainDispatcher: CoroutineDispatcher,
-        mediaFlags: MediaFlags,
     ) {
         val mediaCard = viewHolder.player
         mediaCard.repeatWhenAttached {
@@ -91,7 +89,6 @@
                                 falsingManager,
                                 backgroundDispatcher,
                                 mainDispatcher,
-                                mediaFlags
                             )
                         }
                     }
@@ -107,7 +104,6 @@
         falsingManager: FalsingManager,
         backgroundDispatcher: CoroutineDispatcher,
         mainDispatcher: CoroutineDispatcher,
-        mediaFlags: MediaFlags,
     ) {
         // Set up media control location and its listener.
         viewModel.onLocationChanged(viewController.currentEndLocation)
@@ -164,21 +160,16 @@
             isSongUpdated
         )
 
-        // TODO: We don't need to refresh this state constantly, only if the
-        // state actually changed to something which might impact the
-        // measurement. State refresh interferes with the translation
-        // animation, only run it if it's not running.
-        if (!viewController.metadataAnimationHandler.isRunning) {
-            // Don't refresh in scene framework, because it will calculate
-            // with invalid layout sizes
-            if (!mediaFlags.isSceneContainerEnabled()) {
-                viewController.refreshState()
-            }
-        }
-
         if (viewModel.playTurbulenceNoise) {
             viewController.setUpTurbulenceNoise()
         }
+
+        // TODO: We don't need to refresh this state constantly, only if the state actually changed
+        // to something which might impact the measurement
+        // State refresh interferes with the translation animation, only run it if it's not running.
+        if (!viewController.metadataAnimationHandler.isRunning) {
+            viewController.refreshState()
+        }
     }
 
     private fun bindOutputSwitcherModel(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
index e17c0bb..9bea6e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.content.res.Configuration
+import android.graphics.Rect
 import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
@@ -125,6 +126,7 @@
     /** single pane media container placed at the top of the notifications list */
     var singlePaneContainer: MediaContainerView? = null
         private set
+
     private var splitShadeContainer: ViewGroup? = null
 
     /**
@@ -185,6 +187,13 @@
         }
     }
 
+    fun isWithinMediaViewBounds(x: Int, y: Int): Boolean {
+        val bounds = Rect()
+        mediaHost.hostView.getBoundsOnScreen(bounds)
+
+        return bounds.contains(x, y)
+    }
+
     fun refreshMediaPosition(reason: String) {
         val currentState = statusBarStateController.state
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index c5d7b25..bf9ef8c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -45,10 +45,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 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
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -75,7 +75,6 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PageIndicator
 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 com.android.systemui.shared.system.SysUiStatsLog
@@ -103,13 +102,16 @@
 import javax.inject.Provider
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -121,6 +123,7 @@
  * Class that is responsible for keeping the view carousel up to date. This also handles changes in
  * state and applies them to the media carousel like the expansion.
  */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class MediaCarouselController
 @Inject
@@ -149,7 +152,7 @@
     private val secureSettings: SecureSettings,
     private val mediaCarouselViewModel: MediaCarouselViewModel,
     private val mediaViewControllerFactory: Provider<MediaViewController>,
-    private val sceneInteractor: SceneInteractor,
+    private val deviceEntryInteractor: DeviceEntryInteractor,
 ) : Dumpable {
     /** The current width of the carousel */
     var currentCarouselWidth: Int = 0
@@ -164,6 +167,9 @@
     /** Is the player currently visible (at the end of the transformation */
     private var playersVisible: Boolean = false
 
+    /** Are we currently disabling pagination only allowing one media session to show */
+    private var currentlyDisablePagination: Boolean = false
+
     /**
      * The desired location where we'll be at the end of the transformation. Usually this matches
      * the end location, except when we're still waiting on a state update call.
@@ -220,10 +226,10 @@
     private val animationScaleObserver: ContentObserver =
         object : ContentObserver(executor, 0) {
             override fun onChange(selfChange: Boolean) {
-                if (!mediaFlags.isSceneContainerEnabled()) {
+                if (!SceneContainerFlag.isEnabled) {
                     MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
                 } else {
-                    controllerByViewModel.values.forEach { it.updateAnimatorDurationScale() }
+                    controllerById.values.forEach { it.updateAnimatorDurationScale() }
                 }
             }
         }
@@ -324,9 +330,14 @@
     private var widthInSceneContainerPx = 0
     private var heightInSceneContainerPx = 0
 
-    private val controllerByViewModel = mutableMapOf<MediaCommonViewModel, MediaViewController>()
+    private val controllerById = mutableMapOf<String, MediaViewController>()
     private val commonViewModels = mutableListOf<MediaCommonViewModel>()
 
+    private val isOnGone =
+        keyguardTransitionInteractor
+            .isFinishedIn(Scenes.Gone, GONE)
+            .stateIn(applicationScope, SharingStarted.Eagerly, true)
+
     init {
         dumpManager.registerDumpable(TAG, this)
         mediaFrame = inflateMediaCarousel()
@@ -350,7 +361,7 @@
         inflateSettingsButton()
         mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
         configurationController.addCallback(configListener)
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             setUpListeners()
         } else {
             val visualStabilityCallback = OnReorderingAllowedListener {
@@ -391,7 +402,7 @@
                 listenForAnyStateToGoneKeyguardTransition(this)
                 listenForAnyStateToLockscreenTransition(this)
 
-                if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
+                if (!SceneContainerFlag.isEnabled) return@repeatOnLifecycle
                 listenForMediaItemsChanges(this)
             }
         }
@@ -733,12 +744,12 @@
         when (commonViewModel) {
             is MediaCommonViewModel.MediaControl -> {
                 val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
-                if (mediaFlags.isSceneContainerEnabled()) {
+                if (SceneContainerFlag.isEnabled) {
                     viewController.widthInSceneContainerPx = widthInSceneContainerPx
                     viewController.heightInSceneContainerPx = heightInSceneContainerPx
                 }
                 viewController.attachPlayer(viewHolder)
-                viewController.mediaViewHolder.player.layoutParams = lp
+                viewController.mediaViewHolder?.player?.layoutParams = lp
                 MediaControlViewBinder.bind(
                     viewHolder,
                     commonViewModel.controlViewModel,
@@ -746,15 +757,15 @@
                     falsingManager,
                     backgroundDispatcher,
                     mainDispatcher,
-                    mediaFlags
                 )
                 mediaContent.addView(viewHolder.player, position)
+                controllerById[commonViewModel.instanceId.toString()] = viewController
             }
             is MediaCommonViewModel.MediaRecommendations -> {
                 val viewHolder =
                     RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)
                 viewController.attachRecommendations(viewHolder)
-                viewController.recommendationViewHolder.recommendations.layoutParams = lp
+                viewController.recommendationViewHolder?.recommendations?.layoutParams = lp
                 MediaRecommendationsViewBinder.bind(
                     viewHolder,
                     commonViewModel.recsViewModel,
@@ -762,11 +773,11 @@
                     falsingManager,
                 )
                 mediaContent.addView(viewHolder.recommendations, position)
+                controllerById[commonViewModel.key] = viewController
             }
         }
         onAddOrUpdateVisibleToUserCard(position, isMediaCardUpdate = false)
         viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
-        controllerByViewModel[commonViewModel] = viewController
         updateViewControllerToState(viewController, noAnimation = true)
         updatePageIndicator()
         if (
@@ -793,14 +804,19 @@
     }
 
     private fun onRemoved(commonViewModel: MediaCommonViewModel) {
-        controllerByViewModel.remove(commonViewModel)?.let {
+        val id =
+            when (commonViewModel) {
+                is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString()
+                is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key
+            }
+        controllerById.remove(id)?.let {
             when (commonViewModel) {
                 is MediaCommonViewModel.MediaControl -> {
-                    mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder.player)
-                    mediaContent.removeView(it.mediaViewHolder.player)
+                    mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player)
+                    mediaContent.removeView(it.mediaViewHolder!!.player)
                 }
                 is MediaCommonViewModel.MediaRecommendations -> {
-                    mediaContent.removeView(it.recommendationViewHolder.recommendations)
+                    mediaContent.removeView(it.recommendationViewHolder!!.recommendations)
                 }
             }
             it.onDestroy()
@@ -811,14 +827,19 @@
     }
 
     private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) {
-        controllerByViewModel[commonViewModel]?.let {
+        val id =
+            when (commonViewModel) {
+                is MediaCommonViewModel.MediaControl -> commonViewModel.instanceId.toString()
+                is MediaCommonViewModel.MediaRecommendations -> commonViewModel.key
+            }
+        controllerById[id]?.let {
             mediaContent.removeViewAt(from)
             when (commonViewModel) {
                 is MediaCommonViewModel.MediaControl -> {
-                    mediaContent.addView(it.mediaViewHolder.player, to)
+                    mediaContent.addView(it.mediaViewHolder!!.player, to)
                 }
                 is MediaCommonViewModel.MediaRecommendations -> {
-                    mediaContent.addView(it.recommendationViewHolder.recommendations, to)
+                    mediaContent.addView(it.recommendationViewHolder!!.recommendations, to)
                 }
             }
         }
@@ -855,15 +876,16 @@
                     }
                 }
                 .toHashSet()
-        controllerByViewModel
-            .filter {
-                when (val viewModel = it.key) {
-                    is MediaCommonViewModel.MediaControl ->
-                        !viewIds.contains(viewModel.instanceId.toString())
-                    is MediaCommonViewModel.MediaRecommendations -> !viewIds.contains(viewModel.key)
-                }
+        controllerById
+            .filter { !viewIds.contains(it.key) }
+            .forEach {
+                mediaCarouselScrollHandler.onPrePlayerRemoved(it.value.mediaViewHolder?.player)
+                mediaContent.removeView(it.value.mediaViewHolder?.player)
+                mediaContent.removeView(it.value.recommendationViewHolder?.recommendations)
+                it.value.onDestroy()
+                mediaCarouselScrollHandler.onPlayersChanged()
+                updatePageIndicator()
             }
-            .forEach { onRemoved(it.key) }
     }
 
     private suspend fun getMediaLockScreenSetting(): Boolean {
@@ -888,16 +910,18 @@
         heightInSceneContainerPx = height
         mediaCarouselScrollHandler.playerWidthPlusPadding =
             width + context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
-        mediaContent.minimumWidth = widthInSceneContainerPx
-        mediaContent.minimumHeight = heightInSceneContainerPx
         updatePlayers(recreateMedia = true)
     }
 
     /** Return true if the carousel should be hidden because lockscreen is currently visible */
     fun isLockedAndHidden(): Boolean {
-        val keyguardState = keyguardTransitionInteractor.getFinishedState()
-        return !allowMediaPlayerOnLockScreen &&
-            KeyguardState.lockscreenVisibleInState(keyguardState)
+        val isOnLockscreen =
+            if (SceneContainerFlag.isEnabled) {
+                !deviceEntryInteractor.isDeviceEntered.value
+            } else {
+                !isOnGone.value
+            }
+        return !allowMediaPlayerOnLockScreen && isOnLockscreen
     }
 
     private fun reorderAllPlayers(
@@ -956,7 +980,7 @@
                     .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
             if (existingPlayer == null) {
                 val newPlayer = mediaControlPanelFactory.get()
-                if (mediaFlags.isSceneContainerEnabled()) {
+                if (SceneContainerFlag.isEnabled) {
                     newPlayer.mediaViewController.widthInSceneContainerPx = widthInSceneContainerPx
                     newPlayer.mediaViewController.heightInSceneContainerPx =
                         heightInSceneContainerPx
@@ -1131,7 +1155,7 @@
     }
 
     private fun updatePlayers(recreateMedia: Boolean) {
-        if (mediaFlags.isSceneContainerEnabled()) {
+        if (SceneContainerFlag.isEnabled) {
             updateMediaPlayers(recreateMedia)
             return
         }
@@ -1178,12 +1202,12 @@
             commonViewModels.forEach { viewModel ->
                 when (viewModel) {
                     is MediaCommonViewModel.MediaControl -> {
-                        controllerByViewModel[viewModel]?.mediaViewHolder?.let {
+                        controllerById[viewModel.instanceId.toString()]?.mediaViewHolder?.let {
                             mediaContent.addView(it.player)
                         }
                     }
                     is MediaCommonViewModel.MediaRecommendations -> {
-                        controllerByViewModel[viewModel]?.recommendationViewHolder?.let {
+                        controllerById[viewModel.key]?.recommendationViewHolder?.let {
                             mediaContent.addView(it.recommendations)
                         }
                     }
@@ -1231,14 +1255,12 @@
             currentStartLocation = startLocation
             currentEndLocation = endLocation
             currentTransitionProgress = progress
-            if (!mediaFlags.isSceneContainerEnabled()) {
+            if (!SceneContainerFlag.isEnabled) {
                 for (mediaPlayer in MediaPlayerData.players()) {
                     updateViewControllerToState(mediaPlayer.mediaViewController, immediately)
                 }
             } else {
-                controllerByViewModel.values.forEach {
-                    updateViewControllerToState(it, immediately)
-                }
+                controllerById.values.forEach { updateViewControllerToState(it, immediately) }
             }
             maybeResetSettingsCog()
             updatePageIndicatorAlpha()
@@ -1293,14 +1315,12 @@
 
     /** Update listening to seekbar. */
     private fun updateSeekbarListening(visibleToUser: Boolean) {
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             for (player in MediaPlayerData.players()) {
                 player.setListening(visibleToUser && currentlyExpanded)
             }
         } else {
-            controllerByViewModel.values.forEach {
-                it.setListening(visibleToUser && currentlyExpanded)
-            }
+            controllerById.values.forEach { it.setListening(visibleToUser && currentlyExpanded) }
         }
     }
 
@@ -1308,7 +1328,7 @@
     private fun updateCarouselDimensions() {
         var width = 0
         var height = 0
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             for (mediaPlayer in MediaPlayerData.players()) {
                 val controller = mediaPlayer.mediaViewController
                 // When transitioning the view to gone, the view gets smaller, but the translation
@@ -1318,7 +1338,7 @@
                     Math.max(height, controller.currentHeight + controller.translationY.toInt())
             }
         } else {
-            controllerByViewModel.values.forEach {
+            controllerById.values.forEach {
                 // When transitioning the view to gone, the view gets smaller, but the translation
                 // Doesn't, let's add the translation
                 width = Math.max(width, it.currentWidth + it.translationX.toInt())
@@ -1342,14 +1362,20 @@
         val endShowsActive = hostStates[currentEndLocation]?.showsOnlyActiveMedia ?: true
         val startShowsActive =
             hostStates[currentStartLocation]?.showsOnlyActiveMedia ?: endShowsActive
+        val startDisablePagination = hostStates[currentStartLocation]?.disablePagination ?: false
+        val endDisablePagination = hostStates[currentEndLocation]?.disablePagination ?: false
+
         if (
             currentlyShowingOnlyActive != endShowsActive ||
+                currentlyDisablePagination != endDisablePagination ||
                 ((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
-                    startShowsActive != endShowsActive)
+                    (startShowsActive != endShowsActive ||
+                        startDisablePagination != endDisablePagination))
         ) {
             // Whenever we're transitioning from between differing states or the endstate differs
             // we reset the translation
             currentlyShowingOnlyActive = endShowsActive
+            currentlyDisablePagination = endDisablePagination
             mediaCarouselScrollHandler.resetTranslation(animate = true)
         }
     }
@@ -1400,7 +1426,7 @@
                         !mediaManager.hasActiveMediaOrRecommendation() &&
                         desiredHostState.showsOnlyActiveMedia
 
-                if (!mediaFlags.isSceneContainerEnabled()) {
+                if (!SceneContainerFlag.isEnabled) {
                     for (mediaPlayer in MediaPlayerData.players()) {
                         if (animate) {
                             mediaPlayer.mediaViewController.animatePendingStateChange(
@@ -1415,7 +1441,7 @@
                         mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
                     }
                 } else {
-                    controllerByViewModel.values.forEach { controller ->
+                    controllerById.values.forEach { controller ->
                         if (animate) {
                             controller.animatePendingStateChange(duration, startDelay)
                         }
@@ -1440,10 +1466,10 @@
         }
 
     fun closeGuts(immediate: Boolean = true) {
-        if (!mediaFlags.isSceneContainerEnabled()) {
+        if (!SceneContainerFlag.isEnabled) {
             MediaPlayerData.players().forEach { it.closeGuts(immediate) }
         } else {
-            controllerByViewModel.values.forEach { it.closeGuts(immediate) }
+            controllerById.values.forEach { it.closeGuts(immediate) }
         }
     }
 
@@ -1591,7 +1617,7 @@
 
     @VisibleForTesting
     fun onSwipeToDismiss() {
-        if (mediaFlags.isSceneContainerEnabled()) {
+        if (SceneContainerFlag.isEnabled) {
             mediaCarouselViewModel.onSwipeToDismiss(currentEndLocation)
             return
         }
@@ -1637,6 +1663,7 @@
                     "only active ${desiredHostState?.showsOnlyActiveMedia}"
             )
             println("isSwipedAway: ${MediaPlayerData.isSwipedAway}")
+            println("allowMediaPlayerOnLockScreen: $allowMediaPlayerOnLockScreen")
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index addb014..87610cf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -111,7 +111,6 @@
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder;
 import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel;
 import com.android.systemui.media.controls.util.MediaDataUtils;
-import com.android.systemui.media.controls.util.MediaFlags;
 import com.android.systemui.media.controls.util.MediaUiEventLogger;
 import com.android.systemui.media.controls.util.SmallHash;
 import com.android.systemui.media.dialog.MediaOutputDialogManager;
@@ -120,6 +119,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -209,7 +209,6 @@
     static final long TURBULENCE_NOISE_PLAY_DURATION = 7500L;
 
     private final SeekBarViewModel mSeekBarViewModel;
-    private final MediaFlags mMediaFlags;
     private final CommunalSceneInteractor mCommunalSceneInteractor;
     private SeekBarObserver mSeekBarObserver;
     protected final Executor mBackgroundExecutor;
@@ -323,8 +322,7 @@
             CommunalSceneInteractor communalSceneInteractor,
             NotificationLockscreenUserManager lockscreenUserManager,
             BroadcastDialogController broadcastDialogController,
-            GlobalSettings globalSettings,
-            MediaFlags mediaFlags
+            GlobalSettings globalSettings
     ) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
@@ -343,7 +341,6 @@
         mActivityIntentHelper = activityIntentHelper;
         mLockscreenUserManager = lockscreenUserManager;
         mBroadcastDialogController = broadcastDialogController;
-        mMediaFlags = mediaFlags;
         mCommunalSceneInteractor = communalSceneInteractor;
 
         mSeekBarViewModel.setLogSeek(() -> {
@@ -641,7 +638,7 @@
         // State refresh interferes with the translation animation, only run it if it's not running.
         if (!mMetadataAnimationHandler.isRunning()) {
             // Don't refresh in scene framework, because it will calculate with invalid layout sizes
-            if (!mMediaFlags.isSceneContainerEnabled()) {
+            if (!SceneContainerFlag.isEnabled()) {
                 mMediaViewController.refreshState();
             }
         }
@@ -909,7 +906,7 @@
         // Capture width & height from views in foreground for artwork scaling in background
         int width = mMediaViewHolder.getAlbumView().getMeasuredWidth();
         int height = mMediaViewHolder.getAlbumView().getMeasuredHeight();
-        if (mMediaFlags.isSceneContainerEnabled() && (width <= 0 || height <= 0)) {
+        if (SceneContainerFlag.isEnabled() && (width <= 0 || height <= 0)) {
             // TODO(b/312714128): ensure we have a valid size before setting background
             width = mMediaViewController.getWidthInSceneContainerPx();
             height = mMediaViewController.getHeightInSceneContainerPx();
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 88a28bf..a9d2a54 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -46,10 +46,10 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.view.MediaHost
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.dream.MediaDreamComplication
 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.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.CrossFadeHelper
 import com.android.systemui.statusbar.StatusBarState
@@ -119,7 +119,6 @@
     @Application private val coroutineScope: CoroutineScope,
     private val splitShadeStateController: SplitShadeStateController,
     private val logger: MediaViewLogger,
-    private val mediaFlags: MediaFlags,
 ) {
 
     /** Track the media player setting status on lock screen. */
@@ -1111,7 +1110,7 @@
 
     private fun updateHostAttachment() =
         traceSection("MediaHierarchyManager#updateHostAttachment") {
-            if (mediaFlags.isSceneContainerEnabled()) {
+            if (SceneContainerFlag.isEnabled) {
                 // No need to manage transition states - just update the desired location directly
                 logger.logMediaHostAttachment(desiredLocation)
                 mediaCarouselController.onDesiredLocationChanged(
@@ -1210,7 +1209,6 @@
             (onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
         val location =
             when {
-                mediaFlags.isSceneContainerEnabled() -> desiredLocation
                 dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
                 onCommunal -> LOCATION_COMMUNAL_HUB
                 (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 681bf39..e57de09 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -46,8 +46,8 @@
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
 import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.surfaceeffects.PaintDrawCallback
 import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect
@@ -82,7 +82,6 @@
     private val logger: MediaViewLogger,
     private val seekBarViewModel: SeekBarViewModel,
     @Main private val mainExecutor: DelayableExecutor,
-    private val mediaFlags: MediaFlags,
     private val globalSettings: GlobalSettings,
 ) {
 
@@ -125,7 +124,7 @@
         set(value) {
             if (field != value) {
                 field = value
-                if (!mediaFlags.isSceneContainerEnabled()) return
+                if (!SceneContainerFlag.isEnabled) return
                 locationChangeListener(value)
             }
         }
@@ -196,8 +195,8 @@
     private var isNextButtonAvailable = false
 
     /** View holders for controller */
-    lateinit var recommendationViewHolder: RecommendationViewHolder
-    lateinit var mediaViewHolder: MediaViewHolder
+    var recommendationViewHolder: RecommendationViewHolder? = null
+    var mediaViewHolder: MediaViewHolder? = null
 
     private lateinit var seekBarObserver: SeekBarObserver
     private lateinit var turbulenceNoiseController: TurbulenceNoiseController
@@ -212,7 +211,7 @@
     private val scrubbingChangeListener =
         object : SeekBarViewModel.ScrubbingChangeListener {
             override fun onScrubbingChanged(scrubbing: Boolean) {
-                if (!mediaFlags.isSceneContainerEnabled()) return
+                if (!SceneContainerFlag.isEnabled) return
                 if (isScrubbing == scrubbing) return
                 isScrubbing = scrubbing
                 updateDisplayForScrubbingChange()
@@ -222,7 +221,7 @@
     private val enabledChangeListener =
         object : SeekBarViewModel.EnabledChangeListener {
             override fun onEnabledChanged(enabled: Boolean) {
-                if (!mediaFlags.isSceneContainerEnabled()) return
+                if (!SceneContainerFlag.isEnabled) return
                 if (isSeekBarEnabled == enabled) return
                 isSeekBarEnabled = enabled
                 MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
@@ -238,7 +237,7 @@
      * @param listening True when player should be active. Otherwise, false.
      */
     fun setListening(listening: Boolean) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         seekBarViewModel.listening = listening
     }
 
@@ -272,7 +271,7 @@
                             )
                         )
                     }
-                    if (mediaFlags.isSceneContainerEnabled()) {
+                    if (SceneContainerFlag.isEnabled) {
                         if (
                             this@MediaViewController::recsConfigurationChangeListener.isInitialized
                         ) {
@@ -344,7 +343,7 @@
      * Notify this controller that the view has been removed and all listeners should be destroyed
      */
     fun onDestroy() {
-        if (mediaFlags.isSceneContainerEnabled()) {
+        if (SceneContainerFlag.isEnabled) {
             if (this::seekBarObserver.isInitialized) {
                 seekBarViewModel.progress.removeObserver(seekBarObserver)
             }
@@ -565,7 +564,7 @@
         state: MediaHostState?,
         isGutsAnimation: Boolean = false
     ): TransitionViewState? {
-        if (mediaFlags.isSceneContainerEnabled()) {
+        if (SceneContainerFlag.isEnabled) {
             return obtainSceneContainerViewState()
         }
 
@@ -667,7 +666,7 @@
         }
 
     fun attachPlayer(mediaViewHolder: MediaViewHolder) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         this.mediaViewHolder = mediaViewHolder
 
         // Setting up seek bar.
@@ -741,7 +740,7 @@
     }
 
     fun updateAnimatorDurationScale() {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         if (this::seekBarObserver.isInitialized) {
             seekBarObserver.animationEnabled =
                 globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f
@@ -752,16 +751,18 @@
     private fun updateDisplayForScrubbingChange() {
         mainExecutor.execute {
             val isTimeVisible = canShowScrubbingTime && isScrubbing
-            MediaControlViewBinder.setVisibleAndAlpha(
-                expandedLayout,
-                mediaViewHolder.scrubbingTotalTimeView.id,
-                isTimeVisible
-            )
-            MediaControlViewBinder.setVisibleAndAlpha(
-                expandedLayout,
-                mediaViewHolder.scrubbingElapsedTimeView.id,
-                isTimeVisible
-            )
+            mediaViewHolder!!.let {
+                MediaControlViewBinder.setVisibleAndAlpha(
+                    expandedLayout,
+                    it.scrubbingTotalTimeView.id,
+                    isTimeVisible
+                )
+                MediaControlViewBinder.setVisibleAndAlpha(
+                    expandedLayout,
+                    it.scrubbingElapsedTimeView.id,
+                    isTimeVisible
+                )
+            }
 
             MediaControlViewModel.SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach { id ->
                 val isButtonVisible: Boolean
@@ -780,14 +781,16 @@
                         notVisibleValue = ConstraintSet.GONE
                     }
                 }
-                MediaControlViewBinder.setSemanticButtonVisibleAndAlpha(
-                    mediaViewHolder.getAction(id),
-                    expandedLayout,
-                    collapsedLayout,
-                    isButtonVisible,
-                    notVisibleValue,
-                    showInCollapsed = true
-                )
+                mediaViewHolder!!.let {
+                    MediaControlViewBinder.setSemanticButtonVisibleAndAlpha(
+                        it.getAction(id),
+                        expandedLayout,
+                        collapsedLayout,
+                        isButtonVisible,
+                        notVisibleValue,
+                        showInCollapsed = true
+                    )
+                }
             }
 
             if (!metadataAnimationHandler.isRunning) {
@@ -797,7 +800,7 @@
     }
 
     fun attachRecommendations(recommendationViewHolder: RecommendationViewHolder) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         this.recommendationViewHolder = recommendationViewHolder
 
         attach(recommendationViewHolder.recommendations, TYPE.RECOMMENDATION)
@@ -806,46 +809,48 @@
     }
 
     fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         seekBarViewModel.logSeek = onSeek
         onBindSeekBar(seekBarViewModel)
     }
 
     fun setUpTurbulenceNoise() {
-        if (!mediaFlags.isSceneContainerEnabled()) return
-        if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
-            turbulenceNoiseAnimationConfig =
-                createTurbulenceNoiseConfig(
-                    mediaViewHolder.loadingEffectView,
-                    mediaViewHolder.turbulenceNoiseView,
-                    colorSchemeTransition
-                )
-        }
-        if (Flags.shaderlibLoadingEffectRefactor()) {
-            if (!this::loadingEffect.isInitialized) {
-                loadingEffect =
-                    LoadingEffect(
-                        TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
-                        turbulenceNoiseAnimationConfig,
-                        noiseDrawCallback,
-                        stateChangedCallback
+        if (!SceneContainerFlag.isEnabled) return
+        mediaViewHolder!!.let {
+            if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
+                turbulenceNoiseAnimationConfig =
+                    createTurbulenceNoiseConfig(
+                        it.loadingEffectView,
+                        it.turbulenceNoiseView,
+                        colorSchemeTransition
                     )
             }
-            colorSchemeTransition.loadingEffect = loadingEffect
-            loadingEffect.play()
-            mainExecutor.executeDelayed(
-                loadingEffect::finish,
-                MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
-            )
-        } else {
-            turbulenceNoiseController.play(
-                TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
-                turbulenceNoiseAnimationConfig
-            )
-            mainExecutor.executeDelayed(
-                turbulenceNoiseController::finish,
-                MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
-            )
+            if (Flags.shaderlibLoadingEffectRefactor()) {
+                if (!this::loadingEffect.isInitialized) {
+                    loadingEffect =
+                        LoadingEffect(
+                            TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+                            turbulenceNoiseAnimationConfig,
+                            noiseDrawCallback,
+                            stateChangedCallback
+                        )
+                }
+                colorSchemeTransition.loadingEffect = loadingEffect
+                loadingEffect.play()
+                mainExecutor.executeDelayed(
+                    loadingEffect::finish,
+                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+                )
+            } else {
+                turbulenceNoiseController.play(
+                    TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE,
+                    turbulenceNoiseAnimationConfig
+                )
+                mainExecutor.executeDelayed(
+                    turbulenceNoiseController::finish,
+                    MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION
+                )
+            }
         }
     }
 
@@ -1043,7 +1048,7 @@
      */
     private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? {
         val mediaHostState = mediaHostStatesManager.mediaHostStates[location] ?: return null
-        if (mediaFlags.isSceneContainerEnabled()) {
+        if (SceneContainerFlag.isEnabled) {
             return obtainSceneContainerViewState()
         }
 
@@ -1074,7 +1079,7 @@
     /** Clear all existing measurements and refresh the state to match the view. */
     fun refreshState() =
         traceSection("MediaViewController#refreshState") {
-            if (mediaFlags.isSceneContainerEnabled()) {
+            if (SceneContainerFlag.isEnabled) {
                 // We don't need to recreate measurements for scene container, since it's a known
                 // size. Just get the view state and update the layout controller
                 obtainSceneContainerViewState()?.let {
@@ -1163,13 +1168,13 @@
     }
 
     fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         isPrevButtonAvailable = isAvailable
         prevNotVisibleValue = notVisibleValue
     }
 
     fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
-        if (!mediaFlags.isSceneContainerEnabled()) return
+        if (!SceneContainerFlag.isEnabled) return
         isNextButtonAvailable = isAvailable
         nextNotVisibleValue = notVisibleValue
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index 91050c8..09a6181 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -44,6 +44,7 @@
     lateinit var hostView: UniqueObjectHostView
     var location: Int = -1
         private set
+
     private var visibleChangedListeners: ArraySet<(Boolean) -> Unit> = ArraySet()
 
     private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0)
@@ -287,6 +288,15 @@
                 changedListener?.invoke()
             }
 
+        override var disablePagination: Boolean = false
+            set(value) {
+                if (field == value) {
+                    return
+                }
+                field = value
+                changedListener?.invoke()
+            }
+
         private var lastDisappearHash = disappearParameters.hashCode()
 
         /** A listener for all changes. This won't be copied over when invoking [copy] */
@@ -303,6 +313,7 @@
             mediaHostState.visible = visible
             mediaHostState.disappearParameters = disappearParameters.deepCopy()
             mediaHostState.falsingProtectionNeeded = falsingProtectionNeeded
+            mediaHostState.disablePagination = disablePagination
             return mediaHostState
         }
 
@@ -331,6 +342,9 @@
             if (!disappearParameters.equals(other.disappearParameters)) {
                 return false
             }
+            if (disablePagination != other.disablePagination) {
+                return false
+            }
             return true
         }
 
@@ -342,6 +356,7 @@
             result = 31 * result + showsOnlyActiveMedia.hashCode()
             result = 31 * result + if (visible) 1 else 2
             result = 31 * result + disappearParameters.hashCode()
+            result = 31 * result + disablePagination.hashCode()
             return result
         }
     }
@@ -400,6 +415,12 @@
      */
     var disappearParameters: DisappearParameters
 
+    /**
+     * Whether pagination should be disabled for this host, meaning that when there are multiple
+     * media sessions, only the first one will appear.
+     */
+    var disablePagination: Boolean
+
     /** Get a copy of this view state, deepcopying all appropriate members */
     fun copy(): MediaHostState
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 21c3111..d4af1b5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import javax.inject.Inject
 
 @SysUISingleton
@@ -30,9 +29,8 @@
      * Check whether media control actions should be based on PlaybackState instead of notification
      */
     fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean {
-        val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, user)
         // Allow global override with flag
-        return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
+        return StatusBarManager.useMediaSessionActionsForApp(packageName, user)
     }
 
     /**
@@ -49,7 +47,4 @@
 
     /** Check whether we allow remote media to generate resume controls */
     fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME)
-
-    /** Check whether to use scene framework */
-    fun isSceneContainerEnabled() = SceneContainerFlag.isEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b48b409..875e505 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -94,6 +94,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
 
 import dagger.assisted.Assisted;
 import dagger.assisted.AssistedFactory;
@@ -173,6 +174,7 @@
     private float mActiveRadius;
     private FeatureFlags mFeatureFlags;
     private UserTracker mUserTracker;
+    private VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor;
 
     public enum BroadcastNotifyDialog {
         ACTION_FIRST_LAUNCH,
@@ -195,6 +197,7 @@
             PowerExemptionManager powerExemptionManager,
             KeyguardManager keyGuardManager,
             FeatureFlags featureFlags,
+            VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor,
             UserTracker userTracker) {
         mContext = context;
         mPackageName = packageName;
@@ -209,6 +212,7 @@
         mFeatureFlags = featureFlags;
         mUserTracker = userTracker;
         mToken = token;
+        mVolumePanelGlobalStateInteractor = volumePanelGlobalStateInteractor;
         InfoMediaManager imm =
                 InfoMediaManager.createInstance(mContext, packageName, userHandle, lbm, token);
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
@@ -436,7 +440,7 @@
             launchIntent.putExtra(EXTRA_ROUTE_ID, routeId);
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             mCallback.dismissDialog();
-            mActivityStarter.startActivity(launchIntent, true, controller);
+            startActivity(launchIntent, controller);
         }
     }
 
@@ -447,7 +451,7 @@
         if (launchIntent != null) {
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             mCallback.dismissDialog();
-            mActivityStarter.startActivity(launchIntent, true, controller);
+            startActivity(launchIntent, controller);
         }
     }
 
@@ -951,10 +955,10 @@
             deepLinkIntent.putExtra(
                     Settings.EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY,
                     PAGE_CONNECTED_DEVICES_KEY);
-            mActivityStarter.startActivity(deepLinkIntent, true, controller);
+            startActivity(deepLinkIntent, controller);
             return;
         }
-        mActivityStarter.startActivity(launchIntent, true, controller);
+        startActivity(launchIntent, controller);
     }
 
     void launchLeBroadcastNotifyDialog(View mediaOutputDialog, BroadcastSender broadcastSender,
@@ -998,6 +1002,7 @@
                         mPowerExemptionManager,
                         mKeyGuardManager,
                         mFeatureFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
         MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
                 broadcastSender, controller);
@@ -1244,6 +1249,13 @@
         return !device.isVolumeFixed();
     }
 
+    private void startActivity(Intent intent, ActivityTransitionAnimator.Controller controller) {
+        // Media Output dialog can be shown from the volume panel. This makes sure the panel is
+        // closed when navigating to another activity, so it doesn't stays on top of it
+        mVolumePanelGlobalStateInteractor.setVisible(false);
+        mActivityStarter.startActivity(intent, true, controller);
+    }
+
     @Override
     public void onDevicesUpdated(List<NearbyDevice> nearbyDevices) throws RemoteException {
         mNearbyDeviceInfoMap.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 4f062af..92db804 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.media.taptotransfer.receiver
 
 import android.animation.TimeInterpolator
-import android.annotation.SuppressLint
 import android.animation.ValueAnimator
+import android.annotation.SuppressLint
 import android.app.StatusBarManager
 import android.content.Context
 import android.graphics.Rect
@@ -29,15 +29,15 @@
 import android.os.PowerManager
 import android.view.Gravity
 import android.view.View
+import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
-import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
-import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
-import com.android.internal.widget.CachingIconView
-import com.android.systemui.res.R
 import com.android.app.animation.Interpolators
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.InstanceId
+import com.android.internal.widget.CachingIconView
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.ui.binder.TintedIconViewBinder
 import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +46,7 @@
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttIcon
 import com.android.systemui.media.taptotransfer.common.MediaTttUtils
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
@@ -71,7 +72,7 @@
         private val commandQueue: CommandQueue,
         context: Context,
         logger: MediaTttReceiverLogger,
-        windowManager: WindowManager,
+        viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
         @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
@@ -88,7 +89,7 @@
 ) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
         context,
         logger,
-        windowManager,
+        viewCaptureAwareWindowManager,
         mainExecutor,
         accessibilityManager,
         configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index d6affd2..228b576 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -32,6 +32,10 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.accessibility.AccessibilityEvent
+import android.widget.ImageView
+import androidx.annotation.ColorRes
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleRegistry
@@ -52,6 +56,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.AsyncActivityLauncher
+import java.lang.IllegalArgumentException
 import javax.inject.Inject
 
 class MediaProjectionAppSelectorActivity(
@@ -116,6 +121,7 @@
 
         super.onCreate(savedInstanceState)
         controller.init()
+        setIcon()
         // we override AppList's AccessibilityDelegate set in ResolverActivity.onCreate because in
         // our case this delegate must extend RecyclerViewAccessibilityDelegate, otherwise
         // RecyclerView scrolling is broken
@@ -298,6 +304,29 @@
     override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
         recentsViewController.createView(parent)
 
+    /** Set up intent for the [ChooserActivity] */
+    private fun Intent.configureChooserIntent(
+        resources: Resources,
+        hostUserHandle: UserHandle,
+        personalProfileUserHandle: UserHandle,
+    ) {
+        // Specify the query intent to show icons for all apps on the chooser screen
+        val queryIntent = Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
+        putExtra(Intent.EXTRA_INTENT, queryIntent)
+
+        // Update the title of the chooser
+        putExtra(Intent.EXTRA_TITLE, resources.getString(titleResId))
+
+        // Select host app's profile tab by default
+        val selectedProfile =
+            if (hostUserHandle == personalProfileUserHandle) {
+                PROFILE_PERSONAL
+            } else {
+                PROFILE_WORK
+            }
+        putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
+    }
+
     private val hostUserHandle: UserHandle
         get() {
             val extras =
@@ -321,6 +350,54 @@
             return intent.getIntExtra(EXTRA_HOST_APP_UID, /* defaultValue= */ -1)
         }
 
+    /**
+     * The type of screen sharing being performed. Used to show the right text and icon in the
+     * activity.
+     */
+    private val screenShareType: ScreenShareType?
+        get() {
+            if (!intent.hasExtra(EXTRA_SCREEN_SHARE_TYPE)) {
+                return null
+            } else {
+                val type = intent.getStringExtra(EXTRA_SCREEN_SHARE_TYPE) ?: return null
+                return try {
+                    enumValueOf<ScreenShareType>(type)
+                } catch (e: IllegalArgumentException) {
+                    null
+                }
+            }
+        }
+
+    @get:StringRes
+    private val titleResId: Int
+        get() =
+            when (screenShareType) {
+                ScreenShareType.ShareToApp ->
+                    R.string.media_projection_entry_share_app_selector_title
+                ScreenShareType.SystemCast ->
+                    R.string.media_projection_entry_cast_app_selector_title
+                ScreenShareType.ScreenRecord -> R.string.screenrecord_app_selector_title
+                null -> R.string.screen_share_generic_app_selector_title
+            }
+
+    @get:DrawableRes
+    private val iconResId: Int
+        get() =
+            when (screenShareType) {
+                ScreenShareType.ShareToApp -> R.drawable.ic_present_to_all
+                ScreenShareType.SystemCast -> R.drawable.ic_cast_connected
+                ScreenShareType.ScreenRecord -> R.drawable.ic_screenrecord
+                null -> R.drawable.ic_present_to_all
+            }
+
+    @get:ColorRes
+    private val iconTintResId: Int?
+        get() =
+            when (screenShareType) {
+                ScreenShareType.ScreenRecord -> R.color.screenrecord_icon_color
+                else -> null
+            }
+
     companion object {
         const val TAG = "MediaProjectionAppSelectorActivity"
 
@@ -343,30 +420,18 @@
         const val EXTRA_HOST_APP_UID = "launched_from_host_uid"
         const val KEY_CAPTURE_TARGET = "capture_region"
 
-        /** Set up intent for the [ChooserActivity] */
-        private fun Intent.configureChooserIntent(
-            resources: Resources,
-            hostUserHandle: UserHandle,
-            personalProfileUserHandle: UserHandle
-        ) {
-            // Specify the query intent to show icons for all apps on the chooser screen
-            val queryIntent =
-                Intent(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
-            putExtra(Intent.EXTRA_INTENT, queryIntent)
+        /**
+         * The type of screen sharing being performed.
+         *
+         * The value set for this extra should match the name of a [ScreenShareType].
+         */
+        const val EXTRA_SCREEN_SHARE_TYPE = "screen_share_type"
+    }
 
-            // Update the title of the chooser
-            val title = resources.getString(R.string.screen_share_permission_app_selector_title)
-            putExtra(Intent.EXTRA_TITLE, title)
-
-            // Select host app's profile tab by default
-            val selectedProfile =
-                if (hostUserHandle == personalProfileUserHandle) {
-                    PROFILE_PERSONAL
-                } else {
-                    PROFILE_WORK
-                }
-            putExtra(EXTRA_SELECTED_PROFILE, selectedProfile)
-        }
+    private fun setIcon() {
+        val iconView = findViewById<ImageView>(R.id.media_projection_app_selector_icon) ?: return
+        iconView.setImageResource(iconResId)
+        iconTintResId?.let { iconView.setColorFilter(this.resources.getColor(it, this.theme)) }
     }
 
     private fun setAppListAccessibilityDelegate() {
@@ -406,4 +471,14 @@
             return delegate.onRequestSendAccessibilityEvent(host, child, event)
         }
     }
+
+    /** Enum describing what type of app screen sharing is being performed. */
+    enum class ScreenShareType {
+        /** The selected app will be cast to another device. */
+        SystemCast,
+        /** The selected app will be shared to another app on the device. */
+        ShareToApp,
+        /** The selected app will be recorded. */
+        ScreenRecord,
+    }
 }
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 9b1ca1e..6440205 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -122,8 +122,10 @@
         @Provides
         @MediaProjectionAppSelector
         @MediaProjectionAppSelectorScope
-        fun bindConfigurationController(context: Context): ConfigurationController =
-            ConfigurationControllerImpl(context)
+        fun bindConfigurationController(
+            context: Context,
+            configurationControlleFactory: ConfigurationControllerImpl.Factory
+        ): ConfigurationController = configurationControlleFactory.create(context)
 
         @Provides fun bindIconFactory(context: Context): IconFactory = IconFactory.obtain(context)
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
index 2dbe2aa..bf2aa7e 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -20,7 +20,7 @@
 import android.annotation.UserIdInt
 import android.app.ActivityManager.RecentTaskInfo
 import android.content.ComponentName
-import com.android.wm.shell.util.SplitBounds
+import com.android.wm.shell.shared.split.SplitBounds
 
 data class RecentTask(
     val taskId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 01b1be9..82e58cc 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.kotlin.getOrNull
 import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.GroupedRecentTaskInfo
 import java.util.Optional
 import java.util.concurrent.Executor
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 46aa064..bb4d894 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -27,7 +27,6 @@
 import android.window.RemoteTransition
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.Flags.pssAppSelectorAbruptExitFix
 import com.android.systemui.Flags.pssAppSelectorRecentsSplitScreen
 import com.android.systemui.display.naturalBounds
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
@@ -37,10 +36,10 @@
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
 import com.android.systemui.res.R
 import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
-import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+import com.android.wm.shell.shared.split.SplitBounds
+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.SplitScreen
-import com.android.wm.shell.util.SplitBounds
 import java.util.Optional
 import javax.inject.Inject
 
@@ -160,7 +159,7 @@
 
 
     private fun createAnimation(task: RecentTask, view: View): ActivityOptions =
-        if (pssAppSelectorAbruptExitFix() && task.isForegroundTask) {
+        if (task.isForegroundTask) {
             // When the selected task is in the foreground, the scale up animation doesn't work.
             // We fallback to the default close animation.
             ActivityOptions.makeCustomTaskAnimation(
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 3c83db3..18c6f53 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -73,8 +73,7 @@
 
 import dagger.Lazy;
 
-public class MediaProjectionPermissionActivity extends Activity
-        implements DialogInterface.OnClickListener {
+public class MediaProjectionPermissionActivity extends Activity {
     private static final String TAG = "MediaProjectionPermissionActivity";
     private static final float MAX_APP_NAME_SIZE_PX = 500f;
     private static final String ELLIPSIS = "\u2026";
@@ -269,7 +268,8 @@
         Consumer<BaseMediaProjectionPermissionDialogDelegate<AlertDialog>> onStartRecordingClicked =
                 dialog -> {
                     ScreenShareOption selectedOption = dialog.getSelectedScreenShareOption();
-                    grantMediaProjectionPermission(selectedOption.getMode());
+                    grantMediaProjectionPermission(
+                            selectedOption.getMode(), hasCastingCapabilities);
                 };
         Runnable onCancelClicked = () -> finish(RECORD_CANCEL, /* projection= */ null);
         if (hasCastingCapabilities) {
@@ -305,19 +305,6 @@
         }
     }
 
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        if (which == AlertDialog.BUTTON_POSITIVE) {
-            grantMediaProjectionPermission(ENTIRE_SCREEN);
-        } else {
-            if (mDialog != null) {
-                mDialog.dismiss();
-            }
-            setResult(RESULT_CANCELED);
-            finish(RECORD_CANCEL, /* projection= */ null);
-        }
-    }
-
     private void setUpDialog(AlertDialog dialog) {
         SystemUIDialog.registerDismissListener(dialog);
         SystemUIDialog.applyFlags(dialog);
@@ -345,25 +332,21 @@
         return false;
     }
 
-    private void grantMediaProjectionPermission(int screenShareMode) {
+    private void grantMediaProjectionPermission(
+            int screenShareMode, boolean hasCastingCapabilities) {
         try {
+            IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
+                    mUid, mPackageName, mReviewGrantedConsentRequired);
             if (screenShareMode == ENTIRE_SCREEN) {
-                final IMediaProjection projection =
-                        MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName,
-                                mReviewGrantedConsentRequired);
                 final Intent intent = new Intent();
-                intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
-                        projection.asBinder());
+                setCommonIntentExtras(intent, hasCastingCapabilities, projection);
                 setResult(RESULT_OK, intent);
                 finish(RECORD_CONTENT_DISPLAY, projection);
             }
             if (screenShareMode == SINGLE_APP) {
-                IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(
-                        mUid, mPackageName, mReviewGrantedConsentRequired);
                 final Intent intent = new Intent(this,
                         MediaProjectionAppSelectorActivity.class);
-                intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
-                        projection.asBinder());
+                setCommonIntentExtras(intent, hasCastingCapabilities, projection);
                 intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
                         getHostUserHandle());
                 intent.putExtra(
@@ -391,6 +374,19 @@
         }
     }
 
+    private void setCommonIntentExtras(
+            Intent intent,
+            boolean hasCastingCapabilities,
+            IMediaProjection projection) throws RemoteException {
+        intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION,
+                projection.asBinder());
+        intent.putExtra(
+                MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
+                hasCastingCapabilities
+                        ? MediaProjectionAppSelectorActivity.ScreenShareType.SystemCast.name()
+                        : MediaProjectionAppSelectorActivity.ScreenShareType.ShareToApp.name());
+    }
+
     private UserHandle getHostUserHandle() {
         return UserHandle.getUserHandleForUid(getLaunchedFromUid());
     }
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 42f66cc..7d2a1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -18,7 +18,6 @@
 
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.Flags.glanceableHubBackGesture
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -107,10 +106,7 @@
                     {
                         it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion
                     },
-                SYSUI_STATE_COMMUNAL_HUB_SHOWING to
-                    {
-                        glanceableHubBackGesture() && it.scene == Scenes.Communal
-                    }
+                SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.scene == Scenes.Communal }
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 13a786a..173a964 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -19,25 +19,31 @@
 import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
 import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON;
 import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_OPAQUE;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 import static com.android.systemui.shared.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -59,10 +65,11 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.WorkerThread;
 
-import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dagger.SysUISingleton;
@@ -73,6 +80,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shared.Flags;
 import com.android.systemui.shared.rotation.RotationPolicyUtil;
 import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -106,6 +114,7 @@
         AccessibilityManager.AccessibilityServicesStateChangeListener,
         AccessibilityButtonModeObserver.ModeChangedListener,
         AccessibilityButtonTargetsObserver.TargetsChangedListener,
+        AccessibilityGestureTargetsObserver.TargetsChangedListener,
         OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
         Dumpable, CommandQueue.Callbacks, ConfigurationController.ConfigurationListener {
     private static final String TAG = NavBarHelper.class.getSimpleName();
@@ -121,6 +130,7 @@
     private final SystemActions mSystemActions;
     private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
     private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
+    private final AccessibilityGestureTargetsObserver mAccessibilityGestureTargetsObserver;
     private final List<NavbarTaskbarStateUpdater> mStateListeners = new ArrayList<>();
     private final Context mContext;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -187,6 +197,7 @@
     public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
             AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
+            AccessibilityGestureTargetsObserver accessibilityGestureTargetsObserver,
             SystemActions systemActions,
             OverviewProxyService overviewProxyService,
             Lazy<AssistManager> assistManagerLazy,
@@ -219,6 +230,7 @@
         mSystemActions = systemActions;
         mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
         mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
+        mAccessibilityGestureTargetsObserver = accessibilityGestureTargetsObserver;
         mWm = wm;
         mDefaultDisplayId = displayTracker.getDefaultDisplayId();
         mEdgeBackGestureHandler = edgeBackGestureHandlerFactory.create(context);
@@ -248,6 +260,7 @@
         mAccessibilityManager.addAccessibilityServicesStateChangeListener(this);
         mAccessibilityButtonModeObserver.addListener(this);
         mAccessibilityButtonTargetsObserver.addListener(this);
+        mAccessibilityGestureTargetsObserver.addListener(this);
 
         // Setup assistant listener
         mContentResolver.registerContentObserver(
@@ -290,6 +303,7 @@
         mAccessibilityManager.removeAccessibilityServicesStateChangeListener(this);
         mAccessibilityButtonModeObserver.removeListener(this);
         mAccessibilityButtonTargetsObserver.removeListener(this);
+        mAccessibilityGestureTargetsObserver.removeListener(this);
 
         // Clean up assistant listeners
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
@@ -379,43 +393,50 @@
     }
 
     @Override
+    public void onAccessibilityGestureTargetsChanged(String targets) {
+        updateA11yState();
+    }
+
+    @Override
     public void onConfigChanged(Configuration newConfig) {
         mEdgeBackGestureHandler.onConfigurationChanged(newConfig);
     }
 
+    private int getNumOfA11yShortcutTargetsForNavSystem() {
+        final int buttonMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
+        final int shortcutType;
+        if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
+            shortcutType = buttonMode
+                    != ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU ? SOFTWARE : DEFAULT;
+            // If accessibility button is floating menu mode, there are no clickable targets.
+        } else {
+            if (mNavBarMode == NAV_BAR_MODE_GESTURAL) {
+                shortcutType = GESTURE;
+            } else {
+                shortcutType = buttonMode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR
+                        ? SOFTWARE : DEFAULT;
+            }
+        }
+        return mAccessibilityManager.getAccessibilityShortcutTargets(shortcutType).size();
+    }
+
     /**
      * Updates the current accessibility button state. The accessibility button state is only
      * used for {@link Secure#ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR} and
      * {@link Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}, otherwise it is reset to 0.
      */
-    private void updateA11yState() {
+    @VisibleForTesting
+    void updateA11yState() {
         final long prevState = mA11yButtonState;
         final boolean clickable;
         final boolean longClickable;
-        if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
-                == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
-            // If accessibility button is floating menu mode, click and long click state should be
-            // disabled.
-            clickable = false;
-            longClickable = false;
-            mA11yButtonState = 0;
-        } else {
-            // AccessibilityManagerService resolves services for the current user since the local
-            // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS
-            // permission
-            final List<String> a11yButtonTargets =
-                    mAccessibilityManager.getAccessibilityShortcutTargets(
-                            ShortcutConstants.UserShortcutType.SOFTWARE);
-            final int requestingServices = a11yButtonTargets.size();
-
-            clickable = requestingServices >= 1;
-
-            // `longClickable` is used to determine whether to pop up the accessibility chooser
-            // dialog or not, and it’s also only for multiple services.
-            longClickable = requestingServices >= 2;
-            mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
-                    | (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
-        }
+        int clickableServices = getNumOfA11yShortcutTargetsForNavSystem();
+        clickable = clickableServices >= 1;
+        // `longClickable` is used to determine whether to pop up the accessibility chooser
+        // dialog or not, and it’s also only for multiple services.
+        longClickable = clickableServices >= 2;
+        mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+                | (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
 
         // Update the system actions if the state has changed
         if (prevState != mA11yButtonState) {
@@ -481,9 +502,11 @@
                 Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
                 mUserTracker.getUserId()) != 0;
 
+        boolean supportsSwipeGesture = QuickStepContract.isGesturalMode(mNavBarMode)
+                || (QuickStepContract.isLegacyMode(mNavBarMode) && Flags.threeButtonCornerSwipe());
         mAssistantAvailable = assistantAvailableForUser
                 && mAssistantTouchGestureEnabled
-                && QuickStepContract.isGesturalMode(mNavBarMode);
+                && supportsSwipeGesture;
         dispatchAssistantEventUpdate(mAssistantAvailable, mLongPressHomeEnabled);
     }
 
@@ -516,7 +539,7 @@
      * @return Whether the IME is shown on top of the screen given the {@code vis} flag of
      * {@link InputMethodService} and the keyguard states.
      */
-    public boolean isImeShown(int vis) {
+    public boolean isImeShown(@ImeWindowVisibility int vis) {
         View shadeWindowView =  mNotificationShadeWindowController.getWindowRootView();
         boolean isKeyguardShowing = mKeyguardStateController.isShowing();
         boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index 8e46fe4..5e8c2c9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.navigationbar;
 
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
-
 import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG;
 import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
 import static com.android.wm.shell.Flags.enableTaskbarNavbarUnification;
@@ -32,8 +28,6 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.Trace;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -59,7 +53,6 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.settings.DisplayTracker;
 import com.android.systemui.shared.statusbar.phone.BarTransitions.TransitionMode;
-import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -192,7 +185,6 @@
         }
         final int oldMode = mNavMode;
         mNavMode = mode;
-        updateAccessibilityButtonModeIfNeeded();
 
         mExecutor.execute(() -> {
             // create/destroy nav bar based on nav mode only in unfolded state
@@ -209,34 +201,6 @@
         });
     }
 
-    private void updateAccessibilityButtonModeIfNeeded() {
-        final int mode = mSecureSettings.getIntForUser(
-                Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-
-        // ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU is compatible under gestural or non-gestural
-        // mode, so we don't need to update it.
-        if (mode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
-            return;
-        }
-
-        // ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR is incompatible under gestural mode. Need to
-        // force update to ACCESSIBILITY_BUTTON_MODE_GESTURE.
-        if (QuickStepContract.isGesturalMode(mNavMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR) {
-            mSecureSettings.putIntForUser(
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_GESTURE,
-                    UserHandle.USER_CURRENT);
-            // ACCESSIBILITY_BUTTON_MODE_GESTURE is incompatible under non gestural mode. Need to
-            // force update to ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR.
-        } else if (!QuickStepContract.isGesturalMode(mNavMode)
-                && mode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
-            mSecureSettings.putIntForUser(
-                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
-                    ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
-        }
-    }
-
     private boolean shouldCreateNavBarAndTaskBar(int displayId) {
         if (mHasNavBar.indexOfKey(displayId) > -1) {
             return mHasNavBar.get(displayId);
@@ -353,8 +317,6 @@
     @Override
     public void createNavigationBars(final boolean includeDefaultDisplay,
             RegisterStatusBarResult result) {
-        updateAccessibilityButtonModeIfNeeded();
-
         // Don't need to create nav bar on the default display if we initialize TaskBar.
         final boolean shouldCreateDefaultNavbar = includeDefaultDisplay
                 && !initializeTaskbarIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index e2ba761..a8b979e0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.navigationbar;
 
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
@@ -28,6 +33,7 @@
 import com.android.systemui.navigationbar.views.NavigationBarView;
 import com.android.systemui.res.R;
 
+import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
 
@@ -73,4 +79,15 @@
     static WindowManager provideWindowManager(@DisplayId Context context) {
         return context.getSystemService(WindowManager.class);
     }
+
+    /** A ViewCaptureAwareWindowManager specific to the display's context. */
+    @Provides
+    @NavigationBarScope
+    @DisplayId
+    static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
+            @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
+        return new ViewCaptureAwareWindowManager(windowManager,
+                /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
+                /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 15b1e4d..e44069f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -42,6 +42,8 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
@@ -424,8 +426,8 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
         boolean imeShown = mNavBarHelper.isImeShown(vis);
         if (!imeShown) {
             // Count imperceptible changes as visible so we transition taskbar out quickly.
@@ -561,10 +563,6 @@
     }
 
     @Override
-    public void onRecentsAnimationStateChanged(boolean running) {
-    }
-
-    @Override
     public void onNavigationModeChanged(int mode) {
         mNavigationMode = mode;
         mEdgeBackGestureHandler.onNavigationModeChanged(mode);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 0fe4d36..89d76f0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -29,6 +29,7 @@
 import static java.util.stream.Collectors.joining;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -73,11 +74,12 @@
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.contextualeducation.GestureType;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.plugins.PluginListener;
@@ -102,6 +104,8 @@
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.pip.Pip;
 
+import kotlinx.coroutines.Job;
+
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.Date;
@@ -109,6 +113,7 @@
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
@@ -158,7 +163,7 @@
     private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
         @Override
         public void onTaskStackChanged() {
-            updateRunningActivityGesturesBlocked();
+            updateTopActivity();
         }
         @Override
         public void onTaskCreated(int taskId, ComponentName componentName) {
@@ -222,6 +227,8 @@
     private final Provider<LightBarController> mLightBarControllerProvider;
 
     private final GestureInteractor mGestureInteractor;
+    private final ArraySet<ComponentName> mBlockedActivities = new ArraySet<>();
+    private Job mBlockedActivitiesJob = null;
 
     private final JavaAdapter mJavaAdapter;
 
@@ -450,9 +457,6 @@
         mJavaAdapter = javaAdapter;
         mLastReportedConfig.setTo(mContext.getResources().getConfiguration());
 
-        mJavaAdapter.alwaysCollectFlow(mGestureInteractor.getGestureBlockedActivities(),
-                componentNames -> updateRunningActivityGesturesBlocked());
-
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
                 context.getString(com.android.internal.R.string.config_recentsComponentName));
         if (recentsComponentName != null) {
@@ -472,9 +476,14 @@
                 } else {
                     String[] gestureBlockingActivities = resources.getStringArray(resId);
                     for (String gestureBlockingActivity : gestureBlockingActivities) {
-                        mGestureInteractor.addGestureBlockedActivity(
-                                ComponentName.unflattenFromString(gestureBlockingActivity),
-                                GestureInteractor.Scope.Local);
+                        final ComponentName component =
+                                ComponentName.unflattenFromString(gestureBlockingActivity);
+
+                        if (component != null) {
+                            mGestureInteractor.addGestureBlockedMatcher(
+                                    new TaskMatcher.TopActivityComponent(component),
+                                    GestureInteractor.Scope.Local);
+                        }
                     }
                 }
             } catch (NameNotFoundException e) {
@@ -568,12 +577,11 @@
         }
     }
 
-    private void updateRunningActivityGesturesBlocked() {
+    private void updateTopActivity() {
         if (edgebackGestureHandlerGetRunningTasksBackground()) {
-            mBackgroundExecutor.execute(() -> mGestureBlockingActivityRunning.set(
-                    isGestureBlockingActivityRunning()));
+            mBackgroundExecutor.execute(() -> updateTopActivityPackageName());
         } else {
-            mGestureBlockingActivityRunning.set(isGestureBlockingActivityRunning());
+            updateTopActivityPackageName();
         }
     }
 
@@ -678,6 +686,11 @@
                     Log.e(TAG, "Failed to unregister window manager callbacks", e);
                 }
 
+                if (mBlockedActivitiesJob != null) {
+                    mBlockedActivitiesJob.cancel(new CancellationException());
+                    mBlockedActivitiesJob = null;
+                }
+                mBlockedActivities.clear();
             } else {
                 mBackgroundExecutor.execute(mGestureNavigationSettingsObserver::register);
                 updateDisplaySize();
@@ -710,6 +723,12 @@
                 resetEdgeBackPlugin();
                 mPluginManager.addPluginListener(
                         this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+
+                // Begin listening to changes in blocked activities list
+                mBlockedActivitiesJob = mJavaAdapter.alwaysCollectFlow(
+                        mGestureInteractor.getTopActivityBlocked(),
+                        blocked -> mGestureBlockingActivityRunning.set(blocked));
+
             }
             // Update the ML model resources.
             updateMLModelState();
@@ -1058,8 +1077,6 @@
                 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
                 mEdgeBackPlugin.onMotionEvent(ev);
                 dispatchToBackAnimation(ev);
-                mOverviewProxyService.updateContextualEduStats(mIsTrackpadThreeFingerSwipe,
-                        GestureType.BACK);
             }
             if (mLogGesture || mIsTrackpadThreeFingerSwipe) {
                 mDownPoint.set(ev.getX(), ev.getY());
@@ -1136,6 +1153,8 @@
                         if (mAllowGesture) {
                             if (mBackAnimation != null) {
                                 mBackAnimation.onThresholdCrossed();
+                                mOverviewProxyService.updateContextualEduStats(
+                                        mIsTrackpadThreeFingerSwipe, GestureType.BACK);
                             } else {
                                 pilferPointers();
                             }
@@ -1302,7 +1321,7 @@
         }
     }
 
-    private boolean isGestureBlockingActivityRunning() {
+    private void updateTopActivityPackageName() {
         ActivityManager.RunningTaskInfo runningTask =
                 ActivityManagerWrapper.getInstance().getRunningTask();
         ComponentName topActivity = runningTask == null ? null : runningTask.topActivity;
@@ -1311,25 +1330,22 @@
         } else {
             mPackageName = "_UNKNOWN";
         }
-
-        return topActivity != null && mGestureInteractor.areGesturesBlocked(topActivity);
     }
 
-    public void setBackAnimation(BackAnimation backAnimation) {
+    public void setBackAnimation(@Nullable BackAnimation backAnimation) {
         mBackAnimation = backAnimation;
-        mBackAnimation.setPilferPointerCallback(() -> {
-            pilferPointers();
-        });
-        mBackAnimation.setTopUiRequestCallback(
-                (requestTopUi, tag) -> mUiThreadContext.getExecutor().execute(() ->
-                        mNotificationShadeWindowController.setRequestTopUi(requestTopUi, tag)));
-        updateBackAnimationThresholds();
-        if (mLightBarControllerProvider.get() != null) {
-            mBackAnimation.setStatusBarCustomizer((appearance) -> {
-                mUiThreadContext.getExecutor().execute(() ->
-                        mLightBarControllerProvider.get()
-                                .customizeStatusBarAppearance(appearance));
-            });
+        if (backAnimation != null) {
+            backAnimation.setPilferPointerCallback(this::pilferPointers);
+            backAnimation.setTopUiRequestCallback(
+                    (requestTopUi, tag) -> mUiThreadContext.getExecutor().execute(() ->
+                            mNotificationShadeWindowController.setRequestTopUi(requestTopUi, tag)));
+            updateBackAnimationThresholds();
+            if (mLightBarControllerProvider.get() != null) {
+                mBackAnimation.setStatusBarCustomizer((appearance) ->
+                        mUiThreadContext.getExecutor().execute(() ->
+                            mLightBarControllerProvider.get()
+                                    .customizeStatusBarAppearance(appearance)));
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
index 8f35343..c1f238a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/data/respository/GestureRepository.kt
@@ -16,10 +16,9 @@
 
 package com.android.systemui.navigationbar.gestural.data.respository
 
-import android.content.ComponentName
-import android.util.ArraySet
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.navigationbar.gestural.domain.TaskMatcher
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -28,36 +27,43 @@
 
 /** A repository for storing gesture related information */
 interface GestureRepository {
-    /** A {@link StateFlow} tracking activities currently blocked from gestures. */
-    val gestureBlockedActivities: StateFlow<Set<ComponentName>>
+    /** A {@link StateFlow} tracking matchers that can block gestures. */
+    val gestureBlockedMatchers: StateFlow<Set<TaskMatcher>>
 
-    /** Adds an activity to be blocked from gestures. */
-    suspend fun addGestureBlockedActivity(activity: ComponentName)
+    /** Adds a matcher to determine whether a gesture should be blocked. */
+    suspend fun addGestureBlockedMatcher(matcher: TaskMatcher)
 
-    /** Removes an activity from being blocked from gestures. */
-    suspend fun removeGestureBlockedActivity(activity: ComponentName)
+    /** Removes a matcher from blocking from gestures. */
+    suspend fun removeGestureBlockedMatcher(matcher: TaskMatcher)
 }
 
 @SysUISingleton
 class GestureRepositoryImpl
 @Inject
 constructor(@Main private val mainDispatcher: CoroutineDispatcher) : GestureRepository {
-    private val _gestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(ArraySet())
+    private val _gestureBlockedMatchers = MutableStateFlow<Set<TaskMatcher>>(emptySet())
 
-    override val gestureBlockedActivities: StateFlow<Set<ComponentName>>
-        get() = _gestureBlockedActivities
+    override val gestureBlockedMatchers: StateFlow<Set<TaskMatcher>>
+        get() = _gestureBlockedMatchers
 
-    override suspend fun addGestureBlockedActivity(activity: ComponentName) =
+    override suspend fun addGestureBlockedMatcher(matcher: TaskMatcher) =
         withContext(mainDispatcher) {
-            _gestureBlockedActivities.emit(
-                _gestureBlockedActivities.value.toMutableSet().apply { add(activity) }
-            )
+            val existingMatchers = _gestureBlockedMatchers.value
+            if (existingMatchers.contains(matcher)) {
+                return@withContext
+            }
+
+            _gestureBlockedMatchers.value = existingMatchers.toMutableSet().apply { add(matcher) }
         }
 
-    override suspend fun removeGestureBlockedActivity(activity: ComponentName) =
+    override suspend fun removeGestureBlockedMatcher(matcher: TaskMatcher) =
         withContext(mainDispatcher) {
-            _gestureBlockedActivities.emit(
-                _gestureBlockedActivities.value.toMutableSet().apply { remove(activity) }
-            )
+            val existingMatchers = _gestureBlockedMatchers.value
+            if (!existingMatchers.contains(matcher)) {
+                return@withContext
+            }
+
+            _gestureBlockedMatchers.value =
+                existingMatchers.toMutableSet().apply { remove(matcher) }
         }
 }
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 6dc5939..96386e5 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,18 +16,28 @@
 
 package com.android.systemui.navigationbar.gestural.domain
 
-import android.content.ComponentName
+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
 import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.TaskStackChangeListener
+import com.android.systemui.shared.system.TaskStackChangeListeners
+import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
+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.flow.stateIn
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 /**
  * {@link GestureInteractor} helps interact with gesture-related logic, including accessing the
@@ -37,67 +47,79 @@
 @Inject
 constructor(
     private val gestureRepository: GestureRepository,
-    @Application private val scope: CoroutineScope
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    @Background private val backgroundCoroutineContext: CoroutineContext,
+    @Application private val scope: CoroutineScope,
+    private val activityManagerWrapper: ActivityManagerWrapper,
+    private val taskStackChangeListeners: TaskStackChangeListeners,
 ) {
     enum class Scope {
         Local,
         Global
     }
 
-    private val _localGestureBlockedActivities = MutableStateFlow<Set<ComponentName>>(setOf())
-    /** A {@link StateFlow} for listening to changes in Activities where gestures are blocked */
-    val gestureBlockedActivities: StateFlow<Set<ComponentName>>
-        get() =
-            combine(
-                    gestureRepository.gestureBlockedActivities,
-                    _localGestureBlockedActivities.asStateFlow()
-                ) { global, local ->
-                    global + local
-                }
-                .stateIn(scope, SharingStarted.WhileSubscribed(), setOf())
+    private val _localGestureBlockedMatchers = MutableStateFlow<Set<TaskMatcher>>(setOf())
 
-    /**
-     * Adds an {@link Activity} to be blocked based on component when the topmost, focused {@link
-     * Activity}.
-     */
-    fun addGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
-        scope.launch {
-            when (gestureScope) {
-                Scope.Local -> {
-                    _localGestureBlockedActivities.emit(
-                        _localGestureBlockedActivities.value.toMutableSet().apply { add(activity) }
-                    )
-                }
-                Scope.Global -> {
-                    gestureRepository.addGestureBlockedActivity(activity)
-                }
-            }
-        }
-    }
-
-    /** Removes an {@link Activity} from being blocked from gestures. */
-    fun removeGestureBlockedActivity(activity: ComponentName, gestureScope: Scope) {
-        scope.launch {
-            when (gestureScope) {
-                Scope.Local -> {
-                    _localGestureBlockedActivities.emit(
-                        _localGestureBlockedActivities.value.toMutableSet().apply {
-                            remove(activity)
+    private val _topActivity =
+        conflatedCallbackFlow {
+                val taskListener =
+                    object : TaskStackChangeListener {
+                        override fun onTaskStackChanged() {
+                            trySend(Unit)
                         }
+                    }
+
+                taskStackChangeListeners.registerTaskStackListener(taskListener)
+                awaitClose { taskStackChangeListeners.unregisterTaskStackListener(taskListener) }
+            }
+            .flowOn(mainDispatcher)
+            .emitOnStart()
+            .mapLatest { getTopActivity() }
+            .distinctUntilChanged()
+
+    private suspend fun getTopActivity(): TaskInfo? =
+        withContext(backgroundCoroutineContext) {
+            activityManagerWrapper.runningTask?.let { TaskInfo(it.topActivity, it.activityType) }
+        }
+
+    val topActivityBlocked =
+        combine(
+            _topActivity,
+            gestureRepository.gestureBlockedMatchers,
+            _localGestureBlockedMatchers.asStateFlow()
+        ) { runningTask, global, local ->
+            runningTask != null && (global + local).any { it.matches(runningTask) }
+        }
+
+    /** Adds an [TaskMatcher] to decide whether gestures should be blocked. */
+    fun addGestureBlockedMatcher(matcher: TaskMatcher, gestureScope: Scope) {
+        scope.launch {
+            when (gestureScope) {
+                Scope.Local -> {
+                    _localGestureBlockedMatchers.emit(
+                        _localGestureBlockedMatchers.value.toMutableSet().apply { add(matcher) }
                     )
                 }
                 Scope.Global -> {
-                    gestureRepository.removeGestureBlockedActivity(activity)
+                    gestureRepository.addGestureBlockedMatcher(matcher)
                 }
             }
         }
     }
 
-    /**
-     * Checks whether the specified {@link Activity} {@link ComponentName} is being blocked from
-     * gestures.
-     */
-    fun areGesturesBlocked(activity: ComponentName): Boolean {
-        return gestureBlockedActivities.value.contains(activity)
+    /** Removes a gesture from deciding whether gestures should be blocked */
+    fun removeGestureBlockedMatcher(matcher: TaskMatcher, gestureScope: Scope) {
+        scope.launch {
+            when (gestureScope) {
+                Scope.Local -> {
+                    _localGestureBlockedMatchers.emit(
+                        _localGestureBlockedMatchers.value.toMutableSet().apply { remove(matcher) }
+                    )
+                }
+                Scope.Global -> {
+                    gestureRepository.removeGestureBlockedMatcher(matcher)
+                }
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.kt
new file mode 100644
index 0000000..d62b2c0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/TaskMatcher.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.navigationbar.gestural.domain
+
+import android.content.ComponentName
+
+/**
+ * A simple data class for capturing details around a task. Implements equality to ensure changes
+ * can be identified between emitted values.
+ */
+data class TaskInfo(val topActivity: ComponentName?, val topActivityType: Int) {
+    override fun equals(other: Any?): Boolean {
+        return other is TaskInfo &&
+            other.topActivityType == topActivityType &&
+            other.topActivity == topActivity
+    }
+}
+
+/**
+ * [TaskMatcher] provides a way to identify a task based on particular attributes, such as the top
+ * activity type or component name.
+ */
+sealed interface TaskMatcher {
+    fun matches(info: TaskInfo): Boolean
+
+    class TopActivityType(private val type: Int) : TaskMatcher {
+        override fun matches(info: TaskInfo): Boolean {
+            return info.topActivity != null && info.topActivityType == type
+        }
+    }
+
+    class TopActivityComponent(private val component: ComponentName) : TaskMatcher {
+        override fun matches(info: TaskInfo): Boolean {
+            return component == info.topActivity
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 7b248eb..e8c90c1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -68,6 +68,8 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -102,6 +104,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
@@ -137,7 +140,6 @@
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
-import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.rotation.RotationButtonController;
 import com.android.systemui.shared.rotation.RotationPolicyUtil;
@@ -163,6 +165,7 @@
 import com.android.systemui.util.ViewController;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
 
 import dagger.Lazy;
 
@@ -196,6 +199,7 @@
     private final Context mContext;
     private final Bundle mSavedState;
     private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private final AccessibilityManager mAccessibilityManager;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final StatusBarStateController mStatusBarStateController;
@@ -556,6 +560,7 @@
             @Nullable Bundle savedState,
             @DisplayId Context context,
             @DisplayId WindowManager windowManager,
+            @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             Lazy<AssistManager> assistManagerLazy,
             AccessibilityManager accessibilityManager,
             DeviceProvisionedController deviceProvisionedController,
@@ -601,6 +606,7 @@
         mContext = context;
         mSavedState = savedState;
         mWindowManager = windowManager;
+        mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
         mAccessibilityManager = accessibilityManager;
         mDeviceProvisionedController = deviceProvisionedController;
         mStatusBarStateController = statusBarStateController;
@@ -721,7 +727,7 @@
 
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
 
-        mWindowManager.addView(mFrame,
+        mViewCaptureAwareWindowManager.addView(mFrame,
                 getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
                         .getRotation()));
         mDisplayId = mContext.getDisplayId();
@@ -764,7 +770,7 @@
             mCommandQueue.removeCallback(this);
             Trace.beginSection("NavigationBar#removeViewImmediate");
             try {
-                mWindowManager.removeViewImmediate(mView.getRootView());
+                mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
             } finally {
                 Trace.endSection();
             }
@@ -866,7 +872,7 @@
         if (mOrientationHandle != null) {
             resetSecondaryHandle();
             getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
-            mWindowManager.removeView(mOrientationHandle);
+            mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
                     mOrientationHandleGlobalLayoutListener);
         }
@@ -937,7 +943,7 @@
         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
         mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
                 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
-        mWindowManager.addView(mOrientationHandle, mOrientationParams);
+        mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
         mOrientationHandle.setVisibility(View.GONE);
 
         logNavbarOrientation("initSecondaryHomeHandleForRotation");
@@ -1094,8 +1100,8 @@
     // ----- CommandQueue Callbacks -----
 
     @Override
-    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
         if (displayId != mDisplayId) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
new file mode 100644
index 0000000..b6868c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notifications.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Models the UI state for the user actions for navigating to other scenes or overlays. */
+class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
+    UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        setActions(
+            mapOf(
+                Swipe.Up to UserActionResult.HideOverlay(Overlays.NotificationsShade),
+                Back to UserActionResult.HideOverlay(Overlays.NotificationsShade),
+            )
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationsShadeOverlayActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.kt
new file mode 100644
index 0000000..5be225c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModel.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.notifications.ui.viewmodel
+
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models UI state used to render the content of the notifications shade overlay.
+ *
+ * Different from [NotificationsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class NotificationsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+    val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+    val notificationsPlaceholderViewModelFactory: NotificationsPlaceholderViewModel.Factory,
+    private val sceneInteractor: SceneInteractor,
+) {
+    fun onScrimClicked() {
+        sceneInteractor.hideOverlay(
+            overlay = Overlays.NotificationsShade,
+            loggingReason = "Shade scrim clicked",
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationsShadeOverlayContentViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
deleted file mode 100644
index 2d2b869..0000000
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneActionsViewModel.kt
+++ /dev/null
@@ -1,59 +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.notifications.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [NotificationsShadeSceneContentViewModel] which models the _content_ of the
- * scene.
- */
-class NotificationsShadeSceneActionsViewModel
-@AssistedInject
-constructor(
-    private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        setActions(
-            mapOf(
-                if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
-                    Swipe.Up
-                } else {
-                    Swipe.Down
-                } to SceneFamilies.Home,
-                Back to SceneFamilies.Home,
-            )
-        )
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): NotificationsShadeSceneActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt
deleted file mode 100644
index c1c7320..0000000
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneContentViewModel.kt
+++ /dev/null
@@ -1,49 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.notifications.ui.viewmodel
-
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.shade.ui.viewmodel.BaseShadeSceneViewModel
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-/**
- * Models UI state used to render the content of the notifications shade scene.
- *
- * Different from [NotificationsShadeSceneActionsViewModel], which only models user actions that can
- * be performed to navigate to other scenes.
- */
-class NotificationsShadeSceneContentViewModel
-@AssistedInject
-constructor(
-    deviceEntryInteractor: DeviceEntryInteractor,
-    sceneInteractor: SceneInteractor,
-) :
-    BaseShadeSceneViewModel(
-        deviceEntryInteractor,
-        sceneInteractor,
-    ) {
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): NotificationsShadeSceneContentViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.kt
new file mode 100644
index 0000000..a5c07bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeUserActionsViewModel.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.notifications.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models the UI state for the user actions that the user can perform to navigate to other scenes.
+ */
+class NotificationsShadeUserActionsViewModel @AssistedInject constructor() :
+    UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        setActions(
+            mapOf(
+                Swipe.Up to SceneFamilies.Home,
+                Back to SceneFamilies.Home,
+            )
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationsShadeUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
deleted file mode 100644
index e4bafcd..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ /dev/null
@@ -1,298 +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.qs
-
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.database.ContentObserver
-import android.net.Uri
-import android.os.Handler
-import android.os.UserHandle
-import android.provider.Settings
-import android.text.TextUtils
-import android.util.ArraySet
-import android.util.Log
-import androidx.annotation.GuardedBy
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.Dumpable
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.UserAwareController
-import com.android.systemui.util.settings.SecureSettings
-import java.io.PrintWriter
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private const val TAG = "AutoAddTracker"
-private const val DELIMITER = ","
-
-/**
- * Class to track tiles that have been auto-added
- *
- * The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES].
- *
- * It also handles restore gracefully.
- */
-class AutoAddTracker
-@VisibleForTesting
-constructor(
-    private val secureSettings: SecureSettings,
-    private val broadcastDispatcher: BroadcastDispatcher,
-    private val qsHost: QSHost,
-    private val dumpManager: DumpManager,
-    private val mainHandler: Handler?,
-    private val backgroundExecutor: Executor,
-    private var userId: Int
-) : UserAwareController, Dumpable {
-
-    companion object {
-        private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
-    }
-
-    @GuardedBy("autoAdded") private val autoAdded = ArraySet<String>()
-    private var restoredTiles: Map<String, AutoTile>? = null
-
-    override val currentUserId: Int
-        get() = userId
-
-    private val contentObserver =
-        object : ContentObserver(mainHandler) {
-            override fun onChange(
-                selfChange: Boolean,
-                uris: Collection<Uri>,
-                flags: Int,
-                _userId: Int
-            ) {
-                if (_userId != userId) {
-                    // Ignore changes outside of our user. We'll load the correct value on user
-                    // change
-                    return
-                }
-                loadTiles()
-            }
-        }
-
-    private val restoreReceiver =
-        object : BroadcastReceiver() {
-            override fun onReceive(context: Context, intent: Intent) {
-                if (intent.action != Intent.ACTION_SETTING_RESTORED) return
-                processRestoreIntent(intent)
-            }
-        }
-
-    private fun processRestoreIntent(intent: Intent) {
-        when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
-            Settings.Secure.QS_TILES -> {
-                restoredTiles =
-                    intent
-                        .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                        ?.split(DELIMITER)
-                        ?.mapIndexed(::AutoTile)
-                        ?.associateBy(AutoTile::tileType)
-                        ?: run {
-                            Log.w(TAG, "Null restored tiles for user $userId")
-                            emptyMap()
-                        }
-            }
-            Settings.Secure.QS_AUTO_ADDED_TILES -> {
-                restoredTiles?.let { restoredTiles ->
-                    val restoredAutoAdded =
-                        intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)?.split(DELIMITER)
-                            ?: emptyList()
-                    val autoAddedBeforeRestore =
-                        intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)?.split(DELIMITER)
-                            ?: emptyList()
-
-                    val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
-                    if (tilesToRemove.isNotEmpty()) {
-                        Log.d(TAG, "Removing tiles: $tilesToRemove")
-                        qsHost.removeTiles(tilesToRemove)
-                    }
-                    val tiles =
-                        synchronized(autoAdded) {
-                            autoAdded.clear()
-                            autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
-                            getTilesFromListLocked()
-                        }
-                    saveTiles(tiles)
-                }
-                    ?: run {
-                        Log.w(
-                            TAG,
-                            "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
-                                "${Settings.Secure.QS_TILES} for user $userId"
-                        )
-                    }
-            }
-            else -> {} // Do nothing for other Settings
-        }
-    }
-
-    /** Init method must be called after construction to start listening */
-    fun initialize() {
-        dumpManager.registerDumpable(TAG, this)
-        loadTiles()
-        secureSettings.registerContentObserverForUserSync(
-            secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
-            contentObserver,
-            UserHandle.USER_ALL
-        )
-        registerBroadcastReceiver()
-    }
-
-    /** Unregister listeners, receivers and observers */
-    fun destroy() {
-        dumpManager.unregisterDumpable(TAG)
-        secureSettings.unregisterContentObserverSync(contentObserver)
-        unregisterBroadcastReceiver()
-    }
-
-    private fun registerBroadcastReceiver() {
-        broadcastDispatcher.registerReceiver(
-            restoreReceiver,
-            FILTER,
-            backgroundExecutor,
-            UserHandle.of(userId)
-        )
-    }
-
-    private fun unregisterBroadcastReceiver() {
-        broadcastDispatcher.unregisterReceiver(restoreReceiver)
-    }
-
-    override fun changeUser(newUser: UserHandle) {
-        if (newUser.identifier == userId) return
-        unregisterBroadcastReceiver()
-        userId = newUser.identifier
-        restoredTiles = null
-        loadTiles()
-        registerBroadcastReceiver()
-    }
-
-    fun getRestoredTilePosition(tile: String): Int =
-        restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
-
-    /** Returns `true` if the tile has been auto-added before */
-    fun isAdded(tile: String): Boolean {
-        return synchronized(autoAdded) { tile in autoAdded }
-    }
-
-    /**
-     * Sets a tile as auto-added.
-     *
-     * From here on, [isAdded] will return true for that tile.
-     */
-    fun setTileAdded(tile: String) {
-        val tiles =
-            synchronized(autoAdded) {
-                if (autoAdded.add(tile)) {
-                    getTilesFromListLocked()
-                } else {
-                    null
-                }
-            }
-        tiles?.let { saveTiles(it) }
-    }
-
-    /**
-     * Removes a tile from the list of auto-added.
-     *
-     * This allows for this tile to be auto-added again in the future.
-     */
-    fun setTileRemoved(tile: String) {
-        val tiles =
-            synchronized(autoAdded) {
-                if (autoAdded.remove(tile)) {
-                    getTilesFromListLocked()
-                } else {
-                    null
-                }
-            }
-        tiles?.let { saveTiles(it) }
-    }
-
-    private fun getTilesFromListLocked(): String {
-        return TextUtils.join(DELIMITER, autoAdded)
-    }
-
-    private fun saveTiles(tiles: String) {
-        secureSettings.putStringForUser(
-            Settings.Secure.QS_AUTO_ADDED_TILES,
-            tiles,
-            /* tag */ null,
-            /* makeDefault */ false,
-            userId,
-            /* overrideableByRestore */ true
-        )
-    }
-
-    private fun loadTiles() {
-        synchronized(autoAdded) {
-            autoAdded.clear()
-            autoAdded.addAll(getAdded())
-        }
-    }
-
-    private fun getAdded(): Collection<String> {
-        val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
-        return current?.split(DELIMITER) ?: emptySet()
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("Current user: $userId")
-        pw.println("Restored tiles: $restoredTiles")
-        pw.println("Added tiles: $autoAdded")
-    }
-
-    @SysUISingleton
-    class Builder
-    @Inject
-    constructor(
-        private val secureSettings: SecureSettings,
-        private val broadcastDispatcher: BroadcastDispatcher,
-        private val qsHost: QSHost,
-        private val dumpManager: DumpManager,
-        @Main private val handler: Handler,
-        @Background private val executor: Executor
-    ) {
-        private var userId: Int = 0
-
-        fun setUserId(_userId: Int): Builder {
-            userId = _userId
-            return this
-        }
-
-        fun build(): AutoAddTracker {
-            return AutoAddTracker(
-                secureSettings,
-                broadcastDispatcher,
-                qsHost,
-                dumpManager,
-                handler,
-                executor,
-                userId
-            )
-        }
-    }
-
-    private data class AutoTile(val index: Int, val tileType: String)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 9939075..1511f31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -259,6 +259,13 @@
     }
 
     /**
+     * @return height with the squishiness fraction applied.
+     */
+    int getSquishedQqsHeight() {
+        return mHeader.getSquishedHeight();
+    }
+
+    /**
      * Returns the size of QS (or the QSCustomizer), regardless of the measured size of this view
      * @return size in pixels of QS (or QSCustomizer)
      */
@@ -267,6 +274,13 @@
                 : mQSPanel.getMeasuredHeight();
     }
 
+    /**
+     * @return height with the squishiness fraction applied.
+     */
+    int getSquishedQsHeight() {
+        return mQSPanel.getSquishedHeight();
+    }
+
     public void setExpansion(float expansion) {
         mQsExpansion = expansion;
         if (mQSPanelContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
index 9fa6769..bb238f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentStartable.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.fragments.FragmentService
+import com.android.systemui.qs.composefragment.QSFragmentCompose
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -31,13 +32,18 @@
 @Inject
 constructor(
     private val fragmentService: FragmentService,
-    private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
+    private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>,
+    private val qsFragmentComposeProvider: Provider<QSFragmentCompose>,
 ) : CoreStartable {
     override fun start() {
         fragmentService.addFragmentInstantiationProvider(
             QSFragmentLegacy::class.java,
             qsFragmentLegacyProvider
         )
+        fragmentService.addFragmentInstantiationProvider(
+            QSFragmentCompose::class.java,
+            qsFragmentComposeProvider
+        )
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
index c77233eb..4323b31 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt
@@ -47,12 +47,10 @@
 class QSHostAdapter
 @Inject
 constructor(
-    private val qsTileHost: QSTileHost,
     private val interactor: CurrentTilesInteractor,
     private val context: Context,
     private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
     @Application private val scope: CoroutineScope,
-    flags: QSPipelineFlagsRepository,
     dumpManager: DumpManager,
 ) : QSHost {
 
@@ -60,123 +58,69 @@
         private const val TAG = "QSTileHost"
     }
 
-    private val useNewHost = flags.pipelineEnabled
-
     @GuardedBy("callbacksMap") private val callbacksMap = mutableMapOf<QSHost.Callback, Job>()
 
     init {
         scope.launch { tileServiceRequestControllerBuilder.create(this@QSHostAdapter).init() }
         // Redirect dump to the correct host (needed for CTS tests)
-        dumpManager.registerCriticalDumpable(TAG, if (useNewHost) interactor else qsTileHost)
+        dumpManager.registerCriticalDumpable(TAG, interactor)
     }
 
     override fun getTiles(): Collection<QSTile> {
-        return if (useNewHost) {
-            interactor.currentQSTiles
-        } else {
-            qsTileHost.getTiles()
-        }
+        return interactor.currentQSTiles
     }
 
     override fun getSpecs(): List<String> {
-        return if (useNewHost) {
-            interactor.currentTilesSpecs.map { it.spec }
-        } else {
-            qsTileHost.getSpecs()
-        }
+        return interactor.currentTilesSpecs.map { it.spec }
     }
 
     override fun removeTile(spec: String) {
-        if (useNewHost) {
-            interactor.removeTiles(listOf(TileSpec.create(spec)))
-        } else {
-            qsTileHost.removeTile(spec)
-        }
+        interactor.removeTiles(listOf(TileSpec.create(spec)))
     }
 
     override fun addCallback(callback: QSHost.Callback) {
-        if (useNewHost) {
-            val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
-            synchronized(callbacksMap) { callbacksMap.put(callback, job) }
-        } else {
-            qsTileHost.addCallback(callback)
-        }
+        val job = scope.launch { interactor.currentTiles.collect { callback.onTilesChanged() } }
+        synchronized(callbacksMap) { callbacksMap.put(callback, job) }
     }
 
     override fun removeCallback(callback: QSHost.Callback) {
-        if (useNewHost) {
-            synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
-        } else {
-            qsTileHost.removeCallback(callback)
-        }
+        synchronized(callbacksMap) { callbacksMap.remove(callback)?.cancel() }
     }
 
     override fun removeTiles(specs: Collection<String>) {
-        if (useNewHost) {
-            interactor.removeTiles(specs.map(TileSpec::create))
-        } else {
-            qsTileHost.removeTiles(specs)
-        }
+        interactor.removeTiles(specs.map(TileSpec::create))
     }
 
     override fun removeTileByUser(component: ComponentName) {
-        if (useNewHost) {
-            interactor.removeTiles(listOf(TileSpec.create(component)))
-        } else {
-            qsTileHost.removeTileByUser(component)
-        }
+        interactor.removeTiles(listOf(TileSpec.create(component)))
     }
 
     override fun addTile(spec: String, position: Int) {
-        if (useNewHost) {
-            interactor.addTile(TileSpec.create(spec), position)
-        } else {
-            qsTileHost.addTile(spec, position)
-        }
+        interactor.addTile(TileSpec.create(spec), position)
     }
 
     override fun addTile(component: ComponentName, end: Boolean) {
-        if (useNewHost) {
-            interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
-        } else {
-            qsTileHost.addTile(component, end)
-        }
+        interactor.addTile(TileSpec.create(component), if (end) POSITION_AT_END else 0)
     }
 
     override fun changeTilesByUser(previousTiles: List<String>, newTiles: List<String>) {
-        if (useNewHost) {
-            interactor.setTiles(newTiles.map(TileSpec::create))
-        } else {
-            qsTileHost.changeTilesByUser(previousTiles, newTiles)
-        }
+        interactor.setTiles(newTiles.map(TileSpec::create))
     }
 
     override fun getContext(): Context {
-        return if (useNewHost) {
-            context
-        } else {
-            qsTileHost.context
-        }
+        return context
     }
 
     override fun getUserContext(): Context {
-        return if (useNewHost) {
-            interactor.userContext.value
-        } else {
-            qsTileHost.userContext
-        }
+        return interactor.userContext.value
     }
 
     override fun getUserId(): Int {
-        return if (useNewHost) {
-            interactor.userId.value
-        } else {
-            qsTileHost.userId
-        }
+        return interactor.userId.value
     }
 
     override fun createTile(tileSpec: String): QSTile? {
-        return qsTileHost.createTile(tileSpec)
+        return interactor.createTileSync(TileSpec.create(tileSpec))
     }
 
     override fun addTile(spec: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index a6fd35a..0b37b5b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -992,11 +992,25 @@
         return mContainer.getQqsHeight();
     }
 
+    /**
+     * @return height with the squishiness fraction applied.
+     */
+    public int getSquishedQqsHeight() {
+        return mContainer.getSquishedQqsHeight();
+    }
+
     public int getQSHeight() {
         return mContainer.getQsHeight();
     }
 
     /**
+     * @return height with the squishiness fraction applied.
+     */
+    public int getSquishedQsHeight() {
+        return mContainer.getSquishedQsHeight();
+    }
+
+    /**
      * Pass the size of the navbar when it's at the bottom of the device so it can be used as
      * padding
      * @param padding size of the bottom nav bar in px
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.kt
new file mode 100644
index 0000000..1891c41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSModesEvent.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.qs
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** Events of user interactions with modes from the QS Modes dialog. {@see ModesDialogViewModel} */
+enum class QSModesEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+    @UiEvent(doc = "User turned manual Do Not Disturb on via modes dialog") QS_MODES_DND_ON(1870),
+    @UiEvent(doc = "User turned manual Do Not Disturb off via modes dialog") QS_MODES_DND_OFF(1871),
+    @UiEvent(doc = "User opened mode settings from the Do Not Disturb tile in the modes dialog")
+    QS_MODES_DND_SETTINGS(1872),
+    @UiEvent(doc = "User turned automatic mode on via modes dialog") QS_MODES_MODE_ON(1873),
+    @UiEvent(doc = "User turned automatic mode off via modes dialog") QS_MODES_MODE_OFF(1874),
+    @UiEvent(doc = "User opened mode settings from a mode tile in the modes dialog")
+    QS_MODES_MODE_SETTINGS(1875),
+    @UiEvent(doc = "User clicked on Settings from the modes dialog") QS_MODES_SETTINGS(1876),
+    @UiEvent(doc = "User clicked on Do Not Disturb tile, opening the time selection dialog")
+    QS_MODES_DURATION_DIALOG(1879);
+
+    override fun getId() = _id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 032891f..d3bed27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -733,6 +733,30 @@
         mCanCollapse = canCollapse;
     }
 
+    /**
+     * @return height with the {@link QSPanel#setSquishinessFraction(float)} applied.
+     */
+    public int getSquishedHeight() {
+        if (mFooter != null) {
+            final ViewGroup.LayoutParams footerLayoutParams = mFooter.getLayoutParams();
+            final int footerBottomMargin;
+            if (footerLayoutParams instanceof MarginLayoutParams) {
+                footerBottomMargin = ((MarginLayoutParams) footerLayoutParams).bottomMargin;
+            } else {
+                footerBottomMargin = 0;
+            }
+            // This is the distance between the top of the QSPanel and the last view in the
+            // layout (which is the effective the bottom)
+            return mFooter.getBottom() + footerBottomMargin - getTop();
+        }
+        if (mTileLayout != null) {
+            // Footer absence means that the panel is in the QQS. In this case it's just height
+            // of the tiles + paddings.
+            return mTileLayout.getTilesHeight() + getPaddingBottom() + getPaddingTop();
+        }
+        return getHeight();
+    }
+
     @Nullable
     @VisibleForTesting
     View getMediaPlaceholder() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
deleted file mode 100644
index 03c2aa6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * 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.qs;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.ProtoDumpable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.external.CustomTileStatePersister;
-import com.android.systemui.qs.external.TileLifecycleManager;
-import com.android.systemui.qs.external.TileServiceKey;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.nano.QsTileState;
-import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
-import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.qs.tiles.di.NewQSTileFactory;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserFileManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
-import com.android.systemui.util.settings.SecureSettings;
-
-import dagger.Lazy;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import javax.inject.Inject;
-import javax.inject.Provider;
-
-/** Platform implementation of the quick settings tile host
- *
- * This class keeps track of the set of current tiles and is the in memory source of truth
- * (ground truth is kept in {@link Secure#QS_TILES}). When the ground truth changes,
- * {@link #onTuningChanged} will be called and the tiles will be re-created as needed.
- *
- * This class also provides the interface for adding/removing/changing tiles.
- */
-@SysUISingleton
-public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, ProtoDumpable,
-        PanelInteractor, CustomTileAddedRepository {
-    private static final String TAG = "QSTileHost";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // Shared prefs that hold tile lifecycle info.
-    @VisibleForTesting
-    static final String TILES = "tiles_prefs";
-
-    private final Context mContext;
-    private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
-    private final ArrayList<String> mTileSpecs = new ArrayList<>();
-    private final TunerService mTunerService;
-    private final PluginManager mPluginManager;
-    private final QSLogger mQSLogger;
-    private final CustomTileStatePersister mCustomTileStatePersister;
-    private final Executor mMainExecutor;
-    private final UserFileManager mUserFileManager;
-
-    private final List<Callback> mCallbacks = new ArrayList<>();
-    @Nullable
-    private AutoTileManager mAutoTiles;
-    private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
-    private int mCurrentUser;
-    private final Lazy<ShadeController> mShadeControllerProvider;
-    private Context mUserContext;
-    private UserTracker mUserTracker;
-    private SecureSettings mSecureSettings;
-    // Keep track of whether mTilesList contains the same information as the Settings value.
-    // This is a performance optimization to reduce the number of blocking calls to Settings from
-    // main thread.
-    // This is enforced by only cleaning the flag at the end of a successful run of #onTuningChanged
-    private boolean mTilesListDirty = true;
-
-    private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
-
-    private final QSPipelineFlagsRepository mFeatureFlags;
-
-    @Inject
-    public QSTileHost(Context context,
-            Lazy<NewQSTileFactory> newQsTileFactoryProvider,
-            QSFactory defaultFactory,
-            @Main Executor mainExecutor,
-            PluginManager pluginManager,
-            TunerService tunerService,
-            Provider<AutoTileManager> autoTiles,
-            Lazy<ShadeController> shadeControllerProvider,
-            QSLogger qsLogger,
-            UserTracker userTracker,
-            SecureSettings secureSettings,
-            CustomTileStatePersister customTileStatePersister,
-            TileLifecycleManager.Factory tileLifecycleManagerFactory,
-            UserFileManager userFileManager,
-            QSPipelineFlagsRepository featureFlags
-    ) {
-        mContext = context;
-        mUserContext = context;
-        mTunerService = tunerService;
-        mPluginManager = pluginManager;
-        mQSLogger = qsLogger;
-        mMainExecutor = mainExecutor;
-        mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
-        mUserFileManager = userFileManager;
-        mFeatureFlags = featureFlags;
-
-        mShadeControllerProvider = shadeControllerProvider;
-
-        if (featureFlags.getTilesEnabled()) {
-            mQsFactories.add(newQsTileFactoryProvider.get());
-        }
-        mQsFactories.add(defaultFactory);
-        pluginManager.addPluginListener(this, QSFactory.class, true);
-        mUserTracker = userTracker;
-        mCurrentUser = userTracker.getUserId();
-        mSecureSettings = secureSettings;
-        mCustomTileStatePersister = customTileStatePersister;
-
-        mainExecutor.execute(() -> {
-            // This is technically a hack to avoid circular dependency of
-            // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
-            // finishes before creating any tiles.
-            tunerService.addTunable(this, TILES_SETTING);
-            // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
-            if (!mFeatureFlags.getPipelineEnabled()) {
-                mAutoTiles = autoTiles.get();
-            }
-        });
-    }
-
-    public void destroy() {
-        mTiles.values().forEach(tile -> tile.destroy());
-        mAutoTiles.destroy();
-        mTunerService.removeTunable(this);
-        mPluginManager.removePluginListener(this);
-    }
-
-    @Override
-    public void onPluginConnected(QSFactory plugin, Context pluginContext) {
-        // Give plugins priority over creation so they can override if they wish.
-        mQsFactories.add(0, plugin);
-        String value = mTunerService.getValue(TILES_SETTING);
-        // Force remove and recreate of all tiles.
-        onTuningChanged(TILES_SETTING, "");
-        onTuningChanged(TILES_SETTING, value);
-    }
-
-    @Override
-    public void onPluginDisconnected(QSFactory plugin) {
-        mQsFactories.remove(plugin);
-        // Force remove and recreate of all tiles.
-        String value = mTunerService.getValue(TILES_SETTING);
-        onTuningChanged(TILES_SETTING, "");
-        onTuningChanged(TILES_SETTING, value);
-    }
-
-    @Override
-    public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
-    }
-
-    @Override
-    public void removeCallback(Callback callback) {
-        mCallbacks.remove(callback);
-    }
-
-    @Override
-    public Collection<QSTile> getTiles() {
-        return mTiles.values();
-    }
-
-    @Override
-    public void collapsePanels() {
-        mShadeControllerProvider.get().postAnimateCollapseShade();
-    }
-
-    @Override
-    public void forceCollapsePanels() {
-        mShadeControllerProvider.get().postAnimateForceCollapseShade();
-    }
-
-    @Override
-    public void openPanels() {
-        mShadeControllerProvider.get().postAnimateExpandQs();
-    }
-
-    @Override
-    public Context getContext() {
-        return mContext;
-    }
-
-    @Override
-    public Context getUserContext() {
-        return mUserContext;
-    }
-
-    @Override
-    public int getUserId() {
-        return mCurrentUser;
-    }
-
-    public int indexOf(String spec) {
-        return mTileSpecs.indexOf(spec);
-    }
-
-    /**
-     * Whenever the Secure Setting keeping track of the current tiles changes (or upon start) this
-     * will be called with the new value of the setting.
-     *
-     * This method will do the following:
-     * <ol>
-     *     <li>Destroy any existing tile that's not one of the current tiles (in the setting)</li>
-     *     <li>Create new tiles for those that don't already exist. If this tiles end up being
-     *         not available, they'll also be destroyed.</li>
-     *     <li>Save the resolved list of tiles (current tiles that are available) into the setting.
-     *         This means that after this call ends, the tiles in the Setting, {@link #mTileSpecs},
-     *         and visible tiles ({@link #mTiles}) must match.
-     *         </li>
-     * </ol>
-     *
-     * Additionally, if the user has changed, it'll do the following:
-     * <ul>
-     *     <li>Change the user for SystemUI tiles: {@link QSTile#userSwitch}.</li>
-     *     <li>Destroy any {@link CustomTile} and recreate it for the new user.</li>
-     * </ul>
-     *
-     * This happens in main thread as {@link com.android.systemui.tuner.TunerServiceImpl} dispatches
-     * in main thread.
-     *
-     * @see QSTile#isAvailable
-     */
-    @MainThread
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (!TILES_SETTING.equals(key)) {
-            return;
-        }
-        int currentUser = mUserTracker.getUserId();
-        if (currentUser != mCurrentUser) {
-            mUserContext = mUserTracker.getUserContext();
-            if (mAutoTiles != null) {
-                mAutoTiles.changeUser(UserHandle.of(currentUser));
-            }
-        }
-        // Do not process tiles if the flag is enabled.
-        if (mFeatureFlags.getPipelineEnabled()) {
-            return;
-        }
-        QSPipelineFlagsRepository.Utils.assertInLegacyMode();
-        if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
-            newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
-        }
-        final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
-        if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
-        Log.d(TAG, "Recreating tiles: " + tileSpecs);
-        mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
-                tile -> {
-                    Log.d(TAG, "Destroying tile: " + tile.getKey());
-                    mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
-                    tile.getValue().destroy();
-                });
-        final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
-        for (String tileSpec : tileSpecs) {
-            QSTile tile = mTiles.get(tileSpec);
-            if (tile != null && (!(tile instanceof CustomTile)
-                    || ((CustomTile) tile).getUser() == currentUser)) {
-                if (tile.isAvailable()) {
-                    Log.d(TAG, "Adding " + tile);
-                    tile.removeCallbacks();
-                    if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
-                        tile.userSwitch(currentUser);
-                    }
-                    newTiles.put(tileSpec, tile);
-                    mQSLogger.logTileAdded(tileSpec);
-                } else {
-                    tile.destroy();
-                    Log.d(TAG, "Destroying not available tile: " + tileSpec);
-                    mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
-                }
-            } else {
-                // This means that the tile is a CustomTile AND the user is different, so let's
-                // destroy it
-                if (tile != null) {
-                    tile.destroy();
-                    Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
-                    mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
-                }
-                Log.d(TAG, "Creating tile: " + tileSpec);
-                try {
-                    tile = createTile(tileSpec);
-                    if (tile != null) {
-                        if (tile.isAvailable()) {
-                            newTiles.put(tileSpec, tile);
-                            mQSLogger.logTileAdded(tileSpec);
-                        } else {
-                            tile.destroy();
-                            Log.d(TAG, "Destroying not available tile: " + tileSpec);
-                            mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
-                        }
-                    } else {
-                        Log.d(TAG, "No factory for a spec: " + tileSpec);
-                    }
-                } catch (Throwable t) {
-                    Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
-                }
-            }
-        }
-        mCurrentUser = currentUser;
-        List<String> currentSpecs = new ArrayList<>(mTileSpecs);
-        mTileSpecs.clear();
-        mTileSpecs.addAll(newTiles.keySet()); // Only add the valid (available) tiles.
-        mTiles.clear();
-        mTiles.putAll(newTiles);
-        if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
-            // If we didn't manage to create any tiles, set it to empty (default)
-            Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
-            changeTilesByUser(currentSpecs, loadTileSpecs(mContext, ""));
-        } else {
-            String resolvedTiles = TextUtils.join(",", mTileSpecs);
-            if (!resolvedTiles.equals(newValue)) {
-                // If the resolved tiles (those we actually ended up with) are different than
-                // the ones that are in the setting, update the Setting.
-                saveTilesToSettings(mTileSpecs);
-            }
-            mTilesListDirty = false;
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                mCallbacks.get(i).onTilesChanged();
-            }
-        }
-    }
-
-    /**
-     * Only use with [CustomTile] if the tile doesn't exist anymore (and therefore doesn't need
-     * its lifecycle terminated).
-     */
-    @Override
-    public void removeTile(String spec) {
-        if (spec.startsWith(CustomTile.PREFIX)) {
-            // If the tile is removed (due to it not actually existing), mark it as removed. That
-            // way it will be marked as newly added if it appears in the future.
-            setTileAdded(CustomTile.getComponentFromSpec(spec), mCurrentUser, false);
-        }
-        mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
-    }
-
-    /**
-     * Remove many tiles at once.
-     *
-     * It will only save to settings once (as opposed to {@link QSTileHost#removeTileByUser} called
-     * multiple times).
-     */
-    @Override
-    public void removeTiles(Collection<String> specs) {
-        mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
-    }
-
-    /**
-     * Add a tile to the end
-     *
-     * @param spec string matching a pre-defined tilespec
-     */
-    public void addTile(String spec) {
-        addTile(spec, POSITION_AT_END);
-    }
-
-    @Override
-    public void addTile(String spec, int requestPosition) {
-        mMainExecutor.execute(() ->
-                changeTileSpecs(tileSpecs -> {
-                    if (tileSpecs.contains(spec)) return false;
-
-                    int size = tileSpecs.size();
-                    if (requestPosition == POSITION_AT_END || requestPosition >= size) {
-                        tileSpecs.add(spec);
-                    } else {
-                        tileSpecs.add(requestPosition, spec);
-                    }
-                    return true;
-                })
-        );
-    }
-
-    // When calling this, you may want to modify mTilesListDirty accordingly.
-    @MainThread
-    private void saveTilesToSettings(List<String> tileSpecs) {
-        Log.d(TAG, "Saving tiles: " + tileSpecs + " for user: " + mCurrentUser);
-        mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
-                null /* tag */, false /* default */, mCurrentUser,
-                true /* overrideable by restore */);
-    }
-
-    @MainThread
-    private void changeTileSpecs(Predicate<List<String>> changeFunction) {
-        final List<String> tileSpecs;
-        if (!mTilesListDirty) {
-            tileSpecs = new ArrayList<>(mTileSpecs);
-        } else {
-            tileSpecs = loadTileSpecs(mContext,
-                    mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser));
-        }
-        if (changeFunction.test(tileSpecs)) {
-            mTilesListDirty = true;
-            saveTilesToSettings(tileSpecs);
-        }
-    }
-
-    @Override
-    public void addTile(ComponentName tile) {
-        addTile(tile, /* end */ false);
-    }
-
-    @Override
-    public void addTile(ComponentName tile, boolean end) {
-        String spec = CustomTile.toSpec(tile);
-        addTile(spec, end ? POSITION_AT_END : 0);
-    }
-
-    /**
-     * This will call through {@link #changeTilesByUser}. It should only be used when a tile is
-     * removed by a <b>user action</b> like {@code adb}.
-     */
-    @Override
-    public void removeTileByUser(ComponentName tile) {
-        mMainExecutor.execute(() -> {
-            List<String> newSpecs = new ArrayList<>(mTileSpecs);
-            if (newSpecs.remove(CustomTile.toSpec(tile))) {
-                changeTilesByUser(mTileSpecs, newSpecs);
-            }
-        });
-    }
-
-    /**
-     * Change the tiles triggered by the user editing.
-     * <p>
-     * This is not called on device start, or on user change.
-     *
-     * {@link android.service.quicksettings.TileService#onTileRemoved} will be called for tiles
-     * that are removed.
-     */
-    @MainThread
-    @Override
-    public void changeTilesByUser(List<String> previousTiles, List<String> newTiles) {
-        final List<String> copy = new ArrayList<>(previousTiles);
-        final int NP = copy.size();
-        for (int i = 0; i < NP; i++) {
-            String tileSpec = copy.get(i);
-            if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
-            if (!newTiles.contains(tileSpec)) {
-                ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
-                Intent intent = new Intent().setComponent(component);
-                TileLifecycleManager lifecycleManager = mTileLifeCycleManagerFactory.create(
-                        intent, new UserHandle(mCurrentUser));
-                lifecycleManager.onStopListening();
-                lifecycleManager.onTileRemoved();
-                mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
-                setTileAdded(component, mCurrentUser, false);
-                lifecycleManager.flushMessagesAndUnbind();
-            }
-        }
-        Log.d(TAG, "saveCurrentTiles " + newTiles);
-        mTilesListDirty = true;
-        saveTilesToSettings(newTiles);
-    }
-
-    @Nullable
-    @Override
-    public QSTile createTile(String tileSpec) {
-        for (int i = 0; i < mQsFactories.size(); i++) {
-            QSTile t = mQsFactories.get(i).createTile(tileSpec);
-            if (t != null) {
-                return t;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Check if a particular {@link CustomTile} has been added for a user and has not been removed
-     * since.
-     * @param componentName the {@link ComponentName} of the
-     *                      {@link android.service.quicksettings.TileService} associated with the
-     *                      tile.
-     * @param userId the user to check
-     */
-    @Override
-    public boolean isTileAdded(ComponentName componentName, int userId) {
-        return mUserFileManager
-                .getSharedPreferences(TILES, 0, userId)
-                .getBoolean(componentName.flattenToString(), false);
-    }
-
-    /**
-     * Persists whether a particular {@link CustomTile} has been added and it's currently in the
-     * set of selected tiles ({@link #mTiles}.
-     * @param componentName the {@link ComponentName} of the
-     *                      {@link android.service.quicksettings.TileService} associated
-     *                      with the tile.
-     * @param userId the user for this tile
-     * @param added {@code true} if the tile is being added, {@code false} otherwise
-     */
-    @Override
-    public void setTileAdded(ComponentName componentName, int userId, boolean added) {
-        mUserFileManager.getSharedPreferences(TILES, 0, userId)
-                .edit()
-                .putBoolean(componentName.flattenToString(), added)
-                .apply();
-    }
-
-    @Override
-    public List<String> getSpecs() {
-        return mTileSpecs;
-    }
-
-    protected static List<String> loadTileSpecs(Context context, String tileList) {
-        final Resources res = context.getResources();
-
-        if (TextUtils.isEmpty(tileList)) {
-            tileList = res.getString(R.string.quick_settings_tiles);
-            Log.d(TAG, "Loaded tile specs from default config: " + tileList);
-        } else {
-            Log.d(TAG, "Loaded tile specs from setting: " + tileList);
-        }
-        final ArrayList<String> tiles = new ArrayList<String>();
-        boolean addedDefault = false;
-        Set<String> addedSpecs = new ArraySet<>();
-        for (String tile : tileList.split(",")) {
-            tile = tile.trim();
-            if (tile.isEmpty()) continue;
-            if (tile.equals("default")) {
-                if (!addedDefault) {
-                    List<String> defaultSpecs = QSHost.getDefaultSpecs(context.getResources());
-                    for (String spec : defaultSpecs) {
-                        if (!addedSpecs.contains(spec)) {
-                            tiles.add(spec);
-                            addedSpecs.add(spec);
-                        }
-                    }
-                    addedDefault = true;
-                }
-            } else {
-                if (!addedSpecs.contains(tile)) {
-                    tiles.add(tile);
-                    addedSpecs.add(tile);
-                }
-            }
-        }
-
-        if (!tiles.contains("internet")) {
-            if (tiles.contains("wifi")) {
-                // Replace the WiFi with Internet, and remove the Cell
-                tiles.set(tiles.indexOf("wifi"), "internet");
-                tiles.remove("cell");
-            } else if (tiles.contains("cell")) {
-                // Replace the Cell with Internet
-                tiles.set(tiles.indexOf("cell"), "internet");
-            }
-        } else {
-            tiles.remove("wifi");
-            tiles.remove("cell");
-        }
-        return tiles;
-    }
-
-    @Override
-    public void dump(PrintWriter pw, String[] args) {
-        pw.println("QSTileHost:");
-        pw.println("tile specs: " + mTileSpecs);
-        pw.println("current user: " + mCurrentUser);
-        pw.println("is dirty: " + mTilesListDirty);
-        pw.println("tiles:");
-        mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
-                .forEach(o -> ((Dumpable) o).dump(pw, args));
-    }
-
-    @Override
-    public void dumpProto(@NotNull SystemUIProtoDump systemUIProtoDump, @NotNull String[] args) {
-        List<QsTileState> data = mTiles.values().stream()
-                .map(QSTile::getState)
-                .map(TileStateToProtoKt::toProto)
-                .filter(Objects::nonNull)
-                .collect(Collectors.toList());
-
-        systemUIProtoDump.tiles = data.toArray(new QsTileState[0]);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.kt
new file mode 100644
index 0000000..ef7e7eb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileIcon.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.qs
+
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileImpl
+
+/**
+ * Creates a [QSTile.Icon] from an [Icon].
+ * * [Icon.Loaded] -> [QSTileImpl.DrawableIcon]
+ * * [Icon.Resource] -> [QSTileImpl.ResourceIcon]
+ */
+fun Icon.asQSTileIcon(): QSTile.Icon {
+    return when (this) {
+        is Icon.Loaded -> {
+            QSTileImpl.DrawableIcon(this.drawable)
+        }
+        is Icon.Resource -> {
+            QSTileImpl.ResourceIcon.get(this.res)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index f207b1d..bc695bdd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.leak.RotationUtils;
 
@@ -77,9 +78,11 @@
     @Override
     protected void onInit() {
         super.onInit();
-        updateMediaExpansion();
-        mMediaHost.setShowsOnlyActiveMedia(true);
-        mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+        if (!SceneContainerFlag.isEnabled()) {
+            updateMediaExpansion();
+            mMediaHost.setShowsOnlyActiveMedia(true);
+            mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
+        }
     }
 
     @Override
@@ -125,7 +128,9 @@
         if (newMaxTiles != mView.getNumQuickTiles()) {
             setMaxTiles(newMaxTiles);
         }
-        updateMediaExpansion();
+        if (!SceneContainerFlag.isEnabled()) {
+            updateMediaExpansion();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 5a3f1c0..8fde52c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -123,4 +123,11 @@
         lp.setMarginEnd(marginEnd);
         view.setLayoutParams(lp);
     }
+
+    /**
+     * @return height with the squishiness fraction applied.
+     */
+    public int getSquishedHeight() {
+        return mHeaderQsPanel.getSquishedHeight();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
new file mode 100644
index 0000000..c39ff55
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -0,0 +1,528 @@
+/*
+ * 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.composefragment
+
+import android.annotation.SuppressLint
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.CustomAccessibilityAction
+import androidx.compose.ui.semantics.customActions
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.round
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.modifiers.height
+import com.android.compose.modifiers.padding
+import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.lifecycle.repeatWhenAttached
+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
+import com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.plugins.qs.QSContainerController
+import com.android.systemui.qs.composefragment.ui.notificationScrimClip
+import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
+import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.footer.ui.compose.FooterActions
+import com.android.systemui.qs.panels.ui.compose.QuickQuickSettings
+import com.android.systemui.qs.ui.composable.QuickSettingsTheme
+import com.android.systemui.qs.ui.composable.ShadeBody
+import com.android.systemui.res.R
+import com.android.systemui.util.LifecycleFragment
+import java.util.function.Consumer
+import javax.inject.Inject
+import javax.inject.Named
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+
+@SuppressLint("ValidFragment")
+class QSFragmentCompose
+@Inject
+constructor(
+    private val qsFragmentComposeViewModelFactory: QSFragmentComposeViewModel.Factory,
+    @Named(QUICK_QS_PANEL) private val qqsMediaHost: MediaHost,
+    @Named(QS_PANEL) private val qsMediaHost: MediaHost,
+) : LifecycleFragment(), QS {
+
+    private val scrollListener = MutableStateFlow<QS.ScrollListener?>(null)
+    private val heightListener = MutableStateFlow<QS.HeightListener?>(null)
+    private val qsContainerController = MutableStateFlow<QSContainerController?>(null)
+
+    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()
+
+    // 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)
+        }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        QSComposeFragment.isUnexpectedlyInLegacyMode()
+        viewModel = qsFragmentComposeViewModelFactory.create(lifecycleScope)
+
+        qqsMediaHost.init(MediaHierarchyManager.LOCATION_QQS)
+        qsMediaHost.init(MediaHierarchyManager.LOCATION_QS)
+        setListenerCollections()
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        val context = inflater.context
+        return ComposeView(context).apply {
+            setBackPressedDispatcher()
+            setContent {
+                PlatformTheme {
+                    val visible by viewModel.qsVisible.collectAsStateWithLifecycle()
+                    val qsState by viewModel.expansionState.collectAsStateWithLifecycle()
+
+                    AnimatedVisibility(
+                        visible = visible,
+                        modifier =
+                            Modifier.windowInsetsPadding(WindowInsets.navigationBars).thenIf(
+                                notificationScrimClippingParams.isEnabled
+                            ) {
+                                Modifier.notificationScrimClip(
+                                    notificationScrimClippingParams.leftInset,
+                                    notificationScrimClippingParams.top,
+                                    notificationScrimClippingParams.rightInset,
+                                    notificationScrimClippingParams.bottom,
+                                    notificationScrimClippingParams.radius,
+                                )
+                            }
+                    ) {
+                        AnimatedContent(targetState = qsState) {
+                            when (it) {
+                                QSFragmentComposeViewModel.QSExpansionState.QQS -> {
+                                    QuickQuickSettingsElement()
+                                }
+                                QSFragmentComposeViewModel.QSExpansionState.QS -> {
+                                    QuickSettingsElement()
+                                }
+                                else -> {}
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    override fun setPanelView(notificationPanelView: QS.HeightListener?) {
+        heightListener.value = notificationPanelView
+    }
+
+    override fun hideImmediately() {
+        //        view?.animate()?.cancel()
+        //        view?.y = -qsMinExpansionHeight.toFloat()
+    }
+
+    override fun getQsMinExpansionHeight(): Int {
+        // TODO (b/353253277) implement split screen
+        return qqsHeight.value
+    }
+
+    override fun getDesiredHeight(): Int {
+        /*
+         * Looking at the code, it seems that
+         * * If customizing, then the height is that of the view post-layout, which is set by
+         *   QSContainerImpl.calculateContainerHeight, which is the height the customizer takes
+         * * If not customizing, it's the measured height. So we may want to surface that.
+         */
+        return view?.height ?: 0
+    }
+
+    override fun setHeightOverride(desiredHeight: Int) {
+        viewModel.heightOverrideValue = desiredHeight
+    }
+
+    override fun setHeaderClickable(qsExpansionEnabled: Boolean) {
+        // Empty method
+    }
+
+    override fun isCustomizing(): Boolean {
+        return viewModel.containerViewModel.editModeViewModel.isEditing.value
+    }
+
+    override fun closeCustomizer() {
+        viewModel.containerViewModel.editModeViewModel.stopEditing()
+    }
+
+    override fun setOverscrolling(overscrolling: Boolean) {
+        viewModel.stackScrollerOverscrollingValue = overscrolling
+    }
+
+    override fun setExpanded(qsExpanded: Boolean) {
+        viewModel.isQSExpanded = qsExpanded
+    }
+
+    override fun setListening(listening: Boolean) {
+        // Not needed, views start listening and collection when composed
+    }
+
+    override fun setQsVisible(qsVisible: Boolean) {
+        viewModel.isQSVisible = qsVisible
+    }
+
+    override fun isShowingDetail(): Boolean {
+        return isCustomizing
+    }
+
+    override fun closeDetail() {
+        closeCustomizer()
+    }
+
+    override fun animateHeaderSlidingOut() {
+        // TODO(b/353254353)
+    }
+
+    override fun setQsExpansion(
+        qsExpansionFraction: Float,
+        panelExpansionFraction: Float,
+        headerTranslation: Float,
+        squishinessFraction: Float
+    ) {
+        viewModel.qsExpansionValue = qsExpansionFraction
+        viewModel.panelExpansionFractionValue = panelExpansionFraction
+        viewModel.squishinessFractionValue = squishinessFraction
+
+        // TODO(b/353254353) Handle header translation
+    }
+
+    override fun setHeaderListening(listening: Boolean) {
+        // Not needed, header will start listening as soon as it's composed
+    }
+
+    override fun notifyCustomizeChanged() {
+        // Not needed, only called from inside customizer
+    }
+
+    override fun setContainerController(controller: QSContainerController?) {
+        qsContainerController.value = controller
+    }
+
+    override fun setCollapseExpandAction(action: Runnable?) {
+        viewModel.collapseExpandAccessibilityAction = action
+    }
+
+    override fun getHeightDiff(): Int {
+        return 0 // For now TODO(b/353254353)
+    }
+
+    override fun getHeader(): View? {
+        QSComposeFragment.isUnexpectedlyInLegacyMode()
+        return null
+    }
+
+    override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) {
+        super.setShouldUpdateSquishinessOnMedia(shouldUpdate)
+        // TODO (b/353253280)
+    }
+
+    override fun setInSplitShade(shouldTranslate: Boolean) {
+        // TODO (b/356435605)
+    }
+
+    override fun setTransitionToFullShadeProgress(
+        isTransitioningToFullShade: Boolean,
+        qsTransitionFraction: Float,
+        qsSquishinessFraction: Float
+    ) {
+        super.setTransitionToFullShadeProgress(
+            isTransitioningToFullShade,
+            qsTransitionFraction,
+            qsSquishinessFraction
+        )
+    }
+
+    override fun setFancyClipping(
+        leftInset: Int,
+        top: Int,
+        rightInset: Int,
+        bottom: Int,
+        cornerRadius: Int,
+        visible: Boolean,
+        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
+    }
+
+    override fun isFullyCollapsed(): Boolean {
+        return viewModel.qsExpansionValue <= 0f
+    }
+
+    override fun setCollapsedMediaVisibilityChangedListener(listener: Consumer<Boolean>?) {
+        // TODO (b/353253280)
+    }
+
+    override fun setScrollListener(scrollListener: QS.ScrollListener?) {
+        this.scrollListener.value = scrollListener
+    }
+
+    override fun setOverScrollAmount(overScrollAmount: Int) {
+        super.setOverScrollAmount(overScrollAmount)
+    }
+
+    override fun setIsNotificationPanelFullWidth(isFullWidth: Boolean) {
+        viewModel.isSmallScreenValue = isFullWidth
+    }
+
+    override fun getHeaderTop(): Int {
+        return viewModel.qqsHeaderHeight.value
+    }
+
+    override fun getHeaderBottom(): Int {
+        return headerTop + qqsHeight.value
+    }
+
+    override fun getHeaderLeft(): Int {
+        return qqsPositionOnRoot.left
+    }
+
+    override fun getHeaderBoundsOnScreen(outBounds: Rect) {
+        outBounds.set(qqsPositionOnRoot)
+        view?.getBoundsOnScreen(composeViewPositionOnScreen)
+            ?: run { composeViewPositionOnScreen.setEmpty() }
+        qqsPositionOnRoot.offset(composeViewPositionOnScreen.left, composeViewPositionOnScreen.top)
+    }
+
+    override fun isHeaderShown(): Boolean {
+        return qqsVisible.value
+    }
+
+    private fun setListenerCollections() {
+        lifecycleScope.launch {
+            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    //                    TODO
+                    //                    setListenerJob(
+                    //                            scrollListener,
+                    //
+                    //                    )
+                }
+                launch {
+                    setListenerJob(
+                        heightListener,
+                        viewModel.containerViewModel.editModeViewModel.isEditing
+                    ) {
+                        onQsHeightChanged()
+                    }
+                }
+                launch {
+                    setListenerJob(
+                        qsContainerController,
+                        viewModel.containerViewModel.editModeViewModel.isEditing
+                    ) {
+                        setCustomizerShowing(it)
+                    }
+                }
+            }
+        }
+    }
+
+    @Composable
+    private fun QuickQuickSettingsElement() {
+        val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+        DisposableEffect(Unit) {
+            qqsVisible.value = true
+
+            onDispose { qqsVisible.value = false }
+        }
+        Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) {
+            Box(modifier = Modifier.fillMaxWidth()) {
+                val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
+                if (qsEnabled) {
+                    QuickQuickSettings(
+                        viewModel = viewModel.containerViewModel.quickQuickSettingsViewModel,
+                        modifier =
+                            Modifier.onGloballyPositioned { coordinates ->
+                                    val (leftFromRoot, topFromRoot) =
+                                        coordinates.positionInRoot().round()
+                                    val (width, height) = coordinates.size
+                                    qqsPositionOnRoot.set(
+                                        leftFromRoot,
+                                        topFromRoot,
+                                        leftFromRoot + width,
+                                        topFromRoot + height
+                                    )
+                                }
+                                .layout { measurable, constraints ->
+                                    val placeable = measurable.measure(constraints)
+                                    qqsHeight.value = placeable.height
+
+                                    layout(placeable.width, placeable.height) {
+                                        placeable.place(0, 0)
+                                    }
+                                }
+                                .padding(top = { qqsPadding })
+                                .collapseExpandSemanticAction(
+                                    stringResource(
+                                        id = R.string.accessibility_quick_settings_expand
+                                    )
+                                )
+                    )
+                }
+            }
+            Spacer(modifier = Modifier.weight(1f))
+        }
+    }
+
+    @Composable
+    private fun QuickSettingsElement() {
+        val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+        val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
+        Column(
+            modifier =
+                Modifier.collapseExpandSemanticAction(
+                    stringResource(id = R.string.accessibility_quick_settings_collapse)
+                )
+        ) {
+            val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
+            if (qsEnabled) {
+                Box(modifier = Modifier.fillMaxSize().weight(1f)) {
+                    Column {
+                        Spacer(
+                            modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() }
+                        )
+                        ShadeBody(viewModel = viewModel.containerViewModel)
+                    }
+                }
+                QuickSettingsTheme {
+                    FooterActions(
+                        viewModel = viewModel.footerActionsViewModel,
+                        qsVisibilityLifecycleOwner = this@QSFragmentCompose,
+                        modifier = Modifier.sysuiResTag("qs_footer_actions")
+                    )
+                }
+            }
+        }
+    }
+
+    private fun Modifier.collapseExpandSemanticAction(label: String): Modifier {
+        return viewModel.collapseExpandAccessibilityAction?.let {
+            semantics {
+                customActions =
+                    listOf(
+                        CustomAccessibilityAction(label) {
+                            it.run()
+                            true
+                        }
+                    )
+            }
+        } ?: this
+    }
+}
+
+private fun View.setBackPressedDispatcher() {
+    repeatWhenAttached {
+        repeatOnLifecycle(Lifecycle.State.CREATED) {
+            setViewTreeOnBackPressedDispatcherOwner(
+                object : OnBackPressedDispatcherOwner {
+                    override val onBackPressedDispatcher =
+                        OnBackPressedDispatcher().apply {
+                            setOnBackInvokedDispatcher(it.viewRootImpl.onBackInvokedDispatcher)
+                        }
+
+                    override val lifecycle: Lifecycle = [email protected]
+                }
+            )
+        }
+    }
+}
+
+private suspend inline fun <Listener : Any, Data> setListenerJob(
+    listenerFlow: MutableStateFlow<Listener?>,
+    dataFlow: Flow<Data>,
+    crossinline onCollect: suspend Listener.(Data) -> Unit
+) {
+    coroutineScope {
+        try {
+            listenerFlow.collectLatest { listenerOrNull ->
+                listenerOrNull?.let { currentListener ->
+                    launch {
+                        // Called when editing mode changes
+                        dataFlow.collect { currentListener.onCollect(it) }
+                    }
+                }
+            }
+            awaitCancellation()
+        } finally {
+            listenerFlow.value = null
+        }
+    }
+}
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
new file mode 100644
index 0000000..93c6445
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.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.qs.composefragment.ui
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ClipOp
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.asAndroidPath
+import androidx.compose.ui.graphics.drawscope.ContentDrawScope
+import androidx.compose.ui.graphics.drawscope.clipPath
+import androidx.compose.ui.node.DrawModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.InspectorInfo
+
+/**
+ * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
+ * ([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)
+}
+
+private class NotificationScrimClipNode(
+    var leftInset: Float,
+    var top: Float,
+    var rightInset: Float,
+    var bottom: Float,
+    var radius: Float,
+) : DrawModifierNode, Modifier.Node() {
+    private val path = Path()
+
+    var invalidated = true
+
+    override fun ContentDrawScope.draw() {
+        if (invalidated) {
+            path.rewind()
+            path
+                .asAndroidPath()
+                .addRoundRect(
+                    -leftInset,
+                    top,
+                    size.width + rightInset,
+                    bottom,
+                    radius,
+                    radius,
+                    android.graphics.Path.Direction.CW
+                )
+            invalidated = false
+        }
+        clipPath(path, ClipOp.Difference) { [email protected]() }
+    }
+}
+
+private data class NotificationScrimClipElement(
+    val leftInset: Int,
+    val top: Int,
+    val rightInset: Int,
+    val bottom: Int,
+    val radius: Int,
+) : ModifierNodeElement<NotificationScrimClipNode>() {
+    override fun create(): NotificationScrimClipNode {
+        return NotificationScrimClipNode(
+            leftInset.toFloat(),
+            top.toFloat(),
+            rightInset.toFloat(),
+            bottom.toFloat(),
+            radius.toFloat(),
+        )
+    }
+
+    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
+        }
+    }
+
+    override fun InspectorInfo.inspectableProperties() {
+        name = "notificationScrimClip"
+        properties["leftInset"] = leftInset
+        properties["top"] = top
+        properties["rightInset"] = rightInset
+        properties["bottom"] = bottom
+        properties["radius"] = radius
+    }
+}
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
new file mode 100644
index 0000000..16133f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -0,0 +1,245 @@
+/*
+ * 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.composefragment.viewmodel
+
+import android.content.res.Resources
+import android.graphics.Rect
+import androidx.annotation.FloatRange
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+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.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class QSFragmentComposeViewModel
+@AssistedInject
+constructor(
+    val containerViewModel: QuickSettingsContainerViewModel,
+    @Main private val resources: Resources,
+    private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
+    private val footerActionsController: FooterActionsController,
+    private val sysuiStatusBarStateController: SysuiStatusBarStateController,
+    private val keyguardBypassController: KeyguardBypassController,
+    private val disableFlagsRepository: DisableFlagsRepository,
+    private val largeScreenShadeInterpolator: LargeScreenShadeInterpolator,
+    private val configurationInteractor: ConfigurationInteractor,
+    private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
+    @Assisted private val lifecycleScope: LifecycleCoroutineScope,
+) {
+    val footerActionsViewModel =
+        footerActionsViewModelFactory.create(lifecycleScope).also {
+            lifecycleScope.launch { footerActionsController.init() }
+        }
+
+    private val _qsBounds = MutableStateFlow(Rect())
+
+    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
+        }
+
+    // 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 val _panelFraction = MutableStateFlow(0f)
+    var panelExpansionFractionValue: Float
+        get() = _panelFraction.value
+        set(value) {
+            _panelFraction.value = value
+        }
+
+    private val _squishinessFraction = MutableStateFlow(0f)
+    var squishinessFractionValue: Float
+        get() = _squishinessFraction.value
+        set(value) {
+            _squishinessFraction.value = value
+        }
+
+    val qqsHeaderHeight =
+        configurationInteractor.onAnyConfigurationChange
+            .map {
+                if (LargeScreenUtils.shouldUseLargeScreenShadeHeader(resources)) {
+                    0
+                } else {
+                    largeScreenHeaderHelper.getLargeScreenHeaderHeight()
+                }
+            }
+            .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), 0)
+
+    private val _headerAnimating = MutableStateFlow(false)
+
+    private val _stackScrollerOverscrolling = MutableStateFlow(false)
+    var stackScrollerOverscrollingValue: Boolean
+        get() = _stackScrollerOverscrolling.value
+        set(value) {
+            _stackScrollerOverscrolling.value = value
+        }
+
+    /**
+     * 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()
+            )
+
+    private val _showCollapsedOnKeyguard = MutableStateFlow(false)
+
+    private val _keyguardAndExpanded = MutableStateFlow(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)
+                        }
+
+                        override fun onUpcomingStateChanged(upcomingState: Int) {
+                            if (upcomingState == StatusBarState.KEYGUARD) {
+                                trySend(upcomingState)
+                            }
+                        }
+                    }
+                sysuiStatusBarStateController.addCallback(callback)
+
+                awaitClose { sysuiStatusBarStateController.removeCallback(callback) }
+            }
+            .stateIn(
+                lifecycleScope,
+                SharingStarted.WhileSubscribed(),
+                sysuiStatusBarStateController.state,
+            )
+
+    private val _viewHeight = MutableStateFlow(0)
+
+    private val _headerTranslation = MutableStateFlow(0f)
+
+    private val _inSplitShade = MutableStateFlow(false)
+
+    private val _transitioningToFullShade = MutableStateFlow(false)
+
+    private val _lockscreenToShadeProgress = MutableStateFlow(false)
+
+    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
+        }
+
+    val expansionState: StateFlow<QSExpansionState> =
+        combine(
+                _stackScrollerOverscrolling,
+                _qsExpanded,
+                _qsExpansion,
+            ) { args: Array<Any> ->
+                val expansion = args[2] as Float
+                if (expansion > 0.5f) {
+                    QSExpansionState.QS
+                } else {
+                    QSExpansionState.QQS
+                }
+            }
+            .stateIn(lifecycleScope, SharingStarted.WhileSubscribed(), QSExpansionState.QQS)
+
+    /**
+     * Accessibility action for collapsing/expanding QS. The provided runnable is responsible for
+     * determining the correct action based on the expansion state.
+     */
+    var collapseExpandAccessibilityAction: Runnable? = null
+
+    @AssistedFactory
+    interface Factory {
+        fun create(lifecycleScope: LifecycleCoroutineScope): QSFragmentComposeViewModel
+    }
+
+    sealed interface QSExpansionState {
+        data object QQS : QSExpansionState
+
+        data object QS : QSExpansionState
+
+        @JvmInline value class Expanding(val progress: Float) : QSExpansionState
+
+        @JvmInline value class Collapsing(val progress: Float) : QSExpansionState
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 496a6f8..a947d36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -18,17 +18,14 @@
 
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QSHostAdapter
-import com.android.systemui.qs.QSTileHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.QsEventLoggerImpl
 import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
 import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 
 @Module
 interface QSHostModule {
@@ -37,36 +34,10 @@
 
     @Binds fun provideEventLogger(impl: QsEventLoggerImpl): QsEventLogger
 
-    @Module
-    companion object {
-        private const val MAX_QS_INSTANCE_ID = 1 shl 20
+    @Binds fun providePanelInteractor(impl: PanelInteractorImpl): PanelInteractor
 
-        @Provides
-        @JvmStatic
-        fun providePanelInteractor(
-            featureFlags: QSPipelineFlagsRepository,
-            qsHost: QSTileHost,
-            panelInteractorImpl: PanelInteractorImpl
-        ): PanelInteractor {
-            return if (featureFlags.pipelineEnabled) {
-                panelInteractorImpl
-            } else {
-                qsHost
-            }
-        }
-
-        @Provides
-        @JvmStatic
-        fun provideCustomTileAddedRepository(
-            featureFlags: QSPipelineFlagsRepository,
-            qsHost: QSTileHost,
-            customTileAddedRepository: CustomTileAddedSharedPrefsRepository
-        ): CustomTileAddedRepository {
-            return if (featureFlags.pipelineEnabled) {
-                customTileAddedRepository
-            } else {
-                qsHost
-            }
-        }
-    }
+    @Binds
+    fun provideCustomTileAddedRepository(
+        impl: CustomTileAddedSharedPrefsRepository
+    ): CustomTileAddedRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index b705a03..29bcad4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -16,17 +16,7 @@
 
 package com.android.systemui.qs.dagger;
 
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.dagger.MediaModule;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.qs.ReduceBrightColorsControllerImpl;
 import com.android.systemui.qs.external.QSExternalModule;
@@ -36,24 +26,12 @@
 import com.android.systemui.qs.tiles.di.QSTilesModule;
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter;
 import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.settings.SecureSettings;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
 
 import java.util.Map;
 
-import javax.inject.Named;
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.Multibinds;
 
 /**
  * Module for QS dependencies
@@ -78,45 +56,6 @@
     @Multibinds
     Map<String, QSTileImpl<?>> tileMap();
 
-    @Provides
-    @SysUISingleton
-    static AutoTileManager provideAutoTileManager(
-            Context context,
-            AutoAddTracker.Builder autoAddTrackerBuilder,
-            QSHost host,
-            @Background Handler handler,
-            SecureSettings secureSettings,
-            HotspotController hotspotController,
-            DataSaverController dataSaverController,
-            ManagedProfileController managedProfileController,
-            NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
-            CastController castController,
-            ReduceBrightColorsController reduceBrightColorsController,
-            DeviceControlsController deviceControlsController,
-            WalletController walletController,
-            SafetyController safetyController,
-            @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
-        AutoTileManager manager = new AutoTileManager(
-                context,
-                autoAddTrackerBuilder,
-                host,
-                handler,
-                secureSettings,
-                hotspotController,
-                dataSaverController,
-                managedProfileController,
-                nightDisplayListenerBuilder,
-                castController,
-                reduceBrightColorsController,
-                deviceControlsController,
-                walletController,
-                safetyController,
-                isReduceBrightColorsAvailable
-        );
-        manager.init();
-        return manager;
-    }
-
     @Binds
     QSSceneAdapter bindsQsSceneInteractor(QSSceneAdapterImpl impl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
index 6cf4441..28e4fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
@@ -26,6 +26,8 @@
 import android.content.pm.ServiceInfo;
 import android.os.RemoteException;
 
+import androidx.annotation.Nullable;
+
 import javax.inject.Inject;
 
 // Adapter that wraps calls to PackageManager or IPackageManager for {@link TileLifecycleManager}.
@@ -45,6 +47,7 @@
         mIPackageManager = AppGlobals.getPackageManager();
     }
 
+    @Nullable
     public ServiceInfo getServiceInfo(ComponentName className, int flags, int userId)
             throws RemoteException {
         return mIPackageManager.getServiceInfo(className, flags, userId);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 96df728..cbcf68c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -188,10 +188,10 @@
     public boolean isActiveTile() {
         try {
             ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
-                    META_DATA_QUERY_FLAGS);
-            return info.metaData != null
+                    META_DATA_QUERY_FLAGS, mUser.getIdentifier());
+            return info != null && info.metaData != null
                     && info.metaData.getBoolean(TileService.META_DATA_ACTIVE_TILE, false);
-        } catch (PackageManager.NameNotFoundException e) {
+        } catch (RemoteException e) {
             return false;
         }
     }
@@ -206,10 +206,10 @@
     public boolean isToggleableTile() {
         try {
             ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
-                    META_DATA_QUERY_FLAGS);
-            return info.metaData != null
+                    META_DATA_QUERY_FLAGS, mUser.getIdentifier());
+            return info != null && info.metaData != null
                     && info.metaData.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false);
-        } catch (PackageManager.NameNotFoundException e) {
+        } catch (RemoteException e) {
             return false;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index ba45d17..b0d4fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -21,6 +21,7 @@
 import android.view.ContextThemeWrapper
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleCoroutineScope
 import androidx.lifecycle.LifecycleOwner
 import com.android.settingslib.Utils
 import com.android.systemui.animation.Expandable
@@ -41,6 +42,9 @@
 import javax.inject.Named
 import javax.inject.Provider
 import kotlin.math.max
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -48,6 +52,8 @@
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
 
 private const val TAG = "FooterActionsViewModel"
 
@@ -140,6 +146,31 @@
                 showPowerButton,
             )
         }
+
+        @OptIn(ExperimentalCoroutinesApi::class)
+        fun create(lifecycleCoroutineScope: LifecycleCoroutineScope): FooterActionsViewModel {
+            val globalActionsDialogLite = globalActionsDialogLiteProvider.get()
+            if (lifecycleCoroutineScope.isActive) {
+                lifecycleCoroutineScope.launch(start = CoroutineStart.ATOMIC) {
+                    try {
+                        awaitCancellation()
+                    } finally {
+                        globalActionsDialogLite.destroy()
+                    }
+                }
+            } else {
+                globalActionsDialogLite.destroy()
+            }
+
+            return FooterActionsViewModel(
+                context,
+                footerActionsInteractor,
+                falsingManager,
+                globalActionsDialogLite,
+                activityStarter,
+                showPowerButton,
+            )
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index 6dcdea9..02a607d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -22,10 +22,12 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
 import com.android.systemui.qs.panels.shared.model.PanelsLog
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
@@ -35,19 +37,38 @@
 @Inject
 constructor(
     repo: DefaultLargeTilesRepository,
+    private val currentTilesInteractor: CurrentTilesInteractor,
     private val preferencesInteractor: QSPreferencesInteractor,
     @PanelsLog private val logBuffer: LogBuffer,
     @Application private val applicationScope: CoroutineScope
 ) {
 
     val largeTilesSpecs =
-        preferencesInteractor.largeTilesSpecs
+        combine(preferencesInteractor.largeTilesSpecs, currentTilesInteractor.currentTiles) {
+                largeTiles,
+                currentTiles ->
+                if (currentTiles.isEmpty()) {
+                    largeTiles
+                } else {
+                    // Only current tiles can be resized, so observe the current tiles and find the
+                    // intersection between them and the large tiles.
+                    val newLargeTiles = largeTiles intersect currentTiles.map { it.spec }.toSet()
+                    if (newLargeTiles != largeTiles) {
+                        preferencesInteractor.setLargeTilesSpecs(newLargeTiles)
+                    }
+                    newLargeTiles
+                }
+            }
             .onEach { logChange(it) }
             .stateIn(applicationScope, SharingStarted.Eagerly, repo.defaultLargeTiles)
 
     fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec)
 
     fun resize(spec: TileSpec) {
+        if (!isCurrent(spec)) {
+            return
+        }
+
         if (largeTilesSpecs.value.contains(spec)) {
             preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value - spec)
         } else {
@@ -55,6 +76,10 @@
         }
     }
 
+    private fun isCurrent(spec: TileSpec): Boolean {
+        return currentTilesInteractor.currentTilesSpecs.contains(spec)
+    }
+
     private fun logChange(specs: Set<TileSpec>) {
         logBuffer.log(
             LOG_BUFFER_LARGE_TILES_SPECS_CHANGE_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index 2c57813..9a2315b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -20,103 +20,40 @@
 import androidx.compose.foundation.draganddrop.dragAndDropSource
 import androidx.compose.foundation.draganddrop.dragAndDropTarget
 import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.lazy.grid.LazyGridItemInfo
+import androidx.compose.foundation.lazy.grid.LazyGridState
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
-import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draganddrop.DragAndDropEvent
 import androidx.compose.ui.draganddrop.DragAndDropTarget
 import androidx.compose.ui.draganddrop.DragAndDropTransferData
 import androidx.compose.ui.draganddrop.mimeTypes
+import androidx.compose.ui.draganddrop.toAndroidDragEvent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.IntRect
+import androidx.compose.ui.unit.center
+import androidx.compose.ui.unit.toRect
 import com.android.systemui.qs.panels.shared.model.SizedTile
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
-@Composable
-fun rememberDragAndDropState(listState: EditTileListState): DragAndDropState {
-    val draggedCell: MutableState<SizedTile<EditTileViewModel>?> = remember { mutableStateOf(null) }
-    return remember(listState) { DragAndDropState(draggedCell, listState) }
-}
-
-/**
- * Holds the [TileSpec] of the tile being moved and modify the [EditTileListState] based on drag and
- * drop events.
- */
-class DragAndDropState(
-    val draggedCell: MutableState<SizedTile<EditTileViewModel>?>,
-    private val listState: EditTileListState,
-) {
+/** Holds the [TileSpec] of the tile being moved and receives drag and drop events. */
+interface DragAndDropState {
+    val draggedCell: SizedTile<EditTileViewModel>?
     val dragInProgress: Boolean
-        get() = draggedCell.value != null
 
-    /** Returns index of the dragged tile if it's present in the list. Returns -1 if not. */
-    fun currentPosition(): Int {
-        return draggedCell.value?.let { listState.indexOf(it.tile.tileSpec) } ?: -1
-    }
+    fun isMoving(tileSpec: TileSpec): Boolean
 
-    fun isMoving(tileSpec: TileSpec): Boolean {
-        return draggedCell.value?.let { it.tile.tileSpec == tileSpec } ?: false
-    }
+    fun onStarted(cell: SizedTile<EditTileViewModel>)
 
-    fun onStarted(cell: SizedTile<EditTileViewModel>) {
-        draggedCell.value = cell
-    }
+    fun onMoved(target: Int, insertAfter: Boolean)
 
-    fun onMoved(targetSpec: TileSpec) {
-        draggedCell.value?.let { listState.move(it, targetSpec) }
-    }
+    fun movedOutOfBounds()
 
-    fun movedOutOfBounds() {
-        // Removing the tiles from the current tile grid if it moves out of bounds. This clears
-        // the spacer and makes it apparent that dropping the tile at that point would remove it.
-        draggedCell.value?.let { listState.remove(it.tile.tileSpec) }
-    }
-
-    fun onDrop() {
-        draggedCell.value = null
-    }
-}
-
-/**
- * Registers a tile as a [DragAndDropTarget] to receive drag events and update the
- * [DragAndDropState] with the tile's position, which can be used to insert a temporary placeholder.
- *
- * @param dragAndDropState The [DragAndDropState] using the tiles list
- * @param tileSpec The [TileSpec] of the tile
- * @param acceptDrops Whether the tile should accept a drop based on a given [TileSpec]
- * @param onDrop Action to be executed when a [TileSpec] is dropped on the tile
- */
-@Composable
-fun Modifier.dragAndDropTile(
-    dragAndDropState: DragAndDropState,
-    tileSpec: TileSpec,
-    acceptDrops: (TileSpec) -> Boolean,
-    onDrop: (TileSpec, Int) -> Unit,
-): Modifier {
-    val target =
-        remember(dragAndDropState) {
-            object : DragAndDropTarget {
-                override fun onDrop(event: DragAndDropEvent): Boolean {
-                    return dragAndDropState.draggedCell.value?.let {
-                        onDrop(it.tile.tileSpec, dragAndDropState.currentPosition())
-                        dragAndDropState.onDrop()
-                        true
-                    } ?: false
-                }
-
-                override fun onEntered(event: DragAndDropEvent) {
-                    dragAndDropState.onMoved(tileSpec)
-                }
-            }
-        }
-    return dragAndDropTarget(
-        shouldStartDragAndDrop = { event ->
-            event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
-                dragAndDropState.draggedCell.value?.let { acceptDrops(it.tile.tileSpec) } ?: false
-        },
-        target = target,
-    )
+    fun onDrop()
 }
 
 /**
@@ -135,7 +72,7 @@
         remember(dragAndDropState) {
             object : DragAndDropTarget {
                 override fun onDrop(event: DragAndDropEvent): Boolean {
-                    return dragAndDropState.draggedCell.value?.let {
+                    return dragAndDropState.draggedCell?.let {
                         onDrop(it.tile.tileSpec)
                         dragAndDropState.onDrop()
                         true
@@ -156,19 +93,22 @@
 }
 
 /**
- * Registers a tile list as a [DragAndDropTarget] to receive drop events. Use this on list
- * containers to catch drops outside of tiles.
+ * Registers a tile list as a [DragAndDropTarget] to receive drop events. Use this on the lazy tile
+ * grid to receive drag and drops events.
  *
+ * @param gridState The [LazyGridState] of the tile list
+ * @param contentOffset The [Offset] of the tile list
  * @param dragAndDropState The [DragAndDropState] using the tiles list
- * @param acceptDrops Whether the tile should accept a drop based on a given [TileSpec]
- * @param onDrop Action to be executed when a [TileSpec] is dropped on the tile
+ * @param onDrop Callback when a tile is dropped
  */
 @Composable
 fun Modifier.dragAndDropTileList(
+    gridState: LazyGridState,
+    contentOffset: Offset,
     dragAndDropState: DragAndDropState,
-    acceptDrops: (TileSpec) -> Boolean,
-    onDrop: (TileSpec, Int) -> Unit,
+    onDrop: () -> Unit,
 ): Modifier {
+    val currentContentOffset by rememberUpdatedState(contentOffset)
     val target =
         remember(dragAndDropState) {
             object : DragAndDropTarget {
@@ -176,9 +116,23 @@
                     dragAndDropState.onDrop()
                 }
 
+                override fun onMoved(event: DragAndDropEvent) {
+                    // Drag offset relative to the list's top left corner
+                    val relativeDragOffset = event.dragOffsetRelativeTo(currentContentOffset)
+                    val targetItem =
+                        gridState.layoutInfo.visibleItemsInfo.firstOrNull { item ->
+                            // Check if the drag is on this item
+                            IntRect(item.offset, item.size).toRect().contains(relativeDragOffset)
+                        }
+
+                    targetItem?.let {
+                        dragAndDropState.onMoved(it.index, insertAfter(it, relativeDragOffset))
+                    }
+                }
+
                 override fun onDrop(event: DragAndDropEvent): Boolean {
-                    return dragAndDropState.draggedCell.value?.let {
-                        onDrop(it.tile.tileSpec, dragAndDropState.currentPosition())
+                    return dragAndDropState.draggedCell?.let {
+                        onDrop()
                         dragAndDropState.onDrop()
                         true
                     } ?: false
@@ -188,24 +142,36 @@
     return dragAndDropTarget(
         target = target,
         shouldStartDragAndDrop = { event ->
-            event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) &&
-                dragAndDropState.draggedCell.value?.let { acceptDrops(it.tile.tileSpec) } ?: false
+            event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE)
         },
     )
 }
 
+private fun DragAndDropEvent.dragOffsetRelativeTo(offset: Offset): Offset {
+    return toAndroidDragEvent().run { Offset(x, y) } - offset
+}
+
+private fun insertAfter(item: LazyGridItemInfo, offset: Offset): Boolean {
+    // We want to insert the tile after the target if we're aiming at the right side of a large tile
+    // TODO(ostonge): Verify this behavior in RTL
+    val itemCenter = item.offset + item.size.center
+    return item.span != 1 && offset.x > itemCenter.x
+}
+
+@Composable
 fun Modifier.dragAndDropTileSource(
     sizedTile: SizedTile<EditTileViewModel>,
     onTap: (TileSpec) -> Unit,
     onDoubleTap: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState
 ): Modifier {
+    val state by rememberUpdatedState(dragAndDropState)
     return dragAndDropSource {
         detectTapGestures(
             onTap = { onTap(sizedTile.tile.tileSpec) },
             onDoubleTap = { onDoubleTap(sizedTile.tile.tileSpec) },
             onLongPress = {
-                dragAndDropState.onStarted(sizedTile)
+                state.onStarted(sizedTile)
 
                 // The tilespec from the ClipData transferred isn't actually needed as we're moving
                 // a tile within the same application. We're using a custom MIME type to limit the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
index 3bda775..1674865 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
@@ -43,6 +43,7 @@
             Modifier,
             viewModel::addTile,
             viewModel::removeTile,
+            viewModel::setTiles,
         )
     }
 }
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 fa3008e..4830ba7 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
@@ -17,46 +17,106 @@
 package com.android.systemui.qs.panels.ui.compose
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.runtime.toMutableStateList
 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.model.toGridCells
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
 
+/**
+ * Creates the edit tile list state that is remembered across compositions.
+ *
+ * Changes to the tiles or columns will recreate the state.
+ */
 @Composable
 fun rememberEditListState(
     tiles: List<SizedTile<EditTileViewModel>>,
+    columns: Int,
 ): EditTileListState {
-    return remember(tiles) { EditTileListState(tiles) }
+    return remember(tiles, columns) { EditTileListState(tiles, columns) }
 }
 
 /** Holds the temporary state of the tile list during a drag movement where we move tiles around. */
-class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>) {
-    val tiles: SnapshotStateList<SizedTile<EditTileViewModel>> = tiles.toMutableStateList()
+class EditTileListState(
+    tiles: List<SizedTile<EditTileViewModel>>,
+    private val columns: Int,
+) : DragAndDropState {
+    private val _draggedCell = mutableStateOf<SizedTile<EditTileViewModel>?>(null)
+    override val draggedCell
+        get() = _draggedCell.value
 
-    fun move(sizedTile: SizedTile<EditTileViewModel>, target: TileSpec) {
-        val fromIndex = indexOf(sizedTile.tile.tileSpec)
-        val toIndex = indexOf(target)
+    override val dragInProgress: Boolean
+        get() = _draggedCell.value != null
 
-        if (toIndex == -1 || fromIndex == toIndex) {
-            return
-        }
+    private val _tiles: SnapshotStateList<GridCell> =
+        tiles.toGridCells(columns).toMutableStateList()
+    val tiles: List<GridCell>
+        get() = _tiles.toList()
 
-        if (fromIndex == -1) {
-            // If tile isn't in the list, simply insert it
-            tiles.add(toIndex, sizedTile)
-        } else {
-            // If tile is present in the list, move it
-            tiles.apply { add(toIndex, removeAt(fromIndex)) }
-        }
-    }
-
-    fun remove(tileSpec: TileSpec) {
-        tiles.removeIf { it.tile.tileSpec == tileSpec }
+    fun tileSpecs(): List<TileSpec> {
+        return _tiles.filterIsInstance<TileGridCell>().map { it.tile.tileSpec }
     }
 
     fun indexOf(tileSpec: TileSpec): Int {
-        return tiles.indexOfFirst { it.tile.tileSpec == tileSpec }
+        return _tiles.indexOfFirst { it is TileGridCell && it.tile.tileSpec == tileSpec }
+    }
+
+    override fun isMoving(tileSpec: TileSpec): Boolean {
+        return _draggedCell.value?.let { it.tile.tileSpec == tileSpec } ?: false
+    }
+
+    override fun onStarted(cell: SizedTile<EditTileViewModel>) {
+        _draggedCell.value = cell
+
+        // Add visible spacers to the grid to indicate where the user can move a tile
+        regenerateGrid(includeSpacers = true)
+    }
+
+    override fun onMoved(target: Int, insertAfter: Boolean) {
+        val draggedTile = _draggedCell.value ?: return
+
+        val fromIndex = indexOf(draggedTile.tile.tileSpec)
+        if (fromIndex == target) {
+            return
+        }
+
+        val insertionIndex = if (insertAfter) target + 1 else target
+        if (fromIndex != -1) {
+            val cell = _tiles.removeAt(fromIndex)
+            regenerateGrid(includeSpacers = true)
+            _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
+        } else {
+            // Add the tile with a temporary row which will get reassigned when regenerating spacers
+            _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0))
+        }
+
+        regenerateGrid(includeSpacers = true)
+    }
+
+    override fun movedOutOfBounds() {
+        val draggedTile = _draggedCell.value ?: return
+
+        _tiles.removeIf { cell ->
+            cell is TileGridCell && cell.tile.tileSpec == draggedTile.tile.tileSpec
+        }
+    }
+
+    override fun onDrop() {
+        _draggedCell.value = null
+
+        // Remove the spacers
+        regenerateGrid(includeSpacers = false)
+    }
+
+    private fun regenerateGrid(includeSpacers: Boolean) {
+        _tiles.filterIsInstance<TileGridCell>().toGridCells(columns, includeSpacers).let {
+            _tiles.clear()
+            _tiles.addAll(it)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
index e2f6bcf..fd276c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/GridLayout.kt
@@ -39,6 +39,7 @@
         modifier: Modifier,
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit,
+        onSetTiles: (List<TileSpec>) -> Unit,
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index bd925fe..c75b601 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.runtime.Composable
@@ -24,7 +23,6 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.panels.shared.model.SizedTileImpl
@@ -33,7 +31,6 @@
 import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.res.R
 import javax.inject.Inject
 
 @SysUISingleton
@@ -64,7 +61,7 @@
                 Tile(
                     tile = sizedTiles[index].tile,
                     iconOnly = iconTilesViewModel.isIconTile(sizedTiles[index].tile.spec),
-                    modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+                    modifier = Modifier
                 )
             }
         }
@@ -76,6 +73,7 @@
         modifier: Modifier,
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit,
+        onSetTiles: (List<TileSpec>) -> Unit,
     ) {
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
         val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
@@ -91,12 +89,16 @@
                 }
             }
 
+        val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent }
+        val currentListState = rememberEditListState(currentTiles, columns)
         DefaultEditTileGrid(
-            sizedTiles = sizedTiles,
+            currentListState = currentListState,
+            otherTiles = otherTiles,
             columns = columns,
             modifier = modifier,
             onAddTile = onAddTile,
             onRemoveTile = onRemoveTile,
+            onSetTiles = onSetTiles,
             onResize = iconTilesViewModel::resize,
         )
     }
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 2ee957e..08a56bf 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
@@ -39,6 +39,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.panels.dagger.PaginatedBaseLayoutType
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.FooterHeight
 import com.android.systemui.qs.panels.ui.compose.PaginatedGridLayout.Dimensions.InterPageSpacing
@@ -77,7 +78,7 @@
         Column {
             HorizontalPager(
                 state = pagerState,
-                modifier = Modifier,
+                modifier = Modifier.sysuiResTag("qs_pager"),
                 pageSpacing = if (pages.size > 1) InterPageSpacing else 0.dp,
                 beyondViewportPageCount = 1,
                 verticalAlignment = Alignment.Top,
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 af3803b..eeb55ca 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
@@ -16,17 +16,15 @@
 
 package com.android.systemui.qs.panels.ui.compose
 
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.lazy.grid.GridCells
 import androidx.compose.foundation.lazy.grid.GridItemSpan
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.dimensionResource
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
-import com.android.systemui.res.R
 
 @Composable
 fun QuickQuickSettings(
@@ -44,17 +42,16 @@
     }
     val columns by viewModel.columns.collectAsStateWithLifecycle()
 
-    TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
+    TileLazyGrid(
+        modifier = modifier.sysuiResTag("qqs_tile_layout"),
+        columns = GridCells.Fixed(columns)
+    ) {
         items(
             tiles.size,
             key = { index -> sizedTiles[index].tile.spec.spec },
             span = { index -> GridItemSpan(sizedTiles[index].width) }
         ) { index ->
-            Tile(
-                tile = tiles[index],
-                iconOnly = sizedTiles[index].isIcon,
-                modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
-            )
+            Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 7e6ccd6..24af09d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -22,9 +22,9 @@
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.service.quicksettings.Tile.STATE_INACTIVE
 import android.text.TextUtils
-import androidx.appcompat.content.res.AppCompatResources
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.animateDpAsState
 import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
@@ -44,7 +44,6 @@
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -53,8 +52,11 @@
 import androidx.compose.foundation.layout.size
 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.LazyGridState
 import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.foundation.shape.RoundedCornerShape
@@ -76,9 +78,14 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.layout.positionInRoot
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.onClick
@@ -90,6 +97,7 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.background
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
@@ -97,9 +105,12 @@
 import com.android.systemui.common.ui.compose.load
 import com.android.systemui.plugins.qs.QSTile
 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.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.model.toTileGridCells
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
 import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
 import com.android.systemui.qs.panels.ui.viewmodel.toUiState
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -120,30 +131,40 @@
 ) {
     val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
     val uiState = remember(state) { state.toUiState() }
-    val colors = TileDefaults.getColorForState(uiState.state)
+    val colors = TileDefaults.getColorForState(uiState)
+
+    // TODO(b/361789146): Draw the shapes instead of clipping
+    val tileShape = TileDefaults.animateTileShape(uiState.state)
 
     TileContainer(
         colors = colors,
         showLabels = showLabels,
         label = uiState.label,
         iconOnly = iconOnly,
+        shape = tileShape,
         clickEnabled = true,
         onClick = tile::onClick,
         onLongClick = tile::onLongClick,
-        modifier = modifier,
+        modifier = modifier.height(tileHeight()),
     ) {
         val icon = getTileIcon(icon = uiState.icon)
         if (iconOnly) {
             TileIcon(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,
-                clickEnabled = true,
-                onClick = tile::onSecondaryClick,
-                onLongClick = tile::onLongClick,
+                iconShape = iconShape,
+                toggleClickSupported = state.handlesSecondaryClick,
+                onClick = {
+                    if (state.handlesSecondaryClick) {
+                        tile.onSecondaryClick()
+                    }
+                },
+                onLongClick = { tile.onLongClick(it) },
             )
         }
     }
@@ -155,11 +176,12 @@
     showLabels: Boolean,
     label: String,
     iconOnly: Boolean,
+    shape: Shape,
     clickEnabled: Boolean = false,
     onClick: (Expandable) -> Unit = {},
     onLongClick: (Expandable) -> Unit = {},
     modifier: Modifier = Modifier,
-    content: @Composable BoxScope.() -> Unit,
+    content: @Composable BoxScope.(Expandable) -> Unit,
 ) {
     Column(
         horizontalAlignment = Alignment.CenterHorizontally,
@@ -175,10 +197,8 @@
             }
         Expandable(
             color = backgroundColor,
-            shape = TileDefaults.TileShape,
-            modifier =
-                Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
-                    .clip(TileDefaults.TileShape)
+            shape = shape,
+            modifier = Modifier.height(tileHeight()).clip(shape)
         ) {
             Box(
                 modifier =
@@ -191,7 +211,7 @@
                         }
                         .tilePadding(),
             ) {
-                content()
+                content(it)
             }
         }
 
@@ -213,36 +233,28 @@
     secondaryLabel: String?,
     icon: Icon,
     colors: TileColors,
-    clickEnabled: Boolean = false,
-    onClick: (Expandable) -> Unit = {},
-    onLongClick: (Expandable) -> Unit = {},
+    iconShape: Shape,
+    toggleClickSupported: Boolean = false,
+    onClick: () -> Unit = {},
+    onLongClick: () -> Unit = {},
 ) {
     Row(
         verticalAlignment = Alignment.CenterVertically,
         horizontalArrangement = tileHorizontalArrangement()
     ) {
-        Expandable(
-            color = colors.iconBackground,
-            shape = TileDefaults.TileShape,
-            modifier = Modifier.fillMaxHeight().aspectRatio(1f)
+        // Icon
+        Box(
+            modifier =
+                Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) {
+                    Modifier.clip(iconShape)
+                        .background(colors.iconBackground, { 1f })
+                        .combinedClickable(onClick = onClick, onLongClick = onLongClick)
+                }
         ) {
-            Box(
-                modifier =
-                    Modifier.fillMaxSize().clip(TileDefaults.TileShape).thenIf(clickEnabled) {
-                        Modifier.combinedClickable(
-                            onClick = { onClick(it) },
-                            onLongClick = { onLongClick(it) }
-                        )
-                    }
-            ) {
-                TileIcon(
-                    icon = icon,
-                    color = colors.icon,
-                    modifier = Modifier.align(Alignment.Center)
-                )
-            }
+            TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
         }
 
+        // Labels
         Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
             Text(
                 label,
@@ -270,10 +282,12 @@
 @Composable
 fun TileLazyGrid(
     modifier: Modifier = Modifier,
+    state: LazyGridState = rememberLazyGridState(),
     columns: GridCells,
     content: LazyGridScope.() -> Unit,
 ) {
     LazyVerticalGrid(
+        state = state,
         columns = columns,
         verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
         horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
@@ -284,23 +298,18 @@
 
 @Composable
 fun DefaultEditTileGrid(
-    sizedTiles: List<SizedTile<EditTileViewModel>>,
+    currentListState: EditTileListState,
+    otherTiles: List<SizedTile<EditTileViewModel>>,
     columns: Int,
     modifier: Modifier,
     onAddTile: (TileSpec, Int) -> Unit,
     onRemoveTile: (TileSpec) -> Unit,
+    onSetTiles: (List<TileSpec>) -> Unit,
     onResize: (TileSpec) -> Unit,
 ) {
-    val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent }
-    val currentListState = rememberEditListState(currentTiles)
-    val dragAndDropState = rememberDragAndDropState(currentListState)
-
     val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
         onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
     }
-    val onDropAdd: (TileSpec, Int) -> Unit by rememberUpdatedState { tileSpec, position ->
-        onAddTile(tileSpec, position)
-    }
     val tilePadding = dimensionResource(R.dimen.qs_tile_margin_vertical)
 
     CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
@@ -310,10 +319,10 @@
             modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState())
         ) {
             AnimatedContent(
-                targetState = dragAndDropState.dragInProgress,
+                targetState = currentListState.dragInProgress,
                 modifier = Modifier.wrapContentSize()
             ) { dragIsInProgress ->
-                EditGridHeader(Modifier.dragAndDropRemoveZone(dragAndDropState, onRemoveTile)) {
+                EditGridHeader(Modifier.dragAndDropRemoveZone(currentListState, onRemoveTile)) {
                     if (dragIsInProgress) {
                         RemoveTileTarget()
                     } else {
@@ -323,18 +332,17 @@
             }
 
             CurrentTilesGrid(
-                currentListState.tiles,
+                currentListState,
                 columns,
                 tilePadding,
                 onRemoveTile,
                 onResize,
-                dragAndDropState,
-                onDropAdd,
+                onSetTiles,
             )
 
             // Hide available tiles when dragging
             AnimatedVisibility(
-                visible = !dragAndDropState.dragInProgress,
+                visible = !currentListState.dragInProgress,
                 enter = fadeIn(),
                 exit = fadeOut()
             ) {
@@ -350,7 +358,7 @@
                         columns,
                         tilePadding,
                         addTileToEnd,
-                        dragAndDropState,
+                        currentListState,
                     )
                 }
             }
@@ -360,7 +368,7 @@
                 modifier =
                     Modifier.fillMaxWidth()
                         .weight(1f)
-                        .dragAndDropRemoveZone(dragAndDropState, onRemoveTile)
+                        .dragAndDropRemoveZone(currentListState, onRemoveTile)
             )
         }
     }
@@ -376,7 +384,7 @@
     ) {
         Box(
             contentAlignment = Alignment.Center,
-            modifier = modifier.fillMaxWidth().height(TileDefaults.EditGridHeaderHeight)
+            modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight)
         ) {
             content()
         }
@@ -390,7 +398,7 @@
         horizontalArrangement = tileHorizontalArrangement(),
         modifier =
             Modifier.fillMaxHeight()
-                .border(1.dp, LocalContentColor.current, shape = TileDefaults.TileShape)
+                .border(1.dp, LocalContentColor.current, shape = CircleShape)
                 .padding(10.dp)
     ) {
         Icon(imageVector = Icons.Default.Clear, contentDescription = null)
@@ -415,35 +423,42 @@
 
 @Composable
 private fun CurrentTilesGrid(
-    tiles: List<SizedTile<EditTileViewModel>>,
+    listState: EditTileListState,
     columns: Int,
     tilePadding: Dp,
     onClick: (TileSpec) -> Unit,
     onResize: (TileSpec) -> Unit,
-    dragAndDropState: DragAndDropState,
-    onDrop: (TileSpec, Int) -> Unit
+    onSetTiles: (List<TileSpec>) -> Unit,
 ) {
-    // Current tiles
+    val currentListState by rememberUpdatedState(listState)
+
     CurrentTilesContainer {
-        val cells = tiles.toTileGridCells(columns)
         val tileHeight = tileHeight()
-        val totalRows = cells.lastOrNull()?.row ?: 0
+        val totalRows = listState.tiles.lastOrNull()?.row ?: 0
         val totalHeight = gridHeight(totalRows + 1, tileHeight, tilePadding)
+        val gridState = rememberLazyGridState()
+        var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) }
+
         TileLazyGrid(
+            state = gridState,
             modifier =
                 Modifier.height(totalHeight)
-                    .dragAndDropTileList(dragAndDropState, { true }, onDrop),
+                    .dragAndDropTileList(gridState, gridContentOffset, listState) {
+                        onSetTiles(currentListState.tileSpecs())
+                    }
+                    .onGloballyPositioned { coordinates ->
+                        gridContentOffset = coordinates.positionInRoot()
+                    }
+                    .testTag(CURRENT_TILES_GRID_TEST_TAG),
             columns = GridCells.Fixed(columns)
         ) {
             editTiles(
-                cells,
+                listState.tiles,
                 ClickAction.REMOVE,
                 onClick,
-                dragAndDropState,
+                listState,
                 onResize = onResize,
                 indicatePosition = true,
-                acceptDrops = { true },
-                onDrop = onDrop,
             )
         }
     }
@@ -465,7 +480,7 @@
 
     // Available tiles
     TileLazyGrid(
-        modifier = Modifier.height(availableGridHeight),
+        modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG),
         columns = GridCells.Fixed(columns)
     ) {
         editTiles(
@@ -473,7 +488,6 @@
             ClickAction.ADD,
             onClick,
             dragAndDropState = dragAndDropState,
-            acceptDrops = { false },
             showLabels = true,
         )
         editTiles(
@@ -481,7 +495,6 @@
             ClickAction.ADD,
             onClick,
             dragAndDropState = dragAndDropState,
-            acceptDrops = { false },
             showLabels = true,
         )
     }
@@ -496,64 +509,109 @@
     return ((tileHeight + padding) * rows) - padding
 }
 
+private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any {
+    return if (this is TileGridCell && !dragAndDropState.isMoving(tile.tileSpec)) {
+        key
+    } else {
+        index
+    }
+}
+
 fun LazyGridScope.editTiles(
-    cells: List<TileGridCell>,
+    cells: List<GridCell>,
     clickAction: ClickAction,
     onClick: (TileSpec) -> Unit,
     dragAndDropState: DragAndDropState,
-    acceptDrops: (TileSpec) -> Boolean,
     onResize: (TileSpec) -> Unit = {},
-    onDrop: (TileSpec, Int) -> Unit = { _, _ -> },
     showLabels: Boolean = false,
     indicatePosition: Boolean = false,
 ) {
     items(
         count = cells.size,
-        key = { cells[it].key },
+        key = { cells[it].key(it, dragAndDropState) },
         span = { cells[it].span },
         contentType = { TileType }
     ) { index ->
-        val cell = cells[index]
-        val tileHeight = tileHeight(cell.isIcon && showLabels)
-
-        if (!dragAndDropState.isMoving(cell.tile.tileSpec)) {
-            val onClickActionName =
-                when (clickAction) {
-                    ClickAction.ADD ->
-                        stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
-                    ClickAction.REMOVE ->
-                        stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
-                }
-            val stateDescription =
-                if (indicatePosition) {
-                    stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
+        when (val cell = cells[index]) {
+            is TileGridCell ->
+                if (dragAndDropState.isMoving(cell.tile.tileSpec)) {
+                    // If the tile is being moved, replace it with a visible spacer
+                    SpacerGridCell(
+                        Modifier.background(
+                                color = MaterialTheme.colorScheme.secondary,
+                                alpha = { EditModeTileDefaults.PLACEHOLDER_ALPHA },
+                                shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius)
+                            )
+                            .animateItem()
+                    )
                 } else {
-                    ""
+                    TileGridCell(
+                        cell = cell,
+                        index = index,
+                        dragAndDropState = dragAndDropState,
+                        clickAction = clickAction,
+                        onClick = onClick,
+                        onResize = onResize,
+                        showLabels = showLabels,
+                        indicatePosition = indicatePosition
+                    )
                 }
-            EditTile(
-                tileViewModel = cell.tile,
-                iconOnly = cell.isIcon,
-                showLabels = showLabels,
-                modifier =
-                    Modifier.height(tileHeight)
-                        .animateItem()
-                        .semantics {
-                            onClick(onClickActionName) { false }
-                            this.stateDescription = stateDescription
-                        }
-                        .dragAndDropTile(dragAndDropState, cell.tile.tileSpec, acceptDrops, onDrop)
-                        .dragAndDropTileSource(
-                            cell,
-                            onClick,
-                            onResize,
-                            dragAndDropState,
-                        )
-            )
+            is SpacerGridCell -> SpacerGridCell()
         }
     }
 }
 
 @Composable
+private fun LazyGridItemScope.TileGridCell(
+    cell: TileGridCell,
+    index: Int,
+    dragAndDropState: DragAndDropState,
+    clickAction: ClickAction,
+    onClick: (TileSpec) -> Unit,
+    onResize: (TileSpec) -> Unit = {},
+    showLabels: Boolean = false,
+    indicatePosition: Boolean = false,
+) {
+    val tileHeight = tileHeight(cell.isIcon && showLabels)
+    val onClickActionName =
+        when (clickAction) {
+            ClickAction.ADD -> stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
+            ClickAction.REMOVE ->
+                stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
+        }
+    val stateDescription =
+        if (indicatePosition) {
+            stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
+        } else {
+            ""
+        }
+    EditTile(
+        tileViewModel = cell.tile,
+        iconOnly = cell.isIcon,
+        showLabels = showLabels,
+        modifier =
+            Modifier.height(tileHeight)
+                .animateItem()
+                .semantics {
+                    onClick(onClickActionName) { false }
+                    this.stateDescription = stateDescription
+                }
+                .dragAndDropTileSource(
+                    SizedTileImpl(cell.tile, cell.width),
+                    onClick,
+                    onResize,
+                    dragAndDropState,
+                )
+    )
+}
+
+@Composable
+private fun SpacerGridCell(modifier: Modifier = Modifier) {
+    // By default, spacers are invisible and exist purely to catch drag movements
+    Box(modifier.height(tileHeight()).fillMaxWidth().tilePadding())
+}
+
+@Composable
 fun EditTile(
     tileViewModel: EditTileViewModel,
     iconOnly: Boolean,
@@ -568,6 +626,7 @@
         showLabels = showLabels,
         label = label,
         iconOnly = iconOnly,
+        shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
         modifier = modifier,
     ) {
         if (iconOnly) {
@@ -582,6 +641,7 @@
                 secondaryLabel = tileViewModel.appName?.load(),
                 icon = tileViewModel.icon,
                 colors = colors,
+                iconShape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
             )
         }
     }
@@ -593,15 +653,15 @@
 }
 
 @Composable
-private fun getTileIcon(icon: Supplier<QSTile.Icon>): Icon {
+private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon {
     val context = LocalContext.current
-    return icon.get().let {
+    return icon.get()?.let {
         if (it is QSTileImpl.ResourceIcon) {
             Icon.Resource(it.resId, null)
         } else {
             Icon.Loaded(it.getDrawable(context), null)
         }
-    }
+    } ?: Icon.Resource(R.drawable.ic_error_outline, null)
 }
 
 @OptIn(ExperimentalAnimationGraphicsApi::class)
@@ -612,13 +672,13 @@
     animateToEnd: Boolean = false,
     modifier: Modifier = Modifier,
 ) {
-    val iconModifier = modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+    val iconModifier = modifier.size(TileDefaults.IconSize)
     val context = LocalContext.current
     val loadedDrawable =
         remember(icon, context) {
             when (icon) {
                 is Icon.Loaded -> icon.drawable
-                is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
+                is Icon.Resource -> context.getDrawable(icon.res)
             }
         }
     if (loadedDrawable !is Animatable) {
@@ -642,24 +702,19 @@
             }
         Image(
             painter = painter,
-            contentDescription = null,
+            contentDescription = icon.contentDescription?.load(),
             colorFilter = ColorFilter.tint(color = color),
             modifier = iconModifier
         )
     }
 }
 
-@Composable
 private fun Modifier.tilePadding(): Modifier {
-    return padding(dimensionResource(id = R.dimen.qs_label_container_margin))
+    return padding(TileDefaults.TilePadding)
 }
 
-@Composable
 private fun tileHorizontalArrangement(): Arrangement.Horizontal {
-    return spacedBy(
-        space = dimensionResource(id = R.dimen.qs_label_container_margin),
-        alignment = Alignment.Start
-    )
+    return spacedBy(space = TileDefaults.TileArrangementPadding, alignment = Alignment.Start)
 }
 
 @Composable
@@ -667,7 +722,7 @@
     return if (iconWithLabel) {
         TileDefaults.IconTileWithLabelHeight
     } else {
-        dimensionResource(id = R.dimen.qs_tile_height)
+        TileDefaults.TileHeight
     }
 }
 
@@ -679,14 +734,40 @@
     val icon: Color,
 )
 
-private object TileDefaults {
-    val TileShape = CircleShape
-    val IconTileWithLabelHeight = 140.dp
+private object EditModeTileDefaults {
+    const val PLACEHOLDER_ALPHA = .3f
     val EditGridHeaderHeight = 60.dp
+}
 
+private object TileDefaults {
+    val InactiveCornerRadius = 50.dp
+    val ActiveIconCornerRadius = 16.dp
+    val ActiveTileCornerRadius = 24.dp
+
+    val ToggleTargetSize = 56.dp
+    val IconSize = 24.dp
+
+    val TilePadding = 8.dp
+    val TileArrangementPadding = 6.dp
+
+    val TileHeight = 72.dp
+    val IconTileWithLabelHeight = 140.dp
+
+    /** An active tile without dual target uses the active color as background */
     @Composable
     fun activeTileColors(): TileColors =
         TileColors(
+            background = MaterialTheme.colorScheme.primary,
+            iconBackground = MaterialTheme.colorScheme.primary,
+            label = MaterialTheme.colorScheme.onPrimary,
+            secondaryLabel = MaterialTheme.colorScheme.onPrimary,
+            icon = MaterialTheme.colorScheme.onPrimary,
+        )
+
+    /** An active tile with dual target only show the active color on the icon */
+    @Composable
+    fun activeDualTargetTileColors(): TileColors =
+        TileColors(
             background = MaterialTheme.colorScheme.surfaceVariant,
             iconBackground = MaterialTheme.colorScheme.primary,
             label = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -715,11 +796,53 @@
         )
 
     @Composable
-    fun getColorForState(state: Int): TileColors {
-        return when (state) {
-            STATE_ACTIVE -> activeTileColors()
+    fun getColorForState(uiState: TileUiState): TileColors {
+        return when (uiState.state) {
+            STATE_ACTIVE -> {
+                if (uiState.handlesSecondaryClick) {
+                    activeDualTargetTileColors()
+                } else {
+                    activeTileColors()
+                }
+            }
             STATE_INACTIVE -> inactiveTileColors()
             else -> unavailableTileColors()
         }
     }
+
+    @Composable
+    fun animateIconShape(state: Int): Shape {
+        return animateShape(
+            state = state,
+            activeCornerRadius = ActiveIconCornerRadius,
+            label = "QSTileCornerRadius",
+        )
+    }
+
+    @Composable
+    fun animateTileShape(state: Int): Shape {
+        return animateShape(
+            state = state,
+            activeCornerRadius = ActiveTileCornerRadius,
+            label = "QSTileIconCornerRadius",
+        )
+    }
+
+    @Composable
+    fun animateShape(state: Int, activeCornerRadius: Dp, label: String): Shape {
+        val animatedCornerRadius by
+            animateDpAsState(
+                targetValue =
+                    if (state == STATE_ACTIVE) {
+                        activeCornerRadius
+                    } else {
+                        InactiveCornerRadius
+                    },
+                label = label
+            )
+        return RoundedCornerShape(animatedCornerRadius)
+    }
 }
+
+private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid"
+private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid"
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 c241fd8..8ca8de7 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
@@ -22,6 +22,12 @@
 import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
 import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
 
+/** Represents an item from a grid associated with a row and a span */
+interface GridCell {
+    val row: Int
+    val span: GridItemSpan
+}
+
 /**
  * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's
  * positioned at
@@ -29,10 +35,12 @@
 @Immutable
 data class TileGridCell(
     override val tile: EditTileViewModel,
-    val row: Int,
-    val key: String = "${tile.tileSpec.spec}-$row",
+    override val row: Int,
     override val width: Int,
-) : SizedTile<EditTileViewModel> {
+    override val span: GridItemSpan = GridItemSpan(width)
+) : GridCell, SizedTile<EditTileViewModel> {
+    val key: String = "${tile.tileSpec.spec}-$row"
+
     constructor(
         sizedTile: SizedTile<EditTileViewModel>,
         row: Int
@@ -41,12 +49,30 @@
         row = row,
         width = sizedTile.width,
     )
-
-    val span = GridItemSpan(width)
 }
 
-fun List<SizedTile<EditTileViewModel>>.toTileGridCells(columns: Int): List<TileGridCell> {
+/** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
+@Immutable
+data class SpacerGridCell(
+    override val row: Int,
+    override val span: GridItemSpan = GridItemSpan(1)
+) : GridCell
+
+fun List<SizedTile<EditTileViewModel>>.toGridCells(
+    columns: Int,
+    includeSpacers: Boolean = false
+): List<GridCell> {
     return splitInRowsSequence(this, columns)
-        .flatMapIndexed { index, sizedTiles -> sizedTiles.map { TileGridCell(it, index) } }
+        .flatMapIndexed { rowIndex, sizedTiles ->
+            val row: List<GridCell> = sizedTiles.map { TileGridCell(it, rowIndex) }
+
+            if (includeSpacers) {
+                // Fill the incomplete rows with spacers
+                val numSpacers = columns - sizedTiles.sumOf { it.width }
+                row.toMutableList().apply { repeat(numSpacers) { add(SpacerGridCell(rowIndex)) } }
+            } else {
+                row
+            }
+        }
         .toList()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index ef2c8bf..42715be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -179,6 +179,10 @@
         currentTilesInteractor.removeTiles(listOf(tileSpec))
     }
 
+    fun setTiles(tileSpecs: List<TileSpec>) {
+        currentTilesInteractor.setTiles(tileSpecs)
+    }
+
     /** Immediately resets the current tiles to the default list. */
     fun resetCurrentTilesToDefault() {
         throw NotImplementedError("This is not supported yet")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 4ec59c9..45051fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -25,7 +25,8 @@
     val label: String,
     val secondaryLabel: String,
     val state: Int,
-    val icon: Supplier<QSTile.Icon>,
+    val handlesSecondaryClick: Boolean,
+    val icon: Supplier<QSTile.Icon?>,
 )
 
 fun QSTile.State.toUiState(): TileUiState {
@@ -33,6 +34,7 @@
         label?.toString() ?: "",
         secondaryLabel?.toString() ?: "",
         state,
-        icon?.let { Supplier { icon } } ?: iconSupplier,
+        handlesSecondaryClick,
+        icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
index 8578bb0..44dd801 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileViewModel.kt
@@ -50,8 +50,8 @@
         tile.longClick(expandable)
     }
 
-    fun onSecondaryClick(expandable: Expandable?) {
-        tile.secondaryClick(expandable)
+    fun onSecondaryClick() {
+        tile.secondaryClick(null)
     }
 
     fun startListening(token: Any) = tile.setListening(token, true)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 02379e6..4a96710 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -116,6 +116,8 @@
      */
     fun setTiles(specs: List<TileSpec>)
 
+    fun createTileSync(spec: TileSpec): QSTile?
+
     companion object {
         val POSITION_AT_END: Int = TileSpecRepository.POSITION_AT_END
     }
@@ -190,9 +192,7 @@
             }
 
     init {
-        if (featureFlags.pipelineEnabled) {
-            startTileCollection()
-        }
+        startTileCollection()
     }
 
     private fun startTileCollection() {
@@ -342,15 +342,16 @@
         lifecycleManager.flushMessagesAndUnbind()
     }
 
+    override fun createTileSync(spec: TileSpec): QSTile? {
+        return if (featureFlags.tilesEnabled) {
+            newQSTileFactory.get().createTile(spec.spec)
+        } else {
+            null
+        } ?: tileFactory.createTile(spec.spec)
+    }
+
     private suspend fun createTile(spec: TileSpec): QSTile? {
-        val tile =
-            withContext(mainDispatcher) {
-                if (featureFlags.tilesEnabled) {
-                    newQSTileFactory.get().createTile(spec.spec)
-                } else {
-                    null
-                } ?: tileFactory.createTile(spec.spec)
-            }
+        val tile = withContext(mainDispatcher) { createTileSync(spec) }
         if (tile == null) {
             logger.logTileNotFoundInFactory(spec)
             return null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index c8fbeb5..0bcb6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -40,14 +40,12 @@
 ) : CoreStartable {
 
     override fun start() {
-        if (featureFlags.pipelineEnabled) {
-            accessibilityTilesInteractor.init(currentTilesInteractor)
-            autoAddInteractor.init(currentTilesInteractor)
-            restoreReconciliationInteractor.start()
+        accessibilityTilesInteractor.init(currentTilesInteractor)
+        autoAddInteractor.init(currentTilesInteractor)
+        restoreReconciliationInteractor.start()
 
-            if (NewQsUI.isEnabled) {
-                gridConsistencyInteractor.start()
-            }
+        if (NewQsUI.isEnabled) {
+            gridConsistencyInteractor.start()
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
index 42bee3c..5dc8d1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepository.kt
@@ -9,19 +9,10 @@
 @SysUISingleton
 class QSPipelineFlagsRepository @Inject constructor() {
 
-    val pipelineEnabled: Boolean
-        get() = AconfigFlags.qsNewPipeline()
-
     val tilesEnabled: Boolean
         get() = AconfigFlags.qsNewTiles()
 
     companion object Utils {
-        fun assertInLegacyMode() =
-            RefactorFlagUtils.assertInLegacyMode(
-                AconfigFlags.qsNewPipeline(),
-                AconfigFlags.FLAG_QS_NEW_PIPELINE
-            )
-
         fun assertNewTiles() =
             RefactorFlagUtils.assertInNewMode(
                 AconfigFlags.qsNewTiles(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8887f58..9abc494 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -71,6 +71,7 @@
 import com.android.systemui.qs.logging.QSLogger;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * Base quick-settings tile, extend this to create a new tile.
@@ -350,6 +351,7 @@
 
     public void userSwitch(int newUserId) {
         mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
+        postStale();
     }
 
     public void destroy() {
@@ -667,6 +669,18 @@
         public String toString() {
             return "DrawableIcon";
         }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            // No need to compare equality of the mInvisibleDrawable as that's generated from
+            // mDrawable's constant state.
+            return other instanceof DrawableIcon && ((DrawableIcon) other).mDrawable == mDrawable;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDrawable);
+        }
     }
 
     public static class DrawableIconWithRes extends DrawableIcon {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9f41d98..7ceb786 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -114,7 +114,9 @@
 
     @Override
     public BooleanState newTileState() {
-        return new BooleanState();
+        BooleanState s = new BooleanState();
+        s.handlesSecondaryClick = true;
+        return s;
     }
 
     @Override
@@ -141,10 +143,7 @@
             mDialogViewModel.showDialog(expandable);
         } else {
             // Secondary clicks are header clicks, just toggle.
-            final boolean isEnabled = mState.value;
-            // Immediately enter transient enabling state when turning bluetooth on.
-            refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
-            mController.setBluetoothEnabled(!isEnabled);
+            toggleBluetooth();
         }
     }
 
@@ -160,9 +159,7 @@
                     new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
             return;
         }
-        if (!mState.value) {
-            mController.setBluetoothEnabled(true);
-        }
+        toggleBluetooth();
     }
 
     @Override
@@ -228,6 +225,13 @@
         state.forceExpandIcon = mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG);
     }
 
+    private void toggleBluetooth() {
+        final boolean isEnabled = mState.value;
+        // Immediately enter transient enabling state when turning bluetooth on.
+        refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
+        mController.setBluetoothEnabled(!isEnabled);
+    }
+
     /**
      * Returns the secondary label to use for the given bluetooth connection in the form of the
      * battery level or bluetooth profile name. If the bluetooth is disabled, there's no connected
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 6d98da4..02f6f80 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -52,6 +52,7 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.connectivity.AccessPointController;
 import com.android.systemui.statusbar.connectivity.IconState;
@@ -84,6 +85,7 @@
 
     protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
     private final InternetDialogManager mInternetDialogManager;
+    private final WifiStateWorker mWifiStateWorker;
     final Handler mHandler;
 
     @Inject
@@ -99,11 +101,13 @@
             QSLogger qsLogger,
             NetworkController networkController,
             AccessPointController accessPointController,
-            InternetDialogManager internetDialogManager
+            InternetDialogManager internetDialogManager,
+            WifiStateWorker wifiStateWorker
     ) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mInternetDialogManager = internetDialogManager;
+        mWifiStateWorker = wifiStateWorker;
         mHandler = mainHandler;
         mController = networkController;
         mAccessPointController = accessPointController;
@@ -115,6 +119,7 @@
     public BooleanState newTileState() {
         BooleanState s = new BooleanState();
         s.forceExpandIcon = true;
+        s.handlesSecondaryClick = true;
         return s;
     }
 
@@ -131,6 +136,13 @@
     }
 
     @Override
+    public void secondaryClick(@Nullable Expandable expandable) {
+        // TODO(b/358352265): Figure out the correct action for the secondary click
+        // Toggle Wifi
+        mWifiStateWorker.setWifiEnabled(!mWifiStateWorker.isWifiEnabled());
+    }
+
+    @Override
     public CharSequence getTileLabel() {
         return mContext.getString(R.string.quick_settings_internet_label);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 932dec5..42ef0cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
@@ -55,6 +56,7 @@
     qsLogger: QSLogger,
     viewModel: InternetTileViewModel,
     private val internetDialogManager: InternetDialogManager,
+    private val wifiStateWorker: WifiStateWorker,
     private val accessPointController: AccessPointController,
 ) :
     QSTileImpl<QSTile.BooleanState>(
@@ -81,7 +83,10 @@
         mContext.getString(R.string.quick_settings_internet_label)
 
     override fun newTileState(): QSTile.BooleanState {
-        return QSTile.BooleanState().also { it.forceExpandIcon = true }
+        return QSTile.BooleanState().also {
+            it.forceExpandIcon = true
+            it.handlesSecondaryClick = true
+        }
     }
 
     override fun handleClick(expandable: Expandable?) {
@@ -95,6 +100,12 @@
         }
     }
 
+    override fun secondaryClick(expandable: Expandable?) {
+        // TODO(b/358352265): Figure out the correct action for the secondary click
+        // Toggle wifi
+        wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
+    }
+
     override fun handleUpdateState(state: QSTile.BooleanState, arg: Any?) {
         state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
         state.expandedAccessibilityClassName = Switch::class.java.name
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index 2a33a16..7d23fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -20,10 +20,12 @@
 import android.content.Intent
 import android.os.Handler
 import android.os.Looper
+import android.service.quicksettings.Tile
+import androidx.annotation.DrawableRes
+import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.coroutineScope
 import androidx.lifecycle.repeatOnLifecycle
-import com.android.internal.R.attr.contentDescription
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.animation.Expandable
 import com.android.systemui.dagger.qualifiers.Background
@@ -35,6 +37,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.asQSTileIcon
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
@@ -61,7 +64,7 @@
     activityStarter: ActivityStarter,
     qsLogger: QSLogger,
     qsTileConfigProvider: QSTileConfigProvider,
-    dataInteractor: ModesTileDataInteractor,
+    private val dataInteractor: ModesTileDataInteractor,
     private val tileMapper: ModesTileMapper,
     private val userActionInteractor: ModesTileUserActionInteractor,
 ) :
@@ -94,7 +97,13 @@
 
     override fun getTileLabel(): CharSequence = tileState.label
 
-    override fun newTileState() = QSTile.State()
+    override fun newTileState(): QSTile.State {
+        return QSTile.State().apply {
+            label = mContext.getString(R.string.quick_settings_modes_label)
+            icon = ResourceIcon.get(ICON_RES_ID)
+            state = Tile.STATE_INACTIVE
+        }
+    }
 
     override fun handleClick(expandable: Expandable?) = runBlocking {
         userActionInteractor.handleClick(expandable)
@@ -102,22 +111,26 @@
 
     override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
 
-    override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
-        if (arg is ModesTileModel) {
-            tileState = tileMapper.map(config, arg)
+    @VisibleForTesting
+    public override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
+        // This runBlocking() will block @Background. Due to caches, it's expected to be fast.
+        val model =
+            if (arg is ModesTileModel) arg else runBlocking { dataInteractor.getCurrentTileModel() }
 
-            state?.apply {
-                this.state = tileState.activationState.legacyState
-                icon = ResourceIcon.get(tileState.iconRes ?: R.drawable.qs_dnd_icon_off)
-                label = tileLabel
-                secondaryLabel = tileState.secondaryLabel
-                contentDescription = tileState.contentDescription
-                expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
-            }
+        tileState = tileMapper.map(config, model)
+        state?.apply {
+            this.state = tileState.activationState.legacyState
+            val tileStateIcon = tileState.icon()
+            icon = tileStateIcon?.asQSTileIcon() ?: ResourceIcon.get(ICON_RES_ID)
+            label = tileLabel
+            secondaryLabel = tileState.secondaryLabel
+            contentDescription = tileState.contentDescription
+            expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
         }
     }
 
     companion object {
         const val TILE_SPEC = "dnd"
+        @DrawableRes val ICON_RES_ID = com.android.internal.R.drawable.ic_zen_priority_modes
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
index 0d15a5b..1d42777 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
@@ -47,6 +47,7 @@
     private fun QSTileUserAction.getQSEvent(): QSEvent =
         when (this) {
             is QSTileUserAction.Click -> QSEvent.QS_ACTION_CLICK
+            is QSTileUserAction.ToggleClick -> QSEvent.QS_ACTION_SECONDARY_CLICK
             is QSTileUserAction.LongClick -> QSEvent.QS_ACTION_LONG_PRESS
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
index f0d7206..8ec8a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
@@ -222,6 +222,7 @@
     private fun QSTileUserAction.toLogString(): String =
         when (this) {
             is QSTileUserAction.Click -> "click"
+            is QSTileUserAction.ToggleClick -> "toggle click"
             is QSTileUserAction.LongClick -> "long click"
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
index 9e84f01..d8c5af2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
@@ -229,7 +229,8 @@
         filter { action ->
             val isFalseAction =
                 when (action) {
-                    is QSTileUserAction.Click ->
+                    is QSTileUserAction.Click,
+                    is QSTileUserAction.ToggleClick ->
                         falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)
                     is QSTileUserAction.LongClick ->
                         falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index b2873c5..5ea9e6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -346,7 +346,6 @@
         mCallback = null;
     }
 
-    @VisibleForTesting
     boolean isAirplaneModeEnabled() {
         return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index f018336..89b9eee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -30,7 +30,6 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyDisplayInfo;
-import android.telephony.TelephonyManager;
 import android.text.Html;
 import android.text.Layout;
 import android.text.TextUtils;
@@ -50,9 +49,14 @@
 import android.widget.TextView;
 
 import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.lifecycle.MutableLiveData;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -110,7 +114,6 @@
     protected boolean mCanConfigWifi;
 
     private final InternetDialogManager mInternetDialogManager;
-    private TelephonyManager mTelephonyManager;
     @Nullable
     private AlertDialog mAlertDialog;
     private final UiEventLogger mUiEventLogger;
@@ -169,6 +172,13 @@
     @Nullable
     private Job mClickJob;
 
+    // These are to reduce the UI janky frame duration. b/323286540
+    private LifecycleRegistry mLifecycleRegistry;
+    @VisibleForTesting
+    LifecycleOwner mLifecycleOwner;
+    @VisibleForTesting
+    MutableLiveData<InternetContent> mDataInternetContent = new MutableLiveData<>();
+
     @AssistedFactory
     public interface Factory {
         InternetDialogDelegate create(
@@ -205,7 +215,6 @@
         mInternetDialogManager = internetDialogManager;
         mInternetDialogController = internetDialogController;
         mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
-        mTelephonyManager = mInternetDialogController.getTelephonyManager();
         mCanConfigMobileData = canConfigMobileData;
         mCanConfigWifi = canConfigWifi;
         mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
@@ -227,6 +236,14 @@
             mDialog.dismiss();
         }
         mDialog = dialog;
+        mLifecycleOwner = new LifecycleOwner() {
+            @NonNull
+            @Override
+            public Lifecycle getLifecycle() {
+                return mLifecycleRegistry;
+            }
+        };
+        mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner);
 
         return dialog;
     }
@@ -249,7 +266,9 @@
 
         mWifiNetworkHeight = context.getResources()
                 .getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
-
+        mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
+        mDataInternetContent.observe(
+                mLifecycleOwner, (internetContent) -> updateDialogUI(internetContent));
         mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
         mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
         mDivider = mDialogView.requireViewById(R.id.divider);
@@ -287,6 +306,8 @@
                 mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
         mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context));
         mWifiRecyclerView.setAdapter(mAdapter);
+
+        updateDialogUI(getWifiNetworkContent());
     }
 
     @Override
@@ -294,6 +315,9 @@
         if (DEBUG) {
             Log.d(TAG, "onStart");
         }
+
+        mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
+
         mInternetDialogController.onStart(this, mCanConfigWifi);
         if (!mCanConfigWifi) {
             hideWifiViews();
@@ -315,6 +339,7 @@
         if (DEBUG) {
             Log.d(TAG, "onStop");
         }
+        mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
         mMobileNetworkLayout.setOnClickListener(null);
         mConnectedWifListLayout.setOnClickListener(null);
         if (mSecondaryMobileNetworkLayout != null) {
@@ -348,31 +373,61 @@
      *                                  otherwise {@code false}.
      */
     void updateDialog(boolean shouldUpdateMobileNetwork) {
-        if (DEBUG) {
-            Log.d(TAG, "updateDialog");
-        }
-        mInternetDialogTitle.setText(getDialogTitleText());
-        mInternetDialogSubTitle.setText(getSubtitleText());
-        mAirplaneModeButton.setVisibility(
-                mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+        mBackgroundExecutor.execute(() -> {
+            mDataInternetContent.postValue(getInternetContent(shouldUpdateMobileNetwork));
+        });
+    }
 
-        updateEthernet();
-        if (shouldUpdateMobileNetwork) {
-            setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
-                    mInternetDialogController.isCarrierNetworkActive());
+    private void updateDialogUI(InternetContent internetContent) {
+        if (DEBUG) {
+            Log.d(TAG, "updateDialog ");
         }
 
+        mInternetDialogTitle.setText(internetContent.mInternetDialogTitleString);
+        mInternetDialogSubTitle.setText(internetContent.mInternetDialogSubTitle);
+        mAirplaneModeButton.setVisibility(
+                internetContent.mIsAirplaneModeEnabled ? View.VISIBLE : View.GONE);
+
+        updateEthernet(internetContent);
+        setMobileDataLayout(internetContent);
+
         if (!mCanConfigWifi) {
             return;
         }
+        updateWifiToggle(internetContent);
+        updateConnectedWifi(internetContent);
+        updateWifiListAndSeeAll(internetContent);
+        updateWifiScanNotify(internetContent);
+    }
 
-        final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
-        final boolean isWifiEnabled = mInternetDialogController.isWifiEnabled();
-        final boolean isWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
-        updateWifiToggle(isWifiEnabled, isDeviceLocked);
-        updateConnectedWifi(isWifiEnabled, isDeviceLocked);
-        updateWifiListAndSeeAll(isWifiEnabled, isDeviceLocked);
-        updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
+    private InternetContent getInternetContent(boolean shouldUpdateMobileNetwork) {
+        InternetContent internetContent = new InternetContent();
+        internetContent.mShouldUpdateMobileNetwork = shouldUpdateMobileNetwork;
+        internetContent.mInternetDialogTitleString = getDialogTitleText();
+        internetContent.mInternetDialogSubTitle = getSubtitleText();
+        if (shouldUpdateMobileNetwork) {
+            internetContent.mActiveNetworkIsCellular =
+                    mInternetDialogController.activeNetworkIsCellular();
+            internetContent.mIsCarrierNetworkActive =
+                    mInternetDialogController.isCarrierNetworkActive();
+        }
+        internetContent.mIsAirplaneModeEnabled = mInternetDialogController.isAirplaneModeEnabled();
+        internetContent.mHasEthernet = mInternetDialogController.hasEthernet();
+        internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
+        internetContent.mHasActiveSubIdOnDds = mInternetDialogController.hasActiveSubIdOnDds();
+        internetContent.mIsMobileDataEnabled = mInternetDialogController.isMobileDataEnabled();
+        internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
+        internetContent.mIsWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
+        return internetContent;
+    }
+
+    private InternetContent getWifiNetworkContent() {
+        InternetContent internetContent = new InternetContent();
+        internetContent.mInternetDialogTitleString = getDialogTitleText();
+        internetContent.mInternetDialogSubTitle = getSubtitleText();
+        internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
+        internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
+        return internetContent;
     }
 
     private void setOnClickListener(SystemUIDialog dialog) {
@@ -436,39 +491,39 @@
     }
 
     @MainThread
-    private void updateEthernet() {
+    private void updateEthernet(InternetContent internetContent) {
         mEthernetLayout.setVisibility(
-                mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
+                internetContent.mHasEthernet ? View.VISIBLE : View.GONE);
     }
 
-    private void setMobileDataLayout(boolean activeNetworkIsCellular,
-            boolean isCarrierNetworkActive) {
-
-        if (mDialog != null) {
-            setMobileDataLayout(mDialog, activeNetworkIsCellular, isCarrierNetworkActive);
+    private void setMobileDataLayout(InternetContent internetContent) {
+        if (!internetContent.mShouldUpdateMobileNetwork && mDialog == null) {
+            return;
         }
+        setMobileDataLayout(mDialog, internetContent);
     }
 
-    private void setMobileDataLayout(SystemUIDialog dialog, boolean activeNetworkIsCellular,
-            boolean isCarrierNetworkActive) {
-        boolean isNetworkConnected = activeNetworkIsCellular || isCarrierNetworkActive;
+    private void setMobileDataLayout(SystemUIDialog dialog, InternetContent internetContent) {
+        boolean isNetworkConnected =
+                internetContent.mActiveNetworkIsCellular
+                        || internetContent.mIsCarrierNetworkActive;
         // 1. Mobile network should be gone if airplane mode ON or the list of active
         //    subscriptionId is null.
         // 2. Carrier network should be gone if airplane mode ON and Wi-Fi is OFF.
         if (DEBUG) {
-            Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = " + isCarrierNetworkActive);
+            Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = "
+                    + internetContent.mIsCarrierNetworkActive);
         }
 
-        boolean isWifiEnabled = mInternetDialogController.isWifiEnabled();
-        if (!mInternetDialogController.hasActiveSubIdOnDds()
-                && (!isWifiEnabled || !isCarrierNetworkActive)) {
+        if (!internetContent.mHasActiveSubIdOnDds && (!internetContent.mIsWifiEnabled
+                || !internetContent.mIsCarrierNetworkActive)) {
             mMobileNetworkLayout.setVisibility(View.GONE);
             if (mSecondaryMobileNetworkLayout != null) {
                 mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
             }
         } else {
             mMobileNetworkLayout.setVisibility(View.VISIBLE);
-            mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+            mMobileDataToggle.setChecked(internetContent.mIsMobileDataEnabled);
             mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId));
             String summary = getMobileNetworkSummary(mDefaultDataSubId);
             if (!TextUtils.isEmpty(summary)) {
@@ -508,7 +563,7 @@
                 if (stub != null) {
                     stub.inflate();
                 }
-                mSecondaryMobileNetworkLayout = dialog.findViewById(
+                mSecondaryMobileNetworkLayout = mDialogView.findViewById(
                         R.id.secondary_mobile_network_layout);
                 mSecondaryMobileNetworkLayout.setOnClickListener(
                         this::onClickConnectedSecondarySub);
@@ -567,7 +622,7 @@
             }
 
             // Set airplane mode to the summary for carrier network
-            if (mInternetDialogController.isAirplaneModeEnabled()) {
+            if (internetContent.mIsAirplaneModeEnabled) {
                 mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
                 mAirplaneModeSummaryText.setText(
                         dialog.getContext().getText(R.string.airplane_mode));
@@ -579,17 +634,18 @@
     }
 
     @MainThread
-    private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
-        if (mWiFiToggle.isChecked() != isWifiEnabled) {
-            mWiFiToggle.setChecked(isWifiEnabled);
+    private void updateWifiToggle(InternetContent internetContent) {
+        if (mWiFiToggle.isChecked() != internetContent.mIsWifiEnabled) {
+            mWiFiToggle.setChecked(internetContent.mIsWifiEnabled);
         }
-        if (isDeviceLocked) {
+        if (internetContent.mIsDeviceLocked) {
             mWifiToggleTitleText.setTextAppearance((mConnectedWifiEntry != null)
                     ? R.style.TextAppearance_InternetDialog_Active
                     : R.style.TextAppearance_InternetDialog);
         }
         mTurnWifiOnLayout.setBackground(
-                (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+                (internetContent.mIsDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn
+                        : null);
 
         if (!mCanChangeWifiState && mWiFiToggle.isEnabled()) {
             mWiFiToggle.setEnabled(false);
@@ -601,8 +657,9 @@
     }
 
     @MainThread
-    private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
-        if (mDialog == null || !isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
+    private void updateConnectedWifi(InternetContent internetContent) {
+        if (mDialog == null || !internetContent.mIsWifiEnabled || mConnectedWifiEntry == null
+                || internetContent.mIsDeviceLocked) {
             mConnectedWifListLayout.setVisibility(View.GONE);
             mShareWifiButton.setVisibility(View.GONE);
             return;
@@ -627,8 +684,8 @@
     }
 
     @MainThread
-    private void updateWifiListAndSeeAll(boolean isWifiEnabled, boolean isDeviceLocked) {
-        if (!isWifiEnabled || isDeviceLocked) {
+    private void updateWifiListAndSeeAll(InternetContent internetContent) {
+        if (!internetContent.mIsWifiEnabled || internetContent.mIsDeviceLocked) {
             mWifiRecyclerView.setVisibility(View.GONE);
             mSeeAllLayout.setVisibility(View.GONE);
             return;
@@ -670,9 +727,10 @@
     }
 
     @MainThread
-    private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
-            boolean isDeviceLocked) {
-        if (mDialog == null || isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
+    private void updateWifiScanNotify(InternetContent internetContent) {
+        if (mDialog == null || internetContent.mIsWifiEnabled
+                || !internetContent.mIsWifiScanEnabled
+                || internetContent.mIsDeviceLocked) {
             mWifiScanNotifyLayout.setVisibility(View.GONE);
             return;
         }
@@ -805,62 +863,62 @@
 
     @Override
     public void onRefreshCarrierInfo() {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onSimStateChanged() {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     @WorkerThread
     public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     @WorkerThread
     public void onLost(Network network) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onSubscriptionsChanged(int defaultDataSubId) {
         mDefaultDataSubId = defaultDataSubId;
-        mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onUserMobileDataStateChanged(boolean enabled) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onServiceStateChanged(ServiceState serviceState) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     @WorkerThread
     public void onDataConnectionStateChanged(int state, int networkType) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onSignalStrengthsChanged(SignalStrength signalStrength) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
     public void onCarrierNetworkChange(boolean active) {
-        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+
+        updateDialog(true /* shouldUpdateMobileNetwork */);
     }
 
     @Override
@@ -912,4 +970,20 @@
             return mId;
         }
     }
+
+    @VisibleForTesting
+    static class InternetContent {
+        CharSequence mInternetDialogTitleString = "";
+        CharSequence mInternetDialogSubTitle = "";
+        boolean mIsAirplaneModeEnabled = false;
+        boolean mHasEthernet = false;
+        boolean mShouldUpdateMobileNetwork = false;
+        boolean mActiveNetworkIsCellular = false;
+        boolean mIsCarrierNetworkActive = false;
+        boolean mIsWifiEnabled = false;
+        boolean mHasActiveSubIdOnDds = false;
+        boolean mIsMobileDataEnabled = false;
+        boolean mIsDeviceLocked = false;
+        boolean mIsWifiScanEnabled = false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
index 9b8dba1..9fb1d46 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
@@ -37,19 +37,22 @@
 
     override fun map(config: QSTileConfig, data: AirplaneModeTileModel): QSTileState =
         QSTileState.build(resources, theme, config.uiConfig) {
-            val icon =
+            iconRes =
+                if (data.isEnabled) {
+                    R.drawable.qs_airplane_icon_on
+                } else {
+                    R.drawable.qs_airplane_icon_off
+                }
+
+            icon = {
                 Icon.Loaded(
                     resources.getDrawable(
-                        if (data.isEnabled) {
-                            R.drawable.qs_airplane_icon_on
-                        } else {
-                            R.drawable.qs_airplane_icon_off
-                        },
+                        iconRes!!,
                         theme,
                     ),
                     contentDescription = null
                 )
-            this.icon = { icon }
+            }
             if (data.isEnabled) {
                 activationState = QSTileState.ActivationState.ACTIVE
                 secondaryLabel = resources.getStringArray(R.array.tile_states_airplane)[2]
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
index bf0f8f6..5053291 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
@@ -57,6 +57,7 @@
                         Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index 14fc57c..79fcd37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -49,6 +49,7 @@
                     }
                 }
                 is QSTileUserAction.LongClick -> {}
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
index d4b4fe0..3bbb9aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
@@ -48,6 +48,7 @@
                         Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
index 534bd73..dfdec3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
@@ -49,6 +49,7 @@
                         Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
index c932cee..0aaea8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles.impl.custom.data.repository
 
 import android.content.pm.PackageManager
-import android.content.pm.ServiceInfo
 import android.graphics.drawable.Icon
+import android.os.RemoteException
 import android.os.UserHandle
 import android.service.quicksettings.Tile
 import android.service.quicksettings.TileService
@@ -163,13 +163,14 @@
     override suspend fun isTileActive(): Boolean =
         withContext(backgroundContext) {
             try {
-                val info: ServiceInfo =
+                val info =
                     packageManagerAdapter.getServiceInfo(
                         tileSpec.componentName,
-                        META_DATA_QUERY_FLAGS
+                        META_DATA_QUERY_FLAGS,
+                        getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT,
                     )
-                info.metaData?.getBoolean(TileService.META_DATA_ACTIVE_TILE, false) == true
-            } catch (e: PackageManager.NameNotFoundException) {
+                info?.metaData?.getBoolean(TileService.META_DATA_ACTIVE_TILE, false) == true
+            } catch (e: RemoteException) {
                 false
             }
         }
@@ -177,13 +178,14 @@
     override suspend fun isTileToggleable(): Boolean =
         withContext(backgroundContext) {
             try {
-                val info: ServiceInfo =
+                val info =
                     packageManagerAdapter.getServiceInfo(
                         tileSpec.componentName,
-                        META_DATA_QUERY_FLAGS
+                        META_DATA_QUERY_FLAGS,
+                        getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT
                     )
-                info.metaData?.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false) == true
-            } catch (e: PackageManager.NameNotFoundException) {
+                info?.metaData?.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false) == true
+            } catch (e: RemoteException) {
                 false
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
index 875079c..984228d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
@@ -41,16 +41,25 @@
 ) : QSTileDataToStateMapper<CustomTileDataModel> {
 
     override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
-        val userContext = context.createContextAsUser(UserHandle(data.user.identifier), 0)
+        val userContext =
+            try {
+                context.createContextAsUser(UserHandle(data.user.identifier), 0)
+            } catch (exception: IllegalStateException) {
+                null
+            }
 
         val iconResult =
-            getIconProvider(
-                userContext = userContext,
-                icon = data.tile.icon,
-                callingAppUid = data.callingAppUid,
-                packageName = data.componentName.packageName,
-                defaultIcon = data.defaultTileIcon,
-            )
+            if (userContext != null) {
+                getIconProvider(
+                    userContext = userContext,
+                    icon = data.tile.icon,
+                    callingAppUid = data.callingAppUid,
+                    packageName = data.componentName.packageName,
+                    defaultIcon = data.defaultTileIcon,
+                )
+            } else {
+                IconResult({ null }, true)
+            }
 
         return QSTileState.build(iconResult.iconProvider, data.tile.label) {
             var tileState: Int = data.tile.state
@@ -61,7 +70,7 @@
             icon = iconResult.iconProvider
             activationState =
                 if (iconResult.failedToLoad) {
-                    QSTileState.ActivationState.INACTIVE
+                    QSTileState.ActivationState.UNAVAILABLE
                 } else {
                     QSTileState.ActivationState.valueOf(tileState)
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index 9bdf631..af2bb9d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -74,6 +74,7 @@
                     click(action.expandable, data.tile.activityLaunchForClick)
                 is QSTileUserAction.LongClick ->
                     longClick(user, action.expandable, data.componentName, data.tile.state)
+                is QSTileUserAction.ToggleClick -> {}
             }
             qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
index bedd65e..13afc15 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
@@ -42,6 +42,7 @@
                         flashlightController.setFlashlight(!input.data.isEnabled)
                     }
                 }
+                is QSTileUserAction.ToggleClick -> {}
                 else -> {}
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
index d308ec8..6ab5796 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
@@ -66,8 +66,7 @@
                                         INTERACTION_JANK_TAG
                                     )
                                 )
-                                ?.let { dialogTransitionAnimator.show(dialog, it) }
-                                ?: dialog.show()
+                                ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show()
                         } else {
                             dialog.show()
                         }
@@ -89,8 +88,10 @@
                         Intent(Settings.ACTION_TEXT_READING_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
+
     companion object {
         private const val INTERACTION_JANK_TAG = "font_scaling"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
index e543e4b..8965ef2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
@@ -72,6 +72,10 @@
                 else QSTileState.ActivationState.INACTIVE
 
             supportedActions =
-                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+                setOf(
+                    QSTileState.UserAction.CLICK,
+                    QSTileState.UserAction.TOGGLE_CLICK,
+                    QSTileState.UserAction.LONG_CLICK
+                )
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index eec5d3d..204ead3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -79,6 +79,7 @@
                 flowOf(
                     InternetTileModel.Active(
                         secondaryTitle = secondary,
+                        iconId = wifiIcon.icon.res,
                         icon = Icon.Loaded(context.getDrawable(wifiIcon.icon.res)!!, null),
                         stateDescription = wifiIcon.contentDescription,
                         contentDescription = ContentDescription.Loaded("$internetLabel,$secondary"),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index c0b089d..a963b28 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker
 import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
 import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
 import com.android.systemui.statusbar.connectivity.AccessPointController
@@ -36,6 +37,7 @@
 constructor(
     @Main private val mainContext: CoroutineContext,
     private val internetDialogManager: InternetDialogManager,
+    private val wifiStateWorker: WifiStateWorker,
     private val accessPointController: AccessPointController,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
 ) : QSTileUserActionInteractor<InternetTileModel> {
@@ -53,6 +55,11 @@
                         )
                     }
                 }
+                is QSTileUserAction.ToggleClick -> {
+                    // TODO(b/358352265): Figure out the correct action for the secondary click
+                    // Toggle Wifi
+                    wifiStateWorker.isWifiEnabled = !wifiStateWorker.isWifiEnabled
+                }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
index d643273..aa83877 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
@@ -49,6 +49,7 @@
                         Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
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 77404aa..cca947f 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
@@ -68,6 +68,7 @@
                         Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
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 92efa40..c2d112e 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
@@ -17,30 +17,30 @@
 package com.android.systemui.qs.tiles.impl.modes.domain.interactor
 
 import android.app.Flags
+import android.content.Context
 import android.os.UserHandle
-import com.android.settingslib.notification.data.repository.ZenModeRepository
+import com.android.app.tracing.coroutines.flow.map
+import com.android.systemui.common.shared.model.asIcon
 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.modes.domain.model.ModesTileModel
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
 import javax.inject.Inject
 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
 
 class ModesTileDataInteractor
 @Inject
 constructor(
-    val zenModeRepository: ZenModeRepository,
+    val context: Context,
+    val zenModeInteractor: ZenModeInteractor,
     @Background val bgDispatcher: CoroutineDispatcher,
 ) : QSTileDataInteractor<ModesTileModel> {
-    private val activeModes =
-        zenModeRepository.modes
-            .map { modes -> modes.filter { mode -> mode.isActive }.map { it.name } }
-            .distinctUntilChanged()
 
     override fun tileData(
         user: UserHandle,
@@ -53,9 +53,37 @@
      * TODO(b/299909989): Remove after the transition.
      */
     fun tileData() =
-        activeModes
-            .map { ModesTileModel(isActivated = it.isNotEmpty(), activeModes = it) }
+        zenModeInteractor.activeModes
+            .map { activeModes -> buildTileData(activeModes) }
             .flowOn(bgDispatcher)
+            .distinctUntilChanged()
+
+    suspend fun getCurrentTileModel() = buildTileData(zenModeInteractor.getActiveModes())
+
+    private fun buildTileData(activeModes: ActiveZenModes): ModesTileModel {
+        val modesIconResId = com.android.internal.R.drawable.ic_zen_priority_modes
+
+        if (usesModeIcons()) {
+            val mainModeDrawable = activeModes.mainMode?.icon?.drawable
+            val iconResId = if (mainModeDrawable == null) modesIconResId else null
+
+            return ModesTileModel(
+                isActivated = activeModes.isAnyActive(),
+                icon = (mainModeDrawable ?: context.getDrawable(modesIconResId)!!).asIcon(),
+                iconResId = iconResId,
+                activeModes = activeModes.modeNames
+            )
+        } else {
+            return ModesTileModel(
+                isActivated = activeModes.isAnyActive(),
+                icon = context.getDrawable(modesIconResId)!!.asIcon(),
+                iconResId = modesIconResId,
+                activeModes = activeModes.modeNames
+            )
+        }
+    }
 
     override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi())
+
+    private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index 083bf05..eb8b23c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -41,7 +41,8 @@
     override suspend fun handleInput(input: QSTileInput<ModesTileModel>) {
         with(input) {
             when (action) {
-                is QSTileUserAction.Click -> {
+                is QSTileUserAction.Click,
+                is QSTileUserAction.ToggleClick -> {
                     handleClick(action.expandable)
                 }
                 is QSTileUserAction.LongClick -> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
index cc509ea..db48123 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/model/ModesTileModel.kt
@@ -15,4 +15,18 @@
  */
 
 package com.android.systemui.qs.tiles.impl.modes.domain.model
-data class ModesTileModel(val isActivated: Boolean, val activeModes: List<String>)
+
+import com.android.systemui.common.shared.model.Icon
+
+data class ModesTileModel(
+    val isActivated: Boolean,
+    val activeModes: List<String>,
+    val icon: Icon.Loaded,
+
+    /**
+     * Resource id corresponding to [icon]. Will only be present if it's know to correspond to a
+     * resource with a known id in SystemUI (such as resources from `android.R`,
+     * `com.android.internal.R`, or `com.android.systemui.res` itself).
+     */
+    val iconResId: Int? = null
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 7afdb75..7f571b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -19,7 +19,6 @@
 import android.content.res.Resources
 import android.icu.text.MessageFormat
 import android.widget.Button
-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.modes.domain.model.ModesTileModel
@@ -37,18 +36,10 @@
 ) : QSTileDataToStateMapper<ModesTileModel> {
     override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
         QSTileState.build(resources, theme, config.uiConfig) {
-            iconRes =
-                if (data.isActivated) {
-                    R.drawable.qs_dnd_icon_on
-                } else {
-                    R.drawable.qs_dnd_icon_off
-                }
-            val icon =
-                Icon.Loaded(
-                    resources.getDrawable(iconRes!!, theme),
-                    contentDescription = null,
-                )
-            this.icon = { icon }
+            if (!android.app.Flags.modesUiIcons()) {
+                iconRes = data.iconResId
+            }
+            icon = { data.icon }
             activationState =
                 if (data.isActivated) {
                     QSTileState.ActivationState.ACTIVE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
index 5cee8c4..7076a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
@@ -55,6 +55,7 @@
                         Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
index 5cb0e18..0a0f0a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
@@ -49,6 +49,7 @@
                         Intent(Settings.ACTION_ONE_HANDED_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
index 7c0c41e..bb5df02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
@@ -45,6 +45,7 @@
                     }
                 }
                 is QSTileUserAction.LongClick -> {} // no-op
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
index ed5e4fe..de49e70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -71,6 +71,7 @@
                         Intent(Settings.ACTION_REDUCE_BRIGHT_COLORS_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
index 34385ea..65712c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
@@ -46,6 +46,7 @@
                         Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
index a5dc66c..252e3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -94,8 +94,7 @@
                             )
                             ?.let { controller ->
                                 dialogTransitionAnimator.show(dialog, controller)
-                            }
-                            ?: dialog.show()
+                            } ?: dialog.show()
                     }
                 }
                 is QSTileUserAction.LongClick -> {
@@ -104,6 +103,7 @@
                         Intent(Settings.ACTION_DATA_SAVER_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 5637115..48b39ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -75,6 +75,7 @@
                     }
                 }
                 is QSTileUserAction.LongClick -> {} // no-op
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
index f22a426..d7f64d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
@@ -82,6 +82,7 @@
                     }
                     qsTileIntentUserActionHandler.handle(action.expandable, longClickIntent)
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
index f8dd1730..8897828 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
@@ -54,6 +54,7 @@
                         Intent(Settings.ACTION_DARK_THEME_SETTINGS)
                     )
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
index 031e4d9..45ae09e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
@@ -49,6 +49,7 @@
                         )
                     }
                 }
+                is QSTileUserAction.ToggleClick -> {}
             }
         }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
index 30247c4..549f0a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
@@ -105,6 +105,7 @@
 
     enum class UserAction {
         CLICK,
+        TOGGLE_CLICK,
         LONG_CLICK,
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
index acb2936..bf3bc73 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
@@ -23,5 +23,8 @@
     val expandable: Expandable?
 
     class Click(override val expandable: Expandable?) : QSTileUserAction
+
+    class ToggleClick(override val expandable: Expandable?) : QSTileUserAction
+
     class LongClick(override val expandable: Expandable?) : QSTileUserAction
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index 9bcf927..8077c67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -131,8 +131,8 @@
     }
 
     override fun secondaryClick(expandable: Expandable?) {
-        if (isActionSupported(QSTileState.UserAction.CLICK)) {
-            qsTileViewModel.onActionPerformed(QSTileUserAction.Click(expandable))
+        if (isActionSupported(QSTileState.UserAction.TOGGLE_CLICK)) {
+            qsTileViewModel.onActionPerformed(QSTileUserAction.ToggleClick(expandable))
         }
     }
 
@@ -184,8 +184,7 @@
         }
     }
 
-    override fun isListening(): Boolean =
-        listeningClients.isNotEmpty()
+    override fun isListening(): Boolean = listeningClients.isNotEmpty()
 
     override fun setDetailListening(show: Boolean) {
         // do nothing like QSTileImpl
@@ -238,6 +237,8 @@
                 secondaryLabel = viewModelState.secondaryLabel
                 handlesLongClick =
                     viewModelState.supportedActions.contains(QSTileState.UserAction.LONG_CLICK)
+                handlesSecondaryClick =
+                    viewModelState.supportedActions.contains(QSTileState.UserAction.TOGGLE_CLICK)
 
                 icon =
                     when (val stateIcon = viewModelState.icon()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index ae2f32a..ac6ebe7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.qs.QSContainerImpl
 import com.android.systemui.qs.QSImpl
 import com.android.systemui.qs.dagger.QSSceneComponent
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel.state
 import com.android.systemui.res.R
 import com.android.systemui.settings.brightness.MirrorController
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -126,12 +125,18 @@
     /** The current height of QQS in the current [qsView], or 0 if there's no view. */
     val qqsHeight: Int
 
+    /** @return height with the squishiness fraction applied. */
+    val squishedQqsHeight: Int
+
     /**
      * The current height of QS in the current [qsView], or 0 if there's no view. If customizing, it
      * will return the height allocated to the customizer.
      */
     val qsHeight: Int
 
+    /** @return height with the squishiness fraction applied. */
+    val squishedQsHeight: Int
+
     /** Compatibility for use by LockscreenShadeTransitionController. Matches default from [QS] */
     val isQsFullyCollapsed: Boolean
         get() = true
@@ -142,17 +147,17 @@
     sealed interface State {
 
         val isVisible: Boolean
-        val expansion: Float
+        val expansion: () -> Float
         val squishiness: () -> Float
 
         data object CLOSED : State {
             override val isVisible = false
-            override val expansion = 0f
+            override val expansion = { 0f }
             override val squishiness = { 1f }
         }
 
         /** State for expanding between QQS and QS */
-        data class Expanding(override val expansion: Float) : State {
+        class Expanding(override val expansion: () -> Float) : State {
             override val isVisible = true
             override val squishiness = { 1f }
         }
@@ -165,7 +170,7 @@
          */
         class UnsquishingQQS(override val squishiness: () -> Float) : State {
             override val isVisible = true
-            override val expansion = 0f
+            override val expansion = { 0f }
         }
 
         /**
@@ -176,16 +181,16 @@
          */
         class UnsquishingQS(override val squishiness: () -> Float) : State {
             override val isVisible = true
-            override val expansion = 1f
+            override val expansion = { 1f }
         }
 
         companion object {
             // These are special cases of the expansion.
-            val QQS = Expanding(0f)
-            val QS = Expanding(1f)
+            val QQS = Expanding { 0f }
+            val QS = Expanding { 1f }
 
             /** Collapsing from QS to QQS. [progress] is 0f in QS and 1f in QQS. */
-            fun Collapsing(progress: Float) = Expanding(1f - progress)
+            fun Collapsing(progress: () -> Float) = Expanding { 1f - progress() }
         }
     }
 }
@@ -273,9 +278,15 @@
     override val qqsHeight: Int
         get() = qsImpl.value?.qqsHeight ?: 0
 
+    override val squishedQqsHeight: Int
+        get() = qsImpl.value?.squishedQqsHeight ?: 0
+
     override val qsHeight: Int
         get() = qsImpl.value?.qsHeight ?: 0
 
+    override val squishedQsHeight: Int
+        get() = qsImpl.value?.squishedQsHeight ?: 0
+
     // If value is null, there's no QS and therefore it's fully collapsed.
     override val isQsFullyCollapsed: Boolean
         get() = qsImpl.value?.isFullyCollapsed ?: true
@@ -407,14 +418,14 @@
 
     private fun QSImpl.applyState(state: QSSceneAdapter.State) {
         setQsVisible(state.isVisible)
-        setExpanded(state.isVisible && state.expansion > 0f)
+        setExpanded(state.isVisible && state.expansion() > 0f)
         setListening(state.isVisible)
     }
 
     override fun applyLatestExpansionAndSquishiness() {
         val qsImpl = _qsImpl.value
         val state = state.value
-        qsImpl?.setQsExpansion(state.expansion, 1f, 0f, state.squishiness())
+        qsImpl?.setQsExpansion(state.expansion(), 1f, 0f, state.squishiness())
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
new file mode 100644
index 0000000..f77386d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneContentViewModel.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import androidx.lifecycle.LifecycleOwner
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
+import com.android.systemui.qs.FooterActionsController
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Models UI state needed for rendering the content of the quick settings scene.
+ *
+ * Different from [QuickSettingsUserActionsViewModel] that models the UI state needed to figure out
+ * which user actions can trigger navigation to other scenes.
+ */
+class QuickSettingsSceneContentViewModel
+@AssistedInject
+constructor(
+    val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
+    val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+    val qsSceneAdapter: QSSceneAdapter,
+    private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
+    private val footerActionsController: FooterActionsController,
+    val mediaCarouselInteractor: MediaCarouselInteractor,
+    private val shadeInteractor: ShadeInteractor,
+    private val sceneInteractor: SceneInteractor,
+) : ExclusiveActivatable() {
+
+    val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
+
+    private val footerActionsControllerInitialized = AtomicBoolean(false)
+
+    fun getFooterActionsViewModel(lifecycleOwner: LifecycleOwner): FooterActionsViewModel {
+        if (footerActionsControllerInitialized.compareAndSet(false, true)) {
+            footerActionsController.init()
+        }
+        return footerActionsViewModelFactory.create(lifecycleOwner)
+    }
+
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch {
+                shadeInteractor.shadeMode.collect { shadeMode ->
+                    if (shadeMode == ShadeMode.Split) {
+                        sceneInteractor.snapToScene(Scenes.Shade, "Unfold while on QS")
+                    }
+                }
+            }
+            awaitCancellation()
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickSettingsSceneContentViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
deleted file mode 100644
index 7258882..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModel.kt
+++ /dev/null
@@ -1,96 +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.qs.ui.viewmodel
-
-import androidx.lifecycle.LifecycleOwner
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
-import com.android.systemui.qs.FooterActionsController
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.qs.ui.adapter.QSSceneAdapter
-import com.android.systemui.scene.domain.interactor.SceneBackInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
-import java.util.concurrent.atomic.AtomicBoolean
-import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-
-/** Models UI state and handles user input for the quick settings scene. */
-@SysUISingleton
-class QuickSettingsSceneViewModel
-@Inject
-constructor(
-    val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
-    val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
-    val qsSceneAdapter: QSSceneAdapter,
-    private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
-    private val footerActionsController: FooterActionsController,
-    sceneBackInteractor: SceneBackInteractor,
-    val mediaCarouselInteractor: MediaCarouselInteractor,
-) {
-    private val backScene: Flow<SceneKey> =
-        sceneBackInteractor.backScene
-            .filter { it != Scenes.QuickSettings }
-            .map { it ?: Scenes.Shade }
-
-    val destinationScenes: Flow<Map<UserAction, UserActionResult>> =
-        combine(
-            qsSceneAdapter.isCustomizerShowing,
-            backScene,
-        ) { isCustomizing, backScene ->
-            buildMap<UserAction, UserActionResult> {
-                if (isCustomizing) {
-                    // TODO(b/332749288) Empty map so there are no back handlers and back can close
-                    // customizer
-
-                    // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
-                    // while customizing
-                } else {
-                    put(Back, UserActionResult(backScene))
-                    put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
-                    put(
-                        Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
-                        UserActionResult(SceneFamilies.Home),
-                    )
-                }
-            }
-        }
-
-    val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasAnyMediaOrRecommendation
-
-    private val footerActionsControllerInitialized = AtomicBoolean(false)
-
-    fun getFooterActionsViewModel(lifecycleOwner: LifecycleOwner): FooterActionsViewModel {
-        if (footerActionsControllerInitialized.compareAndSet(false, true)) {
-            footerActionsController.init()
-        }
-        return footerActionsViewModelFactory.create(lifecycleOwner)
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
new file mode 100644
index 0000000..61c4c8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** Models the UI state for the user actions for navigating to other scenes or overlays. */
+class QuickSettingsShadeOverlayActionsViewModel @AssistedInject constructor() :
+    UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        setActions(
+            buildMap {
+                put(Swipe.Up, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+                put(Back, UserActionResult.HideOverlay(Overlays.QuickSettingsShade))
+            }
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickSettingsShadeOverlayActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.kt
new file mode 100644
index 0000000..3b97d82
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModel.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.ui.viewmodel
+
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Models UI state used to render the content of the quick settings shade overlay.
+ *
+ * Different from [QuickSettingsShadeOverlayActionsViewModel], which only models user actions that
+ * can be performed to navigate to other scenes.
+ */
+class QuickSettingsShadeOverlayContentViewModel
+@AssistedInject
+constructor(
+    val sceneInteractor: SceneInteractor,
+    val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
+    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+) {
+    fun onScrimClicked() {
+        sceneInteractor.hideOverlay(
+            overlay = Overlays.QuickSettingsShade,
+            loggingReason = "Shade scrim clicked",
+        )
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickSettingsShadeOverlayContentViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
deleted file mode 100644
index d2967b8..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModel.kt
+++ /dev/null
@@ -1,72 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.qs.ui.viewmodel
-
-import com.android.compose.animation.scene.Back
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeAlignment
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.map
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [QuickSettingsShadeSceneContentViewModel] which models the _content_ of the
- * scene.
- */
-class QuickSettingsShadeSceneActionsViewModel
-@AssistedInject
-constructor(
-    private val shadeInteractor: ShadeInteractor,
-    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) : SceneActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        quickSettingsContainerViewModel.editModeViewModel.isEditing
-            .map { editing ->
-                buildMap {
-                    put(
-                        if (shadeInteractor.shadeAlignment == ShadeAlignment.Top) {
-                            Swipe.Up
-                        } else {
-                            Swipe.Down
-                        },
-                        UserActionResult(SceneFamilies.Home)
-                    )
-                    if (!editing) {
-                        put(Back, UserActionResult(SceneFamilies.Home))
-                    }
-                }
-            }
-            .collectLatest { actions -> setActions(actions) }
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): QuickSettingsShadeSceneActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
index abfca4b..d01b33b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModel.kt
@@ -14,28 +14,22 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.qs.ui.viewmodel
 
-import com.android.systemui.lifecycle.SysUiViewModel
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /**
  * Models UI state used to render the content of the quick settings shade scene.
  *
- * Different from [QuickSettingsShadeSceneActionsViewModel], which only models user actions that can
+ * Different from [QuickSettingsShadeUserActionsViewModel], which only models user actions that can
  * be performed to navigate to other scenes.
  */
 class QuickSettingsShadeSceneContentViewModel
 @AssistedInject
 constructor(
-    val overlayShadeViewModelFactory: OverlayShadeViewModel.Factory,
     val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
-) : SysUiViewModel() {
+) {
 
     @AssistedFactory
     interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
new file mode 100644
index 0000000..d3dc302
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models the UI state for the user actions that the user can perform to navigate to other scenes.
+ *
+ * Different from the [QuickSettingsShadeSceneContentViewModel] which models the _content_ of the
+ * scene.
+ */
+class QuickSettingsShadeUserActionsViewModel
+@AssistedInject
+constructor(
+    val quickSettingsContainerViewModel: QuickSettingsContainerViewModel,
+) : UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        quickSettingsContainerViewModel.editModeViewModel.isEditing
+            .map { editing ->
+                buildMap {
+                    put(Swipe.Up, UserActionResult(SceneFamilies.Home))
+                    if (!editing) {
+                        put(Back, UserActionResult(SceneFamilies.Home))
+                    }
+                }
+            }
+            .collect { actions -> setActions(actions) }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickSettingsShadeUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
new file mode 100644
index 0000000..54e5cac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsUserActionsViewModel.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.qs.ui.viewmodel
+
+import com.android.compose.animation.scene.Back
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.domain.interactor.SceneBackInteractor
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+/**
+ * Models the UI state needed to figure out which user actions can trigger navigation from the quick
+ * settings scene to other scenes.
+ *
+ * Different from [QuickSettingsSceneContentViewModel] that models UI state needed for rendering the
+ * content of the quick settings scene.
+ */
+class QuickSettingsUserActionsViewModel
+@AssistedInject
+constructor(
+    private val qsSceneAdapter: QSSceneAdapter,
+    sceneBackInteractor: SceneBackInteractor,
+) : UserActionsViewModel() {
+
+    private val backScene: Flow<SceneKey> =
+        sceneBackInteractor.backScene
+            .filter { it != Scenes.QuickSettings }
+            .map { it ?: Scenes.Shade }
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        combine(
+                qsSceneAdapter.isCustomizerShowing,
+                backScene,
+            ) { isCustomizing, backScene ->
+                buildMap<UserAction, UserActionResult> {
+                    if (isCustomizing) {
+                        // TODO(b/332749288) Empty map so there are no back handlers and back can
+                        // close
+                        // customizer
+
+                        // TODO(b/330200163) Add an Up from Bottom to be able to collapse the shade
+                        // while customizing
+                    } else {
+                        put(Back, UserActionResult(backScene))
+                        put(Swipe(SwipeDirection.Up), UserActionResult(backScene))
+                        put(
+                            Swipe(fromSource = Edge.Bottom, direction = SwipeDirection.Up),
+                            UserActionResult(SceneFamilies.Home),
+                        )
+                    }
+                }
+            }
+            .collect { actions -> setActions(actions) }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): QuickSettingsUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 15366d5..000781a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -26,7 +26,6 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
-import static com.android.systemui.Flags.glanceableHubBackGesture;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER;
@@ -86,10 +85,10 @@
 import com.android.internal.util.ScreenshotRequest;
 import com.android.systemui.Dumpable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.contextualeducation.GestureType;
 import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -231,7 +230,7 @@
                         // If scene framework is enabled, set the scene container window to
                         // visible and let the touch "slip" into that window.
                         if (SceneContainerFlag.isEnabled()) {
-                            mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
+                            mSceneInteractor.get().onRemoteUserInputStarted("launcher swipe");
                         } else {
                             mShadeViewControllerLazy.get().startInputFocusTransfer();
                         }
@@ -267,7 +266,7 @@
                 if (SceneContainerFlag.isEnabled()) {
                     int action = event.getActionMasked();
                     if (action == ACTION_DOWN) {
-                        mSceneInteractor.get().onRemoteUserInteractionStarted(
+                        mSceneInteractor.get().onRemoteUserInputStarted(
                                 "trackpad swipe");
                     } else if (action == ACTION_UP) {
                         mSceneInteractor.get().changeScene(
@@ -333,6 +332,13 @@
         }
 
         @Override
+        public void updateContextualEduStats(boolean isTrackpadGesture, String gestureType) {
+            verifyCallerAndClearCallingIdentityPostMain("updateContextualEduStats",
+                    () -> mHandler.post(() -> OverviewProxyService.this.updateContextualEduStats(
+                            isTrackpadGesture, GestureType.valueOf(gestureType))));
+        }
+
+        @Override
         public void setHomeRotationEnabled(boolean enabled) {
             verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
                     mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
@@ -830,8 +836,7 @@
                 .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing)
                 .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing)
                 .setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming)
-                .setFlag(SYSUI_STATE_COMMUNAL_HUB_SHOWING,
-                        glanceableHubBackGesture() && communalShowing)
+                .setFlag(SYSUI_STATE_COMMUNAL_HUB_SHOWING, communalShowing)
                 .commitUpdate(mContext.getDisplayId());
     }
 
@@ -889,11 +894,21 @@
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
+
+        // Avoid creating TouchInteractionService because the System user in HSUM mode does not
+        // interact with UI elements
+        UserHandle currentUser = UserHandle.of(mUserTracker.getUserId());
+        if (UserManager.isHeadlessSystemUserMode() && currentUser.isSystem()) {
+            Log.w(TAG_OPS,
+                    "Skipping connection to TouchInteractionService for the System user in HSUM "
+                            + "mode.");
+            return;
+        }
         try {
             mBound = mContext.bindServiceAsUser(mQuickStepIntent,
                     mOverviewServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                    UserHandle.of(mUserTracker.getUserId()));
+                    currentUser);
         } catch (SecurityException e) {
             Log.e(TAG_OPS, "Unable to bind because of security error", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
index 14dfcc5..b0eaf9f 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/CustomTraceState.kt
@@ -21,6 +21,10 @@
 import com.android.traceur.PresetTraceConfigs.getDefaultConfig
 import com.android.traceur.TraceConfig
 
+/**
+ * This class encapsulates the values that go into a customized record issue trace config, part of
+ * the RecordIssueTile feature. This class stores the last configuration chosen by power users.
+ */
 class CustomTraceState(private val prefs: SharedPreferences) {
 
     private var enabledTags: Set<String>?
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 863a899..3d6d00e 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -24,6 +24,7 @@
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
+import android.provider.Settings
 import android.util.Log
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -90,7 +91,16 @@
                 // ViewCapture needs to save it's data before it is disabled, or else the data will
                 // be lost. This is expected to change in the near future, and when that happens
                 // this line should be removed.
-                bgExecutor.execute { traceurMessageSender.stopTracing() }
+                bgExecutor.execute {
+                    if (issueRecordingState.traceConfig.longTrace) {
+                        Settings.Global.putInt(
+                            contentResolver,
+                            NOTIFY_SESSION_ENDED_SETTING,
+                            DISABLED
+                        )
+                    }
+                    traceurMessageSender.stopTracing()
+                }
                 issueRecordingState.isRecording = false
             }
             ACTION_SHARE -> {
@@ -125,6 +135,8 @@
     companion object {
         private const val TAG = "IssueRecordingService"
         private const val CHANNEL_ID = "issue_record"
+        private const val NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended"
+        private const val DISABLED = 0
 
         /**
          * Get an intent to stop the issue recording service.
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 16642ab..51c437dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -52,9 +52,20 @@
         get() = prefs.getBoolean(HAS_APPROVED_SCREEN_RECORDING, false)
         private set(value) = prefs.edit().putBoolean(HAS_APPROVED_SCREEN_RECORDING, value).apply()
 
+    // Store the index of the issue type because res ids are generated at compile time and change
+    // in value from one build to another. The index will not change between package versions.
+    private var issueTypeIndex: Int
+        get() = prefs.getInt(KEY_ISSUE_TYPE_INDEX, ISSUE_TYPE_NOT_SET)
+        set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_INDEX, value).apply()
+
     var issueTypeRes
-        get() = prefs.getInt(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
-        set(value) = prefs.edit().putInt(KEY_ISSUE_TYPE_RES, value).apply()
+        get() =
+            // If the user has never used the record issue tile, we don't show a default issue type
+            if (issueTypeIndex == ISSUE_TYPE_NOT_SET) ISSUE_TYPE_NOT_SET
+            else ALL_ISSUE_TYPES.keys.toIntArray()[issueTypeIndex]
+        set(value) {
+            issueTypeIndex = ALL_ISSUE_TYPES.keys.toIntArray().indexOf(value)
+        }
 
     val traceConfig: TraceConfig
         get() = ALL_ISSUE_TYPES[issueTypeRes] ?: customTraceState.traceConfig
@@ -89,17 +100,17 @@
         private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
         private const val KEY_RECORD_SCREEN = "key_recordScreen"
         private const val KEY_TAG_TITLES = "key_tagTitles"
-        const val KEY_ISSUE_TYPE_RES = "key_issueTypeRes"
+        const val KEY_ISSUE_TYPE_INDEX = "key_issueTypeIndex"
         const val ISSUE_TYPE_NOT_SET = -1
         const val TAG_TITLE_DELIMITER = ": "
 
-        val ALL_ISSUE_TYPES: Map<Int, TraceConfig?> =
-            hashMapOf(
+        val ALL_ISSUE_TYPES: LinkedHashMap<Int, TraceConfig?> =
+            linkedMapOf(
                 Pair(R.string.performance, PresetTraceConfigs.getPerformanceConfig()),
                 Pair(R.string.user_interface, PresetTraceConfigs.getUiConfig()),
                 Pair(R.string.battery, PresetTraceConfigs.getBatteryConfig()),
                 Pair(R.string.thermal, PresetTraceConfigs.getThermalConfig()),
-                Pair(R.string.custom, null),
+                Pair(R.string.custom, null), // Null means we are using a custom trace config
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index f8b3ce1..ed67e64 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDisabledDialogDelegate
 import com.android.systemui.recordissue.IssueRecordingState.Companion.ALL_ISSUE_TYPES
 import com.android.systemui.recordissue.IssueRecordingState.Companion.ISSUE_TYPE_NOT_SET
-import com.android.systemui.recordissue.IssueRecordingState.Companion.KEY_ISSUE_TYPE_RES
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -51,6 +50,8 @@
 import dagger.assisted.AssistedInject
 import java.util.concurrent.Executor
 
+private const val EXTRA_ISSUE_TYPE_RES = "extra_issueTypeRes"
+
 class RecordIssueDialogDelegate
 @AssistedInject
 constructor(
@@ -170,7 +171,7 @@
             PopupMenu.OnMenuItemClickListener {
                 issueTypeButton.text = it.title
                 state.issueTypeRes =
-                    it.intent?.getIntExtra(KEY_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
+                    it.intent?.getIntExtra(EXTRA_ISSUE_TYPE_RES, ISSUE_TYPE_NOT_SET)
                         ?: ISSUE_TYPE_NOT_SET
                 onIssueTypeSelected.run()
                 true
@@ -181,7 +182,7 @@
                 if (it != state.issueTypeRes) {
                     iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
                 }
-                intent = Intent().putExtra(KEY_ISSUE_TYPE_RES, it)
+                intent = Intent().putExtra(EXTRA_ISSUE_TYPE_RES, it)
 
                 if (it == R.string.custom) {
                     setOnMenuItemClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
index efb9375..7a57fba 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/EmptySceneModule.kt
@@ -16,7 +16,8 @@
 
 package com.android.systemui.scene
 
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
 import dagger.Module
 import dagger.Provides
 import dagger.multibindings.ElementsIntoSet
@@ -29,4 +30,10 @@
     fun emptySceneSet(): Set<Scene> {
         return emptySet()
     }
+
+    @Provides
+    @ElementsIntoSet
+    fun emptyOverlaySet(): Set<Overlay> {
+        return emptySet()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 6e89973..00944b8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
 import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.domain.startable.StatusBarStartable
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.flag.DualShade
@@ -42,8 +43,10 @@
         [
             EmptySceneModule::class,
             GoneSceneModule::class,
+            NotificationsShadeOverlayModule::class,
             NotificationsShadeSceneModule::class,
             NotificationsShadeSessionModule::class,
+            QuickSettingsShadeOverlayModule::class,
             QuickSettingsSceneModule::class,
             ShadeSceneModule::class,
             SceneDomainModule::class,
@@ -99,6 +102,11 @@
                         Scenes.Shade.takeUnless { DualShade.isEnabled },
                     ),
                 initialSceneKey = Scenes.Gone,
+                overlayKeys =
+                    listOfNotNull(
+                        Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+                        Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
+                    ),
                 navigationDistances =
                     mapOf(
                             Scenes.Gone to 0,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 7d63b4c..4061ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.scene
 
 import com.android.systemui.CoreStartable
-import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlagsModule
 import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
 import com.android.systemui.scene.domain.SceneDomainModule
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
@@ -28,6 +27,7 @@
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
 import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.domain.startable.StatusBarStartable
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.flag.DualShade
@@ -43,13 +43,14 @@
         [
             BouncerSceneModule::class,
             CommunalSceneModule::class,
-            ComposeBouncerFlagsModule::class,
             EmptySceneModule::class,
             GoneSceneModule::class,
             LockscreenSceneModule::class,
             QuickSettingsSceneModule::class,
             ShadeSceneModule::class,
+            QuickSettingsShadeOverlayModule::class,
             QuickSettingsShadeSceneModule::class,
+            NotificationsShadeOverlayModule::class,
             NotificationsShadeSceneModule::class,
             NotificationsShadeSessionModule::class,
             SceneDomainModule::class,
@@ -108,6 +109,11 @@
                         Scenes.Shade.takeUnless { DualShade.isEnabled },
                     ),
                 initialSceneKey = Scenes.Lockscreen,
+                overlayKeys =
+                    listOfNotNull(
+                        Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
+                        Overlays.QuickSettingsShade.takeIf { DualShade.isEnabled },
+                    ),
                 navigationDistances =
                     mapOf(
                             Scenes.Gone to 0,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index 9a7eef8..16ed59f4 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -53,6 +53,7 @@
                     Scenes.Bouncer,
                 ),
             initialSceneKey = Scenes.Lockscreen,
+            overlayKeys = emptyList(),
             navigationDistances =
                 mapOf(
                     Scenes.Gone to 0,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneStack.kt b/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneStack.kt
index d3e529c..323bb3d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneStack.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneStack.kt
@@ -55,6 +55,9 @@
     }
 }
 
+/** Does this stack contain the given [sceneKey]? O(N) */
+fun SceneStack.contains(sceneKey: SceneKey): Boolean = asIterable().any { it == sceneKey }
+
 /**
  * Returns a new [SceneStack] containing the given [scenes], ordered such that the first argument is
  * the head returned from [peek], then the second, and so forth.
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 3e2c630..d60f05e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -18,7 +18,9 @@
 
 package com.android.systemui.scene.data.repository
 
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.dagger.SysUISingleton
@@ -43,11 +45,27 @@
 @Inject
 constructor(
     @Application applicationScope: CoroutineScope,
-    private val config: SceneContainerConfig,
+    config: SceneContainerConfig,
     private val dataSource: SceneDataSource,
 ) {
+    /**
+     * The keys of all scenes and overlays in the container.
+     *
+     * They will be sorted in z-order such that the last one is the one that should be rendered on
+     * top of all previous ones.
+     */
+    val allContentKeys: List<ContentKey> = config.sceneKeys + config.overlayKeys
+
     val currentScene: StateFlow<SceneKey> = dataSource.currentScene
 
+    /**
+     * The current set of overlays to be shown (may be empty).
+     *
+     * Note that during a transition between overlays, a different set of overlays may be rendered -
+     * but only the ones in this set are considered the current overlays.
+     */
+    val currentOverlays: StateFlow<Set<OverlayKey>> = dataSource.currentOverlays
+
     private val _isVisible = MutableStateFlow(true)
     val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
 
@@ -56,7 +74,10 @@
      *
      * For more information see the logic in `SceneInteractor` that mutates this.
      */
-    val isRemoteUserInteractionOngoing = MutableStateFlow(false)
+    val isRemoteUserInputOngoing = MutableStateFlow(false)
+
+    /** Whether there's ongoing user input on the scene container Composable hierarchy */
+    val isSceneContainerUserInputOngoing = MutableStateFlow(false)
 
     private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
     private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
@@ -69,16 +90,6 @@
                 initialValue = defaultTransitionState,
             )
 
-    /**
-     * Returns the keys to all scenes in the container.
-     *
-     * The scenes will be sorted in z-order such that the last one is the one that should be
-     * rendered on top of all previous ones.
-     */
-    fun allSceneKeys(): List<SceneKey> {
-        return config.sceneKeys
-    }
-
     fun changeScene(
         toScene: SceneKey,
         transitionKey: TransitionKey? = null,
@@ -97,6 +108,48 @@
         )
     }
 
+    /**
+     * Request to show [overlay] so that it animates in from [currentScene] and ends up being
+     * visible on screen.
+     *
+     * After this returns, this overlay will be included in [currentOverlays]. This does nothing if
+     * [overlay] is already shown.
+     */
+    fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey? = null) {
+        dataSource.showOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    /**
+     * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being
+     * visible on screen.
+     *
+     * After this returns, this overlay will not be included in [currentOverlays]. This does nothing
+     * if [overlay] is already hidden.
+     */
+    fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey? = null) {
+        dataSource.hideOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    /**
+     * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up
+     * being visible.
+     *
+     * This throws if [from] is not currently shown or if [to] is already shown.
+     */
+    fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey? = null) {
+        dataSource.replaceOverlay(
+            from = from,
+            to = to,
+            transitionKey = transitionKey,
+        )
+    }
+
     /** Sets whether the container is visible. */
     fun setVisible(isVisible: Boolean) {
         _isVisible.value = isVisible
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
index c176cca..2d40845 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
@@ -58,29 +58,20 @@
 
     fun onSceneChange(from: SceneKey, to: SceneKey) {
         check(from != to) { "from == to, from=${from.debugName}, to=${to.debugName}" }
-        when (stackOperation(from, to)) {
-            Clear -> {
-                _backStack.value = sceneStackOf()
-            }
-            Push -> {
-                _backStack.update { s -> s.push(from) }
-            }
-            Pop -> {
-                _backStack.update { s ->
-                    checkNotNull(s.pop()) { "Cannot pop ${from.debugName} when stack is empty" }
-                        .also {
-                            val popped = s.peek()
-                            check(popped == to) {
-                                "Expected to pop ${to.debugName} but instead popped ${popped?.debugName}"
-                            }
-                        }
-                }
+
+        _backStack.update { stack ->
+            when (stackOperation(from, to, stack)) {
+                null -> stack
+                Clear -> sceneStackOf()
+                Push -> stack.push(from)
+                Pop ->
+                    checkNotNull(stack.pop()) { "Cannot pop ${from.debugName} when stack is empty" }
             }
         }
         logger.logSceneBackStack(backStack.value.asIterable())
     }
 
-    private fun stackOperation(from: SceneKey, to: SceneKey): StackOperation {
+    private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
         val fromDistance =
             checkNotNull(sceneContainerConfig.navigationDistances[from]) {
                 "No distance mapping for scene \"${from.debugName}\"!"
@@ -93,6 +84,7 @@
         return when {
             toDistance == 0 -> Clear
             toDistance > fromDistance -> Push
+            stack.peek() != to -> null
             toDistance < fromDistance -> Pop
             else ->
                 error(
@@ -103,7 +95,10 @@
     }
 
     private sealed interface StackOperation
+
     private data object Clear : StackOperation
+
     private data object Push : StackOperation
+
     private data object Pop : StackOperation
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 2d510e1..04620d6 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -118,8 +118,11 @@
         get() =
             when (this) {
                 is ObservableTransitionState.Idle -> currentScene.canBeOccluded
-                is ObservableTransitionState.Transition ->
+                is ObservableTransitionState.Transition.ChangeScene ->
                     fromScene.canBeOccluded && toScene.canBeOccluded
+                is ObservableTransitionState.Transition.ReplaceOverlay,
+                is ObservableTransitionState.Transition.ShowOrHideOverlay ->
+                    TODO("b/359173565: Handle overlay transitions")
             }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 5885193..0d24adc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.scene.domain.interactor
 
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.dagger.SysUISingleton
@@ -28,8 +30,6 @@
 import com.android.systemui.scene.shared.logger.SceneLogger
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.util.kotlin.getValue
-import com.android.systemui.util.kotlin.pairwiseBy
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -53,6 +53,7 @@
  * other feature modules should depend on and call into this class when their parts of the
  * application state change.
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class SceneInteractor
 @Inject
@@ -78,25 +79,28 @@
     private val onSceneAboutToChangeListener = mutableSetOf<OnSceneAboutToChangeListener>()
 
     /**
+     * The keys of all scenes and overlays in the container.
+     *
+     * They will be sorted in z-order such that the last one is the one that should be rendered on
+     * top of all previous ones.
+     */
+    val allContentKeys: List<ContentKey> = repository.allContentKeys
+
+    /**
      * The current scene.
      *
      * Note that during a transition between scenes, more than one scene might be rendered but only
      * one is considered the committed/current scene.
      */
-    val currentScene: StateFlow<SceneKey> =
-        repository.currentScene
-            .pairwiseBy(initialValue = repository.currentScene.value) { from, to ->
-                logger.logSceneChangeCommitted(
-                    from = from,
-                    to = to,
-                )
-                to
-            }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
-                initialValue = repository.currentScene.value,
-            )
+    val currentScene: StateFlow<SceneKey> = repository.currentScene
+
+    /**
+     * The current set of overlays to be shown (may be empty).
+     *
+     * Note that during a transition between overlays, a different set of overlays may be rendered -
+     * but only the ones in this set are considered the current overlays.
+     */
+    val currentOverlays: StateFlow<Set<OverlayKey>> = repository.currentOverlays
 
     /**
      * The current state of the transition.
@@ -124,7 +128,15 @@
      */
     val transitioningTo: StateFlow<SceneKey?> =
         transitionState
-            .map { state -> (state as? ObservableTransitionState.Transition)?.toScene }
+            .map { state ->
+                when (state) {
+                    is ObservableTransitionState.Idle -> null
+                    is ObservableTransitionState.Transition.ChangeScene -> state.toScene
+                    is ObservableTransitionState.Transition.ShowOrHideOverlay,
+                    is ObservableTransitionState.Transition.ReplaceOverlay ->
+                        TODO("b/359173565: Handle overlay transitions")
+                }
+            }
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.WhileSubscribed(),
@@ -155,11 +167,11 @@
     val isVisible: StateFlow<Boolean> =
         combine(
                 repository.isVisible,
-                repository.isRemoteUserInteractionOngoing,
+                repository.isRemoteUserInputOngoing,
             ) { isVisible, isRemoteUserInteractionOngoing ->
                 isVisibleInternal(
                     raw = isVisible,
-                    isRemoteUserInteractionOngoing = isRemoteUserInteractionOngoing,
+                    isRemoteUserInputOngoing = isRemoteUserInteractionOngoing,
                 )
             }
             .stateIn(
@@ -169,8 +181,13 @@
             )
 
     /** Whether there's an ongoing remotely-initiated user interaction. */
-    val isRemoteUserInteractionOngoing: StateFlow<Boolean> =
-        repository.isRemoteUserInteractionOngoing
+    val isRemoteUserInteractionOngoing: StateFlow<Boolean> = repository.isRemoteUserInputOngoing
+
+    /**
+     * Whether there's an ongoing user interaction started in the scene container Compose hierarchy.
+     */
+    val isSceneContainerUserInputOngoing: StateFlow<Boolean> =
+        repository.isSceneContainerUserInputOngoing
 
     /**
      * The amount of transition into or out of the given [scene].
@@ -185,8 +202,8 @@
                 }
                 is ObservableTransitionState.Transition -> {
                     when {
-                        transition.toScene == scene -> transition.progress
-                        transition.fromScene == scene -> transition.progress.map { 1f - it }
+                        transition.toContent == scene -> transition.progress
+                        transition.fromContent == scene -> transition.progress.map { 1f - it }
                         else -> flowOf(0f)
                     }
                 }
@@ -194,16 +211,6 @@
         }
     }
 
-    /**
-     * Returns the keys of all scenes in the container.
-     *
-     * The scenes will be sorted in z-order such that the last one is the one that should be
-     * rendered on top of all previous ones.
-     */
-    fun allSceneKeys(): List<SceneKey> {
-        return repository.allSceneKeys()
-    }
-
     fun registerSceneStateProcessor(processor: OnSceneAboutToChangeListener) {
         onSceneAboutToChangeListener.add(processor)
     }
@@ -234,14 +241,15 @@
             return
         }
 
-        logger.logSceneChangeRequested(
+        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(resolvedScene, sceneState) }
+
+        logger.logSceneChanged(
             from = currentSceneKey,
             to = resolvedScene,
             reason = loggingReason,
             isInstant = false,
         )
 
-        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(resolvedScene, sceneState) }
         repository.changeScene(resolvedScene, transitionKey)
     }
 
@@ -274,7 +282,7 @@
             return
         }
 
-        logger.logSceneChangeRequested(
+        logger.logSceneChanged(
             from = currentSceneKey,
             to = resolvedScene,
             reason = loggingReason,
@@ -285,12 +293,111 @@
     }
 
     /**
+     * Request to show [overlay] so that it animates in from [currentScene] and ends up being
+     * visible on screen.
+     *
+     * After this returns, this overlay will be included in [currentOverlays]. This does nothing if
+     * [overlay] is already shown.
+     *
+     * @param overlay The overlay to be shown
+     * @param loggingReason The reason why the transition is requested, for logging purposes
+     * @param transitionKey The transition key for this animated transition
+     */
+    @JvmOverloads
+    fun showOverlay(
+        overlay: OverlayKey,
+        loggingReason: String,
+        transitionKey: TransitionKey? = null,
+    ) {
+        if (!validateOverlayChange(to = overlay, loggingReason = loggingReason)) {
+            return
+        }
+
+        logger.logOverlayChangeRequested(
+            to = overlay,
+            reason = loggingReason,
+        )
+
+        repository.showOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    /**
+     * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being
+     * visible on screen.
+     *
+     * After this returns, this overlay will not be included in [currentOverlays]. This does nothing
+     * if [overlay] is already hidden.
+     *
+     * @param overlay The overlay to be hidden
+     * @param loggingReason The reason why the transition is requested, for logging purposes
+     * @param transitionKey The transition key for this animated transition
+     */
+    @JvmOverloads
+    fun hideOverlay(
+        overlay: OverlayKey,
+        loggingReason: String,
+        transitionKey: TransitionKey? = null,
+    ) {
+        if (!validateOverlayChange(from = overlay, loggingReason = loggingReason)) {
+            return
+        }
+
+        logger.logOverlayChangeRequested(
+            from = overlay,
+            reason = loggingReason,
+        )
+
+        repository.hideOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    /**
+     * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up
+     * being visible.
+     *
+     * This throws if [from] is not currently shown or if [to] is already shown.
+     *
+     * @param from The overlay to be hidden, if any
+     * @param to The overlay to be shown, if any
+     * @param loggingReason The reason why the transition is requested, for logging purposes
+     * @param transitionKey The transition key for this animated transition
+     */
+    @JvmOverloads
+    fun replaceOverlay(
+        from: OverlayKey,
+        to: OverlayKey,
+        loggingReason: String,
+        transitionKey: TransitionKey? = null,
+    ) {
+        if (!validateOverlayChange(from = from, to = to, loggingReason = loggingReason)) {
+            return
+        }
+
+        logger.logOverlayChangeRequested(
+            from = from,
+            to = to,
+            reason = loggingReason,
+        )
+
+        repository.replaceOverlay(
+            from = from,
+            to = to,
+            transitionKey = transitionKey,
+        )
+    }
+
+    /**
      * Sets the visibility of the container.
      *
      * Please do not call this from outside of the scene framework. If you are trying to force the
      * visibility to visible or invisible, prefer making changes to the existing caller of this
      * method or to upstream state used to calculate [isVisible]; for an example of the latter,
-     * please see [onRemoteUserInteractionStarted] and [onUserInteractionFinished].
+     * please see [onRemoteUserInputStarted] and [onUserInputFinished].
      */
     fun setVisible(isVisible: Boolean, loggingReason: String) {
         val wasVisible = repository.isVisible.value
@@ -307,6 +414,16 @@
     }
 
     /**
+     * Notifies that a scene container user interaction has begun.
+     *
+     * This is a user interaction that originates within the Composable hierarchy of the scene
+     * container.
+     */
+    fun onSceneContainerUserInputStarted() {
+        repository.isSceneContainerUserInputOngoing.value = true
+    }
+
+    /**
      * Notifies that a remote user interaction has begun.
      *
      * This is a user interaction that originates outside of the UI of the scene container and
@@ -317,18 +434,19 @@
      * then rerouted by window manager to System UI. While the user interaction definitely continues
      * within the System UI process and code, it also originates remotely.
      */
-    fun onRemoteUserInteractionStarted(loggingReason: String) {
-        logger.logRemoteUserInteractionStarted(loggingReason)
-        repository.isRemoteUserInteractionOngoing.value = true
+    fun onRemoteUserInputStarted(loggingReason: String) {
+        logger.logRemoteUserInputStarted(loggingReason)
+        repository.isRemoteUserInputOngoing.value = true
     }
 
     /**
      * Notifies that the current user interaction (internally or remotely started, see
-     * [onRemoteUserInteractionStarted]) has finished.
+     * [onSceneContainerUserInputStarted] and [onRemoteUserInputStarted]) has finished.
      */
-    fun onUserInteractionFinished() {
-        logger.logUserInteractionFinished()
-        repository.isRemoteUserInteractionOngoing.value = false
+    fun onUserInputFinished() {
+        logger.logUserInputFinished()
+        repository.isSceneContainerUserInputOngoing.value = false
+        repository.isRemoteUserInputOngoing.value = false
     }
 
     /**
@@ -357,9 +475,9 @@
 
     private fun isVisibleInternal(
         raw: Boolean = repository.isVisible.value,
-        isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
+        isRemoteUserInputOngoing: Boolean = repository.isRemoteUserInputOngoing.value,
     ): Boolean {
-        return raw || isRemoteUserInteractionOngoing
+        return raw || isRemoteUserInputOngoing
     }
 
     /**
@@ -378,12 +496,12 @@
         to: SceneKey,
         loggingReason: String,
     ): Boolean {
-        if (!repository.allSceneKeys().contains(to)) {
+        if (to !in repository.allContentKeys) {
             return false
         }
 
         val inMidTransitionFromGone =
-            (transitionState.value as? ObservableTransitionState.Transition)?.fromScene ==
+            (transitionState.value as? ObservableTransitionState.Transition)?.fromContent ==
                 Scenes.Gone
         val isChangeAllowed =
             to != Scenes.Gone ||
@@ -399,6 +517,34 @@
         return from != to
     }
 
+    /**
+     * Validates that the given overlay change is allowed.
+     *
+     * Will throw a runtime exception for illegal states.
+     *
+     * @param from The overlay to be hidden, if any
+     * @param to The overlay to be shown, if any
+     * @param loggingReason The reason why the transition is requested, for logging purposes
+     * @return `true` if the scene change is valid; `false` if it shouldn't happen
+     */
+    private fun validateOverlayChange(
+        from: OverlayKey? = null,
+        to: OverlayKey? = null,
+        loggingReason: String,
+    ): Boolean {
+        check(from != null || to != null) {
+            "No overlay key provided for requested change." +
+                " Current transition state is ${transitionState.value}." +
+                " Logging reason for overlay change was: $loggingReason"
+        }
+
+        val isFromValid = (from == null) || (from in currentOverlays.value)
+        val isToValid =
+            (to == null) || (to !in currentOverlays.value && to in repository.allContentKeys)
+
+        return isFromValid && isToValid && from != to
+    }
+
     /** Returns a flow indicating if the currently visible scene can be resolved from [family]. */
     fun isCurrentSceneInFamily(family: SceneKey): Flow<Boolean> =
         currentScene.map { currentScene -> isSceneInFamily(currentScene, family) }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
index 9c2b992..738b184 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -38,6 +38,8 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapConcat
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -73,22 +75,34 @@
             sceneInteractorProvider
                 .get()
                 .transitionState
-                .map { state ->
+                .flatMapConcat { state ->
                     when (state) {
                         is ObservableTransitionState.Idle ->
-                            state.currentScene == Scenes.Shade ||
-                                state.currentScene == Scenes.NotificationsShade ||
-                                state.currentScene == Scenes.QuickSettingsShade ||
-                                state.currentScene == Scenes.Lockscreen
+                            flowOf(
+                                state.currentScene == Scenes.Shade ||
+                                    state.currentScene == Scenes.NotificationsShade ||
+                                    state.currentScene == Scenes.QuickSettingsShade ||
+                                    state.currentScene == Scenes.Lockscreen
+                            )
                         is ObservableTransitionState.Transition ->
-                            state.toScene == Scenes.Shade ||
-                                state.toScene == Scenes.NotificationsShade ||
-                                state.toScene == Scenes.QuickSettingsShade ||
-                                state.toScene == Scenes.Lockscreen ||
-                                state.fromScene == Scenes.Shade ||
-                                state.fromScene == Scenes.NotificationsShade ||
-                                state.fromScene == Scenes.QuickSettingsShade ||
-                                state.fromScene == Scenes.Lockscreen
+                            if (
+                                state.fromContent == Scenes.Bouncer &&
+                                    state.toContent == Scenes.Lockscreen
+                            ) {
+                                // Lockscreen is not visible during preview stage of predictive back
+                                state.isInPreviewStage.map { !it }
+                            } else {
+                                flowOf(
+                                    state.toContent == Scenes.Shade ||
+                                        state.toContent == Scenes.NotificationsShade ||
+                                        state.toContent == Scenes.QuickSettingsShade ||
+                                        state.toContent == Scenes.Lockscreen ||
+                                        state.fromContent == Scenes.Shade ||
+                                        state.fromContent == Scenes.NotificationsShade ||
+                                        state.fromContent == Scenes.QuickSettingsShade ||
+                                        state.fromContent == Scenes.Lockscreen
+                                )
+                            }
                     }
                 }
                 .distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 5b50133..e251c9e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.CoreStartable
 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
 import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
@@ -35,6 +36,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.DisplayId
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
 import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource
@@ -61,6 +63,7 @@
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
+import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor
@@ -77,6 +80,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.combine
@@ -106,6 +110,7 @@
     @Application private val applicationScope: CoroutineScope,
     private val sceneInteractor: SceneInteractor,
     private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor,
     private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
     private val bouncerInteractor: BouncerInteractor,
     private val keyguardInteractor: KeyguardInteractor,
@@ -132,6 +137,8 @@
     private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
     private val dismissCallbackRegistry: DismissCallbackRegistry,
     private val statusBarStateController: SysuiStatusBarStateController,
+    private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    private val vibratorHelper: VibratorHelper,
 ) : CoreStartable {
     private val centralSurfaces: CentralSurfaces?
         get() = centralSurfacesOptLazy.get().getOrNull()
@@ -146,11 +153,12 @@
             respondToFalsingDetections()
             hydrateInteractionState()
             handleBouncerOverscroll()
+            handleDeviceEntryHapticsWhileDeviceLocked()
             hydrateWindowController()
             hydrateBackStack()
             resetShadeSessions()
             handleKeyguardEnabledness()
-            notifyKeyguardDismissCallbacks()
+            notifyKeyguardDismissCancelledCallbacks()
             refreshLockscreenEnabled()
         } else {
             sceneLogger.logFrameworkEnabled(
@@ -187,7 +195,7 @@
                             // current scene
                             when (state) {
                                 is ObservableTransitionState.Idle -> state.currentScene
-                                is ObservableTransitionState.Transition -> state.fromScene
+                                is ObservableTransitionState.Transition -> state.fromContent
                             }.let { it == Scenes.Shade || it == Scenes.QuickSettings }
                         }
                         .distinctUntilChanged()
@@ -218,7 +226,7 @@
                                             }
                                         }
                                         is ObservableTransitionState.Transition -> {
-                                            if (state.fromScene == Scenes.Gone) {
+                                            if (state.fromContent == Scenes.Gone) {
                                                 true to "scene transitioning away from Gone"
                                             } else {
                                                 null
@@ -228,13 +236,16 @@
                                 },
                                 headsUpInteractor.isHeadsUpOrAnimatingAway,
                                 occlusionInteractor.invisibleDueToOcclusion,
+                                alternateBouncerInteractor.isVisible,
                             ) {
                                 visibilityForTransitionState,
                                 isHeadsUpOrAnimatingAway,
                                 invisibleDueToOcclusion,
+                                isAlternateBouncerVisible,
                                 ->
                                 when {
                                     isHeadsUpOrAnimatingAway -> true to "showing a HUN"
+                                    isAlternateBouncerVisible -> true to "showing alternate bouncer"
                                     invisibleDueToOcclusion -> false to "invisible due to occlusion"
                                     else -> visibilityForTransitionState
                                 }
@@ -346,14 +357,15 @@
                             is ObservableTransitionState.Idle -> setOf(transitionState.currentScene)
                             is ObservableTransitionState.Transition ->
                                 setOf(
-                                    transitionState.fromScene,
-                                    transitionState.toScene,
+                                    transitionState.fromContent,
+                                    transitionState.toContent,
                                 )
                         }
                     val isOnLockscreen = renderedScenes.contains(Scenes.Lockscreen)
-                    val isOnBouncer = renderedScenes.contains(Scenes.Bouncer)
+                    val isAlternateBouncerVisible = alternateBouncerInteractor.isVisibleState()
+                    val isOnPrimaryBouncer = renderedScenes.contains(Scenes.Bouncer)
                     if (!deviceUnlockStatus.isUnlocked) {
-                        return@mapNotNull if (isOnLockscreen || isOnBouncer) {
+                        return@mapNotNull if (isOnLockscreen || isOnPrimaryBouncer) {
                             // Already on lockscreen or bouncer, no need to change scenes.
                             null
                         } else {
@@ -365,27 +377,50 @@
                     }
 
                     if (
-                        isOnBouncer &&
+                        isOnPrimaryBouncer &&
                             deviceUnlockStatus.deviceUnlockSource == DeviceUnlockSource.TrustAgent
                     ) {
                         uiEventLogger.log(BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS)
                     }
                     when {
-                        isOnBouncer ->
-                            // When the device becomes unlocked in Bouncer, go to previous scene,
-                            // or Gone.
+                        isAlternateBouncerVisible -> {
+                            // When the device becomes unlocked when the alternate bouncer is
+                            // showing, always hide the alternate bouncer and notify dismiss
+                            // succeeded
+                            alternateBouncerInteractor.hide()
+                            dismissCallbackRegistry.notifyDismissSucceeded()
+
+                            // ... and go to Gone or stay on the current scene
+                            if (
+                                isOnLockscreen ||
+                                    !statusBarStateController.leaveOpenOnKeyguardHide()
+                            ) {
+                                Scenes.Gone to
+                                    "device was unlocked with alternate bouncer showing" +
+                                        " and shade didn't need to be left open"
+                            } else {
+                                null
+                            }
+                        }
+                        isOnPrimaryBouncer -> {
+                            // When the device becomes unlocked in primary Bouncer,
+                            // notify dismiss succeeded and
+                            // go to previous scene or Gone.
+                            dismissCallbackRegistry.notifyDismissSucceeded()
                             if (
                                 previousScene.value == Scenes.Lockscreen ||
                                     !statusBarStateController.leaveOpenOnKeyguardHide()
                             ) {
                                 Scenes.Gone to
-                                    "device was unlocked in Bouncer scene and shade" +
+                                    "device was unlocked with bouncer showing and shade" +
                                         " didn't need to be left open"
                             } else {
                                 val prevScene = previousScene.value
                                 (prevScene ?: Scenes.Gone) to
-                                    "device was unlocked in Bouncer scene, from sceneKey=$prevScene"
+                                    "device was unlocked with primary bouncer showing," +
+                                        " from sceneKey=$prevScene"
                             }
+                        }
                         isOnLockscreen ->
                             // The lockscreen should be dismissed automatically in 2 scenarios:
                             // 1. When face auth bypass is enabled and authentication happens while
@@ -432,7 +467,8 @@
                     sceneInteractor.transitionState.value as? ObservableTransitionState.Transition
                         ?: return@collect
                 if (
-                    transition.fromScene == Scenes.Gone && transition.toScene == Scenes.Lockscreen
+                    transition.fromContent == Scenes.Gone &&
+                        transition.toContent == Scenes.Lockscreen
                 ) {
                     switchToScene(
                         targetSceneKey = Scenes.Gone,
@@ -444,10 +480,13 @@
         applicationScope.launch {
             powerInteractor.isAsleep.collect { isAsleep ->
                 if (isAsleep) {
+                    alternateBouncerInteractor.hide()
+                    dismissCallbackRegistry.notifyDismissCancelled()
+
                     switchToScene(
                         targetSceneKey = Scenes.Lockscreen,
                         loggingReason = "device is starting to sleep",
-                        sceneState = keyguardTransitionInteractor.asleepKeyguardState.value,
+                        sceneState = keyguardInteractor.asleepKeyguardState.value,
                     )
                 } else {
                     val canSwipeToEnter = deviceEntryInteractor.canSwipeToEnter.value
@@ -492,6 +531,37 @@
         }
     }
 
+    private fun handleDeviceEntryHapticsWhileDeviceLocked() {
+        applicationScope.launch {
+            deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
+                // Only check for haptics signals before device is entered
+                if (!isDeviceEntered) {
+                    coroutineScope {
+                        launch {
+                            deviceEntryHapticsInteractor.playSuccessHaptic
+                                .sample(sceneInteractor.currentScene)
+                                .collect { currentScene ->
+                                    vibratorHelper.vibrateAuthSuccess(
+                                        "$TAG, $currentScene device-entry::success"
+                                    )
+                                }
+                        }
+
+                        launch {
+                            deviceEntryHapticsInteractor.playErrorHaptic
+                                .sample(sceneInteractor.currentScene)
+                                .collect { currentScene ->
+                                    vibratorHelper.vibrateAuthError(
+                                        "$TAG, $currentScene device-entry::error"
+                                    )
+                                }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     /** Keeps [SysUiState] up-to-date */
     private fun hydrateSystemUiState() {
         applicationScope.launch {
@@ -538,15 +608,6 @@
         }
 
         applicationScope.launch {
-            sceneInteractor.currentScene
-                .map { it == Scenes.Bouncer }
-                .distinctUntilChanged()
-                .collect { isBouncerShowing ->
-                    windowController.setBouncerShowing(isBouncerShowing)
-                }
-        }
-
-        applicationScope.launch {
             occlusionInteractor.invisibleDueToOcclusion.collect { invisibleDueToOcclusion ->
                 windowController.setKeyguardOccluded(invisibleDueToOcclusion)
             }
@@ -662,8 +723,8 @@
                 .filterIsInstance<ObservableTransitionState.Transition>()
                 // Only consider user-initiated (e.g. drags) that go from bouncer to lockscreen.
                 .filter { transition ->
-                    transition.fromScene == Scenes.Bouncer &&
-                        transition.toScene == Scenes.Lockscreen &&
+                    transition.fromContent == Scenes.Bouncer &&
+                        transition.toContent == Scenes.Lockscreen &&
                         transition.isInitiatedByUserInput
                 }
                 .flatMapLatest { it.progress }
@@ -747,15 +808,23 @@
         }
     }
 
-    private fun notifyKeyguardDismissCallbacks() {
+    private fun notifyKeyguardDismissCancelledCallbacks() {
         applicationScope.launch {
-            sceneInteractor.currentScene.pairwise().collect { (from, to) ->
-                when {
-                    from != Scenes.Bouncer -> Unit
-                    to == Scenes.Gone -> dismissCallbackRegistry.notifyDismissSucceeded()
-                    else -> dismissCallbackRegistry.notifyDismissCancelled()
+            combine(
+                    deviceEntryInteractor.isUnlocked,
+                    sceneInteractor.currentScene.pairwise(),
+                ) { isUnlocked, (from, to) ->
+                    when {
+                        from != Scenes.Bouncer -> false
+                        to != Scenes.Gone && !isUnlocked -> true
+                        else -> false
+                    }
                 }
-            }
+                .collect { notifyKeyguardDismissCancelled ->
+                    if (notifyKeyguardDismissCancelled) {
+                        dismissCallbackRegistry.notifyDismissCancelled()
+                    }
+                }
         }
     }
 
@@ -776,4 +845,8 @@
                 .collectLatest { deviceEntryInteractor.refreshLockscreenEnabled() }
         }
     }
+
+    companion object {
+        private const val TAG = "SceneContainerStartable"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index c6f51b3..d1629c7 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -112,7 +112,7 @@
                 // It
                 // happens only when unlocking or when dismissing a dismissible lockscreen.
                 val isTransitioningAwayFromKeyguard =
-                    transitionState is ObservableTransitionState.Transition &&
+                    transitionState is ObservableTransitionState.Transition.ChangeScene &&
                         transitionState.fromScene.isKeyguard() &&
                         transitionState.toScene == Scenes.Gone
 
@@ -120,7 +120,7 @@
                 val isCurrentSceneShade = currentScene.isShade()
                 // This is true when moving into one of the shade scenes when a non-shade scene.
                 val isTransitioningToShade =
-                    transitionState is ObservableTransitionState.Transition &&
+                    transitionState is ObservableTransitionState.Transition.ChangeScene &&
                         !transitionState.fromScene.isShade() &&
                         transitionState.toScene.isShade()
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
index 893f030..d741368 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/StatusBarStartable.kt
@@ -166,7 +166,7 @@
                     StatusBarManager.DISABLE_NONE,
                     disableToken,
                     applicationContext.packageName,
-                    selectedUserInteractor.getSelectedUserId(true),
+                    selectedUserInteractor.getSelectedUserId(),
                 )
             } catch (e: RemoteException) {
                 Log.d(TAG, "Failed to clear flags", e)
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 6c63c97..751448f 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
@@ -27,7 +27,7 @@
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.ComposeLockscreen
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
 import com.android.systemui.statusbar.phone.PredictiveBackSysUiFlag
 
 /** Helper for reading or using the scene container flag state. */
@@ -43,7 +43,7 @@
                 KeyguardBottomAreaRefactor.isEnabled &&
                 KeyguardWmStateRefactor.isEnabled &&
                 MigrateClocksToBlueprint.isEnabled &&
-                NotificationsHeadsUpRefactor.isEnabled &&
+                NotificationThrottleHun.isEnabled &&
                 PredictiveBackSysUiFlag.isEnabled &&
                 DeviceEntryUdfpsRefactor.isEnabled
 
@@ -59,7 +59,7 @@
             KeyguardBottomAreaRefactor.token,
             KeyguardWmStateRefactor.token,
             MigrateClocksToBlueprint.token,
-            NotificationsHeadsUpRefactor.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/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index cf1518e..fb53ddb 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.scene.shared.logger
 
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
@@ -43,7 +44,7 @@
         )
     }
 
-    fun logSceneChangeRequested(
+    fun logSceneChanged(
         from: SceneKey,
         to: SceneKey,
         reason: String,
@@ -60,7 +61,7 @@
             },
             messagePrinter = {
                 buildString {
-                    append("Scene change requested: $str1 → $str2")
+                    append("Scene changed: $str1 → $str2")
                     if (isInstant) {
                         append(" (instant)")
                     }
@@ -70,21 +71,6 @@
         )
     }
 
-    fun logSceneChangeCommitted(
-        from: SceneKey,
-        to: SceneKey,
-    ) {
-        logBuffer.log(
-            tag = TAG,
-            level = LogLevel.INFO,
-            messageInitializer = {
-                str1 = from.toString()
-                str2 = to.toString()
-            },
-            messagePrinter = { "Scene change committed: $str1 → $str2" },
-        )
-    }
-
     fun logSceneTransition(transitionState: ObservableTransitionState) {
         when (transitionState) {
             is ObservableTransitionState.Transition -> {
@@ -92,8 +78,8 @@
                     tag = TAG,
                     level = LogLevel.INFO,
                     messageInitializer = {
-                        str1 = transitionState.fromScene.toString()
-                        str2 = transitionState.toScene.toString()
+                        str1 = transitionState.fromContent.toString()
+                        str2 = transitionState.toContent.toString()
                     },
                     messagePrinter = { "Scene transition started: $str1 → $str2" },
                 )
@@ -109,6 +95,34 @@
         }
     }
 
+    fun logOverlayChangeRequested(
+        from: OverlayKey? = null,
+        to: OverlayKey? = null,
+        reason: String,
+    ) {
+        logBuffer.log(
+            tag = TAG,
+            level = LogLevel.INFO,
+            messageInitializer = {
+                str1 = from?.toString()
+                str2 = to?.toString()
+                str3 = reason
+            },
+            messagePrinter = {
+                buildString {
+                    append("Overlay change requested: ")
+                    if (str1 != null) {
+                        append(str1)
+                        append(if (str2 == null) " (hidden)" else " → $str2")
+                    } else {
+                        append("$str2 (shown)")
+                    }
+                    append(", reason: $str3")
+                }
+            },
+        )
+    }
+
     fun logVisibilityChange(
         from: Boolean,
         to: Boolean,
@@ -130,7 +144,7 @@
         )
     }
 
-    fun logRemoteUserInteractionStarted(
+    fun logRemoteUserInputStarted(
         reason: String,
     ) {
         logBuffer.log(
@@ -141,7 +155,7 @@
         )
     }
 
-    fun logUserInteractionFinished() {
+    fun logUserInputFinished() {
         logBuffer.log(
             tag = TAG,
             level = LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
new file mode 100644
index 0000000..c47a850
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Overlays.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.scene.shared.model
+
+import com.android.compose.animation.scene.OverlayKey
+
+/**
+ * Keys of all known overlays.
+ *
+ * PLEASE KEEP THE KEYS SORTED ALPHABETICALLY.
+ */
+object Overlays {
+    /**
+     * The notifications shade overlay primarily shows a scrollable list of notifications.
+     *
+     * It's used only in the dual shade configuration, where there are two separate shades: one for
+     * notifications (this overlay) and another for [QuickSettingsShade].
+     *
+     * It's not used in the single/accordion configuration (swipe down once to reveal the shade,
+     * swipe down again the to expand quick settings) or in the "split" shade configuration (on
+     * large screens or unfolded foldables, where notifications and quick settings are shown
+     * side-by-side in their own columns).
+     */
+    @JvmField val NotificationsShade = OverlayKey("notifications_shade")
+
+    /**
+     * The quick settings shade overlay shows the quick settings tiles UI.
+     *
+     * It's used only in the dual shade configuration, where there are two separate shades: one for
+     * quick settings (this overlay) and another for [NotificationsShade].
+     *
+     * It's not used in the single/accordion configuration (swipe down once to reveal the shade,
+     * swipe down again the to expand quick settings) or in the "split" shade configuration (on
+     * large screens or unfolded foldables, where notifications and quick settings are shown
+     * side-by-side in their own columns).
+     */
+    @JvmField val QuickSettingsShade = OverlayKey("quick_settings_shade")
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
deleted file mode 100644
index 103b4a5..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ /dev/null
@@ -1,61 +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.scene.shared.model
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.lifecycle.Activatable
-import kotlinx.coroutines.flow.Flow
-
-/**
- * Defines interface for classes that can describe a "scene".
- *
- * In the scene framework, there can be multiple scenes in a single scene "container". The container
- * takes care of rendering the current scene and allowing scenes to be switched from one to another
- * based on either user action (for example, swiping down while on the lock screen scene may switch
- * to the shade scene).
- */
-interface Scene : Activatable {
-
-    /** Uniquely-identifying key for this scene. The key must be unique within its container. */
-    val key: SceneKey
-
-    override suspend fun activate() = Unit
-
-    /**
-     * The mapping between [UserAction] and destination [UserActionResult]s.
-     *
-     * When the scene framework detects a user action, if the current scene has a map entry for that
-     * user action, the framework starts a transition to the scene in the map.
-     *
-     * Once the [Scene] becomes the current one, the scene framework will read this property and set
-     * up a collector to watch for new mapping values. If every map entry provided by the scene, the
-     * framework will set up user input handling for its [UserAction] and, if such a user action is
-     * detected, initiate a transition to the specified [UserActionResult].
-     *
-     * Note that reading from this method does _not_ mean that any user action has occurred.
-     * Instead, the property is read before any user action/gesture is detected so that the
-     * framework can decide whether to set up gesture/input detectors/listeners in case user actions
-     * of the given types ever occur.
-     *
-     * Note that a missing value for a specific [UserAction] means that the user action of the given
-     * type is not currently active in the scene and should be ignored by the framework, while the
-     * current scene is this one.
-     */
-    val destinationScenes: Flow<Map<UserAction, UserActionResult>>
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
index 0a30c31..2311e47 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.shared.model
 
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 
 /** Models the configuration of the scene container. */
@@ -38,6 +39,13 @@
     val initialSceneKey: SceneKey,
 
     /**
+     * The keys to all overlays in the container, sorted by z-order such that the last one renders
+     * on top of all previous ones. Overlay keys within the same container must not repeat but it's
+     * okay to have the same overlay keys in different containers.
+     */
+    val overlayKeys: List<OverlayKey> = emptyList(),
+
+    /**
      * Navigation distance of each scene.
      *
      * The navigation distance is a measure of how many non-back user action "steps" away from the
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
index 034da25..4538d1c 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSource.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.shared.model
 
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import kotlinx.coroutines.flow.StateFlow
@@ -33,6 +34,14 @@
     val currentScene: StateFlow<SceneKey>
 
     /**
+     * The current set of overlays to be shown (may be empty).
+     *
+     * Note that during a transition between overlays, a different set of overlays may be rendered -
+     * but only the ones in this set are considered the current overlays.
+     */
+    val currentOverlays: StateFlow<Set<OverlayKey>>
+
+    /**
      * Asks for an asynchronous scene switch to [toScene], which will use the corresponding
      * installed transition or the one specified by [transitionKey], if provided.
      */
@@ -47,4 +56,40 @@
     fun snapToScene(
         toScene: SceneKey,
     )
+
+    /**
+     * Request to show [overlay] so that it animates in from [currentScene] and ends up being
+     * visible on screen.
+     *
+     * After this returns, this overlay will be included in [currentOverlays]. This does nothing if
+     * [overlay] is already shown.
+     */
+    fun showOverlay(
+        overlay: OverlayKey,
+        transitionKey: TransitionKey? = null,
+    )
+
+    /**
+     * Request to hide [overlay] so that it animates out to [currentScene] and ends up *not* being
+     * visible on screen.
+     *
+     * After this returns, this overlay will not be included in [currentOverlays]. This does nothing
+     * if [overlay] is already hidden.
+     */
+    fun hideOverlay(
+        overlay: OverlayKey,
+        transitionKey: TransitionKey? = null,
+    )
+
+    /**
+     * Replace [from] by [to] so that [from] ends up not being visible on screen and [to] ends up
+     * being visible.
+     *
+     * This throws if [from] is not currently shown or if [to] is already shown.
+     */
+    fun replaceOverlay(
+        from: OverlayKey,
+        to: OverlayKey,
+        transitionKey: TransitionKey? = null,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
index 43c3635..eb4c0f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneDataSourceDelegator.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.scene.shared.model
 
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import kotlinx.coroutines.CoroutineScope
@@ -49,6 +50,15 @@
                 initialValue = config.initialSceneKey,
             )
 
+    override val currentOverlays: StateFlow<Set<OverlayKey>> =
+        delegateMutable
+            .flatMapLatest { delegate -> delegate.currentOverlays }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = emptySet(),
+            )
+
     override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
         delegateMutable.value.changeScene(
             toScene = toScene,
@@ -62,6 +72,28 @@
         )
     }
 
+    override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        delegateMutable.value.showOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        delegateMutable.value.hideOverlay(
+            overlay = overlay,
+            transitionKey = transitionKey,
+        )
+    }
+
+    override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) {
+        delegateMutable.value.replaceOverlay(
+            from = from,
+            to = to,
+            transitionKey = transitionKey,
+        )
+    }
+
     /**
      * Binds the current, dependency injection provided [SceneDataSource] to the given object.
      *
@@ -82,8 +114,21 @@
         override val currentScene: StateFlow<SceneKey> =
             MutableStateFlow(initialSceneKey).asStateFlow()
 
+        override val currentOverlays: StateFlow<Set<OverlayKey>> =
+            MutableStateFlow(emptySet<OverlayKey>()).asStateFlow()
+
         override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit
 
         override fun snapToScene(toScene: SceneKey) = Unit
+
+        override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit
+
+        override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit
+
+        override fun replaceOverlay(
+            from: OverlayKey,
+            to: OverlayKey,
+            transitionKey: TransitionKey?
+        ) = Unit
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
index ef5290f..115d664 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scenes.kt
@@ -54,7 +54,9 @@
      * large screens or unfolded foldables, where notifications and quick settings are shown
      * side-by-side in their own columns).
      */
-    @JvmField val NotificationsShade = SceneKey("notifications_shade")
+    @Deprecated("The notifications shade scene has been replaced by an overlay")
+    @JvmField
+    val NotificationsShade = SceneKey("notifications_shade")
 
     /**
      * The quick settings scene shows the quick setting tiles.
@@ -83,7 +85,9 @@
      * large screens or unfolded foldables, where notifications and quick settings are shown
      * side-by-side in their own columns).
      */
-    @JvmField val QuickSettingsShade = SceneKey("quick_settings_shade")
+    @Deprecated("The quick settings shade scene has been replaced by an overlay")
+    @JvmField
+    val QuickSettingsShade = SceneKey("quick_settings_shade")
 
     /**
      * The shade is the scene that shows a scrollable list of notifications and the minimized
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
index be95441..b9f57f2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/TransitionKeys.kt
@@ -27,14 +27,6 @@
     /** Reference to the gone/lockscreen to shade transition with split shade enabled. */
     val ToSplitShade = TransitionKey("GoneToSplitShade")
 
-    /** Reference to a scene transition that can collapse the shade scene instantly. */
-    val CollapseShadeInstantly = TransitionKey("CollapseShadeInstantly")
-
-    /**
-     * Reference to a scene transition that brings up the shade from the bottom instead of the top.
-     */
-    val OpenBottomShade = TransitionKey("OpenBottomShade")
-
     /**
      * Reference to a scene transition that can collapse the shade scene slightly faster than a
      * normal collapse would.
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 bccbb11..8a2e274 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
@@ -5,15 +5,19 @@
 import android.view.MotionEvent
 import android.view.View
 import android.view.WindowInsets
-import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
 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 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?,
@@ -23,32 +27,37 @@
         attrs,
     ) {
 
-    private lateinit var viewModel: SceneContainerViewModel
-
+    private var motionEventHandler: SceneContainerViewModel.MotionEventHandler? = null
     // TODO(b/298525212): remove once Compose exposes window inset bounds.
     private val windowInsets: MutableStateFlow<WindowInsets?> = MutableStateFlow(null)
 
     fun init(
-        viewModel: SceneContainerViewModel,
+        viewModelFactory: SceneContainerViewModel.Factory,
         containerConfig: SceneContainerConfig,
         sharedNotificationContainer: SharedNotificationContainer,
         scenes: Set<Scene>,
+        overlays: Set<Overlay>,
         layoutInsetController: LayoutInsetsController,
         sceneDataSourceDelegator: SceneDataSourceDelegator,
+        alternateBouncerDependencies: AlternateBouncerDependencies,
     ) {
-        this.viewModel = viewModel
         setLayoutInsetsController(layoutInsetController)
         SceneWindowRootViewBinder.bind(
             view = this@SceneWindowRootView,
-            viewModel = viewModel,
+            viewModelFactory = viewModelFactory,
+            motionEventHandlerReceiver = { motionEventHandler ->
+                this.motionEventHandler = motionEventHandler
+            },
             windowInsets = windowInsets,
             containerConfig = containerConfig,
             sharedNotificationContainer = sharedNotificationContainer,
             scenes = scenes,
+            overlays = overlays,
             onVisibilityChangedInternal = { isVisible ->
                 super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE)
             },
             dataSourceDelegator = sceneDataSourceDelegator,
+            alternateBouncerDependencies = alternateBouncerDependencies,
         )
     }
 
@@ -64,10 +73,10 @@
     }
 
     override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
-        viewModel.onMotionEvent(ev)
+        motionEventHandler?.onMotionEvent(ev)
         return super.dispatchTouchEvent(ev).also {
             TouchLogger.logDispatchTouch(TAG, ev, it)
-            viewModel.onMotionEventComplete()
+            motionEventHandler?.onMotionEventComplete()
         }
     }
 
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 d31d6f4..7f35d73 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
@@ -29,59 +29,87 @@
 import androidx.compose.ui.unit.dp
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.policy.ScreenDecorationsUtils
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.DisplayCutout
 import com.android.systemui.common.ui.compose.windowinsets.ScreenDecorProvider
+import com.android.systemui.keyguard.ui.composable.AlternateBouncer
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
+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.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
-import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
 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 kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
 
+@ExperimentalCoroutinesApi
 object SceneWindowRootViewBinder {
 
     /** Binds between the view and view-model pertaining to a specific scene container. */
     fun bind(
         view: ViewGroup,
-        viewModel: SceneContainerViewModel,
+        viewModelFactory: SceneContainerViewModel.Factory,
+        motionEventHandlerReceiver: (SceneContainerViewModel.MotionEventHandler?) -> Unit,
         windowInsets: StateFlow<WindowInsets?>,
         containerConfig: SceneContainerConfig,
         sharedNotificationContainer: SharedNotificationContainer,
         scenes: Set<Scene>,
+        overlays: Set<Overlay>,
         onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
         dataSourceDelegator: SceneDataSourceDelegator,
+        alternateBouncerDependencies: AlternateBouncerDependencies,
     ) {
         val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
-        val sortedSceneByKey: Map<SceneKey, Scene> = buildMap {
-            containerConfig.sceneKeys.forEach { sceneKey ->
-                val scene =
-                    checkNotNull(unsortedSceneByKey[sceneKey]) {
-                        "Scene not found for key \"$sceneKey\"!"
-                    }
+        val sortedSceneByKey: Map<SceneKey, Scene> =
+            LinkedHashMap<SceneKey, Scene>(containerConfig.sceneKeys.size).apply {
+                containerConfig.sceneKeys.forEach { sceneKey ->
+                    val scene =
+                        checkNotNull(unsortedSceneByKey[sceneKey]) {
+                            "Scene not found for key \"$sceneKey\"!"
+                        }
 
-                put(sceneKey, scene)
+                    put(sceneKey, scene)
+                }
             }
-        }
+
+        val unsortedOverlayByKey: Map<OverlayKey, Overlay> =
+            overlays.associateBy { overlay -> overlay.key }
+        val sortedOverlayByKey: Map<OverlayKey, Overlay> =
+            LinkedHashMap<OverlayKey, Overlay>(containerConfig.overlayKeys.size).apply {
+                containerConfig.overlayKeys.forEach { overlayKey ->
+                    val overlay =
+                        checkNotNull(unsortedOverlayByKey[overlayKey]) {
+                            "Overlay not found for key \"$overlayKey\"!"
+                        }
+
+                    put(overlayKey, overlay)
+                }
+            }
 
         view.repeatWhenAttached {
-            lifecycleScope.launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
+            view.viewModel(
+                traceName = "SceneWindowRootViewBinder",
+                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                factory = { viewModelFactory.create(motionEventHandlerReceiver) },
+            ) { viewModel ->
+                try {
                     view.setViewTreeOnBackPressedDispatcherOwner(
                         object : OnBackPressedDispatcherOwner {
                             override val onBackPressedDispatcher =
@@ -102,6 +130,7 @@
                                 viewModel = viewModel,
                                 windowInsets = windowInsets,
                                 sceneByKey = sortedSceneByKey,
+                                overlayByKey = sortedOverlayByKey,
                                 dataSourceDelegator = dataSourceDelegator,
                                 containerConfig = containerConfig,
                             )
@@ -120,17 +149,22 @@
                             sharedNotificationContainer
                         )
                         view.addView(sharedNotificationContainer)
+
+                        // TODO(b/358354906): use an overlay for the alternate bouncer
+                        view.addView(
+                            createAlternateBouncerView(
+                                context = view.context,
+                                alternateBouncerDependencies = alternateBouncerDependencies,
+                            )
+                        )
                     }
 
-                    launch {
-                        viewModel.isVisible.collect { isVisible ->
-                            onVisibilityChangedInternal(isVisible)
-                        }
-                    }
+                    view.setSnapshotBinding { onVisibilityChangedInternal(viewModel.isVisible) }
+                    awaitCancellation()
+                } finally {
+                    // Here when destroyed.
+                    view.removeAllViews()
                 }
-
-                // Here when destroyed.
-                view.removeAllViews()
             }
         }
     }
@@ -141,6 +175,7 @@
         viewModel: SceneContainerViewModel,
         windowInsets: StateFlow<WindowInsets?>,
         sceneByKey: Map<SceneKey, Scene>,
+        overlayByKey: Map<OverlayKey, Overlay>,
         dataSourceDelegator: SceneDataSourceDelegator,
         containerConfig: SceneContainerConfig,
     ): View {
@@ -153,8 +188,8 @@
                     ) {
                         SceneContainer(
                             viewModel = viewModel,
-                            sceneByKey =
-                                sceneByKey.mapValues { (_, scene) -> scene as ComposableScene },
+                            sceneByKey = sceneByKey,
+                            overlayByKey = overlayByKey,
                             initialSceneKey = containerConfig.initialSceneKey,
                             dataSourceDelegator = dataSourceDelegator,
                         )
@@ -164,6 +199,19 @@
         }
     }
 
+    private fun createAlternateBouncerView(
+        context: Context,
+        alternateBouncerDependencies: AlternateBouncerDependencies,
+    ): ComposeView {
+        return ComposeView(context).apply {
+            setContent {
+                AlternateBouncer(
+                    alternateBouncerDependencies = alternateBouncerDependencies,
+                )
+            }
+        }
+    }
+
     // TODO(b/298525212): remove once Compose exposes window inset bounds.
     private fun displayCutoutFromWindowInsets(
         scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
deleted file mode 100644
index b707a5a..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneSceneActionsViewModel.kt
+++ /dev/null
@@ -1,92 +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.scene.ui.viewmodel
-
-import androidx.compose.ui.Alignment
-import com.android.compose.animation.scene.Edge
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.map
-
-class GoneSceneActionsViewModel
-@AssistedInject
-constructor(
-    private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        shadeInteractor.shadeMode
-            .map { shadeMode ->
-                buildMap<UserAction, UserActionResult> {
-                    if (
-                        shadeMode is ShadeMode.Single ||
-                            // TODO(b/338577208): Remove this once we add Dual Shade invocation
-                            // zones.
-                            shadeMode is ShadeMode.Dual
-                    ) {
-                        if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
-                            put(
-                                Swipe(
-                                    pointerCount = 2,
-                                    fromSource = Edge.Bottom,
-                                    direction = SwipeDirection.Up,
-                                ),
-                                UserActionResult(SceneFamilies.QuickSettings, OpenBottomShade)
-                            )
-                        } else {
-                            put(
-                                Swipe(
-                                    pointerCount = 2,
-                                    fromSource = Edge.Top,
-                                    direction = SwipeDirection.Down,
-                                ),
-                                UserActionResult(SceneFamilies.QuickSettings)
-                            )
-                        }
-                    }
-
-                    if (shadeInteractor.shadeAlignment == Alignment.BottomEnd) {
-                        put(Swipe.Up, UserActionResult(SceneFamilies.NotifShade, OpenBottomShade))
-                    } else {
-                        put(
-                            Swipe.Down,
-                            UserActionResult(
-                                SceneFamilies.NotifShade,
-                                ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                            )
-                        )
-                    }
-                }
-            }
-            .collectLatest { setActions(it) }
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): GoneSceneActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt
new file mode 100644
index 0000000..ea4122a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/GoneUserActionsViewModel.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.scene.ui.viewmodel
+
+import com.android.compose.animation.scene.Edge
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.map
+
+class GoneUserActionsViewModel
+@AssistedInject
+constructor(
+    private val shadeInteractor: ShadeInteractor,
+) : UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        shadeInteractor.shadeMode
+            .map { shadeMode ->
+                buildMap<UserAction, UserActionResult> {
+                    if (
+                        shadeMode is ShadeMode.Single ||
+                            // TODO(b/338577208): Remove this once we add Dual Shade invocation
+                            // zones.
+                            shadeMode is ShadeMode.Dual
+                    ) {
+                        put(
+                            Swipe(
+                                pointerCount = 2,
+                                fromSource = Edge.Top,
+                                direction = SwipeDirection.Down,
+                            ),
+                            UserActionResult(SceneFamilies.QuickSettings)
+                        )
+                    }
+
+                    put(
+                        Swipe.Down,
+                        UserActionResult(
+                            SceneFamilies.NotifShade,
+                            ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                        )
+                    )
+                }
+            }
+            .collect { setActions(it) }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): GoneUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.kt
deleted file mode 100644
index c2fd65b..0000000
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneActionsViewModel.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.scene.ui.viewmodel
-
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.lifecycle.SysUiViewModel
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/**
- * Base class for view-models that need to keep a map of scene actions (also known as "destination
- * scenes") up-to-date.
- *
- * Subclasses need only to override [hydrateActions], suspending forever if they need; they don't
- * need to worry about resetting the value of [actions] when the view-model is deactivated/canceled,
- * this base class takes care of it.
- */
-abstract class SceneActionsViewModel : SysUiViewModel() {
-
-    private val _actions = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
-    /**
-     * [UserActionResult] by [UserAction] to be collected by the scene container to enable the right
-     * user input/gestures.
-     */
-    val actions: StateFlow<Map<UserAction, UserActionResult>> = _actions.asStateFlow()
-
-    final override suspend fun onActivated() {
-        try {
-            hydrateActions { state -> _actions.value = state }
-            awaitCancellation()
-        } finally {
-            _actions.value = emptyMap()
-        }
-    }
-
-    /**
-     * Keeps the user actions up-to-date (AKA "hydrated").
-     *
-     * Subclasses should implement this `suspend fun` by running coroutine work and calling
-     * [setActions] each time the actions should be published/updated. The work can safely suspend
-     * forever; the base class will take care of canceling it as needed. There's no need to handle
-     * cancellation in this method.
-     *
-     * The base class will also take care of resetting the [actions] flow back to the default value
-     * when this happens.
-     */
-    protected abstract suspend fun hydrateActions(
-        setActions: (Map<UserAction, UserActionResult>) -> Unit,
-    )
-}
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 a28222e..4c6341b 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
@@ -17,56 +17,85 @@
 package com.android.systemui.scene.ui.viewmodel
 
 import android.view.MotionEvent
+import androidx.compose.runtime.getValue
+import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.logger.SceneLogger
 import com.android.systemui.scene.shared.model.Scenes
-import javax.inject.Inject
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.map
 
 /** Models UI state for the scene container. */
-@SysUISingleton
 class SceneContainerViewModel
-@Inject
+@AssistedInject
 constructor(
     private val sceneInteractor: SceneInteractor,
     private val falsingInteractor: FalsingInteractor,
     private val powerInteractor: PowerInteractor,
-) {
-    /**
-     * Keys of all scenes in the container.
-     *
-     * The scenes will be sorted in z-order such that the last one is the one that should be
-     * rendered on top of all previous ones.
-     */
-    val allSceneKeys: List<SceneKey> = sceneInteractor.allSceneKeys()
+    private val logger: SceneLogger,
+    @Assisted private val motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
+) : ExclusiveActivatable() {
 
     /** The scene that should be rendered. */
     val currentScene: StateFlow<SceneKey> = sceneInteractor.currentScene
 
+    private val hydrator = Hydrator("SceneContainerViewModel.hydrator")
+
     /** Whether the container is visible. */
-    val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
+    val isVisible: Boolean by hydrator.hydratedStateOf("isVisible", sceneInteractor.isVisible)
+
+    override suspend fun onActivated(): Nothing {
+        try {
+            // Sends a MotionEventHandler to the owner of the view-model so they can report
+            // MotionEvents into the view-model.
+            motionEventHandlerReceiver(
+                object : MotionEventHandler {
+                    override fun onMotionEvent(motionEvent: MotionEvent) {
+                        [email protected](motionEvent)
+                    }
+
+                    override fun onMotionEventComplete() {
+                        [email protected]()
+                    }
+                }
+            )
+
+            hydrator.activate()
+        } finally {
+            // Clears the previously-sent MotionEventHandler so the owner of the view-model releases
+            // their reference to it.
+            motionEventHandlerReceiver(null)
+        }
+    }
 
     /**
      * Binds the given flow so the system remembers it.
      *
-     * Note that you must call is with `null` when the UI is done or risk a memory leak.
+     * Note that you must call this with `null` when the UI is done or risk a memory leak.
      */
     fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
         sceneInteractor.setTransitionState(transitionState)
     }
 
     /**
-     * Notifies that a [MotionEvent] is first seen at the top of the scene container UI.
+     * Notifies that a [MotionEvent] is first seen at the top of the scene container UI. This
+     * includes gestures on [SharedNotificationContainer] as well as the Composable scene container
+     * hierarchy.
      *
      * Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
      */
@@ -77,11 +106,21 @@
             event.actionMasked == MotionEvent.ACTION_UP ||
                 event.actionMasked == MotionEvent.ACTION_CANCEL
         ) {
-            sceneInteractor.onUserInteractionFinished()
+            sceneInteractor.onUserInputFinished()
         }
     }
 
     /**
+     * Notifies that a scene container user interaction has begun.
+     *
+     * This is a user interaction that has reached the Composable hierarchy of the scene container,
+     * rather than being handled by [SharedNotificationContainer].
+     */
+    fun onSceneContainerUserInputStarted() {
+        sceneInteractor.onSceneContainerUserInputStarted()
+    }
+
+    /**
      * Notifies that a [MotionEvent] that was previously sent to [onMotionEvent] has passed through
      * the scene container UI.
      *
@@ -110,16 +149,29 @@
                 else -> null
             }
 
-        return interactionTypeOrNull?.let { interactionType ->
-            // It's important that the falsing system is always queried, even if no enforcement will
-            // occur. This helps build up the right signal in the system.
-            val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
+        val fromScene = currentScene.value
+        val isAllowed =
+            interactionTypeOrNull?.let { interactionType ->
+                // It's important that the falsing system is always queried, even if no enforcement
+                // will occur. This helps build up the right signal in the system.
+                val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)
 
-            // Only enforce falsing if moving from the lockscreen scene to a new scene.
-            val fromLockscreenScene = currentScene.value == Scenes.Lockscreen
+                // Only enforce falsing if moving from the lockscreen scene to a new scene.
+                val fromLockscreenScene = fromScene == Scenes.Lockscreen
 
-            !fromLockscreenScene || !isFalseTouch
-        } ?: true
+                !fromLockscreenScene || !isFalseTouch
+            } ?: true
+
+        if (isAllowed) {
+            // A scene change is guaranteed; log it.
+            logger.logSceneChanged(
+                from = fromScene,
+                to = toScene,
+                reason = "user interaction",
+                isInstant = false,
+            )
+        }
+        return isAllowed
     }
 
     /**
@@ -130,27 +182,63 @@
         actionResultMap: Map<UserAction, UserActionResult>,
     ): Map<UserAction, UserActionResult> {
         return actionResultMap.mapValues { (_, actionResult) ->
-            sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let {
-                actionResult.copy(toScene = it)
+            when (actionResult) {
+                is UserActionResult.ChangeScene -> {
+                    sceneInteractor.resolveSceneFamilyOrNull(actionResult.toScene)?.value?.let {
+                        toScene ->
+                        UserActionResult(
+                            toScene = toScene,
+                            transitionKey = actionResult.transitionKey,
+                            requiresFullDistanceSwipe = actionResult.requiresFullDistanceSwipe,
+                        )
+                    }
+                }
+                is UserActionResult.ShowOverlay,
+                is UserActionResult.HideOverlay,
+                is UserActionResult.ReplaceByOverlay -> TODO("b/353679003: Support overlays")
             } ?: actionResult
         }
     }
 
-    private fun replaceSceneFamilies(
-        destinationScenes: Map<UserAction, UserActionResult>,
-    ): Flow<Map<UserAction, UserActionResult>> {
-        return destinationScenes
-            .mapValues { (_, actionResult) ->
-                sceneInteractor.resolveSceneFamily(actionResult.toScene).map { scene ->
-                    actionResult.copy(toScene = scene)
-                }
-            }
-            .combineValueFlows()
+    /**
+     * Returns the [ContentKey] whose user actions should be active.
+     *
+     * @param overlayByKey Mapping of [Overlay] by [OverlayKey], ordered by z-order such that the
+     *   last overlay is rendered on top of all other overlays.
+     */
+    fun getActionableContentKey(
+        currentScene: SceneKey,
+        currentOverlays: Set<OverlayKey>,
+        overlayByKey: Map<OverlayKey, Overlay>,
+    ): ContentKey {
+        // Overlay actions take precedence over scene actions.
+        return when (currentOverlays.size) {
+            // No overlays, the scene is actionable.
+            0 -> currentScene
+            // Small optimization for the most common case.
+            1 -> currentOverlays.first()
+            // Find the top-most overlay by z-index.
+            else ->
+                checkNotNull(overlayByKey.asSequence().findLast { it.key in currentOverlays }?.key)
+        }
+    }
+
+    /** Defines interface for classes that can handle externally-reported [MotionEvent]s. */
+    interface MotionEventHandler {
+        /** Notifies that a [MotionEvent] has occurred. */
+        fun onMotionEvent(motionEvent: MotionEvent)
+
+        /**
+         * Notifies that the previous [MotionEvent] reported by [onMotionEvent] has finished
+         * processing.
+         */
+        fun onMotionEventComplete()
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            motionEventHandlerReceiver: (MotionEventHandler?) -> Unit,
+        ): SceneContainerViewModel
     }
 }
-
-private fun <K, V> Map<K, Flow<V>>.combineValueFlows(): Flow<Map<K, V>> =
-    combine(
-        asIterable().map { (k, fv) -> fv.map { k to it } },
-        transform = Array<Pair<K, V>>::toMap,
-    )
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.kt
new file mode 100644
index 0000000..57628d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/UserActionsViewModel.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.scene.ui.viewmodel
+
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * Base class for view-models that need to keep a map of user actions up-to-date.
+ *
+ * Subclasses need only to override [hydrateActions], suspending forever if they need; they don't
+ * need to worry about resetting the value of [actions] when the view-model is deactivated/canceled,
+ * this base class takes care of it.
+ */
+abstract class UserActionsViewModel : ExclusiveActivatable() {
+
+    private val _actions = MutableStateFlow<Map<UserAction, UserActionResult>>(emptyMap())
+    /**
+     * [UserActionResult] by [UserAction] to be collected by the scene container to enable the right
+     * user input/gestures.
+     */
+    val actions: StateFlow<Map<UserAction, UserActionResult>> = _actions.asStateFlow()
+
+    final override suspend fun onActivated(): Nothing {
+        try {
+            hydrateActions { state -> _actions.value = state }
+            awaitCancellation()
+        } finally {
+            _actions.value = emptyMap()
+        }
+    }
+
+    /**
+     * Keeps the user actions up-to-date (AKA "hydrated").
+     *
+     * Subclasses should implement this `suspend fun` by running coroutine work and calling
+     * [setActions] each time the actions should be published/updated. The work can safely suspend
+     * forever; the base class will take care of canceling it as needed. There's no need to handle
+     * cancellation in this method.
+     *
+     * The base class will also take care of resetting the [actions] flow back to the default value
+     * when this happens.
+     */
+    protected abstract suspend fun hydrateActions(
+        setActions: (Map<UserAction, UserActionResult>) -> Unit,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index b54bf6c..f3357ee 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -146,6 +146,10 @@
                     hostUserHandle
                 )
                 intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid)
+                intent.putExtra(
+                    MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE,
+                    MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name,
+                )
                 activityStarter.startActivity(intent, /* dismissShade= */ true)
             }
             dialog.dismiss()
@@ -270,15 +274,18 @@
             return listOf(
                 ScreenShareOption(
                     SINGLE_APP,
-                    R.string.screen_share_permission_dialog_option_single_app,
+                    R.string.screenrecord_permission_dialog_option_text_single_app,
                     R.string.screenrecord_permission_dialog_warning_single_app,
-                    startButtonText = R.string.screenrecord_permission_dialog_continue,
+                    startButtonText =
+                        R.string
+                            .media_projection_entry_generic_permission_dialog_continue_single_app,
                 ),
                 ScreenShareOption(
                     ENTIRE_SCREEN,
-                    R.string.screen_share_permission_dialog_option_entire_screen,
+                    R.string.screenrecord_permission_dialog_option_text_entire_screen,
                     R.string.screenrecord_permission_dialog_warning_entire_screen,
-                    startButtonText = R.string.screenrecord_permission_dialog_continue,
+                    startButtonText =
+                        R.string.screenrecord_permission_dialog_continue_entire_screen,
                 )
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
index 6ff1535..74513f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/AssistContentRequester.java
@@ -91,7 +91,7 @@
             try {
                 boolean success = mActivityTaskManager.requestAssistDataForTask(
                         new AssistDataReceiver(callback, this), taskId, mPackageName,
-                        mAttributionTag);
+                        mAttributionTag, false /* fetchStructure */);
                 if (!success) {
                     callback.onAssistContentAvailable(null);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
index 474afa8b..56afb79 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
@@ -9,7 +9,6 @@
 import android.view.ViewTreeObserver
 import android.view.animation.AccelerateDecelerateInterpolator
 import androidx.constraintlayout.widget.Guideline
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.message.ProfileMessageController
@@ -49,44 +48,19 @@
     }
 
     fun onScreenshotTaken(screenshot: ScreenshotData) {
-        if (screenshotPrivateProfileBehaviorFix()) {
-            mainScope.launch {
-                val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
-                var notifiedApps: List<CharSequence> =
-                    screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
-
-                // If profile first run needs to show, bias towards that, otherwise show screenshot
-                // detection notification if needed.
-                if (profileData != null) {
-                    workProfileFirstRunView.visibility = View.VISIBLE
-                    detectionNoticeView.visibility = View.GONE
-                    profileMessageController.bindView(workProfileFirstRunView, profileData) {
-                        animateOutMessageContainer()
-                    }
-                    animateInMessageContainer()
-                } else if (notifiedApps.isNotEmpty()) {
-                    detectionNoticeView.visibility = View.VISIBLE
-                    workProfileFirstRunView.visibility = View.GONE
-                    screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
-                    animateInMessageContainer()
-                }
-            }
-        } else {
-            val workProfileData =
-                workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
+        mainScope.launch {
+            val profileData = profileMessageController.onScreenshotTaken(screenshot.userHandle)
             var notifiedApps: List<CharSequence> =
                 screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
 
-            // If work profile first run needs to show, bias towards that, otherwise show screenshot
+            // If profile first run needs to show, bias towards that, otherwise show screenshot
             // detection notification if needed.
-            if (workProfileData != null) {
+            if (profileData != null) {
                 workProfileFirstRunView.visibility = View.VISIBLE
                 detectionNoticeView.visibility = View.GONE
-                workProfileMessageController.populateView(
-                    workProfileFirstRunView,
-                    workProfileData,
-                    this::animateOutMessageContainer
-                )
+                profileMessageController.bindView(workProfileFirstRunView, profileData) {
+                    animateOutMessageContainer()
+                }
                 animateInMessageContainer()
             } else if (notifiedApps.isNotEmpty()) {
                 detectionNoticeView.visibility = View.VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
deleted file mode 100644
index 922997d..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ /dev/null
@@ -1,62 +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.screenshot
-
-import android.util.Log
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-
-/** Implementation of [ScreenshotRequestProcessor] */
-class RequestProcessor(
-    private val capture: ImageCapture,
-    private val policy: ScreenshotPolicy,
-) : ScreenshotRequestProcessor {
-
-    override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
-        var result = screenshot
-
-        // Apply work profile screenshots policy:
-        //
-        // If the focused app belongs to a work profile, transforms a full screen
-        // (or partial) screenshot request to a task snapshot (provided image) screenshot.
-
-        // Whenever displayContentInfo is fetched, the topComponent is also populated
-        // regardless of the managed profile status.
-
-        if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-            val info = policy.findPrimaryContent(screenshot.displayId)
-            Log.d(TAG, "findPrimaryContent: $info")
-            result.taskId = info.taskId
-            result.topComponent = info.component
-            result.userHandle = info.user
-
-            if (policy.isManagedProfile(info.user.identifier)) {
-                val image =
-                    capture.captureTask(info.taskId)
-                        ?: throw RequestProcessorException("Task snapshot returned a null Bitmap!")
-
-                // Provide the task snapshot as the screenshot
-                result.type = TAKE_SCREENSHOT_PROVIDED_IMAGE
-                result.bitmap = image
-                result.screenBounds = info.bounds
-            }
-        }
-
-        return result
-    }
-}
-
-private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
index 3ad4075a..ee1008d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotRequestProcessor.kt
@@ -27,5 +27,5 @@
     suspend fun process(original: ScreenshotData): ScreenshotData
 }
 
-/** Exception thrown by [RequestProcessor] if something goes wrong. */
+/** Exception thrown by [ScreenshotRequestProcessor] if something goes wrong. */
 class RequestProcessorException(message: String) : IllegalStateException(message)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
index 644e12c..c4fe7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
@@ -31,6 +31,7 @@
 import android.view.WindowInsets
 import android.view.WindowManager
 import android.window.WindowContext
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.policy.PhoneWindow
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
@@ -41,6 +42,7 @@
 @AssistedInject
 constructor(
     private val windowManager: WindowManager,
+    private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
     private val context: Context,
     @Assisted private val display: Display,
 ) {
@@ -95,7 +97,7 @@
             Log.d(TAG, "attachWindow")
         }
         attachRequested = true
-        windowManager.addView(decorView, params)
+        viewCaptureAwareWindowManager.addView(decorView, params)
 
         decorView.requestApplyInsets()
         decorView.requireViewById<ViewGroup>(R.id.content).apply {
@@ -133,7 +135,7 @@
             if (LogConfig.DEBUG_WINDOW) {
                 Log.d(TAG, "Removing screenshot window")
             }
-            windowManager.removeViewImmediate(decorView)
+            viewCaptureAwareWindowManager.removeViewImmediate(decorView)
             detachRequested = false
         }
         if (attachRequested && !detachRequested) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
index 50ea3bb..448f7c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -99,6 +99,9 @@
     ) {
         val displays = getDisplaysToScreenshot(screenshotRequest.type)
         val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
+        if (displays.isEmpty()) {
+            Log.wtf(TAG, "No displays found for screenshot.")
+        }
         displays.forEach { display ->
             val displayId = display.displayId
             var screenshotHandler: ScreenshotHandler =
@@ -219,8 +222,7 @@
     }
 
     private fun getScreenshotController(display: Display): InteractiveScreenshotHandler {
-        val controller =
-            screenshotController ?: interactiveScreenshotHandlerFactory.create(display)
+        val controller = screenshotController ?: interactiveScreenshotHandlerFactory.create(display)
         screenshotController = controller
         return controller
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index 9db1f24..15a1d1d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -68,10 +68,14 @@
 import com.android.systemui.Flags;
 import com.android.systemui.log.DebugLogger;
 import com.android.systemui.res.R;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
 import com.android.systemui.screenshot.scroll.CropView;
 import com.android.systemui.settings.UserTracker;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -98,6 +102,7 @@
     private static final String TAG = AppClipsActivity.class.getSimpleName();
     private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0);
     private static final int DRAWABLE_END = 2;
+    private static final float DISABLE_ALPHA = 0.5f;
 
     private final AppClipsViewModel.Factory mViewModelFactory;
     private final PackageManager mPackageManager;
@@ -114,6 +119,7 @@
     private Button mCancel;
     private CheckBox mBacklinksIncludeDataCheckBox;
     private TextView mBacklinksDataTextView;
+    private TextView mBacklinksCrossProfileError;
     private AppClipsViewModel mViewModel;
 
     private ResultReceiver mResultReceiver;
@@ -190,8 +196,8 @@
         mBacklinksDataTextView = mLayout.findViewById(R.id.backlinks_data);
         mBacklinksIncludeDataCheckBox = mLayout.findViewById(R.id.backlinks_include_data);
         mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(
-                (buttonView, isChecked) ->
-                        mBacklinksDataTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE));
+                this::backlinksIncludeDataCheckBoxCheckedChangeListener);
+        mBacklinksCrossProfileError = mLayout.findViewById(R.id.backlinks_cross_profile_error);
 
         mViewModel = new ViewModelProvider(this, mViewModelFactory).get(AppClipsViewModel.class);
         mViewModel.getScreenshot().observe(this, this::setScreenshot);
@@ -310,10 +316,11 @@
                 Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
         data.putParcelable(EXTRA_SCREENSHOT_URI, uri);
 
+        InternalBacklinksData selectedBacklink = mViewModel.mSelectedBacklinksLiveData.getValue();
         if (mBacklinksIncludeDataCheckBox.getVisibility() == View.VISIBLE
                 && mBacklinksIncludeDataCheckBox.isChecked()
-                && mViewModel.mSelectedBacklinksLiveData.getValue() != null) {
-            ClipData backlinksData = mViewModel.mSelectedBacklinksLiveData.getValue().getClipData();
+                && selectedBacklink instanceof BacklinksData) {
+            ClipData backlinksData = ((BacklinksData) selectedBacklink).getClipData();
             data.putParcelable(EXTRA_CLIP_DATA, backlinksData);
 
             DebugLogger.INSTANCE.logcatMessage(this,
@@ -344,10 +351,63 @@
 
         // Set up the dropdown when multiple backlinks are available.
         if (backlinksData.size() > 1) {
-            setUpListPopupWindow(backlinksData, mBacklinksDataTextView);
+            setUpListPopupWindow(updateBacklinkLabelsWithDuplicateNames(backlinksData),
+                    mBacklinksDataTextView);
         }
     }
 
+    /**
+     * If there are more than 1 backlinks that have the same app name, then this method appends
+     * a numerical suffix to such backlinks to help users distinguish.
+     */
+    private List<InternalBacklinksData> updateBacklinkLabelsWithDuplicateNames(
+            List<InternalBacklinksData> backlinksData) {
+        // Check if there are multiple backlinks with same name.
+        Map<String, Integer> duplicateNamedBacklinksCountMap = new HashMap<>();
+        for (InternalBacklinksData data : backlinksData) {
+            if (duplicateNamedBacklinksCountMap.containsKey(data.getDisplayLabel())) {
+                int duplicateCount = duplicateNamedBacklinksCountMap.get(data.getDisplayLabel());
+                if (duplicateCount == 0) {
+                    // If this is the first time the loop is coming across a duplicate name, set the
+                    // count to 2. This way the count starts from 1 for all duplicate named
+                    // backlinks.
+                    duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 2);
+                } else {
+                    // For all duplicate named backlinks, increase the duplicate count by 1.
+                    duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), duplicateCount + 1);
+                }
+            } else {
+                // This is the first time the loop is coming across a backlink with this name. Set
+                // its count to 0. The loop will increase its count by 1 when a duplicate is found.
+                duplicateNamedBacklinksCountMap.put(data.getDisplayLabel(), 0);
+            }
+        }
+
+        // Go through the backlinks in reverse order as it is easier to assign the numerical suffix
+        // in descending order of frequency using the duplicate map that was built earlier. For
+        // example, if "App A" is present 3 times, then we assign display label "App A (3)" first
+        // and then "App A (2)", lastly "App A (1)".
+        for (InternalBacklinksData data : backlinksData.reversed()) {
+            String originalBacklinkLabel = data.getDisplayLabel();
+            int duplicateCount = duplicateNamedBacklinksCountMap.get(originalBacklinkLabel);
+
+            // The display label should only be updated if there are multiple backlinks with the
+            // same name.
+            if (duplicateCount > 0) {
+                // Update the display label to: "App name (count)"
+                data.setDisplayLabel(
+                        getString(R.string.backlinks_duplicate_label_format, originalBacklinkLabel,
+                                duplicateCount));
+
+                // Decrease the duplicate count and update the map.
+                duplicateCount--;
+                duplicateNamedBacklinksCountMap.put(originalBacklinkLabel, duplicateCount);
+            }
+        }
+
+        return backlinksData;
+    }
+
     private void setUpListPopupWindow(List<InternalBacklinksData> backlinksData, View anchor) {
         ListPopupWindow listPopupWindow = new ListPopupWindow(this);
         listPopupWindow.setAnchorView(anchor);
@@ -365,7 +425,7 @@
             public View getView(int position, @Nullable View convertView, ViewGroup parent) {
                 TextView itemView = (TextView) super.getView(position, convertView, parent);
                 InternalBacklinksData data = backlinksData.get(position);
-                itemView.setText(data.getClipData().getDescription().getLabel());
+                itemView.setText(data.getDisplayLabel());
 
                 Drawable icon = data.getAppIcon();
                 icon.setBounds(createBacklinksTextViewDrawableBounds());
@@ -387,7 +447,7 @@
      * expected to be already set when this method is called.
      */
     private void updateBacklinksTextView(InternalBacklinksData backlinksData) {
-        mBacklinksDataTextView.setText(backlinksData.getClipData().getDescription().getLabel());
+        mBacklinksDataTextView.setText(backlinksData.getDisplayLabel());
         Drawable appIcon = backlinksData.getAppIcon();
         Rect compoundDrawableBounds = createBacklinksTextViewDrawableBounds();
         appIcon.setBounds(compoundDrawableBounds);
@@ -404,6 +464,38 @@
 
         mBacklinksDataTextView.setCompoundDrawablesRelative(/* start= */ appIcon, /* top= */
                 null, /* end= */ dropDownIcon, /* bottom= */ null);
+
+        updateViewsToShowOrHideBacklinkError(backlinksData);
+    }
+
+    /** Updates views to show or hide error with backlink.  */
+    private void updateViewsToShowOrHideBacklinkError(InternalBacklinksData backlinksData) {
+        // Remove the check box change listener before updating it to avoid updating backlink text
+        // view visibility.
+        mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(null);
+        if (backlinksData instanceof CrossProfileError) {
+            // There's error with the backlink, unselect the checkbox and disable it.
+            mBacklinksIncludeDataCheckBox.setEnabled(false);
+            mBacklinksIncludeDataCheckBox.setChecked(false);
+            mBacklinksIncludeDataCheckBox.setAlpha(DISABLE_ALPHA);
+
+            mBacklinksCrossProfileError.setVisibility(View.VISIBLE);
+        } else {
+            // When there is no error, ensure the check box is enabled and checked.
+            mBacklinksIncludeDataCheckBox.setEnabled(true);
+            mBacklinksIncludeDataCheckBox.setChecked(true);
+            mBacklinksIncludeDataCheckBox.setAlpha(1.0f);
+
+            mBacklinksCrossProfileError.setVisibility(View.GONE);
+        }
+
+        // (Re)Set the check box change listener as we're done making changes to the check box.
+        mBacklinksIncludeDataCheckBox.setOnCheckedChangeListener(
+                this::backlinksIncludeDataCheckBoxCheckedChangeListener);
+    }
+
+    private void backlinksIncludeDataCheckBoxCheckedChangeListener(View unused, boolean isChecked) {
+        mBacklinksDataTextView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
     }
 
     private Rect createBacklinksTextViewDrawableBounds() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
index 3530b3f..9a38358 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsViewModel.java
@@ -23,13 +23,13 @@
 
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 
-import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.IActivityTaskManager;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.app.assist.AssistContent;
 import android.content.ClipData;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -51,11 +51,14 @@
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
+import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.log.DebugLogger;
 import com.android.systemui.screenshot.AssistContentRequester;
 import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
 
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
@@ -82,7 +85,7 @@
     private final ImageExporter mImageExporter;
     private final IActivityTaskManager mAtmService;
     private final AssistContentRequester mAssistContentRequester;
-    private final PackageManager mPackageManager;
+    @Application private final Context mContext;
 
     @Main
     private final Executor mMainExecutor;
@@ -97,13 +100,13 @@
 
     private AppClipsViewModel(AppClipsCrossProcessHelper appClipsCrossProcessHelper,
             ImageExporter imageExporter, IActivityTaskManager atmService,
-            AssistContentRequester assistContentRequester, PackageManager packageManager,
+            AssistContentRequester assistContentRequester, @Application Context context,
             @Main Executor mainExecutor, @Background Executor bgExecutor) {
         mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
         mImageExporter = imageExporter;
         mAtmService = atmService;
         mAssistContentRequester = assistContentRequester;
-        mPackageManager = packageManager;
+        mContext = context;
         mMainExecutor = mainExecutor;
         mBgExecutor = bgExecutor;
 
@@ -243,6 +246,10 @@
                         allTasksOnDisplay
                                 .stream()
                                 .filter(taskInfo -> shouldIncludeTask(taskInfo, taskIdsToIgnore))
+                                .map(taskInfo -> new InternalTaskInfo(taskInfo.topActivityInfo,
+                                        taskInfo.taskId, taskInfo.userId,
+                                        getPackageManagerForUser(taskInfo.userId)))
+                                .filter(this::canAppStartThroughLauncher)
                                 .map(this::getBacklinksDataForTaskInfo)
                                 .toList(),
                         mBgExecutor);
@@ -284,33 +291,34 @@
     /**
      * Returns whether the app represented by the provided {@link TaskInfo} should be included for
      * querying for {@link AssistContent}.
+     *
+     * <p>This does not check whether the task has a launcher icon.
      */
     private boolean shouldIncludeTask(TaskInfo taskInfo, Set<Integer> taskIdsToIgnore) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("shouldIncludeTask taskId %d; topActivity %s", taskInfo.taskId,
                         taskInfo.topActivity));
 
-        // Only consider tasks that shouldn't be ignored, are visible, running, and have a launcher
-        // icon. Furthermore, types such as launcher/home/dock/assistant are ignored.
+        // Only consider tasks that shouldn't be ignored, are visible, and running. Furthermore,
+        // types such as launcher/home/dock/assistant are ignored.
         return !taskIdsToIgnore.contains(taskInfo.taskId)
                 && taskInfo.isVisible
                 && taskInfo.isRunning
                 && taskInfo.numActivities > 0
                 && taskInfo.topActivity != null
                 && taskInfo.topActivityInfo != null
-                && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD
-                && canAppStartThroughLauncher(taskInfo.topActivity.getPackageName());
+                && taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_STANDARD;
     }
 
     /**
-     * Returns whether the app represented by the provided {@code packageName} can be launched
-     * through the all apps tray by a user.
+     * Returns whether the app represented by the {@link InternalTaskInfo} can be launched through
+     * the all apps tray by a user.
      */
-    private boolean canAppStartThroughLauncher(String packageName) {
+    private boolean canAppStartThroughLauncher(InternalTaskInfo internalTaskInfo) {
         // Use Intent.resolveActivity API to check if the intent resolves as that is what Android
         // uses internally when apps use Context.startActivity.
-        return getMainLauncherIntentForPackage(packageName).resolveActivity(mPackageManager)
-                != null;
+        return getMainLauncherIntentForTask(internalTaskInfo)
+                .resolveActivity(internalTaskInfo.getPackageManager()) != null;
     }
 
     /**
@@ -318,18 +326,36 @@
      * is captured by querying the system using {@link TaskInfo#taskId}.
      */
     private ListenableFuture<InternalBacklinksData> getBacklinksDataForTaskInfo(
-            TaskInfo taskInfo) {
+            InternalTaskInfo internalTaskInfo) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("getBacklinksDataForTaskId for taskId %d; topActivity %s",
-                        taskInfo.taskId, taskInfo.topActivity));
+                        internalTaskInfo.getTaskId(),
+                        internalTaskInfo.getTopActivityNameForDebugLogging()));
+
+        // Unlike other SysUI components, App Clips is started by the notes app so it runs as the
+        // same user as the notes app. That is, if the notes app was running as work profile user
+        // then App Clips also runs as work profile user. This is why while checking for user of the
+        // screenshotted app the check is performed using UserHandle.myUserId instead of using the
+        // more complex UserTracker.
+        if (internalTaskInfo.getUserId() != UserHandle.myUserId()) {
+            return getCrossProfileErrorBacklinkForTask(internalTaskInfo);
+        }
 
         SettableFuture<InternalBacklinksData> backlinksData = SettableFuture.create();
-        int taskId = taskInfo.taskId;
-        mAssistContentRequester.requestAssistContent(taskId, assistContent ->
-                backlinksData.set(getBacklinksDataFromAssistContent(taskInfo, assistContent)));
+        int taskId = internalTaskInfo.getTaskId();
+        mAssistContentRequester.requestAssistContent(taskId, assistContent -> backlinksData.set(
+                getBacklinksDataFromAssistContent(internalTaskInfo, assistContent)));
         return withTimeout(backlinksData);
     }
 
+    private ListenableFuture<InternalBacklinksData> getCrossProfileErrorBacklinkForTask(
+            InternalTaskInfo internalTaskInfo) {
+        String appName = internalTaskInfo.getTopActivityAppName();
+        Drawable appIcon = internalTaskInfo.getTopActivityAppIcon();
+        InternalBacklinksData errorData = new CrossProfileError(appIcon, appName);
+        return Futures.immediateFuture(errorData);
+    }
+
     /** Returns the same {@link ListenableFuture} but with a 5 {@link TimeUnit#SECONDS} timeout. */
     private static <V> ListenableFuture<V> withTimeout(ListenableFuture<V> future) {
         return Futures.withTimeout(future, 5L, TimeUnit.SECONDS,
@@ -351,22 +377,24 @@
      *     {@link Intent#ACTION_MAIN} and {@link Intent#CATEGORY_LAUNCHER}.
      * </ul>
      *
-     * @param taskInfo {@link RootTaskInfo} of the task which provided the {@link AssistContent}.
+     * @param internalTaskInfo {@link InternalTaskInfo} of the task which provided the
+     * {@link AssistContent}.
      * @param content the {@link AssistContent} to map into Backlinks {@link ClipData}.
      * @return {@link InternalBacklinksData} that represents the Backlinks data along with app icon.
      */
-    private InternalBacklinksData getBacklinksDataFromAssistContent(TaskInfo taskInfo,
+    private InternalBacklinksData getBacklinksDataFromAssistContent(
+            InternalTaskInfo internalTaskInfo,
             @Nullable AssistContent content) {
         DebugLogger.INSTANCE.logcatMessage(this,
                 () -> String.format("getBacklinksDataFromAssistContent taskId %d; topActivity %s",
-                        taskInfo.taskId, taskInfo.topActivity));
+                        internalTaskInfo.getTaskId(),
+                        internalTaskInfo.getTopActivityNameForDebugLogging()));
 
-        String appName = getAppNameOfTask(taskInfo);
-        String packageName = taskInfo.topActivity.getPackageName();
-        Drawable appIcon = taskInfo.topActivityInfo.loadIcon(mPackageManager);
+        String appName = internalTaskInfo.getTopActivityAppName();
+        Drawable appIcon = internalTaskInfo.getTopActivityAppIcon();
         ClipData mainLauncherIntent = ClipData.newIntent(appName,
-                getMainLauncherIntentForPackage(packageName));
-        InternalBacklinksData fallback = new InternalBacklinksData(mainLauncherIntent, appIcon);
+                getMainLauncherIntentForTask(internalTaskInfo));
+        InternalBacklinksData fallback = new BacklinksData(mainLauncherIntent, appIcon);
         if (content == null) {
             return fallback;
         }
@@ -378,10 +406,10 @@
 
             Uri uri = content.getWebUri();
             Intent backlinksIntent = new Intent(ACTION_VIEW).setData(uri);
-            if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+            if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) {
                 DebugLogger.INSTANCE.logcatMessage(this,
                         () -> "getBacklinksDataFromAssistContent: using app provided uri");
-                return new InternalBacklinksData(ClipData.newRawUri(appName, uri), appIcon);
+                return new BacklinksData(ClipData.newRawUri(appName, uri), appIcon);
             }
         }
 
@@ -391,11 +419,10 @@
                     () -> "getBacklinksDataFromAssistContent: app has provided an intent");
 
             Intent backlinksIntent = content.getIntent();
-            if (doesIntentResolveToSamePackage(backlinksIntent, packageName)) {
+            if (doesIntentResolveToSameTask(backlinksIntent, internalTaskInfo)) {
                 DebugLogger.INSTANCE.logcatMessage(this,
                         () -> "getBacklinksDataFromAssistContent: using app provided intent");
-                return new InternalBacklinksData(ClipData.newIntent(appName, backlinksIntent),
-                        appIcon);
+                return new BacklinksData(ClipData.newIntent(appName, backlinksIntent), appIcon);
             }
         }
 
@@ -404,28 +431,28 @@
         return fallback;
     }
 
-    private boolean doesIntentResolveToSamePackage(Intent intentToResolve,
-            String requiredPackageName) {
-        ComponentName resolvedComponent = intentToResolve.resolveActivity(mPackageManager);
+    private boolean doesIntentResolveToSameTask(Intent intentToResolve,
+            InternalTaskInfo requiredTaskInfo) {
+        PackageManager packageManager = requiredTaskInfo.getPackageManager();
+        ComponentName resolvedComponent = intentToResolve.resolveActivity(packageManager);
         if (resolvedComponent == null) {
             return false;
         }
 
+        String requiredPackageName = requiredTaskInfo.getTopActivityPackageName();
         return resolvedComponent.getPackageName().equals(requiredPackageName);
     }
 
-    private String getAppNameOfTask(TaskInfo taskInfo) {
-        return taskInfo.topActivityInfo.loadLabel(mPackageManager).toString();
-    }
-
-    private Intent getMainLauncherIntentForPackage(String pkgName) {
+    private Intent getMainLauncherIntentForTask(InternalTaskInfo internalTaskInfo) {
+        String pkgName = internalTaskInfo.getTopActivityPackageName();
         Intent intent = new Intent(ACTION_MAIN).addCategory(CATEGORY_LAUNCHER).setPackage(pkgName);
 
         // Not all apps use DEFAULT_CATEGORY for their main launcher activity so the exact component
         // needs to be queried and set on the Intent in order for note-taking apps to be able to
         // start this intent. When starting an activity with an implicit intent, Android adds the
         // DEFAULT_CATEGORY flag otherwise it fails to resolve the intent.
-        ResolveInfo resolvedActivity = mPackageManager.resolveActivity(intent, /* flags= */ 0);
+        PackageManager packageManager = internalTaskInfo.getPackageManager();
+        ResolveInfo resolvedActivity = packageManager.resolveActivity(intent, /* flags= */ 0);
         if (resolvedActivity != null) {
             intent.setComponent(resolvedActivity.getComponentInfo().getComponentName());
         }
@@ -433,6 +460,17 @@
         return intent;
     }
 
+    private PackageManager getPackageManagerForUser(int userId) {
+        // If app clips was launched as the same user, then reuse the available PM from mContext.
+        if (mContext.getUserId() == userId) {
+            return mContext.getPackageManager();
+        }
+
+        // PackageManager required for a different user, create its context and return its PM.
+        UserHandle userHandle = UserHandle.of(userId);
+        return mContext.createContextAsUser(userHandle, /* flags= */ 0).getPackageManager();
+    }
+
     /** Helper factory to help with injecting {@link AppClipsViewModel}. */
     static final class Factory implements ViewModelProvider.Factory {
 
@@ -440,7 +478,7 @@
         private final ImageExporter mImageExporter;
         private final IActivityTaskManager mAtmService;
         private final AssistContentRequester mAssistContentRequester;
-        private final PackageManager mPackageManager;
+        @Application private final Context mContext;
         @Main
         private final Executor mMainExecutor;
         @Background
@@ -449,13 +487,13 @@
         @Inject
         Factory(AppClipsCrossProcessHelper appClipsCrossProcessHelper, ImageExporter imageExporter,
                 IActivityTaskManager atmService, AssistContentRequester assistContentRequester,
-                PackageManager packageManager, @Main Executor mainExecutor,
+                @Application Context context, @Main Executor mainExecutor,
                 @Background Executor bgExecutor) {
             mAppClipsCrossProcessHelper = appClipsCrossProcessHelper;
             mImageExporter = imageExporter;
             mAtmService = atmService;
             mAssistContentRequester = assistContentRequester;
-            mPackageManager = packageManager;
+            mContext = context;
             mMainExecutor = mainExecutor;
             mBgExecutor = bgExecutor;
         }
@@ -469,7 +507,7 @@
 
             //noinspection unchecked
             return (T) new AppClipsViewModel(mAppClipsCrossProcessHelper, mImageExporter,
-                    mAtmService, mAssistContentRequester, mPackageManager, mMainExecutor,
+                    mAtmService, mAssistContentRequester, mContext, mMainExecutor,
                     mBgExecutor);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
index 0e312f9..234692e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/InternalBacklinksData.kt
@@ -16,8 +16,49 @@
 
 package com.android.systemui.screenshot.appclips
 
+import android.app.TaskInfo
 import android.content.ClipData
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
 
-/** A class to hold the [ClipData] for backlinks and the corresponding app's [Drawable] icon. */
-internal data class InternalBacklinksData(val clipData: ClipData, val appIcon: Drawable)
+/**
+ * A class to hold the [ClipData] for backlinks, the corresponding app's [Drawable] icon, and
+ * represent error when necessary.
+ */
+internal sealed class InternalBacklinksData(
+    open val appIcon: Drawable,
+    open var displayLabel: String
+) {
+    data class BacklinksData(val clipData: ClipData, override val appIcon: Drawable) :
+        InternalBacklinksData(appIcon, clipData.description.label.toString())
+
+    data class CrossProfileError(
+        override val appIcon: Drawable,
+        override var displayLabel: String
+    ) : InternalBacklinksData(appIcon, displayLabel)
+}
+
+/**
+ * A class to hold important members of [TaskInfo] and its associated user's [PackageManager] for
+ * ease of querying.
+ *
+ * @note A task can have a different app running on top. For example, an app "A" can use camera app
+ *   to capture an image. In this case the top app will be the camera app even though the task
+ *   belongs to app A. This is expected behaviour because user will be taking a screenshot of the
+ *   content rendered by the camera (top) app.
+ */
+internal data class InternalTaskInfo(
+    private val topActivityInfo: ActivityInfo,
+    val taskId: Int,
+    val userId: Int,
+    val packageManager: PackageManager
+) {
+    fun getTopActivityNameForDebugLogging(): String = topActivityInfo.name
+
+    fun getTopActivityPackageName(): String = topActivityInfo.packageName
+
+    fun getTopActivityAppName(): String = topActivityInfo.loadLabel(packageManager).toString()
+
+    fun getTopActivityAppIcon(): Drawable = topActivityInfo.loadIcon(packageManager)
+}
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 44f767a..2cb9fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/ScreenshotPolicyModule.kt
@@ -19,14 +19,11 @@
 import android.content.ComponentName
 import android.content.Context
 import android.os.Process
-import com.android.systemui.Flags.screenshotPrivateProfileBehaviorFix
 import com.android.systemui.SystemUIService
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.screenshot.ImageCapture
-import com.android.systemui.screenshot.RequestProcessor
-import com.android.systemui.screenshot.ScreenshotPolicy
 import com.android.systemui.screenshot.ScreenshotRequestProcessor
 import com.android.systemui.screenshot.data.repository.DisplayContentRepository
 import com.android.systemui.screenshot.data.repository.DisplayContentRepositoryImpl
@@ -68,23 +65,18 @@
             @Application context: Context,
             @Background background: CoroutineDispatcher,
             imageCapture: ImageCapture,
-            policyProvider: Provider<ScreenshotPolicy>,
-            displayContentRepoProvider: Provider<DisplayContentRepository>,
+            displayContentRepo: DisplayContentRepository,
             policyListProvider: Provider<List<CapturePolicy>>,
         ): ScreenshotRequestProcessor {
-            return if (screenshotPrivateProfileBehaviorFix()) {
-                PolicyRequestProcessor(
-                    background = background,
-                    capture = imageCapture,
-                    displayTasks = displayContentRepoProvider.get(),
-                    policies = policyListProvider.get(),
-                    defaultOwner = Process.myUserHandle(),
-                    defaultComponent =
-                        ComponentName(context.packageName, SystemUIService::class.java.toString())
-                )
-            } else {
-                RequestProcessor(imageCapture, policyProvider.get())
-            }
+            return PolicyRequestProcessor(
+                background = background,
+                capture = imageCapture,
+                displayTasks = displayContentRepo,
+                policies = policyListProvider.get(),
+                defaultOwner = Process.myUserHandle(),
+                defaultComponent =
+                    ComponentName(context.packageName, SystemUIService::class.java.toString())
+            )
         }
     }
 }
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 3fe3162..29450a2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/policy/WorkProfilePolicy.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult
 import com.android.systemui.screenshot.policy.CapturePolicy.PolicyResult.NotMatched
 import com.android.systemui.screenshot.policy.CaptureType.IsolatedTask
-import com.android.wm.shell.shared.desktopmode.DesktopModeFlags
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.first
 
@@ -48,7 +48,7 @@
             return NotMatched(policy = NAME, reason = SHADE_EXPANDED)
         }
 
-        if (DesktopModeFlags.DESKTOP_WINDOWING_MODE.isEnabled(context)) {
+        if (DesktopModeStatus.canEnterDesktopMode(context)) {
             content.rootTasks.firstOrNull()?.also {
                 if (it.windowingMode == WINDOWING_MODE_FREEFORM) {
                     return NotMatched(policy = NAME, reason = DESKTOP_MODE_ENABLED)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index ee1944e..ad27da9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -214,8 +214,7 @@
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
-                if (mCurrentDraggingBoundary != CropBoundary.NONE
-                        && mActivePointerId == event.getPointerId(mActivePointerId)) {
+                if (mCurrentDraggingBoundary != CropBoundary.NONE) {
                     updateListener(MotionEvent.ACTION_UP, event.getX(0));
                     return true;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index e7ee961..edff4bf 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.settings
 
 import android.view.Display
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
 import java.util.concurrent.Executor
 
 /**
@@ -52,6 +53,7 @@
     fun getDisplay(displayId: Int): Display
 
     /** Ćallback for notifying of changes. */
+    @WeaklyReferencedCallback
     interface Callback {
 
         /** Notifies that a display has been added. */
diff --git a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
index 05f19ef..b9f9b92 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.settings;
 
-import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
@@ -67,7 +66,7 @@
             @Background CoroutineDispatcher backgroundDispatcher,
             @Background Handler handler
     ) {
-        int startingUser = ActivityManager.getCurrentUser();
+        int startingUser = userManager.getBootUser().getIdentifier();
         UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
                 iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
         tracker.initialize(startingUser);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 0a1f649..ed590c3 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -36,6 +36,13 @@
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.util.Assert
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
+import javax.inject.Provider
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
@@ -44,30 +51,23 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
-import java.io.PrintWriter
-import java.lang.ref.WeakReference
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.Executor
-import javax.inject.Provider
-import kotlin.properties.ReadWriteProperty
-import kotlin.reflect.KProperty
 
 /**
  * SystemUI cache for keeping track of the current user and associated values.
  *
- * The values provided asynchronously are NOT copies, but shared among all requesters. Do not
- * modify them.
+ * The values provided asynchronously are NOT copies, but shared among all requesters. Do not modify
+ * them.
  *
  * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as
- * soon as possible (and reduce its dependency graph).
- * Other classes that want to listen to the broadcasts listened here SHOULD
- * subscribe to this class instead.
+ * soon as possible (and reduce its dependency graph). Other classes that want to listen to the
+ * broadcasts listened here SHOULD subscribe to this class instead.
  *
  * @see UserTracker
  *
  * Class constructed and initialized in [SettingsModule].
  */
-open class UserTrackerImpl internal constructor(
+open class UserTrackerImpl
+internal constructor(
     private val context: Context,
     private val featureFlagsProvider: Provider<FeatureFlagsClassic>,
     private val userManager: UserManager,
@@ -87,8 +87,8 @@
         private set
 
     private val mutex = Any()
-    private val isBackgroundUserSwitchEnabled: Boolean get() =
-        featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
+    private val isBackgroundUserSwitchEnabled: Boolean
+        get() = featureFlagsProvider.get().isEnabled(Flags.USER_TRACKER_BACKGROUND_CALLBACKS)
 
     @Deprecated("Use UserInteractor.getSelectedUserId()")
     override var userId: Int by SynchronizedDelegate(context.userId)
@@ -118,8 +118,7 @@
     override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList())
         protected set
 
-    @GuardedBy("callbacks")
-    private val callbacks: MutableList<DataItem> = ArrayList()
+    @GuardedBy("callbacks") private val callbacks: MutableList<DataItem> = ArrayList()
 
     private var userSwitchingJob: Job? = null
     private var afterUserSwitchingJob: Job? = null
@@ -128,23 +127,25 @@
         if (initialized) {
             return
         }
+        Log.i(TAG, "Starting user: $startingUser")
         initialized = true
         setUserIdInternal(startingUser)
 
-        val filter = IntentFilter().apply {
-            addAction(Intent.ACTION_LOCALE_CHANGED)
-            addAction(Intent.ACTION_USER_INFO_CHANGED)
-            addAction(Intent.ACTION_PROFILE_ADDED)
-            addAction(Intent.ACTION_PROFILE_REMOVED)
-            addAction(Intent.ACTION_PROFILE_AVAILABLE)
-            addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
-            // These get called when a managed profile goes in or out of quiet mode.
-            addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
-            addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
-            addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
-            addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
-            addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
-        }
+        val filter =
+            IntentFilter().apply {
+                addAction(Intent.ACTION_LOCALE_CHANGED)
+                addAction(Intent.ACTION_USER_INFO_CHANGED)
+                addAction(Intent.ACTION_PROFILE_ADDED)
+                addAction(Intent.ACTION_PROFILE_REMOVED)
+                addAction(Intent.ACTION_PROFILE_AVAILABLE)
+                addAction(Intent.ACTION_PROFILE_UNAVAILABLE)
+                // These get called when a managed profile goes in or out of quiet mode.
+                addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+                addAction(Intent.ACTION_MANAGED_PROFILE_ADDED)
+                addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+                addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
+            }
         context.registerReceiverForAllUsers(this, filter, null, backgroundHandler)
 
         registerUserSwitchObserver()
@@ -191,36 +192,39 @@
     }
 
     private fun registerUserSwitchObserver() {
-        iActivityManager.registerUserSwitchObserver(object : UserSwitchObserver() {
-            override fun onBeforeUserSwitching(newUserId: Int) {
-                handleBeforeUserSwitching(newUserId)
-            }
-
-            override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
-                if (isBackgroundUserSwitchEnabled) {
-                    userSwitchingJob?.cancel()
-                    userSwitchingJob = appScope.launch(backgroundContext) {
-                        handleUserSwitchingCoroutines(newUserId) {
-                            reply?.sendResult(null)
-                        }
-                    }
-                } else {
-                    handleUserSwitching(newUserId)
-                    reply?.sendResult(null)
+        iActivityManager.registerUserSwitchObserver(
+            object : UserSwitchObserver() {
+                override fun onBeforeUserSwitching(newUserId: Int) {
+                    handleBeforeUserSwitching(newUserId)
                 }
-            }
 
-            override fun onUserSwitchComplete(newUserId: Int) {
-                if (isBackgroundUserSwitchEnabled) {
-                    afterUserSwitchingJob?.cancel()
-                    afterUserSwitchingJob = appScope.launch(backgroundContext) {
+                override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+                    if (isBackgroundUserSwitchEnabled) {
+                        userSwitchingJob?.cancel()
+                        userSwitchingJob =
+                            appScope.launch(backgroundContext) {
+                                handleUserSwitchingCoroutines(newUserId) { reply?.sendResult(null) }
+                            }
+                    } else {
+                        handleUserSwitching(newUserId)
+                        reply?.sendResult(null)
+                    }
+                }
+
+                override fun onUserSwitchComplete(newUserId: Int) {
+                    if (isBackgroundUserSwitchEnabled) {
+                        afterUserSwitchingJob?.cancel()
+                        afterUserSwitchingJob =
+                            appScope.launch(backgroundContext) {
+                                handleUserSwitchComplete(newUserId)
+                            }
+                    } else {
                         handleUserSwitchComplete(newUserId)
                     }
-                } else {
-                    handleUserSwitchComplete(newUserId)
                 }
-            }
-        }, TAG)
+            },
+            TAG
+        )
     }
 
     @WorkerThread
@@ -228,9 +232,10 @@
         setUserIdInternal(newUserId)
 
         notifySubscribers { callback, resultCallback ->
-            callback.onBeforeUserSwitching(newUserId)
-            resultCallback.run()
-        }.await()
+                callback.onBeforeUserSwitching(newUserId)
+                resultCallback.run()
+            }
+            .await()
     }
 
     @WorkerThread
@@ -239,31 +244,34 @@
         Log.i(TAG, "Switching to user $newUserId")
 
         notifySubscribers { callback, resultCallback ->
-            callback.onUserChanging(newUserId, userContext, resultCallback)
-        }.await()
+                callback.onUserChanging(newUserId, userContext, resultCallback)
+            }
+            .await()
     }
 
     @WorkerThread
     protected open suspend fun handleUserSwitchingCoroutines(newUserId: Int, onDone: () -> Unit) =
-            coroutineScope {
-                Assert.isNotMainThread()
-                Log.i(TAG, "Switching to user $newUserId")
+        coroutineScope {
+            Assert.isNotMainThread()
+            Log.i(TAG, "Switching to user $newUserId")
 
-                for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
-                    val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
-                    launch(callbackDataItem.executor.asCoroutineDispatcher()) {
+            for (callbackDataItem in synchronized(callbacks) { callbacks.toList() }) {
+                val callback: UserTracker.Callback = callbackDataItem.callback.get() ?: continue
+                launch(callbackDataItem.executor.asCoroutineDispatcher()) {
                         val mutex = Mutex(true)
-                        val thresholdLogJob = launch(backgroundContext) {
-                            delay(USER_CHANGE_THRESHOLD)
-                            Log.e(TAG, "Failed to finish $callback in time")
-                        }
+                        val thresholdLogJob =
+                            launch(backgroundContext) {
+                                delay(USER_CHANGE_THRESHOLD)
+                                Log.e(TAG, "Failed to finish $callback in time")
+                            }
                         callback.onUserChanging(userId, userContext) { mutex.unlock() }
                         mutex.lock()
                         thresholdLogJob.cancel()
-                    }.join()
-                }
-                onDone()
+                    }
+                    .join()
             }
+            onDone()
+        }
 
     @WorkerThread
     protected open fun handleUserSwitchComplete(newUserId: Int) {
@@ -284,36 +292,26 @@
         synchronized(mutex) {
             userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
         }
-        notifySubscribers { callback, _ ->
-            callback.onProfilesChanged(profiles)
-        }
+        notifySubscribers { callback, _ -> callback.onProfilesChanged(profiles) }
     }
 
     override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
-        synchronized(callbacks) {
-            callbacks.add(DataItem(WeakReference(callback), executor))
-        }
+        synchronized(callbacks) { callbacks.add(DataItem(WeakReference(callback), executor)) }
     }
 
     override fun removeCallback(callback: UserTracker.Callback) {
-        synchronized(callbacks) {
-            callbacks.removeIf { it.sameOrEmpty(callback) }
-        }
+        synchronized(callbacks) { callbacks.removeIf { it.sameOrEmpty(callback) } }
     }
 
     private inline fun notifySubscribers(
-            crossinline action: (UserTracker.Callback, resultCallback: Runnable) -> Unit
+        crossinline action: (UserTracker.Callback, resultCallback: Runnable) -> Unit
     ): CountDownLatch {
-        val list = synchronized(callbacks) {
-            callbacks.toList()
-        }
+        val list = synchronized(callbacks) { callbacks.toList() }
         val latch = CountDownLatch(list.size)
         list.forEach {
             val callback = it.callback.get()
             if (callback != null) {
-                it.executor.execute {
-                    action(callback) { latch.countDown() }
-                }
+                it.executor.execute { action(callback) { latch.countDown() } }
             } else {
                 latch.countDown()
             }
@@ -328,20 +326,13 @@
             val ids = userProfiles.map { it.toFullString() }
             pw.println("userProfiles: $ids")
         }
-        val list = synchronized(callbacks) {
-            callbacks.toList()
-        }
+        val list = synchronized(callbacks) { callbacks.toList() }
         pw.println("Callbacks:")
-        list.forEach {
-            it.callback.get()?.let {
-                pw.println("  $it")
-            }
-        }
+        list.forEach { it.callback.get()?.let { pw.println("  $it") } }
     }
 
-    private class SynchronizedDelegate<T : Any>(
-        private var value: T
-    ) : ReadWriteProperty<UserTrackerImpl, T> {
+    private class SynchronizedDelegate<T : Any>(private var value: T) :
+        ReadWriteProperty<UserTrackerImpl, T> {
 
         @GuardedBy("mutex")
         override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
index 7f8c146..52bc25d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ui/viewModel/BrightnessMirrorViewModel.kt
@@ -20,7 +20,6 @@
 import android.util.Log
 import android.view.View
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.lifecycle.SysUiViewModel
 import com.android.systemui.res.R
 import com.android.systemui.settings.brightness.BrightnessSliderController
 import com.android.systemui.settings.brightness.MirrorController
@@ -37,7 +36,7 @@
     private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
     @Main private val resources: Resources,
     val sliderControllerFactory: BrightnessSliderController.Factory,
-) : SysUiViewModel(), MirrorController {
+) : MirrorController {
 
     private val tempPosition = IntArray(2)
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 15bbef0..7fa9926 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shade
 
 import android.content.Context
-import android.graphics.Insets
 import android.graphics.Rect
 import android.os.PowerManager
 import android.os.SystemClock
@@ -26,13 +25,14 @@
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
-import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
 import androidx.compose.ui.platform.ComposeView
 import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleObserver
 import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.LifecycleRegistry
 import androidx.lifecycle.lifecycleScope
@@ -40,7 +40,6 @@
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.Flags
-import com.android.systemui.Flags.glanceableHubBackGesture
 import com.android.systemui.ambient.touch.TouchMonitor
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
 import com.android.systemui.communal.dagger.Communal
@@ -55,10 +54,15 @@
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalTouchLog
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.collectFlow
@@ -88,7 +92,11 @@
     private val communalContent: CommunalContent,
     @Communal private val dataSourceDelegator: SceneDataSourceDelegator,
     private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
+    private val keyguardMediaController: KeyguardMediaController,
+    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
+    @CommunalTouchLog logBuffer: LogBuffer,
 ) : LifecycleOwner {
+    private val logger = Logger(logBuffer, TAG)
 
     private class CommunalWrapper(context: Context) : FrameLayout(context) {
         private val consumers: MutableSet<Consumer<Boolean>> = ArraySet()
@@ -139,6 +147,17 @@
     private var isTrackingHubTouch = false
 
     /**
+     * True if a touch gesture on the lock screen has been consumed by the shade/bouncer and thus
+     * should be ignored by the hub.
+     *
+     * This is necessary on the lock screen as gestures on an empty spot go through special touch
+     * handling logic in [NotificationShadeWindowViewController] that decides if they should go to
+     * the shade or bouncer. Once the shade or bouncer are moving, we don't get the typical cancel
+     * event so to play nice, we ignore touches once we see the shade or bouncer are opening.
+     */
+    private var touchTakenByKeyguardGesture = false
+
+    /**
      * True if the hub UI is fully open, meaning it should receive touch input.
      *
      * Tracks [CommunalInteractor.isCommunalShowing].
@@ -178,6 +197,23 @@
     private var shadeShowingAndConsumingTouches = false
 
     /**
+     * True anytime the shade is processing user touches, regardless of expansion state.
+     *
+     * Based on [ShadeInteractor.isUserInteracting].
+     */
+    private var shadeConsumingTouches = false
+
+    /**
+     * True if the shade is showing at all.
+     *
+     * Inverse of [ShadeInteractor.isShadeFullyCollapsed]
+     */
+    private var shadeShowing = false
+
+    /** True if the keyguard transition state is finished on [KeyguardState.LOCKSCREEN]. */
+    private var onLockscreen = false
+
+    /**
      * True if the shade ever fully expands and the user isn't interacting with it (aka finger on
      * screen dragging). In this case, the shade should handle all touch events until it has fully
      * collapsed.
@@ -192,6 +228,21 @@
      */
     private var isDreaming = false
 
+    /** Observes and logs state when the lifecycle that controls the [touchMonitor] updates. */
+    private val touchLifecycleLogger: LifecycleObserver = LifecycleEventObserver { _, event ->
+        logger.d({
+            "Touch handler lifecycle changed to $str1. hubShowing: $bool1, " +
+                "shadeShowingAndConsumingTouches: $bool2, " +
+                "anyBouncerShowing: $bool3, inEditModeTransition: $bool4"
+        }) {
+            str1 = event.toString()
+            bool1 = hubShowing
+            bool2 = shadeShowingAndConsumingTouches
+            bool3 = anyBouncerShowing
+            bool4 = inEditModeTransition
+        }
+    }
+
     /** Returns a flow that tracks whether communal hub is available. */
     fun communalAvailable(): Flow<Boolean> =
         anyOf(communalInteractor.isCommunalAvailable, communalInteractor.editModeOpen)
@@ -250,10 +301,11 @@
 
         if (touchMonitor == null) {
             touchMonitor =
-                ambientTouchComponentFactory.create(this, HashSet()).getTouchMonitor().apply {
+                ambientTouchComponentFactory.create(this, HashSet(), TAG).getTouchMonitor().apply {
                     init()
                 }
         }
+        lifecycleRegistry.addObserver(touchLifecycleLogger)
         lifecycleRegistry.currentState = Lifecycle.State.CREATED
 
         communalContainerView = containerView
@@ -274,21 +326,13 @@
             // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not
             // occluded.
             lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                // Avoid adding exclusion to end/start edges to allow back gestures.
-                val insets =
-                    if (glanceableHubBackGesture()) {
-                        containerView.rootWindowInsets.getInsets(WindowInsets.Type.systemGestures())
-                    } else {
-                        Insets.NONE
-                    }
-
                 val ltr = containerView.layoutDirection == View.LAYOUT_DIRECTION_LTR
 
                 val backGestureInset =
                     Rect(
-                        if (ltr) 0 else insets.left,
                         0,
-                        if (ltr) insets.right else containerView.right,
+                        0,
+                        if (ltr) 0 else containerView.right,
                         containerView.bottom,
                     )
 
@@ -304,9 +348,9 @@
                             // Only allow swipe up to bouncer and swipe down to shade in the very
                             // top/bottom to avoid conflicting with widgets in the hub grid.
                             Rect(
-                                insets.left,
+                                0,
                                 topEdgeSwipeRegionWidth,
-                                containerView.right - insets.right,
+                                containerView.right,
                                 containerView.bottom - bottomEdgeSwipeRegionWidth
                             ),
                             // Disable back gestures on the left side of the screen, to avoid
@@ -314,6 +358,9 @@
                             backGestureInset
                         )
                     }
+                logger.d({ "Insets updated: $str1" }) {
+                    str1 = containerView.systemGestureExclusionRects.toString()
+                }
             }
         }
 
@@ -329,11 +376,19 @@
             ),
             {
                 anyBouncerShowing = it
+                if (hubShowing) {
+                    logger.d({ "New value for anyBouncerShowing: $bool1" }) { bool1 = it }
+                }
                 updateTouchHandlingState()
             }
         )
         collectFlow(
             containerView,
+            keyguardTransitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
+            { onLockscreen = it }
+        )
+        collectFlow(
+            containerView,
             communalInteractor.isCommunalVisible,
             {
                 hubShowing = it
@@ -365,6 +420,8 @@
                 ::Triple
             ),
             { (isFullyExpanded, isUserInteracting, isShadeFullyCollapsed) ->
+                shadeConsumingTouches = isUserInteracting
+                shadeShowing = !isShadeFullyCollapsed
                 val expandedAndNotInteractive = isFullyExpanded && !isUserInteracting
 
                 // If we ever are fully expanded and not interacting, capture this state as we
@@ -376,7 +433,13 @@
                 // If the shade reaches full expansion without interaction, then we should allow it
                 // to consume touches rather than handling it here until it disappears.
                 shadeShowingAndConsumingTouches =
-                    userNotInteractiveAtShadeFullyExpanded || expandedAndNotInteractive
+                    (userNotInteractiveAtShadeFullyExpanded || expandedAndNotInteractive).also {
+                        if (it != shadeShowingAndConsumingTouches && hubShowing) {
+                            logger.d({ "New value for shadeShowingAndConsumingTouches: $bool1" }) {
+                                bool1 = it
+                            }
+                        }
+                    }
                 updateTouchHandlingState()
             }
         )
@@ -384,6 +447,7 @@
 
         communalContainerWrapper = CommunalWrapper(containerView.context)
         communalContainerWrapper?.addView(communalContainerView)
+        logger.d("Hub container initialized")
         return communalContainerWrapper!!
     }
 
@@ -426,6 +490,10 @@
             (it.parent as ViewGroup).removeView(it)
             communalContainerWrapper = null
         }
+
+        lifecycleRegistry.removeObserver(touchLifecycleLogger)
+
+        logger.d("Hub container disposed")
     }
 
     /**
@@ -440,17 +508,32 @@
     fun onTouchEvent(ev: MotionEvent): Boolean {
         SceneContainerFlag.assertInLegacyMode()
 
-        // In the case that we are handling full swipes on the lockscreen, are on the lockscreen,
-        // and the touch is within the horizontal notification band on the screen, do not process
-        // the touch.
-        if (
-            !hubShowing &&
-                !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
-        ) {
+        if (communalContainerView == null) {
+            // Return early so we don't log unnecessarily and fill up our LogBuffer.
             return false
         }
 
-        return communalContainerView?.let { handleTouchEventOnCommunalView(ev) } ?: false
+        // In the case that we are handling full swipes on the lockscreen, are on the lockscreen,
+        // and the touch is within the horizontal notification band on the screen, do not process
+        // the touch.
+        val touchOnNotifications =
+            !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
+        val touchOnUmo = keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt())
+        val touchOnSmartspace =
+            lockscreenSmartspaceController.isWithinSmartspaceBounds(ev.x.toInt(), ev.y.toInt())
+        if (!hubShowing && (touchOnNotifications || touchOnUmo || touchOnSmartspace)) {
+            logger.d({
+                "Lockscreen touch ignored: touchOnNotifications: $bool1, touchOnUmo: $bool2, " +
+                    "touchOnSmartspace: $bool3"
+            }) {
+                bool1 = touchOnNotifications
+                bool2 = touchOnUmo
+                bool3 = touchOnSmartspace
+            }
+            return false
+        }
+
+        return handleTouchEventOnCommunalView(ev)
     }
 
     private fun handleTouchEventOnCommunalView(ev: MotionEvent): Boolean {
@@ -459,15 +542,59 @@
         val isMove = ev.actionMasked == MotionEvent.ACTION_MOVE
         val isCancel = ev.actionMasked == MotionEvent.ACTION_CANCEL
 
-        val hubOccluded = anyBouncerShowing || shadeShowingAndConsumingTouches
+        val hubOccluded = anyBouncerShowing || shadeConsumingTouches || shadeShowing
 
         if ((isDown || isMove) && !hubOccluded) {
+            if (isDown) {
+                logger.d({
+                    "Touch started. x: $int1, y: $int2, hubShowing: $bool1, isDreaming: $bool2, " +
+                        "onLockscreen: $bool3"
+                }) {
+                    int1 = ev.x.toInt()
+                    int2 = ev.y.toInt()
+                    bool1 = hubShowing
+                    bool2 = isDreaming
+                    bool3 = onLockscreen
+                }
+            }
             isTrackingHubTouch = true
         }
 
         if (isTrackingHubTouch) {
+            // On the lock screen, our touch handlers are not active and we rely on the NSWVC's
+            // touch handling for gestures on blank areas, which can go up to show the bouncer or
+            // down to show the notification shade. We see the touches first and they are not
+            // consumed and cancelled like on the dream or hub so we have to gracefully ignore them
+            // if the shade or bouncer are handling them. This issue only applies to touches on the
+            // keyguard itself, once the bouncer or shade are fully open, our logic stops us from
+            // taking touches.
+            touchTakenByKeyguardGesture =
+                (onLockscreen && (shadeConsumingTouches || anyBouncerShowing)).also {
+                    if (it != touchTakenByKeyguardGesture && it) {
+                        logger.d(
+                            "Lock screen touch consumed by shade or bouncer, ignoring " +
+                                "subsequent touches"
+                        )
+                    }
+                }
             if (isUp || isCancel) {
+                logger.d({
+                    val endReason = if (bool1) "up" else "cancel"
+                    "Touch ended with $endReason. x: $int1, y: $int2, " +
+                        "shadeConsumingTouches: $bool2, anyBouncerShowing: $bool3"
+                }) {
+                    int1 = ev.x.toInt()
+                    int2 = ev.y.toInt()
+                    bool1 = isUp
+                    bool2 = shadeConsumingTouches
+                    bool3 = anyBouncerShowing
+                }
                 isTrackingHubTouch = false
+
+                // Clear out touch taken state to ensure the up/cancel event still gets dispatched
+                // to the hub. This is necessary as the hub always receives at least the initial
+                // down even if the shade or bouncer end up handling the touch.
+                touchTakenByKeyguardGesture = false
             }
             return dispatchTouchEvent(ev)
         }
@@ -489,9 +616,11 @@
         }
         try {
             var handled = false
-            communalContainerWrapper?.dispatchTouchEvent(ev) {
-                if (it) {
-                    handled = true
+            if (!touchTakenByKeyguardGesture) {
+                communalContainerWrapper?.dispatchTouchEvent(ev) {
+                    if (it) {
+                        handled = true
+                    }
                 }
             }
             return handled || hubShowing
@@ -506,4 +635,8 @@
 
     override val lifecycle: Lifecycle
         get() = lifecycleRegistry
+
+    companion object {
+        private const val TAG = "GlanceableHubContainer"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 104d4b5..31813b2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -191,12 +191,10 @@
 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -439,7 +437,6 @@
     private boolean mExpandingFromHeadsUp;
     private boolean mCollapsedOnDown;
     private boolean mClosingWithAlphaFadeOut;
-    private boolean mHeadsUpVisible;
     private boolean mHeadsUpAnimatingAway;
     private final FalsingManager mFalsingManager;
     private final FalsingCollector mFalsingCollector;
@@ -610,7 +607,6 @@
     private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
     private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
     private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
-    private final HeadsUpNotificationInteractor mHeadsUpNotificationInteractor;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final KeyguardInteractor mKeyguardInteractor;
     private final PowerInteractor mPowerInteractor;
@@ -776,7 +772,6 @@
             ActivityStarter activityStarter,
             SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
             ActiveNotificationsInteractor activeNotificationsInteractor,
-            HeadsUpNotificationInteractor headsUpNotificationInteractor,
             ShadeAnimationInteractor shadeAnimationInteractor,
             KeyguardViewConfigurator keyguardViewConfigurator,
             DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor,
@@ -811,7 +806,6 @@
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
         mActiveNotificationsInteractor = activeNotificationsInteractor;
-        mHeadsUpNotificationInteractor = headsUpNotificationInteractor;
         mKeyguardInteractor = keyguardInteractor;
         mPowerInteractor = powerInteractor;
         mKeyguardViewConfigurator = keyguardViewConfigurator;
@@ -1222,11 +1216,6 @@
                     }
                 },
                 mMainDispatcher);
-
-        if (NotificationsHeadsUpRefactor.isEnabled()) {
-            collectFlow(mView, mHeadsUpNotificationInteractor.isHeadsUpOrAnimatingAway(),
-                    setHeadsUpVisible(), mMainDispatcher);
-        }
     }
 
     @VisibleForTesting
@@ -1339,6 +1328,10 @@
                 "NotificationPanelViewController.updateResources");
 
         if (splitShadeChanged) {
+            if (isPanelVisibleBecauseOfHeadsUp()) {
+                // workaround for b/324642496, because HUNs set state to OPENING
+                onPanelStateChanged(STATE_CLOSED);
+            }
             onSplitShadeEnabledChanged();
         }
 
@@ -3073,21 +3066,7 @@
         mPanelAlphaEndAction = r;
     }
 
-    private Consumer<Boolean> setHeadsUpVisible() {
-        return (Boolean isHeadsUpVisible) -> {
-            mHeadsUpVisible = isHeadsUpVisible;
-
-            if (isHeadsUpVisible) {
-                updateNotificationTranslucency();
-            }
-            updateExpansionAndVisibility();
-            updateGestureExclusionRect();
-            mKeyguardStatusBarViewController.updateForHeadsUp();
-        };
-    }
-
     private void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        NotificationsHeadsUpRefactor.assertInLegacyMode();
         mHeadsUpAnimatingAway = headsUpAnimatingAway;
         mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
         updateVisibility();
@@ -3103,16 +3082,13 @@
     }
 
     private boolean shouldPanelBeVisible() {
-        boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
-                : (mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
+        boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
         return headsUpVisible || isExpanded() || mBouncerShowing;
     }
 
     private void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
-        if (!NotificationsHeadsUpRefactor.isEnabled()) {
-            mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
-        }
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(
                 headsUpManager,
                 mStatusBarService,
@@ -3200,8 +3176,7 @@
     }
 
     private boolean isPanelVisibleBecauseOfHeadsUp() {
-        boolean headsUpVisible = NotificationsHeadsUpRefactor.isEnabled() ? mHeadsUpVisible
-                : (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway);
+        boolean headsUpVisible = mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
         return headsUpVisible && mBarState == StatusBarState.SHADE;
     }
 
@@ -3517,7 +3492,6 @@
         ipw.print("mExpandingFromHeadsUp="); ipw.println(mExpandingFromHeadsUp);
         ipw.print("mCollapsedOnDown="); ipw.println(mCollapsedOnDown);
         ipw.print("mClosingWithAlphaFadeOut="); ipw.println(mClosingWithAlphaFadeOut);
-        ipw.print("mHeadsUpVisible="); ipw.println(mHeadsUpVisible);
         ipw.print("mHeadsUpAnimatingAway="); ipw.println(mHeadsUpAnimatingAway);
         ipw.print("mShowIconsWhenExpanded="); ipw.println(mShowIconsWhenExpanded);
         ipw.print("mIndicationBottomPadding="); ipw.println(mIndicationBottomPadding);
@@ -4442,8 +4416,6 @@
     private final class ShadeHeadsUpChangedListener implements OnHeadsUpChangedListener {
         @Override
         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
-
             if (inPinnedMode) {
                 mHeadsUpExistenceChangedRunnable.run();
                 updateNotificationTranslucency();
@@ -4460,8 +4432,6 @@
 
         @Override
         public void onHeadsUpPinned(NotificationEntry entry) {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
-
             if (!isKeyguardShowing()) {
                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(entry, true);
             }
@@ -4469,8 +4439,6 @@
 
         @Override
         public void onHeadsUpUnPinned(NotificationEntry entry) {
-            NotificationsHeadsUpRefactor.assertInLegacyMode();
-
             // When we're unpinning the notification via active edge they remain heads-upped,
             // we need to make sure that an animation happens in this case, otherwise the
             // notification
@@ -4532,6 +4500,13 @@
     private final class StatusBarStateListener implements StateListener {
         @Override
         public void onStateChanged(int statusBarState) {
+            onStateChanged(statusBarState, false);
+        }
+
+        private void onStateChanged(
+                int statusBarState,
+                boolean animatingUnlockedShadeToKeyguardBypass
+        ) {
             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
             boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
             int oldState = mBarState;
@@ -4603,15 +4578,14 @@
                 //  - getting notified again about the current SHADE or KEYGUARD state
                 final boolean animatingUnlockedShadeToKeyguard = oldState == SHADE
                         && statusBarState == KEYGUARD
-                        && mScreenOffAnimationController.isKeyguardShowDelayed();
+                        && mScreenOffAnimationController.isKeyguardShowDelayed()
+                        //Bypasses animatingUnlockedShadeToKeyguard for b/337742708
+                        && !animatingUnlockedShadeToKeyguardBypass;
                 if (!animatingUnlockedShadeToKeyguard) {
                     // Only make the status bar visible if we're not animating the screen off, since
                     // we only want to be showing the clock/notifications during the animation.
-                    if (keyguardShowing) {
-                        mShadeLog.v("Updating keyguard status bar state to visible");
-                    } else {
-                        mShadeLog.v("Updating keyguard status bar state to invisible");
-                    }
+                    mShadeLog.logKeyguardStatudBarVisibiliy(keyguardShowing, isOnAod(),
+                            animatingUnlockedShadeToKeyguardBypass, oldState, statusBarState);
                     mKeyguardStatusBarViewController.updateViewState(
                             /* alpha= */ 1f,
                             keyguardShowing ? View.VISIBLE : View.INVISIBLE);
@@ -4688,7 +4662,8 @@
                     .addTagListener(QS.TAG, mQsController.getQsFragmentListener());
             if (!SceneContainerFlag.isEnabled()) {
                 mStatusBarStateController.addCallback(mStatusBarStateListener);
-                mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+                // Bypass animatingUnlockedShadeToKeyguard in onStateChanged for b/337742708
+                mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState(), true);
             }
             mConfigurationController.addCallback(mConfigurationListener);
             // Theme might have changed between inflating this view and attaching it to the
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 7e0454c..3f3ad13 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -49,6 +49,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.Flags;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.communal.domain.interactor.CommunalInteractor;
 import com.android.systemui.dagger.SysUISingleton;
@@ -342,6 +343,12 @@
                     this::setKeyguardOccluded
             );
         }
+        if (ComposeBouncerFlags.INSTANCE.isComposeBouncerOrSceneContainerEnabled()) {
+            collectFlow(mWindowRootView, mNotificationShadeWindowModel.isBouncerShowing(),
+                    this::setBouncerShowing);
+            collectFlow(mWindowRootView, mNotificationShadeWindowModel.getDoesBouncerRequireIme(),
+                    this::setKeyguardNeedsInput);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 16aef65..830649b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -1005,7 +1005,7 @@
         // When expanding QS, let's authenticate the user if possible,
         // this will speed up notification actions.
         if (height == 0 && !mKeyguardStateController.canDismissLockScreen()) {
-            mDeviceEntryFaceAuthInteractor.onQsExpansionStared();
+            mDeviceEntryFaceAuthInteractor.onShadeExpansionStarted();
         }
     }
 
@@ -1063,13 +1063,17 @@
         mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
         setClippingBounds();
 
-        if (mSplitShadeEnabled) {
-            // In split shade we want to pretend that QS are always collapsed so their behaviour and
-            // interactions don't influence notifications as they do in portrait. But we want to set
-            // 0 explicitly in case we're rotating from non-split shade with QS expansion of 1.
-            mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
-        } else {
-            mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+        if (!SceneContainerFlag.isEnabled()) {
+            if (mSplitShadeEnabled) {
+                // In split shade we want to pretend that QS are always collapsed so their
+                // behaviour and interactions don't influence notifications as they do in portrait.
+                // But we want to set 0 explicitly in case we're rotating from non-split shade with
+                // QS expansion of 1.
+                mNotificationStackScrollLayoutController.setQsExpansionFraction(0);
+            } else {
+                mNotificationStackScrollLayoutController.setQsExpansionFraction(
+                        qsExpansionFraction);
+            }
         }
 
         mDepthController.setQsPanelExpansion(qsExpansionFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index 23e2620..5d03a28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shade
 
 import android.view.MotionEvent
-import androidx.compose.ui.Alignment
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -25,7 +24,6 @@
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.SceneFamilies
 import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.OpenBottomShade
 import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
 import com.android.systemui.shade.ShadeController.ShadeVisibilityListener
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -177,7 +175,6 @@
         sceneInteractor.changeScene(
             SceneFamilies.NotifShade,
             "ShadeController.animateExpandShade",
-            OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
         )
     }
 
@@ -185,7 +182,6 @@
         sceneInteractor.changeScene(
             SceneFamilies.QuickSettings,
             "ShadeController.animateExpandQs",
-            OpenBottomShade.takeIf { shadeInteractor.shadeAlignment == Alignment.BottomEnd }
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 66a310c..f1eaec8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -411,4 +411,29 @@
             }
         )
     }
+
+    fun logKeyguardStatudBarVisibiliy(
+        visibility: Boolean,
+        isOnAod: Boolean,
+        animatingUnlockedShadeToKeyguardBypass: Boolean,
+        oldShadeState: Int,
+        newShadeState: Int,
+    ) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            {
+                bool1 = visibility
+                bool2 = isOnAod
+                bool3 = animatingUnlockedShadeToKeyguardBypass
+                int1 = oldShadeState
+                int2 = newShadeState
+            },
+            {
+                "Setting keyguard status bar visibility to: $bool1, isOnAod: $bool2" +
+                    "oldShadeState: $int1, newShadeState: $int2," +
+                    "animatingUnlockedShadeToKeyguardBypass: $bool3"
+            }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index bc23778..fc8a593 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.shade
 
 import android.annotation.SuppressLint
@@ -31,12 +33,14 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlags
 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.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
+import com.android.systemui.scene.ui.composable.Overlay
+import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.scene.ui.view.SceneWindowRootView
 import com.android.systemui.scene.ui.view.WindowRootView
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
@@ -58,6 +62,7 @@
 import dagger.Provides
 import javax.inject.Named
 import javax.inject.Provider
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Module for providing views related to the shade. */
 @Module
@@ -78,24 +83,28 @@
         @SysUISingleton
         fun providesWindowRootView(
             layoutInflater: LayoutInflater,
-            viewModelProvider: Provider<SceneContainerViewModel>,
+            viewModelFactory: SceneContainerViewModel.Factory,
             containerConfigProvider: Provider<SceneContainerConfig>,
             scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
+            overlaysProvider: Provider<Set<@JvmSuppressWildcards Overlay>>,
             layoutInsetController: NotificationInsetsController,
             sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
+            alternateBouncerDependencies: Provider<AlternateBouncerDependencies>,
         ): WindowRootView {
             return if (SceneContainerFlag.isEnabled) {
                 checkNoSceneDuplicates(scenesProvider.get())
                 val sceneWindowRootView =
                     layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
                 sceneWindowRootView.init(
-                    viewModel = viewModelProvider.get(),
+                    viewModelFactory = viewModelFactory,
                     containerConfig = containerConfigProvider.get(),
                     sharedNotificationContainer =
                         sceneWindowRootView.requireViewById(R.id.shared_notification_container),
                     scenes = scenesProvider.get(),
+                    overlays = overlaysProvider.get(),
                     layoutInsetController = layoutInsetController,
                     sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
+                    alternateBouncerDependencies = alternateBouncerDependencies.get(),
                 )
                 sceneWindowRootView
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index a4fed873..193056c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -18,7 +18,6 @@
 import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.res.R
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -103,9 +102,6 @@
     @Deprecated("Use ShadeInteractor.isQsBypassingShade instead")
     val legacyExpandImmediate: StateFlow<Boolean>
 
-    /** Whether dual shade should be aligned to the bottom (true) or to the top (false). */
-    val isDualShadeAlignedToBottom: Boolean
-
     /**
      * Whether the shade layout should be wide (true) or narrow (false).
      *
@@ -238,9 +234,6 @@
     private val _isShadeLayoutWide = MutableStateFlow(false)
     override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
 
-    override val isDualShadeAlignedToBottom =
-        applicationContext.resources.getBoolean(R.bool.config_dualShadeAlignedToBottom)
-
     override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
         _isShadeLayoutWide.value = isShadeLayoutWide
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
index 8006e942..e276f88 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
@@ -64,7 +64,7 @@
                             0f
                         }
                     )
-                is ObservableTransitionState.Transition ->
+                is ObservableTransitionState.Transition.ChangeScene ->
                     when {
                         state.fromScene == Scenes.Gone ->
                             if (state.toScene.isExpandable()) {
@@ -88,6 +88,9 @@
                             }
                         else -> flowOf(1f)
                     }
+                is ObservableTransitionState.Transition.ShowOrHideOverlay,
+                is ObservableTransitionState.Transition.ReplaceOverlay ->
+                    TODO("b/359173565: Handle overlay transitions")
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
index 79a94a5..8467185 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeAnimationInteractorSceneContainerImpl.kt
@@ -49,10 +49,10 @@
                     is ObservableTransitionState.Idle -> flowOf(false)
                     is ObservableTransitionState.Transition ->
                         if (
-                            (state.fromScene == Scenes.Shade &&
-                                state.toScene != Scenes.QuickSettings) ||
-                                (state.fromScene == Scenes.QuickSettings &&
-                                    state.toScene != Scenes.Shade)
+                            (state.fromContent == Scenes.Shade &&
+                                state.toContent != Scenes.QuickSettings) ||
+                                (state.fromContent == Scenes.QuickSettings &&
+                                    state.toContent != Scenes.Shade)
                         ) {
                             state.isUserInputOngoing.map { !it }
                         } else {
@@ -71,10 +71,10 @@
                     is ObservableTransitionState.Transition ->
                         if (
                             state.isInitiatedByUserInput &&
-                                (state.fromScene == Scenes.Shade ||
-                                    state.toScene == Scenes.Shade ||
-                                    state.fromScene == Scenes.QuickSettings ||
-                                    state.toScene == Scenes.QuickSettings)
+                                (state.fromContent == Scenes.Shade ||
+                                    state.toContent == Scenes.Shade ||
+                                    state.fromContent == Scenes.QuickSettings ||
+                                    state.toContent == Scenes.QuickSettings)
                         ) {
                             state.isUserInputOngoing.map { !it }
                         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 45f359e..73e86a2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.shade.domain.interactor
 
-import com.android.systemui.shade.shared.model.ShadeAlignment
 import com.android.systemui.shade.shared.model.ShadeMode
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -70,9 +69,6 @@
      * wide as the entire screen.
      */
     val isShadeLayoutWide: StateFlow<Boolean>
-
-    /** How to align the shade content. */
-    val shadeAlignment: ShadeAlignment
 }
 
 /** ShadeInteractor methods with implementations that differ between non-empty impls. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
index e77aca9..d51fd28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.shade.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.shade.shared.model.ShadeAlignment
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -48,5 +47,4 @@
     override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean
     override val shadeMode: StateFlow<ShadeMode> = MutableStateFlow(ShadeMode.Single)
     override val isShadeLayoutWide: StateFlow<Boolean> = inactiveFlowBoolean
-    override val shadeAlignment: ShadeAlignment = ShadeAlignment.Top
 }
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 d64b21f..3552092 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
@@ -26,7 +26,6 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.shade.shared.flag.DualShade
-import com.android.systemui.shade.shared.model.ShadeAlignment
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
 import com.android.systemui.statusbar.phone.DozeParameters
@@ -114,15 +113,6 @@
                 initialValue = determineShadeMode(isShadeLayoutWide.value)
             )
 
-    override val shadeAlignment: ShadeAlignment
-        get() {
-            return if (shadeRepository.isDualShadeAlignedToBottom) {
-                ShadeAlignment.Bottom
-            } else {
-                ShadeAlignment.Top
-            }
-        }
-
     override val isExpandToQsEnabled: Flow<Boolean> =
         combine(
             disableFlagsRepository.disableFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 6a21531..e84cfa5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -90,8 +90,8 @@
                         when (state) {
                             is ObservableTransitionState.Idle -> false
                             is ObservableTransitionState.Transition ->
-                                state.toScene == quickSettingsScene &&
-                                    state.fromScene != notificationsScene
+                                state.toContent == quickSettingsScene &&
+                                    state.fromContent != notificationsScene
                         }
                     }
                     .distinctUntilChanged()
@@ -99,14 +99,17 @@
             .distinctUntilChanged()
 
     override val isQsFullscreen: Flow<Boolean> =
-        sceneInteractor
-            .resolveSceneFamily(SceneFamilies.QuickSettings)
-            .flatMapLatestConflated { quickSettingsScene ->
+        combine(
+                shadeRepository.isShadeLayoutWide,
+                sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
+                ::Pair
+            )
+            .flatMapLatestConflated { (isShadeLayoutWide, quickSettingsScene) ->
                 sceneInteractor.transitionState
                     .map { state ->
                         when (state) {
                             is ObservableTransitionState.Idle ->
-                                state.currentScene == quickSettingsScene
+                                !isShadeLayoutWide && state.currentScene == quickSettingsScene
                             is ObservableTransitionState.Transition -> false
                         }
                     }
@@ -147,9 +150,9 @@
                                     flowOf(0f)
                                 }
                             is ObservableTransitionState.Transition ->
-                                if (state.toScene == resolvedSceneKey) {
+                                if (state.toContent == resolvedSceneKey) {
                                     state.progress
-                                } else if (state.fromScene == resolvedSceneKey) {
+                                } else if (state.fromContent == resolvedSceneKey) {
                                     state.progress.map { progress -> 1 - progress }
                                 } else {
                                     flowOf(0f)
@@ -172,8 +175,8 @@
                     is ObservableTransitionState.Transition ->
                         sceneInteractor.resolveSceneFamily(sceneKey).map { resolvedSceneKey ->
                             state.isInitiatedByUserInput &&
-                                (state.toScene == resolvedSceneKey ||
-                                    state.fromScene == resolvedSceneKey)
+                                (state.toContent == resolvedSceneKey ||
+                                    state.fromContent == resolvedSceneKey)
                         }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
deleted file mode 100644
index 06905379..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeAlignment.kt
+++ /dev/null
@@ -1,27 +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.shade.shared.model
-
-/** Enumerates all supported alignments of the shade. */
-sealed interface ShadeAlignment {
-
-    /** Aligns the shade to the top. */
-    data object Top : ShadeAlignment
-
-    /** Aligns the shade to the bottom. */
-    data object Bottom : ShadeAlignment
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt
deleted file mode 100644
index 068d6a7..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/BaseShadeSceneViewModel.kt
+++ /dev/null
@@ -1,56 +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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.shade.ui.viewmodel
-
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
-import com.android.systemui.lifecycle.SysUiViewModel
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.Scenes
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
-
-/** Base class for classes that model UI state of the content of shade scenes. */
-abstract class BaseShadeSceneViewModel(
-    private val deviceEntryInteractor: DeviceEntryInteractor,
-    private val sceneInteractor: SceneInteractor,
-) : SysUiViewModel() {
-
-    private val _isEmptySpaceClickable =
-        MutableStateFlow(!deviceEntryInteractor.isDeviceEntered.value)
-    /** Whether clicking on the empty area of the shade does something */
-    val isEmptySpaceClickable: StateFlow<Boolean> = _isEmptySpaceClickable.asStateFlow()
-
-    override suspend fun onActivated() {
-        deviceEntryInteractor.isDeviceEntered.collectLatest { isDeviceEntered ->
-            _isEmptySpaceClickable.value = !isDeviceEntered
-        }
-    }
-
-    /** Notifies that the empty space in the shade has been clicked. */
-    fun onEmptySpaceClicked() {
-        if (!isEmptySpaceClickable.value) {
-            return
-        }
-
-        sceneInteractor.changeScene(Scenes.Lockscreen, "Shade empty space clicked.")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
index 2f98488..9655d92 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/NotificationShadeWindowModel.kt
@@ -16,14 +16,25 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
 import com.android.systemui.dagger.SysUISingleton
-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
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
-import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+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.util.kotlin.BooleanFlowOperators.any
+import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 
 /** Models UI state for the shade window. */
@@ -32,11 +43,92 @@
 @Inject
 constructor(
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
-    keyguardInteractor: KeyguardInteractor,
+    sceneInteractor: dagger.Lazy<SceneInteractor>,
+    authenticationInteractor: dagger.Lazy<AuthenticationInteractor>,
+    primaryBouncerInteractor: PrimaryBouncerInteractor,
 ) {
+    /**
+     * Considered to be occluded if in OCCLUDED, DREAMING, GLANCEABLE_HUB/Communal, or transitioning
+     * between those states. Every permutation is listed so we can use optimal flows and support
+     * Scenes.
+     */
     val isKeyguardOccluded: Flow<Boolean> =
-        anyOf(
-            keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
-            keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
-        )
+        listOf(
+                // Finished in state...
+                keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
+                keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
+                keyguardTransitionInteractor.transitionValue(Scenes.Communal, GLANCEABLE_HUB).map {
+                    it == 1f
+                },
+
+                // ... or transitions between those states
+                keyguardTransitionInteractor.isInTransition(Edge.create(OCCLUDED, DREAMING)),
+                keyguardTransitionInteractor.isInTransition(Edge.create(DREAMING, OCCLUDED)),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = OCCLUDED, to = Scenes.Communal),
+                    edgeWithoutSceneContainer = Edge.create(from = OCCLUDED, to = GLANCEABLE_HUB),
+                ),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
+                    edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED),
+                ),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = DREAMING, to = Scenes.Communal),
+                    edgeWithoutSceneContainer = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
+                ),
+                keyguardTransitionInteractor.isInTransition(
+                    edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+                    edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+                ),
+            )
+            .any()
+
+    /**
+     * Whether bouncer is currently showing or not.
+     *
+     * Applicable only when either [SceneContainerFlag] or [ComposeBouncerFlags] are enabled,
+     * otherwise it throws an error.
+     */
+    val isBouncerShowing: Flow<Boolean> =
+        when {
+            SceneContainerFlag.isEnabled -> {
+                sceneInteractor.get().transitionState.map { it.isIdle(Scenes.Bouncer) }
+            }
+            ComposeBouncerFlags.isOnlyComposeBouncerEnabled() -> primaryBouncerInteractor.isShowing
+            else ->
+                flow {
+                    error(
+                        "Consume this flow only when SceneContainerFlag " +
+                            "or ComposeBouncerFlags are enabled"
+                    )
+                }
+        }.distinctUntilChanged()
+
+    /**
+     * Whether the bouncer currently require IME for device entry.
+     *
+     * This emits true when the authentication method is set to password and the bouncer is
+     * currently showing. Throws an error when this is used without either [SceneContainerFlag] or
+     * [ComposeBouncerFlags]
+     */
+    val doesBouncerRequireIme: Flow<Boolean> =
+        if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
+                // This is required to make the window, where the bouncer resides,
+                // focusable. InputMethodManager allows IME to be shown only for views
+                // in windows that do not have the FLAG_NOT_FOCUSABLE flag.
+
+                isBouncerShowing
+                    .sample(authenticationInteractor.get().authenticationMethod, ::Pair)
+                    .map { (showing, authMethod) ->
+                        showing && authMethod == AuthenticationMethodModel.Password
+                    }
+            } else {
+                flow {
+                    error(
+                        "Consume this flow only when SceneContainerFlag " +
+                            "or ComposeBouncerFlags are enabled"
+                    )
+                }
+            }
+            .distinctUntilChanged()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
deleted file mode 100644
index 566bc16..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModel.kt
+++ /dev/null
@@ -1,65 +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.shade.ui.viewmodel
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.lifecycle.SysUiViewModel
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Models UI state and handles user input for the overlay shade UI, which shows a shade as an
- * overlay on top of another scene UI.
- */
-class OverlayShadeViewModel
-@AssistedInject
-constructor(private val sceneInteractor: SceneInteractor, shadeInteractor: ShadeInteractor) :
-    SysUiViewModel() {
-    private val _backgroundScene = MutableStateFlow(Scenes.Lockscreen)
-    /** The scene to show in the background when the overlay shade is open. */
-    val backgroundScene: StateFlow<SceneKey> = _backgroundScene.asStateFlow()
-
-    /** Dictates the alignment of the overlay shade panel on the screen. */
-    val panelAlignment = shadeInteractor.shadeAlignment
-
-    override suspend fun onActivated() {
-        sceneInteractor.resolveSceneFamily(SceneFamilies.Home).collectLatest { sceneKey ->
-            _backgroundScene.value = sceneKey
-        }
-    }
-
-    /** Notifies that the user has clicked the semi-transparent background scrim. */
-    fun onScrimClicked() {
-        sceneInteractor.changeScene(
-            toScene = SceneFamilies.Home,
-            loggingReason = "Shade scrim clicked",
-        )
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): OverlayShadeViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 03fdfa9..a154e91 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -24,7 +24,7 @@
 import android.os.UserHandle
 import android.provider.Settings
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.lifecycle.SysUiViewModel
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyItem
@@ -41,11 +41,11 @@
 import dagger.assisted.AssistedInject
 import java.util.Date
 import java.util.Locale
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
@@ -64,7 +64,7 @@
     private val privacyChipInteractor: PrivacyChipInteractor,
     private val clockInteractor: ShadeHeaderClockInteractor,
     private val broadcastDispatcher: BroadcastDispatcher,
-) : SysUiViewModel() {
+) : ExclusiveActivatable() {
     /** True if there is exactly one mobile connection. */
     val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier
 
@@ -104,7 +104,7 @@
     private val _longerDateText: MutableStateFlow<String> = MutableStateFlow("")
     val longerDateText: StateFlow<String> = _longerDateText.asStateFlow()
 
-    override suspend fun onActivated() {
+    override suspend fun onActivated(): Nothing {
         coroutineScope {
             launch {
                 broadcastDispatcher
@@ -131,12 +131,12 @@
             launch {
                 mobileIconsInteractor.filteredSubscriptions
                     .map { list -> list.map { it.subscriptionId } }
-                    .collectLatest { _mobileSubIds.value = it }
+                    .collect { _mobileSubIds.value = it }
             }
 
-            launch {
-                shadeInteractor.isQsEnabled.map { !it }.collectLatest { _isDisabled.value = it }
-            }
+            launch { shadeInteractor.isQsEnabled.map { !it }.collect { _isDisabled.value = it } }
+
+            awaitCancellation()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
deleted file mode 100644
index bdc0fdb..0000000
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModel.kt
+++ /dev/null
@@ -1,77 +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.shade.ui.viewmodel
-
-import com.android.compose.animation.scene.Swipe
-import com.android.compose.animation.scene.SwipeDirection
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.qs.ui.adapter.QSSceneAdapter
-import com.android.systemui.scene.shared.model.SceneFamilies
-import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
-import com.android.systemui.scene.ui.viewmodel.SceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.model.ShadeMode
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.combine
-
-/**
- * Models the UI state for the user actions that the user can perform to navigate to other scenes.
- *
- * Different from the [ShadeSceneContentViewModel] which models the _content_ of the scene.
- */
-class ShadeSceneActionsViewModel
-@AssistedInject
-constructor(
-    private val qsSceneAdapter: QSSceneAdapter,
-    private val shadeInteractor: ShadeInteractor,
-) : SceneActionsViewModel() {
-
-    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
-        combine(
-                shadeInteractor.shadeMode,
-                qsSceneAdapter.isCustomizerShowing,
-            ) { shadeMode, isCustomizerShowing ->
-                buildMap<UserAction, UserActionResult> {
-                    if (!isCustomizerShowing) {
-                        set(
-                            Swipe(SwipeDirection.Up),
-                            UserActionResult(
-                                SceneFamilies.Home,
-                                ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
-                            )
-                        )
-                    }
-
-                    // TODO(b/330200163) Add an else to be able to collapse the shade while
-                    // customizing
-                    if (shadeMode is ShadeMode.Single) {
-                        set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
-                    }
-                }
-            }
-            .collectLatest { actions -> setActions(actions) }
-    }
-
-    @AssistedFactory
-    interface Factory {
-        fun create(): ShadeSceneActionsViewModel
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
index 3cdff96..ce4c081 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneContentViewModel.kt
@@ -20,11 +20,13 @@
 
 import androidx.lifecycle.LifecycleOwner
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.qs.FooterActionsController
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
@@ -34,12 +36,14 @@
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
 
 /**
  * Models UI state used to render the content of the shade scene.
  *
- * Different from [ShadeSceneActionsViewModel], which only models user actions that can be performed
+ * Different from [ShadeUserActionsViewModel], which only models user actions that can be performed
  * to navigate to other scenes.
  */
 class ShadeSceneContentViewModel
@@ -53,20 +57,27 @@
     private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
     private val footerActionsController: FooterActionsController,
     private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
-    deviceEntryInteractor: DeviceEntryInteractor,
-    sceneInteractor: SceneInteractor,
-) :
-    BaseShadeSceneViewModel(
-        deviceEntryInteractor,
-        sceneInteractor,
-    ) {
+    private val deviceEntryInteractor: DeviceEntryInteractor,
+    private val sceneInteractor: SceneInteractor,
+) : ExclusiveActivatable() {
 
     val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
 
+    private val _isEmptySpaceClickable =
+        MutableStateFlow(!deviceEntryInteractor.isDeviceEntered.value)
+    /** Whether clicking on the empty area of the shade does something */
+    val isEmptySpaceClickable: StateFlow<Boolean> = _isEmptySpaceClickable.asStateFlow()
+
     val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation
 
     private val footerActionsControllerInitialized = AtomicBoolean(false)
 
+    override suspend fun onActivated(): Nothing {
+        deviceEntryInteractor.isDeviceEntered.collect { isDeviceEntered ->
+            _isEmptySpaceClickable.value = !isDeviceEntered
+        }
+    }
+
     /**
      * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
      * slightly, in pixels.
@@ -82,6 +93,15 @@
         return footerActionsViewModelFactory.create(lifecycleOwner)
     }
 
+    /** Notifies that the empty space in the shade has been clicked. */
+    fun onEmptySpaceClicked() {
+        if (!isEmptySpaceClickable.value) {
+            return
+        }
+
+        sceneInteractor.changeScene(Scenes.Lockscreen, "Shade empty space clicked.")
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(): ShadeSceneContentViewModel
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
new file mode 100644
index 0000000..f8a850a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.compose.animation.scene.Swipe
+import com.android.compose.animation.scene.SwipeDirection
+import com.android.compose.animation.scene.UserAction
+import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
+import com.android.systemui.scene.shared.model.SceneFamilies
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade
+import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.flow.combine
+
+/**
+ * Models the UI state for the user actions that the user can perform to navigate to other scenes.
+ *
+ * Different from the [ShadeSceneContentViewModel] which models the _content_ of the scene.
+ */
+class ShadeUserActionsViewModel
+@AssistedInject
+constructor(
+    private val qsSceneAdapter: QSSceneAdapter,
+    private val shadeInteractor: ShadeInteractor,
+) : UserActionsViewModel() {
+
+    override suspend fun hydrateActions(setActions: (Map<UserAction, UserActionResult>) -> Unit) {
+        combine(
+                shadeInteractor.shadeMode,
+                qsSceneAdapter.isCustomizerShowing,
+            ) { shadeMode, isCustomizerShowing ->
+                buildMap<UserAction, UserActionResult> {
+                    if (!isCustomizerShowing) {
+                        set(
+                            Swipe(SwipeDirection.Up),
+                            UserActionResult(
+                                SceneFamilies.Home,
+                                ToSplitShade.takeIf { shadeMode is ShadeMode.Split }
+                            )
+                        )
+                    }
+
+                    // TODO(b/330200163) Add an else to be able to collapse the shade while
+                    // customizing
+                    if (shadeMode is ShadeMode.Single) {
+                        set(Swipe(SwipeDirection.Down), UserActionResult(Scenes.QuickSettings))
+                    }
+                }
+            }
+            .collect { actions -> setActions(actions) }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): ShadeUserActionsViewModel
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
index 922560f..0e1bf72 100644
--- a/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/smartspace/config/BcSmartspaceConfigProvider.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.smartspace.config
 
+import com.android.systemui.Flags.smartspaceViewpager2
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.BcSmartspaceConfigPlugin
 
@@ -23,4 +24,7 @@
     BcSmartspaceConfigPlugin {
     override val isDefaultDateWeatherDisabled: Boolean
         get() = true
+
+    override val isViewPager2Enabled: Boolean
+        get() = smartspaceViewpager2()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 50be6dc..f88fd7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -37,6 +37,7 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
 import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.media.INearbyMediaDevicesProvider;
 import android.media.MediaRoute2Info;
 import android.os.Binder;
@@ -257,10 +258,10 @@
          *
          * @param displayId The id of the display to notify.
          * @param vis IME visibility.
-         * @param backDisposition Disposition mode of back button. It should be one of below flags:
+         * @param backDisposition Disposition mode of back button.
          * @param showImeSwitcher {@code true} to show IME switch button.
          */
-        default void setImeWindowStatus(int displayId, int vis,
+        default void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
                 @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
         default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
@@ -743,8 +744,8 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
             SomeArgs args = SomeArgs.obtain();
@@ -1173,7 +1174,7 @@
         }
     }
 
-    @Override
+    // This was previously called from WM, but is now called from WMShell
     public void onRecentsAnimationStateChanged(boolean running) {
         synchronized (mLock) {
             mHandler.obtainMessage(MSG_RECENTS_ANIMATION_STATE_CHANGED, running ? 1 : 0, 0)
@@ -1205,8 +1206,8 @@
         }
     }
 
-    private void handleShowImeButton(int displayId, int vis, int backDisposition,
-            boolean showImeSwitcher) {
+    private void handleShowImeButton(int displayId, @ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
         if (displayId == INVALID_DISPLAY) return;
 
         boolean isConcurrentMultiUserModeEnabled = UserManager.isVisibleBackgroundUsersEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 693cc4a..5ef5a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -28,6 +28,9 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
 
+import static com.android.systemui.Flags.enableViewCaptureTracing;
+import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
+
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
@@ -73,12 +76,17 @@
 import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.res.R;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Lazy;
+
 import javax.inject.Inject;
 
 /**
@@ -100,17 +108,18 @@
     private Context mDisplayContext;
     private final Context mSysUiContext;
     private final Handler mHandler = new H(Looper.getMainLooper());
+    private final Handler mBackgroundHandler;
     private long mShowDelayMs = 0L;
     private final IBinder mWindowToken = new Binder();
     private final CommandQueue mCommandQueue;
 
     private ClingWindowView mClingWindow;
-    /** The last {@link WindowManager} that is used to add the confirmation window. */
+    /** The wrapper on the last {@link WindowManager} used to add the confirmation window. */
     @Nullable
-    private WindowManager mWindowManager;
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     /**
-     * The WindowContext that is registered with {@link #mWindowManager} with options to specify the
-     * {@link RootDisplayArea} to attach the confirmation window.
+     * The WindowContext that is registered with {@link #mViewCaptureAwareWindowManager} with
+     * options to specify the {@link RootDisplayArea} to attach the confirmation window.
      */
     @Nullable
     private Context mWindowContext;
@@ -127,15 +136,21 @@
 
     private ContentObserver mContentObserver;
 
+    private Lazy<ViewCapture> mLazyViewCapture;
+
     @Inject
     public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
-                                     SecureSettings secureSettings) {
+                                     SecureSettings secureSettings,
+                                     dagger.Lazy<ViewCapture> daggerLazyViewCapture,
+                                     @Background Handler backgroundHandler) {
         mSysUiContext = context;
         final Display display = mSysUiContext.getDisplay();
         mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
                 ? mSysUiContext : mSysUiContext.createDisplayContext(display);
         mCommandQueue = commandQueue;
         mSecureSettings = secureSettings;
+        mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+        mBackgroundHandler = backgroundHandler;
     }
 
     boolean loadSetting(int currentUserId) {
@@ -239,14 +254,14 @@
     private void handleHide() {
         if (mClingWindow != null) {
             if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation");
-            if (mWindowManager != null) {
+            if (mViewCaptureAwareWindowManager != null) {
                 try {
-                    mWindowManager.removeView(mClingWindow);
+                    mViewCaptureAwareWindowManager.removeView(mClingWindow);
                 } catch (WindowManager.InvalidDisplayException e) {
                     Log.w(TAG, "Fail to hide the immersive confirmation window because of "
                             + e);
                 }
-                mWindowManager = null;
+                mViewCaptureAwareWindowManager = null;
                 mWindowContext = null;
             }
             mClingWindow = null;
@@ -318,7 +333,7 @@
                 }
             }
             TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
-            mContentObserver = new ContentObserver(mHandler) {
+            mContentObserver = new ContentObserver(mBackgroundHandler) {
                 @Override
                 public void onChange(boolean selfChange) {
                     onSettingChanged(mSysUiContext.getUserId());
@@ -332,6 +347,9 @@
             mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
                     UserHandle.USER_CURRENT);
+            mBackgroundHandler.post(() -> {
+                loadSetting(UserHandle.USER_CURRENT);
+            });
         }
     }
 
@@ -505,8 +523,8 @@
      *         confirmation window.
      */
     @NonNull
-    private WindowManager createWindowManager(int rootDisplayAreaId) {
-        if (mWindowManager != null) {
+    private ViewCaptureAwareWindowManager createWindowManager(int rootDisplayAreaId) {
+        if (mViewCaptureAwareWindowManager != null) {
             throw new IllegalStateException(
                     "Must not create a new WindowManager while there is an existing one");
         }
@@ -515,8 +533,10 @@
         mWindowContextRootDisplayAreaId = rootDisplayAreaId;
         mWindowContext = mDisplayContext.createWindowContext(
                 IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
-        mWindowManager = mWindowContext.getSystemService(WindowManager.class);
-        return mWindowManager;
+        WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
+        mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(wm, mLazyViewCapture,
+                enableViewCaptureTracing());
+        return mViewCaptureAwareWindowManager;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 14e14f4..1481b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
@@ -45,7 +44,7 @@
 import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.SplitShadeStateController
-import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.animation.Interpolators
 import dagger.Lazy
 import java.io.PrintWriter
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 696e222..d523bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -255,8 +255,8 @@
         }
 
         final float stackBottom = SceneContainerFlag.isEnabled()
-                ? ambientState.getStackTop() + ambientState.getStackHeight()
-                : ambientState.getStackY() + ambientState.getStackHeight();
+                ? ambientState.getStackTop() + ambientState.getInterpolatedStackHeight()
+                : ambientState.getStackY() + ambientState.getInterpolatedStackHeight();
 
         if (viewState.hidden) {
             // if the shelf is hidden, position it at the end of the stack (plus the clip
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
index 69ebb76..32d37ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -5,3 +5,20 @@
 [email protected]
 [email protected]
 [email protected]
+
+per-file *Biometrics* = set noparent
+per-file *Biometrics* = file:../keyguard/OWNERS
+per-file *Doze* = set noparent
+per-file *Doze* = file:../keyguard/OWNERS
+per-file *Keyboard* = set noparent
+per-file *Keyboard* = file:../keyguard/OWNERS
+per-file *Keyguard* = set noparent
+per-file *Keyguard* = file:../keyguard/OWNERS
+per-file *Notification* = set noparent
+per-file *Notification* = file:notification/OWNERS
+# Not setting noparent here, since *Mode* matches many other classes (e.g., *ViewModel*)
+per-file *Mode* = file:notification/OWNERS
+per-file *RemoteInput* = set noparent
+per-file *RemoteInput* = file:notification/OWNERS
+per-file *EmptyShadeView* = set noparent
+per-file *EmptyShadeView* = file:notification/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3068460..87f360e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -58,6 +58,7 @@
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIcon.Shape;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Flags;
 import com.android.systemui.res.R;
@@ -211,16 +212,19 @@
     /** Should always be preceded by {@link #reloadDimens()} */
     @VisibleForTesting
     public void maybeUpdateIconScaleDimens() {
-        // We do not resize and scale system icons (on the right), only notification icons (on the
-        // left).
-        if (isNotification()) {
-            updateIconScaleForNotifications();
+        // We scale notification icons (on the left) plus icons on the right that explicitly
+        // want FIXED_SPACE.
+        boolean useNonSystemIconScaling = isNotification()
+                || (usesModeIcons() && mIcon != null && mIcon.shape == Shape.FIXED_SPACE);
+
+        if (useNonSystemIconScaling) {
+            updateIconScaleForNonSystemIcons();
         } else {
             updateIconScaleForSystemIcons();
         }
     }
 
-    private void updateIconScaleForNotifications() {
+    private void updateIconScaleForNonSystemIcons() {
         float iconScale;
         // we need to scale the image size to be same as the original size
         // (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize
@@ -411,7 +415,9 @@
         if (!levelEquals) {
             setImageLevel(icon.iconLevel);
         }
-
+        if (usesModeIcons() && icon.shape == Shape.FIXED_SPACE) {
+            setScaleType(ScaleType.FIT_CENTER);
+        }
         if (!visibilityEquals) {
             setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE);
         }
@@ -471,17 +477,7 @@
      */
     private Drawable getIcon(Context sysuiContext,
             Context context, StatusBarIcon statusBarIcon) {
-        int userId = statusBarIcon.user.getIdentifier();
-        if (userId == UserHandle.USER_ALL) {
-            userId = UserHandle.USER_SYSTEM;
-        }
-
-        // Try to load the monochrome app icon if applicable
-        Drawable icon = maybeGetMonochromeAppIcon(context, statusBarIcon);
-        // Otherwise, just use the icon normally
-        if (icon == null) {
-            icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
-        }
+        Drawable icon = loadDrawable(context, statusBarIcon);
 
         TypedValue typedValue = new TypedValue();
         sysuiContext.getResources().getValue(R.dimen.status_bar_icon_scale_factor,
@@ -509,6 +505,31 @@
     }
 
     @Nullable
+    private Drawable loadDrawable(Context context, StatusBarIcon statusBarIcon) {
+        if (usesModeIcons() && statusBarIcon.preloadedIcon != null) {
+            Drawable.ConstantState cached = statusBarIcon.preloadedIcon.getConstantState();
+            if (cached != null) {
+                return cached.newDrawable(mContext.getResources()).mutate();
+            } else {
+                return statusBarIcon.preloadedIcon.mutate();
+            }
+        } else {
+            int userId = statusBarIcon.user.getIdentifier();
+            if (userId == UserHandle.USER_ALL) {
+                userId = UserHandle.USER_SYSTEM;
+            }
+
+            // Try to load the monochrome app icon if applicable
+            Drawable icon = maybeGetMonochromeAppIcon(context, statusBarIcon);
+            // Otherwise, just use the icon normally
+            if (icon == null) {
+                icon = statusBarIcon.icon.loadDrawableAsUser(context, userId);
+            }
+            return icon;
+        }
+    }
+
+    @Nullable
     private Drawable maybeGetMonochromeAppIcon(Context context,
             StatusBarIcon statusBarIcon) {
         if (android.app.Flags.notificationsUseMonochromeAppIcon()
@@ -1020,4 +1041,9 @@
     public boolean showsConversation() {
         return mShowsConversation;
     }
+
+    private static boolean usesModeIcons() {
+        return android.app.Flags.modesApi() && android.app.Flags.modesUi()
+                && android.app.Flags.modesUiIcons();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 0957e5a..3422c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -54,6 +54,9 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.data.model.SceneStack;
+import com.android.systemui.scene.data.model.SceneStackKt;
+import com.android.systemui.scene.domain.interactor.SceneBackInteractor;
 import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
 import com.android.systemui.scene.domain.interactor.SceneInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -118,6 +121,7 @@
     private final Lazy<SceneInteractor> mSceneInteractorLazy;
     private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractorLazy;
     private final Lazy<KeyguardClockInteractor> mKeyguardClockInteractorLazy;
+    private final Lazy<SceneBackInteractor> mSceneBackInteractorLazy;
     private int mState;
     private int mLastState;
     private int mUpcomingState;
@@ -186,7 +190,8 @@
             Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
             Lazy<SceneInteractor> sceneInteractorLazy,
             Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor,
-            Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy) {
+            Lazy<KeyguardClockInteractor> keyguardClockInteractorLazy,
+            Lazy<SceneBackInteractor> sceneBackInteractorLazy) {
         mUiEventLogger = uiEventLogger;
         mInteractionJankMonitorLazy = interactionJankMonitorLazy;
         mJavaAdapter = javaAdapter;
@@ -196,6 +201,7 @@
         mSceneInteractorLazy = sceneInteractorLazy;
         mSceneContainerOcclusionInteractorLazy = sceneContainerOcclusionInteractor;
         mKeyguardClockInteractorLazy = keyguardClockInteractorLazy;
+        mSceneBackInteractorLazy = sceneBackInteractorLazy;
         for (int i = 0; i < HISTORY_SIZE; i++) {
             mHistoricalRecords[i] = new HistoricalState();
         }
@@ -221,6 +227,7 @@
                     combineFlows(
                         mDeviceUnlockedInteractorLazy.get().getDeviceUnlockStatus(),
                         mSceneInteractorLazy.get().getCurrentScene(),
+                        mSceneBackInteractorLazy.get().getBackStack(),
                         mSceneContainerOcclusionInteractorLazy.get().getInvisibleDueToOcclusion(),
                         this::calculateStateFromSceneFramework),
                     this::onStatusBarStateChanged);
@@ -677,10 +684,15 @@
     private int calculateStateFromSceneFramework(
             DeviceUnlockStatus deviceUnlockStatus,
             SceneKey currentScene,
+            SceneStack backStack,
             boolean isOccluded) {
         SceneContainerFlag.isUnexpectedlyInLegacyMode();
-
-        if (deviceUnlockStatus.isUnlocked() || isOccluded) {
+        if (currentScene.equals(Scenes.Lockscreen)) {
+            return StatusBarState.KEYGUARD;
+        } else if (currentScene.equals(Scenes.Shade)
+                && SceneStackKt.contains(backStack, Scenes.Lockscreen)) {
+            return StatusBarState.SHADE_LOCKED;
+        } else if (deviceUnlockStatus.isUnlocked() || isOccluded) {
             return StatusBarState.SHADE;
         } else {
             return Preconditions.checkNotNull(sStatusBarStateByLockedSceneKey.get(currentScene));
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 173ff37..be733d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -16,14 +16,24 @@
 
 package com.android.systemui.statusbar.chips
 
+import com.android.systemui.CoreStartable
 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 dagger.Binds
 import dagger.Module
 import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 
 @Module
 abstract class StatusBarChipsModule {
+    @Binds
+    @IntoMap
+    @ClassKey(DemoRonChipViewModel::class)
+    abstract fun binds(impl: DemoRonChipViewModel): CoreStartable
+
     companion object {
         @Provides
         @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 18ea0b4..e825258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -69,7 +69,7 @@
                                     state.notificationIconView
                                 )
                             } else {
-                                OngoingActivityChipModel.ChipIcon.Basic(phoneIcon)
+                                OngoingActivityChipModel.ChipIcon.SingleColorIcon(phoneIcon)
                             }
 
                         // This block mimics OngoingCallController#updateChip.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index cf4e707..d4ad6ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -190,7 +190,7 @@
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
-                OngoingActivityChipModel.ChipIcon.Basic(
+                OngoingActivityChipModel.ChipIcon.SingleColorIcon(
                     Icon.Resource(
                         CAST_TO_OTHER_DEVICE_ICON,
                         // This string is "Casting screen"
@@ -215,7 +215,7 @@
     private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.IconOnly(
             icon =
-                OngoingActivityChipModel.ChipIcon.Basic(
+                OngoingActivityChipModel.ChipIcon.SingleColorIcon(
                     Icon.Resource(
                         CAST_TO_OTHER_DEVICE_ICON,
                         // This string is just "Casting"
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/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt
new file mode 100644
index 0000000..cce9a16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.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.statusbar.chips.ron.demo.ui.viewmodel
+
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.graphics.drawable.Drawable
+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.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.commandline.ParseableCommand
+import com.android.systemui.statusbar.commandline.Type
+import com.android.systemui.util.time.SystemClock
+import java.io.PrintWriter
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+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.
+ *
+ * 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"
+ * ```
+ *
+ * To hide the chip:
+ * ```
+ * adb shell cmd statusbar demo-ron --hide
+ * ```
+ *
+ * See [DemoRonCommand] for more information on the adb command spec.
+ */
+@SysUISingleton
+class DemoRonChipViewModel
+@Inject
+constructor(
+    private val commandRegistry: CommandRegistry,
+    private val packageManager: PackageManager,
+    private val systemClock: SystemClock,
+) : OngoingActivityChipViewModel, CoreStartable {
+    override fun start() {
+        commandRegistry.registerCommand("demo-ron") { DemoRonCommand() }
+    }
+
+    private val _chip =
+        MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
+    override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
+
+    private inner class DemoRonCommand : ParseableCommand("demo-ron") {
+        private val packageName: String? by
+            param(
+                longName = "packageName",
+                shortName = "p",
+                description = "The package name for the demo RON app",
+                valueParser = Type.String,
+            )
+
+        private val text: String? by
+            param(
+                longName = "text",
+                shortName = "t",
+                description = "Text to display in the chip",
+                valueParser = Type.String,
+            )
+
+        private val backgroundColor: Int? by
+            param(
+                longName = "color",
+                shortName = "c",
+                description =
+                    "The color to show as the chip background color. " +
+                        "You can either just write a basic color like 'red' or 'green', " +
+                        "or you can include a #RRGGBB string in this format: \"\\\\#434343\".",
+                valueParser = Type.Color,
+            )
+
+        private val hide by
+            flag(
+                longName = "hide",
+                description = "Hides any existing demo RON chip",
+            )
+
+        override fun execute(pw: PrintWriter) {
+            if (!StatusBarRonChips.isEnabled) {
+                pw.println(
+                    "Error: com.android.systemui.status_bar_ron_chips must be enabled " +
+                        "before using this demo feature"
+                )
+                return
+            }
+
+            if (hide) {
+                _chip.value = OngoingActivityChipModel.Hidden()
+                return
+            }
+
+            val currentPackageName = packageName
+            if (currentPackageName == null) {
+                pw.println("--packageName (or -p) must be included")
+                return
+            }
+
+            val appIcon = getAppIcon(currentPackageName)
+            if (appIcon == null) {
+                pw.println("Package $currentPackageName could not be found")
+                return
+            }
+
+            val colors =
+                if (backgroundColor != null) {
+                    ColorsModel.Custom(backgroundColorInt = backgroundColor!!)
+                } else {
+                    ColorsModel.Themed
+                }
+
+            val currentText = text
+            if (currentText != null) {
+                _chip.value =
+                    OngoingActivityChipModel.Shown.Text(
+                        icon = appIcon,
+                        colors = colors,
+                        text = currentText,
+                    )
+            } else {
+                _chip.value =
+                    OngoingActivityChipModel.Shown.Timer(
+                        icon = appIcon,
+                        colors = colors,
+                        startTimeMs = systemClock.elapsedRealtime(),
+                        onClickListener = null,
+                    )
+            }
+        }
+
+        private fun getAppIcon(packageName: String): OngoingActivityChipModel.ChipIcon? {
+            lateinit var iconDrawable: Drawable
+            try {
+                // Note: For the real implementation, we should check if applicationInfo exists
+                // before fetching the icon, so that we either don't show the chip or show a good
+                // backup icon in case the app info can't be found for some reason.
+                iconDrawable = packageManager.getApplicationIcon(packageName)
+            } catch (e: NameNotFoundException) {
+                return null
+            }
+            return OngoingActivityChipModel.ChipIcon.FullColorAppIcon(
+                Icon.Loaded(drawable = iconDrawable, contentDescription = null),
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt
new file mode 100644
index 0000000..4ef1909
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.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.systemui.statusbar.chips.ron.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. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarRonChips {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS
+
+    /** A token used for dependency declaration */
+    val token: FlagToken
+        get() = FlagToken(FLAG_NAME, isEnabled)
+
+    /** Is the refactor enabled */
+    @JvmStatic
+    inline val isEnabled
+        get() = Flags.statusBarRonChips()
+
+    /**
+     * 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 not enabled to ensure that the refactor author catches issues in testing.
+     * Caution!! Using this check incorrectly will cause crashes in nextfood builds!
+     */
+    @JvmStatic
+    inline fun assertInNewMode() = RefactorFlagUtils.assertInNewMode(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/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index 9e6cacb..eb73521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -80,7 +80,7 @@
                     is ScreenRecordChipModel.Recording -> {
                         OngoingActivityChipModel.Shown.Timer(
                             icon =
-                                OngoingActivityChipModel.ChipIcon.Basic(
+                                OngoingActivityChipModel.ChipIcon.SingleColorIcon(
                                     Icon.Resource(
                                         ICON,
                                         ContentDescription.Resource(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 7897f93..d99a916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -110,7 +110,7 @@
     ): OngoingActivityChipModel.Shown {
         return OngoingActivityChipModel.Shown.Timer(
             icon =
-                OngoingActivityChipModel.ChipIcon.Basic(
+                OngoingActivityChipModel.ChipIcon.SingleColorIcon(
                     Icon.Resource(
                         SHARE_TO_APP_ICON,
                         ContentDescription.Resource(R.string.share_to_app_chip_accessibility_label),
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
new file mode 100644
index 0000000..3b1e565
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -0,0 +1,314 @@
+/*
+ * 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.ui.binder
+
+import android.annotation.IdRes
+import android.content.res.ColorStateList
+import android.graphics.drawable.GradientDrawable
+import android.view.View
+import android.view.ViewGroup
+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.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+
+/** Binder for ongoing activity chip views. */
+object OngoingActivityChipBinder {
+    /** Binds the given [chipModel] data to the given [chipView]. */
+    fun bind(chipModel: OngoingActivityChipModel, chipView: View) {
+        val chipContext = chipView.context
+        val chipDefaultIconView: ImageView =
+            chipView.requireViewById(R.id.ongoing_activity_chip_icon)
+        val chipTimeView: ChipChronometer =
+            chipView.requireViewById(R.id.ongoing_activity_chip_time)
+        val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text)
+        val chipBackgroundView: ChipBackgroundContainer =
+            chipView.requireViewById(R.id.ongoing_activity_chip_background)
+
+        when (chipModel) {
+            is OngoingActivityChipModel.Shown -> {
+                // Data
+                setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
+                setChipMainContent(chipModel, chipTextView, chipTimeView)
+                chipView.setOnClickListener(chipModel.onClickListener)
+                updateChipPadding(
+                    chipModel,
+                    chipBackgroundView,
+                    chipTextView,
+                    chipTimeView,
+                )
+
+                // Accessibility
+                setChipAccessibility(chipModel, chipView, chipBackgroundView)
+
+                // Colors
+                val textColor = chipModel.colors.text(chipContext)
+                chipTimeView.setTextColor(textColor)
+                chipTextView.setTextColor(textColor)
+                (chipBackgroundView.background as GradientDrawable).color =
+                    chipModel.colors.background(chipContext)
+            }
+            is OngoingActivityChipModel.Hidden -> {
+                // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+                // [Chronometer.start].
+                chipTimeView.stop()
+            }
+        }
+    }
+
+    private fun setChipIcon(
+        chipModel: OngoingActivityChipModel.Shown,
+        backgroundView: ChipBackgroundContainer,
+        defaultIconView: ImageView,
+    ) {
+        // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
+        // it.
+        backgroundView.removeView(backgroundView.getCustomIconView())
+
+        val iconTint = chipModel.colors.text(defaultIconView.context)
+
+        when (val icon = chipModel.icon) {
+            null -> {
+                defaultIconView.visibility = View.GONE
+            }
+            is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> {
+                IconViewBinder.bind(icon.impl, defaultIconView)
+                defaultIconView.visibility = View.VISIBLE
+                defaultIconView.tintView(iconTint)
+            }
+            is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
+                StatusBarRonChips.assertInNewMode()
+                IconViewBinder.bind(icon.impl, defaultIconView)
+                defaultIconView.visibility = View.VISIBLE
+                defaultIconView.untintView()
+            }
+            is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
+                // Hide the default icon since we'll show this custom icon instead.
+                defaultIconView.visibility = View.GONE
+
+                // Add the new custom icon:
+                // 1. Set up the right visual params.
+                val iconView = icon.impl
+                with(iconView) {
+                    id = CUSTOM_ICON_VIEW_ID
+                    // TODO(b/354930838): Update the content description to not include "phone" and
+                    // maybe include the app name.
+                    contentDescription =
+                        context.resources.getString(R.string.ongoing_phone_call_content_description)
+                    tintView(iconTint)
+                }
+
+                // 2. If we just reinflated the view, we may need to detach the icon view from the
+                // old chip before we reattach it to the new one.
+                // See also: NotificationIconContainerViewBinder#bindIcons.
+                val currentParent = iconView.parent as? ViewGroup
+                if (currentParent != null && currentParent != backgroundView) {
+                    currentParent.removeView(iconView)
+                    currentParent.removeTransientView(iconView)
+                }
+
+                // 3: Add the icon as the starting view.
+                backgroundView.addView(
+                    iconView,
+                    /* index= */ 0,
+                    generateCustomIconLayoutParams(iconView),
+                )
+            }
+        }
+    }
+
+    private fun View.getCustomIconView(): StatusBarIconView? {
+        return this.findViewById(CUSTOM_ICON_VIEW_ID)
+    }
+
+    private fun ImageView.tintView(color: Int) {
+        this.imageTintList = ColorStateList.valueOf(color)
+    }
+
+    private fun ImageView.untintView() {
+        this.imageTintList = null
+    }
+
+    private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
+        val customIconSize =
+            iconView.context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_embedded_padding_icon_size
+            )
+        return FrameLayout.LayoutParams(customIconSize, customIconSize)
+    }
+
+    private fun setChipMainContent(
+        chipModel: OngoingActivityChipModel.Shown,
+        chipTextView: TextView,
+        chipTimeView: ChipChronometer,
+    ) {
+        when (chipModel) {
+            is OngoingActivityChipModel.Shown.Countdown -> {
+                chipTextView.text = chipModel.secondsUntilStarted.toString()
+                chipTextView.visibility = View.VISIBLE
+
+                chipTimeView.hide()
+            }
+            is OngoingActivityChipModel.Shown.Text -> {
+                chipTextView.text = chipModel.text
+                chipTextView.visibility = View.VISIBLE
+
+                chipTimeView.hide()
+            }
+            is OngoingActivityChipModel.Shown.Timer -> {
+                ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+                chipTimeView.visibility = View.VISIBLE
+
+                chipTextView.visibility = View.GONE
+            }
+            is OngoingActivityChipModel.Shown.IconOnly -> {
+                chipTextView.visibility = View.GONE
+                chipTimeView.hide()
+            }
+        }
+    }
+
+    private fun ChipChronometer.hide() {
+        // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
+        // [Chronometer.start].
+        this.stop()
+        this.visibility = View.GONE
+    }
+
+    private fun updateChipPadding(
+        chipModel: OngoingActivityChipModel.Shown,
+        backgroundView: View,
+        chipTextView: TextView,
+        chipTimeView: ChipChronometer,
+    ) {
+        if (chipModel.icon != null) {
+            if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
+                // If the icon is a custom [StatusBarIconView], then it should've come from
+                // `Notification.smallIcon`, which is required to embed its own paddings. We need to
+                // adjust the other paddings to make everything look good :)
+                backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
+                chipTextView.setTextPaddingForEmbeddedPaddingIcon()
+                chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
+            } else {
+                backgroundView.setBackgroundPaddingForNormalIcon()
+                chipTextView.setTextPaddingForNormalIcon()
+                chipTimeView.setTextPaddingForNormalIcon()
+            }
+        } else {
+            backgroundView.setBackgroundPaddingForNoIcon()
+            chipTextView.setTextPaddingForNoIcon()
+            chipTimeView.setTextPaddingForNoIcon()
+        }
+    }
+
+    private fun View.setTextPaddingForEmbeddedPaddingIcon() {
+        val newPaddingEnd =
+            context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
+            )
+        setPaddingRelative(
+            // The icon should embed enough padding between the icon and time view.
+            /* start= */ 0,
+            this.paddingTop,
+            newPaddingEnd,
+            this.paddingBottom,
+        )
+    }
+
+    private fun View.setTextPaddingForNormalIcon() {
+        this.setPaddingRelative(
+            this.context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_icon_text_padding
+            ),
+            paddingTop,
+            // The background view will contain the right end padding.
+            /* end= */ 0,
+            paddingBottom,
+        )
+    }
+
+    private fun View.setTextPaddingForNoIcon() {
+        // The background view will have even start & end paddings, so we don't want the text view
+        // to add any additional padding.
+        this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom)
+    }
+
+    private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
+        val sidePadding =
+            context.resources.getDimensionPixelSize(
+                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
+            )
+        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,
+        )
+    }
+
+    private fun View.setBackgroundPaddingForNoIcon() {
+        // The padding for the normal icon is also appropriate for no icon.
+        setBackgroundPaddingForNormalIcon()
+    }
+
+    private fun setChipAccessibility(
+        chipModel: OngoingActivityChipModel.Shown,
+        chipView: View,
+        chipBackgroundView: View,
+    ) {
+        when (chipModel) {
+            is OngoingActivityChipModel.Shown.Countdown -> {
+                // Set as assertive so talkback will announce the countdown
+                chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+            }
+            is OngoingActivityChipModel.Shown.Timer,
+            is OngoingActivityChipModel.Shown.Text,
+            is OngoingActivityChipModel.Shown.IconOnly -> {
+                chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
+            }
+        }
+        // Clickable chips need to be a minimum size for accessibility purposes, but let
+        // non-clickable chips be smaller.
+        if (chipModel.onClickListener != null) {
+            chipBackgroundView.minimumWidth =
+                chipBackgroundView.context.resources.getDimensionPixelSize(
+                    R.dimen.min_clickable_item_size
+                )
+        } else {
+            chipBackgroundView.minimumWidth = 0
+        }
+    }
+
+    @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index 8a5165d8..4b0fc5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -39,6 +39,24 @@
             Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
     }
 
+    /**
+     * The chip should have the given background color, and text color that matches dark/light
+     * theme.
+     */
+    data class Custom(val backgroundColorInt: Int) : ColorsModel {
+        override fun background(context: Context): ColorStateList =
+            ColorStateList.valueOf(backgroundColorInt)
+
+        // TODO(b/361346412): When dark theme changes, the chip should automatically re-render with
+        // the right text color. Right now, it has the right text color when the chip is first
+        // created but the color doesn't update if dark theme changes.
+        override fun text(context: Context) =
+            Utils.getColorAttrDefaultColor(
+                context,
+                com.android.internal.R.attr.materialColorOnSurface,
+            )
+    }
+
     /** The chip should have a red background with white text. */
     data object Red : ColorsModel {
         override fun background(context: Context): ColorStateList {
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 26a2f91..62622a8 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
@@ -89,6 +89,16 @@
         ) : Shown(icon = null, colors, onClickListener = null) {
             override val logName = "Shown.Countdown"
         }
+
+        /** This chip shows the specified [text] in the chip. */
+        data class Text(
+            override val icon: ChipIcon,
+            override val colors: ColorsModel,
+            // TODO(b/361346412): Enforce a max length requirement?
+            val text: String,
+        ) : Shown(icon, colors, onClickListener = null) {
+            override val logName = "Shown.Text"
+        }
     }
 
     /** Represents an icon to show on the chip. */
@@ -106,7 +116,13 @@
             }
         }
 
-        /** The icon is a basic resource or drawable icon that System UI created internally. */
-        data class Basic(val impl: Icon) : ChipIcon
+        /**
+         * This icon is a single color and it came from basic resource or drawable icon that System
+         * UI created internally.
+         */
+        data class SingleColorIcon(val impl: Icon) : ChipIcon
+
+        /** This icon is an app icon in full color (so it should not get tinted in any way). */
+        data class FullColorAppIcon(val impl: Icon) : ChipIcon
     }
 }
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 b0d897d..199eb06 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
@@ -23,6 +23,8 @@
 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.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
@@ -51,6 +53,7 @@
     shareToAppChipViewModel: ShareToAppChipViewModel,
     castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
     callChipViewModel: CallChipViewModel,
+    demoRonChipViewModel: DemoRonChipViewModel,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
     private enum class ChipType {
@@ -58,6 +61,8 @@
         ShareToApp,
         CastToOtherDevice,
         Call,
+        /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */
+        DemoRon,
     }
 
     /** Model that helps us internally track the various chip states from each of the types. */
@@ -78,6 +83,7 @@
             val shareToApp: OngoingActivityChipModel.Hidden,
             val castToOtherDevice: OngoingActivityChipModel.Hidden,
             val call: OngoingActivityChipModel.Hidden,
+            val demoRon: OngoingActivityChipModel.Hidden,
         ) : InternalChipModel
     }
 
@@ -87,7 +93,8 @@
             shareToAppChipViewModel.chip,
             castToOtherDeviceChipViewModel.chip,
             callChipViewModel.chip,
-        ) { screenRecord, shareToApp, castToOtherDevice, call ->
+            demoRonChipViewModel.chip,
+        ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon ->
             logger.log(
                 TAG,
                 LogLevel.INFO,
@@ -98,7 +105,15 @@
                 },
                 { "Chips: ScreenRecord=$str1 > ShareToApp=$str2 > CastToOther=$str3..." },
             )
-            logger.log(TAG, LogLevel.INFO, { str1 = call.logName }, { "... > Call=$str1" })
+            logger.log(
+                TAG,
+                LogLevel.INFO,
+                {
+                    str1 = call.logName
+                    str2 = demoRon.logName
+                },
+                { "... > Call=$str1 > DemoRon=$str2" }
+            )
             // This `when` statement shows the priority order of the chips.
             when {
                 // Screen recording also activates the media projection APIs, so whenever the
@@ -113,30 +128,36 @@
                     InternalChipModel.Shown(ChipType.CastToOtherDevice, castToOtherDevice)
                 call is OngoingActivityChipModel.Shown ->
                     InternalChipModel.Shown(ChipType.Call, call)
+                demoRon is OngoingActivityChipModel.Shown -> {
+                    StatusBarRonChips.assertInNewMode()
+                    InternalChipModel.Shown(ChipType.DemoRon, demoRon)
+                }
                 else -> {
                     // We should only get here if all chip types are hidden
                     check(screenRecord is OngoingActivityChipModel.Hidden)
                     check(shareToApp is OngoingActivityChipModel.Hidden)
                     check(castToOtherDevice is OngoingActivityChipModel.Hidden)
                     check(call is OngoingActivityChipModel.Hidden)
+                    check(demoRon is OngoingActivityChipModel.Hidden)
                     InternalChipModel.Hidden(
                         screenRecord = screenRecord,
                         shareToApp = shareToApp,
                         castToOtherDevice = castToOtherDevice,
                         call = call,
+                        demoRon = demoRon,
                     )
                 }
             }
         }
 
     /**
-     * A flow modeling the chip that should be shown in the status bar after accounting for possibly
-     * multiple ongoing activities and animation requirements.
+     * A flow modeling the primary chip that should be shown in the status bar after accounting for
+     * possibly multiple ongoing activities and animation requirements.
      *
      * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment] is responsible for
      * actually displaying the chip.
      */
-    val chip: StateFlow<OngoingActivityChipModel> =
+    val primaryChip: StateFlow<OngoingActivityChipModel> =
         internalChip
             .pairwise(initialValue = DEFAULT_INTERNAL_HIDDEN_MODEL)
             .map { (old, new) ->
@@ -154,6 +175,7 @@
                         ChipType.ShareToApp -> new.shareToApp
                         ChipType.CastToOtherDevice -> new.castToOtherDevice
                         ChipType.Call -> new.call
+                        ChipType.DemoRon -> new.demoRon
                     }
                 } else if (new is InternalChipModel.Shown) {
                     // If we have a chip to show, always show it.
@@ -179,6 +201,7 @@
                 shareToApp = OngoingActivityChipModel.Hidden(),
                 castToOtherDevice = OngoingActivityChipModel.Hidden(),
                 call = OngoingActivityChipModel.Hidden(),
+                demoRon = OngoingActivityChipModel.Hidden(),
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
index 01083d9..412c8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.commandline
 
+import androidx.core.graphics.toColorInt
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
@@ -164,10 +165,23 @@
         ?: Result.failure(ArgParseError("Failed to parse $value as a float"))
 }
 
+// See https://developer.android.com/reference/android/graphics/Color#parseColor(java.lang.String)
+// for the supported formats of the color string. tl;dr: #RRGGBB, #AARRGGBB, or a basic color name
+// like "red" or "green". For the RRGGBB values, the `#` needs to be escaped. Use `"\\#RRGGBB"` in
+// the command to escape the `#` correctly.
+private val parseColor: ValueParser<Int> = ValueParser { value ->
+    try {
+        Result.success(value.toColorInt())
+    } catch (e: IllegalArgumentException) {
+        Result.failure(ArgParseError("Failed to parse $value as a color: $e"))
+    }
+}
+
 /** Default parsers that can be use as-is, or [map]ped to another type */
 object Type {
     val Boolean = parseBoolean
     val Int = parseInt
     val Float = parseFloat
     val String = parseString
+    val Color = parseColor
 }
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 ecb6d7f..406a66449f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.phone.LightBarController
+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.ui.SystemBarUtilsProxyImpl
@@ -51,6 +52,11 @@
     @ClassKey(LightBarController::class)
     abstract fun bindLightBarController(impl: LightBarController): CoreStartable
 
+    @Binds
+    @IntoMap
+    @ClassKey(StatusBarSignalPolicy::class)
+    abstract fun bindStatusBarSignalPolicy(impl: StatusBarSignalPolicy): CoreStartable
+
     companion object {
         @Provides
         @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
index 1aeb6b3..c7aa6f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package com.android.systemui.statusbar.gesture
 
 import android.annotation.CallSuper
@@ -55,18 +54,22 @@
      * The callback receive the last motion event in the gesture.
      */
     fun addOnGestureDetectedCallback(tag: String, callback: (MotionEvent) -> Unit) {
-        val callbacksWasEmpty = callbacks.isEmpty()
-        callbacks[tag] = callback
-        if (callbacksWasEmpty) {
-            startGestureListening()
+        synchronized(callbacks) {
+            val callbacksWasEmpty = callbacks.isEmpty()
+            callbacks[tag] = callback
+            if (callbacksWasEmpty) {
+                startGestureListening()
+            }
         }
     }
 
     /** Removes the callback. */
     fun removeOnGestureDetectedCallback(tag: String) {
-        callbacks.remove(tag)
-        if (callbacks.isEmpty()) {
-            stopGestureListening()
+        synchronized(callbacks) {
+            callbacks.remove(tag)
+            if (callbacks.isEmpty()) {
+                stopGestureListening()
+            }
         }
     }
 
@@ -78,7 +81,8 @@
      * event in the gesture.
      */
     internal fun onGestureDetected(e: MotionEvent) {
-        callbacks.values.forEach { it.invoke(e) }
+        val callbackValues = synchronized(callbacks) { ArrayList(callbacks.values) }
+        callbackValues.forEach { it.invoke(e) }
     }
 
     /** Start listening to touch events. */
@@ -86,13 +90,15 @@
     internal open fun startGestureListening() {
         stopGestureListening()
 
-        inputMonitor = InputMonitorCompat(tag, displayId).also {
-            inputReceiver = it.getInputReceiver(
-                Looper.getMainLooper(),
-                Choreographer.getInstance(),
-                this::onInputEvent
-            )
-        }
+        inputMonitor =
+            InputMonitorCompat(tag, displayId).also {
+                inputReceiver =
+                    it.getInputReceiver(
+                        Looper.getMainLooper(),
+                        Choreographer.getInstance(),
+                        this::onInputEvent,
+                    )
+            }
     }
 
     /** Stop listening to touch events. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index ef4dffad..97add30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -26,6 +26,7 @@
 import android.content.Context
 import android.content.Intent
 import android.database.ContentObserver
+import android.graphics.Rect
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
@@ -570,6 +571,20 @@
         plugin?.unregisterListener(listener)
     }
 
+    fun isWithinSmartspaceBounds(x: Int, y: Int): Boolean {
+        smartspaceViews.forEach {
+            val bounds = Rect()
+            with(it as View) {
+                this.getBoundsOnScreen(bounds)
+                if (bounds.contains(x, y)) {
+                    return true
+                }
+            }
+        }
+
+        return false
+    }
+
     private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
         if (isDateWeatherDecoupled && t.featureType == SmartspaceTarget.FEATURE_WEATHER) {
             return false
@@ -587,7 +602,7 @@
                 // Only the primary user can have an associated managed profile, so only show
                 // content for the managed profile if the primary user is active
                 userTracker.userHandle.identifier == UserHandle.USER_SYSTEM &&
-                        (!t.isSensitive || showSensitiveContentForManagedUser)
+                    (!t.isSensitive || showSensitiveContentForManagedUser)
             }
             else -> {
                 false
@@ -705,4 +720,3 @@
         }
     }
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS
new file mode 100644
index 0000000..4c349c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+file:../../keyguard/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 2b7df7d..67c53d46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -142,14 +142,15 @@
     }
 
     override fun onIntentStarted(willAnimate: Boolean) {
+        val reason = "onIntentStarted(willAnimate=$willAnimate)"
         if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) {
-            Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
+            Log.d(TAG, reason)
         }
         notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(willAnimate)
         notificationEntry.isExpandAnimationRunning = willAnimate
 
         if (!willAnimate) {
-            removeHun(animate = true)
+            removeHun(animate = true, reason)
             onFinishAnimationCallback?.run()
         }
     }
@@ -166,13 +167,18 @@
             }
         }
 
-    private fun removeHun(animate: Boolean) {
+    private fun removeHun(animate: Boolean, reason: String) {
         val row = headsUpNotificationRow ?: return
 
         // TODO: b/297247841 - Call on the row we're removing, which may differ from notification.
         HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
 
-        headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
+        headsUpManager.removeNotification(
+            row.entry.key,
+            true /* releaseImmediately */,
+            animate,
+            reason
+        )
     }
 
     override fun onTransitionAnimationCancelled(newKeyguardOccludedState: Boolean?) {
@@ -184,7 +190,7 @@
         // here?
         notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(false)
         notificationEntry.isExpandAnimationRunning = false
-        removeHun(animate = true)
+        removeHun(animate = true, "onLaunchAnimationCancelled()")
         onFinishAnimationCallback?.run()
     }
 
@@ -206,7 +212,7 @@
         notificationEntry.isExpandAnimationRunning = false
         notificationListContainer.setExpandingNotification(null)
         applyParams(null)
-        removeHun(animate = false)
+        removeHun(animate = false, "onLaunchAnimationEnd()")
         onFinishAnimationCallback?.run()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index e50d64b..ec8566b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -496,7 +496,11 @@
                 if (posted?.shouldHeadsUpEver == false) {
                     if (posted.isHeadsUpEntry) {
                         // We don't want this to be interrupting anymore, let's remove it
-                        mHeadsUpManager.removeNotification(posted.key, false /*removeImmediately*/)
+                        mHeadsUpManager.removeNotification(
+                            posted.key,
+                            /* removeImmediately= */ false,
+                            "onEntryUpdated"
+                        )
                     } else if (posted.isBinding) {
                         // Don't let the bind finish
                         cancelHeadsUpBind(posted.entry)
@@ -520,7 +524,11 @@
                     val removeImmediatelyForRemoteInput =
                         (mRemoteInputManager.isSpinning(entryKey) &&
                             !NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY)
-                    mHeadsUpManager.removeNotification(entry.key, removeImmediatelyForRemoteInput)
+                    mHeadsUpManager.removeNotification(
+                        entry.key,
+                        removeImmediatelyForRemoteInput,
+                        "onEntryRemoved, reason: $reason"
+                    )
                 }
             }
 
@@ -721,7 +729,9 @@
                             {
                                 mHeadsUpManager.removeNotification(
                                     entry.key, /* releaseImmediately */
-                                    true
+                                    true,
+                                    "cancel lifetime extension - extended for reason: " +
+                                        "$reason, isSticky: true"
                                 )
                             },
                             removeAfterMillis
@@ -730,7 +740,9 @@
                     mExecutor.execute {
                         mHeadsUpManager.removeNotification(
                             entry.key, /* releaseImmediately */
-                            false
+                            false,
+                            "lifetime extension - extended for reason: $reason" +
+                                ", isSticky: false"
                         )
                     }
                     mNotifsExtendingLifetime[entry] = null
@@ -902,7 +914,7 @@
 
     fun commitModifications() {
         deferred.forEach { (key, releaseImmediately) ->
-            headsUpManager.removeNotification(key, releaseImmediately)
+            headsUpManager.removeNotification(key, releaseImmediately, "commitModifications")
         }
         deferred.clear()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index f74c9a6..e9292f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -79,6 +79,7 @@
             // NOTE: NotificationEntry.isClearable will internally check group children to ensure
             //  the group itself definitively clearable.
             val isClearable = !isSensitiveContentProtectionActive && entry.isClearable
+                    && !entry.isSensitive.value
             when {
                 isSilent && isClearable -> hasClearableSilentNotifs = true
                 isSilent && !isClearable -> hasNonClearableSilentNotifs = true
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 3a2f95e..6d0148a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -22,7 +22,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.systemui.Dumpable;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
@@ -32,6 +32,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
@@ -43,6 +44,7 @@
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.kotlin.BooleanFlowOperators;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
 import java.io.PrintWriter;
@@ -70,7 +72,8 @@
     private final VisibilityLocationProvider mVisibilityLocationProvider;
     private final VisualStabilityProvider mVisualStabilityProvider;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
-    private final CommunalInteractor mCommunalInteractor;
+    private final CommunalSceneInteractor mCommunalSceneInteractor;
+    private final ShadeInteractor mShadeInteractor;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
     private final VisualStabilityCoordinatorLogger mLogger;
 
@@ -110,7 +113,8 @@
             VisibilityLocationProvider visibilityLocationProvider,
             VisualStabilityProvider visualStabilityProvider,
             WakefulnessLifecycle wakefulnessLifecycle,
-            CommunalInteractor communalInteractor,
+            CommunalSceneInteractor communalSceneInteractor,
+            ShadeInteractor shadeInteractor,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
             VisualStabilityCoordinatorLogger logger) {
         mHeadsUpManager = headsUpManager;
@@ -122,7 +126,8 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
         mDelayableExecutor = delayableExecutor;
-        mCommunalInteractor = communalInteractor;
+        mCommunalSceneInteractor = communalSceneInteractor;
+        mShadeInteractor = shadeInteractor;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
         mLogger = logger;
 
@@ -141,7 +146,11 @@
                 this::onShadeOrQsClosingChanged);
         mJavaAdapter.alwaysCollectFlow(mShadeAnimationInteractor.isLaunchingActivity(),
                 this::onLaunchingActivityChanged);
-        mJavaAdapter.alwaysCollectFlow(mCommunalInteractor.isIdleOnCommunal(),
+        mJavaAdapter.alwaysCollectFlow(
+                BooleanFlowOperators.INSTANCE.allOf(
+                        mCommunalSceneInteractor.isIdleOnCommunal(),
+                        BooleanFlowOperators.INSTANCE.not(mShadeInteractor.isAnyFullyExpanded())
+                ),
                 this::onCommunalShowingChanged);
 
         if (SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
index fc7d682..0d209d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CommonNotifCollection.java
@@ -27,9 +27,6 @@
 /**
  * A notification collection that manages the list of {@link NotificationEntry}s that will be
  * rendered.
- *
- * TODO: (b/145659174) Once we fully migrate to {@link NotifPipeline}, we probably won't need this,
- * but having it for now makes it easy to switch between the two.
  */
 public interface CommonNotifCollection {
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
index 069ae93..28e3995 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpRepository.kt
@@ -44,4 +44,10 @@
 
     /** Snooze the currently pinned HUN. */
     fun snooze()
+
+    /** Unpin all currently pinned HUNs. */
+    fun unpinAll(userUnPinned: Boolean)
+
+    /** Release entries that were waiting for a shade expansion to complete. */
+    fun releaseAfterExpansion()
 }
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 1027bc9..9b382e6 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
@@ -89,6 +89,7 @@
                     .filter { it.callType == CallType.Ongoing }
                     .minByOrNull { it.whenTime }
             }
+            .distinctUntilChanged()
             .flowOn(backgroundDispatcher)
 
     /** Are any notifications being actively presented in the notification stack? */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
index 24b75d4..aa203d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationInteractor.kt
@@ -21,11 +21,11 @@
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRepository
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
@@ -58,7 +58,7 @@
 
     /** Set of currently pinned top-level heads up rows to be displayed. */
     val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
             headsUpRepository.activeHeadsUpRows.flatMapLatest { repositories ->
@@ -80,7 +80,7 @@
 
     /** Are there any pinned heads up rows to display? */
     val hasPinnedRows: Flow<Boolean> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
             headsUpRepository.activeHeadsUpRows.flatMapLatest { rows ->
@@ -95,7 +95,7 @@
     }
 
     val isHeadsUpOrAnimatingAway: Flow<Boolean> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
             combine(hasPinnedRows, headsUpRepository.isHeadsUpAnimatingAway) {
@@ -123,7 +123,7 @@
         }
 
     val showHeadsUpStatusBar: Flow<Boolean> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
             combine(hasPinnedRows, canShowHeadsUp) { hasPinnedRows, canShowHeadsUp ->
@@ -148,6 +148,16 @@
     fun snooze() {
         headsUpRepository.snooze()
     }
+
+    /** Unpin all currently pinned HUNs. */
+    fun unpinAll(userUnPinned: Boolean) {
+        headsUpRepository.unpinAll(userUnPinned)
+    }
+
+    /** Notifies that the current scene transition is idle. */
+    fun onTransitionIdle() {
+        headsUpRepository.releaseAfterExpansion()
+    }
 }
 
 class HeadsUpRowInteractor(repository: HeadsUpRowRepository)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 17f401a..0efd5f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -45,7 +45,6 @@
 import android.service.notification.Flags
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -279,7 +278,8 @@
     private val packageManager: PackageManager,
     private val uiEventLogger: UiEventLogger,
     private val context: Context,
-    private val notificationManager: NotificationManager
+    private val notificationManager: NotificationManager,
+    private val logger: VisualInterruptionDecisionLogger
 ) :
     VisualInterruptionFilter(
         types = setOf(PEEK, PULSE),
@@ -354,15 +354,18 @@
 
     override fun shouldSuppress(entry: NotificationEntry): Boolean {
         if (!isCooldownEnabled()) {
+            logger.logAvalancheAllow("cooldown OFF")
             return false
         }
         val timeSinceAvalancheMs = systemClock.currentTimeMillis() - avalancheProvider.startTime
         val timedOut = timeSinceAvalancheMs >= avalancheProvider.timeoutMs
         if (timedOut) {
+            logger.logAvalancheAllow("timedOut! timeSinceAvalancheMs=$timeSinceAvalancheMs")
             return false
         }
         val state = calculateState(entry)
         if (state != State.SUPPRESS) {
+            logger.logAvalancheAllow("state=$state")
             return false
         }
         if (shouldShowEdu()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
index c204ea9..b83259d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionLogger.kt
@@ -93,6 +93,15 @@
             }
         )
     }
+
+    fun logAvalancheAllow(info: String) {
+        buffer.log(
+            TAG,
+            INFO,
+            { str1 = info },
+            { "AvalancheSuppressor: $str1" }
+        )
+    }
 }
 
 private const val TAG = "VisualInterruptionDecisionProvider"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 8e8d9b6..2f8711a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -194,7 +194,8 @@
                     packageManager,
                     uiEventLogger,
                     context,
-                    notificationManager
+                    notificationManager,
+                    logger
                 )
             )
             avalancheProvider.register()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
index ce4356a..18d4a04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/Notifications.proto
@@ -18,7 +18,7 @@
 
 /**
  * NotificationList proto from atoms.proto, duplicated here so that it's accessible in the build.
- * Must be kept in sync with the version in atoms.proto.
+ * Must be kept in sync with the version in stats/atoms/sysui/sysui_atoms.proto.
  */
 
 message Notification {
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 9d13a17..cb3e26b 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
@@ -1804,6 +1804,20 @@
                 NotificationEntry childEntry,
                 NotificationEntry containerEntry
         );
+
+        /**
+         * Called when resetting the alpha value for content views
+         */
+        void logResetAllContentAlphas(
+                NotificationEntry entry
+        );
+
+        /**
+         * Called when resetting the alpha value for content views is skipped
+         */
+        void logSkipResetAllContentAlphas(
+                NotificationEntry entry
+        );
     }
 
     /**
@@ -3001,6 +3015,8 @@
                     mChildrenContainer.animate().cancel();
                 }
                 resetAllContentAlphas();
+            } else {
+                mLogger.logSkipResetAllContentAlphas(getEntry());
             }
             mPublicLayout.setVisibility(mShowingPublic ? View.VISIBLE : View.INVISIBLE);
             updateChildrenVisibility();
@@ -3186,6 +3202,7 @@
 
     @Override
     protected void resetAllContentAlphas() {
+        mLogger.logResetAllContentAlphas(getEntry());
         mPrivateLayout.setAlpha(1f);
         mPrivateLayout.setLayerType(LAYER_TYPE_NONE, null);
         mPublicLayout.setAlpha(1f);
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 4c76e328..c31a2cb 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
@@ -195,6 +195,20 @@
                 ) {
                     mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry);
                 }
+
+                @Override
+                public void logResetAllContentAlphas(
+                        NotificationEntry entry
+                ) {
+                    mLogBufferLogger.logResetAllContentAlphas(entry);
+                }
+
+                @Override
+                public void logSkipResetAllContentAlphas(
+                        NotificationEntry entry
+                ) {
+                    mLogBufferLogger.logSkipResetAllContentAlphas(entry);
+                }
             };
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 7119145..48c974a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -117,6 +117,8 @@
     protected HybridNotificationView mSingleLineView;
 
     @Nullable public DisposableHandle mContractedBinderHandle;
+    @Nullable public DisposableHandle mExpandedBinderHandle;
+    @Nullable public DisposableHandle mHeadsUpBinderHandle;
 
     private RemoteInputView mExpandedRemoteInput;
     private RemoteInputView mHeadsUpRemoteInput;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index a5cd2a2..c342bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -46,6 +46,9 @@
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.InflationException
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
 import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
@@ -286,11 +289,15 @@
                 }
             FLAG_CONTENT_VIEW_EXPANDED ->
                 row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_EXPANDED) {
+                    row.privateLayout.mExpandedBinderHandle?.dispose()
+                    row.privateLayout.mExpandedBinderHandle = null
                     row.privateLayout.setExpandedChild(null)
                     remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
                 }
             FLAG_CONTENT_VIEW_HEADS_UP ->
                 row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_HEADSUP) {
+                    row.privateLayout.mHeadsUpBinderHandle?.dispose()
+                    row.privateLayout.mHeadsUpBinderHandle = null
                     row.privateLayout.setHeadsUpChild(null)
                     remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
                     row.privateLayout.setHeadsUpInflatedSmartReplies(null)
@@ -499,17 +506,87 @@
                     }
             }
 
-            if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) {
+            val richOngoingContentModel = inflationProgress.contentModel.richOngoingContentModel
+
+            if (
+                richOngoingContentModel != null &&
+                    reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0
+            ) {
                 logger.logAsyncTaskProgress(entry, "inflating RON view")
-                inflationProgress.richOngoingNotificationViewHolder =
-                    inflationProgress.contentModel.richOngoingContentModel?.let {
+                val inflateContractedView = reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0
+                val inflateExpandedView = reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0
+                val inflateHeadsUpView = reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0
+
+                inflationProgress.contractedRichOngoingNotificationViewHolder =
+                    if (inflateContractedView) {
                         ronInflater.inflateView(
-                            contentModel = it,
+                            contentModel = richOngoingContentModel,
                             existingView = row.privateLayout.contractedChild,
                             entry = entry,
                             systemUiContext = context,
-                            parentView = row.privateLayout
+                            parentView = row.privateLayout,
+                            viewType = RichOngoingNotificationViewType.Contracted
                         )
+                    } else {
+                        if (
+                            ronInflater.canKeepView(
+                                contentModel = richOngoingContentModel,
+                                existingView = row.privateLayout.contractedChild,
+                                viewType = RichOngoingNotificationViewType.Contracted
+                            )
+                        ) {
+                            KeepExistingView
+                        } else {
+                            NullContentView
+                        }
+                    }
+
+                inflationProgress.expandedRichOngoingNotificationViewHolder =
+                    if (inflateExpandedView) {
+                        ronInflater.inflateView(
+                            contentModel = richOngoingContentModel,
+                            existingView = row.privateLayout.expandedChild,
+                            entry = entry,
+                            systemUiContext = context,
+                            parentView = row.privateLayout,
+                            viewType = RichOngoingNotificationViewType.Expanded
+                        )
+                    } else {
+                        if (
+                            ronInflater.canKeepView(
+                                contentModel = richOngoingContentModel,
+                                existingView = row.privateLayout.expandedChild,
+                                viewType = RichOngoingNotificationViewType.Expanded
+                            )
+                        ) {
+                            KeepExistingView
+                        } else {
+                            NullContentView
+                        }
+                    }
+
+                inflationProgress.headsUpRichOngoingNotificationViewHolder =
+                    if (inflateHeadsUpView) {
+                        ronInflater.inflateView(
+                            contentModel = richOngoingContentModel,
+                            existingView = row.privateLayout.headsUpChild,
+                            entry = entry,
+                            systemUiContext = context,
+                            parentView = row.privateLayout,
+                            viewType = RichOngoingNotificationViewType.HeadsUp
+                        )
+                    } else {
+                        if (
+                            ronInflater.canKeepView(
+                                contentModel = richOngoingContentModel,
+                                existingView = row.privateLayout.headsUpChild,
+                                viewType = RichOngoingNotificationViewType.HeadsUp
+                            )
+                        ) {
+                            KeepExistingView
+                        } else {
+                            NullContentView
+                        }
                     }
             }
 
@@ -618,7 +695,9 @@
         var inflatedSmartReplyState: InflatedSmartReplyState? = null
         var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
         var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null
-        var richOngoingNotificationViewHolder: InflatedContentViewHolder? = null
+        var contractedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
+        var expandedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
+        var headsUpRichOngoingNotificationViewHolder: ContentViewInflationResult? = null
 
         // Inflated SingleLineView that lacks the UI State
         var inflatedSingleLineView: HybridNotificationView? = null
@@ -1428,14 +1507,21 @@
             logger.logAsyncTaskProgress(entry, "finishing")
 
             // before updating the content model, stop existing binding if necessary
-            val hasRichOngoingContentModel = result.contentModel.richOngoingContentModel != null
-            val requestedRichOngoing = reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0
-            val rejectedRichOngoing = requestedRichOngoing && !hasRichOngoingContentModel
-            if (result.richOngoingNotificationViewHolder != null || rejectedRichOngoing) {
+            if (result.contractedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
                 row.privateLayout.mContractedBinderHandle?.dispose()
                 row.privateLayout.mContractedBinderHandle = null
             }
 
+            if (result.expandedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
+                row.privateLayout.mExpandedBinderHandle?.dispose()
+                row.privateLayout.mExpandedBinderHandle = null
+            }
+
+            if (result.headsUpRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) {
+                row.privateLayout.mHeadsUpBinderHandle?.dispose()
+                row.privateLayout.mHeadsUpBinderHandle = null
+            }
+
             // set the content model after disposal and before setting new rich ongoing view
             entry.setContentModel(result.contentModel)
             result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
@@ -1477,19 +1563,53 @@
                 }
             }
 
-            // after updating the content model, set the view, then start the new binder
-            result.richOngoingNotificationViewHolder?.let { viewHolder ->
-                row.privateLayout.contractedChild = viewHolder.view
-                row.privateLayout.expandedChild = null
-                row.privateLayout.headsUpChild = null
-                row.privateLayout.setExpandedInflatedSmartReplies(null)
-                row.privateLayout.setHeadsUpInflatedSmartReplies(null)
-                row.privateLayout.mContractedBinderHandle =
-                    viewHolder.binder.setupContentViewBinder()
-                row.setExpandable(false)
+            val hasRichOngoingViewHolder =
+                result.contractedRichOngoingNotificationViewHolder != null ||
+                    result.expandedRichOngoingNotificationViewHolder != null ||
+                    result.headsUpRichOngoingNotificationViewHolder != null
+
+            if (hasRichOngoingViewHolder) {
+                // after updating the content model, set the view, then start the new binder
+                result.contractedRichOngoingNotificationViewHolder?.let { contractedViewHolder ->
+                    if (contractedViewHolder is InflatedContentViewHolder) {
+                        row.privateLayout.contractedChild = contractedViewHolder.view
+                        row.privateLayout.mContractedBinderHandle =
+                            contractedViewHolder.binder.setupContentViewBinder()
+                    } else if (contractedViewHolder == NullContentView) {
+                        row.privateLayout.contractedChild = null
+                    }
+                }
+
+                result.expandedRichOngoingNotificationViewHolder?.let { expandedViewHolder ->
+                    if (expandedViewHolder is InflatedContentViewHolder) {
+                        row.privateLayout.expandedChild = expandedViewHolder.view
+                        row.privateLayout.mExpandedBinderHandle =
+                            expandedViewHolder.binder.setupContentViewBinder()
+                    } else if (expandedViewHolder == NullContentView) {
+                        row.privateLayout.expandedChild = null
+                    }
+                }
+
+                result.headsUpRichOngoingNotificationViewHolder?.let { headsUpViewHolder ->
+                    if (headsUpViewHolder is InflatedContentViewHolder) {
+                        row.privateLayout.headsUpChild = headsUpViewHolder.view
+                        row.privateLayout.mHeadsUpBinderHandle =
+                            headsUpViewHolder.binder.setupContentViewBinder()
+                    } else if (headsUpViewHolder == NullContentView) {
+                        row.privateLayout.headsUpChild = null
+                    }
+                }
+
+                // clean remoteViewCache when we don't keep existing views.
                 remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED)
                 remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED)
                 remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP)
+
+                // Since RONs don't support smart reply, remove them from HUNs and Expanded.
+                row.privateLayout.setExpandedInflatedSmartReplies(null)
+                row.privateLayout.setHeadsUpInflatedSmartReplies(null)
+
+                row.setExpandable(row.privateLayout.expandedChild != null)
             }
 
             Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row))
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 4f5a04f..b1e9032 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
@@ -128,6 +128,24 @@
             { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
         )
     }
+
+    fun logResetAllContentAlphas(entry: NotificationEntry) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = entry.logKey },
+            { "resetAllContentAlphas: $str1" }
+        )
+    }
+
+    fun logSkipResetAllContentAlphas(entry: NotificationEntry) {
+        notificationRenderBuffer.log(
+            TAG,
+            LogLevel.INFO,
+            { str1 = entry.logKey },
+            { "Skip resetAllContentAlphas: $str1" }
+        )
+    }
 }
 
 private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
index fe86375..da29b0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationContentExtractor.kt
@@ -17,12 +17,12 @@
 package com.android.systemui.statusbar.notification.row
 
 import android.app.Notification
-import android.app.Notification.RichOngoingStyle
 import android.app.PendingIntent
 import android.content.Context
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
 import com.android.systemui.statusbar.notification.row.shared.IconModel
 import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
 import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
@@ -70,12 +70,11 @@
         systemUIContext: Context,
         packageContext: Context
     ): RichOngoingContentModel? {
-        if (builder.style !is RichOngoingStyle) return null
+        val sbn = entry.sbn
+        val notification = sbn.notification
+        val icon = IconModel(notification.smallIcon)
 
         try {
-            val sbn = entry.sbn
-            val notification = sbn.notification
-            val icon = IconModel(notification.smallIcon)
             return if (sbn.packageName == "com.google.android.deskclock") {
                 when (notification.channelId) {
                     "Timers v2" -> {
@@ -90,6 +89,8 @@
                         null
                     }
                 }
+            } else if (builder.style is Notification.EnRouteStyle) {
+                parseEnRouteNotification(notification, icon)
             } else null
         } catch (e: Exception) {
             Log.e("RONs", "Error parsing RON", e)
@@ -203,4 +204,15 @@
             .plusMinutes(minute.toLong())
             .plusSeconds(second.toLong())
     }
+
+    private fun parseEnRouteNotification(
+        notification: Notification,
+        icon: IconModel,
+    ): EnRouteContentModel {
+        return EnRouteContentModel(
+            smallIcon = icon,
+            title = notification.extras.getCharSequence(Notification.EXTRA_TITLE),
+            text = notification.extras.getCharSequence(Notification.EXTRA_TEXT),
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
index e9c4960..2c462b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
@@ -24,12 +24,18 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
 import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel
 import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
-import com.android.systemui.statusbar.notification.row.shared.StopwatchContentModel
 import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
+import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
 import com.android.systemui.statusbar.notification.row.ui.view.TimerView
+import com.android.systemui.statusbar.notification.row.ui.viewbinder.EnRouteViewBinder
 import com.android.systemui.statusbar.notification.row.ui.viewbinder.TimerViewBinder
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.RichOngoingViewModelComponent
 import com.android.systemui.statusbar.notification.row.ui.viewmodel.TimerViewModel
 import javax.inject.Inject
@@ -39,7 +45,35 @@
     fun setupContentViewBinder(): DisposableHandle
 }
 
-class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder)
+enum class RichOngoingNotificationViewType {
+    Contracted,
+    Expanded,
+    HeadsUp,
+}
+
+/**
+ * * Supertype of the 3 different possible result types of
+ *   [RichOngoingNotificationViewInflater.inflateView].
+ */
+sealed interface ContentViewInflationResult {
+
+    /** Indicates that the content view should be removed if present. */
+    data object NullContentView : ContentViewInflationResult
+
+    /**
+     * Indicates that the content view (which *must be* present) should be unmodified during this
+     * inflation.
+     */
+    data object KeepExistingView : ContentViewInflationResult
+
+    /**
+     * Contains the new view and binder that should replace any existing content view for this slot.
+     */
+    data class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) :
+        ContentViewInflationResult
+}
+
+fun ContentViewInflationResult?.shouldDisposeViewBinder() = this !is KeepExistingView
 
 /**
  * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is
@@ -52,7 +86,14 @@
         entry: NotificationEntry,
         systemUiContext: Context,
         parentView: ViewGroup,
-    ): InflatedContentViewHolder?
+        viewType: RichOngoingNotificationViewType,
+    ): ContentViewInflationResult
+
+    fun canKeepView(
+        contentModel: RichOngoingContentModel,
+        existingView: View?,
+        viewType: RichOngoingNotificationViewType
+    ): Boolean
 }
 
 @SysUISingleton
@@ -68,8 +109,9 @@
         entry: NotificationEntry,
         systemUiContext: Context,
         parentView: ViewGroup,
-    ): InflatedContentViewHolder? {
-        if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return null
+        viewType: RichOngoingNotificationViewType,
+    ): ContentViewInflationResult {
+        if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return NullContentView
         val component = viewModelComponentFactory.create(entry)
         return when (contentModel) {
             is TimerContentModel ->
@@ -77,9 +119,31 @@
                     existingView,
                     component::createTimerViewModel,
                     systemUiContext,
-                    parentView
+                    parentView,
+                    viewType
                 )
-            is StopwatchContentModel -> TODO("Not yet implemented")
+            is EnRouteContentModel ->
+                inflateEnRouteView(
+                    existingView,
+                    component::createEnRouteViewModel,
+                    systemUiContext,
+                    parentView,
+                    viewType
+                )
+            else -> TODO("Not yet implemented")
+        }
+    }
+
+    override fun canKeepView(
+        contentModel: RichOngoingContentModel,
+        existingView: View?,
+        viewType: RichOngoingNotificationViewType
+    ): Boolean {
+        if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return false
+        return when (contentModel) {
+            is TimerContentModel -> canKeepTimerView(contentModel, existingView, viewType)
+            is EnRouteContentModel -> canKeepEnRouteView(contentModel, existingView, viewType)
+            else -> TODO("Not yet implemented")
         }
     }
 
@@ -88,17 +152,65 @@
         createViewModel: () -> TimerViewModel,
         systemUiContext: Context,
         parentView: ViewGroup,
-    ): InflatedContentViewHolder? {
-        if (existingView is TimerView && !existingView.isReinflateNeeded()) return null
-        val newView =
-            LayoutInflater.from(systemUiContext)
-                .inflate(
-                    R.layout.rich_ongoing_timer_notification,
-                    parentView,
-                    /* attachToRoot= */ false
-                ) as TimerView
-        return InflatedContentViewHolder(newView) {
-            TimerViewBinder.bindWhileAttached(newView, createViewModel())
+        viewType: RichOngoingNotificationViewType,
+    ): ContentViewInflationResult {
+        if (existingView is TimerView && !existingView.isReinflateNeeded()) return KeepExistingView
+
+        return when (viewType) {
+            RichOngoingNotificationViewType.Contracted -> {
+                val newView =
+                    LayoutInflater.from(systemUiContext)
+                        .inflate(
+                            R.layout.rich_ongoing_timer_notification,
+                            parentView,
+                            /* attachToRoot= */ false
+                        ) as TimerView
+                InflatedContentViewHolder(newView) {
+                    TimerViewBinder.bindWhileAttached(newView, createViewModel())
+                }
+            }
+            RichOngoingNotificationViewType.Expanded,
+            RichOngoingNotificationViewType.HeadsUp -> NullContentView
         }
     }
+
+    private fun canKeepTimerView(
+        contentModel: TimerContentModel,
+        existingView: View?,
+        viewType: RichOngoingNotificationViewType
+    ): Boolean = true
+
+    private fun inflateEnRouteView(
+        existingView: View?,
+        createViewModel: () -> EnRouteViewModel,
+        systemUiContext: Context,
+        parentView: ViewGroup,
+        viewType: RichOngoingNotificationViewType,
+    ): ContentViewInflationResult {
+        if (existingView is EnRouteView && !existingView.isReinflateNeeded())
+            return KeepExistingView
+        return when (viewType) {
+            RichOngoingNotificationViewType.Contracted -> {
+                val newView =
+                    LayoutInflater.from(systemUiContext)
+                        .inflate(
+                            R.layout.notification_template_en_route_contracted,
+                            parentView,
+                            /* attachToRoot= */ false
+                        ) as EnRouteView
+
+                InflatedContentViewHolder(newView) {
+                    EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
+                }
+            }
+            RichOngoingNotificationViewType.Expanded,
+            RichOngoingNotificationViewType.HeadsUp -> NullContentView
+        }
+    }
+
+    private fun canKeepEnRouteView(
+        contentModel: EnRouteContentModel,
+        existingView: View?,
+        viewType: RichOngoingNotificationViewType
+    ): Boolean = true
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
index 4705ace..72823a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/domain/interactor/NotificationRowInteractor.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.notification.row.domain.interactor
 
 import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import com.android.systemui.statusbar.notification.row.shared.EnRouteContentModel
 import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -26,4 +27,8 @@
     /** Content of a rich ongoing timer notification. */
     val timerContentModel: Flow<TimerContentModel> =
         repository.richOngoingContentModel.filterIsInstance<TimerContentModel>()
+
+    /** Content of a rich ongoing timer notification. */
+    val enRouteContentModel: Flow<EnRouteContentModel> =
+        repository.richOngoingContentModel.filterIsInstance<EnRouteContentModel>()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.kt
new file mode 100644
index 0000000..7e78cca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/EnRouteContentModel.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.statusbar.notification.row.shared
+
+/**
+ * Represents something en route.
+ *
+ * @param smallIcon the main small icon of the EnRoute notification.
+ * @param title the title of the EnRoute notification.
+ * @param text the text of the EnRoute notification.
+ */
+data class EnRouteContentModel(
+    val smallIcon: IconModel,
+    val title: CharSequence?,
+    val text: CharSequence?,
+) : RichOngoingContentModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.kt
new file mode 100644
index 0000000..e5c2b5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/view/EnRouteView.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.statusbar.notification.row.ui.view
+
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import com.android.internal.R
+import com.android.internal.widget.NotificationExpandButton
+
+class EnRouteView
+@JvmOverloads
+constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0,
+) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+    private val configTracker = ConfigurationTracker(resources)
+
+    private lateinit var icon: ImageView
+    private lateinit var title: TextView
+    private lateinit var text: TextView
+    private lateinit var expandButton: NotificationExpandButton
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        icon = requireViewById(R.id.icon)
+        title = requireViewById(R.id.title)
+        text = requireViewById(R.id.text)
+
+        expandButton = requireViewById(R.id.expand_button)
+        expandButton.setExpanded(false)
+    }
+
+    /** the resources configuration has changed such that the view needs to be reinflated */
+    fun isReinflateNeeded(): Boolean = configTracker.hasUnhandledConfigChange()
+
+    fun setIcon(icon: Icon?) {
+        this.icon.setImageIcon(icon)
+    }
+
+    fun setTitle(title: CharSequence?) {
+        this.title.text = title
+    }
+
+    fun setText(text: CharSequence?) {
+        this.text.text = text
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
new file mode 100644
index 0000000..3b8957c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/EnRouteViewBinder.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.ui.viewbinder
+
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.EnRouteViewModel
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.launch
+
+/** Binds a [EnRouteView] to its [view model][EnRouteViewModel]. */
+object EnRouteViewBinder {
+    fun bindWhileAttached(
+        view: EnRouteView,
+        viewModel: EnRouteViewModel,
+    ): DisposableHandle {
+        return view.repeatWhenAttached { lifecycleScope.launch { bind(view, viewModel) } }
+    }
+
+    suspend fun bind(
+        view: EnRouteView,
+        viewModel: EnRouteViewModel,
+    ) = coroutineScope {
+        launch { viewModel.icon.collect { view.setIcon(it) } }
+        launch { viewModel.title.collect { view.setTitle(it) } }
+        launch { viewModel.text.collect { view.setText(it) } }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
new file mode 100644
index 0000000..307a983
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row.ui.viewmodel
+
+import android.graphics.drawable.Icon
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.row.domain.interactor.NotificationRowInteractor
+import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
+import com.android.systemui.util.kotlin.FlowDumperImpl
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+
+/** A view model for EnRoute notifications. */
+class EnRouteViewModel
+@Inject
+constructor(
+    dumpManager: DumpManager,
+    rowInteractor: NotificationRowInteractor,
+) : FlowDumperImpl(dumpManager) {
+    init {
+        /* check if */ RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()
+    }
+
+    val icon: Flow<Icon?> = rowInteractor.enRouteContentModel.mapNotNull { it.smallIcon.icon }
+
+    val title: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.title }
+
+    val text: Flow<CharSequence?> = rowInteractor.enRouteContentModel.map { it.text }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
index dad52a3..5552d89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/RichOngoingViewModelComponent.kt
@@ -33,4 +33,6 @@
     }
 
     fun createTimerViewModel(): TimerViewModel
+
+    fun createEnRouteViewModel(): EnRouteViewModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
deleted file mode 100644
index 62641fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsHeadsUpRefactor.kt
+++ /dev/null
@@ -1,53 +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.statusbar.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 notifications heads up refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object NotificationsHeadsUpRefactor {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_HEADS_UP_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.notificationsHeadsUpRefactor()
-
-    /**
-     * 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/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 7c3072d..1431b28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -94,7 +94,6 @@
     private boolean mIsSmallScreen;
     private boolean mPulsing;
     private float mHideAmount;
-    private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
 
     /**
@@ -139,6 +138,9 @@
     /** Fraction of shade expansion. */
     private float mExpansionFraction;
 
+    /** Fraction of QS expansion. 0 when in shade, 1 when in QS. */
+    private float mQsExpansionFraction;
+
     /** Height of the notifications panel when expansion completes. */
     private float mStackEndHeight;
 
@@ -171,7 +173,8 @@
     }
 
     /**
-     * @return Height of the notifications panel without top padding when expansion completes.
+     * @return Height of the available space for the notification content, when the shade
+     * expansion completes.
      */
     public float getStackEndHeight() {
         return mStackEndHeight;
@@ -208,6 +211,14 @@
     }
 
     /**
+     * @param expansionFraction Fraction of QS expansion.
+     */
+    public void setQsExpansionFraction(float expansionFraction) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mQsExpansionFraction = expansionFraction;
+    }
+
+    /**
      * @param isSwipingUp Whether we are swiping up.
      */
     public void setSwipingUp(boolean isSwipingUp) {
@@ -258,19 +269,28 @@
     }
 
     /**
-     * @see #getStackHeight()
+     * @return Fraction of QS expansion.
      */
-    public void setStackHeight(float stackHeight) {
-        mStackHeight = stackHeight;
+    public float getQsExpansionFraction() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
+        return mQsExpansionFraction;
     }
 
     /**
-     * @return Height of notifications panel interpolated by the expansion fraction.
+     * @return Height of the notification content returned by {@link #getStackEndHeight()}, but
+     * interpolated by the shade expansion fraction.
      */
-    public float getStackHeight() {
+    public float getInterpolatedStackHeight() {
         return mStackHeight;
     }
 
+    /**
+     * @see #getInterpolatedStackHeight()
+     */
+    public void setInterpolatedStackHeight(float stackHeight) {
+        mStackHeight = stackHeight;
+    }
+
     @Inject
     public AmbientState(
             @NonNull Context context,
@@ -492,10 +512,12 @@
     }
 
     public int getTopPadding() {
+        SceneContainerFlag.assertInLegacyMode();
         return mTopPadding;
     }
 
     public void setTopPadding(int topPadding) {
+        SceneContainerFlag.assertInLegacyMode();
         mTopPadding = topPadding;
     }
 
@@ -511,8 +533,15 @@
         if (mDozeAmount == 1.0f && !isPulseExpanding()) {
             return mShelf.getHeight();
         }
-        int height = (int) Math.max(mLayoutMinHeight,
-                Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+        int height;
+        if (SceneContainerFlag.isEnabled()) {
+            // TODO(b/192348384): This is probably incorrect as mContentHeight is not up to date.
+            //  Consider removing usages of getInnerHeight in flexiglass if possible.
+            height = (int) Math.min(mLayoutHeight, mContentHeight) - mTopPadding;
+        } else {
+            height = (int) Math.max(mLayoutMinHeight,
+                    Math.min(mLayoutHeight, mContentHeight) - mTopPadding);
+        }
         if (ignorePulseHeight) {
             return height;
         }
@@ -549,6 +578,7 @@
     }
 
     public void setLayoutMinHeight(int layoutMinHeight) {
+        SceneContainerFlag.assertInLegacyMode();
         mLayoutMinHeight = layoutMinHeight;
     }
 
@@ -697,14 +727,6 @@
         return mHideAmount != 0;
     }
 
-    public void setAppearing(boolean appearing) {
-        mAppearing = appearing;
-    }
-
-    public boolean isAppearing() {
-        return mAppearing;
-    }
-
     public void setPulseHeight(float height) {
         if (height != mPulseHeight) {
             mPulseHeight = height;
@@ -835,8 +857,8 @@
         pw.println("mFractionToShade=" + mFractionToShade);
         pw.println("mHideAmount=" + mHideAmount);
         pw.println("mAppearFraction=" + mAppearFraction);
-        pw.println("mAppearing=" + mAppearing);
         pw.println("mExpansionFraction=" + mExpansionFraction);
+        pw.println("mQsExpansionFraction=" + mQsExpansionFraction);
         pw.println("mExpandingVelocity=" + mExpandingVelocity);
         pw.println("mOverScrollTopAmount=" + mOverScrollTopAmount);
         pw.println("mOverScrollBottomAmount=" + mOverScrollBottomAmount);
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 20b1fff..7c01784 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
@@ -114,7 +114,6 @@
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 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.ShadeScrimBounds;
@@ -258,7 +257,7 @@
     private float mOverScrolledBottomPixels;
     private final ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
     private final ListenerSet<Runnable> mHeadsUpHeightChangedListeners = new ListenerSet<>();
-    private NotificationLogger.OnChildLocationsChangedListener mListener;
+    private NotificationLogger.OnChildLocationsChangedListener mLegacyLocationsChangedListener;
     private OnNotificationLocationsChangedListener mLocationsChangedListener;
     private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
@@ -790,7 +789,6 @@
     private void onJustBeforeDraw() {
         if (SceneContainerFlag.isEnabled()) {
             if (mChildrenUpdateRequested) {
-                updateForcedScroll();
                 updateChildren();
                 mChildrenUpdateRequested = false;
             }
@@ -851,7 +849,7 @@
             return; // the rest of the fields are not important in Flexiglass
         }
 
-        y = getTopPadding();
+        y = mAmbientState.getTopPadding();
         drawDebugInfo(canvas, y, Color.RED, /* label= */ "getTopPadding() = " + y);
 
         y = getLayoutHeight();
@@ -875,7 +873,7 @@
         y = (int) (mAmbientState.getStackY());
         drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y);
 
-        y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+        y = (int) (mAmbientState.getStackY() + mAmbientState.getInterpolatedStackHeight());
         drawDebugInfo(canvas, y, Color.LTGRAY,
                 /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
 
@@ -1124,11 +1122,13 @@
 
     @Override
     public void addStackHeightChangedListener(@NonNull Runnable runnable) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mStackHeightChangedListeners.addIfAbsent(runnable);
     }
 
     @Override
     public void removeStackHeightChangedListener(@NonNull Runnable runnable) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         mStackHeightChangedListeners.remove(runnable);
     }
 
@@ -1231,9 +1231,10 @@
 
     @Override
     public void setStackTop(float stackTop) {
-        mAmbientState.setStackTop(stackTop);
-        // TODO(b/332574413): replace the following with using stackTop
-        updateTopPadding(stackTop, isAddOrRemoveAnimationPending());
+        if (mAmbientState.getStackTop() != stackTop) {
+            mAmbientState.setStackTop(stackTop);
+            onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
+        }
     }
 
     @Override
@@ -1244,6 +1245,7 @@
     @Override
     public void setHeadsUpTop(float headsUpTop) {
         mAmbientState.setHeadsUpTop(headsUpTop);
+        requestChildrenUpdate();
     }
 
     @Override
@@ -1252,6 +1254,11 @@
     }
 
     @Override
+    public void closeGutsOnSceneTouch() {
+        mController.closeControlsDueToOutsideTouch();
+    }
+
+    @Override
     public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
         mScrollViewFields.setSyntheticScrollConsumer(consumer);
     }
@@ -1262,6 +1269,11 @@
     }
 
     @Override
+    public void setCurrentGestureInGutsConsumer(@Nullable Consumer<Boolean> consumer) {
+        mScrollViewFields.setCurrentGestureInGutsConsumer(consumer);
+    }
+
+    @Override
     public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
         mScrollViewFields.setHeadsUpHeightConsumer(consumer);
     }
@@ -1281,7 +1293,7 @@
     public void setChildLocationsChangedListener(
             NotificationLogger.OnChildLocationsChangedListener listener) {
         NotificationsLiveDataStoreRefactor.assertInLegacyMode();
-        mListener = listener;
+        mLegacyLocationsChangedListener = listener;
     }
 
     private void setMaxLayoutHeight(int maxLayoutHeight) {
@@ -1296,8 +1308,10 @@
     }
 
     private void updateAlgorithmLayoutMinHeight() {
-        mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
-                ? getLayoutMinHeightInternal() : 0);
+        if (!SceneContainerFlag.isEnabled()) {
+            mAmbientState.setLayoutMinHeight(mQsFullScreen || isHeadsUpTransition()
+                    ? getLayoutMinHeightInternal() : 0);
+        }
     }
 
     /**
@@ -1375,6 +1389,10 @@
     }
 
     private void clampScrollPosition() {
+        // NSSL doesn't control scrolling with SceneContainer enabled
+        if (SceneContainerFlag.isEnabled()) {
+            return;
+        }
         int scrollRange = getScrollRange();
         if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
             // if the scroll boundary updates the position of the stack,
@@ -1385,28 +1403,26 @@
     }
 
     public int getTopPadding() {
+        SceneContainerFlag.assertInLegacyMode();
         return mAmbientState.getTopPadding();
     }
 
-    private void setTopPadding(int topPadding, boolean animate) {
-        if (getTopPadding() != topPadding) {
-            mAmbientState.setTopPadding(topPadding);
-            boolean shouldAnimate = animate || mAnimateNextTopPaddingChange;
-            updateAlgorithmHeightAndPadding();
-            updateContentHeight();
-            if (mAmbientState.isOnKeyguard()
-                    && !mShouldUseSplitNotificationShade
-                    && mShouldSkipTopPaddingAnimationAfterFold) {
-                mShouldSkipTopPaddingAnimationAfterFold = false;
-            } else if (shouldAnimate && mAnimationsEnabled && mIsExpanded) {
-                mTopPaddingNeedsAnimation = true;
-                mNeedsAnimation = true;
-            }
-            updateStackPosition();
-            requestChildrenUpdate();
-            notifyHeightChangeListener(null, shouldAnimate);
-            mAnimateNextTopPaddingChange = false;
+    private void onTopPaddingChanged(boolean animate) {
+        boolean shouldAnimate = animate || mAnimateNextTopPaddingChange;
+        updateAlgorithmHeightAndPadding();
+        updateContentHeight();
+        if (mAmbientState.isOnKeyguard()
+                && !mShouldUseSplitNotificationShade
+                && mShouldSkipTopPaddingAnimationAfterFold) {
+            mShouldSkipTopPaddingAnimationAfterFold = false;
+        } else if (shouldAnimate && mAnimationsEnabled && mIsExpanded) {
+            mTopPaddingNeedsAnimation = true;
+            mNeedsAnimation = true;
         }
+        updateStackPosition();
+        requestChildrenUpdate();
+        notifyHeightChangeListener(null, shouldAnimate);
+        mAnimateNextTopPaddingChange = false;
     }
 
     /**
@@ -1434,6 +1450,11 @@
      * @param listenerNeedsAnimation does the listener need to animate?
      */
     private void updateStackPosition(boolean listenerNeedsAnimation) {
+        // When scene container is active, we only want to recalculate stack heights.
+        if (SceneContainerFlag.isEnabled()) {
+            updateStackEndHeightAndStackHeight(mAmbientState.getExpansionFraction());
+            return;
+        }
         float topOverscrollAmount = mShouldUseSplitNotificationShade
                 ? getCurrentOverScrollAmount(true /* top */) : 0f;
         final float endTopPosition = getTopPadding() + mExtraTopInsetForFullShadeTransition
@@ -1446,10 +1467,8 @@
         if (mAmbientState.isBouncerInTransit() && mQsExpansionFraction > 0f) {
             fraction = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(fraction);
         }
-        if (!SceneContainerFlag.isEnabled()) {
-            final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
-            mAmbientState.setStackY(stackY);
-        }
+        final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
+        mAmbientState.setStackY(stackY);
 
         if (mOnStackYChanged != null) {
             mOnStackYChanged.accept(listenerNeedsAnimation);
@@ -1459,7 +1478,7 @@
 
     @VisibleForTesting
     public void updateStackEndHeightAndStackHeight(float fraction) {
-        final float oldStackHeight = mAmbientState.getStackHeight();
+        final float oldStackHeight = mAmbientState.getInterpolatedStackHeight();
         if (SceneContainerFlag.isEnabled()) {
             final float endHeight;
             if (!shouldSkipHeightUpdate()) {
@@ -1467,27 +1486,34 @@
             } else {
                 endHeight = mAmbientState.getStackEndHeight();
             }
-            updateStackHeight(endHeight, fraction);
+            updateInterpolatedStackHeight(endHeight, fraction);
         } else {
             if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
                 final float endHeight = updateStackEndHeight(
                         getHeight(), getEmptyBottomMarginInternal(), getTopPadding());
-                updateStackHeight(endHeight, fraction);
+                updateInterpolatedStackHeight(endHeight, fraction);
             } else {
                 // Always updateStackHeight to prevent jumps in the stack height when this fraction
                 // suddenly reapplies after a freeze.
                 final float endHeight = mAmbientState.getStackEndHeight();
-                updateStackHeight(endHeight, fraction);
+                updateInterpolatedStackHeight(endHeight, fraction);
             }
         }
-        if (oldStackHeight != mAmbientState.getStackHeight()) {
+        if (oldStackHeight != mAmbientState.getInterpolatedStackHeight()) {
             requestChildrenUpdate();
         }
     }
 
     private float updateStackEndHeight() {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0f;
-        float height = Math.max(0f, mAmbientState.getStackCutoff() - mAmbientState.getStackTop());
+        final float height;
+        if (mMaxDisplayedNotifications != -1) {
+            // The stack intrinsic height already contains the correct value when there is a limit
+            // in the max number of notifications (e.g. as in keyguard).
+            height = mIntrinsicContentHeight;
+        } else {
+            height = Math.max(0f, mAmbientState.getStackCutoff() - mAmbientState.getStackTop());
+        }
         mAmbientState.setStackEndHeight(height);
         return height;
     }
@@ -1507,7 +1533,7 @@
     }
 
     @VisibleForTesting
-    public void updateStackHeight(float endHeight, float fraction) {
+    public void updateInterpolatedStackHeight(float endHeight, float fraction) {
         if (!newAodTransition()) {
             // During the (AOD<=>LS) transition where dozeAmount is changing,
             // apply dozeAmount to stack height instead of expansionFraction
@@ -1517,7 +1543,7 @@
                 fraction = 1f - dozeAmount;
             }
         }
-        mAmbientState.setStackHeight(
+        mAmbientState.setInterpolatedStackHeight(
                 MathUtils.lerp(endHeight * StackScrollAlgorithm.START_FRACTION,
                         endHeight, fraction));
     }
@@ -1546,8 +1572,11 @@
 
         // Update the expand progress between started/stopped events
         mAmbientState.setExpansionFraction(expandFraction);
-        // TODO(b/332577544): don't convert to height which then converts to the fraction again
-        setExpandedHeight(expandFraction * getHeight());
+
+        if (!shouldSkipHeightUpdate()) {
+            updateStackEndHeightAndStackHeight(expandFraction);
+            updateExpandedHeight(expandFraction);
+        }
 
         // expansion stopped event requires that the expandFraction has already been updated
         if (!nowExpanding && wasExpanding) {
@@ -1556,12 +1585,32 @@
         }
     }
 
+    private void updateExpandedHeight(float expandFraction) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        float expandedHeight = expandFraction * getHeight();
+        setIsExpanded(expandedHeight > 0);
+
+        if (mExpandedHeight != expandedHeight) {
+            mExpandedHeight = expandedHeight;
+            updateAlgorithmHeightAndPadding();
+            requestChildrenUpdate();
+            notifyAppearChangedListeners();
+        }
+    }
+
+    @Override
+    public void setQsExpandFraction(float expandFraction) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        mAmbientState.setQsExpansionFraction(expandFraction);
+    }
+
     /**
      * Update the height of the panel.
      *
      * @param height the expanded height of the panel
      */
     public void setExpandedHeight(float height) {
+        SceneContainerFlag.assertInLegacyMode();
         final boolean skipHeightUpdate = shouldSkipHeightUpdate();
 
         updateStackPosition();
@@ -1585,13 +1634,12 @@
         float translationY;
         float appearFraction = 1.0f;
         boolean appearing = calculateAppearFraction(height) < 1;
-        mAmbientState.setAppearing(appearing);
         if (!appearing) {
             translationY = 0;
             if (mShouldShowShelfOnly) {
                 stackHeight = getTopPadding() + mShelf.getIntrinsicHeight();
             } else if (mQsFullScreen) {
-                int stackStartPosition = mContentHeight - getTopPadding() + mIntrinsicPadding;
+                int stackStartPosition = mContentHeight - getTopPadding() + getIntrinsicPadding();
                 int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
                 if (stackStartPosition <= stackEndPosition) {
                     stackHeight = stackEndPosition;
@@ -1694,6 +1742,7 @@
      * Measured relative to the resting position.
      */
     private float getExpandTranslationStart() {
+        SceneContainerFlag.assertInLegacyMode();
         return -getTopPadding() + getMinExpansionHeight() - mShelf.getIntrinsicHeight();
     }
 
@@ -1702,6 +1751,7 @@
      * Measured in absolute height.
      */
     private float getAppearStartPosition() {
+        SceneContainerFlag.assertInLegacyMode();
         if (isHeadsUpTransition()) {
             final NotificationSection firstVisibleSection = getFirstVisibleSection();
             final int pinnedHeight = firstVisibleSection != null
@@ -1721,6 +1771,19 @@
         if (mTopHeadsUpRow == null) {
             return 0;
         }
+        ExpandableNotificationRow row = getTopHeadsUpRow();
+        return row.getPinnedHeadsUpHeight();
+    }
+
+    private int getTopHeadsUpIntrinsicHeight() {
+        if (mTopHeadsUpRow == null) {
+            return 0;
+        }
+        ExpandableNotificationRow row = getTopHeadsUpRow();
+        return row.getIntrinsicHeight();
+    }
+
+    private ExpandableNotificationRow getTopHeadsUpRow() {
         ExpandableNotificationRow row = mTopHeadsUpRow;
         if (row.isChildInGroup()) {
             final NotificationEntry groupSummary =
@@ -1729,7 +1792,7 @@
                 row = groupSummary.getRow();
             }
         }
-        return row.getPinnedHeadsUpHeight();
+        return row;
     }
 
     /**
@@ -1744,6 +1807,7 @@
      *    have the shelf on its own)
      */
     private float getAppearEndPosition() {
+        SceneContainerFlag.assertInLegacyMode();
         if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
             return getAppearEndPositionLegacy();
         }
@@ -1763,7 +1827,7 @@
         } else {
             appearPosition = mEmptyShadeView.getHeight();
         }
-        return appearPosition + (onKeyguard() ? getTopPadding() : mIntrinsicPadding);
+        return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
     }
 
     /**
@@ -1789,7 +1853,7 @@
         } else {
             appearPosition = mEmptyShadeView.getHeight();
         }
-        return appearPosition + (onKeyguard() ? getTopPadding() : mIntrinsicPadding);
+        return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
     }
 
     private boolean isHeadsUpTransition() {
@@ -1804,7 +1868,7 @@
      */
     @FloatRange(from = -1.0, to = 1.0)
     public float calculateAppearFraction(float height) {
-        if (isHeadsUpTransition()) {
+        if (isHeadsUpTransition() && !SceneContainerFlag.isEnabled()) {
             // HUN is a special case because fraction can go negative if swiping up. And for now
             // it must go negative as other pieces responsible for proper translation up assume
             // negative value for HUN going up.
@@ -1935,7 +1999,8 @@
     }
 
     public void lockScrollTo(View v) {
-        if (mForcedScroll == v) {
+        // NSSL shouldn't handle scrolling with SceneContainer enabled.
+        if (mForcedScroll == v || SceneContainerFlag.isEnabled()) {
             return;
         }
         mForcedScroll = v;
@@ -1943,6 +2008,10 @@
     }
 
     public boolean scrollTo(View v) {
+        // NSSL shouldn't handle scrolling with SceneContainer enabled.
+        if (SceneContainerFlag.isEnabled()) {
+            return false;
+        }
         ExpandableView expandableView = (ExpandableView) v;
         int positionInLinearLayout = getPositionInLinearLayout(v);
         int targetScroll = targetScrollForView(expandableView, positionInLinearLayout);
@@ -1964,6 +2033,7 @@
      * the IME.
      */
     private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
+        SceneContainerFlag.assertInLegacyMode();
         return positionInLinearLayout + v.getIntrinsicHeight() +
                 getImeInset() - getHeight()
                 + ((!isExpanded() && isPinnedHeadsUp(v)) ? mHeadsUpInset : getTopPadding());
@@ -2273,6 +2343,7 @@
 
     private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
                                              boolean isRubberbanded) {
+        SceneContainerFlag.assertInLegacyMode();
         amount = Math.max(0, amount);
         if (animate) {
             mStateAnimator.animateOverScrollToAmount(amount, onTop, isRubberbanded);
@@ -2375,6 +2446,11 @@
     }
 
     private int getScrollRange() {
+        // TODO(b/360091533) Disable the usages of #getScrollRange() with SceneContainer enabled,
+        // because NSSL shouldn't control its own scrolling.
+        if (SceneContainerFlag.isEnabled()) {
+            return 0;
+        }
         // In current design, it only use the top HUN to treat all of HUNs
         // although there are more than one HUNs
         int contentHeight = mContentHeight;
@@ -2479,10 +2555,33 @@
     }
 
     @VisibleForTesting
-    void updateContentHeight() {
+    void updateStackHeight() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+
+        final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
+        final int footerIntrinsicHeight =
+                mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
+        final int notificationsHeight = (int) mNotificationStackSizeCalculator.computeHeight(
+                /* notificationStackScrollLayout= */ this,
+                mMaxDisplayedNotifications,
+                shelfIntrinsicHeight
+        );
+        mIntrinsicContentHeight = notificationsHeight;
+        final int fullStackHeight = notificationsHeight + footerIntrinsicHeight + mBottomPadding;
+        if (mScrollViewFields.getIntrinsicStackHeight() != fullStackHeight) {
+            mScrollViewFields.setIntrinsicStackHeight(fullStackHeight);
+            notifyStackHeightChangedListeners();
+        }
+    }
+
+    private void updateContentHeight() {
+        if (SceneContainerFlag.isEnabled()) {
+            updateStackHeight();
+            return;
+        }
+
         final float scrimTopPadding = getScrimTopPaddingOrZero();
         final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0;
-        final int footerIntrinsicHeight = mFooterView != null ? mFooterView.getIntrinsicHeight() : 0;
         final float height =
                 (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
                         /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
@@ -2492,26 +2591,27 @@
         // The topPadding can be bigger than the regular padding when qs is expanded, in that
         // state the maxPanelHeight and the contentHeight should be bigger
         mContentHeight =
-                (int) (height + Math.max(mIntrinsicPadding, getTopPadding()) + mBottomPadding);
-        mScrollViewFields.setIntrinsicStackHeight(
-                (int) (mIntrinsicPadding + mIntrinsicContentHeight + footerIntrinsicHeight
-                        + mBottomPadding));
+                (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding);
         updateScrollability();
         clampScrollPosition();
         updateStackPosition();
         mAmbientState.setContentHeight(mContentHeight);
-
-        notifyStackHeightChangedListeners();
     }
 
     @Override
     public int getIntrinsicStackHeight() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
         return mScrollViewFields.getIntrinsicStackHeight();
     }
 
     @Override
     public int getTopHeadsUpHeight() {
-        return getTopHeadsUpPinnedHeight();
+        return getTopHeadsUpIntrinsicHeight();
+    }
+
+    @Override
+    public int getHeadsUpInset() {
+        return mHeadsUpInset;
     }
 
     /**
@@ -2536,6 +2636,9 @@
     }
 
     private void updateScrollability() {
+        if (SceneContainerFlag.isEnabled()) {
+            return;
+        }
         boolean scrollable = !mQsFullScreen && getScrollRange() > 0;
         if (scrollable != mScrollable) {
             mScrollable = scrollable;
@@ -2545,6 +2648,7 @@
     }
 
     private void updateForwardAndBackwardScrollability() {
+        SceneContainerFlag.assertInLegacyMode();
         boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom();
         boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop();
         boolean changed = forwardScrollable != mForwardScrollable
@@ -2676,6 +2780,7 @@
      * @param animate  whether to animate the change
      */
     public void updateTopPadding(float qsHeight, boolean animate) {
+        SceneContainerFlag.assertInLegacyMode();
         int topPadding = (int) qsHeight;
         int minStackHeight = getLayoutMinHeightInternal();
         if (topPadding + minStackHeight > getHeight()) {
@@ -2683,7 +2788,10 @@
         } else {
             mTopPaddingOverflow = 0;
         }
-        setTopPadding(topPadding, animate && !mKeyguardBypassEnabled);
+        if (mAmbientState.getTopPadding() != topPadding) {
+            mAmbientState.setTopPadding(topPadding);
+            onTopPaddingChanged(/* animate = */ animate && !mKeyguardBypassEnabled);
+        }
         setExpandedHeight(mExpandedHeight);
     }
 
@@ -2698,6 +2806,7 @@
     }
 
     private int getLayoutMinHeightInternal() {
+        SceneContainerFlag.assertInLegacyMode();
         if (isHeadsUpTransition()) {
             ExpandableNotificationRow trackedHeadsUpRow = mAmbientState.getTrackedHeadsUpRow();
             if (trackedHeadsUpRow.isAboveShelf()) {
@@ -2777,7 +2886,9 @@
             NotificationEntry entry = ((ExpandableNotificationRow) child).getEntry();
             entry.removeOnSensitivityChangedListener(mOnChildSensitivityChangedListener);
         }
-        updateScrollStateForRemovedChild(child);
+        if (!SceneContainerFlag.isEnabled()) {
+            updateScrollStateForRemovedChild(child);
+        }
         boolean animationGenerated = container != null && generateRemoveAnimation(child);
         if (animationGenerated) {
             if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
@@ -2956,6 +3067,7 @@
      * @param removedChild the removed child
      */
     private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
+        SceneContainerFlag.assertInLegacyMode();
         final int startingPosition = getPositionInLinearLayout(removedChild);
         final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
         final int endPosition = startingPosition + childHeight;
@@ -2977,6 +3089,7 @@
      * @return the amount of scrolling needed to start clipping notifications.
      */
     private int getScrollAmountToScrollBoundary() {
+        SceneContainerFlag.assertInLegacyMode();
         if (mShouldUseSplitNotificationShade) {
             return mSidePaddings;
         }
@@ -3505,33 +3618,41 @@
 
     @Override
     public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (SceneContainerFlag.isEnabled() && mIsBeingDragged) {
+        if (SceneContainerFlag.isEnabled()) {
             int action = ev.getActionMasked();
-            boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
-            if (mSendingTouchesToSceneFramework) {
-                MotionEvent adjustedEvent = MotionEvent.obtain(ev);
-                adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
-                mController.sendTouchToSceneFramework(adjustedEvent);
-                mScrollViewFields.sendCurrentGestureOverscroll(
-                        getExpandedInThisMotion() && !isUpOrCancel);
-                adjustedEvent.recycle();
-            } else if (!isUpOrCancel) {
-                // if this is the first touch being sent to the scene framework,
-                // convert it into a synthetic DOWN event.
-                mSendingTouchesToSceneFramework = true;
-                MotionEvent downEvent = MotionEvent.obtain(ev);
-                downEvent.setAction(MotionEvent.ACTION_DOWN);
-                downEvent.setLocation(ev.getRawX(), ev.getRawY());
-                mController.sendTouchToSceneFramework(downEvent);
-                mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
-                downEvent.recycle();
+            boolean isTouchInGuts = mController.isTouchInGutsView(ev);
+            if (action == MotionEvent.ACTION_DOWN && !isTouchInGuts) {
+                mController.closeControlsDueToOutsideTouch();
             }
+            if (mIsBeingDragged) {
+                boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
+                if (mSendingTouchesToSceneFramework) {
+                    MotionEvent adjustedEvent = MotionEvent.obtain(ev);
+                    adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
+                    mScrollViewFields.sendCurrentGestureOverscroll(
+                            getExpandedInThisMotion() && !isUpOrCancel);
+                    mController.sendTouchToSceneFramework(adjustedEvent);
+                    adjustedEvent.recycle();
+                } else if (!isUpOrCancel) {
+                    // if this is the first touch being sent to the scene framework,
+                    // convert it into a synthetic DOWN event.
+                    mSendingTouchesToSceneFramework = true;
+                    MotionEvent downEvent = MotionEvent.obtain(ev);
+                    downEvent.setAction(MotionEvent.ACTION_DOWN);
+                    downEvent.setLocation(ev.getRawX(), ev.getRawY());
+                    mScrollViewFields.sendCurrentGestureInGuts(isTouchInGuts);
+                    mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
+                    mController.sendTouchToSceneFramework(downEvent);
+                    downEvent.recycle();
+                }
 
-            if (isUpOrCancel) {
-                mScrollViewFields.sendCurrentGestureOverscroll(false);
-                setIsBeingDragged(false);
+                if (isUpOrCancel) {
+                    mScrollViewFields.sendCurrentGestureInGuts(false);
+                    mScrollViewFields.sendCurrentGestureOverscroll(false);
+                    setIsBeingDragged(false);
+                }
+                return false;
             }
-            return false;
         }
         return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
     }
@@ -3598,7 +3719,7 @@
         if (!isScrollingEnabled()) {
             return false;
         }
-        if (isInsideQsHeader(ev) && !mIsBeingDragged) {
+        if (!isInScrollableRegion(ev) && !mIsBeingDragged) {
             return false;
         }
         mForcedScroll = null;
@@ -3766,11 +3887,26 @@
         return mFlingAfterUpEvent;
     }
 
-    protected boolean isInsideQsHeader(MotionEvent ev) {
-        if (SceneContainerFlag.isEnabled()) {
-            return ev.getY() < mAmbientState.getStackTop();
+    /** Is this touch event inside the scrollable region? */
+    @VisibleForTesting
+    boolean isInScrollableRegion(MotionEvent ev) {
+        if (!SceneContainerFlag.isEnabled()) {
+            return !isInsideQsHeader(ev);
+        }
+        ShadeScrimShape shape = mScrollViewFields.getScrimClippingShape();
+        if (shape == null) {
+            return true; // When there is no scrim, consider this event scrollable.
         }
 
+        ShadeScrimBounds bounds = shape.getBounds();
+        return ev.getX() >= bounds.getLeft()
+                && ev.getX() <= bounds.getRight()
+                && ev.getY() >= bounds.getTop()
+                && ev.getY() <= bounds.getBottom();
+    }
+
+    protected boolean isInsideQsHeader(MotionEvent ev) {
+        SceneContainerFlag.assertInLegacyMode();
         if (QSComposeFragment.isEnabled()) {
             if (mQSHeaderBoundsProvider == null) {
                 return false;
@@ -4072,6 +4208,11 @@
      */
     @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;
         }
@@ -4243,7 +4384,7 @@
                 // Resetting headsUpAnimatingAway on Shade expansion avoids delays caused by
                 // waiting for all child animations to finish.
                 // TODO(b/328390331) Do we need to reset this on QS expanded as well?
-                if (NotificationsHeadsUpRefactor.isEnabled()) {
+                if (SceneContainerFlag.isEnabled()) {
                     setHeadsUpAnimatingAway(false);
                 }
             } else {
@@ -4354,7 +4495,7 @@
 
     void onChildAnimationFinished() {
         setAnimationRunning(false);
-        if (NotificationsHeadsUpRefactor.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             setHeadsUpAnimatingAway(false);
         }
         requestChildrenUpdate();
@@ -4433,8 +4574,8 @@
                 mLocationsChangedListener.onChildLocationsChanged(collectVisibleLocationsCallable);
             }
         } else {
-            if (mListener != null) {
-                mListener.onChildLocationsChanged();
+            if (mLegacyLocationsChangedListener != null) {
+                mLegacyLocationsChangedListener.onChildLocationsChanged();
             }
         }
 
@@ -4538,10 +4679,20 @@
     }
 
     void setIntrinsicPadding(int intrinsicPadding) {
+        SceneContainerFlag.assertInLegacyMode();
         mIntrinsicPadding = intrinsicPadding;
     }
 
+    /**
+     * Distance from the top of the screen in, where notifications should start when fully expanded
+     * or in the LS.
+     *
+     * Always 0 with SceneContainer enabled.
+     */
     int getIntrinsicPadding() {
+        if (SceneContainerFlag.isEnabled()) {
+            return 0;
+        }
         return mIntrinsicPadding;
     }
 
@@ -4787,6 +4938,7 @@
     }
 
     public boolean isBelowLastNotification(float touchX, float touchY) {
+        SceneContainerFlag.assertInLegacyMode();
         int childCount = getChildCount();
         for (int i = childCount - 1; i >= 0; i--) {
             ExpandableView child = getChildAtIndex(i);
@@ -4822,6 +4974,11 @@
     @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);
@@ -4831,6 +4988,11 @@
     @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) {
@@ -4889,7 +5051,7 @@
     }
 
     public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
-        NotificationsHeadsUpRefactor.assertInLegacyMode();
+        SceneContainerFlag.assertInLegacyMode();
         ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
         generateHeadsUpAnimation(row, isHeadsUp);
     }
@@ -4932,7 +5094,7 @@
             mNeedsAnimation = true;
             if (!mIsExpanded && !mWillExpand && !isHeadsUp) {
                 row.setHeadsUpAnimatingAway(true);
-                if (NotificationsHeadsUpRefactor.isEnabled()) {
+                if (SceneContainerFlag.isEnabled()) {
                     setHeadsUpAnimatingAway(true);
                 }
             }
@@ -5016,10 +5178,12 @@
     }
 
     boolean isQsFullScreen() {
+        SceneContainerFlag.assertInLegacyMode();
         return mQsFullScreen;
     }
 
     public void setQsExpansionFraction(float qsExpansionFraction) {
+        SceneContainerFlag.assertInLegacyMode();
         boolean footerAffected = mQsExpansionFraction != qsExpansionFraction
                 && (mQsExpansionFraction == 1 || qsExpansionFraction == 1);
         mQsExpansionFraction = qsExpansionFraction;
@@ -5063,6 +5227,7 @@
     }
 
     private void updateOnScrollChange() {
+        SceneContainerFlag.assertInLegacyMode();
         if (mScrollListener != null) {
             mScrollListener.accept(mOwnScrollY);
         }
@@ -5126,7 +5291,7 @@
         updateClipping();
     }
 
-    /** TODO(b/328390331) make this private, when {@link NotificationsHeadsUpRefactor} is removed */
+    /** TODO(b/328390331) make this private, when {@link SceneContainerFlag} is removed */
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
         if (mHeadsUpAnimatingAway != headsUpAnimatingAway) {
             mHeadsUpAnimatingAway = headsUpAnimatingAway;
@@ -5732,7 +5897,7 @@
         return mDisallowScrollingInThisMotion;
     }
 
-    boolean isBeingDragged() {
+    public boolean isBeingDragged() {
         return mIsBeingDragged;
     }
 
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 41195aa..bcdc3bc 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
@@ -127,11 +127,11 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.shared.GroupHunAnimationFix;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpNotificationViewControllerEmptyImpl;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.phone.HeadsUpTouchHelper.HeadsUpNotificationViewController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -638,8 +638,11 @@
                         if (row.isPinned() && !canChildBeDismissed(row)
                                 && row.getEntry().getSbn().getNotification().fullScreenIntent
                                 == null) {
-                            mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
-                                    true /* removeImmediately */);
+                            mHeadsUpManager.removeNotification(
+                                    row.getEntry().getSbn().getKey(),
+                                    /* removeImmediately= */ true ,
+                                    /* reason= */ "onChildSnappedBack"
+                            );
                         }
                     }
                 }
@@ -682,13 +685,13 @@
             new OnHeadsUpChangedListener() {
                 @Override
                 public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-                    NotificationsHeadsUpRefactor.assertInLegacyMode();
+                    SceneContainerFlag.assertInLegacyMode();
                     mView.setInHeadsUpPinnedMode(inPinnedMode);
                 }
 
                 @Override
                 public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-                    NotificationsHeadsUpRefactor.assertInLegacyMode();
+                    SceneContainerFlag.assertInLegacyMode();
                     NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
                     mView.setTopHeadsUpRow(topEntry != null ? topEntry.getRow() : null);
                     generateHeadsUpAnimation(entry, isHeadsUp);
@@ -770,7 +773,7 @@
                     mHeadsUpManager,
                     statusBarService.get(),
                     getHeadsUpCallback(),
-                    new HeadsUpNotificationViewControllerEmptyImpl()
+                    getHeadsUpNotificationViewController()
             );
         }
         mNotificationRoundnessManager = notificationRoundnessManager;
@@ -876,7 +879,7 @@
             });
         }
 
-        if (!NotificationsHeadsUpRefactor.isEnabled()) {
+        if (!SceneContainerFlag.isEnabled()) {
             mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         }
         mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
@@ -1183,6 +1186,7 @@
     }
 
     public void setIntrinsicPadding(int intrinsicPadding) {
+        SceneContainerFlag.assertInLegacyMode();
         mView.setIntrinsicPadding(intrinsicPadding);
     }
 
@@ -1230,6 +1234,7 @@
     }
 
     public boolean isBelowLastNotification(float x, float y) {
+        SceneContainerFlag.assertInLegacyMode();
         return mView.isBelowLastNotification(x, y);
     }
 
@@ -1270,6 +1275,7 @@
     }
 
     public void setQsExpansionFraction(float expansionFraction) {
+        SceneContainerFlag.assertInLegacyMode();
         mView.setQsExpansionFraction(expansionFraction);
     }
 
@@ -1323,6 +1329,7 @@
     }
 
     public int getTopPadding() {
+        SceneContainerFlag.assertInLegacyMode();
         return mView.getTopPadding();
     }
 
@@ -1402,6 +1409,7 @@
     }
 
     public float calculateAppearFraction(float height) {
+        SceneContainerFlag.assertInLegacyMode();
         return mView.calculateAppearFraction(height);
     }
 
@@ -1501,7 +1509,7 @@
     }
 
     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        NotificationsHeadsUpRefactor.assertInLegacyMode();
+        SceneContainerFlag.assertInLegacyMode();
         mView.setHeadsUpAnimatingAway(headsUpAnimatingAway);
     }
 
@@ -1683,7 +1691,7 @@
                 mVisibilityProvider.obtain(entry, true));
     }
 
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+    private View getGutsView() {
         NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
         NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
         View translatingParentView = mSwipeHelper.getTranslatingParentView();
@@ -1696,15 +1704,35 @@
             // Checking menu
             view = translatingParentView;
         }
+        return view;
+    }
+
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        SceneContainerFlag.assertInLegacyMode();
+        View view = getGutsView();
         if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
             // Touch was outside visible guts / menu notification, close what's visible
-            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
-                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+            closeAndSaveGuts();
         }
     }
 
+    void closeControlsDueToOutsideTouch() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        closeAndSaveGuts();
+    }
+
+    private void closeAndSaveGuts() {
+        mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+                false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                false /* resetMenu */);
+        mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+    }
+
+    boolean isTouchInGutsView(MotionEvent event) {
+        View view = getGutsView();
+        return NotificationSwipeHelper.isTouchInView(event, view);
+    }
+
     public void clearSilentNotifications() {
         FooterViewRefactor.assertInLegacyMode();
         // Leave the shade open if there will be other notifs left over to clear
@@ -1847,6 +1875,32 @@
         return mTouchHandler;
     }
 
+    private HeadsUpNotificationViewController getHeadsUpNotificationViewController() {
+        HeadsUpNotificationViewController headsUpViewController;
+        if (SceneContainerFlag.isEnabled()) {
+            headsUpViewController = new HeadsUpNotificationViewController() {
+                @Override
+                public void setHeadsUpDraggingStartingHeight(int startHeight) {
+                    // do nothing
+                }
+
+                @Override
+                public void setTrackedHeadsUp(ExpandableNotificationRow expandableNotificationRow) {
+                    setTrackingHeadsUp(expandableNotificationRow);
+                }
+
+                @Override
+                public void startExpand(float newX, float newY, boolean startTracking,
+                        float expandedHeight) {
+                    // do nothing
+                }
+            };
+        } else {
+            headsUpViewController = new HeadsUpNotificationViewControllerEmptyImpl();
+        }
+        return headsUpViewController;
+    }
+
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("mMaxAlphaFromView=" + mMaxAlphaFromView);
@@ -2050,7 +2104,6 @@
                 hunWantsIt = mHeadsUpTouchHelper.onInterceptTouchEvent(ev);
                 if (hunWantsIt) {
                     mView.startDraggingOnHun();
-                    mHeadsUpManager.unpinAll(true);
                 }
             }
             boolean swipeWantsIt = false;
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 383d8b3..aa39539 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
@@ -53,6 +53,11 @@
      */
     var currentGestureOverscrollConsumer: Consumer<Boolean>? = null
     /**
+     * When a gesture is on open notification guts, which means scene container should not close the
+     * guts off of this gesture, we can notify the placeholder through here.
+     */
+    var currentGestureInGutsConsumer: Consumer<Boolean>? = null
+    /**
      * Any time the heads up height is recalculated, it should be updated here to be used by the
      * placeholder
      */
@@ -66,6 +71,10 @@
     fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) =
         currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll)
 
+    /** send [isCurrentGestureInGuts] to the [currentGestureInGutsConsumer], if present. */
+    fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) =
+        currentGestureInGutsConsumer?.accept(isCurrentGestureInGuts)
+
     /** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
     fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index aee1d3e..ef1bcfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static androidx.core.math.MathUtils.clamp;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -577,7 +575,8 @@
         final float shelfHeight = showingShelf ? ambientState.getShelf().getIntrinsicHeight() : 0f;
         final float scrimPadding = getScrimTopPaddingOrZero(ambientState);
 
-        final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
+        final float stackHeight =
+                ambientState.getInterpolatedStackHeight() - shelfHeight - scrimPadding;
         final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
         if (stackEndHeight == 0f) {
             // This should not happen, since even when the shade is empty we show EmptyShadeView
@@ -736,7 +735,7 @@
                             || ambientState.getDozeAmount() == 1f
                             || bypassPulseNotExpanding
                             ? ambientState.getInnerHeight()
-                            : ambientState.getStackHeight();
+                            : ambientState.getInterpolatedStackHeight();
                     final float shelfStart = stackBottom
                             - ambientState.getShelf().getIntrinsicHeight()
                             - mPaddingBetweenElements;
@@ -890,7 +889,14 @@
                 continue;
             }
             ExpandableViewState childState = row.getViewState();
-            if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
+            boolean shouldSetTopHeadsUpEntry;
+            if (SceneContainerFlag.isEnabled()) {
+                shouldSetTopHeadsUpEntry = row.isHeadsUp();
+            } else {
+                shouldSetTopHeadsUpEntry = row.mustStayOnScreen();
+            }
+            if (topHeadsUpEntry == null && shouldSetTopHeadsUpEntry
+                    && !childState.headsUpIsVisible) {
                 topHeadsUpEntry = row;
                 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
             }
@@ -898,7 +904,7 @@
             float unmodifiedEndLocation = childState.getYTranslation() + childState.height;
             if (mIsExpanded) {
                 if (SceneContainerFlag.isEnabled()) {
-                    if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(),
+                    if (shouldHunBeVisibleWhenScrolled(row.isHeadsUp(),
                             childState.headsUpIsVisible, row.showingPulsing(),
                             ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) {
                         // the height of this child before clamping it to the top
@@ -909,10 +915,19 @@
                                 /* viewState = */ childState
                         );
                         float baseZ = ambientState.getBaseZHeight();
-                        if (headsUpTranslation < ambientState.getStackTop()) {
-                            // HUN displayed above the stack top, it needs a fix shadow
-                            childState.setZTranslation(baseZ + mPinnedZTranslationExtra);
-                        } else {
+                        if (headsUpTranslation > ambientState.getStackTop()
+                                && row.isAboveShelf()) {
+                            // HUN displayed outside of the stack during transition from Gone/LS;
+                            // add a shadow that corresponds to the transition progress.
+                            float fraction = 1 - ambientState.getExpansionFraction();
+                            childState.setZTranslation(baseZ + fraction * mPinnedZTranslationExtra);
+                        } else if (headsUpTranslation < ambientState.getStackTop()
+                                && row.isAboveShelf()) {
+                            // HUN displayed outside of the stack during transition from QS;
+                            // add a shadow that corresponds to the transition progress.
+                            float fraction = ambientState.getQsExpansionFraction();
+                            childState.setZTranslation(baseZ + fraction * mPinnedZTranslationExtra);
+                        } else if (headsUpTranslation > ambientState.getStackTop()) {
                             // HUN displayed within the stack, add a shadow if it overlaps with
                             // other elements.
                             //
@@ -927,6 +942,8 @@
                                     /* baseZ = */ baseZ,
                                     /* viewState = */ childState
                             );
+                        } else {
+                            childState.setZTranslation(baseZ);
                         }
                         if (isTopEntry && row.isAboveShelf()) {
                             clampHunToMaxTranslation(
@@ -1081,7 +1098,7 @@
         if (scrollingContentTopPadding > 0f) {
             // scrollingContentTopPadding makes a gap between the bottom of the HUN and the top
             // of the scrolling content. Use this to animate to the full shadow.
-            shadowFraction = clamp(overlap / scrollingContentTopPadding, 0f, 1f);
+            shadowFraction = Math.clamp(overlap / scrollingContentTopPadding, 0f, 1f);
         }
 
         if (overlap > 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index f6d9351..4907d44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -39,4 +39,7 @@
      * consumed part of the gesture.
      */
     val isCurrentGestureOverscroll = MutableStateFlow(false)
+
+    /** Whether the current touch gesture is on any open notification guts. */
+    val isCurrentGestureInGuts = MutableStateFlow(false)
 }
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 8557afc..756cd87 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
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.notification.stack.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository
@@ -39,6 +40,7 @@
 constructor(
     private val viewHeightRepository: NotificationViewHeightRepository,
     private val placeholderRepository: NotificationPlaceholderRepository,
+    sceneInteractor: SceneInteractor,
     shadeInteractor: ShadeInteractor,
 ) {
     /** The bounds of the notification stack in the current scene. */
@@ -93,6 +95,15 @@
     val isCurrentGestureOverscroll: Flow<Boolean> =
         viewHeightRepository.isCurrentGestureOverscroll.asStateFlow()
 
+    /** Whether we should close any notification guts that are currently open. */
+    val shouldCloseGuts: Flow<Boolean> =
+        combine(
+            sceneInteractor.isSceneContainerUserInputOngoing,
+            viewHeightRepository.isCurrentGestureInGuts
+        ) { isUserInputOngoing, isCurrentGestureInGuts ->
+            isUserInputOngoing && !isCurrentGestureInGuts
+        }
+
     /** Sets the alpha to apply to the NSSL for the brightness mirror */
     fun setAlphaForBrightnessMirror(alpha: Float) {
         placeholderRepository.alphaForBrightnessMirror.value = alpha
@@ -119,6 +130,10 @@
         viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll
     }
 
+    fun setCurrentGestureInGuts(isInGuts: Boolean) {
+        viewHeightRepository.isCurrentGestureInGuts.value = isInGuts
+    }
+
     fun setConstrainedAvailableSpace(height: Int) {
         placeholderRepository.constrainedAvailableSpace.value = height
     }
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 6226fe7..235b4da 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
@@ -71,6 +71,9 @@
     /** Set a consumer for current gesture overscroll events */
     fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?)
 
+    /** Set a consumer for current gesture in guts events */
+    fun setCurrentGestureInGutsConsumer(consumer: Consumer<Boolean>?)
+
     /** Set a consumer for heads up height changed events */
     fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
 
@@ -80,9 +83,24 @@
     /** sets the current expand fraction */
     fun setExpandFraction(expandFraction: Float)
 
+    /** sets the current QS expand fraction */
+    fun setQsExpandFraction(expandFraction: Float)
+
     /** Sets whether the view is displayed in doze mode. */
     fun setDozing(dozing: Boolean)
 
+    /** Sets whether the view is displayed in pulsing mode. */
+    fun setPulsing(pulsing: Boolean, animated: Boolean)
+
+    /** Gets the inset for HUNs when they are not visible */
+    fun getHeadsUpInset(): Int
+
+    /**
+     * Signals that any open Notification guts should be closed, as scene container is handling
+     * touch events.
+     */
+    fun closeGutsOnSceneTouch()
+
     /** Adds a listener to be notified, when the stack height might have changed. */
     fun addStackHeightChangedListener(runnable: Runnable)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 5572f8e..d770b20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController
@@ -36,7 +37,6 @@
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerShelfViewBinder
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinder
 import com.android.systemui.statusbar.notification.stack.DisplaySwitchNotificationsHiderTracker
@@ -93,7 +93,7 @@
 
         view.repeatWhenAttached {
             lifecycleScope.launch {
-                if (NotificationsHeadsUpRefactor.isEnabled) {
+                if (SceneContainerFlag.isEnabled) {
                     launch { hunBinder.bindHeadsUpNotifications(view) }
                 }
                 launch { bindShelf(shelf) }
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 fd08e89..6d5553f 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,14 +17,15 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewbinder
 
 import android.util.Log
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.flow.filter
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.common.ui.view.onLayoutChanged
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.WindowLifecycleState
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.viewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationScrollViewModel
@@ -33,9 +34,9 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.DisposableHandle
-import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.launch
 
 /** Binds the [NotificationScrollView]. */
@@ -46,7 +47,7 @@
     dumpManager: DumpManager,
     @Main private val mainImmediateDispatcher: CoroutineDispatcher,
     private val view: NotificationScrollView,
-    private val viewModel: NotificationScrollViewModel,
+    private val viewModelFactory: NotificationScrollViewModel.Factory,
     private val configuration: ConfigurationState,
 ) : FlowDumperImpl(dumpManager) {
 
@@ -61,38 +62,59 @@
     }
 
     fun bindWhileAttached(): DisposableHandle {
-        return view.asView().repeatWhenAttached(mainImmediateDispatcher) {
-            repeatOnLifecycle(Lifecycle.State.CREATED) { bind() }
-        }
+        return view.asView().repeatWhenAttached(mainImmediateDispatcher) { bind() }
     }
 
-    suspend fun bind() = coroutineScope {
-        launchAndDispose {
-            updateViewPosition()
-            view.asView().onLayoutChanged { updateViewPosition() }
-        }
+    suspend fun bind(): Nothing =
+        view.asView().viewModel(
+            traceName = "NotificationScrollViewBinder",
+            minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+            factory = viewModelFactory::create,
+        ) { viewModel ->
+            launchAndDispose {
+                updateViewPosition()
+                view.asView().onLayoutChanged { updateViewPosition() }
+            }
 
-        launch {
-            viewModel
-                .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
-                .collect { view.setScrimClippingShape(it) }
-        }
+            launch {
+                viewModel
+                    .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset)
+                    .collect { view.setScrimClippingShape(it) }
+            }
 
-        launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
-        launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
-        launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
-        launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
-        launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+            launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
+            launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
+            launch {
+                viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) }
+            }
+            launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } }
+            launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
+            launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
+            launch {
+                viewModel.isPulsing.collect { isPulsing ->
+                    view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value)
+                }
+            }
+            launch {
+                viewModel.shouldResetStackTop
+                    .filter { it }
+                    .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
+            }
+            launch {
+                viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
+            }
 
-        launchAndDispose {
-            view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
-            view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
-            DisposableHandle {
-                view.setSyntheticScrollConsumer(null)
-                view.setCurrentGestureOverscrollConsumer(null)
+            launchAndDispose {
+                view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
+                view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+                view.setCurrentGestureInGutsConsumer(viewModel.currentGestureInGutsConsumer)
+                DisposableHandle {
+                    view.setSyntheticScrollConsumer(null)
+                    view.setCurrentGestureOverscrollConsumer(null)
+                    view.setCurrentGestureInGutsConsumer(null)
+                }
             }
         }
-    }
 
     /** flow of the scrim clipping radius */
     private val scrimRadius: Flow<Int>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 5fba615..e55492e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
@@ -26,7 +27,6 @@
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel
 import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackInteractor
 import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
@@ -256,7 +256,7 @@
     }
 
     val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(null)
         } else {
             headsUpNotificationInteractor.topHeadsUpRow.dumpWhileCollecting("topHeadsUpRow")
@@ -264,7 +264,7 @@
     }
 
     val pinnedHeadsUpRows: Flow<Set<HeadsUpRowKey>> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(emptySet())
         } else {
             headsUpNotificationInteractor.pinnedHeadsUpRows.dumpWhileCollecting("pinnedHeadsUpRows")
@@ -272,7 +272,7 @@
     }
 
     val headsUpAnimationsEnabled: Flow<Boolean> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
             flowOf(true).dumpWhileCollecting("headsUpAnimationsEnabled")
@@ -280,7 +280,7 @@
     }
 
     val hasPinnedHeadsUpRow: Flow<Boolean> by lazy {
-        if (NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode()) {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
             flowOf(false)
         } else {
             headsUpNotificationInteractor.hasPinnedRows.dumpWhileCollecting("hasPinnedHeadsUpRow")
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 2ba79a8..3e42413 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
@@ -17,11 +17,13 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.ObservableTransitionState.Idle
+import com.android.compose.animation.scene.ObservableTransitionState.Transition
+import com.android.compose.animation.scene.ObservableTransitionState.Transition.ChangeScene
 import com.android.compose.animation.scene.SceneKey
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneFamilies
@@ -33,19 +35,23 @@
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
 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.FlowDumperImpl
+import com.android.systemui.util.kotlin.ActivatableFlowDumper
+import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
 import dagger.Lazy
-import javax.inject.Inject
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
 
 /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */
-@SysUISingleton
 class NotificationScrollViewModel
-@Inject
+@AssistedInject
 constructor(
     dumpManager: DumpManager,
     stackAppearanceInteractor: NotificationStackAppearanceInteractor,
@@ -54,7 +60,49 @@
     // TODO(b/336364825) Remove Lazy when SceneContainerFlag is released -
     // while the flag is off, creating this object too early results in a crash
     keyguardInteractor: Lazy<KeyguardInteractor>,
-) : FlowDumperImpl(dumpManager) {
+) :
+    ActivatableFlowDumper by ActivatableFlowDumperImpl(dumpManager, "NotificationScrollViewModel"),
+    ExclusiveActivatable() {
+
+    override suspend fun onActivated(): Nothing {
+        activateFlowDumper()
+    }
+
+    private fun expandedInScene(scene: SceneKey): Boolean {
+        return when (scene) {
+            Scenes.Lockscreen,
+            Scenes.Shade,
+            Scenes.QuickSettings -> true
+            else -> false
+        }
+    }
+
+    private fun fullyExpandedDuringSceneChange(change: ChangeScene): Boolean {
+        // The lockscreen stack is visible during all transitions away from the lockscreen, so keep
+        // the stack expanded until those transitions finish.
+        return (expandedInScene(change.fromScene) && expandedInScene(change.toScene)) ||
+            change.isBetween({ it == Scenes.Lockscreen }, { true })
+    }
+
+    private fun expandFractionDuringSceneChange(
+        change: ChangeScene,
+        shadeExpansion: Float,
+        qsExpansion: Float,
+    ): Float {
+        return if (fullyExpandedDuringSceneChange(change)) {
+            1f
+        } else if (change.isBetween({ it == Scenes.Gone }, { it in SceneFamilies.NotifShade })) {
+            shadeExpansion
+        } else if (change.isBetween({ it == Scenes.Gone }, { it == Scenes.QuickSettings })) {
+            // during QS expansion, increase fraction at same rate as scrim alpha,
+            // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
+            (qsExpansion / EXPANSION_FOR_MAX_SCRIM_ALPHA - EXPANSION_FOR_DELAYED_STACK_FADE_IN)
+                .coerceIn(0f, 1f)
+        } else {
+            0f
+        }
+    }
+
     /**
      * The expansion fraction of the notification stack. It should go from 0 to 1 when transitioning
      * from Gone to Shade scenes, and remain at 1 when in Lockscreen or Shade scenes and while
@@ -67,48 +115,34 @@
                 shadeInteractor.qsExpansion,
                 sceneInteractor.transitionState,
                 sceneInteractor.resolveSceneFamily(SceneFamilies.QuickSettings),
-            ) { shadeExpansion, shadeMode, qsExpansion, transitionState, quickSettingsScene ->
+            ) { shadeExpansion, _, qsExpansion, transitionState, _ ->
                 when (transitionState) {
-                    is ObservableTransitionState.Idle -> {
-                        when (transitionState.currentScene) {
-                            Scenes.Lockscreen,
-                            Scenes.QuickSettings -> 1f
-                            else -> shadeExpansion
-                        }
-                    }
-                    is ObservableTransitionState.Transition -> {
-                        if (
-                            (transitionState.fromScene in SceneFamilies.NotifShade &&
-                                transitionState.toScene == quickSettingsScene) ||
-                                (transitionState.fromScene in quickSettingsScene &&
-                                    transitionState.toScene in SceneFamilies.NotifShade) ||
-                                (transitionState.fromScene == Scenes.Lockscreen &&
-                                    transitionState.toScene in SceneFamilies.NotifShade) ||
-                                (transitionState.fromScene in SceneFamilies.NotifShade &&
-                                    transitionState.toScene == Scenes.Lockscreen)
-                        ) {
-                            1f
-                        } else if (
-                            shadeMode != ShadeMode.Split &&
-                                (transitionState.fromScene in SceneFamilies.Home &&
-                                    transitionState.toScene == quickSettingsScene) ||
-                                (transitionState.fromScene == quickSettingsScene &&
-                                    transitionState.toScene in SceneFamilies.Home)
-                        ) {
-                            // during QS expansion, increase fraction at same rate as scrim alpha,
-                            // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
-                            (qsExpansion / EXPANSION_FOR_MAX_SCRIM_ALPHA -
-                                    EXPANSION_FOR_DELAYED_STACK_FADE_IN)
-                                .coerceIn(0f, 1f)
-                        } else {
-                            shadeExpansion
-                        }
-                    }
+                    is Idle -> if (expandedInScene(transitionState.currentScene)) 1f else 0f
+                    is ChangeScene ->
+                        expandFractionDuringSceneChange(
+                            transitionState,
+                            shadeExpansion,
+                            qsExpansion,
+                        )
+                    is Transition.ShowOrHideOverlay,
+                    is Transition.ReplaceOverlay -> TODO("b/359173565: Handle overlay transitions")
                 }
             }
             .distinctUntilChanged()
             .dumpWhileCollecting("expandFraction")
 
+    val qsExpandFraction: Flow<Float> =
+        shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
+
+    /** Whether we should close any open notification guts. */
+    val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts
+
+    val shouldResetStackTop: Flow<Boolean> =
+        sceneInteractor.transitionState
+            .mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("shouldResetStackTop")
+
     private operator fun SceneKey.contains(scene: SceneKey) =
         sceneInteractor.isSceneInFamily(scene, this)
 
@@ -169,6 +203,10 @@
     val currentGestureOverscrollConsumer: (Boolean) -> Unit =
         stackAppearanceInteractor::setCurrentGestureOverscroll
 
+    /** Receives whether the current touch gesture is inside any open guts. */
+    val currentGestureInGutsConsumer: (Boolean) -> Unit =
+        stackAppearanceInteractor::setCurrentGestureInGuts
+
     /** Whether the notification stack is scrollable or not. */
     val isScrollable: Flow<Boolean> =
         sceneInteractor.currentScene
@@ -186,4 +224,31 @@
             keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
         }
     }
+
+    /** Whether the notification stack is displayed in pulsing mode. */
+    val isPulsing: Flow<Boolean> by lazy {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
+            flowOf(false)
+        } else {
+            keyguardInteractor.get().isPulsing.dumpWhileCollecting("isPulsing")
+        }
+    }
+
+    val shouldAnimatePulse: StateFlow<Boolean> by lazy {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
+            MutableStateFlow(false)
+        } else {
+            keyguardInteractor.get().isAodAvailable
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationScrollViewModel
+    }
 }
+
+private fun ChangeScene.isBetween(
+    a: (SceneKey) -> Boolean,
+    b: (SceneKey) -> Boolean,
+): Boolean = (a(fromScene) && b(toScene)) || (b(fromScene) && a(toScene))
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 d179888..69c1bf3 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
@@ -16,40 +16,72 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
-import com.android.systemui.dagger.SysUISingleton
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.FeatureFlagsClassic
 import com.android.systemui.flags.Flags
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 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.ShadeScrimBounds
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
-import com.android.systemui.util.kotlin.FlowDumperImpl
-import javax.inject.Inject
+import com.android.systemui.util.kotlin.ActivatableFlowDumper
+import com.android.systemui.util.kotlin.ActivatableFlowDumperImpl
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
 
 /**
  * ViewModel used by the Notification placeholders inside the scene container to update the
  * [NotificationStackAppearanceInteractor], and by extension control the NSSL.
  */
-@SysUISingleton
 class NotificationsPlaceholderViewModel
-@Inject
+@AssistedInject
 constructor(
-    dumpManager: DumpManager,
     private val interactor: NotificationStackAppearanceInteractor,
-    shadeInteractor: ShadeInteractor,
+    private val sceneInteractor: SceneInteractor,
+    private val shadeInteractor: ShadeInteractor,
     private val headsUpNotificationInteractor: HeadsUpNotificationInteractor,
     featureFlags: FeatureFlagsClassic,
-) : FlowDumperImpl(dumpManager) {
+    dumpManager: DumpManager,
+) :
+    ExclusiveActivatable(),
+    ActivatableFlowDumper by ActivatableFlowDumperImpl(
+        dumpManager = dumpManager,
+        tag = "NotificationsPlaceholderViewModel",
+    ) {
+
     /** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
     val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
 
     /** DEBUG: whether the debug logging should be output. */
     val isDebugLoggingEnabled: Boolean = SceneContainerFlag.isEnabled
 
+    override suspend fun onActivated(): Nothing {
+        coroutineScope {
+            launch {
+                shadeInteractor.isAnyExpanded
+                    .filter { it }
+                    .collect { headsUpNotificationInteractor.unpinAll(true) }
+            }
+
+            launch {
+                sceneInteractor.transitionState
+                    .map { state -> state is ObservableTransitionState.Idle }
+                    .filter { it }
+                    .collect { headsUpNotificationInteractor.onTransitionIdle() }
+            }
+        }
+        activateFlowDumper()
+    }
+
     /** Notifies that the bounds of the notification scrim have changed. */
     fun onScrimBoundsChanged(bounds: ShadeScrimBounds?) {
         interactor.setShadeScrimBounds(bounds)
@@ -114,6 +146,11 @@
     fun snoozeHun() {
         headsUpNotificationInteractor.snooze()
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): NotificationsPlaceholderViewModel
+    }
 }
 
 // Expansion fraction thresholds (between 0-1f) at which the corresponding value should be
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 99f7a75..aed00d8 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.compose.animation.scene.SceneKey
 import com.android.systemui.common.shared.model.NotificationContainerBounds
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -141,10 +142,6 @@
     private val communalSceneInteractor: CommunalSceneInteractor,
     unfoldTransitionInteractor: UnfoldTransitionInteractor,
 ) : FlowDumperImpl(dumpManager) {
-    // TODO(b/349784682): Transform deprecated states for Flexiglass
-    private val statesForConstrainedNotifications: Set<KeyguardState> =
-        setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
-    private val statesForHiddenKeyguard: Set<KeyguardState> = setOf(GONE, OCCLUDED)
 
     /**
      * Is either shade/qs expanded? This intentionally does not use the [ShadeInteractor] version,
@@ -217,14 +214,16 @@
 
     /** If the user is visually on one of the unoccluded lockscreen states. */
     val isOnLockscreen: Flow<Boolean> =
-        combine(
-                keyguardTransitionInteractor.finishedKeyguardState.map {
-                    statesForConstrainedNotifications.contains(it)
-                },
+        anyOf(
+                keyguardTransitionInteractor.isFinishedIn(AOD),
+                keyguardTransitionInteractor.isFinishedIn(DOZING),
+                keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER),
+                keyguardTransitionInteractor.isFinishedIn(
+                    scene = Scenes.Bouncer,
+                    stateWithoutSceneContainer = PRIMARY_BOUNCER
+                ),
                 keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
-            ) { constrainedNotificationState, transitioningToOrFromLockscreen ->
-                constrainedNotificationState || transitioningToOrFromLockscreen
-            }
+            )
             .stateIn(
                 scope = applicationScope,
                 started = SharingStarted.Eagerly,
@@ -250,9 +249,10 @@
     /** If the user is visually on the glanceable hub or transitioning to/from it */
     private val isOnGlanceableHub: Flow<Boolean> =
         combine(
-                keyguardTransitionInteractor.finishedKeyguardState.map { state ->
-                    state == GLANCEABLE_HUB
-                },
+                keyguardTransitionInteractor.isFinishedIn(
+                    scene = Scenes.Communal,
+                    stateWithoutSceneContainer = GLANCEABLE_HUB
+                ),
                 anyOf(
                     keyguardTransitionInteractor.isInTransition(
                         edge = Edge.create(to = Scenes.Communal),
@@ -424,32 +424,19 @@
             .onStart { emit(1f) }
             .dumpWhileCollecting("alphaForShadeAndQsExpansion")
 
-    private fun toFlowArray(
-        states: Set<KeyguardState>,
-        flow: (KeyguardState) -> Flow<Boolean>
-    ): Array<Flow<Boolean>> {
-        return states.map { flow(it) }.toTypedArray()
-    }
-
     private val isTransitioningToHiddenKeyguard: Flow<Boolean> =
         flow {
                 while (currentCoroutineContext().isActive) {
                     emit(false)
                     // Ensure states are inactive to start
-                    allOf(
-                            *toFlowArray(statesForHiddenKeyguard) { state ->
-                                keyguardTransitionInteractor.transitionValue(state).map { it == 0f }
-                            }
-                        )
-                        .first { it }
+                    allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone)).first { it }
                     // Wait for a qualifying transition to begin
                     anyOf(
-                            *toFlowArray(statesForHiddenKeyguard) { state ->
-                                keyguardTransitionInteractor
-                                    .transition(Edge.create(to = state))
-                                    .map { it.value > 0f && it.transitionState == RUNNING }
-                                    .onStart { emit(false) }
-                            }
+                            transitionToIsRunning(Edge.create(to = OCCLUDED)),
+                            transitionToIsRunning(
+                                edge = Edge.create(to = Scenes.Gone),
+                                edgeWithoutSceneContainer = Edge.create(to = GONE)
+                            )
                         )
                         .first { it }
                     emit(true)
@@ -458,13 +445,7 @@
                     // it is considered safe to reset alpha to 1f for HUNs.
                     combine(
                             keyguardInteractor.statusBarState,
-                            allOf(
-                                *toFlowArray(statesForHiddenKeyguard) { state ->
-                                    keyguardTransitionInteractor.transitionValue(state).map {
-                                        it == 0f
-                                    }
-                                }
-                            )
+                            allOf(isNotOnState(OCCLUDED), isNotOnState(GONE, Scenes.Gone))
                         ) { statusBarState, stateIsReversed ->
                             statusBarState == SHADE || stateIsReversed
                         }
@@ -473,6 +454,17 @@
             }
             .dumpWhileCollecting("isTransitioningToHiddenKeyguard")
 
+    private fun isNotOnState(stateWithoutSceneContainer: KeyguardState, scene: SceneKey? = null) =
+        keyguardTransitionInteractor
+            .transitionValue(scene = scene, stateWithoutSceneContainer = stateWithoutSceneContainer)
+            .map { it == 0f }
+
+    private fun transitionToIsRunning(edge: Edge, edgeWithoutSceneContainer: Edge? = null) =
+        keyguardTransitionInteractor
+            .transition(edge = edge, edgeWithoutSceneContainer = edgeWithoutSceneContainer)
+            .map { it.value > 0f && it.transitionState == RUNNING }
+            .onStart { emit(false) }
+
     val panelAlpha = keyguardInteractor.panelAlpha
 
     private fun bouncerToGoneNotificationAlpha(viewState: ViewStateAccessor): Flow<Float> =
@@ -511,7 +503,7 @@
                 occludedToAodTransitionViewModel.lockscreenAlpha,
                 occludedToGoneTransitionViewModel.notificationAlpha(viewState),
                 occludedToLockscreenTransitionViewModel.lockscreenAlpha,
-                primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
+                primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
                 glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
                 lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
index 8d73983..dc15970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ui/viewbinder/HeadsUpNotificationViewBinder.kt
@@ -51,7 +51,9 @@
                             }
                             removed.forEach { key ->
                                 val row = obtainView(key)
-                                parentView.generateHeadsUpAnimation(row, /* isHeadsUp= */ false)
+                                if (!parentView.isBeingDragged()) {
+                                    parentView.generateHeadsUpAnimation(row, /* isHeadsUp= */ false)
+                                }
                                 row.markHeadsUpSeen()
                             }
                         }
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 107bf1e..d4ef42c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -613,8 +613,9 @@
                     super.onTransitionAnimationStart(isExpandingFullyAbove)
                     if (Flags.communalHub()) {
                         communalSceneInteractor.snapToScene(
-                            CommunalScenes.Blank,
-                            ActivityTransitionAnimator.TIMINGS.totalDuration
+                            newScene = CommunalScenes.Blank,
+                            loggingReason = "ActivityStarterInternalImpl",
+                            delayMillis = ActivityTransitionAnimator.TIMINGS.totalDuration
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
deleted file mode 100644
index a538856..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.NightDisplayListener;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.UserSettingObserver;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastDevice;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DataSaverController.Listener;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotController.Callback;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.UserAwareController;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Objects;
-
-import javax.inject.Named;
-
-/**
- * Manages which tiles should be automatically added to QS.
- */
-public class AutoTileManager implements UserAwareController {
-    private static final String TAG = "AutoTileManager";
-
-    public static final String HOTSPOT = "hotspot";
-    public static final String SAVER = "saver";
-    public static final String INVERSION = "inversion";
-    public static final String WORK = "work";
-    public static final String NIGHT = "night";
-    public static final String CAST = "cast";
-    public static final String DEVICE_CONTROLS = "controls";
-    public static final String WALLET = "wallet";
-    public static final String BRIGHTNESS = "reduce_brightness";
-    static final String SETTING_SEPARATOR = ":";
-
-    private UserHandle mCurrentUser;
-    private boolean mInitialized;
-    private final String mSafetySpec;
-
-    protected final Context mContext;
-    protected final QSHost mHost;
-    protected final Handler mHandler;
-    protected final SecureSettings mSecureSettings;
-    protected final AutoAddTracker mAutoTracker;
-    private final HotspotController mHotspotController;
-    private final DataSaverController mDataSaverController;
-    private final ManagedProfileController mManagedProfileController;
-    private final NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
-    private NightDisplayListener mNightDisplayListener;
-    private final CastController mCastController;
-    private final DeviceControlsController mDeviceControlsController;
-    private final WalletController mWalletController;
-    private final ReduceBrightColorsController mReduceBrightColorsController;
-    private final SafetyController mSafetyController;
-    private final boolean mIsReduceBrightColorsAvailable;
-    private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
-
-    public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder,
-            QSHost host,
-            @Background Handler handler,
-            SecureSettings secureSettings,
-            HotspotController hotspotController,
-            DataSaverController dataSaverController,
-            ManagedProfileController managedProfileController,
-            NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
-            CastController castController,
-            ReduceBrightColorsController reduceBrightColorsController,
-            DeviceControlsController deviceControlsController,
-            WalletController walletController,
-            SafetyController safetyController,
-            @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
-        mContext = context;
-        mHost = host;
-        mSecureSettings = secureSettings;
-        mCurrentUser = mHost.getUserContext().getUser();
-        mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build();
-        mHandler = handler;
-        mHotspotController = hotspotController;
-        mDataSaverController = dataSaverController;
-        mManagedProfileController = managedProfileController;
-        mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
-        mCastController = castController;
-        mReduceBrightColorsController = reduceBrightColorsController;
-        mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
-        mDeviceControlsController = deviceControlsController;
-        mWalletController = walletController;
-        mSafetyController = safetyController;
-        String safetySpecClass;
-        try {
-            safetySpecClass =
-                    context.getResources().getString(R.string.safety_quick_settings_tile_class);
-            if (safetySpecClass.length() == 0) {
-                safetySpecClass = null;
-            }
-        } catch (Resources.NotFoundException | NullPointerException e) {
-            safetySpecClass = null;
-        }
-        mSafetySpec = safetySpecClass != null ? CustomTile.toSpec(new ComponentName(mContext
-                .getPackageManager().getPermissionControllerPackageName(), safetySpecClass)) : null;
-    }
-
-    /**
-     * Init method must be called after construction to start listening
-     */
-    public void init() {
-        QSPipelineFlagsRepository.Utils.assertInLegacyMode();
-        if (mInitialized) {
-            Log.w(TAG, "Trying to re-initialize");
-            return;
-        }
-        mAutoTracker.initialize();
-        populateSettingsList();
-        startControllersAndSettingsListeners();
-        mInitialized = true;
-    }
-
-    protected void startControllersAndSettingsListeners() {
-        if (!mAutoTracker.isAdded(HOTSPOT)) {
-            mHotspotController.addCallback(mHotspotCallback);
-        }
-        if (!mAutoTracker.isAdded(SAVER)) {
-            mDataSaverController.addCallback(mDataSaverListener);
-        }
-        mManagedProfileController.addCallback(mProfileCallback);
-
-        mNightDisplayListener = mNightDisplayListenerBuilder
-                .setUser(mCurrentUser.getIdentifier())
-                .build();
-        if (!mAutoTracker.isAdded(NIGHT)
-                && ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            mNightDisplayListener.setCallback(mNightDisplayCallback);
-        }
-        if (!mAutoTracker.isAdded(CAST)) {
-            mCastController.addCallback(mCastCallback);
-        }
-        if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
-            mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
-        }
-        // We always want this callback, because if the feature stops being supported,
-        // we want to remove the tile from AutoAddTracker. That way it will be re-added when the
-        // feature is reenabled (similar to work tile).
-        mDeviceControlsController.setCallback(mDeviceControlsCallback);
-        if (!mAutoTracker.isAdded(WALLET)) {
-            initWalletController();
-        }
-        if (mSafetySpec != null) {
-            if (!mAutoTracker.isAdded(mSafetySpec)) {
-                initSafetyTile();
-            }
-            mSafetyController.addCallback(mSafetyCallback);
-        }
-
-        int settingsN = mAutoAddSettingList.size();
-        for (int i = 0; i < settingsN; i++) {
-            if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) {
-                mAutoAddSettingList.get(i).setListening(true);
-            }
-        }
-    }
-
-    protected void stopListening() {
-        mHotspotController.removeCallback(mHotspotCallback);
-        mDataSaverController.removeCallback(mDataSaverListener);
-        mManagedProfileController.removeCallback(mProfileCallback);
-        if (ColorDisplayManager.isNightDisplayAvailable(mContext)
-                && mNightDisplayListener != null) {
-            mNightDisplayListener.setCallback(null);
-        }
-        if (mIsReduceBrightColorsAvailable) {
-            mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
-        }
-        mCastController.removeCallback(mCastCallback);
-        mDeviceControlsController.removeCallback();
-        if (mSafetySpec != null) {
-            mSafetyController.removeCallback(mSafetyCallback);
-        }
-        int settingsN = mAutoAddSettingList.size();
-        for (int i = 0; i < settingsN; i++) {
-            mAutoAddSettingList.get(i).setListening(false);
-        }
-    }
-
-    public void destroy() {
-        stopListening();
-        mAutoTracker.destroy();
-    }
-
-    /**
-     * Populates a list with the pairs setting:spec in the config resource.
-     * <p>
-     * This will only create {@link AutoAddSetting} objects for those tiles that have not been
-     * auto-added before, and set the corresponding {@link ContentObserver} to listening.
-     */
-    private void populateSettingsList() {
-        String [] autoAddList;
-        try {
-            autoAddList = mContext.getResources().getStringArray(
-                    R.array.config_quickSettingsAutoAdd);
-        } catch (Resources.NotFoundException e) {
-            Log.w(TAG, "Missing config resource");
-            return;
-        }
-        // getStringArray returns @NotNull, so if we got here, autoAddList is not null
-        for (String tile : autoAddList) {
-            String[] split = tile.split(SETTING_SEPARATOR);
-            if (split.length == 2) {
-                String setting = split[0];
-                String spec = split[1];
-                // Populate all the settings. As they may not have been added in other users
-                AutoAddSetting s = new AutoAddSetting(
-                        mSecureSettings, mHandler, setting, mCurrentUser.getIdentifier(), spec);
-                mAutoAddSettingList.add(s);
-            } else {
-                Log.w(TAG, "Malformed item in array: " + tile);
-            }
-        }
-    }
-
-    /*
-     * This will be sent off the main thread if needed
-     */
-    @Override
-    public void changeUser(UserHandle newUser) {
-        if (!mInitialized) {
-            throw new IllegalStateException("AutoTileManager not initialized");
-        }
-        if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) {
-            mHandler.post(() -> changeUser(newUser));
-            return;
-        }
-        if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) {
-            return;
-        }
-        stopListening();
-        mCurrentUser = newUser;
-        int settingsN = mAutoAddSettingList.size();
-        for (int i = 0; i < settingsN; i++) {
-            mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier());
-        }
-        mAutoTracker.changeUser(newUser);
-        startControllersAndSettingsListeners();
-    }
-
-    @Override
-    public int getCurrentUserId() {
-        return mCurrentUser.getIdentifier();
-    }
-
-    private final ManagedProfileController.Callback mProfileCallback =
-            new ManagedProfileController.Callback() {
-                @Override
-                public void onManagedProfileChanged() {
-                    if (mManagedProfileController.hasActiveProfile()) {
-                        if (mAutoTracker.isAdded(WORK)) return;
-                        final int position = mAutoTracker.getRestoredTilePosition(WORK);
-                        mHost.addTile(WORK, position);
-                        mAutoTracker.setTileAdded(WORK);
-                    } else {
-                        if (!mAutoTracker.isAdded(WORK)) return;
-                        mHost.removeTile(WORK);
-                        mAutoTracker.setTileRemoved(WORK);
-                    }
-                }
-
-                @Override
-                public void onManagedProfileRemoved() {
-                }
-            };
-
-    private final DataSaverController.Listener mDataSaverListener = new Listener() {
-        @Override
-        public void onDataSaverChanged(boolean isDataSaving) {
-            if (mAutoTracker.isAdded(SAVER)) return;
-            if (isDataSaving) {
-                mHost.addTile(SAVER);
-                mAutoTracker.setTileAdded(SAVER);
-                mHandler.post(() -> mDataSaverController.removeCallback(mDataSaverListener));
-            }
-        }
-    };
-
-    private final HotspotController.Callback mHotspotCallback = new Callback() {
-        @Override
-        public void onHotspotChanged(boolean enabled, int numDevices) {
-            if (mAutoTracker.isAdded(HOTSPOT)) return;
-            if (enabled) {
-                mHost.addTile(HOTSPOT);
-                mAutoTracker.setTileAdded(HOTSPOT);
-                mHandler.post(() -> mHotspotController.removeCallback(mHotspotCallback));
-            }
-        }
-    };
-
-    private final DeviceControlsController.Callback mDeviceControlsCallback =
-            new DeviceControlsController.Callback() {
-        @Override
-        public void onControlsUpdate(@Nullable Integer position) {
-            if (mAutoTracker.isAdded(DEVICE_CONTROLS)) return;
-            if (position != null && !hasTile(DEVICE_CONTROLS)) {
-                mHost.addTile(DEVICE_CONTROLS, position);
-                mAutoTracker.setTileAdded(DEVICE_CONTROLS);
-            }
-            mHandler.post(() -> mDeviceControlsController.removeCallback());
-        }
-
-        @Override
-        public void removeControlsAutoTracker() {
-            mAutoTracker.setTileRemoved(DEVICE_CONTROLS);
-        }
-    };
-
-    private boolean hasTile(String tileSpec) {
-        if (tileSpec == null) return false;
-        Collection<QSTile> tiles = mHost.getTiles();
-        for (QSTile tile : tiles) {
-            if (tileSpec.equals(tile.getTileSpec())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void initWalletController() {
-        if (mAutoTracker.isAdded(WALLET)) return;
-        Integer position = mWalletController.getWalletPosition();
-
-        if (position != null) {
-            mHost.addTile(WALLET, position);
-            mAutoTracker.setTileAdded(WALLET);
-        }
-    }
-
-    private void initSafetyTile() {
-        if (mSafetySpec == null || mAutoTracker.isAdded(mSafetySpec)) {
-            return;
-        }
-        mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true);
-        mAutoTracker.setTileAdded(mSafetySpec);
-    }
-
-    @VisibleForTesting
-    final NightDisplayListener.Callback mNightDisplayCallback =
-            new NightDisplayListener.Callback() {
-        @Override
-        public void onActivated(boolean activated) {
-            if (activated) {
-                addNightTile();
-            }
-        }
-
-        @Override
-        public void onAutoModeChanged(int autoMode) {
-            if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME
-                    || autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) {
-                addNightTile();
-            }
-        }
-
-        private void addNightTile() {
-            if (mAutoTracker.isAdded(NIGHT)) return;
-            mHost.addTile(NIGHT);
-            mAutoTracker.setTileAdded(NIGHT);
-            mHandler.post(() -> mNightDisplayListener.setCallback(null));
-        }
-    };
-
-    @VisibleForTesting
-    final ReduceBrightColorsController.Listener mReduceBrightColorsCallback =
-            new ReduceBrightColorsController.Listener() {
-                @Override
-                public void onActivated(boolean activated) {
-                    if (activated) {
-                        addReduceBrightColorsTile();
-                    }
-                }
-
-                @Override
-                public void onFeatureEnabledChanged(boolean enabled) {
-                    if (!enabled) {
-                        mHost.removeTile(BRIGHTNESS);
-                        mHandler.post(() -> mReduceBrightColorsController.removeCallback(this));
-                    }
-                }
-
-                private void addReduceBrightColorsTile() {
-                    if (mAutoTracker.isAdded(BRIGHTNESS)) return;
-                    mHost.addTile(BRIGHTNESS);
-                    mAutoTracker.setTileAdded(BRIGHTNESS);
-                    mHandler.post(() -> mReduceBrightColorsController.removeCallback(this));
-                }
-            };
-
-    @VisibleForTesting
-    final CastController.Callback mCastCallback = new CastController.Callback() {
-        @Override
-        public void onCastDevicesChanged() {
-            if (mAutoTracker.isAdded(CAST)) return;
-
-            boolean isCasting = false;
-            for (CastDevice device : mCastController.getCastDevices()) {
-                if (device.isCasting()) {
-                    isCasting = true;
-                    break;
-                }
-            }
-
-            if (isCasting) {
-                mHost.addTile(CAST);
-                mAutoTracker.setTileAdded(CAST);
-                mHandler.post(() -> mCastController.removeCallback(mCastCallback));
-            }
-        }
-    };
-
-    @VisibleForTesting
-    final SafetyController.Listener mSafetyCallback = new SafetyController.Listener() {
-        @Override
-        public void onSafetyCenterEnableChanged(boolean isSafetyCenterEnabled) {
-            if (mSafetySpec == null) {
-                return;
-            }
-
-            if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
-                initSafetyTile();
-            } else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
-                mHost.removeTile(mSafetySpec);
-                mAutoTracker.setTileRemoved(mSafetySpec);
-            }
-        }
-    };
-
-    @VisibleForTesting
-    protected UserSettingObserver getSecureSettingForKey(String key) {
-        for (UserSettingObserver s : mAutoAddSettingList) {
-            if (Objects.equals(key, s.getKey())) {
-                return s;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Tracks tiles that should be auto added when a setting changes.
-     * <p>
-     * When the setting changes to a value different from 0, if the tile has not been auto added
-     * before, it will be added and the listener will be stopped.
-     */
-    private class AutoAddSetting extends UserSettingObserver {
-        private final String mSpec;
-
-        AutoAddSetting(
-                SecureSettings secureSettings,
-                Handler handler,
-                String setting,
-                int userId,
-                String tileSpec
-        ) {
-            super(secureSettings, handler, setting, userId);
-            mSpec = tileSpec;
-        }
-
-        @Override
-        protected void handleValueChanged(int value, boolean observedChange) {
-            if (mAutoTracker.isAdded(mSpec)) {
-                // This should not be listening anymore
-                mHandler.post(() -> setListening(false));
-                return;
-            }
-            if (value != 0) {
-                if (mSpec.startsWith(CustomTile.PREFIX)) {
-                    mHost.addTile(CustomTile.getComponentFromSpec(mSpec), /* end */ true);
-                } else {
-                    mHost.addTile(mSpec);
-                }
-                mAutoTracker.setTileAdded(mSpec);
-                mHandler.post(() -> setListening(false));
-            }
-        }
-    }
-}
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 db8cd57..7227b93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -30,6 +30,8 @@
 import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
 import static com.android.systemui.Flags.lightRevealMigration;
 import static com.android.systemui.Flags.newAodTransition;
+import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
+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;
@@ -90,6 +92,7 @@
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleRegistry;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.MetricsLogger;
@@ -159,6 +162,8 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.qs.QSFragmentLegacy;
 import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.qs.composefragment.QSFragmentCompose;
+import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -593,6 +598,8 @@
 
     private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
 
+    private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+
     /**
      * Public constructor for CentralSurfaces.
      *
@@ -705,7 +712,8 @@
             ActivityStarter activityStarter,
             BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
             GlanceableHubContainerController glanceableHubContainerController,
-            EmergencyGestureIntentFactory emergencyGestureIntentFactory
+            EmergencyGestureIntentFactory emergencyGestureIntentFactory,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager
     ) {
         mContext = context;
         mNotificationsController = notificationsController;
@@ -839,6 +847,8 @@
         mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
         mLightRevealScrim = lightRevealScrim;
 
+        mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+
         if (PredictiveBackSysUiFlag.isEnabled()) {
             mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
         }
@@ -861,7 +871,10 @@
         mBubblesOptional.ifPresent(this::initBubbles);
         mKeyguardBypassController.listenForQsExpandedChange();
 
-        mStatusBarSignalPolicy.init();
+        if (!statusBarSignalPolicyRefactor()) {
+            mStatusBarSignalPolicy.init();
+        }
+
         mKeyguardIndicationController.init();
 
         mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
@@ -1431,9 +1444,15 @@
     }
 
     protected QS createDefaultQSFragment() {
+        Class<? extends QS> klass;
+        if (QSComposeFragment.isEnabled()) {
+            klass = QSFragmentCompose.class;
+        } else {
+            klass = QSFragmentLegacy.class;
+        }
         return mFragmentService
                 .getFragmentHostManager(getNotificationShadeWindowView())
-                .create(QSFragmentLegacy.class);
+                .create(klass);
     }
 
     private void setUpPresenter() {
@@ -1674,7 +1693,7 @@
                         mNotificationShadeWindowController.setRequestTopUi(false, TAG);
                     }
                 }, /* isDozing= */ false, RippleShape.CIRCLE,
-                sUiEventLogger).show(animationDelay);
+                sUiEventLogger, mViewCaptureAwareWindowManager).show(animationDelay);
     }
 
     @Override
@@ -2350,10 +2369,21 @@
                 // lock screen where users can use the UDFPS affordance to enter the device
                 mStatusBarKeyguardViewManager.reset(true);
             } else if (mState == StatusBarState.KEYGUARD
-                    && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
-                    && mStatusBarKeyguardViewManager.isSecure()) {
-                Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
-                mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+                    && !mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()) {
+                boolean needsBouncer = mStatusBarKeyguardViewManager.isSecure();
+                if (relockWithPowerButtonImmediately()) {
+                    // Only request if SIM bouncer is needed
+                    needsBouncer = mStatusBarKeyguardViewManager.needsFullscreenBouncer();
+                }
+
+                if (needsBouncer) {
+                    Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
+                    if (SceneContainerFlag.isEnabled()) {
+                        mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+                    } else {
+                        mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+                    }
+                }
             }
         }
     }
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 2e1ab38..bb5aa23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -20,16 +20,17 @@
 import android.graphics.Rect
 import android.os.LocaleList
 import android.view.View.LAYOUT_DIRECTION_RTL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
-@SysUISingleton
-class ConfigurationControllerImpl @Inject constructor(
-        @Application context: Context,
-        ) : ConfigurationController {
+class ConfigurationControllerImpl
+@AssistedInject
+constructor(
+    @Assisted private val context: Context,
+) : ConfigurationController {
 
     private val listeners: MutableList<ConfigurationListener> = ArrayList()
     private val lastConfig = Configuration()
@@ -40,18 +41,17 @@
     private val inCarMode: Boolean
     private var uiMode: Int = 0
     private var localeList: LocaleList? = null
-    private val context: Context
     private var layoutDirection: Int
     private var orientation = Configuration.ORIENTATION_UNDEFINED
 
     init {
         val currentConfig = context.resources.configuration
-        this.context = context
         fontScale = currentConfig.fontScale
         density = currentConfig.densityDpi
         smallestScreenWidth = currentConfig.smallestScreenWidthDp
         maxBounds.set(currentConfig.windowConfiguration.maxBounds)
-        inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
+        inCarMode =
+            currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
                 Configuration.UI_MODE_TYPE_CAR
         uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
         localeList = currentConfig.locales
@@ -60,29 +60,20 @@
 
     override fun notifyThemeChanged() {
         // Avoid concurrent modification exception
-        val listeners = synchronized(this.listeners) {
-           ArrayList(this.listeners)
-        }
+        val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
 
-        listeners.filterForEach({ this.listeners.contains(it) }) {
-            it.onThemeChanged()
-        }
+        listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
     }
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         // Avoid concurrent modification exception
-        val listeners = synchronized(this.listeners) {
-           ArrayList(this.listeners)
-        }
-        listeners.filterForEach({ this.listeners.contains(it) }) {
-            it.onConfigChanged(newConfig)
-        }
+        val listeners = synchronized(this.listeners) { ArrayList(this.listeners) }
+        listeners.filterForEach({ this.listeners.contains(it) }) { it.onConfigChanged(newConfig) }
         val fontScale = newConfig.fontScale
         val density = newConfig.densityDpi
         val uiMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
         val uiModeChanged = uiMode != this.uiMode
-        if (density != this.density || fontScale != this.fontScale ||
-                inCarMode && uiModeChanged) {
+        if (density != this.density || fontScale != this.fontScale || inCarMode && uiModeChanged) {
             listeners.filterForEach({ this.listeners.contains(it) }) {
                 it.onDensityOrFontScaleChanged()
             }
@@ -105,17 +96,13 @@
             // would be a direct reference to windowConfiguration.maxBounds, so the if statement
             // above would always fail. See b/245799099 for more information.
             this.maxBounds.set(maxBounds)
-            listeners.filterForEach({ this.listeners.contains(it) }) {
-                it.onMaxBoundsChanged()
-            }
+            listeners.filterForEach({ this.listeners.contains(it) }) { it.onMaxBoundsChanged() }
         }
 
         val localeList = newConfig.locales
         if (localeList != this.localeList) {
             this.localeList = localeList
-            listeners.filterForEach({ this.listeners.contains(it) }) {
-                it.onLocaleListChanged()
-            }
+            listeners.filterForEach({ this.listeners.contains(it) }) { it.onLocaleListChanged() }
         }
 
         if (uiModeChanged) {
@@ -124,9 +111,7 @@
             context.theme.applyStyle(context.themeResId, true)
 
             this.uiMode = uiMode
-            listeners.filterForEach({ this.listeners.contains(it) }) {
-                it.onUiModeChanged()
-            }
+            listeners.filterForEach({ this.listeners.contains(it) }) { it.onUiModeChanged() }
         }
 
         if (layoutDirection != newConfig.layoutDirection) {
@@ -137,9 +122,7 @@
         }
 
         if (lastConfig.updateFrom(newConfig) and ActivityInfo.CONFIG_ASSETS_PATHS != 0) {
-            listeners.filterForEach({ this.listeners.contains(it) }) {
-                it.onThemeChanged()
-            }
+            listeners.filterForEach({ this.listeners.contains(it) }) { it.onThemeChanged() }
         }
 
         val newOrientation = newConfig.orientation
@@ -152,16 +135,12 @@
     }
 
     override fun addCallback(listener: ConfigurationListener) {
-        synchronized(listeners) {
-            listeners.add(listener)
-        }
+        synchronized(listeners) { listeners.add(listener) }
         listener.onDensityOrFontScaleChanged()
     }
 
     override fun removeCallback(listener: ConfigurationListener) {
-        synchronized(listeners) {
-            listeners.remove(listener)
-        }
+        synchronized(listeners) { listeners.remove(listener) }
     }
 
     override fun isLayoutRtl(): Boolean {
@@ -176,6 +155,15 @@
             else -> "err"
         }
     }
+
+    @AssistedFactory
+    interface Factory {
+        /**
+         * Creates a [ConfigurationController] that uses [context] to resolve the current
+         * configuration and resources.
+         */
+        fun create(context: Context): ConfigurationControllerImpl
+    }
 }
 
 // This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
index 90ebaf2..8f4279e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerStartable.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.phone
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.common.ui.GlobalConfig
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -26,7 +27,7 @@
 class ConfigurationControllerStartable
 @Inject
 constructor(
-    private val configurationController: ConfigurationController,
+    @GlobalConfig private val configurationController: ConfigurationController,
     private val listeners: Set<@JvmSuppressWildcards ConfigurationListener>
 ) : CoreStartable {
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 0067316..f649418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -25,6 +25,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 
 import javax.inject.Inject;
 
@@ -124,6 +125,11 @@
 
         // Begin pulse. Note that it's very important that the pulse finished callback
         // be invoked when we're done so that the caller can drop the pulse wakelock.
+        if (SceneContainerFlag.isEnabled()) {
+            // ScrimController.Callback#onDisplayBlanked is no longer triggered when flexiglass is
+            // on, but we still need to signal that pulsing has started.
+            callback.onPulseStarted();
+        }
         mPulseCallback = callback;
         mPulseReason = reason;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index f13a593..ec92990 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -193,7 +193,11 @@
     void fireNotificationPulse(NotificationEntry entry) {
         Runnable pulseSuppressedListener = () -> {
             mHeadsUpManager.removeNotification(
-                    entry.getKey(), /* releaseImmediately= */ true, /* animate= */ false);
+                    entry.getKey(),
+                    /* releaseImmediately= */ true,
+                    /* animate= */ false,
+                    "fireNotificationPulse"
+            );
         };
         Assert.isMainThread();
         for (Callback callback : mCallbacks) {
@@ -424,14 +428,12 @@
 
     @Override
     public void setDozeScreenBrightness(int brightness) {
-        mDozeLog.traceDozeScreenBrightness(brightness);
         mNotificationShadeWindowController.setDozeScreenBrightness(brightness);
     }
 
 
     @Override
     public void setDozeScreenBrightnessFloat(float brightness) {
-        mDozeLog.traceDozeScreenBrightnessFloat(brightness);
         mNotificationShadeWindowController.setDozeScreenBrightnessFloat(brightness);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 25d9cc7..1efad3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -35,6 +35,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.domain.interactor.ShadeInteractor;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -46,7 +47,6 @@
 import com.android.systemui.statusbar.notification.data.repository.HeadsUpRowRepository;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.AnimationStateHandler;
 import com.android.systemui.statusbar.policy.AvalancheController;
@@ -251,6 +251,12 @@
         return entry != null && mSystemClock.elapsedRealtime() < entry.mPostTime;
     }
 
+    @Override
+    public void releaseAfterExpansion() {
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+        onExpandingFinished();
+    }
+
     public void onExpandingFinished() {
         if (mReleaseOnExpandFinish) {
             releaseAllImmediately();
@@ -277,7 +283,7 @@
     private void onShadeOrQsExpanded(Boolean isExpanded) {
         if (isExpanded != mIsExpanded) {
             mIsExpanded = isExpanded;
-            if (!NotificationsHeadsUpRefactor.isEnabled() && isExpanded) {
+            if (!SceneContainerFlag.isEnabled() && isExpanded) {
                 mHeadsUpAnimatingAway.setValue(false);
             }
         }
@@ -297,6 +303,11 @@
         }
     }
 
+    @Override
+    public void unpinAll(boolean userUnPinned) {
+        super.unpinAll(userUnPinned);
+    }
+
     /**
      * Notifies that a remote input textbox in notification gets active or inactive.
      *
@@ -365,12 +376,14 @@
 
     @Override
     public boolean removeNotification(@NonNull String key, boolean releaseImmediately,
-            boolean animate) {
+            boolean animate, @NonNull String reason) {
         if (animate) {
-            return removeNotification(key, releaseImmediately);
+            return removeNotification(key, releaseImmediately,
+                    "removeNotification(animate: true), reason: " + reason);
         } else {
             mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
-            boolean removed = removeNotification(key, releaseImmediately);
+            final boolean removed = removeNotification(key, releaseImmediately,
+                    "removeNotification(animate: false), reason: " + reason);
             mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(true);
             return removed;
         }
@@ -503,7 +516,7 @@
 
     @Nullable
     private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
-        if (NotificationsHeadsUpRefactor.isEnabled()) {
+        if (SceneContainerFlag.isEnabled()) {
             return (HeadsUpEntryPhone) mTopHeadsUpRow.getValue();
         } else {
             return (HeadsUpEntryPhone) getTopHeadsUpEntry();
@@ -622,6 +635,7 @@
                             mOnReorderingAllowedListener);
                 } else if (mTrackingHeadsUp) {
                     mEntriesToRemoveAfterExpand.add(entry);
+                    mLogger.logRemoveEntryAfterExpand(entry);
                 } else if (mVisualStabilityProvider.isReorderingAllowed()
                         || entry.showingPulsing()) {
                     removeEntry(entry.getKey(), "createRemoveRunnable");
@@ -696,7 +710,7 @@
         }
 
         private NotificationEntry requireEntry() {
-            /* check if */ NotificationsHeadsUpRefactor.isUnexpectedlyInLegacyMode();
+            /* check if */ SceneContainerFlag.isUnexpectedlyInLegacyMode();
             return Objects.requireNonNull(mEntry);
         }
     }
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 c3da7fc..178c318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -435,13 +435,14 @@
 
     /** Should only be called from {@link KeyguardStatusBarViewController}. */
     void onOverlayChanged() {
-        int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
-        mCarrierLabel.setTextAppearance(theme);
+        final int carrierTheme = R.style.TextAppearance_StatusBar_Clock;
+        mCarrierLabel.setTextAppearance(carrierTheme);
         mBatteryView.updatePercentView();
 
+        final int userSwitcherTheme = R.style.TextAppearance_StatusBar_UserChip;
         TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
         if (userSwitcherName != null) {
-            userSwitcherName.setTextAppearance(theme);
+            userSwitcherName.setTextAppearance(userSwitcherTheme);
         }
     }
 
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 1a47081..4604233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -333,10 +333,6 @@
             }
             if (intent.isActivity) {
                 assistManagerLazy.get().hideAssist()
-                // This activity could have started while the device is dreaming, in which case
-                // the dream would occlude the activity. In order to show the newly started
-                // activity, we wake from the dream.
-                keyguardUpdateMonitor.awakenFromDream()
             }
             intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
index 56ea00c..7ef1e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImpl.java
@@ -22,6 +22,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
@@ -43,17 +44,20 @@
     private final UserManager mUserManager;
     private final UserTracker mUserTracker;
     private final LinkedList<UserInfo> mProfiles;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private boolean mListening;
     private int mCurrentUser;
 
     @Inject
     public ManagedProfileControllerImpl(Context context, @Main Executor mainExecutor,
-            UserTracker userTracker, UserManager userManager) {
+            UserTracker userTracker, UserManager userManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mMainExecutor = mainExecutor;
         mUserManager = userManager;
         mUserTracker = userTracker;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mProfiles = new LinkedList<>();
     }
 
@@ -80,6 +84,7 @@
                     StatusBarManager statusBarManager = (StatusBarManager) mContext
                             .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
                     statusBarManager.collapsePanels();
+                    mKeyguardUpdateMonitor.awakenFromDream();
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 3ba62b1..ba39c3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -44,6 +44,7 @@
 
 import androidx.lifecycle.Observer;
 
+import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Flags;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -78,6 +79,8 @@
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor;
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo;
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.time.DateFormatUtil;
@@ -99,7 +102,6 @@
                 CommandQueue.Callbacks,
                 RotationLockControllerCallback,
                 Listener,
-                ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardStateController.Callback,
                 PrivacyItemController.Callback,
@@ -161,6 +163,7 @@
     private final RecordingController mRecordingController;
     private final RingerModeTracker mRingerModeTracker;
     private final PrivacyLogger mPrivacyLogger;
+    private final ZenModeInteractor mZenModeInteractor;
 
     private boolean mZenVisible;
     private boolean mVibrateVisible;
@@ -193,6 +196,7 @@
             PrivacyItemController privacyItemController,
             PrivacyLogger privacyLogger,
             ConnectedDisplayInteractor connectedDisplayInteractor,
+            ZenModeInteractor zenModeInteractor,
             JavaAdapter javaAdapter
     ) {
         mIconController = iconController;
@@ -224,6 +228,7 @@
         mTelecomManager = telecomManager;
         mRingerModeTracker = ringerModeTracker;
         mPrivacyLogger = privacyLogger;
+        mZenModeInteractor = zenModeInteractor;
         mJavaAdapter = javaAdapter;
 
         mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
@@ -355,7 +360,13 @@
         mBluetooth.addCallback(this);
         mProvisionedController.addCallback(this);
         mCurrentUserSetup = mProvisionedController.isCurrentUserSetup();
-        mZenController.addCallback(this);
+        if (usesModeIcons()) {
+            // Note that we're not fully replacing ZenModeController with ZenModeInteractor, so
+            // we listen for the extra event here but still add the ZMC callback.
+            mJavaAdapter.alwaysCollectFlow(mZenModeInteractor.getMainActiveMode(),
+                    this::onMainActiveModeChanged);
+        }
+        mZenController.addCallback(mZenControllerCallback);
         if (!Flags.statusBarScreenSharingChips()) {
             // If the flag is enabled, the cast icon is handled in the new screen sharing chips
             // instead of here so we don't need to listen for events here.
@@ -385,15 +396,43 @@
                 () -> mResources.getString(R.string.accessibility_managed_profile));
     }
 
-    @Override
-    public void onZenChanged(int zen) {
-        updateVolumeZen();
+    private void onMainActiveModeChanged(@Nullable ZenModeInfo mainActiveMode) {
+        if (!usesModeIcons()) {
+            Log.wtf(TAG, "onMainActiveModeChanged shouldn't run if MODES_UI_ICONS is disabled");
+            return;
+        }
+
+        boolean visible = mainActiveMode != null;
+        if (visible) {
+            // Shape=FIXED_SPACE because mode icons can be from 3P packages and may not be square;
+            // we don't want to allow apps to set incredibly wide icons and take up too much space
+            // in the status bar.
+            mIconController.setResourceIcon(mSlotZen,
+                    mainActiveMode.getIcon().key().resPackage(),
+                    mainActiveMode.getIcon().key().resId(),
+                    mainActiveMode.getIcon().drawable(),
+                    mainActiveMode.getName(),
+                    StatusBarIcon.Shape.FIXED_SPACE);
+        }
+        if (visible != mZenVisible) {
+            mIconController.setIconVisibility(mSlotZen, visible);
+            mZenVisible = visible;
+        }
     }
 
-    @Override
-    public void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {
-        updateVolumeZen();
-    }
+    // TODO: b/308591859 - Should be removed and use the ZenModeInteractor only.
+    private final ZenModeController.Callback mZenControllerCallback =
+            new ZenModeController.Callback() {
+                @Override
+                public void onZenChanged(int zen) {
+                    updateVolumeZen();
+                }
+
+                @Override
+                public void onConsolidatedPolicyChanged(NotificationManager.Policy policy) {
+                    updateVolumeZen();
+                }
+            };
 
     private void updateAlarm() {
         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(mUserTracker.getUserId());
@@ -417,15 +456,24 @@
         return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
     }
 
-    private final void updateVolumeZen() {
+    private void updateVolumeZen() {
+        int zen = mZenController.getZen();
+        if (!usesModeIcons()) {
+            updateZenIcon(zen);
+        }
+        updateRingerAndAlarmIcons(zen);
+    }
+
+    private void updateZenIcon(int zen) {
+        if (usesModeIcons()) {
+            Log.wtf(TAG, "updateZenIcon shouldn't be called if MODES_UI_ICONS is enabled");
+            return;
+        }
+
         boolean zenVisible = false;
         int zenIconId = 0;
         String zenDescription = null;
 
-        boolean vibrateVisible = false;
-        boolean muteVisible = false;
-        int zen = mZenController.getZen();
-
         if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) {
             zenVisible = zen != Global.ZEN_MODE_OFF;
             zenIconId = R.drawable.stat_sys_dnd;
@@ -440,7 +488,21 @@
             zenDescription = mResources.getString(R.string.interruption_level_priority);
         }
 
-        if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
+        if (zenVisible) {
+            mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
+        }
+        if (zenVisible != mZenVisible) {
+            mIconController.setIconVisibility(mSlotZen, zenVisible);
+            mZenVisible = zenVisible;
+        }
+    }
+
+    private void updateRingerAndAlarmIcons(int zen) {
+        boolean vibrateVisible = false;
+        boolean muteVisible = false;
+
+        NotificationManager.Policy consolidatedPolicy = mZenController.getConsolidatedPolicy();
+        if (!ZenModeConfig.isZenOverridingRinger(zen, consolidatedPolicy)) {
             final Integer ringerModeInternal =
                     mRingerModeTracker.getRingerModeInternal().getValue();
             if (ringerModeInternal != null) {
@@ -452,14 +514,6 @@
             }
         }
 
-        if (zenVisible) {
-            mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
-        }
-        if (zenVisible != mZenVisible) {
-            mIconController.setIconVisibility(mSlotZen, zenVisible);
-            mZenVisible = zenVisible;
-        }
-
         if (vibrateVisible != mVibrateVisible) {
             mIconController.setIconVisibility(mSlotVibrate, vibrateVisible);
             mVibrateVisible = vibrateVisible;
@@ -888,4 +942,9 @@
 
         mIconController.setIconVisibility(mSlotConnectedDisplay, visible);
     }
+
+    private static boolean usesModeIcons() {
+        return android.app.Flags.modesApi() && android.app.Flags.modesUi()
+                && android.app.Flags.modesUiIcons();
+    }
 }
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 8115c36..d6716a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-
-
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -34,14 +32,14 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Flags;
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
-import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
 import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder;
 import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel;
@@ -51,11 +49,8 @@
 
 public class PhoneStatusBarView extends FrameLayout {
     private static final String TAG = "PhoneStatusBarView";
-    private final StatusBarContentInsetsProvider mContentInsetsProvider;
     private final StatusBarWindowController mStatusBarWindowController;
 
-    private DarkReceiver mBattery;
-    private Clock mClock;
     private int mRotationOrientation = -1;
     @Nullable
     private View mCutoutSpace;
@@ -66,6 +61,10 @@
     private int mStatusBarHeight;
     @Nullable
     private Gefingerpoken mTouchEventHandler;
+    @Nullable
+    private HasCornerCutoutFetcher mHasCornerCutoutFetcher;
+    @Nullable
+    private InsetsFetcher mInsetsFetcher;
     private int mDensity;
     private float mFontScale;
 
@@ -76,7 +75,6 @@
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
         mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
     }
 
@@ -84,6 +82,16 @@
         mTouchEventHandler = handler;
     }
 
+    void setHasCornerCutoutFetcher(@NonNull HasCornerCutoutFetcher cornerCutoutFetcher) {
+        mHasCornerCutoutFetcher = cornerCutoutFetcher;
+        updateCutoutLocation();
+    }
+
+    void setInsetsFetcher(@NonNull InsetsFetcher insetsFetcher) {
+        mInsetsFetcher = insetsFetcher;
+        updateSafeInsets();
+    }
+
     void init(StatusBarUserChipViewModel viewModel) {
         StatusBarUserSwitcherContainer container = findViewById(R.id.user_switcher_container);
         StatusBarUserChipViewBinder.bind(container, viewModel);
@@ -92,8 +100,6 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mBattery = findViewById(R.id.battery);
-        mClock = findViewById(R.id.clock);
         mCutoutSpace = findViewById(R.id.cutout_space_view);
 
         updateResources();
@@ -102,9 +108,6 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        // Always have Battery meters in the status bar observe the dark/light modes.
-        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
-        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mClock);
         if (updateDisplayParameters()) {
             updateLayoutForCutout();
             updateWindowHeight();
@@ -114,8 +117,6 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
-        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mClock);
         mDisplayCutout = null;
     }
 
@@ -135,10 +136,6 @@
         updateWindowHeight();
     }
 
-    void onDensityOrFontScaleChanged() {
-        mClock.onDensityOrFontScaleChanged();
-    }
-
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         if (updateDisplayParameters()) {
@@ -217,8 +214,12 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        mTouchEventHandler.onInterceptTouchEvent(event);
-        return super.onInterceptTouchEvent(event);
+        if (Flags.statusBarSwipeOverChip()) {
+            return mTouchEventHandler.onInterceptTouchEvent(event);
+        } else {
+            mTouchEventHandler.onInterceptTouchEvent(event);
+            return super.onInterceptTouchEvent(event);
+        }
     }
 
     public void updateResources() {
@@ -283,7 +284,14 @@
             return;
         }
 
-        boolean hasCornerCutout = mContentInsetsProvider.currentRotationHasCornerCutout();
+        boolean hasCornerCutout;
+        if (mHasCornerCutoutFetcher != null) {
+            hasCornerCutout = mHasCornerCutoutFetcher.fetchHasCornerCutout();
+        } else {
+            Log.e(TAG, "mHasCornerCutoutFetcher unexpectedly null");
+            hasCornerCutout = true;
+        }
+
         if (mDisplayCutout == null || mDisplayCutout.isEmpty() || hasCornerCutout) {
             mCutoutSpace.setVisibility(View.GONE);
             return;
@@ -301,8 +309,12 @@
     }
 
     private void updateSafeInsets() {
-        Insets insets = mContentInsetsProvider
-                .getStatusBarContentInsetsForCurrentRotation();
+        if (mInsetsFetcher == null) {
+            Log.e(TAG, "mInsetsFetcher unexpectedly null");
+            return;
+        }
+
+        Insets insets  = mInsetsFetcher.fetchInsets();
         setPadding(
                 insets.left,
                 insets.top,
@@ -311,6 +323,17 @@
     }
 
     private void updateWindowHeight() {
+        if (Flags.statusBarStopUpdatingWindowHeight()) {
+            return;
+        }
         mStatusBarWindowController.refreshStatusBarHeight();
     }
+
+    interface HasCornerCutoutFetcher {
+        boolean fetchHasCornerCutout();
+    }
+
+    interface InsetsFetcher {
+        Insets fetchInsets();
+    }
 }
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 5206e46..bef552c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -23,9 +23,13 @@
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Flags
 import com.android.systemui.Gefingerpoken
+import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS
+import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.ui.view.WindowRootView
@@ -34,6 +38,7 @@
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.policy.Clock
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
@@ -67,28 +72,23 @@
     private val viewUtil: ViewUtil,
     private val configurationController: ConfigurationController,
     private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
+    private val darkIconDispatcher: DarkIconDispatcher,
+    private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
 ) : ViewController<PhoneStatusBarView>(view) {
 
-    private lateinit var statusContainer: View
+    private lateinit var battery: BatteryMeterView
+    private lateinit var clock: Clock
+    private lateinit var startSideContainer: View
+    private lateinit var endSideContainer: View
 
-    private val configurationListener =
-        object : ConfigurationController.ConfigurationListener {
-            override fun onDensityOrFontScaleChanged() {
-                mView.onDensityOrFontScaleChanged()
-            }
-        }
-
-    override fun onViewAttached() {
-        statusContainer = mView.requireViewById(R.id.system_icons)
-        statusContainer.setOnHoverListener(
-            statusOverlayHoverListenerFactory.createDarkAwareListener(statusContainer)
-        )
-        statusContainer.setOnTouchListener(object : View.OnTouchListener {
+    private val iconsOnTouchListener =
+        object : View.OnTouchListener {
             override fun onTouch(v: View, event: MotionEvent): Boolean {
-                // We want to handle only mouse events here to avoid stealing finger touches from
-                // status bar which expands shade when swiped down on. We're using onTouchListener
-                // instead of onClickListener as the later will lead to isClickable being set to
-                // true and hence ALL touches always being intercepted. See [View.OnTouchEvent]
+                // We want to handle only mouse events here to avoid stealing finger touches
+                // from status bar which expands shade when swiped down on. See b/326097469.
+                // We're using onTouchListener instead of onClickListener as the later will lead
+                // to isClickable being set to true and hence ALL touches always being
+                // intercepted. See [View.OnTouchEvent]
                 if (event.source == InputDevice.SOURCE_MOUSE) {
                     if (event.action == MotionEvent.ACTION_UP) {
                         v.performClick()
@@ -98,7 +98,20 @@
                 }
                 return false
             }
-        })
+        }
+
+    private val configurationListener =
+        object : ConfigurationController.ConfigurationListener {
+            override fun onDensityOrFontScaleChanged() {
+                clock.onDensityOrFontScaleChanged()
+            }
+        }
+
+    override fun onViewAttached() {
+        clock = mView.requireViewById(R.id.clock)
+        battery = mView.requireViewById(R.id.battery)
+        addDarkReceivers()
+        addCursorSupportToIconContainers()
 
         progressProvider?.setReadyToHandleTransition(true)
         configurationController.addCallback(configurationListener)
@@ -129,15 +142,39 @@
         }
     }
 
-    override fun onViewDetached() {
-        statusContainer.setOnHoverListener(null)
+    private fun addCursorSupportToIconContainers() {
+        endSideContainer = mView.requireViewById(R.id.system_icons)
+        endSideContainer.setOnHoverListener(
+            statusOverlayHoverListenerFactory.createDarkAwareListener(endSideContainer)
+        )
+        endSideContainer.setOnTouchListener(iconsOnTouchListener)
+
+        startSideContainer = mView.requireViewById(R.id.status_bar_start_side_content)
+        startSideContainer.setOnHoverListener(
+            statusOverlayHoverListenerFactory.createDarkAwareListener(startSideContainer)
+        )
+        startSideContainer.setOnTouchListener(iconsOnTouchListener)
+    }
+
+    @VisibleForTesting
+    public override fun onViewDetached() {
+        removeDarkReceivers()
+        startSideContainer.setOnHoverListener(null)
+        endSideContainer.setOnHoverListener(null)
         progressProvider?.setReadyToHandleTransition(false)
         moveFromCenterAnimationController?.onViewDetached()
         configurationController.removeCallback(configurationListener)
     }
 
     init {
+        // These should likely be done in `onInit`, not `init`.
         mView.setTouchEventHandler(PhoneStatusBarViewTouchHandler())
+        mView.setHasCornerCutoutFetcher {
+            statusBarContentInsetsProvider.currentRotationHasCornerCutout()
+        }
+        mView.setInsetsFetcher {
+            statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation()
+        }
         mView.init(userChipViewModel)
     }
 
@@ -178,10 +215,24 @@
         }
     }
 
+    private fun addDarkReceivers() {
+        darkIconDispatcher.addDarkReceiver(battery)
+        darkIconDispatcher.addDarkReceiver(clock)
+    }
+
+    private fun removeDarkReceivers() {
+        darkIconDispatcher.removeDarkReceiver(battery)
+        darkIconDispatcher.removeDarkReceiver(clock)
+    }
+
     inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
         override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
-            onTouch(event)
-            return false
+            return if (Flags.statusBarSwipeOverChip()) {
+                shadeViewController.handleExternalInterceptTouch(event)
+            } else {
+                onTouch(event)
+                false
+            }
         }
 
         override fun onTouchEvent(event: MotionEvent): Boolean {
@@ -277,10 +328,12 @@
         private val viewUtil: ViewUtil,
         private val configurationController: ConfigurationController,
         private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
+        private val darkIconDispatcher: DarkIconDispatcher,
+        private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
     ) {
         fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
             val statusBarMoveFromCenterAnimationController =
-                if (featureFlags.isEnabled(Flags.ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
+                if (featureFlags.isEnabled(ENABLE_UNFOLD_STATUS_BAR_ANIMATIONS)) {
                     unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController()
                 } else {
                     null
@@ -301,6 +354,8 @@
                 viewUtil,
                 configurationController,
                 statusOverlayHoverListenerFactory,
+                darkIconDispatcher,
+                statusBarContentInsetsProvider,
             )
         }
     }
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 da5877b..8f2d4f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -19,12 +19,12 @@
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.shade.ShadeViewController;
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -112,7 +112,7 @@
     }
 
     private void setHeadsAnimatingAway(boolean headsUpAnimatingAway) {
-        if (!NotificationsHeadsUpRefactor.isEnabled()) {
+        if (!SceneContainerFlag.isEnabled()) {
             mHeadsUpManager.setHeadsUpAnimatingAway(headsUpAnimatingAway);
         }
     }
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 f8f9b77..43f9af6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -68,12 +68,11 @@
 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dreams.DreamOverlayStateController;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardWmStateRefactor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
 import com.android.systemui.keyguard.shared.model.DismissAction;
 import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.KeyguardDone;
@@ -104,6 +103,7 @@
 import com.android.systemui.unfold.FoldAodAnimationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.kotlin.JavaAdapter;
 
 import dagger.Lazy;
@@ -170,6 +170,7 @@
     private final Lazy<ShadeController> mShadeController;
     private final Lazy<SceneInteractor> mSceneInteractorLazy;
     private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractorLazy;
+    private final DismissCallbackRegistry mDismissCallbackRegistry;
 
     private Job mListenForAlternateBouncerTransitionSteps = null;
     private Job mListenForKeyguardAuthenticatedBiometricsHandled = null;
@@ -179,6 +180,8 @@
     private float mFraction = -1f;
     private boolean mTracking = false;
     private boolean mBouncerShowingOverDream;
+    private int mAttemptsToShowBouncer = 0;
+    private DelayableExecutor mExecutor;
 
     private final PrimaryBouncerExpansionCallback mExpansionCallback =
             new PrimaryBouncerExpansionCallback() {
@@ -315,8 +318,6 @@
     private boolean mLastScreenOffAnimationPlaying;
     private float mQsExpansion;
 
-    private FeatureFlags mFlags;
-
     final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
     private boolean mIsBackAnimationEnabled;
     private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
@@ -360,8 +361,6 @@
             }
         }
     };
-    private Lazy<WindowManagerLockscreenVisibilityInteractor> mWmLockscreenVisibilityInteractor;
-    private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
     private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
     private final JavaAdapter mJavaAdapter;
     private StatusBarKeyguardViewManagerInteractor mStatusBarKeyguardViewManagerInteractor;
@@ -391,17 +390,19 @@
             UdfpsOverlayInteractor udfpsOverlayInteractor,
             ActivityStarter activityStarter,
             KeyguardTransitionInteractor keyguardTransitionInteractor,
+            KeyguardDismissTransitionInteractor keyguardDismissTransitionInteractor,
             @Main CoroutineDispatcher mainDispatcher,
-            Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
             Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
             SelectedUserInteractor selectedUserInteractor,
-            Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
             JavaAdapter javaAdapter,
             Lazy<SceneInteractor> sceneInteractorLazy,
             StatusBarKeyguardViewManagerInteractor statusBarKeyguardViewManagerInteractor,
-            Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy
+            @Main DelayableExecutor executor,
+            Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
+            DismissCallbackRegistry dismissCallbackRegistry
     ) {
         mContext = context;
+        mExecutor = executor;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
         mConfigurationController = configurationController;
@@ -426,18 +427,19 @@
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+        mKeyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor;
         mMainDispatcher = mainDispatcher;
-        mWmLockscreenVisibilityInteractor = wmLockscreenVisibilityInteractor;
         mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
         mSelectedUserInteractor = selectedUserInteractor;
-        mSurfaceBehindInteractor = surfaceBehindInteractor;
         mJavaAdapter = javaAdapter;
         mSceneInteractorLazy = sceneInteractorLazy;
         mStatusBarKeyguardViewManagerInteractor = statusBarKeyguardViewManagerInteractor;
         mDeviceEntryInteractorLazy = deviceEntryInteractorLazy;
+        mDismissCallbackRegistry = dismissCallbackRegistry;
     }
 
     KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    KeyguardDismissTransitionInteractor mKeyguardDismissTransitionInteractor;
     CoroutineDispatcher mMainDispatcher;
 
     @Override
@@ -545,6 +547,7 @@
 
     @VisibleForTesting
     void consumeFromAlternateBouncerTransitionSteps(TransitionStep step) {
+        SceneContainerFlag.assertInLegacyMode();
         hideAlternateBouncer(false);
     }
 
@@ -554,6 +557,7 @@
      */
     @VisibleForTesting
     void consumeKeyguardAuthenticatedBiometricsHandled(Unit handled) {
+        SceneContainerFlag.assertInLegacyMode();
         if (mAlternateBouncerInteractor.isVisibleState()) {
             hideAlternateBouncer(false);
         }
@@ -709,13 +713,7 @@
      * {@link #needsFullscreenBouncer()}.
      */
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
-        boolean isDozing = mDozing;
-        if (Flags.simPinRaceConditionOnRestart()) {
-            KeyguardState toState = mKeyguardTransitionInteractor.getTransitionState().getValue()
-                    .getTo();
-            isDozing = mDozing || toState == KeyguardState.DOZING || toState == KeyguardState.AOD;
-        }
-        if (needsFullscreenBouncer() && !isDozing) {
+        if (needsFullscreenBouncer() && !mDozing) {
             // The keyguard might be showing (already). So we need to hide it.
             if (!primaryBouncerIsShowing()) {
                 if (SceneContainerFlag.isEnabled()) {
@@ -725,9 +723,22 @@
                 } else {
                     if (Flags.simPinRaceConditionOnRestart()) {
                         if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) {
+                            mAttemptsToShowBouncer = 0;
                             mCentralSurfaces.hideKeyguard();
                         } else {
-                            mCentralSurfaces.showKeyguard();
+                            if (mAttemptsToShowBouncer > 6) {
+                                mAttemptsToShowBouncer = 0;
+                                Log.e(TAG, "Too many failed attempts to show bouncer, showing "
+                                        + "keyguard instead");
+                                mCentralSurfaces.showKeyguard();
+                            } else {
+                                Log.v(TAG, "Failed to show bouncer, attempt #: "
+                                        + mAttemptsToShowBouncer++);
+                                mExecutor.executeDelayed(() ->
+                                        showBouncerOrKeyguard(hideBouncerWhenShowing,
+                                            isFalsingReset),
+                                        500);
+                            }
                         }
                     } else {
                         mCentralSurfaces.hideKeyguard();
@@ -972,13 +983,19 @@
             if (isOccluded && !mDozing) {
                 mCentralSurfaces.hideKeyguard();
                 if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
-                    hideBouncer(false /* destroyView */);
+                    // We're removing "reset" in the refactor - bouncer will be hidden by the root
+                    // cause of the "reset" calls.
+                    if (!KeyguardWmStateRefactor.isEnabled()) {
+                        hideBouncer(false /* destroyView */);
+                    }
                 }
             } else {
                 showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
             }
-            if (hideBouncerWhenShowing) {
+            if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing) {
                 hideAlternateBouncer(true);
+                mDismissCallbackRegistry.notifyDismissCancelled();
+                mPrimaryBouncerInteractor.setDismissAction(null, null);
             }
             mKeyguardUpdateManager.sendKeyguardReset();
             updateStates();
@@ -1003,7 +1020,9 @@
             mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
             mKeyguardMessageAreaController.setMessage("");
         }
-        mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
+        if (!SceneContainerFlag.isEnabled()) {
+            mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer);
+        }
 
         if (updateScrim) {
             mCentralSurfaces.updateScrimController();
@@ -1445,10 +1464,13 @@
             mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing);
             mCentralSurfaces.setBouncerShowing(primaryBouncerShowing);
         }
-        if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate
-                || isPrimaryBouncerShowingChanged) {
-            mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
-                    primaryBouncerShowing);
+        if (!SceneContainerFlag.isEnabled()) {
+            if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing
+                    || mFirstUpdate
+                    || isPrimaryBouncerShowingChanged) {
+                mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
+                        primaryBouncerShowing);
+            }
         }
 
         mFirstUpdate = false;
@@ -1589,7 +1611,7 @@
         }
 
         if (KeyguardWmStateRefactor.isEnabled()) {
-            mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+            mKeyguardDismissTransitionInteractor.startDismissKeyguardTransition(
                     "SBKVM#keyguardAuthenticated");
         }
     }
@@ -1863,6 +1885,11 @@
                 || mode == KeyguardSecurityModel.SecurityMode.SimPuk;
     }
 
+    @VisibleForTesting
+    void setAttemptsToShowBouncer(int attempts) {
+        mAttemptsToShowBouncer = attempts;
+    }
+
     /**
      * Delegate used to send show and hide events to an alternate authentication method instead of
      * the regular pin/pattern/password bouncer.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index e92058b..0a6e7f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -230,7 +230,8 @@
         Runnable action = () -> {
             mBubblesManagerOptional.ifPresent(bubblesManager ->
                     bubblesManager.onUserChangedBubble(entry, !entry.isBubble()));
-            mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true);
+            mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true,
+                    /* reason= */ "onNotificationBubbleIconClicked");
         };
         if (entry.isBubble()) {
             // entry is being un-bubbled, no need to unlock
@@ -621,7 +622,8 @@
 
             // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
             // become canceled shortly by NoMan, but we can't assume that.
-            mHeadsUpManager.removeNotification(key, true /* releaseImmediately */);
+            mHeadsUpManager.removeNotification(key, /* releaseImmediately= */ true,
+                    "removeHunAfterClick");
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ba59398..d5fafe2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Handler;
@@ -23,16 +25,19 @@
 import android.util.Log;
 
 import com.android.settingslib.mobile.TelephonyIcons;
+import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.connectivity.IconState;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor;
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.kotlin.JavaAdapter;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -40,10 +45,13 @@
 
 import javax.inject.Inject;
 
-/** Controls the signal policies for icons shown in the statusbar. **/
+/** Controls the signal policies for icons shown in the statusbar. */
 @SysUISingleton
-public class StatusBarSignalPolicy implements SignalCallback,
-        SecurityController.SecurityControllerCallback, Tunable {
+public class StatusBarSignalPolicy
+        implements SignalCallback,
+                SecurityController.SecurityControllerCallback,
+                Tunable,
+                CoreStartable {
     private static final String TAG = "StatusBarSignalPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -61,16 +69,15 @@
     private final Handler mHandler = Handler.getMain();
     private final CarrierConfigTracker mCarrierConfigTracker;
     private final TunerService mTunerService;
+    private final JavaAdapter mJavaAdapter;
+    private final AirplaneModeInteractor mAirplaneModeInteractor;
 
     private boolean mHideAirplane;
     private boolean mHideMobile;
     private boolean mHideEthernet;
-    private boolean mActivityEnabled;
+    private final boolean mActivityEnabled;
 
-    // Track as little state as possible, and only for padding purposes
-    private boolean mIsAirplaneMode = false;
-
-    private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
+    private final ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
     private boolean mInitialized;
 
     @Inject
@@ -80,15 +87,19 @@
             CarrierConfigTracker carrierConfigTracker,
             NetworkController networkController,
             SecurityController securityController,
-            TunerService tunerService
+            TunerService tunerService,
+            JavaAdapter javaAdapter,
+            AirplaneModeInteractor airplaneModeInteractor
     ) {
         mContext = context;
 
         mIconController = iconController;
         mCarrierConfigTracker = carrierConfigTracker;
+        mJavaAdapter = javaAdapter;
         mNetworkController = networkController;
         mSecurityController = securityController;
         mTunerService = tunerService;
+        mAirplaneModeInteractor = airplaneModeInteractor;
 
         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -100,15 +111,35 @@
         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
     }
 
+    @Override
+    public void start() {
+        if (!statusBarSignalPolicyRefactor()) {
+            return;
+        }
+
+        mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
+        mNetworkController.addCallback(this);
+        mSecurityController.addCallback(this);
+
+        mJavaAdapter.alwaysCollectFlow(
+                mAirplaneModeInteractor.isAirplaneMode(), this::updateAirplaneModeIcon);
+    }
+
     /** Call to initialize and register this class with the system. */
     public void init() {
-        if (mInitialized) {
+        if (mInitialized || statusBarSignalPolicyRefactor()) {
             return;
         }
         mInitialized = true;
         mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
         mNetworkController.addCallback(this);
         mSecurityController.addCallback(this);
+
+        if (statusBarSignalPolicyRefactor()) {
+            mJavaAdapter.alwaysCollectFlow(
+                    mAirplaneModeInteractor.isAirplaneMode(),
+                    this::updateAirplaneModeIcon);
+        }
     }
 
     public void destroy() {
@@ -222,15 +253,19 @@
 
     @Override
     public void setIsAirplaneMode(IconState icon) {
+        if (statusBarSignalPolicyRefactor()) {
+            return;
+        }
+
         if (DEBUG) {
             Log.d(TAG, "setIsAirplaneMode: "
                     + "icon = " + (icon == null ? "" : icon.toString()));
         }
-        mIsAirplaneMode = icon.visible && !mHideAirplane;
+        boolean isAirplaneMode = icon.visible && !mHideAirplane;
         int resId = icon.icon;
         String description = icon.contentDescription;
 
-        if (mIsAirplaneMode && resId > 0) {
+        if (isAirplaneMode && resId > 0) {
             mIconController.setIcon(mSlotAirplane, resId, description);
             mIconController.setIconVisibility(mSlotAirplane, true);
         } else {
@@ -238,6 +273,21 @@
         }
     }
 
+    public void updateAirplaneModeIcon(boolean isAirplaneModeOn) {
+        if (StatusBarSignalPolicyRefactor.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+
+        boolean isAirplaneMode = isAirplaneModeOn && !mHideAirplane;
+        mIconController.setIconVisibility(mSlotAirplane, isAirplaneMode);
+        if (isAirplaneMode) {
+            mIconController.setIcon(
+                    mSlotAirplane,
+                    TelephonyIcons.FLIGHT_MODE_ICON,
+                    mContext.getString(R.string.accessibility_airplane_mode));
+        }
+    }
+
     /**
      * Stores the statusbar state for no Calling & SMS.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt
new file mode 100644
index 0000000..0577f495
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicyRefactor.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.phone
+
+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_signal_policy_refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object StatusBarSignalPolicyRefactor {
+    /** The aconfig flag name */
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_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.statusBarSignalPolicyRefactor()
+
+    /**
+     * 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/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
index 66ac17e..b9cba99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -31,7 +31,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.res.R;
-import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.shared.animation.Interpolators;
 
 /**
  * View to show a toast-like popup on the notification shade and quick settings.
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 5be4ba2..4a0fdee 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
@@ -121,7 +121,7 @@
     private MultiSourceMinAlphaController mEndSideAlphaController;
     private LinearLayout mEndSideContent;
     private View mClockView;
-    private View mOngoingActivityChip;
+    private View mPrimaryOngoingActivityChip;
     private View mNotificationIconAreaInner;
     // Visibilities come in from external system callers via disable flags, but we also sometimes
     // modify the visibilities internally. We need to store both so that we don't accidentally
@@ -354,7 +354,7 @@
         mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
         mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
         mClockView = mStatusBar.findViewById(R.id.clock);
-        mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip);
+        mPrimaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_primary);
         showEndSideContent(false);
         showClock(false);
         initOperatorName();
@@ -636,9 +636,9 @@
         // icons so if the icons are disabled then the activity chip should be, too.)
         boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications;
         if (showOngoingActivityChip) {
-            showOngoingActivityChip(animate);
+            showPrimaryOngoingActivityChip(animate);
         } else {
-            hideOngoingActivityChip(animate);
+            hidePrimaryOngoingActivityChip(animate);
         }
     }
 
@@ -729,20 +729,20 @@
         animateShow(mClockView, animate);
     }
 
-    /** Hides the ongoing activity chip. */
-    private void hideOngoingActivityChip(boolean animate) {
-        animateHiddenState(mOngoingActivityChip, View.GONE, animate);
+    /** Hides the primary ongoing activity chip. */
+    private void hidePrimaryOngoingActivityChip(boolean animate) {
+        animateHiddenState(mPrimaryOngoingActivityChip, View.GONE, animate);
     }
 
     /**
-     * Displays the ongoing activity chip.
+     * Displays the primary ongoing activity chip.
      *
      * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the
      * ongoing call information, If that flag is enabled, it will support different kinds of ongoing
      * activities. See b/332662551.
      */
-    private void showOngoingActivityChip(boolean animate) {
-        animateShow(mOngoingActivityChip, animate);
+    private void showPrimaryOngoingActivityChip(boolean animate) {
+        animateShow(mPrimaryOngoingActivityChip, animate);
     }
 
     /**
@@ -850,7 +850,7 @@
 
     private void initOngoingCallChip() {
         mOngoingCallController.addCallback(mOngoingCallListener);
-        mOngoingCallController.setChipView(mOngoingActivityChip);
+        mOngoingCallController.setChipView(mPrimaryOngoingActivityChip);
     }
 
     @Override
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 4368239..bd6a1c0 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
@@ -227,6 +227,15 @@
                 callNotificationInfo
                     // This shouldn't happen, but protect against it in case
                     ?: return OngoingCallModel.NoCall
+            logger.log(
+                TAG,
+                LogLevel.DEBUG,
+                {
+                    bool1 = Flags.statusBarCallChipNotificationIcon()
+                    bool2 = currentInfo.notificationIconView != null
+                },
+                { "Creating OngoingCallModel.InCall. notifIconFlag=$bool1 hasIcon=$bool2" }
+            )
             val icon =
                 if (Flags.statusBarCallChipNotificationIcon()) {
                     currentInfo.notificationIconView
@@ -257,6 +266,7 @@
 
     private fun updateInfoFromNotifModel(notifModel: ActiveNotificationModel?) {
         if (notifModel == null) {
+            logger.log(TAG, LogLevel.DEBUG, {}, { "NotifInteractorCallModel: null" })
             removeChip()
         } else if (notifModel.callType != CallType.Ongoing) {
             logger.log(
@@ -267,6 +277,18 @@
             )
             removeChip()
         } else {
+            logger.log(
+                TAG,
+                LogLevel.DEBUG,
+                {
+                    str1 = notifModel.key
+                    long1 = notifModel.whenTime
+                    str1 = notifModel.callType.name
+                    bool1 = notifModel.statusBarChipIconView != null
+                },
+                { "NotifInteractorCallModel: key=$str1 when=$long1 callType=$str2 hasIcon=$bool1" }
+            )
+
             val newOngoingCallInfo =
                 CallNotificationInfo(
                     notifModel.key,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
index 8871dae..6c30330 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone.ui;
 
-import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
@@ -64,9 +63,8 @@
     }
 
     @Override
-    protected LinearLayout.LayoutParams onCreateLayoutParams() {
-        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+    protected LinearLayout.LayoutParams onCreateLayoutParams(StatusBarIcon.Shape shape) {
+        LinearLayout.LayoutParams lp = super.onCreateLayoutParams(shape);
         lp.setMargins(mIconHorizontalMargin, 0, mIconHorizontalMargin, 0);
         return lp;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
index 5ad7376..91ead61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
@@ -20,6 +20,7 @@
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
+import static com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl.usesModeIcons;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -27,9 +28,8 @@
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
-import androidx.annotation.VisibleForTesting;
-
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.statusbar.StatusBarIcon.Shape;
 import com.android.systemui.demomode.DemoModeCommandReceiver;
 import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -155,12 +155,11 @@
         };
     }
 
-    @VisibleForTesting
     protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
             StatusBarIcon icon) {
         StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
         view.set(icon);
-        mGroup.addView(view, index, onCreateLayoutParams());
+        mGroup.addView(view, index, onCreateLayoutParams(icon.shape));
         return view;
     }
 
@@ -174,7 +173,7 @@
             int index) {
         mBindableIcons.put(holder.getSlot(), holder);
         ModernStatusBarView view = holder.getInitializer().createAndBind(mContext);
-        mGroup.addView(view, index, onCreateLayoutParams());
+        mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
         if (mIsInDemoMode) {
             mDemoStatusIcons.addBindableIcon(holder);
         }
@@ -183,7 +182,7 @@
 
     protected StatusIconDisplayable addNewWifiIcon(int index, String slot) {
         ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot);
-        mGroup.addView(view, index, onCreateLayoutParams());
+        mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
 
         if (mIsInDemoMode) {
             mDemoStatusIcons.addModernWifiView(mWifiViewModel);
@@ -199,7 +198,7 @@
             int subId
     ) {
         BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId);
-        mGroup.addView(view, index, onCreateLayoutParams());
+        mGroup.addView(view, index, onCreateLayoutParams(Shape.WRAP_CONTENT));
 
         if (mIsInDemoMode) {
             Context mobileContext = mMobileContextProvider
@@ -233,8 +232,12 @@
                 );
     }
 
-    protected LinearLayout.LayoutParams onCreateLayoutParams() {
-        return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
+    protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) {
+        int width = usesModeIcons() && shape == StatusBarIcon.Shape.FIXED_SPACE
+                ? mIconSize
+                : ViewGroup.LayoutParams.WRAP_CONTENT;
+
+        return new LinearLayout.LayoutParams(width, mIconSize);
     }
 
     protected void destroy() {
@@ -256,6 +259,13 @@
     /** Called once an icon has been set. */
     public void onSetIcon(int viewIndex, StatusBarIcon icon) {
         StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);
+        if (usesModeIcons()) {
+            ViewGroup.LayoutParams current = view.getLayoutParams();
+            ViewGroup.LayoutParams desired = onCreateLayoutParams(icon.shape);
+            if (desired.width != current.width || desired.height != current.height) {
+                view.setLayoutParams(desired);
+            }
+        }
         view.set(icon);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
index 1ada30e..0459b97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconController.java
@@ -18,9 +18,12 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.ArraySet;
 
+import androidx.annotation.DrawableRes;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
@@ -58,6 +61,19 @@
     void setIcon(String slot, int resourceId, CharSequence contentDescription);
 
     /**
+     * Adds or updates an icon for the given slot.
+     *
+     * @param resPackage the package name containing the resource in question. Can be null if the
+     *      icon is a system icon (e.g. a resource from {@code android.R.drawable} or
+     *      {@code com.android.internal.R.drawable}).
+     * @param iconResId id of the drawable resource
+     * @param preloadedIcon optional drawable corresponding to {@code iconResId}, if known
+     */
+    void setResourceIcon(String slot, @Nullable String resPackage, @DrawableRes int iconResId,
+            @Nullable Drawable preloadedIcon, CharSequence contentDescription,
+            StatusBarIcon.Shape shape);
+
+    /**
      * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been
      * set up (inflated and added to the view hierarchy).
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
index 85213cb..9b6d32b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImpl.java
@@ -18,16 +18,22 @@
 
 import static com.android.systemui.statusbar.phone.ui.StatusBarIconList.Slot;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.ViewGroup;
 
+import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.statusbar.StatusBarIcon;
@@ -221,19 +227,69 @@
         }
     }
 
-    /** */
     @Override
     public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
+        setResourceIconInternal(
+                slot,
+                Icon.createWithResource(mContext, resourceId),
+                /* preloadedIcon= */ null,
+                contentDescription,
+                StatusBarIcon.Type.SystemIcon,
+                StatusBarIcon.Shape.WRAP_CONTENT);
+    }
+
+    @Override
+    public void setResourceIcon(String slot, @Nullable String resPackage,
+            @DrawableRes int iconResId, @Nullable Drawable preloadedIcon,
+            CharSequence contentDescription, StatusBarIcon.Shape shape) {
+        if (!usesModeIcons()) {
+            Log.wtf("TAG",
+                    "StatusBarIconController.setResourceIcon() should not be called without "
+                            + "MODES_UI & MODES_UI_ICONS!");
+            // Fall back to old implementation, although it will not load the icon if it's from a
+            // different package.
+            setIcon(slot, iconResId, contentDescription);
+            return;
+        }
+
+        Icon icon = resPackage != null
+                ? Icon.createWithResource(resPackage, iconResId)
+                : Icon.createWithResource(mContext, iconResId);
+
+        setResourceIconInternal(
+                slot,
+                icon,
+                preloadedIcon,
+                contentDescription,
+                StatusBarIcon.Type.ResourceIcon,
+                shape);
+    }
+
+    private void setResourceIconInternal(String slot, Icon resourceIcon,
+            @Nullable Drawable preloadedIcon, CharSequence contentDescription,
+            StatusBarIcon.Type type, StatusBarIcon.Shape shape) {
+        checkArgument(resourceIcon.getType() == Icon.TYPE_RESOURCE,
+                "Expected Icon of TYPE_RESOURCE, but got " + resourceIcon.getType());
+        String resPackage = resourceIcon.getResPackage();
+        if (TextUtils.isEmpty(resPackage)) {
+            resPackage = mContext.getPackageName();
+        }
+
         StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0);
         if (holder == null) {
-            StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                    Icon.createWithResource(mContext, resourceId), 0, 0,
-                    contentDescription, StatusBarIcon.Type.SystemIcon);
+            StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, resPackage,
+                    resourceIcon, /* iconLevel= */ 0, /* number=*/ 0,
+                    contentDescription, type, shape);
+            icon.preloadedIcon = preloadedIcon;
             holder = StatusBarIconHolder.fromIcon(icon);
             setIcon(slot, holder);
         } else {
-            holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
+            holder.getIcon().pkg = resPackage;
+            holder.getIcon().icon = resourceIcon;
             holder.getIcon().contentDescription = contentDescription;
+            holder.getIcon().type = type;
+            holder.getIcon().shape = shape;
+            holder.getIcon().preloadedIcon = preloadedIcon;
             handleSet(slot, holder);
         }
     }
@@ -524,4 +580,9 @@
             return slot + EXTERNAL_SLOT_SUFFIX;
         }
     }
+
+    static boolean usesModeIcons() {
+        return android.app.Flags.modesApi() && android.app.Flags.modesUi()
+                && android.app.Flags.modesUiIcons();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.kt
new file mode 100644
index 0000000..a40d510
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteTableLog.kt
@@ -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.
+ */
+
+package com.android.systemui.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class DeviceBasedSatelliteTableLog
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 a81bfa4..4850049 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
@@ -239,6 +239,13 @@
             return factory.create("VerboseDeviceBasedSatelliteInputLog", 200)
         }
 
+        @Provides
+        @SysUISingleton
+        @DeviceBasedSatelliteTableLog
+        fun provideDeviceBasedSatelliteTableLog(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("DeviceBasedSatelliteTableLog", 200)
+        }
+
         const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON =
             "FirstMobileSubShowingNetworkTypeIcon"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index 2e54972..9cbfc44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -198,7 +198,8 @@
 
     fun logServiceProvidersUpdatedBroadcast(intent: Intent) {
         val showSpn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)
-        val spn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
+        val spn = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+        val dataSpn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
         val showPlmn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)
         val plmn = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
 
@@ -208,12 +209,13 @@
             {
                 bool1 = showSpn
                 str1 = spn
+                str2 = dataSpn
                 bool2 = showPlmn
-                str2 = plmn
+                str3 = plmn
             },
             {
                 "Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
-                    " showSpn=$bool1 spn=$str1 showPlmn=$bool2 plmn=$str2"
+                    " showSpn=$bool1 spn=$str1 dataSpn=$str2 showPlmn=$bool2 plmn=$str3"
             }
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 99ed2d9..85bbe7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -21,6 +21,8 @@
 import android.telephony.TelephonyManager.EXTRA_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
+import android.telephony.TelephonyManager.EXTRA_SPN
+import com.android.systemui.Flags.statusBarSwitchToSpnFromDataSpn
 import com.android.systemui.log.table.Diffable
 import com.android.systemui.log.table.TableRowLogger
 
@@ -96,7 +98,13 @@
 
 fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? {
     val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
-    val spn = getStringExtra(EXTRA_DATA_SPN)
+    val spn =
+        if (statusBarSwitchToSpnFromDataSpn()) {
+            getStringExtra(EXTRA_SPN)
+        } else {
+            getStringExtra(EXTRA_DATA_SPN)
+        }
+
     val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false)
     val plmn = getStringExtra(EXTRA_PLMN)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
deleted file mode 100644
index cce3eb0..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
+++ /dev/null
@@ -1,31 +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.statusbar.pipeline.mobile.data.model
-
-import android.telephony.ServiceState
-
-/**
- * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to
- * extract from service state here for consumption downstream
- */
-data class ServiceStateModel(val isEmergencyOnly: Boolean) {
-    companion object {
-        fun fromServiceState(serviceState: ServiceState): ServiceStateModel {
-            return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly)
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 5ad8bf1..32e9c85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -21,7 +21,6 @@
 import com.android.settingslib.SignalIcon.MobileIconGroup
 import com.android.settingslib.mobile.MobileMappings
 import com.android.settingslib.mobile.MobileMappings.Config
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -93,17 +92,15 @@
     val defaultMobileIconGroup: Flow<MobileIconGroup>
 
     /**
-     * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a
-     * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]).
+     * Can the device make emergency calls using the device-based service state? This field is only
+     * useful when all known active subscriptions are OOS and not emergency call capable.
      *
-     * While each [MobileConnectionsRepository] listens for the service state of each subscription,
-     * there is potentially a service state associated with the device itself. This value can be
-     * used to calculate e.g., the emergency calling capability of the device (as opposed to the
-     * emergency calling capability of an individual mobile connection)
+     * Specifically, this checks every [ServiceState] of the device, and looks for any that report
+     * [ServiceState.isEmergencyOnly].
      *
-     * Note: this is a [StateFlow] using an eager sharing strategy.
+     * This is an eager flow, and re-evaluates whenever ACTION_SERVICE_STATE is sent for subId = -1.
      */
-    val deviceServiceState: StateFlow<ServiceStateModel?>
+    val isDeviceEmergencyCallCapable: StateFlow<Boolean>
 
     /**
      * If any active SIM on the device is in
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index b068152..b247da4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -25,7 +25,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
@@ -152,16 +151,17 @@
     override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
         activeRepo.flatMapLatest { it.defaultMobileIconGroup }
 
-    override val deviceServiceState: StateFlow<ServiceStateModel?> =
+    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
         activeRepo
-            .flatMapLatest { it.deviceServiceState }
+            .flatMapLatest { it.isDeviceEmergencyCallCapable }
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
-                realRepository.deviceServiceState.value
+                realRepository.isDeviceEmergencyCallCapable.value
             )
 
     override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
+
     override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
 
     override val defaultDataSubId: StateFlow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index a944e91..3a79f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -137,10 +136,11 @@
 
     override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
 
-    // TODO(b/339023069): demo command for device-based connectivity state
-    override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null)
+    // TODO(b/339023069): demo command for device-based emergency calls state
+    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> = MutableStateFlow(false)
 
     override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())
+
     override fun getIsAnySimSecure(): Boolean = false
 
     override val defaultMobileIconMapping = MutableStateFlow(TelephonyIcons.ICON_NAME_TO_ICON)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 261258a..b756a05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -21,7 +21,6 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.telephony.CarrierConfigManager
-import android.telephony.ServiceState
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -49,7 +48,6 @@
 import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -72,7 +70,6 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
@@ -175,8 +172,8 @@
             }
             .flowOn(bgDispatcher)
 
-    /** Note that this flow is eager, so we don't miss any state */
-    override val deviceServiceState: StateFlow<ServiceStateModel?> =
+    /** Turn ACTION_SERVICE_STATE (for subId = -1) into an event */
+    private val serviceStateChangedEvent: Flow<Unit> =
         broadcastDispatcher
             .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ ->
                 val subId =
@@ -185,24 +182,34 @@
                         INVALID_SUBSCRIPTION_ID
                     )
 
-                val extras = intent.extras
-                if (extras == null) {
-                    logger.logTopLevelServiceStateBroadcastMissingExtras(subId)
-                    return@broadcastFlow null
-                }
-
-                val serviceState = ServiceState.newFromBundle(extras)
-                logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState)
+                // Only emit if the subId is not associated with an active subscription
                 if (subId == INVALID_SUBSCRIPTION_ID) {
-                    // Assume that -1 here is the device's service state. We don't care about
-                    // other ones.
-                    ServiceStateModel.fromServiceState(serviceState)
-                } else {
-                    null
+                    Unit
                 }
             }
-            .filterNotNull()
-            .stateIn(scope, SharingStarted.Eagerly, null)
+            // Emit on start so that we always check the state at least once
+            .onStart { emit(Unit) }
+
+    /** Eager flow to determine the device-based emergency calls only state */
+    override val isDeviceEmergencyCallCapable: StateFlow<Boolean> =
+        serviceStateChangedEvent
+            .mapLatest {
+                val modems = telephonyManager.activeModemCount
+                // Check the service state for every modem. If any state reports emergency calling
+                // capable, then consider the device to have emergency call capabilities
+                (0..<modems)
+                    .map { telephonyManager.getServiceStateForSlot(it) }
+                    .any { it?.isEmergencyOnly == true }
+            }
+            .flowOn(bgDispatcher)
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = LOGGING_PREFIX,
+                columnName = "deviceEmergencyOnly",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.Eagerly, false)
 
     /**
      * State flow that emits the set of mobile data subscriptions, each represented by its own
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index cc4d568..28fff4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -385,7 +385,7 @@
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
-        mobileConnectionsRepo.deviceServiceState.map { it?.isEmergencyOnly ?: false }
+        mobileConnectionsRepo.isDeviceEmergencyCallCapable
 
     /** Vends out new [MobileIconInteractor] for a particular subId */
     override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 03f88c7..f1a444f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -21,7 +21,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteTableLog
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -33,6 +36,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
@@ -47,6 +51,7 @@
     wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
     @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
+    @DeviceBasedSatelliteTableLog private val tableLog: TableLogBuffer,
 ) {
     /** Must be observed by any UI showing Satellite iconography */
     val isSatelliteAllowed =
@@ -55,6 +60,13 @@
             } else {
                 flowOf(false)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_ALLOWED,
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     /** See [SatelliteConnectionState] for relevant states */
@@ -65,6 +77,12 @@
 
                 flowOf(SatelliteConnectionState.Off)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                initialValue = SatelliteConnectionState.Off,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), SatelliteConnectionState.Off)
 
     /** 0-4 description of the connection strength */
@@ -74,6 +92,13 @@
             } else {
                 flowOf(0)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_LEVEL,
+                initialValue = 0,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
 
     val isSatelliteProvisioned = repo.isSatelliteProvisioned
@@ -82,19 +107,27 @@
         wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
 
     private val allConnectionsOos =
-        iconsInteractor.icons.aggregateOver(
-            selector = { intr ->
-                combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
-                    isInService,
-                    isEmergencyOnly,
-                    isNtn ->
-                    !isInService && !isEmergencyOnly && !isNtn
-                }
-            },
-            defaultValue = true, // no connections == everything is OOS
-        ) { isOosAndNotEmergencyAndNotSatellite ->
-            isOosAndNotEmergencyAndNotSatellite.all { it }
-        }
+        iconsInteractor.icons
+            .aggregateOver(
+                selector = { intr ->
+                    combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
+                        isInService,
+                        isEmergencyOnly,
+                        isNtn ->
+                        !isInService && !isEmergencyOnly && !isNtn
+                    }
+                },
+                defaultValue = true, // no connections == everything is OOS
+            ) { isOosAndNotEmergencyAndNotSatellite ->
+                isOosAndNotEmergencyAndNotSatellite.all { it }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_ALL_OOS,
+                initialValue = true,
+            )
 
     /** When all connections are considered OOS, satellite connectivity is potentially valid */
     val areAllConnectionsOutOfService =
@@ -122,10 +155,24 @@
             } else {
                 flowOf(false)
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "",
+                columnName = COL_FULL_OOS,
+                initialValue = true,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), true)
 
     companion object {
         const val TAG = "DeviceBasedSatelliteInteractor"
+
+        const val COL_LEVEL = "level"
+        const val COL_ALL_OOS = "allConnsOOS"
+        const val COL_ALLOWED = "allowed"
+        // Going to try to optimize for not using too much width on the table here. This information
+        // can be ascertained by checking for the device emergency only in the mobile logs as well
+        const val COL_FULL_OOS = "allOosAndNoEmer"
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
index bfe2941..905ed730 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/shared/model/SatelliteConnectionState.kt
@@ -26,8 +26,10 @@
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_OFF
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNAVAILABLE
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
 
-enum class SatelliteConnectionState {
+enum class SatelliteConnectionState : Diffable<SatelliteConnectionState> {
     // State is unknown or undefined
     Unknown,
     // Radio is off
@@ -37,7 +39,15 @@
     // Radio is connected, aka satellite is available for use
     Connected;
 
+    override fun logDiffs(prevVal: SatelliteConnectionState, row: TableRowLogger) {
+        if (prevVal != this) {
+            row.logChange(COL_CONNECTION_STATE, name)
+        }
+    }
+
     companion object {
+        const val COL_CONNECTION_STATE = "connState"
+
         // TODO(b/316635648): validate these states. We don't need the level of granularity that
         //  SatelliteManager gives us.
         fun fromModemState(@SatelliteManager.SatelliteModemState modemState: Int) =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index 48278d4..37f2f19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -22,9 +22,12 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteTableLog
 import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
@@ -33,14 +36,12 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /**
@@ -71,27 +72,12 @@
     @Application scope: CoroutineScope,
     airplaneModeRepository: AirplaneModeRepository,
     @DeviceBasedSatelliteInputLog logBuffer: LogBuffer,
+    @DeviceBasedSatelliteTableLog tableLog: TableLogBuffer,
 ) : DeviceBasedSatelliteViewModel {
-    private val shouldShowIcon: Flow<Boolean> =
-        interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
-            if (!allOos) {
-                flowOf(false)
-            } else {
-                combine(
-                    interactor.isSatelliteAllowed,
-                    interactor.isSatelliteProvisioned,
-                    interactor.isWifiActive,
-                    airplaneModeRepository.isAirplaneMode
-                ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
-                    isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
-                }
-            }
-        }
 
     // This adds a 10 seconds delay before showing the icon
-    private val shouldActuallyShowIcon: StateFlow<Boolean> =
-        shouldShowIcon
-            .distinctUntilChanged()
+    private val shouldShowIconForOosAfterHysteresis: StateFlow<Boolean> =
+        interactor.areAllConnectionsOutOfService
             .flatMapLatest { shouldShow ->
                 if (shouldShow) {
                     logBuffer.log(
@@ -106,11 +92,57 @@
                     flowOf(false)
                 }
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "vm",
+                columnName = COL_VISIBLE_FOR_OOS,
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    private val canShowIcon =
+        combine(
+            interactor.isSatelliteAllowed,
+            interactor.isSatelliteProvisioned,
+        ) { allowed, provisioned ->
+            allowed && provisioned
+        }
+
+    private val showIcon =
+        canShowIcon
+            .flatMapLatest { canShow ->
+                if (!canShow) {
+                    flowOf(false)
+                } else {
+                    combine(
+                        shouldShowIconForOosAfterHysteresis,
+                        interactor.connectionState,
+                        interactor.isWifiActive,
+                        airplaneModeRepository.isAirplaneMode,
+                    ) { showForOos, connectionState, isWifiActive, isAirplaneMode ->
+                        if (isWifiActive || isAirplaneMode) {
+                            false
+                        } else {
+                            showForOos ||
+                                connectionState == SatelliteConnectionState.On ||
+                                connectionState == SatelliteConnectionState.Connected
+                        }
+                    }
+                }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "vm",
+                columnName = COL_VISIBLE,
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val icon: StateFlow<Icon?> =
         combine(
-                shouldActuallyShowIcon,
+                showIcon,
                 interactor.connectionState,
                 interactor.signalStrength,
             ) { shouldShow, state, signalStrength ->
@@ -124,7 +156,7 @@
 
     override val carrierText: StateFlow<String?> =
         combine(
-                shouldActuallyShowIcon,
+                showIcon,
                 interactor.connectionState,
             ) { shouldShow, connectionState ->
                 logBuffer.log(
@@ -134,7 +166,7 @@
                         bool1 = shouldShow
                         str1 = connectionState.name
                     },
-                    { "Updating carrier text. shouldActuallyShow=$bool1 connectionState=$str1" }
+                    { "Updating carrier text. shouldShow=$bool1 connectionState=$str1" }
                 )
                 if (shouldShow) {
                     when (connectionState) {
@@ -143,25 +175,30 @@
                             context.getString(R.string.satellite_connected_carrier_text)
                         SatelliteConnectionState.Off,
                         SatelliteConnectionState.Unknown -> {
-                            null
+                            // If we're showing the satellite icon opportunistically, use the
+                            // emergency-only version of the carrier string
+                            context.getString(R.string.satellite_emergency_only_carrier_text)
                         }
                     }
                 } else {
                     null
                 }
             }
-            .onEach {
-                logBuffer.log(
-                    TAG,
-                    LogLevel.INFO,
-                    { str1 = it },
-                    { "Resulting carrier text = $str1" }
-                )
-            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLog,
+                columnPrefix = "vm",
+                columnName = COL_CARRIER_TEXT,
+                initialValue = null,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
     companion object {
         private const val TAG = "DeviceBasedSatelliteViewModel"
         private val DELAY_DURATION = 10.seconds
+
+        const val COL_VISIBLE_FOR_OOS = "visibleForOos"
+        const val COL_VISIBLE = "visible"
+        const val COL_CARRIER_TEXT = "carrierText"
     }
 }
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 1a55f7d..f5cfc8c 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
@@ -31,7 +31,7 @@
 import androidx.annotation.ArrayRes
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dump.DumpManager
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
 import com.android.systemui.tuner.TunerService
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -252,7 +253,10 @@
             }
             // Only CELLULAR networks may have underlying wifi information that's relevant to SysUI,
             // so skip the underlying network check if it's not CELLULAR.
-            if (!this.hasTransport(TRANSPORT_CELLULAR)) {
+            if (
+                !this.hasTransport(TRANSPORT_CELLULAR) &&
+                    !Flags.statusBarAlwaysCheckUnderlyingNetworks()
+            ) {
                 return mainWifiInfo
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index d46aaf4..87d0e64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -18,27 +18,16 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
-import android.annotation.IdRes
-import android.content.res.ColorStateList
-import android.graphics.drawable.GradientDrawable
 import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.Flags
-import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.dagger.SysUISingleton
 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.StatusBarIconView
-import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
+import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
 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
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
 import javax.inject.Inject
@@ -92,62 +81,22 @@
                 }
 
                 if (Flags.statusBarScreenSharingChips()) {
-                    val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
-                    val chipContext = chipView.context
-                    val chipDefaultIconView: ImageView =
-                        chipView.requireViewById(R.id.ongoing_activity_chip_icon)
-                    val chipTimeView: ChipChronometer =
-                        chipView.requireViewById(R.id.ongoing_activity_chip_time)
-                    val chipTextView: TextView =
-                        chipView.requireViewById(R.id.ongoing_activity_chip_text)
-                    val chipBackgroundView =
-                        chipView.requireViewById<ChipBackgroundContainer>(
-                            R.id.ongoing_activity_chip_background
-                        )
+                    val primaryChipView: View =
+                        view.requireViewById(R.id.ongoing_activity_chip_primary)
                     launch {
-                        viewModel.ongoingActivityChip.collect { chipModel ->
-                            when (chipModel) {
-                                is OngoingActivityChipModel.Shown -> {
-                                    // Data
-                                    setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView)
-                                    setChipMainContent(chipModel, chipTextView, chipTimeView)
-                                    chipView.setOnClickListener(chipModel.onClickListener)
-                                    updateChipPadding(
-                                        chipModel,
-                                        chipBackgroundView,
-                                        chipTextView,
-                                        chipTimeView,
-                                    )
-
-                                    // Accessibility
-                                    setChipAccessibility(chipModel, chipView, chipBackgroundView)
-
-                                    // Colors
-                                    val textColor = chipModel.colors.text(chipContext)
-                                    chipDefaultIconView.imageTintList =
-                                        ColorStateList.valueOf(textColor)
-                                    chipBackgroundView.getCustomIconView()?.imageTintList =
-                                        ColorStateList.valueOf(textColor)
-                                    chipTimeView.setTextColor(textColor)
-                                    chipTextView.setTextColor(textColor)
-                                    (chipBackgroundView.background as GradientDrawable).color =
-                                        chipModel.colors.background(chipContext)
-
-                                    // Notify listeners
+                        viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
+                            OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView)
+                            when (primaryChipModel) {
+                                is OngoingActivityChipModel.Shown ->
                                     listener.onOngoingActivityStatusChanged(
                                         hasOngoingActivity = true,
                                         shouldAnimate = true,
                                     )
-                                }
-                                is OngoingActivityChipModel.Hidden -> {
-                                    // The Chronometer should be stopped to prevent leaks -- see
-                                    // b/192243808 and [Chronometer.start].
-                                    chipTimeView.stop()
+                                is OngoingActivityChipModel.Hidden ->
                                     listener.onOngoingActivityStatusChanged(
                                         hasOngoingActivity = false,
-                                        shouldAnimate = chipModel.shouldAnimate,
+                                        shouldAnimate = primaryChipModel.shouldAnimate,
                                     )
-                                }
                             }
                         }
                     }
@@ -164,214 +113,6 @@
         }
     }
 
-    private fun setChipIcon(
-        chipModel: OngoingActivityChipModel.Shown,
-        backgroundView: ChipBackgroundContainer,
-        defaultIconView: ImageView,
-    ) {
-        // Always remove any previously set custom icon. If we have a new custom icon, we'll re-add
-        // it.
-        backgroundView.removeView(backgroundView.getCustomIconView())
-
-        when (val icon = chipModel.icon) {
-            null -> {
-                defaultIconView.visibility = View.GONE
-            }
-            is OngoingActivityChipModel.ChipIcon.Basic -> {
-                IconViewBinder.bind(icon.impl, defaultIconView)
-                defaultIconView.visibility = View.VISIBLE
-            }
-            is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
-                // Hide the default icon since we'll show this custom icon instead.
-                defaultIconView.visibility = View.GONE
-
-                // Add the new custom icon:
-                // 1. Set up the right visual params.
-                val iconView = icon.impl
-                with(iconView) {
-                    id = CUSTOM_ICON_VIEW_ID
-                    // TODO(b/354930838): Update the content description to not include "phone" and
-                    // maybe include the app name.
-                    contentDescription =
-                        context.resources.getString(R.string.ongoing_phone_call_content_description)
-                }
-
-                // 2. If we just reinflated the view, we may need to detach the icon view from the
-                // old chip before we reattach it to the new one.
-                // See also: NotificationIconContainerViewBinder#bindIcons.
-                val currentParent = iconView.parent as? ViewGroup
-                if (currentParent != null && currentParent != backgroundView) {
-                    currentParent.removeView(iconView)
-                    currentParent.removeTransientView(iconView)
-                }
-
-                // 3: Add the icon as the starting view.
-                backgroundView.addView(
-                    iconView,
-                    /* index= */ 0,
-                    generateCustomIconLayoutParams(iconView),
-                )
-            }
-        }
-    }
-
-    private fun View.getCustomIconView(): StatusBarIconView? {
-        return this.findViewById(CUSTOM_ICON_VIEW_ID)
-    }
-
-    private fun generateCustomIconLayoutParams(iconView: ImageView): FrameLayout.LayoutParams {
-        val customIconSize =
-            iconView.context.resources.getDimensionPixelSize(
-                R.dimen.ongoing_activity_chip_embedded_padding_icon_size
-            )
-        return FrameLayout.LayoutParams(customIconSize, customIconSize)
-    }
-
-    private fun setChipMainContent(
-        chipModel: OngoingActivityChipModel.Shown,
-        chipTextView: TextView,
-        chipTimeView: ChipChronometer,
-    ) {
-        when (chipModel) {
-            is OngoingActivityChipModel.Shown.Countdown -> {
-                chipTextView.text = chipModel.secondsUntilStarted.toString()
-                chipTextView.visibility = View.VISIBLE
-
-                // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
-                // [Chronometer.start].
-                chipTimeView.stop()
-                chipTimeView.visibility = View.GONE
-            }
-            is OngoingActivityChipModel.Shown.Timer -> {
-                ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
-                chipTimeView.visibility = View.VISIBLE
-
-                chipTextView.visibility = View.GONE
-            }
-            is OngoingActivityChipModel.Shown.IconOnly -> {
-                chipTextView.visibility = View.GONE
-                // The Chronometer should be stopped to prevent leaks -- see b/192243808 and
-                // [Chronometer.start].
-                chipTimeView.stop()
-                chipTimeView.visibility = View.GONE
-            }
-        }
-    }
-
-    private fun updateChipPadding(
-        chipModel: OngoingActivityChipModel.Shown,
-        backgroundView: View,
-        chipTextView: TextView,
-        chipTimeView: ChipChronometer,
-    ) {
-        if (chipModel.icon != null) {
-            if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
-                // If the icon is a custom [StatusBarIconView], then it should've come from
-                // `Notification.smallIcon`, which is required to embed its own paddings. We need to
-                // adjust the other paddings to make everything look good :)
-                backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon()
-                chipTextView.setTextPaddingForEmbeddedPaddingIcon()
-                chipTimeView.setTextPaddingForEmbeddedPaddingIcon()
-            } else {
-                backgroundView.setBackgroundPaddingForNormalIcon()
-                chipTextView.setTextPaddingForNormalIcon()
-                chipTimeView.setTextPaddingForNormalIcon()
-            }
-        } else {
-            backgroundView.setBackgroundPaddingForNoIcon()
-            chipTextView.setTextPaddingForNoIcon()
-            chipTimeView.setTextPaddingForNoIcon()
-        }
-    }
-
-    private fun View.setTextPaddingForEmbeddedPaddingIcon() {
-        val newPaddingEnd =
-            context.resources.getDimensionPixelSize(
-                R.dimen.ongoing_activity_chip_text_end_padding_for_embedded_padding_icon
-            )
-        setPaddingRelative(
-            // The icon should embed enough padding between the icon and time view.
-            /* start= */ 0,
-            this.paddingTop,
-            newPaddingEnd,
-            this.paddingBottom,
-        )
-    }
-
-    private fun View.setTextPaddingForNormalIcon() {
-        this.setPaddingRelative(
-            this.context.resources.getDimensionPixelSize(
-                R.dimen.ongoing_activity_chip_icon_text_padding
-            ),
-            paddingTop,
-            // The background view will contain the right end padding.
-            /* end= */ 0,
-            paddingBottom,
-        )
-    }
-
-    private fun View.setTextPaddingForNoIcon() {
-        // The background view will have even start & end paddings, so we don't want the text view
-        // to add any additional padding.
-        this.setPaddingRelative(/* start= */ 0, paddingTop, /* end= */ 0, paddingBottom)
-    }
-
-    private fun View.setBackgroundPaddingForEmbeddedPaddingIcon() {
-        val sidePadding =
-            context.resources.getDimensionPixelSize(
-                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
-            )
-        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,
-        )
-    }
-
-    private fun View.setBackgroundPaddingForNoIcon() {
-        // The padding for the normal icon is also appropriate for no icon.
-        setBackgroundPaddingForNormalIcon()
-    }
-
-    private fun setChipAccessibility(
-        chipModel: OngoingActivityChipModel.Shown,
-        chipView: View,
-        chipBackgroundView: View,
-    ) {
-        when (chipModel) {
-            is OngoingActivityChipModel.Shown.Countdown -> {
-                // Set as assertive so talkback will announce the countdown
-                chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
-            }
-            is OngoingActivityChipModel.Shown.Timer,
-            is OngoingActivityChipModel.Shown.IconOnly -> {
-                chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
-            }
-        }
-        // Clickable chips need to be a minimum size for accessibility purposes, but let
-        // non-clickable chips be smaller.
-        if (chipModel.onClickListener != null) {
-            chipBackgroundView.minimumWidth =
-                chipBackgroundView.context.resources.getDimensionPixelSize(
-                    R.dimen.min_clickable_item_size
-                )
-        } else {
-            chipBackgroundView.minimumWidth = 0
-        }
-    }
-
     private fun animateLightsOutView(view: View, visible: Boolean) {
         view.animate().cancel()
 
@@ -401,10 +142,6 @@
             )
             .start()
     }
-
-    companion object {
-        @IdRes private val CUSTOM_ICON_VIEW_ID = R.id.ongoing_activity_chip_custom_icon
-    }
 }
 
 /** Listener for various events that may affect the status bar's visibility. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index d6c3834..5474231 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -64,8 +64,11 @@
     /** Emits whenever a transition from lockscreen to dream has started. */
     val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
 
-    /** The ongoing activity chip that should be shown on the left-hand side of the status bar. */
-    val ongoingActivityChip: StateFlow<OngoingActivityChipModel>
+    /**
+     * The ongoing activity chip that should be primarily shown on the left-hand side of the status
+     * bar. If there are multiple ongoing activity chips, this one should take priority.
+     */
+    val primaryOngoingActivityChip: StateFlow<OngoingActivityChipModel>
 
     /**
      * True if the current scene can show the home status bar (aka this status bar), and false if
@@ -108,7 +111,7 @@
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
 
-    override val ongoingActivityChip = ongoingActivityChipsViewModel.chip
+    override val primaryOngoingActivityChip = ongoingActivityChipsViewModel.primaryChip
 
     override val isHomeStatusBarAllowedByScene: StateFlow<Boolean> =
         combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index 152d181..7163e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -101,29 +101,20 @@
 
     private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel =
         WifiNetworkModel.Active(
-            networkId = DEMO_NET_ID,
             isValidated = validated ?: true,
             level = level ?: 0,
             ssid = ssid ?: DEMO_NET_SSID,
             hotspotDeviceType = hotspotDeviceType,
-
-            // These fields below aren't supported in demo mode, since they aren't needed to satisfy
-            // the interface.
-            isPasspointAccessPoint = false,
-            isOnlineSignUpForPasspointAccessPoint = false,
-            passpointProviderFriendlyName = null,
         )
 
     private fun FakeWifiEventModel.CarrierMerged.toCarrierMergedModel(): WifiNetworkModel =
         WifiNetworkModel.CarrierMerged(
-            networkId = DEMO_NET_ID,
             subscriptionId = subscriptionId,
             level = level,
             numberOfLevels = numberOfLevels,
         )
 
     companion object {
-        private const val DEMO_NET_ID = 1234
         private const val DEMO_NET_SSID = "Demo SSID"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 885abca..b6e73e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -29,8 +29,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.table.TableLogBuffer
@@ -75,7 +73,6 @@
 class WifiRepositoryImpl
 @Inject
 constructor(
-    featureFlags: FeatureFlags,
     @Application private val scope: CoroutineScope,
     @Main private val mainExecutor: Executor,
     @Background private val bgDispatcher: CoroutineDispatcher,
@@ -90,8 +87,6 @@
             mainExecutor.execute { it.currentState = Lifecycle.State.CREATED }
         }
 
-    private val isInstantTetherEnabled = featureFlags.isEnabled(Flags.INSTANT_TETHER)
-
     private var wifiPickerTracker: WifiPickerTracker? = null
 
     private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = run {
@@ -109,16 +104,11 @@
                             val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection
                             logOnWifiEntriesChanged(connectedEntry)
 
+                            val activeNetworks = wifiPickerTracker?.activeWifiEntries ?: emptyList()
                             val secondaryNetworks =
-                                if (featureFlags.isEnabled(Flags.WIFI_SECONDARY_NETWORKS)) {
-                                    val activeNetworks =
-                                        wifiPickerTracker?.activeWifiEntries ?: emptyList()
-                                    activeNetworks
-                                        .filter { it != connectedEntry && !it.isPrimaryNetwork }
-                                        .map { it.toWifiNetworkModel() }
-                                } else {
-                                    emptyList()
-                                }
+                                activeNetworks
+                                    .filter { it != connectedEntry && !it.isPrimaryNetwork }
+                                    .map { it.toWifiNetworkModel() }
 
                             // [WifiPickerTracker.connectedWifiEntry] will return the same instance
                             // but with updated internals. For example, when its validation status
@@ -130,7 +120,8 @@
                             // into our internal model immediately. [toWifiNetworkModel] always
                             // returns a new instance, so the flow is guaranteed to emit.
                             send(
-                                newPrimaryNetwork = connectedEntry?.toPrimaryWifiNetworkModel()
+                                newPrimaryNetwork =
+                                    connectedEntry?.toPrimaryWifiNetworkModel()
                                         ?: WIFI_NETWORK_DEFAULT,
                                 newSecondaryNetworks = secondaryNetworks,
                                 newIsDefault = connectedEntry?.isDefaultNetwork ?: false,
@@ -259,7 +250,6 @@
             WifiNetworkModel.Invalid(CARRIER_MERGED_INVALID_SUB_ID_REASON)
         } else {
             WifiNetworkModel.CarrierMerged(
-                networkId = NETWORK_ID,
                 subscriptionId = this.subscriptionId,
                 level = this.level,
                 // WifiManager APIs to calculate the signal level start from 0, so
@@ -270,33 +260,34 @@
     }
 
     private fun WifiEntry.convertNormalToModel(): WifiNetworkModel {
-        if (this.level == WIFI_LEVEL_UNREACHABLE || this.level !in WIFI_LEVEL_MIN..WIFI_LEVEL_MAX) {
+        // WifiEntry instance values aren't guaranteed to be stable between method calls because
+        // WifiPickerTracker is continuously updating the same object. Save the level in a local
+        // variable so that checking the level validity here guarantees that the level will still be
+        // valid when we create the `WifiNetworkModel.Active` instance later. Otherwise, the level
+        // could be valid here but become invalid later, and `WifiNetworkModel.Active` will throw
+        // an exception. See b/362384551.
+        val currentLevel = this.level
+        if (
+            currentLevel == WIFI_LEVEL_UNREACHABLE ||
+                currentLevel !in WIFI_LEVEL_MIN..WIFI_LEVEL_MAX
+        ) {
             // If our level means the network is unreachable or the level is otherwise invalid, we
             // don't have an active network.
             return WifiNetworkModel.Inactive
         }
 
         val hotspotDeviceType =
-            if (isInstantTetherEnabled && this is HotspotNetworkEntry) {
+            if (this is HotspotNetworkEntry) {
                 this.deviceType.toHotspotDeviceType()
             } else {
                 WifiNetworkModel.HotspotDeviceType.NONE
             }
 
         return WifiNetworkModel.Active(
-            networkId = NETWORK_ID,
             isValidated = this.hasInternetAccess(),
-            level = this.level,
+            level = currentLevel,
             ssid = this.title,
             hotspotDeviceType = hotspotDeviceType,
-            // With WifiTrackerLib, [WifiEntry.title] will appropriately fetch the  SSID for
-            // typical wifi networks *and* passpoint/OSU APs. So, the AP-specific values can
-            // always be false/null in this repository.
-            // TODO(b/292534484): Remove these fields from the wifi network model once this
-            //  repository is fully enabled.
-            isPasspointAccessPoint = false,
-            isOnlineSignUpForPasspointAccessPoint = false,
-            passpointProviderFriendlyName = null,
         )
     }
 
@@ -408,7 +399,6 @@
     class Factory
     @Inject
     constructor(
-        private val featureFlags: FeatureFlags,
         @Application private val scope: CoroutineScope,
         @Main private val mainExecutor: Executor,
         @Background private val bgDispatcher: CoroutineDispatcher,
@@ -418,7 +408,6 @@
     ) {
         fun create(wifiManager: WifiManager): WifiRepositoryImpl {
             return WifiRepositoryImpl(
-                featureFlags,
                 scope,
                 mainExecutor,
                 bgDispatcher,
@@ -439,19 +428,5 @@
         val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
 
         private const val TAG = "WifiTrackerLibInputLog"
-
-        /**
-         * [WifiNetworkModel.Active.networkId] is only used at the repository layer. It's used by
-         * [WifiRepositoryImpl], which tracks the ID in order to correctly apply the framework
-         * callbacks within the repository.
-         *
-         * Since this class does not need to manually apply framework callbacks and since the
-         * network ID is not used beyond the repository, it's safe to use an invalid ID in this
-         * repository.
-         *
-         * The [WifiNetworkModel.Active.networkId] field should be deleted once we've fully migrated
-         * to [WifiRepositoryImpl].
-         */
-        private const val NETWORK_ID = -1
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 110e339..c0b0c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -83,8 +83,6 @@
                 is WifiNetworkModel.CarrierMerged -> null
                 is WifiNetworkModel.Active ->
                     when {
-                        info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
-                            info.passpointProviderFriendlyName
                         info.hasValidSsid() -> info.ssid
                         else -> null
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
index 7078a2e..39842fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.log.table.TableRowLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType
+import com.android.wifitrackerlib.WifiEntry
 
 /** Provides information about the current wifi network. */
 sealed class WifiNetworkModel : Diffable<WifiNetworkModel> {
@@ -38,6 +39,7 @@
      */
     object Unavailable : WifiNetworkModel() {
         override fun toString() = "WifiNetwork.Unavailable"
+
         override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
             if (prevVal is Unavailable) {
                 return
@@ -48,16 +50,12 @@
 
         override fun logFull(row: TableRowLogger) {
             row.logChange(COL_NETWORK_TYPE, TYPE_UNAVAILABLE)
-            row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
             row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
             row.logChange(COL_VALIDATED, false)
             row.logChange(COL_LEVEL, LEVEL_DEFAULT)
             row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
             row.logChange(COL_SSID, null)
             row.logChange(COL_HOTSPOT, null)
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
-            row.logChange(COL_ONLINE_SIGN_UP, false)
-            row.logChange(COL_PASSPOINT_NAME, null)
         }
     }
 
@@ -67,6 +65,7 @@
         val invalidReason: String,
     ) : WifiNetworkModel() {
         override fun toString() = "WifiNetwork.Invalid[$invalidReason]"
+
         override fun logDiffs(prevVal: WifiNetworkModel, row: TableRowLogger) {
             if (prevVal !is Invalid) {
                 logFull(row)
@@ -80,16 +79,12 @@
 
         override fun logFull(row: TableRowLogger) {
             row.logChange(COL_NETWORK_TYPE, "$TYPE_UNAVAILABLE $invalidReason")
-            row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
             row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
             row.logChange(COL_VALIDATED, false)
             row.logChange(COL_LEVEL, LEVEL_DEFAULT)
             row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
             row.logChange(COL_SSID, null)
             row.logChange(COL_HOTSPOT, null)
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
-            row.logChange(COL_ONLINE_SIGN_UP, false)
-            row.logChange(COL_PASSPOINT_NAME, null)
         }
     }
 
@@ -108,16 +103,12 @@
 
         override fun logFull(row: TableRowLogger) {
             row.logChange(COL_NETWORK_TYPE, TYPE_INACTIVE)
-            row.logChange(COL_NETWORK_ID, NETWORK_ID_DEFAULT)
             row.logChange(COL_SUB_ID, SUB_ID_DEFAULT)
             row.logChange(COL_VALIDATED, false)
             row.logChange(COL_LEVEL, LEVEL_DEFAULT)
             row.logChange(COL_NUM_LEVELS, NUM_LEVELS_DEFAULT)
             row.logChange(COL_SSID, null)
             row.logChange(COL_HOTSPOT, null)
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
-            row.logChange(COL_ONLINE_SIGN_UP, false)
-            row.logChange(COL_PASSPOINT_NAME, null)
         }
     }
 
@@ -129,14 +120,6 @@
      */
     data class CarrierMerged(
         /**
-         * The [android.net.Network.netId] we received from
-         * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
-         *
-         * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
-         */
-        val networkId: Int,
-
-        /**
          * The subscription ID that this connection represents.
          *
          * Comes from [android.net.wifi.WifiInfo.getSubscriptionId].
@@ -154,7 +137,8 @@
     ) : WifiNetworkModel() {
         init {
             require(level in MIN_VALID_LEVEL..numberOfLevels) {
-                "0 <= wifi level <= $numberOfLevels required; level was $level"
+                "CarrierMerged: $MIN_VALID_LEVEL <= wifi level <= $numberOfLevels required; " +
+                    "level was $level"
             }
             require(subscriptionId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 "subscription ID cannot be invalid"
@@ -167,9 +151,6 @@
                 return
             }
 
-            if (prevVal.networkId != networkId) {
-                row.logChange(COL_NETWORK_ID, networkId)
-            }
             if (prevVal.subscriptionId != subscriptionId) {
                 row.logChange(COL_SUB_ID, subscriptionId)
             }
@@ -183,29 +164,17 @@
 
         override fun logFull(row: TableRowLogger) {
             row.logChange(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED)
-            row.logChange(COL_NETWORK_ID, networkId)
             row.logChange(COL_SUB_ID, subscriptionId)
             row.logChange(COL_VALIDATED, true)
             row.logChange(COL_LEVEL, level)
             row.logChange(COL_NUM_LEVELS, numberOfLevels)
             row.logChange(COL_SSID, null)
             row.logChange(COL_HOTSPOT, null)
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, false)
-            row.logChange(COL_ONLINE_SIGN_UP, false)
-            row.logChange(COL_PASSPOINT_NAME, null)
         }
     }
 
     /** Provides information about an active wifi network. */
     data class Active(
-        /**
-         * The [android.net.Network.netId] we received from
-         * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
-         *
-         * Importantly, **not** [android.net.wifi.WifiInfo.getNetworkId].
-         */
-        val networkId: Int,
-
         /** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */
         val isValidated: Boolean = false,
 
@@ -220,19 +189,11 @@
          * isn't a hotspot connection.
          */
         val hotspotDeviceType: HotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
-
-        /** See [android.net.wifi.WifiInfo.isPasspointAp]. */
-        val isPasspointAccessPoint: Boolean = false,
-
-        /** See [android.net.wifi.WifiInfo.isOsuAp]. */
-        val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
-
-        /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
-        val passpointProviderFriendlyName: String? = null,
     ) : WifiNetworkModel() {
         init {
             require(level in MIN_VALID_LEVEL..MAX_VALID_LEVEL) {
-                "0 <= wifi level <= 4 required; level was $level"
+                "Active: $MIN_VALID_LEVEL <= wifi level <= $MAX_VALID_LEVEL required; " +
+                    "level was $level"
             }
         }
 
@@ -247,9 +208,6 @@
                 return
             }
 
-            if (prevVal.networkId != networkId) {
-                row.logChange(COL_NETWORK_ID, networkId)
-            }
             if (prevVal.isValidated != isValidated) {
                 row.logChange(COL_VALIDATED, isValidated)
             }
@@ -262,68 +220,25 @@
             if (prevVal.hotspotDeviceType != hotspotDeviceType) {
                 row.logChange(COL_HOTSPOT, hotspotDeviceType.name)
             }
-
-            // TODO(b/238425913): The passpoint-related values are frequently never used, so it
-            //   would be great to not log them when they're not used.
-            if (prevVal.isPasspointAccessPoint != isPasspointAccessPoint) {
-                row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
-            }
-            if (
-                prevVal.isOnlineSignUpForPasspointAccessPoint !=
-                    isOnlineSignUpForPasspointAccessPoint
-            ) {
-                row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
-            }
-            if (prevVal.passpointProviderFriendlyName != passpointProviderFriendlyName) {
-                row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
-            }
         }
 
         override fun logFull(row: TableRowLogger) {
             row.logChange(COL_NETWORK_TYPE, TYPE_ACTIVE)
-            row.logChange(COL_NETWORK_ID, networkId)
             row.logChange(COL_SUB_ID, null)
             row.logChange(COL_VALIDATED, isValidated)
             row.logChange(COL_LEVEL, level)
             row.logChange(COL_NUM_LEVELS, null)
             row.logChange(COL_SSID, ssid)
             row.logChange(COL_HOTSPOT, hotspotDeviceType.name)
-            row.logChange(COL_PASSPOINT_ACCESS_POINT, isPasspointAccessPoint)
-            row.logChange(COL_ONLINE_SIGN_UP, isOnlineSignUpForPasspointAccessPoint)
-            row.logChange(COL_PASSPOINT_NAME, passpointProviderFriendlyName)
-        }
-
-        override fun toString(): String {
-            // Only include the passpoint-related values in the string if we have them. (Most
-            // networks won't have them so they'll be mostly clutter.)
-            val passpointString =
-                if (
-                    isPasspointAccessPoint ||
-                        isOnlineSignUpForPasspointAccessPoint ||
-                        passpointProviderFriendlyName != null
-                ) {
-                    ", isPasspointAp=$isPasspointAccessPoint, " +
-                        "isOnlineSignUpForPasspointAp=$isOnlineSignUpForPasspointAccessPoint, " +
-                        "passpointName=$passpointProviderFriendlyName"
-                } else {
-                    ""
-                }
-
-            return "WifiNetworkModel.Active(networkId=$networkId, isValidated=$isValidated, " +
-                "level=$level, ssid=$ssid$passpointString)"
         }
 
         companion object {
-            // TODO(b/292534484): Use [com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX] instead
-            // once the migration to WifiTrackerLib is complete.
-            @VisibleForTesting internal const val MAX_VALID_LEVEL = 4
+            @VisibleForTesting internal const val MAX_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MAX
         }
     }
 
     companion object {
-        // TODO(b/292534484): Use [com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN] instead
-        // once the migration to WifiTrackerLib is complete.
-        @VisibleForTesting internal const val MIN_VALID_LEVEL = 0
+        @VisibleForTesting internal const val MIN_VALID_LEVEL = WifiEntry.WIFI_LEVEL_MIN
     }
 
     /**
@@ -367,18 +282,13 @@
 const val TYPE_ACTIVE = "Active"
 
 const val COL_NETWORK_TYPE = "type"
-const val COL_NETWORK_ID = "networkId"
 const val COL_SUB_ID = "subscriptionId"
 const val COL_VALIDATED = "isValidated"
 const val COL_LEVEL = "level"
 const val COL_NUM_LEVELS = "maxLevel"
 const val COL_SSID = "ssid"
 const val COL_HOTSPOT = "hotspot"
-const val COL_PASSPOINT_ACCESS_POINT = "isPasspointAccessPoint"
-const val COL_ONLINE_SIGN_UP = "isOnlineSignUpForPasspointAccessPoint"
-const val COL_PASSPOINT_NAME = "passpointProviderFriendlyName"
 
 val LEVEL_DEFAULT: String? = null
 val NUM_LEVELS_DEFAULT: String? = null
-val NETWORK_ID_DEFAULT: String? = null
 val SUB_ID_DEFAULT: String? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 3786958..f37393a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -40,13 +40,14 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.phone.ExpandHeadsUpOnInlineReply;
 import com.android.systemui.util.ListenerSet;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.time.SystemClock;
 
+import org.jetbrains.annotations.NotNull;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -191,12 +192,14 @@
      * enough and needs to be kept around.
      * @param key the key of the notification to remove
      * @param releaseImmediately force a remove regardless of earliest removal time
+     * @param reason reason for removing the notification
      * @return true if notification is removed, false otherwise
      */
     @Override
-    public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
+    public boolean removeNotification(@NotNull String key, boolean releaseImmediately,
+            @NonNull String reason) {
         final boolean isWaiting = mAvalancheController.isWaiting(key);
-        mLogger.logRemoveNotification(key, releaseImmediately, isWaiting);
+        mLogger.logRemoveNotification(key, releaseImmediately, isWaiting, reason);
 
         if (mAvalancheController.isWaiting(key)) {
             removeEntry(key, "removeNotification (isWaiting)");
@@ -204,6 +207,7 @@
         }
         HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
         if (headsUpEntry == null) {
+            mLogger.logNullEntry(key, reason);
             return true;
         }
         if (releaseImmediately) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 775f34d..d281920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -174,15 +174,24 @@
         IndentingPrintWriter ipw = asIndenting(pw);
         ipw.println("BatteryController state:");
         ipw.increaseIndent();
-        ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery);
-        ipw.print("mLevel="); ipw.println(mLevel);
-        ipw.print("mPluggedIn="); ipw.println(mPluggedIn);
-        ipw.print("mCharging="); ipw.println(mCharging);
-        ipw.print("mCharged="); ipw.println(mCharged);
-        ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender);
-        ipw.print("mIsIncompatibleCharging="); ipw.println(mIsIncompatibleCharging);
-        ipw.print("mPowerSave="); ipw.println(mPowerSave);
-        ipw.print("mStateUnknown="); ipw.println(mStateUnknown);
+        ipw.print("mHasReceivedBattery=");
+        ipw.println(mHasReceivedBattery);
+        ipw.print("mLevel=");
+        ipw.println(mLevel);
+        ipw.print("mPluggedIn=");
+        ipw.println(mPluggedIn);
+        ipw.print("mCharging=");
+        ipw.println(mCharging);
+        ipw.print("mCharged=");
+        ipw.println(mCharged);
+        ipw.print("mIsBatteryDefender=");
+        ipw.println(mIsBatteryDefender);
+        ipw.print("mIsIncompatibleCharging=");
+        ipw.println(mIsIncompatibleCharging);
+        ipw.print("mPowerSave=");
+        ipw.println(mPowerSave);
+        ipw.print("mStateUnknown=");
+        ipw.println(mStateUnknown);
         ipw.println("Callbacks:------------------");
         // Since the above lines are already indented, we need to indent twice for the callbacks.
         ipw.increaseIndent();
@@ -272,7 +281,7 @@
             }
 
             int chargingStatus = intent.getIntExtra(EXTRA_CHARGING_STATUS, CHARGING_POLICY_DEFAULT);
-            boolean isBatteryDefender = chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE;
+            boolean isBatteryDefender = isBatteryDefenderMode(chargingStatus);
             if (isBatteryDefender != mIsBatteryDefender) {
                 mIsBatteryDefender = isBatteryDefender;
                 fireIsBatteryDefenderChanged();
@@ -359,11 +368,24 @@
         return mPluggedChargingSource == BatteryManager.BATTERY_PLUGGED_WIRELESS;
     }
 
-    public boolean isBatteryDefender() {
+    /**
+     * This method is used for tests only. Returns whether the device is in battery defender
+     * mode.
+     */
+    @VisibleForTesting
+    protected boolean isBatteryDefender() {
         return mIsBatteryDefender;
     }
 
     /**
+     * Checks whether the device is in battery defender mode based on the current charging
+     * status. This method can be overridden to have a different definition for its subclasses.
+     */
+    protected boolean isBatteryDefenderMode(int chargingStatus) {
+        return chargingStatus == CHARGING_POLICY_ADAPTIVE_LONGLIFE;
+    }
+
+    /**
      * Returns whether the charging adapter is incompatible.
      */
     public boolean isIncompatibleCharging() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
index 1224275..e29e069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -21,13 +21,12 @@
 import android.content.SharedPreferences
 import android.provider.Settings
 import android.util.Log
-import com.android.systemui.res.R
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.dagger.ControlsComponent
 import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
-import com.android.systemui.statusbar.phone.AutoTileManager
 import com.android.systemui.statusbar.policy.DeviceControlsController.Callback
 import com.android.systemui.util.settings.SecureSettings
 import javax.inject.Inject
@@ -35,14 +34,16 @@
 /**
  * Watches for Device Controls QS Tile activation, which can happen in two ways:
  * <ol>
- *   <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high
- *       priority position.
- *   <li>Device controls service becomes available - For non-migrated users, create a tile and
- *       place at the end of active tiles, and initiate seeding where possible.
+ * <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high priority
+ *   position.
+ * <li>Device controls service becomes available - For non-migrated users, create a tile and place
+ *   at the end of active tiles, and initiate seeding where possible.
  * </ol>
  */
 @SysUISingleton
-public class DeviceControlsControllerImpl @Inject constructor(
+public class DeviceControlsControllerImpl
+@Inject
+constructor(
     private val context: Context,
     private val controlsComponent: ControlsComponent,
     private val userContextProvider: UserContextProvider,
@@ -52,13 +53,14 @@
     private var callback: Callback? = null
     internal var position: Int? = null
 
-    private val listingCallback = object : ControlsListingController.ControlsListingCallback {
-        override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
-            if (!serviceInfos.isEmpty()) {
-                seedFavorites(serviceInfos)
+    private val listingCallback =
+        object : ControlsListingController.ControlsListingCallback {
+            override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+                if (!serviceInfos.isEmpty()) {
+                    seedFavorites(serviceInfos)
+                }
             }
         }
-    }
 
     companion object {
         private const val TAG = "DeviceControlsControllerImpl"
@@ -80,7 +82,7 @@
     }
 
     /**
-     * This migration logic assumes that something like [AutoTileManager] is tracking state
+     * This migration logic assumes that something like [AutoAddTracker] is tracking state
      * externally, and won't call this method after receiving a response via
      * [Callback#onControlsUpdate], once per user. Otherwise the calculated position may be
      * incorrect.
@@ -118,16 +120,19 @@
     }
 
     /**
-     * See if any available control service providers match one of the preferred components. If
-     * they do, and there are no current favorites for that component, query the preferred
-     * component for a limited number of suggested controls.
+     * See if any available control service providers match one of the preferred components. If they
+     * do, and there are no current favorites for that component, query the preferred component for
+     * a limited number of suggested controls.
      */
     private fun seedFavorites(serviceInfos: List<ControlsServiceInfo>) {
-        val preferredControlsPackages = context.getResources().getStringArray(
-            R.array.config_controlsPreferredPackages)
+        val preferredControlsPackages =
+            context.getResources().getStringArray(R.array.config_controlsPreferredPackages)
 
-        val prefs = userContextProvider.userContext.getSharedPreferences(
-            PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+        val prefs =
+            userContextProvider.userContext.getSharedPreferences(
+                PREFS_CONTROLS_FILE,
+                Context.MODE_PRIVATE
+            )
         val seededPackages =
             prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet()) ?: emptySet()
 
@@ -157,21 +162,22 @@
         if (componentsToSeed.isEmpty()) return
 
         controlsController.seedFavoritesForComponents(
-                componentsToSeed,
-                { response ->
-                    Log.d(TAG, "Controls seeded: $response")
-                    if (response.accepted) {
-                        addPackageToSeededSet(prefs, response.packageName)
-                        if (position == null) {
-                            position = QS_DEFAULT_POSITION
-                        }
-                        fireControlsUpdate()
-
-                        controlsComponent.getControlsListingController().ifPresent {
-                            it.removeCallback(listingCallback)
-                        }
+            componentsToSeed,
+            { response ->
+                Log.d(TAG, "Controls seeded: $response")
+                if (response.accepted) {
+                    addPackageToSeededSet(prefs, response.packageName)
+                    if (position == null) {
+                        position = QS_DEFAULT_POSITION
                     }
-                })
+                    fireControlsUpdate()
+
+                    controlsComponent.getControlsListingController().ifPresent {
+                        it.removeCallback(listingCallback)
+                    }
+                }
+            }
+        )
     }
 
     private fun addPackageToSeededSet(prefs: SharedPreferences, pkg: String) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
index fcf77d5..04fe6b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.kt
@@ -96,9 +96,10 @@
      *
      * @param key the key of the notification to remove
      * @param releaseImmediately force a remove regardless of earliest removal time
+     * @param reason reason for removing the notification
      * @return true if notification is removed, false otherwise
      */
-    fun removeNotification(key: String, releaseImmediately: Boolean): Boolean
+    fun removeNotification(key: String, releaseImmediately: Boolean, reason: String): Boolean
 
     /**
      * Try to remove the notification. May not succeed if the notification has not been shown long
@@ -107,9 +108,15 @@
      * @param key the key of the notification to remove
      * @param releaseImmediately force a remove regardless of earliest removal time
      * @param animate if true, animate the removal
+     * @param reason reason for removing the notification
      * @return true if notification is removed, false otherwise
      */
-    fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean): Boolean
+    fun removeNotification(
+        key: String,
+        releaseImmediately: Boolean,
+        animate: Boolean,
+        reason: String
+    ): Boolean
 
     /** Clears all managed notifications. */
     fun releaseAllImmediately()
@@ -246,11 +253,16 @@
 
     override fun removeListener(listener: OnHeadsUpChangedListener) {}
 
-    override fun removeNotification(key: String, releaseImmediately: Boolean) = false
-
-    override fun removeNotification(key: String, releaseImmediately: Boolean, animate: Boolean) =
+    override fun removeNotification(key: String, releaseImmediately: Boolean, reason: String) =
         false
 
+    override fun removeNotification(
+        key: String,
+        releaseImmediately: Boolean,
+        animate: Boolean,
+        reason: String
+    ) = false
+
     override fun setAnimationStateHandler(handler: AnimationStateHandler) {}
 
     override fun setExpanded(entry: NotificationEntry, expanded: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 80c595f..600270c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -16,244 +16,291 @@
 
 package com.android.systemui.statusbar.policy
 
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel.INFO
 import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.logKey
 import javax.inject.Inject
 
 /** Logger for [HeadsUpManager]. */
-class HeadsUpManagerLogger @Inject constructor(
-    @NotificationHeadsUpLog private val buffer: LogBuffer
-) {
+class HeadsUpManagerLogger
+@Inject
+constructor(@NotificationHeadsUpLog private val buffer: LogBuffer) {
     fun logPackageSnoozed(snoozeKey: String) {
-        buffer.log(TAG, INFO, {
-            str1 = snoozeKey
-        }, {
-            "package snoozed $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package snoozed $str1" })
     }
 
     fun logPackageUnsnoozed(snoozeKey: String) {
-        buffer.log(TAG, INFO, {
-            str1 = snoozeKey
-        }, {
-            "package unsnoozed $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package unsnoozed $str1" })
     }
 
     fun logIsSnoozedReturned(snoozeKey: String) {
-        buffer.log(TAG, INFO, {
-            str1 = snoozeKey
-        }, {
-            "package snoozed when queried $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = snoozeKey }, { "package snoozed when queried $str1" })
     }
 
     fun logReleaseAllImmediately() {
-        buffer.log(TAG, INFO, { }, {
-            "release all immediately"
-        })
+        buffer.log(TAG, INFO, {}, { "release all immediately" })
     }
 
     fun logShowNotificationRequest(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "request: show notification $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "request: show notification $str1" })
     }
 
-    fun logAvalancheUpdate(caller: String, isEnabled: Boolean, notifEntryKey: String,
-                           outcome: String) {
-        buffer.log(TAG, INFO, {
-            str1 = caller
-            str2 = notifEntryKey
-            str3 = outcome
-            bool1 = isEnabled
-        }, {
-            "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3"
-        })
+    fun logAvalancheUpdate(
+        caller: String,
+        isEnabled: Boolean,
+        notifEntryKey: String,
+        outcome: String
+    ) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = caller
+                str2 = notifEntryKey
+                str3 = outcome
+                bool1 = isEnabled
+            },
+            { "$str1\n\t=> AC[isEnabled:$bool1] update: $str2\n\t=> $str3" }
+        )
     }
 
-    fun logAvalancheDelete(caller: String, isEnabled: Boolean, notifEntryKey: String,
-                           outcome: String) {
-        buffer.log(TAG, INFO, {
-            str1 = caller
-            str2 = notifEntryKey
-            str3 = outcome
-            bool1 = isEnabled
-        }, {
-            "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3"
-        })
+    fun logAvalancheDelete(
+        caller: String,
+        isEnabled: Boolean,
+        notifEntryKey: String,
+        outcome: String
+    ) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = caller
+                str2 = notifEntryKey
+                str3 = outcome
+                bool1 = isEnabled
+            },
+            { "$str1\n\t=> AC[isEnabled:$bool1] delete: $str2\n\t=> $str3" }
+        )
     }
 
     fun logShowNotification(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "show notification $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "show notification $str1" })
     }
 
     fun logAutoRemoveScheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            long1 = delayMillis
-            str2 = reason
-        }, {
-            "schedule auto remove of $str1 in $long1 ms reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                long1 = delayMillis
+                str2 = reason
+            },
+            { "schedule auto remove of $str1 in $long1 ms reason: $str2" }
+        )
     }
 
     fun logAutoRemoveRequest(entry: NotificationEntry, reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            str2 = reason
-        }, {
-            "request: reschedule auto remove of $str1 reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                str2 = reason
+            },
+            { "request: reschedule auto remove of $str1 reason: $str2" }
+        )
     }
 
     fun logAutoRemoveRescheduled(entry: NotificationEntry, delayMillis: Long, reason: String) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            long1 = delayMillis
-            str2 = reason
-        }, {
-            "reschedule auto remove of $str1 in $long1 ms reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                long1 = delayMillis
+                str2 = reason
+            },
+            { "reschedule auto remove of $str1 in $long1 ms reason: $str2" }
+        )
     }
 
     fun logAutoRemoveCancelRequest(entry: NotificationEntry, reason: String?) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            str2 = reason ?: "unknown"
-        }, {
-            "request: cancel auto remove of $str1 reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                str2 = reason ?: "unknown"
+            },
+            { "request: cancel auto remove of $str1 reason: $str2" }
+        )
     }
 
     fun logAutoRemoveCanceled(entry: NotificationEntry, reason: String?) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            str2 = reason ?: "unknown"
-        }, {
-            "cancel auto remove of $str1 reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                str2 = reason ?: "unknown"
+            },
+            { "cancel auto remove of $str1 reason: $str2" }
+        )
     }
 
     fun logRemoveEntryRequest(key: String, reason: String, isWaiting: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-            str2 = reason
-            bool1 = isWaiting
-        }, {
-            "request: $str2 => remove entry $str1 isWaiting: $isWaiting"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                str2 = reason
+                bool1 = isWaiting
+            },
+            { "request: $str2 => remove entry $str1 isWaiting: $isWaiting" }
+        )
     }
 
     fun logRemoveEntry(key: String, reason: String, isWaiting: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-            str2 = reason
-            bool1 = isWaiting
-        }, {
-            "$str2 => remove entry $str1 isWaiting: $isWaiting"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                str2 = reason
+                bool1 = isWaiting
+            },
+            { "$str2 => remove entry $str1 isWaiting: $isWaiting" }
+        )
     }
 
     fun logUnpinEntryRequest(key: String) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-        }, {
-            "request: unpin entry $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = logKey(key) }, { "request: unpin entry $str1" })
     }
 
     fun logUnpinEntry(key: String) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-        }, {
-            "unpin entry $str1"
-        })
+        buffer.log(TAG, INFO, { str1 = logKey(key) }, { "unpin entry $str1" })
     }
 
-    fun logRemoveNotification(key: String, releaseImmediately: Boolean, isWaiting: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-            bool1 = releaseImmediately
-            bool2 = isWaiting
-        }, {
-            "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2"
-        })
+    fun logRemoveNotification(
+        key: String,
+        releaseImmediately: Boolean,
+        isWaiting: Boolean,
+        reason: String
+    ) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                bool1 = releaseImmediately
+                bool2 = isWaiting
+                str2 = reason
+            },
+            {
+                "remove notification $str1 releaseImmediately: $bool1 isWaiting: $bool2 " +
+                    "reason: $str2"
+            }
+        )
+    }
+
+    fun logNullEntry(key: String, reason: String) {
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                str2 = reason
+            },
+            { "remove notification $str1 when headsUpEntry is null, reason: $str2" }
+        )
     }
 
     fun logNotificationActuallyRemoved(entry: NotificationEntry) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-        }, {
-            "notification removed $str1 "
-        })
+        buffer.log(TAG, INFO, { str1 = entry.logKey }, { "notification removed $str1 " })
     }
 
     fun logUpdateNotificationRequest(key: String, alert: Boolean, hasEntry: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-            bool1 = alert
-            bool2 = hasEntry
-        }, {
-            "request: update notification $str1 alert: $bool1 hasEntry: $bool2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                bool1 = alert
+                bool2 = hasEntry
+            },
+            { "request: update notification $str1 alert: $bool1 hasEntry: $bool2" }
+        )
     }
 
     fun logUpdateNotification(key: String, alert: Boolean, hasEntry: Boolean) {
-        buffer.log(TAG, INFO, {
-            str1 = logKey(key)
-            bool1 = alert
-            bool2 = hasEntry
-        }, {
-            "update notification $str1 alert: $bool1 hasEntry: $bool2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = logKey(key)
+                bool1 = alert
+                bool2 = hasEntry
+            },
+            { "update notification $str1 alert: $bool1 hasEntry: $bool2" }
+        )
     }
 
     fun logUpdateEntry(entry: NotificationEntry, updatePostTime: Boolean, reason: String?) {
-        buffer.log(TAG, INFO, {
-            str1 = entry.logKey
-            bool1 = updatePostTime
-            str2 = reason ?: "unknown"
-        }, {
-            "update entry $str1 updatePostTime: $bool1 reason: $str2"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            {
+                str1 = entry.logKey
+                bool1 = updatePostTime
+                str2 = reason ?: "unknown"
+            },
+            { "update entry $str1 updatePostTime: $bool1 reason: $str2" }
+        )
     }
 
     fun logSnoozeLengthChange(packageSnoozeLengthMs: Int) {
-        buffer.log(TAG, INFO, {
-            int1 = packageSnoozeLengthMs
-        }, {
-            "snooze length changed: ${int1}ms"
-        })
+        buffer.log(
+            TAG,
+            INFO,
+            { int1 = packageSnoozeLengthMs },
+            { "snooze length changed: ${int1}ms" }
+        )
     }
 
     fun logSetEntryPinned(entry: NotificationEntry, isPinned: Boolean, reason: String) {
-        buffer.log(TAG, VERBOSE, {
-            str1 = entry.logKey
-            bool1 = isPinned
-            str2 = reason
-        }, {
-            "$str2 => set entry pinned $str1 pinned: $bool1"
-        })
+        buffer.log(
+            TAG,
+            VERBOSE,
+            {
+                str1 = entry.logKey
+                bool1 = isPinned
+                str2 = reason
+            },
+            { "$str2 => set entry pinned $str1 pinned: $bool1" }
+        )
     }
 
     fun logUpdatePinnedMode(hasPinnedNotification: Boolean) {
-        buffer.log(TAG, INFO, {
-            bool1 = hasPinnedNotification
+        buffer.log(
+            TAG,
+            INFO,
+            { bool1 = hasPinnedNotification },
+            { "has pinned notification changed to $bool1" }
+        )
+    }
+
+    fun logRemoveEntryAfterExpand(entry: NotificationEntry) {
+        buffer.log(TAG, VERBOSE, {
+            str1 = entry.logKey
         }, {
-            "has pinned notification changed to $bool1"
+            "remove entry after expand: $str1"
         })
     }
 }
 
-private const val TAG = "HeadsUpManager"
\ No newline at end of file
+private const val TAG = "HeadsUpManager"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index 21ec14f..591d7af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -403,7 +403,7 @@
                     tileSpec = TileSpec.create(DND_TILE_SPEC),
                     uiConfig =
                         QSTileUIConfig.Resource(
-                            iconRes = R.drawable.qs_dnd_icon_off,
+                            iconRes = com.android.internal.R.drawable.ic_zen_priority_modes,
                             labelRes = R.string.quick_settings_modes_label,
                         ),
                     instanceId = uiEventLogger.getNewInstanceId(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 71bcdfcb..b81af86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -22,8 +22,12 @@
 
 import com.android.internal.R;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.notification.modes.ZenIconLoader;
+import com.android.systemui.common.ui.GlobalConfig;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogBufferFactory;
 import com.android.systemui.settings.UserTracker;
@@ -79,6 +83,7 @@
 import dagger.Provides;
 
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 
 import javax.inject.Named;
 
@@ -100,9 +105,12 @@
     @Binds
     CastController provideCastController(CastControllerImpl controllerImpl);
 
-    /** */
+    /**
+     * @deprecated: unscoped configuration controller shouldn't be injected as it might lead to
+     * wrong updates in case of secondary displays.
+     */
     @Binds
-    ConfigurationController bindConfigurationController(ConfigurationControllerImpl impl);
+    ConfigurationController bindConfigurationController(@GlobalConfig ConfigurationController impl);
 
     /** */
     @Binds
@@ -178,6 +186,15 @@
             DevicePostureControllerImpl devicePostureControllerImpl);
 
     /** */
+    @Provides
+    @SysUISingleton
+    @GlobalConfig
+    static ConfigurationController provideGlobalConfigurationController(
+            @Application Context context, ConfigurationControllerImpl.Factory factory) {
+        return factory.create(context);
+    }
+
+    /** */
     @SysUISingleton
     @Provides
     static AccessPointControllerImpl  provideAccessPointControllerImpl(
@@ -236,4 +253,12 @@
     static LogBuffer provideCastControllerLog(LogBufferFactory factory) {
         return factory.create("CastControllerLog", 50);
     }
+
+    /** Provides a {@link ZenIconLoader} that fetches icons in a background thread. */
+    @Provides
+    @SysUISingleton
+    static ZenIconLoader provideZenIconLoader(
+            @UiBackground ExecutorService backgroundExecutorService) {
+        return new ZenIconLoader(backgroundExecutorService);
+    }
 }
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 efd60f6..dbeaa59 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
@@ -23,15 +23,20 @@
 import android.util.Log
 import androidx.concurrent.futures.await
 import com.android.settingslib.notification.data.repository.ZenModeRepository
+import com.android.settingslib.notification.modes.ZenIcon
 import com.android.settingslib.notification.modes.ZenIconLoader
 import com.android.settingslib.notification.modes.ZenMode
-import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
 import java.time.Duration
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 
 /**
@@ -41,11 +46,12 @@
 class ZenModeInteractor
 @Inject
 constructor(
+    private val context: Context,
     private val zenModeRepository: ZenModeRepository,
     private val notificationSettingsRepository: NotificationSettingsRepository,
+    @Background private val bgDispatcher: CoroutineDispatcher,
+    private val iconLoader: ZenIconLoader,
 ) {
-    private val iconLoader: ZenIconLoader = ZenIconLoader.getInstance()
-
     val isZenModeEnabled: Flow<Boolean> =
         zenModeRepository.globalZenMode
             .map {
@@ -74,8 +80,29 @@
 
     val modes: Flow<List<ZenMode>> = zenModeRepository.modes
 
-    suspend fun getModeIcon(mode: ZenMode, context: Context): Icon {
-        return Icon.Loaded(mode.getIcon(context, iconLoader).await(), contentDescription = null)
+    /** Flow returning the currently active mode(s), if any. */
+    val activeModes: Flow<ActiveZenModes> =
+        modes
+            .map { modes -> buildActiveZenModes(modes) }
+            .flowOn(bgDispatcher)
+            .distinctUntilChanged()
+
+    suspend fun getActiveModes() = buildActiveZenModes(zenModeRepository.getModes())
+
+    private suspend fun buildActiveZenModes(modes: List<ZenMode>): ActiveZenModes {
+        val activeModesList =
+            modes.filter { mode -> mode.isActive }.sortedWith(ZenMode.PRIORITIZING_COMPARATOR)
+        val mainActiveMode =
+            activeModesList.firstOrNull()?.let { ZenModeInfo(it.name, getModeIcon(it)) }
+
+        return ActiveZenModes(activeModesList.map { m -> m.name }, mainActiveMode)
+    }
+
+    val mainActiveMode: Flow<ZenModeInfo?> =
+        activeModes.map { a -> a.mainMode }.distinctUntilChanged()
+
+    suspend fun getModeIcon(mode: ZenMode): ZenIcon {
+        return iconLoader.getIcon(context, mode).await()
     }
 
     fun activateMode(zenMode: ZenMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.kt
new file mode 100644
index 0000000..569e517
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ActiveZenModes.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.statusbar.policy.domain.model
+
+import com.android.settingslib.notification.modes.ZenMode
+
+/**
+ * Represents the list of [ZenMode] instances that are currently active.
+ *
+ * @property modeNames Names of all the active modes, sorted by their priority.
+ * @property mainMode The most prioritized active mode, if any modes active. Guaranteed to be
+ *   non-null if [modeNames] is not empty.
+ */
+data class ActiveZenModes(val modeNames: List<String>, val mainMode: ZenModeInfo?) {
+    fun isAnyActive(): Boolean = modeNames.isNotEmpty()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt
new file mode 100644
index 0000000..5004f4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/model/ZenModeInfo.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy.domain.model
+
+import com.android.settingslib.notification.modes.ZenIcon
+import com.android.settingslib.notification.modes.ZenMode
+
+/** Name and icon of a [ZenMode] */
+data class ZenModeInfo(val name: String, val icon: ZenIcon)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
index 8aa989f..e1dcc52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegate.kt
@@ -21,7 +21,11 @@
 import android.util.Log
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTagsAsResourceId
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.PlatformButton
@@ -57,6 +61,7 @@
     private val activityStarter: ActivityStarter,
     // Using a provider to avoid a circular dependency.
     private val viewModel: Provider<ModesDialogViewModel>,
+    private val dialogEventLogger: ModesDialogEventLogger,
     @Main private val mainCoroutineContext: CoroutineContext,
 ) : SystemUIDialog.Delegate {
     // NOTE: This should only be accessed/written from the main thread.
@@ -87,7 +92,15 @@
     @Composable
     private fun ModesDialogContent(dialog: SystemUIDialog) {
         AlertDialogContent(
-            title = { Text(stringResource(R.string.zen_modes_dialog_title)) },
+            modifier = Modifier.semantics {
+                testTagsAsResourceId = true
+            },
+            title = {
+                Text(
+                    modifier = Modifier.testTag("modes_title"),
+                    text = stringResource(R.string.zen_modes_dialog_title)
+                )
+            },
             content = { ModeTileGrid(viewModel.get()) },
             neutralButton = {
                 PlatformOutlinedButton(onClick = { openSettings(dialog) }) {
@@ -102,7 +115,9 @@
         )
     }
 
-    private fun openSettings(dialog: SystemUIDialog) {
+    @VisibleForTesting
+    fun openSettings(dialog: SystemUIDialog) {
+        dialogEventLogger.logDialogSettings()
         val animationController =
             dialogTransitionAnimator.createActivityTransitionController(dialog)
         if (animationController == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.kt
new file mode 100644
index 0000000..33ed419
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLogger.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.statusbar.policy.ui.dialog
+
+import com.android.internal.logging.UiEventLogger
+import com.android.settingslib.notification.modes.ZenMode
+import com.android.systemui.qs.QSModesEvent
+import javax.inject.Inject
+
+class ModesDialogEventLogger
+@Inject
+constructor(
+    private val uiEventLogger: UiEventLogger,
+) {
+
+    fun logModeOn(mode: ZenMode) {
+        val id =
+            if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_ON else QSModesEvent.QS_MODES_MODE_ON
+        uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+    }
+
+    fun logModeOff(mode: ZenMode) {
+        val id =
+            if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_OFF else QSModesEvent.QS_MODES_MODE_OFF
+        uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+    }
+
+    fun logModeSettings(mode: ZenMode) {
+        val id =
+            if (mode.isManualDnd) QSModesEvent.QS_MODES_DND_SETTINGS
+            else QSModesEvent.QS_MODES_MODE_SETTINGS
+        uiEventLogger.log(id, /* uid= */ 0, mode.rule.packageName)
+    }
+
+    fun logOpenDurationDialog(mode: ZenMode) {
+        // should only occur for manual Do Not Disturb.
+        if (!mode.isManualDnd) {
+            return
+        }
+        uiEventLogger.log(QSModesEvent.QS_MODES_DURATION_DIALOG)
+    }
+
+    fun logDialogSettings() {
+        uiEventLogger.log(QSModesEvent.QS_MODES_SETTINGS)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
index 3b392c8..af93880 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/composable/ModeTile.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy.ui.dialog.composable
 
+import androidx.compose.animation.animateColorAsState
 import androidx.compose.foundation.basicMarquee
 import androidx.compose.foundation.combinedClickable
 import androidx.compose.foundation.layout.Arrangement
@@ -30,8 +31,15 @@
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.getValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import com.android.systemui.common.ui.compose.Icon
@@ -39,12 +47,16 @@
 
 @Composable
 fun ModeTile(viewModel: ModeTileViewModel) {
-    val tileColor =
-        if (viewModel.enabled) MaterialTheme.colorScheme.primary
-        else MaterialTheme.colorScheme.surfaceVariant
-    val contentColor =
-        if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
-        else MaterialTheme.colorScheme.onSurfaceVariant
+    val tileColor: Color by
+        animateColorAsState(
+            if (viewModel.enabled) MaterialTheme.colorScheme.primary
+            else MaterialTheme.colorScheme.surfaceVariant
+        )
+    val contentColor: Color by
+        animateColorAsState(
+            if (viewModel.enabled) MaterialTheme.colorScheme.onPrimary
+            else MaterialTheme.colorScheme.onSurfaceVariant
+        )
 
     CompositionLocalProvider(LocalContentColor provides contentColor) {
         Surface(
@@ -55,9 +67,11 @@
                 modifier =
                     Modifier.combinedClickable(
                             onClick = viewModel.onClick,
-                            onLongClick = viewModel.onLongClick
+                            onLongClick = viewModel.onLongClick,
+                            onLongClickLabel = viewModel.onLongClickLabel
                         )
-                        .padding(20.dp),
+                        .padding(20.dp)
+                        .semantics { stateDescription = viewModel.stateDescription },
                 verticalAlignment = Alignment.CenterVertically,
                 horizontalArrangement =
                     Arrangement.spacedBy(
@@ -70,12 +84,17 @@
                     Text(
                         viewModel.text,
                         fontWeight = FontWeight.W500,
-                        modifier = Modifier.tileMarquee()
+                        modifier = Modifier.tileMarquee().testTag("name")
                     )
                     Text(
                         viewModel.subtext,
                         fontWeight = FontWeight.W400,
-                        modifier = Modifier.tileMarquee()
+                        modifier =
+                            Modifier.tileMarquee()
+                                .testTag(if (viewModel.enabled) "stateOn" else "stateOff")
+                                .clearAndSetSemantics {
+                                    contentDescription = viewModel.subtextDescription
+                                }
                     )
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
index 7c1cb6a..abd2453 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModeTileViewModel.kt
@@ -28,7 +28,10 @@
     val icon: Icon,
     val text: String,
     val subtext: String,
+    val subtextDescription: String, // version of subtext without "on"/"off" for screen readers
     val enabled: Boolean,
+    val stateDescription: String, // "on"/"off" state of the tile, for screen readers
     val onClick: () -> Unit,
     val onLongClick: () -> Unit,
+    val onLongClickLabel: String, // for screen readers
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
index 44b692f..6764839c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModel.kt
@@ -23,6 +23,7 @@
 import android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID
 import com.android.settingslib.notification.modes.EnableZenModeDialog
 import com.android.settingslib.notification.modes.ZenMode
+import com.android.systemui.common.shared.model.asIcon
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.qs.tiles.dialog.QSZenModeDialogMetricsLogger
@@ -30,6 +31,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
@@ -49,6 +51,7 @@
     zenModeInteractor: ZenModeInteractor,
     @Background val bgDispatcher: CoroutineDispatcher,
     private val dialogDelegate: ModesDialogDelegate,
+    private val dialogEventLogger: ModesDialogEventLogger,
 ) {
     private val zenDialogMetricsLogger = QSZenModeDialogMetricsLogger(context)
 
@@ -86,34 +89,45 @@
                 modesList.map { mode ->
                     ModeTileViewModel(
                         id = mode.id,
-                        icon = zenModeInteractor.getModeIcon(mode, context),
-                        text = mode.rule.name,
+                        icon = zenModeInteractor.getModeIcon(mode).drawable().asIcon(),
+                        text = mode.name,
                         subtext = getTileSubtext(mode),
+                        subtextDescription = getModeDescription(mode) ?: "",
                         enabled = mode.isActive,
+                        stateDescription =
+                            context.getString(
+                                if (mode.isActive) R.string.zen_mode_on else R.string.zen_mode_off
+                            ),
                         onClick = {
                             if (!mode.rule.isEnabled) {
                                 openSettings(mode)
                             } else if (mode.isActive) {
+                                dialogEventLogger.logModeOff(mode)
                                 zenModeInteractor.deactivateMode(mode)
                             } else {
                                 if (mode.rule.isManualInvocationAllowed) {
                                     if (zenModeInteractor.shouldAskForZenDuration(mode)) {
+                                        dialogEventLogger.logOpenDurationDialog(mode)
                                         // NOTE: The dialog handles turning on the mode itself.
                                         val dialog = makeZenModeDialog()
                                         dialog.show()
                                     } else {
+                                        dialogEventLogger.logModeOn(mode)
                                         zenModeInteractor.activateMode(mode)
                                     }
                                 }
                             }
                         },
-                        onLongClick = { openSettings(mode) }
+                        onLongClick = { openSettings(mode) },
+                        onLongClickLabel =
+                            context.resources.getString(R.string.accessibility_long_click_tile)
                     )
                 }
             }
             .flowOn(bgDispatcher)
 
     private fun openSettings(mode: ZenMode) {
+        dialogEventLogger.logModeSettings(mode)
         val intent: Intent =
             Intent(ACTION_AUTOMATIC_ZEN_RULE_SETTINGS)
                 .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, mode.id)
@@ -121,17 +135,37 @@
         dialogDelegate.launchFromDialog(intent)
     }
 
-    private fun getTileSubtext(mode: ZenMode): String {
+    /**
+     * Returns a description of the mode, which is:
+     *   * a prompt to set up the mode if it is not enabled
+     *   * if it cannot be manually activated, text that says so
+     *   * otherwise, the trigger description of the mode if it exists...
+     *   * ...or null if it doesn't
+     *
+     * This description is used directly for the content description of a mode tile for screen
+     * readers, and for the tile subtext will be augmented with the current status of the mode.
+     */
+    private fun getModeDescription(mode: ZenMode): String? {
         if (!mode.rule.isEnabled) {
             return context.resources.getString(R.string.zen_mode_set_up)
         }
         if (!mode.rule.isManualInvocationAllowed && !mode.isActive) {
             return context.resources.getString(R.string.zen_mode_no_manual_invocation)
         }
+        return mode.getDynamicDescription(context)
+    }
 
-        val on = context.resources.getString(R.string.zen_mode_on)
-        val off = context.resources.getString(R.string.zen_mode_off)
-        return mode.getDynamicDescription(context) ?: if (mode.isActive) on else off
+    private fun getTileSubtext(mode: ZenMode): String {
+        val modeDescription = getModeDescription(mode)
+        return if (mode.isActive) {
+            if (modeDescription != null) {
+                context.getString(R.string.zen_mode_on_with_details, modeDescription)
+            } else {
+                context.getString(R.string.zen_mode_on)
+            }
+        } else {
+            modeDescription ?: context.getString(R.string.zen_mode_off)
+        }
     }
 
     private fun makeZenModeDialog(): Dialog {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
index 89227cf..fe1d647 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt
@@ -21,10 +21,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 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.domain.interactor.KeyguardStatusBarInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
 import javax.inject.Inject
@@ -58,7 +58,7 @@
 ) {
 
     private val showingHeadsUpStatusBar: Flow<Boolean> =
-        if (NotificationsHeadsUpRefactor.isEnabled) {
+        if (SceneContainerFlag.isEnabled) {
             headsUpNotificationInteractor.showHeadsUpStatusBar
         } else {
             flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index 21f1a3d..c30a6b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -46,6 +46,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DelegateTransitionAnimatorController;
@@ -71,7 +72,7 @@
     private static final boolean DEBUG = false;
 
     private final Context mContext;
-    private final WindowManager mWindowManager;
+    private final ViewCaptureAwareWindowManager mWindowManager;
     private final IWindowManager mIWindowManager;
     private final StatusBarContentInsetsProvider mContentInsetsProvider;
     private int mBarHeight = -1;
@@ -91,14 +92,14 @@
     public StatusBarWindowController(
             Context context,
             @StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
-            WindowManager windowManager,
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             IWindowManager iWindowManager,
             StatusBarContentInsetsProvider contentInsetsProvider,
             FragmentService fragmentService,
             @Main Resources resources,
             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
         mContext = context;
-        mWindowManager = windowManager;
+        mWindowManager = viewCaptureAwareWindowManager;
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
         mStatusBarWindowView = statusBarWindowView;
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 8f04896..3c53d2d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -32,6 +32,7 @@
 import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
 import androidx.annotation.CallSuper
 import androidx.annotation.VisibleForTesting
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.CoreStartable
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Main
@@ -70,7 +71,7 @@
 abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger<T>>(
     internal val context: Context,
     internal val logger: U,
-    internal val windowManager: WindowManager,
+    internal val windowManager: ViewCaptureAwareWindowManager,
     @Main private val mainExecutor: DelayableExecutor,
     private val accessibilityManager: AccessibilityManager,
     private val configurationController: ConfigurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index b6f5433..9b9cba9 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -29,7 +29,6 @@
 import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
 import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
 import android.view.ViewGroup
-import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.ImageView
@@ -38,6 +37,7 @@
 import androidx.annotation.IdRes
 import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.classifier.FalsingCollector
@@ -81,7 +81,7 @@
 constructor(
     context: Context,
     logger: ChipbarLogger,
-    windowManager: WindowManager,
+    viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
     @Main mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
     configurationController: ConfigurationController,
@@ -100,7 +100,7 @@
     TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
         context,
         logger,
-        windowManager,
+        viewCaptureAwareWindowManager,
         mainExecutor,
         accessibilityManager,
         configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index c7fc445..9c8ef04 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -89,6 +89,9 @@
 import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
 import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
 
+import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.flow.StateFlow;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -162,6 +165,7 @@
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final JavaAdapter mJavaAdapter;
     private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+    private final StateFlow<Boolean> mIsKeyguardOnAsleepState;
     private final UiModeManager mUiModeManager;
     private ColorScheme mDarkColorScheme;
     private ColorScheme mLightColorScheme;
@@ -202,8 +206,7 @@
             }
             boolean currentUser = userId == mUserTracker.getUserId();
             boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
-                    ? KeyguardState.Companion.deviceIsAsleepInState(
-                            mKeyguardTransitionInteractor.getFinishedState())
+                    ? ThemeOverlayController.this.mIsKeyguardOnAsleepState.getValue()
                     : mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
 
             if (currentUser && !mAcceptColorEvents && isAsleep) {
@@ -434,6 +437,10 @@
         mUiModeManager = uiModeManager;
         mActivityManager = activityManager;
         dumpManager.registerDumpable(TAG, this);
+
+        Flow<Boolean> isFinishedInAsleepStateFlow = mKeyguardTransitionInteractor
+                .isFinishedInStateWhere(KeyguardState.Companion::deviceIsAsleepInState);
+        mIsKeyguardOnAsleepState = mJavaAdapter.stateInApp(isFinishedInAsleepStateFlow, false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt
deleted file mode 100644
index 8ba8db4..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadKeyboardTutorialModule.kt
+++ /dev/null
@@ -1,33 +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
-
-import android.app.Activity
-import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-
-@Module
-interface TouchpadKeyboardTutorialModule {
-
-    @Binds
-    @IntoMap
-    @ClassKey(TouchpadTutorialActivity::class)
-    fun activity(impl: TouchpadTutorialActivity): Activity
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
new file mode 100644
index 0000000..3fa3f63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
@@ -0,0 +1,75 @@
+/*
+ * 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
+
+import android.app.Activity
+import androidx.compose.runtime.Composable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.TouchpadTutorialScreensProvider
+import com.android.systemui.model.SysUiState
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
+import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
+import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
+
+@Module
+interface TouchpadTutorialModule {
+
+    @Binds
+    @IntoMap
+    @ClassKey(TouchpadTutorialActivity::class)
+    fun activity(impl: TouchpadTutorialActivity): Activity
+
+    companion object {
+        @Provides
+        fun touchpadScreensProvider(): TouchpadTutorialScreensProvider {
+            return ScreensProvider
+        }
+
+        @SysUISingleton
+        @Provides
+        fun touchpadGesturesInteractor(
+            sysUiState: SysUiState,
+            displayTracker: DisplayTracker,
+            @Background backgroundScope: CoroutineScope,
+            logger: InputDeviceTutorialLogger,
+        ): TouchpadGesturesInteractor {
+            return TouchpadGesturesInteractor(sysUiState, displayTracker, backgroundScope, logger)
+        }
+    }
+}
+
+private object ScreensProvider : TouchpadTutorialScreensProvider {
+    @Composable
+    override fun BackGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
+        BackGestureTutorialScreen(onDoneButtonClicked, onBack)
+    }
+
+    @Composable
+    override fun HomeGesture(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
+        HomeGestureTutorialScreen(onDoneButtonClicked, onBack)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
index b6c2ae7..1a41987 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/domain/interactor/TouchpadGesturesInteractor.kt
@@ -16,28 +16,26 @@
 
 package com.android.systemui.touchpad.tutorial.domain.interactor
 
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
 import com.android.systemui.model.SysUiState
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.shared.system.QuickStepContract
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
-@SysUISingleton
-class TouchpadGesturesInteractor
-@Inject
-constructor(
+class TouchpadGesturesInteractor(
     private val sysUiState: SysUiState,
     private val displayTracker: DisplayTracker,
-    @Background private val backgroundScope: CoroutineScope
+    private val backgroundScope: CoroutineScope,
+    private val logger: InputDeviceTutorialLogger,
 ) {
     fun disableGestures() {
+        logger.log("Disabling touchpad gestures across the system")
         setGesturesState(disabled = true)
     }
 
     fun enableGestures() {
+        logger.log("Enabling touchpad gestures across the system")
         setGesturesState(disabled = false)
     }
 
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 5980e1d..a3b1867 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,11 +16,12 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 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.GestureState
@@ -65,7 +66,6 @@
     val onTertiaryFixed = LocalAndroidColorScheme.current.onTertiaryFixed
     val onTertiaryFixedVariant = LocalAndroidColorScheme.current.onTertiaryFixedVariant
     val tertiaryFixedDim = LocalAndroidColorScheme.current.tertiaryFixedDim
-    val surfaceContainer = MaterialTheme.colorScheme.surfaceContainer
     val dynamicProperties =
         rememberLottieDynamicProperties(
             rememberColorFilterProperty(".tertiaryFixedDim", tertiaryFixedDim),
@@ -74,10 +74,9 @@
             rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant)
         )
     val screenColors =
-        remember(onTertiaryFixed, surfaceContainer, tertiaryFixedDim, dynamicProperties) {
+        remember(dynamicProperties) {
             TutorialScreenConfig.Colors(
                 background = onTertiaryFixed,
-                successBackground = surfaceContainer,
                 title = tertiaryFixedDim,
                 animationColors = 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 9ac2cba..57d7c84 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,57 +16,21 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
-import android.graphics.ColorFilter
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffColorFilter
 import androidx.activity.compose.BackHandler
-import androidx.annotation.RawRes
-import androidx.annotation.StringRes
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.EnterTransition
-import androidx.compose.animation.ExitTransition
-import androidx.compose.animation.animateColorAsState
-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.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawBehind
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.compose.ui.platform.LocalContext
-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.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.GestureState
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
@@ -81,6 +45,14 @@
     ): TouchpadGestureMonitor
 }
 
+fun GestureState.toTutorialActionState(): TutorialActionState {
+    return when (this) {
+        NOT_STARTED -> TutorialActionState.NOT_STARTED
+        IN_PROGRESS -> TutorialActionState.IN_PROGRESS
+        FINISHED -> TutorialActionState.FINISHED
+    }
+}
+
 @Composable
 fun GestureTutorialScreen(
     screenConfig: TutorialScreenConfig,
@@ -104,7 +76,11 @@
             )
         }
     TouchpadGesturesHandlingBox(gestureHandler, gestureState) {
-        GestureTutorialContent(gestureState, onDoneButtonClicked, screenConfig)
+        ActionTutorialContent(
+            gestureState.toTutorialActionState(),
+            onDoneButtonClicked,
+            screenConfig
+        )
     }
 }
 
@@ -135,169 +111,3 @@
         content()
     }
 }
-
-@Composable
-private fun GestureTutorialContent(
-    gestureState: GestureState,
-    onDoneButtonClicked: () -> Unit,
-    config: TutorialScreenConfig
-) {
-    val animatedColor by
-        animateColorAsState(
-            targetValue =
-                if (gestureState == FINISHED) config.colors.successBackground
-                else config.colors.background,
-            animationSpec = tween(durationMillis = 150, easing = LinearEasing),
-            label = "backgroundColor"
-        )
-    Column(
-        verticalArrangement = Arrangement.Center,
-        modifier =
-            Modifier.fillMaxSize()
-                .drawBehind { drawRect(animatedColor) }
-                .padding(start = 48.dp, top = 124.dp, end = 48.dp, bottom = 48.dp)
-    ) {
-        Row(modifier = Modifier.fillMaxWidth().weight(1f)) {
-            TutorialDescription(
-                titleTextId =
-                    if (gestureState == FINISHED) config.strings.titleSuccessResId
-                    else config.strings.titleResId,
-                titleColor = config.colors.title,
-                bodyTextId =
-                    if (gestureState == FINISHED) config.strings.bodySuccessResId
-                    else config.strings.bodyResId,
-                modifier = Modifier.weight(1f)
-            )
-            Spacer(modifier = Modifier.width(76.dp))
-            TutorialAnimation(
-                gestureState,
-                config,
-                modifier = Modifier.weight(1f).padding(top = 8.dp)
-            )
-        }
-        DoneButton(onDoneButtonClicked = onDoneButtonClicked)
-    }
-}
-
-@Composable
-fun TutorialDescription(
-    @StringRes titleTextId: Int,
-    titleColor: Color,
-    @StringRes bodyTextId: Int,
-    modifier: Modifier = Modifier
-) {
-    Column(verticalArrangement = Arrangement.Top, modifier = modifier) {
-        Text(
-            text = stringResource(id = titleTextId),
-            style = MaterialTheme.typography.displayLarge,
-            color = titleColor
-        )
-        Spacer(modifier = Modifier.height(16.dp))
-        Text(
-            text = stringResource(id = bodyTextId),
-            style = MaterialTheme.typography.bodyLarge,
-            color = Color.White
-        )
-    }
-}
-
-@Composable
-fun TutorialAnimation(
-    gestureState: GestureState,
-    config: TutorialScreenConfig,
-    modifier: Modifier = Modifier
-) {
-    Box(modifier = modifier.fillMaxWidth()) {
-        AnimatedContent(
-            targetState = gestureState,
-            transitionSpec = {
-                if (initialState == NOT_STARTED && targetState == IN_PROGRESS) {
-                    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
-                }
-            }
-        ) { gestureState ->
-            when (gestureState) {
-                NOT_STARTED ->
-                    EducationAnimation(
-                        config.animations.educationResId,
-                        config.colors.animationColors
-                    )
-                IN_PROGRESS ->
-                    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/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index ed3110c..d4eb0cd 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,11 +16,12 @@
 
 package com.android.systemui.touchpad.tutorial.ui.composable
 
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 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.HomeGestureMonitor
@@ -64,7 +65,6 @@
     val primaryFixedDim = LocalAndroidColorScheme.current.primaryFixedDim
     val onPrimaryFixed = LocalAndroidColorScheme.current.onPrimaryFixed
     val onPrimaryFixedVariant = LocalAndroidColorScheme.current.onPrimaryFixedVariant
-    val surfaceContainer = MaterialTheme.colorScheme.surfaceContainer
     val dynamicProperties =
         rememberLottieDynamicProperties(
             rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
@@ -72,10 +72,9 @@
             rememberColorFilterProperty(".onPrimaryFixedVariant", onPrimaryFixedVariant)
         )
     val screenColors =
-        remember(surfaceContainer, dynamicProperties) {
+        remember(dynamicProperties) {
             TutorialScreenConfig.Colors(
                 background = onPrimaryFixed,
-                successBackground = surfaceContainer,
                 title = primaryFixedDim,
                 animationColors = dynamicProperties,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.kt
deleted file mode 100644
index f2276c8..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialComponents.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.composable
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import com.android.systemui.res.R
-
-@Composable
-fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) {
-    Row(
-        horizontalArrangement = Arrangement.End,
-        verticalAlignment = Alignment.CenterVertically,
-        modifier = modifier.fillMaxWidth()
-    ) {
-        Button(onClick = onDoneButtonClicked) {
-            Text(stringResource(R.string.touchpad_tutorial_done_button))
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt
deleted file mode 100644
index d76ceb9..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialScreenConfig.kt
+++ /dev/null
@@ -1,48 +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.composable
-
-import androidx.annotation.RawRes
-import androidx.annotation.StringRes
-import androidx.compose.ui.graphics.Color
-import com.airbnb.lottie.compose.LottieDynamicProperties
-
-data class TutorialScreenConfig(
-    val colors: Colors,
-    val strings: Strings,
-    val animations: Animations
-) {
-
-    data class Colors(
-        val background: Color,
-        val successBackground: Color,
-        val title: Color,
-        val animationColors: LottieDynamicProperties
-    )
-
-    data class Strings(
-        @StringRes val titleResId: Int,
-        @StringRes val bodyResId: Int,
-        @StringRes val titleSuccessResId: Int,
-        @StringRes val bodySuccessResId: Int,
-    )
-
-    data class Animations(
-        @RawRes val educationResId: Int,
-        @RawRes val successResId: Int,
-    )
-}
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 14355fa..65b452a 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
@@ -34,6 +34,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton
 import com.android.systemui.res.R
 
 @Composable
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
index ad8ab30..821b51a 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt
@@ -27,9 +27,13 @@
 import androidx.lifecycle.Lifecycle.State.STARTED
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.theme.PlatformTheme
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
+import com.android.systemui.inputdevice.tutorial.ui.composable.ActionKeyTutorialScreen
 import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen
 import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen
 import com.android.systemui.touchpad.tutorial.ui.composable.TutorialSelectionScreen
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.ACTION_KEY
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.BACK_GESTURE
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.HOME_GESTURE
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen.TUTORIAL_SELECTION
@@ -40,6 +44,7 @@
 @Inject
 constructor(
     private val viewModelFactory: TouchpadTutorialViewModel.Factory,
+    private val logger: InputDeviceTutorialLogger,
 ) : ComponentActivity() {
 
     private val vm by viewModels<TouchpadTutorialViewModel>(factoryProducer = { viewModelFactory })
@@ -47,9 +52,17 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         enableEdgeToEdge()
-        setContent { PlatformTheme { TouchpadTutorialScreen(vm) { finish() } } }
+        setContent {
+            PlatformTheme { TouchpadTutorialScreen(vm, closeTutorial = ::finishTutorial) }
+        }
         // required to handle 3+ fingers on touchpad
         window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
+        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS)
+    }
+
+    private fun finishTutorial() {
+        logger.logCloseTutorial(TutorialContext.TOUCHPAD_TUTORIAL)
+        finish()
     }
 
     override fun onResume() {
@@ -71,7 +84,7 @@
             TutorialSelectionScreen(
                 onBackTutorialClicked = { vm.goTo(BACK_GESTURE) },
                 onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) },
-                onActionKeyTutorialClicked = {},
+                onActionKeyTutorialClicked = { vm.goTo(ACTION_KEY) },
                 onDoneButtonClicked = closeTutorial
             )
         BACK_GESTURE ->
@@ -84,5 +97,10 @@
                 onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
                 onBack = { vm.goTo(TUTORIAL_SELECTION) },
             )
+        ACTION_KEY -> // TODO(b/358105049) move action key tutorial to OOBE flow
+        ActionKeyTutorialScreen(
+                onDoneButtonClicked = { vm.goTo(TUTORIAL_SELECTION) },
+                onBack = { vm.goTo(TUTORIAL_SELECTION) },
+            )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
index 11984af..43266ad 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModel.kt
@@ -18,18 +18,23 @@
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext
 import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
-class TouchpadTutorialViewModel(private val gesturesInteractor: TouchpadGesturesInteractor) :
-    ViewModel() {
+class TouchpadTutorialViewModel(
+    private val gesturesInteractor: TouchpadGesturesInteractor,
+    private val logger: InputDeviceTutorialLogger
+) : ViewModel() {
 
     private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION)
     val screen: StateFlow<Screen> = _screen
 
     fun goTo(screen: Screen) {
+        logger.logGoingToScreen(screen, TutorialContext.TOUCHPAD_TUTORIAL)
         _screen.value = screen
     }
 
@@ -41,12 +46,16 @@
         gesturesInteractor.enableGestures()
     }
 
-    class Factory @Inject constructor(private val gesturesInteractor: TouchpadGesturesInteractor) :
-        ViewModelProvider.Factory {
+    class Factory
+    @Inject
+    constructor(
+        private val gesturesInteractor: TouchpadGesturesInteractor,
+        private val logger: InputDeviceTutorialLogger
+    ) : ViewModelProvider.Factory {
 
         @Suppress("UNCHECKED_CAST")
         override fun <T : ViewModel> create(modelClass: Class<T>): T {
-            return TouchpadTutorialViewModel(gesturesInteractor) as T
+            return TouchpadTutorialViewModel(gesturesInteractor, logger) as T
         }
     }
 }
@@ -55,4 +64,5 @@
     TUTORIAL_SELECTION,
     BACK_GESTURE,
     HOME_GESTURE,
+    ACTION_KEY,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
index 56b46624..32f2ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/user/CreateUserActivity.java
@@ -116,7 +116,7 @@
         return mCreateUserDialogController.createDialog(
                 this,
                 this::startActivity,
-                (mUserCreator.isMultipleAdminEnabled() && mUserCreator.isUserAdmin()
+                (mUserCreator.canCreateAdminUser() && mUserCreator.isUserAdmin()
                         && !isKeyguardShowing),
                 this::addUserNow,
                 this::finish
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
index 9304a46..a426da9 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.content.pm.UserInfo
 import android.graphics.drawable.Drawable
+import android.multiuser.Flags
 import android.os.UserManager
 import com.android.internal.util.UserIcons
 import com.android.settingslib.users.UserCreatingDialog
@@ -91,7 +92,17 @@
         return userManager.isAdminUser
     }
 
-    fun isMultipleAdminEnabled(): Boolean {
-        return UserManager.isMultipleAdminEnabled()
+    /**
+     * Checks if the creation of a new admin user is allowed.
+     *
+     * @return `true` if creating a new admin is allowed, `false` otherwise.
+     */
+    fun canCreateAdminUser(): Boolean {
+        return if (Flags.unicornModeRefactoringForHsumReadOnly()) {
+            UserManager.isMultipleAdminEnabled() &&
+                !userManager.hasUserRestriction(UserManager.DISALLOW_GRANT_ADMIN)
+        } else {
+            UserManager.isMultipleAdminEnabled()
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
index 59c819d..cd32718 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/SelectedUserInteractor.kt
@@ -3,8 +3,6 @@
 import android.annotation.UserIdInt
 import android.content.pm.UserInfo
 import android.os.UserManager
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.Flags.refactorGetCurrentUser
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.user.data.repository.UserRepository
 import javax.inject.Inject
@@ -21,23 +19,11 @@
     /** Flow providing the [UserInfo] of the currently selected user. */
     val selectedUserInfo = repository.selectedUserInfo
 
-    /**
-     * Returns the ID of the currently-selected user.
-     *
-     * @param bypassFlag this will ignore the feature flag and get the data from the repository
-     *   instead. This is used for refactored methods that were previously pointing to `userTracker`
-     *   and therefore should not be routed back to KeyguardUpdateMonitor when flag is disabled.
-     *   KeyguardUpdateMonitor.getCurrentUser() is deprecated and will be removed soon (together
-     *   with this flag).
-     */
+    /** Returns the ID of the currently-selected user. */
     @UserIdInt
     @JvmOverloads
-    fun getSelectedUserId(bypassFlag: Boolean = false): Int {
-        return if (bypassFlag || refactorGetCurrentUser()) {
-            repository.getSelectedUserInfo().id
-        } else {
-            KeyguardUpdateMonitor.getCurrentUser()
-        }
+    fun getSelectedUserId(): Int {
+        return repository.getSelectedUserInfo().id
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index ecf1165..70774f13 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -28,6 +28,7 @@
 import dagger.Provides;
 
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 import javax.inject.Singleton;
@@ -81,6 +82,18 @@
     @Singleton
     @UiBackground
     public static Executor provideUiBackgroundExecutor() {
+        return provideUiBackgroundExecutorService();
+    }
+
+    /**
+     * Provide an ExecutorService specifically for running UI operations on a separate thread.
+     *
+     * <p>Keep submitted runnables short and to the point, just as with any other UI code.
+     */
+    @Provides
+    @Singleton
+    @UiBackground
+    public static ExecutorService provideUiBackgroundExecutorService() {
         return Executors.newSingleThreadExecutor();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
index e17274c..315a89b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -19,11 +19,13 @@
 import android.util.IndentingPrintWriter
 import com.android.systemui.Dumpable
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.printCollection
 import java.io.PrintWriter
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -64,19 +66,20 @@
 }
 
 /**
- * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
- * [FlowDumper] by delegation.
- *
- * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
- *   unregister itself when there is something to dump.
- * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
- *   class's name will be used. If you're implementing by delegation, you probably want to provide
- *   this tag to get a meaningful dumpable name.
+ * The minimal implementation of FlowDumper. The owner must either register this with the
+ * DumpManager, or else call [dumpFlows] from its own [Dumpable.dump] method.
  */
-open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+open class SimpleFlowDumper : FlowDumper {
+
     private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
     private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
     private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+
+    protected fun isNotEmpty(): Boolean =
+        stateFlowMap.isNotEmpty() || sharedFlowMap.isNotEmpty() || flowCollectionMap.isNotEmpty()
+
+    protected open fun onMapKeysChanged(added: Boolean) {}
+
     override fun dumpFlows(pw: IndentingPrintWriter) {
         pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
             append(key).append('=').println(flow.value)
@@ -92,43 +95,62 @@
         }
     }
 
-    private val Any.idString: String
-        get() = Integer.toHexString(System.identityHashCode(this))
-
     override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
         val mapKey = dumpName to idString
         try {
             collect {
                 flowCollectionMap[mapKey] = it ?: "null"
-                updateRegistration(required = true)
+                onMapKeysChanged(added = true)
                 emit(it)
             }
         } finally {
             flowCollectionMap.remove(mapKey)
-            updateRegistration(required = false)
+            onMapKeysChanged(added = false)
         }
     }
 
     override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
         stateFlowMap[dumpName] = this
+        onMapKeysChanged(added = true)
         return this
     }
 
     override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
         sharedFlowMap[dumpName] = this
+        onMapKeysChanged(added = true)
         return this
     }
 
-    private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+    protected val Any.idString: String
+        get() = Integer.toHexString(System.identityHashCode(this))
+}
+
+/**
+ * An implementation of [FlowDumper] that registers itself whenever there is something to dump. This
+ * class is meant to be extended.
+ *
+ * @param dumpManager this will be used by the [FlowDumperImpl] to register and unregister itself
+ *   when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ *   class's name will be used.
+ */
+abstract class FlowDumperImpl(
+    private val dumpManager: DumpManager,
+    private val tag: String? = null,
+) : SimpleFlowDumper() {
+
+    override fun onMapKeysChanged(added: Boolean) {
+        updateRegistration(required = added)
+    }
+
+    private val dumpManagerName = "[$idString] ${tag ?: javaClass.simpleName}"
+
     private var registered = AtomicBoolean(false)
+
     private fun updateRegistration(required: Boolean) {
-        if (dumpManager == null) return
         if (required && registered.get()) return
         synchronized(registered) {
-            val shouldRegister =
-                stateFlowMap.isNotEmpty() ||
-                    sharedFlowMap.isNotEmpty() ||
-                    flowCollectionMap.isNotEmpty()
+            val shouldRegister = isNotEmpty()
             val wasRegistered = registered.getAndSet(shouldRegister)
             if (wasRegistered != shouldRegister) {
                 if (shouldRegister) {
@@ -140,3 +162,49 @@
         }
     }
 }
+
+/**
+ * A [FlowDumper] that also has an [activateFlowDumper] suspend function that allows the dumper to
+ * be registered with the [DumpManager] only when activated, just like
+ * [Activatable.activate()][com.android.systemui.lifecycle.Activatable.activate].
+ */
+interface ActivatableFlowDumper : FlowDumper {
+    suspend fun activateFlowDumper(): Nothing
+}
+
+/**
+ * Implementation of [ActivatableFlowDumper] that only registers when activated.
+ *
+ * This is generally used to implement [ActivatableFlowDumper] by delegation, especially for
+ * [SysUiViewModel] implementations.
+ *
+ * @param dumpManager used to automatically register and unregister this instance when activated and
+ *   there is something to dump.
+ * @param tag the name with which this is dumper registered.
+ */
+class ActivatableFlowDumperImpl(
+    private val dumpManager: DumpManager,
+    tag: String,
+) : SimpleFlowDumper(), ActivatableFlowDumper {
+
+    private val registration =
+        object : ExclusiveActivatable() {
+            override suspend fun onActivated(): Nothing {
+                try {
+                    dumpManager.registerCriticalDumpable(
+                        dumpManagerName,
+                        this@ActivatableFlowDumperImpl
+                    )
+                    awaitCancellation()
+                } finally {
+                    dumpManager.unregisterDumpable(dumpManagerName)
+                }
+            }
+        }
+
+    private val dumpManagerName = "[$idString] $tag"
+
+    override suspend fun activateFlowDumper(): Nothing {
+        registration.activate()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 055671c..64e056d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -31,7 +31,10 @@
 import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 
 /** A class allowing Java classes to collect on Kotlin flows. */
@@ -58,6 +61,15 @@
     ): Job {
         return scope.launch { flow.collect { consumer.accept(it) } }
     }
+
+    @JvmOverloads
+    fun <T> stateInApp(
+        flow: Flow<T>,
+        initialValue: T,
+        started: SharingStarted = SharingStarted.Eagerly
+    ): StateFlow<T> {
+        return flow.stateIn(scope, started, initialValue)
+    }
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.kt
new file mode 100644
index 0000000..e836731
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerAdapter.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
+
+import android.media.IVolumeController
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
+import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * This class is a bridge between
+ * [com.android.settingslib.volume.data.repository.AudioRepository.volumeControllerEvents] and the
+ * old code that uses [IVolumeController] interface directly.
+ */
+class VolumeControllerAdapter
+@Inject
+constructor(
+    @Application private val coroutineScope: CoroutineScope,
+    private val audioRepository: AudioRepository,
+) {
+
+    /** Collects [Flow] of [VolumeControllerEvent] into [IVolumeController]. */
+    fun collectToController(controller: IVolumeController) {
+        coroutineScope.launch {
+            audioRepository.volumeControllerEvents.collect { event ->
+                when (event) {
+                    is VolumeControllerEvent.VolumeChanged ->
+                        controller.volumeChanged(event.streamType, event.flags)
+                    VolumeControllerEvent.Dismiss -> controller.dismiss()
+                    is VolumeControllerEvent.DisplayCsdWarning ->
+                        controller.displayCsdWarning(event.csdWarning, event.displayDurationMs)
+                    is VolumeControllerEvent.DisplaySafeVolumeWarning ->
+                        controller.displaySafeVolumeWarning(event.flags)
+                    is VolumeControllerEvent.MasterMuteChanged ->
+                        controller.masterMuteChanged(event.flags)
+                    is VolumeControllerEvent.SetA11yMode -> controller.setA11yMode(event.mode)
+                    is VolumeControllerEvent.SetLayoutDirection ->
+                        controller.setLayoutDirection(event.layoutDirection)
+                }
+            }
+        }
+    }
+
+    fun notifyVolumeControllerVisible(isVisible: Boolean) {
+        coroutineScope.launch { audioRepository.notifyVolumeControllerVisible(isVisible) }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerCollector.kt b/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerCollector.kt
deleted file mode 100644
index 6859191..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeControllerCollector.kt
+++ /dev/null
@@ -1,59 +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
-
-import android.media.IVolumeController
-import com.android.settingslib.media.data.repository.VolumeControllerEvent
-import com.android.systemui.dagger.qualifiers.Application
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.launch
-
-/**
- * This class is a bridge between
- * [com.android.settingslib.volume.data.repository.AudioRepository.volumeControllerEvents] and the
- * old code that uses [IVolumeController] interface directly.
- */
-class VolumeControllerCollector
-@Inject
-constructor(@Application private val coroutineScope: CoroutineScope) {
-
-    /** Collects [Flow] of [VolumeControllerEvent] into [IVolumeController]. */
-    fun collectToController(
-        eventsFlow: Flow<VolumeControllerEvent>,
-        controller: IVolumeController
-    ) =
-        coroutineScope.launch {
-            eventsFlow.collect { event ->
-                when (event) {
-                    is VolumeControllerEvent.VolumeChanged ->
-                        controller.volumeChanged(event.streamType, event.flags)
-                    VolumeControllerEvent.Dismiss -> controller.dismiss()
-                    is VolumeControllerEvent.DisplayCsdWarning ->
-                        controller.displayCsdWarning(event.csdWarning, event.displayDurationMs)
-                    is VolumeControllerEvent.DisplaySafeVolumeWarning ->
-                        controller.displaySafeVolumeWarning(event.flags)
-                    is VolumeControllerEvent.MasterMuteChanged ->
-                        controller.masterMuteChanged(event.flags)
-                    is VolumeControllerEvent.SetA11yMode -> controller.setA11yMode(event.mode)
-                    is VolumeControllerEvent.SetLayoutDirection ->
-                        controller.setLayoutDirection(event.layoutDirection)
-                }
-            }
-        }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 066bfc5..8934d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -68,6 +68,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.settingslib.volume.MediaSessions;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
@@ -124,7 +125,6 @@
     static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
     static {
         STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
-        STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
         STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
         STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
         STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
@@ -153,6 +153,7 @@
     private final KeyguardManager mKeyguardManager;
     private final ActivityManager mActivityManager;
     private final UserTracker mUserTracker;
+    private final VolumeControllerAdapter mVolumeControllerAdapter;
     protected C mCallbacks = new C();
     private final State mState = new State();
     protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
@@ -165,6 +166,7 @@
     private boolean mShowSafetyWarning;
     private long mLastToggledRingerOn;
     private boolean mDeviceInteractive = true;
+    boolean mInAudioSharing = false;
 
     private VolumePolicy mVolumePolicy;
     @GuardedBy("this")
@@ -196,6 +198,7 @@
             NotificationManager notificationManager,
             VibratorHelper vibrator,
             IAudioService iAudioService,
+            VolumeControllerAdapter volumeControllerAdapter,
             AccessibilityManager accessibilityManager,
             PackageManager packageManager,
             WakefulnessLifecycle wakefulnessLifecycle,
@@ -232,6 +235,7 @@
         mVibrator = vibrator;
         mHasVibrator = mVibrator.hasVibrator();
         mAudioService = iAudioService;
+        mVolumeControllerAdapter = volumeControllerAdapter;
         mKeyguardManager = keyguardManager;
         mActivityManager = activityManager;
         mUserTracker = userTracker;
@@ -258,10 +262,14 @@
     }
 
     protected void setVolumeController() {
-        try {
-            mAudio.setVolumeController(mVolumeController);
-        } catch (SecurityException e) {
-            Log.w(TAG, "Unable to set the volume controller", e);
+        if (Flags.useVolumeController()) {
+            mVolumeControllerAdapter.collectToController(mVolumeController);
+        } else {
+            try {
+                mAudio.setVolumeController(mVolumeController);
+            } catch (SecurityException e) {
+                Log.w(TAG, "Unable to set the volume controller", e);
+            }
         }
     }
 
@@ -295,6 +303,9 @@
             mJavaAdapter.alwaysCollectFlow(
                     mAudioSharingInteractor.getVolume(),
                     this::handleAudioSharingStreamVolumeChanges);
+            mJavaAdapter.alwaysCollectFlow(
+                    mAudioSharingInteractor.isInAudioSharing(),
+                    inSharing -> mInAudioSharing = inSharing);
         }
     }
 
@@ -380,7 +391,11 @@
     }
 
     public void notifyVisible(boolean visible) {
-        mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+        if (Flags.useVolumeController()) {
+            mVolumeControllerAdapter.notifyVolumeControllerVisible(visible);
+        } else {
+            mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+        }
     }
 
     public void userActivity() {
@@ -510,11 +525,18 @@
             //       Since their values overlap with DEVICE_OUT_EARPIECE and DEVICE_OUT_SPEAKER.
             //       Anyway, we can check BLE devices by using just DEVICE_OUT_BLE_HEADSET.
             final boolean routedToBluetooth =
-                    (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
-                            (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
-                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
-                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
-                            AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0;
+                    // TODO(b/359737651): Need audio support to return broadcast mask.
+                    // For now, mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) will return
+                    // AudioManager.DEVICE_NONE, so we also need to check if the device is in audio
+                    // sharing here.
+                    mInAudioSharing
+                            || (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC)
+                                            & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
+                                                    | AudioManager
+                                                            .DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
+                                                    | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER
+                                                    | AudioManager.DEVICE_OUT_BLE_HEADSET))
+                                    != 0;
             changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
         } else if (stream == AudioManager.STREAM_VOICE_CALL) {
             final boolean routedToBluetooth =
@@ -631,7 +653,6 @@
     private static boolean isLogWorthy(int stream) {
         switch (stream) {
             case AudioSystem.STREAM_ALARM:
-            case AudioSystem.STREAM_BLUETOOTH_SCO:
             case AudioSystem.STREAM_MUSIC:
             case AudioSystem.STREAM_RING:
             case AudioSystem.STREAM_SYSTEM:
@@ -813,6 +834,7 @@
                 ss.dynamic = true;
                 ss.levelMin = mAudioSharingInteractor.getVolumeMin();
                 ss.levelMax = mAudioSharingInteractor.getVolumeMax();
+                ss.routedToBluetooth = true;
                 if (ss.level != volume) {
                     ss.level = volume;
                 }
@@ -1258,6 +1280,8 @@
 
     private final class Receiver extends BroadcastReceiver {
 
+        private static final int STREAM_UNKNOWN = -1;
+
         public void init() {
             final IntentFilter filter = new IntentFilter();
             filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
@@ -1279,30 +1303,39 @@
             final String action = intent.getAction();
             boolean changed = false;
             if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
-                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                        STREAM_UNKNOWN);
                 final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
                 final int oldLevel = intent
                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
                 if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
                         + " level=" + level + " oldLevel=" + oldLevel);
-                changed = updateStreamLevelW(stream, level);
+                if (stream != STREAM_UNKNOWN) {
+                    changed = updateStreamLevelW(stream, level);
+                }
             } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
-                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                        STREAM_UNKNOWN);
                 final int devices = intent
                         .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
                 final int oldDevices = intent
                         .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
                 if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
                         + stream + " devices=" + devices + " oldDevices=" + oldDevices);
-                changed = checkRoutedToBluetoothW(stream);
-                changed |= onVolumeChangedW(stream, 0);
+                if (stream != STREAM_UNKNOWN) {
+                    changed |= checkRoutedToBluetoothW(stream);
+                    changed |= onVolumeChangedW(stream, 0);
+                }
             } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
-                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
+                        STREAM_UNKNOWN);
                 final boolean muted = intent
                         .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
                 if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
                         + " muted=" + muted);
-                changed = updateStreamMuteW(stream, muted);
+                if (stream != STREAM_UNKNOWN) {
+                    changed = updateStreamMuteW(stream, muted);
+                }
             } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
                 if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
                 changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e56f6b3..7786453 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -695,18 +695,15 @@
             addRow(AudioManager.STREAM_MUSIC,
                     R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
             if (!AudioSystem.isSingleVolume(mContext)) {
-
                 addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
                         R.drawable.ic_ring_volume_off, true, false);
-
-
+                addRow(AudioManager.STREAM_NOTIFICATION, R.drawable.ic_volume_ringer,
+                        R.drawable.ic_volume_off, true, false);
                 addRow(STREAM_ALARM,
                         R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
                 addRow(AudioManager.STREAM_VOICE_CALL,
                         com.android.internal.R.drawable.ic_phone,
                         com.android.internal.R.drawable.ic_phone, false, false);
-                addRow(AudioManager.STREAM_BLUETOOTH_SCO,
-                        R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
                 addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
                         R.drawable.ic_volume_system_mute, false, false);
             }
@@ -1892,8 +1889,8 @@
                                 .equals(ss.remoteLabel)) {
                     addRow(
                             stream,
-                            R.drawable.ic_volume_media,
-                            R.drawable.ic_volume_media_mute,
+                            R.drawable.ic_volume_media_bt,
+                            R.drawable.ic_volume_media_bt_mute,
                             true,
                             false,
                             true);
@@ -1994,7 +1991,7 @@
                                             : R.drawable.ic_volume_media_bt;
             }
         } else if (isStreamMuted(ss)) {
-            iconRes = ss.muted ? R.drawable.ic_volume_media_off : row.iconMuteRes;
+            iconRes = (ss.muted && isTv()) ? R.drawable.ic_volume_media_off : row.iconMuteRes;
         } else {
             iconRes = mShowLowMediaVolumeIcon && ss.level * 2 < (ss.levelMax + ss.levelMin)
                       ? R.drawable.ic_volume_media_low : row.iconRes;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index 68d12f6..536403c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -20,9 +20,9 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.os.Handler;
 import android.util.Log;
 
+import com.android.settingslib.volume.data.repository.AudioRepository;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.qs.tiles.DndTile;
@@ -39,23 +39,26 @@
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final Handler mHandler = new Handler();
-
     private boolean mEnabled;
     private final Context mContext;
     private VolumeDialogComponent mVolumeComponent;
     private AudioSharingInteractor mAudioSharingInteractor;
+    private AudioRepository mAudioRepository;
 
     @Inject
-    public VolumeUI(Context context, VolumeDialogComponent volumeDialogComponent,
+    public VolumeUI(Context context,
+            VolumeDialogComponent volumeDialogComponent,
+            AudioRepository audioRepository,
             AudioSharingInteractor audioSharingInteractor) {
         mContext = context;
         mVolumeComponent = volumeDialogComponent;
+        mAudioRepository = audioRepository;
         mAudioSharingInteractor = audioSharingInteractor;
     }
 
     @Override
     public void start() {
+        mAudioRepository.init();
         boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
         boolean enableSafetyWarning =
                 mContext.getResources().getBoolean(R.bool.enable_safety_warning);
@@ -77,7 +80,8 @@
 
     @Override
     public void dump(PrintWriter pw, String[] args) {
-        pw.print("mEnabled="); pw.println(mEnabled);
+        pw.print("mEnabled=");
+        pw.println(mEnabled);
         if (!mEnabled) return;
         mVolumeComponent.dump(pw, args);
     }
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 5d8b6f1..20d598a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -70,6 +70,7 @@
                 coroutineContext,
                 coroutineScope,
                 volumeLogger,
+                com.android.systemui.Flags.useVolumeController(),
             )
 
         @Provides
@@ -79,13 +80,15 @@
             localBluetoothManager: LocalBluetoothManager?,
             @Application coroutineScope: CoroutineScope,
             @Background coroutineContext: CoroutineContext,
+            volumeLogger: VolumeLogger
         ): AudioSharingRepository =
             if (Flags.enableLeAudioSharing() && localBluetoothManager != null) {
                 AudioSharingRepositoryImpl(
                     contentResolver,
                     localBluetoothManager,
                     coroutineScope,
-                    coroutineContext
+                    coroutineContext,
+                    volumeLogger
                 )
             } else {
                 AudioSharingRepositoryEmptyImpl()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
index 73f5237..9715772 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/CaptioningModule.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.volume.dagger
 
 import android.view.accessibility.CaptioningManager
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepositoryImpl
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.data.repository.CaptioningRepository
+import com.android.systemui.accessibility.data.repository.CaptioningRepositoryImpl
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
 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/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index 154737c..73728e6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -26,7 +26,6 @@
 import com.android.settingslib.media.MediaDevice.MediaDeviceType
 import com.android.settingslib.media.PhoneMediaDevice
 import com.android.settingslib.volume.data.repository.AudioRepository
-import com.android.settingslib.volume.data.repository.AudioSharingRepository
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
@@ -37,7 +36,6 @@
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
@@ -60,7 +58,6 @@
     private val bluetoothAdapter: BluetoothAdapter?,
     private val deviceIconInteractor: DeviceIconInteractor,
     private val mediaOutputInteractor: MediaOutputInteractor,
-    audioSharingRepository: AudioSharingRepository,
 ) {
 
     val currentAudioDevice: StateFlow<AudioOutputDevice> =
@@ -78,10 +75,7 @@
             }
             .map { it ?: AudioOutputDevice.Unknown }
             .flowOn(backgroundCoroutineContext)
-            .stateIn(scope, SharingStarted.Eagerly, AudioOutputDevice.Unknown)
-
-    /** Whether the device is in audio sharing */
-    val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
+            .stateIn(scope, SharingStarted.Eagerly, AudioOutputDevice.Unavailable)
 
     private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
         if (
@@ -126,6 +120,11 @@
                     name = name,
                     icon = icon,
                 )
+            deviceType == MediaDeviceType.TYPE_CAST_DEVICE ->
+                AudioOutputDevice.Remote(
+                    name = name,
+                    icon = icon,
+                )
             else ->
                 AudioOutputDevice.BuiltIn(
                     name = name,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
index 2170c36..9aed8ab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioSharingInteractor.kt
@@ -36,11 +36,15 @@
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
 interface AudioSharingInteractor {
+    /** Audio sharing state on the device. */
+    val isInAudioSharing: Flow<Boolean>
+
     /** Audio sharing secondary headset volume changes. */
     val volume: Flow<Int?>
 
@@ -76,6 +80,7 @@
     private val audioVolumeInteractor: AudioVolumeInteractor,
     private val audioSharingRepository: AudioSharingRepository
 ) : AudioSharingInteractor {
+    override val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
 
     override val volume: Flow<Int?> =
         combine(audioSharingRepository.secondaryGroupId, audioSharingRepository.volumeMap) {
@@ -125,13 +130,13 @@
     }
 
     private companion object {
-        const val TAG = "AudioSharingInteractor"
         const val DEFAULT_VOLUME = 20
     }
 }
 
 @SysUISingleton
 class AudioSharingInteractorEmptyImpl @Inject constructor() : AudioSharingInteractor {
+    override val isInAudioSharing: Flow<Boolean> = flowOf(false)
     override val volume: Flow<Int?> = emptyFlow()
     override val volumeMin: Int = EMPTY_VOLUME
     override val volumeMax: Int = EMPTY_VOLUME
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
index ba0b082..0e4cac0b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/model/AudioOutputDevice.kt
@@ -31,6 +31,12 @@
         override val icon: Drawable?,
     ) : AudioOutputDevice
 
+    /** Models a cast audio output device. */
+    data class Remote(
+        override val name: String,
+        override val icon: Drawable?,
+    ) : AudioOutputDevice
+
     /** Models a wired audio output device. */
     data class Wired(
         override val name: String,
@@ -52,4 +58,16 @@
         override val icon: Drawable
             get() = error("Unsupported for unknown device")
     }
+
+    /**
+     * Models a state when current audio output device is not loaded yet or the system failed to
+     * load it.
+     */
+    data object Unavailable : AudioOutputDevice {
+        override val name: String
+            get() = error("Unsupported for unavailable device")
+
+        override val icon: Drawable
+            get() = error("Unsupported for unavailable device")
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
index 85da1d0..52f2ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/domain/CaptioningAvailabilityCriteria.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.volume.panel.component.captioning.domain
 
 import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
index ca5aef8..9e70843 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt
@@ -18,7 +18,7 @@
 
 import android.content.Context
 import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
+import com.android.systemui.accessibility.domain.interactor.CaptioningInteractor
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.res.R
 import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
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 ed25129..f94cbda 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
@@ -18,6 +18,7 @@
 
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.interactor.AudioSharingInteractor
 import com.android.systemui.volume.domain.model.AudioOutputDevice
 import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
@@ -49,11 +50,12 @@
     private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
     audioOutputInteractor: AudioOutputInteractor,
     audioModeInteractor: AudioModeInteractor,
-    interactor: MediaOutputInteractor,
+    mediaOutputInteractor: MediaOutputInteractor,
+    audioSharingInteractor: AudioSharingInteractor,
 ) {
 
     private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
-        interactor.defaultActiveMediaSession
+        mediaOutputInteractor.defaultActiveMediaSession
             .filterData()
             .flatMapLatest { session ->
                 if (session == null) {
@@ -72,34 +74,51 @@
             )
 
     private val currentAudioDevice: Flow<AudioOutputDevice> =
-        audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unknown }
+        audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unavailable }
 
+    /**
+     * Model for the Media Output component in the Volume Panel. It's guaranteed to have an
+     * available device if it's loaded.
+     */
     val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
-        audioModeInteractor.isOngoingCall
-            .flatMapLatest { isOngoingCall ->
-                audioOutputInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
-                    if (isOngoingCall) {
-                        currentAudioDevice.map {
-                            MediaOutputComponentModel.Calling(it, isInAudioSharing)
-                        }
-                    } else {
-                        combine(sessionWithPlaybackState.filterData(), currentAudioDevice) {
-                            sessionWithPlaybackState,
-                            currentAudioDevice ->
-                            if (sessionWithPlaybackState == null) {
-                                MediaOutputComponentModel.Idle(currentAudioDevice, isInAudioSharing)
-                            } else {
-                                MediaOutputComponentModel.MediaSession(
-                                    sessionWithPlaybackState.session,
-                                    sessionWithPlaybackState.isPlaybackActive,
-                                    currentAudioDevice,
-                                    isInAudioSharing,
-                                )
-                            }
+        combine(
+                audioSharingInteractor.isInAudioSharing,
+                audioModeInteractor.isOngoingCall,
+                currentAudioDevice,
+            ) { isInAudioSharing, isOngoingCall, currentAudioDevice ->
+                if (isOngoingCall) {
+                    flowOf(
+                        MediaOutputComponentModel.Calling(
+                            device = currentAudioDevice,
+                            isInAudioSharing = isInAudioSharing,
+                            canOpenAudioSwitcher = false,
+                        )
+                    )
+                } else {
+                    sessionWithPlaybackState.filterData().map { sessionWithPlaybackState ->
+                        if (sessionWithPlaybackState == null) {
+                            MediaOutputComponentModel.Idle(
+                                device = currentAudioDevice,
+                                isInAudioSharing = isInAudioSharing,
+                                canOpenAudioSwitcher =
+                                    !isInAudioSharing &&
+                                        currentAudioDevice !is AudioOutputDevice.Unknown,
+                            )
+                        } else {
+                            MediaOutputComponentModel.MediaSession(
+                                session = sessionWithPlaybackState.session,
+                                isPlaybackActive = sessionWithPlaybackState.isPlaybackActive,
+                                device = currentAudioDevice,
+                                isInAudioSharing = isInAudioSharing,
+                                canOpenAudioSwitcher =
+                                    !isInAudioSharing &&
+                                        currentAudioDevice !is AudioOutputDevice.Unknown,
+                            )
                         }
                     }
                 }
             }
+            .flatMapLatest { it }
             .wrapInResult()
             .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 31a8977..aa07cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -179,9 +179,7 @@
         return MediaDeviceSession(
             packageName = packageName,
             sessionToken = sessionToken,
-            canAdjustVolume =
-                playbackInfo != null &&
-                    playbackInfo?.volumeControl != VolumeProvider.VOLUME_CONTROL_FIXED,
+            canAdjustVolume = playbackInfo.volumeControl != VolumeProvider.VOLUME_CONTROL_FIXED,
             appLabel = getApplicationLabel(packageName) ?: return null
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
index 220fb2b..6588b44 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
@@ -24,11 +24,13 @@
 
     val device: AudioOutputDevice
     val isInAudioSharing: Boolean
+    val canOpenAudioSwitcher: Boolean
 
     /** There is an ongoing call on the device. */
     data class Calling(
         override val device: AudioOutputDevice,
         override val isInAudioSharing: Boolean,
+        override val canOpenAudioSwitcher: Boolean,
     ) : MediaOutputComponentModel
 
     /** There is media playing on the device. */
@@ -37,11 +39,13 @@
         val isPlaybackActive: Boolean,
         override val device: AudioOutputDevice,
         override val isInAudioSharing: Boolean,
+        override val canOpenAudioSwitcher: Boolean,
     ) : MediaOutputComponentModel
 
     /** There is nothing playing on the device. */
     data class Idle(
         override val device: AudioOutputDevice,
         override val isInAudioSharing: Boolean,
+        override val canOpenAudioSwitcher: Boolean,
     ) : MediaOutputComponentModel
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
index 8ba672d..42f88b4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/ConnectedDeviceViewModel.kt
@@ -16,11 +16,15 @@
 
 package com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel
 
+import com.android.systemui.common.shared.model.Color
+
 /**
  * Models part of the Media Session Volume Panel component that displays connected device
  * information.
  */
 data class ConnectedDeviceViewModel(
     val label: CharSequence,
+    val labelColor: Color,
     val deviceName: CharSequence?,
+    val deviceNameColor: Color,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 36b42f2..e565de5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -75,12 +75,25 @@
                         }
                     }
                 ConnectedDeviceViewModel(
-                    label,
-                    if (mediaOutputModel.isInAudioSharing) {
-                        context.getString(R.string.audio_sharing_description)
-                    } else {
-                        mediaOutputModel.device.name
-                    },
+                    label = label,
+                    labelColor =
+                        Color.Attribute(com.android.internal.R.attr.materialColorOnSurfaceVariant),
+                    deviceName =
+                        if (mediaOutputModel.isInAudioSharing) {
+                            context.getString(R.string.audio_sharing_description)
+                        } else {
+                            mediaOutputModel.device
+                                .takeIf { it !is AudioOutputDevice.Unknown }
+                                ?.name ?: context.getString(R.string.media_seamless_other_device)
+                        },
+                    deviceNameColor =
+                        if (mediaOutputModel.canOpenAudioSwitcher) {
+                            Color.Attribute(com.android.internal.R.attr.materialColorOnSurface)
+                        } else {
+                            Color.Attribute(
+                                com.android.internal.R.attr.materialColorOnSurfaceVariant
+                            )
+                        },
                 )
             }
             .stateIn(
@@ -107,19 +120,39 @@
                     DeviceIconViewModel.IsPlaying(
                         icon = icon,
                         iconColor =
-                            Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+                            if (mediaOutputModel.canOpenAudioSwitcher) {
+                                Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+                            } else {
+                                Color.Attribute(
+                                    com.android.internal.R.attr.materialColorSurfaceContainerHighest
+                                )
+                            },
                         backgroundColor =
-                            Color.Attribute(com.android.internal.R.attr.materialColorSecondary),
+                            if (mediaOutputModel.canOpenAudioSwitcher) {
+                                Color.Attribute(com.android.internal.R.attr.materialColorSecondary)
+                            } else {
+                                Color.Attribute(com.android.internal.R.attr.materialColorOutline)
+                            },
                     )
                 } else {
                     DeviceIconViewModel.IsNotPlaying(
                         icon = icon,
                         iconColor =
-                            Color.Attribute(
-                                com.android.internal.R.attr.materialColorOnSurfaceVariant
-                            ),
+                            if (mediaOutputModel.canOpenAudioSwitcher) {
+                                Color.Attribute(
+                                    com.android.internal.R.attr.materialColorOnSurfaceVariant
+                                )
+                            } else {
+                                Color.Attribute(com.android.internal.R.attr.materialColorOutline)
+                            },
                         backgroundColor =
-                            Color.Attribute(com.android.internal.R.attr.materialColorSurface),
+                            if (mediaOutputModel.canOpenAudioSwitcher) {
+                                Color.Attribute(com.android.internal.R.attr.materialColorSurface)
+                            } else {
+                                Color.Attribute(
+                                    com.android.internal.R.attr.materialColorSurfaceContainerHighest
+                                )
+                            },
                     )
                 }
             }
@@ -132,7 +165,7 @@
     val enabled: StateFlow<Boolean> =
         mediaOutputComponentInteractor.mediaOutputModel
             .filterData()
-            .map { !it.isInAudioSharing }
+            .map { it.canOpenAudioSwitcher }
             .stateIn(
                 coroutineScope,
                 SharingStarted.Eagerly,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index cfcd6b1..56d0bce 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -63,13 +63,7 @@
     private val changes = MutableSharedFlow<Unit>()
     private val currentAudioDeviceAttributes: StateFlow<AudioDeviceAttributes?> =
         audioOutputInteractor.currentAudioDevice
-            .map { audioDevice ->
-                if (audioDevice is AudioOutputDevice.Unknown) {
-                    builtinSpeaker
-                } else {
-                    audioDevice.getAudioDeviceAttributes()
-                }
-            }
+            .map { audioDevice -> audioDevice.getAudioDeviceAttributes() }
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), builtinSpeaker)
 
     /**
@@ -185,7 +179,10 @@
                         .firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
                 }
             }
-            else -> null
+            is AudioOutputDevice.Wired -> null
+            is AudioOutputDevice.Remote -> null
+            is AudioOutputDevice.Unknown -> builtinSpeaker
+            is AudioOutputDevice.Unavailable -> builtinSpeaker
         }
     }
 
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 fa40059..4be680e 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
@@ -16,9 +16,8 @@
 
 package com.android.systemui.volume.panel.component.volume.domain.interactor
 
-import android.media.AudioDeviceInfo
 import android.media.AudioManager
-import com.android.settingslib.volume.data.repository.AudioRepository
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
@@ -41,17 +40,21 @@
 constructor(
     @VolumePanelScope scope: CoroutineScope,
     mediaOutputInteractor: MediaOutputInteractor,
-    audioRepository: AudioRepository,
+    audioModeInteractor: AudioModeInteractor,
 ) {
 
     val volumePanelSliders: StateFlow<List<SliderType>> =
         combineTransform(
                 mediaOutputInteractor.activeMediaDeviceSessions,
                 mediaOutputInteractor.defaultActiveMediaSession.filterData(),
-                audioRepository.communicationDevice,
-            ) { activeSessions, defaultSession, communicationDevice ->
+                audioModeInteractor.isOngoingCall,
+            ) { activeSessions, defaultSession, isOngoingCall ->
                 coroutineScope {
                     val viewModels = buildList {
+                        if (isOngoingCall) {
+                            addStream(AudioManager.STREAM_VOICE_CALL)
+                        }
+
                         if (defaultSession?.isTheSameSession(activeSessions.remote) == true) {
                             addSession(activeSessions.remote)
                             addStream(AudioManager.STREAM_MUSIC)
@@ -60,11 +63,10 @@
                             addSession(activeSessions.remote)
                         }
 
-                        if (communicationDevice?.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
-                            addStream(AudioManager.STREAM_BLUETOOTH_SCO)
-                        } else {
+                        if (!isOngoingCall) {
                             addStream(AudioManager.STREAM_VOICE_CALL)
                         }
+
                         addStream(AudioManager.STREAM_RING)
                         addStream(AudioManager.STREAM_NOTIFICATION)
                         addStream(AudioManager.STREAM_ALARM)
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 521f608..ffb1f11 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
@@ -66,7 +66,6 @@
         mapOf(
             AudioStream(AudioManager.STREAM_MUSIC) to R.drawable.ic_music_note,
             AudioStream(AudioManager.STREAM_VOICE_CALL) to R.drawable.ic_call,
-            AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.drawable.ic_call,
             AudioStream(AudioManager.STREAM_RING) to R.drawable.ic_ring_volume,
             AudioStream(AudioManager.STREAM_NOTIFICATION) to R.drawable.ic_volume_ringer,
             AudioStream(AudioManager.STREAM_ALARM) to R.drawable.ic_volume_alarm,
@@ -75,7 +74,6 @@
         mapOf(
             AudioStream(AudioManager.STREAM_MUSIC) to R.string.stream_music,
             AudioStream(AudioManager.STREAM_VOICE_CALL) to R.string.stream_voice_call,
-            AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to R.string.stream_voice_call,
             AudioStream(AudioManager.STREAM_RING) to R.string.stream_ring,
             AudioStream(AudioManager.STREAM_NOTIFICATION) to R.string.stream_notification,
             AudioStream(AudioManager.STREAM_ALARM) to R.string.stream_alarm,
@@ -91,8 +89,6 @@
                 VolumePanelUiEvent.VOLUME_PANEL_MUSIC_SLIDER_TOUCHED,
             AudioStream(AudioManager.STREAM_VOICE_CALL) to
                 VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
-            AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to
-                VolumePanelUiEvent.VOLUME_PANEL_VOICE_CALL_SLIDER_TOUCHED,
             AudioStream(AudioManager.STREAM_RING) to
                 VolumePanelUiEvent.VOLUME_PANEL_RING_SLIDER_TOUCHED,
             AudioStream(AudioManager.STREAM_NOTIFICATION) to
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
index 4b4d69a..45732de 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/AudioVolumeComponentViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.component.volume.ui.viewmodel
 
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
@@ -35,9 +36,11 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
@@ -58,24 +61,31 @@
     mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
     private val streamSliderViewModelFactory: AudioStreamSliderViewModel.Factory,
     private val castVolumeSliderViewModelFactory: CastVolumeSliderViewModel.Factory,
+    audioModeInteractor: AudioModeInteractor,
     streamsInteractor: AudioSlidersInteractor,
 ) {
 
     private val mutableIsExpanded = MutableStateFlow<Boolean?>(null)
-    private val isPlaybackActive: Flow<Boolean?> =
-        mediaOutputInteractor.defaultActiveMediaSession
-            .filterData()
-            .flatMapLatest { session ->
-                if (session == null) {
-                    flowOf(false)
-                } else {
-                    mediaDeviceSessionInteractor.playbackState(session).map { it?.isActive == true }
-                }
+    private val isActive: Flow<Boolean?> =
+        combine(
+                audioModeInteractor.isOngoingCall,
+                mediaOutputInteractor.defaultActiveMediaSession.filterData().flatMapLatest { session
+                    ->
+                    if (session == null) {
+                        flowOf(false)
+                    } else {
+                        mediaDeviceSessionInteractor.playbackState(session).map {
+                            it?.isActive == true
+                        }
+                    }
+                },
+            ) { isOngoingCall, isPlaybackActive ->
+                isOngoingCall || isPlaybackActive
             }
-            .onEach { isPlaybackActive -> mutableIsExpanded.value = !isPlaybackActive }
             .stateIn(scope, SharingStarted.Eagerly, null)
+
     private val portraitExpandable: Flow<SlidersExpandableViewModel> =
-        isPlaybackActive
+        isActive
             .filterNotNull()
             .flatMapLatest { isActive ->
                 if (isActive) {
@@ -105,6 +115,10 @@
             }
             .stateIn(scope, SharingStarted.Eagerly, emptyList())
 
+    init {
+        isActive.filterNotNull().onEach { mutableIsExpanded.value = !it }.launchIn(scope)
+    }
+
     fun isExpandable(isPortrait: Boolean): Flow<SlidersExpandableViewModel> {
         return if (isPortrait) {
             portraitExpandable
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
index e46ce26..24fb001 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepository.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
 import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -27,10 +28,15 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.update
 
-private const val TAG = "VolumePanelGlobalState"
+private const val TAG = "VolumePanelGlobalStateRepository"
 
 @SysUISingleton
-class VolumePanelGlobalStateRepository @Inject constructor(dumpManager: DumpManager) : Dumpable {
+class VolumePanelGlobalStateRepository
+@Inject
+constructor(
+    dumpManager: DumpManager,
+    private val logger: VolumePanelLogger,
+) : Dumpable {
 
     private val mutableGlobalState =
         MutableStateFlow(
@@ -48,6 +54,7 @@
         update: (currentState: VolumePanelGlobalState) -> VolumePanelGlobalState
     ) {
         mutableGlobalState.update(update)
+        logger.onVolumePanelGlobalStateChanged(mutableGlobalState.value)
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
index 5301b00..9de862a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractor.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.domain.model.ComponentModel
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import javax.inject.Inject
 import javax.inject.Provider
@@ -26,8 +27,12 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
 
 interface ComponentsInteractor {
 
@@ -45,6 +50,7 @@
     enabledComponents: Collection<VolumePanelComponentKey>,
     defaultCriteria: Provider<ComponentAvailabilityCriteria>,
     @VolumePanelScope coroutineScope: CoroutineScope,
+    private val logger: VolumePanelLogger,
     private val criteriaByKey:
         Map<
             VolumePanelComponentKey,
@@ -57,12 +63,18 @@
         combine(
                 enabledComponents.map { componentKey ->
                     val componentCriteria = (criteriaByKey[componentKey] ?: defaultCriteria).get()
-                    componentCriteria.isAvailable().map { isAvailable ->
-                        ComponentModel(componentKey, isAvailable = isAvailable)
-                    }
+                    componentCriteria
+                        .isAvailable()
+                        .distinctUntilChanged()
+                        .conflate()
+                        .onEach { logger.onComponentAvailabilityChanged(componentKey, it) }
+                        .map { isAvailable ->
+                            ComponentModel(componentKey, isAvailable = isAvailable)
+                        }
                 }
             ) {
                 it.asList()
             }
-            .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 1)
+            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+            .filterNotNull()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
index cc513b5..276326c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/shared/VolumePanelLogger.kt
@@ -20,15 +20,41 @@
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.dagger.VolumeLog
-import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
+import com.android.systemui.volume.panel.shared.model.VolumePanelGlobalState
+import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
 import javax.inject.Inject
 
 private const val TAG = "SysUI_VolumePanel"
 
 /** Logs events related to the Volume Panel. */
-@VolumePanelScope
 class VolumePanelLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) {
 
+    fun onVolumePanelStateChanged(state: VolumePanelState) {
+        logBuffer.log(TAG, LogLevel.DEBUG, { str1 = state.toString() }, { "State changed: $str1" })
+    }
+
+    fun onComponentAvailabilityChanged(key: VolumePanelComponentKey, isAvailable: Boolean) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = key
+                bool1 = isAvailable
+            },
+            { "$str1 isAvailable=$bool1" }
+        )
+    }
+
+    fun onVolumePanelGlobalStateChanged(globalState: VolumePanelGlobalState) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { bool1 = globalState.isVisible },
+            { "Global state changed: isVisible=$bool1" }
+        )
+    }
+
     fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
         logBuffer.log(
             TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
index 1c51236..a06d3e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/layout/ComponentsLayout.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.ui.layout
 
 import com.android.systemui.volume.panel.ui.viewmodel.ComponentState
+import com.android.systemui.volume.panel.ui.viewmodel.toLogString
 
 /** Represents components grouping into the layout. */
 data class ComponentsLayout(
@@ -29,3 +30,12 @@
     /** This is a separated entity that is always visible on the bottom of the Volume Panel. */
     val bottomBarComponent: ComponentState,
 )
+
+fun ComponentsLayout.toLogString(): String {
+    return "(" +
+        " headerComponents=${headerComponents.joinToString { it.toLogString() }}" +
+        " contentComponents=${contentComponents.joinToString { it.toLogString() }}" +
+        " footerComponents=${footerComponents.joinToString { it.toLogString() }}" +
+        " bottomBarComponent=${bottomBarComponent.toLogString()}" +
+        " )"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
index 5f4dbfb..41c80fa 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/ComponentState.kt
@@ -32,3 +32,5 @@
     val component: VolumePanelUiComponent,
     val isVisible: Boolean,
 )
+
+fun ComponentState.toLogString(): String = "$key:visible=$isVisible"
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
index f495a02f..2f60c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModel.kt
@@ -19,23 +19,30 @@
 import android.content.Context
 import android.content.IntentFilter
 import android.content.res.Resources
+import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.util.kotlin.launchAndDispose
 import com.android.systemui.volume.VolumePanelDialogReceiver
 import com.android.systemui.volume.panel.dagger.VolumePanelComponent
 import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory
 import com.android.systemui.volume.panel.domain.VolumePanelStartable
 import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor
 import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor
+import com.android.systemui.volume.panel.shared.VolumePanelLogger
 import com.android.systemui.volume.panel.ui.composable.ComponentsFactory
 import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
 import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager
+import com.android.systemui.volume.panel.ui.layout.toLogString
+import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
@@ -43,19 +50,23 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
+private const val TAG = "VolumePanelViewModel"
+
 // Can't inject a constructor here because VolumePanelComponent provides this view model for its
 // components.
+@OptIn(ExperimentalCoroutinesApi::class)
 class VolumePanelViewModel(
     resources: Resources,
     coroutineScope: CoroutineScope,
     daggerComponentFactory: VolumePanelComponentFactory,
     configurationController: ConfigurationController,
     broadcastDispatcher: BroadcastDispatcher,
+    private val dumpManager: DumpManager,
+    private val logger: VolumePanelLogger,
     private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
-) {
+) : Dumpable {
 
     private val volumePanelComponent: VolumePanelComponent =
         daggerComponentFactory.create(this, coroutineScope)
@@ -77,9 +88,10 @@
             .onStart { emit(resources.configuration) }
             .map { configuration ->
                 VolumePanelState(
-                    orientation = configuration.orientation,
-                    isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
-                )
+                        orientation = configuration.orientation,
+                        isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen),
+                    )
+                    .also { logger.onVolumePanelStateChanged(it) }
             }
             .stateIn(
                 scope,
@@ -89,7 +101,7 @@
                     isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen)
                 ),
             )
-    val componentsLayout: Flow<ComponentsLayout> =
+    val componentsLayout: StateFlow<ComponentsLayout?> =
         combine(
                 componentsInteractor.components,
                 volumePanelState,
@@ -104,13 +116,18 @@
                     }
                 componentsLayoutManager.layout(scope, componentStates)
             }
-            .shareIn(
+            .stateIn(
                 scope,
                 SharingStarted.Eagerly,
-                replay = 1,
+                null,
             )
 
     init {
+        scope.launchAndDispose {
+            dumpManager.registerNormalDumpable(TAG, this)
+            DisposableHandle { dumpManager.unregisterDumpable(TAG) }
+        }
+
         volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start)
         broadcastDispatcher
             .broadcastFlow(IntentFilter(VolumePanelDialogReceiver.DISMISS_ACTION))
@@ -122,6 +139,13 @@
         volumePanelGlobalStateInteractor.setVisible(false)
     }
 
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        with(pw) {
+            println("volumePanelState=${volumePanelState.value}")
+            println("componentsLayout=${componentsLayout.value?.toLogString()}")
+        }
+    }
+
     class Factory
     @Inject
     constructor(
@@ -129,6 +153,8 @@
         private val daggerComponentFactory: VolumePanelComponentFactory,
         private val configurationController: ConfigurationController,
         private val broadcastDispatcher: BroadcastDispatcher,
+        private val dumpManager: DumpManager,
+        private val logger: VolumePanelLogger,
         private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor,
     ) {
 
@@ -139,6 +165,8 @@
                 daggerComponentFactory,
                 configurationController,
                 broadcastDispatcher,
+                dumpManager,
+                logger,
                 volumePanelGlobalStateInteractor,
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
index 869a82a..d6b159e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/shared/VolumeLogger.kt
@@ -16,7 +16,8 @@
 
 package com.android.systemui.volume.shared
 
-import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
+import com.android.settingslib.volume.shared.AudioLogger
+import com.android.settingslib.volume.shared.AudioSharingLogger
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 import com.android.systemui.dagger.SysUISingleton
@@ -30,7 +31,7 @@
 /** Logs general System UI volume events. */
 @SysUISingleton
 class VolumeLogger @Inject constructor(@VolumeLog private val logBuffer: LogBuffer) :
-    AudioRepositoryImpl.Logger {
+    AudioLogger, AudioSharingLogger {
 
     override fun onSetVolumeRequested(audioStream: AudioStream, volume: Int) {
         logBuffer.log(
@@ -55,4 +56,35 @@
             { "Volume update received: stream=$str1 volume=$int1" }
         )
     }
+
+    override fun onAudioSharingStateChanged(state: Boolean) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { bool1 = state },
+            { "Audio sharing state update: state=$bool1" }
+        )
+    }
+
+    override fun onSecondaryGroupIdChanged(groupId: Int) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { int1 = groupId },
+            { "Secondary group id in audio sharing update: groupId=$int1" }
+        )
+    }
+
+    override fun onVolumeMapChanged(map: Map<Int, Int>) {
+        logBuffer.log(
+            TAG,
+            LogLevel.DEBUG,
+            { str1 = map.toString() },
+            { "Volume map update: map=$str1" }
+        )
+    }
+
+    override fun onSetDeviceVolumeRequested(volume: Int) {
+        logBuffer.log(TAG, LogLevel.DEBUG, { int1 = volume }, { "Set device volume: volume=$int1" })
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 4841c78..ea213cb 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -23,11 +23,19 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.qs.QsEventLogger;
+import com.android.systemui.qs.pipeline.shared.TileSpec;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.QuickAccessWalletTile;
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
+import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
+import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.res.R;
 import com.android.systemui.wallet.controller.WalletContextualLocationsService;
 import com.android.systemui.wallet.ui.WalletActivity;
 
+import java.util.concurrent.Executor;
+
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
@@ -35,14 +43,14 @@
 import dagger.multibindings.IntoMap;
 import dagger.multibindings.StringKey;
 
-import java.util.concurrent.Executor;
-
 /**
  * Module for injecting classes in Wallet.
  */
 @Module
 public abstract class WalletModule {
 
+    public static final String WALLET_TILE_SPEC = "wallet";
+
     @Binds
     @IntoMap
     @ClassKey(WalletContextualLocationsService.class)
@@ -69,4 +77,21 @@
     @StringKey(QuickAccessWalletTile.TILE_SPEC)
     public abstract QSTileImpl<?> bindQuickAccessWalletTile(
             QuickAccessWalletTile quickAccessWalletTile);
+
+    @Provides
+    @IntoMap
+    @StringKey(WALLET_TILE_SPEC)
+    public static QSTileConfig provideQuickAccessWalletTileConfig(QsEventLogger uiEventLogger) {
+        TileSpec tileSpec = TileSpec.create(WALLET_TILE_SPEC);
+        return new QSTileConfig(
+                tileSpec,
+                new QSTileUIConfig.Resource(
+                        R.drawable.ic_wallet_lockscreen,
+                        R.string.wallet_title
+                ),
+                uiEventLogger.getNewInstanceId(),
+                tileSpec.getSpec(),
+                QSTilePolicy.NoRestrictions.INSTANCE
+        );
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 2e29bbd..b1c6455 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -21,6 +21,7 @@
 import static android.app.WallpaperManager.SetWallpaperFlags;
 
 import static com.android.systemui.Flags.fixImageWallpaperCrashSurfaceAlreadyReleased;
+import static com.android.window.flags.Flags.multiCrop;
 import static com.android.window.flags.Flags.offloadColorExtraction;
 
 import android.annotation.Nullable;
@@ -190,7 +191,10 @@
             }
             mWallpaperManager = getDisplayContext().getSystemService(WallpaperManager.class);
             mSurfaceHolder = surfaceHolder;
-            Rect dimensions = mWallpaperManager.peekBitmapDimensions(getSourceFlag(), true);
+            Rect dimensions = !multiCrop()
+                    ? mWallpaperManager.peekBitmapDimensions(getSourceFlag(), true)
+                    : mWallpaperManager.peekBitmapDimensionsAsUser(getSourceFlag(), true,
+                    mUserTracker.getUserId());
             int width = Math.max(MIN_SURFACE_WIDTH, dimensions.width());
             int height = Math.max(MIN_SURFACE_HEIGHT, dimensions.height());
             mSurfaceHolder.setFixedSize(width, height);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 45799b2..7385b82 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -115,7 +115,6 @@
     private final Executor mSysuiUiBgExecutor;
 
     private final Bubbles.SysuiProxy mSysuiProxy;
-    // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
     private final StatusBarWindowCallback mStatusBarWindowCallback;
     private final Runnable mSensitiveStateChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 9ca0591..5f6ad92 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,6 +34,8 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.util.Log;
 import android.view.Display;
 import android.view.KeyEvent;
@@ -58,7 +60,6 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.kotlin.JavaAdapter;
-import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.onehanded.OneHanded;
@@ -68,6 +69,7 @@
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.recents.RecentTasks;
+import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
 import com.android.wm.shell.splitscreen.SplitScreen;
 import com.android.wm.shell.sysui.ShellInterface;
 
@@ -378,8 +380,8 @@
             }
 
             @Override
-            public void setImeWindowStatus(int displayId, int vis, int backDisposition,
-                    boolean showImeSwitcher) {
+            public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+                    @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
                 if (displayId == mDisplayTracker.getDefaultDisplayId()
                         && (vis & InputMethodService.IME_VISIBLE) != 0) {
                     oneHanded.stopOneHanded(
diff --git a/packages/SystemUI/tests/Android.bp b/packages/SystemUI/tests/Android.bp
index 88939a2..f601387 100644
--- a/packages/SystemUI/tests/Android.bp
+++ b/packages/SystemUI/tests/Android.bp
@@ -50,3 +50,14 @@
     additional_manifests: ["AndroidManifest.xml"],
     manifest: "AndroidManifest-base.xml",
 }
+
+test_module_config {
+    name: "SystemUITests_systemui_accessibility",
+    base: "SystemUITests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.systemui.accessibility"],
+    exclude_annotations: [
+        "android.platform.test.annotations.Postsubmit",
+        "android.platform.test.annotations.FlakyTest",
+    ],
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index a8ab922..d85b774 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -446,14 +446,10 @@
         assertFalse(mWifiRepository.isWifiConnectedWithValidSsid());
         mWifiRepository.setWifiNetwork(
                 new WifiNetworkModel.Active(
-                        /* networkId= */ 0,
                         /* isValidated= */ false,
                         /* level= */ 0,
                         /* ssid= */ "",
-                        /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE,
-                        /* isPasspointAccessPoint= */ false,
-                        /* isOnlineSignUpForPasspointAccessPoint= */ false,
-                        /* passpointProviderFriendlyName= */ null));
+                        /* hotspotDeviceType= */ WifiNetworkModel.HotspotDeviceType.NONE));
         assertTrue(mWifiRepository.isWifiConnectedWithValidSsid());
 
         mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 347605d..43a78035 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityTaskManager
 import android.content.pm.PackageManager
 import android.os.PowerManager
+import android.platform.test.annotations.EnableFlags
 import android.telecom.TelecomManager
 import android.telephony.TelephonyManager
 import android.testing.TestableLooper
@@ -26,14 +27,20 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer
+import com.android.systemui.haptics.msdl.msdlPlayer
 import com.android.systemui.shade.ShadeController
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.testKosmos
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -64,6 +71,8 @@
     val fakeSystemClock = FakeSystemClock()
     val mainExecutor = FakeExecutor(fakeSystemClock)
     val backgroundExecutor = FakeExecutor(fakeSystemClock)
+    private val kosmos = testKosmos()
+    private val msdlPlayer: FakeMSDLPlayer = kosmos.msdlPlayer
 
     lateinit var underTest: EmergencyButtonController
 
@@ -84,6 +93,7 @@
                 mainExecutor,
                 backgroundExecutor,
                 mSelectedUserInteractor,
+                msdlPlayer,
             )
         context.setMockPackageManager(packageManager)
         Mockito.`when`(emergencyButton.context).thenReturn(context)
@@ -113,4 +123,13 @@
                 /* isSecure= */ eq(true)
             )
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    fun takeEmergencyCallAction_withMSDLFeedback_playsEmergencyButtonTokenAndNullAttributes() {
+        underTest.takeEmergencyCallAction()
+
+        assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.KEYPRESS_RETURN)
+        assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index e724c60..c0d8be3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -27,6 +29,7 @@
 import static org.mockito.Mockito.when;
 
 import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.KeyEvent;
@@ -43,9 +46,13 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.haptics.msdl.FakeMSDLPlayer;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.res.R;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
 
+import com.google.android.msdl.data.model.MSDLToken;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -85,7 +92,11 @@
     private FakeFeatureFlags mFeatureFlags;
     @Mock
     private SelectedUserInteractor mSelectedUserInteractor;
+    @Mock
+    private UserActivityNotifier mUserActivityNotifier;
     private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
+    private KosmosJavaAdapter mKosmosJavaAdapter = new KosmosJavaAdapter(this);
+    private final FakeMSDLPlayer mMSDLPlayer = mKosmosJavaAdapter.getMsdlPlayer();
 
     @Before
     public void setup() {
@@ -108,7 +119,8 @@
         return new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector,
-                mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor) {
+                mEmergencyButtonController, mFeatureFlags, mSelectedUserInteractor, mMSDLPlayer,
+                mUserActivityNotifier) {
             @Override
             void resetState() {
             }
@@ -197,4 +209,32 @@
         verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true);
         verify(mAbsKeyInputView, never()).setPasswordEntryEnabled(true);
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    public void onPasswordChecked_withMSDLFeedback_withMatch_playsUnlockToken() {
+        mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+        assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.UNLOCK);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    public void onPasswordChecked_withoutMSDLFeedback_withMatch_doesNotPlayToken() {
+        mKeyguardAbsKeyInputViewController.onPasswordChecked(0, true, 100, true);
+        assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    public void onPasswordChecked_withMSDLFeedback_withoutMatch_playsFailureToken() {
+        mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+        assertThat(mMSDLPlayer.getLatestTokenPlayed()).isEqualTo(MSDLToken.FAILURE);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
+    public void onPasswordChecked_withoutMSDLFeedback_withoutMatch_doesNotPlayToken() {
+        mKeyguardAbsKeyInputViewController.onPasswordChecked(0, false, 100, true);
+        assertThat(mMSDLPlayer.getLatestTokenPlayed()).isNull();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 36d4d12..873bc2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -103,6 +103,7 @@
     @Mock lateinit var deleteButton: NumPadButton
     @Mock lateinit var enterButton: View
     @Mock lateinit var uiEventLogger: UiEventLogger
+    @Mock lateinit var mUserActivityNotifier: UserActivityNotifier
 
     @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
 
@@ -149,7 +150,9 @@
             featureFlags,
             mSelectedUserInteractor,
             uiEventLogger,
-            keyguardKeyboardInteractor
+            keyguardKeyboardInteractor,
+            null,
+            mUserActivityNotifier
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 7151c42..f141a49 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -69,6 +69,7 @@
     @Mock
     private lateinit var keyguardMessageAreaController:
         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+    @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
     private val updateMonitorCallbackArgumentCaptor =
         ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
 
@@ -101,7 +102,9 @@
                 emergencyButtonController,
                 fakeFeatureFlags,
                 mSelectedUserInteractor,
-                keyguardKeyboardInteractor
+                keyguardKeyboardInteractor,
+                null,
+                mUserActivityNotifier
             )
         underTest.init()
         underTest.onViewAttached()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index acae913..a03c839 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -63,6 +63,7 @@
     @Mock
     private lateinit var keyguardMessageAreaController:
         KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+    @Mock private lateinit var mUserActivityNotifier: UserActivityNotifier
 
     @Before
     fun setup() {
@@ -96,7 +97,9 @@
                 emergencyButtonController,
                 fakeFeatureFlags,
                 mSelectedUserInteractor,
-                keyguardKeyboardInteractor
+                keyguardKeyboardInteractor,
+                null,
+                mUserActivityNotifier
             )
         underTest.init()
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 075d8ae..52fde7e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -102,6 +102,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.dreams.IDreamManager;
 import android.service.trust.TrustAgentService;
 import android.telephony.ServiceState;
@@ -111,9 +112,9 @@
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.compose.animation.scene.ObservableTransitionState;
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
@@ -129,6 +130,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfig;
 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigImpl;
@@ -138,9 +140,13 @@
 import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
 import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.SessionTracker;
 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.settings.UserTracker;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -149,6 +155,7 @@
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import org.junit.After;
@@ -175,8 +182,11 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper
 public class KeyguardUpdateMonitorTest extends SysuiTestCase {
     private static final String PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY =
@@ -277,6 +287,12 @@
     private SelectedUserInteractor mSelectedUserInteractor;
     @Mock
     private DeviceEntryFaceAuthInteractor mFaceAuthInteractor;
+    @Mock
+    private AlternateBouncerInteractor mAlternateBouncerInteractor;
+    @Mock
+    private JavaAdapter mJavaAdapter;
+    @Mock
+    private SceneInteractor mSceneInteractor;
     @Captor
     private ArgumentCaptor<FaceAuthenticationListener> mFaceAuthenticationListener;
 
@@ -301,6 +317,16 @@
             mFingerprintAuthenticatorsRegisteredCallback;
     private final InstanceId mKeyguardInstanceId = InstanceId.fakeInstanceId(999);
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
+    }
+
+    public KeyguardUpdateMonitorTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setup() throws RemoteException {
         mKosmos = new KosmosJavaAdapter(this);
@@ -328,7 +354,6 @@
         ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(SubscriptionManager::getDefaultSubscriptionId);
         when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mCurrentUserId);
-        when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mCurrentUserId);
 
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.systemui.res.R.integer.config_face_auth_supported_posture,
@@ -993,7 +1018,7 @@
         verifyFingerprintAuthenticateNeverCalled();
         // WHEN alternate bouncer is shown
         mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
-        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+        mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
 
         // THEN make sure FP listening begins
         verifyFingerprintAuthenticateCall();
@@ -1489,7 +1514,7 @@
     @Test
     public void testShouldNotListenForUdfps_whenInLockDown() {
         // GIVEN a "we should listen for udfps" state
-        setKeyguardBouncerVisibility(false /* isVisible */);
+        mKeyguardUpdateMonitor.setPrimaryBouncerVisibility(false /* isVisible */);
         mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
         when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true);
 
@@ -2124,7 +2149,7 @@
         verifyFingerprintAuthenticateNeverCalled();
 
         mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
-        mKeyguardUpdateMonitor.setAlternateBouncerShowing(true);
+        mKeyguardUpdateMonitor.setAlternateBouncerVisibility(true);
 
         verifyFingerprintAuthenticateCall();
     }
@@ -2323,12 +2348,7 @@
     }
 
     private void bouncerFullyVisible() {
-        setKeyguardBouncerVisibility(true);
-    }
-
-    private void setKeyguardBouncerVisibility(boolean isVisible) {
-        mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
-        mTestableLooper.processAllMessages();
+        mKeyguardUpdateMonitor.setPrimaryBouncerVisibility(true);
     }
 
     private void setBroadcastReceiverPendingResult(BroadcastReceiver receiver) {
@@ -2434,7 +2454,12 @@
                     mPackageManager, mFingerprintManager, mBiometricManager,
                     mFaceWakeUpTriggersConfig, mDevicePostureController,
                     Optional.of(mInteractiveToAuthProvider),
-                    mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
+                    mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager,
+                    () -> mAlternateBouncerInteractor,
+                    () -> mJavaAdapter,
+                    () -> mSceneInteractor);
+            setAlternateBouncerVisibility(false);
+            setPrimaryBouncerVisibility(false);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
             start();
         }
@@ -2458,5 +2483,25 @@
         protected int getBiometricLockoutDelay() {
             return 0;
         }
+
+        private void setPrimaryBouncerVisibility(boolean isVisible) {
+            if (SceneContainerFlag.isEnabled()) {
+                ObservableTransitionState transitionState = new ObservableTransitionState.Idle(
+                        isVisible ? Scenes.Bouncer : Scenes.Lockscreen);
+                when(mSceneInteractor.getTransitionState()).thenReturn(
+                        MutableStateFlow(transitionState));
+                onTransitionStateChanged(transitionState);
+            } else {
+                sendPrimaryBouncerChanged(isVisible, isVisible);
+                mTestableLooper.processAllMessages();
+            }
+        }
+
+        private void setAlternateBouncerVisibility(boolean isVisible) {
+            if (SceneContainerFlag.isEnabled()) {
+                when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(isVisible);
+            }
+            onAlternateBouncerVisibilityChange(isVisible);
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 997f8a8..344d065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -80,6 +80,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository;
@@ -109,6 +111,8 @@
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 
+import kotlin.Lazy;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -127,6 +131,7 @@
 
     private ScreenDecorations mScreenDecorations;
     private WindowManager mWindowManager;
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private DisplayManager mDisplayManager;
     private SecureSettings mSecureSettings;
     private FakeExecutor mExecutor;
@@ -173,6 +178,8 @@
     private CutoutDecorProviderFactory mCutoutFactory;
     @Mock
     private JavaAdapter mJavaAdapter;
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
 
     private FakeFacePropertyRepository mFakeFacePropertyRepository =
             new FakeFacePropertyRepository();
@@ -245,12 +252,15 @@
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
                 mFakeFacePropertyRepository));
 
+        mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
+                mLazyViewCapture, false);
         mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
                 mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
                 mThreadFactory,
                 mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader) {
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
+                mViewCaptureAwareWindowManager) {
             @Override
             public void start() {
                 super.start();
@@ -1264,7 +1274,8 @@
                 mDotViewController,
                 mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
                 new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
-                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader);
+                mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
+                mViewCaptureAwareWindowManager);
         screenDecorations.start();
         when(mContext.getDisplay()).thenReturn(mDisplay);
         when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
new file mode 100644
index 0000000..ba990ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/AccessibilityGestureTargetsObserverTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.UserTracker;
+
+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;
+
+/** Test for {@link AccessibilityGestureTargetsObserver}. */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class AccessibilityGestureTargetsObserverTest extends SysuiTestCase {
+    private static final int MY_USER_ID = ActivityManager.getCurrentUser();
+
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private UserTracker mUserTracker;
+    @Mock
+    private AccessibilityGestureTargetsObserver.TargetsChangedListener mListener;
+
+    private AccessibilityGestureTargetsObserver mAccessibilityGestureTargetsObserver;
+
+    private static final String TEST_A11Y_BTN_TARGETS = "Magnification";
+
+    @Before
+    public void setUp() {
+        when(mUserTracker.getUserId()).thenReturn(MY_USER_ID);
+        mAccessibilityGestureTargetsObserver = new AccessibilityGestureTargetsObserver(mContext,
+                mUserTracker);
+    }
+
+    @Test
+    public void onChange_haveListener_invokeCallback() {
+        mAccessibilityGestureTargetsObserver.addListener(mListener);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+                MY_USER_ID);
+
+        mAccessibilityGestureTargetsObserver.mContentObserver.onChange(false);
+
+        verify(mListener).onAccessibilityGestureTargetsChanged(TEST_A11Y_BTN_TARGETS);
+    }
+
+    @Test
+    public void onChange_listenerRemoved_noInvokeCallback() {
+        mAccessibilityGestureTargetsObserver.addListener(mListener);
+        mAccessibilityGestureTargetsObserver.removeListener(mListener);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+                MY_USER_ID);
+
+        mAccessibilityGestureTargetsObserver.mContentObserver.onChange(false);
+
+        verify(mListener, never()).onAccessibilityGestureTargetsChanged(anyString());
+    }
+
+    @Test
+    public void getCurrentAccessibilityGestureTargets_expectedValue() {
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS, TEST_A11Y_BTN_TARGETS,
+                MY_USER_ID);
+
+        final String actualValue =
+                mAccessibilityGestureTargetsObserver.getCurrentAccessibilityGestureTargets();
+
+        assertThat(actualValue).isEqualTo(TEST_A11Y_BTN_TARGETS);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
deleted file mode 100644
index ff47fd1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ /dev/null
@@ -1,358 +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.accessibility;
-
-import static com.android.systemui.accessibility.MagnificationImpl.DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-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.provider.Settings;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.IWindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.IMagnificationConnection;
-import android.view.accessibility.IMagnificationConnectionCallback;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.util.settings.SecureSettings;
-
-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;
-
-/**
- * Tests for {@link android.view.accessibility.IMagnificationConnection} retrieved from
- * {@link MagnificationImpl}
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
-public class IMagnificationConnectionTest extends SysuiTestCase {
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
-    @Mock
-    private AccessibilityManager mAccessibilityManager;
-    @Mock
-    private CommandQueue mCommandQueue;
-    @Mock
-    private IMagnificationConnectionCallback mConnectionCallback;
-    @Mock
-    private WindowMagnificationController mWindowMagnificationController;
-    @Mock
-    private FullscreenMagnificationController mFullscreenMagnificationController;
-    @Mock
-    private MagnificationSettingsController mMagnificationSettingsController;
-    @Mock
-    private ModeSwitchesController mModeSwitchesController;
-    @Mock
-    private SysUiState mSysUiState;
-    @Mock
-    private IRemoteMagnificationAnimationCallback mAnimationCallback;
-    @Mock
-    private OverviewProxyService mOverviewProxyService;
-    @Mock
-    private SecureSettings mSecureSettings;
-    @Mock
-    private AccessibilityLogger mA11yLogger;
-    @Mock
-    private IWindowManager mIWindowManager;
-    @Mock
-    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
-
-    private IMagnificationConnection mIMagnificationConnection;
-    private MagnificationImpl mMagnification;
-    private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-    private TestableLooper mTestableLooper;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        getContext().addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
-        doAnswer(invocation -> {
-            mIMagnificationConnection = invocation.getArgument(0);
-            return null;
-        }).when(mAccessibilityManager).setMagnificationConnection(
-                any(IMagnificationConnection.class));
-        mTestableLooper = TestableLooper.get(this);
-        assertNotNull(mTestableLooper);
-        mMagnification = new MagnificationImpl(getContext(),
-                mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
-                mModeSwitchesController, mSysUiState, mOverviewProxyService, mSecureSettings,
-                mDisplayTracker, getContext().getSystemService(DisplayManager.class),
-                mA11yLogger, mIWindowManager, mAccessibilityManager,
-                mViewCaptureAwareWindowManager);
-        mMagnification.mWindowMagnificationControllerSupplier =
-                new FakeWindowMagnificationControllerSupplier(
-                        mContext.getSystemService(DisplayManager.class));
-        mMagnification.mFullscreenMagnificationControllerSupplier =
-                new FakeFullscreenMagnificationControllerSupplier(
-                        mContext.getSystemService(DisplayManager.class));
-        mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
-                mContext.getSystemService(DisplayManager.class));
-
-        mMagnification.requestMagnificationConnection(true);
-        assertNotNull(mIMagnificationConnection);
-        mIMagnificationConnection.setConnectionCallback(mConnectionCallback);
-    }
-
-    @Test
-    public void enableWindowMagnification_passThrough() throws RemoteException {
-        mIMagnificationConnection.enableWindowMagnification(TEST_DISPLAY, 3.0f, Float.NaN,
-                Float.NaN, 0f, 0f, mAnimationCallback);
-        processAllPendingMessages();
-
-        verify(mWindowMagnificationController).enableWindowMagnification(eq(3.0f),
-                eq(Float.NaN), eq(Float.NaN), eq(0f), eq(0f), eq(mAnimationCallback));
-    }
-
-    @Test
-    public void onFullscreenMagnificationActivationChanged_passThrough() throws RemoteException {
-        mIMagnificationConnection.onFullscreenMagnificationActivationChanged(TEST_DISPLAY, true);
-        processAllPendingMessages();
-
-        verify(mFullscreenMagnificationController)
-                .onFullscreenMagnificationActivationChanged(eq(true));
-    }
-
-    @Test
-    public void disableWindowMagnification_deleteWindowMagnification() throws RemoteException {
-        mIMagnificationConnection.disableWindowMagnification(TEST_DISPLAY,
-                mAnimationCallback);
-        processAllPendingMessages();
-
-        verify(mWindowMagnificationController).deleteWindowMagnification(
-                mAnimationCallback);
-    }
-
-    @Test
-    public void setScaleForWindowMagnification() throws RemoteException {
-        mIMagnificationConnection.setScaleForWindowMagnification(TEST_DISPLAY, 3.0f);
-        processAllPendingMessages();
-
-        verify(mWindowMagnificationController).setScale(3.0f);
-    }
-
-    @Test
-    public void moveWindowMagnifier() throws RemoteException {
-        mIMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
-        processAllPendingMessages();
-
-        verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
-    }
-
-    @Test
-    public void moveWindowMagnifierToPosition() throws RemoteException {
-        mIMagnificationConnection.moveWindowMagnifierToPosition(TEST_DISPLAY,
-                100f, 200f, mAnimationCallback);
-        processAllPendingMessages();
-
-        verify(mWindowMagnificationController).moveWindowMagnifierToPosition(
-                eq(100f), eq(200f), any(IRemoteMagnificationAnimationCallback.class));
-    }
-
-    @Test
-    @RequiresFlagsDisabled(Flags.FLAG_DELAY_SHOW_MAGNIFICATION_BUTTON)
-    public void showMagnificationButton_flagOff_directlyShowButton() throws RemoteException {
-        // magnification settings panel should not be showing
-        assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
-
-        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-        processAllPendingMessages();
-
-        verify(mModeSwitchesController).showButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_DELAY_SHOW_MAGNIFICATION_BUTTON)
-    public void showMagnificationButton_flagOn_delayedShowButton() throws RemoteException {
-        // magnification settings panel should not be showing
-        assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
-
-        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
-        // showMagnificationButton request to Magnification.
-        processAllPendingMessages();
-
-        // The delayed message would be processed after DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS.
-        // So call this processAllPendingMessages with a timeout to verify the showButton
-        // will be called.
-        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
-        processAllPendingMessages(timeout);
-        verify(mModeSwitchesController).showButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void showMagnificationButton_settingsPanelShowing_doNotShowButton()
-            throws RemoteException {
-        when(mMagnificationSettingsController.isMagnificationSettingsShowing()).thenReturn(true);
-
-        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
-        // showMagnificationButton request to Magnification.
-        processAllPendingMessages();
-
-        // If the flag is on, the isMagnificationSettingsShowing will be checked after timeout, so
-        // process all message after a timeout here to verify the showButton will not be called.
-        int timeout = Flags.delayShowMagnificationButton()
-                ? DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100
-                : 0;
-        processAllPendingMessages(timeout);
-        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void removeMagnificationButton() throws RemoteException {
-        mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
-        processAllPendingMessages();
-
-        verify(mModeSwitchesController).removeButton(TEST_DISPLAY);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_DELAY_SHOW_MAGNIFICATION_BUTTON)
-    public void removeMagnificationButton_delayingShowButton_doNotShowButtonAfterTimeout()
-            throws RemoteException {
-        // magnification settings panel should not be showing
-        assertFalse(mMagnification.isMagnificationSettingsPanelShowing(TEST_DISPLAY));
-
-        mIMagnificationConnection.showMagnificationButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-        mIMagnificationConnection.removeMagnificationButton(TEST_DISPLAY);
-        // This processAllPendingMessages lets the IMagnificationConnection to delegate the
-        // requests to Magnification.
-        processAllPendingMessages();
-
-        // Call this processAllPendingMessages with a timeout to ensure the delayed show button
-        // message should be removed and thus the showButton will not be called after timeout.
-        int timeout = DELAY_SHOW_MAGNIFICATION_TIMEOUT_MS + 100;
-        processAllPendingMessages(/* timeForwardMs= */ timeout);
-        verify(mModeSwitchesController, never()).showButton(TEST_DISPLAY,
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-    }
-
-    @Test
-    public void removeMagnificationSettingsPanel() throws RemoteException {
-        mIMagnificationConnection.removeMagnificationSettingsPanel(TEST_DISPLAY);
-        processAllPendingMessages();
-
-        verify(mMagnificationSettingsController).closeMagnificationSettings();
-    }
-
-    @Test
-    public void onUserMagnificationScaleChanged() throws RemoteException {
-        final int testUserId = 1;
-        final float testScale = 3.0f;
-        mIMagnificationConnection.onUserMagnificationScaleChanged(
-                testUserId, TEST_DISPLAY, testScale);
-        processAllPendingMessages();
-
-        assertTrue(mMagnification.mUsersScales.contains(testUserId));
-        assertEquals(mMagnification.mUsersScales.get(testUserId).get(TEST_DISPLAY),
-                (Float) testScale);
-        verify(mMagnificationSettingsController).setMagnificationScale(eq(testScale));
-    }
-
-    private void processAllPendingMessages() {
-        processAllPendingMessages(/* timeForwardMs=*/ 0);
-    }
-
-    private void processAllPendingMessages(int timeForwardMs) {
-        if (timeForwardMs > 0) {
-            mTestableLooper.moveTimeForward(timeForwardMs);
-        }
-        mTestableLooper.processAllMessages();
-    }
-
-    private class FakeWindowMagnificationControllerSupplier extends
-            DisplayIdIndexSupplier<WindowMagnificationController> {
-
-        FakeWindowMagnificationControllerSupplier(DisplayManager displayManager) {
-            super(displayManager);
-        }
-
-        @Override
-        protected WindowMagnificationController createInstance(Display display) {
-            return mWindowMagnificationController;
-        }
-    }
-
-    private class FakeFullscreenMagnificationControllerSupplier extends
-            DisplayIdIndexSupplier<FullscreenMagnificationController> {
-
-        FakeFullscreenMagnificationControllerSupplier(DisplayManager displayManager) {
-            super(displayManager);
-        }
-
-        @Override
-        protected FullscreenMagnificationController createInstance(Display display) {
-            return mFullscreenMagnificationController;
-        }
-    }
-
-    private class FakeSettingsSupplier extends
-            DisplayIdIndexSupplier<MagnificationSettingsController> {
-
-        FakeSettingsSupplier(DisplayManager displayManager) {
-            super(displayManager);
-        }
-
-        @Override
-        protected MagnificationSettingsController createInstance(Display display) {
-            return mMagnificationSettingsController;
-        }
-    }
-}
-
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 44207a0..c6e4e0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -53,6 +53,7 @@
 
 import androidx.test.filters.LargeTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
@@ -112,6 +113,8 @@
     SysUiState mSysUiState;
     @Mock
     SecureSettings mSecureSettings;
+    @Mock
+    ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private SpyWindowMagnificationController mController;
     private WindowMagnificationController mSpyController;
     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -164,7 +167,8 @@
                 mSysUiState,
                 mSecureSettings,
                 scvhSupplier,
-                mSfVsyncFrameProvider);
+                mSfVsyncFrameProvider,
+                mViewCaptureAwareWindowManager);
 
         mSpyController = mController.getSpyController();
     }
@@ -1015,7 +1019,8 @@
                 SysUiState sysUiState,
                 SecureSettings secureSettings,
                 Supplier<SurfaceControlViewHost> scvhSupplier,
-                SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
+                SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+                ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
             super(
                     context,
                     handler,
@@ -1027,7 +1032,8 @@
                     secureSettings,
                     scvhSupplier,
                     sfVsyncFrameProvider,
-                    WindowManagerGlobal::getWindowSession);
+                    WindowManagerGlobal::getWindowSession,
+                    viewCaptureAwareWindowManager);
             mSpyController = Mockito.mock(WindowMagnificationController.class);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
deleted file mode 100644
index f57003e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ /dev/null
@@ -1,1514 +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.accessibility;
-
-import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.view.Choreographer.FrameCallback;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowInsets.Type.systemGestures;
-import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
-
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.hasItems;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalAnswers.returnsSecondArg;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.animation.ValueAnimator;
-import android.annotation.IdRes;
-import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Insets;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.RegionIterator;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.provider.Settings;
-import android.testing.TestableLooper;
-import android.testing.TestableResources;
-import android.text.TextUtils;
-import android.util.Size;
-import android.view.Display;
-import android.view.IWindowSession;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.IRemoteMagnificationAnimationCallback;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-
-import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.AnimatorTestRule;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.util.FakeSharedPreferences;
-import com.android.systemui.util.leak.ReferenceTestUtils;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.utils.os.FakeHandler;
-
-import com.google.common.util.concurrent.AtomicDouble;
-
-import org.junit.After;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@LargeTest
[email protected]
-@RunWith(AndroidJUnit4.class)
-@RequiresFlagsDisabled(Flags.FLAG_CREATE_WINDOWLESS_WINDOW_MAGNIFIER)
-public class WindowMagnificationControllerTest extends SysuiTestCase {
-
-    @Rule
-    // NOTE: pass 'null' to allow this test advances time on the main thread.
-    public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(null);
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
-    @Mock
-    private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
-    @Mock
-    private MirrorWindowControl mMirrorWindowControl;
-    @Mock
-    private WindowMagnifierCallback mWindowMagnifierCallback;
-    @Mock
-    IRemoteMagnificationAnimationCallback mAnimationCallback;
-    @Mock
-    IRemoteMagnificationAnimationCallback mAnimationCallback2;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
-    @Mock
-    private SecureSettings mSecureSettings;
-
-    private long mWaitAnimationDuration;
-    private long mWaitBounceEffectDuration;
-
-    private Handler mHandler;
-    private TestableWindowManager mWindowManager;
-    private SysUiState mSysUiState;
-    private Resources mResources;
-    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
-    private WindowMagnificationController mWindowMagnificationController;
-    private Instrumentation mInstrumentation;
-    private final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1.0f).setDuration(0);
-    private final FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
-    private IWindowSession mWindowSessionSpy;
-
-    private View mSpyView;
-    private View.OnTouchListener mTouchListener;
-    private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
-    private KosmosJavaAdapter mKosmos;
-    private FakeSharedPreferences mSharedPreferences;
-
-    /**
-     *  return whether window magnification is supported for current test context.
-     */
-    private boolean isWindowModeSupported() {
-        return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(mContext);
-        mKosmos = new KosmosJavaAdapter(this);
-        mContext = Mockito.spy(getContext());
-        mHandler = new FakeHandler(TestableLooper.get(this).getLooper());
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        final WindowManager wm = mContext.getSystemService(WindowManager.class);
-        mWindowManager = spy(new TestableWindowManager(wm));
-
-        mWindowSessionSpy = spy(WindowManagerGlobal.getWindowSession());
-
-        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
-        doAnswer(invocation -> {
-            FrameCallback callback = invocation.getArgument(0);
-            callback.doFrame(0);
-            return null;
-        }).when(mSfVsyncFrameProvider).postFrameCallback(
-                any(FrameCallback.class));
-        mSysUiState = new SysUiState(mDisplayTracker, mKosmos.getSceneContainerPlugin());
-        mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
-        when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
-                returnsSecondArg());
-        when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
-                returnsSecondArg());
-
-        mResources = getContext().getOrCreateTestableResources().getResources();
-        // prevent the config orientation from undefined, which may cause config.diff method
-        // neglecting the orientation update.
-        if (mResources.getConfiguration().orientation == ORIENTATION_UNDEFINED) {
-            mResources.getConfiguration().orientation = ORIENTATION_PORTRAIT;
-        }
-
-        // Using the animation duration in WindowMagnificationAnimationController for testing.
-        mWaitAnimationDuration = mResources.getInteger(
-                com.android.internal.R.integer.config_longAnimTime);
-        // Using the bounce effect duration in WindowMagnificationController for testing.
-        mWaitBounceEffectDuration = mResources.getInteger(
-                com.android.internal.R.integer.config_shortAnimTime);
-
-        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
-                mContext, mValueAnimator);
-        mSharedPreferences = new FakeSharedPreferences();
-        when(mContext.getSharedPreferences(
-                eq("window_magnification_preferences"), anyInt()))
-                .thenReturn(mSharedPreferences);
-        mWindowMagnificationController =
-                new WindowMagnificationController(
-                        mContext,
-                        mHandler,
-                        mWindowMagnificationAnimationController,
-                        mMirrorWindowControl,
-                        mTransaction,
-                        mWindowMagnifierCallback,
-                        mSysUiState,
-                        mSecureSettings,
-                        /* scvhSupplier= */ () -> null,
-                        mSfVsyncFrameProvider,
-                        /* globalWindowSessionSupplier= */ () -> mWindowSessionSpy);
-
-        verify(mMirrorWindowControl).setWindowDelegate(
-                any(MirrorWindowControl.MirrorWindowDelegate.class));
-        mSpyView = Mockito.spy(new View(mContext));
-        doAnswer((invocation) -> {
-            mTouchListener = invocation.getArgument(0);
-            return null;
-        }).when(mSpyView).setOnTouchListener(
-                any(View.OnTouchListener.class));
-
-        // skip test if window magnification is not supported to prevent fail results. (b/279820875)
-        Assume.assumeTrue(isWindowModeSupported());
-    }
-
-    @After
-    public void tearDown() {
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.deleteWindowMagnification();
-                });
-        mValueAnimator.cancel();
-    }
-
-    @Test
-    public void initWindowMagnificationController_checkAllowDiagonalScrollingWithSecureSettings() {
-        verify(mSecureSettings).getIntForUser(
-                eq(Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING),
-                /* def */ eq(1), /* userHandle= */ anyInt());
-        assertTrue(mWindowMagnificationController.isDiagonalScrollingEnabled());
-    }
-
-    @Test
-    public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        verify(mMirrorWindowControl).showControl();
-        verify(mWindowMagnifierCallback,
-                timeout(LAYOUT_CHANGE_TIMEOUT_MS).atLeastOnce()).onWindowMagnifierBoundsChanged(
-                eq(mContext.getDisplayId()), any(Rect.class));
-    }
-
-    @Test
-    public void enableWindowMagnification_notifySourceBoundsChanged() {
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                        Float.NaN, /* magnificationFrameOffsetRatioX= */ 0,
-                        /* magnificationFrameOffsetRatioY= */ 0, null));
-
-        // Waits for the surface created
-        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onSourceBoundsChanged(
-                (eq(mContext.getDisplayId())), any());
-    }
-
-    @Test
-    public void enableWindowMagnification_disabled_notifySourceBoundsChanged() {
-        enableWindowMagnification_notifySourceBoundsChanged();
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.deleteWindowMagnification(null));
-        Mockito.reset(mWindowMagnifierCallback);
-
-        enableWindowMagnification_notifySourceBoundsChanged();
-    }
-
-    @Test
-    public void enableWindowMagnification_withAnimation_schedulesFrame() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(2.0f, 10,
-                    10, /* magnificationFrameOffsetRatioX= */ 0,
-                    /* magnificationFrameOffsetRatioY= */ 0,
-                    Mockito.mock(IRemoteMagnificationAnimationCallback.class));
-        });
-        advanceTimeBy(LAYOUT_CHANGE_TIMEOUT_MS);
-
-        verify(mSfVsyncFrameProvider, atLeast(2)).postFrameCallback(any());
-    }
-
-    @Test
-    public void moveWindowMagnifier_enabled_notifySourceBoundsChanged() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                    Float.NaN, 0, 0, null);
-        });
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.moveWindowMagnifier(10, 10);
-        });
-
-        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
-        verify(mWindowMagnifierCallback, atLeast(2)).onSourceBoundsChanged(
-                (eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
-    }
-
-    @Test
-    public void enableWindowMagnification_systemGestureExclusionRectsIsSet() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-        // Wait for Rects updated.
-        waitForIdleSync();
-
-        List<Rect> rects = mWindowManager.getAttachedView().getSystemGestureExclusionRects();
-        assertFalse(rects.isEmpty());
-    }
-
-    @Ignore("The default window size should be constrained after fixing b/288056772")
-    @Test
-    public void enableWindowMagnification_LargeScreen_windowSizeIsConstrained() {
-        final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
-        mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        final int halfScreenSize = screenSize / 2;
-        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
-        // The frame size should be the half of smaller value of window height/width unless it
-        //exceed the max frame size.
-        assertTrue(params.width < halfScreenSize);
-        assertTrue(params.height < halfScreenSize);
-    }
-
-    @Test
-    public void deleteWindowMagnification_destroyControlAndUnregisterComponentCallback() {
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN,
-                        Float.NaN));
-
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.deleteWindowMagnification());
-
-        verify(mMirrorWindowControl).destroyControl();
-        verify(mContext).unregisterComponentCallbacks(mWindowMagnificationController);
-    }
-
-    @Test
-    public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
-        final WindowManager wm = mContext.getSystemService(WindowManager.class);
-        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
-        setSystemGestureInsets();
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    bounds.bottom);
-        });
-        ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.deleteWindowMagnification();
-        });
-
-        verify(mMirrorWindowControl).destroyControl();
-        assertFalse(hasMagnificationOverlapFlag());
-    }
-
-    @Test
-    public void deleteWindowMagnification_notifySourceBoundsChanged() {
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN,
-                        Float.NaN));
-
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.deleteWindowMagnification());
-
-        // The first time is for notifying magnification enabled and the second time is for
-        // notifying magnification disabled.
-        verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged(
-                (eq(mContext.getDisplayId())), any());
-    }
-
-    @Test
-    public void moveMagnifier_schedulesFrame() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.moveWindowMagnifier(100f, 100f);
-        });
-
-        verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
-    }
-
-    @Test
-    public void moveWindowMagnifierToPositionWithAnimation_expectedValuesAndInvokeCallback()
-            throws RemoteException {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                    Float.NaN, 0, 0, null);
-        });
-
-        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
-        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
-                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        final float targetCenterX = sourceBoundsCaptor.getValue().exactCenterX() + 10;
-        final float targetCenterY = sourceBoundsCaptor.getValue().exactCenterY() + 10;
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    targetCenterX, targetCenterY, mAnimationCallback);
-        });
-        advanceTimeBy(mWaitAnimationDuration);
-
-        verify(mAnimationCallback, times(1)).onResult(eq(true));
-        verify(mAnimationCallback, never()).onResult(eq(false));
-        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
-                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
-        assertEquals(mWindowMagnificationController.getCenterX(), targetCenterX, 0);
-        assertEquals(mWindowMagnificationController.getCenterY(), targetCenterY, 0);
-    }
-
-    @Test
-    public void moveWindowMagnifierToPositionMultipleTimes_expectedValuesAndInvokeCallback()
-            throws RemoteException {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                    Float.NaN, 0, 0, null);
-        });
-
-        final ArgumentCaptor<Rect> sourceBoundsCaptor = ArgumentCaptor.forClass(Rect.class);
-        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
-                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        final float centerX = sourceBoundsCaptor.getValue().exactCenterX();
-        final float centerY = sourceBoundsCaptor.getValue().exactCenterY();
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 10, centerY + 10, mAnimationCallback);
-            mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 20, centerY + 20, mAnimationCallback);
-            mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 30, centerY + 30, mAnimationCallback);
-            mWindowMagnificationController.moveWindowMagnifierToPosition(
-                    centerX + 40, centerY + 40, mAnimationCallback2);
-        });
-        advanceTimeBy(mWaitAnimationDuration);
-
-        // only the last one callback will return true
-        verify(mAnimationCallback2).onResult(eq(true));
-        // the others will return false
-        verify(mAnimationCallback, times(3)).onResult(eq(false));
-        verify(mWindowMagnifierCallback, timeout(LAYOUT_CHANGE_TIMEOUT_MS))
-                .onSourceBoundsChanged((eq(mContext.getDisplayId())), sourceBoundsCaptor.capture());
-        assertEquals(mWindowMagnificationController.getCenterX(),
-                sourceBoundsCaptor.getValue().exactCenterX(), 0);
-        assertEquals(mWindowMagnificationController.getCenterY(),
-                sourceBoundsCaptor.getValue().exactCenterY(), 0);
-        assertEquals(mWindowMagnificationController.getCenterX(), centerX + 40, 0);
-        assertEquals(mWindowMagnificationController.getCenterY(), centerY + 40, 0);
-    }
-
-    @Test
-    public void setScale_enabled_expectedValueAndUpdateStateDescription() {
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(2.0f,
-                        Float.NaN, Float.NaN));
-
-        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
-
-        assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
-        final View mirrorView = mWindowManager.getAttachedView();
-        assertNotNull(mirrorView);
-        assertThat(mirrorView.getStateDescription().toString(), containsString("300"));
-    }
-
-    @Test
-    public void onConfigurationChanged_disabled_withoutException() {
-        Display display = Mockito.spy(mContext.getDisplay());
-        when(display.getRotation()).thenReturn(Surface.ROTATION_90);
-        when(mContext.getDisplay()).thenReturn(display);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_ORIENTATION);
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
-        });
-    }
-
-    @Test
-    public void onOrientationChanged_enabled_updateDisplayRotationAndCenterStayAtSamePosition() {
-        final int newRotation = simulateRotateTheDevice();
-        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
-        final float center = Math.min(windowBounds.exactCenterX(), windowBounds.exactCenterY());
-        final float displayWidth = windowBounds.width();
-        final PointF magnifiedCenter = new PointF(center, center + 5f);
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                    magnifiedCenter.x, magnifiedCenter.y);
-            // Get the center again in case the center we set is out of screen.
-            magnifiedCenter.set(mWindowMagnificationController.getCenterX(),
-                    mWindowMagnificationController.getCenterY());
-        });
-        // Rotate the window clockwise 90 degree.
-        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
-                windowBounds.right);
-        mWindowManager.setWindowBounds(windowBounds);
-
-        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
-                ActivityInfo.CONFIG_ORIENTATION));
-
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
-        final PointF expectedCenter = new PointF(magnifiedCenter.y,
-                displayWidth - magnifiedCenter.x);
-        final PointF actualCenter = new PointF(mWindowMagnificationController.getCenterX(),
-                mWindowMagnificationController.getCenterY());
-        assertEquals(expectedCenter, actualCenter);
-    }
-
-    @Test
-    public void onOrientationChanged_disabled_updateDisplayRotation() {
-        final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
-        // Rotate the window clockwise 90 degree.
-        windowBounds.set(windowBounds.top, windowBounds.left, windowBounds.bottom,
-                windowBounds.right);
-        mWindowManager.setWindowBounds(windowBounds);
-        final int newRotation = simulateRotateTheDevice();
-
-        mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.onConfigurationChanged(
-                ActivityInfo.CONFIG_ORIENTATION));
-
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
-    }
-
-    @Test
-    public void onScreenSizeAndDensityChanged_enabledAtTheCenterOfScreen_keepSameWindowSizeRatio() {
-        // The default position is at the center of the screen.
-        final float expectedRatio = 0.5f;
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        // Screen size and density change
-        mContext.getResources().getConfiguration().smallestScreenWidthDp =
-                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
-        final Rect testWindowBounds = new Rect(
-                mWindowManager.getCurrentWindowMetrics().getBounds());
-        testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
-                testWindowBounds.right + 100, testWindowBounds.bottom + 100);
-        mWindowManager.setWindowBounds(testWindowBounds);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
-        });
-
-        // The ratio of center to window size should be the same.
-        assertEquals(expectedRatio,
-                mWindowMagnificationController.getCenterX() / testWindowBounds.width(),
-                0);
-        assertEquals(expectedRatio,
-                mWindowMagnificationController.getCenterY() / testWindowBounds.height(),
-                0);
-    }
-
-    @Test
-    public void onScreenSizeAndDensityChanged_enabled_restoreSavedMagnifierWindow() {
-        int newSmallestScreenWidthDp =
-                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
-        int windowFrameSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        Size preferredWindowSize = new Size(windowFrameSize, windowFrameSize);
-        mSharedPreferences
-                .edit()
-                .putString(String.valueOf(newSmallestScreenWidthDp),
-                        preferredWindowSize.toString())
-                .commit();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        // Change screen density and size to trigger restoring the preferred window size
-        mContext.getResources().getConfiguration().smallestScreenWidthDp = newSmallestScreenWidthDp;
-        final Rect testWindowBounds = new Rect(
-                mWindowManager.getCurrentWindowMetrics().getBounds());
-        testWindowBounds.set(testWindowBounds.left, testWindowBounds.top,
-                testWindowBounds.right + 100, testWindowBounds.bottom + 100);
-        mWindowManager.setWindowBounds(testWindowBounds);
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
-        });
-
-        // wait for rect update
-        waitForIdleSync();
-        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
-        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-        // The width and height of the view include the magnification frame and the margins.
-        assertTrue(params.width == (windowFrameSize + 2 * mirrorSurfaceMargin));
-        assertTrue(params.height == (windowFrameSize + 2 * mirrorSurfaceMargin));
-    }
-
-    @Test
-    public void screenSizeIsChangedToLarge_enabled_defaultWindowSize() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-        final int screenSize = mWindowManager.getCurrentWindowMetrics().getBounds().width() * 10;
-        // Screen size and density change
-        mContext.getResources().getConfiguration().smallestScreenWidthDp =
-                mContext.getResources().getConfiguration().smallestScreenWidthDp * 2;
-        mWindowManager.setWindowBounds(new Rect(0, 0, screenSize, screenSize));
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE);
-        });
-
-        final int defaultWindowSize =
-                mWindowMagnificationController.getMagnificationWindowSizeFromIndex(
-                        WindowMagnificationSettings.MagnificationSize.MEDIUM);
-        WindowManager.LayoutParams params = mWindowManager.getLayoutParamsFromAttachedView();
-
-        assertTrue(params.width == defaultWindowSize);
-        assertTrue(params.height == defaultWindowSize);
-    }
-
-    @Test
-    public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            Mockito.reset(mWindowManager);
-            Mockito.reset(mMirrorWindowControl);
-        });
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
-        });
-
-        verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
-        verify(mWindowManager).removeView(any());
-        verify(mMirrorWindowControl).destroyControl();
-        verify(mWindowManager).addView(any(), any());
-        verify(mMirrorWindowControl).showControl();
-    }
-
-    @Test
-    public void onDensityChanged_disabled_updateDimensions() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
-        });
-
-        verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
-    }
-
-    @Test
-    public void initializeA11yNode_enabled_expectedValues() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
-                    Float.NaN);
-        });
-        final View mirrorView = mWindowManager.getAttachedView();
-        assertNotNull(mirrorView);
-        final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
-
-        mirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
-
-        assertNotNull(nodeInfo.getContentDescription());
-        assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
-        assertThat(nodeInfo.getActionList(),
-                hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
-                        new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_right, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_left, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_down, null),
-                        new AccessibilityAction(R.id.accessibility_action_move_up, null)));
-    }
-
-    @Test
-    public void performA11yActions_visible_expectedResults() {
-        final int displayId = mContext.getDisplayId();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(1.5f, Float.NaN,
-                    Float.NaN);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
-        // Minimum scale is 1.0.
-        verify(mWindowMagnifierCallback).onPerformScaleAction(
-                eq(displayId), /* scale= */ eq(1.0f), /* updatePersistence= */ eq(true));
-
-        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
-        verify(mWindowMagnifierCallback).onPerformScaleAction(
-                eq(displayId), /* scale= */ eq(2.5f), /* updatePersistence= */ eq(true));
-
-        // TODO: Verify the final state when the mirror surface is visible.
-        assertTrue(mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
-        assertTrue(
-                mirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
-        verify(mWindowMagnifierCallback, times(4)).onMove(eq(displayId));
-
-        assertTrue(mirrorView.performAccessibilityAction(
-                AccessibilityAction.ACTION_CLICK.getId(), null));
-        verify(mWindowMagnifierCallback).onClickSettingsButton(eq(displayId));
-    }
-
-    @Test
-    public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
-        final int displayId = mContext.getDisplayId();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(2.5f, Float.NaN,
-                    Float.NaN);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        mirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
-
-        verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
-    }
-
-    @Test
-    public void windowMagnifierEditMode_performA11yClickAction_exitEditMode() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        View closeButton = getInternalView(R.id.close_button);
-        View bottomRightCorner = getInternalView(R.id.bottom_right_corner);
-        View bottomLeftCorner = getInternalView(R.id.bottom_left_corner);
-        View topRightCorner = getInternalView(R.id.top_right_corner);
-        View topLeftCorner = getInternalView(R.id.top_left_corner);
-
-        assertEquals(View.VISIBLE, closeButton.getVisibility());
-        assertEquals(View.VISIBLE, bottomRightCorner.getVisibility());
-        assertEquals(View.VISIBLE, bottomLeftCorner.getVisibility());
-        assertEquals(View.VISIBLE, topRightCorner.getVisibility());
-        assertEquals(View.VISIBLE, topLeftCorner.getVisibility());
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        mirrorView.performAccessibilityAction(AccessibilityAction.ACTION_CLICK.getId(), null);
-
-        assertEquals(View.GONE, closeButton.getVisibility());
-        assertEquals(View.GONE, bottomRightCorner.getVisibility());
-        assertEquals(View.GONE, bottomLeftCorner.getVisibility());
-        assertEquals(View.GONE, topRightCorner.getVisibility());
-        assertEquals(View.GONE, topLeftCorner.getVisibility());
-    }
-
-    @Test
-    public void windowWidthIsNotMax_performA11yActionIncreaseWidth_windowWidthIncreased() {
-        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        final int startingWidth = (int) (windowBounds.width() * 0.8);
-        final int startingHeight = (int) (windowBounds.height() * 0.8);
-        final float changeWindowSizeAmount = mContext.getResources().getFraction(
-                R.fraction.magnification_resize_window_size_amount,
-                /* base= */ 1,
-                /* pbase= */ 1);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mirrorView.performAccessibilityAction(
-                            R.id.accessibility_action_increase_window_width, null);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-        // Window width includes the magnifier frame and the margin. Increasing the window size
-        // will be increasing the amount of the frame size only.
-        int newWindowWidth =
-                (int) ((startingWidth - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
-                        + 2 * mirrorSurfaceMargin;
-        assertEquals(newWindowWidth, actualWindowWidth.get());
-        assertEquals(startingHeight, actualWindowHeight.get());
-    }
-
-    @Test
-    public void windowHeightIsNotMax_performA11yActionIncreaseHeight_windowHeightIncreased() {
-        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        final int startingWidth = (int) (windowBounds.width() * 0.8);
-        final int startingHeight = (int) (windowBounds.height() * 0.8);
-        final float changeWindowSizeAmount = mContext.getResources().getFraction(
-                R.fraction.magnification_resize_window_size_amount,
-                /* base= */ 1,
-                /* pbase= */ 1);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mirrorView.performAccessibilityAction(
-                            R.id.accessibility_action_increase_window_height, null);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-        // Window height includes the magnifier frame and the margin. Increasing the window size
-        // will be increasing the amount of the frame size only.
-        int newWindowHeight =
-                (int) ((startingHeight - 2 * mirrorSurfaceMargin) * (1 + changeWindowSizeAmount))
-                        + 2 * mirrorSurfaceMargin;
-        assertEquals(startingWidth, actualWindowWidth.get());
-        assertEquals(newWindowHeight, actualWindowHeight.get());
-    }
-
-    @Test
-    public void windowWidthIsMax_noIncreaseWindowWidthA11yAction() {
-        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        final int startingWidth = windowBounds.width();
-        final int startingHeight = windowBounds.height();
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AccessibilityNodeInfo accessibilityNodeInfo =
-                mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_increase_window_width, null)));
-    }
-
-    @Test
-    public void windowHeightIsMax_noIncreaseWindowHeightA11yAction() {
-        final Rect windowBounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        final int startingWidth = windowBounds.width();
-        final int startingHeight = windowBounds.height();
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingWidth, startingHeight);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AccessibilityNodeInfo accessibilityNodeInfo =
-                mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_increase_window_height, null)));
-    }
-
-    @Test
-    public void windowWidthIsNotMin_performA11yActionDecreaseWidth_windowWidthDecreased() {
-        int mMinWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int startingSize = (int) (mMinWindowSize * 1.1);
-        final float changeWindowSizeAmount = mContext.getResources().getFraction(
-                R.fraction.magnification_resize_window_size_amount,
-                /* base= */ 1,
-                /* pbase= */ 1);
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mirrorView.performAccessibilityAction(
-                            R.id.accessibility_action_decrease_window_width, null);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-        // Window width includes the magnifier frame and the margin. Decreasing the window size
-        // will be decreasing the amount of the frame size only.
-        int newWindowWidth =
-                (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
-                        + 2 * mirrorSurfaceMargin;
-        assertEquals(newWindowWidth, actualWindowWidth.get());
-        assertEquals(startingSize, actualWindowHeight.get());
-    }
-
-    @Test
-    public void windowHeightIsNotMin_performA11yActionDecreaseHeight_windowHeightDecreased() {
-        int mMinWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int startingSize = (int) (mMinWindowSize * 1.1);
-        final float changeWindowSizeAmount = mContext.getResources().getFraction(
-                R.fraction.magnification_resize_window_size_amount,
-                /* base= */ 1,
-                /* pbase= */ 1);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mirrorView.performAccessibilityAction(
-                            R.id.accessibility_action_decrease_window_height, null);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        final int mirrorSurfaceMargin = mResources.getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-        // Window height includes the magnifier frame and the margin. Decreasing the window size
-        // will be decreasing the amount of the frame size only.
-        int newWindowHeight =
-                (int) ((startingSize - 2 * mirrorSurfaceMargin) * (1 - changeWindowSizeAmount))
-                        + 2 * mirrorSurfaceMargin;
-        assertEquals(startingSize, actualWindowWidth.get());
-        assertEquals(newWindowHeight, actualWindowHeight.get());
-    }
-
-    @Test
-    public void windowWidthIsMin_noDecreaseWindowWidthA11yAction() {
-        int mMinWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int startingSize = mMinWindowSize;
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AccessibilityNodeInfo accessibilityNodeInfo =
-                mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_decrease_window_width, null)));
-    }
-
-    @Test
-    public void windowHeightIsMin_noDecreaseWindowHeightA11yAcyion() {
-        int mMinWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int startingSize = mMinWindowSize;
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-            mWindowMagnificationController.setEditMagnifierSizeMode(true);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-        final AccessibilityNodeInfo accessibilityNodeInfo =
-                mirrorView.createAccessibilityNodeInfo();
-        assertFalse(accessibilityNodeInfo.getActionList().contains(
-                new AccessibilityAction(R.id.accessibility_action_decrease_window_height, null)));
-    }
-
-    @Test
-    public void enableWindowMagnification_hasA11yWindowTitle() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        assertEquals(getContext().getResources().getString(
-                com.android.internal.R.string.android_system_label), getAccessibilityWindowTitle());
-    }
-
-    @Test
-    public void enableWindowMagnificationWithScaleLessThanOne_enabled_disabled() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(0.9f, Float.NaN,
-                    Float.NaN);
-        });
-
-        assertEquals(Float.NaN, mWindowMagnificationController.getScale(), 0);
-    }
-
-    @Test
-    public void enableWindowMagnification_rotationIsChanged_updateRotationValue() {
-        // the config orientation should not be undefined, since it would cause config.diff
-        // returning 0 and thus the orientation changed would not be detected
-        assertNotEquals(ORIENTATION_UNDEFINED, mResources.getConfiguration().orientation);
-
-        final Configuration config = mResources.getConfiguration();
-        config.orientation = config.orientation == ORIENTATION_LANDSCAPE ? ORIENTATION_PORTRAIT
-                : ORIENTATION_LANDSCAPE;
-        final int newRotation = simulateRotateTheDevice();
-
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN, Float.NaN));
-
-        assertEquals(newRotation, mWindowMagnificationController.mRotation);
-    }
-
-    @Test
-    public void enableWindowMagnification_registerComponentCallback() {
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN,
-                        Float.NaN));
-
-        verify(mContext).registerComponentCallbacks(mWindowMagnificationController);
-    }
-
-    @Test
-    public void onLocaleChanged_enabled_updateA11yWindowTitle() {
-        final String newA11yWindowTitle = "new a11y window title";
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-        final TestableResources testableResources = getContext().getOrCreateTestableResources();
-        testableResources.addOverride(com.android.internal.R.string.android_system_label,
-                newA11yWindowTitle);
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
-        });
-
-        assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
-    }
-
-    @Ignore("it's flaky in presubmit but works in abtd, filter for now. b/305654925")
-    @Test
-    public void onSingleTap_enabled_scaleAnimates() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.onSingleTap(mSpyView);
-        });
-
-        final View mirrorView = mWindowManager.getAttachedView();
-
-        final AtomicDouble maxScaleX = new AtomicDouble();
-        advanceTimeBy(mWaitBounceEffectDuration, /* runnableOnEachRefresh= */ () -> {
-            // For some reason the fancy way doesn't compile...
-            // maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
-            final double oldMax = maxScaleX.get();
-            final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
-            assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
-        });
-
-        assertTrue(maxScaleX.get() > 1.0);
-    }
-
-    @Test
-    public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        setSystemGestureInsets();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN, Float.NaN,
-                    Float.NaN);
-        });
-
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
-        });
-
-        ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
-    }
-
-    @Test
-    public void moveWindowMagnificationToRightEdge_dragHandleMovesToLeftAndUpdatesTapExcludeRegion()
-            throws RemoteException {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        setSystemGestureInsets();
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.updateWindowMagnificationInternal(
-                            Float.NaN, Float.NaN, Float.NaN);
-                });
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.moveWindowMagnifier(bounds.width(), 0);
-                });
-
-        // Wait for Region updated.
-        waitForIdleSync();
-
-        final ArgumentCaptor<Region> tapExcludeRegionCapturer =
-                ArgumentCaptor.forClass(Region.class);
-        verify(mWindowSessionSpy, times(2))
-                .updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
-        Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
-        RegionIterator iterator = new RegionIterator(tapExcludeRegion);
-
-        final Rect topRect = new Rect();
-        final Rect bottomRect = new Rect();
-        assertTrue(iterator.next(topRect));
-        assertTrue(iterator.next(bottomRect));
-        assertFalse(iterator.next(new Rect()));
-
-        assertEquals(topRect.right, bottomRect.right);
-        assertNotEquals(topRect.left, bottomRect.left);
-    }
-
-    @Test
-    public void moveWindowMagnificationToLeftEdge_dragHandleMovesToRightAndUpdatesTapExcludeRegion()
-            throws RemoteException {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        setSystemGestureInsets();
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.updateWindowMagnificationInternal(
-                            Float.NaN, Float.NaN, Float.NaN);
-                });
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.moveWindowMagnifier(-bounds.width(), 0);
-                });
-
-        // Wait for Region updated.
-        waitForIdleSync();
-
-        final ArgumentCaptor<Region> tapExcludeRegionCapturer =
-                ArgumentCaptor.forClass(Region.class);
-        verify(mWindowSessionSpy).updateTapExcludeRegion(any(), tapExcludeRegionCapturer.capture());
-        Region tapExcludeRegion = tapExcludeRegionCapturer.getValue();
-        RegionIterator iterator = new RegionIterator(tapExcludeRegion);
-
-        final Rect topRect = new Rect();
-        final Rect bottomRect = new Rect();
-        assertTrue(iterator.next(topRect));
-        assertTrue(iterator.next(bottomRect));
-        assertFalse(iterator.next(new Rect()));
-
-        assertEquals(topRect.left, bottomRect.left);
-        assertNotEquals(topRect.right, bottomRect.right);
-    }
-
-    @Test
-    public void setMinimumWindowSize_enabled_expectedWindowSize() {
-        final int minimumWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int  expectedWindowHeight = minimumWindowSize;
-        final int  expectedWindowWidth = minimumWindowSize;
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
-            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-
-        });
-
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
-    }
-
-    @Test
-    public void setMinimumWindowSizeThenEnable_expectedWindowSize() {
-        final int minimumWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final int  expectedWindowHeight = minimumWindowSize;
-        final int  expectedWindowWidth = minimumWindowSize;
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.setWindowSize(expectedWindowWidth, expectedWindowHeight);
-            mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                    Float.NaN, Float.NaN);
-            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-        });
-
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
-    }
-
-    @Test
-    public void setWindowSizeLessThanMin_enabled_minimumWindowSize() {
-        final int minimumWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.setWindowSize(minimumWindowSize - 10,
-                    minimumWindowSize - 10);
-            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-        });
-
-        assertEquals(minimumWindowSize, actualWindowHeight.get());
-        assertEquals(minimumWindowSize, actualWindowWidth.get());
-    }
-
-    @Test
-    public void setWindowSizeLargerThanScreenSize_enabled_windowSizeIsScreenSize() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.setWindowSize(bounds.width() + 10, bounds.height() + 10);
-            actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-            actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-        });
-
-        assertEquals(bounds.height(), actualWindowHeight.get());
-        assertEquals(bounds.width(), actualWindowWidth.get());
-    }
-
-    @Test
-    public void changeMagnificationSize_expectedWindowSize() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
-        final float magnificationScaleLarge = 2.5f;
-        final int initSize = Math.min(bounds.width(), bounds.height()) / 3;
-        final int magnificationSize = (int) (initSize * magnificationScaleLarge)
-                - (int) (initSize * magnificationScaleLarge) % 2;
-
-        final int expectedWindowHeight = magnificationSize;
-        final int expectedWindowWidth = magnificationSize;
-
-        mInstrumentation.runOnMainSync(
-                () ->
-                        mWindowMagnificationController.updateWindowMagnificationInternal(
-                                Float.NaN, Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.changeMagnificationSize(
-                            WindowMagnificationSettings.MagnificationSize.LARGE);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        assertEquals(expectedWindowHeight, actualWindowHeight.get());
-        assertEquals(expectedWindowWidth, actualWindowWidth.get());
-    }
-
-    @Test
-    public void editModeOnDragCorner_resizesWindow() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
-        final int startingSize = (int) (bounds.width() / 2);
-
-        mInstrumentation.runOnMainSync(
-                () ->
-                        mWindowMagnificationController.updateWindowMagnificationInternal(
-                                Float.NaN, Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-                    mWindowMagnificationController.setEditMagnifierSizeMode(true);
-                });
-
-        waitForIdleSync();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController
-                            .onDrag(getInternalView(R.id.bottom_right_corner), 2f, 1f);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-
-        assertEquals(startingSize + 1, actualWindowHeight.get());
-        assertEquals(startingSize + 2, actualWindowWidth.get());
-    }
-
-    @Test
-    public void editModeOnDragEdge_resizesWindowInOnlyOneDirection() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-
-        final int startingSize = (int) (bounds.width() / 2f);
-
-        mInstrumentation.runOnMainSync(
-                () ->
-                        mWindowMagnificationController.updateWindowMagnificationInternal(
-                                Float.NaN, Float.NaN, Float.NaN));
-
-        final AtomicInteger actualWindowHeight = new AtomicInteger();
-        final AtomicInteger actualWindowWidth = new AtomicInteger();
-
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.setWindowSize(startingSize, startingSize);
-                    mWindowMagnificationController.setEditMagnifierSizeMode(true);
-                    mWindowMagnificationController
-                            .onDrag(getInternalView(R.id.bottom_handle), 2f, 1f);
-                    actualWindowHeight.set(mWindowManager.getLayoutParamsFromAttachedView().height);
-                    actualWindowWidth.set(mWindowManager.getLayoutParamsFromAttachedView().width);
-                });
-        assertEquals(startingSize + 1, actualWindowHeight.get());
-        assertEquals(startingSize, actualWindowWidth.get());
-    }
-
-    @Test
-    public void setWindowCenterOutOfScreen_enabled_magnificationCenterIsInsideTheScreen() {
-
-        final int minimumWindowSize = mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.accessibility_window_magnifier_min_size);
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        mInstrumentation.runOnMainSync(
-                () -> mWindowMagnificationController.updateWindowMagnificationInternal(Float.NaN,
-                        Float.NaN, Float.NaN));
-
-        final AtomicInteger magnificationCenterX = new AtomicInteger();
-        final AtomicInteger magnificationCenterY = new AtomicInteger();
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.setWindowSizeAndCenter(minimumWindowSize,
-                    minimumWindowSize, bounds.right, bounds.bottom);
-            magnificationCenterX.set((int) mWindowMagnificationController.getCenterX());
-            magnificationCenterY.set((int) mWindowMagnificationController.getCenterY());
-        });
-
-        assertTrue(magnificationCenterX.get() < bounds.right);
-        assertTrue(magnificationCenterY.get() < bounds.bottom);
-    }
-
-    @Test
-    public void performSingleTap_DragHandle() {
-        final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        mInstrumentation.runOnMainSync(
-                () -> {
-                    mWindowMagnificationController.updateWindowMagnificationInternal(
-                            1.5f, bounds.centerX(), bounds.centerY());
-                });
-        View dragButton = getInternalView(R.id.drag_handle);
-
-        // Perform a single-tap
-        final long downTime = SystemClock.uptimeMillis();
-        dragButton.dispatchTouchEvent(
-                obtainMotionEvent(downTime, 0, ACTION_DOWN, 100, 100));
-        dragButton.dispatchTouchEvent(
-                obtainMotionEvent(downTime, downTime, ACTION_UP, 100, 100));
-
-        verify(mWindowManager).addView(any(View.class), any());
-    }
-
-    private <T extends View> T getInternalView(@IdRes int idRes) {
-        View mirrorView = mWindowManager.getAttachedView();
-        T view = mirrorView.findViewById(idRes);
-        assertNotNull(view);
-        return view;
-    }
-
-    private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
-            float y) {
-        return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
-    }
-
-    private CharSequence getAccessibilityWindowTitle() {
-        final View mirrorView = mWindowManager.getAttachedView();
-        if (mirrorView == null) {
-            return null;
-        }
-        WindowManager.LayoutParams layoutParams =
-                (WindowManager.LayoutParams) mirrorView.getLayoutParams();
-        return layoutParams.accessibilityTitle;
-    }
-
-    private boolean hasMagnificationOverlapFlag() {
-        return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
-    }
-
-    private void setSystemGestureInsets() {
-        final WindowInsets testInsets = new WindowInsets.Builder()
-                .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
-                .build();
-        mWindowManager.setWindowInsets(testInsets);
-    }
-
-    private int updateMirrorSurfaceMarginDimension() {
-        return mContext.getResources().getDimensionPixelSize(
-                R.dimen.magnification_mirror_surface_margin);
-    }
-
-    @Surface.Rotation
-    private int simulateRotateTheDevice() {
-        final Display display = Mockito.spy(mContext.getDisplay());
-        final int currentRotation = display.getRotation();
-        final int newRotation = (currentRotation + 1) % 4;
-        when(display.getRotation()).thenReturn(newRotation);
-        when(mContext.getDisplay()).thenReturn(display);
-        return newRotation;
-    }
-
-    // advance time based on the device frame refresh rate
-    private void advanceTimeBy(long timeDelta) {
-        advanceTimeBy(timeDelta, /* runnableOnEachRefresh= */ null);
-    }
-
-    // advance time based on the device frame refresh rate, and trigger runnable on each refresh
-    private void advanceTimeBy(long timeDelta, @Nullable Runnable runnableOnEachRefresh) {
-        final float frameRate = mContext.getDisplay().getRefreshRate();
-        final int timeSlot = (int) (1000 / frameRate);
-        int round = (int) Math.ceil((double) timeDelta / timeSlot);
-        for (; round >= 0; round--) {
-            mInstrumentation.runOnMainSync(() -> {
-                mAnimatorTestRule.advanceTimeBy(timeSlot);
-                if (runnableOnEachRefresh != null) {
-                    runnableOnEachRefresh.run();
-                }
-            });
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index 6ff1b81..9b09ec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -90,6 +90,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.LargeTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.AnimatorTestRule;
@@ -144,6 +145,8 @@
     private SurfaceControl.Transaction mTransaction;
     @Mock
     private SecureSettings mSecureSettings;
+    @Mock
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
 
     private long mWaitAnimationDuration;
     private long mWaitBounceEffectDuration;
@@ -240,7 +243,8 @@
                         mSecureSettings,
                         scvhSupplier,
                         /* sfVsyncFrameProvider= */ null,
-                        /* globalWindowSessionSupplier= */ null);
+                        /* globalWindowSessionSupplier= */ null,
+                        mViewCaptureAwareWindowManager);
 
         verify(mMirrorWindowControl).setWindowDelegate(
                 any(MirrorWindowControl.MirrorWindowDelegate.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 9507077..7c0c5c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -22,6 +22,9 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 import static android.view.WindowInsets.Type.systemBars;
 
+import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE;
+import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
@@ -429,7 +432,7 @@
         mSettingView = mWindowMagnificationSettings.getSettingView();
         mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_slider);
         assertThat(mZoomSeekbar.getProgress()).isEqualTo(10);
-        assertThat(mZoomSeekbar.getMax()).isEqualTo(70);
+        assertThat(mZoomSeekbar.getMax()).isEqualTo(getSeekBarMax());
     }
 
     @Test
@@ -473,29 +476,26 @@
 
     @Test
     public void seekbarProgress_maxMagnificationBefore_seekbarProgressIsMax() {
-        mWindowMagnificationSettings.setMagnificationScale(8f);
+        mWindowMagnificationSettings.setMagnificationScale(SCALE_MAX_VALUE);
         setupMagnificationCapabilityAndMode(
                 /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
                 /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
         mWindowMagnificationSettings.showSettingPanel();
 
-        // 8.0f is max magnification {@link MagnificationScaleProvider#MAX_SCALE}.
-        // Max zoom seek bar is 70.
-        assertThat(mZoomSeekbar.getProgress()).isEqualTo(70);
+        assertThat(mZoomSeekbar.getProgress()).isEqualTo(getSeekBarMax());
     }
 
     @Test
     public void seekbarProgress_aboveMaxMagnificationBefore_seekbarProgressIsMax() {
-        mWindowMagnificationSettings.setMagnificationScale(9f);
+        mWindowMagnificationSettings.setMagnificationScale(SCALE_MAX_VALUE + 1f);
         setupMagnificationCapabilityAndMode(
                 /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
                 /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
         mWindowMagnificationSettings.showSettingPanel();
 
-        // Max zoom seek bar is 70.
-        assertThat(mZoomSeekbar.getProgress()).isEqualTo(70);
+        assertThat(mZoomSeekbar.getProgress()).isEqualTo(getSeekBarMax());
     }
 
     @Test
@@ -589,4 +589,11 @@
                 anyInt(),
                 eq(UserHandle.USER_CURRENT))).thenReturn(mode);
     }
+
+    private int getSeekBarMax() {
+        // Calculates the maximum index (or positions) the seekbar can have.
+        // This is achieved by multiplying the range of possible scales with the magnitude of
+        // change per each movement on the seekbar.
+        return (int) ((SCALE_MAX_VALUE - SCALE_MIN_VALUE) * mZoomSeekbar.getChangeMagnitude());
+    }
 }
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 b71739a..5e37d4c 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
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.hardware.display.DisplayManager;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.TestableLooper;
@@ -37,15 +38,20 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCapture;
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.settings.FakeDisplayTracker;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Lazy;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -71,9 +77,11 @@
 
     private Context mContextWrapper;
     private WindowManager mWindowManager;
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private AccessibilityManager mAccessibilityManager;
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private AccessibilityFloatingMenuController mController;
+    private TestableLooper mTestableLooper;
     @Mock
     private AccessibilityButtonTargetsObserver mTargetsObserver;
     @Mock
@@ -83,6 +91,10 @@
     private KeyguardUpdateMonitorCallback mKeyguardCallback;
     @Mock
     private SecureSettings mSecureSettings;
+    @Mock
+    private Lazy<ViewCapture> mLazyViewCapture;
+    @Mock
+    private NavigationModeController mNavigationModeController;
 
     @Before
     public void setUp() throws Exception {
@@ -95,7 +107,10 @@
         };
 
         mWindowManager = mContext.getSystemService(WindowManager.class);
+        mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
+                mLazyViewCapture, /* isViewCaptureEnabled= */ false);
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
+        mTestableLooper = TestableLooper.get(this);
 
         when(mTargetsObserver.getCurrentAccessibilityButtonTargets())
                 .thenReturn(Settings.Secure.getStringForUser(mContextWrapper.getContentResolver(),
@@ -154,7 +169,8 @@
         enableAccessibilityFloatingMenuConfig();
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
-                mAccessibilityManager, mSecureSettings);
+                mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
+                mNavigationModeController);
         captureKeyguardUpdateMonitorCallback();
         mKeyguardCallback.onUserUnlocked();
 
@@ -181,7 +197,8 @@
         enableAccessibilityFloatingMenuConfig();
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
-                mAccessibilityManager, mSecureSettings);
+                mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
+                mNavigationModeController);
         captureKeyguardUpdateMonitorCallback();
 
         mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -195,7 +212,8 @@
         enableAccessibilityFloatingMenuConfig();
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
-                mAccessibilityManager, mSecureSettings);
+                mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
+                mNavigationModeController);
         captureKeyguardUpdateMonitorCallback();
         mKeyguardCallback.onUserUnlocked();
         mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -216,7 +234,8 @@
         mKeyguardCallback.onKeyguardVisibilityChanged(false);
 
         mKeyguardCallback.onUserSwitching(fakeUserId);
-        mKeyguardCallback.onUserSwitchComplete(fakeUserId);
+        mController.mUserInitializationCompleteCallback.onUserInitializationComplete(1);
+        mTestableLooper.processAllMessages();
 
         assertThat(mController.mFloatingMenu).isNotNull();
     }
@@ -321,13 +340,18 @@
 
     private AccessibilityFloatingMenuController setUpController() {
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+        final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager =
+                new ViewCaptureAwareWindowManager(windowManager, mLazyViewCapture,
+                        /* isViewCaptureEnabled= */ false);
         final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
         final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
         mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
         final AccessibilityFloatingMenuController controller =
                 new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
-                        displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver,
-                        mKeyguardUpdateMonitor, mSecureSettings, displayTracker);
+                        viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
+                        mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings,
+                        displayTracker, mNavigationModeController, new Handler(
+                                mTestableLooper.getLooper()));
         controller.init();
 
         return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
deleted file mode 100644
index 19b2700..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ /dev/null
@@ -1,136 +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.accessibility.floatingmenu;
-
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.annotation.NonNull;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.TestableLooper;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-
-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.utils.TestUtils;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
-
-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;
-
-/** Tests for {@link DragToInteractAnimationController}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
[email protected]
-public class DragToInteractAnimationControllerTest extends SysuiTestCase {
-    private DragToInteractAnimationController mDragToInteractAnimationController;
-    private DragToInteractView mInteractView;
-    private DismissView mDismissView;
-
-    @Rule
-    public MockitoRule mockito = MockitoJUnit.rule();
-
-    @Mock
-    private AccessibilityManager mAccessibilityManager;
-
-    @Before
-    public void setUp() throws Exception {
-        final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
-        final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                mockSecureSettings);
-        final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
-                stubWindowManager);
-        final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
-                stubMenuViewAppearance, mockSecureSettings));
-        mInteractView = spy(new DragToInteractView(mContext));
-        mDismissView = spy(new DismissView(mContext));
-
-        if (Flags.floatingMenuDragToEdit()) {
-            mDragToInteractAnimationController = new DragToInteractAnimationController(
-                    mInteractView, stubMenuView);
-        } else {
-            mDragToInteractAnimationController = new DragToInteractAnimationController(
-                    mDismissView, stubMenuView);
-        }
-
-        mDragToInteractAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() {
-            @Override
-            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target,
-                    @NonNull MagnetizedObject<?> draggedObject) {
-
-            }
-
-            @Override
-            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
-                    @NonNull MagnetizedObject<?> draggedObject, float velX, float velY,
-                    boolean wasFlungOut) {
-
-            }
-
-            @Override
-            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target,
-                    @NonNull MagnetizedObject<?> draggedObject) {
-
-            }
-        });
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void showDismissView_success_old() {
-        mDragToInteractAnimationController.showInteractView(true);
-
-        verify(mDismissView).show();
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void hideDismissView_success_old() {
-        mDragToInteractAnimationController.showInteractView(false);
-
-        verify(mDismissView).hide();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void showDismissView_success() {
-        mDragToInteractAnimationController.showInteractView(true);
-
-        verify(mInteractView).show();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void hideDismissView_success() {
-        mDragToInteractAnimationController.showInteractView(false);
-
-        verify(mInteractView).hide();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index c5509ac..157cccc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -44,6 +44,7 @@
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.utils.TestUtils;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.res.R;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -94,7 +95,8 @@
         mMenuViewLayer = spy(new MenuViewLayer(
                 mContext, stubWindowManager, mAccessibilityManager,
                 stubMenuViewModel, stubMenuViewAppearance, mMenuView,
-                mock(IAccessibilityFloatingMenu.class), mSecureSettings));
+                mock(IAccessibilityFloatingMenu.class), mSecureSettings,
+                mock(NavigationModeController.class)));
         doNothing().when(mMenuViewLayer).gotoEditScreen();
 
         doReturn(mDraggableBounds).when(mMenuView).getMenuDraggableBounds();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
deleted file mode 100644
index 4373c88..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ /dev/null
@@ -1,254 +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.accessibility.floatingmenu;
-
-import static android.R.id.empty;
-import static android.view.View.OVER_SCROLL_NEVER;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.TestableLooper;
-import android.view.MotionEvent;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.accessibility.dialog.AccessibilityTarget;
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.accessibility.MotionEventHelper;
-import com.android.systemui.accessibility.utils.TestUtils;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.bubbles.DismissView;
-
-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.MockitoRule;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/** Tests for {@link MenuListViewTouchHandler}. */
-@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
-@SmallTest
-public class MenuListViewTouchHandlerTest extends SysuiTestCase {
-    private final List<AccessibilityTarget> mStubTargets = new ArrayList<>(
-            Collections.singletonList(mock(AccessibilityTarget.class)));
-    private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
-    private MenuView mStubMenuView;
-    private MenuListViewTouchHandler mTouchHandler;
-    private MenuAnimationController mMenuAnimationController;
-    private DragToInteractAnimationController mDragToInteractAnimationController;
-    private RecyclerView mStubListView;
-    private DismissView mDismissView;
-    private DragToInteractView mInteractView;
-
-    @Rule
-    public MockitoRule mockito = MockitoJUnit.rule();
-
-    @Mock
-    private AccessibilityManager mAccessibilityManager;
-
-    @Before
-    public void setUp() throws Exception {
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        final SecureSettings secureSettings = TestUtils.mockSecureSettings();
-        final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                secureSettings);
-        final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
-                windowManager);
-        mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
-                secureSettings);
-        mStubMenuView.setTranslationX(0);
-        mStubMenuView.setTranslationY(0);
-        mMenuAnimationController = spy(new MenuAnimationController(
-                mStubMenuView, stubMenuViewAppearance));
-        mInteractView = spy(new DragToInteractView(mContext));
-        mDismissView = spy(new DismissView(mContext));
-
-        if (Flags.floatingMenuDragToEdit()) {
-            mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
-                    mInteractView, mStubMenuView));
-        } else {
-            mDragToInteractAnimationController = spy(new DragToInteractAnimationController(
-                    mDismissView, mStubMenuView));
-        }
-
-        mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
-                mDragToInteractAnimationController);
-        final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
-        mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
-        mStubListView.setAdapter(stubAdapter);
-    }
-
-    @Test
-    public void onActionDownEvent_shouldCancelAnimations() {
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-
-        verify(mMenuAnimationController).cancelAnimations();
-    }
-
-    @Test
-    public void onActionMoveEvent_notConsumedEvent_shouldMoveToPosition() {
-        doReturn(empty).when(mDragToInteractAnimationController).maybeConsumeMoveMotionEvent(
-                any(MotionEvent.class));
-        final int offset = 100;
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        final MotionEvent stubMoveEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
-                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-        mStubListView.setOverScrollMode(OVER_SCROLL_NEVER);
-
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
-
-        assertThat(mStubMenuView.getTranslationX()).isEqualTo(offset);
-        assertThat(mStubMenuView.getTranslationY()).isEqualTo(offset);
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void onActionMoveEvent_shouldShowDismissView() {
-        final int offset = 100;
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        final MotionEvent stubMoveEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
-                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
-
-        verify(mDismissView).show();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
-    public void onActionMoveEvent_shouldShowInteractView() {
-        final int offset = 100;
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        final MotionEvent stubMoveEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
-                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
-
-        verify(mInteractView).show();
-    }
-
-    @Test
-    public void dragAndDrop_shouldFlingMenuThenSpringToEdge() {
-        final int offset = 100;
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        final MotionEvent stubMoveEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
-                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-        final MotionEvent stubUpEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 5,
-                        MotionEvent.ACTION_UP, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
-
-        verify(mMenuAnimationController).flingMenuThenSpringToEdge(anyFloat(), anyFloat(),
-                anyFloat());
-    }
-
-    @Test
-    public void dragMenuOutOfBoundsAndDrop_moveToLeftEdge_shouldMoveToEdgeAndHide() {
-        final int offset = -100;
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        final MotionEvent stubMoveEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 3,
-                        MotionEvent.ACTION_MOVE, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-        final MotionEvent stubUpEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 5,
-                        MotionEvent.ACTION_UP, mStubMenuView.getTranslationX() + offset,
-                        mStubMenuView.getTranslationY() + offset);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
-
-        verify(mMenuAnimationController).moveToEdgeAndHide();
-    }
-
-    @Test
-    public void receiveActionDownMotionEvent_verifyOnActionDownEnd() {
-        final Runnable onActionDownEnd = mock(Runnable.class);
-        mTouchHandler.setOnActionDownEndListener(onActionDownEnd);
-
-        final MotionEvent stubDownEvent =
-                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0, /* eventTime= */ 1,
-                        MotionEvent.ACTION_DOWN, mStubMenuView.getTranslationX(),
-                        mStubMenuView.getTranslationY());
-        mTouchHandler.onInterceptTouchEvent(mStubListView, stubDownEvent);
-
-        verify(onActionDownEnd).run();
-    }
-
-    @After
-    public void tearDown() {
-        mMotionEventHelper.recycleEvents();
-        mMenuAnimationController.mPositionAnimations.values().forEach(DynamicAnimation::cancel);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
deleted file mode 100644
index bd1a7f0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ /dev/null
@@ -1,112 +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.accessibility.floatingmenu;
-
-import static android.view.WindowInsets.Type.displayCutout;
-import static android.view.WindowInsets.Type.systemBars;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-import android.view.accessibility.AccessibilityManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.SecureSettings;
-
-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;
-
-/** Tests for {@link MenuViewLayerController}. */
-@RunWith(AndroidJUnit4.class)
[email protected](setAsMainLooper = true)
-@SmallTest
-public class MenuViewLayerControllerTest extends SysuiTestCase {
-    @Rule
-    public MockitoRule mockito = MockitoJUnit.rule();
-
-    @Mock
-    private WindowManager mWindowManager;
-
-    @Mock
-    private AccessibilityManager mAccessibilityManager;
-
-    @Mock
-    private SecureSettings mSecureSettings;
-
-    @Mock
-    private WindowMetrics mWindowMetrics;
-
-    private MenuViewLayerController mMenuViewLayerController;
-
-    @Before
-    public void setUp() throws Exception {
-        final WindowManager wm = mContext.getSystemService(WindowManager.class);
-        doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
-                mWindowManager).getMaximumWindowMetrics();
-        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
-        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
-        when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
-        when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
-        mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
-                mAccessibilityManager, mSecureSettings);
-    }
-
-    @Test
-    public void show_shouldAddViewToWindow() {
-        mMenuViewLayerController.show();
-
-        verify(mWindowManager).addView(any(View.class), any(ViewGroup.LayoutParams.class));
-    }
-
-    @Test
-    public void hide_menuIsShowing_removeViewFromWindow() {
-        mMenuViewLayerController.show();
-
-        mMenuViewLayerController.hide();
-
-        verify(mWindowManager).removeView(any(View.class));
-    }
-
-    private WindowInsets stubDisplayInsets() {
-        final int stubStatusBarHeight = 118;
-        final int stubNavigationBarHeight = 125;
-        return new WindowInsets.Builder()
-                .setVisible(systemBars() | displayCutout(), true)
-                .setInsets(systemBars() | displayCutout(),
-                        Insets.of(0, stubStatusBarHeight, 0, stubNavigationBarHeight))
-                .build();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 12140b5..c451c32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -81,9 +81,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.accessibility.utils.TestUtils;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.res.R;
 import com.android.systemui.util.settings.SecureSettings;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.shared.magnetictarget.MagnetizedObject;
 
 import org.junit.After;
 import org.junit.Before;
@@ -169,7 +170,7 @@
 
         mMenuViewLayer = spy(new MenuViewLayer(mSpyContext, mStubWindowManager,
                 mStubAccessibilityManager, mMenuViewModel, menuViewAppearance, mMenuView,
-                mFloatingMenu, mSecureSettings));
+                mFloatingMenu, mSecureSettings, mock(NavigationModeController.class)));
         mMenuAnimationController = mMenuView.getMenuAnimationController();
 
         doNothing().when(mSpyContext).startActivity(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index a18d272..aa8c6b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -57,6 +57,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.ambient.touch.dagger.InputSessionComponent;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.log.LogBufferHelperKt;
 import com.android.systemui.shared.system.InputChannelCompat;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.display.DisplayHelper;
@@ -153,7 +154,8 @@
             when(mWindowManager.getMaximumWindowMetrics()).thenReturn(mWindowMetrics);
             mMonitor = new TouchMonitor(mExecutor, mBackgroundExecutor, mLifecycleRegistry,
                     mInputFactory, mDisplayHelper, mKosmos.getConfigurationInteractor(),
-                    handlers, mIWindowManager,  0);
+                    handlers, mIWindowManager, 0, "TouchMonitorTest",
+                    LogBufferHelperKt.logcatLogBuffer("TouchMonitorTest"));
             clearInvocations(mLifecycleRegistry);
             mMonitor.init();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index dc69cda..1e23690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -29,7 +29,6 @@
 import android.hardware.face.FaceSensorPropertiesInternal
 import android.hardware.fingerprint.FingerprintManager
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.os.Handler
 import android.os.IBinder
 import android.os.UserManager
 import android.testing.TestableLooper
@@ -42,10 +41,10 @@
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.widget.LockPatternUtils
 import com.android.launcher3.icons.IconProvider
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.data.repository.FakeBiometricStatusRepository
 import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -105,7 +104,6 @@
     @Mock lateinit var fingerprintManager: FingerprintManager
     @Mock lateinit var lockPatternUtils: LockPatternUtils
     @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
-    @Mock lateinit var panelInteractionDetector: AuthDialogPanelInteractionDetector
     @Mock lateinit var windowToken: IBinder
     @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
     @Mock lateinit var vibrator: VibratorHelper
@@ -114,6 +112,7 @@
     @Mock lateinit var selectedUserInteractor: SelectedUserInteractor
     @Mock private lateinit var packageManager: PackageManager
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
+    @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
 
     private lateinit var displayRepository: FakeDisplayRepository
     private lateinit var displayStateInteractor: DisplayStateInteractor
@@ -265,14 +264,6 @@
     }
 
     @Test
-    fun testActionCancel_panelInteractionDetectorDisable() {
-        val container = initializeFingerprintContainer()
-        container.mBiometricCallback.onUserCanceled()
-        waitForIdleSync()
-        verify(panelInteractionDetector).disable()
-    }
-
-    @Test
     fun testActionAuthenticated_sendsDismissedAuthenticated() {
         val container = initializeFingerprintContainer()
         container.mBiometricCallback.onAuthenticated()
@@ -416,19 +407,7 @@
     }
 
     @Test
-    fun testShowBiometricUI() {
-        mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
-        val container = initializeFingerprintContainer()
-
-        waitForIdleSync()
-
-        assertThat(container.hasCredentialView()).isFalse()
-        assertThat(container.hasBiometricPrompt()).isTrue()
-    }
-
-    @Test
     fun testShowBiometricUI_ContentViewWithMoreOptionsButton() {
-        mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
         mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         var isButtonClicked = false
         val contentView =
@@ -466,7 +445,6 @@
 
     @Test
     fun testShowCredentialUI_withVerticalListContentView() {
-        mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
         mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         val container =
             initializeFingerprintContainer(
@@ -488,7 +466,6 @@
 
     @Test
     fun testShowCredentialUI_withContentViewWithMoreOptionsButton() {
-        mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
         mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
         val contentView =
             PromptContentViewWithMoreOptionsButton.Builder()
@@ -674,7 +651,6 @@
             fingerprintProps,
             faceProps,
             wakefulnessLifecycle,
-            panelInteractionDetector,
             userManager,
             lockPatternUtils,
             interactionJankMonitor,
@@ -690,9 +666,9 @@
                 activityTaskManager
             ),
             { credentialViewModel },
-            Handler(TestableLooper.get(this).looper),
             fakeExecutor,
-            vibrator
+            vibrator,
+            lazyViewCapture
         ) {
         override fun postOnAnimation(runnable: Runnable) {
             runnable.run()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
deleted file mode 100644
index 0231486..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetectorTest.kt
+++ /dev/null
@@ -1,156 +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.platform.test.flag.junit.FlagsParameterization
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.andSceneContainer
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.shade.shadeTestUtil
-import com.android.systemui.testKosmos
-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.verify
-import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.verifyZeroInteractions
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-
-@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-class AuthDialogPanelInteractionDetectorTest(flags: FlagsParameterization?) : SysuiTestCase() {
-
-    companion object {
-        @JvmStatic
-        @Parameters(name = "{0}")
-        fun getParams(): List<FlagsParameterization> {
-            return FlagsParameterization.allCombinationsOf().andSceneContainer()
-        }
-    }
-
-    init {
-        mSetFlagsRule.setFlagsParameterization(flags!!)
-    }
-
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-
-    private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
-
-    @Mock private lateinit var action: Runnable
-
-    lateinit var detector: AuthDialogPanelInteractionDetector
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        detector =
-            AuthDialogPanelInteractionDetector(
-                kosmos.applicationCoroutineScope,
-                { kosmos.shadeInteractor },
-            )
-    }
-
-    @Test
-    fun enableDetector_expand_shouldRunAction() =
-        testScope.runTest {
-            // GIVEN shade is closed and detector is enabled
-            shadeTestUtil.setShadeExpansion(0f)
-            detector.enable(action)
-            runCurrent()
-
-            // WHEN shade expands
-            shadeTestUtil.setTracking(true)
-            shadeTestUtil.setShadeExpansion(.5f)
-            runCurrent()
-
-            // THEN action was run
-            verify(action).run()
-        }
-
-    @Test
-    fun enableDetector_isUserInteractingTrue_shouldNotPostRunnable() =
-        testScope.runTest {
-            // GIVEN isInteracting starts true
-            shadeTestUtil.setTracking(true)
-            runCurrent()
-            detector.enable(action)
-
-            // THEN action was not run
-            verifyZeroInteractions(action)
-        }
-
-    @Test
-    fun enableDetector_shadeExpandImmediate_shouldNotPostRunnable() =
-        testScope.runTest {
-            // GIVEN shade is closed and detector is enabled
-            shadeTestUtil.setShadeExpansion(0f)
-            detector.enable(action)
-            runCurrent()
-
-            // WHEN shade expands fully instantly
-            shadeTestUtil.setShadeExpansion(1f)
-            runCurrent()
-
-            // THEN action not run
-            verifyZeroInteractions(action)
-            detector.disable()
-        }
-
-    @Test
-    fun disableDetector_shouldNotPostRunnable() =
-        testScope.runTest {
-            // GIVEN shade is closed and detector is enabled
-            shadeTestUtil.setShadeExpansion(0f)
-            detector.enable(action)
-            runCurrent()
-
-            // WHEN detector is disabled and shade opens
-            detector.disable()
-            runCurrent()
-            shadeTestUtil.setTracking(true)
-            shadeTestUtil.setShadeExpansion(.5f)
-            runCurrent()
-
-            // THEN action not run
-            verifyZeroInteractions(action)
-        }
-
-    @Test
-    fun enableDetector_beginCollapse_shouldNotPostRunnable() =
-        testScope.runTest {
-            // GIVEN shade is open and detector is enabled
-            shadeTestUtil.setShadeExpansion(1f)
-            detector.enable(action)
-            runCurrent()
-
-            // WHEN shade begins to collapse
-            shadeTestUtil.programmaticCollapseShade()
-            runCurrent()
-
-            // THEN action not run
-            verifyZeroInteractions(action)
-            detector.disable()
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
deleted file mode 100644
index baef620..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDebouncerTest.kt
+++ /dev/null
@@ -1,221 +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.biometrics
-
-import android.hardware.biometrics.BiometricFaceConstants
-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.deviceentry.shared.model.HelpFaceAuthenticationStatus
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
[email protected]
-class FaceHelpMessageDebouncerTest : SysuiTestCase() {
-    private lateinit var underTest: FaceHelpMessageDebouncer
-    private val window = 9L
-    private val startWindow = 4L
-    private val shownFaceMessageFrequencyBoost = 2
-
-    @Before
-    fun setUp() {
-        underTest =
-            FaceHelpMessageDebouncer(
-                window = window,
-                startWindow = startWindow,
-                shownFaceMessageFrequencyBoost = shownFaceMessageFrequencyBoost,
-            )
-    }
-
-    @Test
-    fun getMessageBeforeStartWindow_null() {
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "testTooClose",
-                0
-            )
-        )
-        assertThat(underTest.getMessageToShow(0)).isNull()
-    }
-
-    @Test
-    fun getMessageAfterStartWindow() {
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                0
-            )
-        )
-
-        assertThat(underTest.getMessageToShow(startWindow)?.msgId)
-            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE)
-        assertThat(underTest.getMessageToShow(startWindow)?.msg).isEqualTo("tooClose")
-    }
-
-    @Test
-    fun getMessageAfterMessagesCleared_null() {
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                0
-            )
-        )
-        underTest.startNewFaceAuthSession(0)
-
-        assertThat(underTest.getMessageToShow(startWindow)).isNull()
-    }
-
-    @Test
-    fun messagesBeforeWindowRemoved() {
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                0
-            )
-        )
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                0
-            )
-        )
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                window - 1
-            )
-        )
-        val lastMessage =
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
-                "tooBright",
-                window
-            )
-        underTest.addMessage(lastMessage)
-
-        assertThat(underTest.getMessageToShow(window + 1)).isEqualTo(lastMessage)
-    }
-
-    @Test
-    fun getMessageTieGoesToMostRecent() {
-        for (i in 1..window step 2) {
-            underTest.addMessage(
-                HelpFaceAuthenticationStatus(
-                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                    "tooClose",
-                    i
-                )
-            )
-            underTest.addMessage(
-                HelpFaceAuthenticationStatus(
-                    BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
-                    "tooBright",
-                    i + 1
-                )
-            )
-        }
-
-        assertThat(underTest.getMessageToShow(window)?.msgId)
-            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT)
-        assertThat(underTest.getMessageToShow(window)?.msg).isEqualTo("tooBright")
-    }
-
-    @Test
-    fun boostCurrentlyShowingMessage() {
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
-                "tooBright",
-                0
-            )
-        )
-
-        val lastMessageShown = underTest.getMessageToShow(startWindow)
-        assertThat(lastMessageShown?.msgId)
-            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT)
-
-        for (i in 1..<shownFaceMessageFrequencyBoost) {
-            underTest.addMessage(
-                HelpFaceAuthenticationStatus(
-                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                    "tooClose",
-                    startWindow
-                )
-            )
-        }
-
-        // although technically there's a different msgId with a higher frequency count now, the
-        // shownFaceMessageFrequencyBoost causes the last message shown to get a "boost"
-        // to keep showing
-        assertThat(underTest.getMessageToShow(startWindow)).isEqualTo(lastMessageShown)
-    }
-
-    @Test
-    fun overcomeBoostedCurrentlyShowingMessage() {
-        // Comments are assuming shownFaceMessageFrequencyBoost = 2
-        // [B], weights: B=1
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_BRIGHT,
-                "tooBright",
-                0
-            )
-        )
-
-        // [B], showing messageB, weights: B=3
-        val messageB = underTest.getMessageToShow(startWindow)
-
-        // [B, C, C], showing messageB, weights: B=3, C=2
-        for (i in 1..shownFaceMessageFrequencyBoost) {
-            underTest.addMessage(
-                HelpFaceAuthenticationStatus(
-                    BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                    "tooClose",
-                    startWindow
-                )
-            )
-        }
-        // messageB is getting boosted to continue to show
-        assertThat(underTest.getMessageToShow(startWindow)).isEqualTo(messageB)
-
-        // receive one more FACE_ACQUIRED_TOO_CLOSE acquired info to pass the boost
-        // [C, C, C], showing messageB, weights: B=2, C=3
-        underTest.addMessage(
-            HelpFaceAuthenticationStatus(
-                BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE,
-                "tooClose",
-                startWindow
-            )
-        )
-
-        // Now FACE_ACQUIRED_TOO_CLOSE has surpassed the boosted messageB frequency
-        // [C, C, C], showing messageC, weights: C=5
-        assertThat(underTest.getMessageToShow(startWindow)?.msgId)
-            .isEqualTo(BiometricFaceConstants.FACE_ACQUIRED_TOO_CLOSE)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 720f207..dc499cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -155,7 +155,7 @@
                         Authenticators.BIOMETRIC_STRONG
                     }
                 isDeviceCredentialAllowed = allowCredentialFallback
-                componentNameForConfirmDeviceCredentialActivity =
+                realCallerForConfirmDeviceCredentialActivity =
                     if (setComponentNameForConfirmDeviceCredentialActivity)
                         componentNameOverriddenForConfirmDeviceCredentialActivity
                     else null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 534f25c..4fc4166 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -43,7 +43,6 @@
 import android.view.Surface
 import androidx.test.filters.SmallTest
 import com.android.app.activityTaskManager
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.biometrics.Utils.toBitmap
@@ -718,13 +717,9 @@
             assertThat(confirmHaptics?.hapticFeedbackConstant)
                 .isEqualTo(
                     if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
-                    else HapticFeedbackConstants.CONFIRM
+                    else HapticFeedbackConstants.BIOMETRIC_CONFIRM
                 )
-            assertThat(confirmHaptics?.flag)
-                .isEqualTo(
-                    if (expectConfirmation) null
-                    else HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
-                )
+            assertThat(confirmHaptics?.flag).isNull()
 
             if (expectConfirmation) {
                 kosmos.promptViewModel.confirmAuthenticated()
@@ -732,9 +727,8 @@
 
             val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
             assertThat(confirmedHaptics?.hapticFeedbackConstant)
-                .isEqualTo(HapticFeedbackConstants.CONFIRM)
-            assertThat(confirmedHaptics?.flag)
-                .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+                .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+            assertThat(confirmedHaptics?.flag).isNull()
         }
 
     @Test
@@ -748,9 +742,8 @@
 
         val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
         assertThat(currentHaptics?.hapticFeedbackConstant)
-            .isEqualTo(HapticFeedbackConstants.CONFIRM)
-        assertThat(currentHaptics?.flag)
-            .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+            .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+        assertThat(currentHaptics?.flag).isNull()
     }
 
     @Test
@@ -758,9 +751,9 @@
         kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
 
         val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
-        assertThat(currentHaptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
-        assertThat(currentHaptics?.flag)
-            .isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+        assertThat(currentHaptics?.hapticFeedbackConstant)
+            .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+        assertThat(currentHaptics?.flag).isNull()
     }
 
     // biometricprompt_sfps_fingerprint_authenticating reused across rotations
@@ -871,8 +864,9 @@
         )
 
         val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
-        assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
-        assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+        assertThat(haptics?.hapticFeedbackConstant)
+            .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+        assertThat(haptics?.flag).isNull()
     }
 
     @Test
@@ -902,10 +896,12 @@
 
         val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
         if (expectConfirmation) {
-            assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.REJECT)
-            assertThat(haptics?.flag).isEqualTo(HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING)
+            assertThat(haptics?.hapticFeedbackConstant)
+                .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+            assertThat(haptics?.flag).isNull()
         } else {
-            assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.CONFIRM)
+            assertThat(haptics?.hapticFeedbackConstant)
+                .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
         }
     }
 
@@ -1385,7 +1381,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionOverriddenByVerticalListContentView() =
         runGenericTest(description = "test description", contentView = promptContentView) {
             val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1396,7 +1392,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
         runGenericTest(
             description = "test description",
@@ -1410,7 +1406,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun descriptionWithoutContentView() =
         runGenericTest(description = "test description") {
             val contentView by collectLastValue(kosmos.promptViewModel.contentView)
@@ -1421,7 +1417,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_nullIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1430,7 +1426,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_defaultFromActivityInfo() =
         runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1445,7 +1441,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_defaultIsNull() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1454,7 +1450,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_default() = runGenericTest {
         val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
         assertThat(logoInfo).isNotNull()
@@ -1462,7 +1458,7 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_resSetByApp() =
         runGenericTest(logoRes = logoResFromApp) {
             val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap()
@@ -1472,7 +1468,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logo_bitmapSetByApp() =
         runGenericTest(logoBitmap = logoBitmapFromApp) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1480,7 +1476,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_emptyIfPkgNameNotFound() =
         runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1488,7 +1484,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_defaultFromActivityInfo() =
         runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1500,7 +1496,7 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_defaultIsEmpty() =
         runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1508,14 +1504,14 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_default() = runGenericTest {
         val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
         assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo)
     }
 
     @Test
-    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
+    @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
     fun logoDescription_setByApp() =
         runGenericTest(logoDescription = logoDescriptionFromApp) {
             val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo)
@@ -1523,7 +1519,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_bottom_rotation0() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
         val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1531,7 +1526,6 @@
     } // TODO(b/335278136): Add test for no sensor landscape
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_bottom_forceLarge() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
         kosmos.promptViewModel.onSwitchToCredential()
@@ -1540,7 +1534,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_bottom_largeScreen() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
         kosmos.displayStateRepository.setIsLargeScreen(true)
@@ -1549,7 +1542,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_right_rotation90() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
         val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1557,7 +1549,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_left_rotation270() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
         val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1565,7 +1556,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun position_top_rotation180() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
         val position by collectLastValue(kosmos.promptViewModel.position)
@@ -1577,7 +1567,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_bottom() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
         val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
@@ -1585,7 +1574,6 @@
     } // TODO(b/335278136): Add test for no sensor landscape
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_right() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
 
@@ -1602,7 +1590,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_right_onlyShortTitle() =
         runGenericTest(subtitle = "") {
             kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
@@ -1617,7 +1604,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_left() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
 
@@ -1634,7 +1620,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_left_onlyShortTitle() =
         runGenericTest(subtitle = "") {
             kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
@@ -1649,7 +1634,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_CONSTRAINT_BP)
     fun guideline_top() = runGenericTest {
         kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
         val guidelineBounds by collectLastValue(kosmos.promptViewModel.guidelineBounds)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
deleted file mode 100644
index 3b2cf61..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/SideFpsOverlayViewModelTest.kt
+++ /dev/null
@@ -1,451 +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.ui.viewmodel
-
-import android.content.res.Configuration.UI_MODE_NIGHT_NO
-import android.content.res.Configuration.UI_MODE_NIGHT_YES
-import android.graphics.Color
-import android.graphics.Rect
-import android.hardware.biometrics.SensorLocationInternal
-import android.hardware.display.DisplayManagerGlobal
-import android.view.Display
-import android.view.DisplayInfo
-import android.view.WindowInsets
-import android.view.WindowMetrics
-import android.view.windowManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.airbnb.lottie.model.KeyPath
-import com.android.keyguard.keyguardUpdateMonitor
-import com.android.settingslib.Utils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider
-import com.android.systemui.biometrics.data.repository.biometricStatusRepository
-import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
-import com.android.systemui.biometrics.domain.interactor.displayStateInteractor
-import com.android.systemui.biometrics.shared.model.AuthenticationReason
-import com.android.systemui.biometrics.shared.model.DisplayRotation
-import com.android.systemui.biometrics.shared.model.FingerprintSensorType
-import com.android.systemui.biometrics.shared.model.LottieCallback
-import com.android.systemui.biometrics.shared.model.SensorStrength
-import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.display.data.repository.displayRepository
-import com.android.systemui.display.data.repository.displayStateRepository
-import com.android.systemui.keyguard.ui.viewmodel.sideFpsProgressBarViewModel
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
-import com.android.systemui.testKosmos
-import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-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.Mockito.mock
-import org.mockito.Mockito.spy
-import org.mockito.junit.MockitoJUnit
-import org.mockito.junit.MockitoRule
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class SideFpsOverlayViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
-
-    @Mock
-    private lateinit var fingerprintInteractiveToAuthProvider: FingerprintInteractiveToAuthProvider
-    @Mock private lateinit var screenSizeFoldProvider: ScreenSizeFoldProvider
-
-    private val contextDisplayInfo = DisplayInfo()
-
-    private val indicatorColor =
-        Utils.getColorAttrDefaultColor(
-            context,
-            com.android.internal.R.attr.materialColorPrimaryFixed
-        )
-    private val outerRimColor =
-        Utils.getColorAttrDefaultColor(
-            context,
-            com.android.internal.R.attr.materialColorPrimaryFixedDim
-        )
-    private val chevronFill =
-        Utils.getColorAttrDefaultColor(
-            context,
-            com.android.internal.R.attr.materialColorOnPrimaryFixed
-        )
-    private val color_blue400 =
-        context.getColor(com.android.settingslib.color.R.color.settingslib_color_blue400)
-
-    private var displayWidth: Int = 0
-    private var displayHeight: Int = 0
-    private var boundsWidth: Int = 0
-    private var boundsHeight: Int = 0
-
-    private lateinit var deviceConfig: DeviceConfig
-    private lateinit var sensorLocation: SensorLocationInternal
-
-    enum class DeviceConfig {
-        X_ALIGNED,
-        Y_ALIGNED,
-    }
-
-    @Before
-    fun setup() {
-        mContext = spy(mContext)
-
-        val resources = mContext.resources
-        whenever(mContext.display)
-            .thenReturn(
-                Display(mock(DisplayManagerGlobal::class.java), 1, contextDisplayInfo, resources)
-            )
-        kosmos.displayStateInteractor.setScreenSizeFoldProvider(screenSizeFoldProvider)
-
-        whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
-            .thenReturn(MutableStateFlow(false))
-    }
-
-    @Test
-    fun updatesOverlayViewProperties_onDisplayRotationChange_xAlignedSensor() {
-        kosmos.testScope.runTest {
-            setupTestConfiguration(
-                DeviceConfig.X_ALIGNED,
-                rotation = DisplayRotation.ROTATION_0,
-                isInRearDisplayMode = false
-            )
-
-            val overlayViewProperties by
-                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewProperties)
-
-            runCurrent()
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
-        }
-    }
-
-    @Test
-    fun updatesOverlayViewProperties_onDisplayRotationChange_yAlignedSensor() {
-        kosmos.testScope.runTest {
-            setupTestConfiguration(
-                DeviceConfig.Y_ALIGNED,
-                rotation = DisplayRotation.ROTATION_0,
-                isInRearDisplayMode = false
-            )
-
-            val overlayViewProperties by
-                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewProperties)
-
-            runCurrent()
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0)
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(0f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
-
-            assertThat(overlayViewProperties?.indicatorAsset).isEqualTo(R.raw.sfps_pulse_landscape)
-            assertThat(overlayViewProperties?.overlayViewRotation).isEqualTo(180f)
-        }
-    }
-
-    @Test
-    fun updatesOverlayViewParams_onDisplayRotationChange_xAlignedSensor() {
-        kosmos.testScope.runTest {
-            mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
-            setupTestConfiguration(
-                DeviceConfig.X_ALIGNED,
-                rotation = DisplayRotation.ROTATION_0,
-                isInRearDisplayMode = false
-            )
-
-            val overlayViewParams by
-                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewParams)
-
-            kosmos.sideFpsOverlayViewModel.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
-            runCurrent()
-
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationX)
-            assertThat(overlayViewParams!!.y).isEqualTo(0)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(0)
-            assertThat(overlayViewParams!!.y)
-                .isEqualTo(
-                    displayHeight - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
-                )
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x)
-                .isEqualTo(
-                    displayWidth - sensorLocation.sensorLocationX - sensorLocation.sensorRadius * 2
-                )
-            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
-            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationX)
-        }
-    }
-
-    @Test
-    fun updatesOverlayViewParams_onDisplayRotationChange_yAlignedSensor() {
-        kosmos.testScope.runTest {
-            mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
-            setupTestConfiguration(
-                DeviceConfig.Y_ALIGNED,
-                rotation = DisplayRotation.ROTATION_0,
-                isInRearDisplayMode = false
-            )
-
-            val overlayViewParams by
-                collectLastValue(kosmos.sideFpsOverlayViewModel.overlayViewParams)
-
-            kosmos.sideFpsOverlayViewModel.setLottieBounds(Rect(0, 0, boundsWidth, boundsHeight))
-            runCurrent()
-
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(displayWidth - boundsWidth)
-            assertThat(overlayViewParams!!.y).isEqualTo(sensorLocation.sensorLocationY)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(sensorLocation.sensorLocationY)
-            assertThat(overlayViewParams!!.y).isEqualTo(0)
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x).isEqualTo(0)
-            assertThat(overlayViewParams!!.y)
-                .isEqualTo(
-                    displayHeight - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
-                )
-
-            kosmos.displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270)
-            assertThat(overlayViewParams).isNotNull()
-            assertThat(overlayViewParams!!.x)
-                .isEqualTo(
-                    displayWidth - sensorLocation.sensorLocationY - sensorLocation.sensorRadius * 2
-                )
-            assertThat(overlayViewParams!!.y).isEqualTo(displayHeight - boundsHeight)
-        }
-    }
-
-    @Test
-    fun updatesLottieCallbacks_onShowIndicatorForDeviceEntry() {
-        kosmos.testScope.runTest {
-            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
-
-            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-                AuthenticationReason.NotRunning
-            )
-            kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
-            updatePrimaryBouncer(
-                isShowing = true,
-                isAnimatingAway = false,
-                fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
-            )
-            runCurrent()
-
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue600", "**"), indicatorColor))
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue400", "**"), outerRimColor))
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".black", "**"), chevronFill))
-        }
-    }
-
-    @Test
-    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inDarkMode() {
-        kosmos.testScope.runTest {
-            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
-            setDarkMode(true)
-
-            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-                AuthenticationReason.BiometricPromptAuthentication
-            )
-            kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
-            updatePrimaryBouncer(
-                isShowing = false,
-                isAnimatingAway = false,
-                fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
-            )
-            runCurrent()
-
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
-        }
-    }
-
-    @Test
-    fun updatesLottieCallbacks_onShowIndicatorForSystemServer_inLightMode() {
-        kosmos.testScope.runTest {
-            val lottieCallbacks by collectLastValue(kosmos.sideFpsOverlayViewModel.lottieCallbacks)
-            setDarkMode(false)
-
-            kosmos.biometricStatusRepository.setFingerprintAuthenticationReason(
-                AuthenticationReason.BiometricPromptAuthentication
-            )
-            kosmos.sideFpsProgressBarViewModel.setVisible(false)
-
-            updatePrimaryBouncer(
-                isShowing = false,
-                isAnimatingAway = false,
-                fpsDetectionRunning = true,
-                isUnlockingWithFpAllowed = true
-            )
-            runCurrent()
-
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".black", "**"), Color.WHITE))
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue600", "**"), color_blue400))
-            assertThat(lottieCallbacks)
-                .contains(LottieCallback(KeyPath(".blue400", "**"), color_blue400))
-        }
-    }
-
-    private fun setDarkMode(inDarkMode: Boolean) {
-        val uiMode =
-            if (inDarkMode) {
-                UI_MODE_NIGHT_YES
-            } else {
-                UI_MODE_NIGHT_NO
-            }
-
-        mContext.resources.configuration.uiMode = uiMode
-    }
-
-    private fun updatePrimaryBouncer(
-        isShowing: Boolean,
-        isAnimatingAway: Boolean,
-        fpsDetectionRunning: Boolean,
-        isUnlockingWithFpAllowed: Boolean,
-    ) {
-        kosmos.keyguardBouncerRepository.setPrimaryShow(isShowing)
-        kosmos.keyguardBouncerRepository.setPrimaryStartingToHide(false)
-        val primaryStartDisappearAnimation = if (isAnimatingAway) Runnable {} else null
-        kosmos.keyguardBouncerRepository.setPrimaryStartDisappearAnimation(
-            primaryStartDisappearAnimation
-        )
-
-        whenever(kosmos.keyguardUpdateMonitor.isFingerprintDetectionRunning)
-            .thenReturn(fpsDetectionRunning)
-        whenever(kosmos.keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
-            .thenReturn(isUnlockingWithFpAllowed)
-        mContext.orCreateTestableResources.addOverride(
-            R.bool.config_show_sidefps_hint_on_bouncer,
-            true
-        )
-    }
-
-    private suspend fun TestScope.setupTestConfiguration(
-        deviceConfig: DeviceConfig,
-        rotation: DisplayRotation = DisplayRotation.ROTATION_0,
-        isInRearDisplayMode: Boolean,
-    ) {
-        [email protected] = deviceConfig
-
-        when (deviceConfig) {
-            DeviceConfig.X_ALIGNED -> {
-                displayWidth = 3000
-                displayHeight = 1500
-                boundsWidth = 200
-                boundsHeight = 100
-                sensorLocation = SensorLocationInternal("", 2500, 0, boundsWidth / 2)
-            }
-            DeviceConfig.Y_ALIGNED -> {
-                displayWidth = 2500
-                displayHeight = 2000
-                boundsWidth = 100
-                boundsHeight = 200
-                sensorLocation = SensorLocationInternal("", displayWidth, 300, boundsHeight / 2)
-            }
-        }
-
-        whenever(kosmos.windowManager.maximumWindowMetrics)
-            .thenReturn(
-                WindowMetrics(
-                    Rect(0, 0, displayWidth, displayHeight),
-                    mock(WindowInsets::class.java),
-                )
-            )
-
-        contextDisplayInfo.uniqueId = DISPLAY_ID
-
-        kosmos.fingerprintPropertyRepository.setProperties(
-            sensorId = 1,
-            strength = SensorStrength.STRONG,
-            sensorType = FingerprintSensorType.POWER_BUTTON,
-            sensorLocations = mapOf(DISPLAY_ID to sensorLocation)
-        )
-
-        kosmos.displayStateRepository.setIsInRearDisplayMode(isInRearDisplayMode)
-        kosmos.displayStateRepository.setCurrentRotation(rotation)
-
-        kosmos.displayRepository.emitDisplayChangeEvent(0)
-        runCurrent()
-    }
-
-    companion object {
-        private const val DISPLAY_ID = "displayId"
-    }
-}
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 4d7c499..2c53fd6 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
@@ -113,7 +113,8 @@
             assertThat(actual)
                 .isEqualTo(
                     AudioSharingButtonState.Visible(
-                        R.string.quick_settings_bluetooth_audio_sharing_button_sharing
+                        R.string.quick_settings_bluetooth_audio_sharing_button_sharing,
+                        isActive = true
                     )
                 )
         }
@@ -163,7 +164,8 @@
             assertThat(actual)
                 .isEqualTo(
                     AudioSharingButtonState.Visible(
-                        R.string.quick_settings_bluetooth_audio_sharing_button
+                        R.string.quick_settings_bluetooth_audio_sharing_button,
+                        isActive = false
                     )
                 )
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index d01fac3..fb0fd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -328,4 +328,70 @@
             dialog.dismiss()
         }
     }
+
+    @Test
+    fun testOnAudioSharingButtonUpdated_visibleActive_activateButton() {
+        testScope.runTest {
+            val dialog = mBluetoothTileDialogDelegate.createDialog()
+            dialog.show()
+            fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+            mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+                dialog,
+                visibility = VISIBLE,
+                label = null,
+                isActive = true
+            )
+
+            val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+            assertThat(audioSharingButton).isNotNull()
+            assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+            assertThat(audioSharingButton.isActivated).isTrue()
+            dialog.dismiss()
+        }
+    }
+
+    @Test
+    fun testOnAudioSharingButtonUpdated_visibleNotActive_inactivateButton() {
+        testScope.runTest {
+            val dialog = mBluetoothTileDialogDelegate.createDialog()
+            dialog.show()
+            fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+            mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+                dialog,
+                visibility = VISIBLE,
+                label = null,
+                isActive = false
+            )
+
+            val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+            assertThat(audioSharingButton).isNotNull()
+            assertThat(audioSharingButton.visibility).isEqualTo(VISIBLE)
+            assertThat(audioSharingButton.isActivated).isFalse()
+            dialog.dismiss()
+        }
+    }
+
+    @Test
+    fun testOnAudioSharingButtonUpdated_gone_inactivateButton() {
+        testScope.runTest {
+            val dialog = mBluetoothTileDialogDelegate.createDialog()
+            dialog.show()
+            fakeSystemClock.setElapsedRealtime(Long.MAX_VALUE)
+            mBluetoothTileDialogDelegate.onAudioSharingButtonUpdated(
+                dialog,
+                visibility = GONE,
+                label = null,
+                isActive = false
+            )
+
+            val audioSharingButton = dialog.requireViewById<View>(R.id.audio_sharing_button)
+
+            assertThat(audioSharingButton).isNotNull()
+            assertThat(audioSharingButton.visibility).isEqualTo(GONE)
+            assertThat(audioSharingButton.isActivated).isFalse()
+            dialog.dismiss()
+        }
+    }
 }
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 9abb85d..d7bea66 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
@@ -77,6 +77,8 @@
 
     @Mock private lateinit var audioSharingInteractor: AudioSharingInteractor
 
+    @Mock private lateinit var bluetoothDeviceMetadataInteractor: BluetoothDeviceMetadataInteractor
+
     @Mock private lateinit var deviceItemInteractor: DeviceItemInteractor
 
     @Mock private lateinit var deviceItemActionInteractor: DeviceItemActionInteractor
@@ -138,6 +140,7 @@
                     )
                 ),
                 audioSharingInteractor,
+                bluetoothDeviceMetadataInteractor,
                 mDialogTransitionAnimator,
                 activityStarter,
                 uiEventLogger,
@@ -150,6 +153,8 @@
         whenever(deviceItemInteractor.deviceItemUpdate).thenReturn(MutableSharedFlow())
         whenever(deviceItemInteractor.deviceItemUpdateRequest)
             .thenReturn(MutableStateFlow(Unit).asStateFlow())
+        whenever(deviceItemInteractor.showSeeAllUpdate).thenReturn(getMutableStateFlow(false))
+        whenever(bluetoothDeviceMetadataInteractor.metadataUpdate).thenReturn(MutableSharedFlow())
         whenever(mBluetoothTileDialogDelegateDelegateFactory.create(any(), anyInt(), any(), any()))
             .thenReturn(bluetoothTileDialogDelegate)
         whenever(bluetoothTileDialogDelegate.createDialog()).thenReturn(sysuiDialog)
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 74bc928..681ea75 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
@@ -59,6 +59,7 @@
     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
@@ -100,6 +101,15 @@
                 iconWithDescription = null,
                 background = null
             )
+        connectedAudioSharingMediaDeviceItem =
+            DeviceItem(
+                type = DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE,
+                cachedBluetoothDevice = cachedBluetoothDevice,
+                deviceName = DEVICE_NAME,
+                connectionSummary = DEVICE_CONNECTION_SUMMARY,
+                iconWithDescription = null,
+                background = null
+            )
         connectedOtherDeviceItem =
             DeviceItem(
                 type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE,
@@ -186,6 +196,21 @@
     }
 
     @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 {
@@ -415,7 +440,7 @@
     }
 
     @Test
-    fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() {
+    fun testOnClick_hasTwoConnectedLeDevice_clickedActiveLe_shouldLaunchSettings() {
         with(kosmos) {
             testScope.runTest {
                 whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice)
@@ -438,7 +463,7 @@
                     if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2
                 }
 
-                actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog)
+                actionInteractorImpl.onClick(activeMediaDeviceItem, dialog)
                 verify(activityStarter)
                     .postStartActivityDismissingKeyguard(
                         ArgumentMatchers.any(),
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 a27ccc6..ef441c1 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
@@ -17,18 +17,24 @@
 package com.android.systemui.bluetooth.qsdialog
 
 import android.bluetooth.BluetoothDevice
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.media.AudioManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
+import android.util.Pair
 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.flags.Flags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
 import com.google.common.truth.Truth.assertThat
+import org.junit.After
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -37,6 +43,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -44,9 +51,11 @@
 class DeviceItemFactoryTest : SysuiTestCase() {
     @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
+    private lateinit var mockitoSession: StaticMockitoSession
     @Mock private lateinit var cachedDevice: CachedBluetoothDevice
     @Mock private lateinit var bluetoothDevice: BluetoothDevice
-    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var localBluetoothManager: LocalBluetoothManager
+    @Mock private lateinit var drawable: Drawable
 
     private val availableMediaDeviceItemFactory = AvailableMediaDeviceItemFactory()
     private val connectedDeviceItemFactory = ConnectedDeviceItemFactory()
@@ -56,16 +65,21 @@
 
     @Before
     fun setup() {
-        `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
-        `when`(cachedDevice.address).thenReturn(DEVICE_ADDRESS)
-        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
-        `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+        mockitoSession =
+            mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking()
+    }
 
-        context.setMockPackageManager(packageManager)
+    @After
+    fun tearDown() {
+        mockitoSession.finishMocking()
     }
 
     @Test
     fun testAvailableMediaDeviceItemFactory_createFromCachedDevice() {
+        `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+        `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+        `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any()))
+            .thenReturn(Pair.create(drawable, ""))
         val deviceItem = availableMediaDeviceItemFactory.create(context, cachedDevice)
 
         assertDeviceItem(deviceItem, DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE)
@@ -73,6 +87,10 @@
 
     @Test
     fun testConnectedDeviceItemFactory_createFromCachedDevice() {
+        `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+        `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+        `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any()))
+            .thenReturn(Pair.create(drawable, ""))
         val deviceItem = connectedDeviceItemFactory.create(context, cachedDevice)
 
         assertDeviceItem(deviceItem, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
@@ -80,6 +98,10 @@
 
     @Test
     fun testSavedDeviceItemFactory_createFromCachedDevice() {
+        `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+        `when`(cachedDevice.connectionSummary).thenReturn(CONNECTION_SUMMARY)
+        `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any()))
+            .thenReturn(Pair.create(drawable, ""))
         val deviceItem = savedDeviceItemFactory.create(context, cachedDevice)
 
         assertDeviceItem(deviceItem, DeviceItemType.SAVED_BLUETOOTH_DEVICE)
@@ -87,6 +109,90 @@
     }
 
     @Test
+    fun testAvailableAudioSharingMediaDeviceItemFactory_createFromCachedDevice() {
+        `when`(cachedDevice.name).thenReturn(DEVICE_NAME)
+        `when`(BluetoothUtils.getBtClassDrawableWithDescription(any(), any()))
+            .thenReturn(Pair.create(drawable, ""))
+        val deviceItem =
+            AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
+                .create(context, cachedDevice)
+
+        assertThat(deviceItem).isNotNull()
+        assertThat(deviceItem.type)
+            .isEqualTo(DeviceItemType.AVAILABLE_AUDIO_SHARING_MEDIA_BLUETOOTH_DEVICE)
+        assertThat(deviceItem.cachedBluetoothDevice).isEqualTo(cachedDevice)
+        assertThat(deviceItem.deviceName).isEqualTo(DEVICE_NAME)
+        assertThat(deviceItem.isActive).isFalse()
+        assertThat(deviceItem.connectionSummary)
+            .isEqualTo(
+                context.getString(
+                    R.string.quick_settings_bluetooth_device_audio_sharing_or_switch_active
+                )
+            )
+    }
+
+    @Test
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_flagOff_returnsFalse() {
+         // Flags.FLAG_ENABLE_LE_AUDIO_SHARING off or the device doesn't support broadcast
+         // source or assistant.
+        `when`(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false)
+
+        assertThat(
+                AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
+                    .isFilterMatched(context, cachedDevice, audioManager)
+            )
+            .isFalse()
+    }
+
+    @Test
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isActiveDevice_returnsFalse() {
+         // 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)
+
+        assertThat(
+                AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
+                    .isFilterMatched(context, cachedDevice, audioManager)
+            )
+            .isFalse()
+    }
+
+    @Test
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_isNotAvailable_returnsFalse() {
+         // 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)
+        `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any()))
+            .thenReturn(false)
+
+        assertThat(
+                AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
+                    .isFilterMatched(context, cachedDevice, audioManager)
+            )
+            .isFalse()
+    }
+
+    @Test
+    fun testAvailableAudioSharingMediaDeviceItemFactory_isFilterMatched_returnsTrue() {
+         // 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)
+        `when`(BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice(any(), any()))
+            .thenReturn(true)
+
+        assertThat(
+                AvailableAudioSharingMediaDeviceItemFactory(localBluetoothManager)
+                    .isFilterMatched(context, cachedDevice, audioManager)
+            )
+            .isTrue()
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_bondedAndNotConnected_returnsTrue() {
         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
@@ -110,7 +216,6 @@
     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_notBonded_returnsFalse() {
         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
-        `when`(cachedDevice.isConnected).thenReturn(false)
 
         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isFalse()
@@ -119,12 +224,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenReturn(ApplicationInfo())
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(cachedDevice.isConnected).thenReturn(false)
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
 
         assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isFalse()
@@ -132,7 +233,9 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
+    fun testSavedFactory_isFilterMatched_notExclusiveManaged_returnsTrue() {
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
         `when`(cachedDevice.isConnected).thenReturn(false)
 
@@ -142,45 +245,9 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenReturn(ApplicationInfo().also { it.enabled = false })
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(cachedDevice.isConnected).thenReturn(false)
-
-        assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isTrue()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenThrow(PackageManager.NameNotFoundException("Test!"))
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(cachedDevice.isConnected).thenReturn(false)
-
-        assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isTrue()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testSavedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
-        `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
-        `when`(cachedDevice.isConnected).thenReturn(false)
-
-        assertThat(savedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isFalse()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testSavedFactory_isFilterMatched_notExclusivelyManaged_connected_returnsFalse() {
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
         `when`(cachedDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
         `when`(cachedDevice.isConnected).thenReturn(true)
 
@@ -191,9 +258,7 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_bondedAndConnected_returnsTrue() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
+        `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true)
 
         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isTrue()
@@ -202,21 +267,6 @@
     @Test
     @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_notConnected_returnsFalse() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(false)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
-
-        assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isFalse()
-    }
-
-    @Test
-    @DisableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_notBonded_returnsFalse() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
-
         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isFalse()
     }
@@ -224,13 +274,8 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_exclusivelyManaged_returnsFalse() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenReturn(ApplicationInfo())
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(true)
 
         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isFalse()
@@ -239,9 +284,9 @@
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_noExclusiveManager_returnsTrue() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
+        `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(true)
 
         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isTrue()
@@ -249,51 +294,10 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_exclusiveManagerNotEnabled_returnsTrue() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenReturn(ApplicationInfo().also { it.enabled = false })
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
-
-        assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isTrue()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_exclusiveManagerNotInstalled_returnsTrue() {
-        `when`(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER))
-            .thenReturn(TEST_EXCLUSIVE_MANAGER.toByteArray())
-        `when`(packageManager.getApplicationInfo(TEST_EXCLUSIVE_MANAGER, 0))
-            .thenThrow(PackageManager.NameNotFoundException("Test!"))
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
-
-        assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isTrue()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
-    fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notBonded_returnsFalse() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
-        `when`(bluetoothDevice.isConnected).thenReturn(true)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
-
-        assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
-            .isFalse()
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
     fun testConnectedFactory_isFilterMatched_notExclusivelyManaged_notConnected_returnsFalse() {
-        `when`(bluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
-        `when`(bluetoothDevice.isConnected).thenReturn(false)
-        audioManager.setMode(AudioManager.MODE_NORMAL)
+        `when`(cachedDevice.device).thenReturn(bluetoothDevice)
+        `when`(BluetoothUtils.isExclusivelyManagedBluetoothDevice(any(), any())).thenReturn(false)
+        `when`(BluetoothUtils.isConnectedBluetoothDevice(any(), any())).thenReturn(false)
 
         assertThat(connectedDeviceItemFactory.isFilterMatched(context, cachedDevice, audioManager))
             .isFalse()
@@ -310,7 +314,5 @@
     companion object {
         const val DEVICE_NAME = "DeviceName"
         const val CONNECTION_SUMMARY = "ConnectionSummary"
-        private const val TEST_EXCLUSIVE_MANAGER = "com.test.manager"
-        private const val DEVICE_ADDRESS = "04:52:C7:0B:D8:3C"
     }
 }
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 7f7abaf..194590c 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
@@ -113,9 +113,11 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(emptyList<DeviceItem>())
+            assertThat(latestShowSeeAll).isFalse()
         }
     }
 
@@ -128,9 +130,11 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(emptyList<DeviceItem>())
+            assertThat(latestShowSeeAll).isFalse()
         }
     }
 
@@ -143,9 +147,11 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem1))
+            assertThat(latestShowSeeAll).isFalse()
         }
     }
 
@@ -158,9 +164,11 @@
             )
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2))
+            assertThat(latestShowSeeAll).isFalse()
         }
     }
 
@@ -184,9 +192,11 @@
             `when`(deviceItem2.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE)
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
+            assertThat(latestShowSeeAll).isFalse()
         }
     }
 
@@ -207,9 +217,30 @@
             `when`(deviceItem2.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE)
 
             val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
             interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
 
             assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem1))
+            assertThat(latestShowSeeAll).isFalse()
+        }
+    }
+
+    @Test
+    fun testUpdateDeviceItems_showMaxDeviceItems_showSeeAll() {
+        testScope.runTest {
+            `when`(bluetoothTileDialogRepository.cachedDevices)
+                .thenReturn(listOf(cachedDevice2, cachedDevice2, cachedDevice2, cachedDevice2))
+            `when`(adapter.mostRecentlyConnectedDevices).thenReturn(null)
+            interactor.setDeviceItemFactoryListForTesting(
+                listOf(createFactory({ true }, deviceItem2))
+            )
+
+            val latest by collectLastValue(interactor.deviceItemUpdate)
+            val latestShowSeeAll by collectLastValue(interactor.showSeeAllUpdate)
+            interactor.updateDeviceItems(mContext, DeviceFetchTrigger.FIRST_LOAD)
+
+            assertThat(latest).isEqualTo(listOf(deviceItem2, deviceItem2, deviceItem2))
+            assertThat(latestShowSeeAll).isTrue()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
index a4936e6..fd550b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/BouncerContentTest.kt
@@ -33,9 +33,10 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
 import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
-import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
+import com.android.systemui.bouncer.ui.viewmodel.bouncerSceneContentViewModelFactory
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.motion.createSysUiComposeMotionTestRule
 import com.android.systemui.scene.domain.startable.sceneContainerStartable
 import com.android.systemui.testKosmos
@@ -81,7 +82,10 @@
     private fun BouncerContentUnderTest() {
         PlatformTheme {
             BouncerContent(
-                viewModel = kosmos.bouncerViewModel,
+                viewModel =
+                    rememberViewModel("test") {
+                        kosmos.bouncerSceneContentViewModelFactory.create()
+                    },
                 layout = BouncerSceneLayout.BESIDE_USER_SWITCHER,
                 modifier = Modifier.fillMaxSize().testTag("BouncerContent"),
                 dialogFactory = bouncerDialogFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
index 2948c02..4b61a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/composable/PatternBouncerTest.kt
@@ -24,14 +24,14 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
-import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.bouncer.ui.viewmodel.patternBouncerViewModelFactory
+import com.android.systemui.lifecycle.activateIn
 import com.android.systemui.motion.createSysUiComposeMotionTestRule
 import com.android.systemui.testKosmos
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.takeWhile
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,15 +51,15 @@
 
     @get:Rule val motionTestRule = createSysUiComposeMotionTestRule(kosmos)
 
-    private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
-    private val viewModel by lazy {
-        PatternBouncerViewModel(
-            applicationContext = context,
-            viewModelScope = kosmos.testScope.backgroundScope,
-            interactor = bouncerInteractor,
+    private val viewModel =
+        kosmos.patternBouncerViewModelFactory.create(
             isInputEnabled = MutableStateFlow(true).asStateFlow(),
             onIntentionalUserInput = {},
         )
+
+    @Before
+    fun setUp() {
+        viewModel.activateIn(motionTestRule.toolkit.testScope)
     }
 
     @Composable
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 034bab8..57b397c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -23,11 +23,13 @@
 import android.view.WindowMetrics
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -40,12 +42,12 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
 import org.mockito.Mockito.eq
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -60,6 +62,7 @@
     @Mock private lateinit var windowManager: WindowManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
     @Mock private lateinit var windowMetrics: WindowMetrics
+    @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
     private val systemClock = FakeSystemClock()
 
     @Before
@@ -68,7 +71,9 @@
         `when`(featureFlags.isEnabled(Flags.CHARGING_RIPPLE)).thenReturn(true)
         controller = WiredChargingRippleController(
                 commandRegistry, batteryController, configurationController,
-                featureFlags, context, windowManager, systemClock, uiEventLogger)
+                featureFlags, context, windowManager,
+                ViewCaptureAwareWindowManager(windowManager,
+                        lazyViewCapture, isViewCaptureEnabled = false), systemClock, uiEventLogger)
         rippleView.setupShader()
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
         controller.registerCallbacks()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 88bfcf0..a1bea06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -39,8 +39,6 @@
 import com.android.internal.logging.testing.FakeMetricsLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -84,7 +82,6 @@
     private AccessibilityManager mAccessibilityManager;
 
     private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-    private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
 
     private final FalsingClassifier.Result mFalsedResult =
             FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
@@ -110,7 +107,7 @@
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
                 mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
-                mAccessibilityManager, false, mFakeFeatureFlags);
+                mAccessibilityManager, false);
 
 
         ArgumentCaptor<GestureFinalizedListener> gestureCompleteListenerCaptor =
@@ -120,7 +117,6 @@
                 gestureCompleteListenerCaptor.capture());
 
         mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
-        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
deleted file mode 100644
index ec8cc4d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ /dev/null
@@ -1,229 +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.classifier;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.view.MotionEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-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;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BrightLineFalsingManagerTest extends SysuiTestCase {
-    private BrightLineFalsingManager mBrightLineFalsingManager;
-    @Mock
-    private FalsingDataProvider mFalsingDataProvider;
-    private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
-    private final Set<FalsingClassifier> mClassifiers = new HashSet<>();
-    @Mock
-    private SingleTapClassifier mSingleTapClassifier;
-    @Mock
-    private LongTapClassifier mLongTapClassifier;
-    @Mock
-    private DoubleTapClassifier mDoubleTapClassifier;
-    @Mock
-    private FalsingClassifier mClassifierA;
-    private final List<MotionEvent> mMotionEventList = new ArrayList<>();
-    @Mock
-    private HistoryTracker mHistoryTracker;
-    @Mock
-    private KeyguardStateController mKeyguardStateController;
-    @Mock
-    private AccessibilityManager mAccessibilityManager;
-    @Captor
-    private ArgumentCaptor<FalsingDataProvider.SessionListener> mSessionListenerArgumentCaptor;
-    @Captor
-    private ArgumentCaptor<HistoryTracker.BeliefListener> mBeliefListenerArgumentCaptor;
-
-    private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
-    private final FalsingClassifier.Result mFalsedResult =
-            FalsingClassifier.Result.falsed(1, getClass().getSimpleName(), "");
-    private final FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        when(mClassifierA.classifyGesture(anyInt(), anyDouble(), anyDouble()))
-                .thenReturn(mFalsedResult);
-        when(mSingleTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
-        when(mLongTapClassifier.isTap(any(List.class), anyDouble())).thenReturn(mFalsedResult);
-        when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
-                .thenReturn(mFalsedResult);
-        mClassifiers.add(mClassifierA);
-        when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
-        when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
-        mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
-                mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
-                mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
-                mAccessibilityManager, false, mFakeFeatureFlags);
-        mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
-    }
-
-    @Test
-    public void testA11yDisablesGesture() {
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
-    }
-
-    @Test
-    public void testA11yDisablesTap() {
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
-    }
-
-
-    @Test
-    public void testA11yDisablesLongTap() {
-        assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isTrue();
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseLongTap(1)).isFalse();
-    }
-
-    @Test
-    public void testA11yDisablesDoubleTap() {
-        assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue();
-        when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse();
-    }
-
-    @Test
-    public void testIsProxNear_noProxEvents_defaultsToFalse() {
-        assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
-    }
-
-    @Test
-    public void testIsProxNear_receivesNearEvent() {
-        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
-            @Override
-            public boolean getCovered() {
-                return true;
-            }
-
-            @Override
-            public long getTimestampNs() {
-                return 0;
-            }
-        });
-        assertThat(mBrightLineFalsingManager.isProximityNear()).isTrue();
-    }
-
-    @Test
-    public void testIsProxNear_receivesNearAndThenFarEvent() {
-        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
-            @Override
-            public boolean getCovered() {
-                return true;
-            }
-
-            @Override
-            public long getTimestampNs() {
-                return 0;
-            }
-        });
-        mBrightLineFalsingManager.onProximityEvent(new FalsingManager.ProximityEvent() {
-            @Override
-            public boolean getCovered() {
-                return false;
-            }
-
-            @Override
-            public long getTimestampNs() {
-                return 5;
-            }
-        });
-        assertThat(mBrightLineFalsingManager.isProximityNear()).isFalse();
-    }
-
-    @Test
-    public void testA11yAction() {
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isTrue();
-        when(mFalsingDataProvider.isA11yAction()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(1)).isFalse();
-    }
-
-    @Test
-    public void testSkipUnfolded() {
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
-        when(mFalsingDataProvider.isUnfolded()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
-    }
-
-    @Test
-    public void testTrackpadGesture() {
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
-        when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
-    }
-
-    @Test
-    public void testAddAndRemoveFalsingBeliefListener() {
-        verify(mHistoryTracker, never()).addBeliefListener(any());
-
-        // Session started
-        final FalsingDataProvider.SessionListener sessionListener = captureSessionListener();
-        sessionListener.onSessionStarted();
-
-        // Verify belief listener added when session started
-        verify(mHistoryTracker).addBeliefListener(mBeliefListenerArgumentCaptor.capture());
-        verify(mHistoryTracker, never()).removeBeliefListener(any());
-
-        // Session ended
-        sessionListener.onSessionEnded();
-
-        // Verify belief listener removed when session ended
-        verify(mHistoryTracker).removeBeliefListener(mBeliefListenerArgumentCaptor.getValue());
-    }
-
-    private FalsingDataProvider.SessionListener captureSessionListener() {
-        verify(mFalsingDataProvider).addSessionListener(mSessionListenerArgumentCaptor.capture());
-        return mSessionListenerArgumentCaptor.getValue();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
index c425e82..5fc1971 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
+import static com.android.systemui.Flags.FLAG_CLIPBOARD_SHARED_TRANSITIONS;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_DISMISS_TAPPED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_EXPANDED_FROM_MINIMIZED;
@@ -26,7 +27,6 @@
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED;
 import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT;
-import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -47,6 +47,8 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.PersistableBundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.view.WindowInsets;
 import android.view.textclassifier.TextLinks;
 
@@ -130,7 +132,6 @@
                 new ClipData.Item("Test Item"));
 
         mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests
-        mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, true); // turned off for old tests
     }
 
     /**
@@ -299,8 +300,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
     public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() {
-        mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false);
         initController();
         mOverlayController.setClipData(mSampleClipData, "");
 
@@ -311,6 +312,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
     public void test_viewCallbacks_onShareTapped() {
         initController();
         mOverlayController.setClipData(mSampleClipData, "");
@@ -324,8 +326,8 @@
     }
 
     @Test
+    @DisableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
     public void test_viewCallbacks_onDismissTapped_sharedTransitionsOff() {
-        mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false);
         initController();
         mOverlayController.setClipData(mSampleClipData, "");
 
@@ -336,6 +338,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
     public void test_viewCallbacks_onDismissTapped() {
         initController();
 
@@ -350,7 +353,6 @@
 
     @Test
     public void test_multipleDismissals_dismissesOnce_sharedTransitionsOff() {
-        mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false);
         initController();
         mCallbacks.onSwipeDismissInitiated(mAnimator);
         mCallbacks.onDismissButtonTapped();
@@ -362,6 +364,7 @@
     }
 
     @Test
+    @EnableFlags(FLAG_CLIPBOARD_SHARED_TRANSITIONS)
     public void test_multipleDismissals_dismissesOnce() {
         initController();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
index e60848b..6e9b24f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt
@@ -123,19 +123,19 @@
                 FakeWidgetMetadata(
                     widgetId = 11,
                     componentName = "com.android.fakePackage1/fakeWidget1",
-                    rank = 3,
+                    rank = 0,
                     userSerialNumber = 0,
                 ),
                 FakeWidgetMetadata(
                     widgetId = 12,
                     componentName = "com.android.fakePackage2/fakeWidget2",
-                    rank = 2,
+                    rank = 1,
                     userSerialNumber = 0,
                 ),
                 FakeWidgetMetadata(
                     widgetId = 13,
                     componentName = "com.android.fakePackage3/fakeWidget3",
-                    rank = 1,
+                    rank = 2,
                     userSerialNumber = 10,
                 ),
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
deleted file mode 100644
index 983a435..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt
+++ /dev/null
@@ -1,171 +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.data.backup
-
-import androidx.room.Room
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.db.CommunalDatabase
-import com.android.systemui.communal.data.db.CommunalWidgetDao
-import com.android.systemui.communal.nano.CommunalHubState
-import com.android.systemui.lifecycle.InstantTaskExecutorRule
-import com.google.common.truth.Correspondence
-import com.google.common.truth.Truth.assertThat
-import java.io.FileNotFoundException
-import java.nio.charset.Charset
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalBackupUtilsTest : SysuiTestCase() {
-    @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule()
-
-    private lateinit var database: CommunalDatabase
-    private lateinit var dao: CommunalWidgetDao
-    private lateinit var underTest: CommunalBackupUtils
-
-    @Before
-    fun setup() {
-        database =
-            Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
-                .allowMainThreadQueries()
-                .build()
-        CommunalDatabase.setInstance(database)
-
-        dao = database.communalWidgetDao()
-        underTest = CommunalBackupUtils(context)
-    }
-
-    @After
-    fun teardown() {
-        database.close()
-        underTest.clear()
-    }
-
-    @Test
-    fun getCommunalHubState_returnsExpectedWidgets() {
-        // Set up database
-        val expectedWidgets =
-            listOf(
-                FakeWidgetMetadata(
-                    widgetId = 11,
-                    componentName = "com.android.fakePackage1/fakeWidget1",
-                    rank = 3,
-                    userSerialNumber = 0,
-                ),
-                FakeWidgetMetadata(
-                    widgetId = 12,
-                    componentName = "com.android.fakePackage2/fakeWidget2",
-                    rank = 2,
-                    userSerialNumber = 0,
-                ),
-                FakeWidgetMetadata(
-                    widgetId = 13,
-                    componentName = "com.android.fakePackage3/fakeWidget3",
-                    rank = 1,
-                    userSerialNumber = 10,
-                ),
-            )
-        expectedWidgets.forEach {
-            dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber)
-        }
-
-        // Get communal hub state
-        val state = underTest.getCommunalHubState()
-        val actualWidgets = state.widgets.toList()
-
-        // Verify the state contains widgets as expected
-        assertThat(actualWidgets)
-            .comparingElementsUsing(represents)
-            .containsExactlyElementsIn(expectedWidgets)
-    }
-
-    @Test
-    fun write_existingContentIsOverwritten() {
-        // Write old data
-        val dataToWrite = "I am old data. Erase me."
-        underTest.writeBytesToDisk(dataToWrite.toByteArray(Charset.defaultCharset()))
-
-        // Verify old data written
-        var dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset())
-        assertThat(dataRead).isEqualTo(dataToWrite)
-
-        // Write new data
-        val newDataToWrite = "I am new data."
-        underTest.writeBytesToDisk(newDataToWrite.toByteArray(Charset.defaultCharset()))
-
-        // Verify new data overwrites old
-        dataRead = underTest.readBytesFromDisk().toString(Charset.defaultCharset())
-        assertThat(dataRead).isEqualTo(newDataToWrite)
-    }
-
-    @Test(expected = FileNotFoundException::class)
-    fun clear_returnsTrueWhenFileDeleted() {
-        // Write bytes to disk
-        underTest.writeBytesToDisk(byteArrayOf(1, 2, 3))
-
-        assertThat(underTest.clear()).isTrue()
-
-        // Verify a read after that throws a FileNotFoundException
-        underTest.readBytesFromDisk()
-    }
-
-    @Test
-    fun clear_returnsFalseWhenFileDoesNotExist() {
-        assertThat(underTest.clear()).isFalse()
-    }
-
-    @Test
-    fun fileExists() {
-        assertThat(underTest.fileExists()).isFalse()
-
-        underTest.writeBytesToDisk(byteArrayOf(1, 2, 3))
-        assertThat(underTest.fileExists()).isTrue()
-
-        underTest.clear()
-        assertThat(underTest.fileExists()).isFalse()
-    }
-
-    data class FakeWidgetMetadata(
-        val widgetId: Int,
-        val componentName: String,
-        val rank: Int,
-        val userSerialNumber: Int,
-    )
-
-    companion object {
-        /**
-         * A comparator for whether a [CommunalHubState.CommunalWidgetItem] represents a
-         * [FakeWidgetMetadata]
-         */
-        val represents: Correspondence<CommunalHubState.CommunalWidgetItem, FakeWidgetMetadata> =
-            Correspondence.from(
-                { actual, expected ->
-                    actual?.widgetId == expected?.widgetId &&
-                        actual?.componentName == expected?.componentName &&
-                        actual?.rank == expected?.rank &&
-                        actual?.userSerialNumber == expected?.userSerialNumber
-                },
-                "represents",
-            )
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
index eb0ab78..ad25502 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt
@@ -72,6 +72,82 @@
         databaseV2.verifyWidgetsV2(fakeWidgetsV1.map { it.getV2() })
     }
 
+    @Test
+    fun migrate2To3_noGapBetweenRanks_ranksReversed() {
+        // Create a communal database in version 2
+        val databaseV2 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 2)
+
+        // Populate some fake data
+        val fakeRanks =
+            listOf(
+                FakeCommunalItemRank(3),
+                FakeCommunalItemRank(2),
+                FakeCommunalItemRank(1),
+                FakeCommunalItemRank(0),
+            )
+        databaseV2.insertRanks(fakeRanks)
+
+        // Verify fake ranks populated
+        databaseV2.verifyRanksInOrder(fakeRanks)
+
+        // Run migration and get database V3
+        val databaseV3 =
+            migrationTestHelper.runMigrationsAndValidate(
+                name = DATABASE_NAME,
+                version = 3,
+                validateDroppedTables = false,
+                CommunalDatabase.MIGRATION_2_3,
+            )
+
+        // Verify ranks are reversed
+        databaseV3.verifyRanksInOrder(
+            listOf(
+                FakeCommunalItemRank(0),
+                FakeCommunalItemRank(1),
+                FakeCommunalItemRank(2),
+                FakeCommunalItemRank(3),
+            )
+        )
+    }
+
+    @Test
+    fun migrate2To3_withGapBetweenRanks_ranksReversed() {
+        // Create a communal database in version 2
+        val databaseV2 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 2)
+
+        // Populate some fake data with gaps between ranks
+        val fakeRanks =
+            listOf(
+                FakeCommunalItemRank(9),
+                FakeCommunalItemRank(7),
+                FakeCommunalItemRank(2),
+                FakeCommunalItemRank(0),
+            )
+        databaseV2.insertRanks(fakeRanks)
+
+        // Verify fake ranks populated
+        databaseV2.verifyRanksInOrder(fakeRanks)
+
+        // Run migration and get database V3
+        val databaseV3 =
+            migrationTestHelper.runMigrationsAndValidate(
+                name = DATABASE_NAME,
+                version = 3,
+                validateDroppedTables = false,
+                CommunalDatabase.MIGRATION_2_3,
+            )
+
+        // Verify ranks are reversed
+        databaseV3.verifyRanksInOrder(
+            listOf(
+                FakeCommunalItemRank(0),
+                FakeCommunalItemRank(2),
+                FakeCommunalItemRank(7),
+                FakeCommunalItemRank(9),
+            )
+        )
+    }
+
     private fun SupportSQLiteDatabase.insertWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) {
         widgets.forEach { widget ->
             execSQL(
@@ -117,6 +193,25 @@
         assertThat(cursor.isAfterLast).isTrue()
     }
 
+    private fun SupportSQLiteDatabase.insertRanks(ranks: List<FakeCommunalItemRank>) {
+        ranks.forEach { rank ->
+            execSQL("INSERT INTO communal_item_rank_table(rank) VALUES(${rank.rank})")
+        }
+    }
+
+    private fun SupportSQLiteDatabase.verifyRanksInOrder(ranks: List<FakeCommunalItemRank>) {
+        val cursor = query("SELECT * FROM communal_item_rank_table ORDER BY uid")
+        assertThat(cursor.moveToFirst()).isTrue()
+
+        ranks.forEach { rank ->
+            assertThat(cursor.getInt(cursor.getColumnIndex("rank"))).isEqualTo(rank.rank)
+            cursor.moveToNext()
+        }
+
+        // Verify there is no more columns
+        assertThat(cursor.isAfterLast).isTrue()
+    }
+
     /**
      * Returns the expected data after migration from V1 to V2, which is simply that the new user
      * serial number field is now set to [CommunalWidgetItem.USER_SERIAL_NUMBER_UNDEFINED].
@@ -143,6 +238,10 @@
         val userSerialNumber: Int,
     )
 
+    private data class FakeCommunalItemRank(
+        val rank: Int,
+    )
+
     companion object {
         private const val DATABASE_NAME = "communal_db"
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
deleted file mode 100644
index d670508..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.data.db
-
-import android.content.ComponentName
-import androidx.room.Room
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.nano.CommunalHubState
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.lifecycle.InstantTaskExecutorRule
-import com.google.common.truth.Truth.assertThat
-import java.io.IOException
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
-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.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class CommunalWidgetDaoTest : SysuiTestCase() {
-    @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule()
-
-    private lateinit var db: CommunalDatabase
-    private lateinit var communalWidgetDao: CommunalWidgetDao
-
-    private val testDispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
-    @Before
-    @Throws(IOException::class)
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        db =
-            Room.inMemoryDatabaseBuilder(context, CommunalDatabase::class.java)
-                .allowMainThreadQueries()
-                .build()
-        communalWidgetDao = db.communalWidgetDao()
-    }
-
-    @After
-    @Throws(IOException::class)
-    fun teardown() {
-        db.close()
-    }
-
-    @Test
-    fun addWidget_readValueInDb() =
-        testScope.runTest {
-            val (widgetId, provider, priority, userSerialNumber) = widgetInfo1
-            communalWidgetDao.addWidget(
-                widgetId = widgetId,
-                provider = provider,
-                priority = priority,
-                userSerialNumber = userSerialNumber,
-            )
-            val entry = communalWidgetDao.getWidgetByIdNow(id = 1)
-            assertThat(entry).isEqualTo(communalWidgetItemEntry1)
-        }
-
-    @Test
-    fun deleteWidget_notInDb_returnsFalse() =
-        testScope.runTest {
-            val (widgetId, provider, priority, userSerialNumber) = widgetInfo1
-            communalWidgetDao.addWidget(
-                widgetId = widgetId,
-                provider = provider,
-                priority = priority,
-                userSerialNumber = userSerialNumber,
-            )
-            assertThat(communalWidgetDao.deleteWidgetById(widgetId = 123)).isFalse()
-        }
-
-    @Test
-    fun addWidget_emitsActiveWidgetsInDb(): Unit =
-        testScope.runTest {
-            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
-            val widgets = collectLastValue(communalWidgetDao.getWidgets())
-            widgetsToAdd.forEach {
-                val (widgetId, provider, priority, userSerialNumber) = it
-                communalWidgetDao.addWidget(
-                    widgetId = widgetId,
-                    provider = provider,
-                    priority = priority,
-                    userSerialNumber = userSerialNumber
-                )
-            }
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry1,
-                    communalWidgetItemEntry1,
-                    communalItemRankEntry2,
-                    communalWidgetItemEntry2
-                )
-        }
-
-    @Test
-    fun deleteWidget_emitsActiveWidgetsInDb() =
-        testScope.runTest {
-            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
-            val widgets = collectLastValue(communalWidgetDao.getWidgets())
-
-            widgetsToAdd.forEach {
-                val (widgetId, provider, priority, userSerialNumber) = it
-                communalWidgetDao.addWidget(
-                    widgetId = widgetId,
-                    provider = provider,
-                    priority = priority,
-                    userSerialNumber = userSerialNumber,
-                )
-            }
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry1,
-                    communalWidgetItemEntry1,
-                    communalItemRankEntry2,
-                    communalWidgetItemEntry2
-                )
-
-            communalWidgetDao.deleteWidgetById(communalWidgetItemEntry1.widgetId)
-            assertThat(widgets()).containsExactly(communalItemRankEntry2, communalWidgetItemEntry2)
-        }
-
-    @Test
-    fun reorderWidget_emitsWidgetsInNewOrder() =
-        testScope.runTest {
-            val widgetsToAdd = listOf(widgetInfo1, widgetInfo2)
-            val widgets = collectLastValue(communalWidgetDao.getWidgets())
-
-            widgetsToAdd.forEach {
-                val (widgetId, provider, priority, userSerialNumber) = it
-                communalWidgetDao.addWidget(
-                    widgetId = widgetId,
-                    provider = provider,
-                    priority = priority,
-                    userSerialNumber = userSerialNumber,
-                )
-            }
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry2,
-                    communalWidgetItemEntry2,
-                    communalItemRankEntry1,
-                    communalWidgetItemEntry1,
-                )
-                .inOrder()
-
-            // swapped priorities
-            val widgetIdsToPriorityMap = mapOf(widgetInfo1.widgetId to 2, widgetInfo2.widgetId to 1)
-            communalWidgetDao.updateWidgetOrder(widgetIdsToPriorityMap)
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry1.copy(rank = 2),
-                    communalWidgetItemEntry1,
-                    communalItemRankEntry2.copy(rank = 1),
-                    communalWidgetItemEntry2
-                )
-                .inOrder()
-        }
-
-    @Test
-    fun addNewWidgetWithReorder_emitsWidgetsInNewOrder() =
-        testScope.runTest {
-            val existingWidgets = listOf(widgetInfo1, widgetInfo2)
-            val widgets = collectLastValue(communalWidgetDao.getWidgets())
-
-            existingWidgets.forEach {
-                val (widgetId, provider, priority, userSerialNumber) = it
-                communalWidgetDao.addWidget(
-                    widgetId = widgetId,
-                    provider = provider,
-                    priority = priority,
-                    userSerialNumber = userSerialNumber,
-                )
-            }
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry2,
-                    communalWidgetItemEntry2,
-                    communalItemRankEntry1,
-                    communalWidgetItemEntry1,
-                )
-                .inOrder()
-
-            // map with no item in the middle at index 1
-            val widgetIdsToIndexMap = mapOf(widgetInfo1.widgetId to 1, widgetInfo2.widgetId to 3)
-            communalWidgetDao.updateWidgetOrder(widgetIdsToIndexMap)
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry2.copy(rank = 3),
-                    communalWidgetItemEntry2,
-                    communalItemRankEntry1.copy(rank = 1),
-                    communalWidgetItemEntry1,
-                )
-                .inOrder()
-            // add the new middle item that we left space for.
-            communalWidgetDao.addWidget(
-                widgetId = widgetInfo3.widgetId,
-                provider = widgetInfo3.provider,
-                priority = 2,
-                userSerialNumber = widgetInfo3.userSerialNumber,
-            )
-            assertThat(widgets())
-                .containsExactly(
-                    communalItemRankEntry2.copy(rank = 3),
-                    communalWidgetItemEntry2,
-                    communalItemRankEntry3.copy(rank = 2),
-                    communalWidgetItemEntry3,
-                    communalItemRankEntry1.copy(rank = 1),
-                    communalWidgetItemEntry1,
-                )
-                .inOrder()
-        }
-
-    @Test
-    fun restoreCommunalHubState() =
-        testScope.runTest {
-            // Set up db
-            listOf(widgetInfo1, widgetInfo2, widgetInfo3).forEach { addWidget(it) }
-
-            // Restore db to fake state
-            communalWidgetDao.restoreCommunalHubState(fakeState)
-
-            // Verify db matches new state
-            val expected = mutableMapOf<CommunalItemRank, CommunalWidgetItem>()
-            fakeState.widgets.forEachIndexed { index, fakeWidget ->
-                // Auto-generated uid continues after the initial 3 widgets and starts at 4
-                val uid = index + 4L
-                val rank = CommunalItemRank(uid = uid, rank = fakeWidget.rank)
-                val widget =
-                    CommunalWidgetItem(
-                        uid = uid,
-                        widgetId = fakeWidget.widgetId,
-                        componentName = fakeWidget.componentName,
-                        itemId = rank.uid,
-                        userSerialNumber = fakeWidget.userSerialNumber,
-                    )
-                expected[rank] = widget
-            }
-            val widgets by collectLastValue(communalWidgetDao.getWidgets())
-            assertThat(widgets).containsExactlyEntriesIn(expected)
-        }
-
-    private fun addWidget(metadata: FakeWidgetMetadata, priority: Int? = null) {
-        communalWidgetDao.addWidget(
-            widgetId = metadata.widgetId,
-            provider = metadata.provider,
-            priority = priority ?: metadata.priority,
-            userSerialNumber = metadata.userSerialNumber,
-        )
-    }
-
-    data class FakeWidgetMetadata(
-        val widgetId: Int,
-        val provider: ComponentName,
-        val priority: Int,
-        val userSerialNumber: Int,
-    )
-
-    companion object {
-        val widgetInfo1 =
-            FakeWidgetMetadata(
-                widgetId = 1,
-                provider = ComponentName("pk_name", "cls_name_1"),
-                priority = 1,
-                userSerialNumber = 0,
-            )
-        val widgetInfo2 =
-            FakeWidgetMetadata(
-                widgetId = 2,
-                provider = ComponentName("pk_name", "cls_name_2"),
-                priority = 2,
-                userSerialNumber = 0,
-            )
-        val widgetInfo3 =
-            FakeWidgetMetadata(
-                widgetId = 3,
-                provider = ComponentName("pk_name", "cls_name_3"),
-                priority = 3,
-                userSerialNumber = 10,
-            )
-        val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.priority)
-        val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.priority)
-        val communalItemRankEntry3 = CommunalItemRank(uid = 3L, rank = widgetInfo3.priority)
-        val communalWidgetItemEntry1 =
-            CommunalWidgetItem(
-                uid = 1L,
-                widgetId = widgetInfo1.widgetId,
-                componentName = widgetInfo1.provider.flattenToString(),
-                itemId = communalItemRankEntry1.uid,
-                userSerialNumber = widgetInfo1.userSerialNumber,
-            )
-        val communalWidgetItemEntry2 =
-            CommunalWidgetItem(
-                uid = 2L,
-                widgetId = widgetInfo2.widgetId,
-                componentName = widgetInfo2.provider.flattenToString(),
-                itemId = communalItemRankEntry2.uid,
-                userSerialNumber = widgetInfo2.userSerialNumber,
-            )
-        val communalWidgetItemEntry3 =
-            CommunalWidgetItem(
-                uid = 3L,
-                widgetId = widgetInfo3.widgetId,
-                componentName = widgetInfo3.provider.flattenToString(),
-                itemId = communalItemRankEntry3.uid,
-                userSerialNumber = widgetInfo3.userSerialNumber,
-            )
-        val fakeState =
-            CommunalHubState().apply {
-                widgets =
-                    listOf(
-                            CommunalHubState.CommunalWidgetItem().apply {
-                                widgetId = 1
-                                componentName = "pk_name/fake_widget_1"
-                                rank = 1
-                                userSerialNumber = 0
-                            },
-                            CommunalHubState.CommunalWidgetItem().apply {
-                                widgetId = 2
-                                componentName = "pk_name/fake_widget_2"
-                                rank = 2
-                                userSerialNumber = 10
-                            },
-                        )
-                        .toTypedArray()
-            }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
deleted file mode 100644
index e4f0910..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ /dev/null
@@ -1,677 +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.controls.management
-
-import android.Manifest
-import android.content.ComponentName
-import android.content.Context
-import android.content.ContextWrapper
-import android.content.Intent
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.pm.ResolveInfo
-import android.content.pm.ServiceInfo
-import android.os.Bundle
-import android.os.UserHandle
-import android.service.controls.ControlsProviderService
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.settingslib.applications.ServiceListing
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.controls.ControlsServiceInfo
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.util.ActivityTaskManagerProxy
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argThat
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.time.FakeSystemClock
-import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
-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.ArgumentMatcher
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.inOrder
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class ControlsListingControllerImplTest : SysuiTestCase() {
-
-    companion object {
-        private const val FLAGS = PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
-                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
-    }
-
-    @Mock
-    private lateinit var mockSL: ServiceListing
-    @Mock
-    private lateinit var mockCallback: ControlsListingController.ControlsListingCallback
-    @Mock
-    private lateinit var mockCallbackOther: ControlsListingController.ControlsListingCallback
-    @Mock(stubOnly = true)
-    private lateinit var userTracker: UserTracker
-    @Mock(stubOnly = true)
-    private lateinit var dumpManager: DumpManager
-    @Mock
-    private lateinit var packageManager: PackageManager
-    @Mock
-    private lateinit var featureFlags: FeatureFlags
-    @Mock
-    private lateinit var activityTaskManagerProxy: ActivityTaskManagerProxy
-
-    private var componentName = ComponentName("pkg", "class1")
-    private var activityName = ComponentName("pkg", "activity")
-
-    private val executor = FakeExecutor(FakeSystemClock())
-
-    private lateinit var controller: ControlsListingControllerImpl
-
-    private var serviceListingCallbackCaptor =
-            ArgumentCaptor.forClass(ServiceListing.Callback::class.java)
-
-    private val user = mContext.userId
-    private val otherUser = user + 1
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-
-        `when`(userTracker.userId).thenReturn(user)
-        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
-        `when`(userTracker.userContext).thenReturn(context)
-        // Return disabled by default
-        `when`(packageManager.getComponentEnabledSetting(any()))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
-        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(true)
-        mContext.setMockPackageManager(packageManager)
-
-        mContext.orCreateTestableResources
-                .addOverride(
-                        R.array.config_controlsPreferredPackages,
-                        arrayOf(componentName.packageName)
-                )
-
-        val wrapper = object : ContextWrapper(mContext) {
-            override fun createContextAsUser(user: UserHandle, flags: Int): Context {
-                return baseContext
-            }
-        }
-
-        controller = ControlsListingControllerImpl(
-                wrapper,
-                executor,
-                { mockSL },
-                userTracker,
-                activityTaskManagerProxy,
-                dumpManager,
-                featureFlags
-        )
-        verify(mockSL).addCallback(capture(serviceListingCallbackCaptor))
-    }
-
-    @After
-    fun tearDown() {
-        executor.advanceClockToLast()
-        executor.runAllReady()
-    }
-
-    @Test
-    fun testInitialStateListening() {
-        verify(mockSL).setListening(true)
-        verify(mockSL).reload()
-    }
-
-    @Test
-    fun testImmediateListingReload_doesNotCrash() {
-        val exec = Executor { it.run() }
-        val mockServiceListing = mock(ServiceListing::class.java)
-        var callback: ServiceListing.Callback? = null
-        `when`(mockServiceListing.addCallback(any<ServiceListing.Callback>())).then {
-            callback = it.getArgument(0)
-            Unit
-        }
-        `when`(mockServiceListing.reload()).then {
-            callback?.onServicesReloaded(listOf(ServiceInfo(componentName)))
-        }
-        ControlsListingControllerImpl(
-                mContext,
-                exec,
-                { mockServiceListing },
-                userTracker,
-                activityTaskManagerProxy,
-                dumpManager,
-                featureFlags
-        )
-    }
-
-    @Test
-    fun testStartsOnUser() {
-        assertEquals(user, controller.currentUserId)
-    }
-
-    @Test
-    fun testCallbackCalledWhenAdded() {
-        controller.addCallback(mockCallback)
-        executor.runAllReady()
-        verify(mockCallback).onServicesUpdated(any())
-        reset(mockCallback)
-
-        controller.addCallback(mockCallbackOther)
-        executor.runAllReady()
-        verify(mockCallbackOther).onServicesUpdated(any())
-        verify(mockCallback, never()).onServicesUpdated(any())
-    }
-
-    @Test
-    fun testCallbackGetsList() {
-        val list = listOf(ServiceInfo(componentName))
-        controller.addCallback(mockCallback)
-        controller.addCallback(mockCallbackOther)
-
-        @Suppress("unchecked_cast")
-        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
-                ArgumentCaptor.forClass(List::class.java)
-                        as ArgumentCaptor<List<ControlsServiceInfo>>
-
-        executor.runAllReady()
-        reset(mockCallback)
-        reset(mockCallbackOther)
-
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-        verify(mockCallback).onServicesUpdated(capture(captor))
-        assertEquals(1, captor.value.size)
-        assertEquals(componentName.flattenToString(), captor.value[0].key)
-
-        verify(mockCallbackOther).onServicesUpdated(capture(captor))
-        assertEquals(1, captor.value.size)
-        assertEquals(componentName.flattenToString(), captor.value[0].key)
-    }
-
-    @Test
-    fun testChangeUser() {
-        controller.changeUser(UserHandle.of(otherUser))
-        executor.runAllReady()
-        assertEquals(otherUser, controller.currentUserId)
-
-        val inOrder = inOrder(mockSL)
-        inOrder.verify(mockSL).setListening(false)
-        inOrder.verify(mockSL).addCallback(any()) // We add a callback because we replaced the SL
-        inOrder.verify(mockSL).setListening(true)
-        inOrder.verify(mockSL).reload()
-    }
-
-    @Test
-    fun testChangeUserSendsCorrectServiceUpdate() {
-        val serviceInfo = ServiceInfo(componentName)
-
-        val list = listOf(serviceInfo)
-        controller.addCallback(mockCallback)
-
-        @Suppress("unchecked_cast")
-        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
-                ArgumentCaptor.forClass(List::class.java)
-                        as ArgumentCaptor<List<ControlsServiceInfo>>
-        executor.runAllReady()
-        reset(mockCallback)
-
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-        verify(mockCallback).onServicesUpdated(capture(captor))
-        assertEquals(1, captor.value.size)
-
-        reset(mockCallback)
-        reset(mockSL)
-
-        val updatedList = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(updatedList)
-        controller.changeUser(UserHandle.of(otherUser))
-        executor.runAllReady()
-        assertEquals(otherUser, controller.currentUserId)
-
-        // this event should was triggered just before the user change, and should
-        // be ignored
-        verify(mockCallback, never()).onServicesUpdated(any())
-
-        serviceListingCallbackCaptor.value.onServicesReloaded(emptyList<ServiceInfo>())
-        executor.runAllReady()
-
-        verify(mockCallback).onServicesUpdated(capture(captor))
-        assertEquals(0, captor.value.size)
-    }
-
-    @Test
-    fun test_nullPanelActivity() {
-        val list = listOf(ServiceInfo(componentName))
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testNoActivity_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityWithoutPermission_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        setUpQueryResult(listOf(ActivityInfo(activityName)))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityPermissionNotExported_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        setUpQueryResult(listOf(
-                ActivityInfo(activityName, permission = Manifest.permission.BIND_CONTROLS)
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityDisabled_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityEnabled_correctPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityDefaultEnabled_correctPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        enabled = true,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityDefaultDisabled_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        enabled = false,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testActivityDifferentPackage_nullPanel() {
-        val serviceInfo = ServiceInfo(
-                componentName,
-                ComponentName("other_package", "cls")
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        enabled = true,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testPackageNotPreferred_correctPanel() {
-        mContext.orCreateTestableResources
-                .addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
-
-        val serviceInfo = ServiceInfo(
-                componentName,
-                activityName
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-                .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
-
-        setUpQueryResult(listOf(
-                ActivityInfo(
-                        activityName,
-                        exported = true,
-                        permission = Manifest.permission.BIND_CONTROLS
-                )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertEquals(activityName, controller.getCurrentServices()[0].panelActivity)
-    }
-
-    @Test
-    fun testListingsNotModifiedByCallback() {
-        // This test checks that if the list passed to the callback is modified, it has no effect
-        // in the resulting services
-        val list = mutableListOf<ServiceInfo>()
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        list.add(ServiceInfo(ComponentName("a", "b")))
-        executor.runAllReady()
-
-        assertTrue(controller.getCurrentServices().isEmpty())
-    }
-
-    @Test
-    fun testForceReloadQueriesPackageManager() {
-        val user = 10
-        `when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
-
-        controller.forceReload()
-        verify(packageManager).queryIntentServicesAsUser(
-                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
-                argThat(FlagsMatcher(
-                        PackageManager.GET_META_DATA.toLong() or
-                                PackageManager.GET_SERVICES.toLong() or
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
-                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
-                )),
-                eq(UserHandle.of(user))
-        )
-    }
-
-    @Test
-    fun testForceReloadUpdatesList() {
-        val resolveInfo = ResolveInfo()
-        resolveInfo.serviceInfo = ServiceInfo(componentName)
-
-        `when`(packageManager.queryIntentServicesAsUser(
-                argThat(IntentMatcherAction(ControlsProviderService.SERVICE_CONTROLS)),
-                argThat(FlagsMatcher(
-                        PackageManager.GET_META_DATA.toLong() or
-                                PackageManager.GET_SERVICES.toLong() or
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE.toLong() or
-                                PackageManager.MATCH_DIRECT_BOOT_UNAWARE.toLong()
-                )),
-                any<UserHandle>()
-        )).thenReturn(listOf(resolveInfo))
-
-        controller.forceReload()
-
-        val services = controller.getCurrentServices()
-        assertThat(services.size).isEqualTo(1)
-        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
-    }
-
-    @Test
-    fun testForceReloadCallsListeners() {
-        controller.addCallback(mockCallback)
-        executor.runAllReady()
-
-        @Suppress("unchecked_cast")
-        val captor: ArgumentCaptor<List<ControlsServiceInfo>> =
-                ArgumentCaptor.forClass(List::class.java)
-                        as ArgumentCaptor<List<ControlsServiceInfo>>
-
-        val resolveInfo = ResolveInfo()
-        resolveInfo.serviceInfo = ServiceInfo(componentName)
-
-        `when`(packageManager.queryIntentServicesAsUser(
-                any(),
-                any<PackageManager.ResolveInfoFlags>(),
-                any<UserHandle>()
-        )).thenReturn(listOf(resolveInfo))
-
-        reset(mockCallback)
-        controller.forceReload()
-
-        verify(mockCallback).onServicesUpdated(capture(captor))
-
-        val services = captor.value
-
-        assertThat(services.size).isEqualTo(1)
-        assertThat(services[0].serviceInfo.componentName).isEqualTo(componentName)
-    }
-
-    @Test
-    fun testNoPanelIfMultiWindowNotSupported() {
-        `when`(activityTaskManagerProxy.supportsMultiWindow(any())).thenReturn(false)
-
-        val serviceInfo = ServiceInfo(
-            componentName,
-            activityName
-        )
-
-        `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
-            .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-
-        setUpQueryResult(listOf(
-            ActivityInfo(
-                activityName,
-                enabled = true,
-                exported = true,
-                permission = Manifest.permission.BIND_CONTROLS
-            )
-        ))
-
-        val list = listOf(serviceInfo)
-        serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
-        executor.runAllReady()
-
-        assertNull(controller.getCurrentServices()[0].panelActivity)
-    }
-
-    private fun ServiceInfo(
-            componentName: ComponentName,
-            panelActivityComponentName: ComponentName? = null
-    ): ServiceInfo {
-        return ServiceInfo().apply {
-            packageName = componentName.packageName
-            name = componentName.className
-            panelActivityComponentName?.let {
-                metaData = Bundle().apply {
-                    putString(
-                            ControlsProviderService.META_DATA_PANEL_ACTIVITY,
-                            it.flattenToShortString()
-                    )
-                }
-            }
-        }
-    }
-
-    private fun ActivityInfo(
-        componentName: ComponentName,
-        exported: Boolean = false,
-        enabled: Boolean = true,
-        permission: String? = null
-    ): ActivityInfo {
-        return ActivityInfo().apply {
-            packageName = componentName.packageName
-            name = componentName.className
-            this.permission = permission
-            this.exported = exported
-            this.enabled = enabled
-        }
-    }
-
-    private fun setUpQueryResult(infos: List<ActivityInfo>) {
-        `when`(
-                packageManager.queryIntentActivitiesAsUser(
-                        argThat(IntentMatcherComponent(activityName)),
-                        argThat(FlagsMatcher(FLAGS)),
-                        eq(UserHandle.of(user))
-                )
-        ).thenReturn(infos.map {
-            ResolveInfo().apply { activityInfo = it }
-        })
-    }
-
-    private class IntentMatcherComponent(
-            private val componentName: ComponentName
-    ) : ArgumentMatcher<Intent> {
-        override fun matches(argument: Intent?): Boolean {
-            return argument?.component == componentName
-        }
-    }
-
-    private class IntentMatcherAction(
-            private val action: String
-    ) : ArgumentMatcher<Intent> {
-        override fun matches(argument: Intent?): Boolean {
-            return argument?.action == action
-        }
-    }
-
-    private class FlagsMatcher(
-            private val flags: Long
-    ) : ArgumentMatcher<PackageManager.ResolveInfoFlags> {
-        override fun matches(argument: PackageManager.ResolveInfoFlags?): Boolean {
-            return flags == argument?.value
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
index 0b7a3ed..40b2a08 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorTest.kt
@@ -22,6 +22,7 @@
 import android.os.PowerManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.keyguard.trustManager
 import com.android.systemui.SysuiTestCase
@@ -37,6 +38,7 @@
 import com.android.systemui.deviceentry.data.repository.fakeFaceWakeUpTriggersConfig
 import com.android.systemui.deviceentry.shared.FaceAuthUiEvent
 import com.android.systemui.deviceentry.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
@@ -52,12 +54,16 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.eq
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -113,6 +119,7 @@
                 powerInteractor,
                 fakeBiometricSettingsRepository,
                 trustManager,
+                { kosmos.sceneInteractor },
                 deviceEntryFaceAuthStatusInteractor,
             )
     }
@@ -279,6 +286,22 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun withSceneContainerEnabled_faceAuthIsRequestedWhenPrimaryBouncerIsVisible() =
+        testScope.runTest {
+            underTest.start()
+
+            kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "for-test")
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(ObservableTransitionState.Idle(Scenes.Bouncer))
+            )
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value)
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, false))
+        }
+
+    @Test
     fun faceAuthIsRequestedWhenAlternateBouncerIsVisible() =
         testScope.runTest {
             underTest.start()
@@ -339,15 +362,88 @@
         }
 
     @Test
-    fun faceAuthIsRequestedWhenQsExpansionStared() =
+    fun faceAuthIsRequestedWhenShadeExpansionStarted() =
         testScope.runTest {
             underTest.start()
 
-            underTest.onQsExpansionStared()
+            underTest.onShadeExpansionStarted()
 
             runCurrent()
             assertThat(faceAuthRepository.runningAuthRequest.value)
-                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, true))
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun faceAuthIsRequestedWhenShadeExpansionIsStarted() =
+        testScope.runTest {
+            underTest.start()
+            faceAuthRepository.canRunFaceAuth.value = true
+            kosmos.sceneInteractor.snapToScene(toScene = Scenes.Lockscreen, "for-test")
+            runCurrent()
+
+            kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test")
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = MutableStateFlow(0.2f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            )
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value)
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun faceAuthIsRequestedOnlyOnceWhenShadeExpansionStarts() =
+        testScope.runTest {
+            underTest.start()
+            faceAuthRepository.canRunFaceAuth.value = true
+            kosmos.sceneInteractor.snapToScene(toScene = Scenes.Lockscreen, "for-test")
+            runCurrent()
+
+            kosmos.sceneInteractor.changeScene(toScene = Scenes.Shade, loggingReason = "for-test")
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = MutableStateFlow(0.2f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            )
+
+            runCurrent()
+            assertThat(faceAuthRepository.runningAuthRequest.value)
+                .isEqualTo(Pair(FaceAuthUiEvent.FACE_AUTH_TRIGGERED_QS_EXPANDED, false))
+            faceAuthRepository.runningAuthRequest.value = null
+
+            // expansion progress shouldn't trigger face auth again
+            kosmos.sceneInteractor.setTransitionState(
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = MutableStateFlow(0.5f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            )
+
+            assertThat(faceAuthRepository.runningAuthRequest.value).isNull()
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 0ac04b6..76539d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -467,6 +467,16 @@
             assertThat(values.toIdSets()).containsExactly(setOf(0, 1, 2))
         }
 
+    @Test
+    fun displayFlow_onlyDefaultDisplayAvailable_neverEmitsEmptySet() =
+        testScope.runTest {
+            setDisplays(0)
+
+            val values: List<Set<Display>> by collectValues(displayRepository.displays)
+
+            assertThat(values.toIdSets()).containsExactly(setOf(0))
+        }
+
     private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
 
     private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index fd9964f..a2b50fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.display.domain.interactor
 
 import android.companion.virtual.VirtualDeviceManager
-import android.companion.virtual.flags.Flags.FLAG_INTERACTIVE_SCREEN_MIRROR
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.view.Display
 import android.view.Display.TYPE_EXTERNAL
@@ -160,7 +158,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
     fun displayState_virtualDeviceOwnedMirrorVirtualDisplay_connected() =
         testScope.runTest {
             whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt()))
@@ -183,7 +180,6 @@
         }
 
     @Test
-    @EnableFlags(FLAG_INTERACTIVE_SCREEN_MIRROR)
     fun virtualDeviceOwnedMirrorVirtualDisplay_emitsConnectedDisplayAddition() =
         testScope.runTest {
             whenever(virtualDeviceManager.isVirtualDeviceOwnedMirrorDisplay(anyInt()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
index 419f7ed..299c384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStatePreventingAdapterTest.java
@@ -29,31 +29,22 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.Executor;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DozeScreenStatePreventingAdapterTest extends SysuiTestCase {
 
-    private Executor mExecutor;
     private DozeMachine.Service mInner;
     private DozeScreenStatePreventingAdapter mWrapper;
 
     @Before
     public void setup() throws Exception {
-        mExecutor = new FakeExecutor(new FakeSystemClock());
         mInner = mock(DozeMachine.Service.class);
-        mWrapper = new DozeScreenStatePreventingAdapter(
-                mInner,
-                mExecutor
-        );
+        mWrapper = new DozeScreenStatePreventingAdapter(mInner);
     }
 
     @Test
@@ -98,7 +89,7 @@
         when(params.getDisplayStateSupported()).thenReturn(false);
 
         assertEquals(DozeScreenStatePreventingAdapter.class,
-                DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+                DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)
                         .getClass());
     }
 
@@ -107,7 +98,6 @@
         DozeParameters params = mock(DozeParameters.class);
         when(params.getDisplayStateSupported()).thenReturn(true);
 
-        assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
-                mExecutor));
+        assertSame(mInner, DozeScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
index 5a89710..0d6a9ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuspendScreenStatePreventingAdapterTest.java
@@ -29,28 +29,22 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.Executor;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DozeSuspendScreenStatePreventingAdapterTest extends SysuiTestCase {
 
-    private Executor mExecutor;
     private DozeMachine.Service mInner;
     private DozeSuspendScreenStatePreventingAdapter mWrapper;
 
     @Before
     public void setup() throws Exception {
-        mExecutor = new FakeExecutor(new FakeSystemClock());
         mInner = mock(DozeMachine.Service.class);
-        mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner, mExecutor);
+        mWrapper = new DozeSuspendScreenStatePreventingAdapter(mInner);
     }
 
     @Test
@@ -101,7 +95,7 @@
         when(params.getDozeSuspendDisplayStateSupported()).thenReturn(false);
 
         assertEquals(DozeSuspendScreenStatePreventingAdapter.class,
-                DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params, mExecutor)
+                DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params)
                         .getClass());
     }
 
@@ -110,7 +104,6 @@
         DozeParameters params = mock(DozeParameters.class);
         when(params.getDozeSuspendDisplayStateSupported()).thenReturn(true);
 
-        assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params,
-                mExecutor));
+        assertSame(mInner, DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(mInner, params));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
index 933ddb5..4a80d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.haptics.vibratorHelper
+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
@@ -47,6 +47,7 @@
     private val lowTickDuration = 12 // Mocked duration of a low tick
     private val dragTextureThresholdMillis =
         lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval
+    private val vibratorHelper = kosmos.fakeVibratorHelper
     private lateinit var sliderHapticFeedbackProvider: SliderHapticFeedbackProvider
 
     @Before
@@ -56,11 +57,11 @@
         whenever(velocityTracker.getAxisVelocity(config.velocityAxis))
             .thenReturn(config.maxVelocityToScale)
 
-        kosmos.vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
+        vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] =
             lowTickDuration
         sliderHapticFeedbackProvider =
             SliderHapticFeedbackProvider(
-                kosmos.vibratorHelper,
+                vibratorHelper,
                 velocityTracker,
                 config,
                 kosmos.fakeSystemClock,
@@ -136,7 +137,7 @@
             sliderHapticFeedbackProvider.onUpperBookend()
             sliderHapticFeedbackProvider.onUpperBookend()
 
-            assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(vibration))
+            assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(vibration))
         }
 
     @Test
@@ -162,7 +163,7 @@
             sliderHapticFeedbackProvider.onProgress(progress)
 
             // THEN the correct composition only plays once
-            assertEquals(/* expected=*/ 1, vibratorHelper.timesVibratedWithEffect(ticks.compose()))
+            assertEquals(/* expected= */ 1, vibratorHelper.timesVibratedWithEffect(ticks.compose()))
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
new file mode 100644
index 0000000..1d96c4d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.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.inputdevice.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+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.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.google.common.truth.Truth.assertThat
+import java.time.Instant
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TutorialSchedulerRepositoryTest : SysuiTestCase() {
+
+    private lateinit var underTest: TutorialSchedulerRepository
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+
+    @Before
+    fun setup() {
+        underTest =
+            TutorialSchedulerRepository(
+                context,
+                testScope.backgroundScope,
+                "TutorialSchedulerRepositoryTest"
+            )
+    }
+
+    @After
+    fun clear() {
+        testScope.launch { underTest.clearDataStore() }
+    }
+
+    @Test
+    fun initialState() =
+        testScope.runTest {
+            assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse()
+            assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+            assertThat(underTest.isLaunched(KEYBOARD)).isFalse()
+            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
+        }
+
+    @Test
+    fun connectKeyboard() =
+        testScope.runTest {
+            val now = Instant.now()
+            underTest.updateFirstConnectionTime(KEYBOARD, now)
+
+            assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue()
+            assertThat(underTest.firstConnectionTime(KEYBOARD)!!.epochSecond)
+                .isEqualTo(now.epochSecond)
+            assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse()
+        }
+
+    @Test
+    fun launchKeyboard() =
+        testScope.runTest {
+            val now = Instant.now()
+            underTest.updateLaunchTime(KEYBOARD, now)
+
+            assertThat(underTest.isLaunched(KEYBOARD)).isTrue()
+            assertThat(underTest.launchTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond)
+            assertThat(underTest.isLaunched(TOUCHPAD)).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
new file mode 100644
index 0000000..f2e43fc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/data/repository/UserInputDeviceRepositoryTest.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.inputdevice.data.repository
+
+import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.inputdevice.data.model.UserDeviceConnectionStatus
+import com.android.systemui.keyboard.data.repository.keyboardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.touchpad.data.repository.touchpadRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+class UserInputDeviceRepositoryTest : SysuiTestCase() {
+
+    private lateinit var underTest: UserInputDeviceRepository
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val keyboardRepository = kosmos.keyboardRepository
+    private val touchpadRepository = kosmos.touchpadRepository
+    private val userRepository = kosmos.fakeUserRepository
+
+    @Before
+    fun setup() {
+        underTest =
+            UserInputDeviceRepository(
+                kosmos.testDispatcher,
+                keyboardRepository,
+                touchpadRepository,
+                kosmos.userRepository
+            )
+        userRepository.setUserInfos(USER_INFOS)
+    }
+
+    @Test
+    fun emitsNewKeyboardConnectedValueOnUserChanged() =
+        testScope.runTest {
+            val isAnyKeyboardConnected by collectValues(underTest.isAnyKeyboardConnectedForUser)
+            userRepository.setSelectedUserInfo(USER_INFOS[0])
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            runCurrent()
+
+            userRepository.setSelectedUserInfo(USER_INFOS[1])
+
+            assertThat(isAnyKeyboardConnected)
+                .containsExactly(
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun emitsNewTouchpadConnectedValueOnUserChanged() =
+        testScope.runTest {
+            val isAnyTouchpadConnected by collectValues(underTest.isAnyTouchpadConnectedForUser)
+            userRepository.setSelectedUserInfo(USER_INFOS[0])
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            runCurrent()
+
+            userRepository.setSelectedUserInfo(USER_INFOS[1])
+
+            assertThat(isAnyTouchpadConnected)
+                .containsExactly(
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[0].id),
+                    UserDeviceConnectionStatus(isConnected = true, USER_INFOS[1].id)
+                )
+                .inOrder()
+        }
+
+    companion object {
+        private val USER_INFOS =
+            listOf(
+                UserInfo(100, "First User", 0),
+                UserInfo(101, "Second User", 0),
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt
new file mode 100644
index 0000000..945f953
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.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.inputdevice.tutorial.domain.interactor
+
+import android.app.Notification
+import android.app.NotificationManager
+import androidx.annotation.StringRes
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.advanceTimeBy
+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.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TutorialNotificationCoordinatorTest : SysuiTestCase() {
+
+    private lateinit var underTest: TutorialNotificationCoordinator
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val keyboardRepository = FakeKeyboardRepository()
+    private val touchpadRepository = FakeTouchpadRepository()
+    private lateinit var dataStoreScope: CoroutineScope
+    private lateinit var repository: TutorialSchedulerRepository
+    @Mock private lateinit var notificationManager: NotificationManager
+    @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification>
+    @get:Rule val rule = MockitoJUnit.rule()
+
+    @Before
+    fun setup() {
+        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
+        repository =
+            TutorialSchedulerRepository(
+                context,
+                dataStoreScope,
+                dataStoreName = "TutorialNotificationCoordinatorTest"
+            )
+        val interactor =
+            TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, repository)
+        underTest =
+            TutorialNotificationCoordinator(
+                testScope.backgroundScope,
+                context,
+                interactor,
+                notificationManager
+            )
+        notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+        underTest.start()
+    }
+
+    @After
+    fun clear() {
+        runBlocking { repository.clearDataStore() }
+        dataStoreScope.cancel()
+    }
+
+    @Test
+    fun showKeyboardNotification() =
+        testScope.runTest {
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            advanceTimeBy(LAUNCH_DELAY)
+            verifyNotification(
+                R.string.launch_keyboard_tutorial_notification_title,
+                R.string.launch_keyboard_tutorial_notification_content
+            )
+        }
+
+    @Test
+    fun showTouchpadNotification() =
+        testScope.runTest {
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            advanceTimeBy(LAUNCH_DELAY)
+            verifyNotification(
+                R.string.launch_touchpad_tutorial_notification_title,
+                R.string.launch_touchpad_tutorial_notification_content
+            )
+        }
+
+    @Test
+    fun showKeyboardTouchpadNotification() =
+        testScope.runTest {
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            advanceTimeBy(LAUNCH_DELAY)
+            verifyNotification(
+                R.string.launch_keyboard_touchpad_tutorial_notification_title,
+                R.string.launch_keyboard_touchpad_tutorial_notification_content
+            )
+        }
+
+    @Test
+    fun doNotShowNotification() =
+        testScope.runTest {
+            advanceTimeBy(LAUNCH_DELAY)
+            verify(notificationManager, never()).notify(eq(TAG), eq(NOTIFICATION_ID), any())
+        }
+
+    private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) {
+        verify(notificationManager)
+            .notify(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture())
+        val notification = notificationCaptor.value
+        val actualTitle = notification.getString(Notification.EXTRA_TITLE)
+        val actualContent = notification.getString(Notification.EXTRA_TEXT)
+        assertThat(actualTitle).isEqualTo(context.getString(titleResId))
+        assertThat(actualContent).isEqualTo(context.getString(contentResId))
+    }
+
+    private fun Notification.getString(key: String): String =
+        this.extras?.getCharSequence(key).toString()
+
+    companion object {
+        private const val TAG = "TutorialSchedulerInteractor"
+        private const val NOTIFICATION_ID = 5566
+        private val LAUNCH_DELAY = 72.hours
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
new file mode 100644
index 0000000..650f9dc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType
+import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository
+import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.TutorialType
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TutorialSchedulerInteractorTest : SysuiTestCase() {
+
+    private lateinit var underTest: TutorialSchedulerInteractor
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private lateinit var dataStoreScope: CoroutineScope
+    private val keyboardRepository = FakeKeyboardRepository()
+    private val touchpadRepository = FakeTouchpadRepository()
+    private lateinit var schedulerRepository: TutorialSchedulerRepository
+
+    @Before
+    fun setup() {
+        dataStoreScope = CoroutineScope(Dispatchers.Unconfined)
+        schedulerRepository =
+            TutorialSchedulerRepository(
+                context,
+                dataStoreScope,
+                dataStoreName = "TutorialSchedulerInteractorTest"
+            )
+        underTest =
+            TutorialSchedulerInteractor(keyboardRepository, touchpadRepository, schedulerRepository)
+    }
+
+    @After
+    fun clear() {
+        runBlocking { schedulerRepository.clearDataStore() }
+        dataStoreScope.cancel()
+    }
+
+    @Test
+    fun connectKeyboard_delayElapse_launchForKeyboard() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.KEYBOARD)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            advanceTimeBy(LAUNCH_DELAY)
+        }
+
+    @Test
+    fun connectBothDevices_delayElapse_launchForBoth() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.BOTH)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            advanceTimeBy(LAUNCH_DELAY)
+        }
+
+    @Test
+    fun connectBothDevice_delayNotElapse_launchNothing() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.NONE)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+        }
+
+    @Test
+    fun nothingConnect_delayElapse_launchNothing() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.NONE)
+
+            keyboardRepository.setIsAnyKeyboardConnected(false)
+            touchpadRepository.setIsAnyTouchpadConnected(false)
+            advanceTimeBy(LAUNCH_DELAY)
+        }
+
+    @Test
+    fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.BOTH)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            advanceTimeBy(REMAINING_TIME)
+        }
+
+    @Test
+    fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() =
+        testScope.runTest {
+            launchAndAssert(TutorialType.NONE)
+
+            keyboardRepository.setIsAnyKeyboardConnected(true)
+            advanceTimeBy(A_SHORT_PERIOD_OF_TIME)
+            touchpadRepository.setIsAnyTouchpadConnected(true)
+            keyboardRepository.setIsAnyKeyboardConnected(false)
+            advanceTimeBy(REMAINING_TIME)
+        }
+
+    private suspend fun launchAndAssert(expectedTutorial: TutorialType) =
+        testScope.backgroundScope.launch {
+            val actualTutorial = underTest.tutorials.first()
+            assertThat(actualTutorial).isEqualTo(expectedTutorial)
+
+            // TODO: need to update after we move launch into the tutorial
+            when (expectedTutorial) {
+                TutorialType.KEYBOARD -> {
+                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
+                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+                }
+                TutorialType.TOUCHPAD -> {
+                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
+                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+                }
+                TutorialType.BOTH -> {
+                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue()
+                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue()
+                }
+                TutorialType.NONE -> {
+                    assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse()
+                    assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse()
+                }
+            }
+        }
+
+    companion object {
+        private val LAUNCH_DELAY = 72.hours
+        private val A_SHORT_PERIOD_OF_TIME = 2.hours
+        private val REMAINING_TIME = 70.hours
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
new file mode 100644
index 0000000..0c716137
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/inputdevice/tutorial/ui/viewmodel/KeyboardTouchpadTutorialViewModelTest.kt
@@ -0,0 +1,316 @@
+/*
+ * 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.viewmodel
+
+import androidx.lifecycle.Lifecycle.Event
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.SavedStateHandle
+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.inputdevice.tutorial.domain.interactor.KeyboardTouchpadConnectionInteractor
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEY
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_KEYBOARD
+import com.android.systemui.inputdevice.tutorial.ui.view.KeyboardTouchpadTutorialActivity.Companion.INTENT_TUTORIAL_TYPE_TOUCHPAD
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.ACTION_KEY
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTURE
+import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE
+import com.android.systemui.keyboard.data.repository.keyboardRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.data.repository.TouchpadRepository
+import com.android.systemui.touchpad.tutorial.touchpadGesturesInteractor
+import com.android.systemui.util.coroutines.MainDispatcherRule
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+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.Mockito.mock
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyboardTouchpadTutorialViewModelTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val sysUiState = kosmos.sysUiState
+    private val touchpadRepo = PrettyFakeTouchpadRepository()
+    private val keyboardRepo = kosmos.keyboardRepository
+    private var startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+    private val viewModel by lazy { createViewModel(startingPeripheral) }
+
+    // createUnsafe so its methods don't have to be called on Main thread
+    private val lifecycle = LifecycleRegistry.createUnsafe(mock(LifecycleOwner::class.java))
+
+    @get:Rule val mainDispatcherRule = MainDispatcherRule(kosmos.testDispatcher)
+
+    private fun createViewModel(
+        startingPeripheral: String = INTENT_TUTORIAL_TYPE_TOUCHPAD,
+        hasTouchpadTutorialScreens: Boolean = true,
+    ): KeyboardTouchpadTutorialViewModel {
+        val viewModel =
+            KeyboardTouchpadTutorialViewModel(
+                Optional.of(kosmos.touchpadGesturesInteractor),
+                KeyboardTouchpadConnectionInteractor(keyboardRepo, touchpadRepo),
+                hasTouchpadTutorialScreens,
+                SavedStateHandle(mapOf(INTENT_TUTORIAL_TYPE_KEY to startingPeripheral))
+            )
+        lifecycle.addObserver(viewModel)
+        return viewModel
+    }
+
+    @Test
+    fun screensOrder_whenTouchpadAndKeyboardConnected() =
+        testScope.runTest {
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+            peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+            goToNextScreen()
+            goToNextScreen()
+            // reached the last screen
+
+            assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY).inOrder()
+            assertThat(closeActivity).isFalse()
+        }
+
+    @Test
+    fun screensOrder_whenKeyboardDisconnectsDuringTutorial() =
+        testScope.runTest {
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+            peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+            // back gesture screen
+            goToNextScreen()
+            // home gesture screen
+            peripheralsState(keyboardConnected = false, touchpadConnected = true)
+            goToNextScreen()
+            // no action key screen because keyboard disconnected
+
+            assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE).inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun screensOrderUntilFinish_whenTouchpadAndKeyboardConnected() =
+        testScope.runTest {
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+
+            peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+            goToNextScreen()
+            goToNextScreen()
+            // we're at the last screen so "next screen" should be actually closing activity
+            goToNextScreen()
+
+            assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY).inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun screensOrder_whenGoingBackToPreviousScreens() =
+        testScope.runTest {
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+            peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+            // back gesture
+            goToNextScreen()
+            // home gesture
+            goToNextScreen()
+            // action key
+
+            goBack()
+            // home gesture
+            goBack()
+            // back gesture
+            goBack()
+            // finish activity
+
+            assertThat(screens)
+                .containsExactly(BACK_GESTURE, HOME_GESTURE, ACTION_KEY, HOME_GESTURE, BACK_GESTURE)
+                .inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun screensOrder_whenGoingBackAndOnlyKeyboardConnected() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+            peripheralsState(keyboardConnected = true, touchpadConnected = false)
+
+            // action key screen
+            goBack()
+            // activity finished
+
+            assertThat(screens).containsExactly(ACTION_KEY).inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun screensOrder_whenTouchpadConnected() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+
+            peripheralsState(keyboardConnected = false, touchpadConnected = true)
+
+            goToNextScreen()
+            goToNextScreen()
+            goToNextScreen()
+
+            assertThat(screens).containsExactly(BACK_GESTURE, HOME_GESTURE).inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun screensOrder_whenKeyboardConnected() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_KEYBOARD
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+
+            peripheralsState(keyboardConnected = true)
+
+            goToNextScreen()
+            goToNextScreen()
+
+            assertThat(screens).containsExactly(ACTION_KEY).inOrder()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun touchpadGesturesDisabled_onlyDuringTouchpadTutorial() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+            collectValues(viewModel.screen) // just to initialize viewModel
+            peripheralsState(keyboardConnected = true, touchpadConnected = true)
+
+            assertGesturesDisabled()
+            goToNextScreen()
+            goToNextScreen()
+            // end of touchpad tutorial, keyboard tutorial starts
+            assertGesturesNotDisabled()
+        }
+
+    @Test
+    fun activityFinishes_ifTouchpadModuleIsNotPresent() =
+        testScope.runTest {
+            val viewModel =
+                createViewModel(
+                    startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD,
+                    hasTouchpadTutorialScreens = false
+                )
+            val screens by collectValues(viewModel.screen)
+            val closeActivity by collectLastValue(viewModel.closeActivity)
+            peripheralsState(touchpadConnected = true)
+
+            assertThat(screens).isEmpty()
+            assertThat(closeActivity).isTrue()
+        }
+
+    @Test
+    fun touchpadGesturesDisabled_whenTutorialGoesToForeground() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+            collectValues(viewModel.screen) // just to initialize viewModel
+            peripheralsState(touchpadConnected = true)
+
+            lifecycle.handleLifecycleEvent(Event.ON_START)
+
+            assertGesturesDisabled()
+        }
+
+    @Test
+    fun touchpadGesturesNotDisabled_whenTutorialGoesToBackground() =
+        testScope.runTest {
+            startingPeripheral = INTENT_TUTORIAL_TYPE_TOUCHPAD
+            collectValues(viewModel.screen)
+            peripheralsState(touchpadConnected = true)
+
+            lifecycle.handleLifecycleEvent(Event.ON_START)
+            lifecycle.handleLifecycleEvent(Event.ON_STOP)
+
+            assertGesturesNotDisabled()
+        }
+
+    @Test
+    fun keyboardShortcutsDisabled_onlyDuringKeyboardTutorial() =
+        testScope.runTest {
+            // TODO(b/358587037)
+        }
+
+    private fun TestScope.goToNextScreen() {
+        viewModel.onDoneButtonClicked()
+        runCurrent()
+    }
+
+    private fun TestScope.goBack() {
+        viewModel.onBack()
+        runCurrent()
+    }
+
+    private fun TestScope.peripheralsState(
+        keyboardConnected: Boolean = false,
+        touchpadConnected: Boolean = false
+    ) {
+        keyboardRepo.setIsAnyKeyboardConnected(keyboardConnected)
+        touchpadRepo.setIsAnyTouchpadConnected(touchpadConnected)
+        runCurrent()
+    }
+
+    private fun TestScope.assertGesturesNotDisabled() = assertFlagEnabled(enabled = false)
+
+    private fun TestScope.assertGesturesDisabled() = assertFlagEnabled(enabled = true)
+
+    private fun TestScope.assertFlagEnabled(enabled: Boolean) {
+        // sysui state is changed on background scope so let's make sure it's executed
+        runCurrent()
+        assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED))
+            .isEqualTo(enabled)
+    }
+
+    // replace below when we have better fake
+    internal class PrettyFakeTouchpadRepository : TouchpadRepository {
+
+        private val _isAnyTouchpadConnected = MutableStateFlow(false)
+        override val isAnyTouchpadConnected: Flow<Boolean> = _isAnyTouchpadConnected
+
+        fun setIsAnyTouchpadConnected(connected: Boolean) {
+            _isAnyTouchpadConnected.value = connected
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index 9d9e5be6..3ccb989 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -34,14 +34,16 @@
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
 import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepositoryImpl
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
@@ -57,29 +59,28 @@
 @RunWith(AndroidJUnit4::class)
 class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
 
-    private val dispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(dispatcher)
+    private val kosmos = testKosmos()
+    private val dispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
     private lateinit var viewModel: StickyKeysIndicatorViewModel
     private val inputManager = mock<InputManager>()
     private val keyboardRepository = FakeKeyboardRepository()
-    private val secureSettings = FakeSettings()
+    private val secureSettings = kosmos.fakeSettings
     private val userRepository = Kosmos().fakeUserRepository
     private val captor =
         ArgumentCaptor.forClass(InputManager.StickyModifierStateListener::class.java)
 
     @Before
     fun setup() {
-        val settingsRepository = UserAwareSecureSettingsRepositoryImpl(
-            secureSettings,
-            userRepository,
-            dispatcher
-        )
-        val stickyKeysRepository = StickyKeysRepositoryImpl(
-            inputManager,
-            dispatcher,
-            settingsRepository,
-            mock<StickyKeysLogger>()
-        )
+        val settingsRepository =
+            UserAwareSecureSettingsRepositoryImpl(secureSettings, userRepository, dispatcher)
+        val stickyKeysRepository =
+            StickyKeysRepositoryImpl(
+                inputManager,
+                dispatcher,
+                settingsRepository,
+                mock<StickyKeysLogger>()
+            )
         setStickyKeySetting(enabled = false)
         viewModel =
             StickyKeysIndicatorViewModel(
@@ -182,16 +183,16 @@
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
             setStickyKeysActive()
 
-            setStickyKeys(mapOf(
-                ALT to false,
-                META to false,
-                SHIFT to false))
+            setStickyKeys(mapOf(ALT to false, META to false, SHIFT to false))
 
-            assertThat(stickyKeys).isEqualTo(mapOf(
-                ALT to Locked(false),
-                META to Locked(false),
-                SHIFT to Locked(false),
-            ))
+            assertThat(stickyKeys)
+                .isEqualTo(
+                    mapOf(
+                        ALT to Locked(false),
+                        META to Locked(false),
+                        SHIFT to Locked(false),
+                    )
+                )
         }
     }
 
@@ -201,9 +202,7 @@
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
             setStickyKeysActive()
 
-            setStickyKeys(mapOf(
-                ALT to false,
-                ALT to true))
+            setStickyKeys(mapOf(ALT to false, ALT to true))
 
             assertThat(stickyKeys).isEqualTo(mapOf(ALT to Locked(true)))
         }
@@ -215,17 +214,23 @@
             val stickyKeys by collectLastValue(viewModel.indicatorContent)
             setStickyKeysActive()
 
-            setStickyKeys(mapOf(
-                META to false,
-                SHIFT to false, // shift is sticky but not locked
-                CTRL to false))
+            setStickyKeys(
+                mapOf(
+                    META to false,
+                    SHIFT to false, // shift is sticky but not locked
+                    CTRL to false
+                )
+            )
             val previousShiftIndex = stickyKeys?.toList()?.indexOf(SHIFT to Locked(false))
 
-            setStickyKeys(mapOf(
-                SHIFT to false,
-                SHIFT to true, // shift is now locked
-                META to false,
-                CTRL to false))
+            setStickyKeys(
+                mapOf(
+                    SHIFT to false,
+                    SHIFT to true, // shift is now locked
+                    META to false,
+                    CTRL to false
+                )
+            )
             assertThat(stickyKeys?.toList()?.indexOf(SHIFT to Locked(true)))
                 .isEqualTo(previousShiftIndex)
         }
@@ -247,17 +252,27 @@
         StickyModifierState() {
 
         private fun isOn(key: ModifierKey) = keys.any { it.key == key && !it.value }
+
         private fun isLocked(key: ModifierKey) = keys.any { it.key == key && it.value }
 
         override fun isAltGrModifierLocked() = isLocked(ALT_GR)
+
         override fun isAltGrModifierOn() = isOn(ALT_GR)
+
         override fun isAltModifierLocked() = isLocked(ALT)
+
         override fun isAltModifierOn() = isOn(ALT)
+
         override fun isCtrlModifierLocked() = isLocked(CTRL)
+
         override fun isCtrlModifierOn() = isOn(CTRL)
+
         override fun isMetaModifierLocked() = isLocked(META)
+
         override fun isMetaModifierOn() = isOn(META)
+
         override fun isShiftModifierLocked() = isLocked(SHIFT)
+
         override fun isShiftModifierOn() = isOn(SHIFT)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 29cd9a2..fa69fdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -50,6 +50,8 @@
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
 import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRendererFactory
 import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -64,12 +66,12 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
@@ -87,6 +89,11 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class CustomizationProviderTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.unconfinedTestDispatcher
+    private val testScope = kosmos.unconfinedTestScope
+    private val fakeSettings = kosmos.unconfinedDispatcherFakeSettings
+
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var userTracker: UserTracker
@@ -104,9 +111,6 @@
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
 
     private lateinit var underTest: CustomizationProvider
-    private lateinit var testScope: TestScope
-
-    private val kosmos = testKosmos()
 
     @Before
     fun setUp() {
@@ -120,8 +124,6 @@
         biometricSettingsRepository = FakeBiometricSettingsRepository()
 
         underTest = CustomizationProvider()
-        val testDispatcher = UnconfinedTestDispatcher()
-        testScope = TestScope(testDispatcher)
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
@@ -170,7 +172,7 @@
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = testDispatcher,
-                        secureSettings = FakeSettings(),
+                        secureSettings = fakeSettings,
                         selectionsManager = localUserSelectionManager,
                     ),
                 dumpManager = mock(),
@@ -216,7 +218,7 @@
                 mainDispatcher = testDispatcher,
                 backgroundHandler = backgroundHandler,
             )
-        underTest.mainDispatcher = UnconfinedTestDispatcher()
+        underTest.mainDispatcher = testDispatcher
 
         underTest.attachInfoForTesting(
             context,
@@ -319,6 +321,7 @@
                         ),
                     )
                 )
+            runCurrent()
         }
 
     @Test
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 e3a38a8..597ffef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,7 +26,6 @@
 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_REFACTOR_GET_CURRENT_USER;
 import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
 import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
 import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -250,7 +249,6 @@
         when(mCommunalTransitionViewModel.getTransitionFromOccludedEnded())
                 .thenReturn(mock(Flow.class));
         when(mSelectedUserInteractor.getSelectedUserId()).thenReturn(mDefaultUserId);
-        when(mSelectedUserInteractor.getSelectedUserId(anyBoolean())).thenReturn(mDefaultUserId);
         when(mProcessWrapper.isSystemUser()).thenReturn(true);
         mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
                 mContext,
@@ -275,7 +273,6 @@
                 mKosmos.getNotificationShadeWindowModel(),
                 mKosmos::getCommunalInteractor);
         mFeatureFlags = new FakeFeatureFlags();
-        mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER);
         mSetFlagsRule.disableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR);
 
         DejankUtils.setImmediate(true);
@@ -443,7 +440,7 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -463,7 +460,7 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -570,7 +567,7 @@
         // When showing and provisioned
         mViewMediator.onSystemReady();
         when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
-        mViewMediator.setShowingLocked(true);
+        mViewMediator.setShowingLocked(true, "");
 
         // and a SIM becomes locked and requires a PIN
         mViewMediator.mUpdateCallback.onSimStateChanged(
@@ -579,7 +576,7 @@
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
 
         // and the keyguard goes away
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         when(mKeyguardStateController.isShowing()).thenReturn(false);
         mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
 
@@ -595,7 +592,7 @@
         // When showing and provisioned
         mViewMediator.onSystemReady();
         when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
 
         // and a SIM becomes locked and requires a PIN
         mViewMediator.mUpdateCallback.onSimStateChanged(
@@ -604,7 +601,7 @@
                 TelephonyManager.SIM_STATE_PIN_REQUIRED);
 
         // and the keyguard goes away
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         when(mKeyguardStateController.isShowing()).thenReturn(false);
         mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
 
@@ -843,7 +840,7 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -863,7 +860,7 @@
         mViewMediator.onSystemReady();
         TestableLooper.get(this).processAllMessages();
 
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         TestableLooper.get(this).processAllMessages();
 
         mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
@@ -978,7 +975,7 @@
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testDoKeyguardWhileInteractive_resets() {
-        mViewMediator.setShowingLocked(true);
+        mViewMediator.setShowingLocked(true, "");
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         TestableLooper.get(this).processAllMessages();
 
@@ -992,7 +989,7 @@
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testDoKeyguardWhileNotInteractive_showsInsteadOfResetting() {
-        mViewMediator.setShowingLocked(true);
+        mViewMediator.setShowingLocked(true, "");
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         TestableLooper.get(this).processAllMessages();
 
@@ -1051,7 +1048,7 @@
         mViewMediator.onSystemReady();
         processAllMessagesAndBgExecutorMessages();
 
-        mViewMediator.setShowingLocked(true);
+        mViewMediator.setShowingLocked(true, "");
 
         RemoteAnimationTarget[] apps = new RemoteAnimationTarget[]{
                 mock(RemoteAnimationTarget.class)
@@ -1123,7 +1120,7 @@
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testNotStartingKeyguardWhenFlagIsDisabled() {
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         when(mKeyguardStateController.isShowing()).thenReturn(false);
 
         mViewMediator.onDreamingStarted();
@@ -1133,7 +1130,7 @@
     @Test
     @TestableLooper.RunWithLooper(setAsMainLooper = true)
     public void testStartingKeyguardWhenFlagIsEnabled() {
-        mViewMediator.setShowingLocked(true);
+        mViewMediator.setShowingLocked(true, "");
         when(mKeyguardStateController.isShowing()).thenReturn(true);
 
         mViewMediator.onDreamingStarted();
@@ -1174,7 +1171,7 @@
         TestableLooper.get(this).processAllMessages();
 
         // WHEN keyguard visibility becomes FALSE
-        mViewMediator.setShowingLocked(false);
+        mViewMediator.setShowingLocked(false, "");
         keyguardUpdateMonitorCallback.onKeyguardVisibilityChanged(false);
         TestableLooper.get(this).processAllMessages();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
index af5187d..1e9db64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
@@ -25,15 +25,14 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.shared.clocks.ClockRegistry
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
@@ -44,12 +43,12 @@
 @SmallTest
 class KeyguardClockRepositoryTest : SysuiTestCase() {
 
-    private lateinit var scheduler: TestCoroutineScheduler
-    private lateinit var dispatcher: CoroutineDispatcher
-    private lateinit var scope: TestScope
+    private val kosmos = testKosmos()
+    private val dispatcher = kosmos.testDispatcher
+    private val scope = kosmos.testScope
+    private val fakeSettings = kosmos.fakeSettings
 
     private lateinit var underTest: KeyguardClockRepository
-    private lateinit var fakeSettings: FakeSettings
     @Mock private lateinit var clockRegistry: ClockRegistry
     @Mock private lateinit var clockEventController: ClockEventController
     private val fakeFeatureFlagsClassic = FakeFeatureFlagsClassic()
@@ -57,10 +56,6 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        fakeSettings = FakeSettings()
-        scheduler = TestCoroutineScheduler()
-        dispatcher = StandardTestDispatcher(scheduler)
-        scope = TestScope(dispatcher)
         underTest =
             KeyguardClockRepositoryImpl(
                 fakeSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
index 8b8a6cb..5a597fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
@@ -21,14 +21,12 @@
 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.settings.FakeUserTracker
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import kotlin.test.Test
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestCoroutineScheduler
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
@@ -38,23 +36,18 @@
 @SmallTest
 class KeyguardSmartspaceRepositoryImplTest : SysuiTestCase() {
 
-    private lateinit var scheduler: TestCoroutineScheduler
-    private lateinit var dispatcher: CoroutineDispatcher
-    private lateinit var scope: TestScope
+    private val kosmos = testKosmos()
+    private val scope = kosmos.testScope
+    private val fakeSettings = kosmos.fakeSettings
 
     private lateinit var underTest: KeyguardSmartspaceRepository
-    private lateinit var fakeSettings: FakeSettings
     private lateinit var fakeUserTracker: FakeUserTracker
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        fakeSettings = FakeSettings()
         fakeUserTracker = FakeUserTracker()
         fakeSettings.userId = fakeUserTracker.userId
-        scheduler = TestCoroutineScheduler()
-        dispatcher = StandardTestDispatcher(scheduler)
-        scope = TestScope(dispatcher)
         underTest =
             KeyguardSmartspaceRepositoryImpl(
                 context = context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 2af4d87..bfe89de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -17,9 +17,6 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.animation.ValueAnimator
-import android.util.Log
-import android.util.Log.TerribleFailure
-import android.util.Log.TerribleFailureHandler
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
@@ -53,7 +50,6 @@
 import kotlinx.coroutines.test.advanceTimeBy
 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
@@ -67,23 +63,14 @@
     private val testScope = kosmos.testScope
 
     private lateinit var underTest: KeyguardTransitionRepository
-    private lateinit var oldWtfHandler: TerribleFailureHandler
-    private lateinit var wtfHandler: WtfHandler
     private lateinit var runner: KeyguardTransitionRunner
 
     @Before
     fun setUp() {
         underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main)
-        wtfHandler = WtfHandler()
-        oldWtfHandler = Log.setWtfHandler(wtfHandler)
         runner = KeyguardTransitionRunner(underTest)
     }
 
-    @After
-    fun tearDown() {
-        oldWtfHandler?.let { Log.setWtfHandler(it) }
-    }
-
     @Test
     fun startTransitionRunsAnimatorToCompletion() =
         testScope.runTest {
@@ -333,15 +320,17 @@
         }
 
     @Test
-    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDthrowsException() =
+    fun attemptTomanuallyUpdateTransitionWithInvalidUUIDEmitsNothing() =
         testScope.runTest {
+            val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
             underTest.updateTransition(UUID.randomUUID(), 0f, TransitionState.RUNNING)
-            assertThat(wtfHandler.failed).isTrue()
+            assertThat(steps.size).isEqualTo(0)
         }
 
     @Test
-    fun attemptToManuallyUpdateTransitionAfterFINISHEDstateThrowsException() =
+    fun attemptToManuallyUpdateTransitionAfterFINISHEDstateEmitsNothing() =
         testScope.runTest {
+            val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
             val uuid =
                 underTest.startTransition(
                     TransitionInfo(
@@ -356,12 +345,19 @@
                 underTest.updateTransition(it, 1f, TransitionState.FINISHED)
                 underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
             }
-            assertThat(wtfHandler.failed).isTrue()
+            assertThat(steps.size).isEqualTo(2)
+            assertThat(steps[0])
+                .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+            assertThat(steps[1])
+                .isEqualTo(
+                    TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME)
+                )
         }
 
     @Test
-    fun attemptToManuallyUpdateTransitionAfterCANCELEDstateThrowsException() =
+    fun attemptToManuallyUpdateTransitionAfterCANCELEDstateEmitsNothing() =
         testScope.runTest {
+            val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF })
             val uuid =
                 underTest.startTransition(
                     TransitionInfo(
@@ -376,7 +372,13 @@
                 underTest.updateTransition(it, 0.2f, TransitionState.CANCELED)
                 underTest.updateTransition(it, 0.5f, TransitionState.RUNNING)
             }
-            assertThat(wtfHandler.failed).isTrue()
+            assertThat(steps.size).isEqualTo(2)
+            assertThat(steps[0])
+                .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
+            assertThat(steps[1])
+                .isEqualTo(
+                    TransitionStep(AOD, LOCKSCREEN, 0.2f, TransitionState.CANCELED, OWNER_NAME)
+                )
         }
 
     @Test
@@ -530,8 +532,6 @@
             }
         assertThat(steps[steps.size - 1])
             .isEqualTo(TransitionStep(from, to, lastValue, status, OWNER_NAME))
-
-        assertThat(wtfHandler.failed).isFalse()
     }
 
     private fun getAnimator(): ValueAnimator {
@@ -541,14 +541,6 @@
         }
     }
 
-    private class WtfHandler : TerribleFailureHandler {
-        var failed = false
-
-        override fun onTerribleFailure(tag: String, what: TerribleFailure, system: Boolean) {
-            failed = true
-        }
-    }
-
     companion object {
         private const val OWNER_NAME = "KeyguardTransitionRunner"
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 32d059b..3cbbb64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -22,6 +22,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.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.flags.EnableSceneContainer
@@ -29,6 +31,10 @@
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 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.scene.data.repository.Idle
 import com.android.systemui.scene.data.repository.Transition
 import com.android.systemui.scene.data.repository.setSceneTransition
@@ -57,31 +63,26 @@
     private val keyguardRepository = kosmos.fakeKeyguardRepository
     private val testScope = kosmos.testScope
 
-    private lateinit var dismissInteractorWithDependencies:
-        KeyguardDismissInteractorFactory.WithDependencies
+    private lateinit var dismissInteractor: KeyguardDismissInteractor
     private lateinit var underTest: KeyguardDismissActionInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        dismissInteractorWithDependencies =
-            KeyguardDismissInteractorFactory.create(
-                context = context,
-                testScope = testScope,
-                keyguardRepository = keyguardRepository,
-            )
-
+        dismissInteractor = kosmos.keyguardDismissInteractor
         underTest =
             KeyguardDismissActionInteractor(
                 repository = keyguardRepository,
                 transitionInteractor = kosmos.keyguardTransitionInteractor,
-                dismissInteractor = dismissInteractorWithDependencies.interactor,
+                dismissInteractor = dismissInteractor,
                 applicationScope = testScope.backgroundScope,
                 sceneInteractor = kosmos.sceneInteractor,
                 deviceEntryInteractor = kosmos.deviceEntryInteractor,
                 quickSettingsSceneFamilyResolver = kosmos.quickSettingsSceneFamilyResolver,
                 notifShadeSceneFamilyResolver = kosmos.notifShadeSceneFamilyResolver,
+                powerInteractor = kosmos.powerInteractor,
+                alternateBouncerInteractor = kosmos.alternateBouncerInteractor,
             )
     }
 
@@ -159,9 +160,7 @@
                     willAnimateOnLockscreen = true,
                 )
             )
-            dismissInteractorWithDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(
-                true
-            )
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
             assertThat(executeDismissAction).isEqualTo(onDismissAction)
         }
 
@@ -234,6 +233,32 @@
         }
 
     @Test
+    fun resetDismissAction_onBouncer_OnAsleep() =
+        testScope.runTest {
+            kosmos.setSceneTransition(Idle(Scenes.Bouncer))
+            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
+                AuthenticationMethodModel.None
+            )
+            val resetDismissAction by collectLastValue(underTest.resetDismissAction)
+            keyguardRepository.setDismissAction(
+                DismissAction.RunAfterKeyguardGone(
+                    dismissAction = {},
+                    onCancelAction = {},
+                    message = "message",
+                    willAnimateOnLockscreen = true,
+                )
+            )
+            assertThat(resetDismissAction).isNull()
+            kosmos.fakePowerRepository.updateWakefulness(
+                rawState = WakefulnessState.ASLEEP,
+                lastWakeReason = WakeSleepReason.POWER_BUTTON,
+                lastSleepReason = WakeSleepReason.TIMEOUT,
+                powerButtonLaunchGestureTriggered = false,
+            )
+            assertThat(resetDismissAction).isEqualTo(Unit)
+        }
+
+    @Test
     fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() =
         testScope.runTest {
             val dismissAction by collectLastValue(underTest.dismissAction)
@@ -274,8 +299,7 @@
     @Test
     fun setKeyguardDone() =
         testScope.runTest {
-            val keyguardDoneTiming by
-                collectLastValue(dismissInteractorWithDependencies.interactor.keyguardDone)
+            val keyguardDoneTiming by collectLastValue(dismissInteractor.keyguardDone)
             runCurrent()
 
             underTest.setKeyguardDone(KeyguardDone.LATER)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
index ecb46bd..fabed03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorTest.kt
@@ -23,11 +23,18 @@
 import androidx.test.filters.SmallTest
 import com.android.keyguard.TrustGrantFlags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeTrustRepository
 import com.android.systemui.keyguard.shared.model.DismissAction
 import com.android.systemui.keyguard.shared.model.KeyguardDone
 import com.android.systemui.keyguard.shared.model.TrustModel
+import com.android.systemui.power.data.repository.fakePowerRepository
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -38,14 +45,16 @@
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class KeyguardDismissInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var dispatcher: TestDispatcher
     private lateinit var testScope: TestScope
 
-    private lateinit var underTestDependencies: KeyguardDismissInteractorFactory.WithDependencies
-    private lateinit var underTest: KeyguardDismissInteractor
+    private val underTest = kosmos.keyguardDismissInteractor
     private val userInfo = UserInfo(0, "", 0)
 
     @Before
@@ -54,13 +63,7 @@
         dispatcher = StandardTestDispatcher()
         testScope = TestScope(dispatcher)
 
-        underTestDependencies =
-            KeyguardDismissInteractorFactory.create(
-                context = context,
-                testScope = testScope,
-            )
-        underTest = underTestDependencies.interactor
-        underTestDependencies.userRepository.setUserInfos(listOf(userInfo))
+        kosmos.fakeUserRepository.setUserInfos(listOf(userInfo))
     }
 
     @Test
@@ -69,10 +72,10 @@
             val dismissKeyguardRequestWithoutImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
 
-            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(null)
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
 
-            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedBiometrics(true)
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
         }
 
@@ -81,7 +84,7 @@
         testScope.runTest {
             val dismissKeyguardRequestWithoutImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
-            underTestDependencies.trustRepository.setRequestDismissKeyguard(
+            kosmos.fakeTrustRepository.setRequestDismissKeyguard(
                 TrustModel(
                     true,
                     0,
@@ -90,8 +93,8 @@
             )
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
 
-            underTestDependencies.powerRepository.setInteractive(true)
-            underTestDependencies.trustRepository.setRequestDismissKeyguard(
+            kosmos.fakePowerRepository.setInteractive(true)
+            kosmos.fakeTrustRepository.setRequestDismissKeyguard(
                 TrustModel(
                     true,
                     0,
@@ -106,15 +109,15 @@
         testScope.runTest {
             val dismissKeyguardRequestWithoutImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
-            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
             runCurrent()
 
             // authenticated different user
-            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(22)
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
 
             // authenticated correct user
-            underTestDependencies.bouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
+            kosmos.fakeKeyguardBouncerRepository.setKeyguardAuthenticatedPrimaryAuth(userInfo.id)
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
         }
 
@@ -123,17 +126,15 @@
         testScope.runTest {
             val dismissKeyguardRequestWithoutImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
-            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
             runCurrent()
 
             // requested from different user
-            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
-                22
-            )
+            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(22)
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
 
             // requested from correct user
-            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                 userInfo.id
             )
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isEqualTo(Unit)
@@ -159,10 +160,10 @@
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
             val dismissKeyguardRequestWithImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
-            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
             runCurrent()
 
-            underTestDependencies.keyguardRepository.setDismissAction(
+            kosmos.fakeKeyguardRepository.setDismissAction(
                 DismissAction.RunImmediately(
                     onDismissAction = { KeyguardDone.IMMEDIATE },
                     onCancelAction = {},
@@ -170,7 +171,7 @@
                     willAnimateOnLockscreen = true,
                 )
             )
-            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                 userInfo.id
             )
             assertThat(dismissKeyguardRequestWithoutImmediateDismissAction).isNull()
@@ -184,10 +185,10 @@
                 collectLastValue(underTest.dismissKeyguardRequestWithoutImmediateDismissAction)
             val dismissKeyguardRequestWithImmediateDismissAction by
                 collectLastValue(underTest.dismissKeyguardRequestWithImmediateDismissAction)
-            underTestDependencies.userRepository.setSelectedUserInfo(userInfo)
+            kosmos.fakeUserRepository.setSelectedUserInfo(userInfo)
             runCurrent()
 
-            underTestDependencies.keyguardRepository.setDismissAction(
+            kosmos.fakeKeyguardRepository.setDismissAction(
                 DismissAction.RunAfterKeyguardGone(
                     dismissAction = {},
                     onCancelAction = {},
@@ -195,7 +196,7 @@
                     willAnimateOnLockscreen = true,
                 )
             )
-            underTestDependencies.bouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
+            kosmos.fakeKeyguardBouncerRepository.setUserRequestedBouncerWhenAlreadyAuthenticated(
                 userInfo.id
             )
             assertThat(dismissKeyguardRequestWithImmediateDismissAction).isNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 4e1b12f..43c7ed6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,7 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -44,7 +44,8 @@
     @Mock private lateinit var activityTaskManagerService: IActivityTaskManager
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
-    @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+    @Mock
+    private lateinit var keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor
 
     @Before
     fun setUp() {
@@ -57,7 +58,7 @@
                 activityTaskManagerService = activityTaskManagerService,
                 keyguardStateController = keyguardStateController,
                 keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
-                keyguardTransitionInteractor = keyguardTransitionInteractor,
+                keyguardDismissTransitionInteractor = keyguardDismissTransitionInteractor,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index 313292a..844a166 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -19,14 +19,19 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.policy.IKeyguardDismissCallback
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
 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.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
@@ -36,6 +41,7 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 
 @ExperimentalCoroutinesApi
@@ -49,20 +55,39 @@
     private val underTest = kosmos.alternateBouncerViewModel
 
     @Test
-    fun showPrimaryBouncer() =
+    fun onTapped() =
         testScope.runTest {
-            underTest.showPrimaryBouncer()
+            underTest.onTapped()
             verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
         }
 
     @Test
-    fun hideAlternateBouncer() =
+    fun onRemovedFromWindow() =
         testScope.runTest {
-            underTest.hideAlternateBouncer()
+            underTest.onRemovedFromWindow()
             verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
         }
 
     @Test
+    fun onBackRequested() =
+        testScope.runTest {
+            kosmos.primaryBouncerInteractor.setDismissAction(
+                mock(ActivityStarter.OnDismissAction::class.java),
+                {},
+            )
+            assertThat(kosmos.primaryBouncerInteractor.bouncerDismissAction).isNotNull()
+
+            val dismissCallback = mock(IKeyguardDismissCallback::class.java)
+            kosmos.dismissCallbackRegistry.addCallback(dismissCallback)
+
+            underTest.onBackRequested()
+            kosmos.fakeExecutor.runAllReady()
+            verify(statusBarKeyguardViewManager).hideAlternateBouncer(any())
+            verify(dismissCallback).onDismissCancelled()
+            assertThat(kosmos.primaryBouncerInteractor.bouncerDismissAction).isNull()
+        }
+
+    @Test
     fun transitionToAlternateBouncer_scrimAlphaUpdate() =
         testScope.runTest {
             val scrimAlphas by collectValues(underTest.scrimAlpha)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index fc7f693..1929cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.dock.DockManagerFake
 import com.android.systemui.doze.util.BurnInHelperWrapper
 import com.android.systemui.flags.FakeFeatureFlags
@@ -53,6 +54,8 @@
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -68,13 +71,11 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlin.math.max
 import kotlin.math.min
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -92,6 +93,11 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
+
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
     @Mock private lateinit var lockPatternUtils: LockPatternUtils
@@ -107,7 +113,6 @@
 
     private lateinit var underTest: KeyguardBottomAreaViewModel
 
-    private lateinit var testScope: TestScope
     private lateinit var repository: FakeKeyguardRepository
     private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
     private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -115,8 +120,6 @@
     private lateinit var dockManager: DockManagerFake
     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
 
-    private val kosmos = testKosmos()
-
     init {
         mSetFlagsRule.setFlagsParameterization(flags)
     }
@@ -152,9 +155,7 @@
         dockManager = DockManagerFake()
         biometricSettingsRepository = FakeBiometricSettingsRepository()
         val featureFlags =
-            FakeFeatureFlags().apply {
-                set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false)
-            }
+            FakeFeatureFlags().apply { set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) }
 
         val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags)
         val keyguardInteractor = withDeps.keyguardInteractor
@@ -163,8 +164,6 @@
         whenever(userTracker.userHandle).thenReturn(mock())
         whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
             .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
-        val testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
         val localUserSelectionManager =
             KeyguardQuickAffordanceLocalUserSelectionManager(
                 context = context,
@@ -200,7 +199,7 @@
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = testDispatcher,
-                        secureSettings = FakeSettings(),
+                        secureSettings = settings,
                         selectionsManager = localUserSelectionManager,
                     ),
                 configs =
@@ -223,6 +222,7 @@
                 broadcastDispatcher = broadcastDispatcher,
                 accessibilityManager = accessibilityManager,
                 pulsingGestureListener = kosmos.pulsingGestureListener,
+                faceAuthInteractor = kosmos.deviceEntryFaceAuthInteractor,
             )
         underTest =
             KeyguardBottomAreaViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 24bea2c..720f2e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -20,6 +20,7 @@
 import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
@@ -46,8 +47,11 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
@@ -55,7 +59,10 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setTransition
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -65,7 +72,7 @@
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth
 import kotlin.math.min
 import kotlin.test.assertEquals
@@ -87,6 +94,10 @@
 @RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
+
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var expandable: Expandable
@@ -143,13 +154,9 @@
     @Mock
     private lateinit var glanceableHubToLockscreenTransitionViewModel:
         GlanceableHubToLockscreenTransitionViewModel
-    @Mock private lateinit var transitionInteractor: KeyguardTransitionInteractor
-
-    private val kosmos = testKosmos()
 
     private lateinit var underTest: KeyguardQuickAffordancesCombinedViewModel
 
-    private val testScope = kosmos.testScope
     private lateinit var repository: FakeKeyguardRepository
     private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
     private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -162,8 +169,6 @@
     // the viewModel does a `map { 1 - it }` on this value, which is why it's different
     private val intendedShadeAlphaMutableStateFlow: MutableStateFlow<Float> = MutableStateFlow(0f)
 
-    private val intendedFinishedKeyguardStateFlow = MutableStateFlow(KeyguardState.LOCKSCREEN)
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -240,7 +245,7 @@
                     KeyguardQuickAffordanceLegacySettingSyncer(
                         scope = testScope.backgroundScope,
                         backgroundDispatcher = kosmos.testDispatcher,
-                        secureSettings = FakeSettings(),
+                        secureSettings = settings,
                         selectionsManager = localUserSelectionManager,
                     ),
                 configs =
@@ -255,7 +260,6 @@
 
         intendedAlphaMutableStateFlow.value = 1f
         intendedShadeAlphaMutableStateFlow.value = 0f
-        intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
         whenever(aodToLockscreenTransitionViewModel.shortcutsAlpha)
             .thenReturn(intendedAlphaMutableStateFlow)
         whenever(dozingToLockscreenTransitionViewModel.shortcutsAlpha).thenReturn(emptyFlow())
@@ -282,8 +286,6 @@
         whenever(glanceableHubToLockscreenTransitionViewModel.shortcutsAlpha)
             .thenReturn(emptyFlow())
         whenever(shadeInteractor.anyExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
-        whenever(transitionInteractor.finishedKeyguardState)
-            .thenReturn(intendedFinishedKeyguardStateFlow)
 
         underTest =
             KeyguardQuickAffordancesCombinedViewModel(
@@ -333,7 +335,7 @@
                     lockscreenToPrimaryBouncerTransitionViewModel,
                 lockscreenToGlanceableHubTransitionViewModel =
                     lockscreenToGlanceableHubTransitionViewModel,
-                transitionInteractor = transitionInteractor,
+                transitionInteractor = kosmos.keyguardTransitionInteractor,
             )
     }
 
@@ -402,6 +404,67 @@
         }
 
     @Test
+    @EnableFlags(com.android.systemui.shared.Flags.FLAG_NEW_CUSTOMIZATION_PICKER_UI)
+    fun startButton_inPreviewMode_onPreviewQuickAffordanceSelected() =
+        testScope.runTest {
+            underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
+            underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true)
+
+            repository.setKeyguardShowing(false)
+            val latest = collectLastValue(underTest.startButton)
+
+            val icon: Icon = mock()
+            val testConfig =
+                TestConfig(
+                    isVisible = true,
+                    isClickable = true,
+                    isActivated = true,
+                    icon = icon,
+                    canShowWhileLocked = false,
+                    intent = null,
+                    slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(),
+                )
+            val defaultConfigKey =
+                setUpQuickAffordanceModel(
+                    position = KeyguardQuickAffordancePosition.BOTTOM_START,
+                    testConfig = testConfig,
+                )
+
+            // Set up the quick access wallet config
+            val quickAccessWalletAffordanceConfigKey =
+                quickAccessWalletAffordanceConfig
+                    .apply {
+                        onTriggeredResult =
+                            KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
+                                intent = Intent("action"),
+                                canShowWhileLocked = false,
+                            )
+                        setState(
+                            KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+                                icon = icon,
+                                activationState = ActivationState.Active,
+                            )
+                        )
+                    }
+                    .let {
+                        KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId() +
+                            "::${quickAccessWalletAffordanceConfig.key}"
+                    }
+
+            // onPreviewQuickAffordanceSelected should trigger the override with the quick access
+            // wallet quick affordance
+            underTest.onPreviewQuickAffordanceSelected(
+                KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
+                BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
+            )
+            Truth.assertThat(latest()?.configKey).isEqualTo(quickAccessWalletAffordanceConfigKey)
+
+            // onClearPreviewQuickAffordances should make the default quick affordance shows again
+            underTest.onClearPreviewQuickAffordances()
+            Truth.assertThat(latest()?.configKey).isEqualTo(defaultConfigKey)
+        }
+
+    @Test
     fun startButton_inPreviewMode_visibleEvenWhenKeyguardNotShowing() =
         testScope.runTest {
             underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
@@ -445,7 +508,7 @@
         }
 
     @Test
-    fun endButton_inHiglightedPreviewMode_dimmedWhenOtherIsSelected() =
+    fun endButton_inHighlightedPreviewMode_dimmedWhenOtherIsSelected() =
         testScope.runTest {
             underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START)
             underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true)
@@ -714,7 +777,10 @@
     @Test
     fun shadeExpansionAlpha_changes_whenOnLockscreen() =
         testScope.runTest {
-            intendedFinishedKeyguardStateFlow.value = KeyguardState.LOCKSCREEN
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Lockscreen),
+                stateTransition = TransitionStep(from = AOD, to = LOCKSCREEN)
+            )
             intendedShadeAlphaMutableStateFlow.value = 0.25f
             val underTest = collectLastValue(underTest.transitionAlpha)
             assertEquals(0.75f, underTest())
@@ -726,7 +792,10 @@
     @Test
     fun shadeExpansionAlpha_alwaysZero_whenNotOnLockscreen() =
         testScope.runTest {
-            intendedFinishedKeyguardStateFlow.value = KeyguardState.GONE
+            kosmos.setTransition(
+                sceneTransition = Idle(Scenes.Gone),
+                stateTransition = TransitionStep(from = AOD, to = GONE)
+            )
             intendedShadeAlphaMutableStateFlow.value = 0.5f
             val underTest = collectLastValue(underTest.transitionAlpha)
             assertEquals(0f, underTest())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.kt
new file mode 100644
index 0000000..2ba670c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/ActivatableTest.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.lifecycle
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.test.junit4.createComposeRule
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ActivatableTest : SysuiTestCase() {
+
+    @get:Rule val composeRule = createComposeRule()
+
+    @Test
+    fun rememberActivated() {
+        val keepAliveMutable = mutableStateOf(true)
+        var isActive = false
+        composeRule.setContent {
+            val keepAlive by keepAliveMutable
+            if (keepAlive) {
+                rememberActivated("test") {
+                    FakeActivatable(
+                        onActivation = { isActive = true },
+                        onDeactivation = { isActive = false },
+                    )
+                }
+            }
+        }
+        assertThat(isActive).isTrue()
+    }
+
+    @Test
+    fun rememberActivated_leavingTheComposition() {
+        val keepAliveMutable = mutableStateOf(true)
+        var isActive = false
+        composeRule.setContent {
+            val keepAlive by keepAliveMutable
+            if (keepAlive) {
+                rememberActivated("name") {
+                    FakeActivatable(
+                        onActivation = { isActive = true },
+                        onDeactivation = { isActive = false },
+                    )
+                }
+            }
+        }
+
+        // Tear down the composable.
+        composeRule.runOnUiThread { keepAliveMutable.value = false }
+        composeRule.waitForIdle()
+
+        assertThat(isActive).isFalse()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
new file mode 100644
index 0000000..73f724e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/SysUiViewModelTest.kt
@@ -0,0 +1,170 @@
+/*
+ * 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.lifecycle
+
+import android.view.View
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.Assert
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
+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.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SysUiViewModelTest : SysuiTestCase() {
+
+    @get:Rule val composeRule = createComposeRule()
+
+    @Test
+    fun rememberActivated() {
+        val keepAliveMutable = mutableStateOf(true)
+        var isActive = false
+        composeRule.setContent {
+            val keepAlive by keepAliveMutable
+            if (keepAlive) {
+                rememberViewModel("test") {
+                    FakeSysUiViewModel(
+                        onActivation = { isActive = true },
+                        onDeactivation = { isActive = false },
+                    )
+                }
+            }
+        }
+        assertThat(isActive).isTrue()
+    }
+
+    @Test
+    fun rememberActivated_withKey() {
+        val keyMutable = mutableStateOf(1)
+        var isActive1 = false
+        var isActive2 = false
+        composeRule.setContent {
+            val key by keyMutable
+            // Need to explicitly state the type to avoid a weird issue where the factory seems to
+            // return Unit instead of FakeSysUiViewModel. It might be an issue with the compose
+            // compiler.
+            val unused: FakeSysUiViewModel =
+                rememberViewModel("test", key) {
+                    when (key) {
+                        1 ->
+                            FakeSysUiViewModel(
+                                onActivation = { isActive1 = true },
+                                onDeactivation = { isActive1 = false },
+                            )
+                        2 ->
+                            FakeSysUiViewModel(
+                                onActivation = { isActive2 = true },
+                                onDeactivation = { isActive2 = false },
+                            )
+                        else -> error("unsupported key $key")
+                    }
+                }
+        }
+        assertThat(isActive1).isTrue()
+        assertThat(isActive2).isFalse()
+
+        composeRule.runOnUiThread { keyMutable.value = 2 }
+        composeRule.waitForIdle()
+        assertThat(isActive1).isFalse()
+        assertThat(isActive2).isTrue()
+
+        composeRule.runOnUiThread { keyMutable.value = 1 }
+        composeRule.waitForIdle()
+        assertThat(isActive1).isTrue()
+        assertThat(isActive2).isFalse()
+    }
+
+    @Test
+    fun rememberActivated_leavingTheComposition() {
+        val keepAliveMutable = mutableStateOf(true)
+        var isActive = false
+        composeRule.setContent {
+            val keepAlive by keepAliveMutable
+            if (keepAlive) {
+                rememberViewModel("test") {
+                    FakeSysUiViewModel(
+                        onActivation = { isActive = true },
+                        onDeactivation = { isActive = false },
+                    )
+                }
+            }
+        }
+
+        // Tear down the composable.
+        composeRule.runOnUiThread { keepAliveMutable.value = false }
+        composeRule.waitForIdle()
+
+        assertThat(isActive).isFalse()
+    }
+
+    @Test
+    fun viewModel_viewBinder() = runTest {
+        Assert.setTestThread(Thread.currentThread())
+
+        val view: View = mock { on { isAttachedToWindow } doReturn false }
+        val viewModel = FakeViewModel()
+        backgroundScope.launch {
+            view.viewModel(
+                traceName = "test",
+                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+                factory = { viewModel },
+            ) {
+                awaitCancellation()
+            }
+        }
+        runCurrent()
+
+        assertThat(viewModel.isActivated).isFalse()
+
+        view.stub { on { isAttachedToWindow } doReturn true }
+        argumentCaptor<View.OnAttachStateChangeListener>()
+            .apply { verify(view).addOnAttachStateChangeListener(capture()) }
+            .allValues
+            .forEach { it.onViewAttachedToWindow(view) }
+        runCurrent()
+
+        assertThat(viewModel.isActivated).isTrue()
+    }
+}
+
+private class FakeViewModel : ExclusiveActivatable() {
+    var isActivated = false
+
+    override suspend fun onActivated(): Nothing {
+        isActivated = true
+        try {
+            awaitCancellation()
+        } finally {
+            isActivated = false
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index e55cb12..030b172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -19,8 +19,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogcatEchoTrackerAlways
 import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
-import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
@@ -57,9 +57,7 @@
                 MAX_SIZE,
                 BUFFER_NAME,
                 systemClock,
-                mock(),
-                testDispatcher,
-                testScope.backgroundScope,
+                LogcatEchoTrackerAlways(),
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
index 8c62bc2..dfd964f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferFactoryTest.kt
@@ -20,25 +20,20 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.LogcatEchoTrackerAlways
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class TableLogBufferFactoryTest : SysuiTestCase() {
     private val dumpManager: DumpManager = mock()
     private val systemClock = FakeSystemClock()
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
     private val underTest =
-        TableLogBufferFactory(dumpManager, systemClock, mock(), testDispatcher, testScope)
+        TableLogBufferFactory(dumpManager, systemClock, LogcatEchoTrackerAlways())
 
     @Test
     fun create_alwaysCreatesNewInstance() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index ace562b..9c4c862 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -23,22 +23,18 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
 import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class TableLogBufferTest : SysuiTestCase() {
@@ -49,9 +45,6 @@
     private lateinit var logcatEchoTracker: LogcatEchoTracker
     private lateinit var localLogcat: FakeLogProxy
 
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
     @Before
     fun setup() {
         localLogcat = FakeLogProxy()
@@ -65,8 +58,6 @@
                 NAME,
                 systemClock,
                 logcatEchoTracker,
-                testDispatcher,
-                testScope.backgroundScope,
                 localLogcat = localLogcat,
             )
     }
@@ -78,8 +69,6 @@
             "name",
             systemClock,
             logcatEchoTracker,
-            testDispatcher,
-            testScope.backgroundScope,
             localLogcat = localLogcat,
         )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index bdee936..ad7a5b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -38,19 +38,27 @@
 import android.media.session.PlaybackState
 import android.net.Uri
 import android.os.Bundle
+import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
 import android.testing.TestableLooper.RunWithLooper
 import androidx.media.utils.MediaConstants
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
 import com.android.systemui.InstanceIdSequenceFake
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.Flags.MEDIA_REMOTE_RESUME
+import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
+import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
+import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.domain.resume.MediaResumeListener
 import com.android.systemui.media.controls.domain.resume.ResumeMediaBrowser
 import com.android.systemui.media.controls.shared.model.EXTRA_KEY_TRIGGER_SOURCE
@@ -58,16 +66,21 @@
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.util.MediaControllerFactory
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.mediaFlags
+import com.android.systemui.plugins.activityStarter
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.testKosmos
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -91,6 +104,8 @@
 import org.mockito.kotlin.capture
 import org.mockito.kotlin.eq
 import org.mockito.quality.Strictness
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
@@ -111,13 +126,13 @@
     return Mockito.anyObject<T>()
 }
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class LegacyMediaDataManagerImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
-    @Mock lateinit var mediaControllerFactory: MediaControllerFactory
     @Mock lateinit var controller: MediaController
     @Mock lateinit var transportControls: MediaController.TransportControls
     @Mock lateinit var playbackInfo: MediaController.PlaybackInfo
@@ -136,7 +151,6 @@
     @Mock lateinit var mediaDataFilter: LegacyMediaDataFilterImpl
     @Mock lateinit var listener: MediaDataManager.Listener
     @Mock lateinit var pendingIntent: PendingIntent
-    @Mock lateinit var activityStarter: ActivityStarter
     @Mock lateinit var smartspaceManager: SmartspaceManager
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@@ -144,7 +158,6 @@
     @Mock private lateinit var mediaRecommendationItem: SmartspaceAction
     lateinit var validRecommendationList: List<SmartspaceAction>
     @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
-    @Mock private lateinit var mediaFlags: MediaFlags
     @Mock private lateinit var logger: MediaUiEventLogger
     lateinit var mediaDataManager: LegacyMediaDataManagerImpl
     lateinit var mediaNotification: StatusBarNotification
@@ -159,6 +172,26 @@
     @Mock private lateinit var ugm: IUriGrantsManager
     @Mock private lateinit var imageSource: ImageDecoder.Source
 
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.progressionOf(
+                Flags.FLAG_MEDIA_LOAD_METADATA_VIA_MEDIA_DATA_LOADER
+            )
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
+    private val activityStarter = kosmos.activityStarter
+    private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
     private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
 
     private val originalSmartspaceSetting =
@@ -188,12 +221,16 @@
             Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION,
             1
         )
+
         mediaDataManager =
             LegacyMediaDataManagerImpl(
                 context = context,
                 backgroundExecutor = backgroundExecutor,
+                backgroundDispatcher = testDispatcher,
                 uiExecutor = uiExecutor,
                 foregroundExecutor = foregroundExecutor,
+                mainDispatcher = testDispatcher,
+                applicationScope = testScope,
                 mediaControllerFactory = mediaControllerFactory,
                 broadcastDispatcher = broadcastDispatcher,
                 dumpManager = dumpManager,
@@ -209,10 +246,11 @@
                 useQsMediaPlayer = true,
                 systemClock = clock,
                 tunerService = tunerService,
-                mediaFlags = mediaFlags,
+                mediaFlags = kosmos.mediaFlags,
                 logger = logger,
                 smartspaceManager = smartspaceManager,
                 keyguardUpdateMonitor = keyguardUpdateMonitor,
+                mediaDataLoader = { kosmos.mediaDataLoader },
             )
         verify(tunerService)
             .addTunable(capture(tunableCaptor), eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
@@ -248,7 +286,7 @@
                 putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
             }
         verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor))
-        whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
+        mediaControllerFactory.setControllerForToken(session.sessionToken, controller)
         whenever(controller.transportControls).thenReturn(transportControls)
         whenever(controller.playbackInfo).thenReturn(playbackInfo)
         whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -278,10 +316,10 @@
         whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
         whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
         whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false)
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
         whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false)
     }
@@ -310,49 +348,51 @@
     }
 
     @Test
-    fun testsetInactive_resume_dismissesMedia() {
-        // WHEN resume controls are present, and time out
-        val desc =
-            MediaDescription.Builder().run {
-                setTitle(SESSION_TITLE)
-                build()
-            }
-        mediaDataManager.addResumptionControls(
-            USER_ID,
-            desc,
-            Runnable {},
-            session.sessionToken,
-            APP_NAME,
-            pendingIntent,
-            PACKAGE_NAME
-        )
-
-        backgroundExecutor.runAllReady()
-        foregroundExecutor.runAllReady()
-        verify(listener)
-            .onMediaDataLoaded(
-                eq(PACKAGE_NAME),
-                eq(null),
-                capture(mediaDataCaptor),
-                eq(true),
-                eq(0),
-                eq(false)
+    fun testsetInactive_resume_dismissesMedia() =
+        testScope.runTest {
+            // WHEN resume controls are present, and time out
+            val desc =
+                MediaDescription.Builder().run {
+                    setTitle(SESSION_TITLE)
+                    build()
+                }
+            mediaDataManager.addResumptionControls(
+                USER_ID,
+                desc,
+                Runnable {},
+                session.sessionToken,
+                APP_NAME,
+                pendingIntent,
+                PACKAGE_NAME
             )
 
-        mediaDataManager.setInactive(PACKAGE_NAME, timedOut = true)
-        verify(logger)
-            .logMediaTimeout(anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId))
+            runCurrent()
+            backgroundExecutor.runAllReady()
+            foregroundExecutor.runAllReady()
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(PACKAGE_NAME),
+                    eq(null),
+                    capture(mediaDataCaptor),
+                    eq(true),
+                    eq(0),
+                    eq(false)
+                )
 
-        // THEN it is removed and listeners are informed
-        foregroundExecutor.advanceClockToLast()
-        foregroundExecutor.runAllReady()
-        verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
-    }
+            mediaDataManager.setInactive(PACKAGE_NAME, timedOut = true)
+            verify(logger)
+                .logMediaTimeout(anyInt(), eq(PACKAGE_NAME), eq(mediaDataCaptor.value.instanceId))
+
+            // THEN it is removed and listeners are informed
+            foregroundExecutor.advanceClockToLast()
+            foregroundExecutor.runAllReady()
+            verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
+        }
 
     @Test
     fun testLoadsMetadataOnBackground() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.numPending()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
     }
 
     @Test
@@ -370,8 +410,7 @@
         mediaDataManager.addListener(listener)
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -389,8 +428,7 @@
         mediaDataManager.addListener(listener)
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -417,11 +455,9 @@
 
     @Test
     fun testOnMetaDataLoaded_conservesActiveFlag() {
-        whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller)
         mediaDataManager.addListener(listener)
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -465,8 +501,7 @@
             }
 
         mediaDataManager.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -552,8 +587,7 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
 
         // Then a media control is created with a placeholder title string
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -583,8 +617,7 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
 
         // Then a media control is created with a placeholder title string
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -625,8 +658,7 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
 
         // Then the media control is added using the notification's title
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -734,8 +766,7 @@
         // GIVEN that the manager has two notifications with resume actions
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         mediaDataManager.onNotificationAdded(KEY_2, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+        testScope.assertRunAllReady(foreground = 2, background = 2)
 
         verify(listener)
             .onMediaDataLoaded(
@@ -822,7 +853,7 @@
     @Test
     fun testOnNotificationRemoved_withResumption_isRemoteAndRemoteAllowed() {
         // With the flag enabled to allow remote media to resume
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true)
 
         // GIVEN that the manager has a notification with a resume action, but is not local
         whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -853,7 +884,7 @@
     @Test
     fun testOnNotificationRemoved_withResumption_isRcnAndRemoteAllowed() {
         // With the flag enabled to allow remote media to resume
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true)
 
         // GIVEN that the manager has a remote cast notification
         addNotificationAndLoad(remoteCastNotification)
@@ -972,7 +1003,7 @@
 
     @Test
     fun testAddResumptionControls_hasPartialProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added with partial progress
         val progress = 0.5
@@ -999,7 +1030,7 @@
 
     @Test
     fun testAddResumptionControls_hasNotPlayedProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have not been played
         val extras =
@@ -1024,7 +1055,7 @@
 
     @Test
     fun testAddResumptionControls_hasFullProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added with progress info
         val extras =
@@ -1050,7 +1081,7 @@
 
     @Test
     fun testAddResumptionControls_hasNoExtras() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that do not have any extras
         val desc =
@@ -1068,7 +1099,7 @@
 
     @Test
     fun testAddResumptionControls_hasEmptyTitle() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have empty title
         val desc =
@@ -1087,8 +1118,7 @@
         )
 
         // Resumption controls are not added.
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(0)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
         verify(listener, never())
             .onMediaDataLoaded(
                 eq(PACKAGE_NAME),
@@ -1102,7 +1132,7 @@
 
     @Test
     fun testAddResumptionControls_hasBlankTitle() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have a blank title
         val desc =
@@ -1121,8 +1151,7 @@
         )
 
         // Resumption controls are not added.
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(0)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
         verify(listener, never())
             .onMediaDataLoaded(
                 eq(PACKAGE_NAME),
@@ -1189,8 +1218,7 @@
         mediaDataManager.onNotificationAdded(KEY, notif)
 
         // THEN it still loads
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -1307,7 +1335,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_headphoneTrigger_isActive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
 
@@ -1333,7 +1361,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_periodicTrigger_notActive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
         val extras =
             Bundle().apply {
                 putString("package_name", PACKAGE_NAME)
@@ -1367,7 +1395,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_noTargets_inactive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
 
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
@@ -1399,7 +1427,7 @@
 
     @Test
     fun testSetRecommendationInactive_notifiesListeners() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
 
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
@@ -1479,8 +1507,7 @@
     fun testOnMediaDataTimedOut_updatesLastActiveTime() {
         // GIVEN that the manager has a notification
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // WHEN the notification times out
         clock.advanceTime(100)
@@ -1588,8 +1615,7 @@
 
         // WHEN the notification is loaded
         mediaDataManager.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // THEN only the first MAX_COMPACT_ACTIONS are actually set
         verify(listener)
@@ -1624,8 +1650,7 @@
 
         // WHEN the notification is loaded
         mediaDataManager.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // THEN only the first MAX_NOTIFICATION_ACTIONS are actually included
         verify(listener)
@@ -1644,7 +1669,6 @@
     @Test
     fun testPlaybackActions_noState_usesNotification() {
         val desc = "Notification Action"
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         whenever(controller.playbackState).thenReturn(null)
 
         val notifWithAction =
@@ -1659,8 +1683,7 @@
             }
         mediaDataManager.onNotificationAdded(KEY, notifWithAction)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -1679,7 +1702,6 @@
     @Test
     fun testPlaybackActions_hasPrevNext() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions =
             PlaybackState.ACTION_PLAY or
                 PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1723,7 +1745,6 @@
     @Test
     fun testPlaybackActions_noPrevNext_usesCustom() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         customDesc.forEach {
@@ -1755,7 +1776,6 @@
 
     @Test
     fun testPlaybackActions_connecting() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder =
             PlaybackState.Builder()
@@ -1776,7 +1796,6 @@
     @Test
     fun testPlaybackActions_reservedSpace() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         customDesc.forEach {
@@ -1814,7 +1833,6 @@
 
     @Test
     fun testPlaybackActions_playPause_hasButton() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY_PAUSE
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -1851,8 +1869,7 @@
 
         // update to remote cast
         mediaDataManager.onNotificationAdded(KEY, remoteCastNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(logger)
             .logPlaybackLocationChange(
                 anyInt(),
@@ -1914,7 +1931,6 @@
 
     @Test
     fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
         whenever(controller.playbackState).thenReturn(state)
 
@@ -1935,46 +1951,48 @@
     }
 
     @Test
-    fun testPlaybackState_PauseStateAfterAddingResumption_keyExists_callsListener() {
-        val desc =
-            MediaDescription.Builder().run {
-                setTitle(SESSION_TITLE)
-                build()
-            }
-        val state =
-            PlaybackState.Builder()
-                .setState(PlaybackState.STATE_PAUSED, 0L, 1f)
-                .setActions(PlaybackState.ACTION_PLAY_PAUSE)
-                .build()
+    fun testPlaybackState_PauseStateAfterAddingResumption_keyExists_callsListener() =
+        testScope.runTest {
+            val desc =
+                MediaDescription.Builder().run {
+                    setTitle(SESSION_TITLE)
+                    build()
+                }
+            val state =
+                PlaybackState.Builder()
+                    .setState(PlaybackState.STATE_PAUSED, 0L, 1f)
+                    .setActions(PlaybackState.ACTION_PLAY_PAUSE)
+                    .build()
 
-        // Add resumption controls in order to have semantic actions.
-        // To make sure that they are not null after changing state.
-        mediaDataManager.addResumptionControls(
-            USER_ID,
-            desc,
-            Runnable {},
-            session.sessionToken,
-            APP_NAME,
-            pendingIntent,
-            PACKAGE_NAME
-        )
-        backgroundExecutor.runAllReady()
-        foregroundExecutor.runAllReady()
-
-        stateCallbackCaptor.value.invoke(PACKAGE_NAME, state)
-
-        verify(listener)
-            .onMediaDataLoaded(
-                eq(PACKAGE_NAME),
-                eq(PACKAGE_NAME),
-                capture(mediaDataCaptor),
-                eq(true),
-                eq(0),
-                eq(false)
+            // Add resumption controls in order to have semantic actions.
+            // To make sure that they are not null after changing state.
+            mediaDataManager.addResumptionControls(
+                USER_ID,
+                desc,
+                Runnable {},
+                session.sessionToken,
+                APP_NAME,
+                pendingIntent,
+                PACKAGE_NAME
             )
-        assertThat(mediaDataCaptor.value.isPlaying).isFalse()
-        assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
-    }
+            runCurrent()
+            backgroundExecutor.runAllReady()
+            foregroundExecutor.runAllReady()
+
+            stateCallbackCaptor.value.invoke(PACKAGE_NAME, state)
+
+            verify(listener)
+                .onMediaDataLoaded(
+                    eq(PACKAGE_NAME),
+                    eq(PACKAGE_NAME),
+                    capture(mediaDataCaptor),
+                    eq(true),
+                    eq(0),
+                    eq(false)
+                )
+            assertThat(mediaDataCaptor.value.isPlaying).isFalse()
+            assertThat(mediaDataCaptor.value.semanticActions).isNotNull()
+        }
 
     @Test
     fun testPlaybackStateNull_Pause_keyExists_callsListener() {
@@ -2036,7 +2054,7 @@
 
     @Test
     fun testRetain_notifPlayer_notifRemoved_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added, times out, and then removed
         addNotificationAndLoad()
@@ -2066,7 +2084,7 @@
 
     @Test
     fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added and times out
         addNotificationAndLoad()
@@ -2084,7 +2102,7 @@
 
     @Test
     fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added and then removed, without timing out
         addNotificationAndLoad()
@@ -2101,7 +2119,7 @@
 
     @Test
     fun testRetain_canResume_removeWhileActive_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control that supports resumption is added
         addNotificationAndLoad()
@@ -2133,8 +2151,7 @@
 
     @Test
     fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2153,8 +2170,7 @@
 
     @Test
     fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2187,8 +2203,7 @@
 
     @Test
     fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control using session actions is added, and then the session is destroyed
@@ -2207,8 +2222,7 @@
 
     @Test
     fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control using session actions and that does allow resumption is added,
@@ -2241,7 +2255,6 @@
 
     @Test
     fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2268,7 +2281,6 @@
 
     @Test
     fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control using session actions is added, and then the session is destroyed
@@ -2287,7 +2299,6 @@
 
     @Test
     fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control using session actions and that does allow resumption is added,
@@ -2320,8 +2331,7 @@
 
     @Test
     fun testSessionDestroyed_noNotificationKey_stillRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a notiifcation is added and then removed before it is fully processed
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
@@ -2392,6 +2402,23 @@
         assertThat(mediaDataCaptor.value.artwork).isNull()
     }
 
+    private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
+        runCurrent()
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            // It doesn't make much sense to count tasks when we use coroutines in loader
+            // so this check is skipped in that scenario.
+            backgroundExecutor.runAllReady()
+            foregroundExecutor.runAllReady()
+        } else {
+            if (background > 0) {
+                assertThat(backgroundExecutor.runAllReady()).isEqualTo(background)
+            }
+            if (foreground > 0) {
+                assertThat(foregroundExecutor.runAllReady()).isEqualTo(foreground)
+            }
+        }
+    }
+
     /** Helper function to add a basic media notification and capture the resulting MediaData */
     private fun addNotificationAndLoad() {
         addNotificationAndLoad(mediaNotification)
@@ -2400,8 +2427,7 @@
     /** Helper function to add the given notification and capture the resulting MediaData */
     private fun addNotificationAndLoad(sbn: StatusBarNotification) {
         mediaDataManager.onNotificationAdded(KEY, sbn)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -2435,8 +2461,8 @@
             pendingIntent,
             packageName
         )
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         verify(listener)
             .onMediaDataLoaded(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 3b541cd..c0f503d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -38,22 +38,33 @@
 import android.media.session.PlaybackState
 import android.net.Uri
 import android.os.Bundle
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
-import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.media.utils.MediaConstants
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags
 import com.android.systemui.InstanceIdSequenceFake
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.controls.data.repository.MediaDataRepository
-import com.android.systemui.media.controls.data.repository.MediaFilterRepository
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags.MEDIA_REMOTE_RESUME
+import com.android.systemui.flags.Flags.MEDIA_RESUME_PROGRESS
+import com.android.systemui.flags.Flags.MEDIA_RETAIN_RECOMMENDATIONS
+import com.android.systemui.flags.Flags.MEDIA_RETAIN_SESSIONS
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.data.repository.mediaDataRepository
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.media.controls.domain.resume.MediaResumeListener
@@ -63,22 +74,21 @@
 import com.android.systemui.media.controls.shared.model.MediaData
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaData
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.util.MediaControllerFactory
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.mediaFlags
+import com.android.systemui.plugins.activityStarter
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.statusbar.notificationLockscreenUserManager
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.android.systemui.util.time.FakeSystemClock
-import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -102,6 +112,8 @@
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.whenever
 import org.mockito.quality.Strictness
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
@@ -125,12 +137,15 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidJUnit4::class)
-class MediaDataProcessorTest : SysuiTestCase() {
-    val kosmos = testKosmos()
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableSceneContainer
+class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val settings = kosmos.fakeSettings
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
-    @Mock lateinit var mediaControllerFactory: MediaControllerFactory
     @Mock lateinit var controller: MediaController
     @Mock lateinit var transportControls: MediaController.TransportControls
     @Mock lateinit var playbackInfo: MediaController.PlaybackInfo
@@ -146,10 +161,8 @@
     @Mock lateinit var mediaSessionBasedFilter: MediaSessionBasedFilter
     @Mock lateinit var mediaDeviceManager: MediaDeviceManager
     @Mock lateinit var mediaDataCombineLatest: MediaDataCombineLatest
-    @Mock lateinit var mediaDataFilter: MediaDataFilterImpl
     @Mock lateinit var listener: MediaDataManager.Listener
     @Mock lateinit var pendingIntent: PendingIntent
-    @Mock lateinit var activityStarter: ActivityStarter
     @Mock lateinit var smartspaceManager: SmartspaceManager
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     private lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
@@ -157,7 +170,6 @@
     @Mock private lateinit var mediaRecommendationItem: SmartspaceAction
     private lateinit var validRecommendationList: List<SmartspaceAction>
     @Mock private lateinit var mediaSmartspaceBaseAction: SmartspaceAction
-    @Mock private lateinit var mediaFlags: MediaFlags
     @Mock private lateinit var logger: MediaUiEventLogger
     private lateinit var mediaCarouselInteractor: MediaCarouselInteractor
     private lateinit var mediaDataProcessor: MediaDataProcessor
@@ -170,13 +182,28 @@
     @Captor lateinit var smartSpaceConfigBuilderCaptor: ArgumentCaptor<SmartspaceConfig>
     @Mock private lateinit var ugm: IUriGrantsManager
     @Mock private lateinit var imageSource: ImageDecoder.Source
-    private lateinit var mediaDataRepository: MediaDataRepository
-    private lateinit var testScope: TestScope
-    private lateinit var testDispatcher: TestDispatcher
-    private lateinit var testableLooper: TestableLooper
-    private lateinit var fakeHandler: FakeHandler
 
-    private val settings = FakeSettings()
+    companion object {
+        @JvmStatic
+        @Parameters(name = "{0}")
+        fun getParams(): List<FlagsParameterization> {
+            return FlagsParameterization.progressionOf(
+                Flags.FLAG_MEDIA_LOAD_METADATA_VIA_MEDIA_DATA_LOADER
+            )
+        }
+    }
+
+    init {
+        mSetFlagsRule.setFlagsParameterization(flags)
+    }
+
+    private val fakeFeatureFlags = kosmos.fakeFeatureFlagsClassic
+    private val activityStarter = kosmos.activityStarter
+    private val mediaControllerFactory = kosmos.fakeMediaControllerFactory
+    private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
+    private val mediaFilterRepository = kosmos.mediaFilterRepository
+    private val mediaDataFilter = kosmos.mediaDataFilter
+
     private val instanceIdSequence = InstanceIdSequenceFake(1 shl 20)
 
     private val originalSmartspaceSetting =
@@ -185,14 +212,11 @@
             Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION,
             1
         )
-    private val mediaFilterRepository: MediaFilterRepository = kosmos.mediaFilterRepository
 
     private lateinit var staticMockSession: MockitoSession
 
     @Before
     fun setup() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         staticMockSession =
             ExtendedMockito.mockitoSession()
                 .mockStatic<UriGrantsManager>(UriGrantsManager::class.java)
@@ -203,17 +227,12 @@
         foregroundExecutor = FakeExecutor(clock)
         backgroundExecutor = FakeExecutor(clock)
         uiExecutor = FakeExecutor(clock)
-        testableLooper = TestableLooper.get(this)
-        fakeHandler = FakeHandler(testableLooper.looper)
         smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
         Settings.Secure.putInt(
             context.contentResolver,
             Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION,
             1
         )
-        testDispatcher = UnconfinedTestDispatcher()
-        testScope = TestScope(testDispatcher)
-        mediaDataRepository = MediaDataRepository(mediaFlags, dumpManager)
         mediaDataProcessor =
             MediaDataProcessor(
                 context = context,
@@ -222,7 +241,7 @@
                 backgroundExecutor = backgroundExecutor,
                 uiExecutor = uiExecutor,
                 foregroundExecutor = foregroundExecutor,
-                handler = fakeHandler,
+                mainDispatcher = testDispatcher,
                 mediaControllerFactory = mediaControllerFactory,
                 broadcastDispatcher = broadcastDispatcher,
                 dumpManager = dumpManager,
@@ -232,13 +251,15 @@
                 useQsMediaPlayer = true,
                 systemClock = clock,
                 secureSettings = settings,
-                mediaFlags = mediaFlags,
+                mediaFlags = kosmos.mediaFlags,
                 logger = logger,
                 smartspaceManager = smartspaceManager,
                 keyguardUpdateMonitor = keyguardUpdateMonitor,
-                mediaDataRepository = mediaDataRepository,
+                mediaDataRepository = kosmos.mediaDataRepository,
+                mediaDataLoader = { kosmos.mediaDataLoader },
             )
         mediaDataProcessor.start()
+        testScope.runCurrent()
         mediaCarouselInteractor =
             MediaCarouselInteractor(
                 applicationScope = testScope.backgroundScope,
@@ -250,7 +271,7 @@
                 mediaDataCombineLatest = mediaDataCombineLatest,
                 mediaDataFilter = mediaDataFilter,
                 mediaFilterRepository = mediaFilterRepository,
-                mediaFlags = mediaFlags
+                mediaFlags = kosmos.mediaFlags
             )
         mediaCarouselInteractor.start()
         verify(mediaTimeoutListener).stateCallback = capture(stateCallbackCaptor)
@@ -258,6 +279,7 @@
         session = MediaSession(context, "MediaDataProcessorTestSession")
         mediaNotification =
             SbnBuilder().run {
+                setUser(UserHandle(USER_ID))
                 setPkg(PACKAGE_NAME)
                 modifyNotification(context).also {
                     it.setSmallIcon(android.R.drawable.ic_media_pause)
@@ -285,7 +307,7 @@
                 putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
             }
         verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor))
-        whenever(mediaControllerFactory.create(eq(session.sessionToken))).thenReturn(controller)
+        mediaControllerFactory.setControllerForToken(session.sessionToken, controller)
         whenever(controller.transportControls).thenReturn(transportControls)
         whenever(controller.playbackInfo).thenReturn(playbackInfo)
         whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -315,10 +337,10 @@
         whenever(mediaSmartspaceTarget.iconGrid).thenReturn(validRecommendationList)
         whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(SMARTSPACE_CREATION_TIME)
         whenever(mediaSmartspaceTarget.expiryTimeMillis).thenReturn(SMARTSPACE_EXPIRY_TIME)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false)
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false)
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, false)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, false)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, false)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, false)
         whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId())
         whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false)
     }
@@ -364,6 +386,7 @@
             PACKAGE_NAME
         )
 
+        testScope.runCurrent()
         backgroundExecutor.runAllReady()
         foregroundExecutor.runAllReady()
         verify(listener)
@@ -389,7 +412,7 @@
     @Test
     fun testLoadsMetadataOnBackground() {
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.numPending()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
     }
 
     @Test
@@ -406,8 +429,7 @@
 
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -424,8 +446,7 @@
     fun testOnMetaDataLoaded_withoutExplicitIndicator() {
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -452,10 +473,8 @@
 
     @Test
     fun testOnMetaDataLoaded_conservesActiveFlag() {
-        whenever(mediaControllerFactory.create(anyObject())).thenReturn(controller)
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -499,8 +518,7 @@
             }
 
         mediaDataProcessor.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -586,8 +604,7 @@
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
 
         // Then a media control is created with a placeholder title string
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -617,8 +634,7 @@
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
 
         // Then a media control is created with a placeholder title string
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -659,8 +675,7 @@
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
 
         // Then the media control is added using the notification's title
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -768,8 +783,7 @@
         // GIVEN that the manager has two notifications with resume actions
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
         mediaDataProcessor.onNotificationAdded(KEY_2, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
+        testScope.assertRunAllReady(foreground = 2, background = 2)
 
         verify(listener)
             .onMediaDataLoaded(
@@ -856,7 +870,7 @@
     @Test
     fun testOnNotificationRemoved_withResumption_isRemoteAndRemoteAllowed() {
         // With the flag enabled to allow remote media to resume
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true)
 
         // GIVEN that the manager has a notification with a resume action, but is not local
         whenever(controller.metadata).thenReturn(metadataBuilder.build())
@@ -887,7 +901,7 @@
     @Test
     fun testOnNotificationRemoved_withResumption_isRcnAndRemoteAllowed() {
         // With the flag enabled to allow remote media to resume
-        whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_REMOTE_RESUME, true)
 
         // GIVEN that the manager has a remote cast notification
         addNotificationAndLoad(remoteCastNotification)
@@ -1006,7 +1020,7 @@
 
     @Test
     fun testAddResumptionControls_hasPartialProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added with partial progress
         val progress = 0.5
@@ -1033,7 +1047,7 @@
 
     @Test
     fun testAddResumptionControls_hasNotPlayedProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have not been played
         val extras =
@@ -1058,7 +1072,7 @@
 
     @Test
     fun testAddResumptionControls_hasFullProgress() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added with progress info
         val extras =
@@ -1084,7 +1098,7 @@
 
     @Test
     fun testAddResumptionControls_hasNoExtras() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that do not have any extras
         val desc =
@@ -1102,7 +1116,7 @@
 
     @Test
     fun testAddResumptionControls_hasEmptyTitle() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have empty title
         val desc =
@@ -1121,8 +1135,7 @@
         )
 
         // Resumption controls are not added.
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(0)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
         verify(listener, never())
             .onMediaDataLoaded(
                 eq(PACKAGE_NAME),
@@ -1136,7 +1149,7 @@
 
     @Test
     fun testAddResumptionControls_hasBlankTitle() {
-        whenever(mediaFlags.isResumeProgressEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RESUME_PROGRESS, true)
 
         // WHEN resumption controls are added that have a blank title
         val desc =
@@ -1155,8 +1168,7 @@
         )
 
         // Resumption controls are not added.
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(0)
+        testScope.assertRunAllReady(foreground = 0, background = 1)
         verify(listener, never())
             .onMediaDataLoaded(
                 eq(PACKAGE_NAME),
@@ -1223,8 +1235,7 @@
         mediaDataProcessor.onNotificationAdded(KEY, notif)
 
         // THEN it still loads
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -1341,7 +1352,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_headphoneTrigger_isActive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
 
@@ -1367,7 +1378,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_periodicTrigger_notActive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
         val extras =
             Bundle().apply {
                 putString("package_name", PACKAGE_NAME)
@@ -1401,7 +1412,7 @@
 
     @Test
     fun testOnSmartspaceMediaDataLoaded_persistentEnabled_noTargets_inactive() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
 
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
@@ -1433,7 +1444,7 @@
 
     @Test
     fun testSetRecommendationInactive_notifiesListeners() {
-        whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_RECOMMENDATIONS, true)
 
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
         val instanceId = instanceIdSequence.lastInstanceId
@@ -1466,6 +1477,7 @@
     fun testOnSmartspaceMediaDataLoaded_settingDisabled_doesNothing() {
         // WHEN media recommendation setting is off
         settings.putInt(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+        testScope.runCurrent()
 
         smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
 
@@ -1483,6 +1495,7 @@
 
         // WHEN the media recommendation setting is turned off
         settings.putInt(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+        testScope.runCurrent()
 
         // THEN listeners are notified
         uiExecutor.advanceClockToLast()
@@ -1503,8 +1516,7 @@
     fun testOnMediaDataTimedOut_updatesLastActiveTime() {
         // GIVEN that the manager has a notification
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // WHEN the notification times out
         clock.advanceTime(100)
@@ -1612,8 +1624,7 @@
 
         // WHEN the notification is loaded
         mediaDataProcessor.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // THEN only the first MAX_COMPACT_ACTIONS are actually set
         verify(listener)
@@ -1648,8 +1659,7 @@
 
         // WHEN the notification is loaded
         mediaDataProcessor.onNotificationAdded(KEY, notif)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         // THEN only the first MAX_NOTIFICATION_ACTIONS are actually included
         verify(listener)
@@ -1668,7 +1678,6 @@
     @Test
     fun testPlaybackActions_noState_usesNotification() {
         val desc = "Notification Action"
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         whenever(controller.playbackState).thenReturn(null)
 
         val notifWithAction =
@@ -1683,8 +1692,7 @@
             }
         mediaDataProcessor.onNotificationAdded(KEY, notifWithAction)
 
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -1703,7 +1711,6 @@
     @Test
     fun testPlaybackActions_hasPrevNext() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions =
             PlaybackState.ACTION_PLAY or
                 PlaybackState.ACTION_SKIP_TO_PREVIOUS or
@@ -1747,7 +1754,6 @@
     @Test
     fun testPlaybackActions_noPrevNext_usesCustom() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         customDesc.forEach {
@@ -1779,7 +1785,6 @@
 
     @Test
     fun testPlaybackActions_connecting() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder =
             PlaybackState.Builder()
@@ -1798,9 +1803,74 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun postWithPlaybackActions_drawablesReused() {
+        whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+        whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+        val stateActions =
+            PlaybackState.ACTION_PAUSE or
+                PlaybackState.ACTION_SKIP_TO_PREVIOUS or
+                PlaybackState.ACTION_SKIP_TO_NEXT
+        val stateBuilder =
+            PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PLAYING, 0, 10f)
+                .setActions(stateActions)
+        whenever(controller.playbackState).thenReturn(stateBuilder.build())
+        val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries)
+
+        mediaDataProcessor.addInternalListener(mediaDataFilter)
+        mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+        addNotificationAndLoad()
+
+        assertThat(userEntries).hasSize(1)
+        val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
+
+        addNotificationAndLoad()
+
+        assertThat(userEntries).hasSize(1)
+        val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
+        assertThat(secondSemanticActions.nextOrCustom?.icon)
+            .isEqualTo(firstSemanticActions.nextOrCustom?.icon)
+        assertThat(secondSemanticActions.prevOrCustom?.icon)
+            .isEqualTo(firstSemanticActions.prevOrCustom?.icon)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun postWithPlaybackActions_drawablesNotReused() {
+        whenever(notificationLockscreenUserManager.isCurrentProfile(USER_ID)).thenReturn(true)
+        whenever(notificationLockscreenUserManager.isProfileAvailable(USER_ID)).thenReturn(true)
+        val stateActions =
+            PlaybackState.ACTION_PAUSE or
+                PlaybackState.ACTION_SKIP_TO_PREVIOUS or
+                PlaybackState.ACTION_SKIP_TO_NEXT
+        val stateBuilder =
+            PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PLAYING, 0, 10f)
+                .setActions(stateActions)
+        whenever(controller.playbackState).thenReturn(stateBuilder.build())
+        val userEntries by testScope.collectLastValue(mediaFilterRepository.selectedUserEntries)
+
+        mediaDataProcessor.addInternalListener(mediaDataFilter)
+        mediaDataFilter.mediaDataProcessor = mediaDataProcessor
+        addNotificationAndLoad()
+
+        assertThat(userEntries).hasSize(1)
+        val firstSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
+
+        addNotificationAndLoad()
+
+        assertThat(userEntries).hasSize(1)
+        val secondSemanticActions = userEntries?.values?.toList()?.get(0)?.semanticActions!!
+        assertThat(secondSemanticActions.nextOrCustom?.icon)
+            .isNotEqualTo(firstSemanticActions.nextOrCustom?.icon)
+        assertThat(secondSemanticActions.prevOrCustom?.icon)
+            .isNotEqualTo(firstSemanticActions.prevOrCustom?.icon)
+    }
+
+    @Test
     fun testPlaybackActions_reservedSpace() {
         val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4")
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         customDesc.forEach {
@@ -1838,7 +1908,6 @@
 
     @Test
     fun testPlaybackActions_playPause_hasButton() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val stateActions = PlaybackState.ACTION_PLAY_PAUSE
         val stateBuilder = PlaybackState.Builder().setActions(stateActions)
         whenever(controller.playbackState).thenReturn(stateBuilder.build())
@@ -1875,8 +1944,7 @@
 
         // update to remote cast
         mediaDataProcessor.onNotificationAdded(KEY, remoteCastNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(logger)
             .logPlaybackLocationChange(
                 anyInt(),
@@ -1938,7 +2006,6 @@
 
     @Test
     fun testPlaybackState_PauseWhenFlagTrue_keyExists_callsListener() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         val state = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 0L, 1f).build()
         whenever(controller.playbackState).thenReturn(state)
 
@@ -1982,6 +2049,7 @@
             pendingIntent,
             PACKAGE_NAME
         )
+        testScope.runCurrent()
         backgroundExecutor.runAllReady()
         foregroundExecutor.runAllReady()
 
@@ -2060,7 +2128,7 @@
 
     @Test
     fun testRetain_notifPlayer_notifRemoved_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added, times out, and then removed
         addNotificationAndLoad()
@@ -2090,7 +2158,7 @@
 
     @Test
     fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added and times out
         addNotificationAndLoad()
@@ -2108,7 +2176,7 @@
 
     @Test
     fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control based on notification is added and then removed, without timing out
         addNotificationAndLoad()
@@ -2125,7 +2193,7 @@
 
     @Test
     fun testRetain_canResume_removeWhileActive_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a media control that supports resumption is added
         addNotificationAndLoad()
@@ -2157,8 +2225,7 @@
 
     @Test
     fun testRetain_sessionPlayer_notifRemoved_doesNotChange() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2177,8 +2244,7 @@
 
     @Test
     fun testRetain_sessionPlayer_sessionDestroyed_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2211,8 +2277,7 @@
 
     @Test
     fun testRetain_sessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control using session actions is added, and then the session is destroyed
@@ -2231,8 +2296,7 @@
 
     @Test
     fun testRetain_sessionPlayer_canResume_destroyedWhileActive_setToResume() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
         addPlaybackStateAction()
 
         // When a media control using session actions and that does allow resumption is added,
@@ -2265,7 +2329,6 @@
 
     @Test
     fun testSessionPlayer_sessionDestroyed_noResume_fullyRemoved() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control with PlaybackState actions is added, times out,
@@ -2292,7 +2355,6 @@
 
     @Test
     fun testSessionPlayer_destroyedWhileActive_noResume_fullyRemoved() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control using session actions is added, and then the session is destroyed
@@ -2311,7 +2373,6 @@
 
     @Test
     fun testSessionPlayer_canResume_destroyedWhileActive_setToResume() {
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
         addPlaybackStateAction()
 
         // When a media control using session actions and that does allow resumption is added,
@@ -2344,8 +2405,7 @@
 
     @Test
     fun testSessionDestroyed_noNotificationKey_stillRemoved() {
-        whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(true)
-        whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true)
+        fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true)
 
         // When a notiifcation is added and then removed before it is fully processed
         mediaDataProcessor.onNotificationAdded(KEY, mediaNotification)
@@ -2416,6 +2476,23 @@
         assertThat(mediaDataCaptor.value.artwork).isNull()
     }
 
+    private fun TestScope.assertRunAllReady(foreground: Int = 0, background: Int = 0) {
+        runCurrent()
+        if (Flags.mediaLoadMetadataViaMediaDataLoader()) {
+            // It doesn't make much sense to count tasks when we use coroutines in loader
+            // so this check is skipped in that scenario.
+            backgroundExecutor.runAllReady()
+            foregroundExecutor.runAllReady()
+        } else {
+            if (background > 0) {
+                assertThat(backgroundExecutor.runAllReady()).isEqualTo(background)
+            }
+            if (foreground > 0) {
+                assertThat(foregroundExecutor.runAllReady()).isEqualTo(foreground)
+            }
+        }
+    }
+
     /** Helper function to add a basic media notification and capture the resulting MediaData */
     private fun addNotificationAndLoad() {
         addNotificationAndLoad(mediaNotification)
@@ -2424,8 +2501,7 @@
     /** Helper function to add the given notification and capture the resulting MediaData */
     private fun addNotificationAndLoad(sbn: StatusBarNotification) {
         mediaDataProcessor.onNotificationAdded(KEY, sbn)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
         verify(listener)
             .onMediaDataLoaded(
                 eq(KEY),
@@ -2459,8 +2535,7 @@
             pendingIntent,
             packageName
         )
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        testScope.assertRunAllReady(foreground = 1, background = 1)
 
         verify(listener)
             .onMediaDataLoaded(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 5142730..6a66c40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -254,22 +254,17 @@
         // AND that token results in a null route
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        val data = loadMediaAndCaptureDeviceData()
+
         // THEN the device should be disabled
-        val data = captureDeviceData(KEY)
         assertThat(data.enabled).isFalse()
     }
 
     @Test
     fun deviceEventOnAddNotification() {
         // WHEN a notification is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
         // THEN the update is dispatched to the listener
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
         assertThat(data.icon).isEqualTo(icon)
@@ -417,15 +412,40 @@
         whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         // WHEN a notification is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
         // THEN it uses the route name (instead of device name)
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
     }
 
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableReused() {
+        whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
+        whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+
+        val firstData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondData = loadMediaAndCaptureDeviceData()
+
+        assertThat(secondData.icon).isEqualTo(firstData.icon)
+    }
+
+    @Test
+    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName_drawableNotReused() {
+        whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
+        whenever(routingSession.selectedRoutes).thenReturn(listOf("selectedRoute", "selectedRoute"))
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+
+        val firstData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondData = loadMediaAndCaptureDeviceData()
+
+        assertThat(secondData.icon).isNotEqualTo(firstData.icon)
+    }
+
     @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
@@ -433,11 +453,8 @@
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN a notification is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
         // THEN the device is disabled and name is set to null
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
     }
@@ -449,23 +466,48 @@
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN a notification is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
         // THEN the device is disabled and name and icon are set to "OTHER DEVICE".
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isEqualTo(context.getString(R.string.media_seamless_other_device))
         assertThat(data.icon).isEqualTo(OTHER_DEVICE_ICON_STUB)
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+    @EnableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableReused() {
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+        context.orCreateTestableResources.removeOverride(R.drawable.ic_media_home_devices)
+
+        val firstData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondData = loadMediaAndCaptureDeviceData()
+
+        assertThat(secondData.icon).isEqualTo(firstData.icon)
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
+    @DisableFlags(com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE)
+    fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_drawableNotReused() {
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
+        context.orCreateTestableResources.removeOverride(R.drawable.ic_media_home_devices)
+
+        val firstData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondData = loadMediaAndCaptureDeviceData()
+
+        assertThat(secondData.icon).isNotEqualTo(firstData.icon)
+    }
+
     @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(listener)
         // AND MR2Manager returns null for routing session
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
@@ -480,13 +522,12 @@
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isNull()
     }
+
     @RequiresFlagsEnabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_returnOtherDevice() {
         // GIVEN a notif is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(listener)
         // AND MR2Manager returns null for routing session
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
@@ -507,9 +548,7 @@
     @Test
     fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(listener)
         // GIVEN that MR2Manager returns null for routing session
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
@@ -529,9 +568,7 @@
     @Test
     fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_returnsOtherDevice() {
         // GIVEN a notif is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(listener)
         // GIVEN that MR2Manager returns null for routing session
         whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
@@ -563,12 +600,8 @@
         whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
         // Then the device name is the PhoneMediaDevice string
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.name).isEqualTo(PhoneMediaDevice.getMediaTransferThisDeviceName(context))
     }
 
@@ -582,12 +615,8 @@
         whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(routingSession.isSystemSession).thenReturn(true)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
         // Then the device name is the selected route name
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
     }
 
@@ -597,11 +626,8 @@
         whenever(routingSession.name).thenReturn(null)
         whenever(routingSession.isSystemSession).thenReturn(false)
         // WHEN a notification is added
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
         // THEN the device is enabled and uses the current connected device name
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
         assertThat(data.enabled).isTrue()
     }
@@ -611,9 +637,7 @@
         whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
         whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
         // GIVEN a controller with local playback type
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(mr2)
         // WHEN onAudioInfoChanged fires with remote playback type
         whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
@@ -630,9 +654,7 @@
         whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
         whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
         // GIVEN a controller with local playback type
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(mr2)
         // WHEN onAudioInfoChanged fires with a volume control id change
         whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
@@ -649,9 +671,7 @@
         whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
         // GIVEN a controller with remote playback type
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
         reset(mr2)
         // WHEN onAudioInfoChanged fires with remote playback type
         val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
@@ -665,9 +685,7 @@
     fun deviceIdChanged_informListener() {
         // GIVEN a notification is added, with a particular device connected
         whenever(device.id).thenReturn(DEVICE_ID)
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
 
         // and later the manager gets a new device ID
         val deviceCallback = captureCallback()
@@ -694,9 +712,7 @@
         // GIVEN a notification is added, with a particular device connected
         whenever(device.id).thenReturn(DEVICE_ID)
         whenever(device.name).thenReturn(DEVICE_NAME)
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
+        loadMediaAndCaptureDeviceData()
 
         // and later the manager gets a new device name
         val deviceCallback = captureCallback()
@@ -725,12 +741,8 @@
         whenever(device.name).thenReturn(DEVICE_NAME)
         val firstIcon = mock(Drawable::class.java)
         whenever(device.icon).thenReturn(firstIcon)
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
 
-        val dataCaptor = ArgumentCaptor.forClass(MediaDeviceData::class.java)
-        verify(listener).onMediaDeviceChanged(eq(KEY), any(), dataCaptor.capture())
+        loadMediaAndCaptureDeviceData()
 
         // and later the manager gets a callback with only the icon changed
         val deviceCallback = captureCallback()
@@ -772,11 +784,7 @@
         setupBroadcastPackage(BROADCAST_APP_NAME)
         broadcastCallback.onBroadcastStarted(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isFalse()
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(DEVICE_NAME)
@@ -791,11 +799,7 @@
         setupBroadcastPackage(BROADCAST_APP_NAME)
         broadcastCallback.onBroadcastStarted(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isTrue()
         assertThat(data.enabled).isTrue()
         assertThat(data.name)
@@ -811,11 +815,7 @@
         setupBroadcastPackage(NORMAL_APP_NAME)
         broadcastCallback.onBroadcastStarted(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isTrue()
         assertThat(data.enabled).isTrue()
         assertThat(data.name).isEqualTo(BROADCAST_APP_NAME)
@@ -829,11 +829,7 @@
         setupLeAudioConfiguration(false)
         broadcastCallback.onBroadcastStopped(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isFalse()
     }
 
@@ -846,11 +842,7 @@
         setupBroadcastPackage(BROADCAST_APP_NAME)
         broadcastCallback.onBroadcastStarted(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isFalse()
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
@@ -858,6 +850,82 @@
 
     @Test
     @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @EnableFlags(
+        Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+    )
+    fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesReused() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(BROADCAST_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        val firstDeviceData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondDeviceData = loadMediaAndCaptureDeviceData()
+
+        assertThat(firstDeviceData.icon).isEqualTo(secondDeviceData.icon)
+    }
+
+    @Test
+    @DisableFlags(
+        Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+    )
+    @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting_drawablesNotReused() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(BROADCAST_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        val firstDeviceData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondDeviceData = loadMediaAndCaptureDeviceData()
+
+        assertThat(firstDeviceData.icon).isNotEqualTo(secondDeviceData.icon)
+    }
+
+    @Test
+    @EnableFlags(
+        Flags.FLAG_LEGACY_LE_AUDIO_SHARING,
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+    )
+    @DisableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
+    fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableReused() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(NORMAL_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        val firstDeviceData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondDeviceData = loadMediaAndCaptureDeviceData()
+
+        assertThat(firstDeviceData.icon).isEqualTo(secondDeviceData.icon)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
+    @DisableFlags(
+        Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
+        com.android.systemui.Flags.FLAG_MEDIA_CONTROLS_DRAWABLES_REUSE
+    )
+    fun onBroadcastStarted_legacy_currentMediaDeviceDataIsNotBroadcasting_drawableNotReused() {
+        val broadcastCallback = setupBroadcastCallback()
+        setupLeAudioConfiguration(true)
+        setupBroadcastPackage(NORMAL_APP_NAME)
+        broadcastCallback.onBroadcastStarted(1, 1)
+
+        val firstDeviceData = loadMediaAndCaptureDeviceData()
+        reset(listener)
+        val secondDeviceData = loadMediaAndCaptureDeviceData()
+
+        assertThat(firstDeviceData.icon).isNotEqualTo(secondDeviceData.icon)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
     @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() {
         val broadcastCallback = setupBroadcastCallback()
@@ -865,11 +933,7 @@
         setupBroadcastPackage(NORMAL_APP_NAME)
         broadcastCallback.onBroadcastStarted(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isFalse()
         assertThat(data.enabled).isFalse()
         assertThat(data.name).isEqualTo(context.getString(R.string.audio_sharing_description))
@@ -883,11 +947,7 @@
         setupLeAudioConfiguration(false)
         broadcastCallback.onBroadcastStopped(1, 1)
 
-        manager.onMediaDataLoaded(KEY, null, mediaData)
-        fakeBgExecutor.runAllReady()
-        fakeFgExecutor.runAllReady()
-
-        val data = captureDeviceData(KEY)
+        val data = loadMediaAndCaptureDeviceData()
         assertThat(data.showBroadcastButton).isFalse()
         assertThat(data.name?.equals(context.getString(R.string.audio_sharing_description)))
             .isFalse()
@@ -903,13 +963,21 @@
         val callback: BluetoothLeBroadcast.Callback =
             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) {}
+
                 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
@@ -941,4 +1009,12 @@
         verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture())
         return captor.getValue()
     }
+
+    private fun loadMediaAndCaptureDeviceData(): MediaDeviceData {
+        manager.onMediaDataLoaded(KEY, null, mediaData)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        return captureDeviceData(KEY)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index f8358c5..03667cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -27,22 +27,28 @@
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
 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.SuccessFingerprintAuthenticationStatus
 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.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -67,18 +73,19 @@
 import com.android.systemui.testKosmos
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.GlobalSettings
-import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeSettings
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import javax.inject.Provider
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Before
@@ -106,11 +113,14 @@
 private const val PAUSED_LOCAL = "paused local"
 private const val PLAYING_LOCAL = "playing local"
 
+@ExperimentalCoroutinesApi
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidJUnit4::class)
 class MediaCarouselControllerTest : SysuiTestCase() {
-    val kosmos = testKosmos()
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.unconfinedTestDispatcher
+    private val secureSettings = kosmos.unconfinedDispatcherFakeSettings
 
     @Mock lateinit var mediaControlPanelFactory: Provider<MediaControlPanel>
     @Mock lateinit var mediaViewControllerFactory: Provider<MediaViewController>
@@ -132,7 +142,6 @@
     @Mock lateinit var mediaFlags: MediaFlags
     @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
     @Mock lateinit var globalSettings: GlobalSettings
-    private lateinit var secureSettings: SecureSettings
     private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
     @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
     @Captor
@@ -144,7 +153,6 @@
 
     private val clock = FakeSystemClock()
     private lateinit var bgExecutor: FakeExecutor
-    private lateinit var testDispatcher: TestDispatcher
     private lateinit var mediaCarouselController: MediaCarouselController
 
     private var originalResumeSetting =
@@ -153,10 +161,8 @@
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        secureSettings = FakeSettings()
         context.resources.configuration.setLocales(LocaleList(Locale.US, Locale.UK))
         bgExecutor = FakeExecutor(clock)
-        testDispatcher = UnconfinedTestDispatcher()
         mediaCarouselController =
             MediaCarouselController(
                 applicationScope = kosmos.applicationCoroutineScope,
@@ -183,10 +189,9 @@
                 secureSettings = secureSettings,
                 mediaCarouselViewModel = kosmos.mediaCarouselViewModel,
                 mediaViewControllerFactory = mediaViewControllerFactory,
-                sceneInteractor = kosmos.sceneInteractor,
+                deviceEntryInteractor = kosmos.deviceEntryInteractor,
             )
         verify(configurationController).addCallback(capture(configListener))
-        verify(mediaDataManager).addListener(capture(listener))
         verify(visualStabilityProvider)
             .addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
         verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCallback))
@@ -405,8 +410,11 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(2).isSsMediaRec)
     }
 
+    @DisableSceneContainer
     @Test
     fun testOrderWithSmartspace_prioritized_updatingVisibleMediaPlayers() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         testPlayerOrdering()
 
         // If smartspace is prioritized
@@ -439,8 +447,11 @@
         assertTrue(MediaPlayerData.playerKeys().elementAt(idx).isSsMediaRec)
     }
 
+    @DisableSceneContainer
     @Test
     fun testPlayingExistingMediaPlayerFromCarousel_visibleMediaPlayersNotUpdated() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         testPlayerOrdering()
         // playing paused player
         listener.value.onMediaDataLoaded(
@@ -547,8 +558,11 @@
         verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
     }
 
+    @DisableSceneContainer
     @Test
     fun testMediaLoaded_ScrollToActivePlayer() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         listener.value.onMediaDataLoaded(
             PLAYING_LOCAL,
             null,
@@ -604,8 +618,11 @@
         )
     }
 
+    @DisableSceneContainer
     @Test
     fun testMediaLoadedFromRecommendationCard_ScrollToActivePlayer() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         listener.value.onSmartspaceMediaDataLoaded(
             SMARTSPACE_KEY,
             EMPTY_SMARTSPACE_MEDIA_DATA.copy(packageName = "PACKAGE_NAME", isActive = true),
@@ -647,8 +664,11 @@
         assertEquals(playerIndex, 0)
     }
 
+    @DisableSceneContainer
     @Test
     fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         var result = false
         mediaCarouselController.updateHostVisibility = { result = true }
 
@@ -658,8 +678,11 @@
         assertEquals(true, result)
     }
 
+    @DisableSceneContainer
     @Test
     fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         var result = false
         mediaCarouselController.updateHostVisibility = { result = true }
 
@@ -788,8 +811,11 @@
         verify(pageIndicator, times(4)).setNumPages(any())
     }
 
+    @DisableSceneContainer
     @Test
     fun testRecommendation_persistentEnabled_newSmartspaceLoaded_updatesSort() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded()
 
         // When an update to existing smartspace data is loaded
@@ -804,8 +830,11 @@
         assertTrue(MediaPlayerData.visiblePlayerKeys().elementAt(0).data.active)
     }
 
+    @DisableSceneContainer
     @Test
     fun testRecommendation_persistentEnabled_inactiveSmartspaceDataLoaded_isAdded() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(true)
 
         // When inactive smartspace data is loaded
@@ -845,7 +874,6 @@
     }
 
     @DisableSceneContainer
-    @ExperimentalCoroutinesApi
     @Test
     fun testKeyguardGone_showMediaCarousel() =
         kosmos.testScope.runTest {
@@ -869,7 +897,6 @@
         }
 
     @EnableSceneContainer
-    @ExperimentalCoroutinesApi
     @Test
     fun testKeyguardGone_showMediaCarousel_scene_container() =
         kosmos.testScope.runTest {
@@ -887,7 +914,6 @@
             job.cancel()
         }
 
-    @ExperimentalCoroutinesApi
     @Test
     fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
@@ -917,7 +943,6 @@
         }
     }
 
-    @ExperimentalCoroutinesApi
     @Test
     fun keyguardShowing_allowedOnLockscreen_updateVisibility() {
         kosmos.testScope.runTest {
@@ -947,6 +972,74 @@
         }
     }
 
+    @EnableSceneContainer
+    @Test
+    fun deviceEntered_mediaAllowed_notLockedAndHidden() {
+        kosmos.testScope.runTest {
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
+            secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
+            setDeviceEntered(true)
+
+            assertEquals(false, mediaCarouselController.isLockedAndHidden())
+
+            settingsJob.cancel()
+        }
+    }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceEntered_mediaNotAllowed_notLockedAndHidden() {
+        kosmos.testScope.runTest {
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
+            secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
+            setDeviceEntered(true)
+
+            assertEquals(false, mediaCarouselController.isLockedAndHidden())
+
+            settingsJob.cancel()
+        }
+    }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceNotEntered_mediaAllowed_notLockedAndHidden() {
+        kosmos.testScope.runTest {
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
+            secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
+            setDeviceEntered(false)
+
+            assertEquals(false, mediaCarouselController.isLockedAndHidden())
+
+            settingsJob.cancel()
+        }
+    }
+
+    @EnableSceneContainer
+    @Test
+    fun deviceNotEntered_mediaNotAllowed_lockedAndHidden() {
+        kosmos.testScope.runTest {
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
+            secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
+            setDeviceEntered(false)
+
+            assertEquals(true, mediaCarouselController.isLockedAndHidden())
+
+            settingsJob.cancel()
+        }
+    }
+
     @Test
     fun testInvisibleToUserAndExpanded_playersNotListening() {
         // Add players to carousel.
@@ -1023,11 +1116,13 @@
         verify(panel).updateAnimatorDurationScale()
     }
 
+    @DisableSceneContainer
     @Test
     fun swipeToDismiss_pausedAndResumeOff_userInitiated() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         // When resumption is disabled, paused media should be dismissed after being swiped away
         Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
-
         val pausedMedia = DATA.copy(isPlaying = false)
         listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
         mediaCarouselController.onSwipeToDismiss()
@@ -1042,8 +1137,11 @@
         verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
     }
 
+    @DisableSceneContainer
     @Test
     fun swipeToDismiss_pausedAndResumeOff_delayed_userInitiated() {
+        verify(mediaDataManager).addListener(capture(listener))
+
         // When resumption is disabled, paused media should be dismissed after being swiped away
         Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
         mediaCarouselController.updateHostVisibility = {}
@@ -1068,6 +1166,7 @@
      * @param function called when a certain configuration change occurs.
      */
     private fun testConfigurationChange(function: () -> Unit) {
+        verify(mediaDataManager).addListener(capture(listener))
         mediaCarouselController.pageIndicator = pageIndicator
         listener.value.onMediaDataLoaded(
             PLAYING_LOCAL,
@@ -1100,4 +1199,30 @@
             mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
         )
     }
+
+    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
+        if (isEntered) {
+            // Unlock the device, marking the device as entered
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+        }
+        setScene(
+            if (isEntered) {
+                Scenes.Gone
+            } else {
+                Scenes.Lockscreen
+            }
+        )
+        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
+    }
+
+    private fun TestScope.setScene(key: SceneKey) {
+        kosmos.sceneInteractor.changeScene(key, "test")
+        kosmos.sceneInteractor.setTransitionState(
+            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+        )
+        runCurrent()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 521aa5a..1260a65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -70,6 +70,7 @@
 import com.android.systemui.bluetooth.BroadcastDialogController
 import com.android.systemui.broadcast.BroadcastSender
 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.domain.pipeline.EMPTY_SMARTSPACE_MEDIA_DATA
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -84,7 +85,6 @@
 import com.android.systemui.media.controls.ui.view.MediaViewHolder
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.controls.util.MediaUiEventLogger
 import com.android.systemui.media.dialog.MediaOutputDialogManager
 import com.android.systemui.monet.ColorScheme
@@ -141,6 +141,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@DisableSceneContainer
 public class MediaControlPanelTest : SysuiTestCase() {
     @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
 
@@ -233,9 +234,7 @@
     @Mock private lateinit var recProgressBar1: SeekBar
     @Mock private lateinit var recProgressBar2: SeekBar
     @Mock private lateinit var recProgressBar3: SeekBar
-    private var shouldShowBroadcastButton: Boolean = false
     @Mock private lateinit var globalSettings: GlobalSettings
-    @Mock private lateinit var mediaFlags: MediaFlags
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
 
@@ -254,7 +253,6 @@
             .thenReturn(applicationInfo)
         whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
         context.setMockPackageManager(packageManager)
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(false)
 
         player =
             object :
@@ -278,7 +276,6 @@
                     lockscreenUserManager,
                     broadcastDialogController,
                     globalSettings,
-                    mediaFlags,
                 ) {
                 override fun loadAnimator(
                     animId: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 6c350cb..2370bca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
 import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.dreams.DreamOverlayStateController
+import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -40,7 +41,6 @@
 import com.android.systemui.media.controls.ui.view.MediaCarouselScrollHandler
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.media.dream.MediaDreamComplication
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.res.R
@@ -85,6 +85,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@DisableSceneContainer
 class MediaHierarchyManagerTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
@@ -105,7 +106,6 @@
     @Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController
     @Mock private lateinit var shadeInteractor: ShadeInteractor
     @Mock lateinit var logger: MediaViewLogger
-    @Mock private lateinit var mediaFlags: MediaFlags
     @Captor
     private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
     @Captor
@@ -139,7 +139,6 @@
         shadeExpansion = MutableStateFlow(0f)
         whenever(shadeInteractor.isQsBypassingShade).thenReturn(isQsBypassingShade)
         whenever(shadeInteractor.shadeExpansion).thenReturn(shadeExpansion)
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(false)
         mediaHierarchyManager =
             MediaHierarchyManager(
                 context,
@@ -160,7 +159,6 @@
                 testScope.backgroundScope,
                 ResourcesSplitShadeStateController(),
                 logger,
-                mediaFlags,
             )
         verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
         verify(statusBarStateController).addCallback(statusBarCallback.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index 00b9a46..e765b6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -38,12 +38,12 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.media.controls.ui.view.GutsViewHolder
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaViewHolder
 import com.android.systemui.media.controls.ui.view.RecommendationViewHolder
 import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
-import com.android.systemui.media.controls.util.MediaFlags
 import com.android.systemui.res.R
 import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView
 import com.android.systemui.surfaceeffects.ripple.MultiRippleView
@@ -113,7 +113,6 @@
     @Mock private lateinit var mediaTitleWidgetState: WidgetState
     @Mock private lateinit var mediaSubTitleWidgetState: WidgetState
     @Mock private lateinit var mediaContainerWidgetState: WidgetState
-    @Mock private lateinit var mediaFlags: MediaFlags
     @Mock private lateinit var seekBarViewModel: SeekBarViewModel
     @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress>
     @Mock private lateinit var globalSettings: GlobalSettings
@@ -140,7 +139,6 @@
                     logger,
                     seekBarViewModel,
                     mainExecutor,
-                    mediaFlags,
                     globalSettings,
                 ) {
                 override fun loadAnimator(
@@ -374,10 +372,9 @@
         verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta }
     }
 
+    @EnableSceneContainer
     @Test
     fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
         getEnabledChangeListener().onEnabledChanged(enabled = false)
@@ -386,10 +383,9 @@
             .isEqualTo(ConstraintSet.INVISIBLE)
     }
 
+    @EnableSceneContainer
     @Test
     fun attachPlayer_seekBarEnabled_seekBarVisible() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
 
@@ -397,10 +393,9 @@
             .isEqualTo(ConstraintSet.VISIBLE)
     }
 
+    @EnableSceneContainer
     @Test
     fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
 
@@ -413,10 +408,9 @@
             .isEqualTo(ConstraintSet.INVISIBLE)
     }
 
+    @EnableSceneContainer
     @Test
     fun attachPlayer_notScrubbing_scrubbingViewsGone() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.canShowScrubbingTime = true
         getScrubbingChangeListener().onScrubbingChanged(true)
@@ -433,10 +427,9 @@
             .isEqualTo(ConstraintSet.GONE)
     }
 
+    @EnableSceneContainer
     @Test
     fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.canShowScrubbingTime = false
         getScrubbingChangeListener().onScrubbingChanged(true)
@@ -452,10 +445,9 @@
             .isEqualTo(ConstraintSet.GONE)
     }
 
+    @EnableSceneContainer
     @Test
     fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true)
         mediaViewController.setUpPrevButtonInfo(false)
@@ -474,10 +466,9 @@
             .isEqualTo(ConstraintSet.GONE)
     }
 
+    @EnableSceneContainer
     @Test
     fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(false)
         mediaViewController.setUpPrevButtonInfo(true)
@@ -496,10 +487,9 @@
             .isEqualTo(ConstraintSet.GONE)
     }
 
+    @EnableSceneContainer
     @Test
     fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true)
         mediaViewController.setUpPrevButtonInfo(true)
@@ -522,10 +512,9 @@
             .isEqualTo(ConstraintSet.VISIBLE)
     }
 
+    @EnableSceneContainer
     @Test
     fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() {
-        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
-
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE)
         mediaViewController.setUpPrevButtonInfo(true, ConstraintSet.INVISIBLE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index d5cd86e..c8cc6b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -50,14 +50,18 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -73,6 +77,8 @@
 
     private static final String TEST_PACKAGE = "test_package";
 
+    private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
     // Mock
     private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
     private MediaController mMediaController = mock(MediaController.class);
@@ -122,6 +128,9 @@
         when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
         mMediaControllers.add(mMediaController);
         when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
+        VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+                VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+                        mKosmos);
 
         mMediaOutputController =
                 new MediaOutputController(
@@ -139,6 +148,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        volumePanelGlobalStateInteractor,
                         mUserTracker);
 
         // Using a fake package will cause routing operations to fail, so we intercept
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index 87d2245..189a561 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -51,14 +51,18 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
 
 import com.google.common.base.Strings;
 
@@ -81,6 +85,8 @@
     private static final String BROADCAST_CODE_TEST = "112233";
     private static final String BROADCAST_CODE_UPDATE_TEST = "11223344";
 
+    private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
     // Mock
     private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
     private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
@@ -123,6 +129,9 @@
         when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST);
         when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn(
                 BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8));
+        VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+                VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+                        mKosmos);
 
         mMediaOutputController =
                 new MediaOutputController(
@@ -140,6 +149,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        volumePanelGlobalStateInteractor,
                         mUserTracker);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 856bc0b..714fad9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -72,15 +72,19 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
 
 import com.google.common.collect.ImmutableList;
 
@@ -158,11 +162,16 @@
     @Mock
     private UserTracker mUserTracker;
 
+    private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
     private FeatureFlags mFlags = mock(FeatureFlags.class);
     private View mDialogLaunchView = mock(View.class);
     private MediaOutputController.Callback mCallback = mock(MediaOutputController.Callback.class);
 
     final Notification mNotification = mock(Notification.class);
+    private final VolumePanelGlobalStateInteractor mVolumePanelGlobalStateInteractor =
+            VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+                    mKosmos);
 
     private Context mSpyContext;
     private String mPackageName = null;
@@ -194,6 +203,7 @@
         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(
                 mCachedBluetoothDeviceManager);
 
+
         mMediaOutputController =
                 new MediaOutputController(
                         mSpyContext,
@@ -210,6 +220,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
@@ -304,6 +315,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         mMediaOutputController.start(mCb);
@@ -346,6 +358,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         mMediaOutputController.start(mCb);
@@ -602,6 +615,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
         testMediaOutputController.start(mCb);
         reset(mCb);
@@ -636,6 +650,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
         testMediaOutputController.start(mCb);
         reset(mCb);
@@ -683,6 +698,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -710,6 +726,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         LocalMediaManager mockLocalMediaManager = mock(LocalMediaManager.class);
@@ -990,6 +1007,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         assertThat(mMediaOutputController.getNotificationIcon()).isNull();
@@ -1193,6 +1211,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        mVolumePanelGlobalStateInteractor,
                         mUserTracker);
 
         testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index f20b04a..90c2930 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -54,14 +54,18 @@
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.broadcast.BroadcastSender;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.kosmos.Kosmos;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor;
+import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractorKosmosKt;
 
 import org.junit.After;
 import org.junit.Before;
@@ -84,6 +88,8 @@
     @Rule
     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
 
+    private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
     // Mock
     private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
     private MediaController mMediaController = mock(MediaController.class);
@@ -136,6 +142,9 @@
         when(mMediaSessionManager.getActiveSessionsForUser(any(),
                 Mockito.eq(userHandle))).thenReturn(
                 mMediaControllers);
+        VolumePanelGlobalStateInteractor volumePanelGlobalStateInteractor =
+                VolumePanelGlobalStateInteractorKosmosKt.getVolumePanelGlobalStateInteractor(
+                        mKosmos);
 
         mMediaOutputController =
                 new MediaOutputController(
@@ -153,6 +162,7 @@
                         mPowerExemptionManager,
                         mKeyguardManager,
                         mFlags,
+                        volumePanelGlobalStateInteractor,
                         mUserTracker);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = makeTestDialog(mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index ffbf62a..b3bd7d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -20,8 +20,8 @@
 import android.os.Handler
 import android.os.PowerManager
 import android.view.ViewGroup
-import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.statusbar.CommandQueue
@@ -36,7 +36,7 @@
     commandQueue: CommandQueue,
     context: Context,
     logger: MediaTttReceiverLogger,
-    windowManager: WindowManager,
+    viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
     configurationController: ConfigurationController,
@@ -55,7 +55,7 @@
         commandQueue,
         context,
         logger,
-        windowManager,
+        viewCaptureAwareWindowManager,
         mainExecutor,
         accessibilityManager,
         configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index f1d833f..9afa5ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -31,12 +31,14 @@
 import android.widget.ImageView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.temporarydisplay.TemporaryViewUiEventLogger
@@ -88,6 +90,9 @@
     private lateinit var commandQueue: CommandQueue
     @Mock
     private lateinit var rippleController: MediaTttReceiverRippleController
+    @Mock
+    private lateinit var lazyViewCapture: Lazy<ViewCapture>
+    private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
     private lateinit var commandQueueCallback: CommandQueue.Callbacks
     private lateinit var fakeAppIconDrawable: Drawable
     private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -122,11 +127,13 @@
         fakeWakeLockBuilder = WakeLockFake.Builder(context)
         fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
 
+        viewCaptureAwareWindowManager = ViewCaptureAwareWindowManager(windowManager,
+                lazyViewCapture, isViewCaptureEnabled = false)
         controllerReceiver = FakeMediaTttChipControllerReceiver(
             commandQueue,
             context,
             logger,
-            windowManager,
+            viewCaptureAwareWindowManager,
             fakeExecutor,
             accessibilityManager,
             configurationController,
@@ -157,7 +164,7 @@
             commandQueue,
             context,
             logger,
-            windowManager,
+            viewCaptureAwareWindowManager,
             FakeExecutor(FakeSystemClock()),
             accessibilityManager,
             configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 111b8d4..b4cad6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -33,6 +33,8 @@
 import android.widget.TextView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.statusbar.IUndoMediaTransferCallback
 import com.android.systemui.SysuiTestCase
@@ -100,6 +102,7 @@
     @Mock private lateinit var windowManager: WindowManager
     @Mock private lateinit var vibratorHelper: VibratorHelper
     @Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
+    @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
     private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
     private lateinit var fakeWakeLock: WakeLockFake
     private lateinit var chipbarCoordinator: ChipbarCoordinator
@@ -145,7 +148,8 @@
             ChipbarCoordinator(
                 context,
                 chipbarLogger,
-                windowManager,
+                ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
+                        isViewCaptureEnabled = false),
                 fakeExecutor,
                 accessibilityManager,
                 configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 7211620..3e3aa4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -15,10 +15,10 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50
 import com.android.wm.shell.recents.RecentTasks
-import com.android.wm.shell.util.GroupedRecentTaskInfo
-import com.android.wm.shell.util.SplitBounds
+import com.android.wm.shell.shared.GroupedRecentTaskInfo
+import com.android.wm.shell.shared.split.SplitBounds
+import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_50_50
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
 import java.util.function.Consumer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
index b337ccfd..69b7b2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewControllerTest.kt
@@ -24,14 +24,13 @@
 import android.view.ViewGroup
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX
 import com.android.systemui.Flags.FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler
 import com.android.systemui.mediaprojection.appselector.data.RecentTask
 import com.android.systemui.util.mockito.mock
+import com.android.wm.shell.shared.split.SplitBounds
 import com.android.wm.shell.splitscreen.SplitScreen
-import com.android.wm.shell.util.SplitBounds
 import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
@@ -141,27 +140,13 @@
     }
 
     @Test
-    fun onRecentAppClicked_fullScreenTaskInForeground_flagOff_usesScaleUpAnimation() {
-        mSetFlagsRule.disableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
-
-        controller.onRecentAppClicked(fullScreenTask, taskView)
-
-        assertThat(getStartedTaskActivityOptions(fullScreenTask.taskId).animationType)
-            .isEqualTo(ActivityOptions.ANIM_SCALE_UP)
-    }
-
-    @Test
-    fun onRecentAppClicked_fullScreenTaskInForeground_flagOn_usesDefaultAnimation() {
-        mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX)
+    fun onRecentAppClicked_fullScreenTaskInForeground_usesDefaultAnimation() {
         assertForegroundTaskUsesDefaultCloseAnimation(fullScreenTask)
     }
 
     @Test
     fun onRecentAppClicked_splitScreenTaskInForeground_flagOn_usesDefaultAnimation() {
-        mSetFlagsRule.enableFlags(
-            FLAG_PSS_APP_SELECTOR_ABRUPT_EXIT_FIX,
-            FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN
-        )
+        mSetFlagsRule.enableFlags(FLAG_PSS_APP_SELECTOR_RECENTS_SPLIT_SCREEN)
         assertForegroundTaskUsesDefaultCloseAnimation(splitScreenTask)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index bfbb7ce..a770ee1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -18,7 +18,9 @@
 
 import static android.app.StatusBarManager.WINDOW_NAVIGATION_BAR;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -37,6 +39,9 @@
 import android.content.ComponentName;
 import android.content.res.Configuration;
 import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.provider.Flags;
 import android.view.IWindowManager;
 import android.view.accessibility.AccessibilityManager;
 
@@ -47,6 +52,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dump.DumpManager;
@@ -94,6 +100,8 @@
     @Mock
     AccessibilityButtonTargetsObserver mAccessibilityButtonTargetObserver;
     @Mock
+    AccessibilityGestureTargetsObserver mAccessibilityGestureTargetObserver;
+    @Mock
     SystemActions mSystemActions;
     @Mock
     OverviewProxyService mOverviewProxyService;
@@ -152,6 +160,7 @@
                 mAccessibilityManager).addAccessibilityServicesStateChangeListener(any());
         mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
                 mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
+                mAccessibilityGestureTargetObserver,
                 mSystemActions, mOverviewProxyService, mAssistManagerLazy,
                 () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class),
                 mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker,
@@ -171,6 +180,7 @@
         mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         verify(mAccessibilityButtonModeObserver, times(1)).addListener(mNavBarHelper);
         verify(mAccessibilityButtonTargetObserver, times(1)).addListener(mNavBarHelper);
+        verify(mAccessibilityGestureTargetObserver, times(1)).addListener(mNavBarHelper);
         verify(mAccessibilityManager, times(1)).addAccessibilityServicesStateChangeListener(
                 mNavBarHelper);
         verify(mAssistManager, times(1)).getAssistInfoForUser(anyInt());
@@ -185,6 +195,7 @@
         mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
         verify(mAccessibilityButtonModeObserver, times(1)).removeListener(mNavBarHelper);
         verify(mAccessibilityButtonTargetObserver, times(1)).removeListener(mNavBarHelper);
+        verify(mAccessibilityGestureTargetObserver, times(1)).removeListener(mNavBarHelper);
         verify(mAccessibilityManager, times(1)).removeAccessibilityServicesStateChangeListener(
                 mNavBarHelper);
         verify(mWm, times(1)).removeRotationWatcher(any());
@@ -353,6 +364,83 @@
         verify(mEdgeBackGestureHandler, times(1)).onConfigurationChanged(any());
     }
 
+    @Test
+    public void updateA11yState_navBarMode_softwareTargets_isClickable() {
+        when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+        when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+                .thenReturn(createFakeShortcutTargets());
+
+        mNavBarHelper.updateA11yState();
+        long state = mNavBarHelper.getA11yButtonState();
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void updateA11yState_gestureMode_softwareTargets_isClickable() {
+        when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+                ACCESSIBILITY_BUTTON_MODE_GESTURE);
+        when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+                .thenReturn(createFakeShortcutTargets());
+
+        mNavBarHelper.updateA11yState();
+        long state = mNavBarHelper.getA11yButtonState();
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void updateA11yState_gestureNavMode_floatingButtonMode_gestureTargets_isClickable() {
+        mNavBarHelper.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
+        when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+                ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+        when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.GESTURE))
+                .thenReturn(createFakeShortcutTargets());
+
+        mNavBarHelper.updateA11yState();
+        long state = mNavBarHelper.getA11yButtonState();
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void updateA11yState_navBarMode_gestureTargets_isNotClickable() {
+        when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+        when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.GESTURE))
+                .thenReturn(createFakeShortcutTargets());
+
+        mNavBarHelper.updateA11yState();
+        long state = mNavBarHelper.getA11yButtonState();
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(0);
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(0);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void updateA11yState_singleTarget_clickableButNotLongClickable() {
+        when(mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()).thenReturn(
+                ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+        when(mAccessibilityManager.getAccessibilityShortcutTargets(UserShortcutType.SOFTWARE))
+                .thenReturn(new ArrayList<>(List.of("a")));
+
+        mNavBarHelper.updateA11yState();
+        long state = mNavBarHelper.getA11yButtonState();
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_CLICKABLE).isEqualTo(
+                SYSUI_STATE_A11Y_BUTTON_CLICKABLE);
+        assertThat(state & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE).isEqualTo(0);
+    }
+
     private List<String> createFakeShortcutTargets() {
         return new ArrayList<>(List.of("a", "b", "c", "d"));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index a52ab0c..2905a73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -78,12 +78,14 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.AccessibilityGestureTargetsObserver;
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.dump.DumpManager;
@@ -214,6 +216,8 @@
     @Mock
     private WindowManager mWindowManager;
     @Mock
+    private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+    @Mock
     private TelecomManager mTelecomManager;
     @Mock
     private InputMethodManager mInputMethodManager;
@@ -284,6 +288,7 @@
             mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
                     mock(AccessibilityButtonModeObserver.class),
                     mock(AccessibilityButtonTargetsObserver.class),
+                    mock(AccessibilityGestureTargetsObserver.class),
                     mSystemActions, mOverviewProxyService,
                     () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
                     mKeyguardStateController, mock(NavigationModeController.class),
@@ -619,6 +624,7 @@
                 null,
                 context,
                 mWindowManager,
+                mViewCaptureAwareWindowManager,
                 () -> mAssistManager,
                 mock(AccessibilityManager.class),
                 deviceProvisionedController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
deleted file mode 100644
index 1eeaef7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * 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.qs;
-
-import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.BroadcastReceiver;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-@SmallTest
-public class AutoAddTrackerTest extends SysuiTestCase {
-
-    private static final int END_POSITION = -1;
-    private static final int USER = 0;
-
-    @Mock
-    private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock
-    private QSHost mQSHost;
-    @Mock
-    private DumpManager mDumpManager;
-    @Captor
-    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
-    @Captor
-    private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
-
-    private Executor mBackgroundExecutor = Runnable::run; // Direct executor
-    private AutoAddTracker mAutoTracker;
-    private SecureSettings mSecureSettings;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mSecureSettings = new FakeSettings();
-
-        mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER);
-
-        mAutoTracker = createAutoAddTracker(USER);
-        mAutoTracker.initialize();
-    }
-
-    @Test
-    public void testChangeFromBackup() {
-        assertFalse(mAutoTracker.isAdded(SAVER));
-
-        mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER);
-
-        assertTrue(mAutoTracker.isAdded(SAVER));
-
-        mAutoTracker.destroy();
-    }
-
-    @Test
-    public void testSetAdded() {
-        assertFalse(mAutoTracker.isAdded(SAVER));
-        mAutoTracker.setTileAdded(SAVER);
-
-        assertTrue(mAutoTracker.isAdded(SAVER));
-
-        mAutoTracker.destroy();
-    }
-
-    @Test
-    public void testPersist() {
-        assertFalse(mAutoTracker.isAdded(SAVER));
-        mAutoTracker.setTileAdded(SAVER);
-
-        mAutoTracker.destroy();
-        mAutoTracker = createAutoAddTracker(USER);
-        mAutoTracker.initialize();
-
-        assertTrue(mAutoTracker.isAdded(SAVER));
-
-        mAutoTracker.destroy();
-    }
-
-    @Test
-    public void testIndependentUsers() {
-        mAutoTracker.setTileAdded(SAVER);
-
-        mAutoTracker = createAutoAddTracker(USER + 1);
-        mAutoTracker.initialize();
-        assertFalse(mAutoTracker.isAdded(SAVER));
-    }
-
-    @Test
-    public void testChangeUser() {
-        mAutoTracker.setTileAdded(SAVER);
-
-        mAutoTracker = createAutoAddTracker(USER + 1);
-        mAutoTracker.changeUser(UserHandle.of(USER));
-        assertTrue(mAutoTracker.isAdded(SAVER));
-    }
-
-    @Test
-    public void testRestoredTilePositionPreserved() {
-        verify(mBroadcastDispatcher).registerReceiver(
-                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
-        String restoredTiles = "saver,internet,work,cast";
-        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
-
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        assertEquals(2, mAutoTracker.getRestoredTilePosition("work"));
-    }
-
-    @Test
-    public void testNoRestoredTileReturnsEndPosition() {
-        verify(mBroadcastDispatcher).registerReceiver(
-                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
-        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, null);
-
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        assertEquals(END_POSITION, mAutoTracker.getRestoredTilePosition("work"));
-    }
-
-    @Test
-    public void testBroadcastReceiverRegistered() {
-        verify(mBroadcastDispatcher).registerReceiver(
-                any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)),
-                anyInt(), any());
-
-        assertTrue(
-                mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED));
-    }
-
-    @Test
-    public void testBroadcastReceiverChangesWithUser() {
-        mAutoTracker.changeUser(UserHandle.of(USER + 1));
-
-        InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher);
-        inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any());
-        inOrder.verify(mBroadcastDispatcher)
-                .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1)), anyInt(),
-                        any());
-    }
-
-    @Test
-    public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() {
-        verify(mBroadcastDispatcher).registerReceiver(
-                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(), anyInt(), any());
-
-        // These tiles were present in the original device
-        String restoredTiles = "saver,work,internet,cast";
-        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        // And these tiles have been auto-added in the original device
-        // (no auto-added before restore)
-        String restoredAutoAddTiles = "work";
-        Intent restoreAutoAddTilesIntent =
-                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
-        // Then, don't remove any current tiles
-        verify(mQSHost, never()).removeTiles(any());
-        assertEquals(restoredAutoAddTiles,
-                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
-    }
-
-    @Test
-    public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() {
-        verify(mBroadcastDispatcher)
-                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
-                        anyInt(), any());
-
-        // These tiles were present in the original device
-        String restoredTiles = "saver,internet,cast";
-        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        // And these tiles have been auto-added in the original device
-        // (no auto-added before restore)
-        String restoredAutoAddTiles = "work";
-        Intent restoreAutoAddTilesIntent =
-                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
-        // Then, remove work tile
-        verify(mQSHost).removeTiles(List.of("work"));
-        assertEquals(restoredAutoAddTiles,
-                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
-    }
-
-    @Test
-    public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() {
-        verify(mBroadcastDispatcher)
-                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
-                        anyInt(), any());
-
-        // These tiles were present in the original device
-        String restoredTiles = "saver,internet,cast";
-        Intent restoreTilesIntent =
-                makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        // And these tiles have been auto-added in the original device
-        // (no auto-added before restore)
-        String restoredAutoAddTiles = "work";
-        Intent restoreAutoAddTilesIntent =
-                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
-        // Then, remove work tile
-        verify(mQSHost).removeTiles(List.of("work"));
-        assertEquals(restoredAutoAddTiles,
-                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
-    }
-
-    @Test
-    public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() {
-        verify(mBroadcastDispatcher)
-                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any(),
-                        anyInt(), any());
-
-        // These tiles were present in the original device
-        String restoredTiles = "saver,internet,cast";
-        Intent restoreTilesIntent =
-                makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
-
-        // And these tiles have been auto-added in the original device
-        // (no auto-added before restore)
-        String restoredAutoAddTiles = "work";
-        Intent restoreAutoAddTilesIntent =
-                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles);
-        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
-
-        // Then, remove work tile
-        verify(mQSHost).removeTiles(List.of("work"));
-
-        String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER);
-        assertEquals(2, setting.split(",").length);
-        assertTrue(setting.contains("work"));
-        assertTrue(setting.contains("inversion"));
-    }
-
-
-    private Intent makeRestoreIntent(
-            String settingName, String previousValue, String restoredValue) {
-        Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED);
-        intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName);
-        intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue);
-        intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue);
-        return intent;
-    }
-
-    private AutoAddTracker createAutoAddTracker(int user) {
-        // Null handler wil dispatch sync.
-        return new AutoAddTracker(
-                mSecureSettings,
-                mBroadcastDispatcher,
-                mQSHost,
-                mDumpManager,
-                null,
-                mBackgroundExecutor,
-                user
-        );
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 206bbbf..4ce2d7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -51,6 +51,7 @@
 
 import androidx.compose.ui.platform.ComposeView;
 import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
@@ -611,7 +612,8 @@
         when(mQSContainerImplController.getView()).thenReturn(mContainer);
         when(mQSPanelController.getTileLayout()).thenReturn(mQQsTileLayout);
         when(mQuickQSPanelController.getTileLayout()).thenReturn(mQsTileLayout);
-        when(mFooterActionsViewModelFactory.create(any())).thenReturn(mFooterActionsViewModel);
+        when(mFooterActionsViewModelFactory.create(any(LifecycleOwner.class)))
+                .thenReturn(mFooterActionsViewModel);
     }
 
     private void setUpMedia() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
deleted file mode 100644
index 6d1bc82..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ /dev/null
@@ -1,786 +0,0 @@
-/*
- * 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.systemui.qs;
-
-
-import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
-import static com.android.systemui.Flags.FLAG_QS_NEW_TILES;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.util.SparseArray;
-
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.util.CollectionUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.Expandable;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.qs.external.CustomTileStatePersister;
-import com.android.systemui.qs.external.TileLifecycleManager;
-import com.android.systemui.qs.external.TileServiceKey;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.di.NewQSTileFactory;
-import com.android.systemui.res.R;
-import com.android.systemui.settings.UserFileManager;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.FakeSharedPreferences;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import dagger.Lazy;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-import javax.inject.Provider;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class QSTileHostTest extends SysuiTestCase {
-
-    private static String MOCK_STATE_STRING = "MockState";
-    private static ComponentName CUSTOM_TILE =
-            ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
-    private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
-    private static final String SETTING = QSHost.TILES_SETTING;
-    @Mock
-    private PluginManager mPluginManager;
-    @Mock
-    private TunerService mTunerService;
-    @Mock
-    private AutoTileManager mAutoTiles;
-    @Mock
-    private ShadeController mShadeController;
-    @Mock
-    private QSLogger mQSLogger;
-    @Mock
-    private CustomTile mCustomTile;
-    @Mock
-    private UserTracker mUserTracker;
-    @Mock
-    private CustomTileStatePersister mCustomTileStatePersister;
-    @Mock
-    private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
-    @Mock
-    private TileLifecycleManager mTileLifecycleManager;
-    @Mock
-    private UserFileManager mUserFileManager;
-
-    private SecureSettings mSecureSettings;
-
-    private QSFactory mDefaultFactory;
-
-    private SparseArray<SharedPreferences> mSharedPreferencesByUser;
-
-    private QSPipelineFlagsRepository mQSPipelineFlagsRepository;
-
-    private FakeExecutor mMainExecutor;
-
-    private QSTileHost mQSTileHost;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
-        mSetFlagsRule.disableFlags(FLAG_QS_NEW_TILES);
-        mQSPipelineFlagsRepository = new QSPipelineFlagsRepository();
-
-        mMainExecutor = new FakeExecutor(new FakeSystemClock());
-
-        mSharedPreferencesByUser = new SparseArray<>();
-        when(mTileLifecycleManagerFactory
-                .create(any(Intent.class), any(UserHandle.class)))
-                .thenReturn(mTileLifecycleManager);
-        when(mUserFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
-                .thenAnswer((Answer<SharedPreferences>) invocation -> {
-                    assertEquals(QSTileHost.TILES, invocation.getArgument(0));
-                    int userId = invocation.getArgument(2);
-                    if (!mSharedPreferencesByUser.contains(userId)) {
-                        mSharedPreferencesByUser.put(userId, new FakeSharedPreferences());
-                    }
-                    return mSharedPreferencesByUser.get(userId);
-                });
-
-        mSecureSettings = new FakeSettings();
-        saveSetting("");
-        setUpTileFactory();
-        mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
-                mPluginManager, mTunerService, () -> mAutoTiles, () -> mShadeController,
-                mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
-                mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
-        mMainExecutor.runAllReady();
-
-        mSecureSettings.registerContentObserverForUserSync(SETTING, new ContentObserver(null) {
-            @Override
-            public void onChange(boolean selfChange) {
-                super.onChange(selfChange);
-                mMainExecutor.execute(() -> mQSTileHost.onTuningChanged(SETTING, getSetting()));
-                mMainExecutor.runAllReady();
-            }
-        }, mUserTracker.getUserId());
-    }
-
-    private void saveSetting(String value) {
-        mSecureSettings.putStringForUser(
-                SETTING, value, "", false, mUserTracker.getUserId(), false);
-    }
-
-    private String getSetting() {
-        return mSecureSettings.getStringForUser(SETTING, mUserTracker.getUserId());
-    }
-
-    private void setUpTileFactory() {
-        mDefaultFactory = new FakeQSFactory(spec -> {
-            if ("spec1".equals(spec)) {
-                return new TestTile1(mQSTileHost);
-            } else if ("spec2".equals(spec)) {
-                return new TestTile2(mQSTileHost);
-            } else if ("spec3".equals(spec)) {
-                return new TestTile3(mQSTileHost);
-            } else if ("na".equals(spec)) {
-                return new NotAvailableTile(mQSTileHost);
-            } else if (CUSTOM_TILE_SPEC.equals(spec)) {
-                QSTile tile = mCustomTile;
-                QSTile.State s = mock(QSTile.State.class);
-                s.spec = spec;
-                when(mCustomTile.getState()).thenReturn(s);
-                return tile;
-            } else if ("internet".equals(spec)
-                    || "wifi".equals(spec)
-                    || "cell".equals(spec)) {
-                return new TestTile1(mQSTileHost);
-            } else {
-                return null;
-            }
-        });
-        when(mCustomTile.isAvailable()).thenReturn(true);
-    }
-
-    @Test
-    public void testLoadTileSpecs_emptySetting() {
-        List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
-        assertFalse(tiles.isEmpty());
-    }
-
-    @Test
-    public void testLoadTileSpecs_nullSetting() {
-        List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
-        assertFalse(tiles.isEmpty());
-    }
-
-    @Test
-    public void testInvalidSpecUsesDefault() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
-        saveSetting("not-valid");
-
-        assertEquals(2, mQSTileHost.getTiles().size());
-    }
-
-    @Test
-    public void testRemoveWifiAndCellularWithoutInternet() {
-        saveSetting("wifi, spec1, cell, spec2");
-
-        assertEquals("internet", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec1", mQSTileHost.getSpecs().get(1));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testRemoveWifiAndCellularWithInternet() {
-        saveSetting("wifi, spec1, cell, spec2, internet");
-
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
-        assertEquals("internet", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testRemoveWifiWithoutInternet() {
-        saveSetting("spec1, wifi, spec2");
-
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("internet", mQSTileHost.getSpecs().get(1));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testRemoveCellWithInternet() {
-        saveSetting("spec1, spec2, cell, internet");
-
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
-        assertEquals("internet", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testNoWifiNoCellularNoInternet() {
-        saveSetting("spec1,spec2");
-
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
-    }
-
-    @Test
-    public void testSpecWithInvalidDoesNotUseDefault() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
-        saveSetting("spec2,not-valid");
-
-        assertEquals(1, mQSTileHost.getTiles().size());
-        QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
-        assertTrue(element instanceof TestTile2);
-    }
-
-    @Test
-    public void testDump() {
-        saveSetting("spec1,spec2");
-        StringWriter w = new StringWriter();
-        PrintWriter pw = new PrintWriter(w);
-        mQSTileHost.dump(pw, new String[]{});
-
-        String output = "QSTileHost:" + "\n"
-                + "tile specs: [spec1, spec2]" + "\n"
-                + "current user: 0" + "\n"
-                + "is dirty: false" + "\n"
-                + "tiles:" + "\n"
-                + "TestTile1:" + "\n"
-                + "    MockState" + "\n"
-                + "TestTile2:" + "\n"
-                + "    MockState" + "\n";
-
-        System.out.println(output);
-        System.out.println(w.getBuffer().toString());
-
-        assertEquals(output, w.getBuffer().toString());
-    }
-
-    @Test
-    public void testDefault() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles_default, "spec1");
-        saveSetting("default");
-        assertEquals(1, mQSTileHost.getTiles().size());
-        QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
-        assertTrue(element instanceof TestTile1);
-        verify(mQSLogger).logTileAdded("spec1");
-    }
-
-    @Test
-    public void testNoRepeatedSpecs_addTile() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
-        saveSetting("spec1,spec2");
-
-        mQSTileHost.addTile("spec1");
-
-        assertEquals(2, mQSTileHost.getSpecs().size());
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
-    }
-
-    @Test
-    public void testAddTileAtValidPosition() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
-        saveSetting("spec1,spec3");
-
-        mQSTileHost.addTile("spec2", 1);
-        mMainExecutor.runAllReady();
-
-        assertEquals(3, mQSTileHost.getSpecs().size());
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(1));
-        assertEquals("spec3", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testAddTileAtInvalidPositionAddsToEnd() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
-        saveSetting("spec1,spec3");
-
-        mQSTileHost.addTile("spec2", 100);
-        mMainExecutor.runAllReady();
-
-        assertEquals(3, mQSTileHost.getSpecs().size());
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec3", mQSTileHost.getSpecs().get(1));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testAddTileAtEnd() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
-        saveSetting("spec1,spec3");
-
-        mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
-        mMainExecutor.runAllReady();
-
-        assertEquals(3, mQSTileHost.getSpecs().size());
-        assertEquals("spec1", mQSTileHost.getSpecs().get(0));
-        assertEquals("spec3", mQSTileHost.getSpecs().get(1));
-        assertEquals("spec2", mQSTileHost.getSpecs().get(2));
-    }
-
-    @Test
-    public void testNoRepeatedSpecs_customTile() {
-        saveSetting(CUSTOM_TILE_SPEC);
-
-        mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
-        mMainExecutor.runAllReady();
-
-        assertEquals(1, mQSTileHost.getSpecs().size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
-    }
-
-    @Test
-    public void testAddedAtBeginningOnDefault_customTile() {
-        saveSetting("spec1"); // seed
-
-        mQSTileHost.addTile(CUSTOM_TILE);
-        mMainExecutor.runAllReady();
-
-        assertEquals(2, mQSTileHost.getSpecs().size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
-    }
-
-    @Test
-    public void testAddedAtBeginning_customTile() {
-        saveSetting("spec1"); // seed
-
-        mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
-        mMainExecutor.runAllReady();
-
-        assertEquals(2, mQSTileHost.getSpecs().size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
-    }
-
-    @Test
-    public void testAddedAtEnd_customTile() {
-        saveSetting("spec1"); // seed
-
-        mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
-        mMainExecutor.runAllReady();
-
-        assertEquals(2, mQSTileHost.getSpecs().size());
-        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(1));
-    }
-
-    @Test
-    public void testLoadTileSpec_repeated() {
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
-
-        assertEquals(2, specs.size());
-        assertEquals("spec1", specs.get(0));
-        assertEquals("spec2", specs.get(1));
-    }
-
-    @Test
-    public void testLoadTileSpec_repeatedInDefault() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
-    }
-
-    @Test
-    public void testLoadTileSpec_repeatedDefaultAndSetting() {
-        mContext.getOrCreateTestableResources()
-                .addOverride(R.string.quick_settings_tiles_default, "spec1");
-        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
-    }
-
-    @Test
-    public void testNotAvailableTile_specNotNull() {
-        saveSetting("na");
-        verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
-    }
-
-    @Test
-    public void testCustomTileRemoved_stateDeleted() {
-        mQSTileHost.changeTilesByUser(List.of(CUSTOM_TILE_SPEC), List.of());
-
-        verify(mCustomTileStatePersister)
-                .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
-    }
-
-    @Test
-    public void testRemoveTiles() {
-        saveSetting("spec1,spec2,spec3");
-
-        mQSTileHost.removeTiles(List.of("spec1", "spec2"));
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec3"), mQSTileHost.getSpecs());
-    }
-
-    @Test
-    public void testTilesRemovedInQuickSuccession() {
-        saveSetting("spec1,spec2,spec3");
-        mQSTileHost.removeTile("spec1");
-        mQSTileHost.removeTile("spec3");
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
-        assertEquals("spec2", getSetting());
-    }
-
-    @Test
-    public void testAddTileInMainThread() {
-        saveSetting("spec1,spec2");
-
-        mQSTileHost.addTile("spec3");
-        assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
-    }
-
-    @Test
-    public void testRemoveTileInMainThread() {
-        saveSetting("spec1,spec2");
-
-        mQSTileHost.removeTile("spec1");
-        assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
-    }
-
-    @Test
-    public void testRemoveTilesInMainThread() {
-        saveSetting("spec1,spec2,spec3");
-
-        mQSTileHost.removeTiles(List.of("spec3", "spec1"));
-        assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
-    }
-
-    @Test
-    public void testRemoveTileByUserInMainThread() {
-        saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
-        mQSTileHost.removeTileByUser(CUSTOM_TILE);
-        assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.getSpecs());
-
-        mMainExecutor.runAllReady();
-        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
-    }
-
-    @Test
-    public void testNonValidTileNotStoredInSettings() {
-        saveSetting("spec1,not-valid");
-
-        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
-        assertEquals("spec1", getSetting());
-    }
-
-    @Test
-    public void testNotAvailableTileNotStoredInSettings() {
-        saveSetting("spec1,na");
-
-        assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
-        assertEquals("spec1", getSetting());
-    }
-
-    @Test
-    public void testIsTileAdded_true() {
-        int user = mUserTracker.getUserId();
-        getSharedPreferencesForUser(user)
-                .edit()
-                .putBoolean(CUSTOM_TILE.flattenToString(), true)
-                .apply();
-
-        assertTrue(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
-    }
-
-    @Test
-    public void testIsTileAdded_false() {
-        int user = mUserTracker.getUserId();
-        getSharedPreferencesForUser(user)
-                .edit()
-                .putBoolean(CUSTOM_TILE.flattenToString(), false)
-                .apply();
-
-        assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
-    }
-
-    @Test
-    public void testIsTileAdded_notSet() {
-        int user = mUserTracker.getUserId();
-
-        assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
-    }
-
-    @Test
-    public void testIsTileAdded_differentUser() {
-        int user = mUserTracker.getUserId();
-        mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user)
-                .edit()
-                .putBoolean(CUSTOM_TILE.flattenToString(), true)
-                .apply();
-
-        assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user + 1));
-    }
-
-    @Test
-    public void testSetTileAdded_true() {
-        int user = mUserTracker.getUserId();
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
-        assertTrue(getSharedPreferencesForUser(user)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testSetTileAdded_false() {
-        int user = mUserTracker.getUserId();
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, false);
-
-        assertFalse(getSharedPreferencesForUser(user)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testSetTileAdded_differentUser() {
-        int user = mUserTracker.getUserId();
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
-        assertFalse(getSharedPreferencesForUser(user + 1)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testSetTileRemoved_afterCustomTileChangedByUser() {
-        int user = mUserTracker.getUserId();
-        saveSetting(CUSTOM_TILE_SPEC);
-
-        // This will be done by TileServiceManager
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
-        mQSTileHost.changeTilesByUser(mQSTileHost.getSpecs(), List.of("spec1"));
-        assertFalse(getSharedPreferencesForUser(user)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testSetTileRemoved_removedByUser() {
-        int user = mUserTracker.getUserId();
-        saveSetting(CUSTOM_TILE_SPEC);
-
-        // This will be done by TileServiceManager
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
-        mQSTileHost.removeTileByUser(CUSTOM_TILE);
-        mMainExecutor.runAllReady();
-        assertFalse(getSharedPreferencesForUser(user)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testSetTileRemoved_removedBySystem() {
-        int user = mUserTracker.getUserId();
-        saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
-        // This will be done by TileServiceManager
-        mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
-
-        mQSTileHost.removeTile(CUSTOM_TILE_SPEC);
-        mMainExecutor.runAllReady();
-        assertFalse(getSharedPreferencesForUser(user)
-                .getBoolean(CUSTOM_TILE.flattenToString(), false));
-    }
-
-    @Test
-    public void testProtoDump_noTiles() {
-        SystemUIProtoDump proto = new SystemUIProtoDump();
-        mQSTileHost.dumpProto(proto, new String[0]);
-
-        assertEquals(0, proto.tiles.length);
-    }
-
-    @Test
-    public void testTilesInOrder() {
-        saveSetting("spec1," + CUSTOM_TILE_SPEC);
-
-        SystemUIProtoDump proto = new SystemUIProtoDump();
-        mQSTileHost.dumpProto(proto, new String[0]);
-
-        assertEquals(2, proto.tiles.length);
-        assertEquals("spec1", proto.tiles[0].getSpec());
-        assertEquals(CUSTOM_TILE.getPackageName(), proto.tiles[1].getComponentName().packageName);
-        assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
-    }
-
-    private SharedPreferences getSharedPreferencesForUser(int user) {
-        return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
-    }
-
-    private class TestQSTileHost extends QSTileHost {
-        TestQSTileHost(Context context, Lazy<NewQSTileFactory> newQSTileFactoryProvider,
-                QSFactory defaultFactory, Executor mainExecutor,
-                PluginManager pluginManager, TunerService tunerService,
-                Provider<AutoTileManager> autoTiles,
-                Lazy<ShadeController> shadeController, QSLogger qsLogger,
-                UserTracker userTracker, SecureSettings secureSettings,
-                CustomTileStatePersister customTileStatePersister,
-                TileLifecycleManager.Factory tileLifecycleManagerFactory,
-                UserFileManager userFileManager, QSPipelineFlagsRepository featureFlags) {
-            super(context, newQSTileFactoryProvider, defaultFactory, mainExecutor, pluginManager,
-                    tunerService, autoTiles, shadeController, qsLogger,
-                    userTracker, secureSettings, customTileStatePersister,
-                    tileLifecycleManagerFactory, userFileManager, featureFlags);
-        }
-
-        @Override
-        public void onPluginConnected(QSFactory plugin, Context pluginContext) {
-        }
-
-        @Override
-        public void onPluginDisconnected(QSFactory plugin) {
-        }
-    }
-
-
-    private class TestTile extends QSTileImpl<QSTile.State> {
-
-        protected TestTile(QSHost host) {
-            super(
-                    host,
-                    mock(QsEventLogger.class),
-                    mock(Looper.class),
-                    mock(Handler.class),
-                    new FalsingManagerFake(),
-                    mock(MetricsLogger.class),
-                    mock(StatusBarStateController.class),
-                    mock(ActivityStarter.class),
-                    QSTileHostTest.this.mQSLogger
-            );
-        }
-
-        @Override
-        public State newTileState() {
-            State s = mock(QSTile.State.class);
-            when(s.toString()).thenReturn(MOCK_STATE_STRING);
-            return s;
-        }
-
-        @Override
-        protected void handleClick(@Nullable Expandable expandable) {}
-
-        @Override
-        protected void handleUpdateState(State state, Object arg) {}
-
-        @Override
-        public int getMetricsCategory() {
-            return 0;
-        }
-
-        @Override
-        public Intent getLongClickIntent() {
-            return null;
-        }
-
-        @Override
-        public CharSequence getTileLabel() {
-            return null;
-        }
-    }
-
-    private class TestTile1 extends TestTile {
-
-        protected TestTile1(QSHost host) {
-            super(host);
-        }
-    }
-
-    private class TestTile2 extends TestTile {
-
-        protected TestTile2(QSHost host) {
-            super(host);
-        }
-    }
-
-    private class TestTile3 extends TestTile {
-
-        protected TestTile3(QSHost host) {
-            super(host);
-        }
-    }
-
-    private class NotAvailableTile extends TestTile {
-
-        protected NotAvailableTile(QSHost host) {
-            super(host);
-        }
-
-        @Override
-        public boolean isAvailable() {
-            return false;
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 68307b1..c1cf91d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -23,6 +23,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
@@ -50,6 +52,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Bundle;
@@ -183,6 +186,40 @@
                 .thenReturn(defaultPackageInfo);
     }
 
+    private void setPackageInstalledForUser(
+            boolean installed,
+            boolean active,
+            boolean toggleable,
+            int user
+    ) throws Exception {
+        ServiceInfo defaultServiceInfo = null;
+        if (installed) {
+            defaultServiceInfo = new ServiceInfo();
+            defaultServiceInfo.metaData = new Bundle();
+            defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, active);
+            defaultServiceInfo.metaData
+                    .putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, toggleable);
+            when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), eq(user)))
+                    .thenReturn(defaultServiceInfo);
+            if (user == 0) {
+                when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt()))
+                        .thenReturn(defaultServiceInfo);
+            }
+            PackageInfo defaultPackageInfo = new PackageInfo();
+            when(mMockPackageManagerAdapter.getPackageInfoAsUser(anyString(), anyInt(), eq(user)))
+                    .thenReturn(defaultPackageInfo);
+        } else {
+            when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), eq(user)))
+                    .thenReturn(null);
+            if (user == 0) {
+                when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt()))
+                        .thenThrow(new PackageManager.NameNotFoundException());
+            }
+            when(mMockPackageManagerAdapter.getPackageInfoAsUser(anyString(), anyInt(), eq(user)))
+                    .thenThrow(new PackageManager.NameNotFoundException());
+        }
+    }
+
     private void verifyBind(int times) {
         assertEquals(times > 0, mContext.isBound(mTileServiceComponentName));
     }
@@ -557,6 +594,100 @@
         verify(mockContext).unbindService(captor.getValue());
     }
 
+    @Test
+    public void testIsActive_user0_packageInstalled() throws Exception {
+        setPackageInstalledForUser(true, true, false, 0);
+        mUser = UserHandle.of(0);
+
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mWrappedContext,
+                mock(IQSService.class),
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher,
+                mTileServiceIntent,
+                mUser,
+                mActivityManager,
+                mDeviceIdleController,
+                mExecutor);
+
+        assertThat(manager.isActiveTile()).isTrue();
+    }
+
+    @Test
+    public void testIsActive_user10_packageInstalled_notForUser0() throws Exception {
+        setPackageInstalledForUser(true, true, false, 10);
+        setPackageInstalledForUser(false, false, false, 0);
+        mUser = UserHandle.of(10);
+
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mWrappedContext,
+                mock(IQSService.class),
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher,
+                mTileServiceIntent,
+                mUser,
+                mActivityManager,
+                mDeviceIdleController,
+                mExecutor);
+
+        assertThat(manager.isActiveTile()).isTrue();
+    }
+
+    @Test
+    public void testIsToggleable_user0_packageInstalled() throws Exception {
+        setPackageInstalledForUser(true, false, true, 0);
+        mUser = UserHandle.of(0);
+
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mWrappedContext,
+                mock(IQSService.class),
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher,
+                mTileServiceIntent,
+                mUser,
+                mActivityManager,
+                mDeviceIdleController,
+                mExecutor);
+
+        assertThat(manager.isToggleableTile()).isTrue();
+    }
+
+    @Test
+    public void testIsToggleable_user10_packageInstalled_notForUser0() throws Exception {
+        setPackageInstalledForUser(true, false, true, 10);
+        setPackageInstalledForUser(false, false, false, 0);
+        mUser = UserHandle.of(10);
+
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mWrappedContext,
+                mock(IQSService.class),
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher,
+                mTileServiceIntent,
+                mUser,
+                mActivityManager,
+                mDeviceIdleController,
+                mExecutor);
+
+        assertThat(manager.isToggleableTile()).isTrue();
+    }
+
+    @Test
+    public void testIsToggleableActive_installedForDifferentUser() throws Exception {
+        setPackageInstalledForUser(true, false, false, 10);
+        setPackageInstalledForUser(false, true, true, 0);
+        mUser = UserHandle.of(10);
+
+        TileLifecycleManager manager = new TileLifecycleManager(mHandler, mWrappedContext,
+                mock(IQSService.class),
+                mMockPackageManagerAdapter,
+                mMockBroadcastDispatcher,
+                mTileServiceIntent,
+                mUser,
+                mActivityManager,
+                mDeviceIdleController,
+                mExecutor);
+
+        assertThat(manager.isToggleableTile()).isFalse();
+        assertThat(manager.isActiveTile()).isFalse();
+    }
+
     private void mockChangeEnabled(long changeId, boolean enabled) {
         doReturn(enabled).when(() -> CompatChanges.isChangeEnabled(eq(changeId), anyString(),
                 any(UserHandle.class)));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
new file mode 100644
index 0000000..d9faa30
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -0,0 +1,228 @@
+/*
+ * 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 androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.assert
+import androidx.compose.ui.test.hasContentDescription
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onChildAt
+import androidx.compose.ui.test.onChildren
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+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.viewmodel.EditTileViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@FlakyTest(bugId = 360351805)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DragAndDropTest : SysuiTestCase() {
+    @get:Rule val composeRule = createComposeRule()
+
+    // TODO(ostonge): Investigate why drag isn't detected when using performTouchInput
+    @Composable
+    private fun EditTileGridUnderTest(
+        listState: EditTileListState,
+        onSetTiles: (List<TileSpec>) -> Unit
+    ) {
+        DefaultEditTileGrid(
+            currentListState = listState,
+            otherTiles = listOf(),
+            columns = 4,
+            modifier = Modifier.fillMaxSize(),
+            onAddTile = { _, _ -> },
+            onRemoveTile = {},
+            onSetTiles = onSetTiles,
+            onResize = {},
+        )
+    }
+
+    @Test
+    fun draggedTile_shouldDisappear() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) {
+                tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
+            }
+        }
+        composeRule.waitForIdle()
+
+        listState.onStarted(TestEditTiles[0])
+
+        // Tile is being dragged, it should be replaced with a placeholder
+        composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist()
+
+        // Available tiles should disappear
+        composeRule.onNodeWithTag(AVAILABLE_TILES_GRID_TEST_TAG).assertDoesNotExist()
+
+        // Remove drop zone should appear
+        composeRule.onNodeWithText("Remove").assertExists()
+
+        // Every other tile should still be in the same order
+        composeRule.assertTileGridContainsExactly(listOf("tileB", "tileC", "tileD_large", "tileE"))
+    }
+
+    @Test
+    fun draggedTile_shouldChangePosition() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) {
+                tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
+            }
+        }
+        composeRule.waitForIdle()
+
+        listState.onStarted(TestEditTiles[0])
+        listState.onMoved(1, false)
+        listState.onDrop()
+
+        // Available tiles should re-appear
+        composeRule.onNodeWithTag(AVAILABLE_TILES_GRID_TEST_TAG).assertExists()
+
+        // Remove drop zone should disappear
+        composeRule.onNodeWithText("Remove").assertDoesNotExist()
+
+        // Tile A and B should swap places
+        composeRule.assertTileGridContainsExactly(
+            listOf("tileB", "tileA", "tileC", "tileD_large", "tileE")
+        )
+    }
+
+    @Test
+    fun draggedTileOut_shouldBeRemoved() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) {
+                tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
+            }
+        }
+        composeRule.waitForIdle()
+
+        listState.onStarted(TestEditTiles[0])
+        listState.movedOutOfBounds()
+        listState.onDrop()
+
+        // Available tiles should re-appear
+        composeRule.onNodeWithTag(AVAILABLE_TILES_GRID_TEST_TAG).assertExists()
+
+        // Remove drop zone should disappear
+        composeRule.onNodeWithText("Remove").assertDoesNotExist()
+
+        // Tile A is gone
+        composeRule.assertTileGridContainsExactly(listOf("tileB", "tileC", "tileD_large", "tileE"))
+    }
+
+    @Test
+    fun draggedNewTileIn_shouldBeAdded() {
+        var tiles by mutableStateOf(TestEditTiles)
+        val listState = EditTileListState(tiles, 4)
+        composeRule.setContent {
+            EditTileGridUnderTest(listState) {
+                tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) }
+            }
+        }
+        composeRule.waitForIdle()
+
+        listState.onStarted(createEditTile("newTile"))
+        // Insert after tileD, which is at index 4
+        // [ a ] [ b ] [ c ] [ empty ]
+        // [ tile d ] [ e ]
+        listState.onMoved(4, insertAfter = true)
+        listState.onDrop()
+
+        // Available tiles should re-appear
+        composeRule.onNodeWithTag(AVAILABLE_TILES_GRID_TEST_TAG).assertExists()
+
+        // Remove drop zone should disappear
+        composeRule.onNodeWithText("Remove").assertDoesNotExist()
+
+        // newTile is added after tileD
+        composeRule.assertTileGridContainsExactly(
+            listOf("tileA", "tileB", "tileC", "tileD_large", "newTile", "tileE")
+        )
+    }
+
+    private fun ComposeContentTestRule.assertTileGridContainsExactly(specs: List<String>) {
+        onNodeWithTag(CURRENT_TILES_GRID_TEST_TAG).onChildren().apply {
+            fetchSemanticsNodes().forEachIndexed { index, _ ->
+                get(index).onChildAt(0).assert(hasContentDescription(specs[index]))
+            }
+        }
+    }
+
+    companion object {
+        private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid"
+        private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid"
+
+        private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> {
+            return SizedTileImpl(
+                EditTileViewModel(
+                    tileSpec = TileSpec.create(tileSpec),
+                    icon =
+                        Icon.Resource(
+                            android.R.drawable.star_on,
+                            ContentDescription.Loaded(tileSpec)
+                        ),
+                    label = Text.Loaded(tileSpec),
+                    appName = null,
+                    isCurrent = true,
+                    availableEditActions = emptySet(),
+                ),
+                getWidth(tileSpec),
+            )
+        }
+
+        private fun getWidth(tileSpec: String): Int {
+            return if (tileSpec.endsWith("large")) {
+                2
+            } else {
+                1
+            }
+        }
+
+        private val TestEditTiles =
+            listOf(
+                createEditTile("tileA"),
+                createEditTile("tileB"),
+                createEditTile("tileC"),
+                createEditTile("tileD_large"),
+                createEditTile("tileE"),
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
index 970cd17..090a85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/shared/QSPipelineFlagsRepositoryTest.kt
@@ -15,20 +15,6 @@
     private val underTest = QSPipelineFlagsRepository()
 
     @Test
-    fun pipelineFlagDisabled() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
-        assertThat(underTest.pipelineEnabled).isFalse()
-    }
-
-    @Test
-    fun pipelineFlagEnabled() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_QS_NEW_PIPELINE)
-
-        assertThat(underTest.pipelineEnabled).isTrue()
-    }
-
-    @Test
     fun tilesFlagDisabled() {
         mSetFlagsRule.disableFlags(Flags.FLAG_QS_NEW_TILES)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index ebab049..748c7d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -497,6 +497,17 @@
         assertThat(mTile.mRefreshes).isEqualTo(1);
     }
 
+    @Test
+    public void testStaleTriggeredOnUserSwitch() {
+        mTile.clearRefreshes();
+
+        mTile.userSwitch(10);
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mTile.isListening());
+        assertThat(mTile.mRefreshes).isEqualTo(1);
+    }
+
     private void assertEvent(UiEventLogger.UiEventEnum eventType,
             UiEventLoggerFake.FakeUiEvent fakeEvent) {
         assertEquals(eventType.getId(), fakeEvent.eventId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 79cb51a..e6ec07e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
@@ -32,6 +31,8 @@
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.AccessPointController
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -46,7 +47,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
 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
 import kotlinx.coroutines.test.StandardTestDispatcher
@@ -58,6 +58,10 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -87,6 +91,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var logger: QSLogger
     @Mock private lateinit var dialogManager: InternetDialogManager
+    @Mock private lateinit var wifiStateWorker: WifiStateWorker
     @Mock private lateinit var accessPointController: AccessPointController
 
     @Before
@@ -122,6 +127,7 @@
                 logger,
                 viewModel,
                 dialogManager,
+                wifiStateWorker,
                 accessPointController
             )
 
@@ -231,11 +237,28 @@
             assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
         }
 
+    @Test
+    fun secondaryClick_turnsWifiOff() {
+        whenever(wifiStateWorker.isWifiEnabled).thenReturn(true)
+
+        underTest.secondaryClick(null)
+
+        verify(wifiStateWorker, times(1)).isWifiEnabled = eq(false)
+    }
+
+    @Test
+    fun secondaryClick_turnsWifiOn() {
+        whenever(wifiStateWorker.isWifiEnabled).thenReturn(false)
+
+        underTest.secondaryClick(null)
+
+        verify(wifiStateWorker, times(1)).isWifiEnabled = eq(true)
+    }
+
     companion object {
         const val WIFI_SSID = "test ssid"
         val ACTIVE_WIFI =
             WifiNetworkModel.Active(
-                networkId = 1,
                 isValidated = true,
                 level = 4,
                 ssid = WIFI_SSID,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 8ea79d7..0cf9604 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -18,7 +18,10 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
@@ -38,6 +41,7 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
+import com.android.systemui.qs.tiles.dialog.WifiStateWorker;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.connectivity.AccessPointController;
 import com.android.systemui.statusbar.connectivity.IconState;
@@ -65,6 +69,8 @@
     @Mock
     private InternetDialogManager mInternetDialogManager;
     @Mock
+    private WifiStateWorker mWifiStateWorker;
+    @Mock
     private QsEventLogger mUiEventLogger;
 
     private TestableLooper mTestableLooper;
@@ -89,7 +95,8 @@
             mock(QSLogger.class),
             mNetworkController,
             mAccessPointController,
-                mInternetDialogManager
+            mInternetDialogManager,
+            mWifiStateWorker
         );
 
         mTile.initialize();
@@ -167,4 +174,22 @@
         assertThat(mTile.getState().icon).isEqualTo(
                 QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable));
     }
+
+    @Test
+    public void secondaryClick_turnsWifiOff() {
+        when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
+
+        mTile.secondaryClick(null);
+
+        verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(false));
+    }
+
+    @Test
+    public void secondaryClick_turnsWifiOn() {
+        when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
+
+        mTile.secondaryClick(null);
+
+        verify(mWifiStateWorker, times(1)).setWifiEnabled(eq(true));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index a5de7cd..848c8db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -25,10 +25,13 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
-import com.android.settingslib.notification.data.repository.FakeZenModeRepository
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
@@ -36,19 +39,21 @@
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
 import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
 import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
 import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.settings.FakeSettings
 import com.android.systemui.util.settings.SecureSettings
 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.After
@@ -65,6 +70,9 @@
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 class ModesTileTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val testDispatcher = kosmos.testDispatcher
 
     @Mock private lateinit var qsHost: QSHost
 
@@ -82,20 +90,13 @@
 
     @Mock private lateinit var dialogDelegate: ModesDialogDelegate
 
-    private val testDispatcher = UnconfinedTestDispatcher()
-    private val testScope = TestScope(testDispatcher)
-
     private val inputHandler = FakeQSTileIntentUserInputHandler()
-    private val zenModeRepository = FakeZenModeRepository()
-    private val tileDataInteractor = ModesTileDataInteractor(zenModeRepository, testDispatcher)
+    private val zenModeRepository = kosmos.zenModeRepository
+    private val tileDataInteractor =
+        ModesTileDataInteractor(context, kosmos.zenModeInteractor, testDispatcher)
     private val mapper =
         ModesTileMapper(
-            context.orCreateTestableResources
-                .apply {
-                    addOverride(R.drawable.qs_dnd_icon_on, TestStubDrawable())
-                    addOverride(R.drawable.qs_dnd_icon_off, TestStubDrawable())
-                }
-                .resources,
+            context.resources,
             context.theme,
         )
 
@@ -119,7 +120,7 @@
                 QSTileConfigTestBuilder.build {
                     uiConfig =
                         QSTileUIConfig.Resource(
-                            iconRes = R.drawable.qs_dnd_icon_off,
+                            iconRes = ModesTile.ICON_RES_ID,
                             labelRes = R.string.quick_settings_modes_label,
                         )
                 }
@@ -171,4 +172,43 @@
 
             assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
         }
+
+    @Test
+    fun handleUpdateState_withTileModel_updatesState() =
+        testScope.runTest {
+            val tileState =
+                QSTile.State().apply {
+                    state = Tile.STATE_INACTIVE
+                    secondaryLabel = "Old secondary label"
+                }
+            val model =
+                ModesTileModel(
+                    isActivated = true,
+                    activeModes = listOf("One", "Two"),
+                    icon = TestStubDrawable().asIcon()
+                )
+
+            underTest.handleUpdateState(tileState, model)
+
+            assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+            assertThat(tileState.secondaryLabel).isEqualTo("2 modes are active")
+        }
+
+    @Test
+    fun handleUpdateState_withNull_updatesState() =
+        testScope.runTest {
+            val tileState =
+                QSTile.State().apply {
+                    state = Tile.STATE_INACTIVE
+                    secondaryLabel = "Old secondary label"
+                }
+            zenModeRepository.addMode("One", active = true)
+            zenModeRepository.addMode("Two", active = true)
+            runCurrent()
+
+            underTest.handleUpdateState(tileState, null)
+
+            assertThat(tileState.state).isEqualTo(Tile.STATE_ACTIVE)
+            assertThat(tileState.secondaryLabel).isEqualTo("2 modes are active")
+        }
 }
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 ff8c448..643debf 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
@@ -6,20 +6,20 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AlertDialog;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Handler;
+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;
@@ -44,20 +44,18 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
 
 import java.util.List;
 
-@Ignore("b/257089187")
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
 public class InternetDialogDelegateTest extends SysuiTestCase {
 
     private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
@@ -87,6 +85,8 @@
     private SystemUIDialog.Factory mSystemUIDialogFactory;
     @Mock
     private SystemUIDialog mSystemUIDialog;
+    @Mock
+    private Window mWindow;
 
     private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
     private InternetDialogDelegate mInternetDialogDelegate;
@@ -121,13 +121,16 @@
         when(mInternetDialogController.getMobileNetworkSummary(anyInt()))
                 .thenReturn(MOBILE_NETWORK_SUMMARY);
         when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
-
+        when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID);
         mMockitoSession = ExtendedMockito.mockitoSession()
                 .spyStatic(WifiEnterpriseRestrictionUtils.class)
                 .startMocking();
         when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
         when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class)))
                 .thenReturn(mSystemUIDialog);
+        when(mSystemUIDialog.getContext()).thenReturn(mContext);
+        when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
         createInternetDialog();
     }
 
@@ -146,6 +149,8 @@
                 mBgExecutor,
                 mKeyguard,
                 mSystemUIDialogFactory);
+        mInternetDialogDelegate.createDialog();
+        mInternetDialogDelegate.onCreate(mSystemUIDialog, null);
         mInternetDialogDelegate.mAdapter = mInternetAdapter;
         mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry;
         mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size();
@@ -163,10 +168,12 @@
         mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
         mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
         mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
+        mInternetDialogDelegate.onStart(mSystemUIDialog);
     }
 
     @After
     public void tearDown() {
+        mInternetDialogDelegate.onStop(mSystemUIDialog);
         mInternetDialogDelegate.dismissDialog();
         mMockitoSession.finishMocking();
     }
@@ -191,59 +198,77 @@
     @Test
     public void updateDialog_withApmOn_internetDialogSubTitleGone() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
     public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
     public void updateDialog_apmOffAndHasEthernet_showEthernet() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         when(mInternetDialogController.hasEthernet()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
     public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         when(mInternetDialogController.hasEthernet()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_apmOnAndHasEthernet_showEthernet() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         when(mInternetDialogController.hasEthernet()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
     public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         when(mInternetDialogController.hasEthernet()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -252,41 +277,56 @@
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         when(mInternetDialogController.hasActiveSubIdOnDds()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
-    public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayout() {
-        // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
-        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-        when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
-
-        mInternetDialogDelegate.updateDialog(true);
-
-        assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
-
+    public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() {
         // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutGone() {
+        // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
+        mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -295,11 +335,14 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
@@ -308,30 +351,39 @@
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() {
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() {
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
         when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -340,10 +392,13 @@
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
         when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true);
         mMobileToggleSwitch.setChecked(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileToggleSwitch.isChecked()).isTrue();
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileToggleSwitch.isChecked()).isTrue();
+                });
     }
 
     @Test
@@ -352,26 +407,32 @@
         when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
         when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false);
         mMobileToggleSwitch.setChecked(false);
-
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mMobileToggleSwitch.isChecked()).isFalse();
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mMobileToggleSwitch.isChecked()).isFalse();
+                });
     }
 
     @Test
     public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
-        mInternetDialogDelegate.dismissDialog();
+        when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
         doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
-        createInternetDialog();
         // The preconditions WiFi ON and Internet WiFi are already in setUp()
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
 
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
-        LinearLayout secondaryLayout = mDialogView.requireViewById(
-                R.id.secondary_mobile_network_layout);
-        assertThat(secondaryLayout.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+                    LinearLayout secondaryLayout = mDialogView.requireViewById(
+                            R.id.secondary_mobile_network_layout);
+                    assertThat(secondaryLayout.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -379,10 +440,13 @@
         // The precondition WiFi ON is already in setUp()
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -390,14 +454,17 @@
         // The precondition WiFi ON is already in setUp()
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         mInternetDialogDelegate.mWifiEntriesCount = 0;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        // Show a blank block to fix the dialog height even if there is no WiFi list
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        verify(mInternetAdapter).setMaxEntriesCount(3);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                    // Show a blank block to fix the dialog height even if there is no WiFi list
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+                    verify(mInternetAdapter).setMaxEntriesCount(3);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+                });
     }
 
     @Test
@@ -405,28 +472,34 @@
         // The precondition WiFi ON is already in setUp()
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         mInternetDialogDelegate.mWifiEntriesCount = 1;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        // Show a blank block to fix the dialog height even if there is no WiFi list
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        verify(mInternetAdapter).setMaxEntriesCount(3);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                    // Show a blank block to fix the dialog height even if there is no WiFi list
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+                    verify(mInternetAdapter).setMaxEntriesCount(3);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+                });
     }
 
     @Test
     public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
         mInternetDialogDelegate.mWifiEntriesCount = 0;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
-        // Show a blank block to fix the dialog height even if there is no WiFi list
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        verify(mInternetAdapter).setMaxEntriesCount(2);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+                    // Show a blank block to fix the dialog height even if there is no WiFi list
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+                    verify(mInternetAdapter).setMaxEntriesCount(2);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.INVISIBLE);
+                });
     }
 
     @Test
@@ -435,13 +508,16 @@
         mInternetDialogDelegate.mConnectedWifiEntry = null;
         mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
         mInternetDialogDelegate.mHasMoreWifiEntries = true;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        verify(mInternetAdapter).setMaxEntriesCount(3);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+                    verify(mInternetAdapter).setMaxEntriesCount(3);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
@@ -449,13 +525,16 @@
         // The preconditions WiFi ON and WiFi entries are already in setUp()
         mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
         mInternetDialogDelegate.mHasMoreWifiEntries = true;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
-        verify(mInternetAdapter).setMaxEntriesCount(2);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+                    verify(mInternetAdapter).setMaxEntriesCount(2);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+                });
     }
 
     @Test
@@ -463,32 +542,38 @@
         // The preconditions WiFi entries are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
         mInternetDialogDelegate.mConnectedWifiEntry = null;
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        // Show WiFi Toggle without background
-        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiToggle.getBackground()).isNull();
-        // Hide Wi-Fi networks and See all
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    // Show WiFi Toggle without background
+                    assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mWifiToggle.getBackground()).isNull();
+                    // Hide Wi-Fi networks and See all
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        // Show WiFi Toggle with highlight background
-        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiToggle.getBackground()).isNotNull();
-        // Hide Wi-Fi networks and See all
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    // Show WiFi Toggle with highlight background
+                    assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mWifiToggle.getBackground()).isNotNull();
+                    // Hide Wi-Fi networks and See all
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                    assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+                    assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
@@ -496,13 +581,16 @@
         mInternetDialogDelegate.dismissDialog();
         when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
         createInternetDialog();
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        // Disable Wi-Fi switch and show restriction message in summary.
-        assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
-        assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mWifiToggleSummary.getText().length()).isNotEqualTo(0);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    // Disable Wi-Fi switch and show restriction message in summary.
+                    assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
+                    assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mWifiToggleSummary.getText().length()).isNotEqualTo(0);
+                });
     }
 
     @Test
@@ -510,50 +598,38 @@
         mInternetDialogDelegate.dismissDialog();
         when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
         createInternetDialog();
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        // Enable Wi-Fi switch and hide restriction message in summary.
-        assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
-        assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    // Enable Wi-Fi switch and hide restriction message in summary.
+                    assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
+                    assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_showSecondaryDataSub() {
-        mInternetDialogDelegate.dismissDialog();
+        when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
         doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId();
         doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
         doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled();
-        createInternetDialog();
-
         clearInvocations(mInternetDialogController);
         mInternetDialogDelegate.updateDialog(true);
+        mBgExecutor.runAllReady();
 
-        LinearLayout primaryLayout = mDialogView.requireViewById(
-                R.id.mobile_network_layout);
-        LinearLayout secondaryLayout = mDialogView.requireViewById(
-                R.id.secondary_mobile_network_layout);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    LinearLayout primaryLayout = mDialogView.requireViewById(
+                            R.id.mobile_network_layout);
+                    LinearLayout secondaryLayout = mDialogView.requireViewById(
+                            R.id.secondary_mobile_network_layout);
 
-        verify(mInternetDialogController).getMobileNetworkSummary(1);
-        assertThat(primaryLayout.getBackground()).isNotEqualTo(secondaryLayout.getBackground());
-
-        // Tap the primary sub info
-        primaryLayout.performClick();
-        ArgumentCaptor<AlertDialog> dialogArgumentCaptor =
-                ArgumentCaptor.forClass(AlertDialog.class);
-        verify(mDialogTransitionAnimator).showFromDialog(dialogArgumentCaptor.capture(),
-                eq(mSystemUIDialog), eq(null), eq(false));
-        AlertDialog dialog = dialogArgumentCaptor.getValue();
-        dialog.show();
-        dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
-        TestableLooper.get(this).processAllMessages();
-        verify(mInternetDialogController).setAutoDataSwitchMobileDataPolicy(1, false);
-
-        // Tap the secondary sub info
-        secondaryLayout.performClick();
-        verify(mInternetDialogController).launchMobileNetworkSettings(any(View.class));
-
-        dialog.dismiss();
+                    verify(mInternetDialogController).getMobileNetworkSummary(1);
+                    assertThat(primaryLayout.getBackground()).isNotEqualTo(
+                            secondaryLayout.getBackground());
+                });
     }
 
     @Test
@@ -561,6 +637,12 @@
         // The preconditions WiFi ON and WiFi entries are already in setUp()
 
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
 
         assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
     }
@@ -569,8 +651,13 @@
     public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
         when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
         when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
 
         assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
     }
@@ -580,8 +667,13 @@
         when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
         when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
         when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
 
         assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
     }
@@ -591,33 +683,43 @@
         when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
         when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
         when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
-        TextView wifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text);
-        assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0);
-        assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull();
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
+                    TextView wifiScanNotifyText = mDialogView.requireViewById(
+                            R.id.wifi_scan_notify_text);
+                    assertThat(wifiScanNotifyText.getText().length()).isNotEqualTo(0);
+                    assertThat(wifiScanNotifyText.getMovementMethod()).isNotNull();
+                });
     }
 
     @Test
     public void updateDialog_wifiIsDisabled_uncheckWifiSwitch() {
         when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
         mWifiToggleSwitch.setChecked(true);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mWifiToggleSwitch.isChecked()).isFalse();
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiToggleSwitch.isChecked()).isFalse();
+                });
     }
 
     @Test
-    public void updateDialog_wifiIsEnabled_checkWifiSwitch() {
+    public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception {
         when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
         mWifiToggleSwitch.setChecked(false);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mWifiToggleSwitch.isChecked()).isTrue();
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mWifiToggleSwitch.isChecked()).isTrue();
+                });
     }
 
     @Test
@@ -699,21 +801,28 @@
     public void updateDialog_shareWifiIntentNull_hideButton() {
         when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
                 .thenReturn(null);
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo(
+                            View.GONE);
+                });
     }
 
     @Test
     public void updateDialog_shareWifiShareable_showButton() {
         when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
                 .thenReturn(new Intent());
-
         mInternetDialogDelegate.updateDialog(false);
+        mBgExecutor.runAllReady();
 
-        assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
-                .isEqualTo(View.VISIBLE);
+        mInternetDialogDelegate.mDataInternetContent.observe(
+                mInternetDialogDelegate.mLifecycleOwner, i -> {
+                    assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
+                            .isEqualTo(View.VISIBLE);
+                });
     }
 
     private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
new file mode 100644
index 0000000..d8618fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/CustomTraceStateTest.kt
@@ -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.systemui.recordissue
+
+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.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.traceur.PresetTraceConfigs
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+/**
+ * CustomTraceState is customized Traceur settings for power users. These settings determine what
+ * tracing is used during the Record Issue Quick Settings flow. This class tests that those features
+ * are persistently and accurately stored across sessions.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class CustomTraceStateTest : SysuiTestCase() {
+
+    private lateinit var underTest: CustomTraceState
+
+    @Before
+    fun setup() {
+        underTest = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0))
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun trace_config_is_stored_accurately() {
+        val expected = PresetTraceConfigs.getUiConfig()
+
+        underTest.traceConfig = expected
+
+        val actual = underTest.traceConfig
+        Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+        Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+        Truth.assertThat(actual.maxLongTraceDurationMinutes)
+            .isEqualTo(expected.maxLongTraceDurationMinutes)
+        Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+        Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+        Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+        Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+        Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+    }
+
+    @Test
+    fun trace_config_is_persistently_stored_between_instances() {
+        val expected = PresetTraceConfigs.getUiConfig()
+
+        underTest.traceConfig = expected
+
+        val actual = CustomTraceState(context.getSharedPreferences(TILE_SPEC, 0)).traceConfig
+        Truth.assertThat(actual.longTrace).isEqualTo(expected.longTrace)
+        Truth.assertThat(actual.maxLongTraceSizeMb).isEqualTo(expected.maxLongTraceSizeMb)
+        Truth.assertThat(actual.maxLongTraceDurationMinutes)
+            .isEqualTo(expected.maxLongTraceDurationMinutes)
+        Truth.assertThat(actual.apps).isEqualTo(expected.apps)
+        Truth.assertThat(actual.winscope).isEqualTo(expected.winscope)
+        Truth.assertThat(actual.attachToBugreport).isEqualTo(expected.attachToBugreport)
+        Truth.assertThat(actual.bufferSizeKb).isEqualTo(expected.bufferSizeKb)
+        Truth.assertThat(actual.tags).isEqualTo(expected.tags)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
new file mode 100644
index 0000000..4ab3c7b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/IssueRecordingStateTest.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.recordissue
+
+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.kosmos.Kosmos
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.settings.userTracker
+import com.google.common.truth.Truth
+import java.util.concurrent.CountDownLatch
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected](setAsMainLooper = true)
+class IssueRecordingStateTest : SysuiTestCase() {
+
+    private val kosmos = Kosmos()
+    private lateinit var underTest: IssueRecordingState
+
+    @Before
+    fun setup() {
+        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+    }
+
+    @Test
+    fun takeBugreport_isSaved_betweenDifferentSessions() {
+        val expected = true
+
+        underTest.takeBugreport = expected
+        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+        Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
+    }
+
+    @Test
+    fun recordScreen_isSaved_betweenDifferentSessions() {
+        val expected = true
+
+        underTest.recordScreen = expected
+        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+        Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
+    }
+
+    @Test
+    fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
+        underTest.markUserApprovalForScreenRecording()
+        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+        Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
+    }
+
+    @Test
+    fun tagTitles_areSavedConsistently() {
+        val expected = setOf("a", "b", "c")
+
+        underTest.tagTitles = expected
+        underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+
+        Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
+    }
+
+    @Test
+    fun isRecording_callsListeners_onTheValueChanging() {
+        val count = CountDownLatch(1)
+        val listener = Runnable { count.countDown() }
+
+        underTest.addListener(listener)
+        underTest.isRecording = true
+
+        Truth.assertThat(count.count).isEqualTo(0)
+    }
+
+    @Test
+    fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
+        val count1 = CountDownLatch(1)
+        val count2 = CountDownLatch(1)
+        val listener1 = Runnable { count1.countDown() }
+        val listener2 = Runnable { count2.countDown() }
+
+        underTest.addListener(listener1)
+        underTest.removeListener(listener1)
+        underTest.addListener(listener2)
+        underTest.isRecording = true
+
+        Truth.assertThat(count1.count).isEqualTo(1)
+        Truth.assertThat(count2.count).isEqualTo(0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
index 11b0bdf..7dae5cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegateTest.kt
@@ -21,13 +21,13 @@
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.Spinner
+import android.widget.TextView
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Dependency
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
@@ -60,7 +60,6 @@
     @Mock private lateinit var starter: ActivityStarter
     @Mock private lateinit var controller: RecordingController
     @Mock private lateinit var userContextProvider: UserContextProvider
-    @Mock private lateinit var flags: FeatureFlags
     @Mock private lateinit var onStartRecordingClicked: Runnable
     @Mock private lateinit var mediaProjectionMetricsLogger: MediaProjectionMetricsLogger
 
@@ -128,6 +127,32 @@
     }
 
     @Test
+    fun startButtonText_entireScreenSelected() {
+        showDialog()
+
+        onSpinnerItemSelected(ENTIRE_SCREEN)
+
+        assertThat(getStartButton().text)
+            .isEqualTo(
+                context.getString(R.string.screenrecord_permission_dialog_continue_entire_screen)
+            )
+    }
+
+    @Test
+    fun startButtonText_singleAppSelected() {
+        showDialog()
+
+        onSpinnerItemSelected(SINGLE_APP)
+
+        assertThat(getStartButton().text)
+            .isEqualTo(
+                context.getString(
+                    R.string.media_projection_entry_generic_permission_dialog_continue_single_app
+                )
+            )
+    }
+
+    @Test
     fun startClicked_singleAppSelected_passesHostUidToAppSelector() {
         showDialog()
         onSpinnerItemSelected(SINGLE_APP)
@@ -152,7 +177,8 @@
         showDialog()
 
         val spinner = dialog.requireViewById<Spinner>(R.id.screen_share_mode_options)
-        val singleApp = context.getString(R.string.screen_share_permission_dialog_option_single_app)
+        val singleApp =
+            context.getString(R.string.screenrecord_permission_dialog_option_text_single_app)
         assertEquals(spinner.adapter.getItem(0), singleApp)
     }
 
@@ -208,8 +234,10 @@
         dialog.requireViewById<View>(android.R.id.button2).performClick()
     }
 
+    private fun getStartButton() = dialog.requireViewById<TextView>(android.R.id.button1)
+
     private fun clickOnStart() {
-        dialog.requireViewById<View>(android.R.id.button1).performClick()
+        getStartButton().performClick()
     }
 
     private fun onSpinnerItemSelected(position: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
index b31d21e..15da77d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/MessageContainerControllerTest.kt
@@ -2,8 +2,6 @@
 
 import android.graphics.drawable.Drawable
 import android.os.UserHandle
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
@@ -90,20 +88,6 @@
     }
 
     @Test
-    @DisableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
-    fun testOnScreenshotTakenUserHandle_withWorkProfileFirstRun() {
-        whenever(workProfileMessageController.onScreenshotTaken(eq(userHandle)))
-            .thenReturn(workProfileData)
-        messageContainer.onScreenshotTaken(screenshotData)
-
-        verify(workProfileMessageController)
-            .populateView(eq(workProfileFirstRunView), eq(workProfileData), any())
-        assertEquals(View.VISIBLE, workProfileFirstRunView.visibility)
-        assertEquals(View.GONE, detectionNoticeView.visibility)
-    }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_SCREENSHOT_PRIVATE_PROFILE_BEHAVIOR_FIX)
     fun testOnScreenshotTakenUserHandle_withProfileProfileFirstRun() = runTest {
         val profileData =
             ProfileMessageController.ProfileFirstRunData(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
deleted file mode 100644
index 0847f01..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ /dev/null
@@ -1,184 +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.screenshot
-
-import android.content.ComponentName
-import android.graphics.Bitmap
-import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
-import android.hardware.HardwareBuffer
-import android.os.UserHandle
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
-import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import com.android.internal.util.ScreenshotRequest
-import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.runBlocking
-import org.junit.Assert
-import org.junit.Test
-
-private const val USER_ID = 1
-private const val TASK_ID = 1
-
-class RequestProcessorTest {
-    private val imageCapture = FakeImageCapture()
-    private val component = ComponentName("android.test", "android.test.Component")
-    private val bounds = Rect(25, 25, 75, 75)
-
-    private val policy = FakeScreenshotPolicy()
-
-    @Test
-    fun testFullScreenshot() = runBlocking {
-        // Indicate that the primary content belongs to a normal user
-        policy.setManagedProfile(USER_ID, false)
-        policy.setDisplayContentInfo(
-            policy.getDefaultDisplayId(),
-            DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
-        )
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_OTHER).build()
-        val processor = RequestProcessor(imageCapture, policy)
-
-        val processedData = processor.process(ScreenshotData.fromRequest(request))
-
-        // Request has topComponent added, but otherwise unchanged.
-        assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
-        assertThat(processedData.source).isEqualTo(SCREENSHOT_OTHER)
-        assertThat(processedData.topComponent).isEqualTo(component)
-    }
-
-    @Test
-    fun testFullScreenshot_managedProfile() = runBlocking {
-        // Provide a fake task bitmap when asked
-        val bitmap = makeHardwareBitmap(100, 100)
-        imageCapture.image = bitmap
-
-        // Indicate that the primary content belongs to a manged profile
-        policy.setManagedProfile(USER_ID, true)
-        policy.setDisplayContentInfo(
-            policy.getDefaultDisplayId(),
-            DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
-        )
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
-        val processor = RequestProcessor(imageCapture, policy)
-
-        val processedData = processor.process(ScreenshotData.fromRequest(request))
-
-        // Expect a task snapshot is taken, overriding the full screen mode
-        assertThat(processedData.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
-        assertThat(processedData.bitmap).isEqualTo(bitmap)
-        assertThat(processedData.screenBounds).isEqualTo(bounds)
-        assertThat(processedData.insets).isEqualTo(Insets.NONE)
-        assertThat(processedData.taskId).isEqualTo(TASK_ID)
-        assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
-    }
-
-    @Test
-    fun testFullScreenshot_managedProfile_nullBitmap() {
-        // Provide a null task bitmap when asked
-        imageCapture.image = null
-
-        // Indicate that the primary content belongs to a manged profile
-        policy.setManagedProfile(USER_ID, true)
-        policy.setDisplayContentInfo(
-            policy.getDefaultDisplayId(),
-            DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID)
-        )
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build()
-        val processor = RequestProcessor(imageCapture, policy)
-
-        Assert.assertThrows(IllegalStateException::class.java) {
-            runBlocking { processor.process(ScreenshotData.fromRequest(request)) }
-        }
-    }
-
-    @Test
-    fun testProvidedImageScreenshot() = runBlocking {
-        val bounds = Rect(50, 50, 150, 150)
-        val processor = RequestProcessor(imageCapture, policy)
-
-        policy.setManagedProfile(USER_ID, false)
-
-        val bitmap = makeHardwareBitmap(100, 100)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
-                .setTopComponent(component)
-                .setTaskId(TASK_ID)
-                .setUserId(USER_ID)
-                .setBitmap(bitmap)
-                .setBoundsOnScreen(bounds)
-                .setInsets(Insets.NONE)
-                .build()
-
-        val screenshotData = ScreenshotData.fromRequest(request)
-        val processedData = processor.process(screenshotData)
-
-        assertThat(processedData).isEqualTo(screenshotData)
-    }
-
-    @Test
-    fun testProvidedImageScreenshot_managedProfile() = runBlocking {
-        val bounds = Rect(50, 50, 150, 150)
-        val processor = RequestProcessor(imageCapture, policy)
-
-        // Indicate that the screenshot belongs to a manged profile
-        policy.setManagedProfile(USER_ID, true)
-
-        val bitmap = makeHardwareBitmap(100, 100)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER)
-                .setTopComponent(component)
-                .setTaskId(TASK_ID)
-                .setUserId(USER_ID)
-                .setBitmap(bitmap)
-                .setBoundsOnScreen(bounds)
-                .setInsets(Insets.NONE)
-                .build()
-
-        val screenshotData = ScreenshotData.fromRequest(request)
-        val processedData = processor.process(screenshotData)
-
-        // Work profile, but already a task snapshot, so no changes
-        assertThat(processedData).isEqualTo(screenshotData)
-    }
-
-    private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
-        val buffer =
-            HardwareBuffer.create(
-                width,
-                height,
-                HardwareBuffer.RGBA_8888,
-                1,
-                HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
-            )
-        return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
-    }
-
-    private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
-        return bitmap.hardwareBuffer == this.hardwareBuffer && bitmap.colorSpace == this.colorSpace
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
index a8d5008..e1cd5e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsActivityTest.java
@@ -30,12 +30,15 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.IActivityTaskManager;
 import android.app.assist.AssistContent;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -51,6 +54,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.UserHandle;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.testing.AndroidTestingRunner;
@@ -120,6 +124,8 @@
     @Mock
     private AssistContentRequester mAssistContentRequester;
     @Mock
+    private Context mMockedContext;
+    @Mock
     private PackageManager mPackageManager;
     @Mock
     private UserTracker mUserTracker;
@@ -127,6 +133,9 @@
     private UiEventLogger mUiEventLogger;
 
     private AppClipsActivity mActivity;
+    private TextView mBacklinksDataTextView;
+    private CheckBox mBacklinksIncludeDataCheckBox;
+    private TextView mBacklinksCrossProfileErrorTextView;
 
     // Using the deprecated ActivityTestRule and SingleActivityFactory to help with injecting mocks.
     private final SingleActivityFactory<AppClipsActivityTestable> mFactory =
@@ -136,7 +145,7 @@
                     return new AppClipsActivityTestable(
                             new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper,
                                     mImageExporter, mAtmService, mAssistContentRequester,
-                                    mPackageManager, getContext().getMainExecutor(),
+                                    mMockedContext, getContext().getMainExecutor(),
                                     directExecutor()),
                             mPackageManager, mUserTracker, mUiEventLogger);
                 }
@@ -162,6 +171,9 @@
         when(mImageExporter.export(any(Executor.class), any(UUID.class), any(Bitmap.class),
                 eq(Process.myUserHandle()), eq(Display.DEFAULT_DISPLAY)))
                 .thenReturn(Futures.immediateFuture(result));
+
+        when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mMockedContext.createContextAsUser(any(), anyInt())).thenReturn(mMockedContext);
     }
 
     @After
@@ -175,10 +187,9 @@
         launchActivity();
 
         assertThat(((ImageView) mActivity.findViewById(R.id.preview)).getDrawable()).isNotNull();
-        assertThat(mActivity.findViewById(R.id.backlinks_data).getVisibility())
-                .isEqualTo(View.GONE);
-        assertThat(mActivity.findViewById(R.id.backlinks_include_data).getVisibility())
-                .isEqualTo(View.GONE);
+        assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBacklinksIncludeDataCheckBox.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -228,20 +239,23 @@
         waitForIdleSync();
 
         assertThat(mDisplayIdCaptor.getValue()).isEqualTo(mActivity.getDisplayId());
-        TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
-        assertThat(backlinksData.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
-        assertThat(backlinksData.getCompoundDrawablesRelative()[0]).isEqualTo(FAKE_DRAWABLE);
+        assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+        assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[0])
+                .isEqualTo(FAKE_DRAWABLE);
 
         // Verify dropdown icon is not shown and there are no click listeners on text view.
-        assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNull();
-        assertThat(backlinksData.hasOnClickListeners()).isFalse();
+        assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[2]).isNull();
+        assertThat(mBacklinksDataTextView.hasOnClickListeners()).isFalse();
 
-        CheckBox backlinksIncludeData = mActivity.findViewById(R.id.backlinks_include_data);
-        assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(backlinksIncludeData.getText().toString())
+        assertThat(mBacklinksIncludeDataCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mBacklinksIncludeDataCheckBox.getText().toString())
                 .isEqualTo(mActivity.getString(R.string.backlinks_include_link));
-        assertThat(backlinksIncludeData.isChecked()).isTrue();
+        assertThat(mBacklinksIncludeDataCheckBox.isChecked()).isTrue();
+
+        assertThat(mBacklinksIncludeDataCheckBox.getAlpha()).isEqualTo(1.0f);
+        assertThat(mBacklinksIncludeDataCheckBox.isEnabled()).isTrue();
+        assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -258,8 +272,7 @@
         assertThat(backlinksIncludeData.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(backlinksIncludeData.isChecked()).isFalse();
 
-        TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
-        assertThat(backlinksData.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mBacklinksDataTextView.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -300,12 +313,68 @@
         waitForIdleSync();
 
         // Verify default backlink shown to user and text view has on click listener.
-        TextView backlinksData = mActivity.findViewById(R.id.backlinks_data);
-        assertThat(backlinksData.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
-        assertThat(backlinksData.hasOnClickListeners()).isTrue();
+        assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(BACKLINKS_TASK_APP_NAME);
+        assertThat(mBacklinksDataTextView.hasOnClickListeners()).isTrue();
 
         // Verify dropdown icon is not null.
-        assertThat(backlinksData.getCompoundDrawablesRelative()[2]).isNotNull();
+        assertThat(mBacklinksDataTextView.getCompoundDrawablesRelative()[2]).isNotNull();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+    public void appClipsLaunched_backlinks_multipleBacklinksAvailable_duplicateName()
+            throws RemoteException {
+        // Set up mocking for multiple backlinks.
+        ResolveInfo resolveInfo1 = createBacklinksTaskResolveInfo();
+
+        ResolveInfo resolveInfo2 = createBacklinksTaskResolveInfo();
+        RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+        int taskId2 = BACKLINKS_TASK_ID + 2;
+        runningTaskInfo2.taskId = taskId2;
+
+        when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+                mDisplayIdCaptor.capture()))
+                .thenReturn(List.of(TASK_THAT_SUPPORTS_BACKLINKS, runningTaskInfo2));
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resolveInfo1,
+                resolveInfo1, resolveInfo1, resolveInfo2, resolveInfo2, resolveInfo2);
+        when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+
+        // Using same AssistContent data for both tasks.
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, BACKLINKS_TASK_ID);
+        mockForAssistContent(ASSIST_CONTENT_FOR_BACKLINKS_TASK, taskId2);
+
+        // Mocking complete, trigger backlinks.
+        launchActivity();
+        waitForIdleSync();
+
+        // Verify default backlink shown to user has the numerical suffix.
+        assertThat(mBacklinksDataTextView.getText().toString()).isEqualTo(
+                getContext().getString(R.string.backlinks_duplicate_label_format,
+                        BACKLINKS_TASK_APP_NAME, 1));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_APP_CLIPS_BACKLINKS)
+    public void appClipsLaunched_backlinks_singleBacklink_crossProfileError()
+            throws RemoteException {
+        // Set up mocking for cross profile backlink.
+        setUpMocksForBacklinks();
+        ActivityManager.RunningTaskInfo crossProfileTaskInfo = createTaskInfoForBacklinksTask();
+        crossProfileTaskInfo.userId = UserHandle.myUserId() + 1;
+        reset(mAtmService);
+        when(mAtmService.getTasks(eq(Integer.MAX_VALUE), eq(false), eq(false),
+                mDisplayIdCaptor.capture())).thenReturn(List.of(crossProfileTaskInfo));
+
+        // Trigger backlinks.
+        launchActivity();
+        waitForIdleSync();
+
+        // Verify views for cross profile backlinks error.
+        assertThat(mBacklinksIncludeDataCheckBox.getAlpha()).isLessThan(1.0f);
+        assertThat(mBacklinksIncludeDataCheckBox.isEnabled()).isFalse();
+        assertThat(mBacklinksIncludeDataCheckBox.isChecked()).isFalse();
+
+        assertThat(mBacklinksCrossProfileErrorTextView.getVisibility()).isEqualTo(View.VISIBLE);
     }
 
     private void setUpMocksForBacklinks() throws RemoteException {
@@ -339,11 +408,15 @@
 
         mActivity = mActivityRule.launchActivity(intent);
         waitForIdleSync();
+        mBacklinksDataTextView = mActivity.findViewById(R.id.backlinks_data);
+        mBacklinksIncludeDataCheckBox = mActivity.findViewById(R.id.backlinks_include_data);
+        mBacklinksCrossProfileErrorTextView = mActivity.findViewById(
+                R.id.backlinks_cross_profile_error);
     }
 
     private ResultReceiver createResultReceiver(
             BiConsumer<Integer, Bundle> resultReceiverConsumer) {
-        ResultReceiver testReceiver = new ResultReceiver(mContext.getMainThreadHandler()) {
+        ResultReceiver testReceiver = new ResultReceiver(getContext().getMainThreadHandler()) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 resultReceiverConsumer.accept(resultCode, resultData);
@@ -360,7 +433,7 @@
     }
 
     private void runOnMainThread(Runnable runnable) {
-        mContext.getMainExecutor().execute(runnable);
+        getContext().getMainExecutor().execute(runnable);
     }
 
     private static ResolveInfo createBacklinksTaskResolveInfo() {
@@ -384,6 +457,7 @@
         taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
         taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
         taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+        taskInfo.userId = UserHandle.myUserId();
         return taskInfo;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
index 178547e..5d71c05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsViewModelTest.java
@@ -30,6 +30,7 @@
 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
@@ -43,6 +44,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -62,6 +64,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.screenshot.AssistContentRequester;
 import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.BacklinksData;
+import com.android.systemui.screenshot.appclips.InternalBacklinksData.CrossProfileError;
 
 import com.google.common.util.concurrent.Futures;
 
@@ -92,10 +96,16 @@
     private static final String BACKLINKS_TASK_PACKAGE_NAME = "backlinksTaskPackageName";
     private static final AssistContent EMPTY_ASSIST_CONTENT = new AssistContent();
 
-    @Mock private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
-    @Mock private ImageExporter mImageExporter;
-    @Mock private IActivityTaskManager mAtmService;
-    @Mock private AssistContentRequester mAssistContentRequester;
+    @Mock
+    private AppClipsCrossProcessHelper mAppClipsCrossProcessHelper;
+    @Mock
+    private ImageExporter mImageExporter;
+    @Mock
+    private IActivityTaskManager mAtmService;
+    @Mock
+    private AssistContentRequester mAssistContentRequester;
+    @Mock
+    Context mMockedContext;
     @Mock
     private PackageManager mPackageManager;
     private ArgumentCaptor<Intent> mPackageManagerIntentCaptor;
@@ -112,10 +122,14 @@
         when(mPackageManager.resolveActivity(mPackageManagerIntentCaptor.capture(), anyInt()))
                 .thenReturn(createBacklinksTaskResolveInfo());
         when(mPackageManager.loadItemIcon(any(), any())).thenReturn(FAKE_DRAWABLE);
+        when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
 
         mViewModel = new AppClipsViewModel.Factory(mAppClipsCrossProcessHelper, mImageExporter,
-                mAtmService, mAssistContentRequester, mPackageManager,
+                mAtmService, mAssistContentRequester, mMockedContext,
                 getContext().getMainExecutor(), directExecutor()).create(AppClipsViewModel.class);
+
+        when(mMockedContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mMockedContext.createContextAsUser(any(), anyInt())).thenReturn(mMockedContext);
     }
 
     @Test
@@ -199,7 +213,7 @@
         assertThat(queriedIntent.getData()).isEqualTo(expectedUri);
         assertThat(queriedIntent.getAction()).isEqualTo(ACTION_VIEW);
 
-        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+        BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
         ClipData clipData = result.getClipData();
         ClipDescription resultDescription = clipData.getDescription();
@@ -238,7 +252,7 @@
         Intent queriedIntent = mPackageManagerIntentCaptor.getValue();
         assertThat(queriedIntent.getPackage()).isEqualTo(expectedIntent.getPackage());
 
-        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+        BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
         ClipData clipData = result.getClipData();
         ClipDescription resultDescription = clipData.getDescription();
@@ -307,6 +321,8 @@
 
     @Test
     public void triggerBacklinks_taskIdsToIgnoreConsidered_noBacklinkAvailable() {
+        mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
+
         mViewModel.triggerBacklinks(Set.of(BACKLINKS_TASK_ID), DEFAULT_DISPLAY);
         waitForIdleSync();
 
@@ -362,16 +378,58 @@
         waitForIdleSync();
 
         // Verify two backlinks are received and the first backlink is set as default selected.
-        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue().getClipData().getItemAt(
-                0).getUri()).isEqualTo(expectedUri);
+        assertThat(
+                ((BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue())
+                        .getClipData().getItemAt(0).getUri())
+                .isEqualTo(expectedUri);
         List<InternalBacklinksData> actualBacklinks = mViewModel.getBacklinksLiveData().getValue();
         assertThat(actualBacklinks).hasSize(2);
-        assertThat(actualBacklinks.get(0).getClipData().getItemAt(0).getUri())
+        assertThat(((BacklinksData) actualBacklinks.get(0)).getClipData().getItemAt(0).getUri())
                 .isEqualTo(expectedUri);
-        assertThat(actualBacklinks.get(1).getClipData().getItemAt(0).getIntent())
+        assertThat(((BacklinksData) actualBacklinks.get(1)).getClipData().getItemAt(0).getIntent())
                 .isEqualTo(expectedIntent);
     }
 
+    @Test
+    public void triggerBacklinks_singleCrossProfileApp_shouldIndicateError()
+            throws RemoteException {
+        reset(mAtmService);
+        RunningTaskInfo taskInfo = createTaskInfoForBacklinksTask();
+        taskInfo.userId = UserHandle.myUserId() + 1;
+        when(mAtmService.getTasks(Integer.MAX_VALUE, false, false, DEFAULT_DISPLAY))
+                .thenReturn(List.of(taskInfo));
+
+        mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+        waitForIdleSync();
+
+        assertThat(mViewModel.mSelectedBacklinksLiveData.getValue())
+                .isInstanceOf(CrossProfileError.class);
+    }
+
+    @Test
+    public void triggerBacklinks_multipleBacklinks_includesCrossProfileError()
+            throws RemoteException {
+        // Set up mocking for multiple backlinks.
+        mockForAssistContent(EMPTY_ASSIST_CONTENT, BACKLINKS_TASK_ID);
+        reset(mAtmService);
+        RunningTaskInfo runningTaskInfo1 = createTaskInfoForBacklinksTask();
+        RunningTaskInfo runningTaskInfo2 = createTaskInfoForBacklinksTask();
+        runningTaskInfo2.userId = UserHandle.myUserId() + 1;
+
+        when(mAtmService.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+                .thenReturn(List.of(runningTaskInfo1, runningTaskInfo2));
+
+        // Set up complete, trigger the backlinks action.
+        mViewModel.triggerBacklinks(Collections.emptySet(), DEFAULT_DISPLAY);
+        waitForIdleSync();
+
+        // Verify two backlinks are received and only second has error.
+        List<InternalBacklinksData> actualBacklinks = mViewModel.getBacklinksLiveData().getValue();
+        assertThat(actualBacklinks).hasSize(2);
+        assertThat(actualBacklinks.get(0)).isInstanceOf(BacklinksData.class);
+        assertThat(actualBacklinks.get(1)).isInstanceOf(CrossProfileError.class);
+    }
+
     private void resetPackageManagerMockingForUsingFallbackBacklinks() {
         ResolveInfo backlinksTaskResolveInfo = createBacklinksTaskResolveInfo();
         reset(mPackageManager);
@@ -389,7 +447,7 @@
     }
 
     private void verifyMainLauncherBacklinksIntent() {
-        InternalBacklinksData result = mViewModel.mSelectedBacklinksLiveData.getValue();
+        BacklinksData result = (BacklinksData) mViewModel.mSelectedBacklinksLiveData.getValue();
         assertThat(result.getAppIcon()).isEqualTo(FAKE_DRAWABLE);
 
         ClipData clipData = result.getClipData();
@@ -436,6 +494,7 @@
         taskInfo.topActivityInfo = createBacklinksTaskResolveInfo().activityInfo;
         taskInfo.baseIntent = new Intent().setComponent(taskInfo.topActivity);
         taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+        taskInfo.userId = UserHandle.myUserId();
         return taskInfo;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
index a5fbfb5..be9fcc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/policy/WorkProfilePolicyTest.kt
@@ -18,11 +18,13 @@
 
 import android.content.ComponentName
 import android.content.Context
+import android.content.res.Resources
 import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.R
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.screenshot.data.model.DisplayContentModel
 import com.android.systemui.screenshot.data.model.DisplayContentScenarios.ActivityNames.FILES
@@ -57,6 +59,7 @@
 import org.mockito.Mock
 import org.mockito.junit.MockitoJUnit
 import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class WorkProfilePolicyTest {
@@ -66,12 +69,17 @@
     @JvmField @Rule(order = 2) val mockitoRule: MockitoRule = MockitoJUnit.rule()
 
     @Mock lateinit var mContext: Context
+    @Mock lateinit var mResources: Resources
 
     private val kosmos = Kosmos()
     private lateinit var policy: WorkProfilePolicy
 
     @Before
     fun setUp() {
+        // Set desktop mode supported
+        whenever(mContext.resources).thenReturn(mResources)
+        whenever(mResources.getBoolean(R.bool.config_isDesktopModeSupported)).thenReturn(true)
+
         policy = WorkProfilePolicy(kosmos.profileTypeRepository, mContext)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 774aa51..2e2ac3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -33,9 +33,11 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.TruthJUnit.assume
+import java.util.concurrent.Executor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
@@ -54,10 +56,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
-
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -71,27 +70,19 @@
         fun isBackgroundUserTrackerEnabled(): Iterable<Boolean> = listOf(true, false)
     }
 
-    @Mock
-    private lateinit var context: Context
+    @Mock private lateinit var context: Context
 
-    @Mock
-    private lateinit var userManager: UserManager
+    @Mock private lateinit var userManager: UserManager
 
-    @Mock
-    private lateinit var iActivityManager: IActivityManager
+    @Mock private lateinit var iActivityManager: IActivityManager
 
-    @Mock
-    private lateinit var userSwitchingReply: IRemoteCallback
+    @Mock private lateinit var userSwitchingReply: IRemoteCallback
 
-    @Mock(stubOnly = true)
-    private lateinit var dumpManager: DumpManager
+    @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
 
-    @Mock(stubOnly = true)
-    private lateinit var handler: Handler
+    @Mock(stubOnly = true) private lateinit var handler: Handler
 
-    @Parameterized.Parameter
-    @JvmField
-    var isBackgroundUserTrackerEnabled: Boolean = false
+    @Parameterized.Parameter @JvmField var isBackgroundUserTrackerEnabled: Boolean = false
 
     private val testScope = TestScope()
     private val testDispatcher = StandardTestDispatcher(testScope.testScheduler)
@@ -104,365 +95,379 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM)
-        `when`(context.user).thenReturn(UserHandle.SYSTEM)
-        `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
+        whenever(context.userId).thenReturn(UserHandle.USER_SYSTEM)
+        whenever(context.user).thenReturn(UserHandle.SYSTEM)
+        whenever(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
             val user = invocation.getArgument<UserHandle>(0)
-            `when`(context.user).thenReturn(user)
-            `when`(context.userId).thenReturn(user.identifier)
+            whenever(context.user).thenReturn(user)
+            whenever(context.userId).thenReturn(user.identifier)
             context
         }
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+        whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
             val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL)
             listOf(info)
         }
 
         featureFlags.set(Flags.USER_TRACKER_BACKGROUND_CALLBACKS, isBackgroundUserTrackerEnabled)
         tracker =
-                UserTrackerImpl(
-                        context,
-                        { featureFlags },
-                        userManager,
-                        iActivityManager,
-                        dumpManager,
-                        testScope.backgroundScope,
-                        testDispatcher,
-                        handler,
-                )
+            UserTrackerImpl(
+                context,
+                { featureFlags },
+                userManager,
+                iActivityManager,
+                dumpManager,
+                testScope.backgroundScope,
+                testDispatcher,
+                handler,
+            )
     }
 
+    @Test fun testNotInitialized() = testScope.runTest { assertThat(tracker.initialized).isFalse() }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserIdBeforeInitThrowsException() = testScope.runTest { tracker.userId }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest { tracker.userHandle }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContextBeforeInitThrowsException() = testScope.runTest { tracker.userContext }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContentResolverBeforeInitThrowsException() =
+        testScope.runTest { tracker.userContentResolver }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest { tracker.userProfiles }
+
     @Test
-    fun testNotInitialized() = testScope.runTest {
-        assertThat(tracker.initialized).isFalse()
-    }
+    fun testInitialize() =
+        testScope.runTest {
+            tracker.initialize(0)
 
-    @Test(expected = IllegalStateException::class)
-    fun testGetUserIdBeforeInitThrowsException() = testScope.runTest {
-        tracker.userId
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun testGetUserHandleBeforeInitThrowsException() = testScope.runTest {
-        tracker.userHandle
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun testGetUserContextBeforeInitThrowsException() = testScope.runTest {
-        tracker.userContext
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun testGetUserContentResolverBeforeInitThrowsException() = testScope.runTest {
-        tracker.userContentResolver
-    }
-
-    @Test(expected = IllegalStateException::class)
-    fun testGetUserProfilesBeforeInitThrowsException() = testScope.runTest {
-        tracker.userProfiles
-    }
+            assertThat(tracker.initialized).isTrue()
+        }
 
     @Test
-    fun testInitialize() = testScope.runTest {
-        tracker.initialize(0)
+    fun testReceiverRegisteredOnInitialize() =
+        testScope.runTest {
+            tracker.initialize(0)
 
-        assertThat(tracker.initialized).isTrue()
-    }
+            val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
 
-    @Test
-    fun testReceiverRegisteredOnInitialize() = testScope.runTest {
-        tracker.initialize(0)
-
-        val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
-
-        verify(context)
+            verify(context)
                 .registerReceiverForAllUsers(eq(tracker), capture(captor), isNull(), eq(handler))
-        with(captor.value) {
-            assertThat(countActions()).isEqualTo(11)
-            assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
-            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
-            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
-            assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
-            assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
-        }
-    }
-
-    @Test
-    fun testInitialValuesSet() = testScope.runTest {
-        val testID = 4
-        tracker.initialize(testID)
-
-        verify(userManager).getProfiles(testID)
-
-        assertThat(tracker.userId).isEqualTo(testID)
-        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
-        assertThat(tracker.userContext.userId).isEqualTo(testID)
-        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
-        assertThat(tracker.userProfiles).hasSize(1)
-
-        val info = tracker.userProfiles[0]
-        assertThat(info.id).isEqualTo(testID)
-    }
-
-    @Test
-    fun testUserSwitch() = testScope.runTest {
-        tracker.initialize(0)
-        val newID = 5
-
-        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
-        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onBeforeUserSwitching(newID)
-        captor.value.onUserSwitching(newID, userSwitchingReply)
-        runCurrent()
-        verify(userSwitchingReply).sendResult(any())
-
-        verify(userManager).getProfiles(newID)
-
-        assertThat(tracker.userId).isEqualTo(newID)
-        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
-        assertThat(tracker.userContext.userId).isEqualTo(newID)
-        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
-        assertThat(tracker.userProfiles).hasSize(1)
-
-        val info = tracker.userProfiles[0]
-        assertThat(info.id).isEqualTo(newID)
-    }
-
-    @Test
-    fun testManagedProfileAvailable() = testScope.runTest {
-        tracker.initialize(0)
-        val profileID = tracker.userId + 10
-
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
+            with(captor.value) {
+                assertThat(countActions()).isEqualTo(11)
+                assertThat(hasAction(Intent.ACTION_LOCALE_CHANGED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_USER_INFO_CHANGED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)).isTrue()
+                assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)).isTrue()
+                assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_ADDED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_PROFILE_ADDED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_PROFILE_REMOVED)).isTrue()
+                assertThat(hasAction(Intent.ACTION_PROFILE_AVAILABLE)).isTrue()
+                assertThat(hasAction(Intent.ACTION_PROFILE_UNAVAILABLE)).isTrue()
+            }
         }
 
-        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-        tracker.onReceive(context, intent)
-
-        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
-    }
-
     @Test
-    fun testManagedProfileUnavailable() = testScope.runTest {
-        tracker.initialize(0)
-        val profileID = tracker.userId + 10
+    fun testInitialValuesSet() =
+        testScope.runTest {
+            val testID = 4
+            tracker.initialize(testID)
 
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
+            verify(userManager).getProfiles(testID)
+
+            assertThat(tracker.userId).isEqualTo(testID)
+            assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
+            assertThat(tracker.userContext.userId).isEqualTo(testID)
+            assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
+            assertThat(tracker.userProfiles).hasSize(1)
+
+            val info = tracker.userProfiles[0]
+            assertThat(info.id).isEqualTo(testID)
         }
 
-        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-        tracker.onReceive(context, intent)
-
-        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
-    }
-
     @Test
-    fun testManagedProfileStartedAndRemoved() = testScope.runTest {
-        tracker.initialize(0)
-        val profileID = tracker.userId + 10
+    fun testUserSwitch() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val newID = 5
 
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onUserSwitching(newID, userSwitchingReply)
+            runCurrent()
+            verify(userSwitchingReply).sendResult(any())
+
+            verify(userManager).getProfiles(newID)
+
+            assertThat(tracker.userId).isEqualTo(newID)
+            assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
+            assertThat(tracker.userContext.userId).isEqualTo(newID)
+            assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
+            assertThat(tracker.userProfiles).hasSize(1)
+
+            val info = tracker.userProfiles[0]
+            assertThat(info.id).isEqualTo(newID)
         }
 
-        // Managed profile started
-        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-        tracker.onReceive(context, intent)
+    @Test
+    fun testManagedProfileAvailable() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val profileID = tracker.userId + 10
 
-        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
+            whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+                val id = invocation.getArgument<Int>(0)
+                val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+                val infoProfile =
+                    UserInfo(
+                        id + 10,
+                        "",
+                        "",
+                        UserInfo.FLAG_MANAGED_PROFILE,
+                        UserManager.USER_TYPE_PROFILE_MANAGED
+                    )
+                infoProfile.profileGroupId = id
+                listOf(info, infoProfile)
+            }
 
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            listOf(UserInfo(invocation.getArgument(0), "", UserInfo.FLAG_FULL))
+            val intent =
+                Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+            tracker.onReceive(context, intent)
+
+            assertThat(tracker.userProfiles.map { it.id })
+                .containsExactly(tracker.userId, profileID)
         }
 
-        val intent2 = Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
-        tracker.onReceive(context, intent2)
-
-        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId)
-    }
-
     @Test
-    fun testCallbackNotCalledOnAdd() = testScope.runTest {
-        tracker.initialize(0)
-        val callback = TestCallback()
+    fun testManagedProfileUnavailable() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val profileID = tracker.userId + 10
 
-        tracker.addCallback(callback, executor)
+            whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+                val id = invocation.getArgument<Int>(0)
+                val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+                val infoProfile =
+                    UserInfo(
+                        id + 10,
+                        "",
+                        "",
+                        UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
+                        UserManager.USER_TYPE_PROFILE_MANAGED
+                    )
+                infoProfile.profileGroupId = id
+                listOf(info, infoProfile)
+            }
 
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
-        assertThat(callback.calledOnUserChanged).isEqualTo(0)
-    }
+            val intent =
+                Intent(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+            tracker.onReceive(context, intent)
 
-    @Test
-    fun testCallbackCalledOnUserChanging() = testScope.runTest {
-        tracker.initialize(0)
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-
-        val newID = 5
-
-        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
-        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onBeforeUserSwitching(newID)
-        captor.value.onUserSwitching(newID, userSwitchingReply)
-        runCurrent()
-
-        verify(userSwitchingReply).sendResult(any())
-        assertThat(callback.calledOnUserChanging).isEqualTo(1)
-        assertThat(callback.lastUser).isEqualTo(newID)
-        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
-    }
-
-    @Test
-    fun testAsyncCallbackWaitsUserToChange() = testScope.runTest {
-        // Skip this test for CountDownLatch variation. The problem is that there would be a
-        // deadlock if the callbacks processing runs on the same thread as the callback (which
-        // is blocked by the latch). Before the change it works because the callbacks are
-        // processed on a binder thread which is always distinct.
-        // This is the issue that this feature addresses.
-        assume().that(isBackgroundUserTrackerEnabled).isTrue()
-
-        tracker.initialize(0)
-        val callback = TestCallback()
-        val callbackExecutor = FakeExecutor(FakeSystemClock())
-        tracker.addCallback(callback, callbackExecutor)
-
-        val newID = 5
-
-        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
-        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onUserSwitching(newID, userSwitchingReply)
-
-        assertThat(callback.calledOnUserChanging).isEqualTo(0)
-        verify(userSwitchingReply, never()).sendResult(any())
-
-        FakeExecutor.exhaustExecutors(callbackExecutor)
-        runCurrent()
-        FakeExecutor.exhaustExecutors(callbackExecutor)
-        runCurrent()
-
-        assertThat(callback.calledOnUserChanging).isEqualTo(1)
-        verify(userSwitchingReply).sendResult(any())
-    }
-
-    @Test
-    fun testCallbackCalledOnUserChanged() = testScope.runTest {
-        tracker.initialize(0)
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-
-        val newID = 5
-
-        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
-        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onBeforeUserSwitching(newID)
-        captor.value.onUserSwitchComplete(newID)
-        runCurrent()
-
-        assertThat(callback.calledOnUserChanged).isEqualTo(1)
-        assertThat(callback.lastUser).isEqualTo(newID)
-        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
-        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
-    }
-
-    @Test
-    fun testCallbackCalledOnUserInfoChanged() = testScope.runTest {
-        tracker.initialize(0)
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-        val profileID = tracker.userId + 10
-
-        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
-            val id = invocation.getArgument<Int>(0)
-            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
-            val infoProfile = UserInfo(
-                    id + 10,
-                    "",
-                    "",
-                    UserInfo.FLAG_MANAGED_PROFILE,
-                    UserManager.USER_TYPE_PROFILE_MANAGED
-            )
-            infoProfile.profileGroupId = id
-            listOf(info, infoProfile)
+            assertThat(tracker.userProfiles.map { it.id })
+                .containsExactly(tracker.userId, profileID)
         }
 
-        val intent = Intent(Intent.ACTION_USER_INFO_CHANGED)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+    @Test
+    fun testManagedProfileStartedAndRemoved() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val profileID = tracker.userId + 10
 
-        tracker.onReceive(context, intent)
+            whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+                val id = invocation.getArgument<Int>(0)
+                val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+                val infoProfile =
+                    UserInfo(
+                        id + 10,
+                        "",
+                        "",
+                        UserInfo.FLAG_MANAGED_PROFILE,
+                        UserManager.USER_TYPE_PROFILE_MANAGED
+                    )
+                infoProfile.profileGroupId = id
+                listOf(info, infoProfile)
+            }
 
-        assertThat(callback.calledOnUserChanged).isEqualTo(0)
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
-        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
-    }
+            // Managed profile started
+            val intent =
+                Intent(Intent.ACTION_MANAGED_PROFILE_UNLOCKED)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+            tracker.onReceive(context, intent)
+
+            assertThat(tracker.userProfiles.map { it.id })
+                .containsExactly(tracker.userId, profileID)
+
+            whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+                listOf(UserInfo(invocation.getArgument(0), "", UserInfo.FLAG_FULL))
+            }
+
+            val intent2 =
+                Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+            tracker.onReceive(context, intent2)
+
+            assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId)
+        }
 
     @Test
-    fun testCallbackRemoved() = testScope.runTest {
-        tracker.initialize(0)
-        val newID = 5
-        val profileID = newID + 10
+    fun testCallbackNotCalledOnAdd() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val callback = TestCallback()
 
-        val callback = TestCallback()
-        tracker.addCallback(callback, executor)
-        tracker.removeCallback(callback)
+            tracker.addCallback(callback, executor)
 
-        val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
-        verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-        captor.value.onUserSwitching(newID, userSwitchingReply)
-        runCurrent()
-        verify(userSwitchingReply).sendResult(any())
-        captor.value.onUserSwitchComplete(newID)
+            assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+            assertThat(callback.calledOnUserChanged).isEqualTo(0)
+        }
 
-        val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
-                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+    @Test
+    fun testCallbackCalledOnUserChanging() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val callback = TestCallback()
+            tracker.addCallback(callback, executor)
 
-        tracker.onReceive(context, intentProfiles)
+            val newID = 5
 
-        assertThat(callback.calledOnUserChanging).isEqualTo(0)
-        assertThat(callback.calledOnUserChanged).isEqualTo(0)
-        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
-    }
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onUserSwitching(newID, userSwitchingReply)
+            runCurrent()
+
+            verify(userSwitchingReply).sendResult(any())
+            assertThat(callback.calledOnUserChanging).isEqualTo(1)
+            assertThat(callback.lastUser).isEqualTo(newID)
+            assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+        }
+
+    @Test
+    fun testAsyncCallbackWaitsUserToChange() =
+        testScope.runTest {
+            // Skip this test for CountDownLatch variation. The problem is that there would be a
+            // deadlock if the callbacks processing runs on the same thread as the callback (which
+            // is blocked by the latch). Before the change it works because the callbacks are
+            // processed on a binder thread which is always distinct.
+            // This is the issue that this feature addresses.
+            assume().that(isBackgroundUserTrackerEnabled).isTrue()
+
+            tracker.initialize(0)
+            val callback = TestCallback()
+            val callbackExecutor = FakeExecutor(FakeSystemClock())
+            tracker.addCallback(callback, callbackExecutor)
+
+            val newID = 5
+
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onUserSwitching(newID, userSwitchingReply)
+
+            assertThat(callback.calledOnUserChanging).isEqualTo(0)
+            verify(userSwitchingReply, never()).sendResult(any())
+
+            FakeExecutor.exhaustExecutors(callbackExecutor)
+            runCurrent()
+            FakeExecutor.exhaustExecutors(callbackExecutor)
+            runCurrent()
+
+            assertThat(callback.calledOnUserChanging).isEqualTo(1)
+            verify(userSwitchingReply).sendResult(any())
+        }
+
+    @Test
+    fun testCallbackCalledOnUserChanged() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val callback = TestCallback()
+            tracker.addCallback(callback, executor)
+
+            val newID = 5
+
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onUserSwitchComplete(newID)
+            runCurrent()
+
+            assertThat(callback.calledOnUserChanged).isEqualTo(1)
+            assertThat(callback.lastUser).isEqualTo(newID)
+            assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+            assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+            assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
+        }
+
+    @Test
+    fun testCallbackCalledOnUserInfoChanged() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val callback = TestCallback()
+            tracker.addCallback(callback, executor)
+            val profileID = tracker.userId + 10
+
+            whenever(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+                val id = invocation.getArgument<Int>(0)
+                val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+                val infoProfile =
+                    UserInfo(
+                        id + 10,
+                        "",
+                        "",
+                        UserInfo.FLAG_MANAGED_PROFILE,
+                        UserManager.USER_TYPE_PROFILE_MANAGED
+                    )
+                infoProfile.profileGroupId = id
+                listOf(info, infoProfile)
+            }
+
+            val intent =
+                Intent(Intent.ACTION_USER_INFO_CHANGED)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+            tracker.onReceive(context, intent)
+
+            assertThat(callback.calledOnUserChanged).isEqualTo(0)
+            assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+            assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+        }
+
+    @Test
+    fun testCallbackRemoved() =
+        testScope.runTest {
+            tracker.initialize(0)
+            val newID = 5
+            val profileID = newID + 10
+
+            val callback = TestCallback()
+            tracker.addCallback(callback, executor)
+            tracker.removeCallback(callback)
+
+            val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+            verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onUserSwitching(newID, userSwitchingReply)
+            runCurrent()
+            verify(userSwitchingReply).sendResult(any())
+            captor.value.onUserSwitchComplete(newID)
+
+            val intentProfiles =
+                Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                    .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+            tracker.onReceive(context, intentProfiles)
+
+            assertThat(callback.calledOnUserChanging).isEqualTo(0)
+            assertThat(callback.calledOnUserChanged).isEqualTo(0)
+            assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+        }
 
     private class TestCallback : UserTracker.Callback {
         var calledOnUserChanging = 0
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 5de31d88..c0444fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -33,7 +33,7 @@
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BACK_GESTURE
+import com.android.systemui.Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.ambient.touch.TouchHandler
 import com.android.systemui.ambient.touch.TouchMonitor
@@ -57,9 +57,12 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.media.controls.controller.keyguardMediaController
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController
 import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -114,7 +117,8 @@
             object : AmbientTouchComponent.Factory {
                 override fun create(
                     lifecycleOwner: LifecycleOwner,
-                    touchHandlers: Set<TouchHandler>
+                    touchHandlers: Set<TouchHandler>,
+                    loggingName: String
                 ): AmbientTouchComponent =
                     object : AmbientTouchComponent {
                         override fun getTouchMonitor(): TouchMonitor = touchMonitor
@@ -134,7 +138,10 @@
                     ambientTouchComponentFactory,
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
-                    kosmos.notificationStackScrollLayoutController
+                    kosmos.notificationStackScrollLayoutController,
+                    kosmos.keyguardMediaController,
+                    kosmos.lockscreenSmartspaceController,
+                    logcatLogBuffer("GlanceableHubContainerControllerTest")
                 )
         }
         testableLooper = TestableLooper.get(this)
@@ -178,7 +185,10 @@
                         ambientTouchComponentFactory,
                         communalContent,
                         kosmos.sceneDataSourceDelegator,
-                        kosmos.notificationStackScrollLayoutController
+                        kosmos.notificationStackScrollLayoutController,
+                        kosmos.keyguardMediaController,
+                        kosmos.lockscreenSmartspaceController,
+                        logcatLogBuffer("GlanceableHubContainerControllerTest")
                     )
 
                 // First call succeeds.
@@ -205,6 +215,9 @@
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
                     kosmos.notificationStackScrollLayoutController,
+                    kosmos.keyguardMediaController,
+                    kosmos.lockscreenSmartspaceController,
+                    logcatLogBuffer("GlanceableHubContainerControllerTest")
                 )
 
             assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
@@ -226,6 +239,9 @@
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
                     kosmos.notificationStackScrollLayoutController,
+                    kosmos.keyguardMediaController,
+                    kosmos.lockscreenSmartspaceController,
+                    logcatLogBuffer("GlanceableHubContainerControllerTest")
                 )
 
             // Only initView without attaching a view as we don't want the flows to start collecting
@@ -401,13 +417,17 @@
                 // Communal is open.
                 goToScene(CommunalScenes.Communal)
 
-                // Shade shows up.
-                shadeTestUtil.setQsExpansion(0.5f)
-                testableLooper.processAllMessages()
+                // Touch starts and ends.
                 assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
                 assertThat(underTest.onTouchEvent(CANCEL_EVENT)).isTrue()
+
+                // Up event is no longer processed
                 assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+
+                // Move event can still be processed
                 assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+                assertThat(underTest.onTouchEvent(MOVE_EVENT)).isTrue()
+                assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
             }
         }
 
@@ -425,7 +445,7 @@
         }
 
     @Test
-    @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+    @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
     fun gestureExclusionZone_setAfterInit() =
         with(kosmos) {
             testScope.runTest {
@@ -451,7 +471,27 @@
         }
 
     @Test
-    @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
+    @EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+    fun gestureExclusionZone_setAfterInit_fullSwipe() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
+                goToScene(CommunalScenes.Communal)
+
+                assertThat(containerView.systemGestureExclusionRects)
+                    .containsExactly(
+                        Rect(
+                            /* left= */ 0,
+                            /* top= */ 0,
+                            /* right= */ 0,
+                            /* bottom= */ CONTAINER_HEIGHT
+                        )
+                    )
+            }
+        }
+
+    @Test
+    @DisableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
     fun gestureExclusionZone_setAfterInit_rtl() =
         with(kosmos) {
             testScope.runTest {
@@ -476,35 +516,8 @@
             }
         }
 
-    @Test
-    @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
-    fun gestureExclusionZone_setAfterInit_backGestureEnabled() =
-        with(kosmos) {
-            testScope.runTest {
-                whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_LTR)
-                goToScene(CommunalScenes.Communal)
-
-                assertThat(containerView.systemGestureExclusionRects)
-                    .containsExactly(
-                        Rect(
-                            /* left= */ FAKE_INSETS.left,
-                            /* top= */ TOP_SWIPE_REGION_WIDTH,
-                            /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right,
-                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
-                        ),
-                        Rect(
-                            /* left= */ 0,
-                            /* top= */ 0,
-                            /* right= */ FAKE_INSETS.right,
-                            /* bottom= */ CONTAINER_HEIGHT
-                        )
-                    )
-            }
-        }
-
-    @Test
-    @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE)
-    fun gestureExclusionZone_setAfterInit_backGestureEnabled_rtl() =
+    @EnableFlags(FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
+    fun gestureExclusionZone_setAfterInit_rtl_fullSwipe() =
         with(kosmos) {
             testScope.runTest {
                 whenever(containerView.layoutDirection).thenReturn(View.LAYOUT_DIRECTION_RTL)
@@ -513,13 +526,7 @@
                 assertThat(containerView.systemGestureExclusionRects)
                     .containsExactly(
                         Rect(
-                            /* left= */ FAKE_INSETS.left,
-                            /* top= */ TOP_SWIPE_REGION_WIDTH,
-                            /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right,
-                            /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH
-                        ),
-                        Rect(
-                            /* left= */ FAKE_INSETS.left,
+                            /* left= */ 0,
                             /* top= */ 0,
                             /* right= */ CONTAINER_WIDTH,
                             /* bottom= */ CONTAINER_HEIGHT
@@ -599,6 +606,30 @@
         }
 
     @Test
+    fun fullScreenSwipeGesture_doNotProcessTouchesInUmo() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is closed.
+                goToScene(CommunalScenes.Blank)
+                whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any()))
+                    .thenReturn(true)
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+            }
+        }
+
+    @Test
+    fun fullScreenSwipeGesture_doNotProcessTouchesInSmartspace() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is closed.
+                goToScene(CommunalScenes.Blank)
+                whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any()))
+                    .thenReturn(true)
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+            }
+        }
+
+    @Test
     fun onTouchEvent_hubOpen_touchesDispatched() =
         with(kosmos) {
             testScope.runTest {
@@ -607,7 +638,9 @@
 
                 // Touch event is sent to the container view.
                 assertThat(underTest.onTouchEvent(DOWN_EVENT)).isTrue()
-                verify(containerView).onTouchEvent(any())
+                verify(containerView).onTouchEvent(DOWN_EVENT)
+                assertThat(underTest.onTouchEvent(UP_EVENT)).isTrue()
+                verify(containerView).onTouchEvent(UP_EVENT)
             }
         }
 
@@ -654,13 +687,107 @@
             }
         }
 
+    @Test
+    fun onTouchEvent_shadeInteracting_movesNotDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // On lockscreen.
+                goToScene(CommunalScenes.Blank)
+                whenever(
+                        notificationStackScrollLayoutController.isBelowLastNotification(
+                            any(),
+                            any()
+                        )
+                    )
+                    .thenReturn(true)
+
+                // Touches not consumed by default but are received by containerView.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+                verify(containerView).onTouchEvent(DOWN_EVENT)
+
+                // User is interacting with shade on lockscreen.
+                shadeTestUtil.setLockscreenShadeTracking(true)
+                testableLooper.processAllMessages()
+
+                // A move event is ignored while the user is already interacting.
+                assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
+                verify(containerView, never()).onTouchEvent(MOVE_EVENT)
+
+                // An up event is still delivered.
+                assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+                verify(containerView).onTouchEvent(UP_EVENT)
+            }
+        }
+
+    @Test
+    fun onTouchEvent_shadeExpanding_touchesNotDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // On lockscreen.
+                goToScene(CommunalScenes.Blank)
+                whenever(
+                        notificationStackScrollLayoutController.isBelowLastNotification(
+                            any(),
+                            any()
+                        )
+                    )
+                    .thenReturn(true)
+
+                // Shade is open slightly.
+                shadeTestUtil.setShadeExpansion(0.01f)
+                testableLooper.processAllMessages()
+
+                // Touches are not consumed.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+                verify(containerView, never()).onTouchEvent(DOWN_EVENT)
+            }
+        }
+
+    @Test
+    fun onTouchEvent_bouncerInteracting_movesNotDispatched() =
+        with(kosmos) {
+            testScope.runTest {
+                // On lockscreen.
+                goToScene(CommunalScenes.Blank)
+                whenever(
+                        notificationStackScrollLayoutController.isBelowLastNotification(
+                            any(),
+                            any()
+                        )
+                    )
+                    .thenReturn(true)
+
+                // Touches not consumed by default but are received by containerView.
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+                verify(containerView).onTouchEvent(DOWN_EVENT)
+
+                // User is interacting with bouncer on lockscreen.
+                fakeKeyguardBouncerRepository.setPrimaryShow(true)
+                testableLooper.processAllMessages()
+
+                // A move event is ignored while the user is already interacting.
+                assertThat(underTest.onTouchEvent(MOVE_EVENT)).isFalse()
+                verify(containerView, never()).onTouchEvent(MOVE_EVENT)
+
+                // An up event is still delivered.
+                assertThat(underTest.onTouchEvent(UP_EVENT)).isFalse()
+                verify(containerView).onTouchEvent(UP_EVENT)
+            }
+        }
+
     private fun initAndAttachContainerView() {
         val mockInsets =
             mock<WindowInsets> {
                 on { getInsets(WindowInsets.Type.systemGestures()) } doReturn FAKE_INSETS
             }
 
-        containerView = spy(View(context)) { on { rootWindowInsets } doReturn mockInsets }
+        containerView =
+            spy(View(context)) {
+                on { rootWindowInsets } doReturn mockInsets
+                // Return true to handle touch events or else further events in the gesture will not
+                // be received as we are using real View objects.
+                onGeneric { onTouchEvent(any()) } doReturn true
+            }
 
         parentView = FrameLayout(context)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 8125ef5..9481e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -155,7 +155,6 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger;
 import com.android.systemui.statusbar.notification.data.repository.NotificationsKeyguardViewStateRepository;
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor;
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -163,7 +162,6 @@
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
-import com.android.systemui.statusbar.notification.stack.data.repository.FakeHeadsUpNotificationRepository;
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -365,13 +363,6 @@
     protected TestScope mTestScope = mKosmos.getTestScope();
     protected ShadeInteractor mShadeInteractor;
     protected PowerInteractor mPowerInteractor;
-    protected FakeHeadsUpNotificationRepository mFakeHeadsUpNotificationRepository =
-            new FakeHeadsUpNotificationRepository();
-    protected NotificationsKeyguardViewStateRepository mNotificationsKeyguardViewStateRepository =
-            new NotificationsKeyguardViewStateRepository();
-    protected NotificationsKeyguardInteractor mNotificationsKeyguardInteractor =
-            new NotificationsKeyguardInteractor(mNotificationsKeyguardViewStateRepository);
-    protected HeadsUpNotificationInteractor mHeadsUpNotificationInteractor;
     protected NotificationPanelViewController.TouchHandler mTouchHandler;
     protected ConfigurationController mConfigurationController;
     protected SysuiStatusBarStateController mStatusBarStateController;
@@ -468,7 +459,8 @@
                 () -> mKosmos.getDeviceUnlockedInteractor(),
                 () -> mKosmos.getSceneInteractor(),
                 () -> mKosmos.getSceneContainerOcclusionInteractor(),
-                () -> mKosmos.getKeyguardClockInteractor());
+                () -> mKosmos.getKeyguardClockInteractor(),
+                () -> mKosmos.getSceneBackInteractor());
 
         KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
         keyguardStatusView.setId(R.id.keyguard_status_view);
@@ -625,7 +617,8 @@
                                 () -> mKosmos.getDeviceUnlockedInteractor(),
                                 () -> mKosmos.getSceneInteractor(),
                                 () -> mKosmos.getSceneContainerOcclusionInteractor(),
-                                () -> mKosmos.getKeyguardClockInteractor()),
+                                () -> mKosmos.getKeyguardClockInteractor(),
+                                () -> mKosmos.getSceneBackInteractor()),
                         mKeyguardBypassController,
                         mDozeParameters,
                         mScreenOffAnimationController,
@@ -687,12 +680,6 @@
         when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes);
         when(longPressHandlingViewRes.getString(anyInt())).thenReturn("");
 
-
-        mHeadsUpNotificationInteractor =
-                new HeadsUpNotificationInteractor(mFakeHeadsUpNotificationRepository,
-                        mDeviceEntryFaceAuthInteractor, mKeyguardTransitionInteractor,
-                        mNotificationsKeyguardInteractor, mShadeInteractor);
-
         mNotificationPanelViewController = new NotificationPanelViewController(
                 mView,
                 mMainHandler,
@@ -767,7 +754,6 @@
                 mActivityStarter,
                 mSharedNotificationContainerInteractor,
                 mActiveNotificationsInteractor,
-                mHeadsUpNotificationInteractor,
                 mShadeAnimationInteractor,
                 mKeyguardViewConfigurator,
                 mDeviceEntryFaceAuthInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 905cc4c..a7fd160 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -58,13 +58,13 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.DejankUtils;
+import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm;
@@ -1375,7 +1375,7 @@
     }
 
     @Test
-    @DisableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @DisableSceneContainer
     public void shadeExpanded_whenHunIsPresent() {
         when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
         assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
index 64eadb7..90655c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt
@@ -19,7 +19,6 @@
 package com.android.systemui.shade
 
 import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper
 import android.view.HapticFeedbackConstants
 import android.view.View
@@ -35,7 +34,6 @@
 import com.android.systemui.statusbar.StatusBarState.SHADE
 import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -243,36 +241,4 @@
         val bottomAreaAlpha by collectLastValue(mFakeKeyguardRepository.bottomAreaAlpha)
         assertThat(bottomAreaAlpha).isEqualTo(1f)
     }
-
-    @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
-    fun shadeExpanded_whenHunIsPresent() = runTest {
-        launch(mainDispatcher) {
-            givenViewAttached()
-
-            // WHEN a pinned heads up is present
-            mFakeHeadsUpNotificationRepository.setNotifications(
-                FakeHeadsUpRowRepository("key", isPinned = true)
-            )
-        }
-        advanceUntilIdle()
-
-        // THEN the panel should be visible
-        assertThat(mNotificationPanelViewController.isExpanded).isTrue()
-    }
-
-    @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
-    fun shadeExpanded_whenHunIsAnimatingAway() = runTest {
-        launch(mainDispatcher) {
-            givenViewAttached()
-
-            // WHEN a heads up is animating away
-            mFakeHeadsUpNotificationRepository.isHeadsUpAnimatingAway.value = true
-        }
-        advanceUntilIdle()
-
-        // THEN the panel should be visible
-        assertThat(mNotificationPanelViewController.isExpanded).isTrue()
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 505f799..3f6617b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -197,6 +197,7 @@
                 () -> sceneInteractor,
                 () -> mKosmos.getFromGoneTransitionInteractor(),
                 () -> mKosmos.getFromLockscreenTransitionInteractor(),
+                () -> mKosmos.getFromOccludedTransitionInteractor(),
                 () -> mKosmos.getSharedNotificationContainerInteractor(),
                 mTestScope);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
new file mode 100644
index 0000000..b65a902
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImplTest.kt
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.shadeTestUtil
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableSceneContainer
+class ShadeInteractorSceneContainerImplTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+    private val configurationRepository = kosmos.fakeConfigurationRepository
+    private val keyguardRepository = kosmos.fakeKeyguardRepository
+    private val sceneInteractor = kosmos.sceneInteractor
+    private val shadeTestUtil = kosmos.shadeTestUtil
+
+    private val underTest = kosmos.shadeInteractorSceneContainerImpl
+
+    @Test
+    fun qsExpansionWhenInSplitShadeAndQsExpanded() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.qsExpansion)
+
+            // WHEN split shade is enabled and QS is expanded
+            shadeTestUtil.setSplitShade(true)
+            configurationRepository.onAnyConfigurationChange()
+            runCurrent()
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.QuickSettings,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Shade),
+                        progress = MutableStateFlow(.3f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+            // THEN legacy shade expansion is passed through
+            Truth.assertThat(actual).isEqualTo(.3f)
+        }
+
+    @Test
+    fun qsExpansionWhenNotInSplitShadeAndQsExpanded() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.qsExpansion)
+
+            // WHEN split shade is not enabled and QS is expanded
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            shadeTestUtil.setSplitShade(false)
+            configurationRepository.onAnyConfigurationChange()
+            runCurrent()
+            val progress = MutableStateFlow(.3f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.QuickSettings,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Shade),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN shade expansion is zero
+            Truth.assertThat(actual).isEqualTo(.7f)
+        }
+
+    @Test
+    fun qsFullscreen_falseWhenTransitioning() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsFullscreen)
+
+            // WHEN scene transition active
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.QuickSettings,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Shade),
+                        progress = MutableStateFlow(.3f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN QS is not fullscreen
+            Truth.assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun qsFullscreen_falseWhenIdleNotQS() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsFullscreen)
+
+            // WHEN Idle but not on QuickSettings scene
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.Shade)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN QS is not fullscreen
+            Truth.assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun qsFullscreen_falseWhenIdleSplitShadeQs() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsFullscreen)
+
+            // WHEN split shade is enabled and Idle on QuickSettings scene
+            shadeTestUtil.setSplitShade(true)
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.QuickSettings)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN QS is not fullscreen
+            Truth.assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun qsFullscreen_trueWhenIdleQS() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsFullscreen)
+
+            // WHEN Idle on QuickSettings scene
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.QuickSettings)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN QS is fullscreen
+            Truth.assertThat(actual).isTrue()
+        }
+
+    @Test
+    fun lockscreenShadeExpansion_idle_onScene() =
+        testScope.runTest {
+            // GIVEN an expansion flow based on transitions to and from a scene
+            val key = Scenes.Shade
+            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+            val expansionAmount by collectLastValue(expansion)
+
+            // WHEN transition state is idle on the scene
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN expansion is 1
+            Truth.assertThat(expansionAmount).isEqualTo(1f)
+        }
+
+    @Test
+    fun lockscreenShadeExpansion_idle_onDifferentScene() =
+        testScope.runTest {
+            // GIVEN an expansion flow based on transitions to and from a scene
+            val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.Shade)
+            val expansionAmount by collectLastValue(expansion)
+
+            // WHEN transition state is idle on a different scene
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(Scenes.Lockscreen)
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN expansion is 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+        }
+
+    @Test
+    fun lockscreenShadeExpansion_transitioning_toScene() =
+        testScope.runTest {
+            // GIVEN an expansion flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+            val expansionAmount by collectLastValue(expansion)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = key,
+                        currentScene = flowOf(key),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN expansion is 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN expansion matches the progress
+            Truth.assertThat(expansionAmount).isEqualTo(.4f)
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN expansion is 1
+            Truth.assertThat(expansionAmount).isEqualTo(1f)
+        }
+
+    @Test
+    fun lockscreenShadeExpansion_transitioning_fromScene() =
+        testScope.runTest {
+            // GIVEN an expansion flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val expansion = underTest.sceneBasedExpansion(sceneInteractor, key)
+            val expansionAmount by collectLastValue(expansion)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = key,
+                        toScene = Scenes.Lockscreen,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN expansion is 1
+            Truth.assertThat(expansionAmount).isEqualTo(1f)
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN expansion reflects the progress
+            Truth.assertThat(expansionAmount).isEqualTo(.6f)
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN expansion is 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+        }
+
+    fun isQsBypassingShade_goneToQs() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsBypassingShade)
+
+            // WHEN transitioning from QS directly to Gone
+            configurationRepository.onAnyConfigurationChange()
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Gone,
+                        toScene = Scenes.QuickSettings,
+                        currentScene = flowOf(Scenes.QuickSettings),
+                        progress = MutableStateFlow(.1f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN qs is bypassing shade
+            Truth.assertThat(actual).isTrue()
+        }
+
+    fun isQsBypassingShade_shadeToQs() =
+        testScope.runTest {
+            val actual by collectLastValue(underTest.isQsBypassingShade)
+
+            // WHEN transitioning from QS to Shade
+            configurationRepository.onAnyConfigurationChange()
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Shade,
+                        toScene = Scenes.QuickSettings,
+                        currentScene = flowOf(Scenes.QuickSettings),
+                        progress = MutableStateFlow(.1f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+            runCurrent()
+
+            // THEN qs is not bypassing shade
+            Truth.assertThat(actual).isFalse()
+        }
+
+    @Test
+    fun lockscreenShadeExpansion_transitioning_toAndFromDifferentScenes() =
+        testScope.runTest {
+            // GIVEN an expansion flow based on transitions to and from a scene
+            val expansion = underTest.sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
+            val expansionAmount by collectLastValue(expansion)
+
+            // WHEN transition state is starting to between different scenes
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = Scenes.Shade,
+                        currentScene = flowOf(Scenes.Shade),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN expansion is 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+            // WHEN transition state is partially complete
+            progress.value = .4f
+
+            // THEN expansion is still 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN expansion is still 0
+            Truth.assertThat(expansionAmount).isEqualTo(0f)
+        }
+
+    @Test
+    fun userInteracting_idle() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = Scenes.Shade
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is idle
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toScene_programmatic() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = key,
+                        currentScene = flowOf(key),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toScene_userInputDriven() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = key,
+                        currentScene = flowOf(key),
+                        progress = progress,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+        }
+
+    @Test
+    fun userInteracting_transitioning_fromScene_programmatic() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = key,
+                        toScene = Scenes.Lockscreen,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is false
+            Truth.assertThat(interacting).isFalse()
+        }
+
+    @Test
+    fun userInteracting_transitioning_fromScene_userInputDriven() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val key = Scenes.QuickSettings
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, key)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to move to the scene
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = key,
+                        toScene = Scenes.Lockscreen,
+                        currentScene = flowOf(Scenes.Lockscreen),
+                        progress = progress,
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+
+            // WHEN transition state is partially to the scene
+            progress.value = .4f
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+
+            // WHEN transition completes
+            progress.value = 1f
+
+            // THEN interacting is true
+            Truth.assertThat(interacting).isTrue()
+        }
+
+    @Test
+    fun userInteracting_transitioning_toAndFromDifferentScenes() =
+        testScope.runTest {
+            // GIVEN an interacting flow based on transitions to and from a scene
+            val interactingFlow = underTest.sceneBasedInteracting(sceneInteractor, Scenes.Shade)
+            val interacting by collectLastValue(interactingFlow)
+
+            // WHEN transition state is starting to between different scenes
+            val progress = MutableStateFlow(0f)
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Transition(
+                        fromScene = Scenes.Lockscreen,
+                        toScene = Scenes.QuickSettings,
+                        currentScene = flowOf(Scenes.QuickSettings),
+                        progress = MutableStateFlow(0f),
+                        isInitiatedByUserInput = true,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            sceneInteractor.setTransitionState(transitionState)
+
+            // THEN interacting is false
+            assertThat(interacting).isFalse()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 150f53d..022825a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -22,8 +22,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.TRANSIT_CLOCK
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
 import com.android.systemui.plugins.clocks.ClockMessageBuffers
@@ -76,7 +74,6 @@
     private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
     private lateinit var registry: ClockRegistry
     private lateinit var pickerConfig: ClockPickerConfig
-    private val featureFlags = FakeFeatureFlags()
 
     companion object {
         private fun failFactory(clockId: ClockId): ClockController {
@@ -532,44 +529,4 @@
         val actual = ClockSettings.serialize(ClockSettings("ID", null))
         assertEquals(expected, actual)
     }
-
-    @Test
-    fun testTransitClockEnabled_hasTransitClock() {
-        testTransitClockFlag(true)
-    }
-
-    @Test
-    fun testTransitClockDisabled_noTransitClock() {
-        testTransitClockFlag(false)
-    }
-
-    private fun testTransitClockFlag(flag: Boolean) {
-        featureFlags.set(TRANSIT_CLOCK, flag)
-        registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
-        val plugin = FakeClockPlugin()
-                .addClock("clock_1")
-                .addClock("DIGITAL_CLOCK_METRO")
-        val lifecycle = FakeLifecycle("metro", plugin)
-        pluginListener.onPluginLoaded(plugin, mockContext, lifecycle)
-
-        val list = registry.getClocks()
-        if (flag) {
-            assertEquals(
-                    setOf(
-                            ClockMetadata(DEFAULT_CLOCK_ID),
-                            ClockMetadata("clock_1"),
-                            ClockMetadata("DIGITAL_CLOCK_METRO")
-                    ),
-                    list.toSet()
-            )
-        } else {
-            assertEquals(
-                    setOf(
-                            ClockMetadata(DEFAULT_CLOCK_ID),
-                            ClockMetadata("clock_1")
-                    ),
-                    list.toSet()
-            )
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
deleted file mode 100644
index 2a6754c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
+++ /dev/null
@@ -1,146 +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.shared.navigationbar
-
-import android.graphics.Rect
-import android.testing.TestableLooper.RunWithLooper
-import android.view.SurfaceControl
-import android.view.View
-import android.view.ViewRootImpl
-import androidx.concurrent.futures.DirectExecutor
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-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.time.FakeSystemClock
-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.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-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
-import org.mockito.junit.MockitoJUnit
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-@RunWithLooper
-class RegionSamplingHelperTest : SysuiTestCase() {
-
-    @Mock
-    lateinit var sampledView: View
-    @Mock
-    lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback
-    @Mock
-    lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener
-    @Mock
-    lateinit var viewRootImpl: ViewRootImpl
-    @Mock
-    lateinit var surfaceControl: SurfaceControl
-    @Mock
-    lateinit var wrappedSurfaceControl: SurfaceControl
-    @JvmField @Rule
-    var rule = MockitoJUnit.rule()
-    lateinit var regionSamplingHelper: RegionSamplingHelper
-
-    @Before
-    fun setup() {
-        whenever(sampledView.isAttachedToWindow).thenReturn(true)
-        whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl)
-        whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
-        whenever(surfaceControl.isValid).thenReturn(true)
-        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
-        whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
-        regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback,
-                DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) {
-            override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
-                return wrappedSurfaceControl
-            }
-        }
-        regionSamplingHelper.setWindowVisible(true)
-    }
-
-    @Test
-    fun testStart_register() {
-        regionSamplingHelper.start(Rect(0, 0, 100, 100))
-        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any())
-    }
-
-    @Test
-    fun testStart_unregister() {
-        regionSamplingHelper.start(Rect(0, 0, 100, 100))
-        regionSamplingHelper.setWindowVisible(false)
-        verify(compositionListener).unregister(any())
-    }
-
-    @Test
-    fun testStart_hasBlur_neverRegisters() {
-        regionSamplingHelper.setWindowHasBlurs(true)
-        regionSamplingHelper.start(Rect(0, 0, 100, 100))
-        verify(compositionListener, never())
-                .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
-    }
-
-    @Test
-    fun testStart_stopAndDestroy() {
-        regionSamplingHelper.start(Rect(0, 0, 100, 100))
-        regionSamplingHelper.stopAndDestroy()
-        verify(compositionListener).unregister(any())
-    }
-
-    @Test
-    fun testCompositionSamplingListener_has_nonEmptyRect() {
-        // simulate race condition
-        val fakeExecutor = FakeExecutor(FakeSystemClock()) // pass in as backgroundExecutor
-        val fakeSamplingCallback = mock(RegionSamplingHelper.SamplingCallback::class.java)
-
-        whenever(fakeSamplingCallback.isSamplingEnabled).thenReturn(true)
-        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
-
-        regionSamplingHelper = object : RegionSamplingHelper(sampledView, fakeSamplingCallback,
-                DirectExecutor.INSTANCE, fakeExecutor, compositionListener) {
-            override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
-                return wrappedSurfaceControl
-            }
-        }
-        regionSamplingHelper.setWindowVisible(true)
-        regionSamplingHelper.start(Rect(0, 0, 100, 100))
-
-        // make sure background task is enqueued
-        assertThat(fakeExecutor.numPending()).isEqualTo(1)
-
-        // make sure regionSamplingHelper will have empty Rect
-        whenever(fakeSamplingCallback.getSampledRegion(any())).thenReturn(Rect(0, 0, 0, 0))
-        regionSamplingHelper.onLayoutChange(sampledView, 0, 0, 0, 0, 0, 0, 0, 0)
-
-        // resume running of background thread
-        fakeExecutor.runAllReady()
-
-        // grab Rect passed into compositionSamplingListener and make sure it's not empty
-        val argumentGrabber = argumentCaptor<Rect>()
-        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl),
-                argumentGrabber.capture())
-        assertThat(argumentGrabber.value.isEmpty).isFalse()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
index 660e8da..39e4fc9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/system/RemoteTransitionTest.java
@@ -30,7 +30,7 @@
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 6916bbd..d10ea1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -15,7 +15,9 @@
 package com.android.systemui.statusbar;
 
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
 import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_ACTIVE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
 
@@ -194,9 +196,11 @@
 
     @Test
     public void testShowImeButton() {
-        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, IME_ACTIVE,
+                BACK_DISPOSITION_ADJUST_NOTHING, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(IME_ACTIVE),
+                eq(BACK_DISPOSITION_ADJUST_NOTHING), eq(true));
     }
 
     @Test
@@ -204,11 +208,13 @@
         // First show in default display to update the "last updated ime display"
         testShowImeButton();
 
-        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, 1, 2, true);
+        mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, IME_ACTIVE,
+                BACK_DISPOSITION_ADJUST_NOTHING, true);
         waitForIdleSync();
-        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(0),
+        verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(0) /* vis */,
                 eq(BACK_DISPOSITION_DEFAULT), eq(false));
-        verify(mCallbacks).setImeWindowStatus(eq(SECONDARY_DISPLAY), eq(1), eq(2), eq(true));
+        verify(mCallbacks).setImeWindowStatus(eq(SECONDARY_DISPLAY), eq(IME_ACTIVE),
+                eq(BACK_DISPOSITION_ADJUST_NOTHING), eq(true));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
index 396d017..d6b3b91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OperatorNameViewControllerTest.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeSubscriptionManagerProxy
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.tuner.TunerService
@@ -71,7 +71,6 @@
 
     private val airplaneModeRepository = FakeAirplaneModeRepository()
     private val connectivityRepository = FakeConnectivityRepository()
-    private val mobileConnectionsRepository = FakeMobileConnectionsRepository()
 
     @Before
     fun setup() {
@@ -81,7 +80,7 @@
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 connectivityRepository,
-                mobileConnectionsRepository,
+                kosmos.fakeMobileConnectionsRepository,
             )
 
         underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 50131cb..60a1855 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.app.Flags;
 import android.app.Notification;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -41,11 +42,16 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Icon;
+import android.graphics.drawable.ShapeDrawable;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.service.notification.StatusBarNotification;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -191,6 +197,34 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+    public void setIcon_withPreloaded_usesPreloaded() {
+        Icon mockIcon = mock(Icon.class);
+        when(mockIcon.loadDrawableAsUser(any(), anyInt())).thenReturn(new ColorDrawable(1));
+        mStatusBarIcon.icon = mockIcon;
+        mStatusBarIcon.preloadedIcon = new ShapeDrawable();
+
+        mIconView.set(mStatusBarIcon);
+
+        assertThat(mIconView.getDrawable()).isNotNull();
+        assertThat(mIconView.getDrawable()).isInstanceOf(ShapeDrawable.class);
+    }
+
+    @Test
+    @DisableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+    public void setIcon_withPreloadedButFlagDisabled_ignoresPreloaded() {
+        Icon mockIcon = mock(Icon.class);
+        when(mockIcon.loadDrawableAsUser(any(), anyInt())).thenReturn(new ColorDrawable(1));
+        mStatusBarIcon.icon = mockIcon;
+        mStatusBarIcon.preloadedIcon = new ShapeDrawable();
+
+        mIconView.set(mStatusBarIcon);
+
+        assertThat(mIconView.getDrawable()).isNotNull();
+        assertThat(mIconView.getDrawable()).isInstanceOf(ColorDrawable.class);
+    }
+
+    @Test
     public void testUpdateIconScale_constrainedDrawableSizeLessThanDpIconSize() {
         int dpIconSize = 60;
         int dpDrawingSize = 30;
@@ -398,6 +432,32 @@
                 mIconView.getIconScale(), 0.01f);
     }
 
+    @Test
+    @EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+    public void set_iconThatWantsFixedSpace_setsScaleType() {
+        mIconView.setScaleType(ImageView.ScaleType.FIT_START);
+        StatusBarIcon icon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+                Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "",
+                StatusBarIcon.Type.SystemIcon, StatusBarIcon.Shape.FIXED_SPACE);
+
+        mIconView.set(icon);
+
+        assertThat(mIconView.getScaleType()).isEqualTo(ImageView.ScaleType.FIT_CENTER);
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS})
+    public void set_iconWithOtherShape_keepsScaleType() {
+        mIconView.setScaleType(ImageView.ScaleType.FIT_START);
+        StatusBarIcon icon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+                Icon.createWithResource(mContext, R.drawable.ic_android), 0, 0, "",
+                StatusBarIcon.Type.SystemIcon, StatusBarIcon.Shape.WRAP_CONTENT);
+
+        mIconView.set(icon);
+
+        assertThat(mIconView.getScaleType()).isEqualTo(ImageView.ScaleType.FIT_START);
+    }
+
     private static StatusBarNotification getMockSbn() {
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         when(sbn.getNotification()).thenReturn(mock(Notification.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
new file mode 100644
index 0000000..593f873
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
@@ -0,0 +1,173 @@
+/*
+ * 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
+
+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.internal.R
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
+import com.android.systemui.statusbar.connectivity.IconState
+import com.android.systemui.statusbar.connectivity.NetworkController
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy_Factory
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
+import com.android.systemui.statusbar.policy.SecurityController
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.verifyZeroInteractions
+import kotlin.test.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarSignalPolicyTest : SysuiTestCase() {
+    private val kosmos = Kosmos().also { it.testCase = this }
+
+    private lateinit var underTest: StatusBarSignalPolicy
+
+    private val testScope = TestScope()
+
+    private val javaAdapter = JavaAdapter(testScope.backgroundScope)
+    private val airplaneModeInteractor = kosmos.airplaneModeInteractor
+
+    private val securityController = mock<SecurityController>()
+    private val tunerService = mock<TunerService>()
+    private val statusBarIconController = mock<StatusBarIconController>()
+    private val networkController = mock<NetworkController>()
+    private val carrierConfigTracker = mock<CarrierConfigTracker>()
+
+    private var slotAirplane: String? = null
+
+    @Before
+    fun setup() {
+        underTest =
+            StatusBarSignalPolicy_Factory.newInstance(
+                mContext,
+                statusBarIconController,
+                carrierConfigTracker,
+                networkController,
+                securityController,
+                tunerService,
+                javaAdapter,
+                airplaneModeInteractor,
+            )
+
+        slotAirplane = mContext.getString(R.string.status_bar_airplane)
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagEnabled_iconUpdated() =
+        testScope.runTest {
+            underTest.start()
+            airplaneModeInteractor.setIsAirplaneMode(true)
+            runCurrent()
+            verify(statusBarIconController).setIconVisibility(slotAirplane, true)
+
+            airplaneModeInteractor.setIsAirplaneMode(false)
+            runCurrent()
+            verify(statusBarIconController).setIconVisibility(slotAirplane, false)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagEnabled_iconNotUpdated() =
+        testScope.runTest {
+            underTest.start()
+            runCurrent()
+            clearInvocations(statusBarIconController)
+
+            // Make sure the legacy code path does not change airplane mode when the refactor
+            // flag is enabled.
+            underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+            runCurrent()
+            verifyZeroInteractions(statusBarIconController)
+
+            underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+            runCurrent()
+            verifyZeroInteractions(statusBarIconController)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagEnabled_initNoOp() =
+        testScope.runTest {
+            // Make sure StatusBarSignalPolicy.init does no initialization when
+            // the refactor flag is disabled.
+            underTest.init()
+            verifyZeroInteractions(securityController, networkController, tunerService)
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagDisabled_iconUpdated() =
+        testScope.runTest {
+            underTest.init()
+
+            underTest.setIsAirplaneMode(IconState(true, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+            runCurrent()
+            verify(statusBarIconController).setIconVisibility(slotAirplane, true)
+
+            underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
+            runCurrent()
+            verify(statusBarIconController).setIconVisibility(slotAirplane, false)
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagDisabled_iconNotUpdated() =
+        testScope.runTest {
+            underTest.init()
+
+            // Make sure changing airplane mode from airplaneModeRepository does nothing
+            // if the StatusBarSignalPolicyRefactor is not enabled.
+            airplaneModeInteractor.setIsAirplaneMode(true)
+            runCurrent()
+            verifyZeroInteractions(statusBarIconController)
+
+            airplaneModeInteractor.setIsAirplaneMode(false)
+            runCurrent()
+            verifyZeroInteractions(statusBarIconController)
+        }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagDisabled_startNoOp() =
+        testScope.runTest {
+            // Make sure StatusBarSignalPolicy.start does no initialization when
+            // the refactor flag is disabled.
+            underTest.start()
+            verifyZeroInteractions(securityController, networkController, tunerService)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index ce79fbd..7bc6d4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -132,10 +132,10 @@
             )
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
-                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
             assertThat(icon.contentDescription).isNotNull()
@@ -170,10 +170,10 @@
             )
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
-                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
             assertThat(icon.contentDescription).isNotNull()
@@ -206,10 +206,10 @@
             repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null))
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
-                .isInstanceOf(OngoingActivityChipModel.ChipIcon.Basic::class.java)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
             assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index a8d2c5b..77992db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -127,7 +127,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
@@ -146,7 +146,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             assertThat((icon.contentDescription as ContentDescription.Resource).res)
@@ -184,7 +184,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.IconOnly::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             // This content description is just generic "Casting", not "Casting screen"
@@ -214,7 +214,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_cast_connected)
             // MediaProjection == screen casting, so this content description reflects that we're
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
new file mode 100644
index 0000000..118dea6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.ron.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.ui.model.ColorsModel
+import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.commandline.commandRegistry
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.mockito.kotlin.any
+import org.mockito.kotlin.whenever
+
+@SmallTest
+class DemoRonChipViewModelTest : 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
+
+    @Before
+    fun setUp() {
+        underTest.start()
+        whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
+            .thenReturn(BitmapDrawable())
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_flagOff_hidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            addDemoRonChip()
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_noPackage_hidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            commandRegistry.onShellCommand(pw, arrayOf("demo-ron"))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_hasPackage_shown() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_hasText_shownWithText() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            commandRegistry.onShellCommand(
+                pw,
+                arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test")
+            )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_supportsColor() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            commandRegistry.onShellCommand(
+                pw,
+                arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343")
+            )
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).colors)
+                .isInstanceOf(ColorsModel.Custom::class.java)
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun chip_hasHideArg_hidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            // First, show a chip
+            addDemoRonChip()
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+
+            // Then, hide the chip
+            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide"))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    private fun addDemoRonChip() {
+        Companion.addDemoRonChip(commandRegistry, pw)
+    }
+
+    companion object {
+        fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
+            commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui"))
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 804eb5c..16101bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -150,7 +150,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
             assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index a2ef599..791a21d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -135,7 +135,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
             assertThat(icon.contentDescription).isNotNull()
@@ -152,7 +152,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
             assertThat(icon.contentDescription).isNotNull()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
index a724cfaa..4977c548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/ChipTransitionHelperTest.kt
@@ -154,5 +154,7 @@
         }
 
     private fun createIcon(@DrawableRes drawable: Int) =
-        OngoingActivityChipModel.ChipIcon.Basic(Icon.Resource(drawable, contentDescription = null))
+        OngoingActivityChipModel.ChipIcon.SingleColorIcon(
+            Icon.Resource(drawable, contentDescription = null)
+        )
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 556ec6a..f528ebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -19,9 +19,12 @@
 import android.content.DialogInterface
 import android.content.packageManager
 import android.content.pm.PackageManager
+import android.graphics.drawable.BitmapDrawable
+import android.platform.test.annotations.EnableFlags
 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
@@ -36,8 +39,11 @@
 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.DemoRonChipViewModelTest.Companion.addDemoRonChip
+import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.commandline.commandRegistry
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -45,6 +51,8 @@
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
 import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -68,11 +76,14 @@
     private val kosmos = Kosmos().also { it.testCase = this }
     private val testScope = kosmos.testScope
     private val systemClock = kosmos.fakeSystemClock
+    private val commandRegistry = kosmos.commandRegistry
 
     private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
     private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
     private val callRepo = kosmos.ongoingCallRepository
 
+    private val pw = PrintWriter(StringWriter())
+
     private val mockSystemUIDialog = mock<SystemUIDialog>()
     private val chipBackgroundView = mock<ChipBackgroundContainer>()
     private val chipView =
@@ -90,72 +101,75 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
+        kosmos.demoRonChipViewModel.start()
+        whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
+            .thenReturn(BitmapDrawable())
     }
 
     @Test
-    fun chip_allHidden_hidden() =
+    fun primaryChip_allHidden_hidden() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
     @Test
-    fun chip_screenRecordShow_restHidden_screenRecordShown() =
+    fun primaryChip_screenRecordShow_restHidden_screenRecordShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsScreenRecordChip(latest)
         }
 
     @Test
-    fun chip_screenRecordShowAndCallShow_screenRecordShown() =
+    fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
 
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsScreenRecordChip(latest)
         }
 
     @Test
-    fun chip_screenRecordShowAndShareToAppShow_screenRecordShown() =
+    fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsScreenRecordChip(latest)
         }
 
     @Test
-    fun chip_shareToAppShowAndCallShow_shareToAppShown() =
+    fun primaryChip_shareToAppShowAndCallShow_shareToAppShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsShareToAppChip(latest)
         }
 
     @Test
-    fun chip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
+    fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.DoingNothing
             // MediaProjection covers both share-to-app and cast-to-other-device
@@ -163,21 +177,30 @@
 
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsCallChip(latest)
         }
 
     @Test
-    fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
-            // Start with just the lower priority call chip
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            // Start with just the lowest priority chip shown
+            addDemoRonChip(commandRegistry, pw)
+            // And everything else hidden
+            callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             screenRecordState.value = ScreenRecordModel.DoingNothing
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
+            assertIsDemoRonChip(latest)
+
+            // WHEN the higher priority call chip is added
+            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+
+            // THEN the higher priority call chip is used
             assertIsCallChip(latest)
 
             // WHEN the higher priority media projection chip is added
@@ -199,16 +222,17 @@
         }
 
     @Test
-    fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
+    @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS)
+    fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
         testScope.runTest {
             // WHEN all chips are active
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-
             callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            addDemoRonChip(commandRegistry, pw)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             // THEN the highest priority screen record is used
             assertIsScreenRecordChip(latest)
@@ -224,15 +248,21 @@
 
             // THEN the lower priority call is used
             assertIsCallChip(latest)
+
+            // WHEN the higher priority call is removed
+            callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+
+            // THEN the lower priority demo RON is used
+            assertIsDemoRonChip(latest)
         }
 
     /** Regression test for b/347726238. */
     @Test
-    fun chip_timerDoesNotResetAfterSubscribersRestart() =
+    fun primaryChip_timerDoesNotResetAfterSubscribersRestart() =
         testScope.runTest {
             var latest: OngoingActivityChipModel? = null
 
-            val job1 = underTest.chip.onEach { latest = it }.launchIn(this)
+            val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
 
             // Start a chip with a timer
             systemClock.setElapsedRealtime(1234)
@@ -249,7 +279,7 @@
             systemClock.setElapsedRealtime(5678)
 
             // WHEN we re-subscribe to the chip flow
-            val job2 = underTest.chip.onEach { latest = it }.launchIn(this)
+            val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
 
             runCurrent()
 
@@ -260,13 +290,13 @@
         }
 
     @Test
-    fun chip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
+    fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
         testScope.runTest {
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsScreenRecordChip(latest)
 
@@ -280,14 +310,14 @@
         }
 
     @Test
-    fun chip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
+    fun primaryChip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
         testScope.runTest {
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
             screenRecordState.value = ScreenRecordModel.DoingNothing
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            val latest by collectLastValue(underTest.chip)
+            val latest by collectLastValue(underTest.primaryChip)
 
             assertIsShareToAppChip(latest)
 
@@ -338,7 +368,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_screenrecord)
         }
@@ -347,7 +377,7 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
         }
@@ -356,9 +386,15 @@
             assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
-                        as OngoingActivityChipModel.ChipIcon.Basic)
+                        as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
         }
+
+        fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
index 8cf7473..1efb3f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
@@ -40,6 +40,15 @@
     }
 
     @Test
+    fun parseColor() {
+        assertThat(Type.Color.parseValue("#434343").isSuccess).isTrue()
+        assertThat(Type.Color.parseValue("#aa123456").isSuccess).isTrue()
+        assertThat(Type.Color.parseValue("red").isSuccess).isTrue()
+
+        assertThat(Type.Color.parseValue("not a color").isFailure).isTrue()
+    }
+
+    @Test
     fun mapToComplexType() {
         val parseSquare = Type.Int.map { Rect(it, it, it, it) }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
index 3abdf62..cb92b77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
@@ -91,7 +91,12 @@
         assertFalse(isExpandAnimationRunning!!)
 
         verify(headsUpManager)
-            .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */)
+            .removeNotification(
+                notificationKey,
+                /* releaseImmediately= */ true,
+                /* animate= */ true,
+                /* reason= */ "onIntentStarted(willAnimate=false)"
+            )
         verify(onFinishAnimationCallback).run()
     }
 
@@ -109,7 +114,12 @@
         assertFalse(isExpandAnimationRunning!!)
 
         verify(headsUpManager)
-            .removeNotification(notificationKey, true /* releaseImmediately */, true /* animate */)
+            .removeNotification(
+                notificationKey,
+                /* releaseImmediately= */ true,
+                /* animate= */ true,
+                /* reason= */ "onLaunchAnimationCancelled()"
+            )
         verify(onFinishAnimationCallback).run()
     }
 
@@ -127,7 +137,12 @@
         assertFalse(isExpandAnimationRunning!!)
 
         verify(headsUpManager)
-            .removeNotification(notificationKey, true /* releaseImmediately */, false /* animate */)
+            .removeNotification(
+                notificationKey,
+                /* releaseImmediately= */ true,
+                /* animate= */ false,
+                /* reason= */ "onLaunchAnimationEnd()"
+            )
         verify(onFinishAnimationCallback).run()
     }
 
@@ -161,12 +176,18 @@
         controller.onTransitionAnimationEnd(isExpandingFullyAbove = true)
 
         verify(headsUpManager)
-            .removeNotification(summary.key, true /* releaseImmediately */, false /* animate */)
+            .removeNotification(
+                summary.key,
+                /* releaseImmediately= */ true,
+                /* animate= */ false,
+                /* reason= */ "onLaunchAnimationEnd()"
+            )
         verify(headsUpManager, never())
             .removeNotification(
                 notification.entry.key,
-                true /* releaseImmediately */,
-                false /* animate */
+                /* releaseImmediately= */ true,
+                /* animate= */ false,
+                /* reason= */ "onLaunchAnimationEnd()"
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 8e9323f..b4f4138 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -108,30 +108,31 @@
     private val executor = FakeExecutor(systemClock)
     private val huns: ArrayList<NotificationEntry> = ArrayList()
     private lateinit var helper: NotificationGroupTestHelper
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         helper = NotificationGroupTestHelper(mContext)
-        coordinator = HeadsUpCoordinator(
-            logger,
-            systemClock,
-            headsUpManager,
-            headsUpViewBinder,
-            visualInterruptionDecisionProvider,
-            remoteInputManager,
-            launchFullScreenIntentProvider,
-            flags,
-            headerController,
-            executor)
+        coordinator =
+            HeadsUpCoordinator(
+                logger,
+                systemClock,
+                headsUpManager,
+                headsUpViewBinder,
+                visualInterruptionDecisionProvider,
+                remoteInputManager,
+                launchFullScreenIntentProvider,
+                flags,
+                headerController,
+                executor
+            )
         coordinator.attach(notifPipeline)
 
         // capture arguments:
         collectionListener = withArgCaptor {
             verify(notifPipeline).addCollectionListener(capture())
         }
-        notifPromoter = withArgCaptor {
-            verify(notifPipeline).addPromoter(capture())
-        }
+        notifPromoter = withArgCaptor { verify(notifPipeline).addPromoter(capture()) }
         notifLifetimeExtender = withArgCaptor {
             verify(notifPipeline).addNotificationLifetimeExtender(capture())
         }
@@ -141,9 +142,7 @@
         beforeFinalizeFilterListener = withArgCaptor {
             verify(notifPipeline).addOnBeforeFinalizeFilterListener(capture())
         }
-        onHeadsUpChangedListener = withArgCaptor {
-            verify(headsUpManager).addListener(capture())
-        }
+        onHeadsUpChangedListener = withArgCaptor { verify(headsUpManager).addListener(capture()) }
         actionPressListener = withArgCaptor {
             verify(remoteInputManager).addActionPressListener(capture())
         }
@@ -187,8 +186,8 @@
         assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
         executor.advanceClockToLast()
         executor.runAllReady()
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
-        verify(headsUpManager, times(1)).removeNotification(anyString(), eq(true))
+        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false), anyString())
+        verify(headsUpManager, times(1)).removeNotification(anyString(), eq(true), anyString())
     }
 
     @Test
@@ -203,8 +202,8 @@
         executor.advanceClockToLast()
         executor.runAllReady()
         assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false), anyString())
+        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true), anyString())
     }
 
     @Test
@@ -217,7 +216,7 @@
         notifLifetimeExtender.cancelLifetimeExtension(entry)
         executor.advanceClockToLast()
         executor.runAllReady()
-        verify(headsUpManager, times(0)).removeNotification(anyString(), any())
+        verify(headsUpManager, never()).removeNotification(anyString(), any(), anyString())
     }
 
     @Test
@@ -227,14 +226,14 @@
 
         whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
         whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
-        assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason = */ 0))
+        assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason= */ 0))
 
         actionPressListener.accept(entry)
         executor.runAllReady()
         verify(endLifetimeExtension, times(1)).onEndLifetimeExtension(notifLifetimeExtender, entry)
 
-        collectionListener.onEntryRemoved(entry, /* reason = */ 0)
-        verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
+        collectionListener.onEntryRemoved(entry, /* reason= */ 0)
+        verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any(), anyString())
     }
 
     @Test
@@ -248,8 +247,8 @@
         whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true)
         assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
 
-        collectionListener.onEntryRemoved(entry, /* reason = */ 0)
-        verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
+        collectionListener.onEntryRemoved(entry, /* reason= */ 0)
+        verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any(), anyString())
     }
 
     @Test
@@ -261,8 +260,8 @@
         addHUN(entry)
         executor.advanceClockToLast()
         executor.runAllReady()
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(false))
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+        verify(headsUpManager, never()).removeNotification(anyString(), eq(false), anyString())
+        verify(headsUpManager, never()).removeNotification(anyString(), eq(true), anyString())
     }
 
     @Test
@@ -273,8 +272,8 @@
         assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
         executor.advanceClockToLast()
         executor.runAllReady()
-        verify(headsUpManager, times(1)).removeNotification(anyString(), eq(false))
-        verify(headsUpManager, times(0)).removeNotification(anyString(), eq(true))
+        verify(headsUpManager, times(1)).removeNotification(anyString(), eq(false), anyString())
+        verify(headsUpManager, never()).removeNotification(anyString(), eq(true), anyString())
     }
 
     @Test
@@ -326,9 +325,8 @@
 
         // THEN only promote the current HUN, mEntry
         assertTrue(notifPromoter.shouldPromoteToTopLevel(entry))
-        assertFalse(notifPromoter.shouldPromoteToTopLevel(NotificationEntryBuilder()
-            .setPkg("test-package2")
-            .build()))
+        val testPackage2 = NotificationEntryBuilder().setPkg("test-package2").build()
+        assertFalse(notifPromoter.shouldPromoteToTopLevel(testPackage2))
     }
 
     @Test
@@ -338,9 +336,9 @@
 
         // THEN only section the current HUN, mEntry
         assertTrue(notifSectioner.isInSection(entry))
-        assertFalse(notifSectioner.isInSection(NotificationEntryBuilder()
-            .setPkg("test-package")
-            .build()))
+        assertFalse(
+            notifSectioner.isInSection(NotificationEntryBuilder().setPkg("test-package").build())
+        )
     }
 
     @Test
@@ -350,10 +348,12 @@
 
         // THEN only the current HUN, mEntry, should be lifetimeExtended
         assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* cancellationReason */ 0))
-        assertFalse(notifLifetimeExtender.maybeExtendLifetime(
-            NotificationEntryBuilder()
-                .setPkg("test-package")
-                .build(), /* cancellationReason */ 0))
+        assertFalse(
+            notifLifetimeExtender.maybeExtendLifetime(
+                NotificationEntryBuilder().setPkg("test-package").build(),
+                /* reason= */ 0
+            )
+        )
     }
 
     @Test
@@ -366,8 +366,9 @@
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry))
         verify(headsUpManager, never()).showNotification(entry)
         withArgCaptor<BindCallback> {
-            verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
-        }.onBindFinished(entry)
+                verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+            }
+            .onBindFinished(entry)
 
         // THEN we tell the HeadsUpManager to show the notification
         verify(headsUpManager).showNotification(entry)
@@ -430,7 +431,7 @@
         whenever(remoteInputManager.isSpinning(any())).thenReturn(false)
 
         // THEN heads up manager should remove the entry
-        verify(headsUpManager).removeNotification(entry.key, false)
+        verify(headsUpManager).removeNotification(eq(entry.key), eq(false), anyString())
     }
 
     private fun addHUN(entry: NotificationEntry) {
@@ -545,19 +546,22 @@
         collectionListener.onEntryAdded(groupSibling1)
         collectionListener.onEntryAdded(groupSibling2)
 
-        val beforeTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
-            .build()
+        val beforeTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
 
-        val afterTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupSibling2))
-            .build()
-        beforeFinalizeFilterListener
-            .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+        val afterTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupSibling2))
+                .build()
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+            listOf(groupPriority, afterTransformGroup)
+        )
 
         verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
         finishBind(groupPriority)
@@ -583,19 +587,22 @@
         collectionListener.onEntryUpdated(groupSibling1)
         collectionListener.onEntryUpdated(groupSibling2)
 
-        val beforeTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
-            .build()
+        val beforeTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
 
-        val afterTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupSibling2))
-            .build()
-        beforeFinalizeFilterListener
-            .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+        val afterTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupSibling2))
+                .build()
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+            listOf(groupPriority, afterTransformGroup)
+        )
 
         verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
         finishBind(groupPriority)
@@ -618,19 +625,22 @@
         collectionListener.onEntryUpdated(groupSummary)
         collectionListener.onEntryUpdated(groupPriority)
 
-        val beforeTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
-            .build()
+        val beforeTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
 
-        val afterTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupSibling2))
-            .build()
-        beforeFinalizeFilterListener
-            .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+        val afterTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupSibling2))
+                .build()
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+            listOf(groupPriority, afterTransformGroup)
+        )
 
         verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupSummary), any())
         finishBind(groupPriority)
@@ -654,19 +664,22 @@
         collectionListener.onEntryUpdated(groupSibling1)
         collectionListener.onEntryUpdated(groupSibling2)
 
-        val beforeTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
-            .build()
+        val beforeTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupPriority, groupSibling2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(beforeTransformGroup))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
 
-        val afterTransformGroup = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupSibling2))
-            .build()
-        beforeFinalizeFilterListener
-            .onBeforeFinalizeFilter(listOf(groupPriority, afterTransformGroup))
+        val afterTransformGroup =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupSibling2))
+                .build()
+        beforeFinalizeFilterListener.onBeforeFinalizeFilter(
+            listOf(groupPriority, afterTransformGroup)
+        )
 
         finishBind(groupSummary)
         verify(headsUpViewBinder, never()).bindHeadsUpView(eq(groupPriority), any())
@@ -688,10 +701,11 @@
         collectionListener.onEntryAdded(groupSummary)
         collectionListener.onEntryAdded(groupSibling1)
         collectionListener.onEntryAdded(groupSibling2)
-        val groupEntry = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupSibling1, groupSibling2))
-            .build()
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupSibling1, groupSibling2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -708,16 +722,16 @@
     @Test
     fun testNoTransferTwoChildAlert_withGroupAlertAll() {
         setShouldHeadsUp(groupSummary)
-        whenever(notifPipeline.allNotifs)
-            .thenReturn(listOf(groupSummary, groupChild1, groupChild2))
+        whenever(notifPipeline.allNotifs).thenReturn(listOf(groupSummary, groupChild1, groupChild2))
 
         collectionListener.onEntryAdded(groupSummary)
         collectionListener.onEntryAdded(groupChild1)
         collectionListener.onEntryAdded(groupChild2)
-        val groupEntry = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupChild1, groupChild2))
-            .build()
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupChild1, groupChild2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -742,10 +756,11 @@
         collectionListener.onEntryAdded(groupSummary)
         collectionListener.onEntryAdded(groupChild1)
         collectionListener.onEntryAdded(groupChild2)
-        val groupEntry = GroupEntryBuilder()
-            .setSummary(groupSummary)
-            .setChildren(listOf(groupChild1, groupChild2))
-            .build()
+        val groupEntry =
+            GroupEntryBuilder()
+                .setSummary(groupSummary)
+                .setChildren(listOf(groupChild1, groupChild2))
+                .build()
         beforeTransformGroupsListener.onBeforeTransformGroups(listOf(groupEntry))
         verify(headsUpViewBinder, never()).bindHeadsUpView(any(), any())
         beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(groupEntry))
@@ -1045,9 +1060,7 @@
             .thenReturn(DecisionImpl.of(should))
     }
 
-    private fun setDefaultShouldFullScreen(
-        originalDecision: FullScreenIntentDecision
-    ) {
+    private fun setDefaultShouldFullScreen(originalDecision: FullScreenIntentDecision) {
         val provider = visualInterruptionDecisionProvider
         whenever(provider.makeUnloggedFullScreenIntentDecision(any())).thenAnswer {
             val entry: NotificationEntry = it.getArgument(0)
@@ -1059,11 +1072,8 @@
         entry: NotificationEntry,
         originalDecision: FullScreenIntentDecision
     ) {
-        whenever(
-            visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry)
-        ).thenAnswer {
-            FullScreenIntentDecisionImpl(entry, originalDecision)
-        }
+        whenever(visualInterruptionDecisionProvider.makeUnloggedFullScreenIntentDecision(entry))
+            .thenAnswer { FullScreenIntentDecisionImpl(entry, originalDecision) }
     }
 
     private fun verifyLoggedFullScreenIntentDecision(
@@ -1089,7 +1099,8 @@
     private fun finishBind(entry: NotificationEntry) {
         verify(headsUpManager, never()).showNotification(entry)
         withArgCaptor<BindCallback> {
-            verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
-        }.onBindFinished(entry)
+                verify(headsUpViewBinder).bindHeadsUpView(eq(entry), capture())
+            }
+            .onBindFinished(entry)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index ad6aca1..3c583f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor
 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
@@ -45,8 +46,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -66,6 +67,7 @@
         SensitiveNotificationProtectionController
     @Mock private lateinit var stackController: NotifStackController
     @Mock private lateinit var section: NotifSection
+    @Mock private lateinit var row: ExpandableNotificationRow
 
     @Before
     fun setUp() {
@@ -74,6 +76,8 @@
         whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(false)
 
         entry = NotificationEntryBuilder().setSection(section).build()
+        entry.row = row
+        entry.setSensitive(false, false)
         coordinator =
             StackCoordinator(
                 groupExpansionManagerImpl,
@@ -189,4 +193,17 @@
             .setNotifStats(NotifStats(1, false, false, true, false))
         verifyZeroInteractions(stackController)
     }
+
+    @Test
+    @EnableFlags(
+        FooterViewRefactor.FLAG_NAME
+    )
+    fun testSetNotificationStats_footerFlagOn_nonClearableRedacted() {
+        entry.setSensitive(true, true)
+        whenever(section.bucket).thenReturn(BUCKET_ALERTING)
+        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+        verify(activeNotificationsInteractor)
+            .setNotifStats(NotifStats(1, hasNonClearableAlertingNotifs = true, false, false, false))
+        verifyZeroInteractions(stackController)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index d1b1f46..ed99705 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -98,16 +98,20 @@
     // instead of VisualInterruptionDecisionProviderTestBase
     // because avalanche code is based on the suppression refactor.
 
+    private fun getAvalancheSuppressor() : AvalancheSuppressor {
+        return AvalancheSuppressor(
+            avalancheProvider, systemClock, settingsInteractor, packageManager,
+            uiEventLogger, context, notificationManager, logger
+        )
+    }
+
     @Test
     fun testAvalancheFilter_suppress_hasNotSeenEdu_showEduHun() {
         setAllowedEmergencyPkg(false)
         whenever(avalancheProvider.timeoutMs).thenReturn(20)
         whenever(avalancheProvider.startTime).thenReturn(whenAgo(10))
 
-        val avalancheSuppressor = AvalancheSuppressor(
-            avalancheProvider, systemClock, settingsInteractor, packageManager,
-            uiEventLogger, context, notificationManager
-        )
+        val avalancheSuppressor = getAvalancheSuppressor()
         avalancheSuppressor.hasSeenEdu = false
 
         withFilter(avalancheSuppressor) {
@@ -128,10 +132,7 @@
         whenever(avalancheProvider.timeoutMs).thenReturn(20)
         whenever(avalancheProvider.startTime).thenReturn(whenAgo(10))
 
-        val avalancheSuppressor = AvalancheSuppressor(
-            avalancheProvider, systemClock, settingsInteractor, packageManager,
-            uiEventLogger, context, notificationManager
-        )
+        val avalancheSuppressor = getAvalancheSuppressor()
         avalancheSuppressor.hasSeenEdu = true
 
         withFilter(avalancheSuppressor) {
@@ -151,8 +152,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -171,8 +171,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldNotHeadsUp(
@@ -191,8 +190,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -209,8 +207,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -227,8 +224,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -245,8 +241,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -263,8 +258,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -281,8 +275,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -300,8 +293,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -318,8 +310,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             assertFsiNotSuppressed()
         }
@@ -330,8 +321,7 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -359,8 +349,7 @@
         setAllowedEmergencyPkg(true)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, settingsInteractor, packageManager,
-                    uiEventLogger, context, notificationManager)
+            getAvalancheSuppressor()
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 9d3d9c1..284efc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -139,6 +139,7 @@
     protected val settingsInteractor: NotificationSettingsInteractor = mock()
     protected val packageManager: PackageManager = mock()
     protected val notificationManager: NotificationManager = mock()
+    protected val logger: VisualInterruptionDecisionLogger = mock()
     protected abstract val provider: VisualInterruptionDecisionProvider
 
     private val neverSuppresses = object : NotificationInterruptSuppressor {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 491919a..30a1214 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -35,6 +35,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView
+import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -74,6 +77,7 @@
 import org.mockito.kotlin.spy
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -125,8 +129,10 @@
             ): RichOngoingContentModel? = fakeRonContentModel
         }
 
-    private var fakeRonViewHolder: InflatedContentViewHolder? = null
-    private val fakeRonViewInflater =
+    private var fakeContractedRonViewHolder: ContentViewInflationResult = NullContentView
+    private var fakeExpandedRonViewHolder: ContentViewInflationResult = NullContentView
+    private var fakeHeadsUpRonViewHolder: ContentViewInflationResult = NullContentView
+    private var fakeRonViewInflater =
         spy(
             object : RichOngoingNotificationViewInflater {
                 override fun inflateView(
@@ -134,8 +140,20 @@
                     existingView: View?,
                     entry: NotificationEntry,
                     systemUiContext: Context,
-                    parentView: ViewGroup
-                ): InflatedContentViewHolder? = fakeRonViewHolder
+                    parentView: ViewGroup,
+                    viewType: RichOngoingNotificationViewType
+                ): ContentViewInflationResult =
+                    when (viewType) {
+                        RichOngoingNotificationViewType.Contracted -> fakeContractedRonViewHolder
+                        RichOngoingNotificationViewType.Expanded -> fakeExpandedRonViewHolder
+                        RichOngoingNotificationViewType.HeadsUp -> fakeHeadsUpRonViewHolder
+                    }
+
+                override fun canKeepView(
+                    contentModel: RichOngoingContentModel,
+                    existingView: View?,
+                    viewType: RichOngoingNotificationViewType
+                ): Boolean = false
             }
         )
 
@@ -149,6 +167,7 @@
                 .setContentText("Text")
                 .setStyle(Notification.BigTextStyle().bigText("big text"))
         testHelper = NotificationTestHelper(mContext, mDependency)
+        testHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL)
         row = spy(testHelper.createRow(builder.build()))
         notificationInflater =
             NotificationRowContentBinderImpl(
@@ -388,15 +407,62 @@
     @Test
     fun testRonModelRequiredForRonView() {
         fakeRonContentModel = null
-        val ronView = View(context)
-        fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mock())
+        val contractedRonView = View(context)
+        val expandedRonView = View(context)
+        val headsUpRonView = View(context)
+        fakeContractedRonViewHolder =
+            InflatedContentViewHolder(view = contractedRonView, binder = mock())
+        fakeExpandedRonViewHolder =
+            InflatedContentViewHolder(view = expandedRonView, binder = mock())
+        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = headsUpRonView, binder = mock())
+
         // WHEN inflater inflates
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
-        verify(fakeRonViewInflater, never()).inflateView(any(), any(), any(), any(), any())
+        val contentToInflate =
+            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
+        inflateAndWait(notificationInflater, contentToInflate, row)
+        verifyZeroInteractions(fakeRonViewInflater)
     }
 
     @Test
-    fun testRonModelTriggersInflationOfRonView() {
+    fun testRonModelCleansUpRemoteViews() {
+        val ronView = View(context)
+
+        val entry = row.entry
+
+        fakeRonContentModel = mock<TimerContentModel>()
+        fakeContractedRonViewHolder =
+            InflatedContentViewHolder(view = ronView, binder = mock<DeferredContentViewBinder>())
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+        // VERIFY
+        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_CONTRACTED))
+        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_EXPANDED))
+        verify(cache).removeCachedView(eq(entry), eq(FLAG_CONTENT_VIEW_HEADS_UP))
+    }
+
+    @Test
+    fun testRonModelCleansUpSmartReplies() {
+        val ronView = View(context)
+
+        val privateLayout = spy(row.privateLayout)
+
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mock<TimerContentModel>()
+        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mock())
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+        // VERIFY
+        verify(privateLayout).setExpandedInflatedSmartReplies(eq(null))
+        verify(privateLayout).setHeadsUpInflatedSmartReplies(eq(null))
+    }
+
+    @Test
+    fun testRonModelTriggersInflationOfContractedRonView() {
         val mockRonModel = mock<TimerContentModel>()
         val ronView = View(context)
         val mockBinder = mock<DeferredContentViewBinder>()
@@ -405,18 +471,229 @@
         val privateLayout = row.privateLayout
 
         fakeRonContentModel = mockRonModel
-        fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
         // WHEN inflater inflates
         inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
         // VERIFY that the inflater is invoked
         verify(fakeRonViewInflater)
-            .inflateView(eq(mockRonModel), any(), eq(entry), any(), eq(privateLayout))
+            .inflateView(
+                eq(mockRonModel),
+                any(),
+                eq(entry),
+                any(),
+                eq(privateLayout),
+                eq(RichOngoingNotificationViewType.Contracted)
+            )
         assertThat(row.privateLayout.contractedChild).isSameInstanceAs(ronView)
         verify(mockBinder).setupContentViewBinder()
     }
 
     @Test
-    fun ronViewAppliesElementsInOrder() {
+    fun testRonModelTriggersInflationOfExpandedRonView() {
+        val mockRonModel = mock<TimerContentModel>()
+        val ronView = View(context)
+        val mockBinder = mock<DeferredContentViewBinder>()
+
+        val entry = row.entry
+        val privateLayout = row.privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeExpandedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
+
+        // VERIFY that the inflater is invoked
+        verify(fakeRonViewInflater)
+            .inflateView(
+                eq(mockRonModel),
+                any(),
+                eq(entry),
+                any(),
+                eq(privateLayout),
+                eq(RichOngoingNotificationViewType.Expanded)
+            )
+        assertThat(row.privateLayout.expandedChild).isSameInstanceAs(ronView)
+        verify(mockBinder).setupContentViewBinder()
+    }
+
+    @Test
+    fun testRonModelTriggersInflationOfHeadsUpRonView() {
+        val mockRonModel = mock<TimerContentModel>()
+        val ronView = View(context)
+        val mockBinder = mock<DeferredContentViewBinder>()
+
+        val entry = row.entry
+        val privateLayout = row.privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
+
+        // VERIFY that the inflater is invoked
+        verify(fakeRonViewInflater)
+            .inflateView(
+                eq(mockRonModel),
+                any(),
+                eq(entry),
+                any(),
+                eq(privateLayout),
+                eq(RichOngoingNotificationViewType.HeadsUp)
+            )
+        assertThat(row.privateLayout.headsUpChild).isSameInstanceAs(ronView)
+        verify(mockBinder).setupContentViewBinder()
+    }
+
+    @Test
+    fun keepExistingViewForContractedRonNotChangingContractedChild() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mContractedBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeContractedRonViewHolder = KeepExistingView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+        // THEN  do not dispose old contracted binder handle and change contracted child
+        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+        verifyZeroInteractions(oldHandle)
+        verify(privateLayout, never()).setContractedChild(any())
+    }
+
+    @Test
+    fun keepExistingViewForExpandedRonNotChangingExpandedChild() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mExpandedBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeExpandedRonViewHolder = KeepExistingView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
+
+        // THEN  do not dispose old expanded binder handle and change expanded child
+        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+        verifyZeroInteractions(oldHandle)
+        verify(privateLayout, never()).setExpandedChild(any())
+    }
+
+    @Test
+    fun keepExistingViewForHeadsUpRonNotChangingHeadsUpChild() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mHeadsUpBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeHeadsUpRonViewHolder = KeepExistingView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
+
+        // THEN - do not dispose old heads up binder handle and change heads up child
+        verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+        verifyZeroInteractions(oldHandle)
+        verify(privateLayout, never()).setHeadsUpChild(any())
+    }
+
+    @Test
+    fun nullContentViewForContractedRonAppliesElementsInOrder() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mContractedBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeContractedRonViewHolder = NullContentView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+
+        // Validate that these 4 steps happen in this precise order
+        inOrder(oldHandle, entry, privateLayout, cache) {
+            verify(oldHandle).dispose()
+            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+            verify(privateLayout).setContractedChild(eq(null))
+        }
+    }
+
+    @Test
+    fun nullContentViewForExpandedRonAppliesElementsInOrder() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mExpandedBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeExpandedRonViewHolder = NullContentView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
+
+        // Validate that these 4 steps happen in this precise order
+        inOrder(oldHandle, entry, privateLayout, cache) {
+            verify(oldHandle).dispose()
+            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+            verify(privateLayout).setExpandedChild(eq(null))
+        }
+    }
+
+    @Test
+    fun nullContentViewForHeadsUpRonAppliesElementsInOrder() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+
+        row.privateLayout.mHeadsUpBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeHeadsUpRonViewHolder = NullContentView
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
+
+        // Validate that these 4 steps happen in this precise order
+        inOrder(oldHandle, entry, privateLayout, cache) {
+            verify(oldHandle).dispose()
+            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+            verify(privateLayout).setHeadsUpChild(eq(null))
+        }
+    }
+
+    @Test
+    fun contractedRonViewAppliesElementsInOrder() {
         val oldHandle = mock<DisposableHandle>()
         val mockRonModel = mock<TimerContentModel>()
         val ronView = View(context)
@@ -429,7 +706,8 @@
         row.privateLayout = privateLayout
 
         fakeRonContentModel = mockRonModel
-        fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+        fakeContractedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
         // WHEN inflater inflates
         inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
 
@@ -443,16 +721,89 @@
     }
 
     @Test
-    fun testRonNotReinflating() {
-        val handle0 = mock<DisposableHandle>()
-        val handle1 = mock<DisposableHandle>()
+    fun expandedRonViewAppliesElementsInOrder() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
         val ronView = View(context)
+        val mockBinder = mock<DeferredContentViewBinder>()
+
+        row.privateLayout.mExpandedBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeExpandedRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_EXPANDED, row)
+
+        // Validate that these 4 steps happen in this precise order
+        inOrder(oldHandle, entry, privateLayout, mockBinder) {
+            verify(oldHandle).dispose()
+            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+            verify(privateLayout).setExpandedChild(eq(ronView))
+            verify(mockBinder).setupContentViewBinder()
+        }
+    }
+
+    @Test
+    fun headsUpRonViewAppliesElementsInOrder() {
+        val oldHandle = mock<DisposableHandle>()
+        val mockRonModel = mock<TimerContentModel>()
+        val ronView = View(context)
+        val mockBinder = mock<DeferredContentViewBinder>()
+
+        row.privateLayout.mHeadsUpBinderHandle = oldHandle
+        val entry = spy(row.entry)
+        row.entry = entry
+        val privateLayout = spy(row.privateLayout)
+        row.privateLayout = privateLayout
+
+        fakeRonContentModel = mockRonModel
+        fakeHeadsUpRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder)
+
+        // WHEN inflater inflates
+        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_HEADS_UP, row)
+
+        // Validate that these 4 steps happen in this precise order
+        inOrder(oldHandle, entry, privateLayout, mockBinder) {
+            verify(oldHandle).dispose()
+            verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel })
+            verify(privateLayout).setHeadsUpChild(eq(ronView))
+            verify(mockBinder).setupContentViewBinder()
+        }
+    }
+
+    @Test
+    fun testRonNotReinflating() {
+        val oldContractedBinderHandle = mock<DisposableHandle>()
+        val oldExpandedBinderHandle = mock<DisposableHandle>()
+        val oldHeadsUpBinderHandle = mock<DisposableHandle>()
+
+        val contractedBinderHandle = mock<DisposableHandle>()
+        val expandedBinderHandle = mock<DisposableHandle>()
+        val headsUpBinderHandle = mock<DisposableHandle>()
+
+        val contractedRonView = View(context)
+        val expandedRonView = View(context)
+        val headsUpRonView = View(context)
+
         val mockRonModel1 = mock<TimerContentModel>()
         val mockRonModel2 = mock<TimerContentModel>()
-        val mockBinder1 = mock<DeferredContentViewBinder>()
-        doReturn(handle1).whenever(mockBinder1).setupContentViewBinder()
 
-        row.privateLayout.mContractedBinderHandle = handle0
+        val mockContractedViewBinder = mock<DeferredContentViewBinder>()
+        val mockExpandedViewBinder = mock<DeferredContentViewBinder>()
+        val mockHeadsUpViewBinder = mock<DeferredContentViewBinder>()
+
+        doReturn(contractedBinderHandle).whenever(mockContractedViewBinder).setupContentViewBinder()
+        doReturn(expandedBinderHandle).whenever(mockExpandedViewBinder).setupContentViewBinder()
+        doReturn(headsUpBinderHandle).whenever(mockHeadsUpViewBinder).setupContentViewBinder()
+
+        row.privateLayout.mContractedBinderHandle = oldContractedBinderHandle
+        row.privateLayout.mExpandedBinderHandle = oldExpandedBinderHandle
+        row.privateLayout.mHeadsUpBinderHandle = oldHeadsUpBinderHandle
         val entry = spy(row.entry)
         row.entry = entry
         val privateLayout = spy(row.privateLayout)
@@ -460,31 +811,87 @@
 
         // WHEN inflater inflates both a model and a view
         fakeRonContentModel = mockRonModel1
-        fakeRonViewHolder = InflatedContentViewHolder(view = ronView, binder = mockBinder1)
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+        fakeContractedRonViewHolder =
+            InflatedContentViewHolder(view = contractedRonView, binder = mockContractedViewBinder)
+        fakeExpandedRonViewHolder =
+            InflatedContentViewHolder(view = expandedRonView, binder = mockExpandedViewBinder)
+        fakeHeadsUpRonViewHolder =
+            InflatedContentViewHolder(view = headsUpRonView, binder = mockHeadsUpViewBinder)
+
+        val contentToInflate =
+            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
+        inflateAndWait(notificationInflater, contentToInflate, row)
 
         // Validate that these 4 steps happen in this precise order
-        inOrder(handle0, entry, privateLayout, mockBinder1, handle1) {
-            verify(handle0).dispose()
+        inOrder(
+            oldContractedBinderHandle,
+            oldExpandedBinderHandle,
+            oldHeadsUpBinderHandle,
+            entry,
+            privateLayout,
+            mockContractedViewBinder,
+            mockExpandedViewBinder,
+            mockHeadsUpViewBinder,
+            contractedBinderHandle,
+            expandedBinderHandle,
+            headsUpBinderHandle
+        ) {
+            verify(oldContractedBinderHandle).dispose()
+            verify(oldExpandedBinderHandle).dispose()
+            verify(oldHeadsUpBinderHandle).dispose()
+
             verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel1 })
-            verify(privateLayout).setContractedChild(eq(ronView))
-            verify(mockBinder1).setupContentViewBinder()
-            verify(handle1, never()).dispose()
+
+            verify(privateLayout).setContractedChild(eq(contractedRonView))
+            verify(mockContractedViewBinder).setupContentViewBinder()
+
+            verify(privateLayout).setExpandedChild(eq(expandedRonView))
+            verify(mockExpandedViewBinder).setupContentViewBinder()
+
+            verify(privateLayout).setHeadsUpChild(eq(headsUpRonView))
+            verify(mockHeadsUpViewBinder).setupContentViewBinder()
+
+            verify(contractedBinderHandle, never()).dispose()
+            verify(expandedBinderHandle, never()).dispose()
+            verify(headsUpBinderHandle, never()).dispose()
         }
 
-        clearInvocations(handle0, entry, privateLayout, mockBinder1, handle1)
+        clearInvocations(
+            oldContractedBinderHandle,
+            oldExpandedBinderHandle,
+            oldHeadsUpBinderHandle,
+            entry,
+            privateLayout,
+            mockContractedViewBinder,
+            mockExpandedViewBinder,
+            mockHeadsUpViewBinder,
+            contractedBinderHandle,
+            expandedBinderHandle,
+            headsUpBinderHandle
+        )
 
         // THEN when the inflater inflates just a model
         fakeRonContentModel = mockRonModel2
-        fakeRonViewHolder = null
-        inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_CONTRACTED, row)
+        fakeContractedRonViewHolder = KeepExistingView
+        fakeExpandedRonViewHolder = KeepExistingView
+        fakeHeadsUpRonViewHolder = KeepExistingView
+
+        inflateAndWait(notificationInflater, contentToInflate, row)
 
         // Validate that for reinflation, the only thing we do us update the model
-        verify(handle1, never()).dispose()
+        verify(contractedBinderHandle, never()).dispose()
+        verify(expandedBinderHandle, never()).dispose()
+        verify(headsUpBinderHandle, never()).dispose()
         verify(entry).setContentModel(argThat { richOngoingContentModel === mockRonModel2 })
         verify(privateLayout, never()).setContractedChild(any())
-        verify(mockBinder1, never()).setupContentViewBinder()
-        verify(handle1, never()).dispose()
+        verify(privateLayout, never()).setExpandedChild(any())
+        verify(privateLayout, never()).setHeadsUpChild(any())
+        verify(mockContractedViewBinder, never()).setupContentViewBinder()
+        verify(mockExpandedViewBinder, never()).setupContentViewBinder()
+        verify(mockHeadsUpViewBinder, never()).setupContentViewBinder()
+        verify(contractedBinderHandle, never()).dispose()
+        verify(expandedBinderHandle, never()).dispose()
+        verify(headsUpBinderHandle, never()).dispose()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index e4945fc..1a1af2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -342,7 +342,7 @@
         val stackTop = 200f
         val stackHeight = 800f
         whenever(ambientState.stackTop).thenReturn(stackTop)
-        whenever(ambientState.stackHeight).thenReturn(stackHeight)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
         val shelfTop = stackTop + stackHeight - shelf.height
         val stackScrollAlgorithmState = StackScrollAlgorithmState()
         val viewInShelf = mock(ExpandableView::class.java)
@@ -378,7 +378,7 @@
         val stackTop = 200f
         val stackHeight = 800f
         whenever(ambientState.stackTop).thenReturn(stackTop)
-        whenever(ambientState.stackHeight).thenReturn(stackHeight)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -404,7 +404,7 @@
     fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
-        whenever(ambientState.stackHeight).thenReturn(100f)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         val endOfStack = 200f + paddingBetweenElements
@@ -433,7 +433,7 @@
         val stackTop = 200f
         val stackHeight = 800f
         whenever(ambientState.stackTop).thenReturn(stackTop)
-        whenever(ambientState.stackHeight).thenReturn(stackHeight)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -459,7 +459,7 @@
     fun updateState_withNullFirstViewInShelf_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
-        whenever(ambientState.stackHeight).thenReturn(100f)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         val endOfStack = 200f + paddingBetweenElements
@@ -488,7 +488,7 @@
         val stackTop = 200f
         val stackHeight = 800f
         whenever(ambientState.stackTop).thenReturn(stackTop)
-        whenever(ambientState.stackHeight).thenReturn(stackHeight)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         val lastVisibleBackgroundChild = mock<ExpandableView>()
@@ -514,7 +514,7 @@
     fun updateState_withCollapsedShade_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
-        whenever(ambientState.stackHeight).thenReturn(100f)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         val endOfStack = 200f + paddingBetweenElements
@@ -543,7 +543,7 @@
         val stackTop = 200f
         val stackHeight = 800f
         whenever(ambientState.stackTop).thenReturn(stackTop)
-        whenever(ambientState.stackHeight).thenReturn(stackHeight)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(stackHeight)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         whenever(ambientState.isShadeExpanded).thenReturn(true)
@@ -583,7 +583,7 @@
     fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
         // GIVEN
         whenever(ambientState.stackY).thenReturn(100f)
-        whenever(ambientState.stackHeight).thenReturn(100f)
+        whenever(ambientState.interpolatedStackHeight).thenReturn(100f)
         val paddingBetweenElements =
             context.resources.getDimensionPixelSize(R.dimen.notification_divider_height)
         val endOfStack = 200f + paddingBetweenElements
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 22b9887..a06f4d2 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
@@ -95,7 +95,8 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
+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.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -246,26 +247,12 @@
         when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
                 .thenReturn((float) stackHeight);
 
-        mStackScroller.updateContentHeight();
+        mStackScroller.updateStackHeight();
 
         assertThat(mStackScroller.getIntrinsicStackHeight()).isEqualTo(stackHeight);
     }
 
     @Test
-    @DisableSceneContainer
-    public void testIntrinsicStackHeight_includesTopScrimPadding() {
-        int stackHeight = 300;
-        int topScrimPadding = px(R.dimen.notification_side_paddings);
-        when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
-                .thenReturn((float) stackHeight);
-
-        mStackScroller.updateContentHeight();
-
-        assertThat(mStackScroller.getIntrinsicStackHeight())
-                .isEqualTo(stackHeight + topScrimPadding);
-    }
-
-    @Test
     @DisableSceneContainer // TODO(b/312473478): address disabled test
     public void testUpdateStackHeight_qsExpansionZero() {
         final float expansionFraction = 0.2f;
@@ -290,8 +277,8 @@
                 endHeight * StackScrollAlgorithm.START_FRACTION,
                 endHeight, expansionFraction);
 
-        mStackScroller.updateStackHeight(endHeight, expansionFraction);
-        assertThat(mAmbientState.getStackHeight()).isEqualTo(expected);
+        mStackScroller.updateInterpolatedStackHeight(endHeight, expansionFraction);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(expected);
     }
 
     @Test
@@ -310,7 +297,7 @@
 
         // THEN stackHeight and stackEndHeight are the same
         verify(mAmbientState).setStackEndHeight(stackEndHeight);
-        verify(mAmbientState).setStackHeight(stackEndHeight);
+        verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight);
     }
 
     @Test
@@ -330,7 +317,7 @@
 
         // THEN stackHeight is changed by the expansion frac
         verify(mAmbientState).setStackEndHeight(stackEndHeight);
-        verify(mAmbientState).setStackHeight(stackEndHeight * 0.75f);
+        verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight * 0.75f);
     }
 
     @Test
@@ -350,7 +337,21 @@
 
         // THEN stackHeight is measured from the stack top
         verify(mAmbientState).setStackEndHeight(stackEndHeight);
-        verify(mAmbientState).setStackHeight(stackEndHeight);
+        verify(mAmbientState).setInterpolatedStackHeight(stackEndHeight);
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void updateStackEndHeightAndStackHeight_maxNotificationsSet_withSceneContainer() {
+        float stackHeight = 300f;
+        when(mStackSizeCalculator.computeHeight(eq(mStackScroller), anyInt(), anyFloat()))
+                .thenReturn(stackHeight);
+        mStackScroller.setMaxDisplayedNotifications(3); // any non-zero amount
+
+        clearInvocations(mAmbientState);
+        mStackScroller.updateStackEndHeightAndStackHeight(1f);
+
+        verify(mAmbientState).setInterpolatedStackHeight(eq(300f));
     }
 
     @Test
@@ -363,7 +364,7 @@
         clearInvocations(mAmbientState);
         mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
         verify(mAmbientState, never()).setStackEndHeight(anyFloat());
-        verify(mAmbientState).setStackHeight(anyFloat());
+        verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
     }
 
     @Test
@@ -378,14 +379,14 @@
         clearInvocations(mAmbientState);
         mStackScroller.updateStackEndHeightAndStackHeight(expansionFraction);
         verify(mAmbientState, never()).setStackEndHeight(anyFloat());
-        verify(mAmbientState).setStackHeight(anyFloat());
+        verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
 
         // Validate that when the animation ends the stackEndHeight is recalculated immediately
         clearInvocations(mAmbientState);
         mStackScroller.setPanelFlinging(false);
         verify(mAmbientState).setFlinging(eq(false));
         verify(mAmbientState).setStackEndHeight(anyFloat());
-        verify(mAmbientState).setStackHeight(anyFloat());
+        verify(mAmbientState).setInterpolatedStackHeight(anyFloat());
     }
 
     @Test
@@ -427,6 +428,86 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void setExpandFraction_fullyCollapsed() {
+        // Given: NSSL has a height
+        when(mStackScroller.getHeight()).thenReturn(1200);
+        // And: stack bounds are set
+        float expandFraction = 0.0f;
+        float stackTop = 100;
+        float stackCutoff = 1100;
+        float stackHeight = stackCutoff - stackTop;
+        mStackScroller.setStackTop(stackTop);
+        mStackScroller.setStackCutoff(stackCutoff);
+
+        // When: panel is fully collapsed
+        mStackScroller.setExpandFraction(expandFraction);
+
+        // Then
+        assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+        assertThat(mAmbientState.isExpansionChanging()).isFalse();
+        assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(
+                stackHeight * StackScrollAlgorithm.START_FRACTION);
+        assertThat(mAmbientState.isShadeExpanded()).isFalse();
+        assertThat(mStackScroller.getExpandedHeight()).isZero();
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void setExpandFraction_expanding() {
+        // Given: NSSL has a height
+        when(mStackScroller.getHeight()).thenReturn(1200);
+        // And: stack bounds are set
+        float expandFraction = 0.6f;
+        float stackTop = 100;
+        float stackCutoff = 1100;
+        float stackHeight = stackCutoff - stackTop;
+        mStackScroller.setStackTop(stackTop);
+        mStackScroller.setStackCutoff(stackCutoff);
+
+        // When: panel is expanding
+        mStackScroller.setExpandFraction(expandFraction);
+
+        // Then
+        assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+        assertThat(mAmbientState.isExpansionChanging()).isTrue();
+        assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isGreaterThan(
+                stackHeight * StackScrollAlgorithm.START_FRACTION);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isLessThan(stackHeight);
+        assertThat(mStackScroller.getExpandedHeight()).isGreaterThan(0f);
+        assertThat(mAmbientState.isShadeExpanded()).isTrue();
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void setExpandFraction_fullyExpanded() {
+        // Given: NSSL has a height
+        int viewHeight = 1200;
+        when(mStackScroller.getHeight()).thenReturn(viewHeight);
+        // And: stack bounds are set
+        float expandFraction = 1.0f;
+        float stackTop = 100;
+        float stackCutoff = 1100;
+        float stackHeight = stackCutoff - stackTop;
+        mStackScroller.setStackTop(stackTop);
+        mStackScroller.setStackCutoff(stackCutoff);
+
+        // When: panel is fully expanded
+        mStackScroller.setExpandFraction(expandFraction);
+
+        // Then
+        assertThat(mAmbientState.getExpansionFraction()).isEqualTo(expandFraction);
+        assertThat(mAmbientState.isExpansionChanging()).isFalse();
+        assertThat(mAmbientState.getStackEndHeight()).isEqualTo(stackHeight);
+        assertThat(mAmbientState.getInterpolatedStackHeight()).isEqualTo(stackHeight);
+        assertThat(mStackScroller.getExpandedHeight()).isEqualTo(viewHeight);
+        assertThat(mAmbientState.isShadeExpanded()).isTrue();
+    }
+
+    @Test
+    @DisableSceneContainer
     public void testSetExpandedHeight_listenerReceivedCallbacks() {
         final float expectedHeight = 0f;
 
@@ -453,6 +534,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
         mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
@@ -813,7 +895,7 @@
 
     @Test
     @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
-    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    @DisableSceneContainer
     public void testInsideQSHeader_noOffset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -831,7 +913,7 @@
 
     @Test
     @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
-    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    @DisableSceneContainer
     public void testInsideQSHeader_Offset() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -852,7 +934,7 @@
 
     @Test
     @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
-    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    @DisableSceneContainer
     public void testInsideQSHeader_noOffset_qsCompose() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(0, 0, 1000, 1000);
@@ -879,7 +961,7 @@
 
     @Test
     @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME})
-    @DisableSceneContainer // TODO(b/312473478): address lack of QS Header
+    @DisableSceneContainer
     public void testInsideQSHeader_Offset_qsCompose() {
         ViewGroup qsHeader = mock(ViewGroup.class);
         Rect boundsOnScreen = new Rect(100, 100, 1000, 1000);
@@ -908,6 +990,53 @@
     }
 
     @Test
+    @EnableSceneContainer
+    public void testIsInsideScrollableRegion_noScrim() {
+        mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000);
+
+        MotionEvent event = transformEventForView(createMotionEvent(250f, 250f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event)).isTrue();
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void testIsInsideScrollableRegion_noOffset() {
+        mStackScroller.setLeftTopRightBottom(0, 0, 1000, 2000);
+        mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+        MotionEvent event1 = transformEventForView(createMotionEvent(500f, 400f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+        MotionEvent event2 = transformEventForView(createMotionEvent(50, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+        MotionEvent event3 = transformEventForView(createMotionEvent(950f, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+        MotionEvent event4 = transformEventForView(createMotionEvent(500f, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+    }
+
+    @Test
+    @EnableSceneContainer
+    public void testIsInsideScrollableRegion_offset() {
+        mStackScroller.setLeftTopRightBottom(1000, 0, 2000, 2000);
+        mStackScroller.setScrimClippingShape(createScrimShape(100, 500, 900, 2000));
+
+        MotionEvent event1 = transformEventForView(createMotionEvent(1500f, 400f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event1)).isFalse();
+
+        MotionEvent event2 = transformEventForView(createMotionEvent(1050, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event2)).isFalse();
+
+        MotionEvent event3 = transformEventForView(createMotionEvent(1950f, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event3)).isFalse();
+
+        MotionEvent event4 = transformEventForView(createMotionEvent(1500f, 1000f), mStackScroller);
+        assertThat(mStackScroller.isInScrollableRegion(event4)).isTrue();
+    }
+
+    @Test
     @DisableSceneContainer // TODO(b/312473478): address disabled test
     public void setFractionToShade_recomputesStackHeight() {
         mStackScroller.setFractionToShade(1f);
@@ -1192,7 +1321,7 @@
 
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     public void testGenerateHeadsUpDisappearEvent_setsHeadsUpAnimatingAway() {
         // GIVEN NSSL is ready for HUN animations
         Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1208,7 +1337,7 @@
     }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     public void testGenerateHeadsUpDisappearEvent_stackExpanded_headsUpAnimatingAwayNotSet() {
         // GIVEN NSSL would be ready for HUN animations, BUT it is expanded
         Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1227,7 +1356,7 @@
     }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     public void testGenerateHeadsUpDisappearEvent_pendingAppearEvent_headsUpAnimatingAwayNotSet() {
         // GIVEN NSSL is ready for HUN animations
         Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1245,7 +1374,7 @@
     }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     public void testGenerateHeadsUpAppearEvent_headsUpAnimatingAwayNotSet() {
         // GIVEN NSSL is ready for HUN animations
         Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1281,7 +1410,7 @@
     }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     public void testOnChildAnimationsFinished_resetsheadsUpAnimatingAway() {
         // GIVEN NSSL is ready for HUN animations
         Consumer<Boolean> headsUpAnimatingAwayListener = mock(BooleanConsumer.class);
@@ -1358,7 +1487,7 @@
     private static MotionEvent transformEventForView(MotionEvent event, View view) {
         // From `ViewGroup#dispatchTransformedTouchEvent`
         MotionEvent transformed = event.copy();
-        transformed.offsetLocation(-view.getTop(), -view.getLeft());
+        transformed.offsetLocation(/* deltaX = */-view.getLeft(), /* deltaY = */ -view.getTop());
         return transformed;
     }
 
@@ -1394,4 +1523,9 @@
     }
 
     private abstract static class BooleanConsumer implements Consumer<Boolean> { }
+
+    private ShadeScrimShape createScrimShape(int left, int top, int right, int bottom) {
+        ShadeScrimBounds bounds = new ShadeScrimBounds(left, top, right, bottom);
+        return new ShadeScrimShape(bounds, 0, 0);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 2d11917..95db95c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -36,6 +36,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
@@ -52,6 +53,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -672,17 +674,32 @@
     }
 
     @Test
-    public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() {
+    @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
+    public void testForceResetSwipeStateDoesNothingIfTranslationIsZeroAndAlphaIsOne() {
         doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
         doReturn(0f).when(mNotificationRow).getTranslationX();
+        doReturn(1f).when(mNotificationRow).getAlpha();
 
         mSwipeHelper.forceResetSwipeState(mNotificationRow);
 
         verify(mNotificationRow).getTranslationX();
+        verify(mNotificationRow).getAlpha();
         verifyNoMoreInteractions(mNotificationRow);
     }
 
     @Test
+    @EnableFlags(NotificationContentAlphaOptimization.FLAG_NAME)
+    public void testForceResetSwipeStateResetsAlphaIfTranslationIsZeroAndAlphaNotOne() {
+        doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
+        doReturn(0f).when(mNotificationRow).getTranslationX();
+        doReturn(0.5f).when(mNotificationRow).getAlpha();
+
+        mSwipeHelper.forceResetSwipeState(mNotificationRow);
+
+        verify(mNotificationRow).setContentAlpha(eq(1f));
+    }
+
+    @Test
     public void testForceResetSwipeStateResetsTranslationAndAlpha() {
         doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth();
         doReturn(10f).when(mNotificationRow).getTranslationX();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ad029d7..3e8bf47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -296,6 +296,7 @@
             collapsedHeight = 100,
             intrinsicHeight = intrinsicHunHeight,
         )
+        ambientState.qsExpansionFraction = 1.0f
         whenever(notificationRow.isAboveShelf).thenReturn(true)
 
         // When
@@ -1113,6 +1114,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun shadeOpened_hunDoesNotOverlapQQS_hunShouldHaveNoShadow() {
         // Given: shade is opened, yTranslation of HUN is equal to QQS Panel's height,
         // the height of HUN is equal to the height of QQS Panel,
@@ -1143,6 +1145,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun shadeClosed_hunShouldHaveFullShadow() {
         // Given: shade is closed, ambientState.stackTranslation == -ambientState.topPadding,
         // the height of HUN is equal to the height of QQS Panel,
@@ -1171,6 +1174,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     fun draggingHunToOpenShade_hunShouldHavePartialShadow() {
         // Given: shade is closed when HUN pops up,
         // now drags down the HUN to open shade
@@ -1446,7 +1450,7 @@
         // set stackEndHeight and stackHeight
         // ExpansionFractionWithoutShelf == stackHeight / stackEndHeight
         ambientState.stackEndHeight = 100f
-        ambientState.stackHeight = ambientState.stackEndHeight * fraction
+        ambientState.interpolatedStackHeight = ambientState.stackEndHeight * fraction
     }
 
     private fun resetViewStates_hunYTranslationIs(expected: Float) {
@@ -1530,7 +1534,7 @@
         // shade is fully open
         ambientState.expansionFraction = 1.0f
         with(fullStackHeight) {
-            ambientState.stackHeight = this
+            ambientState.interpolatedStackHeight = this
             ambientState.stackEndHeight = this
         }
         stackScrollAlgorithm.setIsExpanded(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
deleted file mode 100644
index 665544d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ /dev/null
@@ -1,648 +0,0 @@
-/*
- * 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.statusbar.phone;
-
-import static com.android.systemui.Flags.FLAG_QS_NEW_PIPELINE;
-import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
-import static com.android.systemui.statusbar.phone.AutoTileManager.DEVICE_CONTROLS;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNotNull;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.display.ColorDisplayManager;
-import android.hardware.display.NightDisplayListener;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dagger.NightDisplayListenerModule;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.AutoAddTracker;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.ReduceBrightColorsController;
-import com.android.systemui.qs.UserSettingObserver;
-import com.android.systemui.qs.external.CustomTile;
-import com.android.systemui.res.R;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastDevice;
-import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.DeviceControlsController;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.SafetyController;
-import com.android.systemui.statusbar.policy.WalletController;
-import com.android.systemui.util.settings.FakeSettings;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.InOrder;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.mockito.stubbing.Answer;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.inject.Named;
-
-@RunWith(AndroidJUnit4.class)
-@RunWithLooper
-@SmallTest
-public class AutoTileManagerTest extends SysuiTestCase {
-
-    private static final String TEST_SETTING = "setting";
-    private static final String TEST_SPEC = "spec";
-    private static final String TEST_SETTING_COMPONENT = "setting_component";
-    private static final String TEST_COMPONENT = "test_pkg/test_cls";
-    private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
-    private static final String TEST_CUSTOM_SAFETY_CLASS = "safety_cls";
-    private static final String TEST_CUSTOM_SAFETY_PKG = "safety_pkg";
-    private static final String TEST_CUSTOM_SAFETY_SPEC = CustomTile.toSpec(new ComponentName(
-            TEST_CUSTOM_SAFETY_PKG, TEST_CUSTOM_SAFETY_CLASS));
-    private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
-
-    private static final int USER = 0;
-
-    @Mock private QSHost mQsHost;
-    @Mock private AutoAddTracker mAutoAddTracker;
-    @Mock private CastController mCastController;
-    @Mock private HotspotController mHotspotController;
-    @Mock private DataSaverController mDataSaverController;
-    @Mock private ManagedProfileController mManagedProfileController;
-    @Mock private NightDisplayListener mNightDisplayListener;
-    @Mock(answer = Answers.RETURNS_SELF)
-    private NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
-    @Mock private ReduceBrightColorsController mReduceBrightColorsController;
-    @Mock private DeviceControlsController mDeviceControlsController;
-    @Mock private WalletController mWalletController;
-    @Mock private SafetyController mSafetyController;
-    @Mock(answer = Answers.RETURNS_SELF)
-    private AutoAddTracker.Builder mAutoAddTrackerBuilder;
-    @Mock private Context mUserContext;
-    @Spy private PackageManager mPackageManager;
-    private final boolean mIsReduceBrightColorsAvailable = true;
-
-    private AutoTileManager mAutoTileManager; // under test
-
-    private SecureSettings mSecureSettings;
-    private ManagedProfileController.Callback mManagedProfileCallback;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mSecureSettings = new FakeSettings();
-
-        mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
-
-        mContext.getOrCreateTestableResources().addOverride(
-                R.array.config_quickSettingsAutoAdd,
-                new String[] {
-                        TEST_SETTING + SEPARATOR + TEST_SPEC,
-                        TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC
-                }
-        );
-        mContext.getOrCreateTestableResources().addOverride(
-                com.android.internal.R.bool.config_nightDisplayAvailable, true);
-        mContext.getOrCreateTestableResources().addOverride(
-                R.string.safety_quick_settings_tile_class, TEST_CUSTOM_SAFETY_CLASS);
-
-        when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
-        when(mQsHost.getUserContext()).thenReturn(mUserContext);
-        when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
-        mPackageManager = Mockito.spy(mContext.getPackageManager());
-        when(mPackageManager.getPermissionControllerPackageName())
-                .thenReturn(TEST_CUSTOM_SAFETY_PKG);
-        Context context = Mockito.spy(mContext);
-        when(context.getPackageManager()).thenReturn(mPackageManager);
-        when(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener);
-
-        mAutoTileManager = createAutoTileManager(context);
-        mAutoTileManager.init();
-    }
-
-    @After
-    public void tearDown() {
-        mAutoTileManager.destroy();
-    }
-
-    private AutoTileManager createAutoTileManager(
-            Context context,
-            AutoAddTracker.Builder autoAddTrackerBuilder,
-            HotspotController hotspotController,
-            DataSaverController dataSaverController,
-            ManagedProfileController managedProfileController,
-            NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
-            CastController castController,
-            ReduceBrightColorsController reduceBrightColorsController,
-            DeviceControlsController deviceControlsController,
-            WalletController walletController,
-            SafetyController safetyController,
-            @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
-        return new AutoTileManager(context, autoAddTrackerBuilder, mQsHost,
-                Handler.createAsync(TestableLooper.get(this).getLooper()),
-                mSecureSettings,
-                hotspotController,
-                dataSaverController,
-                managedProfileController,
-                mNightDisplayListenerBuilder,
-                castController,
-                reduceBrightColorsController,
-                deviceControlsController,
-                walletController,
-                safetyController,
-                isReduceBrightColorsAvailable);
-    }
-
-    private AutoTileManager createAutoTileManager(Context context) {
-        return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
-                mDataSaverController, mManagedProfileController, mNightDisplayListenerBuilder,
-                mCastController, mReduceBrightColorsController, mDeviceControlsController,
-                mWalletController, mSafetyController, mIsReduceBrightColorsAvailable);
-    }
-
-    @Test
-    public void testCreatedAutoTileManagerIsNotInitialized() {
-        AutoAddTracker.Builder builder = mock(AutoAddTracker.Builder.class, Answers.RETURNS_SELF);
-        AutoAddTracker tracker = mock(AutoAddTracker.class);
-        when(builder.build()).thenReturn(tracker);
-        HotspotController hC = mock(HotspotController.class);
-        DataSaverController dSC = mock(DataSaverController.class);
-        ManagedProfileController mPC = mock(ManagedProfileController.class);
-        NightDisplayListenerModule.Builder nDSB = mock(NightDisplayListenerModule.Builder.class);
-        CastController cC = mock(CastController.class);
-        ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
-        DeviceControlsController dCC = mock(DeviceControlsController.class);
-        WalletController wC = mock(WalletController.class);
-        SafetyController sC = mock(SafetyController.class);
-
-        AutoTileManager manager =
-                createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDSB, cC, rBC,
-                        dCC, wC, sC, true);
-
-        verify(tracker, never()).initialize();
-        verify(hC, never()).addCallback(any());
-        verify(dSC, never()).addCallback(any());
-        verify(mPC, never()).addCallback(any());
-        verifyNoMoreInteractions(nDSB);
-        verify(cC, never()).addCallback(any());
-        verify(rBC, never()).addCallback(any());
-        verify(dCC, never()).setCallback(any());
-        verify(wC, never()).getWalletPosition();
-        verify(sC, never()).addCallback(any());
-        assertNull(manager.getSecureSettingForKey(TEST_SETTING));
-        assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
-    }
-
-    @Test
-    public void testChangeUserWhenNotInitializedThrows() {
-        AutoTileManager manager = createAutoTileManager(mock(Context.class));
-
-        try {
-            manager.changeUser(UserHandle.of(USER + 1));
-            fail();
-        } catch (Exception e) {
-            // This should throw and take this path
-        }
-    }
-
-    @Test
-    public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
-        );
-
-        InOrder inOrderHotspot = inOrder(mHotspotController);
-        inOrderHotspot.verify(mHotspotController).removeCallback(any());
-        inOrderHotspot.verify(mHotspotController).addCallback(any());
-
-        InOrder inOrderDataSaver = inOrder(mDataSaverController);
-        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
-        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
-        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
-        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
-        if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
-            inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
-            inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-        }
-
-        InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
-        inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
-        inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
-
-        InOrder inOrderCast = inOrder(mCastController);
-        inOrderCast.verify(mCastController).removeCallback(any());
-        inOrderCast.verify(mCastController).addCallback(any());
-
-        InOrder inOrderDevices = inOrder(mDeviceControlsController);
-        inOrderDevices.verify(mDeviceControlsController).removeCallback();
-        inOrderDevices.verify(mDeviceControlsController).setCallback(any());
-
-        verify(mWalletController, times(2)).getWalletPosition();
-
-        InOrder inOrderSafety = inOrder(mSafetyController);
-        inOrderSafety.verify(mSafetyController).removeCallback(any());
-        inOrderSafety.verify(mSafetyController).addCallback(any());
-
-        UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
-        assertEquals(USER + 1, setting.getCurrentUser());
-        assertTrue(setting.isListening());
-    }
-
-    @Test
-    public void testChangeUserSomeCallbacksNotAdded() throws Exception {
-        when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
-        when(mAutoAddTracker.isAdded("work")).thenReturn(true);
-        when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
-        when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 1))
-        );
-
-        verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
-
-        InOrder inOrderHotspot = inOrder(mHotspotController);
-        inOrderHotspot.verify(mHotspotController).removeCallback(any());
-        inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
-
-        InOrder inOrderDataSaver = inOrder(mDataSaverController);
-        inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
-        inOrderDataSaver.verify(mDataSaverController).addCallback(any());
-
-        InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
-        inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
-        inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
-
-        if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
-            inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
-            inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
-        }
-
-        InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
-        inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
-        inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
-
-        InOrder inOrderCast = inOrder(mCastController);
-        inOrderCast.verify(mCastController).removeCallback(any());
-        inOrderCast.verify(mCastController, never()).addCallback(any());
-
-        InOrder inOrderDevices = inOrder(mDeviceControlsController);
-        inOrderDevices.verify(mDeviceControlsController).removeCallback();
-        inOrderDevices.verify(mDeviceControlsController).setCallback(any());
-
-        verify(mWalletController, times(2)).getWalletPosition();
-
-        InOrder inOrderSafety = inOrder(mSafetyController);
-        inOrderSafety.verify(mSafetyController).removeCallback(any());
-        inOrderSafety.verify(mSafetyController).addCallback(any());
-
-        UserSettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
-        assertEquals(USER + 1, setting.getCurrentUser());
-        assertFalse(setting.isListening());
-    }
-
-    @Test
-    public void testGetCurrentUserId() throws Exception {
-        assertEquals(USER, mAutoTileManager.getCurrentUserId());
-
-        TestableLooper.get(this).runWithLooper(() ->
-                mAutoTileManager.changeUser(UserHandle.of(USER + 100))
-        );
-
-        assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
-    }
-
-    @Test
-    public void nightTileAdded_whenActivated() {
-        if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            return;
-        }
-        mAutoTileManager.mNightDisplayCallback.onActivated(true);
-        verify(mQsHost).addTile("night");
-    }
-
-    @Test
-    public void nightTileNotAdded_whenDeactivated() {
-        if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            return;
-        }
-        mAutoTileManager.mNightDisplayCallback.onActivated(false);
-        verify(mQsHost, never()).addTile("night");
-    }
-
-    @Test
-    public void nightTileAdded_whenNightModeTwilight() {
-        if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            return;
-        }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                ColorDisplayManager.AUTO_MODE_TWILIGHT);
-        verify(mQsHost).addTile("night");
-    }
-
-    @Test
-    public void nightTileAdded_whenNightModeCustom() {
-        if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            return;
-        }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
-        verify(mQsHost).addTile("night");
-    }
-
-    @Test
-    public void nightTileNotAdded_whenNightModeDisabled() {
-        if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
-            return;
-        }
-        mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
-                ColorDisplayManager.AUTO_MODE_DISABLED);
-        verify(mQsHost, never()).addTile("night");
-    }
-
-    @Test
-    public void reduceBrightColorsTileAdded_whenActivated() {
-        mAutoTileManager.mReduceBrightColorsCallback.onActivated(true);
-        verify(mQsHost).addTile("reduce_brightness");
-    }
-
-    @Test
-    public void reduceBrightColorsTileNotAdded_whenDeactivated() {
-        mAutoTileManager.mReduceBrightColorsCallback.onActivated(false);
-        verify(mQsHost, never()).addTile("reduce_brightness");
-    }
-
-    private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
-        CastDevice.CastState state = isCasting
-                ? CastDevice.CastState.Connected
-                : CastDevice.CastState.Disconnected;
-        return Collections.singletonList(
-                new CastDevice(
-                        "id",
-                        /* name= */ null,
-                        /* description= */ null,
-                        /* state= */ state,
-                        /* origin= */ CastDevice.CastOrigin.MediaProjection,
-                        /* tag= */ null));
-    }
-
-    @Test
-    public void castTileAdded_whenDeviceIsCasting() {
-        doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices();
-        mAutoTileManager.mCastCallback.onCastDevicesChanged();
-        verify(mQsHost).addTile("cast");
-    }
-
-    @Test
-    public void castTileNotAdded_whenDeviceIsNotCasting() {
-        doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices();
-        mAutoTileManager.mCastCallback.onCastDevicesChanged();
-        verify(mQsHost, never()).addTile("cast");
-    }
-
-    @Test
-    public void testSettingTileAdded_onChanged() {
-        changeValue(TEST_SETTING, 1);
-        verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
-        verify(mQsHost).addTile(TEST_SPEC);
-    }
-
-    @Test
-    public void testSettingTileAddedComponentAtEnd_onChanged() {
-        changeValue(TEST_SETTING_COMPONENT, 1);
-        verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
-        verify(mQsHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
-            , /* end */ true);
-    }
-
-    @Test
-    public void testSettingTileAdded_onlyOnce() {
-        changeValue(TEST_SETTING, 1);
-        changeValue(TEST_SETTING, 2);
-        verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
-        verify(mQsHost).addTile(TEST_SPEC);
-    }
-
-    @Test
-    public void testSettingTileNotAdded_onChangedTo0() {
-        changeValue(TEST_SETTING, 0);
-        verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
-        verify(mQsHost, never()).addTile(TEST_SPEC);
-    }
-
-    @Test
-    public void testSettingTileNotAdded_ifPreviouslyAdded() {
-        when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
-
-        changeValue(TEST_SETTING, 1);
-        verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
-        verify(mQsHost, never()).addTile(TEST_SPEC);
-    }
-
-    @Test
-    public void testSafetyTileNotAdded_ifPreviouslyAdded() {
-        ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
-        mAutoTileManager.init();
-        verify(mQsHost, times(1)).addTile(safetyComponent, true);
-        when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
-        mAutoTileManager.init();
-        verify(mQsHost, times(1)).addTile(safetyComponent, true);
-    }
-
-    @Test
-    public void testSafetyTileAdded_onUserChange() {
-        ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
-        mAutoTileManager.init();
-        verify(mQsHost, times(1)).addTile(safetyComponent, true);
-        when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(false);
-        mAutoTileManager.changeUser(UserHandle.of(USER + 1));
-        verify(mQsHost, times(2)).addTile(safetyComponent, true);
-    }
-
-    @Test
-    public void testSafetyTileRemoved_onSafetyCenterDisable() {
-        ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
-        mAutoTileManager.init();
-        when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
-        mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
-        verify(mQsHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
-    }
-
-    @Test
-    public void testSafetyTileAdded_onSafetyCenterEnable() {
-        ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
-        mAutoTileManager.init();
-        verify(mQsHost, times(1)).addTile(safetyComponent, true);
-        mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
-        mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(true);
-        verify(mQsHost, times(2)).addTile(safetyComponent, true);
-    }
-
-    @Test
-    public void managedProfileAdded_tileAdded() {
-        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
-        when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
-        mAutoTileManager = createAutoTileManager(mContext);
-        Mockito.doAnswer((Answer<Object>) invocation -> {
-            mManagedProfileCallback = invocation.getArgument(0);
-            return null;
-        }).when(mManagedProfileController).addCallback(any());
-        mAutoTileManager.init();
-        when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
-
-        mManagedProfileCallback.onManagedProfileChanged();
-
-        verify(mQsHost, times(1)).addTile(eq("work"), eq(2));
-        verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
-    }
-
-    @Test
-    public void managedProfileRemoved_tileRemoved() {
-        when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
-        mAutoTileManager = createAutoTileManager(mContext);
-        Mockito.doAnswer((Answer<Object>) invocation -> {
-            mManagedProfileCallback = invocation.getArgument(0);
-            return null;
-        }).when(mManagedProfileController).addCallback(any());
-        mAutoTileManager.init();
-        when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
-
-        mManagedProfileCallback.onManagedProfileChanged();
-
-        verify(mQsHost, times(1)).removeTile(eq("work"));
-        verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
-    }
-
-    @Test
-    public void testAddControlsTileIfNotPresent() {
-        String spec = DEVICE_CONTROLS;
-        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
-        when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
-
-        mAutoTileManager.init();
-        ArgumentCaptor<DeviceControlsController.Callback> captor =
-                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
-        verify(mDeviceControlsController).setCallback(captor.capture());
-
-        captor.getValue().onControlsUpdate(3);
-        verify(mQsHost).addTile(spec, 3);
-        verify(mAutoAddTracker).setTileAdded(spec);
-    }
-
-    @Test
-    public void testDontAddControlsTileIfPresent() {
-        String spec = DEVICE_CONTROLS;
-        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
-        when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
-
-        mAutoTileManager.init();
-        ArgumentCaptor<DeviceControlsController.Callback> captor =
-                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
-        verify(mDeviceControlsController).setCallback(captor.capture());
-
-        captor.getValue().removeControlsAutoTracker();
-        verify(mQsHost, never()).addTile(spec, 3);
-        verify(mAutoAddTracker, never()).setTileAdded(spec);
-        verify(mAutoAddTracker).setTileRemoved(spec);
-    }
-
-    @Test
-    public void testRemoveControlsTileFromTrackerWhenRequested() {
-        String spec = "controls";
-        when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(true);
-        QSTile mockTile = mock(QSTile.class);
-        when(mockTile.getTileSpec()).thenReturn(spec);
-        when(mQsHost.getTiles()).thenReturn(List.of(mockTile));
-
-        mAutoTileManager.init();
-        ArgumentCaptor<DeviceControlsController.Callback> captor =
-                ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
-
-        verify(mDeviceControlsController).setCallback(captor.capture());
-
-        captor.getValue().onControlsUpdate(3);
-        verify(mQsHost, never()).addTile(spec, 3);
-        verify(mAutoAddTracker, never()).setTileAdded(spec);
-    }
-
-
-    @Test
-    public void testEmptyArray_doesNotCrash() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.array.config_quickSettingsAutoAdd, new String[0]);
-        createAutoTileManager(mContext).destroy();
-    }
-
-    @Test
-    public void testMissingConfig_doesNotCrash() {
-        mContext.getOrCreateTestableResources().addOverride(
-                R.array.config_quickSettingsAutoAdd, null);
-        createAutoTileManager(mContext).destroy();
-    }
-
-    @Test
-    public void testUserChange_newNightDisplayListenerCreated() {
-        UserHandle newUser = UserHandle.of(1000);
-        mAutoTileManager.changeUser(newUser);
-        InOrder inOrder = inOrder(mNightDisplayListenerBuilder);
-        inOrder.verify(mNightDisplayListenerBuilder).setUser(newUser.getIdentifier());
-        inOrder.verify(mNightDisplayListenerBuilder).build();
-    }
-
-    // Will only notify if it's listening
-    private void changeValue(String key, int value) {
-        mSecureSettings.putIntForUser(key, value, USER);
-        TestableLooper.get(this).processAllMessages();
-    }
-}
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 e9c16c2..c7c08a9 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
@@ -89,6 +89,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.compose.animation.scene.ObservableTransitionState;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
@@ -344,6 +345,7 @@
     @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
     @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
     @Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
+    @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -603,7 +605,8 @@
                 mActivityStarter,
                 mBrightnessMirrorShowingInteractor,
                 mGlanceableHubContainerController,
-                mEmergencyGestureIntentFactory
+                mEmergencyGestureIntentFactory,
+                mViewCaptureAwareWindowManager
         );
         mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
         mCentralSurfaces.initShadeVisibilityListener();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
index 2f81027..c7919df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ManagedProfileControllerImplTest.kt
@@ -20,6 +20,7 @@
 import android.os.UserManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -35,6 +36,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.never
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -46,12 +48,20 @@
 
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var userManager: UserManager
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
 
-        controller = ManagedProfileControllerImpl(context, mainExecutor, userTracker, userManager)
+        controller =
+            ManagedProfileControllerImpl(
+                context,
+                mainExecutor,
+                userTracker,
+                userManager,
+                keyguardUpdateMonitor
+            )
     }
 
     @Test
@@ -107,6 +117,24 @@
         captor.value.onProfilesChanged(userManager.getEnabledProfiles(1))
     }
 
+    @Test
+    fun hasWorkingProfile_setWorkModeEnabled_callsAwakenFromDream() {
+        `when`(userTracker.userId).thenReturn(1)
+        setupWorkingProfile(1)
+        `when`(userManager.requestQuietModeEnabled(any(), any())).thenReturn(false)
+        controller.hasActiveProfile()
+
+        controller.isWorkModeEnabled = true
+
+        verify(keyguardUpdateMonitor).awakenFromDream()
+    }
+
+    @Test
+    fun noWorkingProfile_setWorkModeEnabled_NoAwakenFromDreamCall() {
+        controller.isWorkModeEnabled = true
+        verify(keyguardUpdateMonitor, never()).awakenFromDream()
+    }
+
     private fun setupWorkingProfile(userId: Int) {
         `when`(userManager.getEnabledProfiles(userId))
             .thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index dfee2ed..2ed3473 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -17,23 +17,32 @@
 package com.android.systemui.statusbar.phone
 
 import android.app.AlarmManager
+import android.app.AutomaticZenRule
+import android.app.NotificationManager
 import android.app.admin.DevicePolicyManager
 import android.app.admin.DevicePolicyResourcesManager
 import android.content.SharedPreferences
+import android.net.Uri
 import android.os.UserManager
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
+import android.service.notification.SystemZenRules
+import android.service.notification.ZenModeConfig
 import android.telecom.TelecomManager
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
 import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
+import com.android.systemui.kosmos.testScope
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.screenrecord.RecordingController
@@ -53,19 +62,18 @@
 import com.android.systemui.statusbar.policy.SensorPrivacyController
 import com.android.systemui.statusbar.policy.UserInfoController
 import com.android.systemui.statusbar.policy.ZenModeController
+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.util.RingerModeTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.kotlin.JavaAdapter
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.DateFormatUtil
 import com.android.systemui.util.time.FakeSystemClock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -83,7 +91,10 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.reset
 
 @RunWith(AndroidJUnit4::class)
 @RunWithLooper
@@ -91,7 +102,11 @@
 @SmallTest
 class PhoneStatusBarPolicyTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val zenModeRepository = kosmos.fakeZenModeRepository
+
     companion object {
+        private const val ZEN_SLOT = "zen"
         private const val ALARM_SLOT = "alarm"
         private const val CAST_SLOT = "cast"
         private const val SCREEN_RECORD_SLOT = "screen_record"
@@ -109,7 +124,6 @@
     @Mock private lateinit var userInfoController: UserInfoController
     @Mock private lateinit var rotationLockController: RotationLockController
     @Mock private lateinit var dataSaverController: DataSaverController
-    @Mock private lateinit var zenModeController: ZenModeController
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var locationController: LocationController
@@ -131,8 +145,9 @@
     private lateinit var alarmCallbackCaptor:
         ArgumentCaptor<NextAlarmController.NextAlarmChangeCallback>
 
-    private val testScope = TestScope(UnconfinedTestDispatcher())
+    private val testScope = kosmos.testScope
     private val fakeConnectedDisplayStateProvider = FakeConnectedDisplayStateProvider()
+    private val zenModeController = FakeZenModeController()
 
     private lateinit var executor: FakeExecutor
     private lateinit var statusBarPolicy: PhoneStatusBarPolicy
@@ -234,7 +249,7 @@
             statusBarPolicy.init()
             clearInvocations(iconController)
 
-            fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
+            fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
             runCurrent()
 
             verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
@@ -246,7 +261,8 @@
             statusBarPolicy.init()
             clearInvocations(iconController)
 
-            fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED)
+            fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED)
+            runCurrent()
 
             verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, false)
         }
@@ -257,9 +273,12 @@
             statusBarPolicy.init()
             clearInvocations(iconController)
 
-            fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
-            fakeConnectedDisplayStateProvider.emit(State.DISCONNECTED)
-            fakeConnectedDisplayStateProvider.emit(State.CONNECTED)
+            fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
+            runCurrent()
+            fakeConnectedDisplayStateProvider.setState(State.DISCONNECTED)
+            runCurrent()
+            fakeConnectedDisplayStateProvider.setState(State.CONNECTED)
+            runCurrent()
 
             inOrder(iconController).apply {
                 verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
@@ -274,7 +293,8 @@
             statusBarPolicy.init()
             clearInvocations(iconController)
 
-            fakeConnectedDisplayStateProvider.emit(State.CONNECTED_SECURE)
+            fakeConnectedDisplayStateProvider.setState(State.CONNECTED_SECURE)
+            runCurrent()
 
             verify(iconController).setIconVisibility(CONNECTED_DISPLAY_SLOT, true)
         }
@@ -374,6 +394,111 @@
         verify(iconController, never()).setIconVisibility(eq(SCREEN_RECORD_SLOT), any())
     }
 
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
+    fun zenModeInteractorActiveModeChanged_showsModeIcon() =
+        testScope.runTest {
+            statusBarPolicy.init()
+            reset(iconController)
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setId("bedtime")
+                        .setName("Bedtime Mode")
+                        .setType(AutomaticZenRule.TYPE_BEDTIME)
+                        .setActive(true)
+                        .setPackage(mContext.packageName)
+                        .setIconResId(android.R.drawable.ic_lock_lock)
+                        .build(),
+                    TestModeBuilder()
+                        .setId("other")
+                        .setName("Other Mode")
+                        .setType(AutomaticZenRule.TYPE_OTHER)
+                        .setActive(true)
+                        .setPackage(SystemZenRules.PACKAGE_ANDROID)
+                        .setIconResId(android.R.drawable.ic_media_play)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(true))
+            verify(iconController)
+                .setResourceIcon(
+                    eq(ZEN_SLOT),
+                    eq(mContext.packageName),
+                    eq(android.R.drawable.ic_lock_lock),
+                    any(), // non-null
+                    eq("Bedtime Mode"),
+                    eq(StatusBarIcon.Shape.FIXED_SPACE)
+                )
+
+            zenModeRepository.deactivateMode("bedtime")
+            runCurrent()
+
+            verify(iconController)
+                .setResourceIcon(
+                    eq(ZEN_SLOT),
+                    eq(null),
+                    eq(android.R.drawable.ic_media_play),
+                    any(), // non-null
+                    eq("Other Mode"),
+                    eq(StatusBarIcon.Shape.FIXED_SPACE)
+                )
+
+            zenModeRepository.deactivateMode("other")
+            runCurrent()
+
+            verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(false))
+        }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
+    fun zenModeControllerOnGlobalZenChanged_doesNotUpdateDndIcon() {
+        statusBarPolicy.init()
+        reset(iconController)
+
+        zenModeController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null)
+
+        verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
+        verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
+        verify(iconController, never())
+            .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any())
+    }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
+    fun zenModeInteractorActiveModeChanged_withFlagDisabled_ignored() =
+        testScope.runTest {
+            statusBarPolicy.init()
+            reset(iconController)
+
+            zenModeRepository.addMode(id = "Bedtime", active = true)
+            runCurrent()
+
+            verify(iconController, never()).setIconVisibility(eq(ZEN_SLOT), any())
+            verify(iconController, never()).setIcon(eq(ZEN_SLOT), anyInt(), any())
+            verify(iconController, never())
+                .setResourceIcon(eq(ZEN_SLOT), any(), any(), any(), any(), any())
+        }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_MODES_UI_ICONS)
+    fun zenModeControllerOnGlobalZenChanged_withFlagDisabled_updatesDndIcon() {
+        statusBarPolicy.init()
+        reset(iconController)
+
+        zenModeController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, null)
+
+        verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(true))
+        verify(iconController).setIcon(eq(ZEN_SLOT), anyInt(), eq("Priority only"))
+
+        zenModeController.setZen(Settings.Global.ZEN_MODE_OFF, null, null)
+
+        verify(iconController).setIconVisibility(eq(ZEN_SLOT), eq(false))
+    }
+
     private fun createAlarmInfo(): AlarmManager.AlarmClockInfo {
         return AlarmManager.AlarmClockInfo(10L, null)
     }
@@ -412,14 +537,17 @@
             privacyItemController,
             privacyLogger,
             fakeConnectedDisplayStateProvider,
+            kosmos.zenModeInteractor,
             JavaAdapter(testScope.backgroundScope)
         )
     }
 
     private class FakeConnectedDisplayStateProvider : ConnectedDisplayInteractor {
-        private val flow = MutableSharedFlow<State>()
+        private val flow = MutableStateFlow(State.DISCONNECTED)
 
-        suspend fun emit(value: State) = flow.emit(value)
+        fun setState(value: State) {
+            flow.value = value
+        }
 
         override val connectedDisplayState: Flow<State>
             get() = flow
@@ -433,4 +561,51 @@
         override val concurrentDisplaysInProgress: Flow<Boolean>
             get() = TODO("Not yet implemented")
     }
+
+    private class FakeZenModeController : ZenModeController {
+
+        private val callbacks = mutableListOf<ZenModeController.Callback>()
+        private var zen = Settings.Global.ZEN_MODE_OFF
+        private var consolidatedPolicy = NotificationManager.Policy(0, 0, 0)
+
+        override fun addCallback(listener: ZenModeController.Callback) {
+            callbacks.add(listener)
+        }
+
+        override fun removeCallback(listener: ZenModeController.Callback) {
+            callbacks.remove(listener)
+        }
+
+        override fun setZen(zen: Int, conditionId: Uri?, reason: String?) {
+            this.zen = zen
+            callbacks.forEach { it.onZenChanged(zen) }
+        }
+
+        override fun getZen(): Int = zen
+
+        override fun getManualRule(): ZenModeConfig.ZenRule = throw NotImplementedError()
+
+        override fun getConfig(): ZenModeConfig = throw NotImplementedError()
+
+        fun setConsolidatedPolicy(policy: NotificationManager.Policy) {
+            this.consolidatedPolicy = policy
+            callbacks.forEach { it.onConsolidatedPolicyChanged(consolidatedPolicy) }
+        }
+
+        override fun getConsolidatedPolicy(): NotificationManager.Policy = consolidatedPolicy
+
+        override fun getNextAlarm() = throw NotImplementedError()
+
+        override fun isZenAvailable() = throw NotImplementedError()
+
+        override fun getEffectsSuppressor() = throw NotImplementedError()
+
+        override fun isCountdownConditionSupported() = throw NotImplementedError()
+
+        override fun getCurrentUser() = throw NotImplementedError()
+
+        override fun isVolumeRestricted() = throw NotImplementedError()
+
+        override fun areNotificationsHiddenInShade() = throw NotImplementedError()
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 25314f3..83d0bcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -20,6 +20,9 @@
 import android.app.StatusBarManager.WINDOW_STATE_HIDING
 import android.app.StatusBarManager.WINDOW_STATE_SHOWING
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
+import android.graphics.Insets
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.view.InputDevice
 import android.view.LayoutInflater
 import android.view.MotionEvent
@@ -31,8 +34,11 @@
 import androidx.test.filters.SmallTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.fakeDarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.scene.ui.view.WindowRootView
 import com.android.systemui.shade.ShadeControllerImpl
@@ -40,6 +46,7 @@
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.policy.Clock
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
 import com.android.systemui.unfold.SysUIUnfoldComponent
@@ -68,7 +75,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
 
+    private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
     @Mock private lateinit var shadeViewController: ShadeViewController
     @Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor
     @Mock private lateinit var featureFlags: FeatureFlags
@@ -84,11 +93,18 @@
     @Mock private lateinit var windowRootView: Provider<WindowRootView>
     @Mock private lateinit var shadeLogger: ShadeLogger
     @Mock private lateinit var viewUtil: ViewUtil
+    @Mock private lateinit var statusBarContentInsetsProvider: StatusBarContentInsetsProvider
     private lateinit var statusBarWindowStateController: StatusBarWindowStateController
 
     private lateinit var view: PhoneStatusBarView
     private lateinit var controller: PhoneStatusBarViewController
 
+    private val clockView: Clock
+        get() = view.requireViewById(R.id.clock)
+
+    private val batteryView: BatteryMeterView
+        get() = view.requireViewById(R.id.battery)
+
     private val unfoldConfig = UnfoldConfig()
 
     @Before
@@ -97,6 +113,9 @@
 
         statusBarWindowStateController = StatusBarWindowStateController(DISPLAY_ID, commandQueue)
 
+        `when`(statusBarContentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
+            .thenReturn(Insets.NONE)
+
         `when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
             .thenReturn(moveFromCenterAnimation)
         // create the view and controller on main thread as it requires main looper
@@ -104,7 +123,7 @@
             val parent = FrameLayout(mContext) // add parent to keep layout params
             view =
                 LayoutInflater.from(mContext).inflate(R.layout.status_bar, parent, false)
-                        as PhoneStatusBarView
+                    as PhoneStatusBarView
             controller = createAndInitController(view)
         }
     }
@@ -112,16 +131,25 @@
     @Test
     fun onViewAttachedAndDrawn_startListeningConfigurationControllerCallback() {
         val view = createViewMock()
-        val argumentCaptor = ArgumentCaptor.forClass(
-                ConfigurationController.ConfigurationListener::class.java)
+
         InstrumentationRegistry.getInstrumentation().runOnMainSync {
             controller = createAndInitController(view)
         }
 
-        verify(configurationController).addCallback(argumentCaptor.capture())
-        argumentCaptor.value.onDensityOrFontScaleChanged()
+        verify(configurationController).addCallback(any())
+    }
 
-        verify(view).onDensityOrFontScaleChanged()
+    @Test
+    fun onViewAttachedAndDrawn_darkReceiversRegistered() {
+        val view = createViewMock()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            controller = createAndInitController(view)
+        }
+
+        assertThat(fakeDarkIconDispatcher.receivers.size).isEqualTo(2)
+        assertThat(fakeDarkIconDispatcher.receivers).contains(clockView)
+        assertThat(fakeDarkIconDispatcher.receivers).contains(batteryView)
     }
 
     @Test
@@ -156,10 +184,25 @@
     }
 
     @Test
+    fun onViewDetached_darkReceiversUnregistered() {
+        val view = createViewMock()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            controller = createAndInitController(view)
+        }
+
+        assertThat(fakeDarkIconDispatcher.receivers).isNotEmpty()
+
+        controller.onViewDetached()
+
+        assertThat(fakeDarkIconDispatcher.receivers).isEmpty()
+    }
+
+    @Test
     fun handleTouchEventFromStatusBar_panelsNotEnabled_returnsFalseAndNoViewEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(false)
         val returnVal =
-            view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+            view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0))
         assertThat(returnVal).isFalse()
         verify(shadeViewController, never()).handleExternalTouch(any())
     }
@@ -169,7 +212,7 @@
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(false)
         val returnVal =
-            view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+            view.onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0))
         assertThat(returnVal).isTrue()
         verify(shadeViewController, never()).handleExternalTouch(any())
     }
@@ -178,7 +221,7 @@
     fun handleTouchEventFromStatusBar_viewNotEnabledButIsMoveEvent_viewReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(false)
-        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 2f, 0)
 
         view.onTouchEvent(event)
 
@@ -208,6 +251,50 @@
     }
 
     @Test
+    @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOff_viewReturnsFalse() {
+        `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        val returnVal = view.onInterceptTouchEvent(event)
+
+        assertThat(returnVal).isFalse()
+    }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun handleInterceptTouchEventFromStatusBar_shadeReturnsFalse_flagOn_viewReturnsFalse() {
+        `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(false)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        val returnVal = view.onInterceptTouchEvent(event)
+
+        assertThat(returnVal).isFalse()
+    }
+
+    @Test
+    @DisableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOff_viewReturnsFalse() {
+        `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        val returnVal = view.onInterceptTouchEvent(event)
+
+        assertThat(returnVal).isFalse()
+    }
+
+    @Test
+    @EnableFlags(com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun handleInterceptTouchEventFromStatusBar_shadeReturnsTrue_flagOn_viewReturnsTrue() {
+        `when`(shadeViewController.handleExternalInterceptTouch(any())).thenReturn(true)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        val returnVal = view.onInterceptTouchEvent(event)
+
+        assertThat(returnVal).isTrue()
+    }
+
+    @Test
     fun onTouch_windowHidden_centralSurfacesNotNotified() {
         val callback = getCommandQueueCallback()
         callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)
@@ -244,9 +331,7 @@
             controller = createAndInitController(view)
         }
         val statusContainer = view.requireViewById<View>(R.id.system_icons)
-        statusContainer.dispatchTouchEvent(
-            getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
-        )
+        statusContainer.dispatchTouchEvent(getActionUpEventFromSource(InputDevice.SOURCE_MOUSE))
         verify(shadeControllerImpl).animateExpandShade()
     }
 
@@ -257,9 +342,10 @@
             controller = createAndInitController(view)
         }
         val statusContainer = view.requireViewById<View>(R.id.system_icons)
-        val handled = statusContainer.dispatchTouchEvent(
-            getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
-        )
+        val handled =
+            statusContainer.dispatchTouchEvent(
+                getActionUpEventFromSource(InputDevice.SOURCE_TOUCHSCREEN)
+            )
         assertThat(handled).isFalse()
     }
 
@@ -295,21 +381,23 @@
 
     private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController {
         return PhoneStatusBarViewController.Factory(
-            Optional.of(sysuiUnfoldComponent),
-            Optional.of(progressProvider),
-            featureFlags,
-            userChipViewModel,
-            centralSurfacesImpl,
-            statusBarWindowStateController,
-            shadeControllerImpl,
-            shadeViewController,
-            panelExpansionInteractor,
-            windowRootView,
-            shadeLogger,
-            viewUtil,
-            configurationController,
-            mStatusOverlayHoverListenerFactory
-        )
+                Optional.of(sysuiUnfoldComponent),
+                Optional.of(progressProvider),
+                featureFlags,
+                userChipViewModel,
+                centralSurfacesImpl,
+                statusBarWindowStateController,
+                shadeControllerImpl,
+                shadeViewController,
+                panelExpansionInteractor,
+                windowRootView,
+                shadeLogger,
+                viewUtil,
+                configurationController,
+                mStatusOverlayHoverListenerFactory,
+                fakeDarkIconDispatcher,
+                statusBarContentInsetsProvider,
+            )
             .create(view)
             .also { it.init() }
     }
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 eae4f23..68df748 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
@@ -19,6 +19,8 @@
 import android.content.res.Configuration
 import android.graphics.Insets
 import android.graphics.Rect
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper.RunWithLooper
 import android.view.DisplayCutout
 import android.view.DisplayShape
@@ -30,9 +32,10 @@
 import android.view.WindowInsets
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT
+import com.android.systemui.Flags.FLAG_STATUS_BAR_SWIPE_OVER_CHIP
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.mockito.mock
@@ -40,6 +43,7 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
+import org.mockito.Mockito.never
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
@@ -52,22 +56,14 @@
     private val systemIconsContainer: View
         get() = view.requireViewById(R.id.system_icons)
 
-    private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
     private val windowController = mock<StatusBarWindowController>()
 
     @Before
     fun setUp() {
-        mDependency.injectTestDependency(
-            StatusBarContentInsetsProvider::class.java,
-            contentInsetsProvider
-        )
-        mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
         mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
         context.ensureTestableResources()
         view = spy(createStatusBarView())
         whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(Insets.NONE)
     }
 
     @Test
@@ -82,7 +78,8 @@
     }
 
     @Test
-    fun onInterceptTouchEvent_listenerNotified() {
+    @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_flagOff_listenerNotified() {
         val handler = TestTouchEventHandler()
         view.setTouchEventHandler(handler)
 
@@ -93,6 +90,66 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_flagOn_listenerNotified() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+        view.onInterceptTouchEvent(event)
+
+        assertThat(handler.lastInterceptEvent).isEqualTo(event)
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_listenerReturnsFalse_flagOff_viewReturnsFalse() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.handleTouchReturnValue = false
+
+        assertThat(view.onInterceptTouchEvent(event)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_listenerReturnsFalse_flagOn_viewReturnsFalse() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.handleTouchReturnValue = false
+
+        assertThat(view.onInterceptTouchEvent(event)).isFalse()
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_listenerReturnsTrue_flagOff_viewReturnsFalse() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.handleTouchReturnValue = true
+
+        assertThat(view.onInterceptTouchEvent(event)).isFalse()
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SWIPE_OVER_CHIP)
+    fun onInterceptTouchEvent_listenerReturnsTrue_flagOn_viewReturnsTrue() {
+        val handler = TestTouchEventHandler()
+        view.setTouchEventHandler(handler)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+
+        handler.handleTouchReturnValue = true
+
+        assertThat(view.onInterceptTouchEvent(event)).isTrue()
+    }
+
+    @Test
     fun onTouchEvent_listenerReturnsTrue_viewReturnsTrue() {
         val handler = TestTouchEventHandler()
         view.setTouchEventHandler(handler)
@@ -121,21 +178,40 @@
     }
 
     @Test
-    fun onAttachedToWindow_updatesWindowHeight() {
+    @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onAttachedToWindow_flagOff_updatesWindowHeight() {
         view.onAttachedToWindow()
 
         verify(windowController).refreshStatusBarHeight()
     }
 
     @Test
-    fun onConfigurationChanged_updatesWindowHeight() {
+    @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onAttachedToWindow_flagOn_doesNotUpdateWindowHeight() {
+        view.onAttachedToWindow()
+
+        verify(windowController, never()).refreshStatusBarHeight()
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onConfigurationChanged_flagOff_updatesWindowHeight() {
         view.onConfigurationChanged(Configuration())
 
         verify(windowController).refreshStatusBarHeight()
     }
 
     @Test
-    fun onConfigurationChanged_multipleCalls_updatesWindowHeightMultipleTimes() {
+    @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onConfigurationChanged_flagOn_doesNotUpdateWindowHeight() {
+        view.onConfigurationChanged(Configuration())
+
+        verify(windowController, never()).refreshStatusBarHeight()
+    }
+
+    @Test
+    @DisableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onConfigurationChanged_multipleCalls_flagOff_updatesWindowHeightMultipleTimes() {
         view.onConfigurationChanged(Configuration())
         view.onConfigurationChanged(Configuration())
         view.onConfigurationChanged(Configuration())
@@ -145,10 +221,20 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_STOP_UPDATING_WINDOW_HEIGHT)
+    fun onConfigurationChanged_multipleCalls_flagOn_neverUpdatesWindowHeight() {
+        view.onConfigurationChanged(Configuration())
+        view.onConfigurationChanged(Configuration())
+        view.onConfigurationChanged(Configuration())
+        view.onConfigurationChanged(Configuration())
+
+        verify(windowController, never()).refreshStatusBarHeight()
+    }
+
+    @Test
     fun onAttachedToWindow_updatesLeftTopRightPaddingsBasedOnInsets() {
         val insets = Insets.of(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(insets)
+        view.setInsetsFetcher { insets }
 
         view.onAttachedToWindow()
 
@@ -159,10 +245,39 @@
     }
 
     @Test
+    fun onAttachedToWindow_noInsetsFetcher_noCrash() {
+        // Don't call `PhoneStatusBarView.setInsetsFetcher`
+
+        // WHEN the view is attached
+        view.onAttachedToWindow()
+
+        // THEN there's no crash, and the padding stays as it was
+        assertThat(view.paddingLeft).isEqualTo(0)
+        assertThat(view.paddingTop).isEqualTo(0)
+        assertThat(view.paddingRight).isEqualTo(0)
+        assertThat(view.paddingBottom).isEqualTo(0)
+    }
+
+    @Test
+    fun onAttachedToWindow_thenGetsInsetsFetcher_insetsUpdated() {
+        view.onAttachedToWindow()
+
+        // WHEN the insets fetcher is set after the view is attached
+        val insets = Insets.of(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
+        view.setInsetsFetcher { insets }
+
+        // THEN the insets are updated
+        assertThat(view.paddingLeft).isEqualTo(insets.left)
+        assertThat(view.paddingTop).isEqualTo(insets.top)
+        assertThat(view.paddingRight).isEqualTo(insets.right)
+        assertThat(view.paddingBottom).isEqualTo(0)
+    }
+
+
+    @Test
     fun onConfigurationChanged_updatesLeftTopRightPaddingsBasedOnInsets() {
         val insets = Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(insets)
+        view.setInsetsFetcher { insets }
 
         view.onConfigurationChanged(Configuration())
 
@@ -173,17 +288,39 @@
     }
 
     @Test
+    fun onConfigurationChanged_noInsetsFetcher_noCrash() {
+        // Don't call `PhoneStatusBarView.setInsetsFetcher`
+
+        // WHEN the view is attached
+        view.onConfigurationChanged(Configuration())
+
+        // THEN there's no crash, and the padding stays as it was
+        assertThat(view.paddingLeft).isEqualTo(0)
+        assertThat(view.paddingTop).isEqualTo(0)
+        assertThat(view.paddingRight).isEqualTo(0)
+        assertThat(view.paddingBottom).isEqualTo(0)
+    }
+
+    @Test
     fun onConfigurationChanged_noRelevantChange_doesNotUpdateInsets() {
         val previousInsets =
             Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(previousInsets)
+        val newInsets = Insets.NONE
+
+        var useNewInsets = false
+        val insetsFetcher = PhoneStatusBarView.InsetsFetcher {
+            if (useNewInsets) {
+                newInsets
+            } else {
+                previousInsets
+            }
+        }
+        view.setInsetsFetcher(insetsFetcher)
+
         context.orCreateTestableResources.overrideConfiguration(Configuration())
         view.onAttachedToWindow()
 
-        val newInsets = Insets.NONE
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(newInsets)
+        useNewInsets = true
         view.onConfigurationChanged(Configuration())
 
         assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
@@ -196,16 +333,24 @@
     fun onConfigurationChanged_densityChanged_updatesInsets() {
         val previousInsets =
             Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(previousInsets)
+        val newInsets = Insets.NONE
+
+        var useNewInsets = false
+        val insetsFetcher = PhoneStatusBarView.InsetsFetcher {
+            if (useNewInsets) {
+                newInsets
+            } else {
+                previousInsets
+            }
+        }
+        view.setInsetsFetcher(insetsFetcher)
+
         val configuration = Configuration()
         configuration.densityDpi = 123
         context.orCreateTestableResources.overrideConfiguration(configuration)
         view.onAttachedToWindow()
 
-        val newInsets = Insets.NONE
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(newInsets)
+        useNewInsets = true
         configuration.densityDpi = 456
         view.onConfigurationChanged(configuration)
 
@@ -219,16 +364,24 @@
     fun onConfigurationChanged_fontScaleChanged_updatesInsets() {
         val previousInsets =
             Insets.of(/* left= */ 40, /* top= */ 30, /* right= */ 20, /* bottom= */ 10)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(previousInsets)
+        val newInsets = Insets.NONE
+
+        var useNewInsets = false
+        val insetsFetcher = PhoneStatusBarView.InsetsFetcher {
+            if (useNewInsets) {
+                newInsets
+            } else {
+                previousInsets
+            }
+        }
+        view.setInsetsFetcher(insetsFetcher)
+
         val configuration = Configuration()
         configuration.fontScale = 1f
         context.orCreateTestableResources.overrideConfiguration(configuration)
         view.onAttachedToWindow()
 
-        val newInsets = Insets.NONE
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(newInsets)
+        useNewInsets = true
         configuration.fontScale = 2f
         view.onConfigurationChanged(configuration)
 
@@ -254,8 +407,7 @@
     @Test
     fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
         val insets = Insets.of(/* left= */ 90, /* top= */ 10, /* right= */ 45, /* bottom= */ 50)
-        whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
-            .thenReturn(insets)
+        view.setInsetsFetcher { insets }
 
         view.onApplyWindowInsets(WindowInsets(Rect()))
 
@@ -296,7 +448,7 @@
             /* typeVisibilityMap = */ booleanArrayOf(),
             /* isRound = */ false,
             /* forceConsumingTypes = */ 0,
-            /* forceConsumingCaptionBar = */ false,
+            /* forceConsumingOpaqueCaptionBar = */ false,
             /* suppressScrimTypes = */ 0,
             /* displayCutout = */ DisplayCutout.NO_CUTOUT,
             /* roundedCorners = */ RoundedCorners.NO_ROUNDED_CORNERS,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 9b61105..3e3c046 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -83,10 +83,10 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.EnableSceneContainer;
+import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -108,7 +108,9 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.unfold.SysUIUnfoldComponent;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import com.google.common.truth.Truth;
 
@@ -169,12 +171,14 @@
     @Mock private SelectedUserInteractor mSelectedUserInteractor;
     @Mock private DeviceEntryInteractor mDeviceEntryInteractor;
     @Mock private SceneInteractor mSceneInteractor;
+    @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback
             mBouncerExpansionCallback;
     private FakeKeyguardStateController mKeyguardStateController =
             spy(new FakeKeyguardStateController());
+    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Mock
     private ViewRootImpl mViewRootImpl;
@@ -230,15 +234,16 @@
                         mUdfpsOverlayInteractor,
                         mActivityStarter,
                         mKeyguardTransitionInteractor,
+                        mock(KeyguardDismissTransitionInteractor.class),
                         StandardTestDispatcher(null, null),
-                        () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
                         () -> mock(KeyguardDismissActionInteractor.class),
                         mSelectedUserInteractor,
-                        () -> mock(KeyguardSurfaceBehindInteractor.class),
                         mock(JavaAdapter.class),
                         () -> mSceneInteractor,
                         mock(StatusBarKeyguardViewManagerInteractor.class),
-                        () -> mDeviceEntryInteractor) {
+                        mExecutor,
+                        () -> mDeviceEntryInteractor,
+                        mDismissCallbackRegistry) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
@@ -752,15 +757,16 @@
                         mUdfpsOverlayInteractor,
                         mActivityStarter,
                         mock(KeyguardTransitionInteractor.class),
+                        mock(KeyguardDismissTransitionInteractor.class),
                         StandardTestDispatcher(null, null),
-                        () -> mock(WindowManagerLockscreenVisibilityInteractor.class),
                         () -> mock(KeyguardDismissActionInteractor.class),
                         mSelectedUserInteractor,
-                        () -> mock(KeyguardSurfaceBehindInteractor.class),
                         mock(JavaAdapter.class),
                         () -> mSceneInteractor,
                         mock(StatusBarKeyguardViewManagerInteractor.class),
-                        () -> mDeviceEntryInteractor) {
+                        mExecutor,
+                        () -> mDeviceEntryInteractor,
+                        mDismissCallbackRegistry) {
                     @Override
                     public ViewRootImpl getViewRootImpl() {
                         return mViewRootImpl;
@@ -772,7 +778,11 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
+        reset(mDismissCallbackRegistry);
+        reset(mPrimaryBouncerInteractor);
+
         // GIVEN the keyguard is showing
         reset(mAlternateBouncerInteractor);
         when(mKeyguardStateController.isShowing()).thenReturn(true);
@@ -780,8 +790,10 @@
         // WHEN SBKV is reset with hideBouncerWhenShowing=true
         mStatusBarKeyguardViewManager.reset(true);
 
-        // THEN alternate bouncer is hidden
+        // THEN alternate bouncer is hidden and dismiss actions reset
         verify(mAlternateBouncerInteractor).hide();
+        verify(mDismissCallbackRegistry).notifyDismissCancelled();
+        verify(mPrimaryBouncerInteractor).setDismissAction(eq(null), eq(null));
     }
 
     @Test
@@ -1084,6 +1096,9 @@
                 .thenReturn(KeyguardState.LOCKSCREEN);
 
         reset(mCentralSurfaces);
+        // Advance past reattempts
+        mStatusBarKeyguardViewManager.setAttemptsToShowBouncer(10);
+
         mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
         verify(mPrimaryBouncerInteractor).show(true);
         verify(mCentralSurfaces).showKeyguard();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 9fa392f..7a34e94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -434,7 +434,11 @@
         // Then
         verify(mBubblesManager).onUserChangedBubble(entry, false);
 
-        verify(mHeadsUpManager).removeNotification(entry.getKey(), true);
+        verify(mHeadsUpManager).removeNotification(
+                entry.getKey(),
+                /* releaseImmediately= */ true,
+                /* reason= */ "onNotificationBubbleIconClicked"
+        );
 
         verifyNoMoreInteractions(mContentIntent);
         verifyNoMoreInteractions(mShadeController);
@@ -456,7 +460,11 @@
         // Then
         verify(mBubblesManager).onUserChangedBubble(entry, true);
 
-        verify(mHeadsUpManager).removeNotification(entry.getKey(), true);
+        verify(mHeadsUpManager).removeNotification(
+                entry.getKey(),
+                /* releaseImmediately= */ true,
+                /* reason= */ "onNotificationBubbleIconClicked"
+        );
 
         verify(mContentIntent, atLeastOnce()).isActivity();
         verifyNoMoreInteractions(mContentIntent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
index 230ddf9..48c2cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
@@ -56,11 +56,11 @@
             runCurrent()
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
 
-            sceneRepository.isRemoteUserInteractionOngoing.value = true
+            sceneRepository.isRemoteUserInputOngoing.value = true
             runCurrent()
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue()
 
-            sceneRepository.isRemoteUserInteractionOngoing.value = false
+            sceneRepository.isRemoteUserInputOngoing.value = false
             runCurrent()
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
         }
@@ -71,7 +71,7 @@
         testScope.runTest {
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
 
-            sceneRepository.isRemoteUserInteractionOngoing.value = true
+            sceneRepository.isRemoteUserInputOngoing.value = true
             runCurrent()
 
             assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index bea027f..6e337ef 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
@@ -432,8 +432,7 @@
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -445,8 +444,7 @@
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
@@ -460,8 +458,7 @@
         fragment.disable(DEFAULT_DISPLAY,
                 StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -474,8 +471,7 @@
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -487,22 +483,19 @@
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
 
         // Ongoing call ended
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
 
         // Ongoing call started
         when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -536,8 +529,7 @@
                 /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
 
         // THEN the old callback value is used, so the view is shown
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
 
         // WHEN there's *no* ongoing call via old callback
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -548,8 +540,7 @@
                 /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
 
         // THEN the old callback value is used, so the view is hidden
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -564,8 +555,7 @@
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
                 /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -576,8 +566,7 @@
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
                 /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
 
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
         assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
     }
 
@@ -592,8 +581,7 @@
         fragment.disable(DEFAULT_DISPLAY,
                 StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -607,8 +595,7 @@
 
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -620,15 +607,13 @@
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
                 /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
 
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
 
         // Ongoing activity ended
         mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
                 /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
 
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -661,8 +646,7 @@
                 /* hasOngoingActivity= */ false, /* shouldAnimate= */ false);
 
         // THEN the new callback value is used, so the view is hidden
-        assertEquals(View.GONE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.GONE, getPrimaryOngoingActivityChipView().getVisibility());
 
         // WHEN there's *no* ongoing call via old callback
         when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
@@ -673,8 +657,7 @@
                 /* hasOngoingActivity= */ true, /* shouldAnimate= */ false);
 
         // THEN the new callback value is used, so the view is shown
-        assertEquals(View.VISIBLE,
-                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility());
     }
 
     @Test
@@ -1023,4 +1006,8 @@
     private View getNotificationAreaView() {
         return mFragment.getView().findViewById(R.id.notificationIcons);
     }
+
+    private View getPrimaryOngoingActivityChipView() {
+        return mFragment.getView().findViewById(R.id.ongoing_activity_chip_primary);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
new file mode 100644
index 0000000..90732d01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.phone.ui
+
+import android.app.Flags
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.StatusBarIcon
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
+import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter
+import com.android.systemui.util.Assert
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.RETURNS_DEEP_STUBS
+import org.mockito.kotlin.mock
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class IconManagerTest : SysuiTestCase() {
+
+    private lateinit var underTest: IconManager
+    private lateinit var viewGroup: ViewGroup
+
+    @Before
+    fun setUp() {
+        Assert.setTestThread(Thread.currentThread())
+        viewGroup = LinearLayout(context)
+        underTest =
+            IconManager(
+                viewGroup,
+                StatusBarLocation.HOME,
+                mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
+                mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
+                mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS),
+            )
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun addIcon_shapeWrapContent_addsIconViewWithVariableWidth() {
+        val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.WRAP_CONTENT)
+
+        underTest.addIcon(0, "slot", false, sbIcon)
+
+        assertThat(viewGroup.childCount).isEqualTo(1)
+        val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+        assertThat(iconView).isNotNull()
+
+        assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+        assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
+    fun addIcon_shapeFixedSpace_addsIconViewWithFixedWidth() {
+        val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE)
+
+        underTest.addIcon(0, "slot", false, sbIcon)
+
+        assertThat(viewGroup.childCount).isEqualTo(1)
+        val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+        assertThat(iconView).isNotNull()
+
+        assertThat(iconView.layoutParams.width).isNotEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+        assertThat(iconView.layoutParams.width).isEqualTo(iconView.layoutParams.height)
+        assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.FIT_CENTER)
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
+    fun addIcon_iconsFlagOff_addsIconViewWithVariableWidth() {
+        val sbIcon = newStatusBarIcon(StatusBarIcon.Shape.FIXED_SPACE)
+
+        underTest.addIcon(0, "slot", false, sbIcon)
+
+        assertThat(viewGroup.childCount).isEqualTo(1)
+        val iconView = viewGroup.getChildAt(0) as StatusBarIconView
+        assertThat(iconView).isNotNull()
+
+        assertThat(iconView.layoutParams.width).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+        assertThat(iconView.scaleType).isEqualTo(ImageView.ScaleType.CENTER)
+    }
+
+    private fun newStatusBarIcon(shape: StatusBarIcon.Shape) =
+        StatusBarIcon(
+            UserHandle.CURRENT,
+            context.packageName,
+            Icon.createWithResource(context, android.R.drawable.ic_media_next),
+            0,
+            0,
+            "",
+            StatusBarIcon.Type.ResourceIcon,
+            shape,
+        )
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
index 19abbd5..50a13b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerImplTest.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar.phone.ui
 
+import android.graphics.drawable.ColorDrawable
 import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.statusbar.StatusBarIcon
@@ -406,6 +408,41 @@
             .isInstanceOf(StatusBarIconHolder.BindableIconHolder::class.java)
     }
 
+    @Test
+    fun setIcon_setsIconInHolder() {
+        underTest.setIcon("slot", 123, "description")
+
+        val iconHolder = iconList.getIconHolder("slot", 0)
+        assertThat(iconHolder).isNotNull()
+        assertThat(iconHolder?.icon?.pkg).isEqualTo(mContext.packageName)
+        assertThat(iconHolder?.icon?.icon?.resId).isEqualTo(123)
+        assertThat(iconHolder?.icon?.icon?.resPackage).isEqualTo(mContext.packageName)
+        assertThat(iconHolder?.icon?.contentDescription).isEqualTo("description")
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_UI, android.app.Flags.FLAG_MODES_UI_ICONS)
+    fun setResourceIcon_setsIconAndPreloadedIconInHolder() {
+        val drawable = ColorDrawable(1)
+        underTest.setResourceIcon(
+            "slot",
+            "some.package",
+            123,
+            drawable,
+            "description",
+            StatusBarIcon.Shape.FIXED_SPACE
+        )
+
+        val iconHolder = iconList.getIconHolder("slot", 0)
+        assertThat(iconHolder).isNotNull()
+        assertThat(iconHolder?.icon?.pkg).isEqualTo("some.package")
+        assertThat(iconHolder?.icon?.icon?.resId).isEqualTo(123)
+        assertThat(iconHolder?.icon?.icon?.resPackage).isEqualTo("some.package")
+        assertThat(iconHolder?.icon?.contentDescription).isEqualTo("description")
+        assertThat(iconHolder?.icon?.shape).isEqualTo(StatusBarIcon.Shape.FIXED_SPACE)
+        assertThat(iconHolder?.icon?.preloadedIcon).isEqualTo(drawable)
+    }
+
     private fun createExternalIcon(): StatusBarIcon {
         return StatusBarIcon(
             "external.package",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
index db3e533..7901f47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorTest.kt
@@ -19,13 +19,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -39,9 +37,9 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AirplaneModeInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
-    private val mobileConnectionsRepository =
-        FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), mock<TableLogBuffer> {})
+    private val mobileConnectionsRepository = kosmos.fakeMobileConnectionsRepository
     private val airplaneModeRepository = FakeAirplaneModeRepository()
     private val connectivityRepository = FakeConnectivityRepository()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index b823333..8beed01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -19,12 +19,13 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -35,7 +36,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -43,10 +43,11 @@
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @RunWith(AndroidJUnit4::class)
 class AirplaneModeViewModelImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: AirplaneModeViewModelImpl
 
-    @Mock private lateinit var logger: TableLogBuffer
+    private val logger = logcatTableLogBuffer(kosmos, "AirplaneModeViewModelImplTest")
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
     private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var interactor: AirplaneModeInteractor
@@ -61,7 +62,7 @@
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 connectivityRepository,
-                FakeMobileConnectionsRepository(),
+                kosmos.fakeMobileConnectionsRepository,
             )
         scope = CoroutineScope(IMMEDIATE)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 7d586cd..36f5236 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -42,11 +42,11 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.kotlinArgumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -56,7 +56,6 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
@@ -75,12 +74,13 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileRepositorySwitcherTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: MobileRepositorySwitcher
     private lateinit var realRepo: MobileConnectionsRepositoryImpl
     private lateinit var demoRepo: DemoMobileConnectionsRepository
     private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
     private lateinit var wifiDataSource: DemoModeWifiDataSource
-    private lateinit var logFactory: TableLogBufferFactory
     private lateinit var wifiRepository: FakeWifiRepository
     private lateinit var connectivityRepository: ConnectivityRepository
 
@@ -95,16 +95,12 @@
     private val mobileMappings = FakeMobileMappingsProxy()
     private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
 
-    private val testDispatcher = UnconfinedTestDispatcher()
     private val scope = CoroutineScope(IMMEDIATE)
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        logFactory =
-            TableLogBufferFactory(dumpManager, FakeSystemClock(), mock(), testDispatcher, scope)
-
         // Never start in demo mode
         whenever(demoModeController.isInDemoMode).thenReturn(false)
 
@@ -147,7 +143,7 @@
                 wifiDataSource = wifiDataSource,
                 scope = scope,
                 context = context,
-                logFactory = logFactory,
+                logFactory = kosmos.tableLogBufferFactory,
             )
 
         underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index db6f5927..7d32021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -23,16 +23,16 @@
 import com.android.settingslib.SignalIcon
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.Job
@@ -43,12 +43,11 @@
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.After
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
-import platform.test.runner.parameterized.Parameter
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
 
 /**
  * Parameterized test for all of the common values of [FakeNetworkEventModel]. This test simply
@@ -60,19 +59,11 @@
 @RunWith(ParameterizedAndroidJunit4::class)
 internal class DemoMobileConnectionParameterizedTest(private val testCase: TestCase) :
     SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
-    private val logFactory =
-        TableLogBufferFactory(
-            mock(),
-            FakeSystemClock(),
-            mock(),
-            testDispatcher,
-            testScope.backgroundScope,
-        )
-
     private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
     private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
 
@@ -99,7 +90,7 @@
                 wifiDataSource = mockWifiDataSource,
                 scope = testScope.backgroundScope,
                 context = context,
-                logFactory = logFactory,
+                logFactory = kosmos.tableLogBufferFactory,
             )
 
         connectionsRepo.startProcessingCommands()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 5e0d2fb..5017dda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -24,8 +24,7 @@
 import com.android.settingslib.SignalIcon
 import com.android.settingslib.mobile.TelephonyIcons.THREE_G
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.log.table.tableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -34,9 +33,9 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -56,21 +55,13 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
-    private val dumpManager: DumpManager = mock()
+    private val kosmos = testKosmos()
 
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
     private val fakeNetworkEventFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
     private val fakeWifiEventFlow = MutableStateFlow<FakeWifiEventModel?>(null)
-    private val logFactory =
-        TableLogBufferFactory(
-            dumpManager,
-            FakeSystemClock(),
-            mock(),
-            testDispatcher,
-            testScope.backgroundScope,
-        )
 
     private lateinit var underTest: DemoMobileConnectionsRepository
     private lateinit var mobileDataSource: DemoModeMobileConnectionDataSource
@@ -94,7 +85,7 @@
                 wifiDataSource = wifiDataSource,
                 scope = testScope.backgroundScope,
                 context = context,
-                logFactory = logFactory,
+                logFactory = kosmos.tableLogBufferFactory,
             )
 
         underTest.startProcessingCommands()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index 237aabc..b6e23c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -104,7 +104,7 @@
                 underTest.dataConnectionState.onEach { latestConnState = it }.launchIn(this)
             val netJob = underTest.resolvedNetworkType.onEach { latestNetType = it }.launchIn(this)
 
-            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = NET_ID, level = 1))
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
 
             assertThat(latestConnState).isEqualTo(DataConnectionState.Disconnected)
             assertThat(latestNetType).isNotEqualTo(ResolvedNetworkType.CarrierMergedNetworkType)
@@ -124,7 +124,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 3,
                 )
@@ -145,7 +144,6 @@
             wifiRepository.setIsWifiDefault(true)
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 3,
                 )
@@ -183,7 +181,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID + 10,
                     level = 3,
                 )
@@ -205,7 +202,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 3,
                 )
@@ -226,7 +222,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 3,
                 )
@@ -246,7 +241,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 1,
                     numberOfLevels = 6,
@@ -310,7 +304,6 @@
             whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId = NET_ID,
                     subscriptionId = SUB_ID,
                     level = 3,
                 )
@@ -331,6 +324,5 @@
 
     private companion object {
         const val SUB_ID = 123
-        const val NET_ID = 456
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index fd4c370..a03980a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -29,8 +29,8 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO
-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.log.table.tableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
@@ -42,11 +42,11 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.io.PrintWriter
 import java.io.StringWriter
@@ -73,23 +73,16 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FullMobileConnectionRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: FullMobileConnectionRepository
 
     private val flags =
         FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
 
-    private val systemClock = FakeSystemClock()
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
-    private val tableLogBuffer =
-        TableLogBuffer(
-            maxSize = 100,
-            name = "TestName",
-            systemClock,
-            mock(),
-            testDispatcher,
-            testScope.backgroundScope,
-        )
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "TestName")
     private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
     private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
     private val connectivityManager = mock<ConnectivityManager>()
@@ -372,19 +365,10 @@
     @Test
     fun factory_reusesLogBuffersForSameConnection() =
         testScope.runTest {
-            val realLoggerFactory =
-                TableLogBufferFactory(
-                    mock(),
-                    FakeSystemClock(),
-                    mock(),
-                    testDispatcher,
-                    testScope.backgroundScope,
-                )
-
             val factory =
                 FullMobileConnectionRepository.Factory(
                     scope = testScope.backgroundScope,
-                    realLoggerFactory,
+                    kosmos.tableLogBufferFactory,
                     mobileFactory,
                     carrierMergedFactory,
                 )
@@ -416,19 +400,10 @@
     @Test
     fun factory_reusesLogBuffersForSameSubIDevenIfCarrierMerged() =
         testScope.runTest {
-            val realLoggerFactory =
-                TableLogBufferFactory(
-                    mock(),
-                    FakeSystemClock(),
-                    mock(),
-                    testDispatcher,
-                    testScope.backgroundScope,
-                )
-
             val factory =
                 FullMobileConnectionRepository.Factory(
                     scope = testScope.backgroundScope,
-                    realLoggerFactory,
+                    kosmos.tableLogBufferFactory,
                     mobileFactory,
                     carrierMergedFactory,
                 )
@@ -512,10 +487,8 @@
             val job = underTest.primaryLevel.launchIn(this)
 
             // WHEN we set up carrier merged info
-            val networkId = 2
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 3,
                 )
@@ -527,7 +500,6 @@
             // WHEN we update the info
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 1,
                 )
@@ -565,10 +537,8 @@
             assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
 
             // WHEN isCarrierMerged is set to true
-            val networkId = 2
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 3,
                 )
@@ -581,7 +551,6 @@
             // WHEN the carrier merge network is updated
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 4,
                 )
@@ -632,10 +601,8 @@
                 .onSignalStrengthsChanged(signalStrength)
 
             // THEN updates to the carrier merged level aren't logged
-            val networkId = 2
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 4,
                 )
@@ -644,7 +611,6 @@
 
             wifiRepository.setWifiNetwork(
                 WifiNetworkModel.CarrierMerged(
-                    networkId,
                     SUB_ID,
                     level = 3,
                 )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 8fd0b31..fe408e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.content.Intent
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager.NetworkCallback
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN
 import android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN
@@ -59,6 +60,7 @@
 import android.telephony.TelephonyManager.ERI_OFF
 import android.telephony.TelephonyManager.ERI_ON
 import android.telephony.TelephonyManager.EXTRA_CARRIER_ID
+import android.telephony.TelephonyManager.EXTRA_DATA_SPN
 import android.telephony.TelephonyManager.EXTRA_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
 import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
@@ -69,6 +71,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.mobile.MobileMappings
+import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
@@ -85,16 +88,12 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.configWithOverride
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest.Companion.createTestConfig
-import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.mockito.withArgCaptor
@@ -112,6 +111,8 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -119,7 +120,6 @@
 @RunWith(AndroidJUnit4::class)
 class MobileConnectionRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: MobileConnectionRepositoryImpl
-    private lateinit var connectionsRepo: FakeMobileConnectionsRepository
 
     private val flags =
         FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
@@ -154,8 +154,6 @@
         MockitoAnnotations.initMocks(this)
         whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
 
-        connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
-
         underTest =
             MobileConnectionRepositoryImpl(
                 SUB_1_ID,
@@ -807,6 +805,7 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
     fun networkName_usesBroadcastInfo_returnsDerived() =
         testScope.runTest {
             var latest: NetworkNameModel? = null
@@ -815,14 +814,34 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            // spnIntent() sets all values to true and test strings
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
 
             job.cancel()
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_usesBroadcastInfo_returnsDerived_flagOff() =
+        testScope.runTest {
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            val intent = spnIntent()
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            captor.lastValue.onReceive(context, intent)
+
+            // spnIntent() sets all values to true and test strings
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+            job.cancel()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
     fun networkName_broadcastNotForThisSubId_keepsOldValue() =
         testScope.runTest {
             var latest: NetworkNameModel? = null
@@ -831,22 +850,48 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
 
             // WHEN an intent with a different subId is sent
             val wrongSubIntent = spnIntent(subId = 101)
 
-            captor.value!!.onReceive(context, wrongSubIntent)
+            captor.lastValue.onReceive(context, wrongSubIntent)
 
             // THEN the previous intent's name is still used
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
 
             job.cancel()
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_broadcastNotForThisSubId_keepsOldValue_flagOff() =
+        testScope.runTest {
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            val intent = spnIntent()
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            captor.lastValue.onReceive(context, intent)
+
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+            // WHEN an intent with a different subId is sent
+            val wrongSubIntent = spnIntent(subId = 101)
+
+            captor.lastValue.onReceive(context, wrongSubIntent)
+
+            // THEN the previous intent's name is still used
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+            job.cancel()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
     fun networkName_broadcastHasNoData_updatesToDefault() =
         testScope.runTest {
             var latest: NetworkNameModel? = null
@@ -855,9 +900,9 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
-            assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
 
             val intentWithoutInfo =
                 spnIntent(
@@ -865,7 +910,7 @@
                     showPlmn = false,
                 )
 
-            captor.value!!.onReceive(context, intentWithoutInfo)
+            captor.lastValue.onReceive(context, intentWithoutInfo)
 
             assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
 
@@ -873,6 +918,34 @@
         }
 
     @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_broadcastHasNoData_updatesToDefault_flagOff() =
+        testScope.runTest {
+            var latest: NetworkNameModel? = null
+            val job = underTest.networkName.onEach { latest = it }.launchIn(this)
+
+            val intent = spnIntent()
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            captor.lastValue.onReceive(context, intent)
+
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+
+            val intentWithoutInfo =
+                spnIntent(
+                    showSpn = false,
+                    showPlmn = false,
+                )
+
+            captor.lastValue.onReceive(context, intentWithoutInfo)
+
+            assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+
+            job.cancel()
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
     fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers() =
         testScope.runTest {
             // Use the [StateFlow.value] getter so we can prove that the collection happens
@@ -884,10 +957,172 @@
             val intent = spnIntent()
             val captor = argumentCaptor<BroadcastReceiver>()
             verify(context).registerReceiver(captor.capture(), any())
-            captor.value!!.onReceive(context, intent)
+            captor.lastValue.onReceive(context, intent)
 
             // The value is still there despite no active subscribers
-            assertThat(underTest.networkName.value).isEqualTo(intent.toNetworkNameModel(SEP))
+            assertThat(underTest.networkName.value)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_usingEagerStrategy_retainsNameBetweenSubscribers_flagOff() =
+        testScope.runTest {
+            // Use the [StateFlow.value] getter so we can prove that the collection happens
+            // even when there is no [Job]
+
+            // Starts out default
+            assertThat(underTest.networkName.value).isEqualTo(DEFAULT_NAME_MODEL)
+
+            val intent = spnIntent()
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            captor.lastValue.onReceive(context, intent)
+
+            // The value is still there despite no active subscribers
+            assertThat(underTest.networkName.value)
+                .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_allFieldsSet_doesNotUseDataSpn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_allFieldsSet_flagOff() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = null,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_showPlmn_plmnNotNull_showSpn_spnNull_dataSpnNotNull_flagOff() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = null,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+        }
+
+    @Test
+    fun networkName_showPlmn_noShowSPN() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = false,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = PLMN,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_showPlmn_plmnNull_showSpn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = null,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN"))
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+    fun networkName_showPlmn_plmnNull_showSpn_flagOff() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.networkName)
+            val captor = argumentCaptor<BroadcastReceiver>()
+            verify(context).registerReceiver(captor.capture(), any())
+            val intent =
+                spnIntent(
+                    subId = SUB_1_ID,
+                    showSpn = true,
+                    spn = SPN,
+                    dataSpn = DATA_SPN,
+                    showPlmn = true,
+                    plmn = null,
+                )
+            captor.lastValue.onReceive(context, intent)
+            assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$DATA_SPN"))
         }
 
     @Test
@@ -1128,14 +1363,16 @@
     private fun spnIntent(
         subId: Int = SUB_1_ID,
         showSpn: Boolean = true,
-        spn: String = SPN,
+        spn: String? = SPN,
+        dataSpn: String? = DATA_SPN,
         showPlmn: Boolean = true,
-        plmn: String = PLMN,
+        plmn: String? = PLMN,
     ): Intent =
         Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
             putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
             putExtra(EXTRA_SHOW_SPN, showSpn)
             putExtra(EXTRA_SPN, spn)
+            putExtra(EXTRA_DATA_SPN, dataSpn)
             putExtra(EXTRA_SHOW_PLMN, showPlmn)
             putExtra(EXTRA_PLMN, plmn)
         }
@@ -1148,6 +1385,7 @@
         private const val SEP = "-"
 
         private const val SPN = "testSpn"
+        private const val DATA_SPN = "testDataSpn"
         private const val PLMN = "testPlmn"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 2ab8c0a..0d82c79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -42,7 +42,6 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfigTest
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -97,7 +96,6 @@
 @SmallTest
 class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
     private lateinit var underTest: MobileConnectionRepositoryImpl
-    private lateinit var connectionsRepo: FakeMobileConnectionsRepository
 
     private val flags =
         FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
@@ -123,12 +121,6 @@
         MockitoAnnotations.initMocks(this)
         whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
 
-        connectionsRepo =
-            FakeMobileConnectionsRepository(
-                mobileMappings,
-                tableLogger,
-            )
-
         underTest =
             MobileConnectionRepositoryImpl(
                 SUB_1_ID,
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 76982ae..4b6e313 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
@@ -28,7 +28,6 @@
 import android.net.vcn.VcnTransportInfo
 import android.net.wifi.WifiInfo
 import android.net.wifi.WifiManager
-import android.os.Bundle
 import android.os.ParcelUuid
 import android.telephony.CarrierConfigManager
 import android.telephony.ServiceState
@@ -56,7 +55,6 @@
 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
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -74,7 +72,6 @@
 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.android.systemui.util.time.FakeSystemClock
 import com.android.wifitrackerlib.MergedCarrierEntry
 import com.android.wifitrackerlib.WifiEntry
@@ -98,6 +95,7 @@
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -108,11 +106,7 @@
 class MobileConnectionsRepositoryTest : SysuiTestCase() {
 
     private val flags =
-        FakeFeatureFlagsClassic().also {
-            it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
-            it.set(Flags.INSTANT_TETHER, true)
-            it.set(Flags.WIFI_SECONDARY_NETWORKS, true)
-        }
+        FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
 
     private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
     private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
@@ -191,7 +185,6 @@
 
         wifiRepository =
             WifiRepositoryImpl(
-                flags,
                 testScope.backgroundScope,
                 mainExecutor,
                 testDispatcher,
@@ -602,47 +595,85 @@
 
     @SuppressLint("UnspecifiedRegisterReceiverFlag")
     @Test
-    fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() =
+    fun testDeviceEmergencyCallState_eagerlyChecksState() =
         testScope.runTest {
-            // Value starts out empty (null)
-            assertThat(underTest.deviceServiceState.value).isNull()
+            // Value starts out false
+            assertThat(underTest.isDeviceEmergencyCallCapable.value).isFalse()
+            whenever(telephonyManager.activeModemCount).thenReturn(1)
+            whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { _ ->
+                ServiceState().apply { isEmergencyOnly = true }
+            }
 
             // WHEN an appropriate intent gets sent out
-            val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+            val intent = serviceStateIntent(subId = -1)
             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
                 context,
                 intent,
             )
             runCurrent()
 
-            // THEN the repo's state is updated
-            val expected = ServiceStateModel(isEmergencyOnly = false)
-            assertThat(underTest.deviceServiceState.value).isEqualTo(expected)
+            // THEN the repo's state is updated despite no listeners
+            assertThat(underTest.isDeviceEmergencyCallCapable.value).isEqualTo(true)
         }
 
     @Test
-    fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() =
+    fun testDeviceEmergencyCallState_aggregatesAcrossSlots_oneTrue() =
         testScope.runTest {
-            // device based state tracks -1
-            val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+            val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable)
+
+            // GIVEN there are multiple slots
+            whenever(telephonyManager.activeModemCount).thenReturn(4)
+            // GIVEN only one of them reports ECM
+            whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation ->
+                when (invocation.getArgument(0) as Int) {
+                    0 -> ServiceState().apply { isEmergencyOnly = false }
+                    1 -> ServiceState().apply { isEmergencyOnly = false }
+                    2 -> ServiceState().apply { isEmergencyOnly = true }
+                    3 -> ServiceState().apply { isEmergencyOnly = false }
+                    else -> null
+                }
+            }
+
+            // GIVEN a broadcast goes out for the appropriate subID
+            val intent = serviceStateIntent(subId = -1)
             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
                 context,
                 intent,
             )
             runCurrent()
 
-            val deviceBasedState = ServiceStateModel(isEmergencyOnly = false)
-            assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+            // THEN the device is in ECM, because one of the service states is
+            assertThat(latest).isTrue()
+        }
 
-            // ... and ignores any other subId
-            val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true)
+    @Test
+    fun testDeviceEmergencyCallState_aggregatesAcrossSlots_allFalse() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.isDeviceEmergencyCallCapable)
+
+            // GIVEN there are multiple slots
+            whenever(telephonyManager.activeModemCount).thenReturn(4)
+            // GIVEN only one of them reports ECM
+            whenever(telephonyManager.getServiceStateForSlot(any())).thenAnswer { invocation ->
+                when (invocation.getArgument(0) as Int) {
+                    0 -> ServiceState().apply { isEmergencyOnly = false }
+                    1 -> ServiceState().apply { isEmergencyOnly = false }
+                    2 -> ServiceState().apply { isEmergencyOnly = false }
+                    3 -> ServiceState().apply { isEmergencyOnly = false }
+                    else -> null
+                }
+            }
+
+            // GIVEN a broadcast goes out for the appropriate subID
+            val intent = serviceStateIntent(subId = -1)
             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
                 context,
-                intent2,
+                intent,
             )
             runCurrent()
 
-            assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+            // THEN the device is in ECM, because one of the service states is
+            assertThat(latest).isFalse()
         }
 
     @Test
@@ -1549,15 +1580,8 @@
          */
         private fun serviceStateIntent(
             subId: Int,
-            emergencyOnly: Boolean = false,
         ): Intent {
-            val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly }
-
-            val bundle = Bundle()
-            serviceState.fillInNotifierBundle(bundle)
-
             return Intent(Intent.ACTION_SERVICE_STATE).apply {
-                putExtras(bundle)
                 putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId)
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index e439aff..4fd830d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -18,7 +18,6 @@
 
 import android.platform.test.annotations.EnableFlags
 import android.telephony.CellSignalStrength
-import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -27,12 +26,12 @@
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.CarrierMergedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
@@ -40,12 +39,12 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 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
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.test.TestScope
@@ -61,21 +60,18 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: MobileIconInteractor
     private val mobileMappingsProxy = FakeMobileMappingsProxy()
     private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
 
-    private val subscriptionModel =
-        MutableStateFlow(
-            SubscriptionModel(
-                subscriptionId = SUB_1_ID,
-                carrierName = DEFAULT_NAME,
-                profileClass = PROFILE_CLASS_UNSET,
-            )
+    private val connectionRepository =
+        FakeMobileConnectionRepository(
+            SUB_1_ID,
+            logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"),
         )
 
-    private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock())
-
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index cc0eae7..f6d439a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -28,8 +28,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -37,10 +36,10 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -59,6 +58,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconsInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: MobileIconsInteractor
     private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var connectionsRepository: FakeMobileConnectionsRepository
@@ -72,15 +73,7 @@
     private val testDispatcher = StandardTestDispatcher()
     private val testScope = TestScope(testDispatcher)
 
-    private val tableLogBuffer =
-        TableLogBuffer(
-            8,
-            "MobileIconsInteractorTest",
-            FakeSystemClock(),
-            mock(),
-            testDispatcher,
-            testScope.backgroundScope,
-        )
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconsInteractorTest")
 
     @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
 
@@ -897,13 +890,11 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
 
-            connectionsRepository.deviceServiceState.value =
-                ServiceStateModel(isEmergencyOnly = true)
+            connectionsRepository.isDeviceEmergencyCallCapable.value = true
 
             assertThat(latest).isTrue()
 
-            connectionsRepository.deviceServiceState.value =
-                ServiceStateModel(isEmergencyOnly = false)
+            connectionsRepository.isDeviceEmergencyCallCapable.value = false
 
             assertThat(latest).isFalse()
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 42cb660..84846a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -28,12 +28,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,13 +59,13 @@
 @RunWithLooper(setAsMainLooper = true)
 @OptIn(ExperimentalCoroutinesApi::class)
 class ModernStatusBarMobileViewTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var testableLooper: TestableLooper
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
     private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
 
-    @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var viewLogger: MobileViewLogger
     @Mock private lateinit var constants: ConnectivityConstants
     private lateinit var interactor: FakeMobileIconInteractor
@@ -88,10 +89,11 @@
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 FakeConnectivityRepository(),
-                FakeMobileConnectionsRepository(),
+                kosmos.fakeMobileConnectionsRepository,
             )
 
-        interactor = FakeMobileIconInteractor(tableLogBuffer)
+        interactor =
+            FakeMobileIconInteractor(logcatTableLogBuffer(kosmos, "ModernStatusBarMobileViewTest"))
         createViewModel()
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index deb9fcf..f99fcac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -21,24 +21,23 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -59,13 +58,15 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var commonImpl: MobileIconViewModelCommon
     private lateinit var homeIcon: HomeMobileIconViewModel
     private lateinit var qsIcon: QsMobileIconViewModel
     private lateinit var keyguardIcon: KeyguardMobileIconViewModel
     private lateinit var iconsInteractor: MobileIconsInteractor
     private lateinit var interactor: MobileIconInteractor
-    private lateinit var connectionsRepository: FakeMobileConnectionsRepository
+    private val connectionsRepository = kosmos.fakeMobileConnectionsRepository
     private lateinit var repository: FakeMobileConnectionRepository
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
 
@@ -76,9 +77,9 @@
             it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
         }
 
-    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var constants: ConnectivityConstants
-    @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    private val tableLogBuffer =
+        logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest")
     @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
 
     private val testDispatcher = UnconfinedTestDispatcher()
@@ -91,10 +92,8 @@
             AirplaneModeInteractor(
                 FakeAirplaneModeRepository(),
                 FakeConnectivityRepository(),
-                FakeMobileConnectionsRepository(),
+                connectionsRepository,
             )
-        connectionsRepository =
-            FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
         repository =
             FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
                 isInService.value = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index e510924..4c7cdfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.Flags.NEW_NETWORK_SLICE_UI
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
@@ -51,6 +52,7 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -74,6 +76,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private var connectivityRepository = FakeConnectivityRepository()
 
     private lateinit var underTest: MobileIconViewModel
@@ -84,7 +88,7 @@
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var constants: ConnectivityConstants
-    @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconViewModelTest")
     @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
 
     private val flags =
@@ -118,7 +122,7 @@
             AirplaneModeInteractor(
                 airplaneModeRepository,
                 connectivityRepository,
-                FakeMobileConnectionsRepository(),
+                kosmos.fakeMobileConnectionsRepository,
             )
 
         iconsInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 47899a6..31ba837 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -24,11 +24,10 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
@@ -36,6 +35,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertFalse
@@ -58,12 +58,13 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class MobileIconsViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: MobileIconsViewModel
     private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
     private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
 
     private lateinit var airplaneModeInteractor: AirplaneModeInteractor
-    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var constants: ConnectivityConstants
     @Mock private lateinit var logger: MobileViewLogger
     @Mock private lateinit var verboseLogger: VerboseMobileViewLogger
@@ -79,7 +80,7 @@
             AirplaneModeInteractor(
                 FakeAirplaneModeRepository(),
                 FakeConnectivityRepository(),
-                FakeMobileConnectionsRepository(),
+                kosmos.fakeMobileConnectionsRepository,
             )
 
         underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index cd0390e..a1cb29b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -70,6 +70,7 @@
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
     }
 
@@ -113,6 +114,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.isSatelliteAllowed)
@@ -161,6 +163,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.connectionState)
@@ -217,6 +220,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.signalStrength)
@@ -535,6 +539,7 @@
                     wifiInteractor,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
+                    mock(),
                 )
 
             val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
@@ -569,7 +574,7 @@
             val latest by collectLastValue(underTest.isWifiActive)
 
             // WHEN wifi is active
-            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
 
             // THEN the interactor returns true due to the wifi network being active
             assertThat(latest).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index 64b07fc..c1abf98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlin.time.Duration.Companion.seconds
@@ -44,7 +43,9 @@
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class DeviceBasedSatelliteViewModelTest : SysuiTestCase() {
@@ -73,6 +74,7 @@
                 wifiInteractor,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
 
         underTest =
@@ -82,11 +84,12 @@
                 testScope.backgroundScope,
                 airplaneModeRepository,
                 FakeLogBuffer.Factory.create(),
+                mock(),
             )
     }
 
     @Test
-    fun icon_nullWhenShouldNotShow_satelliteNotAllowed() =
+    fun icon_null_satelliteNotAllowed() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -106,7 +109,30 @@
         }
 
     @Test
-    fun icon_nullWhenShouldNotShow_notAllOos() =
+    fun icon_null_connectedAndNotAllowed() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is not allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = false
+
+            // GIVEN all icons are OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN satellite state is Connected. (this should not ever occur, but still)
+            repo.connectionState.value = SatelliteConnectionState.Connected
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // THEN icon is null despite the connected state
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun icon_null_notAllOos() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -125,9 +151,28 @@
             assertThat(latest).isNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun icon_nullWhenShouldNotShow_isEmergencyOnly() =
+    fun icon_null_allOosAndNotAllowed() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = false
+
+            // GIVEN all icons are OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // THEN icon is null because it is not allowed
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun icon_null_isEmergencyOnly() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -156,7 +201,7 @@
         }
 
     @Test
-    fun icon_nullWhenShouldNotShow_apmIsEnabled() =
+    fun icon_null_apmIsEnabled() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -175,9 +220,8 @@
             assertThat(latest).isNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun icon_satelliteIsOn() =
+    fun icon_notNull_satelliteAllowedAndAllOos() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -199,7 +243,6 @@
             assertThat(latest).isInstanceOf(Icon::class.java)
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun icon_hysteresisWhenEnablingIcon() =
         testScope.runTest {
@@ -232,9 +275,56 @@
             assertThat(latest).isNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun icon_deviceIsProvisioned() =
+    fun icon_ignoresHysteresis_whenConnected() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // GIVEN all icons are OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // GIVEN satellite reports that it is Connected
+            repo.connectionState.value = SatelliteConnectionState.Connected
+
+            // THEN icon is non null because we are connected, despite the normal OOS icon waiting
+            // 10 seconds for hysteresis
+            assertThat(latest).isInstanceOf(Icon::class.java)
+        }
+
+    @Test
+    fun icon_ignoresHysteresis_whenOn() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.icon)
+
+            // GIVEN satellite is allowed
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+
+            // GIVEN all icons are OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = false
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // GIVEN satellite reports that it is Connected
+            repo.connectionState.value = SatelliteConnectionState.On
+
+            // THEN icon is non null because the connection state is On, despite the normal OOS icon
+            // waiting 10 seconds for hysteresis
+            assertThat(latest).isInstanceOf(Icon::class.java)
+        }
+
+    @Test
+    fun icon_satelliteIsProvisioned() =
         testScope.runTest {
             val latest by collectLastValue(underTest.icon)
 
@@ -265,7 +355,6 @@
             assertThat(latest).isInstanceOf(Icon::class.java)
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun icon_wifiIsActive() =
         testScope.runTest {
@@ -286,7 +375,7 @@
             repo.isSatelliteProvisioned.value = true
 
             // GIVEN wifi network is active
-            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
 
             // THEN icon is null because the device is connected to wifi
             assertThat(latest).isNull()
@@ -322,7 +411,28 @@
         }
 
     @Test
-    fun carrierText_nullWhenShouldNotShow_notAllOos() =
+    fun carrierText_null_notAllOos() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.carrierText)
+
+            // GIVEN satellite is allowed + off
+            repo.isSatelliteAllowedForCurrentLocation.value = true
+            repo.connectionState.value = SatelliteConnectionState.Off
+
+            // GIVEN all icons are not OOS
+            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
+            i1.isInService.value = true
+            i1.isEmergencyOnly.value = false
+
+            // GIVEN apm is disabled
+            airplaneModeRepository.setIsAirplaneMode(false)
+
+            // THEN carrier text is null because we have service
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun carrierText_notNull_notAllOos_butConnected() =
         testScope.runTest {
             val latest by collectLastValue(underTest.carrierText)
 
@@ -338,39 +448,9 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // THEN carrier text is null because we have service
-            assertThat(latest).isNull()
-        }
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    @Test
-    fun carrierText_nullWhenShouldNotShow_isEmergencyOnly() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.carrierText)
-
-            // GIVEN satellite is allowed + connected
-            repo.isSatelliteAllowedForCurrentLocation.value = true
-            repo.connectionState.value = SatelliteConnectionState.Connected
-
-            // GIVEN all icons are OOS
-            val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1)
-            i1.isInService.value = false
-            i1.isEmergencyOnly.value = false
-
-            // GIVEN apm is disabled
-            airplaneModeRepository.setIsAirplaneMode(false)
-
-            // Wait for delay to be completed
-            advanceTimeBy(10.seconds)
-
-            // THEN carrier text is set because we don't have service
+            // THEN carrier text is not null, because it is connected
+            // This case should never happen, but let's test it anyway
             assertThat(latest).isNotNull()
-
-            // GIVEN the connection is emergency only
-            i1.isEmergencyOnly.value = true
-
-            // THEN carrier text is null because we have emergency connection
-            assertThat(latest).isNull()
         }
 
     @Test
@@ -394,7 +474,6 @@
             assertThat(latest).isNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun carrierText_satelliteIsOn() =
         testScope.runTest {
@@ -419,9 +498,8 @@
             assertThat(latest).isNotNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun carrierText_hysteresisWhenEnablingText() =
+    fun carrierText_noHysteresisWhenEnablingText_connected() =
         testScope.runTest {
             val latest by collectLastValue(underTest.carrierText)
 
@@ -437,23 +515,10 @@
             // GIVEN apm is disabled
             airplaneModeRepository.setIsAirplaneMode(false)
 
-            // THEN carrier text is null because of the hysteresis
-            assertThat(latest).isNull()
-
-            // Wait for delay to be completed
-            advanceTimeBy(10.seconds)
-
-            // THEN carrier text is set after the delay
+            // THEN carrier text is not null because we skip hysteresis when connected
             assertThat(latest).isNotNull()
-
-            // GIVEN apm is enabled
-            airplaneModeRepository.setIsAirplaneMode(true)
-
-            // THEN carrier text is null immediately
-            assertThat(latest).isNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun carrierText_deviceIsProvisioned() =
         testScope.runTest {
@@ -487,7 +552,6 @@
             assertThat(latest).isNotNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun carrierText_wifiIsActive() =
         testScope.runTest {
@@ -509,7 +573,7 @@
             repo.isSatelliteProvisioned.value = true
 
             // GIVEN wifi network is active
-            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1))
+            wifiRepository.setWifiNetwork(WifiNetworkModel.Active(level = 1))
 
             // THEN carrier text is null because the device is connected to wifi
             assertThat(latest).isNull()
@@ -524,9 +588,8 @@
             assertThat(latest).isNotNull()
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun carrierText_connectionStateUnknown_null() =
+    fun carrierText_connectionStateUnknown_usesEmergencyOnlyText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.carrierText)
 
@@ -542,12 +605,12 @@
             // Wait for delay to be completed
             advanceTimeBy(10.seconds)
 
-            assertThat(latest).isNull()
+            assertThat(latest)
+                .isEqualTo(context.getString(R.string.satellite_emergency_only_carrier_text))
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
-    fun carrierText_connectionStateOff_null() =
+    fun carrierText_connectionStateOff_usesEmergencyOnlyText() =
         testScope.runTest {
             val latest by collectLastValue(underTest.carrierText)
 
@@ -563,10 +626,10 @@
             // Wait for delay to be completed
             advanceTimeBy(10.seconds)
 
-            assertThat(latest).isNull()
+            assertThat(latest)
+                .isEqualTo(context.getString(R.string.satellite_emergency_only_carrier_text))
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun carrierText_connectionStateOn_notConnectedString() =
         testScope.runTest {
@@ -588,7 +651,6 @@
                 .isEqualTo(context.getString(R.string.satellite_connected_carrier_text))
         }
 
-    @OptIn(ExperimentalCoroutinesApi::class)
     @Test
     fun carrierText_connectionStateConnected_connectedString() =
         testScope.runTest {
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 f486787..0945742 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
@@ -21,60 +21,61 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
+import android.net.NetworkCapabilities.TRANSPORT_VPN
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.VpnTransportInfo
 import android.net.vcn.VcnTransportInfo
 import android.net.wifi.WifiInfo
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityInputLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
-import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.getMainOrUnderlyingWifiInfo
+import com.android.systemui.testKosmos
 import com.android.systemui.tuner.TunerService
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.yield
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class ConnectivityRepositoryImplTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: ConnectivityRepositoryImpl
 
-    @Mock private lateinit var connectivityManager: ConnectivityManager
-    @Mock private lateinit var connectivitySlots: ConnectivitySlots
-    @Mock private lateinit var dumpManager: DumpManager
-    @Mock private lateinit var logger: ConnectivityInputLogger
-    private lateinit var testScope: TestScope
-    @Mock private lateinit var tunerService: TunerService
+    private val connectivityManager = mock<ConnectivityManager>()
+    private val connectivitySlots = mock<ConnectivitySlots>()
+    private val dumpManager = kosmos.dumpManager
+    private val logger = ConnectivityInputLogger(FakeLogBuffer.Factory.create())
+    private val testScope = kosmos.testScope
+    private val tunerService = mock<TunerService>()
 
     @Before
     fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        testScope = TestScope(UnconfinedTestDispatcher())
         createAndSetRepo()
     }
 
@@ -89,12 +90,10 @@
             // config_statusBarIconsToExclude when it's first constructed
             createAndSetRepo()
 
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
-
-            job.cancel()
         }
 
     @Test
@@ -102,14 +101,12 @@
         testScope.runTest {
             setUpEthernetWifiMobileSlotNames()
 
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
             assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
-
-            job.cancel()
         }
 
     @Test
@@ -117,19 +114,16 @@
         testScope.runTest {
             setUpEthernetWifiMobileSlotNames()
 
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
 
             // WHEN onTuningChanged with the wrong key
             getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
-            yield()
 
             // THEN we didn't update our value and still have the old one
             assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
-
-            job.cancel()
         }
 
     @Test
@@ -143,8 +137,8 @@
             // config_statusBarIconsToExclude when it's first constructed
             createAndSetRepo()
 
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             // First, update the slots
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
@@ -152,19 +146,16 @@
 
             // WHEN we update to a null value
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
-            yield()
 
             // THEN we go back to our default value
             assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
-
-            job.cancel()
         }
 
     @Test
     fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() =
         testScope.runTest {
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(ConnectivitySlot.WIFI)
             whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
@@ -172,8 +163,6 @@
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
 
             assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
-
-            job.cancel()
         }
 
     @Test
@@ -181,23 +170,21 @@
         testScope.runTest {
             setUpEthernetWifiMobileSlotNames()
 
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             // WHEN there's empty and blank slot names
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,  ,,$SLOT_WIFI")
 
             // THEN we skip that slot but still process the other ones
             assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
-
-            job.cancel()
         }
 
     @Test
     fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() =
         testScope.runTest {
-            var latest: Set<ConnectivitySlot>? = null
-            val job = underTest.forceHiddenSlots.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
             whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
@@ -210,8 +197,6 @@
                 )
 
             assertThat(latest).isEmpty()
-
-            job.cancel()
         }
 
     @Test
@@ -219,29 +204,25 @@
         testScope.runTest {
             setUpEthernetWifiMobileSlotNames()
 
-            var latest1: Set<ConnectivitySlot>? = null
-            val job1 = underTest.forceHiddenSlots.onEach { latest1 = it }.launchIn(this)
+            val latest1 by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
 
             assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
 
             // WHEN we add a second subscriber after having already emitted a value
-            var latest2: Set<ConnectivitySlot>? = null
-            val job2 = underTest.forceHiddenSlots.onEach { latest2 = it }.launchIn(this)
+            val latest2 by collectLastValue(underTest.forceHiddenSlots)
+            runCurrent()
 
             // THEN the second subscribe receives the already-emitted value
             assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
-
-            job1.cancel()
-            job2.cancel()
         }
 
     @Test
     fun defaultConnections_noTransports_nothingIsDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -256,15 +237,12 @@
             assertThat(latest!!.wifi.isDefault).isFalse()
             assertThat(latest!!.ethernet.isDefault).isFalse()
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_cellularTransport_mobileIsDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -279,15 +257,12 @@
             assertThat(latest!!.wifi.isDefault).isFalse()
             assertThat(latest!!.ethernet.isDefault).isFalse()
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_wifiTransport_wifiIsDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -302,15 +277,12 @@
             assertThat(latest!!.ethernet.isDefault).isFalse()
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
             assertThat(latest!!.mobile.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_ethernetTransport_ethernetIsDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -325,15 +297,12 @@
             assertThat(latest!!.wifi.isDefault).isFalse()
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
             assertThat(latest!!.mobile.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_carrierMergedViaWifi_wifiAndCarrierMergedDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -350,15 +319,12 @@
             assertThat(latest!!.wifi.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.mobile.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_carrierMergedViaMobile_mobileCarrierMergedWifiDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -375,15 +341,12 @@
             assertThat(latest!!.mobile.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.wifi.isDefault).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_carrierMergedViaWifiWithVcnTransport_wifiAndCarrierMergedDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -400,15 +363,13 @@
             assertThat(latest!!.wifi.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.mobile.isDefault).isFalse()
-
-            job.cancel()
         }
 
+    /** VCN over W+ (aka VCN over carrier merged). See b/352162710#comment27 scenario #1. */
     @Test
     fun defaultConnections_carrierMergedViaMobileWithVcnTransport_mobileCarrierMergedWifiDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
@@ -425,15 +386,48 @@
             assertThat(latest!!.mobile.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.wifi.isDefault).isTrue()
+        }
 
-            job.cancel()
+    /** VPN over W+ (aka VPN over carrier merged). See b/352162710#comment27 scenario #2. */
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
+    fun defaultConnections_vpnOverCarrierMerged_carrierMergedDefault() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.defaultConnections)
+
+            // Underlying carrier merged network
+            val underlyingCarrierMergedNetwork = mock<Network>()
+            val carrierMergedInfo =
+                mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(true) }
+            val underlyingCapabilities =
+                mock<NetworkCapabilities>().also {
+                    whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                    whenever(it.transportInfo).thenReturn(carrierMergedInfo)
+                }
+            whenever(connectivityManager.getNetworkCapabilities(underlyingCarrierMergedNetwork))
+                .thenReturn(underlyingCapabilities)
+
+            val mainCapabilities =
+                mock<NetworkCapabilities>().also {
+                    whenever(it.hasTransport(TRANSPORT_ETHERNET)).thenReturn(false)
+                    // Transports are WIFI|VPN, *not* CELLULAR.
+                    whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(false)
+                    whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
+                    whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
+                    whenever(it.transportInfo).thenReturn(VpnTransportInfo(0, null, false, false))
+                    whenever(it.underlyingNetworks)
+                        .thenReturn(listOf(underlyingCarrierMergedNetwork))
+                }
+
+            getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, mainCapabilities)
+
+            assertThat(latest!!.carrierMerged.isDefault).isTrue()
         }
 
     @Test
     fun defaultConnections_notCarrierMergedViaWifi_carrierMergedNotDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
@@ -448,15 +442,12 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_notCarrierMergedViaMobile_carrierMergedNotDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val carrierMergedInfo =
                 mock<WifiInfo>().apply { whenever(this.isCarrierMerged).thenReturn(false) }
@@ -471,15 +462,12 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest!!.carrierMerged.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_transportInfoNotWifi_wifiNotDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -492,8 +480,6 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest!!.wifi.isDefault).isFalse()
-
-            job.cancel()
         }
 
     @Test
@@ -531,8 +517,7 @@
     @Test
     fun defaultConnections_cellular_underlyingCarrierMergedViaWifi_allDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             // Underlying carrier merged network
             val underlyingCarrierMergedNetwork = mock<Network>()
@@ -560,16 +545,17 @@
             assertThat(latest!!.mobile.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.wifi.isDefault).isTrue()
-
-            job.cancel()
         }
 
-    /** Test for b/225902574. */
+    /**
+     * Test for b/225902574: VPN over VCN over W+ (aka VPN over VCN over carrier merged).
+     *
+     * Also see b/352162710#comment27 scenario #3 and b/352162710#comment30.
+     */
     @Test
     fun defaultConnections_cellular_underlyingCarrierMergedViaMobileWithVcnTransport_allDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             // Underlying carrier merged network
             val underlyingCarrierMergedNetwork = mock<Network>()
@@ -587,6 +573,7 @@
             val mainCapabilities =
                 mock<NetworkCapabilities>().also {
                     whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(true)
+                    whenever(it.hasTransport(TRANSPORT_VPN)).thenReturn(true)
                     whenever(it.transportInfo).thenReturn(null)
                     whenever(it.underlyingNetworks)
                         .thenReturn(listOf(underlyingCarrierMergedNetwork))
@@ -597,15 +584,12 @@
             assertThat(latest!!.mobile.isDefault).isTrue()
             assertThat(latest!!.carrierMerged.isDefault).isTrue()
             assertThat(latest!!.wifi.isDefault).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_multipleTransports_multipleDefault() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -619,15 +603,12 @@
             assertThat(latest!!.mobile.isDefault).isTrue()
             assertThat(latest!!.ethernet.isDefault).isTrue()
             assertThat(latest!!.wifi.isDefault).isTrue()
-
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_hasValidated_isValidatedTrue() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -638,14 +619,12 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest!!.isValidated).isTrue()
-            job.cancel()
         }
 
     @Test
     fun defaultConnections_noValidated_isValidatedFalse() =
         testScope.runTest {
-            var latest: DefaultConnectionModel? = null
-            val job = underTest.defaultConnections.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.defaultConnections)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -656,7 +635,6 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest!!.isValidated).isFalse()
-            job.cancel()
         }
 
     @Test
@@ -669,8 +647,7 @@
         testScope.runTest {
             val vcnInfo = VcnTransportInfo(SUB_1_ID)
 
-            var latest: Int? = null
-            val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.vcnSubId)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -681,7 +658,6 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isEqualTo(SUB_1_ID)
-            job.cancel()
         }
 
     @Test
@@ -689,8 +665,7 @@
         testScope.runTest {
             val vcnInfo = VcnTransportInfo(INVALID_SUBSCRIPTION_ID)
 
-            var latest: Int? = null
-            val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.vcnSubId)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -701,14 +676,12 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isNull()
-            job.cancel()
         }
 
     @Test
     fun vcnSubId_nullIfNoTransportInfo() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.vcnSubId)
 
             val capabilities =
                 mock<NetworkCapabilities>().also {
@@ -719,7 +692,6 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isNull()
-            job.cancel()
         }
 
     @Test
@@ -728,8 +700,7 @@
             // If the underlying network of the VCN is a WiFi network, then there is no subId that
             // could disagree with telephony's active data subscription id.
 
-            var latest: Int? = null
-            val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.vcnSubId)
 
             val wifiInfo = mock<WifiInfo>()
             val vcnInfo = VcnTransportInfo(wifiInfo)
@@ -742,14 +713,12 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isNull()
-            job.cancel()
         }
 
     @Test
     fun vcnSubId_changingVcnInfoIsTracked() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.vcnSubId.onEach { latest = it }.launchIn(this)
+            val latest by collectLastValue(underTest.vcnSubId)
 
             val wifiInfo = mock<WifiInfo>()
             val wifiVcnInfo = VcnTransportInfo(wifiInfo)
@@ -788,8 +757,6 @@
             getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, capabilities)
 
             assertThat(latest).isNull()
-
-            job.cancel()
         }
 
     @Test
@@ -862,6 +829,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
     fun getMainOrUnderlyingWifiInfo_notCellular_underlyingWifi_noInfo() {
         val underlyingNetwork = mock<Network>()
         val underlyingWifiInfo = mock<WifiInfo>()
@@ -916,6 +884,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_ALWAYS_CHECK_UNDERLYING_NETWORKS)
     fun getMainOrUnderlyingWifiInfo_notCellular_underlyingVcnWithWifi_noInfo() {
         val underlyingNetwork = mock<Network>()
         val underlyingVcnInfo = VcnTransportInfo(mock<WifiInfo>())
@@ -1076,12 +1045,13 @@
                 testScope.backgroundScope,
                 tunerService,
             )
+        testScope.runCurrent()
     }
 
     private fun getTunable(): TunerService.Tunable {
         val callbackCaptor = argumentCaptor<TunerService.Tunable>()
         verify(tunerService).addTunable(callbackCaptor.capture(), any())
-        return callbackCaptor.value!!
+        return callbackCaptor.firstValue
     }
 
     private fun setUpEthernetWifiMobileSlotNames() {
@@ -1094,7 +1064,7 @@
     private fun getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
         val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
         verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
-        return callbackCaptor.value!!
+        return callbackCaptor.firstValue
     }
 
     private companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 60750cf..7ae6ea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -415,9 +415,9 @@
         }
 
     @Test
-    fun ongoingActivityChip_matchesViewModel() =
+    fun primaryOngoingActivityChip_matchesViewModel() =
         testScope.runTest {
-            val latest by collectLastValue(underTest.ongoingActivityChip)
+            val latest by collectLastValue(underTest.primaryOngoingActivityChip)
 
             kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index cefdf7e..e71f521 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -28,7 +28,7 @@
 
     override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
 
-    override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
+    override val primaryOngoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
         MutableStateFlow(OngoingActivityChipModel.Hidden())
 
     override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
index 2238bff..fed3317 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.Flags
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.connectivity.WifiIcons
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.testKosmos
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
@@ -61,6 +62,8 @@
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class InternetTileViewModelTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+
     private lateinit var underTest: InternetTileViewModel
     private lateinit var mobileIconsInteractor: MobileIconsInteractor
 
@@ -73,7 +76,7 @@
     private val wifiInteractor =
         WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
 
-    private val tableLogBuffer: TableLogBuffer = mock()
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "InternetTileViewModelTest")
     private val carrierConfigTracker: CarrierConfigTracker = mock()
 
     private val mobileConnectionsRepository =
@@ -152,7 +155,6 @@
 
             val networkModel =
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 4,
                     ssid = "test ssid",
                 )
@@ -182,7 +184,6 @@
 
             val networkModel =
                 WifiNetworkModel.Active(
-                    networkId = 1,
                     level = 4,
                     ssid = "test ssid",
                     hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
@@ -390,7 +391,6 @@
     private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
         val networkModel =
             WifiNetworkModel.Active(
-                networkId = 1,
                 level = 4,
                 ssid = "test ssid",
                 hotspotDeviceType = hotspot,
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 f8d50f5..46f34e8 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
@@ -25,8 +25,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
@@ -76,7 +74,6 @@
     // inside each test case without needing to manually recreate the repository.
     private val underTest: WifiRepositoryImpl by lazy {
         WifiRepositoryImpl(
-            featureFlags,
             testScope.backgroundScope,
             executor,
             dispatcher,
@@ -89,7 +86,6 @@
 
     private val executor = FakeExecutor(FakeSystemClock())
     private val logger = LogBuffer("name", maxSize = 100, logcatEchoTracker = mock())
-    private val featureFlags = FakeFeatureFlags()
     private val tableLogger = mock<TableLogBuffer>()
     private val wifiManager =
         mock<WifiManager>().apply { whenever(this.maxSignalLevel).thenReturn(10) }
@@ -103,8 +99,6 @@
 
     @Before
     fun setUp() {
-        featureFlags.set(Flags.INSTANT_TETHER, false)
-        featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false)
         whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any()))
             .thenReturn(wifiPickerTracker)
     }
@@ -289,27 +283,6 @@
         }
 
     @Test
-    fun accessPointInfo_alwaysFalse() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.wifiNetwork)
-
-            val wifiEntry =
-                mock<WifiEntry>().apply {
-                    whenever(this.isPrimaryNetwork).thenReturn(true)
-                    whenever(this.level).thenReturn(3)
-                    whenever(this.title).thenReturn(TITLE)
-                }
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
-            getCallback().onWifiEntriesChanged()
-
-            assertThat(latest is WifiNetworkModel.Active).isTrue()
-            val latestActive = latest as WifiNetworkModel.Active
-            assertThat(latestActive.isPasspointAccessPoint).isFalse()
-            assertThat(latestActive.isOnlineSignUpForPasspointAccessPoint).isFalse()
-            assertThat(latestActive.passpointProviderFriendlyName).isNull()
-        }
-
-    @Test
     fun wifiNetwork_unreachableLevel_inactiveNetwork() =
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
@@ -394,7 +367,6 @@
     @Test
     fun wifiNetwork_notHotspot_none() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry =
@@ -409,7 +381,6 @@
     @Test
     fun wifiNetwork_hotspot_unknown() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_UNKNOWN)
@@ -423,7 +394,6 @@
     @Test
     fun wifiNetwork_hotspot_phone() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_PHONE)
@@ -437,7 +407,6 @@
     @Test
     fun wifiNetwork_hotspot_tablet() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_TABLET)
@@ -451,7 +420,6 @@
     @Test
     fun wifiNetwork_hotspot_laptop() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_LAPTOP)
@@ -465,7 +433,6 @@
     @Test
     fun wifiNetwork_hotspot_watch() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_WATCH)
@@ -479,7 +446,6 @@
     @Test
     fun wifiNetwork_hotspot_auto() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_AUTO)
@@ -493,7 +459,6 @@
     @Test
     fun wifiNetwork_hotspot_invalid() =
         testScope.runTest {
-            featureFlags.set(Flags.INSTANT_TETHER, true)
             val latest by collectLastValue(underTest.wifiNetwork)
 
             val wifiEntry = createHotspotWithType(1234)
@@ -505,23 +470,6 @@
         }
 
     @Test
-    fun wifiNetwork_hotspot_flagOff_valueNotUsed() =
-        testScope.runTest {
-            // WHEN the flag is off
-            featureFlags.set(Flags.INSTANT_TETHER, false)
-
-            val latest by collectLastValue(underTest.wifiNetwork)
-
-            val wifiEntry = createHotspotWithType(NetworkProviderInfo.DEVICE_TYPE_WATCH)
-            whenever(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntry)
-            getCallback().onWifiEntriesChanged()
-
-            // THEN NONE is always used, even if the wifi entry does have a hotspot device type
-            assertThat((latest as WifiNetworkModel.Active).hotspotDeviceType)
-                .isEqualTo(WifiNetworkModel.HotspotDeviceType.NONE)
-        }
-
-    @Test
     fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() =
         testScope.runTest {
             val latest by collectLastValue(underTest.wifiNetwork)
@@ -826,7 +774,6 @@
     @Test
     fun secondaryNetworks_activeEntriesEmpty_isEmpty() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             whenever(wifiPickerTracker.activeWifiEntries).thenReturn(listOf())
@@ -839,7 +786,6 @@
     @Test
     fun secondaryNetworks_oneActiveEntry_hasOne() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val wifiEntry = mock<WifiEntry>()
@@ -853,7 +799,6 @@
     @Test
     fun secondaryNetworks_multipleActiveEntries_hasMultiple() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val wifiEntry1 = mock<WifiEntry>()
@@ -868,7 +813,6 @@
     @Test
     fun secondaryNetworks_mapsToInactive() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val inactiveEntry =
@@ -884,7 +828,6 @@
     @Test
     fun secondaryNetworks_mapsToActive() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val activeEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -900,7 +843,6 @@
     @Test
     fun secondaryNetworks_mapsToCarrierMerged() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val carrierMergedEntry =
@@ -917,7 +859,6 @@
     @Test
     fun secondaryNetworks_mapsMultipleInOrder() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val activeEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -937,7 +878,6 @@
     @Test
     fun secondaryNetworks_filtersOutConnectedEntry() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val connectedEntry = mock<WifiEntry>().apply { whenever(this.level).thenReturn(1) }
@@ -959,7 +899,6 @@
     @Test
     fun secondaryNetworks_noConnectedEntry_hasAllActiveEntries() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val secondaryEntry1 = mock<WifiEntry>().apply { whenever(this.level).thenReturn(2) }
@@ -978,7 +917,6 @@
     @Test
     fun secondaryNetworks_filtersOutPrimaryNetwork() =
         testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, true)
             val latest by collectLastValue(underTest.secondaryNetworks)
 
             val primaryEntry =
@@ -1001,20 +939,6 @@
         }
 
     @Test
-    fun secondaryNetworks_flagOff_noNetworks() =
-        testScope.runTest {
-            featureFlags.set(Flags.WIFI_SECONDARY_NETWORKS, false)
-            val latest by collectLastValue(underTest.secondaryNetworks)
-
-            val wifiEntry = mock<WifiEntry>()
-            whenever(wifiPickerTracker.activeWifiEntries).thenReturn(listOf(wifiEntry))
-
-            getCallback().onWifiEntriesChanged()
-
-            assertThat(latest).isEmpty()
-        }
-
-    @Test
     fun isWifiConnectedWithValidSsid_inactiveNetwork_false() =
         testScope.runTest {
             collectLastValue(underTest.wifiNetwork)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
index eb6b068..92860ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiNetworkModelTest.kt
@@ -34,31 +34,30 @@
     @Test
     fun active_levelsInValidRange_noException() {
         (MIN_VALID_LEVEL..MAX_VALID_LEVEL).forEach { level ->
-            WifiNetworkModel.Active(NETWORK_ID, level = level)
+            WifiNetworkModel.Active(level = level)
             // No assert, just need no crash
         }
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun active_levelNegative_exceptionThrown() {
-        WifiNetworkModel.Active(NETWORK_ID, level = MIN_VALID_LEVEL - 1)
+        WifiNetworkModel.Active(level = MIN_VALID_LEVEL - 1)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun active_levelTooHigh_exceptionThrown() {
-        WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1)
+        WifiNetworkModel.Active(level = MAX_VALID_LEVEL + 1)
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun carrierMerged_invalidSubId_exceptionThrown() {
-        WifiNetworkModel.CarrierMerged(NETWORK_ID, INVALID_SUBSCRIPTION_ID, 1)
+        WifiNetworkModel.CarrierMerged(INVALID_SUBSCRIPTION_ID, 1)
     }
 
     @Test
     fun active_hasValidSsid_nullSsid_false() {
         val network =
             WifiNetworkModel.Active(
-                NETWORK_ID,
                 level = MAX_VALID_LEVEL,
                 ssid = null,
             )
@@ -70,7 +69,6 @@
     fun active_hasValidSsid_unknownSsid_false() {
         val network =
             WifiNetworkModel.Active(
-                NETWORK_ID,
                 level = MAX_VALID_LEVEL,
                 ssid = UNKNOWN_SSID,
             )
@@ -82,7 +80,6 @@
     fun active_hasValidSsid_validSsid_true() {
         val network =
             WifiNetworkModel.Active(
-                NETWORK_ID,
                 level = MAX_VALID_LEVEL,
                 ssid = "FakeSsid",
             )
@@ -97,7 +94,6 @@
         val logger = TestLogger()
         val prevVal =
             WifiNetworkModel.CarrierMerged(
-                networkId = 5,
                 subscriptionId = 3,
                 level = 1,
             )
@@ -105,7 +101,6 @@
         WifiNetworkModel.Inactive.logDiffs(prevVal, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
         assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -116,7 +111,6 @@
         val logger = TestLogger()
         val carrierMerged =
             WifiNetworkModel.CarrierMerged(
-                networkId = 6,
                 subscriptionId = 3,
                 level = 2,
             )
@@ -124,7 +118,6 @@
         carrierMerged.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
         assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
@@ -136,7 +129,6 @@
         val logger = TestLogger()
         val activeNetwork =
             WifiNetworkModel.Active(
-                networkId = 5,
                 isValidated = true,
                 level = 3,
                 ssid = "Test SSID",
@@ -146,27 +138,21 @@
         activeNetwork.logDiffs(prevVal = WifiNetworkModel.Inactive, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
         assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
         assertThat(logger.changes).contains(Pair(COL_HOTSPOT, "LAPTOP"))
     }
+
     @Test
     fun logDiffs_activeToInactive_resetsAllActiveFields() {
         val logger = TestLogger()
         val activeNetwork =
-            WifiNetworkModel.Active(
-                networkId = 5,
-                isValidated = true,
-                level = 3,
-                ssid = "Test SSID"
-            )
+            WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
 
         WifiNetworkModel.Inactive.logDiffs(prevVal = activeNetwork, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_INACTIVE))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, NETWORK_ID_DEFAULT.toString()))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "false"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, LEVEL_DEFAULT.toString()))
         assertThat(logger.changes).contains(Pair(COL_SSID, "null"))
@@ -178,7 +164,6 @@
         val logger = TestLogger()
         val activeNetwork =
             WifiNetworkModel.Active(
-                networkId = 5,
                 isValidated = true,
                 level = 3,
                 ssid = "Test SSID",
@@ -186,7 +171,6 @@
             )
         val prevVal =
             WifiNetworkModel.CarrierMerged(
-                networkId = 5,
                 subscriptionId = 3,
                 level = 1,
             )
@@ -194,25 +178,19 @@
         activeNetwork.logDiffs(prevVal, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_ACTIVE))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "5"))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, "3"))
         assertThat(logger.changes).contains(Pair(COL_SSID, "Test SSID"))
         assertThat(logger.changes).contains(Pair(COL_HOTSPOT, "AUTO"))
     }
+
     @Test
     fun logDiffs_activeToCarrierMerged_logsAllFields() {
         val logger = TestLogger()
         val activeNetwork =
-            WifiNetworkModel.Active(
-                networkId = 5,
-                isValidated = true,
-                level = 3,
-                ssid = "Test SSID"
-            )
+            WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
         val carrierMerged =
             WifiNetworkModel.CarrierMerged(
-                networkId = 6,
                 subscriptionId = 3,
                 level = 2,
             )
@@ -220,7 +198,6 @@
         carrierMerged.logDiffs(prevVal = activeNetwork, logger)
 
         assertThat(logger.changes).contains(Pair(COL_NETWORK_TYPE, TYPE_CARRIER_MERGED))
-        assertThat(logger.changes).contains(Pair(COL_NETWORK_ID, "6"))
         assertThat(logger.changes).contains(Pair(COL_SUB_ID, "3"))
         assertThat(logger.changes).contains(Pair(COL_VALIDATED, "true"))
         assertThat(logger.changes).contains(Pair(COL_LEVEL, "2"))
@@ -231,19 +208,9 @@
     fun logDiffs_activeChangesLevel_onlyLevelLogged() {
         val logger = TestLogger()
         val prevActiveNetwork =
-            WifiNetworkModel.Active(
-                networkId = 5,
-                isValidated = true,
-                level = 3,
-                ssid = "Test SSID"
-            )
+            WifiNetworkModel.Active(isValidated = true, level = 3, ssid = "Test SSID")
         val newActiveNetwork =
-            WifiNetworkModel.Active(
-                networkId = 5,
-                isValidated = true,
-                level = 2,
-                ssid = "Test SSID"
-            )
+            WifiNetworkModel.Active(isValidated = true, level = 2, ssid = "Test SSID")
 
         newActiveNetwork.logDiffs(prevActiveNetwork, logger)
 
@@ -265,8 +232,4 @@
             changes.add(Pair(columnName, value.toString()))
         }
     }
-
-    companion object {
-        private const val NETWORK_ID = 2
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 80b10c0..ff398f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -26,7 +26,7 @@
 import android.widget.ImageView
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logcatTableLogBuffer
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -61,10 +62,11 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper(setAsMainLooper = true)
 class ModernStatusBarWifiViewTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var testableLooper: TestableLooper
 
-    @Mock private lateinit var tableLogBuffer: TableLogBuffer
+    private val tableLogBuffer = logcatTableLogBuffer(kosmos, "ModernStatusBarWifiViewTest")
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
     private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -91,7 +93,7 @@
                 AirplaneModeInteractor(
                     airplaneModeRepository,
                     connectivityRepository,
-                    FakeMobileConnectionsRepository(),
+                    kosmos.fakeMobileConnectionsRepository,
                 ),
                 tableLogBuffer,
                 scope,
@@ -193,9 +195,7 @@
     @Test
     fun isIconVisible_notEnabled_outputsFalse() {
         wifiRepository.setIsWifiEnabled(false)
-        wifiRepository.setWifiNetwork(
-            WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
-        )
+        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 2))
 
         val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
 
@@ -210,9 +210,7 @@
     @Test
     fun isIconVisible_enabled_outputsTrue() {
         wifiRepository.setIsWifiEnabled(true)
-        wifiRepository.setWifiNetwork(
-            WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2)
-        )
+        wifiRepository.setWifiNetwork(WifiNetworkModel.Active(isValidated = true, level = 2))
 
         val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
 
@@ -270,4 +268,3 @@
 }
 
 private const val SLOT_NAME = "TestSlotName"
-private const val NETWORK_ID = 200
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index 489319e..82acb40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon.Companion.NO_INTERNET
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -64,6 +65,7 @@
 @RunWith(Parameterized::class)
 internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase) :
     SysuiTestCase() {
+    private val kosmos = testKosmos()
 
     private lateinit var underTest: WifiViewModel
 
@@ -91,7 +93,7 @@
                 AirplaneModeInteractor(
                     airplaneModeRepository,
                     connectivityRepository,
-                    FakeMobileConnectionsRepository(),
+                    kosmos.fakeMobileConnectionsRepository,
                 ),
                 tableLogBuffer,
                 scope,
@@ -204,8 +206,7 @@
                 // Enabled = false => no networks shown
                 TestCase(
                     enabled = false,
-                    network =
-                        WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+                    network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
                     expected = null,
                 ),
                 TestCase(
@@ -215,20 +216,19 @@
                 ),
                 TestCase(
                     enabled = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 1),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 1),
                     expected = null,
                 ),
                 TestCase(
                     enabled = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 3),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 3),
                     expected = null,
                 ),
 
                 // forceHidden = true => no networks shown
                 TestCase(
                     forceHidden = true,
-                    network =
-                        WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+                    network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
                     expected = null,
                 ),
                 TestCase(
@@ -238,12 +238,12 @@
                 ),
                 TestCase(
                     enabled = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 2),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 2),
                     expected = null,
                 ),
                 TestCase(
                     forceHidden = true,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 1),
                     expected = null,
                 ),
 
@@ -263,7 +263,7 @@
                 ),
                 TestCase(
                     alwaysShowIconWhenEnabled = true,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 4),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 4),
                     expected =
                         Expected(
                             iconResource = WIFI_NO_INTERNET_ICONS[4],
@@ -276,7 +276,7 @@
                 ),
                 TestCase(
                     alwaysShowIconWhenEnabled = true,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 2),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 2),
                     expected =
                         Expected(
                             iconResource = WIFI_FULL_ICONS[2],
@@ -303,7 +303,7 @@
                 ),
                 TestCase(
                     hasDataCapabilities = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 2),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 2),
                     expected =
                         Expected(
                             iconResource = WIFI_NO_INTERNET_ICONS[2],
@@ -316,7 +316,7 @@
                 ),
                 TestCase(
                     hasDataCapabilities = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 0),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 0),
                     expected =
                         Expected(
                             iconResource = WIFI_FULL_ICONS[0],
@@ -343,7 +343,7 @@
                 ),
                 TestCase(
                     isDefault = true,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 3),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 3),
                     expected =
                         Expected(
                             iconResource = WIFI_NO_INTERNET_ICONS[3],
@@ -356,7 +356,7 @@
                 ),
                 TestCase(
                     isDefault = true,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 1),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 1),
                     expected =
                         Expected(
                             iconResource = WIFI_FULL_ICONS[1],
@@ -372,8 +372,7 @@
                     enabled = true,
                     isDefault = true,
                     forceHidden = false,
-                    network =
-                        WifiNetworkModel.CarrierMerged(NETWORK_ID, subscriptionId = 1, level = 1),
+                    network = WifiNetworkModel.CarrierMerged(subscriptionId = 1, level = 1),
                     expected = null,
                 ),
 
@@ -390,7 +389,7 @@
                 ),
                 TestCase(
                     isDefault = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = false, level = 3),
+                    network = WifiNetworkModel.Active(isValidated = false, level = 3),
                     expected = null,
                 ),
 
@@ -398,7 +397,7 @@
                 // because wifi isn't the default connection (b/272509965).
                 TestCase(
                     isDefault = false,
-                    network = WifiNetworkModel.Active(NETWORK_ID, isValidated = true, level = 4),
+                    network = WifiNetworkModel.Active(isValidated = true, level = 4),
                     expected = null,
                 ),
             )
@@ -406,4 +405,3 @@
 }
 
 private val IMMEDIATE = Dispatchers.Main.immediate
-private const val NETWORK_ID = 789
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
index bf0a39b..06b3b57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateTest.kt
@@ -57,6 +57,7 @@
     private val activityStarter = kosmos.activityStarter
     private val mockDialogTransitionAnimator = kosmos.mockDialogTransitionAnimator
     private val mockAnimationController = kosmos.mockActivityTransitionAnimatorController
+    private val mockDialogEventLogger = kosmos.mockModesDialogEventLogger
     private lateinit var underTest: ModesDialogDelegate
 
     @Before
@@ -75,6 +76,7 @@
                 mockDialogTransitionAnimator,
                 activityStarter,
                 { kosmos.modesDialogViewModel },
+                mockDialogEventLogger,
                 kosmos.mainCoroutineContext,
             )
     }
@@ -121,4 +123,12 @@
 
         assertThat(underTest.currentDialog).isNull()
     }
+
+    @Test
+    fun openSettings_logsEvent() =
+        testScope.runTest {
+            val dialog: SystemUIDialog = mock()
+            underTest.openSettings(dialog)
+            verify(mockDialogEventLogger).logDialogSettings()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 12cfdcf..e396b56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.ui.viewmodel
 
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -33,7 +33,6 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.domain.interactor.keyguardStatusBarInteractor
 import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
-import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
 import com.android.systemui.statusbar.policy.BatteryController
@@ -127,7 +126,7 @@
         }
 
     @Test
-    @EnableFlags(NotificationsHeadsUpRefactor.FLAG_NAME)
+    @EnableSceneContainer
     fun isVisible_headsUpStatusBarShown_false() =
         testScope.runTest {
             val latest by collectLastValue(underTest.isVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index bb6ba46..54df9e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -25,12 +25,13 @@
 import android.view.accessibility.AccessibilityManager
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.testing.UiEventLoggerFake
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -77,7 +78,7 @@
     @Mock
     private lateinit var dumpManager: DumpManager
     @Mock
-    private lateinit var windowManager: WindowManager
+    private lateinit var windowManager: ViewCaptureAwareWindowManager
     @Mock
     private lateinit var powerManager: PowerManager
 
@@ -1142,7 +1143,7 @@
     inner class TestController(
         context: Context,
         logger: TemporaryViewLogger<ViewInfo>,
-        windowManager: WindowManager,
+        viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
         @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
@@ -1154,7 +1155,7 @@
     ) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger<ViewInfo>>(
         context,
         logger,
-        windowManager,
+        viewCaptureAwareWindowManager,
         mainExecutor,
         accessibilityManager,
         configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 664f2df..4260b65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -30,6 +30,8 @@
 import androidx.core.animation.doOnCancel
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.app.viewcapture.ViewCapture
+import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
@@ -84,6 +86,7 @@
     @Mock private lateinit var viewUtil: ViewUtil
     @Mock private lateinit var vibratorHelper: VibratorHelper
     @Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
+    @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
     private lateinit var chipbarAnimator: TestChipbarAnimator
     private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
     private lateinit var fakeWakeLock: WakeLockFake
@@ -112,7 +115,8 @@
             ChipbarCoordinator(
                 context,
                 logger,
-                windowManager,
+                ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
+                        isViewCaptureEnabled = false),
                 fakeExecutor,
                 accessibilityManager,
                 configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
index c705cea..89e8895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.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.inputdevice.tutorial.inputDeviceTutorialLogger
 import com.android.systemui.model.sysUiState
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED
@@ -41,7 +42,13 @@
     private val sysUiState = kosmos.sysUiState
     private val viewModel =
         TouchpadTutorialViewModel(
-            TouchpadGesturesInteractor(sysUiState, kosmos.displayTracker, testScope.backgroundScope)
+            TouchpadGesturesInteractor(
+                sysUiState,
+                kosmos.displayTracker,
+                testScope.backgroundScope,
+                kosmos.inputDeviceTutorialLogger
+            ),
+            kosmos.inputDeviceTutorialLogger
         )
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
index 84cd79d..25ceea9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/CreateUserActivityTest.kt
@@ -26,8 +26,8 @@
                 val dialog: Dialog = mock()
                 whenever(
                         createDialog(
-                            /* activity = */ nullable(),
-                            /* activityStarter = */ nullable(),
+                            /* activity = */ any(),
+                            /* activityStarter = */ any(),
                             /* isMultipleAdminsEnabled = */ any(),
                             /* successCallback = */ nullable(),
                             /* cancelCallback = */ nullable()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 37a73cf..c235954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -24,21 +24,21 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.unconfinedTestDispatcher
+import com.android.systemui.kosmos.unconfinedTestScope
 import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.model.SelectedUserModel
 import com.android.systemui.user.data.model.SelectionStatus
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
-import com.android.systemui.util.settings.FakeGlobalSettings
+import com.android.systemui.util.settings.unconfinedDispatcherFakeGlobalSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.runBlocking
-import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -52,143 +52,153 @@
 @RunWith(AndroidJUnit4::class)
 class UserRepositoryImplTest : SysuiTestCase() {
 
+    private val kosmos = testKosmos()
+    private val testDispatcher = kosmos.unconfinedTestDispatcher
+    private val testScope = kosmos.unconfinedTestScope
+    private val globalSettings = kosmos.unconfinedDispatcherFakeGlobalSettings
+
     @Mock private lateinit var manager: UserManager
 
     private lateinit var underTest: UserRepositoryImpl
 
-    private lateinit var globalSettings: FakeGlobalSettings
     private lateinit var tracker: FakeUserTracker
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
-        globalSettings = FakeGlobalSettings()
         tracker = FakeUserTracker()
     }
 
     @Test
-    fun userSwitcherSettings() = runSelfCancelingTest {
-        setUpGlobalSettings(
-            isSimpleUserSwitcher = true,
-            isAddUsersFromLockscreen = true,
-            isUserSwitcherEnabled = true,
-        )
-        underTest = create(this)
+    fun userSwitcherSettings() =
+        testScope.runTest {
+            setUpGlobalSettings(
+                isSimpleUserSwitcher = true,
+                isAddUsersFromLockscreen = true,
+                isUserSwitcherEnabled = true,
+            )
+            underTest = create(testScope.backgroundScope)
+            var value: UserSwitcherSettingsModel? = null
+            val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
 
-        var value: UserSwitcherSettingsModel? = null
-        underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+            assertUserSwitcherSettings(
+                model = value,
+                expectedSimpleUserSwitcher = true,
+                expectedAddUsersFromLockscreen = true,
+                expectedUserSwitcherEnabled = true,
+            )
 
-        assertUserSwitcherSettings(
-            model = value,
-            expectedSimpleUserSwitcher = true,
-            expectedAddUsersFromLockscreen = true,
-            expectedUserSwitcherEnabled = true,
-        )
-
-        setUpGlobalSettings(
-            isSimpleUserSwitcher = false,
-            isAddUsersFromLockscreen = true,
-            isUserSwitcherEnabled = true,
-        )
-        assertUserSwitcherSettings(
-            model = value,
-            expectedSimpleUserSwitcher = false,
-            expectedAddUsersFromLockscreen = true,
-            expectedUserSwitcherEnabled = true,
-        )
-    }
+            setUpGlobalSettings(
+                isSimpleUserSwitcher = false,
+                isAddUsersFromLockscreen = true,
+                isUserSwitcherEnabled = true,
+            )
+            assertUserSwitcherSettings(
+                model = value,
+                expectedSimpleUserSwitcher = false,
+                expectedAddUsersFromLockscreen = true,
+                expectedUserSwitcherEnabled = true,
+            )
+            job.cancel()
+        }
 
     @Test
-    fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() = runSelfCancelingTest {
-        underTest = create(this)
+    fun userSwitcherSettings_isUserSwitcherEnabled_notInitialized() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
 
-        var value: UserSwitcherSettingsModel? = null
-        underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
+            var value: UserSwitcherSettingsModel? = null
+            val job = underTest.userSwitcherSettings.onEach { value = it }.launchIn(this)
 
-        assertUserSwitcherSettings(
-            model = value,
-            expectedSimpleUserSwitcher = false,
-            expectedAddUsersFromLockscreen = false,
-            expectedUserSwitcherEnabled =
-                context.resources.getBoolean(
-                    com.android.internal.R.bool.config_showUserSwitcherByDefault
-                ),
-        )
-    }
+            assertUserSwitcherSettings(
+                model = value,
+                expectedSimpleUserSwitcher = false,
+                expectedAddUsersFromLockscreen = false,
+                expectedUserSwitcherEnabled =
+                    context.resources.getBoolean(
+                        com.android.internal.R.bool.config_showUserSwitcherByDefault
+                    ),
+            )
+            job.cancel()
+        }
 
     @Test
-    fun refreshUsers() = runSelfCancelingTest {
-        val mainUserId = 10
-        val mainUser = mock(UserHandle::class.java)
-        whenever(manager.mainUser).thenReturn(mainUser)
-        whenever(mainUser.identifier).thenReturn(mainUserId)
+    fun refreshUsers() =
+        testScope.runTest {
+            val mainUserId = 10
+            val mainUser = mock(UserHandle::class.java)
+            whenever(manager.mainUser).thenReturn(mainUser)
+            whenever(mainUser.identifier).thenReturn(mainUserId)
 
-        underTest = create(this)
-        val initialExpectedValue =
-            setUpUsers(
-                count = 3,
-                selectedIndex = 0,
-            )
-        var userInfos: List<UserInfo>? = null
-        var selectedUserInfo: UserInfo? = null
-        underTest.userInfos.onEach { userInfos = it }.launchIn(this)
-        underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
+            underTest = create(testScope.backgroundScope)
+            val initialExpectedValue =
+                setUpUsers(
+                    count = 3,
+                    selectedIndex = 0,
+                )
+            var userInfos: List<UserInfo>? = null
+            var selectedUserInfo: UserInfo? = null
+            val job1 = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+            val job2 = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
 
-        underTest.refreshUsers()
-        assertThat(userInfos).isEqualTo(initialExpectedValue)
-        assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
-        assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+            underTest.refreshUsers()
+            assertThat(userInfos).isEqualTo(initialExpectedValue)
+            assertThat(selectedUserInfo).isEqualTo(initialExpectedValue[0])
+            assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
 
-        val secondExpectedValue =
-            setUpUsers(
-                count = 4,
-                selectedIndex = 1,
-            )
-        underTest.refreshUsers()
-        assertThat(userInfos).isEqualTo(secondExpectedValue)
-        assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
-        assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
+            val secondExpectedValue =
+                setUpUsers(
+                    count = 4,
+                    selectedIndex = 1,
+                )
+            underTest.refreshUsers()
+            assertThat(userInfos).isEqualTo(secondExpectedValue)
+            assertThat(selectedUserInfo).isEqualTo(secondExpectedValue[1])
+            assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedUserInfo?.id)
 
-        val selectedNonGuestUserId = selectedUserInfo?.id
-        val thirdExpectedValue =
-            setUpUsers(
-                count = 2,
-                isLastGuestUser = true,
-                selectedIndex = 1,
-            )
-        underTest.refreshUsers()
-        assertThat(userInfos).isEqualTo(thirdExpectedValue)
-        assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
-        assertThat(selectedUserInfo?.isGuest).isTrue()
-        assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
-        assertThat(underTest.mainUserId).isEqualTo(mainUserId)
-    }
+            val selectedNonGuestUserId = selectedUserInfo?.id
+            val thirdExpectedValue =
+                setUpUsers(
+                    count = 2,
+                    isLastGuestUser = true,
+                    selectedIndex = 1,
+                )
+            underTest.refreshUsers()
+            assertThat(userInfos).isEqualTo(thirdExpectedValue)
+            assertThat(selectedUserInfo).isEqualTo(thirdExpectedValue[1])
+            assertThat(selectedUserInfo?.isGuest).isTrue()
+            assertThat(underTest.lastSelectedNonGuestUserId).isEqualTo(selectedNonGuestUserId)
+            assertThat(underTest.mainUserId).isEqualTo(mainUserId)
+            job1.cancel()
+            job2.cancel()
+        }
 
     @Test
-    fun refreshUsers_sortsByCreationTime_guestUserLast() = runSelfCancelingTest {
-        underTest = create(this)
-        val unsortedUsers =
-            setUpUsers(
-                count = 3,
-                selectedIndex = 0,
-                isLastGuestUser = true,
-            )
-        unsortedUsers[0].creationTime = 999
-        unsortedUsers[1].creationTime = 900
-        unsortedUsers[2].creationTime = 950
-        val expectedUsers =
-            listOf(
-                unsortedUsers[1],
-                unsortedUsers[0],
-                unsortedUsers[2], // last because this is the guest
-            )
-        var userInfos: List<UserInfo>? = null
-        underTest.userInfos.onEach { userInfos = it }.launchIn(this)
+    fun refreshUsers_sortsByCreationTime_guestUserLast() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            val unsortedUsers =
+                setUpUsers(
+                    count = 3,
+                    selectedIndex = 0,
+                    isLastGuestUser = true,
+                )
+            unsortedUsers[0].creationTime = 999
+            unsortedUsers[1].creationTime = 900
+            unsortedUsers[2].creationTime = 950
+            val expectedUsers =
+                listOf(
+                    unsortedUsers[1],
+                    unsortedUsers[0],
+                    unsortedUsers[2], // last because this is the guest
+                )
+            var userInfos: List<UserInfo>? = null
+            val job = underTest.userInfos.onEach { userInfos = it }.launchIn(this)
 
-        underTest.refreshUsers()
-        assertThat(userInfos).isEqualTo(expectedUsers)
-    }
+            underTest.refreshUsers()
+            assertThat(userInfos).isEqualTo(expectedUsers)
+            job.cancel()
+        }
 
     private fun setUpUsers(
         count: Int,
@@ -206,58 +216,68 @@
         tracker.set(userInfos, selectedIndex)
         return userInfos
     }
-    @Test
-    fun userTrackerCallback_updatesSelectedUserInfo() = runSelfCancelingTest {
-        underTest = create(this)
-        var selectedUserInfo: UserInfo? = null
-        underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
-        setUpUsers(
-            count = 2,
-            selectedIndex = 0,
-        )
-        tracker.onProfileChanged()
-        assertThat(selectedUserInfo?.id).isEqualTo(0)
-        setUpUsers(
-            count = 2,
-            selectedIndex = 1,
-        )
-        tracker.onProfileChanged()
-        assertThat(selectedUserInfo?.id).isEqualTo(1)
-    }
 
     @Test
-    fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
-        underTest = create(this)
-        var selectedUser: SelectedUserModel? = null
-        underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
-        setUpUsers(count = 2, selectedIndex = 1)
+    fun userTrackerCallback_updatesSelectedUserInfo() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            var selectedUserInfo: UserInfo? = null
+            val job = underTest.selectedUserInfo.onEach { selectedUserInfo = it }.launchIn(this)
 
-        // WHEN the user is changing
-        tracker.onUserChanging(userId = 1)
+            setUpUsers(
+                count = 2,
+                selectedIndex = 0,
+            )
+            tracker.onProfileChanged()
+            assertThat(selectedUserInfo?.id).isEqualTo(0)
+            setUpUsers(
+                count = 2,
+                selectedIndex = 1,
+            )
+            tracker.onProfileChanged()
+            assertThat(selectedUserInfo?.id).isEqualTo(1)
+            job.cancel()
+        }
 
-        // THEN the selection status is IN_PROGRESS
-        assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+    @Test
+    fun userTrackerCallback_updatesSelectionStatus() =
+        testScope.runTest {
+            underTest = create(testScope.backgroundScope)
+            var selectedUser: SelectedUserModel? = null
+            val job = underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
 
-        // WHEN the user has finished changing
-        tracker.onUserChanged(userId = 1)
+            setUpUsers(count = 2, selectedIndex = 1)
 
-        // THEN the selection status is COMPLETE
-        assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+            // WHEN the user is changing
+            tracker.onUserChanging(userId = 1)
 
-        tracker.onProfileChanged()
-        assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+            // THEN the selection status is IN_PROGRESS
+            assertThat(selectedUser!!.selectionStatus)
+                .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
 
-        setUpUsers(count = 2, selectedIndex = 0)
+            // WHEN the user has finished changing
+            tracker.onUserChanged(userId = 1)
 
-        tracker.onUserChanging(userId = 0)
-        assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+            // THEN the selection status is COMPLETE
+            assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
 
-        // WHEN a profile change occurs while a user is changing
-        tracker.onProfileChanged()
+            tracker.onProfileChanged()
+            assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
 
-        // THEN the selection status remains as IN_PROGRESS
-        assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
-    }
+            setUpUsers(count = 2, selectedIndex = 0)
+
+            tracker.onUserChanging(userId = 0)
+            assertThat(selectedUser!!.selectionStatus)
+                .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+            // WHEN a profile change occurs while a user is changing
+            tracker.onProfileChanged()
+
+            // THEN the selection status remains as IN_PROGRESS
+            assertThat(selectedUser!!.selectionStatus)
+                .isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+            job.cancel()
+        }
 
     private fun createUserInfo(
         id: Int,
@@ -308,26 +328,13 @@
         assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
     }
 
-    /**
-     * Executes the given block of execution within the scope of a dedicated [CoroutineScope] which
-     * is then automatically canceled and cleaned-up.
-     */
-    private fun runSelfCancelingTest(
-        block: suspend CoroutineScope.() -> Unit,
-    ) =
-        runBlocking(Dispatchers.Main.immediate) {
-            val scope = CoroutineScope(coroutineContext + Job())
-            block(scope)
-            scope.cancel()
-        }
-
-    private fun create(scope: CoroutineScope = TestCoroutineScope()): UserRepositoryImpl {
+    private fun create(scope: CoroutineScope): UserRepositoryImpl {
         return UserRepositoryImpl(
             appContext = context,
             manager = manager,
             applicationScope = scope,
-            mainDispatcher = IMMEDIATE,
-            backgroundDispatcher = IMMEDIATE,
+            mainDispatcher = testDispatcher,
+            backgroundDispatcher = testDispatcher,
             globalSettings = globalSettings,
             tracker = tracker,
         )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
index 78028f8..26f6f31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
@@ -3,7 +3,6 @@
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_REFACTOR_GET_CURRENT_USER
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.google.common.truth.Truth.assertThat
@@ -28,7 +27,6 @@
 
     @Test
     fun getSelectedUserIdReturnsId() {
-        mSetFlagsRule.enableFlags(FLAG_REFACTOR_GET_CURRENT_USER)
         runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[0]) }
 
         val actualId = underTest.getSelectedUserId()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 88b2630..a0cfab4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -23,12 +23,13 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
 import com.android.systemui.user.data.repository.fakeUserRepository
-import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -40,9 +41,10 @@
 @RunWith(AndroidJUnit4::class)
 class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
 
-    private val dispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(dispatcher)
-    private val secureSettings = FakeSettings()
+    private val kosmos = testKosmos()
+    private val dispatcher = kosmos.testDispatcher
+    private val testScope = kosmos.testScope
+    private val secureSettings = kosmos.fakeSettings
     private val userRepository = Kosmos().fakeUserRepository
     private lateinit var repository: UserAwareSecureSettingsRepository
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
new file mode 100644
index 0000000..c140364
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.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.volume
+
+import android.media.IVolumeController
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+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.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class VolumeControllerAdapterTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val eventsFlow = MutableStateFlow<VolumeControllerEvent?>(null)
+    private val underTest =
+        with(kosmos) { VolumeControllerAdapter(applicationCoroutineScope, audioRepository) }
+
+    private val volumeController = mock<IVolumeController> {}
+
+    @Before
+    fun setUp() {
+        kosmos.audioRepository.init()
+    }
+
+    @Test
+    fun volumeControllerEvent_volumeChanged_callsMethod() =
+        testEvent(VolumeControllerEvent.VolumeChanged(3, 0)) {
+            verify(volumeController) { 1 * { volumeController.volumeChanged(eq(3), eq(0)) } }
+        }
+
+    @Test
+    fun volumeControllerEvent_dismiss_callsMethod() =
+        testEvent(VolumeControllerEvent.Dismiss) {
+            verify(volumeController) { 1 * { volumeController.dismiss() } }
+        }
+
+    @Test
+    fun volumeControllerEvent_displayCsdWarning_callsMethod() =
+        testEvent(VolumeControllerEvent.DisplayCsdWarning(0, 1)) {
+            verify(volumeController) { 1 * { volumeController.displayCsdWarning(eq(0), eq(1)) } }
+        }
+
+    @Test
+    fun volumeControllerEvent_displaySafeVolumeWarning_callsMethod() =
+        testEvent(VolumeControllerEvent.DisplaySafeVolumeWarning(1)) {
+            verify(volumeController) { 1 * { volumeController.displaySafeVolumeWarning(eq(1)) } }
+        }
+
+    @Test
+    fun volumeControllerEvent_masterMuteChanged_callsMethod() =
+        testEvent(VolumeControllerEvent.MasterMuteChanged(1)) {
+            verify(volumeController) { 1 * { volumeController.masterMuteChanged(1) } }
+        }
+
+    @Test
+    fun volumeControllerEvent_setA11yMode_callsMethod() =
+        testEvent(VolumeControllerEvent.SetA11yMode(1)) {
+            verify(volumeController) { 1 * { volumeController.setA11yMode(1) } }
+        }
+
+    @Test
+    fun volumeControllerEvent_SetLayoutDirection_callsMethod() =
+        testEvent(VolumeControllerEvent.SetLayoutDirection(1)) {
+            verify(volumeController) { 1 * { volumeController.setLayoutDirection(eq(1)) } }
+        }
+
+    private fun testEvent(event: VolumeControllerEvent, verify: () -> Unit) =
+        kosmos.testScope.runTest {
+            kosmos.audioRepository.sendVolumeControllerEvent(event)
+            underTest.collectToController(volumeController)
+
+            eventsFlow.value = event
+            runCurrent()
+
+            verify()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerCollectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerCollectorTest.kt
deleted file mode 100644
index dd78e4a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerCollectorTest.kt
+++ /dev/null
@@ -1,100 +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
-
-import android.media.IVolumeController
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.settingslib.media.data.repository.VolumeControllerEvent
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.verify
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class VolumeControllerCollectorTest : SysuiTestCase() {
-
-    private val kosmos = testKosmos()
-    private val eventsFlow = MutableStateFlow<VolumeControllerEvent?>(null)
-    private val underTest = VolumeControllerCollector(kosmos.applicationCoroutineScope)
-
-    private val volumeController = mock<IVolumeController> {}
-
-    @Test
-    fun volumeControllerEvent_volumeChanged_callsMethod() =
-        testEvent(VolumeControllerEvent.VolumeChanged(3, 0)) {
-            verify(volumeController) { 1 * { volumeController.volumeChanged(eq(3), eq(0)) } }
-        }
-
-    @Test
-    fun volumeControllerEvent_dismiss_callsMethod() =
-        testEvent(VolumeControllerEvent.Dismiss) {
-            verify(volumeController) { 1 * { volumeController.dismiss() } }
-        }
-
-    @Test
-    fun volumeControllerEvent_displayCsdWarning_callsMethod() =
-        testEvent(VolumeControllerEvent.DisplayCsdWarning(0, 1)) {
-            verify(volumeController) { 1 * { volumeController.displayCsdWarning(eq(0), eq(1)) } }
-        }
-
-    @Test
-    fun volumeControllerEvent_displaySafeVolumeWarning_callsMethod() =
-        testEvent(VolumeControllerEvent.DisplaySafeVolumeWarning(1)) {
-            verify(volumeController) { 1 * { volumeController.displaySafeVolumeWarning(eq(1)) } }
-        }
-
-    @Test
-    fun volumeControllerEvent_masterMuteChanged_callsMethod() =
-        testEvent(VolumeControllerEvent.MasterMuteChanged(1)) {
-            verify(volumeController) { 1 * { volumeController.masterMuteChanged(1) } }
-        }
-
-    @Test
-    fun volumeControllerEvent_setA11yMode_callsMethod() =
-        testEvent(VolumeControllerEvent.SetA11yMode(1)) {
-            verify(volumeController) { 1 * { volumeController.setA11yMode(1) } }
-        }
-
-    @Test
-    fun volumeControllerEvent_SetLayoutDirection_callsMethod() =
-        testEvent(VolumeControllerEvent.SetLayoutDirection(1)) {
-            verify(volumeController) { 1 * { volumeController.setLayoutDirection(eq(1)) } }
-        }
-
-    private fun testEvent(event: VolumeControllerEvent, verify: () -> Unit) =
-        kosmos.testScope.runTest {
-            underTest.collectToController(eventsFlow.filterNotNull(), volumeController)
-
-            eventsFlow.value = event
-            runCurrent()
-
-            verify()
-        }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 8b7d921..f62beeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -48,9 +48,11 @@
 
 import com.android.settingslib.flags.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestCaseExtKt;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.kosmos.Kosmos;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -78,6 +80,8 @@
 @TestableLooper.RunWithLooper
 public class VolumeDialogControllerImplTest extends SysuiTestCase {
 
+    private final Kosmos mKosmos = SysuiTestCaseExtKt.testKosmos(this);
+
     TestableVolumeDialogControllerImpl mVolumeController;
     VolumeDialogControllerImpl.C mCallback;
     @Mock
@@ -146,6 +150,7 @@
                         mNotificationManager,
                         mVibrator,
                         mIAudioService,
+                        VolumeControllerAdapterKosmosKt.getVolumeControllerAdapter(mKosmos),
                         mAccessibilityManager,
                         mPackageManager,
                         mWakefullnessLifcycle,
@@ -229,6 +234,32 @@
     }
 
     @Test
+    public void testVolumeChangeW_inAudioSharing_doStateChanged() {
+        ArgumentCaptor<VolumeDialogController.State> stateCaptor =
+                ArgumentCaptor.forClass(VolumeDialogController.State.class);
+        mVolumeController.setDeviceInteractive(false);
+        when(mWakefullnessLifcycle.getWakefulness())
+                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE);
+        // For now, mAudioManager.getDevicesForStream returns DEVICE_NONE during audio sharing
+        when(mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC))
+                .thenReturn(AudioManager.DEVICE_NONE);
+
+        mVolumeController.mInAudioSharing = true;
+        mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback).onStateChanged(stateCaptor.capture());
+        assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
+        assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
+                .isTrue();
+
+        mVolumeController.mInAudioSharing = false;
+        mVolumeController.onVolumeChangedW(AudioManager.STREAM_MUSIC, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback, times(2)).onStateChanged(stateCaptor.capture());
+        assertThat(stateCaptor.getValue().states.contains(AudioManager.STREAM_MUSIC)).isTrue();
+        assertThat(stateCaptor.getValue().states.get(AudioManager.STREAM_MUSIC).routedToBluetooth)
+                .isFalse();
+    }
+
+    @Test
     public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
         MediaSession.Token token = new MediaSession.Token(Process.myUid(), null);
         mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
@@ -297,6 +328,7 @@
                 NotificationManager notificationManager,
                 VibratorHelper optionalVibrator,
                 IAudioService iAudioService,
+                VolumeControllerAdapter volumeControllerAdapter,
                 AccessibilityManager accessibilityManager,
                 PackageManager packageManager,
                 WakefulnessLifecycle wakefulnessLifecycle,
@@ -316,6 +348,7 @@
                     notificationManager,
                     optionalVibrator,
                     iAudioService,
+                    volumeControllerAdapter,
                     accessibilityManager,
                     packageManager,
                     wakefulnessLifecycle,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
new file mode 100644
index 0000000..98cea9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTestKt.kt
@@ -0,0 +1,156 @@
+/*
+ * 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
+
+import android.app.activityManager
+import android.app.keyguardManager
+import android.content.applicationContext
+import android.content.packageManager
+import android.media.AudioManager
+import android.media.IVolumeController
+import android.os.Handler
+import android.os.looper
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.TestableLooper
+import android.view.accessibility.accessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.wakefulnessLifecycle
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.util.RingerModeLiveData
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.concurrency.FakeThreadFactory
+import com.android.systemui.util.time.fakeSystemClock
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.domain.interactor.audioSharingInteractor
+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.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(AndroidJUnit4::class)
+@SmallTest
[email protected]
+class VolumeDialogControllerImplTestKt : SysuiTestCase() {
+
+    @get:Rule val setFlagsRule = SetFlagsRule()
+
+    private val kosmos: Kosmos = testKosmos()
+    private val audioManager: AudioManager = mock {}
+    private val callbacks: VolumeDialogController.Callbacks = mock {}
+
+    private lateinit var threadFactory: FakeThreadFactory
+    private lateinit var underTest: VolumeDialogControllerImpl
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            audioRepository.init()
+            threadFactory =
+                FakeThreadFactory(FakeExecutor(fakeSystemClock)).apply { setLooper(looper) }
+            underTest =
+                VolumeDialogControllerImpl(
+                        applicationContext,
+                        mock {},
+                        mock {
+                            on { ringerMode }.thenReturn(mock<RingerModeLiveData> {})
+                            on { ringerModeInternal }.thenReturn(mock<RingerModeLiveData> {})
+                        },
+                        threadFactory,
+                        audioManager,
+                        mock {},
+                        mock {},
+                        mock {},
+                        volumeControllerAdapter,
+                        accessibilityManager,
+                        packageManager,
+                        wakefulnessLifecycle,
+                        keyguardManager,
+                        activityManager,
+                        mock { on { userContext }.thenReturn(applicationContext) },
+                        dumpManager,
+                        audioSharingInteractor,
+                        mock {},
+                    )
+                    .apply {
+                        setEnableDialogs(true, true)
+                        addCallback(callbacks, Handler(looper))
+                    }
+        }
+
+    @Test
+    @EnableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER)
+    fun useVolumeControllerEnabled_listensToVolumeController() =
+        testVolumeController { stream: Int, flags: Int ->
+            audioRepository.sendVolumeControllerEvent(
+                VolumeControllerEvent.VolumeChanged(streamType = stream, flags = flags)
+            )
+        }
+
+    @Test
+    @DisableFlags(Flags.FLAG_USE_VOLUME_CONTROLLER)
+    fun useVolumeControllerDisabled_listensToVolumeController() =
+        testVolumeController { stream: Int, flags: Int ->
+            audioManager.emitVolumeChange(stream, flags)
+        }
+
+    private fun testVolumeController(
+        emitVolumeChange: suspend Kosmos.(stream: Int, flags: Int) -> Unit
+    ) =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(wakefulnessLifecycle.wakefulness)
+                    .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
+                underTest.setVolumeController()
+                runCurrent()
+
+                emitVolumeChange(AudioManager.STREAM_SYSTEM, AudioManager.FLAG_SHOW_UI)
+                runCurrent()
+                TestableLooper.get(this@VolumeDialogControllerImplTestKt).processAllMessages()
+
+                verify(callbacks) { 1 * { onShowRequested(any(), any(), any()) } }
+            }
+        }
+
+    private companion object {
+
+        private fun AudioManager.emitVolumeChange(stream: Int, flags: Int = 0) {
+            val captor = argumentCaptor<IVolumeController>()
+            verify(this) { 1 * { volumeController = captor.capture() } }
+            captor.firstValue.volumeChanged(stream, flags)
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index caa1779..1e2648b22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -257,13 +257,13 @@
 
     private State createShellState() {
         State state = new VolumeDialogController.State();
-        for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
+        for (int stream : STREAMS.keySet()) {
             VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
-            ss.name = STREAMS.get(i);
+            ss.name = STREAMS.get(stream);
             ss.level = 1;
             ss.levelMin = 0;
             ss.levelMax = 25;
-            state.states.append(i, ss);
+            state.states.append(stream, ss);
         }
         return state;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 6fb70de..60a15915f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -223,8 +223,11 @@
     }
 
     private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
+        // TODO(b/281648899) remove the when(mWallpaperManager.peekBitmapDimensions(...))
         when(mWallpaperManager.peekBitmapDimensions(anyInt(), anyBoolean()))
                 .thenReturn(new Rect(0, 0, bitmapWidth, bitmapHeight));
+        when(mWallpaperManager.peekBitmapDimensionsAsUser(anyInt(), anyBoolean(), anyInt()))
+                .thenReturn(new Rect(0, 0, bitmapWidth, bitmapHeight));
         when(mWallpaperBitmap.getWidth()).thenReturn(bitmapWidth);
         when(mWallpaperBitmap.getHeight()).thenReturn(bitmapHeight);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 2021f02..55aff13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -123,11 +123,12 @@
                 /* taskId= */ 0,
                 "locus",
                 /* isDismissable= */ true,
+                directExecutor(),
                 directExecutor()
             ) {}
         } else {
             val intent = Intent(Intent.ACTION_VIEW).setPackage(mContext.packageName)
-            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor())
+            Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor(), directExecutor())
         }
     }
 }
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 e5e04dc..3e7980d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -102,6 +102,7 @@
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.protolog.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.launcher3.icons.BubbleIconFactory;
 import com.android.systemui.SysuiTestCase;
@@ -168,7 +169,6 @@
 import com.android.wm.shell.bubbles.BubbleDataRepository;
 import com.android.wm.shell.bubbles.BubbleEducationController;
 import com.android.wm.shell.bubbles.BubbleEntry;
-import com.android.wm.shell.bubbles.BubbleExpandedViewManager;
 import com.android.wm.shell.bubbles.BubbleLogger;
 import com.android.wm.shell.bubbles.BubbleOverflow;
 import com.android.wm.shell.bubbles.BubbleStackView;
@@ -184,11 +184,11 @@
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.common.bubbles.BubbleBarLocation;
-import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils;
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
 import com.android.wm.shell.sysui.ShellCommandHandler;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
@@ -381,6 +381,9 @@
 
     @Before
     public void setUp() throws Exception {
+        // Make sure ProtoLog is initialized before any logging occurs.
+        ProtoLog.init();
+
         MockitoAnnotations.initMocks(this);
         PhysicsAnimatorTestUtils.prepareForTest();
 
@@ -459,7 +462,7 @@
                 mContext.getSystemService(WindowManager.class));
         mPositioner.setMaxBubbles(5);
         mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, mEducationController,
-                syncExecutor);
+                syncExecutor, syncExecutor);
 
         when(mUserManager.getProfiles(ActivityManager.getCurrentUser())).thenReturn(
                 Collections.singletonList(mock(UserInfo.class)));
@@ -1404,7 +1407,6 @@
                 .thenReturn(userContext);
 
         BubbleViewInfoTask.BubbleViewInfo info = BubbleViewInfoTask.BubbleViewInfo.populate(context,
-                BubbleExpandedViewManager.fromBubbleController(mBubbleController),
                 () -> new BubbleTaskView(mock(TaskView.class), mock(Executor.class)),
                 mPositioner,
                 mBubbleController.getStackView(),
@@ -2465,9 +2467,10 @@
         workEntry.setBubbleMetadata(getMetadata());
         workEntry.setFlagBubble(true);
 
+        SyncExecutor executor = new SyncExecutor();
         return new Bubble(mBubblesManager.notifToBubbleEntry(workEntry),
                 null,
-                mock(Bubbles.PendingIntentCanceledListener.class), new SyncExecutor());
+                mock(Bubbles.PendingIntentCanceledListener.class), executor, executor);
     }
 
     private BubbleEntry createBubbleEntry(boolean isConversation) {
diff --git a/packages/SystemUI/tests/utils/src/android/app/StatusBarManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/StatusBarManagerKosmos.kt
new file mode 100644
index 0000000..6251ae9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/app/StatusBarManagerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.statusBarManager by Kosmos.Fixture { mock<StatusBarManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
new file mode 100644
index 0000000..8541d77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/settingslib/notification/modes/ZenIconLoaderKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.notification.modes
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.google.common.util.concurrent.MoreExecutors
+
+val Kosmos.zenIconLoader by Fixture { ZenIconLoader(MoreExecutors.newDirectExecutorService()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
new file mode 100644
index 0000000..5c39e32
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/CaptioningRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.fakeCaptioningRepository by Kosmos.Fixture { FakeCaptioningRepository() }
+val Kosmos.captioningRepository by Kosmos.Fixture { fakeCaptioningRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.kt
new file mode 100644
index 0000000..2a0e764
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeCaptioningRepository.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.accessibility.data.repository
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeCaptioningRepository : CaptioningRepository {
+
+    private val mutableIsSystemAudioCaptioningEnabled = MutableStateFlow(false)
+    override val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
+        get() = mutableIsSystemAudioCaptioningEnabled.asStateFlow()
+
+    private val mutableIsSystemAudioCaptioningUiEnabled = MutableStateFlow(false)
+    override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
+        get() = mutableIsSystemAudioCaptioningUiEnabled.asStateFlow()
+
+    override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
+        mutableIsSystemAudioCaptioningEnabled.value = isEnabled
+    }
+
+    fun setIsSystemAudioCaptioningUiEnabled(isSystemAudioCaptioningUiEnabled: Boolean) {
+        mutableIsSystemAudioCaptioningUiEnabled.value = isSystemAudioCaptioningUiEnabled
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt
new file mode 100644
index 0000000..2125e95
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/domain/interactor/CaptioningInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.domain.interactor
+
+import com.android.systemui.accessibility.data.repository.captioningRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.captioningInteractor by Kosmos.Fixture { CaptioningInteractor(captioningRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
deleted file mode 100644
index 60d97d1..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/ComposeBouncerFlagsKosmos.kt
+++ /dev/null
@@ -1,22 +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.bouncer.shared.flag
-
-import com.android.systemui.kosmos.Kosmos
-
-var Kosmos.fakeComposeBouncerFlags by Kosmos.Fixture { FakeComposeBouncerFlags() }
-val Kosmos.composeBouncerFlags by Kosmos.Fixture<ComposeBouncerFlags> { fakeComposeBouncerFlags }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.kt
deleted file mode 100644
index 7482c0f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/shared/flag/FakeComposeBouncerFlags.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.bouncer.shared.flag
-
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-
-class FakeComposeBouncerFlags(var composeBouncerEnabled: Boolean = false) : ComposeBouncerFlags {
-    override fun isComposeBouncerOrSceneContainerEnabled(): Boolean {
-        return SceneContainerFlag.isEnabled || composeBouncerEnabled
-    }
-
-    @Deprecated(
-        "Avoid using this, this is meant to be used only by the glue code " +
-            "that includes compose bouncer in legacy keyguard.",
-        replaceWith = ReplaceWith("isComposeBouncerOrSceneContainerEnabled()")
-    )
-    override fun isOnlyComposeBouncerEnabled(): Boolean = composeBouncerEnabled
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
index e70631e..5c5969d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerMessageViewModelKosmos.kt
@@ -14,38 +14,43 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
 package com.android.systemui.bouncer.ui.viewmodel
 
 import android.content.applicationContext
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
 import com.android.systemui.deviceentry.domain.interactor.biometricMessageInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
 import com.android.systemui.util.time.systemClock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
-@ExperimentalCoroutinesApi
-val Kosmos.bouncerMessageViewModel by
-    Kosmos.Fixture {
-        BouncerMessageViewModel(
-            applicationContext = applicationContext,
-            applicationScope = testScope.backgroundScope,
-            bouncerInteractor = bouncerInteractor,
-            simBouncerInteractor = simBouncerInteractor,
-            authenticationInteractor = authenticationInteractor,
-            selectedUser = userSwitcherViewModel.selectedUser,
-            clock = systemClock,
-            biometricMessageInteractor = biometricMessageInteractor,
-            faceAuthInteractor = deviceEntryFaceAuthInteractor,
-            deviceUnlockedInteractor = deviceUnlockedInteractor,
-            deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
-            flags = composeBouncerFlags,
-        )
+val Kosmos.bouncerMessageViewModel by Fixture {
+    BouncerMessageViewModel(
+        applicationContext = applicationContext,
+        bouncerInteractor = bouncerInteractor,
+        simBouncerInteractor = simBouncerInteractor,
+        authenticationInteractor = authenticationInteractor,
+        userSwitcherViewModel = userSwitcherViewModel,
+        clock = systemClock,
+        biometricMessageInteractor = biometricMessageInteractor,
+        faceAuthInteractor = deviceEntryFaceAuthInteractor,
+        deviceUnlockedInteractor = deviceUnlockedInteractor,
+        deviceEntryBiometricsAllowedInteractor = deviceEntryBiometricsAllowedInteractor,
+    )
+}
+
+val Kosmos.bouncerMessageViewModelFactory by Fixture {
+    object : BouncerMessageViewModel.Factory {
+        override fun create(): BouncerMessageViewModel {
+            return bouncerMessageViewModel
+        }
     }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index c3dad74..649e4e8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -21,35 +21,103 @@
 import android.app.admin.devicePolicyManager
 import android.content.applicationContext
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.bouncerActionButtonInteractor
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
-import com.android.systemui.bouncer.shared.flag.composeBouncerFlags
 import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.kosmos.testScope
 import com.android.systemui.user.domain.interactor.selectedUserInteractor
 import com.android.systemui.user.ui.viewmodel.userSwitcherViewModel
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.StateFlow
 
-val Kosmos.bouncerViewModel by Fixture {
-    BouncerViewModel(
-        applicationContext = applicationContext,
-        applicationScope = testScope.backgroundScope,
-        mainDispatcher = testDispatcher,
+val Kosmos.bouncerUserActionsViewModel by Fixture {
+    BouncerUserActionsViewModel(
         bouncerInteractor = bouncerInteractor,
-        inputMethodInteractor = inputMethodInteractor,
-        simBouncerInteractor = simBouncerInteractor,
-        authenticationInteractor = authenticationInteractor,
-        selectedUserInteractor = selectedUserInteractor,
-        devicePolicyManager = devicePolicyManager,
-        bouncerMessageViewModel = bouncerMessageViewModel,
-        flags = composeBouncerFlags,
-        selectedUser = userSwitcherViewModel.selectedUser,
-        users = userSwitcherViewModel.users,
-        userSwitcherMenu = userSwitcherViewModel.menu,
-        actionButton = bouncerActionButtonInteractor.actionButton,
     )
 }
+
+val Kosmos.bouncerUserActionsViewModelFactory by Fixture {
+    object : BouncerUserActionsViewModel.Factory {
+        override fun create(): BouncerUserActionsViewModel {
+            return bouncerUserActionsViewModel
+        }
+    }
+}
+
+val Kosmos.bouncerSceneContentViewModel by Fixture {
+    BouncerSceneContentViewModel(
+        applicationContext = applicationContext,
+        bouncerInteractor = bouncerInteractor,
+        authenticationInteractor = authenticationInteractor,
+        devicePolicyManager = devicePolicyManager,
+        bouncerMessageViewModelFactory = bouncerMessageViewModelFactory,
+        userSwitcher = userSwitcherViewModel,
+        actionButtonInteractor = bouncerActionButtonInteractor,
+        pinViewModelFactory = pinBouncerViewModelFactory,
+        patternViewModelFactory = patternBouncerViewModelFactory,
+        passwordViewModelFactory = passwordBouncerViewModelFactory,
+    )
+}
+
+val Kosmos.bouncerSceneContentViewModelFactory by Fixture {
+    object : BouncerSceneContentViewModel.Factory {
+        override fun create(): BouncerSceneContentViewModel {
+            return bouncerSceneContentViewModel
+        }
+    }
+}
+
+val Kosmos.pinBouncerViewModelFactory by Fixture {
+    object : PinBouncerViewModel.Factory {
+        override fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+            authenticationMethod: AuthenticationMethodModel,
+        ): PinBouncerViewModel {
+            return PinBouncerViewModel(
+                applicationContext = applicationContext,
+                interactor = bouncerInteractor,
+                simBouncerInteractor = simBouncerInteractor,
+                isInputEnabled = isInputEnabled,
+                onIntentionalUserInput = onIntentionalUserInput,
+                authenticationMethod = authenticationMethod,
+            )
+        }
+    }
+}
+
+val Kosmos.patternBouncerViewModelFactory by Fixture {
+    object : PatternBouncerViewModel.Factory {
+        override fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+        ): PatternBouncerViewModel {
+            return PatternBouncerViewModel(
+                applicationContext = applicationContext,
+                interactor = bouncerInteractor,
+                isInputEnabled = isInputEnabled,
+                onIntentionalUserInput = onIntentionalUserInput,
+            )
+        }
+    }
+}
+
+val Kosmos.passwordBouncerViewModelFactory by Fixture {
+    object : PasswordBouncerViewModel.Factory {
+        override fun create(
+            isInputEnabled: StateFlow<Boolean>,
+            onIntentionalUserInput: () -> Unit,
+        ): PasswordBouncerViewModel {
+            return PasswordBouncerViewModel(
+                interactor = bouncerInteractor,
+                inputMethodInteractor = inputMethodInteractor,
+                selectedUserInteractor = selectedUserInteractor,
+                isInputEnabled = isInputEnabled,
+                onIntentionalUserInput = onIntentionalUserInput,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
index 86a8ae5..1ef3464 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt
@@ -17,11 +17,8 @@
 package com.android.systemui.common.ui
 
 import android.content.applicationContext
-import android.view.layoutInflater
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.policy.configurationController
 
 val Kosmos.configurationState: ConfigurationState by
-    Kosmos.Fixture {
-        ConfigurationState(configurationController, applicationContext, layoutInflater)
-    }
+    Kosmos.Fixture { ConfigurationStateImpl(configurationController, applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/FakeUsageStatsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/FakeUsageStatsRepository.kt
new file mode 100644
index 0000000..d73de76
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/FakeUsageStatsRepository.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.common.usagestats.data.repository
+
+import android.annotation.CurrentTimeMillisLong
+import android.app.usage.UsageEvents
+import android.os.UserHandle
+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
+
+class FakeUsageStatsRepository : UsageStatsRepository {
+    private val events = mutableMapOf<UserHandle, MutableList<UsageEvents.Event>>()
+
+    override suspend fun queryActivityEvents(query: UsageStatsQuery): List<ActivityEventModel> {
+        return events
+            .getOrDefault(query.user, emptyList())
+            .filter { event ->
+                query.packageNames.isEmpty() || query.packageNames.contains(event.packageName)
+            }
+            .filter { event -> event.timeStamp in query.startTime until query.endTime }
+            .filter { event -> event.eventType.toActivityLifecycle() != Lifecycle.UNKNOWN }
+            .map { event ->
+                ActivityEventModel(
+                    instanceId = event.instanceId,
+                    packageName = event.packageName,
+                    lifecycle = event.eventType.toActivityLifecycle(),
+                    timestamp = event.timeStamp,
+                )
+            }
+    }
+
+    fun addEvent(
+        instanceId: Int,
+        user: UserHandle,
+        packageName: String,
+        @UsageEvents.Event.EventType type: Int,
+        @CurrentTimeMillisLong timestamp: Long,
+    ) {
+        events
+            .getOrPut(user) { mutableListOf() }
+            .add(
+                UsageEvents.Event(type, timestamp).apply {
+                    mPackage = packageName
+                    mInstanceId = instanceId
+                }
+            )
+    }
+}
+
+private fun Int.toActivityLifecycle(): Lifecycle =
+    when (this) {
+        UsageEvents.Event.ACTIVITY_RESUMED -> Lifecycle.RESUMED
+        UsageEvents.Event.ACTIVITY_PAUSED -> Lifecycle.PAUSED
+        UsageEvents.Event.ACTIVITY_STOPPED -> Lifecycle.STOPPED
+        UsageEvents.Event.ACTIVITY_DESTROYED -> Lifecycle.DESTROYED
+        else -> Lifecycle.UNKNOWN
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryKosmos.kt
new file mode 100644
index 0000000..7dac59e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepositoryKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.usagestats.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.usageStatsRepository: UsageStatsRepository by Kosmos.Fixture { fakeUsageStatsRepository }
+val Kosmos.fakeUsageStatsRepository by Kosmos.Fixture { FakeUsageStatsRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorKosmos.kt
new file mode 100644
index 0000000..06a680f
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/usagestats/domain/interactor/UsageStatsInteractorKosmos.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.common.usagestats.domain.interactor
+
+import com.android.systemui.common.usagestats.data.repository.usageStatsRepository
+import com.android.systemui.common.usagestats.domain.UsageStatsInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.settings.userTracker
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.usageStatsInteractor: UsageStatsInteractor by
+    Kosmos.Fixture {
+        UsageStatsInteractor(
+            userTracker = userTracker,
+            repository = usageStatsRepository,
+            systemClock = fakeSystemClock,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
index 14b1984..e0ed687 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalMediaRepository.kt
@@ -28,7 +28,7 @@
     fun mediaActive(timestamp: Long = 0L) {
         _mediaModel.value =
             CommunalMediaModel(
-                hasActiveMediaOrRecommendation = true,
+                hasAnyMediaOrRecommendation = true,
                 createdTimestampMillis = timestamp,
             )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
index c00454f..5d7e7c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt
@@ -30,7 +30,7 @@
     override fun addWidget(
         provider: ComponentName,
         user: UserHandle,
-        priority: Int,
+        rank: Int?,
         configurator: WidgetConfigurator?
     ) {
         coroutineScope.launch {
@@ -38,7 +38,7 @@
             val providerInfo = AppWidgetProviderInfo().apply { this.provider = provider }
             val configured = configurator?.configureWidget(id) ?: true
             if (configured) {
-                onConfigured(id, providerInfo, priority)
+                onConfigured(id, providerInfo, rank ?: -1)
             }
         }
     }
@@ -46,14 +46,14 @@
     fun addWidget(
         appWidgetId: Int,
         componentName: String = "pkg/cls",
-        priority: Int = 0,
+        rank: Int = 0,
         category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
         userId: Int = 0,
     ) {
         fakeDatabase[appWidgetId] =
             CommunalWidgetContentModel.Available(
                 appWidgetId = appWidgetId,
-                priority = priority,
+                rank = rank,
                 providerInfo =
                     AppWidgetProviderInfo().apply {
                         provider = ComponentName.unflattenFromString(componentName)!!
@@ -73,14 +73,14 @@
     fun addPendingWidget(
         appWidgetId: Int,
         componentName: String = "pkg/cls",
-        priority: Int = 0,
+        rank: Int = 0,
         icon: Bitmap? = null,
         userId: Int = 0,
     ) {
         fakeDatabase[appWidgetId] =
             CommunalWidgetContentModel.Pending(
                 appWidgetId = appWidgetId,
-                priority = priority,
+                rank = rank,
                 componentName = ComponentName.unflattenFromString(componentName)!!,
                 icon = icon,
                 user = UserHandle(userId),
@@ -97,8 +97,8 @@
 
     override fun abortRestoreWidgets() {}
 
-    private fun onConfigured(id: Int, providerInfo: AppWidgetProviderInfo, priority: Int) {
+    private fun onConfigured(id: Int, providerInfo: AppWidgetProviderInfo, rank: Int) {
         _communalWidgets.value +=
-            listOf(CommunalWidgetContentModel.Available(id, providerInfo, priority))
+            listOf(CommunalWidgetContentModel.Available(id, providerInfo, rank))
     }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 4ad046c..629fda6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.plugins.activityStarter
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.settings.userTracker
+import com.android.systemui.statusbar.phone.fakeManagedProfileController
 import com.android.systemui.user.data.repository.fakeUserRepository
 import com.android.systemui.util.mockito.mock
 
@@ -61,6 +62,7 @@
         sceneInteractor = sceneInteractor,
         logBuffer = logcatLogBuffer("CommunalInteractor"),
         tableLogBuffer = mock(),
+        managedProfileController = fakeManagedProfileController
     )
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
index ee48c10..2ab8221 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.communal.domain.interactor
 
 import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.shared.log.communalSceneLogger
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 
@@ -24,6 +25,7 @@
     Kosmos.Fixture {
         CommunalSceneInteractor(
             applicationScope = applicationCoroutineScope,
-            communalSceneRepository = communalSceneRepository,
+            repository = communalSceneRepository,
+            logger = communalSceneLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.kt
new file mode 100644
index 0000000..8124224
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/WidgetTrampolineInteractorKosmos.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.communal.domain.interactor
+
+import com.android.systemui.common.usagestats.domain.interactor.usageStatsInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.shared.system.taskStackChangeListeners
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.widgetTrampolineInteractor: WidgetTrampolineInteractor by
+    Kosmos.Fixture {
+        WidgetTrampolineInteractor(
+            activityStarter = activityStarter,
+            systemClock = fakeSystemClock,
+            keyguardTransitionInteractor = keyguardTransitionInteractor,
+            taskStackChangeListeners = taskStackChangeListeners,
+            usageStatsInteractor = usageStatsInteractor,
+            logBuffer = logcatLogBuffer("WidgetTrampolineInteractor"),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt
new file mode 100644
index 0000000..b560ee8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/shared/log/CommunalSceneLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.log
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+
+val Kosmos.communalSceneLogger: CommunalSceneLogger by
+    Kosmos.Fixture { CommunalSceneLogger(logcatLogBuffer("CommunalSceneLogger")) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
index b9be04d..3dfe0ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractorKosmos.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.user.data.repository.userRepository
 import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -57,6 +58,7 @@
             powerInteractor = powerInteractor,
             biometricSettingsRepository = biometricSettingsRepository,
             trustManager = trustManager,
+            sceneInteractor = { sceneInteractor },
             deviceEntryFaceAuthStatusInteractor = deviceEntryFaceAuthStatusInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index caa6e99..13116e7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
 import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.deviceentry.data.repository.deviceEntryRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -34,5 +35,6 @@
             sceneInteractor = sceneInteractor,
             deviceUnlockedInteractor = deviceUnlockedInteractor,
             alternateBouncerInteractor = alternateBouncerInteractor,
+            dismissCallbackRegistry = dismissCallbackRegistry,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
index aa1968a..fb4e2fb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/FakeContextualEducationRepository.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.education.data.repository
 
 import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.education.data.model.EduDeviceConnectionTime
 import com.android.systemui.education.data.model.GestureEduModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,24 +26,36 @@
 class FakeContextualEducationRepository : ContextualEducationRepository {
 
     private val userGestureMap = mutableMapOf<Int, GestureEduModel>()
-    private val _gestureEduModels = MutableStateFlow(GestureEduModel())
+    private val _gestureEduModels = MutableStateFlow(GestureEduModel(userId = 0))
     private val gestureEduModelsFlow = _gestureEduModels.asStateFlow()
+
+    private val userEduDeviceConnectionTimeMap = mutableMapOf<Int, EduDeviceConnectionTime>()
+    private val _eduDeviceConnectionTime = MutableStateFlow(EduDeviceConnectionTime())
+    private val eduDeviceConnectionTime = _eduDeviceConnectionTime.asStateFlow()
+
     private var currentUser: Int = 0
 
     override fun setUser(userId: Int) {
         if (!userGestureMap.contains(userId)) {
-            userGestureMap[userId] = GestureEduModel()
+            userGestureMap[userId] = GestureEduModel(userId = userId)
+            userEduDeviceConnectionTimeMap[userId] = EduDeviceConnectionTime()
         }
         // save data of current user to the map
         userGestureMap[currentUser] = _gestureEduModels.value
+        userEduDeviceConnectionTimeMap[currentUser] = _eduDeviceConnectionTime.value
         // switch to data of new user
         _gestureEduModels.value = userGestureMap[userId]!!
+        _eduDeviceConnectionTime.value = userEduDeviceConnectionTimeMap[userId]!!
     }
 
     override fun readGestureEduModelFlow(gestureType: GestureType): Flow<GestureEduModel> {
         return gestureEduModelsFlow
     }
 
+    override fun readEduDeviceConnectionTime(): Flow<EduDeviceConnectionTime> {
+        return eduDeviceConnectionTime
+    }
+
     override suspend fun updateGestureEduModel(
         gestureType: GestureType,
         transform: (GestureEduModel) -> GestureEduModel
@@ -50,4 +63,11 @@
         val currentModel = _gestureEduModels.value
         _gestureEduModels.value = transform(currentModel)
     }
+
+    override suspend fun updateEduDeviceConnectionTime(
+        transform: (EduDeviceConnectionTime) -> EduDeviceConnectionTime
+    ) {
+        val currentModel = _eduDeviceConnectionTime.value
+        _eduDeviceConnectionTime.value = transform(currentModel)
+    }
 }
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 5088677..811c653 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
@@ -16,19 +16,36 @@
 
 package com.android.systemui.education.domain.interactor
 
+import android.hardware.input.InputManager
 import com.android.systemui.education.data.repository.fakeEduClock
+import com.android.systemui.inputdevice.data.repository.UserInputDeviceRepository
+import com.android.systemui.keyboard.data.repository.keyboardRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.touchpad.data.repository.touchpadRepository
+import com.android.systemui.user.data.repository.userRepository
+import org.mockito.kotlin.mock
 
 var Kosmos.keyboardTouchpadEduInteractor by
     Kosmos.Fixture {
         KeyboardTouchpadEduInteractor(
             backgroundScope = testScope.backgroundScope,
             contextualEducationInteractor = contextualEducationInteractor,
-            clock = fakeEduClock
+            userInputDeviceRepository =
+                UserInputDeviceRepository(
+                    testDispatcher,
+                    keyboardRepository,
+                    touchpadRepository,
+                    userRepository
+                ),
+            clock = fakeEduClock,
+            inputManager = mockEduInputManager
         )
     }
 
+var Kosmos.mockEduInputManager by Kosmos.Fixture { mock<InputManager>() }
+
 var Kosmos.keyboardTouchpadEduStatsInteractor by
     Kosmos.Fixture {
         KeyboardTouchpadEduStatsInteractorImpl(
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 1107971..c252924 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
@@ -22,7 +22,7 @@
 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
-import com.android.systemui.Flags.FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR
+import com.android.systemui.Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
 import com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_SYSUI
 import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
 
@@ -35,7 +35,7 @@
     FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR,
     FLAG_KEYGUARD_WM_STATE_REFACTOR,
     FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
-    FLAG_NOTIFICATIONS_HEADS_UP_REFACTOR,
+    FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
     FLAG_PREDICTIVE_BACK_SYSUI,
     FLAG_SCENE_CONTAINER,
     FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt
new file mode 100644
index 0000000..6b8919d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/graphics/ImageLoaderKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.graphics
+
+import android.content.testableContext
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+
+var Kosmos.imageLoader by Kosmos.Fixture { ImageLoader(testableContext, testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
index 434953f..ff71f2f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/VibratorHelperKosmos.kt
@@ -17,5 +17,7 @@
 package com.android.systemui.haptics
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.VibratorHelper
 
-var Kosmos.vibratorHelper by Kosmos.Fixture { FakeVibratorHelper() }
+var Kosmos.vibratorHelper: VibratorHelper by Kosmos.Fixture { fakeVibratorHelper }
+val Kosmos.fakeVibratorHelper by Kosmos.Fixture { FakeVibratorHelper() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.kt
new file mode 100644
index 0000000..5ad973a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/FakeMSDLPlayer.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.haptics.msdl
+
+import com.google.android.msdl.data.model.FeedbackLevel
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.InteractionProperties
+import com.google.android.msdl.domain.MSDLPlayer
+
+class FakeMSDLPlayer : MSDLPlayer {
+    var currentFeedbackLevel = FeedbackLevel.DEFAULT
+    var latestTokenPlayed: MSDLToken? = null
+        private set
+
+    var latestPropertiesPlayed: InteractionProperties? = null
+        private set
+
+    override fun getSystemFeedbackLevel(): FeedbackLevel = currentFeedbackLevel
+
+    override fun playToken(token: MSDLToken, properties: InteractionProperties?) {
+        latestTokenPlayed = token
+        latestPropertiesPlayed = properties
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
new file mode 100644
index 0000000..f5a05b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.msdl
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.msdlPlayer by Kosmos.Fixture { FakeMSDLPlayer() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt
new file mode 100644
index 0000000..827f0d2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/inputdevice/tutorial/InputDeviceTutorialKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.inputdevice.tutorial
+
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+var Kosmos.inputDeviceTutorialLogger: InputDeviceTutorialLogger by
+    Kosmos.Fixture { mock<InputDeviceTutorialLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index e96aeada..5753c6c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -27,7 +27,6 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.filterNotNull
 
 @SysUISingleton
@@ -37,38 +36,41 @@
     private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
     override val authenticationStatus: Flow<FaceAuthenticationStatus> =
         _authenticationStatus.filterNotNull()
+
     fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
         _authenticationStatus.value = status
     }
+
     private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
     override val detectionStatus: Flow<FaceDetectionStatus>
         get() = _detectionStatus.filterNotNull()
+
     fun setDetectionStatus(status: FaceDetectionStatus) {
         _detectionStatus.value = status
     }
 
     private val _isLockedOut = MutableStateFlow(false)
     override val isLockedOut = _isLockedOut
-    private val _runningAuthRequest = MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?>(null)
-    val runningAuthRequest: StateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
-        _runningAuthRequest.asStateFlow()
+    val runningAuthRequest: MutableStateFlow<Pair<FaceAuthUiEvent, Boolean>?> =
+        MutableStateFlow(null)
 
     private val _isAuthRunning = MutableStateFlow(false)
     override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
 
     override val isBypassEnabled = MutableStateFlow(false)
+
     override fun setLockedOut(isLockedOut: Boolean) {
         _isLockedOut.value = isLockedOut
     }
 
     override fun requestAuthenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
-        _runningAuthRequest.value = uiEvent to fallbackToDetection
+        runningAuthRequest.value = uiEvent to fallbackToDetection
         _isAuthRunning.value = true
     }
 
     override fun cancel() {
         _isAuthRunning.value = false
-        _runningAuthRequest.value = null
+        runningAuthRequest.value = null
     }
 }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 4571c19..54a6c0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -60,13 +60,13 @@
     override val bottomAreaAlpha: StateFlow<Float> = _bottomAreaAlpha
 
     private val _isKeyguardShowing = MutableStateFlow(false)
-    override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
+    override val isKeyguardShowing: StateFlow<Boolean> = _isKeyguardShowing
 
     private val _isKeyguardUnlocked = MutableStateFlow(false)
     override val isKeyguardDismissible: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow()
 
     private val _isKeyguardOccluded = MutableStateFlow(false)
-    override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
+    override val isKeyguardOccluded: StateFlow<Boolean> = _isKeyguardOccluded
 
     private val _isDozing = MutableStateFlow(false)
     override val isDozing: StateFlow<Boolean> = _isDozing
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 616f2b6..a73c184 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -102,6 +102,45 @@
     }
 
     /**
+     * Sends the provided [step] and makes sure that all previous [TransitionState]'s are sent when
+     * [fillInSteps] is true. e.g. when a step FINISHED is provided, a step with STARTED and RUNNING
+     * is also sent.
+     */
+    suspend fun sendTransitionSteps(
+        step: TransitionStep,
+        testScope: TestScope,
+        fillInSteps: Boolean = true,
+    ) {
+        if (fillInSteps && step.transitionState != TransitionState.STARTED) {
+            sendTransitionStep(
+                step =
+                    TransitionStep(
+                        transitionState = TransitionState.STARTED,
+                        from = step.from,
+                        to = step.to,
+                        value = 0f,
+                    )
+            )
+            testScope.testScheduler.runCurrent()
+
+            if (step.transitionState != TransitionState.RUNNING) {
+                sendTransitionStep(
+                    step =
+                        TransitionStep(
+                            transitionState = TransitionState.RUNNING,
+                            from = step.from,
+                            to = step.to,
+                            value = 0.6f,
+                        )
+                )
+                testScope.testScheduler.runCurrent()
+            }
+        }
+        sendTransitionStep(step = step)
+        testScope.testScheduler.runCurrent()
+    }
+
+    /**
      * Sends TransitionSteps between [from] and [to], calling [runCurrent] after each step.
      *
      * By default, sends steps through FINISHED (STARTED, RUNNING, FINISHED) but can be halted part
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt
new file mode 100644
index 0000000..82a5311
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DismissKeyguardInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.keyguardDismissTransitionInteractor: KeyguardDismissTransitionInteractor by
+    Kosmos.Fixture {
+        KeyguardDismissTransitionInteractor(
+            repository = keyguardTransitionRepository,
+            fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor,
+            fromPrimaryBouncerTransitionInteractor = fromPrimaryBouncerTransitionInteractor,
+            fromAodTransitionInteractor = fromAodTransitionInteractor,
+            fromAlternateBouncerTransitionInteractor = fromAlternateBouncerTransitionInteractor,
+            fromDozingTransitionInteractor = fromDozingTransitionInteractor,
+            fromOccludedTransitionInteractor = fromOccludedTransitionInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index f162594..64ae051 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
@@ -38,6 +39,7 @@
             mainDispatcher = testDispatcher,
             keyguardInteractor = keyguardInteractor,
             glanceableHubTransitions = glanceableHubTransitions,
+            communalSceneInteractor = communalSceneInteractor,
             communalSettingsInteractor = communalSettingsInteractor,
             powerInteractor = powerInteractor,
             keyguardOcclusionInteractor = keyguardOcclusionInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 957f092..27eadb1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -16,10 +16,12 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.resolver.notifShadeSceneFamilyResolver
 import com.android.systemui.scene.domain.resolver.quickSettingsSceneFamilyResolver
@@ -37,5 +39,7 @@
             deviceEntryInteractor = deviceEntryInteractor,
             quickSettingsSceneFamilyResolver = quickSettingsSceneFamilyResolver,
             notifShadeSceneFamilyResolver = notifShadeSceneFamilyResolver,
+            powerInteractor = powerInteractor,
+            alternateBouncerInteractor = alternateBouncerInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
deleted file mode 100644
index 9b7bca6..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorFactory.kt
+++ /dev/null
@@ -1,129 +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.content.Context
-import android.os.Handler
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
-import com.android.systemui.bouncer.ui.BouncerView
-import com.android.systemui.classifier.FalsingCollector
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
-import com.android.systemui.keyguard.DismissCallbackRegistry
-import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.time.FakeSystemClock
-import kotlinx.coroutines.test.TestScope
-import org.mockito.Mockito.mock
-
-/**
- * Helper to create a new KeyguardDismissInteractor in a way that doesn't require modifying many
- * tests whenever we add a constructor param.
- */
-object KeyguardDismissInteractorFactory {
-    @JvmOverloads
-    @JvmStatic
-    fun create(
-        context: Context,
-        testScope: TestScope,
-        trustRepository: FakeTrustRepository = FakeTrustRepository(),
-        keyguardRepository: FakeKeyguardRepository = FakeKeyguardRepository(),
-        bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
-        keyguardUpdateMonitor: KeyguardUpdateMonitor = mock(KeyguardUpdateMonitor::class.java),
-        powerRepository: FakePowerRepository = FakePowerRepository(),
-        userRepository: FakeUserRepository = FakeUserRepository(),
-    ): WithDependencies {
-        val primaryBouncerInteractor =
-            PrimaryBouncerInteractor(
-                bouncerRepository,
-                mock(BouncerView::class.java),
-                mock(Handler::class.java),
-                mock(KeyguardStateController::class.java),
-                mock(KeyguardSecurityModel::class.java),
-                mock(PrimaryBouncerCallbackInteractor::class.java),
-                mock(FalsingCollector::class.java),
-                mock(DismissCallbackRegistry::class.java),
-                context,
-                keyguardUpdateMonitor,
-                trustRepository,
-                testScope.backgroundScope,
-                mock(SelectedUserInteractor::class.java),
-                mock(DeviceEntryFaceAuthInteractor::class.java),
-            )
-        val alternateBouncerInteractor =
-            AlternateBouncerInteractor(
-                mock(StatusBarStateController::class.java),
-                mock(KeyguardStateController::class.java),
-                bouncerRepository,
-                FakeFingerprintPropertyRepository(),
-                FakeBiometricSettingsRepository(),
-                FakeSystemClock(),
-                keyguardUpdateMonitor,
-                { mock(DeviceEntryBiometricsAllowedInteractor::class.java) },
-                { mock(KeyguardInteractor::class.java) },
-                { mock(KeyguardTransitionInteractor::class.java) },
-                { mock(SceneInteractor::class.java) },
-                testScope.backgroundScope,
-            )
-        val powerInteractorWithDeps =
-            PowerInteractorFactory.create(
-                repository = powerRepository,
-            )
-        val selectedUserInteractor = SelectedUserInteractor(repository = userRepository)
-        return WithDependencies(
-            trustRepository = trustRepository,
-            keyguardRepository = keyguardRepository,
-            bouncerRepository = bouncerRepository,
-            keyguardUpdateMonitor = keyguardUpdateMonitor,
-            powerRepository = powerRepository,
-            userRepository = userRepository,
-            interactor =
-                KeyguardDismissInteractor(
-                    trustRepository,
-                    keyguardRepository,
-                    primaryBouncerInteractor,
-                    alternateBouncerInteractor,
-                    powerInteractorWithDeps.powerInteractor,
-                    selectedUserInteractor,
-                ),
-        )
-    }
-
-    data class WithDependencies(
-        val trustRepository: FakeTrustRepository,
-        val keyguardRepository: FakeKeyguardRepository,
-        val bouncerRepository: FakeKeyguardBouncerRepository,
-        val keyguardUpdateMonitor: KeyguardUpdateMonitor,
-        val powerRepository: FakePowerRepository,
-        val userRepository: FakeUserRepository,
-        val interactor: KeyguardDismissInteractor,
-    )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
index f33ca95..ace1157 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -20,7 +20,10 @@
 import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.data.repository.trustRepository
+import com.android.systemui.keyguard.dismissCallbackRegistry
 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.user.domain.interactor.selectedUserInteractor
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -29,11 +32,14 @@
 val Kosmos.keyguardDismissInteractor by
     Kosmos.Fixture {
         KeyguardDismissInteractor(
-            trustRepository = trustRepository,
+            mainDispatcher = testDispatcher,
+            scope = applicationCoroutineScope,
             keyguardRepository = keyguardRepository,
             primaryBouncerInteractor = primaryBouncerInteractor,
+            selectedUserInteractor = selectedUserInteractor,
+            dismissCallbackRegistry = dismissCallbackRegistry,
+            trustRepository = trustRepository,
             alternateBouncerInteractor = alternateBouncerInteractor,
             powerInteractor = powerInteractor,
-            selectedUserInteractor = selectedUserInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
index c6b5ed0..007d229 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractorKosmos.kt
@@ -27,7 +27,7 @@
             applicationCoroutineScope,
             keyguardRepository,
             biometricSettingsRepository,
-            keyguardTransitionInteractor,
+            keyguardDismissTransitionInteractor,
             internalTransitionInteractor = internalKeyguardTransitionInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index a95609e..f5232ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -54,6 +54,7 @@
         sceneInteractor: SceneInteractor = mock(),
         fromGoneTransitionInteractor: FromGoneTransitionInteractor = mock(),
         fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor = mock(),
+        fromOccludedTransitionInteractor: FromOccludedTransitionInteractor = mock(),
         sharedNotificationContainerInteractor: SharedNotificationContainerInteractor? = null,
         powerInteractor: PowerInteractor = PowerInteractorFactory.create().powerInteractor,
         testScope: CoroutineScope = TestScope(),
@@ -100,6 +101,7 @@
                 sceneInteractorProvider = { sceneInteractor },
                 fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
                 fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
+                fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor },
                 sharedNotificationContainerInteractor = { sncInteractor },
                 applicationScope = testScope,
             ),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
index 5ab56e9..e85114d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
@@ -38,6 +38,7 @@
             sceneInteractorProvider = { sceneInteractor },
             fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
             fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
+            fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor },
             sharedNotificationContainerInteractor = { sharedNotificationContainerInteractor },
             applicationScope = testScope.backgroundScope,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
index 73799b6..769612c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorKosmos.kt
@@ -20,6 +20,7 @@
 import android.view.accessibility.accessibilityManagerWrapper
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
 import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
@@ -38,5 +39,6 @@
             broadcastDispatcher = broadcastDispatcher,
             accessibilityManager = accessibilityManagerWrapper,
             pulsingGestureListener = pulsingGestureListener,
+            faceAuthInteractor = deviceEntryFaceAuthInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index b68d6a0..aa94c36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
@@ -27,13 +26,6 @@
         KeyguardTransitionInteractor(
             scope = applicationCoroutineScope,
             repository = keyguardTransitionRepository,
-            keyguardRepository = keyguardRepository,
-            fromLockscreenTransitionInteractor = { fromLockscreenTransitionInteractor },
-            fromPrimaryBouncerTransitionInteractor = { fromPrimaryBouncerTransitionInteractor },
-            fromAodTransitionInteractor = { fromAodTransitionInteractor },
-            fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
-            fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
-            fromOccludedTransitionInteractor = { fromOccludedTransitionInteractor },
             sceneInteractor = sceneInteractor
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
deleted file mode 100644
index 9bd346e..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/data/GestureRepositoryKosmos.kt
+++ /dev/null
@@ -1,25 +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.gesture.data
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testDispatcher
-import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
-import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
-
-val Kosmos.gestureRepository: GestureRepository by
-    Kosmos.Fixture { GestureRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
index 658aaa6..1d2439c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/gesture/domain/GestureInteractorKosmos.kt
@@ -16,12 +16,23 @@
 
 package com.android.systemui.keyguard.gesture.domain
 
-import com.android.systemui.keyguard.gesture.data.gestureRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigationbar.gestural.data.gestureRepository
 import com.android.systemui.navigationbar.gestural.domain.GestureInteractor
+import com.android.systemui.shared.system.activityManagerWrapper
+import com.android.systemui.shared.system.taskStackChangeListeners
 
 val Kosmos.gestureInteractor: GestureInteractor by
     Kosmos.Fixture {
-        GestureInteractor(gestureRepository = gestureRepository, scope = applicationCoroutineScope)
+        GestureInteractor(
+            gestureRepository = gestureRepository,
+            mainDispatcher = testDispatcher,
+            backgroundCoroutineContext = backgroundCoroutineContext,
+            scope = applicationCoroutineScope,
+            activityManagerWrapper = activityManagerWrapper,
+            taskStackChangeListeners = taskStackChangeListeners
+        )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
index 2919d3f..1e95fc1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.keyguard.ui.binder
 
 import android.content.applicationContext
-import android.view.layoutInflater
 import android.view.mockedLayoutInflater
 import android.view.windowManager
 import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
@@ -25,7 +24,6 @@
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
-import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
@@ -50,10 +48,10 @@
             alternateBouncerDependencies = { alternateBouncerDependencies },
             windowManager = { windowManager },
             layoutInflater = { mockedLayoutInflater },
-            dismissCallbackRegistry = dismissCallbackRegistry,
         )
     }
 
+@ExperimentalCoroutinesApi
 private val Kosmos.alternateBouncerDependencies by
     Kosmos.Fixture {
         AlternateBouncerDependencies(
@@ -69,6 +67,7 @@
         )
     }
 
+@ExperimentalCoroutinesApi
 private val Kosmos.alternateBouncerUdfpsIconViewModel by
     Kosmos.Fixture {
         AlternateBouncerUdfpsIconViewModel(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
index bdd4afa..f1d87fe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt
@@ -18,6 +18,9 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +31,8 @@
     AlternateBouncerViewModel(
         statusBarKeyguardViewManager = statusBarKeyguardViewManager,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
+        dismissCallbackRegistry = dismissCallbackRegistry,
+        alternateBouncerInteractor = { alternateBouncerInteractor },
+        primaryBouncerInteractor = primaryBouncerInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
index 450dcc2..d06bab2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelKosmos.kt
@@ -19,13 +19,11 @@
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @ExperimentalCoroutinesApi
 val Kosmos.dreamingToLockscreenTransitionViewModel by Fixture {
     DreamingToLockscreenTransitionViewModel(
-        fromDreamingTransitionInteractor = mock(),
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index b9443bc..7cf4083 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -25,6 +25,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.shade.ui.viewmodel.notificationShadeWindowModel
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
 import com.android.systemui.statusbar.phone.dozeParameters
 import com.android.systemui.statusbar.phone.screenOffAnimationController
@@ -39,6 +40,7 @@
         communalInteractor = communalInteractor,
         keyguardTransitionInteractor = keyguardTransitionInteractor,
         notificationsKeyguardInteractor = notificationsKeyguardInteractor,
+        notificationShadeWindowModel = notificationShadeWindowModel,
         alternateBouncerToAodTransitionViewModel = alternateBouncerToAodTransitionViewModel,
         alternateBouncerToGoneTransitionViewModel = alternateBouncerToGoneTransitionViewModel,
         alternateBouncerToLockscreenTransitionViewModel =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 19b32bc..f47b2df 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import com.android.systemui.biometrics.authController
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -34,5 +35,6 @@
             shadeInteractor = shadeInteractor,
             unfoldTransitionInteractor = unfoldTransitionInteractor,
             occlusionInteractor = sceneContainerOcclusionInteractor,
+            deviceEntryInteractor = deviceEntryInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..a25b29fd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelKosmos.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.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.lockscreenUserActionsViewModel by Fixture {
+    LockscreenUserActionsViewModel(
+        deviceEntryInteractor = deviceEntryInteractor,
+        communalInteractor = communalInteractor,
+        shadeInteractor = shadeInteractor,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
index a05e606..4196e54 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
@@ -25,6 +26,7 @@
 
 val Kosmos.occludedToDozingTransitionViewModel by Fixture {
     OccludedToDozingTransitionViewModel(
+        deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
         animationFlow = keyguardTransitionAnimationFlow,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index b34681a..f8df707 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -5,9 +5,12 @@
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 
 var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
+var Kosmos.unconfinedTestDispatcher by Fixture { UnconfinedTestDispatcher() }
 var Kosmos.testScope by Fixture { TestScope(testDispatcher) }
+var Kosmos.unconfinedTestScope by Fixture { TestScope(unconfinedTestDispatcher) }
 var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope }
 var Kosmos.testCase: SysuiTestCase by Fixture()
 var Kosmos.backgroundCoroutineContext: CoroutineContext by Fixture {
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 e6bd24b..851a378 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
@@ -36,12 +36,14 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
+import com.android.systemui.haptics.msdl.msdlPlayer
 import com.android.systemui.haptics.qs.qsLongPressEffect
 import com.android.systemui.jank.interactionJankMonitor
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.fromOccludedTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -50,6 +52,7 @@
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.domain.startable.scrimStartable
@@ -115,6 +118,7 @@
     val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
     val fakeSceneContainerConfig by lazy { kosmos.sceneContainerConfig }
     val sceneInteractor by lazy { kosmos.sceneInteractor }
+    val sceneBackInteractor by lazy { kosmos.sceneBackInteractor }
     val falsingCollector by lazy { kosmos.falsingCollector }
     val powerInteractor by lazy { kosmos.powerInteractor }
     val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
@@ -126,6 +130,7 @@
     val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor }
     val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository }
     val fromLockscreenTransitionInteractor by lazy { kosmos.fromLockscreenTransitionInteractor }
+    val fromOccludedTransitionInteractor by lazy { kosmos.fromOccludedTransitionInteractor }
     val fromPrimaryBouncerTransitionInteractor by lazy {
         kosmos.fromPrimaryBouncerTransitionInteractor
     }
@@ -150,4 +155,5 @@
     val scrimController by lazy { kosmos.scrimController }
     val scrimStartable by lazy { kosmos.scrimStartable }
     val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
+    val msdlPlayer by lazy { kosmos.msdlPlayer }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
index e8b2dd2..e66a2be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeActivatable.kt
@@ -21,11 +21,11 @@
 class FakeActivatable(
     private val onActivation: () -> Unit = {},
     private val onDeactivation: () -> Unit = {},
-) : SafeActivatable() {
+) : ExclusiveActivatable() {
     var activationCount = 0
     var cancellationCount = 0
 
-    override suspend fun onActivated() {
+    override suspend fun onActivated(): Nothing {
         activationCount++
         onActivation()
         try {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt
index 9a56f24..2eb7ce6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/FakeSysUiViewModel.kt
@@ -16,20 +16,41 @@
 
 package com.android.systemui.lifecycle
 
-import kotlinx.coroutines.awaitCancellation
+import androidx.compose.runtime.getValue
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flowOf
 
 class FakeSysUiViewModel(
     private val onActivation: () -> Unit = {},
     private val onDeactivation: () -> Unit = {},
-) : SysUiViewModel() {
+    upstreamFlow: Flow<Boolean> = flowOf(true),
+    upstreamStateFlow: StateFlow<Boolean> = MutableStateFlow(true).asStateFlow(),
+) : ExclusiveActivatable() {
+
     var activationCount = 0
     var cancellationCount = 0
 
-    override suspend fun onActivated() {
+    private val hydrator = Hydrator("test")
+    val stateBackedByFlow: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "test",
+            initialValue = true,
+            source = upstreamFlow,
+        )
+    val stateBackedByStateFlow: Boolean by
+        hydrator.hydratedStateOf(
+            traceName = "test",
+            source = upstreamStateFlow,
+        )
+
+    override suspend fun onActivated(): Nothing {
         activationCount++
         onActivation()
         try {
-            awaitCancellation()
+            hydrator.activate()
         } finally {
             cancellationCount++
             onDeactivation()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.kt
new file mode 100644
index 0000000..c55dc6a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferFactoryKosmos.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.log.table
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.LogcatEchoTrackerAlways
+import com.android.systemui.util.time.fakeSystemClock
+
+val Kosmos.tableLogBufferFactory: TableLogBufferFactory by
+    Kosmos.Fixture {
+        TableLogBufferFactory(
+            dumpManager = dumpManager,
+            systemClock = fakeSystemClock,
+            logcatEchoTracker = LogcatEchoTrackerAlways(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt
new file mode 100644
index 0000000..64c3974
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/log/table/TableLogBufferHelper.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.log.table
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.LogcatEchoTrackerAlways
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.util.time.fakeSystemClock
+
+/**
+ * Creates a [TableLogBuffer] that will echo everything to logcat, which is useful for debugging
+ * tests.
+ */
+fun logcatTableLogBuffer(kosmos: Kosmos, name: String = "EchoToLogcatTableLogBuffer") =
+    logcatTableLogBuffer(kosmos.fakeSystemClock, name)
+
+/**
+ * Creates a [TableLogBuffer] that will echo everything to logcat, which is useful for debugging
+ * tests.
+ */
+fun logcatTableLogBuffer(systemClock: SystemClock, name: String = "EchoToLogcatTableLogBuffer") =
+    TableLogBuffer(maxSize = 50, name, systemClock, logcatEchoTracker = LogcatEchoTrackerAlways())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt
new file mode 100644
index 0000000..aed320c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.controller
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.keyguardMediaController by Kosmos.Fixture { mock<KeyguardMediaController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
index 1473184..61d5f1e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterKosmos.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.media.controls.util.mediaUiEventLogger
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.notificationLockscreenUserManager
-import com.android.systemui.util.time.systemClock
+import com.android.systemui.util.time.fakeSystemClock
 import com.android.systemui.util.wakelock.WakeLockFake
 
 val Kosmos.mediaDataFilter by
@@ -42,7 +42,7 @@
                 ),
             lockscreenUserManager = notificationLockscreenUserManager,
             executor = fakeExecutor,
-            systemClock = systemClock,
+            systemClock = fakeSystemClock,
             logger = mediaUiEventLogger,
             mediaFlags = mediaFlags,
             mediaFilterRepository = mediaFilterRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.kt
new file mode 100644
index 0000000..a5690a0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataLoaderKosmos.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.media.controls.domain.pipeline
+
+import android.app.statusBarManager
+import android.content.testableContext
+import com.android.systemui.graphics.imageLoader
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
+import com.android.systemui.media.controls.util.mediaFlags
+import com.android.systemui.plugins.activityStarter
+
+val Kosmos.mediaDataLoader by
+    Kosmos.Fixture {
+        MediaDataLoader(
+            testableContext,
+            testDispatcher,
+            testScope,
+            activityStarter,
+            fakeMediaControllerFactory,
+            mediaFlags,
+            imageLoader,
+            statusBarManager
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
index cc1ad1f..632436a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorKosmos.kt
@@ -18,7 +18,6 @@
 
 import android.app.smartspace.SmartspaceManager
 import android.content.applicationContext
-import android.os.fakeExecutorHandler
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.concurrency.fakeExecutor
@@ -28,7 +27,7 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.media.controls.data.repository.mediaDataRepository
 import com.android.systemui.media.controls.shared.model.SmartspaceMediaDataProvider
-import com.android.systemui.media.controls.util.mediaControllerFactory
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
 import com.android.systemui.media.controls.util.mediaFlags
 import com.android.systemui.media.controls.util.mediaUiEventLogger
 import com.android.systemui.plugins.activityStarter
@@ -45,8 +44,8 @@
             backgroundExecutor = fakeExecutor,
             uiExecutor = fakeExecutor,
             foregroundExecutor = fakeExecutor,
-            handler = fakeExecutorHandler,
-            mediaControllerFactory = mediaControllerFactory,
+            mainDispatcher = testDispatcher,
+            mediaControllerFactory = fakeMediaControllerFactory,
             broadcastDispatcher = broadcastDispatcher,
             dumpManager = dumpManager,
             activityStarter = activityStarter,
@@ -60,5 +59,6 @@
             smartspaceManager = SmartspaceManager(applicationContext),
             keyguardUpdateMonitor = keyguardUpdateMonitor,
             mediaDataRepository = mediaDataRepository,
+            mediaDataLoader = { mediaDataLoader },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
index b98f557..c479ce6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerKosmos.kt
@@ -22,8 +22,8 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
 import com.android.systemui.media.controls.util.localMediaManagerFactory
-import com.android.systemui.media.controls.util.mediaControllerFactory
 import com.android.systemui.media.muteawait.mediaMuteAwaitConnectionManagerFactory
 import com.android.systemui.statusbar.policy.configurationController
 
@@ -31,7 +31,7 @@
     Kosmos.Fixture {
         MediaDeviceManager(
             context = applicationContext,
-            controllerFactory = mediaControllerFactory,
+            controllerFactory = fakeMediaControllerFactory,
             localMediaManagerFactory = localMediaManagerFactory,
             mr2manager = { MediaRouter2Manager.getInstance(applicationContext) },
             muteAwaitConnectionManagerFactory = mediaMuteAwaitConnectionManagerFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
index 6ec6378..b7660e0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerKosmos.kt
@@ -19,7 +19,7 @@
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.media.controls.util.mediaControllerFactory
+import com.android.systemui.media.controls.util.fakeMediaControllerFactory
 import com.android.systemui.media.controls.util.mediaFlags
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.util.time.systemClock
@@ -27,7 +27,7 @@
 val Kosmos.mediaTimeoutListener by
     Kosmos.Fixture {
         MediaTimeoutListener(
-            mediaControllerFactory = mediaControllerFactory,
+            mediaControllerFactory = fakeMediaControllerFactory,
             mainExecutor = fakeExecutor,
             logger = MediaTimeoutLogger(logcatLogBuffer("MediaTimeoutLogBuffer")),
             statusBarStateController = statusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.kt
new file mode 100644
index 0000000..7f8348e
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/FakeMediaControllerFactory.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.media.controls.util
+
+import android.content.Context
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.MediaSession.Token
+
+class FakeMediaControllerFactory(context: Context) : MediaControllerFactory(context) {
+
+    private val mediaControllersForToken = mutableMapOf<Token, MediaController>()
+
+    override fun create(token: MediaSession.Token): android.media.session.MediaController {
+        if (token !in mediaControllersForToken) {
+            super.create(token)
+        }
+        return mediaControllersForToken[token]!!
+    }
+
+    fun setControllerForToken(token: Token, mediaController: MediaController) {
+        mediaControllersForToken[token] = mediaController
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaControllerFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaControllerFactoryKosmos.kt
index 1ce6e82..7ee58fa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaControllerFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/util/MediaControllerFactoryKosmos.kt
@@ -19,4 +19,5 @@
 import android.content.applicationContext
 import com.android.systemui.kosmos.Kosmos
 
-val Kosmos.mediaControllerFactory by Kosmos.Fixture { MediaControllerFactory(applicationContext) }
+val Kosmos.fakeMediaControllerFactory by
+    Kosmos.Fixture { FakeMediaControllerFactory(applicationContext) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/gestural/data/GestureRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/gestural/data/GestureRepositoryKosmos.kt
new file mode 100644
index 0000000..55ce43a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/navigationbar/gestural/data/GestureRepositoryKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.navigationbar.gestural.data
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepository
+import com.android.systemui.navigationbar.gestural.data.respository.GestureRepositoryImpl
+
+val Kosmos.gestureRepository: GestureRepository by
+    Kosmos.Fixture { GestureRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt
new file mode 100644
index 0000000..3d125e9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/DarkIconDispatcherKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins
+
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.fakeDarkIconDispatcher: FakeDarkIconDispatcher by
+    Kosmos.Fixture { FakeDarkIconDispatcher() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt
new file mode 100644
index 0000000..102a853
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeDarkIconDispatcher.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.plugins
+
+import android.graphics.Rect
+import java.util.ArrayList
+
+class FakeDarkIconDispatcher : DarkIconDispatcher {
+    val receivers = mutableListOf<DarkIconDispatcher.DarkReceiver>()
+
+    override fun setIconsDarkArea(r: ArrayList<Rect>) {}
+
+    override fun addDarkReceiver(receiver: DarkIconDispatcher.DarkReceiver) {
+        receivers.add(receiver)
+    }
+
+    override fun removeDarkReceiver(receiver: DarkIconDispatcher.DarkReceiver) {
+        receivers.remove(receiver)
+    }
+
+    override fun applyDark(`object`: DarkIconDispatcher.DarkReceiver) {}
+}
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 f9f8d23..2deeb25 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
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneBackInteractor
 import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -41,5 +42,6 @@
             { sceneInteractor },
             { sceneContainerOcclusionInteractor },
             { keyguardClockInteractor },
+            { sceneBackInteractor },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
index 9ff7dd5..ffe6918 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
@@ -27,6 +27,9 @@
     numRunningPackages: Int = 0,
 ) : FgsManagerController {
 
+    var initialized = false
+        private set
+
     override var numRunningPackages = numRunningPackages
         set(value) {
             if (value != field) {
@@ -53,7 +56,9 @@
         dialogDismissedListeners.forEach { it.onDialogDismissed() }
     }
 
-    override fun init() {}
+    override fun init() {
+        initialized = true
+    }
 
     override fun showDialog(expandable: Expandable?) {}
 
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
new file mode 100644
index 0000000..d37d8f3
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModelKosmos.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.composefragment.viewmodel
+
+import android.content.res.mainResources
+import androidx.lifecycle.LifecycleCoroutineScope
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.footerActionsController
+import com.android.systemui.qs.footerActionsViewModelFactory
+import com.android.systemui.qs.ui.viewmodel.quickSettingsContainerViewModel
+import com.android.systemui.shade.largeScreenHeaderHelper
+import com.android.systemui.shade.transition.largeScreenShadeInterpolator
+import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.phone.keyguardBypassController
+import com.android.systemui.statusbar.sysuiStatusBarStateController
+
+val Kosmos.qsFragmentComposeViewModelFactory by
+    Kosmos.Fixture {
+        object : QSFragmentComposeViewModel.Factory {
+            override fun create(
+                lifecycleScope: LifecycleCoroutineScope
+            ): QSFragmentComposeViewModel {
+                return QSFragmentComposeViewModel(
+                    quickSettingsContainerViewModel,
+                    mainResources,
+                    footerActionsViewModelFactory,
+                    footerActionsController,
+                    sysuiStatusBarStateController,
+                    keyguardBypassController,
+                    disableFlagsRepository,
+                    largeScreenShadeInterpolator,
+                    configurationInteractor,
+                    largeScreenHeaderHelper,
+                    lifecycleScope,
+                )
+            }
+        }
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
index 76dccdb..0c62d0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
 
 val Kosmos.iconTilesInteractor by
     Kosmos.Fixture {
         IconTilesInteractor(
             defaultLargeTilesRepository,
+            currentTilesInteractor,
             qsPreferencesInteractor,
             FakeLogBuffer.Factory.create(),
             applicationCoroutineScope
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
index 9cb76bb..3943d1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
@@ -28,6 +28,12 @@
         expandable: Expandable? = null,
     ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.Click(expandable), data)
 
+    fun <T> toggleClick(
+        data: T,
+        user: UserHandle = UserHandle.CURRENT,
+        expandable: Expandable? = null,
+    ): QSTileInput<T> = QSTileInput(user, QSTileUserAction.ToggleClick(expandable), data)
+
     fun <T> longClick(
         data: T,
         user: UserHandle = UserHandle.CURRENT,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt
new file mode 100644
index 0000000..73b1859
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/airplane/AirplaneModeTileKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.qs.tiles.impl.airplane
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.statusbar.connectivity.ConnectivityModule
+
+val Kosmos.qsAirplaneModeTileConfig by
+    Kosmos.Fixture { ConnectivityModule.provideAirplaneModeTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
index fa8d363..5ac7c39 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakePackageManagerAdapterFacade.kt
@@ -18,14 +18,19 @@
 
 import android.content.ComponentName
 import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
 import android.content.pm.ServiceInfo
 import android.os.Bundle
+import android.os.UserHandle
 import com.android.systemui.qs.external.PackageManagerAdapter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
+import org.hamcrest.BaseMatcher
+import org.hamcrest.Description
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.hamcrest.MockitoHamcrest.intThat
 
 /**
  * Facade for [PackageManagerAdapter] to provide a fake-like behaviour. You can create this class
@@ -36,29 +41,26 @@
  * [com.android.systemui.qs.external.TileServiceManager.isToggleableTile] or
  * [com.android.systemui.qs.external.TileServiceManager.isActiveTile] when the real objects are
  * used.
+ *
+ * The user this is set up must be a real user (`user >= 0`) or [UserHandle.USER_ALL].
  */
 class FakePackageManagerAdapterFacade(
     val componentName: ComponentName,
     val packageManagerAdapter: PackageManagerAdapter = mock {},
+    user: Int = UserHandle.USER_ALL,
 ) {
 
     private var isToggleable: Boolean = false
     private var isActive: Boolean = false
 
     init {
-        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
-            createServiceInfo()
+        if (user == UserHandle.USER_ALL) {
+            setForAllUsers()
+        } else if (user >= 0) {
+            setExclusiveForUser(user)
+        } else {
+            throw IllegalArgumentException("User must be a real user or UserHandle.USER_ALL")
         }
-        whenever(
-                packageManagerAdapter.getPackageInfoAsUser(
-                    eq(componentName.packageName),
-                    anyInt(),
-                    anyInt()
-                )
-            )
-            .thenAnswer { PackageInfo().apply { packageName = componentName.packageName } }
-        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), anyInt(), anyInt()))
-            .thenAnswer { createServiceInfo() }
     }
 
     private fun createServiceInfo(): ServiceInfo {
@@ -84,4 +86,67 @@
     fun setIsToggleable(isToggleable: Boolean) {
         this.isToggleable = isToggleable
     }
+
+    fun setExclusiveForUser(newUser: Int) {
+        check(newUser >= 0)
+        val notEqualMatcher = NotEqualMatcher(newUser)
+        if (newUser == 0) {
+            whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
+                createServiceInfo()
+            }
+        }
+        whenever(
+                packageManagerAdapter.getPackageInfoAsUser(
+                    eq(componentName.packageName),
+                    anyInt(),
+                    eq(newUser)
+                )
+            )
+            .thenAnswer { PackageInfo().apply { packageName = componentName.packageName } }
+        whenever(
+                packageManagerAdapter.getPackageInfoAsUser(
+                    eq(componentName.packageName),
+                    anyInt(),
+                    intThat(notEqualMatcher),
+                )
+            )
+            .thenThrow(PackageManager.NameNotFoundException())
+
+        whenever(
+                packageManagerAdapter.getServiceInfo(
+                    eq(componentName),
+                    anyInt(),
+                    intThat(notEqualMatcher)
+                )
+            )
+            .thenAnswer { null }
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), anyInt(), eq(newUser)))
+            .thenAnswer { createServiceInfo() }
+    }
+
+    fun setForAllUsers() {
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), any())).thenAnswer {
+            createServiceInfo()
+        }
+        whenever(
+                packageManagerAdapter.getPackageInfoAsUser(
+                    eq(componentName.packageName),
+                    anyInt(),
+                    anyInt()
+                )
+            )
+            .thenAnswer { PackageInfo().apply { packageName = componentName.packageName } }
+        whenever(packageManagerAdapter.getServiceInfo(eq(componentName), anyInt(), anyInt()))
+            .thenAnswer { createServiceInfo() }
+    }
+}
+
+private class NotEqualMatcher(private val notEqualValue: Int) : BaseMatcher<Int>() {
+    override fun describeTo(description: Description?) {
+        description?.appendText("!= $notEqualValue")
+    }
+
+    override fun matches(item: Any?): Boolean {
+        return (item as? Int)?.equals(notEqualValue)?.not() ?: true
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index b6194e3..bbe753e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -27,7 +27,9 @@
 class FakeQSSceneAdapter(
     private val inflateDelegate: suspend (Context) -> View,
     override val qqsHeight: Int = 0,
+    override val squishedQqsHeight: Int = 0,
     override val qsHeight: Int = 0,
+    override val squishedQsHeight: Int = 0,
 ) : QSSceneAdapter {
     private val _customizerState = MutableStateFlow<CustomizerState>(CustomizerState.Hidden)
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
new file mode 100644
index 0000000..8fc40e4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.qs.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.quickSettingsShadeOverlayActionsViewModel:
+    QuickSettingsShadeOverlayActionsViewModel by Fixture {
+    QuickSettingsShadeOverlayActionsViewModel()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.kt
new file mode 100644
index 0000000..ff8b478
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelKosmos.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.qs.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
+
+val Kosmos.quickSettingsShadeOverlayContentViewModel: QuickSettingsShadeOverlayContentViewModel by
+    Kosmos.Fixture {
+        QuickSettingsShadeOverlayContentViewModel(
+            sceneInteractor = sceneInteractor,
+            shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
+            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
deleted file mode 100644
index 5d70ed6..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneActionsViewModelKosmos.kt
+++ /dev/null
@@ -1,28 +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.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.quickSettingsShadeSceneActionsViewModel: QuickSettingsShadeSceneActionsViewModel by
-    Kosmos.Fixture {
-        QuickSettingsShadeSceneActionsViewModel(
-            shadeInteractor = shadeInteractor,
-            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
index 5ad5cb2..cd1704c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneContentViewModelKosmos.kt
@@ -14,18 +14,13 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.qs.ui.viewmodel
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.ui.viewmodel.overlayShadeViewModelFactory
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 val Kosmos.quickSettingsShadeSceneContentViewModel: QuickSettingsShadeSceneContentViewModel by
     Kosmos.Fixture {
         QuickSettingsShadeSceneContentViewModel(
-            overlayShadeViewModelFactory = overlayShadeViewModelFactory,
             quickSettingsContainerViewModel = quickSettingsContainerViewModel,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..06592b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeUserActionsViewModelKosmos.kt
@@ -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 com.android.systemui.qs.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.quickSettingsShadeUserActionsViewModel: QuickSettingsShadeUserActionsViewModel by
+    Kosmos.Fixture {
+        QuickSettingsShadeUserActionsViewModel(
+            quickSettingsContainerViewModel = quickSettingsContainerViewModel,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index dd93141..55f3ed7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -1,10 +1,18 @@
 package com.android.systemui.scene
 
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.classifier.domain.interactor.falsingInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.scene.shared.model.FakeScene
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.logger.sceneLogger
+import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.FakeOverlay
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
 
 var Kosmos.sceneKeys by Fixture {
     listOf(
@@ -17,11 +25,21 @@
     )
 }
 
-val Kosmos.fakeScenes by Fixture { sceneKeys.map { key -> FakeScene(key) }.toSet() }
-
-val Kosmos.scenes by Fixture { fakeScenes }
-
 val Kosmos.initialSceneKey by Fixture { Scenes.Lockscreen }
+
+var Kosmos.overlayKeys by Fixture {
+    listOf(
+        Overlays.NotificationsShade,
+        Overlays.QuickSettingsShade,
+    )
+}
+
+val Kosmos.fakeOverlaysByKeys by Fixture { overlayKeys.associateWith { FakeOverlay(it) } }
+
+val Kosmos.fakeOverlays by Fixture { fakeOverlaysByKeys.values.toSet() }
+
+val Kosmos.overlays by Fixture { fakeOverlays }
+
 var Kosmos.sceneContainerConfig by Fixture {
     val navigationDistances =
         mapOf(
@@ -32,5 +50,28 @@
             Scenes.QuickSettings to 3,
             Scenes.Bouncer to 4,
         )
-    SceneContainerConfig(sceneKeys, initialSceneKey, navigationDistances)
+
+    SceneContainerConfig(
+        sceneKeys = sceneKeys,
+        initialSceneKey = initialSceneKey,
+        overlayKeys = overlayKeys,
+        navigationDistances = navigationDistances,
+    )
+}
+
+val Kosmos.transitionState by Fixture {
+    MutableStateFlow<ObservableTransitionState>(
+        ObservableTransitionState.Idle(sceneContainerConfig.initialSceneKey)
+    )
+}
+
+val Kosmos.sceneContainerViewModel by Fixture {
+    SceneContainerViewModel(
+            sceneInteractor = sceneInteractor,
+            falsingInteractor = falsingInteractor,
+            powerInteractor = powerInteractor,
+            motionEventHandlerReceiver = {},
+            logger = sceneLogger
+        )
+        .apply { setTransitionState(transitionState) }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
index 2f17ca8..c95b2dc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
@@ -19,6 +19,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
@@ -28,7 +29,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
 
 private val mutableTransitionState =
     MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(Scenes.Lockscreen))
@@ -36,20 +36,26 @@
 suspend fun Kosmos.setTransition(
     sceneTransition: ObservableTransitionState,
     stateTransition: TransitionStep? = null,
+    fillInStateSteps: Boolean = true,
     scope: TestScope = testScope,
     repository: SceneContainerRepository = sceneContainerRepository
 ) {
+    var state: TransitionStep? = stateTransition
     if (SceneContainerFlag.isEnabled) {
         setSceneTransition(sceneTransition, scope, repository)
-    } else {
-        if (stateTransition == null) throw IllegalArgumentException("No transitionStep provided")
-        fakeKeyguardTransitionRepository.sendTransitionSteps(
-            from = stateTransition.from,
-            to = stateTransition.to,
-            testScope = scope,
-            throughTransitionState = stateTransition.transitionState
-        )
+
+        if (state != null) {
+            state = getStateWithUndefined(sceneTransition, state)
+        }
     }
+
+    if (state == null) return
+    fakeKeyguardTransitionRepository.sendTransitionSteps(
+        step = state,
+        testScope = scope,
+        fillInSteps = fillInStateSteps,
+    )
+    scope.testScheduler.runCurrent()
 }
 
 fun Kosmos.setSceneTransition(
@@ -59,7 +65,7 @@
 ) {
     repository.setTransitionState(mutableTransitionState)
     mutableTransitionState.value = transition
-    scope.runCurrent()
+    scope.testScheduler.runCurrent()
 }
 
 fun Transition(
@@ -69,6 +75,8 @@
     progress: Flow<Float> = flowOf(0f),
     isInitiatedByUserInput: Boolean = false,
     isUserInputOngoing: Flow<Boolean> = flowOf(false),
+    previewProgress: Flow<Float> = flowOf(0f),
+    isInPreviewStage: Flow<Boolean> = flowOf(false)
 ): ObservableTransitionState.Transition {
     return ObservableTransitionState.Transition(
         fromScene = from,
@@ -76,10 +84,52 @@
         currentScene = currentScene,
         progress = progress,
         isInitiatedByUserInput = isInitiatedByUserInput,
-        isUserInputOngoing = isUserInputOngoing
+        isUserInputOngoing = isUserInputOngoing,
+        previewProgress = previewProgress,
+        isInPreviewStage = isInPreviewStage
     )
 }
 
 fun Idle(currentScene: SceneKey): ObservableTransitionState.Idle {
     return ObservableTransitionState.Idle(currentScene)
 }
+
+private fun getStateWithUndefined(
+    sceneTransition: ObservableTransitionState,
+    state: TransitionStep
+): TransitionStep {
+    return when (sceneTransition) {
+        is ObservableTransitionState.Idle -> {
+            TransitionStep(
+                from = state.from,
+                to =
+                    if (sceneTransition.currentScene != Scenes.Lockscreen) {
+                        KeyguardState.UNDEFINED
+                    } else {
+                        state.to
+                    },
+                value = state.value,
+                transitionState = state.transitionState
+            )
+        }
+        is ObservableTransitionState.Transition -> {
+            TransitionStep(
+                from =
+                    if (sceneTransition.fromContent != Scenes.Lockscreen) {
+                        KeyguardState.UNDEFINED
+                    } else {
+                        state.from
+                    },
+                to =
+                    if (sceneTransition.toContent != Scenes.Lockscreen) {
+                        KeyguardState.UNDEFINED
+                    } else {
+                        state.from
+                    },
+                value = state.value,
+                transitionState = state.transitionState
+            )
+        }
+        else -> state
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
index ae8b411..f84c3bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.scene.domain.resolver.sceneFamilyResolvers
 import com.android.systemui.scene.shared.logger.sceneLogger
 
-val Kosmos.sceneInteractor by
+val Kosmos.sceneInteractor: SceneInteractor by
     Kosmos.Fixture {
         SceneInteractor(
             applicationScope = applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 8e76a0b..b612a8b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -18,13 +18,16 @@
 
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.bouncerInteractor
 import com.android.systemui.bouncer.domain.interactor.simBouncerInteractor
 import com.android.systemui.classifier.falsingCollector
 import com.android.systemui.classifier.falsingManager
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.haptics.vibratorHelper
 import com.android.systemui.keyguard.dismissCallbackRegistry
 import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -54,6 +57,7 @@
         applicationScope = testScope.backgroundScope,
         sceneInteractor = sceneInteractor,
         deviceEntryInteractor = deviceEntryInteractor,
+        deviceEntryHapticsInteractor = deviceEntryHapticsInteractor,
         deviceUnlockedInteractor = deviceUnlockedInteractor,
         bouncerInteractor = bouncerInteractor,
         keyguardInteractor = keyguardInteractor,
@@ -80,5 +84,7 @@
         keyguardEnabledInteractor = keyguardEnabledInteractor,
         dismissCallbackRegistry = dismissCallbackRegistry,
         statusBarStateController = sysuiStatusBarStateController,
+        alternateBouncerInteractor = alternateBouncerInteractor,
+        vibratorHelper = vibratorHelper,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
deleted file mode 100644
index 64e3526..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeScene.kt
+++ /dev/null
@@ -1,43 +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.scene.shared.model
-
-import com.android.compose.animation.scene.SceneKey
-import com.android.compose.animation.scene.UserAction
-import com.android.compose.animation.scene.UserActionResult
-import kotlinx.coroutines.channels.Channel
-import kotlinx.coroutines.flow.onCompletion
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.receiveAsFlow
-
-class FakeScene(
-    override val key: SceneKey,
-) : Scene {
-    var isDestinationScenesBeingCollected = false
-
-    private val destinationScenesChannel = Channel<Map<UserAction, UserActionResult>>()
-
-    override val destinationScenes =
-        destinationScenesChannel
-            .receiveAsFlow()
-            .onStart { isDestinationScenesBeingCollected = true }
-            .onCompletion { isDestinationScenesBeingCollected = false }
-
-    suspend fun setDestinationScenes(value: Map<UserAction, UserActionResult>) {
-        destinationScenesChannel.send(value)
-    }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
index 957a60f..f52572a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/FakeSceneDataSource.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.scene.shared.model
 
+import com.android.compose.animation.scene.OverlayKey
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -29,11 +30,18 @@
     private val _currentScene = MutableStateFlow(initialSceneKey)
     override val currentScene: StateFlow<SceneKey> = _currentScene.asStateFlow()
 
+    private val _currentOverlays = MutableStateFlow<Set<OverlayKey>>(emptySet())
+    override val currentOverlays: StateFlow<Set<OverlayKey>> = _currentOverlays.asStateFlow()
+
     var isPaused = false
         private set
+
     var pendingScene: SceneKey? = null
         private set
 
+    var pendingOverlays: Set<OverlayKey>? = null
+        private set
+
     override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
         if (isPaused) {
             pendingScene = toScene
@@ -46,10 +54,32 @@
         changeScene(toScene)
     }
 
+    override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        if (isPaused) {
+            pendingOverlays = (pendingOverlays ?: currentOverlays.value) + overlay
+        } else {
+            _currentOverlays.value += overlay
+        }
+    }
+
+    override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) {
+        if (isPaused) {
+            pendingOverlays = (pendingOverlays ?: currentOverlays.value) - overlay
+        } else {
+            _currentOverlays.value -= overlay
+        }
+    }
+
+    override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) {
+        hideOverlay(from, transitionKey)
+        showOverlay(to, transitionKey)
+    }
+
     /**
-     * Pauses scene changes.
+     * Pauses scene and overlay changes.
      *
-     * Any following calls to [changeScene] will be conflated and the last one will be remembered.
+     * Any following calls to [changeScene] or overlay changing functions will be conflated and the
+     * last one will be remembered.
      */
     fun pause() {
         check(!isPaused) { "Can't pause what's already paused!" }
@@ -58,11 +88,14 @@
     }
 
     /**
-     * Unpauses scene changes.
+     * Unpauses scene and overlay changes.
      *
      * If there were any calls to [changeScene] since [pause] was called, the latest of the bunch
      * will be replayed.
      *
+     * If there were any calls to show, hide or replace overlays since [pause] was called, they will
+     * all be applied at once.
+     *
      * If [force] is `true`, there will be no check that [isPaused] is true.
      *
      * If [expectedScene] is provided, will assert that it's indeed the latest called.
@@ -76,6 +109,8 @@
         isPaused = false
         pendingScene?.let { _currentScene.value = it }
         pendingScene = null
+        pendingOverlays?.let { _currentOverlays.value = it }
+        pendingOverlays = null
 
         check(expectedScene == null || currentScene.value == expectedScene) {
             """
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt
new file mode 100644
index 0000000..f4f30cd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/FakeOverlay.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.scene.ui
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.compose.animation.scene.ContentScope
+import com.android.compose.animation.scene.OverlayKey
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.scene.ui.composable.Overlay
+import kotlinx.coroutines.awaitCancellation
+
+class FakeOverlay(
+    override val key: OverlayKey,
+) : ExclusiveActivatable(), Overlay {
+
+    @Composable override fun ContentScope.Content(modifier: Modifier) = Unit
+
+    override suspend fun onActivated(): Nothing {
+        awaitCancellation()
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 4660337..4a86fd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -72,10 +72,6 @@
     @Deprecated("Use ShadeInteractor.isUserInteractingWithShade instead")
     override val legacyLockscreenShadeTracking = MutableStateFlow(false)
 
-    private var _isDualShadeAlignedToBottom = false
-    override val isDualShadeAlignedToBottom
-        get() = _isDualShadeAlignedToBottom
-
     private var _isShadeLayoutWide = MutableStateFlow(false)
     override val isShadeLayoutWide: StateFlow<Boolean> = _isShadeLayoutWide.asStateFlow()
 
@@ -155,10 +151,6 @@
         _legacyShadeExpansion.value = expandedFraction
     }
 
-    fun setDualShadeAlignedToBottom(isAlignedToBottom: Boolean) {
-        _isDualShadeAlignedToBottom = isAlignedToBottom
-    }
-
     override fun setShadeLayoutWide(isShadeLayoutWide: Boolean) {
         _isShadeLayoutWide.value = isShadeLayoutWide
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
new file mode 100644
index 0000000..1e00ac4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayActionsViewModelKosmos.kt
@@ -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 com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayActionsViewModel
+
+val Kosmos.notificationsShadeOverlayActionsViewModel:
+    NotificationsShadeOverlayActionsViewModel by Fixture {
+    NotificationsShadeOverlayActionsViewModel()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.kt
new file mode 100644
index 0000000..9cdd519
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeOverlayContentViewModelKosmos.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.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeOverlayContentViewModel
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
+
+val Kosmos.notificationsShadeOverlayContentViewModel:
+    NotificationsShadeOverlayContentViewModel by Fixture {
+    NotificationsShadeOverlayContentViewModel(
+        shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
+        notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
+        sceneInteractor = sceneInteractor,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
deleted file mode 100644
index 9bf4756..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneActionsViewModelKosmos.kt
+++ /dev/null
@@ -1,29 +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.shade.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneActionsViewModel
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.notificationsShadeSceneActionsViewModel:
-    NotificationsShadeSceneActionsViewModel by Fixture {
-    NotificationsShadeSceneActionsViewModel(
-        shadeInteractor = shadeInteractor,
-    )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt
deleted file mode 100644
index 9240102..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeSceneContentViewModelKosmos.kt
+++ /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.
- */
-
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
-package com.android.systemui.shade.ui.viewmodel
-
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneContentViewModel
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-val Kosmos.notificationsShadeSceneContentViewModel:
-    NotificationsShadeSceneContentViewModel by Fixture {
-    NotificationsShadeSceneContentViewModel(
-        deviceEntryInteractor = deviceEntryInteractor,
-        sceneInteractor = sceneInteractor,
-    )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..6345c40
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeUserActionsViewModelKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeUserActionsViewModel
+
+val Kosmos.notificationsShadeUserActionsViewModel:
+    NotificationsShadeUserActionsViewModel by Fixture { NotificationsShadeUserActionsViewModel() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
index 6252d44..9742595 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/NotificationsShadeWindowModelKosmos.kt
@@ -16,14 +16,18 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
-import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 
 val Kosmos.notificationShadeWindowModel: NotificationShadeWindowModel by
     Kosmos.Fixture {
         NotificationShadeWindowModel(
             keyguardTransitionInteractor,
-            keyguardInteractor,
+            sceneInteractor = { sceneInteractor },
+            authenticationInteractor = { authenticationInteractor },
+            primaryBouncerInteractor = primaryBouncerInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
deleted file mode 100644
index 00f1526..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/OverlayShadeViewModelKosmos.kt
+++ /dev/null
@@ -1,38 +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.shade.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.overlayShadeViewModel: OverlayShadeViewModel by
-    Kosmos.Fixture {
-        OverlayShadeViewModel(
-            sceneInteractor = sceneInteractor,
-            shadeInteractor = shadeInteractor,
-        )
-    }
-
-val Kosmos.overlayShadeViewModelFactory: OverlayShadeViewModel.Factory by
-    Kosmos.Fixture {
-        object : OverlayShadeViewModel.Factory {
-            override fun create(): OverlayShadeViewModel {
-                return overlayShadeViewModel
-            }
-        }
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt
deleted file mode 100644
index 2387aa8..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneActionsViewModelKosmos.kt
+++ /dev/null
@@ -1,29 +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.shade.ui.viewmodel
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.qs.ui.adapter.qsSceneAdapter
-import com.android.systemui.shade.domain.interactor.shadeInteractor
-
-val Kosmos.shadeSceneActionsViewModel: ShadeSceneActionsViewModel by Fixture {
-    ShadeSceneActionsViewModel(
-        qsSceneAdapter = qsSceneAdapter,
-        shadeInteractor = shadeInteractor,
-    )
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt
new file mode 100644
index 0000000..48c5121
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModelKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+
+val Kosmos.shadeUserActionsViewModel: ShadeUserActionsViewModel by Fixture {
+    ShadeUserActionsViewModel(
+        qsSceneAdapter = qsSceneAdapter,
+        shadeInteractor = shadeInteractor,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt
new file mode 100644
index 0000000..67f611a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/TaskStackChangeListenersKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.system
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.taskStackChangeListeners: TaskStackChangeListeners by
+    Kosmos.Fixture { TaskStackChangeListeners.getTestInstance() }
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/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt
new file mode 100644
index 0000000..c0d65a0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.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.chips.ron.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
+    Kosmos.Fixture {
+        DemoRonChipViewModel(
+            commandRegistry = commandRegistry,
+            packageManager = packageManager,
+            systemClock = fakeSystemClock,
+        )
+    }
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 16e288f..5382c1c 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,6 +20,7 @@
 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.screenrecord.ui.viewmodel.screenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
 import com.android.systemui.statusbar.chips.statusBarChipsLogger
@@ -32,6 +33,7 @@
             shareToAppChipViewModel = shareToAppChipViewModel,
             castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
             callChipViewModel = callChipViewModel,
+            demoRonChipViewModel = demoRonChipViewModel,
             logger = statusBarChipsLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.kt
new file mode 100644
index 0000000..14777b4
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/commandline/CommandRegistryKosmos.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.commandline
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.commandRegistry: CommandRegistry by
+    Kosmos.Fixture {
+        CommandRegistry(
+            context = applicationContext,
+            // Immediately run anything that comes in
+            mainExecutor = { command -> command.run() },
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
new file mode 100644
index 0000000..2a522ce
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.lockscreen
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.lockscreenSmartspaceController by
+    Kosmos.Fixture { mock<LockscreenSmartspaceController>() }
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 0b309b5..2eb1573 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
@@ -35,7 +35,9 @@
 import com.android.systemui.flags.FakeFeatureFlagsClassic
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.media.controls.util.MediaFeatureFlag
 import com.android.systemui.media.dialog.MediaOutputDialogManager
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shared.system.ActivityManagerWrapper
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper
@@ -46,6 +48,7 @@
 import com.android.systemui.statusbar.RankingBuilder
 import com.android.systemui.statusbar.SmartReplyController
 import com.android.systemui.statusbar.notification.ColorUpdateLogger
+import com.android.systemui.statusbar.notification.ConversationNotificationManager
 import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -62,11 +65,14 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
+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.shared.NotificationRowContentBinderRefactor
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
 import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.SmartActionInflaterImpl
 import com.android.systemui.statusbar.policy.SmartReplyConstants
@@ -80,10 +86,9 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.android.systemui.wmshell.BubblesManager
-import com.google.common.util.concurrent.MoreExecutors
-import com.google.common.util.concurrent.SettableFuture
 import java.util.Optional
 import java.util.concurrent.CountDownLatch
+import java.util.concurrent.Executor
 import java.util.concurrent.TimeUnit
 import kotlinx.coroutines.test.TestScope
 import org.junit.Assert.assertTrue
@@ -128,19 +133,19 @@
         dependency.injectMockDependency(NotificationShadeWindowController::class.java)
         dependency.injectMockDependency(MediaOutputDialogManager::class.java)
 
-        mMockLogger = Mockito.mock(ExpandableNotificationRowLogger::class.java)
-        mStatusBarStateController = Mockito.mock(StatusBarStateController::class.java)
-        mKeyguardBypassController = Mockito.mock(KeyguardBypassController::class.java)
+        mMockLogger = Mockito.mock(ExpandableNotificationRowLogger::class.java, STUB_ONLY)
+        mStatusBarStateController = Mockito.mock(StatusBarStateController::class.java, STUB_ONLY)
+        mKeyguardBypassController = Mockito.mock(KeyguardBypassController::class.java, STUB_ONLY)
         mGroupMembershipManager = GroupMembershipManagerImpl()
-        mSmartReplyController = Mockito.mock(SmartReplyController::class.java)
+        mSmartReplyController = Mockito.mock(SmartReplyController::class.java, STUB_ONLY)
 
         val dumpManager = DumpManager()
         mGroupExpansionManager = GroupExpansionManagerImpl(dumpManager, mGroupMembershipManager)
-        mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java)
+        mHeadsUpManager = Mockito.mock(HeadsUpManager::class.java, STUB_ONLY)
         mIconManager =
             IconManager(
-                Mockito.mock(CommonNotifCollection::class.java),
-                Mockito.mock(LauncherApps::class.java),
+                Mockito.mock(CommonNotifCollection::class.java, STUB_ONLY),
+                Mockito.mock(LauncherApps::class.java, STUB_ONLY),
                 IconBuilder(context),
                 mTestScope,
                 mBgCoroutineContext,
@@ -173,7 +178,7 @@
                 }
             )
         val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags)
-        val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java)
+        val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY)
         val smartReplyStateInflater =
             SmartReplyStateInflaterImpl(
                 constants = mSmartReplyConstants,
@@ -183,7 +188,8 @@
                 smartRepliesInflater =
                     SmartReplyInflaterImpl(
                         constants = mSmartReplyConstants,
-                        keyguardDismissUtil = mock(),
+                        keyguardDismissUtil =
+                            Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY),
                         remoteInputManager = remoteInputManager,
                         smartReplyController = mSmartReplyController,
                         context = context
@@ -191,7 +197,7 @@
                 smartActionsInflater =
                     SmartActionInflaterImpl(
                         constants = mSmartReplyConstants,
-                        activityStarter = mock(),
+                        activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY),
                         smartReplyController = mSmartReplyController,
                         headsUpManager = mHeadsUpManager
                     )
@@ -206,41 +212,42 @@
             }
         val conversationProcessor =
             ConversationNotificationProcessor(
-                mock(),
-                mock(),
+                Mockito.mock(LauncherApps::class.java, STUB_ONLY),
+                Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY),
             )
+
         mContentBinder =
             if (NotificationRowContentBinderRefactor.isEnabled)
                 NotificationRowContentBinderImpl(
-                    mock(),
+                    Mockito.mock(NotifRemoteViewCache::class.java, STUB_ONLY),
                     remoteInputManager,
                     conversationProcessor,
-                    mock(),
-                    mock(),
-                    mock(),
+                    Mockito.mock(RichOngoingNotificationContentExtractor::class.java, STUB_ONLY),
+                    Mockito.mock(RichOngoingNotificationViewInflater::class.java, STUB_ONLY),
+                    Mockito.mock(Executor::class.java, STUB_ONLY),
                     smartReplyStateInflater,
                     notifLayoutInflaterFactoryProvider,
-                    mock(),
-                    mock(),
+                    Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY),
+                    Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY),
                 )
             else
                 NotificationContentInflater(
-                    mock(),
+                    Mockito.mock(NotifRemoteViewCache::class.java, STUB_ONLY),
                     remoteInputManager,
                     conversationProcessor,
-                    mock(),
-                    mock(),
+                    Mockito.mock(MediaFeatureFlag::class.java, STUB_ONLY),
+                    Mockito.mock(Executor::class.java, STUB_ONLY),
                     smartReplyStateInflater,
                     notifLayoutInflaterFactoryProvider,
-                    mock(),
-                    mock(),
+                    Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY),
+                    Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY),
                 )
         mContentBinder.setInflateSynchronously(true)
         mBindStage =
             RowContentBindStage(
                 mContentBinder,
-                mock(),
-                mock(),
+                Mockito.mock(NotifInflationErrorManager::class.java, STUB_ONLY),
+                Mockito.mock(RowContentBindStageLogger::class.java, STUB_ONLY),
             )
 
         val collection = Mockito.mock(CommonNotifCollection::class.java)
@@ -248,7 +255,7 @@
         mBindPipeline =
             NotifBindPipeline(
                 collection,
-                Mockito.mock(NotifBindPipelineLogger::class.java),
+                Mockito.mock(NotifBindPipelineLogger::class.java, STUB_ONLY),
                 NotificationEntryProcessorFactoryExecutorImpl(mMainExecutor),
             )
         mBindPipeline.setStage(mBindStage)
@@ -256,9 +263,11 @@
         val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
         Mockito.verify(collection).addCollectionListener(collectionListenerCaptor.capture())
         mBindPipelineEntryListener = collectionListenerCaptor.value
-        mPeopleNotificationIdentifier = Mockito.mock(PeopleNotificationIdentifier::class.java)
+        mPeopleNotificationIdentifier =
+            Mockito.mock(PeopleNotificationIdentifier::class.java, STUB_ONLY)
         mOnUserInteractionCallback = Mockito.mock(OnUserInteractionCallback::class.java)
-        mDismissibilityProvider = Mockito.mock(NotificationDismissibilityProvider::class.java)
+        mDismissibilityProvider =
+            Mockito.mock(NotificationDismissibilityProvider::class.java, STUB_ONLY)
         val mFutureDismissalRunnable = Mockito.mock(Runnable::class.java)
         whenever(
                 mOnUserInteractionCallback.registerFutureDismissal(
@@ -309,7 +318,7 @@
             entry.ranking = rb.build()
         }
 
-        return generateRow(entry, FLAG_CONTENT_VIEW_ALL)
+        return generateRow(entry, INFLATION_FLAGS)
     }
 
     private fun generateRow(
@@ -320,7 +329,10 @@
         //  set, but we do not want to override an existing value that is needed by a specific test.
 
         val rowInflaterTask =
-            RowInflaterTask(mFakeSystemClock, Mockito.mock(RowInflaterTaskLogger::class.java))
+            RowInflaterTask(
+                mFakeSystemClock,
+                Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY)
+            )
         val row = rowInflaterTask.inflateSynchronously(context, null, entry)
 
         entry.row = row
@@ -329,7 +341,7 @@
         mBindPipeline.manageRow(entry, row)
         row.initialize(
             entry,
-            Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java),
+            Mockito.mock(RemoteInputViewSubcomponent.Factory::class.java, STUB_ONLY),
             APP_NAME,
             entry.key,
             mMockLogger,
@@ -338,23 +350,23 @@
             mGroupExpansionManager,
             mHeadsUpManager,
             mBindStage,
-            Mockito.mock(OnExpandClickListener::class.java),
-            Mockito.mock(CoordinateOnClickListener::class.java),
+            Mockito.mock(OnExpandClickListener::class.java, STUB_ONLY),
+            Mockito.mock(CoordinateOnClickListener::class.java, STUB_ONLY),
             FalsingManagerFake(),
             mStatusBarStateController,
             mPeopleNotificationIdentifier,
             mOnUserInteractionCallback,
-            Optional.of(Mockito.mock(BubblesManager::class.java)),
-            Mockito.mock(NotificationGutsManager::class.java),
+            Optional.of(Mockito.mock(BubblesManager::class.java, STUB_ONLY)),
+            Mockito.mock(NotificationGutsManager::class.java, STUB_ONLY),
             mDismissibilityProvider,
-            Mockito.mock(MetricsLogger::class.java),
-            Mockito.mock(NotificationChildrenContainerLogger::class.java),
-            Mockito.mock(ColorUpdateLogger::class.java),
+            Mockito.mock(MetricsLogger::class.java, STUB_ONLY),
+            Mockito.mock(NotificationChildrenContainerLogger::class.java, STUB_ONLY),
+            Mockito.mock(ColorUpdateLogger::class.java, STUB_ONLY),
             mSmartReplyConstants,
             mSmartReplyController,
             featureFlags,
-            Mockito.mock(IStatusBarService::class.java),
-            Mockito.mock(UiEventLogger::class.java)
+            Mockito.mock(IStatusBarService::class.java, STUB_ONLY),
+            Mockito.mock(UiEventLogger::class.java, STUB_ONLY)
         )
         row.setAboveShelfChangedListener { aboveShelf: Boolean -> }
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags)
@@ -374,12 +386,15 @@
         private const val PKG = "com.android.systemui"
         private const val UID = 1000
         private val USER_HANDLE = UserHandle.of(ActivityManager.getCurrentUser())
-
+        private val INFLATION_FLAGS =
+            FLAG_CONTENT_VIEW_CONTRACTED or FLAG_CONTENT_VIEW_EXPANDED or FLAG_CONTENT_VIEW_HEADS_UP
         private const val IS_CONVERSATION_FLAG = "test.isConversation"
 
         private val Notification.isConversationStyleNotification
             get() = extras.getBoolean(IS_CONVERSATION_FLAG, false)
 
+        private val STUB_ONLY = Mockito.withSettings().stubOnly()
+
         fun markAsConversation(builder: Notification.Builder) {
             builder.addExtras(bundleOf(IS_CONVERSATION_FLAG to true))
         }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.kt
new file mode 100644
index 0000000..7e51135
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/EnRouteViewModelKosmos.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.ui.viewmodel
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository
+import com.android.systemui.statusbar.notification.row.domain.interactor.getNotificationRowInteractor
+
+fun Kosmos.getEnRouteViewModel(repository: NotificationRowRepository) =
+    EnRouteViewModel(
+        dumpManager = dumpManager,
+        rowInteractor = getNotificationRowInteractor(repository),
+    )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
index 7e8f1a9..1fa6236 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/data/repository/HeadsUpNotificationRepositoryKosmos.kt
@@ -44,6 +44,14 @@
         // do nothing
     }
 
+    override fun unpinAll(userUnPinned: Boolean) {
+        // do nothing
+    }
+
+    override fun releaseAfterExpansion() {
+        // do nothing
+    }
+
     fun setNotifications(notifications: List<HeadsUpRowRepository>) {
         this.orderedHeadsUpRows.value = notifications.toList()
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
index dbfd9de..2772d36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
 import com.android.systemui.statusbar.notification.stack.data.repository.notificationPlaceholderRepository
 import com.android.systemui.statusbar.notification.stack.data.repository.notificationViewHeightRepository
@@ -26,6 +27,7 @@
     NotificationStackAppearanceInteractor(
         viewHeightRepository = notificationViewHeightRepository,
         placeholderRepository = notificationPlaceholderRepository,
+        sceneInteractor = sceneInteractor,
         shadeInteractor = shadeInteractor,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
index 20dc668..8bfc390 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelKosmos.kt
@@ -20,16 +20,26 @@
 import com.android.systemui.flags.featureFlagsClassic
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.domain.interactor.sceneInteractor
 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
 
 val Kosmos.notificationsPlaceholderViewModel by Fixture {
     NotificationsPlaceholderViewModel(
-        dumpManager = dumpManager,
         interactor = notificationStackAppearanceInteractor,
+        sceneInteractor = sceneInteractor,
         shadeInteractor = shadeInteractor,
         headsUpNotificationInteractor = headsUpNotificationInteractor,
         featureFlags = featureFlagsClassic,
+        dumpManager = dumpManager,
     )
 }
+
+val Kosmos.notificationsPlaceholderViewModelFactory by Fixture {
+    object : NotificationsPlaceholderViewModel.Factory {
+        override fun create(): NotificationsPlaceholderViewModel {
+            return notificationsPlaceholderViewModel
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.kt
new file mode 100644
index 0000000..ef04b9d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ManagedProfileControllerKosmos.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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.statusbar.phone
+
+import android.testing.LeakCheck
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.utils.leaks.FakeManagedProfileController
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.fakeManagedProfileController by Fixture { FakeManagedProfileController(LeakCheck()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
index 386c7c5..d76defe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/airplane/domain/interactor/AirplaneModeInteractorKosmos.kt
@@ -18,7 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 
 val Kosmos.airplaneModeInteractor: AirplaneModeInteractor by
@@ -26,6 +26,6 @@
         AirplaneModeInteractor(
             FakeAirplaneModeRepository(),
             FakeConnectivityRepository(),
-            FakeMobileConnectionsRepository(),
+            fakeMobileConnectionsRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index 8229575..de73d33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -23,18 +23,16 @@
 import com.android.settingslib.mobile.MobileMappings
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
-import com.android.systemui.util.mockito.mock
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 
 // TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
 class FakeMobileConnectionsRepository(
     mobileMappings: MobileMappingsProxy = FakeMobileMappingsProxy(),
-    val tableLogBuffer: TableLogBuffer = mock<TableLogBuffer> {},
+    val tableLogBuffer: TableLogBuffer,
 ) : MobileConnectionsRepository {
 
     val GSM_KEY = mobileMappings.toIconKey(GSM)
@@ -94,9 +92,10 @@
     private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
     override val defaultMobileIconGroup = _defaultMobileIconGroup
 
-    override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null)
+    override val isDeviceEmergencyCallCapable = MutableStateFlow(false)
 
     override val isAnySimSecure = MutableStateFlow(false)
+
     override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value
 
     private var isInEcmMode: Boolean = false
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
index 9d62ea5..cd22f1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepositoryKosmos.kt
@@ -18,10 +18,12 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.log.table.logcatTableLogBuffer
 
 val Kosmos.fakeMobileConnectionsRepository by Fixture {
-    FakeMobileConnectionsRepository(tableLogBuffer = mock())
+    FakeMobileConnectionsRepository(
+        tableLogBuffer = logcatTableLogBuffer(this, "FakeMobileConnectionsRepository"),
+    )
 }
 
 val Kosmos.mobileConnectionsRepository by
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 aef0828..61b53c9 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,14 +16,20 @@
 
 package com.android.systemui.statusbar.policy.domain.interactor
 
+import android.content.testableContext
+import com.android.settingslib.notification.modes.zenIconLoader
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.shared.notifications.data.repository.notificationSettingsRepository
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 
 val Kosmos.zenModeInteractor by Fixture {
     ZenModeInteractor(
+        context = testableContext,
         zenModeRepository = zenModeRepository,
         notificationSettingsRepository = notificationSettingsRepository,
+        bgDispatcher = testDispatcher,
+        iconLoader = zenIconLoader,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
index 99bb479..932e768 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogDelegateKosmos.kt
@@ -33,6 +33,7 @@
             dialogTransitionAnimator,
             activityStarter,
             { modesDialogViewModel },
+            modesDialogEventLogger,
             mainCoroutineContext,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt
new file mode 100644
index 0000000..24e7a87
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.statusbar.policy.ui.dialog
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+var Kosmos.modesDialogEventLogger by Kosmos.Fixture { ModesDialogEventLogger(uiEventLogger) }
+var Kosmos.mockModesDialogEventLogger by Kosmos.Fixture { mock<ModesDialogEventLogger>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
new file mode 100644
index 0000000..5146f77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/ModesDialogEventLoggerTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.ui.dialog
+
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSModesEvent
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
[email protected]
+class ModesDialogEventLoggerTest : SysuiTestCase() {
+
+    private val uiEventLogger = UiEventLoggerFake()
+    private val underTest = ModesDialogEventLogger(uiEventLogger)
+
+    @Test
+    fun testLogModeOn_manual() {
+        underTest.logModeOn(TestModeBuilder.MANUAL_DND_INACTIVE)
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_ON, "android")
+    }
+
+    @Test
+    fun testLogModeOff_manual() {
+        underTest.logModeOff(TestModeBuilder.MANUAL_DND_ACTIVE)
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_OFF, "android")
+    }
+
+    @Test
+    fun testLogModeSettings_manual() {
+        underTest.logModeSettings(TestModeBuilder.MANUAL_DND_ACTIVE)
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_DND_SETTINGS, "android")
+    }
+
+    @Test
+    fun testLogModeOn_automatic() {
+        underTest.logModeOn(TestModeBuilder().setActive(true).setPackage("pkg1").build())
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_ON, "pkg1")
+    }
+
+    @Test
+    fun testLogModeOff_automatic() {
+        underTest.logModeOff(TestModeBuilder().setActive(false).setPackage("pkg2").build())
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_OFF, "pkg2")
+    }
+
+    @Test
+    fun testLogModeSettings_automatic() {
+        underTest.logModeSettings(TestModeBuilder().setPackage("pkg3").build())
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_MODE_SETTINGS, "pkg3")
+    }
+
+    @Test
+    fun testLogOpenDurationDialog_manual() {
+        underTest.logOpenDurationDialog(TestModeBuilder.MANUAL_DND_INACTIVE)
+
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        // package not logged for duration dialog as it only applies to manual mode
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_DURATION_DIALOG, null)
+    }
+
+    @Test
+    fun testLogOpenDurationDialog_automatic_doesNotLog() {
+        underTest.logOpenDurationDialog(
+            TestModeBuilder().setActive(false).setPackage("mypkg").build()
+        )
+
+        // ignore calls to open dialog on something other than the manual rule (shouldn't happen)
+        assertThat(uiEventLogger.numLogs()).isEqualTo(0)
+    }
+
+    @Test
+    fun testLogDialogSettings() {
+        underTest.logDialogSettings()
+        assertThat(uiEventLogger.numLogs()).isEqualTo(1)
+        uiEventLogger[0].match(QSModesEvent.QS_MODES_SETTINGS, null)
+    }
+
+    private fun UiEventLoggerFake.FakeUiEvent.match(event: QSModesEvent, modePackage: String?) {
+        assertThat(eventId).isEqualTo(event.id)
+        assertThat(packageName).isEqualTo(modePackage)
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
index 00020f8..3571a73 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ui/dialog/viewmodel/ModesDialogViewModelKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
+import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
 import javax.inject.Provider
 
 val Kosmos.modesDialogViewModel: ModesDialogViewModel by
@@ -30,5 +31,6 @@
             zenModeInteractor,
             testDispatcher,
             Provider { modesDialogDelegate }.get(),
+            modesDialogEventLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/FakeTouchpadRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/FakeTouchpadRepository.kt
new file mode 100644
index 0000000..1ec6bbf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/FakeTouchpadRepository.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.touchpad.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeTouchpadRepository : TouchpadRepository {
+
+    private val _isAnyTouchpadConnected = MutableStateFlow(false)
+    override val isAnyTouchpadConnected: Flow<Boolean> = _isAnyTouchpadConnected
+
+    fun setIsAnyTouchpadConnected(connected: Boolean) {
+        _isAnyTouchpadConnected.value = connected
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryKosmos.kt
new file mode 100644
index 0000000..91e2396
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.touchpad.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.touchpadRepository by Kosmos.Fixture { FakeTouchpadRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt
new file mode 100644
index 0000000..ee9a4d2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * 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
+
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
+import com.android.systemui.touchpad.tutorial.domain.interactor.TouchpadGesturesInteractor
+
+var Kosmos.touchpadGesturesInteractor: TouchpadGesturesInteractor by
+    Kosmos.Fixture {
+        TouchpadGesturesInteractor(
+            sysUiState,
+            displayTracker,
+            testScope.backgroundScope,
+            inputDeviceTutorialLogger
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.kt
new file mode 100644
index 0000000..5776203
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/coroutines/MainDispatcherRule.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.util.coroutines
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.setMain
+import org.junit.rules.TestWatcher
+import org.junit.runner.Description
+
+/**
+ * Overrides main dispatcher to passed testDispatcher. You probably want to use it when using
+ * viewModelScope which has hardcoded main dispatcher.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+class MainDispatcherRule(val testDispatcher: TestDispatcher) : TestWatcher() {
+    override fun starting(description: Description) {
+        Dispatchers.setMain(testDispatcher)
+    }
+
+    override fun finished(description: Description) {
+        Dispatchers.resetMain()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/ReferenceTestUtils.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/leak/ReferenceTestUtils.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/ReferenceTestUtils.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/util/leak/ReferenceTestUtils.java
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 295e150..2e1ecfd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -219,7 +219,7 @@
  *
  * NOTE: this uses the KotlinArgumentCaptor to avoid the NullPointerException.
  */
-@Deprecated("Replace with mockito-kotlin", level = WARNING)
+// TODO(359670968): rewrite this to use mockito-kotlin
 inline fun <reified T : Any> withArgCaptor(block: KotlinArgumentCaptor<T>.() -> Unit): T =
     kotlinArgumentCaptor<T>().apply { block() }.value
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
index 35fa2af..73d423c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -19,5 +19,10 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
 
 val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings(testDispatcher) }
+
+val Kosmos.unconfinedDispatcherFakeGlobalSettings: FakeGlobalSettings by Fixture {
+    FakeGlobalSettings(unconfinedTestDispatcher)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
index 76ef202..e1daf9b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettingsKosmos.kt
@@ -19,8 +19,13 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.unconfinedTestDispatcher
 import com.android.systemui.settings.userTracker
 
 val Kosmos.fakeSettings: FakeSettings by Fixture {
     FakeSettings(testDispatcher) { userTracker.userId }
 }
+
+val Kosmos.unconfinedDispatcherFakeSettings: FakeSettings by Fixture {
+    FakeSettings(unconfinedTestDispatcher) { userTracker.userId }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index a8328e4..0089199 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -14,8 +14,11 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.graphics.drawable.Drawable;
 import android.testing.LeakCheck;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.phone.ui.IconManager;
@@ -60,6 +63,12 @@
     }
 
     @Override
+    public void setResourceIcon(String slot, @Nullable String resPackage, int iconResId,
+            @Nullable Drawable preloadedIcon, CharSequence contentDescription,
+            StatusBarIcon.Shape shape) {
+    }
+
+    @Override
     public void setNewWifiIcon() {
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt
deleted file mode 100644
index 0e978f2..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/CaptioningKosmos.kt
+++ /dev/null
@@ -1,23 +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.view.accessibility.data.repository
-
-import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.captioningRepository by Kosmos.Fixture { FakeCaptioningRepository() }
-val Kosmos.captioningInteractor by Kosmos.Fixture { CaptioningInteractor(captioningRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.kt
deleted file mode 100644
index 663aaf2..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/view/accessibility/data/repository/FakeCaptioningRepository.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.view.accessibility.data.repository
-
-import com.android.settingslib.view.accessibility.data.repository.CaptioningRepository
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-class FakeCaptioningRepository : CaptioningRepository {
-
-    private val mutableIsSystemAudioCaptioningEnabled = MutableStateFlow(false)
-    override val isSystemAudioCaptioningEnabled: StateFlow<Boolean>
-        get() = mutableIsSystemAudioCaptioningEnabled.asStateFlow()
-
-    private val mutableIsSystemAudioCaptioningUiEnabled = MutableStateFlow(false)
-    override val isSystemAudioCaptioningUiEnabled: StateFlow<Boolean>
-        get() = mutableIsSystemAudioCaptioningUiEnabled.asStateFlow()
-
-    override suspend fun setIsSystemAudioCaptioningEnabled(isEnabled: Boolean) {
-        mutableIsSystemAudioCaptioningEnabled.value = isEnabled
-    }
-
-    fun setIsSystemAudioCaptioningUiEnabled(isSystemAudioCaptioningUiEnabled: Boolean) {
-        mutableIsSystemAudioCaptioningUiEnabled.value = isSystemAudioCaptioningUiEnabled
-    }
-}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS
new file mode 100644
index 0000000..1f07df9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/src/com/android/systemui/volume/OWNERS
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerAdapterKosmos.kt
new file mode 100644
index 0000000..4045135b9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerAdapterKosmos.kt
@@ -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.
+ */
+
+package com.android.systemui.volume
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.data.repository.audioRepository
+
+val Kosmos.volumeControllerAdapter by
+    Kosmos.Fixture { VolumeControllerAdapter(applicationCoroutineScope, audioRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerCollectorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerCollectorKosmos.kt
deleted file mode 100644
index d60f14c..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/VolumeControllerCollectorKosmos.kt
+++ /dev/null
@@ -1,23 +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
-
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.applicationCoroutineScope
-
-val Kosmos.volumeControllerCollector by
-    Kosmos.Fixture { VolumeControllerCollector(applicationCoroutineScope) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 135cb14..888351f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -18,17 +18,27 @@
 
 import android.media.AudioDeviceInfo
 import android.media.AudioManager
+import com.android.settingslib.volume.data.model.VolumeControllerEvent
 import com.android.settingslib.volume.data.repository.AudioRepository
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.AudioStreamModel
 import com.android.settingslib.volume.shared.model.RingerMode
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.update
 
 class FakeAudioRepository : AudioRepository {
 
+    private val unMutableStreams =
+        setOf(
+            AudioManager.STREAM_VOICE_CALL,
+            AudioManager.STREAM_ALARM,
+        )
+
     private val mutableMode = MutableStateFlow(AudioManager.MODE_NORMAL)
     override val mode: StateFlow<Int> = mutableMode.asStateFlow()
 
@@ -39,10 +49,26 @@
     override val communicationDevice: StateFlow<AudioDeviceInfo?> =
         mutableCommunicationDevice.asStateFlow()
 
+    private val mutableVolumeControllerEvents = MutableSharedFlow<VolumeControllerEvent>(replay = 1)
+    override val volumeControllerEvents: Flow<VolumeControllerEvent>
+        get() = mutableVolumeControllerEvents.asSharedFlow()
+
     private val models: MutableMap<AudioStream, MutableStateFlow<AudioStreamModel>> = mutableMapOf()
     private val lastAudibleVolumes: MutableMap<AudioStream, Int> = mutableMapOf()
     private val deviceCategories: MutableMap<String, Int> = mutableMapOf()
 
+    private val mutableIsVolumeControllerVisible = MutableStateFlow(false)
+    val isVolumeControllerVisible: StateFlow<Boolean>
+        get() = mutableIsVolumeControllerVisible.asStateFlow()
+
+    private var mutableIsInitialized: Boolean = false
+    val isInitialized: Boolean
+        get() = mutableIsInitialized
+
+    override fun init() {
+        mutableIsInitialized = true
+    }
+
     private fun getAudioStreamModelState(
         audioStream: AudioStream
     ): MutableStateFlow<AudioStreamModel> =
@@ -53,7 +79,7 @@
                     volume = 0,
                     minVolume = 0,
                     maxVolume = 10,
-                    isAffectedByMute = false,
+                    isAffectedByMute = audioStream.value !in unMutableStreams,
                     isAffectedByRingerMode = false,
                     isMuted = false,
                 )
@@ -111,4 +137,16 @@
     override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int {
         return deviceCategories[bluetoothAddress] ?: AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN
     }
+
+    suspend fun sendVolumeControllerEvent(event: VolumeControllerEvent) {
+        if (isInitialized) {
+            mutableVolumeControllerEvents.emit(event)
+        }
+    }
+
+    override suspend fun notifyVolumeControllerVisible(isVisible: Boolean) {
+        if (isInitialized) {
+            mutableIsVolumeControllerVisible.value = isVisible
+        }
+    }
 }
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 0a617d1..a4719e5 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
@@ -18,7 +18,6 @@
 
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
 import com.android.settingslib.volume.data.repository.GroupIdToVolumes
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -30,7 +29,7 @@
         MutableStateFlow(TEST_GROUP_ID_INVALID)
     private val mutableVolumeMap: MutableStateFlow<GroupIdToVolumes> = MutableStateFlow(emptyMap())
 
-    override val inAudioSharing: Flow<Boolean> = mutableInAudioSharing
+    override val inAudioSharing: StateFlow<Boolean> = mutableInAudioSharing
     override val primaryGroupId: StateFlow<Int> = mutablePrimaryGroupId
     override val secondaryGroupId: StateFlow<Int> = mutableSecondaryGroupId
     override val volumeMap: StateFlow<GroupIdToVolumes> = mutableVolumeMap
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index e2d414e..3ac565a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.volume.data.repository.audioRepository
-import com.android.systemui.volume.data.repository.audioSharingRepository
 import com.android.systemui.volume.mediaOutputInteractor
 
 val Kosmos.audioOutputInteractor by
@@ -37,6 +36,5 @@
             bluetoothAdapter,
             deviceIconInteractor,
             mediaOutputInteractor,
-            audioSharingRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
index e7162d2..6d30c68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
@@ -17,9 +17,9 @@
 package com.android.systemui.volume.panel.component.captioning
 
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
 import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
 import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria
 import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.captioningViewModel
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
index 0edd9e0..e23a21a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
@@ -18,9 +18,9 @@
 
 import android.content.applicationContext
 import com.android.internal.logging.uiEventLogger
+import com.android.systemui.accessibility.domain.interactor.captioningInteractor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.view.accessibility.data.repository.captioningInteractor
 
 val Kosmos.captioningViewModel by
     Kosmos.Fixture {
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 9f11822..63a1325 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
@@ -20,6 +20,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.volume.domain.interactor.audioModeInteractor
 import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+import com.android.systemui.volume.domain.interactor.audioSharingInteractor
 import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.mediaOutputInteractor
 
@@ -31,5 +32,6 @@
             audioOutputInteractor,
             audioModeInteractor,
             mediaOutputInteractor,
+            audioSharingInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
index 83adc79..ad1292e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -38,6 +38,7 @@
     ): MediaDevice = mock {
         whenever(name).thenReturn(deviceName)
         whenever(icon).thenReturn(deviceIcon)
+        whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE)
     }
 
     fun wiredMediaDevice(
@@ -77,6 +78,18 @@
             whenever(name).thenReturn(deviceName)
             whenever(icon).thenReturn(deviceIcon)
             whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+            whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE)
+        }
+    }
+
+    fun remoteMediaDevice(
+        deviceName: String = "remote_media",
+        deviceIcon: Drawable? = TestStubDrawable(),
+    ): MediaDevice {
+        return mock<MediaDevice> {
+            whenever(name).thenReturn(deviceName)
+            whenever(icon).thenReturn(deviceIcon)
+            whenever(deviceType).thenReturn(MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE)
         }
     }
 }
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 a0a39d1..dd5bbf3 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
@@ -17,15 +17,15 @@
 package com.android.systemui.volume.panel.component.volume.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
 import com.android.systemui.volume.mediaOutputInteractor
 
 val Kosmos.audioSlidersInteractor by
     Kosmos.Fixture {
         AudioSlidersInteractor(
-            testScope.backgroundScope,
+            applicationCoroutineScope,
             mediaOutputInteractor,
-            audioRepository,
+            audioModeInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/VolumeSlidersViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/VolumeSlidersViewModelKosmos.kt
index 45a291e..6e848ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/VolumeSlidersViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/ui/viewmodel/VolumeSlidersViewModelKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
 import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.mediaOutputInteractor
 import com.android.systemui.volume.panel.component.volume.domain.interactor.audioSlidersInteractor
@@ -32,6 +33,7 @@
             mediaDeviceSessionInteractor,
             audioStreamSliderViewModelFactory,
             castVolumeSliderViewModelFactory,
+            audioModeInteractor,
             audioSlidersInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
index 2ba1211..0b438d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/data/repository/VolumePanelGlobalStateRepositoryKosmos.kt
@@ -18,6 +18,7 @@
 
 import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.shared.volumePanelLogger
 
 val Kosmos.volumePanelGlobalStateRepository by
-    Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager) }
+    Kosmos.Fixture { VolumePanelGlobalStateRepository(dumpManager, volumePanelLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
index a18f498..3804a9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.volume.panel.domain.defaultCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import com.android.systemui.volume.panel.ui.composable.enabledComponents
+import com.android.systemui.volume.shared.volumePanelLogger
 import javax.inject.Provider
 
 var Kosmos.criteriaByKey: Map<VolumePanelComponentKey, Provider<ComponentAvailabilityCriteria>> by
@@ -50,6 +51,7 @@
             enabledComponents,
             { defaultCriteria },
             testScope.backgroundScope,
+            volumePanelLogger,
             criteriaByKey,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
index 34a008f..c4fb9e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/viewmodel/VolumePanelViewModelKosmos.kt
@@ -18,17 +18,19 @@
 
 import android.content.applicationContext
 import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.dump.dumpManager
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.statusbar.policy.configurationController
 import com.android.systemui.volume.panel.dagger.factory.volumePanelComponentFactory
 import com.android.systemui.volume.panel.domain.VolumePanelStartable
 import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
+import com.android.systemui.volume.shared.volumePanelLogger
 
 var Kosmos.volumePanelStartables: Set<VolumePanelStartable> by Kosmos.Fixture { emptySet() }
 
 var Kosmos.volumePanelViewModel: VolumePanelViewModel by
-    Kosmos.Fixture { volumePanelViewModelFactory.create(testScope.backgroundScope) }
+    Kosmos.Fixture { volumePanelViewModelFactory.create(applicationCoroutineScope) }
 
 val Kosmos.volumePanelViewModelFactory: VolumePanelViewModel.Factory by
     Kosmos.Fixture {
@@ -37,6 +39,8 @@
             volumePanelComponentFactory,
             configurationController,
             broadcastDispatcher,
+            dumpManager,
+            volumePanelLogger,
             volumePanelGlobalStateInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
index 4e0c0883..d1bee61 100644
--- a/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/telecom/TelecomManagerKosmos.kt
@@ -21,4 +21,5 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.util.mockito.mock
 
-var Kosmos.telecomManager by Fixture<TelecomManager?> { mock() }
+val Kosmos.mockTelecomManager by Fixture<TelecomManager> { mock() }
+var Kosmos.telecomManager by Fixture<TelecomManager?> { mockTelecomManager }
diff --git a/packages/VpnDialogs/res/values-ar/strings.xml b/packages/VpnDialogs/res/values-ar/strings.xml
index 9307a74..4a0929f 100644
--- a/packages/VpnDialogs/res/values-ar/strings.xml
+++ b/packages/VpnDialogs/res/values-ar/strings.xml
@@ -17,8 +17,8 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"طلب الاتصال"</string>
-    <string name="warning" msgid="809658604548412033">"‏يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بالشبكة الافتراضية الخاصة التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر في الجزء العلوي من الشاشة عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
-    <string name="warning" product="tv" msgid="5188957997628124947">"‏يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة افتراضية خاصة (VPN) يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر على شاشتك عندما تكون الشبكة الافتراضية الخاصة نشطة."</string>
+    <string name="warning" msgid="809658604548412033">"‏يريد <xliff:g id="APP">%s</xliff:g> إعداد الاتصال بشبكة VPN التي تتيح له مراقبة حركة المرور على الشبكة. فلا توافق إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر في الجزء العلوي من الشاشة عندما تكون شبكة VPN نشطة."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"‏يريد تطبيق <xliff:g id="APP">%s</xliff:g> إعداد اتصال شبكة VPN يتيح له مراقبة حركة بيانات الشبكة. لا تقبل السماح بذلك إلا إذا كنت تثق في المصدر. &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; يظهر على شاشتك عندما تكون شبكة VPN نشطة."</string>
     <string name="legacy_title" msgid="192936250066580964">"‏VPN متصلة"</string>
     <string name="session" msgid="6470628549473641030">"الجلسة"</string>
     <string name="duration" msgid="3584782459928719435">"المدة:"</string>
@@ -27,9 +27,9 @@
     <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> بايت / <xliff:g id="NUMBER_1">%2$s</xliff:g> من الحزم"</string>
     <string name="always_on_disconnected_title" msgid="1906740176262776166">"‏يتعذّر الاتصال بشبكة VPN التي يتم تشغيلها دائمًا"</string>
     <string name="always_on_disconnected_message" msgid="555634519845992917">"تم إعداد شبكة <xliff:g id="VPN_APP_0">%1$s</xliff:g> بحيث تبقى متصلة بالإنترنت دائمًا، ومع ذلك يتعذّر اتصالها بالإنترنت الآن. سيستخدم هاتفك شبكة عامة إلى أن يتمكّن من إعادة الاتصال بشبكة <xliff:g id="VPN_APP_1">%1$s</xliff:g>."</string>
-    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"‏تم إعداد شبكة <xliff:g id="VPN_APP">%1$s</xliff:g> بحيث تبقى متصلة بالإنترنت دائمًا، ومع ذلك يتعذّر اتصالها بالإنترنت الآن. لن تتوفر لديك إمكانية اتصال إلى أن تتمكّن الشبكة الافتراضية الخاصة (VPN) من إعادة الاتصال."</string>
+    <string name="always_on_disconnected_message_lockdown" msgid="4232225539869452120">"‏تم إعداد شبكة <xliff:g id="VPN_APP">%1$s</xliff:g> بحيث تبقى متصلة بالإنترنت دائمًا، ومع ذلك يتعذّر اتصالها بالإنترنت الآن. لن تتوفر لديك إمكانية اتصال إلى أن تتمكّن شبكة VPN من إعادة الاتصال."</string>
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
-    <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"‏تغيير إعدادات الشبكة الافتراضية الخاصة (VPN)"</string>
+    <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"‏تغيير إعدادات شبكة VPN"</string>
     <string name="configure" msgid="4905518375574791375">"تهيئة"</string>
     <string name="disconnect" msgid="971412338304200056">"قطع الاتصال"</string>
     <string name="open_app" msgid="3717639178595958667">"فتح التطبيق"</string>
diff --git a/packages/VpnDialogs/res/values-be/strings.xml b/packages/VpnDialogs/res/values-be/strings.xml
index 54908f6..58c6349 100644
--- a/packages/VpnDialogs/res/values-be/strings.xml
+++ b/packages/VpnDialogs/res/values-be/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="prompt" msgid="3183836924226407828">"Запыт на падлучэнне"</string>
     <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> спрабуе наладзіць падлучэнне VPN, якое дазваляе сачыць за сеткавым трафікам. Прымайце толькі тады, калі вы давяраеце гэтай крыніцы. Калі VPN актыўны, у верхняй частцы экрана адлюстроўваецца &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
-    <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з\'явіцца значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
+    <string name="warning" product="tv" msgid="5188957997628124947">"Праграма \"<xliff:g id="APP">%s</xliff:g>\" запытвае дазвол на падключэнне да сеткі VPN, каб адсочваць сеткавы трафік. Дайце дазвол, толькі калі вы давяраеце крыніцы. Калі адбудзецца падключэнне да VPN, на экране з’явіцца значок &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt;."</string>
     <string name="legacy_title" msgid="192936250066580964">"VPN падключаны"</string>
     <string name="session" msgid="6470628549473641030">"Сессія"</string>
     <string name="duration" msgid="3584782459928719435">"Працягласць:"</string>
diff --git a/packages/VpnDialogs/res/values-in/strings.xml b/packages/VpnDialogs/res/values-in/strings.xml
index 342f403..c67e5db 100644
--- a/packages/VpnDialogs/res/values-in/strings.xml
+++ b/packages/VpnDialogs/res/values-in/strings.xml
@@ -31,7 +31,7 @@
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Ubah setelan VPN"</string>
     <string name="configure" msgid="4905518375574791375">"Konfigurasikan"</string>
-    <string name="disconnect" msgid="971412338304200056">"Putuskan koneksi"</string>
+    <string name="disconnect" msgid="971412338304200056">"Berhenti hubungkan"</string>
     <string name="open_app" msgid="3717639178595958667">"Buka aplikasi"</string>
     <string name="dismiss" msgid="6192859333764711227">"Tutup"</string>
     <string name="sanitized_vpn_label_with_ellipsis" msgid="7014327474633422235">"<xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_0">%1$s</xliff:g>… ( <xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_1">%2$s</xliff:g>)"</string>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
index 2f5a3dd..83a82ae 100644
--- a/packages/VpnDialogs/res/values-or/strings.xml
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -31,7 +31,7 @@
     <string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"VPN ସେଟିଂସ ବଦଳାନ୍ତୁ"</string>
     <string name="configure" msgid="4905518375574791375">"କନଫିଗର୍‍ କରନ୍ତୁ"</string>
-    <string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
+    <string name="disconnect" msgid="971412338304200056">"ଡିସକନେକ୍ଟ କରନ୍ତୁ"</string>
     <string name="open_app" msgid="3717639178595958667">"ଆପ୍‌ ଖୋଲନ୍ତୁ"</string>
     <string name="dismiss" msgid="6192859333764711227">"ଖାରଜ କରନ୍ତୁ"</string>
     <string name="sanitized_vpn_label_with_ellipsis" msgid="7014327474633422235">"<xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_0">%1$s</xliff:g>… ( <xliff:g id="SANITIZED_VPN_LABEL_WITH_ELLIPSIS_1">%2$s</xliff:g>)"</string>
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 2c4bc7c..26b0f61 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -78,8 +78,8 @@
 import android.util.Size;
 import android.view.Surface;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
 import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
 import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
@@ -171,7 +171,7 @@
             EXTENSIONS_VERSION.startsWith(LATENCY_VERSION_PREFIX) ||
             EXTENSIONS_VERSION.startsWith(EFV_VERSION_PREFIX));
 
-    private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
+    private static HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
     private CameraManager mCameraManager;
 
     private static boolean checkForLatencyAPI() {
@@ -820,7 +820,7 @@
             mCameraManager = getSystemService(CameraManager.class);
 
             String [] cameraIds = mCameraManager.getCameraIdListNoLazy();
-            if (cameraIds != null) {
+            if (cameraIds != null && mMetadataVendorIdMap.isEmpty()) {
                 for (String cameraId : cameraIds) {
                     CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
                     Object thisClass = CameraCharacteristics.Key.class;
@@ -2605,11 +2605,7 @@
             ret.size.height = imageReaderOutputConfig.getSize().getHeight();
             ret.imageFormat = imageReaderOutputConfig.getImageFormat();
             ret.capacity = imageReaderOutputConfig.getMaxImages();
-            if (EFV_SUPPORTED) {
-                ret.usage = imageReaderOutputConfig.getUsage();
-            } else {
-                ret.usage = 0;
-            }
+            ret.usage = imageReaderOutputConfig.getUsage();
         } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
             MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
                     (MultiResolutionImageReaderOutputConfigImpl) output;
diff --git a/ravenwood/.gitignore b/ravenwood/.gitignore
new file mode 100644
index 0000000..751553b
--- /dev/null
+++ b/ravenwood/.gitignore
@@ -0,0 +1 @@
+*.bak
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 6150343..9b0c8e5 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -160,12 +160,21 @@
         "ravenwood-framework",
         "services.core.ravenwood",
         "junit",
+        "framework-annotations-lib",
     ],
     sdk_version: "core_current",
     visibility: ["//frameworks/base"],
     jarjar_rules: ":ravenwood-services-jarjar-rules",
 }
 
+java_device_for_host {
+    name: "ravenwood-junit-impl-for-ravenizer",
+    libs: [
+        "ravenwood-junit-impl",
+    ],
+    visibility: [":__subpackages__"],
+}
+
 // Separated out from ravenwood-junit-impl since it needs to compile
 // against `module_current`
 java_library {
@@ -203,6 +212,7 @@
     libs: [
         "junit",
         "flag-junit",
+        "framework-annotations-lib",
     ],
     visibility: ["//visibility:public"],
 }
@@ -323,6 +333,7 @@
     name: "ravenwood-runtime",
     data: [
         "framework-res",
+        "ravenwood-empty-res",
     ],
     libs: [
         "100-framework-minus-apex.ravenwood",
diff --git a/ravenwood/OWNERS b/ravenwood/OWNERS
index badfca0..f7b13d1 100644
--- a/ravenwood/OWNERS
+++ b/ravenwood/OWNERS
@@ -1,8 +1,8 @@
 set noparent
 
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]  #{LAST_RESORT_SUGGESTION}
+
 
 per-file ravenwood-annotation-allowed-classes.txt = [email protected]
 per-file texts/ravenwood-annotation-allowed-classes.txt = [email protected]
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index fbf27fa..469759b 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -1,20 +1,12 @@
-// Keep the following two TEST_MAPPINGs in sync:
-// frameworks/base/ravenwood/TEST_MAPPING
-// frameworks/base/tools/hoststubgen/TEST_MAPPING
 {
   "presubmit": [
     { "name": "tiny-framework-dump-test" },
     { "name": "hoststubgentest" },
+    { "name": "hoststubgen-test-tiny-test" },
     { "name": "hoststubgen-invoke-test" },
-    {
-      "name": "RavenwoodMockitoTest_device"
-    },
-    {
-      "name": "RavenwoodBivalentTest_device"
-    },
-    {
-      "name": "RavenwoodResApkTest"
-    },
+    { "name": "RavenwoodMockitoTest_device" },
+    { "name": "RavenwoodBivalentTest_device" },
+
     // The sysui tests should match vendor/unbundled_google/packages/SystemUIGoogle/TEST_MAPPING
     {
       "name": "SystemUIGoogleTests",
@@ -42,6 +34,113 @@
     }
   ],
   "ravenwood-presubmit": [
+    // AUTO-GENERATED-START
+    // DO NOT MODIFY MANUALLY
+    // Use scripts/update-test-mapping.sh to update it.
+    {
+      "name": "AdServicesSharedLibrariesUnitTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "android.test.mock.ravenwood.tests",
+      "host": true
+    },
+    {
+      "name": "CarLibHostUnitTest",
+      "host": true
+    },
+    {
+      "name": "CarServiceHostUnitTest",
+      "host": true
+    },
+    {
+      "name": "CarSystemUIRavenTests",
+      "host": true
+    },
+    {
+      "name": "CtsAccountManagerTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsAppTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsContentTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsDatabaseTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsGraphicsTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsIcuTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsInputMethodTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsOsTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsProtoTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsResourcesTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsTextTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "CtsUtilTestCasesRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksCoreSystemPropertiesTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksCoreTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksInputMethodSystemServerTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksMockingServicesTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksServicesTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "FrameworksUtilTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "InternalTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "PowerStatsTestsRavenwood",
+      "host": true
+    },
+    {
+      "name": "RavenwoodBivalentTest",
+      "host": true
+    },
     {
       "name": "RavenwoodMinimumTest",
       "host": true
@@ -51,16 +150,21 @@
       "host": true
     },
     {
-      "name": "CtsUtilTestCasesRavenwood",
+      "name": "RavenwoodResApkTest",
       "host": true
     },
     {
-      "name": "RavenwoodCoreTest",
+      "name": "RavenwoodRuntimeTest",
       "host": true
     },
     {
-      "name": "RavenwoodBivalentTest",
+      "name": "RavenwoodServicesTest",
+      "host": true
+    },
+    {
+      "name": "SystemUiRavenTests",
       "host": true
     }
+    // AUTO-GENERATED-END
   ]
 }
diff --git a/ravenwood/bivalenttest/Android.bp b/ravenwood/bivalenttest/Android.bp
index 06cf08e6..e897735 100644
--- a/ravenwood/bivalenttest/Android.bp
+++ b/ravenwood/bivalenttest/Android.bp
@@ -39,6 +39,9 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
 
+        "junit-params",
+        "platform-parametric-runner-lib",
+
         // To make sure it won't cause VerifyError (b/324063814)
         "platformprotosnano",
     ],
@@ -65,6 +68,9 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
 
+        "junit-params",
+        "platform-parametric-runner-lib",
+
         "ravenwood-junit",
     ],
     jni_libs: [
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
index 3a24c0e..e8f59db 100644
--- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleDeviceOnlyTest.java
@@ -26,6 +26,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/**
+ * Test to ensure @DisabledOnRavenwood works. Note, now the DisabledOnRavenwood annotation
+ * is handled by the test runner, so it won't really need the class rule.
+ */
 @RunWith(AndroidJUnit4.class)
 @DisabledOnRavenwood
 public class RavenwoodClassRuleDeviceOnlyTest {
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
deleted file mode 100644
index aa33dc3..0000000
--- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodClassRuleRavenwoodOnlyTest.java
+++ /dev/null
@@ -1,43 +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.ravenwoodtest.bivalenttest;
-
-import android.platform.test.ravenwood.RavenwoodClassRule;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.ClassRule;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-// TODO: atest RavenwoodBivalentTest_device fails with the following message.
-// `RUNNER ERROR: Instrumentation reported numtests=7 but only ran 6`
-// @android.platform.test.annotations.DisabledOnNonRavenwood
-// Figure it out and then make DisabledOnNonRavenwood support TYPEs as well.
-@Ignore
-public class RavenwoodClassRuleRavenwoodOnlyTest {
-    @ClassRule
-    public static final RavenwoodClassRule sRavenwood = new RavenwoodClassRule();
-
-    @Test
-    public void testRavenwoodOnly() {
-        Assert.assertTrue(RavenwoodRule.isOnRavenwood());
-    }
-}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodRuleTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodRuleTest.java
index 01e90d8..3de372e 100644
--- a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodRuleTest.java
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodRuleTest.java
@@ -15,7 +15,6 @@
  */
 package com.android.ravenwoodtest.bivalenttest;
 
-import android.platform.test.annotations.DisabledOnNonRavenwood;
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.util.Log;
@@ -39,12 +38,6 @@
     }
 
     @Test
-    @DisabledOnNonRavenwood
-    public void testRavenwoodOnly() {
-        Assert.assertTrue(RavenwoodRule.isOnRavenwood());
-    }
-
-    @Test
     public void testDumpSystemProperties() {
         Log.w("XXX", "System properties");
         for (var sp : System.getProperties().entrySet()) {
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java
new file mode 100644
index 0000000..09a0aa8
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/CallTracker.java
@@ -0,0 +1,110 @@
+/*
+ * 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.ravenizer;
+
+import static org.junit.Assert.fail;
+
+import static java.lang.StackWalker.Option.RETAIN_CLASS_REFERENCE;
+
+import android.util.Log;
+
+import java.lang.StackWalker.StackFrame;
+import java.util.HashMap;
+
+/**
+ * Used to keep track of and count the number of calls.
+ */
+public class CallTracker {
+    public static final String TAG = "CallTracker";
+
+    private final HashMap<String, Integer> mNumCalled = new HashMap<>();
+
+    /**
+     * Call it when a method is called. It increments the count for the calling method.
+     */
+    public void incrementMethodCallCount() {
+        var methodName = getCallingMethodName(1);
+
+        Log.i(TAG, "Method called: " + methodName);
+
+        mNumCalled.put(methodName, getNumCalled(methodName) + 1);
+    }
+
+    /**
+     * Return the number of calls of a method.
+     */
+    public int getNumCalled(String methodName) {
+        return mNumCalled.getOrDefault(methodName, 0);
+    }
+
+    /**
+     * Return the current method name. (with the class name.)
+     */
+    private static String getCallingMethodName(int frameOffset) {
+        var walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
+        var caller = walker.walk(frames ->
+                frames.skip(1 + frameOffset).findFirst().map(StackFrame::getMethodName)
+        );
+        return caller.get();
+    }
+
+    /**
+     * Check the number of calls stored in {@link #mNumCalled}.
+     */
+    public void assertCalls(Object... methodNameAndCountPairs) {
+        // Create a local copy
+        HashMap<String, Integer> counts = new HashMap<>(mNumCalled);
+        for (int i = 0; i < methodNameAndCountPairs.length - 1; i += 2) {
+            String methodName = (String) methodNameAndCountPairs[i];
+            int expectedCount = (Integer) methodNameAndCountPairs[i + 1];
+
+            if (getNumCalled(methodName) != expectedCount) {
+                fail(String.format("Method %s: expected call count=%d, actual=%d",
+                        methodName, expectedCount, getNumCalled(methodName)));
+            }
+            counts.remove(methodName);
+        }
+        // All other entries are expected to be 0.
+        var sb = new StringBuilder();
+        for (var e : counts.entrySet()) {
+            if (e.getValue() == 0) {
+                continue;
+            }
+            sb.append(String.format("Method %s: expected call count=0, actual=%d",
+                    e.getKey(), e.getValue()));
+        }
+        if (sb.length() > 0) {
+            fail(sb.toString());
+        }
+    }
+
+    /**
+     * Same as {@link #assertCalls(Object...)} but it kills the process if it fails.
+     * Only use in @AfterClass.
+     */
+    public void assertCallsOrDie(Object... methodNameAndCountPairs) {
+        try {
+            assertCalls(methodNameAndCountPairs);
+        } catch (Throwable th) {
+            // TODO: I don't think it's by spec, but the exception here would be ignored both on
+            // ravenwood and on the device side. Look into it.
+            Log.e(TAG, "*** Failure detected in @AfterClass! ***", th);
+            Log.e(TAG, "JUnit seems to ignore exceptions from @AfterClass, so killing self.");
+            System.exit(7);
+        }
+    }
+
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java
new file mode 100644
index 0000000..d7c2c6c
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.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.ravenwoodtest.bivalenttest.ravenizer;
+
+import static org.junit.Assert.assertFalse;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Make sure RavenwoodAwareTestRunnerTest properly delegates to the original runner,
+ * and also run the special annotated methods.
+ */
+@RunWith(JUnitParamsRunner.class)
+public class RavenwoodAwareTestRunnerTest {
+    public static final String TAG = "RavenwoodAwareTestRunnerTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    private static int getExpectedRavenwoodRunnerInitializingNumCalls() {
+        return RavenwoodRule.isOnRavenwood() ? 1 : 0;
+    }
+
+    @RavenwoodTestRunnerInitializing
+    public static void ravenwoodRunnerInitializing() {
+        // No other calls should have been made.
+        sCallTracker.assertCalls();
+
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        sCallTracker.assertCalls(
+                "ravenwoodRunnerInitializing",
+                getExpectedRavenwoodRunnerInitializingNumCalls()
+        );
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    public void test1() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    @Parameters({"foo", "bar"})
+    public void testWithParams(String arg) {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    @DisabledOnRavenwood
+    public void testDeviceOnly() {
+        assertFalse(RavenwoodRule.isOnRavenwood());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "ravenwoodRunnerInitializing",
+                getExpectedRavenwoodRunnerInitializingNumCalls(),
+                "beforeClass", 1,
+                "test1", 1,
+                "testWithParams", 2
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java
new file mode 100644
index 0000000..7ef672e
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitClassRuleDeviceOnlyTest.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood
+public class RavenwoodImplicitClassRuleDeviceOnlyTest {
+    public static final String TAG = "RavenwoodImplicitClassRuleDeviceOnlyTest";
+
+    @BeforeClass
+    public static void beforeClass() {
+        // This method shouldn't be called -- unless RUN_DISABLED_TESTS is enabled.
+
+        // If we're doing RUN_DISABLED_TESTS, don't throw here, because that'd confuse junit.
+        if (!RavenwoodRule.private$ravenwood().isRunningDisabledTests()) {
+            Assert.assertFalse(RavenwoodRule.isOnRavenwood());
+        }
+    }
+
+    @Test
+    public void testDeviceOnly() {
+        Assert.assertFalse(RavenwoodRule.isOnRavenwood());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        if (RavenwoodRule.isOnRavenwood()) {
+            Log.e(TAG, "Even @AfterClass shouldn't be executed!");
+
+            if (!RavenwoodRule.private$ravenwood().isRunningDisabledTests()) {
+                System.exit(1);
+            }
+        }
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleOrderRewriteTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleOrderRewriteTest.java
new file mode 100644
index 0000000..7ef40dc
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleOrderRewriteTest.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assume;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+
+/**
+ * Make sure ravenizer will inject implicit rules and rewrite the existing rules' orders.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodImplicitRuleOrderRewriteTest {
+
+    private static final TestRule sEmptyRule = (statement, description) -> statement;
+
+    // We have two sets of 9 rules below, for class rules and instance rules.
+    // - Ravenizer will inject 2 more rules of each kind.
+    // - Ravenizer will adjust their order, so even though we'll add two sets of class and instance
+    //  rules with a MIN / MAX order, there will still be no duplicate in the order.
+
+    private static final int EXPECTED_RULE_COUNT = 9 + 2;
+
+    @ClassRule(order = Integer.MIN_VALUE)
+    public static final TestRule sRule01 = sEmptyRule;
+
+    @ClassRule(order = Integer.MIN_VALUE + 1)
+    public static final TestRule sRule02 = sEmptyRule;
+
+    @ClassRule(order = -10)
+    public static final TestRule sRule03 = sEmptyRule;
+
+    @ClassRule(order = -1)
+    public static final TestRule sRule04 = sEmptyRule;
+
+    @ClassRule(order = 0)
+    public static final TestRule sRule05 = sEmptyRule;
+
+    @ClassRule(order = 1)
+    public static final TestRule sRule06 = sEmptyRule;
+
+    @ClassRule(order = 10)
+    public static final TestRule sRule07 = sEmptyRule;
+
+    @ClassRule(order = Integer.MAX_VALUE - 1)
+    public static final TestRule sRule08 = sEmptyRule;
+
+    @ClassRule(order = Integer.MAX_VALUE)
+    public static final TestRule sRule09 = sEmptyRule;
+
+    @Rule(order = Integer.MIN_VALUE)
+    public final TestRule mRule01 = sEmptyRule;
+
+    @Rule(order = Integer.MIN_VALUE + 1)
+    public final TestRule mRule02 = sEmptyRule;
+
+    @Rule(order = -10)
+    public final TestRule mRule03 = sEmptyRule;
+
+    @Rule(order = -1)
+    public final TestRule mRule04 = sEmptyRule;
+
+    @Rule(order = 0)
+    public final TestRule mRule05 = sEmptyRule;
+
+    @Rule(order = 1)
+    public final TestRule mRule06 = sEmptyRule;
+
+    @Rule(order = 10)
+    public final TestRule mRule07 = sEmptyRule;
+
+    @Rule(order = Integer.MAX_VALUE - 1)
+    public final TestRule mRule08 = sEmptyRule;
+
+    @Rule(order = Integer.MAX_VALUE)
+    public final TestRule mRule09 = sEmptyRule;
+
+    private void checkRules(boolean classRule) {
+        final var anotClass = classRule ? ClassRule.class : Rule.class;
+
+        final HashMap<Integer, Integer> ordersUsed = new HashMap<>();
+
+        for (var field : this.getClass().getDeclaredFields()) {
+            if (!field.isAnnotationPresent(anotClass)) {
+                continue;
+            }
+            final var anot = field.getAnnotation(anotClass);
+            final int order = classRule ? ((ClassRule) anot).order() : ((Rule) anot).order();
+
+            if (ordersUsed.containsKey(order)) {
+                fail("Detected duplicate order=" + order);
+            }
+            ordersUsed.put(order, 1);
+        }
+        assertEquals(EXPECTED_RULE_COUNT, ordersUsed.size());
+    }
+
+    @Test
+    public void testClassRules() {
+        Assume.assumeTrue(RavenwoodRule.isOnRavenwood());
+
+        checkRules(true);
+    }
+
+    @Test
+    public void testInstanceRules() {
+        Assume.assumeTrue(RavenwoodRule.isOnRavenwood());
+
+        checkRules(false);
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTest.java
new file mode 100644
index 0000000..ae596b1
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTest.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test to make sure when a test class inherits another test class, the base class's
+ * implicit rules are shadowed and won't be executed.
+ *
+ * ... But for now, we don't have a way to programmatically check it, so for now we need to
+ * check the log file manually.
+ *
+ * TODO: Implement the test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodImplicitRuleShadowingTest extends RavenwoodImplicitRuleShadowingTestBase {
+    @Test
+    public void testOkInSubClass() {
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTestBase.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTestBase.java
new file mode 100644
index 0000000..1ca97af
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodImplicitRuleShadowingTestBase.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * A test class that's just inherited by RavenwoodImplicitRuleShadowingTest.
+ */
+@RunWith(AndroidJUnit4.class)
+public abstract class RavenwoodImplicitRuleShadowingTestBase {
+    @Test
+    public void testOkInBaseClass() {
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
new file mode 100644
index 0000000..9d878f4
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+
+import org.junit.Test;
+
+/**
+ * Test for {@link android.platform.test.annotations.NoRavenizer}
+ */
+@NoRavenizer
+public class RavenwoodNoRavenizerTest {
+    public static final String TAG = "RavenwoodNoRavenizerTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    /**
+     * With @NoRavenizer, this method shouldn't be called.
+     */
+    @RavenwoodTestRunnerInitializing
+    public static void ravenwoodRunnerInitializing() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    /**
+     * Make sure ravenwoodRunnerInitializing() wasn't called.
+     */
+    @Test
+    public void testNotRavenized() {
+        sCallTracker.assertCalls(
+                "ravenwoodRunnerInitializing", 0
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
new file mode 100644
index 0000000..c77841b
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java
@@ -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.ravenwoodtest.bivalenttest.ravenizer;
+
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for "RAVENWOOD_RUN_DISABLED_TESTS" with "REALLY_DISABLED" set.
+ *
+ * This test is only executed on Ravenwood.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodRunDisabledTestsReallyDisabledTest {
+    private static final String TAG = "RavenwoodRunDisabledTestsTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    @RavenwoodTestRunnerInitializing
+    public static void ravenwoodRunnerInitializing() {
+        RavenwoodRule.private$ravenwood().overrideRunDisabledTest(true,
+                "\\#testReallyDisabled$");
+    }
+
+    /**
+     * This test gets to run with RAVENWOOD_RUN_DISABLED_TESTS set.
+     */
+    @Test
+    @DisabledOnRavenwood
+    public void testDisabledTestGetsToRun() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        sCallTracker.incrementMethodCallCount();
+
+        fail("This test won't pass on Ravenwood.");
+    }
+
+    /**
+     * This will still not be executed due to the "really disabled" pattern.
+     */
+    @Test
+    @DisabledOnRavenwood
+    public void testReallyDisabled() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        sCallTracker.incrementMethodCallCount();
+
+        fail("This test won't pass on Ravenwood.");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "testDisabledTestGetsToRun", 1,
+                "testReallyDisabled", 0
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
new file mode 100644
index 0000000..ea1a29d
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.ravenizer;
+
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for "RAVENWOOD_RUN_DISABLED_TESTS". (with no "REALLY_DISABLED" set.)
+ *
+ * This test is only executed on Ravenwood.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodRunDisabledTestsTest {
+    private static final String TAG = "RavenwoodRunDisabledTestsTest";
+
+    @Rule
+    public ExpectedException mExpectedException = ExpectedException.none();
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    @RavenwoodTestRunnerInitializing
+    public static void ravenwoodRunnerInitializing() {
+        RavenwoodRule.private$ravenwood().overrideRunDisabledTest(true, null);
+    }
+
+    @Test
+    @DisabledOnRavenwood
+    public void testDisabledTestGetsToRun() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        sCallTracker.incrementMethodCallCount();
+
+        fail("This test won't pass on Ravenwood.");
+    }
+
+    @Test
+    @DisabledOnRavenwood
+    public void testDisabledButPass() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        sCallTracker.incrementMethodCallCount();
+
+        // When a @DisabledOnRavenwood actually passed, the runner should make fail().
+        mExpectedException.expectMessage("it actually passed under Ravenwood");
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            return;
+        }
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "testDisabledTestGetsToRun", 1,
+                "testDisabledButPass", 1
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithAndroidXRunnerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithAndroidXRunnerTest.java
new file mode 100644
index 0000000..c042eb0
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithAndroidXRunnerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.ravenizer;
+
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Make sure ravenwood's test runner works with {@link AndroidJUnit4}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodRunnerWithAndroidXRunnerTest {
+    public static final String TAG = "RavenwoodRunnerWithAndroidXRunnerTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    @BeforeClass
+    public static void beforeClass() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Before
+    public void beforeTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @After
+    public void afterTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    public void test1() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    public void test2() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "beforeClass", 1,
+                "beforeTest", 2,
+                "afterTest", 2,
+                "test1", 1,
+                "test2", 1
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithJUnitParamsRunnerTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithJUnitParamsRunnerTest.java
new file mode 100644
index 0000000..2feb5ba
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithJUnitParamsRunnerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.ravenizer;
+
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Make sure ravenwood's test runner works with {@link AndroidJUnit4}.
+ */
+@RunWith(JUnitParamsRunner.class)
+public class RavenwoodRunnerWithJUnitParamsRunnerTest  {
+    public static final String TAG = "RavenwoodRunnerTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    @BeforeClass
+    public static void beforeClass() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Before
+    public void beforeTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @After
+    public void afterTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    public void testWithNoParams() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Test
+    @Parameters({"foo", "bar"})
+    public void testWithParams(String arg) {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "beforeClass", 1,
+                "beforeTest", 3,
+                "afterTest", 3,
+                "testWithNoParams", 1,
+                "testWithParams", 2
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithParameterizedAndroidJunit4Test.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithParameterizedAndroidJunit4Test.java
new file mode 100644
index 0000000..7e3bc0f
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunnerWithParameterizedAndroidJunit4Test.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.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Make sure ravenwood's test runner works with {@link ParameterizedAndroidJunit4}.
+ */
+@RunWith(ParameterizedAndroidJunit4.class)
+public class RavenwoodRunnerWithParameterizedAndroidJunit4Test {
+    public static final String TAG = "RavenwoodRunnerTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    private final String mParam;
+
+    private static int sNumInsantiation = 0;
+
+    public RavenwoodRunnerWithParameterizedAndroidJunit4Test(String param) {
+        mParam = param;
+        sNumInsantiation++;
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        // It seems like ParameterizedAndroidJunit4 calls the @BeforeTest / @AfterTest methods
+        // one time too many.
+        // With two parameters, this method should be called only twice, but it's actually
+        // called three times.
+        // So let's not check the number fo beforeClass calls.
+    }
+
+    @Before
+    public void beforeTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @After
+    public void afterTest() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @Parameters
+    public static List<String> getParams() {
+        var params =  new ArrayList<String>();
+        params.add("foo");
+        params.add("bar");
+        return params;
+    }
+
+    @Test
+    public void testWithParams() {
+        sCallTracker.incrementMethodCallCount();
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "beforeTest", sNumInsantiation,
+                "afterTest", sNumInsantiation,
+                "testWithParams", sNumInsantiation
+        );
+    }
+}
diff --git a/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java
new file mode 100644
index 0000000..7e396c2
--- /dev/null
+++ b/ravenwood/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodSuiteTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest.bivalenttest.ravenizer;
+
+import android.util.Log;
+
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Test to make sure {@link Suite} works with the ravenwood test runner.
+ */
+@RunWith(Suite.class)
[email protected]({
+        RavenwoodSuiteTest.Test1.class,
+        RavenwoodSuiteTest.Test2.class
+})
+public class RavenwoodSuiteTest {
+    public static final String TAG = "RavenwoodSuiteTest";
+
+    private static final CallTracker sCallTracker = new CallTracker();
+
+    @AfterClass
+    public static void afterClass() {
+        Log.i(TAG, "afterClass called");
+
+        sCallTracker.assertCallsOrDie(
+                "test1", 1,
+                "test2", 1
+        );
+    }
+
+    /**
+     * Workaround for the issue where tradefed won't think a class is a test class
+     * if it has a @RunWith but no @Test methods, even if it is a Suite.
+     */
+    @Test
+    public void testEmpty() {
+    }
+
+    public static class Test1 {
+        @Test
+        public void test1() {
+            sCallTracker.incrementMethodCallCount();
+        }
+    }
+
+    public static class Test2 {
+        @Test
+        public void test2() {
+            sCallTracker.incrementMethodCallCount();
+        }
+    }
+}
diff --git a/ravenwood/coretest/Android.bp b/ravenwood/coretest/Android.bp
deleted file mode 100644
index a78c5c1..0000000
--- a/ravenwood/coretest/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_ravenwood_test {
-    name: "RavenwoodCoreTest",
-
-    static_libs: [
-        "androidx.annotation_annotation",
-        "androidx.test.ext.junit",
-        "androidx.test.rules",
-    ],
-    srcs: [
-        "test/**/*.java",
-    ],
-    sdk_version: "test_current",
-    auto_gen_config: true,
-}
diff --git a/ravenwood/coretest/README.md b/ravenwood/coretest/README.md
deleted file mode 100644
index b60bfbf..0000000
--- a/ravenwood/coretest/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Ravenwood core test
-
-This test contains (non-bivalent) tests for Ravenwood itself -- e.g. tests for the ravenwood rules.
\ No newline at end of file
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java
deleted file mode 100644
index f1e33cb..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodTestRunnerValidationTest.java
+++ /dev/null
@@ -1,53 +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.ravenwoodtest.coretest;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4; // Intentionally use the deprecated one.
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * Test for the test runner validator in RavenwoodRule.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestRunnerValidationTest {
-    // Note the following rules don't have a @Rule, because they need to be applied in a specific
-    // order. So we use a RuleChain instead.
-    private ExpectedException mThrown = ExpectedException.none();
-    private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Rule
-    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
-    public RavenwoodTestRunnerValidationTest() {
-        Assume.assumeTrue(RavenwoodRule._$RavenwoodPrivate.isOptionalValidationEnabled());
-        // Because RavenwoodRule will throw this error before executing the test method,
-        // we can't do it in the test method itself.
-        // So instead, we initialize it here.
-        mThrown.expectMessage("Switch to androidx.test.ext.junit.runners.AndroidJUnit4");
-    }
-
-    @Test
-    public void testValidateTestRunner() {
-    }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java
deleted file mode 100644
index db95fad..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail01_Test.java
+++ /dev/null
@@ -1,51 +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.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail01_Test {
-    private ExpectedException mThrown = ExpectedException.none();
-    private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Rule
-    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
-    public RavenwoodTestMethodValidation_Fail01_Test() {
-        mThrown.expectMessage("Method setUp() doesn't have @Before");
-    }
-
-    @SuppressWarnings("JUnit4SetUpNotRun")
-    public void setUp() {
-    }
-
-    @Test
-    public void testEmpty() {
-    }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java
deleted file mode 100644
index ddc66c7..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail02_Test.java
+++ /dev/null
@@ -1,51 +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.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail02_Test {
-    private ExpectedException mThrown = ExpectedException.none();
-    private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Rule
-    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
-    public RavenwoodTestMethodValidation_Fail02_Test() {
-        mThrown.expectMessage("Method tearDown() doesn't have @After");
-    }
-
-    @SuppressWarnings("JUnit4TearDownNotRun")
-    public void tearDown() {
-    }
-
-    @Test
-    public void testEmpty() {
-    }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java
deleted file mode 100644
index ec8e907..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_Fail03_Test.java
+++ /dev/null
@@ -1,51 +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.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.RuleChain;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_Fail03_Test {
-    private ExpectedException mThrown = ExpectedException.none();
-    private final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Rule
-    public final RuleChain chain = RuleChain.outerRule(mThrown).around(mRavenwood);
-
-    public RavenwoodTestMethodValidation_Fail03_Test() {
-        mThrown.expectMessage("Method testFoo() doesn't have @Test");
-    }
-
-    @SuppressWarnings("JUnit4TestNotRun")
-    public void testFoo() {
-    }
-
-    @Test
-    public void testEmpty() {
-    }
-}
diff --git a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java b/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java
deleted file mode 100644
index d952d07..0000000
--- a/ravenwood/coretest/test/com/android/ravenwoodtest/coretest/methodvalidation/RavenwoodTestMethodValidation_OkTest.java
+++ /dev/null
@@ -1,56 +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.ravenwoodtest.coretest.methodvalidation;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * RavenwoodRule has a validator to ensure "test-looking" methods have valid JUnit annotations.
- * This class contains tests for this validator.
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodTestMethodValidation_OkTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Before
-    public void setUp() {
-    }
-
-    @Before
-    public void testSetUp() {
-    }
-
-    @After
-    public void tearDown() {
-    }
-
-    @After
-    public void testTearDown() {
-    }
-
-    @Test
-    public void testEmpty() {
-    }
-}
diff --git a/ravenwood/empty-res/Android.bp b/ravenwood/empty-res/Android.bp
new file mode 100644
index 0000000..3af7690
--- /dev/null
+++ b/ravenwood/empty-res/Android.bp
@@ -0,0 +1,4 @@
+android_app {
+    name: "ravenwood-empty-res",
+    sdk_version: "current",
+}
diff --git a/ravenwood/empty-res/AndroidManifest.xml b/ravenwood/empty-res/AndroidManifest.xml
new file mode 100644
index 0000000..f73460b
--- /dev/null
+++ b/ravenwood/empty-res/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.ravenwood.emptyres">
+</manifest>
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
new file mode 100644
index 0000000..f237ba9
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -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 android.platform.test.ravenwood;
+
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+
+import static org.junit.Assert.fail;
+
+import android.os.Bundle;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
+import android.platform.test.ravenwood.RavenwoodTestStats.Result;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runners.model.TestClass;
+
+/**
+ * Provide hook points created by {@link RavenwoodAwareTestRunner}.
+ */
+public class RavenwoodAwareTestRunnerHook {
+    private static final String TAG = "RavenwoodAwareTestRunnerHook";
+
+    private RavenwoodAwareTestRunnerHook() {
+    }
+
+    private static RavenwoodTestStats sStats; // lazy initialization.
+    private static Description sCurrentClassDescription;
+
+    private static RavenwoodTestStats getStats() {
+        if (sStats == null) {
+            // We don't want to throw in the static initializer, because tradefed may not report
+            // it properly, so we initialize it here.
+            sStats = new RavenwoodTestStats();
+        }
+        return sStats;
+    }
+
+    /**
+     * Called when a runner starts, before the inner runner gets a chance to run.
+     */
+    public static void onRunnerInitializing(Runner runner, TestClass testClass) {
+        // This log call also ensures the framework JNI is loaded.
+        Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass()
+                + " runner=" + runner);
+
+        // TODO: Move the initialization code to a better place.
+
+        // This will let AndroidJUnit4 use the original runner.
+        System.setProperty("android.junit.runner",
+                "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
+        System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
+
+
+        // This is needed to make AndroidJUnit4ClassRunner happy.
+        InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+    }
+
+    /**
+     * Called when a whole test class is skipped.
+     */
+    public static void onClassSkipped(Description description) {
+        Log.i(TAG, "onClassSkipped: description=" + description);
+        getStats().onClassSkipped(description);
+    }
+
+    /**
+     * Called before a test / class.
+     *
+     * Return false if it should be skipped.
+     */
+    public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
+            Scope scope, Order order) {
+        Log.i(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
+
+        if (scope == Scope.Class && order == Order.First) {
+            // Keep track of the current class.
+            sCurrentClassDescription = description;
+        }
+
+        // Class-level annotations are checked by the runner already, so we only check
+        // method-level annotations here.
+        if (scope == Scope.Instance && order == Order.First) {
+            if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(
+                    description, true)) {
+                getStats().onTestFinished(sCurrentClassDescription, description, Result.Skipped);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Called after a test / class.
+     *
+     * Return false if the exception should be ignored.
+     */
+    public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
+            Scope scope, Order order, Throwable th) {
+        Log.i(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
+
+        if (scope == Scope.Instance && order == Order.First) {
+            getStats().onTestFinished(sCurrentClassDescription, description,
+                    th == null ? Result.Passed : Result.Failed);
+
+        } else if (scope == Scope.Class && order == Order.Last) {
+            getStats().onClassFinished(sCurrentClassDescription);
+        }
+
+        // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
+        if (RavenwoodRule.private$ravenwood().isRunningDisabledTests()
+                && scope == Scope.Instance && order == Order.First) {
+
+            boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood(
+                    description, false);
+            if (th == null) {
+                // Test passed. Is the test method supposed to be enabled?
+                if (isTestEnabled) {
+                    // Enabled and didn't throw, okay.
+                    return true;
+                } else {
+                    // Disabled and didn't throw. We should report it.
+                    fail("Test wasn't included under Ravenwood, but it actually "
+                            + "passed under Ravenwood; consider updating annotations");
+                    return true; // unreachable.
+                }
+            } else {
+                // Test failed.
+                if (isTestEnabled) {
+                    // Enabled but failed. We should throw the exception.
+                    return true;
+                } else {
+                    // Disabled and failed. Expected. Don't throw.
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Called by {@link RavenwoodAwareTestRunner} to see if it should run a test class or not.
+     */
+    public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
+        return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true);
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 1dd5e1d..48bed79 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -16,8 +16,13 @@
 
 package android.platform.test.ravenwood;
 
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
+
 import android.content.ClipboardManager;
 import android.content.Context;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.hardware.ISerialManager;
 import android.hardware.SerialManager;
 import android.os.Handler;
@@ -31,11 +36,18 @@
 import android.util.ArrayMap;
 import android.util.Singleton;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.IOException;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
 
 public class RavenwoodContext extends RavenwoodBaseContext {
+    private static final String TAG = "Ravenwood";
+
+    private final Object mLock = new Object();
     private final String mPackageName;
     private final HandlerThread mMainThread;
 
@@ -44,15 +56,29 @@
     private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
     private final ArrayMap<String, Supplier<?>> mNameToFactory = new ArrayMap<>();
 
+    private final File mFilesDir;
+    private final File mCacheDir;
+    private final Supplier<Resources> mResourcesSupplier;
+
+    @GuardedBy("mLock")
+    private Resources mResources;
+
+    @GuardedBy("mLock")
+    private Resources.Theme mTheme;
+
     private void registerService(Class<?> serviceClass, String serviceName,
             Supplier<?> serviceSupplier) {
         mClassToName.put(serviceClass, serviceName);
         mNameToFactory.put(serviceName, serviceSupplier);
     }
 
-    public RavenwoodContext(String packageName, HandlerThread mainThread) {
+    public RavenwoodContext(String packageName, HandlerThread mainThread,
+            Supplier<Resources> resourcesSupplier) throws IOException {
         mPackageName = packageName;
         mMainThread = mainThread;
+        mResourcesSupplier = resourcesSupplier;
+        mFilesDir = createTempDir("files-dir");
+        mCacheDir = createTempDir("cache-dir");
 
         // Services provided by a typical shipping device
         registerService(ClipboardManager.class,
@@ -85,6 +111,11 @@
         }
     }
 
+    void cleanUp() {
+        deleteDir(mFilesDir);
+        deleteDir(mCacheDir);
+    }
+
     @Override
     public String getSystemServiceName(Class<?> serviceClass) {
         // TODO: pivot to using SystemServiceRegistry
@@ -150,6 +181,52 @@
         return Context.DEVICE_ID_DEFAULT;
     }
 
+    @Override
+    public File getFilesDir() {
+        return mFilesDir;
+    }
+
+    @Override
+    public File getCacheDir() {
+        return mCacheDir;
+    }
+
+    @Override
+    public boolean deleteFile(String name) {
+        File f = new File(name);
+        return f.delete();
+    }
+
+    @Override
+    public Resources getResources() {
+        synchronized (mLock) {
+            if (mResources == null) {
+                mResources = mResourcesSupplier.get();
+            }
+            return mResources;
+        }
+    }
+
+    @Override
+    public AssetManager getAssets() {
+        return getResources().getAssets();
+    }
+
+    @Override
+    public Theme getTheme() {
+        synchronized (mLock) {
+            if (mTheme == null) {
+                mTheme = getResources().newTheme();
+            }
+            return mTheme;
+        }
+    }
+
+    @Override
+    public String getPackageResourcePath() {
+        return new File(RAVENWOOD_RESOURCE_APK).getAbsolutePath();
+    }
+
     /**
      * Wrap the given {@link Supplier} to become memoized.
      *
@@ -175,4 +252,26 @@
     public interface ThrowingSupplier<T> {
         T get() throws Exception;
     }
+
+
+    static File createTempDir(String prefix) throws IOException {
+        // Create a temp file, delete it and recreate it as a directory.
+        final File dir = File.createTempFile(prefix + "-", "");
+        dir.delete();
+        dir.mkdirs();
+        return dir;
+    }
+
+    static void deleteDir(File dir) {
+        File[] children = dir.listFiles();
+        if (children != null) {
+            for (File child : children) {
+                if (child.isDirectory()) {
+                    deleteDir(child);
+                } else {
+                    child.delete();
+                }
+            }
+        }
+    }
 }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
new file mode 100644
index 0000000..77275c4
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
@@ -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 android.platform.test.ravenwood;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.annotations.EnabledOnRavenwood;
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+
+import org.junit.runner.Description;
+
+/**
+ * Calculates which tests need to be executed on Ravenwood.
+ */
+public class RavenwoodEnablementChecker {
+    private static final String TAG = "RavenwoodDisablementChecker";
+
+    private RavenwoodEnablementChecker() {
+    }
+
+    /**
+     * Determine if the given {@link Description} should be enabled when running on the
+     * Ravenwood test environment.
+     *
+     * A more specific method-level annotation always takes precedence over any class-level
+     * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over
+     * an {@link DisabledOnRavenwood} annotation.
+     */
+    public static boolean shouldEnableOnRavenwood(Description description,
+            boolean takeIntoAccountRunDisabledTestsFlag) {
+        // First, consult any method-level annotations
+        if (description.isTest()) {
+            Boolean result = null;
+
+            // Stopgap for http://g/ravenwood/EPAD-N5ntxM
+            if (description.getMethodName().endsWith("$noRavenwood")) {
+                result = false;
+            } else if (description.getAnnotation(EnabledOnRavenwood.class) != null) {
+                result = true;
+            } else if (description.getAnnotation(DisabledOnRavenwood.class) != null) {
+                result = false;
+            } else if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
+                result = false;
+            }
+            if (result != null) {
+                if (takeIntoAccountRunDisabledTestsFlag
+                        && RavenwoodRule.private$ravenwood().isRunningDisabledTests()) {
+                    result = !shouldStillIgnoreInProbeIgnoreMode(
+                            description.getTestClass(), description.getMethodName());
+                }
+            }
+            if (result != null) {
+                return result;
+            }
+        }
+
+        // Otherwise, consult any class-level annotations
+        return shouldRunClassOnRavenwood(description.getTestClass(),
+                takeIntoAccountRunDisabledTestsFlag);
+    }
+
+    public static boolean shouldRunClassOnRavenwood(@NonNull Class<?> testClass,
+            boolean takeIntoAccountRunDisabledTestsFlag) {
+        boolean result = true;
+        if (testClass.getAnnotation(EnabledOnRavenwood.class) != null) {
+            result = true;
+        } else if (testClass.getAnnotation(DisabledOnRavenwood.class) != null) {
+            result = false;
+        } else if (testClass.getAnnotation(IgnoreUnderRavenwood.class) != null) {
+            result = false;
+        }
+        if (!result) {
+            if (takeIntoAccountRunDisabledTestsFlag
+                    && RavenwoodRule.private$ravenwood().isRunningDisabledTests()) {
+                result = !shouldStillIgnoreInProbeIgnoreMode(testClass, null);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Check if a test should _still_ disabled even if {@code RUN_DISABLED_TESTS}
+     * is true, using {@code REALLY_DISABLED_PATTERN}.
+     *
+     * This only works on tests, not on classes.
+     */
+    static boolean shouldStillIgnoreInProbeIgnoreMode(
+            @NonNull Class<?> testClass, @Nullable String methodName) {
+        if (RavenwoodRule.private$ravenwood().getReallyDisabledPattern().pattern().isEmpty()) {
+            return false;
+        }
+
+        final var fullname = testClass.getName() + (methodName != null ? "#" + methodName : "");
+
+        System.out.println("XXX=" + fullname);
+
+        if (RavenwoodRule.private$ravenwood().getReallyDisabledPattern().matcher(fullname).find()) {
+            System.out.println("Still ignoring " + fullname);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 4357f2b..a2088fd 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,38 +16,34 @@
 
 package android.platform.test.ravenwood;
 
-import static org.junit.Assert.assertFalse;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
+import android.app.ResourcesManager;
+import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.view.DisplayAdjustments;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.os.RuntimeInit;
 import com.android.server.LocalServices;
 
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
 import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executors;
@@ -55,6 +51,7 @@
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 public class RavenwoodRuleImpl {
     private static final String MAIN_THREAD_NAME = "RavenwoodMain";
@@ -89,7 +86,7 @@
                 sPendingUncaughtException.compareAndSet(null, throwable);
             };
 
-    public static void init(RavenwoodRule rule) {
+    public static void init(RavenwoodRule rule) throws IOException {
         if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
             maybeThrowPendingUncaughtException(false);
             Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
@@ -99,10 +96,6 @@
 
         android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
         android.os.Binder.init$ravenwood();
-//        android.os.SystemProperties.init$ravenwood(
-//                rule.mSystemProperties.getValues(),
-//                rule.mSystemProperties.getKeyReadablePredicate(),
-//                rule.mSystemProperties.getKeyWritablePredicate());
         setSystemProperties(rule.mSystemProperties);
 
         ServiceManager.init$ravenwood();
@@ -119,7 +112,29 @@
             main = null;
         }
 
-        rule.mContext = new RavenwoodContext(rule.mPackageName, main);
+        // TODO This should be integrated into LoadedApk
+        final Supplier<Resources> resourcesSupplier = () -> {
+            var resApkFile = new File(RAVENWOOD_RESOURCE_APK);
+            if (!resApkFile.isFile()) {
+                resApkFile = new File(RAVENWOOD_EMPTY_RESOURCES_APK);
+            }
+            assertTrue(resApkFile.isFile());
+            final String res = resApkFile.getAbsolutePath();
+            final var emptyPaths = new String[0];
+
+            ResourcesManager.getInstance().initializeApplicationPaths(res, emptyPaths);
+
+            final var ret = ResourcesManager.getInstance().getResources(null, res,
+                    emptyPaths, emptyPaths, emptyPaths,
+                    emptyPaths, null, null,
+                    new DisplayAdjustments().getCompatibilityInfo(),
+                    RavenwoodRuleImpl.class.getClassLoader(), null);
+
+            assertNotNull(ret);
+            return ret;
+        };
+
+        rule.mContext = new RavenwoodContext(rule.mPackageName, main, resourcesSupplier);
         rule.mInstrumentation = new Instrumentation();
         rule.mInstrumentation.basicInit(rule.mContext);
         InstrumentationRegistry.registerInstance(rule.mInstrumentation, Bundle.EMPTY);
@@ -145,6 +160,9 @@
 
         InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
         rule.mInstrumentation = null;
+        if (rule.mContext != null) {
+            ((RavenwoodContext) rule.mContext).cleanUp();
+        }
         rule.mContext = null;
 
         if (rule.mProvideMainThread) {
@@ -161,6 +179,8 @@
         android.os.Binder.reset$ravenwood();
         android.os.Process.reset$ravenwood();
 
+        ResourcesManager.setInstance(null); // Better structure needed.
+
         if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
             maybeThrowPendingUncaughtException(true);
         }
@@ -205,106 +225,6 @@
         }
     }
 
-    public static void validate(Statement base, Description description,
-            boolean enableOptionalValidation) {
-        validateTestRunner(base, description, enableOptionalValidation);
-        validateTestAnnotations(base, description, enableOptionalValidation);
-    }
-
-    private static void validateTestRunner(Statement base, Description description,
-            boolean shouldFail) {
-        final var testClass = description.getTestClass();
-        final var runWith = testClass.getAnnotation(RunWith.class);
-        if (runWith == null) {
-            return;
-        }
-
-        // Due to build dependencies, we can't directly refer to androidx classes here,
-        // so just check the class name instead.
-        if (runWith.value().getCanonicalName().equals("androidx.test.runner.AndroidJUnit4")) {
-            var message = "Test " + testClass.getCanonicalName() + " uses deprecated"
-                    + " test runner androidx.test.runner.AndroidJUnit4."
-                    + " Switch to androidx.test.ext.junit.runners.AndroidJUnit4.";
-            if (shouldFail) {
-                Assert.fail(message);
-            } else {
-                System.err.println("Warning: " + message);
-            }
-        }
-    }
-
-    /**
-     * @return if a method has any of annotations.
-     */
-    private static boolean hasAnyAnnotations(Method m, Class<? extends Annotation>... annotations) {
-        for (var anno : annotations) {
-            if (m.getAnnotation(anno) != null) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private static void validateTestAnnotations(Statement base, Description description,
-            boolean enableOptionalValidation) {
-        final var testClass = description.getTestClass();
-
-        final var message = new StringBuilder();
-
-        boolean hasErrors = false;
-        for (Method m : collectMethods(testClass)) {
-            if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("test")) {
-                if (!hasAnyAnnotations(m, Test.class, Before.class, After.class,
-                        BeforeClass.class, AfterClass.class)) {
-                    message.append("\nMethod " + m.getName() + "() doesn't have @Test");
-                    hasErrors = true;
-                }
-            }
-            if ("setUp".equals(m.getName())) {
-                if (!hasAnyAnnotations(m, Before.class)) {
-                    message.append("\nMethod " + m.getName() + "() doesn't have @Before");
-                    hasErrors = true;
-                }
-                if (!Modifier.isPublic(m.getModifiers())) {
-                    message.append("\nMethod " + m.getName() + "() must be public");
-                    hasErrors = true;
-                }
-            }
-            if ("tearDown".equals(m.getName())) {
-                if (!hasAnyAnnotations(m, After.class)) {
-                    message.append("\nMethod " + m.getName() + "() doesn't have @After");
-                    hasErrors = true;
-                }
-                if (!Modifier.isPublic(m.getModifiers())) {
-                    message.append("\nMethod " + m.getName() + "() must be public");
-                    hasErrors = true;
-                }
-            }
-        }
-        assertFalse("Problem(s) detected in class " + testClass.getCanonicalName() + ":"
-                + message, hasErrors);
-    }
-
-    /**
-     * Collect all (public or private or any) methods in a class, including inherited methods.
-     */
-    private static List<Method> collectMethods(Class<?> clazz) {
-        var ret = new ArrayList<Method>();
-        collectMethods(clazz, ret);
-        return ret;
-    }
-
-    private static void collectMethods(Class<?> clazz, List<Method> result) {
-        // Class.getMethods() only return public methods, so we need to use getDeclaredMethods()
-        // instead, and recurse.
-        for (var m : clazz.getDeclaredMethods()) {
-            result.add(m);
-        }
-        if (clazz.getSuperclass() != null) {
-            collectMethods(clazz.getSuperclass(), result);
-        }
-    }
-
     /**
      * Set the current configuration to the actual SystemProperties.
      */
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
new file mode 100644
index 0000000..631f68f
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -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 android.platform.test.ravenwood;
+
+import android.util.Log;
+
+import org.junit.runner.Description;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creats a "stats" CSV file containing the test results.
+ *
+ * The output file is created as `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_[TIMESTAMP].csv`.
+ * A symlink to the latest result will be created as
+ * `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_latest.csv`.
+ */
+public class RavenwoodTestStats {
+    private static final String TAG = "RavenwoodTestStats";
+    private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped";
+
+    public enum Result {
+        Passed,
+        Failed,
+        Skipped,
+    }
+
+    private final File mOutputFile;
+    private final PrintWriter mOutputWriter;
+    private final String mTestModuleName;
+
+    public final Map<Description, Map<Description, Result>> mStats = new HashMap<>();
+
+    /** Ctor */
+    public RavenwoodTestStats() {
+        mTestModuleName = guessTestModuleName();
+
+        var basename = "Ravenwood-stats_" + mTestModuleName + "_";
+
+        // Get the current time
+        LocalDateTime now = LocalDateTime.now();
+        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss");
+
+        var tmpdir = System.getProperty("java.io.tmpdir");
+        mOutputFile = new File(tmpdir, basename + now.format(fmt) + ".csv");
+
+        try {
+            mOutputWriter = new PrintWriter(mOutputFile);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to crete logfile. File=" + mOutputFile, e);
+        }
+
+        // Crete the "latest" symlink.
+        Path symlink = Paths.get(tmpdir, basename + "latest.csv");
+        try {
+            if (Files.exists(symlink)) {
+                Files.delete(symlink);
+            }
+            Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName()));
+
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to crete logfile. File=" + mOutputFile, e);
+        }
+
+        Log.i(TAG, "Test result stats file: " + mOutputFile);
+
+        // Print the header.
+        mOutputWriter.println(HEADER);
+        mOutputWriter.flush();
+    }
+
+    private String guessTestModuleName() {
+        // Assume the current directory name is the test module name.
+        File cwd;
+        try {
+            cwd = new File(".").getCanonicalFile();
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to get the current directory", e);
+        }
+        return cwd.getName();
+    }
+
+    private void addResult(Description classDescription, Description methodDescription,
+            Result result) {
+        mStats.compute(classDescription, (classDesc, value) -> {
+            if (value == null) {
+                value = new HashMap<>();
+            }
+            value.put(methodDescription, result);
+            return value;
+        });
+    }
+
+    public void onClassSkipped(Description classDescription) {
+        addResult(classDescription, Description.EMPTY, Result.Skipped);
+        onClassFinished(classDescription);
+    }
+
+    public void onTestFinished(Description classDescription, Description testDescription,
+            Result result) {
+        addResult(classDescription, testDescription, result);
+    }
+
+    public void onClassFinished(Description classDescription) {
+        int passed = 0;
+        int skipped = 0;
+        int failed = 0;
+        for (var e : mStats.get(classDescription).values()) {
+            switch (e) {
+                case Passed: passed++; break;
+                case Skipped: skipped++; break;
+                case Failed: failed++; break;
+            }
+        }
+
+        var testClass = extractTestClass(classDescription);
+
+        mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n",
+                mTestModuleName, (testClass == null ? "?" : testClass.getCanonicalName()),
+                classDescription, passed, failed, skipped);
+        mOutputWriter.flush();
+    }
+
+    /**
+     * Try to extract the class from a description, which is needed because
+     * ParameterizedAndroidJunit4's description doesn't contain a class.
+     */
+    private Class<?> extractTestClass(Description desc) {
+        if (desc.getTestClass() != null) {
+            return desc.getTestClass();
+        }
+        // Look into the children.
+        for (var child : desc.getChildren()) {
+            var fromChild = extractTestClass(child);
+            if (fromChild != null) {
+                return fromChild;
+            }
+        }
+        return null;
+    }
+}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java b/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
deleted file mode 100644
index 2fb8074..0000000
--- a/ravenwood/junit-src/android/platform/test/annotations/DisabledOnNonRavenwood.java
+++ /dev/null
@@ -1,51 +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.platform.test.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Tests marked with this annotation are only executed when running on Ravenwood, but not
- * on a device.
- *
- * This is basically equivalent to the opposite of {@link DisabledOnRavenwood}, but in order to
- * avoid complex structure, and there's no equivalent to the opposite {@link EnabledOnRavenwood},
- * which means if a test class has this annotation, you can't negate it in subclasses or
- * on a per-method basis.
- *
- * THIS ANNOTATION CANNOT BE ADDED TO CLASSES AT THIS PONINT.
- * See {@link com.android.ravenwoodtest.bivalenttest.RavenwoodClassRuleRavenwoodOnlyTest}
- * for the reason.
- *
- * The {@code RAVENWOOD_RUN_DISABLED_TESTS} environmental variable won't work because it won't be
- * propagated to the device. (We may support it in the future, possibly using a debug. sysprop.)
- *
- * @hide
- */
-@Inherited
-@Target({ElementType.METHOD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface DisabledOnNonRavenwood {
-    /**
-     * General free-form description of why this test is being ignored.
-     */
-    String reason() default "";
-}
diff --git a/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java
new file mode 100644
index 0000000..a84f16f
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/annotations/NoRavenizer.java
@@ -0,0 +1,36 @@
+/*
+ * 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.platform.test.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Disable the ravenizer preprocessor for a class. This should be only used for testing
+ * ravenizer itself, or to workaround issues with the preprocessor. A test class probably won't run
+ * properly if it's not preprocessed.
+ *
+ * @hide
+ */
+@Inherited
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NoRavenizer {
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
new file mode 100644
index 0000000..7d99166
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -0,0 +1,412 @@
+/*
+ * 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.platform.test.ravenwood;
+
+import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import android.util.Log;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+import com.android.ravenwood.common.SneakyThrow;
+
+import org.junit.Assume;
+import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.Filterable;
+import org.junit.runner.manipulation.InvalidOrderingException;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.manipulation.Orderable;
+import org.junit.runner.manipulation.Orderer;
+import org.junit.runner.manipulation.Sortable;
+import org.junit.runner.manipulation.Sorter;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.RunnerBuilder;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A test runner used for Ravenwood.
+ *
+ * It will delegate to another runner specified with {@link InnerRunner}
+ * (default = {@link BlockJUnit4ClassRunner}) with the following features.
+ * - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before
+ *   the inner runner gets a chance to run. This can be used to initialize stuff used by the
+ *   inner runner.
+ * - Add hook points, which are handed by RavenwoodAwareTestRunnerHook, with help from
+ *   the four test rules such as {@link #sImplicitClassMinRule}, which are also injected by
+ *   the ravenizer tool.
+ *
+ * We use this runner to:
+ * - Initialize the bare minimum environmnet just to be enough to make the actual test runners
+ *   happy.
+ * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
+ *
+ * This class is built such that it can also be used on a real device, but in that case
+ * it will basically just delegate to the inner wrapper, and won't do anything special.
+ * (no hooks, etc.)
+ */
+public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
+    private static final String TAG = "RavenwoodAwareTestRunner";
+
+    @Inherited
+    @Target({TYPE})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface InnerRunner {
+        Class<? extends Runner> value();
+    }
+
+    /**
+     * An annotation similar to JUnit's BeforeClass, but this gets executed before
+     * the inner runner is instantiated, and only on Ravenwood.
+     * It can be used to initialize what's needed by the inner runner.
+     */
+    @Target({METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface RavenwoodTestRunnerInitializing {
+    }
+
+    /** Scope of a hook. */
+    public enum Scope {
+        Runner,
+        Class,
+        Instance,
+    }
+
+    /** Order of a hook. */
+    public enum Order {
+        First,
+        Last,
+    }
+
+    // The following four rule instances will be injected to tests by the Ravenizer tool.
+
+    public static final TestRule sImplicitClassMinRule = (base, description) ->
+            getCurrentRunner().updateStatement(base, description, Scope.Class, Order.First);
+
+    public static final TestRule sImplicitClassMaxRule = (base, description) ->
+            getCurrentRunner().updateStatement(base, description, Scope.Class, Order.Last);
+
+    public static final TestRule sImplicitInstMinRule = (base, description) ->
+            getCurrentRunner().updateStatement(base, description, Scope.Instance, Order.First);
+
+    public static final TestRule sImplicitInstMaxRule = (base, description) ->
+            getCurrentRunner().updateStatement(base, description, Scope.Instance, Order.Last);
+
+    public static final String IMPLICIT_CLASS_MIN_RULE_NAME = "sImplicitClassMinRule";
+    public static final String IMPLICIT_CLASS_MAX_RULE_NAME = "sImplicitClassMaxRule";
+    public static final String IMPLICIT_INST_MIN_RULE_NAME = "sImplicitInstMinRule";
+    public static final String IMPLICIT_INST_MAX_RULE_NAME = "sImplicitInstMaxRule";
+
+    /** Keeps track of the runner on the current thread. */
+    private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>();
+
+    private static RavenwoodAwareTestRunner getCurrentRunner() {
+        var runner = sCurrentRunner.get();
+        if (runner == null) {
+            throw new RuntimeException("Current test runner not set!");
+        }
+        return runner;
+    }
+
+    private TestClass mTestClass = null;
+    private Runner mRealRunner = null;
+    private Description mDescription = null;
+    private Throwable mExceptionInConstructor = null;
+
+    /** Simple logging method. */
+    private void log(String message) {
+        RavenwoodCommonUtils.log(TAG, "[" + getTestClass().getJavaClass() + "  @" + this + "] "
+                + message);
+    }
+
+    private Error logAndFail(String message, Throwable innerException) {
+        log(message);
+        log("    Exception=" + innerException);
+        throw new AssertionError(message, innerException);
+    }
+
+    public TestClass getTestClass() {
+        return mTestClass;
+    }
+
+    /**
+     * Constructor.
+     */
+    public RavenwoodAwareTestRunner(Class<?> testClass) {
+        try {
+            mTestClass = new TestClass(testClass);
+
+            /*
+             * If the class has @DisabledOnRavenwood, then we'll delegate to
+             * ClassSkippingTestRunner, which simply skips it.
+             */
+            if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood(
+                    mTestClass.getJavaClass())) {
+                mRealRunner = new ClassSkippingTestRunner(mTestClass);
+                mDescription = mRealRunner.getDescription();
+                return;
+            }
+
+            // Find the real runner.
+            final Class<? extends Runner> realRunnerClass;
+            final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class);
+            if (innerRunnerAnnotation != null) {
+                realRunnerClass = innerRunnerAnnotation.value();
+            } else {
+                // Default runner.
+                realRunnerClass = BlockJUnit4ClassRunner.class;
+            }
+
+            onRunnerInitializing();
+
+            try {
+                log("Initializing the inner runner: " + realRunnerClass);
+
+                mRealRunner = instantiateRealRunner(realRunnerClass, testClass);
+                mDescription = mRealRunner.getDescription();
+
+            } catch (InstantiationException | IllegalAccessException
+                     | InvocationTargetException | NoSuchMethodException e) {
+                throw logAndFail("Failed to instantiate " + realRunnerClass, e);
+            }
+        } catch (Throwable th) {
+            // If we throw in the constructor, Tradefed may not report it and just ignore the class,
+            // so record it and throw it when the test actually started.
+            log("Fatal: Exception detected in constructor: " + th.getMessage() + "\n"
+                    + Log.getStackTraceString(th));
+            mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
+                    th);
+            mDescription = Description.createTestDescription(testClass, "Constructor");
+
+            // This is for testing if tradefed is fixed.
+            if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) {
+                throw th;
+            }
+        }
+    }
+
+    private static Runner instantiateRealRunner(
+            Class<? extends Runner> realRunnerClass,
+            Class<?> testClass)
+            throws NoSuchMethodException, InvocationTargetException, InstantiationException,
+            IllegalAccessException {
+        try {
+            return realRunnerClass.getConstructor(Class.class).newInstance(testClass);
+        } catch (NoSuchMethodException e) {
+            var runnerBuilder = new AllDefaultPossibilitiesBuilder();
+            return realRunnerClass.getConstructor(Class.class,
+                    RunnerBuilder.class).newInstance(testClass, runnerBuilder);
+        }
+    }
+
+    /**
+     * Run the bare minimum setup to initialize the wrapped runner.
+     */
+    // This method is called by the ctor, so never make it virtual.
+    private void onRunnerInitializing() {
+        if (!isOnRavenwood()) {
+            return;
+        }
+
+        log("onRunnerInitializing");
+
+        RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
+
+        // Hook point to allow more customization.
+        runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
+    }
+
+    private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass,
+            Object instance) {
+        if (!isOnRavenwood()) {
+            return;
+        }
+        log("runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+
+        for (var method : getTestClass().getAnnotatedMethods(annotationClass)) {
+            ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
+
+            var methodDesc = method.getDeclaringClass().getName() + "."
+                    + method.getMethod().toString();
+            try {
+                method.getMethod().invoke(instance);
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                throw logAndFail("Caught exception while running method " + methodDesc, e);
+            }
+        }
+    }
+
+    @Override
+    public Description getDescription() {
+        return mDescription;
+    }
+
+    @Override
+    public void run(RunNotifier notifier) {
+        if (mRealRunner instanceof ClassSkippingTestRunner) {
+            mRealRunner.run(notifier);
+            RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription());
+            return;
+        }
+
+        if (maybeReportExceptionFromConstructor(notifier)) {
+            return;
+        }
+
+        sCurrentRunner.set(this);
+        try {
+            runWithHooks(getDescription(), Scope.Runner, Order.First,
+                    () -> mRealRunner.run(notifier));
+        } finally {
+            sCurrentRunner.remove();
+        }
+    }
+
+    /** Throw the exception detected in the constructor, if any. */
+    private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) {
+        if (mExceptionInConstructor == null) {
+            return false;
+        }
+        notifier.fireTestStarted(mDescription);
+        notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor));
+        notifier.fireTestFinished(mDescription);
+
+        return true;
+    }
+
+    @Override
+    public void filter(Filter filter) throws NoTestsRemainException {
+        if (mRealRunner instanceof Filterable r) {
+            r.filter(filter);
+        }
+    }
+
+    @Override
+    public void order(Orderer orderer) throws InvalidOrderingException {
+        if (mRealRunner instanceof Orderable r) {
+            r.order(orderer);
+        }
+    }
+
+    @Override
+    public void sort(Sorter sorter) {
+        if (mRealRunner instanceof Sortable r) {
+            r.sort(sorter);
+        }
+    }
+
+    private Statement updateStatement(Statement base, Description description, Scope scope,
+            Order order) {
+        if (!isOnRavenwood()) {
+            return base;
+        }
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                runWithHooks(description, scope, order, base);
+            }
+        };
+    }
+
+    private void runWithHooks(Description description, Scope scope, Order order, Runnable r) {
+        runWithHooks(description, scope, order, new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                r.run();
+            }
+        });
+    }
+
+    private void runWithHooks(Description description, Scope scope, Order order, Statement s) {
+        if (isOnRavenwood()) {
+            Assume.assumeTrue(
+                    RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order));
+        }
+        try {
+            s.evaluate();
+            if (isOnRavenwood()) {
+                RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, null);
+            }
+        } catch (Throwable t) {
+            boolean shouldThrow = true;
+            if (isOnRavenwood()) {
+                shouldThrow = RavenwoodAwareTestRunnerHook.onAfter(
+                        this, description, scope, order, t);
+            }
+            if (shouldThrow) {
+                SneakyThrow.sneakyThrow(t);
+            }
+        }
+    }
+
+    /**
+     * A runner that simply skips a class. It still has to support {@link Filterable}
+     * because otherwise the result still says "SKIPPED" even when it's not included in the
+     * filter.
+     */
+    private static class ClassSkippingTestRunner extends Runner implements Filterable {
+        private final TestClass mTestClass;
+        private final Description mDescription;
+        private boolean mFilteredOut;
+
+        ClassSkippingTestRunner(TestClass testClass) {
+            mTestClass = testClass;
+            mDescription = Description.createTestDescription(
+                    testClass.getJavaClass(), testClass.getJavaClass().getSimpleName());
+            mFilteredOut = false;
+        }
+
+        @Override
+        public Description getDescription() {
+            return mDescription;
+        }
+
+        @Override
+        public void run(RunNotifier notifier) {
+            if (mFilteredOut) {
+                return;
+            }
+            notifier.fireTestSuiteStarted(mDescription);
+            notifier.fireTestIgnored(mDescription);
+            notifier.fireTestSuiteFinished(mDescription);
+        }
+
+        @Override
+        public void filter(Filter filter) throws NoTestsRemainException {
+            if (filter.shouldRun(mDescription)) {
+                mFilteredOut = false;
+            } else {
+                throw new NoTestsRemainException();
+            }
+        }
+    }
+}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
index f4b7ec36..85297fe 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
@@ -16,42 +16,20 @@
 
 package android.platform.test.ravenwood;
 
-import static android.platform.test.ravenwood.RavenwoodRule.ENABLE_PROBE_IGNORED;
-import static android.platform.test.ravenwood.RavenwoodRule.IS_ON_RAVENWOOD;
-import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnDevice;
-import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnRavenwood;
-import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInProbeIgnoreMode;
-
-import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.annotations.EnabledOnRavenwood;
-
-import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
 /**
- * {@code @ClassRule} that respects Ravenwood-specific class annotations. This rule has no effect
- * when tests are run on non-Ravenwood test environments.
+ * No longer needed.
  *
- * By default, all tests are executed on Ravenwood, but annotations such as
- * {@link DisabledOnRavenwood} and {@link EnabledOnRavenwood} can be used at both the method
- * and class level to "ignore" tests that may not be ready.
+ * @deprecated this class used to be used to handle the class level annotation, which
+ * is now done by the test runner, so this class is not needed.
  */
+@Deprecated
 public class RavenwoodClassRule implements TestRule {
     @Override
     public Statement apply(Statement base, Description description) {
-        if (!IS_ON_RAVENWOOD) {
-            // This should be "Assume", not Assert, but if we use assume here, the device side
-            // test runner would complain.
-            // See the TODO comment in RavenwoodClassRuleRavenwoodOnlyTest.
-            Assert.assertTrue(shouldEnableOnDevice(description));
-        } else if (ENABLE_PROBE_IGNORED) {
-            Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
-        } else {
-            Assume.assumeTrue(shouldEnableOnRavenwood(description));
-        }
         return base;
     }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 825c91a..d569896 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -20,18 +20,16 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.UserHandle.SYSTEM;
 
-import static org.junit.Assert.fail;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.log;
 
+import android.annotation.Nullable;
 import android.app.Instrumentation;
 import android.content.Context;
-import android.platform.test.annotations.DisabledOnNonRavenwood;
 import android.platform.test.annotations.DisabledOnRavenwood;
 import android.platform.test.annotations.EnabledOnRavenwood;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
 
 import com.android.ravenwood.common.RavenwoodCommonUtils;
 
-import org.junit.Assume;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -56,16 +54,18 @@
  * before a test class is fully initialized.
  */
 public class RavenwoodRule implements TestRule {
+    private static final String TAG = "RavenwoodRule";
+
     static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
 
     /**
-     * When probing is enabled, all tests will be unconditionally run on Ravenwood to detect
-     * cases where a test is able to pass despite being marked as {@code IgnoreUnderRavenwood}.
+     * When this flag is enabled, all tests will be unconditionally run on Ravenwood to detect
+     * cases where a test is able to pass despite being marked as {@link DisabledOnRavenwood}.
      *
      * This is typically helpful for internal maintainers discovering tests that had previously
      * been ignored, but now have enough Ravenwood-supported functionality to be enabled.
      */
-    static final boolean ENABLE_PROBE_IGNORED = "1".equals(
+    private static final boolean RUN_DISABLED_TESTS = "1".equals(
             System.getenv("RAVENWOOD_RUN_DISABLED_TESTS"));
 
     /**
@@ -90,23 +90,17 @@
      *
      * Because we use a regex-find, setting "." would disable all tests.
      */
-    private static final Pattern REALLY_DISABLE_PATTERN = Pattern.compile(
-            Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLE"), ""));
+    private static final Pattern REALLY_DISABLED_PATTERN = Pattern.compile(
+            Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLED"), ""));
 
-    private static final boolean ENABLE_REALLY_DISABLE_PATTERN =
-            !REALLY_DISABLE_PATTERN.pattern().isEmpty();
-
-    /**
-     * If true, enable optional validation on running tests.
-     */
-    private static final boolean ENABLE_OPTIONAL_VALIDATION = "1".equals(
-            System.getenv("RAVENWOOD_OPTIONAL_VALIDATION"));
+    private static final boolean HAS_REALLY_DISABLE_PATTERN =
+            !REALLY_DISABLED_PATTERN.pattern().isEmpty();
 
     static {
-        if (ENABLE_PROBE_IGNORED) {
-            System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
-            if (ENABLE_REALLY_DISABLE_PATTERN) {
-                System.out.println("$RAVENWOOD_REALLY_DISABLE=" + REALLY_DISABLE_PATTERN.pattern());
+        if (RUN_DISABLED_TESTS) {
+            log(TAG, "$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
+            if (HAS_REALLY_DISABLE_PATTERN) {
+                log(TAG, "$RAVENWOOD_REALLY_DISABLED=" + REALLY_DISABLED_PATTERN.pattern());
             }
         }
     }
@@ -273,117 +267,18 @@
                 "Instrumentation is only available during @Test execution");
     }
 
-    static boolean shouldEnableOnDevice(Description description) {
-        if (description.isTest()) {
-            if (description.getAnnotation(DisabledOnNonRavenwood.class) != null) {
-                return false;
-            }
-        }
-        final var clazz = description.getTestClass();
-        if (clazz != null) {
-            if (clazz.getAnnotation(DisabledOnNonRavenwood.class) != null) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Determine if the given {@link Description} should be enabled when running on the
-     * Ravenwood test environment.
-     *
-     * A more specific method-level annotation always takes precedence over any class-level
-     * annotation, and an {@link EnabledOnRavenwood} annotation always takes precedence over
-     * an {@link DisabledOnRavenwood} annotation.
-     */
-    static boolean shouldEnableOnRavenwood(Description description) {
-        // First, consult any method-level annotations
-        if (description.isTest()) {
-            // Stopgap for http://g/ravenwood/EPAD-N5ntxM
-            if (description.getMethodName().endsWith("$noRavenwood")) {
-                return false;
-            }
-            if (description.getAnnotation(EnabledOnRavenwood.class) != null) {
-                return true;
-            }
-            if (description.getAnnotation(DisabledOnRavenwood.class) != null) {
-                return false;
-            }
-            if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
-                return false;
-            }
-        }
-
-        // Otherwise, consult any class-level annotations
-        final var clazz = description.getTestClass();
-        if (clazz != null) {
-            if (description.getTestClass().getAnnotation(EnabledOnRavenwood.class) != null) {
-                return true;
-            }
-            if (description.getTestClass().getAnnotation(DisabledOnRavenwood.class) != null) {
-                return false;
-            }
-            if (description.getTestClass().getAnnotation(IgnoreUnderRavenwood.class) != null) {
-                return false;
-            }
-        }
-
-        // When no annotations have been requested, assume test should be included
-        return true;
-    }
-
-    static boolean shouldStillIgnoreInProbeIgnoreMode(Description description) {
-        if (!ENABLE_REALLY_DISABLE_PATTERN) {
-            return false;
-        }
-
-        final var fullname = description.getTestClass().getName()
-                + (description.isTest() ? "#" + description.getMethodName() : "");
-
-        if (REALLY_DISABLE_PATTERN.matcher(fullname).find()) {
-            System.out.println("Still ignoring " + fullname);
-            return true;
-        }
-        return false;
-    }
 
     @Override
     public Statement apply(Statement base, Description description) {
-        // No special treatment when running outside Ravenwood; run tests as-is
-        if (!IS_ON_RAVENWOOD) {
-            Assume.assumeTrue(shouldEnableOnDevice(description));
-            return base;
-        }
-
-        if (ENABLE_PROBE_IGNORED) {
-            return applyProbeIgnored(base, description);
-        } else {
-            return applyDefault(base, description);
-        }
-    }
-
-    private void commonPrologue(Statement base, Description description) {
-        RavenwoodRuleImpl.logTestRunner("started", description);
-        RavenwoodRuleImpl.validate(base, description, ENABLE_OPTIONAL_VALIDATION);
-        RavenwoodRuleImpl.init(RavenwoodRule.this);
-    }
-
-    /**
-     * Run the given {@link Statement} with no special treatment.
-     */
-    private Statement applyDefault(Statement base, Description description) {
+        // TODO: Here, we're calling init() / reset() once for each rule.
+        // That means if a test class has multiple rules -- even if they refer to the same
+        // rule instance -- we're calling them multiple times. We need to fix it.
         return new Statement() {
             @Override
             public void evaluate() throws Throwable {
-                Assume.assumeTrue(shouldEnableOnRavenwood(description));
-
-                commonPrologue(base, description);
+                RavenwoodRuleImpl.init(RavenwoodRule.this);
                 try {
                     base.evaluate();
-                    RavenwoodRuleImpl.logTestRunner("finished", description);
-                } catch (Throwable t) {
-                    RavenwoodRuleImpl.logTestRunner("failed", description);
-                    throw t;
                 } finally {
                     RavenwoodRuleImpl.reset(RavenwoodRule.this);
                 }
@@ -392,44 +287,6 @@
     }
 
     /**
-     * Run the given {@link Statement} with probing enabled. All tests will be unconditionally
-     * run on Ravenwood to detect cases where a test is able to pass despite being marked as
-     * {@code IgnoreUnderRavenwood}.
-     */
-    private Statement applyProbeIgnored(Statement base, Description description) {
-        return new Statement() {
-            @Override
-            public void evaluate() throws Throwable {
-                Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
-
-                commonPrologue(base, description);
-                try {
-                    base.evaluate();
-                } catch (Throwable t) {
-                    // If the test isn't included, eat the exception and report the
-                    // assumption failure that test authors expect; otherwise throw
-                    Assume.assumeTrue(shouldEnableOnRavenwood(description));
-                    throw t;
-                } finally {
-                    RavenwoodRuleImpl.logTestRunner("finished", description);
-                    RavenwoodRuleImpl.reset(RavenwoodRule.this);
-                }
-
-                if (!shouldEnableOnRavenwood(description)) {
-                    fail("Test wasn't included under Ravenwood, but it actually "
-                            + "passed under Ravenwood; consider updating annotations");
-                }
-            }
-        };
-    }
-
-    public static class _$RavenwoodPrivate {
-        public static boolean isOptionalValidationEnabled() {
-            return ENABLE_OPTIONAL_VALIDATION;
-        }
-    }
-
-    /**
      * Returns the "real" result from {@link System#currentTimeMillis()}.
      *
      * Currently, it's the same thing as calling {@link System#currentTimeMillis()},
@@ -439,4 +296,47 @@
     public long realCurrentTimeMillis() {
         return System.currentTimeMillis();
     }
+
+    // Below are internal to ravenwood. Don't use them from normal tests...
+
+    public static class RavenwoodPrivate {
+        private RavenwoodPrivate() {
+        }
+
+        private volatile Boolean mRunDisabledTestsOverride = null;
+
+        private volatile Pattern mReallyDisabledPattern = null;
+
+        public boolean isRunningDisabledTests() {
+            if (mRunDisabledTestsOverride != null) {
+                return mRunDisabledTestsOverride;
+            }
+            return RUN_DISABLED_TESTS;
+        }
+
+        public Pattern getReallyDisabledPattern() {
+            if (mReallyDisabledPattern != null) {
+                return mReallyDisabledPattern;
+            }
+            return REALLY_DISABLED_PATTERN;
+        }
+
+        public void overrideRunDisabledTest(boolean runDisabledTests,
+                @Nullable String reallyDisabledPattern) {
+            mRunDisabledTestsOverride = runDisabledTests;
+            mReallyDisabledPattern =
+                    reallyDisabledPattern == null ? null : Pattern.compile(reallyDisabledPattern);
+        }
+
+        public void resetRunDisabledTest() {
+            mRunDisabledTestsOverride = null;
+            mReallyDisabledPattern = null;
+        }
+    }
+
+    private static final RavenwoodPrivate sRavenwoodPrivate = new  RavenwoodPrivate();
+
+    public static RavenwoodPrivate private$ravenwood() {
+        return sRavenwoodPrivate;
+    }
 }
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 5f1b0c2..ef8f584 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -48,11 +48,13 @@
         switch (key) {
             case "gsm.version.baseband":
             case "no.such.thing":
+            case "qemu.sf.lcd_density":
             case "ro.bootloader":
             case "ro.debuggable":
             case "ro.hardware":
             case "ro.hw_timeout_multiplier":
             case "ro.odm.build.media_performance_class":
+            case "ro.sf.lcd_density":
             case "ro.treble.enabled":
             case "ro.vndk.version":
                 return true;
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
new file mode 100644
index 0000000..1e4889c
--- /dev/null
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -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 android.platform.test.ravenwood;
+
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order;
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope;
+
+import org.junit.runner.Description;
+import org.junit.runner.Runner;
+import org.junit.runners.model.TestClass;
+
+/**
+ * Provide hook points created by {@link RavenwoodAwareTestRunner}. This is a version
+ * that's used on a device side test.
+ *
+ * All methods are no-op in real device tests.
+ *
+ * TODO: Use some kind of factory to provide different implementation for the device test
+ * and the ravenwood test.
+ */
+public class RavenwoodAwareTestRunnerHook {
+    private RavenwoodAwareTestRunnerHook() {
+    }
+
+    /**
+     * Called when a runner starts, before the inner runner gets a chance to run.
+     */
+    public static void onRunnerInitializing(Runner runner, TestClass testClass) {
+    }
+
+    /**
+     * Called when a whole test class is skipped.
+     */
+    public static void onClassSkipped(Description description) {
+    }
+
+    /**
+     * Called before a test / class.
+     *
+     * Return false if it should be skipped.
+     */
+    public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
+            Scope scope, Order order) {
+        return true;
+    }
+
+    /**
+     * Called after a test / class.
+     *
+     * Return false if the exception should be ignored.
+     */
+    public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
+            Scope scope, Order order, Throwable th) {
+        return true;
+    }
+
+    public static boolean shouldRunClassOnRavenwood(Class<?> clazz) {
+        return true;
+    }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index 483b98a..a470626 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -17,7 +17,6 @@
 package android.platform.test.ravenwood;
 
 import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
 
 public class RavenwoodRuleImpl {
     public static void init(RavenwoodRule rule) {
@@ -32,10 +31,6 @@
         // No-op when running on a real device
     }
 
-    public static void validate(Statement base, Description description,
-            boolean enableOptionalValidation) {
-    }
-
     public static long realCurrentTimeMillis() {
         return System.currentTimeMillis();
     }
diff --git a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
deleted file mode 100644
index b477117..0000000
--- a/ravenwood/minimum-test/test/com/android/ravenwood/RavenwoodMinimumTest.java
+++ /dev/null
@@ -1,50 +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.ravenwood;
-
-import android.platform.test.annotations.IgnoreUnderRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodMinimumTest {
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProcessApp()
-            .build();
-
-    @Test
-    public void testSimple() {
-        Assert.assertTrue(android.os.Process.isApplicationUid(android.os.Process.myUid()));
-    }
-
-    @Test
-    @IgnoreUnderRavenwood
-    public void testIgnored() {
-        throw new RuntimeException("Shouldn't be executed under ravenwood");
-    }
-
-    @Test
-    public void testIgnored$noRavenwood() {
-        throw new RuntimeException("Shouldn't be executed under ravenwood");
-    }
-}
diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
new file mode 100644
index 0000000..30abaa2
--- /dev/null
+++ b/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ravenwoodtest;
+
+import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodMinimumTest {
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProcessApp()
+            .build();
+
+    @Test
+    public void testSimple() {
+        Assert.assertTrue(android.os.Process.isApplicationUid(android.os.Process.myUid()));
+    }
+
+    @Test
+    @IgnoreUnderRavenwood
+    public void testIgnored() {
+        throw new RuntimeException("Shouldn't be executed under ravenwood");
+    }
+
+    @Test
+    public void testIgnored$noRavenwood() {
+        throw new RuntimeException("Shouldn't be executed under ravenwood");
+    }
+}
diff --git a/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java b/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java
deleted file mode 100644
index 1029ed2..0000000
--- a/ravenwood/resapk_test/test/com/android/ravenwood/resapk_test/RavenwoodResApkTest.java
+++ /dev/null
@@ -1,51 +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.ravenwood.resapk_test;
-
-
-import static junit.framework.TestCase.assertTrue;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.ravenwood.common.RavenwoodCommonUtils;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodResApkTest {
-    /**
-     * Ensure the file "ravenwood-res.apk" exists.
-     * TODO Check the content of it, once Ravenwood supports resources. The file should
-     * be a copy of RavenwoodResApkTest-apk.apk
-     */
-    @Test
-    public void testResApkExists() {
-        var file = "ravenwood-res-apks/ravenwood-res.apk";
-
-        assertTrue(new File(file).exists());
-    }
-
-    @Test
-    public void testFrameworkResExists() {
-        var file = "ravenwood-data/framework-res.apk";
-
-        assertTrue(new File(
-                RavenwoodCommonUtils.getRavenwoodRuntimePath() + "/" + file).exists());
-    }
-}
diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
new file mode 100644
index 0000000..e547114
--- /dev/null
+++ b/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java
@@ -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.ravenwoodtest.resapk_test;
+
+
+import static junit.framework.TestCase.assertTrue;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodResApkTest {
+    /**
+     * Ensure the file "ravenwood-res.apk" exists.
+     * TODO Check the content of it, once Ravenwood supports resources. The file should
+     * be a copy of RavenwoodResApkTest-apk.apk
+     */
+    @Test
+    public void testResApkExists() {
+        var file = "ravenwood-res-apks/ravenwood-res.apk";
+
+        assertTrue(new File(file).exists());
+    }
+
+    @Test
+    public void testFrameworkResExists() {
+        var file = "ravenwood-data/framework-res.apk";
+
+        assertTrue(new File(
+                RavenwoodCommonUtils.getRavenwoodRuntimePath() + "/" + file).exists());
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
index 0238baa..02153a7 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
@@ -38,7 +38,6 @@
      */
     public abstract void setFdInt(FileDescriptor fd, int fdInt);
 
-
     /**
      * Equivalent to Android's FileDescriptor.getInt$().
      */
@@ -49,6 +48,10 @@
      */
     public abstract void closeFd(FileDescriptor fd) throws IOException;
 
+    public abstract long addressOf(Object o);
+
+    public abstract <T> T fromAddress(long address);
+
     /**
      * Placeholder implementation for the host side.
      *
@@ -75,5 +78,15 @@
         public void closeFd(FileDescriptor fd) {
             throw calledOnHostside();
         }
+
+        @Override
+        public long addressOf(Object o) {
+            throw calledOnHostside();
+        }
+
+        @Override
+        public <T> T fromAddress(long address) {
+            throw calledOnHostside();
+        }
     }
 }
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
index a260147..2323c65 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
@@ -18,8 +18,16 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 class OpenJdkWorkaround extends JvmWorkaround {
+
+    // @GuardedBy("sAddressMap")
+    private static final Map<Object, Long> sAddressMap = new WeakHashMap<>();
+    // @GuardedBy("sAddressMap")
+    private static long sCurrentAddress = 1;
+
     @Override
     public void setFdInt(FileDescriptor fd, int fdInt) {
         try {
@@ -60,4 +68,28 @@
                     + " perhaps JRE has changed?", e);
         }
     }
+
+    @Override
+    public long addressOf(Object o) {
+        synchronized (sAddressMap) {
+            Long address = sAddressMap.get(o);
+            if (address == null) {
+                address = sCurrentAddress++;
+                sAddressMap.put(o, address);
+            }
+            return address;
+        }
+    }
+
+    @Override
+    public <T> T fromAddress(long address) {
+        synchronized (sAddressMap) {
+            for (var e : sAddressMap.entrySet()) {
+                if (e.getValue() == address) {
+                    return (T) e.getKey();
+                }
+            }
+        }
+        return null;
+    }
 }
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index c8cc8d9..7b5bc5a 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -21,6 +21,8 @@
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Arrays;
 
 public class RavenwoodCommonUtils {
@@ -42,10 +44,17 @@
 
     private static final boolean IS_ON_RAVENWOOD = RavenwoodDivergence.isOnRavenwood();
 
-    private static final String RAVEWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
+    private static final String RAVENWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
 
     public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood";
 
+    public static final String RAVENWOOD_RESOURCE_APK = "ravenwood-res-apks/ravenwood-res.apk";
+
+    public static final String RAVENWOOD_EMPTY_RESOURCES_APK =
+            RAVENWOOD_RUNTIME_PATH + "ravenwood-data/ravenwood-empty-res.apk";
+
+    public static final String RAVENWOOD_VERSION_JAVA_SYSPROP = "android.ravenwood.version";
+
     // @GuardedBy("sLock")
     private static boolean sIntegrityChecked = false;
 
@@ -72,6 +81,18 @@
         return sEnableExtraRuntimeCheck;
     }
 
+    /** Simple logging method. */
+    public static void log(String tag, String message) {
+        // Avoid using Android's Log class, which could be broken for various reasons.
+        // (e.g. the JNI file doesn't exist for whatever reason)
+        System.out.print(tag + ": " + message + "\n");
+    }
+
+    /** Simple logging method. */
+    private void log(String tag, String format, Object... args) {
+        log(tag, String.format(format, args));
+    }
+
     /**
      * Load the main runtime JNI library.
      */
@@ -176,7 +197,7 @@
      */
     public static String getRavenwoodRuntimePath() {
         ensureOnRavenwood();
-        return RAVEWOOD_RUNTIME_PATH;
+        return RAVENWOOD_RUNTIME_PATH;
     }
 
     private static String getRavenwoodRuntimePathInternal() {
@@ -231,4 +252,17 @@
         var is = new FileInputStream(fd);
         RavenwoodCommonUtils.closeQuietly(is);
     }
+
+    public static void ensureIsPublicVoidMethod(Method method, boolean isStatic) {
+        var ok = Modifier.isPublic(method.getModifiers())
+                && (Modifier.isStatic(method.getModifiers()) == isStatic)
+                && (method.getReturnType() == void.class);
+        if (ok) {
+            return; // okay
+        }
+        throw new AssertionError(String.format(
+                "Method %s.%s() expected to be public %svoid",
+                method.getDeclaringClass().getName(), method.getName(),
+                (isStatic ? "static " : "")));
+    }
 }
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
deleted file mode 100644
index 5a3589d..0000000
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
+++ /dev/null
@@ -1,31 +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.platform.test.ravenwood.nativesubstitution;
-
-import com.android.ravenwood.common.JvmWorkaround;
-
-import java.io.FileDescriptor;
-
-public class ParcelFileDescriptor_host {
-    public static void setFdInt(FileDescriptor fd, int fdInt) {
-        JvmWorkaround.getInstance().setFdInt(fd, fdInt);
-    }
-
-    public static int getFdInt(FileDescriptor fd) {
-        return JvmWorkaround.getInstance().getFdInt(fd);
-    }
-}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
index b00cee0..58f6bbb 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 
 import com.android.internal.ravenwood.RavenwoodEnvironment;
+import com.android.ravenwood.common.JvmWorkaround;
 import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 public class RavenwoodEnvironment_host {
@@ -35,7 +36,10 @@
     /**
      * Called from {@link RavenwoodEnvironment#ensureRavenwoodInitialized()}.
      */
-    public static void ensureRavenwoodInitializedInternal() {
+    public static void nativeEnsureRavenwoodInitialized() {
+
+        // TODO Unify it with the initialization code in RavenwoodAwareTestRunnerHook.
+
         synchronized (sInitializeLock) {
             if (sInitialized) {
                 return;
@@ -55,4 +59,18 @@
             sInitialized = true;
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Called from {@link RavenwoodEnvironment#getRavenwoodRuntimePath()}.
+     */
+    public static String nativeGetRavenwoodRuntimePath(RavenwoodEnvironment env) {
+        return RavenwoodCommonUtils.getRavenwoodRuntimePath();
+    }
+
+    /**
+     * Called from {@link RavenwoodEnvironment#fromAddress(long)}.
+     */
+    public static <T> T nativeFromAddress(RavenwoodEnvironment env, long address) {
+        return JvmWorkaround.getInstance().fromAddress(address);
+    }
+}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index e198646..0f955e7 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -151,6 +151,11 @@
      */
     private static final Class<?>[] sLibandroidClasses = {
             android.util.Log.class,
+            android.os.Parcel.class,
+            android.content.res.ApkAssets.class,
+            android.content.res.AssetManager.class,
+            android.content.res.StringBlock.class,
+            android.content.res.XmlBlock.class,
     };
 
     /**
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
index ecaa816..a5c0b54 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -15,11 +15,15 @@
  */
 package android.system;
 
+import com.android.ravenwood.RavenwoodRuntimeNative;
 import com.android.ravenwood.common.JvmWorkaround;
-import com.android.ravenwood.common.RavenwoodRuntimeNative;
 
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
 
 /**
  * OS class replacement used on Ravenwood. For now, we just implement APIs as we need them...
@@ -36,6 +40,11 @@
         return RavenwoodRuntimeNative.pipe2(flags);
     }
 
+    /** Ravenwood version of the OS API. */
+    public static FileDescriptor[] pipe() throws ErrnoException {
+        return RavenwoodRuntimeNative.pipe2(0);
+    }
+
     public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
         return RavenwoodRuntimeNative.dup(fd);
     }
@@ -69,4 +78,19 @@
     public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
         return RavenwoodRuntimeNative.open(path, flags, mode);
     }
+
+    /** Ravenwood version of the OS API. */
+    public static int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount,
+            long offset) throws ErrnoException, InterruptedIOException {
+        var channel = new FileInputStream(fd).getChannel();
+        var buf = ByteBuffer.wrap(bytes, byteOffset, byteCount);
+        try {
+            return channel.read(buf, offset);
+        } catch (AsynchronousCloseException e) {
+            throw new InterruptedIOException(e.getMessage());
+        } catch (IOException e) {
+            // Most likely EIO
+            throw new ErrnoException("pread", OsConstants.EIO, e);
+        }
+    }
 }
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
new file mode 100644
index 0000000..96aed4b
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
@@ -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.ravenwood;
+
+import com.android.ravenwood.common.JvmWorkaround;
+
+import java.io.FileDescriptor;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Class to host APIs that exist in libcore, but not in standard JRE.
+ */
+public class RavenwoodJdkPatch {
+    /**
+     * Implements FileDescriptor.getInt$()
+     */
+    public static int getInt$(FileDescriptor fd) {
+        return JvmWorkaround.getInstance().getFdInt(fd);
+    }
+
+    /**
+     * Implements FileDescriptor.setInt$(int)
+     */
+    public static void setInt$(FileDescriptor fd, int rawFd) {
+        JvmWorkaround.getInstance().setFdInt(fd, rawFd);
+    }
+
+    /**
+     * Implements LinkedHashMap.eldest()
+     */
+    public static <K, V> Map.Entry<K, V> eldest(LinkedHashMap<K, V> map) {
+        final var it = map.entrySet().iterator();
+        return it.hasNext() ? it.next() : null;
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
new file mode 100644
index 0000000..0d8408c
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -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.ravenwood;
+
+import android.system.ErrnoException;
+import android.system.StructStat;
+
+import com.android.ravenwood.common.JvmWorkaround;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.FileDescriptor;
+
+/**
+ * Class to host all the JNI methods used in ravenwood runtime.
+ */
+public class RavenwoodRuntimeNative {
+    private RavenwoodRuntimeNative() {
+    }
+
+    static {
+        RavenwoodCommonUtils.ensureOnRavenwood();
+        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
+    }
+
+    public static native void applyFreeFunction(long freeFunction, long nativePtr);
+
+    private static native long nLseek(int fd, long offset, int whence) throws ErrnoException;
+
+    private static native int[] nPipe2(int flags) throws ErrnoException;
+
+    private static native int nDup(int oldfd) throws ErrnoException;
+
+    private static native int nFcntlInt(int fd, int cmd, int arg) throws ErrnoException;
+
+    private static native StructStat nFstat(int fd) throws ErrnoException;
+
+    public static native StructStat lstat(String path) throws ErrnoException;
+
+    public static native StructStat stat(String path) throws ErrnoException;
+
+    private static native int nOpen(String path, int flags, int mode) throws ErrnoException;
+
+    public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
+        return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
+    }
+
+    public static FileDescriptor[] pipe2(int flags) throws ErrnoException {
+        var fds = nPipe2(flags);
+        var ret = new FileDescriptor[] {
+                new FileDescriptor(),
+                new FileDescriptor(),
+        };
+        JvmWorkaround.getInstance().setFdInt(ret[0], fds[0]);
+        JvmWorkaround.getInstance().setFdInt(ret[1], fds[1]);
+
+        return ret;
+    }
+
+    public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
+        var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd));
+
+        var retFd = new FileDescriptor();
+        JvmWorkaround.getInstance().setFdInt(retFd, fdInt);
+        return retFd;
+    }
+
+    public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException {
+        var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
+
+        return nFcntlInt(fdInt, cmd, arg);
+    }
+
+    public static StructStat fstat(FileDescriptor fd) throws ErrnoException {
+        var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
+
+        return nFstat(fdInt);
+    }
+
+    public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+        int fd = nOpen(path, flags, mode);
+        if (fd < 0) return null;
+        var retFd = new FileDescriptor();
+        JvmWorkaround.getInstance().setFdInt(retFd, fd);
+        return retFd;
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java
deleted file mode 100644
index beba8339..0000000
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/common/RavenwoodRuntimeNative.java
+++ /dev/null
@@ -1,96 +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.ravenwood.common;
-
-import android.system.ErrnoException;
-import android.system.StructStat;
-
-import java.io.FileDescriptor;
-
-/**
- * Class to host all the JNI methods used in ravenwood runtime.
- */
-public class RavenwoodRuntimeNative {
-    private RavenwoodRuntimeNative() {
-    }
-
-    static {
-        RavenwoodCommonUtils.ensureOnRavenwood();
-        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
-    }
-
-    public static native void applyFreeFunction(long freeFunction, long nativePtr);
-
-    private static native long nLseek(int fd, long offset, int whence) throws ErrnoException;
-
-    private static native int[] nPipe2(int flags) throws ErrnoException;
-
-    private static native int nDup(int oldfd) throws ErrnoException;
-
-    private static native int nFcntlInt(int fd, int cmd, int arg) throws ErrnoException;
-
-    private static native StructStat nFstat(int fd) throws ErrnoException;
-
-    public static native StructStat lstat(String path) throws ErrnoException;
-
-    public static native StructStat stat(String path) throws ErrnoException;
-
-    private static native int nOpen(String path, int flags, int mode) throws ErrnoException;
-
-    public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
-        return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
-    }
-
-    public static FileDescriptor[] pipe2(int flags) throws ErrnoException {
-        var fds = nPipe2(flags);
-        var ret = new FileDescriptor[] {
-                new FileDescriptor(),
-                new FileDescriptor(),
-        };
-        JvmWorkaround.getInstance().setFdInt(ret[0], fds[0]);
-        JvmWorkaround.getInstance().setFdInt(ret[1], fds[1]);
-
-        return ret;
-    }
-
-    public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
-        var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd));
-
-        var retFd = new FileDescriptor();
-        JvmWorkaround.getInstance().setFdInt(retFd, fdInt);
-        return retFd;
-    }
-
-    public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException {
-        var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
-
-        return nFcntlInt(fdInt, cmd, arg);
-    }
-
-    public static StructStat fstat(FileDescriptor fd) throws ErrnoException {
-        var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
-
-        return nFstat(fdInt);
-    }
-
-    public static FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
-        int fd = nOpen(path, flags, mode);
-        if (fd < 0) return null;
-        var retFd = new FileDescriptor();
-        JvmWorkaround.getInstance().setFdInt(retFd, fd);
-        return retFd;
-    }
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
index 7d2b00d..ba89f71 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java
@@ -19,6 +19,8 @@
 // The original is here:
 // $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java
 
+import com.android.ravenwood.common.JvmWorkaround;
+
 import java.lang.reflect.Array;
 
 public class VMRuntime {
@@ -32,14 +34,22 @@
     }
 
     public boolean is64Bit() {
-        return true;
+        return "amd64".equals(System.getProperty("os.arch"));
     }
 
     public static boolean is64BitAbi(String abi) {
-        return true;
+        return abi.contains("64");
     }
 
     public Object newUnpaddedArray(Class<?> componentType, int minLength) {
         return Array.newInstance(componentType, minLength);
     }
+
+    public Object newNonMovableArray(Class<?> componentType, int length) {
+        return Array.newInstance(componentType, length);
+    }
+
+    public long addressOf(Object obj) {
+        return JvmWorkaround.getInstance().addressOf(obj);
+    }
 }
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java
index 65c285e..2bd1ae8 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoUtils.java
@@ -16,7 +16,13 @@
 
 package libcore.io;
 
+import android.system.ErrnoException;
+import android.system.Os;
+
+import com.android.ravenwood.common.JvmWorkaround;
+
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.Socket;
 
@@ -47,6 +53,13 @@
         }
     }
 
+    public static void closeQuietly(FileDescriptor fd) {
+        try {
+            Os.close(fd);
+        } catch (ErrnoException ignored) {
+        }
+    }
+
     public static void deleteContents(File dir) throws IOException {
         File[] files = dir.listFiles();
         if (files != null) {
@@ -58,4 +71,17 @@
             }
         }
     }
+
+    /**
+     * FD owners currently unsupported under Ravenwood; ignored
+     */
+    public static void setFdOwner(FileDescriptor fd, Object owner) {
+    }
+
+    /**
+     * FD owners currently unsupported under Ravenwood; return FD directly
+     */
+    public static int acquireRawFd(FileDescriptor fd) {
+        return JvmWorkaround.getInstance().getFdInt(fd);
+    }
 }
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
new file mode 100644
index 0000000..478503b
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/FP16.java
@@ -0,0 +1,814 @@
+/*
+ * 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 libcore.util;
+
+/**
+ * <p>The {@code FP16} class is a wrapper and a utility class to manipulate half-precision 16-bit
+ * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
+ * floating point data types (also called fp16 or binary16). A half-precision float can be
+ * created from or converted to single-precision floats, and is stored in a short data type.
+ *
+ * <p>The IEEE 754 standard specifies an fp16 as having the following format:</p>
+ * <ul>
+ * <li>Sign bit: 1 bit</li>
+ * <li>Exponent width: 5 bits</li>
+ * <li>Significand: 10 bits</li>
+ * </ul>
+ *
+ * <p>The format is laid out as follows:</p>
+ * <pre>
+ * 1   11111   1111111111
+ * ^   --^--   -----^----
+ * sign  |          |_______ significand
+ *       |
+ *       -- exponent
+ * </pre>
+ *
+ * <p>Half-precision floating points can be useful to save memory and/or
+ * bandwidth at the expense of range and precision when compared to single-precision
+ * floating points (fp32).</p>
+ * <p>To help you decide whether fp16 is the right storage type for you need, please
+ * refer to the table below that shows the available precision throughout the range of
+ * possible values. The <em>precision</em> column indicates the step size between two
+ * consecutive numbers in a specific part of the range.</p>
+ *
+ * <table summary="Precision of fp16 across the range">
+ *     <tr><th>Range start</th><th>Precision</th></tr>
+ *     <tr><td>0</td><td>1 &frasl; 16,777,216</td></tr>
+ *     <tr><td>1 &frasl; 16,384</td><td>1 &frasl; 16,777,216</td></tr>
+ *     <tr><td>1 &frasl; 8,192</td><td>1 &frasl; 8,388,608</td></tr>
+ *     <tr><td>1 &frasl; 4,096</td><td>1 &frasl; 4,194,304</td></tr>
+ *     <tr><td>1 &frasl; 2,048</td><td>1 &frasl; 2,097,152</td></tr>
+ *     <tr><td>1 &frasl; 1,024</td><td>1 &frasl; 1,048,576</td></tr>
+ *     <tr><td>1 &frasl; 512</td><td>1 &frasl; 524,288</td></tr>
+ *     <tr><td>1 &frasl; 256</td><td>1 &frasl; 262,144</td></tr>
+ *     <tr><td>1 &frasl; 128</td><td>1 &frasl; 131,072</td></tr>
+ *     <tr><td>1 &frasl; 64</td><td>1 &frasl; 65,536</td></tr>
+ *     <tr><td>1 &frasl; 32</td><td>1 &frasl; 32,768</td></tr>
+ *     <tr><td>1 &frasl; 16</td><td>1 &frasl; 16,384</td></tr>
+ *     <tr><td>1 &frasl; 8</td><td>1 &frasl; 8,192</td></tr>
+ *     <tr><td>1 &frasl; 4</td><td>1 &frasl; 4,096</td></tr>
+ *     <tr><td>1 &frasl; 2</td><td>1 &frasl; 2,048</td></tr>
+ *     <tr><td>1</td><td>1 &frasl; 1,024</td></tr>
+ *     <tr><td>2</td><td>1 &frasl; 512</td></tr>
+ *     <tr><td>4</td><td>1 &frasl; 256</td></tr>
+ *     <tr><td>8</td><td>1 &frasl; 128</td></tr>
+ *     <tr><td>16</td><td>1 &frasl; 64</td></tr>
+ *     <tr><td>32</td><td>1 &frasl; 32</td></tr>
+ *     <tr><td>64</td><td>1 &frasl; 16</td></tr>
+ *     <tr><td>128</td><td>1 &frasl; 8</td></tr>
+ *     <tr><td>256</td><td>1 &frasl; 4</td></tr>
+ *     <tr><td>512</td><td>1 &frasl; 2</td></tr>
+ *     <tr><td>1,024</td><td>1</td></tr>
+ *     <tr><td>2,048</td><td>2</td></tr>
+ *     <tr><td>4,096</td><td>4</td></tr>
+ *     <tr><td>8,192</td><td>8</td></tr>
+ *     <tr><td>16,384</td><td>16</td></tr>
+ *     <tr><td>32,768</td><td>32</td></tr>
+ * </table>
+ *
+ * <p>This table shows that numbers higher than 1024 lose all fractional precision.</p>
+ *
+ * @hide
+ */
+
+public final class FP16 {
+    /**
+     * The number of bits used to represent a half-precision float value.
+     *
+     * @hide
+     */
+    public static final int SIZE = 16;
+
+    /**
+     * Epsilon is the difference between 1.0 and the next value representable
+     * by a half-precision floating-point.
+     *
+     * @hide
+     */
+    public static final short EPSILON = (short) 0x1400;
+
+    /**
+     * Maximum exponent a finite half-precision float may have.
+     *
+     * @hide
+     */
+    public static final int MAX_EXPONENT = 15;
+    /**
+     * Minimum exponent a normalized half-precision float may have.
+     *
+     * @hide
+     */
+    public static final int MIN_EXPONENT = -14;
+
+    /**
+     * Smallest negative value a half-precision float may have.
+     *
+     * @hide
+     */
+    public static final short LOWEST_VALUE = (short) 0xfbff;
+    /**
+     * Maximum positive finite value a half-precision float may have.
+     *
+     * @hide
+     */
+    public static final short MAX_VALUE = (short) 0x7bff;
+    /**
+     * Smallest positive normal value a half-precision float may have.
+     *
+     * @hide
+     */
+    public static final short MIN_NORMAL = (short) 0x0400;
+    /**
+     * Smallest positive non-zero value a half-precision float may have.
+     *
+     * @hide
+     */
+    public static final short MIN_VALUE = (short) 0x0001;
+    /**
+     * A Not-a-Number representation of a half-precision float.
+     *
+     * @hide
+     */
+    public static final short NaN = (short) 0x7e00;
+    /**
+     * Negative infinity of type half-precision float.
+     *
+     * @hide
+     */
+    public static final short NEGATIVE_INFINITY = (short) 0xfc00;
+    /**
+     * Negative 0 of type half-precision float.
+     *
+     * @hide
+     */
+    public static final short NEGATIVE_ZERO = (short) 0x8000;
+    /**
+     * Positive infinity of type half-precision float.
+     *
+     * @hide
+     */
+    public static final short POSITIVE_INFINITY = (short) 0x7c00;
+    /**
+     * Positive 0 of type half-precision float.
+     *
+     * @hide
+     */
+    public static final short POSITIVE_ZERO = (short) 0x0000;
+
+    /**
+     * The offset to shift by to obtain the sign bit.
+     *
+     * @hide
+     */
+    public static final int SIGN_SHIFT                = 15;
+
+    /**
+     * The offset to shift by to obtain the exponent bits.
+     *
+     * @hide
+     */
+    public static final int EXPONENT_SHIFT            = 10;
+
+    /**
+     * The bitmask to AND a number with to obtain the sign bit.
+     *
+     * @hide
+     */
+    public static final int SIGN_MASK                 = 0x8000;
+
+    /**
+     * The bitmask to AND a number shifted by {@link #EXPONENT_SHIFT} right, to obtain exponent bits.
+     *
+     * @hide
+     */
+    public static final int SHIFTED_EXPONENT_MASK     = 0x1f;
+
+    /**
+     * The bitmask to AND a number with to obtain significand bits.
+     *
+     * @hide
+     */
+    public static final int SIGNIFICAND_MASK          = 0x3ff;
+
+    /**
+     * The bitmask to AND with to obtain exponent and significand bits.
+     *
+     * @hide
+     */
+    public static final int EXPONENT_SIGNIFICAND_MASK = 0x7fff;
+
+    /**
+     * The offset of the exponent from the actual value.
+     *
+     * @hide
+     */
+    public static final int EXPONENT_BIAS             = 15;
+
+    private static final int FP32_SIGN_SHIFT            = 31;
+    private static final int FP32_EXPONENT_SHIFT        = 23;
+    private static final int FP32_SHIFTED_EXPONENT_MASK = 0xff;
+    private static final int FP32_SIGNIFICAND_MASK      = 0x7fffff;
+    private static final int FP32_EXPONENT_BIAS         = 127;
+    private static final int FP32_QNAN_MASK             = 0x400000;
+    private static final int FP32_DENORMAL_MAGIC = 126 << 23;
+    private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
+
+    /** Hidden constructor to prevent instantiation. */
+    private FP16() {}
+
+    /**
+     * <p>Compares the two specified half-precision float values. The following
+     * conditions apply during the comparison:</p>
+     *
+     * <ul>
+     * <li>{@link #NaN} is considered by this method to be equal to itself and greater
+     * than all other half-precision float values (including {@code #POSITIVE_INFINITY})</li>
+     * <li>{@link #POSITIVE_ZERO} is considered by this method to be greater than
+     * {@link #NEGATIVE_ZERO}.</li>
+     * </ul>
+     *
+     * @param x The first half-precision float value to compare.
+     * @param y The second half-precision float value to compare
+     *
+     * @return  The value {@code 0} if {@code x} is numerically equal to {@code y}, a
+     *          value less than {@code 0} if {@code x} is numerically less than {@code y},
+     *          and a value greater than {@code 0} if {@code x} is numerically greater
+     *          than {@code y}
+     *
+     * @hide
+     */
+    public static int compare(short x, short y) {
+        if (less(x, y)) return -1;
+        if (greater(x, y)) return 1;
+
+        // Collapse NaNs, akin to halfToIntBits(), but we want to keep
+        // (signed) short value types to preserve the ordering of -0.0
+        // and +0.0
+        short xBits = isNaN(x) ? NaN : x;
+        short yBits = isNaN(y) ? NaN : y;
+
+        return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+    }
+
+    /**
+     * Returns the closest integral half-precision float value to the specified
+     * half-precision float value. Special values are handled in the
+     * following ways:
+     * <ul>
+     * <li>If the specified half-precision float is NaN, the result is NaN</li>
+     * <li>If the specified half-precision float is infinity (negative or positive),
+     * the result is infinity (with the same sign)</li>
+     * <li>If the specified half-precision float is zero (negative or positive),
+     * the result is zero (with the same sign)</li>
+     * </ul>
+     *
+     * @param h A half-precision float value
+     * @return The value of the specified half-precision float rounded to the nearest
+     *         half-precision float value
+     *
+     * @hide
+     */
+    public static short rint(short h) {
+        int bits = h & 0xffff;
+        int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+        int result = bits;
+
+        if (abs < 0x3c00) {
+            result &= SIGN_MASK;
+            if (abs > 0x3800){
+                result |= 0x3c00;
+            }
+        } else if (abs < 0x6400) {
+            int exp = 25 - (abs >> 10);
+            int mask = (1 << exp) - 1;
+            result += ((1 << (exp - 1)) - (~(abs >> exp) & 1));
+            result &= ~mask;
+        }
+        if (isNaN((short) result)) {
+            // if result is NaN mask with qNaN
+            // (i.e. mask the most significant mantissa bit with 1)
+            // to comply with hardware implementations (ARM64, Intel, etc).
+            result |= NaN;
+        }
+
+        return (short) result;
+    }
+
+    /**
+     * Returns the smallest half-precision float value toward negative infinity
+     * greater than or equal to the specified half-precision float value.
+     * Special values are handled in the following ways:
+     * <ul>
+     * <li>If the specified half-precision float is NaN, the result is NaN</li>
+     * <li>If the specified half-precision float is infinity (negative or positive),
+     * the result is infinity (with the same sign)</li>
+     * <li>If the specified half-precision float is zero (negative or positive),
+     * the result is zero (with the same sign)</li>
+     * </ul>
+     *
+     * @param h A half-precision float value
+     * @return The smallest half-precision float value toward negative infinity
+     *         greater than or equal to the specified half-precision float value
+     *
+     * @hide
+     */
+    public static short ceil(short h) {
+        int bits = h & 0xffff;
+        int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+        int result = bits;
+
+        if (abs < 0x3c00) {
+            result &= SIGN_MASK;
+            result |= 0x3c00 & -(~(bits >> 15) & (abs != 0 ? 1 : 0));
+        } else if (abs < 0x6400) {
+            abs = 25 - (abs >> 10);
+            int mask = (1 << abs) - 1;
+            result += mask & ((bits >> 15) - 1);
+            result &= ~mask;
+        }
+        if (isNaN((short) result)) {
+            // if result is NaN mask with qNaN
+            // (i.e. mask the most significant mantissa bit with 1)
+            // to comply with hardware implementations (ARM64, Intel, etc).
+            result |= NaN;
+        }
+
+        return (short) result;
+    }
+
+    /**
+     * Returns the largest half-precision float value toward positive infinity
+     * less than or equal to the specified half-precision float value.
+     * Special values are handled in the following ways:
+     * <ul>
+     * <li>If the specified half-precision float is NaN, the result is NaN</li>
+     * <li>If the specified half-precision float is infinity (negative or positive),
+     * the result is infinity (with the same sign)</li>
+     * <li>If the specified half-precision float is zero (negative or positive),
+     * the result is zero (with the same sign)</li>
+     * </ul>
+     *
+     * @param h A half-precision float value
+     * @return The largest half-precision float value toward positive infinity
+     *         less than or equal to the specified half-precision float value
+     *
+     * @hide
+     */
+    public static short floor(short h) {
+        int bits = h & 0xffff;
+        int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+        int result = bits;
+
+        if (abs < 0x3c00) {
+            result &= SIGN_MASK;
+            result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
+        } else if (abs < 0x6400) {
+            abs = 25 - (abs >> 10);
+            int mask = (1 << abs) - 1;
+            result += mask & -(bits >> 15);
+            result &= ~mask;
+        }
+        if (isNaN((short) result)) {
+            // if result is NaN mask with qNaN
+            // i.e. (Mask the most significant mantissa bit with 1)
+            result |= NaN;
+        }
+
+        return (short) result;
+    }
+
+    /**
+     * Returns the truncated half-precision float value of the specified
+     * half-precision float value. Special values are handled in the following ways:
+     * <ul>
+     * <li>If the specified half-precision float is NaN, the result is NaN</li>
+     * <li>If the specified half-precision float is infinity (negative or positive),
+     * the result is infinity (with the same sign)</li>
+     * <li>If the specified half-precision float is zero (negative or positive),
+     * the result is zero (with the same sign)</li>
+     * </ul>
+     *
+     * @param h A half-precision float value
+     * @return The truncated half-precision float value of the specified
+     *         half-precision float value
+     *
+     * @hide
+     */
+    public static short trunc(short h) {
+        int bits = h & 0xffff;
+        int abs = bits & EXPONENT_SIGNIFICAND_MASK;
+        int result = bits;
+
+        if (abs < 0x3c00) {
+            result &= SIGN_MASK;
+        } else if (abs < 0x6400) {
+            abs = 25 - (abs >> 10);
+            int mask = (1 << abs) - 1;
+            result &= ~mask;
+        }
+
+        return (short) result;
+    }
+
+    /**
+     * Returns the smaller of two half-precision float values (the value closest
+     * to negative infinity). Special values are handled in the following ways:
+     * <ul>
+     * <li>If either value is NaN, the result is NaN</li>
+     * <li>{@link #NEGATIVE_ZERO} is smaller than {@link #POSITIVE_ZERO}</li>
+     * </ul>
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     * @return The smaller of the two specified half-precision values
+     *
+     * @hide
+     */
+    public static short min(short x, short y) {
+        if (isNaN(x)) return NaN;
+        if (isNaN(y)) return NaN;
+
+        if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+            return (x & SIGN_MASK) != 0 ? x : y;
+        }
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+    }
+
+    /**
+     * Returns the larger of two half-precision float values (the value closest
+     * to positive infinity). Special values are handled in the following ways:
+     * <ul>
+     * <li>If either value is NaN, the result is NaN</li>
+     * <li>{@link #POSITIVE_ZERO} is greater than {@link #NEGATIVE_ZERO}</li>
+     * </ul>
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return The larger of the two specified half-precision values
+     *
+     * @hide
+     */
+    public static short max(short x, short y) {
+        if (isNaN(x)) return NaN;
+        if (isNaN(y)) return NaN;
+
+        if ((x & EXPONENT_SIGNIFICAND_MASK) == 0 && (y & EXPONENT_SIGNIFICAND_MASK) == 0) {
+            return (x & SIGN_MASK) != 0 ? y : x;
+        }
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+    }
+
+    /**
+     * Returns true if the first half-precision float value is less (smaller
+     * toward negative infinity) than the second half-precision float value.
+     * If either of the values is NaN, the result is false.
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return True if x is less than y, false otherwise
+     *
+     * @hide
+     */
+    public static boolean less(short x, short y) {
+        if (isNaN(x)) return false;
+        if (isNaN(y)) return false;
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+    }
+
+    /**
+     * Returns true if the first half-precision float value is less (smaller
+     * toward negative infinity) than or equal to the second half-precision
+     * float value. If either of the values is NaN, the result is false.
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return True if x is less than or equal to y, false otherwise
+     *
+     * @hide
+     */
+    public static boolean lessEquals(short x, short y) {
+        if (isNaN(x)) return false;
+        if (isNaN(y)) return false;
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+    }
+
+    /**
+     * Returns true if the first half-precision float value is greater (larger
+     * toward positive infinity) than the second half-precision float value.
+     * If either of the values is NaN, the result is false.
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return True if x is greater than y, false otherwise
+     *
+     * @hide
+     */
+    public static boolean greater(short x, short y) {
+        if (isNaN(x)) return false;
+        if (isNaN(y)) return false;
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+    }
+
+    /**
+     * Returns true if the first half-precision float value is greater (larger
+     * toward positive infinity) than or equal to the second half-precision float
+     * value. If either of the values is NaN, the result is false.
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return True if x is greater than y, false otherwise
+     *
+     * @hide
+     */
+    public static boolean greaterEquals(short x, short y) {
+        if (isNaN(x)) return false;
+        if (isNaN(y)) return false;
+
+        return ((x & SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
+               ((y & SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+    }
+
+    /**
+     * Returns true if the two half-precision float values are equal.
+     * If either of the values is NaN, the result is false. {@link #POSITIVE_ZERO}
+     * and {@link #NEGATIVE_ZERO} are considered equal.
+     *
+     * @param x The first half-precision value
+     * @param y The second half-precision value
+     *
+     * @return True if x is equal to y, false otherwise
+     *
+     * @hide
+     */
+    public static boolean equals(short x, short y) {
+        if (isNaN(x)) return false;
+        if (isNaN(y)) return false;
+
+        return x == y || ((x | y) & EXPONENT_SIGNIFICAND_MASK) == 0;
+    }
+
+    /**
+     * Returns true if the specified half-precision float value represents
+     * infinity, false otherwise.
+     *
+     * @param h A half-precision float value
+     * @return True if the value is positive infinity or negative infinity,
+     *         false otherwise
+     *
+     * @hide
+     */
+    public static boolean isInfinite(short h) {
+        return (h & EXPONENT_SIGNIFICAND_MASK) == POSITIVE_INFINITY;
+    }
+
+    /**
+     * Returns true if the specified half-precision float value represents
+     * a Not-a-Number, false otherwise.
+     *
+     * @param h A half-precision float value
+     * @return True if the value is a NaN, false otherwise
+     *
+     * @hide
+     */
+    public static boolean isNaN(short h) {
+        return (h & EXPONENT_SIGNIFICAND_MASK) > POSITIVE_INFINITY;
+    }
+
+    /**
+     * Returns true if the specified half-precision float value is normalized
+     * (does not have a subnormal representation). If the specified value is
+     * {@link #POSITIVE_INFINITY}, {@link #NEGATIVE_INFINITY},
+     * {@link #POSITIVE_ZERO}, {@link #NEGATIVE_ZERO}, NaN or any subnormal
+     * number, this method returns false.
+     *
+     * @param h A half-precision float value
+     * @return True if the value is normalized, false otherwise
+     *
+     * @hide
+     */
+    public static boolean isNormalized(short h) {
+        return (h & POSITIVE_INFINITY) != 0 && (h & POSITIVE_INFINITY) != POSITIVE_INFINITY;
+    }
+
+    /**
+     * <p>Converts the specified half-precision float value into a
+     * single-precision float value. The following special cases are handled:</p>
+     * <ul>
+     * <li>If the input is {@link #NaN}, the returned value is {@link Float#NaN}</li>
+     * <li>If the input is {@link #POSITIVE_INFINITY} or
+     * {@link #NEGATIVE_INFINITY}, the returned value is respectively
+     * {@link Float#POSITIVE_INFINITY} or {@link Float#NEGATIVE_INFINITY}</li>
+     * <li>If the input is 0 (positive or negative), the returned value is +/-0.0f</li>
+     * <li>Otherwise, the returned value is a normalized single-precision float value</li>
+     * </ul>
+     *
+     * @param h The half-precision float value to convert to single-precision
+     * @return A normalized single-precision float value
+     *
+     * @hide
+     */
+    public static float toFloat(short h) {
+        int bits = h & 0xffff;
+        int s = bits & SIGN_MASK;
+        int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+        int m = (bits                        ) & SIGNIFICAND_MASK;
+
+        int outE = 0;
+        int outM = 0;
+
+        if (e == 0) { // Denormal or 0
+            if (m != 0) {
+                // Convert denorm fp16 into normalized fp32
+                float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
+                o -= FP32_DENORMAL_FLOAT;
+                return s == 0 ? o : -o;
+            }
+        } else {
+            outM = m << 13;
+            if (e == 0x1f) { // Infinite or NaN
+                outE = 0xff;
+                if (outM != 0) { // SNaNs are quieted
+                    outM |= FP32_QNAN_MASK;
+                }
+            } else {
+                outE = e - EXPONENT_BIAS + FP32_EXPONENT_BIAS;
+            }
+        }
+
+        int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
+        return Float.intBitsToFloat(out);
+    }
+
+    /**
+     * <p>Converts the specified single-precision float value into a
+     * half-precision float value. The following special cases are handled:</p>
+     * <ul>
+     * <li>If the input is NaN (see {@link Float#isNaN(float)}), the returned
+     * value is {@link #NaN}</li>
+     * <li>If the input is {@link Float#POSITIVE_INFINITY} or
+     * {@link Float#NEGATIVE_INFINITY}, the returned value is respectively
+     * {@link #POSITIVE_INFINITY} or {@link #NEGATIVE_INFINITY}</li>
+     * <li>If the input is 0 (positive or negative), the returned value is
+     * {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+     * <li>If the input is a less than {@link #MIN_VALUE}, the returned value
+     * is flushed to {@link #POSITIVE_ZERO} or {@link #NEGATIVE_ZERO}</li>
+     * <li>If the input is a less than {@link #MIN_NORMAL}, the returned value
+     * is a denorm half-precision float</li>
+     * <li>Otherwise, the returned value is rounded to the nearest
+     * representable half-precision float value</li>
+     * </ul>
+     *
+     * @param f The single-precision float value to convert to half-precision
+     * @return A half-precision float value
+     *
+     * @hide
+     */
+    public static short toHalf(float f) {
+        int bits = Float.floatToRawIntBits(f);
+        int s = (bits >>> FP32_SIGN_SHIFT    );
+        int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_SHIFTED_EXPONENT_MASK;
+        int m = (bits                        ) & FP32_SIGNIFICAND_MASK;
+
+        int outE = 0;
+        int outM = 0;
+
+        if (e == 0xff) { // Infinite or NaN
+            outE = 0x1f;
+            outM = m != 0 ? 0x200 : 0;
+        } else {
+            e = e - FP32_EXPONENT_BIAS + EXPONENT_BIAS;
+            if (e >= 0x1f) { // Overflow
+                outE = 0x1f;
+            } else if (e <= 0) { // Underflow
+                if (e < -10) {
+                    // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
+                } else {
+                    // The fp32 value is a normalized float less than MIN_NORMAL,
+                    // we convert to a denorm fp16
+                    m = m | 0x800000;
+                    int shift = 14 - e;
+                    outM = m >> shift;
+
+                    int lowm = m & ((1 << shift) - 1);
+                    int hway = 1 << (shift - 1);
+                    // if above halfway or exactly halfway and outM is odd
+                    if (lowm + (outM & 1) > hway){
+                        // Round to nearest even
+                        // Can overflow into exponent bit, which surprisingly is OK.
+                        // This increment relies on the +outM in the return statement below
+                        outM++;
+                    }
+                }
+            } else {
+                outE = e;
+                outM = m >> 13;
+                // if above halfway or exactly halfway and outM is odd
+                if ((m & 0x1fff) + (outM & 0x1) > 0x1000) {
+                    // Round to nearest even
+                    // Can overflow into exponent bit, which surprisingly is OK.
+                    // This increment relies on the +outM in the return statement below
+                    outM++;
+                }
+            }
+        }
+        // The outM is added here as the +1 increments for outM above can
+        // cause an overflow in the exponent bit which is OK.
+        return (short) ((s << SIGN_SHIFT) | (outE << EXPONENT_SHIFT) + outM);
+    }
+
+    /**
+     * <p>Returns a hexadecimal string representation of the specified half-precision
+     * float value. If the value is a NaN, the result is <code>"NaN"</code>,
+     * otherwise the result follows this format:</p>
+     * <ul>
+     * <li>If the sign is positive, no sign character appears in the result</li>
+     * <li>If the sign is negative, the first character is <code>'-'</code></li>
+     * <li>If the value is inifinity, the string is <code>"Infinity"</code></li>
+     * <li>If the value is 0, the string is <code>"0x0.0p0"</code></li>
+     * <li>If the value has a normalized representation, the exponent and
+     * significand are represented in the string in two fields. The significand
+     * starts with <code>"0x1."</code> followed by its lowercase hexadecimal
+     * representation. Trailing zeroes are removed unless all digits are 0, then
+     * a single zero is used. The significand representation is followed by the
+     * exponent, represented by <code>"p"</code>, itself followed by a decimal
+     * string of the unbiased exponent</li>
+     * <li>If the value has a subnormal representation, the significand starts
+     * with <code>"0x0."</code> followed by its lowercase hexadecimal
+     * representation. Trailing zeroes are removed unless all digits are 0, then
+     * a single zero is used. The significand representation is followed by the
+     * exponent, represented by <code>"p-14"</code></li>
+     * </ul>
+     *
+     * @param h A half-precision float value
+     * @return A hexadecimal string representation of the specified value
+     *
+     * @hide
+     */
+    public static String toHexString(short h) {
+        StringBuilder o = new StringBuilder();
+
+        int bits = h & 0xffff;
+        int s = (bits >>> SIGN_SHIFT    );
+        int e = (bits >>> EXPONENT_SHIFT) & SHIFTED_EXPONENT_MASK;
+        int m = (bits                   ) & SIGNIFICAND_MASK;
+
+        if (e == 0x1f) { // Infinite or NaN
+            if (m == 0) {
+                if (s != 0) o.append('-');
+                o.append("Infinity");
+            } else {
+                o.append("NaN");
+            }
+        } else {
+            if (s == 1) o.append('-');
+            if (e == 0) {
+                if (m == 0) {
+                    o.append("0x0.0p0");
+                } else {
+                    o.append("0x0.");
+                    String significand = Integer.toHexString(m);
+                    o.append(significand.replaceFirst("0{2,}$", ""));
+                    o.append("p-14");
+                }
+            } else {
+                o.append("0x1.");
+                String significand = Integer.toHexString(m);
+                o.append(significand.replaceFirst("0{2,}$", ""));
+                o.append('p');
+                o.append(Integer.toString(e - EXPONENT_BIAS));
+            }
+        }
+
+        return o.toString();
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
index 14b5a4f..4e7dc5d 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -15,7 +15,7 @@
  */
 package libcore.util;
 
-import com.android.ravenwood.common.RavenwoodRuntimeNative;
+import com.android.ravenwood.RavenwoodRuntimeNative;
 
 import java.lang.ref.Cleaner;
 import java.lang.ref.Reference;
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index c804928..f5cb019f 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -245,7 +245,7 @@
     g_StructStat = findClass(env, "android/system/StructStat");
     g_StructTimespecClass = findClass(env, "android/system/StructTimespec");
 
-    jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/common/RavenwoodRuntimeNative",
+    jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/RavenwoodRuntimeNative",
             sMethods, NELEM(sMethods));
     if (res < 0) {
         return res;
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
deleted file mode 100644
index 3332e24..0000000
--- a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
+++ /dev/null
@@ -1,444 +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.ravenwood.runtimetest;
-
-// Copied from libcore/luni/src/test/java/libcore/android/system/OsConstantsTest.java
-
-import static android.system.OsConstants.CAP_TO_INDEX;
-import static android.system.OsConstants.CAP_TO_MASK;
-import static android.system.OsConstants.S_ISBLK;
-import static android.system.OsConstants.S_ISCHR;
-import static android.system.OsConstants.S_ISDIR;
-import static android.system.OsConstants.S_ISFIFO;
-import static android.system.OsConstants.S_ISLNK;
-import static android.system.OsConstants.S_ISREG;
-import static android.system.OsConstants.S_ISSOCK;
-import static android.system.OsConstants.WCOREDUMP;
-import static android.system.OsConstants.WEXITSTATUS;
-import static android.system.OsConstants.WIFEXITED;
-import static android.system.OsConstants.WIFSIGNALED;
-import static android.system.OsConstants.WIFSTOPPED;
-import static android.system.OsConstants.WSTOPSIG;
-import static android.system.OsConstants.WTERMSIG;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.system.OsConstants;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class OsConstantsTest {
-
-    // http://b/15602893
-    @Test
-    public void testBug15602893() {
-        assertTrue(OsConstants.RT_SCOPE_HOST > 0);
-        assertTrue(OsConstants.RT_SCOPE_LINK > 0);
-        assertTrue(OsConstants.RT_SCOPE_SITE > 0);
-
-        assertTrue(OsConstants.IFA_F_TENTATIVE > 0);
-    }
-
-    // introduced for http://b/30402085
-    @Test
-    public void testTcpUserTimeoutIsDefined() {
-        assertTrue(OsConstants.TCP_USER_TIMEOUT > 0);
-    }
-
-    /**
-     * Verifies equality assertions given in the documentation for
-     * {@link OsConstants#SOCK_CLOEXEC} and {@link OsConstants#SOCK_NONBLOCK}.
-     */
-    @Test
-    public void testConstantsEqual() {
-        assertEquals(OsConstants.O_CLOEXEC,  OsConstants.SOCK_CLOEXEC);
-        assertEquals(OsConstants.O_NONBLOCK, OsConstants.SOCK_NONBLOCK);
-    }
-
-    @Test
-    public void test_CAP_constants() {
-        assertEquals(0,  OsConstants.CAP_CHOWN);
-        assertEquals(1,  OsConstants.CAP_DAC_OVERRIDE);
-        assertEquals(2,  OsConstants.CAP_DAC_READ_SEARCH);
-        assertEquals(3,  OsConstants.CAP_FOWNER);
-        assertEquals(4,  OsConstants.CAP_FSETID);
-        assertEquals(5,  OsConstants.CAP_KILL);
-        assertEquals(6,  OsConstants.CAP_SETGID);
-        assertEquals(7,  OsConstants.CAP_SETUID);
-        assertEquals(8,  OsConstants.CAP_SETPCAP);
-        assertEquals(9,  OsConstants.CAP_LINUX_IMMUTABLE);
-        assertEquals(10, OsConstants.CAP_NET_BIND_SERVICE);
-        assertEquals(11, OsConstants.CAP_NET_BROADCAST);
-        assertEquals(12, OsConstants.CAP_NET_ADMIN);
-        assertEquals(13, OsConstants.CAP_NET_RAW);
-        assertEquals(14, OsConstants.CAP_IPC_LOCK);
-        assertEquals(15, OsConstants.CAP_IPC_OWNER);
-        assertEquals(16, OsConstants.CAP_SYS_MODULE);
-        assertEquals(17, OsConstants.CAP_SYS_RAWIO);
-        assertEquals(18, OsConstants.CAP_SYS_CHROOT);
-        assertEquals(19, OsConstants.CAP_SYS_PTRACE);
-        assertEquals(20, OsConstants.CAP_SYS_PACCT);
-        assertEquals(21, OsConstants.CAP_SYS_ADMIN);
-        assertEquals(22, OsConstants.CAP_SYS_BOOT);
-        assertEquals(23, OsConstants.CAP_SYS_NICE);
-        assertEquals(24, OsConstants.CAP_SYS_RESOURCE);
-        assertEquals(25, OsConstants.CAP_SYS_TIME);
-        assertEquals(26, OsConstants.CAP_SYS_TTY_CONFIG);
-        assertEquals(27, OsConstants.CAP_MKNOD);
-        assertEquals(28, OsConstants.CAP_LEASE);
-        assertEquals(29, OsConstants.CAP_AUDIT_WRITE);
-        assertEquals(30, OsConstants.CAP_AUDIT_CONTROL);
-        assertEquals(31, OsConstants.CAP_SETFCAP);
-        assertEquals(32, OsConstants.CAP_MAC_OVERRIDE);
-        assertEquals(33, OsConstants.CAP_MAC_ADMIN);
-        assertEquals(34, OsConstants.CAP_SYSLOG);
-        assertEquals(35, OsConstants.CAP_WAKE_ALARM);
-        assertEquals(36, OsConstants.CAP_BLOCK_SUSPEND);
-        // last constant
-        assertEquals(40, OsConstants.CAP_LAST_CAP);
-    }
-
-    @Test
-    public void test_CAP_TO_INDEX() {
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_CHOWN));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_OVERRIDE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_READ_SEARCH));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FOWNER));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FSETID));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_KILL));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETGID));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETUID));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETPCAP));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LINUX_IMMUTABLE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BIND_SERVICE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BROADCAST));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_ADMIN));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_RAW));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_LOCK));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_OWNER));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_MODULE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RAWIO));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_CHROOT));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PTRACE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PACCT));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_ADMIN));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_BOOT));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_NICE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RESOURCE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TIME));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TTY_CONFIG));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_MKNOD));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LEASE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_WRITE));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_CONTROL));
-        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETFCAP));
-        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_OVERRIDE));
-        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_ADMIN));
-        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_SYSLOG));
-        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_WAKE_ALARM));
-        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_BLOCK_SUSPEND));
-    }
-
-    @Test
-    public void test_CAP_TO_MASK() {
-        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_CHOWN));
-        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_DAC_OVERRIDE));
-        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_DAC_READ_SEARCH));
-        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_FOWNER));
-        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_FSETID));
-        assertEquals(1 << 5,  CAP_TO_MASK(OsConstants.CAP_KILL));
-        assertEquals(1 << 6,  CAP_TO_MASK(OsConstants.CAP_SETGID));
-        assertEquals(1 << 7,  CAP_TO_MASK(OsConstants.CAP_SETUID));
-        assertEquals(1 << 8,  CAP_TO_MASK(OsConstants.CAP_SETPCAP));
-        assertEquals(1 << 9,  CAP_TO_MASK(OsConstants.CAP_LINUX_IMMUTABLE));
-        assertEquals(1 << 10, CAP_TO_MASK(OsConstants.CAP_NET_BIND_SERVICE));
-        assertEquals(1 << 11, CAP_TO_MASK(OsConstants.CAP_NET_BROADCAST));
-        assertEquals(1 << 12, CAP_TO_MASK(OsConstants.CAP_NET_ADMIN));
-        assertEquals(1 << 13, CAP_TO_MASK(OsConstants.CAP_NET_RAW));
-        assertEquals(1 << 14, CAP_TO_MASK(OsConstants.CAP_IPC_LOCK));
-        assertEquals(1 << 15, CAP_TO_MASK(OsConstants.CAP_IPC_OWNER));
-        assertEquals(1 << 16, CAP_TO_MASK(OsConstants.CAP_SYS_MODULE));
-        assertEquals(1 << 17, CAP_TO_MASK(OsConstants.CAP_SYS_RAWIO));
-        assertEquals(1 << 18, CAP_TO_MASK(OsConstants.CAP_SYS_CHROOT));
-        assertEquals(1 << 19, CAP_TO_MASK(OsConstants.CAP_SYS_PTRACE));
-        assertEquals(1 << 20, CAP_TO_MASK(OsConstants.CAP_SYS_PACCT));
-        assertEquals(1 << 21, CAP_TO_MASK(OsConstants.CAP_SYS_ADMIN));
-        assertEquals(1 << 22, CAP_TO_MASK(OsConstants.CAP_SYS_BOOT));
-        assertEquals(1 << 23, CAP_TO_MASK(OsConstants.CAP_SYS_NICE));
-        assertEquals(1 << 24, CAP_TO_MASK(OsConstants.CAP_SYS_RESOURCE));
-        assertEquals(1 << 25, CAP_TO_MASK(OsConstants.CAP_SYS_TIME));
-        assertEquals(1 << 26, CAP_TO_MASK(OsConstants.CAP_SYS_TTY_CONFIG));
-        assertEquals(1 << 27, CAP_TO_MASK(OsConstants.CAP_MKNOD));
-        assertEquals(1 << 28, CAP_TO_MASK(OsConstants.CAP_LEASE));
-        assertEquals(1 << 29, CAP_TO_MASK(OsConstants.CAP_AUDIT_WRITE));
-        assertEquals(1 << 30, CAP_TO_MASK(OsConstants.CAP_AUDIT_CONTROL));
-        assertEquals(1 << 31, CAP_TO_MASK(OsConstants.CAP_SETFCAP));
-        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_MAC_OVERRIDE));
-        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_MAC_ADMIN));
-        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_SYSLOG));
-        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_WAKE_ALARM));
-        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_BLOCK_SUSPEND));
-    }
-
-    @Test
-    public void test_S_ISLNK() {
-        assertTrue(S_ISLNK(OsConstants.S_IFLNK));
-
-        assertFalse(S_ISLNK(OsConstants.S_IFBLK));
-        assertFalse(S_ISLNK(OsConstants.S_IFCHR));
-        assertFalse(S_ISLNK(OsConstants.S_IFDIR));
-        assertFalse(S_ISLNK(OsConstants.S_IFIFO));
-        assertFalse(S_ISLNK(OsConstants.S_IFMT));
-        assertFalse(S_ISLNK(OsConstants.S_IFREG));
-        assertFalse(S_ISLNK(OsConstants.S_IFSOCK));
-        assertFalse(S_ISLNK(OsConstants.S_IRGRP));
-        assertFalse(S_ISLNK(OsConstants.S_IROTH));
-        assertFalse(S_ISLNK(OsConstants.S_IRUSR));
-        assertFalse(S_ISLNK(OsConstants.S_IRWXG));
-        assertFalse(S_ISLNK(OsConstants.S_IRWXO));
-        assertFalse(S_ISLNK(OsConstants.S_IRWXU));
-        assertFalse(S_ISLNK(OsConstants.S_ISGID));
-        assertFalse(S_ISLNK(OsConstants.S_ISUID));
-        assertFalse(S_ISLNK(OsConstants.S_ISVTX));
-        assertFalse(S_ISLNK(OsConstants.S_IWGRP));
-        assertFalse(S_ISLNK(OsConstants.S_IWOTH));
-        assertFalse(S_ISLNK(OsConstants.S_IWUSR));
-        assertFalse(S_ISLNK(OsConstants.S_IXGRP));
-        assertFalse(S_ISLNK(OsConstants.S_IXOTH));
-        assertFalse(S_ISLNK(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISREG() {
-        assertTrue(S_ISREG(OsConstants.S_IFREG));
-
-        assertFalse(S_ISREG(OsConstants.S_IFBLK));
-        assertFalse(S_ISREG(OsConstants.S_IFCHR));
-        assertFalse(S_ISREG(OsConstants.S_IFDIR));
-        assertFalse(S_ISREG(OsConstants.S_IFIFO));
-        assertFalse(S_ISREG(OsConstants.S_IFLNK));
-        assertFalse(S_ISREG(OsConstants.S_IFMT));
-        assertFalse(S_ISREG(OsConstants.S_IFSOCK));
-        assertFalse(S_ISREG(OsConstants.S_IRGRP));
-        assertFalse(S_ISREG(OsConstants.S_IROTH));
-        assertFalse(S_ISREG(OsConstants.S_IRUSR));
-        assertFalse(S_ISREG(OsConstants.S_IRWXG));
-        assertFalse(S_ISREG(OsConstants.S_IRWXO));
-        assertFalse(S_ISREG(OsConstants.S_IRWXU));
-        assertFalse(S_ISREG(OsConstants.S_ISGID));
-        assertFalse(S_ISREG(OsConstants.S_ISUID));
-        assertFalse(S_ISREG(OsConstants.S_ISVTX));
-        assertFalse(S_ISREG(OsConstants.S_IWGRP));
-        assertFalse(S_ISREG(OsConstants.S_IWOTH));
-        assertFalse(S_ISREG(OsConstants.S_IWUSR));
-        assertFalse(S_ISREG(OsConstants.S_IXGRP));
-        assertFalse(S_ISREG(OsConstants.S_IXOTH));
-        assertFalse(S_ISREG(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISDIR() {
-        assertTrue(S_ISDIR(OsConstants.S_IFDIR));
-
-        assertFalse(S_ISDIR(OsConstants.S_IFBLK));
-        assertFalse(S_ISDIR(OsConstants.S_IFCHR));
-        assertFalse(S_ISDIR(OsConstants.S_IFIFO));
-        assertFalse(S_ISDIR(OsConstants.S_IFLNK));
-        assertFalse(S_ISDIR(OsConstants.S_IFMT));
-        assertFalse(S_ISDIR(OsConstants.S_IFREG));
-        assertFalse(S_ISDIR(OsConstants.S_IFSOCK));
-        assertFalse(S_ISDIR(OsConstants.S_IRGRP));
-        assertFalse(S_ISDIR(OsConstants.S_IROTH));
-        assertFalse(S_ISDIR(OsConstants.S_IRUSR));
-        assertFalse(S_ISDIR(OsConstants.S_IRWXG));
-        assertFalse(S_ISDIR(OsConstants.S_IRWXO));
-        assertFalse(S_ISDIR(OsConstants.S_IRWXU));
-        assertFalse(S_ISDIR(OsConstants.S_ISGID));
-        assertFalse(S_ISDIR(OsConstants.S_ISUID));
-        assertFalse(S_ISDIR(OsConstants.S_ISVTX));
-        assertFalse(S_ISDIR(OsConstants.S_IWGRP));
-        assertFalse(S_ISDIR(OsConstants.S_IWOTH));
-        assertFalse(S_ISDIR(OsConstants.S_IWUSR));
-        assertFalse(S_ISDIR(OsConstants.S_IXGRP));
-        assertFalse(S_ISDIR(OsConstants.S_IXOTH));
-        assertFalse(S_ISDIR(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISCHR() {
-        assertTrue(S_ISCHR(OsConstants.S_IFCHR));
-
-        assertFalse(S_ISCHR(OsConstants.S_IFBLK));
-        assertFalse(S_ISCHR(OsConstants.S_IFDIR));
-        assertFalse(S_ISCHR(OsConstants.S_IFIFO));
-        assertFalse(S_ISCHR(OsConstants.S_IFLNK));
-        assertFalse(S_ISCHR(OsConstants.S_IFMT));
-        assertFalse(S_ISCHR(OsConstants.S_IFREG));
-        assertFalse(S_ISCHR(OsConstants.S_IFSOCK));
-        assertFalse(S_ISCHR(OsConstants.S_IRGRP));
-        assertFalse(S_ISCHR(OsConstants.S_IROTH));
-        assertFalse(S_ISCHR(OsConstants.S_IRUSR));
-        assertFalse(S_ISCHR(OsConstants.S_IRWXG));
-        assertFalse(S_ISCHR(OsConstants.S_IRWXO));
-        assertFalse(S_ISCHR(OsConstants.S_IRWXU));
-        assertFalse(S_ISCHR(OsConstants.S_ISGID));
-        assertFalse(S_ISCHR(OsConstants.S_ISUID));
-        assertFalse(S_ISCHR(OsConstants.S_ISVTX));
-        assertFalse(S_ISCHR(OsConstants.S_IWGRP));
-        assertFalse(S_ISCHR(OsConstants.S_IWOTH));
-        assertFalse(S_ISCHR(OsConstants.S_IWUSR));
-        assertFalse(S_ISCHR(OsConstants.S_IXGRP));
-        assertFalse(S_ISCHR(OsConstants.S_IXOTH));
-        assertFalse(S_ISCHR(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISBLK() {
-        assertTrue (S_ISBLK(OsConstants.S_IFBLK));
-
-        assertFalse(S_ISBLK(OsConstants.S_IFCHR));
-        assertFalse(S_ISBLK(OsConstants.S_IFDIR));
-        assertFalse(S_ISBLK(OsConstants.S_IFIFO));
-        assertFalse(S_ISBLK(OsConstants.S_IFLNK));
-        assertFalse(S_ISBLK(OsConstants.S_IFMT));
-        assertFalse(S_ISBLK(OsConstants.S_IFREG));
-        assertFalse(S_ISBLK(OsConstants.S_IFSOCK));
-        assertFalse(S_ISBLK(OsConstants.S_IRGRP));
-        assertFalse(S_ISBLK(OsConstants.S_IROTH));
-        assertFalse(S_ISBLK(OsConstants.S_IRUSR));
-        assertFalse(S_ISBLK(OsConstants.S_IRWXG));
-        assertFalse(S_ISBLK(OsConstants.S_IRWXO));
-        assertFalse(S_ISBLK(OsConstants.S_IRWXU));
-        assertFalse(S_ISBLK(OsConstants.S_ISGID));
-        assertFalse(S_ISBLK(OsConstants.S_ISUID));
-        assertFalse(S_ISBLK(OsConstants.S_ISVTX));
-        assertFalse(S_ISBLK(OsConstants.S_IWGRP));
-        assertFalse(S_ISBLK(OsConstants.S_IWOTH));
-        assertFalse(S_ISBLK(OsConstants.S_IWUSR));
-        assertFalse(S_ISBLK(OsConstants.S_IXGRP));
-        assertFalse(S_ISBLK(OsConstants.S_IXOTH));
-        assertFalse(S_ISBLK(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISFIFO() {
-        assertTrue(S_ISFIFO(OsConstants.S_IFIFO));
-
-        assertFalse(S_ISFIFO(OsConstants.S_IFBLK));
-        assertFalse(S_ISFIFO(OsConstants.S_IFCHR));
-        assertFalse(S_ISFIFO(OsConstants.S_IFDIR));
-        assertFalse(S_ISFIFO(OsConstants.S_IFLNK));
-        assertFalse(S_ISFIFO(OsConstants.S_IFMT));
-        assertFalse(S_ISFIFO(OsConstants.S_IFREG));
-        assertFalse(S_ISFIFO(OsConstants.S_IFSOCK));
-        assertFalse(S_ISFIFO(OsConstants.S_IRGRP));
-        assertFalse(S_ISFIFO(OsConstants.S_IROTH));
-        assertFalse(S_ISFIFO(OsConstants.S_IRUSR));
-        assertFalse(S_ISFIFO(OsConstants.S_IRWXG));
-        assertFalse(S_ISFIFO(OsConstants.S_IRWXO));
-        assertFalse(S_ISFIFO(OsConstants.S_IRWXU));
-        assertFalse(S_ISFIFO(OsConstants.S_ISGID));
-        assertFalse(S_ISFIFO(OsConstants.S_ISUID));
-        assertFalse(S_ISFIFO(OsConstants.S_ISVTX));
-        assertFalse(S_ISFIFO(OsConstants.S_IWGRP));
-        assertFalse(S_ISFIFO(OsConstants.S_IWOTH));
-        assertFalse(S_ISFIFO(OsConstants.S_IWUSR));
-        assertFalse(S_ISFIFO(OsConstants.S_IXGRP));
-        assertFalse(S_ISFIFO(OsConstants.S_IXOTH));
-        assertFalse(S_ISFIFO(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_S_ISSOCK() {
-        assertTrue(S_ISSOCK(OsConstants.S_IFSOCK));
-
-        assertFalse(S_ISSOCK(OsConstants.S_IFBLK));
-        assertFalse(S_ISSOCK(OsConstants.S_IFCHR));
-        assertFalse(S_ISSOCK(OsConstants.S_IFDIR));
-        assertFalse(S_ISSOCK(OsConstants.S_IFIFO));
-        assertFalse(S_ISSOCK(OsConstants.S_IFLNK));
-        assertFalse(S_ISSOCK(OsConstants.S_IFMT));
-        assertFalse(S_ISSOCK(OsConstants.S_IFREG));
-        assertFalse(S_ISSOCK(OsConstants.S_IRGRP));
-        assertFalse(S_ISSOCK(OsConstants.S_IROTH));
-        assertFalse(S_ISSOCK(OsConstants.S_IRUSR));
-        assertFalse(S_ISSOCK(OsConstants.S_IRWXG));
-        assertFalse(S_ISSOCK(OsConstants.S_IRWXO));
-        assertFalse(S_ISSOCK(OsConstants.S_IRWXU));
-        assertFalse(S_ISSOCK(OsConstants.S_ISGID));
-        assertFalse(S_ISSOCK(OsConstants.S_ISUID));
-        assertFalse(S_ISSOCK(OsConstants.S_ISVTX));
-        assertFalse(S_ISSOCK(OsConstants.S_IWGRP));
-        assertFalse(S_ISSOCK(OsConstants.S_IWOTH));
-        assertFalse(S_ISSOCK(OsConstants.S_IWUSR));
-        assertFalse(S_ISSOCK(OsConstants.S_IXGRP));
-        assertFalse(S_ISSOCK(OsConstants.S_IXOTH));
-        assertFalse(S_ISSOCK(OsConstants.S_IXUSR));
-    }
-
-    @Test
-    public void test_WEXITSTATUS() {
-        assertEquals(0, WEXITSTATUS(0x0000));
-        assertEquals(0, WEXITSTATUS(0x00DE));
-        assertEquals(0xF0, WEXITSTATUS(0xF000));
-        assertEquals(0xAB, WEXITSTATUS(0xAB12));
-    }
-
-    @Test
-    public void test_WCOREDUMP() {
-        assertFalse(WCOREDUMP(0));
-        assertTrue(WCOREDUMP(0x80));
-    }
-
-    @Test
-    public void test_WTERMSIG() {
-        assertEquals(0, WTERMSIG(0));
-        assertEquals(0x7f, WTERMSIG(0x7f));
-    }
-
-    @Test
-    public void test_WSTOPSIG() {
-        assertEquals(0, WSTOPSIG(0x0000));
-        assertEquals(0, WSTOPSIG(0x00DE));
-        assertEquals(0xF0, WSTOPSIG(0xF000));
-        assertEquals(0xAB, WSTOPSIG(0xAB12));
-    }
-
-
-    @Test
-    public void test_WIFEXITED() {
-        assertTrue(WIFEXITED(0));
-        assertFalse(WIFEXITED(0x7f));
-    }
-
-    @Test
-    public void test_WIFSTOPPED() {
-        assertFalse(WIFSTOPPED(0));
-        assertTrue(WIFSTOPPED(0x7f));
-    }
-
-    @Test
-    public void test_WIFSIGNALED() {
-        assertFalse(WIFSIGNALED(0));
-        assertTrue(WIFSIGNALED(1));
-    }
-}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
deleted file mode 100644
index 05275b2..0000000
--- a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
+++ /dev/null
@@ -1,225 +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.ravenwood.runtimetest;
-
-import static android.system.OsConstants.S_ISBLK;
-import static android.system.OsConstants.S_ISCHR;
-import static android.system.OsConstants.S_ISDIR;
-import static android.system.OsConstants.S_ISFIFO;
-import static android.system.OsConstants.S_ISLNK;
-import static android.system.OsConstants.S_ISREG;
-import static android.system.OsConstants.S_ISSOCK;
-
-import static org.junit.Assert.assertEquals;
-
-import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
-
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructStat;
-import android.system.StructTimespec;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.ravenwood.common.JvmWorkaround;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.RandomAccessFile;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.attribute.FileTime;
-import java.nio.file.attribute.PosixFileAttributes;
-import java.nio.file.attribute.PosixFilePermission;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-@RunWith(AndroidJUnit4.class)
-public class OsTest {
-    public interface ConsumerWithThrow<T> {
-        void accept(T var1) throws Exception;
-    }
-
-    private void withTestFileFD(ConsumerWithThrow<FileDescriptor> consumer) throws Exception {
-        File file = File.createTempFile("osTest", "bin");
-        try (var raf = new RandomAccessFile(file, "rw")) {
-            var fd = raf.getFD();
-
-            try (var os = new FileOutputStream(fd)) {
-                os.write(1);
-                os.write(2);
-                os.write(3);
-                os.write(4);
-
-                consumer.accept(fd);
-            }
-        }
-    }
-
-    private void withTestFile(ConsumerWithThrow<Path> consumer) throws Exception {
-        var path = Files.createTempFile("osTest", "bin");
-        try (var os = Files.newOutputStream(path)) {
-            os.write(1);
-            os.write(2);
-            os.write(3);
-            os.write(4);
-        }
-        consumer.accept(path);
-    }
-
-    @Test
-    public void testLseek() throws Exception {
-        withTestFileFD((fd) -> {
-            assertEquals(4, Os.lseek(fd, 4, OsConstants.SEEK_SET));
-            assertEquals(4, Os.lseek(fd, 0, OsConstants.SEEK_CUR));
-            assertEquals(6, Os.lseek(fd, 2, OsConstants.SEEK_CUR));
-        });
-    }
-
-    @Test
-    public void testDup() throws Exception {
-        withTestFileFD((fd) -> {
-            var dup = Os.dup(fd);
-
-            checkAreDup(fd, dup);
-        });
-    }
-
-    @Test
-    public void testPipe2() throws Exception {
-        var fds = Os.pipe2(0);
-
-        write(fds[1], 123);
-        assertEquals(123, read(fds[0]));
-    }
-
-    @Test
-    public void testFcntlInt() throws Exception {
-        withTestFileFD((fd) -> {
-            var dupInt = Os.fcntlInt(fd, 0, 0);
-
-            var dup = new FileDescriptor();
-            JvmWorkaround.getInstance().setFdInt(dup, dupInt);
-
-            checkAreDup(fd, dup);
-        });
-    }
-
-    @Test
-    public void testStat() throws Exception {
-        withTestFile(path -> {
-            var attr = Files.readAttributes(path, PosixFileAttributes.class);
-            var stat = Os.stat(path.toAbsolutePath().toString());
-            assertAttributesEqual(attr, stat);
-        });
-    }
-
-    @Test
-    public void testLstat() throws Exception {
-        withTestFile(path -> {
-            // Create a symbolic link
-            var lnk = Files.createTempFile("osTest", "lnk");
-            Files.delete(lnk);
-            Files.createSymbolicLink(lnk, path);
-
-            // Test lstat
-            var attr = Files.readAttributes(lnk, PosixFileAttributes.class, NOFOLLOW_LINKS);
-            var stat = Os.lstat(lnk.toAbsolutePath().toString());
-            assertAttributesEqual(attr, stat);
-
-            // Test stat
-            var followAttr = Files.readAttributes(lnk, PosixFileAttributes.class);
-            var followStat = Os.stat(lnk.toAbsolutePath().toString());
-            assertAttributesEqual(followAttr, followStat);
-        });
-    }
-
-    @Test
-    public void testFstat() throws Exception {
-        withTestFile(path -> {
-            var attr = Files.readAttributes(path, PosixFileAttributes.class);
-            try (var raf = new RandomAccessFile(path.toFile(), "r")) {
-                var fd = raf.getFD();
-                var stat = Os.fstat(fd);
-                assertAttributesEqual(attr, stat);
-            }
-        });
-    }
-
-    // Verify StructStat values from libcore against native JVM PosixFileAttributes
-    private static void assertAttributesEqual(PosixFileAttributes attr, StructStat stat) {
-        assertEquals(attr.lastModifiedTime(), convertTimespecToFileTime(stat.st_mtim));
-        assertEquals(attr.size(), stat.st_size);
-        assertEquals(attr.isDirectory(), S_ISDIR(stat.st_mode));
-        assertEquals(attr.isRegularFile(), S_ISREG(stat.st_mode));
-        assertEquals(attr.isSymbolicLink(), S_ISLNK(stat.st_mode));
-        assertEquals(attr.isOther(), S_ISCHR(stat.st_mode)
-                || S_ISBLK(stat.st_mode) || S_ISFIFO(stat.st_mode) || S_ISSOCK(stat.st_mode));
-        assertEquals(attr.permissions(), convertModeToPosixPerms(stat.st_mode));
-
-    }
-
-    private static FileTime convertTimespecToFileTime(StructTimespec ts) {
-        var nanos = TimeUnit.SECONDS.toNanos(ts.tv_sec);
-        nanos += ts.tv_nsec;
-        return FileTime.from(nanos, TimeUnit.NANOSECONDS);
-    }
-
-    private static Set<PosixFilePermission> convertModeToPosixPerms(int mode) {
-        var set = new HashSet<PosixFilePermission>();
-        if ((mode & OsConstants.S_IRUSR) != 0) set.add(PosixFilePermission.OWNER_READ);
-        if ((mode & OsConstants.S_IWUSR) != 0) set.add(PosixFilePermission.OWNER_WRITE);
-        if ((mode & OsConstants.S_IXUSR) != 0) set.add(PosixFilePermission.OWNER_EXECUTE);
-        if ((mode & OsConstants.S_IRGRP) != 0) set.add(PosixFilePermission.GROUP_READ);
-        if ((mode & OsConstants.S_IWGRP) != 0) set.add(PosixFilePermission.GROUP_WRITE);
-        if ((mode & OsConstants.S_IXGRP) != 0) set.add(PosixFilePermission.GROUP_EXECUTE);
-        if ((mode & OsConstants.S_IROTH) != 0) set.add(PosixFilePermission.OTHERS_READ);
-        if ((mode & OsConstants.S_IWOTH) != 0) set.add(PosixFilePermission.OTHERS_WRITE);
-        if ((mode & OsConstants.S_IXOTH) != 0) set.add(PosixFilePermission.OTHERS_EXECUTE);
-        return set;
-    }
-
-    private static void write(FileDescriptor fd, int oneByte) throws Exception {
-        // Create a dup to avoid closing the FD.
-        try (var dup = new FileOutputStream(Os.dup(fd))) {
-            dup.write(oneByte);
-        }
-    }
-
-    private static int read(FileDescriptor fd) throws Exception {
-        // Create a dup to avoid closing the FD.
-        try (var dup = new FileInputStream(Os.dup(fd))) {
-            return dup.read();
-        }
-    }
-
-    private static void checkAreDup(FileDescriptor fd1, FileDescriptor fd2) throws Exception {
-        assertEquals(4, Os.lseek(fd1, 4, OsConstants.SEEK_SET));
-        assertEquals(4, Os.lseek(fd1, 0, OsConstants.SEEK_CUR));
-
-        // Dup'ed FD shares the same position.
-        assertEquals(4, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
-
-        assertEquals(6, Os.lseek(fd1, 2, OsConstants.SEEK_CUR));
-        assertEquals(6, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
-    }
-}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
new file mode 100644
index 0000000..633ed4e
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java
@@ -0,0 +1,444 @@
+/*
+ * 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.runtimetest;
+
+// Copied from libcore/luni/src/test/java/libcore/android/system/OsConstantsTest.java
+
+import static android.system.OsConstants.CAP_TO_INDEX;
+import static android.system.OsConstants.CAP_TO_MASK;
+import static android.system.OsConstants.S_ISBLK;
+import static android.system.OsConstants.S_ISCHR;
+import static android.system.OsConstants.S_ISDIR;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.S_ISSOCK;
+import static android.system.OsConstants.WCOREDUMP;
+import static android.system.OsConstants.WEXITSTATUS;
+import static android.system.OsConstants.WIFEXITED;
+import static android.system.OsConstants.WIFSIGNALED;
+import static android.system.OsConstants.WIFSTOPPED;
+import static android.system.OsConstants.WSTOPSIG;
+import static android.system.OsConstants.WTERMSIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.system.OsConstants;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class OsConstantsTest {
+
+    // http://b/15602893
+    @Test
+    public void testBug15602893() {
+        assertTrue(OsConstants.RT_SCOPE_HOST > 0);
+        assertTrue(OsConstants.RT_SCOPE_LINK > 0);
+        assertTrue(OsConstants.RT_SCOPE_SITE > 0);
+
+        assertTrue(OsConstants.IFA_F_TENTATIVE > 0);
+    }
+
+    // introduced for http://b/30402085
+    @Test
+    public void testTcpUserTimeoutIsDefined() {
+        assertTrue(OsConstants.TCP_USER_TIMEOUT > 0);
+    }
+
+    /**
+     * Verifies equality assertions given in the documentation for
+     * {@link OsConstants#SOCK_CLOEXEC} and {@link OsConstants#SOCK_NONBLOCK}.
+     */
+    @Test
+    public void testConstantsEqual() {
+        assertEquals(OsConstants.O_CLOEXEC,  OsConstants.SOCK_CLOEXEC);
+        assertEquals(OsConstants.O_NONBLOCK, OsConstants.SOCK_NONBLOCK);
+    }
+
+    @Test
+    public void test_CAP_constants() {
+        assertEquals(0,  OsConstants.CAP_CHOWN);
+        assertEquals(1,  OsConstants.CAP_DAC_OVERRIDE);
+        assertEquals(2,  OsConstants.CAP_DAC_READ_SEARCH);
+        assertEquals(3,  OsConstants.CAP_FOWNER);
+        assertEquals(4,  OsConstants.CAP_FSETID);
+        assertEquals(5,  OsConstants.CAP_KILL);
+        assertEquals(6,  OsConstants.CAP_SETGID);
+        assertEquals(7,  OsConstants.CAP_SETUID);
+        assertEquals(8,  OsConstants.CAP_SETPCAP);
+        assertEquals(9,  OsConstants.CAP_LINUX_IMMUTABLE);
+        assertEquals(10, OsConstants.CAP_NET_BIND_SERVICE);
+        assertEquals(11, OsConstants.CAP_NET_BROADCAST);
+        assertEquals(12, OsConstants.CAP_NET_ADMIN);
+        assertEquals(13, OsConstants.CAP_NET_RAW);
+        assertEquals(14, OsConstants.CAP_IPC_LOCK);
+        assertEquals(15, OsConstants.CAP_IPC_OWNER);
+        assertEquals(16, OsConstants.CAP_SYS_MODULE);
+        assertEquals(17, OsConstants.CAP_SYS_RAWIO);
+        assertEquals(18, OsConstants.CAP_SYS_CHROOT);
+        assertEquals(19, OsConstants.CAP_SYS_PTRACE);
+        assertEquals(20, OsConstants.CAP_SYS_PACCT);
+        assertEquals(21, OsConstants.CAP_SYS_ADMIN);
+        assertEquals(22, OsConstants.CAP_SYS_BOOT);
+        assertEquals(23, OsConstants.CAP_SYS_NICE);
+        assertEquals(24, OsConstants.CAP_SYS_RESOURCE);
+        assertEquals(25, OsConstants.CAP_SYS_TIME);
+        assertEquals(26, OsConstants.CAP_SYS_TTY_CONFIG);
+        assertEquals(27, OsConstants.CAP_MKNOD);
+        assertEquals(28, OsConstants.CAP_LEASE);
+        assertEquals(29, OsConstants.CAP_AUDIT_WRITE);
+        assertEquals(30, OsConstants.CAP_AUDIT_CONTROL);
+        assertEquals(31, OsConstants.CAP_SETFCAP);
+        assertEquals(32, OsConstants.CAP_MAC_OVERRIDE);
+        assertEquals(33, OsConstants.CAP_MAC_ADMIN);
+        assertEquals(34, OsConstants.CAP_SYSLOG);
+        assertEquals(35, OsConstants.CAP_WAKE_ALARM);
+        assertEquals(36, OsConstants.CAP_BLOCK_SUSPEND);
+        // last constant
+        assertEquals(40, OsConstants.CAP_LAST_CAP);
+    }
+
+    @Test
+    public void test_CAP_TO_INDEX() {
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_CHOWN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_OVERRIDE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_READ_SEARCH));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FOWNER));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FSETID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_KILL));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETGID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETUID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETPCAP));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LINUX_IMMUTABLE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BIND_SERVICE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BROADCAST));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_ADMIN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_RAW));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_LOCK));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_OWNER));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_MODULE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RAWIO));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_CHROOT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PTRACE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PACCT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_ADMIN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_BOOT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_NICE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RESOURCE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TIME));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TTY_CONFIG));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_MKNOD));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LEASE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_WRITE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_CONTROL));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETFCAP));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_OVERRIDE));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_ADMIN));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_SYSLOG));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_WAKE_ALARM));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_BLOCK_SUSPEND));
+    }
+
+    @Test
+    public void test_CAP_TO_MASK() {
+        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_CHOWN));
+        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_DAC_OVERRIDE));
+        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_DAC_READ_SEARCH));
+        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_FOWNER));
+        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_FSETID));
+        assertEquals(1 << 5,  CAP_TO_MASK(OsConstants.CAP_KILL));
+        assertEquals(1 << 6,  CAP_TO_MASK(OsConstants.CAP_SETGID));
+        assertEquals(1 << 7,  CAP_TO_MASK(OsConstants.CAP_SETUID));
+        assertEquals(1 << 8,  CAP_TO_MASK(OsConstants.CAP_SETPCAP));
+        assertEquals(1 << 9,  CAP_TO_MASK(OsConstants.CAP_LINUX_IMMUTABLE));
+        assertEquals(1 << 10, CAP_TO_MASK(OsConstants.CAP_NET_BIND_SERVICE));
+        assertEquals(1 << 11, CAP_TO_MASK(OsConstants.CAP_NET_BROADCAST));
+        assertEquals(1 << 12, CAP_TO_MASK(OsConstants.CAP_NET_ADMIN));
+        assertEquals(1 << 13, CAP_TO_MASK(OsConstants.CAP_NET_RAW));
+        assertEquals(1 << 14, CAP_TO_MASK(OsConstants.CAP_IPC_LOCK));
+        assertEquals(1 << 15, CAP_TO_MASK(OsConstants.CAP_IPC_OWNER));
+        assertEquals(1 << 16, CAP_TO_MASK(OsConstants.CAP_SYS_MODULE));
+        assertEquals(1 << 17, CAP_TO_MASK(OsConstants.CAP_SYS_RAWIO));
+        assertEquals(1 << 18, CAP_TO_MASK(OsConstants.CAP_SYS_CHROOT));
+        assertEquals(1 << 19, CAP_TO_MASK(OsConstants.CAP_SYS_PTRACE));
+        assertEquals(1 << 20, CAP_TO_MASK(OsConstants.CAP_SYS_PACCT));
+        assertEquals(1 << 21, CAP_TO_MASK(OsConstants.CAP_SYS_ADMIN));
+        assertEquals(1 << 22, CAP_TO_MASK(OsConstants.CAP_SYS_BOOT));
+        assertEquals(1 << 23, CAP_TO_MASK(OsConstants.CAP_SYS_NICE));
+        assertEquals(1 << 24, CAP_TO_MASK(OsConstants.CAP_SYS_RESOURCE));
+        assertEquals(1 << 25, CAP_TO_MASK(OsConstants.CAP_SYS_TIME));
+        assertEquals(1 << 26, CAP_TO_MASK(OsConstants.CAP_SYS_TTY_CONFIG));
+        assertEquals(1 << 27, CAP_TO_MASK(OsConstants.CAP_MKNOD));
+        assertEquals(1 << 28, CAP_TO_MASK(OsConstants.CAP_LEASE));
+        assertEquals(1 << 29, CAP_TO_MASK(OsConstants.CAP_AUDIT_WRITE));
+        assertEquals(1 << 30, CAP_TO_MASK(OsConstants.CAP_AUDIT_CONTROL));
+        assertEquals(1 << 31, CAP_TO_MASK(OsConstants.CAP_SETFCAP));
+        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_MAC_OVERRIDE));
+        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_MAC_ADMIN));
+        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_SYSLOG));
+        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_WAKE_ALARM));
+        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_BLOCK_SUSPEND));
+    }
+
+    @Test
+    public void test_S_ISLNK() {
+        assertTrue(S_ISLNK(OsConstants.S_IFLNK));
+
+        assertFalse(S_ISLNK(OsConstants.S_IFBLK));
+        assertFalse(S_ISLNK(OsConstants.S_IFCHR));
+        assertFalse(S_ISLNK(OsConstants.S_IFDIR));
+        assertFalse(S_ISLNK(OsConstants.S_IFIFO));
+        assertFalse(S_ISLNK(OsConstants.S_IFMT));
+        assertFalse(S_ISLNK(OsConstants.S_IFREG));
+        assertFalse(S_ISLNK(OsConstants.S_IFSOCK));
+        assertFalse(S_ISLNK(OsConstants.S_IRGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IROTH));
+        assertFalse(S_ISLNK(OsConstants.S_IRUSR));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXG));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXO));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXU));
+        assertFalse(S_ISLNK(OsConstants.S_ISGID));
+        assertFalse(S_ISLNK(OsConstants.S_ISUID));
+        assertFalse(S_ISLNK(OsConstants.S_ISVTX));
+        assertFalse(S_ISLNK(OsConstants.S_IWGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IWOTH));
+        assertFalse(S_ISLNK(OsConstants.S_IWUSR));
+        assertFalse(S_ISLNK(OsConstants.S_IXGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IXOTH));
+        assertFalse(S_ISLNK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISREG() {
+        assertTrue(S_ISREG(OsConstants.S_IFREG));
+
+        assertFalse(S_ISREG(OsConstants.S_IFBLK));
+        assertFalse(S_ISREG(OsConstants.S_IFCHR));
+        assertFalse(S_ISREG(OsConstants.S_IFDIR));
+        assertFalse(S_ISREG(OsConstants.S_IFIFO));
+        assertFalse(S_ISREG(OsConstants.S_IFLNK));
+        assertFalse(S_ISREG(OsConstants.S_IFMT));
+        assertFalse(S_ISREG(OsConstants.S_IFSOCK));
+        assertFalse(S_ISREG(OsConstants.S_IRGRP));
+        assertFalse(S_ISREG(OsConstants.S_IROTH));
+        assertFalse(S_ISREG(OsConstants.S_IRUSR));
+        assertFalse(S_ISREG(OsConstants.S_IRWXG));
+        assertFalse(S_ISREG(OsConstants.S_IRWXO));
+        assertFalse(S_ISREG(OsConstants.S_IRWXU));
+        assertFalse(S_ISREG(OsConstants.S_ISGID));
+        assertFalse(S_ISREG(OsConstants.S_ISUID));
+        assertFalse(S_ISREG(OsConstants.S_ISVTX));
+        assertFalse(S_ISREG(OsConstants.S_IWGRP));
+        assertFalse(S_ISREG(OsConstants.S_IWOTH));
+        assertFalse(S_ISREG(OsConstants.S_IWUSR));
+        assertFalse(S_ISREG(OsConstants.S_IXGRP));
+        assertFalse(S_ISREG(OsConstants.S_IXOTH));
+        assertFalse(S_ISREG(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISDIR() {
+        assertTrue(S_ISDIR(OsConstants.S_IFDIR));
+
+        assertFalse(S_ISDIR(OsConstants.S_IFBLK));
+        assertFalse(S_ISDIR(OsConstants.S_IFCHR));
+        assertFalse(S_ISDIR(OsConstants.S_IFIFO));
+        assertFalse(S_ISDIR(OsConstants.S_IFLNK));
+        assertFalse(S_ISDIR(OsConstants.S_IFMT));
+        assertFalse(S_ISDIR(OsConstants.S_IFREG));
+        assertFalse(S_ISDIR(OsConstants.S_IFSOCK));
+        assertFalse(S_ISDIR(OsConstants.S_IRGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IROTH));
+        assertFalse(S_ISDIR(OsConstants.S_IRUSR));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXG));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXO));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXU));
+        assertFalse(S_ISDIR(OsConstants.S_ISGID));
+        assertFalse(S_ISDIR(OsConstants.S_ISUID));
+        assertFalse(S_ISDIR(OsConstants.S_ISVTX));
+        assertFalse(S_ISDIR(OsConstants.S_IWGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IWOTH));
+        assertFalse(S_ISDIR(OsConstants.S_IWUSR));
+        assertFalse(S_ISDIR(OsConstants.S_IXGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IXOTH));
+        assertFalse(S_ISDIR(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISCHR() {
+        assertTrue(S_ISCHR(OsConstants.S_IFCHR));
+
+        assertFalse(S_ISCHR(OsConstants.S_IFBLK));
+        assertFalse(S_ISCHR(OsConstants.S_IFDIR));
+        assertFalse(S_ISCHR(OsConstants.S_IFIFO));
+        assertFalse(S_ISCHR(OsConstants.S_IFLNK));
+        assertFalse(S_ISCHR(OsConstants.S_IFMT));
+        assertFalse(S_ISCHR(OsConstants.S_IFREG));
+        assertFalse(S_ISCHR(OsConstants.S_IFSOCK));
+        assertFalse(S_ISCHR(OsConstants.S_IRGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IROTH));
+        assertFalse(S_ISCHR(OsConstants.S_IRUSR));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXG));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXO));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXU));
+        assertFalse(S_ISCHR(OsConstants.S_ISGID));
+        assertFalse(S_ISCHR(OsConstants.S_ISUID));
+        assertFalse(S_ISCHR(OsConstants.S_ISVTX));
+        assertFalse(S_ISCHR(OsConstants.S_IWGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IWOTH));
+        assertFalse(S_ISCHR(OsConstants.S_IWUSR));
+        assertFalse(S_ISCHR(OsConstants.S_IXGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IXOTH));
+        assertFalse(S_ISCHR(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISBLK() {
+        assertTrue (S_ISBLK(OsConstants.S_IFBLK));
+
+        assertFalse(S_ISBLK(OsConstants.S_IFCHR));
+        assertFalse(S_ISBLK(OsConstants.S_IFDIR));
+        assertFalse(S_ISBLK(OsConstants.S_IFIFO));
+        assertFalse(S_ISBLK(OsConstants.S_IFLNK));
+        assertFalse(S_ISBLK(OsConstants.S_IFMT));
+        assertFalse(S_ISBLK(OsConstants.S_IFREG));
+        assertFalse(S_ISBLK(OsConstants.S_IFSOCK));
+        assertFalse(S_ISBLK(OsConstants.S_IRGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IROTH));
+        assertFalse(S_ISBLK(OsConstants.S_IRUSR));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXG));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXO));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXU));
+        assertFalse(S_ISBLK(OsConstants.S_ISGID));
+        assertFalse(S_ISBLK(OsConstants.S_ISUID));
+        assertFalse(S_ISBLK(OsConstants.S_ISVTX));
+        assertFalse(S_ISBLK(OsConstants.S_IWGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IWOTH));
+        assertFalse(S_ISBLK(OsConstants.S_IWUSR));
+        assertFalse(S_ISBLK(OsConstants.S_IXGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IXOTH));
+        assertFalse(S_ISBLK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISFIFO() {
+        assertTrue(S_ISFIFO(OsConstants.S_IFIFO));
+
+        assertFalse(S_ISFIFO(OsConstants.S_IFBLK));
+        assertFalse(S_ISFIFO(OsConstants.S_IFCHR));
+        assertFalse(S_ISFIFO(OsConstants.S_IFDIR));
+        assertFalse(S_ISFIFO(OsConstants.S_IFLNK));
+        assertFalse(S_ISFIFO(OsConstants.S_IFMT));
+        assertFalse(S_ISFIFO(OsConstants.S_IFREG));
+        assertFalse(S_ISFIFO(OsConstants.S_IFSOCK));
+        assertFalse(S_ISFIFO(OsConstants.S_IRGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IROTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IRUSR));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXG));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXO));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXU));
+        assertFalse(S_ISFIFO(OsConstants.S_ISGID));
+        assertFalse(S_ISFIFO(OsConstants.S_ISUID));
+        assertFalse(S_ISFIFO(OsConstants.S_ISVTX));
+        assertFalse(S_ISFIFO(OsConstants.S_IWGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IWOTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IWUSR));
+        assertFalse(S_ISFIFO(OsConstants.S_IXGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IXOTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISSOCK() {
+        assertTrue(S_ISSOCK(OsConstants.S_IFSOCK));
+
+        assertFalse(S_ISSOCK(OsConstants.S_IFBLK));
+        assertFalse(S_ISSOCK(OsConstants.S_IFCHR));
+        assertFalse(S_ISSOCK(OsConstants.S_IFDIR));
+        assertFalse(S_ISSOCK(OsConstants.S_IFIFO));
+        assertFalse(S_ISSOCK(OsConstants.S_IFLNK));
+        assertFalse(S_ISSOCK(OsConstants.S_IFMT));
+        assertFalse(S_ISSOCK(OsConstants.S_IFREG));
+        assertFalse(S_ISSOCK(OsConstants.S_IRGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IROTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IRUSR));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXG));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXO));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXU));
+        assertFalse(S_ISSOCK(OsConstants.S_ISGID));
+        assertFalse(S_ISSOCK(OsConstants.S_ISUID));
+        assertFalse(S_ISSOCK(OsConstants.S_ISVTX));
+        assertFalse(S_ISSOCK(OsConstants.S_IWGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IWOTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IWUSR));
+        assertFalse(S_ISSOCK(OsConstants.S_IXGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IXOTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_WEXITSTATUS() {
+        assertEquals(0, WEXITSTATUS(0x0000));
+        assertEquals(0, WEXITSTATUS(0x00DE));
+        assertEquals(0xF0, WEXITSTATUS(0xF000));
+        assertEquals(0xAB, WEXITSTATUS(0xAB12));
+    }
+
+    @Test
+    public void test_WCOREDUMP() {
+        assertFalse(WCOREDUMP(0));
+        assertTrue(WCOREDUMP(0x80));
+    }
+
+    @Test
+    public void test_WTERMSIG() {
+        assertEquals(0, WTERMSIG(0));
+        assertEquals(0x7f, WTERMSIG(0x7f));
+    }
+
+    @Test
+    public void test_WSTOPSIG() {
+        assertEquals(0, WSTOPSIG(0x0000));
+        assertEquals(0, WSTOPSIG(0x00DE));
+        assertEquals(0xF0, WSTOPSIG(0xF000));
+        assertEquals(0xAB, WSTOPSIG(0xAB12));
+    }
+
+
+    @Test
+    public void test_WIFEXITED() {
+        assertTrue(WIFEXITED(0));
+        assertFalse(WIFEXITED(0x7f));
+    }
+
+    @Test
+    public void test_WIFSTOPPED() {
+        assertFalse(WIFSTOPPED(0));
+        assertTrue(WIFSTOPPED(0x7f));
+    }
+
+    @Test
+    public void test_WIFSIGNALED() {
+        assertFalse(WIFSIGNALED(0));
+        assertTrue(WIFSIGNALED(1));
+    }
+}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
new file mode 100644
index 0000000..c2230c7
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java
@@ -0,0 +1,225 @@
+/*
+ * 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.runtimetest;
+
+import static android.system.OsConstants.S_ISBLK;
+import static android.system.OsConstants.S_ISCHR;
+import static android.system.OsConstants.S_ISDIR;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.S_ISSOCK;
+
+import static org.junit.Assert.assertEquals;
+
+import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
+
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.system.StructTimespec;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.ravenwood.common.JvmWorkaround;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
+import java.nio.file.attribute.PosixFileAttributes;
+import java.nio.file.attribute.PosixFilePermission;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class OsTest {
+    public interface ConsumerWithThrow<T> {
+        void accept(T var1) throws Exception;
+    }
+
+    private void withTestFileFD(ConsumerWithThrow<FileDescriptor> consumer) throws Exception {
+        File file = File.createTempFile("osTest", "bin");
+        try (var raf = new RandomAccessFile(file, "rw")) {
+            var fd = raf.getFD();
+
+            try (var os = new FileOutputStream(fd)) {
+                os.write(1);
+                os.write(2);
+                os.write(3);
+                os.write(4);
+
+                consumer.accept(fd);
+            }
+        }
+    }
+
+    private void withTestFile(ConsumerWithThrow<Path> consumer) throws Exception {
+        var path = Files.createTempFile("osTest", "bin");
+        try (var os = Files.newOutputStream(path)) {
+            os.write(1);
+            os.write(2);
+            os.write(3);
+            os.write(4);
+        }
+        consumer.accept(path);
+    }
+
+    @Test
+    public void testLseek() throws Exception {
+        withTestFileFD((fd) -> {
+            assertEquals(4, Os.lseek(fd, 4, OsConstants.SEEK_SET));
+            assertEquals(4, Os.lseek(fd, 0, OsConstants.SEEK_CUR));
+            assertEquals(6, Os.lseek(fd, 2, OsConstants.SEEK_CUR));
+        });
+    }
+
+    @Test
+    public void testDup() throws Exception {
+        withTestFileFD((fd) -> {
+            var dup = Os.dup(fd);
+
+            checkAreDup(fd, dup);
+        });
+    }
+
+    @Test
+    public void testPipe2() throws Exception {
+        var fds = Os.pipe2(0);
+
+        write(fds[1], 123);
+        assertEquals(123, read(fds[0]));
+    }
+
+    @Test
+    public void testFcntlInt() throws Exception {
+        withTestFileFD((fd) -> {
+            var dupInt = Os.fcntlInt(fd, 0, 0);
+
+            var dup = new FileDescriptor();
+            JvmWorkaround.getInstance().setFdInt(dup, dupInt);
+
+            checkAreDup(fd, dup);
+        });
+    }
+
+    @Test
+    public void testStat() throws Exception {
+        withTestFile(path -> {
+            var attr = Files.readAttributes(path, PosixFileAttributes.class);
+            var stat = Os.stat(path.toAbsolutePath().toString());
+            assertAttributesEqual(attr, stat);
+        });
+    }
+
+    @Test
+    public void testLstat() throws Exception {
+        withTestFile(path -> {
+            // Create a symbolic link
+            var lnk = Files.createTempFile("osTest", "lnk");
+            Files.delete(lnk);
+            Files.createSymbolicLink(lnk, path);
+
+            // Test lstat
+            var attr = Files.readAttributes(lnk, PosixFileAttributes.class, NOFOLLOW_LINKS);
+            var stat = Os.lstat(lnk.toAbsolutePath().toString());
+            assertAttributesEqual(attr, stat);
+
+            // Test stat
+            var followAttr = Files.readAttributes(lnk, PosixFileAttributes.class);
+            var followStat = Os.stat(lnk.toAbsolutePath().toString());
+            assertAttributesEqual(followAttr, followStat);
+        });
+    }
+
+    @Test
+    public void testFstat() throws Exception {
+        withTestFile(path -> {
+            var attr = Files.readAttributes(path, PosixFileAttributes.class);
+            try (var raf = new RandomAccessFile(path.toFile(), "r")) {
+                var fd = raf.getFD();
+                var stat = Os.fstat(fd);
+                assertAttributesEqual(attr, stat);
+            }
+        });
+    }
+
+    // Verify StructStat values from libcore against native JVM PosixFileAttributes
+    private static void assertAttributesEqual(PosixFileAttributes attr, StructStat stat) {
+        assertEquals(attr.lastModifiedTime(), convertTimespecToFileTime(stat.st_mtim));
+        assertEquals(attr.size(), stat.st_size);
+        assertEquals(attr.isDirectory(), S_ISDIR(stat.st_mode));
+        assertEquals(attr.isRegularFile(), S_ISREG(stat.st_mode));
+        assertEquals(attr.isSymbolicLink(), S_ISLNK(stat.st_mode));
+        assertEquals(attr.isOther(), S_ISCHR(stat.st_mode)
+                || S_ISBLK(stat.st_mode) || S_ISFIFO(stat.st_mode) || S_ISSOCK(stat.st_mode));
+        assertEquals(attr.permissions(), convertModeToPosixPerms(stat.st_mode));
+
+    }
+
+    private static FileTime convertTimespecToFileTime(StructTimespec ts) {
+        var nanos = TimeUnit.SECONDS.toNanos(ts.tv_sec);
+        nanos += ts.tv_nsec;
+        return FileTime.from(nanos, TimeUnit.NANOSECONDS);
+    }
+
+    private static Set<PosixFilePermission> convertModeToPosixPerms(int mode) {
+        var set = new HashSet<PosixFilePermission>();
+        if ((mode & OsConstants.S_IRUSR) != 0) set.add(PosixFilePermission.OWNER_READ);
+        if ((mode & OsConstants.S_IWUSR) != 0) set.add(PosixFilePermission.OWNER_WRITE);
+        if ((mode & OsConstants.S_IXUSR) != 0) set.add(PosixFilePermission.OWNER_EXECUTE);
+        if ((mode & OsConstants.S_IRGRP) != 0) set.add(PosixFilePermission.GROUP_READ);
+        if ((mode & OsConstants.S_IWGRP) != 0) set.add(PosixFilePermission.GROUP_WRITE);
+        if ((mode & OsConstants.S_IXGRP) != 0) set.add(PosixFilePermission.GROUP_EXECUTE);
+        if ((mode & OsConstants.S_IROTH) != 0) set.add(PosixFilePermission.OTHERS_READ);
+        if ((mode & OsConstants.S_IWOTH) != 0) set.add(PosixFilePermission.OTHERS_WRITE);
+        if ((mode & OsConstants.S_IXOTH) != 0) set.add(PosixFilePermission.OTHERS_EXECUTE);
+        return set;
+    }
+
+    private static void write(FileDescriptor fd, int oneByte) throws Exception {
+        // Create a dup to avoid closing the FD.
+        try (var dup = new FileOutputStream(Os.dup(fd))) {
+            dup.write(oneByte);
+        }
+    }
+
+    private static int read(FileDescriptor fd) throws Exception {
+        // Create a dup to avoid closing the FD.
+        try (var dup = new FileInputStream(Os.dup(fd))) {
+            return dup.read();
+        }
+    }
+
+    private static void checkAreDup(FileDescriptor fd1, FileDescriptor fd2) throws Exception {
+        assertEquals(4, Os.lseek(fd1, 4, OsConstants.SEEK_SET));
+        assertEquals(4, Os.lseek(fd1, 0, OsConstants.SEEK_CUR));
+
+        // Dup'ed FD shares the same position.
+        assertEquals(4, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+
+        assertEquals(6, Os.lseek(fd1, 2, OsConstants.SEEK_CUR));
+        assertEquals(6, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+    }
+}
diff --git a/ravenwood/scripts/list-ravenwood-tests.sh b/ravenwood/scripts/list-ravenwood-tests.sh
index fb9b823..05f3fdf 100755
--- a/ravenwood/scripts/list-ravenwood-tests.sh
+++ b/ravenwood/scripts/list-ravenwood-tests.sh
@@ -15,4 +15,4 @@
 
 # List all the ravenwood test modules.
 
-jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json"
+jq -r 'to_entries[] | select( .value.compatibility_suites | index("ravenwood-tests") ) | .key' "$OUT/module-info.json" | sort
diff --git a/ravenwood/scripts/remove-ravenizer-output.sh b/ravenwood/scripts/remove-ravenizer-output.sh
new file mode 100755
index 0000000..be15b71
--- /dev/null
+++ b/ravenwood/scripts/remove-ravenizer-output.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# 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.
+
+# Delete all the ravenizer output jar files from Soong's intermediate directory.
+
+# `-a -prune` is needed because otherwise find would be confused if the directory disappears.
+
+find "${ANDROID_BUILD_TOP:?}/out/soong/.intermediates/" \
+    -type d \
+    -name 'ravenizer' \
+    -print \
+    -exec rm -fr \{\} \; \
+    -a -prune
diff --git a/ravenwood/scripts/shrink-systemui-test b/ravenwood/scripts/shrink-systemui-test
new file mode 100755
index 0000000..8589c1d
--- /dev/null
+++ b/ravenwood/scripts/shrink-systemui-test
@@ -0,0 +1,131 @@
+#!/bin/bash
+# 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.
+
+set -e
+
+SCRIPT_NAME="${0##*/}"
+
+usage() {
+    cat <<"EOF"
+
+$SCRIPT_NAME: Shrink / unshrink SystemUiRavenTests.
+
+    SystemUiRavenTests has a lot of kotlin source files, so it's slow to build,
+    which is painful when you want to run it after updating ravenwood code
+    that SystemUiRavenTests depends on. (example: junit-src/)
+
+    This script basically removes the test files in SystemUI/multivalentTests
+    that don't have @EnabledOnRavenwood. But if we actaully remove them,
+    soong would re-generate the ninja file, which will take a long time,
+    so instead it'll truncate them.
+
+    This script will also tell git to ignore these files, so they won't shw up
+    in `git status`.
+    (Use `git ls-files -v | sed -ne "s/^[a-zS] //p"` to show ignored filse.)
+
+Usage:
+    $SCRIPT_NAME -s # Shrink the test files.
+
+    $SCRIPT_NAME -u # Undo it.
+
+EOF
+}
+
+TEST_PATH=${ANDROID_BUILD_TOP}/frameworks/base/packages/SystemUI/multivalentTests
+cd "$TEST_PATH"
+
+command=""
+case "$1" in
+    "-s") command=shrink ;;
+    "-u") command=unshrink ;;
+    *) usage ; exit 1 ;;
+esac
+
+
+echo "Listing test files...."
+files=( $(find . -name '*Test.kt' -o -name '*Test.java') )
+
+exemption='(BaseHeadsUpManagerTest)'
+
+shrink() {
+    local target=()
+    for file in ${files[@]}; do
+        # Check for exemption
+        if echo $file | egrep -q "$exemption"; then
+            echo "  Skip exempted file"
+            continue
+        fi
+
+        echo "Checking $file"
+        if ! [[ -f $file ]] ; then
+            echo "  Skip non regular file"
+            continue
+        fi
+
+        if ! [[ -s $file ]] ; then
+            echo "  Skip empty file"
+            continue
+        fi
+
+        if grep -q '@EnabledOnRavenwood' $file ; then
+            echo "  Skip ravenwood test file".
+            continue
+        fi
+
+        # It's a non ravenwood test file. Empty it.
+        : > $file
+
+        # Tell git to ignore the file
+
+        target+=($file)
+
+        echo "  Emptied"
+
+    done
+    if (( ${#target[@]} == 0 )) ; then
+        echo "No files emptied."
+        return 0
+    fi
+
+    git update-index --skip-worktree ${target[@]}
+
+    echo "Emptied ${#target[@]} files"
+    return 0
+}
+
+unshrink() {
+    local target=()
+
+    # Collect empty files
+    for file in ${files[@]}; do
+        if [[ -s $file ]] ; then
+            continue
+        fi
+
+        target+=($file)
+        : > $file
+    done
+    if (( ${#target[@]} == 0 )) ; then
+        echo "No files to restore."
+        return 0
+    fi
+    # Un-ignore the files, and check out the original files
+    echo "Restoring ${#target[@]} files..."
+    git update-index --no-skip-worktree ${target[@]}
+    git checkout goog/main ${target[@]}
+    return 0
+}
+
+$command
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
new file mode 100755
index 0000000..b6cf5b8
--- /dev/null
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+# 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.
+
+# Update f/b/r/TEST_MAPPING with all the ravenwood tests as presubmit.
+#
+# Note, before running it, make sure module-info.json is up-to-date by running
+# (any) build.
+
+set -e
+
+main() {
+    local script_name="${0##*/}"
+    local script_dir="${0%/*}"
+    local test_mapping="$script_dir/../TEST_MAPPING"
+    local test_mapping_bak="$script_dir/../TEST_MAPPING.bak"
+
+    local header="$(sed -ne '1,/AUTO-GENERATED-START/p' "$test_mapping")"
+    local footer="$(sed -ne '/AUTO-GENERATED-END/,$p' "$test_mapping")"
+
+    echo "Getting all tests"
+    local tests=( $("$script_dir/list-ravenwood-tests.sh") )
+
+    local num_tests="${#tests[@]}"
+
+    if (( $num_tests == 0 )) ; then
+        echo "Something went wrong. No ravenwood tests detected." 1>&2
+        return 1
+    fi
+
+    echo "Tests: ${tests[@]}"
+
+    echo "Creating backup at $test_mapping_bak"
+    cp "$test_mapping" "$test_mapping_bak"
+
+    echo "Updating $test_mapping"
+    {
+        echo "$header"
+
+        echo "    // DO NOT MODIFY MANUALLY"
+        echo "    // Use scripts/$script_name to update it."
+
+        local i=0
+        while (( $i < $num_tests )) ; do
+            local comma=","
+            if (( $i == ($num_tests - 1) )); then
+                comma=""
+            fi
+            echo "    {"
+            echo "      \"name\": \"${tests[$i]}\","
+            echo "      \"host\": true"
+            echo "    }$comma"
+
+            i=$(( $i + 1 ))
+        done
+
+        echo "$footer"
+    } >"$test_mapping"
+
+    if cmp "$test_mapping_bak" "$test_mapping" ; then
+        echo "No change detecetd."
+        return 0
+    fi
+    echo "Updated $test_mapping"
+
+    # `|| true` is needed because of `set -e`.
+    diff -u "$test_mapping_bak" "$test_mapping" || true
+    return 0
+}
+
+main
diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index 044239f..b3d3963 100644
--- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -16,6 +16,7 @@
 
 package com.android.ravenwoodtest.servicestest;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -62,7 +63,9 @@
         final SerialManager service = (SerialManager)
                 mRavenwood.getContext().getSystemService(Context.SERIAL_SERVICE);
         final String[] ports = service.getSerialPorts();
-        assertEquals(0, ports.length);
+        final String[] refPorts = mRavenwood.getContext().getResources().getStringArray(
+                com.android.internal.R.array.config_serialPorts);
+        assertArrayEquals(refPorts, ports);
     }
 
     @Test
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index cc9b70e..9c86389 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -9,17 +9,13 @@
 com.android.internal.logging.testing.UiEventLoggerFake
 com.android.internal.os.AndroidPrintStream
 com.android.internal.os.BatteryStatsHistory
-com.android.internal.os.BatteryStatsHistory$TraceDelegate
-com.android.internal.os.BatteryStatsHistory$VarintParceler
 com.android.internal.os.BatteryStatsHistoryIterator
 com.android.internal.os.Clock
 com.android.internal.os.LongArrayMultiStateCounter
-com.android.internal.os.LongArrayMultiStateCounter$LongArrayContainer
 com.android.internal.os.LongMultiStateCounter
 com.android.internal.os.MonotonicClock
 com.android.internal.os.PowerProfile
 com.android.internal.os.PowerStats
-com.android.internal.os.PowerStats$Descriptor
 com.android.internal.os.RuntimeInit
 com.android.internal.power.EnergyConsumerStats
 com.android.internal.power.ModemPowerProfile
@@ -39,12 +35,14 @@
 android.util.DataUnit
 android.util.DayOfMonthCursor
 android.util.DebugUtils
+android.util.DisplayMetrics
 android.util.Dumpable
 android.util.DumpableContainer
 android.util.EmptyArray
 android.util.EventLog
 android.util.FloatProperty
 android.util.FloatMath
+android.util.Half
 android.util.IndentingPrintWriter
 android.util.IntArray
 android.util.IntProperty
@@ -98,10 +96,12 @@
 android.util.SparseIntArray
 android.util.SparseLongArray
 android.util.SparseSetArray
+android.util.StateSet
 android.util.StringBuilderPrinter
 android.util.TeeWriter
 android.util.TimeUtils
 android.util.TimingsTraceLog
+android.util.TypedValue
 android.util.UtilConfig
 android.util.Xml
 
@@ -119,15 +119,9 @@
 android.os.BaseBundle
 android.os.BatteryConsumer
 android.os.BatteryStats
-android.os.BatteryStats$HistoryItem
-android.os.BatteryStats$HistoryStepDetails
-android.os.BatteryStats$HistoryTag
-android.os.BatteryStats$LongCounter
-android.os.BatteryStats$ProcessStateChange
 android.os.BatteryUsageStats
 android.os.BatteryUsageStatsQuery
 android.os.Binder
-android.os.Binder$IdentitySupplier
 android.os.BluetoothBatteryStats
 android.os.Broadcaster
 android.os.Build
@@ -138,7 +132,6 @@
 android.os.DeadObjectException
 android.os.DeadSystemException
 android.os.FileUtils
-android.os.FileUtils$MemoryPipe
 android.os.Handler
 android.os.HandlerExecutor
 android.os.HandlerThread
@@ -150,8 +143,6 @@
 android.os.PackageTagsList
 android.os.Parcel
 android.os.ParcelFileDescriptor
-android.os.ParcelFileDescriptor$AutoCloseInputStream
-android.os.ParcelFileDescriptor$AutoCloseOutputStream
 android.os.ParcelFormatException
 android.os.ParcelUuid
 android.os.Parcelable
@@ -165,7 +156,6 @@
 android.os.RemoteException
 android.os.ResultReceiver
 android.os.ServiceManager
-android.os.ServiceManager$ServiceNotFoundException
 android.os.ServiceSpecificException
 android.os.StrictMode
 android.os.SystemClock
@@ -176,23 +166,20 @@
 android.os.Trace
 android.os.TransactionTooLargeException
 android.os.UserBatteryConsumer
-android.os.UserBatteryConsumer$Builder
 android.os.UidBatteryConsumer
-android.os.UidBatteryConsumer$Builder
 android.os.UserHandle
 android.os.UserManager
 android.os.VibrationAttributes
-android.os.VibrationAttributes$Builder
 android.os.WakeLockStats
 android.os.WorkSource
 
 android.content.ClipData
-android.content.ClipData$Item
 android.content.ClipDescription
 android.content.ClipboardManager
 android.content.ComponentName
 android.content.ContentUris
 android.content.ContentValues
+android.content.Context
 android.content.ContextWrapper
 android.content.Intent
 android.content.IntentFilter
@@ -203,11 +190,7 @@
 android.content.pm.ComponentInfo
 android.content.pm.PackageInfo
 android.content.pm.PackageItemInfo
-android.content.pm.PackageManager$Flags
-android.content.pm.PackageManager$PackageInfoFlags
-android.content.pm.PackageManager$ApplicationInfoFlags
-android.content.pm.PackageManager$ComponentInfoFlags
-android.content.pm.PackageManager$ResolveInfoFlags
+android.content.pm.PackageManager
 android.content.pm.PathPermission
 android.content.pm.ProviderInfo
 android.content.pm.ResolveInfo
@@ -215,6 +198,32 @@
 android.content.pm.Signature
 android.content.pm.UserInfo
 
+android.content.res.ApkAssets
+android.content.res.AssetFileDescriptor
+android.content.res.AssetManager
+android.content.res.ColorStateList
+android.content.res.ConfigurationBoundResourceCache
+android.content.res.Configuration
+android.content.res.CompatibilityInfo
+android.content.res.ComplexColor
+android.content.res.ConstantState
+android.content.res.DrawableCache
+android.content.res.Element
+android.content.res.FontResourcesParser
+android.content.res.FontScaleConverter
+android.content.res.FontScaleConverterImpl
+android.content.res.FontScaleConverterFactory
+android.content.res.Resources
+android.content.res.ResourceId
+android.content.res.ResourcesImpl
+android.content.res.ResourcesKey
+android.content.res.StringBlock
+android.content.res.TagCounter
+android.content.res.ThemedResourceCache
+android.content.res.TypedArray
+android.content.res.Validator
+android.content.res.XmlBlock
+
 android.database.AbstractCursor
 android.database.CharArrayBuffer
 android.database.ContentObservable
@@ -227,7 +236,6 @@
 android.database.DataSetObservable
 android.database.DataSetObserver
 android.database.MatrixCursor
-android.database.MatrixCursor$RowBuilder
 android.database.MergeCursor
 android.database.Observable
 android.database.SQLException
@@ -235,7 +243,6 @@
 android.database.sqlite.SQLiteException
 
 android.text.TextUtils
-android.text.TextUtils$SimpleStringSplitter
 
 android.accounts.Account
 
@@ -245,7 +252,11 @@
 android.graphics.Insets
 android.graphics.Interpolator
 android.graphics.Matrix
+android.graphics.Matrix44
+android.graphics.Outline
+android.graphics.ParcelableColorSpace
 android.graphics.Path
+android.graphics.PixelFormat
 android.graphics.Point
 android.graphics.PointF
 android.graphics.Rect
@@ -255,15 +266,18 @@
 
 android.app.ActivityManager
 android.app.ActivityOptions
+android.app.ApplicationPackageManager
 android.app.BroadcastOptions
 android.app.ComponentOptions
 android.app.Instrumentation
+android.app.LocaleConfig
+android.app.ResourcesManager
+android.app.WindowConfiguration
 
 android.metrics.LogMaker
 
 android.view.Display
-android.view.Display$HdrCapabilities
-android.view.Display$Mode
+android.view.DisplayAdjustments
 android.view.DisplayInfo
 android.view.inputmethod.InputBinding
 
@@ -311,6 +325,7 @@
 com.android.internal.util.QuickSelect
 com.android.internal.util.RingBuffer
 com.android.internal.util.SizedInputStream
+com.android.internal.util.RateLimitingCache
 com.android.internal.util.StringPool
 com.android.internal.util.TokenBucket
 com.android.internal.util.XmlPullParserWrapper
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 4012bdc..d962c82 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -9,11 +9,21 @@
 # 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 shashes (/) as a package name separator.
+# 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
@@ -62,3 +72,7 @@
     method <init> ()V keep
 class android.text.ClipboardManager keep
     method <init> ()V keep
+
+# Just enough to allow ResourcesManager to run
+class android.hardware.display.DisplayManagerGlobal keep
+    method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index f64f26d..952ab82 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -6,8 +6,6 @@
 --default-throw
 
 # Uncomment below lines to enable each feature.
-# --enable-non-stub-method-check
---no-non-stub-method-check
 
 #--default-method-call-hook
 #    com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
diff --git a/ravenwood/tools/ravenizer/Android.bp b/ravenwood/tools/ravenizer/Android.bp
new file mode 100644
index 0000000..2892d07
--- /dev/null
+++ b/ravenwood/tools/ravenizer/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_binary_host {
+    name: "ravenizer",
+    main_class: "com.android.platform.test.ravenwood.ravenizer.RavenizerMain",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "hoststubgen-lib",
+        "ow2-asm",
+        "ow2-asm-analysis",
+        "ow2-asm-commons",
+        "ow2-asm-tree",
+        "ow2-asm-util",
+        "junit",
+        "ravenwood-junit-impl-for-ravenizer",
+    ],
+    visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.kt
new file mode 100644
index 0000000..0dcd271
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Exceptions.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.
+ */
+@file:Suppress("ktlint:standard:filename")
+
+package com.android.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.UserErrorException
+
+/**
+ * Use it for internal exception that really shouldn't happen.
+ */
+class RavenizerInternalException(message: String) : Exception(message)
+
+/**
+ * Thrown when an invalid test is detected in the target jar. (e.g. JUni3 tests)
+ */
+class RavenizerInvalidTestException(message: String) : Exception(message), UserErrorException
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
new file mode 100644
index 0000000..a38512e
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -0,0 +1,249 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.GeneralUserErrorException
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.zipEntryNameToClassName
+import com.android.hoststubgen.executableName
+import com.android.hoststubgen.log
+import com.android.platform.test.ravenwood.ravenizer.adapter.RunnerRewritingAdapter
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.util.CheckClassAdapter
+import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
+import java.io.FileOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.util.zip.ZipEntry
+import java.util.zip.ZipFile
+import java.util.zip.ZipOutputStream
+
+/**
+ * Various stats on Ravenizer.
+ */
+data class RavenizerStats(
+    /** Total end-to-end time. */
+    var totalTime: Double = .0,
+
+    /** Time took to build [ClasNodes] */
+    var loadStructureTime: Double = .0,
+
+    /** Time took to validate the classes */
+    var validationTime: Double = .0,
+
+    /** Total real time spent for converting the jar file */
+    var totalProcessTime: Double = .0,
+
+    /** Total real time spent for converting class files (except for I/O time). */
+    var totalConversionTime: Double = .0,
+
+    /** Total real time spent for copying class files without modification. */
+    var totalCopyTime: Double = .0,
+
+    /** # of entries in the input jar file */
+    var totalEntiries: Int = 0,
+
+    /** # of *.class files in the input jar file */
+    var totalClasses: Int = 0,
+
+    /** # of *.class files that have been processed. */
+    var processedClasses: Int = 0,
+) {
+    override fun toString(): String {
+        return """
+            RavenizerStats{
+              totalTime=$totalTime,
+              loadStructureTime=$loadStructureTime,
+              validationTime=$validationTime,
+              totalProcessTime=$totalProcessTime,
+              totalConversionTime=$totalConversionTime,
+              totalCopyTime=$totalCopyTime,
+              totalEntiries=$totalEntiries,
+              totalClasses=$totalClasses,
+              processedClasses=$processedClasses,
+            }
+            """.trimIndent()
+    }
+}
+
+/**
+ * Main class.
+ */
+class Ravenizer(val options: RavenizerOptions) {
+    fun run() {
+        val stats = RavenizerStats()
+
+        val fatalValidation = options.fatalValidation.get
+
+        stats.totalTime = log.nTime {
+            process(
+                options.inJar.get,
+                options.outJar.get,
+                options.enableValidation.get,
+                fatalValidation,
+                stats,
+            )
+        }
+        log.i(stats.toString())
+    }
+
+    private fun process(
+        inJar: String,
+        outJar: String,
+        enableValidation: Boolean,
+        fatalValidation: Boolean,
+        stats: RavenizerStats,
+    ) {
+        var allClasses = ClassNodes.loadClassStructures(inJar) {
+            time -> stats.loadStructureTime = time
+        }
+        if (enableValidation) {
+            stats.validationTime = log.iTime("Validating classes") {
+                if (!validateClasses(allClasses)) {
+                    var message = "Invalid test class(es) detected." +
+                            " See error log for details."
+                    if (fatalValidation) {
+                        throw RavenizerInvalidTestException(message)
+                    } else {
+                        log.w("Warning: $message")
+                    }
+                }
+            }
+        }
+
+        stats.totalProcessTime = log.iTime("$executableName processing $inJar") {
+            ZipFile(inJar).use { inZip ->
+                val inEntries = inZip.entries()
+
+                stats.totalEntiries = inZip.size()
+
+                ZipOutputStream(BufferedOutputStream(FileOutputStream(outJar))).use { outZip ->
+                    while (inEntries.hasMoreElements()) {
+                        val entry = inEntries.nextElement()
+
+                        if (entry.name.endsWith(".dex")) {
+                            // Seems like it's an ART jar file. We can't process it.
+                            // It's a fatal error.
+                            throw GeneralUserErrorException(
+                                "$inJar is not a desktop jar file. It contains a *.dex file."
+                            )
+                        }
+
+                        val className = zipEntryNameToClassName(entry.name)
+
+                        if (className != null) {
+                            stats.totalClasses += 1
+                        }
+
+                        if (className != null && shouldProcessClass(allClasses, className)) {
+                            stats.processedClasses += 1
+                            processSingleClass(inZip, entry, outZip, allClasses, stats)
+                        } else {
+                            // Too slow, let's use merge_zips to bring back the original classes.
+                            copyZipEntry(inZip, entry, outZip, stats)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Copy a single ZIP entry to the output.
+     */
+    private fun copyZipEntry(
+        inZip: ZipFile,
+        entry: ZipEntry,
+        out: ZipOutputStream,
+        stats: RavenizerStats,
+    ) {
+        stats.totalCopyTime += log.nTime {
+            inZip.getInputStream(entry).use { ins ->
+                // Copy unknown entries as is to the impl out. (but not to the stub out.)
+                val outEntry = ZipEntry(entry.name)
+                outEntry.method = 0
+                outEntry.size = entry.size
+                outEntry.crc = entry.crc
+                out.putNextEntry(outEntry)
+
+                ins.transferTo(out)
+
+                out.closeEntry()
+            }
+        }
+    }
+
+    private fun processSingleClass(
+        inZip: ZipFile,
+        entry: ZipEntry,
+        outZip: ZipOutputStream,
+        allClasses: ClassNodes,
+        stats: RavenizerStats,
+    ) {
+        val newEntry = ZipEntry(entry.name)
+        outZip.putNextEntry(newEntry)
+
+        BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+            processSingleClass(entry, bis, outZip, allClasses, stats)
+        }
+        outZip.closeEntry()
+    }
+
+    /**
+     * Whether a class needs to be processed. This must be kept in sync with [processSingleClass].
+     */
+    private fun shouldProcessClass(classes: ClassNodes, classInternalName: String): Boolean {
+        return !classInternalName.shouldByBypassed()
+                && RunnerRewritingAdapter.shouldProcess(classes, classInternalName)
+    }
+
+    private fun processSingleClass(
+        entry: ZipEntry,
+        input: InputStream,
+        output: OutputStream,
+        allClasses: ClassNodes,
+        stats: RavenizerStats,
+    ) {
+        val cr = ClassReader(input)
+
+        lateinit var data: ByteArray
+        stats.totalConversionTime += log.vTime("Modify ${entry.name}") {
+
+            val classInternalName = zipEntryNameToClassName(entry.name)
+                ?: throw RavenizerInternalException("Unexpected zip entry name: ${entry.name}")
+            val flags = ClassWriter.COMPUTE_MAXS
+            val cw = ClassWriter(flags)
+            var outVisitor: ClassVisitor = cw
+
+            val enableChecker = false
+            if (enableChecker) {
+                outVisitor = CheckClassAdapter(outVisitor)
+            }
+
+            // This must be kept in sync with shouldProcessClass.
+            outVisitor = RunnerRewritingAdapter.maybeApply(
+                classInternalName, allClasses, outVisitor)
+
+            cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
+
+            data = cw.toByteArray()
+        }
+        output.write(data)
+    }
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.kt
new file mode 100644
index 0000000..ff41818
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerMain.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.
+ */
+@file:JvmName("RavenizerMain")
+
+package com.android.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.LogLevel
+import com.android.hoststubgen.executableName
+import com.android.hoststubgen.log
+import com.android.hoststubgen.runMainWithBoilerplate
+
+/**
+ * Entry point.
+ */
+fun main(args: Array<String>) {
+    executableName = "Ravenizer"
+    log.setConsoleLogLevel(LogLevel.Info)
+
+    runMainWithBoilerplate {
+        val options = RavenizerOptions.parseArgs(args)
+
+        log.i("$executableName started")
+        log.v("Options: $options")
+
+        // Run.
+        Ravenizer(options).run()
+    }
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt
new file mode 100644
index 0000000..e8341e5
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.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.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.ArgIterator
+import com.android.hoststubgen.ArgumentsException
+import com.android.hoststubgen.SetOnce
+import com.android.hoststubgen.ensureFileExists
+import com.android.hoststubgen.log
+
+class RavenizerOptions(
+    /** Input jar file*/
+    var inJar: SetOnce<String> = SetOnce(""),
+
+    /** Output jar file */
+    var outJar: SetOnce<String> = SetOnce(""),
+
+    /** Whether to enable test validation. */
+    var enableValidation: SetOnce<Boolean> = SetOnce(true),
+
+    /** Whether the validation failure is fatal or not. */
+    var fatalValidation: SetOnce<Boolean> = SetOnce(false),
+) {
+    companion object {
+        fun parseArgs(args: Array<String>): RavenizerOptions {
+            val ret = RavenizerOptions()
+            val ai = ArgIterator.withAtFiles(args)
+
+            while (true) {
+                val arg = ai.nextArgOptional()
+                if (arg == null) {
+                    break
+                }
+
+                fun nextArg(): String = ai.nextArgRequired(arg)
+
+                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
+                    continue
+                }
+                try {
+                    when (arg) {
+                        // TODO: Write help
+                        "-h", "--help" -> TODO("Help is not implemented yet")
+
+                        "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
+                        "--out-jar" -> ret.outJar.set(nextArg())
+
+                        "--enable-validation" -> ret.enableValidation.set(true)
+                        "--disable-validation" -> ret.enableValidation.set(false)
+
+                        "--fatal-validation" -> ret.fatalValidation.set(true)
+                        "--no-fatal-validation" -> ret.fatalValidation.set(false)
+
+                        else -> throw ArgumentsException("Unknown option: $arg")
+                    }
+                } catch (e: SetOnce.SetMoreThanOnceException) {
+                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
+                }
+            }
+
+            if (!ret.inJar.isSet) {
+                throw ArgumentsException("Required option missing: --in-jar")
+            }
+            if (!ret.outJar.isSet) {
+                throw ArgumentsException("Required option missing: --out-jar")
+            }
+           return ret
+        }
+    }
+
+    override fun toString(): String {
+        return """
+            RavenizerOptions{
+              inJar=$inJar,
+              outJar=$outJar,
+              enableValidation=$enableValidation,
+              fatalValidation=$fatalValidation,
+            }
+            """.trimIndent()
+    }
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
new file mode 100644
index 0000000..1aa70c08
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt
@@ -0,0 +1,102 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer
+
+import android.platform.test.annotations.NoRavenizer
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.findAnyAnnotation
+import com.android.hoststubgen.asm.startsWithAny
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import org.junit.rules.TestRule
+import org.junit.runner.RunWith
+import org.objectweb.asm.Type
+
+data class TypeHolder(
+    val clazz: Class<*>,
+) {
+    val type = Type.getType(clazz)
+    val desc = type.descriptor
+    val descAsSet = setOf<String>(desc)
+    val internlName = type.internalName
+    val humanReadableName = type.internalName.toHumanReadableClassName()
+}
+
+val testAnotType = TypeHolder(org.junit.Test::class.java)
+val ruleAnotType = TypeHolder(org.junit.Rule::class.java)
+val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java)
+val runWithAnotType = TypeHolder(RunWith::class.java)
+val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java)
+val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java)
+
+val testRuleType = TypeHolder(TestRule::class.java)
+val ravenwoodTestRunnerType = TypeHolder(RavenwoodAwareTestRunner::class.java)
+
+/**
+ * Returns true, if a test looks like it's a test class which needs to be processed.
+ */
+fun isTestLookingClass(classes: ClassNodes, className: String): Boolean {
+    // Similar to  com.android.tradefed.lite.HostUtils.testLoadClass(), except it's more lenient,
+    // and accept non-public and/or abstract classes.
+    // HostUtils also checks "Suppress" or "SuiteClasses" but this one doesn't.
+    // TODO: SuiteClasses may need to be supported.
+
+    val cn = classes.findClass(className) ?: return false
+
+    if (cn.findAnyAnnotation(runWithAnotType.descAsSet) != null) {
+        return true
+    }
+    cn.methods?.forEach { method ->
+        if (method.findAnyAnnotation(testAnotType.descAsSet) != null) {
+            return true
+        }
+    }
+
+    // Check the super class.
+    if (cn.superName == null) {
+        return false
+    }
+    return isTestLookingClass(classes, cn.superName)
+}
+
+fun String.isRavenwoodClass(): Boolean {
+    return this.startsWithAny(
+        "com/android/hoststubgen/",
+        "android/platform/test/ravenwood",
+        "com/android/ravenwood/",
+        "com/android/platform/test/ravenwood/",
+    )
+}
+
+/**
+ * Classes that should never be modified.
+ */
+fun String.shouldByBypassed(): Boolean {
+    if (this.isRavenwoodClass()) {
+        return true
+    }
+    return this.startsWithAny(
+        "java/", // just in case...
+        "javax/",
+        "junit/",
+        "org/junit/",
+        "org/mockito/",
+        "kotlin/",
+        "androidx/",
+        "android/support/",
+        // TODO -- anything else?
+    )
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
new file mode 100644
index 0000000..27092d2
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.startsWithAny
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.log
+import org.objectweb.asm.tree.ClassNode
+
+fun validateClasses(classes: ClassNodes): Boolean {
+    var allOk = true
+    classes.forEach { allOk = checkClass(it, classes) && allOk }
+
+    return allOk
+}
+
+/**
+ * Validate a class.
+ *
+ * - A test class shouldn't extend
+ *
+ */
+fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean {
+    if (cn.name.shouldByBypassed()) {
+        // Class doesn't need to be checked.
+        return true
+    }
+    var allOk = true
+
+    // See if there's any class that extends a legacy base class.
+    // But ignore the base classes in android.test.
+    if (!cn.name.startsWithAny("android/test/")) {
+        allOk = checkSuperClass(cn, cn, classes) && allOk
+    }
+    return allOk
+}
+
+fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean {
+    if (currentClass.superName == null || currentClass.superName == "java/lang/Object") {
+        return true // No parent class
+    }
+    if (currentClass.superName.isLegacyTestBaseClass()) {
+        log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends"
+                + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.")
+        return false
+    }
+    classes.findClass(currentClass.superName)?.let {
+        return checkSuperClass(targetClass, it, classes)
+    }
+    // Super class not found.
+    // log.w("Class ${currentClass.superName} not found.")
+    return true
+}
+
+/**
+ * Check if a class internal name is a known legacy test base class.
+ */
+fun String.isLegacyTestBaseClass(): Boolean {
+    return this.startsWithAny(
+        "junit/framework/TestCase",
+
+        // In case the test doesn't statically include JUnit, we need
+        "android/test/AndroidTestCase",
+        "android/test/InstrumentationTestCase",
+        "android/test/InstrumentationTestSuite",
+    )
+}
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
new file mode 100644
index 0000000..eaef2cf
--- /dev/null
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt
@@ -0,0 +1,466 @@
+/*
+ * 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.platform.test.ravenwood.ravenizer.adapter
+
+import android.platform.test.ravenwood.RavenwoodAwareTestRunner
+import com.android.hoststubgen.ClassParseException
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.CTOR_NAME
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.findAnnotationValueAsType
+import com.android.hoststubgen.asm.findAnyAnnotation
+import com.android.hoststubgen.asm.toHumanReadableClassName
+import com.android.hoststubgen.log
+import com.android.hoststubgen.visitors.OPCODE_VERSION
+import com.android.platform.test.ravenwood.ravenizer.RavenizerInternalException
+import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType
+import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass
+import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType
+import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType
+import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType
+import com.android.platform.test.ravenwood.ravenizer.ruleAnotType
+import com.android.platform.test.ravenwood.ravenizer.runWithAnotType
+import com.android.platform.test.ravenwood.ravenizer.testRuleType
+import org.objectweb.asm.AnnotationVisitor
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.FieldVisitor
+import org.objectweb.asm.MethodVisitor
+import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Opcodes.ACC_FINAL
+import org.objectweb.asm.Opcodes.ACC_PUBLIC
+import org.objectweb.asm.Opcodes.ACC_STATIC
+import org.objectweb.asm.commons.AdviceAdapter
+import org.objectweb.asm.tree.ClassNode
+
+/**
+ * Class visitor to update the RunWith and inject some necessary rules.
+ *
+ * - Change the @RunWith(RavenwoodAwareTestRunner.class).
+ * - If the original class has a @RunWith(...), then change it to an @OrigRunWith(...).
+ * - Add RavenwoodAwareTestRunner's member rules as junit rules.
+ * - Update the order of the existing JUnit rules to make sure they don't use the MIN or MAX.
+ */
+class RunnerRewritingAdapter private constructor(
+    protected val classes: ClassNodes,
+    nextVisitor: ClassVisitor,
+) : ClassVisitor(OPCODE_VERSION, nextVisitor) {
+    /** Arbitrary cut-off point when deciding whether to change the order or an existing rule.*/
+    val RULE_ORDER_TWEAK_CUTOFF = 1973020500
+
+    /** Current class's internal name */
+    lateinit var classInternalName: String
+
+    /** [ClassNode] for the current class */
+    lateinit var classNode: ClassNode
+
+    /** True if this visitor is generating code. */
+    var isGeneratingCode = false
+
+    /** Run a [block] with [isGeneratingCode] set to true. */
+    private inline fun <T> generateCode(block: () -> T): T {
+        isGeneratingCode = true
+        try {
+            return block()
+        } finally {
+            isGeneratingCode = false
+        }
+    }
+
+    override fun visit(
+        version: Int,
+        access: Int,
+        name: String?,
+        signature: String?,
+        superName: String?,
+        interfaces: Array<out String>?,
+        ) {
+        classInternalName = name!!
+        classNode = classes.getClass(name)
+        if (!isTestLookingClass(classes, name)) {
+            throw RavenizerInternalException("This adapter shouldn't be used for non-test class")
+        }
+        super.visit(version, access, name, signature, superName, interfaces)
+
+        generateCode {
+            injectRunWithAnnotation()
+            if (!classes.hasClassInitializer(classInternalName)) {
+                injectStaticInitializer()
+            }
+            injectRules()
+        }
+    }
+
+    /**
+     * Remove the original @RunWith annotation.
+     */
+    override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
+        if (!isGeneratingCode && runWithAnotType.desc == descriptor) {
+            return null
+        }
+        return super.visitAnnotation(descriptor, visible)
+    }
+
+    override fun visitField(
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        value: Any?
+    ): FieldVisitor {
+        val fallback = super.visitField(access, name, descriptor, signature, value)
+        if (isGeneratingCode) {
+            return fallback
+        }
+        return FieldRuleOrderRewriter(name, fallback)
+    }
+
+    /** Inject an empty <clinit>. The body will be injected by [visitMethod]. */
+    private fun injectStaticInitializer() {
+        visitMethod(
+            Opcodes.ACC_PRIVATE or Opcodes.ACC_STATIC,
+            CLASS_INITIALIZER_NAME,
+            CLASS_INITIALIZER_DESC,
+            null,
+            null
+        )!!.let { mv ->
+            mv.visitCode()
+            mv.visitInsn(Opcodes.RETURN)
+            mv.visitMaxs(0, 0)
+            mv.visitEnd()
+        }
+    }
+
+    /**
+     * Inject `@RunWith(RavenwoodAwareTestRunner.class)`. If the class already has
+     * a `@RunWith`, then change it to add a `@OrigRunWith`.
+     */
+    private fun injectRunWithAnnotation() {
+        // Extract the original RunWith annotation and its value.
+        val runWith = classNode.findAnyAnnotation(runWithAnotType.descAsSet)
+        val runWithClass = runWith?.let { an ->
+            findAnnotationValueAsType(an, "value")
+        }
+
+        if (runWith != null) {
+            if (runWithClass == ravenwoodTestRunnerType.type) {
+                // It already uses RavenwoodTestRunner. We'll just keep it, but we need to
+                // inject it again because the original one is removed by visitAnnotation().
+                log.d("Class ${classInternalName.toHumanReadableClassName()}" +
+                        " already uses RavenwoodTestRunner.")
+                visitAnnotation(runWithAnotType.desc, true)!!.let { av ->
+                    av.visit("value", ravenwoodTestRunnerType)
+                    av.visitEnd()
+                }
+                return
+            }
+            if (runWithClass == null) {
+                throw ClassParseException("@RunWith annotation doesn't have a property \"value\""
+                        + " in class ${classInternalName.toHumanReadableClassName()}")
+            }
+
+            // Inject an @OrigRunWith.
+            visitAnnotation(innerRunnerAnotType.desc, true)!!.let { av ->
+                av.visit("value", runWithClass)
+                av.visitEnd()
+            }
+        }
+
+        // Inject a @RunWith(RavenwoodAwareTestRunner.class).
+        visitAnnotation(runWithAnotType.desc, true)!!.let { av ->
+            av.visit("value", ravenwoodTestRunnerType.type)
+            av.visitEnd()
+        }
+        log.i("Update the @RunWith: ${classInternalName.toHumanReadableClassName()}")
+    }
+
+    /*
+     Generate the fields and the ctor, which should looks like  this:
+
+  public static final org.junit.rules.TestRule sRavenwoodImplicitClassMinRule;
+    descriptor: Lorg/junit/rules/TestRule;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      0: #49(#50=I#51)
+        org.junit.ClassRule(
+          order=-2147483648
+        )
+
+  public static final org.junit.rules.TestRule sRavenwoodImplicitClassMaxRule;
+    descriptor: Lorg/junit/rules/TestRule;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      0: #49(#50=I#52)
+        org.junit.ClassRule(
+          order=2147483647
+        )
+
+  public final org.junit.rules.TestRule sRavenwoodImplicitInstanceMinRule;
+    descriptor: Lorg/junit/rules/TestRule;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      0: #53(#50=I#51)
+        org.junit.Rule(
+          order=-2147483648
+        )
+
+  public final org.junit.rules.TestRule sRavenwoodImplicitInstanceMaxRule;
+    descriptor: Lorg/junit/rules/TestRule;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      0: #53(#50=I#52)
+        org.junit.Rule(
+          order=2147483647
+        )
+     */
+
+    val sRavenwood_ClassRuleMin = "sRavenwood_ClassRuleMin"
+    val sRavenwood_ClassRuleMax = "sRavenwood_ClassRuleMax"
+    val mRavenwood_InstRuleMin = "mRavenwood_InstRuleMin"
+    val mRavenwood_InstRuleMax = "mRavenwood_InstRuleMax"
+
+    private fun injectRules() {
+        injectRule(sRavenwood_ClassRuleMin, true, Integer.MIN_VALUE)
+        injectRule(sRavenwood_ClassRuleMax, true, Integer.MAX_VALUE)
+        injectRule(mRavenwood_InstRuleMin, false, Integer.MIN_VALUE)
+        injectRule(mRavenwood_InstRuleMax, false, Integer.MAX_VALUE)
+    }
+
+    private fun injectRule(fieldName: String, isStatic: Boolean, order: Int) {
+        visitField(
+            ACC_PUBLIC or ACC_FINAL or (if (isStatic) ACC_STATIC else 0),
+            fieldName,
+            testRuleType.desc,
+            null,
+            null,
+        ).let { fv ->
+            val anot = if (isStatic) { classRuleAnotType } else { ruleAnotType }
+            fv.visitAnnotation(anot.desc, true).let {
+                it.visit("order", order)
+                it.visitEnd()
+            }
+            fv.visitEnd()
+        }
+    }
+
+    override fun visitMethod(
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        exceptions: Array<String>?,
+    ): MethodVisitor {
+        val next = super.visitMethod(access, name, descriptor, signature, exceptions)
+        if (name == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
+            return ClassInitializerVisitor(
+                access, name, descriptor, signature, exceptions, next)
+        }
+        if (name == CTOR_NAME) {
+            return ConstructorVisitor(
+                access, name, descriptor, signature, exceptions, next)
+        }
+        return next
+    }
+
+    /*
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         0: getstatic     #36                 // Field android/platform/test/ravenwood/RavenwoodAwareTestRunner.RavenwoodImplicitClassMinRule:Lorg/junit/rules/TestRule;
+         3: putstatic     #39                 // Field sRavenwoodImplicitClassMinRule:Lorg/junit/rules/TestRule;
+         6: getstatic     #42                 // Field android/platform/test/ravenwood/RavenwoodAwareTestRunner.RavenwoodImplicitClassMaxRule:Lorg/junit/rules/TestRule;
+         9: putstatic     #45                 // Field sRavenwoodImplicitClassMaxRule:Lorg/junit/rules/TestRule;
+        12: return
+      LineNumberTable:
+        line 33: 0
+        line 36: 6
+     */
+    private inner class ClassInitializerVisitor(
+        access: Int,
+        val name: String,
+        val descriptor: String,
+        signature: String?,
+        exceptions: Array<String>?,
+        next: MethodVisitor?,
+    ) : MethodVisitor(OPCODE_VERSION, next) {
+        override fun visitCode() {
+            visitFieldInsn(Opcodes.GETSTATIC,
+                ravenwoodTestRunnerType.internlName,
+                RavenwoodAwareTestRunner.IMPLICIT_CLASS_MIN_RULE_NAME,
+                testRuleType.desc
+            )
+            visitFieldInsn(Opcodes.PUTSTATIC,
+                classInternalName,
+                sRavenwood_ClassRuleMin,
+                testRuleType.desc
+            )
+
+            visitFieldInsn(Opcodes.GETSTATIC,
+                ravenwoodTestRunnerType.internlName,
+                RavenwoodAwareTestRunner.IMPLICIT_CLASS_MAX_RULE_NAME,
+                testRuleType.desc
+            )
+            visitFieldInsn(Opcodes.PUTSTATIC,
+                classInternalName,
+                sRavenwood_ClassRuleMax,
+                testRuleType.desc
+            )
+
+            super.visitCode()
+        }
+    }
+
+    /*
+  public com.android.ravenwoodtest.bivalenttest.runnertest.RavenwoodRunnerTest();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         0: aload_0
+         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
+         4: aload_0
+         5: getstatic     #7                  // Field android/platform/test/ravenwood/RavenwoodAwareTestRunner.RavenwoodImplicitInstanceMinRule:Lorg/junit/rules/TestRule;
+         8: putfield      #13                 // Field sRavenwoodImplicitInstanceMinRule:Lorg/junit/rules/TestRule;
+        11: aload_0
+        12: getstatic     #18                 // Field android/platform/test/ravenwood/RavenwoodAwareTestRunner.RavenwoodImplicitInstanceMaxRule:Lorg/junit/rules/TestRule;
+        15: putfield      #21                 // Field sRavenwoodImplicitInstanceMaxRule:Lorg/junit/rules/TestRule;
+        18: return
+      LineNumberTable:
+        line 31: 0
+        line 38: 4
+        line 41: 11
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      19     0  this   Lcom/android/ravenwoodtest/bivalenttest/runnertest/RavenwoodRunnerTest;
+     */
+    private inner class ConstructorVisitor(
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        exceptions: Array<String>?,
+        next: MethodVisitor?,
+    ) : AdviceAdapter(OPCODE_VERSION, next, ACC_ENUM, name, descriptor) {
+        override fun onMethodEnter() {
+            visitVarInsn(ALOAD, 0)
+            visitFieldInsn(Opcodes.GETSTATIC,
+                ravenwoodTestRunnerType.internlName,
+                RavenwoodAwareTestRunner.IMPLICIT_INST_MIN_RULE_NAME,
+                testRuleType.desc
+            )
+            visitFieldInsn(Opcodes.PUTFIELD,
+                classInternalName,
+                mRavenwood_InstRuleMin,
+                testRuleType.desc
+            )
+
+            visitVarInsn(ALOAD, 0)
+            visitFieldInsn(Opcodes.GETSTATIC,
+                ravenwoodTestRunnerType.internlName,
+                RavenwoodAwareTestRunner.IMPLICIT_INST_MAX_RULE_NAME,
+                testRuleType.desc
+            )
+            visitFieldInsn(Opcodes.PUTFIELD,
+                classInternalName,
+                mRavenwood_InstRuleMax,
+                testRuleType.desc
+            )
+        }
+    }
+
+    /**
+     * Rewrite "order" of the existing junit rules to make sure no rules use a MAX or MIN order.
+     *
+     * Currently, we do it a hacky way -- use an arbitrary cut-off point, and if the order
+     * is larger than that, decrement by 1, and if it's smaller than the negative cut-off point,
+     * increment it by 1.
+     *
+     * (or the arbitrary number is already used.... then we're unlucky, let's change the cut-off
+     * point.)
+     */
+    private inner class FieldRuleOrderRewriter(
+        val fieldName: String,
+        next: FieldVisitor,
+    ) : FieldVisitor(OPCODE_VERSION, next) {
+        override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
+            val fallback = super.visitAnnotation(descriptor, visible)
+            if (descriptor != ruleAnotType.desc && descriptor != classRuleAnotType.desc) {
+                return fallback
+            }
+            return RuleOrderRewriter(fallback)
+        }
+
+        private inner class RuleOrderRewriter(
+            next: AnnotationVisitor,
+        ) : AnnotationVisitor(OPCODE_VERSION, next) {
+            override fun visit(name: String?, origValue: Any?) {
+                if (name != "order") {
+                    return super.visit(name, origValue)
+                }
+                var order = origValue as Int
+                if (order == RULE_ORDER_TWEAK_CUTOFF || order == -RULE_ORDER_TWEAK_CUTOFF) {
+                    // Oops. If this happens, we'll need to change RULE_ORDER_TWEAK_CUTOFF.
+                    // Or, we could scan all the rules in the target jar and find an unused number.
+                    // Because rules propagate to subclasses, we'll at least check all the
+                    // super classes of the current class.
+                    throw RavenizerInternalException(
+                        "OOPS: Field $classInternalName.$fieldName uses $order."
+                                + " We can't update it.")
+                }
+                if (order > RULE_ORDER_TWEAK_CUTOFF) {
+                    order -= 1
+                }
+                if (order < -RULE_ORDER_TWEAK_CUTOFF) {
+                    order += 1
+                }
+                super.visit(name, order)
+            }
+        }
+    }
+
+    companion object {
+        fun shouldProcess(classes: ClassNodes, className: String): Boolean {
+            if (!isTestLookingClass(classes, className)) {
+                return false
+            }
+            // Don't process a class if it has a @NoRavenizer annotation.
+            classes.findClass(className)?.let { cn ->
+                if (cn.findAnyAnnotation(noRavenizerAnotType.descAsSet) != null) {
+                    log.w("Class ${className.toHumanReadableClassName()} has" +
+                        " @${noRavenizerAnotType.humanReadableName}. Skipping."
+                    )
+                    return false
+                }
+            }
+            return true
+        }
+
+        fun maybeApply(
+            className: String,
+            classes: ClassNodes,
+            nextVisitor: ClassVisitor,
+        ): ClassVisitor {
+            if (!shouldProcess(classes, className)) {
+                return nextVisitor
+            } else {
+                return RunnerRewritingAdapter(classes, nextVisitor)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index ded7379..653cd3c3 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -120,6 +120,7 @@
         ":services.backup-sources",
         ":services.companion-sources",
         ":services.contentcapture-sources",
+        ":services.appfunctions-sources",
         ":services.contentsuggestions-sources",
         ":services.contextualsearch-sources",
         ":services.coverage-sources",
@@ -135,6 +136,7 @@
         ":services.searchui-sources",
         ":services.smartspace-sources",
         ":services.soundtrigger-sources",
+        ":services.supervision-sources",
         ":services.systemcaptions-sources",
         ":services.translation-sources",
         ":services.texttospeech-sources",
@@ -217,6 +219,7 @@
         "services.autofill",
         "services.backup",
         "services.companion",
+        "services.appfunctions",
         "services.contentcapture",
         "services.contentsuggestions",
         "services.contextualsearch",
@@ -235,6 +238,7 @@
         "services.searchui",
         "services.smartspace",
         "services.soundtrigger",
+        "services.supervision",
         "services.systemcaptions",
         "services.translation",
         "services.texttospeech",
diff --git a/services/accessibility/Android.bp b/services/accessibility/Android.bp
index efa1397..3d7ad0b 100644
--- a/services/accessibility/Android.bp
+++ b/services/accessibility/Android.bp
@@ -35,7 +35,7 @@
         "androidx.annotation_annotation",
     ],
     static_libs: [
-        "a11ychecker-protos-java-proto-lite",
+        "accessibility_protos_lite",
         "com_android_server_accessibility_flags_lib",
         "//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
     ],
@@ -71,17 +71,6 @@
     aconfig_declarations: "com_android_server_accessibility_flags",
 }
 
-java_library_static {
-    name: "a11ychecker-protos-java-proto-lite",
-    proto: {
-        type: "lite",
-        canonical_path_from_root: false,
-    },
-    srcs: [
-        "java/**/a11ychecker/proto/*.proto",
-    ],
-}
-
 genrule {
     name: "statslog-accessibility-java-gen",
     tools: ["stats-log-api-gen"],
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
index 299d33f..3f85a90 100644
--- a/services/accessibility/TEST_MAPPING
+++ b/services/accessibility/TEST_MAPPING
@@ -25,32 +25,10 @@
       ]
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.accessibility"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksServicesTests_accessibility_Presubmit"
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.accessibilityservice"
-        },
-        {
-          "include-filter": "android.view.accessibility"
-        },
-        {
-          "include-filter": "com.android.internal.accessibility"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksCoreTests_accessibility_NO_FLAKES"
     }
   ],
   "postsubmit": [
@@ -75,18 +53,7 @@
       ]
     },
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.accessibilityservice"
-        },
-        {
-          "include-filter": "android.view.accessibility"
-        },
-        {
-          "include-filter": "com.android.internal.accessibility"
-        }
-      ]
+      "name": "FrameworksCoreTests_accessibility"
     }
   ]
 }
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 8e2e0ad..ee3bbca 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -117,7 +117,14 @@
     name: "enable_magnification_follows_mouse"
     namespace: "accessibility"
     description: "Whether to enable mouse following for fullscreen magnification"
-    bug: "335494097"
+    bug: "354696546"
+}
+
+flag {
+    name: "enable_magnification_keyboard_control"
+    namespace: "accessibility"
+    description: "Whether to enable keyboard control for magnification"
+    bug: "355487062"
 }
 
 flag {
@@ -209,6 +216,16 @@
 }
 
 flag {
+    name: "reset_input_dispatcher_before_first_touch_exploration"
+    namespace: "accessibility"
+    description: "Resets InputDispatcher state by sending ACTION_CANCEL before the first TouchExploration hover events"
+    bug: "364408887"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "scan_packages_without_lock"
     namespace: "accessibility"
     description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
@@ -221,6 +238,7 @@
     description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
     bug: "295575684"
 }
+
 flag {
     name: "send_hover_events_based_on_event_stream"
     namespace: "accessibility"
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index edb6390..3224b27 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -34,6 +34,7 @@
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 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;
@@ -1100,11 +1101,14 @@
         if (svcConnTracingEnabled()) {
             logTraceSvcConn("performGlobalAction", "action=" + action);
         }
+        int currentUserId;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
+            currentUserId = mSystemSupport.getCurrentUserIdLocked();
         }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         final long identity = Binder.clearCallingIdentity();
         try {
             return mSystemActionPerformer.performSystemAction(action);
@@ -2750,6 +2754,11 @@
     @RequiresNoPermission
     @Override
     public void setAnimationScale(float scale) {
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mSystemSupport.getCurrentUserIdLocked();
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         final long identity = Binder.clearCallingIdentity();
         try {
             Settings.Global.putFloat(
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index d16a665..1b2447e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Region;
+import android.hardware.input.InputManager;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -57,6 +58,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
@@ -748,6 +750,7 @@
 
         if ((mEnabledFeatures & FLAG_FEATURE_MOUSE_KEYS) != 0) {
             mMouseKeysInterceptor = new MouseKeysInterceptor(mAms,
+                    Objects.requireNonNull(mContext.getSystemService(InputManager.class)),
                     Looper.myLooper(),
                     Display.DEFAULT_DISPLAY);
             addFirstEventHandler(Display.DEFAULT_DISPLAY, mMouseKeysInterceptor);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 45fcf6b..b541345 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -38,7 +38,11 @@
 import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
 import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
 import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.accessibility.AccessibilityManager.FlashNotificationReason;
 
 import static com.android.hardware.input.Flags.keyboardA11yMouseKeys;
@@ -55,9 +59,11 @@
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
 import static com.android.internal.accessibility.util.AccessibilityStatsLogUtils.logAccessibilityShortcutActivated;
+import static com.android.internal.accessibility.util.AccessibilityUtils.isUserSetupCompleted;
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.accessibility.AccessibilityUserState.doesShortcutTargetsStringContain;
+import static com.android.server.pm.UserManagerService.enforceCurrentUserIfVisibleBackgroundEnabled;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
@@ -157,6 +163,7 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 import android.view.accessibility.IMagnificationConnection;
+import android.view.accessibility.IUserInitializationCompleteCallback;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.internal.R;
@@ -304,6 +311,8 @@
 
     private final PowerManager mPowerManager;
 
+    private final UserManager mUserManager;
+
     private final WindowManagerInternal mWindowManagerService;
 
     private final AccessibilitySecurityPolicy mSecurityPolicy;
@@ -358,6 +367,11 @@
     private final List<SendWindowStateChangedEventRunnable> mSendWindowStateChangedEventRunnables =
             new ArrayList<>();
 
+    @VisibleForTesting
+    final HashSet<IUserInitializationCompleteCallback>
+            mUserInitializationCompleteCallbacks =
+            new HashSet<IUserInitializationCompleteCallback>();
+
     @GuardedBy("mLock")
     private @UserIdInt int mCurrentUserId = UserHandle.USER_SYSTEM;
 
@@ -502,6 +516,7 @@
         super(permissionEnforcer);
         mContext = context;
         mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mUserManager = mContext.getSystemService(UserManager.class);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mTraceManager = AccessibilityTraceManager.getInstance(
                 mWindowManagerService.getAccessibilityController(), this, mLock);
@@ -537,6 +552,7 @@
         super(PermissionEnforcer.fromContext(context));
         mContext = context;
         mPowerManager = context.getSystemService(PowerManager.class);
+        mUserManager = context.getSystemService(UserManager.class);
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mTraceManager = AccessibilityTraceManager.getInstance(
                 mWindowManagerService.getAccessibilityController(), this, mLock);
@@ -937,8 +953,9 @@
                             }
                         }
                         case Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
-                                Settings.Secure.ACCESSIBILITY_QS_TARGETS,
-                                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
+                             Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
+                             Settings.Secure.ACCESSIBILITY_QS_TARGETS,
+                             Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE ->
                                 restoreShortcutTargets(newValue,
                                         ShortcutUtils.convertToType(which));
                     }
@@ -1257,6 +1274,11 @@
     @EnforcePermission(MANAGE_ACCESSIBILITY)
     public void registerSystemAction(RemoteAction action, int actionId) {
         registerSystemAction_enforcePermission();
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mCurrentUserId;
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".registerSystemAction",
                     FLAGS_ACCESSIBILITY_MANAGER, "action=" + action + ";actionId=" + actionId);
@@ -1273,6 +1295,11 @@
     @EnforcePermission(MANAGE_ACCESSIBILITY)
     public void unregisterSystemAction(int actionId) {
         unregisterSystemAction_enforcePermission();
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mCurrentUserId;
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".unregisterSystemAction",
                     FLAGS_ACCESSIBILITY_MANAGER, "actionId=" + actionId);
@@ -1600,6 +1627,11 @@
     @EnforcePermission(STATUS_BAR_SERVICE)
     public void notifyAccessibilityButtonClicked(int displayId, String targetName) {
         notifyAccessibilityButtonClicked_enforcePermission();
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mCurrentUserId;
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonClicked",
                     FLAGS_ACCESSIBILITY_MANAGER,
@@ -1628,6 +1660,11 @@
     @EnforcePermission(STATUS_BAR_SERVICE)
     public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
         notifyAccessibilityButtonVisibilityChanged_enforcePermission();
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mCurrentUserId;
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".notifyAccessibilityButtonVisibilityChanged",
                     FLAGS_ACCESSIBILITY_MANAGER, "shown=" + shown);
@@ -1968,9 +2005,8 @@
                         this, 0, oldUserState.mUserId));
             }
 
-            // Announce user changes only if more that one exist.
-            UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            final boolean announceNewUser = userManager.getUsers().size() > 1;
+            // Announce user changes only if more than one exist.
+            final boolean announceNewUser = mUserManager.getUsers().size() > 1;
 
             // The user changed.
             mCurrentUserId = userId;
@@ -1995,12 +2031,26 @@
             // Accessibility Menu component disabled.
             disableAccessibilityMenuToMigrateIfNeeded();
 
+            // As an initialization step, update the shortcuts for the current user.
+            updateShortcutsForCurrentNavigationMode();
+
             if (announceNewUser) {
                 // Schedule announcement of the current user if needed.
                 mMainHandler.sendMessageDelayed(
                         obtainMessage(AccessibilityManagerService::announceNewUserIfNeeded, this),
                         WAIT_FOR_USER_STATE_FULLY_INITIALIZED_MILLIS);
             }
+
+            for (IUserInitializationCompleteCallback callback
+                    : mUserInitializationCompleteCallbacks) {
+                try {
+                    callback.onUserInitializationComplete(mCurrentUserId);
+                } catch (RemoteException re) {
+                    Log.e("AccessibilityManagerService",
+                            "Error while dispatching userInitializationComplete callback: ",
+                            re);
+                }
+            }
         }
     }
 
@@ -2008,10 +2058,8 @@
         synchronized (mLock) {
             AccessibilityUserState userState = getCurrentUserStateLocked();
             if (userState.isHandlingAccessibilityEventsLocked()) {
-                UserManager userManager = (UserManager) mContext.getSystemService(
-                        Context.USER_SERVICE);
                 String message = mContext.getString(R.string.user_switched,
-                        userManager.getUserInfo(mCurrentUserId).name);
+                        mUserManager.getUserInfo(mCurrentUserId).name);
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_ANNOUNCEMENT);
                 event.getText().add(message);
@@ -2216,6 +2264,69 @@
         mProxyManager.clearCacheLocked();
     }
 
+    @VisibleForTesting
+    void updateShortcutsForCurrentNavigationMode() {
+        synchronized (mLock) {
+            AccessibilityUserState userState = getCurrentUserStateLocked();
+            if (!isUserSetupCompleted(mContext)) {
+                return;
+            }
+            final boolean isInGesturalNavigation = Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(), Settings.Secure.NAVIGATION_MODE,
+                    -1, userState.mUserId) == NAV_BAR_MODE_GESTURAL;
+
+            Set<String> gestureTargets = userState.getShortcutTargetsLocked(GESTURE);
+            Set<String> softwareTargets = userState.getShortcutTargetsLocked(SOFTWARE);
+            int buttonMode = ShortcutUtils.getButtonMode(mContext, userState.mUserId);
+
+            if (android.provider.Flags.a11yStandaloneGestureEnabled()) {
+                if (isInGesturalNavigation) {
+                    if (buttonMode == ACCESSIBILITY_BUTTON_MODE_GESTURE) {
+                        // GESTURE button mode indicates migrating from old version
+                        // User was using gesture, so move all targets into gesture
+                        gestureTargets.addAll(softwareTargets);
+                        softwareTargets.clear();
+                    }
+                    buttonMode = ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+                } else {
+                    // Only change the current button mode if there are gesture targets
+                    // (indicating the user came from gesture mode or is migrating)
+                    if (!gestureTargets.isEmpty()) {
+                        buttonMode = softwareTargets.isEmpty()
+                                ? ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR
+                                : ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+
+                        softwareTargets.addAll(gestureTargets);
+                        gestureTargets.clear();
+                    }
+                }
+            } else {
+                if (!gestureTargets.isEmpty()) {
+                    // Adjust button mode before clearing out gesture targets
+                    if (!softwareTargets.isEmpty()) {
+                        buttonMode = ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+                    } else if (isInGesturalNavigation) {
+                        buttonMode = ACCESSIBILITY_BUTTON_MODE_GESTURE;
+                    } else {
+                        buttonMode = ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+                    }
+                    softwareTargets.addAll(gestureTargets);
+                    gestureTargets.clear();
+                } else if (buttonMode != ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+                    buttonMode = isInGesturalNavigation
+                            ? ACCESSIBILITY_BUTTON_MODE_GESTURE
+                            : ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+                }
+            }
+
+            updateShortcutTargetSets(userState, Set.of(
+                    Pair.create(gestureTargets, GESTURE),
+                    Pair.create(softwareTargets, SOFTWARE)
+            ));
+            ShortcutUtils.setButtonMode(mContext, buttonMode, userState.mUserId);
+        }
+    }
+
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             @NonNull MagnificationConfig config) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
@@ -3113,6 +3224,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
         // We observe windows for accessibility only if there is at least
         // one bound service that can retrieve window content that specified
@@ -3139,6 +3251,14 @@
         for (int i = 0; i < displays.size(); i++) {
             final Display display = displays.get(i);
             if (display != null) {
+                // When supporting visible background users, only track windows on the display
+                // assigned to the current user. The proxy displays are registered only to the
+                // current user.
+                if (UserManager.isVisibleBackgroundUsersEnabled()
+                        && !mProxyManager.isProxyedDisplay(display.getDisplayId())
+                        && !mUmi.isUserVisible(mCurrentUserId, display.getDisplayId())) {
+                    continue;
+                }
                 if (observingWindows) {
                     mA11yWindowManager.startTrackingWindows(display.getDisplayId(),
                             mProxyManager.isProxyedDisplay(display.getDisplayId()));
@@ -3646,6 +3766,23 @@
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
     }
 
+    private void updateShortcutTargetSets(AccessibilityUserState userState,
+            Set<Pair<Set<String>, Integer>> targetSets) {
+        boolean somethingChanged = false;
+        for (Pair<Set<String>, Integer> pair : targetSets) {
+            Set<String> targets = pair.first;
+            int type = pair.second;
+            if (userState.updateShortcutTargetsLocked(targets, type)) {
+                somethingChanged = true;
+                persistColonDelimitedSetToSettingLocked(ShortcutUtils.convertToKey(type),
+                        userState.mUserId, targets, str -> str);
+            }
+        }
+        if (somethingChanged) {
+            scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+        }
+    }
+
     /**
      * 1) Check if the service assigned to accessibility button target sdk version > Q.
      *    If it isn't, remove it from the list and associated setting.
@@ -4710,6 +4847,11 @@
             throws RemoteException {
         registerProxyForDisplay_enforcePermission();
         mSecurityPolicy.checkForAccessibilityPermissionOrRole();
+        int currentUserId;
+        synchronized (mLock) {
+            currentUserId = mCurrentUserId;
+        }
+        enforceCurrentUserIfVisibleBackgroundEnabled(currentUserId);
         if (client == null) {
             return false;
         }
@@ -4761,40 +4903,26 @@
     }
 
     @Override
-    @RequiresNoPermission
-    public boolean startFlashNotificationSequence(String opPkg,
-            @FlashNotificationReason int reason, IBinder token) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return mFlashNotificationsController.startFlashNotificationSequence(opPkg,
-                    reason, token);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+    @EnforcePermission(MANAGE_ACCESSIBILITY)
+    public boolean startFlashNotificationSequence(String opPkg, @FlashNotificationReason int reason,
+            IBinder token) {
+        startFlashNotificationSequence_enforcePermission();
+        return mFlashNotificationsController.startFlashNotificationSequence(opPkg, reason, token);
     }
 
     @Override
-    @RequiresNoPermission
+    @EnforcePermission(MANAGE_ACCESSIBILITY)
     public boolean stopFlashNotificationSequence(String opPkg) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        stopFlashNotificationSequence_enforcePermission();
+        return mFlashNotificationsController.stopFlashNotificationSequence(opPkg);
     }
 
     @Override
-    @RequiresNoPermission
-    public boolean startFlashNotificationEvent(String opPkg,
-            @FlashNotificationReason int reason, String reasonPkg) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            return mFlashNotificationsController.startFlashNotificationEvent(opPkg,
-                    reason, reasonPkg);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+    @EnforcePermission(MANAGE_ACCESSIBILITY)
+    public boolean startFlashNotificationEvent(String opPkg, @FlashNotificationReason int reason,
+            String reasonPkg) {
+        startFlashNotificationEvent_enforcePermission();
+        return mFlashNotificationsController.startFlashNotificationEvent(opPkg, reason, reasonPkg);
     }
 
     @Override
@@ -5505,6 +5633,12 @@
         private final Uri mMouseKeysUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_MOUSE_KEYS_ENABLED);
 
+        private final Uri mNavigationModeUri = Settings.Secure.getUriFor(
+                Settings.Secure.NAVIGATION_MODE);
+
+        private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
+                Settings.Secure.USER_SETUP_COMPLETE);
+
         public AccessibilityContentObserver(Handler handler) {
             super(handler);
         }
@@ -5555,6 +5689,10 @@
                     mAlwaysOnMagnificationUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mMouseKeysUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
+                    mNavigationModeUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
+                    mUserSetupCompleteUri, false, this, UserHandle.USER_ALL);
         }
 
         @Override
@@ -5639,6 +5777,8 @@
                     if (readMouseKeysEnabledLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
+                } else if (mNavigationModeUri.equals(uri) || mUserSetupCompleteUri.equals(uri)) {
+                    updateShortcutsForCurrentNavigationMode();
                 }
             }
         }
@@ -6114,6 +6254,24 @@
     }
 
     @Override
+    @RequiresNoPermission
+    public void registerUserInitializationCompleteCallback(
+            IUserInitializationCompleteCallback callback) {
+        synchronized (mLock) {
+            mUserInitializationCompleteCallbacks.add(callback);
+        }
+    }
+
+    @Override
+    @RequiresNoPermission
+    public void unregisterUserInitializationCompleteCallback(
+            IUserInitializationCompleteCallback callback) {
+        synchronized (mLock) {
+            mUserInitializationCompleteCallbacks.remove(callback);
+        }
+    }
+
+    @Override
     @EnforcePermission(INJECT_EVENTS)
     public void injectInputEventToInputFilter(InputEvent event) {
         injectInputEventToInputFilter_enforcePermission();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 3706dcc..b18e6ba 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -57,6 +57,7 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.util.ShortcutUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -579,6 +580,9 @@
                 .append(String.valueOf(mAlwaysOnMagnificationEnabled));
         pw.append("}");
         pw.println();
+        pw.append("     button mode: ");
+        pw.append(String.valueOf(ShortcutUtils.getButtonMode(mContext, mUserId)));
+        pw.println();
         dumpShortcutTargets(pw, HARDWARE, "shortcut key");
         dumpShortcutTargets(pw, SOFTWARE, "button");
         pw.append("     button target:{").append(mTargetAssignedToAccessibilityButton);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 6007bfd..9a81aa6 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -594,10 +594,6 @@
 
         private boolean windowMattersToAccessibilityLocked(AccessibilityWindow a11yWindow,
                 int windowId, Region regionInScreen, Region unaccountedSpace) {
-            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
-                return false;
-            }
-
             if (a11yWindow.isFocused()) {
                 return true;
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
index 2ce5c2b..54368ca 100644
--- a/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
+++ b/services/accessibility/java/com/android/server/accessibility/MouseKeysInterceptor.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.companion.virtual.VirtualDeviceManager;
 import android.companion.virtual.VirtualDeviceParams;
+import android.hardware.input.InputManager;
 import android.hardware.input.VirtualMouse;
 import android.hardware.input.VirtualMouseButtonEvent;
 import android.hardware.input.VirtualMouseConfig;
@@ -34,8 +35,11 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 
@@ -60,7 +64,7 @@
  * mouse keys of each physical keyboard will control a single (global) mouse pointer.
  */
 public class MouseKeysInterceptor extends BaseEventStreamTransformation
-        implements Handler.Callback {
+        implements Handler.Callback, InputManager.InputDeviceListener {
     private static final String LOG_TAG = "MouseKeysInterceptor";
 
     // To enable these logs, run: 'adb shell setprop log.tag.MouseKeysInterceptor DEBUG'
@@ -77,10 +81,19 @@
 
     private final AccessibilityManagerService mAms;
     private final Handler mHandler;
+    private final InputManager mInputManager;
 
     /** Thread to wait for virtual mouse creation to complete */
     private final Thread mCreateVirtualMouseThread;
 
+    /**
+     * Map of device IDs to a map of key codes to their corresponding {@link MouseKeyEvent} values.
+     * To ensure thread safety for the map, all access and modification of the map
+     * should happen on the same thread, i.e., on the handler thread.
+     */
+    private final SparseArray<SparseArray<MouseKeyEvent>> mDeviceKeyCodeMap =
+            new SparseArray<>();
+
     VirtualDeviceManager.VirtualDevice mVirtualDevice = null;
 
     private VirtualMouse mVirtualMouse = null;
@@ -102,6 +115,21 @@
     /** Whether scroll toggle is on */
     private boolean mScrollToggleOn = false;
 
+    /** The ID of the input device that is currently active */
+    private int mActiveInputDeviceId = 0;
+
+    /**
+     * Enum representing different types of mouse key events, each associated with a specific
+     * key code.
+     *
+     * <p> These events correspond to various mouse actions such as directional movements,
+     * clicks, and scrolls, mapped to specific keys on the keyboard.
+     * The key codes here are the QWERTY key codes, and should be accessed via
+     * {@link MouseKeyEvent#getKeyCode(InputDevice)}
+     * so that it is mapped to the equivalent key on the keyboard layout of the keyboard device
+     * that is actually in use.
+     * </p>
+     */
     public enum MouseKeyEvent {
         DIAGONAL_UP_LEFT_MOVE(KeyEvent.KEYCODE_7),
         UP_MOVE_OR_SCROLL(KeyEvent.KEYCODE_8),
@@ -117,34 +145,64 @@
         RELEASE(KeyEvent.KEYCODE_COMMA),
         SCROLL_TOGGLE(KeyEvent.KEYCODE_PERIOD);
 
-        private final int mKeyCode;
+        private final int mLocationKeyCode;
         MouseKeyEvent(int enumValue) {
-            mKeyCode = enumValue;
+            mLocationKeyCode = enumValue;
         }
 
-        private static final SparseArray<MouseKeyEvent> VALUE_TO_ENUM_MAP = new SparseArray<>();
-
-        static {
-            for (MouseKeyEvent type : MouseKeyEvent.values()) {
-                VALUE_TO_ENUM_MAP.put(type.mKeyCode, type);
-            }
-        }
-
+        @VisibleForTesting
         public final int getKeyCodeValue() {
-            return mKeyCode;
+            return mLocationKeyCode;
         }
 
         /**
-         * Convert int value of the key code to corresponding MouseEvent enum. If no matching
-         * value is found, this will return {@code null}.
+         * Get the key code associated with the given MouseKeyEvent for the given keyboard
+         * input device, taking into account its layout.
+         * The default is to return the keycode for the default layout (QWERTY).
+         * We check if the input device has been generated using {@link InputDevice#getGeneration()}
+         * to test with the default {@link MouseKeyEvent} values in the unit tests.
+         */
+        public int getKeyCode(InputDevice inputDevice) {
+            if (inputDevice.getGeneration() == -1) {
+                return mLocationKeyCode;
+            }
+            return inputDevice.getKeyCodeForKeyLocation(mLocationKeyCode);
+        }
+
+        /**
+         * Convert int value of the key code to corresponding {@link MouseKeyEvent}
+         * enum for a particular device ID.
+         * If no matching value is found, this will return {@code null}.
          */
         @Nullable
-        public static MouseKeyEvent from(int value) {
-            return VALUE_TO_ENUM_MAP.get(value);
+        public static MouseKeyEvent from(int keyCode, int deviceId,
+                SparseArray<SparseArray<MouseKeyEvent>> deviceKeyCodeMap) {
+            SparseArray<MouseKeyEvent> keyCodeToEnumMap = deviceKeyCodeMap.get(deviceId);
+            if (keyCodeToEnumMap != null) {
+                return keyCodeToEnumMap.get(keyCode);
+            }
+            return null;
         }
     }
 
     /**
+     * Create a map of key codes to their corresponding {@link MouseKeyEvent} values
+     * for a specific input device.
+     * The key for {@code mDeviceKeyCodeMap} is the deviceId.
+     * The key for {@code keyCodeToEnumMap} is the keycode for each
+     * {@link MouseKeyEvent} according to the keyboard layout of the input device.
+     */
+    public void initializeDeviceToEnumMap(InputDevice inputDevice) {
+        int deviceId = inputDevice.getId();
+        SparseArray<MouseKeyEvent> keyCodeToEnumMap = new SparseArray<>();
+        for (MouseKeyEvent mouseKeyEventType : MouseKeyEvent.values()) {
+            int keyCode = mouseKeyEventType.getKeyCode(inputDevice);
+            keyCodeToEnumMap.put(keyCode, mouseKeyEventType);
+        }
+        mDeviceKeyCodeMap.put(deviceId, keyCodeToEnumMap);
+    }
+
+    /**
      * Construct a new MouseKeysInterceptor.
      *
      * @param service The service to notify of key events
@@ -152,8 +210,10 @@
      * @param displayId Display ID to send mouse events to
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public MouseKeysInterceptor(AccessibilityManagerService service, Looper looper, int displayId) {
+    public MouseKeysInterceptor(AccessibilityManagerService service,
+            InputManager inputManager, Looper looper, int displayId) {
         mAms = service;
+        mInputManager = inputManager;
         mHandler = new Handler(looper, this);
         // Create the virtual mouse on a separate thread since virtual device creation
         // should happen on an auxiliary thread, and not from the handler's thread.
@@ -163,6 +223,9 @@
             mVirtualMouse = createVirtualMouse(displayId);
         });
         mCreateVirtualMouseThread.start();
+        // Register an input device listener to watch when input devices are
+        // added, removed or reconfigured.
+        mInputManager.registerInputDeviceListener(this, mHandler);
     }
 
     /**
@@ -215,7 +278,8 @@
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     private void performMouseScrollAction(int keyCode) {
-        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+                keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
         float y = switch (mouseKeyEvent) {
             case UP_MOVE_OR_SCROLL -> 1.0f;
             case DOWN_MOVE_OR_SCROLL -> -1.0f;
@@ -247,15 +311,18 @@
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     private void performMouseButtonAction(int keyCode) {
-        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+                keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
         int buttonCode = switch (mouseKeyEvent) {
             case LEFT_CLICK -> VirtualMouseButtonEvent.BUTTON_PRIMARY;
             case RIGHT_CLICK -> VirtualMouseButtonEvent.BUTTON_SECONDARY;
             default -> VirtualMouseButtonEvent.BUTTON_UNKNOWN;
         };
         if (buttonCode != VirtualMouseButtonEvent.BUTTON_UNKNOWN) {
-            sendVirtualMouseButtonEvent(buttonCode, VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
-            sendVirtualMouseButtonEvent(buttonCode, VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE);
+            sendVirtualMouseButtonEvent(buttonCode,
+                    VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
+            sendVirtualMouseButtonEvent(buttonCode,
+                    VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE);
         }
         if (DEBUG) {
             if (buttonCode == VirtualMouseButtonEvent.BUTTON_UNKNOWN) {
@@ -293,7 +360,9 @@
     private void performMousePointerAction(int keyCode) {
         float x = 0f;
         float y = 0f;
-        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(keyCode);
+        MouseKeyEvent mouseKeyEvent = MouseKeyEvent.from(
+                keyCode, mActiveInputDeviceId, mDeviceKeyCodeMap);
+
         switch (mouseKeyEvent) {
             case DIAGONAL_DOWN_LEFT_MOVE -> {
                 x = -MOUSE_POINTER_MOVEMENT_STEP / sqrt(2);
@@ -339,18 +408,19 @@
         }
     }
 
-    private boolean isMouseKey(int keyCode) {
-        return MouseKeyEvent.VALUE_TO_ENUM_MAP.contains(keyCode);
+    private boolean isMouseKey(int keyCode, int deviceId) {
+        SparseArray<MouseKeyEvent> keyCodeToEnumMap = mDeviceKeyCodeMap.get(deviceId);
+        return keyCodeToEnumMap.contains(keyCode);
     }
 
-    private boolean isMouseButtonKey(int keyCode) {
-        return keyCode == MouseKeyEvent.LEFT_CLICK.getKeyCodeValue()
-                || keyCode == MouseKeyEvent.RIGHT_CLICK.getKeyCodeValue();
+    private boolean isMouseButtonKey(int keyCode, InputDevice inputDevice) {
+        return keyCode == MouseKeyEvent.LEFT_CLICK.getKeyCode(inputDevice)
+                || keyCode == MouseKeyEvent.RIGHT_CLICK.getKeyCode(inputDevice);
     }
 
-    private boolean isMouseScrollKey(int keyCode) {
-        return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCodeValue()
-                || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCodeValue();
+    private boolean isMouseScrollKey(int keyCode, InputDevice inputDevice) {
+        return keyCode == MouseKeyEvent.UP_MOVE_OR_SCROLL.getKeyCode(inputDevice)
+                || keyCode == MouseKeyEvent.DOWN_MOVE_OR_SCROLL.getKeyCode(inputDevice);
     }
 
     /**
@@ -373,7 +443,7 @@
     }
 
     /**
-     * Handles key events and forwards mouse key events to the virtual mouse.
+     * Handles key events and forwards mouse key events to the virtual mouse on the handler thread.
      *
      * @param event The key event to handle.
      * @param policyFlags The policy flags associated with the key event.
@@ -385,31 +455,45 @@
             mAms.getTraceManager().logTrace(LOG_TAG + ".onKeyEvent",
                     FLAGS_INPUT_FILTER, "event=" + event + ";policyFlags=" + policyFlags);
         }
+
+        mHandler.post(() -> {
+            onKeyEventInternal(event, policyFlags);
+        });
+    }
+
+    @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+    private void onKeyEventInternal(KeyEvent event, int policyFlags) {
         boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN;
         int keyCode = event.getKeyCode();
+        mActiveInputDeviceId = event.getDeviceId();
+        InputDevice inputDevice = mInputManager.getInputDevice(mActiveInputDeviceId);
 
-        if (!isMouseKey(keyCode)) {
+        if (!mDeviceKeyCodeMap.contains(mActiveInputDeviceId)) {
+            initializeDeviceToEnumMap(inputDevice);
+        }
+
+        if (!isMouseKey(keyCode, mActiveInputDeviceId)) {
             // Pass non-mouse key events to the next handler
             super.onKeyEvent(event, policyFlags);
         } else if (isDown) {
-            if (keyCode == MouseKeyEvent.SCROLL_TOGGLE.getKeyCodeValue()) {
+            if (keyCode == MouseKeyEvent.SCROLL_TOGGLE.getKeyCode(inputDevice)) {
                 mScrollToggleOn = !mScrollToggleOn;
                 if (DEBUG) {
                     Slog.d(LOG_TAG, "Scroll toggle " + (mScrollToggleOn ? "ON" : "OFF"));
                 }
-            } else if (keyCode == MouseKeyEvent.HOLD.getKeyCodeValue()) {
+            } else if (keyCode == MouseKeyEvent.HOLD.getKeyCode(inputDevice)) {
                 sendVirtualMouseButtonEvent(
                         VirtualMouseButtonEvent.BUTTON_PRIMARY,
                         VirtualMouseButtonEvent.ACTION_BUTTON_PRESS
                 );
-            } else if (keyCode == MouseKeyEvent.RELEASE.getKeyCodeValue()) {
+            } else if (keyCode == MouseKeyEvent.RELEASE.getKeyCode(inputDevice)) {
                 sendVirtualMouseButtonEvent(
                         VirtualMouseButtonEvent.BUTTON_PRIMARY,
                         VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE
                 );
-            } else if (isMouseButtonKey(keyCode)) {
+            } else if (isMouseButtonKey(keyCode, inputDevice)) {
                 performMouseButtonAction(keyCode);
-            } else if (mScrollToggleOn && isMouseScrollKey(keyCode)) {
+            } else if (mScrollToggleOn && isMouseScrollKey(keyCode, inputDevice)) {
                 // If the scroll key is pressed down and no other key is active,
                 // set it as the active key and send a message to scroll the pointer
                 if (mActiveScrollKey == KEY_NOT_SET) {
@@ -439,7 +523,8 @@
                 mHandler.removeMessages(MESSAGE_SCROLL_MOUSE_POINTER);
             } else {
                 Slog.i(LOG_TAG, "Dropping event with key code: '" + keyCode
-                        + "', with no matching down event from deviceId = " + event.getDeviceId());
+                        + "', with no matching down event from deviceId = "
+                        + event.getDeviceId());
             }
         }
     }
@@ -503,12 +588,40 @@
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Override
     public void onDestroy() {
-        // Clear mouse state
-        mActiveMoveKey = KEY_NOT_SET;
-        mActiveScrollKey = KEY_NOT_SET;
-        mLastTimeKeyActionPerformed = 0;
+        mHandler.post(() -> {
+            // Clear mouse state
+            mActiveMoveKey = KEY_NOT_SET;
+            mActiveScrollKey = KEY_NOT_SET;
+            mLastTimeKeyActionPerformed = 0;
+            mDeviceKeyCodeMap.clear();
+        });
 
         mHandler.removeCallbacksAndMessages(null);
         mVirtualDevice.close();
     }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        mDeviceKeyCodeMap.remove(deviceId);
+    }
+
+    /**
+     * The user can change the keyboard layout from settings at anytime, which would change
+     * key character map for that device. Hence, we should use this callback to
+     * update the key code to enum mapping if there is a change in the physical keyboard detected.
+     *
+     * @param deviceId The id of the input device that changed.
+     */
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+        InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+        // Update the enum mapping only if input device that changed is a keyboard
+        if (inputDevice.isFullKeyboard() && !mDeviceKeyCodeMap.contains(deviceId)) {
+            initializeDeviceToEnumMap(inputDevice);
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
index f7a59a4b..950246f 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManager.java
@@ -30,7 +30,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.accessibility.Flags;
-import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported;
 
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
@@ -58,7 +57,7 @@
     private final PackageManager mPackageManager;
     private final Set<AccessibilityHierarchyCheck> mHierarchyChecks;
     private final ATFHierarchyBuilder mATFHierarchyBuilder;
-    private final Set<AccessibilityCheckResultReported> mCachedResults = new HashSet<>();
+    private final Set<AndroidAccessibilityCheckerResult> mCachedResults = new HashSet<>();
 
     @VisibleForTesting
     final A11yCheckerTimer mTimer = new A11yCheckerTimer();
@@ -85,35 +84,44 @@
      * logging. Returns the check results for the given nodes.
      */
     @RequiresPermission(allOf = {android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
-    public Set<AccessibilityCheckResultReported> maybeRunA11yChecker(
+    public Set<AndroidAccessibilityCheckerResult> maybeRunA11yChecker(
             List<AccessibilityNodeInfo> nodes, @Nullable String sourceEventClassName,
             ComponentName a11yServiceComponentName, @UserIdInt int userId) {
-        if (!shouldRunA11yChecker()) {
+        if (!shouldRunA11yChecker() || nodes.isEmpty()) {
             return Set.of();
         }
 
-        Set<AccessibilityCheckResultReported> allResults = new HashSet<>();
+        Set<AndroidAccessibilityCheckerResult> allResults = new HashSet<>();
         String defaultBrowserName = mPackageManager.getDefaultBrowserPackageNameAsUser(userId);
 
         try {
+            AndroidAccessibilityCheckerResult.Builder commonResultBuilder =
+                    AccessibilityCheckerUtils.getCommonResultBuilder(nodes.getFirst(),
+                            sourceEventClassName, mPackageManager, a11yServiceComponentName);
+            if (commonResultBuilder == null) {
+                return Set.of();
+            }
             for (AccessibilityNodeInfo nodeInfo : nodes) {
-                // Skip browser results because they are mostly related to web content and not the
-                // browser app itself.
+                // Skip browser results because they are mostly related to web content and
+                // not the browser app itself.
                 if (nodeInfo.getPackageName() == null
                         || nodeInfo.getPackageName().toString().equals(defaultBrowserName)) {
                     continue;
                 }
-                List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(nodeInfo);
-                Set<AccessibilityCheckResultReported> filteredResults =
+                List<AccessibilityHierarchyCheckResult> checkResults = runChecksOnNode(
+                        nodeInfo);
+                Set<AndroidAccessibilityCheckerResult> filteredResults =
                         AccessibilityCheckerUtils.processResults(nodeInfo, checkResults,
-                                sourceEventClassName, mPackageManager, a11yServiceComponentName);
+                                commonResultBuilder);
                 allResults.addAll(filteredResults);
             }
             mCachedResults.addAll(allResults);
+            return allResults;
+
         } catch (RuntimeException e) {
             Slog.e(LOG_TAG, "An unknown error occurred while running a11y checker.", e);
+            return Set.of();
         }
-        return allResults;
     }
 
     private List<AccessibilityHierarchyCheckResult> runChecksOnNode(
@@ -127,7 +135,7 @@
         return checkResults;
     }
 
-    public Set<AccessibilityCheckResultReported> getCachedResults() {
+    public Set<AndroidAccessibilityCheckerResult> getCachedResults() {
         return Collections.unmodifiableSet(mCachedResults);
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java
index 1b3ec5a..fa0bb59 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerStatsdLogger.java
@@ -16,10 +16,9 @@
 
 package com.android.server.accessibility.a11ychecker;
 
+import android.text.TextUtils;
 import android.util.Slog;
 
-import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported;
-
 import java.util.Set;
 
 
@@ -35,11 +34,11 @@
     /**
      * Writes results to statsd.
      */
-    public static void logResults(Set<AccessibilityCheckResultReported> results) {
-        Slog.i(LOG_TAG, String.format("Writing %d AccessibilityCheckResultReported events",
+    public static void logResults(Set<AndroidAccessibilityCheckerResult> results) {
+        Slog.i(LOG_TAG, TextUtils.formatSimple("Writing %d AccessibilityCheckResultReported events",
                 results.size()));
 
-        for (AccessibilityCheckResultReported result : results) {
+        for (AndroidAccessibilityCheckerResult result : results) {
             AccessibilityCheckerStatsLog.write(ATOM_ID,
                     result.getPackageName(),
                     result.getAppVersionCode(),
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
index fa0fed2..a739304 100644
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java
@@ -17,6 +17,8 @@
 package com.android.server.accessibility.a11ychecker;
 
 
+import android.accessibility.AccessibilityCheckClass;
+import android.accessibility.AccessibilityCheckResultType;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.PackageInfo;
@@ -25,9 +27,6 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckClass;
-import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultReported;
-import com.android.server.accessibility.a11ychecker.A11yCheckerProto.AccessibilityCheckResultType;
 
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult;
 import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
@@ -92,44 +91,55 @@
                             AccessibilityCheckClass.TRAVERSAL_ORDER_CHECK));
     // LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto)
 
-    static Set<AccessibilityCheckResultReported> processResults(
+
+    /**
+     * Returns AccessibilityCheckResultReported.Builder with the common fields for all nodes
+     * belonging in the same cache pre-filled.
+     */
+    static @Nullable AndroidAccessibilityCheckerResult.Builder getCommonResultBuilder(
             AccessibilityNodeInfo nodeInfo,
-            List<AccessibilityHierarchyCheckResult> checkResults,
             @Nullable String activityClassName,
             PackageManager packageManager,
             ComponentName a11yServiceComponentName) {
-        String appPackageName = nodeInfo.getPackageName().toString();
-        String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo);
-        if (nodePath == null) {
-            return Set.of();
+        if (nodeInfo.getPackageName() == null) {
+            return null;
         }
-        AccessibilityCheckResultReported.Builder builder;
+        String appPackageName = nodeInfo.getPackageName().toString();
         try {
-            builder = AccessibilityCheckResultReported.newBuilder()
+            return AndroidAccessibilityCheckerResult.newBuilder()
                     .setPackageName(appPackageName)
                     .setAppVersionCode(getAppVersionCode(packageManager, appPackageName))
-                    .setUiElementPath(nodePath)
                     .setActivityName(
                             getActivityName(packageManager, appPackageName, activityClassName))
                     .setWindowTitle(getWindowTitle(nodeInfo))
-                    .setSourceComponentName(a11yServiceComponentName.flattenToString())
-                    .setSourceVersionCode(
-                            getAppVersionCode(packageManager,
-                                    a11yServiceComponentName.getPackageName()));
+                    .setSourceComponentName(a11yServiceComponentName)
+                    .setSourceVersionCode(getAppVersionCode(packageManager,
+                            a11yServiceComponentName.getPackageName()));
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(LOG_TAG, "Unknown package name", e);
+            return null;
+        }
+    }
+
+    static Set<AndroidAccessibilityCheckerResult> processResults(
+            AccessibilityNodeInfo nodeInfo,
+            List<AccessibilityHierarchyCheckResult> checkResults,
+            AndroidAccessibilityCheckerResult.Builder resultBuilder) {
+        String nodePath = AccessibilityNodePathBuilder.createNodePath(nodeInfo);
+        if (resultBuilder == null || nodePath == null) {
             return Set.of();
         }
-
         return checkResults.stream()
                 .filter(checkResult -> checkResult.getType()
                         == AccessibilityCheckResult.AccessibilityCheckResultType.ERROR
                         || checkResult.getType()
                         == AccessibilityCheckResult.AccessibilityCheckResultType.WARNING)
-                .map(checkResult -> builder.setResultCheckClass(
-                        getCheckClass(checkResult)).setResultType(
-                        getCheckResultType(checkResult)).setResultId(
-                        checkResult.getResultId()).build())
+                .map(checkResult -> new AndroidAccessibilityCheckerResult.Builder(resultBuilder)
+                        .setUiElementPath(nodePath)
+                        .setResultCheckClass(getCheckClass(checkResult))
+                        .setResultType(getCheckResultType(checkResult))
+                        .setResultId(checkResult.getResultId())
+                        .build())
                 .collect(Collectors.toUnmodifiableSet());
     }
 
@@ -188,9 +198,9 @@
     private static AccessibilityCheckResultType getCheckResultType(
             AccessibilityHierarchyCheckResult checkResult) {
         return switch (checkResult.getType()) {
-            case ERROR -> AccessibilityCheckResultType.ERROR;
-            case WARNING -> AccessibilityCheckResultType.WARNING;
-            default -> AccessibilityCheckResultType.UNKNOWN_RESULT_TYPE;
+            case ERROR -> AccessibilityCheckResultType.ERROR_CHECK_RESULT_TYPE;
+            case WARNING -> AccessibilityCheckResultType.WARNING_CHECK_RESULT_TYPE;
+            default -> AccessibilityCheckResultType.UNKNOWN_CHECK_RESULT_TYPE;
         };
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/AndroidAccessibilityCheckerResult.java b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AndroidAccessibilityCheckerResult.java
new file mode 100644
index 0000000..c9cd9fe
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/a11ychecker/AndroidAccessibilityCheckerResult.java
@@ -0,0 +1,196 @@
+/*
+ * 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.server.accessibility.a11ychecker;
+
+import android.accessibility.AccessibilityCheckClass;
+import android.accessibility.AccessibilityCheckResultType;
+import android.content.ComponentName;
+import android.text.TextUtils;
+
+public class AndroidAccessibilityCheckerResult implements Cloneable {
+    // Package name of the app containing the checked View.
+    private String mPackageName;
+    // Version code of the app containing the checked View.
+    private long mAppVersionCode;
+    // The path of the View starting from the root element in the window. Each element is
+    // represented by the View's resource id, when available, or the View's class name.
+    private String mUiElementPath;
+    // Class name of the activity containing the checked View.
+    private String mActivityName;
+    // Title of the window containing the checked View.
+    private String mWindowTitle;
+    // The component name of the app running the AccessibilityService which provided the a11y node.
+    private String mSourceComponentName;
+    // Version code of the app running the AccessibilityService that provided the a11y node.
+    private long mSourceVersionCode;
+    // Class Name of the AccessibilityCheck that produced the result.
+    private AccessibilityCheckClass mResultCheckClass;
+    // Result type of the AccessibilityCheckResult.
+    private AccessibilityCheckResultType mResultType;
+    // Result ID of the AccessibilityCheckResult.
+    private int mResultId;
+
+    static final class Builder {
+        private final AndroidAccessibilityCheckerResult mInstance;
+
+        Builder() {
+            mInstance = new AndroidAccessibilityCheckerResult();
+        }
+
+        Builder(Builder otherBuilder) {
+            mInstance = otherBuilder.mInstance.clone();
+        }
+
+        public Builder setPackageName(String packageName) {
+            mInstance.mPackageName = packageName;
+            return this;
+        }
+
+        public Builder setAppVersionCode(long versionCode) {
+            mInstance.mAppVersionCode = versionCode;
+            return this;
+        }
+
+        public Builder setUiElementPath(String uiElementPath) {
+            mInstance.mUiElementPath = uiElementPath;
+            return this;
+        }
+
+        public Builder setActivityName(String activityName) {
+            mInstance.mActivityName = activityName;
+            return this;
+        }
+
+        public Builder setWindowTitle(String windowTitle) {
+            mInstance.mWindowTitle = windowTitle;
+            return this;
+        }
+
+        public Builder setSourceComponentName(ComponentName componentName) {
+            mInstance.mSourceComponentName = componentName.flattenToString();
+            return this;
+        }
+
+        public Builder setSourceVersionCode(long versionCode) {
+            mInstance.mSourceVersionCode = versionCode;
+            return this;
+        }
+
+        public Builder setResultCheckClass(AccessibilityCheckClass checkClass) {
+            mInstance.mResultCheckClass = checkClass;
+            return this;
+        }
+
+        public Builder setResultType(AccessibilityCheckResultType resultType) {
+            mInstance.mResultType = resultType;
+            return this;
+        }
+
+        public Builder setResultId(int resultId) {
+            mInstance.mResultId = resultId;
+            return this;
+        }
+
+        public AndroidAccessibilityCheckerResult build() {
+            // TODO: assert all fields are set, etc
+            return mInstance;
+        }
+    }
+
+    static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public long getAppVersionCode() {
+        return mAppVersionCode;
+    }
+
+    public String getUiElementPath() {
+        return mUiElementPath;
+    }
+
+    public String getActivityName() {
+        return mActivityName;
+    }
+
+    public String getWindowTitle() {
+        return mWindowTitle;
+    }
+
+    public String getSourceComponentName() {
+        return mSourceComponentName;
+    }
+
+    public long getSourceVersionCode() {
+        return mSourceVersionCode;
+    }
+
+    public AccessibilityCheckClass getResultCheckClass() {
+        return mResultCheckClass;
+    }
+
+    public AccessibilityCheckResultType getResultType() {
+        return mResultType;
+    }
+
+    public int getResultId() {
+        return mResultId;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof AndroidAccessibilityCheckerResult)) {
+            return false;
+        }
+        AndroidAccessibilityCheckerResult otherResult = (AndroidAccessibilityCheckerResult) other;
+        return mPackageName.equals(otherResult.mPackageName)
+                && mAppVersionCode == otherResult.mAppVersionCode
+                && mUiElementPath.equals(otherResult.mUiElementPath)
+                && mActivityName.equals(otherResult.mActivityName)
+                && mWindowTitle.equals(otherResult.mWindowTitle)
+                && mSourceComponentName.equals(otherResult.mSourceComponentName)
+                && mSourceVersionCode == otherResult.mSourceVersionCode
+                && mResultCheckClass.equals(otherResult.mResultCheckClass)
+                && mResultType.equals(otherResult.mResultType)
+                && mResultId == otherResult.mResultId;
+    }
+
+    @Override
+    public int hashCode() {
+        return toString().hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return TextUtils.formatSimple("%s:%d:%s:%s:%s:%s:%d:%s:%s:%d", mPackageName,
+                mAppVersionCode, mUiElementPath, mActivityName, mWindowTitle, mSourceComponentName,
+                mSourceVersionCode, mResultCheckClass.name(), mResultType.name(), mResultId);
+    }
+
+    @Override
+    public AndroidAccessibilityCheckerResult clone() {
+        try {
+            return (AndroidAccessibilityCheckerResult) super.clone();
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto b/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto
deleted file mode 100644
index 8beed4a..0000000
--- a/services/accessibility/java/com/android/server/accessibility/a11ychecker/proto/a11ychecker.proto
+++ /dev/null
@@ -1,73 +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.
- */
-syntax = "proto2";
-package android.accessibility;
-
-option java_package = "com.android.server.accessibility.a11ychecker";
-option java_outer_classname = "A11yCheckerProto";
-
-// TODO(b/326385939): remove and replace usage with the atom extension proto, when submitted.
-/** Logs the result of an AccessibilityCheck. */
-message AccessibilityCheckResultReported {
-  // Package name of the app containing the checked View.
-  optional string package_name = 1;
-  // Version code of the app containing the checked View.
-  optional int64 app_version_code = 2;
-  // The path of the View starting from the root element in the window. Each element is
-  // represented by the View's resource id, when available, or the View's class name.
-  optional string ui_element_path = 3;
-  // Class name of the activity containing the checked View.
-  optional string activity_name = 4;
-  // Title of the window containing the checked View.
-  optional string window_title = 5;
-  // The flattened component name of the app running the AccessibilityService which provided the a11y node.
-  optional string source_component_name = 6;
-  // Version code of the app running the AccessibilityService that provided the a11y node.
-  optional int64 source_version_code = 7;
-  // Class Name of the AccessibilityCheck that produced the result.
-  optional AccessibilityCheckClass result_check_class = 8;
-  // Result type of the AccessibilityCheckResult.
-  optional AccessibilityCheckResultType result_type = 9;
-  // Result ID of the AccessibilityCheckResult.
-  optional int32 result_id = 10;
-}
-
-/** The AccessibilityCheck class. */
-// LINT.IfChange
-enum AccessibilityCheckClass {
-  UNKNOWN_CHECK = 0;
-  CLASS_NAME_CHECK = 1;
-  CLICKABLE_SPAN_CHECK = 2;
-  DUPLICATE_CLICKABLE_BOUNDS_CHECK = 3;
-  DUPLICATE_SPEAKABLE_TEXT_CHECK = 4;
-  EDITABLE_CONTENT_DESC_CHECK = 5;
-  IMAGE_CONTRAST_CHECK = 6;
-  LINK_PURPOSE_UNCLEAR_CHECK = 7;
-  REDUNDANT_DESCRIPTION_CHECK = 8;
-  SPEAKABLE_TEXT_PRESENT_CHECK = 9;
-  TEXT_CONTRAST_CHECK = 10;
-  TEXT_SIZE_CHECK = 11;
-  TOUCH_TARGET_SIZE_CHECK = 12;
-  TRAVERSAL_ORDER_CHECK = 13;
-}
-// LINT.ThenChange(/services/accessibility/java/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtils.java)
-
-/** The type of AccessibilityCheckResult */
-enum AccessibilityCheckResultType {
-  UNKNOWN_RESULT_TYPE = 0;
-  ERROR = 1;
-  WARNING = 2;
-}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
index 95559802..7f79556 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTap.java
@@ -89,7 +89,7 @@
 
     @Override
     protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+        cancelPendingTransitions();
         if (!isInsideSlop(rawEvent, mTouchSlop)) {
             cancelGesture(event, rawEvent, policyFlags);
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
index 15e1278..872ade5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
@@ -46,7 +46,6 @@
     @Override
     protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         super.onUp(event, rawEvent, policyFlags);
-        cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
     }
 
     @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 04b42e4..0ed239e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1613,6 +1613,19 @@
                 dispatchGesture(gestureEvent);
             }
             if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
+                if (Flags.resetInputDispatcherBeforeFirstTouchExploration()
+                        && !mState.hasResetInputDispatcherState()) {
+                    // Cancel any possible ongoing touch gesture from before touch exploration
+                    // started. This clears out the InputDispatcher event stream state so that it
+                    // is ready to accept new injected HOVER events.
+                    mDispatcher.sendMotionEvent(
+                            mEvents.get(0),
+                            ACTION_CANCEL,
+                            mRawEvents.get(0),
+                            mPointerIdBits,
+                            mPolicyFlags);
+                    setHasResetInputDispatcherState(true);
+                }
                 // Deliver a down event.
                 mDispatcher.sendMotionEvent(
                         mEvents.get(0),
@@ -1773,4 +1786,9 @@
                 + ", mDraggingPointerId: " + mDraggingPointerId
                 + " }";
     }
+
+    @VisibleForTesting
+    void setHasResetInputDispatcherState(boolean value) {
+        mState.setHasResetInputDispatcherState(value);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index e13994e..f15b8ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -86,6 +86,7 @@
     private MotionEvent mLastInjectedHoverEvent;
     // The last injected hover event used for performing clicks.
     private MotionEvent mLastInjectedHoverEventForClick;
+    private boolean mHasResetInputDispatcherState;
     // The time of the last injected down.
     private long mLastInjectedDownEventTime;
     // Keep track of which pointers sent to the system are down.
@@ -361,6 +362,14 @@
         return mLastInjectedDownEventTime;
     }
 
+    boolean hasResetInputDispatcherState() {
+        return mHasResetInputDispatcherState;
+    }
+
+    void setHasResetInputDispatcherState(boolean value) {
+        mHasResetInputDispatcherState = value;
+    }
+
     public int getLastTouchedWindowId() {
         return mLastTouchedWindowId;
     }
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 b052d23..aa57e0b 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -16,8 +16,6 @@
 
 package com.android.server.accessibility.magnification;
 
-import static android.view.InputDevice.SOURCE_MOUSE;
-import static android.view.InputDevice.SOURCE_STYLUS;
 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
@@ -307,12 +305,8 @@
         }
 
         mDelegatingState = new DelegatingState();
-        mDetectingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
-                ? new DetectingStateWithMultiFinger(context)
-                : new DetectingState(context);
-        mViewportDraggingState = Flags.enableMagnificationMultipleFingerMultipleTapGesture()
-                ? new ViewportDraggingStateWithMultiFinger()
-                : new ViewportDraggingState();
+        mDetectingState = new DetectingState(context);
+        mViewportDraggingState = new ViewportDraggingState();
         mPanningScalingState = new PanningScalingState(context);
         mSinglePanningState = new SinglePanningState(context);
         mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
@@ -342,8 +336,12 @@
                 cancelFling();
             }
             handleTouchEventWith(mCurrentState, event, rawEvent, policyFlags);
-        } else if (Flags.enableMagnificationFollowsMouse()
-                && (event.getSource() == SOURCE_MOUSE || event.getSource() == SOURCE_STYLUS)) {
+        }
+    }
+
+    @Override
+    void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (Flags.enableMagnificationFollowsMouse()) {
             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.
@@ -351,8 +349,6 @@
                 // Send through the mouse/stylus event handler.
                 mMouseEventHandler.onEvent(event, mDisplayId);
             }
-            // Dispatch to normal event handling flow.
-            dispatchTransformedEvent(event, rawEvent, policyFlags);
         }
     }
 
@@ -701,62 +697,6 @@
         }
     }
 
-    final class ViewportDraggingStateWithMultiFinger extends ViewportDraggingState {
-        // LINT.IfChange(viewport_dragging_state_with_multi_finger)
-        @Override
-        public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
-                throws GestureException {
-            final int action = event.getActionMasked();
-            switch (action) {
-                case ACTION_POINTER_DOWN: {
-                    clearAndTransitToPanningScalingState();
-                }
-                break;
-                case ACTION_MOVE: {
-                    if (event.getPointerCount() > 2) {
-                        throw new GestureException("Should have one pointer down.");
-                    }
-                    final float eventX = event.getX();
-                    final float eventY = event.getY();
-                    if (mFullScreenMagnificationController.magnificationRegionContains(
-                            mDisplayId, eventX, eventY)) {
-                        mFullScreenMagnificationController.setCenter(mDisplayId, eventX, eventY,
-                                /* animate */ mLastMoveOutsideMagnifiedRegion,
-                                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
-                        mLastMoveOutsideMagnifiedRegion = false;
-                    } else {
-                        mLastMoveOutsideMagnifiedRegion = true;
-                    }
-                }
-                break;
-
-                case ACTION_UP:
-                case ACTION_CANCEL: {
-                    // If mScaleToRecoverAfterDraggingEnd >= 1.0, the dragging state is triggered
-                    // by zoom in temporary, and the magnifier needs to recover to original scale
-                    // after exiting dragging state.
-                    // Otherwise, the magnifier should be disabled.
-                    if (mScaleToRecoverAfterDraggingEnd >= 1.0f) {
-                        zoomToScale(mScaleToRecoverAfterDraggingEnd, event.getX(),
-                                event.getY());
-                    } else {
-                        zoomOff();
-                    }
-                    clear();
-                    mScaleToRecoverAfterDraggingEnd = Float.NaN;
-                    transitionTo(mDetectingState);
-                }
-                    break;
-
-                case ACTION_DOWN: {
-                    throw new GestureException(
-                            "Unexpected event type: " + MotionEvent.actionToString(action));
-                }
-            }
-        }
-        // LINT.ThenChange(:viewport_dragging_state)
-    }
-
     /**
      * This class handles motion events when the event dispatcher has
      * determined that the user is performing a single-finger drag of the
@@ -777,7 +717,6 @@
 
         protected boolean mLastMoveOutsideMagnifiedRegion;
 
-        // LINT.IfChange(viewport_dragging_state)
         @Override
         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags)
                 throws GestureException {
@@ -788,7 +727,11 @@
                 }
                 break;
                 case ACTION_MOVE: {
-                    if (event.getPointerCount() != 1) {
+                    if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+                        if (event.getPointerCount() > 2) {
+                            throw new GestureException("Should have at most two pointers down.");
+                        }
+                    } else if (event.getPointerCount() != 1) {
                         throw new GestureException("Should have one pointer down.");
                     }
                     final float eventX = event.getX();
@@ -823,14 +766,20 @@
                 }
                     break;
 
-                case ACTION_DOWN:
                 case ACTION_POINTER_UP: {
+                    if (!Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
+                        throw new GestureException(
+                                "Unexpected event type: " + MotionEvent.actionToString(action));
+                    }
+                }
+                break;
+
+                case ACTION_DOWN: {
                     throw new GestureException(
                             "Unexpected event type: " + MotionEvent.actionToString(action));
                 }
             }
         }
-        // LINT.ThenChange(:viewport_dragging_state_with_multi_finger)
 
         private boolean isAlwaysOnMagnificationEnabled() {
             return mFullScreenMagnificationController.isAlwaysOnMagnificationEnabled();
@@ -916,270 +865,31 @@
         }
     }
 
-    final class DetectingStateWithMultiFinger extends DetectingState {
-        private static final int TWO_FINGER_GESTURE_MAX_TAPS = 2;
-        // A flag set to true when two fingers have touched down.
-        // Used to indicate what next finger action should be.
-        private boolean mIsTwoFingerCountReached = false;
-        // A tap counts when two fingers are down and up once.
-        private int mCompletedTapCount = 0;
-        DetectingStateWithMultiFinger(Context context) {
-            super(context);
-        }
-
-        // LINT.IfChange(detecting_state_with_multi_finger)
-        @Override
-        public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-            cacheDelayedMotionEvent(event, rawEvent, policyFlags);
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN: {
-                    mLastDetectingDownEventTime = event.getDownTime();
-                    mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
-
-                    mFirstPointerDownLocation.set(event.getX(), event.getY());
-
-                    if (!mFullScreenMagnificationController.magnificationRegionContains(
-                            mDisplayId, event.getX(), event.getY())) {
-
-                        transitionToDelegatingStateAndClear();
-
-                    } else if (isMultiTapTriggered(2 /* taps */)) {
-
-                        // 3tap and hold
-                        afterLongTapTimeoutTransitionToDraggingState(event);
-
-                    } else if (isTapOutOfDistanceSlop()) {
-
-                        transitionToDelegatingStateAndClear();
-
-                    } else if (mDetectSingleFingerTripleTap
-                            || mDetectTwoFingerTripleTap
-                            // If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
-                            // to ensure reachability of
-                            // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
-                            || isActivated()) {
-
-                        afterMultiTapTimeoutTransitionToDelegatingState();
-
-                    } else {
-
-                        // Delegate pending events without delay
-                        transitionToDelegatingStateAndClear();
-                    }
-                }
-                break;
-                case ACTION_POINTER_DOWN: {
-                    mIsTwoFingerCountReached = mDetectTwoFingerTripleTap
-                            && event.getPointerCount() == 2;
-                    mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
-
-                    if (event.getPointerCount() == 2) {
-                        if (isMultiFingerMultiTapTriggered(
-                                TWO_FINGER_GESTURE_MAX_TAPS - 1, event)) {
-                            // 3tap and hold
-                            afterLongTapTimeoutTransitionToDraggingState(event);
-                        } else {
-                            if (mDetectTwoFingerTripleTap) {
-                                // If mDetectTwoFingerTripleTap, delay transition to the delegating
-                                // state for mMultiTapMaxDelay to ensure reachability of
-                                // multi finger multi tap
-                                afterMultiTapTimeoutTransitionToDelegatingState();
-                            }
-
-                            if (isActivated()) {
-                                // If activated, delay transition to the panning scaling
-                                // state for tap timeout to ensure reachability of
-                                // multi finger multi tap
-                                storePointerDownLocation(mSecondPointerDownLocation, event);
-                                mHandler.sendEmptyMessageDelayed(
-                                        MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
-                                        ViewConfiguration.getTapTimeout());
-                            }
-                        }
-                    } else {
-                        transitionToDelegatingStateAndClear();
-                    }
-                }
-                break;
-                case ACTION_POINTER_UP: {
-                    // If it is a two-finger gesture, do not transition to the delegating state
-                    // to ensure the reachability of
-                    // the two-finger triple tap (triggerable with ACTION_MOVE and ACTION_UP)
-                    if (!mIsTwoFingerCountReached) {
-                        transitionToDelegatingStateAndClear();
-                    }
-                }
-                break;
-                case ACTION_MOVE: {
-                    if (isFingerDown()
-                            && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-                        // Swipe detected - transition immediately
-
-                        // For convenience, viewport dragging takes precedence
-                        // over insta-delegating on 3tap&swipe
-                        // (which is a rare combo to be used aside from magnification)
-                        if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
-                            transitionToViewportDraggingStateAndClear(event);
-                        } else if (isMultiFingerMultiTapTriggered(
-                                TWO_FINGER_GESTURE_MAX_TAPS - 1, event)
-                                && event.getPointerCount() == 2) {
-                            transitionToViewportDraggingStateAndClear(event);
-                        } else if (isActivated() && event.getPointerCount() == 2) {
-                            if (mOverscrollHandler != null
-                                    && overscrollState(event, mFirstPointerDownLocation)
-                                    == OVERSCROLL_VERTICAL_EDGE) {
-                                transitionToDelegatingStateAndClear();
-                            } else {
-                                //Primary pointer is swiping, so transit to PanningScalingState
-                                transitToPanningScalingStateAndClear();
-                            }
-                        } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
-                                && isActivated()
-                                && event.getPointerCount() == 1) {
-                            if (mOverscrollHandler != null
-                                    && overscrollState(event, mFirstPointerDownLocation)
-                                    == OVERSCROLL_VERTICAL_EDGE) {
-                                transitionToDelegatingStateAndClear();
-                            } else if (overscrollState(event, mFirstPointerDownLocation)
-                                    != OVERSCROLL_NONE) {
-                                transitionToDelegatingStateAndClear();
-                            } else {
-                                transitToSinglePanningStateAndClear();
-                            }
-                        } else if (!mIsTwoFingerCountReached) {
-                            // If it is a two-finger gesture, do not transition to the
-                            // delegating state to ensure the reachability of
-                            // the two-finger triple tap (triggerable with ACTION_UP)
-                            transitionToDelegatingStateAndClear();
-                        }
-                    } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
-                            && distanceClosestPointerToPoint(
-                            mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
-                        // Second pointer is swiping, so transit to PanningScalingState
-                        // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
-                        // multi finger multi tap
-                        storePointerDownLocation(mSecondPointerDownLocation, event);
-                        mHandler.sendEmptyMessageDelayed(
-                                MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
-                                ViewConfiguration.getTapTimeout());
-                    }
-                }
-                break;
-                case ACTION_UP: {
-
-                    mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
-                    mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
-
-                    if (!mFullScreenMagnificationController.magnificationRegionContains(
-                            mDisplayId, event.getX(), event.getY())) {
-                        transitionToDelegatingStateAndClear();
-
-                    } else if (isMultiFingerMultiTapTriggered(TWO_FINGER_GESTURE_MAX_TAPS, event)) {
-                        // Placing multiple fingers before a single finger, because achieving a
-                        // multi finger multi tap also means achieving a single finger triple tap
-                        onTripleTap(event);
-
-                    } else if (isMultiTapTriggered(3 /* taps */)) {
-                        onTripleTap(/* up */ event);
-
-                    } else if (
-                            // Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
-                            isFingerDown()
-                            //TODO long tap should never happen here
-                            && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
-                                    || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))
-                            // If it is a two-finger but not reach 3 tap, do not transition to the
-                            // delegating state to ensure the reachability of the triple tap
-                            && mCompletedTapCount == 0) {
-                        transitionToDelegatingStateAndClear();
-
-                    }
-                }
-                break;
-            }
-        }
-        // LINT.ThenChange(:detecting_state)
-
-        @Override
-        public void clear() {
-            mCompletedTapCount = 0;
-            setShortcutTriggered(false);
-            removePendingDelayedMessages();
-            clearDelayedMotionEvents();
-            mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
-            mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
-        }
-
-        private boolean isMultiFingerMultiTapTriggered(int targetTapCount, MotionEvent event) {
-            if (event.getActionMasked() == ACTION_UP && mIsTwoFingerCountReached) {
-                mCompletedTapCount++;
-                mIsTwoFingerCountReached = false;
-            }
-
-            if (mDetectTwoFingerTripleTap && mCompletedTapCount > TWO_FINGER_GESTURE_MAX_TAPS - 1) {
-                final boolean enabled = !isActivated();
-                mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
-            }
-            return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
-        }
-
-        void transitionToDelegatingStateAndClear() {
-            mCompletedTapCount = 0;
-            transitionTo(mDelegatingState);
-            sendDelayedMotionEvents();
-            removePendingDelayedMessages();
-            mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
-            mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
-        }
-
-        void transitionToViewportDraggingStateAndClear(MotionEvent down) {
-
-            if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()");
-            final boolean shortcutTriggered = mShortcutTriggered;
-
-            // Only log the 3tap and hold event
-            if (!shortcutTriggered) {
-                final boolean enabled = !isActivated();
-                if (mCompletedTapCount == TWO_FINGER_GESTURE_MAX_TAPS - 1) {
-                    // Two finger triple tap and hold
-                    mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
-                } else {
-                    // Triple tap and hold also belongs to triple tap event
-                    mMagnificationLogger.logMagnificationTripleTap(enabled);
-                }
-            }
-            clear();
-
-            mViewportDraggingState.prepareForZoomInTemporary(shortcutTriggered);
-            zoomInTemporary(down.getX(), down.getY(), shortcutTriggered);
-            transitionTo(mViewportDraggingState);
-        }
-    }
-
     /**
      * This class handles motion events when the event dispatch has not yet
      * determined what the user is doing. It watches for various tap events.
      */
     class DetectingState implements State, Handler.Callback {
 
-        protected static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
-        protected static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
-        protected static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
+        private static final int MESSAGE_ON_TRIPLE_TAP_AND_HOLD = 1;
+        private static final int MESSAGE_TRANSITION_TO_DELEGATING_STATE = 2;
+        private static final int MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE = 3;
 
         final int mLongTapMinDelay;
         final int mSwipeMinDistance;
         final int mMultiTapMaxDelay;
         final int mMultiTapMaxDistance;
+        @Nullable final TwoFingerDoubleTapHandler mTwoFingerDoubleTapHandler;
 
-        protected MotionEventInfo mDelayedEventQueue;
-        protected MotionEvent mLastDown;
-        protected MotionEvent mPreLastDown;
-        protected MotionEvent mLastUp;
-        protected MotionEvent mPreLastUp;
+        private MotionEventInfo mDelayedEventQueue;
+        private MotionEvent mLastDown;
+        private MotionEvent mPreLastDown;
+        private MotionEvent mLastUp;
+        private MotionEvent mPreLastUp;
 
-        protected PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
-        protected PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
-        protected long mLastDetectingDownEventTime;
+        private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+        private PointF mSecondPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+        private long mLastDetectingDownEventTime;
 
         @VisibleForTesting boolean mShortcutTriggered;
 
@@ -1191,6 +901,9 @@
                     MagnificationGestureMatcher.getMagnificationMultiTapTimeout(context);
             mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop();
             mMultiTapMaxDistance = ViewConfiguration.get(context).getScaledDoubleTapSlop();
+            mTwoFingerDoubleTapHandler =
+                    Flags.enableMagnificationMultipleFingerMultipleTapGesture()
+                            ? new TwoFingerDoubleTapHandler() : null;
         }
 
         @Override
@@ -1218,7 +931,6 @@
             return true;
         }
 
-        // LINT.IfChange(detecting_state)
         @Override
         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
             cacheDelayedMotionEvent(event, rawEvent, policyFlags);
@@ -1244,6 +956,7 @@
                         transitionToDelegatingStateAndClear();
 
                     } else if (mDetectSingleFingerTripleTap
+                            || (mTwoFingerDoubleTapHandler != null && mDetectTwoFingerTripleTap)
                             // If activated, delay an ACTION_DOWN for mMultiTapMaxDelay
                             // to ensure reachability of
                             // STATE_PANNING_SCALING(triggerable with ACTION_POINTER_DOWN)
@@ -1259,6 +972,12 @@
                 }
                 break;
                 case ACTION_POINTER_DOWN: {
+                    if (mTwoFingerDoubleTapHandler != null) {
+                        mTwoFingerDoubleTapHandler.onPointerDown(event);
+                        break;
+                    }
+
+                    // LINT.IfChange(action_pointer_down)
                     if (isActivated() && event.getPointerCount() == 2) {
                         storePointerDownLocation(mSecondPointerDownLocation, event);
                         mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
@@ -1266,13 +985,26 @@
                     } else {
                         transitionToDelegatingStateAndClear();
                     }
+                    // LINT.ThenChange(:action_pointer_down_with_multi_finger)
                 }
                 break;
                 case ACTION_POINTER_UP: {
+                    if (mTwoFingerDoubleTapHandler != null) {
+                        mTwoFingerDoubleTapHandler.onPointerUp();
+                        break;
+                    }
+                    // LINT.IfChange(action_pointer_up)
                     transitionToDelegatingStateAndClear();
+                    // LINT.ThenChange(:action_pointer_up_with_multi_finger)
                 }
                 break;
                 case ACTION_MOVE: {
+                    if (mTwoFingerDoubleTapHandler != null) {
+                        mTwoFingerDoubleTapHandler.onMove(event);
+                        break;
+                    }
+
+                    // LINT.IfChange(action_move)
                     if (isFingerDown()
                             && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
                         // Swipe detected - transition immediately
@@ -1313,12 +1045,20 @@
                         //Second pointer is swiping, so transit to PanningScalingState
                         transitToPanningScalingStateAndClear();
                     }
+                    // LINT.ThenChange(:action_move_with_multi_finger)
                 }
                 break;
                 case ACTION_UP: {
 
                     mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
 
+                    if (mTwoFingerDoubleTapHandler != null) {
+                        mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
+                        mTwoFingerDoubleTapHandler.onUp(event);
+                        break;
+                    }
+
+                    // LINT.IfChange(action_up)
                     if (!mFullScreenMagnificationController.magnificationRegionContains(
                             mDisplayId, event.getX(), event.getY())) {
                         transitionToDelegatingStateAndClear();
@@ -1335,11 +1075,11 @@
                         transitionToDelegatingStateAndClear();
 
                     }
+                    // LINT.ThenChange(:action_up_with_multi_finger)
                 }
                 break;
             }
         }
-        // LINT.ThenChange(:detecting_state_with_multi_finger)
 
         protected void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
             final int index = event.getActionIndex();
@@ -1425,6 +1165,9 @@
 
         @Override
         public void clear() {
+            if (mTwoFingerDoubleTapHandler != null) {
+                mTwoFingerDoubleTapHandler.mCompletedTapCount = 0;
+            }
             setShortcutTriggered(false);
             removePendingDelayedMessages();
             clearDelayedMotionEvents();
@@ -1501,9 +1244,13 @@
         }
 
         void transitionToDelegatingStateAndClear() {
+            if (mTwoFingerDoubleTapHandler != null) {
+                mTwoFingerDoubleTapHandler.mCompletedTapCount = 0;
+            }
             transitionTo(mDelegatingState);
             sendDelayedMotionEvents();
             removePendingDelayedMessages();
+            mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
             mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
         }
 
@@ -1543,9 +1290,15 @@
 
             // Only log the 3tap and hold event
             if (!shortcutTriggered) {
-                // Triple tap and hold also belongs to triple tap event
                 final boolean enabled = !isActivated();
-                mMagnificationLogger.logMagnificationTripleTap(enabled);
+                if (mTwoFingerDoubleTapHandler != null
+                        && mTwoFingerDoubleTapHandler.shouldLogTwoFingerDoubleTap()) {
+                    // Two finger double tap and hold
+                    mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+                } else {
+                    // Triple tap and hold also belongs to triple tap event
+                    mMagnificationLogger.logMagnificationTripleTap(enabled);
+                }
             }
             clear();
 
@@ -1604,6 +1357,173 @@
             }
             return false;
         }
+
+        final class TwoFingerDoubleTapHandler {
+            private static final int TWO_FINGER_GESTURE_MAX_TAPS = 2;
+            // A tap counts when two fingers are down and up once.
+            private int mCompletedTapCount;
+            // A flag set to true when two fingers have touched down.
+            // Used to indicate what next finger action should be.
+            private boolean mIsTwoFingerCountReached;
+
+            TwoFingerDoubleTapHandler() {
+                mCompletedTapCount = 0;
+                mIsTwoFingerCountReached = false;
+            }
+
+            private void onPointerDown(MotionEvent event) {
+                mIsTwoFingerCountReached = mDetectTwoFingerTripleTap
+                        && event.getPointerCount() == 2;
+                mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+
+                // LINT.IfChange(action_pointer_down_with_multi_finger)
+                if (event.getPointerCount() == 2) {
+                    if (isMultiFingerMultiTapTriggered(
+                            TWO_FINGER_GESTURE_MAX_TAPS - 1, event)) {
+                        // 3tap and hold
+                        afterLongTapTimeoutTransitionToDraggingState(event);
+                    } else {
+                        if (mDetectTwoFingerTripleTap) {
+                            // If mDetectTwoFingerTripleTap, delay transition to the delegating
+                            // state for mMultiTapMaxDelay to ensure reachability of
+                            // multi finger multi tap
+                            afterMultiTapTimeoutTransitionToDelegatingState();
+                        }
+
+                        if (isActivated()) {
+                            // If activated, delay transition to the panning scaling
+                            // state for tap timeout to ensure reachability of
+                            // multi finger multi tap
+                            storePointerDownLocation(mSecondPointerDownLocation, event);
+                            mHandler.sendEmptyMessageDelayed(
+                                    MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+                                    ViewConfiguration.getTapTimeout());
+                        }
+                    }
+                } else {
+                    transitionToDelegatingStateAndClear();
+                }
+                // LINT.ThenChange(:action_pointer_down)
+            }
+
+            private void onMove(MotionEvent event) {
+                // LINT.IfChange(action_move_with_multi_finger)
+                if (isFingerDown()
+                        && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
+                    // Swipe detected - transition immediately
+
+                    // For convenience, viewport dragging takes precedence
+                    // over insta-delegating on 3tap&swipe
+                    // (which is a rare combo to be used aside from magnification)
+                    if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
+                        transitionToViewportDraggingStateAndClear(event);
+                    } else if (isMultiFingerMultiTapTriggered(
+                            TWO_FINGER_GESTURE_MAX_TAPS - 1, event)
+                            && event.getPointerCount() == 2) {
+                        transitionToViewportDraggingStateAndClear(event);
+                    } else if (isActivated() && event.getPointerCount() == 2) {
+                        if (mOverscrollHandler != null
+                                && overscrollState(event, mFirstPointerDownLocation)
+                                == OVERSCROLL_VERTICAL_EDGE) {
+                            transitionToDelegatingStateAndClear();
+                        } else {
+                            //Primary pointer is swiping, so transit to PanningScalingState
+                            transitToPanningScalingStateAndClear();
+                        }
+                    } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
+                            && isActivated()
+                            && event.getPointerCount() == 1) {
+                        if (mOverscrollHandler != null
+                                && overscrollState(event, mFirstPointerDownLocation)
+                                == OVERSCROLL_VERTICAL_EDGE) {
+                            transitionToDelegatingStateAndClear();
+                        } else if (overscrollState(event, mFirstPointerDownLocation)
+                                != OVERSCROLL_NONE) {
+                            transitionToDelegatingStateAndClear();
+                        } else {
+                            transitToSinglePanningStateAndClear();
+                        }
+                    } else if (!mIsTwoFingerCountReached) {
+                        // If it is a two-finger gesture, do not transition to the
+                        // delegating state to ensure the reachability of
+                        // the two-finger triple tap (triggerable with ACTION_UP)
+                        transitionToDelegatingStateAndClear();
+                    }
+                } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
+                        && distanceClosestPointerToPoint(
+                        mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
+                    // Second pointer is swiping, so transit to PanningScalingState
+                    // Delay an ACTION_MOVE for tap timeout to ensure it is not trigger from
+                    // multi finger multi tap
+                    storePointerDownLocation(mSecondPointerDownLocation, event);
+                    mHandler.sendEmptyMessageDelayed(
+                            MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+                            ViewConfiguration.getTapTimeout());
+                }
+                // LINT.ThenChange(:action_move)
+            }
+
+            private void onPointerUp() {
+                // If it is a two-finger gesture, do not transition to the delegating state
+                // to ensure the reachability of
+                // the two-finger triple tap (triggerable with ACTION_MOVE and ACTION_UP)
+                // LINT.IfChange(action_pointer_up_with_multi_finger)
+                if (!mIsTwoFingerCountReached) {
+                    transitionToDelegatingStateAndClear();
+                }
+                // LINT.ThenChange(:action_pointer_up)
+            }
+
+            private void onUp(MotionEvent event) {
+                // LINT.IfChange(action_up_with_multi_finger)
+                if (!mFullScreenMagnificationController.magnificationRegionContains(
+                        mDisplayId, event.getX(), event.getY())) {
+                    transitionToDelegatingStateAndClear();
+
+                } else if (isMultiFingerMultiTapTriggered(
+                        TWO_FINGER_GESTURE_MAX_TAPS, event)) {
+                    // Placing multiple fingers before a single finger, because achieving a
+                    // multi finger multi tap also means achieving a single finger
+                    // triple tap
+                    onTripleTap(event);
+
+                } else if (isMultiTapTriggered(3 /* taps */)) {
+                    onTripleTap(/* up */ event);
+
+                } else if (
+                    // Possible to be false on: 3tap&drag -> scale -> PTR_UP -> UP
+                        isFingerDown()
+                                //TODO long tap should never happen here
+                                && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
+                                || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))
+                                // If it is a two-finger but not reach 3 tap, do not
+                                // transition to the delegating state to ensure the
+                                // reachability of the triple tap
+                                && mCompletedTapCount == 0) {
+                    transitionToDelegatingStateAndClear();
+                }
+                // LINT.ThenChange(:action_up)
+            }
+
+            private boolean isMultiFingerMultiTapTriggered(int targetTapCount, MotionEvent event) {
+                if (event.getActionMasked() == ACTION_UP && mIsTwoFingerCountReached) {
+                    mCompletedTapCount++;
+                    mIsTwoFingerCountReached = false;
+                }
+
+                if (mDetectTwoFingerTripleTap
+                        && mCompletedTapCount > TWO_FINGER_GESTURE_MAX_TAPS - 1) {
+                    final boolean enabled = !isActivated();
+                    mMagnificationLogger.logMagnificationTwoFingerTripleTap(enabled);
+                }
+                return mDetectTwoFingerTripleTap && mCompletedTapCount == targetTapCount;
+            }
+
+            private boolean shouldLogTwoFingerDoubleTap() {
+                return mCompletedTapCount
+                        == TwoFingerDoubleTapHandler.TWO_FINGER_GESTURE_MAX_TAPS - 1;
+            }
+        }
     }
 
     private void zoomInTemporary(float centerX, float centerY, boolean shortcutTriggered) {
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 08411c2..446123f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -127,49 +127,41 @@
         if (DEBUG_EVENT_STREAM) {
             storeEventInto(mDebugInputEventHistory, event);
         }
-        if (shouldDispatchTransformedEvent(event)) {
-            dispatchTransformedEvent(event, rawEvent, policyFlags);
-        } else {
-            onMotionEventInternal(event, rawEvent, policyFlags);
+        switch (event.getSource()) {
+            case SOURCE_TOUCHSCREEN: {
+                if (magnificationShortcutExists()) {
+                    // Observe touchscreen events while magnification activation is detected.
+                    onMotionEventInternal(event, rawEvent, policyFlags);
 
-            final int action = event.getAction();
-            if (action == MotionEvent.ACTION_DOWN) {
-                mCallback.onTouchInteractionStart(mDisplayId, getMode());
-            } else if (action == ACTION_UP || action == ACTION_CANCEL) {
-                mCallback.onTouchInteractionEnd(mDisplayId, getMode());
+                    final int action = event.getAction();
+                    if (action == MotionEvent.ACTION_DOWN) {
+                        mCallback.onTouchInteractionStart(mDisplayId, getMode());
+                    } else if (action == ACTION_UP || action == ACTION_CANCEL) {
+                        mCallback.onTouchInteractionEnd(mDisplayId, getMode());
+                    }
+                    // Return early: Do not dispatch event through normal eventing
+                    // flow, it has been fully consumed by the magnifier.
+                    return;
+                }
+            } break;
+            case SOURCE_MOUSE:
+            case SOURCE_STYLUS: {
+                if (magnificationShortcutExists() && Flags.enableMagnificationFollowsMouse()) {
+                    handleMouseOrStylusEvent(event, rawEvent, policyFlags);
+                }
             }
+                break;
+            default:
+                break;
         }
+        // Dispatch event through normal eventing flow.
+        dispatchTransformedEvent(event, rawEvent, policyFlags);
     }
 
-    /**
-     * Some touchscreen, mouse and stylus events may modify magnifier state. Checks for whether the
-     * event should not be dispatched to the magnifier.
-     *
-     * @param event The event to check.
-     * @return `true` if the event should be sent through the normal event flow or `false` if it
-     *     should be observed by magnifier.
-     */
-    private boolean shouldDispatchTransformedEvent(MotionEvent event) {
-        if (event.getSource() == SOURCE_TOUCHSCREEN) {
-            if (mDetectSingleFingerTripleTap
-                    || mDetectTwoFingerTripleTap
-                    || mDetectShortcutTrigger) {
-                // Observe touchscreen events while magnification activation is detected.
-                return false;
-            }
-        }
-        if (Flags.enableMagnificationFollowsMouse()) {
-            if (event.isFromSource(SOURCE_MOUSE) || event.isFromSource(SOURCE_STYLUS)) {
-                // Note that mouse events include other mouse-like pointing devices
-                // such as touchpads and pointing sticks.
-                // Observe any mouse or stylus movement.
-                // We observe all movement to ensure that events continue to come in order,
-                // even though only some movement types actually move the viewport.
-                return false;
-            }
-        }
-        // Magnification dispatches (ignores) all other events
-        return true;
+    private boolean magnificationShortcutExists() {
+        return (mDetectSingleFingerTripleTap
+                || mDetectTwoFingerTripleTap
+                || mDetectShortcutTrigger);
     }
 
     final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
@@ -202,6 +194,13 @@
     abstract void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags);
 
     /**
+     * Called when this MagnificationGestureHandler should handle a mouse or stylus motion event,
+     * but not re-dispatch it when completed.
+     */
+    abstract void handleMouseOrStylusEvent(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags);
+
+    /**
      * Called when the shortcut target is magnification.
      */
     public void notifyShortcutTriggered() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
index 845249e..ab94e98 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MouseEventHandler.java
@@ -39,8 +39,14 @@
      * @param displayId The display that is being magnified
      */
     public void onEvent(MotionEvent event, int displayId) {
-        if (event.getAction() == ACTION_HOVER_MOVE
-                || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE)) {
+        // Ignore gesture events synthesized from the touchpad.
+        // TODO(b/354696546): Use synthesized pinch gestures to control scale.
+        boolean isSynthesizedFromTouchpad =
+                event.getClassification() != MotionEvent.CLASSIFICATION_NONE;
+
+        // Consume only move events from the mouse or hovers from any tool.
+        if (!isSynthesizedFromTouchpad && (event.getAction() == ACTION_HOVER_MOVE
+                || (event.getAction() == ACTION_MOVE && event.getSource() == SOURCE_MOUSE))) {
             final float eventX = event.getX();
             final float eventY = event.getY();
 
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 1818cddb..a841404 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -147,9 +147,13 @@
     }
 
     @Override
+    void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        // Window Magnification viewport doesn't move with mouse events (yet).
+    }
+
+    @Override
     void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         if (event.getSource() != SOURCE_TOUCHSCREEN) {
-            // Window Magnification viewport doesn't move with mouse events (yet).
             return;
         }
         // To keep InputEventConsistencyVerifiers within GestureDetectors happy.
diff --git a/services/appfunctions/Android.bp b/services/appfunctions/Android.bp
new file mode 100644
index 0000000..f8ee823
--- /dev/null
+++ b/services/appfunctions/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "services.appfunctions-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.appfunctions",
+    defaults: ["platform_service_defaults"],
+    srcs: [
+        ":services.appfunctions-sources",
+        "java/**/*.logtags",
+    ],
+    libs: ["services.core"],
+}
diff --git a/services/appfunctions/OWNERS b/services/appfunctions/OWNERS
new file mode 100644
index 0000000..b310894
--- /dev/null
+++ b/services/appfunctions/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/appfunctions/OWNERS
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
new file mode 100644
index 0000000..02800cb
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -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.server.appfunctions;
+
+import android.app.appfunctions.AppFunctionManagerConfiguration;
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/** Service that manages app functions. */
+public class AppFunctionManagerService extends SystemService {
+    private final AppFunctionManagerServiceImpl mServiceImpl;
+
+    public AppFunctionManagerService(Context context) {
+        super(context);
+        mServiceImpl = new AppFunctionManagerServiceImpl(context);
+    }
+
+    @Override
+    public void onStart() {
+        if (AppFunctionManagerConfiguration.isSupported(getContext())) {
+            publishBinderService(Context.APP_FUNCTION_SERVICE, mServiceImpl);
+        }
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
new file mode 100644
index 0000000..f04bd9f
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -0,0 +1,235 @@
+/*
+ * 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.appfunctions.ExecuteAppFunctionAidlRequest;
+import android.app.appfunctions.ExecuteAppFunctionResponse;
+import android.app.appfunctions.IAppFunctionManager;
+import android.app.appfunctions.IAppFunctionService;
+import android.app.appfunctions.IExecuteAppFunctionCallback;
+import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.appfunctions.RemoteServiceCaller.RunServiceCallCallback;
+import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
+
+import java.util.Objects;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/** Implementation of the AppFunctionManagerService. */
+public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub {
+    private static final String TAG = AppFunctionManagerServiceImpl.class.getSimpleName();
+
+    private final RemoteServiceCaller<IAppFunctionService> mRemoteServiceCaller;
+    private final CallerValidator mCallerValidator;
+    private final ServiceHelper mInternalServiceHelper;
+    private final ServiceConfig mServiceConfig;
+
+    public AppFunctionManagerServiceImpl(@NonNull Context context) {
+        this(
+                new RemoteServiceCallerImpl<>(
+                        context,
+                        IAppFunctionService.Stub::asInterface,
+                        new ThreadPoolExecutor(
+                                /* corePoolSize= */ Runtime.getRuntime().availableProcessors(),
+                                /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(),
+                                /* keepAliveTime= */ 0L,
+                                /* unit= */ TimeUnit.SECONDS,
+                                /* workQueue= */ new LinkedBlockingQueue<>())),
+                new CallerValidatorImpl(context),
+                new ServiceHelperImpl(context),
+                new ServiceConfigImpl());
+    }
+
+    @VisibleForTesting
+    AppFunctionManagerServiceImpl(
+            RemoteServiceCaller<IAppFunctionService> remoteServiceCaller,
+            CallerValidator callerValidator,
+            ServiceHelper appFunctionInternalServiceHelper,
+            ServiceConfig serviceConfig) {
+        mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
+        mCallerValidator = Objects.requireNonNull(callerValidator);
+        mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
+        mServiceConfig = serviceConfig;
+    }
+
+    @Override
+    public void executeAppFunction(
+            @NonNull ExecuteAppFunctionAidlRequest requestInternal,
+            @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) {
+        Objects.requireNonNull(requestInternal);
+        Objects.requireNonNull(executeAppFunctionCallback);
+
+        final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback =
+                new SafeOneTimeExecuteAppFunctionCallback(executeAppFunctionCallback);
+
+        String validatedCallingPackage;
+        UserHandle targetUser;
+        try {
+            validatedCallingPackage =
+                    mCallerValidator.validateCallingPackage(requestInternal.getCallingPackage());
+            targetUser =
+                    mCallerValidator.verifyTargetUserHandle(
+                            requestInternal.getUserHandle(), validatedCallingPackage);
+        } catch (SecurityException exception) {
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_DENIED,
+                            exception.getMessage(),
+                            /* extras= */ null));
+            return;
+        }
+
+        // TODO(b/354956319): Add and honor the new enterprise policies.
+        if (mCallerValidator.isUserOrganizationManaged(targetUser)) {
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                            "Cannot run on a device with a device owner or from the managed"
+                                    + " profile.",
+                            /* extras= */ null));
+            return;
+        }
+
+        String targetPackageName = requestInternal.getClientRequest().getTargetPackageName();
+        if (TextUtils.isEmpty(targetPackageName)) {
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT,
+                            "Target package name cannot be empty.",
+                            /* extras= */ null));
+            return;
+        }
+
+        if (!mCallerValidator.verifyCallerCanExecuteAppFunction(
+                validatedCallingPackage, targetPackageName)) {
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_DENIED,
+                            "Caller does not have permission to execute the appfunction",
+                            /* extras= */ null));
+            return;
+        }
+
+        Intent serviceIntent =
+                mInternalServiceHelper.resolveAppFunctionService(targetPackageName, targetUser);
+        if (serviceIntent == null) {
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_INTERNAL_ERROR,
+                            "Cannot find the target service.",
+                            /* extras= */ null));
+            return;
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            bindAppFunctionServiceUnchecked(
+                    requestInternal,
+                    serviceIntent,
+                    targetUser,
+                    safeExecuteAppFunctionCallback,
+                    /* bindFlags= */ Context.BIND_AUTO_CREATE,
+                    /* timeoutInMillis= */ mServiceConfig.getExecuteAppFunctionTimeoutMillis());
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void bindAppFunctionServiceUnchecked(
+            @NonNull ExecuteAppFunctionAidlRequest requestInternal,
+            @NonNull Intent serviceIntent,
+            @NonNull UserHandle targetUser,
+            @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
+            int bindFlags,
+            long timeoutInMillis) {
+        boolean bindServiceResult =
+                mRemoteServiceCaller.runServiceCall(
+                        serviceIntent,
+                        bindFlags,
+                        timeoutInMillis,
+                        targetUser,
+                        new RunServiceCallCallback<IAppFunctionService>() {
+                            @Override
+                            public void onServiceConnected(
+                                    @NonNull IAppFunctionService service,
+                                    @NonNull
+                                            ServiceUsageCompleteListener
+                                                    serviceUsageCompleteListener) {
+                                try {
+                                    service.executeAppFunction(
+                                            requestInternal.getClientRequest(),
+                                            new IExecuteAppFunctionCallback.Stub() {
+                                                @Override
+                                                public void onResult(
+                                                        ExecuteAppFunctionResponse response) {
+                                                    safeExecuteAppFunctionCallback.onResult(
+                                                            response);
+                                                    serviceUsageCompleteListener.onCompleted();
+                                                }
+                                            });
+                                } catch (Exception e) {
+                                    safeExecuteAppFunctionCallback.onResult(
+                                            ExecuteAppFunctionResponse.newFailure(
+                                                    ExecuteAppFunctionResponse
+                                                            .RESULT_APP_UNKNOWN_ERROR,
+                                                    e.getMessage(),
+                                                    /* extras= */ null));
+                                    serviceUsageCompleteListener.onCompleted();
+                                }
+                            }
+
+                            @Override
+                            public void onFailedToConnect() {
+                                Slog.e(TAG, "Failed to connect to service");
+                                safeExecuteAppFunctionCallback.onResult(
+                                        ExecuteAppFunctionResponse.newFailure(
+                                                ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR,
+                                                "Failed to connect to AppFunctionService",
+                                                /* extras= */ null));
+                            }
+
+                            @Override
+                            public void onTimedOut() {
+                                Slog.e(TAG, "Timed out");
+                                safeExecuteAppFunctionCallback.onResult(
+                                        ExecuteAppFunctionResponse.newFailure(
+                                                ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+                                                "Binding to AppFunctionService timed out.",
+                                                /* extras= */ null));
+                            }
+                        });
+
+        if (!bindServiceResult) {
+            Slog.e(TAG, "Failed to bind to the AppFunctionService");
+            safeExecuteAppFunctionCallback.onResult(
+                    ExecuteAppFunctionResponse.newFailure(
+                            ExecuteAppFunctionResponse.RESULT_TIMED_OUT,
+                            "Failed to bind the AppFunctionService.",
+                            /* extras= */ null));
+        }
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
new file mode 100644
index 0000000..ca43dfa
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidator.java
@@ -0,0 +1,80 @@
+/*
+ * 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.Manifest;
+import android.annotation.NonNull;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Interface for validating that the caller has the correct privilege to call an AppFunctionManager
+ * API.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public interface CallerValidator {
+    // TODO(b/357551503): Should we verify NOT instant app?
+    // TODO(b/357551503): Verify that user have been unlocked.
+
+    /**
+     * This method is used to validate that the calling package reported in the request is the same
+     * as the binder calling identity.
+     *
+     * @param claimedCallingPackage The package name of the caller.
+     * @return The package name of the caller.
+     * @throws SecurityException if the package name and uid don't match.
+     */
+    String validateCallingPackage(@NonNull String claimedCallingPackage);
+
+    /**
+     * Validates that the caller can invoke an AppFunctionManager API in the provided target user
+     * space.
+     *
+     * @param targetUserHandle The user which the caller is requesting to execute as.
+     * @param claimedCallingPackage The package name of the caller.
+     * @return The user handle that the call should run as. Will always be a concrete user.
+     * @throws IllegalArgumentException if the target user is a special user.
+     * @throws SecurityException if caller trying to interact across users without {@link
+     *     Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+     */
+    UserHandle verifyTargetUserHandle(
+            @NonNull UserHandle targetUserHandle, @NonNull String claimedCallingPackage);
+
+    /**
+     * Validates that the caller can execute the specified app function.
+     *
+     * <p>The caller can execute if the app function's package name is the same as the caller's
+     * package or the caller has either {@link Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or
+     * {@link Manifest.permission.EXECUTE_APP_FUNCTIONS} granted. In some cases, app functions can
+     * still opt-out of caller having {@link Manifest.permission.EXECUTE_APP_FUNCTIONS}.
+     *
+     * @param callerPackageName The calling package (as previously validated).
+     * @param targetPackageName The package that owns the app function to execute.
+     * @return Whether the caller can execute the specified app function.
+     */
+    boolean verifyCallerCanExecuteAppFunction(
+            @NonNull String callerPackageName, @NonNull String targetPackageName);
+
+    /**
+     * Checks if the user is organization managed.
+     *
+     * @param targetUser The user which the caller is requesting to execute as.
+     * @return Whether the user is organization managed.
+     */
+    boolean isUserOrganizationManaged(@NonNull UserHandle targetUser);
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
new file mode 100644
index 0000000..35905ed
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/CallerValidatorImpl.java
@@ -0,0 +1,198 @@
+/*
+ * 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.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.Objects;
+
+/* Validates that caller has the correct privilege to call an AppFunctionManager Api. */
+class CallerValidatorImpl implements CallerValidator {
+    private final Context mContext;
+
+    CallerValidatorImpl(@NonNull Context context) {
+        mContext = Objects.requireNonNull(context);
+    }
+
+    @Override
+    @NonNull
+    @BinderThread
+    public String validateCallingPackage(@NonNull String claimedCallingPackage) {
+        int callingUid = Binder.getCallingUid();
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            validateCallingPackageInternal(callingUid, claimedCallingPackage);
+            return claimedCallingPackage;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    @Override
+    @NonNull
+    @BinderThread
+    public UserHandle verifyTargetUserHandle(
+            @NonNull UserHandle targetUserHandle, @NonNull String claimedCallingPackage) {
+        int callingPid = Binder.getCallingPid();
+        int callingUid = Binder.getCallingUid();
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            return handleIncomingUser(
+                    claimedCallingPackage, targetUserHandle, callingPid, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    @Override
+    @BinderThread
+    @RequiresPermission(
+            anyOf = {
+                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED,
+                Manifest.permission.EXECUTE_APP_FUNCTIONS
+            },
+            conditional = true)
+    // TODO(b/360864791): Add and honor apps that opt-out from EXECUTE_APP_FUNCTIONS caller.
+    public boolean verifyCallerCanExecuteAppFunction(
+            @NonNull String callerPackageName, @NonNull String targetPackageName) {
+        int pid = Binder.getCallingPid();
+        int uid = Binder.getCallingUid();
+        boolean hasExecutionPermission =
+                mContext.checkPermission(
+                                Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+        boolean hasTrustedExecutionPermission =
+                mContext.checkPermission(Manifest.permission.EXECUTE_APP_FUNCTIONS, pid, uid)
+                        == PackageManager.PERMISSION_GRANTED;
+        boolean isSamePackage = callerPackageName.equals(targetPackageName);
+        return hasExecutionPermission || hasTrustedExecutionPermission || isSamePackage;
+    }
+
+    @Override
+    @BinderThread
+    public boolean isUserOrganizationManaged(@NonNull UserHandle targetUser) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            if (Objects.requireNonNull(mContext.getSystemService(DevicePolicyManager.class))
+                    .isDeviceManaged()) {
+                return true;
+            }
+            return Objects.requireNonNull(mContext.getSystemService(UserManager.class))
+                    .isManagedProfile(targetUser.getIdentifier());
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Helper for dealing with incoming user arguments to system service calls.
+     *
+     * <p>Takes care of checking permissions and if the target is special user, this method will
+     * simply throw.
+     *
+     * @param callingPackageName The package name of the caller.
+     * @param targetUserHandle The user which the caller is requesting to execute as.
+     * @param callingPid The actual pid of the caller as determined by Binder.
+     * @param callingUid The actual uid of the caller as determined by Binder.
+     * @return the user handle that the call should run as. Will always be a concrete user.
+     * @throws IllegalArgumentException if the target user is a special user.
+     * @throws SecurityException if caller trying to interact across user without {@link
+     *     Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+     */
+    @NonNull
+    private UserHandle handleIncomingUser(
+            @NonNull String callingPackageName,
+            @NonNull UserHandle targetUserHandle,
+            int callingPid,
+            int callingUid) {
+        UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+        if (callingUserHandle.equals(targetUserHandle)) {
+            return targetUserHandle;
+        }
+
+        // Duplicates UserController#ensureNotSpecialUser
+        if (targetUserHandle.getIdentifier() < 0) {
+            throw new IllegalArgumentException(
+                    "Call does not support special user " + targetUserHandle);
+        }
+
+        if (mContext.checkPermission(
+                        Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingPid, callingUid)
+                == PackageManager.PERMISSION_GRANTED) {
+            try {
+                mContext.createPackageContextAsUser(
+                        callingPackageName, /* flags= */ 0, targetUserHandle);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new SecurityException(
+                        "Package: "
+                                + callingPackageName
+                                + " haven't installed for user "
+                                + targetUserHandle.getIdentifier());
+            }
+            return targetUserHandle;
+        }
+        throw new SecurityException(
+                "Permission denied while calling from uid "
+                        + callingUid
+                        + " with "
+                        + targetUserHandle
+                        + "; Requires permission: "
+                        + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    }
+
+    /**
+     * Checks that the caller's supposed package name matches the uid making the call.
+     *
+     * @throws SecurityException if the package name and uid don't match.
+     */
+    private void validateCallingPackageInternal(
+            int actualCallingUid, @NonNull String claimedCallingPackage) {
+        UserHandle callingUserHandle = UserHandle.getUserHandleForUid(actualCallingUid);
+        Context actualCallingUserContext =
+                mContext.createContextAsUser(callingUserHandle, /* flags= */ 0);
+        int claimedCallingUid = getPackageUid(actualCallingUserContext, claimedCallingPackage);
+        if (claimedCallingUid != actualCallingUid) {
+            throw new SecurityException(
+                    "Specified calling package ["
+                            + claimedCallingPackage
+                            + "] does not match the calling uid "
+                            + actualCallingUid);
+        }
+    }
+
+    /**
+     * Finds the UID of the {@code packageName} in the given {@code context}. Returns {@link
+     * Process#INVALID_UID} if unable to find the UID.
+     */
+    private int getPackageUid(@NonNull Context context, @NonNull String packageName) {
+        try {
+            return context.getPackageManager().getPackageUid(packageName, /* flags= */ 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return Process.INVALID_UID;
+        }
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
new file mode 100644
index 0000000..98903ae
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
@@ -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.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.os.UserHandle;
+
+/**
+ * Defines a contract for establishing temporary connections to services and executing operations
+ * within a specified timeout. Implementations of this interface provide mechanisms to ensure that
+ * services are properly unbound after the operation completes or a timeout occurs.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public interface RemoteServiceCaller<T> {
+
+    /**
+     * Initiates service binding and executes a provided method when the service connects. Unbinds
+     * the service after execution or upon timeout. Returns the result of the bindService API.
+     *
+     * <p>When the service connection was made successfully, it's the caller responsibility to
+     * report the usage is completed and can be unbound by calling {@link
+     * ServiceUsageCompleteListener#onCompleted()}.
+     *
+     * <p>This method includes a timeout mechanism to prevent the system from being stuck in a state
+     * where a service is bound indefinitely (for example, if the binder method never returns). This
+     * helps ensure that the calling app does not remain alive unnecessarily.
+     *
+     * @param intent An Intent object that describes the service that should be bound.
+     * @param bindFlags Flags used to control the binding process See {@link
+     *     android.content.Context#bindService}.
+     * @param timeoutInMillis The maximum time in milliseconds to wait for the service connection.
+     * @param userHandle The UserHandle of the user for which the service should be bound.
+     * @param callback A callback to be invoked for various events. See {@link
+     *     RunServiceCallCallback}.
+     */
+    boolean runServiceCall(
+            @NonNull Intent intent,
+            int bindFlags,
+            long timeoutInMillis,
+            @NonNull UserHandle userHandle,
+            @NonNull RunServiceCallCallback<T> callback);
+
+    /** An interface for clients to signal that they have finished using a bound service. */
+    interface ServiceUsageCompleteListener {
+        /**
+         * Called when a client has finished using a bound service. This indicates that the service
+         * can be safely unbound.
+         */
+        void onCompleted();
+    }
+
+    interface RunServiceCallCallback<T> {
+        /**
+         * Called when the service connection has been established. Uses {@code
+         * serviceUsageCompleteListener} to report finish using the connected service.
+         */
+        void onServiceConnected(
+                @NonNull T service,
+                @NonNull ServiceUsageCompleteListener serviceUsageCompleteListener);
+
+        /** Called when the service connection was failed to establish. */
+        void onFailedToConnect();
+
+        /**
+         * Called when the whole operation(i.e. binding and the service call) takes longer than
+         * allowed.
+         */
+        void onTimedOut();
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
new file mode 100644
index 0000000..0e18705
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
@@ -0,0 +1,157 @@
+/*
+ * 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.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+import java.util.function.Function;
+
+/**
+ * An implementation of {@link RemoteServiceCaller} that that is based on {@link
+ * Context#bindService}.
+ *
+ * @param <T> Class of wrapped service.
+ * @hide
+ */
+public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> {
+    private static final String TAG = "AppFunctionsServiceCall";
+
+    @NonNull private final Context mContext;
+    @NonNull private final Function<IBinder, T> mInterfaceConverter;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Executor mExecutor;
+
+    /**
+     * @param interfaceConverter A function responsible for converting an IBinder object into the
+     *     desired service interface.
+     * @param executor An Executor instance to dispatch callback.
+     * @param context The system context.
+     */
+    public RemoteServiceCallerImpl(
+            @NonNull Context context,
+            @NonNull Function<IBinder, T> interfaceConverter,
+            @NonNull Executor executor) {
+        mContext = context;
+        mInterfaceConverter = interfaceConverter;
+        mExecutor = executor;
+    }
+
+    @Override
+    public boolean runServiceCall(
+            @NonNull Intent intent,
+            int bindFlags,
+            long timeoutInMillis,
+            @NonNull UserHandle userHandle,
+            @NonNull RunServiceCallCallback<T> callback) {
+        OneOffServiceConnection serviceConnection =
+                new OneOffServiceConnection(
+                        intent, bindFlags, timeoutInMillis, userHandle, callback);
+
+        return serviceConnection.bindAndRun();
+    }
+
+    private class OneOffServiceConnection
+            implements ServiceConnection, ServiceUsageCompleteListener {
+        private final Intent mIntent;
+        private final int mFlags;
+        private final long mTimeoutMillis;
+        private final UserHandle mUserHandle;
+        private final RunServiceCallCallback<T> mCallback;
+        private final Runnable mTimeoutCallback;
+
+        OneOffServiceConnection(
+                @NonNull Intent intent,
+                int flags,
+                long timeoutMillis,
+                @NonNull UserHandle userHandle,
+                @NonNull RunServiceCallCallback<T> callback) {
+            mIntent = intent;
+            mFlags = flags;
+            mTimeoutMillis = timeoutMillis;
+            mCallback = callback;
+            mTimeoutCallback =
+                    () ->
+                            mExecutor.execute(
+                                    () -> {
+                                        safeUnbind();
+                                        mCallback.onTimedOut();
+                                    });
+            mUserHandle = userHandle;
+        }
+
+        public boolean bindAndRun() {
+            boolean bindServiceResult =
+                    mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle);
+
+            if (bindServiceResult) {
+                mHandler.postDelayed(mTimeoutCallback, mTimeoutMillis);
+            } else {
+                safeUnbind();
+            }
+
+            return bindServiceResult;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            T serviceInterface = mInterfaceConverter.apply(service);
+
+            mExecutor.execute(() -> mCallback.onServiceConnected(serviceInterface, this));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            safeUnbind();
+            mExecutor.execute(mCallback::onFailedToConnect);
+        }
+
+        private void safeUnbind() {
+            try {
+                mHandler.removeCallbacks(mTimeoutCallback);
+                mContext.unbindService(this);
+            } catch (Exception ex) {
+                Log.w(TAG, "Failed to unbind", ex);
+            }
+        }
+
+        @Override
+        public void onCompleted() {
+            safeUnbind();
+        }
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java
new file mode 100644
index 0000000..caa4bf0
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfig.java
@@ -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 com.android.server.appfunctions;
+
+/** This interface is used to expose configs to the AppFunctionManagerService. */
+public interface ServiceConfig {
+    // TODO(b/357551503): Obtain namespace from DeviceConfig.
+    String NAMESPACE_APP_FUNCTIONS = "appfunctions";
+
+    /** Returns the maximum time to wait for an app function execution to be complete. */
+    long getExecuteAppFunctionTimeoutMillis();
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java
new file mode 100644
index 0000000..f18789b
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceConfigImpl.java
@@ -0,0 +1,34 @@
+/*
+ * 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.provider.DeviceConfig;
+
+/** Implementation of {@link ServiceConfig} */
+public class ServiceConfigImpl implements ServiceConfig {
+    static final String DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT =
+            "execute_app_function_timeout_millis";
+    static final long DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS = 5000L;
+
+    @Override
+    public long getExecuteAppFunctionTimeoutMillis() {
+        return DeviceConfig.getLong(
+                NAMESPACE_APP_FUNCTIONS,
+                DEVICE_CONFIG_PROPERTY_EXECUTION_TIMEOUT,
+                DEFAULT_EXECUTE_APP_FUNCTION_TIMEOUT_MS);
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java
new file mode 100644
index 0000000..bc7bd2b
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelper.java
@@ -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.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Helper interface for AppFunctionService. */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public interface ServiceHelper {
+    /**
+     * Resolves the AppFunctionService for the target package.
+     *
+     * @param targetPackageName The package name of the target.
+     * @param targetUser The user which the caller is requesting to execute as.
+     * @return The intent to bind to the target service.
+     */
+    Intent resolveAppFunctionService(
+            @NonNull String targetPackageName, @NonNull UserHandle targetUser);
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
new file mode 100644
index 0000000..37a3779
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
@@ -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.server.appfunctions;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.appfunctions.AppFunctionService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
+
+import java.util.Objects;
+
+class ServiceHelperImpl implements ServiceHelper {
+    private final Context mContext;
+
+    // TODO(b/357551503): Keep track of unlocked users.
+
+    ServiceHelperImpl(@NonNull Context context) {
+        mContext = Objects.requireNonNull(context);
+    }
+
+    @Override
+    public Intent resolveAppFunctionService(
+            @NonNull String targetPackageName, @NonNull UserHandle targetUser) {
+        Intent serviceIntent = new Intent(AppFunctionService.SERVICE_INTERFACE);
+        serviceIntent.setPackage(targetPackageName);
+        ResolveInfo resolveInfo =
+                mContext.createContextAsUser(targetUser, /* flags= */ 0)
+                        .getPackageManager()
+                        .resolveService(serviceIntent, 0);
+        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
+            return null;
+        }
+
+        ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+        if (!Manifest.permission.BIND_APP_FUNCTION_SERVICE.equals(serviceInfo.permission)) {
+            return null;
+        }
+        serviceIntent.setComponent(new ComponentName(serviceInfo.packageName, serviceInfo.name));
+
+        return serviceIntent;
+    }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
new file mode 100644
index 0000000..e56f81f0
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
@@ -0,0 +1,145 @@
+/*
+ * 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.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchManager.SearchContext;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.GetSchemaResponse;
+import android.app.appsearch.SetSchemaRequest;
+import android.app.appsearch.SetSchemaResponse;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class for interacting with a system server local appsearch session asynchronously.
+ *
+ * <p>Converts the AppSearch Callback API to {@link AndroidFuture}.
+ */
+@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+public class SyncAppSearchCallHelper implements Closeable {
+    private static final String TAG = SyncAppSearchCallHelper.class.getSimpleName();
+    private final Executor mExecutor;
+    private final AppSearchManager mAppSearchManager;
+    private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
+
+    public SyncAppSearchCallHelper(
+            @NonNull AppSearchManager appSearchManager,
+            @NonNull Executor executor,
+            @NonNull SearchContext appSearchContext) {
+        Objects.requireNonNull(appSearchManager);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(appSearchContext);
+
+        mExecutor = executor;
+        mAppSearchManager = appSearchManager;
+        mSettableSessionFuture = new AndroidFuture<>();
+        mAppSearchManager.createSearchSession(
+                appSearchContext, mExecutor, mSettableSessionFuture::complete);
+    }
+
+    /** Converts a failed app search result codes into an exception. */
+    @NonNull
+    private 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());
+        };
+    }
+
+    private AndroidFuture<AppSearchSession> getSessionAsync() {
+        return mSettableSessionFuture.thenApply(
+                result -> {
+                    if (result.isSuccess()) {
+                        return result.getResultValue();
+                    } else {
+                        throw new RuntimeException(failedResultToException(result));
+                    }
+                });
+    }
+
+    /** Gets the schema for a given app search session. */
+    public AndroidFuture<GetSchemaResponse> getSchema() {
+        return getSessionAsync()
+                .thenComposeAsync(
+                        session -> {
+                            AndroidFuture<AppSearchResult<GetSchemaResponse>>
+                                    settableSchemaResponse = new AndroidFuture<>();
+                            session.getSchema(mExecutor, settableSchemaResponse::complete);
+                            return settableSchemaResponse.thenApply(
+                                    result -> {
+                                        if (result.isSuccess()) {
+                                            return result.getResultValue();
+                                        } else {
+                                            throw new RuntimeException(
+                                                    failedResultToException(result));
+                                        }
+                                    });
+                        },
+                        mExecutor);
+    }
+
+    /** Sets the schema for a given app search session. */
+    public AndroidFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest setSchemaRequest) {
+        return getSessionAsync()
+                .thenComposeAsync(
+                        session -> {
+                            AndroidFuture<AppSearchResult<SetSchemaResponse>>
+                                    settableSchemaResponse = new AndroidFuture<>();
+                            session.setSchema(
+                                    setSchemaRequest,
+                                    mExecutor,
+                                    mExecutor,
+                                    settableSchemaResponse::complete);
+                            return settableSchemaResponse.thenApply(
+                                    result -> {
+                                        if (result.isSuccess()) {
+                                            return result.getResultValue();
+                                        } else {
+                                            throw new RuntimeException(
+                                                    failedResultToException(result));
+                                        }
+                                    });
+                        },
+                        mExecutor);
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            getSessionAsync().get().close();
+        } catch (Exception ex) {
+            Slog.e(TAG, "Failed to close app search session", ex);
+        }
+    }
+}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 569615e..3068398 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.appwidget;
 
+import static android.appwidget.flags.Flags.remoteAdapterConversion;
 import static android.appwidget.flags.Flags.removeAppWidgetServiceIoFromCriticalPath;
 import static android.appwidget.flags.Flags.supportResumeRestoreAfterReboot;
 import static android.content.Context.KEYGUARD_SERVICE;
@@ -213,6 +214,8 @@
             Duration.ofHours(1).toMillis();
     // Default max API calls per reset interval for generated preview API rate limiting.
     private static final int DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL = 2;
+    // Default max number of providers for which to keep previews.
+    private static final int DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS = 50;
     // XML attribute for widget ids that are pending deletion.
     // See {@link Provider#pendingDeletedWidgetIds}.
     private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
@@ -358,10 +361,13 @@
                 SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
                 DEFAULT_GENERATED_PREVIEW_RESET_INTERVAL_MS);
         final int generatedPreviewMaxCallsPerInterval = DeviceConfig.getInt(NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_RESET_INTERVAL_MS,
+                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_CALLS_PER_INTERVAL,
                 DEFAULT_GENERATED_PREVIEW_MAX_CALLS_PER_INTERVAL);
+        final int generatedPreviewsMaxProviders = DeviceConfig.getInt(NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
+                DEFAULT_GENERATED_PREVIEW_MAX_PROVIDERS);
         mGeneratedPreviewsApiCounter = new ApiCounter(generatedPreviewResetInterval,
-                generatedPreviewMaxCallsPerInterval);
+                generatedPreviewMaxCallsPerInterval, generatedPreviewsMaxProviders);
         DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
                 new HandlerExecutor(mCallbackHandler), this::handleSystemUiDeviceConfigChange);
 
@@ -1645,6 +1651,9 @@
     public boolean bindRemoteViewsService(String callingPackage, int appWidgetId, Intent intent,
             IApplicationThread caller, IBinder activtiyToken, IServiceConnection connection,
             long flags) {
+        if (remoteAdapterConversion()) {
+            throw new UnsupportedOperationException("bindRemoteViewsService is deprecated");
+        }
         final int userId = UserHandle.getCallingUserId();
         if (DEBUG) {
             Slog.i(TAG, "bindRemoteViewsService() " + userId);
@@ -4660,6 +4669,13 @@
                         /* defaultValue= */ mGeneratedPreviewsApiCounter.getMaxCallsPerInterval());
                 mGeneratedPreviewsApiCounter.setMaxCallsPerInterval(maxCallsPerInterval);
             }
+            if (changed.contains(
+                    SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS)) {
+                int maxProviders = properties.getInt(
+                        SystemUiDeviceConfigFlags.GENERATED_PREVIEW_API_MAX_PROVIDERS,
+                        /* defaultValue= */ mGeneratedPreviewsApiCounter.getMaxProviders());
+                mGeneratedPreviewsApiCounter.setMaxProviders(maxProviders);
+            }
         }
     }
 
@@ -5444,17 +5460,22 @@
         private long mResetIntervalMs;
         // The max number of API calls per interval.
         private int mMaxCallsPerInterval;
+        // The max number of providers to keep call records for. Any call to tryApiCall for new
+        // providers will return false after this limit.
+        private int mMaxProviders;
+
         // Returns the current time (monotonic). By default this is SystemClock.elapsedRealtime.
         private LongSupplier mMonotonicClock;
 
-        ApiCounter(long resetIntervalMs, int maxCallsPerInterval) {
-            this(resetIntervalMs, maxCallsPerInterval, SystemClock::elapsedRealtime);
+        ApiCounter(long resetIntervalMs, int maxCallsPerInterval, int maxProviders) {
+            this(resetIntervalMs, maxCallsPerInterval, maxProviders, SystemClock::elapsedRealtime);
         }
 
-        ApiCounter(long resetIntervalMs, int maxCallsPerInterval,
+        ApiCounter(long resetIntervalMs, int maxCallsPerInterval, int maxProviders,
                 LongSupplier monotonicClock) {
             mResetIntervalMs = resetIntervalMs;
             mMaxCallsPerInterval = maxCallsPerInterval;
+            mMaxProviders = maxProviders;
             mMonotonicClock = monotonicClock;
         }
 
@@ -5474,12 +5495,27 @@
             return mMaxCallsPerInterval;
         }
 
+        public void setMaxProviders(int maxProviders) {
+            mMaxProviders = maxProviders;
+        }
+
+        public int getMaxProviders() {
+            return mMaxProviders;
+        }
+
         /**
          * Returns true if the API call for the provider should be allowed, false if it should be
          * rate-limited.
          */
         public boolean tryApiCall(@NonNull ProviderId provider) {
-            final ApiCallRecord record = getOrCreateRecord(provider);
+            if (!mCallCount.containsKey(provider)) {
+                if (mCallCount.size() >= mMaxProviders) {
+                    return false;
+                }
+                mCallCount.put(provider, new ApiCallRecord());
+            }
+            ApiCallRecord record = mCallCount.get(provider);
+
             final long now = mMonotonicClock.getAsLong();
             final long timeSinceLastResetMs = now - record.lastResetTimeMs;
             // If the last reset was beyond the reset interval, reset now.
@@ -5500,14 +5536,6 @@
         public void remove(@NonNull ProviderId id) {
             mCallCount.remove(id);
         }
-
-        @NonNull
-        private ApiCallRecord getOrCreateRecord(@NonNull ProviderId provider) {
-            if (!mCallCount.containsKey(provider)) {
-                mCallCount.put(provider, new ApiCallRecord());
-            }
-            return mCallCount.get(provider);
-        }
     }
 
     private class LoadedWidgetState {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 9f7fb57..259ea14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -423,27 +423,22 @@
     @Nullable
     private AutofillManagerServiceImpl getServiceForUserWithLocalBinderIdentityLocked(int userId) {
         final long token = Binder.clearCallingIdentity();
-        AutofillManagerServiceImpl managerService = null;
         try {
-            managerService = getServiceForUserLocked(userId);
+            return getServiceForUserLocked(userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-        return managerService;
     }
 
     @GuardedBy("mLock")
     @Nullable
     private AutofillManagerServiceImpl peekServiceForUserWithLocalBinderIdentityLocked(int userId) {
         final long token = Binder.clearCallingIdentity();
-        AutofillManagerServiceImpl managerService = null;
         try {
-            managerService = peekServiceForUserLocked(userId);
+            return peekServiceForUserLocked(userId);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-
-        return managerService;
     }
 
     @Override // from AbstractMasterSystemService
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 668852b..cd2a535 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -16,10 +16,13 @@
 
 package com.android.server.autofill;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+
 import static com.android.server.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.assist.AssistStructure;
@@ -29,6 +32,7 @@
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.metrics.LogMaker;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.autofill.Dataset;
 import android.service.autofill.FillResponse;
@@ -57,7 +61,6 @@
 import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-
 public final class Helper {
 
     private static final String TAG = "AutofillHelper";
@@ -69,7 +72,7 @@
      * {@code cmd autofill set log_level debug} or through
      * {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
      */
-    public static boolean sDebug = false;
+    public static boolean sDebug = true;
 
     /**
      * Defines a logging flag that can be dynamically changed at runtime using
@@ -104,6 +107,26 @@
     }
 
     /**
+     * Creates the context as the foreground user
+     *
+     * <p>Returns the current context as the current foreground user
+     */
+    @RequiresPermission(INTERACT_ACROSS_USERS)
+    public static Context getUserContext(Context context) {
+        int userId = ActivityManager.getCurrentUser();
+        Context c = context.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
+        if (sDebug) {
+            Slog.d(
+                    TAG,
+                    "Current User: "
+                            + userId
+                            + ", context created as: "
+                            + c.getContentResolver().getUserId());
+        }
+        return c;
+    }
+
+    /**
      * Checks the URI permissions of the remote view,
      * to see if the current userId is able to access it.
      *
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index 930af5e..5044e93 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -71,6 +71,7 @@
 import android.util.Slog;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
 
 import com.android.internal.util.FrameworkStatsLog;
 
@@ -244,10 +245,18 @@
             Slog.e(TAG, "Failed to start new event because already have active event.");
             return;
         }
+        Slog.d(TAG, "Started new PresentationStatsEvent");
         mEventInternal = Optional.of(new PresentationStatsEventInternal());
     }
 
     /**
+     * Test use only, returns a copy of the events object
+     */
+    Optional<PresentationStatsEventInternal> getInternalEvent() {
+        return mEventInternal;
+    }
+
+    /**
      * Set request_id
      */
     public void maybeSetRequestId(int requestId) {
@@ -339,10 +348,16 @@
         });
     }
 
-    public void maybeSetCountShown(int datasets) {
+    /**
+     * This is called when a dataset is shown to the user. Will set the count shown,
+     * related timestamps and presentation reason.
+     */
+    public void logWhenDatasetShown(int datasets) {
         mEventInternal.ifPresent(
                 event -> {
+                    maybeSetSuggestionPresentedTimestampMs();
                     event.mCountShown = datasets;
+                    event.mNoPresentationReason = NOT_SHOWN_REASON_ANY_SHOWN;
                 });
     }
 
@@ -405,7 +420,12 @@
 
     public void maybeSetDisplayPresentationType(@UiType int uiType) {
         mEventInternal.ifPresent(event -> {
-            event.mDisplayPresentationType = getDisplayPresentationType(uiType);
+            // There are cases in which another UI type will show up after selects a dataset
+            // such as with Inline after Fill Dialog. Set as the first presentation type only.
+            if (event.mDisplayPresentationType
+                    == AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE) {
+                event.mDisplayPresentationType = getDisplayPresentationType(uiType);
+            }
         });
     }
 
@@ -430,9 +450,12 @@
     }
 
     public void maybeSetSuggestionSentTimestampMs(int timestamp) {
-        mEventInternal.ifPresent(event -> {
-            event.mSuggestionSentTimestampMs = timestamp;
-        });
+        mEventInternal.ifPresent(
+                event -> {
+                    if (event.mSuggestionSentTimestampMs == DEFAULT_VALUE_INT) {
+                        event.mSuggestionSentTimestampMs = timestamp;
+                    }
+                });
     }
 
     public void maybeSetSuggestionSentTimestampMs() {
@@ -481,8 +504,6 @@
 
     public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
         mEventInternal.ifPresent(event -> {
-            event.mDisplayPresentationType =
-                    AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
             String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
                     Settings.Secure.DEFAULT_INPUT_METHOD, userId);
             if (TextUtils.isEmpty(imeString)) {
@@ -602,40 +623,56 @@
     }
 
     /**
+     * Sets the field length whenever the text changes. Will keep track of the first
+     * and last modification lengths.
+     */
+    public void updateTextFieldLength(AutofillValue value) {
+        mEventInternal.ifPresent(event -> {
+            if (value == null || !value.isText()) {
+                return;
+            }
+
+            int length = value.getTextValue().length();
+
+            if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
+                event.mFieldFirstLength = length;
+            }
+            event.mFieldLastLength = length;
+        });
+    }
+
+    /**
      * Set various timestamps whenever the ViewState is modified
      *
      * <p>If the ViewState contains ViewState.STATE_AUTOFILLED, sets field_autofilled_timestamp_ms
      * else, set field_first_modified_timestamp_ms (if unset) and field_last_modified_timestamp_ms
      */
-    public void onFieldTextUpdated(ViewState state, int length) {
+    public void onFieldTextUpdated(ViewState state, AutofillValue value) {
         mEventInternal.ifPresent(event -> {
-                    int timestamp = getElapsedTime();
-                    // Focused id should be set before this is called
-                    if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
-                        // if these don't match, the currently field different than before
-                        Slog.w(
-                                TAG,
-                                "Bad view state for: " + event.mFocusedId);
-                        return;
-                    }
+            int timestamp = getElapsedTime();
+            // Focused id should be set before this is called
+            if (state == null || state.id == null || state.id.getViewId() != event.mFocusedId) {
+                // if these don't match, the currently field different than before
+                Slog.w(
+                        TAG,
+                        "Bad view state for: " + event.mFocusedId + ", state: " + state);
+                return;
+            }
 
-                    // Text changed because filling into form, just log Autofill timestamp
-                    if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
-                        event.mAutofilledTimestampMs = timestamp;
-                        return;
-                    }
+            updateTextFieldLength(value);
 
-                    // Set length variables
-                    if (event.mFieldFirstLength == DEFAULT_VALUE_INT) {
-                        event.mFieldFirstLength = length;
-                    }
-                    event.mFieldLastLength = length;
+            // Text changed because filling into form, just log Autofill timestamp
+            if ((state.getState() & ViewState.STATE_AUTOFILLED) != 0) {
+                event.mAutofilledTimestampMs = timestamp;
+                return;
+            }
 
-                    // Set timestamp variables
-                    if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
-                        event.mFieldModifiedFirstTimestampMs = timestamp;
-                    }
-                    event.mFieldModifiedLastTimestampMs = timestamp;
+
+            // Set timestamp variables
+            if (event.mFieldModifiedFirstTimestampMs == DEFAULT_VALUE_INT) {
+                event.mFieldModifiedFirstTimestampMs = timestamp;
+            }
+            event.mFieldModifiedLastTimestampMs = timestamp;
         });
     }
 
@@ -796,7 +833,10 @@
         });
     }
 
-    public void logAndEndEvent() {
+    /**
+     * Finish and log the event.
+     */
+    public void logAndEndEvent(String caller) {
         if (!mEventInternal.isPresent()) {
             Slog.w(TAG, "Shouldn't be logging AutofillPresentationEventReported again for same "
                     + "event");
@@ -804,7 +844,8 @@
         }
         PresentationStatsEventInternal event = mEventInternal.get();
         if (sVerbose) {
-            Slog.v(TAG, "Log AutofillPresentationEventReported:"
+            Slog.v(TAG, "(" + caller + ") "
+                    + "Log AutofillPresentationEventReported:"
                     + " requestId=" + event.mRequestId
                     + " sessionId=" + mSessionId
                     + " mNoPresentationEventReason=" + event.mNoPresentationReason
@@ -926,7 +967,7 @@
         mEventInternal = Optional.empty();
     }
 
-    private static final class PresentationStatsEventInternal {
+    static final class PresentationStatsEventInternal {
         int mRequestId;
         @NotShownReason int mNoPresentationReason = NOT_SHOWN_REASON_UNKNOWN;
         boolean mIsDatasetAvailable;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index b7508b4..b109472 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -28,7 +28,6 @@
 import static android.service.autofill.Dataset.PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC;
 import static android.service.autofill.Dataset.PICK_REASON_UNKNOWN;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_CREDMAN_BOTTOM_SHEET;
-import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN;
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
@@ -67,10 +66,10 @@
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SUCCESS;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TIMEOUT;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_TRANSACTION_TOO_LARGE;
+import static com.android.server.autofill.Helper.SaveInfoStats;
 import static com.android.server.autofill.Helper.containsCharsInOrder;
 import static com.android.server.autofill.Helper.createSanitizers;
 import static com.android.server.autofill.Helper.getNumericValue;
-import static com.android.server.autofill.Helper.SaveInfoStats;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 import static com.android.server.autofill.Helper.toArray;
@@ -78,6 +77,7 @@
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_RESULT_SUCCESS;
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_DATASET_AUTHENTICATION;
 import static com.android.server.autofill.PresentationStatsEventLogger.AUTHENTICATION_TYPE_FULL_AUTHENTICATION;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
 import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
@@ -1288,11 +1288,11 @@
      * Clears the existing response for the partition, reads a new structure, and then requests a
      * new fill response from the fill service.
      *
-     * <p> Also asks the IME to make an inline suggestions request if it's enabled.
+     * <p>Also asks the IME to make an inline suggestions request if it's enabled.
      */
     @GuardedBy("mLock")
-    private void requestNewFillResponseLocked(@NonNull ViewState viewState, int newState,
-            int flags) {
+    private Optional<Integer> requestNewFillResponseLocked(
+            @NonNull ViewState viewState, int newState, int flags) {
         boolean isSecondary = shouldRequestSecondaryProvider(flags);
         final FillResponse existingResponse = isSecondary
                 ? viewState.getSecondaryResponse() : viewState.getResponse();
@@ -1333,7 +1333,7 @@
             mFillRequestEventLogger.maybeSetIsAugmented(true);
             mFillRequestEventLogger.logAndEndEvent();
             triggerAugmentedAutofillLocked(flags);
-            return;
+            return Optional.empty();
         }
 
         viewState.setState(newState);
@@ -1353,11 +1353,6 @@
                     + ", flags=" + flags);
         }
         boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
-        mPresentationStatsEventLogger.maybeSetRequestId(requestId);
-        mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
-        mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
-                mFieldClassificationIdSnapshot);
-        mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
         mFillRequestEventLogger.maybeSetRequestId(requestId);
         mFillRequestEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
         mSaveEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -1417,6 +1412,8 @@
 
         // Now request the assist structure data.
         requestAssistStructureLocked(requestId, flags);
+
+        return Optional.of(requestId);
     }
 
     private boolean isRequestSupportFillDialog(int flags) {
@@ -1662,6 +1659,7 @@
         final LogMaker requestLog;
 
         synchronized (mLock) {
+            mPresentationStatsEventLogger.maybeSetRequestId(requestId);
             // Start a new FillResponse logger for the success case.
             mFillResponseEventLogger.startLogForNewResponse();
             mFillResponseEventLogger.maybeSetRequestId(requestId);
@@ -2419,7 +2417,7 @@
                         NOT_SHOWN_REASON_REQUEST_FAILED);
                 mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_FAILURE);
             }
-            mPresentationStatsEventLogger.logAndEndEvent();
+            mPresentationStatsEventLogger.logAndEndEvent("fill request failure");
             mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
             mFillResponseEventLogger.logAndEndEvent();
         }
@@ -2642,6 +2640,8 @@
     public void onShown(int uiType, int numDatasetsShown) {
         synchronized (mLock) {
             mPresentationStatsEventLogger.maybeSetDisplayPresentationType(uiType);
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                    NOT_SHOWN_REASON_ANY_SHOWN);
 
             if (uiType == UI_TYPE_INLINE) {
                 // Inline Suggestions are inflated one at a time
@@ -2657,7 +2657,7 @@
                 }
                 mLoggedInlineDatasetShown = true;
             } else {
-                mPresentationStatsEventLogger.maybeSetCountShown(numDatasetsShown);
+                mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
                 // Explicitly sets maybeSetSuggestionPresentedTimestampMs
                 mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
                 mService.logDatasetShown(this.id, mClientState, uiType);
@@ -2800,7 +2800,10 @@
             if (mCurrentViewId == null) {
                 return;
             }
+            mPresentationStatsEventLogger.logAndEndEvent("fallback from fill dialog");
+            startNewEventForPresentationStatsEventLogger();
             final ViewState currentView = mViewStates.get(mCurrentViewId);
+            logPresentationStatsOnViewEnteredLocked(currentView.getResponse(), false);
             currentView.maybeCallOnFillReady(mFlags);
         }
     }
@@ -2850,7 +2853,7 @@
         if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
             setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
             // Augmented autofill is not logged.
-            mPresentationStatsEventLogger.logAndEndEvent();
+            mPresentationStatsEventLogger.logAndEndEvent("authentication - augmented");
             return;
         }
         if (mResponses == null) {
@@ -2859,7 +2862,7 @@
             Slog.w(TAG, "setAuthenticationResultLocked(" + authenticationId + "): no responses");
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                     AUTHENTICATION_RESULT_FAILURE);
-            mPresentationStatsEventLogger.logAndEndEvent();
+            mPresentationStatsEventLogger.logAndEndEvent("authentication - no response");
             removeFromService();
             return;
         }
@@ -2870,7 +2873,7 @@
             Slog.w(TAG, "no authenticated response");
             mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                     AUTHENTICATION_RESULT_FAILURE);
-            mPresentationStatsEventLogger.logAndEndEvent();
+            mPresentationStatsEventLogger.logAndEndEvent("authentication - bad response");
             removeFromService();
             return;
         }
@@ -2885,7 +2888,7 @@
                 Slog.w(TAG, "no dataset with index " + datasetIdx + " on fill response");
                 mPresentationStatsEventLogger.maybeSetAuthenticationResult(
                         AUTHENTICATION_RESULT_FAILURE);
-                mPresentationStatsEventLogger.logAndEndEvent();
+                mPresentationStatsEventLogger.logAndEndEvent("authentication - no datasets");
                 removeFromService();
                 return;
             }
@@ -3330,7 +3333,7 @@
 
         mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
                 PresentationStatsEventLogger.getNoPresentationEventReason(commitReason));
-        mPresentationStatsEventLogger.logAndEndEvent();
+        mPresentationStatsEventLogger.logAndEndEvent("Context committed");
 
         final int flags = lastResponse.getFlags();
         if ((flags & FillResponse.FLAG_TRACK_CONTEXT_COMMITED) == 0) {
@@ -4299,6 +4302,7 @@
      * Starts (if necessary) a new fill request upon entering a view.
      *
      * <p>A new request will be started in 2 scenarios:
+     *
      * <ol>
      *   <li>If the user manually requested autofill.
      *   <li>If the view is part of a new partition.
@@ -4307,18 +4311,17 @@
      * @param id The id of the view that is entered.
      * @param viewState The view that is entered.
      * @param flags The flag that was passed by the AutofillManager.
-     *
      * @return {@code true} if a new fill response is requested.
      */
     @GuardedBy("mLock")
-    private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
-            @NonNull ViewState viewState, int flags) {
+    private Optional<Integer> requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+            @NonNull AutofillId id, @NonNull ViewState viewState, int flags) {
         // Force new response for manual request
         if ((flags & FLAG_MANUAL_REQUEST) != 0) {
             mSessionFlags.mAugmentedAutofillOnly = false;
             if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
-            requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
-            return true;
+            return requestNewFillResponseLocked(
+                    viewState, ViewState.STATE_RESTARTED_SESSION, flags);
         }
 
         // If it's not, then check if it should start a partition.
@@ -4331,15 +4334,15 @@
             // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as
             // augmentedOnly, but other fields are still fillable by standard autofill.
             mSessionFlags.mAugmentedAutofillOnly = false;
-            requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
-            return true;
+            return requestNewFillResponseLocked(
+                    viewState, ViewState.STATE_STARTED_PARTITION, flags);
         }
 
         if (sVerbose) {
             Slog.v(TAG, "Not starting new partition for view " + id + ": "
                     + viewState.getStateAsString());
         }
-        return false;
+        return Optional.empty();
     }
 
     /**
@@ -4428,31 +4431,32 @@
     void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
             int flags) {
         if (mDestroyed) {
-            Slog.w(TAG, "Call to Session#updateLocked() rejected - session: "
-                    + id + " destroyed");
+            Slog.w(TAG, "updateLocked(" + id + "):  rejected - session: destroyed");
             return;
         }
         if (action == ACTION_RESPONSE_EXPIRED) {
             mSessionFlags.mExpiredResponse = true;
             if (sDebug) {
-                Slog.d(TAG, "Set the response has expired.");
+                Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired.");
             }
             mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
                         NOT_SHOWN_REASON_VIEW_CHANGED);
-            mPresentationStatsEventLogger.logAndEndEvent();
+            mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED");
             return;
         }
 
         id.setSessionId(this.id);
-        if (sVerbose) {
-            Slog.v(TAG, "updateLocked(" + this.id + "): id=" + id + ", action="
-                    + actionAsString(action) + ", flags=" + flags);
-        }
         ViewState viewState = mViewStates.get(id);
         if (sVerbose) {
-            Slog.v(TAG, "updateLocked(" + this.id + "): mCurrentViewId=" + mCurrentViewId
-                    + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
-                    + ", viewState=" + viewState);
+            Slog.v(
+                    TAG,
+                    "updateLocked(" + id + "): "
+                            + "id=" + this.id
+                            + ", action=" + actionAsString(action)
+                            + ", flags=" + flags
+                            + ", mCurrentViewId=" + mCurrentViewId
+                            + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse
+                            + ", viewState=" + viewState);
         }
 
         if (viewState == null) {
@@ -4505,14 +4509,14 @@
                     mSessionFlags.mFillDialogDisabled = true;
                     mPreviouslyFillDialogPotentiallyStarted = false;
                 } else {
-                    // Set the default reason for now if the user doesn't trigger any focus event
-                    // on the autofillable view. This can be changed downstream when more
-                    // information is available or session is committed.
-                    mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                            NOT_SHOWN_REASON_NO_FOCUS);
                     mPreviouslyFillDialogPotentiallyStarted = true;
                 }
-                requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
+                Optional<Integer> maybeRequestId =
+                        requestNewFillResponseLocked(
+                                viewState, ViewState.STATE_STARTED_SESSION, flags);
+                if (maybeRequestId.isPresent()) {
+                    mPresentationStatsEventLogger.maybeSetRequestId(maybeRequestId.get());
+                }
                 break;
             case ACTION_VALUE_CHANGED:
                 if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) {
@@ -4577,8 +4581,10 @@
                 }
                 boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0;
                 if (shouldRequestSecondaryProvider(flags)) {
-                    if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(
-                            id, viewState, flags)) {
+                    Optional<Integer> maybeRequestIdCred =
+                            requestNewFillResponseOnViewEnteredIfNecessaryLocked(
+                                    id, viewState, flags);
+                    if (maybeRequestIdCred.isPresent()) {
                         Slog.v(TAG, "Started a new fill request for secondary provider.");
                         return;
                     }
@@ -4622,17 +4628,7 @@
                     mLogViewEntered = true;
                 }
 
-                // Previously, fill request will only start whenever a view is entered.
-                // With Fill Dialog, request starts prior to view getting entered. So, we can't end
-                // the event at this moment, otherwise we will be wrongly attributing fill dialog
-                // event as concluded.
-                if (!wasPreviouslyFillDialog && !isSameViewAgain) {
-                    // TODO(b/319872477): Re-consider this logic below
-                    mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
-                            NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
-                    mPresentationStatsEventLogger.logAndEndEvent();
-                }
-
+                // Trigger augmented autofill if applicable
                 if ((flags & FLAG_MANUAL_REQUEST) == 0) {
                     // Not a manual request
                     if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains(
@@ -4658,26 +4654,26 @@
                         return;
                     }
                 }
-                // If previous request was FillDialog request, a logger event was already started
-                if (!wasPreviouslyFillDialog) {
+
+                Optional<Integer> maybeNewRequestId =
+                        requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags);
+
+                // Previously, fill request will only start whenever a view is entered.
+                // With Fill Dialog, request starts prior to view getting entered. So, we can't end
+                // the event at this moment, otherwise we will be wrongly attributing fill dialog
+                // event as concluded.
+                if (!wasPreviouslyFillDialog
+                        && (!isSameViewEntered || maybeNewRequestId.isPresent())) {
+                    mPresentationStatsEventLogger.logAndEndEvent("new view entered");
                     startNewEventForPresentationStatsEventLogger();
-                }
-                if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
-                    // If a new request was issued even if previously it was fill dialog request,
-                    // we should end the log event, and start a new one. However, it leaves us
-                    // susceptible to race condition. But since mPresentationStatsEventLogger is
-                    // lock guarded, we should be safe.
-                    if (wasPreviouslyFillDialog) {
-                        mPresentationStatsEventLogger.logAndEndEvent();
-                        startNewEventForPresentationStatsEventLogger();
+                    if (maybeNewRequestId.isPresent()) {
+                        mPresentationStatsEventLogger.maybeSetRequestId(maybeNewRequestId.get());
                     }
-                    return;
                 }
 
-                FillResponse response = viewState.getResponse();
-                if (response != null) {
-                    logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested);
-                }
+                logPresentationStatsOnViewEnteredLocked(
+                        viewState.getResponse(), isCredmanRequested);
+                mPresentationStatsEventLogger.updateTextFieldLength(value);
 
                 if (isSameViewEntered) {
                     setFillDialogDisabledAndStartInput();
@@ -4719,13 +4715,17 @@
     @GuardedBy("mLock")
     private void logPresentationStatsOnViewEnteredLocked(FillResponse response,
             boolean isCredmanRequested) {
-        mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
         mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested);
         mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId(
                 mFieldClassificationIdSnapshot);
-        mPresentationStatsEventLogger.maybeSetAvailableCount(
-                response.getDatasets(), mCurrentViewId);
+        mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
         mPresentationStatsEventLogger.maybeSetFocusedId(mCurrentViewId);
+
+        if (response != null) {
+            mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId());
+            mPresentationStatsEventLogger.maybeSetAvailableCount(
+                    response.getDatasets(), mCurrentViewId);
+        }
     }
 
     @GuardedBy("mLock")
@@ -4796,8 +4796,12 @@
 
         viewState.setCurrentValue(value);
         final String filterText = textValue;
-
         final AutofillValue filledValue = viewState.getAutofilledValue();
+
+        if (textValue != null) {
+            mPresentationStatsEventLogger.onFieldTextUpdated(viewState, value);
+        }
+
         if (filledValue != null) {
             if (filledValue.equals(value)) {
                 // When the update is caused by autofilling the view, just update the
@@ -4821,9 +4825,6 @@
                 currentView.maybeCallOnFillReady(flags);
             }
         }
-        if (textValue != null) {
-            mPresentationStatsEventLogger.onFieldTextUpdated(viewState, textValue.length());
-        }
 
         if (viewState.id.equals(this.mCurrentViewId)
                 && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
@@ -4888,7 +4889,7 @@
                 mSaveEventLogger.logAndEndEvent();
                 mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
                     NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY);
-                mPresentationStatsEventLogger.logAndEndEvent();
+                mPresentationStatsEventLogger.logAndEndEvent("on fill ready");
                 return;
             }
         }
@@ -4920,7 +4921,6 @@
                 synchronized (mLock) {
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
-                    mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_DIALOG);
                 }
                 // Just show fill dialog once, so disabled after shown.
                 // Note: Cannot disable before requestShowFillDialog() because the method
@@ -5084,11 +5084,17 @@
         // Try to get the custom Icon, if one was passed through FillResponse
         int iconResourceId = response.getIconResourceId();
         if (iconResourceId != 0) {
-            serviceIcon = mService.getMaster().getContext().getPackageManager()
-                .getDrawable(
-                    mService.getServicePackageName(),
-                    iconResourceId,
-                    null);
+            long token = Binder.clearCallingIdentity();
+            try {
+                serviceIcon =
+                        mService.getMaster()
+                                .getContext()
+                                .getPackageManager()
+                                .getDrawable(
+                                        mService.getServicePackageName(), iconResourceId, null);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         // Custom icon wasn't fetched, use the default package icon instead
@@ -5114,11 +5120,19 @@
         // Try to get the custom Service name, if one was passed through FillResponse
         int customServiceNameId = response.getServiceDisplayNameResourceId();
         if (customServiceNameId != 0) {
-            serviceLabel = mService.getMaster().getContext().getPackageManager()
-                .getText(
-                    mService.getServicePackageName(),
-                    customServiceNameId,
-                    null);
+            long token = Binder.clearCallingIdentity();
+            try {
+                serviceLabel =
+                        mService.getMaster()
+                                .getContext()
+                                .getPackageManager()
+                                .getText(
+                                        mService.getServicePackageName(),
+                                        customServiceNameId,
+                                        null);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         // Custom label wasn't fetched, use the default package name instead
@@ -6072,6 +6086,11 @@
     private void startNewEventForPresentationStatsEventLogger() {
         synchronized (mLock) {
             mPresentationStatsEventLogger.startNewEvent();
+            // Set the default reason for now if the user doesn't trigger any focus event
+            // on the autofillable view. This can be changed downstream when more
+            // information is available or session is committed.
+            mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+                    NOT_SHOWN_REASON_NO_FOCUS);
             mPresentationStatsEventLogger.maybeSetDetectionPreference(
                     getDetectionPreferenceForLogging());
             mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
@@ -6710,7 +6729,7 @@
             SystemClock.elapsedRealtime() - mStartTime);
         mFillRequestEventLogger.logAndEndEvent();
         mFillResponseEventLogger.logAndEndEvent();
-        mPresentationStatsEventLogger.logAndEndEvent();
+        mPresentationStatsEventLogger.logAndEndEvent("log all events");
         mSaveEventLogger.logAndEndEvent();
         mSessionCommittedEventLogger.logAndEndEvent();
     }
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index a10039f..2446a6d 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -461,9 +461,9 @@
                         }
 
                         @Override
-                        public void onShown() {
+                        public void onShown(int datasetsShown) {
                             if (mCallback != null) {
-                                mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size());
+                                mCallback.onShown(UI_TYPE_DIALOG, datasetsShown);
                             }
                         }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index fa414e3..c7b6be6 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -85,7 +85,7 @@
         void onDatasetPicked(@NonNull Dataset dataset);
         void onDismissed();
         void onCanceled();
-        void onShown();
+        void onShown(int datasetsShown);
         void startIntentSender(IntentSender intentSender);
     }
 
@@ -95,6 +95,7 @@
     private final ComponentName mComponentName;
     private final int mThemeId;
     private final @NonNull Context mContext;
+    private final @NonNull Context mUserContext;
     private final @NonNull UiCallback mCallback;
     private final @NonNull ListView mListView;
     private final @Nullable ItemsAdapter mAdapter;
@@ -104,6 +105,8 @@
     private @Nullable AnnounceFilterResult mAnnounceFilterResult;
     private boolean mDestroyed;
 
+    // System has all permissions, see b/228957088
+    @SuppressWarnings("AndroidFrameworkRequiresPermission")
     DialogFillUi(@NonNull Context context, @NonNull FillResponse response,
             @NonNull AutofillId focusedViewId, @Nullable String filterText,
             @Nullable Drawable serviceIcon, @Nullable String servicePackageName,
@@ -117,6 +120,7 @@
         mComponentName = componentName;
 
         mContext = new ContextThemeWrapper(context, mThemeId);
+        mUserContext = Helper.getUserContext(mContext);
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         final View decor = inflater.inflate(R.layout.autofill_fill_dialog, null);
 
@@ -151,7 +155,8 @@
         mDialog.setContentView(decor);
         setDialogParamsAsBottomSheet();
         mDialog.setOnCancelListener((d) -> mCallback.onCanceled());
-        mDialog.setOnShowListener((d) -> mCallback.onShown());
+        int datasetsShown = (mAdapter != null) ? mAdapter.getCount() : 0;
+        mDialog.setOnShowListener((d) -> mCallback.onShown(datasetsShown));
         show();
     }
 
@@ -224,7 +229,7 @@
         };
 
         final View content = presentation.applyWithTheme(
-                mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+                mUserContext, (ViewGroup) decor, interceptionHandler, mThemeId);
         container.addView(content);
         container.setVisibility(View.VISIBLE);
     }
@@ -263,7 +268,7 @@
             return true;
         };
         final View content = presentation.applyWithTheme(
-                mContext, (ViewGroup) decor, interceptionHandler, mThemeId);
+                mUserContext, (ViewGroup) decor, interceptionHandler, mThemeId);
         container.addView(content);
         container.setVisibility(View.VISIBLE);
         container.setFocusable(true);
@@ -305,7 +310,7 @@
                 try {
                     if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
                     view = presentation.applyWithTheme(
-                            mContext, null, interceptionHandler, mThemeId);
+                            mUserContext, null, interceptionHandler, mThemeId);
                 } catch (RuntimeException e) {
                     Slog.e(TAG, "Error inflating remote views", e);
                     continue;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 1bda70d..dc0b4b8 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -48,6 +48,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutofillWindowPresenter;
 import android.widget.BaseAdapter;
@@ -82,7 +83,6 @@
             com.android.internal.R.style.Theme_DeviceDefault_Light_Autofill;
     private static final int THEME_ID_DARK =
             com.android.internal.R.style.Theme_DeviceDefault_Autofill;
-    private static final int AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS = 5;
 
     private static final TypedValue sTempTypedValue = new TypedValue();
 
@@ -107,14 +107,17 @@
             new AutofillWindowPresenter();
 
     private final @NonNull Context mContext;
+    private final @NonNull Context mUserContext;
 
     private final @NonNull AnchoredWindow mWindow;
 
     private final @NonNull Callback mCallback;
 
+    private final @NonNull WindowManager mWindowManager;
+
     private final @Nullable View mHeader;
     private final @NonNull ListView mListView;
-    private final @Nullable View mFooter;
+    private @Nullable View mFooter;
 
     private final @Nullable ItemsAdapter mAdapter;
 
@@ -133,6 +136,8 @@
 
     private int mMaxInputLengthForAutofill;
 
+    private final boolean mIsCredmanAutofillSession;
+
     public static boolean isFullScreen(Context context) {
         if (sFullScreenMode != null) {
             if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode);
@@ -141,6 +146,8 @@
         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
+    // System has all permissions, see b/228957088
+    @SuppressWarnings("AndroidFrameworkRequiresPermission")
     FillUi(@NonNull Context context, @NonNull FillResponse response,
             @NonNull AutofillId focusedViewId, @Nullable String filterText,
             @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
@@ -153,7 +160,11 @@
         mCallback = callback;
         mFullScreen = isFullScreen(context);
         mContext = new ContextThemeWrapper(context, mThemeId);
+        mUserContext = Helper.getUserContext(mContext);
         mMaxInputLengthForAutofill = maxInputLengthForAutofill;
+        mIsCredmanAutofillSession = (Flags.autofillCredmanIntegration()
+            && ((response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0));
+        mWindowManager = mContext.getSystemService(WindowManager.class);
 
         final LayoutInflater inflater = LayoutInflater.from(mContext);
 
@@ -163,7 +174,8 @@
         final ViewGroup decor;
         if (mFullScreen) {
             decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
-        } else if (headerPresentation != null || footerPresentation != null) {
+        } else if (headerPresentation != null
+                || footerPresentation != null || mIsCredmanAutofillSession) {
             decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
                     null);
         } else {
@@ -215,11 +227,7 @@
             if (sVerbose) {
                 Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
             }
-        } else if (Flags.autofillCredmanIntegration() && (
-                (response.getFlags() & FLAG_CREDENTIAL_MANAGER_RESPONSE) != 0)) {
-            mVisibleDatasetsMaxCount = AUTOFILL_CREDMAN_MAX_VISIBLE_DATASETS;
-        }
-        else {
+        } else {
             mVisibleDatasetsMaxCount = mContext.getResources()
                     .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
         }
@@ -245,7 +253,7 @@
                     throw new RuntimeException("Permission error accessing RemoteView");
                 }
                 content = response.getPresentation().applyWithTheme(
-                        mContext, decor, interceptionHandler, mThemeId);
+                        mUserContext, decor, interceptionHandler, mThemeId);
                 container.addView(content);
             } catch (RuntimeException e) {
                 callback.onCanceled();
@@ -286,7 +294,7 @@
             if (headerPresentation != null) {
                 interactionBlocker = newInteractionBlocker();
                 mHeader = headerPresentation.applyWithTheme(
-                        mContext, null, interactionBlocker, mThemeId);
+                        mUserContext, null, interactionBlocker, mThemeId);
                 final LinearLayout headerContainer =
                         decor.findViewById(R.id.autofill_dataset_header);
                 applyCancelAction(mHeader, response.getCancelIds());
@@ -297,7 +305,7 @@
                 mHeader = null;
             }
 
-            if (footerPresentation != null) {
+            if (footerPresentation != null && !mIsCredmanAutofillSession) {
                 final LinearLayout footerContainer =
                         decor.findViewById(R.id.autofill_dataset_footer);
                 if (footerContainer != null) {
@@ -305,7 +313,7 @@
                         interactionBlocker = newInteractionBlocker();
                     }
                     mFooter = footerPresentation.applyWithTheme(
-                            mContext, null, interactionBlocker, mThemeId);
+                            mUserContext, null, interactionBlocker, mThemeId);
                     applyCancelAction(mFooter, response.getCancelIds());
                     // Footer not supported on some platform e.g. TV
                     if (sVerbose) Slog.v(TAG, "adding footer");
@@ -334,7 +342,7 @@
                     try {
                         if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
                         view = presentation.applyWithTheme(
-                                mContext, null, interceptionHandler, mThemeId);
+                                mUserContext, null, interceptionHandler, mThemeId);
                     } catch (RuntimeException e) {
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
@@ -362,7 +370,22 @@
                     }
 
                     applyCancelAction(view, response.getCancelIds());
-                    items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
+                    if (AutofillManager.PINNED_DATASET_ID.equals(dataset.getId())
+                            && mIsCredmanAutofillSession && !items.isEmpty()) {
+                        final LinearLayout footerContainer =
+                                decor.findViewById(R.id.autofill_dataset_footer);
+                        if (sVerbose) {
+                          Slog.v(TAG, "adding footer");
+                        }
+                        mFooter = view;
+                        footerContainer.addView(mFooter);
+                        footerContainer.setVisibility(View.VISIBLE);
+                        footerContainer.setClickable(true);
+                        footerContainer.setOnClickListener(v -> mCallback.onDatasetPicked(dataset));
+                    } else {
+                        items.add(
+                            new ViewItem(dataset, filterPattern, filterable, valueText, view));
+                    }
                 }
             }
 
@@ -455,12 +478,9 @@
                 if (updateContentSize()) {
                     requestShowFillUi();
                 }
-                if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
-                    mListView.setVerticalScrollBarEnabled(true);
-                    mListView.onVisibilityAggregated(true);
-                } else {
-                    mListView.setVerticalScrollBarEnabled(false);
-                }
+                mListView.setVerticalScrollBarEnabled(true);
+                mListView.onVisibilityAggregated(true);
+
                 if (mAdapter.getCount() != oldCount) {
                     mListView.requestLayout();
                 }
@@ -574,11 +594,18 @@
         return changed;
     }
 
+    private boolean heightLesserThanDisplayScreen(int height) {
+        // Don't update list height for credential options beyond 80% of display window even if we
+        // are still under the max visible number of datasets. This could happen when font or
+        // display size is set to large.
+        return height < (0.8 * mWindowManager.getCurrentWindowMetrics().getBounds().height());
+    }
+
     private boolean updateHeight(View view, Point maxSize) {
         boolean changed = false;
         final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
         final int newContentHeight = mContentHeight + clampedMeasuredHeight;
-        if (newContentHeight != mContentHeight) {
+        if (newContentHeight != mContentHeight && heightLesserThanDisplayScreen(newContentHeight)) {
             mContentHeight = newContentHeight;
             changed = true;
         }
@@ -812,6 +839,7 @@
         pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
         pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
         pw.print(prefix); pw.print("mContext: "); pw.println(mContext);
+        pw.print(prefix); pw.print("mUserContext: "); pw.println(mUserContext);
         pw.print(prefix); pw.print("theme id: "); pw.print(mThemeId);
         switch (mThemeId) {
             case THEME_ID_DARK:
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 4d42f15..2ecce0b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -176,6 +176,8 @@
 
     private boolean mDestroyed;
 
+    // System has all permissions, see b/228957088
+    @SuppressWarnings("AndroidFrameworkRequiresPermission")
     SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
            @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
            @Nullable String servicePackageName, @NonNull ComponentName componentName,
@@ -193,7 +195,7 @@
         mComponentName = componentName;
         mCompatMode = compatMode;
 
-        context = new ContextThemeWrapper(context, mThemeId) {
+        context = new ContextThemeWrapper(Helper.getUserContext(context), mThemeId) {
             @Override
             public void startActivity(Intent intent) {
                 if (resolveActivity(intent) == null) {
@@ -235,6 +237,7 @@
                 return null;
             }
         };
+
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.autofill_save, null);
 
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 8abbe56..22eefb3 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -73,6 +73,8 @@
 
 /**
  * Utility methods to read backup tar file.
+ * Exteranl depenency:
+ *  <li> @android.provider.Settings.Secure.V_TO_U_RESTORE_ALLOWLIST
  */
 public class TarBackupReader {
     private static final int TAR_HEADER_OFFSET_TYPE_CHAR = 156;
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index 2bfdd0a..77650eb 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -28,7 +28,6 @@
     ],
     static_libs: [
         "ukey2_jni",
-        "virtualdevice_flags_lib",
         "virtual_camera_service_aidl-java",
     ],
     lint: {
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 b3a2da4..d56f17b 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -20,6 +20,7 @@
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR;
+import static android.companion.CompanionDeviceManager.RESULT_SECURITY_ERROR;
 import static android.content.ComponentName.createRelative;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 
@@ -40,6 +41,7 @@
 import android.companion.AssociatedDevice;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
+import android.companion.Flags;
 import android.companion.IAssociationRequestCallback;
 import android.content.ComponentName;
 import android.content.Context;
@@ -182,7 +184,11 @@
             String errorMessage = "3p apps are not allowed to create associations on watch.";
             Slog.e(TAG, errorMessage);
             try {
-                callback.onFailure(RESULT_INTERNAL_ERROR);
+                if (Flags.associationFailureCode()) {
+                    callback.onFailure(RESULT_SECURITY_ERROR, errorMessage);
+                } else {
+                    callback.onFailure(RESULT_INTERNAL_ERROR, errorMessage);
+                }
             } catch (RemoteException e) {
                 // ignored
             }
@@ -251,9 +257,12 @@
         } catch (SecurityException e) {
             // Since, at this point the caller is our own UI, we need to catch the exception on
             // forward it back to the application via the callback.
-            Slog.e(TAG, e.getMessage());
             try {
-                callback.onFailure(RESULT_INTERNAL_ERROR);
+                if (Flags.associationFailureCode()) {
+                    callback.onFailure(RESULT_SECURITY_ERROR, e.getMessage());
+                } else {
+                    callback.onFailure(RESULT_INTERNAL_ERROR, e.getMessage());
+                }
             } catch (RemoteException ignore) {
             }
             return;
@@ -378,7 +387,7 @@
             // Send the association back via the app's callback
             if (callback != null) {
                 try {
-                    callback.onFailure(RESULT_INTERNAL_ERROR);
+                    callback.onFailure(RESULT_INTERNAL_ERROR, "Association doesn't exist.");
                 } catch (RemoteException ignore) {
                 }
             }
diff --git a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
index 4678a16..5fd282d 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/ObservableUuidStore.java
@@ -190,7 +190,7 @@
             }
             mCachedPerUser.set(userId, cachedObservableUuids);
         }
-        return cachedObservableUuids;
+        return cachedObservableUuids == null ? new ArrayList<>() : cachedObservableUuids;
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
deleted file mode 100644
index 66313e6..0000000
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-package {
-    default_team: "trendy_team_xr_framework",
-}
-
-java_aconfig_library {
-    name: "virtualdevice_flags_lib",
-    aconfig_declarations: "virtualdevice_flags",
-}
-
-aconfig_declarations {
-    name: "virtualdevice_flags",
-    package: "com.android.server.companion.virtual",
-    container: "system",
-    srcs: [
-        "flags.aconfig",
-    ],
-}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index e57817f..4b9065b 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -27,7 +27,6 @@
 import android.annotation.UserIdInt;
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.AttributionSource;
@@ -61,6 +60,9 @@
 
     private static final String TAG = "GenericWindowPolicyController";
 
+    private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
+            new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+
     /** Interface to listen running applications change on virtual display. */
     public interface RunningAppsChangedListener {
         /**
@@ -69,29 +71,25 @@
         void onRunningAppsChanged(ArraySet<Integer> runningUids);
     }
 
-    /**
-     * For communicating when activities are blocked from running on the display by this policy
-     * controller.
-     */
-    public interface ActivityBlockedCallback {
+    /** Interface to react to activity changes on the virtual display. */
+    public interface ActivityListener {
+
+        /** Called when the top activity changes. */
+        void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+                @UserIdInt int userId);
+
+        /** Called when the display becomes empty. */
+        void onDisplayEmpty(int displayId);
+
         /** Called when an activity is blocked.*/
-        void onActivityBlocked(int displayId, ActivityInfo activityInfo, IntentSender intentSender);
-    }
-    private static final ComponentName BLOCKED_APP_STREAMING_COMPONENT =
-            new ComponentName("android", BlockedAppStreamingActivity.class.getName());
+        void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+                @Nullable IntentSender intentSender);
 
-    /**
-     * For communicating when a secure window shows on the virtual display.
-     */
-    public interface SecureWindowCallback {
         /** Called when a secure window shows on the virtual display. */
-        void onSecureWindowShown(int displayId, int uid);
-    }
+        void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo);
 
-    /** Interface to listen for interception of intents. */
-    public interface IntentListenerCallback {
         /** Returns true when an intent should be intercepted */
-        boolean shouldInterceptIntent(Intent intent);
+        boolean shouldInterceptIntent(@NonNull Intent intent);
     }
 
     /**
@@ -110,12 +108,14 @@
     @NonNull
     @GuardedBy("mGenericWindowPolicyControllerLock")
     private final ArraySet<ComponentName> mActivityPolicyExemptions;
+    @NonNull
+    @GuardedBy("mGenericWindowPolicyControllerLock")
+    private final ArraySet<String> mActivityPolicyPackageExemptions;
     private final boolean mCrossTaskNavigationAllowedByDefault;
     @NonNull
     private final ArraySet<ComponentName> mCrossTaskNavigationExemptions;
-    @Nullable
+    @NonNull
     private final Object mGenericWindowPolicyControllerLock = new Object();
-    @Nullable private final ActivityBlockedCallback mActivityBlockedCallback;
 
     // Do not access mDisplayId and mIsMirrorDisplay directly, instead use waitAndGetDisplayId()
     // and waitAndGetIsMirrorDisplay()
@@ -126,14 +126,12 @@
     @NonNull
     @GuardedBy("mGenericWindowPolicyControllerLock")
     private final ArraySet<Integer> mRunningUids = new ArraySet<>();
-    @Nullable private final ActivityListener mActivityListener;
-    @Nullable private final IntentListenerCallback mIntentListenerCallback;
+    @NonNull private final ActivityListener mActivityListener;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     @NonNull
     @GuardedBy("mGenericWindowPolicyControllerLock")
     private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListeners =
             new ArraySet<>();
-    @Nullable private final SecureWindowCallback mSecureWindowCallback;
     @NonNull private final Set<String> mDisplayCategories;
 
     @GuardedBy("mGenericWindowPolicyControllerLock")
@@ -152,17 +150,13 @@
      *   or blocked.
      * @param activityPolicyExemptions The set of activities explicitly exempt from the default
      *   activity policy.
+     * @param activityPolicyPackageExemptions The set of packages whose activities are explicitly
+     *   exempt from the default activity policy.
      * @param crossTaskNavigationAllowedByDefault Whether cross task navigations are allowed by
      *   default or not.
      * @param crossTaskNavigationExemptions The set of components explicitly exempt from the default
      *   navigation policy.
      * @param activityListener Activity listener to listen for activity changes.
-     * @param activityBlockedCallback Callback that is called when an activity is blocked from
-     *   launching.
-     * @param secureWindowCallback Callback that is called when a secure window shows on the
-     *   virtual display.
-     * @param intentListenerCallback Callback that is called to intercept intents when matching
-     *   passed in filters.
      * @param showTasksInHostDeviceRecents whether to show activities in recents on the host device.
      * @param customHomeComponent The component acting as a home activity on the virtual display. If
      *   {@code null}, then the system-default secondary home activity will be used. This is only
@@ -176,12 +170,10 @@
             @NonNull ArraySet<UserHandle> allowedUsers,
             boolean activityLaunchAllowedByDefault,
             @NonNull Set<ComponentName> activityPolicyExemptions,
+            @NonNull Set<String> activityPolicyPackageExemptions,
             boolean crossTaskNavigationAllowedByDefault,
             @NonNull Set<ComponentName> crossTaskNavigationExemptions,
-            @Nullable ActivityListener activityListener,
-            @Nullable ActivityBlockedCallback activityBlockedCallback,
-            @Nullable SecureWindowCallback secureWindowCallback,
-            @Nullable IntentListenerCallback intentListenerCallback,
+            @NonNull ActivityListener activityListener,
             @NonNull Set<String> displayCategories,
             boolean showTasksInHostDeviceRecents,
             @Nullable ComponentName customHomeComponent) {
@@ -190,13 +182,11 @@
         mAllowedUsers = allowedUsers;
         mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
         mActivityPolicyExemptions = new ArraySet<>(activityPolicyExemptions);
+        mActivityPolicyPackageExemptions = new ArraySet<>(activityPolicyPackageExemptions);
         mCrossTaskNavigationAllowedByDefault = crossTaskNavigationAllowedByDefault;
         mCrossTaskNavigationExemptions = new ArraySet<>(crossTaskNavigationExemptions);
-        mActivityBlockedCallback = activityBlockedCallback;
         setInterestedWindowFlags(windowFlags, systemWindowFlags);
         mActivityListener = activityListener;
-        mSecureWindowCallback = secureWindowCallback;
-        mIntentListenerCallback = intentListenerCallback;
         mDisplayCategories = displayCategories;
         mShowTasksInHostDeviceRecents = showTasksInHostDeviceRecents;
         mCustomHomeComponent = customHomeComponent;
@@ -250,6 +240,7 @@
         synchronized (mGenericWindowPolicyControllerLock) {
             if (mActivityLaunchAllowedByDefault != activityLaunchDefaultAllowed) {
                 mActivityPolicyExemptions.clear();
+                mActivityPolicyPackageExemptions.clear();
             }
             mActivityLaunchAllowedByDefault = activityLaunchDefaultAllowed;
         }
@@ -267,6 +258,18 @@
         }
     }
 
+    void addActivityPolicyExemption(@NonNull String packageName) {
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mActivityPolicyPackageExemptions.add(packageName);
+        }
+    }
+
+    void removeActivityPolicyExemption(@NonNull String packageName) {
+        synchronized (mGenericWindowPolicyControllerLock) {
+            mActivityPolicyPackageExemptions.remove(packageName);
+        }
+    }
+
     /** Register a listener for running applications changes. */
     public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) {
         synchronized (mGenericWindowPolicyControllerLock) {
@@ -286,8 +289,7 @@
             @Nullable Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
             int launchingFromDisplayId, boolean isNewTask, boolean isResultExpected,
             @Nullable Supplier<IntentSender> intentSender) {
-        if (mIntentListenerCallback != null && intent != null
-                && mIntentListenerCallback.shouldInterceptIntent(intent)) {
+        if (intent != null && mActivityListener.shouldInterceptIntent(intent)) {
             logActivityLaunchBlocked("Virtual device intercepting intent");
             return false;
         }
@@ -343,13 +345,10 @@
                     + mDisplayCategories);
             return false;
         }
-        synchronized (mGenericWindowPolicyControllerLock) {
-            if (!isAllowedByPolicy(mActivityLaunchAllowedByDefault, mActivityPolicyExemptions,
-                    activityComponent)) {
-                logActivityLaunchBlocked("Activity launch disallowed by policy: "
-                        + activityComponent);
-                return false;
-            }
+        if (!isAllowedByPolicy(activityComponent)) {
+            logActivityLaunchBlocked("Activity launch disallowed by policy: "
+                    + activityComponent);
+            return false;
         }
         if (isNewTask && launchingFromDisplayId != DEFAULT_DISPLAY
                 && !isAllowedByPolicy(mCrossTaskNavigationAllowedByDefault,
@@ -374,11 +373,9 @@
         int displayId = waitAndGetDisplayId();
         // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
         // aware that the virtual display has a secure window on top.
-        if ((windowFlags & FLAG_SECURE) != 0 && mSecureWindowCallback != null
-                && displayId != INVALID_DISPLAY) {
+        if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) {
             // Post callback on the main thread, so it doesn't block activity launching.
-            mHandler.post(() -> mSecureWindowCallback.onSecureWindowShown(displayId,
-                    activityInfo.applicationInfo.uid));
+            mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
         }
 
         if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
@@ -401,7 +398,7 @@
         // Don't send onTopActivityChanged() callback when topActivity is null because it's defined
         // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when
         // there is no activity running on virtual display.
-        if (mActivityListener != null && topActivity != null && displayId != INVALID_DISPLAY) {
+        if (topActivity != null && displayId != INVALID_DISPLAY) {
             // Post callback on the main thread so it doesn't block activity launching
             mHandler.post(() ->
                     mActivityListener.onTopActivityChanged(displayId, topActivity, userId));
@@ -414,8 +411,7 @@
             mRunningUids.clear();
             mRunningUids.addAll(runningUids);
             int displayId = waitAndGetDisplayId();
-            if (mActivityListener != null && mRunningUids.isEmpty()
-                    && displayId != INVALID_DISPLAY) {
+            if (mRunningUids.isEmpty() && displayId != INVALID_DISPLAY) {
                 // Post callback on the main thread so it doesn't block activity launching
                 mHandler.post(() -> mActivityListener.onDisplayEmpty(displayId));
             }
@@ -465,9 +461,8 @@
         int displayId = waitAndGetDisplayId();
         // Don't trigger activity blocked callback for mirror displays, because we can't show
         // any activity or presentation on it anyway.
-        if (!waitAndGetIsMirrorDisplay() && mActivityBlockedCallback != null
-                && displayId != INVALID_DISPLAY) {
-            mActivityBlockedCallback.onActivityBlocked(displayId, activityInfo,
+        if (!waitAndGetIsMirrorDisplay() && displayId != INVALID_DISPLAY) {
+            mActivityListener.onActivityLaunchBlocked(displayId, activityInfo,
                     intentSender == null ? null : intentSender.get());
         }
         Counter.logIncrementWithUid(
@@ -475,6 +470,16 @@
                 mAttributionSource.getUid());
     }
 
+    private boolean isAllowedByPolicy(ComponentName component) {
+        synchronized (mGenericWindowPolicyControllerLock) {
+            if (mActivityPolicyExemptions.contains(component)
+                    || mActivityPolicyPackageExemptions.contains(component.getPackageName())) {
+                return !mActivityLaunchAllowedByDefault;
+            }
+            return mActivityLaunchAllowedByDefault;
+        }
+    }
+
     private static boolean isAllowedByPolicy(boolean allowedByDefault,
             Set<ComponentName> exemptions, ComponentName component) {
         // Either allowed and the exemptions do not contain the component,
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 8da58cf..d3e808f 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -689,13 +689,15 @@
     }
 
     /** A helper class used to wait for an input device to be registered. */
-    private class WaitForDevice implements  AutoCloseable {
+    private class WaitForDevice implements AutoCloseable {
         private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1);
+        private final String mDeviceName;
         private final InputManager.InputDeviceListener mListener;
 
         private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID;
 
         WaitForDevice(String deviceName, int vendorId, int productId, int associatedDisplayId) {
+            mDeviceName = deviceName;
             mListener = new InputManager.InputDeviceListener() {
                 @Override
                 public void onInputDeviceAdded(int deviceId) {
@@ -741,15 +743,17 @@
             try {
                 if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) {
                     throw new DeviceCreationException(
-                            "Timed out waiting for virtual device to be created.");
+                            "Timed out waiting for virtual input device " + mDeviceName
+                                    + " to be created.");
                 }
             } catch (InterruptedException e) {
                 throw new DeviceCreationException(
-                        "Interrupted while waiting for virtual device to be created.", e);
+                        "Interrupted while waiting for virtual input device " + mDeviceName
+                                + " to be created.", e);
             }
             if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) {
                 throw new IllegalStateException(
-                        "Virtual input device was created with an invalid "
+                        "Virtual input device " + mDeviceName + " was created with an invalid "
                                 + "id=" + mInputDeviceId);
             }
             return mInputDeviceId;
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 2db5443..cd2dd3a 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -24,7 +24,7 @@
 import static android.companion.virtual.VirtualDeviceParams.NAVIGATION_POLICY_DEFAULT_ALLOWED;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
-import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR;
+import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_BLOCKED_ACTIVITY;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CAMERA;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_CLIPBOARD;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
@@ -40,14 +40,15 @@
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
 import android.companion.AssociationInfo;
+import android.companion.virtual.ActivityPolicyExemption;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.IVirtualDeviceActivityListener;
 import android.companion.virtual.IVirtualDeviceIntentInterceptor;
 import android.companion.virtual.IVirtualDeviceSoundEffectListener;
 import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.VirtualDeviceManager.ActivityListener;
 import android.companion.virtual.VirtualDeviceParams;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -55,6 +56,8 @@
 import android.companion.virtual.flags.Flags;
 import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.AttributionSource;
 import android.content.ComponentName;
 import android.content.Context;
@@ -87,6 +90,7 @@
 import android.media.AudioManager;
 import android.media.audiopolicy.AudioMix;
 import android.os.Binder;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.LocaleList;
 import android.os.Looper;
@@ -131,17 +135,22 @@
 
     private static final String TAG = "VirtualDeviceImpl";
 
+    /**
+     * Do not show a toast on the virtual display when a secure surface is shown after
+     * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}. VDM clients should use
+     * {@link VirtualDeviceManager.ActivityListener#onSecureWindowShown} instead to provide
+     * a custom notification if desired.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    public static final long DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN = 311101667L;
+
     private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
             DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED
                     | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL
                     | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
                     | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
 
-    private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC =
-            DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
-                    | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
-                    | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-
     private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:";
 
     /**
@@ -186,7 +195,7 @@
     @GuardedBy("mVirtualDeviceLock")
     private final SparseArray<VirtualDisplayWrapper> mVirtualDisplays = new SparseArray<>();
     private IVirtualDeviceActivityListener mActivityListener;
-    private ActivityListener mActivityListenerAdapter = null;
+    private GenericWindowPolicyController.ActivityListener mActivityListenerAdapter = null;
     private IVirtualDeviceSoundEffectListener mSoundEffectListener;
     private final DisplayManagerGlobal mDisplayManager;
     private final DisplayManagerInternal mDisplayManagerInternal;
@@ -207,51 +216,126 @@
     @GuardedBy("mVirtualDeviceLock")
     @NonNull
     private final Set<ComponentName> mActivityPolicyExemptions;
+    @GuardedBy("mVirtualDeviceLock")
+    @NonNull
+    private final Set<String> mActivityPolicyPackageExemptions = new ArraySet<>();
 
-    private ActivityListener createListenerAdapter() {
-        return new ActivityListener() {
+    private class GwpcActivityListener implements GenericWindowPolicyController.ActivityListener {
 
-            @Override
-            public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity) {
-                try {
-                    mActivityListener.onTopActivityChanged(displayId, topActivity,
-                            UserHandle.USER_NULL);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
-                }
+        @Override
+        public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
+                @UserIdInt int userId) {
+            try {
+                mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+            }
+        }
+
+        @Override
+        public void onDisplayEmpty(int displayId) {
+            try {
+                mActivityListener.onDisplayEmpty(displayId);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+            }
+        }
+
+        @Override
+        public void onActivityLaunchBlocked(int displayId, @NonNull ActivityInfo activityInfo,
+                @Nullable IntentSender intentSender) {
+            Intent intent =
+                    BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
+            if (shouldShowBlockedActivityDialog(
+                    activityInfo.getComponentName(), intent.getComponent())) {
+                mContext.startActivityAsUser(
+                        intent.addFlags(
+                                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
+                        ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
+                        UserHandle.SYSTEM);
             }
 
-            @Override
-            public void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
-                    @UserIdInt int userId) {
-                try {
-                    mActivityListener.onTopActivityChanged(displayId, topActivity, userId);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
-                }
-            }
-
-            @Override
-            public void onDisplayEmpty(int displayId) {
-                try {
-                    mActivityListener.onDisplayEmpty(displayId);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
-                }
-            }
-
-            @Override
-            public void onActivityLaunchBlocked(int displayId,
-                    @NonNull ComponentName componentName, @UserIdInt int userId,
-                    @Nullable IntentSender intentSender) {
+            if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
                 try {
                     mActivityListener.onActivityLaunchBlocked(
-                            displayId, componentName, userId, intentSender);
+                            displayId,
+                            activityInfo.getComponentName(),
+                            UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid),
+                            intentSender);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
                 }
             }
-        };
+        }
+
+        @Override
+        public void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo) {
+            if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+                try {
+                    mActivityListener.onSecureWindowShown(
+                            displayId,
+                            activityInfo.getComponentName(),
+                            UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid));
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+                }
+
+                if (CompatChanges.isChangeEnabled(DO_NOT_SHOW_TOAST_WHEN_SECURE_SURFACE_SHOWN,
+                        mOwnerPackageName,  UserHandle.getUserHandleForUid(mOwnerUid))) {
+                    return;
+                }
+            }
+
+            // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
+            // if the secure window is shown on a non-secure virtual display.
+            DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+            Display display = displayManager.getDisplay(displayId);
+            if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
+                showToastWhereUidIsRunning(activityInfo.applicationInfo.uid,
+                        com.android.internal.R.string.vdm_secure_window,
+                        Toast.LENGTH_LONG, mContext.getMainLooper());
+
+                Counter.logIncrementWithUid(
+                        "virtual_devices.value_secure_window_blocked_count",
+                        mAttributionSource.getUid());
+            }
+        }
+
+        /**
+         * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true
+         * if the intent matches any filter notifying the DisplayPolicyController to abort the
+         * activity launch to be replaced by the interception.
+         */
+        @Override
+        public boolean shouldInterceptIntent(@NonNull Intent intent) {
+            synchronized (mVirtualDeviceLock) {
+                boolean hasInterceptedIntent = false;
+                for (Map.Entry<IBinder, IntentFilter> interceptor
+                        : mIntentInterceptors.entrySet()) {
+                    IntentFilter intentFilter = interceptor.getValue();
+                    // Explicitly match the actions because the intent filter will match any intent
+                    // without an explicit action. If the intent has no action, then require that
+                    // there are no actions specified in the filter either.
+                    boolean explicitActionMatch =
+                            intent.getAction() != null || intentFilter.countActions() == 0;
+                    if (explicitActionMatch && intentFilter.match(
+                            intent.getAction(), intent.getType(), intent.getScheme(),
+                            intent.getData(), intent.getCategories(), TAG) >= 0) {
+                        try {
+                            // For privacy reasons, only returning the intents action and data.
+                            // Any other required field will require a review.
+                            IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
+                                    .onIntentIntercepted(
+                                            new Intent(intent.getAction(), intent.getData()));
+                            hasInterceptedIntent = true;
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Unable to call mActivityListener", e);
+                        }
+                    }
+                }
+                return hasInterceptedIntent;
+            }
+        }
     }
 
     VirtualDeviceImpl(
@@ -373,9 +457,6 @@
         }
 
         int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
-        if (!Flags.consistentDisplayFlags()) {
-            flags |= DEFAULT_VIRTUAL_DISPLAY_FLAGS_PRE_VIC;
-        }
         if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) {
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
         }
@@ -527,13 +608,37 @@
 
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void addActivityPolicyExemption(@NonNull ComponentName componentName) {
+    public void addActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         super.addActivityPolicyExemption_enforcePermission();
+        final int displayId = exemption.getDisplayId();
+        if (exemption.getComponentName() == null || displayId != Display.INVALID_DISPLAY) {
+            if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+                return;
+            }
+        }
         synchronized (mVirtualDeviceLock) {
-            if (mActivityPolicyExemptions.add(componentName)) {
-                for (int i = 0; i < mVirtualDisplays.size(); i++) {
-                    mVirtualDisplays.valueAt(i).getWindowPolicyController()
-                            .addActivityPolicyExemption(componentName);
+            if (displayId != Display.INVALID_DISPLAY) {
+                checkDisplayOwnedByVirtualDeviceLocked(displayId);
+                if (exemption.getComponentName() != null) {
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .addActivityPolicyExemption(exemption.getComponentName());
+                } else if (exemption.getPackageName() != null) {
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .addActivityPolicyExemption(exemption.getPackageName());
+                }
+            } else {
+                if (exemption.getComponentName() != null
+                        && mActivityPolicyExemptions.add(exemption.getComponentName())) {
+                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
+                                .addActivityPolicyExemption(exemption.getComponentName());
+                    }
+                } else if (exemption.getPackageName() != null
+                        && mActivityPolicyPackageExemptions.add(exemption.getPackageName())) {
+                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
+                                .addActivityPolicyExemption(exemption.getPackageName());
+                    }
                 }
             }
         }
@@ -541,45 +646,39 @@
 
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void removeActivityPolicyExemption(@NonNull ComponentName componentName) {
+    public void removeActivityPolicyExemption(@NonNull ActivityPolicyExemption exemption) {
         super.removeActivityPolicyExemption_enforcePermission();
-        synchronized (mVirtualDeviceLock) {
-            if (mActivityPolicyExemptions.remove(componentName)) {
-                for (int i = 0; i < mVirtualDisplays.size(); i++) {
-                    mVirtualDisplays.valueAt(i).getWindowPolicyController()
-                            .removeActivityPolicyExemption(componentName);
-                }
+        final int displayId = exemption.getDisplayId();
+        if (exemption.getComponentName() == null || displayId != Display.INVALID_DISPLAY) {
+            if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+                return;
             }
         }
-    }
-
-    @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void addActivityPolicyExemptionForDisplay(
-            int displayId, @NonNull ComponentName componentName) {
-        super.addActivityPolicyExemptionForDisplay_enforcePermission();
-        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
-            return;
-        }
         synchronized (mVirtualDeviceLock) {
-            checkDisplayOwnedByVirtualDeviceLocked(displayId);
-            mVirtualDisplays.get(displayId).getWindowPolicyController()
-                    .addActivityPolicyExemption(componentName);
-        }
-    }
-
-    @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void removeActivityPolicyExemptionForDisplay(
-            int displayId, @NonNull ComponentName componentName) {
-        super.removeActivityPolicyExemptionForDisplay_enforcePermission();
-        if (!android.companion.virtualdevice.flags.Flags.activityControlApi()) {
-            return;
-        }
-        synchronized (mVirtualDeviceLock) {
-            checkDisplayOwnedByVirtualDeviceLocked(displayId);
-            mVirtualDisplays.get(displayId).getWindowPolicyController()
-                    .removeActivityPolicyExemption(componentName);
+            if (displayId != Display.INVALID_DISPLAY) {
+                checkDisplayOwnedByVirtualDeviceLocked(displayId);
+                if (exemption.getComponentName() != null) {
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .removeActivityPolicyExemption(exemption.getComponentName());
+                } else if (exemption.getPackageName() != null) {
+                    mVirtualDisplays.get(displayId).getWindowPolicyController()
+                            .removeActivityPolicyExemption(exemption.getPackageName());
+                }
+            } else {
+                if (exemption.getComponentName() != null
+                        && mActivityPolicyExemptions.remove(exemption.getComponentName())) {
+                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
+                                .removeActivityPolicyExemption(exemption.getComponentName());
+                    }
+                } else if (exemption.getPackageName() != null
+                        && mActivityPolicyPackageExemptions.remove(exemption.getPackageName())) {
+                    for (int i = 0; i < mVirtualDisplays.size(); i++) {
+                        mVirtualDisplays.valueAt(i).getWindowPolicyController()
+                                .removeActivityPolicyExemption(exemption.getPackageName());
+                    }
+                }
+            }
         }
     }
 
@@ -728,6 +827,7 @@
                 synchronized (mVirtualDeviceLock) {
                     if (getDevicePolicy(policyType) != devicePolicy) {
                         mActivityPolicyExemptions.clear();
+                        mActivityPolicyPackageExemptions.clear();
                     }
                     mDevicePolicies.put(policyType, devicePolicy);
                     for (int i = 0; i < mVirtualDisplays.size(); i++) {
@@ -744,7 +844,7 @@
                     }
                 }
                 break;
-            case POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR:
+            case POLICY_TYPE_BLOCKED_ACTIVITY:
                 if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
                     synchronized (mVirtualDeviceLock) {
                         mDevicePolicies.put(policyType, devicePolicy);
@@ -1254,10 +1354,6 @@
     // as the virtual display doesn't have any focused windows. Hence, call this for
     // associating any input device to the source display if the input device emits any key events.
     private int getTargetDisplayIdForInput(int displayId) {
-        if (!Flags.interactiveScreenMirror()) {
-            return displayId;
-        }
-
         DisplayManagerInternal displayManager = LocalServices.getService(
                 DisplayManagerInternal.class);
         int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId);
@@ -1279,7 +1375,7 @@
                 Flags.vdmCustomHome() ? mParams.getHomeComponent() : null;
 
         if (mActivityListenerAdapter == null) {
-            mActivityListenerAdapter = createListenerAdapter();
+            mActivityListenerAdapter = new GwpcActivityListener();
         }
 
         final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
@@ -1289,14 +1385,12 @@
                 getAllowedUserHandles(),
                 activityLaunchAllowedByDefault,
                 mActivityPolicyExemptions,
+                mActivityPolicyPackageExemptions,
                 crossTaskNavigationAllowedByDefault,
                 /* crossTaskNavigationExemptions= */crossTaskNavigationAllowedByDefault
                         ? mParams.getBlockedCrossTaskNavigations()
                         : mParams.getAllowedCrossTaskNavigations(),
                 mActivityListenerAdapter,
-                this::onActivityBlocked,
-                this::onSecureWindowShown,
-                this::shouldInterceptIntent,
                 displayCategories,
                 showTasksInHostDeviceRecents,
                 homeComponent);
@@ -1313,9 +1407,9 @@
         int displayId;
         displayId = mDisplayManagerInternal.createVirtualDisplay(virtualDisplayConfig, callback,
                 this, gwpc, packageName);
-        gwpc.setDisplayId(displayId, /* isMirrorDisplay= */ Flags.interactiveScreenMirror()
-                && mDisplayManagerInternal.getDisplayIdToMirror(displayId)
-                != Display.INVALID_DISPLAY);
+        boolean isMirrorDisplay =
+                mDisplayManagerInternal.getDisplayIdToMirror(displayId) != Display.INVALID_DISPLAY;
+        gwpc.setDisplayId(displayId, isMirrorDisplay);
 
         boolean showPointer;
         synchronized (mVirtualDeviceLock) {
@@ -1366,29 +1460,6 @@
         }
     }
 
-    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
-    private void onActivityBlocked(int displayId, ActivityInfo activityInfo,
-            IntentSender intentSender) {
-        Intent intent = BlockedAppStreamingActivity.createIntent(activityInfo, getDisplayName());
-        if (shouldShowBlockedActivityDialog(
-                activityInfo.getComponentName(), intent.getComponent())) {
-            mContext.startActivityAsUser(
-                    intent.addFlags(
-                            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK),
-                    ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
-                    UserHandle.SYSTEM);
-        }
-
-        if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
-            mActivityListenerAdapter.onActivityLaunchBlocked(
-                    displayId,
-                    activityInfo.getComponentName(),
-                    UserHandle.getUserHandleForUid(
-                            activityInfo.applicationInfo.uid).getIdentifier(),
-                    intentSender);
-        }
-    }
-
     private boolean shouldShowBlockedActivityDialog(ComponentName blockedComponent,
             ComponentName blockedAppStreamingActivityComponent) {
         if (Objects.equals(blockedComponent, blockedAppStreamingActivityComponent)) {
@@ -1400,28 +1471,7 @@
             return true;
         }
         // Do not show the dialog if disabled by policy.
-        return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY_BEHAVIOR) == DEVICE_POLICY_DEFAULT;
-    }
-
-    private void onSecureWindowShown(int displayId, int uid) {
-        synchronized (mVirtualDeviceLock) {
-            if (!mVirtualDisplays.contains(displayId)) {
-                return;
-            }
-        }
-
-        // If a virtual display isn't secure, the screen can't be captured. Show a warning toast
-        // if the secure window is shown on a non-secure virtual display.
-        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
-        Display display = displayManager.getDisplay(displayId);
-        if ((display.getFlags() & Display.FLAG_SECURE) == 0) {
-            showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
-                    Toast.LENGTH_LONG, mContext.getMainLooper());
-
-            Counter.logIncrementWithUid(
-                    "virtual_devices.value_secure_window_blocked_count",
-                    mAttributionSource.getUid());
-        }
+        return getDevicePolicy(POLICY_TYPE_BLOCKED_ACTIVITY) == DEVICE_POLICY_DEFAULT;
     }
 
     private ArraySet<UserHandle> getAllowedUserHandles() {
@@ -1610,40 +1660,6 @@
         }
     }
 
-    /**
-     * Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
-     * the intent matches any filter notifying the DisplayPolicyController to abort the
-     * activity launch to be replaced by the interception.
-     */
-    private boolean shouldInterceptIntent(Intent intent) {
-        synchronized (mVirtualDeviceLock) {
-            boolean hasInterceptedIntent = false;
-            for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
-                IntentFilter intentFilter = interceptor.getValue();
-                // Explicitly match the actions because the intent filter will match any intent
-                // without an explicit action. If the intent has no action, then require that there
-                // are no actions specified in the filter either.
-                boolean explicitActionMatch =
-                        intent.getAction() != null || intentFilter.countActions() == 0;
-                if (explicitActionMatch && intentFilter.match(
-                        intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
-                        intent.getCategories(), TAG) >= 0) {
-                    try {
-                        // For privacy reasons, only returning the intents action and data. Any
-                        // other required field will require a review.
-                        IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
-                            .onIntentIntercepted(new Intent(intent.getAction(), intent.getData()));
-                        hasInterceptedIntent = true;
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e);
-                    }
-                }
-            }
-
-            return hasInterceptedIntent;
-        }
-    }
-
     interface PendingTrampolineCallback {
         /**
          * Called when the callback should start waiting for the given pending trampoline.
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
index b0bacfd..fed153f 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceLog.java
@@ -48,9 +48,6 @@
     void logCreated(int deviceId, int ownerUid) {
         final long token = Binder.clearCallingIdentity();
         try {
-            if (!Flags.dumpHistory()) {
-                return;
-            }
             addEntry(new LogEntry(TYPE_CREATED, deviceId, System.currentTimeMillis(), ownerUid));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -60,9 +57,6 @@
     void logClosed(int deviceId, int ownerUid) {
         final long token = Binder.clearCallingIdentity();
         try {
-            if (!Flags.dumpHistory()) {
-                return;
-            }
             addEntry(new LogEntry(TYPE_CLOSED, deviceId, System.currentTimeMillis(), ownerUid));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -79,9 +73,6 @@
     void dump(PrintWriter pw) {
         final long token = Binder.clearCallingIdentity();
         try {
-            if (!Flags.dumpHistory()) {
-                return;
-            }
             pw.println("VirtualDevice Log:");
             UidToPackageNameCache packageNameCache = new UidToPackageNameCache(
                     mContext.getPackageManager());
diff --git a/services/companion/java/com/android/server/companion/virtual/flags.aconfig b/services/companion/java/com/android/server/companion/virtual/flags.aconfig
deleted file mode 100644
index 616f5d0..0000000
--- a/services/companion/java/com/android/server/companion/virtual/flags.aconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-# OLD PACKAGE, DO NOT USE: Prefer `flags.aconfig` in core/java/android/companion/virtual
-# (or other custom files) to define your flags
-package: "com.android.server.companion.virtual"
-container: "system"
-
-flag {
-  name: "dump_history"
-  namespace: "virtual_devices"
-  description: "This flag controls if a history of virtual devices is shown in dumpsys virtualdevices"
-  bug: "293114719"
-}
\ No newline at end of file
diff --git a/services/contentcapture/java/com/android/server/contentprotection/OWNERS b/services/contentcapture/java/com/android/server/contentprotection/OWNERS
new file mode 100644
index 0000000..3d09da3
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentprotection/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1040349
+
+include /core/java/android/view/contentprotection/OWNERS
+
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 89d7961..1b5b7e8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -233,6 +233,7 @@
         "stats_flags_lib",
         "core_os_flags_lib",
         "connectivity_flags_lib",
+        "device_config_service_flags_java",
         "dreams_flags_lib",
         "aconfig_new_storage_flags_lib",
         "powerstats_flags_lib",
diff --git a/services/core/java/com/android/server/AlarmManagerInternal.java b/services/core/java/com/android/server/AlarmManagerInternal.java
index b7f2b8d..191137a 100644
--- a/services/core/java/com/android/server/AlarmManagerInternal.java
+++ b/services/core/java/com/android/server/AlarmManagerInternal.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.CurrentTimeMillisLong;
+import android.annotation.UserIdInt;
 import android.app.PendingIntent;
 
 import com.android.server.SystemClockTime.TimeConfidence;
@@ -36,6 +37,16 @@
     /** Returns true if AlarmManager is delaying alarms due to device idle. */
     boolean isIdling();
 
+    /**
+     * Returns the time at which the next alarm for the given user is going to trigger, or 0 if
+     * there is none.
+     *
+     * <p>This value is UTC wall clock time in milliseconds, as returned by
+     * {@link System#currentTimeMillis()} for example.
+     * @see android.app.AlarmManager.AlarmClockInfo#getTriggerTime()
+     */
+    long getNextAlarmTriggerTimeForUser(@UserIdInt int userId);
+
     public void removeAlarmsForUid(int uid);
 
     public void registerInFlightListener(InFlightListener callback);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2de4482..1470e9a 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -23,6 +23,7 @@
 import static com.android.server.health.Utils.copyV1Battery;
 
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
@@ -67,6 +68,7 @@
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.health.HealthServiceWrapper;
@@ -207,18 +209,18 @@
     private final CopyOnWriteArraySet<BatteryManagerInternal.ChargingPolicyChangeListener>
             mChargingPolicyChangeListeners = new CopyOnWriteArraySet<>();
 
-    private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic()
+    private static final Bundle BATTERY_CHANGED_OPTIONS = BroadcastOptions.makeBasic()
             .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
             .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
             .toBundle();
     /** Used for both connected/disconnected, so match using key */
-    private Bundle mPowerOptions = BroadcastOptions.makeBasic()
+    private static final Bundle POWER_OPTIONS = BroadcastOptions.makeBasic()
             .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
             .setDeliveryGroupMatchingKey("android", Intent.ACTION_POWER_CONNECTED)
             .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
             .toBundle();
     /** Used for both low/okay, so match using key */
-    private Bundle mBatteryOptions = BroadcastOptions.makeBasic()
+    private static final Bundle BATTERY_OPTIONS = BroadcastOptions.makeBasic()
             .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
             .setDeliveryGroupMatchingKey("android", Intent.ACTION_BATTERY_OKAY)
             .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
@@ -226,11 +228,60 @@
 
     private MetricsLogger mMetricsLogger;
 
+    private static final int MSG_BROADCAST_BATTERY_CHANGED = 1;
+    private static final int MSG_BROADCAST_POWER_CONNECTION_CHANGED = 2;
+    private static final int MSG_BROADCAST_BATTERY_LOW_OKAY = 3;
+
+    private final Handler.Callback mLocalCallback = msg -> {
+        switch (msg.what) {
+            case MSG_BROADCAST_BATTERY_CHANGED: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final Context context;
+                final Intent intent;
+                try {
+                    context = (Context) args.arg1;
+                    intent = (Intent) args.arg2;
+                } finally {
+                    args.recycle();
+                }
+                broadcastBatteryChangedIntent(context, intent, BATTERY_CHANGED_OPTIONS);
+                return true;
+            }
+            case MSG_BROADCAST_POWER_CONNECTION_CHANGED: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final Context context;
+                final Intent intent;
+                try {
+                    context = (Context) args.arg1;
+                    intent = (Intent) args.arg2;
+                } finally {
+                    args.recycle();
+                }
+                sendBroadcastToAllUsers(context, intent, POWER_OPTIONS);
+                return true;
+            }
+            case MSG_BROADCAST_BATTERY_LOW_OKAY: {
+                final SomeArgs args = (SomeArgs) msg.obj;
+                final Context context;
+                final Intent intent;
+                try {
+                    context = (Context) args.arg1;
+                    intent = (Intent) args.arg2;
+                } finally {
+                    args.recycle();
+                }
+                sendBroadcastToAllUsers(context, intent, BATTERY_OPTIONS);
+                return true;
+            }
+        }
+        return false;
+    };
+
     public BatteryService(Context context) {
         super(context);
 
         mContext = context;
-        mHandler = new Handler(true /*async*/);
+        mHandler = new Handler(mLocalCallback, true /*async*/);
         mLed = new Led(context, getLocalService(LightsManager.class));
         mBatteryStats = BatteryStatsService.getService();
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -660,25 +711,43 @@
                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
-                                mPowerOptions);
-                    }
-                });
+                if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) {
+                    mHandler.removeMessages(MSG_BROADCAST_POWER_CONNECTION_CHANGED);
+                    final SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = mContext;
+                    args.arg2 = statusIntent;
+                    mHandler.obtainMessage(MSG_BROADCAST_POWER_CONNECTION_CHANGED, args)
+                            .sendToTarget();
+                } else {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+                                    POWER_OPTIONS);
+                        }
+                    });
+                }
             }
             else if (mPlugType == 0 && mLastPlugType != 0) {
                 final Intent statusIntent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
-                                mPowerOptions);
-                    }
-                });
+                if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) {
+                    mHandler.removeMessages(MSG_BROADCAST_POWER_CONNECTION_CHANGED);
+                    final SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = mContext;
+                    args.arg2 = statusIntent;
+                    mHandler.obtainMessage(MSG_BROADCAST_POWER_CONNECTION_CHANGED, args)
+                            .sendToTarget();
+                } else {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+                                    POWER_OPTIONS);
+                        }
+                    });
+                }
             }
 
             if (shouldSendBatteryLowLocked()) {
@@ -686,26 +755,44 @@
                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_LOW);
                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
-                                mBatteryOptions);
-                    }
-                });
+                if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) {
+                    mHandler.removeMessages(MSG_BROADCAST_BATTERY_LOW_OKAY);
+                    final SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = mContext;
+                    args.arg2 = statusIntent;
+                    mHandler.obtainMessage(MSG_BROADCAST_BATTERY_LOW_OKAY, args)
+                            .sendToTarget();
+                } else {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+                                    BATTERY_OPTIONS);
+                        }
+                    });
+                }
             } else if (mSentLowBatteryBroadcast &&
                     mHealthInfo.batteryLevel >= mLowBatteryCloseWarningLevel) {
                 mSentLowBatteryBroadcast = false;
                 final Intent statusIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
                 statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
                 statusIntent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
-                                mBatteryOptions);
-                    }
-                });
+                if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) {
+                    mHandler.removeMessages(MSG_BROADCAST_BATTERY_LOW_OKAY);
+                    final SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = mContext;
+                    args.arg2 = statusIntent;
+                    mHandler.obtainMessage(MSG_BROADCAST_BATTERY_LOW_OKAY, args)
+                            .sendToTarget();
+                } else {
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+                                    BATTERY_OPTIONS);
+                        }
+                    });
+                }
             }
 
             // We are doing this after sending the above broadcasts, so anything processing
@@ -777,8 +864,16 @@
                     + ", info:" + mHealthInfo.toString());
         }
 
-        mHandler.post(() -> broadcastBatteryChangedIntent(mContext,
-                intent, mBatteryChangedOptions));
+        if (com.android.server.flags.Flags.consolidateBatteryChangeEvents()) {
+            mHandler.removeMessages(MSG_BROADCAST_BATTERY_CHANGED);
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = mContext;
+            args.arg2 = intent;
+            mHandler.obtainMessage(MSG_BROADCAST_BATTERY_CHANGED, args).sendToTarget();
+        } else {
+            mHandler.post(() -> broadcastBatteryChangedIntent(mContext,
+                    intent, BATTERY_CHANGED_OPTIONS));
+        }
     }
 
     private static void broadcastBatteryChangedIntent(Context context, Intent intent,
@@ -1307,6 +1402,12 @@
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
+    @SuppressLint("AndroidFrameworkRequiresPermission")
+    private static void sendBroadcastToAllUsers(Context context, Intent intent,
+            Bundle options) {
+        context.sendBroadcastAsUser(intent, UserHandle.ALL, null, options);
+    }
+
     private final class Led {
         // must match: config_notificationsBatteryLowBehavior in config.xml
         static final int LOW_BATTERY_BEHAVIOR_DEFAULT = 0;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 361b818..fd512a6 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -94,6 +94,8 @@
 275534 notification_unautogrouped (key|3)
 # when a notification is adjusted via assistant
 27535 notification_adjusted (key|3),(adjustment_type|3),(new_value|3)
+# when a notification cancellation is prevented by the system
+27536 notification_cancel_prevented (key|3)
 
 # ---------------------------
 # Watchdog.java
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a2bbff0..37dddc6 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -10,9 +10,6 @@
 # Zram writeback
 per-file ZramWriteback.java = [email protected], [email protected]
 
-# Userspace reboot
-per-file UserspaceRebootLogger.java = [email protected], [email protected]
-
 # ServiceWatcher
 per-file ServiceWatcher.java = [email protected]
 
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 0815384..fbe593f 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -20,6 +20,8 @@
 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;
@@ -44,6 +46,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
 import android.util.LongArrayQueue;
 import android.util.Slog;
 import android.util.Xml;
@@ -51,7 +54,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
@@ -72,6 +74,7 @@
 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;
@@ -296,7 +299,9 @@
                     this::onSyncRequestNotified);
             setPropertyChangedListenerLocked();
             updateConfigs();
-            registerConnectivityModuleHealthListener();
+            if (!Flags.refactorCrashrecovery()) {
+                registerConnectivityModuleHealthListener();
+            }
         }
     }
 
@@ -309,13 +314,14 @@
      */
     public void registerHealthObserver(PackageHealthObserver observer) {
         synchronized (mLock) {
-            ObserverInternal internalObserver = mAllObservers.get(observer.getName());
+            ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier());
             if (internalObserver != null) {
                 internalObserver.registeredObserver = observer;
             } else {
-                internalObserver = new ObserverInternal(observer.getName(), new ArrayList<>());
+                internalObserver = new ObserverInternal(observer.getUniqueIdentifier(),
+                        new ArrayList<>());
                 internalObserver.registeredObserver = observer;
-                mAllObservers.put(observer.getName(), internalObserver);
+                mAllObservers.put(observer.getUniqueIdentifier(), internalObserver);
                 syncState("added new observer");
             }
         }
@@ -342,12 +348,12 @@
     public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
             long durationMs) {
         if (packageNames.isEmpty()) {
-            Slog.wtf(TAG, "No packages to observe, " + observer.getName());
+            Slog.wtf(TAG, "No packages to observe, " + observer.getUniqueIdentifier());
             return;
         }
         if (durationMs < 1) {
             Slog.wtf(TAG, "Invalid duration " + durationMs + "ms for observer "
-                    + observer.getName() + ". Not observing packages " + packageNames);
+                    + observer.getUniqueIdentifier() + ". Not observing packages " + packageNames);
             durationMs = DEFAULT_OBSERVING_DURATION_MS;
         }
 
@@ -374,14 +380,14 @@
             syncState("observing new packages");
 
             synchronized (mLock) {
-                ObserverInternal oldObserver = mAllObservers.get(observer.getName());
+                ObserverInternal oldObserver = mAllObservers.get(observer.getUniqueIdentifier());
                 if (oldObserver == null) {
-                    Slog.d(TAG, observer.getName() + " started monitoring health "
+                    Slog.d(TAG, observer.getUniqueIdentifier() + " started monitoring health "
                             + "of packages " + packageNames);
-                    mAllObservers.put(observer.getName(),
-                            new ObserverInternal(observer.getName(), packages));
+                    mAllObservers.put(observer.getUniqueIdentifier(),
+                            new ObserverInternal(observer.getUniqueIdentifier(), packages));
                 } else {
-                    Slog.d(TAG, observer.getName() + " added the following "
+                    Slog.d(TAG, observer.getUniqueIdentifier() + " added the following "
                             + "packages to monitor " + packageNames);
                     oldObserver.updatePackagesLocked(packages);
                 }
@@ -405,9 +411,9 @@
     public void unregisterHealthObserver(PackageHealthObserver observer) {
         mLongTaskHandler.post(() -> {
             synchronized (mLock) {
-                mAllObservers.remove(observer.getName());
+                mAllObservers.remove(observer.getUniqueIdentifier());
             }
-            syncState("unregistering observer: " + observer.getName());
+            syncState("unregistering observer: " + observer.getUniqueIdentifier());
         });
     }
 
@@ -781,7 +787,7 @@
          * Identifier for the observer, should not change across device updates otherwise the
          * watchdog may drop observing packages with the old name.
          */
-        String getName();
+        String getUniqueIdentifier();
 
         /**
          * An observer will not be pruned if this is set, even if the observer is not explicitly
@@ -1262,18 +1268,21 @@
 
 
     /** Dump status of every observer in mAllObservers. */
-    public void dump(IndentingPrintWriter pw) {
-        pw.println("Package Watchdog status");
-        pw.increaseIndent();
+    public void dump(PrintWriter pw) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("Package Watchdog status");
+        ipw.increaseIndent();
         synchronized (mLock) {
             for (String observerName : mAllObservers.keySet()) {
-                pw.println("Observer name: " + observerName);
-                pw.increaseIndent();
+                ipw.println("Observer name: " + observerName);
+                ipw.increaseIndent();
                 ObserverInternal observerInternal = mAllObservers.get(observerName);
-                observerInternal.dump(pw);
-                pw.decreaseIndent();
+                observerInternal.dump(ipw);
+                ipw.decreaseIndent();
             }
         }
+        ipw.decreaseIndent();
+        dumpCrashRecoveryEvents(ipw);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index c2cb5e9..cadceb5 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -18,7 +18,7 @@
 
 import static android.provider.DeviceConfig.Properties;
 
-import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -291,13 +291,13 @@
                 Properties properties = new Properties.Builder(namespaceToReset).build();
                 try {
                     if (!DeviceConfig.setProperties(properties)) {
-                        logCriticalInfo(Log.ERROR, "Failed to clear properties under "
+                        logCrashRecoveryEvent(Log.ERROR, "Failed to clear properties under "
                             + namespaceToReset
                             + ". Running `device_config get_sync_disabled_for_tests` will confirm"
                             + " if config-bulk-update is enabled.");
                     }
                 } catch (DeviceConfig.BadConfigException exception) {
-                    logCriticalInfo(Log.WARN, "namespace " + namespaceToReset
+                    logCrashRecoveryEvent(Log.WARN, "namespace " + namespaceToReset
                             + " is already banned, skip reset.");
                 }
             }
@@ -528,7 +528,7 @@
             if (!TextUtils.isEmpty(failedPackage)) {
                 successMsg += " for package " + failedPackage;
             }
-            logCriticalInfo(Log.DEBUG, successMsg);
+            logCrashRecoveryEvent(Log.DEBUG, successMsg);
         } catch (Throwable t) {
             logRescueException(level, failedPackage, t);
         }
@@ -687,7 +687,7 @@
         if (!TextUtils.isEmpty(failedPackageName)) {
             failureMsg += " for package " + failedPackageName;
         }
-        logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
+        logCrashRecoveryEvent(Log.ERROR, failureMsg + ": " + msg);
     }
 
     private static int mapRescueLevelToUserImpact(int rescueLevel) {
@@ -917,7 +917,7 @@
         }
 
         @Override
-        public String getName() {
+        public String getUniqueIdentifier() {
             return NAME;
         }
 
diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java
index 82c2038..dbf144f 100644
--- a/services/core/java/com/android/server/SerialService.java
+++ b/services/core/java/com/android/server/SerialService.java
@@ -56,16 +56,11 @@
         }
     }
 
-    @android.ravenwood.annotation.RavenwoodReplace
     private static String[] getSerialPorts(Context context) {
         return context.getResources().getStringArray(
                 com.android.internal.R.array.config_serialPorts);
     }
 
-    private static String[] getSerialPorts$ravenwood(Context context) {
-        return new String[0];
-    }
-
     public static class Lifecycle extends SystemService {
         private SerialService mService;
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 19279a8..d86bae1 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,6 +224,9 @@
     /** Extended timeout for the system server watchdog for vold#partition operation. */
     private static final int PARTITION_OPERATION_WATCHDOG_TIMEOUT_MS = 3 * 60 * 1000;
 
+    private static final Pattern OBB_FILE_PATH = Pattern.compile(
+            "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/obb/)([^/]+)/([^/]+\\.obb)");
+
     @GuardedBy("mLock")
     private final Set<Integer> mFuseMountedUser = new ArraySet<>();
 
@@ -3144,7 +3147,9 @@
         Objects.requireNonNull(rawPath, "rawPath cannot be null");
         Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
         Objects.requireNonNull(token, "token cannot be null");
-        Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
+        Objects.requireNonNull(obbInfo, "obbInfo cannot be null");
+
+        validateObbInfo(obbInfo, rawPath);
 
         final int callingUid = Binder.getCallingUid();
         final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -3156,6 +3161,34 @@
             Slog.i(TAG, "Send to OBB handler: " + action.toString());
     }
 
+    private void validateObbInfo(ObbInfo obbInfo, String rawPath) {
+        String obbFilePath;
+        try {
+            obbFilePath = new File(rawPath).getCanonicalPath();
+        } catch (IOException ex) {
+            throw new RuntimeException("Failed to resolve path" + rawPath + " : " + ex);
+        }
+
+        Matcher matcher = OBB_FILE_PATH.matcher(obbFilePath);
+
+        if (matcher.matches()) {
+            int userId = UserHandle.getUserId(Binder.getCallingUid());
+            String pathUserId = matcher.group(2);
+            String pathPackageName = matcher.group(3);
+            if ((pathUserId != null && Integer.parseInt(pathUserId) != userId)
+                    || (pathUserId == null && userId != mCurrentUserId)) {
+                throw new SecurityException(
+                        "Path " + obbFilePath + "does not correspond to calling userId " + userId);
+            }
+            if (obbInfo != null && !obbInfo.packageName.equals(pathPackageName)) {
+                throw new SecurityException("Path " + obbFilePath
+                        + " does not contain package name " + pathPackageName);
+            }
+        } else {
+            throw new SecurityException("Invalid path to Obb file : " + obbFilePath);
+        }
+    }
+
     @Override
     public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
         Objects.requireNonNull(rawPath, "rawPath cannot be null");
@@ -3527,7 +3560,8 @@
         // of the calling App
         final long token = Binder.clearCallingIdentity();
         try {
-            Context targetAppContext = mContext.createPackageContext(packageName, 0);
+            Context targetAppContext = mContext.createPackageContextAsUser(packageName,
+                    /* flags= */ 0, UserHandle.of(UserHandle.getUserId(originalUid)));
             Intent intent = new Intent(Intent.ACTION_DEFAULT);
             intent.setClassName(packageName,
                     appInfo.manageSpaceActivityName);
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index e2ab0d9..d80e40c 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -371,6 +371,10 @@
     // exempt from ECM (i.e., they will never be considered "restricted").
     private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedInstallers = new ArraySet<>();
 
+    // A map of UIDs defined by OEMs, mapping from name to value. The UIDs will be registered at the
+    // start of the system which allows OEMs to create and register their system services.
+    @NonNull private final ArrayMap<String, Integer> mOemDefinedUids = new ArrayMap<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -594,6 +598,10 @@
         return mEnhancedConfirmationTrustedInstallers;
     }
 
+    @NonNull
+    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.
@@ -1628,6 +1636,26 @@
                             }
                         }
                     } break;
+                    case "oem-defined-uid": {
+                        final String uidName = parser.getAttributeValue(null, "name");
+                        final String uidValue = parser.getAttributeValue(null, "uid");
+                        if (TextUtils.isEmpty(uidName)) {
+                            Slog.w(TAG, "<" + name + "> without valid uid name in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else if (TextUtils.isEmpty(uidValue)) {
+                            Slog.w(TAG, "<" + name + "> without valid uid value in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            try {
+                                final int oemDefinedUid = Integer.parseInt(uidValue);
+                                mOemDefinedUids.put(uidName, oemDefinedUid);
+                            } catch (NumberFormatException e) {
+                                Slog.w(TAG, "<" + name + "> with invalid uid value: "
+                                        + uidValue + " in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            }
+                        }
+                    } break;
                     case "enhanced-confirmation-trusted-package": {
                         if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
                             SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index a3b6d80..556fae3 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -53,12 +53,7 @@
             "file_patterns": ["StorageManagerService\\.java"]
         },
         {
-            "name": "FrameworksMockingServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.sensorprivacy"
-                }
-            ],
+            "name": "FrameworksMockingServicesTests_sensorprivacy",
             "file_patterns": ["SensorPrivacyService\\.java"]
         },
         {
@@ -206,6 +201,15 @@
                     "include-filter": "com.android.server.wm.BackgroundActivityStart*"
                 }
             ]
+        },
+        {
+            "name": "CtsOsTestCases",
+            "file_patterns": ["StorageManagerService\\.java"],
+            "options": [
+                {
+                    "include-filter": "android.os.storage.cts.StorageManagerTest"
+                }
+            ]
         }
    ]
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 79e09d7..7442277 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1732,41 +1732,47 @@
                 // In the future, we can remove this logic for every notification here and add a
                 // callback so listeners know when their PhoneStateListener's subId becomes invalid,
                 // but for now we use the simplest fix.
-                if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
+                if (validatePhoneId(phoneId)) {
                     mServiceState[phoneId] = state;
 
-                    for (Record r : mRecords) {
-                        if (VDBG) {
-                            log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
-                                    + " phoneId=" + phoneId + " state=" + state);
-                        }
-                        if (r.matchTelephonyCallbackEvent(
-                                TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
-                                && idMatch(r, subId, phoneId)) {
+                    if (SubscriptionManager.isValidSubscriptionId(subId)) {
 
-                            try {
-                                ServiceState stateToSend;
-                                if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                    stateToSend = new ServiceState(state);
-                                } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                    stateToSend = state.createLocationInfoSanitizedCopy(false);
-                                } else {
-                                    stateToSend = state.createLocationInfoSanitizedCopy(true);
+                        for (Record r : mRecords) {
+                            if (VDBG) {
+                                log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId
+                                        + " phoneId=" + phoneId + " state=" + state);
+                            }
+                            if (r.matchTelephonyCallbackEvent(
+                                    TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
+                                    && idMatch(r, subId, phoneId)) {
+
+                                try {
+                                    ServiceState stateToSend;
+                                    if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+                                        stateToSend = new ServiceState(state);
+                                    } else if(checkCoarseLocationAccess(
+                                            r, Build.VERSION_CODES.Q)) {
+                                        stateToSend = state.createLocationInfoSanitizedCopy(false);
+                                    } else {
+                                        stateToSend = state.createLocationInfoSanitizedCopy(true);
+                                    }
+                                    if (DBG) {
+                                        log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+                                                + " subId=" + subId + " phoneId=" + phoneId
+                                                + " state=" + stateToSend);
+                                    }
+                                    r.callback.onServiceStateChanged(stateToSend);
+                                } catch (RemoteException ex) {
+                                    mRemoveList.add(r.binder);
                                 }
-                                if (DBG) {
-                                    log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
-                                            + " subId=" + subId + " phoneId=" + phoneId
-                                            + " state=" + stateToSend);
-                                }
-                                r.callback.onServiceStateChanged(stateToSend);
-                            } catch (RemoteException ex) {
-                                mRemoveList.add(r.binder);
                             }
                         }
                     }
+                    else {
+                        log("notifyServiceStateForSubscriber: INVALID subId=" +subId);
+                    }
                 } else {
-                    log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
-                            + " or subId=" + subId);
+                    log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
                 }
                 handleRemoveListLocked();
             }
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
deleted file mode 100644
index 89327b5..0000000
--- a/services/core/java/com/android/server/UserspaceRebootLogger.java
+++ /dev/null
@@ -1,168 +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.server;
-
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
-import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED;
-
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.util.FrameworkStatsLog;
-
-import java.util.concurrent.Executor;
-
-/**
- * Utility class to help abstract logging {@code UserspaceRebootReported} atom.
- */
-public final class UserspaceRebootLogger {
-
-    private static final String TAG = "UserspaceRebootLogger";
-
-    private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY =
-            "persist.sys.userspace_reboot.log.should_log";
-    private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY =
-            "sys.userspace_reboot.log.last_started";
-    private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
-            "sys.userspace_reboot.log.last_finished";
-    private static final String LAST_BOOT_REASON_PROPERTY = "sys.boot.reason.last";
-
-    private UserspaceRebootLogger() {}
-
-    /**
-     * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
-     * logged on the next successful boot.
-     *
-     * <p>This call should only be made on devices supporting userspace reboot.
-     */
-    public static void noteUserspaceRebootWasRequested() {
-        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
-            Slog.wtf(TAG, "noteUserspaceRebootWasRequested: Userspace reboot is not supported.");
-            return;
-        }
-
-        SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
-        SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
-                String.valueOf(SystemClock.elapsedRealtime()));
-    }
-
-    /**
-     * Updates internal state on boot after successful userspace reboot.
-     *
-     * <p>Should be called right before framework sets {@code sys.boot_completed} property.
-     *
-     * <p>This call should only be made on devices supporting userspace reboot.
-     */
-    public static void noteUserspaceRebootSuccess() {
-        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
-            Slog.wtf(TAG, "noteUserspaceRebootSuccess: Userspace reboot is not supported.");
-            return;
-        }
-
-        SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
-                String.valueOf(SystemClock.elapsedRealtime()));
-    }
-
-    /**
-     * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
-     *
-     * <p>On devices that do not support userspace reboot this method will always return {@code
-     * false}.
-     */
-    public static boolean shouldLogUserspaceRebootEvent() {
-        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
-            return false;
-        }
-
-        return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
-    }
-
-    /**
-     * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}.
-     *
-     * <p>Should be called in the end of {@link
-     * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have
-     * tried to proactivelly unlock storage of the primary user.
-     *
-     * <p>This call should only be made on devices supporting userspace reboot.
-     */
-    public static void logEventAsync(boolean userUnlocked, Executor executor) {
-        if (!PowerManager.isRebootingUserspaceSupportedImpl()) {
-            Slog.wtf(TAG, "logEventAsync: Userspace reboot is not supported.");
-            return;
-        }
-
-        final int outcome = computeOutcome();
-        final long durationMillis;
-        if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
-            durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0)
-                    - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0);
-        } else {
-            durationMillis = 0;
-        }
-        final int encryptionState =
-                userUnlocked
-                    ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED
-                    : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
-        executor.execute(
-                () -> {
-                    Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome
-                            + " durationMillis: " + durationMillis + " encryptionState: "
-                            + encryptionState + " }");
-                    FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome,
-                            durationMillis, encryptionState);
-                    SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "");
-                });
-    }
-
-    private static int computeOutcome() {
-        if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
-        }
-        String reason = TextUtils.emptyIfNull(SystemProperties.get(LAST_BOOT_REASON_PROPERTY, ""));
-        if (reason.startsWith("reboot,")) {
-            reason = reason.substring("reboot".length());
-        }
-        if (reason.startsWith("userspace_failed,watchdog_fork")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-        }
-        if (reason.startsWith("userspace_failed,shutdown_aborted")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
-        }
-        if (reason.startsWith("mount_userdata_failed")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-        }
-        if (reason.startsWith("userspace_failed,init_user0")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-        }
-        if (reason.startsWith("userspace_failed,enablefilecrypto")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
-        }
-        if (reason.startsWith("userspace_failed,watchdog_triggered")) {
-            return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
-        }
-        return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
-    }
-}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 2af5316..765afef 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1773,7 +1773,8 @@
                         // Create a Session for the target user and pass in the bundle
                         completeCloningAccount(response, result, account, toAccounts, userFrom);
                     } else {
-                        super.onResult(result);
+                        // Bundle format is not defined.
+                        super.onResultSkipSanitization(result);
                     }
                 }
             }.bind();
@@ -1860,7 +1861,8 @@
                     // account to avoid retries?
                     // TODO: what we do with the visibility?
 
-                    super.onResult(result);
+                    // Bundle format is not defined.
+                    super.onResultSkipSanitization(result);
                 }
 
                 @Override
@@ -2106,6 +2108,7 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
+            result = sanitizeBundle(result);
             IAccountManagerResponse response = getResponseAndClose();
             if (response != null) {
                 try {
@@ -2456,6 +2459,7 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
+            result = sanitizeBundle(result);
             if (result != null && result.containsKey(AccountManager.KEY_BOOLEAN_RESULT)
                     && !result.containsKey(AccountManager.KEY_INTENT)) {
                 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
@@ -2970,6 +2974,7 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
+                    result = sanitizeBundle(result);
                     if (result != null) {
                         String label = result.getString(AccountManager.KEY_AUTH_TOKEN_LABEL);
                         Bundle bundle = new Bundle();
@@ -3147,6 +3152,7 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
+                    result = sanitizeBundle(result);
                     if (result != null) {
                         if (result.containsKey(AccountManager.KEY_AUTH_TOKEN_LABEL)) {
                             Intent intent = newGrantCredentialsPermissionIntent(
@@ -3618,6 +3624,12 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
+            Bundle sessionBundle = null;
+            if (result != null) {
+                // Session bundle will be removed from result.
+                sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
+            }
+            result = sanitizeBundle(result);
             mNumResults++;
             Intent intent = null;
             if (result != null) {
@@ -3679,7 +3691,6 @@
             // bundle contains data necessary for finishing the session
             // later. The session bundle will be encrypted here and
             // decrypted later when trying to finish the session.
-            Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
             if (sessionBundle != null) {
                 String accountType = sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE);
                 if (TextUtils.isEmpty(accountType)
@@ -4067,6 +4078,7 @@
                 @Override
                 public void onResult(Bundle result) {
                     Bundle.setDefusable(result, true);
+                    result = sanitizeBundle(result);
                     IAccountManagerResponse response = getResponseAndClose();
                     if (response == null) {
                         return;
@@ -4380,6 +4392,7 @@
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
+            result = sanitizeBundle(result);
             mNumResults++;
             if (result == null) {
                 onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, "null bundle");
@@ -4936,6 +4949,68 @@
                 callback, resultReceiver);
     }
 
+
+    // All keys for Strings passed from AbstractAccountAuthenticator using Bundle.
+    private static final String[] sStringBundleKeys = new String[] {
+        AccountManager.KEY_ACCOUNT_NAME,
+        AccountManager.KEY_ACCOUNT_TYPE,
+        AccountManager.KEY_AUTHTOKEN,
+        AccountManager.KEY_AUTH_TOKEN_LABEL,
+        AccountManager.KEY_ERROR_MESSAGE,
+        AccountManager.KEY_PASSWORD,
+        AccountManager.KEY_ACCOUNT_STATUS_TOKEN};
+
+    /**
+     * Keep only documented fields in a Bundle received from AbstractAccountAuthenticator.
+     */
+    protected static Bundle sanitizeBundle(Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        Bundle sanitizedBundle = new Bundle();
+        Bundle.setDefusable(sanitizedBundle, true);
+        int updatedKeysCount = 0;
+        for (String stringKey : sStringBundleKeys) {
+            if (bundle.containsKey(stringKey)) {
+                String value = bundle.getString(stringKey);
+                sanitizedBundle.putString(stringKey, value);
+                updatedKeysCount++;
+            }
+        }
+        String key = AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY;
+        if (bundle.containsKey(key)) {
+            long expiryMillis = bundle.getLong(key, 0L);
+            sanitizedBundle.putLong(key, expiryMillis);
+            updatedKeysCount++;
+        }
+        key = AccountManager.KEY_BOOLEAN_RESULT;
+        if (bundle.containsKey(key)) {
+            boolean booleanResult = bundle.getBoolean(key, false);
+            sanitizedBundle.putBoolean(key, booleanResult);
+            updatedKeysCount++;
+        }
+        key = AccountManager.KEY_ERROR_CODE;
+        if (bundle.containsKey(key)) {
+            int errorCode = bundle.getInt(key, 0);
+            sanitizedBundle.putInt(key, errorCode);
+            updatedKeysCount++;
+        }
+        key = AccountManager.KEY_INTENT;
+        if (bundle.containsKey(key)) {
+            Intent intent = bundle.getParcelable(key, Intent.class);
+            sanitizedBundle.putParcelable(key, intent);
+            updatedKeysCount++;
+        }
+        if (bundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE)) {
+            // The field is not copied in sanitized bundle.
+            updatedKeysCount++;
+        }
+        if (updatedKeysCount != bundle.size()) {
+            Log.w(TAG, "Size mismatch after sanitizeBundle call.");
+        }
+        return sanitizedBundle;
+    }
+
     private abstract class Session extends IAccountAuthenticatorResponse.Stub
             implements IBinder.DeathRecipient, ServiceConnection {
         private final Object mSessionLock = new Object();
@@ -5226,10 +5301,15 @@
                 }
             }
         }
-
         @Override
         public void onResult(Bundle result) {
             Bundle.setDefusable(result, true);
+            result = sanitizeBundle(result);
+            onResultSkipSanitization(result);
+        }
+
+        public void onResultSkipSanitization(Bundle result) {
+            Bundle.setDefusable(result, true);
             mNumResults++;
             Intent intent = null;
             if (result != null) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2e1416b..d4f729c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -6962,7 +6962,8 @@
     }
 
     private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses,
-            boolean evenPersistent, boolean doit, ArrayMap<ComponentName, ServiceRecord> services) {
+            boolean evenPersistent, boolean doit, int minOomAdj,
+            ArrayMap<ComponentName, ServiceRecord> services) {
         boolean didSomething = false;
         for (int i = services.size() - 1; i >= 0; i--) {
             ServiceRecord service = services.valueAt(i);
@@ -6970,6 +6971,11 @@
                     || (service.packageName.equals(packageName)
                         && (filterByClasses == null
                             || filterByClasses.contains(service.name.getClassName())));
+            if (service.app != null && service.app.mState.getCurAdj() < minOomAdj) {
+                Slog.i(TAG, "Skip force stopping service " + service
+                            + ": below minimum oom adj level");
+                continue;
+            }
             if (sameComponent
                     && (service.app == null || evenPersistent || !service.app.isPersistent())) {
                 if (!doit) {
@@ -6993,6 +6999,12 @@
 
     boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
             int userId, boolean evenPersistent, boolean fullStop, boolean doit) {
+        return bringDownDisabledPackageServicesLocked(packageName, filterByClasses, userId,
+                evenPersistent, fullStop, doit, ProcessList.INVALID_ADJ);
+    }
+
+    boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses,
+            int userId, boolean evenPersistent, boolean fullStop, boolean doit, int minOomAdj) {
         boolean didSomething = false;
 
         if (mTmpCollectionResults != null) {
@@ -7002,7 +7014,8 @@
         if (userId == UserHandle.USER_ALL) {
             for (int i = mServiceMap.size() - 1; i >= 0; i--) {
                 didSomething |= collectPackageServicesLocked(packageName, filterByClasses,
-                        evenPersistent, doit, mServiceMap.valueAt(i).mServicesByInstanceName);
+                        evenPersistent, doit, minOomAdj,
+                        mServiceMap.valueAt(i).mServicesByInstanceName);
                 if (!doit && didSomething) {
                     return true;
                 }
@@ -7015,7 +7028,7 @@
             if (smap != null) {
                 ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByInstanceName;
                 didSomething = collectPackageServicesLocked(packageName, filterByClasses,
-                        evenPersistent, doit, items);
+                        evenPersistent, doit, minOomAdj, items);
             }
             if (doit && filterByClasses == null) {
                 forceStopPackageLocked(packageName, userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9d8f337..3c57476 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -50,7 +50,6 @@
 import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_CREATED;
 import static android.app.ActivityManagerInternal.MEDIA_PROJECTION_TOKEN_EVENT_DESTROYED;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BACKUP;
-import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_SHELL;
@@ -101,15 +100,12 @@
 import static android.os.Process.BLUETOOTH_UID;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.INVALID_UID;
-import static android.os.Process.NETWORK_STACK_UID;
-import static android.os.Process.NFC_UID;
 import static android.os.Process.PHONE_UID;
 import static android.os.Process.PROC_OUT_LONG;
 import static android.os.Process.PROC_SPACE_TERM;
 import static android.os.Process.ROOT_UID;
 import static android.os.Process.SCHED_FIFO;
 import static android.os.Process.SCHED_RESET_ON_FORK;
-import static android.os.Process.SE_UID;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SIGNAL_USR1;
 import static android.os.Process.SYSTEM_UID;
@@ -145,8 +141,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALLOWLISTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
@@ -155,7 +149,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
@@ -265,10 +258,7 @@
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetManagerInternal;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledSince;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
@@ -309,16 +299,12 @@
 import android.content.pm.UserInfo;
 import android.content.pm.UserProperties;
 import android.content.pm.VersionedPackage;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
-import android.media.audiofx.AudioEffect;
-import android.net.ConnectivityManager;
-import android.net.Proxy;
 import android.net.Uri;
 import android.os.AppZygote;
 import android.os.BatteryStats;
@@ -372,12 +358,9 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.server.ServerProtoEnums;
-import android.sysprop.InitProperties;
 import android.system.Os;
 import android.system.OsConstants;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
@@ -387,7 +370,6 @@
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Pair;
-import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -436,11 +418,11 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.BootReceiver;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.DisplayThread;
-import com.android.server.IntentResolver;
 import com.android.server.IoThread;
 import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
@@ -451,9 +433,7 @@
 import com.android.server.SystemService;
 import com.android.server.SystemServiceManager;
 import com.android.server.ThreadPriorityBooster;
-import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
-import com.android.server.am.ComponentAliasResolver.Resolution;
 import com.android.server.am.LowMemDetector.MemFactor;
 import com.android.server.appop.AppOpsService;
 import com.android.server.compat.PlatformCompat;
@@ -464,14 +444,12 @@
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.net.NetworkManagementInternal;
 import com.android.server.os.NativeTombstoneManager;
-import com.android.server.pm.Computer;
 import com.android.server.pm.Installer;
 import com.android.server.pm.SaferIntentUtils;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.SELinuxUtil;
-import com.android.server.pm.snapshot.PackageDataSnapshot;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.sdksandbox.SdkSandboxManagerLocal;
 import com.android.server.stats.pull.StatsPullAtomService;
@@ -514,7 +492,6 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
@@ -539,7 +516,6 @@
 
     static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM;
     static final String TAG_BACKUP = TAG + POSTFIX_BACKUP;
-    private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
@@ -567,9 +543,6 @@
 
     static final String SYSTEM_USER_HOME_NEEDED = "ro.system_user_home_needed";
 
-    // Maximum number of receivers an app can register.
-    private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
-
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;
@@ -654,15 +627,6 @@
     static final String EXTRA_BUGREPORT_NONCE = "android.intent.extra.BUGREPORT_NONCE";
     static final String EXTRA_EXTRA_ATTACHMENT_URI =
             "android.intent.extra.EXTRA_ATTACHMENT_URI";
-    /**
-     * It is now required for apps to explicitly set either
-     * {@link android.content.Context#RECEIVER_EXPORTED} or
-     * {@link android.content.Context#RECEIVER_NOT_EXPORTED} when registering a receiver for an
-     * unprotected broadcast in code.
-     */
-    @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
-    private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
 
     /**
      * The maximum number of bytes that {@link #setProcessStateSummary} accepts.
@@ -739,11 +703,9 @@
     // so that dispatch of foreground broadcasts gets precedence.
     private BroadcastQueue mBroadcastQueue;
 
-    @GuardedBy("this")
-    BroadcastStats mLastBroadcastStats;
-
-    @GuardedBy("this")
-    BroadcastStats mCurBroadcastStats;
+    // TODO: Add a consistent way of accessing the methods within this class. Currently, some
+    // methods require access while holding a lock, while others do not.
+    BroadcastController mBroadcastController;
 
     TraceErrorLogger mTraceErrorLogger;
 
@@ -765,6 +727,7 @@
 
     final AppErrors mAppErrors;
     final PackageWatchdog mPackageWatchdog;
+    final CrashRecoveryHelper mCrashRecoveryHelper;
 
     @GuardedBy("mDeliveryGroupPolicyIgnoredActions")
     private final ArraySet<String> mDeliveryGroupPolicyIgnoredActions = new ArraySet();
@@ -862,12 +825,6 @@
     };
 
     /**
-     * Broadcast actions that will always be deliverable to unlaunched/background apps
-     */
-    @GuardedBy("this")
-    private ArraySet<String> mBackgroundLaunchBroadcasts;
-
-    /**
      * When an app has restrictions on the other apps that can have associations with it,
      * it appears here with a set of the allowed apps and also track debuggability of the app.
      */
@@ -1135,97 +1092,6 @@
     private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>();
     private static final int MAX_DUP_SUPPRESSED_STACKS = 5000;
 
-    /**
-     * Keeps track of all IIntentReceivers that have been registered for broadcasts.
-     * Hash keys are the receiver IBinder, hash value is a ReceiverList.
-     */
-    @GuardedBy("this")
-    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
-
-    /**
-     * Resolver for broadcast intents to registered receivers.
-     * Holds BroadcastFilter (subclass of IntentFilter).
-     */
-    final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
-            = new IntentResolver<BroadcastFilter, BroadcastFilter>() {
-        @Override
-        protected boolean allowFilterResult(
-                BroadcastFilter filter, List<BroadcastFilter> dest) {
-            IBinder target = filter.receiverList.receiver.asBinder();
-            for (int i = dest.size() - 1; i >= 0; i--) {
-                if (dest.get(i).receiverList.receiver.asBinder() == target) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
-                int match, int userId, long customFlags) {
-            if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
-                    || userId == filter.owningUserId) {
-                return super.newResult(computer, filter, match, userId, customFlags);
-            }
-            return null;
-        }
-
-        @Override
-        protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
-            return input;
-        }
-
-        @Override
-        protected BroadcastFilter[] newArray(int size) {
-            return new BroadcastFilter[size];
-        }
-
-        @Override
-        protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
-            return packageName.equals(filter.packageName);
-        }
-    };
-
-    /**
-     * State of all active sticky broadcasts per user.  Keys are the action of the
-     * sticky Intent, values are an ArrayList of all broadcasted intents with
-     * that action (which should usually be one).  The SparseArray is keyed
-     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
-     * for stickies that are sent to all users.
-     */
-    @GuardedBy("mStickyBroadcasts")
-    final SparseArray<ArrayMap<String, ArrayList<StickyBroadcast>>> mStickyBroadcasts =
-            new SparseArray<>();
-
-    @VisibleForTesting
-    static final class StickyBroadcast {
-        public Intent intent;
-        public boolean deferUntilActive;
-        public int originalCallingUid;
-        /** The snapshot process state of the app who sent this broadcast */
-        public int originalCallingAppProcessState;
-        public String resolvedDataType;
-
-        public static StickyBroadcast create(Intent intent, boolean deferUntilActive,
-                int originalCallingUid, int originalCallingAppProcessState,
-                String resolvedDataType) {
-            final StickyBroadcast b = new StickyBroadcast();
-            b.intent = intent;
-            b.deferUntilActive = deferUntilActive;
-            b.originalCallingUid = originalCallingUid;
-            b.originalCallingAppProcessState = originalCallingAppProcessState;
-            b.resolvedDataType = resolvedDataType;
-            return b;
-        }
-
-        @Override
-        public String toString() {
-            return "{intent=" + intent + ", defer=" + deferUntilActive + ", originalCallingUid="
-                    + originalCallingUid + ", originalCallingAppProcessState="
-                    + originalCallingAppProcessState + ", type=" + resolvedDataType + "}";
-        }
-    }
-
     final ActiveServices mServices;
 
     final static class Association {
@@ -1687,7 +1553,7 @@
     // Encapsulates the global setting "hidden_api_blacklist_exemptions"
     final HiddenApiSettings mHiddenApiBlacklist;
 
-    private final PlatformCompat mPlatformCompat;
+    final PlatformCompat mPlatformCompat;
 
     PackageManagerInternal mPackageManagerInt;
     PermissionManagerServiceInternal mPermissionManagerInt;
@@ -2328,10 +2194,12 @@
                 mService.mBatteryStatsService.systemServicesReady();
                 mService.mServices.systemServicesReady();
             } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
-                mService.startBroadcastObservers();
+                mService.mBroadcastController.startBroadcastObservers();
             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 if (!refactorCrashrecovery()) {
                     mService.mPackageWatchdog.onPackagesReady();
+                } else {
+                    mService.mCrashRecoveryHelper.registerConnectivityModuleHealthListener();
                 }
                 mService.scheduleHomeTimeout();
             }
@@ -2360,20 +2228,6 @@
         }
     }
 
-    private void maybeLogUserspaceRebootEvent() {
-        if (!UserspaceRebootLogger.shouldLogUserspaceRebootEvent()) {
-            return;
-        }
-        final int userId = mUserController.getCurrentUserId();
-        if (userId != UserHandle.USER_SYSTEM) {
-            // Only log for user0.
-            return;
-        }
-        // TODO(b/148767783): should we check all profiles under user0?
-        UserspaceRebootLogger.logEventAsync(StorageManager.isCeStorageUnlocked(userId),
-                BackgroundThread.getExecutor());
-    }
-
     /**
      * Encapsulates global settings related to hidden API enforcement behaviour, including tracking
      * the latest value via a content observer.
@@ -2516,6 +2370,7 @@
         mUiContext = null;
         mAppErrors = injector.getAppErrors();
         mPackageWatchdog = null;
+        mCrashRecoveryHelper = null;
         mAppOpsService = mInjector.getAppOpsService(null /* recentAccessesFile */,
             null /* storageFile */, null /* handler */);
         mBatteryStatsService = mInjector.getBatteryStatsService();
@@ -2553,6 +2408,7 @@
         mPendingStartActivityUids = new PendingStartActivityUids();
         mUseFifoUiScheduling = false;
         mBroadcastQueue = injector.getBroadcastQueue(this);
+        mBroadcastController = new BroadcastController(mContext, this, mBroadcastQueue);
         mComponentAliasResolver = new ComponentAliasResolver(this);
     }
 
@@ -2595,9 +2451,11 @@
                 : new OomAdjuster(this, mProcessList, activeUids);
 
         mBroadcastQueue = mInjector.getBroadcastQueue(this);
+        mBroadcastController = new BroadcastController(mContext, this, mBroadcastQueue);
 
         mServices = new ActiveServices(this);
         mCpHelper = new ContentProviderHelper(this, true);
+        mCrashRecoveryHelper = new CrashRecoveryHelper(mUiContext);
         mPackageWatchdog = PackageWatchdog.getInstance(mUiContext);
         mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog);
         mUidObserverController = new UidObserverController(mUiHandler);
@@ -2662,6 +2520,7 @@
 
     void setBroadcastQueueForTest(BroadcastQueue broadcastQueue) {
         mBroadcastQueue = broadcastQueue;
+        mBroadcastController.setBroadcastQueueForTest(broadcastQueue);
     }
 
     BroadcastQueue getBroadcastQueue() {
@@ -2696,18 +2555,6 @@
         mLocalPowerManager = LocalServices.getService(PowerManagerInternal.class);
     }
 
-    private ArraySet<String> getBackgroundLaunchBroadcasts() {
-        if (mBackgroundLaunchBroadcasts == null) {
-            mBackgroundLaunchBroadcasts = SystemConfig.getInstance().getAllowImplicitBroadcasts();
-        }
-        return mBackgroundLaunchBroadcasts;
-    }
-
-    private String getWearRemoteIntentAction() {
-        return mContext.getResources().getString(
-                    com.android.internal.R.string.config_wearRemoteIntentAction);
-    }
-
     /**
      * Ensures that the given package name has an explicit set of allowed associations.
      * If it does not, give it an empty set.
@@ -2778,7 +2625,7 @@
     }
 
     /** Updates allowed associations for app info (specifically, based on debuggability).  */
-    private void updateAssociationForApp(ApplicationInfo appInfo) {
+    void updateAssociationForApp(ApplicationInfo appInfo) {
         ensureAllowedAssociations();
         PackageAssociationInfo pai = mAllowedAssociations.get(appInfo.packageName);
         if (pai != null) {
@@ -3123,10 +2970,6 @@
         }
     }
 
-    CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
-        return mAtmInternal.compatibilityInfoForPackage(ai);
-    }
-
     /**
      * Enforces that the uid that calls a method is not an
      * {@link UserHandle#isIsolated(int) isolated} uid.
@@ -3904,7 +3747,7 @@
         forceStopPackage(packageName, userId, ActivityManager.FLAG_OR_STOPPED, null);
     }
 
-    private void forceStopPackage(final String packageName, int userId, int userRunningFlags,
+    void forceStopPackage(final String packageName, int userId, int userRunningFlags,
             String reason) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -4232,7 +4075,7 @@
         mPackageManagerInt.sendPackageRestartedBroadcast(packageName, uid, flags);
     }
 
-    private void cleanupDisabledPackageComponentsLocked(
+    void cleanupDisabledPackageComponentsLocked(
             String packageName, int userId, String[] changedClasses) {
 
         Set<String> disabledClasses = null;
@@ -4377,6 +4220,16 @@
     }
 
     @GuardedBy("this")
+    final boolean forceStopUserPackagesLocked(int userId, String reasonString,
+            boolean evenImportantServices) {
+        int minOomAdj = evenImportantServices ? ProcessList.INVALID_ADJ
+                : ProcessList.FOREGROUND_APP_ADJ;
+        return forceStopPackageInternalLocked(null, -1, false, false,
+                true, false, false, false, userId, reasonString,
+                ApplicationExitInfo.REASON_USER_STOPPED, minOomAdj);
+    }
+
+    @GuardedBy("this")
     final boolean forceStopPackageLocked(String packageName, int appId,
             boolean callerWillRestart, boolean purgeCache, boolean doit,
             boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
@@ -4385,7 +4238,6 @@
                 : ApplicationExitInfo.REASON_USER_REQUESTED;
         return forceStopPackageLocked(packageName, appId, callerWillRestart, purgeCache, doit,
                 evenPersistent, uninstalling, packageStateStopped, userId, reasonString, reason);
-
     }
 
     @GuardedBy("this")
@@ -4393,6 +4245,16 @@
             boolean callerWillRestart, boolean purgeCache, boolean doit,
             boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
             int userId, String reasonString, int reason) {
+        return forceStopPackageInternalLocked(packageName, appId, callerWillRestart, purgeCache,
+                doit, evenPersistent, uninstalling, packageStateStopped, userId, reasonString,
+                reason, ProcessList.INVALID_ADJ);
+    }
+
+    @GuardedBy("this")
+    private boolean forceStopPackageInternalLocked(String packageName, int appId,
+            boolean callerWillRestart, boolean purgeCache, boolean doit,
+            boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
+            int userId, String reasonString, int reason, int minOomAdj) {
         int i;
 
         if (userId == UserHandle.USER_ALL && packageName == null) {
@@ -4431,7 +4293,7 @@
             }
 
             didSomething |= mProcessList.killPackageProcessesLSP(packageName, appId, userId,
-                    ProcessList.INVALID_ADJ, callerWillRestart, false /* allowRestart */, doit,
+                    minOomAdj, callerWillRestart, false /* allowRestart */, doit,
                     evenPersistent, true /* setRemoved */, uninstalling,
                     reason,
                     subReason,
@@ -4440,7 +4302,8 @@
         }
 
         if (mServices.bringDownDisabledPackageServicesLocked(
-                packageName, null /* filterByClasses */, userId, evenPersistent, true, doit)) {
+                packageName, null /* filterByClasses */, userId, evenPersistent,
+                true, doit, minOomAdj)) {
             if (!doit) {
                 return true;
             }
@@ -4450,9 +4313,7 @@
 
         if (packageName == null) {
             // Remove all sticky broadcasts from this user.
-            synchronized (mStickyBroadcasts) {
-                mStickyBroadcasts.remove(userId);
-            }
+            mBroadcastController.removeStickyBroadcasts(userId);
         }
 
         ArrayList<ContentProviderRecord> providers = new ArrayList<>();
@@ -4769,7 +4630,6 @@
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
                     processName, app.getWindowProcessController().getConfiguration());
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
-            app.setCompat(compatibilityInfoForPackage(appInfo));
 
             ProfilerInfo profilerInfo = mAppProfiler.setupProfilerInfoLocked(thread, app, instr);
 
@@ -4808,7 +4668,9 @@
             checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
             bindApplicationTimeMillis = SystemClock.uptimeMillis();
             bindApplicationTimeNanos = SystemClock.uptimeNanos();
-            mAtmInternal.preBindApplication(app.getWindowProcessController());
+            final ActivityTaskManagerInternal.PreBindInfo preBindInfo =
+                    mAtmInternal.preBindApplication(app.getWindowProcessController(), appInfo);
+            app.setCompat(preBindInfo.compatibilityInfo);
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
             if (mPlatformCompat != null) {
                 mPlatformCompat.resetReporting(app.info);
@@ -4850,7 +4712,7 @@
                         enableTrackAllocation,
                         isRestrictedBackupMode || !normalMode,
                         app.isPersistent(),
-                        new Configuration(app.getWindowProcessController().getConfiguration()),
+                        preBindInfo.configuration,
                         app.getCompat(),
                         getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
@@ -5303,12 +5165,6 @@
             // Start looking for apps that are abusing wake locks.
             Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
             mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
-            // Check if we are performing userspace reboot before setting sys.boot_completed to
-            // avoid race with init reseting sys.init.userspace_reboot.in_progress once sys
-            // .boot_completed is 1.
-            if (InitProperties.userspace_reboot_in_progress().orElse(false)) {
-                UserspaceRebootLogger.noteUserspaceRebootSuccess();
-            }
             // Tell anyone interested that we are done booting!
             SystemProperties.set("sys.boot_completed", "1");
             SystemProperties.set("dev.bootcomplete", "1");
@@ -5332,7 +5188,6 @@
                             }, mConstants.FULL_PSS_MIN_INTERVAL);
                         }
                     });
-            maybeLogUserspaceRebootEvent();
             mUserController.scheduleStartProfiles();
         }
         // UART is on if init's console service is running, send a warning notification.
@@ -5567,7 +5422,9 @@
             for (int i=0; i<intents.length; i++) {
                 Intent intent = intents[i];
                 if (intent != null) {
-                    intent.prepareToEnterSystemServer();
+                    if (intent.hasFileDescriptors()) {
+                        throw new IllegalArgumentException("File descriptors passed in Intent");
+                    }
                     if (type == ActivityManager.INTENT_SENDER_BROADCAST &&
                             (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
                         throw new IllegalArgumentException(
@@ -5600,6 +5457,7 @@
                         }
                     }
                     intents[i] = new Intent(intent);
+                    intents[i].removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
                 }
             }
             if (resolvedTypes != null && resolvedTypes.length != intents.length) {
@@ -9332,10 +9190,6 @@
                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
     }
 
-    private void startBroadcastObservers() {
-        mBroadcastQueue.start(mContext.getContentResolver());
-    }
-
     private void updateForceBackgroundCheck(boolean enabled) {
         synchronized (this) {
             synchronized (mProcLock) {
@@ -10527,14 +10381,15 @@
                 pw.println(
                         "-------------------------------------------------------------------------------");
             }
-            dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            mBroadcastController.dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println(
                         "-------------------------------------------------------------------------------");
             }
             if (dumpAll || dumpPackage != null) {
-                dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+                mBroadcastController.dumpBroadcastStatsLocked(fd, pw, args, opti, dumpAll,
+                        dumpPackage);
                 pw.println();
                 if (dumpAll) {
                     pw.println(
@@ -10785,7 +10640,7 @@
             } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
                 // output proto is ActivityManagerServiceDumpBroadcastsProto
                 synchronized (this) {
-                    writeBroadcastsToProtoLocked(proto);
+                    mBroadcastController.writeBroadcastsToProtoLocked(proto);
                 }
             } else if ("provider".equals(cmd)) {
                 String[] newArgs;
@@ -10849,7 +10704,7 @@
                     proto.end(activityToken);
 
                     long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
-                    writeBroadcastsToProtoLocked(proto);
+                    mBroadcastController.writeBroadcastsToProtoLocked(proto);
                     proto.end(broadcastToken);
 
                     long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
@@ -10909,7 +10764,8 @@
                     opti++;
                 }
                 synchronized (this) {
-                    dumpBroadcastsLocked(fd, pw, args, opti, /* dumpAll= */ true, dumpPackage);
+                    mBroadcastController.dumpBroadcastsLocked(fd, pw, args, opti,
+                            /* dumpAll= */ true, dumpPackage);
                 }
             } else if ("broadcast-stats".equals(cmd)) {
                 if (opti < args.length) {
@@ -10918,10 +10774,11 @@
                 }
                 synchronized (this) {
                     if (dumpCheckinFormat) {
-                        dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin,
-                                dumpPackage);
+                        mBroadcastController.dumpBroadcastStatsCheckinLocked(fd, pw, args, opti,
+                                dumpCheckin, dumpPackage);
                     } else {
-                        dumpBroadcastStatsLocked(fd, pw, args, opti, true, dumpPackage);
+                        mBroadcastController.dumpBroadcastStatsLocked(fd, pw, args, opti, true,
+                                dumpPackage);
                     }
                 }
             } else if ("intents".equals(cmd) || "i".equals(cmd)) {
@@ -11075,7 +10932,8 @@
 
         // No piece of data specified, dump everything.
         if (dumpCheckinFormat) {
-            dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin, dumpPackage);
+            mBroadcastController.dumpBroadcastStatsCheckinLocked(fd, pw, args, opti, dumpCheckin,
+                    dumpPackage);
         } else {
             if (dumpClient) {
                 // dumpEverything() will take the lock when needed, and momentarily drop
@@ -11786,42 +11644,6 @@
         }
     }
 
-    void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
-        if (mRegisteredReceivers.size() > 0) {
-            Iterator it = mRegisteredReceivers.values().iterator();
-            while (it.hasNext()) {
-                ReceiverList r = (ReceiverList)it.next();
-                r.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_LIST);
-            }
-        }
-        mReceiverResolver.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
-        mBroadcastQueue.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
-        synchronized (mStickyBroadcasts) {
-            for (int user = 0; user < mStickyBroadcasts.size(); user++) {
-                long token = proto.start(
-                        ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
-                proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
-                for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
-                        : mStickyBroadcasts.valueAt(user).entrySet()) {
-                    long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
-                    proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
-                    for (StickyBroadcast broadcast : ent.getValue()) {
-                        broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
-                                false, true, true, false);
-                    }
-                    proto.end(actionToken);
-                }
-                proto.end(token);
-            }
-        }
-
-        long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
-        proto.write(ActivityManagerServiceDumpBroadcastsProto.MainHandler.HANDLER, mHandler.toString());
-        mHandler.getLooper().dumpDebug(proto,
-            ActivityManagerServiceDumpBroadcastsProto.MainHandler.LOOPER);
-        proto.end(handlerToken);
-    }
-
     void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
         pw.println(
@@ -11857,219 +11679,6 @@
         }
     }
 
-    @NeverCompile
-    void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, String dumpPackage) {
-        boolean dumpConstants = true;
-        boolean dumpHistory = true;
-        boolean needSep = false;
-        boolean onlyHistory = false;
-        boolean printedAnything = false;
-        boolean onlyReceivers = false;
-        int filteredUid = Process.INVALID_UID;
-
-        if ("history".equals(dumpPackage)) {
-            if (opti < args.length && "-s".equals(args[opti])) {
-                dumpAll = false;
-            }
-            onlyHistory = true;
-            dumpPackage = null;
-        }
-        if ("receivers".equals(dumpPackage)) {
-            onlyReceivers = true;
-            dumpPackage = null;
-            if (opti + 2 <= args.length) {
-                for (int i = opti; i < args.length; i++) {
-                    String arg = args[i];
-                    switch (arg) {
-                        case "--uid":
-                            filteredUid = getIntArg(pw, args, ++i, Process.INVALID_UID);
-                            if (filteredUid == Process.INVALID_UID) {
-                                return;
-                            }
-                            break;
-                        default:
-                            pw.printf("Invalid argument at index %d: %s\n", i, arg);
-                            return;
-                    }
-                }
-            }
-        }
-        if (DEBUG_BROADCAST) {
-            Slogf.d(TAG_BROADCAST, "dumpBroadcastsLocked(): dumpPackage=%s, onlyHistory=%b, "
-                    + "onlyReceivers=%b, filteredUid=%d", dumpPackage, onlyHistory, onlyReceivers,
-                    filteredUid);
-        }
-
-        pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
-        if (!onlyHistory && dumpAll) {
-            if (mRegisteredReceivers.size() > 0) {
-                boolean printed = false;
-                Iterator it = mRegisteredReceivers.values().iterator();
-                while (it.hasNext()) {
-                    ReceiverList r = (ReceiverList)it.next();
-                    if (dumpPackage != null && (r.app == null ||
-                            !dumpPackage.equals(r.app.info.packageName))) {
-                        continue;
-                    }
-                    if (filteredUid != Process.INVALID_UID && filteredUid != r.app.uid) {
-                        if (DEBUG_BROADCAST) {
-                            Slogf.v(TAG_BROADCAST, "dumpBroadcastsLocked(): skipping receiver whose"
-                                    + " uid (%d) is not %d: %s", r.app.uid, filteredUid, r.app);
-                        }
-                        continue;
-                    }
-                    if (!printed) {
-                        pw.println("  Registered Receivers:");
-                        needSep = true;
-                        printed = true;
-                        printedAnything = true;
-                    }
-                    pw.print("  * "); pw.println(r);
-                    r.dump(pw, "    ");
-                }
-            } else {
-                if (onlyReceivers) {
-                    pw.println("  (no registered receivers)");
-                }
-            }
-
-            if (!onlyReceivers) {
-                if (mReceiverResolver.dump(pw, needSep
-                        ? "\n  Receiver Resolver Table:" : "  Receiver Resolver Table:",
-                        "    ", dumpPackage, false, false)) {
-                    needSep = true;
-                    printedAnything = true;
-                }
-            }
-        }
-
-        if (!onlyReceivers) {
-            needSep = mBroadcastQueue.dumpLocked(fd, pw, args, opti,
-                    dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep);
-            printedAnything |= needSep;
-        }
-
-        needSep = true;
-
-        synchronized (mStickyBroadcasts) {
-            if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null
-                    && dumpPackage == null) {
-                for (int user = 0; user < mStickyBroadcasts.size(); user++) {
-                    if (needSep) {
-                        pw.println();
-                    }
-                    needSep = true;
-                    printedAnything = true;
-                    pw.print("  Sticky broadcasts for user ");
-                    pw.print(mStickyBroadcasts.keyAt(user));
-                    pw.println(":");
-                    StringBuilder sb = new StringBuilder(128);
-                    for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
-                            : mStickyBroadcasts.valueAt(user).entrySet()) {
-                        pw.print("  * Sticky action ");
-                        pw.print(ent.getKey());
-                        if (dumpAll) {
-                            pw.println(":");
-                            ArrayList<StickyBroadcast> broadcasts = ent.getValue();
-                            final int N = broadcasts.size();
-                            for (int i = 0; i < N; i++) {
-                                final Intent intent = broadcasts.get(i).intent;
-                                final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
-                                sb.setLength(0);
-                                sb.append("    Intent: ");
-                                intent.toShortString(sb, false, true, false, false);
-                                pw.print(sb);
-                                if (deferUntilActive) {
-                                    pw.print(" [D]");
-                                }
-                                pw.println();
-                                pw.print("      originalCallingUid: ");
-                                pw.println(broadcasts.get(i).originalCallingUid);
-                                pw.println();
-                                Bundle bundle = intent.getExtras();
-                                if (bundle != null) {
-                                    pw.print("      extras: ");
-                                    pw.println(bundle);
-                                }
-                            }
-                        } else {
-                            pw.println("");
-                        }
-                    }
-                }
-            }
-        }
-
-        if (!onlyHistory && !onlyReceivers && dumpAll) {
-            pw.println();
-            pw.println("  Queue " + mBroadcastQueue.toString() + ": "
-                    + mBroadcastQueue.describeStateLocked());
-            pw.println("  mHandler:");
-            mHandler.dump(new PrintWriterPrinter(pw), "    ");
-            needSep = true;
-            printedAnything = true;
-        }
-
-        if (!printedAnything) {
-            pw.println("  (nothing)");
-        }
-    }
-
-    @NeverCompile
-    void dumpBroadcastStatsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, String dumpPackage) {
-        if (mCurBroadcastStats == null) {
-            return;
-        }
-
-        pw.println("ACTIVITY MANAGER BROADCAST STATS STATE (dumpsys activity broadcast-stats)");
-        final long now = SystemClock.elapsedRealtime();
-        if (mLastBroadcastStats != null) {
-            pw.print("  Last stats (from ");
-            TimeUtils.formatDuration(mLastBroadcastStats.mStartRealtime, now, pw);
-            pw.print(" to ");
-            TimeUtils.formatDuration(mLastBroadcastStats.mEndRealtime, now, pw);
-            pw.print(", ");
-            TimeUtils.formatDuration(mLastBroadcastStats.mEndUptime
-                    - mLastBroadcastStats.mStartUptime, pw);
-            pw.println(" uptime):");
-            if (!mLastBroadcastStats.dumpStats(pw, "    ", dumpPackage)) {
-                pw.println("    (nothing)");
-            }
-            pw.println();
-        }
-        pw.print("  Current stats (from ");
-        TimeUtils.formatDuration(mCurBroadcastStats.mStartRealtime, now, pw);
-        pw.print(" to now, ");
-        TimeUtils.formatDuration(SystemClock.uptimeMillis()
-                - mCurBroadcastStats.mStartUptime, pw);
-        pw.println(" uptime):");
-        if (!mCurBroadcastStats.dumpStats(pw, "    ", dumpPackage)) {
-            pw.println("    (nothing)");
-        }
-    }
-
-    @NeverCompile
-    void dumpBroadcastStatsCheckinLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean fullCheckin, String dumpPackage) {
-        if (mCurBroadcastStats == null) {
-            return;
-        }
-
-        if (mLastBroadcastStats != null) {
-            mLastBroadcastStats.dumpCheckinStats(pw, dumpPackage);
-            if (fullCheckin) {
-                mLastBroadcastStats = null;
-                return;
-            }
-        }
-        mCurBroadcastStats.dumpCheckinStats(pw, dumpPackage);
-        if (fullCheckin) {
-            mCurBroadcastStats = null;
-        }
-    }
-
     void dumpPermissions(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
 
@@ -12548,7 +12157,7 @@
                 opts.dumpProto = true;
             } else if ("--logstats".equals(opt)) {
                 opts.mDumpAllocatorStats = true;
-            } else if ("-h".equals(opt)) {
+            } else if ("-h".equals(opt) || "--help".equals(opt)) {
                 pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]");
                 pw.println("  -a: include all available information for each process.");
                 pw.println("  -d: include dalvik details.");
@@ -12558,10 +12167,13 @@
                 pw.println("  -p: dump also private dirty memory usage.");
                 pw.println("  --oom: only show processes organized by oom adj.");
                 pw.println("  --local: only collect details locally, don't call process.");
+                pw.println("  --logstats: dump native allocator stats to log");
                 pw.println("  --package: interpret process arg as package, dumping all");
                 pw.println("             processes that have loaded that package.");
                 pw.println("  --checkin: dump data for a checkin");
                 pw.println("  --proto: dump data to proto");
+                pw.println("  --logstats: log native allocator statistics.");
+                pw.println("  --unreachable: dump unreachable native memory with libmemunreachable.");
                 pw.println("If [process] is specified it can be the name or ");
                 pw.println("pid of a specific process to dump.");
                 return;
@@ -13978,7 +13590,12 @@
         enforceNotIsolatedCaller("startService");
         enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
         if (service != null) {
-            service.prepareToEnterSystemServer();
+            // Refuse possible leaked file descriptors
+            if (service.hasFileDescriptors()) {
+                throw new IllegalArgumentException("File descriptors passed in Intent");
+            }
+            // Remove existing mismatch flag so it can be properly updated later
+            service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
         }
 
         if (callingPackage == null) {
@@ -14215,7 +13832,12 @@
         enforceAllowedToStartOrBindServiceIfSdkSandbox(service);
 
         if (service != null) {
-            service.prepareToEnterSystemServer();
+            // Refuse possible leaked file descriptors
+            if (service.hasFileDescriptors()) {
+                throw new IllegalArgumentException("File descriptors passed in Intent");
+            }
+            // Remove existing mismatch flag so it can be properly updated later
+            service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
         }
 
         if (callingPackage == null) {
@@ -14591,33 +14213,6 @@
     // BROADCASTS
     // =========================================================
 
-    private boolean isInstantApp(ProcessRecord record, @Nullable String callerPackage, int uid) {
-        if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
-            return false;
-        }
-        // Easy case -- we have the app's ProcessRecord.
-        if (record != null) {
-            return record.info.isInstantApp();
-        }
-        // Otherwise check with PackageManager.
-        IPackageManager pm = AppGlobals.getPackageManager();
-        try {
-            if (callerPackage == null) {
-                final String[] packageNames = pm.getPackagesForUid(uid);
-                if (packageNames == null || packageNames.length == 0) {
-                    throw new IllegalArgumentException("Unable to determine caller package name");
-                }
-                // Instant Apps can't use shared uids, so its safe to only check the first package.
-                callerPackage = packageNames[0];
-            }
-            mAppOpsService.checkPackage(uid, callerPackage);
-            return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid));
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e);
-            return true;
-        }
-    }
-
     /**
      * @deprecated Use {@link #registerReceiverWithFeature}
      */
@@ -14632,657 +14227,12 @@
     public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
             String callerFeatureId, String receiverId, IIntentReceiver receiver,
             IntentFilter filter, String permission, int userId, int flags) {
-        traceRegistrationBegin(receiverId, receiver, filter, userId);
-        try {
-            return registerReceiverWithFeatureTraced(caller, callerPackage, callerFeatureId,
-                    receiverId, receiver, filter, permission, userId, flags);
-        } finally {
-            traceRegistrationEnd();
-        }
-    }
-
-    private static void traceRegistrationBegin(String receiverId, IIntentReceiver receiver,
-            IntentFilter filter, int userId) {
-        if (!Flags.traceReceiverRegistration()) {
-            return;
-        }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            final StringBuilder sb = new StringBuilder("registerReceiver: ");
-            sb.append(Binder.getCallingUid()); sb.append('/');
-            sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
-            final int actionsCount = filter.safeCountActions();
-            if (actionsCount > 0) {
-                for (int i = 0; i < actionsCount; ++i) {
-                    sb.append(filter.getAction(i));
-                    if (i != actionsCount - 1) sb.append(',');
-                }
-            } else {
-                sb.append("null");
-            }
-            sb.append('/');
-            sb.append('u'); sb.append(userId); sb.append('/');
-            sb.append(receiver == null ? "null" : receiver.asBinder());
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, sb.toString());
-        }
-    }
-
-    private static void traceRegistrationEnd() {
-        if (!Flags.traceReceiverRegistration()) {
-            return;
-        }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    private Intent registerReceiverWithFeatureTraced(IApplicationThread caller,
-            String callerPackage, String callerFeatureId, String receiverId,
-            IIntentReceiver receiver, IntentFilter filter, String permission,
-            int userId, int flags) {
-        enforceNotIsolatedCaller("registerReceiver");
-        ArrayList<StickyBroadcast> stickyBroadcasts = null;
-        ProcessRecord callerApp = null;
-        final boolean visibleToInstantApps
-                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
-
-        int callingUid;
-        int callingPid;
-        boolean instantApp;
-        synchronized (mProcLock) {
-            callerApp = getRecordForAppLOSP(caller);
-            if (callerApp == null) {
-                Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
-                return null;
-            }
-            if (callerApp.info.uid != SYSTEM_UID
-                    && !callerApp.getPkgList().containsKey(callerPackage)
-                    && !"android".equals(callerPackage)) {
-                throw new SecurityException("Given caller package " + callerPackage
-                        + " is not running in process " + callerApp);
-            }
-            callingUid = callerApp.info.uid;
-            callingPid = callerApp.getPid();
-
-            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
-        }
-        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
-                ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
-
-        // Warn if system internals are registering for important broadcasts
-        // without also using a priority to ensure they process the event
-        // before normal apps hear about it
-        if (UserHandle.isCore(callingUid)) {
-            final int priority = filter.getPriority();
-            final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
-                    || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
-            if (!systemPriority) {
-                final int N = filter.countActions();
-                for (int i = 0; i < N; i++) {
-                    // TODO: expand to additional important broadcasts over time
-                    final String action = filter.getAction(i);
-                    if (action.startsWith("android.intent.action.USER_")
-                            || action.startsWith("android.intent.action.PACKAGE_")
-                            || action.startsWith("android.intent.action.UID_")
-                            || action.startsWith("android.intent.action.EXTERNAL_")
-                            || action.startsWith("android.bluetooth.")
-                            || action.equals(Intent.ACTION_SHUTDOWN)) {
-                        if (DEBUG_BROADCAST) {
-                            Slog.wtf(TAG,
-                                    "System internals registering for " + filter.toLongString()
-                                            + " with app priority; this will race with apps!",
-                                    new Throwable());
-                        }
-
-                        // When undefined, assume that system internals need
-                        // to hear about the event first; they can use
-                        // SYSTEM_LOW_PRIORITY if they need to hear last
-                        if (priority == 0) {
-                            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-
-        Iterator<String> actions = filter.actionsIterator();
-        if (actions == null) {
-            ArrayList<String> noAction = new ArrayList<String>(1);
-            noAction.add(null);
-            actions = noAction.iterator();
-        }
-        boolean onlyProtectedBroadcasts = true;
-
-        // Collect stickies of users and check if broadcast is only registered for protected
-        // broadcasts
-        int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
-        synchronized (mStickyBroadcasts) {
-            while (actions.hasNext()) {
-                String action = actions.next();
-                for (int id : userIds) {
-                    ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
-                            mStickyBroadcasts.get(id);
-                    if (stickies != null) {
-                        ArrayList<StickyBroadcast> broadcasts = stickies.get(action);
-                        if (broadcasts != null) {
-                            if (stickyBroadcasts == null) {
-                                stickyBroadcasts = new ArrayList<>();
-                            }
-                            stickyBroadcasts.addAll(broadcasts);
-                        }
-                    }
-                }
-                if (onlyProtectedBroadcasts) {
-                    try {
-                        onlyProtectedBroadcasts &=
-                                AppGlobals.getPackageManager().isProtectedBroadcast(action);
-                    } catch (RemoteException e) {
-                        onlyProtectedBroadcasts = false;
-                        Slog.w(TAG, "Remote exception", e);
-                    }
-                }
-            }
-        }
-
-        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
-            SdkSandboxManagerLocal sdkSandboxManagerLocal =
-                    LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
-            if (sdkSandboxManagerLocal == null) {
-                throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
-                        + " whether SDK sandbox uid can register to broadcast receivers.");
-            }
-            if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
-                    /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
-                throw new SecurityException("SDK sandbox not allowed to register receiver"
-                        + " with the given IntentFilter: " + filter.toLongString());
-            }
-        }
-
-        // If the change is enabled, but neither exported or not exported is set, we need to log
-        // an error so the consumer can know to explicitly set the value for their flag.
-        // If the caller is registering for a sticky broadcast with a null receiver, we won't
-        // require a flag
-        final boolean explicitExportStateDefined =
-                (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
-        if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
-                (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
-            throw new IllegalArgumentException(
-                    "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
-                            + "flag");
-        }
-
-        // Don't enforce the flag check if we're EITHER registering for only protected
-        // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
-        // not be used generally, so we will be marking them as exported by default
-        boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
-                DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
-
-        // A receiver that is visible to instant apps must also be exported.
-        final boolean unexportedReceiverVisibleToInstantApps =
-                ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
-                        (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
-        if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
-            throw new IllegalArgumentException(
-                    "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
-                            + "RECEIVER_NOT_EXPORTED flag");
-        }
-
-        if (!onlyProtectedBroadcasts) {
-            if (receiver == null && !explicitExportStateDefined) {
-                // sticky broadcast, no flag specified (flag isn't required)
-                flags |= Context.RECEIVER_EXPORTED;
-            } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
-                throw new SecurityException(
-                        callerPackage + ": One of RECEIVER_EXPORTED or "
-                                + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
-                                + "isn't being registered exclusively for system broadcasts");
-                // Assume default behavior-- flag check is not enforced
-            } else if (!requireExplicitFlagForDynamicReceivers && (
-                    (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
-                // Change is not enabled, assume exported unless otherwise specified.
-                flags |= Context.RECEIVER_EXPORTED;
-            }
-        } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
-            flags |= Context.RECEIVER_EXPORTED;
-        }
-
-        // Dynamic receivers are exported by default for versions prior to T
-        final boolean exported = (flags & Context.RECEIVER_EXPORTED) != 0;
-
-        ArrayList<StickyBroadcast> allSticky = null;
-        if (stickyBroadcasts != null) {
-            final ContentResolver resolver = mContext.getContentResolver();
-            // Look for any matching sticky broadcasts...
-            for (int i = 0, N = stickyBroadcasts.size(); i < N; i++) {
-                final StickyBroadcast broadcast = stickyBroadcasts.get(i);
-                Intent intent = broadcast.intent;
-                // Don't provided intents that aren't available to instant apps.
-                if (instantApp &&
-                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
-                    continue;
-                }
-                // If intent has scheme "content", it will need to access
-                // provider that needs to lock mProviderMap in ActivityThread
-                // and also it may need to wait application response, so we
-                // cannot lock ActivityManagerService here.
-                final int match;
-                if (Flags.avoidResolvingType()) {
-                    match = filter.match(intent.getAction(), broadcast.resolvedDataType,
-                        intent.getScheme(), intent.getData(), intent.getCategories(),
-                        TAG, false /* supportsWildcards */, null /* ignoreActions */,
-                        intent.getExtras());
-                } else {
-                    match = filter.match(resolver, intent, true, TAG);
-                }
-                if (match >= 0) {
-                    if (allSticky == null) {
-                        allSticky = new ArrayList<>();
-                    }
-                    allSticky.add(broadcast);
-                }
-            }
-        }
-
-        // The first sticky in the list is returned directly back to the client.
-        Intent sticky = allSticky != null ? allSticky.get(0).intent : null;
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
-        if (receiver == null) {
-            return sticky;
-        }
-
-        // SafetyNet logging for b/177931370. If any process other than system_server tries to
-        // listen to this broadcast action, then log it.
-        if (callingPid != Process.myPid()) {
-            if (filter.hasAction("com.android.server.net.action.SNOOZE_WARNING")
-                    || filter.hasAction("com.android.server.net.action.SNOOZE_RAPID")) {
-                EventLog.writeEvent(0x534e4554, "177931370", callingUid, "");
-            }
-        }
-
-        synchronized (this) {
-            IApplicationThread thread;
-            if (callerApp != null && ((thread = callerApp.getThread()) == null
-                    || thread.asBinder() != caller.asBinder())) {
-                // Original caller already died
-                return null;
-            }
-            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
-            if (rl == null) {
-                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
-                        userId, receiver);
-                if (rl.app != null) {
-                    final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
-                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
-                        throw new IllegalStateException("Too many receivers, total of "
-                                + totalReceiversForApp + ", registered for pid: "
-                                + rl.pid + ", callerPackage: " + callerPackage);
-                    }
-                    rl.app.mReceivers.addReceiver(rl);
-                } else {
-                    try {
-                        receiver.asBinder().linkToDeath(rl, 0);
-                    } catch (RemoteException e) {
-                        return sticky;
-                    }
-                    rl.linkedToDeath = true;
-                }
-                mRegisteredReceivers.put(receiver.asBinder(), rl);
-            } else if (rl.uid != callingUid) {
-                throw new IllegalArgumentException(
-                        "Receiver requested to register for uid " + callingUid
-                        + " was previously registered for uid " + rl.uid
-                        + " callerPackage is " + callerPackage);
-            } else if (rl.pid != callingPid) {
-                throw new IllegalArgumentException(
-                        "Receiver requested to register for pid " + callingPid
-                        + " was previously registered for pid " + rl.pid
-                        + " callerPackage is " + callerPackage);
-            } else if (rl.userId != userId) {
-                throw new IllegalArgumentException(
-                        "Receiver requested to register for user " + userId
-                        + " was previously registered for user " + rl.userId
-                        + " callerPackage is " + callerPackage);
-            }
-            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
-                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
-                    exported);
-            if (rl.containsFilter(filter)) {
-                Slog.w(TAG, "Receiver with filter " + filter
-                        + " already registered for pid " + rl.pid
-                        + ", callerPackage is " + callerPackage);
-            } else {
-                rl.add(bf);
-                if (!bf.debugCheck()) {
-                    Slog.w(TAG, "==> For Dynamic broadcast");
-                }
-                mReceiverResolver.addFilter(getPackageManagerInternal().snapshot(), bf);
-            }
-
-            // Enqueue broadcasts for all existing stickies that match
-            // this filter.
-            if (allSticky != null) {
-                ArrayList receivers = new ArrayList();
-                receivers.add(bf);
-                sticky = null;
-
-                final int stickyCount = allSticky.size();
-                for (int i = 0; i < stickyCount; i++) {
-                    final StickyBroadcast broadcast = allSticky.get(i);
-                    final int originalStickyCallingUid = allSticky.get(i).originalCallingUid;
-                    // TODO(b/281889567): consider using checkComponentPermission instead of
-                    //  canAccessUnexportedComponents
-                    if (sticky == null && (exported || originalStickyCallingUid == callingUid
-                            || ActivityManager.canAccessUnexportedComponents(
-                            originalStickyCallingUid))) {
-                        sticky = broadcast.intent;
-                    }
-                    BroadcastQueue queue = mBroadcastQueue;
-                    BroadcastRecord r = new BroadcastRecord(queue, broadcast.intent, null,
-                            null, null, -1, -1, false, null, null, null, null, OP_NONE,
-                            BroadcastOptions.makeWithDeferUntilActive(broadcast.deferUntilActive),
-                            receivers, null, null, 0, null, null, false, true, true, -1,
-                            originalStickyCallingUid, BackgroundStartPrivileges.NONE,
-                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
-                            null /* filterExtrasForReceiver */,
-                            broadcast.originalCallingAppProcessState);
-                    queue.enqueueBroadcastLocked(r);
-                }
-            }
-
-            return sticky;
-        }
+        return mBroadcastController.registerReceiverWithFeature(caller, callerPackage,
+                callerFeatureId, receiverId, receiver, filter, permission, userId, flags);
     }
 
     public void unregisterReceiver(IIntentReceiver receiver) {
-        traceUnregistrationBegin(receiver);
-        try {
-            unregisterReceiverTraced(receiver);
-        } finally {
-            traceUnregistrationEnd();
-        }
-    }
-
-    private static void traceUnregistrationBegin(IIntentReceiver receiver) {
-        if (!Flags.traceReceiverRegistration()) {
-            return;
-        }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    TextUtils.formatSimple("unregisterReceiver: %d/%s", Binder.getCallingUid(),
-                            receiver == null ? "null" : receiver.asBinder()));
-        }
-    }
-
-    private static void traceUnregistrationEnd() {
-        if (!Flags.traceReceiverRegistration()) {
-            return;
-        }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
-    private void unregisterReceiverTraced(IIntentReceiver receiver) {
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Unregister receiver: " + receiver);
-
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            boolean doTrim = false;
-            synchronized(this) {
-                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
-                if (rl != null) {
-                    final BroadcastRecord r = rl.curBroadcast;
-                    if (r != null) {
-                        final boolean doNext = r.queue.finishReceiverLocked(
-                                rl.app, r.resultCode, r.resultData, r.resultExtras,
-                                r.resultAbort, false);
-                        if (doNext) {
-                            doTrim = true;
-                        }
-                    }
-                    if (rl.app != null) {
-                        rl.app.mReceivers.removeReceiver(rl);
-                    }
-                    removeReceiverLocked(rl);
-                    if (rl.linkedToDeath) {
-                        rl.linkedToDeath = false;
-                        rl.receiver.asBinder().unlinkToDeath(rl, 0);
-                    }
-                }
-
-                // If we actually concluded any broadcasts, we might now be able
-                // to trim the recipients' apps from our working set
-                if (doTrim) {
-                    trimApplicationsLocked(false, OOM_ADJ_REASON_FINISH_RECEIVER);
-                    return;
-                }
-            }
-
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    void removeReceiverLocked(ReceiverList rl) {
-        mRegisteredReceivers.remove(rl.receiver.asBinder());
-        for (int i = rl.size() - 1; i >= 0; i--) {
-            mReceiverResolver.removeFilter(rl.get(i));
-        }
-    }
-
-    private final void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
-        mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
-    }
-
-    private List<ResolveInfo> collectReceiverComponents(
-            Intent intent, String resolvedType, int callingUid, int callingPid,
-            int[] users, int[] broadcastAllowList) {
-        // TODO: come back and remove this assumption to triage all broadcasts
-        long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
-
-        List<ResolveInfo> receivers = null;
-        HashSet<ComponentName> singleUserReceivers = null;
-        boolean scannedFirstReceivers = false;
-        for (int user : users) {
-            // Skip users that have Shell restrictions
-            if (callingUid == SHELL_UID
-                    && mUserController.hasUserRestriction(
-                    UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
-                continue;
-            }
-            List<ResolveInfo> newReceivers = mPackageManagerInt.queryIntentReceivers(
-                    intent, resolvedType, pmFlags, callingUid, callingPid, user, /* forSend */true);
-            if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
-                // If this is not the system user, we need to check for
-                // any receivers that should be filtered out.
-                for (int i = 0; i < newReceivers.size(); i++) {
-                    ResolveInfo ri = newReceivers.get(i);
-                    if ((ri.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
-                        newReceivers.remove(i);
-                        i--;
-                    }
-                }
-            }
-            // Replace the alias receivers with their targets.
-            if (newReceivers != null) {
-                for (int i = newReceivers.size() - 1; i >= 0; i--) {
-                    final ResolveInfo ri = newReceivers.get(i);
-                    final Resolution<ResolveInfo> resolution =
-                            mComponentAliasResolver.resolveReceiver(intent, ri, resolvedType,
-                                    pmFlags, user, callingUid, callingPid);
-                    if (resolution == null) {
-                        // It was an alias, but the target was not found.
-                        newReceivers.remove(i);
-                        continue;
-                    }
-                    if (resolution.isAlias()) {
-                        newReceivers.set(i, resolution.getTarget());
-                    }
-                }
-            }
-            if (newReceivers != null && newReceivers.size() == 0) {
-                newReceivers = null;
-            }
-
-            if (receivers == null) {
-                receivers = newReceivers;
-            } else if (newReceivers != null) {
-                // We need to concatenate the additional receivers
-                // found with what we have do far.  This would be easy,
-                // but we also need to de-dup any receivers that are
-                // singleUser.
-                if (!scannedFirstReceivers) {
-                    // Collect any single user receivers we had already retrieved.
-                    scannedFirstReceivers = true;
-                    for (int i = 0; i < receivers.size(); i++) {
-                        ResolveInfo ri = receivers.get(i);
-                        if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                            ComponentName cn = new ComponentName(
-                                    ri.activityInfo.packageName, ri.activityInfo.name);
-                            if (singleUserReceivers == null) {
-                                singleUserReceivers = new HashSet<ComponentName>();
-                            }
-                            singleUserReceivers.add(cn);
-                        }
-                    }
-                }
-                // Add the new results to the existing results, tracking
-                // and de-dupping single user receivers.
-                for (int i = 0; i < newReceivers.size(); i++) {
-                    ResolveInfo ri = newReceivers.get(i);
-                    if ((ri.activityInfo.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
-                        ComponentName cn = new ComponentName(
-                                ri.activityInfo.packageName, ri.activityInfo.name);
-                        if (singleUserReceivers == null) {
-                            singleUserReceivers = new HashSet<ComponentName>();
-                        }
-                        if (!singleUserReceivers.contains(cn)) {
-                            singleUserReceivers.add(cn);
-                            receivers.add(ri);
-                        }
-                    } else {
-                        receivers.add(ri);
-                    }
-                }
-            }
-        }
-        if (receivers != null && broadcastAllowList != null) {
-            for (int i = receivers.size() - 1; i >= 0; i--) {
-                final int receiverAppId = UserHandle.getAppId(
-                        receivers.get(i).activityInfo.applicationInfo.uid);
-                if (receiverAppId >= Process.FIRST_APPLICATION_UID
-                        && Arrays.binarySearch(broadcastAllowList, receiverAppId) < 0) {
-                    receivers.remove(i);
-                }
-            }
-        }
-        return receivers;
-    }
-
-    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
-            String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
-        if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
-            // Don't yell about broadcasts sent via shell
-            return;
-        }
-
-        final String action = intent.getAction();
-        if (isProtectedBroadcast
-                || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
-                || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
-                || Intent.ACTION_MEDIA_BUTTON.equals(action)
-                || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
-                || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
-                || Intent.ACTION_MASTER_CLEAR.equals(action)
-                || Intent.ACTION_FACTORY_RESET.equals(action)
-                || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
-                || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
-                || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
-                || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
-                || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
-                || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
-            // Broadcast is either protected, or it's a public action that
-            // we've relaxed, so it's fine for system internals to send.
-            return;
-        }
-
-        // This broadcast may be a problem...  but there are often system components that
-        // want to send an internal broadcast to themselves, which is annoying to have to
-        // explicitly list each action as a protected broadcast, so we will check for that
-        // one safe case and allow it: an explicit broadcast, only being received by something
-        // that has protected itself.
-        if (intent.getPackage() != null || intent.getComponent() != null) {
-            if (receivers == null || receivers.size() == 0) {
-                // Intent is explicit and there's no receivers.
-                // This happens, e.g. , when a system component sends a broadcast to
-                // its own runtime receiver, and there's no manifest receivers for it,
-                // because this method is called twice for each broadcast,
-                // for runtime receivers and manifest receivers and the later check would find
-                // no receivers.
-                return;
-            }
-            boolean allProtected = true;
-            for (int i = receivers.size()-1; i >= 0; i--) {
-                Object target = receivers.get(i);
-                if (target instanceof ResolveInfo) {
-                    ResolveInfo ri = (ResolveInfo)target;
-                    if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
-                        allProtected = false;
-                        break;
-                    }
-                } else {
-                    BroadcastFilter bf = (BroadcastFilter)target;
-                    if (bf.exported && bf.requiredPermission == null) {
-                        allProtected = false;
-                        break;
-                    }
-                }
-            }
-            if (allProtected) {
-                // All safe!
-                return;
-            }
-        }
-
-        // The vast majority of broadcasts sent from system internals
-        // should be protected to avoid security holes, so yell loudly
-        // to ensure we examine these cases.
-        if (callerApp != null) {
-            Log.wtf(TAG, "Sending non-protected broadcast " + action
-                            + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
-                    new Throwable());
-        } else {
-            Log.wtf(TAG, "Sending non-protected broadcast " + action
-                            + " from system uid " + UserHandle.formatUid(callingUid)
-                            + " pkg " + callerPackage,
-                    new Throwable());
-        }
-    }
-
-    // Apply permission policy around the use of specific broadcast options
-    void enforceBroadcastOptionPermissionsInternal(
-            @Nullable Bundle options, int callingUid) {
-        enforceBroadcastOptionPermissionsInternal(BroadcastOptions.fromBundleNullable(options),
-                callingUid);
-    }
-
-    void enforceBroadcastOptionPermissionsInternal(
-            @Nullable BroadcastOptions options, int callingUid) {
-        if (options != null && callingUid != Process.SYSTEM_UID) {
-            if (options.isAlarmBroadcast()) {
-                if (DEBUG_BROADCAST_LIGHT) {
-                    Slog.w(TAG, "Non-system caller " + callingUid
-                            + " may not flag broadcast as alarm");
-                }
-                throw new SecurityException(
-                        "Non-system callers may not flag broadcasts as alarm");
-            }
-            if (options.isInteractive()) {
-                enforceCallingPermission(
-                        android.Manifest.permission.BROADCAST_OPTION_INTERACTIVE,
-                        "setInteractive");
-            }
-        }
+        mBroadcastController.unregisterReceiver(receiver);
     }
 
     @GuardedBy("this")
@@ -15293,1000 +14243,14 @@
             String[] excludedPackages, int appOp, Bundle bOptions, boolean ordered,
             boolean sticky, int callingPid,
             int callingUid, int realCallingUid, int realCallingPid, int userId) {
-        return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
-                resolvedType, null, resultTo, resultCode, resultData, resultExtras,
+        return mBroadcastController.broadcastIntentLocked(callerApp, callerPackage, callerFeatureId,
+                intent, resolvedType, null, resultTo, resultCode, resultData, resultExtras,
                 requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
                 ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
                 BackgroundStartPrivileges.NONE,
                 null /* broadcastAllowList */, null /* filterExtrasForReceiver */);
     }
 
-    @GuardedBy("this")
-    final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
-            @Nullable String callerFeatureId, Intent intent, String resolvedType,
-            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
-            Bundle resultExtras, String[] requiredPermissions,
-            String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
-            boolean ordered, boolean sticky, int callingPid, int callingUid,
-            int realCallingUid, int realCallingPid, int userId,
-            BackgroundStartPrivileges backgroundStartPrivileges,
-            @Nullable int[] broadcastAllowList,
-            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
-        final int cookie = traceBroadcastIntentBegin(intent, resultTo, ordered, sticky,
-                callingUid, realCallingUid, userId);
-        try {
-            final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
-                    intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
-                    resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
-                    appOp, BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
-                    callingPid, callingUid, realCallingUid, realCallingPid, userId,
-                    backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver);
-            return res;
-        } finally {
-            traceBroadcastIntentEnd(cookie);
-        }
-    }
-
-    private static int traceBroadcastIntentBegin(Intent intent, IIntentReceiver resultTo,
-            boolean ordered, boolean sticky, int callingUid, int realCallingUid, int userId) {
-        if (!Flags.traceReceiverRegistration()) {
-            return BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
-        }
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            final StringBuilder sb = new StringBuilder("broadcastIntent: ");
-            sb.append(callingUid); sb.append('/');
-            final String action = intent.getAction();
-            sb.append(action == null ? null : action); sb.append('/');
-            sb.append("0x"); sb.append(Integer.toHexString(intent.getFlags())); sb.append('/');
-            sb.append(ordered ? "O" : "_");
-            sb.append(sticky ? "S" : "_");
-            sb.append(resultTo != null ? "C" : "_");
-            sb.append('/');
-            sb.append('u'); sb.append(userId);
-            if (callingUid != realCallingUid) {
-                sb.append('/');
-                sb.append("sender="); sb.append(realCallingUid);
-            }
-            return BroadcastQueue.traceBegin(sb.toString());
-        }
-        return 0;
-    }
-
-    private static void traceBroadcastIntentEnd(int cookie) {
-        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
-            BroadcastQueue.traceEnd(cookie);
-        }
-    }
-
-    @GuardedBy("this")
-    final int broadcastIntentLockedTraced(ProcessRecord callerApp, String callerPackage,
-            @Nullable String callerFeatureId, Intent intent, String resolvedType,
-            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
-            Bundle resultExtras, String[] requiredPermissions,
-            String[] excludedPermissions, String[] excludedPackages, int appOp,
-            BroadcastOptions brOptions, boolean ordered, boolean sticky, int callingPid,
-            int callingUid, int realCallingUid, int realCallingPid, int userId,
-            BackgroundStartPrivileges backgroundStartPrivileges,
-            @Nullable int[] broadcastAllowList,
-            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
-        // Ensure all internal loopers are registered for idle checks
-        BroadcastLoopers.addMyLooper();
-
-        if (Process.isSdkSandboxUid(realCallingUid)) {
-            final SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
-                    SdkSandboxManagerLocal.class);
-            if (sdkSandboxManagerLocal == null) {
-                throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
-                        + " a broadcast from an SDK sandbox uid.");
-            }
-            if (!sdkSandboxManagerLocal.canSendBroadcast(intent)) {
-                throw new SecurityException(
-                        "Intent " + intent.getAction() + " may not be broadcast from an SDK sandbox"
-                        + " uid. Given caller package " + callerPackage + " (pid=" + callingPid
-                        + ", realCallingUid=" + realCallingUid + ", callingUid= " + callingUid
-                        + ")");
-            }
-        }
-
-        if ((resultTo != null) && (resultToApp == null)) {
-            if (resultTo.asBinder() instanceof BinderProxy) {
-                // Warn when requesting results without a way to deliver them
-                Slog.wtf(TAG, "Sending broadcast " + intent.getAction()
-                        + " with resultTo requires resultToApp", new Throwable());
-            } else {
-                // If not a BinderProxy above, then resultTo is an in-process
-                // receiver, so splice in system_server process
-                resultToApp = getProcessRecordLocked("system", SYSTEM_UID);
-            }
-        }
-
-        intent = new Intent(intent);
-
-        final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
-        // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
-        if (callerInstantApp) {
-            intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-        }
-
-        if (userId == UserHandle.USER_ALL && broadcastAllowList != null) {
-                Slog.e(TAG, "broadcastAllowList only applies when sending to individual users. "
-                        + "Assuming restrictive whitelist.");
-                broadcastAllowList = new int[]{};
-        }
-
-        // By default broadcasts do not go to stopped apps.
-        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
-
-        // If we have not finished booting, don't allow this to launch new processes.
-        if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        }
-
-        if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
-                (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
-                        + " ordered=" + ordered + " userid=" + userId
-                        + " options=" + (brOptions == null ? "null" : brOptions.toBundle()));
-        if ((resultTo != null) && !ordered) {
-            if (!UserHandle.isCore(callingUid)) {
-                String msg = "Unauthorized unordered resultTo broadcast "
-                             + intent + " sent from uid " + callingUid;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-        }
-
-        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
-                ALLOW_NON_FULL, "broadcast", callerPackage);
-
-        // Make sure that the user who is receiving this broadcast or its parent is running.
-        // If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps.
-        if (userId != UserHandle.USER_ALL && !mUserController.isUserOrItsParentRunning(userId)) {
-            if ((callingUid != SYSTEM_UID
-                    || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
-                    && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
-                Slog.w(TAG, "Skipping broadcast of " + intent
-                        + ": user " + userId + " and its parent (if any) are stopped");
-                scheduleCanceledResultTo(resultToApp, resultTo, intent, userId,
-                        brOptions, callingUid, callerPackage);
-                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
-            }
-        }
-
-        final String action = intent.getAction();
-        if (brOptions != null) {
-            if (brOptions.getTemporaryAppAllowlistDuration() > 0) {
-                // See if the caller is allowed to do this.  Note we are checking against
-                // the actual real caller (not whoever provided the operation as say a
-                // PendingIntent), because that who is actually supplied the arguments.
-                if (checkComponentPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
-                        realCallingPid, realCallingUid, -1, true)
-                        != PackageManager.PERMISSION_GRANTED
-                        && checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
-                        realCallingPid, realCallingUid, -1, true)
-                        != PackageManager.PERMISSION_GRANTED
-                        && checkComponentPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND,
-                        realCallingPid, realCallingUid, -1, true)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    String msg = "Permission Denial: " + intent.getAction()
-                            + " broadcast from " + callerPackage + " (pid=" + callingPid
-                            + ", uid=" + callingUid + ")"
-                            + " requires "
-                            + CHANGE_DEVICE_IDLE_TEMP_WHITELIST + " or "
-                            + START_ACTIVITIES_FROM_BACKGROUND + " or "
-                            + START_FOREGROUND_SERVICES_FROM_BACKGROUND;
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                }
-            }
-            if (brOptions.isDontSendToRestrictedApps()
-                    && !isUidActiveLOSP(callingUid)
-                    && isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
-                Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
-                        + " has background restrictions");
-                return ActivityManager.START_CANCELED;
-            }
-            if (brOptions.allowsBackgroundActivityStarts()) {
-                // See if the caller is allowed to do this.  Note we are checking against
-                // the actual real caller (not whoever provided the operation as say a
-                // PendingIntent), because that who is actually supplied the arguments.
-                if (checkComponentPermission(
-                        android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
-                        realCallingPid, realCallingUid, -1, true)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    String msg = "Permission Denial: " + intent.getAction()
-                            + " broadcast from " + callerPackage + " (pid=" + callingPid
-                            + ", uid=" + callingUid + ")"
-                            + " requires "
-                            + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                } else {
-                    // We set the token to null since if it wasn't for it we'd allow anyway here
-                    backgroundStartPrivileges = BackgroundStartPrivileges.ALLOW_BAL;
-                }
-            }
-
-            if (brOptions.getIdForResponseEvent() > 0) {
-                enforcePermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
-                        callingPid, callingUid, "recordResponseEventWhileInBackground");
-            }
-        }
-
-        // Verify that protected broadcasts are only being sent by system code,
-        // and that system code is only sending protected broadcasts.
-        final boolean isProtectedBroadcast;
-        try {
-            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Remote exception", e);
-            scheduleCanceledResultTo(resultToApp, resultTo, intent,
-                    userId, brOptions, callingUid, callerPackage);
-            return ActivityManager.BROADCAST_SUCCESS;
-        }
-
-        final boolean isCallerSystem;
-        switch (UserHandle.getAppId(callingUid)) {
-            case ROOT_UID:
-            case SYSTEM_UID:
-            case PHONE_UID:
-            case BLUETOOTH_UID:
-            case NFC_UID:
-            case SE_UID:
-            case NETWORK_STACK_UID:
-                isCallerSystem = true;
-                break;
-            default:
-                isCallerSystem = (callerApp != null) && callerApp.isPersistent();
-                break;
-        }
-
-        // First line security check before anything else: stop non-system apps from
-        // sending protected broadcasts.
-        if (!isCallerSystem) {
-            if (isProtectedBroadcast) {
-                String msg = "Permission Denial: not allowed to send broadcast "
-                        + action + " from pid="
-                        + callingPid + ", uid=" + callingUid;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-
-            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
-                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
-                // Special case for compatibility: we don't want apps to send this,
-                // but historically it has not been protected and apps may be using it
-                // to poke their own app widget.  So, instead of making it protected,
-                // just limit it to the caller.
-                if (callerPackage == null) {
-                    String msg = "Permission Denial: not allowed to send broadcast "
-                            + action + " from unknown caller.";
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                } else if (intent.getComponent() != null) {
-                    // They are good enough to send to an explicit component...  verify
-                    // it is being sent to the calling app.
-                    if (!intent.getComponent().getPackageName().equals(
-                            callerPackage)) {
-                        String msg = "Permission Denial: not allowed to send broadcast "
-                                + action + " to "
-                                + intent.getComponent().getPackageName() + " from "
-                                + callerPackage;
-                        Slog.w(TAG, msg);
-                        throw new SecurityException(msg);
-                    }
-                } else {
-                    // Limit broadcast to their own package.
-                    intent.setPackage(callerPackage);
-                }
-            }
-        }
-
-        boolean timeoutExempt = false;
-
-        if (action != null) {
-            if (getBackgroundLaunchBroadcasts().contains(action)) {
-                if (DEBUG_BACKGROUND_CHECK) {
-                    Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
-                }
-                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-            }
-
-            // TODO: b/329211459 - Remove this after background remote intent is fixed.
-            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
-                    && getWearRemoteIntentAction().equals(action)) {
-                final int callerProcState = callerApp != null
-                        ? callerApp.getCurProcState()
-                        : ActivityManager.PROCESS_STATE_NONEXISTENT;
-                if (ActivityManager.RunningAppProcessInfo.procStateToImportance(callerProcState)
-                        > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
-                    return ActivityManager.START_CANCELED;
-                }
-            }
-
-            switch (action) {
-                case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
-                    UserManagerInternal umInternal = LocalServices.getService(
-                            UserManagerInternal.class);
-                    UserInfo userInfo = umInternal.getUserInfo(userId);
-                    if (userInfo != null && userInfo.isCloneProfile()) {
-                        userId = umInternal.getProfileParentId(userId);
-                    }
-                    break;
-                case Intent.ACTION_UID_REMOVED:
-                case Intent.ACTION_PACKAGE_REMOVED:
-                case Intent.ACTION_PACKAGE_CHANGED:
-                case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
-                case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
-                case Intent.ACTION_PACKAGES_SUSPENDED:
-                case Intent.ACTION_PACKAGES_UNSUSPENDED:
-                    // Handle special intents: if this broadcast is from the package
-                    // manager about a package being removed, we need to remove all of
-                    // its activities from the history stack.
-                    if (checkComponentPermission(
-                            android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
-                            callingPid, callingUid, -1, true)
-                            != PackageManager.PERMISSION_GRANTED) {
-                        String msg = "Permission Denial: " + intent.getAction()
-                                + " broadcast from " + callerPackage + " (pid=" + callingPid
-                                + ", uid=" + callingUid + ")"
-                                + " requires "
-                                + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
-                        Slog.w(TAG, msg);
-                        throw new SecurityException(msg);
-                    }
-                    switch (action) {
-                        case Intent.ACTION_UID_REMOVED:
-                            final int uid = getUidFromIntent(intent);
-                            if (uid >= 0) {
-                                mBatteryStatsService.removeUid(uid);
-                                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                                    mAppOpsService.resetAllModes(UserHandle.getUserId(uid),
-                                            intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
-                                } else {
-                                    mAppOpsService.uidRemoved(uid);
-                                    mServices.onUidRemovedLocked(uid);
-                                }
-                            }
-                            break;
-                        case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
-                            // If resources are unavailable just force stop all those packages
-                            // and flush the attribute cache as well.
-                            String list[] =
-                                    intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                            if (list != null && list.length > 0) {
-                                for (int i = 0; i < list.length; i++) {
-                                    forceStopPackageLocked(list[i], -1, false, true, true,
-                                            false, false, false, userId, "storage unmount");
-                                }
-                                mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
-                                sendPackageBroadcastLocked(
-                                        ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
-                                        list, userId);
-                            }
-                            break;
-                        case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
-                            mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
-                            break;
-                        case Intent.ACTION_PACKAGE_REMOVED:
-                        case Intent.ACTION_PACKAGE_CHANGED:
-                            Uri data = intent.getData();
-                            String ssp;
-                            if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
-                                boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
-                                final boolean replacing =
-                                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-                                final boolean killProcess =
-                                        !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
-                                final boolean fullUninstall = removed && !replacing;
-
-                                if (removed) {
-                                    if (killProcess) {
-                                        forceStopPackageLocked(ssp, UserHandle.getAppId(
-                                                intent.getIntExtra(Intent.EXTRA_UID, -1)),
-                                                false, true, true, false, fullUninstall, false,
-                                                userId, "pkg removed");
-                                        getPackageManagerInternal()
-                                                .onPackageProcessKilledForUninstall(ssp);
-                                    } else {
-                                        // Kill any app zygotes always, since they can't fork new
-                                        // processes with references to the old code
-                                        forceStopAppZygoteLocked(ssp, UserHandle.getAppId(
-                                                intent.getIntExtra(Intent.EXTRA_UID, -1)),
-                                                userId);
-                                    }
-                                    final int cmd = killProcess
-                                            ? ApplicationThreadConstants.PACKAGE_REMOVED
-                                            : ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;
-                                    sendPackageBroadcastLocked(cmd,
-                                            new String[] {ssp}, userId);
-                                    if (fullUninstall) {
-                                        // Remove all permissions granted from/to this package
-                                        mUgmInternal.removeUriPermissionsForPackage(ssp, userId,
-                                                true, false);
-
-                                        mAtmInternal.removeRecentTasksByPackageName(ssp, userId);
-
-                                        mServices.forceStopPackageLocked(ssp, userId);
-                                        mAtmInternal.onPackageUninstalled(ssp, userId);
-                                        mBatteryStatsService.notePackageUninstalled(ssp);
-                                    }
-                                } else {
-                                    if (killProcess) {
-                                        int reason;
-                                        int subReason;
-                                        if (replacing) {
-                                            reason = ApplicationExitInfo.REASON_PACKAGE_UPDATED;
-                                            subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
-                                        } else {
-                                            reason =
-                                                    ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE;
-                                            subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
-                                        }
-
-                                        final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
-                                                -1);
-                                        synchronized (mProcLock) {
-                                            mProcessList.killPackageProcessesLSP(ssp,
-                                                    UserHandle.getAppId(extraUid),
-                                                    userId, ProcessList.INVALID_ADJ,
-                                                    reason,
-                                                    subReason,
-                                                    "change " + ssp);
-                                        }
-                                    }
-                                    cleanupDisabledPackageComponentsLocked(ssp, userId,
-                                            intent.getStringArrayExtra(
-                                                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
-                                    mServices.schedulePendingServiceStartLocked(ssp, userId);
-                                }
-                            }
-                            break;
-                        case Intent.ACTION_PACKAGES_SUSPENDED:
-                        case Intent.ACTION_PACKAGES_UNSUSPENDED:
-                            final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(
-                                    intent.getAction());
-                            final String[] packageNames = intent.getStringArrayExtra(
-                                    Intent.EXTRA_CHANGED_PACKAGE_LIST);
-                            final int userIdExtra = intent.getIntExtra(
-                                    Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-
-                            mAtmInternal.onPackagesSuspendedChanged(packageNames, suspended,
-                                    userIdExtra);
-
-                            final boolean quarantined = intent.getBooleanExtra(
-                                    Intent.EXTRA_QUARANTINED, false);
-                            if (suspended && quarantined && packageNames != null) {
-                                for (int i = 0; i < packageNames.length; i++) {
-                                    forceStopPackage(packageNames[i], userId,
-                                            ActivityManager.FLAG_OR_STOPPED, "quarantined");
-                                }
-                            }
-
-                            break;
-                    }
-                    break;
-                case Intent.ACTION_PACKAGE_REPLACED:
-                {
-                    final Uri data = intent.getData();
-                    final String ssp;
-                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
-                        ApplicationInfo aInfo = null;
-                        try {
-                            aInfo = AppGlobals.getPackageManager()
-                                    .getApplicationInfo(ssp, STOCK_PM_FLAGS, userId);
-                        } catch (RemoteException ignore) {}
-                        if (aInfo == null) {
-                            Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"
-                                    + " ssp=" + ssp + " data=" + data);
-                            scheduleCanceledResultTo(resultToApp, resultTo, intent,
-                                    userId, brOptions, callingUid, callerPackage);
-                            return ActivityManager.BROADCAST_SUCCESS;
-                        }
-                        updateAssociationForApp(aInfo);
-                        mAtmInternal.onPackageReplaced(aInfo);
-                        mServices.updateServiceApplicationInfoLocked(aInfo);
-                        sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
-                                new String[] {ssp}, userId);
-                    }
-                    break;
-                }
-                case Intent.ACTION_PACKAGE_ADDED:
-                {
-                    // Special case for adding a package: by default turn on compatibility mode.
-                    Uri data = intent.getData();
-                    String ssp;
-                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
-                        final boolean replacing =
-                                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-                        mAtmInternal.onPackageAdded(ssp, replacing);
-
-                        try {
-                            ApplicationInfo ai = AppGlobals.getPackageManager().
-                                    getApplicationInfo(ssp, STOCK_PM_FLAGS, 0);
-                            mBatteryStatsService.notePackageInstalled(ssp,
-                                    ai != null ? ai.longVersionCode : 0);
-                        } catch (RemoteException e) {
-                        }
-                    }
-                    break;
-                }
-                case Intent.ACTION_PACKAGE_DATA_CLEARED:
-                {
-                    Uri data = intent.getData();
-                    String ssp;
-                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
-                        mAtmInternal.onPackageDataCleared(ssp, userId);
-                    }
-                    break;
-                }
-                case Intent.ACTION_TIMEZONE_CHANGED:
-                    // If this is the time zone changed action, queue up a message that will reset
-                    // the timezone of all currently running processes. This message will get
-                    // queued up before the broadcast happens.
-                    mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
-                    break;
-                case Intent.ACTION_TIME_CHANGED:
-                    // EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between
-                    // the tri-state value it may contain and "unknown".
-                    // For convenience we re-use the Intent extra values.
-                    final int NO_EXTRA_VALUE_FOUND = -1;
-                    final int timeFormatPreferenceMsgValue = intent.getIntExtra(
-                            Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
-                            NO_EXTRA_VALUE_FOUND /* defaultValue */);
-                    // Only send a message if the time preference is available.
-                    if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) {
-                        Message updateTimePreferenceMsg =
-                                mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG,
-                                        timeFormatPreferenceMsgValue, 0);
-                        mHandler.sendMessage(updateTimePreferenceMsg);
-                    }
-                    mBatteryStatsService.noteCurrentTimeChanged();
-                    break;
-                case ConnectivityManager.ACTION_CLEAR_DNS_CACHE:
-                    mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
-                    break;
-                case Proxy.PROXY_CHANGE_ACTION:
-                    mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG));
-                    break;
-                case android.hardware.Camera.ACTION_NEW_PICTURE:
-                case android.hardware.Camera.ACTION_NEW_VIDEO:
-                    // In N we just turned these off; in O we are turing them back on partly,
-                    // only for registered receivers.  This will still address the main problem
-                    // (a spam of apps waking up when a picture is taken putting significant
-                    // memory pressure on the system at a bad point), while still allowing apps
-                    // that are already actively running to know about this happening.
-                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                    break;
-                case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:
-                    mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG);
-                    break;
-                case "com.android.launcher.action.INSTALL_SHORTCUT":
-                    // As of O, we no longer support this broadcasts, even for pre-O apps.
-                    // Apps should now be using ShortcutManager.pinRequestShortcut().
-                    Log.w(TAG, "Broadcast " + action
-                            + " no longer supported. It will not be delivered.");
-                    scheduleCanceledResultTo(resultToApp, resultTo, intent,
-                            userId, brOptions, callingUid, callerPackage);
-                    return ActivityManager.BROADCAST_SUCCESS;
-                case Intent.ACTION_PRE_BOOT_COMPLETED:
-                    timeoutExempt = true;
-                    break;
-                case Intent.ACTION_CLOSE_SYSTEM_DIALOGS:
-                    if (!mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid,
-                            callerPackage)) {
-                        scheduleCanceledResultTo(resultToApp, resultTo, intent,
-                                userId, brOptions, callingUid, callerPackage);
-                        // Returning success seems to be the pattern here
-                        return ActivityManager.BROADCAST_SUCCESS;
-                    }
-                    break;
-            }
-
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
-                final int uid = getUidFromIntent(intent);
-                if (uid != -1) {
-                    final UidRecord uidRec = mProcessList.getUidRecordLOSP(uid);
-                    if (uidRec != null) {
-                        uidRec.updateHasInternetPermission();
-                    }
-                }
-            }
-        }
-
-        final int callerAppProcessState = getRealProcessStateLocked(callerApp, realCallingPid);
-        // Add to the sticky list if requested.
-        if (sticky) {
-            if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
-                    callingPid, callingUid)
-                    != PackageManager.PERMISSION_GRANTED) {
-                String msg =
-                        "Permission Denial: broadcastIntent() requesting a sticky broadcast from"
-                            + " pid="
-                                + callingPid
-                                + ", uid="
-                                + callingUid
-                                + " requires "
-                                + android.Manifest.permission.BROADCAST_STICKY;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-            if (requiredPermissions != null && requiredPermissions.length > 0) {
-                Slog.w(TAG, "Can't broadcast sticky intent " + intent
-                        + " and enforce permissions " + Arrays.toString(requiredPermissions));
-                scheduleCanceledResultTo(resultToApp, resultTo, intent,
-                        userId, brOptions, callingUid, callerPackage);
-                return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
-            }
-            if (intent.getComponent() != null) {
-                throw new SecurityException(
-                        "Sticky broadcasts can't target a specific component");
-            }
-            synchronized (mStickyBroadcasts) {
-                // We use userId directly here, since the "all" target is maintained
-                // as a separate set of sticky broadcasts.
-                if (userId != UserHandle.USER_ALL) {
-                    // But first, if this is not a broadcast to all users, then
-                    // make sure it doesn't conflict with an existing broadcast to
-                    // all users.
-                    ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
-                            UserHandle.USER_ALL);
-                    if (stickies != null) {
-                        ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
-                        if (list != null) {
-                            int N = list.size();
-                            int i;
-                            for (i = 0; i < N; i++) {
-                                if (intent.filterEquals(list.get(i).intent)) {
-                                    throw new IllegalArgumentException("Sticky broadcast " + intent
-                                            + " for user " + userId
-                                            + " conflicts with existing global broadcast");
-                                }
-                            }
-                        }
-                    }
-                }
-                ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
-                        mStickyBroadcasts.get(userId);
-                if (stickies == null) {
-                    stickies = new ArrayMap<>();
-                    mStickyBroadcasts.put(userId, stickies);
-                }
-                ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
-                if (list == null) {
-                    list = new ArrayList<>();
-                    stickies.put(intent.getAction(), list);
-                }
-                final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
-                        callingUid, brOptions, resultTo, ordered,
-                        BroadcastRecord.calculateUrgent(intent, brOptions));
-                final int stickiesCount = list.size();
-                int i;
-                for (i = 0; i < stickiesCount; i++) {
-                    if (intent.filterEquals(list.get(i).intent)) {
-                        // This sticky already exists, replace it.
-                        list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
-                                callingUid, callerAppProcessState, resolvedType));
-                        break;
-                    }
-                }
-                if (i >= stickiesCount) {
-                    list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
-                            callingUid, callerAppProcessState, resolvedType));
-                }
-            }
-        }
-
-        int[] users;
-        if (userId == UserHandle.USER_ALL) {
-            // Caller wants broadcast to go to all started users.
-            users = mUserController.getStartedUserArray();
-        } else {
-            // Caller wants broadcast to go to one specific user.
-            users = new int[] {userId};
-        }
-
-        var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
-                true /* isReceiver */, true /* resolveForStart */, callingUid, callingPid);
-        args.platformCompat = mPlatformCompat;
-
-        // Figure out who all will receive this broadcast.
-        final int cookie = BroadcastQueue.traceBegin("queryReceivers");
-        List receivers = null;
-        List<BroadcastFilter> registeredReceivers = null;
-        // Need to resolve the intent to interested receivers...
-        if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
-            receivers = collectReceiverComponents(
-                    intent, resolvedType, callingUid, callingPid, users, broadcastAllowList);
-        }
-        if (intent.getComponent() == null) {
-            final PackageDataSnapshot snapshot = getPackageManagerInternal().snapshot();
-            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
-                // Query one target user at a time, excluding shell-restricted users
-                for (int i = 0; i < users.length; i++) {
-                    if (mUserController.hasUserRestriction(
-                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
-                        continue;
-                    }
-                    List<BroadcastFilter> registeredReceiversForUser =
-                            mReceiverResolver.queryIntent(snapshot, intent,
-                                    resolvedType, false /*defaultOnly*/, users[i]);
-                    if (registeredReceivers == null) {
-                        registeredReceivers = registeredReceiversForUser;
-                    } else if (registeredReceiversForUser != null) {
-                        registeredReceivers.addAll(registeredReceiversForUser);
-                    }
-                }
-            } else {
-                registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
-                        resolvedType, false /*defaultOnly*/, userId);
-            }
-            if (registeredReceivers != null) {
-                SaferIntentUtils.blockNullAction(args, registeredReceivers);
-            }
-        }
-        BroadcastQueue.traceEnd(cookie);
-
-        final boolean replacePending =
-                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
-
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
-                + " replacePending=" + replacePending);
-        if (registeredReceivers != null && broadcastAllowList != null) {
-            // if a uid whitelist was provided, remove anything in the application space that wasn't
-            // in it.
-            for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
-                final int owningAppId = UserHandle.getAppId(registeredReceivers.get(i).owningUid);
-                if (owningAppId >= Process.FIRST_APPLICATION_UID
-                        && Arrays.binarySearch(broadcastAllowList, owningAppId) < 0) {
-                    registeredReceivers.remove(i);
-                }
-            }
-        }
-
-        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
-
-        // Merge into one list.
-        int ir = 0;
-        if (receivers != null) {
-            // A special case for PACKAGE_ADDED: do not allow the package
-            // being added to see this broadcast.  This prevents them from
-            // using this as a back door to get run as soon as they are
-            // installed.  Maybe in the future we want to have a special install
-            // broadcast or such for apps, but we'd like to deliberately make
-            // this decision.
-            String skipPackages[] = null;
-            if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
-                    || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
-                    || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
-                Uri data = intent.getData();
-                if (data != null) {
-                    String pkgName = data.getSchemeSpecificPart();
-                    if (pkgName != null) {
-                        skipPackages = new String[] { pkgName };
-                    }
-                }
-            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
-                skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            }
-            if (skipPackages != null && (skipPackages.length > 0)) {
-                for (String skipPackage : skipPackages) {
-                    if (skipPackage != null) {
-                        int NT = receivers.size();
-                        for (int it=0; it<NT; it++) {
-                            ResolveInfo curt = (ResolveInfo)receivers.get(it);
-                            if (curt.activityInfo.packageName.equals(skipPackage)) {
-                                receivers.remove(it);
-                                it--;
-                                NT--;
-                            }
-                        }
-                    }
-                }
-            }
-
-            int NT = receivers != null ? receivers.size() : 0;
-            int it = 0;
-            ResolveInfo curt = null;
-            BroadcastFilter curr = null;
-            while (it < NT && ir < NR) {
-                if (curt == null) {
-                    curt = (ResolveInfo)receivers.get(it);
-                }
-                if (curr == null) {
-                    curr = registeredReceivers.get(ir);
-                }
-                if (curr.getPriority() >= curt.priority) {
-                    // Insert this broadcast record into the final list.
-                    receivers.add(it, curr);
-                    ir++;
-                    curr = null;
-                    it++;
-                    NT++;
-                } else {
-                    // Skip to the next ResolveInfo in the final list.
-                    it++;
-                    curt = null;
-                }
-            }
-        }
-        while (ir < NR) {
-            if (receivers == null) {
-                receivers = new ArrayList();
-            }
-            receivers.add(registeredReceivers.get(ir));
-            ir++;
-        }
-
-        if (isCallerSystem) {
-            checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
-                    isProtectedBroadcast, receivers);
-        }
-
-        if ((receivers != null && receivers.size() > 0)
-                || resultTo != null) {
-            BroadcastQueue queue = mBroadcastQueue;
-            SaferIntentUtils.filterNonExportedComponents(args, receivers);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
-                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
-                    requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
-                    receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
-                    ordered, sticky, false, userId,
-                    backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
-                    callerAppProcessState);
-
-            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
-            queue.enqueueBroadcastLocked(r);
-        } else {
-            // There was nobody interested in the broadcast, but we still want to record
-            // that it happened.
-            if (intent.getComponent() == null && intent.getPackage() == null
-                    && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
-                // This was an implicit broadcast... let's record it for posterity.
-                addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
-            }
-        }
-
-        return ActivityManager.BROADCAST_SUCCESS;
-    }
-
-    @GuardedBy("this")
-    private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo,
-            Intent intent, int userId, BroadcastOptions options, int callingUid,
-            String callingPackage) {
-        if (resultTo == null) {
-            return;
-        }
-        final ProcessRecord app = resultToApp;
-        final IApplicationThread thread  = (app != null) ? app.getOnewayThread() : null;
-        if (thread != null) {
-            try {
-                final boolean shareIdentity = (options != null && options.isShareIdentityEnabled());
-                thread.scheduleRegisteredReceiver(
-                        resultTo, intent, Activity.RESULT_CANCELED, null, null,
-                        false, false, true, userId, app.mState.getReportedProcState(),
-                        shareIdentity ? callingUid : Process.INVALID_UID,
-                        shareIdentity ? callingPackage : null);
-            } catch (RemoteException e) {
-                final String msg = "Failed to schedule result of " + intent + " via "
-                        + app + ": " + e;
-                app.killLocked("Can't schedule resultTo", ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
-                Slog.d(TAG, msg);
-            }
-        }
-    }
-
-    @GuardedBy("this")
-    private int getRealProcessStateLocked(ProcessRecord app, int pid) {
-        if (app == null) {
-            synchronized (mPidsSelfLocked) {
-                app = mPidsSelfLocked.get(pid);
-            }
-        }
-        if (app != null && app.getThread() != null && !app.isKilled()) {
-            return app.mState.getCurProcState();
-        }
-        return PROCESS_STATE_NONEXISTENT;
-    }
-
-    @VisibleForTesting
-    ArrayList<StickyBroadcast> getStickyBroadcastsForTest(String action, int userId) {
-        synchronized (mStickyBroadcasts) {
-            final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
-                    mStickyBroadcasts.get(userId);
-            if (stickyBroadcasts == null) {
-                return null;
-            }
-            return stickyBroadcasts.get(action);
-        }
-    }
-
-    /**
-     * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1
-     */
-    private int getUidFromIntent(Intent intent) {
-        if (intent == null) {
-            return -1;
-        }
-        final Bundle intentExtras = intent.getExtras();
-        return intent.hasExtra(Intent.EXTRA_UID)
-                ? intentExtras.getInt(Intent.EXTRA_UID) : -1;
-    }
-
-    final void rotateBroadcastStatsIfNeededLocked() {
-        final long now = SystemClock.elapsedRealtime();
-        if (mCurBroadcastStats == null ||
-                (mCurBroadcastStats.mStartRealtime +(24*60*60*1000) < now)) {
-            mLastBroadcastStats = mCurBroadcastStats;
-            if (mLastBroadcastStats != null) {
-                mLastBroadcastStats.mEndRealtime = SystemClock.elapsedRealtime();
-                mLastBroadcastStats.mEndUptime = SystemClock.uptimeMillis();
-            }
-            mCurBroadcastStats = new BroadcastStats();
-        }
-    }
-
-    final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount,
-            int skipCount, long dispatchTime) {
-        rotateBroadcastStatsIfNeededLocked();
-        mCurBroadcastStats.addBroadcast(action, srcPackage, receiveCount, skipCount, dispatchTime);
-    }
-
-    final void addBackgroundCheckViolationLocked(String action, String targetPackage) {
-        rotateBroadcastStatsIfNeededLocked();
-        mCurBroadcastStats.addBackgroundCheckViolation(action, targetPackage);
-    }
-
-    final void notifyBroadcastFinishedLocked(@NonNull BroadcastRecord original) {
-        final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
-        final String callerPackage = info != null ? info.packageName : original.callerPackage;
-        if (callerPackage != null) {
-            mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
-                    original.callingUid, 0, callerPackage).sendToTarget();
-        }
-    }
-
-    final Intent verifyBroadcastLocked(Intent intent) {
-        if (intent != null) {
-            intent.prepareToEnterSystemServer();
-        }
-
-        int flags = intent.getFlags();
-
-        if (!mProcessesReady) {
-            // if the caller really truly claims to know what they're doing, go
-            // ahead and allow the broadcast without launching any receivers
-            if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
-                // This will be turned into a FLAG_RECEIVER_REGISTERED_ONLY later on if needed.
-            } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
-                Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
-                        + " before boot completion");
-                throw new IllegalStateException("Cannot broadcast before boot completed");
-            }
-        }
-
-        if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
-            throw new IllegalArgumentException(
-                    "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
-        }
-
-        if ((flags & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
-            switch (Binder.getCallingUid()) {
-                case ROOT_UID:
-                case SHELL_UID:
-                    break;
-                default:
-                    Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID "
-                            + Binder.getCallingUid());
-                    intent.removeFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
-                    break;
-            }
-        }
-
-        return intent;
-    }
-
     /**
      * @deprecated Use {@link #broadcastIntentWithFeature}
      */
@@ -16308,110 +14272,14 @@
             String[] requiredPermissions, String[] excludedPermissions,
             String[] excludedPackages, int appOp, Bundle bOptions,
             boolean serialized, boolean sticky, int userId) {
-        enforceNotIsolatedCaller("broadcastIntent");
-
-        synchronized(this) {
-            intent = verifyBroadcastLocked(intent);
-
-            final ProcessRecord callerApp = getRecordForAppLOSP(caller);
-            final int callingPid = Binder.getCallingPid();
-            final int callingUid = Binder.getCallingUid();
-
-            // We're delivering the result to the caller
-            final ProcessRecord resultToApp = callerApp;
-
-            // Permission regimes around sender-supplied broadcast options.
-            enforceBroadcastOptionPermissionsInternal(bOptions, callingUid);
-
-            final ComponentName cn = intent.getComponent();
-
-            Trace.traceBegin(
-                    Trace.TRACE_TAG_ACTIVITY_MANAGER,
-                    "broadcastIntent:" + (cn != null ? cn.toString() : intent.getAction()));
-
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                return broadcastIntentLocked(callerApp,
-                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
-                        intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
-                        resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
-                        appOp, bOptions, serialized, sticky, callingPid, callingUid, callingUid,
-                        callingPid, userId, BackgroundStartPrivileges.NONE, null, null);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-            }
-        }
-    }
-
-    // Not the binder call surface
-    int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
-            int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
-            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode,
-            String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
-            boolean serialized, boolean sticky, int userId,
-            BackgroundStartPrivileges backgroundStartPrivileges,
-            @Nullable int[] broadcastAllowList) {
-        synchronized(this) {
-            intent = verifyBroadcastLocked(intent);
-
-            final long origId = Binder.clearCallingIdentity();
-            String[] requiredPermissions = requiredPermission == null ? null
-                    : new String[] {requiredPermission};
-            try {
-                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
-                        resultToApp, resultTo, resultCode, resultData, resultExtras,
-                        requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
-                        uid, realCallingUid, realCallingPid, userId,
-                        backgroundStartPrivileges, broadcastAllowList,
-                        null /* filterExtrasForReceiver */);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
+        return mBroadcastController.broadcastIntentWithFeature(caller, callingFeatureId, intent,
+                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+                excludedPermissions, excludedPackages, appOp, bOptions, serialized, sticky, userId);
     }
 
     @Override
     public final void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
-        // Refuse possible leaked file descriptors
-        if (intent != null && intent.hasFileDescriptors() == true) {
-            throw new IllegalArgumentException("File descriptors passed in Intent");
-        }
-
-        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
-                userId, true, ALLOW_NON_FULL, "removeStickyBroadcast", null);
-
-        if (checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
-                != PackageManager.PERMISSION_GRANTED) {
-            String msg = "Permission Denial: unbroadcastIntent() from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid()
-                    + " requires " + android.Manifest.permission.BROADCAST_STICKY;
-            Slog.w(TAG, msg);
-            throw new SecurityException(msg);
-        }
-        synchronized (mStickyBroadcasts) {
-            ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
-            if (stickies != null) {
-                ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
-                if (list != null) {
-                    int N = list.size();
-                    int i;
-                    for (i = 0; i < N; i++) {
-                        if (intent.filterEquals(list.get(i).intent)) {
-                            list.remove(i);
-                            break;
-                        }
-                    }
-                    if (list.size() <= 0) {
-                        stickies.remove(intent.getAction());
-                    }
-                }
-                if (stickies.size() <= 0) {
-                    mStickyBroadcasts.remove(userId);
-                }
-            }
-        }
+        mBroadcastController.unbroadcastIntent(caller, intent, userId);
     }
 
     void backgroundServicesFinishedLocked(int userId) {
@@ -16420,31 +14288,32 @@
 
     public void finishReceiver(IBinder caller, int resultCode, String resultData,
             Bundle resultExtras, boolean resultAbort, int flags) {
-        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + caller);
+        mBroadcastController.finishReceiver(caller, resultCode, resultData, resultExtras,
+                resultAbort, flags);
+    }
 
-        // Refuse possible leaked file descriptors
-        if (resultExtras != null && resultExtras.hasFileDescriptors()) {
-            throw new IllegalArgumentException("File descriptors passed in Bundle");
-        }
+    @VisibleForTesting
+    ArrayList<BroadcastController.StickyBroadcast> getStickyBroadcastsForTest(String action,
+            int userId) {
+        return mBroadcastController.getStickyBroadcastsForTest(action, userId);
+    }
 
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ProcessRecord callerApp = getRecordForAppLOSP(caller);
-                if (callerApp == null) {
-                    Slog.w(TAG, "finishReceiver: no app for " + caller);
-                    return;
-                }
+    final void notifyBroadcastFinishedLocked(@NonNull BroadcastRecord original) {
+        mBroadcastController.notifyBroadcastFinishedLocked(original);
+    }
 
-                mBroadcastQueue.finishReceiverLocked(callerApp, resultCode,
-                        resultData, resultExtras, resultAbort, true);
-                // updateOomAdjLocked() will be done here
-                trimApplicationsLocked(false, OOM_ADJ_REASON_FINISH_RECEIVER);
-            }
+    final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount,
+            int skipCount, long dispatchTime) {
+        mBroadcastController.addBroadcastStatLocked(action, srcPackage, receiveCount, skipCount,
+                dispatchTime);
+    }
 
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
+    final void addBackgroundCheckViolationLocked(String action, String targetPackage) {
+        mBroadcastController.addBackgroundCheckViolationLocked(action, targetPackage);
+    }
+
+    void removeReceiverLocked(ReceiverList rl) {
+        mBroadcastController.removeReceiverLocked(rl);
     }
 
     // =========================================================
@@ -17809,7 +15678,7 @@
     }
 
     @GuardedBy("this")
-    private void trimApplicationsLocked(boolean forceFullOomAdj, @OomAdjReason int oomAdjReason) {
+    void trimApplicationsLocked(boolean forceFullOomAdj, @OomAdjReason int oomAdjReason) {
         // First remove any unused application processes whose package
         // has been removed.
         boolean didSomething = false;
@@ -18388,43 +16257,55 @@
 
         boolean closeFd = true;
         try {
-            synchronized (mProcLock) {
-                if (fd == null) {
-                    throw new IllegalArgumentException("null fd");
-                }
-                mBinderTransactionTrackingEnabled = false;
+            Objects.requireNonNull(fd);
 
-                PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
-                pw.println("Binder transaction traces for all processes.\n");
-                mProcessList.forEachLruProcessesLOSP(true, process -> {
+            record ProcessToDump(String processName, IApplicationThread thread) { }
+
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd.getFileDescriptor()));
+            pw.println("Binder transaction traces for all processes.\n");
+            final ArrayList<ProcessToDump> processes = new ArrayList<>();
+            synchronized (mProcLock) {
+                // Since dumping binder transactions is a long-running operation, we can't do it
+                // with mProcLock held. Do the initial verification here, and save the processes
+                // to dump later outside the lock.
+                final ArrayList<ProcessRecord> unverifiedProcesses =
+                        new ArrayList<>(mProcessList.getLruProcessesLOSP());
+                for (int i = 0, size = unverifiedProcesses.size(); i < size; i++) {
+                    ProcessRecord process = unverifiedProcesses.get(i);
                     final IApplicationThread thread = process.getThread();
                     if (!processSanityChecksLPr(process, thread)) {
-                        return;
+                        continue;
                     }
-
-                    pw.println("Traces for process: " + process.processName);
-                    pw.flush();
-                    try {
-                        TransferPipe tp = new TransferPipe();
-                        try {
-                            thread.stopBinderTrackingAndDump(tp.getWriteFd());
-                            tp.go(fd.getFileDescriptor());
-                        } finally {
-                            tp.kill();
-                        }
-                    } catch (IOException e) {
-                        pw.println("Failure while dumping IPC traces from " + process +
-                                ".  Exception: " + e);
-                        pw.flush();
-                    } catch (RemoteException e) {
-                        pw.println("Got a RemoteException while dumping IPC traces from " +
-                                process + ".  Exception: " + e);
-                        pw.flush();
-                    }
-                });
-                closeFd = false;
-                return true;
+                    processes.add(new ProcessToDump(process.processName, process.getThread()));
+                }
+                mBinderTransactionTrackingEnabled = false;
             }
+            for (int i = 0, size = processes.size(); i < size; i++) {
+                final String processName = processes.get(i).processName();
+                final IApplicationThread thread = processes.get(i).thread();
+
+                pw.println("Traces for process: " + processName);
+                pw.flush();
+                try {
+                    TransferPipe tp = new TransferPipe();
+                    try {
+                        thread.stopBinderTrackingAndDump(tp.getWriteFd());
+                        tp.go(fd.getFileDescriptor());
+                    } finally {
+                        tp.kill();
+                    }
+                } catch (IOException e) {
+                    pw.println("Failure while dumping IPC traces from " + processName +
+                            ".  Exception: " + e);
+                    pw.flush();
+                } catch (RemoteException e) {
+                    pw.println("Got a RemoteException while dumping IPC traces from " +
+                            processName + ".  Exception: " + e);
+                    pw.flush();
+                }
+            }
+            closeFd = false;
+            return true;
         } finally {
             if (fd != null && closeFd) {
                 try {
@@ -18799,7 +16680,7 @@
 
         @Override
         public void enforceBroadcastOptionsPermissions(Bundle options, int callingUid) {
-            enforceBroadcastOptionPermissionsInternal(options, callingUid);
+            mBroadcastController.enforceBroadcastOptionPermissionsInternal(options, callingUid);
         }
 
         /**
@@ -19189,7 +17070,7 @@
                 @Nullable int[] broadcastAllowList) {
             synchronized (ActivityManagerService.this) {
                 final ProcessRecord resultToApp = getRecordForAppLOSP(resultToThread);
-                return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
+                return mBroadcastController.broadcastIntentInPackage(packageName, featureId,
                         uid, realCallingUid, realCallingPid, intent, resolvedType, resultToApp,
                         resultTo, resultCode, resultData, resultExtras, requiredPermission,
                         bOptions, serialized, sticky, userId,
@@ -19206,13 +17087,13 @@
                 @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
                 @Nullable Bundle bOptions) {
             synchronized (ActivityManagerService.this) {
-                intent = verifyBroadcastLocked(intent);
+                intent = mBroadcastController.verifyBroadcastLocked(intent);
 
                 final int callingPid = Binder.getCallingPid();
                 final int callingUid = Binder.getCallingUid();
                 final long origId = Binder.clearCallingIdentity();
                 try {
-                    return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/,
+                    return mBroadcastController.broadcastIntentLocked(null /*callerApp*/,
                             null /*callerPackage*/, null /*callingFeatureId*/, intent,
                             null /* resolvedType */, null /* resultToApp */, resultTo,
                             0 /* resultCode */, null /* resultData */,
@@ -19872,6 +17753,11 @@
         }
 
         @Override
+        public boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId) {
+            return mUserController.isEarlyPackageKillEnabledForUserSwitch(fromUserId, toUserId);
+        }
+
+        @Override
         public void setStopUserOnSwitch(int value) {
             ActivityManagerService.this.setStopUserOnSwitch(value);
         }
@@ -21124,26 +19010,6 @@
         }
     }
 
-    /**
-     * Gets an {@code int} argument from the given {@code index} on {@code args}, logging an error
-     * message on {@code pw} when it cannot be parsed.
-     *
-     * Returns {@code int} argument or {@code invalidValue} if it could not be parsed.
-     */
-    private static int getIntArg(PrintWriter pw, String[] args, int index, int invalidValue) {
-        if (index > args.length) {
-            pw.println("Missing argument");
-            return invalidValue;
-        }
-        String arg = args[index];
-        try {
-            return Integer.parseInt(arg);
-        } catch (Exception e) {
-            pw.printf("Non-numeric argument at index %d: %s\n", index, arg);
-            return invalidValue;
-        }
-    }
-
     private void notifyMediaProjectionEvent(int uid, @NonNull IBinder projectionToken,
             @MediaProjectionTokenEvent int event) {
         synchronized (mMediaProjectionTokenMap) {
diff --git a/services/core/java/com/android/server/am/Android.bp b/services/core/java/com/android/server/am/Android.bp
index 0294ffe..ceba01e4 100644
--- a/services/core/java/com/android/server/am/Android.bp
+++ b/services/core/java/com/android/server/am/Android.bp
@@ -9,3 +9,10 @@
     name: "am_flags_lib",
     aconfig_declarations: "am_flags",
 }
+
+java_aconfig_library {
+    name: "am_flags_host_lib",
+    host_supported: true,
+    libs: ["fake_device_config"],
+    aconfig_declarations: "am_flags",
+}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 4a31fd1..4c87e1c 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -308,7 +308,7 @@
     /**
      * Cache the package name and information about if it's a system module.
      */
-    @GuardedBy("mLock")
+    @GuardedBy("mSystemModulesCache")
     private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>();
 
     /**
@@ -1603,7 +1603,7 @@
         if (moduleInfos == null) {
             return;
         }
-        synchronized (mLock) {
+        synchronized (mSystemModulesCache) {
             for (ModuleInfo info : moduleInfos) {
                 mSystemModulesCache.put(info.getPackageName(), Boolean.TRUE);
             }
@@ -1611,7 +1611,7 @@
     }
 
     private boolean isSystemModule(String packageName) {
-        synchronized (mLock) {
+        synchronized (mSystemModulesCache) {
             final Boolean val = mSystemModulesCache.get(packageName);
             if (val != null) {
                 return val.booleanValue();
@@ -1639,7 +1639,7 @@
             }
         }
         // Update the cache.
-        synchronized (mLock) {
+        synchronized (mSystemModulesCache) {
             mSystemModulesCache.put(packageName, isSystemModule);
         }
         return isSystemModule;
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 4a7ad31..1b00cec 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -16,7 +16,6 @@
 
 package com.android.server.am;
 
-import static android.app.ApplicationStartInfo.START_TIMESTAMP_LAUNCH;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -51,6 +50,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ProcessMap;
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
 import com.android.server.IoThread;
 import com.android.server.ServiceThread;
 import com.android.server.SystemServiceManager;
@@ -107,6 +108,16 @@
 
     @VisibleForTesting boolean mEnabled = false;
 
+    /**
+     * Monotonic clock which does not reset on reboot.
+     *
+     * Time for offset is persisted along with records, see {@link #persistProcessStartInfo}.
+     * This does not follow the recommendation of {@link MonotonicClock} to persist on shutdown as
+     * it's ok in this case to lose any time change past the last persist as records added since
+     * then will be lost as well and the purpose of this clock is to keep records in order.
+     */
+    @VisibleForTesting MonotonicClock mMonotonicClock = null;
+
     /** Initialized in {@link #init} and read-only after that. */
     @VisibleForTesting ActivityManagerService mService;
 
@@ -203,6 +214,15 @@
         IoThread.getHandler().post(() -> {
             loadExistingProcessStartInfo();
         });
+
+        if (mMonotonicClock == null) {
+            // This should only happen if there are no persisted records, or if the records were
+            // persisted by a version without the monotonic clock. Either way, create a new clock
+            // with no offset. In the case of records with no monotonic time the value will default
+            // to 0 and all new records will correctly end up in front of them.
+            mMonotonicClock = new MonotonicClock(Clock.SYSTEM_CLOCK.elapsedRealtime(),
+                    Clock.SYSTEM_CLOCK);
+        }
     }
 
     /**
@@ -264,7 +284,7 @@
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
+            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
             start.setIntent(intent);
             start.setStartType(ApplicationStartInfo.START_TYPE_UNSET);
@@ -396,7 +416,7 @@
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
+            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
             addBaseFieldsFromProcessRecord(start, app);
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
             start.addStartupTimestamp(
@@ -422,7 +442,7 @@
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
+            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
             addBaseFieldsFromProcessRecord(start, app);
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
             start.addStartupTimestamp(
@@ -444,7 +464,7 @@
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
+            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
             addBaseFieldsFromProcessRecord(start, app);
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
             start.addStartupTimestamp(
@@ -461,7 +481,7 @@
             if (!mEnabled) {
                 return;
             }
-            ApplicationStartInfo start = new ApplicationStartInfo();
+            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
             addBaseFieldsFromProcessRecord(start, app);
             start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
             start.addStartupTimestamp(
@@ -632,7 +652,8 @@
 
                     Collections.sort(
                             list, (a, b) ->
-                            Long.compare(getStartTimestamp(b), getStartTimestamp(a)));
+                            Long.compare(b.getMonoticCreationTimeMs(),
+                                    a.getMonoticCreationTimeMs()));
                     int size = list.size();
                     if (maxNum > 0) {
                         size = Math.min(size, maxNum);
@@ -898,6 +919,10 @@
                     case (int) AppsStartInfoProto.PACKAGES:
                         loadPackagesFromProto(proto, next);
                         break;
+                    case (int) AppsStartInfoProto.MONOTONIC_TIME:
+                        long monotonicTime = proto.readLong(AppsStartInfoProto.MONOTONIC_TIME);
+                        mMonotonicClock = new MonotonicClock(monotonicTime, Clock.SYSTEM_CLOCK);
+                        break;
                 }
             }
         } catch (IOException | IllegalArgumentException | WireTypeMismatchException
@@ -979,6 +1004,7 @@
                     mLastAppStartInfoPersistTimestamp = now;
                 }
             }
+            proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime());
             if (succeeded) {
                 proto.flush();
                 af.finishWrite(out);
@@ -1099,13 +1125,12 @@
         }
     }
 
-    /** Convenience method to obtain timestamp of beginning of start.*/
-    private static long getStartTimestamp(ApplicationStartInfo startInfo) {
-        if (startInfo.getStartupTimestamps() == null
-                    || !startInfo.getStartupTimestamps().containsKey(START_TIMESTAMP_LAUNCH)) {
-            return -1;
+    private long getMonotonicTime() {
+        if (mMonotonicClock == null) {
+            // This should never happen. Return 0 to not interfere with past or future records.
+            return 0;
         }
-        return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH);
+        return mMonotonicClock.monotonicTime();
     }
 
     /** A container class of (@link android.app.ApplicationStartInfo) */
@@ -1143,7 +1168,7 @@
 
             // Sort records so we can remove the least recent ones.
             Collections.sort(mInfos, (a, b) ->
-                    Long.compare(getStartTimestamp(b), getStartTimestamp(a)));
+                    Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs()));
 
             // Remove records and trim list object back to size.
             mInfos.subList(0, mInfos.size() - getMaxCapacity()).clear();
@@ -1165,8 +1190,8 @@
                 long oldestTimeStamp = Long.MAX_VALUE;
                 for (int i = 0; i < size; i++) {
                     ApplicationStartInfo startInfo = mInfos.get(i);
-                    if (getStartTimestamp(startInfo) < oldestTimeStamp) {
-                        oldestTimeStamp = getStartTimestamp(startInfo);
+                    if (startInfo.getMonoticCreationTimeMs() < oldestTimeStamp) {
+                        oldestTimeStamp = startInfo.getMonoticCreationTimeMs();
                         oldestIndex = i;
                     }
                 }
@@ -1176,7 +1201,7 @@
             }
             mInfos.add(info);
             Collections.sort(mInfos, (a, b) ->
-                    Long.compare(getStartTimestamp(b), getStartTimestamp(a)));
+                    Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs()));
         }
 
         /**
@@ -1337,7 +1362,9 @@
                         mUid = proto.readInt(AppsStartInfoProto.Package.User.UID);
                         break;
                     case (int) AppsStartInfoProto.Package.User.APP_START_INFO:
-                        ApplicationStartInfo info = new ApplicationStartInfo();
+                        // Create record with monotonic time 0 in case the persisted record does not
+                        // have a create time.
+                        ApplicationStartInfo info = new ApplicationStartInfo(0);
                         info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO);
                         mInfos.add(info);
                         break;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6333159..955b75d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -123,31 +123,16 @@
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.power.optimization.Flags;
-import com.android.server.power.stats.AggregatedPowerStatsConfig;
-import com.android.server.power.stats.AmbientDisplayPowerStatsProcessor;
-import com.android.server.power.stats.AudioPowerStatsProcessor;
 import com.android.server.power.stats.BatteryExternalStatsWorker;
 import com.android.server.power.stats.BatteryStatsDumpHelperImpl;
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.power.stats.BatteryUsageStatsProvider;
-import com.android.server.power.stats.BluetoothPowerStatsProcessor;
-import com.android.server.power.stats.CameraPowerStatsProcessor;
-import com.android.server.power.stats.CpuPowerStatsProcessor;
-import com.android.server.power.stats.CustomEnergyConsumerPowerStatsProcessor;
-import com.android.server.power.stats.FlashlightPowerStatsProcessor;
-import com.android.server.power.stats.GnssPowerStatsProcessor;
-import com.android.server.power.stats.MobileRadioPowerStatsProcessor;
-import com.android.server.power.stats.PhoneCallPowerStatsProcessor;
-import com.android.server.power.stats.PowerStatsAggregator;
-import com.android.server.power.stats.PowerStatsExporter;
+import com.android.server.power.stats.PowerAttributor;
 import com.android.server.power.stats.PowerStatsScheduler;
 import com.android.server.power.stats.PowerStatsStore;
 import com.android.server.power.stats.PowerStatsUidResolver;
-import com.android.server.power.stats.ScreenPowerStatsProcessor;
-import com.android.server.power.stats.SensorPowerStatsProcessor;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
-import com.android.server.power.stats.VideoPowerStatsProcessor;
-import com.android.server.power.stats.WifiPowerStatsProcessor;
+import com.android.server.power.stats.processor.MultiStatePowerAttributor;
 import com.android.server.power.stats.wakeups.CpuWakeupStats;
 
 import java.io.File;
@@ -207,7 +192,7 @@
     private final AtomicFile mConfigFile;
     private final BatteryStats.BatteryStatsDumpHelper mDumpHelper;
     private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
-    private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+    private final PowerAttributor mPowerAttributor;
 
     private volatile boolean mMonitorEnabled = true;
 
@@ -445,14 +430,12 @@
             mStats.startTrackingSystemServerCpuTime();
         }
 
-        mAggregatedPowerStatsConfig = createAggregatedPowerStatsConfig();
-        mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
+        mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
+        mPowerAttributor = new MultiStatePowerAttributor(mContext, mPowerStatsStore, mPowerProfile,
+                mCpuScalingPolicies, mPowerStatsUidResolver);
         mPowerStatsScheduler = createPowerStatsScheduler(mContext);
-        PowerStatsExporter powerStatsExporter =
-                new PowerStatsExporter(mPowerStatsStore,
-                        new PowerStatsAggregator(mAggregatedPowerStatsConfig, mStats.getHistory()));
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context,
-                powerStatsExporter, mPowerProfile, mCpuScalingPolicies,
+                mPowerAttributor, mPowerProfile, mCpuScalingPolicies,
                 mPowerStatsStore, Clock.SYSTEM_CLOCK);
         mStats.saveBatteryUsageStatsOnReset(mBatteryUsageStatsProvider, mPowerStatsStore);
         mDumpHelper = new BatteryStatsDumpHelperImpl(mBatteryUsageStatsProvider);
@@ -472,152 +455,11 @@
                             onAlarmListener, aHandler);
                 };
         return new PowerStatsScheduler(mStats::schedulePowerStatsSampleCollection,
-                new PowerStatsAggregator(mAggregatedPowerStatsConfig,
-                        mStats.getHistory()), aggregatedPowerStatsSpanDuration,
+                mStats.getHistory(), mPowerAttributor, aggregatedPowerStatsSpanDuration,
                 powerStatsAggregationPeriod, mPowerStatsStore, alarmScheduler, Clock.SYSTEM_CLOCK,
                 mMonotonicClock, () -> mStats.getHistory().getStartTime(), mHandler);
     }
 
-    private AggregatedPowerStatsConfig createAggregatedPowerStatsConfig() {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new CpuPowerStatsProcessor(mPowerProfile, mCpuScalingPolicies));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .setProcessor(
-                        new ScreenPowerStatsProcessor(mPowerProfile));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
-                        BatteryConsumer.POWER_COMPONENT_SCREEN)
-                .setProcessor(new AmbientDisplayPowerStatsProcessor());
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new MobileRadioPowerStatsProcessor(mPowerProfile));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                .setProcessor(new PhoneCallPowerStatsProcessor());
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new WifiPowerStatsProcessor(mPowerProfile));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new BluetoothPowerStatsProcessor(mPowerProfile));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new AudioPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(new VideoPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new FlashlightPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new CameraPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new GnssPowerStatsProcessor(mPowerProfile, mPowerStatsUidResolver));
-
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(new SensorPowerStatsProcessor(
-                        () -> mContext.getSystemService(SensorManager.class)));
-
-        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-        return config;
-    }
-
     private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
             String configString) {
         if (configString == null) {
@@ -662,79 +504,86 @@
     }
 
     public void systemServicesReady() {
+        MultiStatePowerAttributor attributor = (MultiStatePowerAttributor) mPowerAttributor;
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CPU,
                 Flags.streamlinedBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_CPU,
                 Flags.streamlinedBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_SCREEN,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_SCREEN,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                 Flags.streamlinedConnectivityBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                 Flags.streamlinedConnectivityBatteryStats());
+        attributor.setPowerComponentSupported(
+                BatteryConsumer.POWER_COMPONENT_PHONE,
+                Flags.streamlinedConnectivityBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_WIFI,
                 Flags.streamlinedConnectivityBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_WIFI,
                 Flags.streamlinedConnectivityBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                 Flags.streamlinedConnectivityBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
                 Flags.streamlinedConnectivityBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_AUDIO,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_AUDIO,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_VIDEO,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_VIDEO,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_GNSS,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_GNSS,
                 Flags.streamlinedMiscBatteryStats());
 
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_SENSORS,
                 Flags.streamlinedMiscBatteryStats());
 
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_CAMERA,
                 Flags.streamlinedMiscBatteryStats());
-        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+        attributor.setPowerComponentSupported(
                 BatteryConsumer.POWER_COMPONENT_CAMERA,
                 Flags.streamlinedMiscBatteryStats());
 
         // By convention POWER_COMPONENT_ANY represents custom Energy Consumers
         mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_ANY,
                 Flags.streamlinedMiscBatteryStats());
+        attributor.setPowerComponentSupported(
+                BatteryConsumer.POWER_COMPONENT_ANY,
+                Flags.streamlinedMiscBatteryStats());
 
         mWorker.systemServicesReady();
         mStats.systemServicesReady(mContext);
@@ -1347,7 +1196,6 @@
                             bus.getStatsDuration(),
                             bus.getDischargePercentage(),
                             bus.getDischargeDurationMs());
-
             if (DBG) {
                 Slog.d(TAG, "BatteryUsageStats dump = " + bus);
             }
@@ -1357,45 +1205,25 @@
 
             final float totalDeviceConsumedPowerMah = (float) deviceConsumer.getConsumedPower();
 
-            for (@BatteryConsumer.PowerComponent int componentId = 0;
-                    componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                    componentId++) {
+            for (@BatteryConsumer.PowerComponentId int componentIndex :
+                    deviceConsumer.getPowerComponentIds()) {
 
                 for (@BatteryConsumer.ProcessState int processState : UID_PROCESS_STATES) {
 
-                    if (!addStatsForPredefinedComponent(
+                    if (!addStatsForPowerComponent(
                             data,
                             sessionInfo,
                             Process.INVALID_UID,
                             processState,
                             totalDeviceConsumedPowerMah,
+                            0,
                             deviceConsumer,
-                            componentId)) {
+                            componentIndex)) {
                         return StatsManager.PULL_SUCCESS;
                     }
                 }
             }
 
-            final int customPowerComponentCount = deviceConsumer.getCustomPowerComponentCount();
-            for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-                    componentId
-                            < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                                    + customPowerComponentCount;
-                    componentId++) {
-
-                if (!addStatsForCustomComponent(
-                        data,
-                        sessionInfo,
-                        Process.INVALID_UID,
-                        BatteryConsumer.PROCESS_STATE_UNSPECIFIED,
-                        0,
-                        totalDeviceConsumedPowerMah,
-                        deviceConsumer,
-                        componentId)) {
-                    return StatsManager.PULL_SUCCESS;
-                }
-            }
-
             final List<UidBatteryConsumer> uidConsumers = bus.getUidBatteryConsumers();
             uidConsumers.sort(
                     Comparator.<BatteryConsumer>comparingDouble(BatteryConsumer::getConsumedPower)
@@ -1406,47 +1234,22 @@
                 final int uid = uidConsumer.getUid();
                 final float totalConsumedPowerMah = (float) uidConsumer.getConsumedPower();
 
-                for (@BatteryConsumer.PowerComponent int componentId = 0;
-                        componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
-                        componentId++) {
+                for (@BatteryConsumer.PowerComponentId int componentIndex :
+                        uidConsumer.getPowerComponentIds()) {
 
                     for (@BatteryConsumer.ProcessState int processState : UID_PROCESS_STATES) {
 
-                        if (!addStatsForPredefinedComponent(
+                        long timeInProcessStateMs = uidConsumer.getTimeInProcessStateMs(
+                                processState);
+                        if (!addStatsForPowerComponent(
                                 data,
                                 sessionInfo,
                                 uid,
                                 processState,
                                 totalConsumedPowerMah,
+                                timeInProcessStateMs,
                                 uidConsumer,
-                                componentId)) {
-                            return StatsManager.PULL_SUCCESS;
-                        }
-                    }
-                }
-
-                // looping over custom components
-                for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-                        componentId
-                                < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                                        + customPowerComponentCount;
-                        componentId++) {
-                    for (@BatteryConsumer.ProcessState int processState : UID_PROCESS_STATES) {
-                        final long timeInStateMillis =
-                                uidConsumer.getTimeInProcessStateMs(processState);
-                        if (timeInStateMillis <= 0) {
-                            continue;
-                        }
-
-                        if (!addStatsForCustomComponent(
-                                data,
-                                sessionInfo,
-                                uid,
-                                processState,
-                                timeInStateMillis,
-                                totalConsumedPowerMah,
-                                uidConsumer,
-                                componentId)) {
+                                componentIndex)) {
                             return StatsManager.PULL_SUCCESS;
                         }
                     }
@@ -1455,20 +1258,21 @@
             return StatsManager.PULL_SUCCESS;
         }
 
-        private boolean addStatsForPredefinedComponent(
+        private boolean addStatsForPowerComponent(
                 List<StatsEvent> data,
                 SessionInfo sessionInfo,
                 int uid,
                 @BatteryConsumer.ProcessState int processState,
                 float totalConsumedPowerMah,
+                long timeInState,
                 BatteryConsumer batteryConsumer,
-                @BatteryConsumer.PowerComponent int componentId) {
+                @BatteryConsumer.PowerComponentId int componentId) {
             final BatteryConsumer.Key key = batteryConsumer.getKey(componentId, processState);
             if (key == null) {
                 return true;
             }
 
-            final String powerComponentName = BatteryConsumer.powerComponentIdToString(componentId);
+            final String powerComponentName = batteryConsumer.getPowerComponentName(componentId);
             final float powerMah = (float) batteryConsumer.getConsumedPower(key);
             final long powerComponentDurationMillis = batteryConsumer.getUsageDurationMillis(key);
 
@@ -1476,13 +1280,6 @@
                 return true;
             }
 
-            long timeInState = 0;
-            if (batteryConsumer instanceof UidBatteryConsumer) {
-                timeInState =
-                        ((UidBatteryConsumer) batteryConsumer)
-                                .getTimeInProcessStateMs(processState);
-            }
-
             return addStatsAtom(
                     data,
                     sessionInfo,
@@ -1495,44 +1292,6 @@
                     powerComponentDurationMillis);
         }
 
-        private boolean addStatsForCustomComponent(
-                List<StatsEvent> data,
-                SessionInfo sessionInfo,
-                int uid,
-                @BatteryConsumer.ProcessState int processState,
-                long timeInStateMillis,
-                float totalConsumedPowerMah,
-                BatteryConsumer batteryConsumer,
-                int componentId) {
-
-            if (componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
-                throw new IllegalArgumentException("Invalid custom component id: " + componentId);
-            }
-
-            final float powerMah =
-                    (float) batteryConsumer.getConsumedPowerForCustomComponent(componentId);
-            if (powerMah == 0) {
-                return true;
-            }
-
-            final String powerComponentName =
-                    batteryConsumer.getCustomPowerComponentName(componentId);
-
-            final long powerComponentDurationMillis =
-                    batteryConsumer.getUsageDurationForCustomComponentMillis(componentId);
-
-            return addStatsAtom(
-                    data,
-                    sessionInfo,
-                    uid,
-                    processState,
-                    timeInStateMillis,
-                    powerComponentName,
-                    totalConsumedPowerMah,
-                    powerMah,
-                    powerComponentDurationMillis);
-        }
-
         /**
          * Returns true on success and false if reached max atoms capacity and no more atoms should
          * be added
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
new file mode 100644
index 0000000..f7085b4
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -0,0 +1,2415 @@
+/*
+ * 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 android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static android.os.Process.BLUETOOTH_UID;
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.NETWORK_STACK_UID;
+import static android.os.Process.NFC_UID;
+import static android.os.Process.PHONE_UID;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SE_UID;
+import static android.os.Process.SHELL_UID;
+import static android.os.Process.SYSTEM_UID;
+
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
+import static com.android.server.am.ActivityManagerService.CLEAR_DNS_CACHE_MSG;
+import static com.android.server.am.ActivityManagerService.HANDLE_TRUST_STORAGE_UPDATE_MSG;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityManagerService.UPDATE_HTTP_PROXY_MSG;
+import static com.android.server.am.ActivityManagerService.UPDATE_TIME_PREFERENCE_MSG;
+import static com.android.server.am.ActivityManagerService.UPDATE_TIME_ZONE;
+import static com.android.server.am.ActivityManagerService.checkComponentPermission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.ApplicationExitInfo;
+import android.app.ApplicationThreadConstants;
+import android.app.BackgroundStartPrivileges;
+import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
+import android.app.compat.CompatChanges;
+import android.appwidget.AppWidgetManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
+import android.media.audiofx.AudioEffect;
+import android.net.ConnectivityManager;
+import android.net.Proxy;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.BinderProxy;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.IntentResolver;
+import com.android.server.LocalManagerRegistry;
+import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
+import com.android.server.pm.Computer;
+import com.android.server.pm.SaferIntentUtils;
+import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.snapshot.PackageDataSnapshot;
+import com.android.server.sdksandbox.SdkSandboxManagerLocal;
+import com.android.server.utils.Slogf;
+
+import dalvik.annotation.optimization.NeverCompile;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+class BroadcastController {
+    private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
+
+    /**
+     * It is now required for apps to explicitly set either
+     * {@link android.content.Context#RECEIVER_EXPORTED} or
+     * {@link android.content.Context#RECEIVER_NOT_EXPORTED} when registering a receiver for an
+     * unprotected broadcast in code.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
+
+    // Maximum number of receivers an app can register.
+    private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000;
+
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final ActivityManagerService mService;
+    @NonNull
+    private BroadcastQueue mBroadcastQueue;
+
+    @GuardedBy("mService")
+    BroadcastStats mLastBroadcastStats;
+
+    @GuardedBy("mService")
+    BroadcastStats mCurBroadcastStats;
+
+    /**
+     * Broadcast actions that will always be deliverable to unlaunched/background apps
+     */
+    @GuardedBy("mService")
+    private ArraySet<String> mBackgroundLaunchBroadcasts;
+
+    /**
+     * State of all active sticky broadcasts per user.  Keys are the action of the
+     * sticky Intent, values are an ArrayList of all broadcasted intents with
+     * that action (which should usually be one).  The SparseArray is keyed
+     * by the user ID the sticky is for, and can include UserHandle.USER_ALL
+     * for stickies that are sent to all users.
+     */
+    @GuardedBy("mStickyBroadcasts")
+    final SparseArray<ArrayMap<String, ArrayList<StickyBroadcast>>> mStickyBroadcasts =
+            new SparseArray<>();
+
+    /**
+     * Keeps track of all IIntentReceivers that have been registered for broadcasts.
+     * Hash keys are the receiver IBinder, hash value is a ReceiverList.
+     */
+    @GuardedBy("mService")
+    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
+
+    /**
+     * Resolver for broadcast intents to registered receivers.
+     * Holds BroadcastFilter (subclass of IntentFilter).
+     */
+    final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver =
+            new IntentResolver<>() {
+        @Override
+        protected boolean allowFilterResult(
+                BroadcastFilter filter, List<BroadcastFilter> dest) {
+            IBinder target = filter.receiverList.receiver.asBinder();
+            for (int i = dest.size() - 1; i >= 0; i--) {
+                if (dest.get(i).receiverList.receiver.asBinder() == target) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        @Override
+        protected BroadcastFilter newResult(@NonNull Computer computer, BroadcastFilter filter,
+                int match, int userId, long customFlags) {
+            if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
+                    || userId == filter.owningUserId) {
+                return super.newResult(computer, filter, match, userId, customFlags);
+            }
+            return null;
+        }
+
+        @Override
+        protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) {
+            return input;
+        }
+
+        @Override
+        protected BroadcastFilter[] newArray(int size) {
+            return new BroadcastFilter[size];
+        }
+
+        @Override
+        protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
+            return packageName.equals(filter.packageName);
+        }
+    };
+
+    BroadcastController(Context context, ActivityManagerService service, BroadcastQueue queue) {
+        mContext = context;
+        mService = service;
+        mBroadcastQueue = queue;
+    }
+
+    void setBroadcastQueueForTest(BroadcastQueue broadcastQueue) {
+        mBroadcastQueue = broadcastQueue;
+    }
+
+    Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
+            String callerFeatureId, String receiverId, IIntentReceiver receiver,
+            IntentFilter filter, String permission, int userId, int flags) {
+        traceRegistrationBegin(receiverId, receiver, filter, userId);
+        try {
+            return registerReceiverWithFeatureTraced(caller, callerPackage, callerFeatureId,
+                    receiverId, receiver, filter, permission, userId, flags);
+        } finally {
+            traceRegistrationEnd();
+        }
+    }
+
+    private static void traceRegistrationBegin(String receiverId, IIntentReceiver receiver,
+            IntentFilter filter, int userId) {
+        if (!Flags.traceReceiverRegistration()) {
+            return;
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            final StringBuilder sb = new StringBuilder("registerReceiver: ");
+            sb.append(Binder.getCallingUid()); sb.append('/');
+            sb.append(receiverId == null ? "null" : receiverId); sb.append('/');
+            final int actionsCount = filter.safeCountActions();
+            if (actionsCount > 0) {
+                for (int i = 0; i < actionsCount; ++i) {
+                    sb.append(filter.getAction(i));
+                    if (i != actionsCount - 1) sb.append(',');
+                }
+            } else {
+                sb.append("null");
+            }
+            sb.append('/');
+            sb.append('u'); sb.append(userId); sb.append('/');
+            sb.append(receiver == null ? "null" : receiver.asBinder());
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, sb.toString());
+        }
+    }
+
+    private static void traceRegistrationEnd() {
+        if (!Flags.traceReceiverRegistration()) {
+            return;
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    private Intent registerReceiverWithFeatureTraced(IApplicationThread caller,
+            String callerPackage, String callerFeatureId, String receiverId,
+            IIntentReceiver receiver, IntentFilter filter, String permission,
+            int userId, int flags) {
+        mService.enforceNotIsolatedCaller("registerReceiver");
+        ArrayList<StickyBroadcast> stickyBroadcasts = null;
+        ProcessRecord callerApp = null;
+        final boolean visibleToInstantApps =
+                (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
+
+        int callingUid;
+        int callingPid;
+        boolean instantApp;
+        synchronized (mService.mProcLock) {
+            callerApp = mService.getRecordForAppLOSP(caller);
+            if (callerApp == null) {
+                Slog.w(TAG, "registerReceiverWithFeature: no app for " + caller);
+                return null;
+            }
+            if (callerApp.info.uid != SYSTEM_UID
+                    && !callerApp.getPkgList().containsKey(callerPackage)
+                    && !"android".equals(callerPackage)) {
+                throw new SecurityException("Given caller package " + callerPackage
+                        + " is not running in process " + callerApp);
+            }
+            callingUid = callerApp.info.uid;
+            callingPid = callerApp.getPid();
+
+            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
+        }
+        userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+                ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
+
+        // Warn if system internals are registering for important broadcasts
+        // without also using a priority to ensure they process the event
+        // before normal apps hear about it
+        if (UserHandle.isCore(callingUid)) {
+            final int priority = filter.getPriority();
+            final boolean systemPriority = (priority >= IntentFilter.SYSTEM_HIGH_PRIORITY)
+                    || (priority <= IntentFilter.SYSTEM_LOW_PRIORITY);
+            if (!systemPriority) {
+                final int N = filter.countActions();
+                for (int i = 0; i < N; i++) {
+                    // TODO: expand to additional important broadcasts over time
+                    final String action = filter.getAction(i);
+                    if (action.startsWith("android.intent.action.USER_")
+                            || action.startsWith("android.intent.action.PACKAGE_")
+                            || action.startsWith("android.intent.action.UID_")
+                            || action.startsWith("android.intent.action.EXTERNAL_")
+                            || action.startsWith("android.bluetooth.")
+                            || action.equals(Intent.ACTION_SHUTDOWN)) {
+                        if (DEBUG_BROADCAST) {
+                            Slog.wtf(TAG,
+                                    "System internals registering for " + filter.toLongString()
+                                            + " with app priority; this will race with apps!",
+                                    new Throwable());
+                        }
+
+                        // When undefined, assume that system internals need
+                        // to hear about the event first; they can use
+                        // SYSTEM_LOW_PRIORITY if they need to hear last
+                        if (priority == 0) {
+                            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        Iterator<String> actions = filter.actionsIterator();
+        if (actions == null) {
+            ArrayList<String> noAction = new ArrayList<String>(1);
+            noAction.add(null);
+            actions = noAction.iterator();
+        }
+        boolean onlyProtectedBroadcasts = true;
+
+        // Collect stickies of users and check if broadcast is only registered for protected
+        // broadcasts
+        int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
+        synchronized (mStickyBroadcasts) {
+            while (actions.hasNext()) {
+                String action = actions.next();
+                for (int id : userIds) {
+                    ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+                            mStickyBroadcasts.get(id);
+                    if (stickies != null) {
+                        ArrayList<StickyBroadcast> broadcasts = stickies.get(action);
+                        if (broadcasts != null) {
+                            if (stickyBroadcasts == null) {
+                                stickyBroadcasts = new ArrayList<>();
+                            }
+                            stickyBroadcasts.addAll(broadcasts);
+                        }
+                    }
+                }
+                if (onlyProtectedBroadcasts) {
+                    try {
+                        onlyProtectedBroadcasts &=
+                                AppGlobals.getPackageManager().isProtectedBroadcast(action);
+                    } catch (RemoteException e) {
+                        onlyProtectedBroadcasts = false;
+                        Slog.w(TAG, "Remote exception", e);
+                    }
+                }
+            }
+        }
+
+        if (Process.isSdkSandboxUid(Binder.getCallingUid())) {
+            SdkSandboxManagerLocal sdkSandboxManagerLocal =
+                    LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
+            if (sdkSandboxManagerLocal == null) {
+                throw new IllegalStateException("SdkSandboxManagerLocal not found when checking"
+                        + " whether SDK sandbox uid can register to broadcast receivers.");
+            }
+            if (!sdkSandboxManagerLocal.canRegisterBroadcastReceiver(
+                    /*IntentFilter=*/ filter, flags, onlyProtectedBroadcasts)) {
+                throw new SecurityException("SDK sandbox not allowed to register receiver"
+                        + " with the given IntentFilter: " + filter.toLongString());
+            }
+        }
+
+        // If the change is enabled, but neither exported or not exported is set, we need to log
+        // an error so the consumer can know to explicitly set the value for their flag.
+        // If the caller is registering for a sticky broadcast with a null receiver, we won't
+        // require a flag
+        final boolean explicitExportStateDefined =
+                (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED)) != 0;
+        if (((flags & Context.RECEIVER_EXPORTED) != 0) && (
+                (flags & Context.RECEIVER_NOT_EXPORTED) != 0)) {
+            throw new IllegalArgumentException(
+                    "Receiver can't specify both RECEIVER_EXPORTED and RECEIVER_NOT_EXPORTED"
+                            + "flag");
+        }
+
+        // Don't enforce the flag check if we're EITHER registering for only protected
+        // broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
+        // not be used generally, so we will be marking them as exported by default
+        boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
+                DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+
+        // A receiver that is visible to instant apps must also be exported.
+        final boolean unexportedReceiverVisibleToInstantApps =
+                ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
+                        (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
+        if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
+            throw new IllegalArgumentException(
+                    "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
+                            + "RECEIVER_NOT_EXPORTED flag");
+        }
+
+        if (!onlyProtectedBroadcasts) {
+            if (receiver == null && !explicitExportStateDefined) {
+                // sticky broadcast, no flag specified (flag isn't required)
+                flags |= Context.RECEIVER_EXPORTED;
+            } else if (requireExplicitFlagForDynamicReceivers && !explicitExportStateDefined) {
+                throw new SecurityException(
+                        callerPackage + ": One of RECEIVER_EXPORTED or "
+                                + "RECEIVER_NOT_EXPORTED should be specified when a receiver "
+                                + "isn't being registered exclusively for system broadcasts");
+                // Assume default behavior-- flag check is not enforced
+            } else if (!requireExplicitFlagForDynamicReceivers && (
+                    (flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
+                // Change is not enabled, assume exported unless otherwise specified.
+                flags |= Context.RECEIVER_EXPORTED;
+            }
+        } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+            flags |= Context.RECEIVER_EXPORTED;
+        }
+
+        // Dynamic receivers are exported by default for versions prior to T
+        final boolean exported = (flags & Context.RECEIVER_EXPORTED) != 0;
+
+        ArrayList<StickyBroadcast> allSticky = null;
+        if (stickyBroadcasts != null) {
+            final ContentResolver resolver = mContext.getContentResolver();
+            // Look for any matching sticky broadcasts...
+            for (int i = 0, N = stickyBroadcasts.size(); i < N; i++) {
+                final StickyBroadcast broadcast = stickyBroadcasts.get(i);
+                Intent intent = broadcast.intent;
+                // Don't provided intents that aren't available to instant apps.
+                if (instantApp && (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS)
+                        == 0) {
+                    continue;
+                }
+                // If intent has scheme "content", it will need to access
+                // provider that needs to lock mProviderMap in ActivityThread
+                // and also it may need to wait application response, so we
+                // cannot lock ActivityManagerService here.
+                final int match;
+                if (Flags.avoidResolvingType()) {
+                    match = filter.match(intent.getAction(), broadcast.resolvedDataType,
+                            intent.getScheme(), intent.getData(), intent.getCategories(),
+                            TAG, false /* supportsWildcards */, null /* ignoreActions */,
+                            intent.getExtras());
+                } else {
+                    match = filter.match(resolver, intent, true, TAG);
+                }
+                if (match >= 0) {
+                    if (allSticky == null) {
+                        allSticky = new ArrayList<>();
+                    }
+                    allSticky.add(broadcast);
+                }
+            }
+        }
+
+        // The first sticky in the list is returned directly back to the client.
+        Intent sticky = allSticky != null ? allSticky.get(0).intent : null;
+        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
+        if (receiver == null) {
+            return sticky;
+        }
+
+        // SafetyNet logging for b/177931370. If any process other than system_server tries to
+        // listen to this broadcast action, then log it.
+        if (callingPid != Process.myPid()) {
+            if (filter.hasAction("com.android.server.net.action.SNOOZE_WARNING")
+                    || filter.hasAction("com.android.server.net.action.SNOOZE_RAPID")) {
+                EventLog.writeEvent(0x534e4554, "177931370", callingUid, "");
+            }
+        }
+
+        synchronized (mService) {
+            IApplicationThread thread;
+            if (callerApp != null && ((thread = callerApp.getThread()) == null
+                    || thread.asBinder() != caller.asBinder())) {
+                // Original caller already died
+                return null;
+            }
+            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
+            if (rl == null) {
+                rl = new ReceiverList(mService, callerApp, callingPid, callingUid,
+                        userId, receiver);
+                if (rl.app != null) {
+                    final int totalReceiversForApp = rl.app.mReceivers.numberOfReceivers();
+                    if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
+                        throw new IllegalStateException("Too many receivers, total of "
+                                + totalReceiversForApp + ", registered for pid: "
+                                + rl.pid + ", callerPackage: " + callerPackage);
+                    }
+                    rl.app.mReceivers.addReceiver(rl);
+                } else {
+                    try {
+                        receiver.asBinder().linkToDeath(rl, 0);
+                    } catch (RemoteException e) {
+                        return sticky;
+                    }
+                    rl.linkedToDeath = true;
+                }
+                mRegisteredReceivers.put(receiver.asBinder(), rl);
+            } else if (rl.uid != callingUid) {
+                throw new IllegalArgumentException(
+                        "Receiver requested to register for uid " + callingUid
+                                + " was previously registered for uid " + rl.uid
+                                + " callerPackage is " + callerPackage);
+            } else if (rl.pid != callingPid) {
+                throw new IllegalArgumentException(
+                        "Receiver requested to register for pid " + callingPid
+                                + " was previously registered for pid " + rl.pid
+                                + " callerPackage is " + callerPackage);
+            } else if (rl.userId != userId) {
+                throw new IllegalArgumentException(
+                        "Receiver requested to register for user " + userId
+                                + " was previously registered for user " + rl.userId
+                                + " callerPackage is " + callerPackage);
+            }
+            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
+                    receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps,
+                    exported);
+            if (rl.containsFilter(filter)) {
+                Slog.w(TAG, "Receiver with filter " + filter
+                        + " already registered for pid " + rl.pid
+                        + ", callerPackage is " + callerPackage);
+            } else {
+                rl.add(bf);
+                if (!bf.debugCheck()) {
+                    Slog.w(TAG, "==> For Dynamic broadcast");
+                }
+                mReceiverResolver.addFilter(mService.getPackageManagerInternal().snapshot(), bf);
+            }
+
+            // Enqueue broadcasts for all existing stickies that match
+            // this filter.
+            if (allSticky != null) {
+                ArrayList receivers = new ArrayList();
+                receivers.add(bf);
+                sticky = null;
+
+                final int stickyCount = allSticky.size();
+                for (int i = 0; i < stickyCount; i++) {
+                    final StickyBroadcast broadcast = allSticky.get(i);
+                    final int originalStickyCallingUid = allSticky.get(i).originalCallingUid;
+                    // TODO(b/281889567): consider using checkComponentPermission instead of
+                    //  canAccessUnexportedComponents
+                    if (sticky == null && (exported || originalStickyCallingUid == callingUid
+                            || ActivityManager.canAccessUnexportedComponents(
+                            originalStickyCallingUid))) {
+                        sticky = broadcast.intent;
+                    }
+                    BroadcastQueue queue = mBroadcastQueue;
+                    BroadcastRecord r = new BroadcastRecord(queue, broadcast.intent, null,
+                            null, null, -1, -1, false, null, null, null, null, OP_NONE,
+                            BroadcastOptions.makeWithDeferUntilActive(broadcast.deferUntilActive),
+                            receivers, null, null, 0, null, null, false, true, true, -1,
+                            originalStickyCallingUid, BackgroundStartPrivileges.NONE,
+                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
+                            null /* filterExtrasForReceiver */,
+                            broadcast.originalCallingAppProcessState);
+                    queue.enqueueBroadcastLocked(r);
+                }
+            }
+
+            return sticky;
+        }
+    }
+
+    void unregisterReceiver(IIntentReceiver receiver) {
+        traceUnregistrationBegin(receiver);
+        try {
+            unregisterReceiverTraced(receiver);
+        } finally {
+            traceUnregistrationEnd();
+        }
+    }
+
+    private static void traceUnregistrationBegin(IIntentReceiver receiver) {
+        if (!Flags.traceReceiverRegistration()) {
+            return;
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                    TextUtils.formatSimple("unregisterReceiver: %d/%s", Binder.getCallingUid(),
+                            receiver == null ? "null" : receiver.asBinder()));
+        }
+    }
+
+    private static void traceUnregistrationEnd() {
+        if (!Flags.traceReceiverRegistration()) {
+            return;
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
+    private void unregisterReceiverTraced(IIntentReceiver receiver) {
+        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Unregister receiver: " + receiver);
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            boolean doTrim = false;
+            synchronized (mService) {
+                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
+                if (rl != null) {
+                    final BroadcastRecord r = rl.curBroadcast;
+                    if (r != null) {
+                        final boolean doNext = r.queue.finishReceiverLocked(
+                                rl.app, r.resultCode, r.resultData, r.resultExtras,
+                                r.resultAbort, false);
+                        if (doNext) {
+                            doTrim = true;
+                        }
+                    }
+                    if (rl.app != null) {
+                        rl.app.mReceivers.removeReceiver(rl);
+                    }
+                    removeReceiverLocked(rl);
+                    if (rl.linkedToDeath) {
+                        rl.linkedToDeath = false;
+                        rl.receiver.asBinder().unlinkToDeath(rl, 0);
+                    }
+                }
+
+                // If we actually concluded any broadcasts, we might now be able
+                // to trim the recipients' apps from our working set
+                if (doTrim) {
+                    mService.trimApplicationsLocked(false, OOM_ADJ_REASON_FINISH_RECEIVER);
+                    return;
+                }
+            }
+
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void removeReceiverLocked(ReceiverList rl) {
+        mRegisteredReceivers.remove(rl.receiver.asBinder());
+        for (int i = rl.size() - 1; i >= 0; i--) {
+            mReceiverResolver.removeFilter(rl.get(i));
+        }
+    }
+
+    int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
+            Intent intent, String resolvedType, IIntentReceiver resultTo,
+            int resultCode, String resultData, Bundle resultExtras,
+            String[] requiredPermissions, String[] excludedPermissions,
+            String[] excludedPackages, int appOp, Bundle bOptions,
+            boolean serialized, boolean sticky, int userId) {
+        mService.enforceNotIsolatedCaller("broadcastIntent");
+
+        synchronized (mService) {
+            intent = verifyBroadcastLocked(intent);
+
+            final ProcessRecord callerApp = mService.getRecordForAppLOSP(caller);
+            final int callingPid = Binder.getCallingPid();
+            final int callingUid = Binder.getCallingUid();
+
+            // We're delivering the result to the caller
+            final ProcessRecord resultToApp = callerApp;
+
+            // Permission regimes around sender-supplied broadcast options.
+            enforceBroadcastOptionPermissionsInternal(bOptions, callingUid);
+
+            final ComponentName cn = intent.getComponent();
+
+            Trace.traceBegin(
+                    Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                    "broadcastIntent:" + (cn != null ? cn.toString() : intent.getAction()));
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                return broadcastIntentLocked(callerApp,
+                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
+                        intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
+                        resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
+                        appOp, bOptions, serialized, sticky, callingPid, callingUid, callingUid,
+                        callingPid, userId, BackgroundStartPrivileges.NONE, null, null);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            }
+        }
+    }
+
+    // Not the binder call surface
+    int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
+            int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode,
+            String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
+            boolean serialized, boolean sticky, int userId,
+            BackgroundStartPrivileges backgroundStartPrivileges,
+            @Nullable int[] broadcastAllowList) {
+        synchronized (mService) {
+            intent = verifyBroadcastLocked(intent);
+
+            final long origId = Binder.clearCallingIdentity();
+            String[] requiredPermissions = requiredPermission == null ? null
+                    : new String[] {requiredPermission};
+            try {
+                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
+                        resultToApp, resultTo, resultCode, resultData, resultExtras,
+                        requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
+                        uid, realCallingUid, realCallingPid, userId,
+                        backgroundStartPrivileges, broadcastAllowList,
+                        null /* filterExtrasForReceiver */);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
+            @Nullable String callerFeatureId, Intent intent, String resolvedType,
+            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
+            Bundle resultExtras, String[] requiredPermissions,
+            String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
+            boolean ordered, boolean sticky, int callingPid, int callingUid,
+            int realCallingUid, int realCallingPid, int userId,
+            BackgroundStartPrivileges backgroundStartPrivileges,
+            @Nullable int[] broadcastAllowList,
+            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
+        final int cookie = traceBroadcastIntentBegin(intent, resultTo, ordered, sticky,
+                callingUid, realCallingUid, userId);
+        try {
+            final BroadcastSentEventRecord broadcastSentEventRecord =
+                    new BroadcastSentEventRecord();
+            final int res = broadcastIntentLockedTraced(callerApp, callerPackage, callerFeatureId,
+                    intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
+                    resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
+                    appOp, BroadcastOptions.fromBundleNullable(bOptions), ordered, sticky,
+                    callingPid, callingUid, realCallingUid, realCallingPid, userId,
+                    backgroundStartPrivileges, broadcastAllowList, filterExtrasForReceiver,
+                    broadcastSentEventRecord);
+            broadcastSentEventRecord.setResult(res);
+            broadcastSentEventRecord.logToStatsd();
+            return res;
+        } finally {
+            traceBroadcastIntentEnd(cookie);
+        }
+    }
+
+    private static int traceBroadcastIntentBegin(Intent intent, IIntentReceiver resultTo,
+            boolean ordered, boolean sticky, int callingUid, int realCallingUid, int userId) {
+        if (!Flags.traceReceiverRegistration()) {
+            return BroadcastQueue.traceBegin("broadcastIntentLockedTraced");
+        }
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            final StringBuilder sb = new StringBuilder("broadcastIntent: ");
+            sb.append(callingUid); sb.append('/');
+            final String action = intent.getAction();
+            sb.append(action == null ? null : action); sb.append('/');
+            sb.append("0x"); sb.append(Integer.toHexString(intent.getFlags())); sb.append('/');
+            sb.append(ordered ? "O" : "_");
+            sb.append(sticky ? "S" : "_");
+            sb.append(resultTo != null ? "C" : "_");
+            sb.append('/');
+            sb.append('u'); sb.append(userId);
+            if (callingUid != realCallingUid) {
+                sb.append('/');
+                sb.append("sender="); sb.append(realCallingUid);
+            }
+            return BroadcastQueue.traceBegin(sb.toString());
+        }
+        return 0;
+    }
+
+    private static void traceBroadcastIntentEnd(int cookie) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+            BroadcastQueue.traceEnd(cookie);
+        }
+    }
+
+    @GuardedBy("mService")
+    final int broadcastIntentLockedTraced(ProcessRecord callerApp, String callerPackage,
+            @Nullable String callerFeatureId, Intent intent, String resolvedType,
+            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
+            Bundle resultExtras, String[] requiredPermissions,
+            String[] excludedPermissions, String[] excludedPackages, int appOp,
+            BroadcastOptions brOptions, boolean ordered, boolean sticky, int callingPid,
+            int callingUid, int realCallingUid, int realCallingPid, int userId,
+            BackgroundStartPrivileges backgroundStartPrivileges,
+            @Nullable int[] broadcastAllowList,
+            @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
+            @NonNull BroadcastSentEventRecord broadcastSentEventRecord) {
+        // Ensure all internal loopers are registered for idle checks
+        BroadcastLoopers.addMyLooper();
+
+        if (Process.isSdkSandboxUid(realCallingUid)) {
+            final SdkSandboxManagerLocal sdkSandboxManagerLocal = LocalManagerRegistry.getManager(
+                    SdkSandboxManagerLocal.class);
+            if (sdkSandboxManagerLocal == null) {
+                throw new IllegalStateException("SdkSandboxManagerLocal not found when sending"
+                        + " a broadcast from an SDK sandbox uid.");
+            }
+            if (!sdkSandboxManagerLocal.canSendBroadcast(intent)) {
+                throw new SecurityException(
+                        "Intent " + intent.getAction() + " may not be broadcast from an SDK sandbox"
+                                + " uid. Given caller package " + callerPackage
+                                + " (pid=" + callingPid + ", realCallingUid=" + realCallingUid
+                                + ", callingUid= " + callingUid + ")");
+            }
+        }
+
+        if ((resultTo != null) && (resultToApp == null)) {
+            if (resultTo.asBinder() instanceof BinderProxy) {
+                // Warn when requesting results without a way to deliver them
+                Slog.wtf(TAG, "Sending broadcast " + intent.getAction()
+                        + " with resultTo requires resultToApp", new Throwable());
+            } else {
+                // If not a BinderProxy above, then resultTo is an in-process
+                // receiver, so splice in system_server process
+                resultToApp = mService.getProcessRecordLocked("system", SYSTEM_UID);
+            }
+        }
+
+        intent = new Intent(intent);
+        broadcastSentEventRecord.setIntent(intent);
+        broadcastSentEventRecord.setOriginalIntentFlags(intent.getFlags());
+        broadcastSentEventRecord.setSenderUid(callingUid);
+        broadcastSentEventRecord.setRealSenderUid(realCallingUid);
+        broadcastSentEventRecord.setSticky(sticky);
+        broadcastSentEventRecord.setOrdered(ordered);
+        broadcastSentEventRecord.setResultRequested(resultTo != null);
+        final int callerAppProcessState = getRealProcessStateLocked(callerApp, realCallingPid);
+        broadcastSentEventRecord.setSenderProcState(callerAppProcessState);
+        broadcastSentEventRecord.setSenderUidState(getRealUidStateLocked(callerApp,
+                realCallingPid));
+
+        final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
+        // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
+        if (callerInstantApp) {
+            intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+        }
+
+        if (userId == UserHandle.USER_ALL && broadcastAllowList != null) {
+            Slog.e(TAG, "broadcastAllowList only applies when sending to individual users. "
+                    + "Assuming restrictive whitelist.");
+            broadcastAllowList = new int[]{};
+        }
+
+        // By default broadcasts do not go to stopped apps.
+        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
+
+        // If we have not finished booting, don't allow this to launch new processes.
+        if (!mService.mProcessesReady
+                && (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        }
+
+        if (DEBUG_BROADCAST_LIGHT) {
+            Slog.v(TAG_BROADCAST,
+                    (sticky ? "Broadcast sticky: " : "Broadcast: ") + intent
+                            + " ordered=" + ordered + " userid=" + userId
+                            + " options=" + (brOptions == null ? "null" : brOptions.toBundle()));
+        }
+        if ((resultTo != null) && !ordered) {
+            if (!UserHandle.isCore(callingUid)) {
+                String msg = "Unauthorized unordered resultTo broadcast "
+                        + intent + " sent from uid " + callingUid;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+        }
+
+        userId = mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+                ALLOW_NON_FULL, "broadcast", callerPackage);
+
+        // Make sure that the user who is receiving this broadcast or its parent is running.
+        // If not, we will just skip it. Make an exception for shutdown broadcasts, upgrade steps.
+        if (userId != UserHandle.USER_ALL && !mService.mUserController.isUserOrItsParentRunning(
+                userId)) {
+            if ((callingUid != SYSTEM_UID
+                    || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
+                    && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
+                Slog.w(TAG, "Skipping broadcast of " + intent
+                        + ": user " + userId + " and its parent (if any) are stopped");
+                scheduleCanceledResultTo(resultToApp, resultTo, intent, userId,
+                        brOptions, callingUid, callerPackage);
+                return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
+            }
+        }
+
+        final String action = intent.getAction();
+        if (brOptions != null) {
+            if (brOptions.getTemporaryAppAllowlistDuration() > 0) {
+                // See if the caller is allowed to do this.  Note we are checking against
+                // the actual real caller (not whoever provided the operation as say a
+                // PendingIntent), because that who is actually supplied the arguments.
+                if (checkComponentPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED
+                        && checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED
+                        && checkComponentPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    String msg = "Permission Denial: " + intent.getAction()
+                            + " broadcast from " + callerPackage + " (pid=" + callingPid
+                            + ", uid=" + callingUid + ")"
+                            + " requires "
+                            + CHANGE_DEVICE_IDLE_TEMP_WHITELIST + " or "
+                            + START_ACTIVITIES_FROM_BACKGROUND + " or "
+                            + START_FOREGROUND_SERVICES_FROM_BACKGROUND;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            }
+            if (brOptions.isDontSendToRestrictedApps()
+                    && !mService.isUidActiveLOSP(callingUid)
+                    && mService.isBackgroundRestrictedNoCheck(callingUid, callerPackage)) {
+                Slog.i(TAG, "Not sending broadcast " + action + " - app " + callerPackage
+                        + " has background restrictions");
+                return ActivityManager.START_CANCELED;
+            }
+            if (brOptions.allowsBackgroundActivityStarts()) {
+                // See if the caller is allowed to do this.  Note we are checking against
+                // the actual real caller (not whoever provided the operation as say a
+                // PendingIntent), because that who is actually supplied the arguments.
+                if (checkComponentPermission(
+                        android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
+                        realCallingPid, realCallingUid, -1, true)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    String msg = "Permission Denial: " + intent.getAction()
+                            + " broadcast from " + callerPackage + " (pid=" + callingPid
+                            + ", uid=" + callingUid + ")"
+                            + " requires "
+                            + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                } else {
+                    // We set the token to null since if it wasn't for it we'd allow anyway here
+                    backgroundStartPrivileges = BackgroundStartPrivileges.ALLOW_BAL;
+                }
+            }
+
+            if (brOptions.getIdForResponseEvent() > 0) {
+                mService.enforcePermission(
+                        android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
+                        callingPid, callingUid, "recordResponseEventWhileInBackground");
+            }
+        }
+
+        // Verify that protected broadcasts are only being sent by system code,
+        // and that system code is only sending protected broadcasts.
+        final boolean isProtectedBroadcast;
+        try {
+            isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Remote exception", e);
+            scheduleCanceledResultTo(resultToApp, resultTo, intent,
+                    userId, brOptions, callingUid, callerPackage);
+            return ActivityManager.BROADCAST_SUCCESS;
+        }
+
+        final boolean isCallerSystem;
+        switch (UserHandle.getAppId(callingUid)) {
+            case ROOT_UID:
+            case SYSTEM_UID:
+            case PHONE_UID:
+            case BLUETOOTH_UID:
+            case NFC_UID:
+            case SE_UID:
+            case NETWORK_STACK_UID:
+                isCallerSystem = true;
+                break;
+            default:
+                isCallerSystem = (callerApp != null) && callerApp.isPersistent();
+                break;
+        }
+
+        // First line security check before anything else: stop non-system apps from
+        // sending protected broadcasts.
+        if (!isCallerSystem) {
+            if (isProtectedBroadcast) {
+                String msg = "Permission Denial: not allowed to send broadcast "
+                        + action + " from pid="
+                        + callingPid + ", uid=" + callingUid;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+
+            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+                // Special case for compatibility: we don't want apps to send this,
+                // but historically it has not been protected and apps may be using it
+                // to poke their own app widget.  So, instead of making it protected,
+                // just limit it to the caller.
+                if (callerPackage == null) {
+                    String msg = "Permission Denial: not allowed to send broadcast "
+                            + action + " from unknown caller.";
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                } else if (intent.getComponent() != null) {
+                    // They are good enough to send to an explicit component...  verify
+                    // it is being sent to the calling app.
+                    if (!intent.getComponent().getPackageName().equals(
+                            callerPackage)) {
+                        String msg = "Permission Denial: not allowed to send broadcast "
+                                + action + " to "
+                                + intent.getComponent().getPackageName() + " from "
+                                + callerPackage;
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    }
+                } else {
+                    // Limit broadcast to their own package.
+                    intent.setPackage(callerPackage);
+                }
+            }
+        }
+
+        boolean timeoutExempt = false;
+
+        if (action != null) {
+            if (getBackgroundLaunchBroadcasts().contains(action)) {
+                if (DEBUG_BACKGROUND_CHECK) {
+                    Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
+                }
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            }
+
+            // TODO: b/329211459 - Remove this after background remote intent is fixed.
+            if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+                    && getWearRemoteIntentAction().equals(action)) {
+                final int callerProcState = callerApp != null
+                        ? callerApp.getCurProcState()
+                        : ActivityManager.PROCESS_STATE_NONEXISTENT;
+                if (ActivityManager.RunningAppProcessInfo.procStateToImportance(callerProcState)
+                        > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+                    return ActivityManager.START_CANCELED;
+                }
+            }
+
+            switch (action) {
+                case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
+                    UserManagerInternal umInternal = LocalServices.getService(
+                            UserManagerInternal.class);
+                    UserInfo userInfo = umInternal.getUserInfo(userId);
+                    if (userInfo != null && userInfo.isCloneProfile()) {
+                        userId = umInternal.getProfileParentId(userId);
+                    }
+                    break;
+                case Intent.ACTION_UID_REMOVED:
+                case Intent.ACTION_PACKAGE_REMOVED:
+                case Intent.ACTION_PACKAGE_CHANGED:
+                case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+                case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+                case Intent.ACTION_PACKAGES_SUSPENDED:
+                case Intent.ACTION_PACKAGES_UNSUSPENDED:
+                    // Handle special intents: if this broadcast is from the package
+                    // manager about a package being removed, we need to remove all of
+                    // its activities from the history stack.
+                    if (checkComponentPermission(
+                            android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
+                            callingPid, callingUid, -1, true)
+                            != PackageManager.PERMISSION_GRANTED) {
+                        String msg = "Permission Denial: " + intent.getAction()
+                                + " broadcast from " + callerPackage + " (pid=" + callingPid
+                                + ", uid=" + callingUid + ")"
+                                + " requires "
+                                + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    }
+                    switch (action) {
+                        case Intent.ACTION_UID_REMOVED:
+                            final int uid = getUidFromIntent(intent);
+                            if (uid >= 0) {
+                                mService.mBatteryStatsService.removeUid(uid);
+                                if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                                    mService.mAppOpsService.resetAllModes(UserHandle.getUserId(uid),
+                                            intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
+                                } else {
+                                    mService.mAppOpsService.uidRemoved(uid);
+                                    mService.mServices.onUidRemovedLocked(uid);
+                                }
+                            }
+                            break;
+                        case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
+                            // If resources are unavailable just force stop all those packages
+                            // and flush the attribute cache as well.
+                            String[] list = intent.getStringArrayExtra(
+                                    Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                            if (list != null && list.length > 0) {
+                                for (int i = 0; i < list.length; i++) {
+                                    mService.forceStopPackageLocked(list[i], -1, false, true, true,
+                                            false, false, false, userId, "storage unmount");
+                                }
+                                mService.mAtmInternal.cleanupRecentTasksForUser(
+                                        UserHandle.USER_ALL);
+                                sendPackageBroadcastLocked(
+                                        ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,
+                                        list, userId);
+                            }
+                            break;
+                        case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
+                            mService.mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
+                            break;
+                        case Intent.ACTION_PACKAGE_REMOVED:
+                        case Intent.ACTION_PACKAGE_CHANGED:
+                            Uri data = intent.getData();
+                            String ssp;
+                            if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                                boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);
+                                final boolean replacing =
+                                        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                                final boolean killProcess =
+                                        !intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);
+                                final boolean fullUninstall = removed && !replacing;
+
+                                if (removed) {
+                                    if (killProcess) {
+                                        mService.forceStopPackageLocked(ssp, UserHandle.getAppId(
+                                                        intent.getIntExtra(Intent.EXTRA_UID, -1)),
+                                                false, true, true, false, fullUninstall, false,
+                                                userId, "pkg removed");
+                                        mService.getPackageManagerInternal()
+                                                .onPackageProcessKilledForUninstall(ssp);
+                                    } else {
+                                        // Kill any app zygotes always, since they can't fork new
+                                        // processes with references to the old code
+                                        mService.forceStopAppZygoteLocked(ssp, UserHandle.getAppId(
+                                                        intent.getIntExtra(Intent.EXTRA_UID, -1)),
+                                                userId);
+                                    }
+                                    final int cmd = killProcess
+                                            ? ApplicationThreadConstants.PACKAGE_REMOVED
+                                            : ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;
+                                    sendPackageBroadcastLocked(cmd,
+                                            new String[] {ssp}, userId);
+                                    if (fullUninstall) {
+                                        // Remove all permissions granted from/to this package
+                                        mService.mUgmInternal.removeUriPermissionsForPackage(ssp,
+                                                userId, true, false);
+
+                                        mService.mAtmInternal.removeRecentTasksByPackageName(ssp,
+                                                userId);
+
+                                        mService.mServices.forceStopPackageLocked(ssp, userId);
+                                        mService.mAtmInternal.onPackageUninstalled(ssp, userId);
+                                        mService.mBatteryStatsService.notePackageUninstalled(ssp);
+                                    }
+                                } else {
+                                    if (killProcess) {
+                                        int reason;
+                                        int subReason;
+                                        if (replacing) {
+                                            reason = ApplicationExitInfo.REASON_PACKAGE_UPDATED;
+                                            subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
+                                        } else {
+                                            reason =
+                                                    ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE;
+                                            subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
+                                        }
+
+                                        final int extraUid = intent.getIntExtra(Intent.EXTRA_UID,
+                                                -1);
+                                        synchronized (mService.mProcLock) {
+                                            mService.mProcessList.killPackageProcessesLSP(ssp,
+                                                    UserHandle.getAppId(extraUid),
+                                                    userId, ProcessList.INVALID_ADJ,
+                                                    reason,
+                                                    subReason,
+                                                    "change " + ssp);
+                                        }
+                                    }
+                                    mService.cleanupDisabledPackageComponentsLocked(ssp, userId,
+                                            intent.getStringArrayExtra(
+                                                    Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));
+                                    mService.mServices.schedulePendingServiceStartLocked(
+                                            ssp, userId);
+                                }
+                            }
+                            break;
+                        case Intent.ACTION_PACKAGES_SUSPENDED:
+                        case Intent.ACTION_PACKAGES_UNSUSPENDED:
+                            final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(
+                                    intent.getAction());
+                            final String[] packageNames = intent.getStringArrayExtra(
+                                    Intent.EXTRA_CHANGED_PACKAGE_LIST);
+                            final int userIdExtra = intent.getIntExtra(
+                                    Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
+                            mService.mAtmInternal.onPackagesSuspendedChanged(packageNames,
+                                    suspended, userIdExtra);
+
+                            final boolean quarantined = intent.getBooleanExtra(
+                                    Intent.EXTRA_QUARANTINED, false);
+                            if (suspended && quarantined && packageNames != null) {
+                                for (int i = 0; i < packageNames.length; i++) {
+                                    mService.forceStopPackage(packageNames[i], userId,
+                                            ActivityManager.FLAG_OR_STOPPED, "quarantined");
+                                }
+                            }
+
+                            break;
+                    }
+                    break;
+                case Intent.ACTION_PACKAGE_REPLACED: {
+                    final Uri data = intent.getData();
+                    final String ssp;
+                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                        ApplicationInfo aInfo = null;
+                        try {
+                            aInfo = AppGlobals.getPackageManager()
+                                    .getApplicationInfo(ssp, STOCK_PM_FLAGS, userId);
+                        } catch (RemoteException ignore) {
+                        }
+                        if (aInfo == null) {
+                            Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"
+                                    + " ssp=" + ssp + " data=" + data);
+                            scheduleCanceledResultTo(resultToApp, resultTo, intent,
+                                    userId, brOptions, callingUid, callerPackage);
+                            return ActivityManager.BROADCAST_SUCCESS;
+                        }
+                        mService.updateAssociationForApp(aInfo);
+                        mService.mAtmInternal.onPackageReplaced(aInfo);
+                        mService.mServices.updateServiceApplicationInfoLocked(aInfo);
+                        sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,
+                                new String[] {ssp}, userId);
+                    }
+                    break;
+                }
+                case Intent.ACTION_PACKAGE_ADDED: {
+                    // Special case for adding a package: by default turn on compatibility mode.
+                    Uri data = intent.getData();
+                    String ssp;
+                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                        final boolean replacing =
+                                intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                        mService.mAtmInternal.onPackageAdded(ssp, replacing);
+
+                        try {
+                            ApplicationInfo ai = AppGlobals.getPackageManager()
+                                    .getApplicationInfo(ssp, STOCK_PM_FLAGS, 0);
+                            mService.mBatteryStatsService.notePackageInstalled(ssp,
+                                    ai != null ? ai.longVersionCode : 0);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                    break;
+                }
+                case Intent.ACTION_PACKAGE_DATA_CLEARED: {
+                    Uri data = intent.getData();
+                    String ssp;
+                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                        mService.mAtmInternal.onPackageDataCleared(ssp, userId);
+                    }
+                    break;
+                }
+                case Intent.ACTION_TIMEZONE_CHANGED:
+                    // If this is the time zone changed action, queue up a message that will reset
+                    // the timezone of all currently running processes. This message will get
+                    // queued up before the broadcast happens.
+                    mService.mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
+                    break;
+                case Intent.ACTION_TIME_CHANGED:
+                    // EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between
+                    // the tri-state value it may contain and "unknown".
+                    // For convenience we re-use the Intent extra values.
+                    final int NO_EXTRA_VALUE_FOUND = -1;
+                    final int timeFormatPreferenceMsgValue = intent.getIntExtra(
+                            Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,
+                            NO_EXTRA_VALUE_FOUND /* defaultValue */);
+                    // Only send a message if the time preference is available.
+                    if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) {
+                        Message updateTimePreferenceMsg =
+                                mService.mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG,
+                                        timeFormatPreferenceMsgValue, 0);
+                        mService.mHandler.sendMessage(updateTimePreferenceMsg);
+                    }
+                    mService.mBatteryStatsService.noteCurrentTimeChanged();
+                    break;
+                case ConnectivityManager.ACTION_CLEAR_DNS_CACHE:
+                    mService.mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
+                    break;
+                case Proxy.PROXY_CHANGE_ACTION:
+                    mService.mHandler.sendMessage(mService.mHandler.obtainMessage(
+                            UPDATE_HTTP_PROXY_MSG));
+                    break;
+                case android.hardware.Camera.ACTION_NEW_PICTURE:
+                case android.hardware.Camera.ACTION_NEW_VIDEO:
+                    // In N we just turned these off; in O we are turing them back on partly,
+                    // only for registered receivers.  This will still address the main problem
+                    // (a spam of apps waking up when a picture is taken putting significant
+                    // memory pressure on the system at a bad point), while still allowing apps
+                    // that are already actively running to know about this happening.
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    break;
+                case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:
+                    mService.mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG);
+                    break;
+                case "com.android.launcher.action.INSTALL_SHORTCUT":
+                    // As of O, we no longer support this broadcasts, even for pre-O apps.
+                    // Apps should now be using ShortcutManager.pinRequestShortcut().
+                    Log.w(TAG, "Broadcast " + action
+                            + " no longer supported. It will not be delivered.");
+                    scheduleCanceledResultTo(resultToApp, resultTo, intent,
+                            userId, brOptions, callingUid, callerPackage);
+                    return ActivityManager.BROADCAST_SUCCESS;
+                case Intent.ACTION_PRE_BOOT_COMPLETED:
+                    timeoutExempt = true;
+                    break;
+                case Intent.ACTION_CLOSE_SYSTEM_DIALOGS:
+                    if (!mService.mAtmInternal.checkCanCloseSystemDialogs(callingPid, callingUid,
+                            callerPackage)) {
+                        scheduleCanceledResultTo(resultToApp, resultTo, intent,
+                                userId, brOptions, callingUid, callerPackage);
+                        // Returning success seems to be the pattern here
+                        return ActivityManager.BROADCAST_SUCCESS;
+                    }
+                    break;
+            }
+
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                    || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+                    || Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                final int uid = getUidFromIntent(intent);
+                if (uid != -1) {
+                    final UidRecord uidRec = mService.mProcessList.getUidRecordLOSP(uid);
+                    if (uidRec != null) {
+                        uidRec.updateHasInternetPermission();
+                    }
+                }
+            }
+        }
+
+        // Add to the sticky list if requested.
+        if (sticky) {
+            if (mService.checkPermission(android.Manifest.permission.BROADCAST_STICKY,
+                    callingPid, callingUid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                String msg =
+                        "Permission Denial: broadcastIntent() requesting a sticky broadcast from"
+                                + " pid="
+                                + callingPid
+                                + ", uid="
+                                + callingUid
+                                + " requires "
+                                + android.Manifest.permission.BROADCAST_STICKY;
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+            if (requiredPermissions != null && requiredPermissions.length > 0) {
+                Slog.w(TAG, "Can't broadcast sticky intent " + intent
+                        + " and enforce permissions " + Arrays.toString(requiredPermissions));
+                scheduleCanceledResultTo(resultToApp, resultTo, intent,
+                        userId, brOptions, callingUid, callerPackage);
+                return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
+            }
+            if (intent.getComponent() != null) {
+                throw new SecurityException(
+                        "Sticky broadcasts can't target a specific component");
+            }
+            synchronized (mStickyBroadcasts) {
+                // We use userId directly here, since the "all" target is maintained
+                // as a separate set of sticky broadcasts.
+                if (userId != UserHandle.USER_ALL) {
+                    // But first, if this is not a broadcast to all users, then
+                    // make sure it doesn't conflict with an existing broadcast to
+                    // all users.
+                    ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(
+                            UserHandle.USER_ALL);
+                    if (stickies != null) {
+                        ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+                        if (list != null) {
+                            int N = list.size();
+                            int i;
+                            for (i = 0; i < N; i++) {
+                                if (intent.filterEquals(list.get(i).intent)) {
+                                    throw new IllegalArgumentException("Sticky broadcast " + intent
+                                            + " for user " + userId
+                                            + " conflicts with existing global broadcast");
+                                }
+                            }
+                        }
+                    }
+                }
+                ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+                        mStickyBroadcasts.get(userId);
+                if (stickies == null) {
+                    stickies = new ArrayMap<>();
+                    mStickyBroadcasts.put(userId, stickies);
+                }
+                ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+                if (list == null) {
+                    list = new ArrayList<>();
+                    stickies.put(intent.getAction(), list);
+                }
+                final boolean deferUntilActive = BroadcastRecord.calculateDeferUntilActive(
+                        callingUid, brOptions, resultTo, ordered,
+                        BroadcastRecord.calculateUrgent(intent, brOptions));
+                final int stickiesCount = list.size();
+                int i;
+                for (i = 0; i < stickiesCount; i++) {
+                    if (intent.filterEquals(list.get(i).intent)) {
+                        // This sticky already exists, replace it.
+                        list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive,
+                                callingUid, callerAppProcessState, resolvedType));
+                        break;
+                    }
+                }
+                if (i >= stickiesCount) {
+                    list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
+                            callingUid, callerAppProcessState, resolvedType));
+                }
+            }
+        }
+
+        int[] users;
+        if (userId == UserHandle.USER_ALL) {
+            // Caller wants broadcast to go to all started users.
+            users = mService.mUserController.getStartedUserArray();
+        } else {
+            // Caller wants broadcast to go to one specific user.
+            users = new int[] {userId};
+        }
+
+        var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
+                true /* isReceiver */, true /* resolveForStart */, callingUid, callingPid);
+        args.platformCompat = mService.mPlatformCompat;
+
+        // Figure out who all will receive this broadcast.
+        final int cookie = BroadcastQueue.traceBegin("queryReceivers");
+        List receivers = null;
+        List<BroadcastFilter> registeredReceivers = null;
+        // Need to resolve the intent to interested receivers...
+        if ((intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+            receivers = collectReceiverComponents(
+                    intent, resolvedType, callingUid, callingPid, users, broadcastAllowList);
+        }
+        if (intent.getComponent() == null) {
+            final PackageDataSnapshot snapshot = mService.getPackageManagerInternal().snapshot();
+            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
+                // Query one target user at a time, excluding shell-restricted users
+                for (int i = 0; i < users.length; i++) {
+                    if (mService.mUserController.hasUserRestriction(
+                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
+                        continue;
+                    }
+                    List<BroadcastFilter> registeredReceiversForUser =
+                            mReceiverResolver.queryIntent(snapshot, intent,
+                                    resolvedType, false /*defaultOnly*/, users[i]);
+                    if (registeredReceivers == null) {
+                        registeredReceivers = registeredReceiversForUser;
+                    } else if (registeredReceiversForUser != null) {
+                        registeredReceivers.addAll(registeredReceiversForUser);
+                    }
+                }
+            } else {
+                registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
+                        resolvedType, false /*defaultOnly*/, userId);
+            }
+            if (registeredReceivers != null) {
+                SaferIntentUtils.blockNullAction(args, registeredReceivers);
+            }
+        }
+        BroadcastQueue.traceEnd(cookie);
+
+        final boolean replacePending =
+                (intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
+
+        if (DEBUG_BROADCAST) {
+            Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
+                    + " replacePending=" + replacePending);
+        }
+        if (registeredReceivers != null && broadcastAllowList != null) {
+            // if a uid whitelist was provided, remove anything in the application space that wasn't
+            // in it.
+            for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
+                final int owningAppId = UserHandle.getAppId(registeredReceivers.get(i).owningUid);
+                if (owningAppId >= Process.FIRST_APPLICATION_UID
+                        && Arrays.binarySearch(broadcastAllowList, owningAppId) < 0) {
+                    registeredReceivers.remove(i);
+                }
+            }
+        }
+
+        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
+
+        // Merge into one list.
+        int ir = 0;
+        if (receivers != null) {
+            // A special case for PACKAGE_ADDED: do not allow the package
+            // being added to see this broadcast.  This prevents them from
+            // using this as a back door to get run as soon as they are
+            // installed.  Maybe in the future we want to have a special install
+            // broadcast or such for apps, but we'd like to deliberately make
+            // this decision.
+            String[] skipPackages = null;
+            if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
+                    || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
+                    || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
+                Uri data = intent.getData();
+                if (data != null) {
+                    String pkgName = data.getSchemeSpecificPart();
+                    if (pkgName != null) {
+                        skipPackages = new String[] { pkgName };
+                    }
+                }
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
+                skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            }
+            if (skipPackages != null && (skipPackages.length > 0)) {
+                for (String skipPackage : skipPackages) {
+                    if (skipPackage != null) {
+                        int NT = receivers.size();
+                        for (int it = 0; it < NT; it++) {
+                            ResolveInfo curt = (ResolveInfo) receivers.get(it);
+                            if (curt.activityInfo.packageName.equals(skipPackage)) {
+                                receivers.remove(it);
+                                it--;
+                                NT--;
+                            }
+                        }
+                    }
+                }
+            }
+
+            int NT = receivers != null ? receivers.size() : 0;
+            int it = 0;
+            ResolveInfo curt = null;
+            BroadcastFilter curr = null;
+            while (it < NT && ir < NR) {
+                if (curt == null) {
+                    curt = (ResolveInfo) receivers.get(it);
+                }
+                if (curr == null) {
+                    curr = registeredReceivers.get(ir);
+                }
+                if (curr.getPriority() >= curt.priority) {
+                    // Insert this broadcast record into the final list.
+                    receivers.add(it, curr);
+                    ir++;
+                    curr = null;
+                    it++;
+                    NT++;
+                } else {
+                    // Skip to the next ResolveInfo in the final list.
+                    it++;
+                    curt = null;
+                }
+            }
+        }
+        while (ir < NR) {
+            if (receivers == null) {
+                receivers = new ArrayList();
+            }
+            receivers.add(registeredReceivers.get(ir));
+            ir++;
+        }
+
+        if (isCallerSystem) {
+            checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
+                    isProtectedBroadcast, receivers);
+        }
+
+        if ((receivers != null && receivers.size() > 0)
+                || resultTo != null) {
+            BroadcastQueue queue = mBroadcastQueue;
+            SaferIntentUtils.filterNonExportedComponents(args, receivers);
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+                    requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
+                    receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
+                    ordered, sticky, false, userId,
+                    backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver,
+                    callerAppProcessState);
+            broadcastSentEventRecord.setBroadcastRecord(r);
+
+            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
+            queue.enqueueBroadcastLocked(r);
+        } else {
+            // There was nobody interested in the broadcast, but we still want to record
+            // that it happened.
+            if (intent.getComponent() == null && intent.getPackage() == null
+                    && (intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+                // This was an implicit broadcast... let's record it for posterity.
+                addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
+            }
+        }
+
+        return ActivityManager.BROADCAST_SUCCESS;
+    }
+
+    @GuardedBy("mService")
+    private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo,
+            Intent intent, int userId, BroadcastOptions options, int callingUid,
+            String callingPackage) {
+        if (resultTo == null) {
+            return;
+        }
+        final ProcessRecord app = resultToApp;
+        final IApplicationThread thread  = (app != null) ? app.getOnewayThread() : null;
+        if (thread != null) {
+            try {
+                final boolean shareIdentity = (options != null && options.isShareIdentityEnabled());
+                thread.scheduleRegisteredReceiver(
+                        resultTo, intent, Activity.RESULT_CANCELED, null, null,
+                        false, false, true, userId, app.mState.getReportedProcState(),
+                        shareIdentity ? callingUid : Process.INVALID_UID,
+                        shareIdentity ? callingPackage : null);
+            } catch (RemoteException e) {
+                final String msg = "Failed to schedule result of " + intent + " via "
+                        + app + ": " + e;
+                app.killLocked("Can't schedule resultTo", ApplicationExitInfo.REASON_OTHER,
+                        ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
+                Slog.d(TAG, msg);
+            }
+        }
+    }
+
+    @GuardedBy("mService")
+    private int getRealProcessStateLocked(ProcessRecord app, int pid) {
+        if (app == null) {
+            synchronized (mService.mPidsSelfLocked) {
+                app = mService.mPidsSelfLocked.get(pid);
+            }
+        }
+        if (app != null && app.getThread() != null && !app.isKilled()) {
+            return app.mState.getCurProcState();
+        }
+        return PROCESS_STATE_NONEXISTENT;
+    }
+
+    @GuardedBy("mService")
+    private int getRealUidStateLocked(ProcessRecord app, int pid) {
+        if (app == null) {
+            synchronized (mService.mPidsSelfLocked) {
+                app = mService.mPidsSelfLocked.get(pid);
+            }
+        }
+        if (app != null && app.getThread() != null && !app.isKilled()) {
+            final UidRecord uidRecord = app.getUidRecord();
+            if (uidRecord != null) {
+                return uidRecord.getCurProcState();
+            }
+        }
+        return PROCESS_STATE_NONEXISTENT;
+    }
+
+    @VisibleForTesting
+    ArrayList<StickyBroadcast> getStickyBroadcastsForTest(String action, int userId) {
+        synchronized (mStickyBroadcasts) {
+            final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts =
+                    mStickyBroadcasts.get(userId);
+            if (stickyBroadcasts == null) {
+                return null;
+            }
+            return stickyBroadcasts.get(action);
+        }
+    }
+
+    void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, true, ALLOW_NON_FULL,
+                "removeStickyBroadcast", null);
+
+        if (mService.checkCallingPermission(android.Manifest.permission.BROADCAST_STICKY)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: unbroadcastIntent() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.BROADCAST_STICKY;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        synchronized (mStickyBroadcasts) {
+            ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
+            if (stickies != null) {
+                ArrayList<StickyBroadcast> list = stickies.get(intent.getAction());
+                if (list != null) {
+                    int N = list.size();
+                    int i;
+                    for (i = 0; i < N; i++) {
+                        if (intent.filterEquals(list.get(i).intent)) {
+                            list.remove(i);
+                            break;
+                        }
+                    }
+                    if (list.size() <= 0) {
+                        stickies.remove(intent.getAction());
+                    }
+                }
+                if (stickies.size() <= 0) {
+                    mStickyBroadcasts.remove(userId);
+                }
+            }
+        }
+    }
+
+    void finishReceiver(IBinder caller, int resultCode, String resultData,
+            Bundle resultExtras, boolean resultAbort, int flags) {
+        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + caller);
+
+        // Refuse possible leaked file descriptors
+        if (resultExtras != null && resultExtras.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Bundle");
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mService) {
+                final ProcessRecord callerApp = mService.getRecordForAppLOSP(caller);
+                if (callerApp == null) {
+                    Slog.w(TAG, "finishReceiver: no app for " + caller);
+                    return;
+                }
+
+                mBroadcastQueue.finishReceiverLocked(callerApp, resultCode,
+                        resultData, resultExtras, resultAbort, true);
+                // updateOomAdjLocked() will be done here
+                mService.trimApplicationsLocked(false, OOM_ADJ_REASON_FINISH_RECEIVER);
+            }
+
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
+     * @return uid from the extra field {@link Intent#EXTRA_UID} if present, Otherwise -1
+     */
+    private int getUidFromIntent(Intent intent) {
+        if (intent == null) {
+            return -1;
+        }
+        final Bundle intentExtras = intent.getExtras();
+        return intent.hasExtra(Intent.EXTRA_UID)
+                ? intentExtras.getInt(Intent.EXTRA_UID) : -1;
+    }
+
+    final void rotateBroadcastStatsIfNeededLocked() {
+        final long now = SystemClock.elapsedRealtime();
+        if (mCurBroadcastStats == null
+                || (mCurBroadcastStats.mStartRealtime + (24 * 60 * 60 * 1000) < now)) {
+            mLastBroadcastStats = mCurBroadcastStats;
+            if (mLastBroadcastStats != null) {
+                mLastBroadcastStats.mEndRealtime = SystemClock.elapsedRealtime();
+                mLastBroadcastStats.mEndUptime = SystemClock.uptimeMillis();
+            }
+            mCurBroadcastStats = new BroadcastStats();
+        }
+    }
+
+    final void addBroadcastStatLocked(String action, String srcPackage, int receiveCount,
+            int skipCount, long dispatchTime) {
+        rotateBroadcastStatsIfNeededLocked();
+        mCurBroadcastStats.addBroadcast(action, srcPackage, receiveCount, skipCount, dispatchTime);
+    }
+
+    final void addBackgroundCheckViolationLocked(String action, String targetPackage) {
+        rotateBroadcastStatsIfNeededLocked();
+        mCurBroadcastStats.addBackgroundCheckViolation(action, targetPackage);
+    }
+
+    final void notifyBroadcastFinishedLocked(@NonNull BroadcastRecord original) {
+        final ApplicationInfo info = original.callerApp != null ? original.callerApp.info : null;
+        final String callerPackage = info != null ? info.packageName : original.callerPackage;
+        if (callerPackage != null) {
+            mService.mHandler.obtainMessage(ActivityManagerService.DISPATCH_SENDING_BROADCAST_EVENT,
+                    original.callingUid, 0, callerPackage).sendToTarget();
+        }
+    }
+
+    final Intent verifyBroadcastLocked(Intent intent) {
+        if (intent != null) {
+            // Refuse possible leaked file descriptors
+            if (intent.hasFileDescriptors()) {
+                throw new IllegalArgumentException("File descriptors passed in Intent");
+            }
+            // Remove existing mismatch flag so it can be properly updated later
+            intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+        }
+
+        int flags = intent.getFlags();
+
+        if (!mService.mProcessesReady) {
+            // if the caller really truly claims to know what they're doing, go
+            // ahead and allow the broadcast without launching any receivers
+            if ((flags & Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
+                // This will be turned into a FLAG_RECEIVER_REGISTERED_ONLY later on if needed.
+            } else if ((flags & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+                Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+                        + " before boot completion");
+                throw new IllegalStateException("Cannot broadcast before boot completed");
+            }
+        }
+
+        if ((flags & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+            throw new IllegalArgumentException(
+                    "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+        }
+
+        if ((flags & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
+            switch (Binder.getCallingUid()) {
+                case ROOT_UID:
+                case SHELL_UID:
+                    break;
+                default:
+                    Slog.w(TAG, "Removing FLAG_RECEIVER_FROM_SHELL because caller is UID "
+                            + Binder.getCallingUid());
+                    intent.removeFlags(Intent.FLAG_RECEIVER_FROM_SHELL);
+                    break;
+            }
+        }
+
+        return intent;
+    }
+
+    private ArraySet<String> getBackgroundLaunchBroadcasts() {
+        if (mBackgroundLaunchBroadcasts == null) {
+            mBackgroundLaunchBroadcasts = SystemConfig.getInstance().getAllowImplicitBroadcasts();
+        }
+        return mBackgroundLaunchBroadcasts;
+    }
+
+    private boolean isInstantApp(ProcessRecord record, @Nullable String callerPackage, int uid) {
+        if (UserHandle.getAppId(uid) < FIRST_APPLICATION_UID) {
+            return false;
+        }
+        // Easy case -- we have the app's ProcessRecord.
+        if (record != null) {
+            return record.info.isInstantApp();
+        }
+        // Otherwise check with PackageManager.
+        IPackageManager pm = AppGlobals.getPackageManager();
+        try {
+            if (callerPackage == null) {
+                final String[] packageNames = pm.getPackagesForUid(uid);
+                if (packageNames == null || packageNames.length == 0) {
+                    throw new IllegalArgumentException("Unable to determine caller package name");
+                }
+                // Instant Apps can't use shared uids, so its safe to only check the first package.
+                callerPackage = packageNames[0];
+            }
+            mService.mAppOpsService.checkPackage(uid, callerPackage);
+            return pm.isInstantApp(callerPackage, UserHandle.getUserId(uid));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error looking up if " + callerPackage + " is an instant app.", e);
+            return true;
+        }
+    }
+
+    private String getWearRemoteIntentAction() {
+        return mContext.getResources().getString(
+                com.android.internal.R.string.config_wearRemoteIntentAction);
+    }
+
+    private void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+        mService.mProcessList.sendPackageBroadcastLocked(cmd, packages, userId);
+    }private List<ResolveInfo> collectReceiverComponents(
+            Intent intent, String resolvedType, int callingUid, int callingPid,
+            int[] users, int[] broadcastAllowList) {
+        // TODO: come back and remove this assumption to triage all broadcasts
+        long pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
+
+        List<ResolveInfo> receivers = null;
+        HashSet<ComponentName> singleUserReceivers = null;
+        boolean scannedFirstReceivers = false;
+        for (int user : users) {
+            // Skip users that have Shell restrictions
+            if (callingUid == SHELL_UID
+                    && mService.mUserController.hasUserRestriction(
+                    UserManager.DISALLOW_DEBUGGING_FEATURES, user)) {
+                continue;
+            }
+            List<ResolveInfo> newReceivers = mService.mPackageManagerInt.queryIntentReceivers(
+                    intent, resolvedType, pmFlags, callingUid, callingPid, user, /* forSend */true);
+            if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
+                // If this is not the system user, we need to check for
+                // any receivers that should be filtered out.
+                for (int i = 0; i < newReceivers.size(); i++) {
+                    ResolveInfo ri = newReceivers.get(i);
+                    if ((ri.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
+                        newReceivers.remove(i);
+                        i--;
+                    }
+                }
+            }
+            // Replace the alias receivers with their targets.
+            if (newReceivers != null) {
+                for (int i = newReceivers.size() - 1; i >= 0; i--) {
+                    final ResolveInfo ri = newReceivers.get(i);
+                    final ComponentAliasResolver.Resolution<ResolveInfo> resolution =
+                            mService.mComponentAliasResolver.resolveReceiver(intent, ri,
+                                    resolvedType, pmFlags, user, callingUid, callingPid);
+                    if (resolution == null) {
+                        // It was an alias, but the target was not found.
+                        newReceivers.remove(i);
+                        continue;
+                    }
+                    if (resolution.isAlias()) {
+                        newReceivers.set(i, resolution.getTarget());
+                    }
+                }
+            }
+            if (newReceivers != null && newReceivers.size() == 0) {
+                newReceivers = null;
+            }
+
+            if (receivers == null) {
+                receivers = newReceivers;
+            } else if (newReceivers != null) {
+                // We need to concatenate the additional receivers
+                // found with what we have do far.  This would be easy,
+                // but we also need to de-dup any receivers that are
+                // singleUser.
+                if (!scannedFirstReceivers) {
+                    // Collect any single user receivers we had already retrieved.
+                    scannedFirstReceivers = true;
+                    for (int i = 0; i < receivers.size(); i++) {
+                        ResolveInfo ri = receivers.get(i);
+                        if ((ri.activityInfo.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+                            ComponentName cn = new ComponentName(
+                                    ri.activityInfo.packageName, ri.activityInfo.name);
+                            if (singleUserReceivers == null) {
+                                singleUserReceivers = new HashSet<ComponentName>();
+                            }
+                            singleUserReceivers.add(cn);
+                        }
+                    }
+                }
+                // Add the new results to the existing results, tracking
+                // and de-dupping single user receivers.
+                for (int i = 0; i < newReceivers.size(); i++) {
+                    ResolveInfo ri = newReceivers.get(i);
+                    if ((ri.activityInfo.flags & ActivityInfo.FLAG_SINGLE_USER) != 0) {
+                        ComponentName cn = new ComponentName(
+                                ri.activityInfo.packageName, ri.activityInfo.name);
+                        if (singleUserReceivers == null) {
+                            singleUserReceivers = new HashSet<ComponentName>();
+                        }
+                        if (!singleUserReceivers.contains(cn)) {
+                            singleUserReceivers.add(cn);
+                            receivers.add(ri);
+                        }
+                    } else {
+                        receivers.add(ri);
+                    }
+                }
+            }
+        }
+        if (receivers != null && broadcastAllowList != null) {
+            for (int i = receivers.size() - 1; i >= 0; i--) {
+                final int receiverAppId = UserHandle.getAppId(
+                        receivers.get(i).activityInfo.applicationInfo.uid);
+                if (receiverAppId >= Process.FIRST_APPLICATION_UID
+                        && Arrays.binarySearch(broadcastAllowList, receiverAppId) < 0) {
+                    receivers.remove(i);
+                }
+            }
+        }
+        return receivers;
+    }
+
+    private void checkBroadcastFromSystem(Intent intent, ProcessRecord callerApp,
+            String callerPackage, int callingUid, boolean isProtectedBroadcast, List receivers) {
+        if ((intent.getFlags() & Intent.FLAG_RECEIVER_FROM_SHELL) != 0) {
+            // Don't yell about broadcasts sent via shell
+            return;
+        }
+
+        final String action = intent.getAction();
+        if (isProtectedBroadcast
+                || Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+                || Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(action)
+                || Intent.ACTION_MEDIA_BUTTON.equals(action)
+                || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
+                || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
+                || Intent.ACTION_MASTER_CLEAR.equals(action)
+                || Intent.ACTION_FACTORY_RESET.equals(action)
+                || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
+                || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
+                || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
+                || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
+                || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
+                || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
+            // Broadcast is either protected, or it's a public action that
+            // we've relaxed, so it's fine for system internals to send.
+            return;
+        }
+
+        // This broadcast may be a problem...  but there are often system components that
+        // want to send an internal broadcast to themselves, which is annoying to have to
+        // explicitly list each action as a protected broadcast, so we will check for that
+        // one safe case and allow it: an explicit broadcast, only being received by something
+        // that has protected itself.
+        if (intent.getPackage() != null || intent.getComponent() != null) {
+            if (receivers == null || receivers.size() == 0) {
+                // Intent is explicit and there's no receivers.
+                // This happens, e.g. , when a system component sends a broadcast to
+                // its own runtime receiver, and there's no manifest receivers for it,
+                // because this method is called twice for each broadcast,
+                // for runtime receivers and manifest receivers and the later check would find
+                // no receivers.
+                return;
+            }
+            boolean allProtected = true;
+            for (int i = receivers.size() - 1; i >= 0; i--) {
+                Object target = receivers.get(i);
+                if (target instanceof ResolveInfo) {
+                    ResolveInfo ri = (ResolveInfo) target;
+                    if (ri.activityInfo.exported && ri.activityInfo.permission == null) {
+                        allProtected = false;
+                        break;
+                    }
+                } else {
+                    BroadcastFilter bf = (BroadcastFilter) target;
+                    if (bf.exported && bf.requiredPermission == null) {
+                        allProtected = false;
+                        break;
+                    }
+                }
+            }
+            if (allProtected) {
+                // All safe!
+                return;
+            }
+        }
+
+        // The vast majority of broadcasts sent from system internals
+        // should be protected to avoid security holes, so yell loudly
+        // to ensure we examine these cases.
+        if (callerApp != null) {
+            Log.wtf(TAG, "Sending non-protected broadcast " + action
+                            + " from system " + callerApp.toShortString() + " pkg " + callerPackage,
+                    new Throwable());
+        } else {
+            Log.wtf(TAG, "Sending non-protected broadcast " + action
+                            + " from system uid " + UserHandle.formatUid(callingUid)
+                            + " pkg " + callerPackage,
+                    new Throwable());
+        }
+    }
+
+    // Apply permission policy around the use of specific broadcast options
+    void enforceBroadcastOptionPermissionsInternal(
+            @Nullable Bundle options, int callingUid) {
+        enforceBroadcastOptionPermissionsInternal(BroadcastOptions.fromBundleNullable(options),
+                callingUid);
+    }
+
+    private void enforceBroadcastOptionPermissionsInternal(
+            @Nullable BroadcastOptions options, int callingUid) {
+        if (options != null && callingUid != Process.SYSTEM_UID) {
+            if (options.isAlarmBroadcast()) {
+                if (DEBUG_BROADCAST_LIGHT) {
+                    Slog.w(TAG, "Non-system caller " + callingUid
+                            + " may not flag broadcast as alarm");
+                }
+                throw new SecurityException(
+                        "Non-system callers may not flag broadcasts as alarm");
+            }
+            if (options.isInteractive()) {
+                mService.enforceCallingPermission(
+                        android.Manifest.permission.BROADCAST_OPTION_INTERACTIVE,
+                        "setInteractive");
+            }
+        }
+    }
+
+    void startBroadcastObservers() {
+        mBroadcastQueue.start(mContext.getContentResolver());
+    }
+
+    void removeStickyBroadcasts(int userId) {
+        synchronized (mStickyBroadcasts) {
+            mStickyBroadcasts.remove(userId);
+        }
+    }
+
+    @NeverCompile
+    void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage) {
+        boolean dumpConstants = true;
+        boolean dumpHistory = true;
+        boolean needSep = false;
+        boolean onlyHistory = false;
+        boolean printedAnything = false;
+        boolean onlyReceivers = false;
+        int filteredUid = Process.INVALID_UID;
+
+        if ("history".equals(dumpPackage)) {
+            if (opti < args.length && "-s".equals(args[opti])) {
+                dumpAll = false;
+            }
+            onlyHistory = true;
+            dumpPackage = null;
+        }
+        if ("receivers".equals(dumpPackage)) {
+            onlyReceivers = true;
+            dumpPackage = null;
+            if (opti + 2 <= args.length) {
+                for (int i = opti; i < args.length; i++) {
+                    String arg = args[i];
+                    switch (arg) {
+                        case "--uid":
+                            filteredUid = getIntArg(pw, args, ++i, Process.INVALID_UID);
+                            if (filteredUid == Process.INVALID_UID) {
+                                return;
+                            }
+                            break;
+                        default:
+                            pw.printf("Invalid argument at index %d: %s\n", i, arg);
+                            return;
+                    }
+                }
+            }
+        }
+        if (DEBUG_BROADCAST) {
+            Slogf.d(TAG_BROADCAST, "dumpBroadcastsLocked(): dumpPackage=%s, onlyHistory=%b, "
+                            + "onlyReceivers=%b, filteredUid=%d", dumpPackage, onlyHistory,
+                    onlyReceivers, filteredUid);
+        }
+
+        pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
+        if (!onlyHistory && dumpAll) {
+            if (mRegisteredReceivers.size() > 0) {
+                boolean printed = false;
+                Iterator it = mRegisteredReceivers.values().iterator();
+                while (it.hasNext()) {
+                    ReceiverList r = (ReceiverList) it.next();
+                    if (dumpPackage != null && (r.app == null
+                            || !dumpPackage.equals(r.app.info.packageName))) {
+                        continue;
+                    }
+                    if (filteredUid != Process.INVALID_UID && filteredUid != r.app.uid) {
+                        if (DEBUG_BROADCAST) {
+                            Slogf.v(TAG_BROADCAST, "dumpBroadcastsLocked(): skipping receiver whose"
+                                    + " uid (%d) is not %d: %s", r.app.uid, filteredUid, r.app);
+                        }
+                        continue;
+                    }
+                    if (!printed) {
+                        pw.println("  Registered Receivers:");
+                        needSep = true;
+                        printed = true;
+                        printedAnything = true;
+                    }
+                    pw.print("  * "); pw.println(r);
+                    r.dump(pw, "    ");
+                }
+            } else {
+                if (onlyReceivers) {
+                    pw.println("  (no registered receivers)");
+                }
+            }
+
+            if (!onlyReceivers) {
+                if (mReceiverResolver.dump(pw, needSep
+                                ? "\n  Receiver Resolver Table:" : "  Receiver Resolver Table:",
+                        "    ", dumpPackage, false, false)) {
+                    needSep = true;
+                    printedAnything = true;
+                }
+            }
+        }
+
+        if (!onlyReceivers) {
+            needSep = mBroadcastQueue.dumpLocked(fd, pw, args, opti,
+                    dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep);
+            printedAnything |= needSep;
+        }
+
+        needSep = true;
+
+        synchronized (mStickyBroadcasts) {
+            if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null
+                    && dumpPackage == null) {
+                for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+                    if (needSep) {
+                        pw.println();
+                    }
+                    needSep = true;
+                    printedAnything = true;
+                    pw.print("  Sticky broadcasts for user ");
+                    pw.print(mStickyBroadcasts.keyAt(user));
+                    pw.println(":");
+                    StringBuilder sb = new StringBuilder(128);
+                    for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+                            : mStickyBroadcasts.valueAt(user).entrySet()) {
+                        pw.print("  * Sticky action ");
+                        pw.print(ent.getKey());
+                        if (dumpAll) {
+                            pw.println(":");
+                            ArrayList<StickyBroadcast> broadcasts = ent.getValue();
+                            final int N = broadcasts.size();
+                            for (int i = 0; i < N; i++) {
+                                final Intent intent = broadcasts.get(i).intent;
+                                final boolean deferUntilActive = broadcasts.get(i).deferUntilActive;
+                                sb.setLength(0);
+                                sb.append("    Intent: ");
+                                intent.toShortString(sb, false, true, false, false);
+                                pw.print(sb);
+                                if (deferUntilActive) {
+                                    pw.print(" [D]");
+                                }
+                                pw.println();
+                                pw.print("      originalCallingUid: ");
+                                pw.println(broadcasts.get(i).originalCallingUid);
+                                pw.println();
+                                Bundle bundle = intent.getExtras();
+                                if (bundle != null) {
+                                    pw.print("      extras: ");
+                                    pw.println(bundle);
+                                }
+                            }
+                        } else {
+                            pw.println("");
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!onlyHistory && !onlyReceivers && dumpAll) {
+            pw.println();
+            pw.println("  Queue " + mBroadcastQueue.toString() + ": "
+                    + mBroadcastQueue.describeStateLocked());
+            pw.println("  mHandler:");
+            mService.mHandler.dump(new PrintWriterPrinter(pw), "    ");
+            needSep = true;
+            printedAnything = true;
+        }
+
+        if (!printedAnything) {
+            pw.println("  (nothing)");
+        }
+    }
+
+    /**
+     * Gets an {@code int} argument from the given {@code index} on {@code args}, logging an error
+     * message on {@code pw} when it cannot be parsed.
+     *
+     * Returns {@code int} argument or {@code invalidValue} if it could not be parsed.
+     */
+    private static int getIntArg(PrintWriter pw, String[] args, int index, int invalidValue) {
+        if (index > args.length) {
+            pw.println("Missing argument");
+            return invalidValue;
+        }
+        String arg = args[index];
+        try {
+            return Integer.parseInt(arg);
+        } catch (Exception e) {
+            pw.printf("Non-numeric argument at index %d: %s\n", index, arg);
+            return invalidValue;
+        }
+    }
+
+    @NeverCompile
+    void dumpBroadcastStatsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage) {
+        if (mCurBroadcastStats == null) {
+            return;
+        }
+
+        pw.println("ACTIVITY MANAGER BROADCAST STATS STATE (dumpsys activity broadcast-stats)");
+        final long now = SystemClock.elapsedRealtime();
+        if (mLastBroadcastStats != null) {
+            pw.print("  Last stats (from ");
+            TimeUtils.formatDuration(mLastBroadcastStats.mStartRealtime, now, pw);
+            pw.print(" to ");
+            TimeUtils.formatDuration(mLastBroadcastStats.mEndRealtime, now, pw);
+            pw.print(", ");
+            TimeUtils.formatDuration(mLastBroadcastStats.mEndUptime
+                    - mLastBroadcastStats.mStartUptime, pw);
+            pw.println(" uptime):");
+            if (!mLastBroadcastStats.dumpStats(pw, "    ", dumpPackage)) {
+                pw.println("    (nothing)");
+            }
+            pw.println();
+        }
+        pw.print("  Current stats (from ");
+        TimeUtils.formatDuration(mCurBroadcastStats.mStartRealtime, now, pw);
+        pw.print(" to now, ");
+        TimeUtils.formatDuration(SystemClock.uptimeMillis()
+                - mCurBroadcastStats.mStartUptime, pw);
+        pw.println(" uptime):");
+        if (!mCurBroadcastStats.dumpStats(pw, "    ", dumpPackage)) {
+            pw.println("    (nothing)");
+        }
+    }
+
+    @NeverCompile
+    void dumpBroadcastStatsCheckinLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean fullCheckin, String dumpPackage) {
+        if (mCurBroadcastStats == null) {
+            return;
+        }
+
+        if (mLastBroadcastStats != null) {
+            mLastBroadcastStats.dumpCheckinStats(pw, dumpPackage);
+            if (fullCheckin) {
+                mLastBroadcastStats = null;
+                return;
+            }
+        }
+        mCurBroadcastStats.dumpCheckinStats(pw, dumpPackage);
+        if (fullCheckin) {
+            mCurBroadcastStats = null;
+        }
+    }
+
+    void writeBroadcastsToProtoLocked(ProtoOutputStream proto) {
+        if (mRegisteredReceivers.size() > 0) {
+            Iterator it = mRegisteredReceivers.values().iterator();
+            while (it.hasNext()) {
+                ReceiverList r = (ReceiverList) it.next();
+                r.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.RECEIVER_LIST);
+            }
+        }
+        mReceiverResolver.dumpDebug(proto,
+                ActivityManagerServiceDumpBroadcastsProto.RECEIVER_RESOLVER);
+        mBroadcastQueue.dumpDebug(proto, ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
+        synchronized (mStickyBroadcasts) {
+            for (int user = 0; user < mStickyBroadcasts.size(); user++) {
+                long token = proto.start(
+                        ActivityManagerServiceDumpBroadcastsProto.STICKY_BROADCASTS);
+                proto.write(StickyBroadcastProto.USER, mStickyBroadcasts.keyAt(user));
+                for (Map.Entry<String, ArrayList<StickyBroadcast>> ent
+                        : mStickyBroadcasts.valueAt(user).entrySet()) {
+                    long actionToken = proto.start(StickyBroadcastProto.ACTIONS);
+                    proto.write(StickyBroadcastProto.StickyAction.NAME, ent.getKey());
+                    for (StickyBroadcast broadcast : ent.getValue()) {
+                        broadcast.intent.dumpDebug(proto, StickyBroadcastProto.StickyAction.INTENTS,
+                                false, true, true, false);
+                    }
+                    proto.end(actionToken);
+                }
+                proto.end(token);
+            }
+        }
+
+        long handlerToken = proto.start(ActivityManagerServiceDumpBroadcastsProto.HANDLER);
+        proto.write(ActivityManagerServiceDumpBroadcastsProto.MainHandler.HANDLER,
+                mService.mHandler.toString());
+        mService.mHandler.getLooper().dumpDebug(proto,
+                ActivityManagerServiceDumpBroadcastsProto.MainHandler.LOOPER);
+        proto.end(handlerToken);
+    }
+
+    @VisibleForTesting
+    static final class StickyBroadcast {
+        public Intent intent;
+        public boolean deferUntilActive;
+        public int originalCallingUid;
+        /** The snapshot process state of the app who sent this broadcast */
+        public int originalCallingAppProcessState;
+        public String resolvedDataType;
+
+        public static StickyBroadcast create(Intent intent, boolean deferUntilActive,
+                int originalCallingUid, int originalCallingAppProcessState,
+                String resolvedDataType) {
+            final StickyBroadcast b = new StickyBroadcast();
+            b.intent = intent;
+            b.deferUntilActive = deferUntilActive;
+            b.originalCallingUid = originalCallingUid;
+            b.originalCallingAppProcessState = originalCallingAppProcessState;
+            b.resolvedDataType = resolvedDataType;
+            return b;
+        }
+
+        @Override
+        public String toString() {
+            return "{intent=" + intent + ", defer=" + deferUntilActive + ", originalCallingUid="
+                    + originalCallingUid + ", originalCallingAppProcessState="
+                    + originalCallingAppProcessState + ", type=" + resolvedDataType + "}";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index a7b2eb1..8fe33d1 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -929,9 +929,9 @@
             // For ordered broadcast, check if the receivers for the new broadcast is a superset
             // of those for the previous one as skipping and removing only one of them could result
             // in an inconsistent state.
-            if (testRecord.ordered || testRecord.prioritized) {
+            if (testRecord.ordered) {
                 return containsAllReceivers(r, testRecord, recordsLookupCache);
-            } else if (testRecord.resultTo != null) {
+            } else if (testRecord.prioritized || testRecord.resultTo != null) {
                 return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED
                         ? r.containsReceiver(testRecord.receivers.get(testIndex))
                         : containsAllReceivers(r, testRecord, recordsLookupCache);
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index edb04c5..f908c67 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -53,6 +53,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.IntArray;
 import android.util.PrintWriterPrinter;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -940,6 +941,46 @@
         return type;
     }
 
+    int[] calculateTypesForLogging() {
+        final IntArray types = new IntArray();
+        if (isForeground()) {
+            types.add(BROADCAST_TYPE_FOREGROUND);
+        } else {
+            types.add(BROADCAST_TYPE_BACKGROUND);
+        }
+        if (alarm) {
+            types.add(BROADCAST_TYPE_ALARM);
+        }
+        if (interactive) {
+            types.add(BROADCAST_TYPE_INTERACTIVE);
+        }
+        if (ordered) {
+            types.add(BROADCAST_TYPE_ORDERED);
+        }
+        if (prioritized) {
+            types.add(BROADCAST_TYPE_PRIORITIZED);
+        }
+        if (resultTo != null) {
+            types.add(BROADCAST_TYPE_RESULT_TO);
+        }
+        if (deferUntilActive) {
+            types.add(BROADCAST_TYPE_DEFERRABLE_UNTIL_ACTIVE);
+        }
+        if (pushMessage) {
+            types.add(BROADCAST_TYPE_PUSH_MESSAGE);
+        }
+        if (pushMessageOverQuota) {
+            types.add(BROADCAST_TYPE_PUSH_MESSAGE_OVER_QUOTA);
+        }
+        if (sticky) {
+            types.add(BROADCAST_TYPE_STICKY);
+        }
+        if (initialSticky) {
+            types.add(BROADCAST_TYPE_INITIAL_STICKY);
+        }
+        return types.toArray();
+    }
+
     public BroadcastRecord maybeStripForHistory() {
         if (!intent.canStripForHistory()) {
             return this;
diff --git a/services/core/java/com/android/server/am/BroadcastSentEventRecord.java b/services/core/java/com/android/server/am/BroadcastSentEventRecord.java
new file mode 100644
index 0000000..f2ac6d5
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastSentEventRecord.java
@@ -0,0 +1,134 @@
+/*
+ * 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 android.app.AppProtoEnums.BROADCAST_TYPE_ORDERED;
+import static android.app.AppProtoEnums.BROADCAST_TYPE_RESULT_TO;
+import static android.app.AppProtoEnums.BROADCAST_TYPE_STICKY;
+
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_SENT;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_SENT__RESULT__FAILED_STICKY_CANT_HAVE_PERMISSION;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_SENT__RESULT__FAILED_USER_STOPPED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_SENT__RESULT__SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_SENT__RESULT__UNKNOWN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.Intent;
+import android.util.IntArray;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+final class BroadcastSentEventRecord {
+    @NonNull private Intent mIntent;
+    private int mOriginalIntentFlags;
+    private int mSenderUid;
+    private int mRealSenderUid;
+    private boolean mSticky;
+    private boolean mOrdered;
+    private boolean mResultRequested;
+    private int mSenderProcState;
+    private int mSenderUidState;
+    @Nullable private BroadcastRecord mBroadcastRecord;
+    private int mResult;
+
+    public void setIntent(@NonNull Intent intent) {
+        mIntent = intent;
+    }
+
+    public void setSenderUid(int uid) {
+        mSenderUid = uid;
+    }
+
+    public void setRealSenderUid(int uid) {
+        mRealSenderUid = uid;
+    }
+
+    public void setOriginalIntentFlags(int flags) {
+        mOriginalIntentFlags = flags;
+    }
+
+    public void setSticky(boolean sticky) {
+        mSticky = sticky;
+    }
+
+    public void setOrdered(boolean ordered) {
+        mOrdered = ordered;
+    }
+
+    public void setResultRequested(boolean resultRequested) {
+        mResultRequested = resultRequested;
+    }
+
+    public void setSenderProcState(int procState) {
+        mSenderProcState = procState;
+    }
+
+    public void setSenderUidState(int procState) {
+        mSenderUidState = procState;
+    }
+
+    public void setBroadcastRecord(@NonNull BroadcastRecord record) {
+        mBroadcastRecord = record;
+    }
+
+    public void setResult(int result) {
+        mResult = result;
+    }
+
+    public void logToStatsd() {
+        if (Flags.logBroadcastSentEvent()) {
+            int loggingResult = switch (mResult) {
+                case ActivityManager.BROADCAST_SUCCESS ->
+                        BROADCAST_SENT__RESULT__SUCCESS;
+                case ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION ->
+                        BROADCAST_SENT__RESULT__FAILED_STICKY_CANT_HAVE_PERMISSION;
+                case ActivityManager.BROADCAST_FAILED_USER_STOPPED ->
+                        BROADCAST_SENT__RESULT__FAILED_USER_STOPPED;
+                default -> BROADCAST_SENT__RESULT__UNKNOWN;
+            };
+            int[] types = calculateTypesForLogging();
+            FrameworkStatsLog.write(BROADCAST_SENT, mIntent.getAction(), mIntent.getFlags(),
+                    mOriginalIntentFlags, mSenderUid, mRealSenderUid, mIntent.getPackage() != null,
+                    mIntent.getComponent() != null,
+                    mBroadcastRecord != null ? mBroadcastRecord.receivers.size() : 0,
+                    loggingResult,
+                    mBroadcastRecord != null ? mBroadcastRecord.getDeliveryGroupPolicy() : 0,
+                    ActivityManager.processStateAmToProto(mSenderProcState),
+                    ActivityManager.processStateAmToProto(mSenderUidState), types);
+        }
+    }
+
+    private int[] calculateTypesForLogging() {
+        if (mBroadcastRecord != null) {
+            return mBroadcastRecord.calculateTypesForLogging();
+        } else {
+            final IntArray types = new IntArray();
+            if (mSticky) {
+                types.add(BROADCAST_TYPE_STICKY);
+            }
+            if (mOrdered) {
+                types.add(BROADCAST_TYPE_ORDERED);
+            }
+            if (mResultRequested) {
+                types.add(BROADCAST_TYPE_RESULT_TO);
+            }
+            return types.toArray();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index ab63e24..3f54cfc 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -78,7 +78,6 @@
 import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
 import static android.os.Process.setProcessGroup;
-import static android.os.Process.setThreadPriority;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -447,6 +446,19 @@
         long getElapsedRealtimeMillis() {
             return SystemClock.elapsedRealtime();
         }
+
+        void batchSetOomAdj(ArrayList<ProcessRecord> procsToOomAdj) {
+            ProcessList.batchSetOomAdj(procsToOomAdj);
+        }
+
+        void setOomAdj(int pid, int uid, int adj) {
+            ProcessList.setOomAdj(pid, uid, adj);
+        }
+
+        void setThreadPriority(int tid, int priority) {
+            Process.setThreadPriority(tid, priority);
+        }
+
     }
 
     boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1126,26 +1138,31 @@
         final int numLru = lruList.size();
         if (mConstants.USE_TIERED_CACHED_ADJ) {
             final long now = mInjector.getUptimeMillis();
+            int uiTargetAdj = 10;
             for (int i = numLru - 1; i >= 0; i--) {
                 ProcessRecord app = lruList.get(i);
                 final ProcessStateRecord state = app.mState;
                 final ProcessCachedOptimizerRecord opt = app.mOptRecord;
-                if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
-                        >= UNKNOWN_ADJ) {
+                if (!app.isKilledByAm() && app.getThread() != null
+                        && (state.getCurAdj() >= UNKNOWN_ADJ
+                            || (state.hasShownUi() && state.getCurAdj() >= CACHED_APP_MIN_ADJ))) {
                     final ProcessServiceRecord psr = app.mServices;
                     int targetAdj = CACHED_APP_MIN_ADJ;
 
                     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
+                        targetAdj += uiTargetAdj++;
                     } else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
                             && (state.getLastStateTime()
                                     + mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
                         // Older cached apps get 950
                         targetAdj += 50;
                     } else {
-                        // Newer cached apps get 910
-                        targetAdj += 10;
+                        // Newer cached apps get 920
+                        targetAdj += 20;
                     }
                     state.setCurRawAdj(targetAdj);
                     state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
@@ -1201,6 +1218,7 @@
                     >= UNKNOWN_ADJ) {
                     final ProcessServiceRecord psr = app.mServices;
                     switch (state.getCurProcState()) {
+                        case PROCESS_STATE_LAST_ACTIVITY:
                         case PROCESS_STATE_CACHED_ACTIVITY:
                         case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                         case ActivityManager.PROCESS_STATE_CACHED_RECENT:
@@ -1430,7 +1448,7 @@
         }
 
         if (!mProcsToOomAdj.isEmpty()) {
-            ProcessList.batchSetOomAdj(mProcsToOomAdj);
+            mInjector.batchSetOomAdj(mProcsToOomAdj);
             mProcsToOomAdj.clear();
         }
 
@@ -2180,7 +2198,6 @@
                 procState = PROCESS_STATE_LAST_ACTIVITY;
                 schedGroup = SCHED_GROUP_BACKGROUND;
                 state.setAdjType("previous-expired");
-                adj = CACHED_APP_MIN_ADJ;
                 if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
                     reportOomAdjMessageLocked(TAG_OOM_ADJ, "Expire prev adj: " + app);
                 }
@@ -3400,7 +3417,7 @@
             if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) {
                 mProcsToOomAdj.add(app);
             } else {
-                ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
+                mInjector.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
             }
 
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
@@ -3460,10 +3477,11 @@
                             ActivityManagerService.setFifoPriority(app, true /* enable */);
                         } else {
                             // Boost priority for top app UI and render threads
-                            setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+                            mInjector.setThreadPriority(app.getPid(),
+                                    THREAD_PRIORITY_TOP_APP_BOOST);
                             if (renderThreadTid != 0) {
                                 try {
-                                    setThreadPriority(renderThreadTid,
+                                    mInjector.setThreadPriority(renderThreadTid,
                                             THREAD_PRIORITY_TOP_APP_BOOST);
                                 } catch (IllegalArgumentException e) {
                                     // thread died, ignore
@@ -3477,14 +3495,14 @@
                     if (app.useFifoUiScheduling()) {
                         // Reset UI pipeline to SCHED_OTHER
                         ActivityManagerService.setFifoPriority(app, false /* enable */);
-                        setThreadPriority(app.getPid(), state.getSavedPriority());
+                        mInjector.setThreadPriority(app.getPid(), state.getSavedPriority());
                     } else {
                         // Reset priority for top app UI and render threads
-                        setThreadPriority(app.getPid(), 0);
+                        mInjector.setThreadPriority(app.getPid(), 0);
                     }
 
                     if (renderThreadTid != 0) {
-                        setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
+                        mInjector.setThreadPriority(renderThreadTid, THREAD_PRIORITY_DISPLAY);
                     }
                 }
             } catch (Exception e) {
@@ -3674,7 +3692,7 @@
                 if (app.useFifoUiScheduling()) {
                     mService.scheduleAsFifoPriority(app.getPid(), true);
                 } else {
-                    setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
+                    mInjector.setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
                 }
                 if (isScreenOnOrAnimatingLocked(state)) {
                     initialSchedGroup = SCHED_GROUP_TOP_APP;
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 0b6d135..2937307 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -33,6 +33,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.providers.settings.Flags;
 
 import android.aconfigd.Aconfigd.StorageRequestMessage;
 import android.aconfigd.Aconfigd.StorageRequestMessages;
@@ -51,6 +52,7 @@
 import java.util.Map;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Set;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 
 /**
@@ -201,6 +203,7 @@
         "pixel_watch",
         "platform_compat",
         "platform_security",
+        "pixel_watch_debug_trace",
         "pmw",
         "power",
         "preload_safety",
@@ -215,6 +218,7 @@
         "stability",
         "statsd",
         "system_performance",
+        "system_sw_battery",
         "system_sw_touch",
         "system_sw_usb",
         "test_suites",
@@ -455,6 +459,24 @@
     }
 
     /**
+     * Send a request to aconfig storage to remove a flag local override.
+     *
+     * @param proto
+     * @param packageName the package of the flag
+     * @param flagName the name of the flag
+     */
+    static void writeFlagOverrideRemovalRequest(
+        ProtoOutputStream proto, String packageName, String flagName) {
+      long msgsToken = proto.start(StorageRequestMessages.MSGS);
+      long msgToken = proto.start(StorageRequestMessage.REMOVE_LOCAL_OVERRIDE_MESSAGE);
+      proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.PACKAGE_NAME, packageName);
+      proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.FLAG_NAME, flagName);
+      proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.REMOVE_ALL, false);
+      proto.end(msgToken);
+      proto.end(msgsToken);
+    }
+
+    /**
      * deserialize a flag input proto stream and log
      * @param proto
      */
@@ -499,8 +521,15 @@
         ProtoOutputStream requests = new ProtoOutputStream();
         for (String flagName : props.getKeyset()) {
             String flagValue = props.getString(flagName, null);
-            if (flagName == null || flagValue == null) {
-                continue;
+
+            if (Flags.syncLocalOverridesRemovalNewStorage()) {
+                if (flagName == null) {
+                    continue;
+                }
+            } else {
+                if (flagName == null || flagValue == null) {
+                    continue;
+                }
             }
 
             int idx = flagName.indexOf(":");
@@ -517,7 +546,13 @@
             }
             String packageName = fullFlagName.substring(0, idx);
             String realFlagName = fullFlagName.substring(idx+1);
-            writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, true);
+
+            if (Flags.syncLocalOverridesRemovalNewStorage() && flagValue == null) {
+              writeFlagOverrideRemovalRequest(requests, packageName, realFlagName);
+            } else {
+              writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, true);
+            }
+
             ++num_requests;
         }
 
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index bac5132..6e8eb7d7 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -50,47 +50,18 @@
       ]
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.am."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_am_Presubmit"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.am."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_am_Presubmit"
     },
     {
       "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
-      "name": "FrameworksCoreTests",
-      "options": [
-        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
-        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
-      ]
+      "name": "FrameworksCoreTests_battery_stats"
     },
     {
       "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
-      "name": "FrameworksServicesTests",
-      "options": [
-        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
-      ]
+      "name": "FrameworksServicesTests_battery_stats"
     },
     {
       "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
@@ -98,12 +69,7 @@
     },
     {
       "file_patterns": ["Broadcast.*"],
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        { "include-filter": "com.android.server.am.BroadcastRecordTest" },
-        { "include-filter": "com.android.server.am.BroadcastQueueTest" },
-        { "include-filter": "com.android.server.am.BroadcastQueueModernImplTest" }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_am_broadcast"
     },
     {
       "file_patterns": ["Broadcast.*"],
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 30efa3e..b186eaa 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -131,6 +131,7 @@
 import com.android.internal.util.ObjectUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.AlarmManagerInternal;
 import com.android.server.FactoryResetter;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -247,6 +248,12 @@
     private static final int USER_COMPLETED_EVENT_DELAY_MS = 5 * 1000;
 
     /**
+     * If a user has an alarm in the next this many milliseconds, avoid stopping it due to
+     * scheduled background stopping.
+     */
+    private static final long TIME_BEFORE_USERS_ALARM_TO_AVOID_STOPPING_MS = 60 * 60_000; // 60 mins
+
+    /**
      * Maximum number of users we allow to be running at a time, including system user.
      *
      * <p>This parameter only affects how many background users will be stopped when switching to a
@@ -439,6 +446,15 @@
     @GuardedBy("mLock")
     private final List<PendingUserStart> mPendingUserStarts = new ArrayList<>();
 
+    /**
+     * Contains users which cannot abort the shutdown process.
+     *
+     * <p> For example, we don't abort shutdown for users whose processes have already been stopped
+     * due to {@link #isEarlyPackageKillEnabledForUserSwitch(int, int)}.
+     */
+    @GuardedBy("mLock")
+    private final ArraySet<Integer> mDoNotAbortShutdownUserIds = new ArraySet<>();
+
     private final UserLifecycleListener mUserLifecycleListener = new UserLifecycleListener() {
         @Override
         public void onUserCreated(UserInfo user, Object token) {
@@ -509,11 +525,11 @@
         }
     }
 
-    private boolean shouldStopUserOnSwitch() {
+    private boolean isStopUserOnSwitchEnabled() {
         synchronized (mLock) {
             if (mStopUserOnSwitch != STOP_USER_ON_SWITCH_DEFAULT) {
                 final boolean value = mStopUserOnSwitch == STOP_USER_ON_SWITCH_TRUE;
-                Slogf.i(TAG, "shouldStopUserOnSwitch(): returning overridden value (%b)", value);
+                Slogf.i(TAG, "isStopUserOnSwitchEnabled(): returning overridden value (%b)", value);
                 return value;
             }
         }
@@ -521,6 +537,26 @@
         return property == -1 ? mDelayUserDataLocking : property == 1;
     }
 
+    /**
+     * Get whether or not the previous user's packages will be killed before the user is
+     * stopped during a user switch.
+     *
+     * <p> The primary use case of this method is for {@link com.android.server.SystemService}
+     * classes to call this API in their
+     * {@link com.android.server.SystemService#onUserSwitching} method implementation to prevent
+     * restarting any of the previous user's processes that will be killed during the user switch.
+     */
+    boolean isEarlyPackageKillEnabledForUserSwitch(int fromUserId, int toUserId) {
+        // NOTE: The logic in this method could be extended to cover other cases where
+        // the previous user is also stopped like: guest users, ephemeral users,
+        // and users with DISALLOW_RUN_IN_BACKGROUND. Currently, this is not done
+        // because early killing is not enabled for these cases by default.
+        if (fromUserId == UserHandle.USER_SYSTEM) {
+            return false;
+        }
+        return isStopUserOnSwitchEnabled();
+    }
+
     void finishUserSwitch(UserState uss) {
         // This call holds the AM lock so we post to the handler.
         mHandler.post(() -> {
@@ -1247,6 +1283,7 @@
                 return;
             }
             uss.setState(UserState.STATE_SHUTDOWN);
+            mDoNotAbortShutdownUserIds.remove(userId);
         }
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("setUserState-STATE_SHUTDOWN-" + userId + "-[stopUser]");
@@ -1555,7 +1592,8 @@
 
     private void stopPackagesOfStoppedUser(@UserIdInt int userId, String reason) {
         if (DEBUG_MU) Slogf.i(TAG, "stopPackagesOfStoppedUser(%d): %s", userId, reason);
-        mInjector.activityManagerForceStopPackage(userId, reason);
+        mInjector.activityManagerForceStopUserPackages(userId, reason,
+                /* evenImportantServices= */ true);
         if (mInjector.getUserManager().isPreCreated(userId)) {
             // Don't fire intent for precreated.
             return;
@@ -1608,6 +1646,21 @@
         }
     }
 
+    private void stopPreviousUserPackagesIfEnabled(int fromUserId, int toUserId) {
+        if (!android.multiuser.Flags.stopPreviousUserApps()
+                || !isEarlyPackageKillEnabledForUserSwitch(fromUserId, toUserId)) {
+            return;
+        }
+        // Stop the previous user's packages early to reduce resource usage
+        // during user switching. Only do this when the previous user will
+        // be stopped regardless.
+        synchronized (mLock) {
+            mDoNotAbortShutdownUserIds.add(fromUserId);
+        }
+        mInjector.activityManagerForceStopUserPackages(fromUserId,
+                "early stop user packages", /* evenImportantServices= */ false);
+    }
+
     void scheduleStartProfiles() {
         // Parent user transition to RUNNING_UNLOCKING happens on FgThread, so it is busy, there is
         // a chance the profile will reach RUNNING_LOCKED while parent is still locked, so no
@@ -1889,7 +1942,8 @@
                     updateStartedUserArrayLU();
                     needStart = true;
                     updateUmState = true;
-                } else if (uss.state == UserState.STATE_SHUTDOWN) {
+                } 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,
@@ -1924,6 +1978,7 @@
                 boolean userSwitchUiEnabled;
                 synchronized (mLock) {
                     mCurrentUserId = userId;
+                    ActivityManager.invalidateGetCurrentUserIdCache();
                     userSwitchUiEnabled = mUserSwitchUiEnabled;
                 }
                 mInjector.updateUserConfiguration();
@@ -2185,6 +2240,7 @@
                 return true;
             }
             mTargetUserId = targetUserId;
+            ActivityManager.invalidateGetCurrentUserIdCache();
             userSwitchUiEnabled = mUserSwitchUiEnabled;
         }
         if (userSwitchUiEnabled) {
@@ -2262,6 +2318,7 @@
         synchronized (mLock) {
             nextUserId = ObjectUtils.getOrElse(mPendingTargetUserIds.poll(), UserHandle.USER_NULL);
             mTargetUserId = UserHandle.USER_NULL;
+            ActivityManager.invalidateGetCurrentUserIdCache();
         }
         if (nextUserId != UserHandle.USER_NULL) {
             switchUser(nextUserId);
@@ -2293,7 +2350,7 @@
                 hasUserRestriction(UserManager.DISALLOW_RUN_IN_BACKGROUND, oldUserId);
         synchronized (mLock) {
             // If running in background is disabled or mStopUserOnSwitch mode, stop the user.
-            if (hasRestriction || shouldStopUserOnSwitch()) {
+            if (hasRestriction || isStopUserOnSwitchEnabled()) {
                 Slogf.i(TAG, "Stopping user %d and its profiles on user switch", oldUserId);
                 stopUsersLU(oldUserId, /* allowDelayedLocking= */ false, null, null);
                 return;
@@ -2371,6 +2428,12 @@
     void processScheduledStopOfBackgroundUser(Integer userIdInteger) {
         final int userId = userIdInteger;
         Slogf.d(TAG, "Considering stopping background user %d due to inactivity", userId);
+
+        if (avoidStoppingUserDueToUpcomingAlarm(userId)) {
+            // We want this user running soon for alarm-purposes, so don't stop it now. Reschedule.
+            scheduleStopOfBackgroundUser(userId);
+            return;
+        }
         synchronized (mLock) {
             if (getCurrentOrTargetUserIdLU() == userId) {
                 return;
@@ -2390,6 +2453,18 @@
         }
     }
 
+    /**
+     * Returns whether we should avoid stopping the user now due to it having an alarm set to fire
+     * soon.
+     */
+    private boolean avoidStoppingUserDueToUpcomingAlarm(@UserIdInt int userId) {
+        final long alarmWallclockMs
+                = mInjector.getAlarmManagerInternal().getNextAlarmTriggerTimeForUser(userId);
+        return System.currentTimeMillis() <  alarmWallclockMs
+                && (alarmWallclockMs
+                    < System.currentTimeMillis() + TIME_BEFORE_USERS_ALARM_TO_AVOID_STOPPING_MS);
+    }
+
     private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
         t.traceBegin("timeoutUserSwitch-" + oldUserId + "-to-" + newUserId);
@@ -2949,6 +3024,9 @@
         mInjector.getUserManagerInternal().addUserLifecycleListener(mUserLifecycleListener);
         updateProfileRelatedCaches();
         mInjector.reportCurWakefulnessUsageEvent();
+
+        // IpcDataCache must be invalidated before it starts caching.
+        ActivityManager.invalidateGetCurrentUserIdCache();
     }
 
     // TODO(b/266158156): remove this method if initial system user boot logic is refactored?
@@ -3112,6 +3190,9 @@
 
     @GuardedBy("mLock")
     private int getCurrentOrTargetUserIdLU() {
+        // Note: this result is currently cached by ActivityManager.getCurrentUser() - changes to
+        // the logic here may require updating how the cache is invalidated.
+        // See ActivityManager.invalidateGetCurrentUserIdCache() for more details.
         return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId;
     }
 
@@ -3425,7 +3506,7 @@
             pw.println("  mLastActiveUsersForDelayedLocking:" + mLastActiveUsersForDelayedLocking);
             pw.println("  mDelayUserDataLocking:" + mDelayUserDataLocking);
             pw.println("  mAllowUserUnlocking:" + mAllowUserUnlocking);
-            pw.println("  shouldStopUserOnSwitch():" + shouldStopUserOnSwitch());
+            pw.println("  isStopUserOnSwitchEnabled():" + isStopUserOnSwitchEnabled());
             pw.println("  mStopUserOnSwitch:" + mStopUserOnSwitch);
             pw.println("  mMaxRunningUsers:" + mMaxRunningUsers);
             pw.println("  mBackgroundUserScheduledStopTimeSecs:"
@@ -3522,6 +3603,7 @@
                         Integer.toString(msg.arg1), msg.arg1);
 
                 mInjector.getSystemServiceManager().onUserSwitching(msg.arg2, msg.arg1);
+                stopPreviousUserPackagesIfEnabled(msg.arg2, msg.arg1);
                 scheduleOnUserCompletedEvent(msg.arg1,
                         UserCompletedEventType.EVENT_TYPE_USER_SWITCHING,
                         USER_COMPLETED_EVENT_DELAY_MS);
@@ -3860,6 +3942,10 @@
             return mPowerManagerInternal;
         }
 
+        AlarmManagerInternal getAlarmManagerInternal() {
+            return LocalServices.getService(AlarmManagerInternal.class);
+        }
+
         KeyguardManager getKeyguardManager() {
             return mService.mContext.getSystemService(KeyguardManager.class);
         }
@@ -3896,10 +3982,10 @@
             }.sendNext();
         }
 
-        void activityManagerForceStopPackage(@UserIdInt int userId, String reason) {
+        void activityManagerForceStopUserPackages(@UserIdInt int userId, String reason,
+                boolean evenImportantServices) {
             synchronized (mService) {
-                mService.forceStopPackageLocked(null, -1, false, false, true, false, false, false,
-                        userId, reason);
+                mService.forceStopUserPackagesLocked(userId, reason, evenImportantServices);
             }
         };
 
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 5315167..3334393 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -184,3 +184,14 @@
     description: "Defer submitting binder calls to paused processes."
     bug: "327038797"
 }
+
+flag {
+    name: "log_broadcast_sent_event"
+    namespace: "backstage_power"
+    description: "Log the broadcast send event to Statsd"
+    bug: "355261986"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4c65bd2..8c5152f 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -2307,7 +2307,7 @@
                 return;
             }
 
-            final int userId = mContext.getUserId();
+            final int userId = ActivityManager.getCurrentUser();
             final boolean isNotGame = Arrays.stream(packages).noneMatch(
                     p -> isPackageGame(p, userId));
             synchronized (mUidObserverLock) {
diff --git a/services/core/java/com/android/server/app/TEST_MAPPING b/services/core/java/com/android/server/app/TEST_MAPPING
index 82840ee..b718ce6 100644
--- a/services/core/java/com/android/server/app/TEST_MAPPING
+++ b/services/core/java/com/android/server/app/TEST_MAPPING
@@ -26,15 +26,7 @@
       ]
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.app"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_app"
     },
     {
       "name": "FrameworksCoreGameManagerTests",
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index f61bd60..6ae6f3d 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -33,7 +33,6 @@
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
 import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_CAMERA_SANDBOXED;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
@@ -1413,6 +1412,22 @@
     }
 
     public void uidRemoved(int uid) {
+        if (Flags.dontRemoveExistingUidStates()) {
+            // b/358365471 If apps sharing UID are installed on multiple users and only one of
+            // them is installed for a single user while keeping the others we observe this
+            // subroutine get invoked incorrectly since the UID still exists.
+            final long token = Binder.clearCallingIdentity();
+            try {
+                String uidName = getPackageManagerInternal().getNameForUid(uid);
+                if (uidName != null) {
+                    Slog.e(TAG, "Tried to remove existing UID. uid: " + uid + " name: " + uidName);
+                    return;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         synchronized (this) {
             if (mUidStates.indexOfKey(uid) >= 0) {
                 mUidStates.get(uid).clear();
@@ -3099,11 +3114,6 @@
                     packageName);
         }
         if (!isIncomingPackageValid(packageName, UserHandle.getUserId(uid))) {
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (code == OP_BLUETOOTH_CONNECT) {
-                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as incoming "
-                        + "package: " + packageName + " and uid: " + uid + " is invalid");
-            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -3133,13 +3143,6 @@
             }
         } catch (SecurityException e) {
             logVerifyAndGetBypassFailure(uid, e, "noteOperation");
-            // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-            if (code == OP_BLUETOOTH_CONNECT) {
-                Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
-                        + " verifyAndGetBypass returned a SecurityException for package: "
-                        + packageName + " and uid: " + uid + " and attributionTag: "
-                        + attributionTag, e);
-            }
             return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                     packageName);
         }
@@ -3157,17 +3160,6 @@
                 if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName + "flags: " +
                         AppOpsManager.flagsToString(flags));
-                // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                if (code == OP_BLUETOOTH_CONNECT) {
-                    Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
-                            + " #getOpsLocked returned null for"
-                            + " uid: " + uid
-                            + " packageName: " + packageName
-                            + " attributionTag: " + attributionTag
-                            + " pvr.isAttributionTagValid: " + pvr.isAttributionTagValid
-                            + " pvr.bypass: " + pvr.bypass);
-                    Slog.e(TAG, "mUidStates.get(" + uid + "): " + mUidStates.get(uid));
-                }
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
                         packageName);
             }
@@ -3212,11 +3204,6 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag,
                             virtualDeviceId, flags, uidMode);
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (code == OP_BLUETOOTH_CONNECT && uidMode == MODE_ERRORED) {
-                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
-                                + " uid mode is MODE_ERRORED");
-                    }
                     return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                 }
             } else {
@@ -3236,11 +3223,6 @@
                     attributedOp.rejected(uidState.getState(), flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag,
                             virtualDeviceId, flags, mode);
-                    // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                    if (code == OP_BLUETOOTH_CONNECT && mode == MODE_ERRORED) {
-                        Slog.e(TAG, "noting OP_BLUETOOTH_CONNECT returned MODE_ERRORED as"
-                                + " package mode is MODE_ERRORED");
-                    }
                     return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                 }
             }
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 539dbca..2ce4623 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -1413,11 +1413,11 @@
             pw.print("-");
             pw.print(flagsToString(mOpFlag));
             pw.print("] at ");
-            date.setTime(discretizeTimeStamp(mNoteTime));
+            date.setTime(mNoteTime);
             pw.print(sdf.format(date));
             if (mNoteDuration != -1) {
                 pw.print(" for ");
-                pw.print(discretizeDuration(mNoteDuration));
+                pw.print(mNoteDuration);
                 pw.print(" milliseconds ");
             }
             if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 65f6af7..9317c1e 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -12,40 +12,13 @@
             "name": "CtsAppOps2TestCases"
         },
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.appop"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_appop"
         },
         {
-            "name": "FrameworksMockingServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.appop"
-                }
-            ]
+            "name": "FrameworksMockingServicesTests_android_server_appop"
         },
         {
-            "name": "CtsPermissionTestCases",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.BackgroundPermissionsTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.SplitPermissionTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.PermissionFlagsTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.SharedUidPermissionsTest"
-                }
-            ]
+            "name": "CtsPermissionTestCases_Platform"
         },
         {
             "name": "CtsAppTestCases",
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index ca907c5..1cf9935 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1714,6 +1714,10 @@
         sendIILMsg(MSG_IIL_BTLEAUDIO_TIMEOUT, SENDMSG_QUEUE, device, codec, address, delayMs);
     }
 
+    /*package*/ void setHearingAidTimeout(String address, int delayMs) {
+        sendLMsg(MSG_IL_BT_HEARING_AID_TIMEOUT, SENDMSG_QUEUE, address, delayMs);
+    }
+
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
         synchronized (mDeviceStateLock) {
             mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
@@ -1959,6 +1963,13 @@
                                 (String) msg.obj, msg.arg1, msg.arg2);
                     }
                     break;
+                case MSG_IL_BT_HEARING_AID_TIMEOUT:
+                    // msg.obj  == address of Hearing Aid device
+                    synchronized (mDeviceStateLock) {
+                        mDeviceInventory.onMakeHearingAidDeviceUnavailableNow(
+                                (String) msg.obj);
+                    }
+                    break;
                 case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: {
                     final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
                     final Pair<Integer, Boolean> codecAndChanged = mBtHelper.getCodecWithFallback(
@@ -2234,6 +2245,7 @@
     private static final int MSG_L_SYNCHRONIZE_ADI_DEVICES_IN_INVENTORY = 58;
     private static final int MSG_IL_UPDATED_ADI_DEVICE_STATE = 59;
     private static final int MSG_L_SET_FORCE_BT_A2DP_USE_NO_MUTE = 60;
+    private static final int MSG_IL_BT_HEARING_AID_TIMEOUT = 61;
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
@@ -2246,6 +2258,7 @@
             case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
             case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
             case MSG_CHECK_MUTE_MUSIC:
+            case MSG_IL_BT_HEARING_AID_TIMEOUT:
                 return true;
             default:
                 return false;
@@ -2330,6 +2343,7 @@
                 case MSG_IL_BTA2DP_TIMEOUT:
                 case MSG_IIL_BTLEAUDIO_TIMEOUT:
                 case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+                case MSG_IL_BT_HEARING_AID_TIMEOUT:
                     if (sLastDeviceConnectMsgTime >= time) {
                         // add a little delay to make sure messages are ordered as expected
                         time = sLastDeviceConnectMsgTime + 30;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 8d8a54e..a9bff8b 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1050,6 +1050,11 @@
         }
     }
 
+    /*package*/ void onMakeHearingAidDeviceUnavailableNow(String address) {
+        synchronized (mDevicesLock) {
+            makeHearingAidDeviceUnavailable(address);
+        }
+    }
 
     /**
      * Goes over all connected LE Audio devices in the provided group ID and
@@ -1457,7 +1462,7 @@
     private int setDevicesRoleForCapturePreset(int capturePreset, int role,
                                                @NonNull List<AudioDeviceAttributes> devices) {
         return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
-            return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d);
+            return mAudioSystem.setDevicesRoleForCapturePreset(p, r, d);
         }, (p, r, d) -> {
                 return mAudioSystem.clearDevicesRoleForCapturePreset(p, r);
             }, capturePreset, role, devices);
@@ -1902,12 +1907,10 @@
                     .set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
                     .record();
             if (toRemove.size() > 0) {
-                /*final int delay = */
-                checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
+                final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
                         AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
                 toRemove.stream().forEach(deviceAddress ->
-                        // TODO delay not used?
-                        makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/)
+                        makeHearingAidDeviceUnavailableLater(deviceAddress, delay)
                 );
             }
         }
@@ -2498,6 +2501,15 @@
         mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
     }
 
+    @GuardedBy("mDevicesLock")
+    private void makeHearingAidDeviceUnavailableLater(
+            String address, int delayMs) {
+        // the device will be made unavailable later, so consider it disconnected right away
+        mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address));
+        // send the delayed message to make the device unavailable later
+        mDeviceBroker.setHearingAidTimeout(address, delayMs);
+    }
+
     /**
      * Returns whether a device of type DEVICE_OUT_HEARING_AID is connected.
      * Visibility by APM plays no role
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
index c5180af..5283edd 100644
--- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -33,6 +33,7 @@
 
 import android.annotation.Nullable;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArraySet;
 import android.util.IntArray;
@@ -190,6 +191,7 @@
                 mIsUpdateDeferred = true;
                 return;
             }
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "audioserver_permission_update");
             try {
                 for (byte i = 0; i < PermissionEnum.ENUM_SIZE; i++) {
                     var newPerms = getUidsHoldingPerm(i);
@@ -203,6 +205,8 @@
                 mDest = null;
                 // We didn't necessarily finish
                 mIsUpdateDeferred = true;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 53b04df..4e24cf3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -284,11 +284,16 @@
 import java.util.Objects;
 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;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.BooleanSupplier;
 import java.util.stream.Collectors;
 
@@ -785,6 +790,8 @@
     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
 
     private final Executor mAudioServerLifecycleExecutor;
+    private long mSysPropListenerNativeHandle;
+    private final List<Future> mScheduledPermissionTasks = new ArrayList();
 
     private IMediaProjectionManager mProjectionService; // to validate projection token
 
@@ -1092,7 +1099,8 @@
 
         public Lifecycle(Context context) {
             super(context);
-            var audioserverLifecycleExecutor = Executors.newSingleThreadExecutor();
+            var audioserverLifecycleExecutor = Executors.newSingleThreadScheduledExecutor(
+                    (Runnable r) -> new Thread(r, "audioserver_lifecycle"));
             var audioPolicyFacade = new DefaultAudioPolicyFacade(audioserverLifecycleExecutor);
             mService = new AudioService(context,
                               AudioSystemAdapter.getDefaultAdapter(),
@@ -1222,34 +1230,6 @@
         mBroadcastHandlerThread = new HandlerThread("AudioService Broadcast");
         mBroadcastHandlerThread.start();
 
-        // Listen to permission invalidations for the PermissionProvider
-        if (audioserverPermissions()) {
-            final Handler broadcastHandler = mBroadcastHandlerThread.getThreadHandler();
-            mAudioSystem.listenForSystemPropertyChange(PermissionManager.CACHE_KEY_PACKAGE_INFO,
-                    new Runnable() {
-                        // Roughly chosen to be long enough to suppress the autocork behavior
-                        // of the permission cache (50ms), and longer than the task could reasonably
-                        // take, even with many packages and users, while not introducing visible
-                        // permission leaks - since the app needs to restart, and trigger an action
-                        // which requires permissions from audioserver before this delay.
-                        // For RECORD_AUDIO, we are additionally protected by appops.
-                        final long UPDATE_DELAY_MS = 110;
-                        final AtomicLong scheduledUpdateTimestamp = new AtomicLong(0);
-                        @Override
-                        public void run() {
-                            var currentTime = SystemClock.uptimeMillis();
-                            if (currentTime > scheduledUpdateTimestamp.get()) {
-                                scheduledUpdateTimestamp.set(currentTime + UPDATE_DELAY_MS);
-                                broadcastHandler.postAtTime( () ->
-                                        mAudioServerLifecycleExecutor.execute(mPermissionProvider
-                                            ::onPermissionStateChanged),
-                                        currentTime + UPDATE_DELAY_MS
-                                    );
-                            }
-                        }
-            });
-        }
-
         mDeviceBroker = new AudioDeviceBroker(mContext, this, mAudioSystem);
 
         mIsSingleVolume = AudioSystem.isSingleVolume(context);
@@ -1717,8 +1697,10 @@
 
     public void onSystemReady() {
         mSystemReady = true;
+        if (audioserverPermissions()) {
+            setupPermissionListener();
+        }
         scheduleLoadSoundEffects();
-
         mDeviceBroker.onSystemReady();
 
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_HDMI_CEC)) {
@@ -10608,6 +10590,67 @@
         }
     }
 
+    /* Listen to permission invalidations for the PermissionProvider */
+    private void setupPermissionListener() {
+        // Roughly chosen to be long enough to suppress the autocork behavior of the permission
+        // cache (50ms), while not introducing visible permission leaks - since the app needs to
+        // restart, and trigger an action which requires permissions from audioserver before this
+        // delay. For RECORD_AUDIO, we are additionally protected by appops.
+        final long UPDATE_DELAY_MS = 60;
+        // instanceof to simplify the construction requirements of AudioService for testing: no
+        // delayed execution during unit tests.
+        if (mAudioServerLifecycleExecutor instanceof ScheduledExecutorService exec) {
+            // The order on the task list is an embedding on the scheduling order of the executor,
+            // since we synchronously add the scheduled task to our local queue. This list should
+            // almost always have only two elements, except in cases of serious system contention.
+            Runnable task = () -> {
+                synchronized (mScheduledPermissionTasks) {
+                    mScheduledPermissionTasks.add(exec.schedule(() -> {
+                        try {
+                            // Our goal is to remove all tasks which don't correspond to ourselves
+                            // on this queue. Either they are already done (ahead of us), or we
+                            // should cancel them (behind us), since their work is redundant after
+                            // we fire.
+                            // We must be the first non-completed task in the queue, since the
+                            // execution order matches the queue order. Note, this task is the only
+                            // writer on elements in the queue, and the task is serialized, so
+                            //  => no in-flight cancellation
+                            //  => exists at least one non-completed task (ourselves)
+                            //  => the queue is non-empty (only completed tasks removed)
+                            synchronized (mScheduledPermissionTasks) {
+                                final var iter = mScheduledPermissionTasks.iterator();
+                                while (iter.next().isDone()) {
+                                    iter.remove();
+                                }
+                                // iter is on the first element which is not completed (us)
+                                while (iter.hasNext()) {
+                                    if (!iter.next().cancel(false)) {
+                                        throw new AssertionError(
+                                                "Cancel should be infallible since we" +
+                                                "cancel from the executor");
+                                    }
+                                    iter.remove();
+                                }
+                            }
+                            mPermissionProvider.onPermissionStateChanged();
+                        } catch (Exception e) {
+                            // Handle executor routing exceptions to nowhere
+                            Thread.getDefaultUncaughtExceptionHandler()
+                                    .uncaughtException(Thread.currentThread(), e);
+                        }
+                    }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS));
+                }
+            };
+            mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
+                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
+                    task);
+        } else {
+            mAudioSystem.listenForSystemPropertyChange(
+                    PermissionManager.CACHE_KEY_PACKAGE_INFO,
+                    () -> mAudioServerLifecycleExecutor.execute(
+                                mPermissionProvider::onPermissionStateChanged));
+        }
+    }
 
     //==========================================================================================
     // Audio Focus
@@ -10691,7 +10734,7 @@
         return true;
     }
 
-    public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
+    public int requestAudioFocus(AudioAttributes aa, int focusReqType, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName,
             String attributionTag, int flags, IAudioPolicyCallback pcb, int sdk) {
         if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) != 0) {
@@ -10700,7 +10743,7 @@
         final int uid = Binder.getCallingUid();
         MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus")
                 .setUid(uid)
-                //.putInt("durationHint", durationHint)
+                //.putInt("focusReqType", focusReqType)
                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
                 .set(MediaMetrics.Property.EVENT, "requestAudioFocus")
@@ -10760,9 +10803,14 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
+            //TODO move inside HardeningEnforcer after refactor that moves permission checks
+            //     in the blockFocusMethod
+            if (permissionOverridesCheck) {
+                mHardeningEnforcer.metricsLogFocusReq(/*blocked*/false, focusReqType, uid);
+            }
             if (!permissionOverridesCheck && mHardeningEnforcer.blockFocusMethod(uid,
                     HardeningEnforcer.METHOD_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS,
-                    clientId, durationHint, callingPackageName, attributionTag, sdk)) {
+                    clientId, focusReqType, callingPackageName, attributionTag, sdk)) {
                 final String reason = "Audio focus request blocked by hardening";
                 Log.w(TAG, reason);
                 mmi.set(MediaMetrics.Property.EARLY_RETURN, reason).record();
@@ -10773,14 +10821,14 @@
         }
 
         mmi.record();
-        return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
+        return mMediaFocusControl.requestAudioFocus(aa, focusReqType, cb, fd,
                 clientId, callingPackageName, flags, sdk,
-                forceFocusDuckingForAccessibility(aa, durationHint, uid), -1 /*testUid, ignored*/,
+                forceFocusDuckingForAccessibility(aa, focusReqType, uid), -1 /*testUid, ignored*/,
                 permissionOverridesCheck);
     }
 
     /** see {@link AudioManager#requestAudioFocusForTest(AudioFocusRequest, String, int, int)} */
-    public int requestAudioFocusForTest(AudioAttributes aa, int durationHint, IBinder cb,
+    public int requestAudioFocusForTest(AudioAttributes aa, int focusReqType, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName,
             int flags, int fakeUid, int sdk) {
         if (!enforceQueryAudioStateForTest("focus request")) {
@@ -10791,7 +10839,7 @@
             Log.e(TAG, reason);
             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
         }
-        return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
+        return mMediaFocusControl.requestAudioFocus(aa, focusReqType, cb, fd,
                 clientId, callingPackageName, flags,
                 sdk, false /*forceDuck*/, fakeUid, true /*permissionOverridesCheck*/);
     }
@@ -14663,6 +14711,25 @@
         return activeAssistantUids;
     }
 
+    @Override
+    /** @see AudioManager#permissionUpdateBarrier() */
+    public void permissionUpdateBarrier() {
+        mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+        List<Future> snapshot;
+        synchronized (mScheduledPermissionTasks) {
+            snapshot = List.copyOf(mScheduledPermissionTasks);
+        }
+        for (var x : snapshot) {
+            try {
+                x.get();
+            } catch (CancellationException e) {
+                // Task completed
+            } catch (InterruptedException | ExecutionException e) {
+                Log.wtf(TAG, "Exception which should never occur", e);
+            }
+        }
+    }
+
     List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) {
         return mDeviceBroker.getDeviceIdentityAddresses(device);
     }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index d083c68..5cabdde 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -748,8 +748,12 @@
         return AudioSystem.setMasterMute(mute);
     }
 
-    public void listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
-        AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+    public long listenForSystemPropertyChange(String systemPropertyName, Runnable callback) {
+        return AudioSystem.listenForSystemPropertyChange(systemPropertyName, callback);
+    }
+
+    public void triggerSystemPropertyUpdate(long handle) {
+        AudioSystem.triggerSystemPropertyUpdate(handle);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/HardeningEnforcer.java b/services/core/java/com/android/server/audio/HardeningEnforcer.java
index 8ae04ac..faeba5d 100644
--- a/services/core/java/com/android/server/audio/HardeningEnforcer.java
+++ b/services/core/java/com/android/server/audio/HardeningEnforcer.java
@@ -31,7 +31,9 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 
+import com.android.modules.expresslog.Counter;
 import com.android.server.utils.EventLogger;
 
 import java.io.PrintWriter;
@@ -55,6 +57,30 @@
     final EventLogger mEventLogger = new EventLogger(LOG_NB_EVENTS,
             "Hardening enforcement");
 
+    // capacity = 4 for each of the focus request types
+    static final SparseArray<String> METRIC_COUNTERS_FOCUS_DENIAL = new SparseArray<>(4);
+    static final SparseArray<String> METRIC_COUNTERS_FOCUS_GRANT = new SparseArray<>(4);
+
+    static {
+        METRIC_COUNTERS_FOCUS_GRANT.put(AudioManager.AUDIOFOCUS_GAIN,
+                "media_audio.value_audio_focus_gain_granted");
+        METRIC_COUNTERS_FOCUS_GRANT.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
+                "media_audio.value_audio_focus_gain_transient_granted");
+        METRIC_COUNTERS_FOCUS_GRANT.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+                "media_audio.value_audio_focus_gain_transient_duck_granted");
+        METRIC_COUNTERS_FOCUS_GRANT.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE,
+                "media_audio.value_audio_focus_gain_transient_excl_granted");
+
+        METRIC_COUNTERS_FOCUS_DENIAL.put(AudioManager.AUDIOFOCUS_GAIN,
+                "media_audio.value_audio_focus_gain_appops_denial");
+        METRIC_COUNTERS_FOCUS_DENIAL.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
+                "media_audio.value_audio_focus_gain_transient_appops_denial");
+        METRIC_COUNTERS_FOCUS_DENIAL.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+                "media_audio.value_audio_focus_gain_transient_duck_appops_denial");
+        METRIC_COUNTERS_FOCUS_DENIAL.put(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE,
+                "media_audio.value_audio_focus_gain_transient_excl_appops_denial");
+    }
+
     /**
      * Matches calls from {@link AudioManager#setStreamVolume(int, int, int)}
      */
@@ -129,41 +155,61 @@
      * Checks whether the call in the current thread should be allowed or blocked
      * @param focusMethod name of the method to check, for logging purposes
      * @param clientId id of the requester
-     * @param durationHint focus type being requested
+     * @param focusReqType focus type being requested
      * @param attributionTag attribution of the caller
      * @param targetSdk target SDK of the caller
      * @return false if the method call is allowed, true if it should be a no-op
      */
     @SuppressWarnings("AndroidFrameworkCompatChange")
     protected boolean blockFocusMethod(int callingUid, int focusMethod, @NonNull String clientId,
-            int durationHint, @NonNull String packageName, String attributionTag, int targetSdk) {
+            int focusReqType, @NonNull String packageName, String attributionTag, int targetSdk) {
         if (packageName.isEmpty()) {
             packageName = getPackNameForUid(callingUid);
         }
 
+        boolean blocked = true;
         if (noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, callingUid, packageName, attributionTag)) {
             if (DEBUG) {
                 Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking");
             }
-            return false;
+            blocked = false;
         } else if (targetSdk < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
             if (DEBUG) {
                 Slog.i(TAG, "blockFocusMethod pack:" + packageName + " NOT blocking due to sdk="
                         + targetSdk);
             }
+            blocked = false;
+        }
+
+        metricsLogFocusReq(blocked, focusReqType, callingUid);
+
+        if (!blocked) {
             return false;
         }
 
         String errorMssg = "Focus request DENIED for uid:" + callingUid
-                + " clientId:" + clientId + " req:" + durationHint
+                + " clientId:" + clientId + " req:" + focusReqType
                 + " procState:" + mActivityManager.getUidProcessState(callingUid);
-
-        // TODO metrics
         mEventLogger.enqueueAndSlog(errorMssg, EventLogger.Event.ALOGI, TAG);
 
         return true;
     }
 
+    /*package*/ void metricsLogFocusReq(boolean blocked, int focusReq, int callingUid) {
+        final String metricId = blocked ? METRIC_COUNTERS_FOCUS_DENIAL.get(focusReq)
+                : METRIC_COUNTERS_FOCUS_GRANT.get(focusReq);
+        if (TextUtils.isEmpty(metricId)) {
+            Slog.e(TAG, "Bad string for focus metrics gain:" + focusReq + " blocked:" + blocked);
+            return;
+        }
+        try {
+            Counter.logIncrementWithUid(metricId, callingUid);
+        } catch (Exception e) {
+            Slog.e(TAG, "Counter error metricId:" + metricId + " for focus req:" + focusReq
+                    + " from uid:" + callingUid, e);
+        }
+    }
+
     private String getPackNameForUid(int uid) {
         final long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 70f3193..7e26356 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -1302,7 +1302,7 @@
         mEventLogger.enqueue((new EventLogger.StringEvent(
                 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
                     + "/" + Binder.getCallingPid()
-                    + " clientId=" + clientId))
+                    + " clientId=" + clientId + " callingPack=" + callingPackageName))
                 .printLog(TAG));
         try {
             // this will take care of notifying the new focus owner if needed
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index dc79ab2..643f330 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -900,8 +900,10 @@
 
         try {
             if (!isAbsoluteVolume) {
-                mLogger.enqueue(
-                        SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f, device));
+                if (mSafeMediaVolumeDevices.indexOfKey(device) >= 0) {
+                    mLogger.enqueue(SoundDoseEvent.getAbsVolumeAttenuationEvent(/*attenuation=*/0.f,
+                            device));
+                }
                 // remove any possible previous attenuation
                 soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
 
@@ -912,8 +914,12 @@
                     && safeDevicesContains(device)) {
                 float attenuationDb = -AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC,
                         (newIndex + 5) / 10, device);
-                mLogger.enqueue(
-                        SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device));
+
+                if (mSafeMediaVolumeDevices.indexOfKey(device) >= 0) {
+                    mLogger.enqueue(
+                            SoundDoseEvent.getAbsVolumeAttenuationEvent(attenuationDb, device));
+                }
+
                 soundDose.updateAttenuation(attenuationDb, device);
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index f050090..368b828 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -29,21 +29,7 @@
             ]
         },
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                  "include-filter": "com.android.server.audio"
-                },
-                {
-                  "include-annotation": "android.platform.test.annotations.Presubmit"
-                },
-                {
-                  "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                  "exclude-annotation": "org.junit.Ignore"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_audio"
         }
     ]
 }
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 276ab03..abfbddc 100644
--- a/services/core/java/com/android/server/biometrics/AuthSession.java
+++ b/services/core/java/com/android/server/biometrics/AuthSession.java
@@ -221,7 +221,8 @@
         mFingerprintSensorProperties = fingerprintSensorProperties;
         mCancelled = false;
         mBiometricFrameworkStatsLogger = logger;
-        mOperationContext = new OperationContextExt(true /* isBP */);
+        mOperationContext = new OperationContextExt(true /* isBP */,
+                preAuthInfo.getIsMandatoryBiometricsAuthentication() /* isMandatoryBiometrics */);
         mBiometricManager = mContext.getSystemService(BiometricManager.class);
 
         mSfpsSensorIds = mFingerprintSensorProperties.stream().filter(
@@ -285,7 +286,8 @@
             sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId,
                     mUserId, mSensorReceiver, mOpPackageName, mRequestId, cookie,
                     mPromptInfo.isAllowBackgroundAuthentication(),
-                    mPromptInfo.isForLegacyFingerprintManager());
+                    mPromptInfo.isForLegacyFingerprintManager(),
+                    mOperationContext.getIsMandatoryBiometrics());
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/BiometricSensor.java b/services/core/java/com/android/server/biometrics/BiometricSensor.java
index 42dd36a..c7532d4 100644
--- a/services/core/java/com/android/server/biometrics/BiometricSensor.java
+++ b/services/core/java/com/android/server/biometrics/BiometricSensor.java
@@ -107,12 +107,13 @@
     void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId,
             int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
             long requestId, int cookie, boolean allowBackgroundAuthentication,
-            boolean isForLegacyFingerprintManager)
+            boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics)
             throws RemoteException {
         mCookie = cookie;
         impl.prepareForAuthentication(requireConfirmation, token,
                 sessionId, userId, sensorReceiver, opPackageName, requestId, mCookie,
-                allowBackgroundAuthentication, isForLegacyFingerprintManager);
+                allowBackgroundAuthentication, isForLegacyFingerprintManager,
+                isMandatoryBiometrics);
         mSensorState = STATE_WAITING_FOR_COOKIE;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 0bd22f3..ac3c028 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -77,13 +77,15 @@
     private final int mBiometricStrengthRequested;
     private final BiometricCameraManager mBiometricCameraManager;
     private final boolean mOnlyMandatoryBiometricsRequested;
+    private final boolean mIsMandatoryBiometricsAuthentication;
 
     private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
             boolean credentialRequested, List<BiometricSensor> eligibleSensors,
             List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
             PromptInfo promptInfo, int userId, Context context,
             BiometricCameraManager biometricCameraManager,
-            boolean isOnlyMandatoryBiometricsRequested) {
+            boolean isOnlyMandatoryBiometricsRequested,
+            boolean isMandatoryBiometricsAuthentication) {
         mBiometricRequested = biometricRequested;
         mBiometricStrengthRequested = biometricStrengthRequested;
         mBiometricCameraManager = biometricCameraManager;
@@ -97,6 +99,7 @@
         this.userId = userId;
         this.context = context;
         this.mOnlyMandatoryBiometricsRequested = isOnlyMandatoryBiometricsRequested;
+        this.mIsMandatoryBiometricsAuthentication = isMandatoryBiometricsAuthentication;
     }
 
     static PreAuthInfo create(ITrustManager trustManager,
@@ -110,12 +113,16 @@
 
         final boolean isOnlyMandatoryBiometricsRequested = promptInfo.getAuthenticators()
                 == BiometricManager.Authenticators.MANDATORY_BIOMETRICS;
+        boolean isMandatoryBiometricsAuthentication = false;
 
         if (dropCredentialFallback(promptInfo.getAuthenticators(),
                 settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(
                         userId), trustManager)) {
+            isMandatoryBiometricsAuthentication = true;
             promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG);
-            promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
+            if (promptInfo.getNegativeButtonText() == null) {
+                promptInfo.setNegativeButtonText(context.getString(R.string.cancel));
+            }
         }
 
         final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
@@ -166,7 +173,8 @@
 
         return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
                 eligibleSensors, ineligibleSensors, credentialAvailable, promptInfo, userId,
-                context, biometricCameraManager, isOnlyMandatoryBiometricsRequested);
+                context, biometricCameraManager, isOnlyMandatoryBiometricsRequested,
+                isMandatoryBiometricsAuthentication);
     }
 
     private static boolean dropCredentialFallback(int authenticators,
@@ -387,25 +395,6 @@
                     status = CREDENTIAL_NOT_ENROLLED;
                 }
             }
-        } else if (Flags.mandatoryBiometrics() && mOnlyMandatoryBiometricsRequested) {
-            if (!eligibleSensors.isEmpty()) {
-                for (BiometricSensor sensor : eligibleSensors) {
-                    modality |= sensor.modality;
-                }
-
-                if (modality == TYPE_FACE && cameraPrivacyEnabled) {
-                    // If the only modality requested is face, credential is unavailable,
-                    // and the face sensor privacy is enabled then return
-                    // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
-                    //
-                    // Note: This sensor will not be eligible for calls to authenticate.
-                    status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
-                } else {
-                    status = AUTHENTICATOR_OK;
-                }
-            } else {
-                status = MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
-            }
         } else if (mBiometricRequested) {
             if (!eligibleSensors.isEmpty()) {
                 for (BiometricSensor sensor : eligibleSensors) {
@@ -434,6 +423,9 @@
         } else if (credentialRequested) {
             modality |= TYPE_CREDENTIAL;
             status = credentialAvailable ? AUTHENTICATOR_OK : CREDENTIAL_NOT_ENROLLED;
+        } else if (Flags.mandatoryBiometrics() && mOnlyMandatoryBiometricsRequested
+                && !mIsMandatoryBiometricsAuthentication) {
+            status = MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
         } else {
             // This should not be possible via the public API surface and is here mainly for
             // "correctness". An exception should have been thrown before getting here.
@@ -457,6 +449,12 @@
                         getInternalStatus().second));
     }
 
+    /** Returns if mandatory biometrics authentication is in effect */
+    boolean getIsMandatoryBiometricsAuthentication() {
+        return mIsMandatoryBiometricsAuthentication;
+    }
+
+
     /**
      * For the given request, generate the appropriate reason why authentication cannot be started.
      * Note that for some errors, modality is intentionally cleared.
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index df29ca4..8711214 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -35,6 +35,7 @@
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_SENSOR_PRIVACY_ENABLED;
 import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
+import static com.android.server.biometrics.PreAuthInfo.MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -48,6 +49,7 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.biometrics.SensorProperties;
@@ -309,11 +311,16 @@
                 break;
             case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
             case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
+                biometricManagerCode = Flags.mandatoryBiometrics()
+                        ? BiometricManager.BIOMETRIC_ERROR_LOCKOUT
+                        : BiometricManager.BIOMETRIC_SUCCESS;
                 break;
             case BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE:
+                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
+                break;
             default:
                 Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -375,6 +382,8 @@
                 return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
             case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
                 return BiometricConstants.BIOMETRIC_ERROR_SENSOR_PRIVACY_ENABLED;
+            case MANDATORY_BIOMETRIC_UNAVAILABLE_ERROR:
+                return BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE;
             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
             case BIOMETRIC_HARDWARE_NOT_DETECTED:
             case BIOMETRIC_NOT_ENABLED_FOR_APPS:
@@ -586,6 +595,8 @@
         }
     }
 
+    // LINT.IfChange
+
     /**
      * Checks if a client package is running in the background.
      *
@@ -618,4 +629,6 @@
 
         return true;
     }
+    // LINT.ThenChange(frameworks/base/packages/SystemUI/shared/biometrics/src/com/android
+    // /systemui/biometrics/Utils.kt)
 }
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
index f31b2e1..bc58501 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricFrameworkStatsLogger.java
@@ -98,7 +98,8 @@
                 foldType(operationContext.getFoldState()),
                 operationContext.getOrderAndIncrement(),
                 toProtoWakeReason(operationContext),
-                toProtoWakeReasonDetails(operationContext));
+                toProtoWakeReasonDetails(operationContext),
+                operationContext.getIsMandatoryBiometrics());
     }
 
     /** {@see FrameworkStatsLog.BIOMETRIC_AUTHENTICATED}. */
@@ -149,7 +150,8 @@
                 foldType(operationContext.getFoldState()),
                 operationContext.getOrderAndIncrement(),
                 toProtoWakeReason(operationContext),
-                toProtoWakeReasonDetails(operationContext));
+                toProtoWakeReasonDetails(operationContext),
+                operationContext.getIsMandatoryBiometrics());
     }
 
     @VisibleForTesting
@@ -241,6 +243,14 @@
                 -1 /* sensorId */);
     }
 
+    /** {@see FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED}. */
+    public void reportFingerprintsLoe(int statsModality) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+                statsModality,
+                BiometricsProtoEnums.ISSUE_FINGERPRINTS_LOE,
+                -1 /* sensorId */);
+    }
+
     /** {@see FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION}. */
     public void logFrameworkNotification(int action, int modality) {
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_FRR_NOTIFICATION,
diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
index ff1e5d5..9351bc0 100644
--- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
+++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java
@@ -296,6 +296,15 @@
         mSink.reportUnknownTemplateEnrolledFramework(mStatsModality);
     }
 
+    /** Report unknown enrollment in framework settings */
+    public void logFingerprintsLoe() {
+        if (shouldSkipLogging()) {
+            return;
+        }
+
+        mSink.reportFingerprintsLoe(mStatsModality);
+    }
+
     /**
      * Get a callback to start/stop ALS capture when the client runs. Do not create
      * multiple callbacks since there is at most one light sensor (they will all share
diff --git a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
index da4e515..4df63e2 100644
--- a/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
+++ b/services/core/java/com/android/server/biometrics/log/OperationContextExt.java
@@ -50,21 +50,33 @@
     @Surface.Rotation private int mOrientation = Surface.ROTATION_0;
     private int mFoldState = IBiometricContextListener.FoldState.UNKNOWN;
     private final boolean mIsBP;
+    private final boolean mIsMandatoryBiometrics;
 
     /** Create a context. */
     public OperationContextExt(boolean isBP) {
         this(new OperationContext(), isBP, BiometricAuthenticator.TYPE_NONE);
     }
 
-    public OperationContextExt(boolean isBP, @BiometricAuthenticator.Modality int modality) {
-        this(new OperationContext(), isBP, modality);
+    public OperationContextExt(boolean isBP, boolean isMandatoryBiometrics) {
+        this(new OperationContext(), isBP, BiometricAuthenticator.TYPE_NONE, isMandatoryBiometrics);
+    }
+
+    public OperationContextExt(boolean isBP, @BiometricAuthenticator.Modality int modality,
+            boolean isMandatoryBiometrics) {
+        this(new OperationContext(), isBP, modality, isMandatoryBiometrics);
     }
 
     /** Create a wrapped context. */
     public OperationContextExt(@NonNull OperationContext context, boolean isBP,
             @BiometricAuthenticator.Modality int modality) {
+        this(context, isBP, modality, false /* isMandatoryBiometrics */);
+    }
+
+    public OperationContextExt(@NonNull OperationContext context, boolean isBP,
+            @BiometricAuthenticator.Modality int modality, boolean isMandatoryBiometrics) {
         mAidlContext = context;
         mIsBP = isBP;
+        mIsMandatoryBiometrics = isMandatoryBiometrics;
 
         if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
             mAidlContext.operationState = OperationState.fingerprintOperationState(
@@ -285,6 +297,11 @@
         return mAidlContext.operationState;
     }
 
+    /** If mandatory biometrics is active. */
+    public boolean getIsMandatoryBiometrics() {
+        return mIsMandatoryBiometrics;
+    }
+
     /** Update this object with the latest values from the given context. */
     OperationContextExt update(@NonNull BiometricContext biometricContext, boolean isCrypto) {
         mAidlContext.isAod = biometricContext.isAod();
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index fbd32a6..04522e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -59,9 +59,10 @@
     public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int cookie, int sensorId, boolean shouldVibrate,
-            @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) {
+            @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
+            boolean isMandatoryBiometrics) {
         super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId,
-                logger, biometricContext);
+                logger, biometricContext, isMandatoryBiometrics);
         mPowerManager = context.getSystemService(PowerManager.class);
         mShouldVibrate = shouldVibrate;
     }
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 daaafcb..09386ae28 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -99,7 +99,7 @@
             boolean shouldVibrate, int sensorStrength) {
         super(context, lazyDaemon, token, listener, options.getUserId(),
                 options.getOpPackageName(), cookie, options.getSensorId(), shouldVibrate,
-                biometricLogger, biometricContext);
+                biometricLogger, biometricContext, options.isMandatoryBiometrics());
         mIsStrongBiometric = isStrongBiometric;
         mOperationId = operationId;
         mRequireConfirmation = requireConfirmation;
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 438367d..32c0bd3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -60,7 +60,7 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             int enrollReason) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
-                shouldVibrate, logger, biometricContext);
+                shouldVibrate, logger, biometricContext, false /* isMandatoryBiometrics */);
         mBiometricUtils = utils;
         mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
         mTimeoutSec = timeoutSec;
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 2adf0cb..b573b56 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -37,7 +37,7 @@
             int userId, @NonNull String owner, int sensorId,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
-                biometricLogger, biometricContext);
+                biometricLogger, biometricContext, false /* isMandatoryBiometrics */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 0f01510..3bc51a9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -41,26 +41,29 @@
     private final OperationContextExt mOperationContext;
 
     /**
-     * @param context    system_server context
-     * @param lazyDaemon pointer for lazy retrieval of the HAL
-     * @param token      a unique token for the client
-     * @param listener   recipient of related events (e.g. authentication)
-     * @param userId     target user id for operation
-     * @param owner      name of the client that owns this
-     * @param cookie     BiometricPrompt authentication cookie (to be moved into a subclass soon)
-     * @param sensorId   ID of the sensor that the operation should be requested of
-     * @param biometricLogger framework stats logger
+     * @param context          system_server context
+     * @param lazyDaemon       pointer for lazy retrieval of the HAL
+     * @param token            a unique token for the client
+     * @param listener         recipient of related events (e.g. authentication)
+     * @param userId           target user id for operation
+     * @param owner            name of the client that owns this
+     * @param cookie           BiometricPrompt authentication cookie (to be moved into a subclass
+     *                         soon)
+     * @param sensorId         ID of the sensor that the operation should be requested of
+     * @param biometricLogger  framework stats logger
      * @param biometricContext system context metadata
      */
     public HalClientMonitor(@NonNull Context context, @NonNull Supplier<T> lazyDaemon,
             @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
             @NonNull String owner, int cookie, int sensorId,
-            @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
+            @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
+            boolean isMandatoryBiometrics) {
         super(context, token, listener, userId, owner, cookie, sensorId,
                 biometricLogger, biometricContext);
         mLazyDaemon = lazyDaemon;
         int modality = listener != null ? listener.getModality() : BiometricAuthenticator.TYPE_NONE;
-        mOperationContext = new OperationContextExt(isBiometricPrompt(), modality);
+        mOperationContext = new OperationContextExt(isBiometricPrompt(), modality,
+                isMandatoryBiometrics);
     }
 
     @Nullable
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 77e27ba..6c30559 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -143,7 +143,8 @@
             @NonNull BiometricUtils<S> utils,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */,
-                userId, owner, 0 /* cookie */, sensorId, logger, biometricContext);
+                userId, owner, 0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mBiometricUtils = utils;
         mAuthenticatorIds = authenticatorIds;
         mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
@@ -161,6 +162,11 @@
 
         getLogger().logUnknownEnrollmentInHal();
 
+        if (mBiometricUtils.hasValidBiometricUserState(getContext(), getTargetUserId())
+                && Flags.notifyFingerprintsLoe()) {
+            getLogger().logFingerprintsLoe();
+        }
+
         mCurrentTask.start(mRemoveCallback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 81ab26d..2c2c685 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -55,7 +55,8 @@
         // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
         super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner,
-                0 /* cookie */, sensorId, logger, biometricContext);
+                0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mEnrolledList = enrolledList;
         mInitialEnrolledSize = mEnrolledList.size();
         mUtils = utils;
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index d5aa5e2..6c93366 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -49,7 +49,7 @@
             @NonNull IInvalidationCallback callback) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId,
                 context.getOpPackageName(), 0 /* cookie */, sensorId,
-                logger, biometricContext);
+                logger, biometricContext, false /* isMandatoryBiometrics */);
         mAuthenticatorIds = authenticatorIds;
         mInvalidationCallback = callback;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index d2ef278..ad5877a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -49,7 +49,7 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
-                logger, biometricContext);
+                logger, biometricContext, false /* isMandatoryBiometrics */);
         mBiometricUtils = utils;
         mAuthenticatorIds = authenticatorIds;
         mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 88f4da2..0c8a2dd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -32,7 +32,8 @@
             @NonNull IBinder token, int userId, @NonNull String owner, int sensorId,
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) {
         super(context, lazyDaemon, token, null /* listener */, userId, owner,
-                0 /* cookie */, sensorId, biometricLogger, biometricContext);
+                0 /* cookie */, sensorId, biometricLogger, biometricContext,
+                false /* isMandatoryBiometrics */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
index 21c9f64..ff694cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java
@@ -51,7 +51,8 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull UserStartedCallback<U> callback) {
         super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
-                0 /* cookie */, sensorId, logger, biometricContext);
+                0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mUserStartedCallback = callback;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
index e01c4ec..9119bc7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java
@@ -54,7 +54,8 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             @NonNull UserStoppedCallback callback) {
         super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(),
-                0 /* cookie */, sensorId, logger, biometricContext);
+                0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mUserStoppedCallback = callback;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index 2211003..67d7505 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -63,13 +63,14 @@
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
             String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication,
-            boolean isForLegacyFingerprintManager)
+            boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics)
             throws RemoteException {
         mFaceService.prepareForAuthentication(requireConfirmation, token, operationId,
                 sensorReceiver, new FaceAuthenticateOptions.Builder()
                         .setUserId(userId)
                         .setSensorId(mSensorId)
                         .setOpPackageName(opPackageName)
+                        .setIsMandatoryBiometrics(isMandatoryBiometrics)
                         .build(),
                 requestId, cookie, allowBackgroundAuthentication);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
index cf677d5..7b1186c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java
@@ -80,13 +80,16 @@
     private final AuthSessionCoordinator mAuthSessionCoordinator;
     @NonNull
     private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+    @NonNull
+    private final FaceUtils mBiometricUtils;
 
     public AidlResponseHandler(@NonNull Context context,
             @NonNull BiometricScheduler scheduler, int sensorId, int userId,
             @NonNull LockoutTracker lockoutTracker,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull AuthSessionCoordinator authSessionCoordinator,
-            @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+            @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback,
+            @NonNull FaceUtils biometricUtils) {
         mContext = context;
         mScheduler = scheduler;
         mSensorId = sensorId;
@@ -95,6 +98,7 @@
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mAuthSessionCoordinator = authSessionCoordinator;
         mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
+        mBiometricUtils = biometricUtils;
     }
 
     @Override
@@ -167,8 +171,7 @@
         } else {
             currentUserId = client.getTargetUserId();
         }
-        final CharSequence name = FaceUtils.getInstance(mSensorId)
-                .getUniqueName(mContext, currentUserId);
+        final CharSequence name = mBiometricUtils.getUniqueName(mContext, currentUserId);
         final Face face = new Face(name, enrollmentId, mSensorId);
 
         handleResponse(FaceEnrollClient.class, (c) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 8b4da31..8eb62eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -83,7 +83,8 @@
             boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
         super(context, lazyDaemon, token, listener, options.getUserId(),
                 options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
-                false /* shouldVibrate */, logger, biometricContext);
+                false /* shouldVibrate */, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         setRequestId(requestId);
         mAuthenticationStateListeners = authenticationStateListeners;
         mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index 3eecc6de..d4ec573 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -60,7 +60,6 @@
 import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.face.FaceService;
-import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -85,6 +84,7 @@
     private final int mMaxTemplatesPerUser;
     private final boolean mDebugConsent;
     private final @android.hardware.face.FaceEnrollOptions.EnrollReason int mEnrollReason;
+    private final BiometricUtils<Face> mBiometricUtils;
 
     private final ClientMonitorCallback mPreviewHandleDeleterCallback =
             new ClientMonitorCallback() {
@@ -107,7 +107,8 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             int maxTemplatesPerUser, boolean debugConsent,
             android.hardware.face.FaceEnrollOptions options,
-            @NonNull AuthenticationStateListeners authenticationStateListeners) {
+            @NonNull AuthenticationStateListeners authenticationStateListeners,
+            @NonNull BiometricUtils<Face> biometricUtils) {
         super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
                 timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext,
                 BiometricFaceConstants.reasonToMetric(options.getEnrollReason()));
@@ -122,6 +123,7 @@
         mDebugConsent = debugConsent;
         mDisabledFeatures = disabledFeatures;
         mPreviewSurface = previewSurface;
+        mBiometricUtils = biometricUtils;
         Slog.w(TAG, "EnrollOptions "
                 + android.hardware.face.FaceEnrollOptions.enrollReasonToString(
                         options.getEnrollReason()));
@@ -144,7 +146,7 @@
 
     @Override
     protected boolean hasReachedEnrollmentLimit() {
-        return FaceUtils.getInstance(getSensorId()).getBiometricsForUser(getContext(),
+        return mBiometricUtils.getBiometricsForUser(getContext(),
                 getTargetUserId()).size() >= mMaxTemplatesPerUser;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index 1f4f612..dbd293c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -42,7 +42,8 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName,
-                0 /* cookie */, sensorId, logger, biometricContext);
+                0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mAuthenticatorIds = authenticatorIds;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index c41b706..8d1336f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -55,7 +55,7 @@
             @NonNull String owner, int sensorId, @NonNull BiometricLogger logger,
             @NonNull BiometricContext biometricContext, int feature) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
-                logger, biometricContext);
+                logger, biometricContext, false /* isMandatoryBiometrics */);
         mUserId = userId;
         mFeature = feature;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
index 964bf6c..c27b7c4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java
@@ -30,7 +30,6 @@
 import com.android.server.biometrics.sensors.InternalCleanupClient;
 import com.android.server.biometrics.sensors.InternalEnumerateClient;
 import com.android.server.biometrics.sensors.RemovalClient;
-import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.List;
 import java.util.Map;
@@ -75,7 +74,7 @@
     @Override
     protected void onAddUnknownTemplate(int userId,
             @NonNull BiometricAuthenticator.Identifier identifier) {
-        FaceUtils.getInstance(getSensorId()).addBiometricForUser(
+        mBiometricUtils.addBiometricForUser(
                 getContext(), getTargetUserId(), (Face) identifier);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index f0a4189..bb213bf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -72,7 +72,6 @@
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.SensorList;
-import com.android.server.biometrics.sensors.face.FaceUtils;
 import com.android.server.biometrics.sensors.face.ServiceProvider;
 import com.android.server.biometrics.sensors.face.UsageStats;
 import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSensorAdapter;
@@ -326,8 +325,8 @@
         }
 
         if (Build.isDebuggable()) {
-            BiometricUtils<Face> utils = FaceUtils.getInstance(
-                    mFaceSensors.keyAt(0));
+            BiometricUtils<Face> utils = mFaceSensors.get(
+                    mFaceSensors.keyAt(0)).getFaceUtilsInstance();
             for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
                 List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
                 Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
@@ -386,7 +385,7 @@
                     new InvalidationRequesterClient<>(mContext, userId, sensorId,
                             BiometricLogger.ofUnknown(mContext),
                             mBiometricContext,
-                            FaceUtils.getInstance(sensorId));
+                            mFaceSensors.get(sensorId).getFaceUtilsInstance());
             scheduleForSensor(sensorId, client);
         });
     }
@@ -415,7 +414,8 @@
     @NonNull
     @Override
     public List<Face> getEnrolledFaces(int sensorId, int userId) {
-        return FaceUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
+        return mFaceSensors.get(sensorId).getFaceUtilsInstance()
+                .getBiometricsForUser(mContext, userId);
     }
 
     @Override
@@ -497,13 +497,14 @@
             final FaceEnrollClient client = new FaceEnrollClient(mContext,
                     mFaceSensors.get(sensorId).getLazySession(), token,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
-                    ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
+                    opPackageName, id, mFaceSensors.get(sensorId).getFaceUtilsInstance(),
+                    disabledFeatures, ENROLL_TIMEOUT_SEC, previewSurface, sensorId,
                     createLogger(BiometricsProtoEnums.ACTION_ENROLL,
                             BiometricsProtoEnums.CLIENT_UNKNOWN,
                             mAuthenticationStatsCollector),
                     mBiometricContext, maxTemplatesPerUser, debugConsent, options,
-                    mAuthenticationStateListeners);
+                    mAuthenticationStateListeners,
+                    mFaceSensors.get(sensorId).getFaceUtilsInstance());
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
         return id;
@@ -615,7 +616,7 @@
     @Override
     public void scheduleRemoveAll(int sensorId, @NonNull IBinder token, int userId,
             @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
-        final List<Face> faces = FaceUtils.getInstance(sensorId)
+        final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
                 .getBiometricsForUser(mContext, userId);
         final int[] faceIds = new int[faces.size()];
         for (int i = 0; i < faces.size(); i++) {
@@ -632,7 +633,7 @@
             final FaceRemovalClient client = new FaceRemovalClient(mContext,
                     mFaceSensors.get(sensorId).getLazySession(), token,
                     new ClientMonitorCallbackConverter(receiver), faceIds, userId,
-                    opPackageName, FaceUtils.getInstance(sensorId), sensorId,
+                    opPackageName, mFaceSensors.get(sensorId).getFaceUtilsInstance(), sensorId,
                     createLogger(BiometricsProtoEnums.ACTION_REMOVE,
                             BiometricsProtoEnums.CLIENT_UNKNOWN,
                             mAuthenticationStatsCollector),
@@ -666,7 +667,7 @@
             @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
         mHandler.post(() -> {
             mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
-            final List<Face> faces = FaceUtils.getInstance(sensorId)
+            final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
                     .getBiometricsForUser(mContext, userId);
             if (faces.isEmpty()) {
                 Slog.w(getTag(), "Ignoring setFeature, no templates enrolled for user: " + userId);
@@ -687,7 +688,7 @@
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) {
         mHandler.post(() -> {
             mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId);
-            final List<Face> faces = FaceUtils.getInstance(sensorId)
+            final List<Face> faces = mFaceSensors.get(sensorId).getFaceUtilsInstance()
                     .getBiometricsForUser(mContext, userId);
             if (faces.isEmpty()) {
                 Slog.w(getTag(), "Ignoring getFeature, no templates enrolled for user: " + userId);
@@ -727,7 +728,7 @@
                                     BiometricsProtoEnums.CLIENT_UNKNOWN,
                                     mAuthenticationStatsCollector),
                             mBiometricContext,
-                            FaceUtils.getInstance(sensorId),
+                            mFaceSensors.get(sensorId).getFaceUtilsInstance(),
                             mFaceSensors.get(sensorId).getAuthenticatorIds());
             if (favorHalEnrollments) {
                 client.setFavorHalEnrollments();
@@ -768,7 +769,7 @@
             JSONArray sets = new JSONArray();
             for (UserInfo user : UserManager.get(mContext).getUsers()) {
                 final int userId = user.getUserHandle().getIdentifier();
-                final int c = FaceUtils.getInstance(sensorId)
+                final int c = mFaceSensors.get(sensorId).getFaceUtilsInstance()
                         .getBiometricsForUser(mContext, userId).size();
                 JSONObject set = new JSONObject();
                 set.put("id", userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index d02eefa..93bc1f2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -60,7 +60,8 @@
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @Authenticators.Types int biometricStrength) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
-                0 /* cookie */, sensorId, logger, biometricContext);
+                0 /* cookie */, sensorId, logger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
         mLockoutTracker = lockoutTracker;
         mLockoutResetDispatcher = lockoutResetDispatcher;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index f6da872..fc73e58 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -52,7 +52,7 @@
             @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
             int feature, boolean enabled, byte[] hardwareAuthToken) {
         super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId,
-                logger, biometricContext);
+                logger, biometricContext, false /* isMandatoryBiometrics */);
         mFeature = feature;
         mEnabled = enabled;
         mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index b0e7575..6f95349 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -158,7 +158,7 @@
                                         Slog.e(TAG, "Face sensor hardware unavailable.");
                                         mCurrentSession = null;
                                     }
-                                });
+                                }, getFaceUtilsInstance());
 
                         return Sensor.this.getStartUserClient(resultController, sensorId,
                                 newUserId, provider);
@@ -280,8 +280,7 @@
             final long userToken = proto.start(SensorStateProto.USER_STATES);
             proto.write(UserStateProto.USER_ID, userId);
             proto.write(UserStateProto.NUM_ENROLLED,
-                    FaceUtils.getInstance(mSensorProperties.sensorId)
-                            .getBiometricsForUser(mContext, userId).size());
+                    getFaceUtilsInstance().getBiometricsForUser(mContext, userId).size());
             proto.end(userToken);
         }
 
@@ -358,4 +357,8 @@
             Supplier<AidlSession> lazySession) {
         mLazySession = lazySession;
     }
+
+    public FaceUtils getFaceUtilsInstance() {
+        return FaceUtils.getInstance(mSensorProperties.sensorId);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
index 9a4c29d..444a6d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java
@@ -159,6 +159,11 @@
     }
 
     @Override
+    public FaceUtils getFaceUtilsInstance() {
+        return FaceUtils.getLegacyInstance(getSensorProperties().sensorId);
+    }
+
+    @Override
     protected LockoutTracker getLockoutTracker(boolean forAuth) {
         return mLockoutTracker;
     }
@@ -180,7 +185,8 @@
                 mLockoutTracker,
                 mLockoutResetDispatcher,
                 mAuthSessionCoordinator,
-                mAidlResponseHandlerCallback);
+                mAidlResponseHandlerCallback,
+                getFaceUtilsInstance());
     }
 
     private IBiometricsFace getIBiometricsFace() {
@@ -247,8 +253,7 @@
         return new FaceUpdateActiveUserClient(getContext(), this::getIBiometricsFace,
                 mUserStartedCallback, userId, TAG, getSensorProperties().sensorId,
                 BiometricLogger.ofUnknown(getContext()), getBiometricContext(),
-                !FaceUtils.getInstance(getSensorProperties().sensorId).getBiometricsForUser(
-                        getContext(), userId).isEmpty(),
+                !getFaceUtilsInstance().getBiometricsForUser(getContext(), userId).isEmpty(),
                 getAuthenticatorIds());
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index b6fa0c1..d50ab8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -63,13 +63,14 @@
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
             String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication,
-            boolean isForLegacyFingerprintManager)
+            boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics)
             throws RemoteException {
         mFingerprintService.prepareForAuthentication(token, operationId, sensorReceiver,
                 new FingerprintAuthenticateOptions.Builder()
                         .setSensorId(mSensorId)
                         .setUserId(userId)
                         .setOpPackageName(opPackageName)
+                        .setIsMandatoryBiometrics(isMandatoryBiometrics)
                         .build(),
                 requestId, cookie, allowBackgroundAuthentication, isForLegacyFingerprintManager);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
index 6d1715f..80b7cde 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java
@@ -80,13 +80,16 @@
     private final AuthSessionCoordinator mAuthSessionCoordinator;
     @NonNull
     private final AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+    @NonNull
+    private final FingerprintUtils mBiometricUtils;
 
     public AidlResponseHandler(@NonNull Context context,
             @NonNull BiometricScheduler scheduler, int sensorId, int userId,
             @NonNull LockoutTracker lockoutTracker,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull AuthSessionCoordinator authSessionCoordinator,
-            @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) {
+            @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback,
+            @NonNull FingerprintUtils biometricUtils) {
         mContext = context;
         mScheduler = scheduler;
         mSensorId = sensorId;
@@ -95,6 +98,7 @@
         mLockoutResetDispatcher = lockoutResetDispatcher;
         mAuthSessionCoordinator = authSessionCoordinator;
         mAidlResponseHandlerCallback = aidlResponseHandlerCallback;
+        mBiometricUtils = biometricUtils;
     }
 
     @Override
@@ -158,8 +162,7 @@
         } else {
             currentUserId = client.getTargetUserId();
         }
-        final CharSequence name = FingerprintUtils.getInstance(mSensorId)
-                .getUniqueName(mContext, currentUserId);
+        final CharSequence name = mBiometricUtils.getUniqueName(mContext, currentUserId);
         final Fingerprint fingerprint = new Fingerprint(name, currentUserId,
                 enrollmentId, mSensorId);
         handleResponse(FingerprintEnrollClient.class, (c) -> {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index fb48053..a81ab8e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -71,7 +71,8 @@
             boolean isStrongBiometric) {
         super(context, lazyDaemon, token, listener, options.getUserId(),
                 options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
-                true /* shouldVibrate */, biometricLogger, biometricContext);
+                true /* shouldVibrate */, biometricLogger, biometricContext,
+                false /* isMandatoryBiometrics */);
         setRequestId(requestId);
         mAuthenticationStateListeners = authenticationStateListeners;
         mIsStrongBiometric = isStrongBiometric;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index 0353969..f77e116 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -42,7 +42,8 @@
             @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
             Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
-                0 /* cookie */, sensorId, biometricLogger, biometricContext);
+                0 /* cookie */, sensorId, biometricLogger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mAuthenticatorIds = authenticatorIds;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
index 1fc5179..40b8a45 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java
@@ -81,7 +81,7 @@
     @Override
     protected void onAddUnknownTemplate(int userId,
             @NonNull BiometricAuthenticator.Identifier identifier) {
-        FingerprintUtils.getInstance(getSensorId()).addBiometricForUser(
+        mBiometricUtils.addBiometricForUser(
                 getContext(), getTargetUserId(), (Fingerprint) identifier);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 12baf00..9edaa4e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -79,7 +79,6 @@
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.SensorList;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler;
 import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
@@ -354,8 +353,9 @@
         }
 
         if (Build.isDebuggable()) {
-            BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
-                    mFingerprintSensors.keyAt(0));
+            final int sensorId = mFingerprintSensors.keyAt(0);
+            final BiometricUtils<Fingerprint> utils = mFingerprintSensors.get(sensorId)
+                    .getFingerprintUtilsInstance();
             for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
                 List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
                 Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
@@ -442,7 +442,7 @@
                     new InvalidationRequesterClient<>(mContext, userId, sensorId,
                             BiometricLogger.ofUnknown(mContext),
                             mBiometricContext,
-                            FingerprintUtils.getInstance(sensorId));
+                            mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance());
             scheduleForSensor(sensorId, client);
         });
     }
@@ -507,7 +507,7 @@
             final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
                     mFingerprintSensors.get(sensorId).getLazySession(), token, id,
                     new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
-                    opPackageName, FingerprintUtils.getInstance(sensorId),
+                    opPackageName, mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
                     sensorId, createLogger(BiometricsProtoEnums.ACTION_ENROLL,
                             BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector),
                     mBiometricContext,
@@ -638,8 +638,8 @@
     public void scheduleRemoveAll(int sensorId, @NonNull IBinder token,
             @NonNull IFingerprintServiceReceiver receiver, int userId,
             @NonNull String opPackageName) {
-        final List<Fingerprint> fingers = FingerprintUtils.getInstance(sensorId)
-                .getBiometricsForUser(mContext, userId);
+        final List<Fingerprint> fingers = mFingerprintSensors.get(sensorId)
+                .getFingerprintUtilsInstance().getBiometricsForUser(mContext, userId);
         final int[] fingerIds = new int[fingers.size()];
         for (int i = 0; i < fingers.size(); i++) {
             fingerIds[i] = fingers.get(i).getBiometricId();
@@ -655,11 +655,10 @@
             final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext,
                     mFingerprintSensors.get(sensorId).getLazySession(), token,
                     new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
-                    opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
-                    createLogger(BiometricsProtoEnums.ACTION_REMOVE,
-                            BiometricsProtoEnums.CLIENT_UNKNOWN,
-                            mAuthenticationStatsCollector),
-                    mBiometricContext,
+                    opPackageName, mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
+                    sensorId, createLogger(BiometricsProtoEnums.ACTION_REMOVE,
+                    BiometricsProtoEnums.CLIENT_UNKNOWN,
+                    mAuthenticationStatsCollector), mBiometricContext,
                     mFingerprintSensors.get(sensorId).getAuthenticatorIds());
             scheduleForSensor(sensorId, client, mBiometricStateCallback);
         });
@@ -683,7 +682,7 @@
                                     BiometricsProtoEnums.CLIENT_UNKNOWN,
                                     mAuthenticationStatsCollector),
                             mBiometricContext,
-                            FingerprintUtils.getInstance(sensorId),
+                            mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance(),
                             mFingerprintSensors.get(sensorId).getAuthenticatorIds());
             if (favorHalEnrollments) {
                 client.setFavorHalEnrollments();
@@ -706,14 +705,15 @@
 
     @Override
     public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
-        FingerprintUtils.getInstance(sensorId)
+        mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
                 .renameBiometricForUser(mContext, userId, fingerId, name);
     }
 
     @NonNull
     @Override
     public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
-        return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId);
+        return mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
+                .getBiometricsForUser(mContext, userId);
     }
 
     @Override
@@ -842,7 +842,7 @@
             JSONArray sets = new JSONArray();
             for (UserInfo user : UserManager.get(mContext).getUsers()) {
                 final int userId = user.getUserHandle().getIdentifier();
-                final int c = FingerprintUtils.getInstance(sensorId)
+                final int c = mFingerprintSensors.get(sensorId).getFingerprintUtilsInstance()
                         .getBiometricsForUser(mContext, userId).size();
                 JSONObject set = new JSONObject();
                 set.put("id", userId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 387ae12..81a3d83 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -59,7 +59,8 @@
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @Authenticators.Types int biometricStrength) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
-                0 /* cookie */, sensorId, biometricLogger, biometricContext);
+                0 /* cookie */, sensorId, biometricLogger, biometricContext,
+                false /* isMandatoryBiometrics */);
         mHardwareAuthToken = hardwareAuthToken == null ? null :
                 HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
         mLockoutCache = lockoutTracker;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index 1c6dfe0..d12d7b2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -170,7 +170,7 @@
                                                 "Fingerprint sensor hardware unavailable.");
                                         mCurrentSession = null;
                                     }
-                                });
+                                }, getFingerprintUtilsInstance());
 
                         return Sensor.this.getStartUserClient(resultController, sensorId,
                                 newUserId);
@@ -187,7 +187,7 @@
                             + halInterfaceVersion);
                     mCurrentSession = new AidlSession(halInterfaceVersion,
                             newSession, userIdStarted, resultController);
-                    if (FingerprintUtils.getInstance(sensorId)
+                    if (getFingerprintUtilsInstance()
                             .isInvalidationInProgress(mContext, userIdStarted)) {
                         Slog.w(TAG,
                                 "Scheduling unfinished invalidation request for "
@@ -307,9 +307,8 @@
 
             final long userToken = proto.start(SensorStateProto.USER_STATES);
             proto.write(UserStateProto.USER_ID, userId);
-            proto.write(UserStateProto.NUM_ENROLLED,
-                    FingerprintUtils.getInstance(mSensorProperties.sensorId)
-                            .getBiometricsForUser(mContext, userId).size());
+            proto.write(UserStateProto.NUM_ENROLLED, getFingerprintUtilsInstance()
+                    .getBiometricsForUser(mContext, userId).size());
             proto.end(userToken);
         }
 
@@ -386,4 +385,8 @@
     public FingerprintProvider getProvider() {
         return mProvider;
     }
+
+    public FingerprintUtils getFingerprintUtilsInstance() {
+        return FingerprintUtils.getInstance(mSensorProperties.sensorId);
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index 59e64cd..87bd807 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -121,6 +121,11 @@
             final int targetId = getTargetUserId();
             Slog.d(TAG, "Setting active user: " + targetId);
             HidlToAidlSessionAdapter sessionAdapter = (HidlToAidlSessionAdapter) getFreshDaemon();
+            if (sessionAdapter.getIBiometricsFingerprint() == null) {
+                Slog.e(TAG, "Failed to setActiveGroup: HIDL daemon is null.");
+                mCallback.onClientFinished(this, false /* success */);
+                return;
+            }
             sessionAdapter.setActiveGroup(targetId, mDirectory.getAbsolutePath());
             mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics
                     ? sessionAdapter.getAuthenticatorIdForUpdateClient() : 0L);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
index 3214b6d..8f52d00 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java
@@ -148,6 +148,11 @@
     }
 
     @Override
+    public FingerprintUtils getFingerprintUtilsInstance() {
+        return FingerprintUtils.getLegacyInstance(getSensorProperties().sensorId);
+    }
+
+    @Override
     @Nullable
     @VisibleForTesting
     protected AidlSession getSessionForUser(int userId) {
@@ -186,7 +191,8 @@
                 mLockoutTracker,
                 mLockoutResetDispatcher,
                 mAuthSessionCoordinator,
-                mAidlResponseHandlerCallback);
+                mAidlResponseHandlerCallback,
+                getFingerprintUtilsInstance());
     }
 
     @VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() {
@@ -266,8 +272,7 @@
                 () -> getSession().getSession(), newUserId, TAG,
                 getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()),
                 getBiometricContext(), () -> mCurrentUserId,
-                !FingerprintUtils.getInstance(getSensorProperties().sensorId)
-                        .getBiometricsForUser(getContext(),
+                !getFingerprintUtilsInstance().getBiometricsForUser(getContext(),
                 newUserId).isEmpty(), getAuthenticatorIds(), forceUpdateAuthenticatorIds,
                 mUserStartedCallback);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
index b469752..671bd87 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java
@@ -209,6 +209,10 @@
         return null;
     }
 
+    protected IBiometricsFingerprint getIBiometricsFingerprint() {
+        return mSession.get();
+    }
+
     public long getAuthenticatorIdForUpdateClient() throws RemoteException {
         return mSession.get().getAuthenticatorId();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index 01d1e378..76b5c4e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -60,7 +60,7 @@
     public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
             long sessionId, int userId, IBiometricSensorReceiver sensorReceiver,
             String opPackageName, long requestId, int cookie, boolean allowBackgroundAuthentication,
-            boolean isForLegacyFingerprintManager)
+            boolean isForLegacyFingerprintManager, boolean isMandatoryBiometrics)
             throws RemoteException {
     }
 
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
index d9f8588..7b50465 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -222,7 +222,7 @@
         }
 
         TunerSession tunerSession = radioModule.openSession(callback);
-        if (legacyConfig != null) {
+        if (tunerSession != null && legacyConfig != null) {
             tunerSession.setConfiguration(legacyConfig);
         }
         return tunerSession;
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
index 4edd441..a176a32 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -247,6 +247,7 @@
         return mProperties;
     }
 
+    @Nullable
     TunerSession openSession(android.hardware.radio.ITunerCallback userCb)
             throws RemoteException {
         mLogger.logRadioEvent("Open TunerSession");
@@ -260,7 +261,13 @@
             antennaConnected = mAntennaConnected;
             currentProgramInfo = mCurrentProgramInfo;
             if (isFirstTunerSession) {
-                mService.setTunerCallback(mHalTunerCallback);
+                try {
+                    mService.setTunerCallback(mHalTunerCallback);
+                } catch (RemoteException ex) {
+                    Slogf.wtf(TAG, ex, "Failed to register HAL callback for module %d",
+                            mProperties.getId());
+                    return null;
+                }
             }
         }
         // Propagate state to new client.
diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING
index bc1c728..3997bcf 100644
--- a/services/core/java/com/android/server/compat/TEST_MAPPING
+++ b/services/core/java/com/android/server/compat/TEST_MAPPING
@@ -2,12 +2,7 @@
     "presubmit": [
         // Unit tests
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.compat"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_compat"
         },
         // Tests for the TestRule
         {
diff --git a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
index 4b8f08e..1649753 100644
--- a/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
+++ b/services/core/java/com/android/server/compat/overrides/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.compat.overrides"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_compat_overrides"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5905b7d..e1bb8a1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -344,7 +344,11 @@
     private final AppOpsManager mAppOpsManager;
     private final ConnectivityDiagnosticsManager mConnectivityDiagnosticsManager;
     private final TelephonyManager mTelephonyManager;
+
+    // null if FEATURE_TELEPHONY_SUBSCRIPTION is not declared
+    @Nullable
     private final CarrierConfigManager mCarrierConfigManager;
+
     private final SubscriptionManager mSubscriptionManager;
 
     // The context is for specific user which is created from mUserId
@@ -2837,8 +2841,10 @@
                     createUserAndRestrictedProfilesRanges(mUserId,
                             mConfig.allowedApplications, mConfig.disallowedApplications));
 
-            mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
-                    mCarrierConfigChangeListener);
+            if (mCarrierConfigManager != null) {
+                mCarrierConfigManager.registerCarrierConfigChangeListener(mExecutor,
+                        mCarrierConfigChangeListener);
+            }
         }
 
         @Override
@@ -3343,6 +3349,10 @@
          */
         @Nullable
         private CarrierConfigInfo getCarrierConfigForUnderlyingNetwork() {
+            if (mCarrierConfigManager == null) {
+                return null;
+            }
+
             final int subId = getCellSubIdForNetworkCapabilities(mUnderlyingNetworkCapabilities);
             if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 Log.d(TAG, "Underlying network is not a cellular network");
@@ -3962,8 +3972,10 @@
 
             resetIkeState();
 
-            mCarrierConfigManager.unregisterCarrierConfigChangeListener(
-                    mCarrierConfigChangeListener);
+            if (mCarrierConfigManager != null) {
+                mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+                        mCarrierConfigChangeListener);
+            }
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
 
             mExecutor.shutdown();
diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
new file mode 100644
index 0000000..3eb3380
--- /dev/null
+++ b/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java
@@ -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.
+ */
+
+package com.android.server.crashrecovery;
+
+import android.os.Environment;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+
+/**
+ * Class containing helper methods for the CrashRecoveryModule.
+ *
+ * @hide
+ */
+public class CrashRecoveryUtils {
+    private static final String TAG = "CrashRecoveryUtils";
+    private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 1000 * 1000; // ~1MB
+    private static final Object sFileLock = new Object();
+
+    /** Persist recovery related events in crashrecovery events file.**/
+    public static void logCrashRecoveryEvent(int priority, String msg) {
+        Slog.println(priority, TAG, msg);
+        try {
+            File fname = getCrashRecoveryEventsFile();
+            synchronized (sFileLock) {
+                FileOutputStream out = new FileOutputStream(fname, true);
+                PrintWriter pw = new PrintWriter(out);
+                String dateString = LocalDateTime.now(ZoneId.systemDefault()).toString();
+                pw.println(dateString + ": " + msg);
+                pw.close();
+            }
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage());
+        }
+    }
+
+    /** Dump recovery related events from crashrecovery events file.**/
+    public static void dumpCrashRecoveryEvents(IndentingPrintWriter pw) {
+        pw.println("CrashRecovery Events: ");
+        pw.increaseIndent();
+        final File file = getCrashRecoveryEventsFile();
+        final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE;
+        synchronized (sFileLock) {
+            try (BufferedReader in = new BufferedReader(new FileReader(file))) {
+                if (skipSize > 0) {
+                    in.skip(skipSize);
+                }
+                String line;
+                while ((line = in.readLine()) != null) {
+                    pw.println(line);
+                }
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage());
+            }
+        }
+        pw.decreaseIndent();
+    }
+
+    private static File getCrashRecoveryEventsFile() {
+        File systemDir = new File(Environment.getDataDirectory(), "system");
+        return new File(systemDir, "crashrecovery-events.txt");
+    }
+}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 38e6d82..1094bee 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -1002,9 +1002,9 @@
 
     /**
      * Checks if the process can cancel a device state request. If the calling process ID is not
-     * both the top app and foregrounded nor does the process ID and userID match the IDs that made
-     * the device state request, then check if this process holds the CONTROL_DEVICE_STATE
-     * permission.
+     * both the top app and foregrounded, verify that the calling process is in the foreground and
+     * that it matches the process ID and user ID that made the device state request. If neither are
+     * true, then check if this process holds the CONTROL_DEVICE_STATE permission.
      *
      * @param callingPid Process ID that is requesting this state change
      * @param callingUid UID that is requesting this state change
@@ -1018,8 +1018,8 @@
         if (Flags.deviceStateRequesterCancelState()) {
             synchronized (mLock) {
                 isAllowedToControlState =
-                        isAllowedToControlState || doCallingIdsMatchOverrideRequestIdsLocked(
-                                callingPid, callingUid);
+                        isTopApp || (isForegroundApp && doCallingIdsMatchOverrideRequestIdsLocked(
+                                callingPid, callingUid));
             }
         }
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 226bdf5..907e7c6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
 import static com.android.server.display.BrightnessMappingStrategy.INVALID_LUX;
 import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
 
@@ -43,6 +45,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.util.EventLog;
+import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -241,6 +244,9 @@
 
     private int mDisplayState = Display.STATE_UNKNOWN;
 
+    // True if the normal brightness should be forced while device is dozing.
+    private boolean mUseNormalBrightnessForDoze;
+
     // True if we are collecting a brightness adjustment sample, along with some data
     // for the initial state of the sample.
     private boolean mBrightnessAdjustmentSamplePending;
@@ -442,11 +448,12 @@
     public void configure(int state, @Nullable BrightnessConfiguration configuration,
             float brightness, boolean userChangedBrightness, float adjustment,
             boolean userChangedAutoBrightnessAdjustment, int displayPolicy, int displayState,
-            boolean shouldResetShortTermModel) {
+            boolean useNormalBrightnessForDoze, boolean shouldResetShortTermModel) {
         mState = state;
         boolean changed = setBrightnessConfiguration(configuration, shouldResetShortTermModel);
         changed |= setDisplayPolicy(displayPolicy);
         mDisplayState = displayState;
+        mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
         if (userChangedAutoBrightnessAdjustment) {
             changed |= setAutoBrightnessAdjustment(adjustment);
         }
@@ -556,6 +563,7 @@
     public void resetShortTermModel() {
         mCurrentBrightnessMapper.clearUserDataPoints();
         mShortTermModel.reset();
+        Slog.i(TAG, "Resetting short term model");
     }
 
     public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
@@ -592,74 +600,79 @@
     }
 
     public void dump(PrintWriter pw) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+        ipw.increaseIndent();
         pw.println();
         pw.println("Automatic Brightness Controller Configuration:");
-        pw.println("  mState=" + configStateToString(mState));
-        pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
-        pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
-        pw.println("  mDozeScaleFactor=" + mDozeScaleFactor);
-        pw.println("  mInitialLightSensorRate=" + mInitialLightSensorRate);
-        pw.println("  mNormalLightSensorRate=" + mNormalLightSensorRate);
-        pw.println("  mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
-        pw.println("  mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
-        pw.println("  mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
-        pw.println("  mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
-        pw.println("  mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
-        pw.println("  mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
-        pw.println("  mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
-        pw.println("  mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
-        pw.println("  mWeightingIntercept=" + mWeightingIntercept);
+        pw.println("----------------------------------------------");
+        ipw.println("mState=" + configStateToString(mState));
+        ipw.println("mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+        ipw.println("mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+        ipw.println("mDozeScaleFactor=" + mDozeScaleFactor);
+        ipw.println("mInitialLightSensorRate=" + mInitialLightSensorRate);
+        ipw.println("mNormalLightSensorRate=" + mNormalLightSensorRate);
+        ipw.println("mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
+        ipw.println("mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
+        ipw.println("mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
+        ipw.println("mBrighteningLightDebounceConfigIdle=" + mBrighteningLightDebounceConfigIdle);
+        ipw.println("mDarkeningLightDebounceConfigIdle=" + mDarkeningLightDebounceConfigIdle);
+        ipw.println("mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
+        ipw.println("mAmbientLightHorizonLong=" + mAmbientLightHorizonLong);
+        ipw.println("mAmbientLightHorizonShort=" + mAmbientLightHorizonShort);
+        ipw.println("mWeightingIntercept=" + mWeightingIntercept);
 
         pw.println();
         pw.println("Automatic Brightness Controller State:");
-        pw.println("  mLightSensor=" + mLightSensor);
-        pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
-        pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
-        pw.println("  mCurrentLightSensorRate=" + mCurrentLightSensorRate);
-        pw.println("  mAmbientLux=" + mAmbientLux);
-        pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
-        pw.println("  mPreThresholdLux=" + mPreThresholdLux);
-        pw.println("  mPreThresholdBrightness=" + mPreThresholdBrightness);
-        pw.println("  mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
-        pw.println("  mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
-        pw.println("  mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
-        pw.println("  mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
-        pw.println("  mLastObservedLux=" + mLastObservedLux);
-        pw.println("  mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
-        pw.println("  mRecentLightSamples=" + mRecentLightSamples);
-        pw.println("  mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
-        pw.println("  mScreenAutoBrightness=" + mScreenAutoBrightness);
-        pw.println("  mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
-        pw.println("  mShortTermModel=");
-        mShortTermModel.dump(pw);
-        pw.println("  mPausedShortTermModel=");
-        mPausedShortTermModel.dump(pw);
+        pw.println("--------------------------------------");
+        ipw.println("mLightSensor=" + mLightSensor);
+        ipw.println("mLightSensorEnabled=" + mLightSensorEnabled);
+        ipw.println("mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
+        ipw.println("mCurrentLightSensorRate=" + mCurrentLightSensorRate);
+        ipw.println("mAmbientLux=" + mAmbientLux);
+        ipw.println("mAmbientLuxValid=" + mAmbientLuxValid);
+        ipw.println("mPreThresholdLux=" + mPreThresholdLux);
+        ipw.println("mPreThresholdBrightness=" + mPreThresholdBrightness);
+        ipw.println("mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold);
+        ipw.println("mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold);
+        ipw.println("mScreenBrighteningThreshold=" + mScreenBrighteningThreshold);
+        ipw.println("mScreenDarkeningThreshold=" + mScreenDarkeningThreshold);
+        ipw.println("mLastObservedLux=" + mLastObservedLux);
+        ipw.println("mLastObservedLuxTime=" + TimeUtils.formatUptime(mLastObservedLuxTime));
+        ipw.println("mRecentLightSamples=" + mRecentLightSamples);
+        ipw.println("mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
+        ipw.println("mScreenAutoBrightness=" + mScreenAutoBrightness);
+        ipw.println("mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
+        ipw.println("mShortTermModel=");
 
-        pw.println();
-        pw.println("  mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
-        pw.println("  mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
-        pw.println("  mBrightnessAdjustmentSampleOldBrightness="
+        mShortTermModel.dump(ipw);
+        ipw.println("mPausedShortTermModel=");
+        mPausedShortTermModel.dump(ipw);
+
+        ipw.println();
+        ipw.println("mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
+        ipw.println("mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
+        ipw.println("mBrightnessAdjustmentSampleOldBrightness="
                 + mBrightnessAdjustmentSampleOldBrightness);
-        pw.println("  mForegroundAppPackageName=" + mForegroundAppPackageName);
-        pw.println("  mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
-        pw.println("  mForegroundAppCategory=" + mForegroundAppCategory);
-        pw.println("  mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
-        pw.println("  Current mode="
+        ipw.println("mForegroundAppPackageName=" + mForegroundAppPackageName);
+        ipw.println("mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+        ipw.println("mForegroundAppCategory=" + mForegroundAppCategory);
+        ipw.println("mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
+        ipw.println("Current mode="
                 + autoBrightnessModeToString(mCurrentBrightnessMapper.getMode()));
 
         for (int i = 0; i < mBrightnessMappingStrategyMap.size(); i++) {
-            pw.println();
-            pw.println("  Mapper for mode "
+            ipw.println();
+            ipw.println("Mapper for mode "
                     + autoBrightnessModeToString(mBrightnessMappingStrategyMap.keyAt(i)) + ":");
-            mBrightnessMappingStrategyMap.valueAt(i).dump(pw,
+            mBrightnessMappingStrategyMap.valueAt(i).dump(ipw,
                     mBrightnessRangeController.getNormalBrightnessMax());
         }
 
-        pw.println();
-        pw.println("  mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
-        pw.println("  mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
-        pw.println("  mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
-        pw.println("  mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
+        ipw.println();
+        ipw.println("mAmbientBrightnessThresholds=" + mAmbientBrightnessThresholds);
+        ipw.println("mAmbientBrightnessThresholdsIdle=" + mAmbientBrightnessThresholdsIdle);
+        ipw.println("mScreenBrightnessThresholds=" + mScreenBrightnessThresholds);
+        ipw.println("mScreenBrightnessThresholdsIdle=" + mScreenBrightnessThresholdsIdle);
     }
 
     public float[] getLastSensorValues() {
@@ -1264,11 +1277,10 @@
     }
 
     private boolean shouldApplyDozeScaleFactor() {
-        // Apply the doze scale factor if the display is in doze. We shouldn't rely on the display
-        // policy here - the screen might turn on while the policy is POLICY_DOZE and in this
-        // situation, we shouldn't apply the doze scale factor. We also don't apply the doze scale
-        // factor if we have a designated brightness curve for doze.
-        return Display.isDozeState(mDisplayState) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
+        // We don't apply the doze scale factor if we have a designated brightness curve for doze.
+        return (mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+                ? !mUseNormalBrightnessForDoze && mDisplayPolicy == POLICY_DOZE
+                : Display.isDozeState(mDisplayState)) && getMode() != AUTO_BRIGHTNESS_MODE_DOZE;
     }
 
     private class ShortTermModel {
@@ -1334,8 +1346,10 @@
                     + "\n mIsValid: " + mIsValid;
         }
 
-        void dump(PrintWriter pw) {
-            pw.println(this);
+        void dump(IndentingPrintWriter ipw) {
+            ipw.increaseIndent();
+            ipw.println(this);
+            ipw.decreaseIndent();
         }
 
     }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index b0507fb..6a019f3 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -1073,7 +1073,9 @@
             pw.println("  mBrightnessRangeAdjustmentApplied=" + mBrightnessRangeAdjustmentApplied);
             pw.println("  shortTermModelTimeout=" + getShortTermModelTimeout());
 
-            pw.println("  Previous short-term models (oldest to newest): ");
+            if (!mPreviousBrightnessSplines.isEmpty()) {
+                pw.println("  Previous short-term models (oldest to newest): ");
+            }
             for (int i = 0; i < mPreviousBrightnessSplines.size(); i++) {
                 pw.println("  Computed at "
                         + FORMAT.format(new Date(mBrightnessSplineChangeTimes.get(i))) + ": ");
diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java
index 7d26004..5488446 100644
--- a/services/core/java/com/android/server/display/BrightnessSetting.java
+++ b/services/core/java/com/android/server/display/BrightnessSetting.java
@@ -150,6 +150,13 @@
     }
 
     /**
+     * Flush the brightness update that has been made to the persistent data store.
+     */
+    public void saveIfNeeded() {
+        mPersistentDataStore.saveIfNeeded();
+    }
+
+    /**
      * @return The brightness for the default display in nits. Used when the underlying display
      * device has changed but we want to persist the nit value.
      */
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index 631e751..b56a234 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -265,6 +265,7 @@
 
     private void dumpLocal(PrintWriter pw) {
         pw.println("BrightnessThrottler:");
+        pw.println("--------------------");
         pw.println("  mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
         pw.println("  mThermalThrottlingData=" + mThermalThrottlingData);
         pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index ac5dd20..0f65360 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -782,6 +782,7 @@
 
     public void dump(final PrintWriter pw) {
         pw.println("BrightnessTracker state:");
+        pw.println("------------------------");
         synchronized (mDataCollectionLock) {
             pw.println("  mStarted=" + mStarted);
             pw.println("  mLightSensor=" + mLightSensor);
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 146810f..4c133ff 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -95,6 +95,7 @@
 
     public void dumpLocked(IndentingPrintWriter ipw) {
         ipw.println("DeviceStateToLayoutMap:");
+        ipw.println("-----------------------");
         ipw.increaseIndent();
 
         ipw.println("mIsPortInDisplayLayoutEnabled=" + mIsPortInDisplayLayoutEnabled);
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index dc611fc..334dda0 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import android.hardware.display.BrightnessInfo;
 import android.text.TextUtils;
 
 import com.android.server.display.brightness.BrightnessEvent;
@@ -50,6 +51,8 @@
 
     private final boolean mIsUserInitiatedChange;
 
+    private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason;
+
     private DisplayBrightnessState(Builder builder) {
         mBrightness = builder.getBrightness();
         mHdrBrightness = builder.getHdrBrightness();
@@ -64,6 +67,7 @@
         mBrightnessEvent = builder.getBrightnessEvent();
         mBrightnessAdjustmentFlag = builder.getBrightnessAdjustmentFlag();
         mIsUserInitiatedChange = builder.isUserInitiatedChange();
+        mBrightnessMaxReason = builder.getBrightnessMaxReason();
     }
 
     /**
@@ -159,6 +163,13 @@
         return mIsUserInitiatedChange;
     }
 
+    /**
+     * Gets reason for max brightness restriction
+     */
+    public @BrightnessInfo.BrightnessMaxReason int getBrightnessMaxReason() {
+        return mBrightnessMaxReason;
+    }
+
     @Override
     public String toString() {
         StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -180,6 +191,8 @@
                 .append(Objects.toString(mBrightnessEvent, "null"));
         stringBuilder.append("\n    mBrightnessAdjustmentFlag:").append(mBrightnessAdjustmentFlag);
         stringBuilder.append("\n    mIsUserInitiatedChange:").append(mIsUserInitiatedChange);
+        stringBuilder.append("\n    mBrightnessMaxReason:")
+                .append(BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
         return stringBuilder.toString();
     }
 
@@ -212,7 +225,8 @@
                     == otherState.shouldUpdateScreenBrightnessSetting()
                 && Objects.equals(mBrightnessEvent, otherState.getBrightnessEvent())
                 && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag()
-                && mIsUserInitiatedChange == otherState.isUserInitiatedChange();
+                && mIsUserInitiatedChange == otherState.isUserInitiatedChange()
+                && mBrightnessMaxReason == otherState.getBrightnessMaxReason();
     }
 
     @Override
@@ -221,7 +235,7 @@
                 mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
                 mCustomAnimationRate,
                 mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag,
-                mIsUserInitiatedChange);
+                mIsUserInitiatedChange, mBrightnessMaxReason);
     }
 
     /**
@@ -245,12 +259,11 @@
         private float mMinBrightness;
         private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
         private boolean mShouldUpdateScreenBrightnessSetting;
-
         private BrightnessEvent mBrightnessEvent;
-
-        public int mBrightnessAdjustmentFlag = 0;
-
+        private int mBrightnessAdjustmentFlag = 0;
         private boolean mIsUserInitiatedChange;
+        private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
+                BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
 
         /**
          * Create a builder starting with the values from the specified {@link
@@ -274,6 +287,7 @@
             builder.setBrightnessEvent(state.getBrightnessEvent());
             builder.setBrightnessAdjustmentFlag(state.getBrightnessAdjustmentFlag());
             builder.setIsUserInitiatedChange(state.isUserInitiatedChange());
+            builder.setBrightnessMaxReason(state.getBrightnessMaxReason());
             return builder;
         }
 
@@ -506,5 +520,21 @@
             mIsUserInitiatedChange = isUserInitiatedChange;
             return this;
         }
+
+        /**
+         * Gets reason for max brightness restriction
+         */
+        public @BrightnessInfo.BrightnessMaxReason int getBrightnessMaxReason() {
+            return mBrightnessMaxReason;
+        }
+
+        /**
+         * Sets reason for max brightness restriction
+         */
+        public Builder setBrightnessMaxReason(
+                @BrightnessInfo.BrightnessMaxReason int brightnessMaxReason) {
+            mBrightnessMaxReason = brightnessMaxReason;
+            return this;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index cc115f1..d78fdfa 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -150,7 +150,9 @@
  *      <screenBrightnessDefault>0.65</screenBrightnessDefault>
  *      <powerThrottlingConfig>
  *        <brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>
- *        <pollingWindowMillis>15</pollingWindowMillis>
+ *        <customAnimationRateSec>0.004</customAnimationRateSec>
+ *        <pollingWindowMaxMillis>30000</pollingWindowMaxMillis>
+ *        <pollingWindowMinMillis>10000</pollingWindowMinMillis>
  *          <powerThrottlingMap>
  *              <powerThrottlingPoint>
  *                  <thermalStatus>severe</thermalStatus>
@@ -2184,9 +2186,13 @@
             return;
         }
         float lowestBrightnessCap = powerThrottlingCfg.getBrightnessLowestCapAllowed().floatValue();
-        int pollingWindowMillis = powerThrottlingCfg.getPollingWindowMillis().intValue();
+        float customAnimationRateSec = powerThrottlingCfg.getCustomAnimationRateSec().floatValue();
+        int pollingWindowMaxMillis = powerThrottlingCfg.getPollingWindowMaxMillis().intValue();
+        int pollingWindowMinMillis = powerThrottlingCfg.getPollingWindowMinMillis().intValue();
         mPowerThrottlingConfigData = new PowerThrottlingConfigData(lowestBrightnessCap,
-                                                                   pollingWindowMillis);
+                                                                   customAnimationRateSec,
+                                                                   pollingWindowMaxMillis,
+                                                                   pollingWindowMinMillis);
     }
 
     private void loadRefreshRateSetting(DisplayConfiguration config) {
@@ -2980,12 +2986,19 @@
     public static class PowerThrottlingConfigData {
         /** Lowest brightness cap allowed for this device. */
         public final float brightnessLowestCapAllowed;
-        /** Time window for polling power in seconds. */
-        public final int pollingWindowMillis;
+        /** Time take to animate brightness in seconds. */
+        public final float customAnimationRateSec;
+        /** 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,
-                int pollingWindowMillis) {
+                float customAnimationRateSec, int pollingWindowMaxMillis,
+                int pollingWindowMinMillis) {
             this.brightnessLowestCapAllowed = brightnessLowestCapAllowed;
-            this.pollingWindowMillis = pollingWindowMillis;
+            this.customAnimationRateSec = customAnimationRateSec;
+            this.pollingWindowMaxMillis = pollingWindowMaxMillis;
+            this.pollingWindowMinMillis = pollingWindowMinMillis;
         }
 
         @Override
@@ -2993,7 +3006,9 @@
             return "PowerThrottlingConfigData{"
                     + "brightnessLowestCapAllowed: "
                     + brightnessLowestCapAllowed
-                    + ", pollingWindowMillis: " + pollingWindowMillis
+                    + ", customAnimationRateSec: " + customAnimationRateSec
+                    + ", pollingWindowMaxMillis: " + pollingWindowMaxMillis
+                    + ", pollingWindowMinMillis: " + pollingWindowMinMillis
                     + "} ";
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9e905ab..2b732ea 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -64,7 +64,6 @@
 import android.app.compat.CompatChanges;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.flags.Flags;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
@@ -270,6 +269,7 @@
 
     private final Context mContext;
     private final DisplayManagerHandler mHandler;
+    private final HandlerExecutor mHandlerExecutor;
     private final Handler mUiHandler;
     private final DisplayModeDirector mDisplayModeDirector;
     private final ExternalDisplayPolicy mExternalDisplayPolicy;
@@ -316,6 +316,7 @@
     public boolean mSafeMode;
 
     // All callback records indexed by calling process id.
+    @GuardedBy("mSyncRoot")
     private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
 
     /**
@@ -355,7 +356,7 @@
             new CopyOnWriteArrayList<>();
 
     /** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
-    private final SparseArray<DisplayPowerControllerInterface> mDisplayPowerControllers =
+    private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
             new SparseArray<>();
 
     /** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
@@ -603,6 +604,7 @@
         mContext = context;
         mFlags = injector.getFlags();
         mHandler = new DisplayManagerHandler(displayThreadLooper);
+        mHandlerExecutor = new HandlerExecutor(mHandler);
         mUiHandler = UiThread.getHandler();
         mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContext,
@@ -727,7 +729,7 @@
                 if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
                     return;
                 }
-                final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
+                final DisplayPowerController dpc = mDisplayPowerControllers.get(
                         logicalDisplay.getDisplayIdLocked());
                 if (dpc == null) {
                     return;
@@ -761,12 +763,13 @@
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
             mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+
             ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
             activityManager.addOnUidImportanceListener(mUidImportanceListener, IMPORTANCE_CACHED);
 
             mDeviceStateManager = LocalServices.getService(DeviceStateManagerInternal.class);
             mContext.getSystemService(DeviceStateManager.class).registerCallback(
-                    new HandlerExecutor(mHandler), new DeviceStateListener());
+                    mHandlerExecutor, new DeviceStateListener());
 
             mLogicalDisplayMapper.onWindowManagerReady();
             scheduleTraversalLocked(false);
@@ -1020,6 +1023,10 @@
     private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
         @Override
         public void onUidImportance(int uid, int importance) {
+          onUidImportanceInternal(uid, importance);
+        }
+
+        private void onUidImportanceInternal(int uid, int importance) {
             synchronized (mPendingCallbackSelfLocked) {
                 if (importance >= IMPORTANCE_GONE) {
                     // Clean up as the app is already gone
@@ -1182,7 +1189,10 @@
 
     private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[]
             frameRateOverrides, DisplayInfo info, int callingUid) {
+        // Start with the display frame rate
         float frameRateHz = info.renderFrameRate;
+
+        // If the app has a specific override, use that instead
         for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) {
             if (frameRateOverride.uid == callingUid) {
                 frameRateHz = frameRateOverride.frameRateHz;
@@ -1201,18 +1211,21 @@
                                 DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE, callingUid);
 
         // Override the refresh rate only if it is a divisor of the current
-        // refresh rate. This calculation needs to be in sync with the native code
+        // vsync rate. This calculation needs to be in sync with the native code
         // in RefreshRateSelector::getFrameRateDivisor
         Display.Mode currentMode = info.getMode();
-        float numPeriods = currentMode.getRefreshRate() / frameRateHz;
+        float vsyncRate = currentMode.getVsyncRate();
+        float numPeriods = vsyncRate / frameRateHz;
         float numPeriodsRound = Math.round(numPeriods);
         if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVISORS) {
             return info;
         }
-        frameRateHz = currentMode.getRefreshRate() / numPeriodsRound;
+        frameRateHz = vsyncRate / numPeriodsRound;
 
         DisplayInfo overriddenInfo = new DisplayInfo();
         overriddenInfo.copyFrom(info);
+
+        // If there is a mode that matches the override, use that one
         for (Display.Mode mode : info.supportedModes) {
             if (!mode.equalsExceptRefreshRate(currentMode)) {
                 continue;
@@ -1232,8 +1245,9 @@
                 return overriddenInfo;
             }
         }
-
         overriddenInfo.refreshRateOverride = frameRateHz;
+
+        // Create a fake mode for app compat
         if (!displayModeReturnsPhysicalRefreshRate) {
             overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes,
                     info.supportedModes.length + 1);
@@ -1633,8 +1647,7 @@
                 && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
             // Only a valid media projection or a virtual device can create a mirror virtual
             // display.
-            if (!canProjectVideo(projection)
-                    && !isMirroringSupportedByVirtualDevice(virtualDevice)) {
+            if (!canProjectVideo(projection) && virtualDevice == null) {
                 throw new SecurityException("Requires CAPTURE_VIDEO_OUTPUT or "
                         + "CAPTURE_SECURE_VIDEO_OUTPUT permission, or an appropriate "
                         + "MediaProjection token in order to create a screen sharing virtual "
@@ -1896,10 +1909,6 @@
         return -1;
     }
 
-    private static boolean isMirroringSupportedByVirtualDevice(IVirtualDevice virtualDevice) {
-        return Flags.interactiveScreenMirror() && virtualDevice != null;
-    }
-
     private void resizeVirtualDisplayInternal(IBinder appToken,
             int width, int height, int densityDpi) {
         synchronized (mSyncRoot) {
@@ -2057,7 +2066,7 @@
             configurePreferredDisplayModeLocked(display);
         }
 
-        DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
+        DisplayPowerController dpc = addDisplayPowerControllerLocked(display);
         if (dpc != null) {
             final int leadDisplayId = display.getLeadDisplayIdLocked();
             updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
@@ -2066,7 +2075,7 @@
             // that the follower display was added before the lead display.
             mLogicalDisplayMapper.forEachLocked(d -> {
                 if (d.getLeadDisplayIdLocked() == displayId) {
-                    DisplayPowerControllerInterface followerDpc =
+                    DisplayPowerController followerDpc =
                             mDisplayPowerControllers.get(d.getDisplayIdLocked());
                     if (followerDpc != null) {
                         updateDisplayPowerControllerLeaderLocked(followerDpc, displayId);
@@ -2150,7 +2159,7 @@
         scheduleTraversalLocked(false);
         mPersistentDataStore.saveIfNeeded();
 
-        DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
             final int leadDisplayId = display.getLeadDisplayIdLocked();
             updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
@@ -2164,7 +2173,7 @@
     }
 
     private void updateDisplayPowerControllerLeaderLocked(
-            @NonNull DisplayPowerControllerInterface dpc, int leadDisplayId) {
+            @NonNull DisplayPowerController dpc, int leadDisplayId) {
         if (dpc.getLeadDisplayId() == leadDisplayId) {
             // Lead display hasn't changed, nothing to do.
             return;
@@ -2173,7 +2182,7 @@
         // If it has changed, then we need to unregister from the previous leader if there was one.
         final int prevLeaderId = dpc.getLeadDisplayId();
         if (prevLeaderId != Layout.NO_LEAD_DISPLAY) {
-            final DisplayPowerControllerInterface prevLeader =
+            final DisplayPowerController prevLeader =
                     mDisplayPowerControllers.get(prevLeaderId);
             if (prevLeader != null) {
                 prevLeader.removeDisplayBrightnessFollower(dpc);
@@ -2182,7 +2191,7 @@
 
         // And then, if it's following, register it with the new one.
         if (leadDisplayId != Layout.NO_LEAD_DISPLAY) {
-            final DisplayPowerControllerInterface newLeader =
+            final DisplayPowerController newLeader =
                     mDisplayPowerControllers.get(leadDisplayId);
             if (newLeader != null) {
                 newLeader.addDisplayBrightnessFollower(dpc);
@@ -2223,7 +2232,7 @@
     private void releaseDisplayAndEmitEvent(LogicalDisplay display, int event) {
         final int displayId = display.getDisplayIdLocked();
 
-        final DisplayPowerControllerInterface dpc =
+        final DisplayPowerController dpc =
                 mDisplayPowerControllers.removeReturnOld(displayId);
         if (dpc != null) {
             updateDisplayPowerControllerLeaderLocked(dpc, Layout.NO_LEAD_DISPLAY);
@@ -2270,7 +2279,7 @@
 
     private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
-        final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+        final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
         if (dpc != null) {
             final int leadDisplayId = display.getLeadDisplayIdLocked();
             updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
@@ -2691,14 +2700,14 @@
             if (userId != mCurrentUserId) {
                 return;
             }
-            DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
+            DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
             if (dpc != null) {
                 dpc.setBrightnessConfiguration(c, /* shouldResetShortTermModel= */ true);
             }
         }
     }
 
-    private DisplayPowerControllerInterface getDpcFromUniqueIdLocked(String uniqueId) {
+    private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
         final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
         final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
         if (logicalDisplay != null) {
@@ -2739,7 +2748,7 @@
                 final BrightnessConfiguration config =
                         getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
                 if (config != null) {
-                    final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
+                    final DisplayPowerController dpc = mDisplayPowerControllers.get(
                             logicalDisplay.getDisplayIdLocked());
                     if (dpc != null) {
                         dpc.setBrightnessConfiguration(config,
@@ -2986,7 +2995,7 @@
 
     void setAutoBrightnessLoggingEnabled(boolean enabled) {
         synchronized (mSyncRoot) {
-            final DisplayPowerControllerInterface displayPowerController =
+            final DisplayPowerController displayPowerController =
                     mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
@@ -2996,7 +3005,7 @@
 
     void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
         synchronized (mSyncRoot) {
-            final DisplayPowerControllerInterface displayPowerController =
+            final DisplayPowerController displayPowerController =
                     mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
@@ -3022,7 +3031,7 @@
 
     void setAmbientColorTemperatureOverride(float cct) {
         synchronized (mSyncRoot) {
-            final DisplayPowerControllerInterface displayPowerController =
+            final DisplayPowerController displayPowerController =
                     mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
             if (displayPowerController != null) {
                 displayPowerController.setAmbientColorTemperatureOverride(cct);
@@ -3032,7 +3041,7 @@
 
     void setDockedAndIdleEnabled(boolean enabled, int displayId) {
         synchronized (mSyncRoot) {
-            final DisplayPowerControllerInterface displayPowerController =
+            final DisplayPowerController displayPowerController =
                     mDisplayPowerControllers.get(displayId);
             if (displayPowerController != null) {
                 displayPowerController.setAutomaticScreenBrightnessMode(enabled
@@ -3231,36 +3240,40 @@
         // After releasing the lock, send the notifications out.
         for (int i = 0; i < mTempCallbacks.size(); i++) {
             CallbackRecord callbackRecord = mTempCallbacks.get(i);
-            final int uid = callbackRecord.mUid;
-            final int pid = callbackRecord.mPid;
-            if (isUidCached(uid)) {
-                // For cached apps, save the pending event until it becomes non-cached
-                synchronized (mPendingCallbackSelfLocked) {
-                    SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(
-                            uid);
-                    if (extraLogging(callbackRecord.mPackageName)) {
-                        Slog.i(TAG, "Uid is cached: " + uid
-                                + ", pendingCallbacks: " + pendingCallbacks);
-                    }
-                    if (pendingCallbacks == null) {
-                        pendingCallbacks = new SparseArray<>();
-                        mPendingCallbackSelfLocked.put(uid, pendingCallbacks);
-                    }
-                    PendingCallback pendingCallback = pendingCallbacks.get(pid);
-                    if (pendingCallback == null) {
-                        pendingCallbacks.put(pid,
-                                new PendingCallback(callbackRecord, displayId, event));
-                    } else {
-                        pendingCallback.addDisplayEvent(displayId, event);
-                    }
-                }
-            } else {
-                callbackRecord.notifyDisplayEventAsync(displayId, event);
-            }
+            deliverEventInternal(callbackRecord, displayId, event);
         }
         mTempCallbacks.clear();
     }
 
+    private void deliverEventInternal(CallbackRecord callbackRecord, int displayId, int event) {
+        final int uid = callbackRecord.mUid;
+        final int pid = callbackRecord.mPid;
+        if (isUidCached(uid)) {
+            // For cached apps, save the pending event until it becomes non-cached
+            synchronized (mPendingCallbackSelfLocked) {
+                SparseArray<PendingCallback> pendingCallbacks = mPendingCallbackSelfLocked.get(
+                    uid);
+                if (extraLogging(callbackRecord.mPackageName)) {
+                    Slog.i(TAG, "Uid is cached: " + uid
+                            + ", pendingCallbacks: " + pendingCallbacks);
+                }
+                if (pendingCallbacks == null) {
+                    pendingCallbacks = new SparseArray<>();
+                    mPendingCallbackSelfLocked.put(uid, pendingCallbacks);
+                }
+                PendingCallback pendingCallback = pendingCallbacks.get(pid);
+                if (pendingCallback == null) {
+                    pendingCallbacks.put(pid,
+                            new PendingCallback(callbackRecord, displayId, event));
+                } else {
+                    pendingCallback.addDisplayEvent(displayId, event);
+                }
+            }
+        } else {
+            callbackRecord.notifyDisplayEventAsync(displayId, event);
+        }
+    }
+
     private boolean extraLogging(String packageName) {
         return mExtraDisplayEventLogging && mExtraDisplayLoggingPackageName.equals(packageName);
     }
@@ -3339,6 +3352,7 @@
             pw.println();
             final int displayStateCount = mDisplayStates.size();
             pw.println("Display States: size=" + displayStateCount);
+            pw.println("---------------------");
             for (int i = 0; i < displayStateCount; i++) {
                 final int displayId = mDisplayStates.keyAt(i);
                 final int displayState = mDisplayStates.valueAt(i);
@@ -3354,6 +3368,7 @@
 
             pw.println();
             pw.println("Display Adapters: size=" + mDisplayAdapters.size());
+            pw.println("------------------------");
             for (DisplayAdapter adapter : mDisplayAdapters) {
                 pw.println("  " + adapter.getName());
                 adapter.dumpLocked(ipw);
@@ -3361,6 +3376,7 @@
 
             pw.println();
             pw.println("Display Devices: size=" + mDisplayDeviceRepo.sizeLocked());
+            pw.println("-----------------------");
             mDisplayDeviceRepo.forEachLocked(device -> {
                 pw.println("  " + device.getDisplayDeviceInfoLocked());
                 device.dumpLocked(ipw);
@@ -3372,6 +3388,7 @@
             final int callbackCount = mCallbacks.size();
             pw.println();
             pw.println("Callbacks: size=" + callbackCount);
+            pw.println("-----------------");
             for (int i = 0; i < callbackCount; i++) {
                 CallbackRecord callback = mCallbacks.valueAt(i);
                 pw.println("  " + i + ": mPid=" + callback.mPid
@@ -3384,6 +3401,7 @@
             for (int i = 0; i < displayPowerControllerCount; i++) {
                 mDisplayPowerControllers.valueAt(i).dump(pw);
             }
+
             pw.println();
             mPersistentDataStore.dump(pw);
 
@@ -3402,8 +3420,10 @@
         }
         pw.println();
         mDisplayModeDirector.dump(pw);
+        pw.println();
         mBrightnessSynchronizer.dump(pw);
         if (mSmallAreaDetectionController != null) {
+            pw.println();
             mSmallAreaDetectionController.dump(pw);
         }
 
@@ -3563,7 +3583,7 @@
     }
 
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
-    private DisplayPowerControllerInterface addDisplayPowerControllerLocked(
+    private DisplayPowerController addDisplayPowerControllerLocked(
             LogicalDisplay display) {
         if (mPowerHandler == null) {
             // initPowerManagement has not yet been called.
@@ -3577,7 +3597,7 @@
         final int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
         final BrightnessSetting brightnessSetting = new BrightnessSetting(userSerial,
                 mPersistentDataStore, display, mSyncRoot);
-        final DisplayPowerControllerInterface displayPowerController;
+        final DisplayPowerController displayPowerController;
 
         // If display is internal and has a HighBrightnessModeMetadata mapping, use that.
         // Or create a new one and use that.
@@ -3756,7 +3776,7 @@
 
         public boolean mWifiDisplayScanRequested;
 
-        CallbackRecord(int pid, int uid, IDisplayManagerCallback callback,
+        CallbackRecord(int pid, int uid, @NonNull IDisplayManagerCallback callback,
                 @EventsMask long eventsMask) {
             mPid = pid;
             mUid = uid;
@@ -3784,7 +3804,9 @@
         }
 
         /**
-         * @return {@code false} if RemoteException happens; otherwise {@code true} for success.
+         * @return {@code false} if RemoteException happens; otherwise {@code true} for
+         * success.  This returns true even if the event was deferred because the remote client is
+         * cached.
          */
         public boolean notifyDisplayEventAsync(int displayId, @DisplayEvent int event) {
             if (!shouldSendEvent(event)) {
@@ -3797,9 +3819,19 @@
                             "notifyDisplayEventAsync#notSendingEvent=" + event + ",mEventsMask="
                                     + mEventsMask);
                 }
+                // The client is not interested in this event, so do nothing.
                 return true;
             }
 
+            return transmitDisplayEvent(displayId, event);
+        }
+
+        /**
+         * Transmit a single display event.  The client is presumed ready.  Return true on success
+         * and false if the client died.
+         */
+        private boolean transmitDisplayEvent(int displayId, @DisplayEvent int event) {
+            // The client is ready to receive the event.
             try {
                 mCallback.onDisplayEvent(displayId, event);
                 return true;
@@ -3811,6 +3843,9 @@
             }
         }
 
+        /**
+         * Return true if the client is interested in this event.
+         */
         private boolean shouldSendEvent(@DisplayEvent int event) {
             final long mask = mEventsMask.get();
             switch (event) {
@@ -4365,7 +4400,7 @@
                                     uniqueId, userSerial);
                     if (config == null) {
                         // Get default configuration
-                        DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
+                        DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
                         if (dpc != null) {
                             config = dpc.getDefaultBrightnessConfiguration();
                         }
@@ -4419,7 +4454,7 @@
                     if (display == null || !display.isEnabledLocked()) {
                         return null;
                     }
-                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         return dpc.getBrightnessInfo();
                     }
@@ -4464,7 +4499,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         dpc.setBrightness(brightness);
                     }
@@ -4484,7 +4519,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mSyncRoot) {
-                    DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
+                    DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
                     if (dpc != null) {
                         brightness = dpc.getScreenBrightnessSetting();
                     }
@@ -4811,7 +4846,7 @@
                             id).getPrimaryDisplayDeviceLocked();
                     final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
                     if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
-                        final DisplayPowerControllerInterface displayPowerController =
+                        final DisplayPowerController displayPowerController =
                                 mDisplayPowerControllers.get(id);
                         if (displayPowerController != null) {
                             ready &= displayPowerController.requestPowerState(request,
@@ -5192,7 +5227,7 @@
                     return null;
                 }
 
-                DisplayPowerControllerInterface displayPowerController =
+                DisplayPowerController displayPowerController =
                         mDisplayPowerControllers.get(logicalDisplay.getDisplayIdLocked());
                 if (displayPowerController == null) {
                     Slog.w(TAG,
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index a188e79..b05a96e 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -39,12 +39,12 @@
 
     @Nullable
     private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
-    private final DisplayPowerControllerInterface mDisplayPowerController;
+    private final DisplayPowerController mDisplayPowerController;
     private boolean mIsActive;
 
     public DisplayOffloadSessionImpl(
             @Nullable DisplayManagerInternal.DisplayOffloader displayOffloader,
-            DisplayPowerControllerInterface displayPowerController) {
+            DisplayPowerController displayPowerController) {
         mDisplayOffloader = displayOffloader;
         mDisplayPowerController = displayPowerController;
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 8bb33dd..bb2bed7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -126,7 +126,7 @@
  * slower by changing the "animator duration scale" option in Development Settings.
  */
 final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
-        DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
+        DisplayWhiteBalanceController.Callbacks{
     private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
     private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
 
@@ -253,10 +253,10 @@
     // The display blanker.
     private final DisplayBlanker mBlanker;
 
-    // The LogicalDisplay tied to this DisplayPowerController2.
+    // The LogicalDisplay tied to this DisplayPowerController.
     private final LogicalDisplay mLogicalDisplay;
 
-    // The ID of the LogicalDisplay tied to this DisplayPowerController2.
+    // The ID of the LogicalDisplay tied to this DisplayPowerController.
     private final int mDisplayId;
 
     // The ID of the display which this display follows for brightness purposes.
@@ -466,7 +466,7 @@
     private ObjectAnimator mColorFadeOffAnimator;
     private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
 
-    // True if this DisplayPowerController2 has been stopped and should no longer be running.
+    // True if this DisplayPowerController has been stopped and should no longer be running.
     private boolean mStopped;
 
     private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -481,7 +481,7 @@
     // DPCs following the brightness of this DPC. This is used in concurrent displays mode - there
     // is one lead display, the additional displays follow the brightness value of the lead display.
     @GuardedBy("mLock")
-    private final SparseArray<DisplayPowerControllerInterface> mDisplayBrightnessFollowers =
+    private final SparseArray<DisplayPowerController> mDisplayBrightnessFollowers =
             new SparseArray();
 
     private boolean mBootCompleted;
@@ -492,6 +492,11 @@
     // Used to scale the brightness in doze mode
     private float mDozeScaleFactor;
 
+    // Used to keep track of the display state from the latest request to override the doze screen
+    // state.
+    @GuardedBy("mLock")
+    private int mPendingOverrideDozeScreenStateLocked;
+
     /**
      * Creates the display power controller.
      */
@@ -586,7 +591,8 @@
                         mThermalBrightnessThrottlingDataId,
                         logicalDisplay.getPowerThrottlingDataIdLocked(),
                         mDisplayDeviceConfig, displayDeviceInfo.width, displayDeviceInfo.height,
-                        displayToken, mDisplayId), mContext, flags, mSensorManager);
+                        displayToken, mDisplayId), mContext, flags, mSensorManager,
+                        mDisplayBrightnessController.getCurrentBrightness());
         // Seed the cached brightness
         saveBrightnessInfo(getScreenBrightnessSetting());
         mAutomaticBrightnessStrategy =
@@ -673,7 +679,6 @@
     /**
      * Returns true if the proximity sensor screen-off function is available.
      */
-    @Override
     public boolean isProximitySensorAvailable() {
         return mDisplayPowerProximityStateController.isProximitySensorAvailable();
     }
@@ -685,7 +690,6 @@
      * @param includePackage if false will null out the package name in events
      */
     @Nullable
-    @Override
     public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
             @UserIdInt int userId, boolean includePackage) {
         if (mBrightnessTracker == null) {
@@ -694,7 +698,6 @@
         return mBrightnessTracker.getEvents(userId, includePackage);
     }
 
-    @Override
     public void onSwitchUser(@UserIdInt int newUserId, int userSerial, float newBrightness) {
         Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId, userSerial, newBrightness);
         mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -731,7 +734,6 @@
     }
 
     @Nullable
-    @Override
     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
             @UserIdInt int userId) {
         if (mBrightnessTracker == null) {
@@ -743,7 +745,6 @@
     /**
      * Persist the brightness slider events and ambient brightness stats to disk.
      */
-    @Override
     public void persistBrightnessTrackerState() {
         if (mBrightnessTracker != null) {
             mBrightnessTracker.persistBrightnessTrackerState();
@@ -800,21 +801,32 @@
         }
     }
 
-    @Override
     public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) {
         Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState));
-        mHandler.postAtTime(() -> {
-            if (mDisplayOffloadSession == null
-                    || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
-                            || displayState == Display.STATE_UNKNOWN)) {
-                return;
+        if (mDisplayOffloadSession != null
+                && (DisplayOffloadSession.isSupportedOffloadState(displayState)
+                || displayState == Display.STATE_UNKNOWN)) {
+            if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+                mWakelockController.acquireWakelock(
+                        WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
             }
-            mDisplayStateController.overrideDozeScreenState(displayState, reason);
-            updatePowerState();
-        }, mClock.uptimeMillis());
+            synchronized (mLock) {
+                mPendingOverrideDozeScreenStateLocked = displayState;
+            }
+            mHandler.postAtTime(() -> {
+                synchronized (mLock) {
+                    mDisplayStateController
+                            .overrideDozeScreenState(mPendingOverrideDozeScreenStateLocked, reason);
+                }
+                updatePowerState();
+                if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+                    mWakelockController.releaseWakelock(
+                            WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE);
+                }
+            }, mClock.uptimeMillis());
+        }
     }
 
-    @Override
     public void setDisplayOffloadSession(DisplayOffloadSession session) {
         if (session == mDisplayOffloadSession) {
             return;
@@ -823,7 +835,6 @@
         mDisplayOffloadSession = session;
     }
 
-    @Override
     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
         if (mAutomaticBrightnessController == null) {
             return null;
@@ -838,12 +849,11 @@
      *
      * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
      */
-    @Override
     public void onDisplayChanged(HighBrightnessModeMetadata hbmMetadata, int leadDisplayId) {
         mLeadDisplayId = leadDisplayId;
         final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
         if (device == null) {
-            Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
+            Slog.wtf(mTag, "Display Device is null in DisplayPowerController for display: "
                     + mLogicalDisplay.getDisplayIdLocked());
             return;
         }
@@ -917,10 +927,9 @@
     /**
      * Unregisters all listeners and interrupts all running threads; halting future work.
      *
-     * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
+     * This method should be called when the DisplayPowerController is no longer in use; i.e. when
      * the {@link #mDisplayId display} has been removed.
      */
-    @Override
     public void stop() {
         synchronized (mLock) {
             clearDisplayBrightnessFollowersLocked();
@@ -1197,7 +1206,6 @@
         }
     }
 
-    @Override
     public void setAutomaticScreenBrightnessMode(
             @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
         Message msg = mHandler.obtainMessage();
@@ -1295,7 +1303,7 @@
         boolean mustInitialize = false;
         mBrightnessReasonTemp.set(null);
         mTempBrightnessEvent.reset();
-        SparseArray<DisplayPowerControllerInterface> displayBrightnessFollowers;
+        SparseArray<DisplayPowerController> displayBrightnessFollowers;
         synchronized (mLock) {
             if (mStopped) {
                 return;
@@ -1338,30 +1346,6 @@
             initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
-        if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
-            // Sometimes, a display-state change can come without an associated PowerRequest,
-            // as with DisplayOffload.  For those cases, we have to make sure to also mark the
-            // display as "not ready" so that we can inform power-manager when the state-change is
-            // complete.
-            if (mPowerState.getScreenState() != state) {
-                final boolean wasReady;
-                synchronized (mLock) {
-                    wasReady = mDisplayReadyLocked;
-                    mDisplayReadyLocked = false;
-                    mustNotify = true;
-                }
-
-                if (wasReady) {
-                    // If we went from ready to not-ready from the state-change (instead of a
-                    // PowerRequest) there's a good chance that nothing is keeping PowerManager
-                    // from suspending. Grab the unfinished business suspend blocker to keep the
-                    // device awake until the display-state change goes into effect.
-                    mWakelockController.acquireWakelock(
-                            WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
-                }
-            }
-        }
-
         // Animate the screen state change unless already animating.
         // The transition may be deferred, so after this point we will use the
         // actual state instead of the desired one.
@@ -1419,7 +1403,7 @@
 
             mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
                     allowAutoBrightnessWhileDozing, mBrightnessReasonTemp.getReason(),
-                    mPowerRequest.policy,
+                    mPowerRequest.policy, mPowerRequest.useNormalBrightnessForDoze,
                     mDisplayBrightnessController.getLastUserSetScreenBrightness(),
                     userSetBrightnessChanged);
 
@@ -1485,7 +1469,9 @@
             brightnessState = clampScreenBrightness(brightnessState);
         }
 
-        if (Display.isDozeState(state)) {
+        if (mFlags.isNormalBrightnessForDozeParameterEnabled()
+                ? !mPowerRequest.useNormalBrightnessForDoze && mPowerRequest.policy == POLICY_DOZE
+                : Display.isDozeState(state)) {
             // TODO(b/329676661): Introduce a config property to choose between this brightness
             //  strategy and DOZE_DEFAULT
             // On some devices, when auto-brightness is disabled and the device is dozing, we use
@@ -1506,7 +1492,7 @@
             }
 
             // Use default brightness when dozing unless overridden.
-            if (Float.isNaN(brightnessState) && Display.isDozeState(state)
+            if (Float.isNaN(brightnessState)
                     && !mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig()) {
                 rawBrightnessState = mScreenBrightnessDozeConfig;
                 brightnessState = clampScreenBrightness(rawBrightnessState);
@@ -1550,7 +1536,7 @@
         float ambientLux = mAutomaticBrightnessController == null ? 0
                 : mAutomaticBrightnessController.getAmbientLux();
         for (int i = 0; i < displayBrightnessFollowers.size(); i++) {
-            DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i);
+            DisplayPowerController follower = displayBrightnessFollowers.valueAt(i);
             follower.setBrightnessToFollow(rawBrightnessState,
                     mDisplayBrightnessController.convertToNits(rawBrightnessState),
                     ambientLux, slowChange);
@@ -1592,7 +1578,7 @@
         // brightness sources (such as an app override) are not saved to the setting, but should be
         // reflected in HBM calculations.
         mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
-                mBrightnessClamperController.getBrightnessMaxReason());
+                clampedState.getBrightnessMaxReason());
 
         // Animate the screen brightness when the screen is on or dozing.
         // Skip the animation when the screen is off or suspended.
@@ -1808,7 +1794,7 @@
 
             if (userSetBrightnessChanged
                     || newEvent.getReason().getReason() != BrightnessReason.REASON_TEMPORARY) {
-                logBrightnessEvent(newEvent, unthrottledBrightnessState);
+                logBrightnessEvent(newEvent, unthrottledBrightnessState, clampedState);
             }
             if (mBrightnessEventRingBuffer != null) {
                 mBrightnessEventRingBuffer.append(newEvent);
@@ -1907,7 +1893,6 @@
         }
     }
 
-    @Override
     public void updateBrightness() {
         sendUpdatePowerState();
     }
@@ -1916,12 +1901,10 @@
      * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
      * currently enabled and forcing the screen to be dark.
      */
-    @Override
     public void ignoreProximitySensorUntilChanged() {
         mDisplayPowerProximityStateController.ignoreProximitySensorUntilChanged();
     }
 
-    @Override
     public void setBrightnessConfiguration(BrightnessConfiguration c,
             boolean shouldResetShortTermModel) {
         Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS,
@@ -1929,28 +1912,24 @@
         msg.sendToTarget();
     }
 
-    @Override
     public void setTemporaryBrightness(float brightness) {
         Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
                 Float.floatToIntBits(brightness), 0 /*unused*/);
         msg.sendToTarget();
     }
 
-    @Override
     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
         Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
                 Float.floatToIntBits(adjustment), 0 /*unused*/);
         msg.sendToTarget();
     }
 
-    @Override
     public void setBrightnessFromOffload(float brightness) {
         Message msg = mHandler.obtainMessage(MSG_SET_BRIGHTNESS_FROM_OFFLOAD,
                 Float.floatToIntBits(brightness), 0 /*unused*/);
         mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
     }
 
-    @Override
     public float[] getAutoBrightnessLevels(
             @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
         int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
@@ -1959,7 +1938,6 @@
         return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevels(mode, preset);
     }
 
-    @Override
     public float[] getAutoBrightnessLuxLevels(
             @AutomaticBrightnessController.AutomaticBrightnessMode int mode) {
         int preset = Settings.System.getIntForUser(mContext.getContentResolver(),
@@ -1968,7 +1946,6 @@
         return mDisplayDeviceConfig.getAutoBrightnessBrighteningLevelsLux(mode, preset);
     }
 
-    @Override
     public BrightnessInfo getBrightnessInfo() {
         synchronized (mCachedBrightnessInfo) {
             return new BrightnessInfo(
@@ -1982,7 +1959,6 @@
         }
     }
 
-    @Override
     public void onBootCompleted() {
         Message msg = mHandler.obtainMessage(MSG_BOOT_COMPLETED);
         mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -2001,6 +1977,9 @@
         synchronized (mCachedBrightnessInfo) {
             float stateMax = state != null ? state.getMaxBrightness() : PowerManager.BRIGHTNESS_MAX;
             float stateMin = state != null ? state.getMinBrightness() : PowerManager.BRIGHTNESS_MAX;
+            @BrightnessInfo.BrightnessMaxReason int maxReason =
+                    state != null ? state.getBrightnessMaxReason()
+                            : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
             final float minBrightness = Math.max(stateMin, Math.min(
                     mBrightnessRangeController.getCurrentBrightnessMin(), stateMax));
             final float maxBrightness = Math.min(
@@ -2027,7 +2006,7 @@
                             mBrightnessRangeController.getTransitionPoint());
             changed |=
                     mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
-                            mBrightnessClamperController.getBrightnessMaxReason());
+                            maxReason);
             return changed;
         }
     }
@@ -2488,20 +2467,21 @@
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
         mAutomaticBrightnessStrategy.setUseAutoBrightness(screenBrightnessModeSetting
                 == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
+        if (screenBrightnessModeSetting == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
+            // In manual mode, all brightness changes should be saved immediately.
+            mDisplayBrightnessController.saveBrightnessIfNeeded();
+        }
     }
 
-
-    @Override
     public float getScreenBrightnessSetting() {
         return mDisplayBrightnessController.getScreenBrightnessSetting();
     }
 
-    @Override
     public float getDozeBrightnessForOffload() {
         return mDisplayBrightnessController.getCurrentBrightness() * mDozeScaleFactor;
     }
 
-    @Override
     public void setBrightness(float brightness) {
         // After HBMController and NBMController migration to Clampers framework
         // currentBrightnessMax should be taken from clampers controller
@@ -2510,7 +2490,6 @@
                 mBrightnessRangeController.getCurrentBrightnessMax());
     }
 
-    @Override
     public void setBrightness(float brightness, int userSerial) {
         // After HBMController and NBMController migration to Clampers framework
         // currentBrightnessMax should be taken from clampers controller
@@ -2519,17 +2498,14 @@
                 mBrightnessRangeController.getCurrentBrightnessMax());
     }
 
-    @Override
     public int getDisplayId() {
         return mDisplayId;
     }
 
-    @Override
     public int getLeadDisplayId() {
         return mLeadDisplayId;
     }
 
-    @Override
     public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
             boolean slowChange) {
         mBrightnessRangeController.onAmbientLuxChange(ambientLux);
@@ -2590,16 +2566,14 @@
                 mAutomaticBrightnessController.getLastSensorTimestamps());
     }
 
-    @Override
-    public void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+    public void addDisplayBrightnessFollower(DisplayPowerController follower) {
         synchronized (mLock) {
             mDisplayBrightnessFollowers.append(follower.getDisplayId(), follower);
             sendUpdatePowerStateLocked();
         }
     }
 
-    @Override
-    public void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower) {
+    public void removeDisplayBrightnessFollower(DisplayPowerController follower) {
         synchronized (mLock) {
             mDisplayBrightnessFollowers.remove(follower.getDisplayId());
             mHandler.postAtTime(() -> follower.setBrightnessToFollow(
@@ -2611,7 +2585,7 @@
     @GuardedBy("mLock")
     private void clearDisplayBrightnessFollowersLocked() {
         for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
-            DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+            DisplayPowerController follower = mDisplayBrightnessFollowers.valueAt(i);
             mHandler.postAtTime(() -> follower.setBrightnessToFollow(
                     PowerManager.BRIGHTNESS_INVALID_FLOAT, BrightnessMappingStrategy.INVALID_NITS,
                     /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis());
@@ -2619,11 +2593,12 @@
         mDisplayBrightnessFollowers.clear();
     }
 
-    @Override
     public void dump(final PrintWriter pw) {
         synchronized (mLock) {
             pw.println();
             pw.println("Display Power Controller:");
+            pw.println("-------------------------");
+
             pw.println("  mDisplayId=" + mDisplayId);
             pw.println("  mLeadDisplayId=" + mLeadDisplayId);
             pw.println("  mLightSensor=" + mLightSensor);
@@ -2698,25 +2673,39 @@
                     + mColorFadeOffAnimator.isStarted());
         }
 
+        pw.println();
         if (mPowerState != null) {
             mPowerState.dump(pw);
         }
 
-        if (mAutomaticBrightnessController != null) {
-            mAutomaticBrightnessController.dump(pw);
-            dumpBrightnessEvents(pw);
+        pw.println();
+        if (mDisplayBrightnessController != null) {
+            mDisplayBrightnessController.dump(pw);
         }
 
-        dumpRbcEvents(pw);
-
-        if (mScreenOffBrightnessSensorController != null) {
-            mScreenOffBrightnessSensorController.dump(pw);
+        pw.println();
+        if (mBrightnessClamperController != null) {
+            mBrightnessClamperController.dump(ipw);
         }
 
+        pw.println();
         if (mBrightnessRangeController != null) {
             mBrightnessRangeController.dump(pw);
         }
 
+        pw.println();
+        if (mAutomaticBrightnessController != null) {
+            mAutomaticBrightnessController.dump(pw);
+            dumpBrightnessEvents(pw);
+        }
+        dumpRbcEvents(pw);
+
+        pw.println();
+        if (mScreenOffBrightnessSensorController != null) {
+            mScreenOffBrightnessSensorController.dump(pw);
+        }
+
+        pw.println();
         if (mBrightnessThrottler != null) {
             mBrightnessThrottler.dump(pw);
         }
@@ -2728,24 +2717,13 @@
         }
 
         pw.println();
-
         if (mWakelockController != null) {
             mWakelockController.dumpLocal(pw);
         }
 
         pw.println();
-        if (mDisplayBrightnessController != null) {
-            mDisplayBrightnessController.dump(pw);
-        }
-
-        pw.println();
         if (mDisplayStateController != null) {
-            mDisplayStateController.dumpsys(pw);
-        }
-
-        pw.println();
-        if (mBrightnessClamperController != null) {
-            mBrightnessClamperController.dump(ipw);
+            mDisplayStateController.dump(pw);
         }
     }
 
@@ -2925,7 +2903,8 @@
         return FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__ENTIRE_REASON__REASON_UNKNOWN;
     }
 
-    private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness) {
+    private void logBrightnessEvent(BrightnessEvent event, float unmodifiedBrightness,
+            DisplayBrightnessState brightnessState) {
         int modifier = event.getReason().getModifier();
         int flags = event.getFlags();
         // It's easier to check if the brightness is at maximum level using the brightness
@@ -2962,7 +2941,7 @@
                     event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT,
                     event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR,
                     (modifier & BrightnessReason.MODIFIER_LOW_POWER) > 0,
-                    mBrightnessClamperController.getBrightnessMaxReason(),
+                    brightnessState.getBrightnessMaxReason(),
                     // TODO: (flc) add brightnessMinReason here too.
                     (modifier & BrightnessReason.MODIFIER_DIMMED) > 0,
                     event.isRbcEnabled(),
@@ -3150,19 +3129,17 @@
         }
     }
 
-    @Override
     public void setAutoBrightnessLoggingEnabled(boolean enabled) {
         if (mAutomaticBrightnessController != null) {
             mAutomaticBrightnessController.setLoggingEnabled(enabled);
         }
     }
 
-    @Override // DisplayWhiteBalanceController.Callbacks
+    // DisplayWhiteBalanceController.Callbacks
     public void updateWhiteBalance() {
         sendUpdatePowerState();
     }
 
-    @Override
     public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
         Message msg = mHandler.obtainMessage();
         msg.what = MSG_SET_DWBC_LOGGING_ENABLED;
@@ -3170,7 +3147,6 @@
         msg.sendToTarget();
     }
 
-    @Override
     public void setAmbientColorTemperatureOverride(float cct) {
         Message msg = mHandler.obtainMessage();
         msg.what = MSG_SET_DWBC_COLOR_OVERRIDE;
@@ -3295,10 +3271,10 @@
         BrightnessClamperController getBrightnessClamperController(Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags, SensorManager sensorManager) {
+                DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
 
             return new BrightnessClamperController(handler, clamperChangeListener, data, context,
-                    flags, sensorManager);
+                    flags, sensorManager, currentBrightness);
         }
 
         DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler,
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
deleted file mode 100644
index d28578a..0000000
--- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
+++ /dev/null
@@ -1,267 +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.server.display;
-
-import android.content.pm.ParceledListSlice;
-import android.hardware.display.AmbientBrightnessDayStats;
-import android.hardware.display.BrightnessChangeEvent;
-import android.hardware.display.BrightnessConfiguration;
-import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManager;
-import android.view.Display;
-
-import java.io.PrintWriter;
-
-/**
- * An interface to manage the display's power state and brightness
- */
-public interface DisplayPowerControllerInterface {
-    /**
-     * Notified when the display is changed.
-     *
-     * We use this to apply any changes that might be needed when displays get swapped on foldable
-     * devices, when layouts change, etc.
-     *
-     * Must be called while holding the SyncRoot lock.
-     *
-     * @param hbmInfo The high brightness mode metadata, like
-     *                remaining time and hbm events, for the corresponding
-     *                physical display, to make sure we stay within the safety margins.
-     * @param leadDisplayId The display who is considered our "leader" for things like brightness.
-     */
-    void onDisplayChanged(HighBrightnessModeMetadata hbmInfo, int leadDisplayId);
-
-    /**
-     * Unregisters all listeners and interrupts all running threads; halting future work.
-     *
-     * This method should be called when the DisplayPowerController is no longer in use; i.e. when
-     * the display has been removed.
-     */
-    void stop();
-
-    /**
-     * Used to update the display's BrightnessConfiguration
-     * @param config The new BrightnessConfiguration
-     */
-    void setBrightnessConfiguration(BrightnessConfiguration config,
-            boolean shouldResetShortTermModel);
-
-    /**
-     * Used to set the ambient color temperature of the Display
-     * @param ambientColorTemperature The target ambientColorTemperature
-     */
-    void setAmbientColorTemperatureOverride(float ambientColorTemperature);
-
-    /**
-     * Used to decide the associated AutomaticBrightnessController's BrightnessMode
-     * @param mode The auto-brightness mode
-     */
-    void setAutomaticScreenBrightnessMode(
-            @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-
-    /**
-     * Used to enable/disable the logging of the WhileBalance associated entities
-     * @param enabled Flag which represents if the logging is the be enabled
-     */
-    void setDisplayWhiteBalanceLoggingEnabled(boolean enabled);
-
-    /**
-     * Used to dump the state.
-     * @param writer The PrintWriter used to dump the state.
-     */
-    void dump(PrintWriter writer);
-
-    /**
-     * Used to get the ambient brightness stats
-     */
-    ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId);
-
-    /**
-     * Get the default brightness configuration
-     */
-    BrightnessConfiguration getDefaultBrightnessConfiguration();
-
-    /**
-     * Set the screen brightness of the associated display
-     * @param brightness The value to which the brightness is to be set
-     */
-    void setBrightness(float brightness);
-
-    /**
-     * Set the screen brightness of the associated display
-     * @param brightness The value to which the brightness is to be set
-     * @param userSerial The user for which the brightness value is to be set.
-     */
-    void setBrightness(float brightness, int userSerial);
-
-    /**
-     * Checks if the proximity sensor is available
-     */
-    boolean isProximitySensorAvailable();
-
-    /**
-     * Persist the brightness slider events and ambient brightness stats to disk.
-     */
-    void persistBrightnessTrackerState();
-
-    /**
-     * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
-     * currently enabled and forcing the screen to be dark.
-     */
-    void ignoreProximitySensorUntilChanged();
-
-    /**
-     * Requests a new power state.
-     *
-     * @param request The requested power state.
-     * @param waitForNegativeProximity If true, issues a request to wait for
-     * negative proximity before turning the screen back on,
-     * assuming the screen was turned off by the proximity sensor.
-     * @return True if display is ready, false if there are important changes that must
-     * be made asynchronously.
-     */
-    boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
-            boolean waitForNegativeProximity);
-
-    /**
-     * Overrides the current doze screen state.
-     *
-     * @param displayState the new doze display state.
-     * @param reason the reason behind the new doze display state.
-     */
-    void overrideDozeScreenState(int displayState, @Display.StateReason int reason);
-
-    void setDisplayOffloadSession(DisplayManagerInternal.DisplayOffloadSession session);
-
-    /**
-     * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
-     * value.
-     */
-    void setTemporaryAutoBrightnessAdjustment(float adjustment);
-
-    /**
-     * Sets temporary brightness from the offload chip until we get a brightness value from
-     * the light sensor.
-     * @param brightness The brightness value between {@link PowerManager.BRIGHTNESS_MIN} and
-     * {@link PowerManager.BRIGHTNESS_MAX}. Values outside of that range will be ignored.
-     */
-    void setBrightnessFromOffload(float brightness);
-
-    /**
-     * Gets the screen brightness setting
-     */
-    float getScreenBrightnessSetting();
-
-    /**
-     * Gets the brightness value used when the device is in doze
-     */
-    float getDozeBrightnessForOffload();
-
-    /**
-     * Sets up the temporary brightness for the associated display
-     */
-    void setTemporaryBrightness(float brightness);
-
-    /**
-     * Gets the associated {@link BrightnessInfo}
-     */
-    BrightnessInfo getBrightnessInfo();
-
-    /**
-     * Get the {@link BrightnessChangeEvent}s for the specified user.
-     */
-    ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(int userId, boolean hasUsageStats);
-
-    /**
-     * Sets up the logging for the associated {@link AutomaticBrightnessController}
-     * @param enabled Flag to represent if the logging is to be enabled
-     */
-    void setAutoBrightnessLoggingEnabled(boolean enabled);
-
-    /**
-     * Handles the changes to be done to update the brightness when the user is changed
-     * @param newUserId The new userId
-     * @param userSerial The serial number of the new user
-     * @param newBrightness The brightness for the new user
-     */
-    void onSwitchUser(int newUserId, int userSerial, float newBrightness);
-
-    /**
-     * Get the ID of the display associated with this DPC.
-     * @return The display ID
-     */
-    int getDisplayId();
-
-    /**
-     * Get the ID of the display that is the leader of this DPC.
-     *
-     * Note that this is different than the display associated with the DPC. The leader is another
-     * display which we follow for things like brightness.
-     *
-     * Must be called while holding the SyncRoot lock.
-     */
-    int getLeadDisplayId();
-
-    /**
-     * Set the brightness to follow if this is an additional display in a set of concurrent
-     * displays.
-     * @param leadDisplayBrightness The brightness of the lead display in the set of concurrent
-     *                              displays
-     * @param nits The brightness value in nits if the device supports nits. Set to a negative
-     *             number otherwise.
-     * @param ambientLux The lux value that will be passed to {@link HighBrightnessModeController}
-     * @param slowChange Indicates whether we should slowly animate to the given brightness value.
-     */
-    void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux,
-            boolean slowChange);
-
-    /**
-     * Add an additional display that will copy the brightness value from this display. This is used
-     * when the device is in concurrent displays mode.
-     * @param follower The DPC that should copy the brightness value from this DPC
-     */
-    void addDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
-
-    /**
-     * Removes the given display from the list of brightness followers.
-     * @param follower The DPC to remove from the followers list
-     */
-    void removeDisplayBrightnessFollower(DisplayPowerControllerInterface follower);
-
-    /**
-     * Indicate that boot has been completed and the screen is ready to update.
-     */
-    void onBootCompleted();
-
-    /**
-     * Get the brightness levels used to determine automatic brightness based on lux levels.
-     * @param mode The auto-brightness mode
-     * @return The brightness levels for the specified mode. The values are between
-     * {@link PowerManager.BRIGHTNESS_MIN} and {@link PowerManager.BRIGHTNESS_MAX}.
-     */
-    float[] getAutoBrightnessLevels(
-            @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-
-    /**
-     * Get the lux levels used to determine automatic brightness.
-     * @param mode The auto-brightness mode
-     * @return The lux levels for the specified mode
-     */
-    float[] getAutoBrightnessLuxLevels(
-            @AutomaticBrightnessController.AutomaticBrightnessMode int mode);
-}
diff --git a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index 882c02f..215932c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -315,6 +315,7 @@
     public void dumpLocal(PrintWriter pw) {
         pw.println();
         pw.println("DisplayPowerProximityStateController:");
+        pw.println("-------------------------------------");
         synchronized (mLock) {
             pw.println("  mPendingWaitForNegativeProximityLocked="
                     + mPendingWaitForNegativeProximityLocked);
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index e5efebc..2fbb114 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -344,7 +344,6 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println();
         pw.println("Display Power State:");
         pw.println("  mStopped=" + mStopped);
         pw.println("  mScreenState=" + Display.stateToString(mScreenState));
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index 44c8d1c..28a0b28 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -231,7 +231,7 @@
         if (!isExternalDisplayAllowed()) {
             Slog.w(TAG, "handleExternalDisplayConnectedLocked: External display can not be used"
                                 + " because it is currently not allowed.");
-            mDisplayNotificationManager.onHighTemperatureExternalDisplayNotAllowed();
+            mHandler.post(mDisplayNotificationManager::onHighTemperatureExternalDisplayNotAllowed);
             return;
         }
 
@@ -329,7 +329,7 @@
 
         if (!isExternalDisplayAllowed()) {
             Slog.w(TAG, "External display is currently not allowed and is getting disabled.");
-            mDisplayNotificationManager.onHighTemperatureExternalDisplayNotAllowed();
+            mHandler.post(mDisplayNotificationManager::onHighTemperatureExternalDisplayNotAllowed);
         }
 
         mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, /*enabled=*/ false);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 6e0b9cf..0570b2a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -1286,7 +1286,10 @@
                 pw.println("  " + mSupportedModes.valueAt(i));
             }
             pw.println("mSupportedColorModes=" + mSupportedColorModes);
-            pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
+            pw.println("");
+            pw.println("DisplayDeviceConfig: ");
+            pw.println("---------------------");
+            pw.println(mDisplayDeviceConfig);
         }
 
         private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5d55d190..e8be8a4 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -1040,6 +1040,9 @@
 
     public void dumpLocked(PrintWriter pw) {
         pw.println("mDisplayId=" + mDisplayId);
+        pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null
+                ? mPrimaryDisplayDevice.getNameLocked() + "(" + mPrimaryDisplayDevice.getUniqueId()
+                        + ")" : "null"));
         pw.println("mIsEnabled=" + mIsEnabled);
         pw.println("mIsInTransition=" + mIsInTransition);
         pw.println("mLayerStack=" + mLayerStack);
@@ -1049,8 +1052,6 @@
         pw.println("mRequestedColorMode=" + mRequestedColorMode);
         pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")");
         pw.println("mDisplayScalingDisabled=" + mDisplayScalingDisabled);
-        pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
-                mPrimaryDisplayDevice.getNameLocked() : "null"));
         pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
         pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
         pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index e9ecfc6..c3f6a52 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -427,6 +427,7 @@
 
     public void dumpLocked(PrintWriter pw) {
         pw.println("LogicalDisplayMapper:");
+        pw.println("---------------------");
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         ipw.increaseIndent();
 
@@ -477,14 +478,14 @@
             // The boot animation might still be in progress, we do not want to switch states now
             // as the boot animation would end up with an incorrect size.
             if (DEBUG) {
-                Slog.d(TAG, "Postponing transition to state: " + mPendingDeviceState
-                        + " until boot is completed");
+                Slog.d(TAG, "Postponing transition to state: "
+                        + mPendingDeviceState.getIdentifier() + " until boot is completed");
             }
             mDeviceStateToBeAppliedAfterBoot = state;
             return;
         }
 
-        Slog.i(TAG, "Requesting Transition to state: " + state + ", from state="
+        Slog.i(TAG, "Requesting Transition to state: " + state.getIdentifier() + ", from state="
                 + mDeviceState.getIdentifier() + ", interactive=" + mInteractive
                 + ", mBootCompleted=" + mBootCompleted);
         // As part of a state transition, we may need to turn off some displays temporarily so that
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 2d7792d..9cdc918 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -628,7 +628,9 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("PersistentDataStore");
+        pw.println("PersistentDataStore:");
+        pw.println("--------------------");
+
         pw.println("  mLoaded=" + mLoaded);
         pw.println("  mDirty=" + mDirty);
         pw.println("  RememberedWifiDisplays:");
diff --git a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
index 0a884c9..b63eba3 100644
--- a/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
+++ b/services/core/java/com/android/server/display/ScreenOffBrightnessSensorController.java
@@ -121,6 +121,7 @@
     /** Dump current state */
     public void dump(PrintWriter pw) {
         pw.println("Screen Off Brightness Sensor Controller:");
+        pw.println("----------------------------------------");
         IndentingPrintWriter idpw = new IndentingPrintWriter(pw);
         idpw.increaseIndent();
         idpw.println("registered=" + mRegistered);
diff --git a/services/core/java/com/android/server/display/SmallAreaDetectionController.java b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
index bf384b0..3ed7e57 100644
--- a/services/core/java/com/android/server/display/SmallAreaDetectionController.java
+++ b/services/core/java/com/android/server/display/SmallAreaDetectionController.java
@@ -133,7 +133,8 @@
     }
 
     void dump(PrintWriter pw) {
-        pw.println("Small area detection allowlist");
+        pw.println("Small area detection allowlist:");
+        pw.println("-------------------------------");
         pw.println("  Packages:");
         synchronized (mLock) {
             for (String pkg : mAllowPkgMap.keySet()) {
diff --git a/services/core/java/com/android/server/display/WakelockController.java b/services/core/java/com/android/server/display/WakelockController.java
index 7bc7971..3520723 100644
--- a/services/core/java/com/android/server/display/WakelockController.java
+++ b/services/core/java/com/android/server/display/WakelockController.java
@@ -20,6 +20,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.display.utils.DebugUtils;
 
@@ -37,7 +38,8 @@
     public static final int WAKE_LOCK_PROXIMITY_NEGATIVE = 2;
     public static final int WAKE_LOCK_PROXIMITY_DEBOUNCE = 3;
     public static final int WAKE_LOCK_STATE_CHANGED = 4;
-    public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 5;
+    public static final int WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE = 5;
+    public static final int WAKE_LOCK_UNFINISHED_BUSINESS = 6;
 
     @VisibleForTesting
     static final int WAKE_LOCK_MAX = WAKE_LOCK_UNFINISHED_BUSINESS;
@@ -53,18 +55,23 @@
             WAKE_LOCK_PROXIMITY_NEGATIVE,
             WAKE_LOCK_PROXIMITY_DEBOUNCE,
             WAKE_LOCK_STATE_CHANGED,
+            WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
             WAKE_LOCK_UNFINISHED_BUSINESS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WAKE_LOCK_TYPE {
     }
 
+    private final Object mLock = new Object();
+
     // Asynchronous callbacks into the power manager service.
     // Only invoked from the handler thread while no locks are held.
     private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks;
 
     // Identifiers for suspend blocker acquisition requests
     private final String mSuspendBlockerIdUnfinishedBusiness;
+    @GuardedBy("mLock")
+    private final String mSuspendBlockerOverrideDozeScreenState;
     private final String mSuspendBlockerIdOnStateChanged;
     private final String mSuspendBlockerIdProxPositive;
     private final String mSuspendBlockerIdProxNegative;
@@ -73,6 +80,10 @@
     // True if we have unfinished business and are holding a suspend-blocker.
     private boolean mUnfinishedBusiness;
 
+    // True if we have are holding a suspend-blocker to override the doze screen state.
+    @GuardedBy("mLock")
+    private boolean mIsOverrideDozeScreenStateAcquired;
+
     // True if we have have debounced the proximity change impact and are holding a suspend-blocker.
     private boolean mHasProximityDebounced;
 
@@ -108,6 +119,7 @@
         mTag = TAG + "[" + mDisplayId + "]";
         mDisplayPowerCallbacks = callbacks;
         mSuspendBlockerIdUnfinishedBusiness = "[" + displayId + "]unfinished business";
+        mSuspendBlockerOverrideDozeScreenState =  "[" + displayId + "]override doze screen state";
         mSuspendBlockerIdOnStateChanged = "[" + displayId + "]on state changed";
         mSuspendBlockerIdProxPositive = "[" + displayId + "]prox positive";
         mSuspendBlockerIdProxNegative = "[" + displayId + "]prox negative";
@@ -154,6 +166,10 @@
                 return acquireProxDebounceSuspendBlocker();
             case WAKE_LOCK_STATE_CHANGED:
                 return acquireStateChangedSuspendBlocker();
+            case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+                synchronized (mLock) {
+                    return acquireOverrideDozeScreenStateSuspendBlockerLocked();
+                }
             case WAKE_LOCK_UNFINISHED_BUSINESS:
                 return acquireUnfinishedBusinessSuspendBlocker();
             default:
@@ -171,6 +187,10 @@
                 return releaseProxDebounceSuspendBlocker();
             case WAKE_LOCK_STATE_CHANGED:
                 return releaseStateChangedSuspendBlocker();
+            case WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE:
+                synchronized (mLock) {
+                    return releaseOverrideDozeScreenStateSuspendBlockerLocked();
+                }
             case WAKE_LOCK_UNFINISHED_BUSINESS:
                 return releaseUnfinishedBusinessSuspendBlocker();
             default:
@@ -220,6 +240,42 @@
     }
 
     /**
+     * Acquires the suspend blocker to override the doze screen state and notifies the
+     * PowerManagerService about the changes. Note that this utility is syncronized because a
+     * request to override the doze screen state can come from a non-power thread.
+     */
+    @GuardedBy("mLock")
+    private boolean acquireOverrideDozeScreenStateSuspendBlockerLocked() {
+        // Grab a wake lock if we have unfinished business.
+        if (!mIsOverrideDozeScreenStateAcquired) {
+            if (DEBUG) {
+                Slog.d(mTag, "Acquiring suspend blocker to override the doze screen state...");
+            }
+            mDisplayPowerCallbacks.acquireSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+            mIsOverrideDozeScreenStateAcquired = true;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Releases the override doze screen state suspend blocker and notifies the PowerManagerService
+     * about the changes.
+     */
+    @GuardedBy("mLock")
+    private boolean releaseOverrideDozeScreenStateSuspendBlockerLocked() {
+        if (mIsOverrideDozeScreenStateAcquired) {
+            if (DEBUG) {
+                Slog.d(mTag, "Finished overriding doze screen state...");
+            }
+            mDisplayPowerCallbacks.releaseSuspendBlocker(mSuspendBlockerOverrideDozeScreenState);
+            mIsOverrideDozeScreenStateAcquired = false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Acquires the unfinished business wakelock and notifies the PowerManagerService about the
      * changes.
      */
@@ -361,11 +417,13 @@
      */
     public void dumpLocal(PrintWriter pw) {
         pw.println("WakelockController State:");
+        pw.println("-------------------------");
         pw.println("  mDisplayId=" + mDisplayId);
         pw.println("  mUnfinishedBusiness=" + hasUnfinishedBusiness());
         pw.println("  mOnStateChangePending=" + isOnStateChangedPending());
         pw.println("  mOnProximityPositiveMessages=" + isProximityPositiveAcquired());
         pw.println("  mOnProximityNegativeMessages=" + isProximityNegativeAcquired());
+        pw.println("  mIsOverrideDozeScreenStateAcquired=" + isOverrideDozeScreenStateAcquired());
     }
 
     @VisibleForTesting
@@ -394,6 +452,13 @@
     }
 
     @VisibleForTesting
+    String getSuspendBlockerOverrideDozeScreenState() {
+        synchronized (mLock) {
+            return mSuspendBlockerOverrideDozeScreenState;
+        }
+    }
+
+    @VisibleForTesting
     boolean hasUnfinishedBusiness() {
         return mUnfinishedBusiness;
     }
@@ -417,4 +482,11 @@
     boolean hasProximitySensorDebounced() {
         return mHasProximityDebounced;
     }
+
+    @VisibleForTesting
+    boolean isOverrideDozeScreenStateAcquired() {
+        synchronized (mLock) {
+            return mIsOverrideDozeScreenStateAcquired;
+        }
+    }
 }
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 e157b05..72a91d5 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -339,6 +339,13 @@
     }
 
     /**
+     * Flush the brightness update that has been made to the persistent data store.
+     */
+    public void saveBrightnessIfNeeded() {
+        mBrightnessSetting.saveIfNeeded();
+    }
+
+    /**
      * Sets the current screen brightness, and notifies the BrightnessSetting about the change.
      */
     public void updateScreenBrightnessSetting(float brightnessValue, float maxBrightness) {
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 7835220..06890e7 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.view.Display;
@@ -167,7 +168,7 @@
             StrategySelectionRequest strategySelectionRequest) {
         DisplayBrightnessStrategy displayBrightnessStrategy = mInvalidBrightnessStrategy;
         int targetDisplayState = strategySelectionRequest.getTargetDisplayState();
-        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = strategySelectionRequest
+        DisplayPowerRequest displayPowerRequest = strategySelectionRequest
                 .getDisplayPowerRequest();
         setAllowAutoBrightnessWhileDozing(strategySelectionRequest.getDisplayOffloadSession());
         if (targetDisplayState == Display.STATE_OFF) {
@@ -261,6 +262,7 @@
     public void dump(PrintWriter writer) {
         writer.println();
         writer.println("DisplayBrightnessStrategySelector:");
+        writer.println("----------------------------------");
         writer.println("  mDisplayId= " + mDisplayId);
         writer.println("  mOldBrightnessStrategyName= " + mOldBrightnessStrategyName);
         writer.println(
@@ -301,6 +303,7 @@
                 mAllowAutoBrightnessWhileDozing,
                 BrightnessReason.REASON_UNKNOWN,
                 strategySelectionRequest.getDisplayPowerRequest().policy,
+                strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze,
                 strategySelectionRequest.getLastUserSetScreenBrightness(),
                 strategySelectionRequest.isUserSetBrightnessChanged());
         return mAutomaticBrightnessStrategy1.isAutoBrightnessValid();
@@ -331,12 +334,14 @@
     /**
      * Validates if the conditions are met to qualify for the DozeBrightnessStrategy.
      */
-    private boolean shouldUseDozeBrightnessStrategy(
-            DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
+    private boolean shouldUseDozeBrightnessStrategy(DisplayPowerRequest displayPowerRequest) {
         // We are not checking the targetDisplayState, but rather relying on the policy because
         // a user can define a different display state(displayPowerRequest.dozeScreenState) too
-        // in the request with the Doze policy
-        return displayPowerRequest.policy == DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE
+        // in the request with the Doze policy and user might request an override to force certain
+        // brightness.
+        return (!mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+                || !displayPowerRequest.useNormalBrightnessForDoze)
+                && displayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE
                 && !mAllowAutoBrightnessWhileDozing
                 && BrightnessUtils.isValidBrightnessValue(displayPowerRequest.dozeScreenBrightness);
     }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index 59fffe7..d3be33f 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -75,10 +75,13 @@
     private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
 
     private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
+    private final DisplayManagerFlags mDisplayManagerFlags;
     private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
 
     private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
     @Nullable
+    private BrightnessPowerClamper mPowerClamper;
+    @Nullable
     private Type mClamperType = null;
 
     private boolean mClamperApplied = false;
@@ -93,16 +96,18 @@
 
     public BrightnessClamperController(Handler handler,
             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
-            DisplayManagerFlags flags, SensorManager sensorManager) {
-        this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager);
+            DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
+        this(new Injector(), handler, clamperChangeListener, data, context, flags, sensorManager,
+                currentBrightness);
     }
 
     @VisibleForTesting
     BrightnessClamperController(Injector injector, Handler handler,
             ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context,
-            DisplayManagerFlags flags, SensorManager sensorManager) {
+            DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
         mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
         mHandler = handler;
+        mDisplayManagerFlags = flags;
         mLightSensorController = injector.getLightSensorController(sensorManager, context,
                 mLightSensorListener, mHandler);
 
@@ -117,7 +122,15 @@
         };
 
         mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags,
-                context);
+                context, currentBrightness);
+        if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+            for (BrightnessClamper clamper: mClampers) {
+                if (clamper.getType() == Type.POWER) {
+                    mPowerClamper = (BrightnessPowerClamper) clamper;
+                    break;
+                }
+            }
+        }
         mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener,
                 data);
 
@@ -161,6 +174,7 @@
         builder.setBrightness(cappedBrightness);
         builder.setMaxBrightness(mBrightnessCap);
         builder.setCustomAnimationRate(mCustomAnimationRate);
+        builder.setBrightnessMaxReason(getBrightnessMaxReason());
 
         if (mClamperType != null) {
             builder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
@@ -182,22 +196,17 @@
             mModifiers.get(i).apply(request, builder);
         }
 
+        if (mDisplayManagerFlags.isPowerThrottlingClamperEnabled()) {
+            if (mPowerClamper != null) {
+                mPowerClamper.updateCurrentBrightness(cappedBrightness);
+            }
+        }
+
         return builder.build();
     }
 
-    /**
-     * See BrightnessThrottler.getBrightnessMaxReason:
-     * used in:
-     * 1) DPC2.CachedBrightnessInfo to determine changes
-     * 2) DPC2.logBrightnessEvent
-     * 3) HBMController - for logging
-     * Method is called in mHandler thread (DisplayControllerHandler), in the same thread
-     * recalculateBrightnessCap and DPC2.updatePowerStateInternal are called.
-     * Should be moved to DisplayBrightnessState OR derived from DisplayBrightnessState
-     * TODO: b/263362199
-     */
     @BrightnessInfo.BrightnessMaxReason
-    public int getBrightnessMaxReason() {
+    private int getBrightnessMaxReason() {
         if (mClamperType == null) {
             return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
         } else if (mClamperType == Type.THERMAL) {
@@ -224,6 +233,7 @@
      */
     public void dump(PrintWriter writer) {
         writer.println("BrightnessClamperController:");
+        writer.println("----------------------------");
         writer.println("  mBrightnessCap: " + mBrightnessCap);
         writer.println("  mClamperType: " + mClamperType);
         writer.println("  mClamperApplied: " + mClamperApplied);
@@ -321,13 +331,17 @@
 
         List<BrightnessClamper<? super DisplayDeviceData>> getClampers(Handler handler,
                 ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
-                DisplayManagerFlags flags, Context context) {
+                DisplayManagerFlags flags, Context context, float currentBrightness) {
             List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
             clampers.add(
                     new BrightnessThermalClamper(handler, clamperChangeListener, data));
             if (flags.isPowerThrottlingClamperEnabled()) {
-                clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
-                        data));
+                // Check if power-throttling config is present.
+                PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
+                if (configData != null) {
+                    clampers.add(new BrightnessPowerClamper(handler, clamperChangeListener,
+                            data, currentBrightness));
+                }
             }
             if (flags.isBrightnessWearBedtimeModeClamperEnabled()) {
                 clampers.add(new BrightnessWearBedtimeModeClamper(handler, context,
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 790322d..85e81f9 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
@@ -21,16 +21,23 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.os.Handler;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
 import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.Temperature;
 import android.provider.DeviceConfigInterface;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingConfigData;
 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData;
 import com.android.server.display.DisplayDeviceConfig.PowerThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessUtils;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.display.utils.DeviceConfigParsingUtils;
 
@@ -65,14 +72,21 @@
     private PowerThrottlingData mPowerThrottlingDataActive = null;
     @Nullable
     private PowerThrottlingConfigData mPowerThrottlingConfigData = null;
-
+    @NonNull
+    private final ThermalLevelListener mThermalLevelListener;
+    @NonNull
+    private final PowerChangeListener mPowerChangeListener;
     private @Temperature.ThrottlingStatus int mCurrentThermalLevel = Temperature.THROTTLING_NONE;
+    private boolean mCurrentThermalLevelChanged = false;
     private float mCurrentAvgPowerConsumed = 0;
     @Nullable
     private String mUniqueDisplayId = null;
     @Nullable
     private String mDataId = null;
-
+    private float mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
+    private float mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
+    private float mCustomAnimationRateSecDeviceConfig =
+                        DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
     private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
         try {
             int status = DeviceConfigParsingUtils.parseThermalStatus(key);
@@ -88,23 +102,41 @@
 
 
     BrightnessPowerClamper(Handler handler, ClamperChangeListener listener,
-            PowerData powerData) {
-        this(new Injector(), handler, listener, powerData);
+            PowerData powerData, float currentBrightness) {
+        this(new Injector(), handler, listener, powerData, currentBrightness);
     }
 
     @VisibleForTesting
     BrightnessPowerClamper(Injector injector, Handler handler, ClamperChangeListener listener,
-            PowerData powerData) {
+                           PowerData powerData, float currentBrightness) {
         super(handler, listener);
         mInjector = injector;
-        mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
+        mCurrentBrightness = currentBrightness;
+        mPowerChangeListener = (powerConsumed, thermalStatus) -> {
+            recalculatePowerQuotaChange(powerConsumed, thermalStatus);
+        };
+        mPowerThrottlingConfigData = powerData.getPowerThrottlingConfigData();
+        if (mPowerThrottlingConfigData != null) {
+            mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+        }
+        mThermalLevelListener = new ThermalLevelListener(handler);
+        mPmicMonitor =
+            mInjector.getPmicMonitor(mPowerChangeListener,
+                    mThermalLevelListener.getThermalService(),
+                    mPowerThrottlingConfigData.pollingWindowMaxMillis,
+                    mPowerThrottlingConfigData.pollingWindowMinMillis);
 
+        mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
         mHandler.post(() -> {
             setDisplayData(powerData);
             loadOverrideData();
             start();
         });
+    }
 
+    @VisibleForTesting
+    PowerChangeListener getPowerChangeListener() {
+        return mPowerChangeListener;
     }
 
     @Override
@@ -114,6 +146,11 @@
     }
 
     @Override
+    float getCustomAnimationRate() {
+        return mCustomAnimationRateSec;
+    }
+
+    @Override
     void onDeviceConfigChanged() {
         mHandler.post(() -> {
             loadOverrideData();
@@ -134,6 +171,9 @@
         if (mPmicMonitor != null) {
             mPmicMonitor.shutdown();
         }
+        if (mThermalLevelListener != null) {
+            mThermalLevelListener.stop();
+        }
     }
 
     /**
@@ -144,11 +184,20 @@
         pw.println("  mCurrentAvgPowerConsumed=" + mCurrentAvgPowerConsumed);
         pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
         pw.println("  mCurrentThermalLevel=" + mCurrentThermalLevel);
+        pw.println("  mCurrentThermalLevelChanged=" + mCurrentThermalLevelChanged);
         pw.println("  mPowerThrottlingDataFromDDC=" + (mPowerThrottlingDataFromDDC == null ? "null"
                 : mPowerThrottlingDataFromDDC.toString()));
+        mThermalLevelListener.dump(pw);
         super.dump(pw);
     }
 
+    /**
+     * Updates current brightness, for power calculations.
+     */
+    public void updateCurrentBrightness(float currentBrightness) {
+        mCurrentBrightness = currentBrightness;
+    }
+
     private void recalculateActiveData() {
         if (mUniqueDisplayId == null || mDataId == null) {
             return;
@@ -156,17 +205,11 @@
         mPowerThrottlingDataActive = mPowerThrottlingDataOverride
                 .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
                         mPowerThrottlingDataFromDDC);
-        if (mPowerThrottlingDataActive != null) {
-            if (mPmicMonitor != null) {
-                mPmicMonitor.stop();
-                mPmicMonitor.start();
-            }
-        } else {
+        if (mPowerThrottlingDataActive == null) {
             if (mPmicMonitor != null) {
                 mPmicMonitor.stop();
             }
         }
-        recalculateBrightnessCap();
     }
 
     private void loadOverrideData() {
@@ -198,21 +241,57 @@
         if (mPowerThrottlingDataActive == null) {
             return;
         }
-        if (powerQuota > 0 && mCurrentAvgPowerConsumed > powerQuota) {
-            isActive = true;
-            // calculate new brightness Cap.
-            // Brightness has a linear relation to power-consumed.
-            targetBrightnessCap =
-                    (powerQuota / mCurrentAvgPowerConsumed) * PowerManager.BRIGHTNESS_MAX;
-            // Cap to lowest allowed brightness on device.
+        if (powerQuota > 0) {
+            if (BrightnessUtils.isValidBrightnessValue(mCurrentBrightness)
+                    && (mCurrentAvgPowerConsumed > powerQuota)) {
+                isActive = true;
+                // calculate new brightness Cap.
+                // Brightness has a linear relation to power-consumed.
+                targetBrightnessCap =
+                    (powerQuota / mCurrentAvgPowerConsumed) * mCurrentBrightness;
+            } else if (mCurrentThermalLevelChanged) {
+                if (mCurrentThermalLevel == Temperature.THROTTLING_NONE) {
+                    // reset pmic and remove the power-throttling cap.
+                    isActive = true;
+                    targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+                    mPmicMonitor.stop();
+                } else {
+                    isActive = true;
+                    // Since the thermal status has changed, we need to remove power-throttling cap.
+                    // Instead of recalculating and changing brightness again, adding flicker,
+                    // we will wait for the next pmic cycle to re-evaluate this value
+                    // make act on it, if needed.
+                    targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+                    if (mPmicMonitor.isStopped()) {
+                        mPmicMonitor.start();
+                    }
+                }
+            } else { // Current power consumed is under the quota.
+                isActive = true;
+                targetBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+            }
+        }
+
+        // Cap to lowest allowed brightness on device.
+        if (mPowerThrottlingConfigData != null) {
             targetBrightnessCap = Math.max(targetBrightnessCap,
-                    mPowerThrottlingConfigData.brightnessLowestCapAllowed);
+                                mPowerThrottlingConfigData.brightnessLowestCapAllowed);
         }
 
         if (mBrightnessCap != targetBrightnessCap || mIsActive != isActive) {
             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
+                    + " mCurrentThermalLevelChanged:" + mCurrentThermalLevelChanged
+                    + " mCurrentAvgPowerConsumed:" + mCurrentAvgPowerConsumed
+                    + " mCustomAnimationRateSec:" + mCustomAnimationRateSecDeviceConfig);
+            mCustomAnimationRateSec = mCustomAnimationRateSecDeviceConfig;
             mChangeListener.onChanged();
+        } else {
+            mCustomAnimationRateSec = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET;
         }
     }
 
@@ -234,6 +313,11 @@
 
     private void recalculatePowerQuotaChange(float avgPowerConsumed, int thermalStatus) {
         mHandler.post(() -> {
+            if (mCurrentThermalLevel != thermalStatus) {
+                mCurrentThermalLevelChanged = true;
+            } else {
+                mCurrentThermalLevelChanged = false;
+            }
             mCurrentThermalLevel = thermalStatus;
             mCurrentAvgPowerConsumed = avgPowerConsumed;
             recalculateBrightnessCap();
@@ -244,14 +328,107 @@
         if (mPowerThrottlingConfigData == null) {
             return;
         }
-        PowerChangeListener listener = (powerConsumed, thermalStatus) -> {
-            recalculatePowerQuotaChange(powerConsumed, thermalStatus);
-        };
-        mPmicMonitor =
-            mInjector.getPmicMonitor(listener, mPowerThrottlingConfigData.pollingWindowMillis);
+        if (mPowerThrottlingConfigData.pollingWindowMaxMillis
+                <= mPowerThrottlingConfigData.pollingWindowMinMillis) {
+            Slog.e(TAG, "Brightness power max polling window:"
+                    + mPowerThrottlingConfigData.pollingWindowMaxMillis
+                    + " msec, should be greater than brightness min polling window:"
+                    + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+            return;
+        }
+        if ((mPowerThrottlingConfigData.pollingWindowMaxMillis
+                % mPowerThrottlingConfigData.pollingWindowMinMillis) != 0) {
+            Slog.e(TAG, "Brightness power max polling window:"
+                    + mPowerThrottlingConfigData.pollingWindowMaxMillis
+                    + " msec, is not divisible by brightness min polling window:"
+                    + mPowerThrottlingConfigData.pollingWindowMinMillis + " msec.");
+            return;
+        }
+        mCustomAnimationRateSecDeviceConfig = mPowerThrottlingConfigData.customAnimationRateSec;
+        mThermalLevelListener.start();
+    }
+
+    private void activatePmicMonitor() {
+        if (!mPmicMonitor.isStopped()) {
+            return;
+        }
         mPmicMonitor.start();
     }
 
+    private void deactivatePmicMonitor(@Temperature.ThrottlingStatus int status) {
+        if (status != Temperature.THROTTLING_NONE) {
+            return;
+        }
+        if (mPmicMonitor.isStopped()) {
+            return;
+        }
+        mPmicMonitor.stop();
+    }
+
+    private final class ThermalLevelListener extends IThermalEventListener.Stub {
+        private final Handler mHandler;
+        private IThermalService mThermalService;
+        private boolean mStarted;
+
+        ThermalLevelListener(Handler handler) {
+            mHandler = handler;
+            mStarted = false;
+            mThermalService = IThermalService.Stub.asInterface(
+                    ServiceManager.getService(Context.THERMAL_SERVICE));
+        }
+
+        IThermalService getThermalService() {
+            return mThermalService;
+        }
+
+        void start() {
+            if (mStarted) {
+                return;
+            }
+            if (mThermalService == null) {
+                return;
+            }
+            try {
+                // TODO b/279114539 Try DISPLAY first and then fallback to SKIN.
+                mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN);
+                mStarted = true;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register thermal status listener", e);
+            }
+        }
+
+        @Override
+        public void notifyThrottling(Temperature temp) {
+            @Temperature.ThrottlingStatus int status = temp.getStatus();
+            if (status >= Temperature.THROTTLING_LIGHT) {
+                Slog.d(TAG, "Activating pmic monitor due to thermal state:" + status);
+                mHandler.post(() -> activatePmicMonitor());
+            } else {
+                if (!mPmicMonitor.isStopped()) {
+                    mHandler.post(() -> deactivatePmicMonitor(status));
+                }
+            }
+        }
+
+        void stop() {
+            if (!mStarted) {
+                return;
+            }
+            try {
+                mThermalService.unregisterThermalEventListener(this);
+                mStarted = false;
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to unregister thermal status listener", e);
+            }
+            mThermalService = null;
+        }
+
+        void dump(PrintWriter writer) {
+            writer.println("  ThermalLevelObserver:");
+            writer.println("    mStarted: " + mStarted);
+        }
+    }
+
     public interface PowerData {
         @NonNull
         String getUniqueDisplayId();
@@ -279,8 +456,12 @@
 
     @VisibleForTesting
     static class Injector {
-        PmicMonitor getPmicMonitor(PowerChangeListener listener, int pollingTime) {
-            return new PmicMonitor(listener, pollingTime);
+        PmicMonitor getPmicMonitor(PowerChangeListener powerChangeListener,
+                                   IThermalService thermalService,
+                                   int pollingMaxTimeMillis,
+                                   int pollingMinTimeMillis) {
+            return new PmicMonitor(powerChangeListener, thermalService, pollingMaxTimeMillis,
+                                        pollingMinTimeMillis);
         }
 
         DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
diff --git a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
index 26784f23..355f4fe 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/PmicMonitor.java
@@ -18,15 +18,12 @@
 
 import static com.android.server.display.brightness.clamper.BrightnessPowerClamper.PowerChangeListener;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.hardware.power.stats.EnergyConsumer;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.IThermalService;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.Temperature;
 import android.power.PowerStatsInternal;
 import android.util.IntArray;
@@ -51,25 +48,30 @@
 
     // The executor to periodically monitor the display power.
     private final ScheduledExecutorService mExecutor;
-    @NonNull
-    private final PowerChangeListener mPowerChangeListener;
-    private final long mPowerMonitorPeriodConfigSecs;
+    private final long mPowerMonitorPeriodConfigMillis;
     private final PowerStatsInternal mPowerStatsInternal;
     @VisibleForTesting final IThermalService mThermalService;
+    @VisibleForTesting PowerChangeListener mPowerChangeListener;
     private ScheduledFuture<?> mPmicMonitorFuture;
     private float mLastEnergyConsumed = 0;
-    private float mCurrentAvgPower = 0;
+    private float mCurrentTotalAvgPower = 0;
     private Temperature mCurrentTemperature;
     private long mCurrentTimestampMillis = 0;
+    private float[] mAvgPowerCircularList;
+    private int mPowerListStart = 0;
+    private int mPowerListEnd = 0;
 
-    PmicMonitor(PowerChangeListener listener, int powerMonitorPeriodConfigSecs) {
+    PmicMonitor(PowerChangeListener listener,
+                IThermalService thermalService,
+                int pollingMaxTimeMillis,
+                int pollingMinTimeMillis) {
         mPowerChangeListener = listener;
+        mThermalService = thermalService;
         mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
-        mThermalService = IThermalService.Stub.asInterface(
-                ServiceManager.getService(Context.THERMAL_SERVICE));
+        mAvgPowerCircularList = new float[pollingMaxTimeMillis / pollingMinTimeMillis];
         // start a periodic worker thread.
         mExecutor = Executors.newSingleThreadScheduledExecutor();
-        mPowerMonitorPeriodConfigSecs = (long) powerMonitorPeriodConfigSecs;
+        mPowerMonitorPeriodConfigMillis = pollingMinTimeMillis;
     }
 
     @Nullable
@@ -141,12 +143,28 @@
 
         // capture thermal state.
         Temperature displayTemperature = getDisplayTemperature();
-        mCurrentAvgPower = currentPower;
+        boolean isBufferFull = false;
+        mAvgPowerCircularList[mPowerListEnd] = currentPower;
+        mCurrentTotalAvgPower += currentPower;
+        mPowerListEnd =
+            (mPowerListEnd + 1) % mAvgPowerCircularList.length;
+        if (mPowerListStart == mPowerListEnd) {
+            isBufferFull = true;
+        }
+
         mCurrentTemperature = displayTemperature;
         mLastEnergyConsumed = displayResults[0].energyUWs;
         mCurrentTimestampMillis = displayResults[0].timestampMs;
-        if (mCurrentTemperature != null) {
-            mPowerChangeListener.onChanged(mCurrentAvgPower, mCurrentTemperature.getStatus());
+
+        if (mCurrentTemperature != null && isBufferFull) {
+            mPowerChangeListener.onChanged(mCurrentTotalAvgPower / mAvgPowerCircularList.length,
+                                            mCurrentTemperature.getStatus());
+        }
+
+        // average power long-term list is full, reset values for next cycle.
+        if (isBufferFull) {
+            mCurrentTotalAvgPower = mCurrentTotalAvgPower - mAvgPowerCircularList[mPowerListStart];
+            mPowerListStart = (mPowerListStart + 1) % mAvgPowerCircularList.length;
         }
     }
 
@@ -165,11 +183,11 @@
         if (mPmicMonitorFuture == null) {
             mPmicMonitorFuture = mExecutor.scheduleAtFixedRate(
                                     this::capturePeriodicDisplayPower,
-                                    mPowerMonitorPeriodConfigSecs,
-                                    mPowerMonitorPeriodConfigSecs,
-                                    TimeUnit.SECONDS);
+                                    mPowerMonitorPeriodConfigMillis,
+                                    mPowerMonitorPeriodConfigMillis,
+                                    TimeUnit.MILLISECONDS);
         } else {
-            Slog.e(TAG, "already scheduled, stop() called before start.");
+            Slog.e(TAG, "PMIC already scheduled, stop() called before start.");
         }
     }
 
@@ -184,6 +202,23 @@
     }
 
     /**
+     * Updates PMIC configuration.
+     */
+    public void updateConfiguration() {
+        if (mPmicMonitorFuture != null) {
+            mPmicMonitorFuture.cancel(true);
+            mPmicMonitorFuture = null;
+        }
+    }
+
+    /**
+     * Returns if PMIC monitor is stopped.
+     */
+    public boolean isStopped() {
+        return mPmicMonitorFuture == null;
+    }
+
+    /**
      * Shutdown power IC service and worker thread.
      */
     public void shutdown() {
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
index 1db9bbe..91c985830 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategy.java
@@ -100,6 +100,7 @@
         writer.println("AutoBrightnessFallbackStrategy:");
         writer.println("  mLeadDisplayId=" + mLeadDisplayId);
         writer.println("  mIsDisplayEnabled=" + mIsDisplayEnabled);
+        writer.println("");
         if (mScreenOffBrightnessSensorController != null) {
             IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
             mScreenOffBrightnessSensorController.dump(ipw);
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 706b4a6..bf01f2d 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -131,13 +131,14 @@
      */
     public void setAutoBrightnessState(int targetDisplayState,
             boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
-            float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
+            boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness,
+            boolean userSetBrightnessChanged) {
         // We are still in the process of updating the power state, so there's no need to trigger
         // an update again
-        switchMode(targetDisplayState, /* sendUpdate= */ false);
+        switchMode(targetDisplayState, useNormalBrightnessForDoze, policy, /* sendUpdate= */ false);
 
-        // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
-        // only be enabled if the config allows it
+        // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness
+        // should only be enabled if the config allows it
         final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
                 && policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
 
@@ -157,7 +158,8 @@
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness,
-                policy, targetDisplayState, mBrightnessConfiguration, autoBrightnessState);
+                policy, targetDisplayState, useNormalBrightnessForDoze, mBrightnessConfiguration,
+                autoBrightnessState);
         mIsConfigured = true;
     }
 
@@ -344,6 +346,8 @@
                     strategySelectionNotifyRequest.getSelectedDisplayBrightnessStrategy()
                             .getReason(),
                     strategySelectionNotifyRequest.getDisplayPowerRequest().policy,
+                    strategySelectionNotifyRequest.getDisplayPowerRequest()
+                            .useNormalBrightnessForDoze,
                     strategySelectionNotifyRequest.getLastUserSetScreenBrightness(),
                     strategySelectionNotifyRequest.isUserSetBrightnessChanged());
         }
@@ -469,7 +473,8 @@
     @VisibleForTesting
     void accommodateUserBrightnessChanges(boolean userSetBrightnessChanged,
             float lastUserSetScreenBrightness, int policy, int displayState,
-            BrightnessConfiguration brightnessConfiguration, int autoBrightnessState) {
+            boolean useNormalBrightnessForDoze, BrightnessConfiguration brightnessConfiguration,
+            int autoBrightnessState) {
         // Update the pending auto-brightness adjustments if any. This typically checks and adjusts
         // the state of the class if the user moves the brightness slider and has settled to a
         // different value
@@ -485,8 +490,12 @@
             mAutomaticBrightnessController.configure(autoBrightnessState,
                     brightnessConfiguration,
                     lastUserSetScreenBrightness,
-                    userSetBrightnessChanged, autoBrightnessAdjustment,
-                    mAutoBrightnessAdjustmentChanged, policy, displayState,
+                    userSetBrightnessChanged,
+                    autoBrightnessAdjustment,
+                    mAutoBrightnessAdjustmentChanged,
+                    policy,
+                    displayState,
+                    useNormalBrightnessForDoze,
                     mShouldResetShortTermModel);
             mShouldResetShortTermModel = false;
             // We take note if the user brightness point is still being used in the current
@@ -494,11 +503,18 @@
             mIsShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints();
         }
     }
-    private void switchMode(int state, boolean sendUpdate) {
+
+    private void switchMode(int state, boolean useNormalBrightnessForDoze, int policy,
+            boolean sendUpdate) {
         if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
                 && mAutomaticBrightnessController != null
                 && !mAutomaticBrightnessController.isInIdleMode()) {
-            mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+
+            boolean shouldUseDozeMode =
+                    mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()
+                            ? !useNormalBrightnessForDoze && policy == POLICY_DOZE
+                            : Display.isDozeState(state);
+            mAutomaticBrightnessController.switchMode(shouldUseDozeMode
                     ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT, sendUpdate);
         }
     }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
index c87872c..5e79565 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2.java
@@ -108,9 +108,10 @@
      */
     public void setAutoBrightnessState(int targetDisplayState,
             boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
-            float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
-        // If the policy is POLICY_DOZE and the display state is STATE_ON, auto-brightness should
-        // only be enabled if the config allows it
+            boolean useNormalBrightnessForDoze, float lastUserSetScreenBrightness,
+            boolean userSetBrightnessChanged) {
+        // If the policy is POLICY_DOZE and the display state is not STATE_OFF, auto-brightness
+        // should only be enabled if the config allows it
         final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig
                 && policy == POLICY_DOZE && targetDisplayState != Display.STATE_OFF;
 
@@ -120,7 +121,7 @@
                 && brightnessReason != BrightnessReason.REASON_OVERRIDE
                 && mAutomaticBrightnessController != null;
         mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness()
-                && !((targetDisplayState == Display.STATE_ON  && policy != POLICY_DOZE)
+                && !((targetDisplayState == Display.STATE_ON && policy != POLICY_DOZE)
                 || autoBrightnessEnabledInDoze);
         final int autoBrightnessState = mIsAutoBrightnessEnabled
                 && brightnessReason != BrightnessReason.REASON_FOLLOWER
@@ -130,7 +131,8 @@
                         : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
 
         accommodateUserBrightnessChanges(userSetBrightnessChanged, lastUserSetScreenBrightness,
-                policy, targetDisplayState, mBrightnessConfiguration, autoBrightnessState);
+                policy, targetDisplayState, useNormalBrightnessForDoze, mBrightnessConfiguration,
+                autoBrightnessState);
     }
 
     public boolean isAutoBrightnessEnabled() {
@@ -367,7 +369,8 @@
     @VisibleForTesting
     void accommodateUserBrightnessChanges(boolean userSetBrightnessChanged,
             float lastUserSetScreenBrightness, int policy, int displayState,
-            BrightnessConfiguration brightnessConfiguration, int autoBrightnessState) {
+            boolean useNormalBrightnessForDoze, BrightnessConfiguration brightnessConfiguration,
+            int autoBrightnessState) {
         // Update the pending auto-brightness adjustments if any. This typically checks and adjusts
         // the state of the class if the user moves the brightness slider and has settled to a
         // different value
@@ -384,7 +387,10 @@
                     brightnessConfiguration,
                     lastUserSetScreenBrightness,
                     userSetBrightnessChanged, autoBrightnessAdjustment,
-                    mAutoBrightnessAdjustmentChanged, policy, displayState,
+                    mAutoBrightnessAdjustmentChanged,
+                    policy,
+                    displayState,
+                    useNormalBrightnessForDoze,
                     mShouldResetShortTermModel);
             mShouldResetShortTermModel = false;
             // We take note if the user brightness point is still being used in the current
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 e1934b0..69b67c8 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -183,15 +183,22 @@
             Flags::offloadDozeOverrideHoldsWakelock
     );
 
-    private final FlagState mOffloadSessionCancelBlockScreenOn =
-            new FlagState(
-                    Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON,
-                    Flags::offloadSessionCancelBlockScreenOn);
+    private final FlagState mOffloadSessionCancelBlockScreenOn = new FlagState(
+            Flags.FLAG_OFFLOAD_SESSION_CANCEL_BLOCK_SCREEN_ON,
+            Flags::offloadSessionCancelBlockScreenOn);
 
-    private final FlagState mNewHdrBrightnessModifier =
-            new FlagState(
-                    Flags.FLAG_NEW_HDR_BRIGHTNESS_MODIFIER,
-                    Flags::newHdrBrightnessModifier);
+    private final FlagState mNewHdrBrightnessModifier = new FlagState(
+            Flags.FLAG_NEW_HDR_BRIGHTNESS_MODIFIER,
+            Flags::newHdrBrightnessModifier);
+
+    private final FlagState mIdleScreenConfigInSubscribingLightSensor = new FlagState(
+            Flags.FLAG_IDLE_SCREEN_CONFIG_IN_SUBSCRIBING_LIGHT_SENSOR,
+            Flags::idleScreenConfigInSubscribingLightSensor);
+
+    private final FlagState mNormalBrightnessForDozeParameter = new FlagState(
+            Flags.FLAG_NORMAL_BRIGHTNESS_FOR_DOZE_PARAMETER,
+            Flags::normalBrightnessForDozeParameter
+    );
 
     /**
      * @return {@code true} if 'port' is allowed in display layout configuration file.
@@ -393,11 +400,27 @@
     }
 
     /**
+     * @return Whether the useDozeBrightness parameter should be used
+     */
+    public boolean isNormalBrightnessForDozeParameterEnabled() {
+        return mNormalBrightnessForDozeParameter.isEnabled();
+    }
+
+     /**
+      * @return {@code true} if idle timer refresh rate config is accounted for while subscribing to
+      * the light sensor
+      */
+    public boolean isIdleScreenConfigInSubscribingLightSensorEnabled() {
+        return mIdleScreenConfigInSubscribingLightSensor.isEnabled();
+    }
+
+    /**
      * dumps all flagstates
      * @param pw printWriter
      */
     public void dump(PrintWriter pw) {
         pw.println("DisplayManagerFlags:");
+        pw.println("--------------------");
         pw.println(" " + mAdaptiveToneImprovements1);
         pw.println(" " + mAdaptiveToneImprovements2);
         pw.println(" " + mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState);
@@ -431,6 +454,8 @@
         pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
         pw.println(" " + mOffloadSessionCancelBlockScreenOn);
         pw.println(" " + mNewHdrBrightnessModifier);
+        pw.println(" " + mNormalBrightnessForDozeParameter);
+        pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
     }
 
     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 ac5f97f..da5063a 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
@@ -250,6 +250,13 @@
     namespace: "display_manager"
     description: "Define doze brightness in the float scale [0, 1]."
     bug: "343796384"
+}
+
+flag {
+    name: "normal_brightness_for_doze_parameter"
+    namespace: "wear_frameworks"
+    description: "Feature flag for the parameter specifying whether brightness should be adjusted while dozing."
+    bug: "343283838"
     is_fixed_read_only: true
 }
 
@@ -323,3 +330,14 @@
     bug: "331275392"
     is_fixed_read_only: true
 }
+
+flag {
+    name: "idle_screen_config_in_subscribing_light_sensor"
+    namespace: "display_manager"
+    description: "Account for Idle screen refresh rate configs while subscribing to light sensor"
+    bug: "358019330"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
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 5e471c8..18e0d6e 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -483,6 +483,7 @@
                 /* attemptReadFromFeatureParams= */ true);
             mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
                 /* attemptReadFromFeatureParams= */ true);
+            mBrightnessObserver.loadIdleScreenRefreshRateConfigs(displayDeviceConfig);
             mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
             mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
         }
@@ -577,7 +578,8 @@
      * @param pw The stream to dump information to.
      */
     public void dump(PrintWriter pw) {
-        pw.println("DisplayModeDirector");
+        pw.println("DisplayModeDirector:");
+        pw.println("--------------------");
         synchronized (mLock) {
             pw.println("  mSupportedModesByDisplay:");
             for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
@@ -1499,10 +1501,18 @@
         }
 
         private void updateLayoutLimitedFrameRate(int displayId, @Nullable DisplayInfo info) {
-            Vote vote = info != null && info.layoutLimitedRefreshRate != null
-                    ? Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min,
-                    info.layoutLimitedRefreshRate.max) : null;
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, vote);
+            Vote refreshRateVote = null;
+            Vote frameRateVote = null;
+            if (info != null && info.layoutLimitedRefreshRate != null) {
+                refreshRateVote = Vote.forPhysicalRefreshRates(info.layoutLimitedRefreshRate.min,
+                        info.layoutLimitedRefreshRate.max);
+                frameRateVote = Vote.forRenderFrameRates(info.layoutLimitedRefreshRate.min,
+                        info.layoutLimitedRefreshRate.max);
+            }
+            mVotesStorage.updateVote(
+                    displayId, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE, refreshRateVote);
+            mVotesStorage.updateVote(
+                    displayId, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE, frameRateVote);
         }
 
         private void removeUserSettingDisplayPreferredSize(int displayId) {
@@ -1592,6 +1602,12 @@
                                 - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
                             SYNCHRONIZED_REFRESH_RATE_TARGET
                                 + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+            mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE,
+                    Vote.forRenderFrameRates(
+                            SYNCHRONIZED_REFRESH_RATE_TARGET
+                                    - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+                            SYNCHRONIZED_REFRESH_RATE_TARGET
+                                    + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
         }
 
         private void removeDisplaysSynchronizedPeakRefreshRate(final int displayId) {
@@ -1603,11 +1619,12 @@
                     return;
                 }
                 mExternalDisplaysConnected.remove(displayId);
-                if (mExternalDisplaysConnected.size() != 0) {
+                if (!mExternalDisplaysConnected.isEmpty()) {
                     return;
                 }
             }
             mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE, null);
+            mVotesStorage.updateGlobalVote(Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE, null);
         }
 
         private void updateDisplayDeviceConfig(int displayId) {
@@ -1745,6 +1762,10 @@
         private SparseArray<RefreshRateRange> mHighZoneRefreshRateForThermals;
         private int mRefreshRateInHighZone;
 
+        @Nullable
+        private List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+                mIdleScreenRefreshRateTimeoutLuxThresholdPoints;
+
         @GuardedBy("mLock")
         private @Temperature.ThrottlingStatus int mThermalStatus = Temperature.THROTTLING_NONE;
 
@@ -1758,6 +1779,24 @@
             mRefreshRateInHighZone = context.getResources().getInteger(
                     R.integer.config_fixedRefreshRateInHighZone);
             mVsyncLowLightBlockingVoteEnabled = flags.isVsyncLowLightVoteEnabled();
+            loadIdleScreenRefreshRateConfigs(/* displayDeviceConfig= */ null);
+        }
+
+        private void loadIdleScreenRefreshRateConfigs(DisplayDeviceConfig displayDeviceConfig) {
+            synchronized (mLock) {
+                if (!mDisplayManagerFlags.isIdleScreenConfigInSubscribingLightSensorEnabled()
+                        || displayDeviceConfig == null || displayDeviceConfig
+                        .getIdleScreenRefreshRateTimeoutLuxThresholdPoint().isEmpty()) {
+                    // Setting this to null will let surface flinger know that the idle timer is not
+                    // configured in the display configs
+                    mIdleScreenRefreshRateConfig = null;
+                    mIdleScreenRefreshRateTimeoutLuxThresholdPoints = null;
+                    return;
+                }
+                mIdleScreenRefreshRateTimeoutLuxThresholdPoints =
+                        displayDeviceConfig
+                                .getIdleScreenRefreshRateTimeoutLuxThresholdPoint();
+            }
         }
 
         /**
@@ -1806,11 +1845,19 @@
             return mRefreshRateInLowZone;
         }
 
+        @Nullable
         @VisibleForTesting
         IdleScreenRefreshRateConfig getIdleScreenRefreshRateConfig() {
             return mIdleScreenRefreshRateConfig;
         }
 
+        @Nullable
+        @VisibleForTesting
+        List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+                getIdleScreenRefreshRateTimeoutLuxThresholdPoints() {
+            return mIdleScreenRefreshRateTimeoutLuxThresholdPoints;
+        }
+
         private void loadLowBrightnessThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
                 boolean attemptReadFromFeatureParams) {
             loadRefreshRateInHighZone(displayDeviceConfig, attemptReadFromFeatureParams);
@@ -2205,12 +2252,11 @@
                 mShouldObserveAmbientHighChange = false;
             }
 
-            if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
+            if (shouldRegisterLightSensor()) {
                 Sensor lightSensor = getLightSensor();
 
                 if (lightSensor != null && lightSensor != mLightSensor) {
                     final Resources res = mContext.getResources();
-
                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
                     mLightSensor = lightSensor;
                 }
@@ -2436,8 +2482,8 @@
             }
 
             boolean registerForThermals = false;
-            if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
-                     && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
+            if (shouldRegisterLightSensor() && isDeviceActive() && !mLowPowerModeEnabled
+                    && mRefreshRateChangeable) {
                 registerLightSensor();
                 registerForThermals = mLowZoneRefreshRateForThermals != null
                         || mHighZoneRefreshRateForThermals != null;
@@ -2456,6 +2502,17 @@
             }
         }
 
+        private boolean shouldRegisterLightSensor() {
+            return mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange
+                    || isIdleScreenRefreshRateConfigDefined();
+        }
+
+        private boolean isIdleScreenRefreshRateConfigDefined() {
+            return mDisplayManagerFlags.isIdleScreenConfigInSubscribingLightSensorEnabled()
+                    && mIdleScreenRefreshRateTimeoutLuxThresholdPoints != null
+                    && !mIdleScreenRefreshRateTimeoutLuxThresholdPoints.isEmpty();
+        }
+
         private void registerLightSensor() {
             if (mRegisteredLightSensor == mLightSensor) {
                 return;
@@ -2556,7 +2613,6 @@
                     // is interrupted by a new sensor event.
                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                 }
-
                 if (mDisplayManagerFlags.isIdleScreenRefreshRateTimeoutEnabled()) {
                     updateIdleScreenRefreshRate(mAmbientLux);
                 }
@@ -2621,24 +2677,15 @@
         }
 
         private void updateIdleScreenRefreshRate(float ambientLux) {
-            List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
-                    idleScreenRefreshRateTimeoutLuxThresholdPoints;
-            synchronized (mLock) {
-                if (mDefaultDisplayDeviceConfig == null || mDefaultDisplayDeviceConfig
-                        .getIdleScreenRefreshRateTimeoutLuxThresholdPoint().isEmpty()) {
-                    // Setting this to null will let surface flinger know that the idle timer is not
-                    // configured in the display configs
-                    mIdleScreenRefreshRateConfig = null;
-                    return;
-                }
-
-                idleScreenRefreshRateTimeoutLuxThresholdPoints =
-                        mDefaultDisplayDeviceConfig
-                                .getIdleScreenRefreshRateTimeoutLuxThresholdPoint();
+            if (mIdleScreenRefreshRateTimeoutLuxThresholdPoints == null
+                    || mIdleScreenRefreshRateTimeoutLuxThresholdPoints.isEmpty()) {
+                mIdleScreenRefreshRateConfig = null;
+                return;
             }
+
             int newTimeout = -1;
             for (IdleScreenRefreshRateTimeoutLuxThresholdPoint point :
-                    idleScreenRefreshRateTimeoutLuxThresholdPoints) {
+                    mIdleScreenRefreshRateTimeoutLuxThresholdPoints) {
                 int newLux = point.getLux().intValue();
                 if (newLux <= ambientLux) {
                     newTimeout = point.getTimeout().intValue();
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 7cbdd13..459f9a6e 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -92,48 +92,58 @@
     // render rate vote can still apply
     int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 9;
 
-    // Restrict all displays to 60Hz when external display is connected. It votes [59Hz, 61Hz].
+    // Restrict all displays physical refresh rate to 60Hz when external display is connected.
+    // It votes [59Hz, 61Hz].
     int PRIORITY_SYNCHRONIZED_REFRESH_RATE = 10;
 
+    // PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE has a higher priority than
+    // PRIORITY_SYNCHRONIZED_REFRESH_RATE and will limit render rate to [59Hz, 61Hz].
+    // In case physical refresh rate vote discarded (due to physical refresh rate not supported),
+    // render rate vote can still apply.
+    int PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE = 11;
+
     // Restrict displays max available resolution and refresh rates. It votes [0, LIMIT]
-    int PRIORITY_LIMIT_MODE = 11;
+    int PRIORITY_LIMIT_MODE = 12;
 
     // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
     // rate to max value (same as for PRIORITY_UDFPS) on lock screen
-    int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 12;
+    int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 13;
 
     // For concurrent displays we want to limit refresh rate on all displays
-    int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 13;
+    int PRIORITY_LAYOUT_LIMITED_REFRESH_RATE = 14;
+
+    // For concurrent displays we want to limit refresh rate on all displays
+    int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 15;
 
     // For internal application to limit display modes to specific ids
-    int PRIORITY_SYSTEM_REQUESTED_MODES = 14;
+    int PRIORITY_SYSTEM_REQUESTED_MODES = 16;
 
     // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
     // Settings.Global.LOW_POWER_MODE is on.
     // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
     // higher priority votes), render rate limit can still apply
-    int PRIORITY_LOW_POWER_MODE_MODES = 15;
+    int PRIORITY_LOW_POWER_MODE_MODES = 17;
 
     // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
     // Settings.Global.LOW_POWER_MODE is on.
-    int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 16;
+    int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 18;
 
     // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
     // higher priority voters' result is a range, it will fix the rate to a single choice.
     // It's used to avoid refresh rate switches in certain conditions which may result in the
     // user seeing the display flickering when the switches occur.
-    int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 17;
+    int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 19;
 
     // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
-    int PRIORITY_SKIN_TEMPERATURE = 18;
+    int PRIORITY_SKIN_TEMPERATURE = 20;
 
     // The proximity sensor needs the refresh rate to be locked in order to function, so this is
     // set to a high priority.
-    int PRIORITY_PROXIMITY = 19;
+    int PRIORITY_PROXIMITY = 21;
 
     // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
     // to function, so this needs to be the highest priority of all votes.
-    int PRIORITY_UDFPS = 20;
+    int PRIORITY_UDFPS = 22;
 
     @IntDef(prefix = { "PRIORITY_" }, value = {
             PRIORITY_DEFAULT_RENDER_FRAME_RATE,
@@ -147,8 +157,10 @@
             PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
             PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
             PRIORITY_SYNCHRONIZED_REFRESH_RATE,
+            PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE,
             PRIORITY_LIMIT_MODE,
             PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE,
+            PRIORITY_LAYOUT_LIMITED_REFRESH_RATE,
             PRIORITY_LAYOUT_LIMITED_FRAME_RATE,
             PRIORITY_SYSTEM_REQUESTED_MODES,
             PRIORITY_LOW_POWER_MODE_MODES,
@@ -267,12 +279,16 @@
                 return "PRIORITY_LIMIT_MODE";
             case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
                 return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
+            case PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE:
+                return "PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE";
             case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
                 return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
             case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
                 return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
             case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
                 return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
+            case PRIORITY_LAYOUT_LIMITED_REFRESH_RATE:
+                return "PRIORITY_LAYOUT_LIMITED_REFRESH_RATE";
             case PRIORITY_LAYOUT_LIMITED_FRAME_RATE:
                 return "PRIORITY_LAYOUT_LIMITED_FRAME_RATE";
             case PRIORITY_SYSTEM_REQUESTED_MODES:
diff --git a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
index 280a7e1..8a8440b 100644
--- a/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
+++ b/services/core/java/com/android/server/display/notifications/DisplayNotificationManager.java
@@ -26,6 +26,7 @@
 import android.app.NotificationManager;
 import android.content.Context;
 import android.content.res.Resources;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -197,7 +198,8 @@
             return;
         }
 
-        mNotificationManager.cancel(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID);
+        mNotificationManager.cancelAsUser(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID,
+                UserHandle.CURRENT);
     }
 
     /**
@@ -210,8 +212,8 @@
             return;
         }
 
-        mNotificationManager.notify(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID,
-                notification);
+        mNotificationManager.notifyAsUser(DISPLAY_NOTIFICATION_TAG, DISPLAY_NOTIFICATION_ID,
+                notification, UserHandle.CURRENT);
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/state/DisplayStateController.java b/services/core/java/com/android/server/display/state/DisplayStateController.java
index 21bb208..dba6874 100644
--- a/services/core/java/com/android/server/display/state/DisplayStateController.java
+++ b/services/core/java/com/android/server/display/state/DisplayStateController.java
@@ -114,9 +114,9 @@
      *
      * @param pw The PrintWriter used to dump the state.
      */
-    public void dumpsys(PrintWriter pw) {
-        pw.println();
+    public void dump(PrintWriter pw) {
         pw.println("DisplayStateController:");
+        pw.println("-----------------------");
         pw.println("  mPerformScreenOffTransition:" + mPerformScreenOffTransition);
         pw.println("  mDozeStateOverride=" + mDozeStateOverride);
 
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 7ea576d..49c45a7 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -427,7 +427,8 @@
      *      The writer used to dump the state.
      */
     public void dump(PrintWriter writer) {
-        writer.println("DisplayWhiteBalanceController");
+        writer.println("DisplayWhiteBalanceController:");
+        writer.println("------------------------------");
         writer.println("  mLoggingEnabled=" + mLoggingEnabled);
         writer.println("  mEnabled=" + mEnabled);
         writer.println("  mStrongModeEnabled=" + mStrongModeEnabled);
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
index 0efb749..a5755ac 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -23,7 +23,6 @@
 import android.os.Message;
 import android.util.Slog;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
@@ -129,7 +128,8 @@
      *      The writer used to dump the state.
      */
     public void dump(PrintWriter writer) {
-        writer.println("DisplayWhiteBalanceSettings");
+        writer.println("DisplayWhiteBalanceSettings:");
+        writer.println("----------------------------");
         writer.println("  mLoggingEnabled=" + mLoggingEnabled);
         writer.println("  mContext=" + mContext);
         writer.println("  mHandler=" + mHandler);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index a3b77e8..19305de 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -543,12 +543,14 @@
     }
 
     private void startDozingInternal(IBinder token, int screenState,
-            @Display.StateReason int reason, float screenBrightnessFloat, int screenBrightnessInt) {
+            @Display.StateReason int reason, float screenBrightnessFloat, int screenBrightnessInt,
+            boolean useNormalBrightnessForDoze) {
         Slog.d(TAG, "Dream requested to start dozing: " + token
                 + ", screenState=" + Display.stateToString(screenState)
                 + ", reason=" + Display.stateReasonToString(reason)
                 + ", screenBrightnessFloat=" + screenBrightnessFloat
-                + ", screenBrightnessInt=" + screenBrightnessInt);
+                + ", screenBrightnessInt=" + screenBrightnessInt
+                + ", useNormalBrightnessForDoze=" + useNormalBrightnessForDoze);
 
         synchronized (mLock) {
             if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.canDoze) {
@@ -556,7 +558,8 @@
                 mCurrentDream.dozeScreenBrightness = screenBrightnessInt;
                 mCurrentDream.dozeScreenBrightnessFloat = screenBrightnessFloat;
                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
-                        screenState, reason, screenBrightnessFloat, screenBrightnessInt);
+                        screenState, reason, screenBrightnessFloat, screenBrightnessInt,
+                        useNormalBrightnessForDoze);
                 if (!mCurrentDream.isDozing) {
                     mCurrentDream.isDozing = true;
                     mDozeWakeLock.acquire();
@@ -578,7 +581,8 @@
                         Display.STATE_UNKNOWN,
                         Display.STATE_REASON_DREAM_MANAGER,
                         PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                        PowerManager.BRIGHTNESS_DEFAULT);
+                        PowerManager.BRIGHTNESS_DEFAULT,
+                        /* useNormalBrightnessForDoze= */ false);
             }
         }
     }
@@ -1098,7 +1102,8 @@
         @Override // Binder call
         public void startDozing(
                 IBinder token, int screenState, @Display.StateReason int reason,
-                float screenBrightnessFloat, int screeBrightnessInt) {
+                float screenBrightnessFloat, int screeBrightnessInt,
+                boolean useNormalBrightnessForDoze) {
             // Requires no permission, called by Dream from an arbitrary process.
             if (token == null) {
                 throw new IllegalArgumentException("token must not be null");
@@ -1107,7 +1112,7 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 startDozingInternal(token, screenState, reason, screenBrightnessFloat,
-                        screeBrightnessInt);
+                        screeBrightnessInt, useNormalBrightnessForDoze);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1116,7 +1121,8 @@
         @Override // Binder call
         public void startDozingOneway(
                 IBinder token, int screenState, @Display.StateReason int reason,
-                float screenBrightnessFloat, int screeBrightnessInt) {
+                float screenBrightnessFloat, int screeBrightnessInt,
+                boolean useNormalBrightnessForDoze) {
             // Requires no permission, called by Dream from an arbitrary process.
             if (token == null) {
                 throw new IllegalArgumentException("token must not be null");
@@ -1125,7 +1131,7 @@
             final long ident = Binder.clearCallingIdentity();
             try {
                 startDozingInternal(token, screenState, reason, screenBrightnessFloat,
-                        screeBrightnessInt);
+                        screeBrightnessInt, useNormalBrightnessForDoze);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index c361aee..649678c 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -45,3 +45,14 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "backstage_power"
+    name: "consolidate_battery_change_events"
+    description: "Optimize battery status updates by delivering only the most recent battery information"
+    bug: "361334584"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 154710f..4b85217 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -116,11 +116,11 @@
     private boolean mWasActiveSourceSetToConnectedDevice = false;
 
     @VisibleForTesting
-    protected boolean getWasActiveSourceSetToConnectedDevice() {
+    protected boolean getWasActivePathSetToConnectedDevice() {
         return mWasActiveSourceSetToConnectedDevice;
     }
 
-    protected void setWasActiveSourceSetToConnectedDevice(
+    protected void setWasActivePathSetToConnectedDevice(
             boolean wasActiveSourceSetToConnectedDevice) {
         mWasActiveSourceSetToConnectedDevice = wasActiveSourceSetToConnectedDevice;
     }
@@ -404,6 +404,15 @@
         }
     }
 
+    @Override
+    void setActivePath(int path) {
+        super.setActivePath(path);
+        if (path != Constants.INVALID_PHYSICAL_ADDRESS
+                && path != Constants.TV_PHYSICAL_ADDRESS) {
+            setWasActivePathSetToConnectedDevice(true);
+        }
+    }
+
     @ServiceThreadOnly
     void updateActiveInput(int path, boolean notifyInputChange) {
         assertRunOnServiceThread();
@@ -512,7 +521,6 @@
                 || info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
             mService.getHdmiCecNetwork().updateDevicePowerStatus(logicalAddress,
                     HdmiControlManager.POWER_STATUS_ON);
-            setWasActiveSourceSetToConnectedDevice(true);
             ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
             ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
         } else {
@@ -528,16 +536,16 @@
     protected int handleStandby(HdmiCecMessage message) {
         assertRunOnServiceThread();
 
-        // If a device has previously asserted the active source status, ignore <Standby> from
-        // non-active source.
-        if (getWasActiveSourceSetToConnectedDevice()
+        // If the TV has previously changed the active path, ignore <Standby> from non-active
+        // source.
+        if (getWasActivePathSetToConnectedDevice()
                 && getActiveSource().logicalAddress != message.getSource()) {
             Slog.d(TAG, "<Standby> was not sent by the current active source, ignoring."
                     + " Current active source has logical address "
                     + getActiveSource().logicalAddress);
             return Constants.HANDLED;
         }
-        setWasActiveSourceSetToConnectedDevice(false);
+        setWasActivePathSetToConnectedDevice(false);
         return super.handleStandby(message);
     }
 
@@ -1225,11 +1233,15 @@
         boolean avrSystemAudioMode = HdmiUtils.parseCommandParamSystemAudioStatus(message);
         // Set System Audio Mode according to TV's settings.
         // Handle <System Audio Mode Status> here only when
-        // SystemAudioAutoInitiationAction timeout
+        // SystemAudioAutoInitiationAction timeout.
+        // If AVR reports SAM on and it is in standby, the action SystemAudioActionFromTv
+        // triggers a <SAM Request> that will wake-up the AVR.
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr == null) {
             setSystemAudioMode(false);
-        } else if (avrSystemAudioMode != tvSystemAudioMode) {
+        } else if (avrSystemAudioMode != tvSystemAudioMode
+                    || (avrSystemAudioMode && avr.getDevicePowerStatus()
+                        == HdmiControlManager.POWER_STATUS_STANDBY)) {
             addAndStartAction(new SystemAudioActionFromTv(this, avr.getLogicalAddress(),
                     tvSystemAudioMode, null));
         } else {
@@ -1505,7 +1517,7 @@
             invokeStandbyCompletedCallback(callback);
             return;
         }
-        setWasActiveSourceSetToConnectedDevice(false);
+        setWasActivePathSetToConnectedDevice(false);
         boolean sendStandbyOnSleep =
                 mService.getHdmiCecConfig().getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 3161b77..310f592 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -485,6 +485,7 @@
      * @return true if the hour is valid
      */
     private static boolean isValidHour(int value) {
+        value = bcdToDecimal(value);
         return isWithinRange(value, 0, 23);
     }
 
@@ -496,6 +497,7 @@
      * @return true if the minute is valid
      */
     private static boolean isValidMinute(int value) {
+        value = bcdToDecimal(value);
         return isWithinRange(value, 0, 59);
     }
 
@@ -507,10 +509,24 @@
      * @return true if the duration hours is valid
      */
     private static boolean isValidDurationHours(int value) {
+        value = bcdToDecimal(value);
         return isWithinRange(value, 0, 99);
     }
 
     /**
+     * Convert BCD value to decimal value.
+     *
+     * @param value BCD value
+     * @return decimal value
+     */
+    private static int bcdToDecimal(int value) {
+        int tens = (value & 0xF0) >> 4;
+        int ones = (value & 0x0F);
+
+        return tens * 10 + ones;
+    }
+
+    /**
      * Check if the given value is a valid recording sequence. A valid value is adheres to range
      * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
      *
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index bbe7b2b..3c7b9d3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -4328,6 +4328,7 @@
         HdmiCecLocalDevicePlayback playback = playback();
         HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
         if (playback != null) {
+            playback.dismissUiOnActiveSourceStatusRecovered();
             playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
                     caller);
             playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 0c4fb26..d48957b 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -44,7 +44,8 @@
     static final int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
 
     // Time out in millseconds used for <Routing Information>
-    private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
+    @VisibleForTesting
+    static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
 
     // If set to true, call {@link HdmiControlService#invokeInputChangeListener()} when
     // the routing control/active source change happens. The listener should be called if
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index 56e538b..028637b 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -16,7 +16,9 @@
 
 package com.android.server.hdmi;
 
+import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -89,8 +91,13 @@
 
         // If System Audio Control feature is enabled, turn on system audio mode when new AVR is
         // detected. Otherwise, turn off system audio mode.
+        // If AVR reports SAM on and it is in standby, the action SystemAudioActionFromTv
+        // triggers a <SAM Request> that will wake-up the AVR.
         boolean targetSystemAudioMode = tv().isSystemAudioControlFeatureEnabled();
-        if (currentSystemAudioMode != targetSystemAudioMode) {
+        if (currentSystemAudioMode != targetSystemAudioMode
+                || (currentSystemAudioMode && tv().getAvrDeviceInfo() != null
+                && tv().getAvrDeviceInfo().getDevicePowerStatus()
+                == HdmiControlManager.POWER_STATUS_STANDBY)) {
             // Start System Audio Control feature actions only if necessary.
             addAndStartAction(
                     new SystemAudioActionFromTv(tv(), mAvrAddress, targetSystemAudioMode, null));
diff --git a/services/core/java/com/android/server/hdmi/TEST_MAPPING b/services/core/java/com/android/server/hdmi/TEST_MAPPING
index c0fa121..1c85c7f 100644
--- a/services/core/java/com/android/server/hdmi/TEST_MAPPING
+++ b/services/core/java/com/android/server/hdmi/TEST_MAPPING
@@ -1,21 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.hdmi"
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_hdmi_Presubmit"
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index 819b9a1..73f18d1 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -21,7 +21,7 @@
 import android.annotation.UserIdInt;
 import android.graphics.PointF;
 import android.hardware.display.DisplayViewport;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
 import android.os.IBinder;
 import android.view.InputChannel;
 import android.view.inputmethod.InputMethodSubtype;
@@ -230,18 +230,14 @@
     public abstract int getLastUsedInputDeviceId();
 
     /**
-     * Notify Keyboard system shortcut was triggered by the user and handled by the framework.
+     * Notify key gesture was completed by the user.
      *
-     * NOTE: This is just to notify that a system shortcut was triggered. No further action is
-     * required to execute the said shortcut. This callback is meant for purposes of providing user
-     * hints or logging, etc.
-     *
-     * @param deviceId the device ID of the keyboard using which the shortcut was triggered
-     * @param keycodes the keys pressed for triggering the shortcut
-     * @param modifierState the modifier state of the key event that triggered the shortcut
-     * @param shortcut the shortcut that was triggered
+     * @param deviceId the device ID of the keyboard using which the event was completed
+     * @param keycodes the keys pressed for the event
+     * @param modifierState the modifier state
+     * @param event the gesture event that was completed
      *
      */
-    public abstract void notifyKeyboardShortcutTriggered(int deviceId, int[] keycodes,
-            int modifierState, @KeyboardSystemShortcut.SystemShortcut int shortcut);
+    public abstract void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
+            @KeyGestureEvent.KeyGestureType int event);
 }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e555761..84cee7e 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -20,10 +20,14 @@
 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
+import static com.android.hardware.input.Flags.touchpadVisualizer;
+
 import android.Manifest;
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.PermissionManuallyEnforced;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.bluetooth.BluetoothAdapter;
@@ -33,6 +37,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.graphics.PixelFormat;
 import android.graphics.PointF;
 import android.hardware.SensorPrivacyManager;
@@ -46,18 +51,18 @@
 import android.hardware.input.IInputDevicesChangedListener;
 import android.hardware.input.IInputManager;
 import android.hardware.input.IInputSensorEventListener;
+import android.hardware.input.IKeyGestureEventListener;
 import android.hardware.input.IKeyboardBacklightListener;
-import android.hardware.input.IKeyboardSystemShortcutListener;
 import android.hardware.input.IStickyModifierStateListener;
 import android.hardware.input.ITabletModeChangedListener;
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputSensorInfo;
 import android.hardware.input.InputSettings;
+import android.hardware.input.KeyGestureEvent;
 import android.hardware.input.KeyGlyphMap;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.KeyboardLayoutSelectionResult;
-import android.hardware.input.KeyboardSystemShortcut;
 import android.hardware.input.TouchCalibration;
 import android.hardware.lights.Light;
 import android.hardware.lights.LightState;
@@ -121,7 +126,7 @@
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerInternal.LidSwitchCallback;
 import com.android.server.input.debug.FocusEventDebugView;
-import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.input.debug.TouchpadDebugViewController;
 import com.android.server.policy.WindowManagerPolicy;
 
 import libcore.io.IoUtils;
@@ -159,7 +164,7 @@
     private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
     private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
     private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
-    private static final int MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED = 4;
+    private static final int MSG_KEY_GESTURE_COMPLETED = 4;
 
     private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
     private static final AdditionalDisplayInputProperties
@@ -171,7 +176,7 @@
     private final InputManagerHandler mHandler;
     private DisplayManagerInternal mDisplayManagerInternal;
 
-    private InputMethodManagerInternal mInputMethodManagerInternal;
+    private PackageManagerInternal mPackageManagerInternal;
 
     private final File mDoubleTouchGestureEnableFile;
 
@@ -303,14 +308,15 @@
     // Manages battery state for input devices.
     private final BatteryController mBatteryController;
 
+    @Nullable
+    private final TouchpadDebugViewController mTouchpadDebugViewController;
+
     // Manages Keyboard backlight
     private final KeyboardBacklightControllerInterface mKeyboardBacklightController;
 
     // Manages Sticky modifier state
     private final StickyModifierStateController mStickyModifierStateController;
-
-    // Manages keyboard system shortcut callbacks
-    private final KeyboardShortcutCallbackHandler mKeyboardShortcutCallbackHandler;
+    private final KeyGestureController mKeyGestureController;
 
     // Manages Keyboard microphone mute led
     private final KeyboardLedController mKeyboardLedController;
@@ -460,6 +466,9 @@
         mSettingsObserver = new InputSettingsObserver(mContext, mHandler, this, mNative);
         mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore,
                 injector.getLooper());
+        mTouchpadDebugViewController =
+                touchpadVisualizer() ? new TouchpadDebugViewController(mContext,
+                        injector.getLooper(), this) : null;
         mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(),
                 injector.getUEventManager());
         mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
@@ -467,7 +476,7 @@
                         injector.getLooper(), injector.getUEventManager())
                 : new KeyboardBacklightControllerInterface() {};
         mStickyModifierStateController = new StickyModifierStateController();
-        mKeyboardShortcutCallbackHandler = new KeyboardShortcutCallbackHandler();
+        mKeyGestureController = new KeyGestureController();
         mKeyboardLedController = new KeyboardLedController(mContext, injector.getLooper(),
                 mNative);
         mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
@@ -538,8 +547,7 @@
         }
 
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-        mInputMethodManagerInternal =
-                LocalServices.getService(InputMethodManagerInternal.class);
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
 
         mSettingsObserver.registerAndUpdate();
 
@@ -1788,6 +1796,16 @@
         return mNative.getSensorList(deviceId);
     }
 
+    /**
+     * Retrieves the hardware properties of the touchpad for the given device ID.
+     * Returns null if the device has no touchpad hardware properties
+     * or if the device ID is invalid.
+     */
+    @Nullable
+    public TouchpadHardwareProperties getTouchpadHardwareProperties(int deviceId) {
+        return mNative.getTouchpadHardwareProperties(deviceId);
+    }
+
     @Override // Binder call
     public boolean registerSensorListener(IInputSensorEventListener listener) {
         if (DEBUG) {
@@ -2250,6 +2268,18 @@
 
     // Native callback.
     @SuppressWarnings("unused")
+    private void notifyTouchpadHardwareState(TouchpadHardwareState hardwareStates, int deviceId) {
+        // TODO(b/286551975): sent the touchpad hardware state data here to TouchpadDebugActivity
+        Slog.d(TAG, "notifyTouchpadHardwareState: Time: "
+                + hardwareStates.getTimestamp() + ", No. Buttons: "
+                + hardwareStates.getButtonsDown() + ", No. Fingers: "
+                + hardwareStates.getFingerCount() + ", No. Touch: "
+                + hardwareStates.getTouchCount() + ", Id: "
+                + deviceId);
+    }
+
+    // Native callback.
+    @SuppressWarnings("unused")
     private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
         if (DEBUG) {
             Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
@@ -2710,34 +2740,60 @@
                 lockedModifierState);
     }
 
-    @Override
-    @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    public void registerKeyboardSystemShortcutListener(
-            @NonNull IKeyboardSystemShortcutListener listener) {
-        super.registerKeyboardSystemShortcutListener_enforcePermission();
-        Objects.requireNonNull(listener);
-        mKeyboardShortcutCallbackHandler.registerKeyboardSystemShortcutListener(listener,
-                Binder.getCallingPid());
-    }
-
-    @Override
-    @EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_SYSTEM_SHORTCUTS)
-    public void unregisterKeyboardSystemShortcutListener(
-            @NonNull IKeyboardSystemShortcutListener listener) {
-        super.unregisterKeyboardSystemShortcutListener_enforcePermission();
-        Objects.requireNonNull(listener);
-        mKeyboardShortcutCallbackHandler.unregisterKeyboardSystemShortcutListener(listener,
-                Binder.getCallingPid());
-    }
-
-    private void handleKeyboardSystemShortcutTriggered(int deviceId,
-            KeyboardSystemShortcut shortcut) {
-        InputDevice device = getInputDevice(deviceId);
-        if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
+    /**
+     * Enforces the caller contains the necessary permission to manage key gestures.
+     */
+    @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
+    private void enforceManageKeyGesturePermission() {
+        // TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
+        //  permission is rolled out
+        String systemUIPackage = mContext.getString(R.string.config_systemUi);
+        int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
+                .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
+                        UserHandle.USER_SYSTEM));
+        if (UserHandle.getCallingAppId() == systemUIAppId) {
             return;
         }
-        KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, shortcut);
-        mKeyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(deviceId, shortcut);
+        if (mContext.checkCallingOrSelfPermission(
+                Manifest.permission.MANAGE_KEY_GESTURES) == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+
+        String message = "Managing Key Gestures requires the following permission: "
+                + Manifest.permission.MANAGE_KEY_GESTURES;
+        throw new SecurityException(message);
+    }
+
+
+    @Override
+    @PermissionManuallyEnforced
+    public void registerKeyGestureEventListener(
+            @NonNull IKeyGestureEventListener listener) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(listener);
+        mKeyGestureController.registerKeyGestureEventListener(listener,
+                Binder.getCallingPid());
+    }
+
+    @Override
+    @PermissionManuallyEnforced
+    public void unregisterKeyGestureEventListener(
+            @NonNull IKeyGestureEventListener listener) {
+        enforceManageKeyGesturePermission();
+
+        Objects.requireNonNull(listener);
+        mKeyGestureController.unregisterKeyGestureEventListener(listener,
+                Binder.getCallingPid());
+    }
+
+    private void handleKeyGestureCompleted(KeyGestureEvent event) {
+        InputDevice device = getInputDevice(event.getDeviceId());
+        if (device == null || device.isVirtual()) {
+            return;
+        }
+        KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event);
+        mKeyGestureController.onKeyGestureEvent(event);
     }
 
     /**
@@ -2908,10 +2964,9 @@
                     boolean inTabletMode = (boolean) args.arg1;
                     deliverTabletModeChanged(whenNanos, inTabletMode);
                     break;
-                case MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED:
-                    int deviceId = msg.arg1;
-                    KeyboardSystemShortcut shortcut = (KeyboardSystemShortcut) msg.obj;
-                    handleKeyboardSystemShortcutTriggered(deviceId, shortcut);
+                case MSG_KEY_GESTURE_COMPLETED:
+                    KeyGestureEvent event = (KeyGestureEvent) msg.obj;
+                    handleKeyGestureCompleted(event);
             }
         }
     }
@@ -3239,10 +3294,11 @@
         }
 
         @Override
-        public void notifyKeyboardShortcutTriggered(int deviceId, int[] keycodes, int modifierState,
-                @KeyboardSystemShortcut.SystemShortcut int shortcut) {
-            mHandler.obtainMessage(MSG_KEYBOARD_SYSTEM_SHORTCUT_TRIGGERED, deviceId, 0,
-                    new KeyboardSystemShortcut(keycodes, modifierState, shortcut)).sendToTarget();
+        public void notifyKeyGestureCompleted(int deviceId, int[] keycodes, int modifierState,
+                @KeyGestureEvent.KeyGestureType int gestureType) {
+            mHandler.obtainMessage(MSG_KEY_GESTURE_COMPLETED,
+                    new KeyGestureEvent(deviceId, keycodes, modifierState,
+                            gestureType)).sendToTarget();
         }
     }
 
@@ -3308,6 +3364,13 @@
         }
     }
 
+    void updateTouchpadVisualizerEnabled(boolean enabled) {
+        mNative.setShouldNotifyTouchpadHardwareState(enabled);
+        if (mTouchpadDebugViewController != null) {
+            mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(enabled);
+        }
+    }
+
     void updatePointerLocationEnabled(boolean enabled) {
         mWindowManagerCallbacks.notifyPointerLocationChanged(enabled);
     }
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 000f312..835fb72 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -71,6 +71,8 @@
                         (reason) -> updateTouchpadTapToClickEnabled()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_TAP_DRAGGING),
                         (reason) -> updateTouchpadTapDraggingEnabled()),
+                Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_VISUALIZER),
+                        (reason) -> updateTouchpadHardwareStateNotificationsEnabled()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE),
                         (reason) -> updateTouchpadRightClickZoneEnabled()),
                 Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
@@ -177,6 +179,10 @@
         mNative.setTouchpadTapDraggingEnabled(InputSettings.useTouchpadTapDragging(mContext));
     }
 
+    private void updateTouchpadHardwareStateNotificationsEnabled() {
+        mService.updateTouchpadVisualizerEnabled(InputSettings.useTouchpadVisualizer(mContext));
+    }
+
     private void updateTouchpadRightClickZoneEnabled() {
         mNative.setTouchpadRightClickZoneEnabled(InputSettings.useTouchpadRightClickZone(mContext));
     }
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
new file mode 100644
index 0000000..674d3c4
--- /dev/null
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -0,0 +1,134 @@
+/*
+ * 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.server.input;
+
+import android.annotation.BinderThread;
+import android.hardware.input.IKeyGestureEventListener;
+import android.hardware.input.KeyGestureEvent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
+ * key gesture event occurs.
+ */
+final class KeyGestureController {
+
+    private static final String TAG = "KeyGestureController";
+
+    // To enable these logs, run:
+    // 'adb shell setprop log.tag.KeyGestureController DEBUG' (requires restart)
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    // List of currently registered key gesture event listeners keyed by process pid
+    @GuardedBy("mKeyGestureEventListenerRecords")
+    private final SparseArray<KeyGestureEventListenerRecord>
+            mKeyGestureEventListenerRecords = new SparseArray<>();
+
+    public void onKeyGestureEvent(KeyGestureEvent event) {
+        if (DEBUG) {
+            Slog.d(TAG, "Key gesture event occurred, event = " + event);
+        }
+
+        synchronized (mKeyGestureEventListenerRecords) {
+            for (int i = 0; i < mKeyGestureEventListenerRecords.size(); i++) {
+                mKeyGestureEventListenerRecords.valueAt(i).onKeyGestureEvent(event);
+            }
+        }
+    }
+
+    /** Register the key gesture event listener for a process. */
+    @BinderThread
+    public void registerKeyGestureEventListener(IKeyGestureEventListener listener,
+            int pid) {
+        synchronized (mKeyGestureEventListenerRecords) {
+            if (mKeyGestureEventListenerRecords.get(pid) != null) {
+                throw new IllegalStateException("The calling process has already registered "
+                        + "a KeyGestureEventListener.");
+            }
+            KeyGestureEventListenerRecord record = new KeyGestureEventListenerRecord(
+                    pid, listener);
+            try {
+                listener.asBinder().linkToDeath(record, 0);
+            } catch (RemoteException ex) {
+                throw new RuntimeException(ex);
+            }
+            mKeyGestureEventListenerRecords.put(pid, record);
+        }
+    }
+
+    /** Unregister the key gesture event listener for a process. */
+    @BinderThread
+    public void unregisterKeyGestureEventListener(IKeyGestureEventListener listener,
+            int pid) {
+        synchronized (mKeyGestureEventListenerRecords) {
+            KeyGestureEventListenerRecord record =
+                    mKeyGestureEventListenerRecords.get(pid);
+            if (record == null) {
+                throw new IllegalStateException("The calling process has no registered "
+                        + "KeyGestureEventListener.");
+            }
+            if (record.mListener.asBinder() != listener.asBinder()) {
+                throw new IllegalStateException("The calling process has a different registered "
+                        + "KeyGestureEventListener.");
+            }
+            record.mListener.asBinder().unlinkToDeath(record, 0);
+            mKeyGestureEventListenerRecords.remove(pid);
+        }
+    }
+
+    private void onKeyGestureEventListenerDied(int pid) {
+        synchronized (mKeyGestureEventListenerRecords) {
+            mKeyGestureEventListenerRecords.remove(pid);
+        }
+    }
+
+    // A record of a registered key gesture event listener from one process.
+    private class KeyGestureEventListenerRecord implements IBinder.DeathRecipient {
+        public final int mPid;
+        public final IKeyGestureEventListener mListener;
+
+        KeyGestureEventListenerRecord(int pid, IKeyGestureEventListener listener) {
+            mPid = pid;
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) {
+                Slog.d(TAG, "Key gesture event listener for pid " + mPid + " died.");
+            }
+            onKeyGestureEventListenerDied(mPid);
+        }
+
+        public void onKeyGestureEvent(KeyGestureEvent event) {
+            try {
+                mListener.onKeyGestureEvent(event.getDeviceId(), event.getKeycodes(),
+                        event.getModifierState(), event.getKeyGestureType());
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify process " + mPid
+                        + " that key gesture event occurred, assuming it died.", ex);
+                binderDied();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index 3d2f951..1daf4db 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -24,9 +24,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.input.KeyGestureEvent;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
-import android.hardware.input.KeyboardSystemShortcut;
 import android.icu.util.ULocale;
 import android.text.TextUtils;
 import android.util.Log;
@@ -66,14 +66,17 @@
      * defined in "stats/atoms/input/input_extension_atoms.proto"
      */
     public static void logKeyboardSystemsEventReportedAtom(@NonNull InputDevice inputDevice,
-            @NonNull KeyboardSystemShortcut keyboardSystemShortcut) {
+            @NonNull KeyGestureEvent keyGestureEvent) {
+        if (inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+            return;
+        }
         FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
                 inputDevice.getVendorId(), inputDevice.getProductId(),
-                keyboardSystemShortcut.getSystemShortcut(), keyboardSystemShortcut.getKeycodes(),
-                keyboardSystemShortcut.getModifierState(), inputDevice.getDeviceBus());
+                keyGestureEvent.getKeyGestureType(), keyGestureEvent.getKeycodes(),
+                keyGestureEvent.getModifierState(), inputDevice.getDeviceBus());
 
         if (DEBUG) {
-            Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemShortcut);
+            Slog.d(TAG, "Logging Keyboard system event: " + keyGestureEvent);
         }
     }
 
diff --git a/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java b/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java
deleted file mode 100644
index 092058e..0000000
--- a/services/core/java/com/android/server/input/KeyboardShortcutCallbackHandler.java
+++ /dev/null
@@ -1,137 +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.server.input;
-
-import android.annotation.BinderThread;
-import android.hardware.input.IKeyboardSystemShortcutListener;
-import android.hardware.input.KeyboardSystemShortcut;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
- * keyboard shortcut is triggered.
- */
-final class KeyboardShortcutCallbackHandler {
-
-    private static final String TAG = "KeyboardShortcut";
-
-    // To enable these logs, run:
-    // 'adb shell setprop log.tag.KeyboardShortcutCallbackHandler DEBUG' (requires restart)
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    // List of currently registered keyboard system shortcut listeners keyed by process pid
-    @GuardedBy("mKeyboardSystemShortcutListenerRecords")
-    private final SparseArray<KeyboardSystemShortcutListenerRecord>
-            mKeyboardSystemShortcutListenerRecords = new SparseArray<>();
-
-    public void onKeyboardSystemShortcutTriggered(int deviceId,
-            KeyboardSystemShortcut systemShortcut) {
-        if (DEBUG) {
-            Slog.d(TAG, "Keyboard system shortcut triggered, deviceId = " + deviceId
-                    + ", systemShortcut = " + systemShortcut);
-        }
-
-        synchronized (mKeyboardSystemShortcutListenerRecords) {
-            for (int i = 0; i < mKeyboardSystemShortcutListenerRecords.size(); i++) {
-                mKeyboardSystemShortcutListenerRecords.valueAt(i).onKeyboardSystemShortcutTriggered(
-                        deviceId, systemShortcut);
-            }
-        }
-    }
-
-    /** Register the keyboard system shortcut listener for a process. */
-    @BinderThread
-    public void registerKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener,
-            int pid) {
-        synchronized (mKeyboardSystemShortcutListenerRecords) {
-            if (mKeyboardSystemShortcutListenerRecords.get(pid) != null) {
-                throw new IllegalStateException("The calling process has already registered "
-                        + "a KeyboardSystemShortcutListener.");
-            }
-            KeyboardSystemShortcutListenerRecord record = new KeyboardSystemShortcutListenerRecord(
-                    pid, listener);
-            try {
-                listener.asBinder().linkToDeath(record, 0);
-            } catch (RemoteException ex) {
-                throw new RuntimeException(ex);
-            }
-            mKeyboardSystemShortcutListenerRecords.put(pid, record);
-        }
-    }
-
-    /** Unregister the keyboard system shortcut listener for a process. */
-    @BinderThread
-    public void unregisterKeyboardSystemShortcutListener(IKeyboardSystemShortcutListener listener,
-            int pid) {
-        synchronized (mKeyboardSystemShortcutListenerRecords) {
-            KeyboardSystemShortcutListenerRecord record =
-                    mKeyboardSystemShortcutListenerRecords.get(pid);
-            if (record == null) {
-                throw new IllegalStateException("The calling process has no registered "
-                        + "KeyboardSystemShortcutListener.");
-            }
-            if (record.mListener.asBinder() != listener.asBinder()) {
-                throw new IllegalStateException("The calling process has a different registered "
-                        + "KeyboardSystemShortcutListener.");
-            }
-            record.mListener.asBinder().unlinkToDeath(record, 0);
-            mKeyboardSystemShortcutListenerRecords.remove(pid);
-        }
-    }
-
-    private void onKeyboardSystemShortcutListenerDied(int pid) {
-        synchronized (mKeyboardSystemShortcutListenerRecords) {
-            mKeyboardSystemShortcutListenerRecords.remove(pid);
-        }
-    }
-
-    // A record of a registered keyboard system shortcut listener from one process.
-    private class KeyboardSystemShortcutListenerRecord implements IBinder.DeathRecipient {
-        public final int mPid;
-        public final IKeyboardSystemShortcutListener mListener;
-
-        KeyboardSystemShortcutListenerRecord(int pid, IKeyboardSystemShortcutListener listener) {
-            mPid = pid;
-            mListener = listener;
-        }
-
-        @Override
-        public void binderDied() {
-            if (DEBUG) {
-                Slog.d(TAG, "Keyboard system shortcut listener for pid " + mPid + " died.");
-            }
-            onKeyboardSystemShortcutListenerDied(mPid);
-        }
-
-        public void onKeyboardSystemShortcutTriggered(int deviceId, KeyboardSystemShortcut data) {
-            try {
-                mListener.onKeyboardSystemShortcutTriggered(deviceId, data.getKeycodes(),
-                        data.getModifierState(), data.getSystemShortcut());
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify process " + mPid
-                        + " that keyboard system shortcut was triggered, assuming it died.", ex);
-                binderDied();
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index a9d40bb..1e7c97f9 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -135,6 +135,8 @@
 
     void setTouchpadTapDraggingEnabled(boolean enabled);
 
+    void setShouldNotifyTouchpadHardwareState(boolean enabled);
+
     void setTouchpadRightClickZoneEnabled(boolean enabled);
 
     void setShowTouches(boolean enabled);
@@ -214,6 +216,9 @@
 
     InputSensorInfo[] getSensorList(int deviceId);
 
+    @Nullable
+    TouchpadHardwareProperties getTouchpadHardwareProperties(int deviceId);
+
     boolean flushSensor(int deviceId, int sensorType);
 
     boolean enableSensor(int deviceId, int sensorType, int samplingPeriodUs,
@@ -395,6 +400,9 @@
         public native void setTouchpadTapDraggingEnabled(boolean enabled);
 
         @Override
+        public native void setShouldNotifyTouchpadHardwareState(boolean enabled);
+
+        @Override
         public native void setTouchpadRightClickZoneEnabled(boolean enabled);
 
         @Override
@@ -507,6 +515,9 @@
         public native InputSensorInfo[] getSensorList(int deviceId);
 
         @Override
+        public native TouchpadHardwareProperties getTouchpadHardwareProperties(int deviceId);
+
+        @Override
         public native boolean flushSensor(int deviceId, int sensorType);
 
         @Override
diff --git a/services/core/java/com/android/server/input/OWNERS b/services/core/java/com/android/server/input/OWNERS
index 4c20c4d..e2834ec 100644
--- a/services/core/java/com/android/server/input/OWNERS
+++ b/services/core/java/com/android/server/input/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
 include /INPUT_OWNERS
diff --git a/services/core/java/com/android/server/input/TouchpadFingerState.java b/services/core/java/com/android/server/input/TouchpadFingerState.java
new file mode 100644
index 0000000..8e803e8
--- /dev/null
+++ b/services/core/java/com/android/server/input/TouchpadFingerState.java
@@ -0,0 +1,261 @@
+/*
+ * 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.server.input;
+
+import com.android.internal.util.DataClass;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByNative;
+
+/**
+ * This class represents the Touchpad Finger State of a single contact on the touchpad.
+ * It is used for the touchpad visualizer project at TouchpadDebugActivity
+ */
+@DataClass(genToString = true)
+@UsedByNative(
+        description = "Called from JNI in jni/com_android_server_input_InputManagerService.cpp",
+        kind = KeepItemKind.CLASS_AND_MEMBERS)
+public final class TouchpadFingerState{
+    /**
+     * The large radius of the ellipse of the finger touching the pad.
+     */
+    private final float mTouchMajor;
+
+    /**
+     * The small radius of the ellipse of the finger touching the pad.
+     */
+    private final float mTouchMinor;
+
+    /**
+     * The large radius of the ellipse of the finger, including parts
+     * that are hovering over the pad but not quite touching.
+     */
+    private final float mWidthMajor;
+
+    /**
+     * The small radius of the ellipse of the finger, including parts
+     * that are hovering over the pad but not quite touching.
+     */
+    private final float mWidthMinor;
+
+    /**
+     * Pressure applied by a finger on the touchpad.
+     */
+    private final float mPressure;
+
+    /**
+     * Orientation of a finger on the touchpad. Measured in radians.
+     */
+    private final float mOrientation;
+
+    /**
+     * The X-coordinate of the center of the ellipse that represents a finger.
+     */
+    private final float mPositionX;
+
+    /**
+     * The Y-coordinate of the center of the ellipse that represents a finger.
+     */
+    private final float mPositionY;
+
+    /**
+     * A number that identifies a single physical finger across consecutive frames.
+     * If a finger is the same physical finger across two consecutive frames, it
+     * must have the same tracking ID; if it's a different finger, it should
+     * have a different tracking ID.
+     */
+    private final int mTrackingId;
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/input/
+    // TouchpadFingerState.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+    /**
+     * Creates a new TouchpadFingerState.
+     *
+     * @param touchMajor
+     *   The large radius of the ellipse of the finger touching the pad.
+     * @param touchMinor
+     *   The small radius of the ellipse of the finger touching the pad.
+     * @param widthMajor
+     *   The large radius of the ellipse of the finger, including parts
+     *   that are hovering over the pad but not quite touching.
+     * @param widthMinor
+     *   The small radius of the ellipse of the finger, including parts
+     *   that are hovering over the pad but not quite touching.
+     * @param pressure
+     *   Pressure applied by a finger on the touchpad.
+     * @param orientation
+     *   Orientation of a finger on the touchpad. Measured in radians.
+     * @param positionX
+     *   The X-coordinate of the center of the ellipse that represents a finger.
+     * @param positionY
+     *   The Y-coordinate of the center of the ellipse that represents a finger.
+     * @param trackingId
+     *   A number that identifies a single physical finger across consecutive frames.
+     *   If a finger is the same physical finger across two consecutive frames, it
+     *   must have the same tracking ID; if it's a different finger, it should
+     *   have a different tracking ID.
+     */
+    @DataClass.Generated.Member
+    public TouchpadFingerState(
+            float touchMajor,
+            float touchMinor,
+            float widthMajor,
+            float widthMinor,
+            float pressure,
+            float orientation,
+            float positionX,
+            float positionY,
+            int trackingId) {
+        this.mTouchMajor = touchMajor;
+        this.mTouchMinor = touchMinor;
+        this.mWidthMajor = widthMajor;
+        this.mWidthMinor = widthMinor;
+        this.mPressure = pressure;
+        this.mOrientation = orientation;
+        this.mPositionX = positionX;
+        this.mPositionY = positionY;
+        this.mTrackingId = trackingId;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The large radius of the ellipse of the finger touching the pad.
+     */
+    @DataClass.Generated.Member
+    public float getTouchMajor() {
+        return mTouchMajor;
+    }
+
+    /**
+     * The small radius of the ellipse of the finger touching the pad.
+     */
+    @DataClass.Generated.Member
+    public float getTouchMinor() {
+        return mTouchMinor;
+    }
+
+    /**
+     * The large radius of the ellipse of the finger, including parts
+     * that are hovering over the pad but not quite touching.
+     */
+    @DataClass.Generated.Member
+    public float getWidthMajor() {
+        return mWidthMajor;
+    }
+
+    /**
+     * The small radius of the ellipse of the finger, including parts
+     * that are hovering over the pad but not quite touching.
+     */
+    @DataClass.Generated.Member
+    public float getWidthMinor() {
+        return mWidthMinor;
+    }
+
+    /**
+     * Pressure applied by a finger on the touchpad.
+     */
+    @DataClass.Generated.Member
+    public float getPressure() {
+        return mPressure;
+    }
+
+    /**
+     * Orientation of a finger on the touchpad. Measured in radians.
+     */
+    @DataClass.Generated.Member
+    public float getOrientation() {
+        return mOrientation;
+    }
+
+    /**
+     * The X-coordinate of the center of the ellipse that represents a finger.
+     */
+    @DataClass.Generated.Member
+    public float getPositionX() {
+        return mPositionX;
+    }
+
+    /**
+     * The Y-coordinate of the center of the ellipse that represents a finger.
+     */
+    @DataClass.Generated.Member
+    public float getPositionY() {
+        return mPositionY;
+    }
+
+    /**
+     * A number that identifies a single physical finger across consecutive frames.
+     * If a finger is the same physical finger across two consecutive frames, it
+     * must have the same tracking ID; if it's a different finger, it should
+     * have a different tracking ID.
+     */
+    @DataClass.Generated.Member
+    public int getTrackingId() {
+        return mTrackingId;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "TouchpadFingerState { " +
+                "touchMajor = " + mTouchMajor + ", " +
+                "touchMinor = " + mTouchMinor + ", " +
+                "widthMajor = " + mWidthMajor + ", " +
+                "widthMinor = " + mWidthMinor + ", " +
+                "pressure = " + mPressure + ", " +
+                "orientation = " + mOrientation + ", " +
+                "positionX = " + mPositionX + ", " +
+                "positionY = " + mPositionY + ", " +
+                "trackingId = " + mTrackingId +
+        " }";
+    }
+
+    @DataClass.Generated(
+            time = 1724078820706L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/services/core/java/com/android/server/input/"
+                    + "TouchpadFingerState.java",
+            inputSignatures = "private final  float mTouchMajor\nprivate final  float mTouchMinor\n"
+                    + "private final  float mWidthMajor\nprivate final  float mWidthMinor\nprivate"
+                    + " final  float mPressure\nprivate final  float mOrientation\nprivate final  "
+                    + "float mPositionX\nprivate final  float mPositionY\nprivate final  int "
+                    + "mTrackingId\nclass TouchpadFingerState extends java.lang.Object implements"
+                    + " []\[email protected](genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/input/TouchpadHardwareProperties.java b/services/core/java/com/android/server/input/TouchpadHardwareProperties.java
new file mode 100644
index 0000000..71abb19
--- /dev/null
+++ b/services/core/java/com/android/server/input/TouchpadHardwareProperties.java
@@ -0,0 +1,535 @@
+/*
+ * 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.server.input;
+
+import com.android.internal.util.DataClass;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByNative;
+
+/**
+ * A Java representation of hardware properties for a touchpad or mouse device.
+ * This class mirrors the Gestures library HardwareProperties C++ struct used for representing
+ * touchpad and mouse device properties, including touch area, resolution, and features like haptic
+ * feedback, multitouch, and scroll wheels. It facilitates interaction between native and managed
+ * code in Android.
+ */
+@DataClass(
+        genToString = true
+)
+@UsedByNative(
+        description = "Called from JNI in jni/com_android_server_input_InputManagerService.cpp",
+        kind = KeepItemKind.CLASS_AND_MEMBERS)
+public class TouchpadHardwareProperties {
+    /**
+     * The minimum X coordinate that the device can report.
+     */
+    private float mLeft;
+
+    /**
+     * The minimum Y coordinate that the device can report.
+     */
+    private float mTop;
+
+    /**
+     * The maximum X coordinate that the device can report.
+     */
+    private float mRight;
+
+    /**
+     * The maximum Y coordinate that the device can report.
+     */
+    private float mBottom;
+
+    /**
+     * The resolution of the X axis, in units per mm. Set to 0 if the
+     * resolution is unknown.
+     */
+    private float mResX;
+    /**
+     * The resolutions of the Y axis, in units per mm. Set to 0 if the
+     * resolution is unknown.
+     */
+    private float mResY;
+
+    /**
+     * The minimum orientation value.
+     */
+    private float mOrientationMinimum;
+    /**
+     * The maximum orientation value.
+     */
+    private float mOrientationMaximum;
+
+    /**
+     * The maximum number of finger slots that the device can report in one
+     * HardwareState struct.
+     */
+    private short mMaxFingerCount;
+
+    /**
+     * Whether the touchpad has a button under its touch surface, allowing the
+     * user to click by pressing (almost) anywhere on the pad, as opposed to
+     * having one or more separate buttons for clicking.
+     */
+    private boolean mIsButtonPad;
+
+    /**
+     * Whether the touchpad is haptic, meaning that it reports true pressure (not
+     * just touch area) via the pressure axis, and can provide haptic feedback.
+     */
+    private boolean mIsHapticPad;
+
+    /**
+     * Whether the touchpad reports pressure values in any way.
+     */
+    private boolean mReportsPressure = true;
+
+    /**
+     * Returns a string representation of this instance, including all fields.
+     */
+    public String toString() {
+        return "HardwareProperties{"
+                + "left=" + mLeft
+                + ", top=" + mTop
+                + ", right=" + mRight
+                + ", bottom=" + mBottom
+                + ", resX=" + mResX
+                + ", resY=" + mResY
+                + ", orientationMinimum=" + mOrientationMinimum
+                + ", orientationMaximum=" + mOrientationMaximum
+                + ", maxFingerCount=" + mMaxFingerCount
+                + ", isButtonPad=" + mIsButtonPad
+                + ", isHapticPad=" + mIsHapticPad
+                + ", reportsPressure=" + mReportsPressure
+                + '}';
+    }
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/input
+    // /TouchpadHardwareProperties.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ TouchpadHardwareProperties(
+            float left,
+            float top,
+            float right,
+            float bottom,
+            float resX,
+            float resY,
+            float orientationMinimum,
+            float orientationMaximum,
+            short maxFingerCount,
+            boolean isButtonPad,
+            boolean isHapticPad,
+            boolean reportsPressure) {
+        this.mLeft = left;
+        this.mTop = top;
+        this.mRight = right;
+        this.mBottom = bottom;
+        this.mResX = resX;
+        this.mResY = resY;
+        this.mOrientationMinimum = orientationMinimum;
+        this.mOrientationMaximum = orientationMaximum;
+        this.mMaxFingerCount = maxFingerCount;
+        this.mIsButtonPad = isButtonPad;
+        this.mIsHapticPad = isHapticPad;
+        this.mReportsPressure = reportsPressure;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The minimum X coordinate that the device can report.
+     */
+    @DataClass.Generated.Member
+    public float getLeft() {
+        return mLeft;
+    }
+
+    /**
+     * The minimum Y coordinate that the device can report.
+     */
+    @DataClass.Generated.Member
+    public float getTop() {
+        return mTop;
+    }
+
+    /**
+     * The maximum X coordinate that the device can report.
+     */
+    @DataClass.Generated.Member
+    public float getRight() {
+        return mRight;
+    }
+
+    /**
+     * The maximum Y coordinate that the device can report.
+     */
+    @DataClass.Generated.Member
+    public float getBottom() {
+        return mBottom;
+    }
+
+    /**
+     * The resolution of the X axis, in units per mm. Set to 0 if the
+     * resolution is unknown.
+     */
+    @DataClass.Generated.Member
+    public float getResX() {
+        return mResX;
+    }
+
+    /**
+     * The resolutions of the Y axis, in units per mm. Set to 0 if the
+     * resolution is unknown.
+     */
+    @DataClass.Generated.Member
+    public float getResY() {
+        return mResY;
+    }
+
+    /**
+     * The minimum orientation value.
+     */
+    @DataClass.Generated.Member
+    public float getOrientationMinimum() {
+        return mOrientationMinimum;
+    }
+
+    /**
+     * The maximum orientation value.
+     */
+    @DataClass.Generated.Member
+    public float getOrientationMaximum() {
+        return mOrientationMaximum;
+    }
+
+    /**
+     * The maximum number of finger slots that the device can report in one
+     * HardwareState struct.
+     */
+    @DataClass.Generated.Member
+    public short getMaxFingerCount() {
+        return mMaxFingerCount;
+    }
+
+    /**
+     * Whether the touchpad has a button under its touch surface, allowing the
+     * user to click by pressing (almost) anywhere on the pad, as opposed to
+     * having one or more separate buttons for clicking.
+     */
+    @DataClass.Generated.Member
+    public boolean isIsButtonPad() {
+        return mIsButtonPad;
+    }
+
+    /**
+     * Whether the touchpad is haptic, meaning that it reports true pressure (not
+     * just touch area) via the pressure axis, and can provide haptic feedback.
+     */
+    @DataClass.Generated.Member
+    public boolean isIsHapticPad() {
+        return mIsHapticPad;
+    }
+
+    /**
+     * Whether the touchpad reports pressure values in any way.
+     */
+    @DataClass.Generated.Member
+    public boolean isReportsPressure() {
+        return mReportsPressure;
+    }
+
+    /**
+     * A builder for {@link TouchpadHardwareProperties}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private float mLeft;
+        private float mTop;
+        private float mRight;
+        private float mBottom;
+        private float mResX;
+        private float mResY;
+        private float mOrientationMinimum;
+        private float mOrientationMaximum;
+        private short mMaxFingerCount;
+        private boolean mIsButtonPad;
+        private boolean mIsHapticPad;
+        private boolean mReportsPressure;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param left
+         *   The minimum X coordinate that the device can report.
+         * @param top
+         *   The minimum Y coordinate that the device can report.
+         * @param right
+         *   The maximum X coordinate that the device can report.
+         * @param bottom
+         *   The maximum Y coordinate that the device can report.
+         * @param resX
+         *   The resolution of the X axis, in units per mm. Set to 0 if the
+         *   resolution is unknown.
+         * @param resY
+         *   The resolutions of the Y axis, in units per mm. Set to 0 if the
+         *   resolution is unknown.
+         * @param orientationMinimum
+         *   The minimum orientation value.
+         * @param orientationMaximum
+         *   The maximum orientation value.
+         * @param maxFingerCount
+         *   The maximum number of finger slots that the device can report in one
+         *   HardwareState struct.
+         * @param isButtonPad
+         *   Whether the touchpad has a button under its touch surface, allowing the
+         *   user to click by pressing (almost) anywhere on the pad, as opposed to
+         *   having one or more separate buttons for clicking.
+         * @param isHapticPad
+         *   Whether the touchpad is haptic, meaning that it reports true pressure (not
+         *   just touch area) via the pressure axis, and can provide haptic feedback.
+         */
+        public Builder(
+                float left,
+                float top,
+                float right,
+                float bottom,
+                float resX,
+                float resY,
+                float orientationMinimum,
+                float orientationMaximum,
+                short maxFingerCount,
+                boolean isButtonPad,
+                boolean isHapticPad) {
+            mLeft = left;
+            mTop = top;
+            mRight = right;
+            mBottom = bottom;
+            mResX = resX;
+            mResY = resY;
+            mOrientationMinimum = orientationMinimum;
+            mOrientationMaximum = orientationMaximum;
+            mMaxFingerCount = maxFingerCount;
+            mIsButtonPad = isButtonPad;
+            mIsHapticPad = isHapticPad;
+        }
+
+        /**
+         * The minimum X coordinate that the device can report.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setLeft(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mLeft = value;
+            return this;
+        }
+
+        /**
+         * The minimum Y coordinate that the device can report.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setTop(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mTop = value;
+            return this;
+        }
+
+        /**
+         * The maximum X coordinate that the device can report.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setRight(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mRight = value;
+            return this;
+        }
+
+        /**
+         * The maximum Y coordinate that the device can report.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setBottom(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mBottom = value;
+            return this;
+        }
+
+        /**
+         * The resolution of the X axis, in units per mm. Set to 0 if the
+         * resolution is unknown.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setResX(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mResX = value;
+            return this;
+        }
+
+        /**
+         * The resolutions of the Y axis, in units per mm. Set to 0 if the
+         * resolution is unknown.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setResY(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mResY = value;
+            return this;
+        }
+
+        /**
+         * The minimum orientation value.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setOrientationMinimum(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mOrientationMinimum = value;
+            return this;
+        }
+
+        /**
+         * The maximum orientation value.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setOrientationMaximum(float value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mOrientationMaximum = value;
+            return this;
+        }
+
+        /**
+         * The maximum number of finger slots that the device can report in one
+         * HardwareState struct.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setMaxFingerCount(short value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100;
+            mMaxFingerCount = value;
+            return this;
+        }
+
+        /**
+         * Whether the touchpad has a button under its touch surface, allowing the
+         * user to click by pressing (almost) anywhere on the pad, as opposed to
+         * having one or more separate buttons for clicking.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setIsButtonPad(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x200;
+            mIsButtonPad = value;
+            return this;
+        }
+
+        /**
+         * Whether the touchpad is haptic, meaning that it reports true pressure (not
+         * just touch area) via the pressure axis, and can provide haptic feedback.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setIsHapticPad(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x400;
+            mIsHapticPad = value;
+            return this;
+        }
+
+        /**
+         * Whether the touchpad reports pressure values in any way.
+         */
+        @DataClass.Generated.Member
+        public @android.annotation.NonNull Builder setReportsPressure(boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x800;
+            mReportsPressure = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @android.annotation.NonNull TouchpadHardwareProperties build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1000; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x800) == 0) {
+                mReportsPressure = true;
+            }
+            TouchpadHardwareProperties o = new TouchpadHardwareProperties(
+                    mLeft,
+                    mTop,
+                    mRight,
+                    mBottom,
+                    mResX,
+                    mResY,
+                    mOrientationMinimum,
+                    mOrientationMaximum,
+                    mMaxFingerCount,
+                    mIsButtonPad,
+                    mIsHapticPad,
+                    mReportsPressure);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x1000) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1723570664889L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/services/core"
+                    + "/java/com/android/server/input/TouchpadHardwareProperties.java",
+            inputSignatures = "private  float mLeft\nprivate  float mTop\nprivate  float mRight\n"
+                    + "private  float mBottom\nprivate  float mResX\nprivate  float mResY\n"
+                    + "private  float mOrientationMinimum\nprivate  float mOrientationMaximum\n"
+                    + "private  short mMaxFingerCount\nprivate  boolean mIsButtonPad\n"
+                    + "private  boolean mIsHapticPad\nprivate  boolean mReportsPressure\n"
+                    + "public  java.lang.String toString()\n"
+                    + "class TouchpadHardwareProperties extends java.lang.Object implements []\n"
+                    + "@com.android.internal.util.DataClass(genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+    //@formatter:on
+    // End of generated code
+}
diff --git a/services/core/java/com/android/server/input/TouchpadHardwareState.java b/services/core/java/com/android/server/input/TouchpadHardwareState.java
new file mode 100644
index 0000000..6eac3b5
--- /dev/null
+++ b/services/core/java/com/android/server/input/TouchpadHardwareState.java
@@ -0,0 +1,185 @@
+/*
+ * 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.server.input;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.DataClass;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.UsedByNative;
+
+/**
+ * This class represents a touchpad hardware state at a single moment in time.
+ * It is only used by the touchpad visualization which is implemented in TouchpadDebugActivity.
+ */
+@DataClass(genToString = true)
+@UsedByNative(
+        description = "Called from JNI in jni/com_android_server_input_InputManagerService.cpp",
+        kind = KeepItemKind.CLASS_AND_MEMBERS)
+public final class TouchpadHardwareState{
+    /**
+     * The time at which the event was received by the system.
+     * The time is in milliseconds and start counting when the program starts.
+      */
+    private final float mTimestamp;
+
+    /**
+     * Number of buttons pressed. Note that in our case while using
+     * a touchpad only one button is available and can be pressed.
+     */
+    private final int mButtonsDown;
+
+    /**
+     * The number of FingerState structs pointed to by the fingers field.
+     */
+    private final int mFingerCount;
+
+    /**
+     * The number of fingers touching the pad, which may be more than fingerCount.
+     */
+    private final int mTouchCount;
+
+    /**
+     * Array of fingerStates that indicates the properties of each finger touching the touchpad.
+     */
+    private final @NonNull TouchpadFingerState[] mFingerStates;
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/input/
+    // TouchpadHardwareState.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new TouchpadHardwareState.
+     *
+     * @param timestamp
+     *   The time at which the event was received by the system.
+     *   The time is in milliseconds and start counting when the program starts.
+     * @param buttonsDown
+     *   Number of buttons pressed. Note that in our case while using
+     *   a touchpad only one button is available and can be pressed.
+     * @param fingerCount
+     *   The number of FingerState structs pointed to by the fingers field.
+     * @param touchCount
+     *   The number of fingers touching the pad, which may be more than fingerCount.
+     * @param fingerStates
+     *   Array of fingerStates that indicates the properties of each finger touching the touchpad.
+     */
+    @DataClass.Generated.Member
+    public TouchpadHardwareState(
+            float timestamp,
+            int buttonsDown,
+            int fingerCount,
+            int touchCount,
+            @NonNull TouchpadFingerState[] fingerStates) {
+        this.mTimestamp = timestamp;
+        this.mButtonsDown = buttonsDown;
+        this.mFingerCount = fingerCount;
+        this.mTouchCount = touchCount;
+        this.mFingerStates = fingerStates;
+        AnnotationValidations.validate(
+                NonNull.class, null, mFingerStates);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The time at which the event was received by the system.
+     * The time is in milliseconds and start counting when the program starts.
+     */
+    @DataClass.Generated.Member
+    public float getTimestamp() {
+        return mTimestamp;
+    }
+
+    /**
+     * Number of buttons pressed. Note that in our case while using
+     * a touchpad only one button is available and can be pressed.
+     */
+    @DataClass.Generated.Member
+    public int getButtonsDown() {
+        return mButtonsDown;
+    }
+
+    /**
+     * The number of FingerState structs pointed to by the fingers field.
+     */
+    @DataClass.Generated.Member
+    public int getFingerCount() {
+        return mFingerCount;
+    }
+
+    /**
+     * The number of fingers touching the pad, which may be more than fingerCount.
+     */
+    @DataClass.Generated.Member
+    public int getTouchCount() {
+        return mTouchCount;
+    }
+
+    /**
+     * Array of fingerStates that indicates the properties of each finger touching the touchpad.
+     */
+    @DataClass.Generated.Member
+    public @NonNull TouchpadFingerState[] getFingerStates() {
+        return mFingerStates;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "TouchpadHardwareState { " +
+                "timestamp = " + mTimestamp + ", " +
+                "buttonsDown = " + mButtonsDown + ", " +
+                "fingerCount = " + mFingerCount + ", " +
+                "touchCount = " + mTouchCount + ", " +
+                "fingerStates = " + java.util.Arrays.toString(mFingerStates) +
+        " }";
+    }
+
+    @DataClass.Generated(
+            time = 1724079048292L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/services/core/java/com/android/server/input/"
+                    + "TouchpadHardwareState.java",
+            inputSignatures = "private final  float mTimestamp\nprivate final  int mButtonsDown\n"
+                    + "private final  int mFingerCount\nprivate final  int mTouchCount\nprivate "
+                    + "final @android.annotation.NonNull com.android.server.input."
+                    + "TouchpadFingerState[] mFingerStates\nclass TouchpadHardwareState extends "
+                    + "java.lang.Object implements []\[email protected]"
+                    + "(genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugView.java b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
new file mode 100644
index 0000000..7785ffb
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugView.java
@@ -0,0 +1,204 @@
+/*
+ * 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.server.input.debug;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+public class TouchpadDebugView extends LinearLayout {
+    /**
+     * Input device ID for the touchpad that this debug view is displaying.
+     */
+    private final int mTouchpadId;
+
+    @NonNull
+    private final WindowManager mWindowManager;
+
+    @NonNull
+    private final WindowManager.LayoutParams mWindowLayoutParams;
+
+    private final int mTouchSlop;
+
+    private float mTouchDownX;
+    private float mTouchDownY;
+    private int mScreenWidth;
+    private int mScreenHeight;
+    private int mWindowLocationBeforeDragX;
+    private int mWindowLocationBeforeDragY;
+
+    public TouchpadDebugView(Context context, int touchpadId) {
+        super(context);
+        mTouchpadId = touchpadId;
+        mWindowManager =
+                Objects.requireNonNull(getContext().getSystemService(WindowManager.class));
+        init(context);
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+
+        // TODO(b/360137366): Use the hardware properties to initialise layout parameters.
+        mWindowLayoutParams = new WindowManager.LayoutParams();
+        mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+        mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+        mWindowLayoutParams.privateFlags |=
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        mWindowLayoutParams.setFitInsetsTypes(0);
+        mWindowLayoutParams.layoutInDisplayCutoutMode =
+                WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mWindowLayoutParams.format = PixelFormat.TRANSLUCENT;
+        mWindowLayoutParams.setTitle("TouchpadDebugView - display " + mContext.getDisplayId());
+
+        mWindowLayoutParams.x = 40;
+        mWindowLayoutParams.y = 100;
+        mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
+    }
+
+    private void init(Context context) {
+        setOrientation(VERTICAL);
+        setLayoutParams(new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT));
+        setBackgroundColor(Color.TRANSPARENT);
+
+        // TODO(b/286551975): Replace this content with the touchpad debug view.
+        TextView textView1 = new TextView(context);
+        textView1.setBackgroundColor(Color.parseColor("#FFFF0000"));
+        textView1.setTextSize(20);
+        textView1.setText("Touchpad Debug View 1");
+        textView1.setGravity(Gravity.CENTER);
+        textView1.setTextColor(Color.WHITE);
+        textView1.setLayoutParams(new LayoutParams(1000, 200));
+
+        TextView textView2 = new TextView(context);
+        textView2.setBackgroundColor(Color.BLUE);
+        textView2.setTextSize(20);
+        textView2.setText("Touchpad Debug View 2");
+        textView2.setGravity(Gravity.CENTER);
+        textView2.setTextColor(Color.WHITE);
+        textView2.setLayoutParams(new LayoutParams(1000, 200));
+
+        addView(textView1);
+        addView(textView2);
+
+        updateScreenDimensions();
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        float deltaX;
+        float deltaY;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mWindowLocationBeforeDragX = mWindowLayoutParams.x;
+                mWindowLocationBeforeDragY = mWindowLayoutParams.y;
+                mTouchDownX = event.getRawX() - mWindowLocationBeforeDragX;
+                mTouchDownY = event.getRawY() - mWindowLocationBeforeDragY;
+                return true;
+
+            case MotionEvent.ACTION_MOVE:
+                deltaX = event.getRawX() - mWindowLayoutParams.x - mTouchDownX;
+                deltaY = event.getRawY() - mWindowLayoutParams.y - mTouchDownY;
+                Slog.d("TouchpadDebugView", "Slop = " + mTouchSlop);
+                if (isSlopExceeded(deltaX, deltaY)) {
+                    Slog.d("TouchpadDebugView", "Slop exceeded");
+                    mWindowLayoutParams.x =
+                            Math.max(0, Math.min((int) (event.getRawX() - mTouchDownX),
+                                    mScreenWidth - this.getWidth()));
+                    mWindowLayoutParams.y =
+                            Math.max(0, Math.min((int) (event.getRawY() - mTouchDownY),
+                                    mScreenHeight - this.getHeight()));
+
+                    Slog.d("TouchpadDebugView", "New position X: "
+                            + mWindowLayoutParams.x + ", Y: " + mWindowLayoutParams.y);
+
+                    mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+                }
+                return true;
+
+            case MotionEvent.ACTION_UP:
+                deltaX = event.getRawX() - mWindowLayoutParams.x - mTouchDownX;
+                deltaY = event.getRawY() - mWindowLayoutParams.y - mTouchDownY;
+                if (!isSlopExceeded(deltaX, deltaY)) {
+                    performClick();
+                }
+                return true;
+
+            case MotionEvent.ACTION_CANCEL:
+                // Move the window back to the original position
+                mWindowLayoutParams.x = mWindowLocationBeforeDragX;
+                mWindowLayoutParams.y = mWindowLocationBeforeDragY;
+                mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+                return true;
+
+            default:
+                return super.onTouchEvent(event);
+        }
+    }
+
+    @Override
+    public boolean performClick() {
+        super.performClick();
+        Slog.d("TouchpadDebugView", "You clicked me!");
+        return true;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateScreenDimensions();
+
+        // Adjust view position to stay within screen bounds after rotation
+        mWindowLayoutParams.x =
+                Math.max(0, Math.min(mWindowLayoutParams.x, mScreenWidth - getWidth()));
+        mWindowLayoutParams.y =
+                Math.max(0, Math.min(mWindowLayoutParams.y, mScreenHeight - getHeight()));
+        mWindowManager.updateViewLayout(this, mWindowLayoutParams);
+    }
+
+    private boolean isSlopExceeded(float deltaX, float deltaY) {
+        return deltaX * deltaX + deltaY * deltaY >= mTouchSlop * mTouchSlop;
+    }
+
+    private void updateScreenDimensions() {
+        Rect windowBounds =
+                mWindowManager.getCurrentWindowMetrics().getBounds();
+        mScreenWidth = windowBounds.width();
+        mScreenHeight = windowBounds.height();
+    }
+
+    public int getTouchpadId() {
+        return mTouchpadId;
+    }
+
+    public WindowManager.LayoutParams getWindowLayoutParams() {
+        return mWindowLayoutParams;
+    }
+}
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
new file mode 100644
index 0000000..c28e74a
--- /dev/null
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -0,0 +1,135 @@
+/*
+ * 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.server.input.debug;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Slog;
+import android.view.InputDevice;
+import android.view.WindowManager;
+
+import com.android.server.input.InputManagerService;
+import com.android.server.input.TouchpadHardwareProperties;
+
+import java.util.Objects;
+
+public class TouchpadDebugViewController implements InputManager.InputDeviceListener {
+
+    private static final String TAG = "TouchpadDebugView";
+
+    private final Context mContext;
+    private final Handler mHandler;
+
+    @Nullable
+    private TouchpadDebugView mTouchpadDebugView;
+
+    private final InputManagerService mInputManagerService;
+    private boolean mTouchpadVisualizerEnabled = false;
+
+    public TouchpadDebugViewController(Context context, Looper looper,
+            InputManagerService inputManagerService) {
+        //TODO(b/363979581): Handle multi-display scenarios
+        mContext = context;
+        mHandler = new Handler(looper);
+        mInputManagerService = inputManagerService;
+    }
+
+    @Override
+    public void onInputDeviceAdded(int deviceId) {
+        final InputManager inputManager = Objects.requireNonNull(
+                mContext.getSystemService(InputManager.class));
+        InputDevice inputDevice = inputManager.getInputDevice(deviceId);
+
+        if (Objects.requireNonNull(inputDevice).supportsSource(
+                InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
+                && mTouchpadVisualizerEnabled) {
+            showDebugView(deviceId);
+        }
+    }
+
+    @Override
+    public void onInputDeviceRemoved(int deviceId) {
+        hideDebugView(deviceId);
+    }
+
+    @Override
+    public void onInputDeviceChanged(int deviceId) {
+    }
+
+    /**
+     * Notify the controller that the touchpad visualizer setting value has changed.
+     * This must be called from the same looper thread as {@code mHandler}.
+     */
+    public void updateTouchpadVisualizerEnabled(boolean touchpadVisualizerEnabled) {
+        if (mTouchpadVisualizerEnabled == touchpadVisualizerEnabled) {
+            return;
+        }
+        mTouchpadVisualizerEnabled = touchpadVisualizerEnabled;
+        final InputManager inputManager = Objects.requireNonNull(
+                mContext.getSystemService(InputManager.class));
+        if (touchpadVisualizerEnabled) {
+            inputManager.registerInputDeviceListener(this, mHandler);
+            for (int deviceId : inputManager.getInputDeviceIds()) {
+                onInputDeviceAdded(deviceId);
+            }
+        } else {
+            if (mTouchpadDebugView != null) {
+                hideDebugView(mTouchpadDebugView.getTouchpadId());
+            }
+            inputManager.unregisterInputDeviceListener(this);
+        }
+    }
+
+    private void showDebugView(int touchpadId) {
+        if (mTouchpadDebugView != null) {
+            return;
+        }
+        final WindowManager wm = Objects.requireNonNull(
+                mContext.getSystemService(WindowManager.class));
+
+        mTouchpadDebugView = new TouchpadDebugView(mContext, touchpadId);
+        final WindowManager.LayoutParams mWindowLayoutParams =
+                mTouchpadDebugView.getWindowLayoutParams();
+
+        wm.addView(mTouchpadDebugView, mWindowLayoutParams);
+        Slog.d(TAG, "Touchpad debug view created.");
+
+        TouchpadHardwareProperties mTouchpadHardwareProperties =
+                mInputManagerService.getTouchpadHardwareProperties(
+                        touchpadId);
+        if (mTouchpadHardwareProperties != null) {
+            Slog.d(TAG, mTouchpadHardwareProperties.toString());
+        } else {
+            Slog.w(TAG, "Failed to retrieve touchpad hardware properties for "
+                    + "device ID: " + touchpadId);
+        }
+    }
+
+    private void hideDebugView(int touchpadId) {
+        if (mTouchpadDebugView == null || mTouchpadDebugView.getTouchpadId() != touchpadId) {
+            return;
+        }
+        final WindowManager wm = Objects.requireNonNull(
+                mContext.getSystemService(WindowManager.class));
+        wm.removeView(mTouchpadDebugView);
+        mTouchpadDebugView = null;
+        Slog.d(TAG, "Touchpad debug view removed.");
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
index 58e3452..e1f26d6 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodManagerImpl.java
@@ -116,11 +116,11 @@
         boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
                 @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
                 @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
-                @SoftInputShowHideReason int reason);
+                @SoftInputShowHideReason int reason, boolean async);
 
         boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
                 @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
-                ResultReceiver resultReceiver, @SoftInputShowHideReason int reason);
+                ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async);
 
         @PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
         void hideSoftInputFromServerForTest();
@@ -132,7 +132,8 @@
                 @Nullable EditorInfo editorInfo, IRemoteInputConnection inputConnection,
                 IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
                 int unverifiedTargetSdkVersion, @UserIdInt int userId,
-                @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+                @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+                boolean useAsyncShowHideMethod);
 
         InputBindResult startInputOrWindowGainedFocus(
                 @StartInputReason int startInputReason, IInputMethodClient client,
@@ -290,17 +291,17 @@
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
+            @SoftInputShowHideReason int reason, boolean async) {
         return mCallback.showSoftInput(client, windowToken, statsToken, flags, lastClickToolType,
-                resultReceiver, reason);
+                resultReceiver, reason, async);
     }
 
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
         return mCallback.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
-                reason);
+                reason, async);
     }
 
     @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
@@ -336,11 +337,13 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+            boolean useAsyncShowHideMethod) {
         mCallback.startInputOrWindowGainedFocusAsync(
                 startInputReason, client, windowToken, startInputFlags, softInputMode,
                 windowFlags, editorInfo, inputConnection, remoteAccessibilityInputConnection,
-                unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq);
+                unverifiedTargetSdkVersion, userId, imeDispatcher, startInputSeq,
+                useAsyncShowHideMethod);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 079b724..ec1993a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -32,6 +33,8 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManagerInternal;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
@@ -99,24 +102,16 @@
     /**
      * A set of status bits regarding the active IME.
      *
-     * <p>This value is a combination of following two bits:</p>
-     * <dl>
-     * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
-     * <dd>
-     *   If this bit is ON, connected IME is ready to accept touch/key events.
-     * </dd>
-     * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
-     * <dd>
-     *   If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
-     * </dd>
-     * </dl>
-     * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
-     * {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
+     * <em>Do not update this value outside of {@link #setImeWindowVis} and
+     * {@link InputMethodBindingController#unbindCurrentMethod}.</em>
      */
-    @GuardedBy("ImfLock.class") private int mImeWindowVis;
-
+    @ImeWindowVisibility
     @GuardedBy("ImfLock.class")
-    private int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
+    private int mImeWindowVis;
+
+    @BackDispositionMode
+    @GuardedBy("ImfLock.class")
+    private int mBackDisposition = BACK_DISPOSITION_DEFAULT;
 
     @Nullable private CountDownLatch mLatchForTesting;
 
@@ -718,22 +713,24 @@
     }
 
     @GuardedBy("ImfLock.class")
-    void setImeWindowVis(int imeWindowVis) {
+    void setImeWindowVis(@ImeWindowVisibility int imeWindowVis) {
         mImeWindowVis = imeWindowVis;
     }
 
+    @ImeWindowVisibility
     @GuardedBy("ImfLock.class")
     int getImeWindowVis() {
         return mImeWindowVis;
     }
 
+    @BackDispositionMode
     @GuardedBy("ImfLock.class")
     int getBackDisposition() {
         return mBackDisposition;
     }
 
     @GuardedBy("ImfLock.class")
-    void setBackDisposition(int backDisposition) {
+    void setBackDisposition(@BackDispositionMode int backDisposition) {
         mBackDisposition = backDisposition;
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 8afbd56..af0ccf9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -86,6 +86,8 @@
 import android.content.res.Resources;
 import android.hardware.input.InputManager;
 import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.media.AudioManagerInternal;
 import android.net.Uri;
 import android.os.Binder;
@@ -324,7 +326,7 @@
      * Figures out the target IME user ID for a given {@link Binder} IPC.
      *
      * @param callingProcessUserId the user ID of the calling process
-     * @return User ID to be used for this {@link Binder} call.
+     * @return the user ID to be used for this {@link Binder} call
      */
     @GuardedBy("ImfLock.class")
     @UserIdInt
@@ -334,6 +336,32 @@
     }
 
     /**
+     * Figures out the targetIMuser for a given {@link Binder} IPC. In case
+     * {@code callingProcessUserId} is SYSTEM user, then it will return the owner of the display
+     * associated with the {@code client} passed as parameter.
+     *
+     * @param callingProcessUserId the user ID of the calling process
+     * @param client               the input method client used to retrieve the user id in case
+     *                             {@code callingProcessUserId} is assigned to SYSTEM user
+     * @return the user ID to be used for this {@link Binder} call
+     */
+    @GuardedBy("ImfLock.class")
+    @UserIdInt
+    @BinderThread
+    private int resolveImeUserIdLocked(@UserIdInt int callingProcessUserId,
+            @NonNull IInputMethodClient client) {
+        if (mConcurrentMultiUserModeEnabled) {
+            if (callingProcessUserId == UserHandle.USER_SYSTEM) {
+                final var clientState = mClientController.getClient(client.asBinder());
+                return mUserManagerInternal.getUserAssignedToDisplay(
+                        clientState.mSelfReportedDisplayId);
+            }
+            return callingProcessUserId;
+        }
+        return mCurrentImeUserId;
+    }
+
+   /**
      * Figures out the target IME user ID associated with the given {@code displayId}.
      *
      * @param displayId the display ID to be queried about
@@ -2618,7 +2646,8 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private boolean shouldShowImeSwitcherLocked(int visibility, @UserIdInt int userId) {
+    private boolean shouldShowImeSwitcherLocked(@ImeWindowVisibility int visibility,
+            @UserIdInt int userId) {
         if (!mShowOngoingImeSwitcherForPhones) return false;
         // When the IME switcher dialog is shown, the IME switcher button should be hidden.
         // TODO(b/305849394): Make mMenuController multi-user aware.
@@ -2722,8 +2751,8 @@
     @BinderThread
     @GuardedBy("ImfLock.class")
     @SuppressWarnings("deprecation")
-    private void setImeWindowStatusLocked(int vis, int backDisposition,
-            @NonNull UserData userData) {
+    private void setImeWindowStatusLocked(@ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, @NonNull UserData userData) {
         final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
 
         final int userId = userData.mUserId;
@@ -2772,7 +2801,7 @@
         final int userId = resolveImeUserIdFromDisplayIdLocked(displayId);
         if (disableImeIcon) {
             final var bindingController = getInputMethodBindingController(userId);
-            updateSystemUiLocked(0, bindingController.getBackDisposition(), userId);
+            updateSystemUiLocked(0 /* vis */, bindingController.getBackDisposition(), userId);
         } else {
             updateSystemUiLocked(userId);
         }
@@ -2787,7 +2816,8 @@
     }
 
     @GuardedBy("ImfLock.class")
-    private void updateSystemUiLocked(int vis, int backDisposition, @UserIdInt int userId) {
+    private void updateSystemUiLocked(@ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, @UserIdInt int userId) {
         // To minimize app compat risk, ignore background users' request for single-user mode.
         // TODO(b/357178609): generalize the logic and remove this special rule.
         if (!mConcurrentMultiUserModeEnabled && userId != mCurrentImeUserId) {
@@ -3033,6 +3063,7 @@
                 intent.putExtra("input_method_id", id);
                 mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
             }
+            bindingController.unbindCurrentMethod();
             unbindCurrentClientLocked(UnbindReason.SWITCH_IME, userId);
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -3057,14 +3088,14 @@
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             int lastClickToolType, ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
+            @SoftInputShowHideReason int reason, boolean async) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#showSoftInput", mDumper);
         synchronized (ImfLock.class) {
             final int uid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(uid);
-            final int userId = resolveImeUserIdLocked(callingUserId);
+            final int userId = resolveImeUserIdLocked(callingUserId, client);
             final boolean result = showSoftInputLocked(client, windowToken, statsToken, flags,
                     lastClickToolType, resultReceiver, reason, uid, userId);
             // When ZeroJankProxy is enabled, the app has already received "true" as the return
@@ -3504,13 +3535,13 @@
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput", mDumper);
         synchronized (ImfLock.class) {
             final int uid = Binder.getCallingUid();
             final int callingUserId = UserHandle.getUserId(uid);
-            final int userId = resolveImeUserIdLocked(callingUserId);
+            final int userId = resolveImeUserIdLocked(callingUserId, client);
             final boolean result = hideSoftInputLocked(client, windowToken, statsToken, flags,
                     resultReceiver, reason, uid, userId);
             // When ZeroJankProxy is enabled, the app has already received "true" as the return
@@ -3646,7 +3677,8 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+            boolean useAsyncShowHideMethod) {
         // implemented by ZeroJankProxy
     }
 
@@ -4229,6 +4261,9 @@
             @NonNull UserData userData) {
         final var bindingController = userData.mBindingController;
         final var currentImi = bindingController.getSelectedMethod();
+        if (currentImi == null) {
+            return false;
+        }
         final ImeSubtypeListItem nextSubtype = userData.mSwitchingController
                 .getNextInputMethodLocked(onlyCurrentIme, currentImi,
                         bindingController.getCurrentSubtype(),
@@ -4246,6 +4281,9 @@
     private boolean shouldOfferSwitchingToNextInputMethodLocked(@NonNull UserData userData) {
         final var bindingController = userData.mBindingController;
         final var currentImi = bindingController.getSelectedMethod();
+        if (currentImi == null) {
+            return false;
+        }
         final ImeSubtypeListItem nextSubtype = userData.mSwitchingController
                 .getNextInputMethodLocked(false /* onlyCurrentIme */, currentImi,
                         bindingController.getCurrentSubtype(),
@@ -6812,7 +6850,8 @@
 
         @BinderThread
         @Override
-        public void setImeWindowStatusAsync(int vis, int backDisposition) {
+        public void setImeWindowStatusAsync(@ImeWindowVisibility int vis,
+                @BackDispositionMode int backDisposition) {
             synchronized (ImfLock.class) {
                 if (!calledWithValidTokenLocked(mToken, mUserData)) {
                     return;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
index d9e9e00..cf2cdc1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java
@@ -33,6 +33,7 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.Slog;
@@ -115,7 +116,11 @@
         final var selectedImi = selectedIndex >= 0 ? items.get(selectedIndex).mImi : null;
         final var languageSettingsIntent = selectedImi != null
                 ? selectedImi.createImeLanguageSettingsActivityIntent() : null;
-        final boolean hasLanguageSettingsButton = languageSettingsIntent != 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);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index c77b768..96b3e08 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -614,242 +614,61 @@
         }
     }
 
-    @VisibleForTesting
-    public static class ControllerImpl {
-
-        @NonNull
-        private final DynamicRotationList mSwitchingAwareRotationList;
-        @NonNull
-        private final StaticRotationList mSwitchingUnawareRotationList;
-        /** List of input methods and subtypes. */
-        @Nullable
-        private final RotationList mRotationList;
-        /** List of input methods and subtypes suitable for hardware keyboards. */
-        @Nullable
-        private final RotationList mHardwareRotationList;
-
-        /**
-         * Whether there was a user action since the last input method and subtype switch.
-         * Used to determine the switching behaviour for {@link #MODE_AUTO}.
-         */
-        private boolean mUserActionSinceSwitch;
-
-        @NonNull
-        public static ControllerImpl createFrom(@Nullable ControllerImpl currentInstance,
-                @NonNull List<ImeSubtypeListItem> sortedEnabledItems,
-                @NonNull List<ImeSubtypeListItem> hardwareKeyboardItems) {
-            final var switchingAwareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
-                    true /* supportsSwitchingToNextInputMethod */);
-            final var switchingUnawareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
-                    false /* supportsSwitchingToNextInputMethod */);
-
-            final DynamicRotationList switchingAwareRotationList;
-            if (currentInstance != null && Objects.equals(
-                    currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
-                    switchingAwareImeSubtypes)) {
-                // Can reuse the current instance.
-                switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
-            } else {
-                switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
-            }
-
-            final StaticRotationList switchingUnawareRotationList;
-            if (currentInstance != null && Objects.equals(
-                    currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
-                    switchingUnawareImeSubtypes)) {
-                // Can reuse the current instance.
-                switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
-            } else {
-                switchingUnawareRotationList = new StaticRotationList(switchingUnawareImeSubtypes);
-            }
-
-            final RotationList rotationList;
-            if (!Flags.imeSwitcherRevamp()) {
-                rotationList = null;
-            } else if (currentInstance != null && currentInstance.mRotationList != null
-                    && Objects.equals(
-                            currentInstance.mRotationList.mItems, sortedEnabledItems)) {
-                // Can reuse the current instance.
-                rotationList = currentInstance.mRotationList;
-            } else {
-                rotationList = new RotationList(sortedEnabledItems);
-            }
-
-            final RotationList hardwareRotationList;
-            if (!Flags.imeSwitcherRevamp()) {
-                hardwareRotationList = null;
-            } else if (currentInstance != null && currentInstance.mHardwareRotationList != null
-                    && Objects.equals(
-                            currentInstance.mHardwareRotationList.mItems, hardwareKeyboardItems)) {
-                // Can reuse the current instance.
-                hardwareRotationList = currentInstance.mHardwareRotationList;
-            } else {
-                hardwareRotationList = new RotationList(hardwareKeyboardItems);
-            }
-
-            return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList,
-                    rotationList, hardwareRotationList);
-        }
-
-        private ControllerImpl(@NonNull DynamicRotationList switchingAwareRotationList,
-                @NonNull StaticRotationList switchingUnawareRotationList,
-                @Nullable RotationList rotationList,
-                @Nullable RotationList hardwareRotationList) {
-            mSwitchingAwareRotationList = switchingAwareRotationList;
-            mSwitchingUnawareRotationList = switchingUnawareRotationList;
-            mRotationList = rotationList;
-            mHardwareRotationList = hardwareRotationList;
-        }
-
-        @Nullable
-        public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme,
-                @Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
-                @SwitchMode int mode, boolean forward) {
-            if (imi == null) {
-                return null;
-            }
-            if (Flags.imeSwitcherRevamp() && mRotationList != null) {
-                return mRotationList.next(imi, subtype, onlyCurrentIme,
-                        isRecency(mode, forward), forward);
-            } else if (imi.supportsSwitchingToNextInputMethod()) {
-                return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
-                        subtype);
-            } else {
-                return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
-                        subtype);
-            }
-        }
-
-        @Nullable
-        public ImeSubtypeListItem getNextInputMethodForHardware(boolean onlyCurrentIme,
-                @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
-                @SwitchMode int mode, boolean forward) {
-            if (Flags.imeSwitcherRevamp() && mHardwareRotationList != null) {
-                return mHardwareRotationList.next(imi, subtype, onlyCurrentIme,
-                        isRecency(mode, forward), forward);
-            }
-            return null;
-        }
-
-        /**
-         * Called when the user took an action that should update the recency of the current
-         * input method and subtype in the switching list.
-         *
-         * @param imi     the currently selected input method.
-         * @param subtype the currently selected input method subtype, if any.
-         * @return {@code true} if the recency was updated, otherwise {@code false}.
-         * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
-         */
-        public boolean onUserActionLocked(@NonNull InputMethodInfo imi,
-                @Nullable InputMethodSubtype subtype) {
-            boolean recencyUpdated = false;
-            if (Flags.imeSwitcherRevamp()) {
-                if (mRotationList != null) {
-                    recencyUpdated |= mRotationList.setMostRecent(imi, subtype);
-                }
-                if (mHardwareRotationList != null) {
-                    recencyUpdated |= mHardwareRotationList.setMostRecent(imi, subtype);
-                }
-                if (recencyUpdated) {
-                    mUserActionSinceSwitch = true;
-                }
-            } else if (imi.supportsSwitchingToNextInputMethod()) {
-                mSwitchingAwareRotationList.onUserAction(imi, subtype);
-            }
-            return recencyUpdated;
-        }
-
-        /** Called when the input method and subtype was changed. */
-        public void onInputMethodSubtypeChanged() {
-            mUserActionSinceSwitch = false;
-        }
-
-        /**
-         * Whether the given mode and direction result in recency or static order.
-         *
-         * <p>{@link #MODE_AUTO} resolves to the recency order for the first forwards switch
-         * after an {@link #onUserActionLocked user action}, and otherwise to the static order.</p>
-         *
-         * @param mode    the switching mode.
-         * @param forward the switching direction.
-         * @return {@code true} for the recency order, otherwise {@code false}.
-         */
-        private boolean isRecency(@SwitchMode int mode, boolean forward) {
-            if (mode == MODE_AUTO && mUserActionSinceSwitch && forward) {
-                return true;
-            } else {
-                return mode == MODE_RECENT;
-            }
-        }
-
-        @NonNull
-        private static List<ImeSubtypeListItem> filterImeSubtypeList(
-                @NonNull List<ImeSubtypeListItem> items,
-                boolean supportsSwitchingToNextInputMethod) {
-            final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
-            final int numItems = items.size();
-            for (int i = 0; i < numItems; i++) {
-                final ImeSubtypeListItem item = items.get(i);
-                if (item.mImi.supportsSwitchingToNextInputMethod()
-                        == supportsSwitchingToNextInputMethod) {
-                    result.add(item);
-                }
-            }
-            return result;
-        }
-
-        protected void dump(@NonNull Printer pw, @NonNull String prefix) {
-            pw.println(prefix + "mSwitchingAwareRotationList:");
-            mSwitchingAwareRotationList.dump(pw, prefix + "  ");
-            pw.println(prefix + "mSwitchingUnawareRotationList:");
-            mSwitchingUnawareRotationList.dump(pw, prefix + "  ");
-            if (Flags.imeSwitcherRevamp()) {
-                if (mRotationList != null) {
-                    pw.println(prefix + "mRotationList:");
-                    mRotationList.dump(pw, prefix + "  ");
-                }
-                if (mHardwareRotationList != null) {
-                    pw.println(prefix + "mHardwareRotationList:");
-                    mHardwareRotationList.dump(pw, prefix + "  ");
-                }
-                pw.println(prefix + "User action since last switch: " + mUserActionSinceSwitch);
-            }
-        }
-    }
-
     @NonNull
-    private ControllerImpl mController;
-
-    InputMethodSubtypeSwitchingController() {
-        mController = ControllerImpl.createFrom(null, Collections.emptyList(),
-                Collections.emptyList());
-    }
+    private DynamicRotationList mSwitchingAwareRotationList =
+            new DynamicRotationList(Collections.emptyList());
+    @NonNull
+    private StaticRotationList mSwitchingUnawareRotationList =
+            new StaticRotationList(Collections.emptyList());
+    /** List of input methods and subtypes. */
+    @NonNull
+    private RotationList mRotationList = new RotationList(Collections.emptyList());
+    /** List of input methods and subtypes suitable for hardware keyboards. */
+    @NonNull
+    private RotationList mHardwareRotationList = new RotationList(Collections.emptyList());
 
     /**
-     * Called when the user took an action that should update the recency of the current
-     * input method and subtype in the switching list.
-     *
-     * @param imi     the currently selected input method.
-     * @param subtype the currently selected input method subtype, if any.
-     * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
+     * Whether there was a user action since the last input method and subtype switch.
+     * Used to determine the switching behaviour for {@link #MODE_AUTO}.
      */
-    public void onUserActionLocked(@NonNull InputMethodInfo imi,
-            @Nullable InputMethodSubtype subtype) {
-        mController.onUserActionLocked(imi, subtype);
-    }
+    private boolean mUserActionSinceSwitch;
 
-    /** Called when the input method and subtype was changed. */
-    public void onInputMethodSubtypeChanged() {
-        mController.onInputMethodSubtypeChanged();
-    }
+    /**
+     * Updates the list of input methods and subtypes used for switching. If the given items are
+     * equal to the existing ones (regardless of recency order), the update is skipped and the
+     * current recency order is kept. Otherwise, the recency order is reset.
+     *
+     * @param sortedEnabledItems    the sorted list of enabled input methods and subtypes.
+     * @param hardwareKeyboardItems the unsorted list of enabled input method and subtypes
+     *                              suitable for hardware keyboards.
+     */
+    @VisibleForTesting
+    void update(@NonNull List<ImeSubtypeListItem> sortedEnabledItems,
+            @NonNull List<ImeSubtypeListItem> hardwareKeyboardItems) {
+        final var switchingAwareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+                true /* supportsSwitchingToNextInputMethod */);
+        final var switchingUnawareImeSubtypes = filterImeSubtypeList(sortedEnabledItems,
+                false /* supportsSwitchingToNextInputMethod */);
 
-    public void resetCircularListLocked(@NonNull Context context,
-            @NonNull InputMethodSettings settings) {
-        mController = ControllerImpl.createFrom(mController,
-                getSortedInputMethodAndSubtypeList(
-                        false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
-                        false /* forImeMenu */, context, settings),
-                getInputMethodAndSubtypeListForHardwareKeyboard(context, settings));
+        if (!Objects.equals(mSwitchingAwareRotationList.mImeSubtypeList,
+                switchingAwareImeSubtypes)) {
+            mSwitchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
+        }
+
+        if (!Objects.equals(mSwitchingUnawareRotationList.mImeSubtypeList,
+                switchingUnawareImeSubtypes)) {
+            mSwitchingUnawareRotationList = new StaticRotationList(switchingUnawareImeSubtypes);
+        }
+
+        if (Flags.imeSwitcherRevamp()
+                && !Objects.equals(mRotationList.mItems, sortedEnabledItems)) {
+            mRotationList = new RotationList(sortedEnabledItems);
+        }
+
+        if (Flags.imeSwitcherRevamp()
+                && !Objects.equals(mHardwareRotationList.mItems, hardwareKeyboardItems)) {
+            mHardwareRotationList = new RotationList(hardwareKeyboardItems);
+        }
     }
 
     /**
@@ -867,9 +686,18 @@
      */
     @Nullable
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
-            @Nullable InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
+            @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
             @SwitchMode int mode, boolean forward) {
-        return mController.getNextInputMethod(onlyCurrentIme, imi, subtype, mode, forward);
+        if (Flags.imeSwitcherRevamp()) {
+            return mRotationList.next(imi, subtype, onlyCurrentIme,
+                    isRecency(mode, forward), forward);
+        } else if (imi.supportsSwitchingToNextInputMethod()) {
+            return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                    subtype);
+        } else {
+            return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
+                    subtype);
+        }
     }
 
     /**
@@ -890,11 +718,98 @@
     public ImeSubtypeListItem getNextInputMethodForHardware(boolean onlyCurrentIme,
             @NonNull InputMethodInfo imi, @Nullable InputMethodSubtype subtype,
             @SwitchMode int mode, boolean forward) {
-        return mController.getNextInputMethodForHardware(onlyCurrentIme, imi, subtype, mode,
-                forward);
+        if (Flags.imeSwitcherRevamp()) {
+            return mHardwareRotationList.next(imi, subtype, onlyCurrentIme,
+                    isRecency(mode, forward), forward);
+        }
+        return null;
     }
 
-    public void dump(@NonNull Printer pw, @NonNull String prefix) {
-        mController.dump(pw, prefix);
+    /**
+     * Called when the user took an action that should update the recency of the current
+     * input method and subtype in the switching list.
+     *
+     * @param imi     the currently selected input method.
+     * @param subtype the currently selected input method subtype, if any.
+     * @return {@code true} if the recency was updated, otherwise {@code false}.
+     * @see android.inputmethodservice.InputMethodServiceInternal#notifyUserActionIfNecessary()
+     */
+    public boolean onUserActionLocked(@NonNull InputMethodInfo imi,
+            @Nullable InputMethodSubtype subtype) {
+        boolean recencyUpdated = false;
+        if (Flags.imeSwitcherRevamp()) {
+            recencyUpdated |= mRotationList.setMostRecent(imi, subtype);
+            recencyUpdated |= mHardwareRotationList.setMostRecent(imi, subtype);
+            if (recencyUpdated) {
+                mUserActionSinceSwitch = true;
+            }
+        } else if (imi.supportsSwitchingToNextInputMethod()) {
+            mSwitchingAwareRotationList.onUserAction(imi, subtype);
+        }
+        return recencyUpdated;
+    }
+
+    /** Called when the input method and subtype was changed. */
+    public void onInputMethodSubtypeChanged() {
+        mUserActionSinceSwitch = false;
+    }
+
+    /**
+     * Whether the given mode and direction result in recency or static order.
+     *
+     * <p>{@link #MODE_AUTO} resolves to the recency order for the first forwards switch
+     * after an {@link #onUserActionLocked user action}, and otherwise to the static order.</p>
+     *
+     * @param mode    the switching mode.
+     * @param forward the switching direction.
+     * @return {@code true} for the recency order, otherwise {@code false}.
+     */
+    private boolean isRecency(@SwitchMode int mode, boolean forward) {
+        if (mode == MODE_AUTO && mUserActionSinceSwitch && forward) {
+            return true;
+        } else {
+            return mode == MODE_RECENT;
+        }
+    }
+
+    @NonNull
+    private static List<ImeSubtypeListItem> filterImeSubtypeList(
+            @NonNull List<ImeSubtypeListItem> items,
+            boolean supportsSwitchingToNextInputMethod) {
+        final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
+        final int numItems = items.size();
+        for (int i = 0; i < numItems; i++) {
+            final ImeSubtypeListItem item = items.get(i);
+            if (item.mImi.supportsSwitchingToNextInputMethod()
+                    == supportsSwitchingToNextInputMethod) {
+                result.add(item);
+            }
+        }
+        return result;
+    }
+
+    void dump(@NonNull Printer pw, @NonNull String prefix) {
+        pw.println(prefix + "mSwitchingAwareRotationList:");
+        mSwitchingAwareRotationList.dump(pw, prefix + "  ");
+        pw.println(prefix + "mSwitchingUnawareRotationList:");
+        mSwitchingUnawareRotationList.dump(pw, prefix + "  ");
+        if (Flags.imeSwitcherRevamp()) {
+            pw.println(prefix + "mRotationList:");
+            mRotationList.dump(pw, prefix + "  ");
+            pw.println(prefix + "mHardwareRotationList:");
+            mHardwareRotationList.dump(pw, prefix + "  ");
+            pw.println(prefix + "User action since last switch: " + mUserActionSinceSwitch);
+        }
+    }
+
+    InputMethodSubtypeSwitchingController() {
+    }
+
+    public void resetCircularListLocked(@NonNull Context context,
+            @NonNull InputMethodSettings settings) {
+        update(getSortedInputMethodAndSubtypeList(
+                        false /* includeAuxiliarySubtypes */, false /* isScreenLocked */,
+                        false /* forImeMenu */, context, settings),
+                getInputMethodAndSubtypeListForHardwareKeyboard(context, settings));
     }
 }
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
index c940a9c..214aa1d 100644
--- a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -77,6 +77,7 @@
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A proxy that processes all {@link IInputMethodManager} calls asynchronously.
@@ -175,19 +176,45 @@
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
             @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
             @MotionEvent.ToolType int lastClickToolType, ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
-        offload(() -> mInner.showSoftInput(
-                client, windowToken, statsToken, flags, lastClickToolType, resultReceiver, reason));
-        return true;
+            @SoftInputShowHideReason int reason, boolean async) {
+
+        if (async) {
+            offload(() -> mInner.showSoftInput(
+                    client, windowToken, statsToken, flags, lastClickToolType, resultReceiver,
+                    reason, async));
+            return true;
+        } else {
+            final var future = CompletableFuture.supplyAsync(
+                    () -> mInner.showSoftInput(
+                            client,
+                            windowToken,
+                            statsToken,
+                            flags,
+                            lastClickToolType,
+                            resultReceiver,
+                            reason,
+                            async),
+                    this::offload);
+            return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+        }
     }
 
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
-        offload(() -> mInner.hideSoftInput(
-                client, windowToken, statsToken, flags, resultReceiver, reason));
-        return true;
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason, boolean async) {
+
+        if (async) {
+            offload(() -> mInner.hideSoftInput(
+                    client, windowToken, statsToken, flags, resultReceiver, reason, async));
+            return true;
+        } else {
+            final var future = CompletableFuture.supplyAsync(
+                    () -> mInner.hideSoftInput(
+                            client, windowToken, statsToken, flags, resultReceiver, reason, async),
+                    this::offload);
+            return future.completeOnTimeout(false, 1, TimeUnit.SECONDS).join();
+        }
     }
 
     @Override
@@ -207,7 +234,8 @@
             IRemoteInputConnection inputConnection,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             int unverifiedTargetSdkVersion, @UserIdInt int userId,
-            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq,
+            boolean useAsyncShowHideMethod) {
         offload(() -> {
             InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
                     windowToken, startInputFlags, softInputMode, windowFlags,
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
index be8d2e1..5c05fce 100644
--- a/services/core/java/com/android/server/integrity/TEST_MAPPING
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.integrity."
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_integrity"
     },
     {
       "name": "GtsSecurityHostTestCases",
diff --git a/services/core/java/com/android/server/lights/TEST_MAPPING b/services/core/java/com/android/server/lights/TEST_MAPPING
index 17b98ce8..1d2cd3c 100644
--- a/services/core/java/com/android/server/lights/TEST_MAPPING
+++ b/services/core/java/com/android/server/lights/TEST_MAPPING
@@ -9,11 +9,7 @@
       ]
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.lights"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksServicesTests_android_server_lights"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/locales/TEST_MAPPING b/services/core/java/com/android/server/locales/TEST_MAPPING
index fd8cddc..26e4685 100644
--- a/services/core/java/com/android/server/locales/TEST_MAPPING
+++ b/services/core/java/com/android/server/locales/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.locales."
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_locales"
     },
     {
       "name": "CtsLocaleManagerHostTestCases"
diff --git a/services/core/java/com/android/server/location/TEST_MAPPING b/services/core/java/com/android/server/location/TEST_MAPPING
index f5deb2b..64b1ed2 100644
--- a/services/core/java/com/android/server/location/TEST_MAPPING
+++ b/services/core/java/com/android/server/location/TEST_MAPPING
@@ -16,10 +16,7 @@
       "name": "CtsLocationNoneTestCases"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [{
-        "include-filter": "com.android.server.location"
-      }]
+      "name": "FrameworksMockingServicesTests_location"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/location/contexthub/TEST_MAPPING b/services/core/java/com/android/server/location/contexthub/TEST_MAPPING
index 2f6aa53..85ea5a4 100644
--- a/services/core/java/com/android/server/location/contexthub/TEST_MAPPING
+++ b/services/core/java/com/android/server/location/contexthub/TEST_MAPPING
@@ -1,21 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.location.contexthub."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_location_contexthub_Presubmit"
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index db4d68b..4fcf27d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -756,15 +756,9 @@
 
         unlockIntent.setFlags(
                 Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        PendingIntent intent;
-        if (android.app.admin.flags.Flags.hsumUnlockNotificationFix()) {
-            intent = PendingIntent.getActivityAsUser(mContext, 0, unlockIntent,
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED,
-                    null, parent);
-        } else {
-            intent = PendingIntent.getActivity(mContext, 0, unlockIntent,
-                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
-        }
+        PendingIntent intent = PendingIntent.getActivityAsUser(mContext, 0, unlockIntent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED,
+                null, parent);
 
         Slogf.d(TAG, "Showing encryption notification for user %d; reason: %s",
                 user.getIdentifier(), reason);
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index d0b8990..f44b852 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -142,7 +142,6 @@
             ERROR_KEYSTORE_FAILURE,
             ERROR_NO_NETWORK,
             ERROR_TIMEOUT_EXHAUSTED,
-            ERROR_NO_REBOOT_ESCROW_DATA,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface RebootEscrowErrorCode {
@@ -158,7 +157,6 @@
     static final int ERROR_KEYSTORE_FAILURE = 7;
     static final int ERROR_NO_NETWORK = 8;
     static final int ERROR_TIMEOUT_EXHAUSTED = 9;
-    static final int ERROR_NO_REBOOT_ESCROW_DATA = 10;
 
     private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
 
@@ -507,9 +505,6 @@
         if (rebootEscrowUsers.isEmpty()) {
             Slog.i(TAG, "No reboot escrow data found for users,"
                     + " skipping loading escrow data");
-            setLoadEscrowDataErrorCode(ERROR_NO_REBOOT_ESCROW_DATA, retryHandler);
-            reportMetricOnRestoreComplete(
-                    /* success= */ false, /* attemptCount= */ 1, retryHandler);
             clearMetricsStorage();
             return;
         }
diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING
index 256d9ba..ffbdf7f 100644
--- a/services/core/java/com/android/server/locksettings/TEST_MAPPING
+++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING
@@ -14,15 +14,7 @@
     ],
     "presubmit": [
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.locksettings."
-                },
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_locksettings"
         }
     ],
     "postsubmit": [
diff --git a/services/core/java/com/android/server/logcat/TEST_MAPPING b/services/core/java/com/android/server/logcat/TEST_MAPPING
index 9041552..5b07cd9 100644
--- a/services/core/java/com/android/server/logcat/TEST_MAPPING
+++ b/services/core/java/com/android/server/logcat/TEST_MAPPING
@@ -1,11 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.logcat"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksServicesTests_android_server_logcat_Presubmit"
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 1070f2f..e1b8e9f 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1364,14 +1364,14 @@
             if (manager == null || manager.mLastSessionCreationRequest == null) {
                 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
                         + "Ignoring unknown request.");
-                userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId);
+                routerRecord.notifySessionCreationFailed(requestId);
                 return;
             }
             if (!TextUtils.equals(manager.mLastSessionCreationRequest.mOldSession.getId(),
                     oldSession.getId())) {
                 Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
                         + "Ignoring unmatched routing session.");
-                userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId);
+                routerRecord.notifySessionCreationFailed(requestId);
                 return;
             }
             if (!TextUtils.equals(manager.mLastSessionCreationRequest.mRoute.getId(),
@@ -1384,7 +1384,7 @@
                 } else {
                     Slog.w(TAG, "requestCreateSessionWithRouter2Locked: "
                             + "Ignoring unmatched route.");
-                    userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId);
+                    routerRecord.notifySessionCreationFailed(requestId);
                     return;
                 }
             }
@@ -1396,7 +1396,7 @@
                     && !TextUtils.equals(route.getId(), defaultRouteId)) {
                 Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to"
                         + route);
-                userHandler.notifySessionCreationFailedToRouter(routerRecord, requestId);
+                routerRecord.notifySessionCreationFailed(requestId);
                 return;
             }
         }
@@ -1484,8 +1484,7 @@
                 && !TextUtils.equals(route.getId(), defaultRouteId)) {
             userHandler.sendMessage(
                     obtainMessage(
-                            UserHandler::notifySessionCreationFailedToRouter,
-                            userHandler,
+                            RouterRecord::notifySessionCreationFailed,
                             routerRecord,
                             toOriginalRequestId(DUMMY_REQUEST_ID)));
         } else {
@@ -1762,12 +1761,7 @@
         if (routerRecord == null) {
             Slog.w(TAG, "requestCreateSessionWithManagerLocked: Ignoring session creation for "
                     + "unknown router.");
-            try {
-                managerRecord.mManager.notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "requestCreateSessionWithManagerLocked: Failed to notify failure. "
-                        + "Manager probably died.");
-            }
+            managerRecord.notifyRequestFailed(requestId, REASON_UNKNOWN_ERROR);
             return;
         }
 
@@ -1780,10 +1774,8 @@
                             "requestCreateSessionWithManagerLocked: Notifying failure for pending"
                                 + " session creation request - oldSession: %s, route: %s",
                             lastRequest.mOldSession, lastRequest.mRoute));
-            managerRecord.mUserRecord.mHandler.notifyRequestFailedToManager(
-                    managerRecord.mManager,
-                    toOriginalRequestId(lastRequest.mManagerRequestId),
-                    REASON_UNKNOWN_ERROR);
+            managerRecord.notifyRequestFailed(
+                    toOriginalRequestId(lastRequest.mManagerRequestId), REASON_UNKNOWN_ERROR);
         }
         managerRecord.mLastSessionCreationRequest = new SessionCreationRequest(routerRecord,
                 MediaRoute2ProviderService.REQUEST_ID_NONE, uniqueRequestId,
@@ -1793,15 +1785,12 @@
         // As a return, media router will request to create a session.
         routerRecord.mUserRecord.mHandler.sendMessage(
                 obtainMessage(
-                        UserHandler::requestRouterCreateSessionOnHandler,
-                        routerRecord.mUserRecord.mHandler,
-                        uniqueRequestId,
+                        RouterRecord::requestCreateSessionByManager,
                         routerRecord,
                         managerRecord,
+                        uniqueRequestId,
                         oldSession,
-                        route,
-                        transferInitiatorUserHandle,
-                        transferInitiatorPackageName));
+                        route));
     }
 
     @GuardedBy("mLock")
@@ -2256,6 +2245,71 @@
         }
 
         /**
+         * Notifies the corresponding router of a request failure.
+         *
+         * @param requestId The id of the request that failed.
+         */
+        public void notifySessionCreationFailed(int requestId) {
+            try {
+                mRouter.notifySessionCreated(requestId, /* sessionInfo= */ null);
+            } catch (RemoteException ex) {
+                Slog.w(
+                        TAG,
+                        "Failed to notify router of the session creation failure."
+                                + " Router probably died.",
+                        ex);
+            }
+        }
+
+        /**
+         * Notifies the corresponding router of the release of the given {@link RoutingSessionInfo}.
+         */
+        public void notifySessionReleased(RoutingSessionInfo sessionInfo) {
+            try {
+                mRouter.notifySessionReleased(sessionInfo);
+            } catch (RemoteException ex) {
+                Slog.w(
+                        TAG,
+                        "Failed to notify router of the session release. Router probably died.",
+                        ex);
+            }
+        }
+
+        /**
+         * Sends the corresponding router a {@link RoutingSessionInfo session} creation request,
+         * with the given {@link MediaRoute2Info} as the initial member.
+         *
+         * <p>Must be called on the thread of the corresponding {@link UserHandler}.
+         *
+         * @param managerRecord The record of the manager that made the request.
+         * @param uniqueRequestId The id of the request.
+         * @param oldSession The session from which the transfer originated.
+         * @param route The initial route member of the session to create.
+         */
+        public void requestCreateSessionByManager(
+                ManagerRecord managerRecord,
+                long uniqueRequestId,
+                RoutingSessionInfo oldSession,
+                MediaRoute2Info route) {
+            try {
+                if (route.isSystemRoute() && !hasSystemRoutingPermission()) {
+                    // The router lacks permission to modify system routing, so we hide system
+                    // route info from them.
+                    route = mUserRecord.mHandler.mSystemProvider.getDefaultRoute();
+                }
+                mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
+            } catch (RemoteException ex) {
+                Slog.w(
+                        TAG,
+                        "getSessionHintsForCreatingSessionOnHandler: "
+                                + "Failed to request. Router probably died.",
+                        ex);
+                managerRecord.notifyRequestFailed(
+                        toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
+            }
+        }
+
+        /**
          * Sends the corresponding router an update for the given session.
          *
          * <p>Note: These updates are not directly visible to the app.
@@ -2360,6 +2414,25 @@
             }
         }
 
+        /**
+         * Notifies the corresponding manager of a request failure.
+         *
+         * <p>Must be called on the thread of the corresponding {@link UserHandler}.
+         *
+         * @param requestId The id of the request that failed.
+         * @param reason The reason of the failure. One of
+         */
+        public void notifyRequestFailed(int requestId, int reason) {
+            try {
+                mManager.notifyRequestFailed(requestId, reason);
+            } catch (RemoteException ex) {
+                Slog.w(
+                        TAG,
+                        "Failed to notify manager of the request failure. Manager probably died.",
+                        ex);
+            }
+        }
+
         private void updateScanningState(@ScanningState int scanningState) {
             if (mScanningState == scanningState) {
                 return;
@@ -2738,30 +2811,6 @@
             return -1;
         }
 
-        private void requestRouterCreateSessionOnHandler(
-                long uniqueRequestId,
-                @NonNull RouterRecord routerRecord,
-                @NonNull ManagerRecord managerRecord,
-                @NonNull RoutingSessionInfo oldSession,
-                @NonNull MediaRoute2Info route,
-                @NonNull UserHandle transferInitiatorUserHandle,
-                @NonNull String transferInitiatorPackageName) {
-            try {
-                if (route.isSystemRoute() && !routerRecord.hasSystemRoutingPermission()) {
-                    // The router lacks permission to modify system routing, so we hide system
-                    // route info from them.
-                    route = mSystemProvider.getDefaultRoute();
-                }
-                routerRecord.mRouter.requestCreateSessionByManager(
-                        uniqueRequestId, oldSession, route);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
-                        + "Failed to request. Router probably died.", ex);
-                notifyRequestFailedToManager(managerRecord.mManager,
-                        toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
-            }
-        }
-
         private void requestCreateSessionWithRouter2OnHandler(
                 long uniqueRequestId,
                 long managerRequestId,
@@ -2774,8 +2823,7 @@
             if (provider == null) {
                 Slog.w(TAG, "requestCreateSessionWithRouter2OnHandler: Ignoring session "
                         + "creation request since no provider found for given route=" + route);
-                notifySessionCreationFailedToRouter(routerRecord,
-                        toOriginalRequestId(uniqueRequestId));
+                routerRecord.notifySessionCreationFailed(toOriginalRequestId(uniqueRequestId));
                 return;
             }
 
@@ -3054,7 +3102,7 @@
                         + sessionInfo);
                 return;
             }
-            notifySessionReleasedToRouter(routerRecord, sessionInfo);
+            routerRecord.notifySessionReleased(sessionInfo);
         }
 
         private void onRequestFailedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -3073,8 +3121,7 @@
             final int requesterId = toRequesterId(uniqueRequestId);
             ManagerRecord manager = findManagerWithId(requesterId);
             if (manager != null) {
-                notifyRequestFailedToManager(
-                        manager.mManager, toOriginalRequestId(uniqueRequestId), reason);
+                manager.notifyRequestFailed(toOriginalRequestId(uniqueRequestId), reason);
             }
 
             // Currently, only manager records can get notified of failures.
@@ -3109,40 +3156,19 @@
             // Notify the requester about the failure.
             // The call should be made by either MediaRouter2 or MediaRouter2Manager.
             if (matchingRequest.mManagerRequestId == MediaRouter2Manager.REQUEST_ID_NONE) {
-                notifySessionCreationFailedToRouter(
-                        matchingRequest.mRouterRecord, toOriginalRequestId(uniqueRequestId));
+                matchingRequest.mRouterRecord.notifySessionCreationFailed(
+                        toOriginalRequestId(uniqueRequestId));
             } else {
                 final int requesterId = toRequesterId(matchingRequest.mManagerRequestId);
                 ManagerRecord manager = findManagerWithId(requesterId);
                 if (manager != null) {
-                    notifyRequestFailedToManager(manager.mManager,
+                    manager.notifyRequestFailed(
                             toOriginalRequestId(matchingRequest.mManagerRequestId), reason);
                 }
             }
             return true;
         }
 
-        private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord,
-                int requestId) {
-            try {
-                routerRecord.mRouter.notifySessionCreated(requestId,
-                        /* sessionInfo= */ null);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify router of the session creation failure."
-                        + " Router probably died.", ex);
-            }
-        }
-
-        private void notifySessionReleasedToRouter(@NonNull RouterRecord routerRecord,
-                @NonNull RoutingSessionInfo sessionInfo) {
-            try {
-                routerRecord.mRouter.notifySessionReleased(sessionInfo);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify router of the session release."
-                        + " Router probably died.", ex);
-            }
-        }
-
         private List<IMediaRouter2Manager> getManagers() {
             final List<IMediaRouter2Manager> managers = new ArrayList<>();
             MediaRouter2ServiceImpl service = mServiceRef.get();
@@ -3379,16 +3405,6 @@
             //    need to update routers other than the one making the update.
         }
 
-        private void notifyRequestFailedToManager(@NonNull IMediaRouter2Manager manager,
-                int requestId, int reason) {
-            try {
-                manager.notifyRequestFailed(requestId, reason);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify manager of the request failure."
-                        + " Manager probably died.", ex);
-            }
-        }
-
         private void updateDiscoveryPreferenceOnHandler() {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a44e553..66e61c0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,9 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.service.notification.Condition.STATE_TRUE;
-import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
-
 import android.app.INotificationManager;
 import android.app.NotificationManager;
 import android.content.ComponentName;
@@ -322,20 +319,7 @@
                 final Condition c = conditions[i];
                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
                 r.info = info;
-                if (android.app.Flags.modesUi()) {
-                    // if user turned on the mode, ignore the update unless the app also wants the
-                    // mode on. this will update the origin of the mode and let the owner turn it
-                    // off when the context ends
-                    if (r.condition != null && r.condition.source == ORIGIN_USER_IN_SYSTEMUI) {
-                        if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) {
-                            r.condition = c;
-                        }
-                    } else {
-                        r.condition = c;
-                    }
-                } else {
-                    r.condition = c;
-                }
+                r.condition = c;
             }
         }
         final int N = conditions.length;
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index c8cb54f..bad959a 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -19,6 +19,9 @@
 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_NIGHT;
 import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
 
+import static com.android.server.notification.ZenLog.traceApplyDeviceEffect;
+import static com.android.server.notification.ZenLog.traceScheduleApplyDeviceEffect;
+
 import android.app.UiModeManager;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
@@ -77,6 +80,8 @@
             if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
                     != effects.shouldSuppressAmbientDisplay()) {
                 try {
+                    traceApplyDeviceEffect("suppressAmbientDisplay",
+                            effects.shouldSuppressAmbientDisplay());
                     mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
                             effects.shouldSuppressAmbientDisplay());
                 } catch (Exception e) {
@@ -87,6 +92,8 @@
             if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
                 if (mColorDisplayManager != null) {
                     try {
+                        traceApplyDeviceEffect("displayGrayscale",
+                                effects.shouldDisplayGrayscale());
                         mColorDisplayManager.setSaturationLevel(
                                 effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
                                         : SATURATION_LEVEL_FULL_COLOR);
@@ -99,6 +106,7 @@
             if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
                 if (mWallpaperManager != null) {
                     try {
+                        traceApplyDeviceEffect("dimWallpaper", effects.shouldDimWallpaper());
                         mWallpaperManager.setWallpaperDimAmount(
                                 effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
                                         : WALLPAPER_DIM_AMOUNT_NORMAL);
@@ -134,6 +142,7 @@
             unregisterScreenOffReceiver();
             updateNightModeImmediately(useNightMode);
         } else {
+            traceScheduleApplyDeviceEffect("nightMode", useNightMode);
             registerScreenOffReceiver();
         }
     }
@@ -150,6 +159,7 @@
     private void updateNightModeImmediately(boolean useNightMode) {
         Binder.withCleanCallingIdentity(() -> {
             try {
+                traceApplyDeviceEffect("nightMode", useNightMode);
                 mUiModeManager.setAttentionModeThemeOverlay(
                         useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
                                 : MODE_ATTENTION_THEME_OVERLAY_OFF);
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 1cdab44..4fa7112 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -118,11 +118,51 @@
     private final ArrayMap<FullyQualifiedGroupKey, ArrayMap<String, NotificationAttributes>>
             mAggregatedNotifications = new ArrayMap<>();
 
-    private static final List<NotificationSectioner> NOTIFICATION_SHADE_SECTIONS = List.of(
-        new NotificationSectioner("AlertingSection", 0, (record) ->
-            record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
-        new NotificationSectioner("SilentSection", 1, (record) ->
-            record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT));
+    private static List<NotificationSectioner> NOTIFICATION_SHADE_SECTIONS =
+            getNotificationShadeSections();
+
+    private static List<NotificationSectioner> getNotificationShadeSections() {
+        ArrayList<NotificationSectioner> sectionsList = new ArrayList<>();
+        if (android.service.notification.Flags.notificationClassification()) {
+            sectionsList.addAll(List.of(
+                new NotificationSectioner("PromotionsSection", 0, (record) ->
+                    NotificationChannel.PROMOTIONS_ID.equals(record.getChannel().getId())),
+                new NotificationSectioner("SocialSection", 0, (record) ->
+                    NotificationChannel.SOCIAL_MEDIA_ID.equals(record.getChannel().getId())),
+                new NotificationSectioner("NewsSection", 0, (record) ->
+                    NotificationChannel.NEWS_ID.equals(record.getChannel().getId())),
+                new NotificationSectioner("RecsSection", 0, (record) ->
+                    NotificationChannel.RECS_ID.equals(record.getChannel().getId()))));
+        }
+
+        if (Flags.notificationForceGroupConversations()) {
+            // add priority people section
+            sectionsList.add(new NotificationSectioner("PeopleSection(priority)", 1, (record) ->
+                    record.isConversation() && record.getChannel().isImportantConversation()));
+
+            if (android.app.Flags.sortSectionByTime()) {
+                // add single people (alerting) section
+                sectionsList.add(new NotificationSectioner("PeopleSection", 0,
+                        NotificationRecord::isConversation));
+            } else {
+                // add people alerting section
+                sectionsList.add(new NotificationSectioner("PeopleSection(alerting)", 1, (record) ->
+                        record.isConversation()
+                        && record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT));
+                // add people silent section
+                sectionsList.add(new NotificationSectioner("PeopleSection(silent)", 1, (record) ->
+                        record.isConversation()
+                        && record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT));
+            }
+        }
+
+        sectionsList.addAll(List.of(
+            new NotificationSectioner("AlertingSection", 0, (record) ->
+                record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT),
+            new NotificationSectioner("SilentSection", 1, (record) ->
+                record.getImportance() < NotificationManager.IMPORTANCE_DEFAULT)));
+        return sectionsList;
+    }
 
     public GroupHelper(Context context, PackageManager packageManager, int autoGroupAtCount,
             int autoGroupSparseGroupsAtCount, Callback callback) {
@@ -131,6 +171,7 @@
         mContext = context;
         mPackageManager = packageManager;
         mAutogroupSparseGroupsAtCount = autoGroupSparseGroupsAtCount;
+        NOTIFICATION_SHADE_SECTIONS = getNotificationShadeSections();
     }
 
     private String generatePackageKey(int userId, String pkg) {
@@ -808,61 +849,19 @@
                 }
             }
 
+            // The list of notification operations required after the channel update
             final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
 
-            final Set<FullyQualifiedGroupKey> oldGroups =
-                    new HashSet<>(mAggregatedNotifications.keySet());
-            for (FullyQualifiedGroupKey oldFullAggKey : oldGroups) {
-                // Only check aggregate groups that match the same userId & packageName
-                if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
-                    final ArrayMap<String, NotificationAttributes> notificationsInAggGroup =
-                            mAggregatedNotifications.get(oldFullAggKey);
-                    if (notificationsInAggGroup == null) {
-                        continue;
-                    }
+            // Check any already auto-grouped notifications that may need to be re-grouped
+            // after the channel update
+            notificationsToMove.addAll(
+                    getAutogroupedNotificationsMoveOps(userId, pkgName,
+                        notificationsToCheck));
 
-                    FullyQualifiedGroupKey newFullAggregateGroupKey = null;
-                    for (String key : notificationsInAggGroup.keySet()) {
-                        if (notificationsToCheck.get(key) != null) {
-                            // check if section changes
-                            NotificationSectioner sectioner = getSection(
-                                    notificationsToCheck.get(key));
-                            if (sectioner == null) {
-                                continue;
-                            }
-                            newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
-                                    sectioner);
-                            if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
-                                if (DEBUG) {
-                                    Log.i(TAG, "Change section on channel update: " + key);
-                                }
-                                notificationsToMove.add(
-                                        new NotificationMoveOp(notificationsToCheck.get(key),
-                                            oldFullAggKey, newFullAggregateGroupKey));
-                            }
-                        }
-                    }
-
-                    if (newFullAggregateGroupKey != null) {
-                        // Add any notifications left ungrouped to the new section
-                        ArrayMap<String, NotificationAttributes> ungrouped =
-                            mUngroupedAbuseNotifications.get(newFullAggregateGroupKey);
-                        if (ungrouped != null) {
-                            for (NotificationRecord r : notificationList) {
-                                if (ungrouped.containsKey(r.getKey())) {
-                                    if (DEBUG) {
-                                        Log.i(TAG, "Add previously ungrouped: " + r);
-                                    }
-                                    notificationsToMove.add(
-                                        new NotificationMoveOp(r, null, newFullAggregateGroupKey));
-                                }
-                            }
-                            //Cleanup mUngroupedAbuseNotifications
-                            mUngroupedAbuseNotifications.remove(newFullAggregateGroupKey);
-                        }
-                    }
-                }
-            }
+            // Check any ungrouped notifications that may need to be auto-grouped
+            // after the channel update
+            notificationsToMove.addAll(
+                    getUngroupedNotificationsMoveOps(userId, pkgName, notificationsToCheck));
 
             // Batch move to new section
             if (!notificationsToMove.isEmpty()) {
@@ -872,10 +871,103 @@
     }
 
     @GuardedBy("mAggregatedNotifications")
+    private List<NotificationMoveOp> getAutogroupedNotificationsMoveOps(int userId, String pkgName,
+            ArrayMap<String, NotificationRecord> notificationsToCheck) {
+        final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+        final Set<FullyQualifiedGroupKey> oldGroups =
+                new HashSet<>(mAggregatedNotifications.keySet());
+        // Move auto-grouped updated notifications from the old groups to the new groups (section)
+        for (FullyQualifiedGroupKey oldFullAggKey : oldGroups) {
+            // Only check aggregate groups that match the same userId & packageName
+            if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
+                final ArrayMap<String, NotificationAttributes> notificationsInAggGroup =
+                        mAggregatedNotifications.get(oldFullAggKey);
+                if (notificationsInAggGroup == null) {
+                    continue;
+                }
+
+                FullyQualifiedGroupKey newFullAggregateGroupKey = null;
+                for (String key : notificationsInAggGroup.keySet()) {
+                    if (notificationsToCheck.get(key) != null) {
+                        // check if section changes
+                        NotificationSectioner sectioner = getSection(notificationsToCheck.get(key));
+                        if (sectioner == null) {
+                            continue;
+                        }
+                        newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
+                                sectioner);
+                        if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
+                            if (DEBUG) {
+                                Log.i(TAG, "Change section on channel update: " + key);
+                            }
+                            notificationsToMove.add(
+                                    new NotificationMoveOp(notificationsToCheck.get(key),
+                                        oldFullAggKey, newFullAggregateGroupKey));
+                            notificationsToCheck.remove(key);
+                        }
+                    }
+                }
+            }
+        }
+        return notificationsToMove;
+    }
+
+    @GuardedBy("mAggregatedNotifications")
+    private List<NotificationMoveOp> getUngroupedNotificationsMoveOps(int userId, String pkgName,
+            final ArrayMap<String, NotificationRecord> notificationsToCheck) {
+        final ArrayList<NotificationMoveOp> notificationsToMove = new ArrayList<>();
+        // Move any remaining ungrouped updated notifications from the old ungrouped list
+        // to the new ungrouped section list, if necessary
+        if (!notificationsToCheck.isEmpty()) {
+            final Set<FullyQualifiedGroupKey> oldUngroupedSectionKeys =
+                    new HashSet<>(mUngroupedAbuseNotifications.keySet());
+            for (FullyQualifiedGroupKey oldFullAggKey : oldUngroupedSectionKeys) {
+                // Only check aggregate groups that match the same userId & packageName
+                if (pkgName.equals(oldFullAggKey.pkg) && userId == oldFullAggKey.userId) {
+                    final ArrayMap<String, NotificationAttributes> ungroupedOld =
+                            mUngroupedAbuseNotifications.get(oldFullAggKey);
+                    if (ungroupedOld == null) {
+                        continue;
+                    }
+
+                    FullyQualifiedGroupKey newFullAggregateGroupKey = null;
+                    final Set<String> ungroupedKeys = new HashSet<>(ungroupedOld.keySet());
+                    for (String key : ungroupedKeys) {
+                        NotificationRecord record = notificationsToCheck.get(key);
+                        if (record != null) {
+                            // check if section changes
+                            NotificationSectioner sectioner = getSection(record);
+                            if (sectioner == null) {
+                                continue;
+                            }
+                            newFullAggregateGroupKey = new FullyQualifiedGroupKey(userId, pkgName,
+                                    sectioner);
+                            if (!oldFullAggKey.equals(newFullAggregateGroupKey)) {
+                                if (DEBUG) {
+                                    Log.i(TAG, "Change ungrouped section: " + key);
+                                }
+                                notificationsToMove.add(
+                                        new NotificationMoveOp(record, oldFullAggKey,
+                                            newFullAggregateGroupKey));
+                                notificationsToCheck.remove(key);
+                                //Remove from previous ungrouped list
+                                ungroupedOld.remove(key);
+                            }
+                        }
+                    }
+                    mUngroupedAbuseNotifications.put(oldFullAggKey, ungroupedOld);
+                }
+            }
+        }
+        return notificationsToMove;
+    }
+
+    @GuardedBy("mAggregatedNotifications")
     private void moveNotificationsToNewSection(final int userId, final String pkgName,
             final List<NotificationMoveOp> notificationsToMove) {
         record GroupUpdateOp(FullyQualifiedGroupKey groupKey, NotificationRecord record,
                              boolean hasSummary) { }
+        // Bundled operations to apply to groups affected by the channel update
         ArrayMap<FullyQualifiedGroupKey, GroupUpdateOp> groupsToUpdate = new ArrayMap<>();
 
         for (NotificationMoveOp moveOp: notificationsToMove) {
@@ -901,35 +993,36 @@
                 // Only add once, for triggering notification
                 if (!groupsToUpdate.containsKey(oldFullAggregateGroupKey)) {
                     groupsToUpdate.put(oldFullAggregateGroupKey,
-                            new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
+                        new GroupUpdateOp(oldFullAggregateGroupKey, record, true));
                 }
             }
 
-            // Add/update aggregate summary for new group
+            // Add moved notifications to the ungrouped list for new group and do grouping
+            // after all notifications have been handled
             if (newFullAggregateGroupKey != null) {
                 final ArrayMap<String, NotificationAttributes> newAggregatedNotificationsAttrs =
                         mAggregatedNotifications.getOrDefault(newFullAggregateGroupKey,
                             new ArrayMap<>());
-                boolean newGroupExists = !newAggregatedNotificationsAttrs.isEmpty();
-                newAggregatedNotificationsAttrs.put(record.getKey(),
-                        new NotificationAttributes(record.getFlags(),
-                            record.getNotification().getSmallIcon(),
-                            record.getNotification().color,
-                            record.getNotification().visibility,
-                            record.getNotification().getGroupAlertBehavior(),
-                            record.getChannel().getId()));
-                mAggregatedNotifications.put(newFullAggregateGroupKey,
-                        newAggregatedNotificationsAttrs);
+                boolean hasSummary = !newAggregatedNotificationsAttrs.isEmpty();
+                ArrayMap<String, NotificationAttributes> ungrouped =
+                        mUngroupedAbuseNotifications.getOrDefault(newFullAggregateGroupKey,
+                            new ArrayMap<>());
+                ungrouped.put(record.getKey(), new NotificationAttributes(
+                        record.getFlags(),
+                        record.getNotification().getSmallIcon(),
+                        record.getNotification().color,
+                        record.getNotification().visibility,
+                        record.getNotification().getGroupAlertBehavior(),
+                        record.getChannel().getId()));
+                mUngroupedAbuseNotifications.put(newFullAggregateGroupKey, ungrouped);
+
+                record.setOverrideGroupKey(null);
 
                 // Only add once, for triggering notification
                 if (!groupsToUpdate.containsKey(newFullAggregateGroupKey)) {
                     groupsToUpdate.put(newFullAggregateGroupKey,
-                            new GroupUpdateOp(newFullAggregateGroupKey, record, newGroupExists));
+                        new GroupUpdateOp(newFullAggregateGroupKey, record, hasSummary));
                 }
-
-                // Add notification to new group. do not request resort
-                record.setOverrideGroupKey(null);
-                mCallback.addAutoGroup(record.getKey(), newFullAggregateGroupKey.toString(), false);
             }
         }
 
@@ -937,18 +1030,26 @@
         for (FullyQualifiedGroupKey groupKey : groupsToUpdate.keySet()) {
             final ArrayMap<String, NotificationAttributes> aggregatedNotificationsAttrs =
                     mAggregatedNotifications.getOrDefault(groupKey, new ArrayMap<>());
-            if (aggregatedNotificationsAttrs.isEmpty()) {
-                mCallback.removeAutoGroupSummary(userId, pkgName, groupKey.toString());
-                mAggregatedNotifications.remove(groupKey);
-            } else {
-                NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
-                boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
+            final ArrayMap<String, NotificationAttributes> ungrouped =
+                    mUngroupedAbuseNotifications.getOrDefault(groupKey, new ArrayMap<>());
+
+            NotificationRecord triggeringNotification = groupsToUpdate.get(groupKey).record;
+            boolean hasSummary = groupsToUpdate.get(groupKey).hasSummary;
+            //Group needs to be created/updated
+            if (ungrouped.size() >= mAutoGroupAtCount
+                    || (hasSummary && !aggregatedNotificationsAttrs.isEmpty())) {
                 NotificationSectioner sectioner = getSection(triggeringNotification);
                 if (sectioner == null) {
                     continue;
                 }
-                updateAggregateAppGroup(groupKey, triggeringNotification.getKey(), hasSummary,
-                        sectioner.mSummaryId);
+                aggregateUngroupedNotifications(groupKey, triggeringNotification.getKey(),
+                        ungrouped, hasSummary, sectioner.mSummaryId);
+            } else {
+                // Remove empty groups
+                if (aggregatedNotificationsAttrs.isEmpty() && hasSummary) {
+                    mCallback.removeAutoGroupSummary(userId, pkgName, groupKey.toString());
+                    mAggregatedNotifications.remove(groupKey);
+                }
             }
         }
     }
@@ -1305,8 +1406,10 @@
         }
 
         private boolean isNotificationGroupable(final NotificationRecord record) {
-            if (record.isConversation()) {
-                return false;
+            if (!Flags.notificationForceGroupConversations()) {
+                if (record.isConversation()) {
+                    return false;
+                }
             }
 
             Notification notification = record.getSbn().getNotification();
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 1fdb57c..0cc50e6 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -75,7 +75,9 @@
 import com.android.internal.util.function.TriPredicate;
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.LocalServices;
 import com.android.server.notification.NotificationManagerService.DumpFilter;
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -134,6 +136,7 @@
     private final UserProfiles mUserProfiles;
     protected final IPackageManager mPm;
     protected final UserManager mUm;
+    private final UserManagerInternal mUserManagerInternal;
     private final Config mConfig;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -195,6 +198,7 @@
         mConfig = getConfig();
         mApprovalLevel = APPROVAL_BY_COMPONENT;
         mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
     }
 
     abstract protected Config getConfig();
@@ -950,7 +954,7 @@
                 || isPackageOrComponentAllowed(component.getPackageName(), userId))) {
             return false;
         }
-        return isValidService(component, userId);
+        return componentHasBindPermission(component, userId);
     }
 
     private boolean componentHasBindPermission(ComponentName component, int userId) {
@@ -1302,12 +1306,11 @@
                     if (TextUtils.equals(getPackageName(approvedPackageOrComponent), packageName)) {
                         final ComponentName component = ComponentName.unflattenFromString(
                                 approvedPackageOrComponent);
-                        if (component != null && !isValidService(component, userId)) {
+                        if (component != null && !componentHasBindPermission(component, userId)) {
                             approved.removeAt(j);
                             if (DEBUG) {
                                 Slog.v(TAG, "Removing " + approvedPackageOrComponent
-                                        + " from approved list; no bind permission or "
-                                        + "service interface filter found "
+                                        + " from approved list; no bind permission found "
                                         + mConfig.bindPermission);
                             }
                         }
@@ -1326,11 +1329,6 @@
         }
     }
 
-    protected boolean isValidService(ComponentName component, int userId) {
-        return componentHasBindPermission(component, userId) && queryPackageForServices(
-                component.getPackageName(), userId).contains(component);
-    }
-
     protected boolean isValidEntry(String packageOrComponent, int userId) {
         return hasMatchingServices(packageOrComponent, userId);
     }
@@ -1372,9 +1370,14 @@
     @GuardedBy("mMutex")
     protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind,
             final IntArray activeUsers,
-            SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) {
-        mEnabledServicesForCurrentProfiles.clear();
-        mEnabledServicesPackageNames.clear();
+            SparseArray<ArraySet<ComponentName>> approvedComponentsByUser,
+            boolean isVisibleBackgroundUser) {
+        // When it is a visible background user in Automotive MUMD environment,
+        // don't clear mEnabledServicesForCurrentProfile and mEnabledServicesPackageNames.
+        if (!isVisibleBackgroundUser) {
+            mEnabledServicesForCurrentProfiles.clear();
+            mEnabledServicesPackageNames.clear();
+        }
         final int nUserIds = activeUsers.size();
 
         for (int i = 0; i < nUserIds; ++i) {
@@ -1395,7 +1398,12 @@
             }
 
             componentsToBind.put(userId, add);
-
+            // When it is a visible background user in Automotive MUMD environment,
+            // skip adding items to mEnabledServicesForCurrentProfile
+            // and mEnabledServicesPackageNames.
+            if (isVisibleBackgroundUser) {
+                continue;
+            }
             mEnabledServicesForCurrentProfiles.addAll(userComponents);
 
             for (int j = 0; j < userComponents.size(); j++) {
@@ -1443,7 +1451,10 @@
         IntArray userIds = mUserProfiles.getCurrentProfileIds();
         boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind, mContext)
                 && allowRebindForParentUser();
+        boolean isVisibleBackgroundUser = false;
         if (userToRebind != USER_ALL && !rebindAllCurrentUsers) {
+            isVisibleBackgroundUser =
+                    mUserManagerInternal.isVisibleBackgroundFullUser(userToRebind);
             userIds = new IntArray(1);
             userIds.add(userToRebind);
         }
@@ -1458,7 +1469,8 @@
 
             // Filter approvedComponentsByUser to collect all of the components that are allowed
             // for the currently active user(s).
-            populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser);
+            populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser,
+                    isVisibleBackgroundUser);
 
             // For every current non-system connection, disconnect services that are no longer
             // approved, or ALL services if we are force rebinding
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index b4459cb..abb2132 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -489,6 +489,7 @@
 
                 } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
                     hasValidSound = false;
+                    hasValidVibrate = false;
                 }
             }
         }
@@ -753,6 +754,13 @@
         // notifying app does not have the VIBRATE permission.
         final long identity = Binder.clearCallingIdentity();
         try {
+            // Need to explicitly cancel a previously playing vibration
+            // Otherwise a looping vibration will not be stopped when starting a new one.
+            if (mVibrateNotificationKey != null
+                    && !mVibrateNotificationKey.equals(record.getKey())) {
+                mVibrateNotificationKey = null;
+                mVibratorHelper.cancelVibration();
+            }
             final float scale = getVibrationIntensity(record);
             final VibrationEffect scaledEffect = Float.compare(scale, DEFAULT_VOLUME) != 0
                     ? mVibratorHelper.scale(effect, scale) : effect;
@@ -1519,7 +1527,14 @@
         @Override
         public void setLastNotificationUpdateTimeMs(NotificationRecord record,
                 long timestampMillis) {
-            super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+            if (Flags.politeNotificationsAttnUpdate()) {
+                // Set last update per package/channel only for exempt notifications
+                if (isAvalancheExempted(record)) {
+                    super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+                }
+            } else {
+                super.setLastNotificationUpdateTimeMs(record, timestampMillis);
+            }
             mLastNotificationTimestamp = timestampMillis;
             mAppStrategy.setLastNotificationUpdateTimeMs(record, timestampMillis);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 1938642..e2889fa 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -29,8 +29,8 @@
 import android.media.AudioAttributes;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Slog;
+
 import com.android.internal.compat.IPlatformCompat;
 
 /**
@@ -79,6 +79,11 @@
         if (restrictAudioAttributesCall() || restrictAudioAttributesAlarm()
                 || restrictAudioAttributesMedia()) {
             AudioAttributes attributes = record.getChannel().getAudioAttributes();
+            if (attributes == null) {
+                if (DBG) Slog.d(TAG, "missing AudioAttributes");
+                return null;
+            }
+
             boolean updateAttributes =  false;
             if (restrictAudioAttributesCall()
                     && !record.getNotification().isStyle(Notification.CallStyle.class)
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c7c984b..54e9189 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1583,6 +1583,8 @@
                     // respond to direct replies with updates. So we need to update System UI
                     // immediately.
                     if (lifetimeExtensionRefactor()) {
+                        // We need to reset this to allow the notif to be updated again.
+                        r.setCanceledAfterLifetimeExtension(false);
                         maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
                                 r.getSbn().getPackageName(), packageImportance);
                     }
@@ -1639,9 +1641,12 @@
                     // respond to direct replies with updates. So we need to update System UI
                     // immediately.
                     if (lifetimeExtensionRefactor()) {
+                        // We need to reset this to allow the notif to be updated again.
+                        r.setCanceledAfterLifetimeExtension(false);
                         maybeNotifySystemUiListenerLifetimeExtendedLocked(r,
                                 r.getSbn().getPackageName(), packageImportance);
                     }
+
                     r.recordSmartReplied();
                     LogMaker logMaker = r.getLogMaker()
                             .setCategory(MetricsEvent.SMART_REPLY_ACTION)
@@ -5675,7 +5680,7 @@
             // a "normal" rule, it must provide a CP/ConfigActivity too.
             if (android.app.Flags.modesApi()) {
                 boolean isImplicitRuleUpdateFromSystem = updateId != null
-                        && ZenModeHelper.isImplicitRuleId(updateId)
+                        && ZenModeConfig.isImplicitRuleId(updateId)
                         && isCallerSystemOrSystemUi();
                 if (!isImplicitRuleUpdateFromSystem
                         && rule.getOwner() == null
@@ -7595,16 +7600,14 @@
                     + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
         }
 
-        if (android.app.Flags.secureAllowlistToken()) {
-            IBinder allowlistToken = notification.getAllowlistToken();
-            if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
-                throw new SecurityException(
-                        "Unexpected allowlist token received from " + callingUid);
-            }
-            // allowlistToken is populated by unparceling, so it can be null if the notification was
-            // posted from inside system_server. Ensure it's the expected value.
-            notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
+        IBinder allowlistToken = notification.getAllowlistToken();
+        if (allowlistToken != null && allowlistToken != ALLOWLIST_TOKEN) {
+            throw new SecurityException(
+                    "Unexpected allowlist token received from " + callingUid);
         }
+        // allowlistToken is populated by unparceling, so it can be null if the notification was
+        // posted from inside system_server. Ensure it's the expected value.
+        notification.overrideAllowlistToken(ALLOWLIST_TOKEN);
 
         checkRestrictedCategories(notification);
 
@@ -8769,12 +8772,10 @@
          */
         private boolean enqueueNotification() {
             synchronized (mNotificationLock) {
-                if (android.app.Flags.secureAllowlistToken()) {
-                    // allowlistToken is populated by unparceling, so it will be absent if the
-                    // EnqueueNotificationRunnable is created directly by NMS (as we do for group
-                    // summaries) instead of via notify(). Fix that.
-                    r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
-                }
+                // allowlistToken is populated by unparceling, so it will be absent if the
+                // EnqueueNotificationRunnable is created directly by NMS (as we do for group
+                // summaries) instead of via notify(). Fix that.
+                r.getNotification().overrideAllowlistToken(ALLOWLIST_TOKEN);
 
                 final long snoozeAt =
                         mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
@@ -11741,17 +11742,39 @@
     private void maybeNotifySystemUiListenerLifetimeExtendedLocked(NotificationRecord record,
             String pkg, int packageImportance) {
         if (record != null && (record.getSbn().getNotification().flags
-                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0) {
+                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY) > 0
+                && !record.isCanceledAfterLifetimeExtension()) {
             boolean isAppForeground = pkg != null && packageImportance == IMPORTANCE_FOREGROUND;
 
-            // Lifetime extended notifications don't need to alert on state change.
+            // Save the original Record's post silently value, so we can restore it after we send
+            // the SystemUI specific silent update.
+            boolean savedPostSilentlyState = record.shouldPostSilently();
+            boolean savedOnlyAlertOnceState = (record.getNotification().flags
+                    & FLAG_ONLY_ALERT_ONCE) > 0;
+            // Lifetime extended notifications don't need to alert on new state change.
             record.setPostSilently(true);
             // We also set FLAG_ONLY_ALERT_ONCE to avoid the notification from HUN-ing again.
             record.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
 
+            PostNotificationTracker tracker = mPostNotificationTrackerFactory.newTracker(null);
+            tracker.addCleanupRunnable(() -> {
+                synchronized (mNotificationLock) {
+                    // Mark that the notification has been updated due to cancelation, so it won't
+                    // be updated again if the app cancels multiple times.
+                    record.setCanceledAfterLifetimeExtension(true);
+                    // Set the post silently status to the record's previous value.
+                    record.setPostSilently(savedPostSilentlyState);
+                    // Remove FLAG_ONLY_ALERT_ONCE if the notification did not previously have it.
+                    if (!savedOnlyAlertOnceState) {
+                        record.getNotification().flags &= ~FLAG_ONLY_ALERT_ONCE;
+                    }
+                }
+            });
+
             mHandler.post(new EnqueueNotificationRunnable(record.getUser().getIdentifier(),
-                    record, isAppForeground, /* isAppProvided= */ false,
-                    mPostNotificationTrackerFactory.newTracker(null)));
+                    record, isAppForeground, /* isAppProvided= */ false, tracker));
+
+            EventLogTags.writeNotificationCancelPrevented(record.getKey());
         }
     }
 
@@ -13351,17 +13374,23 @@
         @ElapsedRealtimeLong private final long mStartTime;
         @Nullable private final WakeLock mWakeLock;
         private boolean mOngoing;
+        private final List<Runnable> mCleanupRunnables;
 
         @VisibleForTesting
         PostNotificationTracker(@Nullable WakeLock wakeLock) {
             mStartTime = SystemClock.elapsedRealtime();
             mWakeLock = wakeLock;
             mOngoing = true;
+            mCleanupRunnables = new ArrayList<Runnable>();
             if (DBG) {
                 Slog.d(TAG, "PostNotification: Started");
             }
         }
 
+        void addCleanupRunnable(Runnable runnable) {
+            mCleanupRunnables.add(runnable);
+        }
+
         @ElapsedRealtimeLong
         long getStartTime() {
             return mStartTime;
@@ -13373,8 +13402,9 @@
         }
 
         /**
-         * Cancels the tracker (releasing the acquired WakeLock). Either {@link #finish} or
-         * {@link #cancel} (exclusively) should be called on this object before it's discarded.
+         * Cancels the tracker (releasing the acquired WakeLock) and runs any set cleanup runnables.
+         * Either {@link #finish} or {@link #cancel} (exclusively) should be called on this object
+         * before it's discarded.
          */
         void cancel() {
             if (!isOngoing()) {
@@ -13385,6 +13415,9 @@
             if (mWakeLock != null) {
                 Binder.withCleanCallingIdentity(() -> mWakeLock.release());
             }
+            for (Runnable r : mCleanupRunnables) {
+                r.run();
+            }
             if (DBG) {
                 long elapsedTime = SystemClock.elapsedRealtime() - mStartTime;
                 Slog.d(TAG, TextUtils.formatSimple("PostNotification: Abandoned after %d ms",
@@ -13393,9 +13426,10 @@
         }
 
         /**
-         * Finishes the tracker (releasing the acquired WakeLock) and returns the time elapsed since
-         * the operation started, in milliseconds. Either {@link #finish} or {@link #cancel}
-         * (exclusively) should be called on this object before it's discarded.
+         * Finishes the tracker (releasing the acquired WakeLock), runs any set cleanup runnables,
+         * and returns the time elapsed since the operation started, in milliseconds.
+         * Either {@link #finish} or {@link #cancel} (exclusively) should be called on this object
+         * before it's discarded.
          */
         @DurationMillisLong
         long finish() {
@@ -13408,6 +13442,9 @@
             if (mWakeLock != null) {
                 Binder.withCleanCallingIdentity(() -> mWakeLock.release());
             }
+            for (Runnable r : mCleanupRunnables) {
+                r.run();
+            }
             if (DBG) {
                 Slog.d(TAG,
                         TextUtils.formatSimple("PostNotification: Finished in %d ms", elapsedTime));
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1392003..e541246 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -222,6 +222,9 @@
     private boolean mPendingLogUpdate = false;
     private int mProposedImportance = IMPORTANCE_UNSPECIFIED;
     private boolean mSensitiveContent = false;
+    // Whether an app has attempted to cancel this notification after it has been marked as
+    // lifetime extended.
+    private boolean mCanceledAfterLifetimeExtension = false;
 
     public NotificationRecord(Context context, StatusBarNotification sbn,
             NotificationChannel channel) {
@@ -535,6 +538,7 @@
                 + NotificationListenerService.Ranking.importanceToString(mProposedImportance));
         pw.println(prefix + "mIsAppImportanceLocked=" + mIsAppImportanceLocked);
         pw.println(prefix + "mSensitiveContent=" + mSensitiveContent);
+        pw.println(prefix + "mCanceledAfterLifetimeExtension=" + mCanceledAfterLifetimeExtension);
         pw.println(prefix + "mIntercept=" + mIntercept);
         pw.println(prefix + "mHidden==" + mHidden);
         pw.println(prefix + "mGlobalSortKey=" + mGlobalSortKey);
@@ -1620,6 +1624,14 @@
         mPkgAllowedAsConvo = allowedAsConvo;
     }
 
+    public boolean isCanceledAfterLifetimeExtension() {
+        return mCanceledAfterLifetimeExtension;
+    }
+
+    public void setCanceledAfterLifetimeExtension(boolean canceledAfterLifetimeExtension) {
+        mCanceledAfterLifetimeExtension = canceledAfterLifetimeExtension;
+    }
+
     /**
      * Whether this notification is a conversation notification.
      */
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 82c5733..1aa5ac0 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -23,18 +23,15 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.provider.Settings.Global;
-import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeDiff;
 import android.util.LocalLog;
-import android.util.Log;
-import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.List;
 
 public class ZenLog {
@@ -61,6 +58,8 @@
     private static final int TYPE_RECORD_CALLER = 19;
     private static final int TYPE_CHECK_REPEAT_CALLER = 20;
     private static final int TYPE_ALERT_ON_UPDATED_INTERCEPT = 21;
+    private static final int TYPE_APPLY_DEVICE_EFFECT = 22;
+    private static final int TYPE_SCHEDULE_APPLY_DEVICE_EFFECT = 23;
 
     public static void traceIntercepted(NotificationRecord record, String reason) {
         append(TYPE_INTERCEPTED, record.getKey() + "," + reason);
@@ -173,6 +172,14 @@
                 + ", given uri=" + hasUri);
     }
 
+    public static void traceApplyDeviceEffect(String effect, boolean newValue) {
+        append(TYPE_APPLY_DEVICE_EFFECT, effect + " -> " + newValue);
+    }
+
+    public static void traceScheduleApplyDeviceEffect(String effect, boolean scheduledValue) {
+        append(TYPE_SCHEDULE_APPLY_DEVICE_EFFECT, effect + " -> " + scheduledValue);
+    }
+
     private static String subscribeResult(IConditionProvider provider, RemoteException e) {
         return provider == null ? "no provider" : e != null ? e.getMessage() : "ok";
     }
@@ -196,6 +203,8 @@
             case TYPE_RECORD_CALLER: return "record_caller";
             case TYPE_CHECK_REPEAT_CALLER: return "check_repeat_caller";
             case TYPE_ALERT_ON_UPDATED_INTERCEPT: return "alert_on_updated_intercept";
+            case TYPE_APPLY_DEVICE_EFFECT: return "apply_device_effect";
+            case TYPE_SCHEDULE_APPLY_DEVICE_EFFECT: return "schedule_device_effect";
             default: return "unknown";
         }
     }
@@ -273,4 +282,14 @@
             STATE_CHANGES.dump(prefix, pw);
         }
     }
+
+    @VisibleForTesting(/* otherwise = VisibleForTesting.NONE */)
+    public static void clear() {
+        synchronized (INTERCEPTION_EVENTS) {
+            INTERCEPTION_EVENTS.clear();
+        }
+        synchronized (STATE_CHANGES) {
+            STATE_CHANGES.clear();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 268d835..d495ef5 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -82,7 +82,7 @@
         for (ZenRule automaticRule : config.automaticRules.values()) {
             if (automaticRule.component != null) {
                 evaluateRule(automaticRule, current, trigger, processSubscriptions, false);
-                updateSnoozing(automaticRule);
+                automaticRule.reconsiderConditionOverride();
             }
         }
 
@@ -187,13 +187,4 @@
                     + rule.conditionId);
         }
     }
-
-    private boolean updateSnoozing(ZenRule rule) {
-        if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
-            rule.snoozing = false;
-            if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
-            return true;
-        }
-        return false;
-    }
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 8c280ed..2ada9ae4 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -37,6 +37,9 @@
 import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.implicitRuleId;
 
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.Preconditions.checkArgument;
@@ -153,8 +156,6 @@
     static final int RULE_LIMIT_PER_PACKAGE = 100;
     private static final Duration DELETED_RULE_KEPT_FOR = Duration.ofDays(30);
 
-    private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
-
     private static final int MAX_ICON_RESOURCE_NAME_LENGTH = 1000;
 
     /**
@@ -225,10 +226,8 @@
         mDefaultConfig = Flags.modesUi()
                 ? ZenModeConfig.getDefaultConfig()
                 : readDefaultConfig(mContext.getResources());
-        updateDefaultConfigAutomaticRules();
-        if (Flags.modesApi()) {
-            updateDefaultAutomaticRulePolicies();
-        }
+        updateDefaultConfig(mContext, mDefaultConfig);
+
         mConfig = mDefaultConfig.copy();
         synchronized (mConfigsArrayLock) {
             mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
@@ -484,7 +483,7 @@
             newConfig = mConfig.copy();
             ZenRule rule = new ZenRule();
             populateZenRule(pkg, automaticZenRule, rule, origin, /* isNew= */ true);
-            rule = maybeRestoreRemovedRule(newConfig, rule, automaticZenRule, origin);
+            rule = maybeRestoreRemovedRule(newConfig, pkg, rule, automaticZenRule, origin);
             newConfig.automaticRules.put(rule.id, rule);
             maybeReplaceDefaultRule(newConfig, automaticZenRule);
 
@@ -497,7 +496,7 @@
     }
 
     @GuardedBy("mConfigLock")
-    private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, ZenRule ruleToAdd,
+    private ZenRule maybeRestoreRemovedRule(ZenModeConfig config, String pkg, ZenRule ruleToAdd,
             AutomaticZenRule azrToAdd, @ConfigOrigin int origin) {
         if (!Flags.modesApi()) {
             return ruleToAdd;
@@ -521,10 +520,18 @@
         if (origin != ORIGIN_APP) {
             return ruleToAdd; // Okay to create anew.
         }
+        if (Flags.modesUi()) {
+            if (!Objects.equals(ruleToRestore.pkg, pkg)
+                    || !Objects.equals(ruleToRestore.component, azrToAdd.getOwner())) {
+                // Apps are not allowed to change the owner via updateAutomaticZenRule(). Thus, if
+                // they have to, delete+add is their only option.
+                return ruleToAdd;
+            }
+        }
 
         // "Preserve" the previous rule by considering the azrToAdd an update instead.
         // Only app-modifiable fields will actually be modified.
-        populateZenRule(ruleToRestore.pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false);
+        populateZenRule(pkg, azrToAdd, ruleToRestore, origin, /* isNew= */ false);
         return ruleToRestore;
     }
 
@@ -643,10 +650,10 @@
                 if ((rule.userModifiedFields & AutomaticZenRule.FIELD_INTERRUPTION_FILTER) == 0) {
                     rule.zenMode = zenMode;
                 }
-                rule.snoozing = false;
                 rule.condition = new Condition(rule.conditionId,
                         mContext.getString(R.string.zen_mode_implicit_activated),
                         STATE_TRUE);
+                rule.resetConditionOverride();
 
                 setConfigLocked(newConfig, /* triggeringComponent= */ null, ORIGIN_APP,
                         "applyGlobalZenModeAsImplicitZenRule", callingUid);
@@ -756,7 +763,9 @@
             try {
                 ApplicationInfo applicationInfo = mPm.getApplicationInfo(pkg, 0);
                 rule.name = applicationInfo.loadLabel(mPm).toString();
-                rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
+                if (!Flags.modesUi()) {
+                    rule.iconResName = drawableResIdToResName(pkg, applicationInfo.icon);
+                }
             } catch (PackageManager.NameNotFoundException e) {
                 // Should not happen, since it's the app calling us (?)
                 Log.w(TAG, "Package not found for creating implicit zen rule");
@@ -781,14 +790,6 @@
         return rule;
     }
 
-    private static String implicitRuleId(String forPackage) {
-        return IMPLICIT_RULE_ID_PREFIX + forPackage;
-    }
-
-    static boolean isImplicitRuleId(@NonNull String ruleId) {
-        return ruleId.startsWith(IMPLICIT_RULE_ID_PREFIX);
-    }
-
     boolean removeAutomaticZenRule(String id, @ConfigOrigin int origin, String reason,
             int callingUid) {
         checkManageRuleOrigin("removeAutomaticZenRule", origin);
@@ -867,8 +868,8 @@
                 ZenRule deletedRule = ruleToRemove.copy();
                 deletedRule.deletionInstant = Instant.now(mClock);
                 // If the rule is restored it shouldn't be active (or snoozed).
-                deletedRule.snoozing = false;
                 deletedRule.condition = null;
+                deletedRule.resetConditionOverride();
                 // Overwrites a previously-deleted rule with the same conditionId, but that's okay.
                 config.deletedRules.put(deletedKey, deletedRule);
             }
@@ -885,7 +886,12 @@
             if (rule == null || !canManageAutomaticZenRule(rule)) {
                 return Condition.STATE_UNKNOWN;
             }
-            return rule.condition != null ? rule.condition.state : STATE_FALSE;
+            if (Flags.modesApi() && Flags.modesUi()) {
+                return rule.isAutomaticActive() ? STATE_TRUE : STATE_FALSE;
+            } else {
+                // Buggy, does not consider snoozing!
+                return rule.condition != null ? rule.condition.state : STATE_FALSE;
+            }
         }
     }
 
@@ -943,12 +949,52 @@
         }
 
         for (ZenRule rule : rules) {
-            rule.condition = condition;
-            updateSnoozing(rule);
+            applyConditionAndReconsiderOverride(rule, condition, origin);
             setConfigLocked(config, rule.component, origin, "conditionChanged", callingUid);
         }
     }
 
+    private static void applyConditionAndReconsiderOverride(ZenRule rule, Condition condition,
+            int origin) {
+        if (Flags.modesApi() && Flags.modesUi()) {
+            if (origin == ORIGIN_USER_IN_SYSTEMUI && condition != null
+                    && condition.source == SOURCE_USER_ACTION) {
+                // Apply as override, instead of actual condition.
+                // If the new override is the reverse of a previous (still active) override, try
+                // removing the previous override, as long as the resulting state, based on the
+                // previous owner-provided condition, is the desired one (active or inactive).
+                // This allows the rule owner to resume controlling the rule after
+                // snoozing-unsnoozing or activating-stopping.
+                if (condition.state == STATE_TRUE) {
+                    rule.resetConditionOverride();
+                    if (!rule.isAutomaticActive()) {
+                        rule.setConditionOverride(OVERRIDE_ACTIVATE);
+                    }
+                } else if (condition.state == STATE_FALSE) {
+                    rule.resetConditionOverride();
+                    if (rule.isAutomaticActive()) {
+                        rule.setConditionOverride(OVERRIDE_DEACTIVATE);
+                    }
+                }
+            } else if (origin == ORIGIN_USER_IN_APP && condition != null
+                    && condition.source == SOURCE_USER_ACTION) {
+                // Remove override and just apply the condition. Since the app is reporting that the
+                // user asked for it, by definition it knows that, and will adjust its automatic
+                // behavior accordingly -> no need to override.
+                rule.condition = condition;
+                rule.resetConditionOverride();
+            } else {
+                // Update the condition, and check whether we can remove the override (if automatic
+                // and manual decisions agree).
+                rule.condition = condition;
+                rule.reconsiderConditionOverride();
+            }
+        } else {
+            rule.condition = condition;
+            rule.reconsiderConditionOverride();
+        }
+    }
+
     private static List<ZenRule> findMatchingRules(ZenModeConfig config, Uri id,
             Condition condition) {
         List<ZenRule> matchingRules = new ArrayList<>();
@@ -971,15 +1017,6 @@
         return true;
     }
 
-    private boolean updateSnoozing(ZenRule rule) {
-        if (rule != null && rule.snoozing && !rule.isTrueOrUnknown()) {
-            rule.snoozing = false;
-            if (DEBUG) Log.d(TAG, "Snoozing reset for " + rule.conditionId);
-            return true;
-        }
-        return false;
-    }
-
     public int getCurrentInstanceCount(ComponentName cn) {
         if (cn == null) {
             return 0;
@@ -1034,7 +1071,7 @@
     }
 
     void updateZenRulesOnLocaleChange() {
-        updateDefaultConfigAutomaticRules();
+        updateRuleStringsForCurrentLocale(mContext, mDefaultConfig);
         synchronized (mConfigLock) {
             if (mConfig == null) {
                 return;
@@ -1181,7 +1218,7 @@
 
             if (rule.enabled != azr.isEnabled()) {
                 rule.enabled = azr.isEnabled();
-                rule.snoozing = false;
+                rule.resetConditionOverride();
                 modified = true;
             }
             if (!Objects.equals(rule.configurationActivity, azr.getConfigurationActivity())) {
@@ -1271,7 +1308,7 @@
             return modified;
         } else {
             if (rule.enabled != azr.isEnabled()) {
-                rule.snoozing = false;
+                rule.resetConditionOverride();
             }
             rule.name = azr.getName();
             rule.condition = null;
@@ -1573,18 +1610,16 @@
                     // For API calls (different origin) keep old behavior of snoozing all rules.
                     for (ZenRule automaticRule : newConfig.automaticRules.values()) {
                         if (automaticRule.isAutomaticActive()) {
-                            automaticRule.snoozing = true;
+                            automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
                         }
                     }
                 }
             } else {
                 if (zenMode == Global.ZEN_MODE_OFF) {
                     newConfig.manualRule = null;
-                    // User deactivation of DND means just turning off the manual DND rule.
-                    // For API calls (different origin) keep old behavior of snoozing all rules.
                     for (ZenRule automaticRule : newConfig.automaticRules.values()) {
                         if (automaticRule.isAutomaticActive()) {
-                            automaticRule.snoozing = true;
+                            automaticRule.setConditionOverride(OVERRIDE_DEACTIVATE);
                         }
                     }
 
@@ -1626,13 +1661,11 @@
     void dump(ProtoOutputStream proto) {
         proto.write(ZenModeProto.ZEN_MODE, mZenMode);
         synchronized (mConfigLock) {
-            if (mConfig.manualRule != null) {
+            if (mConfig.isManualActive()) {
                 mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
             }
             for (ZenRule rule : mConfig.automaticRules.values()) {
-                if (rule.enabled && rule.condition != null
-                        && rule.condition.state == STATE_TRUE
-                        && !rule.snoozing) {
+                if (rule.isAutomaticActive()) {
                     rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
                 }
             }
@@ -1695,8 +1728,8 @@
                 for (ZenRule automaticRule : config.automaticRules.values()) {
                     if (forRestore) {
                         // don't restore transient state from restored automatic rules
-                        automaticRule.snoozing = false;
                         automaticRule.condition = null;
+                        automaticRule.resetConditionOverride();
                         automaticRule.creationTime = time;
                     }
 
@@ -1717,6 +1750,15 @@
                                     manualRulePolicy.overwrittenWith(automaticRule.zenPolicy);
                         }
                     }
+
+                    if (Flags.modesApi() && Flags.modesUi()
+                            && config.version < ZenModeConfig.XML_VERSION_MODES_UI) {
+                        // Clear icons from implicit rules. App icons are not suitable for some
+                        // surfaces, so juse use a default (the user can select a different one).
+                        if (ZenModeConfig.isImplicitRuleId(automaticRule.id)) {
+                            automaticRule.iconResName = null;
+                        }
+                    }
                 }
             }
 
@@ -2083,17 +2125,25 @@
     @GuardedBy("mConfigLock")
     private void applyCustomPolicy(ZenPolicy policy, ZenRule rule, boolean useManualConfig) {
         if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
-            policy.apply(new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .allowPriorityChannels(false)
-                    .build());
+            if (Flags.modesApi() && Flags.modesUi()) {
+                policy.apply(ZenPolicy.getBasePolicyInterruptionFilterNone());
+            } else {
+                policy.apply(new ZenPolicy.Builder()
+                        .disallowAllSounds()
+                        .allowPriorityChannels(false)
+                        .build());
+            }
         } else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
-            policy.apply(new ZenPolicy.Builder()
-                    .disallowAllSounds()
-                    .allowAlarms(true)
-                    .allowMedia(true)
-                    .allowPriorityChannels(false)
-                    .build());
+            if (Flags.modesApi() && Flags.modesUi()) {
+                policy.apply(ZenPolicy.getBasePolicyInterruptionFilterAlarms());
+            } else {
+                policy.apply(new ZenPolicy.Builder()
+                        .disallowAllSounds()
+                        .allowAlarms(true)
+                        .allowMedia(true)
+                        .allowPriorityChannels(false)
+                        .build());
+            }
         } else if (rule.zenPolicy != null) {
             policy.apply(rule.zenPolicy);
         } else {
@@ -2185,30 +2235,49 @@
         }
     }
 
-    private void updateDefaultConfigAutomaticRules() {
-        for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+    /**
+     * Apply changes to the <em>default</em> {@link ZenModeConfig} so that the rules included by
+     * default (Events / Sleeping) support the latest Zen features and are ready for new users.
+     *
+     * <p>This includes: setting a fully populated ZenPolicy, setting correct type and
+     * allowManualInvocation=true, and ensuring default names and trigger descriptions correspond
+     * to the current locale.
+     */
+    private static void updateDefaultConfig(Context context, ZenModeConfig defaultConfig) {
+        if (Flags.modesApi()) {
+            updateDefaultAutomaticRulePolicies(defaultConfig);
+        }
+        if (Flags.modesApi() && Flags.modesUi()) {
+            SystemZenRules.maybeUpgradeRules(context, defaultConfig);
+        }
+        updateRuleStringsForCurrentLocale(context, defaultConfig);
+    }
+
+    private static void updateRuleStringsForCurrentLocale(Context context,
+            ZenModeConfig defaultConfig) {
+        for (ZenRule rule : defaultConfig.automaticRules.values()) {
             if (ZenModeConfig.EVENTS_DEFAULT_RULE_ID.equals(rule.id)) {
-                rule.name = mContext.getResources()
+                rule.name = context.getResources()
                         .getString(R.string.zen_mode_default_events_name);
             } else if (ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID.equals(rule.id)) {
-                rule.name = mContext.getResources()
+                rule.name = context.getResources()
                         .getString(R.string.zen_mode_default_every_night_name);
             }
             if (Flags.modesApi() && Flags.modesUi()) {
-                SystemZenRules.updateTriggerDescription(mContext, rule);
+                SystemZenRules.updateTriggerDescription(context, rule);
             }
         }
     }
 
     // Updates the policies in the default automatic rules (provided via default XML config) to
     // be fully filled in default values.
-    private void updateDefaultAutomaticRulePolicies() {
+    private static void updateDefaultAutomaticRulePolicies(ZenModeConfig defaultConfig) {
         if (!Flags.modesApi()) {
             // Should be checked before calling, but just in case.
             return;
         }
-        ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy();
-        for (ZenRule rule : mDefaultConfig.automaticRules.values()) {
+        ZenPolicy defaultPolicy = defaultConfig.getZenPolicy();
+        for (ZenRule rule : defaultConfig.automaticRules.values()) {
             if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) {
                 rule.zenPolicy = defaultPolicy.copy();
             }
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 7265cff..aac2c40 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -149,3 +149,10 @@
   description: "This flag enables forced auto-grouping singleton groups"
   bug: "336488844"
 }
+
+flag {
+  name: "notification_force_group_conversations"
+  namespace: "systemui"
+  description: "This flag enables forced auto-grouping conversations"
+  bug: "336488844"
+}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 46585a5..6303ecd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -80,6 +80,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.content.om.OverlayConfig;
@@ -1180,6 +1181,7 @@
         // intent, querying the PackageManagerService for the actual current
         // state may lead to contradictions within OMS. Better then to lag
         // behind until all pending intents have been processed.
+        @GuardedBy("itself")
         private final ArrayMap<String, PackageStateUsers> mCache = new ArrayMap<>();
         private final ArraySet<Integer> mInitializedUsers = new ArraySet<>();
 
@@ -1207,10 +1209,12 @@
             }
 
             final ArrayMap<String, PackageState> userPackages = new ArrayMap<>();
-            for (int i = 0, n = mCache.size(); i < n; i++) {
-                final PackageStateUsers pkg = mCache.valueAt(i);
-                if (pkg.mInstalledUsers.contains(userId)) {
-                    userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+            synchronized (mCache) {
+                for (int i = 0, n = mCache.size(); i < n; i++) {
+                    final PackageStateUsers pkg = mCache.valueAt(i);
+                    if (pkg.mInstalledUsers.contains(userId)) {
+                        userPackages.put(mCache.keyAt(i), pkg.mPackageState);
+                    }
                 }
             }
             return userPackages;
@@ -1220,7 +1224,11 @@
         @Nullable
         public PackageState getPackageStateForUser(@NonNull final String packageName,
                 final int userId) {
-            final PackageStateUsers pkg = mCache.get(packageName);
+            final PackageStateUsers pkg;
+
+            synchronized (mCache) {
+                pkg = mCache.get(packageName);
+            }
             if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
                 return pkg.mPackageState;
             }
@@ -1251,12 +1259,15 @@
         @NonNull
         private PackageState addPackageUser(@NonNull final PackageState pkg,
                 final int user) {
-            PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName());
-            if (pkgUsers == null) {
-                pkgUsers = new PackageStateUsers(pkg);
-                mCache.put(pkg.getPackageName(), pkgUsers);
-            } else {
-                pkgUsers.mPackageState = pkg;
+            PackageStateUsers pkgUsers;
+            synchronized (mCache) {
+                pkgUsers = mCache.get(pkg.getPackageName());
+                if (pkgUsers == null) {
+                    pkgUsers = new PackageStateUsers(pkg);
+                    mCache.put(pkg.getPackageName(), pkgUsers);
+                } else {
+                    pkgUsers.mPackageState = pkg;
+                }
             }
             pkgUsers.mInstalledUsers.add(user);
             return pkgUsers.mPackageState;
@@ -1265,18 +1276,24 @@
 
         @NonNull
         private void removePackageUser(@NonNull final String packageName, final int user) {
-            final PackageStateUsers pkgUsers = mCache.get(packageName);
-            if (pkgUsers == null) {
-                return;
+            // synchronize should include the call to the other removePackageUser() method so that
+            // the access and modification happen under the same lock.
+            synchronized (mCache) {
+                final PackageStateUsers pkgUsers = mCache.get(packageName);
+                if (pkgUsers == null) {
+                    return;
+                }
+                removePackageUser(pkgUsers, user);
             }
-            removePackageUser(pkgUsers, user);
         }
 
         @NonNull
         private void removePackageUser(@NonNull final PackageStateUsers pkg, final int user) {
             pkg.mInstalledUsers.remove(user);
             if (pkg.mInstalledUsers.isEmpty()) {
-                mCache.remove(pkg.mPackageState.getPackageName());
+                synchronized (mCache) {
+                    mCache.remove(pkg.mPackageState.getPackageName());
+                }
             }
         }
 
@@ -1386,8 +1403,10 @@
         public void forgetAllPackageInfos(final int userId) {
             // Iterate in reverse order since removing the package in all users will remove the
             // package from the cache.
-            for (int i = mCache.size() - 1; i >= 0; i--) {
-                removePackageUser(mCache.valueAt(i), userId);
+            synchronized (mCache) {
+                for (int i = mCache.size() - 1; i >= 0; i--) {
+                    removePackageUser(mCache.valueAt(i), userId);
+                }
             }
         }
 
@@ -1405,22 +1424,23 @@
 
         public void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
             pw.println("AndroidPackage cache");
+            synchronized (mCache) {
+                if (!dumpState.isVerbose()) {
+                    pw.println(TAB1 + mCache.size() + " package(s)");
+                    return;
+                }
 
-            if (!dumpState.isVerbose()) {
-                pw.println(TAB1 + mCache.size() + " package(s)");
-                return;
-            }
+                if (mCache.size() == 0) {
+                    pw.println(TAB1 + "<empty>");
+                    return;
+                }
 
-            if (mCache.size() == 0) {
-                pw.println(TAB1 + "<empty>");
-                return;
-            }
-
-            for (int i = 0, n = mCache.size(); i < n; i++) {
-                final String packageName = mCache.keyAt(i);
-                final PackageStateUsers pkg = mCache.valueAt(i);
-                pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
-                pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+                for (int i = 0, n = mCache.size(); i < n; i++) {
+                    final String packageName = mCache.keyAt(i);
+                    final PackageStateUsers pkg = mCache.valueAt(i);
+                    pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
+                    pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING
index 82e7817..ce047bb 100644
--- a/services/core/java/com/android/server/om/TEST_MAPPING
+++ b/services/core/java/com/android/server/om/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.om."
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_om"
     },
     {
       "name": "OverlayDeviceTests"
diff --git a/services/core/java/com/android/server/pdb/TEST_MAPPING b/services/core/java/com/android/server/pdb/TEST_MAPPING
index 9e98023..ed6dfd8 100644
--- a/services/core/java/com/android/server/pdb/TEST_MAPPING
+++ b/services/core/java/com/android/server/pdb/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.pdb.PersistentDataBlockServiceTest"
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_pdb"
         }
     ]
 }
diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
index e3061a7..4135161 100644
--- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
+++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java
@@ -69,6 +69,7 @@
         mUserManager = mSystemUserContext.getSystemService(UserManager.class);
         NotificationChannel channel = new NotificationChannel(BUSN_CHANNEL_ID, BUSN_CHANNEL_NAME,
                 NotificationManager.IMPORTANCE_HIGH);
+        channel.setSound(null, null);
         mNotificationManager.createNotificationChannel(channel);
         setupFocusControlAudioPolicy();
     }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ee0159d..4665a72 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -3065,10 +3065,9 @@
             case DumpState.DUMP_PREFERRED_XML:
             {
                 pw.flush();
-                FileOutputStream fout = new FileOutputStream(fd);
-                BufferedOutputStream str = new BufferedOutputStream(fout);
                 TypedXmlSerializer serializer = Xml.newFastSerializer();
-                try {
+                try (BufferedOutputStream str =
+                             new BufferedOutputStream(new FileOutputStream(fd))) {
                     serializer.setOutput(str, StandardCharsets.UTF_8.name());
                     serializer.startDocument(null, true);
                     serializer.setFeature(
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index e34bdc6..9ecc7b9 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
 import static android.os.Trace.TRACE_TAG_DALVIK;
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
 
 import static com.android.server.LocalManagerRegistry.ManagerNotFoundException;
@@ -739,6 +740,78 @@
     }
 
     /**
+     * Perform dexopt if needed for the installation
+     */
+    static void performDexoptIfNeeded(InstallRequest installRequest, DexManager dexManager,
+            Context context, PackageManagerTracedLock.RawLock installLock) {
+
+        // Construct the DexoptOptions early to see if we should skip running dexopt.
+        //
+        // Do not run PackageDexOptimizer through the local performDexOpt
+        // method because `pkg` may not be in `mPackages` yet.
+        //
+        // Also, don't fail application installs if the dexopt step fails.
+        DexoptOptions dexoptOptions = getDexoptOptionsByInstallRequest(installRequest, dexManager);
+        // Check whether we need to dexopt the app.
+        //
+        // NOTE: it is IMPORTANT to call dexopt:
+        //   - after doRename which will sync the package data from AndroidPackage and
+        //     its corresponding ApplicationInfo.
+        //   - after installNewPackageLIF or replacePackageLIF which will update result with the
+        //     uid of the application (pkg.applicationInfo.uid).
+        //     This update happens in place!
+        //
+        // We only need to dexopt if the package meets ALL of the following conditions:
+        //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
+        //   2) it is not debuggable.
+        //   3) it is not on Incremental File System.
+        //
+        // Note that we do not dexopt instant apps by default. dexopt can take some time to
+        // complete, so we skip this step during installation. Instead, we'll take extra time
+        // the first time the instant app starts. It's preferred to do it this way to provide
+        // continuous progress to the useur instead of mysteriously blocking somewhere in the
+        // middle of running an instant app. The default behaviour can be overridden
+        // via gservices.
+        //
+        // Furthermore, dexopt may be skipped, depending on the install scenario and current
+        // state of the device.
+        //
+        // TODO(b/174695087): instantApp and onIncremental should be removed and their install
+        //       path moved to SCENARIO_FAST.
+
+        final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
+                dexoptOptions, context);
+        if (performDexopt) {
+            // dexopt can take long, and ArtService doesn't require installd, so we release
+            // the lock here and re-acquire the lock after dexopt is finished.
+            if (installLock != null) {
+                installLock.unlock();
+            }
+            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);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                if (installLock != null) {
+                    installLock.lock();
+                }
+            }
+        }
+    }
+
+    /**
      * Use ArtService to perform dexopt by the given InstallRequest.
      */
     static DexoptResult dexoptPackageUsingArtService(InstallRequest installRequest,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 98e3e24..43a285c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -172,11 +172,9 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.server.EventLogTags;
 import com.android.server.SystemConfig;
-import com.android.server.art.model.DexoptResult;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
-import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.permission.Permission;
@@ -209,6 +207,7 @@
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 
+
 final class InstallPackageHelper {
     private final PackageManagerService mPm;
     private final AppDataHelper mAppDataHelper;
@@ -686,6 +685,9 @@
                     (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
             final boolean fullApp =
                     (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+            final boolean isPackageDeviceAdmin = mPm.isPackageDeviceAdmin(packageName, userId);
+            final boolean isProtectedPackage = mPm.mProtectedPackages != null
+                    && mPm.mProtectedPackages.isPackageStateProtected(userId, packageName);
 
             // writer
             synchronized (mPm.mLock) {
@@ -694,7 +696,8 @@
                 if (pkgSetting == null || pkgSetting.getPkg() == null) {
                     return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
                 }
-                if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp())) {
+                if (instantApp && (pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()
+                        || isPackageDeviceAdmin || isProtectedPackage)) {
                     return Pair.create(PackageManager.INSTALL_FAILED_INVALID_URI, intentSender);
                 }
                 if (!snapshot.canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) {
@@ -985,15 +988,6 @@
         return false;
     }
 
-    void installPackagesTraced(List<InstallRequest> requests) {
-        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
-            installPackagesLI(requests);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-    }
-
     /**
      * Installs one or more packages atomically. This operation is broken up into four phases:
      * <ul>
@@ -1013,15 +1007,31 @@
      *
      * Failure at any phase will result in a full failure to install all packages.
      */
-    @GuardedBy("mPm.mInstallLock")
-    private void installPackagesLI(List<InstallRequest> requests) {
-        final Set<String> scannedPackages = new ArraySet<>(requests.size());
-        final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
-        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
-        CriticalEventLog.getInstance().logInstallPackagesStarted();
+    void installPackagesTraced(List<InstallRequest> requests) {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
         boolean success = false;
+        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
+        final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
         try {
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
+            CriticalEventLog.getInstance().logInstallPackagesStarted();
+
+            if (prepareInstallPackages(requests)
+                    && scanInstallPackages(requests, createdAppId, versionInfos)) {
+                List<ReconciledPackage> reconciledPackages =
+                        reconcileInstallPackages(requests, versionInfos);
+                if (reconciledPackages != null && commitInstallPackages(reconciledPackages)) {
+                    success = true;
+                }
+            }
+        } finally {
+            completeInstallProcess(requests, createdAppId, success);
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    private boolean prepareInstallPackages(List<InstallRequest> requests) {
+        // TODO: will remove the locking after doRename is moved out of prepare
+        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
             for (InstallRequest request : requests) {
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
@@ -1032,17 +1042,27 @@
                             prepareFailure.getMessage());
                     request.setOriginPackage(prepareFailure.mConflictingPackage);
                     request.setOriginPermission(prepareFailure.mConflictingPermission);
-                    return;
+                    return false;
                 } finally {
                     request.onPrepareFinished();
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
+            }
+        }
+        return true;
+    }
 
+    private boolean scanInstallPackages(List<InstallRequest> requests,
+            Map<String, Boolean> createdAppId, Map<String, Settings.VersionInfo> versionInfos) {
+        // TODO(b/362840929): remove locker
+        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+            final Set<String> scannedPackages = new ArraySet<>(requests.size());
+            for (InstallRequest request : requests) {
                 final ParsedPackage packageToScan = request.getParsedPackage();
                 if (packageToScan == null) {
                     request.setError(INSTALL_FAILED_SESSION_INVALID,
                             "Failed to obtain package to scan");
-                    return;
+                    return false;
                 }
                 request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
                 final String packageName = packageToScan.getPackageName();
@@ -1060,7 +1080,7 @@
                                 "Duplicate package "
                                         + packageName
                                         + " in multi-package install request.");
-                        return;
+                        return false;
                     }
                     if (!checkNoAppStorageIsConsistent(
                             request.getScanRequestOldPackage(), packageToScan)) {
@@ -1070,7 +1090,7 @@
                                 INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                 "Update attempted to change value of "
                                         + PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
-                        return;
+                        return false;
                     }
                     final boolean isApex = (request.getScanFlags() & SCAN_AS_APEX) != 0;
                     final boolean isSdkLibrary = packageToScan.isSdkLibrary();
@@ -1083,7 +1103,7 @@
                             mPm.getSettingsVersionForPackage(packageToScan));
                 } catch (PackageManagerException e) {
                     request.setError("Scanning Failed.", e);
-                    return;
+                    return false;
                 }
                 if (request.isArchived()) {
                     final SparseArray<String> responsibleInstallerTitles =
@@ -1095,17 +1115,22 @@
                         request.setError(PackageManagerException.ofInternalError(
                                 "Failed to obtain the responsible installer info",
                                 INTERNAL_ERROR_ARCHIVE_NO_INSTALLER_TITLE));
-                        return;
+                        return false;
                     }
                     request.setResponsibleInstallerTitles(responsibleInstallerTitles);
                 }
             }
+        }
+        return true;
+    }
 
-            List<ReconciledPackage> reconciledPackages;
+    private List<ReconciledPackage> reconcileInstallPackages(List<InstallRequest> requests,
+            Map<String, Settings.VersionInfo> versionInfos) {
+        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
             synchronized (mPm.mLock) {
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
-                    reconciledPackages = ReconcilePackageUtils.reconcilePackages(
+                    return ReconcilePackageUtils.reconcilePackages(
                             requests, Collections.unmodifiableMap(mPm.mPackages),
                             versionInfos, mSharedLibraries, mPm.mSettings.getKeySetManagerService(),
                             mPm.mSettings, mPm.mInjector.getSystemConfig());
@@ -1113,70 +1138,80 @@
                     for (InstallRequest request : requests) {
                         request.setError("Reconciliation failed...", e);
                     }
-                    return;
+                    return null;
                 } finally {
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
-                if (Flags.improveInstallFreeze()) {
-                    // Postpone freezer until after reconcile
-                    for (ReconciledPackage reconciledPkg : reconciledPackages) {
-                        InstallRequest installRequest = reconciledPkg.mInstallRequest;
-                        String packageName = installRequest.getParsedPackage().getPackageName();
-                        PackageFreezer freezer = freezePackageForInstall(packageName,
-                                UserHandle.USER_ALL, installRequest.getInstallFlags(),
-                                "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED,
-                                installRequest);
-                        installRequest.setFreezer(freezer);
-                    }
+            }
+        }
+    }
+
+
+    private boolean commitInstallPackages(List<ReconciledPackage> reconciledPackages) {
+        try (PackageManagerTracedLock installLock = mPm.mInstallLock.acquireLock()) {
+            if (Flags.improveInstallFreeze()) {
+                // Postpone freezer until after reconcile
+                for (ReconciledPackage reconciledPkg : reconciledPackages) {
+                    InstallRequest installRequest = reconciledPkg.mInstallRequest;
+                    String packageName = installRequest.getParsedPackage().getPackageName();
+                    PackageFreezer freezer = freezePackageForInstall(packageName,
+                            UserHandle.USER_ALL, installRequest.getInstallFlags(),
+                            "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED,
+                            installRequest);
+                    installRequest.setFreezer(freezer);
                 }
+            }
+            synchronized (mPm.mLock) {
                 try {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                     commitPackagesLocked(reconciledPackages, mPm.mUserManager.getUserIds());
-                    success = true;
                 } finally {
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             }
             executePostCommitStepsLIF(reconciledPackages);
-        } finally {
-            if (success) {
-                for (InstallRequest request : requests) {
-                    if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) {
-                        continue;
-                    }
-                    if (request.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
-                        continue;
-                    }
-                    // For incremental installs, we bypass the verifier prior to install. Now
-                    // that we know the package is valid, send a notice to the verifier with
-                    // the root hash of the base.apk.
-                    final String baseCodePath = request.getPkg().getBaseApkPath();
-                    final String[] splitCodePaths = request.getPkg().getSplitCodePaths();
-                    final Uri originUri = request.getOriginUri();
-                    final int verificationId = mPm.mPendingVerificationToken++;
-                    final String rootHashString = PackageManagerServiceUtils
-                            .buildVerificationRootHashString(baseCodePath, splitCodePaths);
-                    VerificationUtils.broadcastPackageVerified(verificationId, originUri,
-                            PackageManager.VERIFICATION_ALLOW, rootHashString,
-                            request.getDataLoaderType(), request.getUser(), mContext);
+        }
+        return true;
+    }
+
+    private void completeInstallProcess(List<InstallRequest> requests,
+            Map<String, Boolean> createdAppId, boolean success) {
+        if (success) {
+            for (InstallRequest request : requests) {
+                if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) {
+                    continue;
                 }
-            } else {
-                for (InstallRequest installRequest : requests) {
-                    if (installRequest.getParsedPackage() != null && createdAppId.getOrDefault(
-                            installRequest.getParsedPackage().getPackageName(), false)) {
-                        cleanUpAppIdCreation(installRequest);
-                    }
+                if (request.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
+                    continue;
                 }
-                // TODO(b/194319951): create a more descriptive reason than unknown
-                // mark all non-failure installs as UNKNOWN so we do not treat them as success
-                for (InstallRequest request : requests) {
-                    request.closeFreezer();
-                    if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
-                        request.setReturnCode(PackageManager.INSTALL_UNKNOWN);
-                    }
+                // For incremental installs, we bypass the verifier prior to install. Now
+                // that we know the package is valid, send a notice to the verifier with
+                // the root hash of the base.apk.
+                final String baseCodePath = request.getPkg().getBaseApkPath();
+                final String[] splitCodePaths = request.getPkg().getSplitCodePaths();
+                final Uri originUri = request.getOriginUri();
+                final int verificationId = mPm.mPendingVerificationToken++;
+                final String rootHashString = PackageManagerServiceUtils
+                        .buildVerificationRootHashString(baseCodePath, splitCodePaths);
+                VerificationUtils.broadcastPackageVerified(verificationId, originUri,
+                        PackageManager.VERIFICATION_ALLOW, rootHashString,
+                        request.getDataLoaderType(), request.getUser(), mContext);
+            }
+        } else {
+            for (InstallRequest installRequest : requests) {
+                if (installRequest.getParsedPackage() != null && createdAppId.getOrDefault(
+                        installRequest.getParsedPackage().getPackageName(), false)) {
+                    cleanUpAppIdCreation(installRequest);
                 }
             }
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            // TODO(b/194319951): create a more descriptive reason than unknown
+            // mark all non-failure installs as UNKNOWN so we do not treat them as success
+            for (InstallRequest request : requests) {
+                request.closeFreezer();
+                if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+                    request.setReturnCode(PackageManager.INSTALL_UNKNOWN);
+                }
+            }
         }
     }
 
@@ -1395,7 +1430,34 @@
                                 "Package " + pkgName + " is a persistent app. "
                                         + "Persistent apps are not updateable.");
                     }
+                    // When updating an sdk library, make sure that the versionMajor is
+                    // changed if the targetSdkVersion and minSdkVersion have changed
+                    if (parsedPackage.isSdkLibrary() && ps.getPkg() != null
+                            && ps.getPkg().isSdkLibrary()) {
+                        final int oldMinSdk = ps.getPkg().getMinSdkVersion();
+                        final int newMinSdk = parsedPackage.getMinSdkVersion();
+                        if (oldTargetSdk != newTargetSdk || oldMinSdk != newMinSdk) {
+                            final int oldVersionMajor = ps.getPkg().getSdkLibVersionMajor();
+                            final int newVersionMajor = parsedPackage.getSdkLibVersionMajor();
+                            if (oldVersionMajor == newVersionMajor) {
+                                throw new PrepareFailure(
+                                        PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                                        "Failure updating " + pkgName + " as it updates"
+                                                + " an sdk library <"
+                                                + parsedPackage.getSdkLibraryName() + ">"
+                                                + " without changing the versionMajor, but the"
+                                                + " targetSdkVersion or minSdkVersion has changed:"
+                                                + " Old targetSdkVersion: " + oldTargetSdk
+                                                + " new targetSdkVersion: " + newTargetSdk
+                                                + " Old minSdkVersion: " + oldMinSdk
+                                                + " new minSdkVersion: " + newMinSdk
+                                                + " versionMajor: " + newVersionMajor
+                                    );
+                            }
+                        }
+                    }
                 }
+
             }
 
             PackageSetting signatureCheckPs = ps;
@@ -2205,8 +2267,9 @@
                 //  by apexd to be more accurate.
                 installRequest.setScannedPackageSettingFirstInstallTimeFromReplaced(
                         deletedPkgSetting, allUsers);
-                installRequest.setScannedPackageSettingLastUpdateTime(
-                        System.currentTimeMillis());
+                long currentTime = System.currentTimeMillis();
+                installRequest.setScannedPackageSettingLastUpdateTime(currentTime);
+                installRequest.setScannedPackageSettingFirstInstallTime(currentTime);
 
                 installRequest.getRemovedInfo().mBroadcastAllowList =
                         mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(),
@@ -2568,70 +2631,8 @@
                         pkg.getBaseApkPath(), pkg.getSplitCodePaths());
             }
 
-            // Construct the DexoptOptions early to see if we should skip running dexopt.
-            //
-            // Do not run PackageDexOptimizer through the local performDexOpt
-            // method because `pkg` may not be in `mPackages` yet.
-            //
-            // Also, don't fail application installs if the dexopt step fails.
-            DexoptOptions dexoptOptions = DexOptHelper.getDexoptOptionsByInstallRequest(
-                    installRequest, mDexManager);
-            // Check whether we need to dexopt the app.
-            //
-            // NOTE: it is IMPORTANT to call dexopt:
-            //   - after doRename which will sync the package data from AndroidPackage and
-            //     its corresponding ApplicationInfo.
-            //   - after installNewPackageLIF or replacePackageLIF which will update result with the
-            //     uid of the application (pkg.applicationInfo.uid).
-            //     This update happens in place!
-            //
-            // We only need to dexopt if the package meets ALL of the following conditions:
-            //   1) it is not an instant app or if it is then dexopt is enabled via gservices.
-            //   2) it is not debuggable.
-            //   3) it is not on Incremental File System.
-            //
-            // Note that we do not dexopt instant apps by default. dexopt can take some time to
-            // complete, so we skip this step during installation. Instead, we'll take extra time
-            // the first time the instant app starts. It's preferred to do it this way to provide
-            // continuous progress to the useur instead of mysteriously blocking somewhere in the
-            // middle of running an instant app. The default behaviour can be overridden
-            // via gservices.
-            //
-            // Furthermore, dexopt may be skipped, depending on the install scenario and current
-            // state of the device.
-            //
-            // TODO(b/174695087): instantApp and onIncremental should be removed and their install
-            //       path moved to SCENARIO_FAST.
-
-            final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
-                    dexoptOptions, mContext);
-            if (performDexopt) {
-                // dexopt can take long, and ArtService doesn't require installd, so we release
-                // the lock here and re-acquire the lock after dexopt is finished.
-                PackageManagerTracedLock.RawLock installLock = mPm.mInstallLock.getRawLock();
-                installLock.unlock();
-                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);
-                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-                } finally {
-                    installLock.lock();
-                }
-            }
+            DexOptHelper.performDexoptIfNeeded(installRequest, mDexManager, mContext,
+                    mPm.mInstallLock.getRawLock());
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
                 incrementalStorages);
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index dd2583a0d..ae7749b 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -58,6 +58,7 @@
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageUserStateInternal;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -867,6 +868,14 @@
         mScanResult.mPkgSetting.setLastUpdateTime(lastUpdateTim);
     }
 
+    public void setScannedPackageSettingFirstInstallTime(long firstInstallTime) {
+        assertScanResultExists();
+        PackageUserStateInternal userState = mScanResult.mPkgSetting.getUserStates().get(mUserId);
+        if (userState != null && userState.getFirstInstallTimeMillis() == 0) {
+            mScanResult.mPkgSetting.setFirstInstallTime(firstInstallTime, mUserId);
+        }
+    }
+
     public void setRemovedAppId(int appId) {
         if (mRemovedInfo != null) {
             mRemovedInfo.mUid = appId;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 20859da..ff9c3e5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -60,6 +60,7 @@
 import android.app.ApplicationPackageManager;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
@@ -2034,6 +2035,10 @@
         // CHECKSTYLE:ON IndentationCheck
         t.traceEnd();
 
+        t.traceBegin("get system config");
+        SystemConfig systemConfig = injector.getSystemConfig();
+        t.traceEnd();
+
         t.traceBegin("addSharedUsers");
         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
@@ -2053,6 +2058,13 @@
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        final ArrayMap<String, Integer> oemDefinedUids = systemConfig.getOemDefinedUids();
+        final int numOemDefinedUids = oemDefinedUids.size();
+        for (int i = 0; i < numOemDefinedUids; i++) {
+            mSettings.addOemSharedUserLPw(oemDefinedUids.keyAt(i), oemDefinedUids.valueAt(i),
+                    ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        }
+
         t.traceEnd();
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
@@ -2084,10 +2096,7 @@
         mContext.getSystemService(DisplayManager.class)
                 .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
 
-        t.traceBegin("get system config");
-        SystemConfig systemConfig = injector.getSystemConfig();
         mAvailableFeatures = systemConfig.getAvailableFeatures();
-        t.traceEnd();
 
         mProtectedPackages = new ProtectedPackages(mContext);
 
@@ -3364,8 +3373,10 @@
     // TODO(b/261957226): centralise this logic in DPM
     boolean isPackageDeviceAdmin(String packageName, int userId) {
         final IDevicePolicyManager dpm = getDevicePolicyManager();
+        final DevicePolicyManagerInternal dpmi =
+                mInjector.getLocalService(DevicePolicyManagerInternal.class);
         try {
-            if (dpm != null) {
+            if (dpm != null && dpmi != null) {
                 final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
                         /* callingUserOnly =*/ false);
                 final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
@@ -3388,7 +3399,8 @@
                     if (dpm.packageHasActiveAdmins(packageName, users[i])) {
                         return true;
                     }
-                    if (isDeviceManagementRoleHolder(packageName, users[i])) {
+                    if (isDeviceManagementRoleHolder(packageName, users[i])
+                            && dpmi.isUserOrganizationManaged(users[i])) {
                         return true;
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index d374142..9fb9e71 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -42,6 +42,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
@@ -981,39 +982,23 @@
     }
 
     int[] queryInstalledUsers(int[] users, boolean installed) {
-        int num = 0;
+        IntArray installedUsers = new IntArray(users.length);
         for (int user : users) {
             if (getInstalled(user) == installed) {
-                num++;
+                installedUsers.add(user);
             }
         }
-        int[] res = new int[num];
-        num = 0;
-        for (int user : users) {
-            if (getInstalled(user) == installed) {
-                res[num] = user;
-                num++;
-            }
-        }
-        return res;
+        return installedUsers.toArray();
     }
 
     int[] queryUsersInstalledOrHasData(int[] users) {
-        int num = 0;
+        IntArray usersInstalledOrHasData = new IntArray(users.length);
         for (int user : users) {
             if (getInstalled(user) || readUserState(user).dataExists()) {
-                num++;
+                usersInstalledOrHasData.add(user);
             }
         }
-        int[] res = new int[num];
-        num = 0;
-        for (int user : users) {
-            if (getInstalled(user) || readUserState(user).dataExists()) {
-                res[num] = user;
-                num++;
-            }
-        }
-        return res;
+        return usersInstalledOrHasData.toArray();
     }
 
     long getCeDataInode(int userId) {
@@ -1283,25 +1268,14 @@
     }
 
     public int[] getNotInstalledUserIds() {
-        int count = 0;
         int userStateCount = mUserStates.size();
+        IntArray notInstalledUsers = new IntArray(userStateCount);
         for (int i = 0; i < userStateCount; i++) {
             if (!mUserStates.valueAt(i).isInstalled()) {
-                count++;
+                notInstalledUsers.add(mUserStates.keyAt(i));
             }
         }
-        if (count == 0) {
-            return EmptyArray.INT;
-        }
-
-        int[] excludedUserIds = new int[count];
-        int idx = 0;
-        for (int i = 0; i < userStateCount; i++) {
-            if (!mUserStates.valueAt(i).isInstalled()) {
-                excludedUserIds[idx++] = mUserStates.keyAt(i);
-            }
-        }
-        return excludedUserIds;
+        return notInstalledUsers.toArray();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 26da84f..f9a8968 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -292,8 +292,10 @@
         // Step 2: destroy app data.
         mAppDataHelper.destroyAppDataLIF(resolvedPkg, userId, appDataDeletionFlags);
         if (userId != UserHandle.USER_ALL) {
-            ps.setCeDataInode(-1, userId);
-            ps.setDeDataInode(-1, userId);
+            synchronized (mPm.mLock) {
+                ps.setCeDataInode(-1, userId);
+                ps.setDeDataInode(-1, userId);
+            }
         }
 
         final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm,
@@ -425,19 +427,21 @@
             }
             final boolean isArchive = (flags & PackageManager.DELETE_ARCHIVE) != 0;
             final long currentTimeMillis = System.currentTimeMillis();
-            for (int userId : outInfo.mRemovedUsers) {
-                if (DEBUG_REMOVE) {
-                    final boolean wasInstalled = deletedPs.getInstalled(userId);
-                    Slog.d(TAG, "    user " + userId + ": " + wasInstalled + " => " + false);
+            synchronized (mPm.mLock) {
+                for (int userId : outInfo.mRemovedUsers) {
+                    if (DEBUG_REMOVE) {
+                        final boolean wasInstalled = deletedPs.getInstalled(userId);
+                        Slog.d(TAG, "    user " + userId + ": " + wasInstalled + " => " + false);
+                    }
+                    deletedPs.setInstalled(/* installed= */ false, userId);
                 }
-                deletedPs.setInstalled(/* installed= */ false, userId);
-            }
 
-            // Preserve split apk information for downgrade check with DELETE_KEEP_DATA and archived
-            // app cases
-            if (deletedPkg != null && deletedPkg.getSplitNames() != null) {
-                deletedPs.setSplitNames(deletedPkg.getSplitNames());
-                deletedPs.setSplitRevisionCodes(deletedPkg.getSplitRevisionCodes());
+                // Preserve split apk information for downgrade check with DELETE_KEEP_DATA and
+                // archived app cases
+                if (deletedPkg != null && deletedPkg.getSplitNames() != null) {
+                    deletedPs.setSplitNames(deletedPkg.getSplitNames());
+                    deletedPs.setSplitRevisionCodes(deletedPkg.getSplitRevisionCodes());
+                }
             }
         }
 
@@ -448,17 +452,19 @@
             if (DEBUG_REMOVE) {
                 Slog.d(TAG, "Propagating install state across downgrade");
             }
-            for (int userId : allUserHandles) {
-                final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId);
-                if (DEBUG_REMOVE) {
-                    Slog.d(TAG, "    user " + userId + " => " + installed);
-                }
-                if (installed != deletedPs.getInstalled(userId)) {
-                    installedStateChanged = true;
-                }
-                deletedPs.setInstalled(installed, userId);
-                if (installed) {
-                    deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+            synchronized (mPm.mLock) {
+                for (int userId : allUserHandles) {
+                    final boolean installed = ArrayUtils.contains(outInfo.mOrigUsers, userId);
+                    if (DEBUG_REMOVE) {
+                        Slog.d(TAG, "    user " + userId + " => " + installed);
+                    }
+                    if (installed != deletedPs.getInstalled(userId)) {
+                        installedStateChanged = true;
+                    }
+                    deletedPs.setInstalled(installed, userId);
+                    if (installed) {
+                        deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId);
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 61fddba..0802e9e 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -443,17 +443,16 @@
 
         // Take care of first install / last update times.
         final long scanFileTime = getLastModifiedTime(parsedPackage);
-        final long existingFirstInstallTime = userId == UserHandle.USER_ALL
-                ? PackageStateUtils.getEarliestFirstInstallTime(pkgSetting.getUserStates())
-                : pkgSetting.readUserState(userId).getFirstInstallTimeMillis();
+        final long earliestFirstInstallTime =
+                PackageStateUtils.getEarliestFirstInstallTime((pkgSetting.getUserStates()));
         if (currentTime != 0) {
-            if (existingFirstInstallTime == 0) {
+            if (earliestFirstInstallTime == 0) {
                 pkgSetting.setFirstInstallTime(currentTime, userId)
                         .setLastUpdateTime(currentTime);
             } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
                 pkgSetting.setLastUpdateTime(currentTime);
             }
-        } else if (existingFirstInstallTime == 0) {
+        } else if (earliestFirstInstallTime == 0) {
             // We need *something*.  Take time stamp of the file.
             pkgSetting.setFirstInstallTime(scanFileTime, userId)
                     .setLastUpdateTime(scanFileTime);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 55280b4..1f672a0 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -977,6 +977,21 @@
         return null;
     }
 
+    SharedUserSetting addOemSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
+        if (!name.startsWith("android.uid")) {
+            PackageManagerService.reportSettingsProblem(Log.ERROR,
+                    "Failed to add oem defined shared user because of invalid name: " + name);
+            return null;
+        }
+        // OEM defined uids must be in the OEM reserved range
+        if (uid < 2900 || uid > 2999) {
+            PackageManagerService.reportSettingsProblem(Log.ERROR,
+                    "Failed to add oem defined shared user because of invalid uid: " + uid);
+            return null;
+        }
+        return addSharedUserLPw(name, uid, pkgFlags, pkgPrivateFlags);
+    }
+
     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
         SharedUserSetting s = mSharedUsers.get(name);
         if (s != null) {
@@ -5560,18 +5575,18 @@
                     }
                     if (!paths.getOverlayPaths().isEmpty()) {
                         pw.print(prefix);
-                        pw.println("    ");
+                        pw.print("    ");
                         pw.print(libOverlayPaths.getKey());
                         pw.println(" overlay paths:");
                         for (String path : paths.getOverlayPaths()) {
                             pw.print(prefix);
-                            pw.print("        ");
+                            pw.print("      ");
                             pw.println(path);
                         }
                     }
                     if (!paths.getResourceDirs().isEmpty()) {
                         pw.print(prefix);
-                        pw.println("      ");
+                        pw.print("    ");
                         pw.print(libOverlayPaths.getKey());
                         pw.println(" legacy overlay paths:");
                         for (String path : paths.getResourceDirs()) {
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 00582bf..045d4db 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -282,6 +282,12 @@
             for (int j = 0; j < idSize; j++) {
                 ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
             }
+            if (ShortcutService.DEBUG_REBOOT) {
+                Slog.d(TAG, "Persist shortcut ids pinned by "
+                    + getPackageName() + " from "
+                    + up.userId + "@" + up.packageName + " ids=["
+                    + String.join(", ", ids) + "]");
+            }
             out.endTag(null, TAG_PACKAGE);
         }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 84674b2..60056eb 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1850,9 +1850,17 @@
             }
             getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
 
+            if (ShortcutService.DEBUG_REBOOT) {
+                Slog.d(TAG, "Persisting shortcuts from "
+                    + getOwnerUserId() + "@" + getPackageName());
+            }
             for (int j = 0; j < size; j++) {
+                final ShortcutInfo si = mShortcuts.valueAt(j);
                 saveShortcut(
-                        out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
+                        out, si, forBackup, getPackageInfo().isBackupAllowed());
+                if (ShortcutService.DEBUG_REBOOT) {
+                    Slog.d(TAG, si.toSimpleString());
+                }
             }
 
             if (!forBackup) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 021f7aa..a3ff195 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -172,7 +172,7 @@
     static final boolean DEBUG = false; // STOPSHIP if true
     static final boolean DEBUG_LOAD = false; // STOPSHIP if true
     static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
-    static final boolean DEBUG_REBOOT = true;
+    static final boolean DEBUG_REBOOT = Build.IS_DEBUGGABLE;
 
     @VisibleForTesting
     static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -588,7 +588,7 @@
 
     void handleOnDefaultLauncherChanged(int userId) {
         if (DEBUG) {
-            Slog.v(TAG, "Default launcher changed for user: " + userId);
+            Slog.v(TAG, "Default launcher changed for userId=" + userId);
         }
 
         // Default launcher is removed or changed, revoke all URI permissions.
@@ -712,7 +712,7 @@
     /** lifecycle event */
     void handleUnlockUser(int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "handleUnlockUser: user=" + userId);
+            Slog.d(TAG, "handleUnlockUser: userId=" + userId);
         }
         synchronized (mUnlockedUsers) {
             mUnlockedUsers.put(userId, true);
@@ -739,7 +739,7 @@
     /** lifecycle event */
     void handleStopUser(int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "handleStopUser: user=" + userId);
+            Slog.d(TAG, "handleStopUser: userId=" + userId);
         }
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "shortcutHandleStopUser");
         synchronized (mServiceLock) {
@@ -755,7 +755,7 @@
     @GuardedBy("mServiceLock")
     private void unloadUserLocked(int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "unloadUserLocked: user=" + userId);
+            Slog.d(TAG, "unloadUserLocked: userId=" + userId);
         }
         // Cancel any ongoing background tasks.
         getUserShortcutsLocked(userId).cancelAllInFlightTasks();
@@ -1221,7 +1221,7 @@
 
     private void scheduleSaveInner(@UserIdInt int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "Scheduling to save for " + userId);
+            Slog.d(TAG, "Scheduling to save for userId=" + userId);
         }
         synchronized (mServiceLock) {
             if (!mDirtyUserIds.contains(userId)) {
@@ -1333,7 +1333,7 @@
     // Requires mServiceLock held, but "Locked" prefix would look weird so we just say "L".
     void throwIfUserLockedL(@UserIdInt int userId) {
         if (!isUserUnlockedL(userId)) {
-            throw new IllegalStateException("User " + userId + " is locked or not running");
+            throw new IllegalStateException("User (with userId=" + userId + ") is locked or not running");
         }
     }
 
@@ -1720,7 +1720,7 @@
 
         // Otherwise, make sure the arguments are valid.
         if (UserHandle.getUserId(callingUid) != userId) {
-            throw new SecurityException("Invalid user-ID");
+            throw new SecurityException("Invalid userId");
         }
     }
 
@@ -1735,7 +1735,7 @@
 
         // Otherwise, make sure the arguments are valid.
         if (UserHandle.getUserId(callingUid) != userId) {
-            throw new SecurityException("Invalid user-ID");
+            throw new SecurityException("Invalid userId");
         }
         if (injectGetPackageUid(packageName, userId) != callingUid) {
             throw new SecurityException("Calling package name mismatch");
@@ -1755,7 +1755,7 @@
         }
         final int callingUid = injectBinderCallingUid();
         if (UserHandle.getUserId(callingUid) != si.getUserId()) {
-            throw new SecurityException("User-ID in shortcut doesn't match the caller");
+            throw new SecurityException("UserId in shortcut doesn't match the caller");
         }
     }
 
@@ -1822,7 +1822,7 @@
         final int userId = sp.getPackageUserId();
         if (DEBUG) {
             Slog.d(TAG, String.format(
-                    "Shortcut changes: package=%s, user=%d", packageName, userId));
+                    "Shortcut changes: package=%s, userId=%d", packageName, userId));
         }
         injectPostToHandlerDebounced(sp, notifyListenerRunnable(packageName, userId));
         notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
@@ -1832,7 +1832,7 @@
     private void notifyListeners(@NonNull final String packageName, @UserIdInt final int userId) {
         if (DEBUG) {
             Slog.d(TAG, String.format(
-                    "Shortcut changes: package=%s, user=%d", packageName, userId));
+                    "Shortcut changes: package=%s, userId=%d", packageName, userId));
         }
         injectPostToHandler(notifyListenerRunnable(packageName, userId));
     }
@@ -2736,14 +2736,14 @@
     void resetThrottlingInner(@UserIdInt int userId) {
         synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
-                Log.w(TAG, "User " + userId + " is locked or not running");
+                Log.w(TAG, "User (with userId=" + userId + ") is locked or not running");
                 return;
             }
 
             getUserShortcutsLocked(userId).resetThrottling();
         }
         scheduleSaveUser(userId);
-        Slog.i(TAG, "ShortcutManager: throttling counter reset for user " + userId);
+        Slog.i(TAG, "ShortcutManager: throttling counter reset for userId=" + userId);
     }
 
     void resetAllThrottlingInner() {
@@ -2755,7 +2755,7 @@
     @Override
     public void onApplicationActive(String packageName, int userId) {
         if (DEBUG) {
-            Slog.d(TAG, "onApplicationActive: package=" + packageName + "  userid=" + userId);
+            Slog.d(TAG, "onApplicationActive: package=" + packageName + "  userId=" + userId);
         }
         enforceResetThrottlingPermission();
         synchronized (mServiceLock) {
@@ -2822,7 +2822,7 @@
 
             if (defaultLauncher != null) {
                 if (DEBUG) {
-                    Slog.v(TAG, "Detected launcher: " + defaultLauncher + " user: " + userId);
+                    Slog.v(TAG, "Detected launcher: " + defaultLauncher + " userId=" + userId);
                 }
                 return defaultLauncher.equals(packageName);
             } else {
@@ -2875,11 +2875,11 @@
                 if (defaultLauncher != null) {
                     if (DEBUG) {
                         Slog.v(TAG, "Default launcher from RoleManager: " + defaultLauncher
-                                + " user: " + userId);
+                                + " userId=" + userId);
                     }
                     user.setCachedLauncher(defaultLauncher);
                 } else {
-                    Slog.e(TAG, "Default launcher not found." + " user: " + userId);
+                    Slog.e(TAG, "Default launcher not found." + " userId=" + userId);
                 }
 
                 return defaultLauncher;
@@ -2974,7 +2974,7 @@
                 int queryFlags, int userId, int callingPid, int callingUid) {
             if (DEBUG_REBOOT) {
                 Slog.d(TAG, "Getting shortcuts for launcher= " + callingPackage
-                        + "user=" + userId + " pkg=" + packageName);
+                        + "userId=" + userId + " pkg=" + packageName);
             }
             final ArrayList<ShortcutInfo> ret = new ArrayList<>();
 
@@ -3793,7 +3793,7 @@
                     if (!isUserUnlockedL(userId)) {
                         if (DEBUG) {
                             Slog.d(TAG, "Ignoring package broadcast " + action
-                                    + " for locked/stopped user " + userId);
+                                    + " for locked/stopped userId=" + userId);
                         }
                         return;
                     }
@@ -3814,10 +3814,10 @@
                 switch (action) {
                     case Intent.ACTION_PACKAGE_ADDED:
                         if (replacing) {
-                            Slog.d(TAG, "replacing package: " + packageName + " userId" + userId);
+                            Slog.d(TAG, "replacing package: " + packageName + " userId=" + userId);
                             handlePackageUpdateFinished(packageName, userId);
                         } else {
-                            Slog.d(TAG, "adding package: " + packageName + " userId" + userId);
+                            Slog.d(TAG, "adding package: " + packageName + " userId=" + userId);
                             handlePackageAdded(packageName, userId);
                         }
                         break;
@@ -3825,21 +3825,21 @@
                         if (!replacing || (replacing && archival)) {
                             if (!replacing) {
                                 Slog.d(TAG, "removing package: "
-                                        + packageName + " userId" + userId);
+                                        + packageName + " userId=" + userId);
                             } else if (archival) {
                                 Slog.d(TAG, "archiving package: "
-                                        + packageName + " userId" + userId);
+                                        + packageName + " userId=" + userId);
                             }
                             handlePackageRemoved(packageName, userId);
                         }
                         break;
                     case Intent.ACTION_PACKAGE_CHANGED:
-                        Slog.d(TAG, "changing package: " + packageName + " userId" + userId);
+                        Slog.d(TAG, "changing package: " + packageName + " userId=" + userId);
                         handlePackageChanged(packageName, userId);
                         break;
                     case Intent.ACTION_PACKAGE_DATA_CLEARED:
                         Slog.d(TAG, "clearing data for package: "
-                                + packageName + " userId" + userId);
+                                + packageName + " userId=" + userId);
                         handlePackageDataCleared(packageName, userId);
                         break;
                 }
@@ -3902,7 +3902,7 @@
                     if (!isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
                         if (DEBUG) {
                             Slog.d(TAG, "Uninstalled: " + spi.getPackageName()
-                                    + " user " + spi.getPackageUserId());
+                                    + " userId=" + spi.getPackageUserId());
                         }
                         gonePackages.add(
                                 UserPackage.of(spi.getPackageUserId(), spi.getPackageName()));
@@ -3927,7 +3927,7 @@
     @GuardedBy("mServiceLock")
     private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
         if (DEBUG_REBOOT) {
-            Slog.d(TAG, "rescan updated package user=" + userId + " last scanned=" + lastScanTime);
+            Slog.d(TAG, "rescan updated package userId=" + userId + " last scanned=" + lastScanTime);
         }
         final ShortcutUser user = getUserShortcutsLocked(userId);
 
@@ -3953,7 +3953,7 @@
 
     private void handlePackageAdded(String packageName, @UserIdInt int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
+            Slog.d(TAG, String.format("handlePackageAdded: %s userId=%d", packageName, userId));
         }
         synchronized (mServiceLock) {
             final ShortcutUser user = getUserShortcutsLocked(userId);
@@ -3965,7 +3965,7 @@
 
     private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
+            Slog.d(TAG, String.format("handlePackageUpdateFinished: %s userId=%d",
                     packageName, userId));
         }
         synchronized (mServiceLock) {
@@ -3981,7 +3981,7 @@
 
     private void handlePackageRemoved(String packageName, @UserIdInt int packageUserId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName,
+            Slog.d(TAG, String.format("handlePackageRemoved: %s userId=%d", packageName,
                     packageUserId));
         }
         cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ false);
@@ -3991,7 +3991,7 @@
 
     private void handlePackageDataCleared(String packageName, int packageUserId) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, String.format("handlePackageDataCleared: %s user=%d", packageName,
+            Slog.d(TAG, String.format("handlePackageDataCleared: %s userId=%d", packageName,
                     packageUserId));
         }
         cleanUpPackageForAllLoadedUsers(packageName, packageUserId, /* appStillExists = */ true);
@@ -4006,7 +4006,7 @@
             return;
         }
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, String.format("handlePackageChanged: %s user=%d", packageName,
+            Slog.d(TAG, String.format("handlePackageChanged: %s userId=%d", packageName,
                     packageUserId));
         }
 
@@ -4193,7 +4193,7 @@
     private void forUpdatedPackages(@UserIdInt int userId, long lastScanTime, boolean afterOta,
             Consumer<ApplicationInfo> callback) {
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "forUpdatedPackages for user " + userId + ", lastScanTime=" + lastScanTime
+            Slog.d(TAG, "forUpdatedPackages for userId=" + userId + ", lastScanTime=" + lastScanTime
                     + " afterOta=" + afterOta);
         }
         final List<PackageInfo> list = getInstalledPackages(userId);
@@ -4302,7 +4302,7 @@
             return mContext.createContextAsUser(UserHandle.of(userId), /* flags */ 0)
                     .getPackageManager().getResourcesForApplication(packageName);
         } catch (NameNotFoundException e) {
-            Slog.e(TAG, "Resources of package " + packageName + " for user " + userId
+            Slog.e(TAG, "Resources of package " + packageName + " for userId=" + userId
                     + " not found");
             return null;
         } finally {
@@ -4512,17 +4512,17 @@
     public byte[] getBackupPayload(@UserIdInt int userId) {
         enforceSystem();
         if (DEBUG) {
-            Slog.d(TAG, "Backing up user " + userId);
+            Slog.d(TAG, "Backing up user with userId=" + userId);
         }
         synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
-                wtf("Can't backup: user " + userId + " is locked or not running");
+                wtf("Can't backup: userId=" + userId + " is locked or not running");
                 return null;
             }
 
             final ShortcutUser user = getUserShortcutsLocked(userId);
             if (user == null) {
-                wtf("Can't backup: user not found: id=" + userId);
+                wtf("Can't backup: user not found: userId=" + userId);
                 return null;
             }
 
@@ -4562,11 +4562,11 @@
     public void applyRestore(byte[] payload, @UserIdInt int userId) {
         enforceSystem();
         if (DEBUG || DEBUG_REBOOT) {
-            Slog.d(TAG, "Restoring user " + userId);
+            Slog.d(TAG, "Restoring user with userId=" + userId);
         }
         synchronized (mServiceLock) {
             if (!isUserUnlockedL(userId)) {
-                wtf("Can't restore: user " + userId + " is locked or not running");
+                wtf("Can't restore: user (with userId=" + userId + ") is locked or not running");
                 return;
             }
             // Note we print the file timestamps in dumpsys too, but also printing the timestamp
@@ -4989,7 +4989,7 @@
                             mUserId = UserHandle.parseUserArg(getNextArgRequired());
                             if (!isUserUnlockedL(mUserId)) {
                                 throw new CommandException(
-                                        "User " + mUserId + " is not running or locked");
+                                        "User (with userId=" + mUserId + ") is not running or locked");
                             }
                             break;
                         }
@@ -5094,7 +5094,7 @@
             synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
 
-                Slog.i(TAG, "cmd: handleResetThrottling: user=" + mUserId);
+                Slog.i(TAG, "cmd: handleResetThrottling: userId=" + mUserId);
 
                 resetThrottlingInner(mUserId);
             }
@@ -5136,7 +5136,7 @@
                 final String defaultLauncher = getDefaultLauncher(mUserId);
                 if (defaultLauncher == null) {
                     throw new CommandException(
-                            "Failed to get the default launcher for user " + mUserId);
+                            "Failed to get the default launcher for userId=" + mUserId);
                 }
 
                 // Get the class name of the component from PM to keep the old behaviour.
@@ -5157,7 +5157,7 @@
             synchronized (mServiceLock) {
                 parseOptionsLocked(/* takeUser =*/ true);
 
-                Slog.i(TAG, "cmd: handleUnloadUser: user=" + mUserId);
+                Slog.i(TAG, "cmd: handleUnloadUser: userId=" + mUserId);
 
                 ShortcutService.this.handleStopUser(mUserId);
             }
@@ -5168,7 +5168,7 @@
                 parseOptionsLocked(/* takeUser =*/ true);
                 final String packageName = getNextArgRequired();
 
-                Slog.i(TAG, "cmd: handleClearShortcuts: user" + mUserId + ", " + packageName);
+                Slog.i(TAG, "cmd: handleClearShortcuts: userId=" + mUserId + ", " + packageName);
 
                 ShortcutService.this.cleanUpPackageForAllLoadedUsers(packageName, mUserId,
                         /* appStillExists = */ true);
@@ -5180,7 +5180,7 @@
                 parseOptionsLocked(/* takeUser =*/ true);
                 final String packageName = getNextArgRequired();
 
-                Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+                Slog.i(TAG, "cmd: handleGetShortcuts: userId=" + mUserId + ", flags="
                         + mShortcutMatchFlags + ", package=" + packageName);
 
                 final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 74594cc..94bdfbd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -56,8 +56,8 @@
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
+import com.android.server.rollback.ApexdRevertLogger;
 import com.android.server.rollback.RollbackManagerInternal;
-import com.android.server.rollback.WatchdogRollbackLogger;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
@@ -764,7 +764,7 @@
     private void logFailedApexSessionsIfNecessary() {
         synchronized (mFailedPackageNames) {
             if (!mFailedPackageNames.isEmpty()) {
-                WatchdogRollbackLogger.logApexdRevert(mContext,
+                ApexdRevertLogger.logApexdRevert(mContext,
                         mFailedPackageNames, mNativeFailureReason);
             }
         }
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index c40608d..c75622c 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -29,12 +29,7 @@
       "name": "CtsMatchFlagTestCases"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.pm."
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_pm"
     },
     {
       "file_patterns": ["(/|^)PackageManagerService\\.java","(/|^)UserManagerService\\.java"],
@@ -104,6 +99,54 @@
           "include-filter": "android.appsecurity.cts.EphemeralTest#testGetSearchableInfo"
         }
       ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUninstallationTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
     }
   ],
   "presubmit-large":[
@@ -163,6 +206,70 @@
     },
     {
       "name": "CtsUpdateOwnershipEnforcementTestCases"
+    },
+    {
+      "name": "CtsPackageInstallerCUJInstallationTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUninstallationTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateOwnerShipTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
+    },
+    {
+      "name": "CtsPackageInstallerCUJUpdateSelfTestCases",
+      "file_patterns": [
+        "core/java/.*Install.*",
+        "services/core/.*Install.*",
+        "services/core/java/com/android/server/pm/.*"
+      ],
+      "options":[
+          {
+              "exclude-annotation":"androidx.test.filters.FlakyTest"
+          },
+          {
+              "exclude-annotation":"org.junit.Ignore"
+          }
+      ]
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 57d7d79..a683a8c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -25,6 +25,7 @@
 import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
+import static android.os.UserHandle.USER_SYSTEM;
 import static android.os.UserManager.DEV_CREATE_OVERRIDE_PROPERTY;
 import static android.os.UserManager.DISALLOW_USER_SWITCH;
 import static android.os.UserManager.SYSTEM_USER_MODE_EMULATION_PROPERTY;
@@ -379,6 +380,10 @@
 
     /** Count down latch to wait while boot user is not set.*/
     private final CountDownLatch mBootUserLatch = new CountDownLatch(1);
+
+    /** Current boot phase. */
+    private @SystemService.BootPhase int mCurrentBootPhase;
+
     /**
      * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
      */
@@ -968,6 +973,7 @@
 
         @Override
         public void onBootPhase(int phase) {
+            mUms.mCurrentBootPhase = phase;
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                 mUms.cleanupPartialUsers();
 
@@ -1367,6 +1373,10 @@
         }
 
         if (isHeadlessSystemUserMode()) {
+            if (mContext.getResources()
+                    .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser)) {
+                return UserHandle.USER_SYSTEM;
+            }
             // Return the previous foreground user, if there is one.
             final int previousUser = getPreviousFullUserToEnterForeground();
             if (previousUser != UserHandle.USER_NULL) {
@@ -2509,6 +2519,38 @@
     }
 
     /**
+     * This method validates whether calling user is valid in visible background users feature.
+     * Valid user is the current user or the system or in the same profile group as the current
+     * user. Visible background users are not valid calling users.
+     */
+    public static void enforceCurrentUserIfVisibleBackgroundEnabled(@UserIdInt int currentUserId) {
+        if (!UserManager.isVisibleBackgroundUsersEnabled()) {
+            return;
+        }
+        final int callingUserId = UserHandle.getCallingUserId();
+        if (DBG) {
+            Slog.d(LOG_TAG, "enforceValidCallingUser: callingUserId=" + callingUserId
+                    + " isSystemUser=" + (callingUserId == USER_SYSTEM)
+                    + " currentUserId=" + currentUserId
+                    + " callingPid=" + Binder.getCallingPid()
+                    + " callingUid=" + Binder.getCallingUid());
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (callingUserId != USER_SYSTEM && callingUserId != currentUserId
+                    && !UserManagerService.getInstance()
+                    .isSameProfileGroup(callingUserId, currentUserId)) {
+                throw new SecurityException(
+                        "Invalid calling user on devices that enable visible background users. "
+                                + "callingUserId=" + callingUserId + " currentUserId="
+                                + currentUserId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
      * Gets the current and target user ids as a {@link Pair}, calling
      * {@link ActivityManagerInternal} directly (and without performing any permission check).
      *
@@ -6204,6 +6246,11 @@
             Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " 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.");
+            return false;
+        }
         return removeUserWithProfilesUnchecked(userId);
     }
 
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 95e5b84..2bc6d53 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -503,26 +503,21 @@
             String restriction,
             boolean isMainUser,
             boolean isProfileOwnerOnOrgOwnedDevice) {
-        if (android.app.admin.flags.Flags.esimManagementEnabled()) {
-            if (IMMUTABLE_BY_OWNERS.contains(restriction)) {
-                return false;
-            }
-            if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) {
-                return false;
-            }
-            if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) {
-                return false;
-            }
-            if (!isProfileOwnerOnOrgOwnedDevice
-                    && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains(
-                            restriction)) {
-                return false;
-            }
-            return true;
+        if (IMMUTABLE_BY_OWNERS.contains(restriction)) {
+            return false;
         }
-        return !IMMUTABLE_BY_OWNERS.contains(restriction)
-                && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
-                && !(!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction));
+        if (DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)) {
+            return false;
+        }
+        if (!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction)) {
+            return false;
+        }
+        if (!isProfileOwnerOnOrgOwnedDevice
+                && PROFILE_OWNER_ORGANIZATION_OWNED_PROFILE_RESTRICTIONS.contains(
+                restriction)) {
+            return false;
+        }
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
index 1c86c4f..64bcc22 100644
--- a/services/core/java/com/android/server/pm/dex/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.pm.dex"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_pm_dex"
     },
     {
       "name": "DynamicCodeLoggerIntegrationTests"
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index f518769..e9cb279 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -375,24 +375,34 @@
                 @Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation,
                 @NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
                         Boolean, SyncNotedAppOp> superImpl) {
-            if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
-                final int shellUid = UserHandle.getUid(
-                        UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    return superImpl.apply(code,
-                            new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
-                                    attributionSource.getAttributionTag(),
-                                    attributionSource.getToken(), /*renouncedPermissions*/ null,
-                                    attributionSource.getDeviceId(), attributionSource.getNext()),
-                            shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                            skiProxyOperation);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
+            if (!isDelegateOp(code)) {
+                return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
+                        message, shouldCollectMessage, skiProxyOperation);
             }
-            return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
-                    message, shouldCollectMessage, skiProxyOperation);
+
+            final int shellUid = UserHandle.getUid(
+                    UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
+            AttributionSource next = attributionSource.getNext();
+            if (next != null && next.getUid() == mDelegateAndOwnerUid) {
+                next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+                        next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null,
+                        next.getDeviceId(), next.getNext());
+                attributionSource = new AttributionSource(attributionSource, next);
+            }
+            if (attributionSource.getUid() == mDelegateAndOwnerUid) {
+                attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
+                        attributionSource.getAttributionTag(),
+                        attributionSource.getToken(), /*renouncedPermissions*/ null,
+                        attributionSource.getDeviceId(), attributionSource.getNext());
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return superImpl.apply(code, attributionSource,
+                        shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                        skiProxyOperation);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index aaa38a3..6e7a009 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -887,7 +887,7 @@
             grantPermissionsToSystemPackage(pm,
                     getDefaultSystemHandlerActivityPackage(pm,
                             SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
-                    userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS);
+                    userId, PHONE_PERMISSIONS, CALENDAR_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
         }
 
         // Print Spooler
@@ -1366,7 +1366,7 @@
 
         for (int requestedPermissionNum = 0; requestedPermissionNum < numRequestedPermissions;
                 requestedPermissionNum++) {
-            String permission = requestedPermissions[requestedPermissionNum];
+            String permission = sortedRequestedPermissions[requestedPermissionNum];
 
             // If there is a disabled system app it may request a permission the updated
             // version ot the data partition doesn't, In this case skip the permission.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 46e6546..df9f7fb 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -26,7 +26,6 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.OP_BLUETOOTH_CONNECT;
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
 import static android.permission.flags.Flags.serverSideAttributionRegistration;
@@ -1640,22 +1639,7 @@
                         throw new SecurityException(msg + ":" + e.getMessage());
                     }
                 }
-                int result = Math.max(checkedOpResult, notedOpResult);
-                // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
-                if (op == OP_BLUETOOTH_CONNECT && result == MODE_ERRORED) {
-                    if (result == checkedOpResult) {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
-                                + " checkOp for resolvedAttributionSource "
-                                + resolvedAttributionSource + " and op " + op
-                                + " returned MODE_ERRORED");
-                    } else {
-                        Slog.e(LOG_TAG, "BLUETOOTH_CONNECT permission hard denied as"
-                                + " noteOp for resolvedAttributionSource "
-                                + resolvedAttributionSource + " and op " + notedOp
-                                + " returned MODE_ERRORED");
-                    }
-                }
-                return result;
+                return Math.max(checkedOpResult, notedOpResult);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index 24323c8..8a3c74b 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -1,24 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "CtsPermissionTestCases",
-            "options": [
-                {
-                    "exclude-annotation": "androidx.test.filters.FlakyTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.BackgroundPermissionsTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.SplitPermissionTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.PermissionFlagsTest"
-                },
-                {
-                    "include-filter": "android.permission.cts.SharedUidPermissionsTest"
-                }
-            ]
+            "name": "CtsPermissionTestCases_Platform"
         },
         {
             "name": "CtsAppSecurityHostTestCases",
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 5a45186..027e69c 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -17,7 +17,9 @@
 package com.android.server.policy;
 
 import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
+import static com.android.hardware.input.Flags.modifierShortcutManagerRefactor;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.role.RoleManager;
@@ -29,8 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Icon;
-import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -38,6 +39,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyCharacterMap;
@@ -82,8 +84,8 @@
     private static final String ATTRIBUTE_SHIFT = "shift";
     private static final String ATTRIBUTE_ROLE = "role";
 
-    private final SparseArray<Intent> mIntentShortcuts = new SparseArray<>();
-    private final SparseArray<Intent> mShiftShortcuts = new SparseArray<>();
+    private final SparseArray<Intent> mCategoryShortcuts = new SparseArray<>();
+    private final SparseArray<Intent> mShiftCategoryShortcuts = new SparseArray<>();
     private final SparseArray<String> mRoleShortcuts = new SparseArray<String>();
     private final SparseArray<String> mShiftRoleShortcuts = new SparseArray<String>();
     private final Map<String, Intent> mRoleIntents = new HashMap<String, Intent>();
@@ -128,6 +130,7 @@
     private boolean mSearchKeyShortcutPending = false;
     private boolean mConsumeSearchKeyUp = true;
     private UserHandle mCurrentUser;
+    private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>();
 
     ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
         mContext = context;
@@ -135,7 +138,14 @@
         RoleManager rm = mContext.getSystemService(RoleManager.class);
         rm.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
                 (String roleName, UserHandle user) -> {
-                    mRoleIntents.remove(roleName);
+                    if (modifierShortcutManagerRefactor()) {
+                        mBookmarks.values().stream().filter(b ->
+                                b instanceof RoleBookmark
+                                && ((RoleBookmark) b).getRole().equals(roleName))
+                                .forEach(Bookmark::clearIntent);
+                    } else {
+                        mRoleIntents.remove(roleName);
+                    }
                 }, UserHandle.ALL);
         mCurrentUser = currentUser;
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -147,8 +157,26 @@
 
         // Role based shortcuts may resolve to different apps for different users
         // so clear the cache.
-        mRoleIntents.clear();
-        mComponentIntents.clear();
+        clearRoleIntents();
+        clearComponentIntents();
+    }
+
+    void clearRoleIntents() {
+        if (modifierShortcutManagerRefactor()) {
+            mBookmarks.values().stream().filter(b ->
+                    b instanceof RoleBookmark).forEach(Bookmark::clearIntent);
+        } else {
+            mRoleIntents.clear();
+        }
+    }
+
+    void clearComponentIntents() {
+        if (modifierShortcutManagerRefactor()) {
+            mBookmarks.values().stream().filter(b ->
+                    b instanceof ComponentBookmark).forEach(Bookmark::clearIntent);
+        } else {
+            mComponentIntents.clear();
+        }
     }
 
     /**
@@ -177,77 +205,111 @@
 
         Intent shortcutIntent = null;
 
-        // If the Shift key is pressed, then search for the shift shortcuts.
-        SparseArray<Intent> shortcutMap = isShiftOn ? mShiftShortcuts : mIntentShortcuts;
-
         // First try the exact keycode (with modifiers).
         int shortcutChar = kcm.get(keyCode, metaState);
         if (shortcutChar == 0) {
             return null;
         }
-        shortcutIntent = shortcutMap.get(shortcutChar);
 
-        if (shortcutIntent == null) {
-            // Next try the primary character on that key.
-            shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
-            if (shortcutChar == 0) {
-                return null;
+        if (modifierShortcutManagerRefactor()) {
+            Bookmark bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn));
+            if (bookmark == null) {
+                // Next try the primary character on that key.
+                shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+                if (shortcutChar == 0) {
+                    return null;
+                }
+                bookmark = mBookmarks.get(new Pair<>((char) shortcutChar, isShiftOn));
             }
+
+            if (bookmark != null) {
+                Context context = modifierShortcutManagerMultiuser()
+                        ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+                shortcutIntent = bookmark.getIntent(context);
+            } else {
+                Log.d(TAG, "No bookmark found for "
+                        + (isShiftOn ? "SHIFT+" : "") + (char) shortcutChar);
+            }
+        } else {
+            // If the Shift key is pressed, then search for the shift shortcuts.
+            SparseArray<Intent> shortcutMap = isShiftOn
+                    ? mShiftCategoryShortcuts : mCategoryShortcuts;
             shortcutIntent = shortcutMap.get(shortcutChar);
-        }
 
-        if (shortcutIntent == null) {
-            // Next check for role based shortcut with primary character.
-            String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
-                    : mRoleShortcuts.get(shortcutChar);
-            if (role != null) {
-                shortcutIntent = getRoleLaunchIntent(role);
-            }
-        }
-
-        if (modifierShortcutManagerMultiuser()) {
             if (shortcutIntent == null) {
-                // Next check component based shortcuts with primary character.
-                ComponentName component = isShiftOn
-                        ? mShiftComponentShortcuts.get(shortcutChar)
-                        : mComponentShortcuts.get(shortcutChar);
-                if (component != null) {
-                    shortcutIntent = resolveComponentNameIntent(component);
+                // Next try the primary character on that key.
+                shortcutChar = Character.toLowerCase(kcm.getDisplayLabel(keyCode));
+                if (shortcutChar == 0) {
+                    return null;
+                }
+                shortcutIntent = shortcutMap.get(shortcutChar);
+            }
+
+            if (shortcutIntent == null) {
+                // Next check for role based shortcut with primary character.
+                String role = isShiftOn ? mShiftRoleShortcuts.get(shortcutChar)
+                        : mRoleShortcuts.get(shortcutChar);
+                if (role != null) {
+                    shortcutIntent = getRoleLaunchIntent(role);
+                }
+            }
+
+            if (modifierShortcutManagerMultiuser()) {
+                if (shortcutIntent == null) {
+                    // Next check component based shortcuts with primary character.
+                    ComponentName component = isShiftOn
+                            ? mShiftComponentShortcuts.get(shortcutChar)
+                            : mComponentShortcuts.get(shortcutChar);
+                    if (component != null) {
+                        shortcutIntent = resolveComponentNameIntent(component);
+                    }
                 }
             }
         }
         return shortcutIntent;
     }
 
+    @Nullable
+    private static Intent getRoleLaunchIntent(Context context, String role) {
+        Intent intent = null;
+        RoleManager rm = context.getSystemService(RoleManager.class);
+        PackageManager pm = context.getPackageManager();
+        if (rm.isRoleAvailable(role)) {
+            String rolePackage = rm.getDefaultApplication(role);
+            if (rolePackage != null) {
+                intent = pm.getLaunchIntentForPackage(rolePackage);
+                if (intent != null) {
+                    intent.putExtra(EXTRA_ROLE, role);
+
+                } else {
+                    Log.w(TAG, "No launch intent for role " + role);
+                }
+            } else {
+                Log.w(TAG, "No default application for role "
+                        + role + " user=" + context.getUser());
+            }
+        } else {
+            Log.w(TAG, "Role " + role + " is not available.");
+        }
+        return intent;
+    }
+
+    @Nullable
     private Intent getRoleLaunchIntent(String role) {
         Intent intent = mRoleIntents.get(role);
         if (intent == null) {
             Context context = modifierShortcutManagerMultiuser()
                     ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
-            RoleManager rm = context.getSystemService(RoleManager.class);
-            PackageManager pm = context.getPackageManager();
-            if (rm.isRoleAvailable(role)) {
-                String rolePackage = rm.getDefaultApplication(role);
-                if (rolePackage != null) {
-                    intent = pm.getLaunchIntentForPackage(rolePackage);
-                    if (intent != null) {
-                        intent.putExtra(EXTRA_ROLE, role);
-                        mRoleIntents.put(role, intent);
-                    } else {
-                        Log.w(TAG, "No launch intent for role " + role);
-                    }
-                } else {
-                    Log.w(TAG, "No default application for role " + role);
-                }
-            } else {
-                Log.w(TAG, "Role " + role + " is not available.");
+            intent = getRoleLaunchIntent(context, role);
+            if (intent != null) {
+                mRoleIntents.put(role, intent);
             }
         }
+
         return intent;
     }
 
     private void loadShortcuts() {
-
         try {
             XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks);
             XmlUtils.beginDocument(parser, TAG_BOOKMARKS);
@@ -277,57 +339,84 @@
                     continue;
                 }
 
-                final int shortcutChar = shortcutName.charAt(0);
                 final boolean isShiftShortcut = (shiftName != null && shiftName.equals("true"));
-                final Intent intent;
-                if (packageName != null && className != null) {
-                    if (roleName != null || categoryName != null) {
-                        Log.w(TAG, "Cannot specify role or category when package and class"
-                                + " are present for bookmark packageName=" + packageName
-                                + " className=" + className + " shortcutChar=" + shortcutChar);
-                        continue;
+
+                if (modifierShortcutManagerRefactor()) {
+                    final char shortcutChar = shortcutName.charAt(0);
+                    Bookmark bookmark = null;
+                    if (packageName != null && className != null) {
+                        bookmark = new ComponentBookmark(
+                                shortcutChar, isShiftShortcut, packageName, className);
+                    } else if (categoryName != null) {
+                        bookmark = new CategoryBookmark(
+                                shortcutChar, isShiftShortcut, categoryName);
+                    } else if (roleName != null) {
+                        bookmark = new RoleBookmark(shortcutChar, isShiftShortcut, roleName);
                     }
-                    if (modifierShortcutManagerMultiuser()) {
-                        ComponentName componentName = new ComponentName(packageName, className);
-                        if (isShiftShortcut) {
-                            mShiftComponentShortcuts.put(shortcutChar, componentName);
+                    if (bookmark != null) {
+                        Log.d(TAG, "adding shortcut " + bookmark + "shift="
+                                + isShiftShortcut + " char=" + shortcutChar);
+                        mBookmarks.put(new Pair<>(shortcutChar, isShiftShortcut), bookmark);
+                    }
+                } else {
+                    final int shortcutChar = shortcutName.charAt(0);
+                    if (packageName != null && className != null) {
+                        if (roleName != null || categoryName != null) {
+                            Log.w(TAG, "Cannot specify role or category when package and class"
+                                    + " are present for bookmark packageName=" + packageName
+                                    + " className=" + className + " shortcutChar=" + shortcutChar);
+                            continue;
+                        }
+                        if (modifierShortcutManagerMultiuser()) {
+                            ComponentName componentName =
+                                    new ComponentName(packageName, className);
+                            if (isShiftShortcut) {
+                                mShiftComponentShortcuts.put(shortcutChar, componentName);
+                            } else {
+                                mComponentShortcuts.put(shortcutChar, componentName);
+                            }
                         } else {
-                            mComponentShortcuts.put(shortcutChar, componentName);
+                            Intent intent = resolveComponentNameIntent(packageName, className);
+                            if (isShiftShortcut) {
+                                mShiftCategoryShortcuts.put(shortcutChar, intent);
+                            } else {
+                                mCategoryShortcuts.put(shortcutChar, intent);
+                            }
+                        }
+                        continue;
+                    } else if (categoryName != null) {
+                        if (roleName != null) {
+                            Log.w(TAG, "Cannot specify role bookmark when category is present for"
+                                    + " bookmark shortcutChar=" + shortcutChar
+                                    + " category= " + categoryName);
+                            continue;
+                        }
+                        Intent intent = Intent.makeMainSelectorActivity(
+                                Intent.ACTION_MAIN, categoryName);
+                        if (intent == null) {
+                            Log.w(TAG, "Null selector intent for " + categoryName);
+                        } else {
+                            if (isShiftShortcut) {
+                                mShiftCategoryShortcuts.put(shortcutChar, intent);
+                            } else {
+                                mCategoryShortcuts.put(shortcutChar, intent);
+                            }
+                        }
+                        continue;
+                    } else if (roleName != null) {
+                        // We can't resolve the role at the time of this file being parsed as the
+                        // device hasn't finished booting, so we will look it up lazily.
+                        if (isShiftShortcut) {
+                            mShiftRoleShortcuts.put(shortcutChar, roleName);
+                        } else {
+                            mRoleShortcuts.put(shortcutChar, roleName);
                         }
                         continue;
                     } else {
-                        intent = resolveComponentNameIntent(packageName, className);
-                    }
-                } else if (categoryName != null) {
-                    if (roleName != null) {
-                        Log.w(TAG, "Cannot specify role bookmark when category is present for"
-                                + " bookmark shortcutChar=" + shortcutChar
-                                + " category= " + categoryName);
+                        Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
+                                + ": missing package/class, category or role attributes");
                         continue;
                     }
-                    intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, categoryName);
-                    if (intent == null) {
-                        Log.w(TAG, "Null selector intent for " + categoryName);
-                    }
-                } else if (roleName != null) {
-                    // We can't resolve the role at the time of this file being parsed as the
-                    // device hasn't finished booting, so we will look it up lazily.
-                    if (isShiftShortcut) {
-                        mShiftRoleShortcuts.put(shortcutChar, roleName);
-                    } else {
-                        mRoleShortcuts.put(shortcutChar, roleName);
-                    }
-                    continue;
-                } else {
-                    Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutName
-                            + ": missing package/class, category or role attributes");
-                    continue;
-                }
-
-                if (isShiftShortcut) {
-                    mShiftShortcuts.put(shortcutChar, intent);
-                } else {
-                    mIntentShortcuts.put(shortcutChar, intent);
                 }
             }
         } catch (XmlPullParserException | IOException e) {
@@ -337,21 +426,35 @@
 
     @Nullable
     private Intent resolveComponentNameIntent(ComponentName componentName) {
-        Intent intent = mComponentIntents.get(componentName);
-        if (intent == null) {
-            intent = resolveComponentNameIntent(
-                    componentName.getPackageName(), componentName.getClassName());
-            if (intent != null) {
-                mComponentIntents.put(componentName, intent);
+        if (modifierShortcutManagerRefactor()) {
+            return null;
+        } else {
+            Intent intent = mComponentIntents.get(componentName);
+            if (intent == null) {
+                intent = resolveComponentNameIntent(
+                        componentName.getPackageName(), componentName.getClassName());
+                if (intent != null) {
+                    mComponentIntents.put(componentName, intent);
+                }
             }
+            return intent;
         }
-        return intent;
     }
 
     @Nullable
     private Intent resolveComponentNameIntent(String packageName, String className) {
-        Context context = modifierShortcutManagerMultiuser()
-                ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+        if (modifierShortcutManagerRefactor()) {
+            return null;
+        } else {
+            Context context = modifierShortcutManagerMultiuser()
+                        ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+            return resolveComponentNameIntent(context, packageName, className);
+        }
+    }
+
+    @Nullable
+    private static Intent resolveComponentNameIntent(
+            Context context, String packageName, String className) {
         PackageManager pm = context.getPackageManager();
         int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
         if (!modifierShortcutManagerMultiuser()) {
@@ -476,7 +579,7 @@
                             + "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
                             + " category=" + category + " role=" + role);
                 }
-                notifyKeyboardShortcutTriggered(keyEvent, getSystemShortcutFromIntent(intent));
+                notifyKeyGestureCompleted(keyEvent, getKeyGestureTypeFromIntent(intent));
                 return true;
             } else {
                 return false;
@@ -497,19 +600,19 @@
                         + "the activity to which it is registered was not found: "
                         + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode));
             }
-            notifyKeyboardShortcutTriggered(keyEvent, getSystemShortcutFromIntent(shortcutIntent));
+            notifyKeyGestureCompleted(keyEvent, getKeyGestureTypeFromIntent(shortcutIntent));
             return true;
         }
         return false;
     }
 
-    private void notifyKeyboardShortcutTriggered(KeyEvent event,
-            @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
-        if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+    private void notifyKeyGestureCompleted(KeyEvent event,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
+        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
             return;
         }
-        mInputManagerInternal.notifyKeyboardShortcutTriggered(event.getDeviceId(),
-                new int[]{event.getKeyCode()}, event.getMetaState(), systemShortcut);
+        mInputManagerInternal.notifyKeyGestureCompleted(event.getDeviceId(),
+                new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
     }
 
     /**
@@ -563,64 +666,81 @@
      */
     public KeyboardShortcutGroup getApplicationLaunchKeyboardShortcuts(int deviceId) {
         List<KeyboardShortcutInfo> shortcuts = new ArrayList();
-        for (int i = 0; i <  mIntentShortcuts.size(); i++) {
-            KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                    (char) (mIntentShortcuts.keyAt(i)), mIntentShortcuts.valueAt(i), false);
-            if (info != null) {
-                shortcuts.add(info);
-            }
-        }
-
-        for (int i = 0; i <  mShiftShortcuts.size(); i++) {
-            KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                    (char) (mShiftShortcuts.keyAt(i)), mShiftShortcuts.valueAt(i), true);
-            if (info != null) {
-                shortcuts.add(info);
-            }
-        }
-
-        for (int i = 0; i <  mRoleShortcuts.size(); i++) {
-            String role = mRoleShortcuts.valueAt(i);
-            KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                    (char) (mRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), false);
-            if (info != null) {
-                shortcuts.add(info);
-            }
-        }
-
-        for (int i = 0; i <  mShiftRoleShortcuts.size(); i++) {
-            String role = mShiftRoleShortcuts.valueAt(i);
-            KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                    (char) (mShiftRoleShortcuts.keyAt(i)), getRoleLaunchIntent(role), true);
-            if (info != null) {
-                shortcuts.add(info);
-            }
-        }
-
-        if (modifierShortcutManagerMultiuser()) {
-            for (int i = 0; i < mComponentShortcuts.size(); i++) {
-                ComponentName component = mComponentShortcuts.valueAt(i);
+        if (modifierShortcutManagerRefactor()) {
+            for (Bookmark b : mBookmarks.values()) {
                 KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                        (char) (mComponentShortcuts.keyAt(i)),
-                        resolveComponentNameIntent(component),
+                        b.getShortcutChar(), b.getIntent(mContext), b.isShift());
+                if (info != null) {
+                    shortcuts.add(info);
+                }
+            }
+        } else {
+            for (int i = 0; i <  mCategoryShortcuts.size(); i++) {
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                        (char) (mCategoryShortcuts.keyAt(i)),
+                        mCategoryShortcuts.valueAt(i),
                         false);
                 if (info != null) {
                     shortcuts.add(info);
                 }
             }
 
-            for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
-                ComponentName component = mShiftComponentShortcuts.valueAt(i);
+            for (int i = 0; i <  mShiftCategoryShortcuts.size(); i++) {
                 KeyboardShortcutInfo info = shortcutInfoFromIntent(
-                        (char) (mShiftComponentShortcuts.keyAt(i)),
-                        resolveComponentNameIntent(component),
+                        (char) (mShiftCategoryShortcuts.keyAt(i)),
+                        mShiftCategoryShortcuts.valueAt(i),
                         true);
                 if (info != null) {
                     shortcuts.add(info);
                 }
             }
-        }
 
+            for (int i = 0; i <  mRoleShortcuts.size(); i++) {
+                String role = mRoleShortcuts.valueAt(i);
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                        (char) (mRoleShortcuts.keyAt(i)),
+                        getRoleLaunchIntent(role),
+                        false);
+                if (info != null) {
+                    shortcuts.add(info);
+                }
+            }
+
+            for (int i = 0; i <  mShiftRoleShortcuts.size(); i++) {
+                String role = mShiftRoleShortcuts.valueAt(i);
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                        (char) (mShiftRoleShortcuts.keyAt(i)),
+                        getRoleLaunchIntent(role),
+                        true);
+                if (info != null) {
+                    shortcuts.add(info);
+                }
+            }
+
+            if (modifierShortcutManagerMultiuser()) {
+                for (int i = 0; i < mComponentShortcuts.size(); i++) {
+                    ComponentName component = mComponentShortcuts.valueAt(i);
+                    KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                            (char) (mComponentShortcuts.keyAt(i)),
+                            resolveComponentNameIntent(component),
+                            false);
+                    if (info != null) {
+                        shortcuts.add(info);
+                    }
+                }
+
+                for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+                    ComponentName component = mShiftComponentShortcuts.valueAt(i);
+                    KeyboardShortcutInfo info = shortcutInfoFromIntent(
+                            (char) (mShiftComponentShortcuts.keyAt(i)),
+                            resolveComponentNameIntent(component),
+                            true);
+                    if (info != null) {
+                        shortcuts.add(info);
+                    }
+                }
+            }
+        }
         return new KeyboardShortcutGroup(
                 mContext.getString(R.string.keyboard_shortcut_group_applications),
                 shortcuts);
@@ -710,21 +830,21 @@
 
 
     /**
-     * Find Keyboard shortcut event corresponding to intent filter category. Returns
-     * {@code SYSTEM_SHORTCUT_UNSPECIFIED if no matching event found}
+     * Find Key gesture type corresponding to intent filter category. Returns
+     * {@code KEY_GESTURE_TYPE_UNSPECIFIED if no matching event found}
      */
-    @KeyboardSystemShortcut.SystemShortcut
-    private static int getSystemShortcutFromIntent(Intent intent) {
+    @KeyGestureEvent.KeyGestureType
+    private static int getKeyGestureTypeFromIntent(Intent intent) {
         Intent selectorIntent = intent.getSelector();
         if (selectorIntent != null) {
             Set<String> selectorCategories = selectorIntent.getCategories();
             if (selectorCategories != null && !selectorCategories.isEmpty()) {
                 for (String intentCategory : selectorCategories) {
-                    int systemShortcut = getEventFromSelectorCategory(intentCategory);
-                    if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+                    int keyGestureType = getKeyGestureTypeFromSelectorCategory(intentCategory);
+                    if (keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
                         continue;
                     }
-                    return systemShortcut;
+                    return keyGestureType;
                 }
             }
         }
@@ -733,126 +853,239 @@
         // so check for that.
         String role = intent.getStringExtra(ModifierShortcutManager.EXTRA_ROLE);
         if (!TextUtils.isEmpty(role)) {
-            return getLogEventFromRole(role);
+            return getKeyGestureTypeFromRole(role);
         }
 
         Set<String> intentCategories = intent.getCategories();
         if (intentCategories == null || intentCategories.isEmpty()
                 || !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) {
-            return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+            return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
         }
         if (intent.getComponent() == null) {
-            return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+            return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
         }
 
         // TODO(b/280423320): Add new field package name associated in the
         //  KeyboardShortcutEvent atom and log it accordingly.
-        return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+        return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
     }
 
-    @KeyboardSystemShortcut.SystemShortcut
-    private static int getEventFromSelectorCategory(String category) {
+    @KeyGestureEvent.KeyGestureType
+    private static int getKeyGestureTypeFromSelectorCategory(String category) {
         switch (category) {
             case Intent.CATEGORY_APP_BROWSER:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER;
             case Intent.CATEGORY_APP_EMAIL:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL;
             case Intent.CATEGORY_APP_CONTACTS:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS;
             case Intent.CATEGORY_APP_CALENDAR:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR;
             case Intent.CATEGORY_APP_CALCULATOR:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR;
             case Intent.CATEGORY_APP_MUSIC:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC;
             case Intent.CATEGORY_APP_MAPS:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS;
             case Intent.CATEGORY_APP_MESSAGING:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING;
             case Intent.CATEGORY_APP_GALLERY:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_GALLERY;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY;
             case Intent.CATEGORY_APP_FILES:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FILES;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES;
             case Intent.CATEGORY_APP_WEATHER:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_WEATHER;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER;
             case Intent.CATEGORY_APP_FITNESS:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_FITNESS;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS;
             default:
-                return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+                return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
         }
     }
 
     /**
-     * Find KeyboardLogEvent corresponding to the provide system role name.
-     * Returns {@code null} if no matching event found.
+     * Find KeyGestureType corresponding to the provide system role name.
+     * Returns {@code KEY_GESTURE_TYPE_UNSPECIFIED} if no matching event found.
      */
-    @KeyboardSystemShortcut.SystemShortcut
-    private static int getLogEventFromRole(String role) {
+    @KeyGestureEvent.KeyGestureType
+    private static int getKeyGestureTypeFromRole(String role) {
         if (RoleManager.ROLE_BROWSER.equals(role)) {
-            return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER;
+            return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER;
         } else if (RoleManager.ROLE_SMS.equals(role)) {
-            return KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING;
+            return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING;
         } else {
-            Log.w(TAG, "Keyboard shortcut to launch "
-                    + role + " not supported for logging");
-            return KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED;
+            Log.w(TAG, "Keyboard gesture event to launch " + role + " not supported for logging");
+            return KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED;
         }
     }
 
     void dump(String prefix, PrintWriter pw) {
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw,  "  ", prefix);
         ipw.println("ModifierShortcutManager shortcuts:");
-
-        ipw.increaseIndent();
-        ipw.println("Roles");
-        ipw.increaseIndent();
-        for (int i = 0; i <  mRoleShortcuts.size(); i++) {
-            String role = mRoleShortcuts.valueAt(i);
-            char shortcutChar = (char) mRoleShortcuts.keyAt(i);
-            Intent intent = getRoleLaunchIntent(role);
-            ipw.println(shortcutChar + " " + role + " " + intent);
-        }
-
-        for (int i = 0; i <  mShiftRoleShortcuts.size(); i++) {
-            String role = mShiftRoleShortcuts.valueAt(i);
-            char shortcutChar = (char) mShiftRoleShortcuts.keyAt(i);
-            Intent intent = getRoleLaunchIntent(role);
-            ipw.println("SHIFT+" + shortcutChar + " " + role + " " + intent);
-        }
-
-        ipw.decreaseIndent();
-        ipw.println("Selectors");
-        ipw.increaseIndent();
-        for (int i = 0; i <  mIntentShortcuts.size(); i++) {
-            char shortcutChar = (char) mIntentShortcuts.keyAt(i);
-            Intent intent = mIntentShortcuts.valueAt(i);
-            ipw.println(shortcutChar + " " + intent);
-        }
-
-        for (int i = 0; i <  mShiftShortcuts.size(); i++) {
-            char shortcutChar = (char) mShiftShortcuts.keyAt(i);
-            Intent intent = mShiftShortcuts.valueAt(i);
-            ipw.println("SHIFT+" + shortcutChar + " " + intent);
-
-        }
-
-        if (modifierShortcutManagerMultiuser()) {
-            ipw.decreaseIndent();
-            ipw.println("ComponentNames");
+        if (modifierShortcutManagerRefactor()) {
             ipw.increaseIndent();
-            for (int i = 0; i < mComponentShortcuts.size(); i++) {
-                char shortcutChar = (char) mComponentShortcuts.keyAt(i);
-                ComponentName component = mComponentShortcuts.valueAt(i);
-                Intent intent = resolveComponentNameIntent(component);
-                ipw.println(shortcutChar + " " + component + " " + intent);
+            for (Bookmark b : mBookmarks.values()) {
+                boolean isShift = b.isShift();
+                char shortcutChar = b.getShortcutChar();
+                Context context = modifierShortcutManagerMultiuser()
+                        ? mContext.createContextAsUser(mCurrentUser, 0) : mContext;
+
+                Intent intent = b.getIntent(context);
+                ipw.print(isShift ? "SHIFT+" : "");
+                ipw.println(shortcutChar + " " + intent);
+                ipw.increaseIndent();
+                ipw.increaseIndent();
+                KeyboardShortcutInfo info = shortcutInfoFromIntent(shortcutChar, intent, isShift);
+                if (info != null) {
+                    ipw.println("Resolves to: " + info.getLabel());
+                } else {
+                    ipw.println("<No KeyboardShortcutInfo available for this shortcut>");
+                }
+                ipw.decreaseIndent();
+                ipw.decreaseIndent();
+            }
+        } else {
+            ipw.increaseIndent();
+            ipw.println("Roles");
+            ipw.increaseIndent();
+            for (int i = 0; i < mRoleShortcuts.size(); i++) {
+                String role = mRoleShortcuts.valueAt(i);
+                char shortcutChar = (char) mRoleShortcuts.keyAt(i);
+                Intent intent = getRoleLaunchIntent(role);
+                ipw.println(shortcutChar + " " + role + " " + intent);
             }
 
-            for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
-                char shortcutChar = (char) mShiftComponentShortcuts.keyAt(i);
-                ComponentName component = mShiftComponentShortcuts.valueAt(i);
-                Intent intent = resolveComponentNameIntent(component);
-                ipw.println("SHIFT+" + shortcutChar + " " + component + " " + intent);
+            for (int i = 0; i < mShiftRoleShortcuts.size(); i++) {
+                String role = mShiftRoleShortcuts.valueAt(i);
+                char shortcutChar = (char) mShiftRoleShortcuts.keyAt(i);
+                Intent intent = getRoleLaunchIntent(role);
+                ipw.println("SHIFT+" + shortcutChar + " " + role + " " + intent);
             }
+
+            ipw.decreaseIndent();
+            ipw.println("Selectors");
+            ipw.increaseIndent();
+            for (int i = 0; i < mCategoryShortcuts.size(); i++) {
+                char shortcutChar = (char) mCategoryShortcuts.keyAt(i);
+                Intent intent = mCategoryShortcuts.valueAt(i);
+                ipw.println(shortcutChar + " " + intent);
+            }
+
+            for (int i = 0; i < mShiftCategoryShortcuts.size(); i++) {
+                char shortcutChar = (char) mShiftCategoryShortcuts.keyAt(i);
+                Intent intent = mShiftCategoryShortcuts.valueAt(i);
+                ipw.println("SHIFT+" + shortcutChar + " " + intent);
+
+            }
+
+            if (modifierShortcutManagerMultiuser()) {
+                ipw.decreaseIndent();
+                ipw.println("ComponentNames");
+                ipw.increaseIndent();
+                for (int i = 0; i < mComponentShortcuts.size(); i++) {
+                    char shortcutChar = (char) mComponentShortcuts.keyAt(i);
+                    ComponentName component = mComponentShortcuts.valueAt(i);
+                    Intent intent = resolveComponentNameIntent(component);
+                    ipw.println(shortcutChar + " " + component + " " + intent);
+                }
+
+                for (int i = 0; i < mShiftComponentShortcuts.size(); i++) {
+                    char shortcutChar = (char) mShiftComponentShortcuts.keyAt(i);
+                    ComponentName component = mShiftComponentShortcuts.valueAt(i);
+                    Intent intent = resolveComponentNameIntent(component);
+                    ipw.println("SHIFT+" + shortcutChar + " " + component + " " + intent);
+                }
+            }
+        }
+    }
+
+    private abstract static  class Bookmark {
+        private final char mShortcutChar;
+        private final boolean mShift;
+        protected Intent mIntent;
+
+        Bookmark(char shortcutChar, boolean shift) {
+            mShortcutChar = shortcutChar;
+            mShift = shift;
+        }
+
+        public char getShortcutChar() {
+            return mShortcutChar;
+        }
+
+        public boolean isShift() {
+            return mShift;
+        }
+
+        public abstract Intent getIntent(Context context);
+
+        public void clearIntent() {
+            mIntent = null;
+        }
+
+    }
+
+    private static final class RoleBookmark extends Bookmark {
+        private final String mRole;
+
+        RoleBookmark(char shortcutChar, boolean shift, String role) {
+            super(shortcutChar, shift);
+            mRole = role;
+        }
+
+        public String getRole() {
+            return mRole;
+        }
+
+        @Nullable
+        @Override
+        public Intent getIntent(Context context) {
+            if (mIntent != null) {
+                return mIntent;
+            }
+            mIntent = getRoleLaunchIntent(context, mRole);
+            return mIntent;
+        }
+    }
+
+    private static final class CategoryBookmark extends Bookmark {
+        private final String mCategory;
+
+        CategoryBookmark(char shortcutChar, boolean shift, String category) {
+            super(shortcutChar, shift);
+            mCategory = category;
+        }
+
+        @NonNull
+        @Override
+        public Intent getIntent(Context context) {
+            if (mIntent != null) {
+                return mIntent;
+            }
+
+            mIntent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, mCategory);
+            return mIntent;
+        }
+    }
+
+    private static final class ComponentBookmark extends Bookmark {
+        private final String mPackageName;
+        private final String mClassName;
+
+        ComponentBookmark(
+                char shortcutChar, boolean shift, String packageName, String className) {
+            super(shortcutChar, shift);
+            mPackageName = packageName;
+            mClassName = className;
+        }
+
+        @Nullable
+        @Override
+        public Intent getIntent(Context context) {
+            if (mIntent != null) {
+                return mIntent;
+            }
+            mIntent = resolveComponentNameIntent(context, mPackageName, mClassName);
+            return mIntent;
         }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 720c1c2..749025b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.policy;
 
+import static android.Manifest.permission.CREATE_VIRTUAL_DEVICE;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
@@ -59,13 +60,18 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD;
@@ -139,7 +145,7 @@
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
 import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
+import android.hardware.input.KeyGestureEvent;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioSystem;
@@ -1076,16 +1082,6 @@
     private void interceptPowerKeyUp(KeyEvent event, boolean canceled) {
         // Inform the StatusBar; but do not allow it to consume the event.
         sendSystemKeyToStatusBarAsync(event);
-
-        final boolean handled = canceled || mPowerKeyHandled;
-
-        if (!handled) {
-            if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) == 0) {
-                // Abort possibly stuck animations only when power key up without long press case.
-                mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
-            }
-        }
-
         finishPowerKeyPress();
     }
 
@@ -1819,7 +1815,7 @@
     }
 
     private void handleShortPressOnHome(KeyEvent event) {
-        notifyKeyboardShortcutTriggered(event, KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME);
+        notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
 
         // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
         final HdmiControl hdmiControl = getHdmiControl();
@@ -2053,8 +2049,8 @@
             }
             switch (mDoubleTapOnHomeBehavior) {
                 case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI:
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
                     mHomeConsumed = true;
                     toggleRecentApps();
                     break;
@@ -2082,23 +2078,23 @@
                 case LONG_PRESS_HOME_ALL_APPS:
                     if (mHasFeatureLeanback) {
                         launchAllAppsAction();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_ALL_APPS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                     } else {
                         launchAllAppsViaA11y();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
                     }
                     break;
                 case LONG_PRESS_HOME_ASSIST:
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+                    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:
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                     toggleNotificationPanel();
                     break;
                 default:
@@ -3068,7 +3064,7 @@
     /** {@inheritDoc} */
     @Override
     public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
-            int[] outAppOp) {
+            int[] outAppOp, int displayId) {
         if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                 != PERMISSION_GRANTED) {
             return ADD_PERMISSION_DENIED;
@@ -3108,6 +3104,12 @@
                 case TYPE_VOICE_INTERACTION:
                 case TYPE_QS_DIALOG:
                 case TYPE_NAVIGATION_BAR_PANEL:
+                case TYPE_STATUS_BAR:
+                case TYPE_NOTIFICATION_SHADE:
+                case TYPE_NAVIGATION_BAR:
+                case TYPE_STATUS_BAR_ADDITIONAL:
+                case TYPE_STATUS_BAR_SUB_PANEL:
+                case TYPE_VOICE_INTERACTION_STARTING:
                     // The window manager will check these.
                     return ADD_OKAY;
             }
@@ -3151,6 +3153,13 @@
             return ADD_OKAY;
         }
 
+        // Allow virtual device owners to add overlays on the displays they own.
+        if (mWindowManagerFuncs.isCallerVirtualDeviceOwner(displayId, callingUid)
+                && mContext.checkCallingOrSelfPermission(CREATE_VIRTUAL_DEVICE)
+                == PERMISSION_GRANTED) {
+            return ADD_OKAY;
+        }
+
         // check if user has enabled this operation. SecurityException will be thrown if this app
         // has not been allowed by the user. The reason to use "noteOp" (instead of checkOp) is to
         // make sure the usage is logged.
@@ -3285,29 +3294,29 @@
             WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
         };
 
-    private void notifyKeyboardShortcutTriggeredOnActionUp(KeyEvent event,
-            @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
+    private void notifyKeyGestureCompletedOnActionUp(KeyEvent event,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
         if (event.getAction() != KeyEvent.ACTION_UP) {
             return;
         }
-        notifyKeyboardShortcutTriggered(event, systemShortcut);
+        notifyKeyGestureCompleted(event, gestureType);
     }
 
-    private void notifyKeyboardShortcutTriggeredOnActionDown(KeyEvent event,
-            @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
+    private void notifyKeyGestureCompletedOnActionDown(KeyEvent event,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
         if (event.getAction() != KeyEvent.ACTION_DOWN) {
             return;
         }
-        notifyKeyboardShortcutTriggered(event, systemShortcut);
+        notifyKeyGestureCompleted(event, gestureType);
     }
 
-    private void notifyKeyboardShortcutTriggered(KeyEvent event,
-            @KeyboardSystemShortcut.SystemShortcut int systemShortcut) {
-        if (systemShortcut == KeyboardSystemShortcut.SYSTEM_SHORTCUT_UNSPECIFIED) {
+    private void notifyKeyGestureCompleted(KeyEvent event,
+            @KeyGestureEvent.KeyGestureType int gestureType) {
+        if (gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_UNSPECIFIED) {
             return;
         }
-        mInputManagerInternal.notifyKeyboardShortcutTriggered(event.getDeviceId(),
-                new int[]{event.getKeyCode()}, event.getMetaState(), systemShortcut);
+        mInputManagerInternal.notifyKeyGestureCompleted(event.getDeviceId(),
+                new int[]{event.getKeyCode()}, event.getMetaState(), gestureType);
     }
 
     @Override
@@ -3352,6 +3361,12 @@
             mConsumedKeysForDevice.put(deviceId, consumedKeys);
         }
 
+        // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
+        if ((event.isMetaPressed() || KeyEvent.isMetaKey(keyCode))
+                && shouldInterceptShortcuts(focusedToken)) {
+            return keyNotConsumed;
+        }
+
         if (interceptSystemKeysAndShortcuts(focusedToken, event)
                 && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
             consumedKeys.add(keyCode);
@@ -3417,8 +3432,8 @@
             case KeyEvent.KEYCODE_RECENT_APPS:
                 if (firstDown) {
                     showRecentApps(false /* triggeredFromAltTab */);
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
                 }
                 return true;
             case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3427,8 +3442,8 @@
                         preloadRecentApps();
                     } else if (!down) {
                         toggleRecentApps();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
                     }
                 }
                 return true;
@@ -3437,8 +3452,8 @@
                     launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                             deviceId, event.getEventTime(),
                             AssistUtils.INVOCATION_TYPE_UNKNOWN);
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
                     return true;
                 }
                 break;
@@ -3451,16 +3466,16 @@
             case KeyEvent.KEYCODE_I:
                 if (firstDown && event.isMetaPressed()) {
                     showSystemSettings();
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_L:
                 if (firstDown && event.isMetaPressed()) {
                     lockNow(null /* options */);
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LOCK_SCREEN);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN);
                     return true;
                 }
                 break;
@@ -3468,12 +3483,12 @@
                 if (firstDown && event.isMetaPressed()) {
                     if (event.isCtrlPressed()) {
                         sendSystemKeyToStatusBarAsync(event);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_NOTES);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES);
                     } else {
                         toggleNotificationPanel();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                     }
                     return true;
                 }
@@ -3481,8 +3496,8 @@
             case KeyEvent.KEYCODE_S:
                 if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
                     interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_TAKE_SCREENSHOT);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT);
                     return true;
                 }
                 break;
@@ -3495,16 +3510,16 @@
                         } catch (RemoteException e) {
                             Slog.d(TAG, "Error taking bugreport", e);
                         }
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT);
                         return true;
                     }
                 }
                 // fall through
             case KeyEvent.KEYCODE_ESCAPE:
                 if (firstDown && event.isMetaPressed()) {
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
                     injectBackGesture(event.getDownTime());
                     return true;
                 }
@@ -3513,8 +3528,8 @@
                     StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                     if (statusbar != null) {
                         statusbar.moveFocusedTaskToFullscreen(getTargetDisplayIdForKeyEvent(event));
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION);
                         return true;
                     }
                 }
@@ -3524,8 +3539,8 @@
                     StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                     if (statusbar != null) {
                         statusbar.moveFocusedTaskToDesktop(getTargetDisplayIdForKeyEvent(event));
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_DESKTOP_MODE);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE);
                         return true;
                     }
                 }
@@ -3535,15 +3550,15 @@
                     if (event.isCtrlPressed()) {
                         moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
                                 true /* leftOrTop */);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
                     } else if (event.isAltPressed()) {
                         setSplitscreenFocus(true /* leftOrTop */);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
                     } else {
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
                         injectBackGesture(event.getDownTime());
                     }
                     return true;
@@ -3554,13 +3569,13 @@
                     if (event.isCtrlPressed()) {
                         moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
                                 false /* leftOrTop */);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
                         return true;
                     } else if (event.isAltPressed()) {
                         setSplitscreenFocus(false /* leftOrTop */);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_CHANGE_SPLITSCREEN_FOCUS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
                         return true;
                     }
                 }
@@ -3568,8 +3583,8 @@
             case KeyEvent.KEYCODE_SLASH:
                 if (firstDown && event.isMetaPressed() && !keyguardOn) {
                     toggleKeyboardShortcutsMenu(event.getDeviceId());
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER);
                     return true;
                 }
                 break;
@@ -3585,18 +3600,6 @@
                 if (down) {
                     int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
 
-                    // Disable autobrightness if it's on
-                    int auto = Settings.System.getIntForUser(
-                            mContext.getContentResolver(),
-                            Settings.System.SCREEN_BRIGHTNESS_MODE,
-                            Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
-                            UserHandle.USER_CURRENT_OR_SELF);
-                    if (auto != 0) {
-                        Settings.System.putIntForUser(mContext.getContentResolver(),
-                                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
-                                UserHandle.USER_CURRENT_OR_SELF);
-                    }
                     int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
 
                     float minLinearBrightness = mPowerManager.getBrightnessConstraint(
@@ -3622,31 +3625,31 @@
                     intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
                     startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
 
-                    int systemShortcut = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
-                            ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_DOWN
-                            : KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_UP;
-                    notifyKeyboardShortcutTriggered(event, systemShortcut);
+                    int gestureType = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
+                            ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN
+                            : KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP;
+                    notifyKeyGestureCompleted(event, gestureType);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
                 if (down) {
                     mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
                 if (down) {
                     mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
                 // TODO: Add logic
                 if (!down) {
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE);
                 }
                 return true;
             case KeyEvent.KEYCODE_VOLUME_UP:
@@ -3673,8 +3676,8 @@
                 if (firstDown && !keyguardOn && isUserSetupComplete()) {
                     if (event.isMetaPressed()) {
                         showRecentApps(false);
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
                         return true;
                     } else if (mRecentAppsHeldModifiers == 0) {
                         final int shiftlessModifiers =
@@ -3683,8 +3686,8 @@
                                 shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                             mRecentAppsHeldModifiers = shiftlessModifiers;
                             showRecentApps(true);
-                            notifyKeyboardShortcutTriggered(event,
-                                    KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS);
+                            notifyKeyGestureCompleted(event,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
                             return true;
                         }
                     }
@@ -3697,20 +3700,20 @@
                         Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
                         msg.setAsynchronous(true);
                         msg.sendToTarget();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_ALL_APPS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
                     } else {
                         launchAllAppsViaA11y();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
                     }
                 }
                 return true;
             case KeyEvent.KEYCODE_NOTIFICATION:
                 if (!down) {
                     toggleNotificationPanel();
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                 }
                 return true;
             case KeyEvent.KEYCODE_SEARCH:
@@ -3718,8 +3721,8 @@
                     switch (mSearchKeyBehavior) {
                         case SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY: {
                             launchTargetSearchActivity();
-                            notifyKeyboardShortcutTriggered(event,
-                                    KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SEARCH);
+                            notifyKeyGestureCompleted(event,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
                             return true;
                         }
                         case SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH:
@@ -3732,8 +3735,8 @@
                 if (firstDown) {
                     int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
                     sendSwitchKeyboardLayout(event, focusedToken, direction);
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LANGUAGE_SWITCH);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH);
                     return true;
                 }
                 break;
@@ -3752,13 +3755,13 @@
                     if (mPendingCapsLockToggle) {
                         mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                         mPendingCapsLockToggle = false;
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
                     } else if (mPendingMetaAction) {
                         if (!canceled) {
                             launchAllAppsViaA11y();
-                            notifyKeyboardShortcutTriggered(event,
-                                    KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS);
+                            notifyKeyGestureCompleted(event,
+                                    KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
                         }
                         mPendingMetaAction = false;
                     }
@@ -3786,16 +3789,16 @@
                     if (mPendingCapsLockToggle) {
                         mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                         mPendingCapsLockToggle = false;
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
                         return true;
                     }
                 }
                 break;
             case KeyEvent.KEYCODE_CAPS_LOCK:
                 if (!down) {
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
                 }
                 break;
             case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
@@ -3809,12 +3812,12 @@
                 if (firstDown) {
                     if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
                         toggleNotificationPanel();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
                     } else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) {
                         showSystemSettings();
-                        notifyKeyboardShortcutTriggered(event,
-                                KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS);
+                        notifyKeyGestureCompleted(event,
+                                KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
                     }
                 }
                 return true;
@@ -3842,6 +3845,15 @@
         return (metaState & KeyEvent.META_META_ON) != 0;
     }
 
+    private boolean shouldInterceptShortcuts(IBinder focusedToken) {
+        KeyInterceptionInfo info =
+                mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+        boolean hasInterceptWindowFlag = (info.layoutParamsPrivateFlags
+                & WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS) != 0;
+        return hasInterceptWindowFlag && mButtonOverridePermissionChecker.canAppOverrideSystemKey(
+                mContext, info.windowOwnerUid);
+    }
+
     /**
      * In this function, we check whether a system key should be sent to the application. We also
      * detect the key gesture on this key, even if the key will be sent to the app. The gesture
@@ -4760,8 +4772,8 @@
         // Handle special keys.
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
                 if (down) {
                     // There may have other embedded activities on the same Task. Try to move the
                     // focus before processing the back event.
@@ -4782,12 +4794,12 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                int systemShortcut = keyCode == KEYCODE_VOLUME_DOWN
-                        ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_DOWN
+                int gestureType = keyCode == KEYCODE_VOLUME_DOWN
+                        ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_DOWN
                         : keyCode == KEYCODE_VOLUME_UP
-                                ? KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_UP
-                                : KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_MUTE;
-                notifyKeyboardShortcutTriggeredOnActionDown(event, systemShortcut);
+                                ? KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP
+                                : KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE;
+                notifyKeyGestureCompletedOnActionDown(event, gestureType);
                 if (down) {
                     sendSystemKeyToStatusBarAsync(event);
 
@@ -4888,8 +4900,8 @@
             }
 
             case KeyEvent.KEYCODE_TV_POWER: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
                 if (down && hdmiControlManager != null) {
@@ -4899,8 +4911,8 @@
             }
 
             case KeyEvent.KEYCODE_POWER: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER);
                 EventLogTags.writeInterceptPower(
                         KeyEvent.actionToString(event.getAction()),
                         mPowerKeyHandled ? 1 : 0,
@@ -4923,16 +4935,16 @@
             case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
                 // fall through
             case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION);
                 result &= ~ACTION_PASS_TO_USER;
                 interceptSystemNavigationKey(event);
                 break;
             }
 
             case KeyEvent.KEYCODE_SLEEP: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false;
                 if (!mPowerManager.isInteractive()) {
@@ -4948,8 +4960,8 @@
             }
 
             case KeyEvent.KEYCODE_SOFT_SLEEP: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false;
                 if (!down) {
@@ -4960,8 +4972,8 @@
             }
 
             case KeyEvent.KEYCODE_WAKEUP: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_WAKEUP);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_WAKEUP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = true;
                 break;
@@ -4970,8 +4982,8 @@
             case KeyEvent.KEYCODE_MUTE:
                 result &= ~ACTION_PASS_TO_USER;
                 if (down && event.getRepeatCount() == 0) {
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_MUTE);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE);
                     toggleMicrophoneMuteFromKey();
                 }
                 break;
@@ -4986,8 +4998,8 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
-                notifyKeyboardShortcutTriggeredOnActionUp(event,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY);
+                notifyKeyGestureCompletedOnActionUp(event,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY);
                 if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
                     // If the global session is active pass all media keys to it
                     // instead of the active window.
@@ -5032,8 +5044,8 @@
                             0 /* unused */, event.getEventTime() /* eventTime */);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
@@ -5044,8 +5056,8 @@
                     Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
-                    notifyKeyboardShortcutTriggered(event,
-                            KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT);
+                    notifyKeyGestureCompleted(event,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 338b479..bdb174d 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -46,18 +46,7 @@
       ]
     },
     {
-      "name": "CtsPermissionTestCases",
-      "options": [
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-          "include-filter": "android.permission.cts.SplitPermissionTest"
-        },
-        {
-          "include-filter": "android.permission.cts.BackgroundPermissionsTest"
-        }
-      ]
+      "name": "CtsPermissionTestCases_Platform"
     },
     {
       "name": "CtsBackupTestCases",
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 67f5f27..892af6b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -313,12 +313,6 @@
         }
 
         /**
-         * Hint to window manager that the user has started a navigation action that should
-         * abort animations that have no timeout, in case they got stuck.
-         */
-        void triggerAnimationFailsafe();
-
-        /**
          * The keyguard showing state has changed
          */
         void onKeyguardShowingAndNotOccludedChanged();
@@ -368,6 +362,12 @@
          * Invoked when a screenshot is taken of the given display to notify registered listeners.
          */
         List<ComponentName> notifyScreenshotListeners(int displayId);
+
+        /**
+         * Returns whether the given UID is the owner of a virtual device, which the given display
+         * belongs to.
+         */
+        boolean isCallerVirtualDeviceOwner(int displayId, int callingUid);
     }
 
     /**
@@ -427,6 +427,7 @@
      * @param packageName package name
      * @param outAppOp First element will be filled with the app op corresponding to
      *                 this window, or OP_NONE.
+     * @param displayId The display on which this window is being added.
      *
      * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
      *      else an error code, usually
@@ -435,7 +436,7 @@
      * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
      */
     int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
-            int[] outAppOp);
+            int[] outAppOp, int displayId);
 
     /**
      * After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index d0b70c3..da8b01a 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -176,8 +176,9 @@
 
         final DreamManagerInternal dreamManager =
                 LocalServices.getService(DreamManagerInternal.class);
-
-        dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener);
+        if(dreamManager != null){
+            dreamManager.registerDreamManagerStateListener(mDreamManagerStateListener);
+        }
     }
 
     private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index b28da55b..303828f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -360,7 +360,13 @@
             IWakeLockCallback callback, int newFlags, String newTag, String newPackageName,
             int newOwnerUid, int newOwnerPid, WorkSource newWorkSource, String newHistoryTag,
             IWakeLockCallback newCallback) {
-
+        // Todo(b/359154665): We do this because the newWorkSource can potentially be updated
+        // before the request is processed on the notifier thread. This would generally happen is
+        // the Worksource's set method is called, which as of this comment happens only in
+        // PowerManager#setWorksource and WifiManager#WifiLock#setWorksource. Both these places
+        // need to be updated and the WorkSource#set should be deprecated to avoid falling into
+        // such traps
+        newWorkSource = (newWorkSource == null) ? null : new WorkSource(newWorkSource);
         final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
         final int newMonitorType = getBatteryStatsWakeLockMonitorType(newFlags);
         if (workSource != null && newWorkSource != null
@@ -1058,9 +1064,9 @@
     private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
             int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName,
             String historyTag) {
+        long currentTime = mInjector.currentTimeMillis();
         mHandler.post(() -> {
             if (mFlags.improveWakelockLatency()) {
-                long currentTime = mInjector.currentTimeMillis();
                 if (isEnabled) {
                     notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags,
                             workSource, packageName, historyTag, currentTime);
diff --git a/services/core/java/com/android/server/power/PowerGroup.java b/services/core/java/com/android/server/power/PowerGroup.java
index 72594b3..2e8a0c6 100644
--- a/services/core/java/com/android/server/power/PowerGroup.java
+++ b/services/core/java/com/android/server/power/PowerGroup.java
@@ -433,7 +433,8 @@
     boolean updateLocked(float screenBrightnessOverride, CharSequence overrideTag,
             boolean useProximitySensor, boolean boostScreenBrightness, int dozeScreenState,
             @Display.StateReason int dozeScreenStateReason,
-            float dozeScreenBrightness, boolean overrideDrawWakeLock,
+            float dozeScreenBrightness, boolean useNormalBrightnessForDoze,
+            boolean overrideDrawWakeLock,
             PowerSaveState powerSaverState, boolean quiescent,
             boolean dozeAfterScreenOff, boolean bootCompleted,
             boolean screenBrightnessBoostInProgress, boolean waitForNegativeProximity,
@@ -461,11 +462,13 @@
                 }
             }
             mDisplayPowerRequest.dozeScreenBrightness = dozeScreenBrightness;
+            mDisplayPowerRequest.useNormalBrightnessForDoze = useNormalBrightnessForDoze;
         } else {
             mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
             mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             mDisplayPowerRequest.dozeScreenStateReason =
                     Display.STATE_REASON_DEFAULT_POLICY;
+            mDisplayPowerRequest.useNormalBrightnessForDoze = false;
         }
         mDisplayPowerRequest.lowPowerMode = powerSaverState.batterySaverEnabled;
         mDisplayPowerRequest.screenLowPowerBrightnessFactor = powerSaverState.brightnessFactor;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 699c9b5..12e7fd0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -101,7 +101,6 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
-import android.sysprop.InitProperties;
 import android.sysprop.PowerProperties;
 import android.util.ArrayMap;
 import android.util.IntArray;
@@ -126,13 +125,12 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.util.Preconditions;
+import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.EventLogTags;
 import com.android.server.LockGuard;
-import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
-import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.feature.DeviceConfigParameterProvider;
@@ -662,6 +660,7 @@
      */
     private float mDozeScreenBrightnessOverrideFromDreamManagerFloat =
             PowerManager.BRIGHTNESS_INVALID_FLOAT;
+    private boolean mUseNormalBrightnessForDoze;
 
     // Keep display state when dozing.
     private boolean mDrawWakeLockOverrideFromSidekick;
@@ -1273,8 +1272,7 @@
             mHalInteractiveModeEnabled = true;
 
             mWakefulnessRaw = WAKEFULNESS_AWAKE;
-            sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1")
-                    || InitProperties.userspace_reboot_in_progress().orElse(false);
+            sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
 
             mNativeWrapper.nativeInit(this);
             mNativeWrapper.nativeSetAutoSuspend(false);
@@ -1381,8 +1379,10 @@
                     new DisplayGroupPowerChangeListener();
             mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener);
 
-            // This DreamManager method does not acquire a lock, so it should be safe to call.
-            mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener());
+            if(mDreamManager != null){
+                // This DreamManager method does not acquire a lock, so it should be safe to call.
+                mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener());
+            }
 
             mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager,
                     mInjector.createSuspendBlocker(
@@ -3545,7 +3545,7 @@
         }
 
         // Stop dream.
-        if (isDreaming) {
+        if (isDreaming && mDreamManager != null) {
             mDreamManager.stopDream(/* immediate= */ false, "power manager request" /*reason*/);
         }
     }
@@ -3656,6 +3656,7 @@
                         mDozeScreenStateOverrideFromDreamManager,
                         mDozeScreenStateOverrideReasonFromDreamManager,
                         mDozeScreenBrightnessOverrideFromDreamManagerFloat,
+                        mUseNormalBrightnessForDoze,
                         mDrawWakeLockOverrideFromSidekick,
                         mBatterySaverSupported
                                 ?
@@ -4030,10 +4031,9 @@
                 throw new UnsupportedOperationException(
                         "Attempted userspace reboot on a device that doesn't support it");
             }
-            UserspaceRebootLogger.noteUserspaceRebootWasRequested();
         }
         if (mHandler == null || !mSystemReady) {
-            if (RescueParty.isRecoveryTriggeredReboot()) {
+            if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) {
                 // If we're stuck in a really low-level reboot loop, and a
                 // rescue party is trying to prompt the user for a factory data
                 // reset, we must GET TO DA CHOPPA!
@@ -4470,13 +4470,13 @@
 
     private void setDozeOverrideFromDreamManagerInternal(
             int screenState, @Display.StateReason int reason, float screenBrightnessFloat,
-            int screenBrightnessInt) {
+            int screenBrightnessInt, boolean useNormalBrightnessForDoze) {
         synchronized (mLock) {
             if (mDozeScreenStateOverrideFromDreamManager != screenState
                     || mDozeScreenBrightnessOverrideFromDreamManager != screenBrightnessInt
                     || !BrightnessSynchronizer.floatEquals(
-                    mDozeScreenBrightnessOverrideFromDreamManagerFloat,
-                    screenBrightnessFloat)) {
+                    mDozeScreenBrightnessOverrideFromDreamManagerFloat, screenBrightnessFloat)
+                    || mUseNormalBrightnessForDoze != useNormalBrightnessForDoze) {
                 mDozeScreenStateOverrideFromDreamManager = screenState;
                 mDozeScreenStateOverrideReasonFromDreamManager = reason;
                 mDozeScreenBrightnessOverrideFromDreamManager = screenBrightnessInt;
@@ -4484,6 +4484,7 @@
                         isValidBrightnessValue(screenBrightnessFloat)
                                 ? screenBrightnessFloat
                                 : BrightnessSynchronizer.brightnessIntToFloat(screenBrightnessInt);
+                mUseNormalBrightnessForDoze = useNormalBrightnessForDoze;
                 mDirty |= DIRTY_SETTINGS;
                 updatePowerStateLocked();
             }
@@ -4798,6 +4799,7 @@
             pw.println("  mDrawWakeLockOverrideFromSidekick=" + mDrawWakeLockOverrideFromSidekick);
             pw.println("  mDozeScreenBrightnessOverrideFromDreamManager="
                     + mDozeScreenBrightnessOverrideFromDreamManager);
+            pw.println("  mUseNormalBrightnessForDoze=" + mUseNormalBrightnessForDoze);
             pw.println("  mScreenBrightnessMinimum=" + mScreenBrightnessMinimum);
             pw.println("  mScreenBrightnessMaximum=" + mScreenBrightnessMaximum);
             pw.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
@@ -7115,7 +7117,8 @@
 
         @Override
         public void setDozeOverrideFromDreamManager(
-                int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt) {
+                int screenState, int reason, float screenBrightnessFloat, int screenBrightnessInt,
+                boolean useNormalBrightnessForDoze) {
             switch (screenState) {
                 case Display.STATE_UNKNOWN:
                 case Display.STATE_OFF:
@@ -7138,7 +7141,7 @@
                 screenBrightnessFloat = PowerManager.BRIGHTNESS_INVALID_FLOAT;
             }
             setDozeOverrideFromDreamManagerInternal(screenState, reason, screenBrightnessFloat,
-                    screenBrightnessInt);
+                    screenBrightnessInt, useNormalBrightnessForDoze);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 4b4e442..d209ea9 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -59,8 +59,8 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.crashrecovery.CrashRecoveryHelper;
 import com.android.server.LocalServices;
-import com.android.server.RescueParty;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.File;
@@ -339,7 +339,7 @@
                             com.android.internal.R.string.reboot_to_update_reboot));
             }
         } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
-            if (RescueParty.isRecoveryTriggeredReboot()) {
+            if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) {
                 // We're not actually doing a factory reset yet; we're rebooting
                 // to ask the user if they'd like to reset, so give them a less
                 // scary dialog message.
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index e64704a..4ce01d2 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -8,11 +8,7 @@
       ]
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.power"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"}
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_power_Presubmit"
     },
     {
       "name": "PowerServiceTests",
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 7f24769..6847a5c 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -54,6 +54,7 @@
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.Temperature;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Slog;
@@ -247,6 +248,7 @@
 
     private void setStatusLocked(int newStatus) {
         if (newStatus != mStatus) {
+            Trace.traceCounter(Trace.TRACE_TAG_POWER, "ThermalManagerService.status", newStatus);
             mStatus = newStatus;
             notifyStatusListenersLocked();
         }
@@ -1644,8 +1646,7 @@
                         if (Flags.allowThermalHeadroomThresholds()) {
                             for (int severity = ThrottlingSeverity.LIGHT;
                                     severity <= ThrottlingSeverity.SHUTDOWN; severity++) {
-                                if (severity != ThrottlingSeverity.SEVERE
-                                        && threshold.hotThrottlingThresholds.length > severity) {
+                                if (threshold.hotThrottlingThresholds.length > severity) {
                                     updateHeadroomThreshold(severity,
                                             threshold.hotThrottlingThresholds[severity],
                                             severeThreshold);
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index 968ff59..eda222e 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.PowerManager;
+import android.os.Process;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -122,6 +123,9 @@
 
     private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
+    @VisibleForTesting
+    static final String SYSTEM_PACKAGE_NAME = "System";
+
     /**
      * Lock protects WakeLockLog.dump (binder thread) from conflicting with changes to the log
      * happening on the background thread.
@@ -516,21 +520,26 @@
                 return;
             }
 
-            String[] packages;
-            if (uidToPackagesCache.contains(tag.ownerUid)) {
-                packages = uidToPackagesCache.get(tag.ownerUid);
-            } else {
-                packages = packageManager.getPackagesForUid(tag.ownerUid);
-                uidToPackagesCache.put(tag.ownerUid, packages);
+            if (tag.ownerUid == Process.SYSTEM_UID) {
+                packageName = SYSTEM_PACKAGE_NAME;
             }
+            else {
+                String[] packages;
+                if (uidToPackagesCache.contains(tag.ownerUid)) {
+                    packages = uidToPackagesCache.get(tag.ownerUid);
+                } else {
+                    packages = packageManager.getPackagesForUid(tag.ownerUid);
+                    uidToPackagesCache.put(tag.ownerUid, packages);
+                }
 
-            if (packages != null && packages.length > 0) {
-                packageName = packages[0];
-                if (packages.length > 1) {
-                    StringBuilder sb = new StringBuilder();
-                    sb.append(packageName)
-                            .append(",...");
-                    packageName = sb.toString();
+                if (packages != null && packages.length > 0) {
+                    packageName = packages[0];
+                    if (packages.length > 1) {
+                        StringBuilder sb = new StringBuilder();
+                        sb.append(packageName)
+                                .append(",...");
+                        packageName = sb.toString();
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index 9a4c60d..68760aa 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -864,7 +864,8 @@
                     buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
                             R.string.dynamic_mode_notification_title,
                             R.string.dynamic_mode_notification_summary,
-                            Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L),
+                            Settings.ACTION_BATTERY_SAVER_SETTINGS, 0L,
+                            R.drawable.ic_settings),
                     UserHandle.ALL);
         });
     }
@@ -889,7 +890,8 @@
                             R.string.dynamic_mode_notification_summary_v2,
                             Settings.ACTION_BATTERY_SAVER_SETTINGS,
                             0L /* timeoutMs */,
-                            highlightBundle),
+                            highlightBundle,
+                            R.drawable.ic_qs_battery_saver),
                     UserHandle.ALL);
         });
     }
@@ -911,7 +913,8 @@
                             R.string.battery_saver_off_notification_title,
                             R.string.battery_saver_charged_notification_summary,
                             Settings.ACTION_BATTERY_SAVER_SETTINGS,
-                            STICKY_DISABLED_NOTIFY_TIMEOUT_MS),
+                            STICKY_DISABLED_NOTIFY_TIMEOUT_MS,
+                            R.drawable.ic_settings),
                     UserHandle.ALL);
         });
     }
@@ -926,7 +929,7 @@
     }
 
     private Notification buildNotification(@NonNull String channelId, @StringRes int titleId,
-            @StringRes int summaryId, @NonNull String intentAction, long timeoutMs) {
+            @StringRes int summaryId, @NonNull String intentAction, long timeoutMs, int iconResId) {
         Resources res = mContext.getResources();
         Intent intent = new Intent(intentAction);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -937,7 +940,7 @@
         final String summary = res.getString(summaryId);
 
         return new Notification.Builder(mContext, channelId)
-                .setSmallIcon(R.drawable.ic_battery)
+                .setSmallIcon(iconResId)
                 .setContentTitle(title)
                 .setContentText(summary)
                 .setContentIntent(batterySaverIntent)
@@ -950,7 +953,7 @@
 
     private Notification buildNotificationV2(@NonNull String channelId, @StringRes int titleId,
             @StringRes int summaryId, @NonNull String intentAction, long timeoutMs,
-            @NonNull Bundle highlightBundle) {
+            @NonNull Bundle highlightBundle, int iconResId) {
         Resources res = mContext.getResources();
         Intent intent = new Intent(intentAction)
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
@@ -963,7 +966,7 @@
         final String summary = res.getString(summaryId);
 
         return new Notification.Builder(mContext, channelId)
-                .setSmallIcon(R.drawable.ic_battery)
+                .setSmallIcon(iconResId)
                 .setContentTitle(title)
                 .setContentText(summary)
                 .setContentIntent(batterySaverIntent)
diff --git a/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
index eb91a72..d29dbfe 100644
--- a/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
@@ -11,10 +11,7 @@
       "name": "CtsLocationNoneTestCases"
     },
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {"include-filter": "com.android.server.location"}
-      ]
+      "name": "FrameworksMockingServicesTests_location"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
deleted file mode 100644
index 7496d2d..0000000
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStats.java
+++ /dev/null
@@ -1,370 +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.server.power.stats;
-
-import android.annotation.CurrentTimeMillisLong;
-import android.annotation.DurationMillisLong;
-import android.annotation.NonNull;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.UserHandle;
-import android.text.format.DateFormat;
-import android.util.IndentingPrintWriter;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.TimeUtils;
-
-import com.android.internal.os.PowerStats;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-import com.android.server.power.stats.AggregatedPowerStatsConfig.PowerComponent;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.TimeZone;
-
-/**
- * This class represents aggregated power stats for a variety of power components (CPU, WiFi,
- * etc) covering a specific period of power usage history.
- */
-class AggregatedPowerStats {
-    private static final String TAG = "AggregatedPowerStats";
-    private static final int MAX_CLOCK_UPDATES = 100;
-    private static final String XML_TAG_AGGREGATED_POWER_STATS = "agg-power-stats";
-
-    private final AggregatedPowerStatsConfig mConfig;
-    private final SparseArray<PowerComponentAggregatedPowerStats> mPowerComponentStats;
-    private final PowerComponentAggregatedPowerStats mGenericPowerComponent;
-
-    static class ClockUpdate {
-        public long monotonicTime;
-        @CurrentTimeMillisLong
-        public long currentTime;
-    }
-
-    private final List<ClockUpdate> mClockUpdates = new ArrayList<>();
-
-    @DurationMillisLong
-    private long mDurationMs;
-
-    AggregatedPowerStats(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
-        this(aggregatedPowerStatsConfig, new SparseBooleanArray());
-    }
-
-    AggregatedPowerStats(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
-            @NonNull SparseBooleanArray enabledComponents) {
-        mConfig = aggregatedPowerStatsConfig;
-        List<PowerComponent> configs =
-                aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
-        mPowerComponentStats = new SparseArray<>(configs.size());
-        for (int i = 0; i < configs.size(); i++) {
-            PowerComponent powerComponent = configs.get(i);
-            if (enabledComponents.get(powerComponent.getPowerComponentId(), true)) {
-                mPowerComponentStats.put(powerComponent.getPowerComponentId(),
-                        new PowerComponentAggregatedPowerStats(this, powerComponent));
-            }
-        }
-        mGenericPowerComponent = createGenericPowerComponent();
-        mPowerComponentStats.put(BatteryConsumer.POWER_COMPONENT_ANY, mGenericPowerComponent);
-    }
-
-    private PowerComponentAggregatedPowerStats createGenericPowerComponent() {
-        PowerComponent config = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
-        config.trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-        PowerComponentAggregatedPowerStats stats =
-                new PowerComponentAggregatedPowerStats(this, config);
-        stats.setPowerStatsDescriptor(
-                new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_ANY, 0, null, 0, 0,
-                        new PersistableBundle()));
-        return stats;
-    }
-
-    /**
-     * Records a mapping of monotonic time to wall-clock time. Since wall-clock time can change,
-     * there may be multiple clock updates in one set of aggregated stats.
-     *
-     * @param monotonicTime monotonic time in milliseconds, see
-     *                      {@link com.android.internal.os.MonotonicClock}
-     * @param currentTime   current time in milliseconds, see {@link System#currentTimeMillis()}
-     */
-    void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) {
-        ClockUpdate clockUpdate = new ClockUpdate();
-        clockUpdate.monotonicTime = monotonicTime;
-        clockUpdate.currentTime = currentTime;
-        if (mClockUpdates.size() < MAX_CLOCK_UPDATES) {
-            mClockUpdates.add(clockUpdate);
-        } else {
-            Slog.i(TAG, "Too many clock updates. Replacing the previous update with "
-                    + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
-            mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate);
-        }
-    }
-
-    /**
-     * Start time according to {@link com.android.internal.os.MonotonicClock}
-     */
-    long getStartTime() {
-        if (mClockUpdates.isEmpty()) {
-            return 0;
-        } else {
-            return mClockUpdates.get(0).monotonicTime;
-        }
-    }
-
-    List<ClockUpdate> getClockUpdates() {
-        return mClockUpdates;
-    }
-
-    void setDuration(long durationMs) {
-        mDurationMs = durationMs;
-    }
-
-    @DurationMillisLong
-    public long getDuration() {
-        return mDurationMs;
-    }
-
-    List<PowerComponentAggregatedPowerStats> getPowerComponentStats() {
-        List<PowerComponentAggregatedPowerStats> list = new ArrayList<>(
-                mPowerComponentStats.size());
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
-            if (stats != mGenericPowerComponent) {
-                list.add(stats);
-            }
-        }
-        return list;
-    }
-
-    PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
-        return mPowerComponentStats.get(powerComponentId);
-    }
-
-    void start(long timestampMs) {
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            PowerComponentAggregatedPowerStats component = mPowerComponentStats.valueAt(i);
-            component.getConfig().getProcessor().start(component, timestampMs);
-        }
-    }
-
-    void setDeviceState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
-            long time) {
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            mPowerComponentStats.valueAt(i).setState(stateId, state, time);
-        }
-    }
-
-    void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
-            long time) {
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            mPowerComponentStats.valueAt(i).setUidState(uid, stateId, state, time);
-        }
-    }
-
-    boolean isCompatible(PowerStats powerStats) {
-        int powerComponentId = powerStats.descriptor.powerComponentId;
-        PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
-        return stats != null && stats.isCompatible(powerStats);
-    }
-
-    void addPowerStats(PowerStats powerStats, long time) {
-        int powerComponentId = powerStats.descriptor.powerComponentId;
-        PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
-        if (stats == null) {
-            PowerComponent powerComponent = mConfig.createPowerComponent(powerComponentId);
-            if (powerComponent == null) {
-                return;
-            }
-
-            stats = new PowerComponentAggregatedPowerStats(this, powerComponent);
-            stats.setPowerStatsDescriptor(powerStats.descriptor);
-            stats.copyStatesFrom(mGenericPowerComponent);
-            mPowerComponentStats.put(powerComponentId, stats);
-        }
-
-        PowerStatsProcessor processor = stats.getConfig().getProcessor();
-        processor.addPowerStats(stats, powerStats, time);
-    }
-
-    public void noteStateChange(BatteryStats.HistoryItem item) {
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
-            stats.getConfig().getProcessor().noteStateChange(stats, item);
-        }
-    }
-
-    void finish(long timestampMs) {
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            PowerComponentAggregatedPowerStats component = mPowerComponentStats.valueAt(i);
-            component.getConfig().getProcessor().finish(component, timestampMs);
-        }
-    }
-
-    void reset() {
-        mClockUpdates.clear();
-        mDurationMs = 0;
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            mPowerComponentStats.valueAt(i).reset();
-        }
-    }
-
-    public void writeXml(TypedXmlSerializer serializer) throws IOException {
-        serializer.startTag(null, XML_TAG_AGGREGATED_POWER_STATS);
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
-            if (stats != mGenericPowerComponent) {
-                stats.writeXml(serializer);
-            }
-        }
-        serializer.endTag(null, XML_TAG_AGGREGATED_POWER_STATS);
-        serializer.flush();
-    }
-
-    @NonNull
-    public static AggregatedPowerStats createFromXml(
-            TypedXmlPullParser parser, AggregatedPowerStatsConfig aggregatedPowerStatsConfig)
-            throws XmlPullParserException, IOException {
-        AggregatedPowerStats stats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
-        boolean inElement = false;
-        boolean skipToEnd = false;
-        int eventType = parser.getEventType();
-        while (eventType != XmlPullParser.END_DOCUMENT
-                   && !(eventType == XmlPullParser.END_TAG
-                        && parser.getName().equals(XML_TAG_AGGREGATED_POWER_STATS))) {
-            if (!skipToEnd && eventType == XmlPullParser.START_TAG) {
-                switch (parser.getName()) {
-                    case XML_TAG_AGGREGATED_POWER_STATS:
-                        inElement = true;
-                        break;
-                    case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT: {
-                        if (!inElement) {
-                            break;
-                        }
-
-                        int powerComponentId = parser.getAttributeInt(null,
-                                PowerComponentAggregatedPowerStats.XML_ATTR_ID);
-
-                        PowerComponentAggregatedPowerStats powerComponentStats =
-                                stats.getPowerComponentStats(powerComponentId);
-                        if (powerComponentStats == null) {
-                            PowerComponent powerComponent =
-                                    aggregatedPowerStatsConfig.createPowerComponent(
-                                            powerComponentId);
-                            if (powerComponent != null) {
-                                powerComponentStats = new PowerComponentAggregatedPowerStats(stats,
-                                        powerComponent);
-                                stats.mPowerComponentStats.put(powerComponentId,
-                                        powerComponentStats);
-                            }
-                        }
-                        if (powerComponentStats != null) {
-                            if (!powerComponentStats.readFromXml(parser)) {
-                                skipToEnd = true;
-                            }
-                        }
-                        break;
-                    }
-                }
-            }
-            eventType = parser.next();
-        }
-        return stats;
-    }
-
-    void dump(IndentingPrintWriter ipw) {
-        StringBuilder sb = new StringBuilder();
-        long baseTime = 0;
-        for (int i = 0; i < mClockUpdates.size(); i++) {
-            ClockUpdate clockUpdate = mClockUpdates.get(i);
-            sb.setLength(0);
-            if (i == 0) {
-                baseTime = clockUpdate.monotonicTime;
-                sb.append("Start time: ")
-                        .append(formatDateTime(clockUpdate.currentTime))
-                        .append(" (")
-                        .append(baseTime)
-                        .append(") duration: ")
-                        .append(mDurationMs);
-                ipw.println(sb);
-            } else {
-                sb.setLength(0);
-                sb.append("Clock update:  ");
-                TimeUtils.formatDuration(
-                        clockUpdate.monotonicTime - baseTime, sb,
-                        TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
-                sb.append(" ").append(formatDateTime(clockUpdate.currentTime));
-                ipw.increaseIndent();
-                ipw.println(sb);
-                ipw.decreaseIndent();
-            }
-        }
-
-        ipw.println("Device");
-        ipw.increaseIndent();
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            mPowerComponentStats.valueAt(i).dumpDevice(ipw);
-        }
-        ipw.decreaseIndent();
-
-        Set<Integer> uids = new HashSet<>();
-        for (int i = 0; i < mPowerComponentStats.size(); i++) {
-            mPowerComponentStats.valueAt(i).collectUids(uids);
-        }
-
-        Integer[] allUids = uids.toArray(new Integer[uids.size()]);
-        Arrays.sort(allUids);
-        for (int uid : allUids) {
-            ipw.println(UserHandle.formatUid(uid));
-            ipw.increaseIndent();
-            for (int i = 0; i < mPowerComponentStats.size(); i++) {
-                mPowerComponentStats.valueAt(i).dumpUid(ipw, uid);
-            }
-            ipw.decreaseIndent();
-        }
-    }
-
-    private static String formatDateTime(long timeInMillis) {
-        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
-        format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
-        return format.format(new Date(timeInMillis));
-    }
-
-    @Override
-    public String toString() {
-        StringWriter sw = new StringWriter();
-        dump(new IndentingPrintWriter(sw));
-        return sw.toString();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
deleted file mode 100644
index 1f4a391..0000000
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ /dev/null
@@ -1,250 +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.server.power.stats;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.BatteryConsumer;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Supplier;
-
-/**
- * Configuration that controls how power stats are aggregated.  It determines which state changes
- * are to be considered as essential dimensions ("tracked states") for each power component (CPU,
- * WiFi, etc).  Also, it determines which states are tracked globally and which ones on a per-UID
- * basis.
- */
-public class AggregatedPowerStatsConfig {
-    public static final int STATE_POWER = 0;
-    public static final int STATE_SCREEN = 1;
-    public static final int STATE_PROCESS_STATE = 2;
-
-    @IntDef({
-            STATE_POWER,
-            STATE_SCREEN,
-            STATE_PROCESS_STATE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface TrackedState {
-    }
-
-    static final String STATE_NAME_POWER = "pwr";
-    static final int POWER_STATE_BATTERY = 0;
-    static final int POWER_STATE_OTHER = 1;   // Plugged in, or on wireless charger, etc.
-    static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
-
-    static final String STATE_NAME_SCREEN = "scr";
-    static final int SCREEN_STATE_ON = 0;
-    static final int SCREEN_STATE_OTHER = 1;  // Off, doze etc
-    static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
-
-    static final String STATE_NAME_PROCESS_STATE = "ps";
-    static final String[] STATE_LABELS_PROCESS_STATE;
-
-    static {
-        String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
-        for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
-            procStateLabels[i] = BatteryConsumer.processStateToString(i);
-        }
-        STATE_LABELS_PROCESS_STATE = procStateLabels;
-    }
-
-    /**
-     * Configuration for a give power component (CPU, WiFi, etc)
-     */
-    public static class PowerComponent {
-        private final int mPowerComponentId;
-        private @TrackedState int[] mTrackedDeviceStates;
-        private @TrackedState int[] mTrackedUidStates;
-        private PowerStatsProcessor mProcessor = NO_OP_PROCESSOR;
-
-        PowerComponent(int powerComponentId) {
-            this.mPowerComponentId = powerComponentId;
-        }
-
-        /**
-         * Configures which states should be tracked as separate dimensions for the entire device.
-         */
-        public PowerComponent trackDeviceStates(@TrackedState int... states) {
-            if (mTrackedDeviceStates != null) {
-                throw new IllegalStateException("Component is already configured");
-            }
-            mTrackedDeviceStates = states;
-            return this;
-        }
-
-        /**
-         * Configures which states should be tracked as separate dimensions on a per-UID basis.
-         */
-        public PowerComponent trackUidStates(@TrackedState int... states) {
-            if (mTrackedUidStates != null) {
-                throw new IllegalStateException("Component is already configured");
-            }
-            mTrackedUidStates = states;
-            return this;
-        }
-
-        /**
-         * Takes an object that should be invoked for every aggregated stats span
-         * before giving the aggregates stats to consumers. The processor can complete the
-         * aggregation process, for example by computing estimated power usage.
-         */
-        public PowerComponent setProcessor(@NonNull PowerStatsProcessor processor) {
-            mProcessor = processor;
-            return this;
-        }
-
-        public int getPowerComponentId() {
-            return mPowerComponentId;
-        }
-
-        public MultiStateStats.States[] getDeviceStateConfig() {
-            return new MultiStateStats.States[]{
-                    new MultiStateStats.States(STATE_NAME_POWER,
-                            isTracked(mTrackedDeviceStates, STATE_POWER),
-                            STATE_LABELS_POWER),
-                    new MultiStateStats.States(STATE_NAME_SCREEN,
-                            isTracked(mTrackedDeviceStates, STATE_SCREEN),
-                            STATE_LABELS_SCREEN),
-            };
-        }
-
-        public MultiStateStats.States[] getUidStateConfig() {
-            return new MultiStateStats.States[]{
-                    new MultiStateStats.States(STATE_NAME_POWER,
-                            isTracked(mTrackedUidStates, STATE_POWER),
-                            AggregatedPowerStatsConfig.STATE_LABELS_POWER),
-                    new MultiStateStats.States(STATE_NAME_SCREEN,
-                            isTracked(mTrackedUidStates, STATE_SCREEN),
-                            AggregatedPowerStatsConfig.STATE_LABELS_SCREEN),
-                    new MultiStateStats.States(STATE_NAME_PROCESS_STATE,
-                            isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
-                            AggregatedPowerStatsConfig.STATE_LABELS_PROCESS_STATE),
-            };
-        }
-
-        @NonNull
-        PowerStatsProcessor getProcessor() {
-            return mProcessor;
-        }
-
-        private boolean isTracked(int[] trackedStates, int state) {
-            if (trackedStates == null) {
-                return false;
-            }
-
-            for (int trackedState : trackedStates) {
-                if (trackedState == state) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-    }
-
-    private final List<PowerComponent> mPowerComponents = new ArrayList<>();
-    private PowerComponent mCustomPowerComponent;
-    private Supplier<PowerStatsProcessor> mCustomPowerStatsProcessorFactory;
-
-    /**
-     * Creates a configuration for the specified power component, which may be one of the
-     * standard power component IDs, e.g. {@link BatteryConsumer#POWER_COMPONENT_CPU}, or
-     * a custom power component.
-     */
-    public PowerComponent trackPowerComponent(int powerComponentId) {
-        PowerComponent builder = new PowerComponent(powerComponentId);
-        mPowerComponents.add(builder);
-        return builder;
-    }
-
-    /**
-     * Creates a configuration for the specified power component, which is a subcomponent
-     * of a different power component.  The tracked states will be the same as the parent
-     * component's.
-     */
-    public PowerComponent trackPowerComponent(int powerComponentId,
-            int parentPowerComponentId) {
-        PowerComponent parent = null;
-        for (int i = 0; i < mPowerComponents.size(); i++) {
-            PowerComponent powerComponent = mPowerComponents.get(i);
-            if (powerComponent.getPowerComponentId() == parentPowerComponentId) {
-                parent = powerComponent;
-                break;
-            }
-        }
-
-        if (parent == null) {
-            throw new IllegalArgumentException(
-                    "Parent component " + parentPowerComponentId + " is not configured");
-        }
-
-        PowerComponent powerComponent = trackPowerComponent(powerComponentId);
-        powerComponent.mTrackedDeviceStates = parent.mTrackedDeviceStates;
-        powerComponent.mTrackedUidStates = parent.mTrackedUidStates;
-        return powerComponent;
-    }
-
-    /**
-     * Creates a configuration for custom power components, which are yet to be discovered
-     * dynamically through the integration with PowerStatsService.
-     */
-    public PowerComponent trackCustomPowerComponents(
-            Supplier<PowerStatsProcessor> processorFactory) {
-        mCustomPowerStatsProcessorFactory = processorFactory;
-        mCustomPowerComponent = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
-        return mCustomPowerComponent;
-    }
-
-    /**
-     * Returns configurations for all registered or dynamically discovered power components.
-     */
-    public List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
-        return mPowerComponents;
-    }
-
-    /**
-     * Creates a configuration for a custom power component discovered dynamically through the
-     * integration with PowerStatsService.
-     */
-    @Nullable
-    public PowerComponent createPowerComponent(int powerComponentId) {
-        if (mCustomPowerComponent == null) {
-            return null;
-        }
-
-        PowerComponent powerComponent = new PowerComponent(powerComponentId);
-        powerComponent.trackDeviceStates(mCustomPowerComponent.mTrackedDeviceStates);
-        powerComponent.trackUidStates(mCustomPowerComponent.mTrackedUidStates);
-
-        if (mCustomPowerStatsProcessorFactory != null) {
-            powerComponent.setProcessor(mCustomPowerStatsProcessorFactory.get());
-        }
-
-        return powerComponent;
-    }
-
-    private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
-        @Override
-        void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        }
-    };
-}
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
deleted file mode 100644
index 7ba4330..0000000
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsSection.java
+++ /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.server.power.stats;
-
-import android.util.IndentingPrintWriter;
-
-import com.android.modules.utils.TypedXmlSerializer;
-
-import java.io.IOException;
-
-class AggregatedPowerStatsSection extends PowerStatsSpan.Section {
-    public static final String TYPE = "aggregated-power-stats";
-
-    private final AggregatedPowerStats mAggregatedPowerStats;
-
-    AggregatedPowerStatsSection(AggregatedPowerStats aggregatedPowerStats) {
-        super(TYPE);
-        mAggregatedPowerStats = aggregatedPowerStats;
-    }
-
-    public AggregatedPowerStats getAggregatedPowerStats() {
-        return mAggregatedPowerStats;
-    }
-
-    @Override
-    void write(TypedXmlSerializer serializer) throws IOException {
-        mAggregatedPowerStats.writeXml(serializer);
-    }
-
-    @Override
-    public void dump(IndentingPrintWriter ipw) {
-        mAggregatedPowerStats.dump(ipw);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
deleted file mode 100644
index a42929f..0000000
--- a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerStatsProcessor.java
+++ /dev/null
@@ -1,75 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.PersistableBundle;
-
-import com.android.internal.os.PowerStats;
-
-public class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
-    private final PowerStatsLayout mStatsLayout;
-    private final PowerStats.Descriptor mDescriptor;
-    private final long[] mTmpDeviceStats;
-    private PowerStats.Descriptor mScreenPowerStatsDescriptor;
-    private ScreenPowerStatsLayout mScreenPowerStatsLayout;
-    private long[] mTmpScreenStats;
-
-    public AmbientDisplayPowerStatsProcessor() {
-        mStatsLayout = new PowerStatsLayout();
-        mStatsLayout.addDeviceSectionPowerEstimate();
-        PersistableBundle extras = new PersistableBundle();
-        mStatsLayout.toExtras(extras);
-        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
-                mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
-        mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        stats.setPowerStatsDescriptor(mDescriptor);
-
-        PowerComponentAggregatedPowerStats screenStats =
-                stats.getAggregatedPowerStats().getPowerComponentStats(
-                        BatteryConsumer.POWER_COMPONENT_SCREEN);
-        if (screenStats == null) {
-            return;
-        }
-
-        if (mScreenPowerStatsDescriptor == null) {
-            mScreenPowerStatsDescriptor = screenStats.getPowerStatsDescriptor();
-            if (mScreenPowerStatsDescriptor == null) {
-                return;
-            }
-
-            mScreenPowerStatsLayout = new ScreenPowerStatsLayout(mScreenPowerStatsDescriptor);
-            mTmpScreenStats = new long[mScreenPowerStatsDescriptor.statsArrayLength];
-        }
-
-        MultiStateStats.States[] deviceStateConfig = screenStats.getConfig().getDeviceStateConfig();
-
-        // Ambient display power estimates have already been calculated by the screen power stats
-        // processor. All that remains to be done is copy the estimates over.
-        MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
-                states -> {
-                    screenStats.getDeviceStats(mTmpScreenStats, states);
-                    double power =
-                            mScreenPowerStatsLayout.getScreenDozePowerEstimate(mTmpScreenStats);
-                    mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
-                    stats.setDeviceStats(states, mTmpDeviceStats);
-                });
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
deleted file mode 100644
index a48f162..0000000
--- a/services/core/java/com/android/server/power/stats/AudioPowerStatsProcessor.java
+++ /dev/null
@@ -1,37 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-
-import com.android.internal.os.PowerProfile;
-
-public class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-    public AudioPowerStatsProcessor(PowerProfile powerProfile,
-            PowerStatsUidResolver uidResolver) {
-        super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver,
-                powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
-    }
-
-    @Override
-    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
-        return (item.states & BatteryStats.HistoryItem.STATE_AUDIO_ON_FLAG) != 0
-                ? STATE_ON
-                : STATE_OFF;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index 2f16419..8311034 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -120,7 +120,7 @@
     private int mScreenState;
 
     @GuardedBy("this")
-    private int[] mPerDisplayScreenStates = null;
+    private int[] mPerDisplayScreenStates;
 
     @GuardedBy("this")
     private boolean mUseLatestStates = true;
@@ -149,8 +149,7 @@
     // WiFi keeps an accumulated total of stats. Keep the last WiFi stats so we can compute a delta.
     // (This is unlike Bluetooth, where BatteryStatsImpl is left responsible for taking the delta.)
     @GuardedBy("mWorkerLock")
-    private WifiActivityEnergyInfo mLastWifiInfo =
-            new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+    private WifiActivityEnergyInfo mLastWifiInfo = null;
 
     /**
      * Maps an {@link EnergyConsumerType} to it's corresponding {@link EnergyConsumer#id}s,
@@ -244,6 +243,7 @@
             }
             synchronized (mStats) {
                 mStats.initEnergyConsumerStatsLocked(supportedStdBuckets, customBucketNames);
+                mPerDisplayScreenStates = new int[mStats.getDisplayCount()];
             }
         }
     }
@@ -491,6 +491,12 @@
                                 onBatteryScreenOff, screenState, displayScreenStates,
                                 useLatestStates);
                     } finally {
+                        if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
+                            synchronized (mStats) {
+                                // This helps mStats deal with ignoring data from prior to resets.
+                                mStats.informThatAllExternalStatsAreFlushed();
+                            }
+                        }
                         if (DEBUG) {
                             Slog.d(TAG, "end updateExternalStatsSync");
                         }
@@ -768,7 +774,6 @@
 
         // WiFi and Modem state are updated without the mStats lock held, because they
         // do some network stats retrieval before internally grabbing the mStats lock.
-
         if (wifiInfo != null) {
             if (wifiInfo.isValid()) {
                 final long wifiChargeUC =
@@ -791,11 +796,6 @@
             mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
                     uptime, networkStatsManager);
         }
-
-        if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
-            // This helps mStats deal with ignoring data from prior to resets.
-            mStats.informThatAllExternalStatsAreFlushed();
-        }
     }
 
     /**
@@ -827,8 +827,18 @@
         return null;
     }
 
+    /**
+     * Return a delta WifiActivityEnergyInfo from the last WifiActivityEnergyInfo passed to the
+     * method.
+     */
+    @VisibleForTesting
     @GuardedBy("mWorkerLock")
-    private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
+    public WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
+        if (mLastWifiInfo == null) {
+            // This is the first time WifiActivityEnergyInfo has been collected since system boot.
+            // Use this first WifiActivityEnergyInfo as the starting point for all accumulations.
+            mLastWifiInfo = latest;
+        }
         final long timePeriodMs = latest.getTimeSinceBootMillis()
                 - mLastWifiInfo.getTimeSinceBootMillis();
         final long lastScanMs = mLastWifiInfo.getControllerScanDurationMillis();
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 c878f14..680b1ac 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -23,8 +23,6 @@
 import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
 import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
 
-import static com.android.server.power.stats.MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -149,6 +147,7 @@
 import com.android.server.LocalServices;
 import com.android.server.power.optimization.Flags;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
 
 import libcore.util.EmptyArray;
 
@@ -180,7 +179,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.IntSupplier;
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 
@@ -1167,7 +1165,6 @@
     private static final int USB_DATA_CONNECTED = 2;
     int mUsbDataState = USB_DATA_UNKNOWN;
 
-    private static final int GPS_SIGNAL_QUALITY_NONE = 2;
     int mGpsSignalQualityBin = -1;
     final StopwatchTimer[] mGpsSignalQualityTimer =
         new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
@@ -2029,7 +2026,7 @@
         void setContext(Context context) {
             mPackageManager = context.getPackageManager();
             mConsumedEnergyRetriever = new PowerStatsCollector.ConsumedEnergyRetrieverImpl(
-                    LocalServices.getService(PowerStatsInternal.class));
+                    LocalServices.getService(PowerStatsInternal.class), () -> mBatteryVoltageMv);
             mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
             mTelephonyManager =
                     (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2084,11 +2081,6 @@
         }
 
         @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> mBatteryVoltageMv;
-        }
-
-        @Override
         public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
             return mScreenUsageTimeRetriever;
         }
@@ -2131,13 +2123,13 @@
         @Override
         public LongSupplier getCallDurationSupplier() {
             return () -> mPhoneOnTimer.getTotalTimeLocked(mClock.elapsedRealtime() * 1000,
-                    STATS_SINCE_CHARGED);
+                    STATS_SINCE_CHARGED) / 1000;
         }
 
         @Override
         public LongSupplier getPhoneSignalScanDurationSupplier() {
             return () -> mPhoneSignalScanningTimer.getTotalTimeLocked(
-                    mClock.elapsedRealtime() * 1000, STATS_SINCE_CHARGED);
+                    mClock.elapsedRealtime() * 1000, STATS_SINCE_CHARGED) / 1000;
         }
     }
 
@@ -5528,7 +5520,7 @@
             mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
                     HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
             mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs,
-                    GPS_SIGNAL_QUALITY_NONE);
+                    HistoryItem.GNSS_SIGNAL_QUALITY_NONE);
             stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
             mGpsSignalQualityBin = -1;
             if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
@@ -13192,7 +13184,8 @@
                 final int freq = deltaInfo.getSpecificInfoFrequencyRange(index);
 
                 // Map RadioAccessNetworkType to course grain RadioAccessTechnology.
-                final int ratBucket = mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
+                final int ratBucket = MobileRadioPowerStatsLayout
+                        .mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
                 final RadioAccessTechnologyBatteryStats ratStats = getRatBatteryStatsLocked(
                         ratBucket);
 
@@ -15301,15 +15294,6 @@
                 mHistory.writeHistoryItem(elapsedRealtimeMs, uptimeMs);
             }
         }
-        if (!onBattery &&
-                (status == BatteryManager.BATTERY_STATUS_FULL ||
-                        status == BatteryManager.BATTERY_STATUS_UNKNOWN)) {
-            // We don't record history while we are plugged in and fully charged
-            // (or when battery is not present).  The next time we are
-            // unplugged, history will be cleared.
-            mHistory.setHistoryRecordingEnabled(DEBUG);
-        }
-
         mLastLearnedBatteryCapacityUah = chargeFullUah;
         if (mMinLearnedBatteryCapacityUah == -1) {
             mMinLearnedBatteryCapacityUah = chargeFullUah;
@@ -15487,7 +15471,8 @@
         final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
         final long totalControllerActivityTimeMs =
                 computeBatteryRealtime(mClock.elapsedRealtime() * 1000, which) / 1000;
-        final long sleepTimeMs = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+        final long sleepTimeMs = Math.max(0,
+                totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs));
         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
         final long monitoredRailChargeConsumedMaMs =
                 counter.getMonitoredRailChargeConsumedMaMs().getCountLocked(which);
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index d51cfea..87a3e5e 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power.stats;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.SensorManager;
 import android.os.BatteryConsumer;
@@ -27,7 +28,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.CpuScalingPolicies;
@@ -44,8 +44,7 @@
 public class BatteryUsageStatsProvider {
     private static final String TAG = "BatteryUsageStatsProv";
     private final Context mContext;
-    private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
-    private final PowerStatsExporter mPowerStatsExporter;
+    private final PowerAttributor mPowerAttributor;
     private final PowerStatsStore mPowerStatsStore;
     private final PowerProfile mPowerProfile;
     private final CpuScalingPolicies mCpuScalingPolicies;
@@ -53,16 +52,18 @@
     private final Object mLock = new Object();
     private List<PowerCalculator> mPowerCalculators;
 
-    public BatteryUsageStatsProvider(Context context,
-            PowerStatsExporter powerStatsExporter,
-            PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
-            PowerStatsStore powerStatsStore, Clock clock) {
+    public BatteryUsageStatsProvider(@NonNull Context context,
+            @NonNull PowerAttributor powerAttributor,
+            @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
+            @NonNull PowerStatsStore powerStatsStore, @NonNull Clock clock) {
         mContext = context;
-        mPowerStatsExporter = powerStatsExporter;
+        mPowerAttributor = powerAttributor;
         mPowerStatsStore = powerStatsStore;
         mPowerProfile = powerProfile;
         mCpuScalingPolicies = cpuScalingPolicies;
         mClock = clock;
+
+        mPowerStatsStore.addSectionReader(new BatteryUsageStatsSection.Reader());
     }
 
     private List<PowerCalculator> getPowerCalculators() {
@@ -72,55 +73,67 @@
 
                 // Power calculators are applied in the order of registration
                 mPowerCalculators.add(new BatteryChargeCalculator());
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_CPU)) {
                     mPowerCalculators.add(
                             new CpuPowerCalculator(mCpuScalingPolicies, mPowerProfile));
                 }
                 mPowerCalculators.add(new MemoryPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new WakelockPowerCalculator(mPowerProfile));
                 if (!BatteryStats.checkWifiOnly(mContext)) {
-                    if (!mPowerStatsExporterEnabled.get(
+                    if (!mPowerAttributor.isPowerComponentSupported(
                             BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)) {
                         mPowerCalculators.add(new MobileRadioPowerCalculator(mPowerProfile));
                     }
-                    if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_PHONE)) {
+                    if (!mPowerAttributor.isPowerComponentSupported(
+                            BatteryConsumer.POWER_COMPONENT_PHONE)) {
                         mPowerCalculators.add(new PhonePowerCalculator(mPowerProfile));
                     }
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_WIFI)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_WIFI)) {
                     mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH)) {
                     mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SENSORS)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_SENSORS)) {
                     mPowerCalculators.add(new SensorPowerCalculator(
                             mContext.getSystemService(SensorManager.class)));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_GNSS)) {
                     mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_CAMERA)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_CAMERA)) {
                     mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)) {
                     mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_AUDIO)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_AUDIO)) {
                     mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_VIDEO)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_VIDEO)) {
                     mPowerCalculators.add(new VideoPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)) {
                     mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
                 }
-                if (!mPowerStatsExporterEnabled.get(
+                if (!mPowerAttributor.isPowerComponentSupported(
                         BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY)) {
                     mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
                 }
                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
-                if (!mPowerStatsExporterEnabled.get(BatteryConsumer.POWER_COMPONENT_ANY)) {
+                if (!mPowerAttributor.isPowerComponentSupported(
+                        BatteryConsumer.POWER_COMPONENT_ANY)) {
                     mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
                 }
                 mPowerCalculators.add(new UserPowerCalculator());
@@ -196,7 +209,7 @@
         final boolean includeProcessStateData = ((query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA) != 0)
                 && stats.isProcessStateDataAvailable();
-        final boolean includeVirtualUids =  ((query.getFlags()
+        final boolean includeVirtualUids = ((query.getFlags()
                 & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_VIRTUAL_UIDS) != 0);
         final double minConsumedPowerThreshold = query.getMinConsumedPowerThreshold();
 
@@ -258,10 +271,8 @@
             }
         }
 
-        if (mPowerStatsExporterEnabled.indexOfValue(true) >= 0) {
-            mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder,
-                    monotonicStartTime, monotonicEndTime);
-        }
+        mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, stats.getHistory(),
+                monotonicStartTime, monotonicEndTime);
 
         BatteryUsageStats batteryUsageStats = batteryUsageStatsBuilder.build();
         if (includeProcessStateData) {
@@ -272,6 +283,7 @@
 
     // STOPSHIP(b/229906525): remove verification before shipping
     private static boolean sErrorReported;
+
     private void verify(BatteryUsageStats stats) {
         if (sErrorReported) {
             return;
@@ -390,7 +402,7 @@
             // while the "to" timestamp is *inclusive*.
             boolean isInRange =
                     (query.getFromTimestamp() == 0 || minTime > query.getFromTimestamp())
-                    && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
+                            && (query.getToTimestamp() == 0 || maxTime <= query.getToTimestamp());
             if (!isInRange) {
                 continue;
             }
@@ -422,12 +434,4 @@
         }
         return builder.build();
     }
-
-    /**
-     * Specify whether PowerStats based attribution is supported for the specified component.
-     */
-    public void setPowerStatsExporterEnabled(int powerComponentId, boolean enabled) {
-        mPowerStatsExporterEnabled.put(powerComponentId, enabled);
-        mPowerStatsExporter.setPowerComponentEnabled(powerComponentId, enabled);
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
index b95faac..af36524 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsSection.java
@@ -19,8 +19,11 @@
 import android.os.BatteryUsageStats;
 import android.util.IndentingPrintWriter;
 
+import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 
 class BatteryUsageStatsSection extends PowerStatsSpan.Section {
@@ -38,7 +41,7 @@
     }
 
     @Override
-    void write(TypedXmlSerializer serializer) throws IOException {
+    public void write(TypedXmlSerializer serializer) throws IOException {
         mBatteryUsageStats.writeXml(serializer);
     }
 
@@ -46,4 +49,17 @@
     public void dump(IndentingPrintWriter ipw) {
         mBatteryUsageStats.dump(ipw, "");
     }
+
+    static class Reader implements PowerStatsSpan.SectionReader {
+        @Override
+        public String getType() {
+            return TYPE;
+        }
+
+        @Override
+        public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return new BatteryUsageStatsSection(BatteryUsageStats.createFromXml(parser));
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
deleted file mode 100644
index 502337c..0000000
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsLayout.java
+++ /dev/null
@@ -1,24 +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.server.power.stats;
-
-class BinaryStatePowerStatsLayout extends EnergyConsumerPowerStatsLayout {
-    BinaryStatePowerStatsLayout() {
-        addDeviceSectionUsageDuration();
-        addUidSectionUsageDuration();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
deleted file mode 100644
index 599e63d..0000000
--- a/services/core/java/com/android/server/power/stats/BinaryStatePowerStatsProcessor.java
+++ /dev/null
@@ -1,287 +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.server.power.stats;
-
-import android.annotation.IntDef;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.Process;
-
-import com.android.internal.os.PowerStats;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
-    static final int STATE_OFF = 0;
-    static final int STATE_ON = 1;
-
-    @IntDef(flag = true, prefix = {"STATE_"}, value = {
-            STATE_OFF,
-            STATE_ON,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    protected @interface BinaryState {
-    }
-
-    private final int mPowerComponentId;
-    private final PowerStatsUidResolver mUidResolver;
-    private final UsageBasedPowerEstimator mUsageBasedPowerEstimator;
-    private boolean mEnergyConsumerSupported;
-    private int mInitiatingUid = Process.INVALID_UID;
-    private @BinaryState int mLastState = STATE_OFF;
-    private long mLastStateTimestamp;
-    private long mLastUpdateTimestamp;
-
-    private PowerStats.Descriptor mDescriptor;
-    private final BinaryStatePowerStatsLayout mStatsLayout;
-    private PowerStats mPowerStats;
-    private PowerEstimationPlan mPlan;
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-
-    BinaryStatePowerStatsProcessor(int powerComponentId,
-            PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) {
-        this(powerComponentId, uidResolver, averagePowerMilliAmp,
-                new BinaryStatePowerStatsLayout());
-    }
-
-    BinaryStatePowerStatsProcessor(int powerComponentId,
-            PowerStatsUidResolver uidResolver, double averagePowerMilliAmp,
-            BinaryStatePowerStatsLayout statsLayout) {
-        mPowerComponentId = powerComponentId;
-        mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp);
-        mUidResolver = uidResolver;
-        mStatsLayout = statsLayout;
-    }
-
-    protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item);
-
-    private void ensureInitialized() {
-        if (mDescriptor != null) {
-            return;
-        }
-
-        PersistableBundle extras = new PersistableBundle();
-        mStatsLayout.toExtras(extras);
-        mDescriptor = new PowerStats.Descriptor(mPowerComponentId,
-                mStatsLayout.getDeviceStatsArrayLength(), null, 0,
-                mStatsLayout.getUidStatsArrayLength(), extras);
-        mPowerStats = new PowerStats(mDescriptor);
-        mPowerStats.stats = new long[mDescriptor.statsArrayLength];
-        mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
-    }
-
-    @Override
-    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        ensureInitialized();
-
-        // Establish a baseline at the beginning of an accumulation pass
-        mLastState = STATE_OFF;
-        mLastStateTimestamp = timestampMs;
-        mInitiatingUid = Process.INVALID_UID;
-        flushPowerStats(stats, mLastStateTimestamp);
-    }
-
-    @Override
-    void noteStateChange(PowerComponentAggregatedPowerStats stats,
-            BatteryStats.HistoryItem item) {
-        @BinaryState int state = getBinaryState(item);
-        if (state == mLastState) {
-            return;
-        }
-
-        if (state == STATE_ON) {
-            if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_START)) {
-                mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
-            }
-        } else {
-            recordUsageDuration(mPowerStats, mInitiatingUid, item.time);
-            mInitiatingUid = Process.INVALID_UID;
-            if (!mEnergyConsumerSupported) {
-                flushPowerStats(stats, item.time);
-            }
-        }
-        mLastStateTimestamp = item.time;
-        mLastState = state;
-    }
-
-    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
-        long durationMs = time - mLastStateTimestamp;
-        mStatsLayout.setUsageDuration(mPowerStats.stats,
-                mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
-
-        if (uid != Process.INVALID_UID) {
-            long[] uidStats = mPowerStats.uidStats.get(uid);
-            if (uidStats == null) {
-                uidStats = new long[mDescriptor.uidStatsArrayLength];
-                mPowerStats.uidStats.put(uid, uidStats);
-                mStatsLayout.setUidUsageDuration(uidStats, durationMs);
-            } else {
-                mStatsLayout.setUsageDuration(mPowerStats.stats,
-                        mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
-            }
-        }
-        mLastStateTimestamp = time;
-    }
-
-    void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
-            long timestampMs) {
-        ensureInitialized();
-
-        if (mLastState == STATE_ON) {
-            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
-        }
-
-        long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0);
-        if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) {
-            mEnergyConsumerSupported = true;
-            mStatsLayout.setConsumedEnergy(mPowerStats.stats, 0, consumedEnergy);
-        }
-
-        flushPowerStats(stats, timestampMs);
-    }
-
-    private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
-        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
-        stats.addPowerStats(mPowerStats, timestamp);
-
-        Arrays.fill(mPowerStats.stats, 0);
-        mPowerStats.uidStats.clear();
-        mLastUpdateTimestamp = timestamp;
-    }
-
-    private static class Intermediates {
-        public long duration;
-        public double power;
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (mLastState == STATE_ON) {
-            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
-        }
-        flushPowerStats(stats, timestampMs);
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported);
-        combineDevicePowerEstimates(stats);
-
-        List<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-
-        computeUidActivityTotals(stats, uids);
-        computeUidPowerEstimates(stats, uids);
-    }
-
-    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            PowerEstimationPlan plan, boolean energyConsumerSupported) {
-        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
-                continue;
-            }
-
-            long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
-            if (duration > 0) {
-                double power;
-                if (energyConsumerSupported) {
-                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
-                } else {
-                    power = mUsageBasedPowerEstimator.calculatePower(duration);
-                }
-                mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
-                stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
-            }
-        }
-    }
-
-    private void combineDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate estimation =
-                    mPlan.combinedDeviceStateEstimations.get(i);
-            Intermediates intermediates = new Intermediates();
-            estimation.intermediates = intermediates;
-            for (int j = estimation.deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation deviceStateEstimation =
-                        estimation.deviceStateEstimations.get(j);
-                if (!stats.getDeviceStats(mTmpDeviceStatsArray,
-                        deviceStateEstimation.stateValues)) {
-                    continue;
-                }
-                intermediates.power += mStatsLayout.getDevicePowerEstimate(mTmpDeviceStatsArray);
-            }
-        }
-    }
-
-    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats,
-            List<Integer> uids) {
-        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
-            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
-            Intermediates intermediates =
-                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-            for (int j = uids.size() - 1; j >= 0; j--) {
-                int uid = uids.get(j);
-                for (UidStateProportionalEstimate proportionalEstimate :
-                        uidStateEstimate.proportionalEstimates) {
-                    if (stats.getUidStats(mTmpUidStatsArray, uid,
-                            proportionalEstimate.stateValues)) {
-                        intermediates.duration +=
-                                mStatsLayout.getUidUsageDuration(mTmpUidStatsArray);
-                    }
-                }
-            }
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
-            List<Integer> uids) {
-        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
-            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
-            Intermediates intermediates =
-                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-            if (intermediates.duration == 0) {
-                continue;
-            }
-            List<UidStateProportionalEstimate> proportionalEstimates =
-                    uidStateEstimate.proportionalEstimates;
-            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
-                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
-                for (int k = uids.size() - 1; k >= 0; k--) {
-                    int uid = uids.get(k);
-                    if (stats.getUidStats(mTmpUidStatsArray, uid,
-                            proportionalEstimate.stateValues)) {
-                        double power = intermediates.power
-                                * mStatsLayout.getUidUsageDuration(mTmpUidStatsArray)
-                                / intermediates.duration;
-                        mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-                        stats.setUidStats(uid, proportionalEstimate.stateValues,
-                                mTmpUidStatsArray);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
index 8a5085b..539c415 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsCollector.java
@@ -28,13 +28,12 @@
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
 
 public class BluetoothPowerStatsCollector extends PowerStatsCollector {
     private static final String TAG = "BluetoothPowerStatsCollector";
@@ -43,7 +42,7 @@
 
     private static final long ENERGY_UNSPECIFIED = -1;
 
-    interface BluetoothStatsRetriever {
+    public interface BluetoothStatsRetriever {
         interface Callback {
             void onBluetoothScanTime(int uid, long scanTimeMs);
         }
@@ -54,29 +53,25 @@
                 BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback);
     }
 
-    interface Injector {
+    public interface Injector {
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         PackageManager getPackageManager();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
         BluetoothStatsRetriever getBluetoothStatsRetriever();
     }
 
     private final Injector mInjector;
 
-    private BluetoothPowerStatsLayout mLayout;
+    private com.android.server.power.stats.format.BluetoothPowerStatsLayout mLayout;
     private boolean mIsInitialized;
     private PowerStats mPowerStats;
     private long[] mDeviceStats;
     private BluetoothStatsRetriever mBluetoothStatsRetriever;
     private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
-    private int[] mEnergyConsumerIds = new int[0];
-    private long[] mLastConsumedEnergyUws;
-    private int mLastVoltageMv;
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
 
     private long mLastRxTime;
     private long mLastTxTime;
@@ -93,7 +88,7 @@
 
     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
 
-    BluetoothPowerStatsCollector(Injector injector) {
+    public BluetoothPowerStatsCollector(Injector injector) {
         super(injector.getHandler(),  injector.getPowerStatsCollectionThrottlePeriod(
                         BatteryConsumer.powerComponentIdToString(
                                 BatteryConsumer.POWER_COMPONENT_BLUETOOTH)),
@@ -122,21 +117,11 @@
             return false;
         }
 
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
         mBluetoothStatsRetriever = mInjector.getBluetoothStatsRetriever();
-        mEnergyConsumerIds =
-                mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH);
-        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
-        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
-
-        mLayout = new BluetoothPowerStatsLayout();
-        mLayout.addDeviceBluetoothControllerActivity();
-        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
-        mLayout.addDeviceSectionUsageDuration();
-        mLayout.addDeviceSectionPowerEstimate();
-        mLayout.addUidTrafficStats();
-        mLayout.addUidSectionPowerEstimate();
+        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
+        mConsumedEnergyHelper = new ConsumedEnergyHelper(mConsumedEnergyRetriever,
+                EnergyConsumerType.BLUETOOTH);
+        mLayout = new BluetoothPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount());
 
         PersistableBundle extras = new PersistableBundle();
         mLayout.toExtras(extras);
@@ -152,7 +137,7 @@
     }
 
     @Override
-    protected PowerStats collectStats() {
+    public PowerStats collectStats() {
         if (!ensureInitialized()) {
             return null;
         }
@@ -162,9 +147,7 @@
         collectBluetoothActivityInfo();
         collectBluetoothScanStats();
 
-        if (mEnergyConsumerIds.length != 0) {
-            collectEnergyConsumers();
-        }
+        mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
         return mPowerStats;
     }
@@ -296,34 +279,6 @@
         mLayout.setDeviceScanTime(mDeviceStats, totalScanTime);
     }
 
-    private void collectEnergyConsumers() {
-        int voltageMv = mVoltageSupplier.getAsInt();
-        if (voltageMv <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                    + " mV) when querying energy consumers");
-            return;
-        }
-
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        mLastVoltageMv = voltageMv;
-
-        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
-        if (energyUws == null) {
-            return;
-        }
-
-        for (int i = energyUws.length - 1; i >= 0; i--) {
-            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
-                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
-            if (energyDelta < 0) {
-                // Likely, restart of powerstats HAL
-                energyDelta = 0;
-            }
-            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
-            mLastConsumedEnergyUws[i] = energyUws[i];
-        }
-    }
-
     @Override
     protected void onUidRemoved(int uid) {
         super.onUidRemoved(uid);
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java
deleted file mode 100644
index 9358b5e..0000000
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsLayout.java
+++ /dev/null
@@ -1,143 +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.server.power.stats;
-
-import android.annotation.NonNull;
-import android.os.PersistableBundle;
-
-import com.android.internal.os.PowerStats;
-
-public class BluetoothPowerStatsLayout extends PowerStatsLayout {
-    private static final String EXTRA_DEVICE_RX_TIME_POSITION = "dt-rx";
-    private static final String EXTRA_DEVICE_TX_TIME_POSITION = "dt-tx";
-    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
-    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
-    private static final String EXTRA_UID_RX_BYTES_POSITION = "ub-rx";
-    private static final String EXTRA_UID_TX_BYTES_POSITION = "ub-tx";
-    private static final String EXTRA_UID_SCAN_TIME_POSITION = "ut-scan";
-
-    private int mDeviceRxTimePosition;
-    private int mDeviceTxTimePosition;
-    private int mDeviceIdleTimePosition;
-    private int mDeviceScanTimePosition;
-    private int mUidRxBytesPosition;
-    private int mUidTxBytesPosition;
-    private int mUidScanTimePosition;
-
-    BluetoothPowerStatsLayout() {
-    }
-
-    BluetoothPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
-        super(descriptor);
-    }
-
-    void addDeviceBluetoothControllerActivity() {
-        mDeviceRxTimePosition = addDeviceSection(1, "rx");
-        mDeviceTxTimePosition = addDeviceSection(1, "tx");
-        mDeviceIdleTimePosition = addDeviceSection(1, "idle");
-        mDeviceScanTimePosition = addDeviceSection(1, "scan", FLAG_OPTIONAL);
-    }
-
-    void addUidTrafficStats() {
-        mUidRxBytesPosition = addUidSection(1, "rx-B");
-        mUidTxBytesPosition = addUidSection(1, "tx-B");
-        mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
-    }
-
-    public void setDeviceRxTime(long[] stats, long durationMillis) {
-        stats[mDeviceRxTimePosition] = durationMillis;
-    }
-
-    public long getDeviceRxTime(long[] stats) {
-        return stats[mDeviceRxTimePosition];
-    }
-
-    public void setDeviceTxTime(long[] stats, long durationMillis) {
-        stats[mDeviceTxTimePosition] = durationMillis;
-    }
-
-    public long getDeviceTxTime(long[] stats) {
-        return stats[mDeviceTxTimePosition];
-    }
-
-    public void setDeviceIdleTime(long[] stats, long durationMillis) {
-        stats[mDeviceIdleTimePosition] = durationMillis;
-    }
-
-    public long getDeviceIdleTime(long[] stats) {
-        return stats[mDeviceIdleTimePosition];
-    }
-
-    public void setDeviceScanTime(long[] stats, long durationMillis) {
-        stats[mDeviceScanTimePosition] = durationMillis;
-    }
-
-    public long getDeviceScanTime(long[] stats) {
-        return stats[mDeviceScanTimePosition];
-    }
-
-    public void setUidRxBytes(long[] stats, long count) {
-        stats[mUidRxBytesPosition] = count;
-    }
-
-    public long getUidRxBytes(long[] stats) {
-        return stats[mUidRxBytesPosition];
-    }
-
-    public void setUidTxBytes(long[] stats, long count) {
-        stats[mUidTxBytesPosition] = count;
-    }
-
-    public long getUidTxBytes(long[] stats) {
-        return stats[mUidTxBytesPosition];
-    }
-
-    public void setUidScanTime(long[] stats, long count) {
-        stats[mUidScanTimePosition] = count;
-    }
-
-    public long getUidScanTime(long[] stats) {
-        return stats[mUidScanTimePosition];
-    }
-
-    /**
-     * Copies the elements of the stats array layout into <code>extras</code>
-     */
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
-        extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
-        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
-        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
-        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
-        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
-        extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
-    }
-
-    /**
-     * Retrieves elements of the stats array layout from <code>extras</code>
-     */
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
-        mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
-        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
-        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
-        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
-        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
-        mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
deleted file mode 100644
index 077b057..0000000
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerStatsProcessor.java
+++ /dev/null
@@ -1,304 +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.server.power.stats;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "BluetoothPowerStatsProcessor";
-
-    private final UsageBasedPowerEstimator mRxPowerEstimator;
-    private final UsageBasedPowerEstimator mTxPowerEstimator;
-    private final UsageBasedPowerEstimator mIdlePowerEstimator;
-
-    private PowerStats.Descriptor mLastUsedDescriptor;
-    private BluetoothPowerStatsLayout mStatsLayout;
-    // Sequence of steps for power estimation and intermediate results.
-    private PowerEstimationPlan mPlan;
-
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-
-    public BluetoothPowerStatsProcessor(PowerProfile powerProfile) {
-        mRxPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX));
-        mTxPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX));
-        mIdlePowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE));
-    }
-
-    private static class Intermediates {
-        /**
-         * Number of received bytes
-         */
-        public long rxBytes;
-        /**
-         * Duration of receiving
-         */
-        public long rxTime;
-        /**
-         * Estimated power for the RX state.
-         */
-        public double rxPower;
-        /**
-         * Number of transmitted bytes
-         */
-        public long txBytes;
-        /**
-         * Duration of transmitting
-         */
-        public long txTime;
-        /**
-         * Estimated power for the TX state.
-         */
-        public double txPower;
-        /**
-         * Estimated power for IDLE, SCAN states.
-         */
-        public double idlePower;
-        /**
-         * Total scan time.
-         */
-        public long scanTime;
-        /**
-         * Measured consumed energy from power monitoring hardware (micro-coulombs)
-         */
-        public long consumedEnergy;
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (stats.getPowerStatsDescriptor() == null) {
-            return;
-        }
-
-        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-            Intermediates intermediates = new Intermediates();
-            estimation.intermediates = intermediates;
-            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
-        }
-
-        double ratio = 1.0;
-        if (mStatsLayout.getEnergyConsumerCount() != 0) {
-            ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
-            if (ratio != 1) {
-                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-                    adjustDevicePowerEstimates(stats, estimation.stateValues,
-                            (Intermediates) estimation.intermediates, ratio);
-                }
-            }
-        }
-
-        combineDeviceStateEstimates();
-
-        ArrayList<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-        if (!uids.isEmpty()) {
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-        }
-        mPlan.resetIntermediates();
-    }
-
-    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
-        if (descriptor.equals(mLastUsedDescriptor)) {
-            return;
-        }
-
-        mLastUsedDescriptor = descriptor;
-        mStatsLayout = new BluetoothPowerStatsLayout(descriptor);
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-    }
-
-    /**
-     * Compute power estimates using the power profile.
-     */
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates) {
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
-            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
-        }
-
-        intermediates.rxTime = mStatsLayout.getDeviceRxTime(mTmpDeviceStatsArray);
-        intermediates.txTime = mStatsLayout.getDeviceTxTime(mTmpDeviceStatsArray);
-        intermediates.scanTime = mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray);
-        long idleTime = mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray);
-
-        intermediates.rxPower = mRxPowerEstimator.calculatePower(intermediates.rxTime);
-        intermediates.txPower = mTxPowerEstimator.calculatePower(intermediates.txTime);
-        intermediates.idlePower = mIdlePowerEstimator.calculatePower(idleTime);
-        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                intermediates.rxPower + intermediates.txPower + intermediates.idlePower);
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * Compute an adjustment ratio using the total power estimated using the power profile
-     * and the total power measured by hardware.
-     */
-    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
-        long totalConsumedEnergy = 0;
-        double totalPower = 0;
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            Intermediates intermediates =
-                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
-            totalPower += intermediates.rxPower + intermediates.txPower + intermediates.idlePower;
-            totalConsumedEnergy += intermediates.consumedEnergy;
-        }
-
-        if (totalPower == 0) {
-            return 1;
-        }
-
-        return uCtoMah(totalConsumedEnergy) / totalPower;
-    }
-
-    /**
-     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
-     * estimated power matches the measured consumed power.  We are not claiming that all
-     * averages captured in the power profile have to be off by the same percentage in reality.
-     */
-    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates, double ratio) {
-        double adjutedPower;
-        intermediates.rxPower *= ratio;
-        intermediates.txPower *= ratio;
-        intermediates.idlePower *= ratio;
-        adjutedPower = intermediates.rxPower + intermediates.txPower + intermediates.idlePower;
-
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, adjutedPower);
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * Combine power estimates before distributing them proportionally to UIDs.
-     */
-    private void combineDeviceStateEstimates() {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
-            Intermediates cdseIntermediates = new Intermediates();
-            cdse.intermediates = cdseIntermediates;
-            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
-            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation dse = deviceStateEstimations.get(j);
-                Intermediates intermediates = (Intermediates) dse.intermediates;
-                cdseIntermediates.rxTime += intermediates.rxTime;
-                cdseIntermediates.rxBytes += intermediates.rxBytes;
-                cdseIntermediates.rxPower += intermediates.rxPower;
-                cdseIntermediates.txTime += intermediates.txTime;
-                cdseIntermediates.txBytes += intermediates.txBytes;
-                cdseIntermediates.txPower += intermediates.txPower;
-                cdseIntermediates.idlePower += intermediates.idlePower;
-                cdseIntermediates.scanTime += intermediates.scanTime;
-                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
-            }
-        }
-    }
-
-    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            intermediates.rxBytes += mStatsLayout.getUidRxBytes(mTmpUidStatsArray);
-            intermediates.txBytes += mStatsLayout.getUidTxBytes(mTmpUidStatsArray);
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-
-        // Scan is more expensive than data transfer, so in the presence of large
-        // of scanning duration, blame apps according to the time they spent scanning.
-        // This may disproportionately blame apps that do a lot of scanning, which is
-        // the tread-off we are making in the absence of more detailed metrics.
-        boolean normalizeRxByScanTime = intermediates.scanTime > intermediates.rxTime;
-        boolean normalizeTxByScanTime = intermediates.scanTime > intermediates.txTime;
-
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            double power = 0;
-            if (normalizeRxByScanTime) {
-                if (intermediates.scanTime != 0) {
-                    power += intermediates.rxPower * mStatsLayout.getUidScanTime(mTmpUidStatsArray)
-                            / intermediates.scanTime;
-                }
-            } else {
-                if (intermediates.rxBytes != 0) {
-                    power += intermediates.rxPower * mStatsLayout.getUidRxBytes(mTmpUidStatsArray)
-                            / intermediates.rxBytes;
-                }
-            }
-            if (normalizeTxByScanTime) {
-                if (intermediates.scanTime != 0) {
-                    power += intermediates.txPower * mStatsLayout.getUidScanTime(mTmpUidStatsArray)
-                            / intermediates.scanTime;
-                }
-            } else {
-                if (intermediates.txBytes != 0) {
-                    power += intermediates.txPower * mStatsLayout.getUidTxBytes(mTmpUidStatsArray)
-                            / intermediates.txBytes;
-                }
-            }
-            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
index 8705bd5..0f70064 100644
--- a/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CameraPowerStatsCollector.java
@@ -19,12 +19,13 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
 public class CameraPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
 
-    CameraPowerStatsCollector(Injector injector) {
+    public CameraPowerStatsCollector(Injector injector) {
         super(injector, BatteryConsumer.POWER_COMPONENT_CAMERA,
                 BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_CAMERA),
-                EnergyConsumerType.CAMERA, /* energy consumer name */ null,
-                new BinaryStatePowerStatsLayout());
+                EnergyConsumerType.CAMERA, new BinaryStatePowerStatsLayout());
     }
 }
diff --git a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
deleted file mode 100644
index 15c3eb8..0000000
--- a/services/core/java/com/android/server/power/stats/CameraPowerStatsProcessor.java
+++ /dev/null
@@ -1,37 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-
-import com.android.internal.os.PowerProfile;
-
-public class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-    public CameraPowerStatsProcessor(PowerProfile powerProfile,
-            PowerStatsUidResolver uidResolver) {
-        super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver,
-                powerProfile.getAveragePower(PowerProfile.POWER_CAMERA));
-    }
-
-    @Override
-    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
-        return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0
-                ? STATE_ON
-                : STATE_OFF;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index b5ef67b..128f14a 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -31,11 +31,10 @@
 import com.android.internal.os.CpuScalingPolicies;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
 
 import java.io.PrintWriter;
-import java.util.Arrays;
 import java.util.Locale;
-import java.util.function.IntSupplier;
 
 /**
  * Collects snapshots of power-related system statistics.
@@ -46,7 +45,6 @@
 public class CpuPowerStatsCollector extends PowerStatsCollector {
     private static final String TAG = "CpuPowerStatsCollector";
     private static final long NANOS_PER_MILLIS = 1000000;
-    private static final long ENERGY_UNSPECIFIED = -1;
     private static final int DEFAULT_CPU_POWER_BRACKETS = 3;
     private static final int DEFAULT_CPU_POWER_BRACKETS_PER_ENERGY_CONSUMER = 2;
 
@@ -58,7 +56,6 @@
         PowerProfile getPowerProfile();
         KernelCpuStatsReader getKernelCpuStatsReader();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
 
         default int getDefaultCpuPowerBrackets() {
@@ -76,8 +73,7 @@
     private CpuScalingPolicies mCpuScalingPolicies;
     private PowerProfile mPowerProfile;
     private KernelCpuStatsReader mKernelCpuStatsReader;
-    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
     private int mDefaultCpuPowerBrackets;
     private int mDefaultCpuPowerBracketsPerEnergyConsumer;
     private long[] mCpuTimeByScalingStep;
@@ -85,15 +81,12 @@
     private long[] mTempUidStats;
     private final SparseArray<UidStats> mUidStats = new SparseArray<>();
     private boolean mIsPerUidTimeInStateSupported;
-    private int[] mCpuEnergyConsumerIds = new int[0];
     private PowerStats.Descriptor mPowerStatsDescriptor;
     // Reusable instance
     private PowerStats mCpuPowerStats;
     private CpuPowerStatsLayout mLayout;
     private long mLastUpdateTimestampNanos;
     private long mLastUpdateUptimeMillis;
-    private int mLastVoltageMv;
-    private long[] mLastConsumedEnergyUws;
 
     CpuPowerStatsCollector(Injector injector) {
         super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
@@ -115,31 +108,22 @@
         mCpuScalingPolicies = mInjector.getCpuScalingPolicies();
         mPowerProfile = mInjector.getPowerProfile();
         mKernelCpuStatsReader = mInjector.getKernelCpuStatsReader();
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
         mDefaultCpuPowerBrackets = mInjector.getDefaultCpuPowerBrackets();
         mDefaultCpuPowerBracketsPerEnergyConsumer =
                 mInjector.getDefaultCpuPowerBracketsPerEnergyConsumer();
 
         mIsPerUidTimeInStateSupported = mKernelCpuStatsReader.isSupportedFeature();
-        mCpuEnergyConsumerIds =
-                mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
-        mLastConsumedEnergyUws = new long[mCpuEnergyConsumerIds.length];
-        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+
+        mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+                EnergyConsumerType.CPU_CLUSTER);
 
         int cpuScalingStepCount = mCpuScalingPolicies.getScalingStepCount();
         mCpuTimeByScalingStep = new long[cpuScalingStepCount];
         mTempCpuTimeByScalingStep = new long[cpuScalingStepCount];
         int[] scalingStepToPowerBracketMap = initPowerBrackets();
 
-        mLayout = new CpuPowerStatsLayout();
-        mLayout.addDeviceSectionCpuTimeByScalingStep(cpuScalingStepCount);
-        mLayout.addDeviceSectionCpuTimeByCluster(mCpuScalingPolicies.getPolicies().length);
-        mLayout.addDeviceSectionUsageDuration();
-        mLayout.addDeviceSectionEnergyConsumers(mCpuEnergyConsumerIds.length);
-        mLayout.addDeviceSectionPowerEstimate();
-        mLayout.addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
-        mLayout.addUidSectionPowerEstimate();
+        mLayout = new CpuPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+                mCpuScalingPolicies.getPolicies().length, scalingStepToPowerBracketMap);
 
         PersistableBundle extras = new PersistableBundle();
         mLayout.toExtras(extras);
@@ -158,16 +142,18 @@
     private int[] initPowerBrackets() {
         if (mPowerProfile.getCpuPowerBracketCount() != PowerProfile.POWER_BRACKETS_UNSPECIFIED) {
             return initPowerBracketsFromPowerProfile();
-        } else if (mCpuEnergyConsumerIds.length == 0 || mCpuEnergyConsumerIds.length == 1) {
+        } else if (mConsumedEnergyHelper.getEnergyConsumerCount() == 0
+                || mConsumedEnergyHelper.getEnergyConsumerCount() == 1) {
             return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
-        } else if (mCpuScalingPolicies.getPolicies().length == mCpuEnergyConsumerIds.length) {
+        } else if (mCpuScalingPolicies.getPolicies().length
+                == mConsumedEnergyHelper.getEnergyConsumerCount()) {
             return initPowerBracketsByCluster(mDefaultCpuPowerBracketsPerEnergyConsumer);
         } else {
             Slog.i(TAG, "Assigning a single power brackets to each CPU_CLUSTER energy consumer."
                         + " Number of CPU clusters ("
                         + mCpuScalingPolicies.getPolicies().length
                         + ") does not match the number of energy consumers ("
-                        + mCpuEnergyConsumerIds.length + "). "
+                        + mConsumedEnergyHelper.getEnergyConsumerCount() + "). "
                         + " Using default power bucket assignment.");
             return initDefaultPowerBrackets(mDefaultCpuPowerBrackets);
         }
@@ -368,41 +354,11 @@
         }
         mLayout.setUsageDuration(mCpuPowerStats.stats, uptimeDelta);
 
-        if (mCpuEnergyConsumerIds.length != 0) {
-            collectEnergyConsumers();
-        }
+        mConsumedEnergyHelper.collectConsumedEnergy(mCpuPowerStats, mLayout);
 
         return mCpuPowerStats;
     }
 
-    private void collectEnergyConsumers() {
-        int voltageMv = mVoltageSupplier.getAsInt();
-        if (voltageMv <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                          + " mV) when querying energy consumers");
-            return;
-        }
-
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        mLastVoltageMv = voltageMv;
-
-        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mCpuEnergyConsumerIds);
-        if (energyUws == null) {
-            return;
-        }
-
-        for (int i = energyUws.length - 1; i >= 0; i--) {
-            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
-                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
-            if (energyDelta < 0) {
-                // Likely, restart of powerstats HAL
-                energyDelta = 0;
-            }
-            mLayout.setConsumedEnergy(mCpuPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
-            mLastConsumedEnergyUws[i] = energyUws[i];
-        }
-    }
-
     @VisibleForNative
     interface KernelCpuStatsCallback {
         @Keep // Called from native
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
deleted file mode 100644
index 2a02bd0..0000000
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
+++ /dev/null
@@ -1,178 +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.server.power.stats;
-
-import android.os.PersistableBundle;
-
-/**
- * Captures the positions and lengths of sections of the stats array, such as time-in-state,
- * power usage estimates etc.
- */
-public class CpuPowerStatsLayout extends PowerStatsLayout {
-    private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
-    private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
-    private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
-    private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
-    private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
-    private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
-
-    private int mDeviceCpuTimeByScalingStepPosition;
-    private int mDeviceCpuTimeByScalingStepCount;
-    private int mDeviceCpuTimeByClusterPosition;
-    private int mDeviceCpuTimeByClusterCount;
-
-    private int mUidPowerBracketsPosition;
-    private int mUidPowerBracketCount;
-
-    private int[] mScalingStepToPowerBracketMap;
-
-    /**
-     * Declare that the stats array has a section capturing CPU time per scaling step
-     */
-    public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
-        mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps");
-        mDeviceCpuTimeByScalingStepCount = scalingStepCount;
-    }
-
-    public int getCpuScalingStepCount() {
-        return mDeviceCpuTimeByScalingStepCount;
-    }
-
-    /**
-     * Saves the time duration in the <code>stats</code> element
-     * corresponding to the CPU scaling <code>state</code>.
-     */
-    public void setTimeByScalingStep(long[] stats, int step, long value) {
-        stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
-    }
-
-    /**
-     * Extracts the time duration from the <code>stats</code> element
-     * corresponding to the CPU scaling <code>step</code>.
-     */
-    public long getTimeByScalingStep(long[] stats, int step) {
-        return stats[mDeviceCpuTimeByScalingStepPosition + step];
-    }
-
-    /**
-     * Declare that the stats array has a section capturing CPU time in each cluster
-     */
-    public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
-        mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters");
-        mDeviceCpuTimeByClusterCount = clusterCount;
-    }
-
-    public int getCpuClusterCount() {
-        return mDeviceCpuTimeByClusterCount;
-    }
-
-    /**
-     * Saves the time duration in the <code>stats</code> element
-     * corresponding to the CPU <code>cluster</code>.
-     */
-    public void setTimeByCluster(long[] stats, int cluster, long value) {
-        stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
-    }
-
-    /**
-     * Extracts the time duration from the <code>stats</code> element
-     * corresponding to the CPU <code>cluster</code>.
-     */
-    public long getTimeByCluster(long[] stats, int cluster) {
-        return stats[mDeviceCpuTimeByClusterPosition + cluster];
-    }
-
-    /**
-     * Declare that the UID stats array has a section capturing CPU time per power bracket.
-     */
-    public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
-        mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
-        updatePowerBracketCount();
-        mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time");
-    }
-
-    private void updatePowerBracketCount() {
-        mUidPowerBracketCount = 1;
-        for (int bracket : mScalingStepToPowerBracketMap) {
-            if (bracket >= mUidPowerBracketCount) {
-                mUidPowerBracketCount = bracket + 1;
-            }
-        }
-    }
-
-    public int[] getScalingStepToPowerBracketMap() {
-        return mScalingStepToPowerBracketMap;
-    }
-
-    public int getCpuPowerBracketCount() {
-        return mUidPowerBracketCount;
-    }
-
-    /**
-     * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
-     */
-    public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
-        stats[mUidPowerBracketsPosition + bracket] = value;
-    }
-
-    /**
-     * Extracts the time in <code>bracket</code> from a UID stats array.
-     */
-    public long getUidTimeByPowerBracket(long[] stats, int bracket) {
-        return stats[mUidPowerBracketsPosition + bracket];
-    }
-
-    /**
-     * Copies the elements of the stats array layout into <code>extras</code>
-     */
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
-                mDeviceCpuTimeByScalingStepPosition);
-        extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
-                mDeviceCpuTimeByScalingStepCount);
-        extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
-                mDeviceCpuTimeByClusterPosition);
-        extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
-                mDeviceCpuTimeByClusterCount);
-        extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
-        putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
-                mScalingStepToPowerBracketMap);
-    }
-
-    /**
-     * Retrieves elements of the stats array layout from <code>extras</code>
-     */
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mDeviceCpuTimeByScalingStepPosition =
-                extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
-        mDeviceCpuTimeByScalingStepCount =
-                extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
-        mDeviceCpuTimeByClusterPosition =
-                extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
-        mDeviceCpuTimeByClusterCount =
-                extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
-        mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
-        mScalingStepToPowerBracketMap =
-                getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
-        if (mScalingStepToPowerBracketMap == null) {
-            mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
-        }
-        updatePowerBracketCount();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
deleted file mode 100644
index 6da0a8f..0000000
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
+++ /dev/null
@@ -1,489 +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.server.power.stats;
-
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.internal.os.CpuScalingPolicies;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-public class CpuPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "CpuPowerStatsProcessor";
-
-    private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
-    private static final int UNKNOWN = -1;
-
-    private final CpuScalingPolicies mCpuScalingPolicies;
-    // Number of CPU core clusters
-    private final int mCpuClusterCount;
-    // Total number of CPU scaling steps across all clusters
-    private final int mCpuScalingStepCount;
-    // Map of scaling step to the corresponding core cluster mScalingStepToCluster[step]->cluster
-    private final int[] mScalingStepToCluster;
-    // Average power consumed by the CPU when it is powered up (per power_profile.xml)
-    private final double mPowerMultiplierForCpuActive;
-    // Average power consumed by each cluster when it is powered up (per power_profile.xml)
-    private final double[] mPowerMultipliersByCluster;
-    // Average power consumed by each scaling step when running code (per power_profile.xml)
-    private final double[] mPowerMultipliersByScalingStep;
-    // A map used to combine energy consumers into a smaller set, in case power brackets
-    // are defined in a way that does not allow an unambiguous mapping of energy consumers to
-    // brackets
-    private int[] mEnergyConsumerToCombinedEnergyConsumerMap;
-    // A map of combined energy consumers to the corresponding collections of power brackets.
-    // For example, if there are two CPU_CLUSTER rails and each maps to three brackets,
-    // this map will look like this:
-    //     0 : [0, 1, 2]
-    //     1 : [3, 4, 5]
-    private int[][] mCombinedEnergyConsumerToPowerBracketMap;
-
-    // Cached reference to a PowerStats descriptor. Almost never changes in practice,
-    // helping to avoid reparsing the descriptor for every PowerStats span.
-    private PowerStats.Descriptor mLastUsedDescriptor;
-    // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
-    // mLastUsedDescriptor changes
-    private CpuPowerStatsLayout mStatsLayout;
-    // Sequence of steps for power estimation and intermediate results.
-    private PowerEstimationPlan mPlan;
-
-    // Temp array for retrieval of device power stats, to avoid repeated allocations
-    private long[] mTmpDeviceStatsArray;
-    // Temp array for retrieval of UID power stats, to avoid repeated allocations
-    private long[] mTmpUidStatsArray;
-
-    public CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
-        mCpuScalingPolicies = scalingPolicies;
-        mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
-        mScalingStepToCluster = new int[mCpuScalingStepCount];
-        mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount];
-
-        int step = 0;
-        int[] policies = scalingPolicies.getPolicies();
-        mCpuClusterCount = policies.length;
-        mPowerMultipliersByCluster = new double[mCpuClusterCount];
-        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
-            int policy = policies[cluster];
-            mPowerMultipliersByCluster[cluster] =
-                    powerProfile.getAveragePowerForCpuScalingPolicy(policy) / HOUR_IN_MILLIS;
-            int[] frequencies = scalingPolicies.getFrequencies(policy);
-            for (int i = 0; i < frequencies.length; i++) {
-                mScalingStepToCluster[step] = cluster;
-                mPowerMultipliersByScalingStep[step] =
-                        powerProfile.getAveragePowerForCpuScalingStep(policy, i) / HOUR_IN_MILLIS;
-                step++;
-            }
-        }
-        mPowerMultiplierForCpuActive =
-                powerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE) / HOUR_IN_MILLIS;
-    }
-
-    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
-        if (descriptor.equals(mLastUsedDescriptor)) {
-            return;
-        }
-
-        mLastUsedDescriptor = descriptor;
-        mStatsLayout = new CpuPowerStatsLayout();
-        mStatsLayout.fromExtras(descriptor.extras);
-
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-    }
-
-    /**
-     * Temporary struct to capture intermediate results of power estimation.
-     */
-    private static final class Intermediates {
-        public long uptime;
-        // Sum of all time-in-step values, combining all time-in-step durations across all cores.
-        public long cumulativeTime;
-        // CPU activity durations per cluster
-        public long[] timeByCluster;
-        // Sums of time-in-step values, aggregated by cluster, combining all cores in the cluster.
-        public long[] cumulativeTimeByCluster;
-        public long[] timeByScalingStep;
-        public double[] powerByCluster;
-        public double[] powerByScalingStep;
-        public long[] powerByEnergyConsumer;
-    }
-
-    /**
-     * Temporary struct to capture intermediate results of power estimation.
-     */
-    private static class DeviceStatsIntermediates {
-        public double power;
-        public long[] timeByBracket;
-        public double[] powerByBracket;
-    }
-
-    @Override
-    public void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (stats.getPowerStatsDescriptor() == null) {
-            return;
-        }
-
-        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-            if (mStatsLayout.getEnergyConsumerCount() != 0) {
-                initEnergyConsumerToPowerBracketMaps();
-            }
-        }
-
-        Intermediates intermediates = new Intermediates();
-
-        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
-        if (cpuScalingStepCount != mCpuScalingStepCount) {
-            Log.e(TAG, "Mismatched CPU scaling step count in PowerStats: " + cpuScalingStepCount
-                       + ", expected: " + mCpuScalingStepCount);
-            return;
-        }
-
-        int clusterCount = mStatsLayout.getCpuClusterCount();
-        if (clusterCount != mCpuClusterCount) {
-            Log.e(TAG, "Mismatched CPU cluster count in PowerStats: " + clusterCount
-                       + ", expected: " + mCpuClusterCount);
-            return;
-        }
-
-        computeTotals(stats, intermediates);
-        if (intermediates.cumulativeTime == 0) {
-            return;
-        }
-
-        estimatePowerByScalingStep(intermediates);
-        estimatePowerByDeviceState(stats, intermediates);
-        combineDeviceStateEstimates();
-
-        ArrayList<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-        if (!uids.isEmpty()) {
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-        }
-        mPlan.resetIntermediates();
-    }
-
-    /*
-     * Populate data structures (two maps) needed to use power rail data, aka energy consumers,
-     * to attribute power usage to apps.
-     *
-     * At this point, the algorithm covers only the most basic cases:
-     * - Each cluster is mapped to unique power brackets (possibly multiple for each cluster):
-     *          CL_0: [bracket0, bracket1]
-     *          CL_1: [bracket3]
-     *      In this case, the consumed energy is distributed  to the corresponding brackets
-     *      proportionally.
-     * - Brackets span multiple clusters:
-     *          CL_0: [bracket0, bracket1]
-     *          CL_1: [bracket1, bracket2]
-     *          CL_2: [bracket3, bracket4]
-     *      In this case, we combine energy consumers into groups unambiguously mapped to
-     *      brackets. In the above example, consumed energy for CL_0 and CL_1 will be combined
-     *      because they both map to the same power bracket (bracket1):
-     *          (CL_0+CL_1): [bracket0, bracket1, bracket2]
-     *          CL_2: [bracket3, bracket4]
-     */
-    private void initEnergyConsumerToPowerBracketMaps() {
-        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
-        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
-
-        mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
-        mCombinedEnergyConsumerToPowerBracketMap = new int[energyConsumerCount][];
-
-        int[] policies = mCpuScalingPolicies.getPolicies();
-        if (energyConsumerCount == policies.length) {
-            int[] scalingStepToPowerBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
-            ArraySet<Integer>[] clusterToBrackets = new ArraySet[policies.length];
-            int step = 0;
-            for (int cluster = 0; cluster < policies.length; cluster++) {
-                int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]);
-                clusterToBrackets[cluster] = new ArraySet<>(freqs.length);
-                for (int j = 0; j < freqs.length; j++) {
-                    clusterToBrackets[cluster].add(scalingStepToPowerBracketMap[step++]);
-                }
-            }
-
-            ArraySet<Integer>[] combinedEnergyConsumers = new ArraySet[policies.length];
-            int combinedEnergyConsumersCount = 0;
-
-            for (int cluster = 0; cluster < clusterToBrackets.length; cluster++) {
-                int combineWith = UNKNOWN;
-                for (int i = 0; i < combinedEnergyConsumersCount; i++) {
-                    if (containsAny(combinedEnergyConsumers[i], clusterToBrackets[cluster])) {
-                        combineWith = i;
-                        break;
-                    }
-                }
-                if (combineWith != UNKNOWN) {
-                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = combineWith;
-                    combinedEnergyConsumers[combineWith].addAll(clusterToBrackets[cluster]);
-                } else {
-                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] =
-                            combinedEnergyConsumersCount;
-                    combinedEnergyConsumers[combinedEnergyConsumersCount++] =
-                            clusterToBrackets[cluster];
-                }
-            }
-
-            for (int i = combinedEnergyConsumers.length - 1; i >= 0; i--) {
-                mCombinedEnergyConsumerToPowerBracketMap[i] =
-                        new int[combinedEnergyConsumers[i].size()];
-                for (int j = combinedEnergyConsumers[i].size() - 1; j >= 0; j--) {
-                    mCombinedEnergyConsumerToPowerBracketMap[i][j] =
-                            combinedEnergyConsumers[i].valueAt(j);
-                }
-            }
-        } else {
-            // All CPU cluster energy consumers are combined into one, which is
-            // distributed proportionally to all power brackets.
-            int[] map = new int[powerBracketCount];
-            for (int i = 0; i < map.length; i++) {
-                map[i] = i;
-            }
-            mCombinedEnergyConsumerToPowerBracketMap[0] = map;
-        }
-    }
-
-    private static boolean containsAny(ArraySet<Integer> set1, ArraySet<Integer> set2) {
-        for (int i = 0; i < set2.size(); i++) {
-            if (set1.contains(set2.valueAt(i))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void computeTotals(PowerComponentAggregatedPowerStats stats,
-            Intermediates intermediates) {
-        intermediates.timeByScalingStep = new long[mCpuScalingStepCount];
-        intermediates.timeByCluster = new long[mCpuClusterCount];
-        intermediates.cumulativeTimeByCluster = new long[mCpuClusterCount];
-
-        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
-        for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(i);
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
-                continue;
-            }
-
-            intermediates.uptime += mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
-
-            for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
-                intermediates.timeByCluster[cluster] +=
-                        mStatsLayout.getTimeByCluster(mTmpDeviceStatsArray, cluster);
-            }
-
-            for (int step = 0; step < mCpuScalingStepCount; step++) {
-                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
-                intermediates.cumulativeTime += timeInStep;
-                intermediates.timeByScalingStep[step] += timeInStep;
-                intermediates.cumulativeTimeByCluster[mScalingStepToCluster[step]] += timeInStep;
-            }
-        }
-    }
-
-    private void estimatePowerByScalingStep(Intermediates intermediates) {
-        // CPU consumes some power when it's on - no matter which cores are running.
-        double cpuActivePower = mPowerMultiplierForCpuActive * intermediates.uptime;
-
-        // Additionally, every cluster consumes some power when any of its cores are running
-        intermediates.powerByCluster = new double[mCpuClusterCount];
-        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
-            intermediates.powerByCluster[cluster] =
-                    mPowerMultipliersByCluster[cluster] * intermediates.timeByCluster[cluster];
-        }
-
-        // Finally, additional power is consumed depending on the frequency scaling
-        intermediates.powerByScalingStep = new double[mCpuScalingStepCount];
-        for (int step = 0; step < mCpuScalingStepCount; step++) {
-            int cluster = mScalingStepToCluster[step];
-
-            double power;
-
-            // Distribute base power proportionally
-            power = cpuActivePower * intermediates.timeByScalingStep[step]
-                    / intermediates.cumulativeTime;
-
-            // Distribute per-cluster power proportionally
-            long cumulativeTimeInCluster = intermediates.cumulativeTimeByCluster[cluster];
-            if (cumulativeTimeInCluster != 0) {
-                power += intermediates.powerByCluster[cluster]
-                         * intermediates.timeByScalingStep[step]
-                         / cumulativeTimeInCluster;
-            }
-
-            power += mPowerMultipliersByScalingStep[step] * intermediates.timeByScalingStep[step];
-
-            intermediates.powerByScalingStep[step] = power;
-        }
-    }
-
-    private void estimatePowerByDeviceState(PowerComponentAggregatedPowerStats stats,
-            Intermediates intermediates) {
-        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
-        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
-        int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
-        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
-        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
-        for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
-            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
-            deviceStateEstimation.intermediates = new DeviceStatsIntermediates();
-            DeviceStatsIntermediates deviceStatsIntermediates =
-                    (DeviceStatsIntermediates) deviceStateEstimation.intermediates;
-            deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
-            deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
-
-            stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
-            for (int step = 0; step < cpuScalingStepCount; step++) {
-                if (intermediates.timeByScalingStep[step] == 0) {
-                    continue;
-                }
-
-                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
-                double stepPower = intermediates.powerByScalingStep[step] * timeInStep
-                                   / intermediates.timeByScalingStep[step];
-
-                int bracket = scalingStepToBracketMap[step];
-                deviceStatsIntermediates.timeByBracket[bracket] += timeInStep;
-                deviceStatsIntermediates.powerByBracket[bracket] += stepPower;
-            }
-
-            if (energyConsumerCount != 0) {
-                adjustEstimatesUsingEnergyConsumers(intermediates, deviceStatsIntermediates);
-            }
-
-            double power = 0;
-            for (int i = deviceStatsIntermediates.powerByBracket.length - 1; i >= 0; i--) {
-                power += deviceStatsIntermediates.powerByBracket[i];
-            }
-            deviceStatsIntermediates.power = power;
-            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
-            stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
-        }
-    }
-
-    private void adjustEstimatesUsingEnergyConsumers(
-            Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
-        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
-        if (energyConsumerCount == 0) {
-            return;
-        }
-
-        if (intermediates.powerByEnergyConsumer == null) {
-            intermediates.powerByEnergyConsumer = new long[energyConsumerCount];
-        } else {
-            Arrays.fill(intermediates.powerByEnergyConsumer, 0);
-        }
-        for (int i = 0; i < energyConsumerCount; i++) {
-            intermediates.powerByEnergyConsumer[mEnergyConsumerToCombinedEnergyConsumerMap[i]] +=
-                    mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
-        }
-
-        for (int combinedConsumer = mCombinedEnergyConsumerToPowerBracketMap.length - 1;
-                combinedConsumer >= 0; combinedConsumer--) {
-            int[] combinedEnergyConsumerToPowerBracketMap =
-                    mCombinedEnergyConsumerToPowerBracketMap[combinedConsumer];
-            if (combinedEnergyConsumerToPowerBracketMap == null) {
-                continue;
-            }
-
-            double consumedEnergy = uCtoMah(intermediates.powerByEnergyConsumer[combinedConsumer]);
-
-            double totalModeledPower = 0;
-            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
-                totalModeledPower += deviceStatsIntermediates.powerByBracket[bracket];
-            }
-            if (totalModeledPower == 0) {
-                continue;
-            }
-
-            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
-                deviceStatsIntermediates.powerByBracket[bracket] =
-                        consumedEnergy * deviceStatsIntermediates.powerByBracket[bracket]
-                        / totalModeledPower;
-            }
-        }
-    }
-
-    private void combineDeviceStateEstimates() {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
-            DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates();
-            cdse.intermediates = cdseIntermediates;
-            int bracketCount = mStatsLayout.getCpuPowerBracketCount();
-            cdseIntermediates.timeByBracket = new long[bracketCount];
-            cdseIntermediates.powerByBracket = new double[bracketCount];
-            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
-            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation dse = deviceStateEstimations.get(j);
-                DeviceStatsIntermediates intermediates =
-                        (DeviceStatsIntermediates) dse.intermediates;
-                cdseIntermediates.power += intermediates.power;
-                for (int k = 0; k < bracketCount; k++) {
-                    cdseIntermediates.timeByBracket[k] += intermediates.timeByBracket[k];
-                    cdseIntermediates.powerByBracket[k] += intermediates.powerByBracket[k];
-                }
-            }
-        }
-    }
-
-    private void estimateUidPowerConsumption(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        CombinedDeviceStateEstimate combinedDeviceStateEstimate =
-                uidStateEstimate.combinedDeviceStateEstimate;
-        DeviceStatsIntermediates cdsIntermediates =
-                (DeviceStatsIntermediates) combinedDeviceStateEstimate.intermediates;
-        for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
-            UidStateProportionalEstimate proportionalEstimate =
-                    uidStateEstimate.proportionalEstimates.get(i);
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            double power = 0;
-            for (int bracket = 0; bracket < mStatsLayout.getCpuPowerBracketCount(); bracket++) {
-                if (cdsIntermediates.timeByBracket[bracket] == 0) {
-                    continue;
-                }
-
-                long timeInBracket = mStatsLayout.getUidTimeByPowerBracket(mTmpUidStatsArray,
-                        bracket);
-                if (timeInBracket == 0) {
-                    continue;
-                }
-
-                power += cdsIntermediates.powerByBracket[bracket] * timeInBracket
-                            / cdsIntermediates.timeByBracket[bracket];
-            }
-
-            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
index 5b7467e..c1f2ae8 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
@@ -62,7 +62,7 @@
                     builder.getAggregateBatteryConsumerBuilder(
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
             for (int i = 0; i < customEnergyConsumerPowerMah.length; i++) {
-                deviceBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+                deviceBatteryConsumerBuilder.setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
                         customEnergyConsumerPowerMah[i]);
             }
@@ -72,7 +72,7 @@
                     builder.getAggregateBatteryConsumerBuilder(
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
             for (int i = 0; i < totalAppPowerMah.length; i++) {
-                appsBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
+                appsBatteryConsumerBuilder.setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
                         totalAppPowerMah[i]);
             }
@@ -96,7 +96,7 @@
                 newTotalPowerMah = totalPowerMah;
             }
             for (int i = 0; i < customEnergyConsumerPowerMah.length; i++) {
-                app.setConsumedPowerForCustomComponent(
+                app.setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
                         customEnergyConsumerPowerMah[i]);
                 if (!app.isVirtualUid()) {
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
index 4bfe442..c1c7e12 100644
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsCollector.java
@@ -19,6 +19,8 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -29,7 +31,8 @@
     private final EnergyConsumerPowerStatsCollector.Injector mInjector;
     private List<EnergyConsumerPowerStatsCollector> mCollectors;
 
-    CustomEnergyConsumerPowerStatsCollector(EnergyConsumerPowerStatsCollector.Injector injector) {
+    public CustomEnergyConsumerPowerStatsCollector(
+            EnergyConsumerPowerStatsCollector.Injector injector) {
         super(injector.getHandler(), 0, injector.getUidResolver(), injector.getClock());
         mInjector = injector;
     }
@@ -46,7 +49,8 @@
         for (int i = 0; i < energyConsumerIds.length; i++) {
             String name = retriever.getEnergyConsumerName(energyConsumerIds[i]);
             EnergyConsumerPowerStatsCollector collector = new EnergyConsumerPowerStatsCollector(
-                    mInjector, powerComponentId++, name, energyConsumerIds[i], sLayout);
+                    mInjector, powerComponentId++, name, EnergyConsumerType.OTHER,
+                    energyConsumerIds[i], sLayout);
             collector.setEnabled(true);
             collector.addConsumer(this::deliverStats);
             mCollectors.add(collector);
diff --git a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
deleted file mode 100644
index a86242a..0000000
--- a/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerStatsProcessor.java
+++ /dev/null
@@ -1,83 +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.server.power.stats;
-
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
-    private static final EnergyConsumerPowerStatsLayout sLayout =
-            new EnergyConsumerPowerStatsLayout();
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-    private PowerEstimationPlan mPlan;
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        computeDevicePowerEstimates(stats);
-
-        List<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-
-        if (!uids.isEmpty()) {
-            computeUidPowerEstimates(stats, uids);
-        }
-    }
-
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
-                continue;
-            }
-
-            sLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                    uCtoMah(sLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0)));
-            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
-            List<Integer> uids) {
-        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
-            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
-            List<UidStateProportionalEstimate> proportionalEstimates =
-                    uidStateEstimate.proportionalEstimates;
-            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
-                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
-                for (int k = uids.size() - 1; k >= 0; k--) {
-                    int uid = uids.get(k);
-                    if (stats.getUidStats(mTmpUidStatsArray, uid,
-                            proportionalEstimate.stateValues)) {
-                        sLayout.setUidPowerEstimate(mTmpUidStatsArray,
-                                uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0)));
-                        stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
index 79fbe8e..1d2e388 100644
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsCollector.java
@@ -16,78 +16,57 @@
 
 package com.android.server.power.stats;
 
-import android.hardware.power.stats.EnergyConsumerAttribution;
-import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.Handler;
 import android.os.PersistableBundle;
-import android.util.Slog;
-import android.util.SparseLongArray;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
-
-import java.util.function.IntSupplier;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
 
 public class EnergyConsumerPowerStatsCollector extends PowerStatsCollector {
-    private static final String TAG = "EnergyConsumerPowerStatsCollector";
 
-    private static final long ENERGY_UNSPECIFIED = -1;
-
-    interface Injector {
+    public interface Injector {
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
     }
 
+    private static final int UNSPECIFIED = -1;
+
     private final Injector mInjector;
     private final int mPowerComponentId;
     private final String mPowerComponentName;
+    private final int mEnergyConsumerId;
     private final int mEnergyConsumerType;
-    private final String mEnergyConsumerName;
 
     private final EnergyConsumerPowerStatsLayout mLayout;
     private boolean mIsInitialized;
 
     private PowerStats mPowerStats;
-    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
-    private int[] mEnergyConsumerIds;
-    private long mLastConsumedEnergyUws = ENERGY_UNSPECIFIED;
-    private SparseLongArray mLastConsumerEnergyPerUid = new SparseLongArray();
-    private int mLastVoltageMv;
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
     private long mLastUpdateTimestamp;
-    private boolean mFirstCollection = true;
 
     EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
             String powerComponentName, @EnergyConsumerType int energyConsumerType,
-            String energyConsumerName, EnergyConsumerPowerStatsLayout statsLayout) {
-        super(injector.getHandler(),
-                injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
-                injector.getUidResolver(), injector.getClock());
-        mInjector = injector;
-        mPowerComponentId = powerComponentId;
-        mPowerComponentName = powerComponentName;
-        mEnergyConsumerType = energyConsumerType;
-        mEnergyConsumerName = energyConsumerName;
-        mLayout = statsLayout;
+            EnergyConsumerPowerStatsLayout statsLayout) {
+        this(injector, powerComponentId, powerComponentName, energyConsumerType, UNSPECIFIED,
+                statsLayout);
     }
 
     EnergyConsumerPowerStatsCollector(Injector injector, int powerComponentId,
-            String powerComponentName, int energyConsumerId,
-            EnergyConsumerPowerStatsLayout statsLayout) {
+            String powerComponentName, @EnergyConsumerType int energyConsumerType,
+            int energyConsumerId, EnergyConsumerPowerStatsLayout statsLayout) {
         super(injector.getHandler(),
                 injector.getPowerStatsCollectionThrottlePeriod(powerComponentName),
                 injector.getUidResolver(), injector.getClock());
         mInjector = injector;
         mPowerComponentId = powerComponentId;
         mPowerComponentName = powerComponentName;
-        mEnergyConsumerIds = new int[]{energyConsumerId};
-        mEnergyConsumerType = EnergyConsumerType.OTHER;
-        mEnergyConsumerName = null;
+        mEnergyConsumerId = energyConsumerId;
+        mEnergyConsumerType = energyConsumerType;
         mLayout = statsLayout;
     }
 
@@ -100,12 +79,14 @@
             return false;
         }
 
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
-        if (mEnergyConsumerIds == null) {
-            mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(mEnergyConsumerType,
-                    mEnergyConsumerName);
+        if (mEnergyConsumerId != UNSPECIFIED) {
+            mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+                    mEnergyConsumerId, true /* perUidAttribution */);
+        } else {
+            mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+                    mEnergyConsumerType);
         }
+
         PersistableBundle extras = new PersistableBundle();
         mLayout.toExtras(extras);
         PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
@@ -124,85 +105,15 @@
             return null;
         }
 
-        if (mEnergyConsumerIds.length == 0) {
-            return null;
-        }
-
-        int voltageMv = mVoltageSupplier.getAsInt();
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        if (averageVoltage <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                    + " mV) when querying energy consumers");
-            return null;
-        }
-
-        mLastVoltageMv = voltageMv;
-
-        EnergyConsumerResult[] energy =
-                    mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds);
-        long consumedEnergy = 0;
-        if (energy != null) {
-            for (int i = energy.length - 1; i >= 0; i--) {
-                if (energy[i].energyUWs != ENERGY_UNSPECIFIED) {
-                    consumedEnergy += energy[i].energyUWs;
-                }
-            }
-        }
-
-        long energyDelta = mLastConsumedEnergyUws != ENERGY_UNSPECIFIED
-                ? consumedEnergy - mLastConsumedEnergyUws : 0;
-        mLastConsumedEnergyUws = consumedEnergy;
-        if (energyDelta < 0) {
-            // Likely, restart of powerstats HAL
-            energyDelta = 0;
-        }
-
-        if (energyDelta == 0 && !mFirstCollection) {
-            return null;
-        }
-
-        mLayout.setConsumedEnergy(mPowerStats.stats, 0, uJtoUc(energyDelta, averageVoltage));
-
         mPowerStats.uidStats.clear();
-        if (energy != null) {
-            for (int i = energy.length - 1; i >= 0; i--) {
-                EnergyConsumerAttribution[] perUid = energy[i].attribution;
-                if (perUid == null) {
-                    continue;
-                }
 
-                for (EnergyConsumerAttribution attribution : perUid) {
-                    int uid = mUidResolver.mapUid(attribution.uid);
-                    long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
-                    mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
-                    if (lastEnergy == ENERGY_UNSPECIFIED) {
-                        continue;
-                    }
-                    long deltaEnergy = attribution.energyUWs - lastEnergy;
-                    if (deltaEnergy <= 0) {
-                        continue;
-                    }
-                    long[] uidStats = mPowerStats.uidStats.get(uid);
-                    if (uidStats == null) {
-                        uidStats = new long[mLayout.getUidStatsArrayLength()];
-                        mPowerStats.uidStats.put(uid, uidStats);
-                    }
-
-                    mLayout.setUidConsumedEnergy(uidStats, 0,
-                            mLayout.getUidConsumedEnergy(uidStats, 0)
-                                    + uJtoUc(deltaEnergy, averageVoltage));
-                }
-            }
+        if (!mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout)) {
+            return null;
         }
+
         long timestamp = mClock.elapsedRealtime();
         mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
         mLastUpdateTimestamp = timestamp;
-        mFirstCollection = false;
         return mPowerStats;
     }
-
-    @Override
-    protected void onUidRemoved(int uid) {
-        mLastConsumerEnergyPerUid.delete(uid);
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
deleted file mode 100644
index 8430f56..0000000
--- a/services/core/java/com/android/server/power/stats/EnergyConsumerPowerStatsLayout.java
+++ /dev/null
@@ -1,33 +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.server.power.stats;
-
-class EnergyConsumerPowerStatsLayout extends PowerStatsLayout {
-    EnergyConsumerPowerStatsLayout() {
-        // Add a section for consumed energy, even if the specific device does not
-        // have support EnergyConsumers.  This is done to guarantee format compatibility between
-        // PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
-        addDeviceSectionEnergyConsumers(1);
-        addDeviceSectionPowerEstimate();
-
-        // Allocate a cell for per-UID consumed energy attribution. We won't know whether the
-        // corresponding energy consumer does per-UID attribution until we get data from
-        // PowerStatsService.
-        addUidSectionEnergyConsumers(1);
-        addUidSectionPowerEstimate();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
deleted file mode 100644
index f7216c9..0000000
--- a/services/core/java/com/android/server/power/stats/FlashlightPowerStatsProcessor.java
+++ /dev/null
@@ -1,37 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-
-import com.android.internal.os.PowerProfile;
-
-public class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-    public FlashlightPowerStatsProcessor(PowerProfile powerProfile,
-            PowerStatsUidResolver uidResolver) {
-        super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver,
-                powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT));
-    }
-
-    @Override
-    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
-        return (item.states2 & BatteryStats.HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0
-                ? STATE_ON
-                : STATE_OFF;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
index 168a874..c1b4c31 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/GnssPowerStatsCollector.java
@@ -19,12 +19,13 @@
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 
+import com.android.server.power.stats.format.GnssPowerStatsLayout;
+
 public class GnssPowerStatsCollector extends EnergyConsumerPowerStatsCollector {
 
-    GnssPowerStatsCollector(Injector injector) {
+    public GnssPowerStatsCollector(Injector injector) {
         super(injector, BatteryConsumer.POWER_COMPONENT_GNSS,
                 BatteryConsumer.powerComponentIdToString(BatteryConsumer.POWER_COMPONENT_GNSS),
-                EnergyConsumerType.GNSS, /* energy consumer name */ null,
-                new GnssPowerStatsLayout());
+                EnergyConsumerType.GNSS, new GnssPowerStatsLayout());
     }
 }
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
deleted file mode 100644
index 9a1317d..0000000
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsLayout.java
+++ /dev/null
@@ -1,65 +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.server.power.stats;
-
-import android.location.GnssSignalQuality;
-import android.os.PersistableBundle;
-
-class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
-    private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig";
-    private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig";
-
-    private int mDeviceSignalLevelTimePosition;
-    private int mUidSignalLevelTimePosition;
-
-    GnssPowerStatsLayout() {
-        mDeviceSignalLevelTimePosition = addDeviceSection(
-                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
-        mUidSignalLevelTimePosition = addUidSection(
-                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
-    }
-
-    @Override
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION);
-        mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION);
-    }
-
-    @Override
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition);
-        extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition);
-    }
-
-    public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
-        stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis;
-    }
-
-    public long getDeviceSignalLevelTime(long[] stats, int signalLevel) {
-        return stats[mDeviceSignalLevelTimePosition + signalLevel];
-    }
-
-    public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
-        stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis;
-    }
-
-    public long getUidSignalLevelTime(long[] stats, int signalLevel) {
-        return stats[mUidSignalLevelTimePosition + signalLevel];
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
deleted file mode 100644
index 572bde9..0000000
--- a/services/core/java/com/android/server/power/stats/GnssPowerStatsProcessor.java
+++ /dev/null
@@ -1,143 +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.server.power.stats;
-
-import android.location.GnssSignalQuality;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.Process;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import java.util.Arrays;
-
-public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-    private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
-    private long mGnssSignalLevelTimestamp;
-    private final long[] mGnssSignalDurations =
-            new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
-    private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
-    private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
-            new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
-    private final boolean mUseSignalLevelEstimators;
-    private long[] mTmpDeviceStatsArray;
-
-    public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
-        super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
-                powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON),
-                sStatsLayout);
-
-        boolean useSignalLevelEstimators = false;
-        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
-            double power = powerProfile.getAveragePower(
-                    PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level);
-            if (power != 0) {
-                useSignalLevelEstimators = true;
-            }
-            mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power);
-        }
-        mUseSignalLevelEstimators = useSignalLevelEstimators;
-    }
-
-    @Override
-    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
-        if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) {
-            mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
-            return STATE_OFF;
-        }
-
-        noteGnssSignalLevel(item);
-        return STATE_ON;
-    }
-
-    private void noteGnssSignalLevel(BatteryStats.HistoryItem item) {
-        int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
-                >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
-        if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
-            signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
-        }
-        if (signalLevel == mGnssSignalLevel) {
-            return;
-        }
-
-        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
-            mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp;
-        }
-        mGnssSignalLevel = signalLevel;
-        mGnssSignalLevelTimestamp = item.time;
-    }
-
-    @Override
-    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
-        super.recordUsageDuration(powerStats, uid, time);
-
-        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
-            mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp;
-        } else if (mUseSignalLevelEstimators) {
-            // Default GNSS signal quality to GOOD for the purposes of power attribution
-            mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] +=
-                    time - mGnssSignalLevelTimestamp;
-        }
-
-        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
-            long duration = mGnssSignalDurations[level];
-            sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration);
-            if (uid != Process.INVALID_UID) {
-                long[] uidStats = powerStats.uidStats.get(uid);
-                if (uidStats == null) {
-                    uidStats = new long[powerStats.descriptor.uidStatsArrayLength];
-                    powerStats.uidStats.put(uid, uidStats);
-                    sStatsLayout.setUidSignalLevelTime(uidStats, level, duration);
-                } else {
-                    sStatsLayout.setUidSignalLevelTime(uidStats, level,
-                            sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration);
-                }
-            }
-        }
-
-        mGnssSignalLevelTimestamp = time;
-        Arrays.fill(mGnssSignalDurations, 0);
-    }
-
-    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            PowerEstimationPlan plan, boolean energyConsumerSupported) {
-        if (!mUseSignalLevelEstimators || energyConsumerSupported) {
-            super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported);
-            return;
-        }
-
-        if (mTmpDeviceStatsArray == null) {
-            mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength];
-        }
-
-        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
-                continue;
-            }
-
-            double power = 0;
-            for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
-                long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level);
-                power += mSignalLevelEstimators[level].calculatePower(duration);
-            }
-            sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
-            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index c88e1b0..cbd6fab 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -35,12 +35,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
 
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 
@@ -56,8 +56,6 @@
 
     private static final long MODEM_ACTIVITY_REQUEST_TIMEOUT = 20000;
 
-    private static final long ENERGY_UNSPECIFIED = -1;
-
     @VisibleForTesting
     @AccessNetworkConstants.RadioAccessNetworkType
     static final int[] NETWORK_TYPES = {
@@ -77,14 +75,13 @@
                 long elapsedRealtimeMs, long uptimeMs);
     }
 
-    interface Injector {
+    public interface Injector {
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         PackageManager getPackageManager();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
         Supplier<NetworkStats> getMobileNetworkStatsSupplier();
         TelephonyManager getTelephonyManager();
         LongSupplier getCallDurationSupplier();
@@ -103,18 +100,14 @@
     private LongSupplier mCallDurationSupplier;
     private LongSupplier mScanDurationSupplier;
     private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
-    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
-    private int[] mEnergyConsumerIds = new int[0];
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
     private long mLastUpdateTimestampMillis;
     private ModemActivityInfo mLastModemActivityInfo;
     private NetworkStats mLastNetworkStats;
-    private long[] mLastConsumedEnergyUws;
-    private int mLastVoltageMv;
     private long mLastCallDuration;
     private long mLastScanDuration;
 
-    MobileRadioPowerStatsCollector(Injector injector, Observer observer) {
+    public MobileRadioPowerStatsCollector(Injector injector, Observer observer) {
         super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
                         BatteryConsumer.powerComponentIdToString(
                                 BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)),
@@ -144,34 +137,22 @@
             return false;
         }
 
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
-
         mTelephonyManager = mInjector.getTelephonyManager();
         mNetworkStatsSupplier = mInjector.getMobileNetworkStatsSupplier();
         mCallDurationSupplier = mInjector.getCallDurationSupplier();
         mScanDurationSupplier = mInjector.getPhoneSignalScanDurationSupplier();
 
-        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+        mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
                 EnergyConsumerType.MOBILE_RADIO);
-        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
-        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
 
-        mLayout = new MobileRadioPowerStatsLayout();
-        mLayout.addDeviceMobileActivity();
-        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
-        mLayout.addStateStats();
-        mLayout.addUidNetworkStats();
-        mLayout.addDeviceSectionUsageDuration();
-        mLayout.addDeviceSectionPowerEstimate();
-        mLayout.addUidSectionPowerEstimate();
+        mLayout = new MobileRadioPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount());
 
         SparseArray<String> stateLabels = new SparseArray<>();
         for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
             final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
                     ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
             for (int freq = 0; freq < freqCount; freq++) {
-                int stateKey = makeStateKey(rat, freq);
+                int stateKey = MobileRadioPowerStatsLayout.makeStateKey(rat, freq);
                 StringBuilder sb = new StringBuilder();
                 if (rat != BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER) {
                     sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]);
@@ -200,7 +181,7 @@
     }
 
     @Override
-    protected PowerStats collectStats() {
+    public PowerStats collectStats() {
         if (!ensureInitialized()) {
             return null;
         }
@@ -210,9 +191,8 @@
 
         ModemActivityInfo modemActivityDelta = collectModemActivityInfo();
         List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
-        if (mEnergyConsumerIds.length != 0) {
-            collectEnergyConsumers();
-        }
+
+        mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
         if (mPowerStats.durationMs == 0) {
             setTimestamp(mClock.elapsedRealtime());
@@ -346,65 +326,8 @@
         return delta;
     }
 
-    private void collectEnergyConsumers() {
-        int voltageMv = mVoltageSupplier.getAsInt();
-        if (voltageMv <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                    + " mV) when querying energy consumers");
-            return;
-        }
-
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        mLastVoltageMv = voltageMv;
-
-        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
-        if (energyUws == null) {
-            return;
-        }
-
-        for (int i = energyUws.length - 1; i >= 0; i--) {
-            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
-                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
-            if (energyDelta < 0) {
-                // Likely, restart of powerstats HAL
-                energyDelta = 0;
-            }
-            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
-            mLastConsumedEnergyUws[i] = energyUws[i];
-        }
-    }
-
-    static int makeStateKey(int rat, int freqRange) {
-        if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
-            return rat | (freqRange << 8);
-        } else {
-            return rat;
-        }
-    }
-
     private void setTimestamp(long timestamp) {
         mPowerStats.durationMs = Math.max(timestamp - mLastUpdateTimestampMillis, 0);
         mLastUpdateTimestampMillis = timestamp;
     }
-
-    @BatteryStats.RadioAccessTechnology
-    static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
-            @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
-        switch (networkType) {
-            case AccessNetworkConstants.AccessNetworkType.NGRAN:
-                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
-            case AccessNetworkConstants.AccessNetworkType.EUTRAN:
-                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
-            case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
-            case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
-            case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
-            case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
-            case AccessNetworkConstants.AccessNetworkType.IWLAN:
-                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
-            default:
-                Slog.w(TAG,
-                        "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
-                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
deleted file mode 100644
index 07d78f8..0000000
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
+++ /dev/null
@@ -1,256 +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.server.power.stats;
-
-import android.annotation.NonNull;
-import android.os.PersistableBundle;
-import android.telephony.ModemActivityInfo;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.os.PowerStats;
-
-/**
- * Captures the positions and lengths of sections of the stats array, such as time-in-state,
- * power usage estimates etc.
- */
-class MobileRadioPowerStatsLayout extends PowerStatsLayout {
-    private static final String TAG = "MobileRadioPowerStatsLayout";
-    private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep";
-    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
-    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
-    private static final String EXTRA_DEVICE_CALL_TIME_POSITION = "dt-call";
-    private static final String EXTRA_DEVICE_CALL_POWER_POSITION = "dp-call";
-    private static final String EXTRA_STATE_RX_TIME_POSITION = "srx";
-    private static final String EXTRA_STATE_TX_TIMES_POSITION = "stx";
-    private static final String EXTRA_STATE_TX_TIMES_COUNT = "stxc";
-    private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
-    private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
-    private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
-    private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
-
-    private int mDeviceSleepTimePosition;
-    private int mDeviceIdleTimePosition;
-    private int mDeviceScanTimePosition;
-    private int mDeviceCallTimePosition;
-    private int mDeviceCallPowerPosition;
-    private int mStateRxTimePosition;
-    private int mStateTxTimesPosition;
-    private int mStateTxTimesCount;
-    private int mUidRxBytesPosition;
-    private int mUidTxBytesPosition;
-    private int mUidRxPacketsPosition;
-    private int mUidTxPacketsPosition;
-
-    MobileRadioPowerStatsLayout() {
-    }
-
-    MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
-        super(descriptor);
-    }
-
-    void addDeviceMobileActivity() {
-        mDeviceSleepTimePosition = addDeviceSection(1, "sleep");
-        mDeviceIdleTimePosition = addDeviceSection(1, "idle");
-        mDeviceScanTimePosition = addDeviceSection(1, "scan");
-        mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL);
-    }
-
-    void addStateStats() {
-        mStateRxTimePosition = addStateSection(1, "rx");
-        mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
-        mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx");
-    }
-
-    void addUidNetworkStats() {
-        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
-        mUidRxBytesPosition = addUidSection(1, "rx-B");
-        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
-        mUidTxBytesPosition = addUidSection(1, "tx-B");
-    }
-
-    @Override
-    public void addDeviceSectionPowerEstimate() {
-        super.addDeviceSectionPowerEstimate();
-        // Printed as part of the PhoneCallPowerStatsProcessor
-        mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN);
-    }
-
-    public void setDeviceSleepTime(long[] stats, long durationMillis) {
-        stats[mDeviceSleepTimePosition] = durationMillis;
-    }
-
-    public long getDeviceSleepTime(long[] stats) {
-        return stats[mDeviceSleepTimePosition];
-    }
-
-    public void setDeviceIdleTime(long[] stats, long durationMillis) {
-        stats[mDeviceIdleTimePosition] = durationMillis;
-    }
-
-    public long getDeviceIdleTime(long[] stats) {
-        return stats[mDeviceIdleTimePosition];
-    }
-
-    public void setDeviceScanTime(long[] stats, long durationMillis) {
-        stats[mDeviceScanTimePosition] = durationMillis;
-    }
-
-    public long getDeviceScanTime(long[] stats) {
-        return stats[mDeviceScanTimePosition];
-    }
-
-    public void setDeviceCallTime(long[] stats, long durationMillis) {
-        stats[mDeviceCallTimePosition] = durationMillis;
-    }
-
-    public long getDeviceCallTime(long[] stats) {
-        return stats[mDeviceCallTimePosition];
-    }
-
-    public void setDeviceCallPowerEstimate(long[] stats, double power) {
-        stats[mDeviceCallPowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
-    }
-
-    public double getDeviceCallPowerEstimate(long[] stats) {
-        return stats[mDeviceCallPowerPosition] / MILLI_TO_NANO_MULTIPLIER;
-    }
-
-    public void setStateRxTime(long[] stats, long durationMillis) {
-        stats[mStateRxTimePosition] = durationMillis;
-    }
-
-    public long getStateRxTime(long[] stats) {
-        return stats[mStateRxTimePosition];
-    }
-
-    public void setStateTxTime(long[] stats, int level, int durationMillis) {
-        stats[mStateTxTimesPosition + level] = durationMillis;
-    }
-
-    public long getStateTxTime(long[] stats, int level) {
-        return stats[mStateTxTimesPosition + level];
-    }
-
-    public void setUidRxBytes(long[] stats, long count) {
-        stats[mUidRxBytesPosition] = count;
-    }
-
-    public long getUidRxBytes(long[] stats) {
-        return stats[mUidRxBytesPosition];
-    }
-
-    public void setUidTxBytes(long[] stats, long count) {
-        stats[mUidTxBytesPosition] = count;
-    }
-
-    public long getUidTxBytes(long[] stats) {
-        return stats[mUidTxBytesPosition];
-    }
-
-    public void setUidRxPackets(long[] stats, long count) {
-        stats[mUidRxPacketsPosition] = count;
-    }
-
-    public long getUidRxPackets(long[] stats) {
-        return stats[mUidRxPacketsPosition];
-    }
-
-    public void setUidTxPackets(long[] stats, long count) {
-        stats[mUidTxPacketsPosition] = count;
-    }
-
-    public long getUidTxPackets(long[] stats) {
-        return stats[mUidTxPacketsPosition];
-    }
-
-    /**
-     * Copies the elements of the stats array layout into <code>extras</code>
-     */
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
-        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
-        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
-        extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
-        extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
-        extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
-        extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
-        extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
-        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
-        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
-        extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
-        extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
-    }
-
-    /**
-     * Retrieves elements of the stats array layout from <code>extras</code>
-     */
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
-        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
-        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
-        mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
-        mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
-        mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
-        mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
-        mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
-        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
-        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
-        mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
-        mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
-    }
-
-    public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange,
-            long rxTime, int[] txTime) {
-        if (txTime.length != mStateTxTimesCount) {
-            Slog.wtf(TAG, "Invalid TX time array size: " + txTime.length);
-            return;
-        }
-
-        boolean nonZero = false;
-        if (rxTime != 0) {
-            nonZero = true;
-        } else {
-            for (int i = txTime.length - 1; i >= 0; i--) {
-                if (txTime[i] != 0) {
-                    nonZero = true;
-                    break;
-                }
-            }
-        }
-
-        if (!nonZero) {
-            return;
-        }
-
-        int rat = MobileRadioPowerStatsCollector.mapRadioAccessNetworkTypeToRadioAccessTechnology(
-                networkType);
-        int stateKey = MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange);
-        long[] stats = stateStats.get(stateKey);
-        if (stats == null) {
-            stats = new long[getStateStatsArrayLength()];
-            stateStats.put(stateKey, stats);
-        }
-
-        stats[mStateRxTimePosition] += rxTime;
-        for (int i = mStateTxTimesCount - 1; i >= 0; i--) {
-            stats[mStateTxTimesPosition + i] += txTime[i];
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
deleted file mode 100644
index dcce562..0000000
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
+++ /dev/null
@@ -1,401 +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.server.power.stats;
-
-import android.os.BatteryStats;
-import android.telephony.CellSignalStrength;
-import android.telephony.ModemActivityInfo;
-import android.telephony.ServiceState;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-import com.android.internal.power.ModemPowerProfile;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "MobileRadioPowerStatsProcessor";
-    private static final boolean DEBUG = false;
-
-    private static final int NUM_SIGNAL_STRENGTH_LEVELS =
-            CellSignalStrength.getNumSignalStrengthLevels();
-    private static final int IGNORE = -1;
-
-    private final UsageBasedPowerEstimator mSleepPowerEstimator;
-    private final UsageBasedPowerEstimator mIdlePowerEstimator;
-    private final UsageBasedPowerEstimator mCallPowerEstimator;
-    private final UsageBasedPowerEstimator mScanPowerEstimator;
-
-    private static class RxTxPowerEstimators {
-        UsageBasedPowerEstimator mRxPowerEstimator;
-        UsageBasedPowerEstimator[] mTxPowerEstimators =
-                new UsageBasedPowerEstimator[ModemActivityInfo.getNumTxPowerLevels()];
-    }
-
-    private final SparseArray<RxTxPowerEstimators> mRxTxPowerEstimators = new SparseArray<>();
-
-    private PowerStats.Descriptor mLastUsedDescriptor;
-    private MobileRadioPowerStatsLayout mStatsLayout;
-    // Sequence of steps for power estimation and intermediate results.
-    private PowerEstimationPlan mPlan;
-
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpStateStatsArray;
-    private long[] mTmpUidStatsArray;
-
-    public MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
-        final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
-                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
-                Double.NaN);
-        if (Double.isNaN(sleepDrainRateMa)) {
-            mSleepPowerEstimator = null;
-        } else {
-            mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa);
-        }
-
-        final double idleDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
-                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
-                Double.NaN);
-        if (Double.isNaN(idleDrainRateMa)) {
-            mIdlePowerEstimator = null;
-        } else {
-            mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa);
-        }
-
-        // Instantiate legacy power estimators
-        double powerRadioActiveMa =
-                powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN);
-        if (Double.isNaN(powerRadioActiveMa)) {
-            double sum = 0;
-            sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-            for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
-                sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
-            }
-            powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
-        }
-        mCallPowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
-
-        mScanPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
-
-        for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
-            final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
-                    ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
-            for (int freqRange = 0; freqRange < freqCount; freqRange++) {
-                mRxTxPowerEstimators.put(
-                        MobileRadioPowerStatsCollector.makeStateKey(rat, freqRange),
-                        buildRxTxPowerEstimators(powerProfile, rat, freqRange));
-            }
-        }
-    }
-
-    private static RxTxPowerEstimators buildRxTxPowerEstimators(PowerProfile powerProfile, int rat,
-            int freqRange) {
-        RxTxPowerEstimators estimators = new RxTxPowerEstimators();
-        long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
-                ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
-        double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN);
-        if (Double.isNaN(rxDrainRateMa)) {
-            Log.w(TAG, "Unavailable Power Profile constant for key 0x"
-                    + Long.toHexString(rxKey));
-            rxDrainRateMa = 0;
-        }
-        estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa);
-        for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
-            long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
-                    ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
-            double txDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
-                    Double.NaN);
-            if (Double.isNaN(txDrainRateMa)) {
-                Log.w(TAG, "Unavailable Power Profile constant for key 0x"
-                        + Long.toHexString(txKey));
-                txDrainRateMa = 0;
-            }
-            estimators.mTxPowerEstimators[txLevel] = new UsageBasedPowerEstimator(txDrainRateMa);
-        }
-        return estimators;
-    }
-
-    private static class Intermediates {
-        /**
-         * Number of received packets
-         */
-        public long rxPackets;
-        /**
-         * Number of transmitted packets
-         */
-        public long txPackets;
-        /**
-         * Estimated power for the RX state of the modem.
-         */
-        public double rxPower;
-        /**
-         * Estimated power for the TX state of the modem.
-         */
-        public double txPower;
-        /**
-         * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
-         */
-        public double inactivePower;
-        /**
-         * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
-         */
-        public double callPower;
-        /**
-         * Measured consumed energy from power monitoring hardware (micro-coulombs)
-         */
-        public long consumedEnergy;
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (stats.getPowerStatsDescriptor() == null) {
-            return;
-        }
-
-        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-            Intermediates intermediates = new Intermediates();
-            estimation.intermediates = intermediates;
-            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
-        }
-
-        if (mStatsLayout.getEnergyConsumerCount() != 0) {
-            double ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
-            if (ratio != 1) {
-                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-                    adjustDevicePowerEstimates(stats, estimation.stateValues,
-                            (Intermediates) estimation.intermediates, ratio);
-                }
-            }
-        }
-
-        combineDeviceStateEstimates();
-
-        ArrayList<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-        if (!uids.isEmpty()) {
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-        }
-        mPlan.resetIntermediates();
-    }
-
-    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
-        if (descriptor.equals(mLastUsedDescriptor)) {
-            return;
-        }
-
-        mLastUsedDescriptor = descriptor;
-        mStatsLayout = new MobileRadioPowerStatsLayout(descriptor);
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpStateStatsArray = new long[descriptor.stateStatsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-    }
-
-    /**
-     * Compute power estimates using the power profile.
-     */
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates) {
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
-            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
-        }
-
-        if (mSleepPowerEstimator != null) {
-            intermediates.inactivePower += mSleepPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceSleepTime(mTmpDeviceStatsArray));
-        }
-
-        if (mIdlePowerEstimator != null) {
-            intermediates.inactivePower += mIdlePowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
-        }
-
-        if (mScanPowerEstimator != null) {
-            intermediates.inactivePower += mScanPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
-        }
-
-        stats.forEachStateStatsKey(key -> {
-            RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
-            stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
-            long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
-            intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
-            for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
-                long txTime = mStatsLayout.getStateTxTime(mTmpStateStatsArray, txLevel);
-                intermediates.txPower +=
-                        estimators.mTxPowerEstimators[txLevel].calculatePower(txTime);
-            }
-        });
-
-        if (mCallPowerEstimator != null) {
-            intermediates.callPower = mCallPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceCallTime(mTmpDeviceStatsArray));
-        }
-
-        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
-        mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * Compute an adjustment ratio using the total power estimated using the power profile
-     * and the total power measured by hardware.
-     */
-    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
-        long totalConsumedEnergy = 0;
-        double totalPower = 0;
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            Intermediates intermediates =
-                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
-            totalPower += intermediates.rxPower + intermediates.txPower
-                    + intermediates.inactivePower + intermediates.callPower;
-            totalConsumedEnergy += intermediates.consumedEnergy;
-        }
-
-        if (totalPower == 0) {
-            return 1;
-        }
-
-        return uCtoMah(totalConsumedEnergy) / totalPower;
-    }
-
-    /**
-     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
-     * estimated power matches the measured consumed power.  We are not claiming that all
-     * averages captured in the power profile have to be off by the same percentage in reality.
-     */
-    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates, double ratio) {
-        intermediates.rxPower *= ratio;
-        intermediates.txPower *= ratio;
-        intermediates.inactivePower *= ratio;
-        intermediates.callPower *= ratio;
-
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
-        mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * This step is effectively a no-op in the cases where we track the same states for
-     * the entire device and all UIDs (e.g. screen on/off, on-battery/on-charger etc). However,
-     * if the lists of tracked states are not the same, we need to combine some estimates
-     * before distributing them proportionally to UIDs.
-     */
-    private void combineDeviceStateEstimates() {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
-            Intermediates cdseIntermediates = new Intermediates();
-            cdse.intermediates = cdseIntermediates;
-            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
-            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation dse = deviceStateEstimations.get(j);
-                Intermediates intermediates = (Intermediates) dse.intermediates;
-                cdseIntermediates.rxPower += intermediates.rxPower;
-                cdseIntermediates.txPower += intermediates.txPower;
-                cdseIntermediates.inactivePower += intermediates.inactivePower;
-                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
-            }
-        }
-    }
-
-    private void computeUidRxTxTotals(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
-            intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            double power = 0;
-            if (intermediates.rxPackets != 0) {
-                power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
-                        / intermediates.rxPackets;
-            }
-            if (intermediates.txPackets != 0) {
-                power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
-                        / intermediates.txPackets;
-            }
-
-            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-
-            if (DEBUG) {
-                Slog.d(TAG, "UID: " + uid
-                        + " states: " + Arrays.toString(proportionalEstimate.stateValues)
-                        + " stats: " + Arrays.toString(mTmpUidStatsArray)
-                        + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
-                        + " rx-power: " + intermediates.rxPower
-                        + " rx-packets: " + intermediates.rxPackets
-                        + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
-                        + " tx-power: " + intermediates.txPower
-                        + " tx-packets: " + intermediates.txPackets
-                        + " power: " + power);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
deleted file mode 100644
index c3a0aeb..0000000
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ /dev/null
@@ -1,505 +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.server.power.stats;
-
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.LongArrayMultiStateCounter;
-import com.android.internal.util.Preconditions;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.function.Consumer;
-
-/**
- * Maintains multidimensional multi-state stats.  States could be something like on-battery (0,1),
- * screen-on (0,1), process state etc.  Dimensions refer to the metrics themselves, e.g.
- * CPU residency, Network packet counts etc.  All metrics must be represented as <code>long</code>
- * values;
- */
-public class MultiStateStats {
-    private static final String TAG = "MultiStateStats";
-
-    private static final String XML_TAG_STATS = "stats";
-    public static final int STATE_DOES_NOT_EXIST = -1;
-
-    /**
-     * A set of states, e.g. on-battery, screen-on, procstate.  The state values are integers
-     * from 0 to States.mLabels.length
-     */
-    public static class States {
-        final String mName;
-        final boolean mTracked;
-        final String[] mLabels;
-
-        public States(String name, boolean tracked, String... labels) {
-            mName = name;
-            mTracked = tracked;
-            mLabels = labels;
-        }
-
-        public boolean isTracked() {
-            return mTracked;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public String[] getLabels() {
-            return mLabels;
-        }
-
-        /**
-         * Finds state by name in the provided array. If not found, returns STATE_DOES_NOT_EXIST.
-         */
-        public static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
-            for (int i = 0; i < states.length; i++) {
-                if (states[i].getName().equals(name)) {
-                    return i;
-                }
-            }
-            return STATE_DOES_NOT_EXIST;
-        }
-
-        /**
-         * Iterates over all combinations of tracked states and invokes <code>consumer</code>
-         * for each of them.
-         */
-        public static void forEachTrackedStateCombination(States[] states,
-                Consumer<int[]> consumer) {
-            forEachTrackedStateCombination(consumer, states, new int[states.length], 0);
-        }
-
-        /**
-         * Recursive function that does a depth-first traversal of the multi-dimensional
-         * state space. Each time the traversal reaches the end of the <code>states</code> array,
-         * <code>statesValues</code> contains a unique combination of values for all tracked states.
-         * For untracked states, the corresponding values are left as 0.  The end result is
-         * that the <code>consumer</code> is invoked for every unique combination of tracked state
-         * values.  For example, it may be a sequence of calls like screen-on/power-on,
-         * screen-on/power-off, screen-off/power-on, screen-off/power-off.
-         */
-        private static void forEachTrackedStateCombination(Consumer<int[]> consumer,
-                States[] states, int[] statesValues, int stateIndex) {
-            if (stateIndex < statesValues.length) {
-                if (!states[stateIndex].mTracked) {
-                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
-                    return;
-                }
-                for (int i = 0; i < states[stateIndex].mLabels.length; i++) {
-                    statesValues[stateIndex] = i;
-                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
-                }
-                return;
-            }
-            consumer.accept(statesValues);
-        }
-    }
-
-    /**
-     * Factory for MultiStateStats containers. All generated containers retain their connection
-     * to the Factory and the corresponding configuration.
-     */
-    public static class Factory {
-        private static final int INVALID_SERIAL_STATE = -1;
-        final int mDimensionCount;
-        final States[] mStates;
-        /*
-         * The LongArrayMultiStateCounter object that is used for accumulation of per-state
-         * stats thinks of "state" as a simple 0-based index. This Factory object's job is to
-         * map a combination of individual states (e.g. on-battery, process state etc) to
-         * such a simple index.
-         *
-         * This task is performed in two steps:
-         * 1) We define "composite state" as an integer that combines all constituent States
-         * into one integer as bit fields. This gives us a convenient mechanism for updating a
-         * single constituent State at a time.  We maintain an array of bit field masks
-         * corresponding to each constituent State.
-         *
-         * 2) We map composite states to "serial states", i.e. simple integer indexes, taking
-         * into account which constituent states are configured as tracked.  If a state is not
-         * tracked, there is no need to maintain separate counts for its values, thus
-         * all values of that constituent state can be mapped to the same serial state.
-         */
-        private final int[] mStateBitFieldMasks;
-        private final short[] mStateBitFieldShifts;
-        final int[] mCompositeToSerialState;
-        final int mSerialStateCount;
-
-        public Factory(int dimensionCount, States... states) {
-            mDimensionCount = dimensionCount;
-            mStates = states;
-
-            int serialStateCount = 1;
-            for (States state : mStates) {
-                if (state.mTracked) {
-                    serialStateCount *= state.mLabels.length;
-                }
-            }
-            mSerialStateCount = serialStateCount;
-
-            mStateBitFieldMasks = new int[mStates.length];
-            mStateBitFieldShifts = new short[mStates.length];
-
-            int shift = 0;
-            for (int i = 0; i < mStates.length; i++) {
-                mStateBitFieldShifts[i] = (short) shift;
-                if (mStates[i].mLabels.length < 2) {
-                    throw new IllegalArgumentException("Invalid state: " + Arrays.toString(
-                            mStates[i].mLabels) + ". Should have at least two values.");
-                }
-                int max = mStates[i].mLabels.length - 1;
-                int bitcount = Integer.SIZE - Integer.numberOfLeadingZeros(max);
-                mStateBitFieldMasks[i] = ((1 << bitcount) - 1) << shift;
-                shift = shift + bitcount;
-            }
-
-            if (shift >= Integer.SIZE - 1) {
-                throw new IllegalArgumentException("Too many states: " + shift
-                        + " bits are required to represent the composite state, but only "
-                        + (Integer.SIZE - 1) + " are available");
-            }
-
-            // Create a mask that filters out all non tracked states
-            int trackedMask = 0xFFFFFFFF;
-            for (int state = 0; state < mStates.length; state++) {
-                if (!mStates[state].mTracked) {
-                    trackedMask &= ~mStateBitFieldMasks[state];
-                }
-            }
-
-            mCompositeToSerialState = new int[1 << shift];
-            Arrays.fill(mCompositeToSerialState, INVALID_SERIAL_STATE);
-
-            int nextSerialState = 0;
-            for (int composite = 0; composite < mCompositeToSerialState.length; composite++) {
-                if (!isValidCompositeState(composite)) continue;
-
-                // Values of an untracked State map to different composite states, but must map to
-                // the same serial state. Achieve that by computing a "base composite", which
-                // is equivalent to the current composite, but has 0 for all untracked States.
-                // See if the base composite already has a serial state assigned.  If so, just use
-                // the same one for the current composite.
-                int baseComposite = composite & trackedMask;
-                if (mCompositeToSerialState[baseComposite] != INVALID_SERIAL_STATE) {
-                    mCompositeToSerialState[composite] = mCompositeToSerialState[baseComposite];
-                } else {
-                    mCompositeToSerialState[composite] = nextSerialState++;
-                }
-            }
-        }
-
-        private boolean isValidCompositeState(int composite) {
-            for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
-                int state = extractStateFromComposite(composite, stateIndex);
-                if (state >= mStates[stateIndex].mLabels.length) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private int extractStateFromComposite(int compositeState, int stateIndex) {
-            return (compositeState & mStateBitFieldMasks[stateIndex])
-                   >>> mStateBitFieldShifts[stateIndex];
-        }
-
-        int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
-            return (baseCompositeState & ~mStateBitFieldMasks[stateIndex])
-                    | (value << mStateBitFieldShifts[stateIndex]);
-        }
-
-        int setStateInComposite(int compositeState, String stateName, String stateLabel) {
-            for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
-                States stateConfig = mStates[stateIndex];
-                if (stateConfig.mName.equals(stateName)) {
-                    for (int state = 0; state < stateConfig.mLabels.length; state++) {
-                        if (stateConfig.mLabels[state].equals(stateLabel)) {
-                            return setStateInComposite(compositeState, stateIndex, state);
-                        }
-                    }
-                    Slog.e(TAG, "Unexpected label '" + stateLabel + "' for state: " + stateName);
-                    return -1;
-                }
-            }
-            Slog.e(TAG, "Unsupported state: " + stateName);
-            return -1;
-        }
-
-        /**
-         * Allocates a new stats container using this Factory's configuration.
-         */
-        public MultiStateStats create() {
-            return new MultiStateStats(this, mDimensionCount);
-        }
-
-        /**
-         * Returns the total number of composite states handled by this container. For example,
-         * if there are two states: on-battery (0,1) and screen-on (0,1), both tracked, then the
-         * serial state count will be 2 * 2 = 4
-         */
-        @VisibleForTesting
-        public int getSerialStateCount() {
-            return mSerialStateCount;
-        }
-
-        /**
-         * Returns the integer index used by this container to represent the supplied composite
-         * state.
-         */
-        @VisibleForTesting
-        public int getSerialState(int[] states) {
-            Preconditions.checkArgument(states.length == mStates.length);
-            int compositeState = 0;
-            for (int i = 0; i < states.length; i++) {
-                compositeState = setStateInComposite(compositeState, i, states[i]);
-            }
-            int serialState = mCompositeToSerialState[compositeState];
-            if (serialState == INVALID_SERIAL_STATE) {
-                throw new IllegalArgumentException("State values out of bounds: "
-                                                   + Arrays.toString(states));
-            }
-            return serialState;
-        }
-
-        int getSerialState(int compositeState) {
-            return mCompositeToSerialState[compositeState];
-        }
-    }
-
-    private final Factory mFactory;
-    private final LongArrayMultiStateCounter mCounter;
-    private int mCompositeState;
-    private boolean mTracking;
-
-    public MultiStateStats(Factory factory, int dimensionCount) {
-        this.mFactory = factory;
-        mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
-    }
-
-    public int getDimensionCount() {
-        return mFactory.mDimensionCount;
-    }
-
-    public States[] getStates() {
-        return mFactory.mStates;
-    }
-
-    /**
-     * Copies time-in-state and timestamps from the supplied prototype. Does not
-     * copy accumulated counts.
-     */
-    public void copyStatesFrom(MultiStateStats otherStats) {
-        mCounter.copyStatesFrom(otherStats.mCounter);
-    }
-
-    /**
-     * Updates the current composite state by changing one of the States supplied to the Factory
-     * constructor.
-     *
-     * @param stateIndex  Corresponds to the index of the States supplied to the Factory constructor
-     * @param state       The new value of the state (e.g. 0 or 1 for "on-battery")
-     * @param timestampMs The time when the state change occurred
-     */
-    public void setState(int stateIndex, int state, long timestampMs) {
-        if (!mTracking) {
-            mCounter.updateValues(new long[mCounter.getArrayLength()], timestampMs);
-            mTracking = true;
-        }
-        mCompositeState = mFactory.setStateInComposite(mCompositeState, stateIndex, state);
-        mCounter.setState(mFactory.mCompositeToSerialState[mCompositeState], timestampMs);
-    }
-
-    /**
-     * Adds the delta to the metrics.  The number of values must correspond to the dimension count
-     * supplied to the Factory constructor
-     */
-    public void increment(long[] values, long timestampMs) {
-        mCounter.incrementValues(values, timestampMs);
-        mTracking = true;
-    }
-
-    /**
-     * Returns accumulated stats for the specified composite state.
-     */
-    public void getStats(long[] outValues, int[] states) {
-        mCounter.getCounts(outValues, mFactory.getSerialState(states));
-    }
-
-    /**
-     * Updates the stats values for the provided combination of states.
-     */
-    public void setStats(int[] states, long[] values) {
-        mCounter.setValues(mFactory.getSerialState(states), values);
-    }
-
-    /**
-     * Resets the counters.
-     */
-    public void reset() {
-        mCounter.reset();
-        mTracking = false;
-    }
-
-    /**
-     * Stores contents in an XML doc.
-     */
-    public void writeXml(TypedXmlSerializer serializer) throws IOException {
-        long[] tmpArray = new long[mCounter.getArrayLength()];
-
-        try {
-            States.forEachTrackedStateCombination(mFactory.mStates,
-                    states -> {
-                        try {
-                            writeXmlForStates(serializer, states, tmpArray);
-                        } catch (IOException e) {
-                            throw new RuntimeException(e);
-                        }
-                    });
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof IOException) {
-                throw (IOException) e.getCause();
-            } else {
-                throw e;
-            }
-        }
-    }
-
-    private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
-            throws IOException {
-        mCounter.getCounts(values, mFactory.getSerialState(states));
-        boolean nonZero = false;
-        for (long value : values) {
-            if (value != 0) {
-                nonZero = true;
-                break;
-            }
-        }
-        if (!nonZero) {
-            return;
-        }
-
-        serializer.startTag(null, XML_TAG_STATS);
-
-        for (int i = 0; i < states.length; i++) {
-            if (mFactory.mStates[i].mTracked && states[i] != 0) {
-                serializer.attribute(null, mFactory.mStates[i].mName,
-                        mFactory.mStates[i].mLabels[states[i]]);
-            }
-        }
-        for (int i = 0; i < values.length; i++) {
-            if (values[i] != 0) {
-                serializer.attributeLong(null, "_" + i, values[i]);
-            }
-        }
-        serializer.endTag(null, XML_TAG_STATS);
-    }
-
-    /**
-     * Populates the object with contents in an XML doc. The parser is expected to be
-     * positioned on the opening tag of the corresponding element.
-     */
-    public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
-            IOException {
-        String outerTag = parser.getName();
-        long[] tmpArray = new long[mCounter.getArrayLength()];
-        int eventType = parser.getEventType();
-        while (eventType != XmlPullParser.END_DOCUMENT
-               && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
-            if (eventType == XmlPullParser.START_TAG) {
-                if (parser.getName().equals(XML_TAG_STATS)) {
-                    Arrays.fill(tmpArray, 0);
-                    int compositeState = 0;
-                    int attributeCount = parser.getAttributeCount();
-                    for (int i = 0; i < attributeCount; i++) {
-                        String attributeName = parser.getAttributeName(i);
-                        if (attributeName.startsWith("_")) {
-                            int index;
-                            try {
-                                index = Integer.parseInt(attributeName.substring(1));
-                            } catch (NumberFormatException e) {
-                                throw new XmlPullParserException(
-                                        "Unexpected index syntax: " + attributeName, parser, e);
-                            }
-                            if (index < 0 || index >= tmpArray.length) {
-                                Slog.e(TAG, "State index out of bounds: " + index
-                                            + " length: " + tmpArray.length);
-                                return false;
-                            }
-                            tmpArray[index] = parser.getAttributeLong(i);
-                        } else {
-                            String attributeValue = parser.getAttributeValue(i);
-                            compositeState = mFactory.setStateInComposite(compositeState,
-                                    attributeName, attributeValue);
-                            if (compositeState == -1) {
-                                return false;
-                            }
-                        }
-                    }
-                    mCounter.setValues(mFactory.getSerialState(compositeState), tmpArray);
-                }
-            }
-            eventType = parser.next();
-        }
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        long[] values = new long[mCounter.getArrayLength()];
-        States.forEachTrackedStateCombination(mFactory.mStates, states -> {
-            mCounter.getCounts(values, mFactory.getSerialState(states));
-            boolean nonZero = false;
-            for (long value : values) {
-                if (value != 0) {
-                    nonZero = true;
-                    break;
-                }
-            }
-            if (!nonZero) {
-                return;
-            }
-
-            if (!sb.isEmpty()) {
-                sb.append("\n");
-            }
-
-            sb.append("(");
-            boolean first = true;
-            for (int i = 0; i < states.length; i++) {
-                if (mFactory.mStates[i].mTracked) {
-                    if (!first) {
-                        sb.append(" ");
-                    }
-                    first = false;
-                    sb.append(mFactory.mStates[i].mLabels[states[i]]);
-                }
-            }
-            sb.append(") ");
-            sb.append(Arrays.toString(values));
-        });
-        return sb.toString();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
deleted file mode 100644
index ec23fa0..0000000
--- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
+++ /dev/null
@@ -1,79 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.PersistableBundle;
-
-import com.android.internal.os.PowerStats;
-
-public class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
-    private final PowerStatsLayout mStatsLayout;
-    private final PowerStats.Descriptor mDescriptor;
-    private final long[] mTmpDeviceStats;
-    private PowerStats.Descriptor mMobileRadioStatsDescriptor;
-    private MobileRadioPowerStatsLayout mMobileRadioStatsLayout;
-    private long[] mTmpMobileRadioDeviceStats;
-
-    public PhoneCallPowerStatsProcessor() {
-        mStatsLayout = new PowerStatsLayout();
-        mStatsLayout.addDeviceSectionPowerEstimate();
-        PersistableBundle extras = new PersistableBundle();
-        mStatsLayout.toExtras(extras);
-        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE,
-                mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
-        mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        stats.setPowerStatsDescriptor(mDescriptor);
-
-        PowerComponentAggregatedPowerStats mobileRadioStats =
-                stats.getAggregatedPowerStats().getPowerComponentStats(
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
-        if (mobileRadioStats == null) {
-            return;
-        }
-
-        if (mMobileRadioStatsDescriptor == null) {
-            mMobileRadioStatsDescriptor = mobileRadioStats.getPowerStatsDescriptor();
-            if (mMobileRadioStatsDescriptor == null) {
-                return;
-            }
-
-            mMobileRadioStatsLayout =
-                    new MobileRadioPowerStatsLayout(
-                            mMobileRadioStatsDescriptor);
-            mTmpMobileRadioDeviceStats = new long[mMobileRadioStatsDescriptor.statsArrayLength];
-        }
-
-        MultiStateStats.States[] deviceStateConfig =
-                mobileRadioStats.getConfig().getDeviceStateConfig();
-
-        // Phone call power estimates have already been calculated by the mobile radio stats
-        // processor. All that remains to be done is copy the estimates over.
-        MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
-                states -> {
-                    mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
-                    double callPowerEstimate =
-                            mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
-                                    mTmpMobileRadioDeviceStats);
-                    mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
-                    stats.setDeviceStats(states, mTmpDeviceStats);
-                });
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerAttributor.java b/services/core/java/com/android/server/power/stats/PowerAttributor.java
new file mode 100644
index 0000000..d1f8564
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/PowerAttributor.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.power.stats;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.os.BatteryStatsHistory;
+
+public interface PowerAttributor {
+
+    /**
+     * Returns true if the specified power component can be handled by this PowerAttributor
+     */
+    boolean isPowerComponentSupported(@BatteryConsumer.PowerComponentId int powerComponentId);
+
+    /**
+     * Performs the power attribution calculations and returns the results by populating the
+     * supplied BatteryUsageStats.Builder
+     */
+    void estimatePowerConsumption(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            BatteryStatsHistory batteryHistory, long monotonicStartTime, long monotonicEndTime);
+
+    /**
+     * Computes estimated power consumption attribution for the specified time range and stores
+     * it in PowerStatsStore for potential accumulation.
+     *
+     * Returns the monotonic timestamp of the last processed history item.
+     */
+    long storeEstimatedPowerConsumption(BatteryStatsHistory batteryStatsHistory, long startTime,
+            long endTimeMs);
+
+    /**
+     * Returns the monotonic timestamp of the last processed history item, stored in
+     * PowerStatsStore.
+     */
+    long getLastSavedEstimatesPowerConsumptionTimestamp();
+
+    /**
+     * Performs the power attribution calculation and prints the results.
+     */
+    void dumpEstimatedPowerConsumption(IndentingPrintWriter ipw,
+            BatteryStatsHistory batteryStatsHistory, long startTime, long endTime);
+}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
deleted file mode 100644
index 6820197..0000000
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ /dev/null
@@ -1,594 +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.server.power.stats;
-
-import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.UserHandle;
-import android.util.IndentingPrintWriter;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.os.PowerStats;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.function.IntConsumer;
-
-/**
- * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
- * treats stats as arrays of nonspecific longs. Subclasses contain specific logic to interpret those
- * longs and use them for calculations such as power attribution. They may use meta-data supplied
- * as part of the {@link PowerStats.Descriptor}.
- */
-class PowerComponentAggregatedPowerStats {
-    private static final String TAG = "AggregatePowerStats";
-    static final String XML_TAG_POWER_COMPONENT = "power_component";
-    static final String XML_ATTR_ID = "id";
-    private static final String XML_TAG_DEVICE_STATS = "device-stats";
-    private static final String XML_TAG_STATE_STATS = "state-stats";
-    private static final String XML_ATTR_KEY = "key";
-    private static final String XML_TAG_UID_STATS = "uid-stats";
-    private static final String XML_ATTR_UID = "uid";
-    private static final long UNKNOWN = -1;
-
-    public final int powerComponentId;
-    @NonNull
-    private final AggregatedPowerStats mAggregatedPowerStats;
-    @NonNull
-    private final AggregatedPowerStatsConfig.PowerComponent mConfig;
-    private final MultiStateStats.States[] mDeviceStateConfig;
-    private final MultiStateStats.States[] mUidStateConfig;
-    private final int[] mDeviceStates;
-
-    private MultiStateStats.Factory mStatsFactory;
-    private MultiStateStats.Factory mStateStatsFactory;
-    private MultiStateStats.Factory mUidStatsFactory;
-    private PowerStats.Descriptor mPowerStatsDescriptor;
-    private long mPowerStatsTimestamp;
-    private MultiStateStats mDeviceStats;
-    private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
-    private final SparseArray<UidStats> mUidStats = new SparseArray<>();
-    private long[] mZeroArray;
-
-    private static class UidStats {
-        public int[] states;
-        public MultiStateStats stats;
-        public boolean updated;
-    }
-
-    PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
-            @NonNull AggregatedPowerStatsConfig.PowerComponent config) {
-        mAggregatedPowerStats = aggregatedPowerStats;
-        mConfig = config;
-        powerComponentId = config.getPowerComponentId();
-        mDeviceStateConfig = config.getDeviceStateConfig();
-        mUidStateConfig = config.getUidStateConfig();
-        mDeviceStates = new int[mDeviceStateConfig.length];
-        mPowerStatsTimestamp = UNKNOWN;
-    }
-
-    @NonNull
-    AggregatedPowerStats getAggregatedPowerStats() {
-        return mAggregatedPowerStats;
-    }
-
-    @NonNull
-    public AggregatedPowerStatsConfig.PowerComponent getConfig() {
-        return mConfig;
-    }
-
-    @Nullable
-    public PowerStats.Descriptor getPowerStatsDescriptor() {
-        return mPowerStatsDescriptor;
-    }
-
-    public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) {
-        mPowerStatsDescriptor = powerStatsDescriptor;
-    }
-
-    void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
-            long timestampMs) {
-        if (mDeviceStats == null) {
-            createDeviceStats(timestampMs);
-        }
-
-        mDeviceStates[stateId] = state;
-
-        if (mDeviceStateConfig[stateId].isTracked()) {
-            if (mDeviceStats != null) {
-                mDeviceStats.setState(stateId, state, timestampMs);
-            }
-            for (int i = mStateStats.size() - 1; i >= 0; i--) {
-                MultiStateStats stateStats = mStateStats.valueAt(i);
-                stateStats.setState(stateId, state, timestampMs);
-            }
-        }
-
-        int uidStateId = MultiStateStats.States
-                .findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName());
-        if (uidStateId != STATE_DOES_NOT_EXIST && mUidStateConfig[uidStateId].isTracked()) {
-            for (int i = mUidStats.size() - 1; i >= 0; i--) {
-                PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
-                if (uidStats.stats == null) {
-                    createUidStats(uidStats, timestampMs);
-                }
-
-                uidStats.states[uidStateId] = state;
-                if (uidStats.stats != null) {
-                    uidStats.stats.setState(uidStateId, state, timestampMs);
-                }
-            }
-        }
-    }
-
-    void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
-            long timestampMs) {
-        if (!mUidStateConfig[stateId].isTracked()) {
-            return;
-        }
-
-        UidStats uidStats = getUidStats(uid);
-        if (uidStats.stats == null) {
-            createUidStats(uidStats, timestampMs);
-        }
-
-        uidStats.states[stateId] = state;
-
-        if (uidStats.stats != null) {
-            uidStats.stats.setState(stateId, state, timestampMs);
-        }
-    }
-
-    void setDeviceStats(int[] states, long[] values) {
-        if (mDeviceStats == null) {
-            createDeviceStats(0);
-        }
-        mDeviceStats.setStats(states, values);
-    }
-
-    void setUidStats(int uid, int[] states, long[] values) {
-        UidStats uidStats = getUidStats(uid);
-        uidStats.stats.setStats(states, values);
-    }
-
-    boolean isCompatible(PowerStats powerStats) {
-        return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
-    }
-
-    void addPowerStats(PowerStats powerStats, long timestampMs) {
-        mPowerStatsDescriptor = powerStats.descriptor;
-
-        if (mDeviceStats == null) {
-            createDeviceStats(timestampMs);
-        }
-
-        for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) {
-            int key = powerStats.stateStats.keyAt(i);
-            MultiStateStats stateStats = mStateStats.get(key);
-            if (stateStats == null) {
-                stateStats = createStateStats(key, timestampMs);
-            }
-            stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs);
-        }
-        mDeviceStats.increment(powerStats.stats, timestampMs);
-
-        for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
-            int uid = powerStats.uidStats.keyAt(i);
-            PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
-            if (uidStats.stats == null) {
-                createUidStats(uidStats, timestampMs);
-            }
-            uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
-            uidStats.updated = true;
-        }
-
-        // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment.
-        // It is essential to call `stats.increment(zero)` in order to record the new
-        // timestamp, which will ensure correct proportional attribution across all UIDs
-        for (int i = mUidStats.size() - 1; i >= 0; i--) {
-            PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
-            if (!uidStats.updated && uidStats.stats != null) {
-                if (mZeroArray == null
-                        || mZeroArray.length != mPowerStatsDescriptor.uidStatsArrayLength) {
-                    mZeroArray = new long[mPowerStatsDescriptor.uidStatsArrayLength];
-                }
-                uidStats.stats.increment(mZeroArray, timestampMs);
-            }
-            uidStats.updated = false;
-        }
-
-        mPowerStatsTimestamp = timestampMs;
-    }
-
-    void reset() {
-        mStatsFactory = null;
-        mUidStatsFactory = null;
-        mDeviceStats = null;
-        mStateStats.clear();
-        for (int i = mUidStats.size() - 1; i >= 0; i--) {
-            mUidStats.valueAt(i).stats = null;
-        }
-    }
-
-    private UidStats getUidStats(int uid) {
-        UidStats uidStats = mUidStats.get(uid);
-        if (uidStats == null) {
-            uidStats = new UidStats();
-            uidStats.states = new int[mUidStateConfig.length];
-            for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
-                if (mUidStateConfig[stateId].isTracked()) {
-                    int deviceStateId = MultiStateStats.States.findTrackedStateByName(
-                            mDeviceStateConfig, mUidStateConfig[stateId].getName());
-                    if (deviceStateId != STATE_DOES_NOT_EXIST
-                            && mDeviceStateConfig[deviceStateId].isTracked()) {
-                        uidStats.states[stateId] = mDeviceStates[deviceStateId];
-                    }
-                }
-            }
-            mUidStats.put(uid, uidStats);
-        }
-        return uidStats;
-    }
-
-    void collectUids(Collection<Integer> uids) {
-        for (int i = mUidStats.size() - 1; i >= 0; i--) {
-            if (mUidStats.valueAt(i).stats != null) {
-                uids.add(mUidStats.keyAt(i));
-            }
-        }
-    }
-
-    boolean getDeviceStats(long[] outValues, int[] deviceStates) {
-        if (deviceStates.length != mDeviceStateConfig.length) {
-            throw new IllegalArgumentException(
-                    "Invalid number of tracked states: " + deviceStates.length
-                    + " expected: " + mDeviceStateConfig.length);
-        }
-        if (mDeviceStats != null) {
-            mDeviceStats.getStats(outValues, deviceStates);
-            return true;
-        }
-        return false;
-    }
-
-    boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
-        if (deviceStates.length != mDeviceStateConfig.length) {
-            throw new IllegalArgumentException(
-                    "Invalid number of tracked states: " + deviceStates.length
-                            + " expected: " + mDeviceStateConfig.length);
-        }
-        MultiStateStats stateStats = mStateStats.get(key);
-        if (stateStats != null) {
-            stateStats.getStats(outValues, deviceStates);
-            return true;
-        }
-        return false;
-    }
-
-    void forEachStateStatsKey(IntConsumer consumer) {
-        for (int i = mStateStats.size() - 1; i >= 0; i--) {
-            consumer.accept(mStateStats.keyAt(i));
-        }
-    }
-
-    boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
-        if (uidStates.length != mUidStateConfig.length) {
-            throw new IllegalArgumentException(
-                    "Invalid number of tracked states: " + uidStates.length
-                    + " expected: " + mUidStateConfig.length);
-        }
-        UidStats uidStats = mUidStats.get(uid);
-        if (uidStats != null && uidStats.stats != null) {
-            uidStats.stats.getStats(outValues, uidStates);
-            return true;
-        }
-        return false;
-    }
-
-    private void createDeviceStats(long timestampMs) {
-        if (mStatsFactory == null) {
-            if (mPowerStatsDescriptor == null) {
-                return;
-            }
-            mStatsFactory = new MultiStateStats.Factory(
-                    mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
-        }
-
-        mDeviceStats = mStatsFactory.create();
-        if (mPowerStatsTimestamp != UNKNOWN) {
-            timestampMs = mPowerStatsTimestamp;
-        }
-        if (timestampMs != UNKNOWN) {
-            for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
-                int state = mDeviceStates[stateId];
-                mDeviceStats.setState(stateId, state, timestampMs);
-                for (int i = mStateStats.size() - 1; i >= 0; i--) {
-                    MultiStateStats stateStats = mStateStats.valueAt(i);
-                    stateStats.setState(stateId, state, timestampMs);
-                }
-            }
-        }
-    }
-
-    private MultiStateStats createStateStats(int key, long timestampMs) {
-        if (mStateStatsFactory == null) {
-            if (mPowerStatsDescriptor == null) {
-                return null;
-            }
-            mStateStatsFactory = new MultiStateStats.Factory(
-                    mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig);
-        }
-
-        MultiStateStats stateStats = mStateStatsFactory.create();
-        mStateStats.put(key, stateStats);
-        if (mDeviceStats != null) {
-            stateStats.copyStatesFrom(mDeviceStats);
-        }
-
-        return stateStats;
-    }
-
-    private void createUidStats(UidStats uidStats, long timestampMs) {
-        if (mUidStatsFactory == null) {
-            if (mPowerStatsDescriptor == null) {
-                return;
-            }
-            mUidStatsFactory = new MultiStateStats.Factory(
-                    mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
-        }
-
-        uidStats.stats = mUidStatsFactory.create();
-
-        if (mPowerStatsTimestamp != UNKNOWN) {
-            timestampMs = mPowerStatsTimestamp;
-        }
-        if (timestampMs != UNKNOWN) {
-            for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
-                uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs);
-            }
-        }
-    }
-
-    void copyStatesFrom(PowerComponentAggregatedPowerStats source) {
-        if (source.mDeviceStates.length == mDeviceStates.length) {
-            System.arraycopy(source.mDeviceStates, 0, mDeviceStates, 0, mDeviceStates.length);
-            if (source.mDeviceStats != null) {
-                createDeviceStats(0);
-                if (mDeviceStats != null) {
-                    mDeviceStats.copyStatesFrom(source.mDeviceStats);
-                }
-            }
-        } else {
-            Slog.wtf(TAG, "State configurations have different lengths: "
-                    + source.mDeviceStates.length + " vs " + mDeviceStates.length);
-        }
-        for (int i = source.mUidStats.size() - 1; i >= 0; i--) {
-            int uid = source.mUidStats.keyAt(i);
-            UidStats sourceUidStats = source.mUidStats.valueAt(i);
-            if (sourceUidStats.states == null) {
-                continue;
-            }
-            UidStats uidStats = new UidStats();
-            uidStats.states = Arrays.copyOf(sourceUidStats.states, sourceUidStats.states.length);
-            if (sourceUidStats.stats != null) {
-                createUidStats(uidStats, 0);
-                if (uidStats.stats != null) {
-                    uidStats.stats.copyStatesFrom(sourceUidStats.stats);
-                }
-            }
-            mUidStats.put(uid, uidStats);
-        }
-    }
-
-    public void writeXml(TypedXmlSerializer serializer) throws IOException {
-        // No stats aggregated - can skip writing XML altogether
-        if (mPowerStatsDescriptor == null) {
-            return;
-        }
-
-        serializer.startTag(null, XML_TAG_POWER_COMPONENT);
-        serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
-        mPowerStatsDescriptor.writeXml(serializer);
-
-        if (mDeviceStats != null) {
-            serializer.startTag(null, XML_TAG_DEVICE_STATS);
-            mDeviceStats.writeXml(serializer);
-            serializer.endTag(null, XML_TAG_DEVICE_STATS);
-        }
-
-        for (int i = 0; i < mStateStats.size(); i++) {
-            serializer.startTag(null, XML_TAG_STATE_STATS);
-            serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i));
-            mStateStats.valueAt(i).writeXml(serializer);
-            serializer.endTag(null, XML_TAG_STATE_STATS);
-        }
-
-        for (int i = mUidStats.size() - 1; i >= 0; i--) {
-            int uid = mUidStats.keyAt(i);
-            UidStats uidStats = mUidStats.valueAt(i);
-            if (uidStats.stats != null) {
-                serializer.startTag(null, XML_TAG_UID_STATS);
-                serializer.attributeInt(null, XML_ATTR_UID, uid);
-                uidStats.stats.writeXml(serializer);
-                serializer.endTag(null, XML_TAG_UID_STATS);
-            }
-        }
-
-        serializer.endTag(null, XML_TAG_POWER_COMPONENT);
-        serializer.flush();
-    }
-
-    public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
-            IOException {
-        String outerTag = parser.getName();
-        int eventType = parser.getEventType();
-        while (eventType != XmlPullParser.END_DOCUMENT
-                && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
-            if (eventType == XmlPullParser.START_TAG) {
-                switch (parser.getName()) {
-                    case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
-                        mPowerStatsDescriptor = PowerStats.Descriptor.createFromXml(parser);
-                        if (mPowerStatsDescriptor == null) {
-                            return false;
-                        }
-                        break;
-                    case XML_TAG_DEVICE_STATS:
-                        if (mDeviceStats == null) {
-                            createDeviceStats(UNKNOWN);
-                        }
-                        if (!mDeviceStats.readFromXml(parser)) {
-                            return false;
-                        }
-                        break;
-                    case XML_TAG_STATE_STATS:
-                        int key = parser.getAttributeInt(null, XML_ATTR_KEY);
-                        MultiStateStats stats = mStateStats.get(key);
-                        if (stats == null) {
-                            stats = createStateStats(key, UNKNOWN);
-                        }
-                        if (!stats.readFromXml(parser)) {
-                            return false;
-                        }
-                        break;
-                    case XML_TAG_UID_STATS:
-                        int uid = parser.getAttributeInt(null, XML_ATTR_UID);
-                        UidStats uidStats = getUidStats(uid);
-                        if (uidStats.stats == null) {
-                            createUidStats(uidStats, UNKNOWN);
-                        }
-                        if (!uidStats.stats.readFromXml(parser)) {
-                            return false;
-                        }
-                        break;
-                }
-            }
-            eventType = parser.next();
-        }
-        return true;
-    }
-
-    void dumpDevice(IndentingPrintWriter ipw) {
-        if (mDeviceStats != null) {
-            dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null,
-                    mPowerStatsDescriptor.getDeviceStatsFormatter());
-        }
-
-        if (mStateStats.size() != 0) {
-            ipw.increaseIndent();
-            String header = mPowerStatsDescriptor.name + " states";
-            PowerStats.PowerStatsFormatter formatter =
-                    mPowerStatsDescriptor.getStateStatsFormatter();
-            for (int i = 0; i < mStateStats.size(); i++) {
-                int key = mStateStats.keyAt(i);
-                String stateLabel = mPowerStatsDescriptor.getStateLabel(key);
-                MultiStateStats stateStats = mStateStats.valueAt(i);
-                dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter);
-            }
-            ipw.decreaseIndent();
-        }
-    }
-
-    void dumpUid(IndentingPrintWriter ipw, int uid) {
-        UidStats uidStats = mUidStats.get(uid);
-        if (uidStats != null && uidStats.stats != null) {
-            dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null,
-                    mPowerStatsDescriptor.getUidStatsFormatter());
-        }
-    }
-
-    private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats,
-            String header, String additionalLabel,
-            PowerStats.PowerStatsFormatter statsFormatter) {
-        boolean[] firstLine = new boolean[]{true};
-        long[] values = new long[stats.getDimensionCount()];
-        MultiStateStats.States[] stateInfo = stats.getStates();
-        MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
-            stats.getStats(values, states);
-            boolean nonZero = false;
-            for (long value : values) {
-                if (value != 0) {
-                    nonZero = true;
-                    break;
-                }
-            }
-            if (!nonZero) {
-                return;
-            }
-
-            if (firstLine[0]) {
-                ipw.println(header);
-                ipw.increaseIndent();
-            }
-            firstLine[0] = false;
-            StringBuilder sb = new StringBuilder();
-            sb.append("(");
-            boolean first = true;
-            for (int i = 0; i < states.length; i++) {
-                if (stateInfo[i].isTracked()) {
-                    if (!first) {
-                        sb.append(" ");
-                    }
-                    first = false;
-                    sb.append(stateInfo[i].getLabels()[states[i]]);
-                }
-            }
-            if (additionalLabel != null) {
-                sb.append(" ").append(additionalLabel);
-            }
-            sb.append(") ").append(statsFormatter.format(values));
-            ipw.println(sb);
-        });
-        if (!firstLine[0]) {
-            ipw.decreaseIndent();
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringWriter sw = new StringWriter();
-        IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
-        ipw.increaseIndent();
-        dumpDevice(ipw);
-        ipw.decreaseIndent();
-
-        int[] uids = new int[mUidStats.size()];
-        for (int i = uids.length - 1; i >= 0; i--) {
-            uids[i] = mUidStats.keyAt(i);
-        }
-        Arrays.sort(uids);
-        for (int uid : uids) {
-            ipw.println(UserHandle.formatUid(uid));
-            ipw.increaseIndent();
-            dumpUid(ipw, uid);
-            ipw.decreaseIndent();
-        }
-
-        ipw.flush();
-
-        return sw.toString();
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
deleted file mode 100644
index 081e560..0000000
--- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java
+++ /dev/null
@@ -1,173 +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.server.power.stats;
-
-import android.annotation.NonNull;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.BatteryStatsHistoryIterator;
-
-import java.util.function.Consumer;
-
-/**
- * Power stats aggregator. It reads through portions of battery stats history, finds
- * relevant items (state changes, power stats etc) and produces one or more
- * {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history.
- */
-public class PowerStatsAggregator {
-    private static final long UNINITIALIZED = -1;
-    private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
-    private final BatteryStatsHistory mHistory;
-    private final SparseBooleanArray mEnabledComponents =
-            new SparseBooleanArray(BatteryConsumer.POWER_COMPONENT_COUNT + 10);
-    private AggregatedPowerStats mStats;
-    private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-    private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-
-    public PowerStatsAggregator(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
-            @NonNull BatteryStatsHistory history) {
-        mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
-        mHistory = history;
-    }
-
-    void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
-        synchronized (this) {
-            if (mStats != null) {
-                mStats = null;
-            }
-            mEnabledComponents.put(powerComponentId, enabled);
-        }
-    }
-
-    /**
-     * Iterates of the battery history and aggregates power stats between the specified times.
-     * The start and end are specified in the battery-stats monotonic time, which is the
-     * adjusted elapsed time found in HistoryItem.time.
-     * <p>
-     * The aggregated stats are sent to the consumer. One aggregation pass may produce
-     * multiple sets of aggregated stats if there was an incompatible change that occurred in the
-     * middle of the recorded battery history.
-     * <p>
-     * Note: the AggregatedPowerStats object is reused, so the consumer should fully consume
-     * the stats in the <code>accept</code> method and never cache it.
-     */
-    public void aggregatePowerStats(long startTimeMs, long endTimeMs,
-            Consumer<AggregatedPowerStats> consumer) {
-        synchronized (this) {
-            if (mStats == null) {
-                mStats = new AggregatedPowerStats(mAggregatedPowerStatsConfig, mEnabledComponents);
-            }
-
-            mStats.start(startTimeMs);
-
-            boolean clockUpdateAdded = false;
-            long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
-            long lastTime = 0;
-            int lastStates = 0xFFFFFFFF;
-            int lastStates2 = 0xFFFFFFFF;
-            try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) {
-                while (iterator.hasNext()) {
-                    BatteryStats.HistoryItem item = iterator.next();
-
-                    if (!clockUpdateAdded) {
-                        mStats.addClockUpdate(item.time, item.currentTime);
-                        if (baseTime == UNINITIALIZED) {
-                            baseTime = item.time;
-                        }
-                        clockUpdateAdded = true;
-                    } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
-                               || item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
-                        mStats.addClockUpdate(item.time, item.currentTime);
-                    }
-
-                    lastTime = item.time;
-
-                    int batteryState =
-                            (item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
-                                    ? AggregatedPowerStatsConfig.POWER_STATE_OTHER
-                                    : AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-                    if (batteryState != mCurrentBatteryState) {
-                        mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState,
-                                item.time);
-                        mCurrentBatteryState = batteryState;
-                    }
-
-                    int screenState =
-                            (item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
-                                    ? AggregatedPowerStatsConfig.SCREEN_STATE_ON
-                                    : AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-                    if (screenState != mCurrentScreenState) {
-                        mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState,
-                                item.time);
-                        mCurrentScreenState = screenState;
-                    }
-
-                    if ((item.states
-                            & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES)
-                            != lastStates
-                            || (item.states2
-                            & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2)
-                            != lastStates2) {
-                        mStats.noteStateChange(item);
-                        lastStates = item.states
-                                & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES;
-                        lastStates2 = item.states2
-                                & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2;
-                    }
-
-                    if (item.processStateChange != null) {
-                        mStats.setUidState(item.processStateChange.uid,
-                                AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
-                                item.processStateChange.processState, item.time);
-                    }
-
-                    if (item.powerStats != null) {
-                        if (!mStats.isCompatible(item.powerStats)) {
-                            if (lastTime > baseTime) {
-                                mStats.setDuration(lastTime - baseTime);
-                                mStats.finish(lastTime);
-                                consumer.accept(mStats);
-                            }
-                            mStats.reset();
-                            mStats.addClockUpdate(item.time, item.currentTime);
-                            baseTime = lastTime = item.time;
-                        }
-                        mStats.addPowerStats(item.powerStats, item.time);
-                    }
-                }
-            }
-            if (lastTime > baseTime) {
-                mStats.setDuration(lastTime - baseTime);
-                mStats.finish(lastTime);
-                consumer.accept(mStats);
-            }
-
-            mStats.reset();     // to free up memory
-        }
-    }
-
-    /**
-     * Reset to prepare for a new aggregation session.
-     */
-    public void reset() {
-        synchronized (this) {
-            mStats = null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
index f5b0005..291f0e3 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsCollector.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerAttribution;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.ConditionVariable;
@@ -26,13 +27,16 @@
 import android.power.PowerStatsInternal;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
+import android.util.SparseLongArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.PowerStatsLayout;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -41,6 +45,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
+import java.util.function.IntSupplier;
 
 /**
  * Collects snapshots of power-related system statistics.
@@ -53,6 +58,8 @@
     private static final String TAG = "PowerStatsCollector";
     private static final int MILLIVOLTS_PER_VOLT = 1000;
     private static final long POWER_STATS_ENERGY_CONSUMERS_TIMEOUT = 20000;
+    private static final long ENERGY_UNSPECIFIED = -1;
+
     private final Handler mHandler;
     protected final PowerStatsUidResolver mUidResolver;
     protected final Clock mClock;
@@ -235,46 +242,31 @@
         return (deltaEnergyUj * MILLIVOLTS_PER_VOLT + (avgVoltageMv / 2)) / avgVoltageMv;
     }
 
-    interface ConsumedEnergyRetriever {
+    public interface ConsumedEnergyRetriever {
+
         @NonNull
-        int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType, String name);
+        int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType);
 
         String getEnergyConsumerName(int energyConsumerId);
 
         @Nullable
         EnergyConsumerResult[] getConsumedEnergy(int[] energyConsumerIds);
 
-        @Nullable
-        default long[] getConsumedEnergyUws(int[] energyConsumerIds) {
-            EnergyConsumerResult[] results = getConsumedEnergy(energyConsumerIds);
-            if (results == null) {
-                return null;
-            }
-
-            long[] energy = new long[energyConsumerIds.length];
-            for (int i = 0; i < energyConsumerIds.length; i++) {
-                int id = energyConsumerIds[i];
-                for (EnergyConsumerResult result : results) {
-                    if (result.id == id) {
-                        energy[i] = result.energyUWs;
-                        break;
-                    }
-                }
-            }
-            return energy;
-        }
-
-        default int[] getEnergyConsumerIds(@EnergyConsumerType int energyConsumerType) {
-            return getEnergyConsumerIds(energyConsumerType, null);
-        }
+        /**
+         * Returns the last known battery/charger voltage in milli-volts.
+         */
+        int getVoltageMv();
     }
 
     static class ConsumedEnergyRetrieverImpl implements ConsumedEnergyRetriever {
         private final PowerStatsInternal mPowerStatsInternal;
+        private final IntSupplier mVoltageSupplier;
         private EnergyConsumer[] mEnergyConsumers;
 
-        ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal) {
+        ConsumedEnergyRetrieverImpl(PowerStatsInternal powerStatsInternal,
+                IntSupplier voltageSupplier) {
             mPowerStatsInternal = powerStatsInternal;
+            mVoltageSupplier = voltageSupplier;
         }
 
         private void ensureEnergyConsumers() {
@@ -293,8 +285,9 @@
             }
         }
 
+        @NonNull
         @Override
-        public int[] getEnergyConsumerIds(int energyConsumerType, String name) {
+        public int[] getEnergyConsumerIds(int energyConsumerType) {
             ensureEnergyConsumers();
 
             if (mEnergyConsumers.length == 0) {
@@ -303,8 +296,7 @@
 
             List<EnergyConsumer> energyConsumers = new ArrayList<>();
             for (EnergyConsumer energyConsumer : mEnergyConsumers) {
-                if (energyConsumer.type == energyConsumerType
-                        && (name == null || name.equals(energyConsumer.name))) {
+                if (energyConsumer.type == energyConsumerType) {
                     energyConsumers.add(energyConsumer);
                 }
             }
@@ -335,6 +327,11 @@
         }
 
         @Override
+        public int getVoltageMv() {
+            return mVoltageSupplier.getAsInt();
+        }
+
+        @Override
         public String getEnergyConsumerName(int energyConsumerId) {
             ensureEnergyConsumers();
 
@@ -368,4 +365,152 @@
             return sb.toString();
         }
     }
+
+    class ConsumedEnergyHelper implements PowerStatsUidResolver.Listener {
+        private final ConsumedEnergyRetriever mConsumedEnergyRetriever;
+        private final @EnergyConsumerType int mEnergyConsumerType;
+        private final boolean mPerUidAttributionSupported;
+
+        private boolean mIsInitialized;
+        private boolean mFirstCollection = true;
+        private int[] mEnergyConsumerIds;
+        private long[] mLastConsumedEnergyUws;
+        private final SparseLongArray mLastConsumerEnergyPerUid;
+        private int mLastVoltageMv;
+
+        ConsumedEnergyHelper(ConsumedEnergyRetriever consumedEnergyRetriever,
+                @EnergyConsumerType int energyConsumerType) {
+            mConsumedEnergyRetriever = consumedEnergyRetriever;
+            mEnergyConsumerType = energyConsumerType;
+            mPerUidAttributionSupported = false;
+            mLastConsumerEnergyPerUid = null;
+        }
+
+        ConsumedEnergyHelper(ConsumedEnergyRetriever consumedEnergyRetriever,
+                int energyConsumerId, boolean perUidAttributionSupported) {
+            mConsumedEnergyRetriever = consumedEnergyRetriever;
+            mEnergyConsumerType = EnergyConsumerType.OTHER;
+            mEnergyConsumerIds = new int[]{energyConsumerId};
+            mPerUidAttributionSupported = perUidAttributionSupported;
+            mLastConsumerEnergyPerUid = mPerUidAttributionSupported ? new SparseLongArray() : null;
+        }
+
+        private void ensureInitialized() {
+            if (!mIsInitialized) {
+                if (mEnergyConsumerIds == null) {
+                    mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
+                            mEnergyConsumerType);
+                }
+                mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
+                Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+                mUidResolver.addListener(this);
+                mIsInitialized = true;
+            }
+        }
+
+        int getEnergyConsumerCount() {
+            ensureInitialized();
+            return mEnergyConsumerIds.length;
+        }
+
+        boolean collectConsumedEnergy(PowerStats powerStats, PowerStatsLayout layout) {
+            ensureInitialized();
+
+            if (mEnergyConsumerIds.length == 0) {
+                return false;
+            }
+
+            int voltageMv = mConsumedEnergyRetriever.getVoltageMv();
+            int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
+            if (averageVoltage <= 0) {
+                Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
+                        + " mV) when querying energy consumers");
+                return false;
+            }
+
+            mLastVoltageMv = voltageMv;
+
+            EnergyConsumerResult[] energy =
+                    mConsumedEnergyRetriever.getConsumedEnergy(mEnergyConsumerIds);
+            System.out.println("mEnergyConsumerIds = " + Arrays.toString(mEnergyConsumerIds) + " "
+                    + "energy = "
+                    + Arrays.toString(energy));
+            if (energy == null) {
+                return false;
+            }
+
+            for (int i = 0; i < mEnergyConsumerIds.length; i++) {
+                populatePowerStats(powerStats, layout, energy, i, averageVoltage);
+            }
+            mFirstCollection = false;
+            return true;
+        }
+
+        private void populatePowerStats(PowerStats powerStats, PowerStatsLayout layout,
+                @NonNull EnergyConsumerResult[] energy, int energyConsumerIndex,
+                int averageVoltage) {
+            long consumedEnergy = energy[energyConsumerIndex].energyUWs;
+            long energyDelta = mLastConsumedEnergyUws[energyConsumerIndex] != ENERGY_UNSPECIFIED
+                    ? consumedEnergy - mLastConsumedEnergyUws[energyConsumerIndex] : 0;
+            mLastConsumedEnergyUws[energyConsumerIndex] = consumedEnergy;
+            if (energyDelta < 0) {
+                // Likely, restart of powerstats HAL
+                energyDelta = 0;
+            }
+
+            if (energyDelta == 0 && !mFirstCollection) {
+                return;
+            }
+
+            layout.setConsumedEnergy(powerStats.stats, energyConsumerIndex,
+                    uJtoUc(energyDelta, averageVoltage));
+
+            if (!mPerUidAttributionSupported) {
+                return;
+            }
+
+            EnergyConsumerAttribution[] perUid = energy[energyConsumerIndex].attribution;
+            if (perUid == null) {
+                return;
+            }
+
+            for (EnergyConsumerAttribution attribution : perUid) {
+                int uid = mUidResolver.mapUid(attribution.uid);
+                long lastEnergy = mLastConsumerEnergyPerUid.get(uid, ENERGY_UNSPECIFIED);
+                mLastConsumerEnergyPerUid.put(uid, attribution.energyUWs);
+                if (lastEnergy == ENERGY_UNSPECIFIED) {
+                    continue;
+                }
+                long deltaEnergy = attribution.energyUWs - lastEnergy;
+                if (deltaEnergy <= 0) {
+                    continue;
+                }
+
+                long[] uidStats = powerStats.uidStats.get(uid);
+                if (uidStats == null) {
+                    uidStats = new long[layout.getUidStatsArrayLength()];
+                    powerStats.uidStats.put(uid, uidStats);
+                }
+
+                layout.setUidConsumedEnergy(uidStats, energyConsumerIndex,
+                        layout.getUidConsumedEnergy(uidStats, energyConsumerIndex)
+                                + uJtoUc(deltaEnergy, averageVoltage));
+            }
+        }
+
+        @Override
+        public void onAfterIsolatedUidRemoved(int isolatedUid, int parentUid) {
+            if (mLastConsumerEnergyPerUid != null) {
+                mHandler.post(() -> mLastConsumerEnergyPerUid.delete(isolatedUid));
+            }
+        }
+
+        @Override
+        public void onIsolatedUidAdded(int isolatedUid, int parentUid) {
+        }
+
+        @Override
+        public void onBeforeIsolatedUidRemoved(int isolatedUid, int parentUid) {
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
deleted file mode 100644
index 281faf1..0000000
--- a/services/core/java/com/android/server/power/stats/PowerStatsExporter.java
+++ /dev/null
@@ -1,383 +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.server.power.stats;
-
-import android.os.AggregateBatteryConsumer;
-import android.os.BatteryConsumer;
-import android.os.BatteryUsageStats;
-import android.os.UidBatteryConsumer;
-import android.util.Slog;
-
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Given a time range, converts accumulated PowerStats to BatteryUsageStats.  Combines
- * stores spans of PowerStats with the yet-unprocessed tail of battery history.
- */
-public class PowerStatsExporter {
-    private static final String TAG = "PowerStatsExporter";
-    private final PowerStatsStore mPowerStatsStore;
-    private final PowerStatsAggregator mPowerStatsAggregator;
-    private final long mBatterySessionTimeSpanSlackMillis;
-    private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2);
-
-    public PowerStatsExporter(PowerStatsStore powerStatsStore,
-            PowerStatsAggregator powerStatsAggregator) {
-        this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS);
-    }
-
-    public PowerStatsExporter(PowerStatsStore powerStatsStore,
-            PowerStatsAggregator powerStatsAggregator,
-            long batterySessionTimeSpanSlackMillis) {
-        mPowerStatsStore = powerStatsStore;
-        mPowerStatsAggregator = powerStatsAggregator;
-        mBatterySessionTimeSpanSlackMillis = batterySessionTimeSpanSlackMillis;
-    }
-
-    /**
-     * Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated
-     * PowerStats, both stored in PowerStatsStore and not-yet processed.
-     */
-    public void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
-            long monotonicStartTime, long monotonicEndTime) {
-        synchronized (this) {
-            boolean hasStoredSpans = false;
-            long maxEndTime = monotonicStartTime;
-            List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
-            for (int i = spans.size() - 1; i >= 0; i--) {
-                PowerStatsSpan.Metadata metadata = spans.get(i);
-                if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
-                    continue;
-                }
-
-                List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
-                long spanMinTime = Long.MAX_VALUE;
-                long spanMaxTime = Long.MIN_VALUE;
-                for (int j = 0; j < timeFrames.size(); j++) {
-                    PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
-                    long startMonotonicTime = timeFrame.startMonotonicTime;
-                    long endMonotonicTime = startMonotonicTime + timeFrame.duration;
-                    if (startMonotonicTime < spanMinTime) {
-                        spanMinTime = startMonotonicTime;
-                    }
-                    if (endMonotonicTime > spanMaxTime) {
-                        spanMaxTime = endMonotonicTime;
-                    }
-                }
-
-                if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
-                    continue;
-                }
-
-                if (spanMaxTime > maxEndTime) {
-                    maxEndTime = spanMaxTime;
-                }
-
-                PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
-                        AggregatedPowerStatsSection.TYPE);
-                if (span == null) {
-                    Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
-                    continue;
-                }
-                List<PowerStatsSpan.Section> sections = span.getSections();
-                for (int k = 0; k < sections.size(); k++) {
-                    hasStoredSpans = true;
-                    PowerStatsSpan.Section section = sections.get(k);
-                    populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
-                            ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
-                }
-            }
-
-            if (!hasStoredSpans
-                    || maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
-                mPowerStatsAggregator.aggregatePowerStats(maxEndTime, monotonicEndTime,
-                        stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
-            }
-            mPowerStatsAggregator.reset();
-        }
-    }
-
-    private void populateBatteryUsageStatsBuilder(
-            BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) {
-        List<PowerComponentAggregatedPowerStats> powerComponentStats =
-                stats.getPowerComponentStats();
-        for (int i = powerComponentStats.size() - 1; i >= 0; i--) {
-            populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, powerComponentStats.get(i));
-        }
-    }
-
-    private static void populateBatteryUsageStatsBuilder(
-            BatteryUsageStats.Builder batteryUsageStatsBuilder,
-            PowerComponentAggregatedPowerStats powerComponentStats) {
-        PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
-        if (descriptor == null) {
-            return;
-        }
-        boolean isCustomComponent =
-                descriptor.powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-
-        PowerStatsLayout layout = new PowerStatsLayout();
-        layout.fromExtras(descriptor.extras);
-
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-        for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
-            if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
-                if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
-                    continue;
-                }
-            } else if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
-                continue;
-            }
-
-            for (int powerState = 0; powerState < BatteryConsumer.POWER_STATE_COUNT; powerState++) {
-                if (batteryUsageStatsBuilder.isPowerStateDataNeeded() && !isCustomComponent) {
-                    if (powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
-                        continue;
-                    }
-                } else if (powerState != BatteryConsumer.POWER_STATE_BATTERY) {
-                    continue;
-                }
-
-                populateAggregatedBatteryConsumer(batteryUsageStatsBuilder, powerComponentStats,
-                        layout, deviceStats, screenState, powerState);
-            }
-        }
-        if (layout.isUidPowerAttributionSupported()) {
-            populateBatteryConsumers(batteryUsageStatsBuilder,
-                    powerComponentStats, layout);
-        }
-    }
-
-    private static void populateAggregatedBatteryConsumer(
-            BatteryUsageStats.Builder batteryUsageStatsBuilder,
-            PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout,
-            long[] deviceStats, @BatteryConsumer.ScreenState int screenState,
-            @BatteryConsumer.PowerState int powerState) {
-        int powerComponentId = powerComponentStats.powerComponentId;
-        boolean isCustomComponent =
-                powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-
-        double[] totalPower = new double[1];
-        MultiStateStats.States.forEachTrackedStateCombination(
-                powerComponentStats.getConfig().getDeviceStateConfig(),
-                states -> {
-                    if (!areMatchingStates(states, screenState, powerState)) {
-                        return;
-                    }
-
-                    if (!powerComponentStats.getDeviceStats(deviceStats, states)) {
-                        return;
-                    }
-
-                    totalPower[0] += layout.getDevicePowerEstimate(deviceStats);
-                });
-
-        AggregateBatteryConsumer.Builder deviceScope =
-                batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
-        if (isCustomComponent) {
-            if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(powerComponentId)) {
-                deviceScope.addConsumedPowerForCustomComponent(powerComponentId, totalPower[0]);
-            }
-        } else {
-            BatteryConsumer.Key key = deviceScope.getKey(powerComponentId,
-                    BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState);
-            if (key != null) {
-                deviceScope.addConsumedPower(key, totalPower[0],
-                        BatteryConsumer.POWER_MODEL_UNDEFINED);
-            }
-            deviceScope.addConsumedPower(powerComponentId, totalPower[0],
-                    BatteryConsumer.POWER_MODEL_UNDEFINED);
-        }
-    }
-
-    private static void populateBatteryConsumers(
-            BatteryUsageStats.Builder batteryUsageStatsBuilder,
-            PowerComponentAggregatedPowerStats powerComponentStats,
-            PowerStatsLayout layout) {
-        AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig();
-        int powerComponentId = powerComponent.getPowerComponentId();
-        boolean isCustomComponent =
-                powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
-        PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-
-        // TODO(b/347101393): add support for per-procstate breakdown for custom energy consumers
-        boolean breakDownByProcState = batteryUsageStatsBuilder.isProcessStateDataNeeded()
-                && powerComponent
-                .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE].isTracked()
-                && !isCustomComponent;
-
-        ArrayList<Integer> uids = new ArrayList<>();
-        powerComponentStats.collectUids(uids);
-        for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
-            if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
-                if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
-                    continue;
-                }
-            } else if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
-                continue;
-            }
-
-            for (int powerState = 0; powerState < BatteryConsumer.POWER_STATE_COUNT; powerState++) {
-                if (batteryUsageStatsBuilder.isPowerStateDataNeeded() && !isCustomComponent) {
-                    if (powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
-                        continue;
-                    }
-                } else if (powerState != BatteryConsumer.POWER_STATE_BATTERY) {
-                    continue;
-                }
-
-                populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponentStats, layout,
-                        uids, powerComponent, uidStats, breakDownByProcState, screenState,
-                        powerState);
-            }
-        }
-    }
-
-    private static void populateUidBatteryConsumers(
-            BatteryUsageStats.Builder batteryUsageStatsBuilder,
-            PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout,
-            List<Integer> uids, AggregatedPowerStatsConfig.PowerComponent powerComponent,
-            long[] uidStats, boolean breakDownByProcState,
-            @BatteryConsumer.ScreenState int screenState,
-            @BatteryConsumer.PowerState int powerState) {
-        int powerComponentId = powerComponentStats.powerComponentId;
-        double[] powerByProcState =
-                new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
-        double powerAllApps = 0;
-        for (int uid : uids) {
-            UidBatteryConsumer.Builder builder =
-                    batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
-
-            Arrays.fill(powerByProcState, 0);
-
-            MultiStateStats.States.forEachTrackedStateCombination(
-                    powerComponent.getUidStateConfig(),
-                    states -> {
-                        if (!areMatchingStates(states, screenState, powerState)) {
-                            return;
-                        }
-
-                        if (!powerComponentStats.getUidStats(uidStats, uid, states)) {
-                            return;
-                        }
-
-                        double power = layout.getUidPowerEstimate(uidStats);
-                        int procState = breakDownByProcState
-                                ? states[AggregatedPowerStatsConfig.STATE_PROCESS_STATE]
-                                : BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
-                        powerByProcState[procState] += power;
-                    });
-
-            double powerAllProcStates = 0;
-            for (int procState = 0; procState < powerByProcState.length; procState++) {
-                double power = powerByProcState[procState];
-                if (power == 0) {
-                    continue;
-                }
-                powerAllProcStates += power;
-                if (breakDownByProcState
-                        && procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
-                    if (batteryUsageStatsBuilder.isPowerStateDataNeeded()) {
-                        builder.addConsumedPower(
-                                builder.getKey(powerComponentId, procState, screenState,
-                                        powerState),
-                                power, BatteryConsumer.POWER_MODEL_UNDEFINED);
-                    } else {
-                        builder.addConsumedPower(
-                                builder.getKey(powerComponentId, procState, screenState,
-                                        BatteryConsumer.POWER_STATE_UNSPECIFIED),
-                                power, BatteryConsumer.POWER_MODEL_UNDEFINED);
-                    }
-                }
-            }
-            if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
-                if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(powerComponentId)) {
-                    builder.addConsumedPowerForCustomComponent(powerComponentId,
-                            powerAllProcStates);
-                }
-            } else {
-                builder.addConsumedPower(powerComponentId, powerAllProcStates,
-                        BatteryConsumer.POWER_MODEL_UNDEFINED);
-            }
-            powerAllApps += powerAllProcStates;
-        }
-
-        AggregateBatteryConsumer.Builder allAppsScope =
-                batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
-                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
-        if (powerComponentId >= BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID) {
-            if (batteryUsageStatsBuilder.isSupportedCustomPowerComponent(powerComponentId)) {
-                allAppsScope.addConsumedPowerForCustomComponent(powerComponentId, powerAllApps);
-            }
-        } else {
-            BatteryConsumer.Key key = allAppsScope.getKey(powerComponentId,
-                    BatteryConsumer.PROCESS_STATE_ANY, screenState, powerState);
-            if (key != null) {
-                allAppsScope.addConsumedPower(key, powerAllApps,
-                        BatteryConsumer.POWER_MODEL_UNDEFINED);
-            }
-            allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
-                    BatteryConsumer.POWER_MODEL_UNDEFINED);
-        }
-    }
-
-    private static boolean areMatchingStates(int[] states,
-            @BatteryConsumer.ScreenState int screenState,
-            @BatteryConsumer.PowerState int powerState) {
-        switch (screenState) {
-            case BatteryConsumer.SCREEN_STATE_ON:
-                if (states[AggregatedPowerStatsConfig.STATE_SCREEN]
-                        != AggregatedPowerStatsConfig.SCREEN_STATE_ON) {
-                    return false;
-                }
-                break;
-            case BatteryConsumer.SCREEN_STATE_OTHER:
-                if (states[AggregatedPowerStatsConfig.STATE_SCREEN]
-                        != AggregatedPowerStatsConfig.SCREEN_STATE_OTHER) {
-                    return false;
-                }
-                break;
-        }
-
-        switch (powerState) {
-            case BatteryConsumer.POWER_STATE_BATTERY:
-                if (states[AggregatedPowerStatsConfig.STATE_POWER]
-                        != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
-                    return false;
-                }
-                break;
-            case BatteryConsumer.POWER_STATE_OTHER:
-                if (states[AggregatedPowerStatsConfig.STATE_POWER]
-                        != AggregatedPowerStatsConfig.POWER_STATE_OTHER) {
-                    return false;
-                }
-                break;
-        }
-        return true;
-    }
-
-    void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
-        mPowerStatsAggregator.setPowerComponentEnabled(powerComponentId, enabled);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
deleted file mode 100644
index 62abfc6..0000000
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ /dev/null
@@ -1,360 +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.server.power.stats;
-
-import android.os.PersistableBundle;
-import android.util.Slog;
-
-import com.android.internal.os.PowerStats;
-
-/**
- * Captures the positions and lengths of sections of the stats array, such as usage duration,
- * power usage estimates etc.
- */
-public class PowerStatsLayout {
-    private static final String TAG = "PowerStatsLayout";
-    private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
-    private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
-    private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
-    private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
-    private static final String EXTRA_UID_DURATION_POSITION = "ud";
-    private static final String EXTRA_UID_ENERGY_CONSUMERS_POSITION = "ue";
-    private static final String EXTRA_UID_ENERGY_CONSUMERS_COUNT = "uec";
-    private static final String EXTRA_UID_POWER_POSITION = "up";
-
-    protected static final int UNSUPPORTED = -1;
-    protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
-    protected static final int FLAG_OPTIONAL = 1;
-    protected static final int FLAG_HIDDEN = 2;
-    protected static final int FLAG_FORMAT_AS_POWER = 4;
-
-    private int mDeviceStatsArrayLength;
-    private int mStateStatsArrayLength;
-    private int mUidStatsArrayLength;
-
-    private StringBuilder mDeviceFormat = new StringBuilder();
-    private StringBuilder mStateFormat = new StringBuilder();
-    private StringBuilder mUidFormat = new StringBuilder();
-
-    protected int mDeviceDurationPosition = UNSUPPORTED;
-    private int mDeviceEnergyConsumerPosition;
-    private int mDeviceEnergyConsumerCount;
-    private int mDevicePowerEstimatePosition = UNSUPPORTED;
-    private int mUidDurationPosition = UNSUPPORTED;
-    private int mUidEnergyConsumerPosition = UNSUPPORTED;
-    private int mUidEnergyConsumerCount;
-    private int mUidPowerEstimatePosition = UNSUPPORTED;
-
-    public PowerStatsLayout() {
-    }
-
-    public PowerStatsLayout(PowerStats.Descriptor descriptor) {
-        fromExtras(descriptor.extras);
-    }
-
-    public int getDeviceStatsArrayLength() {
-        return mDeviceStatsArrayLength;
-    }
-
-    public int getStateStatsArrayLength() {
-        return mStateStatsArrayLength;
-    }
-
-    public int getUidStatsArrayLength() {
-        return mUidStatsArrayLength;
-    }
-
-    /**
-     * @param label should not contain either spaces or colons
-     */
-    private void appendFormat(StringBuilder sb, int position, int length, String label,
-            int flags) {
-        if ((flags & FLAG_HIDDEN) != 0) {
-            return;
-        }
-
-        if (!sb.isEmpty()) {
-            sb.append(' ');
-        }
-
-        sb.append(label).append(':');
-        sb.append(position);
-        if (length != 1) {
-            sb.append('[').append(length).append(']');
-        }
-        if ((flags & FLAG_FORMAT_AS_POWER) != 0) {
-            sb.append('p');
-        }
-        if ((flags & FLAG_OPTIONAL) != 0) {
-            sb.append('?');
-        }
-    }
-
-    protected int addDeviceSection(int length, String label, int flags) {
-        int position = mDeviceStatsArrayLength;
-        mDeviceStatsArrayLength += length;
-        appendFormat(mDeviceFormat, position, length, label, flags);
-        return position;
-    }
-
-    protected int addDeviceSection(int length, String label) {
-        return addDeviceSection(length, label, 0);
-    }
-
-    protected int addStateSection(int length, String label, int flags) {
-        int position = mStateStatsArrayLength;
-        mStateStatsArrayLength += length;
-        appendFormat(mStateFormat, position, length, label, flags);
-        return position;
-    }
-
-    protected int addStateSection(int length, String label) {
-        return addStateSection(length, label, 0);
-    }
-
-
-    protected int addUidSection(int length, String label, int flags) {
-        int position = mUidStatsArrayLength;
-        mUidStatsArrayLength += length;
-        appendFormat(mUidFormat, position, length, label, flags);
-        return position;
-    }
-
-    protected int addUidSection(int length, String label) {
-        return addUidSection(length, label, 0);
-    }
-
-    /**
-     * Declare that the stats array has a section capturing usage duration
-     */
-    public void addDeviceSectionUsageDuration() {
-        mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
-    }
-
-    /**
-     * Saves the usage duration in the corresponding <code>stats</code> element.
-     */
-    public void setUsageDuration(long[] stats, long value) {
-        stats[mDeviceDurationPosition] = value;
-    }
-
-    /**
-     * Extracts the usage duration from the corresponding <code>stats</code> element.
-     */
-    public long getUsageDuration(long[] stats) {
-        return stats[mDeviceDurationPosition];
-    }
-
-    /**
-     * Declares that the stats array has a section capturing EnergyConsumer data from
-     * PowerStatsService.
-     */
-    public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
-        mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy",
-                FLAG_OPTIONAL);
-        mDeviceEnergyConsumerCount = energyConsumerCount;
-    }
-
-    public int getEnergyConsumerCount() {
-        return mDeviceEnergyConsumerCount;
-    }
-
-    /**
-     * Saves the accumulated energy for the specified rail the corresponding
-     * <code>stats</code> element.
-     */
-    public void setConsumedEnergy(long[] stats, int index, long energy) {
-        stats[mDeviceEnergyConsumerPosition + index] = energy;
-    }
-
-    /**
-     * Extracts the EnergyConsumer data from a device stats array for the specified
-     * EnergyConsumer.
-     */
-    public long getConsumedEnergy(long[] stats, int index) {
-        return stats[mDeviceEnergyConsumerPosition + index];
-    }
-
-    /**
-     * Declare that the stats array has a section capturing a power estimate
-     */
-    public void addDeviceSectionPowerEstimate() {
-        mDevicePowerEstimatePosition = addDeviceSection(1, "power",
-                FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
-    }
-
-    /**
-     * Converts the supplied mAh power estimate to a long and saves it in the corresponding
-     * element of <code>stats</code>.
-     */
-    public void setDevicePowerEstimate(long[] stats, double power) {
-        stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
-    }
-
-    /**
-     * Extracts the power estimate from a device stats array and converts it to mAh.
-     */
-    public double getDevicePowerEstimate(long[] stats) {
-        return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
-    }
-
-    /**
-     * Declare that the UID stats array has a section capturing usage duration
-     */
-    public void addUidSectionUsageDuration() {
-        mUidDurationPosition = addUidSection(1, "time");
-    }
-
-    /**
-     * Declare that the UID stats array has a section capturing a power estimate
-     */
-    public void addUidSectionPowerEstimate() {
-        mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
-    }
-
-    /**
-     * Returns true if power for this component is attributed to UIDs (apps).
-     */
-    public boolean isUidPowerAttributionSupported() {
-        return mUidPowerEstimatePosition != UNSUPPORTED;
-    }
-
-    /**
-     * Saves usage duration it in the corresponding element of <code>stats</code>.
-     */
-    public void setUidUsageDuration(long[] stats, long durationMs) {
-        stats[mUidDurationPosition] = durationMs;
-    }
-
-    /**
-     * Extracts the usage duration from a UID stats array.
-     */
-    public long getUidUsageDuration(long[] stats) {
-        return stats[mUidDurationPosition];
-    }
-
-    /**
-     * Declares that the UID stats array has a section capturing EnergyConsumer data from
-     * PowerStatsService.
-     */
-    public void addUidSectionEnergyConsumers(int energyConsumerCount) {
-        mUidEnergyConsumerPosition = addUidSection(energyConsumerCount, "energy",
-                FLAG_OPTIONAL);
-        mUidEnergyConsumerCount = energyConsumerCount;
-    }
-
-    public int getUidEnergyConsumerCount() {
-        return mUidEnergyConsumerCount;
-    }
-
-    /**
-     * Saves the accumulated energy for the specified rail the corresponding
-     * <code>stats</code> element.
-     */
-    public void setUidConsumedEnergy(long[] stats, int index, long energy) {
-        stats[mUidEnergyConsumerPosition + index] = energy;
-    }
-
-    /**
-     * Extracts the EnergyConsumer data from a uid stats array for the specified
-     * EnergyConsumer.
-     */
-    public long getUidConsumedEnergy(long[] stats, int index) {
-        return stats[mUidEnergyConsumerPosition + index];
-    }
-
-    /**
-     * Converts the supplied mAh power estimate to a long and saves it in the corresponding
-     * element of <code>stats</code>.
-     */
-    public void setUidPowerEstimate(long[] stats, double power) {
-        stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
-    }
-
-    /**
-     * Extracts the power estimate from a UID stats array and converts it to mAh.
-     */
-    public double getUidPowerEstimate(long[] stats) {
-        return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
-    }
-
-    /**
-     * Copies the elements of the stats array layout into <code>extras</code>
-     */
-    public void toExtras(PersistableBundle extras) {
-        extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
-        extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION,
-                mDeviceEnergyConsumerPosition);
-        extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT,
-                mDeviceEnergyConsumerCount);
-        extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
-        extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
-        extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
-        extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
-        extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
-        extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
-        extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
-        extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
-    }
-
-    /**
-     * Retrieves elements of the stats array layout from <code>extras</code>
-     */
-    public void fromExtras(PersistableBundle extras) {
-        mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
-        mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
-        mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
-        mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
-        mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
-        mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
-        mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
-        mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
-    }
-
-    protected void putIntArray(PersistableBundle extras, String key, int[] array) {
-        if (array == null) {
-            return;
-        }
-
-        StringBuilder sb = new StringBuilder();
-        for (int value : array) {
-            if (!sb.isEmpty()) {
-                sb.append(',');
-            }
-            sb.append(value);
-        }
-        extras.putString(key, sb.toString());
-    }
-
-    protected int[] getIntArray(PersistableBundle extras, String key) {
-        String string = extras.getString(key);
-        if (string == null) {
-            return null;
-        }
-        String[] values = string.trim().split(",");
-        int[] result = new int[values.length];
-        for (int i = 0; i < values.length; i++) {
-            try {
-                result[i] = Integer.parseInt(values[i]);
-            } catch (NumberFormatException e) {
-                Slog.wtf(TAG, "Invalid CSV format: " + string);
-                return null;
-            }
-        }
-        return result;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
deleted file mode 100644
index c81c7ff..0000000
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ /dev/null
@@ -1,342 +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.server.power.stats;
-
-import static com.android.server.power.stats.MultiStateStats.STATE_DOES_NOT_EXIST;
-import static com.android.server.power.stats.MultiStateStats.States.findTrackedStateByName;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.BatteryStats;
-import android.util.Log;
-
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/*
- * The power estimation algorithm used by PowerStatsProcessor can roughly be
- * described like this:
- *
- * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
- * a metric such as CPU time-in-state.
- *
- * 2. Combine estimates obtain in step 1, aggregating across states that are *not* tracked
- * per UID.
- *
- * 2. For each UID, compute the proportion of the combined estimates in each state
- * and attribute the corresponding portion of the total power estimate in that state to the UID.
- */
-public abstract class PowerStatsProcessor {
-    private static final String TAG = "PowerStatsProcessor";
-
-    private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
-
-    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-    }
-
-    void noteStateChange(PowerComponentAggregatedPowerStats stats,
-            BatteryStats.HistoryItem item) {
-    }
-
-    void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
-            long timestampMs) {
-        stats.addPowerStats(powerStats, timestampMs);
-    }
-
-    abstract void finish(PowerComponentAggregatedPowerStats stats, long timestampMs);
-
-    protected static class PowerEstimationPlan {
-        private final AggregatedPowerStatsConfig.PowerComponent mConfig;
-        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
-        public List<CombinedDeviceStateEstimate> combinedDeviceStateEstimations = new ArrayList<>();
-        public List<UidStateEstimate> uidStateEstimates = new ArrayList<>();
-
-        public PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config) {
-            mConfig = config;
-            addDeviceStateEstimations();
-            combineDeviceStateEstimations();
-            addUidStateEstimations();
-        }
-
-        private void addDeviceStateEstimations() {
-            MultiStateStats.States[] config = mConfig.getDeviceStateConfig();
-            int[][] deviceStateCombinations = getAllTrackedStateCombinations(config);
-            for (int[] deviceStateCombination : deviceStateCombinations) {
-                deviceStateEstimations.add(
-                        new DeviceStateEstimation(config, deviceStateCombination));
-            }
-        }
-
-        private void combineDeviceStateEstimations() {
-            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
-            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
-            MultiStateStats.States[] deviceStatesTrackedPerUid =
-                    new MultiStateStats.States[deviceStateConfig.length];
-
-            for (int i = 0; i < deviceStateConfig.length; i++) {
-                if (!deviceStateConfig[i].isTracked()) {
-                    continue;
-                }
-
-                int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName());
-                if (index != STATE_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
-                    deviceStatesTrackedPerUid[i] = deviceStateConfig[i];
-                }
-            }
-
-            combineDeviceStateEstimationsRecursively(deviceStateConfig, deviceStatesTrackedPerUid,
-                    new int[deviceStateConfig.length], 0);
-        }
-
-        private void combineDeviceStateEstimationsRecursively(
-                MultiStateStats.States[] deviceStateConfig,
-                MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state) {
-            if (state >= deviceStateConfig.length) {
-                DeviceStateEstimation dse = getDeviceStateEstimate(stateValues);
-                CombinedDeviceStateEstimate cdse = getCombinedDeviceStateEstimate(
-                        deviceStatesTrackedPerUid, stateValues);
-                if (cdse == null) {
-                    cdse = new CombinedDeviceStateEstimate(deviceStatesTrackedPerUid, stateValues);
-                    combinedDeviceStateEstimations.add(cdse);
-                }
-                cdse.deviceStateEstimations.add(dse);
-                return;
-            }
-
-            if (deviceStateConfig[state].isTracked()) {
-                for (int stateValue = 0;
-                        stateValue < deviceStateConfig[state].getLabels().length;
-                        stateValue++) {
-                    stateValues[state] = stateValue;
-                    combineDeviceStateEstimationsRecursively(deviceStateConfig,
-                            deviceStatesTrackedPerUid, stateValues, state + 1);
-                }
-            } else {
-                combineDeviceStateEstimationsRecursively(deviceStateConfig,
-                        deviceStatesTrackedPerUid, stateValues, state + 1);
-            }
-        }
-
-        private void addUidStateEstimations() {
-            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
-            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
-            MultiStateStats.States[] uidStatesTrackedForDevice =
-                    new MultiStateStats.States[uidStateConfig.length];
-            MultiStateStats.States[] uidStatesNotTrackedForDevice =
-                    new MultiStateStats.States[uidStateConfig.length];
-
-            for (int i = 0; i < uidStateConfig.length; i++) {
-                if (!uidStateConfig[i].isTracked()) {
-                    continue;
-                }
-
-                int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName());
-                if (index != STATE_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
-                    uidStatesTrackedForDevice[i] = uidStateConfig[i];
-                } else {
-                    uidStatesNotTrackedForDevice[i] = uidStateConfig[i];
-                }
-            }
-
-            @AggregatedPowerStatsConfig.TrackedState
-            int[][] uidStateCombinations = getAllTrackedStateCombinations(uidStateConfig);
-            for (int[] stateValues : uidStateCombinations) {
-                CombinedDeviceStateEstimate combined =
-                        getCombinedDeviceStateEstimate(uidStatesTrackedForDevice, stateValues);
-                if (combined == null) {
-                    // This is not supposed to be possible
-                    Log.wtf(TAG, "Mismatch in UID and combined device states: "
-                                 + concatLabels(uidStatesTrackedForDevice, stateValues));
-                    continue;
-                }
-                UidStateEstimate uidStateEstimate = getUidStateEstimate(combined);
-                if (uidStateEstimate == null) {
-                    uidStateEstimate = new UidStateEstimate(combined, uidStatesNotTrackedForDevice);
-                    uidStateEstimates.add(uidStateEstimate);
-                }
-                uidStateEstimate.proportionalEstimates.add(
-                        new UidStateProportionalEstimate(stateValues));
-            }
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder sb = new StringBuilder();
-            sb.append("Step 1. Compute device-wide power estimates for state combinations:\n");
-            for (DeviceStateEstimation deviceStateEstimation : deviceStateEstimations) {
-                sb.append("    ").append(deviceStateEstimation.id).append("\n");
-            }
-            sb.append("Step 2. Combine device-wide estimates that are untracked per UID:\n");
-            boolean any = false;
-            for (CombinedDeviceStateEstimate cdse : combinedDeviceStateEstimations) {
-                if (cdse.deviceStateEstimations.size() <= 1) {
-                    continue;
-                }
-                any = true;
-                sb.append("    ").append(cdse.id).append(": ");
-                for (int i = 0; i < cdse.deviceStateEstimations.size(); i++) {
-                    if (i != 0) {
-                        sb.append(" + ");
-                    }
-                    sb.append(cdse.deviceStateEstimations.get(i).id);
-                }
-                sb.append("\n");
-            }
-            if (!any) {
-                sb.append("    N/A\n");
-            }
-            sb.append("Step 3. Proportionally distribute power estimates to UIDs:\n");
-            for (UidStateEstimate uidStateEstimate : uidStateEstimates) {
-                sb.append("    ").append(uidStateEstimate.combinedDeviceStateEstimate.id)
-                        .append("\n        among: ");
-                for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
-                    UidStateProportionalEstimate uspe =
-                            uidStateEstimate.proportionalEstimates.get(i);
-                    if (i != 0) {
-                        sb.append(", ");
-                    }
-                    sb.append(concatLabels(uidStateEstimate.states, uspe.stateValues));
-                }
-                sb.append("\n");
-            }
-            return sb.toString();
-        }
-
-        @Nullable
-        public DeviceStateEstimation getDeviceStateEstimate(int[] stateValues) {
-            String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues);
-            for (int i = 0; i < deviceStateEstimations.size(); i++) {
-                DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i);
-                if (deviceStateEstimation.id.equals(label)) {
-                    return deviceStateEstimation;
-                }
-            }
-            return null;
-        }
-
-        public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate(
-                MultiStateStats.States[] deviceStates, int[] stateValues) {
-            String label = concatLabels(deviceStates, stateValues);
-            for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) {
-                CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i);
-                if (cdse.id.equals(label)) {
-                    return cdse;
-                }
-            }
-            return null;
-        }
-
-        public UidStateEstimate getUidStateEstimate(CombinedDeviceStateEstimate combined) {
-            for (int i = 0; i < uidStateEstimates.size(); i++) {
-                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
-                if (uidStateEstimate.combinedDeviceStateEstimate == combined) {
-                    return uidStateEstimate;
-                }
-            }
-            return null;
-        }
-
-        public void resetIntermediates() {
-            for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
-                deviceStateEstimations.get(i).intermediates = null;
-            }
-            for (int i = combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-                combinedDeviceStateEstimations.get(i).intermediates = null;
-            }
-            for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
-                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
-                List<UidStateProportionalEstimate> proportionalEstimates =
-                        uidStateEstimate.proportionalEstimates;
-                for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
-                    proportionalEstimates.get(j).intermediates = null;
-                }
-            }
-        }
-    }
-
-    protected static class DeviceStateEstimation {
-        public final String id;
-        public final int[] stateValues;
-        public Object intermediates;
-
-        public DeviceStateEstimation(MultiStateStats.States[] config, int[] stateValues) {
-            id = concatLabels(config, stateValues);
-            this.stateValues = stateValues;
-        }
-    }
-
-    protected static class CombinedDeviceStateEstimate {
-        public final String id;
-        public final int[] stateValues;
-        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
-        public Object intermediates;
-
-        public CombinedDeviceStateEstimate(MultiStateStats.States[] config, int[] stateValues) {
-            this.stateValues = Arrays.copyOf(stateValues, stateValues.length);
-            id = concatLabels(config, stateValues);
-        }
-    }
-
-    protected static class UidStateEstimate {
-        public final MultiStateStats.States[] states;
-        public CombinedDeviceStateEstimate combinedDeviceStateEstimate;
-        public List<UidStateProportionalEstimate> proportionalEstimates = new ArrayList<>();
-
-        public UidStateEstimate(CombinedDeviceStateEstimate combined,
-                MultiStateStats.States[] states) {
-            combinedDeviceStateEstimate = combined;
-            this.states = states;
-        }
-    }
-
-    protected static class UidStateProportionalEstimate {
-        public final int[] stateValues;
-        public Object intermediates;
-
-        protected UidStateProportionalEstimate(int[] stateValues) {
-            this.stateValues = stateValues;
-        }
-    }
-
-    @NonNull
-    private static String concatLabels(MultiStateStats.States[] config, int[] stateValues) {
-        List<String> labels = new ArrayList<>();
-        for (int state = 0; state < config.length; state++) {
-            if (config[state] != null && config[state].isTracked()) {
-                labels.add(config[state].getName()
-                           + "=" + config[state].getLabels()[stateValues[state]]);
-            }
-        }
-        Collections.sort(labels);
-        return labels.toString();
-    }
-
-    private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) {
-        List<int[]> combinations = new ArrayList<>();
-        MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> {
-            combinations.add(Arrays.copyOf(stateValues, stateValues.length));
-        });
-        return combinations.toArray(new int[combinations.size()][0]);
-    }
-
-    public static double uCtoMah(long chargeUC) {
-        return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
index abe4c0c..38ca087 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsScheduler.java
@@ -23,6 +23,7 @@
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
 import com.android.internal.os.Clock;
 import com.android.internal.os.MonotonicClock;
 
@@ -51,7 +52,8 @@
     private final Handler mHandler;
     private final Runnable mPowerStatsCollector;
     private final Supplier<Long> mEarliestAvailableBatteryHistoryTimeMs;
-    private final PowerStatsAggregator mPowerStatsAggregator;
+    private final BatteryStatsHistory mBatteryStatsHistory;
+    private final PowerAttributor mPowerAttributor;
     private long mLastSavedSpanEndMonotonicTime;
 
     /**
@@ -66,12 +68,13 @@
     }
 
     public PowerStatsScheduler(Runnable powerStatsCollector,
-            PowerStatsAggregator powerStatsAggregator,
+            BatteryStatsHistory batteryStatsHistory, PowerAttributor powerAttributor,
             @DurationMillisLong long aggregatedPowerStatsSpanDuration,
             @DurationMillisLong long powerStatsAggregationPeriod, PowerStatsStore powerStatsStore,
             AlarmScheduler alarmScheduler, Clock clock, MonotonicClock monotonicClock,
             Supplier<Long> earliestAvailableBatteryHistoryTimeMs, Handler handler) {
-        mPowerStatsAggregator = powerStatsAggregator;
+        mBatteryStatsHistory = batteryStatsHistory;
+        mPowerAttributor = powerAttributor;
         mAggregatedPowerStatsSpanDuration = aggregatedPowerStatsSpanDuration;
         mPowerStatsAggregationPeriod = powerStatsAggregationPeriod;
         mPowerStatsStore = powerStatsStore;
@@ -123,12 +126,8 @@
         long endTimeMs = alignToWallClock(startTime + mAggregatedPowerStatsSpanDuration,
                 mAggregatedPowerStatsSpanDuration, currentMonotonicTime, currentTimeMillis);
         while (endTimeMs <= currentMonotonicTime) {
-            mPowerStatsAggregator.aggregatePowerStats(startTime, endTimeMs,
-                    stats -> {
-                        storeAggregatedPowerStats(stats);
-                        mLastSavedSpanEndMonotonicTime = stats.getStartTime() + stats.getDuration();
-                    });
-
+            mLastSavedSpanEndMonotonicTime = mPowerAttributor.storeEstimatedPowerConsumption(
+                    mBatteryStatsHistory, startTime, endTimeMs);
             startTime = endTimeMs;
             endTimeMs += mAggregatedPowerStatsSpanDuration;
         }
@@ -153,15 +152,8 @@
             mPowerStatsStore.dump(ipw);
             // Aggregate the remainder of power stats and dump the results without storing them yet.
             long powerStoreEndMonotonicTime = getLastSavedSpanEndMonotonicTime();
-            mPowerStatsAggregator.aggregatePowerStats(powerStoreEndMonotonicTime,
-                    MonotonicClock.UNDEFINED,
-                    stats -> {
-                        // Create a PowerStatsSpan for consistency of the textual output
-                        PowerStatsSpan span = PowerStatsStore.createPowerStatsSpan(stats);
-                        if (span != null) {
-                            span.dump(ipw);
-                        }
-                    });
+            mPowerAttributor.dumpEstimatedPowerConsumption(ipw, mBatteryStatsHistory,
+                    powerStoreEndMonotonicTime, MonotonicClock.UNDEFINED);
         });
 
         awaitCompletion();
@@ -223,28 +215,13 @@
     }
 
     private long getLastSavedSpanEndMonotonicTime() {
-        if (mLastSavedSpanEndMonotonicTime != 0) {
-            return mLastSavedSpanEndMonotonicTime;
-        }
-
-        mLastSavedSpanEndMonotonicTime = -1;
-        for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
-            if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
-                for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
-                    long endMonotonicTime = timeFrame.startMonotonicTime + timeFrame.duration;
-                    if (endMonotonicTime > mLastSavedSpanEndMonotonicTime) {
-                        mLastSavedSpanEndMonotonicTime = endMonotonicTime;
-                    }
-                }
-            }
+        if (mLastSavedSpanEndMonotonicTime == 0) {
+            mLastSavedSpanEndMonotonicTime =
+                    mPowerAttributor.getLastSavedEstimatesPowerConsumptionTimestamp();
         }
         return mLastSavedSpanEndMonotonicTime;
     }
 
-    private void storeAggregatedPowerStats(AggregatedPowerStats stats) {
-        mPowerStatsStore.storeAggregatedPowerStats(stats);
-    }
-
     private void awaitCompletion() {
         ConditionVariable done = new ConditionVariable();
         mHandler.post(done::open);
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
index 4df919d..fc0611f 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsSpan.java
@@ -44,6 +44,7 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -72,7 +73,7 @@
     private static final DateTimeFormatter DATE_FORMAT =
             DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
 
-    static class TimeFrame {
+    public static class TimeFrame {
         public final long startMonotonicTime;
         @CurrentTimeMillisLong
         public final long startTime;
@@ -119,7 +120,7 @@
         }
     }
 
-    static class Metadata {
+    public static class Metadata {
         static final Comparator<Metadata> COMPARATOR = Comparator.comparing(Metadata::getId);
 
         private final long mId;
@@ -262,7 +263,7 @@
     public abstract static class Section {
         private final String mType;
 
-        Section(String type) {
+        protected Section(String type) {
             mType = type;
         }
 
@@ -274,7 +275,10 @@
             return mType;
         }
 
-        abstract void write(TypedXmlSerializer serializer) throws IOException;
+        /**
+         * Adds the contents of this section to the XML doc.
+         */
+        public abstract void write(TypedXmlSerializer serializer) throws IOException;
 
         /**
          * Prints the section type.
@@ -290,6 +294,11 @@
      */
     public interface SectionReader {
         /**
+         * Returns the unique type of content handled by this reader.
+         */
+        String getType();
+
+        /**
          * Reads the contents of the section using the parser. The type of the object
          * read and the corresponding XML format are determined by the section type.
          */
@@ -316,12 +325,18 @@
         return mMetadata.mId;
     }
 
-    void addTimeFrame(long monotonicTime, @CurrentTimeMillisLong long wallClockTime,
+    /**
+     * Adds a time frame covered by this PowerStats span
+     */
+    public void addTimeFrame(long monotonicTime, @CurrentTimeMillisLong long wallClockTime,
             @DurationMillisLong long duration) {
         mMetadata.mTimeFrames.add(new TimeFrame(monotonicTime, wallClockTime, duration));
     }
 
-    void addSection(Section section) {
+    /**
+     * Adds the supplied section to the span.
+     */
+    public void addSection(Section section) {
         mMetadata.addSection(section.getType());
         mSections.add(section);
     }
@@ -354,7 +369,7 @@
 
     @Nullable
     static PowerStatsSpan read(InputStream in, TypedXmlPullParser parser,
-            SectionReader sectionReader, String... sectionTypes)
+            Map<String, SectionReader> sectionReaders, String... sectionTypes)
             throws IOException, XmlPullParserException {
         Set<String> neededSections = Sets.newArraySet(sectionTypes);
         boolean selectSections = !neededSections.isEmpty();
@@ -386,7 +401,11 @@
                 if (tag.equals(XML_TAG_SECTION)) {
                     String sectionType = parser.getAttributeValue(null, XML_ATTR_SECTION_TYPE);
                     if (!selectSections || neededSections.contains(sectionType)) {
-                        Section section = sectionReader.read(sectionType, parser);
+                        Section section = null;
+                        SectionReader sectionReader = sectionReaders.get(sectionType);
+                        if (sectionReader != null) {
+                            section = sectionReader.read(sectionType, parser);
+                        }
                         if (section == null) {
                             if (selectSections) {
                                 throw new XmlPullParserException(
@@ -396,11 +415,11 @@
                                     @Override
                                     public void dump(IndentingPrintWriter ipw) {
                                         ipw.println("Unsupported PowerStatsStore section type: "
-                                                    + sectionType);
+                                                + sectionType);
                                     }
 
                                     @Override
-                                    void write(TypedXmlSerializer serializer) {
+                                    public void write(TypedXmlSerializer serializer) {
                                     }
                                 };
                             }
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsStore.java b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
index 7bcdc71..a875c30 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -42,6 +42,7 @@
 import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -66,28 +67,31 @@
     private FileLock mJvmLock;
     private final long mMaxStorageBytes;
     private final Handler mHandler;
-    private final PowerStatsSpan.SectionReader mSectionReader;
+    private final Map<String, PowerStatsSpan.SectionReader> mSectionReaders = new HashMap<>();
     private volatile List<PowerStatsSpan.Metadata> mTableOfContents;
 
-    public PowerStatsStore(@NonNull File systemDir, Handler handler,
-            AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
-        this(systemDir, MAX_POWER_STATS_SPAN_STORAGE_BYTES, handler,
-                new DefaultSectionReader(aggregatedPowerStatsConfig));
+    public PowerStatsStore(@NonNull File systemDir, Handler handler) {
+        this(systemDir, MAX_POWER_STATS_SPAN_STORAGE_BYTES, handler);
     }
 
     @VisibleForTesting
-    public PowerStatsStore(@NonNull File systemDir, long maxStorageBytes, Handler handler,
-            @NonNull PowerStatsSpan.SectionReader sectionReader) {
+    public PowerStatsStore(@NonNull File systemDir, long maxStorageBytes, Handler handler) {
         mSystemDir = systemDir;
         mStoreDir = new File(systemDir, POWER_STATS_DIR);
         mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
         mHandler = handler;
         mMaxStorageBytes = maxStorageBytes;
-        mSectionReader = sectionReader;
         mHandler.post(this::maybeClearLegacyStore);
     }
 
     /**
+     * Registers a Reader for a section type, which is determined by `sectionReader.getType()`
+     */
+    public void addSectionReader(PowerStatsSpan.SectionReader sectionReader) {
+        mSectionReaders.put(sectionReader.getType(), sectionReader);
+    }
+
+    /**
      * Returns the metadata for all {@link PowerStatsSpan}'s contained in the store.
      */
     @NonNull
@@ -169,7 +173,7 @@
         try {
             File file = makePowerStatsSpanFilename(id);
             try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
-                return PowerStatsSpan.read(inputStream, parser, mSectionReader, sectionTypes);
+                return PowerStatsSpan.read(inputStream, parser, mSectionReaders, sectionTypes);
             } catch (IOException | XmlPullParserException e) {
                 Slog.wtf(TAG, "Cannot read PowerStatsSpan file: " + file, e);
             }
@@ -179,41 +183,6 @@
         return null;
     }
 
-    void storeAggregatedPowerStats(AggregatedPowerStats stats) {
-        PowerStatsSpan span = createPowerStatsSpan(stats);
-        if (span == null) {
-            return;
-        }
-        storePowerStatsSpan(span);
-    }
-
-    static PowerStatsSpan createPowerStatsSpan(AggregatedPowerStats stats) {
-        List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
-        if (clockUpdates.isEmpty()) {
-            Slog.w(TAG, "No clock updates in aggregated power stats " + stats);
-            return null;
-        }
-
-        long monotonicTime = clockUpdates.get(0).monotonicTime;
-        long durationSum = 0;
-        PowerStatsSpan span = new PowerStatsSpan(monotonicTime);
-        for (int i = 0; i < clockUpdates.size(); i++) {
-            AggregatedPowerStats.ClockUpdate clockUpdate = clockUpdates.get(i);
-            long duration;
-            if (i == clockUpdates.size() - 1) {
-                duration = stats.getDuration() - durationSum;
-            } else {
-                duration = clockUpdate.monotonicTime - monotonicTime;
-            }
-            span.addTimeFrame(clockUpdate.monotonicTime, clockUpdate.currentTime, duration);
-            monotonicTime = clockUpdate.monotonicTime;
-            durationSum += duration;
-        }
-
-        span.addSection(new AggregatedPowerStatsSection(stats));
-        return span;
-    }
-
     /**
      * Stores a {@link PowerStatsSpan} containing a single section for the supplied
      * battery usage stats.
@@ -344,28 +313,4 @@
         }
         ipw.decreaseIndent();
     }
-
-    private static class DefaultSectionReader implements PowerStatsSpan.SectionReader {
-        private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
-
-        DefaultSectionReader(AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
-            mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
-        }
-
-        @Override
-        public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
-                throws IOException, XmlPullParserException {
-            switch (sectionType) {
-                case AggregatedPowerStatsSection.TYPE:
-                    return new AggregatedPowerStatsSection(
-                            AggregatedPowerStats.createFromXml(parser,
-                                    mAggregatedPowerStatsConfig));
-                case BatteryUsageStatsSection.TYPE:
-                    return new BatteryUsageStatsSection(
-                            BatteryUsageStats.createFromXml(parser));
-                default:
-                    return null;
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
index 291f289..8371e66 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerStatsCollector.java
@@ -21,19 +21,16 @@
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.PersistableBundle;
-import android.util.Slog;
 import android.util.SparseLongArray;
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
-
-import java.util.Arrays;
-import java.util.function.IntSupplier;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
 
 public class ScreenPowerStatsCollector extends PowerStatsCollector {
     private static final String TAG = "ScreenPowerStatsCollector";
 
-    interface ScreenUsageTimeRetriever {
+    public interface ScreenUsageTimeRetriever {
         interface Callback {
             void onUidTopActivityTime(int uid, long topActivityTimeMs);
         }
@@ -45,30 +42,23 @@
         long getScreenDozeTimeMs(int display);
     }
 
-    interface Injector {
+    public interface Injector {
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
         ScreenUsageTimeRetriever getScreenUsageTimeRetriever();
         int getDisplayCount();
     }
 
-    private static final long ENERGY_UNSPECIFIED = -1;
-
     private final Injector mInjector;
     private boolean mIsInitialized;
     private ScreenPowerStatsLayout mLayout;
     private int mDisplayCount;
     private PowerStats mPowerStats;
-    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
     private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
-    private int[] mEnergyConsumerIds = new int[0];
-    private long[] mLastConsumedEnergyUws;
-    private int mLastVoltageMv;
     private boolean mFirstSample = true;
     private long[] mLastScreenOnTime;
     private long[][] mLastBrightnessLevelTime;
@@ -76,7 +66,7 @@
     private final SparseLongArray mLastTopActivityTime = new SparseLongArray();
     private long mLastCollectionTime;
 
-    ScreenPowerStatsCollector(Injector injector) {
+    public ScreenPowerStatsCollector(Injector injector) {
         super(injector.getHandler(),
                 injector.getPowerStatsCollectionThrottlePeriod(
                         BatteryConsumer.powerComponentIdToString(
@@ -95,21 +85,12 @@
         }
 
         mDisplayCount = mInjector.getDisplayCount();
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
         mScreenUsageTimeRetriever = mInjector.getScreenUsageTimeRetriever();
-        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
-                EnergyConsumerType.DISPLAY);
-        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
-        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
 
-        mLayout = new ScreenPowerStatsLayout();
-        mLayout.addDeviceScreenUsageDurationSection(mInjector.getDisplayCount());
-        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
-        mLayout.addDeviceSectionUsageDuration();
-        mLayout.addDeviceSectionPowerEstimate();
-        mLayout.addUidTopActivitiyDuration();
-        mLayout.addUidSectionPowerEstimate();
+        mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+                EnergyConsumerType.DISPLAY);
+        mLayout = new ScreenPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+                mInjector.getDisplayCount());
 
         PersistableBundle extras = new PersistableBundle();
         mLayout.toExtras(extras);
@@ -129,14 +110,12 @@
     }
 
     @Override
-    protected PowerStats collectStats() {
+    public PowerStats collectStats() {
         if (!ensureInitialized()) {
             return null;
         }
 
-        if (mEnergyConsumerIds.length != 0) {
-            collectEnergyConsumers();
-        }
+        mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
         for (int display = 0; display < mDisplayCount; display++) {
             long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
@@ -192,34 +171,6 @@
         return mPowerStats;
     }
 
-    private void collectEnergyConsumers() {
-        int voltageMv = mVoltageSupplier.getAsInt();
-        if (voltageMv <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                    + " mV) when querying energy consumers");
-            return;
-        }
-
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        mLastVoltageMv = voltageMv;
-
-        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
-        if (energyUws == null) {
-            return;
-        }
-
-        for (int i = energyUws.length - 1; i >= 0; i--) {
-            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
-                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
-            if (energyDelta < 0) {
-                // Likely, restart of powerstats HAL
-                energyDelta = 0;
-            }
-            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
-            mLastConsumedEnergyUws[i] = energyUws[i];
-        }
-    }
-
     @Override
     protected void onUidRemoved(int uid) {
         mLastTopActivityTime.delete(uid);
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
deleted file mode 100644
index f134aa8..0000000
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsLayout.java
+++ /dev/null
@@ -1,171 +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.server.power.stats;
-
-import android.annotation.NonNull;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-
-import com.android.internal.os.PowerStats;
-
-/**
- * Captures the positions and lengths of sections of the stats array, such as time-in-state,
- * power usage estimates etc.
- */
-public class ScreenPowerStatsLayout extends PowerStatsLayout {
-    private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc";
-    private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd";
-    private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd";
-    private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd";
-    private static final String EXTRA_DEVICE_DOZE_POWER_POSITION = "ddp";
-    private static final String EXTRA_UID_FOREGROUND_DURATION = "uf";
-
-    private int mDisplayCount;
-    private int mDeviceScreenOnDurationPosition;
-    private int[] mDeviceBrightnessDurationPositions;
-    private int mDeviceScreenDozeDurationPosition;
-    private int mDeviceScreenDozePowerPosition;
-    private int mUidTopActivityTimePosition;
-
-    ScreenPowerStatsLayout() {
-    }
-
-    ScreenPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
-        super(descriptor);
-    }
-
-    void addDeviceScreenUsageDurationSection(int displayCount) {
-        mDisplayCount = displayCount;
-        mDeviceScreenOnDurationPosition = addDeviceSection(displayCount, "on");
-        mDeviceBrightnessDurationPositions = new int[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
-        for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
-            mDeviceBrightnessDurationPositions[level] =
-                    addDeviceSection(displayCount, BatteryStats.SCREEN_BRIGHTNESS_NAMES[level]);
-        }
-        mDeviceScreenDozeDurationPosition = addDeviceSection(displayCount, "doze");
-    }
-
-    @Override
-    public void addDeviceSectionPowerEstimate() {
-        super.addDeviceSectionPowerEstimate();
-        // Used by AmbientDisplayPowerStatsProcessor
-        mDeviceScreenDozePowerPosition = addDeviceSection(1, "doze-power", FLAG_HIDDEN);
-    }
-
-    public int getDisplayCount() {
-        return mDisplayCount;
-    }
-
-    /**
-     * Stores screen-on time for the specified display.
-     */
-    public void setScreenOnDuration(long[] stats, int display, long durationMs) {
-        stats[mDeviceScreenOnDurationPosition + display] = durationMs;
-    }
-
-    /**
-     * Returns screen-on time for the specified display.
-     */
-    public long getScreenOnDuration(long[] stats, int display) {
-        return stats[mDeviceScreenOnDurationPosition + display];
-    }
-
-    /**
-     * Stores time at the specified brightness level for the specified display.
-     */
-    public void setBrightnessLevelDuration(long[] stats, int display, int brightnessLevel,
-            long durationMs) {
-        stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display] = durationMs;
-    }
-
-    /**
-     * Returns time at the specified brightness level for the specified display.
-     */
-    public long getBrightnessLevelDuration(long[] stats, int display, int brightnessLevel) {
-        return stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display];
-    }
-
-    /**
-     * Stores time in the doze (ambient) state for the specified display.
-     */
-    public void setScreenDozeDuration(long[] stats, int display, long durationMs) {
-        stats[mDeviceScreenDozeDurationPosition + display] = durationMs;
-    }
-
-    /**
-     * Retrieves time in the doze (ambient) state for the specified display.
-     */
-    public long getScreenDozeDuration(long[] stats, int display) {
-        return stats[mDeviceScreenDozeDurationPosition + display];
-    }
-
-    /**
-     * Stores estimated power in the doze (ambient) state.
-     */
-    public void setScreenDozePowerEstimate(long[] stats, double power) {
-        stats[mDeviceScreenDozePowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
-    }
-
-    /**
-     * Retrieves estimated power in the doze (ambient) state.
-     */
-    public double getScreenDozePowerEstimate(long[] stats) {
-        return stats[mDeviceScreenDozePowerPosition] / MILLI_TO_NANO_MULTIPLIER;
-    }
-
-    void addUidTopActivitiyDuration() {
-        mUidTopActivityTimePosition = addUidSection(1, "top");
-    }
-
-    /**
-     * Stores time the UID spent in the TOP state.
-     */
-    public void setUidTopActivityDuration(long[] stats, long durationMs) {
-        stats[mUidTopActivityTimePosition] = durationMs;
-    }
-
-    /**
-     * Returns time the UID spent in the TOP state.
-     */
-    public long getUidTopActivityDuration(long[] stats) {
-        return stats[mUidTopActivityTimePosition];
-    }
-
-    @Override
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
-        extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
-        extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
-                mDeviceBrightnessDurationPositions);
-        extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
-        extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
-        extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
-    }
-
-    @Override
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
-        mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
-        mDeviceBrightnessDurationPositions = extras.getIntArray(
-                EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
-        mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
-        mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
-        mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
deleted file mode 100644
index 908c751..0000000
--- a/services/core/java/com/android/server/power/stats/ScreenPowerStatsProcessor.java
+++ /dev/null
@@ -1,239 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
-
-import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
-import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
-import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import android.os.BatteryStats;
-import android.util.Slog;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ScreenPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "ScreenPowerStatsProcessor";
-    private final int mDisplayCount;
-    private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators;
-    private final UsageBasedPowerEstimator[] mScreenDozePowerEstimators;
-    private final UsageBasedPowerEstimator[][] mScreenBrightnessLevelPowerEstimators;
-    private PowerStats.Descriptor mLastUsedDescriptor;
-    private ScreenPowerStatsLayout mStatsLayout;
-    private PowerEstimationPlan mPlan;
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-
-    private static class Intermediates {
-        public double power;
-    }
-
-    public ScreenPowerStatsProcessor(PowerProfile powerProfile) {
-        mDisplayCount = powerProfile.getNumDisplays();
-        mScreenOnPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
-        mScreenDozePowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
-        mScreenBrightnessLevelPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount][];
-        for (int display = 0; display < mDisplayCount; display++) {
-            mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator(
-                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display));
-
-            double averagePowerFullBrightness = powerProfile.getAveragePowerForOrdinal(
-                    POWER_GROUP_DISPLAY_SCREEN_FULL, display);
-            mScreenBrightnessLevelPowerEstimators[display] =
-                    new UsageBasedPowerEstimator[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
-            for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
-                // For example, if the number of bins is 3, the corresponding averages
-                // are calculated as 0.5 * full, 1.5 * full, 2.5 * full
-                final double binPowerMah = averagePowerFullBrightness * (bin + 0.5)
-                        / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
-                mScreenBrightnessLevelPowerEstimators[display][bin] =
-                        new UsageBasedPowerEstimator(binPowerMah);
-            }
-
-            mScreenDozePowerEstimators[display] = new UsageBasedPowerEstimator(
-                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, display));
-        }
-    }
-
-    private boolean unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
-        if (descriptor == null) {
-            return false;
-        }
-
-        if (descriptor.equals(mLastUsedDescriptor)) {
-            return true;
-        }
-
-        mLastUsedDescriptor = descriptor;
-        mStatsLayout = new ScreenPowerStatsLayout(descriptor);
-        if (mStatsLayout.getDisplayCount() != mDisplayCount) {
-            Slog.e(TAG, "Incompatible number of displays: " + mStatsLayout.getDisplayCount()
-                    + ", expected: " + mDisplayCount);
-            return false;
-        }
-
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-        return true;
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (!unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor())) {
-            return;
-        }
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        computeDevicePowerEstimates(stats);
-        combineDeviceStateEstimates();
-
-        List<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-
-        if (!uids.isEmpty()) {
-            computeUidPowerEstimates(stats, uids);
-        }
-        mPlan.resetIntermediates();
-    }
-
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
-                continue;
-            }
-
-            if (estimation.stateValues[STATE_SCREEN] == SCREEN_STATE_ON) {
-                double power;
-                if (mStatsLayout.getEnergyConsumerCount() > 0) {
-                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
-                } else {
-                    power = 0;
-                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
-                        power += computeDisplayPower(mTmpDeviceStatsArray, display);
-                    }
-                }
-                mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
-                Intermediates intermediates = new Intermediates();
-                intermediates.power = power;
-                estimation.intermediates = intermediates;
-            } else {
-                double power = 0;
-                if (mStatsLayout.getEnergyConsumerCount() > 0) {
-                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
-                } else {
-                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
-                        power += mScreenDozePowerEstimators[display].calculatePower(
-                                mStatsLayout.getScreenDozeDuration(mTmpDeviceStatsArray, display));
-                    }
-                }
-                mStatsLayout.setScreenDozePowerEstimate(mTmpDeviceStatsArray, power);
-            }
-
-            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
-        }
-    }
-
-    private double computeDisplayPower(long[] stats, int display) {
-        double power = mScreenOnPowerEstimators[display]
-                .calculatePower(mStatsLayout.getScreenOnDuration(stats, display));
-        for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
-            power += mScreenBrightnessLevelPowerEstimators[display][bin]
-                    .calculatePower(mStatsLayout.getBrightnessLevelDuration(stats, display, bin));
-        }
-        return power;
-    }
-
-    /**
-     * Combine power estimates before distributing them proportionally to UIDs.
-     */
-    private void combineDeviceStateEstimates() {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
-            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
-            double power = 0;
-            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation dse = deviceStateEstimations.get(j);
-                Intermediates intermediates = (Intermediates) dse.intermediates;
-                if (intermediates != null) {
-                    power += intermediates.power;
-                }
-            }
-            if (power != 0) {
-                Intermediates cdseIntermediates = new Intermediates();
-                cdseIntermediates.power = power;
-                cdse.intermediates = cdseIntermediates;
-            }
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
-            List<Integer> uids) {
-        int[] uidStateValues = new int[stats.getConfig().getUidStateConfig().length];
-        uidStateValues[STATE_SCREEN] = SCREEN_STATE_ON;
-        uidStateValues[STATE_PROCESS_STATE] = PROCESS_STATE_ANY;
-
-        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
-            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
-            Intermediates intermediates =
-                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-            int[] deviceStateValues = uidStateEstimate.combinedDeviceStateEstimate
-                    .stateValues;
-            if (deviceStateValues[STATE_SCREEN] != SCREEN_STATE_ON
-                    || intermediates == null) {
-                continue;
-            }
-
-            uidStateValues[STATE_POWER] = deviceStateValues[STATE_POWER];
-
-            long totalTopActivityDuration = 0;
-            for (int j = uids.size() - 1; j >= 0; j--) {
-                int uid = uids.get(j);
-                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
-                    totalTopActivityDuration +=
-                            mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
-                }
-            }
-
-            if (totalTopActivityDuration == 0) {
-                return;
-            }
-
-            for (int j = uids.size() - 1; j >= 0; j--) {
-                int uid = uids.get(j);
-                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
-                    long duration = mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
-                    double power = intermediates.power * duration / totalTopActivityDuration;
-                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-                    stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray);
-                }
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
deleted file mode 100644
index e66cd39..0000000
--- a/services/core/java/com/android/server/power/stats/SensorPowerStatsLayout.java
+++ /dev/null
@@ -1,81 +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.server.power.stats;
-
-import android.os.PersistableBundle;
-import android.util.Slog;
-import android.util.SparseIntArray;
-
-public class SensorPowerStatsLayout extends PowerStatsLayout {
-    private static final String TAG = "SensorPowerStatsLayout";
-    private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
-    private static final String EXTRA_UID_SENSOR_POSITIONS = "usp";
-
-    private final SparseIntArray mSensorPositions = new SparseIntArray();
-
-    void addUidSensorSection(int handle, String label) {
-        mSensorPositions.put(handle, addUidSection(1, label, FLAG_OPTIONAL));
-    }
-
-    /**
-     * Returns the position in the uid stats array of the duration element corresponding
-     * to the specified sensor identified by its handle.
-     */
-    public int getUidSensorDurationPosition(int handle) {
-        return mSensorPositions.get(handle, UNSUPPORTED);
-    }
-
-    /**
-     * Adds the specified duration to the accumulated timer for the specified sensor.
-     */
-    public void addUidSensorDuration(long[] stats, int handle, long durationMs) {
-        int position = mSensorPositions.get(handle, UNSUPPORTED);
-        if (position == UNSUPPORTED) {
-            Slog.e(TAG, "Unknown sensor: " + handle);
-            return;
-        }
-        stats[position] += durationMs;
-    }
-
-    @Override
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-
-        int[] handlers = new int[mSensorPositions.size()];
-        int[] uidDurationPositions = new int[mSensorPositions.size()];
-
-        for (int i = 0; i < mSensorPositions.size(); i++) {
-            handlers[i] = mSensorPositions.keyAt(i);
-            uidDurationPositions[i] = mSensorPositions.valueAt(i);
-        }
-
-        extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
-        extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
-    }
-
-    @Override
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-
-        int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
-        int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
-
-        for (int i = 0; i < handlers.length; i++) {
-            mSensorPositions.put(handlers[i], uidDurationPositions[i]);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
deleted file mode 100644
index 5bd3288..0000000
--- a/services/core/java/com/android/server/power/stats/SensorPowerStatsProcessor.java
+++ /dev/null
@@ -1,312 +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.server.power.stats;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.Supplier;
-
-public class SensorPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "SensorPowerStatsProcessor";
-    private static final String ANDROID_SENSOR_TYPE_PREFIX = "android.sensor.";
-
-    private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60;
-    private static final String SENSOR_EVENT_TAG_PREFIX = "sensor:0x";
-    private final Supplier<SensorManager> mSensorManagerSupplier;
-
-    private static final long INITIAL_TIMESTAMP = -1;
-    private SensorManager mSensorManager;
-    private SensorPowerStatsLayout mStatsLayout;
-    private PowerStats mPowerStats;
-    private boolean mIsInitialized;
-    private PowerStats.Descriptor mDescriptor;
-    private long mLastUpdateTimestamp;
-    private PowerEstimationPlan mPlan;
-
-    private static class SensorState {
-        public int sensorHandle;
-        public boolean stateOn;
-        public int uid;
-        public long startTime = INITIAL_TIMESTAMP;
-    }
-
-    private static class Intermediates {
-        public double power;
-    }
-
-    private final SparseArray<SensorState> mSensorStates = new SparseArray<>();
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-
-    public SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
-        mSensorManagerSupplier = sensorManagerSupplier;
-    }
-
-    private boolean ensureInitialized() {
-        if (mIsInitialized) {
-            return true;
-        }
-
-        mSensorManager = mSensorManagerSupplier.get();
-        if (mSensorManager == null) {
-            return false;
-        }
-
-        mStatsLayout = new SensorPowerStatsLayout();
-        List<Sensor> sensorList = new ArrayList<>(mSensorManager.getSensorList(Sensor.TYPE_ALL));
-        sensorList.sort(Comparator.comparingInt(Sensor::getId));
-        for (int i = 0; i < sensorList.size(); i++) {
-            Sensor sensor = sensorList.get(i);
-            String label = makeLabel(sensor, sensorList);
-            mStatsLayout.addUidSensorSection(sensor.getHandle(), label);
-        }
-        mStatsLayout.addUidSectionPowerEstimate();
-        mStatsLayout.addDeviceSectionPowerEstimate();
-
-        PersistableBundle extras = new PersistableBundle();
-        mStatsLayout.toExtras(extras);
-        mDescriptor = new PowerStats.Descriptor(
-                BatteryConsumer.POWER_COMPONENT_SENSORS, mStatsLayout.getDeviceStatsArrayLength(),
-                null, 0, mStatsLayout.getUidStatsArrayLength(),
-                extras);
-
-        mPowerStats = new PowerStats(mDescriptor);
-        mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
-        mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
-
-        mIsInitialized = true;
-        return true;
-    }
-
-    private String makeLabel(Sensor sensor, List<Sensor> sensorList) {
-        int type = sensor.getType();
-        String label = sensor.getStringType();
-
-        boolean isSingleton = true;
-        for (int i = sensorList.size() - 1; i >= 0; i--) {
-            Sensor s = sensorList.get(i);
-            if (s == sensor) {
-                continue;
-            }
-            if (s.getType() == type) {
-                isSingleton = false;
-                break;
-            }
-        }
-        if (!isSingleton) {
-            StringBuilder sb = new StringBuilder(label).append('.');
-            if (sensor.getId() > 0) { // 0 and -1 are reserved
-                sb.append(sensor.getId());
-            } else {
-                sb.append(sensor.getName());
-            }
-            label = sb.toString();
-        }
-        if (label.startsWith(ANDROID_SENSOR_TYPE_PREFIX)) {
-            label = label.substring(ANDROID_SENSOR_TYPE_PREFIX.length());
-        }
-        return label.replace(' ', '_');
-    }
-
-    @Override
-    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (!ensureInitialized()) {
-            return;
-        }
-
-        // Establish a baseline at the beginning of an accumulation pass
-        mLastUpdateTimestamp = timestampMs;
-        flushPowerStats(stats, timestampMs);
-    }
-
-    @Override
-    void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
-        if (!mIsInitialized) {
-            return;
-        }
-
-        if (item.eventTag == null || item.eventTag.string == null
-                || !item.eventTag.string.startsWith(SENSOR_EVENT_TAG_PREFIX)) {
-            return;
-        }
-
-        int sensorHandle;
-        try {
-            sensorHandle = Integer.parseInt(item.eventTag.string, SENSOR_EVENT_TAG_PREFIX.length(),
-                    item.eventTag.string.length(), 16);
-        } catch (NumberFormatException e) {
-            Slog.wtf(TAG, "Bad format of event tag: " + item.eventTag.string);
-            return;
-        }
-
-        SensorState sensor = mSensorStates.get(sensorHandle);
-        if (sensor == null) {
-            sensor = new SensorState();
-            sensor.sensorHandle = sensorHandle;
-            mSensorStates.put(sensorHandle, sensor);
-        }
-
-        int uid = item.eventTag.uid;
-        boolean sensorOn = (item.states & BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG) != 0;
-        if (sensorOn) {
-            if (!sensor.stateOn) {
-                sensor.stateOn = true;
-                sensor.uid = uid;
-                sensor.startTime = item.time;
-            } else if (sensor.uid != uid) {
-                recordUsageDuration(sensor, item.time);
-                sensor.uid = uid;
-            }
-        } else {
-            if (sensor.stateOn) {
-                recordUsageDuration(sensor, item.time);
-                sensor.stateOn = false;
-            }
-        }
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (!mIsInitialized) {
-            return;
-        }
-
-        for (int i = mSensorStates.size() - 1; i >= 0; i--) {
-            SensorState sensor = mSensorStates.valueAt(i);
-            if (sensor.stateOn) {
-                recordUsageDuration(sensor, timestampMs);
-            }
-        }
-        flushPowerStats(stats, timestampMs);
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        List<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-
-        computeUidPowerEstimates(stats, uids);
-        computeDevicePowerEstimates(stats);
-
-        mPlan.resetIntermediates();
-    }
-
-    protected void recordUsageDuration(SensorState sensorState, long time) {
-        long durationMs = Math.max(0, time - sensorState.startTime);
-        if (durationMs > 0) {
-            long[] uidStats = mPowerStats.uidStats.get(sensorState.uid);
-            if (uidStats == null) {
-                uidStats = new long[mDescriptor.uidStatsArrayLength];
-                mPowerStats.uidStats.put(sensorState.uid, uidStats);
-            }
-            mStatsLayout.addUidSensorDuration(uidStats, sensorState.sensorHandle, durationMs);
-        }
-        sensorState.startTime = time;
-    }
-
-    private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
-        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
-        stats.addPowerStats(mPowerStats, timestamp);
-
-        Arrays.fill(mPowerStats.stats, 0);
-        mPowerStats.uidStats.clear();
-        mLastUpdateTimestamp = timestamp;
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
-            List<Integer> uids) {
-        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
-        int[] uidSensorDurationPositions = new int[sensorList.size()];
-        double[] sensorPower = new double[sensorList.size()];
-        for (int i = sensorList.size() - 1; i >= 0; i--) {
-            Sensor sensor = sensorList.get(i);
-            uidSensorDurationPositions[i] =
-                    mStatsLayout.getUidSensorDurationPosition(sensor.getHandle());
-            sensorPower[i] = sensor.getPower() / MILLIS_IN_HOUR;
-        }
-
-        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
-            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
-            List<UidStateProportionalEstimate> proportionalEstimates =
-                    uidStateEstimate.proportionalEstimates;
-            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
-                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
-                for (int k = uids.size() - 1; k >= 0; k--) {
-                    int uid = uids.get(k);
-                    if (!stats.getUidStats(mTmpUidStatsArray, uid,
-                            proportionalEstimate.stateValues)) {
-                        continue;
-                    }
-                    double power = 0;
-                    for (int m = 0; m < uidSensorDurationPositions.length; m++) {
-                        int position = uidSensorDurationPositions[m];
-                        if (position == PowerStatsLayout.UNSUPPORTED
-                                || mTmpUidStatsArray[position] == 0) {
-                            continue;
-                        }
-                        power += sensorPower[m] * mTmpUidStatsArray[position];
-                    }
-                    if (power == 0) {
-                        continue;
-                    }
-
-                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-                    stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-
-                    Intermediates intermediates = (Intermediates) uidStateEstimate
-                            .combinedDeviceStateEstimate.intermediates;
-                    if (intermediates == null) {
-                        intermediates = new Intermediates();
-                        uidStateEstimate.combinedDeviceStateEstimate.intermediates = intermediates;
-                    }
-                    intermediates.power += power;
-                }
-            }
-        }
-    }
-
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate estimation =
-                    mPlan.combinedDeviceStateEstimations.get(i);
-            if (estimation.intermediates == null) {
-                continue;
-            }
-
-            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
-                continue;
-            }
-
-            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                    ((Intermediates) estimation.intermediates).power);
-            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
deleted file mode 100644
index 48dac8a..0000000
--- a/services/core/java/com/android/server/power/stats/VideoPowerStatsProcessor.java
+++ /dev/null
@@ -1,37 +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.server.power.stats;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-
-import com.android.internal.os.PowerProfile;
-
-public class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-    public VideoPowerStatsProcessor(PowerProfile powerProfile,
-            PowerStatsUidResolver uidResolver) {
-        super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver,
-                powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
-    }
-
-    @Override
-    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
-        return (item.states2 & BatteryStats.HistoryItem.STATE2_VIDEO_ON_FLAG) != 0
-                ? STATE_ON
-                : STATE_OFF;
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
index 90981ada..7a84b05 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -28,12 +28,11 @@
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
-import java.util.function.IntSupplier;
 import java.util.function.Supplier;
 
 public class WifiPowerStatsCollector extends PowerStatsCollector {
@@ -41,15 +40,13 @@
 
     private static final long WIFI_ACTIVITY_REQUEST_TIMEOUT = 20000;
 
-    private static final long ENERGY_UNSPECIFIED = -1;
-
     interface Observer {
         void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo info,
                 List<BatteryStatsImpl.NetworkStatsDelta> delta, long elapsedRealtimeMs,
                 long uptimeMs);
     }
 
-    interface WifiStatsRetriever {
+    public interface WifiStatsRetriever {
         interface Callback {
             void onWifiScanTime(int uid, long scanTimeMs, long batchScanTimeMs);
         }
@@ -58,14 +55,13 @@
         long getWifiActiveDuration();
     }
 
-    interface Injector {
+    public interface Injector {
         Handler getHandler();
         Clock getClock();
         PowerStatsUidResolver getUidResolver();
         long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
         PackageManager getPackageManager();
         ConsumedEnergyRetriever getConsumedEnergyRetriever();
-        IntSupplier getVoltageSupplier();
         Supplier<NetworkStats> getWifiNetworkStatsSupplier();
         WifiManager getWifiManager();
         WifiStatsRetriever getWifiStatsRetriever();
@@ -83,13 +79,9 @@
     private volatile WifiManager mWifiManager;
     private volatile Supplier<NetworkStats> mNetworkStatsSupplier;
     private volatile WifiStatsRetriever mWifiStatsRetriever;
-    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private IntSupplier mVoltageSupplier;
-    private int[] mEnergyConsumerIds = new int[0];
+    private ConsumedEnergyHelper mConsumedEnergyHelper;
     private WifiActivityEnergyInfo mLastWifiActivityInfo;
     private NetworkStats mLastNetworkStats;
-    private long[] mLastConsumedEnergyUws;
-    private int mLastVoltageMv;
 
     private static class WifiScanTimes {
         public long basicScanTimeMs;
@@ -99,7 +91,7 @@
     private final SparseArray<WifiScanTimes> mLastScanTimes = new SparseArray<>();
     private long mLastWifiActiveDuration;
 
-    WifiPowerStatsCollector(Injector injector, Observer observer) {
+    public WifiPowerStatsCollector(Injector injector, Observer observer) {
         super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
                         BatteryConsumer.powerComponentIdToString(
                                 BatteryConsumer.POWER_COMPONENT_WIFI)),
@@ -128,25 +120,17 @@
             return false;
         }
 
-        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
-        mVoltageSupplier = mInjector.getVoltageSupplier();
         mWifiManager = mInjector.getWifiManager();
         mNetworkStatsSupplier = mInjector.getWifiNetworkStatsSupplier();
         mWifiStatsRetriever = mInjector.getWifiStatsRetriever();
         mPowerReportingSupported =
                 mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported();
 
-        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI);
-        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
-        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);
+        mConsumedEnergyHelper = new ConsumedEnergyHelper(mInjector.getConsumedEnergyRetriever(),
+                EnergyConsumerType.WIFI);
 
-        mLayout = new WifiPowerStatsLayout();
-        mLayout.addDeviceWifiActivity(mPowerReportingSupported);
-        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
-        mLayout.addUidNetworkStats();
-        mLayout.addDeviceSectionUsageDuration();
-        mLayout.addDeviceSectionPowerEstimate();
-        mLayout.addUidSectionPowerEstimate();
+        mLayout = new WifiPowerStatsLayout(mConsumedEnergyHelper.getEnergyConsumerCount(),
+                mPowerReportingSupported);
 
         PersistableBundle extras = new PersistableBundle();
         mLayout.toExtras(extras);
@@ -162,7 +146,7 @@
     }
 
     @Override
-    protected PowerStats collectStats() {
+    public PowerStats collectStats() {
         if (!ensureInitialized()) {
             return null;
         }
@@ -176,9 +160,7 @@
         List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
         collectWifiScanTime();
 
-        if (mEnergyConsumerIds.length != 0) {
-            collectEnergyConsumers();
-        }
+        mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
         if (mObserver != null) {
             mObserver.onWifiPowerStatsRetrieved(activityInfo, networkStatsDeltas,
@@ -318,34 +300,6 @@
         mLayout.setDeviceBatchedScanTime(mDeviceStats, mScanTimes.batchedScanTimeMs);
     }
 
-    private void collectEnergyConsumers() {
-        int voltageMv = mVoltageSupplier.getAsInt();
-        if (voltageMv <= 0) {
-            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
-                    + " mV) when querying energy consumers");
-            return;
-        }
-
-        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
-        mLastVoltageMv = voltageMv;
-
-        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
-        if (energyUws == null) {
-            return;
-        }
-
-        for (int i = energyUws.length - 1; i >= 0; i--) {
-            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
-                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
-            if (energyDelta < 0) {
-                // Likely, restart of powerstats HAL
-                energyDelta = 0;
-            }
-            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
-            mLastConsumedEnergyUws[i] = energyUws[i];
-        }
-    }
-
     @Override
     protected void onUidRemoved(int uid) {
         super.onUidRemoved(uid);
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
deleted file mode 100644
index e2e8226..0000000
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
+++ /dev/null
@@ -1,241 +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.server.power.stats;
-
-import android.annotation.NonNull;
-import android.os.PersistableBundle;
-
-import com.android.internal.os.PowerStats;
-
-public class WifiPowerStatsLayout extends PowerStatsLayout {
-    private static final String TAG = "WifiPowerStatsLayout";
-    private static final int UNSPECIFIED = -1;
-    private static final String EXTRA_POWER_REPORTING_SUPPORTED = "prs";
-    private static final String EXTRA_DEVICE_RX_TIME_POSITION = "dt-rx";
-    private static final String EXTRA_DEVICE_TX_TIME_POSITION = "dt-tx";
-    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
-    private static final String EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION = "dt-basic-scan";
-    private static final String EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION = "dt-batch-scan";
-    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
-    private static final String EXTRA_DEVICE_ACTIVE_TIME_POSITION = "dt-on";
-    private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
-    private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
-    private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
-    private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
-    private static final String EXTRA_UID_SCAN_TIME_POSITION = "ut-scan";
-    private static final String EXTRA_UID_BATCH_SCAN_TIME_POSITION = "ut-bscan";
-
-    private boolean mPowerReportingSupported;
-    private int mDeviceRxTimePosition;
-    private int mDeviceTxTimePosition;
-    private int mDeviceIdleTimePosition;
-    private int mDeviceScanTimePosition;
-    private int mDeviceBasicScanTimePosition;
-    private int mDeviceBatchedScanTimePosition;
-    private int mDeviceActiveTimePosition;
-    private int mUidRxBytesPosition;
-    private int mUidTxBytesPosition;
-    private int mUidRxPacketsPosition;
-    private int mUidTxPacketsPosition;
-    private int mUidScanTimePosition;
-    private int mUidBatchScanTimePosition;
-
-    WifiPowerStatsLayout() {
-    }
-
-    WifiPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
-        super(descriptor);
-    }
-
-    void addDeviceWifiActivity(boolean powerReportingSupported) {
-        mPowerReportingSupported = powerReportingSupported;
-        if (mPowerReportingSupported) {
-            mDeviceActiveTimePosition = UNSPECIFIED;
-            mDeviceRxTimePosition = addDeviceSection(1, "rx");
-            mDeviceTxTimePosition = addDeviceSection(1, "tx");
-            mDeviceIdleTimePosition = addDeviceSection(1, "idle");
-            mDeviceScanTimePosition = addDeviceSection(1, "scan");
-        } else {
-            mDeviceActiveTimePosition = addDeviceSection(1, "rx-tx");
-            mDeviceRxTimePosition = UNSPECIFIED;
-            mDeviceTxTimePosition = UNSPECIFIED;
-            mDeviceIdleTimePosition = UNSPECIFIED;
-            mDeviceScanTimePosition = UNSPECIFIED;
-        }
-        mDeviceBasicScanTimePosition = addDeviceSection(1, "basic-scan", FLAG_OPTIONAL);
-        mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL);
-    }
-
-    void addUidNetworkStats() {
-        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
-        mUidRxBytesPosition = addUidSection(1, "rx-B");
-        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
-        mUidTxBytesPosition = addUidSection(1, "tx-B");
-        mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
-        mUidBatchScanTimePosition = addUidSection(1, "batched-scan", FLAG_OPTIONAL);
-    }
-
-    public boolean isPowerReportingSupported() {
-        return mPowerReportingSupported;
-    }
-
-    public void setDeviceRxTime(long[] stats, long durationMillis) {
-        stats[mDeviceRxTimePosition] = durationMillis;
-    }
-
-    public long getDeviceRxTime(long[] stats) {
-        return stats[mDeviceRxTimePosition];
-    }
-
-    public void setDeviceTxTime(long[] stats, long durationMillis) {
-        stats[mDeviceTxTimePosition] = durationMillis;
-    }
-
-    public long getDeviceTxTime(long[] stats) {
-        return stats[mDeviceTxTimePosition];
-    }
-
-    public void setDeviceScanTime(long[] stats, long durationMillis) {
-        stats[mDeviceScanTimePosition] = durationMillis;
-    }
-
-    public long getDeviceScanTime(long[] stats) {
-        return stats[mDeviceScanTimePosition];
-    }
-
-    public void setDeviceBasicScanTime(long[] stats, long durationMillis) {
-        stats[mDeviceBasicScanTimePosition] = durationMillis;
-    }
-
-    public long getDeviceBasicScanTime(long[] stats) {
-        return stats[mDeviceBasicScanTimePosition];
-    }
-
-    public void setDeviceBatchedScanTime(long[] stats, long durationMillis) {
-        stats[mDeviceBatchedScanTimePosition] = durationMillis;
-    }
-
-    public long getDeviceBatchedScanTime(long[] stats) {
-        return stats[mDeviceBatchedScanTimePosition];
-    }
-
-    public void setDeviceIdleTime(long[] stats, long durationMillis) {
-        stats[mDeviceIdleTimePosition] = durationMillis;
-    }
-
-    public long getDeviceIdleTime(long[] stats) {
-        return stats[mDeviceIdleTimePosition];
-    }
-
-    public void setDeviceActiveTime(long[] stats, long durationMillis) {
-        stats[mDeviceActiveTimePosition] = durationMillis;
-    }
-
-    public long getDeviceActiveTime(long[] stats) {
-        return stats[mDeviceActiveTimePosition];
-    }
-
-    public void setUidRxBytes(long[] stats, long count) {
-        stats[mUidRxBytesPosition] = count;
-    }
-
-    public long getUidRxBytes(long[] stats) {
-        return stats[mUidRxBytesPosition];
-    }
-
-    public void setUidTxBytes(long[] stats, long count) {
-        stats[mUidTxBytesPosition] = count;
-    }
-
-    public long getUidTxBytes(long[] stats) {
-        return stats[mUidTxBytesPosition];
-    }
-
-    public void setUidRxPackets(long[] stats, long count) {
-        stats[mUidRxPacketsPosition] = count;
-    }
-
-    public long getUidRxPackets(long[] stats) {
-        return stats[mUidRxPacketsPosition];
-    }
-
-    public void setUidTxPackets(long[] stats, long count) {
-        stats[mUidTxPacketsPosition] = count;
-    }
-
-    public long getUidTxPackets(long[] stats) {
-        return stats[mUidTxPacketsPosition];
-    }
-
-    public void setUidScanTime(long[] stats, long count) {
-        stats[mUidScanTimePosition] = count;
-    }
-
-    public long getUidScanTime(long[] stats) {
-        return stats[mUidScanTimePosition];
-    }
-
-    public void setUidBatchScanTime(long[] stats, long count) {
-        stats[mUidBatchScanTimePosition] = count;
-    }
-
-    public long getUidBatchedScanTime(long[] stats) {
-        return stats[mUidBatchScanTimePosition];
-    }
-
-    /**
-     * Copies the elements of the stats array layout into <code>extras</code>
-     */
-    public void toExtras(PersistableBundle extras) {
-        super.toExtras(extras);
-        extras.putBoolean(EXTRA_POWER_REPORTING_SUPPORTED, mPowerReportingSupported);
-        extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
-        extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
-        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
-        extras.putInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION, mDeviceBasicScanTimePosition);
-        extras.putInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION, mDeviceBatchedScanTimePosition);
-        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
-        extras.putInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION, mDeviceActiveTimePosition);
-        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
-        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
-        extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
-        extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
-        extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
-        extras.putInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION, mUidBatchScanTimePosition);
-    }
-
-    /**
-     * Retrieves elements of the stats array layout from <code>extras</code>
-     */
-    public void fromExtras(PersistableBundle extras) {
-        super.fromExtras(extras);
-        mPowerReportingSupported = extras.getBoolean(EXTRA_POWER_REPORTING_SUPPORTED);
-        mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
-        mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
-        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
-        mDeviceBasicScanTimePosition = extras.getInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION);
-        mDeviceBatchedScanTimePosition = extras.getInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION);
-        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
-        mDeviceActiveTimePosition = extras.getInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION);
-        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
-        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
-        mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
-        mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
-        mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
-        mUidBatchScanTimePosition = extras.getInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION);
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
deleted file mode 100644
index 4e035c3..0000000
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
+++ /dev/null
@@ -1,392 +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.server.power.stats;
-
-import android.util.Slog;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class WifiPowerStatsProcessor extends PowerStatsProcessor {
-    private static final String TAG = "WifiPowerStatsProcessor";
-    private static final boolean DEBUG = false;
-
-    private final UsageBasedPowerEstimator mRxPowerEstimator;
-    private final UsageBasedPowerEstimator mTxPowerEstimator;
-    private final UsageBasedPowerEstimator mIdlePowerEstimator;
-
-    private final UsageBasedPowerEstimator mActivePowerEstimator;
-    private final UsageBasedPowerEstimator mScanPowerEstimator;
-    private final UsageBasedPowerEstimator mBatchedScanPowerEstimator;
-
-    private PowerStats.Descriptor mLastUsedDescriptor;
-    private WifiPowerStatsLayout mStatsLayout;
-    // Sequence of steps for power estimation and intermediate results.
-    private PowerEstimationPlan mPlan;
-
-    private long[] mTmpDeviceStatsArray;
-    private long[] mTmpUidStatsArray;
-    private boolean mHasWifiPowerController;
-
-    public WifiPowerStatsProcessor(PowerProfile powerProfile) {
-        mRxPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
-        mTxPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
-        mIdlePowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
-        mActivePowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE));
-        mScanPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
-        mBatchedScanPowerEstimator = new UsageBasedPowerEstimator(
-                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
-    }
-
-    private static class Intermediates {
-        /**
-         * Estimated power for the RX state.
-         */
-        public double rxPower;
-        /**
-         * Estimated power for the TX state.
-         */
-        public double txPower;
-        /**
-         * Estimated power in the SCAN state
-         */
-        public double scanPower;
-        /**
-         * Estimated power for IDLE, SCAN states.
-         */
-        public double idlePower;
-        /**
-         * Number of received packets
-         */
-        public long rxPackets;
-        /**
-         * Number of transmitted packets
-         */
-        public long txPackets;
-        /**
-         * Total duration of unbatched scans across all UIDs.
-         */
-        public long basicScanDuration;
-        /**
-         * Estimated power in the unbatched SCAN state
-         */
-        public double basicScanPower;
-        /**
-         * Total duration of batched scans across all UIDs.
-         */
-        public long batchedScanDuration;
-        /**
-         * Estimated power in the BATCHED SCAN state
-         */
-        public double batchedScanPower;
-        /**
-         * Estimated total power when active; used only in the absence of WiFiManager power
-         * reporting.
-         */
-        public double activePower;
-        /**
-         * Measured consumed energy from power monitoring hardware (micro-coulombs)
-         */
-        public long consumedEnergy;
-    }
-
-    @Override
-    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
-        if (stats.getPowerStatsDescriptor() == null) {
-            return;
-        }
-
-        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
-
-        if (mPlan == null) {
-            mPlan = new PowerEstimationPlan(stats.getConfig());
-        }
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-            Intermediates intermediates = new Intermediates();
-            estimation.intermediates = intermediates;
-            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
-        }
-
-        double ratio = 1.0;
-        if (mStatsLayout.getEnergyConsumerCount() != 0) {
-            ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
-            if (ratio != 1) {
-                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
-                    adjustDevicePowerEstimates(stats, estimation.stateValues,
-                            (Intermediates) estimation.intermediates, ratio);
-                }
-            }
-        }
-
-        combineDeviceStateEstimates();
-
-        ArrayList<Integer> uids = new ArrayList<>();
-        stats.collectUids(uids);
-        if (!uids.isEmpty()) {
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-
-            for (int uid : uids) {
-                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
-                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
-                }
-            }
-        }
-        mPlan.resetIntermediates();
-    }
-
-    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
-        if (descriptor.equals(mLastUsedDescriptor)) {
-            return;
-        }
-
-        mLastUsedDescriptor = descriptor;
-        mStatsLayout = new WifiPowerStatsLayout(descriptor);
-        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
-        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
-        mHasWifiPowerController = mStatsLayout.isPowerReportingSupported();
-    }
-
-    /**
-     * Compute power estimates using the power profile.
-     */
-    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates) {
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
-            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
-        }
-
-        intermediates.basicScanDuration =
-                mStatsLayout.getDeviceBasicScanTime(mTmpDeviceStatsArray);
-        intermediates.batchedScanDuration =
-                mStatsLayout.getDeviceBatchedScanTime(mTmpDeviceStatsArray);
-        if (mHasWifiPowerController) {
-            intermediates.rxPower = mRxPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceRxTime(mTmpDeviceStatsArray));
-            intermediates.txPower = mTxPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceTxTime(mTmpDeviceStatsArray));
-            intermediates.scanPower = mScanPowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
-            intermediates.idlePower = mIdlePowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
-            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                    intermediates.rxPower + intermediates.txPower + intermediates.scanPower
-                            + intermediates.idlePower);
-        } else {
-            intermediates.activePower = mActivePowerEstimator.calculatePower(
-                    mStatsLayout.getDeviceActiveTime(mTmpDeviceStatsArray));
-            intermediates.basicScanPower =
-                    mScanPowerEstimator.calculatePower(intermediates.basicScanDuration);
-            intermediates.batchedScanPower =
-                    mBatchedScanPowerEstimator.calculatePower(intermediates.batchedScanDuration);
-            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
-                    intermediates.activePower + intermediates.basicScanPower
-                            + intermediates.batchedScanPower);
-        }
-
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * Compute an adjustment ratio using the total power estimated using the power profile
-     * and the total power measured by hardware.
-     */
-    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
-        long totalConsumedEnergy = 0;
-        double totalPower = 0;
-
-        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
-            Intermediates intermediates =
-                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
-            if (mHasWifiPowerController) {
-                totalPower += intermediates.rxPower + intermediates.txPower
-                        + intermediates.scanPower + intermediates.idlePower;
-            } else {
-                totalPower += intermediates.activePower + intermediates.basicScanPower
-                        + intermediates.batchedScanPower;
-            }
-            totalConsumedEnergy += intermediates.consumedEnergy;
-        }
-
-        if (totalPower == 0) {
-            return 1;
-        }
-
-        return uCtoMah(totalConsumedEnergy) / totalPower;
-    }
-
-    /**
-     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
-     * estimated power matches the measured consumed power.  We are not claiming that all
-     * averages captured in the power profile have to be off by the same percentage in reality.
-     */
-    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
-            int[] deviceStates, Intermediates intermediates, double ratio) {
-        double adjutedPower;
-        if (mHasWifiPowerController) {
-            intermediates.rxPower *= ratio;
-            intermediates.txPower *= ratio;
-            intermediates.scanPower *= ratio;
-            intermediates.idlePower *= ratio;
-            adjutedPower = intermediates.rxPower + intermediates.txPower + intermediates.scanPower
-                    + intermediates.idlePower;
-        } else {
-            intermediates.activePower *= ratio;
-            intermediates.basicScanPower *= ratio;
-            intermediates.batchedScanPower *= ratio;
-            adjutedPower = intermediates.activePower + intermediates.basicScanPower
-                    + intermediates.batchedScanPower;
-        }
-
-        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
-            return;
-        }
-
-        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, adjutedPower);
-        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
-    }
-
-    /**
-     * Combine power estimates before distributing them proportionally to UIDs.
-     */
-    private void combineDeviceStateEstimates() {
-        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
-            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
-            Intermediates
-                    cdseIntermediates = new Intermediates();
-            cdse.intermediates = cdseIntermediates;
-            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
-            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
-                DeviceStateEstimation dse = deviceStateEstimations.get(j);
-                Intermediates intermediates = (Intermediates) dse.intermediates;
-                if (mHasWifiPowerController) {
-                    cdseIntermediates.rxPower += intermediates.rxPower;
-                    cdseIntermediates.txPower += intermediates.txPower;
-                    cdseIntermediates.scanPower += intermediates.scanPower;
-                    cdseIntermediates.idlePower += intermediates.idlePower;
-                } else {
-                    cdseIntermediates.activePower += intermediates.activePower;
-                    cdseIntermediates.basicScanPower += intermediates.basicScanPower;
-                    cdseIntermediates.batchedScanPower += intermediates.batchedScanPower;
-                }
-                cdseIntermediates.basicScanDuration += intermediates.basicScanDuration;
-                cdseIntermediates.batchedScanDuration += intermediates.batchedScanDuration;
-                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
-            }
-        }
-    }
-
-    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
-            intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
-        }
-    }
-
-    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
-            UidStateEstimate uidStateEstimate) {
-        Intermediates intermediates =
-                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
-        for (UidStateProportionalEstimate proportionalEstimate :
-                uidStateEstimate.proportionalEstimates) {
-            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
-                continue;
-            }
-
-            double power = 0;
-            if (mHasWifiPowerController) {
-                if (intermediates.rxPackets != 0) {
-                    power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
-                            / intermediates.rxPackets;
-                }
-                if (intermediates.txPackets != 0) {
-                    power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
-                            / intermediates.txPackets;
-                }
-                long totalScanDuration =
-                        intermediates.basicScanDuration + intermediates.batchedScanDuration;
-                if (totalScanDuration != 0) {
-                    long scanDuration = mStatsLayout.getUidScanTime(mTmpUidStatsArray)
-                            + mStatsLayout.getUidBatchedScanTime(mTmpUidStatsArray);
-                    power += intermediates.scanPower * scanDuration / totalScanDuration;
-                }
-            } else {
-                long totalPackets = intermediates.rxPackets + intermediates.txPackets;
-                if (totalPackets != 0) {
-                    long packets = mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
-                            + mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
-                    power += intermediates.activePower * packets / totalPackets;
-                }
-
-                if (intermediates.basicScanDuration != 0) {
-                    long scanDuration = mStatsLayout.getUidScanTime(mTmpUidStatsArray);
-                    power += intermediates.basicScanPower * scanDuration
-                            / intermediates.basicScanDuration;
-                }
-
-                if (intermediates.batchedScanDuration != 0) {
-                    long batchedScanDuration = mStatsLayout.getUidBatchedScanTime(
-                            mTmpUidStatsArray);
-                    power += intermediates.batchedScanPower * batchedScanDuration
-                            / intermediates.batchedScanDuration;
-                }
-            }
-            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
-            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
-
-            if (DEBUG) {
-                Slog.d(TAG, "UID: " + uid
-                        + " states: " + Arrays.toString(proportionalEstimate.stateValues)
-                        + " stats: " + Arrays.toString(mTmpUidStatsArray)
-                        + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
-                        + " rx-power: " + intermediates.rxPower
-                        + " rx-packets: " + intermediates.rxPackets
-                        + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
-                        + " tx-power: " + intermediates.txPower
-                        + " tx-packets: " + intermediates.txPackets
-                        + " power: " + power);
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java
new file mode 100644
index 0000000..1b99b0d
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/AmbientDisplayPowerStatsLayout.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.format;
+
+public class AmbientDisplayPowerStatsLayout extends PowerStatsLayout {
+    public AmbientDisplayPowerStatsLayout() {
+        addDeviceSectionPowerEstimate();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java
new file mode 100644
index 0000000..4a26d83
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/BinaryStatePowerStatsLayout.java
@@ -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.server.power.stats.format;
+
+import com.android.internal.os.PowerStats;
+
+public class BinaryStatePowerStatsLayout extends EnergyConsumerPowerStatsLayout {
+    public BinaryStatePowerStatsLayout() {
+        addDeviceSectionUsageDuration();
+        addUidSectionUsageDuration();
+    }
+
+    public BinaryStatePowerStatsLayout(PowerStats.Descriptor descriptor) {
+        super(descriptor);
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java
new file mode 100644
index 0000000..534a9f7
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/BluetoothPowerStatsLayout.java
@@ -0,0 +1,143 @@
+/*
+ * 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.format;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class BluetoothPowerStatsLayout extends PowerStatsLayout {
+    private static final String EXTRA_DEVICE_RX_TIME_POSITION = "dt-rx";
+    private static final String EXTRA_DEVICE_TX_TIME_POSITION = "dt-tx";
+    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
+    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
+    private static final String EXTRA_UID_RX_BYTES_POSITION = "ub-rx";
+    private static final String EXTRA_UID_TX_BYTES_POSITION = "ub-tx";
+    private static final String EXTRA_UID_SCAN_TIME_POSITION = "ut-scan";
+
+    private int mDeviceRxTimePosition;
+    private int mDeviceTxTimePosition;
+    private int mDeviceIdleTimePosition;
+    private int mDeviceScanTimePosition;
+    private int mUidRxBytesPosition;
+    private int mUidTxBytesPosition;
+    private int mUidScanTimePosition;
+
+    public BluetoothPowerStatsLayout(int energyConsumerCount) {
+        addDeviceBluetoothControllerActivity();
+        addDeviceSectionEnergyConsumers(energyConsumerCount);
+        addDeviceSectionUsageDuration();
+        addDeviceSectionPowerEstimate();
+        addUidTrafficStats();
+        addUidSectionPowerEstimate();
+    }
+
+    public BluetoothPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
+        mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
+        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+        mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
+    }
+
+    /**
+     * Copies the elements of the stats array layout into <code>extras</code>
+     */
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
+        extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
+        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+        extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
+    }
+
+    private void addDeviceBluetoothControllerActivity() {
+        mDeviceRxTimePosition = addDeviceSection(1, "rx");
+        mDeviceTxTimePosition = addDeviceSection(1, "tx");
+        mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+        mDeviceScanTimePosition = addDeviceSection(1, "scan", FLAG_OPTIONAL);
+    }
+
+    private void addUidTrafficStats() {
+        mUidRxBytesPosition = addUidSection(1, "rx-B");
+        mUidTxBytesPosition = addUidSection(1, "tx-B");
+        mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
+    }
+
+    public void setDeviceRxTime(long[] stats, long durationMillis) {
+        stats[mDeviceRxTimePosition] = durationMillis;
+    }
+
+    public long getDeviceRxTime(long[] stats) {
+        return stats[mDeviceRxTimePosition];
+    }
+
+    public void setDeviceTxTime(long[] stats, long durationMillis) {
+        stats[mDeviceTxTimePosition] = durationMillis;
+    }
+
+    public long getDeviceTxTime(long[] stats) {
+        return stats[mDeviceTxTimePosition];
+    }
+
+    public void setDeviceIdleTime(long[] stats, long durationMillis) {
+        stats[mDeviceIdleTimePosition] = durationMillis;
+    }
+
+    public long getDeviceIdleTime(long[] stats) {
+        return stats[mDeviceIdleTimePosition];
+    }
+
+    public void setDeviceScanTime(long[] stats, long durationMillis) {
+        stats[mDeviceScanTimePosition] = durationMillis;
+    }
+
+    public long getDeviceScanTime(long[] stats) {
+        return stats[mDeviceScanTimePosition];
+    }
+
+    public void setUidRxBytes(long[] stats, long count) {
+        stats[mUidRxBytesPosition] = count;
+    }
+
+    public long getUidRxBytes(long[] stats) {
+        return stats[mUidRxBytesPosition];
+    }
+
+    public void setUidTxBytes(long[] stats, long count) {
+        stats[mUidTxBytesPosition] = count;
+    }
+
+    public long getUidTxBytes(long[] stats) {
+        return stats[mUidTxBytesPosition];
+    }
+
+    public void setUidScanTime(long[] stats, long count) {
+        stats[mUidScanTimePosition] = count;
+    }
+
+    public long getUidScanTime(long[] stats) {
+        return stats[mUidScanTimePosition];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java
new file mode 100644
index 0000000..3186d7d
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/CpuPowerStatsLayout.java
@@ -0,0 +1,190 @@
+/*
+ * 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.format;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class CpuPowerStatsLayout extends PowerStatsLayout {
+    private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION = "dt";
+    private static final String EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT = "dtc";
+    private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION = "dc";
+    private static final String EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT = "dcc";
+    private static final String EXTRA_UID_BRACKETS_POSITION = "ub";
+    private static final String EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET = "us";
+
+    private int mDeviceCpuTimeByScalingStepPosition;
+    private int mDeviceCpuTimeByScalingStepCount;
+    private int mDeviceCpuTimeByClusterPosition;
+    private int mDeviceCpuTimeByClusterCount;
+
+    private int mUidPowerBracketsPosition;
+    private int mUidPowerBracketCount;
+
+    private int[] mScalingStepToPowerBracketMap;
+
+    public CpuPowerStatsLayout(int energyConsumerCount, int cpuScalingPolicyCount,
+            int[] scalingStepToPowerBracketMap) {
+        addDeviceSectionCpuTimeByScalingStep(scalingStepToPowerBracketMap.length);
+        addDeviceSectionCpuTimeByCluster(cpuScalingPolicyCount);
+        addDeviceSectionUsageDuration();
+        addDeviceSectionEnergyConsumers(energyConsumerCount);
+        addDeviceSectionPowerEstimate();
+        addUidSectionCpuTimeByPowerBracket(scalingStepToPowerBracketMap);
+        addUidSectionPowerEstimate();
+    }
+
+    public CpuPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mDeviceCpuTimeByScalingStepPosition =
+                extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION);
+        mDeviceCpuTimeByScalingStepCount =
+                extras.getInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT);
+        mDeviceCpuTimeByClusterPosition =
+                extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION);
+        mDeviceCpuTimeByClusterCount =
+                extras.getInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT);
+        mUidPowerBracketsPosition = extras.getInt(EXTRA_UID_BRACKETS_POSITION);
+        mScalingStepToPowerBracketMap =
+                getIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET);
+        if (mScalingStepToPowerBracketMap == null) {
+            mScalingStepToPowerBracketMap = new int[mDeviceCpuTimeByScalingStepCount];
+        }
+        updatePowerBracketCount();
+    }
+
+    /**
+     * Copies the elements of the stats array layout into <code>extras</code>
+     */
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_POSITION,
+                mDeviceCpuTimeByScalingStepPosition);
+        extras.putInt(EXTRA_DEVICE_TIME_BY_SCALING_STEP_COUNT,
+                mDeviceCpuTimeByScalingStepCount);
+        extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_POSITION,
+                mDeviceCpuTimeByClusterPosition);
+        extras.putInt(EXTRA_DEVICE_TIME_BY_CLUSTER_COUNT,
+                mDeviceCpuTimeByClusterCount);
+        extras.putInt(EXTRA_UID_BRACKETS_POSITION, mUidPowerBracketsPosition);
+        putIntArray(extras, EXTRA_UID_STATS_SCALING_STEP_TO_POWER_BRACKET,
+                mScalingStepToPowerBracketMap);
+    }
+
+    /**
+     * Declare that the stats array has a section capturing CPU time per scaling step
+     */
+    private void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
+        mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps");
+        mDeviceCpuTimeByScalingStepCount = scalingStepCount;
+    }
+
+    public int getCpuScalingStepCount() {
+        return mDeviceCpuTimeByScalingStepCount;
+    }
+
+    /**
+     * Saves the time duration in the <code>stats</code> element
+     * corresponding to the CPU scaling <code>state</code>.
+     */
+    public void setTimeByScalingStep(long[] stats, int step, long value) {
+        stats[mDeviceCpuTimeByScalingStepPosition + step] = value;
+    }
+
+    /**
+     * Extracts the time duration from the <code>stats</code> element
+     * corresponding to the CPU scaling <code>step</code>.
+     */
+    public long getTimeByScalingStep(long[] stats, int step) {
+        return stats[mDeviceCpuTimeByScalingStepPosition + step];
+    }
+
+    /**
+     * Declare that the stats array has a section capturing CPU time in each cluster
+     */
+    private void addDeviceSectionCpuTimeByCluster(int clusterCount) {
+        mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters");
+        mDeviceCpuTimeByClusterCount = clusterCount;
+    }
+
+    public int getCpuClusterCount() {
+        return mDeviceCpuTimeByClusterCount;
+    }
+
+    /**
+     * Saves the time duration in the <code>stats</code> element
+     * corresponding to the CPU <code>cluster</code>.
+     */
+    public void setTimeByCluster(long[] stats, int cluster, long value) {
+        stats[mDeviceCpuTimeByClusterPosition + cluster] = value;
+    }
+
+    /**
+     * Extracts the time duration from the <code>stats</code> element
+     * corresponding to the CPU <code>cluster</code>.
+     */
+    public long getTimeByCluster(long[] stats, int cluster) {
+        return stats[mDeviceCpuTimeByClusterPosition + cluster];
+    }
+
+    /**
+     * Declare that the UID stats array has a section capturing CPU time per power bracket.
+     */
+    private void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
+        mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
+        updatePowerBracketCount();
+        mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time");
+    }
+
+    private void updatePowerBracketCount() {
+        mUidPowerBracketCount = 1;
+        for (int bracket : mScalingStepToPowerBracketMap) {
+            if (bracket >= mUidPowerBracketCount) {
+                mUidPowerBracketCount = bracket + 1;
+            }
+        }
+    }
+
+    public int[] getScalingStepToPowerBracketMap() {
+        return mScalingStepToPowerBracketMap;
+    }
+
+    public int getCpuPowerBracketCount() {
+        return mUidPowerBracketCount;
+    }
+
+    /**
+     * Saves time in <code>bracket</code> in the corresponding section of <code>stats</code>.
+     */
+    public void setUidTimeByPowerBracket(long[] stats, int bracket, long value) {
+        stats[mUidPowerBracketsPosition + bracket] = value;
+    }
+
+    /**
+     * Extracts the time in <code>bracket</code> from a UID stats array.
+     */
+    public long getUidTimeByPowerBracket(long[] stats, int bracket) {
+        return stats[mUidPowerBracketsPosition + bracket];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java
new file mode 100644
index 0000000..e7a4822
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/EnergyConsumerPowerStatsLayout.java
@@ -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.server.power.stats.format;
+
+import com.android.internal.os.PowerStats;
+
+public class EnergyConsumerPowerStatsLayout extends PowerStatsLayout {
+    public EnergyConsumerPowerStatsLayout() {
+        // Add a section for consumed energy, even if the specific device does not
+        // have support EnergyConsumers.  This is done to guarantee format compatibility between
+        // PowerStats created by a PowerStatsCollector and those produced by a PowerStatsProcessor.
+        addDeviceSectionEnergyConsumers(1);
+        addDeviceSectionPowerEstimate();
+
+        // Allocate a cell for per-UID consumed energy attribution. We won't know whether the
+        // corresponding energy consumer does per-UID attribution until we get data from
+        // PowerStatsService.
+        addUidSectionEnergyConsumers(1);
+        addUidSectionPowerEstimate();
+    }
+
+    public EnergyConsumerPowerStatsLayout(PowerStats.Descriptor descriptor) {
+        super(descriptor);
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java
new file mode 100644
index 0000000..b70b173
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/GnssPowerStatsLayout.java
@@ -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.server.power.stats.format;
+
+import android.annotation.NonNull;
+import android.location.GnssSignalQuality;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class GnssPowerStatsLayout extends BinaryStatePowerStatsLayout {
+    private static final String EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION = "dt-sig";
+    private static final String EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION = "ut-sig";
+
+    private final int mDeviceSignalLevelTimePosition;
+    private final int mUidSignalLevelTimePosition;
+
+    public GnssPowerStatsLayout() {
+        mDeviceSignalLevelTimePosition = addDeviceSection(
+                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+        mUidSignalLevelTimePosition = addUidSection(
+                GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS, "level");
+    }
+
+    public GnssPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mDeviceSignalLevelTimePosition = extras.getInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION);
+        mUidSignalLevelTimePosition = extras.getInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION);
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_TIME_SIGNAL_LEVEL_POSITION, mDeviceSignalLevelTimePosition);
+        extras.putInt(EXTRA_UID_TIME_SIGNAL_LEVEL_POSITION, mUidSignalLevelTimePosition);
+    }
+
+    public void setDeviceSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+        stats[mDeviceSignalLevelTimePosition + signalLevel] = durationMillis;
+    }
+
+    public long getDeviceSignalLevelTime(long[] stats, int signalLevel) {
+        return stats[mDeviceSignalLevelTimePosition + signalLevel];
+    }
+
+    public void setUidSignalLevelTime(long[] stats, int signalLevel, long durationMillis) {
+        stats[mUidSignalLevelTimePosition + signalLevel] = durationMillis;
+    }
+
+    public long getUidSignalLevelTime(long[] stats, int signalLevel) {
+        return stats[mUidSignalLevelTimePosition + signalLevel];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java
new file mode 100644
index 0000000..da6fc41
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/MobileRadioPowerStatsLayout.java
@@ -0,0 +1,287 @@
+/*
+ * 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.format;
+
+import android.annotation.NonNull;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ModemActivityInfo;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class MobileRadioPowerStatsLayout extends PowerStatsLayout {
+    private static final String TAG = "MobileRadioPowerStatsLayout";
+    private static final String EXTRA_DEVICE_SLEEP_TIME_POSITION = "dt-sleep";
+    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
+    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
+    private static final String EXTRA_DEVICE_CALL_TIME_POSITION = "dt-call";
+    private static final String EXTRA_DEVICE_CALL_POWER_POSITION = "dp-call";
+    private static final String EXTRA_STATE_RX_TIME_POSITION = "srx";
+    private static final String EXTRA_STATE_TX_TIMES_POSITION = "stx";
+    private static final String EXTRA_STATE_TX_TIMES_COUNT = "stxc";
+    private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
+    private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
+    private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
+    private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
+
+    private int mDeviceSleepTimePosition;
+    private int mDeviceIdleTimePosition;
+    private int mDeviceScanTimePosition;
+    private int mDeviceCallTimePosition;
+    private int mDeviceCallPowerPosition;
+    private int mStateRxTimePosition;
+    private int mStateTxTimesPosition;
+    private int mStateTxTimesCount;
+    private int mUidRxBytesPosition;
+    private int mUidTxBytesPosition;
+    private int mUidRxPacketsPosition;
+    private int mUidTxPacketsPosition;
+
+    public MobileRadioPowerStatsLayout(int energyConsumerCount) {
+        addDeviceMobileActivity();
+        addDeviceSectionEnergyConsumers(energyConsumerCount);
+        addStateStats();
+        addUidNetworkStats();
+        addDeviceSectionUsageDuration();
+        addDeviceSectionPowerEstimate();
+        addUidSectionPowerEstimate();
+    }
+
+    public MobileRadioPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mDeviceSleepTimePosition = extras.getInt(EXTRA_DEVICE_SLEEP_TIME_POSITION);
+        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+        mDeviceCallTimePosition = extras.getInt(EXTRA_DEVICE_CALL_TIME_POSITION);
+        mDeviceCallPowerPosition = extras.getInt(EXTRA_DEVICE_CALL_POWER_POSITION);
+        mStateRxTimePosition = extras.getInt(EXTRA_STATE_RX_TIME_POSITION);
+        mStateTxTimesPosition = extras.getInt(EXTRA_STATE_TX_TIMES_POSITION);
+        mStateTxTimesCount = extras.getInt(EXTRA_STATE_TX_TIMES_COUNT);
+        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+        mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+        mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
+    }
+
+    /**
+     * Copies the elements of the stats array layout into <code>extras</code>
+     */
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_SLEEP_TIME_POSITION, mDeviceSleepTimePosition);
+        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+        extras.putInt(EXTRA_DEVICE_CALL_TIME_POSITION, mDeviceCallTimePosition);
+        extras.putInt(EXTRA_DEVICE_CALL_POWER_POSITION, mDeviceCallPowerPosition);
+        extras.putInt(EXTRA_STATE_RX_TIME_POSITION, mStateRxTimePosition);
+        extras.putInt(EXTRA_STATE_TX_TIMES_POSITION, mStateTxTimesPosition);
+        extras.putInt(EXTRA_STATE_TX_TIMES_COUNT, mStateTxTimesCount);
+        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+        extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+        extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+    }
+
+    public static int makeStateKey(int rat, int freqRange) {
+        if (rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR) {
+            return rat | (freqRange << 8);
+        } else {
+            return rat;
+        }
+    }
+
+    @BatteryStats.RadioAccessTechnology
+    public static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
+            @AccessNetworkConstants.RadioAccessNetworkType int networkType) {
+        switch (networkType) {
+            case AccessNetworkConstants.AccessNetworkType.NGRAN:
+                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR;
+            case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE;
+            case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
+            case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
+            case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
+            case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
+            case AccessNetworkConstants.AccessNetworkType.IWLAN:
+                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+            default:
+                Slog.w(TAG,
+                        "Unhandled RadioAccessNetworkType (" + networkType + "), mapping to OTHER");
+                return BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+        }
+    }
+
+    private void addDeviceMobileActivity() {
+        mDeviceSleepTimePosition = addDeviceSection(1, "sleep");
+        mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+        mDeviceScanTimePosition = addDeviceSection(1, "scan");
+        mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL);
+    }
+
+    private void addStateStats() {
+        mStateRxTimePosition = addStateSection(1, "rx");
+        mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
+        mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx");
+    }
+
+    private void addUidNetworkStats() {
+        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+        mUidRxBytesPosition = addUidSection(1, "rx-B");
+        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+        mUidTxBytesPosition = addUidSection(1, "tx-B");
+    }
+
+    @Override
+    protected void addDeviceSectionPowerEstimate() {
+        super.addDeviceSectionPowerEstimate();
+        // Printed as part of the PhoneCallPowerStatsProcessor
+        mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN);
+    }
+
+    public void setDeviceSleepTime(long[] stats, long durationMillis) {
+        stats[mDeviceSleepTimePosition] = durationMillis;
+    }
+
+    public long getDeviceSleepTime(long[] stats) {
+        return stats[mDeviceSleepTimePosition];
+    }
+
+    public void setDeviceIdleTime(long[] stats, long durationMillis) {
+        stats[mDeviceIdleTimePosition] = durationMillis;
+    }
+
+    public long getDeviceIdleTime(long[] stats) {
+        return stats[mDeviceIdleTimePosition];
+    }
+
+    public void setDeviceScanTime(long[] stats, long durationMillis) {
+        stats[mDeviceScanTimePosition] = durationMillis;
+    }
+
+    public long getDeviceScanTime(long[] stats) {
+        return stats[mDeviceScanTimePosition];
+    }
+
+    public void setDeviceCallTime(long[] stats, long durationMillis) {
+        stats[mDeviceCallTimePosition] = durationMillis;
+    }
+
+    public long getDeviceCallTime(long[] stats) {
+        return stats[mDeviceCallTimePosition];
+    }
+
+    public void setDeviceCallPowerEstimate(long[] stats, double power) {
+        stats[mDeviceCallPowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+    }
+
+    public double getDeviceCallPowerEstimate(long[] stats) {
+        return stats[mDeviceCallPowerPosition] / MILLI_TO_NANO_MULTIPLIER;
+    }
+
+    public void setStateRxTime(long[] stats, long durationMillis) {
+        stats[mStateRxTimePosition] = durationMillis;
+    }
+
+    public long getStateRxTime(long[] stats) {
+        return stats[mStateRxTimePosition];
+    }
+
+    public void setStateTxTime(long[] stats, int level, int durationMillis) {
+        stats[mStateTxTimesPosition + level] = durationMillis;
+    }
+
+    public long getStateTxTime(long[] stats, int level) {
+        return stats[mStateTxTimesPosition + level];
+    }
+
+    public void setUidRxBytes(long[] stats, long count) {
+        stats[mUidRxBytesPosition] = count;
+    }
+
+    public long getUidRxBytes(long[] stats) {
+        return stats[mUidRxBytesPosition];
+    }
+
+    public void setUidTxBytes(long[] stats, long count) {
+        stats[mUidTxBytesPosition] = count;
+    }
+
+    public long getUidTxBytes(long[] stats) {
+        return stats[mUidTxBytesPosition];
+    }
+
+    public void setUidRxPackets(long[] stats, long count) {
+        stats[mUidRxPacketsPosition] = count;
+    }
+
+    public long getUidRxPackets(long[] stats) {
+        return stats[mUidRxPacketsPosition];
+    }
+
+    public void setUidTxPackets(long[] stats, long count) {
+        stats[mUidTxPacketsPosition] = count;
+    }
+
+    public long getUidTxPackets(long[] stats) {
+        return stats[mUidTxPacketsPosition];
+    }
+
+    public void addRxTxTimesForRat(SparseArray<long[]> stateStats, int networkType, int freqRange,
+            long rxTime, int[] txTime) {
+        if (txTime.length != mStateTxTimesCount) {
+            Slog.wtf(TAG, "Invalid TX time array size: " + txTime.length);
+            return;
+        }
+
+        boolean nonZero = false;
+        if (rxTime != 0) {
+            nonZero = true;
+        } else {
+            for (int i = txTime.length - 1; i >= 0; i--) {
+                if (txTime[i] != 0) {
+                    nonZero = true;
+                    break;
+                }
+            }
+        }
+
+        if (!nonZero) {
+            return;
+        }
+
+        int rat = mapRadioAccessNetworkTypeToRadioAccessTechnology(networkType);
+        int stateKey = makeStateKey(rat, freqRange);
+        long[] stats = stateStats.get(stateKey);
+        if (stats == null) {
+            stats = new long[getStateStatsArrayLength()];
+            stateStats.put(stateKey, stats);
+        }
+
+        stats[mStateRxTimePosition] += rxTime;
+        for (int i = mStateTxTimesCount - 1; i >= 0; i--) {
+            stats[mStateTxTimesPosition + i] += txTime[i];
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java
new file mode 100644
index 0000000..5a34148
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/PhoneCallPowerStatsLayout.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.format;
+
+public class PhoneCallPowerStatsLayout extends PowerStatsLayout {
+    public PhoneCallPowerStatsLayout() {
+        addDeviceSectionPowerEstimate();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java
new file mode 100644
index 0000000..d070919
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/PowerStatsLayout.java
@@ -0,0 +1,352 @@
+/*
+ * 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.format;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as usage duration,
+ * power usage estimates etc.
+ */
+public class PowerStatsLayout {
+    private static final String TAG = "PowerStatsLayout";
+    private static final String EXTRA_DEVICE_POWER_POSITION = "dp";
+    private static final String EXTRA_DEVICE_DURATION_POSITION = "dd";
+    private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION = "de";
+    private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
+    private static final String EXTRA_UID_DURATION_POSITION = "ud";
+    private static final String EXTRA_UID_ENERGY_CONSUMERS_POSITION = "ue";
+    private static final String EXTRA_UID_ENERGY_CONSUMERS_COUNT = "uec";
+    private static final String EXTRA_UID_POWER_POSITION = "up";
+
+    public static final int UNSUPPORTED = -1;
+    protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+    protected static final int FLAG_OPTIONAL = 1;
+    protected static final int FLAG_HIDDEN = 2;
+    protected static final int FLAG_FORMAT_AS_POWER = 4;
+
+    private int mDeviceStatsArrayLength;
+    private int mStateStatsArrayLength;
+    private int mUidStatsArrayLength;
+
+    private final StringBuilder mDeviceFormat = new StringBuilder();
+    private final StringBuilder mStateFormat = new StringBuilder();
+    private final StringBuilder mUidFormat = new StringBuilder();
+
+    protected int mDeviceDurationPosition = UNSUPPORTED;
+    private int mDeviceEnergyConsumerPosition;
+    private int mDeviceEnergyConsumerCount;
+    private int mDevicePowerEstimatePosition = UNSUPPORTED;
+    private int mUidDurationPosition = UNSUPPORTED;
+    private int mUidEnergyConsumerPosition = UNSUPPORTED;
+    private int mUidEnergyConsumerCount;
+    private int mUidPowerEstimatePosition = UNSUPPORTED;
+
+    public PowerStatsLayout() {
+    }
+
+    public PowerStatsLayout(PowerStats.Descriptor descriptor) {
+        PersistableBundle extras = descriptor.extras;
+        mDeviceDurationPosition = extras.getInt(EXTRA_DEVICE_DURATION_POSITION);
+        mDeviceEnergyConsumerPosition = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION);
+        mDeviceEnergyConsumerCount = extras.getInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT);
+        mDevicePowerEstimatePosition = extras.getInt(EXTRA_DEVICE_POWER_POSITION);
+        mUidDurationPosition = extras.getInt(EXTRA_UID_DURATION_POSITION);
+        mUidEnergyConsumerPosition = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION);
+        mUidEnergyConsumerCount = extras.getInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT);
+        mUidPowerEstimatePosition = extras.getInt(EXTRA_UID_POWER_POSITION);
+    }
+
+    /**
+     * Copies the elements of the stats array layout into <code>extras</code>
+     */
+    public void toExtras(PersistableBundle extras) {
+        extras.putInt(EXTRA_DEVICE_DURATION_POSITION, mDeviceDurationPosition);
+        extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_POSITION, mDeviceEnergyConsumerPosition);
+        extras.putInt(EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT, mDeviceEnergyConsumerCount);
+        extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
+        extras.putInt(EXTRA_UID_DURATION_POSITION, mUidDurationPosition);
+        extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_POSITION, mUidEnergyConsumerPosition);
+        extras.putInt(EXTRA_UID_ENERGY_CONSUMERS_COUNT, mUidEnergyConsumerCount);
+        extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+        extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
+        extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
+        extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
+    }
+
+    public int getDeviceStatsArrayLength() {
+        return mDeviceStatsArrayLength;
+    }
+
+    public int getStateStatsArrayLength() {
+        return mStateStatsArrayLength;
+    }
+
+    public int getUidStatsArrayLength() {
+        return mUidStatsArrayLength;
+    }
+
+    /**
+     * @param label should not contain either spaces or colons
+     */
+    private void appendFormat(StringBuilder sb, int position, int length, String label,
+            int flags) {
+        if ((flags & FLAG_HIDDEN) != 0) {
+            return;
+        }
+
+        if (!sb.isEmpty()) {
+            sb.append(' ');
+        }
+
+        sb.append(label).append(':');
+        sb.append(position);
+        if (length != 1) {
+            sb.append('[').append(length).append(']');
+        }
+        if ((flags & FLAG_FORMAT_AS_POWER) != 0) {
+            sb.append('p');
+        }
+        if ((flags & FLAG_OPTIONAL) != 0) {
+            sb.append('?');
+        }
+    }
+
+    protected int addDeviceSection(int length, String label, int flags) {
+        int position = mDeviceStatsArrayLength;
+        mDeviceStatsArrayLength += length;
+        appendFormat(mDeviceFormat, position, length, label, flags);
+        return position;
+    }
+
+    protected int addDeviceSection(int length, String label) {
+        return addDeviceSection(length, label, 0);
+    }
+
+    protected int addStateSection(int length, String label, int flags) {
+        int position = mStateStatsArrayLength;
+        mStateStatsArrayLength += length;
+        appendFormat(mStateFormat, position, length, label, flags);
+        return position;
+    }
+
+    protected int addStateSection(int length, String label) {
+        return addStateSection(length, label, 0);
+    }
+
+
+    protected int addUidSection(int length, String label, int flags) {
+        int position = mUidStatsArrayLength;
+        mUidStatsArrayLength += length;
+        appendFormat(mUidFormat, position, length, label, flags);
+        return position;
+    }
+
+    protected int addUidSection(int length, String label) {
+        return addUidSection(length, label, 0);
+    }
+
+    /**
+     * Declare that the stats array has a section capturing usage duration
+     */
+    protected void addDeviceSectionUsageDuration() {
+        mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
+    }
+
+    /**
+     * Saves the usage duration in the corresponding <code>stats</code> element.
+     */
+    public void setUsageDuration(long[] stats, long value) {
+        stats[mDeviceDurationPosition] = value;
+    }
+
+    /**
+     * Extracts the usage duration from the corresponding <code>stats</code> element.
+     */
+    public long getUsageDuration(long[] stats) {
+        return stats[mDeviceDurationPosition];
+    }
+
+    /**
+     * Declares that the stats array has a section capturing EnergyConsumer data from
+     * PowerStatsService.
+     */
+    protected void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
+        mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy",
+                FLAG_OPTIONAL);
+        mDeviceEnergyConsumerCount = energyConsumerCount;
+    }
+
+    public int getEnergyConsumerCount() {
+        return mDeviceEnergyConsumerCount;
+    }
+
+    /**
+     * Saves the accumulated energy for the specified rail the corresponding
+     * <code>stats</code> element.
+     */
+    public void setConsumedEnergy(long[] stats, int index, long energy) {
+        stats[mDeviceEnergyConsumerPosition + index] = energy;
+    }
+
+    /**
+     * Extracts the EnergyConsumer data from a device stats array for the specified
+     * EnergyConsumer.
+     */
+    public long getConsumedEnergy(long[] stats, int index) {
+        return stats[mDeviceEnergyConsumerPosition + index];
+    }
+
+    /**
+     * Declare that the stats array has a section capturing a power estimate
+     */
+    protected void addDeviceSectionPowerEstimate() {
+        mDevicePowerEstimatePosition = addDeviceSection(1, "power",
+                FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
+    }
+
+    /**
+     * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+     * element of <code>stats</code>.
+     */
+    public void setDevicePowerEstimate(long[] stats, double power) {
+        stats[mDevicePowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+    }
+
+    /**
+     * Extracts the power estimate from a device stats array and converts it to mAh.
+     */
+    public double getDevicePowerEstimate(long[] stats) {
+        return stats[mDevicePowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+    }
+
+    /**
+     * Declare that the UID stats array has a section capturing usage duration
+     */
+    protected void addUidSectionUsageDuration() {
+        mUidDurationPosition = addUidSection(1, "time");
+    }
+
+    /**
+     * Declare that the UID stats array has a section capturing a power estimate
+     */
+    protected void addUidSectionPowerEstimate() {
+        mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
+    }
+
+    /**
+     * Returns true if power for this component is attributed to UIDs (apps).
+     */
+    public boolean isUidPowerAttributionSupported() {
+        return mUidPowerEstimatePosition != UNSUPPORTED;
+    }
+
+    /**
+     * Saves usage duration it in the corresponding element of <code>stats</code>.
+     */
+    public void setUidUsageDuration(long[] stats, long durationMs) {
+        stats[mUidDurationPosition] = durationMs;
+    }
+
+    /**
+     * Extracts the usage duration from a UID stats array.
+     */
+    public long getUidUsageDuration(long[] stats) {
+        return stats[mUidDurationPosition];
+    }
+
+    /**
+     * Declares that the UID stats array has a section capturing EnergyConsumer data from
+     * PowerStatsService.
+     */
+    protected void addUidSectionEnergyConsumers(int energyConsumerCount) {
+        mUidEnergyConsumerPosition = addUidSection(energyConsumerCount, "energy",
+                FLAG_OPTIONAL);
+        mUidEnergyConsumerCount = energyConsumerCount;
+    }
+
+    public int getUidEnergyConsumerCount() {
+        return mUidEnergyConsumerCount;
+    }
+
+    /**
+     * Saves the accumulated energy for the specified rail the corresponding
+     * <code>stats</code> element.
+     */
+    public void setUidConsumedEnergy(long[] stats, int index, long energy) {
+        stats[mUidEnergyConsumerPosition + index] = energy;
+    }
+
+    /**
+     * Extracts the EnergyConsumer data from a uid stats array for the specified
+     * EnergyConsumer.
+     */
+    public long getUidConsumedEnergy(long[] stats, int index) {
+        return stats[mUidEnergyConsumerPosition + index];
+    }
+
+    /**
+     * Converts the supplied mAh power estimate to a long and saves it in the corresponding
+     * element of <code>stats</code>.
+     */
+    public void setUidPowerEstimate(long[] stats, double power) {
+        stats[mUidPowerEstimatePosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+    }
+
+    /**
+     * Extracts the power estimate from a UID stats array and converts it to mAh.
+     */
+    public double getUidPowerEstimate(long[] stats) {
+        return stats[mUidPowerEstimatePosition] / MILLI_TO_NANO_MULTIPLIER;
+    }
+
+    protected void putIntArray(PersistableBundle extras, String key, int[] array) {
+        if (array == null) {
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (int value : array) {
+            if (!sb.isEmpty()) {
+                sb.append(',');
+            }
+            sb.append(value);
+        }
+        extras.putString(key, sb.toString());
+    }
+
+    protected int[] getIntArray(PersistableBundle extras, String key) {
+        String string = extras.getString(key);
+        if (string == null) {
+            return null;
+        }
+        String[] values = string.trim().split(",");
+        int[] result = new int[values.length];
+        for (int i = 0; i < values.length; i++) {
+            try {
+                result[i] = Integer.parseInt(values[i]);
+            } catch (NumberFormatException e) {
+                Slog.wtf(TAG, "Invalid CSV format: " + string);
+                return null;
+            }
+        }
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
new file mode 100644
index 0000000..6f6a7ff
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/ScreenPowerStatsLayout.java
@@ -0,0 +1,173 @@
+/*
+ * 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.format;
+
+import android.annotation.NonNull;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+/**
+ * Captures the positions and lengths of sections of the stats array, such as time-in-state,
+ * power usage estimates etc.
+ */
+public class ScreenPowerStatsLayout extends PowerStatsLayout {
+    private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc";
+    private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd";
+    private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd";
+    private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd";
+    private static final String EXTRA_DEVICE_DOZE_POWER_POSITION = "ddp";
+    private static final String EXTRA_UID_FOREGROUND_DURATION = "uf";
+
+    private int mDisplayCount;
+    private int mDeviceScreenOnDurationPosition;
+    private int[] mDeviceBrightnessDurationPositions;
+    private int mDeviceScreenDozeDurationPosition;
+    private int mDeviceScreenDozePowerPosition;
+    private int mUidTopActivityTimePosition;
+
+    public ScreenPowerStatsLayout(int energyConsumerCount, int displayCount) {
+        addDeviceScreenUsageDurationSection(displayCount);
+        addDeviceSectionEnergyConsumers(energyConsumerCount);
+        addDeviceSectionUsageDuration();
+        addDeviceSectionPowerEstimate();
+        addUidTopActivitiyDuration();
+        addUidSectionPowerEstimate();
+    }
+
+    public ScreenPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
+        mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
+        mDeviceBrightnessDurationPositions = extras.getIntArray(
+                EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
+        mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
+        mDeviceScreenDozePowerPosition = extras.getInt(EXTRA_DEVICE_DOZE_POWER_POSITION);
+        mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
+        extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
+        extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
+                mDeviceBrightnessDurationPositions);
+        extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
+        extras.putInt(EXTRA_DEVICE_DOZE_POWER_POSITION, mDeviceScreenDozePowerPosition);
+        extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
+    }
+
+    private void addDeviceScreenUsageDurationSection(int displayCount) {
+        mDisplayCount = displayCount;
+        mDeviceScreenOnDurationPosition = addDeviceSection(displayCount, "on");
+        mDeviceBrightnessDurationPositions = new int[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
+        for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
+            mDeviceBrightnessDurationPositions[level] =
+                    addDeviceSection(displayCount, BatteryStats.SCREEN_BRIGHTNESS_NAMES[level]);
+        }
+        mDeviceScreenDozeDurationPosition = addDeviceSection(displayCount, "doze");
+    }
+
+    @Override
+    protected void addDeviceSectionPowerEstimate() {
+        super.addDeviceSectionPowerEstimate();
+        // Used by AmbientDisplayPowerStatsProcessor
+        mDeviceScreenDozePowerPosition = addDeviceSection(1, "doze-power", FLAG_HIDDEN);
+    }
+
+    public int getDisplayCount() {
+        return mDisplayCount;
+    }
+
+    /**
+     * Stores screen-on time for the specified display.
+     */
+    public void setScreenOnDuration(long[] stats, int display, long durationMs) {
+        stats[mDeviceScreenOnDurationPosition + display] = durationMs;
+    }
+
+    /**
+     * Returns screen-on time for the specified display.
+     */
+    public long getScreenOnDuration(long[] stats, int display) {
+        return stats[mDeviceScreenOnDurationPosition + display];
+    }
+
+    /**
+     * Stores time at the specified brightness level for the specified display.
+     */
+    public void setBrightnessLevelDuration(long[] stats, int display, int brightnessLevel,
+            long durationMs) {
+        stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display] = durationMs;
+    }
+
+    /**
+     * Returns time at the specified brightness level for the specified display.
+     */
+    public long getBrightnessLevelDuration(long[] stats, int display, int brightnessLevel) {
+        return stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display];
+    }
+
+    /**
+     * Stores time in the doze (ambient) state for the specified display.
+     */
+    public void setScreenDozeDuration(long[] stats, int display, long durationMs) {
+        stats[mDeviceScreenDozeDurationPosition + display] = durationMs;
+    }
+
+    /**
+     * Retrieves time in the doze (ambient) state for the specified display.
+     */
+    public long getScreenDozeDuration(long[] stats, int display) {
+        return stats[mDeviceScreenDozeDurationPosition + display];
+    }
+
+    /**
+     * Stores estimated power in the doze (ambient) state.
+     */
+    public void setScreenDozePowerEstimate(long[] stats, double power) {
+        stats[mDeviceScreenDozePowerPosition] = (long) (power * MILLI_TO_NANO_MULTIPLIER);
+    }
+
+    /**
+     * Retrieves estimated power in the doze (ambient) state.
+     */
+    public double getScreenDozePowerEstimate(long[] stats) {
+        return stats[mDeviceScreenDozePowerPosition] / MILLI_TO_NANO_MULTIPLIER;
+    }
+
+    private void addUidTopActivitiyDuration() {
+        mUidTopActivityTimePosition = addUidSection(1, "top");
+    }
+
+    /**
+     * Stores time the UID spent in the TOP state.
+     */
+    public void setUidTopActivityDuration(long[] stats, long durationMs) {
+        stats[mUidTopActivityTimePosition] = durationMs;
+    }
+
+    /**
+     * Returns time the UID spent in the TOP state.
+     */
+    public long getUidTopActivityDuration(long[] stats) {
+        return stats[mUidTopActivityTimePosition];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
new file mode 100644
index 0000000..e8df3dd
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/SensorPowerStatsLayout.java
@@ -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.server.power.stats.format;
+
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import com.android.internal.os.PowerStats;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class SensorPowerStatsLayout extends PowerStatsLayout {
+    private static final String TAG = "SensorPowerStatsLayout";
+    private static final String EXTRA_DEVICE_SENSOR_HANDLES = "dsh";
+    private static final String EXTRA_UID_SENSOR_POSITIONS = "usp";
+
+    private final SparseIntArray mSensorPositions = new SparseIntArray();
+
+    public SensorPowerStatsLayout(Map<Integer, String> idToLabelMap) {
+        Integer[] keys = new Integer[idToLabelMap.size()];
+        idToLabelMap.keySet().toArray(keys);
+        Arrays.sort(keys);
+        for (int i = 0; i < keys.length; i++) {
+            addUidSensorSection(keys[i], idToLabelMap.get(keys[i]));
+        }
+        addUidSectionPowerEstimate();
+        addDeviceSectionPowerEstimate();
+    }
+
+    public SensorPowerStatsLayout(PowerStats.Descriptor descriptor) {
+        super(descriptor);
+
+        PersistableBundle extras = descriptor.extras;
+        int[] handlers = extras.getIntArray(EXTRA_DEVICE_SENSOR_HANDLES);
+        int[] uidDurationPositions = extras.getIntArray(EXTRA_UID_SENSOR_POSITIONS);
+
+        if (handlers != null && uidDurationPositions != null) {
+            for (int i = 0; i < handlers.length; i++) {
+                mSensorPositions.put(handlers[i], uidDurationPositions[i]);
+            }
+        }
+    }
+
+    @Override
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+
+        int[] handlers = new int[mSensorPositions.size()];
+        int[] uidDurationPositions = new int[mSensorPositions.size()];
+
+        for (int i = 0; i < mSensorPositions.size(); i++) {
+            handlers[i] = mSensorPositions.keyAt(i);
+            uidDurationPositions[i] = mSensorPositions.valueAt(i);
+        }
+
+        extras.putIntArray(EXTRA_DEVICE_SENSOR_HANDLES, handlers);
+        extras.putIntArray(EXTRA_UID_SENSOR_POSITIONS, uidDurationPositions);
+    }
+
+    private void addUidSensorSection(int handle, String label) {
+        mSensorPositions.put(handle, addUidSection(1, label, FLAG_OPTIONAL));
+    }
+
+    /**
+     * Returns the position in the uid stats array of the duration element corresponding
+     * to the specified sensor identified by its handle.
+     */
+    public int getUidSensorDurationPosition(int handle) {
+        return mSensorPositions.get(handle, UNSUPPORTED);
+    }
+
+    /**
+     * Adds the specified duration to the accumulated timer for the specified sensor.
+     */
+    public void addUidSensorDuration(long[] stats, int handle, long durationMs) {
+        int position = mSensorPositions.get(handle, UNSUPPORTED);
+        if (position == UNSUPPORTED) {
+            Slog.e(TAG, "Unknown sensor: " + handle);
+            return;
+        }
+        stats[position] += durationMs;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java
new file mode 100644
index 0000000..ce7ef12
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/format/WifiPowerStatsLayout.java
@@ -0,0 +1,240 @@
+/*
+ * 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.format;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+
+public class WifiPowerStatsLayout extends PowerStatsLayout {
+    private static final int UNSPECIFIED = -1;
+    private static final String EXTRA_POWER_REPORTING_SUPPORTED = "prs";
+    private static final String EXTRA_DEVICE_RX_TIME_POSITION = "dt-rx";
+    private static final String EXTRA_DEVICE_TX_TIME_POSITION = "dt-tx";
+    private static final String EXTRA_DEVICE_SCAN_TIME_POSITION = "dt-scan";
+    private static final String EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION = "dt-basic-scan";
+    private static final String EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION = "dt-batch-scan";
+    private static final String EXTRA_DEVICE_IDLE_TIME_POSITION = "dt-idle";
+    private static final String EXTRA_DEVICE_ACTIVE_TIME_POSITION = "dt-on";
+    private static final String EXTRA_UID_RX_BYTES_POSITION = "urxb";
+    private static final String EXTRA_UID_TX_BYTES_POSITION = "utxb";
+    private static final String EXTRA_UID_RX_PACKETS_POSITION = "urxp";
+    private static final String EXTRA_UID_TX_PACKETS_POSITION = "utxp";
+    private static final String EXTRA_UID_SCAN_TIME_POSITION = "ut-scan";
+    private static final String EXTRA_UID_BATCH_SCAN_TIME_POSITION = "ut-bscan";
+
+    private boolean mPowerReportingSupported;
+    private int mDeviceRxTimePosition;
+    private int mDeviceTxTimePosition;
+    private int mDeviceIdleTimePosition;
+    private int mDeviceScanTimePosition;
+    private int mDeviceBasicScanTimePosition;
+    private int mDeviceBatchedScanTimePosition;
+    private int mDeviceActiveTimePosition;
+    private int mUidRxBytesPosition;
+    private int mUidTxBytesPosition;
+    private int mUidRxPacketsPosition;
+    private int mUidTxPacketsPosition;
+    private int mUidScanTimePosition;
+    private int mUidBatchScanTimePosition;
+
+    public WifiPowerStatsLayout(int energyConsumerCount, boolean powerReportingSupported) {
+        addDeviceWifiActivity(powerReportingSupported);
+        addDeviceSectionEnergyConsumers(energyConsumerCount);
+        addUidNetworkStats();
+        addDeviceSectionUsageDuration();
+        addDeviceSectionPowerEstimate();
+        addUidSectionPowerEstimate();
+    }
+
+    public WifiPowerStatsLayout(@NonNull PowerStats.Descriptor descriptor) {
+        super(descriptor);
+        PersistableBundle extras = descriptor.extras;
+        mPowerReportingSupported = extras.getBoolean(EXTRA_POWER_REPORTING_SUPPORTED);
+        mDeviceRxTimePosition = extras.getInt(EXTRA_DEVICE_RX_TIME_POSITION);
+        mDeviceTxTimePosition = extras.getInt(EXTRA_DEVICE_TX_TIME_POSITION);
+        mDeviceScanTimePosition = extras.getInt(EXTRA_DEVICE_SCAN_TIME_POSITION);
+        mDeviceBasicScanTimePosition = extras.getInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION);
+        mDeviceBatchedScanTimePosition = extras.getInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION);
+        mDeviceIdleTimePosition = extras.getInt(EXTRA_DEVICE_IDLE_TIME_POSITION);
+        mDeviceActiveTimePosition = extras.getInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION);
+        mUidRxBytesPosition = extras.getInt(EXTRA_UID_RX_BYTES_POSITION);
+        mUidTxBytesPosition = extras.getInt(EXTRA_UID_TX_BYTES_POSITION);
+        mUidRxPacketsPosition = extras.getInt(EXTRA_UID_RX_PACKETS_POSITION);
+        mUidTxPacketsPosition = extras.getInt(EXTRA_UID_TX_PACKETS_POSITION);
+        mUidScanTimePosition = extras.getInt(EXTRA_UID_SCAN_TIME_POSITION);
+        mUidBatchScanTimePosition = extras.getInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION);
+    }
+
+    /**
+     * Copies the elements of the stats array layout into <code>extras</code>
+     */
+    public void toExtras(PersistableBundle extras) {
+        super.toExtras(extras);
+        extras.putBoolean(EXTRA_POWER_REPORTING_SUPPORTED, mPowerReportingSupported);
+        extras.putInt(EXTRA_DEVICE_RX_TIME_POSITION, mDeviceRxTimePosition);
+        extras.putInt(EXTRA_DEVICE_TX_TIME_POSITION, mDeviceTxTimePosition);
+        extras.putInt(EXTRA_DEVICE_SCAN_TIME_POSITION, mDeviceScanTimePosition);
+        extras.putInt(EXTRA_DEVICE_BASIC_SCAN_TIME_POSITION, mDeviceBasicScanTimePosition);
+        extras.putInt(EXTRA_DEVICE_BATCHED_SCAN_TIME_POSITION, mDeviceBatchedScanTimePosition);
+        extras.putInt(EXTRA_DEVICE_IDLE_TIME_POSITION, mDeviceIdleTimePosition);
+        extras.putInt(EXTRA_DEVICE_ACTIVE_TIME_POSITION, mDeviceActiveTimePosition);
+        extras.putInt(EXTRA_UID_RX_BYTES_POSITION, mUidRxBytesPosition);
+        extras.putInt(EXTRA_UID_TX_BYTES_POSITION, mUidTxBytesPosition);
+        extras.putInt(EXTRA_UID_RX_PACKETS_POSITION, mUidRxPacketsPosition);
+        extras.putInt(EXTRA_UID_TX_PACKETS_POSITION, mUidTxPacketsPosition);
+        extras.putInt(EXTRA_UID_SCAN_TIME_POSITION, mUidScanTimePosition);
+        extras.putInt(EXTRA_UID_BATCH_SCAN_TIME_POSITION, mUidBatchScanTimePosition);
+    }
+
+    private void addDeviceWifiActivity(boolean powerReportingSupported) {
+        mPowerReportingSupported = powerReportingSupported;
+        if (mPowerReportingSupported) {
+            mDeviceActiveTimePosition = UNSPECIFIED;
+            mDeviceRxTimePosition = addDeviceSection(1, "rx");
+            mDeviceTxTimePosition = addDeviceSection(1, "tx");
+            mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+            mDeviceScanTimePosition = addDeviceSection(1, "scan");
+        } else {
+            mDeviceActiveTimePosition = addDeviceSection(1, "rx-tx");
+            mDeviceRxTimePosition = UNSPECIFIED;
+            mDeviceTxTimePosition = UNSPECIFIED;
+            mDeviceIdleTimePosition = UNSPECIFIED;
+            mDeviceScanTimePosition = UNSPECIFIED;
+        }
+        mDeviceBasicScanTimePosition = addDeviceSection(1, "basic-scan", FLAG_OPTIONAL);
+        mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL);
+    }
+
+    private void addUidNetworkStats() {
+        mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+        mUidRxBytesPosition = addUidSection(1, "rx-B");
+        mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+        mUidTxBytesPosition = addUidSection(1, "tx-B");
+        mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
+        mUidBatchScanTimePosition = addUidSection(1, "batched-scan", FLAG_OPTIONAL);
+    }
+
+    public boolean isPowerReportingSupported() {
+        return mPowerReportingSupported;
+    }
+
+    public void setDeviceRxTime(long[] stats, long durationMillis) {
+        stats[mDeviceRxTimePosition] = durationMillis;
+    }
+
+    public long getDeviceRxTime(long[] stats) {
+        return stats[mDeviceRxTimePosition];
+    }
+
+    public void setDeviceTxTime(long[] stats, long durationMillis) {
+        stats[mDeviceTxTimePosition] = durationMillis;
+    }
+
+    public long getDeviceTxTime(long[] stats) {
+        return stats[mDeviceTxTimePosition];
+    }
+
+    public void setDeviceScanTime(long[] stats, long durationMillis) {
+        stats[mDeviceScanTimePosition] = durationMillis;
+    }
+
+    public long getDeviceScanTime(long[] stats) {
+        return stats[mDeviceScanTimePosition];
+    }
+
+    public void setDeviceBasicScanTime(long[] stats, long durationMillis) {
+        stats[mDeviceBasicScanTimePosition] = durationMillis;
+    }
+
+    public long getDeviceBasicScanTime(long[] stats) {
+        return stats[mDeviceBasicScanTimePosition];
+    }
+
+    public void setDeviceBatchedScanTime(long[] stats, long durationMillis) {
+        stats[mDeviceBatchedScanTimePosition] = durationMillis;
+    }
+
+    public long getDeviceBatchedScanTime(long[] stats) {
+        return stats[mDeviceBatchedScanTimePosition];
+    }
+
+    public void setDeviceIdleTime(long[] stats, long durationMillis) {
+        stats[mDeviceIdleTimePosition] = durationMillis;
+    }
+
+    public long getDeviceIdleTime(long[] stats) {
+        return stats[mDeviceIdleTimePosition];
+    }
+
+    public void setDeviceActiveTime(long[] stats, long durationMillis) {
+        stats[mDeviceActiveTimePosition] = durationMillis;
+    }
+
+    public long getDeviceActiveTime(long[] stats) {
+        return stats[mDeviceActiveTimePosition];
+    }
+
+    public void setUidRxBytes(long[] stats, long count) {
+        stats[mUidRxBytesPosition] = count;
+    }
+
+    public long getUidRxBytes(long[] stats) {
+        return stats[mUidRxBytesPosition];
+    }
+
+    public void setUidTxBytes(long[] stats, long count) {
+        stats[mUidTxBytesPosition] = count;
+    }
+
+    public long getUidTxBytes(long[] stats) {
+        return stats[mUidTxBytesPosition];
+    }
+
+    public void setUidRxPackets(long[] stats, long count) {
+        stats[mUidRxPacketsPosition] = count;
+    }
+
+    public long getUidRxPackets(long[] stats) {
+        return stats[mUidRxPacketsPosition];
+    }
+
+    public void setUidTxPackets(long[] stats, long count) {
+        stats[mUidTxPacketsPosition] = count;
+    }
+
+    public long getUidTxPackets(long[] stats) {
+        return stats[mUidTxPacketsPosition];
+    }
+
+    public void setUidScanTime(long[] stats, long count) {
+        stats[mUidScanTimePosition] = count;
+    }
+
+    public long getUidScanTime(long[] stats) {
+        return stats[mUidScanTimePosition];
+    }
+
+    public void setUidBatchScanTime(long[] stats, long count) {
+        stats[mUidBatchScanTimePosition] = count;
+    }
+
+    public long getUidBatchedScanTime(long[] stats) {
+        return stats[mUidBatchScanTimePosition];
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
new file mode 100644
index 0000000..a4758dd
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStats.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
+
+import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.power.stats.processor.AggregatedPowerStatsConfig.PowerComponent;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
+
+/**
+ * This class represents aggregated power stats for a variety of power components (CPU, WiFi,
+ * etc) covering a specific period of power usage history.
+ */
+class AggregatedPowerStats {
+    private static final String TAG = "AggregatedPowerStats";
+    private static final int MAX_CLOCK_UPDATES = 100;
+    private static final String XML_TAG_AGGREGATED_POWER_STATS = "agg-power-stats";
+
+    private final AggregatedPowerStatsConfig mConfig;
+    private final SparseArray<PowerComponentAggregatedPowerStats> mPowerComponentStats;
+    private final PowerComponentAggregatedPowerStats mGenericPowerComponent;
+
+    static class ClockUpdate {
+        public long monotonicTime;
+        @CurrentTimeMillisLong
+        public long currentTime;
+    }
+
+    private final List<ClockUpdate> mClockUpdates = new ArrayList<>();
+
+    @DurationMillisLong
+    private long mDurationMs;
+
+    AggregatedPowerStats(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+        this(aggregatedPowerStatsConfig, new SparseBooleanArray());
+    }
+
+    AggregatedPowerStats(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig,
+            @NonNull SparseBooleanArray enabledComponents) {
+        mConfig = aggregatedPowerStatsConfig;
+        List<PowerComponent> configs =
+                aggregatedPowerStatsConfig.getPowerComponentsAggregatedStatsConfigs();
+        mPowerComponentStats = new SparseArray<>(configs.size());
+        for (int i = 0; i < configs.size(); i++) {
+            PowerComponent powerComponent = configs.get(i);
+            if (enabledComponents.get(powerComponent.getPowerComponentId(), true)) {
+                mPowerComponentStats.put(powerComponent.getPowerComponentId(),
+                        new PowerComponentAggregatedPowerStats(this, powerComponent));
+            }
+        }
+        mGenericPowerComponent = createGenericPowerComponent();
+        mPowerComponentStats.put(BatteryConsumer.POWER_COMPONENT_ANY, mGenericPowerComponent);
+    }
+
+    private PowerComponentAggregatedPowerStats createGenericPowerComponent() {
+        PowerComponent config = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
+        config.trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+        PowerComponentAggregatedPowerStats stats =
+                new PowerComponentAggregatedPowerStats(this, config);
+        stats.setPowerStatsDescriptor(
+                new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_ANY, 0, null, 0, 0,
+                        new PersistableBundle()));
+        return stats;
+    }
+
+    /**
+     * Records a mapping of monotonic time to wall-clock time. Since wall-clock time can change,
+     * there may be multiple clock updates in one set of aggregated stats.
+     *
+     * @param monotonicTime monotonic time in milliseconds, see
+     *                      {@link com.android.internal.os.MonotonicClock}
+     * @param currentTime   current time in milliseconds, see {@link System#currentTimeMillis()}
+     */
+    void addClockUpdate(long monotonicTime, @CurrentTimeMillisLong long currentTime) {
+        ClockUpdate clockUpdate = new ClockUpdate();
+        clockUpdate.monotonicTime = monotonicTime;
+        clockUpdate.currentTime = currentTime;
+        if (mClockUpdates.size() < MAX_CLOCK_UPDATES) {
+            mClockUpdates.add(clockUpdate);
+        } else {
+            Slog.i(TAG, "Too many clock updates. Replacing the previous update with "
+                    + DateFormat.format("yyyy-MM-dd-HH-mm-ss", currentTime));
+            mClockUpdates.set(mClockUpdates.size() - 1, clockUpdate);
+        }
+    }
+
+    /**
+     * Start time according to {@link com.android.internal.os.MonotonicClock}
+     */
+    long getStartTime() {
+        if (mClockUpdates.isEmpty()) {
+            return 0;
+        } else {
+            return mClockUpdates.get(0).monotonicTime;
+        }
+    }
+
+    // TODO - DO NOT SUBMIT public
+    public List<ClockUpdate> getClockUpdates() {
+        return mClockUpdates;
+    }
+
+    void setDuration(long durationMs) {
+        mDurationMs = durationMs;
+    }
+
+    @DurationMillisLong
+    public long getDuration() {
+        return mDurationMs;
+    }
+
+    List<PowerComponentAggregatedPowerStats> getPowerComponentStats() {
+        List<PowerComponentAggregatedPowerStats> list = new ArrayList<>(
+                mPowerComponentStats.size());
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
+            if (stats != mGenericPowerComponent) {
+                list.add(stats);
+            }
+        }
+        return list;
+    }
+
+    PowerComponentAggregatedPowerStats getPowerComponentStats(int powerComponentId) {
+        return mPowerComponentStats.get(powerComponentId);
+    }
+
+    void start(long timestampMs) {
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).start(timestampMs);
+        }
+    }
+
+    void setDeviceState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+            long time) {
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).setState(stateId, state, time);
+        }
+    }
+
+    void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+            long time) {
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).setUidState(uid, stateId, state, time);
+        }
+    }
+
+    boolean isCompatible(PowerStats powerStats) {
+        int powerComponentId = powerStats.descriptor.powerComponentId;
+        PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
+        return stats != null && stats.isCompatible(powerStats);
+    }
+
+    void addPowerStats(PowerStats powerStats, long time) {
+        int powerComponentId = powerStats.descriptor.powerComponentId;
+        PowerComponentAggregatedPowerStats stats = mPowerComponentStats.get(powerComponentId);
+        if (stats == null) {
+            PowerComponent powerComponent = mConfig.createPowerComponent(powerComponentId);
+            if (powerComponent == null) {
+                return;
+            }
+
+            stats = new PowerComponentAggregatedPowerStats(this, powerComponent);
+            stats.setPowerStatsDescriptor(powerStats.descriptor);
+            stats.copyStatesFrom(mGenericPowerComponent);
+            stats.start(time);
+            mPowerComponentStats.put(powerComponentId, stats);
+        }
+
+        stats.addPowerStats(powerStats, time);
+    }
+
+    public void noteStateChange(BatteryStats.HistoryItem item) {
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).noteStateChange(item);
+        }
+    }
+
+    void finish(long timestampMs) {
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            PowerComponentAggregatedPowerStats component = mPowerComponentStats.valueAt(i);
+            component.finish(timestampMs);
+        }
+    }
+
+    void reset() {
+        mClockUpdates.clear();
+        mDurationMs = 0;
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).reset();
+        }
+    }
+
+    public void writeXml(TypedXmlSerializer serializer) throws IOException {
+        serializer.startTag(null, XML_TAG_AGGREGATED_POWER_STATS);
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            PowerComponentAggregatedPowerStats stats = mPowerComponentStats.valueAt(i);
+            if (stats != mGenericPowerComponent) {
+                stats.writeXml(serializer);
+            }
+        }
+        serializer.endTag(null, XML_TAG_AGGREGATED_POWER_STATS);
+        serializer.flush();
+    }
+
+    @NonNull
+    public static AggregatedPowerStats createFromXml(
+            TypedXmlPullParser parser, AggregatedPowerStatsConfig aggregatedPowerStatsConfig)
+            throws XmlPullParserException, IOException {
+        AggregatedPowerStats stats = new AggregatedPowerStats(aggregatedPowerStatsConfig);
+        boolean inElement = false;
+        boolean skipToEnd = false;
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT
+                   && !(eventType == XmlPullParser.END_TAG
+                        && parser.getName().equals(XML_TAG_AGGREGATED_POWER_STATS))) {
+            if (!skipToEnd && eventType == XmlPullParser.START_TAG) {
+                switch (parser.getName()) {
+                    case XML_TAG_AGGREGATED_POWER_STATS:
+                        inElement = true;
+                        break;
+                    case PowerComponentAggregatedPowerStats.XML_TAG_POWER_COMPONENT: {
+                        if (!inElement) {
+                            break;
+                        }
+
+                        int powerComponentId = parser.getAttributeInt(null,
+                                PowerComponentAggregatedPowerStats.XML_ATTR_ID);
+
+                        PowerComponentAggregatedPowerStats
+                                powerComponentStats =
+                                stats.getPowerComponentStats(powerComponentId);
+                        if (powerComponentStats == null) {
+                            PowerComponent powerComponent =
+                                    aggregatedPowerStatsConfig.createPowerComponent(
+                                            powerComponentId);
+                            if (powerComponent != null) {
+                                powerComponentStats = new PowerComponentAggregatedPowerStats(stats,
+                                        powerComponent);
+                                stats.mPowerComponentStats.put(powerComponentId,
+                                        powerComponentStats);
+                            }
+                        }
+                        if (powerComponentStats != null) {
+                            if (!powerComponentStats.readFromXml(parser)) {
+                                skipToEnd = true;
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            eventType = parser.next();
+        }
+        return stats;
+    }
+
+    void dump(IndentingPrintWriter ipw) {
+        StringBuilder sb = new StringBuilder();
+        long baseTime = 0;
+        for (int i = 0; i < mClockUpdates.size(); i++) {
+            ClockUpdate clockUpdate = mClockUpdates.get(i);
+            sb.setLength(0);
+            if (i == 0) {
+                baseTime = clockUpdate.monotonicTime;
+                sb.append("Start time: ")
+                        .append(formatDateTime(clockUpdate.currentTime))
+                        .append(" (")
+                        .append(baseTime)
+                        .append(") duration: ")
+                        .append(mDurationMs);
+                ipw.println(sb);
+            } else {
+                sb.setLength(0);
+                sb.append("Clock update:  ");
+                TimeUtils.formatDuration(
+                        clockUpdate.monotonicTime - baseTime, sb,
+                        TimeUtils.HUNDRED_DAY_FIELD_LEN + 3);
+                sb.append(" ").append(formatDateTime(clockUpdate.currentTime));
+                ipw.increaseIndent();
+                ipw.println(sb);
+                ipw.decreaseIndent();
+            }
+        }
+
+        ipw.println("Device");
+        ipw.increaseIndent();
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).dumpDevice(ipw);
+        }
+        ipw.decreaseIndent();
+
+        Set<Integer> uids = new HashSet<>();
+        for (int i = 0; i < mPowerComponentStats.size(); i++) {
+            mPowerComponentStats.valueAt(i).collectUids(uids);
+        }
+
+        Integer[] allUids = uids.toArray(new Integer[uids.size()]);
+        Arrays.sort(allUids);
+        for (int uid : allUids) {
+            ipw.println(UserHandle.formatUid(uid));
+            ipw.increaseIndent();
+            for (int i = 0; i < mPowerComponentStats.size(); i++) {
+                mPowerComponentStats.valueAt(i).dumpUid(ipw, uid);
+            }
+            ipw.decreaseIndent();
+        }
+    }
+
+    private static String formatDateTime(long timeInMillis) {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+        format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+        return format.format(new Date(timeInMillis));
+    }
+
+    @Override
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        dump(new IndentingPrintWriter(sw));
+        return sw.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java
new file mode 100644
index 0000000..eaeda43
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsConfig.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.processor;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryConsumer;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Configuration that controls how power stats are aggregated.  It determines which state changes
+ * are to be considered as essential dimensions ("tracked states") for each power component (CPU,
+ * WiFi, etc).  Also, it determines which states are tracked globally and which ones on a per-UID
+ * basis.
+ */
+class AggregatedPowerStatsConfig {
+    public static final int STATE_POWER = 0;
+    public static final int STATE_SCREEN = 1;
+    public static final int STATE_PROCESS_STATE = 2;
+
+    @IntDef({
+            STATE_POWER,
+            STATE_SCREEN,
+            STATE_PROCESS_STATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TrackedState {
+    }
+
+    static final String STATE_NAME_POWER = "pwr";
+    static final int POWER_STATE_BATTERY = 0;
+    static final int POWER_STATE_OTHER = 1;   // Plugged in, or on wireless charger, etc.
+    static final String[] STATE_LABELS_POWER = {"pwr-battery", "pwr-other"};
+
+    static final String STATE_NAME_SCREEN = "scr";
+    static final int SCREEN_STATE_ON = 0;
+    static final int SCREEN_STATE_OTHER = 1;  // Off, doze etc
+    static final String[] STATE_LABELS_SCREEN = {"scr-on", "scr-other"};
+
+    static final String STATE_NAME_PROCESS_STATE = "ps";
+    static final String[] STATE_LABELS_PROCESS_STATE;
+
+    static {
+        String[] procStateLabels = new String[BatteryConsumer.PROCESS_STATE_COUNT];
+        for (int i = 0; i < BatteryConsumer.PROCESS_STATE_COUNT; i++) {
+            procStateLabels[i] = BatteryConsumer.processStateToString(i);
+        }
+        STATE_LABELS_PROCESS_STATE = procStateLabels;
+    }
+
+    /**
+     * Configuration for a give power component (CPU, WiFi, etc)
+     */
+    static class PowerComponent {
+        private final int mPowerComponentId;
+        private @TrackedState int[] mTrackedDeviceStates;
+        private @TrackedState int[] mTrackedUidStates;
+        private Supplier<PowerStatsProcessor> mProcessorSupplier;
+
+        PowerComponent(int powerComponentId) {
+            this.mPowerComponentId = powerComponentId;
+        }
+
+        /**
+         * Configures which states should be tracked as separate dimensions for the entire device.
+         */
+        public PowerComponent trackDeviceStates(@TrackedState int... states) {
+            if (mTrackedDeviceStates != null) {
+                throw new IllegalStateException("Component is already configured");
+            }
+            mTrackedDeviceStates = states;
+            return this;
+        }
+
+        /**
+         * Configures which states should be tracked as separate dimensions on a per-UID basis.
+         */
+        public PowerComponent trackUidStates(@TrackedState int... states) {
+            if (mTrackedUidStates != null) {
+                throw new IllegalStateException("Component is already configured");
+            }
+            mTrackedUidStates = states;
+            return this;
+        }
+
+        /**
+         * A PowerStatsProcessor takes an object that should be invoked for every aggregated
+         * stats span before giving the aggregates stats to consumers. The processor can complete
+         * the aggregation process, for example by computing estimated power usage.
+         */
+        public PowerComponent setProcessorSupplier(
+                @NonNull Supplier<PowerStatsProcessor> processorSupplier) {
+            mProcessorSupplier = processorSupplier;
+            return this;
+        }
+
+        public int getPowerComponentId() {
+            return mPowerComponentId;
+        }
+
+        public MultiStateStats.States[] getDeviceStateConfig() {
+            return new MultiStateStats.States[]{
+                    new MultiStateStats.States(STATE_NAME_POWER,
+                            isTracked(mTrackedDeviceStates, STATE_POWER),
+                            STATE_LABELS_POWER),
+                    new MultiStateStats.States(STATE_NAME_SCREEN,
+                            isTracked(mTrackedDeviceStates, STATE_SCREEN),
+                            STATE_LABELS_SCREEN),
+            };
+        }
+
+        public MultiStateStats.States[] getUidStateConfig() {
+            return new MultiStateStats.States[]{
+                    new MultiStateStats.States(STATE_NAME_POWER,
+                            isTracked(mTrackedUidStates, STATE_POWER),
+                            AggregatedPowerStatsConfig.STATE_LABELS_POWER),
+                    new MultiStateStats.States(STATE_NAME_SCREEN,
+                            isTracked(mTrackedUidStates, STATE_SCREEN),
+                            AggregatedPowerStatsConfig.STATE_LABELS_SCREEN),
+                    new MultiStateStats.States(STATE_NAME_PROCESS_STATE,
+                            isTracked(mTrackedUidStates, STATE_PROCESS_STATE),
+                            AggregatedPowerStatsConfig.STATE_LABELS_PROCESS_STATE),
+            };
+        }
+
+        @NonNull
+        PowerStatsProcessor createProcessor() {
+            if (mProcessorSupplier == null) {
+                return NO_OP_PROCESSOR;
+            }
+            return mProcessorSupplier.get();
+        }
+
+        private boolean isTracked(int[] trackedStates, int state) {
+            if (trackedStates == null) {
+                return false;
+            }
+
+            for (int trackedState : trackedStates) {
+                if (trackedState == state) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+    }
+
+    private final List<PowerComponent> mPowerComponents = new ArrayList<>();
+    private PowerComponent mCustomPowerComponent;
+    private Supplier<PowerStatsProcessor> mCustomPowerStatsProcessorFactory;
+
+    /**
+     * Creates a configuration for the specified power component, which may be one of the
+     * standard power component IDs, e.g. {@link BatteryConsumer#POWER_COMPONENT_CPU}, or
+     * a custom power component.
+     */
+    PowerComponent trackPowerComponent(int powerComponentId) {
+        PowerComponent builder = new PowerComponent(powerComponentId);
+        mPowerComponents.add(builder);
+        return builder;
+    }
+
+    /**
+     * Creates a configuration for the specified power component, which is a subcomponent
+     * of a different power component.  The tracked states will be the same as the parent
+     * component's.
+     */
+    PowerComponent trackPowerComponent(int powerComponentId,
+            int parentPowerComponentId) {
+        PowerComponent parent = null;
+        for (int i = 0; i < mPowerComponents.size(); i++) {
+            PowerComponent powerComponent = mPowerComponents.get(i);
+            if (powerComponent.getPowerComponentId() == parentPowerComponentId) {
+                parent = powerComponent;
+                break;
+            }
+        }
+
+        if (parent == null) {
+            throw new IllegalArgumentException(
+                    "Parent component " + parentPowerComponentId + " is not configured");
+        }
+
+        PowerComponent powerComponent = trackPowerComponent(powerComponentId);
+        powerComponent.mTrackedDeviceStates = parent.mTrackedDeviceStates;
+        powerComponent.mTrackedUidStates = parent.mTrackedUidStates;
+        return powerComponent;
+    }
+
+    /**
+     * Creates a configuration for custom power components, which are yet to be discovered
+     * dynamically through the integration with PowerStatsService.
+     */
+    PowerComponent trackCustomPowerComponents(
+            Supplier<PowerStatsProcessor> processorFactory) {
+        mCustomPowerStatsProcessorFactory = processorFactory;
+        mCustomPowerComponent = new PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY);
+        return mCustomPowerComponent;
+    }
+
+    /**
+     * Returns configurations for all registered or dynamically discovered power components.
+     */
+    List<PowerComponent> getPowerComponentsAggregatedStatsConfigs() {
+        return mPowerComponents;
+    }
+
+    /**
+     * Creates a configuration for a custom power component discovered dynamically through the
+     * integration with PowerStatsService.
+     */
+    @Nullable
+    PowerComponent createPowerComponent(int powerComponentId) {
+        if (mCustomPowerComponent == null) {
+            return null;
+        }
+
+        PowerComponent powerComponent = new PowerComponent(powerComponentId);
+        powerComponent.trackDeviceStates(mCustomPowerComponent.mTrackedDeviceStates);
+        powerComponent.trackUidStates(mCustomPowerComponent.mTrackedUidStates);
+
+        if (mCustomPowerStatsProcessorFactory != null) {
+            powerComponent.setProcessorSupplier(mCustomPowerStatsProcessorFactory);
+        }
+
+        return powerComponent;
+    }
+
+    private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
+        @Override
+        void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java
new file mode 100644
index 0000000..4a9730c
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AggregatedPowerStatsSection.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.util.IndentingPrintWriter;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.power.stats.PowerStatsSpan;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AggregatedPowerStatsSection extends PowerStatsSpan.Section {
+    public static final String TYPE = "aggregated-power-stats";
+
+    private final AggregatedPowerStats mAggregatedPowerStats;
+
+    AggregatedPowerStatsSection(AggregatedPowerStats aggregatedPowerStats) {
+        super(TYPE);
+        mAggregatedPowerStats = aggregatedPowerStats;
+    }
+
+    public AggregatedPowerStats getAggregatedPowerStats() {
+        return mAggregatedPowerStats;
+    }
+
+    @Override
+    public void write(TypedXmlSerializer serializer) throws IOException {
+        mAggregatedPowerStats.writeXml(serializer);
+    }
+
+    @Override
+    public void dump(IndentingPrintWriter ipw) {
+        mAggregatedPowerStats.dump(ipw);
+    }
+
+    static class Reader implements PowerStatsSpan.SectionReader {
+        private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+
+        Reader(AggregatedPowerStatsConfig config) {
+            mAggregatedPowerStatsConfig = config;
+        }
+
+        @Override
+        public String getType() {
+            return TYPE;
+        }
+
+        @Override
+        public PowerStatsSpan.Section read(String sectionType, TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return new AggregatedPowerStatsSection(
+                    AggregatedPowerStats.createFromXml(parser, mAggregatedPowerStatsConfig));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
new file mode 100644
index 0000000..32dfdf9
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
@@ -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.server.power.stats.processor;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.AmbientDisplayPowerStatsLayout;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
+
+class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
+    private final AmbientDisplayPowerStatsLayout mStatsLayout;
+    private final PowerStats.Descriptor mDescriptor;
+    private final long[] mTmpDeviceStats;
+    private PowerStats.Descriptor mScreenPowerStatsDescriptor;
+    private ScreenPowerStatsLayout mScreenPowerStatsLayout;
+    private long[] mTmpScreenStats;
+
+    AmbientDisplayPowerStatsProcessor() {
+        mStatsLayout = new AmbientDisplayPowerStatsLayout();
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
+        mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        stats.setPowerStatsDescriptor(mDescriptor);
+
+        PowerComponentAggregatedPowerStats screenStats =
+                stats.getAggregatedPowerStats().getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_SCREEN);
+        if (screenStats == null) {
+            return;
+        }
+
+        if (mScreenPowerStatsDescriptor == null) {
+            mScreenPowerStatsDescriptor = screenStats.getPowerStatsDescriptor();
+            if (mScreenPowerStatsDescriptor == null) {
+                return;
+            }
+
+            mScreenPowerStatsLayout = new ScreenPowerStatsLayout(mScreenPowerStatsDescriptor);
+            mTmpScreenStats = new long[mScreenPowerStatsDescriptor.statsArrayLength];
+        }
+
+        MultiStateStats.States[] deviceStateConfig = screenStats.getConfig().getDeviceStateConfig();
+
+        // Ambient display power estimates have already been calculated by the screen power stats
+        // processor. All that remains to be done is copy the estimates over.
+        MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
+                states -> {
+                    screenStats.getDeviceStats(mTmpScreenStats, states);
+                    double power =
+                            mScreenPowerStatsLayout.getScreenDozePowerEstimate(mTmpScreenStats);
+                    mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
+                    stats.setDeviceStats(states, mTmpDeviceStats);
+                });
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java
new file mode 100644
index 0000000..ad1b4a7
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/AudioPowerStatsProcessor.java
@@ -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.server.power.stats.processor;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+class AudioPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    AudioPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_AUDIO, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_AUDIO));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states & BatteryStats.HistoryItem.STATE_AUDIO_ON_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
new file mode 100644
index 0000000..e45a707
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessor.java
@@ -0,0 +1,296 @@
+/*
+ * 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.processor;
+
+import android.annotation.IntDef;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.Process;
+
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
+    static final int STATE_OFF = 0;
+    static final int STATE_ON = 1;
+
+    @IntDef(flag = true, prefix = {"STATE_"}, value = {
+            STATE_OFF,
+            STATE_ON,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    protected @interface BinaryState {
+    }
+
+    private final int mPowerComponentId;
+    private final PowerStatsUidResolver mUidResolver;
+    private final UsageBasedPowerEstimator mUsageBasedPowerEstimator;
+    private boolean mEnergyConsumerSupported;
+    private int mInitiatingUid = Process.INVALID_UID;
+    private @BinaryState int mLastState = STATE_OFF;
+    private long mLastStateTimestamp;
+    private long mLastUpdateTimestamp;
+
+    private PowerStats.Descriptor mDescriptor;
+    private final BinaryStatePowerStatsLayout mStatsLayout;
+    private PowerStats mPowerStats;
+    private PowerEstimationPlan mPlan;
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    BinaryStatePowerStatsProcessor(int powerComponentId,
+            PowerStatsUidResolver uidResolver, double averagePowerMilliAmp) {
+        this(powerComponentId, uidResolver, averagePowerMilliAmp,
+                new BinaryStatePowerStatsLayout());
+    }
+
+    BinaryStatePowerStatsProcessor(int powerComponentId,
+            PowerStatsUidResolver uidResolver, double averagePowerMilliAmp,
+            BinaryStatePowerStatsLayout statsLayout) {
+        mPowerComponentId = powerComponentId;
+        mUsageBasedPowerEstimator = new UsageBasedPowerEstimator(averagePowerMilliAmp);
+        mUidResolver = uidResolver;
+        mStatsLayout = statsLayout;
+    }
+
+    protected abstract @BinaryState int getBinaryState(BatteryStats.HistoryItem item);
+
+    private void ensureInitialized() {
+        if (mDescriptor != null) {
+            return;
+        }
+
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(mPowerComponentId,
+                mStatsLayout.getDeviceStatsArrayLength(), null, 0,
+                mStatsLayout.getUidStatsArrayLength(), extras);
+        mPowerStats = new PowerStats(mDescriptor);
+        mPowerStats.stats = new long[mDescriptor.statsArrayLength];
+        mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
+    }
+
+    @Override
+    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        ensureInitialized();
+
+        // Establish a baseline at the beginning of an accumulation pass
+        mLastState = STATE_OFF;
+        mLastStateTimestamp = timestampMs;
+        mInitiatingUid = Process.INVALID_UID;
+        flushPowerStats(stats, mLastStateTimestamp);
+    }
+
+    @Override
+    void noteStateChange(PowerComponentAggregatedPowerStats stats,
+            BatteryStats.HistoryItem item) {
+        @BinaryState int state = getBinaryState(item);
+        if (state == mLastState) {
+            return;
+        }
+
+        if (state == STATE_ON) {
+            if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START)) {
+                mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
+            }
+        } else {
+            if (mInitiatingUid == Process.INVALID_UID) {
+                if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                        | BatteryStats.HistoryItem.EVENT_FLAG_FINISH)) {
+                    mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
+                }
+            }
+            recordUsageDuration(mPowerStats, mInitiatingUid, item.time);
+            mInitiatingUid = Process.INVALID_UID;
+            if (!mEnergyConsumerSupported) {
+                flushPowerStats(stats, item.time);
+            }
+        }
+        mLastStateTimestamp = item.time;
+        mLastState = state;
+    }
+
+    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
+        long durationMs = time - mLastStateTimestamp;
+        mStatsLayout.setUsageDuration(mPowerStats.stats,
+                mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
+
+        if (uid != Process.INVALID_UID) {
+            long[] uidStats = mPowerStats.uidStats.get(uid);
+            if (uidStats == null) {
+                uidStats = new long[mDescriptor.uidStatsArrayLength];
+                mPowerStats.uidStats.put(uid, uidStats);
+                mStatsLayout.setUidUsageDuration(uidStats, durationMs);
+            } else {
+                mStatsLayout.setUsageDuration(mPowerStats.stats,
+                        mStatsLayout.getUsageDuration(mPowerStats.stats) + durationMs);
+            }
+        }
+        mLastStateTimestamp = time;
+    }
+
+    void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
+            long timestampMs) {
+        ensureInitialized();
+
+        if (mLastState == STATE_ON) {
+            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+        }
+
+        long consumedEnergy = mStatsLayout.getConsumedEnergy(powerStats.stats, 0);
+        if (consumedEnergy != BatteryStats.POWER_DATA_UNAVAILABLE) {
+            mEnergyConsumerSupported = true;
+            mStatsLayout.setConsumedEnergy(mPowerStats.stats, 0, consumedEnergy);
+        }
+
+        flushPowerStats(stats, timestampMs);
+    }
+
+    private void flushPowerStats(PowerComponentAggregatedPowerStats stats, long timestamp) {
+        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+        stats.addProcessedPowerStats(mPowerStats, timestamp);
+
+        Arrays.fill(mPowerStats.stats, 0);
+        mPowerStats.uidStats.clear();
+        mLastUpdateTimestamp = timestamp;
+    }
+
+    private static class Intermediates {
+        public long duration;
+        public double power;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (mLastState == STATE_ON) {
+            recordUsageDuration(mPowerStats, mInitiatingUid, timestampMs);
+        }
+        flushPowerStats(stats, timestampMs);
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        computeDevicePowerEstimates(stats, mPlan, mEnergyConsumerSupported);
+        combineDevicePowerEstimates(stats);
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        computeUidActivityTotals(stats, uids);
+        computeUidPowerEstimates(stats, uids);
+    }
+
+    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            PowerEstimationPlan plan, boolean energyConsumerSupported) {
+        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            long duration = mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
+            if (duration > 0) {
+                double power;
+                if (energyConsumerSupported) {
+                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+                } else {
+                    power = mUsageBasedPowerEstimator.calculatePower(duration);
+                }
+                mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+                stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+            }
+        }
+    }
+
+    private void combineDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate estimation =
+                    mPlan.combinedDeviceStateEstimations.get(i);
+            Intermediates intermediates = new Intermediates();
+            estimation.intermediates = intermediates;
+            for (int j = estimation.deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation deviceStateEstimation =
+                        estimation.deviceStateEstimations.get(j);
+                if (!stats.getDeviceStats(mTmpDeviceStatsArray,
+                        deviceStateEstimation.stateValues)) {
+                    continue;
+                }
+                intermediates.power += mStatsLayout.getDevicePowerEstimate(mTmpDeviceStatsArray);
+            }
+        }
+    }
+
+    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            Intermediates intermediates =
+                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+            for (int j = uids.size() - 1; j >= 0; j--) {
+                int uid = uids.get(j);
+                for (UidStateProportionalEstimate proportionalEstimate :
+                        uidStateEstimate.proportionalEstimates) {
+                    if (stats.getUidStats(mTmpUidStatsArray, uid,
+                            proportionalEstimate.stateValues)) {
+                        intermediates.duration +=
+                                mStatsLayout.getUidUsageDuration(mTmpUidStatsArray);
+                    }
+                }
+            }
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            Intermediates intermediates =
+                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+            if (intermediates.duration == 0) {
+                continue;
+            }
+            List<UidStateProportionalEstimate> proportionalEstimates =
+                    uidStateEstimate.proportionalEstimates;
+            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+                for (int k = uids.size() - 1; k >= 0; k--) {
+                    int uid = uids.get(k);
+                    if (stats.getUidStats(mTmpUidStatsArray, uid,
+                            proportionalEstimate.stateValues)) {
+                        double power = intermediates.power
+                                * mStatsLayout.getUidUsageDuration(mTmpUidStatsArray)
+                                / intermediates.duration;
+                        mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+                        stats.setUidStats(uid, proportionalEstimate.stateValues,
+                                mTmpUidStatsArray);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
new file mode 100644
index 0000000..4c1a0db
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/BluetoothPowerStatsProcessor.java
@@ -0,0 +1,304 @@
+/*
+ * 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.processor;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class BluetoothPowerStatsProcessor extends PowerStatsProcessor {
+    private final UsageBasedPowerEstimator mRxPowerEstimator;
+    private final UsageBasedPowerEstimator mTxPowerEstimator;
+    private final UsageBasedPowerEstimator mIdlePowerEstimator;
+
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    private BluetoothPowerStatsLayout mStatsLayout;
+    // Sequence of steps for power estimation and intermediate results.
+    private PowerEstimationPlan mPlan;
+
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    BluetoothPowerStatsProcessor(PowerProfile powerProfile) {
+        mRxPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX));
+        mTxPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX));
+        mIdlePowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE));
+    }
+
+    private static class Intermediates {
+        /**
+         * Number of received bytes
+         */
+        public long rxBytes;
+        /**
+         * Duration of receiving
+         */
+        public long rxTime;
+        /**
+         * Estimated power for the RX state.
+         */
+        public double rxPower;
+        /**
+         * Number of transmitted bytes
+         */
+        public long txBytes;
+        /**
+         * Duration of transmitting
+         */
+        public long txTime;
+        /**
+         * Estimated power for the TX state.
+         */
+        public double txPower;
+        /**
+         * Estimated power for IDLE, SCAN states.
+         */
+        public double idlePower;
+        /**
+         * Total scan time.
+         */
+        public long scanTime;
+        /**
+         * Measured consumed energy from power monitoring hardware (micro-coulombs)
+         */
+        public long consumedEnergy;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (stats.getPowerStatsDescriptor() == null) {
+            return;
+        }
+
+        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            Intermediates intermediates = new Intermediates();
+            estimation.intermediates = intermediates;
+            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
+        }
+
+        double ratio = 1.0;
+        if (mStatsLayout.getEnergyConsumerCount() != 0) {
+            ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
+            if (ratio != 1) {
+                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+                    adjustDevicePowerEstimates(stats, estimation.stateValues,
+                            (Intermediates) estimation.intermediates, ratio);
+                }
+            }
+        }
+
+        combineDeviceStateEstimates();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+        if (!uids.isEmpty()) {
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+        }
+        mPlan.resetIntermediates();
+    }
+
+    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new BluetoothPowerStatsLayout(descriptor);
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+    }
+
+    /**
+     * Compute power estimates using the power profile.
+     */
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates) {
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
+            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+        }
+
+        intermediates.rxTime = mStatsLayout.getDeviceRxTime(mTmpDeviceStatsArray);
+        intermediates.txTime = mStatsLayout.getDeviceTxTime(mTmpDeviceStatsArray);
+        intermediates.scanTime = mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray);
+        long idleTime = mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray);
+
+        intermediates.rxPower = mRxPowerEstimator.calculatePower(intermediates.rxTime);
+        intermediates.txPower = mTxPowerEstimator.calculatePower(intermediates.txTime);
+        intermediates.idlePower = mIdlePowerEstimator.calculatePower(idleTime);
+        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                intermediates.rxPower + intermediates.txPower + intermediates.idlePower);
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * Compute an adjustment ratio using the total power estimated using the power profile
+     * and the total power measured by hardware.
+     */
+    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
+        long totalConsumedEnergy = 0;
+        double totalPower = 0;
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            Intermediates intermediates =
+                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
+            totalPower += intermediates.rxPower + intermediates.txPower + intermediates.idlePower;
+            totalConsumedEnergy += intermediates.consumedEnergy;
+        }
+
+        if (totalPower == 0) {
+            return 1;
+        }
+
+        return uCtoMah(totalConsumedEnergy) / totalPower;
+    }
+
+    /**
+     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
+     * estimated power matches the measured consumed power.  We are not claiming that all
+     * averages captured in the power profile have to be off by the same percentage in reality.
+     */
+    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates, double ratio) {
+        double adjutedPower;
+        intermediates.rxPower *= ratio;
+        intermediates.txPower *= ratio;
+        intermediates.idlePower *= ratio;
+        adjutedPower = intermediates.rxPower + intermediates.txPower + intermediates.idlePower;
+
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, adjutedPower);
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * Combine power estimates before distributing them proportionally to UIDs.
+     */
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            Intermediates cdseIntermediates = new Intermediates();
+            cdse.intermediates = cdseIntermediates;
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                Intermediates intermediates = (Intermediates) dse.intermediates;
+                cdseIntermediates.rxTime += intermediates.rxTime;
+                cdseIntermediates.rxBytes += intermediates.rxBytes;
+                cdseIntermediates.rxPower += intermediates.rxPower;
+                cdseIntermediates.txTime += intermediates.txTime;
+                cdseIntermediates.txBytes += intermediates.txBytes;
+                cdseIntermediates.txPower += intermediates.txPower;
+                cdseIntermediates.idlePower += intermediates.idlePower;
+                cdseIntermediates.scanTime += intermediates.scanTime;
+                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
+            }
+        }
+    }
+
+    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            intermediates.rxBytes += mStatsLayout.getUidRxBytes(mTmpUidStatsArray);
+            intermediates.txBytes += mStatsLayout.getUidTxBytes(mTmpUidStatsArray);
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+
+        // Scan is more expensive than data transfer, so in the presence of large
+        // of scanning duration, blame apps according to the time they spent scanning.
+        // This may disproportionately blame apps that do a lot of scanning, which is
+        // the tread-off we are making in the absence of more detailed metrics.
+        boolean normalizeRxByScanTime = intermediates.scanTime > intermediates.rxTime;
+        boolean normalizeTxByScanTime = intermediates.scanTime > intermediates.txTime;
+
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            if (normalizeRxByScanTime) {
+                if (intermediates.scanTime != 0) {
+                    power += intermediates.rxPower * mStatsLayout.getUidScanTime(mTmpUidStatsArray)
+                            / intermediates.scanTime;
+                }
+            } else {
+                if (intermediates.rxBytes != 0) {
+                    power += intermediates.rxPower * mStatsLayout.getUidRxBytes(mTmpUidStatsArray)
+                            / intermediates.rxBytes;
+                }
+            }
+            if (normalizeTxByScanTime) {
+                if (intermediates.scanTime != 0) {
+                    power += intermediates.txPower * mStatsLayout.getUidScanTime(mTmpUidStatsArray)
+                            / intermediates.scanTime;
+                }
+            } else {
+                if (intermediates.txBytes != 0) {
+                    power += intermediates.txPower * mStatsLayout.getUidTxBytes(mTmpUidStatsArray)
+                            / intermediates.txBytes;
+                }
+            }
+            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java
new file mode 100644
index 0000000..8309061
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/CameraPowerStatsProcessor.java
@@ -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.server.power.stats.processor;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+class CameraPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    CameraPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_CAMERA, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_CAMERA));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_CAMERA_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
new file mode 100644
index 0000000..5f7a3da
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+class CpuPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "CpuPowerStatsProcessor";
+
+    private static final double HOUR_IN_MILLIS = TimeUnit.HOURS.toMillis(1);
+    private static final int UNKNOWN = -1;
+
+    private final CpuScalingPolicies mCpuScalingPolicies;
+    // Number of CPU core clusters
+    private final int mCpuClusterCount;
+    // Total number of CPU scaling steps across all clusters
+    private final int mCpuScalingStepCount;
+    // Map of scaling step to the corresponding core cluster mScalingStepToCluster[step]->cluster
+    private final int[] mScalingStepToCluster;
+    // Average power consumed by the CPU when it is powered up (per power_profile.xml)
+    private final double mPowerMultiplierForCpuActive;
+    // Average power consumed by each cluster when it is powered up (per power_profile.xml)
+    private final double[] mPowerMultipliersByCluster;
+    // Average power consumed by each scaling step when running code (per power_profile.xml)
+    private final double[] mPowerMultipliersByScalingStep;
+    // A map used to combine energy consumers into a smaller set, in case power brackets
+    // are defined in a way that does not allow an unambiguous mapping of energy consumers to
+    // brackets
+    private int[] mEnergyConsumerToCombinedEnergyConsumerMap;
+    // A map of combined energy consumers to the corresponding collections of power brackets.
+    // For example, if there are two CPU_CLUSTER rails and each maps to three brackets,
+    // this map will look like this:
+    //     0 : [0, 1, 2]
+    //     1 : [3, 4, 5]
+    private int[][] mCombinedEnergyConsumerToPowerBracketMap;
+
+    // Cached reference to a PowerStats descriptor. Almost never changes in practice,
+    // helping to avoid reparsing the descriptor for every PowerStats span.
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    // Cached results of parsing of current PowerStats.Descriptor. Only refreshed when
+    // mLastUsedDescriptor changes
+    private CpuPowerStatsLayout mStatsLayout;
+    // Sequence of steps for power estimation and intermediate results.
+    private PowerEstimationPlan mPlan;
+
+    // Temp array for retrieval of device power stats, to avoid repeated allocations
+    private long[] mTmpDeviceStatsArray;
+    // Temp array for retrieval of UID power stats, to avoid repeated allocations
+    private long[] mTmpUidStatsArray;
+
+    CpuPowerStatsProcessor(PowerProfile powerProfile, CpuScalingPolicies scalingPolicies) {
+        mCpuScalingPolicies = scalingPolicies;
+        mCpuScalingStepCount = scalingPolicies.getScalingStepCount();
+        mScalingStepToCluster = new int[mCpuScalingStepCount];
+        mPowerMultipliersByScalingStep = new double[mCpuScalingStepCount];
+
+        int step = 0;
+        int[] policies = scalingPolicies.getPolicies();
+        mCpuClusterCount = policies.length;
+        mPowerMultipliersByCluster = new double[mCpuClusterCount];
+        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+            int policy = policies[cluster];
+            mPowerMultipliersByCluster[cluster] =
+                    powerProfile.getAveragePowerForCpuScalingPolicy(policy) / HOUR_IN_MILLIS;
+            int[] frequencies = scalingPolicies.getFrequencies(policy);
+            for (int i = 0; i < frequencies.length; i++) {
+                mScalingStepToCluster[step] = cluster;
+                mPowerMultipliersByScalingStep[step] =
+                        powerProfile.getAveragePowerForCpuScalingStep(policy, i) / HOUR_IN_MILLIS;
+                step++;
+            }
+        }
+        mPowerMultiplierForCpuActive =
+                powerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE) / HOUR_IN_MILLIS;
+    }
+
+    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new CpuPowerStatsLayout(descriptor);
+
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+    }
+
+    /**
+     * Temporary struct to capture intermediate results of power estimation.
+     */
+    private static final class Intermediates {
+        public long uptime;
+        // Sum of all time-in-step values, combining all time-in-step durations across all cores.
+        public long cumulativeTime;
+        // CPU activity durations per cluster
+        public long[] timeByCluster;
+        // Sums of time-in-step values, aggregated by cluster, combining all cores in the cluster.
+        public long[] cumulativeTimeByCluster;
+        public long[] timeByScalingStep;
+        public double[] powerByCluster;
+        public double[] powerByScalingStep;
+        public long[] powerByEnergyConsumer;
+    }
+
+    /**
+     * Temporary struct to capture intermediate results of power estimation.
+     */
+    private static class DeviceStatsIntermediates {
+        public double power;
+        public long[] timeByBracket;
+        public double[] powerByBracket;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (stats.getPowerStatsDescriptor() == null) {
+            return;
+        }
+
+        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+            if (mStatsLayout.getEnergyConsumerCount() != 0) {
+                initEnergyConsumerToPowerBracketMaps();
+            }
+        }
+
+        Intermediates intermediates = new Intermediates();
+
+        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+        if (cpuScalingStepCount != mCpuScalingStepCount) {
+            Log.e(TAG, "Mismatched CPU scaling step count in PowerStats: " + cpuScalingStepCount
+                       + ", expected: " + mCpuScalingStepCount);
+            return;
+        }
+
+        int clusterCount = mStatsLayout.getCpuClusterCount();
+        if (clusterCount != mCpuClusterCount) {
+            Log.e(TAG, "Mismatched CPU cluster count in PowerStats: " + clusterCount
+                       + ", expected: " + mCpuClusterCount);
+            return;
+        }
+
+        computeTotals(stats, intermediates);
+        if (intermediates.cumulativeTime == 0) {
+            return;
+        }
+
+        estimatePowerByScalingStep(intermediates);
+        estimatePowerByDeviceState(stats, intermediates);
+        combineDeviceStateEstimates();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+        if (!uids.isEmpty()) {
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    estimateUidPowerConsumption(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+        }
+        mPlan.resetIntermediates();
+    }
+
+    /*
+     * Populate data structures (two maps) needed to use power rail data, aka energy consumers,
+     * to attribute power usage to apps.
+     *
+     * At this point, the algorithm covers only the most basic cases:
+     * - Each cluster is mapped to unique power brackets (possibly multiple for each cluster):
+     *          CL_0: [bracket0, bracket1]
+     *          CL_1: [bracket3]
+     *      In this case, the consumed energy is distributed  to the corresponding brackets
+     *      proportionally.
+     * - Brackets span multiple clusters:
+     *          CL_0: [bracket0, bracket1]
+     *          CL_1: [bracket1, bracket2]
+     *          CL_2: [bracket3, bracket4]
+     *      In this case, we combine energy consumers into groups unambiguously mapped to
+     *      brackets. In the above example, consumed energy for CL_0 and CL_1 will be combined
+     *      because they both map to the same power bracket (bracket1):
+     *          (CL_0+CL_1): [bracket0, bracket1, bracket2]
+     *          CL_2: [bracket3, bracket4]
+     */
+    private void initEnergyConsumerToPowerBracketMaps() {
+        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+
+        mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
+        mCombinedEnergyConsumerToPowerBracketMap = new int[energyConsumerCount][];
+
+        int[] policies = mCpuScalingPolicies.getPolicies();
+        if (energyConsumerCount == policies.length) {
+            int[] scalingStepToPowerBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+            ArraySet<Integer>[] clusterToBrackets = new ArraySet[policies.length];
+            int step = 0;
+            for (int cluster = 0; cluster < policies.length; cluster++) {
+                int[] freqs = mCpuScalingPolicies.getFrequencies(policies[cluster]);
+                clusterToBrackets[cluster] = new ArraySet<>(freqs.length);
+                for (int j = 0; j < freqs.length; j++) {
+                    clusterToBrackets[cluster].add(scalingStepToPowerBracketMap[step++]);
+                }
+            }
+
+            ArraySet<Integer>[] combinedEnergyConsumers = new ArraySet[policies.length];
+            int combinedEnergyConsumersCount = 0;
+
+            for (int cluster = 0; cluster < clusterToBrackets.length; cluster++) {
+                int combineWith = UNKNOWN;
+                for (int i = 0; i < combinedEnergyConsumersCount; i++) {
+                    if (containsAny(combinedEnergyConsumers[i], clusterToBrackets[cluster])) {
+                        combineWith = i;
+                        break;
+                    }
+                }
+                if (combineWith != UNKNOWN) {
+                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] = combineWith;
+                    combinedEnergyConsumers[combineWith].addAll(clusterToBrackets[cluster]);
+                } else {
+                    mEnergyConsumerToCombinedEnergyConsumerMap[cluster] =
+                            combinedEnergyConsumersCount;
+                    combinedEnergyConsumers[combinedEnergyConsumersCount++] =
+                            clusterToBrackets[cluster];
+                }
+            }
+
+            for (int i = combinedEnergyConsumers.length - 1; i >= 0; i--) {
+                mCombinedEnergyConsumerToPowerBracketMap[i] =
+                        new int[combinedEnergyConsumers[i].size()];
+                for (int j = combinedEnergyConsumers[i].size() - 1; j >= 0; j--) {
+                    mCombinedEnergyConsumerToPowerBracketMap[i][j] =
+                            combinedEnergyConsumers[i].valueAt(j);
+                }
+            }
+        } else {
+            // All CPU cluster energy consumers are combined into one, which is
+            // distributed proportionally to all power brackets.
+            int[] map = new int[powerBracketCount];
+            for (int i = 0; i < map.length; i++) {
+                map[i] = i;
+            }
+            mCombinedEnergyConsumerToPowerBracketMap[0] = map;
+        }
+    }
+
+    private static boolean containsAny(ArraySet<Integer> set1, ArraySet<Integer> set2) {
+        for (int i = 0; i < set2.size(); i++) {
+            if (set1.contains(set2.valueAt(i))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void computeTotals(PowerComponentAggregatedPowerStats stats,
+            Intermediates intermediates) {
+        intermediates.timeByScalingStep = new long[mCpuScalingStepCount];
+        intermediates.timeByCluster = new long[mCpuClusterCount];
+        intermediates.cumulativeTimeByCluster = new long[mCpuClusterCount];
+
+        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+        for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
+                continue;
+            }
+
+            intermediates.uptime += mStatsLayout.getUsageDuration(mTmpDeviceStatsArray);
+
+            for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+                intermediates.timeByCluster[cluster] +=
+                        mStatsLayout.getTimeByCluster(mTmpDeviceStatsArray, cluster);
+            }
+
+            for (int step = 0; step < mCpuScalingStepCount; step++) {
+                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+                intermediates.cumulativeTime += timeInStep;
+                intermediates.timeByScalingStep[step] += timeInStep;
+                intermediates.cumulativeTimeByCluster[mScalingStepToCluster[step]] += timeInStep;
+            }
+        }
+    }
+
+    private void estimatePowerByScalingStep(Intermediates intermediates) {
+        // CPU consumes some power when it's on - no matter which cores are running.
+        double cpuActivePower = mPowerMultiplierForCpuActive * intermediates.uptime;
+
+        // Additionally, every cluster consumes some power when any of its cores are running
+        intermediates.powerByCluster = new double[mCpuClusterCount];
+        for (int cluster = 0; cluster < mCpuClusterCount; cluster++) {
+            intermediates.powerByCluster[cluster] =
+                    mPowerMultipliersByCluster[cluster] * intermediates.timeByCluster[cluster];
+        }
+
+        // Finally, additional power is consumed depending on the frequency scaling
+        intermediates.powerByScalingStep = new double[mCpuScalingStepCount];
+        for (int step = 0; step < mCpuScalingStepCount; step++) {
+            int cluster = mScalingStepToCluster[step];
+
+            double power;
+
+            // Distribute base power proportionally
+            power = cpuActivePower * intermediates.timeByScalingStep[step]
+                    / intermediates.cumulativeTime;
+
+            // Distribute per-cluster power proportionally
+            long cumulativeTimeInCluster = intermediates.cumulativeTimeByCluster[cluster];
+            if (cumulativeTimeInCluster != 0) {
+                power += intermediates.powerByCluster[cluster]
+                         * intermediates.timeByScalingStep[step]
+                         / cumulativeTimeInCluster;
+            }
+
+            power += mPowerMultipliersByScalingStep[step] * intermediates.timeByScalingStep[step];
+
+            intermediates.powerByScalingStep[step] = power;
+        }
+    }
+
+    private void estimatePowerByDeviceState(PowerComponentAggregatedPowerStats stats,
+            Intermediates intermediates) {
+        int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
+        int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
+        int[] scalingStepToBracketMap = mStatsLayout.getScalingStepToPowerBracketMap();
+        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+        List<DeviceStateEstimation> deviceStateEstimations = mPlan.deviceStateEstimations;
+        for (int dse = deviceStateEstimations.size() - 1; dse >= 0; dse--) {
+            DeviceStateEstimation deviceStateEstimation = deviceStateEstimations.get(dse);
+            deviceStateEstimation.intermediates = new DeviceStatsIntermediates();
+            DeviceStatsIntermediates deviceStatsIntermediates =
+                    (DeviceStatsIntermediates) deviceStateEstimation.intermediates;
+            deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
+            deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
+
+            stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
+            for (int step = 0; step < cpuScalingStepCount; step++) {
+                if (intermediates.timeByScalingStep[step] == 0) {
+                    continue;
+                }
+
+                long timeInStep = mStatsLayout.getTimeByScalingStep(mTmpDeviceStatsArray, step);
+                double stepPower = intermediates.powerByScalingStep[step] * timeInStep
+                                   / intermediates.timeByScalingStep[step];
+
+                int bracket = scalingStepToBracketMap[step];
+                deviceStatsIntermediates.timeByBracket[bracket] += timeInStep;
+                deviceStatsIntermediates.powerByBracket[bracket] += stepPower;
+            }
+
+            if (energyConsumerCount != 0) {
+                adjustEstimatesUsingEnergyConsumers(intermediates, deviceStatsIntermediates);
+            }
+
+            double power = 0;
+            for (int i = deviceStatsIntermediates.powerByBracket.length - 1; i >= 0; i--) {
+                power += deviceStatsIntermediates.powerByBracket[i];
+            }
+            deviceStatsIntermediates.power = power;
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+            stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+
+    private void adjustEstimatesUsingEnergyConsumers(
+            Intermediates intermediates, DeviceStatsIntermediates deviceStatsIntermediates) {
+        int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+        if (energyConsumerCount == 0) {
+            return;
+        }
+
+        if (intermediates.powerByEnergyConsumer == null) {
+            intermediates.powerByEnergyConsumer = new long[energyConsumerCount];
+        } else {
+            Arrays.fill(intermediates.powerByEnergyConsumer, 0);
+        }
+        for (int i = 0; i < energyConsumerCount; i++) {
+            intermediates.powerByEnergyConsumer[mEnergyConsumerToCombinedEnergyConsumerMap[i]] +=
+                    mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+        }
+
+        for (int combinedConsumer = mCombinedEnergyConsumerToPowerBracketMap.length - 1;
+                combinedConsumer >= 0; combinedConsumer--) {
+            int[] combinedEnergyConsumerToPowerBracketMap =
+                    mCombinedEnergyConsumerToPowerBracketMap[combinedConsumer];
+            if (combinedEnergyConsumerToPowerBracketMap == null) {
+                continue;
+            }
+
+            double consumedEnergy = uCtoMah(intermediates.powerByEnergyConsumer[combinedConsumer]);
+
+            double totalModeledPower = 0;
+            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+                totalModeledPower += deviceStatsIntermediates.powerByBracket[bracket];
+            }
+            if (totalModeledPower == 0) {
+                continue;
+            }
+
+            for (int bracket : combinedEnergyConsumerToPowerBracketMap) {
+                deviceStatsIntermediates.powerByBracket[bracket] =
+                        consumedEnergy * deviceStatsIntermediates.powerByBracket[bracket]
+                        / totalModeledPower;
+            }
+        }
+    }
+
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            DeviceStatsIntermediates cdseIntermediates = new DeviceStatsIntermediates();
+            cdse.intermediates = cdseIntermediates;
+            int bracketCount = mStatsLayout.getCpuPowerBracketCount();
+            cdseIntermediates.timeByBracket = new long[bracketCount];
+            cdseIntermediates.powerByBracket = new double[bracketCount];
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                DeviceStatsIntermediates intermediates =
+                        (DeviceStatsIntermediates) dse.intermediates;
+                cdseIntermediates.power += intermediates.power;
+                for (int k = 0; k < bracketCount; k++) {
+                    cdseIntermediates.timeByBracket[k] += intermediates.timeByBracket[k];
+                    cdseIntermediates.powerByBracket[k] += intermediates.powerByBracket[k];
+                }
+            }
+        }
+    }
+
+    private void estimateUidPowerConsumption(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        CombinedDeviceStateEstimate combinedDeviceStateEstimate =
+                uidStateEstimate.combinedDeviceStateEstimate;
+        DeviceStatsIntermediates cdsIntermediates =
+                (DeviceStatsIntermediates) combinedDeviceStateEstimate.intermediates;
+        for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+            UidStateProportionalEstimate proportionalEstimate =
+                    uidStateEstimate.proportionalEstimates.get(i);
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            for (int bracket = 0; bracket < mStatsLayout.getCpuPowerBracketCount(); bracket++) {
+                if (cdsIntermediates.timeByBracket[bracket] == 0) {
+                    continue;
+                }
+
+                long timeInBracket = mStatsLayout.getUidTimeByPowerBracket(mTmpUidStatsArray,
+                        bracket);
+                if (timeInBracket == 0) {
+                    continue;
+                }
+
+                power += cdsIntermediates.powerByBracket[bracket] * timeInBracket
+                            / cdsIntermediates.timeByBracket[bracket];
+            }
+
+            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
new file mode 100644
index 0000000..76adc47
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsProcessor.java
@@ -0,0 +1,84 @@
+/*
+ * 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.server.power.stats.processor;
+
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class CustomEnergyConsumerPowerStatsProcessor extends PowerStatsProcessor {
+    private static final EnergyConsumerPowerStatsLayout sLayout =
+            new EnergyConsumerPowerStatsLayout();
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+    private PowerEstimationPlan mPlan;
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        computeDevicePowerEstimates(stats);
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        if (!uids.isEmpty()) {
+            computeUidPowerEstimates(stats, uids);
+        }
+    }
+
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            sLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                    uCtoMah(sLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0)));
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            List<UidStateProportionalEstimate> proportionalEstimates =
+                    uidStateEstimate.proportionalEstimates;
+            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+                for (int k = uids.size() - 1; k >= 0; k--) {
+                    int uid = uids.get(k);
+                    if (stats.getUidStats(mTmpUidStatsArray, uid,
+                            proportionalEstimate.stateValues)) {
+                        sLayout.setUidPowerEstimate(mTmpUidStatsArray,
+                                uCtoMah(sLayout.getUidConsumedEnergy(mTmpUidStatsArray, 0)));
+                        stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java
new file mode 100644
index 0000000..b0bef69
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/FlashlightPowerStatsProcessor.java
@@ -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.server.power.stats.processor;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+class FlashlightPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    FlashlightPowerStatsProcessor(PowerProfile powerProfile,
+            PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_FLASHLIGHT));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_FLASHLIGHT_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java
new file mode 100644
index 0000000..f1e3e90
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/GnssPowerStatsProcessor.java
@@ -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 com.android.server.power.stats.processor;
+
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.GnssPowerStatsLayout;
+
+import java.util.Arrays;
+
+class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
+    private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
+            new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+    private final boolean mUseSignalLevelEstimators;
+    private long[] mTmpDeviceStatsArray;
+    private int mGnssSignalLevel;
+    private long mGnssSignalLevelTimestamp;
+    private final long[] mGnssSignalDurations =
+            new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
+
+    GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_GPS_ON),
+                sStatsLayout);
+
+        boolean useSignalLevelEstimators = false;
+        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+            double power = powerProfile.getAveragePower(
+                    PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, level);
+            if (power != 0) {
+                useSignalLevelEstimators = true;
+            }
+            mSignalLevelEstimators[level] = new UsageBasedPowerEstimator(power);
+        }
+        mUseSignalLevelEstimators = useSignalLevelEstimators;
+    }
+
+    @Override
+    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        super.start(stats, timestampMs);
+
+        mGnssSignalLevelTimestamp = timestampMs;
+        mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+        Arrays.fill(mGnssSignalDurations, 0);
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) != 0
+                ? STATE_ON : STATE_OFF;
+    }
+
+    @Override
+    void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
+        super.noteStateChange(stats, item);
+
+        int signalLevel;
+        if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) != 0) {
+            signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+                    >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+            if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
+                // Default GNSS signal quality to GOOD for the purposes of power attribution
+                signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD;
+            }
+        } else {
+            signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
+        }
+        if (signalLevel == mGnssSignalLevel) {
+            return;
+        }
+
+        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+            mGnssSignalDurations[mGnssSignalLevel] += item.time - mGnssSignalLevelTimestamp;
+        }
+        mGnssSignalLevel = signalLevel;
+        mGnssSignalLevelTimestamp = item.time;
+    }
+
+    @Override
+    protected void recordUsageDuration(PowerStats powerStats, int uid, long time) {
+        super.recordUsageDuration(powerStats, uid, time);
+
+        if (mGnssSignalLevel != GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN) {
+            mGnssSignalDurations[mGnssSignalLevel] += time - mGnssSignalLevelTimestamp;
+        } else if (mUseSignalLevelEstimators) {
+            // Default GNSS signal quality to GOOD for the purposes of power attribution
+            mGnssSignalDurations[GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD] +=
+                    time - mGnssSignalLevelTimestamp;
+        }
+
+        for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+            long duration = mGnssSignalDurations[level];
+            sStatsLayout.setDeviceSignalLevelTime(powerStats.stats, level, duration);
+            if (uid != Process.INVALID_UID) {
+                long[] uidStats = powerStats.uidStats.get(uid);
+                if (uidStats == null) {
+                    uidStats = new long[powerStats.descriptor.uidStatsArrayLength];
+                    powerStats.uidStats.put(uid, uidStats);
+                    sStatsLayout.setUidSignalLevelTime(uidStats, level, duration);
+                } else {
+                    sStatsLayout.setUidSignalLevelTime(uidStats, level,
+                            sStatsLayout.getUidSignalLevelTime(uidStats, level) + duration);
+                }
+            }
+        }
+
+        mGnssSignalLevelTimestamp = time;
+        Arrays.fill(mGnssSignalDurations, 0);
+    }
+
+    protected void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            PowerEstimationPlan plan, boolean energyConsumerSupported) {
+        if (!mUseSignalLevelEstimators || energyConsumerSupported) {
+            super.computeDevicePowerEstimates(stats, plan, energyConsumerSupported);
+            return;
+        }
+
+        if (mTmpDeviceStatsArray == null) {
+            mTmpDeviceStatsArray = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+        }
+
+        for (int i = plan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = plan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            for (int level = 0; level < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; level++) {
+                long duration = sStatsLayout.getDeviceSignalLevelTime(mTmpDeviceStatsArray, level);
+                power += mSignalLevelEstimators[level].calculatePower(duration);
+            }
+            sStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
new file mode 100644
index 0000000..b4c40de8
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
@@ -0,0 +1,402 @@
+/*
+ * 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.processor;
+
+import android.os.BatteryStats;
+import android.telephony.CellSignalStrength;
+import android.telephony.ModemActivityInfo;
+import android.telephony.ServiceState;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.internal.power.ModemPowerProfile;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "MobileRadioPowerStats";
+    private static final boolean DEBUG = false;
+
+    private static final int NUM_SIGNAL_STRENGTH_LEVELS =
+            CellSignalStrength.getNumSignalStrengthLevels();
+    private static final int IGNORE = -1;
+
+    private final UsageBasedPowerEstimator mSleepPowerEstimator;
+    private final UsageBasedPowerEstimator mIdlePowerEstimator;
+    private final UsageBasedPowerEstimator mCallPowerEstimator;
+    private final UsageBasedPowerEstimator mScanPowerEstimator;
+
+    private static class RxTxPowerEstimators {
+        UsageBasedPowerEstimator mRxPowerEstimator;
+        UsageBasedPowerEstimator[] mTxPowerEstimators =
+                new UsageBasedPowerEstimator[ModemActivityInfo.getNumTxPowerLevels()];
+    }
+
+    private final SparseArray<RxTxPowerEstimators> mRxTxPowerEstimators = new SparseArray<>();
+
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    private MobileRadioPowerStatsLayout mStatsLayout;
+    // Sequence of steps for power estimation and intermediate results.
+    private PowerEstimationPlan mPlan;
+
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpStateStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    MobileRadioPowerStatsProcessor(PowerProfile powerProfile) {
+        final double sleepDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_SLEEP,
+                Double.NaN);
+        if (Double.isNaN(sleepDrainRateMa)) {
+            mSleepPowerEstimator = null;
+        } else {
+            mSleepPowerEstimator = new UsageBasedPowerEstimator(sleepDrainRateMa);
+        }
+
+        final double idleDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(
+                PowerProfile.SUBSYSTEM_MODEM | ModemPowerProfile.MODEM_DRAIN_TYPE_IDLE,
+                Double.NaN);
+        if (Double.isNaN(idleDrainRateMa)) {
+            mIdlePowerEstimator = null;
+        } else {
+            mIdlePowerEstimator = new UsageBasedPowerEstimator(idleDrainRateMa);
+        }
+
+        // Instantiate legacy power estimators
+        double powerRadioActiveMa =
+                powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_ACTIVE, Double.NaN);
+        if (Double.isNaN(powerRadioActiveMa)) {
+            double sum = 0;
+            sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+            for (int i = 0; i < NUM_SIGNAL_STRENGTH_LEVELS; i++) {
+                sum += powerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+            }
+            powerRadioActiveMa = sum / (NUM_SIGNAL_STRENGTH_LEVELS + 1);
+        }
+        mCallPowerEstimator = new UsageBasedPowerEstimator(powerRadioActiveMa);
+
+        mScanPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePowerOrDefault(PowerProfile.POWER_RADIO_SCANNING, 0));
+
+        for (int rat = 0; rat < BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+            final int freqCount = rat == BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR
+                    ? ServiceState.FREQUENCY_RANGE_COUNT : 1;
+            for (int freqRange = 0; freqRange < freqCount; freqRange++) {
+                mRxTxPowerEstimators.put(
+                        MobileRadioPowerStatsLayout.makeStateKey(rat, freqRange),
+                        buildRxTxPowerEstimators(powerProfile, rat, freqRange));
+            }
+        }
+    }
+
+    private static RxTxPowerEstimators buildRxTxPowerEstimators(PowerProfile powerProfile, int rat,
+            int freqRange) {
+        RxTxPowerEstimators estimators = new RxTxPowerEstimators();
+        long rxKey = ModemPowerProfile.getAverageBatteryDrainKey(
+                ModemPowerProfile.MODEM_DRAIN_TYPE_RX, rat, freqRange, IGNORE);
+        double rxDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(rxKey, Double.NaN);
+        if (Double.isNaN(rxDrainRateMa)) {
+            Log.w(TAG, "Unavailable Power Profile constant for key 0x" + Long.toHexString(rxKey));
+            rxDrainRateMa = 0;
+        }
+        estimators.mRxPowerEstimator = new UsageBasedPowerEstimator(rxDrainRateMa);
+        for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+            long txKey = ModemPowerProfile.getAverageBatteryDrainKey(
+                    ModemPowerProfile.MODEM_DRAIN_TYPE_TX, rat, freqRange, txLevel);
+            double txDrainRateMa = powerProfile.getAverageBatteryDrainOrDefaultMa(txKey,
+                    Double.NaN);
+            if (Double.isNaN(txDrainRateMa)) {
+                Log.w(TAG, "Unavailable Power Profile constant for key 0x"
+                        + Long.toHexString(txKey));
+                txDrainRateMa = 0;
+            }
+            estimators.mTxPowerEstimators[txLevel] = new UsageBasedPowerEstimator(txDrainRateMa);
+        }
+        return estimators;
+    }
+
+    private static class Intermediates {
+        /**
+         * Number of received packets
+         */
+        public long rxPackets;
+        /**
+         * Number of transmitted packets
+         */
+        public long txPackets;
+        /**
+         * Estimated power for the RX state of the modem.
+         */
+        public double rxPower;
+        /**
+         * Estimated power for the TX state of the modem.
+         */
+        public double txPower;
+        /**
+         * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+         */
+        public double inactivePower;
+        /**
+         * Estimated power for IDLE, SLEEP and CELL-SCAN states of the modem.
+         */
+        public double callPower;
+        /**
+         * Measured consumed energy from power monitoring hardware (micro-coulombs)
+         */
+        public long consumedEnergy;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (stats.getPowerStatsDescriptor() == null) {
+            return;
+        }
+
+        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            Intermediates intermediates = new Intermediates();
+            estimation.intermediates = intermediates;
+            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
+        }
+
+        if (mStatsLayout.getEnergyConsumerCount() != 0) {
+            double ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
+            if (ratio != 1) {
+                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+                    adjustDevicePowerEstimates(stats, estimation.stateValues,
+                            (Intermediates) estimation.intermediates, ratio);
+                }
+            }
+        }
+
+        combineDeviceStateEstimates();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+        if (!uids.isEmpty()) {
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidRxTxTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+        }
+        mPlan.resetIntermediates();
+    }
+
+    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new MobileRadioPowerStatsLayout(descriptor);
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpStateStatsArray = new long[descriptor.stateStatsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+    }
+
+    /**
+     * Compute power estimates using the power profile.
+     */
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates) {
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
+            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+        }
+
+        if (mSleepPowerEstimator != null) {
+            intermediates.inactivePower += mSleepPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceSleepTime(mTmpDeviceStatsArray));
+        }
+
+        if (mIdlePowerEstimator != null) {
+            intermediates.inactivePower += mIdlePowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
+        }
+
+        if (mScanPowerEstimator != null) {
+            intermediates.inactivePower += mScanPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
+        }
+
+        stats.forEachStateStatsKey(key -> {
+            RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
+            stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
+            long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
+            intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
+            for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
+                long txTime = mStatsLayout.getStateTxTime(mTmpStateStatsArray, txLevel);
+                intermediates.txPower +=
+                        estimators.mTxPowerEstimators[txLevel].calculatePower(txTime);
+            }
+        });
+
+        if (mCallPowerEstimator != null) {
+            intermediates.callPower = mCallPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceCallTime(mTmpDeviceStatsArray));
+        }
+
+        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+        mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * Compute an adjustment ratio using the total power estimated using the power profile
+     * and the total power measured by hardware.
+     */
+    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
+        long totalConsumedEnergy = 0;
+        double totalPower = 0;
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            Intermediates intermediates =
+                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
+            totalPower += intermediates.rxPower + intermediates.txPower
+                    + intermediates.inactivePower + intermediates.callPower;
+            totalConsumedEnergy += intermediates.consumedEnergy;
+        }
+
+        if (totalPower == 0) {
+            return 1;
+        }
+
+        return uCtoMah(totalConsumedEnergy) / totalPower;
+    }
+
+    /**
+     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
+     * estimated power matches the measured consumed power.  We are not claiming that all
+     * averages captured in the power profile have to be off by the same percentage in reality.
+     */
+    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates, double ratio) {
+        intermediates.rxPower *= ratio;
+        intermediates.txPower *= ratio;
+        intermediates.inactivePower *= ratio;
+        intermediates.callPower *= ratio;
+
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                intermediates.rxPower + intermediates.txPower + intermediates.inactivePower);
+        mStatsLayout.setDeviceCallPowerEstimate(mTmpDeviceStatsArray, intermediates.callPower);
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * This step is effectively a no-op in the cases where we track the same states for
+     * the entire device and all UIDs (e.g. screen on/off, on-battery/on-charger etc). However,
+     * if the lists of tracked states are not the same, we need to combine some estimates
+     * before distributing them proportionally to UIDs.
+     */
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            Intermediates cdseIntermediates = new Intermediates();
+            cdse.intermediates = cdseIntermediates;
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                Intermediates intermediates = (Intermediates) dse.intermediates;
+                cdseIntermediates.rxPower += intermediates.rxPower;
+                cdseIntermediates.txPower += intermediates.txPower;
+                cdseIntermediates.inactivePower += intermediates.inactivePower;
+                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
+            }
+        }
+    }
+
+    private void computeUidRxTxTotals(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
+            intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            if (intermediates.rxPackets != 0) {
+                power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+                        / intermediates.rxPackets;
+            }
+            if (intermediates.txPackets != 0) {
+                power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+                        / intermediates.txPackets;
+            }
+
+            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+            if (DEBUG) {
+                Slog.d(TAG, "UID: " + uid
+                        + " states: " + Arrays.toString(proportionalEstimate.stateValues)
+                        + " stats: " + Arrays.toString(mTmpUidStatsArray)
+                        + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+                        + " rx-power: " + intermediates.rxPower
+                        + " rx-packets: " + intermediates.rxPackets
+                        + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+                        + " tx-power: " + intermediates.txPower
+                        + " tx-packets: " + intermediates.txPackets
+                        + " power: " + power);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java
new file mode 100644
index 0000000..2ba4a52
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStatePowerAttributor.java
@@ -0,0 +1,310 @@
+/*
+ * 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.processor;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerAttributor;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+import java.util.List;
+
+public class MultiStatePowerAttributor implements PowerAttributor {
+    private static final String TAG = "MultiStatePowerAttributor";
+
+    private final PowerStatsStore mPowerStatsStore;
+    private final PowerStatsExporter mPowerStatsExporter;
+    private final PowerStatsAggregator mPowerStatsAggregator;
+    private final SparseBooleanArray mPowerStatsExporterEnabled = new SparseBooleanArray();
+
+    // TODO(b/346371828): remove dependency on PowerStatsUidResolver. At the time of power
+    // attribution isolates UIDs are supposed to be long forgotten.
+    public MultiStatePowerAttributor(Context context, PowerStatsStore powerStatsStore,
+            @NonNull PowerProfile powerProfile, @NonNull CpuScalingPolicies cpuScalingPolicies,
+            @NonNull PowerStatsUidResolver powerStatsUidResolver) {
+        this(powerStatsStore, new PowerStatsAggregator(
+                createAggregatedPowerStatsConfig(context, powerProfile, cpuScalingPolicies,
+                        powerStatsUidResolver)));
+    }
+
+    @VisibleForTesting
+    MultiStatePowerAttributor(PowerStatsStore powerStatsStore,
+            PowerStatsAggregator powerStatsAggregator) {
+        mPowerStatsStore = powerStatsStore;
+        mPowerStatsAggregator = powerStatsAggregator;
+        mPowerStatsStore.addSectionReader(
+                new AggregatedPowerStatsSection.Reader(mPowerStatsAggregator.getConfig()));
+        mPowerStatsExporter = new PowerStatsExporter(mPowerStatsStore, mPowerStatsAggregator);
+    }
+
+    private static AggregatedPowerStatsConfig createAggregatedPowerStatsConfig(Context context,
+            PowerProfile powerProfile, CpuScalingPolicies cpuScalingPolicies,
+            PowerStatsUidResolver powerStatsUidResolver) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new CpuPowerStatsProcessor(powerProfile, cpuScalingPolicies));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .setProcessorSupplier(
+                        () -> new ScreenPowerStatsProcessor(powerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .setProcessorSupplier(AmbientDisplayPowerStatsProcessor::new);
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new MobileRadioPowerStatsProcessor(powerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .setProcessorSupplier(PhoneCallPowerStatsProcessor::new);
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new WifiPowerStatsProcessor(powerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new BluetoothPowerStatsProcessor(powerProfile));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AUDIO)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new AudioPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_VIDEO)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new VideoPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new FlashlightPowerStatsProcessor(powerProfile,
+                                powerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new CameraPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new GnssPowerStatsProcessor(powerProfile, powerStatsUidResolver));
+
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(() -> new SensorPowerStatsProcessor(
+                        () -> context.getSystemService(SensorManager.class)));
+
+        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+        return config;
+    }
+
+    /**
+     * Marks the specified power component as supported by this PowerAttributor
+     */
+    public void setPowerComponentSupported(@BatteryConsumer.PowerComponentId int powerComponentId,
+            boolean enabled) {
+        mPowerStatsExporterEnabled.put(powerComponentId, enabled);
+        mPowerStatsExporter.setPowerComponentEnabled(powerComponentId, enabled);
+    }
+
+    @Override
+    public boolean isPowerComponentSupported(
+            @BatteryConsumer.PowerComponentId int powerComponentId) {
+        return mPowerStatsExporterEnabled.get(powerComponentId);
+    }
+
+    @Override
+    public void estimatePowerConsumption(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            BatteryStatsHistory batteryHistory, long monotonicStartTime, long monotonicEndTime) {
+        mPowerStatsExporter.exportAggregatedPowerStats(batteryUsageStatsBuilder, batteryHistory,
+                monotonicStartTime, monotonicEndTime);
+    }
+
+    @Override
+    public void dumpEstimatedPowerConsumption(IndentingPrintWriter ipw,
+            BatteryStatsHistory batteryStatsHistory,
+            long startTime, long endTime) {
+        mPowerStatsAggregator.aggregatePowerStats(batteryStatsHistory, startTime, endTime,
+                stats -> {
+                    // Create a PowerStatsSpan for consistency of the textual output
+                    PowerStatsSpan span = createPowerStatsSpan(stats);
+                    if (span != null) {
+                        span.dump(ipw);
+                    }
+                });
+    }
+
+    @Override
+    public long storeEstimatedPowerConsumption(BatteryStatsHistory batteryStatsHistory,
+            long startTime, long endTimeMs) {
+        long[] lastSavedMonotonicTime = new long[1];
+        mPowerStatsAggregator.aggregatePowerStats(batteryStatsHistory, startTime, endTimeMs,
+                stats -> {
+                    storeAggregatedPowerStats(stats);
+                    lastSavedMonotonicTime[0] = stats.getStartTime() + stats.getDuration();
+                });
+        return lastSavedMonotonicTime[0];
+    }
+
+    @VisibleForTesting
+    void storeAggregatedPowerStats(AggregatedPowerStats stats) {
+        PowerStatsSpan span = createPowerStatsSpan(stats);
+        if (span == null) {
+            return;
+        }
+        mPowerStatsStore.storePowerStatsSpan(span);
+    }
+
+    private static PowerStatsSpan createPowerStatsSpan(AggregatedPowerStats stats) {
+        List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
+        if (clockUpdates.isEmpty()) {
+            Slog.w(TAG, "No clock updates in aggregated power stats " + stats);
+            return null;
+        }
+
+        long monotonicTime = clockUpdates.get(0).monotonicTime;
+        long durationSum = 0;
+        PowerStatsSpan span = new PowerStatsSpan(monotonicTime);
+        for (int i = 0; i < clockUpdates.size(); i++) {
+            AggregatedPowerStats.ClockUpdate clockUpdate = clockUpdates.get(i);
+            long duration;
+            if (i == clockUpdates.size() - 1) {
+                duration = stats.getDuration() - durationSum;
+            } else {
+                duration = clockUpdate.monotonicTime - monotonicTime;
+            }
+            span.addTimeFrame(clockUpdate.monotonicTime, clockUpdate.currentTime, duration);
+            monotonicTime = clockUpdate.monotonicTime;
+            durationSum += duration;
+        }
+
+        span.addSection(new AggregatedPowerStatsSection(stats));
+        return span;
+    }
+
+    @Override
+    public long getLastSavedEstimatesPowerConsumptionTimestamp() {
+        long timestamp = -1;
+        for (PowerStatsSpan.Metadata metadata : mPowerStatsStore.getTableOfContents()) {
+            if (metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+                for (PowerStatsSpan.TimeFrame timeFrame : metadata.getTimeFrames()) {
+                    long endMonotonicTime = timeFrame.startMonotonicTime + timeFrame.duration;
+                    if (endMonotonicTime > timestamp) {
+                        timestamp = endMonotonicTime;
+                    }
+                }
+            }
+        }
+        return timestamp;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
new file mode 100644
index 0000000..28474a5
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.LongArrayMultiStateCounter;
+import com.android.internal.util.Preconditions;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+/**
+ * Maintains multidimensional multi-state stats.  States could be something like on-battery (0,1),
+ * screen-on (0,1), process state etc.  Dimensions refer to the metrics themselves, e.g.
+ * CPU residency, Network packet counts etc.  All metrics must be represented as <code>long</code>
+ * values;
+ */
+class MultiStateStats {
+    private static final String TAG = "MultiStateStats";
+
+    private static final String XML_TAG_STATS = "stats";
+    public static final int STATE_DOES_NOT_EXIST = -1;
+
+    /**
+     * A set of states, e.g. on-battery, screen-on, procstate.  The state values are integers
+     * from 0 to States.mLabels.length
+     */
+    static class States {
+        final String mName;
+        final boolean mTracked;
+        final String[] mLabels;
+
+        States(String name, boolean tracked, String... labels) {
+            mName = name;
+            mTracked = tracked;
+            mLabels = labels;
+        }
+
+        public boolean isTracked() {
+            return mTracked;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public String[] getLabels() {
+            return mLabels;
+        }
+
+        /**
+         * Finds state by name in the provided array. If not found, returns STATE_DOES_NOT_EXIST.
+         */
+        public static int findTrackedStateByName(MultiStateStats.States[] states, String name) {
+            for (int i = 0; i < states.length; i++) {
+                if (states[i].getName().equals(name)) {
+                    return i;
+                }
+            }
+            return STATE_DOES_NOT_EXIST;
+        }
+
+        /**
+         * Iterates over all combinations of tracked states and invokes <code>consumer</code>
+         * for each of them.
+         */
+        public static void forEachTrackedStateCombination(States[] states,
+                Consumer<int[]> consumer) {
+            forEachTrackedStateCombination(consumer, states, new int[states.length], 0);
+        }
+
+        /**
+         * Recursive function that does a depth-first traversal of the multi-dimensional
+         * state space. Each time the traversal reaches the end of the <code>states</code> array,
+         * <code>statesValues</code> contains a unique combination of values for all tracked states.
+         * For untracked states, the corresponding values are left as 0.  The end result is
+         * that the <code>consumer</code> is invoked for every unique combination of tracked state
+         * values.  For example, it may be a sequence of calls like screen-on/power-on,
+         * screen-on/power-off, screen-off/power-on, screen-off/power-off.
+         */
+        private static void forEachTrackedStateCombination(Consumer<int[]> consumer,
+                States[] states, int[] statesValues, int stateIndex) {
+            if (stateIndex < statesValues.length) {
+                if (!states[stateIndex].mTracked) {
+                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+                    return;
+                }
+                for (int i = 0; i < states[stateIndex].mLabels.length; i++) {
+                    statesValues[stateIndex] = i;
+                    forEachTrackedStateCombination(consumer, states, statesValues, stateIndex + 1);
+                }
+                return;
+            }
+            consumer.accept(statesValues);
+        }
+    }
+
+    /**
+     * Factory for MultiStateStats containers. All generated containers retain their connection
+     * to the Factory and the corresponding configuration.
+     */
+    static class Factory {
+        private static final int INVALID_SERIAL_STATE = -1;
+        final int mDimensionCount;
+        final States[] mStates;
+        /*
+         * The LongArrayMultiStateCounter object that is used for accumulation of per-state
+         * stats thinks of "state" as a simple 0-based index. This Factory object's job is to
+         * map a combination of individual states (e.g. on-battery, process state etc) to
+         * such a simple index.
+         *
+         * This task is performed in two steps:
+         * 1) We define "composite state" as an integer that combines all constituent States
+         * into one integer as bit fields. This gives us a convenient mechanism for updating a
+         * single constituent State at a time.  We maintain an array of bit field masks
+         * corresponding to each constituent State.
+         *
+         * 2) We map composite states to "serial states", i.e. simple integer indexes, taking
+         * into account which constituent states are configured as tracked.  If a state is not
+         * tracked, there is no need to maintain separate counts for its values, thus
+         * all values of that constituent state can be mapped to the same serial state.
+         */
+        private final int[] mStateBitFieldMasks;
+        private final short[] mStateBitFieldShifts;
+        final int[] mCompositeToSerialState;
+        final int mSerialStateCount;
+
+        Factory(int dimensionCount, States... states) {
+            mDimensionCount = dimensionCount;
+            mStates = states;
+
+            int serialStateCount = 1;
+            for (States state : mStates) {
+                if (state.mTracked) {
+                    serialStateCount *= state.mLabels.length;
+                }
+            }
+            mSerialStateCount = serialStateCount;
+
+            mStateBitFieldMasks = new int[mStates.length];
+            mStateBitFieldShifts = new short[mStates.length];
+
+            int shift = 0;
+            for (int i = 0; i < mStates.length; i++) {
+                mStateBitFieldShifts[i] = (short) shift;
+                if (mStates[i].mLabels.length < 2) {
+                    throw new IllegalArgumentException("Invalid state: " + Arrays.toString(
+                            mStates[i].mLabels) + ". Should have at least two values.");
+                }
+                int max = mStates[i].mLabels.length - 1;
+                int bitcount = Integer.SIZE - Integer.numberOfLeadingZeros(max);
+                mStateBitFieldMasks[i] = ((1 << bitcount) - 1) << shift;
+                shift = shift + bitcount;
+            }
+
+            if (shift >= Integer.SIZE - 1) {
+                throw new IllegalArgumentException("Too many states: " + shift
+                        + " bits are required to represent the composite state, but only "
+                        + (Integer.SIZE - 1) + " are available");
+            }
+
+            // Create a mask that filters out all non tracked states
+            int trackedMask = 0xFFFFFFFF;
+            for (int state = 0; state < mStates.length; state++) {
+                if (!mStates[state].mTracked) {
+                    trackedMask &= ~mStateBitFieldMasks[state];
+                }
+            }
+
+            mCompositeToSerialState = new int[1 << shift];
+            Arrays.fill(mCompositeToSerialState, INVALID_SERIAL_STATE);
+
+            int nextSerialState = 0;
+            for (int composite = 0; composite < mCompositeToSerialState.length; composite++) {
+                if (!isValidCompositeState(composite)) continue;
+
+                // Values of an untracked State map to different composite states, but must map to
+                // the same serial state. Achieve that by computing a "base composite", which
+                // is equivalent to the current composite, but has 0 for all untracked States.
+                // See if the base composite already has a serial state assigned.  If so, just use
+                // the same one for the current composite.
+                int baseComposite = composite & trackedMask;
+                if (mCompositeToSerialState[baseComposite] != INVALID_SERIAL_STATE) {
+                    mCompositeToSerialState[composite] = mCompositeToSerialState[baseComposite];
+                } else {
+                    mCompositeToSerialState[composite] = nextSerialState++;
+                }
+            }
+        }
+
+        private boolean isValidCompositeState(int composite) {
+            for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
+                int state = extractStateFromComposite(composite, stateIndex);
+                if (state >= mStates[stateIndex].mLabels.length) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private int extractStateFromComposite(int compositeState, int stateIndex) {
+            return (compositeState & mStateBitFieldMasks[stateIndex])
+                   >>> mStateBitFieldShifts[stateIndex];
+        }
+
+        int setStateInComposite(int baseCompositeState, int stateIndex, int value) {
+            return (baseCompositeState & ~mStateBitFieldMasks[stateIndex])
+                    | (value << mStateBitFieldShifts[stateIndex]);
+        }
+
+        int setStateInComposite(int compositeState, String stateName, String stateLabel) {
+            for (int stateIndex = 0; stateIndex < mStates.length; stateIndex++) {
+                States stateConfig = mStates[stateIndex];
+                if (stateConfig.mName.equals(stateName)) {
+                    for (int state = 0; state < stateConfig.mLabels.length; state++) {
+                        if (stateConfig.mLabels[state].equals(stateLabel)) {
+                            return setStateInComposite(compositeState, stateIndex, state);
+                        }
+                    }
+                    Slog.e(TAG, "Unexpected label '" + stateLabel + "' for state: " + stateName);
+                    return -1;
+                }
+            }
+            Slog.e(TAG, "Unsupported state: " + stateName);
+            return -1;
+        }
+
+        /**
+         * Allocates a new stats container using this Factory's configuration.
+         */
+        MultiStateStats create() {
+            return new MultiStateStats(this, mDimensionCount);
+        }
+
+        /**
+         * Returns the total number of composite states handled by this container. For example,
+         * if there are two states: on-battery (0,1) and screen-on (0,1), both tracked, then the
+         * serial state count will be 2 * 2 = 4
+         */
+        @VisibleForTesting
+        public int getSerialStateCount() {
+            return mSerialStateCount;
+        }
+
+        /**
+         * Returns the integer index used by this container to represent the supplied composite
+         * state.
+         */
+        @VisibleForTesting
+        public int getSerialState(int[] states) {
+            Preconditions.checkArgument(states.length == mStates.length);
+            int compositeState = 0;
+            for (int i = 0; i < states.length; i++) {
+                compositeState = setStateInComposite(compositeState, i, states[i]);
+            }
+            int serialState = mCompositeToSerialState[compositeState];
+            if (serialState == INVALID_SERIAL_STATE) {
+                throw new IllegalArgumentException("State values out of bounds: "
+                                                   + Arrays.toString(states));
+            }
+            return serialState;
+        }
+
+        int getSerialState(int compositeState) {
+            return mCompositeToSerialState[compositeState];
+        }
+    }
+
+    private final Factory mFactory;
+    private final LongArrayMultiStateCounter mCounter;
+    private int mCompositeState;
+    private boolean mTracking;
+
+    MultiStateStats(Factory factory, int dimensionCount) {
+        this.mFactory = factory;
+        mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
+    }
+
+    int getDimensionCount() {
+        return mFactory.mDimensionCount;
+    }
+
+    States[] getStates() {
+        return mFactory.mStates;
+    }
+
+    /**
+     * Copies time-in-state and timestamps from the supplied prototype. Does not
+     * copy accumulated counts.
+     */
+    void copyStatesFrom(MultiStateStats otherStats) {
+        mCounter.copyStatesFrom(otherStats.mCounter);
+    }
+
+    /**
+     * Updates the current composite state by changing one of the States supplied to the Factory
+     * constructor.
+     *
+     * @param stateIndex  Corresponds to the index of the States supplied to the Factory constructor
+     * @param state       The new value of the state (e.g. 0 or 1 for "on-battery")
+     * @param timestampMs The time when the state change occurred
+     */
+    void setState(int stateIndex, int state, long timestampMs) {
+        if (!mTracking) {
+            mCounter.updateValues(new long[mCounter.getArrayLength()], timestampMs);
+            mTracking = true;
+        }
+        mCompositeState = mFactory.setStateInComposite(mCompositeState, stateIndex, state);
+        mCounter.setState(mFactory.mCompositeToSerialState[mCompositeState], timestampMs);
+    }
+
+    /**
+     * Adds the delta to the metrics.  The number of values must correspond to the dimension count
+     * supplied to the Factory constructor
+     */
+    void increment(long[] values, long timestampMs) {
+        mCounter.incrementValues(values, timestampMs);
+        mTracking = true;
+    }
+
+    /**
+     * Returns accumulated stats for the specified composite state.
+     */
+    void getStats(long[] outValues, int[] states) {
+        mCounter.getCounts(outValues, mFactory.getSerialState(states));
+    }
+
+    /**
+     * Updates the stats values for the provided combination of states.
+     */
+    void setStats(int[] states, long[] values) {
+        mCounter.setValues(mFactory.getSerialState(states), values);
+    }
+
+    /**
+     * Resets the counters.
+     */
+    void reset() {
+        mCounter.reset();
+        mTracking = false;
+    }
+
+    /**
+     * Stores contents in an XML doc.
+     */
+    void writeXml(TypedXmlSerializer serializer) throws IOException {
+        long[] tmpArray = new long[mCounter.getArrayLength()];
+
+        try {
+            States.forEachTrackedStateCombination(mFactory.mStates,
+                    states -> {
+                        try {
+                            writeXmlForStates(serializer, states, tmpArray);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    });
+        } catch (RuntimeException e) {
+            if (e.getCause() instanceof IOException) {
+                throw (IOException) e.getCause();
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
+            throws IOException {
+        mCounter.getCounts(values, mFactory.getSerialState(states));
+        boolean nonZero = false;
+        for (long value : values) {
+            if (value != 0) {
+                nonZero = true;
+                break;
+            }
+        }
+        if (!nonZero) {
+            return;
+        }
+
+        serializer.startTag(null, XML_TAG_STATS);
+
+        for (int i = 0; i < states.length; i++) {
+            if (mFactory.mStates[i].mTracked && states[i] != 0) {
+                serializer.attribute(null, mFactory.mStates[i].mName,
+                        mFactory.mStates[i].mLabels[states[i]]);
+            }
+        }
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] != 0) {
+                serializer.attributeLong(null, "_" + i, values[i]);
+            }
+        }
+        serializer.endTag(null, XML_TAG_STATS);
+    }
+
+    /**
+     * Populates the object with contents in an XML doc. The parser is expected to be
+     * positioned on the opening tag of the corresponding element.
+     */
+    boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
+        String outerTag = parser.getName();
+        long[] tmpArray = new long[mCounter.getArrayLength()];
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT
+               && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
+            if (eventType == XmlPullParser.START_TAG) {
+                if (parser.getName().equals(XML_TAG_STATS)) {
+                    Arrays.fill(tmpArray, 0);
+                    int compositeState = 0;
+                    int attributeCount = parser.getAttributeCount();
+                    for (int i = 0; i < attributeCount; i++) {
+                        String attributeName = parser.getAttributeName(i);
+                        if (attributeName.startsWith("_")) {
+                            int index;
+                            try {
+                                index = Integer.parseInt(attributeName.substring(1));
+                            } catch (NumberFormatException e) {
+                                throw new XmlPullParserException(
+                                        "Unexpected index syntax: " + attributeName, parser, e);
+                            }
+                            if (index < 0 || index >= tmpArray.length) {
+                                Slog.e(TAG, "State index out of bounds: " + index
+                                            + " length: " + tmpArray.length);
+                                return false;
+                            }
+                            tmpArray[index] = parser.getAttributeLong(i);
+                        } else {
+                            String attributeValue = parser.getAttributeValue(i);
+                            compositeState = mFactory.setStateInComposite(compositeState,
+                                    attributeName, attributeValue);
+                            if (compositeState == -1) {
+                                return false;
+                            }
+                        }
+                    }
+                    mCounter.setValues(mFactory.getSerialState(compositeState), tmpArray);
+                }
+            }
+            eventType = parser.next();
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        long[] values = new long[mCounter.getArrayLength()];
+        States.forEachTrackedStateCombination(mFactory.mStates, states -> {
+            mCounter.getCounts(values, mFactory.getSerialState(states));
+            boolean nonZero = false;
+            for (long value : values) {
+                if (value != 0) {
+                    nonZero = true;
+                    break;
+                }
+            }
+            if (!nonZero) {
+                return;
+            }
+
+            if (!sb.isEmpty()) {
+                sb.append("\n");
+            }
+
+            sb.append("(");
+            boolean first = true;
+            for (int i = 0; i < states.length; i++) {
+                if (mFactory.mStates[i].mTracked) {
+                    if (!first) {
+                        sb.append(" ");
+                    }
+                    first = false;
+                    sb.append(mFactory.mStates[i].mLabels[states[i]]);
+                }
+            }
+            sb.append(") ");
+            sb.append(Arrays.toString(values));
+        });
+        return sb.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
new file mode 100644
index 0000000..3957ae0
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
@@ -0,0 +1,80 @@
+/*
+ * 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.processor;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
+import com.android.server.power.stats.format.PhoneCallPowerStatsLayout;
+
+class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
+    private final PhoneCallPowerStatsLayout mStatsLayout;
+    private final PowerStats.Descriptor mDescriptor;
+    private final long[] mTmpDeviceStats;
+    private PowerStats.Descriptor mMobileRadioStatsDescriptor;
+    private MobileRadioPowerStatsLayout mMobileRadioStatsLayout;
+    private long[] mTmpMobileRadioDeviceStats;
+
+    PhoneCallPowerStatsProcessor() {
+        mStatsLayout = new PhoneCallPowerStatsLayout();
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_PHONE,
+                mStatsLayout.getDeviceStatsArrayLength(), null, 0, 0, extras);
+        mTmpDeviceStats = new long[mDescriptor.statsArrayLength];
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        stats.setPowerStatsDescriptor(mDescriptor);
+
+        PowerComponentAggregatedPowerStats mobileRadioStats =
+                stats.getAggregatedPowerStats().getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+        if (mobileRadioStats == null) {
+            return;
+        }
+
+        if (mMobileRadioStatsDescriptor == null) {
+            mMobileRadioStatsDescriptor = mobileRadioStats.getPowerStatsDescriptor();
+            if (mMobileRadioStatsDescriptor == null) {
+                return;
+            }
+
+            mMobileRadioStatsLayout =
+                    new MobileRadioPowerStatsLayout(
+                            mMobileRadioStatsDescriptor);
+            mTmpMobileRadioDeviceStats = new long[mMobileRadioStatsDescriptor.statsArrayLength];
+        }
+
+        MultiStateStats.States[] deviceStateConfig =
+                mobileRadioStats.getConfig().getDeviceStateConfig();
+
+        // Phone call power estimates have already been calculated by the mobile radio stats
+        // processor. All that remains to be done is copy the estimates over.
+        MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
+                states -> {
+                    mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
+                    double callPowerEstimate =
+                            mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
+                                    mTmpMobileRadioDeviceStats);
+                    mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
+                    stats.setDeviceStats(states, mTmpDeviceStats);
+                });
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
new file mode 100644
index 0000000..d04c5ba
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryStats;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.IntConsumer;
+
+/**
+ * Aggregated power stats for a specific power component (e.g. CPU, WiFi, etc). This class
+ * treats stats as arrays of nonspecific longs. Subclasses contain specific logic to interpret those
+ * longs and use them for calculations such as power attribution. They may use meta-data supplied
+ * as part of the {@link PowerStats.Descriptor}.
+ */
+class PowerComponentAggregatedPowerStats {
+    private static final String TAG = "AggregatePowerStats";
+    static final String XML_TAG_POWER_COMPONENT = "power_component";
+    static final String XML_ATTR_ID = "id";
+    private static final String XML_TAG_DEVICE_STATS = "device-stats";
+    private static final String XML_TAG_STATE_STATS = "state-stats";
+    private static final String XML_ATTR_KEY = "key";
+    private static final String XML_TAG_UID_STATS = "uid-stats";
+    private static final String XML_ATTR_UID = "uid";
+    private static final long UNKNOWN = -1;
+
+    public final int powerComponentId;
+    @NonNull
+    private final AggregatedPowerStats mAggregatedPowerStats;
+    @NonNull
+    private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+    private final MultiStateStats.States[] mDeviceStateConfig;
+    private final MultiStateStats.States[] mUidStateConfig;
+    private final int[] mDeviceStates;
+
+    private PowerStatsProcessor mProcessor;
+    private MultiStateStats.Factory mStatsFactory;
+    private MultiStateStats.Factory mStateStatsFactory;
+    private MultiStateStats.Factory mUidStatsFactory;
+    private PowerStats.Descriptor mPowerStatsDescriptor;
+    private long mPowerStatsTimestamp;
+    private MultiStateStats mDeviceStats;
+    private final SparseArray<MultiStateStats> mStateStats = new SparseArray<>();
+    private final SparseArray<UidStats> mUidStats = new SparseArray<>();
+    private long[] mZeroArray;
+
+    private static class UidStats {
+        public int[] states;
+        public MultiStateStats stats;
+        public boolean updated;
+    }
+
+    PowerComponentAggregatedPowerStats(@NonNull AggregatedPowerStats aggregatedPowerStats,
+            @NonNull AggregatedPowerStatsConfig.PowerComponent config) {
+        mAggregatedPowerStats = aggregatedPowerStats;
+        mConfig = config;
+        powerComponentId = config.getPowerComponentId();
+        mDeviceStateConfig = config.getDeviceStateConfig();
+        mUidStateConfig = config.getUidStateConfig();
+        mDeviceStates = new int[mDeviceStateConfig.length];
+        mPowerStatsTimestamp = UNKNOWN;
+    }
+
+    @NonNull
+    AggregatedPowerStats getAggregatedPowerStats() {
+        return mAggregatedPowerStats;
+    }
+
+    @NonNull
+    public AggregatedPowerStatsConfig.PowerComponent getConfig() {
+        return mConfig;
+    }
+
+    @Nullable
+    public PowerStats.Descriptor getPowerStatsDescriptor() {
+        return mPowerStatsDescriptor;
+    }
+
+    public void setPowerStatsDescriptor(PowerStats.Descriptor powerStatsDescriptor) {
+        mPowerStatsDescriptor = powerStatsDescriptor;
+    }
+
+    void start(long timestampMs) {
+        if (mProcessor == null) {
+            mProcessor = mConfig.createProcessor();
+        }
+        mProcessor.start(this, timestampMs);
+    }
+
+    void finish(long timestampMs) {
+        mProcessor.finish(this, timestampMs);
+    }
+
+    void noteStateChange(BatteryStats.HistoryItem item) {
+        mProcessor.noteStateChange(this, item);
+    }
+
+    void setState(@AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+            long timestampMs) {
+        if (mDeviceStats == null) {
+            createDeviceStats(timestampMs);
+        }
+
+        mDeviceStates[stateId] = state;
+
+        if (mDeviceStateConfig[stateId].isTracked()) {
+            if (mDeviceStats != null) {
+                mDeviceStats.setState(stateId, state, timestampMs);
+            }
+            for (int i = mStateStats.size() - 1; i >= 0; i--) {
+                MultiStateStats stateStats = mStateStats.valueAt(i);
+                stateStats.setState(stateId, state, timestampMs);
+            }
+        }
+
+        int uidStateId = MultiStateStats.States
+                .findTrackedStateByName(mUidStateConfig, mDeviceStateConfig[stateId].getName());
+        if (uidStateId != MultiStateStats.STATE_DOES_NOT_EXIST
+                && mUidStateConfig[uidStateId].isTracked()) {
+            for (int i = mUidStats.size() - 1; i >= 0; i--) {
+                PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+                if (uidStats.stats == null) {
+                    createUidStats(uidStats, timestampMs);
+                }
+
+                uidStats.states[uidStateId] = state;
+                if (uidStats.stats != null) {
+                    uidStats.stats.setState(uidStateId, state, timestampMs);
+                }
+            }
+        }
+    }
+
+    void setUidState(int uid, @AggregatedPowerStatsConfig.TrackedState int stateId, int state,
+            long timestampMs) {
+        if (!mUidStateConfig[stateId].isTracked()) {
+            return;
+        }
+
+        UidStats uidStats = getUidStats(uid);
+        if (uidStats.stats == null) {
+            createUidStats(uidStats, timestampMs);
+        }
+
+        uidStats.states[stateId] = state;
+
+        if (uidStats.stats != null) {
+            uidStats.stats.setState(stateId, state, timestampMs);
+        }
+    }
+
+    void setDeviceStats(int[] states, long[] values) {
+        if (mDeviceStats == null) {
+            createDeviceStats(0);
+        }
+        mDeviceStats.setStats(states, values);
+    }
+
+    void setUidStats(int uid, int[] states, long[] values) {
+        UidStats uidStats = getUidStats(uid);
+        if (uidStats.stats == null) {
+            createUidStats(uidStats, mPowerStatsTimestamp);
+        }
+        uidStats.stats.setStats(states, values);
+    }
+
+    boolean isCompatible(PowerStats powerStats) {
+        return mPowerStatsDescriptor == null || mPowerStatsDescriptor.equals(powerStats.descriptor);
+    }
+
+    void addPowerStats(PowerStats powerStats, long timestampMs) {
+        // Should call powerStats.addProcessedPowerStats
+        mProcessor.addPowerStats(this, powerStats, timestampMs);
+    }
+
+    /**
+     * Should be called ONLY by PowerStatsProcessor.processPowerStats.
+     */
+    void addProcessedPowerStats(PowerStats powerStats, long timestampMs) {
+        mPowerStatsDescriptor = powerStats.descriptor;
+
+        if (mDeviceStats == null) {
+            createDeviceStats(timestampMs);
+        }
+
+        for (int i = powerStats.stateStats.size() - 1; i >= 0; i--) {
+            int key = powerStats.stateStats.keyAt(i);
+            MultiStateStats stateStats = mStateStats.get(key);
+            if (stateStats == null) {
+                stateStats = createStateStats(key, timestampMs);
+            }
+            stateStats.increment(powerStats.stateStats.valueAt(i), timestampMs);
+        }
+        mDeviceStats.increment(powerStats.stats, timestampMs);
+
+        for (int i = powerStats.uidStats.size() - 1; i >= 0; i--) {
+            int uid = powerStats.uidStats.keyAt(i);
+            PowerComponentAggregatedPowerStats.UidStats uidStats = getUidStats(uid);
+            if (uidStats.stats == null) {
+                createUidStats(uidStats, timestampMs);
+            }
+            uidStats.stats.increment(powerStats.uidStats.valueAt(i), timestampMs);
+            uidStats.updated = true;
+        }
+
+        // For UIDs not mentioned in the PowerStats object, we must assume a 0 increment.
+        // It is essential to call `stats.increment(zero)` in order to record the new
+        // timestamp, which will ensure correct proportional attribution across all UIDs
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            PowerComponentAggregatedPowerStats.UidStats uidStats = mUidStats.valueAt(i);
+            if (!uidStats.updated && uidStats.stats != null) {
+                if (mZeroArray == null
+                        || mZeroArray.length != mPowerStatsDescriptor.uidStatsArrayLength) {
+                    mZeroArray = new long[mPowerStatsDescriptor.uidStatsArrayLength];
+                }
+                uidStats.stats.increment(mZeroArray, timestampMs);
+            }
+            uidStats.updated = false;
+        }
+
+        mPowerStatsTimestamp = timestampMs;
+    }
+
+    void reset() {
+        mStatsFactory = null;
+        mUidStatsFactory = null;
+        mDeviceStats = null;
+        mStateStats.clear();
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            mUidStats.valueAt(i).stats = null;
+        }
+    }
+
+    private UidStats getUidStats(int uid) {
+        UidStats uidStats = mUidStats.get(uid);
+        if (uidStats == null) {
+            uidStats = new UidStats();
+            uidStats.states = new int[mUidStateConfig.length];
+            for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+                if (mUidStateConfig[stateId].isTracked()) {
+                    int deviceStateId = MultiStateStats.States.findTrackedStateByName(
+                            mDeviceStateConfig, mUidStateConfig[stateId].getName());
+                    if (deviceStateId != MultiStateStats.STATE_DOES_NOT_EXIST
+                            && mDeviceStateConfig[deviceStateId].isTracked()) {
+                        uidStats.states[stateId] = mDeviceStates[deviceStateId];
+                    }
+                }
+            }
+            mUidStats.put(uid, uidStats);
+        }
+        return uidStats;
+    }
+
+    void collectUids(Collection<Integer> uids) {
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            if (mUidStats.valueAt(i).stats != null) {
+                uids.add(mUidStats.keyAt(i));
+            }
+        }
+    }
+
+    boolean getDeviceStats(long[] outValues, int[] deviceStates) {
+        if (deviceStates.length != mDeviceStateConfig.length) {
+            throw new IllegalArgumentException(
+                    "Invalid number of tracked states: " + deviceStates.length
+                    + " expected: " + mDeviceStateConfig.length);
+        }
+        if (mDeviceStats != null) {
+            mDeviceStats.getStats(outValues, deviceStates);
+            return true;
+        }
+        return false;
+    }
+
+    boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
+        if (deviceStates.length != mDeviceStateConfig.length) {
+            throw new IllegalArgumentException(
+                    "Invalid number of tracked states: " + deviceStates.length
+                            + " expected: " + mDeviceStateConfig.length);
+        }
+        MultiStateStats stateStats = mStateStats.get(key);
+        if (stateStats != null) {
+            stateStats.getStats(outValues, deviceStates);
+            return true;
+        }
+        return false;
+    }
+
+    void forEachStateStatsKey(IntConsumer consumer) {
+        for (int i = mStateStats.size() - 1; i >= 0; i--) {
+            consumer.accept(mStateStats.keyAt(i));
+        }
+    }
+
+    boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
+        if (uidStates.length != mUidStateConfig.length) {
+            throw new IllegalArgumentException(
+                    "Invalid number of tracked states: " + uidStates.length
+                    + " expected: " + mUidStateConfig.length);
+        }
+        UidStats uidStats = mUidStats.get(uid);
+        if (uidStats != null && uidStats.stats != null) {
+            uidStats.stats.getStats(outValues, uidStates);
+            return true;
+        }
+        return false;
+    }
+
+    private void createDeviceStats(long timestampMs) {
+        if (mStatsFactory == null) {
+            if (mPowerStatsDescriptor == null) {
+                return;
+            }
+            mStatsFactory = new MultiStateStats.Factory(
+                    mPowerStatsDescriptor.statsArrayLength, mDeviceStateConfig);
+        }
+
+        mDeviceStats = mStatsFactory.create();
+        if (mPowerStatsTimestamp != UNKNOWN) {
+            timestampMs = mPowerStatsTimestamp;
+        }
+        if (timestampMs != UNKNOWN) {
+            for (int stateId = 0; stateId < mDeviceStateConfig.length; stateId++) {
+                int state = mDeviceStates[stateId];
+                mDeviceStats.setState(stateId, state, timestampMs);
+                for (int i = mStateStats.size() - 1; i >= 0; i--) {
+                    MultiStateStats stateStats = mStateStats.valueAt(i);
+                    stateStats.setState(stateId, state, timestampMs);
+                }
+            }
+        }
+    }
+
+    private MultiStateStats createStateStats(int key, long timestampMs) {
+        if (mStateStatsFactory == null) {
+            if (mPowerStatsDescriptor == null) {
+                return null;
+            }
+            mStateStatsFactory = new MultiStateStats.Factory(
+                    mPowerStatsDescriptor.stateStatsArrayLength, mDeviceStateConfig);
+        }
+
+        MultiStateStats stateStats = mStateStatsFactory.create();
+        mStateStats.put(key, stateStats);
+        if (mDeviceStats != null) {
+            stateStats.copyStatesFrom(mDeviceStats);
+        }
+
+        return stateStats;
+    }
+
+    private void createUidStats(UidStats uidStats, long timestampMs) {
+        if (mUidStatsFactory == null) {
+            if (mPowerStatsDescriptor == null) {
+                return;
+            }
+            mUidStatsFactory = new MultiStateStats.Factory(
+                    mPowerStatsDescriptor.uidStatsArrayLength, mUidStateConfig);
+        }
+
+        uidStats.stats = mUidStatsFactory.create();
+
+        if (mPowerStatsTimestamp != UNKNOWN) {
+            timestampMs = mPowerStatsTimestamp;
+        }
+        if (timestampMs != UNKNOWN) {
+            for (int stateId = 0; stateId < mUidStateConfig.length; stateId++) {
+                uidStats.stats.setState(stateId, uidStats.states[stateId], timestampMs);
+            }
+        }
+    }
+
+    void copyStatesFrom(PowerComponentAggregatedPowerStats source) {
+        if (source.mDeviceStates.length == mDeviceStates.length) {
+            System.arraycopy(source.mDeviceStates, 0, mDeviceStates, 0, mDeviceStates.length);
+            if (source.mDeviceStats != null) {
+                createDeviceStats(0);
+                if (mDeviceStats != null) {
+                    mDeviceStats.copyStatesFrom(source.mDeviceStats);
+                }
+            }
+        } else {
+            Slog.wtf(TAG, "State configurations have different lengths: "
+                    + source.mDeviceStates.length + " vs " + mDeviceStates.length);
+        }
+        for (int i = source.mUidStats.size() - 1; i >= 0; i--) {
+            int uid = source.mUidStats.keyAt(i);
+            UidStats sourceUidStats = source.mUidStats.valueAt(i);
+            if (sourceUidStats.states == null) {
+                continue;
+            }
+            UidStats uidStats = new UidStats();
+            uidStats.states = Arrays.copyOf(sourceUidStats.states, sourceUidStats.states.length);
+            if (sourceUidStats.stats != null) {
+                createUidStats(uidStats, 0);
+                if (uidStats.stats != null) {
+                    uidStats.stats.copyStatesFrom(sourceUidStats.stats);
+                }
+            }
+            mUidStats.put(uid, uidStats);
+        }
+    }
+
+    public void writeXml(TypedXmlSerializer serializer) throws IOException {
+        // No stats aggregated - can skip writing XML altogether
+        if (mPowerStatsDescriptor == null) {
+            return;
+        }
+
+        serializer.startTag(null, XML_TAG_POWER_COMPONENT);
+        serializer.attributeInt(null, XML_ATTR_ID, powerComponentId);
+        mPowerStatsDescriptor.writeXml(serializer);
+
+        if (mDeviceStats != null) {
+            serializer.startTag(null, XML_TAG_DEVICE_STATS);
+            mDeviceStats.writeXml(serializer);
+            serializer.endTag(null, XML_TAG_DEVICE_STATS);
+        }
+
+        for (int i = 0; i < mStateStats.size(); i++) {
+            serializer.startTag(null, XML_TAG_STATE_STATS);
+            serializer.attributeInt(null, XML_ATTR_KEY, mStateStats.keyAt(i));
+            mStateStats.valueAt(i).writeXml(serializer);
+            serializer.endTag(null, XML_TAG_STATE_STATS);
+        }
+
+        for (int i = mUidStats.size() - 1; i >= 0; i--) {
+            int uid = mUidStats.keyAt(i);
+            UidStats uidStats = mUidStats.valueAt(i);
+            if (uidStats.stats != null) {
+                serializer.startTag(null, XML_TAG_UID_STATS);
+                serializer.attributeInt(null, XML_ATTR_UID, uid);
+                uidStats.stats.writeXml(serializer);
+                serializer.endTag(null, XML_TAG_UID_STATS);
+            }
+        }
+
+        serializer.endTag(null, XML_TAG_POWER_COMPONENT);
+        serializer.flush();
+    }
+
+    public boolean readFromXml(TypedXmlPullParser parser) throws XmlPullParserException,
+            IOException {
+        String outerTag = parser.getName();
+        int eventType = parser.getEventType();
+        while (eventType != XmlPullParser.END_DOCUMENT
+                && !(eventType == XmlPullParser.END_TAG && parser.getName().equals(outerTag))) {
+            if (eventType == XmlPullParser.START_TAG) {
+                switch (parser.getName()) {
+                    case PowerStats.Descriptor.XML_TAG_DESCRIPTOR:
+                        mPowerStatsDescriptor = PowerStats.Descriptor.createFromXml(parser);
+                        if (mPowerStatsDescriptor == null) {
+                            return false;
+                        }
+                        break;
+                    case XML_TAG_DEVICE_STATS:
+                        if (mDeviceStats == null) {
+                            createDeviceStats(UNKNOWN);
+                        }
+                        if (!mDeviceStats.readFromXml(parser)) {
+                            return false;
+                        }
+                        break;
+                    case XML_TAG_STATE_STATS:
+                        int key = parser.getAttributeInt(null, XML_ATTR_KEY);
+                        MultiStateStats stats = mStateStats.get(key);
+                        if (stats == null) {
+                            stats = createStateStats(key, UNKNOWN);
+                        }
+                        if (!stats.readFromXml(parser)) {
+                            return false;
+                        }
+                        break;
+                    case XML_TAG_UID_STATS:
+                        int uid = parser.getAttributeInt(null, XML_ATTR_UID);
+                        UidStats uidStats = getUidStats(uid);
+                        if (uidStats.stats == null) {
+                            createUidStats(uidStats, UNKNOWN);
+                        }
+                        if (!uidStats.stats.readFromXml(parser)) {
+                            return false;
+                        }
+                        break;
+                }
+            }
+            eventType = parser.next();
+        }
+        return true;
+    }
+
+    void dumpDevice(IndentingPrintWriter ipw) {
+        if (mDeviceStats != null) {
+            dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null,
+                    mPowerStatsDescriptor.getDeviceStatsFormatter());
+        }
+
+        if (mStateStats.size() != 0) {
+            ipw.increaseIndent();
+            String header = mPowerStatsDescriptor.name + " states";
+            PowerStats.PowerStatsFormatter formatter =
+                    mPowerStatsDescriptor.getStateStatsFormatter();
+            for (int i = 0; i < mStateStats.size(); i++) {
+                int key = mStateStats.keyAt(i);
+                String stateLabel = mPowerStatsDescriptor.getStateLabel(key);
+                MultiStateStats stateStats = mStateStats.valueAt(i);
+                dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter);
+            }
+            ipw.decreaseIndent();
+        }
+    }
+
+    void dumpUid(IndentingPrintWriter ipw, int uid) {
+        UidStats uidStats = mUidStats.get(uid);
+        if (uidStats != null && uidStats.stats != null) {
+            dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null,
+                    mPowerStatsDescriptor.getUidStatsFormatter());
+        }
+    }
+
+    private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats,
+            String header, String additionalLabel,
+            PowerStats.PowerStatsFormatter statsFormatter) {
+        boolean[] firstLine = new boolean[]{true};
+        long[] values = new long[stats.getDimensionCount()];
+        MultiStateStats.States[] stateInfo = stats.getStates();
+        MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
+            stats.getStats(values, states);
+            boolean nonZero = false;
+            for (long value : values) {
+                if (value != 0) {
+                    nonZero = true;
+                    break;
+                }
+            }
+            if (!nonZero) {
+                return;
+            }
+
+            if (firstLine[0]) {
+                ipw.println(header);
+                ipw.increaseIndent();
+            }
+            firstLine[0] = false;
+            StringBuilder sb = new StringBuilder();
+            sb.append("(");
+            boolean first = true;
+            for (int i = 0; i < states.length; i++) {
+                if (stateInfo[i].isTracked()) {
+                    if (!first) {
+                        sb.append(" ");
+                    }
+                    first = false;
+                    sb.append(stateInfo[i].getLabels()[states[i]]);
+                }
+            }
+            if (additionalLabel != null) {
+                sb.append(" ").append(additionalLabel);
+            }
+            sb.append(") ").append(statsFormatter.format(values));
+            ipw.println(sb);
+        });
+        if (!firstLine[0]) {
+            ipw.decreaseIndent();
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        IndentingPrintWriter ipw = new IndentingPrintWriter(sw);
+        ipw.increaseIndent();
+        dumpDevice(ipw);
+        ipw.decreaseIndent();
+
+        int[] uids = new int[mUidStats.size()];
+        for (int i = uids.length - 1; i >= 0; i--) {
+            uids[i] = mUidStats.keyAt(i);
+        }
+        Arrays.sort(uids);
+        for (int uid : uids) {
+            ipw.println(UserHandle.formatUid(uid));
+            ipw.increaseIndent();
+            dumpUid(ipw, uid);
+            ipw.decreaseIndent();
+        }
+
+        ipw.flush();
+
+        return sw.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
new file mode 100644
index 0000000..32c1056
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsAggregator.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.processor;
+
+import android.annotation.NonNull;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.BatteryStatsHistoryIterator;
+
+import java.util.function.Consumer;
+
+/**
+ * Power stats aggregator. It reads through portions of battery stats history, finds
+ * relevant items (state changes, power stats etc) and produces one or more
+ * {@link AggregatedPowerStats} that adds up power stats from the samples found in battery history.
+ */
+public class PowerStatsAggregator {
+    private static final long UNINITIALIZED = -1;
+    private final AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
+    private final SparseBooleanArray mEnabledComponents =
+            new SparseBooleanArray(BatteryConsumer.POWER_COMPONENT_COUNT + 10);
+    private AggregatedPowerStats mStats;
+    private int mCurrentBatteryState = AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+    private int mCurrentScreenState = AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+
+    @VisibleForTesting
+    public PowerStatsAggregator() {
+        this(new AggregatedPowerStatsConfig());
+    }
+
+    PowerStatsAggregator(@NonNull AggregatedPowerStatsConfig aggregatedPowerStatsConfig) {
+        mAggregatedPowerStatsConfig = aggregatedPowerStatsConfig;
+    }
+
+    AggregatedPowerStatsConfig getConfig() {
+        return mAggregatedPowerStatsConfig;
+    }
+
+    void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
+        synchronized (this) {
+            if (mStats != null) {
+                mStats = null;
+            }
+            mEnabledComponents.put(powerComponentId, enabled);
+        }
+    }
+
+    /**
+     * Iterates of the battery history and aggregates power stats between the specified times.
+     * The start and end are specified in the battery-stats monotonic time, which is the
+     * adjusted elapsed time found in HistoryItem.time.
+     * <p>
+     * The aggregated stats are sent to the consumer. One aggregation pass may produce
+     * multiple sets of aggregated stats if there was an incompatible change that occurred in the
+     * middle of the recorded battery history.
+     * <p>
+     * Note: the AggregatedPowerStats object is reused, so the consumer should fully consume
+     * the stats in the <code>accept</code> method and never cache it.
+     */
+    public void aggregatePowerStats(BatteryStatsHistory history, long startTimeMs, long endTimeMs,
+            Consumer<AggregatedPowerStats> consumer) {
+        synchronized (this) {
+            if (mStats == null) {
+                mStats = new AggregatedPowerStats(mAggregatedPowerStatsConfig, mEnabledComponents);
+            }
+
+            mStats.start(startTimeMs);
+
+            boolean clockUpdateAdded = false;
+            long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED;
+            long lastTime = 0;
+            int lastStates = 0xFFFFFFFF;
+            int lastStates2 = 0xFFFFFFFF;
+            try (BatteryStatsHistoryIterator iterator = history.iterate(startTimeMs, endTimeMs)) {
+                while (iterator.hasNext()) {
+                    BatteryStats.HistoryItem item = iterator.next();
+
+                    if (!clockUpdateAdded) {
+                        mStats.addClockUpdate(item.time, item.currentTime);
+                        if (baseTime == UNINITIALIZED) {
+                            baseTime = item.time;
+                        }
+                        clockUpdateAdded = true;
+                    } else if (item.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
+                               || item.cmd == BatteryStats.HistoryItem.CMD_RESET) {
+                        mStats.addClockUpdate(item.time, item.currentTime);
+                    }
+
+                    lastTime = item.time;
+
+                    int batteryState =
+                            (item.states & BatteryStats.HistoryItem.STATE_BATTERY_PLUGGED_FLAG) != 0
+                                    ? AggregatedPowerStatsConfig.POWER_STATE_OTHER
+                                    : AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+                    if (batteryState != mCurrentBatteryState) {
+                        mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_POWER, batteryState,
+                                item.time);
+                        mCurrentBatteryState = batteryState;
+                    }
+
+                    int screenState =
+                            (item.states & BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG) != 0
+                                    ? AggregatedPowerStatsConfig.SCREEN_STATE_ON
+                                    : AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+                    if (screenState != mCurrentScreenState) {
+                        mStats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN, screenState,
+                                item.time);
+                        mCurrentScreenState = screenState;
+                    }
+
+                    if ((item.states
+                            & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES)
+                            != lastStates
+                            || (item.states2
+                            & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2)
+                            != lastStates2) {
+                        mStats.noteStateChange(item);
+                        lastStates = item.states
+                                & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES;
+                        lastStates2 = item.states2
+                                & BatteryStats.HistoryItem.IMPORTANT_FOR_POWER_STATS_STATES2;
+                    }
+
+                    if (item.processStateChange != null) {
+                        mStats.setUidState(item.processStateChange.uid,
+                                AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+                                item.processStateChange.processState, item.time);
+                    }
+
+                    if (item.powerStats != null) {
+                        if (!mStats.isCompatible(item.powerStats)) {
+                            if (lastTime > baseTime) {
+                                mStats.setDuration(lastTime - baseTime);
+                                mStats.finish(lastTime);
+                                consumer.accept(mStats);
+                            }
+                            mStats.reset();
+                            mStats.addClockUpdate(item.time, item.currentTime);
+                            baseTime = lastTime = item.time;
+                        }
+                        mStats.addPowerStats(item.powerStats, item.time);
+                    }
+                }
+            }
+            if (lastTime > baseTime) {
+                mStats.setDuration(lastTime - baseTime);
+                mStats.finish(lastTime);
+                consumer.accept(mStats);
+            }
+
+            mStats.reset();     // to free up memory
+        }
+    }
+
+    /**
+     * Reset to prepare for a new aggregation session.
+     */
+    public void reset() {
+        synchronized (this) {
+            mStats = null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
new file mode 100644
index 0000000..fab87d6
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsExporter.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import android.annotation.Nullable;
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.format.PowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Given a time range, converts accumulated PowerStats to BatteryUsageStats.  Combines
+ * stores spans of PowerStats with the yet-unprocessed tail of battery history.
+ */
+class PowerStatsExporter {
+    private static final String TAG = "PowerStatsExporter";
+    private final PowerStatsStore mPowerStatsStore;
+    private final PowerStatsAggregator mPowerStatsAggregator;
+    private final long mBatterySessionTimeSpanSlackMillis;
+    private static final long BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS = TimeUnit.MINUTES.toMillis(2);
+
+    PowerStatsExporter(PowerStatsStore powerStatsStore, PowerStatsAggregator powerStatsAggregator) {
+        this(powerStatsStore, powerStatsAggregator, BATTERY_SESSION_TIME_SPAN_SLACK_MILLIS);
+    }
+
+    PowerStatsExporter(PowerStatsStore powerStatsStore,
+            PowerStatsAggregator powerStatsAggregator,
+            long batterySessionTimeSpanSlackMillis) {
+        mPowerStatsStore = powerStatsStore;
+        mPowerStatsAggregator = powerStatsAggregator;
+        mBatterySessionTimeSpanSlackMillis = batterySessionTimeSpanSlackMillis;
+    }
+
+    /**
+     * Populates the provided BatteryUsageStats.Builder with power estimates from the accumulated
+     * PowerStats, both stored in PowerStatsStore and not-yet processed.
+     */
+    void exportAggregatedPowerStats(BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            BatteryStatsHistory history, long monotonicStartTime, long monotonicEndTime) {
+        synchronized (mPowerStatsAggregator) {
+            boolean hasStoredSpans = false;
+            long maxEndTime = monotonicStartTime;
+            List<PowerStatsSpan.Metadata> spans = mPowerStatsStore.getTableOfContents();
+            for (int i = spans.size() - 1; i >= 0; i--) {
+                PowerStatsSpan.Metadata metadata = spans.get(i);
+                if (!metadata.getSections().contains(AggregatedPowerStatsSection.TYPE)) {
+                    continue;
+                }
+
+                List<PowerStatsSpan.TimeFrame> timeFrames = metadata.getTimeFrames();
+                long spanMinTime = Long.MAX_VALUE;
+                long spanMaxTime = Long.MIN_VALUE;
+                for (int j = 0; j < timeFrames.size(); j++) {
+                    PowerStatsSpan.TimeFrame timeFrame = timeFrames.get(j);
+                    long startMonotonicTime = timeFrame.startMonotonicTime;
+                    long endMonotonicTime = startMonotonicTime + timeFrame.duration;
+                    if (startMonotonicTime < spanMinTime) {
+                        spanMinTime = startMonotonicTime;
+                    }
+                    if (endMonotonicTime > spanMaxTime) {
+                        spanMaxTime = endMonotonicTime;
+                    }
+                }
+
+                if (!(spanMinTime >= monotonicStartTime && spanMaxTime < monotonicEndTime)) {
+                    continue;
+                }
+
+                if (spanMaxTime > maxEndTime) {
+                    maxEndTime = spanMaxTime;
+                }
+
+                PowerStatsSpan span = mPowerStatsStore.loadPowerStatsSpan(metadata.getId(),
+                        AggregatedPowerStatsSection.TYPE);
+                if (span == null) {
+                    Slog.e(TAG, "Could not read PowerStatsStore section " + metadata);
+                    continue;
+                }
+                List<PowerStatsSpan.Section> sections = span.getSections();
+                for (int k = 0; k < sections.size(); k++) {
+                    hasStoredSpans = true;
+                    PowerStatsSpan.Section section = sections.get(k);
+                    populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder,
+                            ((AggregatedPowerStatsSection) section).getAggregatedPowerStats());
+                }
+            }
+
+            if (!hasStoredSpans
+                    || maxEndTime < monotonicEndTime - mBatterySessionTimeSpanSlackMillis) {
+                mPowerStatsAggregator.aggregatePowerStats(history, maxEndTime, monotonicEndTime,
+                        stats -> populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, stats));
+            }
+            mPowerStatsAggregator.reset();
+        }
+    }
+
+    @VisibleForTesting
+    void populateBatteryUsageStatsBuilder(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder, AggregatedPowerStats stats) {
+        List<PowerComponentAggregatedPowerStats> powerComponentStats =
+                stats.getPowerComponentStats();
+        for (int i = powerComponentStats.size() - 1; i >= 0; i--) {
+            populateBatteryUsageStatsBuilder(batteryUsageStatsBuilder, powerComponentStats.get(i));
+        }
+    }
+
+    private void populateBatteryUsageStatsBuilder(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            PowerComponentAggregatedPowerStats powerComponentStats) {
+        PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
+        if (descriptor == null) {
+            return;
+        }
+
+        if (!batteryUsageStatsBuilder.isSupportedPowerComponent(descriptor.powerComponentId)) {
+            return;
+        }
+
+        PowerStatsLayout layout = new PowerStatsLayout(descriptor);
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
+            if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
+                if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                    continue;
+                }
+            } else if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                continue;
+            }
+
+            for (int powerState = 0; powerState < BatteryConsumer.POWER_STATE_COUNT; powerState++) {
+                if (batteryUsageStatsBuilder.isPowerStateDataNeeded()) {
+                    if (powerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+                        populateAggregatedBatteryConsumer(batteryUsageStatsBuilder,
+                                powerComponentStats,
+                                layout, deviceStats, screenState, powerState);
+                    }
+                } else if (powerState == BatteryConsumer.POWER_STATE_BATTERY) {
+                    populateAggregatedBatteryConsumer(batteryUsageStatsBuilder,
+                            powerComponentStats,
+                            layout, deviceStats, screenState, powerState);
+                }
+            }
+        }
+        if (layout.isUidPowerAttributionSupported()) {
+            populateBatteryConsumers(batteryUsageStatsBuilder,
+                    powerComponentStats, layout);
+        }
+    }
+
+    private void populateAggregatedBatteryConsumer(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout,
+            long[] deviceStats, @BatteryConsumer.ScreenState int screenState,
+            @BatteryConsumer.PowerState int powerState) {
+        int powerComponentId = powerComponentStats.powerComponentId;
+        double[] totalPower = new double[1];
+        MultiStateStats.States.forEachTrackedStateCombination(
+                powerComponentStats.getConfig().getDeviceStateConfig(),
+                states -> {
+                    if (!areMatchingStates(states, screenState, powerState)) {
+                        return;
+                    }
+
+                    if (!powerComponentStats.getDeviceStats(deviceStats, states)) {
+                        return;
+                    }
+
+                    totalPower[0] += layout.getDevicePowerEstimate(deviceStats);
+                });
+
+        AggregateBatteryConsumer.Builder deviceScope =
+                batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
+        BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, deviceScope,
+                powerComponentId, screenState, powerState);
+        if (key != null) {
+            deviceScope.addConsumedPower(key, totalPower[0],
+                    BatteryConsumer.POWER_MODEL_UNDEFINED);
+        }
+        deviceScope.addConsumedPower(powerComponentId, totalPower[0],
+                BatteryConsumer.POWER_MODEL_UNDEFINED);
+    }
+
+    private void populateBatteryConsumers(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            PowerComponentAggregatedPowerStats powerComponentStats,
+            PowerStatsLayout layout) {
+        AggregatedPowerStatsConfig.PowerComponent powerComponent = powerComponentStats.getConfig();
+        PowerStats.Descriptor descriptor = powerComponentStats.getPowerStatsDescriptor();
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+
+        boolean breakDownByProcState = batteryUsageStatsBuilder.isProcessStateDataNeeded()
+                && powerComponent
+                .getUidStateConfig()[AggregatedPowerStatsConfig.STATE_PROCESS_STATE].isTracked();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        powerComponentStats.collectUids(uids);
+        for (int screenState = 0; screenState < BatteryConsumer.SCREEN_STATE_COUNT; screenState++) {
+            if (batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
+                if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                    continue;
+                }
+            } else if (screenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED) {
+                continue;
+            }
+
+            for (int powerState = 0; powerState < BatteryConsumer.POWER_STATE_COUNT; powerState++) {
+                if (batteryUsageStatsBuilder.isPowerStateDataNeeded()) {
+                    if (powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+                        continue;
+                    }
+                } else if (powerState != BatteryConsumer.POWER_STATE_BATTERY) {
+                    continue;
+                }
+
+                populateUidBatteryConsumers(batteryUsageStatsBuilder, powerComponentStats, layout,
+                        uids, powerComponent, uidStats, breakDownByProcState, screenState,
+                        powerState);
+            }
+        }
+    }
+
+    private void populateUidBatteryConsumers(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            PowerComponentAggregatedPowerStats powerComponentStats, PowerStatsLayout layout,
+            List<Integer> uids, AggregatedPowerStatsConfig.PowerComponent powerComponent,
+            long[] uidStats, boolean breakDownByProcState,
+            @BatteryConsumer.ScreenState int screenState,
+            @BatteryConsumer.PowerState int powerState) {
+        if (!batteryUsageStatsBuilder.isPowerStateDataNeeded()
+                && powerState != BatteryConsumer.POWER_STATE_BATTERY) {
+            return;
+        }
+
+        @BatteryConsumer.PowerComponentId int powerComponentId =
+                powerComponentStats.powerComponentId;
+        double[] powerByProcState =
+                new double[breakDownByProcState ? BatteryConsumer.PROCESS_STATE_COUNT : 1];
+        double powerAllApps = 0;
+        for (int uid : uids) {
+            UidBatteryConsumer.Builder builder =
+                    batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+            Arrays.fill(powerByProcState, 0);
+
+            MultiStateStats.States.forEachTrackedStateCombination(
+                    powerComponent.getUidStateConfig(),
+                    states -> {
+                        if (!areMatchingStates(states, screenState, powerState)) {
+                            return;
+                        }
+
+                        if (!powerComponentStats.getUidStats(uidStats, uid, states)) {
+                            return;
+                        }
+
+                        double power = layout.getUidPowerEstimate(uidStats);
+                        if (breakDownByProcState) {
+                            int procState = states[AggregatedPowerStatsConfig.STATE_PROCESS_STATE];
+                            // There is a difference in how PowerComponentAggregatedPowerStats
+                            // and BatteryUsageStats see the "unspecified" process state.
+                            // PowerComponentAggregatedPowerStats preserves it as is.
+                            // BatteryUsageStats uses PROCESS_STATE_UNSPECIFIED to hold the total
+                            // across all states, and PROCESS_STATE_UNSPECIFIED is treated
+                            // the same as PROCESS_STATE_BACKGROUND, which makes sense since
+                            // PROCESS_STATE_UNSPECIFIED is only present for headless processes
+                            // like Process.ROOT_UID, Process.WIFI_UID etc.
+                            if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                                procState = BatteryConsumer.PROCESS_STATE_BACKGROUND;
+                            }
+                            powerByProcState[procState] += power;
+                        }
+                        powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED] += power;
+                    });
+
+            int resultScreenState = batteryUsageStatsBuilder.isScreenStateDataNeeded()
+                    ? screenState : BatteryConsumer.SCREEN_STATE_UNSPECIFIED;
+            int resultPowerState = batteryUsageStatsBuilder.isPowerStateDataNeeded()
+                    ? powerState : BatteryConsumer.POWER_STATE_UNSPECIFIED;
+            for (int procState = 0; procState < powerByProcState.length; procState++) {
+                double power = powerByProcState[procState];
+                if (power == 0) {
+                    continue;
+                }
+                BatteryConsumer.Key key = builder.getKey(powerComponentId, procState,
+                        resultScreenState, resultPowerState);
+                builder.addConsumedPower(key, power, BatteryConsumer.POWER_MODEL_UNDEFINED);
+            }
+
+            if (resultScreenState != BatteryConsumer.SCREEN_STATE_UNSPECIFIED
+                    || resultPowerState != BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+                builder.addConsumedPower(powerComponentId,
+                        powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED],
+                        BatteryConsumer.POWER_MODEL_UNDEFINED);
+            }
+            powerAllApps += powerByProcState[BatteryConsumer.PROCESS_STATE_UNSPECIFIED];
+        }
+
+        AggregateBatteryConsumer.Builder allAppsScope =
+                batteryUsageStatsBuilder.getAggregateBatteryConsumerBuilder(
+                        BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
+        BatteryConsumer.Key key = getKeyForPartialTotal(batteryUsageStatsBuilder, allAppsScope,
+                powerComponentId, screenState, powerState);
+        if (key != null) {
+            allAppsScope.addConsumedPower(key, powerAllApps,
+                    BatteryConsumer.POWER_MODEL_UNDEFINED);
+        }
+        allAppsScope.addConsumedPower(powerComponentId, powerAllApps,
+                BatteryConsumer.POWER_MODEL_UNDEFINED);
+    }
+
+    @Nullable
+    private BatteryConsumer.Key getKeyForPartialTotal(
+            BatteryUsageStats.Builder batteryUsageStatsBuilder,
+            AggregateBatteryConsumer.Builder builder,
+            @BatteryConsumer.PowerComponentId int powerComponentId,
+            @BatteryConsumer.ScreenState int screenState,
+            @BatteryConsumer.PowerState int powerState) {
+        if (!batteryUsageStatsBuilder.isScreenStateDataNeeded()) {
+            screenState = BatteryConsumer.SCREEN_STATE_UNSPECIFIED;
+        }
+        if (!batteryUsageStatsBuilder.isPowerStateDataNeeded()) {
+            powerState = BatteryConsumer.POWER_STATE_UNSPECIFIED;
+        }
+
+        if (screenState == BatteryConsumer.SCREEN_STATE_UNSPECIFIED
+                && powerState == BatteryConsumer.POWER_STATE_UNSPECIFIED) {
+            return null;
+        }
+
+        return builder.getKey(powerComponentId, BatteryConsumer.PROCESS_STATE_UNSPECIFIED,
+                screenState, powerState);
+    }
+
+    private static boolean areMatchingStates(int[] states,
+            @BatteryConsumer.ScreenState int screenState,
+            @BatteryConsumer.PowerState int powerState) {
+        switch (screenState) {
+            case BatteryConsumer.SCREEN_STATE_ON:
+                if (states[AggregatedPowerStatsConfig.STATE_SCREEN]
+                        != AggregatedPowerStatsConfig.SCREEN_STATE_ON) {
+                    return false;
+                }
+                break;
+            case BatteryConsumer.SCREEN_STATE_OTHER:
+                if (states[AggregatedPowerStatsConfig.STATE_SCREEN]
+                        != AggregatedPowerStatsConfig.SCREEN_STATE_OTHER) {
+                    return false;
+                }
+                break;
+        }
+
+        switch (powerState) {
+            case BatteryConsumer.POWER_STATE_BATTERY:
+                if (states[AggregatedPowerStatsConfig.STATE_POWER]
+                        != AggregatedPowerStatsConfig.POWER_STATE_BATTERY) {
+                    return false;
+                }
+                break;
+            case BatteryConsumer.POWER_STATE_OTHER:
+                if (states[AggregatedPowerStatsConfig.STATE_POWER]
+                        != AggregatedPowerStatsConfig.POWER_STATE_OTHER) {
+                    return false;
+                }
+                break;
+        }
+        return true;
+    }
+
+    public void setPowerComponentEnabled(int powerComponentId, boolean enabled) {
+        mPowerStatsAggregator.setPowerComponentEnabled(powerComponentId, enabled);
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java
new file mode 100644
index 0000000..838fc62
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/PowerStatsProcessor.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.processor;
+
+import static com.android.server.power.stats.processor.MultiStateStats.STATE_DOES_NOT_EXIST;
+import static com.android.server.power.stats.processor.MultiStateStats.States.findTrackedStateByName;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.BatteryStats;
+import android.util.Log;
+
+import com.android.internal.os.PowerStats;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/*
+ * The power estimation algorithm used by PowerStatsProcessor can roughly be
+ * described like this:
+ *
+ * 1. Estimate power usage for each state combination (e.g. power-battery/screen-on) using
+ * a metric such as CPU time-in-state.
+ *
+ * 2. Combine estimates obtain in step 1, aggregating across states that are *not* tracked
+ * per UID.
+ *
+ * 2. For each UID, compute the proportion of the combined estimates in each state
+ * and attribute the corresponding portion of the total power estimate in that state to the UID.
+ */
+abstract class PowerStatsProcessor {
+    private static final String TAG = "PowerStatsProcessor";
+
+    private static final double MILLIAMPHOUR_PER_MICROCOULOMB = 1.0 / 1000.0 / 60.0 / 60.0;
+
+    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+    }
+
+    void noteStateChange(PowerComponentAggregatedPowerStats stats,
+            BatteryStats.HistoryItem item) {
+    }
+
+    void addPowerStats(PowerComponentAggregatedPowerStats stats, PowerStats powerStats,
+            long timestampMs) {
+        stats.addProcessedPowerStats(powerStats, timestampMs);
+    }
+
+    abstract void finish(PowerComponentAggregatedPowerStats stats, long timestampMs);
+
+    protected static class PowerEstimationPlan {
+        private final AggregatedPowerStatsConfig.PowerComponent mConfig;
+        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+        public List<CombinedDeviceStateEstimate> combinedDeviceStateEstimations = new ArrayList<>();
+        public List<UidStateEstimate> uidStateEstimates = new ArrayList<>();
+
+        public PowerEstimationPlan(AggregatedPowerStatsConfig.PowerComponent config) {
+            mConfig = config;
+            addDeviceStateEstimations();
+            combineDeviceStateEstimations();
+            addUidStateEstimations();
+        }
+
+        private void addDeviceStateEstimations() {
+            MultiStateStats.States[] config = mConfig.getDeviceStateConfig();
+            int[][] deviceStateCombinations = getAllTrackedStateCombinations(config);
+            for (int[] deviceStateCombination : deviceStateCombinations) {
+                deviceStateEstimations.add(
+                        new DeviceStateEstimation(config, deviceStateCombination));
+            }
+        }
+
+        private void combineDeviceStateEstimations() {
+            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+            MultiStateStats.States[] deviceStatesTrackedPerUid =
+                    new MultiStateStats.States[deviceStateConfig.length];
+
+            for (int i = 0; i < deviceStateConfig.length; i++) {
+                if (!deviceStateConfig[i].isTracked()) {
+                    continue;
+                }
+
+                int index = findTrackedStateByName(uidStateConfig, deviceStateConfig[i].getName());
+                if (index != STATE_DOES_NOT_EXIST && uidStateConfig[index].isTracked()) {
+                    deviceStatesTrackedPerUid[i] = deviceStateConfig[i];
+                }
+            }
+
+            combineDeviceStateEstimationsRecursively(deviceStateConfig, deviceStatesTrackedPerUid,
+                    new int[deviceStateConfig.length], 0);
+        }
+
+        private void combineDeviceStateEstimationsRecursively(
+                MultiStateStats.States[] deviceStateConfig,
+                MultiStateStats.States[] deviceStatesTrackedPerUid, int[] stateValues, int state) {
+            if (state >= deviceStateConfig.length) {
+                DeviceStateEstimation dse = getDeviceStateEstimate(stateValues);
+                CombinedDeviceStateEstimate cdse = getCombinedDeviceStateEstimate(
+                        deviceStatesTrackedPerUid, stateValues);
+                if (cdse == null) {
+                    cdse = new CombinedDeviceStateEstimate(deviceStatesTrackedPerUid, stateValues);
+                    combinedDeviceStateEstimations.add(cdse);
+                }
+                cdse.deviceStateEstimations.add(dse);
+                return;
+            }
+
+            if (deviceStateConfig[state].isTracked()) {
+                for (int stateValue = 0;
+                        stateValue < deviceStateConfig[state].getLabels().length;
+                        stateValue++) {
+                    stateValues[state] = stateValue;
+                    combineDeviceStateEstimationsRecursively(deviceStateConfig,
+                            deviceStatesTrackedPerUid, stateValues, state + 1);
+                }
+            } else {
+                combineDeviceStateEstimationsRecursively(deviceStateConfig,
+                        deviceStatesTrackedPerUid, stateValues, state + 1);
+            }
+        }
+
+        private void addUidStateEstimations() {
+            MultiStateStats.States[] deviceStateConfig = mConfig.getDeviceStateConfig();
+            MultiStateStats.States[] uidStateConfig = mConfig.getUidStateConfig();
+            MultiStateStats.States[] uidStatesTrackedForDevice =
+                    new MultiStateStats.States[uidStateConfig.length];
+            MultiStateStats.States[] uidStatesNotTrackedForDevice =
+                    new MultiStateStats.States[uidStateConfig.length];
+
+            for (int i = 0; i < uidStateConfig.length; i++) {
+                if (!uidStateConfig[i].isTracked()) {
+                    continue;
+                }
+
+                int index = findTrackedStateByName(deviceStateConfig, uidStateConfig[i].getName());
+                if (index != STATE_DOES_NOT_EXIST && deviceStateConfig[index].isTracked()) {
+                    uidStatesTrackedForDevice[i] = uidStateConfig[i];
+                } else {
+                    uidStatesNotTrackedForDevice[i] = uidStateConfig[i];
+                }
+            }
+
+            @AggregatedPowerStatsConfig.TrackedState
+            int[][] uidStateCombinations = getAllTrackedStateCombinations(uidStateConfig);
+            for (int[] stateValues : uidStateCombinations) {
+                CombinedDeviceStateEstimate combined =
+                        getCombinedDeviceStateEstimate(uidStatesTrackedForDevice, stateValues);
+                if (combined == null) {
+                    // This is not supposed to be possible
+                    Log.wtf(TAG, "Mismatch in UID and combined device states: "
+                                 + concatLabels(uidStatesTrackedForDevice, stateValues));
+                    continue;
+                }
+                UidStateEstimate uidStateEstimate = getUidStateEstimate(combined);
+                if (uidStateEstimate == null) {
+                    uidStateEstimate = new UidStateEstimate(combined, uidStatesNotTrackedForDevice);
+                    uidStateEstimates.add(uidStateEstimate);
+                }
+                uidStateEstimate.proportionalEstimates.add(
+                        new UidStateProportionalEstimate(stateValues));
+            }
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Step 1. Compute device-wide power estimates for state combinations:\n");
+            for (DeviceStateEstimation deviceStateEstimation : deviceStateEstimations) {
+                sb.append("    ").append(deviceStateEstimation.id).append("\n");
+            }
+            sb.append("Step 2. Combine device-wide estimates that are untracked per UID:\n");
+            boolean any = false;
+            for (CombinedDeviceStateEstimate cdse : combinedDeviceStateEstimations) {
+                if (cdse.deviceStateEstimations.size() <= 1) {
+                    continue;
+                }
+                any = true;
+                sb.append("    ").append(cdse.id).append(": ");
+                for (int i = 0; i < cdse.deviceStateEstimations.size(); i++) {
+                    if (i != 0) {
+                        sb.append(" + ");
+                    }
+                    sb.append(cdse.deviceStateEstimations.get(i).id);
+                }
+                sb.append("\n");
+            }
+            if (!any) {
+                sb.append("    N/A\n");
+            }
+            sb.append("Step 3. Proportionally distribute power estimates to UIDs:\n");
+            for (UidStateEstimate uidStateEstimate : uidStateEstimates) {
+                sb.append("    ").append(uidStateEstimate.combinedDeviceStateEstimate.id)
+                        .append("\n        among: ");
+                for (int i = 0; i < uidStateEstimate.proportionalEstimates.size(); i++) {
+                    UidStateProportionalEstimate uspe =
+                            uidStateEstimate.proportionalEstimates.get(i);
+                    if (i != 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(concatLabels(uidStateEstimate.states, uspe.stateValues));
+                }
+                sb.append("\n");
+            }
+            return sb.toString();
+        }
+
+        @Nullable
+        public DeviceStateEstimation getDeviceStateEstimate(int[] stateValues) {
+            String label = concatLabels(mConfig.getDeviceStateConfig(), stateValues);
+            for (int i = 0; i < deviceStateEstimations.size(); i++) {
+                DeviceStateEstimation deviceStateEstimation = this.deviceStateEstimations.get(i);
+                if (deviceStateEstimation.id.equals(label)) {
+                    return deviceStateEstimation;
+                }
+            }
+            return null;
+        }
+
+        public CombinedDeviceStateEstimate getCombinedDeviceStateEstimate(
+                MultiStateStats.States[] deviceStates, int[] stateValues) {
+            String label = concatLabels(deviceStates, stateValues);
+            for (int i = 0; i < combinedDeviceStateEstimations.size(); i++) {
+                CombinedDeviceStateEstimate cdse = combinedDeviceStateEstimations.get(i);
+                if (cdse.id.equals(label)) {
+                    return cdse;
+                }
+            }
+            return null;
+        }
+
+        public UidStateEstimate getUidStateEstimate(CombinedDeviceStateEstimate combined) {
+            for (int i = 0; i < uidStateEstimates.size(); i++) {
+                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+                if (uidStateEstimate.combinedDeviceStateEstimate == combined) {
+                    return uidStateEstimate;
+                }
+            }
+            return null;
+        }
+
+        public void resetIntermediates() {
+            for (int i = deviceStateEstimations.size() - 1; i >= 0; i--) {
+                deviceStateEstimations.get(i).intermediates = null;
+            }
+            for (int i = combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+                combinedDeviceStateEstimations.get(i).intermediates = null;
+            }
+            for (int i = uidStateEstimates.size() - 1; i >= 0; i--) {
+                UidStateEstimate uidStateEstimate = uidStateEstimates.get(i);
+                List<UidStateProportionalEstimate> proportionalEstimates =
+                        uidStateEstimate.proportionalEstimates;
+                for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                    proportionalEstimates.get(j).intermediates = null;
+                }
+            }
+        }
+    }
+
+    protected static class DeviceStateEstimation {
+        public final String id;
+        public final int[] stateValues;
+        public Object intermediates;
+
+        public DeviceStateEstimation(MultiStateStats.States[] config, int[] stateValues) {
+            id = concatLabels(config, stateValues);
+            this.stateValues = stateValues;
+        }
+    }
+
+    protected static class CombinedDeviceStateEstimate {
+        public final String id;
+        public final int[] stateValues;
+        public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
+        public Object intermediates;
+
+        public CombinedDeviceStateEstimate(MultiStateStats.States[] config, int[] stateValues) {
+            this.stateValues = Arrays.copyOf(stateValues, stateValues.length);
+            id = concatLabels(config, stateValues);
+        }
+    }
+
+    protected static class UidStateEstimate {
+        public final MultiStateStats.States[] states;
+        public CombinedDeviceStateEstimate combinedDeviceStateEstimate;
+        public List<UidStateProportionalEstimate> proportionalEstimates = new ArrayList<>();
+
+        public UidStateEstimate(CombinedDeviceStateEstimate combined,
+                MultiStateStats.States[] states) {
+            combinedDeviceStateEstimate = combined;
+            this.states = states;
+        }
+    }
+
+    protected static class UidStateProportionalEstimate {
+        public final int[] stateValues;
+        public Object intermediates;
+
+        protected UidStateProportionalEstimate(int[] stateValues) {
+            this.stateValues = stateValues;
+        }
+    }
+
+    @NonNull
+    private static String concatLabels(MultiStateStats.States[] config, int[] stateValues) {
+        List<String> labels = new ArrayList<>();
+        for (int state = 0; state < config.length; state++) {
+            if (config[state] != null && config[state].isTracked()) {
+                labels.add(config[state].getName()
+                           + "=" + config[state].getLabels()[stateValues[state]]);
+            }
+        }
+        Collections.sort(labels);
+        return labels.toString();
+    }
+
+    private static int[][] getAllTrackedStateCombinations(MultiStateStats.States[] states) {
+        List<int[]> combinations = new ArrayList<>();
+        MultiStateStats.States.forEachTrackedStateCombination(states, stateValues -> {
+            combinations.add(Arrays.copyOf(stateValues, stateValues.length));
+        });
+        return combinations.toArray(new int[combinations.size()][0]);
+    }
+
+    public static double uCtoMah(long chargeUC) {
+        return chargeUC * MILLIAMPHOUR_PER_MICROCOULOMB;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
new file mode 100644
index 0000000..b295e30
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/ScreenPowerStatsProcessor.java
@@ -0,0 +1,241 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
+
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_AMBIENT;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL;
+import static com.android.internal.os.PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import android.os.BatteryStats;
+import android.util.Slog;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class ScreenPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "ScreenPowerStatsProcessor";
+    private final int mDisplayCount;
+    private final UsageBasedPowerEstimator[] mScreenOnPowerEstimators;
+    private final UsageBasedPowerEstimator[] mScreenDozePowerEstimators;
+    private final UsageBasedPowerEstimator[][] mScreenBrightnessLevelPowerEstimators;
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    private ScreenPowerStatsLayout mStatsLayout;
+    private PowerEstimationPlan mPlan;
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    private static class Intermediates {
+        public double power;
+    }
+
+    ScreenPowerStatsProcessor(PowerProfile powerProfile) {
+        mDisplayCount = powerProfile.getNumDisplays();
+        mScreenOnPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
+        mScreenDozePowerEstimators = new UsageBasedPowerEstimator[mDisplayCount];
+        mScreenBrightnessLevelPowerEstimators = new UsageBasedPowerEstimator[mDisplayCount][];
+        for (int display = 0; display < mDisplayCount; display++) {
+            mScreenOnPowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_SCREEN_ON, display));
+
+            double averagePowerFullBrightness = powerProfile.getAveragePowerForOrdinal(
+                    POWER_GROUP_DISPLAY_SCREEN_FULL, display);
+            mScreenBrightnessLevelPowerEstimators[display] =
+                    new UsageBasedPowerEstimator[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
+            for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+                // For example, if the number of bins is 3, the corresponding averages
+                // are calculated as 0.5 * full, 1.5 * full, 2.5 * full
+                final double binPowerMah = averagePowerFullBrightness * (bin + 0.5)
+                        / BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS;
+                mScreenBrightnessLevelPowerEstimators[display][bin] =
+                        new UsageBasedPowerEstimator(binPowerMah);
+            }
+
+            mScreenDozePowerEstimators[display] = new UsageBasedPowerEstimator(
+                    powerProfile.getAveragePowerForOrdinal(POWER_GROUP_DISPLAY_AMBIENT, display));
+        }
+    }
+
+    private boolean unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor == null) {
+            return false;
+        }
+
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return true;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new ScreenPowerStatsLayout(descriptor);
+        if (mStatsLayout.getDisplayCount() != mDisplayCount) {
+            Slog.e(TAG, "Incompatible number of displays: " + mStatsLayout.getDisplayCount()
+                    + ", expected: " + mDisplayCount);
+            return false;
+        }
+
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+        return true;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor())) {
+            return;
+        }
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        computeDevicePowerEstimates(stats);
+        combineDeviceStateEstimates();
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        if (!uids.isEmpty()) {
+            computeUidPowerEstimates(stats, uids);
+        }
+        mPlan.resetIntermediates();
+    }
+
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            if (estimation.stateValues[STATE_SCREEN] == SCREEN_STATE_ON) {
+                double power;
+                if (mStatsLayout.getEnergyConsumerCount() > 0) {
+                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+                } else {
+                    power = 0;
+                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
+                        power += computeDisplayPower(mTmpDeviceStatsArray, display);
+                    }
+                }
+                mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+                Intermediates intermediates = new Intermediates();
+                intermediates.power = power;
+                estimation.intermediates = intermediates;
+            } else {
+                double power = 0;
+                if (mStatsLayout.getEnergyConsumerCount() > 0) {
+                    power = uCtoMah(mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, 0));
+                } else {
+                    for (int display = 0; display < mStatsLayout.getDisplayCount(); display++) {
+                        power += mScreenDozePowerEstimators[display].calculatePower(
+                                mStatsLayout.getScreenDozeDuration(mTmpDeviceStatsArray, display));
+                    }
+                }
+                mStatsLayout.setScreenDozePowerEstimate(mTmpDeviceStatsArray, power);
+            }
+
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+
+    private double computeDisplayPower(long[] stats, int display) {
+        double power = mScreenOnPowerEstimators[display]
+                .calculatePower(mStatsLayout.getScreenOnDuration(stats, display));
+        for (int bin = 0; bin < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; bin++) {
+            power += mScreenBrightnessLevelPowerEstimators[display][bin]
+                    .calculatePower(mStatsLayout.getBrightnessLevelDuration(stats, display, bin));
+        }
+        return power;
+    }
+
+    /**
+     * Combine power estimates before distributing them proportionally to UIDs.
+     */
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            double power = 0;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                Intermediates intermediates = (Intermediates) dse.intermediates;
+                if (intermediates != null) {
+                    power += intermediates.power;
+                }
+            }
+            if (power != 0) {
+                Intermediates cdseIntermediates = new Intermediates();
+                cdseIntermediates.power = power;
+                cdse.intermediates = cdseIntermediates;
+            }
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        int[] uidStateValues = new int[stats.getConfig().getUidStateConfig().length];
+        uidStateValues[STATE_SCREEN] = SCREEN_STATE_ON;
+        uidStateValues[STATE_PROCESS_STATE] = PROCESS_STATE_UNSPECIFIED;
+
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            Intermediates intermediates =
+                    (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+            int[] deviceStateValues = uidStateEstimate.combinedDeviceStateEstimate
+                    .stateValues;
+            if (deviceStateValues[STATE_SCREEN] != SCREEN_STATE_ON
+                    || intermediates == null) {
+                continue;
+            }
+
+            uidStateValues[STATE_POWER] = deviceStateValues[STATE_POWER];
+
+            long totalTopActivityDuration = 0;
+            for (int j = uids.size() - 1; j >= 0; j--) {
+                int uid = uids.get(j);
+                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
+                    totalTopActivityDuration +=
+                            mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
+                }
+            }
+
+            if (totalTopActivityDuration == 0) {
+                return;
+            }
+
+            for (int j = uids.size() - 1; j >= 0; j--) {
+                int uid = uids.get(j);
+                if (stats.getUidStats(mTmpUidStatsArray, uid, uidStateValues)) {
+                    long duration = mStatsLayout.getUidTopActivityDuration(mTmpUidStatsArray);
+                    double power = intermediates.power * duration / totalTopActivityDuration;
+                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+                    stats.setUidStats(uid, uidStateValues, mTmpUidStatsArray);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
new file mode 100644
index 0000000..67013ea
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
@@ -0,0 +1,310 @@
+/*
+ * 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.processor;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.PowerStatsLayout;
+import com.android.server.power.stats.format.SensorPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+class SensorPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "SensorPowerStatsProcessor";
+    private static final String ANDROID_SENSOR_TYPE_PREFIX = "android.sensor.";
+
+    private static final double MILLIS_IN_HOUR = 1000.0 * 60 * 60;
+    private static final String SENSOR_EVENT_TAG_PREFIX = "sensor:0x";
+    private final Supplier<SensorManager> mSensorManagerSupplier;
+
+    private static final long INITIAL_TIMESTAMP = -1;
+    private SensorManager mSensorManager;
+    private SensorPowerStatsLayout mStatsLayout;
+    private PowerStats mPowerStats;
+    private boolean mIsInitialized;
+    private PowerStats.Descriptor mDescriptor;
+    private long mLastUpdateTimestamp;
+    private PowerEstimationPlan mPlan;
+
+    private static class SensorState {
+        public int sensorHandle;
+        public boolean stateOn;
+        public int uid;
+        public long startTime = INITIAL_TIMESTAMP;
+    }
+
+    private static class Intermediates {
+        public double power;
+    }
+
+    private final SparseArray<SensorState> mSensorStates = new SparseArray<>();
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+
+    SensorPowerStatsProcessor(Supplier<SensorManager> sensorManagerSupplier) {
+        mSensorManagerSupplier = sensorManagerSupplier;
+    }
+
+    private boolean ensureInitialized() {
+        if (mIsInitialized) {
+            return true;
+        }
+
+        mSensorManager = mSensorManagerSupplier.get();
+        if (mSensorManager == null) {
+            return false;
+        }
+
+        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        mStatsLayout = new SensorPowerStatsLayout(sensorList.stream().collect(
+                Collectors.toMap(Sensor::getHandle, sensor -> makeLabel(sensor, sensorList))));
+
+        PersistableBundle extras = new PersistableBundle();
+        mStatsLayout.toExtras(extras);
+        mDescriptor = new PowerStats.Descriptor(
+                BatteryConsumer.POWER_COMPONENT_SENSORS, mStatsLayout.getDeviceStatsArrayLength(),
+                null, 0, mStatsLayout.getUidStatsArrayLength(),
+                extras);
+
+        mPowerStats = new PowerStats(mDescriptor);
+        mTmpUidStatsArray = new long[mDescriptor.uidStatsArrayLength];
+        mTmpDeviceStatsArray = new long[mDescriptor.statsArrayLength];
+
+        mIsInitialized = true;
+        return true;
+    }
+
+    private String makeLabel(Sensor sensor, List<Sensor> sensorList) {
+        int type = sensor.getType();
+        String label = sensor.getStringType();
+
+        boolean isSingleton = true;
+        for (int i = sensorList.size() - 1; i >= 0; i--) {
+            Sensor s = sensorList.get(i);
+            if (s == sensor) {
+                continue;
+            }
+            if (s.getType() == type) {
+                isSingleton = false;
+                break;
+            }
+        }
+        if (!isSingleton) {
+            StringBuilder sb = new StringBuilder(label).append('.');
+            if (sensor.getId() > 0) { // 0 and -1 are reserved
+                sb.append(sensor.getId());
+            } else {
+                sb.append(sensor.getName());
+            }
+            label = sb.toString();
+        }
+        if (label.startsWith(ANDROID_SENSOR_TYPE_PREFIX)) {
+            label = label.substring(ANDROID_SENSOR_TYPE_PREFIX.length());
+        }
+        return label.replace(' ', '_');
+    }
+
+    @Override
+    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!ensureInitialized()) {
+            return;
+        }
+
+        // Establish a baseline at the beginning of an accumulation pass
+        mLastUpdateTimestamp = timestampMs;
+        flushPowerStats(stats, timestampMs);
+    }
+
+    @Override
+    void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
+        if (!mIsInitialized) {
+            return;
+        }
+
+        if (item.eventTag == null || item.eventTag.string == null
+                || !item.eventTag.string.startsWith(SENSOR_EVENT_TAG_PREFIX)) {
+            return;
+        }
+
+        int sensorHandle;
+        try {
+            sensorHandle = Integer.parseInt(item.eventTag.string, SENSOR_EVENT_TAG_PREFIX.length(),
+                    item.eventTag.string.length(), 16);
+        } catch (NumberFormatException e) {
+            Slog.wtf(TAG, "Bad format of event tag: " + item.eventTag.string);
+            return;
+        }
+
+        SensorState sensor = mSensorStates.get(sensorHandle);
+        if (sensor == null) {
+            sensor = new SensorState();
+            sensor.sensorHandle = sensorHandle;
+            mSensorStates.put(sensorHandle, sensor);
+        }
+
+        int uid = item.eventTag.uid;
+        boolean sensorOn = (item.states & BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG) != 0;
+        if (sensorOn) {
+            if (!sensor.stateOn) {
+                sensor.stateOn = true;
+                sensor.uid = uid;
+                sensor.startTime = item.time;
+            } else if (sensor.uid != uid) {
+                recordUsageDuration(sensor, item.time);
+                sensor.uid = uid;
+            }
+        } else {
+            if (sensor.stateOn) {
+                recordUsageDuration(sensor, item.time);
+                sensor.stateOn = false;
+            }
+        }
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (!mIsInitialized) {
+            return;
+        }
+
+        for (int i = mSensorStates.size() - 1; i >= 0; i--) {
+            SensorState sensor = mSensorStates.valueAt(i);
+            if (sensor.stateOn) {
+                recordUsageDuration(sensor, timestampMs);
+            }
+        }
+        flushPowerStats(stats, timestampMs);
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        List<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+
+        computeUidPowerEstimates(stats, uids);
+        computeDevicePowerEstimates(stats);
+
+        mPlan.resetIntermediates();
+    }
+
+    protected void recordUsageDuration(SensorState sensorState, long time) {
+        long durationMs = Math.max(0, time - sensorState.startTime);
+        if (durationMs > 0) {
+            long[] uidStats = mPowerStats.uidStats.get(sensorState.uid);
+            if (uidStats == null) {
+                uidStats = new long[mDescriptor.uidStatsArrayLength];
+                mPowerStats.uidStats.put(sensorState.uid, uidStats);
+            }
+            mStatsLayout.addUidSensorDuration(uidStats, sensorState.sensorHandle, durationMs);
+        }
+        sensorState.startTime = time;
+    }
+
+    private void flushPowerStats(
+            PowerComponentAggregatedPowerStats stats, long timestamp) {
+        mPowerStats.durationMs = timestamp - mLastUpdateTimestamp;
+        stats.addProcessedPowerStats(mPowerStats, timestamp);
+
+        Arrays.fill(mPowerStats.stats, 0);
+        mPowerStats.uidStats.clear();
+        mLastUpdateTimestamp = timestamp;
+    }
+
+    private void computeUidPowerEstimates(
+            PowerComponentAggregatedPowerStats stats,
+            List<Integer> uids) {
+        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        int[] uidSensorDurationPositions = new int[sensorList.size()];
+        double[] sensorPower = new double[sensorList.size()];
+        for (int i = sensorList.size() - 1; i >= 0; i--) {
+            Sensor sensor = sensorList.get(i);
+            uidSensorDurationPositions[i] =
+                    mStatsLayout.getUidSensorDurationPosition(sensor.getHandle());
+            sensorPower[i] = sensor.getPower() / MILLIS_IN_HOUR;
+        }
+
+        for (int i = mPlan.uidStateEstimates.size() - 1; i >= 0; i--) {
+            UidStateEstimate uidStateEstimate = mPlan.uidStateEstimates.get(i);
+            List<UidStateProportionalEstimate> proportionalEstimates =
+                    uidStateEstimate.proportionalEstimates;
+            for (int j = proportionalEstimates.size() - 1; j >= 0; j--) {
+                UidStateProportionalEstimate proportionalEstimate = proportionalEstimates.get(j);
+                for (int k = uids.size() - 1; k >= 0; k--) {
+                    int uid = uids.get(k);
+                    if (!stats.getUidStats(mTmpUidStatsArray, uid,
+                            proportionalEstimate.stateValues)) {
+                        continue;
+                    }
+                    double power = 0;
+                    for (int m = 0; m < uidSensorDurationPositions.length; m++) {
+                        int position = uidSensorDurationPositions[m];
+                        if (position == PowerStatsLayout.UNSUPPORTED
+                                || mTmpUidStatsArray[position] == 0) {
+                            continue;
+                        }
+                        power += sensorPower[m] * mTmpUidStatsArray[position];
+                    }
+                    if (power == 0) {
+                        continue;
+                    }
+
+                    mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+                    stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+                    Intermediates intermediates = (Intermediates) uidStateEstimate
+                            .combinedDeviceStateEstimate.intermediates;
+                    if (intermediates == null) {
+                        intermediates = new Intermediates();
+                        uidStateEstimate.combinedDeviceStateEstimate.intermediates = intermediates;
+                    }
+                    intermediates.power += power;
+                }
+            }
+        }
+    }
+
+    private void computeDevicePowerEstimates(
+            PowerComponentAggregatedPowerStats stats) {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate estimation =
+                    mPlan.combinedDeviceStateEstimations.get(i);
+            if (estimation.intermediates == null) {
+                continue;
+            }
+
+            if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+                continue;
+            }
+
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                    ((Intermediates) estimation.intermediates).power);
+            stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java
new file mode 100644
index 0000000..a6c3807
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/VideoPowerStatsProcessor.java
@@ -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.server.power.stats.processor;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.PowerStatsUidResolver;
+
+class VideoPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+    VideoPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
+        super(BatteryConsumer.POWER_COMPONENT_VIDEO, uidResolver,
+                powerProfile.getAveragePower(PowerProfile.POWER_VIDEO));
+    }
+
+    @Override
+    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
+        return (item.states2 & BatteryStats.HistoryItem.STATE2_VIDEO_ON_FLAG) != 0
+                ? STATE_ON
+                : STATE_OFF;
+    }
+}
diff --git a/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
new file mode 100644
index 0000000..0df01cf
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/processor/WifiPowerStatsProcessor.java
@@ -0,0 +1,394 @@
+/*
+ * 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.processor;
+
+import android.util.Slog;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.UsageBasedPowerEstimator;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class WifiPowerStatsProcessor extends PowerStatsProcessor {
+    private static final String TAG = "WifiPowerStatsProcessor";
+    private static final boolean DEBUG = false;
+
+    private final UsageBasedPowerEstimator mRxPowerEstimator;
+    private final UsageBasedPowerEstimator mTxPowerEstimator;
+    private final UsageBasedPowerEstimator mIdlePowerEstimator;
+
+    private final UsageBasedPowerEstimator mActivePowerEstimator;
+    private final UsageBasedPowerEstimator mScanPowerEstimator;
+    private final UsageBasedPowerEstimator mBatchedScanPowerEstimator;
+
+    private PowerStats.Descriptor mLastUsedDescriptor;
+    private WifiPowerStatsLayout mStatsLayout;
+    // Sequence of steps for power estimation and intermediate results.
+    private PowerEstimationPlan mPlan;
+
+    private long[] mTmpDeviceStatsArray;
+    private long[] mTmpUidStatsArray;
+    private boolean mHasWifiPowerController;
+
+    WifiPowerStatsProcessor(PowerProfile powerProfile) {
+        mRxPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX));
+        mTxPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX));
+        mIdlePowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE));
+        mActivePowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_ACTIVE));
+        mScanPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_SCAN));
+        mBatchedScanPowerEstimator = new UsageBasedPowerEstimator(
+                powerProfile.getAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN));
+    }
+
+    private static class Intermediates {
+        /**
+         * Estimated power for the RX state.
+         */
+        public double rxPower;
+        /**
+         * Estimated power for the TX state.
+         */
+        public double txPower;
+        /**
+         * Estimated power in the SCAN state
+         */
+        public double scanPower;
+        /**
+         * Estimated power for IDLE, SCAN states.
+         */
+        public double idlePower;
+        /**
+         * Number of received packets
+         */
+        public long rxPackets;
+        /**
+         * Number of transmitted packets
+         */
+        public long txPackets;
+        /**
+         * Total duration of unbatched scans across all UIDs.
+         */
+        public long basicScanDuration;
+        /**
+         * Estimated power in the unbatched SCAN state
+         */
+        public double basicScanPower;
+        /**
+         * Total duration of batched scans across all UIDs.
+         */
+        public long batchedScanDuration;
+        /**
+         * Estimated power in the BATCHED SCAN state
+         */
+        public double batchedScanPower;
+        /**
+         * Estimated total power when active; used only in the absence of WiFiManager power
+         * reporting.
+         */
+        public double activePower;
+        /**
+         * Measured consumed energy from power monitoring hardware (micro-coulombs)
+         */
+        public long consumedEnergy;
+    }
+
+    @Override
+    void finish(PowerComponentAggregatedPowerStats stats, long timestampMs) {
+        if (stats.getPowerStatsDescriptor() == null) {
+            return;
+        }
+
+        unpackPowerStatsDescriptor(stats.getPowerStatsDescriptor());
+
+        if (mPlan == null) {
+            mPlan = new PowerEstimationPlan(stats.getConfig());
+        }
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+            Intermediates intermediates = new Intermediates();
+            estimation.intermediates = intermediates;
+            computeDevicePowerEstimates(stats, estimation.stateValues, intermediates);
+        }
+
+        double ratio = 1.0;
+        if (mStatsLayout.getEnergyConsumerCount() != 0) {
+            ratio = computeEstimateAdjustmentRatioUsingConsumedEnergy();
+            if (ratio != 1) {
+                for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+                    DeviceStateEstimation estimation = mPlan.deviceStateEstimations.get(i);
+                    adjustDevicePowerEstimates(stats, estimation.stateValues,
+                            (Intermediates) estimation.intermediates, ratio);
+                }
+            }
+        }
+
+        combineDeviceStateEstimates();
+
+        ArrayList<Integer> uids = new ArrayList<>();
+        stats.collectUids(uids);
+        if (!uids.isEmpty()) {
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidActivityTotals(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+
+            for (int uid : uids) {
+                for (int i = 0; i < mPlan.uidStateEstimates.size(); i++) {
+                    computeUidPowerEstimates(stats, uid, mPlan.uidStateEstimates.get(i));
+                }
+            }
+        }
+        mPlan.resetIntermediates();
+    }
+
+    private void unpackPowerStatsDescriptor(PowerStats.Descriptor descriptor) {
+        if (descriptor.equals(mLastUsedDescriptor)) {
+            return;
+        }
+
+        mLastUsedDescriptor = descriptor;
+        mStatsLayout = new WifiPowerStatsLayout(descriptor);
+        mTmpDeviceStatsArray = new long[descriptor.statsArrayLength];
+        mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
+        mHasWifiPowerController = mStatsLayout.isPowerReportingSupported();
+    }
+
+    /**
+     * Compute power estimates using the power profile.
+     */
+    private void computeDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates) {
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        for (int i = mStatsLayout.getEnergyConsumerCount() - 1; i >= 0; i--) {
+            intermediates.consumedEnergy += mStatsLayout.getConsumedEnergy(mTmpDeviceStatsArray, i);
+        }
+
+        intermediates.basicScanDuration =
+                mStatsLayout.getDeviceBasicScanTime(mTmpDeviceStatsArray);
+        intermediates.batchedScanDuration =
+                mStatsLayout.getDeviceBatchedScanTime(mTmpDeviceStatsArray);
+        if (mHasWifiPowerController) {
+            intermediates.rxPower = mRxPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceRxTime(mTmpDeviceStatsArray));
+            intermediates.txPower = mTxPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceTxTime(mTmpDeviceStatsArray));
+            intermediates.scanPower = mScanPowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceScanTime(mTmpDeviceStatsArray));
+            intermediates.idlePower = mIdlePowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceIdleTime(mTmpDeviceStatsArray));
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                    intermediates.rxPower + intermediates.txPower + intermediates.scanPower
+                            + intermediates.idlePower);
+        } else {
+            intermediates.activePower = mActivePowerEstimator.calculatePower(
+                    mStatsLayout.getDeviceActiveTime(mTmpDeviceStatsArray));
+            intermediates.basicScanPower =
+                    mScanPowerEstimator.calculatePower(intermediates.basicScanDuration);
+            intermediates.batchedScanPower =
+                    mBatchedScanPowerEstimator.calculatePower(intermediates.batchedScanDuration);
+            mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
+                    intermediates.activePower + intermediates.basicScanPower
+                            + intermediates.batchedScanPower);
+        }
+
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * Compute an adjustment ratio using the total power estimated using the power profile
+     * and the total power measured by hardware.
+     */
+    private double computeEstimateAdjustmentRatioUsingConsumedEnergy() {
+        long totalConsumedEnergy = 0;
+        double totalPower = 0;
+
+        for (int i = mPlan.deviceStateEstimations.size() - 1; i >= 0; i--) {
+            Intermediates intermediates =
+                    (Intermediates) mPlan.deviceStateEstimations.get(i).intermediates;
+            if (mHasWifiPowerController) {
+                totalPower += intermediates.rxPower + intermediates.txPower
+                        + intermediates.scanPower + intermediates.idlePower;
+            } else {
+                totalPower += intermediates.activePower + intermediates.basicScanPower
+                        + intermediates.batchedScanPower;
+            }
+            totalConsumedEnergy += intermediates.consumedEnergy;
+        }
+
+        if (totalPower == 0) {
+            return 1;
+        }
+
+        return uCtoMah(totalConsumedEnergy) / totalPower;
+    }
+
+    /**
+     * Uniformly apply the same adjustment to all power estimates in order to ensure that the total
+     * estimated power matches the measured consumed power.  We are not claiming that all
+     * averages captured in the power profile have to be off by the same percentage in reality.
+     */
+    private void adjustDevicePowerEstimates(PowerComponentAggregatedPowerStats stats,
+            int[] deviceStates, Intermediates intermediates, double ratio) {
+        double adjutedPower;
+        if (mHasWifiPowerController) {
+            intermediates.rxPower *= ratio;
+            intermediates.txPower *= ratio;
+            intermediates.scanPower *= ratio;
+            intermediates.idlePower *= ratio;
+            adjutedPower = intermediates.rxPower + intermediates.txPower + intermediates.scanPower
+                    + intermediates.idlePower;
+        } else {
+            intermediates.activePower *= ratio;
+            intermediates.basicScanPower *= ratio;
+            intermediates.batchedScanPower *= ratio;
+            adjutedPower = intermediates.activePower + intermediates.basicScanPower
+                    + intermediates.batchedScanPower;
+        }
+
+        if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStates)) {
+            return;
+        }
+
+        mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, adjutedPower);
+        stats.setDeviceStats(deviceStates, mTmpDeviceStatsArray);
+    }
+
+    /**
+     * Combine power estimates before distributing them proportionally to UIDs.
+     */
+    private void combineDeviceStateEstimates() {
+        for (int i = mPlan.combinedDeviceStateEstimations.size() - 1; i >= 0; i--) {
+            CombinedDeviceStateEstimate cdse = mPlan.combinedDeviceStateEstimations.get(i);
+            Intermediates
+                    cdseIntermediates = new Intermediates();
+            cdse.intermediates = cdseIntermediates;
+            List<DeviceStateEstimation> deviceStateEstimations = cdse.deviceStateEstimations;
+            for (int j = deviceStateEstimations.size() - 1; j >= 0; j--) {
+                DeviceStateEstimation dse = deviceStateEstimations.get(j);
+                Intermediates intermediates = (Intermediates) dse.intermediates;
+                if (mHasWifiPowerController) {
+                    cdseIntermediates.rxPower += intermediates.rxPower;
+                    cdseIntermediates.txPower += intermediates.txPower;
+                    cdseIntermediates.scanPower += intermediates.scanPower;
+                    cdseIntermediates.idlePower += intermediates.idlePower;
+                } else {
+                    cdseIntermediates.activePower += intermediates.activePower;
+                    cdseIntermediates.basicScanPower += intermediates.basicScanPower;
+                    cdseIntermediates.batchedScanPower += intermediates.batchedScanPower;
+                }
+                cdseIntermediates.basicScanDuration += intermediates.basicScanDuration;
+                cdseIntermediates.batchedScanDuration += intermediates.batchedScanDuration;
+                cdseIntermediates.consumedEnergy += intermediates.consumedEnergy;
+            }
+        }
+    }
+
+    private void computeUidActivityTotals(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            intermediates.rxPackets += mStatsLayout.getUidRxPackets(mTmpUidStatsArray);
+            intermediates.txPackets += mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
+        }
+    }
+
+    private void computeUidPowerEstimates(PowerComponentAggregatedPowerStats stats, int uid,
+            UidStateEstimate uidStateEstimate) {
+        Intermediates intermediates =
+                (Intermediates) uidStateEstimate.combinedDeviceStateEstimate.intermediates;
+        for (UidStateProportionalEstimate proportionalEstimate :
+                uidStateEstimate.proportionalEstimates) {
+            if (!stats.getUidStats(mTmpUidStatsArray, uid, proportionalEstimate.stateValues)) {
+                continue;
+            }
+
+            double power = 0;
+            if (mHasWifiPowerController) {
+                if (intermediates.rxPackets != 0) {
+                    power += intermediates.rxPower * mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+                            / intermediates.rxPackets;
+                }
+                if (intermediates.txPackets != 0) {
+                    power += intermediates.txPower * mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+                            / intermediates.txPackets;
+                }
+                long totalScanDuration =
+                        intermediates.basicScanDuration + intermediates.batchedScanDuration;
+                if (totalScanDuration != 0) {
+                    long scanDuration = mStatsLayout.getUidScanTime(mTmpUidStatsArray)
+                            + mStatsLayout.getUidBatchedScanTime(mTmpUidStatsArray);
+                    power += intermediates.scanPower * scanDuration / totalScanDuration;
+                }
+            } else {
+                long totalPackets = intermediates.rxPackets + intermediates.txPackets;
+                if (totalPackets != 0) {
+                    long packets = mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+                            + mStatsLayout.getUidTxPackets(mTmpUidStatsArray);
+                    power += intermediates.activePower * packets / totalPackets;
+                }
+
+                if (intermediates.basicScanDuration != 0) {
+                    long scanDuration = mStatsLayout.getUidScanTime(mTmpUidStatsArray);
+                    power += intermediates.basicScanPower * scanDuration
+                            / intermediates.basicScanDuration;
+                }
+
+                if (intermediates.batchedScanDuration != 0) {
+                    long batchedScanDuration = mStatsLayout.getUidBatchedScanTime(
+                            mTmpUidStatsArray);
+                    power += intermediates.batchedScanPower * batchedScanDuration
+                            / intermediates.batchedScanDuration;
+                }
+            }
+            mStatsLayout.setUidPowerEstimate(mTmpUidStatsArray, power);
+            stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
+
+            if (DEBUG) {
+                Slog.d(TAG, "UID: " + uid
+                        + " states: " + Arrays.toString(proportionalEstimate.stateValues)
+                        + " stats: " + Arrays.toString(mTmpUidStatsArray)
+                        + " rx: " + mStatsLayout.getUidRxPackets(mTmpUidStatsArray)
+                        + " rx-power: " + intermediates.rxPower
+                        + " rx-packets: " + intermediates.rxPackets
+                        + " tx: " + mStatsLayout.getUidTxPackets(mTmpUidStatsArray)
+                        + " tx-power: " + intermediates.txPower
+                        + " tx-packets: " + intermediates.txPackets
+                        + " power: " + power);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 8f99d28..89fa9b6 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -62,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 
@@ -207,19 +208,28 @@
     private final IBinder mService = new IPowerStatsService.Stub() {
 
         @Override
-        public void getSupportedPowerMonitors(ResultReceiver resultReceiver) {
+        public void getSupportedPowerMonitors(@NonNull ResultReceiver resultReceiver) {
+            if (Flags.verifyNonNullArguments()) {
+                Objects.requireNonNull(resultReceiver);
+            }
             getHandler().post(() -> getSupportedPowerMonitorsImpl(resultReceiver));
         }
 
         @Override
-        public void getPowerMonitorReadings(int[] powerMonitorIds, ResultReceiver resultReceiver) {
+        public void getPowerMonitorReadings(@NonNull int[] powerMonitorIds,
+                @NonNull ResultReceiver resultReceiver) {
+            if (Flags.verifyNonNullArguments()) {
+                Objects.requireNonNull(powerMonitorIds);
+                Objects.requireNonNull(resultReceiver);
+            }
             int callingUid = Binder.getCallingUid();
             getHandler().post(() ->
                     getPowerMonitorReadingsImpl(powerMonitorIds, resultReceiver, callingUid));
         }
 
         @Override
-        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+                @Nullable String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
             if (mPowerStatsLogger == null) {
@@ -263,6 +273,11 @@
         }
     };
 
+    @VisibleForTesting
+    IPowerStatsService getIPowerStatsServiceForTest() {
+        return (IPowerStatsService) mService;
+    }
+
     private class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
         public Executor mExecutor = new HandlerExecutor(getHandler());
 
@@ -289,12 +304,22 @@
     @Override
     public void onStart() {
         if (getPowerStatsHal().isInitialized()) {
-            mPowerStatsInternal = new LocalService();
-            publishLocalService(PowerStatsInternal.class, mPowerStatsInternal);
+            publishLocalService(PowerStatsInternal.class, getPowerStatsInternal());
         }
         publishBinderService(Context.POWER_STATS_SERVICE, mService);
     }
 
+    /**
+     * Returns the PowerStatsInternal associated with this service, maybe creating it if needed.
+     */
+    @VisibleForTesting
+    public PowerStatsInternal getPowerStatsInternal() {
+        if (mPowerStatsInternal == null) {
+            mPowerStatsInternal = new LocalService();
+        }
+        return mPowerStatsInternal;
+    }
+
     private void onSystemServicesReady() {
         mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, mPowerStatsInternal);
         mDeviceConfigListener.startListening();
@@ -441,7 +466,13 @@
 
     private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
             int[] energyConsumerIds) {
-        EnergyConsumerResult[] results = getPowerStatsHal().getEnergyConsumed(energyConsumerIds);
+        EnergyConsumerResult[] results;
+        try {
+            results = getPowerStatsHal().getEnergyConsumed(energyConsumerIds);
+        } catch (Exception e) {
+            future.completeExceptionally(e);
+            return;
+        }
 
         // STOPSHIP(253292374): Remove once missing EnergyConsumer results issue is resolved.
         EnergyConsumer[] energyConsumers = getEnergyConsumerInfo();
@@ -508,12 +539,20 @@
 
     private void getStateResidencyAsync(CompletableFuture<StateResidencyResult[]> future,
             int[] powerEntityIds) {
-        future.complete(getPowerStatsHal().getStateResidency(powerEntityIds));
+        try {
+            future.complete(getPowerStatsHal().getStateResidency(powerEntityIds));
+        } catch (Exception e) {
+            future.completeExceptionally(e);
+        }
     }
 
     private void readEnergyMeterAsync(CompletableFuture<EnergyMeasurement[]> future,
             int[] channelIds) {
-        future.complete(getPowerStatsHal().readEnergyMeter(channelIds));
+        try {
+            future.complete(getPowerStatsHal().readEnergyMeter(channelIds));
+        } catch (Exception e) {
+            future.completeExceptionally(e);
+        }
     }
 
     private static class PowerMonitorState {
diff --git a/services/core/java/com/android/server/powerstats/TEST_MAPPING b/services/core/java/com/android/server/powerstats/TEST_MAPPING
index 79224a5..0ba1da9a95 100644
--- a/services/core/java/com/android/server/powerstats/TEST_MAPPING
+++ b/services/core/java/com/android/server/powerstats/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.powerstats"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_powerstats"
     }
   ]
 }
diff --git a/services/core/java/com/android/server/powerstats/flags.aconfig b/services/core/java/com/android/server/powerstats/flags.aconfig
index 0a4a751..29ad7dc 100644
--- a/services/core/java/com/android/server/powerstats/flags.aconfig
+++ b/services/core/java/com/android/server/powerstats/flags.aconfig
@@ -10,4 +10,15 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
+}
+
+flag {
+    name: "verify_non_null_arguments"
+    namespace: "backstage_power"
+    description: "Verify arguments passed are non-null"
+    bug: "356731520"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/rollback/ApexdRevertLogger.java b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
new file mode 100644
index 0000000..9950cc7
--- /dev/null
+++ b/services/core/java/com/android/server/rollback/ApexdRevertLogger.java
@@ -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.rollback;
+
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
+import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
+
+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.os.SystemProperties;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class handles the logic for logging Apexd-triggered rollback events.
+ * TODO: b/354112511 Refactor to have a separate metric for ApexdReverts
+ */
+public final class ApexdRevertLogger {
+    private static final String TAG = "WatchdogRollbackLogger";
+
+    private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+
+    /**
+     * Logs that one or more apexd reverts have occurred, along with the crashing native process
+     * that caused apexd to revert during boot.
+     *
+     * @param context the context to use when determining the log packages
+     * @param failedPackageNames a list of names of packages which were reverted
+     * @param failingNativeProcess the crashing native process which caused a revert
+     */
+    public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
+            @NonNull String failingNativeProcess) {
+        Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
+        for (VersionedPackage logPackage: logPackages) {
+            logEvent(logPackage,
+                    failingNativeProcess);
+        }
+    }
+
+    /**
+     * Gets the set of parent packages for a given set of failed package names. In the case that
+     * multiple sessions have failed, we want to log failure for each of the parent packages.
+     * Even if multiple failed packages have the same parent, we only log the parent package once.
+     */
+    private static Set<VersionedPackage> getLogPackages(Context context,
+            @NonNull List<String> failedPackageNames) {
+        Set<VersionedPackage> parentPackages = new ArraySet<>();
+        for (String failedPackageName: failedPackageNames) {
+            parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
+        }
+        return parentPackages;
+    }
+
+    /**
+     * Returns the logging parent of a given package if it exists, {@code null} otherwise.
+     *
+     * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the
+     * metadata of a package's AndroidManifest.xml.
+     */
+    @VisibleForTesting
+    @Nullable
+    private static VersionedPackage getLogPackage(Context context,
+            @NonNull VersionedPackage failingPackage) {
+        String logPackageName;
+        VersionedPackage loggingParent;
+        logPackageName = getLoggingParentName(context, failingPackage.getPackageName());
+        if (logPackageName == null) {
+            return null;
+        }
+        try {
+            loggingParent = new VersionedPackage(logPackageName, context.getPackageManager()
+                    .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode());
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+        return loggingParent;
+    }
+
+    @Nullable
+    private static String getLoggingParentName(Context context, @NonNull String packageName) {
+        PackageManager packageManager = context.getPackageManager();
+        try {
+            int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+            ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
+            if (ai == null || ai.metaData == null) {
+                return null;
+            }
+            return ai.metaData.getString(LOGGING_PARENT_KEY);
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e);
+            return null;
+        }
+    }
+
+    /**
+     * Log a Apexd rollback event to statsd.
+     *
+     * @param logPackage         the package to associate the rollback with.
+     * @param failingPackageName the failing package or process which triggered the rollback.
+     */
+    private static void logEvent(@Nullable VersionedPackage logPackage,
+            @NonNull String failingPackageName) {
+        Slog.i(TAG, "Watchdog event occurred with type: ROLLBACK_SUCCESS"
+                + " logPackage: " + logPackage
+                + " rollbackReason: REASON_NATIVE_CRASH_DURING_BOOT"
+                + " failedPackageName: " + failingPackageName);
+        CrashRecoveryStatsLog.write(
+                WATCHDOG_ROLLBACK_OCCURRED,
+                WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                (logPackage != null) ? logPackage.getPackageName() : "",
+                (logPackage != null) ? logPackage.getVersionCode() : 0,
+                WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
+                failingPackageName,
+                new byte[]{});
+
+        logTestProperties(logPackage, failingPackageName);
+    }
+
+    /**
+     * Writes properties which will be used by rollback tests to check if rollback has occurred
+     * have occurred.
+     *
+     * persist.sys.rollbacktest.enabled: true if rollback tests are running
+     * persist.sys.rollbacktest.ROLLBACK_SUCCESS.logPackage: the package to associate the rollback
+     * persist.sys.rollbacktest.ROLLBACK_SUCCESS.rollbackReason: the reason Apexd triggered it
+     * persist.sys.rollbacktest.ROLLBACK_SUCCESS.failedPackageName: the failing package or process
+     * which triggered the rollback
+     */
+    private static void logTestProperties(@Nullable VersionedPackage logPackage,
+            @NonNull String failingPackageName) {
+        // This property should be on only during the tests
+        final String prefix = "persist.sys.rollbacktest.";
+        if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+            return;
+        }
+        String key = prefix +  "ROLLBACK_SUCCESS";
+        SystemProperties.set(key, String.valueOf(true));
+        SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
+        SystemProperties.set(key + ".rollbackReason", "REASON_NATIVE_CRASH_DURING_BOOT");
+        SystemProperties.set(key + ".failedPackageName", failingPackageName);
+    }
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 4f28e02..68026ea 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -16,6 +16,10 @@
 
 package com.android.server.rollback;
 
+import static android.content.pm.Flags.provideInfoOfApkInApex;
+
+import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent;
+
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -23,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 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;
@@ -37,6 +42,7 @@
 import android.os.SystemProperties;
 import android.sysprop.CrashRecoveryProperties;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -230,7 +236,7 @@
 
 
     @Override
-    public String getName() {
+    public String getUniqueIdentifier() {
         return NAME;
     }
 
@@ -486,19 +492,36 @@
      */
     @AnyThread
     private boolean isModule(String packageName) {
-        // Check if the package is an APK inside an APEX. If it is, use the parent APEX package when
-        // querying PackageManager.
-        String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage(
-                packageName);
-        if (apexPackageName != null) {
-            packageName = apexPackageName;
-        }
-
         PackageManager pm = mContext.getPackageManager();
-        try {
-            return pm.getModuleInfo(packageName, 0) != null;
-        } catch (PackageManager.NameNotFoundException ignore) {
-            return false;
+
+        if (Flags.refactorCrashrecovery() && provideInfoOfApkInApex()) {
+            // Check if the package is listed among the system modules or is an
+            // APK inside an updatable APEX.
+            try {
+                final PackageInfo pkg = pm.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;
+            }
+        } else {
+            // Check if the package is an APK inside an APEX. If it is, use the parent APEX package
+            // when querying PackageManager.
+            String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage(
+                    packageName);
+            if (apexPackageName != null) {
+                packageName = apexPackageName;
+            }
+
+            try {
+                return pm.getModuleInfo(packageName, 0) != null;
+            } catch (PackageManager.NameNotFoundException ignore) {
+                return false;
+            }
         }
     }
 
@@ -512,11 +535,13 @@
     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: "
-                + (failedPackage == null ? null : failedPackage.getPackageName())
+                + " 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;
@@ -704,6 +729,7 @@
         }
 
         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) {
diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING
index 2cc931b..291b8db 100644
--- a/services/core/java/com/android/server/rollback/TEST_MAPPING
+++ b/services/core/java/com/android/server/rollback/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.rollback"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_rollback"
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 7fc0292..d763199 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -165,25 +165,6 @@
     }
 
     /**
-     * Logs that one or more apexd reverts have occurred, along with the crashing native process
-     * that caused apexd to revert during boot.
-     *
-     * @param context the context to use when determining the log packages
-     * @param failedPackageNames a list of names of packages which were reverted
-     * @param failingNativeProcess the crashing native process which caused a revert
-     */
-    public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
-            @NonNull String failingNativeProcess) {
-        Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
-        for (VersionedPackage logPackage: logPackages) {
-            logEvent(logPackage,
-                    WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                    WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
-                    failingNativeProcess);
-        }
-    }
-
-    /**
      * Log a Watchdog rollback event to statsd.
      *
      * @param logPackage the package to associate the rollback with.
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 331a594..b35a0a7 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -70,6 +70,8 @@
 import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
 import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
 import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
+import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;
+import static com.android.server.stats.pull.netstats.NetworkStatsUtils.isAddEntriesSupported;
 
 import static libcore.io.IoUtils.closeQuietly;
 
@@ -210,7 +212,6 @@
 import com.android.internal.os.StoragedUidIoStatsReader;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FrameworkStatsLog;
-import com.android.net.module.util.NetworkStatsUtils;
 import com.android.role.RoleManagerLocal;
 import com.android.server.BinderCallsStatsService;
 import com.android.server.LocalManagerRegistry;
@@ -1388,14 +1389,22 @@
 
     @NonNull
     private static NetworkStats removeEmptyEntries(NetworkStats stats) {
-        NetworkStats ret = new NetworkStats(0, 1);
+        final ArrayList<NetworkStats.Entry> entries = new ArrayList<>();
         for (NetworkStats.Entry e : stats) {
             if (e.getRxBytes() != 0 || e.getRxPackets() != 0 || e.getTxBytes() != 0
                     || e.getTxPackets() != 0 || e.getOperations() != 0) {
-                ret = ret.addEntry(e);
+                entries.add(e);
             }
         }
-        return ret;
+        if (isAddEntriesSupported()) {
+            return new NetworkStats(0, entries.size()).addEntries(entries);
+        } else {
+            NetworkStats outputStats = new NetworkStats(0L, 1);
+            for (NetworkStats.Entry e : entries) {
+                outputStats = outputStats.addEntry(e);
+            }
+            return outputStats;
+        }
     }
 
     private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
@@ -1587,14 +1596,14 @@
                 getNetworkStatsManager().querySummary(template, startTime, endTime);
 
         final NetworkStats nonTaggedStats =
-                NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats);
+                fromPublicNetworkStats(queryNonTaggedStats);
         queryNonTaggedStats.close();
         if (!includeTags) return nonTaggedStats;
 
         final android.app.usage.NetworkStats queryTaggedStats =
                 getNetworkStatsManager().queryTaggedSummary(template, startTime, endTime);
         final NetworkStats taggedStats =
-                NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats);
+                fromPublicNetworkStats(queryTaggedStats);
         queryTaggedStats.close();
         return nonTaggedStats.add(taggedStats);
     }
@@ -1720,11 +1729,19 @@
     @NonNull
     private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
             @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) {
-        NetworkStats ret = new NetworkStats(0, 1);
+        final ArrayList<NetworkStats.Entry> entries = new ArrayList();
         for (NetworkStats.Entry e : stats) {
-            ret = ret.addEntry(slicer.apply(e));
+            entries.add(slicer.apply(e));
         }
-        return ret;
+        if (isAddEntriesSupported()) {
+            return new NetworkStats(0, entries.size()).addEntries(entries);
+        } else {
+            NetworkStats outputStats = new NetworkStats(0L, 1);
+            for (NetworkStats.Entry e : entries) {
+                outputStats = outputStats.addEntry(e);
+            }
+            return outputStats;
+        }
     }
 
     private void registerWifiBytesTransferBackground() {
diff --git a/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java
new file mode 100644
index 0000000..0318bdd
--- /dev/null
+++ b/services/core/java/com/android/server/stats/pull/netstats/NetworkStatsUtils.java
@@ -0,0 +1,125 @@
+/*
+ * 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.stats.pull.netstats;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.SET_ALL;
+
+import android.app.usage.NetworkStats;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.stats.Flags;
+
+import java.util.ArrayList;
+
+/**
+ * Utility methods for accessing {@link android.net.NetworkStats}.
+ */
+public class NetworkStatsUtils {
+
+    /**
+     * Convert structure from android.app.usage.NetworkStats to android.net.NetworkStats.
+     */
+    public static android.net.NetworkStats fromPublicNetworkStats(
+            NetworkStats publiceNetworkStats) {
+        final ArrayList<android.net.NetworkStats.Entry> entries = new ArrayList<>();
+        while (publiceNetworkStats.hasNextBucket()) {
+            NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+            publiceNetworkStats.getNextBucket(bucket);
+            entries.add(fromBucket(bucket));
+        }
+        android.net.NetworkStats stats = new android.net.NetworkStats(0L, 1);
+        // The new API is only supported on devices running the mainline version of `NetworkStats`.
+        // It should always be used when available for memory efficiency.
+        if (isAddEntriesSupported()) {
+            stats = stats.addEntries(entries);
+        } else {
+            for (android.net.NetworkStats.Entry entry : entries) {
+                stats = stats.addEntry(entry);
+            }
+        }
+        return stats;
+    }
+
+    /**
+     * Convert structure from android.app.usage.NetworkStats.Bucket
+     * to android.net.NetworkStats.Entry.
+     */
+    @VisibleForTesting
+    public static android.net.NetworkStats.Entry fromBucket(NetworkStats.Bucket bucket) {
+        return new android.net.NetworkStats.Entry(
+                null /* IFACE_ALL */, bucket.getUid(), convertBucketState(bucket.getState()),
+                convertBucketTag(bucket.getTag()), convertBucketMetered(bucket.getMetered()),
+                convertBucketRoaming(bucket.getRoaming()),
+                convertBucketDefaultNetworkStatus(bucket.getDefaultNetworkStatus()),
+                bucket.getRxBytes(), bucket.getRxPackets(),
+                bucket.getTxBytes(), bucket.getTxPackets(), 0 /* operations */);
+    }
+
+    private static int convertBucketState(int networkStatsSet) {
+        switch (networkStatsSet) {
+            case NetworkStats.Bucket.STATE_ALL: return SET_ALL;
+            case NetworkStats.Bucket.STATE_DEFAULT: return android.net.NetworkStats.SET_DEFAULT;
+            case NetworkStats.Bucket.STATE_FOREGROUND:
+                return android.net.NetworkStats.SET_FOREGROUND;
+        }
+        return 0;
+    }
+
+    private static int convertBucketTag(int tag) {
+        switch (tag) {
+            case NetworkStats.Bucket.TAG_NONE: return android.net.NetworkStats.TAG_NONE;
+        }
+        return tag;
+    }
+
+    private static int convertBucketMetered(int metered) {
+        switch (metered) {
+            case NetworkStats.Bucket.METERED_ALL: return METERED_ALL;
+            case NetworkStats.Bucket.METERED_NO: return android.net.NetworkStats.METERED_NO;
+            case NetworkStats.Bucket.METERED_YES: return android.net.NetworkStats.METERED_YES;
+        }
+        return 0;
+    }
+
+    private static int convertBucketRoaming(int roaming) {
+        switch (roaming) {
+            case NetworkStats.Bucket.ROAMING_ALL: return ROAMING_ALL;
+            case NetworkStats.Bucket.ROAMING_NO: return android.net.NetworkStats.ROAMING_NO;
+            case NetworkStats.Bucket.ROAMING_YES: return android.net.NetworkStats.ROAMING_YES;
+        }
+        return 0;
+    }
+
+    private static int convertBucketDefaultNetworkStatus(int defaultNetworkStatus) {
+        switch (defaultNetworkStatus) {
+            case NetworkStats.Bucket.DEFAULT_NETWORK_ALL:
+                return DEFAULT_NETWORK_ALL;
+            case NetworkStats.Bucket.DEFAULT_NETWORK_NO:
+                return android.net.NetworkStats.DEFAULT_NETWORK_NO;
+            case NetworkStats.Bucket.DEFAULT_NETWORK_YES:
+                return android.net.NetworkStats.DEFAULT_NETWORK_YES;
+        }
+        return 0;
+    }
+
+    public static boolean isAddEntriesSupported() {
+        return Flags.netstatsUseAddEntries();
+    }
+}
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index f360837..afea303 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -1,6 +1,20 @@
 package: "com.android.server.stats"
 container: "system"
 
+# Note: To ensure compatibility across all release configurations, initiate the ramp-up process
+# only after the 'com.android.net.flags.netstats_add_entries' flag has been fully deployed.
+# This flag provides the necessary API from the Connectivity module.
+# The flag was added because the flag 'com.android.net.flags.netstats_add_entries' for API
+# is already being rolled out, and modifying behavior during an active rollout might
+# lead to unwanted issues.
+flag {
+    name: "netstats_use_add_entries"
+    namespace: "statsd"
+    description: "Use NetworkStats#addEntries to reduce memory footprint"
+    bug: "335680025"
+    is_fixed_read_only: true
+}
+
 flag {
     name: "add_mobile_bytes_transfer_by_proc_state_puller"
     namespace: "statsd"
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a4a29a0..09d2a02 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -20,6 +20,8 @@
 import android.app.ITransientNotificationCallback;
 import android.content.ComponentName;
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.UserHandle;
@@ -54,13 +56,12 @@
      * Used by InputMethodManagerService to notify the IME status.
      *
      * @param displayId The display to which the IME is bound to.
-     * @param vis Bit flags about the IME visibility.
-     *            (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
-     * @param backDisposition Bit flags about the IME back disposition.
-     *         (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
+     * @param vis The IME visibility.
+     * @param backDisposition The IME back disposition.
      * @param showImeSwitcher {@code true} when the IME switcher button should be shown.
      */
-    void setImeWindowStatus(int displayId, int vis, int backDisposition, boolean showImeSwitcher);
+    void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+            @BackDispositionMode int backDisposition, boolean showImeSwitcher);
 
     /**
      * See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
@@ -167,11 +168,6 @@
      */
     void onDisplayReady(int displayId);
 
-    /**
-     * Notifies System UI whether the recents animation is running.
-     */
-    void onRecentsAnimationStateChanged(boolean running);
-
     /** @see com.android.internal.statusbar.IStatusBar#onSystemBarAttributesChanged */
     void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
             AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c3601b3c..0fd5967 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -26,6 +26,10 @@
 import static android.app.StatusBarManager.NavBarMode;
 import static android.app.StatusBarManager.SessionFlags;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.os.UserHandle.USER_SYSTEM;
+import static android.os.UserHandle.getCallingUserId;
+import static android.os.UserManager.isVisibleBackgroundUsersEnabled;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
@@ -60,6 +64,8 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
 import android.media.INearbyMediaDevicesProvider;
 import android.media.MediaRoute2Info;
 import android.net.Uri;
@@ -113,6 +119,7 @@
 import com.android.server.UiThread;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.notification.NotificationDelegate;
+import com.android.server.pm.UserManagerService;
 import com.android.server.policy.GlobalActionsProvider;
 import com.android.server.power.ShutdownCheckPoints;
 import com.android.server.power.ShutdownThread;
@@ -145,9 +152,9 @@
 
     /**
      * In apps targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or higher, calling
-     * {@link android.service.quicksettings.TileService#requestListeningState} will check that the 
-     * calling package (uid) and the package of the target {@link android.content.ComponentName} 
-     * match. It'll also make sure that the context used can take actions on behalf of the current 
+     * {@link android.service.quicksettings.TileService#requestListeningState} will check that the
+     * calling package (uid) and the package of the target {@link android.content.ComponentName}
+     * match. It'll also make sure that the context used can take actions on behalf of the current
      * user.
      */
     @ChangeId
@@ -197,6 +204,9 @@
 
     private IOverlayManager mOverlayManager;
 
+    private final boolean mVisibleBackgroundUsersEnabled;
+    private final UserManagerService mUserManager;
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
             mBar.asBinder().unlinkToDeath(this,0);
@@ -297,6 +307,9 @@
 
         mTileRequestTracker = new TileRequestTracker(mContext);
         mSessionMonitor = new SessionMonitor(mContext);
+
+        mVisibleBackgroundUsersEnabled = isVisibleBackgroundUsersEnabled();
+        mUserManager = UserManagerService.getInstance();
     }
 
     /**
@@ -534,8 +547,8 @@
         }
 
         @Override
-        public void setImeWindowStatus(int displayId, int vis, int backDisposition,
-                boolean showImeSwitcher) {
+        public void setImeWindowStatus(int displayId, @ImeWindowVisibility int vis,
+                @BackDispositionMode int backDisposition, boolean showImeSwitcher) {
             StatusBarManagerService.this.setImeWindowStatus(displayId, vis, backDisposition,
                     showImeSwitcher);
         }
@@ -686,17 +699,6 @@
         }
 
         @Override
-        public void onRecentsAnimationStateChanged(boolean running) {
-            IStatusBar bar = mBar;
-            if (bar != null) {
-                try {
-                    bar.onRecentsAnimationStateChanged(running);
-                } catch (RemoteException ex) {}
-            }
-
-        }
-
-        @Override
         public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
                 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
                 @Behavior int behavior, @InsetsType int requestedVisibleTypes,
@@ -911,6 +913,7 @@
     @Override
     public void expandNotificationsPanel() {
         enforceExpandStatusBar();
+        enforceValidCallingUser();
 
         if (isDisable2FlagSet(DISABLE2_NOTIFICATION_SHADE)) {
             return;
@@ -926,6 +929,8 @@
 
     @Override
     public void collapsePanels() {
+        enforceValidCallingUser();
+
         if (!checkCanCollapseStatusBar("collapsePanels")) {
             return;
         }
@@ -940,6 +945,8 @@
 
     @Override
     public void togglePanel() {
+        enforceValidCallingUser();
+
         if (!checkCanCollapseStatusBar("togglePanel")) {
             return;
         }
@@ -959,6 +966,7 @@
     @Override
     public void expandSettingsPanel(String subPanel) {
         enforceExpandStatusBar();
+        enforceValidCallingUser();
 
         if (mBar != null) {
             try {
@@ -973,6 +981,7 @@
             addQsTileToFrontOrEnd(component, false);
         } else {
             enforceStatusBarOrShell();
+            enforceValidCallingUser();
 
             if (mBar != null) {
                 try {
@@ -985,6 +994,7 @@
 
     private void addQsTileToFrontOrEnd(ComponentName tile, boolean end) {
         enforceStatusBarOrShell();
+        enforceValidCallingUser();
 
         if (mBar != null) {
             try {
@@ -996,6 +1006,7 @@
 
     public void remTile(ComponentName component) {
         enforceStatusBarOrShell();
+        enforceValidCallingUser();
 
         if (mBar != null) {
             try {
@@ -1018,6 +1029,7 @@
 
     public void clickTile(ComponentName component) {
         enforceStatusBarOrShell();
+        enforceValidCallingUser();
 
         if (mBar != null) {
             try {
@@ -1029,6 +1041,8 @@
 
     @Override
     public void handleSystemKey(KeyEvent key) throws RemoteException {
+        enforceValidCallingUser();
+
         if (!checkCanCollapseStatusBar("handleSystemKey")) {
             return;
         }
@@ -1053,6 +1067,8 @@
 
     @Override
     public void showPinningEnterExitToast(boolean entering) throws RemoteException {
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.showPinningEnterExitToast(entering);
@@ -1063,6 +1079,8 @@
 
     @Override
     public void showPinningEscapeToast() throws RemoteException {
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.showPinningEscapeToast();
@@ -1076,6 +1094,8 @@
             int[] sensorIds, boolean credentialAllowed, boolean requireConfirmation,
             int userId, long operationId, String opPackageName, long requestId) {
         enforceBiometricDialog();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.showAuthenticationDialog(promptInfo, receiver, sensorIds, credentialAllowed,
@@ -1088,6 +1108,8 @@
     @Override
     public void onBiometricAuthenticated(@Modality int modality) {
         enforceBiometricDialog();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.onBiometricAuthenticated(modality);
@@ -1099,6 +1121,8 @@
     @Override
     public void onBiometricHelp(@Modality int modality, String message) {
         enforceBiometricDialog();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.onBiometricHelp(modality, message);
@@ -1110,6 +1134,8 @@
     @Override
     public void onBiometricError(int modality, int error, int vendorCode) {
         enforceBiometricDialog();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.onBiometricError(modality, error, vendorCode);
@@ -1121,6 +1147,8 @@
     @Override
     public void hideAuthenticationDialog(long requestId) {
         enforceBiometricDialog();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.hideAuthenticationDialog(requestId);
@@ -1132,6 +1160,8 @@
     @Override
     public void setBiometicContextListener(IBiometricContextListener listener) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         synchronized (mLock) {
             mBiometricContextListener = listener;
         }
@@ -1146,6 +1176,8 @@
     @Override
     public void setUdfpsRefreshRateCallback(IUdfpsRefreshRateRequestCallback callback) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.setUdfpsRefreshRateCallback(callback);
@@ -1156,6 +1188,8 @@
 
     @Override
     public void startTracing() {
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mBar.startTracing();
@@ -1167,6 +1201,8 @@
 
     @Override
     public void stopTracing() {
+        enforceValidCallingUser();
+
         if (mBar != null) {
             try {
                 mTracingEnabled = false;
@@ -1190,6 +1226,7 @@
     @Override
     public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
+        enforceValidCallingUser();
 
         synchronized (mLock) {
             disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
@@ -1293,6 +1330,7 @@
     public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
             String contentDescription) {
         enforceStatusBar();
+        enforceValidCallingUser();
 
         synchronized (mIcons) {
             StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
@@ -1313,6 +1351,7 @@
     @Override
     public void setIconVisibility(String slot, boolean visibility) {
         enforceStatusBar();
+        enforceValidCallingUser();
 
         synchronized (mIcons) {
             StatusBarIcon icon = mIcons.get(slot);
@@ -1336,6 +1375,7 @@
     @Override
     public void removeIcon(String slot) {
         enforceStatusBar();
+        enforceValidCallingUser();
 
         synchronized (mIcons) {
             mIcons.remove(slot);
@@ -1351,9 +1391,10 @@
     }
 
     @Override
-    public void setImeWindowStatus(int displayId, final int vis, final int backDisposition,
-            final boolean showImeSwitcher) {
+    public void setImeWindowStatus(int displayId, @ImeWindowVisibility final int vis,
+            @BackDispositionMode final int backDisposition, final boolean showImeSwitcher) {
         enforceStatusBar();
+        enforceValidCallingUser();
 
         if (SPEW) {
             Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
@@ -1418,8 +1459,10 @@
         private String mPackageName = "none";
         private int mDisabled1 = 0;
         private int mDisabled2 = 0;
+        @ImeWindowVisibility
         private int mImeWindowVis = 0;
-        private int mImeBackDisposition = 0;
+        @BackDispositionMode
+        private int mImeBackDisposition = BACK_DISPOSITION_DEFAULT;
         private boolean mShowImeSwitcher = false;
         private LetterboxDetails[] mLetterboxDetails = new LetterboxDetails[0];
 
@@ -1462,7 +1505,8 @@
             return mDisabled1 == disabled1 && mDisabled2 == disabled2;
         }
 
-        private void setImeWindowState(final int vis, final int backDisposition,
+        private void setImeWindowState(@ImeWindowVisibility final int vis,
+                @BackDispositionMode final int backDisposition,
                 final boolean showImeSwitcher) {
             mImeWindowVis = vis;
             mImeBackDisposition = backDisposition;
@@ -1544,6 +1588,7 @@
     @Override
     public RegisterStatusBarResult registerStatusBar(IStatusBar bar) {
         enforceStatusBarService();
+        enforceValidCallingUser();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
         mBar = bar;
@@ -1594,6 +1639,8 @@
     @Override
     public void onPanelRevealed(boolean clearNotificationEffects, int numItems) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onPanelRevealed(clearNotificationEffects, numItems);
@@ -1605,6 +1652,8 @@
     @Override
     public void clearNotificationEffects() throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.clearEffects();
@@ -1616,6 +1665,8 @@
     @Override
     public void onPanelHidden() throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onPanelHidden();
@@ -1630,6 +1681,8 @@
     @Override
     public void shutdown() {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         String reason = PowerManager.SHUTDOWN_USER_REQUESTED;
         ShutdownCheckPoints.recordCheckPoint(Binder.getCallingPid(), reason);
         final long identity = Binder.clearCallingIdentity();
@@ -1649,6 +1702,8 @@
     @Override
     public void reboot(boolean safeMode) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         String reason = safeMode
                 ? PowerManager.REBOOT_SAFE_MODE
                 : PowerManager.SHUTDOWN_USER_REQUESTED;
@@ -1675,6 +1730,8 @@
     @Override
     public void restart() {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mHandler.post(() -> {
@@ -1688,6 +1745,8 @@
     @Override
     public void onGlobalActionsShown() {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mGlobalActionListener == null) return;
@@ -1700,6 +1759,8 @@
     @Override
     public void onGlobalActionsHidden() {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             if (mGlobalActionListener == null) return;
@@ -1711,6 +1772,8 @@
 
     @Override
     public void onNotificationClick(String key, NotificationVisibility nv) {
+        // enforceValidCallingUser is not required here as the NotificationManagerService
+        // will handle multi-user scenarios
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
@@ -1726,6 +1789,8 @@
     public void onNotificationActionClick(
             String key, int actionIndex, Notification.Action action, NotificationVisibility nv,
             boolean generatedByAssistant) {
+        // enforceValidCallingUser is not required here as the NotificationManagerService
+        // will handle multi-user scenarios
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
@@ -1741,6 +1806,8 @@
     @Override
     public void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId) {
+        // enforceValidCallingUser is not required here as the NotificationManagerService
+        // will handle multi-user scenarios
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
@@ -1759,6 +1826,8 @@
             @NotificationStats.DismissalSurface int dismissalSurface,
             @NotificationStats.DismissalSentiment int dismissalSentiment,
             NotificationVisibility nv) {
+        // enforceValidCallingUser is not required here as the NotificationManagerService
+        // will handle multi-user scenarios
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
@@ -1776,6 +1845,8 @@
             NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys)
             throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationVisibilityChanged(
@@ -1789,6 +1860,8 @@
     public void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded,
             int location) throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationExpansionChanged(
@@ -1801,6 +1874,8 @@
     @Override
     public void onNotificationDirectReplied(String key) throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationDirectReplied(key);
@@ -1813,6 +1888,8 @@
     public void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount,
             int smartActionCount, boolean generatedByAssistant, boolean editBeforeSending) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSmartSuggestionsAdded(key, smartReplyCount,
@@ -1827,6 +1904,8 @@
             String key, int replyIndex, CharSequence reply, int notificationLocation,
             boolean modifiedBeforeSending) throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex, reply,
@@ -1839,6 +1918,8 @@
     @Override
     public void onNotificationSettingsViewed(String key) throws RemoteException {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationSettingsViewed(key);
@@ -1863,6 +1944,8 @@
     @Override
     public void onNotificationBubbleChanged(String key, boolean isBubble, int flags) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationBubbleChanged(key, isBubble, flags);
@@ -1874,6 +1957,8 @@
     @Override
     public void onBubbleMetadataFlagChanged(String key, int flags) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onBubbleMetadataFlagChanged(key, flags);
@@ -1885,6 +1970,8 @@
     @Override
     public void hideCurrentInputMethodForBubbles(int displayId) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long token = Binder.clearCallingIdentity();
         try {
             InputMethodManagerInternal.get().hideInputMethod(
@@ -1923,6 +2010,8 @@
     @Override
     public void onNotificationFeedbackReceived(String key, Bundle feedback) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         final long identity = Binder.clearCallingIdentity();
         try {
             mNotificationDelegate.onNotificationFeedbackReceived(key, feedback);
@@ -1942,6 +2031,8 @@
     @Override
     public void showInattentiveSleepWarning() {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -1954,6 +2045,8 @@
     @Override
     public void dismissInattentiveSleepWarning(boolean animated) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -1966,6 +2059,8 @@
     @Override
     public void suppressAmbientDisplay(boolean suppress) {
         enforceStatusBarService();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2179,6 +2274,8 @@
     @Override
     public void cancelRequestAddTile(@NonNull String packageName) {
         enforceStatusBar();
+        enforceValidCallingUser();
+
         cancelRequestAddTileInternal(packageName);
     }
 
@@ -2202,23 +2299,31 @@
 
     @Override
     public void onSessionStarted(@SessionFlags int sessionType, InstanceId instance) {
+        enforceValidCallingUser();
+
         mSessionMonitor.onSessionStarted(sessionType, instance);
     }
 
     @Override
     public void onSessionEnded(@SessionFlags int sessionType, InstanceId instance) {
+        enforceValidCallingUser();
+
         mSessionMonitor.onSessionEnded(sessionType, instance);
     }
 
     @Override
     public void registerSessionListener(@SessionFlags int sessionFlags,
             ISessionListener listener) {
+        enforceValidCallingUser();
+
         mSessionMonitor.registerSessionListener(sessionFlags, listener);
     }
 
     @Override
     public void unregisterSessionListener(@SessionFlags int sessionFlags,
             ISessionListener listener) {
+        enforceValidCallingUser();
+
         mSessionMonitor.unregisterSessionListener(sessionFlags, listener);
     }
 
@@ -2233,6 +2338,8 @@
      */
     public void setNavBarMode(@NavBarMode int navBarMode) {
         enforceStatusBar();
+        enforceValidCallingUser();
+
         if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) {
             throw new IllegalArgumentException("Supplied navBarMode not supported: " + navBarMode);
         }
@@ -2243,6 +2350,7 @@
             throw new SecurityException("Calling user id: " + callingUserId
                     + ", cannot call on behalf of current user id: " + mCurrentUserId + ".");
         }
+
         final long userIdentity = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -2316,6 +2424,8 @@
             @Nullable IUndoMediaTransferCallback undoCallback
     ) {
         enforceMediaContentControl();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2340,6 +2450,8 @@
             @Nullable Icon appIcon,
             @Nullable CharSequence appName) {
         enforceMediaContentControl();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2367,6 +2479,8 @@
             @NonNull INearbyMediaDevicesProvider provider
     ) {
         enforceMediaContentControl();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2393,6 +2507,8 @@
             @NonNull INearbyMediaDevicesProvider provider
     ) {
         enforceMediaContentControl();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2407,6 +2523,8 @@
     @Override
     public void showRearDisplayDialog(int currentState) {
         enforceControlDeviceStatePermission();
+        enforceValidCallingUser();
+
         IStatusBar bar = mBar;
         if (bar != null) {
             try {
@@ -2579,4 +2697,32 @@
     private static final Context getUiContext() {
         return ActivityThread.currentActivityThread().getSystemUiContext();
     }
+
+    /**
+     * This method validates whether the calling user is allowed to control the status bar
+     * on a device that enables visible background users.
+     * Only system or current user or the user that belongs to the same profile group as the
+     * current user is permitted to control the status bar.
+     */
+    private void enforceValidCallingUser() {
+        if (!mVisibleBackgroundUsersEnabled) {
+            return;
+        }
+
+        int callingUserId = getCallingUserId();
+        if (callingUserId == USER_SYSTEM || callingUserId == mCurrentUserId) {
+            return;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (mUserManager.isSameProfileGroup(callingUserId, mCurrentUserId)) {
+                return;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+
+        throw new SecurityException("User " + callingUserId
+                + " is not permitted to use this method");
+    }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
index d6bf02f..6466519 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java
@@ -190,6 +190,9 @@
                 case "notification-icons":
                     info.setNotificationIconsDisabled(true);
                     break;
+                case "quick-settings":
+                    info.setQuickSettingsDisabled(true);
+                    break;
                 default:
                     break;
             }
@@ -277,6 +280,7 @@
         pw.println("        system-icons        - disable system icons appearing in status bar");
         pw.println("        clock               - disable clock appearing in status bar");
         pw.println("        notification-icons  - disable notification icons from status bar");
+        pw.println("        quick-settings      - disable Quick Settings");
         pw.println("");
         pw.println("  tracing (start | stop)");
         pw.println("    Start or stop SystemUI tracing");
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 68eb8eb..8e24e9f 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -38,6 +38,7 @@
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.service.tracing.TraceReportService;
 import android.tracing.ITracingServiceProxy;
 import android.tracing.TraceReportParams;
@@ -76,8 +77,6 @@
     // Keep this in sync with the definitions in TraceService
     private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
             "com.android.traceur.NOTIFY_SESSION_STOPPED";
-    private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
-            "com.android.traceur.NOTIFY_SESSION_STOLEN";
 
     private static final int REPORT_BEGIN =
             TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
@@ -89,6 +88,8 @@
             TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
     private static final int REPORT_SVC_COMM_ERROR =
             TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+    private static final String NOTIFY_SESSION_ENDED_SETTING = "should_notify_trace_session_ended";
+    private static final int ENABLED = 1;
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -97,13 +98,24 @@
 
     private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
         /**
-         * Notifies system tracing app that a tracing session has ended. If a session is repurposed
-         * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
-         * there is no buffer available to dump.
+         * Notifies system tracing app that a tracing session has ended. sessionStolen is ignored,
+         * as trace sessions are no longer stolen and are always cloned instead.
+         * <p>
+         * Cases exist where user-flows besides Traceur's QS Tile may end long-trace sessions. In
+         * these cases, a Global int will be set to flag the upcoming notifyTraceSessionEnded call
+         * as purposely muted once.
          */
         @Override
-        public void notifyTraceSessionEnded(boolean sessionStolen) {
-            TracingServiceProxy.this.notifyTraceur(sessionStolen);
+        public void notifyTraceSessionEnded(boolean sessionStolen /* unused */) {
+            long identity = Binder.clearCallingIdentity();
+            if (Settings.Global.getInt(mContext.getContentResolver(),
+                    NOTIFY_SESSION_ENDED_SETTING, ENABLED) == ENABLED) {
+                TracingServiceProxy.this.notifyTraceur();
+            } else {
+                Settings.Global.putInt(mContext.getContentResolver(), NOTIFY_SESSION_ENDED_SETTING,
+                        ENABLED);
+            }
+            Binder.restoreCallingIdentity(identity);
         }
 
         @Override
@@ -132,7 +144,7 @@
         }
     }
 
-    private void notifyTraceur(boolean sessionStolen) {
+    private void notifyTraceur() {
         final Intent intent = new Intent();
 
         try {
@@ -141,11 +153,7 @@
                     PackageManager.MATCH_SYSTEM_ONLY);
 
             intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
-            if (sessionStolen) {
-                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
-            } else {
-                intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
-            }
+            intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
 
             final long identity = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6721893..60b5e65 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,6 +19,8 @@
 import static android.media.AudioManager.DEVICE_NONE;
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
+import static android.media.tv.flags.Flags.tifUnbindInactiveTis;
+import static android.media.tv.flags.Flags.kidsModeTvdbSharing;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -249,7 +251,7 @@
     }
 
     private void registerBroadcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor() {
+        PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
             private void buildTvInputList(String[] packages) {
                 int userId = getChangingUserId();
                 synchronized (mLock) {
@@ -499,6 +501,7 @@
                     && parentInfo != null
                     && parentInfo.id == mCurrentUserId) {
                 // only the children of the current user can be started in background
+                mCurrentUserId = userId;
                 startProfileLocked(userId);
             }
         }
@@ -805,6 +808,19 @@
         }
     }
 
+    private boolean isServiceSingleUser(ComponentName component) {
+        try {
+            ServiceInfo serviceInfo = getContext().getPackageManager()
+                    .getServiceInfo(component, 0);
+            // Check if the single-user flag is present
+            return (serviceInfo.flags & ServiceInfo.FLAG_SINGLE_USER) != 0;
+        } catch (PackageManager.NameNotFoundException e) {
+            // Handle the case where the service is not found
+            Slog.e(TAG, "Service not found: " + component, e);
+            return false;
+        }
+    }
+
     @GuardedBy("mLock")
     private void abortPendingCreateSessionRequestsLocked(ServiceState serviceState,
             String inputId, int userId) {
@@ -2840,6 +2856,26 @@
         }
 
         @Override
+        public int getClientUserId(String sessionId) {
+            ensureTunerResourceAccessPermission();
+            int clientUserId = TvInputManager.UNKNOWN_CLIENT_USER_ID;
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        clientUserId = getClientUserIdLocked(sessionId);
+                    } catch (ClientUserIdNotFoundException e) {
+                        Slog.e(TAG, "error in getClientUserId", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+            return clientUserId;
+        }
+
+        @Override
         public int getClientPriority(int useCase, String sessionId) {
             ensureTunerResourceAccessPermission();
             final int callingPid = Binder.getCallingPid();
@@ -2924,6 +2960,16 @@
             return mSessionIdToSessionStateMap.get(sessionId).callingPid;
         }
 
+        @GuardedBy("mLock")
+        private int getClientUserIdLocked(String sessionId) throws ClientUserIdNotFoundException {
+            SessionState sessionState = mSessionIdToSessionStateMap.get(sessionId);
+            if (sessionState == null) {
+                throw new ClientUserIdNotFoundException(
+                        "Client UserId not found with sessionId " + sessionId);
+            }
+            return sessionState.userId;
+        }
+
         private void ensureTunerResourceAccessPermission() {
             if (mContext.checkCallingPermission(
                     android.Manifest.permission.TUNER_RESOURCE_ACCESS)
@@ -3260,7 +3306,20 @@
         return filteredDisplayName;
     }
 
-    private static final class UserState {
+    private class TvInputManagerCallbackList extends RemoteCallbackList<ITvInputManagerCallback> {
+        @Override
+        public void onCallbackDied(ITvInputManagerCallback callback) {
+            synchronized (mLock) {
+                for (int i = 0; i < mUserStates.size(); i++) {
+                    int userId = mUserStates.keyAt(i);
+                    UserState userState = getOrCreateUserStateLocked(userId);
+                    userState.callbackPidUidMap.remove(callback);
+                }
+            }
+        }
+    }
+
+    private final class UserState {
         // A mapping from the TV input id to its TvInputState.
         private Map<String, TvInputState> inputMap = new HashMap<>();
 
@@ -3281,8 +3340,8 @@
         private final Map<IBinder, SessionState> sessionStateMap = new HashMap<>();
 
         // A list of callbacks.
-        private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks =
-                new RemoteCallbackList<>();
+        private final TvInputManagerCallbackList mCallbacks =
+                new TvInputManagerCallbackList();
 
         private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap =
                 new HashMap<>();
@@ -3495,11 +3554,15 @@
                     "bindServiceAsUser(service=" + serviceState.component + ", userId=" + userId
                             + ")");
         }
+        int bindUserId = userId;
+        if (kidsModeTvdbSharing() && isServiceSingleUser(serviceState.component)) {
+            bindUserId = UserHandle.USER_SYSTEM;
+        }
         Intent i =
                 new Intent(TvInputService.SERVICE_INTERFACE).setComponent(serviceState.component);
         serviceState.bound = mContext.bindServiceAsUser(i, serviceState.connection,
                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
-                new UserHandle(userId));
+                new UserHandle(bindUserId));
         if (!serviceState.bound) {
             Slog.e(TAG, "failed to bind " + serviceState.component + " for userId " + userId);
             mContext.unbindService(serviceState.connection);
@@ -4466,12 +4529,14 @@
                     break;
                 }
                 case MSG_UPDATE_HARDWARE_TIS_BINDING:
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    int userId = (int) args.arg1;
-                    synchronized (mLock) {
-                        updateHardwareTvInputServiceBindingLocked(userId);
+                    if (tifUnbindInactiveTis()) {
+                        SomeArgs args = (SomeArgs) msg.obj;
+                        int userId = (int) args.arg1;
+                        synchronized (mLock) {
+                            updateHardwareTvInputServiceBindingLocked(userId);
+                        }
+                        args.recycle();
                     }
-                    args.recycle();
                     break;
                 default: {
                     Slog.w(TAG, "unhandled message code: " + msg.what);
@@ -4700,4 +4765,10 @@
             super(name);
         }
     }
+
+    private static class ClientUserIdNotFoundException extends IllegalArgumentException {
+        ClientUserIdNotFoundException(String name) {
+            super(name);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index edd2fa9..6a7fc6d 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -519,7 +519,7 @@
     }
 
     private void registerBroadcastReceivers() {
-        PackageMonitor monitor = new PackageMonitor() {
+        PackageMonitor monitor = new PackageMonitor(/* supportsPackageRestartQuery */ true) {
             private void buildTvInteractiveAppServiceList(String[] packages) {
                 int userId = getChangingUserId();
                 synchronized (mLock) {
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 440d2514..eb5361c 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java
@@ -26,6 +26,11 @@
  * @hide
  */
 public class CasResource {
+    /**
+     * Handle of the current resource. Should not be changed and should be aligned with the driver
+     * level implementation.
+     */
+    final int mHandle;
 
     private final int mSystemId;
 
@@ -39,11 +44,16 @@
     private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>();
 
     CasResource(Builder builder) {
+        this.mHandle = builder.mHandle;
         this.mSystemId = builder.mSystemId;
         this.mMaxSessionNum = builder.mMaxSessionNum;
         this.mAvailableSessionNum = builder.mMaxSessionNum;
     }
 
+    public int getHandle() {
+        return mHandle;
+    }
+
     public int getSystemId() {
         return mSystemId;
     }
@@ -136,10 +146,12 @@
      */
     public static class Builder {
 
+        private final int mHandle;
         private int mSystemId;
         protected int mMaxSessionNum;
 
-        Builder(int systemId) {
+        Builder(int 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 31149f3..5cef729 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java
@@ -42,8 +42,8 @@
      * Builder class for {@link CiCamResource}.
      */
     public static class Builder extends CasResource.Builder {
-        Builder(int systemId) {
-            super(systemId);
+        Builder(int handle, int systemId) {
+            super(handle, systemId);
         }
 
         /**
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 0afb049..9229f7f 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -203,13 +203,7 @@
         @Override
         public void unregisterClientProfile(int clientId) throws RemoteException {
             enforceTrmAccessPermission("unregisterClientProfile");
-            synchronized (mLock) {
-                if (!checkClientExists(clientId)) {
-                    Slog.e(TAG, "Unregistering non exists client:" + clientId);
-                    return;
-                }
-                unregisterClientProfileInternal(clientId);
-            }
+            unregisterClientProfileInternal(clientId);
         }
 
         @Override
@@ -291,20 +285,7 @@
                 Slog.e(TAG, "frontendHandle can't be null");
                 return false;
             }
-            synchronized (mLock) {
-                if (!checkClientExists(request.clientId)) {
-                    Slog.e(TAG, "Request frontend from unregistered client: "
-                            + request.clientId);
-                    return false;
-                }
-                // If the request client is holding or sharing a frontend, throw an exception.
-                if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
-                    Slog.e(TAG, "Release frontend before requesting another one. Client id: "
-                            + request.clientId);
-                    return false;
-                }
-                return requestFrontendInternal(request, frontendHandle);
-            }
+            return requestFrontendInternal(request, frontendHandle);
         }
 
         @Override
@@ -376,13 +357,7 @@
             if (demuxHandle == null) {
                 throw new RemoteException("demuxHandle can't be null");
             }
-            synchronized (mLock) {
-                if (!checkClientExists(request.clientId)) {
-                    throw new RemoteException("Request demux from unregistered client:"
-                            + request.clientId);
-                }
-                return requestDemuxInternal(request, demuxHandle);
-            }
+            return requestDemuxInternal(request, demuxHandle);
         }
 
         @Override
@@ -409,13 +384,7 @@
             if (casSessionHandle == null) {
                 throw new RemoteException("casSessionHandle can't be null");
             }
-            synchronized (mLock) {
-                if (!checkClientExists(request.clientId)) {
-                    throw new RemoteException("Request cas from unregistered client:"
-                            + request.clientId);
-                }
-                return requestCasSessionInternal(request, casSessionHandle);
-            }
+            return requestCasSessionInternal(request, casSessionHandle);
         }
 
         @Override
@@ -425,13 +394,7 @@
             if (ciCamHandle == null) {
                 throw new RemoteException("ciCamHandle can't be null");
             }
-            synchronized (mLock) {
-                if (!checkClientExists(request.clientId)) {
-                    throw new RemoteException("Request ciCam from unregistered client:"
-                            + request.clientId);
-                }
-                return requestCiCamInternal(request, ciCamHandle);
-            }
+            return requestCiCamInternal(request, ciCamHandle);
         }
 
         @Override
@@ -442,42 +405,14 @@
             if (lnbHandle == null) {
                 throw new RemoteException("lnbHandle can't be null");
             }
-            synchronized (mLock) {
-                if (!checkClientExists(request.clientId)) {
-                    throw new RemoteException("Request lnb from unregistered client:"
-                            + request.clientId);
-                }
-                return requestLnbInternal(request, lnbHandle);
-            }
+            return requestLnbInternal(request, lnbHandle);
         }
 
         @Override
         public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException {
             enforceTunerAccessPermission("releaseFrontend");
             enforceTrmAccessPermission("releaseFrontend");
-            if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
-                    frontendHandle)) {
-                throw new RemoteException("frontendHandle can't be invalid");
-            }
-            synchronized (mLock) {
-                if (!checkClientExists(clientId)) {
-                    throw new RemoteException("Release frontend from unregistered client:"
-                            + clientId);
-                }
-                FrontendResource fe = getFrontendResource(frontendHandle);
-                if (fe == null) {
-                    throw new RemoteException("Releasing frontend does not exist.");
-                }
-                int ownerClientId = fe.getOwnerClientId();
-                ClientProfile ownerProfile = getClientProfile(ownerClientId);
-                if (ownerClientId != clientId
-                        && (ownerProfile != null
-                              && !ownerProfile.getShareFeClientIds().contains(clientId))) {
-                    throw new RemoteException(
-                            "Client is not the current owner of the releasing fe.");
-                }
-                releaseFrontendInternal(fe, clientId);
-            }
+            releaseFrontendInternal(frontendHandle, clientId);
         }
 
         @Override
@@ -746,17 +681,23 @@
 
     @VisibleForTesting
     protected void unregisterClientProfileInternal(int clientId) {
-        if (DEBUG) {
-            Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
-        }
-        removeClientProfile(clientId);
-        // Remove the Media Resource Manager callingPid to tvAppId mapping
-        if (mMediaResourceManager != null) {
-            try {
-                mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
-                        + " remote exception: " + e);
+        synchronized (mLock) {
+            if (!checkClientExists(clientId)) {
+                Slog.e(TAG, "Unregistering non exists client:" + clientId);
+                return;
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")");
+            }
+            removeClientProfile(clientId);
+            // Remove the Media Resource Manager callingPid to tvAppId mapping
+            if (mMediaResourceManager != null) {
+                try {
+                    mMediaResourceManager.overridePid(Binder.getCallingPid(), -1);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Could not overridePid in resourceManagerSercice when unregister,"
+                            + " remote exception: " + e);
+                }
             }
         }
     }
@@ -992,10 +933,14 @@
             return;
         }
         // Add the new Cas Resource.
-        cas = new CasResource.Builder(casSystemId)
+        int casSessionHandle = generateResourceHandle(
+                TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId);
+        cas = new CasResource.Builder(casSessionHandle, casSystemId)
                              .maxSessionNum(maxSessionNum)
                              .build();
-        ciCam = new CiCamResource.Builder(casSystemId)
+        int ciCamHandle = generateResourceHandle(
+                TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId);
+        ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId)
                              .maxSessionNum(maxSessionNum)
                              .build();
         addCasResource(cas);
@@ -1007,86 +952,120 @@
         if (DEBUG) {
             Slog.d(TAG, "requestFrontend(request=" + request + ")");
         }
-
-        frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        ClientProfile requestClient = getClientProfile(request.clientId);
-        // TODO: check if this is really needed
-        if (requestClient == null) {
+        int[] reclaimOwnerId = new int[1];
+        if (!claimFrontend(request, frontendHandle, reclaimOwnerId)) {
             return false;
         }
-        clientPriorityUpdateOnRequest(requestClient);
-        int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        // Priority max value is 1000
-        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
-        boolean isRequestFromSameProcess = false;
-        // If the desired frontend id was specified, we only need to check the frontend.
-        boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID;
-        for (FrontendResource fr : getFrontendResources().values()) {
-            int frontendId = getResourceIdFromHandle(fr.getHandle());
-            if (fr.getType() == request.frontendType
-                    && (!hasDesiredFrontend || frontendId == request.desiredId)) {
-                if (!fr.isInUse()) {
-                    // Unused resource cannot be acquired if the max is already reached, but
-                    // TRM still has to look for the reclaim candidate
-                    if (isFrontendMaxNumUseReached(request.frontendType)) {
-                        continue;
-                    }
-                    // Grant unused frontend with no exclusive group members first.
-                    if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
-                        grantingFrontendHandle = fr.getHandle();
-                        break;
-                    } else if (grantingFrontendHandle
-                            == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-                        // Grant the unused frontend with lower id first if all the unused
-                        // frontends have exclusive group members.
-                        grantingFrontendHandle = fr.getHandle();
-                    }
-                } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-                    // Record the frontend id with the lowest client priority among all the
-                    // in use frontends when no available frontend has been found.
-                    int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
-                    if (currentLowestPriority > priority) {
-                        // we need to check the max used num if the target frontend type is not
-                        // currently in primary use (and simply blocked due to exclusive group)
-                        ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId());
-                        int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
-                        FrontendResource primaryFe = getFrontendResource(primaryFeId);
-                        if (fr.getType() != primaryFe.getType()
-                                && isFrontendMaxNumUseReached(fr.getType())) {
+        if (frontendHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return false;
+        }
+        if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+            if (!reclaimResource(reclaimOwnerId[0], TunerResourceManager
+                    .TUNER_RESOURCE_TYPE_FRONTEND)) {
+                return false;
+            }
+            synchronized (mLock) {
+                if (getFrontendResource(frontendHandle[0]).isInUse()) {
+                    Slog.e(TAG, "Reclaimed frontend still in use");
+                    return false;
+                }
+                updateFrontendClientMappingOnNewGrant(frontendHandle[0], request.clientId);
+            }
+        }
+        return true;
+    }
+
+    protected boolean claimFrontend(
+            TunerFrontendRequest request,
+            int[] frontendHandle,
+            int[] reclaimOwnerId
+    ) {
+        frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        reclaimOwnerId[0] = INVALID_CLIENT_ID;
+        synchronized (mLock) {
+            if (!checkClientExists(request.clientId)) {
+                Slog.e(TAG, "Request frontend from unregistered client: "
+                        + request.clientId);
+                return false;
+            }
+            // If the request client is holding or sharing a frontend, throw an exception.
+            if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
+                Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+                        + request.clientId);
+                return false;
+            }
+            ClientProfile requestClient = getClientProfile(request.clientId);
+            clientPriorityUpdateOnRequest(requestClient);
+            FrontendResource grantingFrontend = null;
+            FrontendResource inUseLowestPriorityFrontend = null;
+            // Priority max value is 1000
+            int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+            boolean isRequestFromSameProcess = false;
+            // If the desired frontend id was specified, we only need to check the frontend.
+            boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest
+                    .DEFAULT_DESIRED_ID;
+            for (FrontendResource fr : getFrontendResources().values()) {
+                int frontendId = getResourceIdFromHandle(fr.getHandle());
+                if (fr.getType() == request.frontendType
+                        && (!hasDesiredFrontend || frontendId == request.desiredId)) {
+                    if (!fr.isInUse()) {
+                        // Unused resource cannot be acquired if the max is already reached, but
+                        // TRM still has to look for the reclaim candidate
+                        if (isFrontendMaxNumUseReached(request.frontendType)) {
                             continue;
                         }
-                        // update the target frontend
-                        inUseLowestPriorityFrHandle = fr.getHandle();
-                        currentLowestPriority = priority;
-                        isRequestFromSameProcess = (requestClient.getProcessId()
-                            == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+                        // Grant unused frontend with no exclusive group members first.
+                        if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
+                            grantingFrontend = fr;
+                            break;
+                        } else if (grantingFrontend == null) {
+                            // Grant the unused frontend with lower id first if all the unused
+                            // frontends have exclusive group members.
+                            grantingFrontend = fr;
+                        }
+                    } else if (grantingFrontend == null) {
+                        // Record the frontend id with the lowest client priority among all the
+                        // in use frontends when no available frontend has been found.
+                        int priority = getFrontendHighestClientPriority(fr.getOwnerClientId());
+                        if (currentLowestPriority > priority) {
+                            // we need to check the max used num if the target frontend type is not
+                            // currently in primary use (and simply blocked due to exclusive group)
+                            ClientProfile targetOwnerProfile =
+                                    getClientProfile(fr.getOwnerClientId());
+                            int primaryFeId = targetOwnerProfile.getPrimaryFrontend();
+                            FrontendResource primaryFe = getFrontendResource(primaryFeId);
+                            if (fr.getType() != primaryFe.getType()
+                                    && isFrontendMaxNumUseReached(fr.getType())) {
+                                continue;
+                            }
+                            // update the target frontend
+                            inUseLowestPriorityFrontend = fr;
+                            currentLowestPriority = priority;
+                            isRequestFromSameProcess = (requestClient.getProcessId()
+                                == (getClientProfile(fr.getOwnerClientId())).getProcessId());
+                        }
                     }
                 }
             }
-        }
 
-        // Grant frontend when there is unused resource.
-        if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-            frontendHandle[0] = grantingFrontendHandle;
-            updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.clientId);
-            return true;
-        }
-
-        // When all the resources are occupied, grant the lowest priority resource if the
-        // request client has higher priority.
-        if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
-            && ((requestClient.getPriority() > currentLowestPriority) || (
-            (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
-            if (!reclaimResource(
-                    getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(),
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
-                return false;
+            // Grant frontend when there is unused resource.
+            if (grantingFrontend != null) {
+                updateFrontendClientMappingOnNewGrant(grantingFrontend.getHandle(),
+                        request.clientId);
+                frontendHandle[0] = grantingFrontend.getHandle();
+                return true;
             }
-            frontendHandle[0] = inUseLowestPriorityFrHandle;
-            updateFrontendClientMappingOnNewGrant(
-                    inUseLowestPriorityFrHandle, request.clientId);
-            return true;
+
+            // When all the resources are occupied, grant the lowest priority resource if the
+            // request client has higher priority.
+            if (inUseLowestPriorityFrontend != null
+                    && ((requestClient.getPriority() > currentLowestPriority)
+                    || ((requestClient.getPriority() == currentLowestPriority)
+                    && isRequestFromSameProcess))) {
+                frontendHandle[0] = inUseLowestPriorityFrontend.getHandle();
+                reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId();
+                return true;
+            }
         }
 
         return false;
@@ -1192,165 +1171,257 @@
     }
 
     @VisibleForTesting
-    protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) {
+    protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle)
+            throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestLnb(request=" + request + ")");
         }
-
-        lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        ClientProfile requestClient = getClientProfile(request.clientId);
-        clientPriorityUpdateOnRequest(requestClient);
-        int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        // Priority max value is 1000
-        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
-        boolean isRequestFromSameProcess = false;
-        for (LnbResource lnb : getLnbResources().values()) {
-            if (!lnb.isInUse()) {
-                // Grant the unused lnb with lower handle first
-                grantingLnbHandle = lnb.getHandle();
-                break;
-            } else {
-                // Record the lnb id with the lowest client priority among all the
-                // in use lnb when no available lnb has been found.
-                int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
-                if (currentLowestPriority > priority) {
-                    inUseLowestPriorityLnbHandle = lnb.getHandle();
-                    currentLowestPriority = priority;
-                    isRequestFromSameProcess = (requestClient.getProcessId()
-                        == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
-                }
-            }
+        int[] reclaimOwnerId = new int[1];
+        if (!claimLnb(request, lnbHandle, reclaimOwnerId)) {
+            return false;
         }
-
-        // Grant Lnb when there is unused resource.
-        if (grantingLnbHandle > -1) {
-            lnbHandle[0] = grantingLnbHandle;
-            updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.clientId);
-            return true;
+        if (lnbHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return false;
         }
-
-        // When all the resources are occupied, grant the lowest priority resource if the
-        // request client has higher priority.
-        if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE
-            && ((requestClient.getPriority() > currentLowestPriority) || (
-            (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
-            if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(),
+        if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+            if (!reclaimResource(reclaimOwnerId[0],
                     TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) {
                 return false;
             }
-            lnbHandle[0] = inUseLowestPriorityLnbHandle;
-            updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.clientId);
-            return true;
+            synchronized (mLock) {
+                if (getLnbResource(lnbHandle[0]).isInUse()) {
+                    Slog.e(TAG, "Reclaimed lnb still in use");
+                    return false;
+                }
+                updateLnbClientMappingOnNewGrant(lnbHandle[0], request.clientId);
+            }
+        }
+        return true;
+    }
+
+    protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId)
+            throws RemoteException {
+        lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        reclaimOwnerId[0] = INVALID_CLIENT_ID;
+        synchronized (mLock) {
+            if (!checkClientExists(request.clientId)) {
+                throw new RemoteException("Request lnb from unregistered client:"
+                        + request.clientId);
+            }
+            ClientProfile requestClient = getClientProfile(request.clientId);
+            clientPriorityUpdateOnRequest(requestClient);
+            LnbResource grantingLnb = null;
+            LnbResource inUseLowestPriorityLnb = null;
+            // Priority max value is 1000
+            int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+            boolean isRequestFromSameProcess = false;
+            for (LnbResource lnb : getLnbResources().values()) {
+                if (!lnb.isInUse()) {
+                    // Grant the unused lnb with lower handle first
+                    grantingLnb = lnb;
+                    break;
+                } else {
+                    // Record the lnb id with the lowest client priority among all the
+                    // in use lnb when no available lnb has been found.
+                    int priority = updateAndGetOwnerClientPriority(lnb.getOwnerClientId());
+                    if (currentLowestPriority > priority) {
+                        inUseLowestPriorityLnb = lnb;
+                        currentLowestPriority = priority;
+                        isRequestFromSameProcess = (requestClient.getProcessId()
+                            == (getClientProfile(lnb.getOwnerClientId())).getProcessId());
+                    }
+                }
+            }
+
+            // Grant Lnb when there is unused resource.
+            if (grantingLnb != null) {
+                updateLnbClientMappingOnNewGrant(grantingLnb.getHandle(), request.clientId);
+                lnbHandle[0] = grantingLnb.getHandle();
+                return true;
+            }
+
+            // When all the resources are occupied, grant the lowest priority resource if the
+            // request client has higher priority.
+            if (inUseLowestPriorityLnb != null
+                    && ((requestClient.getPriority() > currentLowestPriority) || (
+                    (requestClient.getPriority() == currentLowestPriority)
+                        && isRequestFromSameProcess))) {
+                lnbHandle[0] = inUseLowestPriorityLnb.getHandle();
+                reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId();
+                return true;
+            }
         }
 
         return false;
     }
 
     @VisibleForTesting
-    protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) {
+    protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle)
+            throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestCasSession(request=" + request + ")");
         }
-        CasResource cas = getCasResource(request.casSystemId);
-        // Unregistered Cas System is treated as having unlimited sessions.
-        if (cas == null) {
-            cas = new CasResource.Builder(request.casSystemId)
-                                 .maxSessionNum(Integer.MAX_VALUE)
-                                 .build();
-            addCasResource(cas);
+        int[] reclaimOwnerId = new int[1];
+        if (!claimCasSession(request, casSessionHandle, reclaimOwnerId)) {
+            return false;
         }
-        casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        ClientProfile requestClient = getClientProfile(request.clientId);
-        clientPriorityUpdateOnRequest(requestClient);
-        int lowestPriorityOwnerId = -1;
-        // Priority max value is 1000
-        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
-        boolean isRequestFromSameProcess = false;
-        if (!cas.isFullyUsed()) {
-            casSessionHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
-            updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
-            return true;
+        if (casSessionHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return false;
         }
-        for (int ownerId : cas.getOwnerClientIds()) {
-            // Record the client id with lowest priority that is using the current Cas system.
-            int priority = updateAndGetOwnerClientPriority(ownerId);
-            if (currentLowestPriority > priority) {
-                lowestPriorityOwnerId = ownerId;
-                currentLowestPriority = priority;
-                isRequestFromSameProcess = (requestClient.getProcessId()
-                    == (getClientProfile(ownerId)).getProcessId());
-            }
-        }
-
-        // When all the Cas sessions are occupied, reclaim the lowest priority client if the
-        // request client has higher priority.
-        if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
-        || ((requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
-            if (!reclaimResource(lowestPriorityOwnerId,
+        if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+            if (!reclaimResource(reclaimOwnerId[0],
                     TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) {
                 return false;
             }
-            casSessionHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId());
-            updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
-            return true;
+            synchronized (mLock) {
+                if (getCasResource(request.casSystemId).isFullyUsed()) {
+                    Slog.e(TAG, "Reclaimed cas still fully used");
+                    return false;
+                }
+                updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+            }
         }
+        return true;
+    }
+
+    protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle,
+            int[] reclaimOwnerId) throws RemoteException {
+        casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        reclaimOwnerId[0] = INVALID_CLIENT_ID;
+        synchronized (mLock) {
+            if (!checkClientExists(request.clientId)) {
+                throw new RemoteException("Request cas from unregistered client:"
+                        + request.clientId);
+            }
+            CasResource cas = getCasResource(request.casSystemId);
+            // Unregistered Cas System is treated as having unlimited sessions.
+            if (cas == null) {
+                int resourceHandle = generateResourceHandle(
+                        TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId);
+                cas = new CasResource.Builder(resourceHandle, request.casSystemId)
+                                    .maxSessionNum(Integer.MAX_VALUE)
+                                    .build();
+                addCasResource(cas);
+            }
+            ClientProfile requestClient = getClientProfile(request.clientId);
+            clientPriorityUpdateOnRequest(requestClient);
+            int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+            // Priority max value is 1000
+            int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+            boolean isRequestFromSameProcess = false;
+            if (!cas.isFullyUsed()) {
+                updateCasClientMappingOnNewGrant(request.casSystemId, request.clientId);
+                casSessionHandle[0] = cas.getHandle();
+                return true;
+            }
+            for (int ownerId : cas.getOwnerClientIds()) {
+                // Record the client id with lowest priority that is using the current Cas system.
+                int priority = updateAndGetOwnerClientPriority(ownerId);
+                if (currentLowestPriority > priority) {
+                    lowestPriorityOwnerId = ownerId;
+                    currentLowestPriority = priority;
+                    isRequestFromSameProcess = (requestClient.getProcessId()
+                        == (getClientProfile(ownerId)).getProcessId());
+                }
+            }
+
+            // When all the Cas sessions are occupied, reclaim the lowest priority client if the
+            // request client has higher priority.
+            if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+                    && ((requestClient.getPriority() > currentLowestPriority)
+                    || ((requestClient.getPriority() == currentLowestPriority)
+                    && isRequestFromSameProcess))) {
+                casSessionHandle[0] = cas.getHandle();
+                reclaimOwnerId[0] = lowestPriorityOwnerId;
+                return true;
+            }
+        }
+
         return false;
     }
 
     @VisibleForTesting
-    protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) {
+    protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle)
+            throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")");
         }
-        CiCamResource ciCam = getCiCamResource(request.ciCamId);
-        // Unregistered Cas System is treated as having unlimited sessions.
-        if (ciCam == null) {
-            ciCam = new CiCamResource.Builder(request.ciCamId)
-                                     .maxSessionNum(Integer.MAX_VALUE)
-                                     .build();
-            addCiCamResource(ciCam);
+        int[] reclaimOwnerId = new int[1];
+        if (!claimCiCam(request, ciCamHandle, reclaimOwnerId)) {
+            return false;
         }
-        ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        ClientProfile requestClient = getClientProfile(request.clientId);
-        clientPriorityUpdateOnRequest(requestClient);
-        int lowestPriorityOwnerId = -1;
-        // Priority max value is 1000
-        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
-        boolean isRequestFromSameProcess = false;
-        if (!ciCam.isFullyUsed()) {
-            ciCamHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
-            updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
-            return true;
+        if (ciCamHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return false;
         }
-        for (int ownerId : ciCam.getOwnerClientIds()) {
-            // Record the client id with lowest priority that is using the current Cas system.
-            int priority = updateAndGetOwnerClientPriority(ownerId);
-            if (currentLowestPriority > priority) {
-                lowestPriorityOwnerId = ownerId;
-                currentLowestPriority = priority;
-                isRequestFromSameProcess = (requestClient.getProcessId()
-                    == (getClientProfile(ownerId)).getProcessId());
-            }
-        }
-
-        // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
-        // request client has higher priority.
-        if (lowestPriorityOwnerId > -1 && ((requestClient.getPriority() > currentLowestPriority)
-            || ((requestClient.getPriority() == currentLowestPriority)
-                && isRequestFromSameProcess))) {
-            if (!reclaimResource(lowestPriorityOwnerId,
+        if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+            if (!reclaimResource(reclaimOwnerId[0],
                     TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM)) {
                 return false;
             }
-            ciCamHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCam.getCiCamId());
-            updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
-            return true;
+            synchronized (mLock) {
+                if (getCiCamResource(request.ciCamId).isFullyUsed()) {
+                    Slog.e(TAG, "Reclaimed ciCam still fully used");
+                    return false;
+                }
+                updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+            }
         }
+        return true;
+    }
+
+    protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle,
+            int[] reclaimOwnerId) throws RemoteException {
+        ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        reclaimOwnerId[0] = INVALID_CLIENT_ID;
+        synchronized (mLock) {
+            if (!checkClientExists(request.clientId)) {
+                throw new RemoteException("Request ciCam from unregistered client:"
+                        + request.clientId);
+            }
+            CiCamResource ciCam = getCiCamResource(request.ciCamId);
+            // Unregistered CiCam is treated as having unlimited sessions.
+            if (ciCam == null) {
+                int resourceHandle = generateResourceHandle(
+                        TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId);
+                ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId)
+                                    .maxSessionNum(Integer.MAX_VALUE)
+                                    .build();
+                addCiCamResource(ciCam);
+            }
+            ClientProfile requestClient = getClientProfile(request.clientId);
+            clientPriorityUpdateOnRequest(requestClient);
+            int lowestPriorityOwnerId = INVALID_CLIENT_ID;
+            // Priority max value is 1000
+            int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+            boolean isRequestFromSameProcess = false;
+            if (!ciCam.isFullyUsed()) {
+                updateCiCamClientMappingOnNewGrant(request.ciCamId, request.clientId);
+                ciCamHandle[0] = ciCam.getHandle();
+                return true;
+            }
+            for (int ownerId : ciCam.getOwnerClientIds()) {
+                // Record the client id with lowest priority that is using the current CiCam.
+                int priority = updateAndGetOwnerClientPriority(ownerId);
+                if (currentLowestPriority > priority) {
+                    lowestPriorityOwnerId = ownerId;
+                    currentLowestPriority = priority;
+                    isRequestFromSameProcess = (requestClient.getProcessId()
+                        == (getClientProfile(ownerId)).getProcessId());
+                }
+            }
+
+            // When all the CiCam sessions are occupied, reclaim the lowest priority client if the
+            // request client has higher priority.
+            if (lowestPriorityOwnerId != INVALID_CLIENT_ID
+                    && ((requestClient.getPriority() > currentLowestPriority)
+                    || ((requestClient.getPriority() == currentLowestPriority)
+                    && isRequestFromSameProcess))) {
+                ciCamHandle[0] = ciCam.getHandle();
+                reclaimOwnerId[0] = lowestPriorityOwnerId;
+                return true;
+            }
+        }
+
         return false;
     }
 
@@ -1383,20 +1454,49 @@
     }
 
     @VisibleForTesting
-    protected void releaseFrontendInternal(FrontendResource fe, int clientId) {
+    protected void releaseFrontendInternal(int frontendHandle, int clientId)
+            throws RemoteException {
         if (DEBUG) {
-            Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )");
+            Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )");
         }
-        if (clientId == fe.getOwnerClientId()) {
-            ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
-            if (ownerClient != null) {
-                for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
-                    reclaimResource(shareOwnerId,
-                            TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+        if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND,
+                frontendHandle)) {
+            throw new RemoteException("frontendHandle can't be invalid");
+        }
+        Set<Integer> reclaimedResourceOwnerIds = unclaimFrontend(frontendHandle, clientId);
+        if (reclaimedResourceOwnerIds != null) {
+            for (int shareOwnerId : reclaimedResourceOwnerIds) {
+                reclaimResource(shareOwnerId,
+                        TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
+            }
+        }
+        synchronized (mLock) {
+            clearFrontendAndClientMapping(getClientProfile(clientId));
+        }
+    }
+
+    private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException {
+        Set<Integer> reclaimedResourceOwnerIds = null;
+        synchronized (mLock) {
+            if (!checkClientExists(clientId)) {
+                throw new RemoteException("Release frontend from unregistered client:"
+                        + clientId);
+            }
+            FrontendResource fe = getFrontendResource(frontendHandle);
+            if (fe == null) {
+                throw new RemoteException("Releasing frontend does not exist.");
+            }
+            int ownerClientId = fe.getOwnerClientId();
+            ClientProfile ownerProfile = getClientProfile(ownerClientId);
+            if (ownerClientId == clientId) {
+                reclaimedResourceOwnerIds = ownerProfile.getShareFeClientIds();
+            } else {
+                if (!ownerProfile.getShareFeClientIds().contains(clientId)) {
+                    throw new RemoteException("Client is not a sharee of the releasing fe.");
                 }
             }
         }
-        clearFrontendAndClientMapping(getClientProfile(clientId));
+        return reclaimedResourceOwnerIds;
     }
 
     @VisibleForTesting
@@ -1432,103 +1532,129 @@
     }
 
     @VisibleForTesting
-    protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) {
+    public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request,
+                @NonNull int[] demuxHandle) throws RemoteException {
         if (DEBUG) {
             Slog.d(TAG, "requestDemux(request=" + request + ")");
         }
-
-        // For Tuner 2.0 and below or any HW constraint devices that are unable to support
-        // ITuner.openDemuxById(), demux resources are not really managed under TRM and
-        // mDemuxResources.size() will be zero
-        if (mDemuxResources.size() == 0) {
-            // There are enough Demux resources, so we don't manage Demux in R.
-            demuxHandle[0] =
-                    generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
-            return true;
-        }
-
-        demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        ClientProfile requestClient = getClientProfile(request.clientId);
-
-        if (requestClient == null) {
+        int[] reclaimOwnerId = new int[1];
+        if (!claimDemux(request, demuxHandle, reclaimOwnerId)) {
             return false;
         }
+        if (demuxHandle[0] == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            return false;
+        }
+        if (reclaimOwnerId[0] != INVALID_CLIENT_ID) {
+            if (!reclaimResource(reclaimOwnerId[0],
+                    TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
+                return false;
+            }
+            synchronized (mLock) {
+                if (getDemuxResource(demuxHandle[0]).isInUse()) {
+                    Slog.e(TAG, "Reclaimed demux still in use");
+                    return false;
+                }
+                updateDemuxClientMappingOnNewGrant(demuxHandle[0], request.clientId);
+            }
+        }
+        return true;
+    }
 
-        clientPriorityUpdateOnRequest(requestClient);
-        int grantingDemuxHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        int inUseLowestPriorityDrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
-        // Priority max value is 1000
-        int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
-        boolean isRequestFromSameProcess = false;
-        // If the desired demux id was specified, we only need to check the demux.
-        boolean hasDesiredDemuxCap = request.desiredFilterTypes
-                != DemuxFilterMainType.UNDEFINED;
-        int smallestNumOfSupportedCaps = Integer.SIZE + 1;
-        int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
-        for (DemuxResource dr : getDemuxResources().values()) {
-            if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
-                if (!dr.isInUse()) {
-                    int numOfSupportedCaps = dr.getNumOfCaps();
+    protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId)
+            throws RemoteException {
+        demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        reclaimOwnerId[0] = INVALID_CLIENT_ID;
+        synchronized (mLock) {
+            if (!checkClientExists(request.clientId)) {
+                throw new RemoteException("Request demux from unregistered client:"
+                        + request.clientId);
+            }
 
-                    // look for the demux with minimum caps supporting the desired caps
-                    if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
-                        smallestNumOfSupportedCaps = numOfSupportedCaps;
-                        grantingDemuxHandle = dr.getHandle();
-                    }
-                } else if (grantingDemuxHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-                    // Record the demux id with the lowest client priority among all the
-                    // in use demuxes when no availabledemux has been found.
-                    int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
-                    if (currentLowestPriority >= priority) {
+            // For Tuner 2.0 and below or any HW constraint devices that are unable to support
+            // ITuner.openDemuxById(), demux resources are not really managed under TRM and
+            // mDemuxResources.size() will be zero
+            if (mDemuxResources.size() == 0) {
+                // There are enough Demux resources, so we don't manage Demux in R.
+                demuxHandle[0] =
+                        generateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX, 0);
+                return true;
+            }
+
+            ClientProfile requestClient = getClientProfile(request.clientId);
+            if (requestClient == null) {
+                return false;
+            }
+            clientPriorityUpdateOnRequest(requestClient);
+            DemuxResource grantingDemux = null;
+            DemuxResource inUseLowestPriorityDemux = null;
+            // Priority max value is 1000
+            int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+            boolean isRequestFromSameProcess = false;
+            // If the desired demux id was specified, we only need to check the demux.
+            boolean hasDesiredDemuxCap = request.desiredFilterTypes
+                    != DemuxFilterMainType.UNDEFINED;
+            int smallestNumOfSupportedCaps = Integer.SIZE + 1;
+            int smallestNumOfSupportedCapsInUse = Integer.SIZE + 1;
+            for (DemuxResource dr : getDemuxResources().values()) {
+                if (!hasDesiredDemuxCap || dr.hasSufficientCaps(request.desiredFilterTypes)) {
+                    if (!dr.isInUse()) {
                         int numOfSupportedCaps = dr.getNumOfCaps();
-                        boolean shouldUpdate = false;
-                        // update lowest priority
-                        if (currentLowestPriority > priority) {
-                            currentLowestPriority = priority;
-                            isRequestFromSameProcess = (requestClient.getProcessId()
-                                == (getClientProfile(dr.getOwnerClientId())).getProcessId());
 
-                            // reset the smallest caps when lower priority resource is found
-                            smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
-
-                            shouldUpdate = true;
-                        } else {
-                            // This is the case when the priority is the same as previously found
-                            // one. Update smallest caps when priority.
-                            if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
-                                smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
-                                shouldUpdate = true;
-                            }
+                        // look for the demux with minimum caps supporting the desired caps
+                        if (smallestNumOfSupportedCaps > numOfSupportedCaps) {
+                            smallestNumOfSupportedCaps = numOfSupportedCaps;
+                            grantingDemux = dr;
                         }
-                        if (shouldUpdate) {
-                            inUseLowestPriorityDrHandle = dr.getHandle();
+                    } else if (grantingDemux == null) {
+                        // Record the demux id with the lowest client priority among all the
+                        // in use demuxes when no availabledemux has been found.
+                        int priority = updateAndGetOwnerClientPriority(dr.getOwnerClientId());
+                        if (currentLowestPriority >= priority) {
+                            int numOfSupportedCaps = dr.getNumOfCaps();
+                            boolean shouldUpdate = false;
+                            // update lowest priority
+                            if (currentLowestPriority > priority) {
+                                currentLowestPriority = priority;
+                                isRequestFromSameProcess = (requestClient.getProcessId()
+                                    == (getClientProfile(dr.getOwnerClientId())).getProcessId());
+
+                                // reset the smallest caps when lower priority resource is found
+                                smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+
+                                shouldUpdate = true;
+                            } else {
+                                // This is the case when the priority is the same as previously
+                                // found one. Update smallest caps when priority.
+                                if (smallestNumOfSupportedCapsInUse > numOfSupportedCaps) {
+                                    smallestNumOfSupportedCapsInUse = numOfSupportedCaps;
+                                    shouldUpdate = true;
+                                }
+                            }
+                            if (shouldUpdate) {
+                                inUseLowestPriorityDemux = dr;
+                            }
                         }
                     }
                 }
             }
-        }
 
-        // Grant demux when there is unused resource.
-        if (grantingDemuxHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
-            demuxHandle[0] = grantingDemuxHandle;
-            updateDemuxClientMappingOnNewGrant(grantingDemuxHandle, request.clientId);
-            return true;
-        }
-
-        // When all the resources are occupied, grant the lowest priority resource if the
-        // request client has higher priority.
-        if (inUseLowestPriorityDrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
-            && ((requestClient.getPriority() > currentLowestPriority) || (
-            (requestClient.getPriority() == currentLowestPriority) && isRequestFromSameProcess))) {
-            if (!reclaimResource(
-                    getDemuxResource(inUseLowestPriorityDrHandle).getOwnerClientId(),
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) {
-                return false;
+            // Grant demux when there is unused resource.
+            if (grantingDemux != null) {
+                updateDemuxClientMappingOnNewGrant(grantingDemux.getHandle(), request.clientId);
+                demuxHandle[0] = grantingDemux.getHandle();
+                return true;
             }
-            demuxHandle[0] = inUseLowestPriorityDrHandle;
-            updateDemuxClientMappingOnNewGrant(
-                    inUseLowestPriorityDrHandle, request.clientId);
-            return true;
+
+            // When all the resources are occupied, grant the lowest priority resource if the
+            // request client has higher priority.
+            if (inUseLowestPriorityDemux != null
+                    && ((requestClient.getPriority() > currentLowestPriority) || (
+                    (requestClient.getPriority() == currentLowestPriority)
+                        && isRequestFromSameProcess))) {
+                demuxHandle[0] = inUseLowestPriorityDemux.getHandle();
+                reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId();
+                return true;
+            }
         }
 
         return false;
@@ -1792,7 +1918,9 @@
             return;
         }
 
-        mListeners.put(clientId, record);
+        synchronized (mLock) {
+            mListeners.put(clientId, record);
+        }
     }
 
     @VisibleForTesting
@@ -1808,33 +1936,44 @@
 
         // Reclaim all the resources of the share owners of the frontend that is used by the current
         // resource reclaimed client.
-        ClientProfile profile = getClientProfile(reclaimingClientId);
-        // TODO: check if this check is really needed.
-        if (profile == null) {
-            return true;
-        }
-        Set<Integer> shareFeClientIds = profile.getShareFeClientIds();
-        for (int clientId : shareFeClientIds) {
-            try {
-                mListeners.get(clientId).getListener().onReclaimResources();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
-                return false;
+        Set<Integer> shareFeClientIds;
+        synchronized (mLock) {
+            ClientProfile profile = getClientProfile(reclaimingClientId);
+            if (profile == null) {
+                return true;
             }
-            clearAllResourcesAndClientMapping(getClientProfile(clientId));
+            shareFeClientIds = profile.getShareFeClientIds();
+        }
+        ResourcesReclaimListenerRecord listenerRecord = null;
+        for (int clientId : shareFeClientIds) {
+            synchronized (mLock) {
+                listenerRecord = mListeners.get(clientId);
+            }
+            if (listenerRecord != null) {
+                try {
+                    listenerRecord.getListener().onReclaimResources();
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to reclaim resources on client " + clientId, e);
+                }
+            }
         }
 
         if (DEBUG) {
             Slog.d(TAG, "Reclaiming resources because higher priority client request resource type "
                     + resourceType + ", clientId:" + reclaimingClientId);
         }
-        try {
-            mListeners.get(reclaimingClientId).getListener().onReclaimResources();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
-            return false;
+
+        synchronized (mLock) {
+            listenerRecord = mListeners.get(reclaimingClientId);
         }
-        clearAllResourcesAndClientMapping(profile);
+        if (listenerRecord != null) {
+            try {
+                listenerRecord.getListener().onReclaimResources();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to reclaim resources on client " + reclaimingClientId, e);
+            }
+        }
+
         return true;
     }
 
@@ -2258,6 +2397,7 @@
         addResourcesReclaimListener(clientId, listener);
     }
 
+    @SuppressWarnings("GuardedBy") // Lock is held on mListeners
     private void removeClientProfile(int clientId) {
         for (int shareOwnerId : getClientProfile(clientId).getShareFeClientIds()) {
             clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
@@ -2265,12 +2405,9 @@
         clearAllResourcesAndClientMapping(getClientProfile(clientId));
         mClientProfiles.remove(clientId);
 
-        // it may be called by unregisterClientProfileInternal under test
-        synchronized (mLock) {
-            ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
-            if (record != null) {
-                record.getListener().asBinder().unlinkToDeath(record, 0);
-            }            
+        ResourcesReclaimListenerRecord record = mListeners.remove(clientId);
+        if (record != null) {
+            record.getListener().asBinder().unlinkToDeath(record, 0);
         }
     }
 
@@ -2304,7 +2441,8 @@
         profile.releaseFrontend();
     }
 
-    private void clearAllResourcesAndClientMapping(ClientProfile profile) {
+    @VisibleForTesting
+    protected void clearAllResourcesAndClientMapping(ClientProfile profile) {
         // TODO: check if this check is really needed. Maybe needed for reclaimResource path.
         if (profile == null) {
             return;
diff --git a/services/core/java/com/android/server/uri/TEST_MAPPING b/services/core/java/com/android/server/uri/TEST_MAPPING
index b42d154..0d756bb 100644
--- a/services/core/java/com/android/server/uri/TEST_MAPPING
+++ b/services/core/java/com/android/server/uri/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
     "presubmit": [
         {
-            "name": "FrameworksServicesTests",
-            "options": [
-                {
-                    "include-filter": "com.android.server.uri."
-                }
-            ]
+            "name": "FrameworksServicesTests_android_server_uri"
         },
         {
             "name": "CtsStorageHostTestCases",
diff --git a/services/core/java/com/android/server/utils/TEST_MAPPING b/services/core/java/com/android/server/utils/TEST_MAPPING
index bb7cea9..dcf0049 100644
--- a/services/core/java/com/android/server/utils/TEST_MAPPING
+++ b/services/core/java/com/android/server/utils/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.utils"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_android_server_utils"
     }
   ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ecd140e..96a25da 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,7 +44,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
@@ -324,11 +323,8 @@
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
             // Get only configs as needed to save memory.
             final PersistableBundle carrierConfig =
-                    Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
-                            ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
-                                    VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
-                            : mCarrierConfigManager.getConfigForSubId(subId,
-                                    VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+                    CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+                            VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
             if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
                 mReadySubIdsBySlotId.put(slotId, subId);
 
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
new file mode 100644
index 0000000..b4d3862
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -0,0 +1,146 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.ExternalVibration;
+import android.os.ExternalVibrationScale;
+import android.os.IBinder;
+import android.os.VibrationAttributes;
+import android.os.vibrator.Flags;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * A vibration session holding a single {@link ExternalVibration} request.
+ */
+final class ExternalVibrationSession extends Vibration
+        implements VibrationSession, IBinder.DeathRecipient {
+
+    private final ExternalVibration mExternalVibration;
+    private final ExternalVibrationScale mScale = new ExternalVibrationScale();
+
+    @Nullable
+    private Runnable mBinderDeathCallback;
+
+    ExternalVibrationSession(ExternalVibration externalVibration) {
+        super(externalVibration.getToken(), new CallerInfo(
+                externalVibration.getVibrationAttributes(), externalVibration.getUid(),
+                // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
+                // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
+                Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
+        mExternalVibration = externalVibration;
+    }
+
+    public ExternalVibrationScale getScale() {
+        return mScale;
+    }
+
+    @Override
+    public CallerInfo getCallerInfo() {
+        return callerInfo;
+    }
+
+    @Override
+    public VibrationSession.DebugInfo getDebugInfo() {
+        return new Vibration.DebugInfoImpl(getStatus(), stats, /* playedEffect= */ null,
+                /* originalEffect= */ null, mScale.scaleLevel, mScale.adaptiveHapticsScale,
+                callerInfo);
+    }
+
+    @Override
+    public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
+        return new VibrationStats.StatsInfo(
+                mExternalVibration.getUid(),
+                FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
+                mExternalVibration.getVibrationAttributes().getUsage(), getStatus(), stats,
+                completionUptimeMillis);
+    }
+
+    @Override
+    public boolean isRepeating() {
+        // We don't currently know if the external vibration is repeating, so we just use a
+        // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration.
+        int usage = mExternalVibration.getVibrationAttributes().getUsage();
+        return usage == VibrationAttributes.USAGE_RINGTONE
+                || usage == VibrationAttributes.USAGE_ALARM;
+    }
+
+    @Override
+    public void linkToDeath(Runnable callback) {
+        synchronized (this) {
+            mBinderDeathCallback = callback;
+        }
+        mExternalVibration.linkToDeath(this);
+    }
+
+    @Override
+    public void unlinkToDeath() {
+        mExternalVibration.unlinkToDeath(this);
+        synchronized (this) {
+            mBinderDeathCallback = null;
+        }
+    }
+
+    @Override
+    public void binderDied() {
+        synchronized (this) {
+            if (mBinderDeathCallback != null) {
+                mBinderDeathCallback.run();
+            }
+        }
+    }
+
+    @Override
+    void end(EndInfo endInfo) {
+        super.end(endInfo);
+        if (stats.hasStarted()) {
+            // External vibration doesn't have feedback from total time the vibrator was playing
+            // with non-zero amplitude, so we use the duration between start and end times of
+            // the vibration as the time the vibrator was ON, since the haptic channels are
+            // open for this duration and can receive vibration waveform data.
+            stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
+        }
+    }
+
+    @Override
+    public void notifyEnded() {
+        // Notify external client that this vibration should stop sending data to the vibrator.
+        mExternalVibration.mute();
+    }
+
+    boolean isHoldingSameVibration(ExternalVibration vib) {
+        return mExternalVibration.equals(vib);
+    }
+
+    void muteScale() {
+        mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
+        if (Flags.hapticsScaleV2Enabled()) {
+            mScale.scaleFactor = 0;
+        }
+    }
+
+    void scale(VibrationScaler scaler, int usage) {
+        mScale.scaleLevel = scaler.getScaleLevel(usage);
+        if (Flags.hapticsScaleV2Enabled()) {
+            mScale.scaleFactor = scaler.getScaleFactor(usage);
+        }
+        mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
+        stats.reportAdaptiveScale(mScale.adaptiveHapticsScale);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 46bd7af..ea4bd01 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -51,37 +51,22 @@
     @NonNull
     private volatile CombinedVibration mEffectToPlay;
 
-    /** Vibration status. */
-    private Vibration.Status mStatus;
-
     /** Reported scale values applied to the vibration effects. */
     private int mScaleLevel;
     private float mAdaptiveScale;
 
     HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect,
-            @NonNull CallerInfo callerInfo) {
+            @NonNull VibrationSession.CallerInfo callerInfo) {
         super(token, callerInfo);
         mOriginalEffect = effect;
         mEffectToPlay = effect;
-        mStatus = Vibration.Status.RUNNING;
         mScaleLevel = VibrationScaler.SCALE_NONE;
         mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE;
     }
 
-    /**
-     * Set the {@link Status} of this vibration and reports the current system time as this
-     * vibration end time, for debugging purposes.
-     *
-     * <p>This method will only accept given value if the current status is {@link
-     * Status#RUNNING}.
-     */
-    public void end(EndInfo info) {
-        if (hasEnded()) {
-            // Vibration already ended, keep first ending status set and ignore this one.
-            return;
-        }
-        mStatus = info.status;
-        stats.reportEnded(info.endedBy);
+    @Override
+    public void end(EndInfo endInfo) {
+        super.end(endInfo);
         mCompletionLatch.countDown();
     }
 
@@ -144,11 +129,6 @@
         // No need to update fallback effects, they are already configured per device.
     }
 
-    /** Return true is current status is different from {@link Status#RUNNING}. */
-    public boolean hasEnded() {
-        return mStatus != Status.RUNNING;
-    }
-
     @Override
     public boolean isRepeating() {
         return mOriginalEffect.getDuration() == Long.MAX_VALUE;
@@ -159,22 +139,24 @@
         return mEffectToPlay;
     }
 
-    /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */
-    public Vibration.DebugInfo getDebugInfo() {
+    @Override
+    public VibrationSession.DebugInfo getDebugInfo() {
         // Clear the original effect if it's the same as the effect that was played, for simplicity
         CombinedVibration originalEffect =
                 Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect;
-        return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect,
+        return new Vibration.DebugInfoImpl(getStatus(), stats, mEffectToPlay, originalEffect,
                 mScaleLevel, mAdaptiveScale, callerInfo);
     }
 
-    /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
+    @Override
     public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
-        int vibrationType = isRepeating()
-                ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
-                : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
+        int vibrationType = mEffectToPlay.hasVendorEffects()
+                ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR
+                : isRepeating()
+                        ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED
+                        : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE;
         return new VibrationStats.StatsInfo(
-                callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus,
+                callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), getStatus(),
                 stats, completionUptimeMillis);
     }
 
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
index 65fc7b2..d10ef31 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -16,6 +16,7 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -28,7 +29,10 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
+import android.view.InputDevice;
 
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.vibrator.persistence.XmlParserException;
 import com.android.internal.vibrator.persistence.XmlReader;
@@ -42,6 +46,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
+import java.util.Locale;
 
 /**
  * Class that loads custom {@link VibrationEffect} to be performed for each
@@ -92,105 +97,146 @@
     private static final String ATTRIBUTE_ID = "id";
 
     /**
-     * Parses the haptic feedback vibration customization XML file for the device, and provides a
-     * mapping of the customized effect IDs to their respective {@link VibrationEffect}s.
-     *
-     * <p>This is potentially expensive, so avoid calling repeatedly. One call is enough, and the
-     * caller should process the returned mapping (if any) for further queries.
-     *
-     * @param res {@link Resources} object to be used for reading the device's resources.
-     * @return a {@link SparseArray} that maps each customized haptic feedback effect ID to its
-     *      respective {@link VibrationEffect}, or {@code null}, if the device has not configured
-     *      a file for haptic feedback constants customization.
-     * @throws {@link IOException} if an IO error occurs while parsing the customization XML.
-     * @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing
-     *      the XML, like an invalid XML content or an invalid haptic feedback constant.
+     * A {@link SparseArray} that maps each customized haptic feedback effect ID to its
+     * respective {@link VibrationEffect}. If this is empty, system's default vibration will be
+     * used.
      */
-    @Nullable
-    static SparseArray<VibrationEffect> loadVibrations(Resources res, VibratorInfo vibratorInfo)
-            throws CustomizationParserException, IOException {
-        try {
-            return loadVibrationsInternal(res, vibratorInfo);
-        } catch (VibrationXmlParser.ParseFailedException
-                 | XmlParserException
-                 | XmlPullParserException e) {
-            throw new CustomizationParserException(
-                    "Error parsing haptic feedback customization file.", e);
+    @NonNull
+    private final SparseArray<VibrationEffect> mHapticCustomizations;
+
+    /**
+     * A {@link SparseArray} similar to {@link mHapticCustomizations} but for rotary input source
+     * specific customization.
+     */
+    @NonNull
+    private final SparseArray<VibrationEffect> mHapticCustomizationsForSourceRotary;
+
+    /**
+     * A {@link SparseArray} similar to {@link mHapticCustomizations} but for touch screen input
+     * source specific customization.
+     */
+    @NonNull
+    private final SparseArray<VibrationEffect> mHapticCustomizationsForSourceTouchScreen;
+
+    HapticFeedbackCustomization(Resources res, VibratorInfo vibratorInfo) {
+        if (!Flags.hapticFeedbackVibrationOemCustomizationEnabled()) {
+            Slog.d(TAG, "Haptic feedback customization feature is not enabled.");
+            mHapticCustomizations = new SparseArray<>();
+            mHapticCustomizationsForSourceRotary = new SparseArray<>();
+            mHapticCustomizationsForSourceTouchScreen = new SparseArray<>();
+            return;
+        }
+
+        // Load base customizations.
+        SparseArray<VibrationEffect> hapticCustomizations;
+        hapticCustomizations = loadCustomizedFeedbackVibrationFromFile(res, vibratorInfo);
+        if (hapticCustomizations.size() == 0) {
+            // Input source customized haptic feedback was directly added in res. So, no need to old
+            // loading path.
+            hapticCustomizations = loadCustomizedFeedbackVibrationFromRes(res, vibratorInfo,
+                    R.xml.haptic_feedback_customization);
+        }
+        mHapticCustomizations = hapticCustomizations;
+
+        // Load customizations specified by input sources.
+        if (android.os.vibrator.Flags.hapticFeedbackInputSourceCustomizationEnabled()) {
+            mHapticCustomizationsForSourceRotary =
+                    loadCustomizedFeedbackVibrationFromRes(res, vibratorInfo,
+                            R.xml.haptic_feedback_customization_source_rotary_encoder);
+            mHapticCustomizationsForSourceTouchScreen =
+                    loadCustomizedFeedbackVibrationFromRes(res, vibratorInfo,
+                            R.xml.haptic_feedback_customization_source_touchscreen);
+        } else {
+            mHapticCustomizationsForSourceRotary = new SparseArray<>();
+            mHapticCustomizationsForSourceTouchScreen = new SparseArray<>();
         }
     }
 
+    @VisibleForTesting
+    HapticFeedbackCustomization(@NonNull SparseArray<VibrationEffect> hapticCustomizations,
+            @NonNull SparseArray<VibrationEffect> hapticCustomizationsForSourceRotary,
+            @NonNull SparseArray<VibrationEffect> hapticCustomizationsForSourceTouchScreen) {
+        mHapticCustomizations = hapticCustomizations;
+        mHapticCustomizationsForSourceRotary = hapticCustomizationsForSourceRotary;
+        mHapticCustomizationsForSourceTouchScreen = hapticCustomizationsForSourceTouchScreen;
+    }
+
     @Nullable
-    private static SparseArray<VibrationEffect> loadVibrationsInternal(
-            Resources res, VibratorInfo vibratorInfo) throws
-                    CustomizationParserException,
-                    IOException,
-                    XmlParserException,
-                    XmlPullParserException {
-        if (!Flags.hapticFeedbackVibrationOemCustomizationEnabled()) {
-            Slog.d(TAG, "Haptic feedback customization feature is not enabled.");
-            return null;
+    VibrationEffect getEffect(int effectId) {
+        return mHapticCustomizations.get(effectId);
+    }
+
+    @Nullable
+    VibrationEffect getEffect(int effectId, int inputSource) {
+        VibrationEffect resultVibration = null;
+        if ((InputDevice.SOURCE_ROTARY_ENCODER & inputSource) != 0) {
+            resultVibration = mHapticCustomizationsForSourceRotary.get(effectId);
+        } else if ((InputDevice.SOURCE_TOUCHSCREEN & inputSource) != 0) {
+            resultVibration = mHapticCustomizationsForSourceTouchScreen.get(effectId);
         }
-
-        // Old loading path that reads customization from file at dir defined by config.
-        TypedXmlPullParser parser = readCustomizationFile(res);
-        if (parser == null) {
-            // When old loading path doesn't succeed, try loading customization from resources.
-            parser = readCustomizationResources(res);
+        if (resultVibration == null) {
+            resultVibration = mHapticCustomizations.get(effectId);
         }
-        if (parser == null) {
-            Slog.d(TAG, "No loadable haptic feedback customization.");
-            return null;
-        }
+        return resultVibration;
+    }
 
-        XmlUtils.beginDocument(parser, TAG_CONSTANTS);
-        XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
-        int rootDepth = parser.getDepth();
-
-        SparseArray<VibrationEffect> mapping = new SparseArray<>();
-        while (XmlReader.readNextTagWithin(parser, rootDepth)) {
-            XmlValidator.checkStartTag(parser, TAG_CONSTANT);
-            int customizationDepth = parser.getDepth();
-
-            // Only attribute in tag is the `id` attribute.
-            XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_ID);
-            int effectId = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_ID);
-            if (mapping.contains(effectId)) {
-                throw new CustomizationParserException(
-                        "Multiple customizations found for effect " + effectId);
+    /**
+     * Parses the haptic feedback vibration customization XML file for the device whose directory is
+     * specified by config. See {@link R.string.config_hapticFeedbackCustomizationFile}.
+     *
+     * @return Return a mapping of the customized effect IDs to their respective
+     * {@link VibrationEffect}s.
+     */
+    @NonNull
+    private static SparseArray<VibrationEffect> loadCustomizedFeedbackVibrationFromFile(
+            Resources res, VibratorInfo vibratorInfo) {
+        try {
+            TypedXmlPullParser parser = readCustomizationFile(res);
+            if (parser == null) {
+                Slog.d(TAG, "No loadable haptic feedback customization from file.");
+                return new SparseArray<>();
             }
-
-            // Move the parser one step into the `<constant>` tag.
-            XmlValidator.checkParserCondition(
-                    XmlReader.readNextTagWithin(parser, customizationDepth),
-                    "Unsupported empty customization tag for effect " + effectId);
-
-            ParsedVibration parsedVibration = VibrationXmlParser.parseElement(
-                    parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
-            VibrationEffect effect = parsedVibration.resolve(vibratorInfo);
-            if (effect != null) {
-                if (effect.getDuration() == Long.MAX_VALUE) {
-                    throw new CustomizationParserException(String.format(
-                            "Vibration for effect ID %d is repeating, which is not allowed as a"
-                            + " haptic feedback: %s", effectId, effect));
-                }
-                mapping.put(effectId, effect);
-            }
-
-            XmlReader.readEndTag(parser, TAG_CONSTANT, customizationDepth);
+            return parseVibrations(parser, vibratorInfo);
+        } catch (XmlPullParserException | XmlParserException | IOException e) {
+            Slog.e(TAG, "Error parsing haptic feedback customizations from file", e);
+            return new SparseArray<>();
         }
+    }
 
-        // Make checks that the XML ends well.
-        XmlReader.readEndTag(parser, TAG_CONSTANTS, rootDepth);
-        XmlReader.readDocumentEndTag(parser);
-
-        return mapping;
+    /**
+     * Parses the haptic feedback vibration customization XML resource for the device.
+     *
+     * @return Return a mapping of the customized effect IDs to their respective
+     * {@link VibrationEffect}s.
+     */
+    @NonNull
+    private static SparseArray<VibrationEffect> loadCustomizedFeedbackVibrationFromRes(
+            Resources res, VibratorInfo vibratorInfo, int xmlResId) {
+        try {
+            TypedXmlPullParser parser = readCustomizationResources(res, xmlResId);
+            if (parser == null) {
+                Slog.d(TAG, "No loadable haptic feedback customization from res.");
+                return new SparseArray<>();
+            }
+            return parseVibrations(parser, vibratorInfo);
+        } catch (XmlPullParserException | XmlParserException | IOException e) {
+            Slog.e(TAG, "Error parsing haptic feedback customizations from res", e);
+            return new SparseArray<>();
+        }
     }
 
     // TODO(b/356412421): deprecate old path related files.
     private static TypedXmlPullParser readCustomizationFile(Resources res)
             throws XmlPullParserException {
-        String customizationFile = res.getString(
-                com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
+        String customizationFile;
+        try {
+            customizationFile = res.getString(
+                    R.string.config_hapticFeedbackCustomizationFile);
+        } catch (Resources.NotFoundException e) {
+            Slog.e(TAG, "Customization file directory config not found.", e);
+            return null;
+        }
+
         if (TextUtils.isEmpty(customizationFile)) {
             return null;
         }
@@ -211,13 +257,14 @@
         return parser;
     }
 
-    private static TypedXmlPullParser readCustomizationResources(Resources res) {
+    @Nullable
+    private static TypedXmlPullParser readCustomizationResources(Resources res, int xmlResId) {
         if (!Flags.loadHapticFeedbackVibrationCustomizationFromResources()) {
             return null;
         }
         final XmlResourceParser resParser;
         try {
-            resParser = res.getXml(com.android.internal.R.xml.haptic_feedback_customization);
+            resParser = res.getXml(xmlResId);
         } catch (Resources.NotFoundException e) {
             Slog.e(TAG, "Haptic customization resource not found.", e);
             return null;
@@ -226,16 +273,52 @@
         return XmlUtils.makeTyped(resParser);
     }
 
-    /**
-     * Represents an error while parsing a haptic feedback customization XML.
-     */
-    static final class CustomizationParserException extends Exception {
-        private CustomizationParserException(String message) {
-            super(message);
+    @NonNull
+    private static SparseArray<VibrationEffect> parseVibrations(TypedXmlPullParser parser,
+            VibratorInfo vibratorInfo)
+            throws XmlPullParserException, IOException, XmlParserException {
+        XmlUtils.beginDocument(parser, TAG_CONSTANTS);
+        XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+        int rootDepth = parser.getDepth();
+
+        SparseArray<VibrationEffect> mapping = new SparseArray<>();
+        while (XmlReader.readNextTagWithin(parser, rootDepth)) {
+            XmlValidator.checkStartTag(parser, TAG_CONSTANT);
+            int customizationDepth = parser.getDepth();
+
+            // Only attribute in tag is the `id` attribute.
+            XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_ID);
+            int effectId = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_ID);
+            if (mapping.contains(effectId)) {
+                Slog.e(TAG, "Multiple customizations found for effect " + effectId);
+                return new SparseArray<>();
+            }
+
+            // Move the parser one step into the `<constant>` tag.
+            XmlValidator.checkParserCondition(
+                    XmlReader.readNextTagWithin(parser, customizationDepth),
+                    "Unsupported empty customization tag for effect " + effectId);
+
+            ParsedVibration parsedVibration = VibrationXmlParser.parseElement(
+                    parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+            VibrationEffect effect = parsedVibration.resolve(vibratorInfo);
+            if (effect != null) {
+                if (effect.getDuration() == Long.MAX_VALUE) {
+                    Slog.e(TAG, String.format(Locale.getDefault(),
+                            "Vibration for effect ID %d is repeating, which is not allowed as a"
+                                    + " haptic feedback: %s", effectId, effect));
+                    return new SparseArray<>();
+                }
+                mapping.put(effectId, effect);
+            }
+
+            XmlReader.readEndTag(parser, TAG_CONSTANT, customizationDepth);
         }
 
-        private CustomizationParserException(String message, Throwable cause) {
-            super(message, cause);
-        }
+        // Make checks that the XML ends well.
+        XmlReader.readEndTag(parser, TAG_CONSTANTS, rootDepth);
+        XmlReader.readDocumentEndTag(parser);
+
+        return mapping;
     }
 }
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
index eccbffb..cae6b34 100644
--- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -16,19 +16,17 @@
 
 package com.android.server.vibrator;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.os.VibratorInfo;
-import android.util.Slog;
-import android.util.SparseArray;
 import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.io.IOException;
 import java.io.PrintWriter;
 
 /**
@@ -52,39 +50,29 @@
     private final boolean mHapticTextHandleEnabled;
     // Vibrator effect for haptic feedback during boot when safe mode is enabled.
     private final VibrationEffect mSafeModeEnabledVibrationEffect;
-    // Haptic feedback vibration customizations specific to the device.
-    // If present and valid, a vibration here will be used for an effect.
-    // Otherwise, the system's default vibration will be used.
-    @Nullable private final SparseArray<VibrationEffect> mHapticCustomizations;
+
+    private final HapticFeedbackCustomization mHapticFeedbackCustomization;
 
     private float mKeyboardVibrationFixedAmplitude;
 
-    public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
-        this(res, vibrator.getInfo());
-    }
-
     public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) {
-        this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo));
+        this(res, vibratorInfo, new HapticFeedbackCustomization(res, vibratorInfo));
     }
 
-    @VisibleForTesting HapticFeedbackVibrationProvider(
-            Resources res,
-            VibratorInfo vibratorInfo,
-            @Nullable SparseArray<VibrationEffect> hapticCustomizations) {
+    @VisibleForTesting
+    HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo,
+            HapticFeedbackCustomization hapticFeedbackCustomization) {
         mVibratorInfo = vibratorInfo;
         mHapticTextHandleEnabled = res.getBoolean(
                 com.android.internal.R.bool.config_enableHapticTextHandle);
+        mHapticFeedbackCustomization = hapticFeedbackCustomization;
 
-        if (hapticCustomizations != null && hapticCustomizations.size() == 0) {
-            hapticCustomizations = null;
-        }
-        mHapticCustomizations = hapticCustomizations;
-        mSafeModeEnabledVibrationEffect =
-                effectHasCustomization(HapticFeedbackConstants.SAFE_MODE_ENABLED)
-                        ? mHapticCustomizations.get(HapticFeedbackConstants.SAFE_MODE_ENABLED)
-                        : VibrationSettings.createEffectFromResource(
-                                res,
-                                com.android.internal.R.array.config_safeModeEnabledVibePattern);
+        VibrationEffect safeModeVibration = mHapticFeedbackCustomization.getEffect(
+                HapticFeedbackConstants.SAFE_MODE_ENABLED);
+        mSafeModeEnabledVibrationEffect = safeModeVibration != null ? safeModeVibration
+                : VibrationSettings.createEffectFromResource(res,
+                        com.android.internal.R.array.config_safeModeEnabledVibePattern);
+
         mKeyboardVibrationFixedAmplitude = res.getFloat(
                 com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude);
         if (mKeyboardVibrationFixedAmplitude < 0 || mKeyboardVibrationFixedAmplitude > 1) {
@@ -100,89 +88,41 @@
      * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
      *          the provided effect ID is not supported.
      */
-    @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
-        switch (effectId) {
-            case HapticFeedbackConstants.CONTEXT_CLICK:
-            case HapticFeedbackConstants.GESTURE_END:
-            case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
-            case HapticFeedbackConstants.SCROLL_TICK:
-            case HapticFeedbackConstants.SEGMENT_TICK:
-                return getVibration(effectId, VibrationEffect.EFFECT_TICK);
-
-            case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
-                if (!mHapticTextHandleEnabled) {
-                    return null;
-                }
-                // fallthrough
-            case HapticFeedbackConstants.CLOCK_TICK:
-            case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
-                return getVibration(effectId, VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.KEYBOARD_RELEASE:
-            case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
-                return getKeyboardVibration(effectId);
-
-            case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
-            case HapticFeedbackConstants.DRAG_CROSSING:
-                return getVibration(
-                        effectId,
-                        VibrationEffect.EFFECT_TICK,
-                        /* fallbackForPredefinedEffect= */ false);
-
-            case HapticFeedbackConstants.VIRTUAL_KEY:
-            case HapticFeedbackConstants.EDGE_RELEASE:
-            case HapticFeedbackConstants.CALENDAR_DATE:
-            case HapticFeedbackConstants.CONFIRM:
-            case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
-            case HapticFeedbackConstants.GESTURE_START:
-            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
-            case HapticFeedbackConstants.SCROLL_LIMIT:
-                return getVibration(effectId, VibrationEffect.EFFECT_CLICK);
-
-            case HapticFeedbackConstants.LONG_PRESS:
-            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
-            case HapticFeedbackConstants.DRAG_START:
-            case HapticFeedbackConstants.EDGE_SQUEEZE:
-                return getVibration(effectId, VibrationEffect.EFFECT_HEAVY_CLICK);
-
-            case HapticFeedbackConstants.REJECT:
-            case HapticFeedbackConstants.BIOMETRIC_REJECT:
-                return getVibration(effectId, VibrationEffect.EFFECT_DOUBLE_CLICK);
-
-            case HapticFeedbackConstants.SAFE_MODE_ENABLED:
-                return mSafeModeEnabledVibrationEffect;
-
-            case HapticFeedbackConstants.ASSISTANT_BUTTON:
-                return getAssistantButtonVibration();
-
-            case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
-                return getVibration(
-                        effectId,
-                        VibrationEffect.Composition.PRIMITIVE_TICK,
-                        /* primitiveScale= */ 0.4f,
-                        VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.TOGGLE_ON:
-                return getVibration(
-                        effectId,
-                        VibrationEffect.Composition.PRIMITIVE_TICK,
-                        /* primitiveScale= */ 0.5f,
-                        VibrationEffect.EFFECT_TICK);
-
-            case HapticFeedbackConstants.TOGGLE_OFF:
-                return getVibration(
-                        effectId,
-                        VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
-                        /* primitiveScale= */ 0.2f,
-                        VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.NO_HAPTICS:
-            default:
-                return null;
+    @Nullable public VibrationEffect getVibration(int effectId) {
+        if (!isFeedbackConstantEnabled(effectId)) {
+            return null;
         }
+        VibrationEffect customizedVibration = mHapticFeedbackCustomization.getEffect(effectId);
+        if (customizedVibration != null) {
+            return customizedVibration;
+        }
+        return getVibrationForHapticFeedback(effectId);
     }
 
     /**
+     * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
+     * {@link HapticFeedbackConstants}).
+     *
+     * @param effectId    the haptic feedback effect ID whose respective vibration we want to get.
+     * @param inputSource the {@link InputDevice.Source} that customizes the haptic feedback
+     *                    corresponding to the {@code effectId}.
+     * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
+     * the provided effect ID is not supported.
+     */
+    @Nullable public VibrationEffect getVibration(int effectId, int inputSource) {
+        if (!isFeedbackConstantEnabled(effectId)) {
+            return null;
+        }
+        VibrationEffect customizedVibration = mHapticFeedbackCustomization.getEffect(effectId,
+                inputSource);
+        if (customizedVibration != null) {
+            return customizedVibration;
+        }
+        return getVibrationForHapticFeedback(effectId);
+    }
+
+    // TODO(b/354049335): handle input source customized VibrationAttributes.
+    /**
      * Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
      *
      * @param effectId the haptic feedback effect ID whose respective vibration attributes we want
@@ -255,61 +195,106 @@
         pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
     }
 
-    private VibrationEffect getVibration(int effectId, int predefinedVibrationEffectId) {
-        return getVibration(
-                effectId, predefinedVibrationEffectId, /* fallbackForPredefinedEffect= */ true);
+    private boolean isFeedbackConstantEnabled(int effectId) {
+        return switch (effectId) {
+            case HapticFeedbackConstants.TEXT_HANDLE_MOVE -> mHapticTextHandleEnabled;
+            case HapticFeedbackConstants.NO_HAPTICS -> false;
+            default -> true;
+        };
     }
 
     /**
-     * Returns the customized vibration for {@code hapticFeedbackId}, or
-     * {@code predefinedVibrationEffectId} if a customization does not exist for the haptic
-     * feedback.
-     *
-     * <p>If a customization does not exist and the default predefined effect is to be returned,
-     * {@code fallbackForPredefinedEffect} will be used to decide whether or not to fallback
-     * to a generic pattern if the predefined effect is not hardware supported.
-     *
-     * @see VibrationEffect#get(int, boolean)
+     * Get {@link VibrationEffect} respective {@code effectId} from platform-wise mapping. This
+     * method doesn't include OEM customizations.
      */
-    private VibrationEffect getVibration(
-            int hapticFeedbackId,
-            int predefinedVibrationEffectId,
-            boolean fallbackForPredefinedEffect) {
-        if (effectHasCustomization(hapticFeedbackId)) {
-            return mHapticCustomizations.get(hapticFeedbackId);
+    @Nullable
+    private VibrationEffect getVibrationForHapticFeedback(int effectId) {
+        switch (effectId) {
+            case HapticFeedbackConstants.CONTEXT_CLICK:
+            case HapticFeedbackConstants.GESTURE_END:
+            case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
+            case HapticFeedbackConstants.SCROLL_TICK:
+            case HapticFeedbackConstants.SEGMENT_TICK:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
+            case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+            case HapticFeedbackConstants.CLOCK_TICK:
+            case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.KEYBOARD_RELEASE:
+            case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+                // keyboard effect is not customized by the input source.
+                return getKeyboardVibration(effectId);
+
+            case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+            case HapticFeedbackConstants.DRAG_CROSSING:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, /* fallback= */ false);
+
+            case HapticFeedbackConstants.VIRTUAL_KEY:
+            case HapticFeedbackConstants.EDGE_RELEASE:
+            case HapticFeedbackConstants.CALENDAR_DATE:
+            case HapticFeedbackConstants.CONFIRM:
+            case HapticFeedbackConstants.BIOMETRIC_CONFIRM:
+            case HapticFeedbackConstants.GESTURE_START:
+            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+            case HapticFeedbackConstants.SCROLL_LIMIT:
+                return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+            case HapticFeedbackConstants.LONG_PRESS:
+            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+            case HapticFeedbackConstants.DRAG_START:
+            case HapticFeedbackConstants.EDGE_SQUEEZE:
+                return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+            case HapticFeedbackConstants.REJECT:
+            case HapticFeedbackConstants.BIOMETRIC_REJECT:
+                return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+            case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+                // safe mode effect is not customized by the input source.
+                return mSafeModeEnabledVibrationEffect;
+
+            case HapticFeedbackConstants.ASSISTANT_BUTTON:
+                // assistant effect is not customized by the input source.
+                return getAssistantButtonVibration();
+
+            case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
+                return getVibration(
+                        VibrationEffect.Composition.PRIMITIVE_TICK,
+                        /* primitiveScale= */ 0.4f,
+                        VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.TOGGLE_ON:
+                return getVibration(
+                        VibrationEffect.Composition.PRIMITIVE_TICK,/* primitiveScale= */ 0.5f,
+                        VibrationEffect.EFFECT_TICK);
+
+            case HapticFeedbackConstants.TOGGLE_OFF:
+                return getVibration(
+                        VibrationEffect.Composition.PRIMITIVE_LOW_TICK,
+                        /* primitiveScale= */ 0.2f,
+                        VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.NO_HAPTICS:
+            default:
+                return null;
         }
-        return VibrationEffect.get(predefinedVibrationEffectId, fallbackForPredefinedEffect);
     }
 
-    /**
-     * Returns the customized vibration for {@code hapticFeedbackId}, or some fallback vibration if
-     * a customization does not exist for the ID.
-     *
-     * <p>The fallback will be a primitive composition formed of {@code primitiveId} and
-     * {@code primitiveScale}, if the primitive is supported. Otherwise, it will be a predefined
-     * vibration of {@code elsePredefinedVibrationEffectId}.
-     */
-    private VibrationEffect getVibration(
-            int hapticFeedbackId,
-            int primitiveId,
-            float primitiveScale,
-            int elsePredefinedVibrationEffectId) {
-        if (effectHasCustomization(hapticFeedbackId)) {
-            return mHapticCustomizations.get(hapticFeedbackId);
-        }
+    @NonNull
+    private VibrationEffect getVibration(int primitiveId, float primitiveScale,
+            int predefinedVibrationEffectId) {
         if (mVibratorInfo.isPrimitiveSupported(primitiveId)) {
             return VibrationEffect.startComposition()
                     .addPrimitive(primitiveId, primitiveScale)
                     .compose();
-        } else {
-            return VibrationEffect.get(elsePredefinedVibrationEffectId);
         }
+        return VibrationEffect.get(predefinedVibrationEffectId);
     }
 
+    @NonNull
     private VibrationEffect getAssistantButtonVibration() {
-        if (effectHasCustomization(HapticFeedbackConstants.ASSISTANT_BUTTON)) {
-            return mHapticCustomizations.get(HapticFeedbackConstants.ASSISTANT_BUTTON);
-        }
         if (mVibratorInfo.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
                 && mVibratorInfo.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)) {
             // quiet ramp, short pause, then sharp tick
@@ -322,15 +307,8 @@
         return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
     }
 
-    private boolean effectHasCustomization(int effectId) {
-        return mHapticCustomizations != null && mHapticCustomizations.contains(effectId);
-    }
-
+    @NonNull
     private VibrationEffect getKeyboardVibration(int effectId) {
-        if (effectHasCustomization(effectId)) {
-            return mHapticCustomizations.get(effectId);
-        }
-
         int primitiveId;
         int predefinedEffectId;
         boolean predefinedEffectFallback;
@@ -354,8 +332,7 @@
                         .compose();
             }
         }
-        return getVibration(effectId, predefinedEffectId,
-                /* fallbackForPredefinedEffect= */ predefinedEffectFallback);
+        return VibrationEffect.get(predefinedEffectId, predefinedEffectFallback);
     }
 
     private VibrationAttributes createKeyboardVibrationAttributes(
@@ -364,21 +341,7 @@
         if ((privFlags & HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS) == 0) {
             return TOUCH_VIBRATION_ATTRIBUTES;
         }
-        return new VibrationAttributes.Builder(IME_FEEDBACK_VIBRATION_ATTRIBUTES)
-                // TODO(b/332661766): Remove CATEGORY_KEYBOARD logic
-                .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
-                .build();
-    }
-
-    @Nullable
-    private static SparseArray<VibrationEffect> loadHapticCustomizations(
-            Resources res, VibratorInfo vibratorInfo) {
-        try {
-            return HapticFeedbackCustomization.loadVibrations(res, vibratorInfo);
-        } catch (IOException | HapticFeedbackCustomization.CustomizationParserException e) {
-            Slog.e(TAG, "Unable to load haptic customizations.", e);
-            return null;
-        }
+        return IME_FEEDBACK_VIBRATION_ATTRIBUTES;
     }
 
     private static boolean shouldBypassInterruptionPolicy(int effectId) {
diff --git a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
index 4e58b9a..83e05f4 100644
--- a/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
+++ b/services/core/java/com/android/server/vibrator/InputDeviceDelegate.java
@@ -26,6 +26,7 @@
 import android.view.InputDevice;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
 
 /** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */
 final class InputDeviceDelegate implements InputManager.InputDeviceListener {
@@ -93,7 +94,7 @@
      *
      * @return {@link #isAvailable()}
      */
-    public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) {
+    public boolean vibrateIfAvailable(CallerInfo callerInfo, CombinedVibration effect) {
         synchronized (mLock) {
             for (int i = 0; i < mInputDeviceVibrators.size(); i++) {
                 mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect,
diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
index 8f36118..407f3d9 100644
--- a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
@@ -52,6 +52,7 @@
             long vibratorOnResult = controller.on(effect, getVibration().id);
             vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS);
             handleVibratorOnResult(vibratorOnResult);
+            getVibration().stats.reportPerformVendorEffect(vibratorOnResult);
             return List.of(new CompleteEffectVibratorStep(conductor, startTime,
                     /* cancelled= */ false, controller, mPendingVibratorOffDeadline));
         } finally {
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 5c567da..9a04793 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -31,7 +31,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.proto.ProtoOutputStream;
 
-import java.io.PrintWriter;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
@@ -52,131 +51,69 @@
     private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
 
     public final long id;
-    public final CallerInfo callerInfo;
+    public final VibrationSession.CallerInfo callerInfo;
     public final VibrationStats stats = new VibrationStats();
     public final IBinder callerToken;
 
-    /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */
-    enum Status {
-        UNKNOWN(VibrationProto.UNKNOWN),
-        RUNNING(VibrationProto.RUNNING),
-        FINISHED(VibrationProto.FINISHED),
-        FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED),
-        FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES),
-        CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED),
-        CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF),
-        CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE),
-        CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
-        CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER),
-        CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
-        CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
-        CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS),
-        IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
-        IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
-        IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
-        IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN),
-        IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS),
-        IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND),
-        IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION),
-        IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED),
-        IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL),
-        IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE),
-        IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING),
-        IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
-        IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
-        IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
-        IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED),
-        IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE),
-        IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER);
+    private VibrationSession.Status mStatus;
 
-        private final int mProtoEnumValue;
-
-        Status(int value) {
-            mProtoEnumValue = value;
-        }
-
-        public int getProtoEnumValue() {
-            return mProtoEnumValue;
-        }
-    }
-
-    Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) {
+    Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) {
         Objects.requireNonNull(token);
         Objects.requireNonNull(callerInfo);
+        mStatus = VibrationSession.Status.RUNNING;
         this.id = sNextVibrationId.getAndIncrement();
         this.callerToken = token;
         this.callerInfo = callerInfo;
     }
 
+    VibrationSession.Status getStatus() {
+        return mStatus;
+    }
+
+    /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */
+    boolean hasEnded() {
+        return mStatus != VibrationSession.Status.RUNNING;
+    }
+
+    /**
+     * Set the {@link VibrationSession} of this vibration and reports the current system time as
+     * this vibration end time, for debugging purposes.
+     *
+     * <p>This method will only accept given value if the current status is {@link
+     * VibrationSession.Status#RUNNING}.
+     */
+    void end(Vibration.EndInfo endInfo) {
+        if (hasEnded()) {
+            // Vibration already ended, keep first ending status set and ignore this one.
+            return;
+        }
+        mStatus = endInfo.status;
+        stats.reportEnded(endInfo.endedBy);
+    }
+
     /** Return true if vibration is a repeating vibration. */
     abstract boolean isRepeating();
 
-    /**
-     * Holds lightweight immutable info on the process that triggered the vibration. This data
-     * could potentially be kept in memory for a long time for bugreport dumpsys operations.
-     *
-     * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to
-     * potentially expensive or resource-linked objects, such as {@link IBinder}.
-     */
-    static final class CallerInfo {
-        public final VibrationAttributes attrs;
-        public final int uid;
-        public final int deviceId;
-        public final String opPkg;
-        public final String reason;
+    /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */
+    abstract VibrationSession.DebugInfo getDebugInfo();
 
-        CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
-                String reason) {
-            Objects.requireNonNull(attrs);
-            this.attrs = attrs;
-            this.uid = uid;
-            this.deviceId = deviceId;
-            this.opPkg = opPkg;
-            this.reason = reason;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof CallerInfo)) return false;
-            CallerInfo that = (CallerInfo) o;
-            return Objects.equals(attrs, that.attrs)
-                    && uid == that.uid
-                    && deviceId == that.deviceId
-                    && Objects.equals(opPkg, that.opPkg)
-                    && Objects.equals(reason, that.reason);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(attrs, uid, deviceId, opPkg, reason);
-        }
-
-        @Override
-        public String toString() {
-            return "CallerInfo{"
-                    + " uid=" + uid
-                    + ", opPkg=" + opPkg
-                    + ", deviceId=" + deviceId
-                    + ", attrs=" + attrs
-                    + ", reason=" + reason
-                    + '}';
-        }
-    }
+    /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */
+    abstract VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis);
 
     /** Immutable info passed as a signal to end a vibration. */
     static final class EndInfo {
-        /** The {@link Status} to be set to the vibration when it ends with this info. */
+        /** The vibration status to be set when it ends with this info. */
         @NonNull
-        public final Status status;
+        public final VibrationSession.Status status;
         /** Info about the process that ended the vibration. */
-        public final CallerInfo endedBy;
+        public final VibrationSession.CallerInfo endedBy;
 
-        EndInfo(@NonNull Vibration.Status status) {
+        EndInfo(@NonNull VibrationSession.Status status) {
             this(status, null);
         }
 
-        EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) {
+        EndInfo(@NonNull VibrationSession.Status status,
+                @Nullable VibrationSession.CallerInfo endedBy) {
             this.status = status;
             this.endedBy = endedBy;
         }
@@ -211,10 +148,10 @@
      * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to
      * potentially expensive or resource-linked objects, such as {@link IBinder}.
      */
-    static final class DebugInfo {
-        final Status mStatus;
+    static final class DebugInfoImpl implements VibrationSession.DebugInfo {
+        final VibrationSession.Status mStatus;
         final long mCreateTime;
-        final CallerInfo mCallerInfo;
+        final VibrationSession.CallerInfo mCallerInfo;
         @Nullable
         final CombinedVibration mPlayedEffect;
 
@@ -226,9 +163,10 @@
         private final int mScaleLevel;
         private final float mAdaptiveScale;
 
-        DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
+        DebugInfoImpl(VibrationSession.Status status, VibrationStats stats,
+                @Nullable CombinedVibration playedEffect,
                 @Nullable CombinedVibration originalEffect, int scaleLevel,
-                float adaptiveScale, @NonNull CallerInfo callerInfo) {
+                float adaptiveScale, @NonNull VibrationSession.CallerInfo callerInfo) {
             Objects.requireNonNull(callerInfo);
             mCreateTime = stats.getCreateTimeDebug();
             mStartTime = stats.getStartTimeDebug();
@@ -243,6 +181,27 @@
         }
 
         @Override
+        public VibrationSession.Status getStatus() {
+            return mStatus;
+        }
+
+        @Override
+        public long getCreateUptimeMillis() {
+            return mCreateTime;
+        }
+
+        @Override
+        public VibrationSession.CallerInfo getCallerInfo() {
+            return mCallerInfo;
+        }
+
+        @Nullable
+        @Override
+        public Object getDumpAggregationKey() {
+            return mPlayedEffect;
+        }
+
+        @Override
         public String toString() {
             return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true)
                     + ", startTime: " + formatTime(mStartTime, /*includeDate=*/ true)
@@ -257,17 +216,13 @@
                     + ", callerInfo: " + mCallerInfo;
         }
 
-        void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
+        @Override
+        public void logMetrics(VibratorFrameworkStatsLogger statsLogger) {
             statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale);
         }
 
-        /**
-         * Write this info in a compact way into given {@link PrintWriter}.
-         *
-         * <p>This is used by dumpsys to log multiple vibration records in single lines that are
-         * easy to skim through by the sorted created time.
-         */
-        void dumpCompact(IndentingPrintWriter pw) {
+        @Override
+        public void dumpCompact(IndentingPrintWriter pw) {
             boolean isExternalVibration = mPlayedEffect == null;
             String timingsStr = String.format(Locale.ROOT,
                     "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s",
@@ -282,12 +237,6 @@
                     VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale,
                     Long.toBinaryString(mCallerInfo.attrs.getFlags()),
                     mCallerInfo.attrs.usageToString());
-            // Optional, most vibrations have category unknown so skip them to simplify the logs
-            String categoryStr =
-                    mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN
-                            ? " | category=" + VibrationAttributes.categoryToString(
-                            mCallerInfo.attrs.getCategory())
-                            : "";
             // Optional, most vibrations should not be defined via AudioAttributes
             // so skip them to simplify the logs
             String audioUsageStr =
@@ -302,11 +251,11 @@
                     " | played: %s | original: %s",
                     mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
                     mOriginalEffect == null ? null : mOriginalEffect.toDebugString());
-            pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr);
+            pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr);
         }
 
-        /** Write this info into given {@link PrintWriter}. */
-        void dump(IndentingPrintWriter pw) {
+        @Override
+        public void dump(IndentingPrintWriter pw) {
             pw.println("Vibration:");
             pw.increaseIndent();
             pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
@@ -323,8 +272,8 @@
             pw.decreaseIndent();
         }
 
-        /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
-        void dump(ProtoOutputStream proto, long fieldId) {
+        @Override
+        public void dump(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
             proto.write(VibrationProto.START_TIME, mStartTime);
             proto.write(VibrationProto.END_TIME, mEndTime);
@@ -335,7 +284,6 @@
             final VibrationAttributes attrs = mCallerInfo.attrs;
             proto.write(VibrationAttributesProto.USAGE, attrs.getUsage());
             proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage());
-            proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory());
             proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags());
             proto.end(attrsToken);
 
diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java
index a74c4e0..b3862cc 100644
--- a/services/core/java/com/android/server/vibrator/VibrationScaler.java
+++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java
@@ -134,7 +134,8 @@
         return effect.resolve(mDefaultVibrationAmplitude)
                 .applyEffectStrength(newEffectStrength)
                 .scale(scaleFactor)
-                .scaleLinearly(adaptiveScale);
+                // Make sure this is the last one so it is applied on top of the settings scaling.
+                .applyAdaptiveScale(adaptiveScale);
     }
 
     /**
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
new file mode 100644
index 0000000..5640b49
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -0,0 +1,208 @@
+/*
+ * 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.vibrator;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.CombinedVibration;
+import android.os.IBinder;
+import android.os.VibrationAttributes;
+import android.util.IndentingPrintWriter;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * Represents a generic vibration session that plays one or more vibration requests.
+ *
+ * <p>This might represent:
+ *
+ * <ol>
+ *     <li>A single {@link CombinedVibration} playback.
+ *     <li>An {@link android.os.ExternalVibration} playback.
+ * </ol>
+ */
+interface VibrationSession {
+
+    /** Returns data about the client app that triggered this vibration session. */
+    CallerInfo getCallerInfo();
+
+    /** Returns debug data for logging and metric reports. */
+    DebugInfo getDebugInfo();
+
+    /**
+     * Links this session to the app process death with given callback to handle it.
+     *
+     * <p>This can be used by the service to end the vibration session when the app process dies.
+     */
+    void linkToDeath(Runnable callback);
+
+    /** Removes link to the app process death. */
+    void unlinkToDeath();
+
+    /** Notify the session end was requested, which might be acted upon asynchronously. */
+    void notifyEnded();
+
+    /**
+     * Session status with reference to values from vibratormanagerservice.proto for logging.
+     */
+    enum Status {
+        UNKNOWN(VibrationProto.UNKNOWN),
+        RUNNING(VibrationProto.RUNNING),
+        FINISHED(VibrationProto.FINISHED),
+        FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED),
+        FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES),
+        CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED),
+        CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF),
+        CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE),
+        CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER),
+        CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER),
+        CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON),
+        CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED),
+        CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS),
+        IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS),
+        IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING),
+        IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING),
+        IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN),
+        IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS),
+        IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND),
+        IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION),
+        IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED),
+        IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL),
+        IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE),
+        IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING),
+        IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER),
+        IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE),
+        IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS),
+        IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED),
+        IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE),
+        IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER);
+
+        private final int mProtoEnumValue;
+
+        Status(int value) {
+            mProtoEnumValue = value;
+        }
+
+        public int getProtoEnumValue() {
+            return mProtoEnumValue;
+        }
+    }
+
+    /**
+     * Holds lightweight immutable info on the process that triggered the vibration session.
+     *
+     * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys
+     * operations. It shouldn't hold any references to potentially expensive or resource-linked
+     * objects, such as {@link IBinder}.
+     */
+    final class CallerInfo {
+        public final VibrationAttributes attrs;
+        public final int uid;
+        public final int deviceId;
+        public final String opPkg;
+        public final String reason;
+
+        CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg,
+                String reason) {
+            Objects.requireNonNull(attrs);
+            this.attrs = attrs;
+            this.uid = uid;
+            this.deviceId = deviceId;
+            this.opPkg = opPkg;
+            this.reason = reason;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CallerInfo)) return false;
+            CallerInfo that = (CallerInfo) o;
+            return Objects.equals(attrs, that.attrs)
+                    && uid == that.uid
+                    && deviceId == that.deviceId
+                    && Objects.equals(opPkg, that.opPkg)
+                    && Objects.equals(reason, that.reason);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(attrs, uid, deviceId, opPkg, reason);
+        }
+
+        @Override
+        public String toString() {
+            return "CallerInfo{"
+                    + " uid=" + uid
+                    + ", opPkg=" + opPkg
+                    + ", deviceId=" + deviceId
+                    + ", attrs=" + attrs
+                    + ", reason=" + reason
+                    + '}';
+        }
+    }
+
+    /**
+     * Interface for lightweight debug information about the vibration session for debugging.
+     *
+     * <p>This data could potentially be kept in memory for a long time for bugreport dumpsys
+     * operations. It shouldn't hold any references to potentially expensive or resource-linked
+     * objects, such as {@link IBinder}.
+     */
+    interface DebugInfo {
+
+        /** Return the vibration session status. */
+        Status getStatus();
+
+        /** Returns the session creation time from {@link android.os.SystemClock#uptimeMillis()}. */
+        long getCreateUptimeMillis();
+
+        /** Returns information about the process that created the session. */
+        CallerInfo getCallerInfo();
+
+        /**
+         * Returns the aggregation key for log records.
+         *
+         * <p>This is used to aggregate similar vibration sessions triggered in quick succession
+         * (e.g. multiple keyboard vibrations when the user is typing).
+         *
+         * <p>This does not need to include data from {@link CallerInfo} or {@link Status}.
+         *
+         * @see GroupedAggregatedLogRecords
+         */
+        @Nullable
+        Object getDumpAggregationKey();
+
+        /** Logs vibration session fields for metric reports. */
+        void logMetrics(VibratorFrameworkStatsLogger statsLogger);
+
+        /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
+        void dump(ProtoOutputStream proto, long fieldId);
+
+        /** Write this info into given {@link PrintWriter}. */
+        void dump(IndentingPrintWriter pw);
+
+        /**
+         * Write this info in a compact way into given {@link PrintWriter}.
+         *
+         * <p>This is used by dumpsys to log multiple records in single lines that are easy to skim
+         * through by the sorted created time.
+         */
+        void dumpCompact(IndentingPrintWriter pw);
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index f2f5eda..69cdcf4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,7 +16,6 @@
 
 package com.android.server.vibrator;
 
-import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
 import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
@@ -67,6 +66,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -191,8 +192,6 @@
     @GuardedBy("mLock")
     private boolean mVibrateOn;
     @GuardedBy("mLock")
-    private boolean mKeyboardVibrationOn;
-    @GuardedBy("mLock")
     private int mRingerMode;
     @GuardedBy("mLock")
     private boolean mOnWirelessCharger;
@@ -419,46 +418,46 @@
     /**
      * Check if given vibration should be ignored by the service.
      *
-     * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored,
+     * @return One of VibrationSession.Status.IGNORED_* values if the vibration should be ignored,
      * null otherwise.
      */
     @Nullable
-    public Vibration.Status shouldIgnoreVibration(@NonNull Vibration.CallerInfo callerInfo) {
+    public Status shouldIgnoreVibration(@NonNull CallerInfo callerInfo) {
         final int usage = callerInfo.attrs.getUsage();
         synchronized (mLock) {
             if (!mUidObserver.isUidForeground(callerInfo.uid)
                     && !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
-                return Vibration.Status.IGNORED_BACKGROUND;
+                return Status.IGNORED_BACKGROUND;
             }
 
             if (callerInfo.deviceId != Context.DEVICE_ID_DEFAULT
                     && callerInfo.deviceId != Context.DEVICE_ID_INVALID) {
-                return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+                return Status.IGNORED_FROM_VIRTUAL_DEVICE;
             }
 
             if (callerInfo.deviceId == Context.DEVICE_ID_INVALID
                     && isAppRunningOnAnyVirtualDevice(callerInfo.uid)) {
-                return Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE;
+                return Status.IGNORED_FROM_VIRTUAL_DEVICE;
             }
 
             if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) {
-                return Vibration.Status.IGNORED_FOR_POWER;
+                return Status.IGNORED_FOR_POWER;
             }
 
             if (!callerInfo.attrs.isFlagSet(
                     VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
                     && !shouldVibrateForUserSetting(callerInfo)) {
-                return Vibration.Status.IGNORED_FOR_SETTINGS;
+                return Status.IGNORED_FOR_SETTINGS;
             }
 
             if (!callerInfo.attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) {
                 if (!shouldVibrateForRingerModeLocked(usage)) {
-                    return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+                    return Status.IGNORED_FOR_RINGER_MODE;
                 }
             }
 
             if (mVibrationConfig.ignoreVibrationsOnWirelessCharger() && mOnWirelessCharger) {
-                return Vibration.Status.IGNORED_ON_WIRELESS_CHARGER;
+                return Status.IGNORED_ON_WIRELESS_CHARGER;
             }
         }
         return null;
@@ -474,7 +473,7 @@
      *
      * @return true if the vibration should be cancelled when the screen goes off, false otherwise.
      */
-    public boolean shouldCancelVibrationOnScreenOff(@NonNull Vibration.CallerInfo callerInfo,
+    public boolean shouldCancelVibrationOnScreenOff(@NonNull CallerInfo callerInfo,
             long vibrationStartUptimeMillis) {
         PowerManagerInternal pm;
         synchronized (mLock) {
@@ -486,8 +485,8 @@
             // ignored here and not cancel a vibration, and those are usually triggered by timeout
             // or inactivity, so it's unlikely that it will override a more active goToSleep reason.
             PowerManager.SleepData sleepData = pm.getLastGoToSleep();
-            if ((sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis)
-                    || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason)) {
+            if (sleepData != null && (sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis
+                    || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason))) {
                 // Ignore screen off events triggered before the vibration started, and all
                 // automatic "go to sleep" events from allowlist.
                 Slog.d(TAG, "Ignoring screen off event triggered at uptime "
@@ -525,21 +524,13 @@
      * {@code false} to ignore the vibration.
      */
     @GuardedBy("mLock")
-    private boolean shouldVibrateForUserSetting(Vibration.CallerInfo callerInfo) {
+    private boolean shouldVibrateForUserSetting(CallerInfo callerInfo) {
         final int usage = callerInfo.attrs.getUsage();
         if (!mVibrateOn && (VIBRATE_ON_DISABLED_USAGE_ALLOWED != usage)) {
             // Main setting disabled.
             return false;
         }
 
-        if (mVibrationConfig.isKeyboardVibrationSettingsSupported()) {
-            int category = callerInfo.attrs.getCategory();
-            if (usage == USAGE_TOUCH && category == CATEGORY_KEYBOARD) {
-                // Keyboard touch has a different user setting.
-                return mKeyboardVibrationOn;
-            }
-        }
-
         // Apply individual user setting based on usage.
         return getCurrentIntensity(usage) != Vibrator.VIBRATION_INTENSITY_OFF;
     }
@@ -556,10 +547,11 @@
             mVibrateInputDevices =
                     loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
             mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
-            mKeyboardVibrationOn = loadSystemSetting(
-                    Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
 
-            int keyboardIntensity = getDefaultIntensity(USAGE_IME_FEEDBACK);
+            boolean isKeyboardVibrationOn = loadSystemSetting(
+                    Settings.System.KEYBOARD_VIBRATION_ENABLED, 1, userHandle) > 0;
+            int keyboardIntensity = toIntensity(isKeyboardVibrationOn,
+                    getDefaultIntensity(USAGE_IME_FEEDBACK));
             int alarmIntensity = toIntensity(
                     loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
                     getDefaultIntensity(USAGE_ALARM));
@@ -654,7 +646,6 @@
             return "VibrationSettings{"
                     + "mVibratorConfig=" + mVibrationConfig
                     + ", mVibrateOn=" + mVibrateOn
-                    + ", mKeyboardVibrationOn=" + mKeyboardVibrationOn
                     + ", mVibrateInputDevices=" + mVibrateInputDevices
                     + ", mBatterySaverMode=" + mBatterySaverMode
                     + ", mRingerMode=" + ringerModeToString(mRingerMode)
@@ -671,7 +662,6 @@
             pw.println("VibrationSettings:");
             pw.increaseIndent();
             pw.println("vibrateOn = " + mVibrateOn);
-            pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn);
             pw.println("vibrateInputDevices = " + mVibrateInputDevices);
             pw.println("batterySaverMode = " + mBatterySaverMode);
             pw.println("ringerMode = " + ringerModeToString(mRingerMode));
@@ -698,8 +688,6 @@
     void dump(ProtoOutputStream proto) {
         synchronized (mLock) {
             proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
-            proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON,
-                    mKeyboardVibrationOn);
             proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
             proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
                     getCurrentIntensity(USAGE_ALARM));
@@ -774,6 +762,11 @@
         return value;
     }
 
+    @VibrationIntensity
+    private int toIntensity(boolean enabled, @VibrationIntensity int defaultValue) {
+        return enabled ? defaultValue : Vibrator.VIBRATION_INTENSITY_OFF;
+    }
+
     private boolean loadBooleanSetting(String settingKey, int userHandle) {
         return loadSystemSetting(settingKey, 0, userHandle) != 0;
     }
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index dd66809..fc0c6e7 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -79,6 +79,7 @@
     private int mVibratorSetAmplitudeCount;
     private int mVibratorSetExternalControlCount;
     private int mVibratorPerformCount;
+    private int mVibratorPerformVendorCount;
     private int mVibratorComposeCount;
     private int mVibratorComposePwleCount;
 
@@ -165,7 +166,7 @@
      * @return true if the status was accepted. This method will only accept given values if
      * the end timestamp was never set.
      */
-    boolean reportEnded(@Nullable Vibration.CallerInfo endedBy) {
+    boolean reportEnded(@Nullable VibrationSession.CallerInfo endedBy) {
         if (hasEnded()) {
             // Vibration already ended, keep first ending stats set and ignore this one.
             return false;
@@ -186,7 +187,7 @@
      * <p>This method will only accept the first value as the one that was interrupted by this
      * vibration, and will ignore all successive calls.
      */
-    void reportInterruptedAnotherVibration(@NonNull Vibration.CallerInfo callerInfo) {
+    void reportInterruptedAnotherVibration(@NonNull VibrationSession.CallerInfo callerInfo) {
         if (mInterruptedUsage < 0) {
             mInterruptedUsage = callerInfo.attrs.getUsage();
         }
@@ -239,6 +240,11 @@
         }
     }
 
+    /** Report a call to vibrator method to trigger a vendor vibration effect. */
+    void reportPerformVendorEffect(long halResult) {
+        mVibratorPerformVendorCount++;
+    }
+
     /** Report a call to vibrator method to trigger a vibration as a composition of primitives. */
     void reportComposePrimitives(long halResult, PrimitiveSegment[] primitives) {
         mVibratorComposeCount++;
@@ -313,6 +319,7 @@
         public final int halOnCount;
         public final int halOffCount;
         public final int halPerformCount;
+        public final int halPerformVendorCount;
         public final int halSetAmplitudeCount;
         public final int halSetExternalControlCount;
         public final int halCompositionSize;
@@ -323,7 +330,7 @@
         public final int[] halUnsupportedEffectsUsed;
         private boolean mIsWritten;
 
-        StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status,
+        StatsInfo(int uid, int vibrationType, int usage, VibrationSession.Status status,
                 VibrationStats stats, long completionUptimeMillis) {
             this.uid = uid;
             this.vibrationType = vibrationType;
@@ -357,6 +364,7 @@
             halOnCount = stats.mVibratorOnCount;
             halOffCount = stats.mVibratorOffCount;
             halPerformCount = stats.mVibratorPerformCount;
+            halPerformVendorCount = stats.mVibratorPerformVendorCount;
             halSetAmplitudeCount = stats.mVibratorSetAmplitudeCount;
             halSetExternalControlCount = stats.mVibratorSetExternalControlCount;
             halCompositionSize = stats.mVibrationCompositionTotalSize;
@@ -390,7 +398,8 @@
                     halOnCount, halOffCount, halPerformCount, halSetAmplitudeCount,
                     halSetExternalControlCount, halSupportedCompositionPrimitivesUsed,
                     halSupportedEffectsUsed, halUnsupportedCompositionPrimitivesUsed,
-                    halUnsupportedEffectsUsed, halCompositionSize, halPwleSize, adaptiveScale);
+                    halUnsupportedEffectsUsed, halCompositionSize, halPwleSize, adaptiveScale,
+                    halPerformVendorCount);
         }
 
         private static int[] filteredKeys(SparseBooleanArray supportArray, boolean supported) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 7152844..5137d19 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -32,6 +32,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -217,7 +218,7 @@
     }
 
     /**
-     * Calculate the {@link Vibration.Status} based on the current queue state and the expected
+     * Calculate the {@link Vibration.EndInfo} based on the current queue state and the expected
      * number of {@link StartSequentialEffectStep} to be played.
      */
     @Nullable
@@ -235,10 +236,10 @@
         }
         // No pending steps, and something happened.
         if (mSuccessfulVibratorOnSteps > 0) {
-            return new Vibration.EndInfo(Vibration.Status.FINISHED);
+            return new Vibration.EndInfo(Status.FINISHED);
         }
         // If no step was able to turn the vibrator ON successfully.
-        return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED);
+        return new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED);
     }
 
     /**
@@ -352,7 +353,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Binder died, cancelling vibration...");
         }
-        notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
+        notifyCancelled(new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED),
                 /* immediate= */ false);
     }
 
@@ -377,7 +378,7 @@
         if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) {
             Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo
                     + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
-            cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON);
+            cancelInfo = new Vibration.EndInfo(Status.CANCELLED_BY_UNKNOWN_REASON);
         }
         synchronized (mLock) {
             if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index cfb4c74..ab4a4d8 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -240,7 +241,7 @@
                 runCurrentVibrationWithWakeLockAndDeathLink();
             } finally {
                 clientVibrationCompleteIfNotAlready(
-                        new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED));
+                        new Vibration.EndInfo(Status.FINISHED_UNEXPECTED));
             }
         } finally {
             mWakeLock.release();
@@ -259,7 +260,7 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Error linking vibration to token death", e);
             clientVibrationCompleteIfNotAlready(
-                    new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN));
+                    new Vibration.EndInfo(Status.IGNORED_ERROR_TOKEN));
             return;
         }
         // Ensure that the unlink always occurs now.
diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java
index de5e662..3a814cd 100644
--- a/services/core/java/com/android/server/vibrator/VibratorControlService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java
@@ -559,8 +559,8 @@
     }
 
     /**
-     * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
-     * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+     * Record for a single {@link VibrationSession.DebugInfo}, that can be grouped by usage and
+     * aggregated by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
      */
     private static final class VibrationScaleParamRecord
             implements GroupedAggregatedLogRecords.SingleLogRecord {
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 8cc157c..4fc0b74 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -279,8 +279,8 @@
                 vendorEffect.getVendorData().writeToParcel(vendorData, /* flags= */ 0);
                 vendorData.setDataPosition(0);
                 long duration = mNativeWrapper.performVendorEffect(vendorData,
-                        vendorEffect.getEffectStrength(), vendorEffect.getLinearScale(),
-                        vibrationId);
+                        vendorEffect.getEffectStrength(), vendorEffect.getScale(),
+                        vendorEffect.getAdaptiveScale(), vibrationId);
                 if (duration > 0) {
                     mCurrentAmplitude = -1;
                     notifyListenerOnVibrating(true);
@@ -459,7 +459,7 @@
                 long vibrationId);
 
         private static native long performVendorEffect(long nativePtr, Parcel vendorData,
-                long strength, float scale, long vibrationId);
+                long strength, float scale, float adaptiveScale, long vibrationId);
 
         private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
                 long vibrationId);
@@ -518,8 +518,9 @@
 
         /** Turns vibrator on to perform a vendor-specific effect. */
         public long performVendorEffect(Parcel vendorData, long strength, float scale,
-                long vibrationId) {
-            return performVendorEffect(mNativePtr, vendorData, strength, scale, vibrationId);
+                float adaptiveScale, long vibrationId) {
+            return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
+                    vibrationId);
         }
 
         /** Turns vibrator on to perform effect composed of give primitives effect. */
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index f2ad5b9..799934a 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -73,9 +73,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.SystemService;
 import com.android.server.pm.BackgroundUserSoundNotifier;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.DebugInfo;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import libcore.util.NativeAllocationRegistry;
 
@@ -160,11 +162,12 @@
     @GuardedBy("mLock")
     private VibrationStepConductor mNextVibration;
     @GuardedBy("mLock")
-    private ExternalVibrationHolder mCurrentExternalVibration;
+    private ExternalVibrationSession mCurrentExternalVibration;
     @GuardedBy("mLock")
     private boolean mServiceReady;
 
-    private final VibrationSettings mVibrationSettings;
+    @VisibleForTesting
+    final VibrationSettings mVibrationSettings;
     private final VibrationScaler mVibrationScaler;
     private final VibratorControlService mVibratorControlService;
     private final InputDeviceDelegate mInputDeviceDelegate;
@@ -184,13 +187,12 @@
                     // When the system is entering a non-interactive state, we want to cancel
                     // vibrations in case a misbehaving app has abandoned them.
                     if (shouldCancelOnScreenOffLocked(mNextVibration)) {
-                        clearNextVibrationLocked(
-                                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF));
+                        clearNextVibrationLocked(new Vibration.EndInfo(
+                                Status.CANCELLED_BY_SCREEN_OFF));
                     }
                     if (shouldCancelOnScreenOffLocked(mCurrentVibration)) {
-                        mCurrentVibration.notifyCancelled(
-                                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
-                                /* immediate= */ false);
+                        mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
+                                Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
                     }
                 }
             } else if (android.multiuser.Flags.addUiForSoundsFromBackgroundUsers()
@@ -198,12 +200,11 @@
                 synchronized (mLock) {
                     if (shouldCancelOnFgUserRequest(mNextVibration)) {
                         clearNextVibrationLocked(new Vibration.EndInfo(
-                                Vibration.Status.CANCELLED_BY_FOREGROUND_USER));
+                                Status.CANCELLED_BY_FOREGROUND_USER));
                     }
                     if (shouldCancelOnFgUserRequest(mCurrentVibration)) {
                         mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
-                                        Vibration.Status.CANCELLED_BY_FOREGROUND_USER),
-                                /* immediate= */ false);
+                                Status.CANCELLED_BY_FOREGROUND_USER), /* immediate= */ false);
                     }
                 }
             }
@@ -220,12 +221,12 @@
                     }
                     synchronized (mLock) {
                         if (shouldCancelAppOpModeChangedLocked(mNextVibration)) {
-                            clearNextVibrationLocked(
-                                    new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_APP_OPS));
+                            clearNextVibrationLocked(new Vibration.EndInfo(
+                                    Status.CANCELLED_BY_APP_OPS));
                         }
                         if (shouldCancelAppOpModeChangedLocked(mCurrentVibration)) {
                             mCurrentVibration.notifyCancelled(new Vibration.EndInfo(
-                                    Vibration.Status.CANCELLED_BY_APP_OPS), /* immediate= */ false);
+                                    Status.CANCELLED_BY_APP_OPS), /* immediate= */ false);
                         }
                     }
                 }
@@ -441,8 +442,8 @@
                     return false;
                 }
                 AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId,
-                        new Vibration.CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg,
-                                null), effects);
+                        new CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg, null),
+                        effects);
                 mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration);
                 updateAlwaysOnLocked(alwaysOnVibration);
             }
@@ -487,27 +488,18 @@
     HalVibration performHapticFeedbackInternal(
             int uid, int deviceId, String opPkg, int constant, String reason,
             IBinder token, int flags, int privFlags) {
-        HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
-        if (hapticVibrationProvider == null) {
-            Slog.e(TAG, "performHapticFeedback; haptic vibration provider not ready.");
-            return null;
-        }
-        if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
-                && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
-            Slog.w(TAG, "performHapticFeedback; no permission for system constant " + constant);
-            return null;
-        }
-        VibrationEffect effect = hapticVibrationProvider.getVibrationForHapticFeedback(constant);
-        if (effect == null) {
-            Slog.w(TAG, "performHapticFeedback; vibration absent for constant " + constant);
-            return null;
-        }
-        CombinedVibration vib = CombinedVibration.createParallel(effect);
-        VibrationAttributes attrs = hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
-                constant, flags, privFlags);
+        // Make sure we report the constant id in the requested haptic feedback reason.
         reason = "performHapticFeedback(constant=" + constant + "): " + reason;
-        VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
-        return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, vib, attrs, reason, token);
+        HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
+        Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider);
+        if (ignoreStatus != null) {
+            logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus);
+            return null;
+        }
+        return performHapticFeedbackWithEffect(uid, deviceId, opPkg, constant, reason, token,
+                hapticVibrationProvider.getVibration(constant),
+                hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
+                        constant, flags, privFlags));
     }
 
     /**
@@ -520,12 +512,34 @@
     HalVibration performHapticFeedbackForInputDeviceInternal(
             int uid, int deviceId, String opPkg, int constant, int inputDeviceId, int inputSource,
             String reason, IBinder token, int flags, int privFlags) {
-        // TODO(b/355543835): implement input device specific logic.
-        if (DEBUG) {
-            Slog.d(TAG, "performHapticFeedbackForInput: input device specific not implemented.");
+        // Make sure we report the constant id in the requested haptic feedback reason.
+        reason = "performHapticFeedbackForInputDevice(constant=" + constant + ", inputDeviceId="
+                + inputDeviceId + ", inputSource=" + inputSource + "): " + reason;
+        HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider();
+        Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider);
+        if (ignoreStatus != null) {
+            logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus);
+            return null;
         }
-        return performHapticFeedbackInternal(uid, deviceId, opPkg, constant, reason, /* token= */
-                this, flags, privFlags);
+        return performHapticFeedbackWithEffect(uid, deviceId, opPkg, constant, reason, token,
+                hapticVibrationProvider.getVibration(constant, inputSource),
+                hapticVibrationProvider.getVibrationAttributesForHapticFeedback(
+                        constant, flags, privFlags));
+    }
+
+    private HalVibration performHapticFeedbackWithEffect(int uid, int deviceId, String opPkg,
+            int constant, String reason, IBinder token, VibrationEffect effect,
+            VibrationAttributes attrs) {
+        if (effect == null) {
+            logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason,
+                    Status.IGNORED_UNSUPPORTED);
+            Slog.w(TAG,
+                    "performHapticFeedbackWithEffect; vibration absent for constant " + constant);
+            return null;
+        }
+        CombinedVibration vib = CombinedVibration.createParallel(effect);
+        VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant);
+        return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, vib, attrs, reason, token);
     }
 
     /**
@@ -563,22 +577,25 @@
     private HalVibration vibrateInternal(int uid, int deviceId, String opPkg,
             @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs,
             String reason, IBinder token) {
+        CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason);
         if (token == null) {
             Slog.e(TAG, "token must not be null");
+            logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN);
             return null;
         }
         if (effect.hasVendorEffects()
                 && !hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) {
-            Slog.w(TAG, "vibrate; no permission for vendor effects");
+            Slog.e(TAG, "vibrate; no permission for vendor effects");
+            logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION);
             return null;
         }
         enforceUpdateAppOpsStatsPermission(uid);
         if (!isEffectValid(effect)) {
+            logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED);
             return null;
         }
         // Create Vibration.Stats as close to the received request as possible, for tracking.
-        HalVibration vib = new HalVibration(token, effect,
-                new Vibration.CallerInfo(attrs, uid, deviceId, opPkg, reason));
+        HalVibration vib = new HalVibration(token, effect, callerInfo);
         fillVibrationFallbacks(vib, effect);
 
         if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
@@ -605,11 +622,11 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mCurrentExternalVibration != null) {
-                        mCurrentExternalVibration.mute();
+                        mCurrentExternalVibration.notifyEnded();
                         vib.stats.reportInterruptedAnotherVibration(
                                 mCurrentExternalVibration.callerInfo);
                         endExternalVibrateLocked(
-                                new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+                                new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
                                         vib.callerInfo),
                                 /* continueExternalControl= */ false);
                     } else if (mCurrentVibration != null) {
@@ -625,7 +642,7 @@
                             vib.stats.reportInterruptedAnotherVibration(
                                     mCurrentVibration.getVibration().callerInfo);
                             mCurrentVibration.notifyCancelled(
-                                    new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
+                                    new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
                                             vib.callerInfo),
                                     /* immediate= */ false);
                         }
@@ -657,7 +674,7 @@
                     Slog.d(TAG, "Canceling vibration");
                 }
                 Vibration.EndInfo cancelledByUserInfo =
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER);
+                        new Vibration.EndInfo(Status.CANCELLED_BY_USER);
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNextVibration != null
@@ -673,9 +690,9 @@
                     }
                     if (mCurrentExternalVibration != null
                             && shouldCancelVibration(
-                            mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
+                            mCurrentExternalVibration.getCallerInfo().attrs,
                             usageFilter)) {
-                        mCurrentExternalVibration.mute();
+                        mCurrentExternalVibration.notifyEnded();
                         endExternalVibrateLocked(
                                 cancelledByUserInfo, /* continueExternalControl= */ false);
                     }
@@ -840,7 +857,7 @@
                             : vibrationEndInfo.status));
                 }
                 mCurrentVibration.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
                         /* immediate= */ false);
             }
         }
@@ -891,7 +908,7 @@
             // Note that we don't consider pipelining here, because new pipelined ones should
             // replace pending non-executing pipelined ones anyway.
             clearNextVibrationLocked(
-                    new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo));
+                    new Vibration.EndInfo(Status.IGNORED_SUPERSEDED, vib.callerInfo));
             mNextVibration = conductor;
             return null;
         } finally {
@@ -914,15 +931,15 @@
                     if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) {
                         // Shouldn't happen. The method call already logs a wtf.
                         mCurrentVibration = null;  // Aborted.
-                        return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_SCHEDULING);
+                        return new Vibration.EndInfo(Status.IGNORED_ERROR_SCHEDULING);
                     }
                     return null;
                 case AppOpsManager.MODE_ERRORED:
                     Slog.w(TAG, "Start AppOpsManager operation errored for uid "
                             + vib.callerInfo.uid);
-                    return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
+                    return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS);
                 default:
-                    return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
+                    return new Vibration.EndInfo(Status.IGNORED_APP_OPS);
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -930,7 +947,7 @@
     }
 
     @GuardedBy("mLock")
-    private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo,
+    private void endVibrationLocked(Vibration vib, Vibration.EndInfo vibrationEndInfo,
             boolean shouldWriteStats) {
         vib.end(vibrationEndInfo);
         logAndRecordVibration(vib.getDebugInfo());
@@ -940,15 +957,6 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib,
-            Vibration.EndInfo vibrationEndInfo) {
-        vib.end(vibrationEndInfo);
-        logAndRecordVibration(vib.getDebugInfo());
-        mFrameworkStatsLogger.writeVibrationReportedAsync(
-                vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis()));
-    }
-
     private VibrationStepConductor createVibrationStepConductor(HalVibration vib) {
         CompletableFuture<Void> requestVibrationParamsFuture = null;
 
@@ -970,17 +978,32 @@
         vib.scaleEffects(mVibrationScaler);
         mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay());
 
-        return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
+        return new Vibration.EndInfo(Status.FORWARDED_TO_INPUT_DEVICES);
     }
 
-    private void logAndRecordVibration(Vibration.DebugInfo info) {
+    private void logAndRecordPerformHapticFeedbackAttempt(int uid, int deviceId, String opPkg,
+            String reason, Status status) {
+        CallerInfo callerInfo = new CallerInfo(
+                VibrationAttributes.createForUsage(VibrationAttributes.USAGE_UNKNOWN),
+                uid, deviceId, opPkg, reason);
+        logAndRecordVibrationAttempt(/* effect= */ null, callerInfo, status);
+    }
+
+    private void logAndRecordVibrationAttempt(@Nullable CombinedVibration effect,
+            CallerInfo callerInfo, Status status) {
+        logAndRecordVibration(
+                new Vibration.DebugInfoImpl(status, new VibrationStats(),
+                        effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE,
+                        VibrationScaler.ADAPTIVE_SCALE_NONE, callerInfo));
+    }
+
+    private void logAndRecordVibration(DebugInfo info) {
         info.logMetrics(mFrameworkStatsLogger);
-        logVibrationStatus(info.mCallerInfo.uid, info.mCallerInfo.attrs, info.mStatus);
+        logVibrationStatus(info.getCallerInfo().uid, info.getCallerInfo().attrs, info.getStatus());
         mVibratorManagerRecords.record(info);
     }
 
-    private void logVibrationStatus(int uid, VibrationAttributes attrs,
-            Vibration.Status status) {
+    private void logVibrationStatus(int uid, VibrationAttributes attrs, Status status) {
         switch (status) {
             case IGNORED_BACKGROUND:
                 Slog.e(TAG, "Ignoring incoming vibration as process with"
@@ -1125,15 +1148,14 @@
 
         if (ongoingVibrationImportance > newVibrationImportance) {
             // Existing vibration has higher importance and should not be cancelled.
-            return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE,
+            return new Vibration.EndInfo(Status.IGNORED_FOR_HIGHER_IMPORTANCE,
                     ongoingVibration.callerInfo);
         }
 
         // Same importance, use repeating as a tiebreaker.
         if (ongoingVibration.isRepeating() && !newVibration.isRepeating()) {
             // Ongoing vibration is repeating and new one is not, give priority to ongoing
-            return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_ONGOING,
-                    ongoingVibration.callerInfo);
+            return new Vibration.EndInfo(Status.IGNORED_FOR_ONGOING, ongoingVibration.callerInfo);
         }
         // New vibration is repeating or this is a complete tie between them,
         // give priority to new vibration.
@@ -1184,8 +1206,8 @@
      */
     @GuardedBy("mLock")
     @Nullable
-    private Vibration.EndInfo shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) {
-        Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
+    private Vibration.EndInfo shouldIgnoreVibrationLocked(CallerInfo callerInfo) {
+        Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo);
         if (statusFromSettings != null) {
             return new Vibration.EndInfo(statusFromSettings);
         }
@@ -1195,15 +1217,30 @@
             if (mode == AppOpsManager.MODE_ERRORED) {
                 // We might be getting calls from within system_server, so we don't actually
                 // want to throw a SecurityException here.
-                return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS);
+                return new Vibration.EndInfo(Status.IGNORED_ERROR_APP_OPS);
             } else {
-                return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS);
+                return new Vibration.EndInfo(Status.IGNORED_APP_OPS);
             }
         }
 
         return null;
     }
 
+    @Nullable
+    private Status shouldIgnoreHapticFeedback(int constant, String reason,
+            HapticFeedbackVibrationProvider hapticVibrationProvider) {
+        if (hapticVibrationProvider == null) {
+            Slog.e(TAG, reason + "; haptic vibration provider not ready.");
+            return Status.IGNORED_ERROR_SCHEDULING;
+        }
+        if (hapticVibrationProvider.isRestrictedHapticFeedback(constant)
+                && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) {
+            Slog.w(TAG, reason + "; no permission for system constant " + constant);
+            return Status.IGNORED_MISSING_PERMISSION;
+        }
+        return null;
+    }
+
     /**
      * Return true if the vibration has the same token and usage belongs to given usage class.
      *
@@ -1240,7 +1277,7 @@
      * {@code attrs}. This will return one of the AppOpsManager.MODE_*.
      */
     @GuardedBy("mLock")
-    private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+    private int checkAppOpModeLocked(CallerInfo callerInfo) {
         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
                 callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg);
         int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs);
@@ -1255,7 +1292,7 @@
 
     /** Start an operation in {@link AppOpsManager}, if allowed. */
     @GuardedBy("mLock")
-    private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+    private int startAppOpModeLocked(CallerInfo callerInfo) {
         return fixupAppOpModeLocked(
                 mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg),
                 callerInfo.attrs);
@@ -1266,7 +1303,7 @@
      * operation with same uid was previously started.
      */
     @GuardedBy("mLock")
-    private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) {
+    private void finishAppOpModeLocked(CallerInfo callerInfo) {
         mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg);
     }
 
@@ -1684,10 +1721,10 @@
      */
     private static final class AlwaysOnVibration {
         public final int alwaysOnId;
-        public final Vibration.CallerInfo callerInfo;
+        public final CallerInfo callerInfo;
         public final SparseArray<PrebakedSegment> effects;
 
-        AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo,
+        AlwaysOnVibration(int alwaysOnId, CallerInfo callerInfo,
                 SparseArray<PrebakedSegment> effects) {
             this.alwaysOnId = alwaysOnId;
             this.callerInfo = callerInfo;
@@ -1695,113 +1732,6 @@
         }
     }
 
-    /** Holder for a {@link ExternalVibration}. */
-    private final class ExternalVibrationHolder extends Vibration implements
-            IBinder.DeathRecipient {
-
-        public final ExternalVibration externalVibration;
-        public final ExternalVibrationScale scale = new ExternalVibrationScale();
-
-        private Vibration.Status mStatus;
-
-        private ExternalVibrationHolder(ExternalVibration externalVibration) {
-            super(externalVibration.getToken(), new Vibration.CallerInfo(
-                    externalVibration.getVibrationAttributes(), externalVibration.getUid(),
-                    // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice
-                    // instead of using DEVICE_ID_INVALID here and relying on the UID checks.
-                    Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null));
-            this.externalVibration = externalVibration;
-            mStatus = Vibration.Status.RUNNING;
-        }
-
-        public void muteScale() {
-            scale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE;
-            if (Flags.hapticsScaleV2Enabled()) {
-                scale.scaleFactor = 0;
-            }
-        }
-
-        public void scale(VibrationScaler scaler, int usage) {
-            scale.scaleLevel = scaler.getScaleLevel(usage);
-            if (Flags.hapticsScaleV2Enabled()) {
-                scale.scaleFactor = scaler.getScaleFactor(usage);
-            }
-            scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
-            stats.reportAdaptiveScale(scale.adaptiveHapticsScale);
-        }
-
-        public void mute() {
-            externalVibration.mute();
-        }
-
-        public void linkToDeath() {
-            externalVibration.linkToDeath(this);
-        }
-
-        public void unlinkToDeath() {
-            externalVibration.unlinkToDeath(this);
-        }
-
-        public boolean isHoldingSameVibration(ExternalVibration externalVibration) {
-            return this.externalVibration.equals(externalVibration);
-        }
-
-        public void end(Vibration.EndInfo info) {
-            if (mStatus != Vibration.Status.RUNNING) {
-                // Already ended, ignore this call
-                return;
-            }
-            mStatus = info.status;
-            stats.reportEnded(info.endedBy);
-
-            if (stats.hasStarted()) {
-                // External vibration doesn't have feedback from total time the vibrator was playing
-                // with non-zero amplitude, so we use the duration between start and end times of
-                // the vibration as the time the vibrator was ON, since the haptic channels are
-                // open for this duration and can receive vibration waveform data.
-                stats.reportVibratorOn(
-                        stats.getEndUptimeMillis() - stats.getStartUptimeMillis());
-            }
-        }
-
-        public void binderDied() {
-            synchronized (mLock) {
-                if (mCurrentExternalVibration != null) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "External vibration finished because binder died");
-                    }
-                    endExternalVibrateLocked(
-                            new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
-                            /* continueExternalControl= */ false);
-                }
-            }
-        }
-
-        public Vibration.DebugInfo getDebugInfo() {
-            return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null,
-                    /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale,
-                    callerInfo);
-        }
-
-        public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) {
-            return new VibrationStats.StatsInfo(
-                    externalVibration.getUid(),
-                    FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
-                    externalVibration.getVibrationAttributes().getUsage(), mStatus, stats,
-                    completionUptimeMillis);
-        }
-
-        @Override
-        boolean isRepeating() {
-            // We don't currently know if the external vibration is repeating, so we just use a
-            // heuristic based on the usage. Ideally this would be propagated in the
-            // ExternalVibration.
-            int usage = externalVibration.getVibrationAttributes().getUsage();
-            return usage == VibrationAttributes.USAGE_RINGTONE
-                    || usage == VibrationAttributes.USAGE_ALARM;
-        }
-    }
-
     /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
     @VisibleForTesting
     public static class NativeWrapper {
@@ -1861,7 +1791,7 @@
                     new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
         }
 
-        synchronized void record(Vibration.DebugInfo info) {
+        synchronized void record(DebugInfo info) {
             GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord =
                     mRecentVibrations.add(new VibrationRecord(info));
             if (droppedRecord != null) {
@@ -1918,25 +1848,25 @@
     }
 
     /**
-     * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated
-     * by UID, {@link VibrationAttributes} and {@link VibrationEffect}.
+     * Record for a single {@link DebugInfo}, that can be grouped by usage and aggregated by UID,
+     * {@link VibrationAttributes} and {@link CombinedVibration}.
      */
     private static final class VibrationRecord
             implements GroupedAggregatedLogRecords.SingleLogRecord {
-        private final Vibration.DebugInfo mInfo;
+        private final DebugInfo mInfo;
 
-        VibrationRecord(Vibration.DebugInfo info) {
+        VibrationRecord(DebugInfo info) {
             mInfo = info;
         }
 
         @Override
         public int getGroupKey() {
-            return mInfo.mCallerInfo.attrs.getUsage();
+            return mInfo.getCallerInfo().attrs.getUsage();
         }
 
         @Override
         public long getCreateUptimeMs() {
-            return mInfo.mCreateTime;
+            return mInfo.getCreateUptimeMillis();
         }
 
         @Override
@@ -1944,10 +1874,10 @@
             if (!(record instanceof VibrationRecord)) {
                 return false;
             }
-            Vibration.DebugInfo info = ((VibrationRecord) record).mInfo;
-            return mInfo.mCallerInfo.uid == info.mCallerInfo.uid
-                    && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs)
-                    && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect);
+            DebugInfo info = ((VibrationRecord) record).mInfo;
+            return mInfo.getCallerInfo().uid == info.getCallerInfo().uid
+                    && Objects.equals(mInfo.getCallerInfo().attrs, info.getCallerInfo().attrs)
+                    && Objects.equals(mInfo.getDumpAggregationKey(), info.getDumpAggregationKey());
         }
 
         @Override
@@ -1997,7 +1927,8 @@
                 setExternalControl(false, mCurrentExternalVibration.stats);
             }
             // The external control was turned off, end it and report metrics right away.
-            endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo);
+            endVibrationLocked(mCurrentExternalVibration, vibrationEndInfo,
+                    /* shouldWriteStats= */ true);
             mCurrentExternalVibration = null;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -2057,17 +1988,18 @@
         @Override
         public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
             // Create Vibration.Stats as close to the received request as possible, for tracking.
-            ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+            ExternalVibrationSession externalVibration = new ExternalVibrationSession(vib);
             // Mute the request until we run all the checks and accept the vibration.
-            vibHolder.muteScale();
+            externalVibration.muteScale();
             boolean alreadyUnderExternalControl = false;
             boolean waitForCompletion = false;
 
             synchronized (mLock) {
                 if (!hasExternalControlCapability()) {
-                    endVibrationAndWriteStatsLocked(vibHolder,
-                            new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED));
-                    return vibHolder.scale;
+                    endVibrationLocked(externalVibration,
+                            new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED),
+                            /* shouldWriteStats= */ true);
+                    return externalVibration.getScale();
                 }
 
                 if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
@@ -2076,44 +2008,46 @@
                     Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
                             + " tried to play externally controlled vibration"
                             + " without VIBRATE permission, ignoring.");
-                    endVibrationAndWriteStatsLocked(vibHolder,
-                            new Vibration.EndInfo(Vibration.Status.IGNORED_MISSING_PERMISSION));
-                    return vibHolder.scale;
+                    endVibrationLocked(externalVibration,
+                            new Vibration.EndInfo(Status.IGNORED_MISSING_PERMISSION),
+                            /* shouldWriteStats= */ true);
+                    return externalVibration.getScale();
                 }
 
                 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(
-                        vibHolder.callerInfo);
+                        externalVibration.callerInfo);
 
                 if (vibrationEndInfo == null
                         && mCurrentExternalVibration != null
                         && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
                     // We are already playing this external vibration, so we can return the same
                     // scale calculated in the previous call to this method.
-                    return mCurrentExternalVibration.scale;
+                    return mCurrentExternalVibration.getScale();
                 }
 
                 if (vibrationEndInfo == null) {
                     // Check if ongoing vibration is more important than this vibration.
-                    vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vibHolder);
+                    vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(externalVibration);
                 }
 
                 if (vibrationEndInfo != null) {
-                    endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo);
-                    return vibHolder.scale;
+                    endVibrationLocked(externalVibration, vibrationEndInfo,
+                            /* shouldWriteStats= */ true);
+                    return externalVibration.getScale();
                 }
 
                 if (mCurrentExternalVibration == null) {
                     // If we're not under external control right now, then cancel any normal
                     // vibration that may be playing and ready the vibrator for external control.
                     if (mCurrentVibration != null) {
-                        vibHolder.stats.reportInterruptedAnotherVibration(
+                        externalVibration.stats.reportInterruptedAnotherVibration(
                                 mCurrentVibration.getVibration().callerInfo);
                         clearNextVibrationLocked(
-                                new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL,
-                                        vibHolder.callerInfo));
+                                new Vibration.EndInfo(Status.IGNORED_FOR_EXTERNAL,
+                                        externalVibration.callerInfo));
                         mCurrentVibration.notifyCancelled(
-                                new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
-                                        vibHolder.callerInfo),
+                                new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+                                        externalVibration.callerInfo),
                                 /* immediate= */ true);
                         waitForCompletion = true;
                     }
@@ -2127,12 +2061,12 @@
                     // Note that this doesn't support multiple concurrent external controls, as we
                     // would need to mute the old one still if it came from a different controller.
                     alreadyUnderExternalControl = true;
-                    mCurrentExternalVibration.mute();
-                    vibHolder.stats.reportInterruptedAnotherVibration(
+                    mCurrentExternalVibration.notifyEnded();
+                    externalVibration.stats.reportInterruptedAnotherVibration(
                             mCurrentExternalVibration.callerInfo);
                     endExternalVibrateLocked(
-                            new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED,
-                                    vibHolder.callerInfo),
+                            new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+                                    externalVibration.callerInfo),
                             /* continueExternalControl= */ true);
                 }
 
@@ -2144,9 +2078,9 @@
                     mVibrationSettings.update();
                 }
 
-                mCurrentExternalVibration = vibHolder;
-                vibHolder.linkToDeath();
-                vibHolder.scale(mVibrationScaler, attrs.getUsage());
+                mCurrentExternalVibration = externalVibration;
+                externalVibration.linkToDeath(this::onExternalVibrationBinderDied);
+                externalVibration.scale(mVibrationScaler, attrs.getUsage());
             }
 
             if (waitForCompletion) {
@@ -2155,27 +2089,27 @@
                     synchronized (mLock) {
                         // Trigger endExternalVibrateLocked to unlink to death recipient.
                         endExternalVibrateLocked(
-                                new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING),
+                                new Vibration.EndInfo(Status.IGNORED_ERROR_CANCELLING),
                                 /* continueExternalControl= */ false);
                         // Mute the request, vibration will be ignored.
-                        vibHolder.muteScale();
+                        externalVibration.muteScale();
                     }
-                    return vibHolder.scale;
+                    return externalVibration.getScale();
                 }
             }
             if (!alreadyUnderExternalControl) {
                 if (DEBUG) {
                     Slog.d(TAG, "Vibrator going under external control.");
                 }
-                setExternalControl(true, vibHolder.stats);
+                setExternalControl(true, externalVibration.stats);
             }
             if (DEBUG) {
                 Slog.d(TAG, "Playing external vibration: " + vib);
             }
             // Vibrator will start receiving data from external channels after this point.
             // Report current time as the vibration start time, for debugging.
-            vibHolder.stats.reportStarted();
-            return vibHolder.scale;
+            externalVibration.stats.reportStarted();
+            return externalVibration.getScale();
         }
 
         @Override
@@ -2187,7 +2121,7 @@
                         Slog.d(TAG, "Stopping external vibration: " + vib);
                     }
                     endExternalVibrateLocked(
-                            new Vibration.EndInfo(Vibration.Status.FINISHED),
+                            new Vibration.EndInfo(Status.FINISHED),
                             /* continueExternalControl= */ false);
                 }
             }
@@ -2201,6 +2135,19 @@
             }
             return false;
         }
+
+        private void onExternalVibrationBinderDied() {
+            synchronized (mLock) {
+                if (mCurrentExternalVibration != null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "External vibration finished because binder died");
+                    }
+                    endExternalVibrateLocked(
+                            new Vibration.EndInfo(Status.CANCELLED_BINDER_DIED),
+                            /* continueExternalControl= */ false);
+                }
+            }
+        }
     }
 
     /** Provide limited functionality from {@link VibratorManagerService} as shell commands. */
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ba2594a..f53dda6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -42,6 +42,7 @@
 import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_LOCK_ORIG;
 import static com.android.server.wallpaper.WallpaperUtils.getWallpaperDir;
 import static com.android.server.wallpaper.WallpaperUtils.makeWallpaperIdLocked;
+import static com.android.window.flags.Flags.avoidRebindingIntentionallyDisconnectedWallpaper;
 import static com.android.window.flags.Flags.multiCrop;
 import static com.android.window.flags.Flags.offloadColorExtraction;
 
@@ -897,6 +898,12 @@
                     return;
                 }
 
+                if (avoidRebindingIntentionallyDisconnectedWallpaper()
+                        && mWallpaper.connection == null) {
+                    Slog.w(TAG, "Trying to reset an intentionally disconnected wallpaper!");
+                    return;
+                }
+
                 if (!mWallpaper.wallpaperUpdating && mWallpaper.userId == mCurrentUserId) {
                     Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
                             + ", reverting to built-in wallpaper!");
@@ -1066,6 +1073,13 @@
                 if (mWallpaper.wallpaperUpdating) {
                     return;
                 }
+
+                if (avoidRebindingIntentionallyDisconnectedWallpaper()
+                        && mWallpaper.connection == null) {
+                    Slog.w(TAG, "Trying to rebind an intentionally disconnected wallpaper!");
+                    return;
+                }
+
                 final ComponentName wpService = mWallpaper.wallpaperComponent;
                 // The broadcast of package update could be delayed after service disconnected. Try
                 // to re-bind the service for 10 seconds.
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index c4d601d..6740153 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -19,14 +19,12 @@
 import static android.webkit.Flags.updateServiceV2;
 
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
 import android.content.res.XmlResourceParser;
 import android.os.Build;
 import android.os.RemoteException;
@@ -79,7 +77,7 @@
         XmlResourceParser parser = null;
         List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>();
         try {
-            parser = AppGlobals.getInitialApplication().getResources().getXml(
+            parser = mContext.getResources().getXml(
                     com.android.internal.R.xml.config_webview_packages);
             XmlUtils.beginDocument(parser, TAG_START);
             while(true) {
@@ -148,7 +146,7 @@
     }
 
     public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
-        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+        PackageManager pm = mContext.getPackageManager();
         return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY)
                 .getLongVersionCode();
     }
@@ -203,47 +201,48 @@
     @Override
     public void enablePackageForAllUsers(String packageName, boolean enable) {
         UserManager userManager = mContext.getSystemService(UserManager.class);
-        for(UserInfo userInfo : userManager.getUsers()) {
-            enablePackageForUser(packageName, enable, userInfo.id);
+        for (UserHandle user : userManager.getUserHandles(false)) {
+            enablePackageForUser(packageName, enable, user);
         }
     }
 
-    private void enablePackageForUser(String packageName, boolean enable, int userId) {
+    private void enablePackageForUser(String packageName, boolean enable, UserHandle user) {
+        Context contextAsUser = mContext.createContextAsUser(user, 0);
+        PackageManager pm = contextAsUser.getPackageManager();
         try {
-            AppGlobals.getPackageManager().setApplicationEnabledSetting(
+            pm.setApplicationEnabledSetting(
                     packageName,
                     enable ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
-                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0,
-                    userId, null);
-        } catch (RemoteException | IllegalArgumentException e) {
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0);
+        } catch (IllegalArgumentException e) {
             Log.w(TAG, "Tried to " + (enable ? "enable " : "disable ") + packageName
-                    + " for user " + userId + ": " + e);
+                    + " for user " + user + ": " + e);
         }
     }
 
     @Override
     public void installExistingPackageForAllUsers(String packageName) {
         UserManager userManager = mContext.getSystemService(UserManager.class);
-        for (UserInfo userInfo : userManager.getUsers()) {
-            installPackageForUser(packageName, userInfo.id);
+        for (UserHandle user : userManager.getUserHandles(false)) {
+            installPackageForUser(packageName, user);
         }
     }
 
-    private void installPackageForUser(String packageName, int userId) {
-        final Context contextAsUser = mContext.createContextAsUser(UserHandle.of(userId), 0);
-        final PackageInstaller installer = contextAsUser.getPackageManager().getPackageInstaller();
+    private void installPackageForUser(String packageName, UserHandle user) {
+        Context contextAsUser = mContext.createContextAsUser(user, 0);
+        PackageInstaller installer = contextAsUser.getPackageManager().getPackageInstaller();
         installer.installExistingPackage(packageName, PackageManager.INSTALL_REASON_UNKNOWN, null);
     }
 
     @Override
     public boolean systemIsDebuggable() {
-        return Build.IS_DEBUGGABLE;
+        return Build.isDebuggable();
     }
 
     @Override
     public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
             throws NameNotFoundException {
-        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+        PackageManager pm = mContext.getPackageManager();
         return pm.getPackageInfo(configInfo.packageName, PACKAGE_FLAGS);
     }
 
@@ -327,5 +326,5 @@
     // flags declaring we want extra info from the package manager for webview providers
     private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
             | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
-            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER;
+            | PackageManager.MATCH_ANY_USER;
 }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2c73412..7cbacd6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1617,8 +1617,7 @@
             // causing the notifying, or the recents/home window is removed, then we won't need the
             // delayed notification anymore.
             void onWMTransition(@TransitionType int type, @TransitionFlags int flags) {
-                if (Flags.delayNotificationToMagnificationWhenRecentsWindowToFrontTransition()
-                        && type == WindowManager.TRANSIT_TO_FRONT
+                if (type == WindowManager.TRANSIT_TO_FRONT
                         && (flags & TRANSIT_FLAG_IS_RECENTS) != 0) {
                     // Delay the recents to front transition notification then send after if needed.
                     mHasDelayedNotificationForRecentsToFrontTransition = true;
@@ -1765,15 +1764,7 @@
             IBinder topFocusedWindowToken = null;
 
             synchronized (mService.mGlobalLock) {
-                // If there is a recents animation running, then use the animation target as the
-                // top window state. Otherwise,do not send the windows if there is no top focus as
-                // the window manager is still looking for where to put it. We will do the work when
-                // we get a focus change callback.
-                final RecentsAnimationController controller =
-                        mService.getRecentsAnimationController();
-                final WindowState topFocusedWindowState = controller != null
-                        ? controller.getTargetAppMainWindow()
-                        : getTopFocusWindow();
+                final WindowState topFocusedWindowState = getTopFocusWindow();
                 if (topFocusedWindowState == null) {
                     if (DEBUG) {
                         Slog.d(LOG_TAG, "top focused window is null, compute it again later");
@@ -1908,10 +1899,6 @@
 
         private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
                 Region regionInScreen, Region unaccountedSpace) {
-            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
-                return false;
-            }
-
             if (a11yWindow.isFocused()) {
                 return true;
             }
@@ -2451,7 +2438,7 @@
 
                             long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
                             synchronized (mService.mGlobalLock) {
-                                mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
+                                mService.dumpDebugLocked(os, WindowTracingLogLevel.ALL);
                             }
                             os.end(tokenInner);
                             os.write(CPU_STATS, printCpuStats(reportedTimeStampNanos));
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index d8e7c77..fd2a909 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -659,7 +659,6 @@
         private boolean mIsPIPMenu;
         private boolean mIsFocused;
         private boolean mShouldMagnify;
-        private boolean mIgnoreDuetoRecentsAnimation;
         private final Region mTouchableRegionInScreen = new Region();
         private final Region mTouchableRegionInWindow = new Region();
         private WindowInfo mWindowInfo;
@@ -692,10 +691,6 @@
             instance.mIsFocused = windowState != null && windowState.isFocused();
             instance.mShouldMagnify = windowState == null || windowState.shouldMagnify();
 
-            final RecentsAnimationController controller = service.getRecentsAnimationController();
-            instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
-                    && controller.shouldIgnoreForAccessibility(windowState);
-
             final Rect windowFrame = new Rect(inputWindowHandle.frame);
             getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
                     instance.mTouchableRegionInWindow, windowFrame, magnificationInverseMatrix,
@@ -793,13 +788,6 @@
         }
 
         /**
-         * @return true if it's running the recent animation but not the target app.
-         */
-        public boolean ignoreRecentsAnimationForAccessibility() {
-            return mIgnoreDuetoRecentsAnimation;
-        }
-
-        /**
          * @return true if this window is the trusted overlay.
          */
         public boolean isTrustedOverlay() {
@@ -909,7 +897,6 @@
                     + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags)
                     + ", focused=" + mIsFocused
                     + ", shouldMagnify=" + mShouldMagnify
-                    + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation
                     + ", isTrustedOverlay=" + isTrustedOverlay()
                     + ", regionInScreen=" + mTouchableRegionInScreen
                     + ", touchableRegion=" + mTouchableRegionInWindow
diff --git a/services/core/java/com/android/server/wm/ActionChain.java b/services/core/java/com/android/server/wm/ActionChain.java
new file mode 100644
index 0000000..d63044a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActionChain.java
@@ -0,0 +1,240 @@
+/*
+ * 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.wm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+
+import com.android.window.flags.Flags;
+
+/**
+ * Represents a chain of WM actions where each action is "caused by" the prior action (except the
+ * first one of course). A whole chain is associated with one Transition (in fact, the purpose
+ * of this object is to communicate, to all callees, which transition they are part of).
+ *
+ * A single action is defined as "one logical thing requested of WM". This usually corresponds to
+ * each ingress-point into the process. For example, when starting an activity:
+ *   * the first action is to pause the current/top activity.
+ *       At this point, control leaves the process while the activity pauses.
+ *   * Then WM receives completePause (a new ingress). This is a new action that gets linked
+ *       to the prior action. This action involves resuming the next activity, at which point,
+ *       control leaves the process again.
+ *   * Eventually, when everything is done, we will have formed a chain of actions.
+ *
+ * We don't technically need to hold onto each prior action in the chain once a new action has
+ * been linked to the same transition; however, keeping the whole chain enables improved
+ * debugging and the ability to detect anomalies.
+ */
+public class ActionChain {
+    private static final String TAG = "TransitionChain";
+
+    /**
+     * Normal link type. This means the action was expected and is properly linked to the
+     * current chain.
+     */
+    static final int TYPE_NORMAL = 0;
+
+    /**
+     * This is the "default" link. It means we haven't done anything to properly track this case
+     * so it may or may not be correct. It represents the behavior as if there was no tracking.
+     *
+     * Any type that has "default" behavior uses the global "collecting transition" if it exists,
+     * otherwise it doesn't use any transition.
+     */
+    static final int TYPE_DEFAULT = 1;
+
+    /**
+     * This means the action was performed via a legacy code-path. These should be removed
+     * eventually. This will have the "default" behavior.
+     */
+    static final int TYPE_LEGACY = 2;
+
+    /** This is for a test. */
+    static final int TYPE_TEST = 3;
+
+    /** This is finishing a transition. Collection isn't supported during this. */
+    static final int TYPE_FINISH = 4;
+
+    /**
+     * Something unexpected happened so this action was started to recover from the unexpected
+     * state. This means that a "real" chain-link couldn't be determined. For now, the behavior of
+     * this is the same as "default".
+     */
+    static final int TYPE_FAILSAFE = 5;
+
+    /**
+     * Types of chain links (ie. how is this action associated with the chain it is linked to).
+     * @hide
+     */
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_NORMAL,
+            TYPE_DEFAULT,
+            TYPE_LEGACY,
+            TYPE_TEST,
+            TYPE_FINISH,
+            TYPE_FAILSAFE
+    })
+    public @interface LinkType {}
+
+    /** Identifies the entry-point of this action. */
+    @NonNull
+    final String mSource;
+
+    /** Reference to ATMS. TEMPORARY! ONLY USE THIS WHEN tracker_plumbing flag is DISABLED! */
+    @Nullable
+    ActivityTaskManagerService mTmpAtm;
+
+    /** The transition that this chain's changes belong to. */
+    @Nullable
+    Transition mTransition;
+
+    /** The previous action in the chain. */
+    @Nullable
+    ActionChain mPrevious = null;
+
+    /** Classification of how this action is connected to the chain. */
+    @LinkType int mType = TYPE_NORMAL;
+
+    /** When this Action started. */
+    long mCreateTimeMs;
+
+    private ActionChain(String source, @LinkType int type, Transition transit) {
+        mSource = source;
+        mCreateTimeMs = System.currentTimeMillis();
+        mType = type;
+        mTransition = transit;
+        if (mTransition != null) {
+            mTransition.recordChain(this);
+        }
+    }
+
+    private Transition getTransition() {
+        if (!Flags.transitTrackerPlumbing()) {
+            return mTmpAtm.getTransitionController().getCollectingTransition();
+        }
+        return mTransition;
+    }
+
+    boolean isFinishing() {
+        return mType == TYPE_FINISH;
+    }
+
+    /**
+     * Some common checks to determine (and report) whether this chain has a collecting transition.
+     */
+    private boolean expectCollecting() {
+        final Transition transition = getTransition();
+        if (transition == null) {
+            Slog.e(TAG, "Can't collect into a chain with no transition");
+            return false;
+        }
+        if (isFinishing()) {
+            Slog.e(TAG, "Trying to collect into a finished transition");
+            return false;
+        }
+        if (transition.mController.getCollectingTransition() != mTransition) {
+            Slog.e(TAG, "Mismatch between current collecting ("
+                    + transition.mController.getCollectingTransition() + ") and chain ("
+                    + transition + ")");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Helper to collect a container into the associated transition. This will automatically do
+     * nothing if the chain isn't associated with a collecting transition.
+     */
+    void collect(@NonNull WindowContainer wc) {
+        if (!wc.mTransitionController.isShellTransitionsEnabled()) return;
+        if (!expectCollecting()) return;
+        getTransition().collect(wc);
+    }
+
+    /**
+     * An interface for creating and tracking action chains.
+     */
+    static class Tracker {
+        private final ActivityTaskManagerService mAtm;
+
+        Tracker(ActivityTaskManagerService atm) {
+            mAtm = atm;
+        }
+
+        private ActionChain makeChain(String source, @LinkType int type, Transition transit) {
+            final ActionChain out = new ActionChain(source, type, transit);
+            if (!Flags.transitTrackerPlumbing()) {
+                out.mTmpAtm = mAtm;
+            }
+            return out;
+        }
+
+        private ActionChain makeChain(String source, @LinkType int type) {
+            return makeChain(source, type,
+                    mAtm.getTransitionController().getCollectingTransition());
+        }
+
+        /**
+         * Starts tracking a normal action.
+         * @see #TYPE_NORMAL
+         */
+        @NonNull
+        ActionChain start(String source, Transition transit) {
+            return makeChain(source, TYPE_NORMAL, transit);
+        }
+
+        /** @see #TYPE_DEFAULT */
+        @NonNull
+        ActionChain startDefault(String source) {
+            return makeChain(source, TYPE_DEFAULT);
+        }
+
+        /**
+         * Starts tracking an action that finishes a transition.
+         * @see #TYPE_NORMAL
+         */
+        @NonNull
+        ActionChain startFinish(String source, Transition finishTransit) {
+            return makeChain(source, TYPE_FINISH, finishTransit);
+        }
+
+        /** @see #TYPE_LEGACY */
+        @NonNull
+        ActionChain startLegacy(String source) {
+            return makeChain(source, TYPE_LEGACY, null);
+        }
+
+        /** @see #TYPE_FAILSAFE */
+        @NonNull
+        ActionChain startFailsafe(String source) {
+            return makeChain(source, TYPE_FAILSAFE);
+        }
+    }
+
+    /** Helpers for usage in tests. */
+    @NonNull
+    static ActionChain test() {
+        return new ActionChain("test", TYPE_TEST, null /* transition */);
+    }
+
+    @NonNull
+    static ActionChain testFinish(Transition toFinish) {
+        return new ActionChain("test", TYPE_FINISH, toFinish);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 0c10551..e27b2be 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1156,7 +1156,7 @@
                 }
 
                 if (rootTask.inFreeformWindowingMode()) {
-                    rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+                    rootTask.setRootTaskWindowingMode(WINDOWING_MODE_FULLSCREEN);
                     rootTask.setBounds(null);
                 } else if (!r.supportsFreeform()) {
                     throw new IllegalStateException(
@@ -1165,9 +1165,9 @@
                     // If the window is on a freeform display, set it to undefined. It will be
                     // resolved to freeform and it can adjust windowing mode when the display mode
                     // changes in runtime.
-                    rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+                    rootTask.setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
                 } else {
-                    rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+                    rootTask.setRootTaskWindowingMode(WINDOWING_MODE_FREEFORM);
                 }
             }
         } finally {
@@ -1278,7 +1278,7 @@
         if (fullscreenRequest == FULLSCREEN_MODE_REQUEST_ENTER) {
             final int restoreWindowingMode = requester.getRequestedOverrideWindowingMode();
             targetWindowingMode = WINDOWING_MODE_FULLSCREEN;
-            requester.setWindowingMode(targetWindowingMode);
+            requester.setRootTaskWindowingMode(targetWindowingMode);
             // The restore windowing mode must be set after the windowing mode is set since
             // Task#setWindowingMode resets the restore windowing mode to WINDOWING_MODE_INVALID.
             requester.mMultiWindowRestoreWindowingMode = restoreWindowingMode;
@@ -1297,9 +1297,8 @@
     public void startLockTaskModeByToken(IBinder token) {
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            if (r != null) {
-                mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */);
-            }
+            if (r == null) return;
+            mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index fb2bf39..fb5c1154 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -838,12 +838,13 @@
         }
 
         if (android.app.Flags.appStartInfoTimestamps()) {
+            final int pid = r.getPid();
             // Log here to match StatsD for time to first frame.
             mLoggerHandler.post(
                     () -> mSupervisor.mService.mWindowManager.mAmInternal.addStartInfoTimestamp(
                             ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME,
-                            timestampNs, r.getUid(), r.getPid(),
-                            info.mLastLaunchedActivity.mUserId));
+                            timestampNs, infoSnapshot.applicationInfo.uid, pid,
+                            infoSnapshot.userId));
         }
 
         return infoSnapshot;
@@ -1275,10 +1276,8 @@
         final ActivityRecord r = info.mLastLaunchedActivity;
         final long lastTopLossTime = r.topResumedStateLossTime;
         final WindowManagerService wm = mSupervisor.mService.mWindowManager;
-        final Object controller = wm.getRecentsAnimationController();
         mLoggerHandler.postDelayed(() -> {
-            if (lastTopLossTime != r.topResumedStateLossTime
-                    || controller != wm.getRecentsAnimationController()) {
+            if (lastTopLossTime != r.topResumedStateLossTime) {
                 // Skip if the animation was finished in a short time.
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5c096ec..99747e0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -109,8 +109,6 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Surface.ROTATION_270;
-import static android.view.Surface.ROTATION_90;
 import static android.view.WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15;
 import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
@@ -228,7 +226,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
-import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -236,7 +233,6 @@
 import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE;
 import static com.android.server.wm.TaskPersister.DEBUG;
@@ -341,7 +337,6 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
-import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets;
@@ -640,12 +635,6 @@
 
     private SizeConfigurationBuckets mSizeConfigurations;
 
-    /**
-     * The precomputed display insets for resolving configuration. It will be non-null if
-     * {@link #shouldCreateCompatDisplayInsets} returns {@code true}.
-     */
-    private CompatDisplayInsets mCompatDisplayInsets;
-
     @VisibleForTesting
     final TaskFragment.ConfigOverrideHint mResolveConfigHint;
 
@@ -789,30 +778,12 @@
      */
     private boolean mWillCloseOrEnterPip;
 
-    final LetterboxUiController mLetterboxUiController;
-
     /**
      * App Compat Facade
      */
     @NonNull
     final AppCompatController mAppCompatController;
 
-    /**
-     * The scale to fit at least one side of the activity to its parent. If the activity uses
-     * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
-     */
-    private float mSizeCompatScale = 1f;
-
-    /**
-     * The bounds in global coordinates for activity in size compatibility mode.
-     * @see ActivityRecord#hasSizeCompatBounds()
-     */
-    private Rect mSizeCompatBounds;
-
-    // Whether this activity is in size compatibility mode because its bounds don't fit in parent
-    // naturally.
-    private boolean mInSizeCompatModeForBounds = false;
-
     // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its
     // requested orientation, even when it's letterbox for another reason (e.g., size compat mode)
     // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false.
@@ -841,6 +812,8 @@
     /** The last set {@link DropInputMode} for this activity surface. */
     @DropInputMode
     private int mLastDropInputMode = DropInputMode.NONE;
+    /** Whether the input to this activity will be dropped during the current playing animation. */
+    private boolean mIsInputDroppedForAnimation;
 
     /**
      * Whether the application has desk mode resources. Calculated and cached when
@@ -1262,10 +1235,6 @@
         if (mPendingRelaunchCount != 0) {
             pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount);
         }
-        if (mSizeCompatScale != 1f || mSizeCompatBounds != null) {
-            pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
-                    + mSizeCompatBounds);
-        }
         if (mRemovingFromDisplay) {
             pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay);
         }
@@ -1324,7 +1293,7 @@
             pw.print(prefix); pw.println("mWaitForEnteringPinnedMode=true");
         }
 
-        mLetterboxUiController.dump(pw, prefix);
+        mAppCompatController.dump(pw, prefix);
     }
 
     static boolean dumpActivity(FileDescriptor fd, PrintWriter pw, int index, ActivityRecord r,
@@ -1679,6 +1648,15 @@
         }
     }
 
+    /** Sets if all input will be dropped as a protection during the client-driven animation. */
+    void setDropInputForAnimation(boolean isInputDroppedForAnimation) {
+        if (mIsInputDroppedForAnimation == isInputDroppedForAnimation) {
+            return;
+        }
+        mIsInputDroppedForAnimation = isInputDroppedForAnimation;
+        updateUntrustedEmbeddingInputProtection();
+    }
+
     /**
      * Sets to drop input when obscured to activity if it is embedded in untrusted mode.
      *
@@ -1691,7 +1669,10 @@
         if (getSurfaceControl() == null) {
             return;
         }
-        if (isEmbeddedInUntrustedMode()) {
+        if (mIsInputDroppedForAnimation) {
+            // Disable all input during the animation.
+            setDropInputMode(DropInputMode.ALL);
+        } else if (isEmbeddedInUntrustedMode()) {
             // Set drop input to OBSCURED when untrusted embedded.
             setDropInputMode(DropInputMode.OBSCURED);
         } else {
@@ -1834,33 +1815,36 @@
             }
         }
 
-        mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
+        mAppCompatController.getAppCompatLetterboxPolicy()
+                .onMovedToDisplay(mDisplayContent.getDisplayId());
     }
 
     void layoutLetterboxIfNeeded(WindowState winHint) {
-        mLetterboxUiController.layoutLetterboxIfNeeded(winHint);
+        mAppCompatController.getAppCompatLetterboxPolicy().start(winHint);
     }
 
     boolean hasWallpaperBackgroundForLetterbox() {
-        return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();
+        return mAppCompatController.getAppCompatLetterboxOverrides()
+                .hasWallpaperBackgroundForLetterbox();
     }
 
     void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
-        mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
+        mAppCompatController.getAppCompatLetterboxPolicy()
+                .updateLetterboxSurfaceIfNeeded(winHint, t, getPendingTransaction());
     }
 
     void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
-        mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint);
+        mAppCompatController.getAppCompatLetterboxPolicy().updateLetterboxSurfaceIfNeeded(winHint);
     }
 
     /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
     Rect getLetterboxInsets() {
-        return mLetterboxUiController.getLetterboxInsets();
+        return mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxInsets();
     }
 
     /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
     void getLetterboxInnerBounds(Rect outBounds) {
-        mLetterboxUiController.getLetterboxInnerBounds(outBounds);
+        mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxInnerBounds(outBounds);
     }
 
     /**
@@ -1868,7 +1852,8 @@
      *     when the current activity is displayed.
      */
     boolean isFullyTransparentBarAllowed(Rect rect) {
-        return mLetterboxUiController.isFullyTransparentBarAllowed(rect);
+        return mAppCompatController.getAppCompatLetterboxPolicy()
+                .isFullyTransparentBarAllowed(rect);
     }
 
     private static class Token extends Binder {
@@ -1984,12 +1969,10 @@
         }
 
         // Don't move below setActivityType since it triggers onConfigurationChange ->
-        // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
+        // resolveOverrideConfiguration that requires having mAppCompatController initialised.
         // Don't move below setOrientation(info.screenOrientation) since it triggers
-        // getOverrideOrientation that requires having mLetterboxUiController
-        // initialised.
+        // getOverrideOrientation that requires having mAppCompatController initialised.
         mAppCompatController = new AppCompatController(mWmService, this);
-        mLetterboxUiController = new LetterboxUiController(mWmService, this);
         mResolveConfigHint = new TaskFragment.ConfigOverrideHint();
         if (mWmService.mFlags.mInsetsDecoupledConfiguration) {
             // When the stable configuration is the default behavior, override for the legacy apps
@@ -2012,7 +1995,7 @@
         mIsUserAlwaysVisible =  properties != null && properties.getAlwaysVisible();
 
         mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0 || mIsUserAlwaysVisible;
-        setOrientation(info.screenOrientation);
+        setOverrideOrientation(info.screenOrientation);
         mRotationAnimationHint = info.rotationAnimation;
 
         mShowWhenLocked = (aInfo.flags & ActivityInfo.FLAG_SHOW_WHEN_LOCKED) != 0;
@@ -2650,7 +2633,8 @@
                 || mStartingWindow == null
                 || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
                 // skip copy splash screen to client if it was resized
-                || (mStartingData != null && mStartingData.mResizedFromTransfer)) {
+                || (mStartingData != null && mStartingData.mResizedFromTransfer)
+                || isRelaunching()) {
             return false;
         }
         if (isTransferringSplashScreen()) {
@@ -3292,6 +3276,12 @@
             return false;
         }
 
+        // Check if activity is top activity of its task fragment - this prevents any trampolines
+        // followed by enterPictureInPictureMode() calls by an activity from below in its stack.
+        if (getTaskFragment() == null || getTaskFragment().getTopNonFinishingActivity() != this) {
+            return false;
+        }
+
         // Check to see if PiP is supported for the display this container is on.
         if (mDisplayContent != null && !mDisplayContent.mDwpcHelper.isEnteringPipAllowed(
                 getUid())) {
@@ -3622,10 +3612,10 @@
             final WindowContainer<?> trigger = endTask ? task : this;
             final Transition newTransition =
                     mTransitionController.requestCloseTransitionIfNeeded(trigger);
-            if (newTransition != null) {
-                newTransition.collectClose(trigger);
-            } else if (mTransitionController.isCollecting()) {
-                mTransitionController.getCollectingTransition().collectClose(trigger);
+            final Transition transition = newTransition != null
+                    ? newTransition : mTransitionController.getCollectingTransition();
+            if (transition != null) {
+                transition.collectClose(trigger);
             }
             if (isState(RESUMED)) {
                 if (endTask) {
@@ -4298,9 +4288,9 @@
         // closing the task.
         final WindowContainer trigger = remove && task != null && task.getChildCount() == 1
                 ? task : this;
-        final Transition newTransit = mTransitionController.requestCloseTransitionIfNeeded(trigger);
-        if (newTransit != null) {
-            newTransit.collectClose(trigger);
+        final Transition tr = mTransitionController.requestCloseTransitionIfNeeded(trigger);
+        if (tr != null) {
+            tr.collectClose(trigger);
         } else if (mTransitionController.isCollecting()) {
             mTransitionController.getCollectingTransition().collectClose(trigger);
         }
@@ -4368,7 +4358,8 @@
 
         mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         mTaskSupervisor.mStoppingActivities.remove(this);
-        mLetterboxUiController.destroy();
+
+        mAppCompatController.getAppCompatLetterboxPolicy().stop();
         mAppCompatController.getTransparentPolicy().stop();
 
         // Defer removal of this activity when either a child is animating, or app transition is on
@@ -4965,9 +4956,8 @@
         newIntents.add(intent);
     }
 
-    final boolean isSleeping() {
-        final Task rootTask = getRootTask();
-        return rootTask != null ? rootTask.shouldSleepActivities() : mAtmService.isSleepingLocked();
+    boolean isSleeping() {
+        return task != null ? task.shouldSleepActivities() : mAtmService.isSleepingLocked();
     }
 
     /**
@@ -4991,7 +4981,7 @@
         final ReferrerIntent rintent = new ReferrerIntent(intent, getFilteredReferrer(referrer),
                 callerToken);
         boolean unsent = true;
-        final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
+        final boolean isTopActivityWhileSleeping = isSleeping() && isTopRunningActivity();
 
         // We want to immediately deliver the intent to the activity if:
         // - It is currently resumed or paused. i.e. it is currently visible to the user and we want
@@ -5315,12 +5305,14 @@
         task.lastDescription = description;
     }
 
-    void setDeferHidingClient(boolean deferHidingClient) {
-        if (mDeferHidingClient == deferHidingClient) {
-            return;
-        }
-        mDeferHidingClient = deferHidingClient;
-        if (!mDeferHidingClient && !mVisibleRequested) {
+    void setDeferHidingClient() {
+        mDeferHidingClient = true;
+    }
+
+    void clearDeferHidingClient() {
+        if (!mDeferHidingClient) return;
+        mDeferHidingClient = false;
+        if (!mVisibleRequested) {
             // Hiding the client is no longer deferred and the app isn't visible still, go ahead and
             // update the visibility.
             setVisibility(false);
@@ -5444,8 +5436,8 @@
         boolean isCollecting = false;
         boolean inFinishingTransition = false;
         if (mTransitionController.isShellTransitionsEnabled()) {
-            isCollecting = mTransitionController.isCollecting();
-            if (isCollecting) {
+            if (mTransitionController.isCollecting()) {
+                isCollecting = true;
                 mTransitionController.collect(this);
             } else {
                 // Failsafe to make sure that we show any activities that were incorrectly hidden
@@ -5564,10 +5556,7 @@
             return false;
         }
         if (!mDisplayContent.mAppTransition.isTransitionSet()) {
-            // Defer committing visibility for non-home app which is animating by recents.
-            if (isActivityTypeHome() || !isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
-                return false;
-            }
+            return false;
         }
         if (mWaitForEnteringPinnedMode && mVisible == visible) {
             // If the visibility is not changed during enter PIP, we don't want to include it in
@@ -5721,8 +5710,7 @@
     private void postApplyAnimation(boolean visible, boolean fromTransition) {
         final boolean usingShellTransitions = mTransitionController.isShellTransitionsEnabled();
         final boolean delayed = !usingShellTransitions && isAnimating(PARENTS | CHILDREN,
-                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION
-                        | ANIMATION_TYPE_RECENTS);
+                ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION);
         if (!delayed && !usingShellTransitions) {
             // We aren't delayed anything, but exiting windows rely on the animation finished
             // callback being called in case the ActivityRecord was pretending to be delayed,
@@ -5744,7 +5732,7 @@
         // animation and aren't in RESUMED state. Otherwise, we'll update client visibility in
         // onAnimationFinished or activityStopped.
         if (visible || (mState != RESUMED && (usingShellTransitions || !isAnimating(
-                PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)))) {
+                PARENTS, ANIMATION_TYPE_APP_TRANSITION)))) {
             setClientVisible(visible);
         }
 
@@ -5856,7 +5844,7 @@
 
     void setState(State state, String reason) {
         ProtoLog.v(WM_DEBUG_STATES, "State movement: %s from:%s to:%s reason:%s",
-                this, getState(), state, reason);
+                this, mState, state, reason);
 
         if (state == mState) {
             // No need to do anything if state doesn't change.
@@ -5916,6 +5904,7 @@
                     mAtmService.updateBatteryStats(this, false);
                 }
                 mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+                idle = false;
                 // Fall through.
             case DESTROYING:
                 if (app != null && !app.hasActivities()) {
@@ -6181,7 +6170,7 @@
         // Now for any activities that aren't visible to the user, make sure they no longer are
         // keeping the screen frozen.
         if (DEBUG_VISIBILITY) {
-            Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + getState());
+            Slog.v(TAG_VISIBILITY, "Making invisible: " + this + ", state=" + mState);
         }
         try {
             final boolean canEnterPictureInPicture = checkEnterPictureInPictureState(
@@ -6190,10 +6179,14 @@
             // stopped or stopping. This gives it a chance to enter Pip in onPause().
             final boolean deferHidingClient = canEnterPictureInPicture
                     && !isState(STARTED, STOPPING, STOPPED, PAUSED);
-            setDeferHidingClient(deferHidingClient);
+            if (deferHidingClient) {
+                setDeferHidingClient();
+            } else {
+                clearDeferHidingClient();
+            }
             setVisibility(false);
 
-            switch (getState()) {
+            switch (mState) {
                 case STOPPING:
                 case STOPPED:
                     // Reset the flag indicating that an app can enter picture-in-picture once the
@@ -6229,7 +6222,7 @@
                 Slog.v(TAG_VISIBILITY, "Resume visible activity, " + this);
             }
             return getRootTask().resumeTopActivityUncheckedLocked(activeActivity /* prev */,
-                    null /* options */);
+                    null /* options */, false /* skipPause */);
         } else if (shouldPauseActivity(activeActivity)) {
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG_VISIBILITY, "Pause visible activity, " + this);
@@ -6429,7 +6422,7 @@
         mTaskSupervisor.mStoppingActivities.remove(this);
         if (getDisplayArea().allResumedActivitiesComplete()) {
             // Construct the compat environment at a relatively stable state if needed.
-            updateCompatDisplayInsets();
+            mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
             mRootWindowContainer.executeAppTransitionForAllDisplay();
         }
 
@@ -6519,9 +6512,7 @@
     void stopIfPossible() {
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
         if (finishing) {
-            Slog.e(TAG, "Request to stop a finishing activity: " + this);
-            destroyIfPossible("stopIfPossible-finishing");
-            return;
+            throw new IllegalStateException("Request to stop a finishing activity: " + this);
         }
         if (isNoHistory()) {
             if (!task.shouldSleepActivities()) {
@@ -7533,6 +7524,10 @@
         if (mStartingWindow == win) {
             // This could only happen when the window is removed from hierarchy. So do not keep its
             // reference anymore.
+            if (mStartingSurface != null) {
+                // Ensure the reference in client side can be removed.
+                mStartingSurface.remove(false /* animate */, false /* hasImeSurface */);
+            }
             mStartingWindow = null;
             mStartingData = null;
             mStartingSurface = null;
@@ -7688,7 +7683,7 @@
             t.setLayer(mAnimationBoundsLayer, getLastLayer());
 
             if (mNeedsLetterboxedAnimation) {
-                final int cornerRadius = mLetterboxUiController
+                final int cornerRadius = mAppCompatController.getAppCompatLetterboxPolicy()
                         .getRoundedCornersRadius(findMainWindow());
 
                 final Rect letterboxInnerBounds = new Rect();
@@ -7716,7 +7711,7 @@
                 // Ensure that the activity content is hidden when the decor surface is boosted to
                 // prevent UI redressing attack.
                 && !isDecorSurfaceBoosted)
-                || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS
+                || isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION
                         | ANIMATION_TYPE_PREDICT_BACK);
 
         if (mSurfaceControl != null) {
@@ -8059,8 +8054,8 @@
         if (getRequestedConfigurationOrientation(false, requestedOrientation)
                     != getRequestedConfigurationOrientation(false /*forDisplay */)) {
             // Do not change the requested configuration now, because this will be done when setting
-            // the orientation below with the new mCompatDisplayInsets
-            clearSizeCompatModeAttributes();
+            // the orientation below with the new mAppCompatDisplayInsets
+            mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatModeAttributes();
         }
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Setting requested orientation %s for %s",
@@ -8154,6 +8149,9 @@
      */
     @Override
     protected int getOverrideOrientation() {
+        if (mWmService.mConstants.mIgnoreActivityOrientationRequest) {
+            return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        }
         return mAppCompatController.getOrientationPolicy()
                 .overrideOrientationIfNeeded(super.getOverrideOrientation());
     }
@@ -8192,19 +8190,8 @@
     }
 
     @Nullable
-    CompatDisplayInsets getCompatDisplayInsets() {
-        if (mAppCompatController.getTransparentPolicy().isRunning()) {
-            return mAppCompatController.getTransparentPolicy().getInheritedCompatDisplayInsets();
-        }
-        return mCompatDisplayInsets;
-    }
-
-    /**
-     * @return The {@code true} if the current instance has {@link mCompatDisplayInsets} without
-     * considering the inheritance implemented in {@link #getCompatDisplayInsets()}
-     */
-    boolean hasCompatDisplayInsetsWithoutInheritance() {
-        return mCompatDisplayInsets != null;
+    AppCompatDisplayInsets getAppCompatDisplayInsets() {
+        return mAppCompatController.getAppCompatSizeCompatModePolicy().getAppCompatDisplayInsets();
     }
 
     /**
@@ -8212,10 +8199,12 @@
      *         density than its parent or its bounds don't fit in parent naturally.
      */
     boolean inSizeCompatMode() {
-        if (mInSizeCompatModeForBounds) {
+        final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
+                .getAppCompatSizeCompatModePolicy();
+        if (scmPolicy.isInSizeCompatModeForBounds()) {
             return true;
         }
-        if (getCompatDisplayInsets() == null || !shouldCreateCompatDisplayInsets()
+        if (getAppCompatDisplayInsets() == null || !shouldCreateAppCompatDisplayInsets()
                 // The orientation is different from parent when transforming.
                 || isFixedRotationTransforming()) {
             return false;
@@ -8241,13 +8230,13 @@
      * Indicates the activity will keep the bounds and screen configuration when it was first
      * launched, no matter how its parent changes.
      *
-     * <p>If {@true}, then {@link CompatDisplayInsets} will be created in {@link
+     * <p>If {@true}, then {@link AppCompatDisplayInsets} will be created in {@link
      * #resolveOverrideConfiguration} to "freeze" activity bounds and insets.
      *
      * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
      *         aspect ratio.
      */
-    boolean shouldCreateCompatDisplayInsets() {
+    boolean shouldCreateAppCompatDisplayInsets() {
         if (mAppCompatController.getAppCompatAspectRatioOverrides().hasFullscreenOverride()) {
             // If the user has forced the applications aspect ratio to be fullscreen, don't use size
             // compatibility mode in any situation. The user has been warned and therefore accepts
@@ -8269,7 +8258,7 @@
         final TaskDisplayArea tda = getTaskDisplayArea();
         if (inMultiWindowMode() || (tda != null && tda.inFreeformWindowingMode())) {
             final ActivityRecord root = task != null ? task.getRootActivity() : null;
-            if (root != null && root != this && !root.shouldCreateCompatDisplayInsets()) {
+            if (root != null && root != this && !root.shouldCreateAppCompatDisplayInsets()) {
                 // If the root activity doesn't use size compatibility mode, the activities above
                 // are forced to be the same for consistent visual appearance.
                 return false;
@@ -8309,69 +8298,7 @@
 
     @Override
     boolean hasSizeCompatBounds() {
-        return mSizeCompatBounds != null;
-    }
-
-    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
-    private void updateCompatDisplayInsets() {
-        if (getCompatDisplayInsets() != null || !shouldCreateCompatDisplayInsets()) {
-            // The override configuration is set only once in size compatibility mode.
-            return;
-        }
-
-        Configuration overrideConfig = getRequestedOverrideConfiguration();
-        final Configuration fullConfig = getConfiguration();
-
-        // Ensure the screen related fields are set. It is used to prevent activity relaunch
-        // when moving between displays. For screenWidthDp and screenWidthDp, because they
-        // are relative to bounds and density, they will be calculated in
-        // {@link Task#computeConfigResourceOverrides} and the result will also be
-        // relatively fixed.
-        overrideConfig.colorMode = fullConfig.colorMode;
-        overrideConfig.densityDpi = fullConfig.densityDpi;
-        // The smallest screen width is the short side of screen bounds. Because the bounds
-        // and density won't be changed, smallestScreenWidthDp is also fixed.
-        overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp;
-        if (ActivityInfo.isFixedOrientation(getOverrideOrientation())) {
-            // lock rotation too. When in size-compat, onConfigurationChanged will watch for and
-            // apply runtime rotation changes.
-            overrideConfig.windowConfiguration.setRotation(
-                    fullConfig.windowConfiguration.getRotation());
-        }
-
-        final Rect letterboxedContainerBounds = mAppCompatController
-                .getAppCompatAspectRatioPolicy().getLetterboxedContainerBounds();
-
-        // The role of CompatDisplayInsets is like the override bounds.
-        mCompatDisplayInsets =
-                new CompatDisplayInsets(
-                        mDisplayContent, this, letterboxedContainerBounds,
-                        mResolveConfigHint.mUseOverrideInsetsForConfig);
-    }
-
-    private void clearSizeCompatModeAttributes() {
-        mInSizeCompatModeForBounds = false;
-        final float lastSizeCompatScale = mSizeCompatScale;
-        mSizeCompatScale = 1f;
-        if (mSizeCompatScale != lastSizeCompatScale) {
-            forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
-        }
-        mSizeCompatBounds = null;
-        mCompatDisplayInsets = null;
-        mAppCompatController.getTransparentPolicy().clearInheritedCompatDisplayInsets();
-    }
-
-    @VisibleForTesting
-    void clearSizeCompatMode() {
-        clearSizeCompatModeAttributes();
-        // Clear config override in #updateCompatDisplayInsets().
-        final int activityType = getActivityType();
-        final Configuration overrideConfig = getRequestedOverrideConfiguration();
-        overrideConfig.unset();
-        // Keep the activity type which was set when attaching to a task to prevent leaving it
-        // undefined.
-        overrideConfig.windowConfiguration.setActivityType(activityType);
-        onRequestedOverrideConfigurationChanged(overrideConfig);
+        return mAppCompatController.getAppCompatSizeCompatModePolicy().hasSizeCompatBounds();
     }
 
     @Override
@@ -8389,7 +8316,9 @@
 
     @Override
     float getCompatScale() {
-        return hasSizeCompatBounds() ? mSizeCompatScale : super.getCompatScale();
+        // We need to invoke {#getCompatScale()} only if the CompatScale is not available.
+        return mAppCompatController.getAppCompatSizeCompatModePolicy()
+                .getCompatScaleIfAvailable(ActivityRecord.super::getCompatScale);
     }
 
     @Override
@@ -8456,9 +8385,12 @@
                     .hasFullscreenOverride()) {
             resolveAspectRatioRestriction(newParentConfiguration);
         }
-        final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets();
-        if (compatDisplayInsets != null) {
-            resolveSizeCompatModeConfiguration(newParentConfiguration, compatDisplayInsets);
+        final AppCompatDisplayInsets appCompatDisplayInsets = getAppCompatDisplayInsets();
+        final AppCompatSizeCompatModePolicy scmPolicy =
+                mAppCompatController.getAppCompatSizeCompatModePolicy();
+        if (appCompatDisplayInsets != null) {
+            scmPolicy.resolveSizeCompatModeConfiguration(newParentConfiguration,
+                    appCompatDisplayInsets, mTmpBounds);
         } else if (inMultiWindowMode() && !isFixedOrientationLetterboxAllowed) {
             // We ignore activities' requested orientation in multi-window modes. They may be
             // taken into consideration in resolveFixedOrientationConfiguration call above.
@@ -8476,13 +8408,14 @@
         if (!Flags.immersiveAppRepositioning()
                 && !mAppCompatController.getAppCompatAspectRatioPolicy()
                     .isLetterboxedForFixedOrientationAndAspectRatio()
-                && !mInSizeCompatModeForBounds
+                && !scmPolicy.isInSizeCompatModeForBounds()
                 && !mAppCompatController.getAppCompatAspectRatioOverrides()
                     .hasFullscreenOverride()) {
             resolveAspectRatioRestriction(newParentConfiguration);
         }
 
-        if (isFixedOrientationLetterboxAllowed || compatDisplayInsets != null
+        if (isFixedOrientationLetterboxAllowed
+                || scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
                 // In fullscreen, can be letterboxed for aspect ratio.
                 || !inMultiWindowMode()) {
             updateResolvedBoundsPosition(newParentConfiguration);
@@ -8490,8 +8423,8 @@
 
         boolean isIgnoreOrientationRequest = mDisplayContent != null
                 && mDisplayContent.getIgnoreOrientationRequest();
-        if (compatDisplayInsets == null
-                // for size compat mode set in updateCompatDisplayInsets
+        if (!scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
+                // for size compat mode set in updateAppCompatDisplayInsets
                 // Fixed orientation letterboxing is possible on both large screen devices
                 // with ignoreOrientationRequest enabled and on phones in split screen even with
                 // ignoreOrientationRequest disabled.
@@ -8518,7 +8451,7 @@
         getResolvedOverrideConfiguration().seq = mConfigurationSeq;
 
         // Sandbox max bounds by setting it to the activity bounds, if activity is letterboxed, or
-        // has or will have mCompatDisplayInsets for size compat. Also forces an activity to be
+        // has or will have mAppCompatDisplayInsets for size compat. Also forces an activity to be
         // sandboxed or not depending upon the configuration settings.
         if (providesMaxBounds()) {
             mTmpBounds.set(resolvedConfig.windowConfiguration.getBounds());
@@ -8539,8 +8472,8 @@
                         info.neverSandboxDisplayApis(sConstrainDisplayApisConfig),
                         info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig),
                         !matchParentBounds(),
-                        compatDisplayInsets != null,
-                        shouldCreateCompatDisplayInsets());
+                        scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance(),
+                        shouldCreateAppCompatDisplayInsets());
             }
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
@@ -8552,7 +8485,8 @@
                 resolvedConfig,
                 mOptOutEdgeToEdge,
                 hasFixedRotationTransform(),
-                getCompatDisplayInsets() != null);
+                getAppCompatDisplayInsets() != null,
+                task);
         mResolveConfigHint.resetTmpOverrides();
 
         logAppCompatState();
@@ -8562,7 +8496,7 @@
         return Rect.copyOrNull(mResolveConfigHint.mParentAppBoundsOverride);
     }
 
-    private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
+    void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
             @NonNull Configuration parentConfig) {
         task.computeConfigResourceOverrides(resolvedConfig, parentConfig, mResolveConfigHint);
         // Reset the temp info which should only take effect for the specified computation.
@@ -8574,7 +8508,7 @@
      * Returns whether activity bounds are letterboxed.
      *
      * <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
-     * LetterboxUiController#shouldShowLetterboxUi} for more context.
+     * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
      */
     boolean areBoundsLetterboxed() {
         return getAppCompatState(/* ignoreVisibility= */ true)
@@ -8614,7 +8548,9 @@
         if (mAppCompatController.getTransparentPolicy().isRunning()) {
             return mAppCompatController.getTransparentPolicy().getInheritedAppCompatState();
         }
-        if (mInSizeCompatModeForBounds) {
+        final AppCompatSizeCompatModePolicy scmPolicy = mAppCompatController
+                .getAppCompatSizeCompatModePolicy();
+        if (scmPolicy.isInSizeCompatModeForBounds()) {
             return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
         }
         // Letterbox for fixed orientation. This check returns true only when an activity is
@@ -8650,8 +8586,9 @@
         if (resolvedBounds.isEmpty()) {
             return;
         }
-        final Rect screenResolvedBounds =
-                mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
+        final AppCompatSizeCompatModePolicy scmPolicy =
+                mAppCompatController.getAppCompatSizeCompatModePolicy();
+        final Rect screenResolvedBounds = scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
         final Rect parentAppBounds = mResolveConfigHint.mParentAppBoundsOverride;
         final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
         final float screenResolvedBoundsWidth = screenResolvedBounds.width();
@@ -8681,7 +8618,7 @@
                 offsetX = Math.max(0, (int) Math.ceil((appWidth
                         - screenResolvedBoundsWidth) * positionMultiplier)
                         // This is added to make sure that insets added inside
-                        // CompatDisplayInsets#getContainerBounds() do not break the alignment
+                        // AppCompatDisplayInsets#getContainerBounds() do not break the alignment
                         // provided by the positionMultiplier
                         - screenResolvedBounds.left + parentAppBounds.left);
             }
@@ -8702,19 +8639,15 @@
                 offsetY = Math.max(0, (int) Math.ceil((appHeight
                         - screenResolvedBoundsHeight) * positionMultiplier)
                         // This is added to make sure that insets added inside
-                        // CompatDisplayInsets#getContainerBounds() do not break the alignment
+                        // AppCompatDisplayInsets#getContainerBounds() do not break the alignment
                         // provided by the positionMultiplier
                         - screenResolvedBounds.top + parentAppBounds.top);
             }
         }
-
-        if (mSizeCompatBounds != null) {
-            mSizeCompatBounds.offset(offsetX , offsetY);
-            final int dy = mSizeCompatBounds.top - resolvedBounds.top;
-            final int dx = mSizeCompatBounds.left - resolvedBounds.left;
-            offsetBounds(resolvedConfig, dx, dy);
-        } else {
-            offsetBounds(resolvedConfig, offsetX, offsetY);
+        // If in SCM, apply offset to resolved bounds relative to size compat bounds. If
+        // not, apply directly to resolved bounds.
+        if (!scmPolicy.applyOffsetIfNeeded(resolvedBounds, resolvedConfig, offsetX, offsetY)) {
+            AppCompatUtils.offsetBounds(resolvedConfig, offsetX, offsetY);
         }
 
         // If the top is aligned with parentAppBounds add the vertical insets back so that the app
@@ -8722,9 +8655,7 @@
         if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top
                 && !isImmersiveMode) {
             resolvedConfig.windowConfiguration.getBounds().top = parentBounds.top;
-            if (mSizeCompatBounds != null) {
-                mSizeCompatBounds.top = parentBounds.top;
-            }
+            scmPolicy.alignToTopIfNeeded(parentBounds);
         }
 
         // Since bounds has changed, the configuration needs to be computed accordingly.
@@ -8734,13 +8665,7 @@
         // easier to resolve the relative position in parent container. However, if the activity is
         // scaled, the position should follow the scale because the configuration will be sent to
         // the client which is expected to be in a scaled environment.
-        if (mSizeCompatScale != 1f) {
-            final int screenPosX = resolvedBounds.left;
-            final int screenPosY = resolvedBounds.top;
-            final int dx = (int) (screenPosX / mSizeCompatScale + 0.5f) - screenPosX;
-            final int dy = (int) (screenPosY / mSizeCompatScale + 0.5f) - screenPosY;
-            offsetBounds(resolvedConfig, dx, dy);
-        }
+        scmPolicy.applySizeCompatScaleIfNeeded(resolvedBounds, resolvedConfig);
     }
 
     boolean isImmersiveMode(@NonNull Rect parentBounds) {
@@ -8762,7 +8687,9 @@
     @NonNull Rect getScreenResolvedBounds() {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
-        return mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds;
+        final AppCompatSizeCompatModePolicy scmPolicy =
+                mAppCompatController.getAppCompatSizeCompatModePolicy();
+        return scmPolicy.replaceResolvedBoundsIfNeeded(resolvedBounds);
     }
 
     void recomputeConfiguration() {
@@ -8913,10 +8840,12 @@
                 || orientationRespectedWithInsets)) {
             return;
         }
-        final CompatDisplayInsets compatDisplayInsets = getCompatDisplayInsets();
+        final AppCompatDisplayInsets mAppCompatDisplayInsets = getAppCompatDisplayInsets();
+        final AppCompatSizeCompatModePolicy scmPolicy =
+                mAppCompatController.getAppCompatSizeCompatModePolicy();
 
-        if (compatDisplayInsets != null
-                && !compatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) {
+        if (scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()
+                && !mAppCompatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) {
             // App prefers to keep its original size.
             // If the size compat is from previous fixed orientation letterboxing, we may want to
             // have fixed orientation letterbox again, otherwise it will show the size compat
@@ -8968,8 +8897,8 @@
                 .applyDesiredAspectRatio(newParentConfig, parentBounds, resolvedBounds,
                         containingBoundsWithInsets, containingBounds);
 
-        if (compatDisplayInsets != null) {
-            compatDisplayInsets.getBoundsByRotation(mTmpBounds,
+        if (scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()) {
+            mAppCompatDisplayInsets.getBoundsByRotation(mTmpBounds,
                     newParentConfig.windowConfiguration.getRotation());
             if (resolvedBounds.width() != mTmpBounds.width()
                     || resolvedBounds.height() != mTmpBounds.height()) {
@@ -8992,7 +8921,7 @@
 
         // Calculate app bounds using fixed orientation bounds because they will be needed later
         // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
-        mResolveConfigHint.mTmpCompatInsets = compatDisplayInsets;
+        mResolveConfigHint.mTmpCompatInsets = mAppCompatDisplayInsets;
         computeConfigByResolveHint(getResolvedOverrideConfiguration(), newParentConfig);
         mAppCompatController.getAppCompatAspectRatioPolicy()
                 .setLetterboxBoundsForFixedOrientationAndAspectRatio(new Rect(resolvedBounds));
@@ -9029,246 +8958,15 @@
         }
     }
 
-    /**
-     * Resolves consistent screen configuration for orientation and rotation changes without
-     * inheriting the parent bounds.
-     */
-    private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration,
-            @NonNull CompatDisplayInsets compatDisplayInsets) {
-        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
-        final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
-
-        // When an activity needs to be letterboxed because of fixed orientation, use fixed
-        // orientation bounds (stored in resolved bounds) instead of parent bounds since the
-        // activity will be displayed within them even if it is in size compat mode. They should be
-        // saved here before resolved bounds are overridden below.
-        final boolean useResolvedBounds = Flags.immersiveAppRepositioning()
-                ? mAppCompatController.getAppCompatAspectRatioPolicy()
-                    .isAspectRatioApplied()
-                : mAppCompatController.getAppCompatAspectRatioPolicy()
-                    .isLetterboxedForFixedOrientationAndAspectRatio();
-        final Rect containerBounds = useResolvedBounds
-                ? new Rect(resolvedBounds)
-                : newParentConfiguration.windowConfiguration.getBounds();
-        final Rect containerAppBounds = useResolvedBounds
-                ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
-                : mResolveConfigHint.mParentAppBoundsOverride;
-
-        final int requestedOrientation = getRequestedConfigurationOrientation();
-        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
-        final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig
-                ? mResolveConfigHint.mTmpOverrideConfigOrientation
-                : newParentConfiguration.orientation;
-        final int orientation = orientationRequested
-                ? requestedOrientation
-                // We should use the original orientation of the activity when possible to avoid
-                // forcing the activity in the opposite orientation.
-                : compatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
-                        ? compatDisplayInsets.mOriginalRequestedOrientation
-                        : parentOrientation;
-        int rotation = newParentConfiguration.windowConfiguration.getRotation();
-        final boolean isFixedToUserRotation = mDisplayContent == null
-                || mDisplayContent.getDisplayRotation().isFixedToUserRotation();
-        if (!isFixedToUserRotation && !compatDisplayInsets.mIsFloating) {
-            // Use parent rotation because the original display can be rotated.
-            resolvedConfig.windowConfiguration.setRotation(rotation);
-        } else {
-            final int overrideRotation = resolvedConfig.windowConfiguration.getRotation();
-            if (overrideRotation != ROTATION_UNDEFINED) {
-                rotation = overrideRotation;
-            }
-        }
-
-        // Use compat insets to lock width and height. We should not use the parent width and height
-        // because apps in compat mode should have a constant width and height. The compat insets
-        // are locked when the app is first launched and are never changed after that, so we can
-        // rely on them to contain the original and unchanging width and height of the app.
-        final Rect containingAppBounds = new Rect();
-        final Rect containingBounds = mTmpBounds;
-        compatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
-                orientation, orientationRequested, isFixedToUserRotation);
-        resolvedBounds.set(containingBounds);
-        // The size of floating task is fixed (only swap), so the aspect ratio is already correct.
-        if (!compatDisplayInsets.mIsFloating) {
-            mAppCompatController.getAppCompatAspectRatioPolicy()
-                    .applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
-                            containingBounds);
-        }
-
-        // Use resolvedBounds to compute other override configurations such as appBounds. The bounds
-        // are calculated in compat container space. The actual position on screen will be applied
-        // later, so the calculation is simpler that doesn't need to involve offset from parent.
-        mResolveConfigHint.mTmpCompatInsets = compatDisplayInsets;
-        computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
-        // Use current screen layout as source because the size of app is independent to parent.
-        resolvedConfig.screenLayout = computeScreenLayout(
-                getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
-                resolvedConfig.screenHeightDp);
-
-        // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
-        // the parent bounds appropriately.
-        if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
-            resolvedConfig.orientation = parentOrientation;
-        }
-
-        // Below figure is an example that puts an activity which was launched in a larger container
-        // into a smaller container.
-        //   The outermost rectangle is the real display bounds.
-        //   "@" is the container app bounds (parent bounds or fixed orientation bounds)
-        //   "#" is the {@code resolvedBounds} that applies to application.
-        //   "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled.
-        // ------------------------------
-        // |                            |
-        // |    @@@@*********@@@@###    |
-        // |    @   *       *   @  #    |
-        // |    @   *       *   @  #    |
-        // |    @   *       *   @  #    |
-        // |    @@@@*********@@@@  #    |
-        // ---------#--------------#-----
-        //          #              #
-        //          ################
-        // The application is still layouted in "#" since it was launched, and it will be visually
-        // scaled and positioned to "*".
-
-        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
-
-        // Calculates the scale the size compatibility bounds into the region which is available
-        // to application.
-        final float lastSizeCompatScale = mSizeCompatScale;
-        updateSizeCompatScale(resolvedAppBounds, containerAppBounds);
-
-        final int containerTopInset = containerAppBounds.top - containerBounds.top;
-        final boolean topNotAligned =
-                containerTopInset != resolvedAppBounds.top - resolvedBounds.top;
-        if (mSizeCompatScale != 1f || topNotAligned) {
-            if (mSizeCompatBounds == null) {
-                mSizeCompatBounds = new Rect();
-            }
-            mSizeCompatBounds.set(resolvedAppBounds);
-            mSizeCompatBounds.offsetTo(0, 0);
-            mSizeCompatBounds.scale(mSizeCompatScale);
-            // The insets are included in height, e.g. the area of real cutout shouldn't be scaled.
-            mSizeCompatBounds.bottom += containerTopInset;
-        } else {
-            mSizeCompatBounds = null;
-        }
-        if (mSizeCompatScale != lastSizeCompatScale) {
-            forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */);
-        }
-
-        // The position will be later adjusted in updateResolvedBoundsPosition.
-        // Above coordinates are in "@" space, now place "*" and "#" to screen space.
-        final boolean fillContainer = resolvedBounds.equals(containingBounds);
-        final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
-        final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
-
-        if (screenPosX != 0 || screenPosY != 0) {
-            if (mSizeCompatBounds != null) {
-                mSizeCompatBounds.offset(screenPosX, screenPosY);
-            }
-            // Add the global coordinates and remove the local coordinates.
-            final int dx = screenPosX - resolvedBounds.left;
-            final int dy = screenPosY - resolvedBounds.top;
-            offsetBounds(resolvedConfig, dx, dy);
-        }
-
-        mInSizeCompatModeForBounds =
-                isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds);
-    }
-
-    void updateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) {
-        mSizeCompatScale = mAppCompatController.getTransparentPolicy()
-                .findOpaqueNotFinishingActivityBelow()
-                .map(activityRecord -> activityRecord.mSizeCompatScale)
-                .orElseGet(() -> calculateSizeCompatScale(resolvedAppBounds, containerAppBounds));
-    }
-
-    private float calculateSizeCompatScale(Rect resolvedAppBounds, Rect containerAppBounds) {
-        final int contentW = resolvedAppBounds.width();
-        final int contentH = resolvedAppBounds.height();
-        final int viewportW = containerAppBounds.width();
-        final int viewportH = containerAppBounds.height();
-        // Allow an application to be up-scaled if its window is smaller than its
-        // original container or if it's a freeform window in desktop mode.
-        boolean shouldAllowUpscaling = !(contentW <= viewportW && contentH <= viewportH)
-                || (canEnterDesktopMode(mAtmService.mContext)
-                    && getWindowingMode() == WINDOWING_MODE_FREEFORM);
-        return shouldAllowUpscaling ? Math.min(
-                (float) viewportW / contentW, (float) viewportH / contentH) : 1f;
-    }
-
-    private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
-        if (mAppCompatController.getTransparentPolicy().isRunning()) {
-            // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
-            // is letterboxed.
-            return false;
-        }
-        final int appWidth = appBounds.width();
-        final int appHeight = appBounds.height();
-        final int containerAppWidth = containerBounds.width();
-        final int containerAppHeight = containerBounds.height();
-
-        if (containerAppWidth == appWidth && containerAppHeight == appHeight) {
-            // Matched the container bounds.
-            return false;
-        }
-        if (containerAppWidth > appWidth && containerAppHeight > appHeight) {
-            // Both sides are smaller than the container.
-            return true;
-        }
-        if (containerAppWidth < appWidth || containerAppHeight < appHeight) {
-            // One side is larger than the container.
-            return true;
-        }
-
-        // The rest of the condition is that only one side is smaller than the container, but it
-        // still needs to exclude the cases where the size is limited by the fixed aspect ratio.
-        final float maxAspectRatio = getMaxAspectRatio();
-        if (maxAspectRatio > 0) {
-            final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
-                    / Math.min(appWidth, appHeight);
-            if (aspectRatio >= maxAspectRatio) {
-                // The current size has reached the max aspect ratio.
-                return false;
-            }
-        }
-        final float minAspectRatio = getMinAspectRatio();
-        if (minAspectRatio > 0) {
-            // The activity should have at least the min aspect ratio, so this checks if the
-            // container still has available space to provide larger aspect ratio.
-            final float containerAspectRatio =
-                    (0.5f + Math.max(containerAppWidth, containerAppHeight))
-                            / Math.min(containerAppWidth, containerAppHeight);
-            if (containerAspectRatio <= minAspectRatio) {
-                // The long side has reached the parent.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** @return The horizontal / vertical offset of putting the content in the center of viewport.*/
-    private static int getCenterOffset(int viewportDim, int contentDim) {
-        return (int) ((viewportDim - contentDim + 1) * 0.5f);
-    }
-
-    private static void offsetBounds(Configuration inOutConfig, int offsetX, int offsetY) {
-        inOutConfig.windowConfiguration.getBounds().offset(offsetX, offsetY);
-        inOutConfig.windowConfiguration.getAppBounds().offset(offsetX, offsetY);
-    }
-
     @Override
     public Rect getBounds() {
         // TODO(b/268458693): Refactor configuration inheritance in case of translucent activities
         final Rect superBounds = super.getBounds();
+        final AppCompatSizeCompatModePolicy scmPolicy =
+                mAppCompatController.getAppCompatSizeCompatModePolicy();
         return mAppCompatController.getTransparentPolicy().findOpaqueNotFinishingActivityBelow()
                 .map(ActivityRecord::getBounds)
-                .orElseGet(() -> {
-                    if (mSizeCompatBounds != null) {
-                        return mSizeCompatBounds;
-                    }
-                    return superBounds;
-                });
+                .orElseGet(() -> scmPolicy.getAppSizeCompatBoundsIfAvailable(superBounds));
     }
 
     @Override
@@ -9290,13 +8988,13 @@
         if (info.alwaysSandboxDisplayApis(sConstrainDisplayApisConfig)) {
             return true;
         }
-        // Max bounds should be sandboxed when an activity should have compatDisplayInsets, and it
-        // will keep the same bounds and screen configuration when it was first launched regardless
-        // how its parent window changes, so that the sandbox API will provide a consistent result.
-        if (getCompatDisplayInsets() != null || shouldCreateCompatDisplayInsets()) {
+        // Max bounds should be sandboxed when an activity should have mAppCompatDisplayInsets,
+        // and it will keep the same bounds and screen configuration when it was first launched
+        // regardless how its parent window changes, so that the sandbox API will provide a
+        // consistent result.
+        if (getAppCompatDisplayInsets() != null || shouldCreateAppCompatDisplayInsets()) {
             return true;
         }
-
         // No need to sandbox for resizable apps in (including in multi-window) because
         // resizableActivity=true indicates that they support multi-window. Likewise, do not sandbox
         // for activities in letterbox since the activity has declared it can handle resizing.
@@ -9347,7 +9045,7 @@
                 mTransitionController.collect(this);
             }
         }
-        if (getCompatDisplayInsets() != null) {
+        if (getAppCompatDisplayInsets() != null) {
             Configuration overrideConfig = getRequestedOverrideConfiguration();
             // Adapt to changes in orientation locking. The app is still non-resizable, but
             // it can change which orientation is fixed. If the fixed orientation changes,
@@ -9427,9 +9125,9 @@
         if (mVisibleRequested) {
             // It may toggle the UI for user to restart the size compatibility mode activity.
             display.handleActivitySizeCompatModeIfNeeded(this);
-        } else if (getCompatDisplayInsets() != null && !visibleIgnoringKeyguard
+        } else if (getAppCompatDisplayInsets() != null && !visibleIgnoringKeyguard
                 && (app == null || !app.hasVisibleActivities())) {
-            // visibleIgnoringKeyguard is checked to avoid clearing mCompatDisplayInsets during
+            // visibleIgnoringKeyguard is checked to avoid clearing mAppCompatDisplayInsets during
             // displays change. Displays are turned off during the change so mVisibleRequested
             // can be false.
             // The override changes can only be obtained from display, because we don't have the
@@ -9592,14 +9290,14 @@
 
         // Calling from here rather than from onConfigurationChanged because it's possible that
         // onConfigurationChanged was called before mVisibleRequested became true and
-        // mCompatDisplayInsets may not be called again when mVisibleRequested changes. And we
-        // don't want to save mCompatDisplayInsets in onConfigurationChanged without visibility
+        // mAppCompatDisplayInsets may not be called again when mVisibleRequested changes. And we
+        // don't want to save mAppCompatDisplayInsets in onConfigurationChanged without visibility
         // check to avoid remembering obsolete configuration which can lead to unnecessary
         // size-compat mode.
         if (mVisibleRequested) {
             // Calling from here rather than resolveOverrideConfiguration to ensure that this is
             // called after full config is updated in ConfigurationContainer#onConfigurationChanged.
-            updateCompatDisplayInsets();
+            mAppCompatController.getAppCompatSizeCompatModePolicy().updateAppCompatDisplayInsets();
         }
 
         // Short circuit: if the two full configurations are equal (the common case), then there is
@@ -9939,7 +9637,7 @@
 
         // Reset the existing override configuration so it can be updated according to the latest
         // configuration.
-        clearSizeCompatMode();
+        mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         if (!attachedToProcess()) {
             return;
@@ -10323,7 +10021,7 @@
      * Write all fields to an {@code ActivityRecordProto}. This assumes the
      * {@code ActivityRecordProto} is the outer-most proto data.
      */
-    void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+    void dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
         writeNameToProto(proto, NAME);
         super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
         proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
@@ -10401,9 +10099,9 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
+            @WindowTracingLogLevel int logLevel) {
         // Critical log level logs only visible elements to mitigate performance overheard
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
@@ -10425,202 +10123,6 @@
         proto.end(token);
     }
 
-    /**
-     * The precomputed insets of the display in each rotation. This is used to make the size
-     * compatibility mode activity compute the configuration without relying on its current display.
-     */
-    static class CompatDisplayInsets {
-        /** The original rotation the compat insets were computed in. */
-        final @Rotation int mOriginalRotation;
-        /** The original requested orientation for the activity. */
-        final @Configuration.Orientation int mOriginalRequestedOrientation;
-        /** The container width on rotation 0. */
-        private final int mWidth;
-        /** The container height on rotation 0. */
-        private final int mHeight;
-        /** Whether the {@link Task} windowingMode represents a floating window*/
-        final boolean mIsFloating;
-        /**
-         * Whether is letterboxed because of fixed orientation or aspect ratio when
-         * the unresizable activity is first shown.
-         */
-        final boolean mIsInFixedOrientationOrAspectRatioLetterbox;
-        /**
-         * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
-         * is used to compute the appBounds.
-         */
-        final Rect[] mNonDecorInsets = new Rect[4];
-        /**
-         * The stableInsets for each rotation. Includes the status bar inset and the
-         * nonDecorInsets. It is used to compute {@link Configuration#screenWidthDp} and
-         * {@link Configuration#screenHeightDp}.
-         */
-        final Rect[] mStableInsets = new Rect[4];
-
-        /** Constructs the environment to simulate the bounds behavior of the given container. */
-        CompatDisplayInsets(DisplayContent display, ActivityRecord container,
-                @Nullable Rect letterboxedContainerBounds, boolean useOverrideInsets) {
-            mOriginalRotation = display.getRotation();
-            mIsFloating = container.getWindowConfiguration().tasksAreFloating();
-            mOriginalRequestedOrientation = container.getRequestedConfigurationOrientation();
-            if (mIsFloating) {
-                final Rect containerBounds = container.getWindowConfiguration().getBounds();
-                mWidth = containerBounds.width();
-                mHeight = containerBounds.height();
-                // For apps in freeform, the task bounds are the parent bounds from the app's
-                // perspective. No insets because within a window.
-                final Rect emptyRect = new Rect();
-                for (int rotation = 0; rotation < 4; rotation++) {
-                    mNonDecorInsets[rotation] = emptyRect;
-                    mStableInsets[rotation] = emptyRect;
-                }
-                mIsInFixedOrientationOrAspectRatioLetterbox = false;
-                return;
-            }
-
-            final Task task = container.getTask();
-
-            mIsInFixedOrientationOrAspectRatioLetterbox = letterboxedContainerBounds != null;
-
-            // Store the bounds of the Task for the non-resizable activity to use in size compat
-            // mode so that the activity will not be resized regardless the windowing mode it is
-            // currently in.
-            // When an activity needs to be letterboxed because of fixed orientation or aspect
-            // ratio, use resolved bounds instead of task bounds since the activity will be
-            // displayed within these even if it is in size compat mode.
-            final Rect filledContainerBounds = mIsInFixedOrientationOrAspectRatioLetterbox
-                    ? letterboxedContainerBounds
-                    : task != null ? task.getBounds() : display.getBounds();
-            final boolean useActivityRotation = container.hasFixedRotationTransform()
-                    && mIsInFixedOrientationOrAspectRatioLetterbox;
-            final int filledContainerRotation = useActivityRotation
-                    ? container.getWindowConfiguration().getRotation()
-                    : display.getConfiguration().windowConfiguration.getRotation();
-            final Point dimensions = getRotationZeroDimensions(
-                    filledContainerBounds, filledContainerRotation);
-            mWidth = dimensions.x;
-            mHeight = dimensions.y;
-
-            // Bounds of the filled container if it doesn't fill the display.
-            final Rect unfilledContainerBounds =
-                    filledContainerBounds.equals(display.getBounds()) ? null : new Rect();
-            final DisplayPolicy policy = display.getDisplayPolicy();
-            for (int rotation = 0; rotation < 4; rotation++) {
-                mNonDecorInsets[rotation] = new Rect();
-                mStableInsets[rotation] = new Rect();
-                final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-                final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
-                final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
-                final DisplayPolicy.DecorInsets.Info decorInfo =
-                        policy.getDecorInsetsInfo(rotation, dw, dh);
-                if (useOverrideInsets) {
-                    mStableInsets[rotation].set(decorInfo.mOverrideConfigInsets);
-                    mNonDecorInsets[rotation].set(decorInfo.mOverrideNonDecorInsets);
-                } else {
-                    mStableInsets[rotation].set(decorInfo.mConfigInsets);
-                    mNonDecorInsets[rotation].set(decorInfo.mNonDecorInsets);
-                }
-
-                if (unfilledContainerBounds == null) {
-                    continue;
-                }
-                // The insets is based on the display, but the container may be smaller than the
-                // display, so update the insets to exclude parts that are not intersected with the
-                // container.
-                unfilledContainerBounds.set(filledContainerBounds);
-                display.rotateBounds(
-                        filledContainerRotation,
-                        rotation,
-                        unfilledContainerBounds);
-                updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
-                updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]);
-            }
-        }
-
-        /**
-         * Gets the width and height of the {@code container} when it is not rotated, so that after
-         * the display is rotated, we can calculate the bounds by rotating the dimensions.
-         * @see #getBoundsByRotation
-         */
-        private static Point getRotationZeroDimensions(final Rect bounds, int rotation) {
-            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-            final int width = bounds.width();
-            final int height = bounds.height();
-            return rotated ? new Point(height, width) : new Point(width, height);
-        }
-
-        /**
-         * Updates the display insets to exclude the parts that are not intersected with the given
-         * bounds.
-         */
-        private static void updateInsetsForBounds(Rect bounds, int displayWidth, int displayHeight,
-                Rect inset) {
-            inset.left = Math.max(0, inset.left - bounds.left);
-            inset.top = Math.max(0, inset.top - bounds.top);
-            inset.right = Math.max(0, bounds.right - displayWidth + inset.right);
-            inset.bottom = Math.max(0, bounds.bottom - displayHeight + inset.bottom);
-        }
-
-        void getBoundsByRotation(Rect outBounds, int rotation) {
-            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-            final int dw = rotated ? mHeight : mWidth;
-            final int dh = rotated ? mWidth : mHeight;
-            outBounds.set(0, 0, dw, dh);
-        }
-
-        void getFrameByOrientation(Rect outBounds, int orientation) {
-            final int longSide = Math.max(mWidth, mHeight);
-            final int shortSide = Math.min(mWidth, mHeight);
-            final boolean isLandscape = orientation == ORIENTATION_LANDSCAPE;
-            outBounds.set(0, 0, isLandscape ? longSide : shortSide,
-                    isLandscape ? shortSide : longSide);
-        }
-
-        // TODO(b/267151420): Explore removing getContainerBounds() from CompatDisplayInsets.
-        /** Gets the horizontal centered container bounds for size compatibility mode. */
-        void getContainerBounds(Rect outAppBounds, Rect outBounds, int rotation, int orientation,
-                boolean orientationRequested, boolean isFixedToUserRotation) {
-            getFrameByOrientation(outBounds, orientation);
-            if (mIsFloating) {
-                outAppBounds.set(outBounds);
-                return;
-            }
-
-            getBoundsByRotation(outAppBounds, rotation);
-            final int dW = outAppBounds.width();
-            final int dH = outAppBounds.height();
-            final boolean isOrientationMismatched =
-                    ((outBounds.width() > outBounds.height()) != (dW > dH));
-
-            if (isOrientationMismatched && isFixedToUserRotation && orientationRequested) {
-                // The orientation is mismatched but the display cannot rotate. The bounds will fit
-                // to the short side of container.
-                if (orientation == ORIENTATION_LANDSCAPE) {
-                    outBounds.bottom = (int) ((float) dW * dW / dH);
-                    outBounds.right = dW;
-                } else {
-                    outBounds.bottom = dH;
-                    outBounds.right = (int) ((float) dH * dH / dW);
-                }
-                outBounds.offset(getCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
-            }
-            outAppBounds.set(outBounds);
-
-            if (isOrientationMismatched) {
-                // One side of container is smaller than the requested size, then it will be scaled
-                // and the final position will be calculated according to the parent container and
-                // scale, so the original size shouldn't be shrunk by insets.
-                final Rect insets = mNonDecorInsets[rotation];
-                outBounds.offset(insets.left, insets.top);
-                outAppBounds.offset(insets.left, insets.top);
-            } else if (rotation != ROTATION_UNDEFINED) {
-                // Ensure the app bounds won't overlap with insets.
-                TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
-                        mNonDecorInsets[rotation]);
-            }
-        }
-    }
-
     private static class AppSaturationInfo {
         float[] mMatrix = new float[9];
         float[] mTranslation = new float[3];
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a0ef030..35ec5ad 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -424,13 +424,19 @@
                 Intent intent = intents[i];
                 NeededUriGrants intentGrants = null;
 
-                intent.prepareToEnterSystemServer();
+                // Refuse possible leaked file descriptors.
+                if (intent.hasFileDescriptors()) {
+                    throw new IllegalArgumentException("File descriptors passed in Intent");
+                }
 
                 // Get the flag earlier because the intent may be modified in resolveActivity below.
                 final boolean componentSpecified = intent.getComponent() != null;
                 // Don't modify the client's object!
                 intent = new Intent(intent);
 
+                // Remove existing mismatch flag so it can be properly updated later
+                intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
+
                 // Collect information about the target of the Intent.
                 ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
                         0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
@@ -612,11 +618,10 @@
         final Task task = r.getTask();
         mService.deferWindowLayout();
         try {
-            final TransitionController controller = r.mTransitionController;
-            final Transition transition = controller.getCollectingTransition();
+            final Transition transition = r.mTransitionController.getCollectingTransition();
             if (transition != null) {
                 transition.setRemoteAnimationApp(r.app.getThread());
-                controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
+                transition.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
             }
             task.moveToFront("startExistingRecents");
             task.mInResumeTopActivity = true;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 18aa9a0..1822a80 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -721,7 +721,13 @@
             onExecutionStarted();
 
             if (mRequest.intent != null) {
-                mRequest.intent.prepareToEnterSystemServer();
+                // Refuse possible leaked file descriptors
+                if (mRequest.intent.hasFileDescriptors()) {
+                    throw new IllegalArgumentException("File descriptors passed in Intent");
+                }
+
+                // Remove existing mismatch flag so it can be properly updated later
+                mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
             }
 
             final LaunchingState launchingState;
@@ -1355,9 +1361,21 @@
             mService.resumeAppSwitches();
         }
 
+        // Only do the create here since startActivityInner can abort. If it doesn't abort,
+        // the requestStart will be sent in handleStartRequest.
+        final Transition newTransition = r.mTransitionController.isShellTransitionsEnabled()
+                ? r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
+        // Because startActivity must run immediately, it can get combined with another
+        // transition meaning it is no-longer independent. This is NOT desirable, but is the
+        // only option for the time being.
+        final boolean isIndependent = newTransition != null;
+        final Transition transition = isIndependent ? newTransition
+                : mService.getTransitionController().getCollectingTransition();
+
         mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                 request.voiceInteractor, startFlags, checkedOptions,
-                inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid);
+                inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid, transition,
+                isIndependent);
 
         if (request.outActivity != null) {
             request.outActivity[0] = mLastStartActivityRecord;
@@ -1509,33 +1527,27 @@
             int startFlags, ActivityOptions options, Task inTask,
             TaskFragment inTaskFragment,
             BalVerdict balVerdict,
-            NeededUriGrants intentGrants, int realCallingUid) {
+            NeededUriGrants intentGrants, int realCallingUid, Transition transition,
+            boolean isIndependentLaunch) {
         int result = START_CANCELED;
         final Task startedActivityRootTask;
 
-        // Create a transition now to record the original intent of actions taken within
-        // startActivityInner. Otherwise, logic in startActivityInner could start a different
-        // transition based on a sub-action.
-        // Only do the create here (and defer requestStart) since startActivityInner might abort.
-        final TransitionController transitionController = r.mTransitionController;
-        Transition newTransition = transitionController.isShellTransitionsEnabled()
-                ? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
         RemoteTransition remoteTransition = r.takeRemoteTransition();
         // Create a display snapshot as soon as possible.
-        if (newTransition != null && mRequest.freezeScreen) {
+        if (isIndependentLaunch && mRequest.freezeScreen) {
             final TaskDisplayArea tda = mLaunchParams.hasPreferredTaskDisplayArea()
                     ? mLaunchParams.mPreferredTaskDisplayArea
                     : mRootWindowContainer.getDefaultTaskDisplayArea();
             final DisplayContent dc = mRootWindowContainer.getDisplayContentOrCreate(
                     tda.getDisplayId());
             if (dc != null) {
-                transitionController.collect(dc);
-                transitionController.collectVisibleChange(dc);
+                transition.collect(dc);
+                transition.collectVisibleChange(dc);
             }
         }
         try {
             mService.deferWindowLayout();
-            transitionController.collect(r);
+            r.mTransitionController.collect(r);
             try {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
                 result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
@@ -1545,8 +1557,8 @@
                 Slog.e(TAG, "Exception on startActivityInner", ex);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-                startedActivityRootTask = handleStartResult(r, options, result, newTransition,
-                        remoteTransition);
+                startedActivityRootTask = handleStartResult(r, options, result, isIndependentLaunch,
+                        remoteTransition, transition);
             }
         } finally {
             mService.continueWindowLayout();
@@ -1571,8 +1583,8 @@
      * @return the root task where the successful started activity resides.
      */
     private @Nullable Task handleStartResult(@NonNull ActivityRecord started,
-            ActivityOptions options, int result, Transition newTransition,
-            RemoteTransition remoteTransition) {
+            ActivityOptions options, int result, boolean isIndependentLaunch,
+            RemoteTransition remoteTransition, @NonNull Transition transition) {
         final boolean userLeaving = mSupervisor.mUserLeaving;
         mSupervisor.mUserLeaving = false;
         final Task currentRootTask = started.getRootTask();
@@ -1596,8 +1608,9 @@
                     && !startedActivityRootTask.mCreatedByOrganizer) {
                 startedActivityRootTask.removeIfPossible("handleStartResult");
             }
-            if (newTransition != null) {
-                newTransition.abort();
+            if (isIndependentLaunch
+                    && mService.getTransitionController().isShellTransitionsEnabled()) {
+                transition.abort();
             }
             return null;
         }
@@ -1649,44 +1662,46 @@
             // The activity is started new rather than just brought forward, so record it as an
             // existence change.
             transitionController.collectExistenceChange(started);
-        } else if (result == START_DELIVERED_TO_TOP && newTransition != null
+        } else if (result == START_DELIVERED_TO_TOP && isIndependentLaunch
                 // An activity has changed order/visibility or the task is occluded by a transient
                 // activity, so this isn't just deliver-to-top
                 && mMovedToTopActivity == null
                 && !transitionController.hasOrderChanges()
                 && !transitionController.isTransientHide(startedActivityRootTask)
-                && !newTransition.hasChanged(mLastStartActivityRecord)) {
+                && !transition.hasChanged(mLastStartActivityRecord)) {
             // We just delivered to top, so there isn't an actual transition here.
             if (!forceTransientTransition) {
-                newTransition.abort();
-                newTransition = null;
+                transition.abort();
+                transition = null;
             }
         }
-        if (forceTransientTransition) {
-            transitionController.collect(mLastStartActivityRecord);
-            transitionController.collect(mPriorAboveTask);
+        if (forceTransientTransition && transition != null) {
+            transition.collect(mLastStartActivityRecord);
+            transition.collect(mPriorAboveTask);
             // If keyguard is active and occluded, the transient target won't be moved to front
             // to be collected, so set transient again after it is collected.
-            transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
+            transition.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
             final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
             // update wallpaper target to TransientHide
             dc.mWallpaperController.adjustWallpaperWindows();
             // execute transition because there is no change
-            transitionController.setReady(dc, true /* ready */);
+            transition.setReady(dc, true /* ready */);
         }
-        if (!userLeaving) {
+        if (!userLeaving && transition != null) {
             // no-user-leaving implies not entering PiP.
-            transitionController.setCanPipOnFinish(false /* canPipOnFinish */);
+            transition.setCanPipOnFinish(false /* canPipOnFinish */);
         }
-        if (newTransition != null) {
-            transitionController.requestStartTransition(newTransition,
+        if (isIndependentLaunch && transition != null) {
+            transitionController.requestStartTransition(transition,
                     mTargetTask == null ? started.getTask() : mTargetTask,
                     remoteTransition, null /* displayChange */);
         } else if (result == START_SUCCESS && mStartActivity.isState(RESUMED)) {
             // Do nothing if the activity is started and is resumed directly.
         } else if (isStarted) {
             // Make the collecting transition wait until this request is ready.
-            transitionController.setReady(started, false);
+            if (transition != null) {
+                transition.setReady(started, false);
+            }
         }
         return startedActivityRootTask;
     }
@@ -1936,7 +1951,7 @@
                 && sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
                 && balVerdict.allows()) {
             mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
-                    sourceRecord, "launch-into-pip");
+                    sourceRecord, "launch-into-pip", null /* bounds */);
         }
 
         mSupervisor.getBackgroundActivityLaunchController()
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 26a6b00..b8ce02ed 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -380,7 +380,16 @@
     public abstract void onPackageAdded(String name, boolean replacing);
     public abstract void onPackageReplaced(ApplicationInfo aInfo);
 
-    public abstract CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai);
+    /** The data for IApplicationThread#bindApplication. */
+    public static final class PreBindInfo {
+        public final @NonNull CompatibilityInfo compatibilityInfo;
+        public final @NonNull Configuration configuration;
+
+        PreBindInfo(@NonNull CompatibilityInfo compatInfo, @NonNull Configuration config) {
+            compatibilityInfo = compatInfo;
+            configuration = config;
+        }
+    }
 
     public final class ActivityTokens {
         private final @NonNull IBinder mActivityToken;
@@ -502,7 +511,9 @@
     public abstract void resumeTopActivities(boolean scheduleIdle);
 
     /** Called by AM just before it binds to an application process. */
-    public abstract void preBindApplication(WindowProcessController wpc);
+    @NonNull
+    public abstract PreBindInfo preBindApplication(@NonNull WindowProcessController wpc,
+            @NonNull ApplicationInfo info);
 
     /** Called by AM when an application process attaches. */
     public abstract boolean attachApplication(WindowProcessController wpc) throws RemoteException;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e4cae58..e25d940d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -124,8 +124,6 @@
 import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_DONT_LOCK;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT;
@@ -241,7 +239,6 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
-import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
@@ -441,13 +438,10 @@
 
     /** It is set from keyguard-going-away to set-keyguard-shown. */
     static final int DEMOTE_TOP_REASON_DURING_UNLOCKING = 1;
-    /** It is set if legacy recents animation is running. */
-    static final int DEMOTE_TOP_REASON_ANIMATING_RECENTS = 1 << 1;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
             DEMOTE_TOP_REASON_DURING_UNLOCKING,
-            DEMOTE_TOP_REASON_ANIMATING_RECENTS,
     })
     @interface DemoteTopReason {}
 
@@ -801,6 +795,7 @@
     WindowOrganizerController mWindowOrganizerController;
     TaskOrganizerController mTaskOrganizerController;
     TaskFragmentOrganizerController mTaskFragmentOrganizerController;
+    ActionChain.Tracker mChainTracker;
 
     @Nullable
     private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
@@ -875,6 +870,7 @@
         mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
         mWindowOrganizerController = new WindowOrganizerController(this);
+        mChainTracker = new ActionChain.Tracker(this);
         mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
         mTaskFragmentOrganizerController =
                 mWindowOrganizerController.mTaskFragmentOrganizerController;
@@ -1321,7 +1317,12 @@
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) {
         enforceNotIsolatedCaller("startActivityIntentSender");
         if (fillInIntent != null) {
-            fillInIntent.prepareToEnterSystemServer();
+            // Refuse possible leaked file descriptors
+            if (fillInIntent.hasFileDescriptors()) {
+                throw new IllegalArgumentException("File descriptors passed in Intent");
+            }
+            // Remove existing mismatch flag so it can be properly updated later
+            fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
         }
 
         if (!(target instanceof PendingIntentRecord)) {
@@ -1347,10 +1348,10 @@
     @Override
     public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
             Bundle bOptions) {
-        if (intent != null) {
-            intent.prepareToEnterSystemServer();
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
         }
-
         SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
 
         synchronized (mGlobalLock) {
@@ -1365,6 +1366,8 @@
                 return false;
             }
             intent = new Intent(intent);
+            // Remove existing mismatch flag so it can be properly updated later
+            intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH);
             // The caller is not allowed to change the data.
             intent.setDataAndType(r.intent.getData(), r.intent.getType());
             // And we are resetting to find the next component...
@@ -1773,35 +1776,23 @@
     }
 
     /**
-     * Start the recents activity to perform the recents animation.
+     * Preload the recents activity.
      *
-     * @param intent                 The intent to start the recents activity.
-     * @param eventTime              When the (touch) event is triggered to start recents activity.
-     * @param recentsAnimationRunner Pass {@code null} to only preload the activity.
+     * @param intent The intent to preload the recents activity.
      */
     @Override
-    public void startRecentsActivity(Intent intent, long eventTime,
-            @Nullable IRecentsAnimationRunner recentsAnimationRunner) {
-        enforceTaskPermission("startRecentsActivity()");
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
+    public void preloadRecentsActivity(Intent intent) {
+        enforceTaskPermission("preloadRecentsActivity()");
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
                 final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
                 final int recentsUid = mRecentTasks.getRecentsComponentUid();
-                final WindowProcessController caller = getProcessController(callingPid, callingUid);
-
-                // Start a new recents animation
                 final RecentsAnimation anim = new RecentsAnimation(this, mTaskSupervisor,
-                        getActivityStartController(), mWindowManager, intent, recentsComponent,
-                        recentsFeatureId, recentsUid, caller);
-                if (recentsAnimationRunner == null) {
-                    anim.preloadRecentsActivity();
-                } else {
-                    anim.startRecentsActivity(recentsAnimationRunner, eventTime);
-                }
+                        getActivityStartController(), intent, recentsComponent,
+                        recentsFeatureId, recentsUid);
+                anim.preloadRecentsActivity();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2116,12 +2107,7 @@
                     Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
                     return false;
                 }
-
-                if (task.isLeafTask()) {
-                    mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
-                } else {
-                    mTaskSupervisor.removeRootTask(task);
-                }
+                removeTask(task);
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2129,6 +2115,14 @@
         }
     }
 
+    void removeTask(@NonNull Task task) {
+        if (task.isLeafTask()) {
+            mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
+        } else {
+            mTaskSupervisor.removeRootTask(task);
+        }
+    }
+
     @Override
     public void removeAllVisibleRecentTasks() {
         mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
@@ -2563,23 +2557,6 @@
     }
 
     @Override
-    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
-        enforceTaskPermission("cancelRecentsAnimation()");
-        final long callingUid = Binder.getCallingUid();
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimation(restoreHomeRootTaskPosition
-                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
-                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
     public void startSystemLockTaskMode(int taskId) {
         enforceTaskPermission("startSystemLockTaskMode");
         // This makes inner call to look as if it was initiated by system.
@@ -2938,7 +2915,7 @@
                             }
                             getTransitionController().requestStartTransition(transition, task,
                                     null /* remoteTransition */, null /* displayChange */);
-                            getTransitionController().collect(task);
+                            transition.collect(task);
                             task.resize(bounds, resizeMode, preserveWindow);
                             transition.setReady(task, true);
                         });
@@ -3144,9 +3121,23 @@
                 null, PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
     }
 
+    /**
+     * Requests assist data for a particular Task.
+     *
+     * <p>This is used by the system components to request assist data for a Task.
+     *
+     * @param receiver The receiver to send the assist data to.
+     * @param taskId The Task to request assist data for.
+     * @param callingPackageName The package name of the caller.
+     * @param callingAttributionTag The attribution tag of the caller.
+     * @param fetchStructure Whether to fetch the assist structure. Note that this is slow and
+     *     should be avoided if possible.
+     * @return Whether the request was successful.
+     */
     @Override
     public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
-            String callingPackageName, @Nullable String callingAttributionTag) {
+            String callingPackageName, @Nullable String callingAttributionTag,
+            boolean fetchStructure) {
         mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "requestAssistDataForTask()");
         final long callingId = Binder.clearCallingIdentity();
@@ -3171,7 +3162,7 @@
         List<IBinder> topActivityToken = new ArrayList<>();
         topActivityToken.add(tokens.getActivityToken());
         requester.requestAssistData(topActivityToken, true /* fetchData */,
-                false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
+                false /* fetchScreenshot */, fetchStructure, true /* allowFetchData */,
                 false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
                 Binder.getCallingUid(), callingPackageName, callingAttributionTag);
 
@@ -3635,6 +3626,11 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                boolean isPowerModePreApplied = false;
+                if (mPowerModeReasons == 0) {
+                    startPowerMode(POWER_MODE_REASON_START_ACTIVITY);
+                    isPowerModePreApplied = true;
+                }
                 // Keyguard asked us to clear the home task snapshot before going away, so do that.
                 if ((flags & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) {
                     mActivityClientController.invalidateHomeTaskSnapshot(null /* token */);
@@ -3643,9 +3639,19 @@
                     mDemoteTopAppReasons |= DEMOTE_TOP_REASON_DURING_UNLOCKING;
                 }
 
-                mRootWindowContainer.forAllDisplays(displayContent -> {
-                    mKeyguardController.keyguardGoingAway(displayContent.getDisplayId(), flags);
-                });
+                boolean foundResumed = false;
+                for (int i = mRootWindowContainer.getChildCount() - 1; i >= 0; i--) {
+                    final DisplayContent dc = mRootWindowContainer.getChildAt(i);
+                    final boolean wasNoResumed = dc.mFocusedApp == null
+                            || !dc.mFocusedApp.isState(RESUMED);
+                    mKeyguardController.keyguardGoingAway(dc.mDisplayId, flags);
+                    if (wasNoResumed && dc.mFocusedApp != null && dc.mFocusedApp.isState(RESUMED)) {
+                        foundResumed = true;
+                    }
+                }
+                if (isPowerModePreApplied && !foundResumed) {
+                    endPowerMode(POWER_MODE_REASON_START_ACTIVITY);
+                }
             }
             WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
             if (wallpaperManagerInternal != null) {
@@ -3754,6 +3760,7 @@
             // Shell calls back into Core with the entry bounds to be applied with startWCT.
             final Transition enterPipTransition = new Transition(TRANSIT_PIP,
                     0 /* flags */, getTransitionController(), mWindowManager.mSyncEngine);
+            r.setPictureInPictureParams(params);
             enterPipTransition.setPipActivity(r);
             r.mAutoEnteringPip = isAutoEnter;
             getTransitionController().startCollectOrQueue(enterPipTransition, (deferred) -> {
@@ -3796,9 +3803,22 @@
                         r.shortComponentName, Boolean.toString(isAutoEnter));
                 r.setPictureInPictureParams(params);
                 r.mAutoEnteringPip = isAutoEnter;
-                mRootWindowContainer.moveActivityToPinnedRootTask(r,
-                        null /* launchIntoPipHostActivity */, "enterPictureInPictureMode",
-                        transition);
+
+                if (transition != null) {
+                    mRootWindowContainer.moveActivityToPinnedRootTaskAndRequestStart(r,
+                            "enterPictureInPictureMode");
+                } else if (getTransitionController().isCollecting()
+                        || !getTransitionController().isShellTransitionsEnabled()) {
+                    mRootWindowContainer.moveActivityToPinnedRootTask(r,
+                            null /* launchIntoPipHostActivity */, "enterPictureInPictureMode",
+                            null /* bounds */);
+                } else {
+                    // Need to make a transition just for this. This shouldn't really happen
+                    // though because if transition == null, we should be part of an existing one.
+                    getTransitionController().createTransition(TRANSIT_PIP);
+                    mRootWindowContainer.moveActivityToPinnedRootTaskAndRequestStart(r,
+                            "enterPictureInPictureMode");
+                }
                 // Continue the pausing process after entering pip.
                 if (r.isState(PAUSING) && r.mPauseSchedulePendingForPip) {
                     r.getTask().schedulePauseActivity(r, false /* userLeaving */,
@@ -4608,7 +4628,12 @@
         return kept;
     }
 
-    /** Update default (global) configuration and notify listeners about changes. */
+    /**
+     * Updates default (global) configuration and notifies listeners about changes.
+     *
+     * @param values The new configuration. It must always be a new instance from the caller, and
+     *               it won't be modified after calling this method.
+     */
     int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
             boolean persistent, int userId) {
 
@@ -4622,24 +4647,6 @@
         ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
                 + "to: %s", values);
         writeConfigurationChanged(changes);
-        FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
-                values.colorMode,
-                values.densityDpi,
-                values.fontScale,
-                values.hardKeyboardHidden,
-                values.keyboard,
-                values.keyboardHidden,
-                values.mcc,
-                values.mnc,
-                values.navigation,
-                values.navigationHidden,
-                values.orientation,
-                values.screenHeightDp,
-                values.screenLayout,
-                values.screenWidthDp,
-                values.smallestScreenWidthDp,
-                values.touchscreen,
-                values.uiMode);
 
         // Note: certain tests currently run as platform_app which is not allowed
         // to set debug system properties. To ensure that system properties are set
@@ -4687,13 +4694,6 @@
         // resources have that config before following boot code is executed.
         mSystemThread.applyConfigurationToResources(mTempConfig);
 
-        if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-            final Message msg = PooledLambda.obtainMessage(
-                    ActivityTaskManagerService::sendPutConfigurationForUserMsg,
-                    this, userId, new Configuration(mTempConfig));
-            mH.sendMessage(msg);
-        }
-
         SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
         for (int i = pidMap.size() - 1; i >= 0; i--) {
             final int pid = pidMap.keyAt(i);
@@ -4703,19 +4703,32 @@
             app.onConfigurationChanged(mTempConfig);
         }
 
-        final Message msg = PooledLambda.obtainMessage(
-                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
-                mAmInternal, changes, initLocale);
-        mH.sendMessage(msg);
+        final Configuration configurationForSettings =
+                persistent && Settings.System.hasInterestingConfigurationChanges(changes)
+                        ? new Configuration(mTempConfig) : null;
+        mH.post(() -> {
+            FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
+                    values.colorMode, values.densityDpi, values.fontScale,
+                    values.hardKeyboardHidden, values.keyboard, values.keyboardHidden,
+                    values.mcc, values.mnc, values.navigation, values.navigationHidden,
+                    values.orientation, values.screenHeightDp, values.screenLayout,
+                    values.screenWidthDp, values.smallestScreenWidthDp, values.touchscreen,
+                    values.uiMode);
+            if ((changes & ActivityInfo.CONFIG_ORIENTATION) != 0) {
+                FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_ORIENTATION_CHANGED,
+                        values.orientation);
+            }
+            if (configurationForSettings != null) {
+                Settings.System.putConfigurationForUser(mContext.getContentResolver(),
+                        configurationForSettings, userId);
+            }
+            mAmInternal.broadcastGlobalConfigurationChanged(changes, initLocale);
+        });
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
         // Update stored global config and notify everyone about the change.
         mRootWindowContainer.onConfigurationChanged(mTempConfig);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        if ((changes & ActivityInfo.CONFIG_ORIENTATION) != 0) {
-            FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_ORIENTATION_CHANGED,
-                    values.orientation);
-        }
 
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         return changes;
@@ -4764,6 +4777,10 @@
         }
     }
 
+    boolean mayBeLaunchingApp() {
+        return (mPowerModeReasons & POWER_MODE_REASON_START_ACTIVITY) != 0;
+    }
+
     void startPowerMode(@PowerModeReason int reason) {
         final int prevReasons = mPowerModeReasons;
         mPowerModeReasons |= reason;
@@ -4865,11 +4882,6 @@
         mWindowManager.setEventDispatching(booted && !mShuttingDown);
     }
 
-    private void sendPutConfigurationForUserMsg(int userId, Configuration config) {
-        final ContentResolver resolver = mContext.getContentResolver();
-        Settings.System.putConfigurationForUser(resolver, config, userId);
-    }
-
     boolean isActivityStartsLoggingEnabled() {
         return mAmInternal.isActivityStartsLoggingEnabled();
     }
@@ -6416,13 +6428,6 @@
         }
 
         @Override
-        public CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
-            synchronized (mGlobalLock) {
-                return compatibilityInfoForPackageLocked(ai);
-            }
-        }
-
-        @Override
         public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
                 int requestCode, int resultCode, Intent data) {
             final ActivityRecord r;
@@ -6700,9 +6705,13 @@
 
         @HotPath(caller = HotPath.PROCESS_CHANGE)
         @Override
-        public void preBindApplication(WindowProcessController wpc) {
+        public PreBindInfo preBindApplication(WindowProcessController wpc, ApplicationInfo info) {
             synchronized (mGlobalLockWithoutBoost) {
                 mTaskSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo);
+                wpc.onConfigurationChanged(getGlobalConfiguration());
+                // The "info" can be the target of instrumentation.
+                return new PreBindInfo(compatibilityInfoForPackageLocked(info),
+                        new Configuration(wpc.getConfiguration()));
             }
         }
 
@@ -6767,7 +6776,7 @@
             synchronized (mGlobalLock) {
                 // The output proto of "activity --proto activities"
                 mRootWindowContainer.dumpDebug(
-                        proto, ROOT_WINDOW_CONTAINER, WindowTraceLogLevel.ALL);
+                        proto, ROOT_WINDOW_CONTAINER, WindowTracingLogLevel.ALL);
             }
         }
 
@@ -7469,9 +7478,7 @@
                     FEATURE_LEANBACK);
             final boolean isArc = arcFeature != null && arcFeature.version >= 0;
             final boolean isTv = tvFeature != null && tvFeature.version >= 0;
-            sIsPip2ExperimentEnabled = SystemProperties.getBoolean(
-                    "persist.wm_shell.pip2", false)
-                    || (Flags.enablePip2Implementation() && !isArc && !isTv);
+            sIsPip2ExperimentEnabled = Flags.enablePip2() && !isArc && !isTv;
         }
         return sIsPip2ExperimentEnabled;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 8ef2693..9a3ad2d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -107,7 +107,6 @@
 import android.app.servertransaction.PauseActivityItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.StopActivityItem;
-import android.companion.virtual.VirtualDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -158,6 +157,7 @@
 import com.android.server.am.ActivityManagerService;
 import com.android.server.am.HostingRecord;
 import com.android.server.am.UserState;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.pm.SaferIntentUtils;
 import com.android.server.utils.Slogf;
 import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
@@ -202,6 +202,12 @@
      */
     private static final int KILL_TASK_PROCESSES_TIMEOUT_MS = 1000;
 
+    /**
+     * The delay to run idle check. It may give a chance to keep launch power mode if an activity
+     * is starting while the device is sleeping and then the device is unlocked in a short time.
+     */
+    private static final int IDLE_NOW_DELAY_WHILE_SLEEPING_MS = 100;
+
     private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG;
     private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_TASK_MSG + 1;
     private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_TASK_MSG + 2;
@@ -279,7 +285,7 @@
     private WindowManagerService mWindowManager;
 
     private AppOpsManager mAppOpsManager;
-    private VirtualDeviceManager mVirtualDeviceManager;
+    private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
 
     /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
@@ -1292,16 +1298,24 @@
         if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY)  {
             return Context.DEVICE_ID_DEFAULT;
         }
-        if (mVirtualDeviceManager == null) {
+        if (mVirtualDeviceManagerInternal == null) {
             if (mService.mHasCompanionDeviceSetupFeature) {
-                mVirtualDeviceManager =
-                        mService.mContext.getSystemService(VirtualDeviceManager.class);
+                mVirtualDeviceManagerInternal =
+                        LocalServices.getService(VirtualDeviceManagerInternal.class);
             }
-            if (mVirtualDeviceManager == null) {
+            if (mVirtualDeviceManagerInternal == null) {
                 return Context.DEVICE_ID_DEFAULT;
             }
         }
-        return mVirtualDeviceManager.getDeviceIdForDisplayId(displayId);
+        return mVirtualDeviceManagerInternal.getDeviceIdForDisplayId(displayId);
+    }
+
+    boolean isDeviceOwnerUid(int displayId, int callingUid) {
+        final int deviceId = getDeviceIdForDisplayId(displayId);
+        if (deviceId == Context.DEVICE_ID_DEFAULT || deviceId == Context.DEVICE_ID_INVALID) {
+            return false;
+        }
+        return mVirtualDeviceManagerInternal.getDeviceOwnerUid(deviceId) == callingUid;
     }
 
     private AppOpsManager getAppOpsManager() {
@@ -1528,9 +1542,12 @@
             }
 
             mService.deferWindowLayout();
-            final Transition newTransition = task.mTransitionController.isShellTransitionsEnabled()
-                    ? task.mTransitionController.isCollecting() ? null
-                    : task.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
+            boolean newTransition = false;
+            Transition transition = task.mTransitionController.getCollectingTransition();
+            if (transition == null && task.mTransitionController.isShellTransitionsEnabled()) {
+                transition = task.mTransitionController.createTransition(TRANSIT_TO_FRONT);
+                newTransition = true;
+            }
             task.mTransitionController.collect(task);
             reason = reason + " findTaskToMoveToFront";
             boolean reparented = false;
@@ -1574,8 +1591,8 @@
                 // transition to avoid delaying the starting window.
                 r.showStartingWindow(true /* taskSwitch */);
             }
-            if (newTransition != null) {
-                task.mTransitionController.requestStartTransition(newTransition, task,
+            if (newTransition) {
+                task.mTransitionController.requestStartTransition(transition, task,
                         options != null ? options.getRemoteTransition() : null,
                         null /* displayChange */);
             }
@@ -1644,7 +1661,7 @@
 
         mService.deferWindowLayout();
         try {
-            rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            rootTask.setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
             if (rootTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
                 rootTask.setBounds(null);
             }
@@ -1705,7 +1722,10 @@
             // Prevent recursion.
             return;
         }
-        final Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
+        Transition transit = task.mTransitionController.requestCloseTransitionIfNeeded(task);
+        if (transit == null) {
+            transit = task.mTransitionController.getCollectingTransition();
+        }
         if (transit != null) {
             transit.collectClose(task);
             if (!task.mTransitionController.useFullReadyTracking()) {
@@ -1717,8 +1737,6 @@
                 // before anything that may need it to wait (setReady(false)).
                 transit.setReady(task, true);
             }
-        } else if (task.mTransitionController.isCollecting()) {
-            task.mTransitionController.getCollectingTransition().collectClose(task);
         }
         // Consume the stopping activities immediately so activity manager won't skip killing
         // the process because it is still foreground state, i.e. RESUMED -> PAUSING set from
@@ -2248,7 +2266,9 @@
     final void scheduleIdle() {
         if (!mHandler.hasMessages(IDLE_NOW_MSG)) {
             if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdle: Callers=" + Debug.getCallers(4));
-            mHandler.sendEmptyMessage(IDLE_NOW_MSG);
+            final long delayMs = mService.isSleepingLocked() && mService.mayBeLaunchingApp()
+                    ? IDLE_NOW_DELAY_WHILE_SLEEPING_MS : 0;
+            mHandler.sendEmptyMessageDelayed(IDLE_NOW_MSG, delayMs);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
index d2f3d1d..f245efd 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioOverrides.java
@@ -220,7 +220,7 @@
     float getFixedOrientationLetterboxAspectRatio(@NonNull Configuration parentConfiguration) {
         return shouldUseSplitScreenAspectRatio(parentConfiguration)
                 ? getSplitScreenAspectRatio()
-                : mActivityRecord.shouldCreateCompatDisplayInsets()
+                : mActivityRecord.shouldCreateAppCompatDisplayInsets()
                         ? getDefaultMinAspectRatioForUnresizableApps()
                         : getDefaultMinAspectRatio();
     }
@@ -233,7 +233,8 @@
         return mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
     }
 
-    private float getDisplaySizeMinAspectRatio() {
+    @VisibleForTesting
+    float getDisplaySizeMinAspectRatio() {
         final DisplayArea displayArea = mActivityRecord.getDisplayArea();
         if (displayArea == null) {
             return mActivityRecord.info.getMinAspectRatio();
@@ -263,7 +264,13 @@
                     && cameraPolicy.isTreatmentEnabledForActivity(mActivityRecord));
     }
 
-    @VisibleForTesting
+    /**
+     * Returns the value of the user aspect ratio override property. If unset, return {@code true}.
+     */
+    boolean getAllowUserAspectRatioOverridePropertyValue() {
+        return !mAllowUserAspectRatioOverrideOptProp.isFalse();
+    }
+
     int getUserMinAspectRatioOverrideCode() {
         try {
             return mActivityRecord.mAtmService.getPackageManager()
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index aeaaffd..d8abf69 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -217,7 +217,7 @@
      */
     boolean isCameraCompatSplitScreenAspectRatioAllowed() {
         return mAppCompatConfiguration.isCameraCompatSplitScreenAspectRatioEnabled()
-                && !mActivityRecord.shouldCreateCompatDisplayInsets();
+                && !mActivityRecord.shouldCreateAppCompatDisplayInsets();
     }
 
     @FreeformCameraCompatMode
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 1562cf6..a42b879 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -24,6 +24,7 @@
 import android.content.res.Configuration;
 import android.widget.Toast;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.window.flags.Flags;
 
 /**
@@ -32,7 +33,8 @@
 class AppCompatCameraPolicy {
 
     @Nullable
-    private final CameraStateMonitor mCameraStateMonitor;
+    @VisibleForTesting
+    final CameraStateMonitor mCameraStateMonitor;
     @Nullable
     private final ActivityRefresher mActivityRefresher;
     @Nullable
@@ -122,6 +124,9 @@
     }
 
     void start() {
+        if (mDisplayRotationCompatPolicy != null) {
+            mDisplayRotationCompatPolicy.start();
+        }
         if (mCameraCompatFreeformPolicy != null) {
             mCameraCompatFreeformPolicy.start();
         }
@@ -150,6 +155,10 @@
         return mCameraCompatFreeformPolicy != null;
     }
 
+    boolean hasCameraStateMonitor() {
+        return mCameraStateMonitor != null;
+    }
+
     @ScreenOrientation
     int getOrientation() {
         return mDisplayRotationCompatPolicy != null
diff --git a/services/core/java/com/android/server/wm/AppCompatConfiguration.java b/services/core/java/com/android/server/wm/AppCompatConfiguration.java
index ffa4251d..42378aa 100644
--- a/services/core/java/com/android/server/wm/AppCompatConfiguration.java
+++ b/services/core/java/com/android/server/wm/AppCompatConfiguration.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.utils.DimenPxIntSupplier;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.function.Function;
@@ -1382,6 +1383,26 @@
         setUserAppAspectRatioFullscreenOverrideEnabled(false);
     }
 
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        // TODO(b/359438445): Add more useful information to dump().
+        pw.println(prefix + "  letterboxPositionForHorizontalReachability="
+                + letterboxHorizontalReachabilityPositionToString(
+                    getLetterboxPositionForHorizontalReachability(
+                            /* isInFullScreenBookMode */ false)));
+        pw.println(prefix + "  letterboxPositionForVerticalReachability="
+                + letterboxVerticalReachabilityPositionToString(
+                    getLetterboxPositionForVerticalReachability(
+                            /* isInFullScreenTabletopMode */ false)));
+        pw.println(prefix + "  fixedOrientationLetterboxAspectRatio="
+                + getFixedOrientationLetterboxAspectRatio());
+        pw.println(prefix + "  defaultMinAspectRatioForUnresizableApps="
+                + getDefaultMinAspectRatioForUnresizableApps());
+        pw.println(prefix + "  isSplitScreenAspectRatioForUnresizableAppsEnabled="
+                + getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
+        pw.println(prefix + "  isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
+                + getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
+    }
+
     /**
      * Checks whether the multiplier is between [0,1].
      *
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index d38edfc..173362c 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -21,6 +21,8 @@
 
 import com.android.server.wm.utils.OptPropFactory;
 
+import java.io.PrintWriter;
+
 /**
  * Allows the interaction with all the app compat policies and configurations
  */
@@ -37,9 +39,15 @@
     @NonNull
     private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
     @NonNull
+    private final DesktopAppCompatAspectRatioPolicy mDesktopAppCompatAspectRatioPolicy;
+    @NonNull
     private final AppCompatOverrides mAppCompatOverrides;
     @NonNull
     private final AppCompatDeviceStateQuery mAppCompatDeviceStateQuery;
+    @NonNull
+    private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
+    @NonNull
+    private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
@@ -57,6 +65,12 @@
                 mTransparentPolicy, mAppCompatOverrides);
         mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
                 wmService.mAppCompatConfiguration);
+        mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
+                wmService.mAppCompatConfiguration);
+        mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
+                mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
+        mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord,
+                mAppCompatOverrides);
     }
 
     @NonNull
@@ -75,6 +89,11 @@
     }
 
     @NonNull
+    DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+        return mDesktopAppCompatAspectRatioPolicy;
+    }
+
+    @NonNull
     AppCompatOverrides getAppCompatOverrides() {
         return mAppCompatOverrides;
     }
@@ -113,6 +132,11 @@
     }
 
     @NonNull
+    AppCompatLetterboxPolicy getAppCompatLetterboxPolicy() {
+        return mAppCompatLetterboxPolicy;
+    }
+
+    @NonNull
     AppCompatFocusOverrides getAppCompatFocusOverrides() {
         return mAppCompatOverrides.getAppCompatFocusOverrides();
     }
@@ -127,4 +151,20 @@
         return mAppCompatDeviceStateQuery;
     }
 
+    @NonNull
+    AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+        return mAppCompatOverrides.getAppCompatLetterboxOverrides();
+    }
+
+    @NonNull
+    AppCompatSizeCompatModePolicy getAppCompatSizeCompatModePolicy() {
+        return mAppCompatSizeCompatModePolicy;
+    }
+
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        getTransparentPolicy().dump(pw, prefix);
+        getAppCompatLetterboxPolicy().dump(pw, prefix);
+        getAppCompatSizeCompatModePolicy().dump(pw, prefix);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatDisplayInsets.java b/services/core/java/com/android/server/wm/AppCompatDisplayInsets.java
new file mode 100644
index 0000000..743bfb9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatDisplayInsets.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.wm;
+
+
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Surface;
+
+/**
+ * The precomputed insets of the display in each rotation. This is used to make the size
+ * compatibility mode activity compute the configuration without relying on its current display.
+ */
+class AppCompatDisplayInsets {
+    /** The original rotation the compat insets were computed in. */
+    final @Surface.Rotation int mOriginalRotation;
+    /** The original requested orientation for the activity. */
+    final @Configuration.Orientation int mOriginalRequestedOrientation;
+    /** The container width on rotation 0. */
+    private final int mWidth;
+    /** The container height on rotation 0. */
+    private final int mHeight;
+    /** Whether the {@link Task} windowingMode represents a floating window*/
+    final boolean mIsFloating;
+    /**
+     * Whether is letterboxed because of fixed orientation or aspect ratio when
+     * the unresizable activity is first shown.
+     */
+    final boolean mIsInFixedOrientationOrAspectRatioLetterbox;
+    /**
+     * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
+     * is used to compute the appBounds.
+     */
+    final Rect[] mNonDecorInsets = new Rect[4];
+    /**
+     * The stableInsets for each rotation. Includes the status bar inset and the
+     * nonDecorInsets. It is used to compute {@link Configuration#screenWidthDp} and
+     * {@link Configuration#screenHeightDp}.
+     */
+    final Rect[] mStableInsets = new Rect[4];
+
+    /** Constructs the environment to simulate the bounds behavior of the given container. */
+    AppCompatDisplayInsets(@NonNull DisplayContent display, @NonNull ActivityRecord container,
+            @Nullable Rect letterboxedContainerBounds, boolean useOverrideInsets) {
+        mOriginalRotation = display.getRotation();
+        mIsFloating = container.getWindowConfiguration().tasksAreFloating();
+        mOriginalRequestedOrientation = container.getRequestedConfigurationOrientation();
+        if (mIsFloating) {
+            final Rect containerBounds = container.getWindowConfiguration().getBounds();
+            mWidth = containerBounds.width();
+            mHeight = containerBounds.height();
+            // For apps in freeform, the task bounds are the parent bounds from the app's
+            // perspective. No insets because within a window.
+            final Rect emptyRect = new Rect();
+            for (int rotation = 0; rotation < 4; rotation++) {
+                mNonDecorInsets[rotation] = emptyRect;
+                mStableInsets[rotation] = emptyRect;
+            }
+            mIsInFixedOrientationOrAspectRatioLetterbox = false;
+            return;
+        }
+
+        final Task task = container.getTask();
+
+        mIsInFixedOrientationOrAspectRatioLetterbox = letterboxedContainerBounds != null;
+
+        // Store the bounds of the Task for the non-resizable activity to use in size compat
+        // mode so that the activity will not be resized regardless the windowing mode it is
+        // currently in.
+        // When an activity needs to be letterboxed because of fixed orientation or aspect
+        // ratio, use resolved bounds instead of task bounds since the activity will be
+        // displayed within these even if it is in size compat mode.
+        final Rect filledContainerBounds = mIsInFixedOrientationOrAspectRatioLetterbox
+                ? letterboxedContainerBounds
+                : task != null ? task.getBounds() : display.getBounds();
+        final boolean useActivityRotation = container.hasFixedRotationTransform()
+                && mIsInFixedOrientationOrAspectRatioLetterbox;
+        final int filledContainerRotation = useActivityRotation
+                ? container.getWindowConfiguration().getRotation()
+                : display.getConfiguration().windowConfiguration.getRotation();
+        final Point dimensions = getRotationZeroDimensions(
+                filledContainerBounds, filledContainerRotation);
+        mWidth = dimensions.x;
+        mHeight = dimensions.y;
+
+        // Bounds of the filled container if it doesn't fill the display.
+        final Rect unfilledContainerBounds =
+                filledContainerBounds.equals(display.getBounds()) ? null : new Rect();
+        final DisplayPolicy policy = display.getDisplayPolicy();
+        for (int rotation = 0; rotation < 4; rotation++) {
+            mNonDecorInsets[rotation] = new Rect();
+            mStableInsets[rotation] = new Rect();
+            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+            final int dw = rotated ? display.mBaseDisplayHeight : display.mBaseDisplayWidth;
+            final int dh = rotated ? display.mBaseDisplayWidth : display.mBaseDisplayHeight;
+            final DisplayPolicy.DecorInsets.Info decorInfo =
+                    policy.getDecorInsetsInfo(rotation, dw, dh);
+            if (useOverrideInsets) {
+                mStableInsets[rotation].set(decorInfo.mOverrideConfigInsets);
+                mNonDecorInsets[rotation].set(decorInfo.mOverrideNonDecorInsets);
+            } else {
+                mStableInsets[rotation].set(decorInfo.mConfigInsets);
+                mNonDecorInsets[rotation].set(decorInfo.mNonDecorInsets);
+            }
+
+            if (unfilledContainerBounds == null) {
+                continue;
+            }
+            // The insets is based on the display, but the container may be smaller than the
+            // display, so update the insets to exclude parts that are not intersected with the
+            // container.
+            unfilledContainerBounds.set(filledContainerBounds);
+            display.rotateBounds(
+                    filledContainerRotation,
+                    rotation,
+                    unfilledContainerBounds);
+            updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
+            updateInsetsForBounds(unfilledContainerBounds, dw, dh, mStableInsets[rotation]);
+        }
+    }
+
+    /**
+     * Gets the width and height of the {@code container} when it is not rotated, so that after
+     * the display is rotated, we can calculate the bounds by rotating the dimensions.
+     * @see #getBoundsByRotation
+     */
+    @NonNull
+    private static Point getRotationZeroDimensions(final @NonNull Rect bounds,
+            @Surface.Rotation int rotation) {
+        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+        final int width = bounds.width();
+        final int height = bounds.height();
+        return rotated ? new Point(height, width) : new Point(width, height);
+    }
+
+    /**
+     * Updates the display insets to exclude the parts that are not intersected with the given
+     * bounds.
+     */
+    private static void updateInsetsForBounds(@NonNull Rect bounds, int displayWidth,
+            int displayHeight, @NonNull Rect inset) {
+        inset.left = Math.max(0, inset.left - bounds.left);
+        inset.top = Math.max(0, inset.top - bounds.top);
+        inset.right = Math.max(0, bounds.right - displayWidth + inset.right);
+        inset.bottom = Math.max(0, bounds.bottom - displayHeight + inset.bottom);
+    }
+
+    void getBoundsByRotation(@NonNull Rect outBounds, @Surface.Rotation int rotation) {
+        final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+        final int dw = rotated ? mHeight : mWidth;
+        final int dh = rotated ? mWidth : mHeight;
+        outBounds.set(0, 0, dw, dh);
+    }
+
+    void getFrameByOrientation(@NonNull Rect outBounds,
+            @Configuration.Orientation int orientation) {
+        final int longSide = Math.max(mWidth, mHeight);
+        final int shortSide = Math.min(mWidth, mHeight);
+        final boolean isLandscape = orientation == ORIENTATION_LANDSCAPE;
+        outBounds.set(0, 0, isLandscape ? longSide : shortSide,
+                isLandscape ? shortSide : longSide);
+    }
+
+    /** Gets the horizontal centered container bounds for size compatibility mode. */
+    void getContainerBounds(@NonNull Rect outAppBounds, @NonNull Rect outBounds,
+            @Surface.Rotation int rotation, @Configuration.Orientation int orientation,
+            boolean orientationRequested, boolean isFixedToUserRotation) {
+        getFrameByOrientation(outBounds, orientation);
+        if (mIsFloating) {
+            outAppBounds.set(outBounds);
+            return;
+        }
+
+        getBoundsByRotation(outAppBounds, rotation);
+        final int dW = outAppBounds.width();
+        final int dH = outAppBounds.height();
+        final boolean isOrientationMismatched =
+                ((outBounds.width() > outBounds.height()) != (dW > dH));
+
+        if (isOrientationMismatched && isFixedToUserRotation && orientationRequested) {
+            // The orientation is mismatched but the display cannot rotate. The bounds will fit
+            // to the short side of container.
+            if (orientation == ORIENTATION_LANDSCAPE) {
+                outBounds.bottom = (int) ((float) dW * dW / dH);
+                outBounds.right = dW;
+            } else {
+                outBounds.bottom = dH;
+                outBounds.right = (int) ((float) dH * dH / dW);
+            }
+            outBounds.offset(getCenterOffset(mWidth, outBounds.width()), 0 /* dy */);
+        }
+        outAppBounds.set(outBounds);
+
+        if (isOrientationMismatched) {
+            // One side of container is smaller than the requested size, then it will be scaled
+            // and the final position will be calculated according to the parent container and
+            // scale, so the original size shouldn't be shrunk by insets.
+            final Rect insets = mNonDecorInsets[rotation];
+            outBounds.offset(insets.left, insets.top);
+            outAppBounds.offset(insets.left, insets.top);
+        } else if (rotation != ROTATION_UNDEFINED) {
+            // Ensure the app bounds won't overlap with insets.
+            TaskFragment.intersectWithInsetsIfFits(outAppBounds, outBounds,
+                    mNonDecorInsets[rotation]);
+        }
+    }
+
+    /** @return The horizontal / vertical offset of putting the content in the center of viewport.*/
+    private static int getCenterOffset(int viewportDim, int contentDim) {
+        return (int) ((viewportDim - contentDim + 1) * 0.5f);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
new file mode 100644
index 0000000..24ed14c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxOverrides.java
@@ -0,0 +1,147 @@
+/*
+ * 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.wm;
+
+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.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.graphics.Color;
+import android.util.Slog;
+import android.view.WindowManager;
+
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+/**
+ * Encapsulates overrides and configuration related to the Letterboxing policy.
+ */
+class AppCompatLetterboxOverrides {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "AppCompatLetterboxOverrides" : TAG_ATM;
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final AppCompatConfiguration mAppCompatConfiguration;
+
+    private boolean mShowWallpaperForLetterboxBackground;
+
+    AppCompatLetterboxOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration) {
+        mActivityRecord = activityRecord;
+        mAppCompatConfiguration = appCompatConfiguration;
+    }
+
+    boolean shouldLetterboxHaveRoundedCorners() {
+        // TODO(b/214030873): remove once background is drawn for transparent activities
+        // Letterbox shouldn't have rounded corners if the activity is transparent
+        return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
+                && mActivityRecord.fillsParent();
+    }
+
+    boolean isLetterboxEducationEnabled() {
+        return mAppCompatConfiguration.getIsEducationEnabled();
+    }
+
+    boolean hasWallpaperBackgroundForLetterbox() {
+        return mShowWallpaperForLetterboxBackground;
+    }
+
+    boolean checkWallpaperBackgroundForLetterbox(boolean wallpaperShouldBeShown) {
+        if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
+            mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
+            return true;
+        }
+        return false;
+    }
+
+    @NonNull
+    Color getLetterboxBackgroundColor() {
+        final WindowState w = mActivityRecord.findMainWindow();
+        if (w == null || w.isLetterboxedForDisplayCutout()) {
+            return Color.valueOf(Color.BLACK);
+        }
+        final @LetterboxBackgroundType int letterboxBackgroundType =
+                mAppCompatConfiguration.getLetterboxBackgroundType();
+        final ActivityManager.TaskDescription taskDescription = mActivityRecord.taskDescription;
+        switch (letterboxBackgroundType) {
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
+                if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
+                    return Color.valueOf(taskDescription.getBackgroundColorFloating());
+                }
+                break;
+            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
+                if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
+                    return Color.valueOf(taskDescription.getBackgroundColor());
+                }
+                break;
+            case LETTERBOX_BACKGROUND_WALLPAPER:
+                if (hasWallpaperBackgroundForLetterbox()) {
+                    // Color is used for translucent scrim that dims wallpaper.
+                    return mAppCompatConfiguration.getLetterboxBackgroundColor();
+                }
+                Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+                        + "blur is not supported by a device or not supported in the current "
+                        + "window configuration or both alpha scrim and blur radius aren't "
+                        + "provided so using solid color background");
+                break;
+            case LETTERBOX_BACKGROUND_SOLID_COLOR:
+                return mAppCompatConfiguration.getLetterboxBackgroundColor();
+            default:
+                throw new AssertionError(
+                        "Unexpected letterbox background type: " + letterboxBackgroundType);
+        }
+        // If picked option configured incorrectly or not supported then default to a solid color
+        // background.
+        return mAppCompatConfiguration.getLetterboxBackgroundColor();
+    }
+
+    int getLetterboxActivityCornersRadius() {
+        return mAppCompatConfiguration.getLetterboxActivityCornersRadius();
+    }
+
+    boolean isLetterboxActivityCornersRounded() {
+        return mAppCompatConfiguration.isLetterboxActivityCornersRounded();
+    }
+
+    @LetterboxBackgroundType
+    int getLetterboxBackgroundType() {
+        return mAppCompatConfiguration.getLetterboxBackgroundType();
+    }
+
+    int getLetterboxWallpaperBlurRadiusPx() {
+        int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
+        return Math.max(blurRadius, 0);
+    }
+
+    float getLetterboxWallpaperDarkScrimAlpha() {
+        float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
+        // No scrim by default.
+        return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
+    }
+
+    boolean isLetterboxWallpaperBlurSupported() {
+        return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
+                .isCrossWindowBlurEnabled();
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
new file mode 100644
index 0000000..afc6506
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -0,0 +1,411 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.LetterboxDetails;
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+import java.io.PrintWriter;
+
+/**
+ * Encapsulates the logic for the Letterboxing policy.
+ */
+class AppCompatLetterboxPolicy {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final LetterboxPolicyState mLetterboxPolicyState;
+    @NonNull
+    private final AppCompatRoundedCorners mAppCompatRoundedCorners;
+    @NonNull
+    private final AppCompatConfiguration mAppCompatConfiguration;
+
+    private boolean mLastShouldShowLetterboxUi;
+
+    AppCompatLetterboxPolicy(@NonNull ActivityRecord  activityRecord,
+            @NonNull AppCompatConfiguration appCompatConfiguration) {
+        mActivityRecord = activityRecord;
+        mLetterboxPolicyState = new LetterboxPolicyState();
+        // TODO (b/358334569) Improve cutout logic dependency on app compat.
+        mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord,
+                this::isLetterboxedNotForDisplayCutout);
+        mAppCompatConfiguration = appCompatConfiguration;
+    }
+
+    /** Cleans up {@link Letterbox} if it exists.*/
+    void stop() {
+        mLetterboxPolicyState.stop();
+    }
+
+    /** @return {@value true} if the letterbox policy is running and the activity letterboxed. */
+    boolean isRunning() {
+        return mLetterboxPolicyState.isRunning();
+    }
+
+    void onMovedToDisplay(int displayId) {
+        mLetterboxPolicyState.onMovedToDisplay(displayId);
+    }
+
+    /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+    @NonNull
+    Rect getLetterboxInsets() {
+        return mLetterboxPolicyState.getLetterboxInsets();
+    }
+
+    /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
+    void getLetterboxInnerBounds(@NonNull Rect outBounds) {
+        mLetterboxPolicyState.getLetterboxInnerBounds(outBounds);
+    }
+
+    @Nullable
+    LetterboxDetails getLetterboxDetails() {
+        return mLetterboxPolicyState.getLetterboxDetails();
+    }
+
+    /**
+     * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
+     *     when the current activity is displayed.
+     */
+    boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
+        return mLetterboxPolicyState.isFullyTransparentBarAllowed(rect);
+    }
+
+    void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
+            @NonNull SurfaceControl.Transaction t,
+            @NonNull SurfaceControl.Transaction inputT) {
+        mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint, t, inputT);
+    }
+
+    void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint) {
+        mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint,
+                mActivityRecord.getSyncTransaction(), mActivityRecord.getPendingTransaction());
+    }
+
+    void start(@NonNull WindowState w) {
+        if (shouldNotLayoutLetterbox(w)) {
+            return;
+        }
+        mAppCompatRoundedCorners.updateRoundedCornersIfNeeded(w);
+        updateWallpaperForLetterbox(w);
+        if (shouldShowLetterboxUi(w)) {
+            mLetterboxPolicyState.layoutLetterboxIfNeeded(w);
+        }  else {
+            mLetterboxPolicyState.hide();
+        }
+    }
+
+    @VisibleForTesting
+    boolean shouldShowLetterboxUi(@NonNull WindowState mainWindow) {
+        if (mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+                .getIsRelaunchingAfterRequestedOrientationChanged()) {
+            return mLastShouldShowLetterboxUi;
+        }
+
+        final boolean shouldShowLetterboxUi =
+                (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
+                        || mActivityRecord.isVisibleRequested())
+                        && mainWindow.areAppWindowBoundsLetterboxed()
+                        // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
+                        // WindowContainer#showWallpaper because the later will return true when
+                        // this activity is using blurred wallpaper for letterbox background.
+                        && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
+
+        mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
+
+        return shouldShowLetterboxUi;
+    }
+
+    @VisibleForTesting
+    @Nullable
+    Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+        return mAppCompatRoundedCorners.getCropBoundsIfNeeded(mainWindow);
+    }
+
+    /**
+     * Returns rounded corners radius the letterboxed activity should have based on override in
+     * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+     * Device corners can be different on the right and left sides, but we use the same radius
+     * for all corners for consistency and pick a minimal bottom one for consistency with a
+     * taskbar rounded corners.
+     *
+     * @param mainWindow    The {@link WindowState} to consider for the rounded corners calculation.
+     */
+    int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+        return mAppCompatRoundedCorners.getRoundedCornersRadius(mainWindow);
+    }
+
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        final WindowState mainWin = mActivityRecord.findMainWindow();
+        if (mainWin == null) {
+            return;
+        }
+        boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed();
+        pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
+        pw.println(prefix + "isLetterboxRunning=" + isRunning());
+        if (!areBoundsLetterboxed) {
+            return;
+        }
+        pw.println(prefix + "  letterboxReason="
+                + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin));
+        mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy().dump(pw, prefix);
+        final AppCompatLetterboxOverrides letterboxOverride = mActivityRecord.mAppCompatController
+                .getAppCompatLetterboxOverrides();
+        pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
+                letterboxOverride.getLetterboxBackgroundColor().toArgb()));
+        pw.println(prefix + "  letterboxBackgroundType="
+                + letterboxBackgroundTypeToString(letterboxOverride.getLetterboxBackgroundType()));
+        pw.println(prefix + "  letterboxCornerRadius=" + getRoundedCornersRadius(mainWin));
+        if (letterboxOverride.getLetterboxBackgroundType() == LETTERBOX_BACKGROUND_WALLPAPER) {
+            pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
+                    + letterboxOverride.isLetterboxWallpaperBlurSupported());
+            pw.println(prefix + "  letterboxBackgroundWallpaperDarkScrimAlpha="
+                    + letterboxOverride.getLetterboxWallpaperDarkScrimAlpha());
+            pw.println(prefix + "  letterboxBackgroundWallpaperBlurRadius="
+                    + letterboxOverride.getLetterboxWallpaperBlurRadiusPx());
+        }
+        mAppCompatConfiguration.dump(pw, prefix);
+    }
+
+    private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) {
+        final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+                .mAppCompatController.getAppCompatLetterboxOverrides();
+        final @LetterboxBackgroundType int letterboxBackgroundType =
+                letterboxOverrides.getLetterboxBackgroundType();
+        boolean wallpaperShouldBeShown =
+                letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
+                        // Don't use wallpaper as a background if letterboxed for display cutout.
+                        && isLetterboxedNotForDisplayCutout(mainWindow)
+                        // Check that dark scrim alpha or blur radius are provided
+                        && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() > 0
+                        || letterboxOverrides.getLetterboxWallpaperDarkScrimAlpha() > 0)
+                        // Check that blur is supported by a device if blur radius is provided.
+                        && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() <= 0
+                        || letterboxOverrides.isLetterboxWallpaperBlurSupported());
+        if (letterboxOverrides.checkWallpaperBackgroundForLetterbox(wallpaperShouldBeShown)) {
+            mActivityRecord.requestUpdateWallpaperIfNeeded();
+        }
+    }
+
+    private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) {
+        return shouldShowLetterboxUi(mainWindow)
+                && !mainWindow.isLetterboxedForDisplayCutout();
+    }
+
+    private static boolean shouldNotLayoutLetterbox(@Nullable WindowState w) {
+        if (w == null) {
+            return true;
+        }
+        final int type = w.mAttrs.type;
+        // Allow letterbox to be displayed early for base application or application starting
+        // windows even if it is not on the top z order to prevent flickering when the
+        // letterboxed window is brought to the top
+        return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
+                || w.mAnimatingExit;
+    }
+
+    private class LetterboxPolicyState {
+
+        @Nullable
+        private Letterbox mLetterbox;
+
+        void layoutLetterboxIfNeeded(@NonNull WindowState w) {
+            if (!isRunning()) {
+                final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+                        .mAppCompatController.getAppCompatLetterboxOverrides();
+                final AppCompatReachabilityPolicy reachabilityPolicy = mActivityRecord
+                        .mAppCompatController.getAppCompatReachabilityPolicy();
+                mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
+                        mActivityRecord.mWmService.mTransactionFactory,
+                        reachabilityPolicy, letterboxOverrides,
+                        this::getLetterboxParentSurface);
+                mLetterbox.attachInput(w);
+                mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+                        .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame);
+            }
+            final Point letterboxPosition = new Point();
+            if (mActivityRecord.isInLetterboxAnimation()) {
+                // In this case we attach the letterbox to the task instead of the activity.
+                mActivityRecord.getTask().getPosition(letterboxPosition);
+            } else {
+                mActivityRecord.getPosition(letterboxPosition);
+            }
+
+            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
+            // priority because the activity is launched in a rotated environment. In multi-window
+            // mode, the taskFragment-level represents this for both split-screen
+            // and activity-embedding. In fullscreen-mode, the task container does
+            // (since the orientation letterbox is also applied to the task).
+            final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
+            final Rect spaceToFill = transformedBounds != null
+                    ? transformedBounds
+                    : mActivityRecord.inMultiWindowMode()
+                            ? mActivityRecord.getTaskFragment().getBounds()
+                            : mActivityRecord.getRootTask().getParent().getBounds();
+            // In case of translucent activities an option is to use the WindowState#getFrame() of
+            // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
+            // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
+            // information and in particular it might provide a value for a smaller area making
+            // the letterbox overlap with the translucent activity's frame.
+            // If we use WindowState#getFrame() for the translucent activity's letterbox inner
+            // frame, the letterbox will then be overlapped with the translucent activity's frame.
+            // Because the surface layer of letterbox is lower than an activity window, this
+            // won't crop the content, but it may affect other features that rely on values stored
+            // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
+            // For this reason we use ActivityRecord#getBounds() that the translucent activity
+            // inherits from the first opaque activity beneath and also takes care of the scaling
+            // in case of activities in size compat mode.
+            final TransparentPolicy transparentPolicy =
+                    mActivityRecord.mAppCompatController.getTransparentPolicy();
+            final Rect innerFrame =
+                    transparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
+            mLetterbox.layout(spaceToFill, innerFrame, letterboxPosition);
+            if (mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+                    .isDoubleTapEvent()) {
+                // We need to notify Shell that letterbox position has changed.
+                mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
+            }
+        }
+
+        /**
+         * @return  {@code true} if the policy is running and so if the current activity is
+         *          letterboxed.
+         */
+        boolean isRunning() {
+            return mLetterbox != null;
+        }
+
+        void onMovedToDisplay(int displayId) {
+            if (isRunning()) {
+                mLetterbox.onMovedToDisplay(displayId);
+            }
+        }
+
+        /** Cleans up {@link Letterbox} if it exists.*/
+        void stop() {
+            if (isRunning()) {
+                mLetterbox.destroy();
+                mLetterbox = null;
+            }
+            mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+                    .setLetterboxInnerBoundsSupplier(null);
+        }
+
+        void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint,
+                @NonNull SurfaceControl.Transaction t,
+                @NonNull SurfaceControl.Transaction inputT) {
+            if (shouldNotLayoutLetterbox(winHint)) {
+                return;
+            }
+            start(winHint);
+            if (isRunning() && mLetterbox.needsApplySurfaceChanges()) {
+                mLetterbox.applySurfaceChanges(t, inputT);
+            }
+        }
+
+        void hide() {
+            if (isRunning()) {
+                mLetterbox.hide();
+            }
+        }
+
+        /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
+        @NonNull
+        Rect getLetterboxInsets() {
+            if (isRunning()) {
+                return mLetterbox.getInsets();
+            } else {
+                return new Rect();
+            }
+        }
+
+        /** Gets the inner bounds of letterbox. The bounds will be empty with no letterbox. */
+        void getLetterboxInnerBounds(@NonNull Rect outBounds) {
+            if (isRunning()) {
+                outBounds.set(mLetterbox.getInnerFrame());
+                final WindowState w = mActivityRecord.findMainWindow();
+                if (w != null) {
+                    AppCompatUtils.adjustBoundsForTaskbar(w, outBounds);
+                }
+            } else {
+                outBounds.setEmpty();
+            }
+        }
+
+        /** Gets the outer bounds of letterbox. The bounds will be empty with no letterbox. */
+        private void getLetterboxOuterBounds(@NonNull Rect outBounds) {
+            if (isRunning()) {
+                outBounds.set(mLetterbox.getOuterFrame());
+            } else {
+                outBounds.setEmpty();
+            }
+        }
+
+        /**
+         * @return {@code true} if bar shown within a given rectangle is allowed to be fully
+         *          transparent when the current activity is displayed.
+         */
+        boolean isFullyTransparentBarAllowed(@NonNull Rect rect) {
+            return !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect);
+        }
+
+        @Nullable
+        LetterboxDetails getLetterboxDetails() {
+            final WindowState w = mActivityRecord.findMainWindow();
+            if (!isRunning() || w == null || w.isLetterboxedForDisplayCutout()) {
+                return null;
+            }
+            final Rect letterboxInnerBounds = new Rect();
+            final Rect letterboxOuterBounds = new Rect();
+            getLetterboxInnerBounds(letterboxInnerBounds);
+            getLetterboxOuterBounds(letterboxOuterBounds);
+
+            if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
+                return null;
+            }
+
+            return new LetterboxDetails(
+                    letterboxInnerBounds,
+                    letterboxOuterBounds,
+                    w.mAttrs.insetsFlags.appearance
+            );
+        }
+
+        @Nullable
+        private SurfaceControl getLetterboxParentSurface() {
+            if (mActivityRecord.isInLetterboxAnimation()) {
+                return mActivityRecord.getTask().getSurfaceControl();
+            }
+            return mActivityRecord.getSurfaceControl();
+        }
+
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 80bbee3..2f03105 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -37,6 +37,8 @@
     private final AppCompatResizeOverrides mAppCompatResizeOverrides;
     @NonNull
     private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
+    @NonNull
+    private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
 
     AppCompatOverrides(@NonNull ActivityRecord activityRecord,
             @NonNull AppCompatConfiguration appCompatConfiguration,
@@ -54,6 +56,8 @@
         mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
                 appCompatConfiguration, optPropBuilder);
         mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
+        mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord,
+                appCompatConfiguration);
     }
 
     @NonNull
@@ -85,4 +89,9 @@
     AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
         return mAppCompatReachabilityOverrides;
     }
+
+    @NonNull
+    AppCompatLetterboxOverrides getAppCompatLetterboxOverrides() {
+        return mAppCompatLetterboxOverrides;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index c3bf116..d03a803 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.PrintWriter;
 import java.util.function.Supplier;
 
 /**
@@ -74,6 +75,25 @@
         handleVerticalDoubleTap(y);
     }
 
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        final AppCompatReachabilityOverrides reachabilityOverrides =
+                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+        pw.println(prefix + "  isVerticalThinLetterboxed=" + reachabilityOverrides
+                .isVerticalThinLetterboxed());
+        pw.println(prefix + "  isHorizontalThinLetterboxed=" + reachabilityOverrides
+                .isHorizontalThinLetterboxed());
+        pw.println(prefix + "  isHorizontalReachabilityEnabled="
+                + reachabilityOverrides.isHorizontalReachabilityEnabled());
+        pw.println(prefix + "  isVerticalReachabilityEnabled="
+                + reachabilityOverrides.isVerticalReachabilityEnabled());
+        pw.println(prefix + "  letterboxHorizontalPositionMultiplier="
+                + reachabilityOverrides.getHorizontalPositionMultiplier(
+                mActivityRecord.getParent().getConfiguration()));
+        pw.println(prefix + "  letterboxVerticalPositionMultiplier="
+                + reachabilityOverrides.getVerticalPositionMultiplier(
+                mActivityRecord.getParent().getConfiguration()));
+    }
+
     private void handleHorizontalDoubleTap(int x) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
                 mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
diff --git a/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
new file mode 100644
index 0000000..077ce00
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatRoundedCorners.java
@@ -0,0 +1,143 @@
+/*
+ * 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.wm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.function.Predicate;
+
+/**
+ * Utility to unify rounded corners in app compat.
+ */
+class AppCompatRoundedCorners {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final Predicate<WindowState> mIsLetterboxedNotForDisplayCutout;
+
+    AppCompatRoundedCorners(@NonNull ActivityRecord  activityRecord,
+            @NonNull Predicate<WindowState> isLetterboxedNotForDisplayCutout) {
+        mActivityRecord = activityRecord;
+        mIsLetterboxedNotForDisplayCutout = isLetterboxedNotForDisplayCutout;
+    }
+
+    void updateRoundedCornersIfNeeded(@NonNull final WindowState mainWindow) {
+        final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
+        if (windowSurface == null || !windowSurface.isValid()) {
+            return;
+        }
+
+        // cropBounds must be non-null for the cornerRadius to be ever applied.
+        mActivityRecord.getSyncTransaction()
+                .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
+                .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
+    }
+
+    @VisibleForTesting
+    @Nullable
+    Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) {
+        if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+            // We don't want corner radius on the window.
+            // In the case the ActivityRecord requires a letterboxed animation we never want
+            // rounded corners on the window because rounded corners are applied at the
+            // animation-bounds surface level and rounded corners on the window would interfere
+            // with that leading to unexpected rounded corner positioning during the animation.
+            return null;
+        }
+
+        final Rect cropBounds = new Rect(mActivityRecord.getBounds());
+
+        // In case of translucent activities we check if the requested size is different from
+        // the size provided using inherited bounds. In that case we decide to not apply rounded
+        // corners because we assume the specific layout would. This is the case when the layout
+        // of the translucent activity uses only a part of all the bounds because of the use of
+        // LayoutParams.WRAP_CONTENT.
+        final TransparentPolicy transparentPolicy = mActivityRecord.mAppCompatController
+                .getTransparentPolicy();
+        if (transparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
+                || cropBounds.height() != mainWindow.mRequestedHeight)) {
+            return null;
+        }
+
+        // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
+        // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
+        // are in screen coordinates
+        AppCompatUtils.adjustBoundsForTaskbar(mainWindow, cropBounds);
+
+        final float scale = mainWindow.mInvGlobalScale;
+        if (scale != 1f && scale > 0f) {
+            cropBounds.scale(scale);
+        }
+
+        // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
+        // control is in the top left corner of an app window so offsetting bounds
+        // accordingly.
+        cropBounds.offsetTo(0, 0);
+        return cropBounds;
+    }
+
+    /**
+     * Returns rounded corners radius the letterboxed activity should have based on override in
+     * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
+     * Device corners can be different on the right and left sides, but we use the same radius
+     * for all corners for consistency and pick a minimal bottom one for consistency with a
+     * taskbar rounded corners.
+     *
+     * @param mainWindow    The {@link WindowState} to consider for rounded corners calculation.
+     */
+    int getRoundedCornersRadius(@NonNull final WindowState mainWindow) {
+        if (!requiresRoundedCorners(mainWindow)) {
+            return 0;
+        }
+        final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+                .mAppCompatController.getAppCompatLetterboxOverrides();
+        final int radius;
+        if (letterboxOverrides.getLetterboxActivityCornersRadius() >= 0) {
+            radius = letterboxOverrides.getLetterboxActivityCornersRadius();
+        } else {
+            final InsetsState insetsState = mainWindow.getInsetsState();
+            radius = Math.min(
+                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
+                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
+        }
+
+        final float scale = mainWindow.mInvGlobalScale;
+        return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
+    }
+
+    private static int getInsetsStateCornerRadius(@NonNull InsetsState insetsState,
+            @RoundedCorner.Position int position) {
+        final RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
+        return corner == null ? 0 : corner.getRadius();
+    }
+
+    private boolean requiresRoundedCorners(@NonNull final WindowState mainWindow) {
+        final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
+                .mAppCompatController.getAppCompatLetterboxOverrides();
+        return mIsLetterboxedNotForDisplayCutout.test(mainWindow)
+                && letterboxOverrides.isLetterboxActivityCornersRounded();
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
new file mode 100644
index 0000000..3be266e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AppCompatSizeCompatModePolicy.java
@@ -0,0 +1,439 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+
+import static com.android.server.wm.DesktopModeHelper.canEnterDesktopMode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+
+import com.android.window.flags.Flags;
+
+import java.io.PrintWriter;
+import java.util.function.DoubleSupplier;
+
+/**
+ * Encapsulate logic related to the SizeCompatMode.
+ */
+class AppCompatSizeCompatModePolicy {
+
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final AppCompatOverrides mAppCompatOverrides;
+
+    // Whether this activity is in size compatibility mode because its bounds don't fit in parent
+    // naturally.
+    private boolean mInSizeCompatModeForBounds = false;
+    /**
+     * The scale to fit at least one side of the activity to its parent. If the activity uses
+     * 1920x1080, and the actually size on the screen is 960x540, then the scale is 0.5.
+     */
+    private float mSizeCompatScale = 1f;
+
+    /**
+     * The bounds in global coordinates for activity in size compatibility mode.
+     * @see #hasSizeCompatBounds()
+     */
+    private Rect mSizeCompatBounds;
+
+    /**
+     * The precomputed display insets for resolving configuration. It will be non-null if
+     * {@link #shouldCreateAppCompatDisplayInsets} returns {@code true}.
+     */
+    @Nullable
+    private AppCompatDisplayInsets mAppCompatDisplayInsets;
+
+    AppCompatSizeCompatModePolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatOverrides appCompatOverrides) {
+        mActivityRecord = activityRecord;
+        mAppCompatOverrides = appCompatOverrides;
+    }
+
+    boolean isInSizeCompatModeForBounds() {
+        return mInSizeCompatModeForBounds;
+    }
+
+    void setInSizeCompatModeForBounds(boolean inSizeCompatModeForBounds) {
+        mInSizeCompatModeForBounds = inSizeCompatModeForBounds;
+    }
+
+    boolean hasSizeCompatBounds() {
+        return mSizeCompatBounds != null;
+    }
+
+    /**
+     * @return The {@code true} if the current instance has {@link mAppCompatDisplayInsets} without
+     * considering the inheritance implemented in {@link #getAppCompatDisplayInsets()}
+     */
+    boolean hasAppCompatDisplayInsetsWithoutInheritance() {
+        return mAppCompatDisplayInsets != null;
+    }
+
+    @Nullable
+    AppCompatDisplayInsets getAppCompatDisplayInsets() {
+        final TransparentPolicy transparentPolicy = mActivityRecord.mAppCompatController
+                .getTransparentPolicy();
+        if (transparentPolicy.isRunning()) {
+            return transparentPolicy.getInheritedAppCompatDisplayInsets();
+        }
+        return mAppCompatDisplayInsets;
+    }
+
+    float getCompatScaleIfAvailable(@NonNull DoubleSupplier scaleWhenNotAvailable) {
+        return hasSizeCompatBounds() ? mSizeCompatScale
+                : (float) scaleWhenNotAvailable.getAsDouble();
+    }
+
+    @NonNull
+    Rect getAppSizeCompatBoundsIfAvailable(@NonNull Rect boundsWhenNotAvailable) {
+        return hasSizeCompatBounds() ? mSizeCompatBounds : boundsWhenNotAvailable;
+    }
+
+    @NonNull
+    Rect replaceResolvedBoundsIfNeeded(@NonNull Rect resolvedBounds) {
+        return hasSizeCompatBounds() ? mSizeCompatBounds : resolvedBounds;
+    }
+
+    boolean applyOffsetIfNeeded(@NonNull Rect resolvedBounds,
+            @NonNull Configuration resolvedConfig, int offsetX, int offsetY) {
+        if (hasSizeCompatBounds()) {
+            mSizeCompatBounds.offset(offsetX , offsetY);
+            final int dy = mSizeCompatBounds.top - resolvedBounds.top;
+            final int dx = mSizeCompatBounds.left - resolvedBounds.left;
+            AppCompatUtils.offsetBounds(resolvedConfig, dx, dy);
+            return true;
+        }
+        return false;
+    }
+
+    void alignToTopIfNeeded(@NonNull Rect parentBounds) {
+        if (hasSizeCompatBounds()) {
+            mSizeCompatBounds.top = parentBounds.top;
+        }
+    }
+
+    void applySizeCompatScaleIfNeeded(@NonNull Rect resolvedBounds,
+            @NonNull Configuration resolvedConfig) {
+        if (mSizeCompatScale != 1f) {
+            final int screenPosX = resolvedBounds.left;
+            final int screenPosY = resolvedBounds.top;
+            final int dx = (int) (screenPosX / mSizeCompatScale + 0.5f) - screenPosX;
+            final int dy = (int) (screenPosY / mSizeCompatScale + 0.5f) - screenPosY;
+            AppCompatUtils.offsetBounds(resolvedConfig, dx, dy);
+        }
+    }
+
+    void updateSizeCompatScale(@NonNull Rect resolvedAppBounds, @NonNull Rect containerAppBounds) {
+        mSizeCompatScale = mActivityRecord.mAppCompatController.getTransparentPolicy()
+                .findOpaqueNotFinishingActivityBelow()
+                .map(activityRecord -> mSizeCompatScale)
+                .orElseGet(() -> calculateSizeCompatScale(resolvedAppBounds, containerAppBounds));
+    }
+
+    void clearSizeCompatModeAttributes() {
+        mInSizeCompatModeForBounds = false;
+        final float lastSizeCompatScale = mSizeCompatScale;
+        mSizeCompatScale = 1f;
+        if (mSizeCompatScale != lastSizeCompatScale) {
+            mActivityRecord.forAllWindows(WindowState::updateGlobalScale,
+                    false /* traverseTopToBottom */);
+        }
+        mSizeCompatBounds = null;
+        mAppCompatDisplayInsets = null;
+        mActivityRecord.mAppCompatController.getTransparentPolicy()
+                .clearInheritedAppCompatDisplayInsets();
+    }
+
+    void clearSizeCompatMode() {
+        clearSizeCompatModeAttributes();
+        // Clear config override in #updateAppCompatDisplayInsets().
+        final int activityType = mActivityRecord.getActivityType();
+        final Configuration overrideConfig = mActivityRecord.getRequestedOverrideConfiguration();
+        overrideConfig.unset();
+        // Keep the activity type which was set when attaching to a task to prevent leaving it
+        // undefined.
+        overrideConfig.windowConfiguration.setActivityType(activityType);
+        mActivityRecord.onRequestedOverrideConfigurationChanged(overrideConfig);
+    }
+
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        if (mSizeCompatScale != 1f || hasSizeCompatBounds()) {
+            pw.println(prefix + "mSizeCompatScale=" + mSizeCompatScale + " mSizeCompatBounds="
+                    + mSizeCompatBounds);
+        }
+    }
+
+    /**
+     * Resolves consistent screen configuration for orientation and rotation changes without
+     * inheriting the parent bounds.
+     */
+    void resolveSizeCompatModeConfiguration(@NonNull Configuration newParentConfiguration,
+            @NonNull AppCompatDisplayInsets appCompatDisplayInsets, @NonNull Rect tmpBounds) {
+        final Configuration resolvedConfig = mActivityRecord.getResolvedOverrideConfiguration();
+        final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+
+        // When an activity needs to be letterboxed because of fixed orientation, use fixed
+        // orientation bounds (stored in resolved bounds) instead of parent bounds since the
+        // activity will be displayed within them even if it is in size compat mode. They should be
+        // saved here before resolved bounds are overridden below.
+        final AppCompatAspectRatioPolicy aspectRatioPolicy = mActivityRecord.mAppCompatController
+                .getAppCompatAspectRatioPolicy();
+        final boolean useResolvedBounds = Flags.immersiveAppRepositioning()
+                ? aspectRatioPolicy.isAspectRatioApplied()
+                : aspectRatioPolicy.isLetterboxedForFixedOrientationAndAspectRatio();
+        final Rect containerBounds = useResolvedBounds
+                ? new Rect(resolvedBounds)
+                : newParentConfiguration.windowConfiguration.getBounds();
+        final Rect containerAppBounds = useResolvedBounds
+                ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
+                : mActivityRecord.mResolveConfigHint.mParentAppBoundsOverride;
+
+        final int requestedOrientation = mActivityRecord.getRequestedConfigurationOrientation();
+        final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
+        final int parentOrientation = mActivityRecord.mResolveConfigHint.mUseOverrideInsetsForConfig
+                ? mActivityRecord.mResolveConfigHint.mTmpOverrideConfigOrientation
+                : newParentConfiguration.orientation;
+        final int orientation = orientationRequested
+                ? requestedOrientation
+                // We should use the original orientation of the activity when possible to avoid
+                // forcing the activity in the opposite orientation.
+                : appCompatDisplayInsets.mOriginalRequestedOrientation != ORIENTATION_UNDEFINED
+                        ? appCompatDisplayInsets.mOriginalRequestedOrientation
+                        : parentOrientation;
+        int rotation = newParentConfiguration.windowConfiguration.getRotation();
+        final boolean isFixedToUserRotation = mActivityRecord.mDisplayContent == null
+                || mActivityRecord.mDisplayContent.getDisplayRotation().isFixedToUserRotation();
+        if (!isFixedToUserRotation && !appCompatDisplayInsets.mIsFloating) {
+            // Use parent rotation because the original display can be rotated.
+            resolvedConfig.windowConfiguration.setRotation(rotation);
+        } else {
+            final int overrideRotation = resolvedConfig.windowConfiguration.getRotation();
+            if (overrideRotation != ROTATION_UNDEFINED) {
+                rotation = overrideRotation;
+            }
+        }
+
+        // Use compat insets to lock width and height. We should not use the parent width and height
+        // because apps in compat mode should have a constant width and height. The compat insets
+        // are locked when the app is first launched and are never changed after that, so we can
+        // rely on them to contain the original and unchanging width and height of the app.
+        final Rect containingAppBounds = new Rect();
+        final Rect containingBounds = tmpBounds;
+        appCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation,
+                orientation, orientationRequested, isFixedToUserRotation);
+        resolvedBounds.set(containingBounds);
+        // The size of floating task is fixed (only swap), so the aspect ratio is already correct.
+        if (!appCompatDisplayInsets.mIsFloating) {
+            mActivityRecord.mAppCompatController.getAppCompatAspectRatioPolicy()
+                    .applyAspectRatioForLetterbox(resolvedBounds, containingAppBounds,
+                            containingBounds);
+        }
+
+        // Use resolvedBounds to compute other override configurations such as appBounds. The bounds
+        // are calculated in compat container space. The actual position on screen will be applied
+        // later, so the calculation is simpler that doesn't need to involve offset from parent.
+        mActivityRecord.mResolveConfigHint.mTmpCompatInsets = appCompatDisplayInsets;
+        mActivityRecord.computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
+        // Use current screen layout as source because the size of app is independent to parent.
+        resolvedConfig.screenLayout = ActivityRecord.computeScreenLayout(
+                mActivityRecord.getConfiguration().screenLayout, resolvedConfig.screenWidthDp,
+                resolvedConfig.screenHeightDp);
+
+        // Use parent orientation if it cannot be decided by bounds, so the activity can fit inside
+        // the parent bounds appropriately.
+        if (resolvedConfig.screenWidthDp == resolvedConfig.screenHeightDp) {
+            resolvedConfig.orientation = parentOrientation;
+        }
+
+        // Below figure is an example that puts an activity which was launched in a larger container
+        // into a smaller container.
+        //   The outermost rectangle is the real display bounds.
+        //   "@" is the container app bounds (parent bounds or fixed orientation bounds)
+        //   "#" is the {@code resolvedBounds} that applies to application.
+        //   "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled.
+        // ------------------------------
+        // |                            |
+        // |    @@@@*********@@@@###    |
+        // |    @   *       *   @  #    |
+        // |    @   *       *   @  #    |
+        // |    @   *       *   @  #    |
+        // |    @@@@*********@@@@  #    |
+        // ---------#--------------#-----
+        //          #              #
+        //          ################
+        // The application is still layouted in "#" since it was launched, and it will be visually
+        // scaled and positioned to "*".
+
+        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+        // Calculates the scale the size compatibility bounds into the region which is available
+        // to application.
+        final float lastSizeCompatScale = mSizeCompatScale;
+        updateSizeCompatScale(resolvedAppBounds, containerAppBounds);
+
+        final int containerTopInset = containerAppBounds.top - containerBounds.top;
+        final boolean topNotAligned =
+                containerTopInset != resolvedAppBounds.top - resolvedBounds.top;
+        if (mSizeCompatScale != 1f || topNotAligned) {
+            if (mSizeCompatBounds == null) {
+                mSizeCompatBounds = new Rect();
+            }
+            mSizeCompatBounds.set(resolvedAppBounds);
+            mSizeCompatBounds.offsetTo(0, 0);
+            mSizeCompatBounds.scale(mSizeCompatScale);
+            // The insets are included in height, e.g. the area of real cutout shouldn't be scaled.
+            mSizeCompatBounds.bottom += containerTopInset;
+        } else {
+            mSizeCompatBounds = null;
+        }
+        if (mSizeCompatScale != lastSizeCompatScale) {
+            mActivityRecord.forAllWindows(WindowState::updateGlobalScale,
+                    false /* traverseTopToBottom */);
+        }
+
+        // The position will be later adjusted in updateResolvedBoundsPosition.
+        // Above coordinates are in "@" space, now place "*" and "#" to screen space.
+        final boolean fillContainer = resolvedBounds.equals(containingBounds);
+        final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
+        final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
+
+        if (screenPosX != 0 || screenPosY != 0) {
+            if (hasSizeCompatBounds()) {
+                mSizeCompatBounds.offset(screenPosX, screenPosY);
+            }
+            // Add the global coordinates and remove the local coordinates.
+            final int dx = screenPosX - resolvedBounds.left;
+            final int dy = screenPosY - resolvedBounds.top;
+            AppCompatUtils.offsetBounds(resolvedConfig, dx, dy);
+        }
+
+        mInSizeCompatModeForBounds = isInSizeCompatModeForBounds(resolvedAppBounds,
+                containerAppBounds);
+    }
+
+    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
+    void updateAppCompatDisplayInsets() {
+        if (getAppCompatDisplayInsets() != null
+                || !mActivityRecord.shouldCreateAppCompatDisplayInsets()) {
+            // The override configuration is set only once in size compatibility mode.
+            return;
+        }
+
+        Configuration overrideConfig = mActivityRecord.getRequestedOverrideConfiguration();
+        final Configuration fullConfig = mActivityRecord.getConfiguration();
+
+        // Ensure the screen related fields are set. It is used to prevent activity relaunch
+        // when moving between displays. For screenWidthDp and screenWidthDp, because they
+        // are relative to bounds and density, they will be calculated in
+        // {@link Task#computeConfigResourceOverrides} and the result will also be
+        // relatively fixed.
+        overrideConfig.colorMode = fullConfig.colorMode;
+        overrideConfig.densityDpi = fullConfig.densityDpi;
+        // The smallest screen width is the short side of screen bounds. Because the bounds
+        // and density won't be changed, smallestScreenWidthDp is also fixed.
+        overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp;
+        if (ActivityInfo.isFixedOrientation(mActivityRecord.getOverrideOrientation())) {
+            // lock rotation too. When in size-compat, onConfigurationChanged will watch for and
+            // apply runtime rotation changes.
+            overrideConfig.windowConfiguration.setRotation(
+                    fullConfig.windowConfiguration.getRotation());
+        }
+
+        final Rect letterboxedContainerBounds = mActivityRecord.mAppCompatController
+                .getAppCompatAspectRatioPolicy().getLetterboxedContainerBounds();
+
+        // The role of AppCompatDisplayInsets is like the override bounds.
+        mAppCompatDisplayInsets =
+                new AppCompatDisplayInsets(mActivityRecord.mDisplayContent, mActivityRecord,
+                        letterboxedContainerBounds, mActivityRecord.mResolveConfigHint
+                            .mUseOverrideInsetsForConfig);
+    }
+
+
+    private boolean isInSizeCompatModeForBounds(final @NonNull Rect appBounds,
+            final @NonNull Rect containerBounds) {
+        if (mActivityRecord.mAppCompatController.getTransparentPolicy().isRunning()) {
+            // To avoid wrong app behaviour, we decided to disable SCM when a translucent activity
+            // is letterboxed.
+            return false;
+        }
+        final int appWidth = appBounds.width();
+        final int appHeight = appBounds.height();
+        final int containerAppWidth = containerBounds.width();
+        final int containerAppHeight = containerBounds.height();
+
+        if (containerAppWidth == appWidth && containerAppHeight == appHeight) {
+            // Matched the container bounds.
+            return false;
+        }
+        if (containerAppWidth > appWidth && containerAppHeight > appHeight) {
+            // Both sides are smaller than the container.
+            return true;
+        }
+        if (containerAppWidth < appWidth || containerAppHeight < appHeight) {
+            // One side is larger than the container.
+            return true;
+        }
+
+        // The rest of the condition is that only one side is smaller than the container, but it
+        // still needs to exclude the cases where the size is limited by the fixed aspect ratio.
+        final float maxAspectRatio = mActivityRecord.getMaxAspectRatio();
+        if (maxAspectRatio > 0) {
+            final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
+                    / Math.min(appWidth, appHeight);
+            if (aspectRatio >= maxAspectRatio) {
+                // The current size has reached the max aspect ratio.
+                return false;
+            }
+        }
+        final float minAspectRatio = mActivityRecord.getMinAspectRatio();
+        if (minAspectRatio > 0) {
+            // The activity should have at least the min aspect ratio, so this checks if the
+            // container still has available space to provide larger aspect ratio.
+            final float containerAspectRatio =
+                    (0.5f + Math.max(containerAppWidth, containerAppHeight))
+                            / Math.min(containerAppWidth, containerAppHeight);
+            if (containerAspectRatio <= minAspectRatio) {
+                // The long side has reached the parent.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private float calculateSizeCompatScale(@NonNull Rect resolvedAppBounds,
+            @NonNull Rect containerAppBounds) {
+        final int contentW = resolvedAppBounds.width();
+        final int contentH = resolvedAppBounds.height();
+        final int viewportW = containerAppBounds.width();
+        final int viewportH = containerAppBounds.height();
+        // Allow an application to be up-scaled if its window is smaller than its
+        // original container or if it's a freeform window in desktop mode.
+        boolean shouldAllowUpscaling = !(contentW <= viewportW && contentH <= viewportH)
+                || (canEnterDesktopMode(mActivityRecord.mAtmService.mContext)
+                    && mActivityRecord.getWindowingMode() == WINDOWING_MODE_FREEFORM);
+        return shouldAllowUpscaling ? Math.min(
+                (float) viewportW / contentW, (float) viewportH / contentH) : 1f;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 0244d27..69421d0 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -28,13 +28,16 @@
 import android.app.TaskInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.WindowInsets;
 
 import java.util.function.BooleanSupplier;
 
 /**
  * Utilities for App Compat policies and overrides.
  */
-class AppCompatUtils {
+final class AppCompatUtils {
 
     /**
      * Lazy version of a {@link BooleanSupplier} which access an existing BooleanSupplier and
@@ -135,8 +138,8 @@
         // Whether the direct top activity is eligible for letterbox education.
         appCompatTaskInfo.setEligibleForLetterboxEducation(
                 isTopActivityResumed && top.isEligibleForLetterboxEducation());
-        appCompatTaskInfo.setLetterboxEducationEnabled(top.mLetterboxUiController
-                .isLetterboxEducationEnabled());
+        appCompatTaskInfo.setLetterboxEducationEnabled(top.mAppCompatController
+                .getAppCompatLetterboxOverrides().isLetterboxEducationEnabled());
 
         final AppCompatAspectRatioOverrides aspectRatioOverrides =
                 top.mAppCompatController.getAppCompatAspectRatioOverrides();
@@ -184,6 +187,8 @@
         appCompatTaskInfo.setTopActivityLetterboxed(top.areBoundsLetterboxed());
         appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode = top.mAppCompatController
                 .getAppCompatCameraOverrides().getFreeformCameraCompatMode();
+        appCompatTaskInfo.setHasMinAspectRatioOverride(top.mAppCompatController
+                .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(task));
     }
 
     /**
@@ -212,6 +217,45 @@
         return "UNKNOWN_REASON";
     }
 
+    /**
+     * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
+     */
+    @Nullable
+    static InsetsSource getExpandedTaskbarOrNull(@NonNull final WindowState mainWindow) {
+        final InsetsState state = mainWindow.getInsetsState();
+        for (int i = state.sourceSize() - 1; i >= 0; i--) {
+            final InsetsSource source = state.sourceAt(i);
+            if (source.getType() == WindowInsets.Type.navigationBars()
+                    && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
+                    && source.isVisible()) {
+                return source;
+            }
+        }
+        return null;
+    }
+
+    static void adjustBoundsForTaskbar(@NonNull final WindowState mainWindow,
+            @NonNull final Rect bounds) {
+        // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+        // an insets frame is equal to a navigation bar which shouldn't affect position of
+        // rounded corners since apps are expected to handle navigation bar inset.
+        // This condition checks whether the taskbar is visible.
+        // Do not crop the taskbar inset if the window is in immersive mode - the user can
+        // swipe to show/hide the taskbar as an overlay.
+        // Adjust the bounds only in case there is an expanded taskbar,
+        // otherwise the rounded corners will be shown behind the navbar.
+        final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
+        if (expandedTaskbarOrNull != null) {
+            // Rounded corners should be displayed above the expanded taskbar.
+            bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
+        }
+    }
+
+    static void offsetBounds(@NonNull Configuration inOutConfig, int offsetX, int offsetY) {
+        inOutConfig.windowConfiguration.getBounds().offset(offsetX, offsetY);
+        inOutConfig.windowConfiguration.getAppBounds().offset(offsetX, offsetY);
+    }
+
     private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
         info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
         info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index bc7e84a..90d33fb 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -134,8 +134,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
@@ -402,8 +402,7 @@
             mRemoteAnimationController.goodToGo(transit);
         } else if ((isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
                 && topOpeningAnim != null) {
-            if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
-                    && mService.getRecentsAnimationController() == null) {
+            if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()) {
                 final NavBarFadeAnimationController controller =
                         new NavBarFadeAnimationController(mDisplayContent);
                 // For remote animation case, the nav bar fades out and in is controlled by the
@@ -471,11 +470,9 @@
     }
 
     private boolean needsBoosting() {
-        final boolean recentsAnimRunning = mService.getRecentsAnimationController() != null;
         return !mNextAppTransitionRequests.isEmpty()
                 || mAppTransitionState == APP_STATE_READY
-                || mAppTransitionState == APP_STATE_RUNNING
-                || recentsAnimRunning;
+                || mAppTransitionState == APP_STATE_RUNNING;
     }
 
     void registerListenerLocked(AppTransitionListener listener) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 5a0cbf3..ab02d49 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -68,7 +68,6 @@
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp;
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -98,6 +97,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -173,41 +173,6 @@
                 || !transitionGoodToGoForTaskFragments()) {
             return;
         }
-        final boolean isRecentsInOpening = mDisplayContent.mOpeningApps.stream().anyMatch(
-                ConfigurationContainer::isActivityTypeRecents);
-        // In order to avoid visual clutter caused by a conflict between app transition
-        // animation and recents animation, app transition is delayed until recents finishes.
-        // One exceptional case. When 3P launcher is used and a user taps a task screenshot in
-        // task switcher (isRecentsInOpening=true), app transition must start even though
-        // recents is running. Otherwise app transition is blocked until timeout (b/232984498).
-        // When 1P launcher is used, this animation is controlled by the launcher outside of
-        // the app transition, so delaying app transition doesn't cause visible delay. After
-        // recents finishes, app transition is handled just to commit visibility on apps.
-        if (!isRecentsInOpening) {
-            final ArraySet<WindowContainer> participants = new ArraySet<>();
-            participants.addAll(mDisplayContent.mOpeningApps);
-            participants.addAll(mDisplayContent.mChangingContainers);
-            boolean deferForRecents = false;
-            for (int i = 0; i < participants.size(); i++) {
-                WindowContainer wc = participants.valueAt(i);
-                final ActivityRecord activity = getAppFromContainer(wc);
-                if (activity == null) {
-                    continue;
-                }
-                // Don't defer recents animation if one of activity isn't running for it, that one
-                // might be started from quickstep.
-                if (!activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
-                    deferForRecents = false;
-                    break;
-                }
-                deferForRecents = true;
-            }
-            if (deferForRecents) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Delaying app transition for recents animation to finish");
-                return;
-            }
-        }
 
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
 
@@ -289,10 +254,12 @@
                 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
         final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
 
-        // No AE remote animation with Shell transition.
-        // Unfreeze the windows that were previously frozen for TaskFragment animation.
-        unfreezeEmbeddedChangingWindows();
-        overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+        // Check if there is any override
+        if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
+            // Unfreeze the windows that were previously frozen for TaskFragment animation.
+            unfreezeEmbeddedChangingWindows();
+            overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
+        }
 
         final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps)
                 || containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -723,6 +690,64 @@
     }
 
     /**
+     * Overrides the pending transition with the remote animation defined by the
+     * {@link ITaskFragmentOrganizer} if all windows in the transition are children of
+     * {@link TaskFragment} that are organized by the same organizer.
+     *
+     * @return {@code true} if the transition is overridden.
+     */
+    private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+            ArraySet<Integer> activityTypes) {
+        if (transitionMayContainNonAppWindows(transit)) {
+            return false;
+        }
+        if (!transitionContainsTaskFragmentWithBoundsOverride()) {
+            // No need to play TaskFragment remote animation if all embedded TaskFragment in the
+            // transition fill the Task.
+            return false;
+        }
+
+        final Task task = findParentTaskForAllEmbeddedWindows();
+        final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
+        final RemoteAnimationDefinition definition = organizer != null
+                ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+                    .getRemoteAnimationDefinition(organizer)
+                : null;
+        final RemoteAnimationAdapter adapter = definition != null
+                ? definition.getAdapter(transit, activityTypes)
+                : null;
+        if (adapter == null) {
+            return false;
+        }
+        mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
+                adapter, false /* sync */, true /*isActivityEmbedding*/);
+        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
+                "Override with TaskFragment remote animation for transit=%s",
+                AppTransition.appTransitionOldToString(transit));
+
+        final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController
+                .getTaskFragmentOrganizerUid(organizer);
+        final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding(
+                organizerUid);
+        final RemoteAnimationController remoteAnimationController =
+                mDisplayContent.mAppTransition.getRemoteAnimationController();
+        if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) {
+            // We are going to use client-driven animation, Disable all input on activity windows
+            // during the animation (unless it is fully trusted) to ensure it is safe to allow
+            // client to animate the surfaces.
+            // This is needed for all activity windows in the animation Task.
+            remoteAnimationController.setOnRemoteAnimationReady(() -> {
+                final Consumer<ActivityRecord> updateActivities =
+                        activity -> activity.setDropInputForAnimation(true);
+                task.forAllActivities(updateActivities);
+            });
+            ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment."
+                    + " Disabled all input during TaskFragment remote animation.", task.mTaskId);
+        }
+        return true;
+    }
+
+    /**
      * Overrides the pending transition with the remote animation defined for the transition in the
      * set of defined remote animations in the app window token.
      */
@@ -1032,12 +1057,8 @@
     private void applyAnimations(ArraySet<ActivityRecord> openingApps,
             ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
             LayoutParams animLp, boolean voiceInteraction) {
-        final RecentsAnimationController rac = mService.getRecentsAnimationController();
         if (transit == WindowManager.TRANSIT_OLD_UNSET
                 || (openingApps.isEmpty() && closingApps.isEmpty())) {
-            if (rac != null) {
-                rac.sendTasksAppeared();
-            }
             return;
         }
 
@@ -1075,9 +1096,6 @@
                 voiceInteraction);
         applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                 voiceInteraction);
-        if (rac != null) {
-            rac.sendTasksAppeared();
-        }
 
         for (int i = 0; i < openingApps.size(); ++i) {
             openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index 9fd543f..fcaab2c 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -152,10 +152,12 @@
      * @param r activity record for which the warning may be displayed
      */
     public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
-        final Configuration globalConfig = mAtm.getGlobalConfiguration();
-        if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
+        final DisplayContent dc = r.getDisplayContent();
+        final Configuration config = dc == null
+                ? mAtm.getGlobalConfiguration() : dc.getConfiguration();
+        if (config.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
                 && r.info.applicationInfo.requiresSmallestWidthDp
-                > globalConfig.smallestScreenWidthDp) {
+                > config.smallestScreenWidthDp) {
             mUiHandler.showUnsupportedDisplaySizeDialog(r);
         }
     }
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index eb85c1a..f0a6e9e 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -174,11 +174,6 @@
                 mNavBarToken = w.mToken;
                 // Do not animate movable navigation bar (e.g. 3-buttons mode).
                 if (navigationBarCanMove) return;
-                // Or when the navigation bar is currently controlled by recents animation.
-                final RecentsAnimationController recents = mService.getRecentsAnimationController();
-                if (recents != null && recents.isNavigationBarAttachedToApp()) {
-                    return;
-                }
             } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS
                     || mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) {
                 action = Operation.ACTION_SEAMLESS;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 06e665e..4e4616d 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -273,8 +273,9 @@
                                     customAppTransition.mBackgroundColor);
                         }
                     }
-                    infoBuilder.setLetterboxColor(currentActivity.mLetterboxUiController
-                            .getLetterboxBackgroundColor().toArgb());
+                    infoBuilder.setLetterboxColor(currentActivity.mAppCompatController
+                            .getAppCompatLetterboxOverrides()
+                                .getLetterboxBackgroundColor().toArgb());
                     removedWindowContainer = currentActivity;
                     prevTask = prevActivities.get(0).getTask();
                     backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
@@ -282,8 +283,10 @@
                     // keyguard locked and activities are unable to show when locked.
                     backType = BackNavigationInfo.TYPE_CALLBACK;
                 }
-            } else if (currentTask.mAtmService.getLockTaskController().isTaskLocked(currentTask)) {
+            } else if (currentTask.mAtmService.getLockTaskController().isTaskLocked(currentTask)
+                    || currentTask.getWindowConfiguration().tasksAreFloating()) {
                 // Do not predict if current task is in task locked.
+                // Also, it is unable to play cross task animation for floating task.
                 backType = BackNavigationInfo.TYPE_CALLBACK;
             } else {
                 // Check back-to-home or cross-task
@@ -336,13 +339,12 @@
                     removedWindowContainer,
                     BackNavigationInfo.typeToString(backType));
 
-            // For now, we only animate when going home, cross task or cross-activity.
             boolean prepareAnimation =
                     (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
                                     || backType == BackNavigationInfo.TYPE_CROSS_TASK
                                     || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY
                                     || backType == BackNavigationInfo.TYPE_DIALOG_CLOSE)
-                            && adapter != null;
+                            && (adapter != null && adapter.isAnimatable(backType));
 
             if (prepareAnimation) {
                 final AnimationHandler.ScheduleAnimationBuilder builder =
@@ -366,7 +368,7 @@
                         // client process to prevent the unexpected relayout when handling the back
                         // animation.
                         for (int i = prevActivities.size() - 1; i >= 0; --i) {
-                            prevActivities.get(i).setDeferHidingClient(true);
+                            prevActivities.get(i).setDeferHidingClient();
                         }
                     } else {
                         scheduleAnimation(builder);
@@ -571,10 +573,15 @@
 
     private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) {
         mPendingAnimation = builder.build();
-        mWindowManagerService.mWindowPlacerLocked.requestTraversal();
-        if (mShowWallpaper) {
-            mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController
-                    .adjustWallpaperWindows();
+        if (mAnimationHandler.mOpenAnimAdaptor != null
+                && mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition != null) {
+            startAnimation();
+        } else {
+            mWindowManagerService.mWindowPlacerLocked.requestTraversal();
+            if (mShowWallpaper) {
+                mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController
+                        .adjustWallpaperWindows();
+            }
         }
     }
 
@@ -761,7 +768,7 @@
             if (isMonitorForRemote()) {
                 mObserver.sendResult(null /* result */);
             }
-            if (isMonitorAnimationOrTransition()) {
+            if (isMonitorAnimationOrTransition() && canCancelAnimations()) {
                 clearBackAnimations(true /* cancel */);
             }
             cancelPendingAnimation();
@@ -874,6 +881,7 @@
         } else {
             if (mAnimationHandler.mPrepareCloseTransition != null) {
                 Slog.e(TAG, "Gesture animation is applied on another transition?");
+                return;
             }
             mAnimationHandler.mPrepareCloseTransition = transition;
             if (!migratePredictToTransition) {
@@ -1200,7 +1208,7 @@
         }
 
         void markWindowHasDrawn(ActivityRecord activity) {
-            if (!mComposed || mWaitTransition || mOpenAnimAdaptor.mPreparedOpenTransition == null
+            if (!mComposed || mWaitTransition
                     || mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) {
                 return;
             }
@@ -1212,6 +1220,10 @@
                 }
                 allWindowDrawn &= next.mAppWindowDrawn;
             }
+            // Do not remove until transition ready.
+            if (!activity.isVisible()) {
+                return;
+            }
             if (allWindowDrawn) {
                 mOpenAnimAdaptor.cleanUpWindowlessSurface(true);
             }
@@ -1286,6 +1298,14 @@
             if (mOpenAnimAdaptor.mRequestedStartingSurfaceId == INVALID_TASK_ID) {
                 return;
             }
+            boolean allWindowDrawn = true;
+            for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
+                final BackWindowAnimationAdaptor next = mOpenAnimAdaptor.mAdaptors[i];
+                allWindowDrawn &= next.mAppWindowDrawn;
+            }
+            if (!allWindowDrawn) {
+                return;
+            }
             final SurfaceControl startingSurface = mOpenAnimAdaptor.mStartingSurface;
             if (startingSurface != null && startingSurface.isValid()) {
                 startTransaction.addTransactionCommittedListener(Runnable::run, () -> {
@@ -1817,8 +1837,10 @@
                             mNavigationMonitor.cancelBackNavigating("cancelAnimation");
                             mBackAnimationAdapter.getRunner().onAnimationCancelled();
                         } else {
-                            mBackAnimationAdapter.getRunner().onAnimationStart(
-                                    targets, null, null, callback);
+                            mBackAnimationAdapter.getRunner().onAnimationStart(targets,
+                                    mOpenAnimAdaptor.mPreparedOpenTransition != null
+                                            ? mOpenAnimAdaptor.mPreparedOpenTransition.getToken()
+                                            : null, callback);
                         }
                     } catch (RemoteException e) {
                         e.printStackTrace();
@@ -2046,11 +2068,22 @@
         }
     }
 
+    /** If the open transition is playing, wait for transition to clear the animation */
+    private boolean canCancelAnimations() {
+        if (!Flags.migratePredictiveBackTransition()) {
+            return true;
+        }
+        return mAnimationHandler.mOpenAnimAdaptor == null
+                || mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition == null;
+    }
+
     void startAnimation() {
         if (!mBackAnimationInProgress) {
             // gesture is already finished, do not start animation
             if (mPendingAnimation != null) {
-                clearBackAnimations(true /* cancel */);
+                if (canCancelAnimations()) {
+                    clearBackAnimations(true /* cancel */);
+                }
                 mPendingAnimation = null;
             }
             return;
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 02c8a49..2259b5a 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -21,10 +21,10 @@
 import static android.app.ActivityOptions.BackgroundActivityStartMode;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
-import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -39,13 +39,15 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 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.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_FG_ONLY;
 import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel;
 import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS;
+import static com.android.window.flags.Flags.balAdditionalStartModes;
 import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg;
-import static com.android.window.flags.Flags.balImprovedMetrics;
 import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck;
+import static com.android.window.flags.Flags.balImprovedMetrics;
 import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
 import static com.android.window.flags.Flags.balRequireOptInSameUid;
 import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid;
@@ -84,6 +86,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.UiThread;
 import com.android.server.am.PendingIntentRecord;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
@@ -107,6 +110,17 @@
     private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
     private static final int NO_PROCESS_UID = -1;
 
+    private static final BalCheckConfiguration BAL_CHECK_FOREGROUND = new BalCheckConfiguration(
+            /* isCheckingForFgsStarts */ false,
+            /* checkVisibility */ true,
+            /* checkOtherExemptions */ false,
+            ACTIVITY_BG_START_GRACE_PERIOD_MS);
+    private static final BalCheckConfiguration BAL_CHECK_BACKGROUND = new BalCheckConfiguration(
+            /* isCheckingForFgsStarts */ false,
+            /* checkVisibility */ false,
+            /* checkOtherExemptions */ true,
+            ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
     static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
     static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
     static final String AUTO_OPT_IN_SAME_UID = "sameUid";
@@ -412,6 +426,8 @@
                 int callingUid, String callingPackage, ActivityOptions checkedOptions) {
             switch (checkedOptions.getPendingIntentCreatorBackgroundActivityStartMode()) {
                 case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+                case MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE:
+                case MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS:
                     return BackgroundStartPrivileges.ALLOW_BAL;
                 case MODE_BACKGROUND_ACTIVITY_START_DENIED:
                     return BackgroundStartPrivileges.NONE;
@@ -752,7 +768,7 @@
         // PendingIntents is null).
         BalVerdict resultForRealCaller = state.callerIsRealCaller() && resultForCaller.allows()
                 ? resultForCaller
-                : checkBackgroundActivityStartAllowedBySender(state)
+                : checkBackgroundActivityStartAllowedByRealCaller(state)
                         .setBasedOnRealCaller();
         state.setResultForRealCaller(resultForRealCaller);
 
@@ -827,6 +843,37 @@
      * or {@link #BAL_BLOCK} if the launch should be blocked
      */
     BalVerdict checkBackgroundActivityStartAllowedByCaller(BalState state) {
+        if (state.isPendingIntent()) {
+            // PendingIntents should mostly be allowed by the sender (real caller) or a permission
+            // the creator of the PendingIntent has. Visibility should be the exceptional case, so
+            // test it last (this does not change the result, just the bal code).
+            BalVerdict result = BalVerdict.BLOCK;
+            if (!(balAdditionalStartModes()
+                    && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                    == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+                result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+            }
+            if (result == BalVerdict.BLOCK) {
+                result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+
+            }
+            return result;
+        } else {
+            BalVerdict result = checkBackgroundActivityStartAllowedByCallerInForeground(state);
+            if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+                    && state.mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                    == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+                result = checkBackgroundActivityStartAllowedByCallerInBackground(state);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedByCallerInForeground(BalState state) {
         // This is used to block background activity launch even if the app is still
         // visible to user after user clicking home button.
 
@@ -842,7 +889,16 @@
             return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW,
                     /*background*/ false, "callingUid has non-app visible window");
         }
+        // Don't abort if the callerApp or other processes of that uid are considered to be in the
+        // foreground.
+        return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_FOREGROUND);
+    }
 
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedByCallerInBackground(BalState state) {
         // don't abort for the most important UIDs
         final int callingAppId = UserHandle.getAppId(state.mCallingUid);
         if (state.mCallingUid == Process.ROOT_UID
@@ -922,25 +978,29 @@
                     "OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION appop is granted");
         }
 
-        // If we don't have callerApp at this point, no caller was provided to startActivity().
-        // That's the case for PendingIntent-based starts, since the creator's process might not be
-        // up and alive.
         // Don't abort if the callerApp or other processes of that uid are allowed in any way.
-        BalVerdict callerAppAllowsBal = checkProcessAllowsBal(state.mCallerApp, state);
-        if (callerAppAllowsBal.allows()) {
-            return callerAppAllowsBal;
-        }
-
-        // If we are here, it means all exemptions based on the creator failed
-        return BalVerdict.BLOCK;
+        return checkProcessAllowsBal(state.mCallerApp, state, BAL_CHECK_BACKGROUND);
     }
 
     /**
      * @return A code denoting which BAL rule allows an activity to be started,
      * or {@link #BAL_BLOCK} if the launch should be blocked
      */
-    BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+    BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
+        BalVerdict result = checkBackgroundActivityStartAllowedByRealCallerInForeground(state);
+        if (result == BalVerdict.BLOCK && !(balAdditionalStartModes()
+                && state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                == MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE)) {
+            result = checkBackgroundActivityStartAllowedByRealCallerInBackground(state);
+        }
+        return result;
+    }
 
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedByRealCallerInForeground(BalState state) {
         // Normal apps with visible app window will be allowed to start activity if app switching
         // is allowed, or apps like live wallpaper with non app visible window will be allowed.
         // The home app can start apps even if app switches are usually disallowed.
@@ -966,6 +1026,16 @@
             }
         }
 
+        // Don't abort if the realCallerApp or other processes of that uid are considered to be in
+        // the foreground.
+        return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_FOREGROUND);
+    }
+
+    /**
+     * @return A code denoting which BAL rule allows an activity to be started,
+     * or {@link #BAL_BLOCK} if the launch should be blocked
+     */
+    BalVerdict checkBackgroundActivityStartAllowedByRealCallerInBackground(BalState state) {
         if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
                 == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
                 && hasBalPermission(state.mRealCallingUid, state.mRealCallingPid)) {
@@ -992,14 +1062,7 @@
         }
 
         // don't abort if the callerApp or other processes of that uid are allowed in any way
-        BalVerdict realCallerAppAllowsBal =
-                checkProcessAllowsBal(state.mRealCallerApp, state);
-        if (realCallerAppAllowsBal.allows()) {
-            return realCallerAppAllowsBal;
-        }
-
-        // If we are here, it means all exemptions based on PI sender failed
-        return BalVerdict.BLOCK;
+        return checkProcessAllowsBal(state.mRealCallerApp, state, BAL_CHECK_BACKGROUND);
     }
 
     @VisibleForTesting boolean hasBalPermission(int uid, int pid) {
@@ -1015,13 +1078,13 @@
      * exceptions.
      */
     @VisibleForTesting BalVerdict checkProcessAllowsBal(WindowProcessController app,
-            BalState state) {
+            BalState state, BalCheckConfiguration balCheckConfiguration) {
         if (app == null) {
             return BalVerdict.BLOCK;
         }
         // first check the original calling process
         final BalVerdict balAllowedForCaller = app
-                .areBackgroundActivityStartsAllowed(state.mAppSwitchState);
+                .areBackgroundActivityStartsAllowed(state.mAppSwitchState, balCheckConfiguration);
         if (balAllowedForCaller.allows()) {
             return balAllowedForCaller.withProcessInfo("callerApp process", app);
         } else {
@@ -1033,7 +1096,7 @@
                     final WindowProcessController proc = uidProcesses.valueAt(i);
                     if (proc != app) {
                         BalVerdict balAllowedForUid = proc.areBackgroundActivityStartsAllowed(
-                                state.mAppSwitchState);
+                                state.mAppSwitchState, balCheckConfiguration);
                         if (balAllowedForUid.allows()) {
                             return balAllowedForUid.withProcessInfo("process", proc);
                         }
@@ -1506,10 +1569,13 @@
         PackageManager pm = mService.mContext.getPackageManager();
         ApplicationInfo applicationInfo;
 
+        final int sourceUserId = UserHandle.getUserId(sourceUid);
         try {
-            applicationInfo = pm.getApplicationInfo(packageName, 0);
+            applicationInfo = pm.getApplicationInfoAsUser(packageName, /* flags= */ 0,
+                    sourceUserId);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+            Slog.wtf(TAG, "Package name: " + packageName + " not found for user "
+                    + sourceUserId);
             return bas.optedIn(ar);
         }
 
@@ -1682,6 +1748,21 @@
                             (state.mOriginatingPendingIntent != null));
         }
 
+        if (finalVerdict.getRawCode() == BAL_ALLOW_GRACE_PERIOD) {
+            if (state.realCallerExplicitOptInOrAutoOptIn()
+                    && state.mResultForRealCaller.allows()
+                    && state.mResultForRealCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+                // real caller could allow with a different exemption
+            } else if (state.callerExplicitOptInOrAutoOptIn() && state.mResultForCaller.allows()
+                    && state.mResultForCaller.getRawCode() != BAL_ALLOW_GRACE_PERIOD) {
+                // caller could allow with a different exemption
+            } else {
+                // log to determine grace period length distribution
+                Slog.wtf(TAG, "Activity start ONLY allowed by BAL_ALLOW_GRACE_PERIOD "
+                        + finalVerdict.mMessage + ": " + state);
+            }
+        }
+
         if (balImprovedMetrics()) {
             if (shouldLogStats(finalVerdict, state)) {
                 String activityName;
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 4a870a3..1073713 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
 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.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
@@ -48,7 +47,6 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.IntArray;
-import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
@@ -100,60 +98,75 @@
         mBackgroundActivityStartCallback = callback;
     }
 
+    record BalCheckConfiguration(
+            boolean isCheckingForFgsStart,
+            boolean checkVisibility,
+            boolean checkOtherExemptions,
+            long gracePeriod
+    ) {
+    }
+
+    /**
+     * Check configuration for foreground service starts.
+     *
+     * The check executes all parts of the BAL checks and uses the same grace period,
+     * so FGS is allowed whenever BAL is allowed.
+     */
+    static final BalCheckConfiguration CHECK_FOR_FGS_START = new BalCheckConfiguration(
+            /* isCheckingForFgsStarts */ true,
+            /* checkVisibility */ true,
+            /* checkOtherExemptions */ true,
+            ACTIVITY_BG_START_GRACE_PERIOD_MS);
+
     BalVerdict areBackgroundActivityStartsAllowed(
             int pid, int uid, String packageName,
-            int appSwitchState, boolean isCheckingForFgsStart,
+            int appSwitchState, BalCheckConfiguration checkConfiguration,
             boolean hasActivityInVisibleTask, boolean hasBackgroundActivityStartPrivileges,
             long lastStopAppSwitchesTime, long lastActivityLaunchTime,
             long lastActivityFinishTime) {
         // Allow if the proc is instrumenting with background activity starts privs.
-        if (hasBackgroundActivityStartPrivileges) {
+        if (checkConfiguration.checkOtherExemptions && hasBackgroundActivityStartPrivileges) {
             return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ true,
                     "process instrumenting with background activity starts privileges");
         }
         // Allow if the flag was explicitly set.
-        if (isBackgroundStartAllowedByToken(uid, packageName, isCheckingForFgsStart)) {
+        if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid,
+                packageName, checkConfiguration.isCheckingForFgsStart)) {
             return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION,
                     /*background*/ true, "process allowed by token");
         }
         // Allow if the caller is bound by a UID that's currently foreground.
         // But still respect the appSwitchState.
-        boolean allowBoundByForegroundUid =
+        if (checkConfiguration.checkVisibility && (
                 Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid()
-                ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
-                : isBoundByForegroundUid();
-        if (allowBoundByForegroundUid) {
+                        ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid()
+                        : isBoundByForegroundUid())) {
             return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND
                     : BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false,
                     "process bound by foreground uid");
         }
         // Allow if the caller has an activity in any foreground task.
-        if (hasActivityInVisibleTask && appSwitchState != APP_SWITCH_DISALLOW) {
+        if (checkConfiguration.checkVisibility && hasActivityInVisibleTask
+                && appSwitchState != APP_SWITCH_DISALLOW) {
             return new BalVerdict(BAL_ALLOW_FOREGROUND, /*background*/ false,
                     "process has activity in foreground task");
         }
 
         // If app switching is not allowed, we ignore all the start activity grace period
         // exception so apps cannot start itself in onPause() after pressing home button.
-        if (appSwitchState == APP_SWITCH_ALLOW) {
+        if (checkConfiguration.checkOtherExemptions && appSwitchState == APP_SWITCH_ALLOW) {
             // Allow if any activity in the caller has either started or finished very recently, and
             // it must be started or finished after last stop app switches time.
-            final long now = SystemClock.uptimeMillis();
-            if (now - lastActivityLaunchTime < ACTIVITY_BG_START_GRACE_PERIOD_MS
-                    || now - lastActivityFinishTime < ACTIVITY_BG_START_GRACE_PERIOD_MS) {
-                // If activity is started and finished before stop app switch time, we should not
-                // let app to be able to start background activity even it's in grace period.
-                if (lastActivityLaunchTime > lastStopAppSwitchesTime
-                        || lastActivityFinishTime > lastStopAppSwitchesTime) {
+            if (lastActivityLaunchTime > lastStopAppSwitchesTime
+                    || lastActivityFinishTime > lastStopAppSwitchesTime) {
+                final long now = SystemClock.uptimeMillis();
+                long timeSinceLastStartOrFinish = now - Math.max(lastActivityLaunchTime,
+                        lastActivityFinishTime);
+                if (timeSinceLastStartOrFinish < checkConfiguration.gracePeriod) {
                     return new BalVerdict(BAL_ALLOW_GRACE_PERIOD, /*background*/ true,
-                            "within " + ACTIVITY_BG_START_GRACE_PERIOD_MS + "ms grace period");
+                            "within " + checkConfiguration.gracePeriod + "ms grace period ("
+                                    + timeSinceLastStartOrFinish + "ms)");
                 }
-                if (DEBUG_ACTIVITY_STARTS) {
-                    Slog.d(TAG, "[Process(" + pid + ")] Activity start within "
-                            + ACTIVITY_BG_START_GRACE_PERIOD_MS
-                            + "ms grace period but also within stop app switch window");
-                }
-
             }
         }
         return BalVerdict.BLOCK;
diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
index 2755a80..dda39a6 100644
--- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
+++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java
@@ -60,6 +60,11 @@
     @Nullable
     private Task mCameraTask;
 
+    /**
+     * Value toggled on {@link #start()} to {@code true} and on {@link #dispose()} to {@code false}.
+     */
+    private boolean mIsRunning;
+
     CameraCompatFreeformPolicy(@NonNull DisplayContent displayContent,
             @NonNull CameraStateMonitor cameraStateMonitor,
             @NonNull ActivityRefresher activityRefresher) {
@@ -71,12 +76,19 @@
     void start() {
         mCameraStateMonitor.addCameraStateListener(this);
         mActivityRefresher.addEvaluator(this);
+        mIsRunning = true;
     }
 
     /** Releases camera callback listener. */
     void dispose() {
         mCameraStateMonitor.removeCameraStateListener(this);
         mActivityRefresher.removeEvaluator(this);
+        mIsRunning = false;
+    }
+
+    @VisibleForTesting
+    boolean isRunning() {
+        return mIsRunning;
     }
 
     // Refreshing only when configuration changes after rotation or camera split screen aspect ratio
@@ -109,10 +121,10 @@
     }
 
     @Override
-    public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+    public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
             @NonNull String cameraId) {
         if (!isTreatmentEnabledForActivity(cameraActivity)) {
-            return false;
+            return;
         }
         final int existingCameraCompatMode = cameraActivity.mAppCompatController
                 .getAppCompatCameraOverrides()
@@ -124,11 +136,9 @@
             cameraActivity.mAppCompatController.getAppCompatCameraOverrides()
                     .setFreeformCameraCompatMode(newCameraCompatMode);
             forceUpdateActivityAndTask(cameraActivity);
-            return true;
         } else {
             mIsCameraCompatTreatmentPending = false;
         }
-        return false;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/CameraStateMonitor.java b/services/core/java/com/android/server/wm/CameraStateMonitor.java
index 068fc00..8bfef6d 100644
--- a/services/core/java/com/android/server/wm/CameraStateMonitor.java
+++ b/services/core/java/com/android/server/wm/CameraStateMonitor.java
@@ -26,6 +26,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLog;
 
 import java.util.ArrayList;
@@ -74,14 +75,10 @@
     private final ArrayList<CameraCompatStateListener> mCameraStateListeners = new ArrayList<>();
 
     /**
-     * {@link CameraCompatStateListener} which returned {@code true} on the last {@link
-     * CameraCompatStateListener#onCameraOpened(ActivityRecord, String)}, if any.
-     *
-     * <p>This allows the {@link CameraStateMonitor} to notify a particular listener when camera
-     * closes, so they can revert any changes.
+     * Value toggled on {@link #startListeningToCameraState()} to {@code true} and on {@link
+     * #dispose()} to {@code false}.
      */
-    @Nullable
-    private CameraCompatStateListener mCurrentListenerForCameraActivity;
+    private boolean mIsRunning;
 
     private final CameraManager.AvailabilityCallback mAvailabilityCallback =
             new  CameraManager.AvailabilityCallback() {
@@ -111,6 +108,7 @@
     void startListeningToCameraState() {
         mCameraManager.registerAvailabilityCallback(
                 mWmService.mContext.getMainExecutor(), mAvailabilityCallback);
+        mIsRunning = true;
     }
 
     /** Releases camera callback listener. */
@@ -118,6 +116,12 @@
         if (mCameraManager != null) {
             mCameraManager.unregisterAvailabilityCallback(mAvailabilityCallback);
         }
+        mIsRunning = false;
+    }
+
+    @VisibleForTesting
+    boolean isRunning() {
+        return mIsRunning;
     }
 
     void addCameraStateListener(CameraCompatStateListener listener) {
@@ -167,12 +171,7 @@
             @NonNull String cameraId) {
         for (int i = 0; i < mCameraStateListeners.size(); i++) {
             CameraCompatStateListener listener = mCameraStateListeners.get(i);
-            boolean activeCameraTreatment = listener.onCameraOpened(
-                    cameraActivity, cameraId);
-            if (activeCameraTreatment) {
-                mCurrentListenerForCameraActivity = listener;
-                break;
-            }
+            listener.onCameraOpened(cameraActivity, cameraId);
         }
     }
 
@@ -226,19 +225,30 @@
                 return;
             }
 
-            if (mCurrentListenerForCameraActivity != null) {
-                boolean closeSuccessful =
-                        mCurrentListenerForCameraActivity.onCameraClosed(cameraId);
-                if (closeSuccessful) {
-                    mCameraIdPackageBiMapping.removeCameraId(cameraId);
-                    mCurrentListenerForCameraActivity = null;
-                } else {
-                    rescheduleRemoveCameraActivity(cameraId);
-                }
+            final boolean closeSuccessfulForAllListeners = notifyListenersCameraClosed(cameraId);
+            if (closeSuccessfulForAllListeners) {
+                // Finish cleaning up.
+                mCameraIdPackageBiMapping.removeCameraId(cameraId);
+            } else {
+                // Not ready to process closure yet - the camera activity might be refreshing.
+                // Try again later.
+                rescheduleRemoveCameraActivity(cameraId);
             }
         }
     }
 
+    /**
+     * @return {@code false} if any listeners have reported issues processing the close.
+     */
+    private boolean notifyListenersCameraClosed(@NonNull String cameraId) {
+        boolean closeSuccessfulForAllListeners = true;
+        for (int i = 0; i < mCameraStateListeners.size(); i++) {
+            closeSuccessfulForAllListeners &= mCameraStateListeners.get(i).onCameraClosed(cameraId);
+        }
+
+        return closeSuccessfulForAllListeners;
+    }
+
     // TODO(b/335165310): verify that this works in multi instance and permission dialogs.
     /**
      * Finds a visible activity with the given package name.
@@ -286,11 +296,9 @@
     interface CameraCompatStateListener {
         /**
          * Notifies the compat listener that an activity has opened camera.
-         *
-         * @return true if the treatment has been applied.
          */
         // TODO(b/336474959): try to decouple `cameraId` from the listeners.
-        boolean onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
+        void onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId);
         /**
          * Notifies the compat listener that camera is closed.
          *
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 3ebaf03..670a61d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -195,12 +195,14 @@
      * screenWidthDp, screenHeightDp, smallestScreenWidthDp, and orientation.
      * All overrides to those fields should be in this method.
      *
+     * Task is only needed for split-screen to apply an offset special handling.
+     *
      * TODO: Consider integrate this with computeConfigByResolveHint()
      */
     static void applySizeOverrideIfNeeded(DisplayContent displayContent, ApplicationInfo appInfo,
             Configuration newParentConfiguration, Configuration inOutConfig,
             boolean optsOutEdgeToEdge, boolean hasFixedRotationTransform,
-            boolean hasCompatDisplayInsets) {
+            boolean hasCompatDisplayInsets, Task task) {
         if (displayContent == null) {
             return;
         }
@@ -223,13 +225,16 @@
         }
         if (!optsOutEdgeToEdge && (!useOverrideInsetsForConfig
                 || hasCompatDisplayInsets
-                || isFloating
                 || rotation == ROTATION_UNDEFINED)) {
             // If the insets configuration decoupled logic is not enabled for the app, or the app
             // already has a compat override, or the context doesn't contain enough info to
             // calculate the override, skip the override.
             return;
         }
+        if (isFloating) {
+            // Floating window won't have any insets affect configuration. Skip the override.
+            return;
+        }
         // Make sure the orientation related fields will be updated by the override insets, because
         // fixed rotation has assigned the fields from display's configuration.
         if (hasFixedRotationTransform) {
@@ -255,8 +260,18 @@
             inOutConfig.windowConfiguration.setAppBounds(
                     newParentConfiguration.windowConfiguration.getBounds());
             outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
-            outAppBounds.inset(displayContent.getDisplayPolicy()
-                    .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets);
+            if (task != null) {
+                task = task.getCreatedByOrganizerTask();
+                if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
+                    outAppBounds.offset(task.mOffsetXForInsets, task.mOffsetYForInsets);
+                }
+            }
+            final DisplayPolicy.DecorInsets.Info decor =
+                    displayContent.getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
+            outAppBounds.intersectUnchecked(decor.mOverrideNonDecorFrame);
+            if (task != null && (task.mOffsetYForInsets != 0 || task.mOffsetXForInsets != 0)) {
+                outAppBounds.offset(-task.mOffsetXForInsets, -task.mOffsetYForInsets);
+            }
         }
         float density = inOutConfig.densityDpi;
         if (density == Configuration.DENSITY_DPI_UNDEFINED) {
@@ -807,23 +822,23 @@
      */
     @CallSuper
     protected void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
+            @WindowTracingLogLevel int logLevel) {
         final long token = proto.start(fieldId);
 
-        if (logLevel == WindowTraceLogLevel.ALL || mHasOverrideConfiguration) {
+        if (logLevel == WindowTracingLogLevel.ALL || mHasOverrideConfiguration) {
             mRequestedOverrideConfiguration.dumpDebug(proto, OVERRIDE_CONFIGURATION,
-                    logLevel == WindowTraceLogLevel.CRITICAL);
+                    logLevel == WindowTracingLogLevel.CRITICAL);
         }
 
         // Unless trace level is set to `WindowTraceLogLevel.ALL` don't dump anything that isn't
         // required to mitigate performance overhead
-        if (logLevel == WindowTraceLogLevel.ALL) {
+        if (logLevel == WindowTracingLogLevel.ALL) {
             mFullConfiguration.dumpDebug(proto, FULL_CONFIGURATION, false /* critical */);
             mMergedOverrideConfiguration.dumpDebug(proto, MERGED_OVERRIDE_CONFIGURATION,
                     false /* critical */);
         }
 
-        if (logLevel == WindowTraceLogLevel.TRIM) {
+        if (logLevel == WindowTracingLogLevel.TRIM) {
             // Required for Fass to automatically detect pip transitions in Winscope traces
             dumpDebugWindowingMode(proto);
         }
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
new file mode 100644
index 0000000..ff1742b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
+
+import android.annotation.NonNull;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+// TODO(b/359217664): Consider refactoring into AppCompatAspectRatioPolicy.
+/**
+ * Encapsulate app compat aspect ratio policy logic specific for desktop windowing initial bounds
+ * calculation.
+ */
+public class DesktopAppCompatAspectRatioPolicy {
+
+    @NonNull
+    private final AppCompatOverrides mAppCompatOverrides;
+    @NonNull
+    private final AppCompatConfiguration mAppCompatConfiguration;
+    @NonNull
+    private final ActivityRecord mActivityRecord;
+    @NonNull
+    private final TransparentPolicy mTransparentPolicy;
+
+    DesktopAppCompatAspectRatioPolicy(@NonNull ActivityRecord activityRecord,
+            @NonNull AppCompatOverrides appCompatOverrides,
+            @NonNull TransparentPolicy transparentPolicy,
+            @NonNull AppCompatConfiguration appCompatConfiguration) {
+        mActivityRecord = activityRecord;
+        mAppCompatOverrides = appCompatOverrides;
+        mTransparentPolicy = transparentPolicy;
+        mAppCompatConfiguration = appCompatConfiguration;
+    }
+
+    /**
+     * Calculates the final aspect ratio of an launching activity based on the task it will be
+     * launched in. Takes into account any min or max aspect ratio constraints.
+     */
+    float calculateAspectRatio(@NonNull Task task) {
+        final float maxAspectRatio = getMaxAspectRatio();
+        final float minAspectRatio = getMinAspectRatio(task);
+        float desiredAspectRatio = 0;
+        desiredAspectRatio = getDesiredAspectRatio(task);
+        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+            desiredAspectRatio = maxAspectRatio;
+        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+            desiredAspectRatio = minAspectRatio;
+        }
+        return desiredAspectRatio;
+    }
+
+    /**
+     * Returns the aspect ratio desired by the system for current activity, not taking into account
+     * any min or max aspect ratio constraints.
+     */
+    @VisibleForTesting
+    float getDesiredAspectRatio(@NonNull Task task) {
+        final float letterboxAspectRatioOverride = getFixedOrientationLetterboxAspectRatio(task);
+        // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
+        // be respected in #calculateAspectRatio.
+        if (isDefaultMultiWindowLetterboxAspectRatioDesired(task)) {
+            return DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+        } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+            return letterboxAspectRatioOverride;
+        }
+        return AppCompatUtils.computeAspectRatio(task.getDisplayArea().getBounds());
+    }
+
+    /**
+     * Determines the letterbox aspect ratio for an application based on its orientation and
+     * resizability.
+     */
+    private float getFixedOrientationLetterboxAspectRatio(@NonNull Task task) {
+        return mActivityRecord.shouldCreateAppCompatDisplayInsets()
+                ? getDefaultMinAspectRatioForUnresizableApps(task)
+                : getDefaultMinAspectRatio(task);
+    }
+
+    /**
+     * Calculates the aspect ratio of the available display area when an app enters split-screen on
+     * a given device, taking into account any dividers and insets.
+     */
+    private float getSplitScreenAspectRatio(@NonNull Task task) {
+        // Getting the same aspect ratio that apps get in split screen.
+        final DisplayArea displayArea = task.getDisplayArea();
+        final int dividerWindowWidth =
+                mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+                        R.dimen.docked_stack_divider_thickness);
+        final int dividerInsets =
+                mActivityRecord.mWmService.mContext.getResources().getDimensionPixelSize(
+                        R.dimen.docked_stack_divider_insets);
+        final int dividerSize = dividerWindowWidth - dividerInsets * 2;
+        final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+        if (bounds.width() >= bounds.height()) {
+            bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
+            bounds.right = bounds.centerX();
+        } else {
+            bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
+            bounds.bottom = bounds.centerY();
+        }
+        return AppCompatUtils.computeAspectRatio(bounds);
+    }
+
+
+    /**
+     * Returns the minimum aspect ratio for unresizable apps as determined by the system.
+     */
+    private float getDefaultMinAspectRatioForUnresizableApps(@NonNull Task task) {
+        if (!mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()) {
+            return mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+                    > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+                    ? mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps()
+                    : getDefaultMinAspectRatio(task);
+        }
+
+        return getSplitScreenAspectRatio(task);
+    }
+
+    /**
+     * Returns the default minimum aspect ratio for apps as determined by the system.
+     */
+    private float getDefaultMinAspectRatio(@NonNull Task task) {
+        if (!mAppCompatConfiguration.getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
+            return mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio();
+        }
+        return getDisplayAreaMinAspectRatio(task);
+    }
+
+    /**
+     * Calculates the aspect ratio of the available display area.
+     */
+    private float getDisplayAreaMinAspectRatio(@NonNull Task task) {
+        final DisplayArea displayArea = task.getDisplayArea();
+        final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+        return AppCompatUtils.computeAspectRatio(bounds);
+    }
+
+    /**
+     * Returns {@code true} if the default aspect ratio for a letterboxed app in multi-window mode
+     * should be used.
+     */
+    private boolean isDefaultMultiWindowLetterboxAspectRatioDesired(@NonNull Task task) {
+        final DisplayContent dc = task.mDisplayContent;
+        final int windowingMode = task.getDisplayArea().getWindowingMode();
+        return WindowConfiguration.inMultiWindowMode(windowingMode)
+                && !dc.getIgnoreOrientationRequest();
+    }
+
+    /**
+     * Returns the min aspect ratio of this activity.
+     */
+    private float getMinAspectRatio(@NonNull Task task) {
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedMinAspectRatio();
+        }
+
+        final ActivityInfo info = mActivityRecord.info;
+        if (info.applicationInfo == null) {
+            return info.getMinAspectRatio();
+        }
+
+        final AppCompatAspectRatioOverrides aspectRatioOverrides =
+                mAppCompatOverrides.getAppCompatAspectRatioOverrides();
+        if (shouldApplyUserMinAspectRatioOverride(task)) {
+            return aspectRatioOverrides.getUserMinAspectRatio();
+        }
+
+        if (!aspectRatioOverrides.shouldOverrideMinAspectRatio()
+                && !mAppCompatOverrides.getAppCompatCameraOverrides()
+                .shouldOverrideMinAspectRatioForCamera()) {
+            return info.getMinAspectRatio();
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
+                && !ActivityInfo.isFixedOrientationPortrait(
+                        mActivityRecord.getOverrideOrientation())) {
+            return info.getMinAspectRatio();
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN)
+                && isFullscreenPortrait(task)) {
+            return info.getMinAspectRatio();
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN)) {
+            return Math.max(aspectRatioOverrides.getSplitScreenAspectRatio(),
+                    info.getMinAspectRatio());
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE)) {
+            return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+                    info.getMinAspectRatio());
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM)) {
+            return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+                    info.getMinAspectRatio());
+        }
+
+        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_SMALL)) {
+            return Math.max(ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE,
+                    info.getMinAspectRatio());
+        }
+        return info.getMinAspectRatio();
+    }
+
+    /**
+     * Returns the max aspect ratio of this activity.
+     */
+    private float getMaxAspectRatio() {
+        if (mTransparentPolicy.isRunning()) {
+            return mTransparentPolicy.getInheritedMaxAspectRatio();
+        }
+        return mActivityRecord.info.getMaxAspectRatio();
+    }
+
+    /**
+     * Whether an applications minimum aspect ratio has been overridden.
+     */
+    boolean hasMinAspectRatioOverride(@NonNull Task task) {
+        return mActivityRecord.info.getMinAspectRatio() < getMinAspectRatio(task);
+    }
+
+    /**
+     * Whether we should apply the user aspect ratio override to the min aspect ratio for the
+     * current app.
+     */
+    boolean shouldApplyUserMinAspectRatioOverride(@NonNull Task task) {
+        if (!shouldEnableUserAspectRatioSettings(task)) {
+            return false;
+        }
+
+        final int userAspectRatioCode = mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+                .getUserMinAspectRatioOverrideCode();
+
+        return userAspectRatioCode != USER_MIN_ASPECT_RATIO_UNSET
+                && userAspectRatioCode != USER_MIN_ASPECT_RATIO_APP_DEFAULT
+                && userAspectRatioCode != USER_MIN_ASPECT_RATIO_FULLSCREEN;
+    }
+
+    /**
+     * Whether we should enable users to resize the current app.
+     */
+    private boolean shouldEnableUserAspectRatioSettings(@NonNull Task task) {
+        // We use mBooleanPropertyAllowUserAspectRatioOverride to allow apps to opt-out which has
+        // effect only if explicitly false. If mBooleanPropertyAllowUserAspectRatioOverride is null,
+        // the current app doesn't opt-out so the first part of the predicate is true.
+        return mAppCompatOverrides.getAppCompatAspectRatioOverrides()
+                    .getAllowUserAspectRatioOverridePropertyValue()
+                && mAppCompatConfiguration.isUserAppAspectRatioSettingsEnabled()
+                && task.mDisplayContent.getIgnoreOrientationRequest();
+    }
+
+    /**
+     * Returns {@code true} if the task window is portrait and fullscreen.
+     */
+    private boolean isFullscreenPortrait(@NonNull Task task) {
+        return task.getConfiguration().orientation == ORIENTATION_PORTRAIT
+                && task.getWindowConfiguration().getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index 8f1828d7..c3db7dd 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -16,31 +16,28 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.isFixedOrientation;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
-import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
-import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
-import static com.android.server.wm.AppCompatUtils.computeAspectRatio;
 import static com.android.server.wm.LaunchParamsUtil.applyLayoutGravity;
 import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
-import android.app.AppCompatTaskInfo;
 import android.app.TaskInfo;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
+import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.pm.ActivityInfo.WindowLayout;
 import android.graphics.Rect;
 import android.os.SystemProperties;
 import android.util.Size;
 import android.view.Gravity;
 
-import com.android.internal.R;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.utils.DesktopModeFlagsUtil;
 
 import java.util.function.Consumer;
@@ -60,7 +57,7 @@
      * Updates launch bounds for an activity with respect to its activity options, window layout,
      * android manifest and task configuration.
      */
-    static void updateInitialBounds(@NonNull Task task, @Nullable ActivityInfo.WindowLayout layout,
+    static void updateInitialBounds(@NonNull Task task, @Nullable WindowLayout layout,
             @Nullable ActivityRecord activity, @Nullable ActivityOptions options,
             @NonNull Rect outBounds, @NonNull Consumer<String> logger) {
         // Use stable frame instead of raw frame to avoid launching freeform windows on top of
@@ -98,7 +95,8 @@
      * fullscreen size, aspect ratio, orientation and resizability to calculate an area this is
      * compatible with the applications previous configuration.
      */
-    private static @NonNull Rect calculateInitialBounds(@NonNull Task task,
+    @NonNull
+    private static Rect calculateInitialBounds(@NonNull Task task,
             @NonNull ActivityRecord activity, @NonNull Rect stableBounds
     ) {
         final TaskInfo taskInfo = task.getTaskInfo();
@@ -116,18 +114,19 @@
             // applied.
             return centerInScreen(idealSize, screenBounds);
         }
-        // TODO(b/353457301): Replace with app compat aspect ratio method when refactoring complete.
-        float appAspectRatio = calculateAspectRatio(task, activity);
+        final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
+                activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(task);
         final float tdaWidth = stableBounds.width();
         final float tdaHeight = stableBounds.height();
-        final int activityOrientation = activity.getOverrideOrientation();
+        final int activityOrientation = getActivityOrientation(activity, task);
         final Size initialSize = switch (taskInfo.configuration.orientation) {
             case ORIENTATION_LANDSCAPE -> {
                 // Device in landscape orientation.
                 if (appAspectRatio == 0) {
                     appAspectRatio = 1;
                 }
-                if (taskInfo.isResizeable) {
+                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, taskInfo, task)) {
                     if (isFixedOrientationPortrait(activityOrientation)) {
                         // For portrait resizeable activities, respect apps fullscreen width but
                         // apply ideal size height.
@@ -139,14 +138,13 @@
                 }
                 // If activity is unresizeable, regardless of orientation, calculate maximum size
                 // (within the ideal size) maintaining original aspect ratio.
-                yield maximizeSizeGivenAspectRatio(
-                        activity.getOverrideOrientation(), idealSize, appAspectRatio);
+                yield maximizeSizeGivenAspectRatio(activityOrientation, idealSize, appAspectRatio);
             }
             case ORIENTATION_PORTRAIT -> {
                 // Device in portrait orientation.
                 final int customPortraitWidthForLandscapeApp = screenBounds.width()
                         - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
-                if (taskInfo.isResizeable) {
+                if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, taskInfo, task)) {
                     if (isFixedOrientationLandscape(activityOrientation)) {
                         if (appAspectRatio == 0) {
                             appAspectRatio = tdaWidth / (tdaWidth - 1);
@@ -180,11 +178,38 @@
     }
 
     /**
+     * Whether the activity's aspect ratio can be changed or if it should be maintained as if it was
+     * unresizeable.
+     */
+    private static boolean canChangeAspectRatio(
+            @NonNull DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy,
+            @NonNull TaskInfo taskInfo, @NonNull Task task) {
+        return taskInfo.isResizeable
+                && !desktopAppCompatAspectRatioPolicy.hasMinAspectRatioOverride(task);
+    }
+
+    private static @ScreenOrientation int getActivityOrientation(
+            @NonNull ActivityRecord activity, @NonNull Task task) {
+        final int activityOrientation = activity.getOverrideOrientation();
+        final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
+                activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        if (desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task)
+                && (!isFixedOrientation(activityOrientation)
+                    || activityOrientation == SCREEN_ORIENTATION_LOCKED)) {
+            // If a user aspect ratio override should be applied, treat the activity as portrait if
+            // it has not specified a fix orientation.
+            return SCREEN_ORIENTATION_PORTRAIT;
+        }
+        return activityOrientation;
+    }
+
+    /**
      * Calculates the largest size that can fit in a given area while maintaining a specific aspect
      * ratio.
      */
-    private static @NonNull Size maximizeSizeGivenAspectRatio(
-            @ActivityInfo.ScreenOrientation int orientation,
+    @NonNull
+    private static Size maximizeSizeGivenAspectRatio(
+            @ScreenOrientation int orientation,
             @NonNull Size targetArea,
             float aspectRatio
     ) {
@@ -229,68 +254,11 @@
     }
 
     /**
-     * Calculates the aspect ratio of an activity from its fullscreen bounds.
-     */
-    @VisibleForTesting
-    static float calculateAspectRatio(@NonNull Task task, @NonNull ActivityRecord activity) {
-        final TaskInfo taskInfo = task.getTaskInfo();
-        final float fullscreenWidth = task.getDisplayArea().getBounds().width();
-        final float fullscreenHeight = task.getDisplayArea().getBounds().height();
-        final float maxAspectRatio = activity.getMaxAspectRatio();
-        final float minAspectRatio = activity.getMinAspectRatio();
-        float desiredAspectRatio = 0;
-        if (taskInfo.isRunning) {
-            final AppCompatTaskInfo appCompatTaskInfo =  taskInfo.appCompatTaskInfo;
-            final int appLetterboxWidth =
-                    taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth;
-            final int appLetterboxHeight =
-                    taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight;
-            if (appCompatTaskInfo.isTopActivityLetterboxed()) {
-                desiredAspectRatio = (float) Math.max(appLetterboxWidth, appLetterboxHeight)
-                        / Math.min(appLetterboxWidth, appLetterboxHeight);
-            } else {
-                desiredAspectRatio = Math.max(fullscreenHeight, fullscreenWidth)
-                        / Math.min(fullscreenHeight, fullscreenWidth);
-            }
-        } else {
-            final float letterboxAspectRatioOverride =
-                    getFixedOrientationLetterboxAspectRatio(activity, task);
-            if (!task.mDisplayContent.getIgnoreOrientationRequest()) {
-                desiredAspectRatio = DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
-            } else if (letterboxAspectRatioOverride
-                    > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
-                desiredAspectRatio = letterboxAspectRatioOverride;
-            }
-        }
-        // If the activity matches display orientation, the display aspect ratio should be used
-        if (activityMatchesDisplayOrientation(
-                taskInfo.configuration.orientation,
-                activity.getOverrideOrientation())) {
-            desiredAspectRatio = Math.max(fullscreenWidth, fullscreenHeight)
-                    / Math.min(fullscreenWidth, fullscreenHeight);
-        }
-        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
-            desiredAspectRatio = maxAspectRatio;
-        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
-            desiredAspectRatio = minAspectRatio;
-        }
-        return desiredAspectRatio;
-    }
-
-    private static boolean activityMatchesDisplayOrientation(
-            @Configuration.Orientation int deviceOrientation,
-            @ActivityInfo.ScreenOrientation int activityOrientation) {
-        if (deviceOrientation == ORIENTATION_PORTRAIT) {
-            return isFixedOrientationPortrait(activityOrientation);
-        }
-        return isFixedOrientationLandscape(activityOrientation);
-    }
-
-    /**
      * Calculates the desired initial bounds for applications in desktop windowing. This is done as
      * a scale of the screen bounds.
      */
-    private static @NonNull Size calculateIdealSize(@NonNull Rect screenBounds, float scale) {
+    @NonNull
+    private static Size calculateIdealSize(@NonNull Rect screenBounds, float scale) {
         final int width = (int) (screenBounds.width() * scale);
         final int height = (int) (screenBounds.height() * scale);
         return new Size(width, height);
@@ -299,7 +267,8 @@
     /**
      * Adjusts bounds to be positioned in the middle of the screen.
      */
-    private static @NonNull Rect centerInScreen(@NonNull Size desiredSize,
+    @NonNull
+    private static Rect centerInScreen(@NonNull Size desiredSize,
             @NonNull Rect screenBounds) {
         // TODO(b/325240051): Position apps with bottom heavy offset
         final int heightOffset = (screenBounds.height() - desiredSize.getHeight()) / 2;
@@ -309,57 +278,4 @@
         resultBounds.offset(screenBounds.left + widthOffset, screenBounds.top + heightOffset);
         return resultBounds;
     }
-
-    private static float getFixedOrientationLetterboxAspectRatio(@NonNull ActivityRecord activity,
-            @NonNull Task task) {
-        return activity.shouldCreateCompatDisplayInsets()
-                ? getDefaultMinAspectRatioForUnresizableApps(activity, task)
-                : activity.mAppCompatController.getAppCompatAspectRatioOverrides()
-                        .getDefaultMinAspectRatio();
-    }
-
-    private static float getDefaultMinAspectRatioForUnresizableApps(
-            @NonNull ActivityRecord activity,
-            @NonNull Task task) {
-        final AppCompatAspectRatioOverrides appCompatAspectRatioOverrides =
-                activity.mAppCompatController.getAppCompatAspectRatioOverrides();
-        if (appCompatAspectRatioOverrides.isSplitScreenAspectRatioForUnresizableAppsEnabled()) {
-            // Default letterbox aspect ratio for unresizable apps.
-            return getSplitScreenAspectRatio(activity, task);
-        }
-
-        if (appCompatAspectRatioOverrides.getDefaultMinAspectRatioForUnresizableAppsFromConfig()
-                > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
-            return appCompatAspectRatioOverrides
-                    .getDefaultMinAspectRatioForUnresizableAppsFromConfig();
-        }
-
-        return appCompatAspectRatioOverrides.getDefaultMinAspectRatio();
-    }
-
-    /**
-     * Calculates the aspect ratio of the available display area when an app enters split-screen on
-     * a given device, taking into account any dividers and insets.
-     */
-    private static float getSplitScreenAspectRatio(@NonNull ActivityRecord activity,
-            @NonNull Task task) {
-        final int dividerWindowWidth =
-                activity.mWmService.mContext.getResources().getDimensionPixelSize(
-                        R.dimen.docked_stack_divider_thickness);
-        final int dividerInsets =
-                activity.mWmService.mContext.getResources().getDimensionPixelSize(
-                        R.dimen.docked_stack_divider_insets);
-        final int dividerSize = dividerWindowWidth - dividerInsets * 2;
-        final Rect bounds = new Rect(0, 0,
-                task.mDisplayContent.getDisplayInfo().appWidth,
-                task.mDisplayContent.getDisplayInfo().appHeight);
-        if (bounds.width() >= bounds.height()) {
-            bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
-            bounds.right = bounds.centerX();
-        } else {
-            bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
-            bounds.bottom = bounds.centerY();
-        }
-        return computeAspectRatio(bounds);
-    }
 }
diff --git a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
index 3dba57f..4abf806 100644
--- a/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
+++ b/services/core/java/com/android/server/wm/DimmerAnimationHelper.java
@@ -56,9 +56,14 @@
         Change() {}
 
         Change(@NonNull Change other) {
+            copyFrom(other);
+        }
+
+        void copyFrom(@NonNull Change other) {
             mAlpha = other.mAlpha;
             mBlurRadius = other.mBlurRadius;
             mDimmingContainer = other.mDimmingContainer;
+            mGeometryParent = other.mGeometryParent;
             mRelativeLayer = other.mRelativeLayer;
         }
 
@@ -83,8 +88,8 @@
         }
     }
 
-    private Change mCurrentProperties = new Change();
-    private Change mRequestedProperties = new Change();
+    private final Change mCurrentProperties = new Change();
+    private final Change mRequestedProperties = new Change();
     private AnimationSpec mAlphaAnimationSpec;
 
     private final AnimationAdapterFactory mAnimationAdapterFactory;
@@ -123,12 +128,15 @@
      * {@link Change#setRequestedAppearance(float, int)}
      */
     void applyChanges(@NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim) {
+        final Change startProperties = new Change(mCurrentProperties);
+        mCurrentProperties.copyFrom(mRequestedProperties);
+
         if (mRequestedProperties.mDimmingContainer == null) {
             Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
                     + "call adjustRelativeLayer?");
             return;
         }
-        if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
+        if (mRequestedProperties.mDimmingContainer.getSurfaceControl() == null) {
             Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
                     + "does not have a surface");
             dim.remove(t);
@@ -137,52 +145,49 @@
 
         dim.ensureVisible(t);
         reparent(dim.mDimSurface,
-                mRequestedProperties.mGeometryParent != mCurrentProperties.mGeometryParent
+                startProperties.mGeometryParent != mRequestedProperties.mGeometryParent
                         ? mRequestedProperties.mGeometryParent.getSurfaceControl() : null,
                 mRequestedProperties.mDimmingContainer.getSurfaceControl(),
                 mRequestedProperties.mRelativeLayer, t);
 
-        if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) {
+        if (!startProperties.hasSameVisualProperties(mRequestedProperties)) {
             stopCurrentAnimation(dim.mDimSurface);
 
             if (dim.mSkipAnimation
                     // If the container doesn't change but requests a dim change, then it is
                     // directly providing us the animated values
-                    || (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties)
+                    || (startProperties.hasSameDimmingContainer(mRequestedProperties)
                     && dim.isDimming())) {
                 ProtoLog.d(WM_DEBUG_DIMMER,
                         "%s skipping animation and directly setting alpha=%f, blur=%d",
-                        dim, mRequestedProperties.mAlpha,
+                        dim, startProperties.mAlpha,
                         mRequestedProperties.mBlurRadius);
-                setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha,
-                        mRequestedProperties.mBlurRadius, t);
+                setCurrentAlphaBlur(dim.mDimSurface, t);
                 dim.mSkipAnimation = false;
             } else {
-                startAnimation(t, dim);
+                startAnimation(t, dim, startProperties, mRequestedProperties);
             }
-
         } else if (!dim.isDimming()) {
             // We are not dimming, so we tried the exit animation but the alpha is already 0,
             // therefore, let's just remove this surface
             dim.remove(t);
         }
-        mCurrentProperties = new Change(mRequestedProperties);
     }
 
     private void startAnimation(
-            @NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim) {
+            @NonNull SurfaceControl.Transaction t, @NonNull Dimmer.DimState dim,
+            @NonNull Change from, @NonNull Change to) {
         ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
-        mAlphaAnimationSpec = getRequestedAnimationSpec();
+        mAlphaAnimationSpec = getRequestedAnimationSpec(from, to);
         mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
                 dim.mHostContainer.mWmService.mSurfaceAnimationRunner);
 
-        float targetAlpha = mRequestedProperties.mAlpha;
-        int targetBlur = mRequestedProperties.mBlurRadius;
+        float targetAlpha = to.mAlpha;
 
         mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
                 ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
                     synchronized (dim.mHostContainer.mWmService.mGlobalLock) {
-                        setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t);
+                        setCurrentAlphaBlur(dim.mDimSurface, t);
                         if (targetAlpha == 0f && !dim.isDimming()) {
                             dim.remove(t);
                         }
@@ -207,15 +212,15 @@
     }
 
     @NonNull
-    private AnimationSpec getRequestedAnimationSpec() {
-        final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
-        final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
-        long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
-                * Math.abs(mRequestedProperties.mAlpha - startAlpha));
+    private static AnimationSpec getRequestedAnimationSpec(Change from, Change to) {
+        final float startAlpha = Math.max(from.mAlpha, 0f);
+        final int startBlur = Math.max(from.mBlurRadius, 0);
+        long duration = (long) (getDimDuration(to.mDimmingContainer)
+                * Math.abs(to.mAlpha - startAlpha));
 
         final AnimationSpec spec =  new AnimationSpec(
-                new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha),
-                new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius),
+                new AnimationSpec.AnimationExtremes<>(startAlpha, to.mAlpha),
+                new AnimationSpec.AnimationExtremes<>(startBlur, to.mBlurRadius),
                 duration
         );
         ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec);
@@ -225,7 +230,7 @@
     /**
      * Change the geometry and relative parent of this dim layer
      */
-    void reparent(@NonNull SurfaceControl dimLayer,
+    static void reparent(@NonNull SurfaceControl dimLayer,
                   @Nullable SurfaceControl newGeometryParent,
                   @NonNull SurfaceControl relativeParent,
                   int relativePosition,
@@ -240,17 +245,16 @@
         }
     }
 
-    void setAlphaBlur(@NonNull SurfaceControl sc, float alpha, int blur,
-                      @NonNull SurfaceControl.Transaction t) {
+    void setCurrentAlphaBlur(@NonNull SurfaceControl sc, @NonNull SurfaceControl.Transaction t) {
         try {
-            t.setAlpha(sc, alpha);
-            t.setBackgroundBlurRadius(sc, blur);
+            t.setAlpha(sc, mCurrentProperties.mAlpha);
+            t.setBackgroundBlurRadius(sc, mCurrentProperties.mBlurRadius);
         } catch (NullPointerException e) {
             Log.w(TAG , "Tried to change look of dim " + sc + " after remove",  e);
         }
     }
 
-    private long getDimDuration(@NonNull WindowContainer<?> container) {
+    private static long getDimDuration(@NonNull WindowContainer<?> container) {
         // Use the same duration as the animation on the WindowContainer
         AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
         final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 86f69cd..ca5485e 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -356,7 +356,7 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fcc6b11..e8a3951 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -45,7 +45,6 @@
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
 import static android.view.Display.INVALID_DISPLAY;
-import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 import static android.view.Display.STATE_UNKNOWN;
 import static android.view.Display.isSuspendedState;
 import static android.view.InsetsSource.ID_IME;
@@ -80,6 +79,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
@@ -235,7 +235,6 @@
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager;
@@ -572,8 +571,6 @@
 
     boolean mWallpaperMayChange = false;
 
-    private final SurfaceSession mSession = new SurfaceSession();
-
     /**
      * A perf hint session which will boost the refresh rate for the display and change sf duration
      * to handle larger workloads.
@@ -857,8 +854,8 @@
             return false;
         }
         if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
-                && !mImeLayeringTarget.isRequestedVisible(ime())
-                && !mImeLayeringTarget.isVisibleRequested()) {
+                && !(mImeLayeringTarget.isRequestedVisible(ime())
+                        && mImeLayeringTarget.isVisibleRequested())) {
             return false;
         }
 
@@ -951,6 +948,7 @@
                     w.updateLastFrames();
                     mWmService.mFrameChangingWindows.remove(w);
                 }
+                w.updateSurfacePositionNonOrganized();
                 w.onResizeHandled();
             }
 
@@ -1283,7 +1281,7 @@
      * @param transaction as part of which to perform the configuration
      */
     private void configureSurfaces(Transaction transaction) {
-        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder()
                 .setOpaque(true)
                 .setContainerLayer()
                 .setCallsite("DisplayContent");
@@ -1834,7 +1832,7 @@
         if (mTransitionController.useShellTransitionsRotation()) {
             return ROTATION_UNDEFINED;
         }
-        final int activityOrientation = r.getOverrideOrientation();
+        int activityOrientation = r.getOverrideOrientation();
         if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM
                 || shouldIgnoreOrientationRequest(activityOrientation)) {
             return ROTATION_UNDEFINED;
@@ -1845,14 +1843,15 @@
                     r /* boundary */, false /* includeBoundary */, true /* traverseTopToBottom */);
             if (nextCandidate != null) {
                 r = nextCandidate;
+                activityOrientation = r.getOverrideOrientation();
             }
         }
-        if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */)
-                == getConfiguration().orientation) {
+        if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */,
+                activityOrientation) == getConfiguration().orientation) {
             return ROTATION_UNDEFINED;
         }
         final int currentRotation = getRotation();
-        final int rotation = mDisplayRotation.rotationForOrientation(r.getRequestedOrientation(),
+        final int rotation = mDisplayRotation.rotationForOrientation(activityOrientation,
                 currentRotation);
         if (rotation == currentRotation) {
             return ROTATION_UNDEFINED;
@@ -1935,7 +1934,6 @@
             return false;
         }
         if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()
-                && mFixedRotationTransitionListener.mAnimatingRecents == null
                 && !mTransitionController.isTransientLaunch(r)) {
             // Use normal rotation animation for orientation change of visible wallpaper if recents
             // animation is not running (it may be swiping to home).
@@ -1961,9 +1959,7 @@
 
     /** Returns {@code true} if the top activity is transformed with the new rotation of display. */
     boolean hasTopFixedRotationLaunchingApp() {
-        return mFixedRotationLaunchingApp != null
-                // Ignore animating recents because it hasn't really become the top.
-                && mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
+        return mFixedRotationLaunchingApp != null;
     }
 
     /** It usually means whether the recents activity is launching with a different rotation. */
@@ -1990,8 +1986,7 @@
             mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
             // Delay the hide animation to avoid blinking by clicking navigation bar that may
             // toggle fixed rotation in a short time.
-            final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
-                    || mTransitionController.isTransientLaunch(r);
+            final boolean shouldDebounce = mTransitionController.isTransientLaunch(r);
             startAsyncRotation(shouldDebounce);
         } else if (mFixedRotationLaunchingApp != null && r == null) {
             mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
@@ -2023,12 +2018,9 @@
             // the heavy operations. This also benefits that the states of multiple activities
             // are handled together.
             r.linkFixedRotationTransform(prevRotatedLaunchingApp);
-            if (r != mFixedRotationTransitionListener.mAnimatingRecents) {
-                // Only update the record for normal activity so the display orientation can be
-                // updated when the transition is done if it becomes the top. And the case of
-                // recents can be handled when the recents animation is finished.
-                setFixedRotationLaunchingAppUnchecked(r, rotation);
-            }
+            // Only update the record for normal activity so the display orientation can be
+            // updated when the transition is done if it becomes the top.
+            setFixedRotationLaunchingAppUnchecked(r, rotation);
             return;
         }
 
@@ -3220,22 +3212,21 @@
      * If xPpi or yDpi is equal to {@link #INVALID_DPI}, the values are ignored.
      */
     void setForcedSize(int width, int height, float xDPI, float yDPI) {
-  	// Can't force size higher than the maximal allowed
-        if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
-            final float ratio = mMaxUiWidth / (float) width;
-            height = (int) (height * ratio);
-            width = mMaxUiWidth;
-        }
-
         mIsSizeForced = mInitialDisplayWidth != width || mInitialDisplayHeight != height;
         if (mIsSizeForced) {
+            if (mMaxUiWidth > 0 && width > mMaxUiWidth) {
+                final float ratio = mMaxUiWidth / (float) width;
+                height = (int) (height * ratio);
+                width = mMaxUiWidth;
+            }
             final Point size = getValidForcedSize(width, height);
             width = size.x;
             height = size.y;
         }
 
         Slog.i(TAG_WM, "Using new display size: " + width + "x" + height);
-        updateBaseDisplayMetrics(width, height, mBaseDisplayDensity,
+        updateBaseDisplayMetrics(width, height,
+                mIsDensityForced ? mBaseDisplayDensity : mInitialDisplayDensity,
                 xDPI != INVALID_DPI ? xDPI : mBaseDisplayPhysicalXDpi,
                 yDPI != INVALID_DPI ? yDPI : mBaseDisplayPhysicalYDpi);
         reconfigureDisplayLocked();
@@ -3496,10 +3487,7 @@
      */
     void collectDisplayChange(@NonNull Transition transition) {
         if (!mLastHasContent) return;
-        if (!transition.isCollecting()) {
-            throw new IllegalArgumentException("Can only collect display change if transition"
-                    + " is collecting");
-        }
+        if (!transition.isCollecting()) return;
         if (!transition.mParticipants.contains(this)) {
             transition.collect(this);
             startAsyncRotationIfNeeded();
@@ -3565,9 +3553,9 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
+            @WindowTracingLogLevel int logLevel) {
         // Critical log level logs only visible elements to mitigate performance overheard
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
@@ -4605,7 +4593,7 @@
         // removed on the task.
         removeImeSurfaceImmediately();
         mImeScreenshot = new ImeScreenshot(
-                mWmService.mSurfaceControlFactory.apply(null), imeTarget);
+                mWmService.mSurfaceControlFactory.get(), imeTarget);
         // If the caller requests to hide IME, then allow to show IME snapshot for any target task.
         // So IME won't look like suddenly disappeared. It usually happens when turning off screen.
         mImeScreenshot.attachAndShow(t, hideImeWindow /* anyTargetTask */);
@@ -5438,14 +5426,8 @@
     }
 
     @Override
-    SurfaceSession getSession() {
-        return mSession;
-    }
-
-    @Override
     SurfaceControl.Builder makeChildSurface(WindowContainer child) {
-        SurfaceSession s = child != null ? child.getSession() : getSession();
-        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer();
+        final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder().setContainerLayer();
         if (child == null) {
             return b;
         }
@@ -5461,12 +5443,12 @@
      * and other potpourii.
      */
     SurfaceControl.Builder makeOverlay() {
-        return mWmService.makeSurfaceBuilder(mSession).setParent(getOverlayLayer());
+        return mWmService.makeSurfaceBuilder().setParent(getOverlayLayer());
     }
 
     @Override
     public SurfaceControl.Builder makeAnimationLeash() {
-        return mWmService.makeSurfaceBuilder(mSession).setParent(mSurfaceControl)
+        return mWmService.makeSurfaceBuilder().setParent(mSurfaceControl)
                 .setContainerLayer();
     }
 
@@ -5901,18 +5883,13 @@
         final Region local = Region.obtain();
         final int[] remainingLeftRight =
                 {mSystemGestureExclusionLimit, mSystemGestureExclusionLimit};
-        final RecentsAnimationController recentsAnimationController =
-                mWmService.getRecentsAnimationController();
 
         // Traverse all windows top down to assemble the gesture exclusion rects.
         // For each window, we only take the rects that fall within its touchable region.
         forAllWindows(w -> {
-            final boolean ignoreRecentsAnimationTarget = recentsAnimationController != null
-                    && recentsAnimationController.shouldApplyInputConsumer(w.getActivityRecord());
             if (!w.canReceiveTouchInput() || !w.isVisible()
                     || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0
-                    || unhandled.isEmpty()
-                    || ignoreRecentsAnimationTarget) {
+                    || unhandled.isEmpty()) {
                 return;
             }
 
@@ -6124,16 +6101,7 @@
     void getKeepClearAreas(Set<Rect> outRestricted, Set<Rect> outUnrestricted) {
         final Matrix tmpMatrix = new Matrix();
         final float[] tmpFloat9 = new float[9];
-        final RecentsAnimationController recentsAnimationController =
-                mWmService.getRecentsAnimationController();
         forAllWindows(w -> {
-            // Skip the window if it is part of Recents animation
-            final boolean ignoreRecentsAnimationTarget = recentsAnimationController != null
-                    && recentsAnimationController.shouldApplyInputConsumer(w.getActivityRecord());
-            if (ignoreRecentsAnimationTarget) {
-                return false;  // continue traversal
-            }
-
             if (w.isVisible() && !w.inPinnedWindowingMode()) {
                 w.getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9);
 
@@ -6308,14 +6276,6 @@
     }
 
     boolean updateDisplayOverrideConfigurationLocked() {
-        // Preemptively cancel the running recents animation -- SysUI can't currently handle this
-        // case properly since the signals it receives all happen post-change
-        final RecentsAnimationController recentsAnimationController =
-                mWmService.getRecentsAnimationController();
-        if (recentsAnimationController != null) {
-            recentsAnimationController.cancelAnimationForDisplayChange();
-        }
-
         Configuration values = new Configuration();
         computeScreenConfiguration(values);
 
@@ -6556,7 +6516,12 @@
 
     @VisibleForTesting
     boolean shouldDestroyContentOnRemove() {
-        return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
+        return getRemoveContentMode() == REMOVE_CONTENT_MODE_DESTROY;
+    }
+
+    @WindowManager.RemoveContentMode
+    int getRemoveContentMode() {
+        return mWmService.mDisplayWindowSettings.getRemoveContentModeLocked(this);
     }
 
     boolean shouldSleep() {
@@ -6911,79 +6876,11 @@
     /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
     class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
 
-        /**
-         * The animating activity which shows the recents task list. It is set between
-         * {@link RecentsAnimationController#initialize} and
-         * {@link RecentsAnimationController#cleanupAnimation}.
-         */
-        private ActivityRecord mAnimatingRecents;
-
-        /** Whether {@link #mAnimatingRecents} is going to be the top activity. */
-        private boolean mRecentsWillBeTop;
-
         FixedRotationTransitionListener(int displayId) {
             super(displayId);
         }
 
         /**
-         * If the recents activity has a fixed orientation which is different from the current top
-         * activity, it will be rotated before being shown so we avoid a screen rotation animation
-         * when showing the Recents view.
-         */
-        void onStartRecentsAnimation(@NonNull ActivityRecord r) {
-            mAnimatingRecents = r;
-            if (r.isVisible() && mFocusedApp != null && !mFocusedApp.occludesParent()) {
-                // The recents activity has shown with the orientation determined by the top
-                // activity, keep its current orientation to avoid flicking by the configuration
-                // change of visible activity.
-                return;
-            }
-            rotateInDifferentOrientationIfNeeded(r);
-            if (r.hasFixedRotationTransform()) {
-                // Set the record so we can recognize it to continue to update display orientation
-                // if the recents activity becomes the top later.
-                setFixedRotationLaunchingApp(r, r.getWindowConfiguration().getRotation());
-            }
-        }
-
-        /**
-         * If {@link #mAnimatingRecents} still has fixed rotation, it should be moved to top so we
-         * don't clear {@link #mFixedRotationLaunchingApp} that will be handled by transition.
-         */
-        void onFinishRecentsAnimation() {
-            final ActivityRecord animatingRecents = mAnimatingRecents;
-            final boolean recentsWillBeTop = mRecentsWillBeTop;
-            mAnimatingRecents = null;
-            mRecentsWillBeTop = false;
-            if (recentsWillBeTop) {
-                // The recents activity will be the top, such as staying at recents list or
-                // returning to home (if home and recents are the same activity).
-                return;
-            }
-
-            if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp
-                    && animatingRecents.isVisible() && animatingRecents != topRunningActivity()) {
-                // The recents activity should be going to be invisible (switch to another app or
-                // return to original top). Only clear the top launching record without finishing
-                // the transform immediately because it won't affect display orientation. And before
-                // the visibility is committed, the recents activity may perform relayout which may
-                // cause unexpected configuration change if the rotated configuration is restored.
-                // The transform will be finished when the transition is done.
-                setFixedRotationLaunchingAppUnchecked(null);
-            } else {
-                // If there is already a launching activity that is not the recents, before its
-                // transition is completed, the recents animation may be started. So if the recents
-                // activity won't be the top, the display orientation should be updated according
-                // to the current top activity.
-                continueUpdateOrientationForDiffOrienLaunchingApp();
-            }
-        }
-
-        void notifyRecentsWillBeTop() {
-            mRecentsWillBeTop = true;
-        }
-
-        /**
          * Returns {@code true} if the transient launch (e.g. recents animation) requested a fixed
          * orientation, then the rotation change should be deferred.
          */
@@ -6993,8 +6890,6 @@
                 if (hasFixedRotationTransientLaunch()) {
                     source = mFixedRotationLaunchingApp;
                 }
-            } else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) {
-                source = mAnimatingRecents;
             }
             if (source == null || source.getRequestedConfigurationOrientation(
                     true /* forDisplay */) == ORIENTATION_UNDEFINED) {
@@ -7007,19 +6902,7 @@
         @Override
         public void onAppTransitionFinishedLocked(IBinder token) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            // Ignore the animating recents so the fixed rotation transform won't be switched twice
-            // by finishing the recents animation and moving it to top. That also avoids flickering
-            // due to wait for previous activity to be paused if it supports PiP that ignores the
-            // effect of resume-while-pausing.
-            if (r == null || r == mAnimatingRecents) {
-                return;
-            }
-            if (mAnimatingRecents != null && mRecentsWillBeTop) {
-                // The activity is not the recents and it should be moved to back later, so it is
-                // better to keep its current appearance for the next transition. Otherwise the
-                // display orientation may be updated too early and the layout procedures at the
-                // end of finishing recents animation is skipped. That causes flickering because
-                // the surface of closing app hasn't updated to invisible.
+            if (r == null) {
                 return;
             }
             if (mFixedRotationLaunchingApp == null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c3339cd..5c62120 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1036,7 +1036,7 @@
     /**
      * Check if a window can be added to the system.
      *
-     * Currently enforces that two window types are singletons per display:
+     * Currently enforces that these window types are singletons per display:
      * <ul>
      * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li>
      * <li>{@link WindowManager.LayoutParams#TYPE_NOTIFICATION_SHADE}</li>
@@ -1058,41 +1058,39 @@
             ActivityTaskManagerService.enforceTaskPermission("DisplayPolicy");
         }
 
+        final String systemUiPermission =
+                mService.isCallerVirtualDeviceOwner(mDisplayContent.getDisplayId(), callingUid)
+                        // Allow virtual device owners to add system windows on their displays.
+                        ? android.Manifest.permission.CREATE_VIRTUAL_DEVICE
+                        : android.Manifest.permission.STATUS_BAR_SERVICE;
+
         switch (attrs.type) {
             case TYPE_STATUS_BAR:
-                mContext.enforcePermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
                         "DisplayPolicy");
                 if (mStatusBar != null && mStatusBar.isAlive()) {
                     return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
             case TYPE_NOTIFICATION_SHADE:
-                mContext.enforcePermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
                         "DisplayPolicy");
-                if (mNotificationShade != null) {
-                    if (mNotificationShade.isAlive()) {
-                        return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                    }
+                if (mNotificationShade != null && mNotificationShade.isAlive()) {
+                    return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
             case TYPE_NAVIGATION_BAR:
-                mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
-                        callingPid, callingUid, "DisplayPolicy");
+                mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
+                        "DisplayPolicy");
                 if (mNavigationBar != null && mNavigationBar.isAlive()) {
                     return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
-                mContext.enforcePermission(android.Manifest.permission.STATUS_BAR_SERVICE,
-                        callingPid, callingUid, "DisplayPolicy");
-                break;
             case TYPE_STATUS_BAR_ADDITIONAL:
             case TYPE_STATUS_BAR_SUB_PANEL:
             case TYPE_VOICE_INTERACTION_STARTING:
-                mContext.enforcePermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
                         "DisplayPolicy");
                 break;
             case TYPE_STATUS_BAR_PANEL:
@@ -1102,8 +1100,7 @@
         if (attrs.providedInsets != null) {
             // Recents component is allowed to add inset types.
             if (!mService.mAtmService.isCallerRecents(callingUid)) {
-                mContext.enforcePermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                mContext.enforcePermission(systemUiPermission, callingPid, callingUid,
                         "DisplayPolicy");
             }
         }
@@ -1596,7 +1593,7 @@
             final ActivityRecord currentActivity = win.getActivityRecord();
             if (currentActivity != null) {
                 final LetterboxDetails currentLetterboxDetails = currentActivity
-                        .mLetterboxUiController.getLetterboxDetails();
+                        .mAppCompatController.getAppCompatLetterboxPolicy().getLetterboxDetails();
                 if (currentLetterboxDetails != null) {
                     mLetterboxDetails.add(currentLetterboxDetails);
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index a5da5e7..5200e82 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -611,16 +611,6 @@
             mDisplayRotationCoordinator.onDefaultDisplayRotationChanged(rotation);
         }
 
-        // Preemptively cancel the running recents animation -- SysUI can't currently handle this
-        // case properly since the signals it receives all happen post-change. We do this earlier
-        // in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems
-        // to happen too late.
-        final RecentsAnimationController recentsAnimationController =
-                mService.getRecentsAnimationController();
-        if (recentsAnimationController != null) {
-            recentsAnimationController.cancelAnimationForDisplayChange();
-        }
-
         ProtoLog.v(WM_DEBUG_ORIENTATION,
                 "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
                         displayId, rotation, oldRotation, lastOrientation);
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index e50a089..27d9767 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -76,6 +76,11 @@
     @ScreenOrientation
     private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
 
+    /**
+     * Value toggled on {@link #start()} to {@code true} and on {@link #dispose()} to {@code false}.
+     */
+    private boolean mIsRunning;
+
     DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent,
             @NonNull CameraStateMonitor cameraStateMonitor,
             @NonNull ActivityRefresher activityRefresher) {
@@ -90,12 +95,19 @@
     void start() {
         mCameraStateMonitor.addCameraStateListener(this);
         mActivityRefresher.addEvaluator(this);
+        mIsRunning = true;
     }
 
     /** Releases camera state listener. */
     void dispose() {
         mCameraStateMonitor.removeCameraStateListener(this);
         mActivityRefresher.removeEvaluator(this);
+        mIsRunning = false;
+    }
+
+    @VisibleForTesting
+    boolean isRunning() {
+        return mIsRunning;
     }
 
     /**
@@ -298,7 +310,7 @@
     }
 
     @Override
-    public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+    public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
             @NonNull String cameraId) {
         mCameraTask = cameraActivity.getTask();
         // Checking whether an activity in fullscreen rather than the task as this camera
@@ -306,7 +318,7 @@
         if (cameraActivity.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
             recomputeConfigurationForCameraCompatIfNeeded(cameraActivity);
             mDisplayContent.updateOrientation();
-            return true;
+            return;
         }
         // Checking that the whole app is in multi-window mode as we shouldn't show toast
         // for the activity embedding case.
@@ -320,7 +332,6 @@
                         (String) packageManager.getApplicationLabel(
                                 packageManager.getApplicationInfo(cameraActivity.packageName,
                                         /* flags */ 0)));
-                return true;
             } catch (PackageManager.NameNotFoundException e) {
                 ProtoLog.e(WM_DEBUG_ORIENTATION,
                         "DisplayRotationCompatPolicy: Multi-window toast not shown as "
@@ -328,7 +339,6 @@
                         cameraActivity.packageName);
             }
         }
-        return false;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 2f0ee17..e585efa8 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
@@ -68,6 +69,7 @@
         mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
     }
 
+    /** Stores the size override settings. If the width or height is zero, it means to clear. */
     void setForcedSize(@NonNull DisplayContent displayContent, int width, int height) {
         if (displayContent.isDefaultDisplay) {
             final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height);
@@ -183,7 +185,7 @@
         final DisplayInfo displayInfo = dc.getDisplayInfo();
         final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo);
         if (settings.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) {
-            if (dc.isPrivate()) {
+            if (dc.isPrivate() || dc.getDisplay().getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT) {
                 // For private displays by default content is destroyed on removal.
                 return REMOVE_CONTENT_MODE_DESTROY;
             }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 59435b8..b09d63f 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -175,7 +175,7 @@
      */
     private CompletableFuture<Void> showInputSurface() {
         if (mInputSurface == null) {
-            mInputSurface = mService.makeSurfaceBuilder(mDisplayContent.getSession())
+            mInputSurface = mService.makeSurfaceBuilder()
                     .setContainerLayer()
                     .setName("Drag and Drop Input Consumer")
                     .setCallsite("DragState.showInputSurface")
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index c66d659..169a76f 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -403,7 +403,7 @@
 
         @Override
         public void dumpProto(ProtoOutputStream proto, long fieldId,
-                              @WindowTraceLogLevel int logLevel) {
+                              @WindowTracingLogLevel int logLevel) {
             final long token = proto.start(fieldId);
 
             final long token2 = proto.start(IDENTIFIER);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3a5f9b7dd4..43c3d05 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -104,7 +104,7 @@
                 mGivenInsetsReady = true;
                 ImeTracker.forLogging().onProgress(mStatsToken,
                         ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
-                mStateController.notifyControlChanged(mControlTarget);
+                mStateController.notifyControlChanged(mControlTarget, this);
                 setImeShowing(true);
             } else if (wasServerVisible && mServerVisible && mGivenInsetsReady
                     && givenInsetsPending) {
@@ -132,15 +132,15 @@
     }
 
     @Override
-    protected boolean isLeashReadyForDispatching() {
+    protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
             final WindowState ws =
                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
             final boolean isDrawn = ws != null && ws.isDrawn();
-            return super.isLeashReadyForDispatching() && mServerVisible && isDrawn
-                    && mGivenInsetsReady;
+            return super.isLeashReadyForDispatching(target)
+                    && mServerVisible && isDrawn && mGivenInsetsReady;
         } else {
-            return super.isLeashReadyForDispatching();
+            return super.isLeashReadyForDispatching(target);
         }
     }
 
@@ -726,7 +726,7 @@
     }
 
     @Override
-    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel) {
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, INSETS_SOURCE_PROVIDER, logLevel);
         final WindowState imeRequesterWindow =
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 4204670..a288cc7 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -77,8 +77,7 @@
         mWindowHandle.scaleFactor = 1.0f;
         mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE;
 
-        mInputSurface = mService.makeSurfaceBuilder(
-                        mService.mRoot.getDisplayContent(displayId).getSession())
+        mInputSurface = mService.makeSurfaceBuilder()
                 .setContainerLayer()
                 .setName("Input Consumer " + name)
                 .setCallsite("InputConsumerImpl")
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1e7de2b..232c3b6 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -281,7 +281,7 @@
                         + " - Input overlay layer is not initialized.");
                 return null;
             }
-            return mService.makeSurfaceBuilder(dc.getSession())
+            return mService.makeSurfaceBuilder()
                     .setContainerLayer()
                     .setName(name)
                     .setCallsite("createSurfaceForGestureMonitor")
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b8869f1..ddbfd70 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -49,7 +49,6 @@
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.Nullable;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
 import android.os.IBinder;
@@ -111,7 +110,7 @@
      * draw the live-tile above the recents activity, we also need to provide that activity as a
      * z-layering reference so that we can place the recents input consumer above it.
      */
-    private WeakReference<ActivityRecord> mActiveRecentsActivity = null;
+    private WeakReference<Task> mActiveRecentsTask = null;
     private WeakReference<Task> mActiveRecentsLayerRef = null;
 
     private class UpdateInputWindows implements Runnable {
@@ -388,13 +387,13 @@
 
     /**
      * Inform InputMonitor when recents is active so it can enable the recents input consumer.
-     * @param activity The active recents activity. {@code null} means recents is not active.
+     * @param task The active recents task. {@code null} means recents is not active.
      * @param layer A task whose Z-layer is used as a reference for how to sort the consumer.
      */
-    void setActiveRecents(@Nullable ActivityRecord activity, @Nullable Task layer) {
-        final boolean clear = activity == null;
-        final boolean wasActive = mActiveRecentsActivity != null && mActiveRecentsLayerRef != null;
-        mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
+    void setActiveRecents(@Nullable Task task, @Nullable Task layer) {
+        final boolean clear = task == null;
+        final boolean wasActive = mActiveRecentsTask != null && mActiveRecentsLayerRef != null;
+        mActiveRecentsTask = clear ? null : new WeakReference<>(task);
         mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
         if (clear && wasActive) {
             setUpdateInputWindowsNeededLw();
@@ -413,17 +412,12 @@
         // Request focus for the recents animation input consumer if an input consumer should
         // be applied for the window.
         if (recentsAnimationInputConsumer != null && focus != null) {
-            final RecentsAnimationController recentsAnimationController =
-                    mService.getRecentsAnimationController();
             // Apply recents input consumer when the focusing window is in recents animation.
-            final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null
-                    && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord))
-                    // Shell transitions doesn't use RecentsAnimationController but we still
-                    // have carryover legacy logic that relies on the consumer.
-                    || (getWeak(mActiveRecentsActivity) != null && focus.inTransition()
+            final boolean shouldApplyRecentsInputConsumer =
+                    getWeak(mActiveRecentsTask) != null && focus.inTransition()
                             // only take focus from the recents activity to avoid intercepting
                             // events before the gesture officially starts.
-                            && focus.isActivityTypeHomeOrRecents());
+                            && focus.isActivityTypeHomeOrRecents();
             if (shouldApplyRecentsInputConsumer) {
                 if (mInputFocus != recentsAnimationInputConsumer.mWindowHandle.token) {
                     requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
@@ -569,7 +563,6 @@
         private boolean mAddRecentsAnimationInputConsumerHandle;
 
         private boolean mInDrag;
-        private final Rect mTmpRect = new Rect();
 
         private void updateInputWindows(boolean inDrag) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -586,18 +579,15 @@
 
             resetInputConsumers(mInputTransaction);
             // Update recents input consumer layer if active
-            final ActivityRecord activeRecents = getWeak(mActiveRecentsActivity);
+            final Task activeRecents = getWeak(mActiveRecentsTask);
             if (mAddRecentsAnimationInputConsumerHandle && activeRecents != null
                     && activeRecents.getSurfaceControl() != null) {
                 WindowContainer layer = getWeak(mActiveRecentsLayerRef);
                 layer = layer != null ? layer : activeRecents;
                 // Handle edge-case for SUW where windows don't exist yet
                 if (layer.getSurfaceControl() != null) {
-                    final WindowState targetAppMainWindow = activeRecents.findMainWindow();
-                    if (targetAppMainWindow != null) {
-                        targetAppMainWindow.getBounds(mTmpRect);
-                        mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
-                    }
+                    mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(
+                            activeRecents.getBounds());
                     mRecentsAnimationInputConsumer.show(mInputTransaction, layer);
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
@@ -629,24 +619,6 @@
                 return;
             }
 
-            // This only works for legacy transitions.
-            final RecentsAnimationController recentsAnimationController =
-                    mService.getRecentsAnimationController();
-            final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
-                    && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
-            if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
-                if (recentsAnimationController.updateInputConsumerForApp(
-                        mRecentsAnimationInputConsumer.mWindowHandle)) {
-                    final DisplayArea targetDA =
-                            recentsAnimationController.getTargetAppDisplayArea();
-                    if (targetDA != null) {
-                        mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
-                        mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
-                        mAddRecentsAnimationInputConsumerHandle = false;
-                    }
-                }
-            }
-
             if (w.inPinnedWindowingMode()) {
                 if (mAddPipInputConsumerHandle) {
                     final Task rootTask = w.getTask().getRootTask();
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index baf0db2..0c0b794 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -65,6 +65,6 @@
     InsetsControlTarget getImeControlTarget();
 
     void dumpProto(ProtoOutputStream proto, long fieldId,
-                   @WindowTraceLogLevel int logLevel);
+                   @WindowTracingLogLevel int logLevel);
 }
 
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 62bef74..129078b 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -502,13 +502,6 @@
             // Notification shade has control anyways, no reason to force anything.
             return focusedWin;
         }
-        if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
-            ComponentName component = focusedWin.mActivityRecord != null
-                    ? focusedWin.mActivityRecord.mActivityComponent : null;
-            mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
-                    component, focusedWin.getRequestedVisibleTypes());
-            return mDisplayContent.mRemoteInsetsControlTarget;
-        }
         if (areTypesForciblyShowing(Type.statusBars())) {
             // Status bar is forcibly shown. We don't want the client to control the status bar, and
             // we will dispatch the real visibility of status bar to the client.
@@ -525,7 +518,17 @@
                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
             // window hides status bar, unless the notification shade can receive keys.
-            return mPolicy.getTopFullscreenOpaqueWindow();
+            if (remoteInsetsControllerControlsSystemBars(
+                    mPolicy.getTopFullscreenOpaqueWindow())) {
+                notifyRemoteInsetsController(mPolicy.getTopFullscreenOpaqueWindow());
+                return mDisplayContent.mRemoteInsetsControlTarget;
+            } else {
+                return mPolicy.getTopFullscreenOpaqueWindow();
+            }
+        }
+        if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+            notifyRemoteInsetsController(focusedWin);
+            return mDisplayContent.mRemoteInsetsControlTarget;
         }
         return focusedWin;
     }
@@ -562,13 +565,6 @@
                 return focusedWin;
             }
         }
-        if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
-            ComponentName component = focusedWin.mActivityRecord != null
-                    ? focusedWin.mActivityRecord.mActivityComponent : null;
-            mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
-                    component, focusedWin.getRequestedVisibleTypes());
-            return mDisplayContent.mRemoteInsetsControlTarget;
-        }
         if (areTypesForciblyShowing(Type.navigationBars())) {
             // Navigation bar is forcibly shown. We don't want the client to control the navigation
             // bar, and we will dispatch the real visibility of navigation bar to the client.
@@ -586,11 +582,31 @@
                 && (notificationShade == null || !notificationShade.canReceiveKeys())) {
             // Non-fullscreen focused window should not break the state that the top-fullscreen-app
             // window hides navigation bar, unless the notification shade can receive keys.
-            return mPolicy.getTopFullscreenOpaqueWindow();
+            if (remoteInsetsControllerControlsSystemBars(
+                    mPolicy.getTopFullscreenOpaqueWindow())) {
+                notifyRemoteInsetsController(mPolicy.getTopFullscreenOpaqueWindow());
+                return mDisplayContent.mRemoteInsetsControlTarget;
+            } else {
+                return mPolicy.getTopFullscreenOpaqueWindow();
+            }
+        }
+        if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
+            notifyRemoteInsetsController(focusedWin);
+            return mDisplayContent.mRemoteInsetsControlTarget;
         }
         return focusedWin;
     }
 
+    private void notifyRemoteInsetsController(@Nullable WindowState win) {
+        if (win == null) {
+            return;
+        }
+        ComponentName component = win.mActivityRecord != null
+                ? win.mActivityRecord.mActivityComponent : null;
+        mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
+                component, win.getRequestedVisibleTypes());
+    }
+
     boolean areTypesForciblyShowing(@InsetsType int types) {
         return (mForcedShowingTypes & types) == types;
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index f5c92f6..8f90b2d 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -411,7 +411,7 @@
             changed = true;
         }
         if (changed) {
-            mStateController.notifyControlChanged(mControlTarget);
+            mStateController.notifyControlChanged(mControlTarget, this);
         }
     }
 
@@ -556,11 +556,37 @@
         }
         mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
                 initiallyVisible, surfacePosition, getInsetsHint());
+        mStateController.notifySurfaceTransactionReady(this, getSurfaceTransactionId(leash), true);
 
         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
     }
 
+    private long getSurfaceTransactionId(SurfaceControl leash) {
+        // Here returns mNativeObject (long) as the ID instead of the leash itself so that
+        // InsetsStateController won't keep referencing the leash unexpectedly.
+        return leash != null ? leash.mNativeObject : 0;
+    }
+
+    /**
+     * This is called when the surface transaction of the leash initialization has been committed.
+     *
+     * @param id Indicates which transaction is committed so that stale callbacks can be dropped.
+     */
+    void onSurfaceTransactionCommitted(long id) {
+        if (mIsLeashReadyForDispatching) {
+            return;
+        }
+        if (mControl == null) {
+            return;
+        }
+        if (id != getSurfaceTransactionId(mControl.getLeash())) {
+            return;
+        }
+        mIsLeashReadyForDispatching = true;
+        mStateController.notifySurfaceTransactionReady(this, 0, false);
+    }
+
     void startSeamlessRotation() {
         if (!mSeamlessRotating) {
             mSeamlessRotating = true;
@@ -582,10 +608,6 @@
         return true;
     }
 
-    void onSurfaceTransactionApplied() {
-        mIsLeashReadyForDispatching = true;
-    }
-
     void setClientVisible(boolean clientVisible) {
         if (mClientVisible == clientVisible) {
             return;
@@ -612,8 +634,9 @@
                 mServerVisible, mClientVisible);
     }
 
-    protected boolean isLeashReadyForDispatching() {
-        return mIsLeashReadyForDispatching;
+    protected boolean isLeashReadyForDispatching(InsetsControlTarget target) {
+        // If the target is not the control target, we are ready for dispatching a null-leash to it.
+        return target != mControlTarget || mIsLeashReadyForDispatching;
     }
 
     /**
@@ -626,7 +649,7 @@
     @Nullable
     InsetsSourceControl getControl(InsetsControlTarget target) {
         if (target == mControlTarget) {
-            if (!isLeashReadyForDispatching() && mControl != null) {
+            if (!isLeashReadyForDispatching(target) && mControl != null) {
                 // The surface transaction of preparing leash is not applied yet. We don't send it
                 // to the client in case that the client applies its transaction sooner than ours
                 // that we could unexpectedly overwrite the surface state.
@@ -725,7 +748,7 @@
         }
     }
 
-    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
+    void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel) {
         final long token = proto.start(fieldId);
         mSource.dumpDebug(proto, SOURCE);
         mTmpRect.dumpDebug(proto, FRAME);
@@ -799,6 +822,7 @@
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
                 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
+                mStateController.notifySurfaceTransactionReady(InsetsSourceProvider.this, 0, false);
                 mControl = null;
                 mControlTarget = null;
                 mAdapter = null;
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 9c2a8de..0daddc0 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -34,6 +34,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.SparseArray;
+import android.util.SparseLongArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
@@ -59,13 +60,14 @@
     private final DisplayContent mDisplayContent;
 
     private final SparseArray<InsetsSourceProvider> mProviders = new SparseArray<>();
+    private final SparseLongArray mSurfaceTransactionIds = new SparseLongArray();
     private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
             mControlTargetProvidersMap = new ArrayMap<>();
+    private final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>>
+            mPendingTargetProvidersMap = new ArrayMap<>();
     private final SparseArray<InsetsControlTarget> mIdControlTargetMap = new SparseArray<>();
     private final SparseArray<InsetsControlTarget> mIdFakeControlTargetMap = new SparseArray<>();
 
-    private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
-
     private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
         if (w.isReadyToDispatchInsetsState()) {
             w.notifyInsetsChanged();
@@ -327,11 +329,11 @@
         }
         if (lastTarget != null) {
             removeFromControlMaps(lastTarget, provider, fake);
-            mPendingControlChanged.add(lastTarget);
+            addToPendingControlMaps(lastTarget, provider);
         }
         if (target != null) {
             addToControlMaps(target, provider, fake);
-            mPendingControlChanged.add(target);
+            addToPendingControlMaps(target, provider);
         }
     }
 
@@ -364,8 +366,15 @@
         }
     }
 
-    void notifyControlChanged(InsetsControlTarget target) {
-        mPendingControlChanged.add(target);
+    private void addToPendingControlMaps(@NonNull InsetsControlTarget target,
+            InsetsSourceProvider provider) {
+        final ArrayList<InsetsSourceProvider> array =
+                mPendingTargetProvidersMap.computeIfAbsent(target, key -> new ArrayList<>());
+        array.add(provider);
+    }
+
+    void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) {
+        addToPendingControlMaps(target, provider);
         notifyPendingInsetsControlChanged();
 
         if (android.view.inputmethod.Flags.refactorInsetsController()) {
@@ -376,26 +385,58 @@
         }
     }
 
+    void notifySurfaceTransactionReady(InsetsSourceProvider provider, long id, boolean ready) {
+        if (ready) {
+            mSurfaceTransactionIds.put(provider.getSource().getId(), id);
+        } else {
+            mSurfaceTransactionIds.delete(provider.getSource().getId());
+        }
+    }
+
     private void notifyPendingInsetsControlChanged() {
-        if (mPendingControlChanged.isEmpty()) {
+        if (mPendingTargetProvidersMap.isEmpty()) {
             return;
         }
+        final int size = mSurfaceTransactionIds.size();
+        final SparseLongArray surfaceTransactionIds = new SparseLongArray(size);
+        for (int i = 0; i < size; i++) {
+            surfaceTransactionIds.append(
+                    mSurfaceTransactionIds.keyAt(i), mSurfaceTransactionIds.valueAt(i));
+        }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                final InsetsSourceProvider provider = mProviders.valueAt(i);
-                provider.onSurfaceTransactionApplied();
+            for (int i = 0; i < size; i++) {
+                final int sourceId = surfaceTransactionIds.keyAt(i);
+                final InsetsSourceProvider provider = mProviders.get(sourceId);
+                if (provider == null) {
+                    continue;
+                }
+                provider.onSurfaceTransactionCommitted(surfaceTransactionIds.valueAt(i));
             }
             final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>();
             int displayId = mDisplayContent.getDisplayId();
-            for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
-                final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
-                controlTarget.notifyInsetsControlChanged(displayId);
-                if (mControlTargetProvidersMap.containsKey(controlTarget)) {
-                    // We only collect targets who get controls, not lose controls.
-                    newControlTargets.add(controlTarget);
+            final ArrayMap<InsetsControlTarget, ArrayList<InsetsSourceProvider>> pendingControlMap =
+                    mPendingTargetProvidersMap;
+            for (int i = pendingControlMap.size() - 1; i >= 0; i--) {
+                final InsetsControlTarget target = pendingControlMap.keyAt(i);
+                final ArrayList<InsetsSourceProvider> providers = pendingControlMap.valueAt(i);
+                for (int p = providers.size() - 1; p >= 0; p--) {
+                    final InsetsSourceProvider provider = providers.get(p);
+                    if (provider.isLeashReadyForDispatching(target)) {
+                        // Stop waiting for this provider.
+                        providers.remove(p);
+                    }
+                }
+                if (providers.isEmpty()) {
+                    pendingControlMap.removeAt(i);
+
+                    // All controls of this target are ready to be dispatched.
+                    target.notifyInsetsControlChanged(displayId);
+                    if (mControlTargetProvidersMap.containsKey(target)) {
+                        // We only collect targets who get controls, not lose controls.
+                        newControlTargets.add(target);
+                    }
                 }
             }
-            mPendingControlChanged.clear();
 
             // This updates the insets visibilities AFTER sending current insets state and controls
             // to the clients, so that the clients can change the current visibilities to the
@@ -424,7 +465,7 @@
      * @param target the control target to check.
      */
     boolean hasPendingControls(@NonNull InsetsControlTarget target) {
-        return mPendingControlChanged.contains(target);
+        return mPendingTargetProvidersMap.containsKey(target);
     }
 
     void dump(String prefix, PrintWriter pw) {
@@ -462,7 +503,7 @@
         }
     }
 
-    void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+    void dumpDebug(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
         for (int i = mProviders.size() - 1; i >= 0; i--) {
             final InsetsSourceProvider provider = mProviders.valueAt(i);
             provider.dumpDebug(proto,
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index e18ca85..5d8a96c 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -51,6 +51,7 @@
 import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -619,7 +620,8 @@
             }
             state.mKeyguardGoingAway = false;
             state.writeEventLog("goingAwayTimeout");
-            mWindowManager.mPolicy.startKeyguardExitAnimation(0);
+            mWindowManager.mPolicy.startKeyguardExitAnimation(
+                    SystemClock.uptimeMillis() - GOING_AWAY_TIMEOUT_MS);
         }
     };
 
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3fc5eaf..252590e 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -38,9 +38,6 @@
 
 import com.android.server.UiThread;
 
-import java.util.function.BooleanSupplier;
-import java.util.function.DoubleSupplier;
-import java.util.function.IntSupplier;
 import java.util.function.Supplier;
 
 /**
@@ -54,12 +51,6 @@
 
     private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
     private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
-    private final BooleanSupplier mAreCornersRounded;
-    private final Supplier<Color> mColorSupplier;
-    // Parameters for "blurred wallpaper" letterbox background.
-    private final BooleanSupplier mHasWallpaperBackgroundSupplier;
-    private final IntSupplier mBlurRadiusSupplier;
-    private final DoubleSupplier mDarkScrimAlphaSupplier;
     private final Supplier<SurfaceControl> mParentSurfaceSupplier;
 
     private final Rect mOuter = new Rect();
@@ -77,6 +68,8 @@
     private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
     @NonNull
     private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+    @NonNull
+    private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
 
     /**
      * Constructs a Letterbox.
@@ -85,24 +78,14 @@
      */
     public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
-            BooleanSupplier areCornersRounded,
-            Supplier<Color> colorSupplier,
-            BooleanSupplier hasWallpaperBackgroundSupplier,
-            IntSupplier blurRadiusSupplier,
-            DoubleSupplier darkScrimAlphaSupplier,
             @NonNull AppCompatReachabilityPolicy appCompatReachabilityPolicy,
+            @NonNull AppCompatLetterboxOverrides appCompatLetterboxOverrides,
             Supplier<SurfaceControl> parentSurface) {
         mSurfaceControlFactory = surfaceControlFactory;
         mTransactionFactory = transactionFactory;
-        mAreCornersRounded = areCornersRounded;
-        mColorSupplier = colorSupplier;
-        mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier;
-        mBlurRadiusSupplier = blurRadiusSupplier;
-        mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
         mAppCompatReachabilityPolicy = appCompatReachabilityPolicy;
+        mAppCompatLetterboxOverrides = appCompatLetterboxOverrides;
         mParentSurfaceSupplier = parentSurface;
-        // TODO Remove after Letterbox refactoring.
-        mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(this::getInnerFrame);
     }
 
     /**
@@ -252,7 +235,8 @@
      * Returns {@code true} when using {@link #mFullWindowSurface} instead of {@link mSurfaces}.
      */
     private boolean useFullWindowSurface() {
-        return mAreCornersRounded.getAsBoolean() || mHasWallpaperBackgroundSupplier.getAsBoolean();
+        return mAppCompatLetterboxOverrides.shouldLetterboxHaveRoundedCorners()
+                || mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox();
     }
 
     private final class TapEventReceiver extends InputEventReceiver {
@@ -431,7 +415,7 @@
                     createSurface(t);
                 }
 
-                mColor = mColorSupplier.get();
+                mColor = mAppCompatLetterboxOverrides.getLetterboxBackgroundColor();
                 mParentSurface = mParentSurfaceSupplier.get();
                 t.setColor(mSurface, getRgbColorArray());
                 t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
@@ -439,7 +423,8 @@
                         mSurfaceFrameRelative.height());
                 t.reparent(mSurface, mParentSurface);
 
-                mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.getAsBoolean();
+                mHasWallpaperBackground = mAppCompatLetterboxOverrides
+                        .hasWallpaperBackgroundForLetterbox();
                 updateAlphaAndBlur(t);
 
                 t.show(mSurface);
@@ -460,17 +445,19 @@
                 t.setBackgroundBlurRadius(mSurface, 0);
                 return;
             }
-            final float alpha = (float) mDarkScrimAlphaSupplier.getAsDouble();
+            final float alpha = mAppCompatLetterboxOverrides.getLetterboxWallpaperDarkScrimAlpha();
             t.setAlpha(mSurface, alpha);
 
             // Translucent dark scrim can be shown without blur.
-            if (mBlurRadiusSupplier.getAsInt() <= 0) {
+            final int blurRadiusPx = mAppCompatLetterboxOverrides
+                    .getLetterboxWallpaperBlurRadiusPx();
+            if (blurRadiusPx <= 0) {
                 // Removing pre-exesting blur
                 t.setBackgroundBlurRadius(mSurface, 0);
                 return;
             }
 
-            t.setBackgroundBlurRadius(mSurface, mBlurRadiusSupplier.getAsInt());
+            t.setBackgroundBlurRadius(mSurface, blurRadiusPx);
         }
 
         private float[] getRgbColorArray() {
@@ -487,8 +474,9 @@
                     // and mParentSurface may never be updated in applySurfaceChanges but this
                     // doesn't mean that update is needed.
                     || !mSurfaceFrameRelative.isEmpty()
-                    && (mHasWallpaperBackgroundSupplier.getAsBoolean() != mHasWallpaperBackground
-                    || !mColorSupplier.get().equals(mColor)
+                    && (mAppCompatLetterboxOverrides.hasWallpaperBackgroundForLetterbox()
+                        != mHasWallpaperBackground
+                    || !mAppCompatLetterboxOverrides.getLetterboxBackgroundColor().equals(mColor)
                     || mParentSurfaceSupplier.get() != mParentSurface);
         }
     }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
deleted file mode 100644
index 4740fc4..0000000
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ /dev/null
@@ -1,585 +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.server.wm;
-
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-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.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager.TaskDescription;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.Slog;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.RoundedCorner;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.LetterboxDetails;
-import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
-
-import java.io.PrintWriter;
-
-/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
-// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
-// SizeCompatTests and LetterboxTests but not all.
-final class LetterboxUiController {
-
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
-
-    private final Point mTmpPoint = new Point();
-
-    private final AppCompatConfiguration mAppCompatConfiguration;
-
-    private final ActivityRecord mActivityRecord;
-
-    // TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
-    @NonNull
-    private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
-    @NonNull
-    private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
-    @NonNull
-    private final TransparentPolicy mTransparentPolicy;
-    @NonNull
-    private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
-
-    private boolean mShowWallpaperForLetterboxBackground;
-
-    @Nullable
-    private Letterbox mLetterbox;
-
-    private boolean mLastShouldShowLetterboxUi;
-
-    LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
-        mAppCompatConfiguration = wmService.mAppCompatConfiguration;
-        // Given activityRecord may not be fully constructed since LetterboxUiController
-        // is created in its constructor. It shouldn't be used in this constructor but it's safe
-        // to use it after since controller is only used in ActivityRecord.
-        mActivityRecord = activityRecord;
-        // TODO(b/356385137): Remove these we added to make dependencies temporarily explicit.
-        mAppCompatReachabilityOverrides = mActivityRecord.mAppCompatController
-                .getAppCompatReachabilityOverrides();
-        mAppCompatReachabilityPolicy = mActivityRecord.mAppCompatController
-                .getAppCompatReachabilityPolicy();
-        mTransparentPolicy = mActivityRecord.mAppCompatController.getTransparentPolicy();
-        mAppCompatOrientationOverrides = mActivityRecord.mAppCompatController
-                .getAppCompatOrientationOverrides();
-
-    }
-
-    /** Cleans up {@link Letterbox} if it exists.*/
-    void destroy() {
-        if (mLetterbox != null) {
-            mLetterbox.destroy();
-            mLetterbox = null;
-            // TODO Remove after Letterbox refactoring.
-            mAppCompatReachabilityPolicy.setLetterboxInnerBoundsSupplier(null);
-        }
-    }
-
-    void onMovedToDisplay(int displayId) {
-        if (mLetterbox != null) {
-            mLetterbox.onMovedToDisplay(displayId);
-        }
-    }
-
-    boolean hasWallpaperBackgroundForLetterbox() {
-        return mShowWallpaperForLetterboxBackground;
-    }
-
-    /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
-    Rect getLetterboxInsets() {
-        if (mLetterbox != null) {
-            return mLetterbox.getInsets();
-        } else {
-            return new Rect();
-        }
-    }
-
-    /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
-    void getLetterboxInnerBounds(Rect outBounds) {
-        if (mLetterbox != null) {
-            outBounds.set(mLetterbox.getInnerFrame());
-            final WindowState w = mActivityRecord.findMainWindow();
-            if (w != null) {
-                adjustBoundsForTaskbar(w, outBounds);
-            }
-        } else {
-            outBounds.setEmpty();
-        }
-    }
-
-    /** Gets the outer bounds of letterbox. The bounds will be empty if there is no letterbox. */
-    private void getLetterboxOuterBounds(Rect outBounds) {
-        if (mLetterbox != null) {
-            outBounds.set(mLetterbox.getOuterFrame());
-        } else {
-            outBounds.setEmpty();
-        }
-    }
-
-    /**
-     * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
-     *     when the current activity is displayed.
-     */
-    boolean isFullyTransparentBarAllowed(Rect rect) {
-        return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
-    }
-
-    void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
-        updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction(),
-                mActivityRecord.getPendingTransaction());
-    }
-
-    void updateLetterboxSurfaceIfNeeded(WindowState winHint, @NonNull Transaction t,
-            @NonNull Transaction inputT) {
-        if (shouldNotLayoutLetterbox(winHint)) {
-            return;
-        }
-        layoutLetterboxIfNeeded(winHint);
-        if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
-            mLetterbox.applySurfaceChanges(t, inputT);
-        }
-    }
-
-    void layoutLetterboxIfNeeded(WindowState w) {
-        if (shouldNotLayoutLetterbox(w)) {
-            return;
-        }
-        updateRoundedCornersIfNeeded(w);
-        updateWallpaperForLetterbox(w);
-        if (shouldShowLetterboxUi(w)) {
-            if (mLetterbox == null) {
-                mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
-                        mActivityRecord.mWmService.mTransactionFactory,
-                        this::shouldLetterboxHaveRoundedCorners,
-                        this::getLetterboxBackgroundColor,
-                        this::hasWallpaperBackgroundForLetterbox,
-                        this::getLetterboxWallpaperBlurRadiusPx,
-                        this::getLetterboxWallpaperDarkScrimAlpha,
-                        mAppCompatReachabilityPolicy,
-                        this::getLetterboxParentSurface);
-                mLetterbox.attachInput(w);
-            }
-
-            if (mActivityRecord.isInLetterboxAnimation()) {
-                // In this case we attach the letterbox to the task instead of the activity.
-                mActivityRecord.getTask().getPosition(mTmpPoint);
-            } else {
-                mActivityRecord.getPosition(mTmpPoint);
-            }
-
-            // Get the bounds of the "space-to-fill". The transformed bounds have the highest
-            // priority because the activity is launched in a rotated environment. In multi-window
-            // mode, the taskFragment-level represents this for both split-screen
-            // and activity-embedding. In fullscreen-mode, the task container does
-            // (since the orientation letterbox is also applied to the task).
-            final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
-            final Rect spaceToFill = transformedBounds != null
-                    ? transformedBounds
-                    : mActivityRecord.inMultiWindowMode()
-                            ? mActivityRecord.getTaskFragment().getBounds()
-                            : mActivityRecord.getRootTask().getParent().getBounds();
-            // In case of translucent activities an option is to use the WindowState#getFrame() of
-            // the first opaque activity beneath. In some cases (e.g. an opaque activity is using
-            // non MATCH_PARENT layouts or a Dialog theme) this might not provide the correct
-            // information and in particular it might provide a value for a smaller area making
-            // the letterbox overlap with the translucent activity's frame.
-            // If we use WindowState#getFrame() for the translucent activity's letterbox inner
-            // frame, the letterbox will then be overlapped with the translucent activity's frame.
-            // Because the surface layer of letterbox is lower than an activity window, this
-            // won't crop the content, but it may affect other features that rely on values stored
-            // in mLetterbox, e.g. transitions, a status bar scrim and recents preview in Launcher
-            // For this reason we use ActivityRecord#getBounds() that the translucent activity
-            // inherits from the first opaque activity beneath and also takes care of the scaling
-            // in case of activities in size compat mode.
-            final Rect innerFrame =
-                    mTransparentPolicy.isRunning() ? mActivityRecord.getBounds() : w.getFrame();
-            mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
-            if (mAppCompatReachabilityOverrides.isDoubleTapEvent()) {
-                // We need to notify Shell that letterbox position has changed.
-                mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
-            }
-        } else if (mLetterbox != null) {
-            mLetterbox.hide();
-        }
-    }
-
-    SurfaceControl getLetterboxParentSurface() {
-        if (mActivityRecord.isInLetterboxAnimation()) {
-            return mActivityRecord.getTask().getSurfaceControl();
-        }
-        return mActivityRecord.getSurfaceControl();
-    }
-
-    private static boolean shouldNotLayoutLetterbox(WindowState w) {
-        if (w == null) {
-            return true;
-        }
-        final int type = w.mAttrs.type;
-        // Allow letterbox to be displayed early for base application or application starting
-        // windows even if it is not on the top z order to prevent flickering when the
-        // letterboxed window is brought to the top
-        return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING)
-                || w.mAnimatingExit;
-    }
-
-    private boolean shouldLetterboxHaveRoundedCorners() {
-        // TODO(b/214030873): remove once background is drawn for transparent activities
-        // Letterbox shouldn't have rounded corners if the activity is transparent
-        return mAppCompatConfiguration.isLetterboxActivityCornersRounded()
-                && mActivityRecord.fillsParent();
-    }
-
-    boolean isLetterboxEducationEnabled() {
-        return mAppCompatConfiguration.getIsEducationEnabled();
-    }
-
-    @VisibleForTesting
-    boolean shouldShowLetterboxUi(WindowState mainWindow) {
-        if (mAppCompatOrientationOverrides.getIsRelaunchingAfterRequestedOrientationChanged()) {
-            return mLastShouldShowLetterboxUi;
-        }
-
-        final boolean shouldShowLetterboxUi =
-                (mActivityRecord.isInLetterboxAnimation() || mActivityRecord.isVisible()
-                        || mActivityRecord.isVisibleRequested())
-                && mainWindow.areAppWindowBoundsLetterboxed()
-                // Check for FLAG_SHOW_WALLPAPER explicitly instead of using
-                // WindowContainer#showWallpaper because the later will return true when this
-                // activity is using blurred wallpaper for letterbox background.
-                && (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) == 0;
-
-        mLastShouldShowLetterboxUi = shouldShowLetterboxUi;
-
-        return shouldShowLetterboxUi;
-    }
-
-    Color getLetterboxBackgroundColor() {
-        final WindowState w = mActivityRecord.findMainWindow();
-        if (w == null || w.isLetterboxedForDisplayCutout()) {
-            return Color.valueOf(Color.BLACK);
-        }
-        @LetterboxBackgroundType int letterboxBackgroundType =
-                mAppCompatConfiguration.getLetterboxBackgroundType();
-        TaskDescription taskDescription = mActivityRecord.taskDescription;
-        switch (letterboxBackgroundType) {
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
-                if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
-                    return Color.valueOf(taskDescription.getBackgroundColorFloating());
-                }
-                break;
-            case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
-                if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
-                    return Color.valueOf(taskDescription.getBackgroundColor());
-                }
-                break;
-            case LETTERBOX_BACKGROUND_WALLPAPER:
-                if (hasWallpaperBackgroundForLetterbox()) {
-                    // Color is used for translucent scrim that dims wallpaper.
-                    return mAppCompatConfiguration.getLetterboxBackgroundColor();
-                }
-                Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
-                        + "blur is not supported by a device or not supported in the current "
-                        + "window configuration or both alpha scrim and blur radius aren't "
-                        + "provided so using solid color background");
-                break;
-            case LETTERBOX_BACKGROUND_SOLID_COLOR:
-                return mAppCompatConfiguration.getLetterboxBackgroundColor();
-            default:
-                throw new AssertionError(
-                    "Unexpected letterbox background type: " + letterboxBackgroundType);
-        }
-        // If picked option configured incorrectly or not supported then default to a solid color
-        // background.
-        return mAppCompatConfiguration.getLetterboxBackgroundColor();
-    }
-
-    private void updateRoundedCornersIfNeeded(final WindowState mainWindow) {
-        final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
-        if (windowSurface == null || !windowSurface.isValid()) {
-            return;
-        }
-
-        // cropBounds must be non-null for the cornerRadius to be ever applied.
-        mActivityRecord.getSyncTransaction()
-                .setCrop(windowSurface, getCropBoundsIfNeeded(mainWindow))
-                .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
-    }
-
-    @VisibleForTesting
-    @Nullable
-    Rect getCropBoundsIfNeeded(final WindowState mainWindow) {
-        if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
-            // We don't want corner radius on the window.
-            // In the case the ActivityRecord requires a letterboxed animation we never want
-            // rounded corners on the window because rounded corners are applied at the
-            // animation-bounds surface level and rounded corners on the window would interfere
-            // with that leading to unexpected rounded corner positioning during the animation.
-            return null;
-        }
-
-        final Rect cropBounds = new Rect(mActivityRecord.getBounds());
-
-        // In case of translucent activities we check if the requested size is different from
-        // the size provided using inherited bounds. In that case we decide to not apply rounded
-        // corners because we assume the specific layout would. This is the case when the layout
-        // of the translucent activity uses only a part of all the bounds because of the use of
-        // LayoutParams.WRAP_CONTENT.
-        if (mTransparentPolicy.isRunning() && (cropBounds.width() != mainWindow.mRequestedWidth
-                || cropBounds.height() != mainWindow.mRequestedHeight)) {
-            return null;
-        }
-
-        // It is important to call {@link #adjustBoundsIfNeeded} before {@link cropBounds.offsetTo}
-        // because taskbar bounds used in {@link #adjustBoundsIfNeeded}
-        // are in screen coordinates
-        adjustBoundsForTaskbar(mainWindow, cropBounds);
-
-        final float scale = mainWindow.mInvGlobalScale;
-        if (scale != 1f && scale > 0f) {
-            cropBounds.scale(scale);
-        }
-
-        // ActivityRecord bounds are in screen coordinates while (0,0) for activity's surface
-        // control is in the top left corner of an app window so offsetting bounds
-        // accordingly.
-        cropBounds.offsetTo(0, 0);
-        return cropBounds;
-    }
-
-    private boolean requiresRoundedCorners(final WindowState mainWindow) {
-        return isLetterboxedNotForDisplayCutout(mainWindow)
-                && mAppCompatConfiguration.isLetterboxActivityCornersRounded();
-    }
-
-    // Returns rounded corners radius the letterboxed activity should have based on override in
-    // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
-    // Device corners can be different on the right and left sides, but we use the same radius
-    // for all corners for consistency and pick a minimal bottom one for consistency with a
-    // taskbar rounded corners.
-    int getRoundedCornersRadius(final WindowState mainWindow) {
-        if (!requiresRoundedCorners(mainWindow)) {
-            return 0;
-        }
-
-        final int radius;
-        if (mAppCompatConfiguration.getLetterboxActivityCornersRadius() >= 0) {
-            radius = mAppCompatConfiguration.getLetterboxActivityCornersRadius();
-        } else {
-            final InsetsState insetsState = mainWindow.getInsetsState();
-            radius = Math.min(
-                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
-                    getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
-        }
-
-        final float scale = mainWindow.mInvGlobalScale;
-        return (scale != 1f && scale > 0f) ? (int) (scale * radius) : radius;
-    }
-
-    /**
-     * Returns the taskbar in case it is visible and expanded in height, otherwise returns null.
-     */
-    @VisibleForTesting
-    @Nullable
-    InsetsSource getExpandedTaskbarOrNull(final WindowState mainWindow) {
-        final InsetsState state = mainWindow.getInsetsState();
-        for (int i = state.sourceSize() - 1; i >= 0; i--) {
-            final InsetsSource source = state.sourceAt(i);
-            if (source.getType() == WindowInsets.Type.navigationBars()
-                    && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)
-                    && source.isVisible()) {
-                return source;
-            }
-        }
-        return null;
-    }
-
-    private void adjustBoundsForTaskbar(final WindowState mainWindow, final Rect bounds) {
-        // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
-        // an insets frame is equal to a navigation bar which shouldn't affect position of
-        // rounded corners since apps are expected to handle navigation bar inset.
-        // This condition checks whether the taskbar is visible.
-        // Do not crop the taskbar inset if the window is in immersive mode - the user can
-        // swipe to show/hide the taskbar as an overlay.
-        // Adjust the bounds only in case there is an expanded taskbar,
-        // otherwise the rounded corners will be shown behind the navbar.
-        final InsetsSource expandedTaskbarOrNull = getExpandedTaskbarOrNull(mainWindow);
-        if (expandedTaskbarOrNull != null) {
-            // Rounded corners should be displayed above the expanded taskbar.
-            bounds.bottom = Math.min(bounds.bottom, expandedTaskbarOrNull.getFrame().top);
-        }
-    }
-
-    private int getInsetsStateCornerRadius(
-                InsetsState insetsState, @RoundedCorner.Position int position) {
-        RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
-        return corner == null ? 0 : corner.getRadius();
-    }
-
-    private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
-        return shouldShowLetterboxUi(mainWindow)
-                && !mainWindow.isLetterboxedForDisplayCutout();
-    }
-
-    private void updateWallpaperForLetterbox(WindowState mainWindow) {
-        @LetterboxBackgroundType int letterboxBackgroundType =
-                mAppCompatConfiguration.getLetterboxBackgroundType();
-        boolean wallpaperShouldBeShown =
-                letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
-                        // Don't use wallpaper as a background if letterboxed for display cutout.
-                        && isLetterboxedNotForDisplayCutout(mainWindow)
-                        // Check that dark scrim alpha or blur radius are provided
-                        && (getLetterboxWallpaperBlurRadiusPx() > 0
-                                || getLetterboxWallpaperDarkScrimAlpha() > 0)
-                        // Check that blur is supported by a device if blur radius is provided.
-                        && (getLetterboxWallpaperBlurRadiusPx() <= 0
-                                || isLetterboxWallpaperBlurSupported());
-        if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
-            mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
-            mActivityRecord.requestUpdateWallpaperIfNeeded();
-        }
-    }
-
-    private int getLetterboxWallpaperBlurRadiusPx() {
-        int blurRadius = mAppCompatConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx();
-        return Math.max(blurRadius, 0);
-    }
-
-    private float getLetterboxWallpaperDarkScrimAlpha() {
-        float alpha = mAppCompatConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
-        // No scrim by default.
-        return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
-    }
-
-    private boolean isLetterboxWallpaperBlurSupported() {
-        return mAppCompatConfiguration.mContext.getSystemService(WindowManager.class)
-                .isCrossWindowBlurEnabled();
-    }
-
-    void dump(PrintWriter pw, String prefix) {
-        final WindowState mainWin = mActivityRecord.findMainWindow();
-        if (mainWin == null) {
-            return;
-        }
-
-        pw.println(prefix + "isTransparentPolicyRunning="
-                + mActivityRecord.mAppCompatController.getTransparentPolicy().isRunning());
-
-        boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed();
-        pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
-        if (!areBoundsLetterboxed) {
-            return;
-        }
-
-        pw.println(prefix + "  letterboxReason="
-                + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin));
-        pw.println(prefix + "  activityAspectRatio="
-                + AppCompatUtils.computeAspectRatio(mActivityRecord.getBounds()));
-
-        boolean shouldShowLetterboxUi = shouldShowLetterboxUi(mainWin);
-        pw.println(prefix + "shouldShowLetterboxUi=" + shouldShowLetterboxUi);
-
-        if (!shouldShowLetterboxUi) {
-            return;
-        }
-        pw.println(prefix + "  isVerticalThinLetterboxed="
-                + mAppCompatReachabilityOverrides.isVerticalThinLetterboxed());
-        pw.println(prefix + "  isHorizontalThinLetterboxed="
-                + mAppCompatReachabilityOverrides.isHorizontalThinLetterboxed());
-        pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
-                getLetterboxBackgroundColor().toArgb()));
-        pw.println(prefix + "  letterboxBackgroundType="
-                + letterboxBackgroundTypeToString(
-                        mAppCompatConfiguration.getLetterboxBackgroundType()));
-        pw.println(prefix + "  letterboxCornerRadius="
-                + getRoundedCornersRadius(mainWin));
-        if (mAppCompatConfiguration.getLetterboxBackgroundType()
-                == LETTERBOX_BACKGROUND_WALLPAPER) {
-            pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
-                    + isLetterboxWallpaperBlurSupported());
-            pw.println(prefix + "  letterboxBackgroundWallpaperDarkScrimAlpha="
-                    + getLetterboxWallpaperDarkScrimAlpha());
-            pw.println(prefix + "  letterboxBackgroundWallpaperBlurRadius="
-                    + getLetterboxWallpaperBlurRadiusPx());
-        }
-        final AppCompatReachabilityOverrides reachabilityOverrides = mActivityRecord
-                .mAppCompatController.getAppCompatReachabilityOverrides();
-        pw.println(prefix + "  isHorizontalReachabilityEnabled="
-                + reachabilityOverrides.isHorizontalReachabilityEnabled());
-        pw.println(prefix + "  isVerticalReachabilityEnabled="
-                + reachabilityOverrides.isVerticalReachabilityEnabled());
-        pw.println(prefix + "  letterboxHorizontalPositionMultiplier="
-                + mAppCompatReachabilityOverrides.getHorizontalPositionMultiplier(mActivityRecord
-                    .getParent().getConfiguration()));
-        pw.println(prefix + "  letterboxVerticalPositionMultiplier="
-                + mAppCompatReachabilityOverrides.getVerticalPositionMultiplier(mActivityRecord
-                    .getParent().getConfiguration()));
-        pw.println(prefix + "  letterboxPositionForHorizontalReachability="
-                + AppCompatConfiguration.letterboxHorizontalReachabilityPositionToString(
-                mAppCompatConfiguration.getLetterboxPositionForHorizontalReachability(false)));
-        pw.println(prefix + "  letterboxPositionForVerticalReachability="
-                + AppCompatConfiguration.letterboxVerticalReachabilityPositionToString(
-                mAppCompatConfiguration.getLetterboxPositionForVerticalReachability(false)));
-        pw.println(prefix + "  fixedOrientationLetterboxAspectRatio="
-                + mAppCompatConfiguration.getFixedOrientationLetterboxAspectRatio());
-        pw.println(prefix + "  defaultMinAspectRatioForUnresizableApps="
-                + mAppCompatConfiguration.getDefaultMinAspectRatioForUnresizableApps());
-        pw.println(prefix + "  isSplitScreenAspectRatioForUnresizableAppsEnabled="
-                + mAppCompatConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
-        pw.println(prefix + "  isDisplayAspectRatioEnabledForFixedOrientationLetterbox="
-                + mAppCompatConfiguration
-                .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox());
-    }
-
-    @Nullable
-    LetterboxDetails getLetterboxDetails() {
-        final WindowState w = mActivityRecord.findMainWindow();
-        if (mLetterbox == null || w == null || w.isLetterboxedForDisplayCutout()) {
-            return null;
-        }
-        Rect letterboxInnerBounds = new Rect();
-        Rect letterboxOuterBounds = new Rect();
-        getLetterboxInnerBounds(letterboxInnerBounds);
-        getLetterboxOuterBounds(letterboxOuterBounds);
-
-        if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) {
-            return null;
-        }
-
-        return new LetterboxDetails(
-                letterboxInnerBounds,
-                letterboxOuterBounds,
-                w.mAttrs.insetsFlags.appearance
-        );
-    }
-}
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index 1a895ea..403d3bd 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -91,7 +91,6 @@
         return (transit == TRANSIT_OLD_TASK_OPEN || transit == TRANSIT_OLD_TASK_TO_FRONT
                 || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
                 && displayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
-                && service.getRecentsAnimationController() == null
                 && displayContent.getAsyncRotationController() == null;
     }
 
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 60454fc..5d6d8bc 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -24,9 +24,11 @@
 per-file Background*Start* = set noparent
 per-file Background*Start* = file:/BAL_OWNERS
 per-file Background*Start* = [email protected], [email protected]
+per-file BackgroundLaunchProcessController.java = file:/BAL_OWNERS
 
 # File related to activity callers
 per-file ActivityCallerState.java = file:/core/java/android/app/COMPONENT_CALLER_OWNERS
 
 # Files related to tracing
 per-file *TransitionTracer.java = file:platform/development:/tools/winscope/OWNERS
+per-file *WindowTracing* = file:platform/development:/tools/winscope/OWNERS
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index c592caf..c06efc7 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -17,82 +17,54 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
-import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
 import static com.android.server.wm.ActivityRecord.State.STOPPING;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
-import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove;
 
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.content.ComponentName;
 import android.content.Intent;
-import android.os.RemoteException;
-import android.os.Trace;
 import android.util.Slog;
-import android.view.IRecentsAnimationRunner;
 
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
-import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
-import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener;
 
 /**
  * Manages the recents animation, including the reordering of the root tasks for the transition and
  * cleanup. See {@link com.android.server.wm.RecentsAnimationController}.
  */
-class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChangedListener {
+class RecentsAnimation {
     private static final String TAG = RecentsAnimation.class.getSimpleName();
 
-    private final ActivityTaskManagerService mService;
     private final ActivityTaskSupervisor mTaskSupervisor;
     private final ActivityStartController mActivityStartController;
-    private final WindowManagerService mWindowManager;
     private final TaskDisplayArea mDefaultTaskDisplayArea;
     private final Intent mTargetIntent;
     private final ComponentName mRecentsComponent;
     private final @Nullable String mRecentsFeatureId;
     private final int mRecentsUid;
-    private final @Nullable WindowProcessController mCaller;
     private final int mUserId;
     private final int mTargetActivityType;
 
-    /**
-     * The activity which has been launched behind. We need to remember the activity because the
-     * target root task may have other activities, then we are able to restore the launch-behind
-     * state for the exact activity.
-     */
-    private ActivityRecord mLaunchedTargetActivity;
-
-    // The root task to restore the target root task behind when the animation is finished
-    private Task mRestoreTargetBehindRootTask;
-
     RecentsAnimation(ActivityTaskManagerService atm, ActivityTaskSupervisor taskSupervisor,
-            ActivityStartController activityStartController, WindowManagerService wm,
+            ActivityStartController activityStartController,
             Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId,
-            int recentsUid, @Nullable WindowProcessController caller) {
-        mService = atm;
+            int recentsUid) {
         mTaskSupervisor = taskSupervisor;
-        mDefaultTaskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea();
+        mDefaultTaskDisplayArea = atm.mRootWindowContainer.getDefaultTaskDisplayArea();
         mActivityStartController = activityStartController;
-        mWindowManager = wm;
         mTargetIntent = targetIntent;
         mRecentsComponent = recentsComponent;
         mRecentsFeatureId = recentsFeatureId;
         mRecentsUid = recentsUid;
-        mCaller = caller;
         mUserId = atm.getCurrentUserId();
         mTargetActivityType = targetIntent.getComponent() != null
                 && recentsComponent.equals(targetIntent.getComponent())
@@ -171,310 +143,6 @@
         }
     }
 
-    void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
-
-        // Cancel any existing recents animation running synchronously (do not hold the
-        // WM lock) before starting the newly requested recents animation as they can not coexist
-        if (mWindowManager.getRecentsAnimationController() != null) {
-            mWindowManager.getRecentsAnimationController().forceCancelAnimation(
-                    REORDER_MOVE_TO_ORIGINAL_POSITION, "startRecentsActivity");
-        }
-
-        // If the activity is associated with the root recents task, then try and get that first
-        Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
-                mTargetActivityType);
-        ActivityRecord targetActivity = getTargetActivity(targetRootTask);
-        final boolean hasExistingActivity = targetActivity != null;
-        if (hasExistingActivity) {
-            mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask);
-            if (mRestoreTargetBehindRootTask == null
-                    && targetRootTask.getTopMostTask() == targetActivity.getTask()) {
-                notifyAnimationCancelBeforeStart(recentsAnimationRunner);
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                        "No root task above target root task=%s", targetRootTask);
-                return;
-            }
-        }
-
-        // Send launch hint if we are actually launching the target. If it's already visible
-        // (shouldn't happen in general) we don't need to send it.
-        if (targetActivity == null || !targetActivity.isVisibleRequested()) {
-            mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(
-                    true /* forceSend */, targetActivity);
-        }
-
-        final LaunchingState launchingState =
-                mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent);
-
-        setProcessAnimating(true);
-
-        mService.deferWindowLayout();
-        try {
-            if (hasExistingActivity) {
-                // Move the recents activity into place for the animation if it is not top most
-                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask);
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s",
-                        targetRootTask, getRootTaskAbove(targetRootTask));
-
-                // If there are multiple tasks in the target root task (ie. the root home task,
-                // with 3p and default launchers coexisting), then move the task to the top as a
-                // part of moving the root task to the front
-                final Task task = targetActivity.getTask();
-                if (targetRootTask.getTopMostTask() != task) {
-                    targetRootTask.positionChildAtTop(task);
-                }
-            } else {
-                // No recents activity, create the new recents activity bottom most
-                startRecentsActivityInBackground("startRecentsActivity_noTargetActivity");
-
-                // Move the recents activity into place for the animation
-                targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
-                        mTargetActivityType);
-                targetActivity = getTargetActivity(targetRootTask);
-                mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask);
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s",
-                        targetRootTask, getRootTaskAbove(targetRootTask));
-
-                mWindowManager.prepareAppTransitionNone();
-                mWindowManager.executeAppTransition();
-
-                // TODO: Maybe wait for app to draw in this particular case?
-
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Started intent=%s", mTargetIntent);
-            }
-
-            // Mark the target activity as launch-behind to bump its visibility for the
-            // duration of the gesture that is driven by the recents component
-            targetActivity.mLaunchTaskBehind = true;
-            mLaunchedTargetActivity = targetActivity;
-            // TODO(b/156772625): Evaluate to send new intents vs. replacing the intent extras.
-            targetActivity.intent.replaceExtras(mTargetIntent);
-
-            // Fetch all the surface controls and pass them to the client to get the animation
-            // started
-            mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
-                    this, mDefaultTaskDisplayArea.getDisplayId(),
-                    mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity);
-
-            // If we updated the launch-behind state, update the visibility of the activities after
-            // we fetch the visible tasks to be controlled by the animation
-            mService.mRootWindowContainer.ensureActivitiesVisible();
-
-            ActivityOptions options = null;
-            if (eventTime > 0) {
-                options = ActivityOptions.makeBasic();
-                options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
-            }
-            mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
-                    START_TASK_TO_FRONT, !hasExistingActivity, targetActivity, options);
-
-            // Register for root task order changes
-            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this);
-        } catch (Exception e) {
-            Slog.e(TAG, "Failed to start recents activity", e);
-            throw e;
-        } finally {
-            mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode,
-            boolean sendUserLeaveHint) {
-        synchronized (mService.mGlobalLock) {
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "onAnimationFinished(): controller=%s reorderMode=%d",
-                            mWindowManager.getRecentsAnimationController(), reorderMode);
-
-            // Unregister for root task order changes
-            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(this);
-
-            final RecentsAnimationController controller =
-                    mWindowManager.getRecentsAnimationController();
-            if (controller == null) return;
-
-            // Just to be sure end the launch hint in case the target activity was never launched.
-            // However, if we're keeping the activity and making it visible, we can leave it on.
-            if (reorderMode != REORDER_KEEP_IN_PLACE) {
-                mService.endPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
-            }
-
-            // Once the target is shown, prevent spurious background app switches
-            if (reorderMode == REORDER_MOVE_TO_TOP) {
-                mService.stopAppSwitches();
-            }
-
-            inSurfaceTransaction(() -> {
-                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                        "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
-                mService.deferWindowLayout();
-                try {
-                    mWindowManager.cleanupRecentsAnimation(reorderMode);
-
-                    final Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(
-                            WINDOWING_MODE_UNDEFINED, mTargetActivityType);
-                    // Prefer to use the original target activity instead of top activity because
-                    // we may have moved another task to top (starting 3p launcher).
-                    final ActivityRecord targetActivity = targetRootTask != null
-                            ? targetRootTask.isInTask(mLaunchedTargetActivity)
-                            : null;
-                    ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                            "onAnimationFinished(): targetRootTask=%s targetActivity=%s "
-                                    + "mRestoreTargetBehindRootTask=%s",
-                            targetRootTask, targetActivity, mRestoreTargetBehindRootTask);
-                    if (targetActivity == null) {
-                        return;
-                    }
-
-                    // Restore the launched-behind state
-                    targetActivity.mLaunchTaskBehind = false;
-
-                    if (reorderMode == REORDER_MOVE_TO_TOP) {
-                        // Bring the target root task to the front
-                        mTaskSupervisor.mNoAnimActivities.add(targetActivity);
-
-                        if (sendUserLeaveHint) {
-                            // Setting this allows the previous app to PiP.
-                            mTaskSupervisor.mUserLeaving = true;
-                            targetRootTask.moveTaskToFront(targetActivity.getTask(),
-                                    true /* noAnimation */, null /* activityOptions */,
-                                    targetActivity.appTimeTracker,
-                                    "RecentsAnimation.onAnimationFinished()");
-                        } else {
-                            targetRootTask.moveToFront("RecentsAnimation.onAnimationFinished()");
-                        }
-
-                        if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
-                            final Task topRootTask = getTopNonAlwaysOnTopRootTask();
-                            if (topRootTask != targetRootTask) {
-                                ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
-                                        "Expected target rootTask=%s"
-                                        + " to be top most but found rootTask=%s",
-                                        targetRootTask, topRootTask);
-                            }
-                        }
-                    } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
-                        // Restore the target root task to its previous position
-                        final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea();
-                        taskDisplayArea.moveRootTaskBehindRootTask(targetRootTask,
-                                mRestoreTargetBehindRootTask);
-                        if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
-                            final Task aboveTargetRootTask = getRootTaskAbove(targetRootTask);
-                            if (mRestoreTargetBehindRootTask != null
-                                    && aboveTargetRootTask != mRestoreTargetBehindRootTask) {
-                                ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS,
-                                        "Expected target rootTask=%s to restored behind "
-                                                + "rootTask=%s but it is behind rootTask=%s",
-                                        targetRootTask, mRestoreTargetBehindRootTask,
-                                        aboveTargetRootTask);
-                            }
-                        }
-                    } else {
-                        // If there is no recents screenshot animation, we can update the visibility
-                        // of target root task immediately because it is visually invisible and the
-                        // launch-behind state is restored. That also prevents the next transition
-                        // type being disturbed if the visibility is updated after setting the next
-                        // transition (the target activity will be one of closing apps).
-                        if (!controller.shouldDeferCancelWithScreenshot()
-                                && !targetRootTask.isFocusedRootTaskOnDisplay()) {
-                            targetRootTask.ensureActivitiesVisible(null /* starting */);
-                        }
-                        // Keep target root task in place, nothing changes, so ignore the transition
-                        // logic below
-                        return;
-                    }
-
-                    mWindowManager.prepareAppTransitionNone();
-                    mService.mRootWindowContainer.ensureActivitiesVisible();
-                    mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
-
-                    // No reason to wait for the pausing activity in this case, as the hiding of
-                    // surfaces needs to be done immediately.
-                    mWindowManager.executeAppTransition();
-
-                    final Task rootTask = targetRootTask.getRootTask();
-                    // Client state may have changed during the recents animation, so force
-                    // send task info so the client can synchronize its state.
-                    rootTask.dispatchTaskInfoChangedIfNeeded(true /* force */);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Failed to clean up recents activity", e);
-                    throw e;
-                } finally {
-                    mTaskSupervisor.mUserLeaving = false;
-                    mService.continueWindowLayout();
-                    // Make sure the surfaces are updated with the latest state. Sometimes the
-                    // surface placement may be skipped if display configuration is changed (i.e.
-                    // {@link DisplayContent#mWaitingForConfig} is true).
-                    if (mWindowManager.mRoot.isLayoutNeeded()) {
-                        mWindowManager.mRoot.performSurfacePlacement();
-                    }
-                    setProcessAnimating(false);
-                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-                }
-            });
-        }
-    }
-
-    // No-op wrapper to keep legacy code.
-    private static void inSurfaceTransaction(Runnable exec) {
-        exec.run();
-    }
-
-    /** Gives the owner of recents animation higher priority. */
-    private void setProcessAnimating(boolean animating) {
-        if (mCaller == null) return;
-        // Apply the top-app scheduling group to who runs the animation.
-        mCaller.setRunningRecentsAnimation(animating);
-        int demoteReasons = mService.mDemoteTopAppReasons;
-        if (animating) {
-            demoteReasons |= ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS;
-        } else {
-            demoteReasons &= ~ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS;
-        }
-        mService.mDemoteTopAppReasons = demoteReasons;
-        // Make the demotion of the real top app take effect. No need to restore top app state for
-        // finishing recents because addToStopping -> scheduleIdle -> activityIdleInternal ->
-        // trimApplications will have a full update.
-        if (animating && mService.mTopApp != null) {
-            mService.mTopApp.scheduleUpdateOomAdj();
-        }
-    }
-
-    @Override
-    public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
-            boolean sendUserLeaveHint) {
-        finishAnimation(reorderMode, sendUserLeaveHint);
-    }
-
-    @Override
-    public void onRootTaskOrderChanged(Task rootTask) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onRootTaskOrderChanged(): rootTask=%s", rootTask);
-        if (mDefaultTaskDisplayArea.getRootTask(t -> t == rootTask) == null
-                || !rootTask.shouldBeVisible(null)) {
-            // The root task is not visible, so ignore this change
-            return;
-        }
-        final RecentsAnimationController controller =
-                mWindowManager.getRecentsAnimationController();
-        if (controller == null) {
-            return;
-        }
-
-        // We defer canceling the recents animation until the next app transition in the following
-        // cases:
-        // 1) The next launching task is not being animated by the recents animation
-        // 2) The next task is home activity. (i.e. pressing home key to back home in recents).
-        if ((!controller.isAnimatingTask(rootTask.getTopMostTask())
-                || controller.isTargetApp(rootTask.getTopNonFinishingActivity()))
-                && controller.shouldDeferCancelUntilNextTransition()) {
-            // Always prepare an app transition since we rely on the transition callbacks to cleanup
-            mWindowManager.prepareAppTransitionNone();
-            controller.setCancelOnNextTransitionStart();
-        }
-    }
-
     private void startRecentsActivityInBackground(String reason) {
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchActivityType(mTargetActivityType);
@@ -492,26 +160,6 @@
     }
 
     /**
-     * Called only when the animation should be canceled prior to starting.
-     */
-    static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) {
-        try {
-            recentsAnimationRunner.onAnimationCanceled(null /* taskIds */,
-                    null /* taskSnapshots */);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to cancel recents animation before start", e);
-        }
-    }
-
-    /**
-     * @return The top root task that is not always-on-top.
-     */
-    private Task getTopNonAlwaysOnTopRootTask() {
-        return mDefaultTaskDisplayArea.getRootTask(task ->
-                !task.getWindowConfiguration().isAlwaysOnTop());
-    }
-
-    /**
      * @return the top activity in the {@param targetRootTask} matching the {@param component},
      * or just the top activity of the top task if no task matches the component.
      */
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
deleted file mode 100644
index 6f94713..0000000
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ /dev/null
@@ -1,1382 +0,0 @@
-/*
- * 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.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
-import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.app.WindowConfiguration;
-import android.graphics.GraphicBuffer;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.IntArray;
-import android.util.Slog;
-import android.util.SparseBooleanArray;
-import android.util.proto.ProtoOutputStream;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
-import android.view.InputWindowHandle;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
-import android.view.WindowInsets.Type;
-import android.window.PictureInPictureSurfaceTransaction;
-import android.window.TaskSnapshot;
-import android.window.WindowAnimationState;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.protolog.ProtoLog;
-import com.android.server.LocalServices;
-import com.android.server.inputmethod.InputMethodManagerInternal;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-import com.android.server.wm.utils.InsetUtils;
-
-import com.google.android.collect.Sets;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
-/**
- * Controls a single instance of the remote driven recents animation. In particular, this allows
- * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
- * runner is provided an animation controller which allows it to take screenshots and to notify
- * window manager when the animation is completed. In addition, window manager may also notify the
- * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
- */
-public class RecentsAnimationController implements DeathRecipient {
-    private static final String TAG = RecentsAnimationController.class.getSimpleName();
-    private static final long FAILSAFE_DELAY = 1000;
-
-    // Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state
-    private static final int MODE_UNKNOWN = -1;
-
-    public static final int REORDER_KEEP_IN_PLACE = 0;
-    public static final int REORDER_MOVE_TO_TOP = 1;
-    public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2;
-
-    @IntDef(prefix = { "REORDER_MODE_" }, value = {
-            REORDER_KEEP_IN_PLACE,
-            REORDER_MOVE_TO_TOP,
-            REORDER_MOVE_TO_ORIGINAL_POSITION
-    })
-    public @interface ReorderMode {}
-
-    private final WindowManagerService mService;
-    @VisibleForTesting
-    final StatusBarManagerInternal mStatusBar;
-    private IRecentsAnimationRunner mRunner;
-    private final RecentsAnimationCallbacks mCallbacks;
-    private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
-    private final IntArray mPendingNewTaskTargets = new IntArray(0);
-
-    private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
-            new ArrayList<>();
-    private final int mDisplayId;
-    private boolean mWillFinishToHome = false;
-    private final Runnable mFailsafeRunnable = this::onFailsafe;
-
-    // The recents component app token that is shown behind the visible tasks
-    private ActivityRecord mTargetActivityRecord;
-    private DisplayContent mDisplayContent;
-    private int mTargetActivityType;
-
-    // We start the RecentsAnimationController in a pending-start state since we need to wait for
-    // the wallpaper/activity to draw before we can give control to the handler to start animating
-    // the visible task surfaces
-    private boolean mPendingStart = true;
-
-    // Set when the animation has been canceled
-    private boolean mCanceled;
-
-    // Whether or not the input consumer is enabled. The input consumer must be both registered and
-    // enabled for it to start intercepting touch events.
-    private boolean mInputConsumerEnabled;
-
-    private final Rect mTmpRect = new Rect();
-
-    private boolean mLinkedToDeathOfRunner;
-
-    // Whether to try to defer canceling from a root task order change until the next transition
-    private boolean mRequestDeferCancelUntilNextTransition;
-    // Whether to actually defer canceling until the next transition
-    private boolean mCancelOnNextTransitionStart;
-    // Whether to take a screenshot when handling a deferred cancel
-    private boolean mCancelDeferredWithScreenshot;
-    // The reorder mode to apply after the cleanupScreenshot() callback
-    private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION;
-
-    @VisibleForTesting
-    boolean mIsAddingTaskToTargets;
-    private boolean mNavigationBarAttachedToApp;
-    private ActivityRecord mNavBarAttachedApp;
-
-    private final ArrayList<RemoteAnimationTarget> mPendingTaskAppears = new ArrayList<>();
-
-    /**
-     * An app transition listener to cancel the recents animation only after the app transition
-     * starts or is canceled.
-     */
-    final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
-        @Override
-        public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
-                long statusBarAnimationDuration) {
-            continueDeferredCancel();
-            return 0;
-        }
-
-        @Override
-        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled) {
-            continueDeferredCancel();
-        }
-
-        private void continueDeferredCancel() {
-            mDisplayContent.mAppTransition.unregisterListener(this);
-            if (mCanceled) {
-                return;
-            }
-
-            if (mCancelOnNextTransitionStart) {
-                mCancelOnNextTransitionStart = false;
-                cancelAnimationWithScreenshot(mCancelDeferredWithScreenshot);
-            }
-        }
-    };
-
-    public interface RecentsAnimationCallbacks {
-        /** Callback when recents animation is finished. */
-        void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
-    }
-
-    private final IRecentsAnimationController mController =
-            new IRecentsAnimationController.Stub() {
-
-        @Override
-        public TaskSnapshot screenshotTask(int taskId) {
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "screenshotTask(%d): mCanceled=%b", taskId, mCanceled);
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    if (mCanceled) {
-                        return null;
-                    }
-                    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-                        final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
-                        final Task task = adapter.mTask;
-                        if (task.mTaskId == taskId) {
-                            final TaskSnapshotController snapshotController =
-                                    mService.mTaskSnapshotController;
-                            final ArraySet<Task> tasks = Sets.newArraySet(task);
-                            snapshotController.snapshotTasks(tasks);
-                            snapshotController.addSkipClosingAppSnapshotTasks(tasks);
-                            return snapshotController.getSnapshot(taskId, task.mUserId,
-                                    false /* restoreFromDisk */, false /* isLowResolution */);
-                        }
-                    }
-                    return null;
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setFinishTaskTransaction(int taskId,
-                PictureInPictureSurfaceTransaction finishTransaction,
-                SurfaceControl overlay) {
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "setFinishTaskTransaction(%d): transaction=%s", taskId, finishTransaction);
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-                        final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
-                        if (taskAdapter.mTask.mTaskId == taskId) {
-                            taskAdapter.mFinishTransaction = finishTransaction;
-                            taskAdapter.mFinishOverlay = overlay;
-                            break;
-                        }
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint,
-                IResultReceiver finishCb) {
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Note, the callback will handle its own synchronization, do not lock on WM lock
-                // prior to calling the callback
-                mCallbacks.onAnimationFinished(moveHomeToTop
-                        ? REORDER_MOVE_TO_TOP
-                        : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-            if (finishCb != null) {
-                try {
-                    finishCb.send(0, new Bundle());
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to report animation finished", e);
-                }
-            }
-        }
-
-        @Override
-        public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars)
-                throws RemoteException {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-                        final Task task = mPendingAnimations.get(i).mTask;
-                        if (task.getActivityType() != mTargetActivityType) {
-                            task.setCanAffectSystemUiFlags(behindSystemBars);
-                        }
-                    }
-                    InputMethodManagerInternal.get().maybeFinishStylusHandwriting();
-                    mService.mWindowPlacerLocked.requestTraversal();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setInputConsumerEnabled(boolean enabled) {
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "setInputConsumerEnabled(%s): mCanceled=%b", enabled, mCanceled);
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    if (mCanceled) {
-                        return;
-                    }
-                    mInputConsumerEnabled = enabled;
-                    final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
-                    inputMonitor.updateInputWindowsLw(true /*force*/);
-                    mService.scheduleAnimationLocked();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
-            synchronized (mService.mGlobalLock) {
-                setDeferredCancel(defer, screenshot);
-            }
-        }
-
-        @Override
-        public void cleanupScreenshot() {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                // Note, the callback will handle its own synchronization, do not lock on WM lock
-                // prior to calling the callback
-                continueDeferredCancelAnimation();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void setWillFinishToHome(boolean willFinishToHome) {
-            synchronized (mService.getWindowManagerLock()) {
-                RecentsAnimationController.this.setWillFinishToHome(willFinishToHome);
-            }
-        }
-
-        @Override
-        public boolean removeTask(int taskId) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    return removeTaskInternal(taskId);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void detachNavigationBarFromApp(boolean moveHomeToTop) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    restoreNavigationBarFromApp(
-                            moveHomeToTop || mIsAddingTaskToTargets /* animate */);
-                    mService.mWindowPlacerLocked.requestTraversal();
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void animateNavigationBarToApp(long duration) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                synchronized (mService.getWindowManagerLock()) {
-                    animateNavigationBarForAppLaunch(duration);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
-        @Override
-        public void handOffAnimation(
-                RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
-            // unused legacy implementation
-        }
-    };
-
-    /**
-     * @param remoteAnimationRunner The remote runner which should be notified when the animation is
-     *                              ready to start or has been canceled
-     * @param callbacks Callbacks to be made when the animation finishes
-     */
-    RecentsAnimationController(WindowManagerService service,
-            IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
-            int displayId) {
-        mService = service;
-        mRunner = remoteAnimationRunner;
-        mCallbacks = callbacks;
-        mDisplayId = displayId;
-        mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
-        mDisplayContent = service.mRoot.getDisplayContent(displayId);
-    }
-
-    /**
-     * Initializes the recents animation controller. This is a separate call from the constructor
-     * because it may call cancelAnimation() which needs to properly clean up the controller
-     * in the window manager.
-     */
-    public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
-            ActivityRecord targetActivity) {
-        mTargetActivityType = targetActivityType;
-        mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
-
-        // Make leashes for each of the visible/target tasks and add it to the recents animation to
-        // be started
-        // TODO(b/153090560): Support Recents on multiple task display areas
-        final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
-                .getVisibleTasks();
-        final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
-                .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
-        if (targetRootTask != null) {
-            targetRootTask.forAllLeafTasks(t -> {
-                if (!visibleTasks.contains(t)) {
-                    visibleTasks.add(t);
-                }
-            }, true /* traverseTopToBottom */);
-        }
-
-        final int taskCount = visibleTasks.size();
-        for (int i = taskCount - 1; i >= 0; i--) {
-            final Task task = visibleTasks.get(i);
-            if (skipAnimation(task)) {
-                continue;
-            }
-            addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
-                    (type, anim) -> task.forAllWindows(win -> {
-                        win.onAnimationFinished(type, anim);
-                    }, true /* traverseTopToBottom */));
-        }
-
-        // Skip the animation if there is nothing to animate
-        if (mPendingAnimations.isEmpty()) {
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
-            return;
-        }
-
-        try {
-            linkToDeathOfRunner();
-        } catch (RemoteException e) {
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
-            return;
-        }
-
-        attachNavigationBarToApp();
-
-        // Adjust the wallpaper visibility for the showing target activity
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                "setHomeApp(%s)", targetActivity.getName());
-        mTargetActivityRecord = targetActivity;
-        if (targetActivity.windowsCanBeWallpaperTarget()) {
-            mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            mDisplayContent.setLayoutNeeded();
-        }
-
-        mService.mWindowPlacerLocked.performSurfacePlacement();
-
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);
-
-        // Notify that the animation has started
-        if (mStatusBar != null) {
-            mStatusBar.onRecentsAnimationStateChanged(true /* running */);
-        }
-    }
-
-    /**
-     * Return whether the given window should still be considered interesting for the all-drawn
-     * state.  This is only interesting for the target app, which may have child windows that are
-     * not actually visible and should not be considered interesting and waited upon.
-     */
-    protected boolean isInterestingForAllDrawn(WindowState window) {
-        if (isTargetApp(window.getActivityRecord())) {
-            if (window.getWindowType() != TYPE_BASE_APPLICATION
-                    && window.getAttrs().alpha == 0f) {
-                // If there is a cihld window that is alpha 0, then ignore that window
-                return false;
-            }
-        }
-        // By default all windows are still interesting for all drawn purposes
-        return true;
-    }
-
-    /**
-     * Whether a task should be filtered from the recents animation. This can be true for tasks
-     * being displayed outside of recents.
-     */
-    private boolean skipAnimation(Task task) {
-        final WindowConfiguration config = task.getWindowConfiguration();
-        return task.isAlwaysOnTop() || config.tasksAreFloating();
-    }
-
-    @VisibleForTesting
-    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
-        return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
-                null /* finishedCallback */);
-    }
-
-    @VisibleForTesting
-    TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
-            OnAnimationFinishedCallback finishedCallback) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
-        final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
-                isRecentTaskInvisible);
-        task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
-                ANIMATION_TYPE_RECENTS, finishedCallback);
-        task.commitPendingTransaction();
-        mPendingAnimations.add(taskAdapter);
-        return taskAdapter;
-    }
-
-    @VisibleForTesting
-    void removeAnimation(TaskAnimationAdapter taskAdapter) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                "removeAnimation(%d)", taskAdapter.mTask.mTaskId);
-        taskAdapter.onRemove();
-        mPendingAnimations.remove(taskAdapter);
-    }
-
-    @VisibleForTesting
-    void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeWallpaperAnimation()");
-        wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(
-                wallpaperAdapter.getLastAnimationType(), wallpaperAdapter);
-        mPendingWallpaperAnimations.remove(wallpaperAdapter);
-    }
-
-    void startAnimation() {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                "startAnimation(): mPendingStart=%b mCanceled=%b", mPendingStart, mCanceled);
-        if (!mPendingStart || mCanceled) {
-            // Skip starting if we've already started or canceled the animation
-            return;
-        }
-        try {
-            // Create the app targets
-            final RemoteAnimationTarget[] appTargets = createAppAnimations();
-
-            // Skip the animation if there is nothing to animate
-            if (appTargets.length == 0) {
-                cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
-                return;
-            }
-
-            // Create the wallpaper targets
-            final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
-
-            mPendingStart = false;
-
-            final Rect contentInsets;
-            final WindowState targetAppMainWindow = getTargetAppMainWindow();
-            if (targetAppMainWindow != null) {
-                contentInsets = targetAppMainWindow
-                        .getInsetsStateWithVisibilityOverride()
-                        .calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(),
-                                false /* ignoreVisibility */).toRect();
-            } else {
-                // If the window for the activity had not yet been created, use the display insets.
-                mService.getStableInsets(mDisplayId, mTmpRect);
-                contentInsets = mTmpRect;
-            }
-            mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
-                    null, new Bundle());
-            ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                    "startAnimation(): Notify animation start: %s",
-                    mPendingAnimations.stream()
-                            .map(anim->anim.mTask.mTaskId).collect(Collectors.toList()));
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to start recents animation", e);
-        }
-
-        if (mTargetActivityRecord != null) {
-            final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
-            reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
-            mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
-                    .notifyTransitionStarting(reasons);
-        }
-    }
-
-    boolean isNavigationBarAttachedToApp() {
-        return mNavigationBarAttachedToApp;
-    }
-
-    @VisibleForTesting
-    WindowState getNavigationBarWindow() {
-        return mDisplayContent.getDisplayPolicy().getNavigationBar();
-    }
-
-    private void attachNavigationBarToApp() {
-        if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
-                // Skip the case where the nav bar is controlled by fade rotation.
-                || mDisplayContent.getAsyncRotationController() != null) {
-            return;
-        }
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
-            final Task task = adapter.mTask;
-            if (task.isActivityTypeHomeOrRecents()) {
-                continue;
-            }
-            mNavBarAttachedApp = task.getTopVisibleActivity();
-            break;
-        }
-
-        final WindowState navWindow = getNavigationBarWindow();
-        if (mNavBarAttachedApp == null || navWindow == null || navWindow.mToken == null) {
-            return;
-        }
-        mNavigationBarAttachedToApp = true;
-        navWindow.mToken.cancelAnimation();
-        final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
-        final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
-        navWindow.setSurfaceTranslationY(-mNavBarAttachedApp.getBounds().top);
-        t.reparent(navSurfaceControl, mNavBarAttachedApp.getSurfaceControl());
-        t.show(navSurfaceControl);
-
-        final WindowContainer imeContainer = mDisplayContent.getImeContainer();
-        if (imeContainer.isVisible()) {
-            t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
-        } else {
-            // Place the nav bar on top of anything else in the top activity.
-            t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
-        }
-        if (mStatusBar != null) {
-            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false);
-        }
-    }
-
-    @VisibleForTesting
-    void restoreNavigationBarFromApp(boolean animate) {
-        if (!mNavigationBarAttachedToApp) {
-            return;
-        }
-        mNavigationBarAttachedToApp = false;
-
-        if (mStatusBar != null) {
-            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true);
-        }
-
-        final WindowState navWindow = getNavigationBarWindow();
-        if (navWindow == null) {
-            return;
-        }
-        navWindow.setSurfaceTranslationY(0);
-
-        final WindowToken navToken = navWindow.mToken;
-        if (navToken == null) {
-            return;
-        }
-        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
-        final WindowContainer parent = navToken.getParent();
-        t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
-
-        if (animate) {
-            final NavBarFadeAnimationController controller =
-                        new NavBarFadeAnimationController(mDisplayContent);
-            controller.fadeWindowToken(true);
-        } else {
-            // Reparent the SurfaceControl of nav bar token back.
-            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
-        }
-    }
-
-    void animateNavigationBarForAppLaunch(long duration) {
-        if (!mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
-                // Skip the case where the nav bar is controlled by fade rotation.
-                || mDisplayContent.getAsyncRotationController() != null
-                || mNavigationBarAttachedToApp
-                || mNavBarAttachedApp == null) {
-            return;
-        }
-
-        final NavBarFadeAnimationController controller =
-                new NavBarFadeAnimationController(mDisplayContent);
-        controller.fadeOutAndInSequentially(duration, null /* fadeOutParent */,
-                mNavBarAttachedApp.getSurfaceControl());
-    }
-
-    void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
-        if (mRunner != null) {
-            mIsAddingTaskToTargets = task != null;
-            mNavBarAttachedApp = task == null ? null : task.getTopVisibleActivity();
-            // No need to send task appeared when the task target already exists, or when the
-            // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
-            if (isAnimatingTask(task) || skipAnimation(task)) {
-                return;
-            }
-            collectTaskRemoteAnimations(task, MODE_OPENING, finishedCallback);
-        }
-    }
-
-    void sendTasksAppeared() {
-        if (mPendingTaskAppears.isEmpty() || mRunner == null) return;
-        try {
-            final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray(
-                    new RemoteAnimationTarget[0]);
-            mRunner.onTasksAppeared(targets);
-            mPendingTaskAppears.clear();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to report task appeared", e);
-        }
-    }
-
-    private void collectTaskRemoteAnimations(Task task, int mode,
-            OnAnimationFinishedCallback finishedCallback) {
-        final SparseBooleanArray recentTaskIds =
-                mService.mAtmService.getRecentTasks().getRecentTaskIds();
-
-        // The target must be built off the root task (the leaf task surface would be cropped
-        // within the root surface). However, recents only tracks leaf task ids, so we'll traverse
-        // and create animation target for all visible leaf tasks.
-        task.forAllLeafTasks(leafTask -> {
-            if (!leafTask.shouldBeVisible(null /* starting */)) {
-                return;
-            }
-            final int taskId = leafTask.mTaskId;
-            TaskAnimationAdapter adapter = addAnimation(leafTask,
-                    !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
-            mPendingNewTaskTargets.add(taskId);
-            final RemoteAnimationTarget target =
-                    adapter.createRemoteAnimationTarget(taskId, mode);
-            if (target != null) {
-                mPendingTaskAppears.add(target);
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                        "collectTaskRemoteAnimations, target: %s", target);
-            }
-        }, false /* traverseTopToBottom */);
-    }
-
-    private boolean removeTaskInternal(int taskId) {
-        boolean result = false;
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            // Only allows when task target has became visible to user, to prevent
-            // the flickering during remove animation and task visible.
-            final TaskAnimationAdapter target = mPendingAnimations.get(i);
-            if (target.mTask.mTaskId == taskId && target.mTask.isOnTop()) {
-                removeAnimation(target);
-                final int taskIndex = mPendingNewTaskTargets.indexOf(taskId);
-                if (taskIndex != -1) {
-                    mPendingNewTaskTargets.remove(taskIndex);
-                }
-                result = true;
-                break;
-            }
-        }
-        return result;
-    }
-
-    private RemoteAnimationTarget[] createAppAnimations() {
-        final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
-            final RemoteAnimationTarget target =
-                    taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID, MODE_UNKNOWN);
-            if (target != null) {
-                targets.add(target);
-            } else {
-                removeAnimation(taskAdapter);
-            }
-        }
-        return targets.toArray(new RemoteAnimationTarget[targets.size()]);
-    }
-
-    private RemoteAnimationTarget[] createWallpaperAnimations() {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "createWallpaperAnimations()");
-        return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent, 0L, 0L,
-                adapter -> {
-                    synchronized (mService.mGlobalLock) {
-                        // If the wallpaper animation is canceled, continue with the recents
-                        // animation
-                        mPendingWallpaperAnimations.remove(adapter);
-                    }
-                }, mPendingWallpaperAnimations);
-    }
-
-    void forceCancelAnimation(@ReorderMode int reorderMode, String reason) {
-        if (!mCanceled) {
-            cancelAnimation(reorderMode, reason);
-        } else {
-            continueDeferredCancelAnimation();
-        }
-    }
-
-    void cancelAnimation(@ReorderMode int reorderMode, String reason) {
-        cancelAnimation(reorderMode, false /*screenshot */, reason);
-    }
-
-    void cancelAnimationWithScreenshot(boolean screenshot) {
-        cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "rootTaskOrderChanged");
-    }
-
-    /**
-     * Cancels the running animation when starting home, providing a snapshot for the runner to
-     * properly handle the cancellation. This call uses the provided hint to determine how to
-     * finish the animation.
-     */
-    public void cancelAnimationForHomeStart() {
-        final int reorderMode = mTargetActivityType == ACTIVITY_TYPE_HOME && mWillFinishToHome
-                ? REORDER_MOVE_TO_TOP
-                : REORDER_KEEP_IN_PLACE;
-        cancelAnimation(reorderMode, true /* screenshot */, "cancelAnimationForHomeStart");
-    }
-
-    /**
-     * Cancels the running animation when there is a display change, providing a snapshot for the
-     * runner to properly handle the cancellation. This call uses the provided hint to determine
-     * how to finish the animation.
-     */
-    public void cancelAnimationForDisplayChange() {
-        cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
-                true /* screenshot */, "cancelAnimationForDisplayChange");
-    }
-
-    private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
-        synchronized (mService.getWindowManagerLock()) {
-            if (mCanceled) {
-                // We've already canceled the animation
-                return;
-            }
-            mService.mH.removeCallbacks(mFailsafeRunnable);
-            mCanceled = true;
-
-            if (screenshot && !mPendingAnimations.isEmpty()) {
-                final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
-                mPendingCancelWithScreenshotReorderMode = reorderMode;
-
-                if (!snapshotMap.isEmpty()) {
-                    try {
-                        int[] taskIds = new int[snapshotMap.size()];
-                        TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
-                        for (int i = snapshotMap.size() - 1; i >= 0; i--) {
-                            taskIds[i] = snapshotMap.keyAt(i).mTaskId;
-                            snapshots[i] = snapshotMap.valueAt(i);
-                        }
-                        mRunner.onAnimationCanceled(taskIds, snapshots);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Failed to cancel recents animation", e);
-                    }
-                    // Schedule a new failsafe for if the runner doesn't clean up the screenshot
-                    scheduleFailsafe();
-                    return;
-                }
-                // Fallback to a normal cancel since we couldn't screenshot
-            }
-
-            // Notify the runner and clean up the animation immediately
-            // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
-            // to the runner if we this actually triggers cancel twice on the caller
-            try {
-                mRunner.onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to cancel recents animation", e);
-            }
-            mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
-        }
-    }
-
-    @VisibleForTesting
-    void continueDeferredCancelAnimation() {
-        mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode,
-                false /* sendUserLeaveHint */);
-    }
-
-    @VisibleForTesting
-    void setWillFinishToHome(boolean willFinishToHome) {
-        mWillFinishToHome = willFinishToHome;
-    }
-
-    /**
-     * Cancel recents animation when the next app transition starts.
-     * <p>
-     * When we cancel the recents animation due to a root task order change, we can't just cancel it
-     * immediately as it would lead to a flicker in Launcher if we just remove the task from the
-     * leash. Instead we screenshot the previous task and replace the child of the leash with the
-     * screenshot, so that Launcher can still control the leash lifecycle & make the next app
-     * transition animate smoothly without flickering.
-     */
-    void setCancelOnNextTransitionStart() {
-        mCancelOnNextTransitionStart = true;
-    }
-
-    /**
-     * Requests that we attempt to defer the cancel until the next app transition if we are
-     * canceling from a root task order change.  If {@param screenshot} is specified, then the
-     * system will replace the contents of the leash with a screenshot, which must be cleaned up
-     * when the runner calls cleanUpScreenshot().
-     */
-    void setDeferredCancel(boolean defer, boolean screenshot) {
-        mRequestDeferCancelUntilNextTransition = defer;
-        mCancelDeferredWithScreenshot = screenshot;
-    }
-
-    /**
-     * @return Whether we should defer the cancel from a root task order change until the next app
-     * transition.
-     */
-    boolean shouldDeferCancelUntilNextTransition() {
-        return mRequestDeferCancelUntilNextTransition;
-    }
-
-    /**
-     * @return Whether we should both defer the cancel from a root task order change until the next
-     * app transition, and also that the deferred cancel should replace the contents of the leash
-     * with a screenshot.
-     */
-    boolean shouldDeferCancelWithScreenshot() {
-        return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
-    }
-
-    private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
-        final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
-        final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
-            final Task task = adapter.mTask;
-            if (task.isActivityTypeHome()) continue;
-            snapshotController.recordSnapshot(task);
-            final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
-                    false /* restoreFromDisk */, false /* isLowResolution */);
-            if (snapshot != null) {
-                snapshotMap.put(task, snapshot);
-                // Defer until the runner calls back to cleanupScreenshot()
-                adapter.setSnapshotOverlay(snapshot);
-            }
-        }
-        snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet());
-        return snapshotMap;
-    }
-
-    void cleanupAnimation(@ReorderMode int reorderMode) {
-        ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                        "cleanupAnimation(): Notify animation finished mPendingAnimations=%d "
-                                + "reorderMode=%d",
-                        mPendingAnimations.size(), reorderMode);
-        if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION
-                && mTargetActivityRecord != mDisplayContent.topRunningActivity()) {
-            // Notify the state at the beginning because the removeAnimation may notify the
-            // transition is finished. This is a signal that there will be a next transition.
-            mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop();
-        }
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
-            if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
-                taskAdapter.mTask.dontAnimateDimExit();
-            }
-            removeAnimation(taskAdapter);
-            taskAdapter.onCleanup();
-        }
-        // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
-        mPendingNewTaskTargets.clear();
-        mPendingTaskAppears.clear();
-
-        for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
-            final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
-            removeWallpaperAnimation(wallpaperAdapter);
-        }
-
-        restoreNavigationBarFromApp(
-                reorderMode == REORDER_MOVE_TO_TOP || mIsAddingTaskToTargets /* animate */);
-
-        // Clear any pending failsafe runnables
-        mService.mH.removeCallbacks(mFailsafeRunnable);
-        mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
-
-        // Clear references to the runner
-        unlinkToDeathOfRunner();
-        mRunner = null;
-        mCanceled = true;
-
-        // Restore IME icon only when moving the original app task to front from recents, in case
-        // IME icon may missing if the moving task has already been the current focused task.
-        if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) {
-            InputMethodManagerInternal.get().updateImeWindowStatus(
-                    false /* disableImeIcon */, mDisplayId);
-        }
-
-        // Update the input windows after the animation is complete
-        final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
-        inputMonitor.updateInputWindowsLw(true /*force*/);
-
-        // We have deferred all notifications to the target app as a part of the recents animation,
-        // so if we are actually transitioning there, notify again here
-        if (mTargetActivityRecord != null) {
-            if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
-                mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
-                        mTargetActivityRecord.token);
-            }
-        }
-        mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
-
-        // Notify that the animation has ended
-        if (mStatusBar != null) {
-            mStatusBar.onRecentsAnimationStateChanged(false /* running */);
-        }
-    }
-
-    void scheduleFailsafe() {
-        mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
-    }
-
-    void onFailsafe() {
-        forceCancelAnimation(
-                mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
-                "onFailsafe");
-    }
-
-    private void linkToDeathOfRunner() throws RemoteException {
-        if (!mLinkedToDeathOfRunner) {
-            mRunner.asBinder().linkToDeath(this, 0);
-            mLinkedToDeathOfRunner = true;
-        }
-    }
-
-    private void unlinkToDeathOfRunner() {
-        if (mLinkedToDeathOfRunner) {
-            mRunner.asBinder().unlinkToDeath(this, 0);
-            mLinkedToDeathOfRunner = false;
-        }
-    }
-
-    @Override
-    public void binderDied() {
-        forceCancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
-
-        synchronized (mService.getWindowManagerLock()) {
-            // Clear associated input consumers on runner death
-            final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
-            final InputConsumerImpl consumer = inputMonitor.getInputConsumer(
-                    INPUT_CONSUMER_RECENTS_ANIMATION);
-            if (consumer != null) {
-                inputMonitor.destroyInputConsumer(consumer.mToken);
-            }
-        }
-    }
-
-    void checkAnimationReady(WallpaperController wallpaperController) {
-        if (mPendingStart) {
-            final boolean wallpaperReady = !isTargetOverWallpaper()
-                    || (wallpaperController.getWallpaperTarget() != null
-                            && wallpaperController.wallpaperTransitionReady());
-            if (wallpaperReady) {
-                mService.getRecentsAnimationController().startAnimation();
-            }
-        }
-    }
-
-    boolean isWallpaperVisible(WindowState w) {
-        return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
-                ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
-                        || isAnimatingTask(w.getTask()))
-                && isTargetOverWallpaper() && w.isOnScreen();
-    }
-
-    /**
-     * @return Whether to use the input consumer to override app input to route home/recents.
-     */
-    boolean shouldApplyInputConsumer(ActivityRecord activity) {
-        // Only apply the input consumer if it is enabled, it is not the target (home/recents)
-        // being revealed with the transition, and we are actively animating the app as a part of
-        // the animation
-        return mInputConsumerEnabled && activity != null
-                && !isTargetApp(activity) && isAnimatingApp(activity);
-    }
-
-    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) {
-        // Update the input consumer touchable region to match the target app main window
-        final WindowState targetAppMainWindow = getTargetAppMainWindow();
-        if (targetAppMainWindow != null) {
-            targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.touchableRegion.set(mTmpRect);
-            return true;
-        }
-        return false;
-    }
-
-    boolean isTargetApp(ActivityRecord activity) {
-        return mTargetActivityRecord != null && activity == mTargetActivityRecord;
-    }
-
-    private boolean isTargetOverWallpaper() {
-        if (mTargetActivityRecord == null) {
-            return false;
-        }
-        return mTargetActivityRecord.windowsCanBeWallpaperTarget();
-    }
-
-    WindowState getTargetAppMainWindow() {
-        if (mTargetActivityRecord == null) {
-            return null;
-        }
-        return mTargetActivityRecord.findMainWindow();
-    }
-
-    DisplayArea getTargetAppDisplayArea() {
-        if (mTargetActivityRecord == null) {
-            return null;
-        }
-        return mTargetActivityRecord.getDisplayArea();
-    }
-
-    boolean isAnimatingTask(Task task) {
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            if (task == mPendingAnimations.get(i).mTask) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean isAnimatingWallpaper(WallpaperWindowToken token) {
-        for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
-            if (token == mPendingWallpaperAnimations.get(i).getToken()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean isAnimatingApp(ActivityRecord activity) {
-        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            if (activity.isDescendantOf(mPendingAnimations.get(i).mTask)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    boolean shouldIgnoreForAccessibility(WindowState windowState) {
-        final Task task = windowState.getTask();
-        return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
-    }
-
-    /**
-     * If the animation target ActivityRecord has a fixed rotation ({@link
-     * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly.
-     *
-     * This avoids any screen rotation animation when animating to the Recents view.
-     */
-    void linkFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
-        if (mTargetActivityRecord == null) {
-            return;
-        }
-        wallpaper.linkFixedRotationTransform(mTargetActivityRecord);
-    }
-
-    @VisibleForTesting
-    class TaskAnimationAdapter implements AnimationAdapter {
-
-        private final Task mTask;
-        private SurfaceControl mCapturedLeash;
-        private OnAnimationFinishedCallback mCapturedFinishCallback;
-        private @AnimationType int mLastAnimationType;
-        private final boolean mIsRecentTaskInvisible;
-        private RemoteAnimationTarget mTarget;
-        private final Rect mBounds = new Rect();
-        // The bounds of the target relative to its parent.
-        private final Rect mLocalBounds = new Rect();
-        // The final surface transaction when animation is finished.
-        private PictureInPictureSurfaceTransaction mFinishTransaction;
-        // An overlay used to mask the content as an app goes into PIP
-        private SurfaceControl mFinishOverlay;
-        // An overlay used for canceling the animation with a screenshot
-        private SurfaceControl mSnapshotOverlay;
-
-        TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
-            mTask = task;
-            mIsRecentTaskInvisible = isRecentTaskInvisible;
-            mBounds.set(mTask.getBounds());
-
-            mLocalBounds.set(mBounds);
-            Point tmpPos = new Point();
-            mTask.getRelativePosition(tmpPos);
-            mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
-        }
-
-        /**
-         * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus
-         *                       can differ from taskInfo. This mismatch is needed, however, in
-         *                       some cases where we are animating root tasks but need need leaf
-         *                       ids for identification. If this is INVALID (-1), then mTaskId
-         *                       will be used.
-         * @param overrideMode overrides the target's mode. If this is -1, the mode will be
-         *                     calculated relative to going to the target activity (ie. OPENING if
-         *                     this is the target task, CLOSING otherwise).
-         */
-        RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId, int overrideMode) {
-            ActivityRecord topApp = mTask.getTopRealVisibleActivity();
-            if (topApp == null) {
-                topApp = mTask.getTopVisibleActivity();
-            }
-            final WindowState mainWindow = topApp != null
-                    ? topApp.findMainWindow()
-                    : null;
-            if (mainWindow == null) {
-                return null;
-            }
-            final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
-                    mBounds, Type.systemBars(), false /* ignoreVisibility */).toRect();
-            InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
-            final int mode = overrideMode != MODE_UNKNOWN
-                    ? overrideMode
-                    : topApp.getActivityType() == mTargetActivityType
-                            ? MODE_OPENING
-                            : MODE_CLOSING;
-            if (overrideTaskId < 0) {
-                overrideTaskId = mTask.mTaskId;
-            }
-            mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash,
-                    !topApp.fillsParent(), new Rect(),
-                    insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
-                    mLocalBounds, mBounds, mTask.getWindowConfiguration(),
-                    mIsRecentTaskInvisible, null, null, mTask.getTaskInfo(),
-                    topApp.checkEnterPictureInPictureAppOpsState());
-
-            final ActivityRecord topActivity = mTask.getTopNonFinishingActivity();
-            if (topActivity != null && topActivity.mStartingData != null
-                    && topActivity.mStartingData.hasImeSurface()) {
-                mTarget.setWillShowImeOnTarget(true);
-            }
-            return mTarget;
-        }
-
-        void setSnapshotOverlay(TaskSnapshot snapshot) {
-            // Create a surface control for the snapshot and reparent it to the leash
-            final HardwareBuffer buffer = snapshot.getHardwareBuffer();
-            if (buffer == null) {
-                return;
-            }
-
-            final SurfaceSession session = new SurfaceSession();
-            mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session)
-                    .setName("RecentTaskScreenshotSurface")
-                    .setCallsite("TaskAnimationAdapter.setSnapshotOverlay")
-                    .setFormat(buffer.getFormat())
-                    .setParent(mCapturedLeash)
-                    .setBLASTLayer()
-                    .build();
-
-            final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth();
-            mTask.getPendingTransaction()
-                    .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer))
-                    .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace())
-                    .setLayer(mSnapshotOverlay, Integer.MAX_VALUE)
-                    .setMatrix(mSnapshotOverlay, scale, 0, 0, scale)
-                    .show(mSnapshotOverlay)
-                    .apply();
-        }
-
-        void onRemove() {
-            if (mSnapshotOverlay != null) {
-                // Clean up the snapshot overlay if necessary
-                mTask.getPendingTransaction()
-                        .remove(mSnapshotOverlay)
-                        .apply();
-                mSnapshotOverlay = null;
-            }
-            mTask.setCanAffectSystemUiFlags(true);
-            mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this);
-        }
-
-        void onCleanup() {
-            final Transaction pendingTransaction = mTask.getPendingTransaction();
-            if (mFinishTransaction != null) {
-                // Reparent the overlay
-                if (mFinishOverlay != null) {
-                    pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl);
-                }
-
-                // Transfer the transform from the leash to the task
-                PictureInPictureSurfaceTransaction.apply(mFinishTransaction,
-                        mTask.mSurfaceControl, pendingTransaction);
-                mTask.setLastRecentsAnimationTransaction(mFinishTransaction, mFinishOverlay);
-                if (mDisplayContent.isFixedRotationLaunchingApp(mTargetActivityRecord)) {
-                    // The transaction is needed for position when rotating the display.
-                    mDisplayContent.mPinnedTaskController.setEnterPipTransaction(
-                            mFinishTransaction);
-                }
-                // In the case where we are transferring the transform to the task in preparation
-                // for entering PIP, we disable the task being able to affect sysui flags otherwise
-                // it may cause a flash
-                if (mTask.getActivityType() != mTargetActivityType
-                        && mFinishTransaction.getShouldDisableCanAffectSystemUiFlags()) {
-                    mTask.setCanAffectSystemUiFlags(false);
-                }
-                mFinishTransaction = null;
-                mFinishOverlay = null;
-                pendingTransaction.apply();
-            } else if (!mTask.isAttached()) {
-                // Apply the task's pending transaction in case it is detached and its transaction
-                // is not reachable.
-                pendingTransaction.apply();
-            }
-        }
-
-        @VisibleForTesting
-        public SurfaceControl getSnapshotOverlay() {
-            return mSnapshotOverlay;
-        }
-
-        @Override
-        public boolean getShowWallpaper() {
-            return false;
-        }
-
-        @Override
-        public void startAnimation(SurfaceControl animationLeash, Transaction t,
-                @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
-            // Restore position and root task crop until client has a chance to modify it.
-            t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top);
-            mTmpRect.set(mLocalBounds);
-            mTmpRect.offsetTo(0, 0);
-            t.setWindowCrop(animationLeash, mTmpRect);
-            mCapturedLeash = animationLeash;
-            mCapturedFinishCallback = finishCallback;
-            mLastAnimationType = type;
-        }
-
-        @Override
-        public void onAnimationCancelled(SurfaceControl animationLeash) {
-            // Cancel the animation immediately if any single task animator is canceled
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
-        }
-
-        @Override
-        public long getDurationHint() {
-            return 0;
-        }
-
-        @Override
-        public long getStatusBarTransitionsStartTime() {
-            return SystemClock.uptimeMillis();
-        }
-
-        @Override
-        public void dump(PrintWriter pw, String prefix) {
-            pw.print(prefix); pw.println("task=" + mTask);
-            if (mTarget != null) {
-                pw.print(prefix); pw.println("Target:");
-                mTarget.dump(pw, prefix + "  ");
-            } else {
-                pw.print(prefix); pw.println("Target: null");
-            }
-            pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
-            pw.println("mLocalBounds=" + mLocalBounds);
-            pw.println("mFinishTransaction=" + mFinishTransaction);
-            pw.println("mBounds=" + mBounds);
-            pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
-        }
-
-        @Override
-        public void dumpDebug(ProtoOutputStream proto) {
-            final long token = proto.start(REMOTE);
-            if (mTarget != null) {
-                mTarget.dumpDebug(proto, TARGET);
-            }
-            proto.end(token);
-        }
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        final String innerPrefix = prefix + "  ";
-        pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
-        pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
-        pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
-        pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
-        pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
-        pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord);
-        pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
-        pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
-                + mRequestDeferCancelUntilNextTransition);
-        pw.print(innerPrefix); pw.println("mCancelOnNextTransitionStart="
-                + mCancelOnNextTransitionStart);
-        pw.print(innerPrefix); pw.println("mCancelDeferredWithScreenshot="
-                + mCancelDeferredWithScreenshot);
-        pw.print(innerPrefix); pw.println("mPendingCancelWithScreenshotReorderMode="
-                + mPendingCancelWithScreenshotReorderMode);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index f8665c7..432089f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -53,6 +53,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 /**
  * Helper class to run app animations in a remote process.
@@ -348,6 +349,10 @@
             } finally {
                 mIsFinishing = false;
             }
+            // Reset input for all activities when the remote animation is finished.
+            final Consumer<ActivityRecord> updateActivities =
+                    activity -> activity.setDropInputForAnimation(false);
+            mDisplayContent.forAllActivities(updateActivities);
         }
         setRunningRemoteAnimation(false);
         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bded98c..866dcd5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -804,12 +804,6 @@
 
         checkAppTransitionReady(surfacePlacer);
 
-        // Defer starting the recents animation until the wallpaper has drawn
-        final RecentsAnimationController recentsAnimationController =
-                mWmService.getRecentsAnimationController();
-        if (recentsAnimationController != null) {
-            recentsAnimationController.checkAnimationReady(defaultDisplay.mWallpaperController);
-        }
         mWmService.mAtmService.mBackNavigationController
                 .checkAnimationReady(defaultDisplay.mWallpaperController);
 
@@ -1002,6 +996,7 @@
                 // complete configuration.
                 continue;
             }
+            win.updateSurfacePositionIfNeeded();
             win.reportResized();
             mWmService.mResizingWindows.remove(i);
         }
@@ -1189,8 +1184,8 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            @WindowTracingLogLevel int logLevel) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
@@ -1470,9 +1465,6 @@
         // Updates the extra information of the intent.
         if (fromHomeKey) {
             homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
-            if (mWindowManager.getRecentsAnimationController() != null) {
-                mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();
-            }
         }
         homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);
 
@@ -1633,7 +1625,7 @@
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
             r.moveFocusableActivityToTop(myReason);
-            return resumeFocusedTasksTopActivities(r.getRootTask(), prev, null);
+            return resumeFocusedTasksTopActivities(r.getRootTask(), prev);
         }
         int userId = mWmService.getUserAssignedToDisplay(taskDisplayArea.getDisplayId());
         return startHomeOnTaskDisplayArea(userId, myReason, taskDisplayArea,
@@ -2040,33 +2032,39 @@
                 onTop);
     }
 
-    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
-            @Nullable ActivityRecord launchIntoPipHostActivity, String reason) {
-        moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, null /* transition */);
+    /** Wrapper/Helper for tests */
+    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, String reason) {
+        Transition newTransit = (r.mTransitionController.isCollecting()
+                || !r.mTransitionController.isShellTransitionsEnabled())
+                ? null : r.mTransitionController.createTransition(TRANSIT_PIP);
+        moveActivityToPinnedRootTaskInner(r, null /* launchIntoPipHostActivity */, reason,
+                null /* bounds */, newTransit != null);
     }
 
     void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
             @Nullable ActivityRecord launchIntoPipHostActivity, String reason,
-            @Nullable Transition transition) {
-        moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, transition,
-                null /* bounds */);
+            @Nullable Rect bounds) {
+        moveActivityToPinnedRootTaskInner(r, launchIntoPipHostActivity, reason, bounds,
+                false /* requestStart */);
     }
 
-    void moveActivityToPinnedRootTask(@NonNull ActivityRecord r,
+    /**
+     * Moves activity to pinned in the provided transition and also requests start on that
+     * Transition at an appropriate time.
+     */
+    void moveActivityToPinnedRootTaskAndRequestStart(@NonNull ActivityRecord r, String reason) {
+        moveActivityToPinnedRootTaskInner(r, null /* launchIntoPipHostActivity */, reason,
+                null /* bounds */, true /* requestStart */);
+    }
+
+    private void moveActivityToPinnedRootTaskInner(@NonNull ActivityRecord r,
             @Nullable ActivityRecord launchIntoPipHostActivity, String reason,
-            @Nullable Transition transition, @Nullable Rect bounds) {
+            @Nullable Rect bounds, boolean requestStart) {
         final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
         final Task task = r.getTask();
         final Task rootTask;
 
-        Transition newTransition = transition;
-        // Create a transition now (if not provided) to collect the current pinned Task dismiss.
-        // Only do the create here as the Task (trigger) to enter PIP is not ready yet.
         final TransitionController transitionController = task.mTransitionController;
-        if (newTransition == null && !transitionController.isCollecting()
-                && transitionController.getTransitionPlayer() != null) {
-            newTransition = transitionController.createTransition(TRANSIT_PIP);
-        }
 
         transitionController.deferTransitionReady();
         Transition.ReadyCondition pipChangesApplied = new Transition.ReadyCondition("movedToPip");
@@ -2202,7 +2200,7 @@
                     // During recents animations, the original task is "occluded" by launcher but
                     // it wasn't paused (due to transient-launch). If we reparent to the (top) task
                     // now, it will take focus briefly which confuses the RecentTasks tracker.
-                    rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+                    rootTask.setRootTaskWindowingMode(WINDOWING_MODE_PINNED);
                 }
                 // Temporarily disable focus when reparenting to avoid intermediate focus change
                 // (because the task is on top and the activity is resumed), which could cause the
@@ -2235,7 +2233,7 @@
 
             // TODO(remove-legacy-transit): Move this to the `singleActivity` case when removing
             //                              legacy transit.
-            rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
+            rootTask.setRootTaskWindowingMode(WINDOWING_MODE_PINNED);
             if (isPip2ExperimentEnabled() && bounds != null) {
                 // set the final pip bounds in advance if pip2 is enabled
                 rootTask.setBounds(bounds);
@@ -2281,14 +2279,16 @@
             }
         }
 
-        if (newTransition != null) {
+        // can be null (for now) if shell transitions are disabled or inactive at this time
+        final Transition transit = transitionController.getCollectingTransition();
+        if (requestStart && transit != null) {
             // Request at end since we want task-organizer events from ensureActivitiesVisible
             // to be recognized.
-            transitionController.requestStartTransition(newTransition, rootTask,
+            transitionController.requestStartTransition(transit, rootTask,
                     null /* remoteTransition */, null /* displayChange */);
             // A new transition was created just for this operations. Since the operation is
             // complete, mark it as ready.
-            newTransition.setReady(rootTask, true /* ready */);
+            transit.setReady(rootTask, true /* ready */);
         }
 
         resumeFocusedTasksTopActivities();
@@ -2470,12 +2470,12 @@
     }
 
     boolean resumeFocusedTasksTopActivities() {
-        return resumeFocusedTasksTopActivities(null, null, null);
+        return resumeFocusedTasksTopActivities(null, null, null, false /* deferPause */);
     }
 
     boolean resumeFocusedTasksTopActivities(
-            Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) {
-        return resumeFocusedTasksTopActivities(targetRootTask, target, targetOptions,
+            Task targetRootTask, ActivityRecord target) {
+        return resumeFocusedTasksTopActivities(targetRootTask, target, null /* targetOptions */,
                 false /* deferPause */);
     }
 
@@ -2527,7 +2527,8 @@
                 // activity is started and resumed, and no recursion occurs.
                 final Task focusedRoot = display.getFocusedRootTask();
                 if (focusedRoot != null) {
-                    result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions);
+                    result |= focusedRoot.resumeTopActivityUncheckedLocked(
+                            target, targetOptions, false /* skipPause */);
                 } else if (targetRootTask == null) {
                     result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
                             display.getDefaultTaskDisplayArea());
@@ -2632,7 +2633,7 @@
                         // process the keyguard going away, which can happen before the sleep
                         // token is released. As a result, it is important we resume the
                         // activity here.
-                        rootTask.resumeTopActivityUncheckedLocked(null, null);
+                        rootTask.resumeTopActivityUncheckedLocked();
                     }
                     // The visibility update must not be called before resuming the top, so the
                     // display orientation can be updated first if needed. Otherwise there may
@@ -3424,28 +3425,63 @@
         return null;
     }
 
+    /** Returns the top direct activity if it should be idle but has not yet been reported. */
+    @Nullable
+    private static ActivityRecord getNotYetIdleActivity(@NonNull TaskFragment visibleTf) {
+        for (int i = visibleTf.getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = visibleTf.getChildAt(i).asActivityRecord();
+            if (r == null || r.finishing) {
+                continue;
+            }
+            if (!r.idle && (r.isState(RESUMED)
+                    // Its process is not attached yet and it may resume later.
+                    || (r.app == null && r.isFocusable()))) {
+                return r;
+            }
+            // Only check the top running activity.
+            break;
+        }
+        return null;
+    }
+
     boolean allResumedActivitiesIdle() {
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
-            // TODO(b/117135575): Check resumed activities on all visible root tasks.
             final DisplayContent display = getChildAt(displayNdx);
             if (display.isSleeping()) {
                 // No resumed activities while display is sleeping.
                 continue;
             }
 
-            // If the focused root task is not null or not empty, there should have some activities
-            // resuming or resumed. Make sure these activities are idle.
-            final Task rootTask = display.getFocusedRootTask();
-            if (rootTask == null || !rootTask.hasActivity()) {
-                continue;
-            }
-            final ActivityRecord resumedActivity = rootTask.getTopResumedActivity();
-            if (resumedActivity == null || !resumedActivity.idle) {
-                ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: rootTask=%d %s "
-                        + "not idle", rootTask.getRootTaskId(), resumedActivity);
+            final boolean foundNotIdle = display.forAllLeafTasks(task -> {
+                if (!task.isVisibleRequested()) {
+                    return false;
+                }
+                final ActivityRecord notIdle = getNotYetIdleActivity(task);
+                if (notIdle != null) {
+                    ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: %s not idle", notIdle);
+                    return true;
+                }
+                if (task.isLeafTaskFragment()) {
+                    // The task doesn't contain child TaskFragment.
+                    return false;
+                }
+                return task.forAllLeafTaskFragments(tf -> {
+                    if (!tf.isVisibleRequested()) {
+                        return false;
+                    }
+                    final ActivityRecord tfNotIdle = getNotYetIdleActivity(tf);
+                    if (tfNotIdle != null) {
+                        ProtoLog.d(WM_DEBUG_STATES, "allResumedActivitiesIdle: %s not idle",
+                                tfNotIdle);
+                        return true;
+                    }
+                    return false;
+                });
+            });
+            if (foundNotIdle) {
                 return false;
             }
-            if (mTransitionController.isTransientLaunch(resumedActivity)) {
+            if (mTransitionController.hasTransientLaunch(display)) {
                 // Not idle if the transient transition animation is running.
                 return false;
             }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 7c875c1..2ea2aeb6 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
-import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -72,7 +71,6 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.View;
 import android.view.View.FocusDirection;
 import android.view.WindowInsets;
@@ -108,7 +106,6 @@
     @NonNull
     final WindowProcessController mProcess;
     private final String mStringName;
-    SurfaceSession mSurfaceSession;
     private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
     /** Set of visible alert/app-overlay windows connected to this session. */
     private final ArraySet<WindowState> mAlertWindows = new ArraySet<>();
@@ -719,12 +716,10 @@
             mPackageName = mProcess.mInfo.packageName;
             mRelayoutTag = "relayoutWindow: " + mPackageName;
         }
-        if (mSurfaceSession == null) {
+        if (mProcess.mWindowSession == null) {
             if (DEBUG) {
-                Slog.v(TAG_WM, "First window added to " + this + ", creating SurfaceSession");
+                Slog.v(TAG_WM, "First window added to " + mProcess);
             }
-            mSurfaceSession = new SurfaceSession();
-            ProtoLog.i(WM_SHOW_TRANSACTIONS, "  NEW SURFACE SESSION %s", mSurfaceSession);
             mService.mSessions.add(this);
             if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                 mService.dispatchNewAnimatorScaleLocked(this);
@@ -821,18 +816,11 @@
         }
 
         mService.mSessions.remove(this);
-        if (mSurfaceSession == null) {
+        if (mProcess.mWindowSession == null) {
             return;
         }
 
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "  KILL SURFACE SESSION %s", mSurfaceSession);
-        try {
-            mSurfaceSession.kill();
-        } catch (Exception e) {
-            Slog.w(TAG_WM, "Exception thrown when killing surface session " + mSurfaceSession
-                    + " in session " + this + ": " + e.toString());
-        }
-        mSurfaceSession = null;
+        mProcess.mWindowSession = null;
         mAddedWindows.clear();
         mAlertWindows.clear();
         setHasOverlayUi(false);
@@ -857,7 +845,6 @@
                 pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
                 pw.print(" mAlertWindows="); pw.print(mAlertWindows);
                 pw.print(" mClientDead="); pw.print(mClientDead);
-                pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
         pw.print(prefix); pw.print("mPackageName="); pw.println(mPackageName);
         if (isSatellitePointingUiPackage()) {
             pw.print(prefix); pw.println("mIsSatellitePointingUiPackage=true");
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 0f9c001..52994c7 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -31,6 +31,8 @@
 import android.view.WindowManager;
 import android.window.TaskSnapshot;
 
+import com.android.window.flags.Flags;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -150,6 +152,9 @@
                 if (mOpenActivities.isEmpty()) {
                     return false;
                 }
+                if (Flags.alwaysCaptureActivitySnapshot()) {
+                    return true;
+                }
                 for (int i = mOpenActivities.size() - 1; i >= 0; --i) {
                     if (!mOpenActivities.get(i).mOptInOnBackInvoked) {
                         return false;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 9cfd396..57f9be0 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -32,8 +32,8 @@
 import android.view.SurfaceControl.Transaction;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.common.LogLevel;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -585,7 +585,6 @@
             ANIMATION_TYPE_APP_TRANSITION,
             ANIMATION_TYPE_SCREEN_ROTATION,
             ANIMATION_TYPE_DIMMER,
-            ANIMATION_TYPE_RECENTS,
             ANIMATION_TYPE_WINDOW_ANIMATION,
             ANIMATION_TYPE_INSETS_CONTROL,
             ANIMATION_TYPE_TOKEN_TRANSFORM,
@@ -604,7 +603,6 @@
             case ANIMATION_TYPE_APP_TRANSITION: return "app_transition";
             case ANIMATION_TYPE_SCREEN_ROTATION: return "screen_rotation";
             case ANIMATION_TYPE_DIMMER: return "dimmer";
-            case ANIMATION_TYPE_RECENTS: return "recents_animation";
             case ANIMATION_TYPE_WINDOW_ANIMATION: return "window_animation";
             case ANIMATION_TYPE_INSETS_CONTROL: return "insets_animation";
             case ANIMATION_TYPE_TOKEN_TRANSFORM: return "token_transform";
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d3df5fd..8c93b4fe 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -61,7 +61,6 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -95,7 +94,6 @@
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE_PRIV;
 import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_PINNABLE;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.TaskProto.AFFINITY;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
@@ -168,7 +166,6 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
-import android.view.WindowManager.TransitionOldType;
 import android.window.ITaskOrganizer;
 import android.window.PictureInPictureSurfaceTransaction;
 import android.window.StartingWindowInfo;
@@ -499,6 +496,13 @@
      */
     boolean mIsTrimmableFromRecents;
 
+    /**
+     * Bounds offset should be applied when calculating compatible configuration for apps targeting
+     * SDK level 34 or before.
+     */
+    int mOffsetXForInsets;
+    int mOffsetYForInsets;
+
     private final AnimatingActivityRegistry mAnimatingActivityRegistry =
             new AnimatingActivityRegistry();
 
@@ -1278,8 +1282,8 @@
         EventLogTags.writeWmTaskMoved(mTaskId, getRootTaskId(), getDisplayId(), toTop ? 1 : 0,
                 position);
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
-        if (taskDisplayArea != null && isLeafTask()) {
-            taskDisplayArea.onLeafTaskMoved(this, toTop, toBottom);
+        if (taskDisplayArea != null) {
+            taskDisplayArea.onTaskMoved(this, toTop, toBottom);
         }
         if (isPersistable) {
             mLastTimeMoved = System.currentTimeMillis();
@@ -2950,14 +2954,6 @@
         if (isOrganized()) {
             return false;
         }
-        // Don't animate while the task runs recents animation but only if we are in the mode
-        // where we cancel with deferred screenshot, which means that the controller has
-        // transformed the task.
-        final RecentsAnimationController controller = mWmService.getRecentsAnimationController();
-        if (controller != null && controller.isAnimatingTask(this)
-                && controller.shouldDeferCancelUntilNextTransition()) {
-            return false;
-        }
         return true;
     }
 
@@ -2969,8 +2965,7 @@
 
     /** Checking if self or its child tasks are animated by recents animation. */
     boolean isAnimatingByRecents() {
-        return isAnimating(CHILDREN, ANIMATION_TYPE_RECENTS)
-                || mTransitionController.isTransientHide(this);
+        return mTransitionController.isTransientHide(this);
     }
 
     WindowState getTopVisibleAppMainWindow() {
@@ -3275,30 +3270,6 @@
     }
 
     @Override
-    protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
-            @TransitionOldType int transit, boolean isVoiceInteraction,
-            @Nullable ArrayList<WindowContainer> sources) {
-        final RecentsAnimationController control = mWmService.getRecentsAnimationController();
-        if (control != null) {
-            // We let the transition to be controlled by RecentsAnimation, and callback task's
-            // RemoteAnimationTarget for remote runner to animate.
-            if (enter && !isActivityTypeHomeOrRecents()) {
-                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
-                        "applyAnimationUnchecked, control: %s, task: %s, transit: %s",
-                        control, asTask(), AppTransition.appTransitionOldToString(transit));
-                final int size = sources != null ? sources.size() : 0;
-                control.addTaskToTargets(this, (type, anim) -> {
-                    for (int i = 0; i < size; ++i) {
-                        sources.get(i).onAnimationFinished(type, anim);
-                    }
-                });
-            }
-        } else {
-            super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
-        }
-    }
-
-    @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
         mAnimatingActivityRegistry.dump(pw, "AnimatingApps:", prefix);
@@ -3330,6 +3301,7 @@
 
         info.userId = isLeafTask() ? mUserId : mCurrentUser;
         info.taskId = mTaskId;
+        info.effectiveUid = effectiveUid;
         info.displayId = getDisplayId();
         info.displayAreaFeatureId = tda != null ? tda.mFeatureId : FEATURE_UNDEFINED;
         final Intent baseIntent = getBaseIntent();
@@ -3507,7 +3479,7 @@
                         | StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS);
         if ((info.startingWindowTypeParameter
                 & StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) {
-            final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION);
+            final WindowState topMainWin = getTopFullscreenMainWindow();
             if (topMainWin != null) {
                 info.mainWindowLayoutParams = topMainWin.getAttrs();
                 info.requestedVisibleTypes = topMainWin.getRequestedVisibleTypes();
@@ -3535,7 +3507,7 @@
      * {@link android.window.TaskFragmentOrganizer}
      */
     TaskFragmentParentInfo getTaskFragmentParentInfo() {
-        return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(),
+        return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), mTaskId,
                 shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity(),
                 getDecorSurface());
     }
@@ -4540,7 +4512,8 @@
             reparent(parent, MAX_VALUE);
         }
 
-        setWindowingMode(mMultiWindowRestoreWindowingMode);
+        // mMultiWindowRestoreWindowingMode is INVALID for non-root tasks
+        setRootTaskWindowingMode(mMultiWindowRestoreWindowingMode);
     }
 
     @Override
@@ -4553,18 +4526,30 @@
             return;
         }
 
-        setWindowingMode(windowingMode, false /* creating */);
+        setWindowingModeInner(windowingMode, false /* creating */);
     }
 
     /**
-     * Specialization of {@link #setWindowingMode(int)} for this subclass.
+     * Version of {@link #setWindowingMode(int)} for root tasks.
      *
      * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending
      *         on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the
      *         previous non-transient mode if this root task is currently in a transient mode.
+     */
+    public void setRootTaskWindowingMode(int preferredWindowingMode) {
+        if (!isRootTask()) {
+            Slog.wtf(TAG, "Trying to set root-task windowing-mode on a non-root-task: " + this,
+                    new Throwable());
+            super.setWindowingMode(preferredWindowingMode);
+            return;
+        }
+        setWindowingModeInner(preferredWindowingMode, false /* creating */);
+    }
+
+    /**
      * @param creating {@code true} if this is being run during task construction.
      */
-    void setWindowingMode(int preferredWindowingMode, boolean creating) {
+    private void setWindowingModeInner(int preferredWindowingMode, boolean creating) {
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
         if (taskDisplayArea == null) {
             Slog.d(TAG, "taskDisplayArea is null, bail early");
@@ -4657,6 +4642,9 @@
                     mTransitionController.collect(topActivity);
 
                     final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
+                    // Reset the activity windowing mode to match the parent.
+                    topActivity.getRequestedOverrideConfiguration()
+                            .windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
                     topActivity.reparent(lastParentBeforePip,
                             lastParentBeforePip.getChildCount() /* top */,
                             "movePinnedActivityToOriginalTask");
@@ -4689,6 +4677,15 @@
                 // it does not follow the ActivityStarter path.
                 if (topActivity.shouldBeVisible()) {
                     mAtmService.resumeAppSwitches();
+                    // In pip1, when expanding pip to full-screen, the "behind" task is not
+                    // actually becoming invisible since task windowing mode is pinned.
+                    if (!isPip2ExperimentEnabled) {
+                        final ActivityRecord ar = mAtmService.mLastResumedActivity;
+                        if (ar != null && ar.getTask() != null) {
+                            mAtmService.takeTaskSnapshot(ar.getTask().mTaskId,
+                                    true /* updateCache */);
+                        }
+                    }
                 }
             } else if (isPip2ExperimentEnabled) {
                 super.setWindowingMode(windowingMode);
@@ -4740,7 +4737,7 @@
             }
         }
         if (isAttached()) {
-            setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
             moveTaskToBackInner(this, null /* transition */);
         }
         if (top.isAttached()) {
@@ -5086,10 +5083,10 @@
         return someActivityResumed;
     }
 
-    /** @see #resumeTopActivityUncheckedLocked(ActivityRecord, ActivityOptions, boolean) */
     @GuardedBy("mService")
-    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
-        return resumeTopActivityUncheckedLocked(prev, options, false /* skipPause */);
+    boolean resumeTopActivityUncheckedLocked() {
+        return resumeTopActivityUncheckedLocked(null /* prev */, null /* options */,
+                false /* skipPause */);
     }
 
     @GuardedBy("mService")
@@ -5138,8 +5135,7 @@
                 // Try to move focus to the next visible root task with a running activity if this
                 // root task is not covering the entire screen or is on a secondary display with
                 // no home root task.
-                return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedTask,
-                        prev, null /* targetOptions */);
+                return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedTask, prev);
             }
         }
 
@@ -5661,8 +5657,10 @@
             if (noAnimation) {
                 mDisplayContent.prepareAppTransition(TRANSIT_NONE);
                 mTaskSupervisor.mNoAnimActivities.add(top);
-                mTransitionController.collect(top);
-                mTransitionController.setNoAnimation(top);
+                if (mTransitionController.isShellTransitionsEnabled()) {
+                    mTransitionController.collect(top);
+                    mTransitionController.setNoAnimation(top);
+                }
                 ActivityOptions.abort(options);
             } else {
                 updateTransitLocked(TRANSIT_TO_FRONT, options);
@@ -5847,6 +5845,10 @@
         super.dumpInner(prefix, pw, dumpAll, dumpPackage);
         if (mCreatedByOrganizer) {
             pw.println(prefix + "  mCreatedByOrganizer=true");
+            if (mOffsetXForInsets != 0 || mOffsetYForInsets != 0) {
+                pw.println(prefix + "  mOffsetXForInsets=" + mOffsetXForInsets
+                        + " mOffsetYForInsets=" + mOffsetYForInsets);
+            }
         }
         if (mLastNonFullscreenBounds != null) {
             pw.print(prefix); pw.print("  mLastNonFullscreenBounds=");
@@ -6002,7 +6004,7 @@
         }
 
         final Task task = getBottomMostTask();
-        setWindowingMode(WINDOWING_MODE_UNDEFINED);
+        setRootTaskWindowingMode(WINDOWING_MODE_UNDEFINED);
 
         // Task could have been removed from the hierarchy due to windowing mode change
         // where its only child is reparented back to their original parent task.
@@ -6103,9 +6105,7 @@
 
         if (canBeLaunchedOnDisplay(newParent.getDisplayId())) {
             reparent(newParent, onTop ? POSITION_TOP : POSITION_BOTTOM);
-            if (isLeafTask()) {
-                newParent.onLeafTaskMoved(this, onTop, !onTop);
-            }
+            newParent.onTaskMoved(this, onTop, !onTop);
         } else {
             Slog.w(TAG, "Task=" + this + " can't reparent to " + newParent);
         }
@@ -6194,26 +6194,6 @@
         ActivityOptions.abort(options);
     }
 
-    boolean shouldSleepActivities() {
-        final DisplayContent display = mDisplayContent;
-        final boolean isKeyguardGoingAway = (mDisplayContent != null)
-                ? mDisplayContent.isKeyguardGoingAway()
-                : mRootWindowContainer.getDefaultDisplay().isKeyguardGoingAway();
-
-        // Do not sleep activities in this root task if we're marked as focused and the keyguard
-        // is in the process of going away.
-        if (isKeyguardGoingAway && isFocusedRootTaskOnDisplay()
-                // Avoid resuming activities on secondary displays since we don't want bubble
-                // activities to be resumed while bubble is still collapsed.
-                // TODO(b/113840485): Having keyguard going away state for secondary displays.
-                && display != null
-                && display.isDefaultDisplay) {
-            return false;
-        }
-
-        return display != null ? display.isSleeping() : mAtmService.isSleepingLocked();
-    }
-
     private Rect getRawBounds() {
         return super.getBounds();
     }
@@ -6245,8 +6225,8 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            @WindowTracingLogLevel int logLevel) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
@@ -6674,7 +6654,7 @@
 
             // Set windowing mode after attached to display area or it abort silently.
             if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
-                task.setWindowingMode(mWindowingMode, true /* creating */);
+                task.setWindowingModeInner(mWindowingMode, true /* creating */);
             }
             return task;
         }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index dba1c36..638e92f 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -37,6 +37,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.ColorInt;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
@@ -142,13 +143,6 @@
      * current focused root task.
      */
     Task mLastFocusedRootTask;
-    /**
-     * All of the root tasks on this display. Order matters, topmost root task is in front of all
-     * other root tasks, bottommost behind. Accessed directly by ActivityManager package classes.
-     * Any calls changing the list should also call {@link #onRootTaskOrderChanged(Task)}.
-     */
-    private ArrayList<OnRootTaskOrderChangedListener> mRootTaskOrderChangedCallbacks =
-            new ArrayList<>();
 
     /**
      * The task display area is removed from the system and we are just waiting for all activities
@@ -331,7 +325,6 @@
         mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded("addChildTask");
 
         mAtmService.updateSleepIfNeededLocked();
-        onRootTaskOrderChanged(task);
     }
 
     @Override
@@ -423,10 +416,6 @@
 
         // Update the top resumed activity because the preferred top focusable task may be changed.
         mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded("positionChildTaskAt");
-
-        if (mChildren.indexOf(child) != oldPosition) {
-            onRootTaskOrderChanged(child);
-        }
     }
 
     void onLeafTaskRemoved(int taskId) {
@@ -435,7 +424,19 @@
         }
     }
 
-    void onLeafTaskMoved(Task t, boolean toTop, boolean toBottom) {
+    void onTaskMoved(@NonNull Task t, boolean toTop, boolean toBottom) {
+        if (toBottom && !t.isLeafTask()) {
+            // Return early when a non-leaf task moved to bottom, to prevent sending duplicated
+            // leaf task movement callback if the leaf task is moved along with its parent tasks.
+            // Unless, we also track the task id, like `mLastLeafTaskToFrontId`.
+            return;
+        }
+
+        final Task topLeafTask = t.getTopLeafTask();
+        onLeafTaskMoved(topLeafTask, toTop, toBottom);
+    }
+
+    void onLeafTaskMoved(@NonNull Task t, boolean toTop, boolean toBottom) {
         if (toBottom) {
             mAtmService.getTaskChangeNotificationController().notifyTaskMovedToBack(
                     t.getTaskInfo());
@@ -831,7 +832,6 @@
             mLaunchAdjacentFlagRootTask = null;
         }
         mDisplayContent.releaseSelfIfNeeded();
-        onRootTaskOrderChanged(rootTask);
     }
 
     /**
@@ -923,7 +923,7 @@
             if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
                     && candidateTask.getWindowingMode() != windowingMode) {
                 candidateTask.mTransitionController.collect(candidateTask);
-                candidateTask.setWindowingMode(windowingMode);
+                candidateTask.setRootTaskWindowingMode(windowingMode);
             }
             return candidateTask.getRootTask();
         }
@@ -1730,35 +1730,6 @@
         return mRemoved;
     }
 
-    /**
-     * Adds a listener to be notified whenever the root task order in the display changes. Currently
-     * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the
-     * current animation when the system state changes.
-     */
-    void registerRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) {
-        if (!mRootTaskOrderChangedCallbacks.contains(listener)) {
-            mRootTaskOrderChangedCallbacks.add(listener);
-        }
-    }
-
-    /**
-     * Removes a previously registered root task order change listener.
-     */
-    void unregisterRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) {
-        mRootTaskOrderChangedCallbacks.remove(listener);
-    }
-
-    /**
-     * Notifies of a root task order change
-     *
-     * @param rootTask The root task which triggered the order change
-     */
-    void onRootTaskOrderChanged(Task rootTask) {
-        for (int i = mRootTaskOrderChangedCallbacks.size() - 1; i >= 0; i--) {
-            mRootTaskOrderChangedCallbacks.get(i).onRootTaskOrderChanged(rootTask);
-        }
-    }
-
     @Override
     boolean canCreateRemoteAnimationTarget() {
         // In the legacy transition system, promoting animation target from TaskFragment to
@@ -1773,13 +1744,6 @@
         return mDisplayContent.isHomeSupported() && mCanHostHomeTask;
     }
 
-    /**
-     * Callback for when the order of the root tasks in the display changes.
-     */
-    interface OnRootTaskOrderChangedListener {
-        void onRootTaskOrderChanged(Task rootTask);
-    }
-
     void ensureActivitiesVisible(ActivityRecord starting, boolean notifyClients) {
         mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index ed0dc3b..f58b322 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -478,6 +478,10 @@
         mTaskFragmentOrganizerProcessName = processName;
     }
 
+    void onTaskFragmentOrganizerRestarted(@NonNull ITaskFragmentOrganizer organizer) {
+        mTaskFragmentOrganizer = organizer;
+    }
+
     void onTaskFragmentOrganizerRemoved() {
         mTaskFragmentOrganizer = null;
     }
@@ -1776,11 +1780,6 @@
         if (resuming != null) {
             // We do not want to trigger auto-PiP upon launch of a translucent activity.
             final boolean resumingOccludesParent = resuming.occludesParent();
-            // Resuming the new resume activity only if the previous activity can't go into Pip
-            // since we want to give Pip activities a chance to enter Pip before resuming the
-            // next activity.
-            final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
-                    "shouldAutoPipWhilePausing", userLeaving);
 
             if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
                 // If a new task is being launched, then mark the existing top activity as
@@ -1790,6 +1789,12 @@
                 Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
                         resuming, resuming.getOptions());
             }
+
+            // Resuming the new resume activity only if the previous activity can't go into Pip
+            // since we want to give Pip activities a chance to enter Pip before resuming the
+            // next activity.
+            final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
+                    "shouldAutoPipWhilePausing", userLeaving);
             if (prev.supportsEnterPipOnTaskSwitch && userLeaving
                     && resumingOccludesParent && lastResumedCanPip
                     && prev.pictureInPictureArgs.isAutoEnterEnabled()) {
@@ -1923,7 +1928,7 @@
                     prev.setState(STOPPING, "completePausedLocked");
                 } else if (!prev.isVisibleRequested() || shouldSleepOrShutDownActivities()) {
                     // Clear out any deferred client hide we might currently have.
-                    prev.setDeferHidingClient(false);
+                    prev.clearDeferHidingClient();
                     // If we were visible then resumeTopActivities will release resources before
                     // stopping.
                     prev.addToStopping(true /* scheduleIdle */, false /* idleDelayed */,
@@ -1944,8 +1949,7 @@
         if (resumeNext) {
             final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
             if (topRootTask != null && !topRootTask.shouldSleepOrShutDownActivities()) {
-                mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev,
-                        null /* targetOptions */);
+                mRootWindowContainer.resumeFocusedTasksTopActivities(topRootTask, prev);
             } else {
                 // checkReadyForSleep();
                 final ActivityRecord top =
@@ -2141,8 +2145,22 @@
     }
 
     boolean shouldSleepActivities() {
-        final Task task = getRootTask();
-        return task != null && task.shouldSleepActivities();
+        final DisplayContent dc = mDisplayContent;
+        if (dc == null) {
+            return mAtmService.isSleepingLocked();
+        }
+        if (!dc.isSleeping()) {
+            return false;
+        }
+        // In case the unlocking order is keyguard-going-away -> screen-turning-on (display is
+        // sleeping by screen-off-token which may be notified to release from power manager's
+        // thread), keep the activities resume-able to avoid extra activity lifecycle when
+        // performing keyguard-going-away. This only applies to default display because currently
+        // the per-display keyguard-going-away state is assigned from a global signal.
+        if (!dc.isDefaultDisplay || !dc.isKeyguardGoingAway()) {
+            return true;
+        }
+        return !shouldBeVisible(null /* starting */);
     }
 
     @Override
@@ -2220,7 +2238,7 @@
 
     static class ConfigOverrideHint {
         @Nullable DisplayInfo mTmpOverrideDisplayInfo;
-        @Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
+        @Nullable AppCompatDisplayInsets mTmpCompatInsets;
         @Nullable Rect mParentAppBoundsOverride;
         int mTmpOverrideConfigOrientation;
         boolean mUseOverrideInsetsForConfig;
@@ -2290,7 +2308,7 @@
     void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
             @NonNull Configuration parentConfig, @Nullable ConfigOverrideHint overrideHint) {
         DisplayInfo overrideDisplayInfo = null;
-        ActivityRecord.CompatDisplayInsets compatInsets = null;
+        AppCompatDisplayInsets compatInsets = null;
         boolean useOverrideInsetsForConfig = false;
         if (overrideHint != null) {
             overrideDisplayInfo = overrideHint.mTmpOverrideDisplayInfo;
@@ -2960,7 +2978,13 @@
     }
 
     boolean shouldRemoveSelfOnLastChildRemoval() {
-        return !mCreatedByOrganizer || mIsRemovalRequested;
+        if (!mCreatedByOrganizer || mIsRemovalRequested) {
+            return true;
+        }
+        // The TaskFragmentOrganizer may be killed while the embedded TaskFragments remains in WM
+        // core. The embedded TaskFragment should still be removed when the child activities are
+        // all gone (like package force-stopped).
+        return mIsEmbedded && mTaskFragmentOrganizer == null;
     }
 
     /**
@@ -3325,8 +3349,8 @@
 
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            @WindowTracingLogLevel int logLevel) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 561ff7d..5aa34d2 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -46,6 +46,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
 import android.window.ITaskFragmentOrganizer;
 import android.window.ITaskFragmentOrganizerController;
@@ -86,6 +87,12 @@
     private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
             new ArrayMap<>();
     /**
+     * The cached {@link TaskFragmentOrganizerState} for the {@link ITaskFragmentOrganizer} that
+     * have no running process.
+     */
+    private final ArrayList<TaskFragmentOrganizerState> mCachedTaskFragmentOrganizerStates =
+            new ArrayList<>();
+    /**
      * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
      */
     private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
@@ -105,11 +112,16 @@
      * {@link TaskFragment TaskFragments}.
      */
     private class TaskFragmentOrganizerState implements IBinder.DeathRecipient {
+        @NonNull
         private final ArrayList<TaskFragment> mOrganizedTaskFragments = new ArrayList<>();
-        private final IApplicationThread mAppThread;
-        private final ITaskFragmentOrganizer mOrganizer;
-        private final int mOrganizerPid;
         private final int mOrganizerUid;
+        @NonNull
+        private IApplicationThread mAppThread;
+        @NonNull
+        private ITaskFragmentOrganizer mOrganizer;
+        private int mOrganizerPid;
+        @Nullable
+        private Bundle mSavedState;
 
         /**
          * Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
@@ -145,6 +157,13 @@
         private final boolean mIsSystemOrganizer;
 
         /**
+         * {@link RemoteAnimationDefinition} for embedded activities transition animation that is
+         * organized by this organizer.
+         */
+        @Nullable
+        private RemoteAnimationDefinition mRemoteAnimationDefinition;
+
+        /**
          * Map from {@link TaskFragmentTransaction#getTransactionToken()} to the
          * {@link Transition#getSyncId()} that has been deferred. {@link TransitionController} will
          * wait until the organizer finished handling the {@link TaskFragmentTransaction}.
@@ -182,6 +201,24 @@
             }
         }
 
+        void restore(@NonNull ITaskFragmentOrganizer organizer, int pid) {
+            mOrganizer = organizer;
+            mOrganizerPid = pid;
+            mAppThread = getAppThread(pid, mOrganizerUid);
+            for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
+                mOrganizedTaskFragments.get(i).onTaskFragmentOrganizerRestarted(organizer);
+            }
+            try {
+                mOrganizer.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "TaskFragmentOrganizer failed to register death recipient");
+            }
+        }
+
+        boolean hasSavedState() {
+            return mSavedState != null && !mSavedState.isEmpty();
+        }
+
         /**
          * @return {@code true} if taskFragment is organized and not sent the appeared event before.
          */
@@ -226,12 +263,18 @@
                         containsNonEmbeddedActivity ? null : task,
                         null /* remoteTransition */, null /* displayChange */);
             }
+
             // Defer to avoid unnecessary layout when there are multiple TaskFragments removal.
             mAtmService.deferWindowLayout();
             try {
-                while (!mOrganizedTaskFragments.isEmpty()) {
-                    final TaskFragment taskFragment = mOrganizedTaskFragments.remove(0);
-                    taskFragment.removeImmediately();
+                // Removes the TaskFragments if no saved state or is empty.
+                final boolean haveSavedState = hasSavedState();
+                for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
+                    final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
+                    if (!haveSavedState || !taskFragment.hasChild()) {
+                        mOrganizedTaskFragments.remove(i);
+                        taskFragment.removeImmediately();
+                    }
                 }
             } finally {
                 mAtmService.continueWindowLayout();
@@ -407,8 +450,7 @@
                 change.setTaskFragmentToken(lastParentTfToken);
             }
             // Only pass the activity token to the client if it belongs to the same process.
-            if (Flags.fixPipRestoreToOverlay() && nextFillTaskActivity != null
-                    && nextFillTaskActivity.getPid() == mOrganizerPid) {
+            if (nextFillTaskActivity != null && nextFillTaskActivity.getPid() == mOrganizerPid) {
                 change.setOtherActivityToken(nextFillTaskActivity.token);
             }
             return change;
@@ -479,15 +521,15 @@
     }
 
     @Override
-    public void registerOrganizer(
-            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
-        registerOrganizerInternal(
-                organizer,
-                Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer);
+    public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer,
+            boolean isSystemOrganizer, @NonNull Bundle outSavedState) {
+        registerOrganizerInternal(organizer,
+                Flags.taskFragmentSystemOrganizerFlag() && isSystemOrganizer, outSavedState);
     }
 
     private void registerOrganizerInternal(
-            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer) {
+            @NonNull ITaskFragmentOrganizer organizer, boolean isSystemOrganizer,
+            @NonNull Bundle outSavedState) {
         if (isSystemOrganizer) {
             enforceTaskPermission("registerSystemOrganizer()");
         }
@@ -497,16 +539,49 @@
             ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
                     "Register task fragment organizer=%s uid=%d pid=%d",
                     organizer.asBinder(), uid, pid);
+
             if (isOrganizerRegistered(organizer)) {
                 throw new IllegalStateException(
                         "Replacing existing organizer currently unsupported");
             }
+
+            if (restoreFromCachedStateIfPossible(organizer, pid, uid, outSavedState)) {
+                return;
+            }
+
             mTaskFragmentOrganizerState.put(organizer.asBinder(),
                     new TaskFragmentOrganizerState(organizer, pid, uid, isSystemOrganizer));
             mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
         }
     }
 
+    private boolean restoreFromCachedStateIfPossible(@NonNull ITaskFragmentOrganizer organizer,
+            int pid, int uid, @NonNull Bundle outSavedState) {
+        if (!Flags.aeBackStackRestore()) {
+            return false;
+        }
+
+        TaskFragmentOrganizerState cachedState = null;
+        int i = mCachedTaskFragmentOrganizerStates.size() - 1;
+        for (; i >= 0; i--) {
+            final TaskFragmentOrganizerState state = mCachedTaskFragmentOrganizerStates.get(i);
+            if (state.mOrganizerUid == uid) {
+                cachedState = state;
+                break;
+            }
+        }
+        if (cachedState == null) {
+            return false;
+        }
+
+        mCachedTaskFragmentOrganizerStates.remove(cachedState);
+        outSavedState.putAll(cachedState.mSavedState);
+        cachedState.restore(organizer, pid);
+        mTaskFragmentOrganizerState.put(organizer.asBinder(), cachedState);
+        mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
+        return true;
+    }
+
     @Override
     public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
         final int pid = Binder.getCallingPid();
@@ -525,6 +600,67 @@
     }
 
     @Override
+    public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer,
+            @NonNull RemoteAnimationDefinition definition) {
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Register remote animations for organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            if (organizerState == null) {
+                throw new IllegalStateException("The organizer hasn't been registered.");
+            }
+            if (organizerState.mRemoteAnimationDefinition != null) {
+                throw new IllegalStateException(
+                        "The organizer has already registered remote animations="
+                                + organizerState.mRemoteAnimationDefinition);
+            }
+
+            definition.setCallingPidUid(pid, uid);
+            organizerState.mRemoteAnimationDefinition = definition;
+        }
+    }
+
+    @Override
+    public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer) {
+        final int pid = Binder.getCallingPid();
+        final long uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Unregister remote animations for organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            if (organizerState == null) {
+                Slog.e(TAG, "The organizer hasn't been registered.");
+                return;
+            }
+
+            organizerState.mRemoteAnimationDefinition = null;
+        }
+    }
+
+    @Override
+    public void setSavedState(@NonNull ITaskFragmentOrganizer organizer, @Nullable Bundle state) {
+        final int pid = Binder.getCallingPid();
+        final int uid = Binder.getCallingUid();
+        synchronized (mGlobalLock) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set state for organizer=%s uid=%d pid=%d",
+                    organizer.asBinder(), uid, pid);
+            final TaskFragmentOrganizerState organizerState = mTaskFragmentOrganizerState.get(
+                    organizer.asBinder());
+            if (organizerState == null) {
+                throw new IllegalStateException("The organizer hasn't been registered.");
+            }
+
+            organizerState.mSavedState = state;
+        }
+    }
+
+    @Override
     public void onTransactionHandled(@NonNull IBinder transactionToken,
             @NonNull WindowContainerTransaction wct,
             @WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
@@ -565,6 +701,25 @@
         }
     }
 
+    /**
+     * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
+     * {@code null} if it doesn't.
+     */
+    @Nullable
+    public RemoteAnimationDefinition getRemoteAnimationDefinition(
+            @NonNull ITaskFragmentOrganizer organizer) {
+        synchronized (mGlobalLock) {
+            final TaskFragmentOrganizerState organizerState =
+                    mTaskFragmentOrganizerState.get(organizer.asBinder());
+            if (organizerState == null) {
+                Slog.e(TAG, "TaskFragmentOrganizer has been unregistered or died when trying"
+                        + " to play animation on its organized windows.");
+                return null;
+            }
+            return organizerState.mRemoteAnimationDefinition;
+        }
+    }
+
     int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
         final TaskFragmentOrganizerState state = validateAndGetState(organizer);
         return state.mOrganizerUid;
@@ -769,9 +924,11 @@
         // Remove any pending event of this organizer first because state.dispose() may trigger
         // event dispatch as result of surface placement.
         mPendingTaskFragmentEvents.remove(organizer.asBinder());
-        // remove all of the children of the organized TaskFragment
         state.dispose(reason);
         mTaskFragmentOrganizerState.remove(organizer.asBinder());
+        if (state.hasSavedState()) {
+            mCachedTaskFragmentOrganizerStates.add(state);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 2c5beda..de2e4f5 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -390,7 +390,7 @@
                 t.mTaskAppearedSent = false;
             }
             if (removeFromSystem) {
-                mService.removeTask(t.mTaskId);
+                mService.removeTask(t);
             }
             return taskAppearedSent;
         }
@@ -684,7 +684,8 @@
                 removalInfo.playRevealAnimation = false;
             } else if (removalInfo.playRevealAnimation && playShiftUpAnimation) {
                 removalInfo.roundedCornerRadius =
-                        topActivity.mLetterboxUiController.getRoundedCornersRadius(mainWindow);
+                        topActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+                                .getRoundedCornersRadius(mainWindow);
                 removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
                 removalInfo.mainFrame = new Rect(mainWindow.getFrame());
                 removalInfo.mainFrame.offsetTo(mainWindow.mSurfacePosition.x,
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 486a61b..e6226ab 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -181,7 +181,7 @@
     final @TransitionType int mType;
     private int mSyncId = -1;
     private @TransitionFlags int mFlags;
-    private final TransitionController mController;
+    final TransitionController mController;
     private final BLASTSyncEngine mSyncEngine;
     private final Token mToken;
 
@@ -329,6 +329,9 @@
      */
     ArrayList<ActivityRecord> mConfigAtEndActivities = null;
 
+    /** The current head of the chain of actions related to this transition. */
+    ActionChain mChainHead = null;
+
     @VisibleForTesting
     Transition(@TransitionType int type, @TransitionFlags int flags,
             TransitionController controller, BLASTSyncEngine syncEngine) {
@@ -420,6 +423,18 @@
                 updateTransientFlags(mChanges.valueAt(i));
             }
         }
+
+        // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
+        // Also interpret HOME transient launch as recents
+        if (activity.isActivityTypeHomeOrRecents()) {
+            addFlag(TRANSIT_FLAG_IS_RECENTS);
+            // When starting recents animation, we assume the recents activity is behind the app
+            // task and should not affect system bar appearance,
+            // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
+            // the gesture threshold.
+            activity.getTask().setCanAffectSystemUiFlags(false);
+        }
+
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
                 + "transient-launch", mSyncId, activity);
     }
@@ -1039,9 +1054,8 @@
      * needs to be passed/applied in shell because until finish is called, shell owns the surfaces.
      * Additionally, this gives shell the ability to better deal with merged transitions.
      */
-    private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info) {
-        // usually only size 1
-        final ArraySet<DisplayContent> displays = new ArraySet<>();
+    private void buildFinishTransaction(SurfaceControl.Transaction t, TransitionInfo info,
+            DisplayContent[] participantDisplays) {
         for (int i = mTargets.size() - 1; i >= 0; --i) {
             final WindowContainer<?> target = mTargets.get(i).mContainer;
             if (target.getParent() == null) continue;
@@ -1053,7 +1067,6 @@
             t.setCornerRadius(targetLeash, 0);
             t.setShadowRadius(targetLeash, 0);
             t.setAlpha(targetLeash, 1);
-            displays.add(target.getDisplayContent());
             // For config-at-end, the end-transform will be reset after the config is actually
             // applied in the client (since the transform depends on config). The other properties
             // remain here because shell might want to persistently override them.
@@ -1067,9 +1080,8 @@
         }
         // Need to update layers on involved displays since they were all paused while
         // the animation played. This puts the layers back into the correct order.
-        for (int i = displays.size() - 1; i >= 0; --i) {
-            if (displays.valueAt(i) == null) continue;
-            assignLayers(displays.valueAt(i), t);
+        for (int i = participantDisplays.length - 1; i >= 0; --i) {
+            assignLayers(participantDisplays[i], t);
         }
 
         for (int i = 0; i < info.getRootCount(); ++i) {
@@ -1195,10 +1207,14 @@
      * The transition has finished animating and is ready to finalize WM state. This should not
      * be called directly; use {@link TransitionController#finishTransition} instead.
      */
-    void finishTransition() {
+    void finishTransition(@NonNull ActionChain chain) {
         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER) && mIsPlayerEnabled) {
             asyncTraceEnd(System.identityHashCode(this));
         }
+        if (!chain.isFinishing()) {
+            throw new IllegalStateException("Can't finish on a non-finishing transition "
+                    + chain.mTransition);
+        }
         mLogger.mFinishTimeNs = SystemClock.elapsedRealtimeNanos();
         mController.mLoggerHandler.post(mLogger::logOnFinish);
         mController.mTransitionTracer.logFinishedTransition(this);
@@ -1441,7 +1457,7 @@
             // Clean up input monitors (for recents)
             final DisplayContent dc =
                     mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
-            dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+            dc.getInputMonitor().setActiveRecents(null /* task */, null /* layer */);
             dc.getInputMonitor().updateInputWindowsLw(false /* force */);
         }
         if (mTransientLaunches != null) {
@@ -1721,6 +1737,7 @@
                 transaction.addTransactionCompletedListener(Runnable::run,
                         (stats) -> listener.run());
             }
+            mTransactionCompletedListeners = null;
         }
 
         // Fall-back to the default display if there isn't one participating.
@@ -1763,6 +1780,7 @@
         if (mPriorVisibilityMightBeDirty) {
             updatePriorVisibility();
         }
+
         // Resolve the animating targets from the participants.
         mTargets = calculateTargets(mParticipants, mChanges);
 
@@ -1776,6 +1794,8 @@
         mController.moveToPlaying(this);
 
         // Repopulate the displays based on the resolved targets.
+        final DisplayContent[] participantDisplays = mTargetDisplays.toArray(
+                new DisplayContent[mTargetDisplays.size()]);
         mTargetDisplays.clear();
         for (int i = 0; i < info.getRootCount(); ++i) {
             final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(
@@ -1790,7 +1810,7 @@
                 // If on a rotation leash, the wallpaper token surface needs to be shown explicitly
                 // because shell only gets the leash and the wallpaper token surface is not allowed
                 // to be changed by non-transition logic until the transition is finished.
-                if (Flags.ensureWallpaperInTransitions() && wp.isVisibleRequested()
+                if (wp.mWmService.mFlags.mEnsureWallpaperInTransitions && wp.isVisibleRequested()
                         && wp.getFixedRotationLeash() != null) {
                     transaction.show(wp.mSurfaceControl);
                 }
@@ -1840,8 +1860,9 @@
             // already been reset by the original hiding-transition's finishTransaction (we can't
             // show in the finishTransaction because by then the activity doesn't hide until
             // surface placement).
-            for (WindowContainer p = ar.getParent(); p != null && !containsChangeFor(p, mTargets);
-                    p = p.getParent()) {
+            for (WindowContainer p = ar.getParent();
+                 p != null && !containsChangeFor(p, mTargets) && !p.isOrganized();
+                 p = p.getParent()) {
                 if (p.getSurfaceControl() != null) {
                     transaction.show(p.getSurfaceControl());
                 }
@@ -1868,7 +1889,9 @@
                 controller.setupStartTransaction(transaction);
             }
         }
-        buildFinishTransaction(mFinishTransaction, info);
+        // Use participant displays here (rather than just targets) because it's possible for
+        // there to be order changes between non-top tasks in an otherwise no-op transition.
+        buildFinishTransaction(mFinishTransaction, info, participantDisplays);
         mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
         buildCleanupTransaction(mCleanupTransaction, info);
         if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) {
@@ -2148,7 +2171,7 @@
         if (mFinishTransaction != null) {
             mFinishTransaction.apply();
         }
-        mController.finishTransition(this);
+        mController.finishTransition(mController.mAtm.mChainTracker.startFinish("clean-up", this));
     }
 
     private void cleanUpInternal() {
@@ -2201,7 +2224,8 @@
             if (wallpaper != null) {
                 if (!wallpaper.isVisible() && wallpaper.isVisibleRequested()) {
                     wallpaper.commitVisibility(showWallpaper);
-                } else if (Flags.ensureWallpaperInTransitions() && wallpaper.isVisible()
+                } else if (wallpaper.mWmService.mFlags.mEnsureWallpaperInTransitions
+                        && wallpaper.isVisible()
                         && !showWallpaper && !wallpaper.getDisplayContent().isKeyguardLocked()
                         && !wallpaperIsOwnTarget(wallpaper)) {
                     wallpaper.setVisibleRequested(false);
@@ -2233,7 +2257,7 @@
         // Recents has an input-consumer to grab input from the "live tile" app. Set that up here
         final InputConsumerImpl recentsAnimationInputConsumer =
                 dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
-        ActivityRecord recentsActivity = null;
+        Task recentsTask = null;
         if (recentsAnimationInputConsumer != null) {
             // Find the top-most going-away task and the recents activity. The top-most
             // is used as layer reference while the recents is used for registering the consumer
@@ -2248,20 +2272,20 @@
                 final int activityType = taskInfo.topActivityType;
                 final boolean isRecents = activityType == ACTIVITY_TYPE_HOME
                         || activityType == ACTIVITY_TYPE_RECENTS;
-                if (isRecents && recentsActivity == null) {
-                    recentsActivity = task.getTopVisibleActivity();
+                if (isRecents && recentsTask == null) {
+                    recentsTask = task;
                 } else if (!isRecents && topNonRecentsTask == null) {
                     topNonRecentsTask = task;
                 }
             }
-            if (recentsActivity != null && topNonRecentsTask != null) {
+            if (recentsTask != null && topNonRecentsTask != null) {
                 recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(
                         topNonRecentsTask.getBounds());
-                dc.getInputMonitor().setActiveRecents(recentsActivity, topNonRecentsTask);
+                dc.getInputMonitor().setActiveRecents(recentsTask, topNonRecentsTask);
             }
         }
 
-        if (recentsActivity == null) {
+        if (recentsTask == null) {
             // No recents activity on `dc`, its probably on a different display.
             return;
         }
@@ -2919,7 +2943,7 @@
                     // Use parent rotation because shell doesn't know the surface is rotated.
                     endRotation = parent.getWindowConfiguration().getRotation();
                 }
-            } else if (isWallpaper(target) && Flags.ensureWallpaperInTransitions()
+            } else if (isWallpaper(target) && target.mWmService.mFlags.mEnsureWallpaperInTransitions
                     && target.getRelativeDisplayRotation() != 0
                     && !target.mTransitionController.useShellTransitionsRotation()) {
                 // If the wallpaper is "fixed-rotated", shell is unaware of this, so use the
@@ -3363,6 +3387,11 @@
         return false;
     }
 
+    void recordChain(@NonNull ActionChain chain) {
+        chain.mPrevious = mChainHead;
+        mChainHead = chain;
+    }
+
     @VisibleForTesting
     static class ChangeInfo {
         private static final int FLAG_NONE = 0;
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 67d7b37..50fe69c 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -19,7 +19,6 @@
 import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
@@ -409,6 +408,10 @@
      */
     @Nullable
     Transition getCollectingTransition() {
+        if (mCollectingTransition != null && !mCollectingTransition.isCollecting()) {
+            Slog.wtfStack(TAG, "Collecting Transition (#" + mCollectingTransition.getSyncId()
+                    + ") is not collecting. state=" + mCollectingTransition.getState());
+        }
         return mCollectingTransition;
     }
 
@@ -918,7 +921,12 @@
     }
 
     /** @see Transition#finishTransition */
-    void finishTransition(Transition record) {
+    void finishTransition(@NonNull ActionChain chain) {
+        if (!chain.isFinishing()) {
+            throw new IllegalStateException("Can't finish on a non-finishing transition "
+                    + chain.mTransition);
+        }
+        final Transition record = chain.mTransition;
         // It is usually a no-op but make sure that the metric consumer is removed.
         mTransitionMetricsReporter.reportAnimationStart(record.getToken(), 0 /* startTime */);
         // It is a no-op if the transition did not change the display.
@@ -934,7 +942,7 @@
             mTrackCount = 0;
         }
         updateRunningRemoteAnimation(record, false /* isPlaying */);
-        record.finishTransition();
+        record.finishTransition(chain);
         for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) {
             final WindowState w = mAnimatingExitWindows.get(i);
             if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) {
@@ -1311,23 +1319,6 @@
     void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelowTask) {
         if (mCollectingTransition == null) return;
         mCollectingTransition.setTransientLaunch(activity, restoreBelowTask);
-
-        // TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
-        // Also interpret HOME transient launch as recents
-        if (activity.isActivityTypeHomeOrRecents()) {
-            mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
-            // When starting recents animation, we assume the recents activity is behind the app
-            // task and should not affect system bar appearance,
-            // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
-            // the gesture threshold.
-            activity.getTask().setCanAffectSystemUiFlags(false);
-        }
-    }
-
-    /** @see Transition#setCanPipOnFinish */
-    void setCanPipOnFinish(boolean canPipOnFinish) {
-        if (mCollectingTransition == null) return;
-        mCollectingTransition.setCanPipOnFinish(canPipOnFinish);
     }
 
     void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
@@ -1459,7 +1450,7 @@
      *
      * WARNING: ONLY use this if the transition absolutely cannot be deferred!
      */
-    @NonNull
+    @Nullable
     Transition createAndStartCollecting(int type) {
         if (mTransitionPlayers.isEmpty()) {
             return null;
diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java
index 39b2635..f1941af 100644
--- a/services/core/java/com/android/server/wm/TransparentPolicy.java
+++ b/services/core/java/com/android/server/wm/TransparentPolicy.java
@@ -31,6 +31,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -94,7 +95,7 @@
         }
         final boolean wasStarted = mTransparentPolicyState.isRunning();
         mTransparentPolicyState.reset();
-        // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the
+        // In case mActivityRecord.hasAppCompatDisplayInsetsWithoutOverride() we don't apply the
         // opaque activity constraints because we're expecting the activity is already letterboxed.
         final ActivityRecord firstOpaqueActivity = mActivityRecord.getTask().getActivity(
                 FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
@@ -158,12 +159,12 @@
         return mTransparentPolicyState.mInheritedOrientation;
     }
 
-    ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() {
-        return mTransparentPolicyState.mInheritedCompatDisplayInsets;
+    AppCompatDisplayInsets getInheritedAppCompatDisplayInsets() {
+        return mTransparentPolicyState.mInheritedAppCompatDisplayInsets;
     }
 
-    void clearInheritedCompatDisplayInsets() {
-        mTransparentPolicyState.clearInheritedCompatDisplayInsets();
+    void clearInheritedAppCompatDisplayInsets() {
+        mTransparentPolicyState.clearInheritedAppCompatDisplayInsets();
     }
 
     /**
@@ -188,6 +189,10 @@
         return mTransparentPolicyState.findOpaqueNotFinishingActivityBelow();
     }
 
+    void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+        pw.println(prefix + "isTransparentPolicyRunning=" + isRunning());
+    }
+
     // We evaluate the case when the policy should not be applied.
     private boolean shouldSkipTransparentPolicy(@Nullable ActivityRecord opaqueActivity) {
         if (opaqueActivity == null || opaqueActivity.isEmbedded()) {
@@ -196,8 +201,10 @@
             // never has letterbox.
             return true;
         }
+        final AppCompatSizeCompatModePolicy scmPolicy = mActivityRecord.mAppCompatController
+                .getAppCompatSizeCompatModePolicy();
         if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent()
-                || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) {
+                || scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance()) {
             return true;
         }
         return false;
@@ -234,9 +241,9 @@
         // The app compat state for the opaque activity if any
         private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
 
-        // The CompatDisplayInsets of the opaque activity beneath the translucent one.
+        // The AppCompatDisplayInsets of the opaque activity beneath the translucent one.
         @Nullable
-        private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
+        private AppCompatDisplayInsets mInheritedAppCompatDisplayInsets;
 
         @Nullable
         private ActivityRecord mFirstOpaqueActivity;
@@ -298,7 +305,7 @@
             }
             mInheritedOrientation = opaqueActivity.getRequestedConfigurationOrientation();
             mInheritedAppCompatState = opaqueActivity.getAppCompatState();
-            mInheritedCompatDisplayInsets = opaqueActivity.getCompatDisplayInsets();
+            mInheritedAppCompatDisplayInsets = opaqueActivity.getAppCompatDisplayInsets();
         }
 
         private void reset() {
@@ -310,7 +317,7 @@
             mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO;
             mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO;
             mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN;
-            mInheritedCompatDisplayInsets = null;
+            mInheritedAppCompatDisplayInsets = null;
             if (mFirstOpaqueActivity != null) {
                 mFirstOpaqueActivity.mAppCompatController.getTransparentPolicy()
                         .mDestroyListeners.remove(mActivityRecord.mAppCompatController
@@ -330,11 +337,13 @@
             // Do not enable the policy if the activity can affect display orientation.
             final int orientation = mActivityRecord.getOverrideOrientation();
             return orientation == SCREEN_ORIENTATION_UNSPECIFIED
+                    // This "!condition" is true if the activity is multi-window mode or the
+                    // display ignores requested orientation.
                     || !mActivityRecord.handlesOrientationChangeFromDescendant(orientation);
         }
 
-        private void clearInheritedCompatDisplayInsets() {
-            mInheritedCompatDisplayInsets = null;
+        private void clearInheritedAppCompatDisplayInsets() {
+            mInheritedAppCompatDisplayInsets = null;
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 9b868be..030e848 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -51,7 +51,7 @@
 
     void requireOverlaySurfaceControl() {
         if (mSurfaceControl == null) {
-            final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+            final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder()
                 .setContainerLayer()
                 .setHidden(true)
                 .setCallsite("TrustedOverlayHost.requireOverlaySurfaceControl")
@@ -112,11 +112,16 @@
         final SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
 
         for (int i = mOverlays.size() - 1; i >= 0; i--) {
-           SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
-           if (l.getSurfaceControl().isSameSurface(p.getSurfaceControl())) {
-               mOverlays.remove(i);
-               t.reparent(l.getSurfaceControl(), null);
-               l.release();
+            SurfaceControlViewHost.SurfacePackage l = mOverlays.get(i);
+            SurfaceControl overlaySurfaceControl = l.getSurfaceControl();
+            if (overlaySurfaceControl == null) {
+                // Remove the overlay if the surfacepackage was released. Ownership
+                // is shared, so this may happen.
+                mOverlays.remove(i);
+            } else if (overlaySurfaceControl.isSameSurface(p.getSurfaceControl())) {
+                mOverlays.remove(i);
+                t.reparent(l.getSurfaceControl(), null);
+                l.release();
            }
         }
         t.apply();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ea1f3a..06010bb 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -59,7 +59,6 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wallpaper.WallpaperCropper.WallpaperCropUtils;
-import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -173,12 +172,12 @@
                 && animatingContainer.getAnimation() != null
                 && animatingContainer.getAnimation().getShowWallpaper();
         final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper;
-        if (isRecentsTransitionTarget(w) || isBackNavigationTarget(w)) {
-            if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
+        if (isBackNavigationTarget(w)) {
+            if (DEBUG_WALLPAPER) Slog.v(TAG, "Found back animation wallpaper target: " + w);
             mFindResults.setWallpaperTarget(w);
             return true;
-        } else if (hasWallpaper && w.isOnScreen()
-                && (mWallpaperTarget == w || w.isDrawFinishedLw())) {
+        } else if (hasWallpaper
+                && (w.mActivityRecord != null ? w.isOnScreen() : w.isReadyForDisplay())) {
             if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w);
             mFindResults.setWallpaperTarget(w);
             mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
@@ -200,15 +199,6 @@
         return false;
     };
 
-    private boolean isRecentsTransitionTarget(WindowState w) {
-        if (w.mTransitionController.isShellTransitionsEnabled()) {
-            return false;
-        }
-        // The window is either the recents activity or is in the task animating by the recents.
-        final RecentsAnimationController controller = mService.getRecentsAnimationController();
-        return controller != null && controller.isWallpaperVisible(w);
-    }
-
     private boolean isBackNavigationTarget(WindowState w) {
         // The window is in animating by back navigation and set to show wallpaper.
         return mService.mAtmService.mBackNavigationController.isWallpaperVisible(w);
@@ -759,7 +749,7 @@
 
     void collectTopWallpapers(Transition transition) {
         if (mFindResults.hasTopShowWhenLockedWallpaper()) {
-            if (Flags.ensureWallpaperInTransitions()) {
+            if (mService.mFlags.mEnsureWallpaperInTransitions) {
                 transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper.mToken);
             } else {
                 transition.collect(mFindResults.mTopWallpaper.mTopShowWhenLockedWallpaper);
@@ -767,7 +757,7 @@
 
         }
         if (mFindResults.hasTopHideWhenLockedWallpaper()) {
-            if (Flags.ensureWallpaperInTransitions()) {
+            if (mService.mFlags.mEnsureWallpaperInTransitions) {
                 transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper.mToken);
             } else {
                 transition.collect(mFindResults.mTopWallpaper.mTopHideWhenLockedWallpaper);
@@ -929,12 +919,6 @@
                 Slog.v(TAG, "*** WALLPAPER DRAW TIMEOUT");
             }
 
-            // If there was a pending recents animation, start the animation anyways (it's better
-            // to not see the wallpaper than for the animation to not start)
-            if (mService.getRecentsAnimationController() != null) {
-                mService.getRecentsAnimationController().startAnimation();
-            }
-
             // If there was a pending back navigation animation that would show wallpaper, start
             // the animation due to it was skipped in previous surface placement.
             mService.mAtmService.mBackNavigationController.startAnimation();
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 31156de..89ad564 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -32,7 +32,6 @@
 import android.util.SparseArray;
 
 import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
 
 import java.util.function.Consumer;
 
@@ -85,7 +84,7 @@
     public void prepareSurfaces() {
         super.prepareSurfaces();
 
-        if (Flags.ensureWallpaperInTransitions()) {
+        if (mWmService.mFlags.mEnsureWallpaperInTransitions) {
             // Similar to Task.prepareSurfaces, outside of transitions we need to apply visibility
             // changes directly. In transitions the transition player will take care of applying the
             // visibility change.
@@ -162,15 +161,7 @@
                 mDisplayContent.mWallpaperController.getWallpaperTarget();
 
         if (visible && wallpaperTarget != null) {
-            final RecentsAnimationController recentsAnimationController =
-                    mWmService.getRecentsAnimationController();
-            if (recentsAnimationController != null
-                    && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) {
-                // If the Recents animation is running, and the wallpaper target is the animating
-                // task we want the wallpaper to be rotated in the same orientation as the
-                // RecentsAnimation's target (e.g the launcher)
-                recentsAnimationController.linkFixedRotationTransformIfNeeded(this);
-            } else if ((wallpaperTarget.mActivityRecord == null
+            if ((wallpaperTarget.mActivityRecord == null
                     // Ignore invisible activity because it may be moving to background.
                     || wallpaperTarget.mActivityRecord.isVisibleRequested())
                     && wallpaperTarget.mToken.hasFixedRotationTransform()) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 34b9913..2c58c61 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -97,10 +97,10 @@
 
     /**
      * @return If a window animation has outsets applied to it.
-     * @see Animation#hasExtension()
+     * @see Animation#getExtensionEdges()
      */
     public boolean hasExtension() {
-        return mAnimation.hasExtension();
+        return mAnimation.getExtensionEdges() != 0;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 03342d3..2342de3 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -19,7 +19,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -28,6 +27,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.content.Context;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -69,6 +69,8 @@
 
     private Choreographer mChoreographer;
 
+    private final HandlerExecutor mExecutor;
+
     /**
      * Indicates whether we have an animation frame callback scheduled, which will happen at
      * vsync-app and then schedule the animation tick at the right time (vsync-sf).
@@ -80,8 +82,7 @@
      * A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
      * executed and the corresponding transaction is closed and applied.
      */
-    private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
-    private boolean mInExecuteAfterPrepareSurfacesRunnables;
+    private ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
 
     private final SurfaceControl.Transaction mTransaction;
 
@@ -92,6 +93,7 @@
         mTransaction = service.mTransactionFactory.get();
         service.mAnimationHandler.runWithScissors(
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
+        mExecutor = new HandlerExecutor(service.mAnimationHandler);
 
         mAnimationFrameCallback = frameTimeNs -> {
             synchronized (mService.mGlobalLock) {
@@ -199,6 +201,19 @@
             updateRunningExpensiveAnimationsLegacy();
         }
 
+        final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables;
+        if (!afterPrepareSurfacesRunnables.isEmpty()) {
+            mAfterPrepareSurfacesRunnables = new ArrayList<>();
+            mTransaction.addTransactionCommittedListener(mExecutor, () -> {
+                synchronized (mService.mGlobalLock) {
+                    // Traverse in order they were added.
+                    for (int i = 0, size = afterPrepareSurfacesRunnables.size(); i < size; i++) {
+                        afterPrepareSurfacesRunnables.get(i).run();
+                    }
+                    afterPrepareSurfacesRunnables.clear();
+                }
+            });
+        }
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "applyTransaction");
         mTransaction.apply();
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
@@ -206,7 +221,6 @@
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
 
         mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
-        executeAfterPrepareSurfacesRunnables();
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit"
@@ -218,8 +232,8 @@
     private void updateRunningExpensiveAnimationsLegacy() {
         final boolean runningExpensiveAnimations =
                 mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
-                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
-                                | ANIMATION_TYPE_RECENTS /* typesToCheck */);
+                        ANIMATION_TYPE_APP_TRANSITION
+                                | ANIMATION_TYPE_SCREEN_ROTATION /* typesToCheck */);
         if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
             mService.mSnapshotController.setPause(true);
             mTransaction.setEarlyWakeupStart();
@@ -288,34 +302,10 @@
 
     /**
      * Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
-     * the corresponding transaction is closed and applied.
+     * the corresponding transaction is closed, applied, and committed.
      */
     void addAfterPrepareSurfacesRunnable(Runnable r) {
-        // If runnables are already being handled in executeAfterPrepareSurfacesRunnable, then just
-        // immediately execute the runnable passed in.
-        if (mInExecuteAfterPrepareSurfacesRunnables) {
-            r.run();
-            return;
-        }
-
         mAfterPrepareSurfacesRunnables.add(r);
         scheduleAnimation();
     }
-
-    void executeAfterPrepareSurfacesRunnables() {
-
-        // Don't even think about to start recursing!
-        if (mInExecuteAfterPrepareSurfacesRunnables) {
-            return;
-        }
-        mInExecuteAfterPrepareSurfacesRunnables = true;
-
-        // Traverse in order they were added.
-        final int size = mAfterPrepareSurfacesRunnables.size();
-        for (int i = 0; i < size; i++) {
-            mAfterPrepareSurfacesRunnables.get(i).run();
-        }
-        mAfterPrepareSurfacesRunnables.clear();
-        mInExecuteAfterPrepareSurfacesRunnables = false;
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 64f9c01..1eeb3ec 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -19,6 +19,7 @@
 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -50,7 +51,6 @@
 import static com.android.server.wm.IdentifierProto.USER_ID;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -100,7 +100,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
 import android.view.WindowManager;
 import android.view.WindowManager.TransitionOldType;
 import android.view.animation.Animation;
@@ -706,7 +705,7 @@
         mLastSurfacePosition.set(0, 0);
         mLastDeltaRotation = Surface.ROTATION_0;
 
-        final Builder b = mWmService.makeSurfaceBuilder(null)
+        final Builder b = mWmService.makeSurfaceBuilder()
                 .setContainerLayer()
                 .setName(getName());
 
@@ -1241,8 +1240,7 @@
      */
     boolean inTransitionSelfOrParent() {
         if (!mTransitionController.isShellTransitionsEnabled()) {
-            return isAnimating(PARENTS | TRANSITION,
-                    ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
+            return isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_APP_TRANSITION);
         }
         return inTransition();
     }
@@ -1611,8 +1609,8 @@
      *
      * @param orientation the specified orientation.
      */
-    void setOrientation(@ScreenOrientation int orientation) {
-        setOrientation(orientation, null /* requestingContainer */);
+    protected void setOrientation(@ScreenOrientation int orientation) {
+        setOrientation(orientation, null /* requesting */);
     }
 
     /**
@@ -1732,13 +1730,13 @@
      *         last time {@link #getOrientation(int) was called.
      */
     @Nullable
-    WindowContainer getLastOrientationSource() {
-        final WindowContainer source = mLastOrientationSource;
-        if (source != null && source != this) {
-            final WindowContainer nextSource = source.getLastOrientationSource();
-            if (nextSource != null) {
-                return nextSource;
-            }
+    final WindowContainer<?> getLastOrientationSource() {
+        if (mLastOrientationSource == null) {
+            return null;
+        }
+        WindowContainer<?> source = this;
+        while (source != source.mLastOrientationSource && source.mLastOrientationSource != null) {
+            source = source.mLastOrientationSource;
         }
         return source;
     }
@@ -2663,13 +2661,6 @@
         return true;
     }
 
-    SurfaceSession getSession() {
-        if (getParent() != null) {
-            return getParent().getSession();
-        }
-        return null;
-    }
-
     void assignLayer(Transaction t, int layer) {
         // Don't assign layers while a transition animation is playing
         // TODO(b/173528115): establish robust best-practices around z-order fighting.
@@ -2784,9 +2775,9 @@
     @CallSuper
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
+            @WindowTracingLogLevel int logLevel) {
         boolean isVisible = isVisible();
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible) {
             return;
         }
 
@@ -2997,12 +2988,18 @@
         // Make sure display isn't a part of the transition already - needed for legacy transitions.
         if (mDisplayContent.inTransition()) return false;
 
-        if (!ActivityTaskManagerService.isPip2ExperimentEnabled()) {
-            // Screenshots are turned off when PiP is undergoing changes.
-            return !inPinnedWindowingMode() && getParent() != null
-                    && !getParent().inPinnedWindowingMode();
-        }
-        return true;
+        // Screenshots are turned off when PiP is undergoing changes.
+        return ActivityTaskManagerService.isPip2ExperimentEnabled() || !isPipChange();
+    }
+
+    /** Returns true if WC is pinned and undergoing changes. */
+    private boolean isPipChange() {
+        final boolean isExitingPip = this.asTaskFragment() != null
+                && mTransitionController.getWindowingModeAtStart(this) == WINDOWING_MODE_PINNED
+                && !inPinnedWindowingMode();
+
+        return isExitingPip || inPinnedWindowingMode()
+                || (getParent() != null && getParent().inPinnedWindowingMode());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
index 57fc4c7..80f3c44 100644
--- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
+++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java
@@ -20,7 +20,7 @@
 import static android.view.SurfaceControl.METADATA_WINDOW_TYPE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainerThumbnailProto.HEIGHT;
 import static com.android.server.wm.WindowContainerThumbnailProto.SURFACE_ANIMATOR;
 import static com.android.server.wm.WindowContainerThumbnailProto.WIDTH;
@@ -118,14 +118,7 @@
                         mWindowContainer.getDisplayContent().mAppTransition.canSkipFirstFrame(),
                         mWindowContainer.getDisplayContent().getWindowCornerRadius()),
                 mWindowContainer.mWmService.mSurfaceAnimationRunner), false /* hidden */,
-                ANIMATION_TYPE_RECENTS);
-    }
-
-    /**
-     * Start animation with existing adapter.
-     */
-    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) {
-        mSurfaceAnimator.startAnimation(t, anim, hidden, ANIMATION_TYPE_RECENTS);
+                ANIMATION_TYPE_APP_TRANSITION);
     }
 
     private void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 1931be4..47c42f4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -34,6 +34,10 @@
  */
 final class WindowManagerConstants {
 
+    /** The orientation of activity will be always "unspecified". */
+    private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
+            "ignore_activity_orientation_request";
+
     /**
      * The minimum duration between gesture exclusion logging for a given window in
      * milliseconds.
@@ -58,6 +62,9 @@
     /** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
     boolean mSystemGestureExcludedByPreQStickyImmersive;
 
+    /** @see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST */
+    boolean mIgnoreActivityOrientationRequest;
+
     private final WindowManagerGlobalLock mGlobalLock;
     private final Runnable mUpdateSystemGestureExclusionCallback;
     private final DeviceConfigInterface mDeviceConfig;
@@ -89,6 +96,7 @@
         updateSystemGestureExclusionLogDebounceMillis();
         updateSystemGestureExclusionLimitDp();
         updateSystemGestureExcludedByPreQStickyImmersive();
+        updateIgnoreActivityOrientationRequest();
     }
 
     private void onAndroidPropertiesChanged(DeviceConfig.Properties properties) {
@@ -127,6 +135,9 @@
                     case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
                         updateSystemGestureExclusionLogDebounceMillis();
                         break;
+                    case KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST:
+                        updateIgnoreActivityOrientationRequest();
+                        break;
                     default:
                         break;
                 }
@@ -152,6 +163,12 @@
                 KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
     }
 
+    private void updateIgnoreActivityOrientationRequest() {
+        mIgnoreActivityOrientationRequest = mDeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
+    }
+
     void dump(PrintWriter pw) {
         pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
 
@@ -161,6 +178,8 @@
         pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
         pw.print("  "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
         pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+        pw.print("  "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
+        pw.print("="); pw.println(mIgnoreActivityOrientationRequest);
         pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java
index f3e6a18..7ef8d8d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerFlags.java
+++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import android.app.AppGlobals;
+import android.content.pm.PackageManager;
+
 import com.android.window.flags.Flags;
 
 /**
@@ -53,5 +56,26 @@
     final boolean mRespectNonTopVisibleFixedOrientation =
             Flags.respectNonTopVisibleFixedOrientation();
 
+    final boolean mEnsureWallpaperInTransitions;
+
     /* End Available Flags */
+
+    WindowManagerFlags() {
+        boolean isWatch;
+        try {
+            isWatch = AppGlobals.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_WATCH, 0 /* version */);
+        } catch (Throwable e) {
+            isWatch = false;
+        }
+        /*
+         * Wallpaper enablement is separated on Wear vs Phone as the latter appears to still exhibit
+         * regressions when enabled (for example b/353870983). These don't exist on Wear likely
+         * due to differences in SysUI/transition implementations. Wear enablement is required for
+         * 25Q2 while phone doesn't have as pressing a constraint and will wait to resolve any
+         * outstanding issues prior to roll-out.
+         */
+        mEnsureWallpaperInTransitions = (isWatch && Flags.ensureWallpaperInWearTransitions())
+                || Flags.ensureWallpaperInTransitions();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5749272..fb57a1b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -32,6 +32,7 @@
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_PC;
@@ -128,7 +129,6 @@
 import static com.android.server.wm.SensitiveContentPackages.PackageInfo;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -245,7 +245,6 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
@@ -265,7 +264,6 @@
 import android.view.IInputFilter;
 import android.view.IOnKeyguardExitResult;
 import android.view.IPinnedTaskListener;
-import android.view.IRecentsAnimationRunner;
 import android.view.IRotationWatcher;
 import android.view.IScrollCaptureResponseListener;
 import android.view.ISystemGestureExclusionListener;
@@ -290,7 +288,6 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
 import android.view.View;
 import android.view.View.FocusDirection;
 import android.view.ViewDebug;
@@ -388,7 +385,6 @@
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
 import java.util.function.Supplier;
 
 /** {@hide} */
@@ -680,7 +676,6 @@
     private final SparseIntArray mOrientationMapping = new SparseIntArray();
 
     final AccessibilityController mAccessibilityController;
-    private RecentsAnimationController mRecentsAnimationController;
 
     Watermark mWatermark;
     StrictModeFlash mStrictModeFlash;
@@ -1114,7 +1109,7 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
-    Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory;
+    Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
     Supplier<SurfaceControl.Transaction> mTransactionFactory;
 
     private final SurfaceControl.Transaction mTransaction;
@@ -1158,17 +1153,12 @@
                 return;
             }
 
-            // While running a recents animation, this will get called early because we show the
-            // recents animation target activity immediately when the animation starts. Defer the
-            // mLaunchTaskBehind updates until recents animation finishes.
-            if (atoken.mLaunchTaskBehind && !isRecentsAnimationTarget(atoken)) {
+            if (atoken.mLaunchTaskBehind) {
                 mAtmService.mTaskSupervisor.scheduleLaunchTaskBehindComplete(atoken.token);
                 atoken.mLaunchTaskBehind = false;
             } else {
                 atoken.updateReportedVisibilityLocked();
-                // We should also defer sending the finished callback until the recents animation
-                // successfully finishes.
-                if (atoken.mEnteringAnimation && !isRecentsAnimationTarget(atoken)) {
+                if (atoken.mEnteringAnimation) {
                     atoken.mEnteringAnimation = false;
                     if (atoken.attachedToProcess()) {
                         try {
@@ -1210,7 +1200,7 @@
             final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
             DisplayWindowSettingsProvider displayWindowSettingsProvider,
             Supplier<SurfaceControl.Transaction> transactionFactory,
-            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
+            Supplier<SurfaceControl.Builder> surfaceControlFactory) {
         final WindowManagerService[] wms = new WindowManagerService[1];
         DisplayThread.getHandler().runWithScissors(() ->
                 wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,
@@ -1239,7 +1229,7 @@
             boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
             DisplayWindowSettingsProvider displayWindowSettingsProvider,
             Supplier<SurfaceControl.Transaction> transactionFactory,
-            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
+            Supplier<SurfaceControl.Builder> surfaceControlFactory) {
         installLock(this, INDEX_WINDOW);
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
@@ -1534,7 +1524,7 @@
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
                 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
         int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
-                appOp);
+                appOp, displayId);
         if (res != ADD_OKAY) {
             return res;
         }
@@ -2732,8 +2722,7 @@
                         win.mTransitionController.mAnimatingExitWindows.add(win);
                         reason = "inTransition";
                     }
-                } else if (win.isAnimating(PARENTS | TRANSITION,
-                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)) {
+                } else if (win.isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) {
                     // Already animating as part of a legacy app-transition.
                     reason = "inLegacyTransition";
                 }
@@ -3167,7 +3156,7 @@
     }
 
     // TODO(multi-display): remove when no default display use case.
-    // (i.e. KeyguardController / RecentsAnimation)
+    // (i.e. KeyguardController)
     public void executeAppTransition() {
         if (!checkCallingPermission(MANAGE_APP_TOKENS, "executeAppTransition()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3175,57 +3164,6 @@
         getDefaultDisplayContentLocked().executeAppTransition();
     }
 
-    void initializeRecentsAnimation(int targetActivityType,
-            IRecentsAnimationRunner recentsAnimationRunner,
-            RecentsAnimationController.RecentsAnimationCallbacks callbacks, int displayId,
-            SparseBooleanArray recentTaskIds, ActivityRecord targetActivity) {
-        mRecentsAnimationController = new RecentsAnimationController(this, recentsAnimationRunner,
-                callbacks, displayId);
-        mRoot.getDisplayContent(displayId).mAppTransition.updateBooster();
-        mRecentsAnimationController.initialize(targetActivityType, recentTaskIds, targetActivity);
-    }
-
-    @VisibleForTesting
-    void setRecentsAnimationController(RecentsAnimationController controller) {
-        mRecentsAnimationController = controller;
-    }
-
-    RecentsAnimationController getRecentsAnimationController() {
-        return mRecentsAnimationController;
-    }
-
-    void cancelRecentsAnimation(
-            @RecentsAnimationController.ReorderMode int reorderMode, String reason) {
-        if (mRecentsAnimationController != null) {
-            // This call will call through to cleanupAnimation() below after the animation is
-            // canceled
-            mRecentsAnimationController.cancelAnimation(reorderMode, reason);
-        }
-    }
-
-
-    void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        if (mRecentsAnimationController != null) {
-            final RecentsAnimationController controller = mRecentsAnimationController;
-            mRecentsAnimationController = null;
-            controller.cleanupAnimation(reorderMode);
-            // TODO(multi-display): currently only default display support recents animation.
-            final DisplayContent dc = getDefaultDisplayContentLocked();
-            if (dc.mAppTransition.isTransitionSet()) {
-                dc.mSkipAppTransitionAnimation = true;
-            }
-            dc.forAllWindowContainers((wc) -> {
-                if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) {
-                    wc.cancelAnimation();
-                }
-            });
-        }
-    }
-
-    boolean isRecentsAnimationTarget(ActivityRecord r) {
-        return mRecentsAnimationController != null && mRecentsAnimationController.isTargetApp(r);
-    }
-
     boolean isValidPictureInPictureAspectRatio(DisplayContent displayContent, float aspectRatio) {
         return displayContent.getPinnedTaskController().isValidPictureInPictureAspectRatio(
                 aspectRatio);
@@ -3257,11 +3195,6 @@
     }
 
     @Override
-    public void triggerAnimationFailsafe() {
-        mH.sendEmptyMessage(H.ANIMATION_FAILSAFE);
-    }
-
-    @Override
     public void onKeyguardShowingAndNotOccludedChanged() {
         mH.sendEmptyMessage(H.RECOMPUTE_FOCUS);
         dispatchKeyguardLockedState();
@@ -5651,7 +5584,6 @@
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_HIDE_TIMEOUT = 52;
         public static final int SET_HAS_OVERLAY_UI = 58;
-        public static final int ANIMATION_FAILSAFE = 60;
         public static final int RECOMPUTE_FOCUS = 61;
         public static final int ON_POINTER_DOWN_OUTSIDE_FOCUS = 62;
         public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
@@ -5886,14 +5818,6 @@
                     mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
                     break;
                 }
-                case ANIMATION_FAILSAFE: {
-                    synchronized (mGlobalLock) {
-                        if (mRecentsAnimationController != null) {
-                            mRecentsAnimationController.scheduleFailsafe();
-                        }
-                    }
-                    break;
-                }
                 case RECOMPUTE_FOCUS: {
                     synchronized (mGlobalLock) {
                         updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
@@ -6099,7 +6023,7 @@
                     displayContent.setForcedSize(displayContent.mInitialDisplayWidth,
                             displayContent.mInitialDisplayHeight,
                             displayContent.mInitialPhysicalXDpi,
-                            displayContent.mInitialPhysicalXDpi);
+                            displayContent.mInitialPhysicalYDpi);
                 }
             }
         } finally {
@@ -6858,7 +6782,7 @@
      * @param proto     Stream to write the WindowContainer object to.
      * @param logLevel  Determines the amount of data to be written to the Protobuf.
      */
-    void dumpDebugLocked(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
+    void dumpDebugLocked(ProtoOutputStream proto, @WindowTracingLogLevel int logLevel) {
         mPolicy.dumpDebug(proto, POLICY);
         mRoot.dumpDebug(proto, ROOT_WINDOW_CONTAINER, logLevel);
         final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
@@ -7035,10 +6959,6 @@
                     pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
                     pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
                     pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
-            if (mRecentsAnimationController != null) {
-                pw.print("  mRecentsAnimationController="); pw.println(mRecentsAnimationController);
-                mRecentsAnimationController.dump(pw, "    ");
-            }
         }
     }
 
@@ -7217,7 +7137,7 @@
         if (useProto) {
             final ProtoOutputStream proto = new ProtoOutputStream(fd);
             synchronized (mGlobalLock) {
-                dumpDebugLocked(proto, WindowTraceLogLevel.ALL);
+                dumpDebugLocked(proto, WindowTracingLogLevel.ALL);
             }
             proto.flush();
             return;
@@ -7656,7 +7576,7 @@
                         displayId);
                 return REMOVE_CONTENT_MODE_UNDEFINED;
             }
-            return mDisplayWindowSettings.getRemoveContentModeLocked(displayContent);
+            return displayContent.getRemoveContentMode();
         }
     }
 
@@ -7998,7 +7918,8 @@
             }
             boolean allWindowsDrawn = false;
             synchronized (mGlobalLock) {
-                if (mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
+                if (displayId == INVALID_DISPLAY
+                        && mRoot.getDefaultDisplay().mDisplayUpdater.waitForTransition(message)) {
                     // Use the ready-to-play of transition as the signal.
                     return;
                 }
@@ -8598,7 +8519,7 @@
                     return null;
                 }
                 // TODO(b/210039666): Use a method like add/removeDisplayOverlay if available.
-                return makeSurfaceBuilder(dc.getSession())
+                return makeSurfaceBuilder()
                         .setContainerLayer()
                         .setName("IME Handwriting Surface")
                         .setCallsite("getHandwritingSurfaceForDisplay")
@@ -8906,8 +8827,8 @@
         }
     }
 
-    SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
-        return mSurfaceControlFactory.apply(s);
+    SurfaceControl.Builder makeSurfaceBuilder() {
+        return mSurfaceControlFactory.get();
     }
 
     /**
@@ -9022,16 +8943,25 @@
             // display it's on to the top since that window won't be able to get focus anyway.
             return;
         }
+
+        final ActivityRecord touchedApp = t.getActivityRecord();
+        if (touchedApp != null && touchedApp.getTask() != null) {
+            final ActivityRecord top = touchedApp.getTask().topRunningActivity();
+            if (top != null && top != touchedApp && top.getTaskFragment().getBounds().contains(
+                    touchedApp.getTaskFragment().getBounds())) {
+                // This is a special case where the pointer-down-outside focus on an Activity that's
+                // entirely occluded by the task top running activity, this is possible if the
+                // pointer-down-outside-focus event is delayed (after new activity started on top).
+                // In that case, drop the event to prevent changing focus to a background activity.
+                Slog.w(TAG, "onPointerDownOutsideFocusLocked, drop event because " + touchedApp
+                        + " is occluded and should not be focused.");
+                return;
+            }
+        }
+
         clearPointerDownOutsideFocusRunnable();
 
-        // For embedded activity that is showing side-by-side with another activity, delay
-        // handling the touch-outside event to prevent focus rapid changes back-n-forth.
-        // Otherwise, handle the touch-outside event directly.
-        final WindowState w = t.getWindowState();
-        final ActivityRecord activity = w != null ? w.getActivityRecord() : null;
-        if (mFocusedInputTarget != t && mFocusedInputTarget != null
-                && activity != null && activity.isEmbedded()
-                && activity.getTaskFragment().getAdjacentTaskFragment() != null) {
+        if (shouldDelayTouchOutside(t)) {
             mPointerDownOutsideFocusRunnable = () -> handlePointerDownOutsideFocus(t);
             mH.postDelayed(mPointerDownOutsideFocusRunnable, POINTER_DOWN_OUTSIDE_FOCUS_TIMEOUT_MS);
         } else if (!fromHandler) {
@@ -9044,6 +8974,32 @@
         }
     }
 
+    private boolean shouldDelayTouchOutside(InputTarget t) {
+        final ActivityRecord activity = t.getActivityRecord();
+        final Task task = activity != null ? activity.getTask() : null;
+
+        final boolean isInputTargetNotFocused =
+                mFocusedInputTarget != t && mFocusedInputTarget != null;
+        if (!isInputTargetNotFocused) {
+            return false;
+        }
+
+        // For embedded activity that is showing side-by-side with another activity, delay
+        // handling the touch-outside event to prevent focus rapid changes back-n-forth.
+        final boolean shouldDelayTouchForEmbeddedActivity = activity != null
+                && activity.isEmbedded()
+                && activity.getTaskFragment().getAdjacentTaskFragment() != null;
+
+        // For cases when there are multiple freeform windows where non-top windows are blocking
+        // the gesture zones, delay handling the touch-outside event to prevent refocusing the
+        // the non-top windows during the gesture.
+        final boolean shouldDelayTouchForFreeform =
+                task != null && task.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+
+        // If non of the above cases are true, handle the touch-outside event directly.
+        return shouldDelayTouchForEmbeddedActivity || shouldDelayTouchForFreeform;
+    }
+
     private void handlePointerDownOutsideFocus(InputTarget t) {
         synchronized (mGlobalLock) {
             if (mPointerDownOutsideFocusRunnable != null
@@ -9053,17 +9009,6 @@
             }
             clearPointerDownOutsideFocusRunnable();
 
-            if (mRecentsAnimationController != null
-                    && mRecentsAnimationController.getTargetAppMainWindow() == t) {
-                // If there is an active recents animation and touched window is the target,
-                // then ignore the touch. The target already handles touches using its own
-                // input monitor and we don't want to trigger any lifecycle changes from
-                // focusing another window.
-                // TODO(b/186770026): We should remove this once we support multiple resumed
-                //  activities while in overview
-                return;
-            }
-
             final WindowState w = t.getWindowState();
             if (w != null) {
                 final Task task = w.getTask();
@@ -10176,6 +10121,23 @@
         }
     }
 
+    /**
+     * Returns whether the given UID is the owner of a virtual device, which the given display
+     * belongs to.
+     */
+    @Override
+    public boolean isCallerVirtualDeviceOwner(int displayId, int callingUid) {
+        if (!android.companion.virtualdevice.flags.Flags.statusBarAndInsets()) {
+            return false;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            return mAtmService.mTaskSupervisor.isDeviceOwnerUid(displayId, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @RequiresPermission(ACCESS_SURFACE_FLINGER)
     @Override
     public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 092a751..06d8c37 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -283,24 +283,28 @@
     }
 
     private int runDisplayDensity(PrintWriter pw) throws RemoteException {
-        String densityStr = getNextArg();
+        String densityStr = null;
         String arg = getNextArg();
         int density;
         int displayId = Display.DEFAULT_DISPLAY;
-        if ("-d".equals(densityStr) && arg != null) {
+        if (!"-d".equals(arg) && !"-u".equals(arg)) {
+            densityStr = arg;
+            arg = getNextArg();
+        }
+        if ("-d".equals(arg)) {
+            arg = getNextArg();
             try {
                 displayId = Integer.parseInt(arg);
             } catch (NumberFormatException e) {
                 getErrPrintWriter().println("Error: bad number " + e);
             }
-            densityStr = getNextArg();
-        } else if ("-u".equals(densityStr) && arg != null) {
+        } else if ("-u".equals(arg)) {
+            arg = getNextArg();
             displayId = mInterface.getDisplayIdByUniqueId(arg);
             if (displayId == Display.INVALID_DISPLAY) {
                 getErrPrintWriter().println("Error: the uniqueId is invalid ");
                 return -1;
             }
-            densityStr = getNextArg();
         }
 
         if (densityStr == null) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 58c48ad..476443a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -18,9 +18,12 @@
 
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.isStartResultSuccessful;
+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.WINDOW_CONFIG_BOUNDS;
+import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
@@ -220,7 +223,8 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
+                final ActionChain chain = mService.mChainTracker.startLegacy("applyTransactLegacy");
+                applyTransaction(t, -1 /*syncId*/, chain, caller);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -239,7 +243,8 @@
         try {
             synchronized (mGlobalLock) {
                 if (callback == null) {
-                    applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller);
+                    final ActionChain chain = mService.mChainTracker.startLegacy("applySyncLegacy");
+                    applyTransaction(t, -1 /* syncId*/, chain, caller);
                     return -1;
                 }
 
@@ -259,13 +264,15 @@
                 final int syncId = syncGroup.mSyncId;
                 if (mTransitionController.isShellTransitionsEnabled()) {
                     mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> {
-                        applyTransaction(t, syncId, null /* transition */, caller, deferred);
+                        applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
+                                "applySyncLegacy"), caller, deferred);
                         setSyncReady(syncId);
                     });
                 } else {
                     if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
                         mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup);
-                        applyTransaction(t, syncId, null /*transition*/, caller);
+                        applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
+                                "applySyncLegacy"), caller);
                         setSyncReady(syncId);
                     } else {
                         // Because the BLAST engine only supports one sync at a time, queue the
@@ -273,7 +280,8 @@
                         mService.mWindowManager.mSyncEngine.queueSyncSet(
                                 () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
                                 () -> {
-                                    applyTransaction(t, syncId, null /*transition*/, caller);
+                                    applyTransaction(t, syncId, mService.mChainTracker.startLegacy(
+                                            "applySyncLegacy"), caller);
                                     setSyncReady(syncId);
                                 });
                     }
@@ -310,7 +318,8 @@
                         throw new IllegalArgumentException("Can't use legacy transitions in"
                                 + " compatibility mode with no WCT.");
                     }
-                    applyTransaction(t, -1 /* syncId */, null, caller);
+                    applyTransaction(t, -1 /* syncId */,
+                            mService.mChainTracker.startLegacy("wrongLegacyTransit"), caller);
                     return null;
                 }
                 final WindowContainerTransaction wct =
@@ -322,19 +331,20 @@
                     // This is a direct call from shell, so the entire transition lifecycle is
                     // contained in the provided transaction if provided. Thus, we can setReady
                     // immediately after apply.
-                    final Transition.ReadyCondition wctApplied =
-                            new Transition.ReadyCondition("start WCT applied");
                     final boolean needsSetReady = t != null;
                     final Transition nextTransition = new Transition(type, 0 /* flags */,
                             mTransitionController, mService.mWindowManager.mSyncEngine);
+                    final Transition.ReadyCondition wctApplied =
+                            new Transition.ReadyCondition("start WCT applied");
                     nextTransition.mReadyTracker.add(wctApplied);
                     nextTransition.calcParallelCollectType(wct);
                     mTransitionController.startCollectOrQueue(nextTransition,
                             (deferred) -> {
+                                final ActionChain chain = mService.mChainTracker.start(
+                                        "startNewTransit", nextTransition);
                                 nextTransition.start();
                                 nextTransition.mLogger.mStartWCT = wct;
-                                applyTransaction(wct, -1 /* syncId */, nextTransition, caller,
-                                        deferred);
+                                applyTransaction(wct, -1 /* syncId */, chain, caller, deferred);
                                 wctApplied.meet();
                                 if (needsSetReady) {
                                     setAllReadyIfNeeded(nextTransition, wct);
@@ -342,6 +352,17 @@
                             });
                     return nextTransition.getToken();
                 }
+                // The transition already started collecting before sending a request to shell,
+                // so just start here.
+                if (!transition.isCollecting() && !transition.isForcePlaying()) {
+                    Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+                            + " means Shell took too long to respond to a request. WM State may be"
+                            + " incorrect now, please file a bug");
+                    final ActionChain chain = mService.mChainTracker.startFailsafe("startTransit");
+                    chain.mTransition = null;
+                    applyTransaction(wct, -1 /*syncId*/, chain, caller);
+                    return transition.getToken();
+                }
                 // Currently, application of wct can span multiple looper loops (ie.
                 // waitAsyncStart), so add a condition to ensure that it finishes applying.
                 final Transition.ReadyCondition wctApplied;
@@ -351,32 +372,24 @@
                 } else {
                     wctApplied = null;
                 }
-                // The transition already started collecting before sending a request to shell,
-                // so just start here.
-                if (!transition.isCollecting() && !transition.isForcePlaying()) {
-                    Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
-                            + " means Shell took too long to respond to a request. WM State may be"
-                            + " incorrect now, please file a bug");
-                    applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
-                    if (wctApplied != null) {
-                        wctApplied.meet();
-                    }
-                    return transition.getToken();
-                }
                 transition.mLogger.mStartWCT = wct;
                 if (transition.shouldApplyOnDisplayThread()) {
                     mService.mH.post(() -> {
                         synchronized (mService.mGlobalLock) {
+                            final ActionChain chain = mService.mChainTracker.start(
+                                    "startTransit", transition);
                             transition.start();
-                            applyTransaction(wct, -1 /* syncId */, transition, caller);
+                            applyTransaction(wct, -1 /* syncId */, chain, caller);
                             if (wctApplied != null) {
                                 wctApplied.meet();
                             }
                         }
                     });
                 } else {
+                    final ActionChain chain = mService.mChainTracker.start("startTransit",
+                            transition);
                     transition.start();
-                    applyTransaction(wct, -1 /* syncId */, transition, caller);
+                    applyTransaction(wct, -1 /* syncId */, chain, caller);
                     if (wctApplied != null) {
                         wctApplied.meet();
                     }
@@ -475,7 +488,8 @@
                 dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */,
                         false /* isActivityEmbedding */);
                 syncId = startSyncWithOrganizer(callback);
-                applyTransaction(t, syncId, null /* transition */, caller);
+                applyTransaction(t, syncId, mService.mChainTracker.startLegacy("legacyTransit"),
+                        caller);
                 setSyncReady(syncId);
             }
         } finally {
@@ -493,6 +507,8 @@
         try {
             synchronized (mGlobalLock) {
                 final Transition transition = Transition.fromBinder(transitionToken);
+                final ActionChain chain =
+                        mService.mChainTracker.startFinish("finishTransit", transition);
                 // apply the incoming transaction before finish in case it alters the visibility
                 // of the participants.
                 if (t != null) {
@@ -500,9 +516,9 @@
                     // changes of the transition participants will only set visible-requested
                     // and still let finishTransition handle the participants.
                     mTransitionController.mFinishingTransition = transition;
-                    applyTransaction(t, -1 /* syncId */, null /*transition*/, caller, transition);
+                    applyTransaction(t, -1 /* syncId */, chain, caller);
                 }
-                mTransitionController.finishTransition(transition);
+                mTransitionController.finishTransition(chain);
                 mTransitionController.mFinishingTransition = null;
             }
         } finally {
@@ -537,9 +553,10 @@
         final CallerInfo caller = new CallerInfo();
         final long ident = Binder.clearCallingIdentity();
         try {
-            if (mTransitionController.getTransitionPlayer() == null) {
+            if (!mTransitionController.isShellTransitionsEnabled()) {
                 // No need to worry about transition when Shell transition is not enabled.
-                applyTransaction(wct, -1 /* syncId */, null /* transition */, caller);
+                applyTransaction(wct, -1 /* syncId */,
+                        mService.mChainTracker.startLegacy("legacyTFTransact"), caller);
                 return;
             }
 
@@ -548,8 +565,8 @@
                 // Although there is an active sync, we want to apply the transaction now.
                 // TODO(b/232042367) Redesign the organizer update on activity callback so that we
                 // we will know about the transition explicitly.
-                final Transition transition = mTransitionController.getCollectingTransition();
-                if (transition == null) {
+                final ActionChain chain = mService.mChainTracker.startDefault("tfTransact");
+                if (chain.mTransition == null) {
                     // This should rarely happen, and we should try to avoid using
                     // {@link #applySyncTransaction} with Shell transition.
                     // We still want to apply and merge the transaction to the active sync
@@ -559,7 +576,7 @@
                                     + " because there is an ongoing sync for"
                                     + " applySyncTransaction().");
                 }
-                applyTransaction(wct, -1 /* syncId */, transition, caller);
+                applyTransaction(wct, -1 /* syncId */, chain, caller);
                 return;
             }
 
@@ -570,8 +587,9 @@
                     transition.abort();
                     return;
                 }
-                if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred)
-                        == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
+                final ActionChain chain = mService.mChainTracker.start("tfTransact", transition);
+                final int effects = applyTransaction(wct, -1 /* syncId */, chain, caller, deferred);
+                if (effects == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
                     transition.abort();
                     return;
                 }
@@ -586,15 +604,10 @@
     }
 
     private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @Nullable Transition transition, @NonNull CallerInfo caller) {
-        return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
-    }
-
-    private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) {
+            @NonNull ActionChain chain, @NonNull CallerInfo caller, boolean deferred) {
         if (deferred) {
             try {
-                return applyTransaction(t, syncId, transition, caller);
+                return applyTransaction(t, syncId, chain, caller);
             } catch (RuntimeException e) {
                 // If the transaction is deferred, the caller could be from TransitionController
                 // #tryStartCollectFromQueue that executes on system's worker thread rather than
@@ -604,19 +617,17 @@
             }
             return TRANSACT_EFFECTS_NONE;
         }
-        return applyTransaction(t, syncId, transition, caller);
+        return applyTransaction(t, syncId, chain, caller);
     }
 
     /**
      * @param syncId If non-null, this will be a sync-transaction.
-     * @param transition A transition to collect changes into.
+     * @param chain A lifecycle-chain to acculumate changes into.
      * @param caller Info about the calling process.
-     * @param finishTransition The transition that is currently being finished.
      * @return The effects of the window container transaction.
      */
     private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
-            @Nullable Transition transition, @NonNull CallerInfo caller,
-            @Nullable Transition finishTransition) {
+            @NonNull ActionChain chain, @NonNull CallerInfo caller) {
         int effects = TRANSACT_EFFECTS_NONE;
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
         mService.deferWindowLayout();
@@ -624,20 +635,21 @@
         boolean deferResume = true;
         mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
         boolean deferTransitionReady = false;
-        if (transition != null && !t.isEmpty()) {
-            if (transition.isCollecting()) {
+        if (chain.mTransition != null && !t.isEmpty() && !chain.isFinishing()) {
+            if (chain.mTransition.isCollecting()) {
                 deferTransitionReady = true;
-                transition.deferTransitionReady();
+                chain.mTransition.deferTransitionReady();
             } else {
                 Slog.w(TAG, "Transition is not collecting when applyTransaction."
-                        + " transition=" + transition + " state=" + transition.getState());
-                transition = null;
+                        + " transition=" + chain.mTransition + " state="
+                        + chain.mTransition.getState());
+                chain.mTransition = null;
             }
         }
         try {
             final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
-            if (transition != null) {
-                transition.applyDisplayChangeIfNeeded(haveConfigChanges);
+            if (chain.mTransition != null) {
+                chain.mTransition.applyDisplayChangeIfNeeded(haveConfigChanges);
                 if (!haveConfigChanges.isEmpty()) {
                     effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
                 }
@@ -645,7 +657,7 @@
             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
             final int hopSize = hops.size();
             Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries;
-            if (transition != null) {
+            if (chain.mTransition != null) {
                 // Mark any config-at-end containers before applying config changes so that
                 // the config changes don't dispatch to client.
                 entries = t.getChanges().entrySet().iterator();
@@ -655,7 +667,7 @@
                     if (!entry.getValue().getConfigAtTransitionEnd()) continue;
                     final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
                     if (wc == null || !wc.isAttached()) continue;
-                    transition.setConfigAtEnd(wc);
+                    chain.mTransition.setConfigAtEnd(wc);
                 }
             }
             entries = t.getChanges().entrySet().iterator();
@@ -672,15 +684,13 @@
                 if (syncId >= 0) {
                     addToSyncSet(syncId, wc);
                 }
-                if (transition != null) transition.collect(wc);
+                chain.collect(wc);
 
                 if ((entry.getValue().getChangeMask()
                         & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
                     // Disable entering pip (eg. when recents pretends to finish itself)
-                    if (finishTransition != null) {
-                        finishTransition.setCanPipOnFinish(false /* canPipOnFinish */);
-                    } else if (transition != null) {
-                        transition.setCanPipOnFinish(false /* canPipOnFinish */);
+                    if (chain.mTransition != null) {
+                        chain.mTransition.setCanPipOnFinish(false /* canPipOnFinish */);
                     }
                 }
                 // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the
@@ -728,9 +738,9 @@
             if (hopSize > 0) {
                 final boolean isInLockTaskMode = mService.isInLockTaskMode();
                 for (int i = 0; i < hopSize; ++i) {
-                    effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
+                    effects |= applyHierarchyOp(hops.get(i), effects, syncId, chain,
                             isInLockTaskMode, caller, t.getErrorCallbackToken(),
-                            t.getTaskFragmentOrganizer(), finishTransition);
+                            t.getTaskFragmentOrganizer());
                 }
             }
             // Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -789,7 +799,7 @@
             }
         } finally {
             if (deferTransitionReady) {
-                transition.continueTransitionReady();
+                chain.mTransition.continueTransitionReady();
             }
             mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
             if (deferResume) {
@@ -821,6 +831,31 @@
                 final Configuration c =
                         new Configuration(container.getRequestedOverrideConfiguration());
                 c.setTo(change.getConfiguration(), configMask, windowMask);
+                if (container.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+                        && (change.getConfigSetMask() & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) {
+                    // Special handling for split screen window got offset. The insets calculation
+                    // for configuration should be stable regardless of the offset. Set offset to
+                    // the task level to be applied when calculate compat override for apps
+                    // targeting SDK level 34 or before.
+                    final Task task = container.asTask();
+                    if (task != null) {
+                        if (c.screenWidthDp != SCREEN_WIDTH_DP_UNDEFINED
+                                && c.screenHeightDp != SCREEN_HEIGHT_DP_UNDEFINED) {
+                            final Rect oldBounds = container.getRequestedOverrideBounds();
+                            final Rect newBounds =
+                                    change.getConfiguration().windowConfiguration.getBounds();
+                            if (oldBounds.width() == newBounds.width()
+                                    && oldBounds.height() == newBounds.height()) {
+                                task.mOffsetXForInsets = oldBounds.left - newBounds.left;
+                                task.mOffsetYForInsets = oldBounds.top - newBounds.top;
+                            } else {
+                                task.mOffsetXForInsets = task.mOffsetYForInsets = 0;
+                            }
+                        } else {
+                            task.mOffsetXForInsets = task.mOffsetYForInsets = 0;
+                        }
+                    }
+                }
                 container.onRequestedOverrideConfigurationChanged(c);
             }
             effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
@@ -851,7 +886,11 @@
             }
 
             final int prevMode = container.getRequestedOverrideWindowingMode();
-            container.setWindowingMode(windowingMode);
+            if (container.asTask() != null && container.asTask().isRootTask()) {
+                container.asTask().setRootTaskWindowingMode(windowingMode);
+            } else {
+                container.setWindowingMode(windowingMode);
+            }
             if (prevMode != container.getWindowingMode()) {
                 // The activity in the container may become focusable or non-focusable due to
                 // windowing modes changes (such as entering or leaving pinned windowing mode),
@@ -1050,9 +1089,9 @@
     }
 
     private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
-            int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
+            int syncId, @NonNull ActionChain chain, boolean isInLockTaskMode,
             @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
-            @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {
+            @Nullable ITaskFragmentOrganizer organizer) {
         final int type = hop.getType();
         switch (type) {
             case HIERARCHY_OP_TYPE_REMOVE_TASK: {
@@ -1122,7 +1161,7 @@
                 break;
             }
             case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
-                effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId,
+                effects |= reparentChildrenTasksHierarchyOp(hop, chain.mTransition, syncId,
                         isInLockTaskMode);
                 break;
             }
@@ -1175,13 +1214,13 @@
                 if (syncId >= 0) {
                     addToSyncSet(syncId, wc);
                 }
-                if (transition != null) {
-                    transition.collect(wc);
+                if (chain.mTransition != null) {
+                    chain.mTransition.collect(wc);
                     if (hop.isReparent()) {
                         if (wc.getParent() != null) {
                             // Collect the current parent. It's visibility may change as
                             // a result of this reparenting.
-                            transition.collect(wc.getParent());
+                            chain.mTransition.collect(wc.getParent());
                         }
                         if (hop.getNewParent() != null) {
                             final WindowContainer parentWc =
@@ -1190,16 +1229,22 @@
                                 Slog.e(TAG, "Can't resolve parent window from token");
                                 break;
                             }
-                            transition.collect(parentWc);
+                            chain.mTransition.collect(parentWc);
                         }
                     }
                 }
-                effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+                if (wc.asTask() != null) {
+                    effects |= sanitizeAndApplyHierarchyOpForTask(wc.asTask(), hop);
+                } else if (wc.asDisplayArea() != null) {
+                    effects |= sanitizeAndApplyHierarchyOpForDisplayArea(wc.asDisplayArea(), hop);
+                } else {
+                    throw new IllegalArgumentException("Invalid container in hierarchy op");
+                }
                 break;
             }
             case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION: {
-                effects |= applyTaskFragmentOperation(hop, transition, isInLockTaskMode, caller,
-                        errorCallbackToken, organizer);
+                effects |= applyTaskFragmentOperation(hop, chain, isInLockTaskMode,
+                        caller, errorCallbackToken, organizer);
                 break;
             }
             case HIERARCHY_OP_TYPE_PENDING_INTENT: {
@@ -1294,7 +1339,7 @@
                 Rect entryBounds = hop.getBounds();
                 mService.mRootWindowContainer.moveActivityToPinnedRootTask(
                         pipActivity, null /* launchIntoPipHostActivity */,
-                        "moveActivityToPinnedRootTask", null /* transition */, entryBounds);
+                        "moveActivityToPinnedRootTask", entryBounds);
 
                 if (pipActivity.isState(PAUSING) && pipActivity.mPauseSchedulePendingForPip) {
                     // Continue the pausing process. This must be done after moving PiP activity to
@@ -1313,13 +1358,13 @@
                 break;
             }
             case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: {
-                if (finishTransition == null) break;
+                if (!chain.isFinishing()) break;
                 final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
                 if (container == null) break;
                 final Task thisTask = container.asActivityRecord() != null
                         ? container.asActivityRecord().getTask() : container.asTask();
                 if (thisTask == null) break;
-                final Task restoreAt = finishTransition.getTransientLaunchRestoreTarget(container);
+                final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container);
                 if (restoreAt == null) break;
                 final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea();
                 taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt);
@@ -1409,7 +1454,7 @@
      *         {@link #TRANSACT_EFFECTS_LIFECYCLE} or {@link #TRANSACT_EFFECTS_CLIENT_CONFIG}.
      */
     private int applyTaskFragmentOperation(@NonNull WindowContainerTransaction.HierarchyOp hop,
-            @Nullable Transition transition, boolean isInLockTaskMode, @NonNull CallerInfo caller,
+            @NonNull ActionChain chain, boolean isInLockTaskMode, @NonNull CallerInfo caller,
             @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) {
         if (!validateTaskFragmentOperation(hop, errorCallbackToken, organizer)) {
             return TRANSACT_EFFECTS_NONE;
@@ -1432,7 +1477,7 @@
                     break;
                 }
                 createTaskFragment(taskFragmentCreationParams, errorCallbackToken, caller,
-                        transition);
+                        chain.mTransition);
                 break;
             }
             case OP_TYPE_DELETE_TASK_FRAGMENT: {
@@ -1449,7 +1494,7 @@
                         break;
                     }
                 }
-                effects |= deleteTaskFragment(taskFragment, transition);
+                effects |= deleteTaskFragment(taskFragment, chain.mTransition);
                 break;
             }
             case OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -1498,14 +1543,14 @@
                             opType, exception);
                     break;
                 }
-                if (transition != null) {
-                    transition.collect(activity);
+                if (chain.mTransition != null) {
+                    chain.collect(activity);
                     if (activity.getParent() != null) {
                         // Collect the current parent. Its visibility may change as a result of
                         // this reparenting.
-                        transition.collect(activity.getParent());
+                        chain.collect(activity.getParent());
                     }
-                    transition.collect(taskFragment);
+                    chain.collect(taskFragment);
                 }
                 activity.reparent(taskFragment, POSITION_TOP);
                 effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -1661,8 +1706,8 @@
                 // If any TaskFragment in the Task is collected by the transition, we make the decor
                 // surface visible in sync with the TaskFragment transition. Otherwise, we make the
                 // decor surface visible immediately.
-                final TaskFragment syncTaskFragment = transition != null
-                        ? task.getTaskFragment(transition.mParticipants::contains)
+                final TaskFragment syncTaskFragment = chain.mTransition != null
+                        ? task.getTaskFragment(chain.mTransition.mParticipants::contains)
                         : null;
 
                 if (syncTaskFragment != null) {
@@ -1714,7 +1759,7 @@
                     // The decor surface boost/unboost must be applied after the transition is
                     // completed. Otherwise, the decor surface could be moved before Shell completes
                     // the transition, causing flicker.
-                    runAfterTransition(transition, task::commitDecorSurfaceBoostedState);
+                    runAfterTransition(chain.mTransition, task::commitDecorSurfaceBoostedState);
                 }
                 break;
             }
@@ -1850,12 +1895,22 @@
         return starterResult[0];
     }
 
-    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
-            WindowContainerTransaction.HierarchyOp hop) {
-        final Task task = container.asTask();
-        if (task == null) {
-            throw new IllegalArgumentException("Invalid container in hierarchy op");
+    private int sanitizeAndApplyHierarchyOpForDisplayArea(@NonNull DisplayArea displayArea,
+            @NonNull WindowContainerTransaction.HierarchyOp hop) {
+        if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) {
+            throw new UnsupportedOperationException("DisplayArea only supports reordering");
         }
+        if (displayArea.getParent() == null) {
+            return TRANSACT_EFFECTS_NONE;
+        }
+        displayArea.getParent().positionChildAt(
+                hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                displayArea, hop.includingParents());
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
+    private int sanitizeAndApplyHierarchyOpForTask(@NonNull Task task,
+            @NonNull WindowContainerTransaction.HierarchyOp hop) {
         final DisplayContent dc = task.getDisplayContent();
         if (dc == null) {
             Slog.w(TAG, "Container is no longer attached: " + task);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2bae0a8..87ce866 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -89,6 +89,7 @@
 import com.android.server.Watchdog;
 import com.android.server.grammaticalinflection.GrammaticalInflectionManagerInternal;
 import com.android.server.wm.ActivityTaskManagerService.HotPath;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -284,14 +285,11 @@
     static final int ANIMATING_REASON_REMOTE_ANIMATION = 1;
     /** It is set for wakefulness transition. */
     static final int ANIMATING_REASON_WAKEFULNESS_CHANGE = 1 << 1;
-    /** Whether the legacy {@link RecentsAnimation} is running. */
-    static final int ANIMATING_REASON_LEGACY_RECENT_ANIMATION = 1 << 2;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
             ANIMATING_REASON_REMOTE_ANIMATION,
             ANIMATING_REASON_WAKEFULNESS_CHANGE,
-            ANIMATING_REASON_LEGACY_RECENT_ANIMATION,
     })
     @interface AnimatingReason {}
 
@@ -360,7 +358,6 @@
         mUseFifoUiScheduling = com.android.window.flags.Flags.fifoPriorityForMajorUiProcesses()
                 && (isSysUiPackage || mAtm.isCallerRecents(uid));
 
-        onConfigurationChanged(atm.getGlobalConfiguration());
         mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
     }
 
@@ -698,20 +695,13 @@
     public boolean areBackgroundFgsStartsAllowed() {
         return areBackgroundActivityStartsAllowed(
                 mAtm.getBalAppSwitchesState(),
-                true /* isCheckingForFgsStart */).allows();
+                BackgroundLaunchProcessController.CHECK_FOR_FGS_START).allows();
     }
 
     BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
-            int appSwitchState) {
-        return areBackgroundActivityStartsAllowed(
-                appSwitchState,
-                false /* isCheckingForFgsStart */);
-    }
-
-    private BackgroundActivityStartController.BalVerdict areBackgroundActivityStartsAllowed(
-            int appSwitchState, boolean isCheckingForFgsStart) {
+            int appSwitchState, BalCheckConfiguration checkConfiguration) {
         return mBgLaunchController.areBackgroundActivityStartsAllowed(mPid, mUid,
-                mInfo.packageName, appSwitchState, isCheckingForFgsStart,
+                mInfo.packageName, appSwitchState, checkConfiguration,
                 hasActivityInVisibleTask(), mInstrumentingWithBackgroundActivityStartPrivileges,
                 mAtm.getLastStopAppSwitchesTime(),
                 mLastActivityLaunchTime, mLastActivityFinishTime);
@@ -1689,7 +1679,8 @@
                 resolvedConfig,
                 false /* optsOutEdgeToEdge */,
                 false /* hasFixedRotationTransform */,
-                false /* hasCompatDisplayInsets */);
+                false /* hasCompatDisplayInsets */,
+                null /* task */);
     }
 
     void dispatchConfiguration(@NonNull Configuration config) {
@@ -1934,7 +1925,12 @@
                 // showing.
                 // If the configuration has been overridden by previous activity, empty it.
                 mIsActivityConfigOverrideAllowed = false;
-                unregisterActivityConfigurationListener();
+                // The call to `onServiceStarted` is not guarded with WM lock.
+                mAtm.mH.post(() -> {
+                    synchronized (mAtm.mGlobalLock) {
+                        unregisterActivityConfigurationListener();
+                    }
+                });
                 break;
             default:
                 break;
@@ -2016,14 +2012,6 @@
         return mStoppedState == STOPPED_STATE_FIRST_LAUNCH;
     }
 
-    void setRunningRecentsAnimation(boolean running) {
-        if (running) {
-            addAnimatingReason(ANIMATING_REASON_LEGACY_RECENT_ANIMATION);
-        } else {
-            removeAnimatingReason(ANIMATING_REASON_LEGACY_RECENT_ANIMATION);
-        }
-    }
-
     void setRunningRemoteAnimation(boolean running) {
         if (running) {
             addAnimatingReason(ANIMATING_REASON_REMOTE_ANIMATION);
@@ -2118,9 +2106,6 @@
             if ((animatingReasons & ANIMATING_REASON_WAKEFULNESS_CHANGE) != 0) {
                 pw.print("wakefulness|");
             }
-            if ((animatingReasons & ANIMATING_REASON_LEGACY_RECENT_ANIMATION) != 0) {
-                pw.print("legacy-recents");
-            }
             pw.println();
         }
         if (mUseFifoUiScheduling) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ec2fd3f..256d0c6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -128,7 +128,6 @@
 import static com.android.server.wm.MoveAnimationSpecProto.TO;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_STARTING_REVEAL;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
@@ -234,7 +233,6 @@
 import android.view.Surface;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewTreeObserver;
@@ -581,7 +579,7 @@
      * is guaranteed to be cleared.
      */
     static final int EXIT_ANIMATING_TYPES = ANIMATION_TYPE_APP_TRANSITION
-            | ANIMATION_TYPE_WINDOW_ANIMATION | ANIMATION_TYPE_RECENTS;
+            | ANIMATION_TYPE_WINDOW_ANIMATION;
 
     /** Currently running an exit animation? */
     boolean mAnimatingExit;
@@ -1705,18 +1703,6 @@
         return mActivityRecord != null ? mActivityRecord.getTaskFragment() : null;
     }
 
-    @Nullable Task getRootTask() {
-        final Task task = getTask();
-        if (task != null) {
-            return task.getRootTask();
-        }
-        // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
-        // associate them with some root task to enable dimming.
-        final DisplayContent dc = getDisplayContent();
-        return mAttrs.type >= FIRST_SYSTEM_WINDOW
-                && dc != null ? dc.getDefaultTaskDisplayArea().getRootHomeTask() : null;
-    }
-
     /**
      * Retrieves the visible bounds of the window.
      * @param bounds The rect which gets the bounds.
@@ -1971,13 +1957,9 @@
      * it must be drawn before allDrawn can become true.
      */
     boolean isInteresting() {
-        final RecentsAnimationController recentsAnimationController =
-                mWmService.getRecentsAnimationController();
         return mActivityRecord != null
                 && (!mActivityRecord.isFreezingScreen() || !mAppFreezing)
-                && mViewVisibility == View.VISIBLE
-                && (recentsAnimationController == null
-                         || recentsAnimationController.isInterestingForAllDrawn(this));
+                && mViewVisibility == View.VISIBLE;
     }
 
     /**
@@ -2385,6 +2367,8 @@
             mWmService.mDisplayManagerInternal.onPresentation(dc.getDisplay().getDisplayId(),
                     /*isShown=*/ false);
         }
+        // Check if window provides non decor insets before clearing its provided insets.
+        final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
 
         dc.getDisplayPolicy().removeWindowLw(this);
 
@@ -2395,6 +2379,18 @@
         mWmService.postWindowRemoveCleanupLocked(this);
 
         consumeInsetsChange();
+
+        // Update the orientation when removing a window affecting the display orientation.
+        // Also recompute configuration if it provides screen decor insets.
+        boolean needToSendNewConfiguration = dc.getLastOrientationSource() == this
+                && dc.updateOrientation();
+        if (windowProvidesDisplayDecorInsets) {
+            needToSendNewConfiguration |= dc.getDisplayPolicy().updateDecorInsetsInfo();
+        }
+
+        if (needToSendNewConfiguration) {
+            dc.sendNewConfiguration();
+        }
     }
 
     @Override
@@ -2447,16 +2443,10 @@
                     mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
                     mWmService.mDisplayFrozen, Debug.getCallers(6));
 
-            // Visibility of the removed window. Will be used later to update orientation later on.
-            boolean wasVisible = false;
-
             // First, see if we need to run an animation. If we do, we have to hold off on removing the
             // window until the animation is done. If the display is frozen, just remove immediately,
             // since the animation wouldn't be seen.
             if (mHasSurface && mToken.okToAnimate()) {
-                // If we are not currently running the exit animation, we need to see about starting one
-                wasVisible = isVisible();
-
                 // Remove immediately if there is display transition because the animation is
                 // usually unnoticeable (e.g. covered by rotation animation) and the animation
                 // bounds could be inconsistent, such as depending on when the window applies
@@ -2466,7 +2456,9 @@
                         // look weird if its orientation is changed.
                         && !inRelaunchingActivity();
 
-                if (wasVisible && isDisplayed()) {
+                // If we are not currently running the exit animation,
+                // we need to see about starting one
+                if (isVisible() && isDisplayed()) {
                     final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
 
                     // Try starting an animation.
@@ -2515,21 +2507,7 @@
                 }
             }
 
-            // Check if window provides non decor insets before clearing its provided insets.
-            final boolean windowProvidesDisplayDecorInsets = providesDisplayDecorInsets();
-
             removeImmediately();
-            // Removing a visible window may affect the display orientation so just update it if
-            // needed. Also recompute configuration if it provides screen decor insets.
-            boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
-            if (windowProvidesDisplayDecorInsets) {
-                needToSendNewConfiguration |=
-                        displayContent.getDisplayPolicy().updateDecorInsetsInfo();
-            }
-
-            if (needToSendNewConfiguration) {
-                displayContent.sendNewConfiguration();
-            }
             mWmService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
                             : UPDATE_FOCUS_NORMAL,
@@ -2579,10 +2557,9 @@
             return false;
         }
 
-        final Task rootTask = getRootTask();
-        if (rootTask != null && !rootTask.isFocusable()) {
-            // Ignore when the root task shouldn't receive input event.
-            // (i.e. the minimized root task in split screen mode.)
+        final Task task = getTask();
+        if (task != null && !task.isFocusable()) {
+            // The task can be set as non-focusable, e.g. swapping split-screen sides.
             return false;
         }
 
@@ -2608,7 +2585,7 @@
         }
 
         // Don't allow transient-launch activities to take IME.
-        if (rootTask != null && mActivityRecord != null
+        if (task != null && mActivityRecord != null
                 && mTransitionController.isTransientLaunch(mActivityRecord)) {
             return false;
         }
@@ -2794,11 +2771,9 @@
                 // means we need to intercept touches outside of that window. The dim layer
                 // user associated with the window (task or root task) will give us the good
                 // bounds, as they would be used to display the dim layer.
-                final TaskFragment taskFragment = getTaskFragment();
+                final TaskFragment taskFragment = mActivityRecord.getTaskFragment();
                 if (taskFragment != null) {
                     taskFragment.getDimBounds(mTmpRect);
-                } else if (getRootTask() != null) {
-                    getRootTask().getDimBounds(mTmpRect);
                 }
             }
         }
@@ -3006,7 +2981,8 @@
                 resolvedConfig,
                 (mAttrs.privateFlags & PRIVATE_FLAG_OPT_OUT_EDGE_TO_EDGE) != 0,
                 false /* hasFixedRotationTransform */,
-                false /* hasCompatDisplayInsets */);
+                false /* hasCompatDisplayInsets */,
+                null /* task */);
     }
 
     /**
@@ -3754,7 +3730,7 @@
         // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
         // since it will be destroyed anyway. This also prevents the client from receiving
         // windowing mode change before it is destroyed.
-        if (inRelaunchingActivity()) {
+        if (inRelaunchingActivity() && mAttrs.type != TYPE_APPLICATION_STARTING) {
             return;
         }
         // If this is an activity or wallpaper and is invisible or going invisible, don't report
@@ -3942,14 +3918,6 @@
         }
     }
 
-    private int getRootTaskId() {
-        final Task rootTask = getRootTask();
-        if (rootTask == null) {
-            return INVALID_TASK_ID;
-        }
-        return rootTask.mTaskId;
-    }
-
     public void registerFocusObserver(IWindowFocusObserver observer) {
         synchronized (mWmService.mGlobalLock) {
             if (mFocusCallbacks == null) {
@@ -3975,7 +3943,7 @@
      * Returns {@code true} if activity bounds are letterboxed or letterboxed for display cutout.
      *
      * <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link
-     * LetterboxUiController#shouldShowLetterboxUi} for more context.
+     * AppCompatLetterboxOverrides#shouldShowLetterboxUi} for more context.
      */
     boolean areAppWindowBoundsLetterboxed() {
         return mActivityRecord != null && !isStartingWindowAssociatedToTask()
@@ -4076,16 +4044,21 @@
     @CallSuper
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
+            @WindowTracingLogLevel int logLevel) {
         boolean isVisible = isVisible();
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible) {
             return;
         }
 
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(DISPLAY_ID, getDisplayId());
-        proto.write(STACK_ID, getRootTaskId());
+        int rootTaskId = INVALID_TASK_ID;
+        final Task task = getTask();
+        if (task != null) {
+            rootTaskId = task.getRootTaskId();
+        }
+        proto.write(STACK_ID, rootTaskId);
         mAttrs.dumpDebug(proto, ATTRIBUTES);
         mGivenContentInsets.dumpDebug(proto, GIVEN_CONTENT_INSETS);
         mWindowFrames.dumpDebug(proto, WINDOW_FRAMES);
@@ -4143,8 +4116,9 @@
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         pw.print(prefix + "mDisplayId=" + getDisplayId());
-        if (getRootTask() != null) {
-            pw.print(" rootTaskId=" + getRootTaskId());
+        final Task task = getTask();
+        if (task != null) {
+            pw.print(" taskId=" + task.mTaskId);
         }
         pw.println(" mSession=" + mSession
                 + " mClient=" + mClient.asBinder());
@@ -4674,17 +4648,6 @@
         if (!isImeLayeringTarget()) {
             return false;
         }
-        if (!com.android.window.flags.Flags.doNotSkipImeByTargetVisibility()) {
-            // Note that we don't process IME window if the IME input target is not on the screen.
-            // In case some unexpected IME visibility cases happen like starting the remote
-            // animation on the keyguard but seeing the IME window that originally on the app
-            // which behinds the keyguard.
-            final WindowState imeInputTarget = getImeInputTarget();
-            if (imeInputTarget != null
-                    && !(imeInputTarget.isDrawn() || imeInputTarget.isVisibleRequested())) {
-                return false;
-            }
-        }
         return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
     }
 
@@ -5198,15 +5161,6 @@
     }
 
     @Override
-    SurfaceSession getSession() {
-        if (mSession.mSurfaceSession != null) {
-            return mSession.mSurfaceSession;
-        } else {
-            return getParent().getSession();
-        }
-    }
-
-    @Override
     boolean needsZBoost() {
         final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING);
         if (mIsImWindow && target != null) {
@@ -5337,6 +5291,14 @@
         super.prepareSurfaces();
     }
 
+    void updateSurfacePositionIfNeeded() {
+        if (mWindowFrames.mRelFrame.top == mWindowFrames.mLastRelFrame.top
+                && mWindowFrames.mRelFrame.left == mWindowFrames.mLastRelFrame.left) {
+            return;
+        }
+        updateSurfacePosition(getSyncTransaction());
+    }
+
     @Override
     @VisibleForTesting
     void updateSurfacePosition(Transaction t) {
@@ -6144,7 +6106,7 @@
 
     @Override
     public void dumpProto(ProtoOutputStream proto, long fieldId,
-                          @WindowTraceLogLevel int logLevel) {
+                          @WindowTracingLogLevel int logLevel) {
         dumpDebug(proto, fieldId, logLevel);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 24a2a62..82fa9d4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -67,9 +67,8 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
-import com.android.internal.protolog.common.LogLevel;
 import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
+import com.android.internal.protolog.common.LogLevel;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
@@ -322,8 +321,7 @@
         }
 
         if (DEBUG_VISIBILITY) {
-            Slog.v(TAG, "Creating surface in session "
-                    + mSession.mSurfaceSession + " window " + this
+            Slog.v(TAG, "Creating surface " + this
                     + " format=" + attrs.format + " flags=" + flags);
         }
 
@@ -359,9 +357,8 @@
             w.mInputWindowHandle.forceChange();
 
             ProtoLog.i(WM_SHOW_SURFACE_ALLOC,
-                        "  CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x / %s",
-                    mSurfaceControl, mSession.mSurfaceSession, mSession.mPid, attrs.format,
-                        flags, this);
+                    "  CREATE SURFACE %s: pid=%d format=%d flags=0x%x / %s",
+                    mSurfaceControl, mSession.mPid, attrs.format, flags, this);
         } catch (OutOfResourcesException e) {
             Slog.w(TAG, "OutOfResourcesException creating surface");
             mService.mRoot.reclaimSomeSurfaceMemory(this, "create", true);
@@ -413,7 +410,7 @@
             ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
                     mWin, new RuntimeException().fillInStackTrace());
             destroySurface(t);
-            if (Flags.ensureWallpaperInTransitions()) {
+            if (mService.mFlags.mEnsureWallpaperInTransitions) {
                 if (mWallpaperControllerLocked.isWallpaperTarget(mWin)) {
                     mWin.requestUpdateWallpaperIfNeeded();
                 }
@@ -464,7 +461,7 @@
 
         if (!w.isOnScreen()) {
             hide(t, "prepareSurfaceLocked");
-            if (!w.mIsWallpaper || !Flags.ensureWallpaperInTransitions()) {
+            if (!w.mIsWallpaper || !mService.mFlags.mEnsureWallpaperInTransitions) {
                 mWallpaperControllerLocked.hideWallpapers(w);
             }
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 11ef2cd..67bd5cb 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -698,8 +698,8 @@
     @CallSuper
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
-            @WindowTraceLogLevel int logLevel) {
-        if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
+            @WindowTracingLogLevel int logLevel) {
+        if (logLevel == WindowTracingLogLevel.CRITICAL && !isVisible()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowTraceLogLevel.java b/services/core/java/com/android/server/wm/WindowTraceLogLevel.java
deleted file mode 100644
index 2165c66..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceLogLevel.java
+++ /dev/null
@@ -1,49 +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.wm;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-@IntDef({
-        WindowTraceLogLevel.ALL,
-        WindowTraceLogLevel.TRIM,
-        WindowTraceLogLevel.CRITICAL,
-})
-@Retention(RetentionPolicy.SOURCE)
-@interface WindowTraceLogLevel{
-    /**
-     * Logs all elements with maximum amount of information.
-     *
-     * Used to store the current window manager state when generating a bug report
-     */
-    int ALL = 0;
-    /**
-     * Logs all elements but doesn't write all configuration data
-     *
-     * Default log level for manually activated Winscope traces
-     */
-    int TRIM = 1;
-    /**
-     * Logs only visible elements, with the minimum amount of performance overhead
-     *
-     * Default log level for continuous traces
-     */
-    int CRITICAL = 2;
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 04d5c03..fe26726 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -112,7 +112,7 @@
         saveForBugreportInternal(pw);
     }
 
-    abstract void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw);
+    abstract void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw);
     abstract void setLogFrequency(boolean onFrame, PrintWriter pw);
     abstract void setBufferCapacity(int capacity, PrintWriter pw);
     abstract boolean isEnabled();
@@ -158,7 +158,7 @@
      * @param where Logging point descriptor
      * @param elapsedRealtimeNanos Timestamp
      */
-    protected void dumpToProto(ProtoOutputStream os, @WindowTraceLogLevel int logLevel,
+    protected void dumpToProto(ProtoOutputStream os, @WindowTracingLogLevel int logLevel,
             String where, long elapsedRealtimeNanos) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
         try {
diff --git a/services/core/java/com/android/server/wm/WindowTracingDataSource.java b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
index 3d2c0d3..dc048ef 100644
--- a/services/core/java/com/android/server/wm/WindowTracingDataSource.java
+++ b/services/core/java/com/android/server/wm/WindowTracingDataSource.java
@@ -33,8 +33,8 @@
 import android.util.proto.ProtoInputStream;
 
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
 
 public final class WindowTracingDataSource extends DataSource<WindowTracingDataSource.Instance,
         WindowTracingDataSource.TlsState, Void> {
@@ -50,12 +50,14 @@
     }
 
     public static class Config {
-        public final @WindowTraceLogLevel int mLogLevel;
-        public final boolean mLogOnFrame;
+        public final @WindowTracingLogLevel int mLogLevel;
+        public final @WindowTracingLogFrequency int mLogFrequency;
 
-        private Config(@WindowTraceLogLevel int logLevel, boolean logOnFrame) {
+        private Config(
+                @WindowTracingLogLevel int logLevel,
+                @WindowTracingLogFrequency int logFrequency) {
             mLogLevel = logLevel;
-            mLogOnFrame = logOnFrame;
+            mLogFrequency = logFrequency;
         }
     }
 
@@ -68,20 +70,17 @@
         }
     }
 
-    private static final Config CONFIG_DEFAULT = new Config(WindowTraceLogLevel.TRIM, true);
+    private static final Config CONFIG_DEFAULT =
+            new Config(WindowTracingLogLevel.TRIM, WindowTracingLogFrequency.FRAME);
     private static final int CONFIG_VALUE_UNSPECIFIED = 0;
     private static final String TAG = "WindowTracingDataSource";
 
     @NonNull
-    private final Consumer<Config> mOnStartCallback;
-    @NonNull
-    private final Consumer<Config> mOnStopCallback;
+    private final WeakReference<WindowTracingPerfetto> mWindowTracing;
 
-    public WindowTracingDataSource(@NonNull Consumer<Config> onStart,
-            @NonNull Consumer<Config> onStop) {
+    public WindowTracingDataSource(WindowTracingPerfetto windowTracing) {
         super(DATA_SOURCE_NAME);
-        mOnStartCallback = onStart;
-        mOnStopCallback = onStop;
+        mWindowTracing = new WeakReference<>(windowTracing);
 
         Producer.init(InitArguments.DEFAULTS);
         DataSourceParams params =
@@ -90,6 +89,7 @@
                                 PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
                         .build();
         register(params);
+        Log.i(TAG, "Registered with perfetto service");
     }
 
     @Override
@@ -99,12 +99,18 @@
         return new Instance(this, instanceIndex, config != null ? config : CONFIG_DEFAULT) {
             @Override
             protected void onStart(StartCallbackArguments args) {
-                mOnStartCallback.accept(mConfig);
+                WindowTracingPerfetto windowTracing = mWindowTracing.get();
+                if (windowTracing != null) {
+                    windowTracing.onStart(mConfig);
+                }
             }
 
             @Override
             protected void onStop(StopCallbackArguments args) {
-                mOnStopCallback.accept(mConfig);
+                WindowTracingPerfetto windowTracing = mWindowTracing.get();
+                if (windowTracing != null) {
+                    windowTracing.onStop(mConfig);
+                }
             }
         };
     }
@@ -160,45 +166,48 @@
             throw new RuntimeException("Failed to parse WindowManagerConfig", e);
         }
 
-        @WindowTraceLogLevel int logLevel;
+        @WindowTracingLogLevel int logLevel;
         switch(parsedLogLevel) {
             case CONFIG_VALUE_UNSPECIFIED:
                 Log.w(TAG, "Unspecified log level. Defaulting to TRIM");
-                logLevel = WindowTraceLogLevel.TRIM;
+                logLevel = WindowTracingLogLevel.TRIM;
                 break;
             case WindowManagerConfig.LOG_LEVEL_VERBOSE:
-                logLevel = WindowTraceLogLevel.ALL;
+                logLevel = WindowTracingLogLevel.ALL;
                 break;
             case WindowManagerConfig.LOG_LEVEL_DEBUG:
-                logLevel = WindowTraceLogLevel.TRIM;
+                logLevel = WindowTracingLogLevel.TRIM;
                 break;
             case WindowManagerConfig.LOG_LEVEL_CRITICAL:
-                logLevel = WindowTraceLogLevel.CRITICAL;
+                logLevel = WindowTracingLogLevel.CRITICAL;
                 break;
             default:
                 Log.w(TAG, "Unrecognized log level. Defaulting to TRIM");
-                logLevel = WindowTraceLogLevel.TRIM;
+                logLevel = WindowTracingLogLevel.TRIM;
                 break;
         }
 
-        boolean logOnFrame;
+        @WindowTracingLogFrequency int logFrequency;
         switch(parsedLogFrequency) {
             case CONFIG_VALUE_UNSPECIFIED:
-                Log.w(TAG, "Unspecified log frequency. Defaulting to 'log on frame'");
-                logOnFrame = true;
+                Log.w(TAG, "Unspecified log frequency. Defaulting to 'frame'");
+                logFrequency = WindowTracingLogFrequency.FRAME;
                 break;
             case WindowManagerConfig.LOG_FREQUENCY_FRAME:
-                logOnFrame = true;
+                logFrequency = WindowTracingLogFrequency.FRAME;
                 break;
             case WindowManagerConfig.LOG_FREQUENCY_TRANSACTION:
-                logOnFrame = false;
+                logFrequency = WindowTracingLogFrequency.TRANSACTION;
+                break;
+            case WindowManagerConfig.LOG_FREQUENCY_SINGLE_DUMP:
+                logFrequency = WindowTracingLogFrequency.SINGLE_DUMP;
                 break;
             default:
-                Log.w(TAG, "Unrecognized log frequency. Defaulting to 'log on frame'");
-                logOnFrame = true;
+                Log.w(TAG, "Unrecognized log frequency. Defaulting to 'frame'");
+                logFrequency = WindowTracingLogFrequency.FRAME;
                 break;
         }
 
-        return new Config(logLevel, logOnFrame);
+        return new Config(logLevel, logFrequency);
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTracingLegacy.java b/services/core/java/com/android/server/wm/WindowTracingLegacy.java
index 7a36707..34fd088 100644
--- a/services/core/java/com/android/server/wm/WindowTracingLegacy.java
+++ b/services/core/java/com/android/server/wm/WindowTracingLegacy.java
@@ -30,6 +30,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.TraceBuffer;
 
 import java.io.File;
@@ -58,7 +59,7 @@
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
 
-    protected @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+    protected @WindowTracingLogLevel int mLogLevel = WindowTracingLogLevel.TRIM;
     protected boolean mLogOnFrame = false;
 
     WindowTracingLegacy(WindowManagerService service, Choreographer choreographer) {
@@ -66,6 +67,7 @@
                 service.mGlobalLock, BUFFER_CAPACITY_TRIM);
     }
 
+    @VisibleForTesting
     WindowTracingLegacy(File traceFile, WindowManagerService service, Choreographer choreographer,
             WindowManagerGlobalLock globalLock, int bufferSize) {
         super(service, choreographer, globalLock);
@@ -74,20 +76,20 @@
     }
 
     @Override
-    void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+    void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw) {
         logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
         mLogLevel = logLevel;
 
         switch (logLevel) {
-            case WindowTraceLogLevel.ALL: {
+            case WindowTracingLogLevel.ALL: {
                 setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
                 break;
             }
-            case WindowTraceLogLevel.TRIM: {
+            case WindowTracingLogLevel.TRIM: {
                 setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
                 break;
             }
-            case WindowTraceLogLevel.CRITICAL: {
+            case WindowTracingLogLevel.CRITICAL: {
                 setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
                 break;
             }
@@ -141,19 +143,19 @@
                 String logLevelStr = shell.getNextArgRequired().toLowerCase();
                 switch (logLevelStr) {
                     case "all": {
-                        setLogLevel(WindowTraceLogLevel.ALL, pw);
+                        setLogLevel(WindowTracingLogLevel.ALL, pw);
                         break;
                     }
                     case "trim": {
-                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        setLogLevel(WindowTracingLogLevel.TRIM, pw);
                         break;
                     }
                     case "critical": {
-                        setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+                        setLogLevel(WindowTracingLogLevel.CRITICAL, pw);
                         break;
                     }
                     default: {
-                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        setLogLevel(WindowTracingLogLevel.TRIM, pw);
                         break;
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java b/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java
new file mode 100644
index 0000000..8e2c308
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracingLogFrequency.java
@@ -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.server.wm;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({
+        WindowTracingLogFrequency.FRAME,
+        WindowTracingLogFrequency.TRANSACTION,
+        WindowTracingLogFrequency.SINGLE_DUMP,
+})
+@Retention(RetentionPolicy.SOURCE)
+@interface WindowTracingLogFrequency {
+    /**
+     * Trace state snapshots when a frame is committed.
+     */
+    int FRAME = 0;
+    /**
+     * Trace state snapshots when a transaction is committed.
+     */
+    int TRANSACTION = 1;
+    /**
+     * Trace single state snapshots when the Perfetto data source is started.
+     */
+    int SINGLE_DUMP = 2;
+}
diff --git a/services/core/java/com/android/server/wm/WindowTracingLogLevel.java b/services/core/java/com/android/server/wm/WindowTracingLogLevel.java
new file mode 100644
index 0000000..4f901c6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/WindowTracingLogLevel.java
@@ -0,0 +1,49 @@
+/*
+ * 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.wm;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@IntDef({
+        WindowTracingLogLevel.ALL,
+        WindowTracingLogLevel.TRIM,
+        WindowTracingLogLevel.CRITICAL,
+})
+@Retention(RetentionPolicy.SOURCE)
+@interface WindowTracingLogLevel {
+    /**
+     * Logs all elements with maximum amount of information.
+     *
+     * Used to store the current window manager state when generating a bug report
+     */
+    int ALL = 0;
+    /**
+     * Logs all elements but doesn't write all configuration data
+     *
+     * Default log level for manually activated Winscope traces
+     */
+    int TRIM = 1;
+    /**
+     * Logs only visible elements, with the minimum amount of performance overhead
+     *
+     * Default log level for continuous traces
+     */
+    int CRITICAL = 2;
+}
diff --git a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
index 653b6da..22d6c86 100644
--- a/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
+++ b/services/core/java/com/android/server/wm/WindowTracingPerfetto.java
@@ -25,6 +25,8 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -33,15 +35,20 @@
 
     private final AtomicInteger mCountSessionsOnFrame = new AtomicInteger();
     private final AtomicInteger mCountSessionsOnTransaction = new AtomicInteger();
-    private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(
-            this::onStart, this::onStop);
+    private final WindowTracingDataSource mDataSource = new WindowTracingDataSource(this);
 
     WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer) {
-        super(service, choreographer, service.mGlobalLock);
+        this(service, choreographer, service.mGlobalLock);
+    }
+
+    @VisibleForTesting
+    WindowTracingPerfetto(WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock) {
+        super(service, choreographer, globalLock);
     }
 
     @Override
-    void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+    void setLogLevel(@WindowTracingLogLevel int logLevel, PrintWriter pw) {
         logAndPrintln(pw, "Log level must be configured through perfetto");
     }
 
@@ -110,7 +117,15 @@
                     if (!isDataSourceStarting) {
                         return;
                     }
-                } else if (isOnFrameLogEvent != dataSourceConfig.mLogOnFrame) {
+                } else if (isOnFrameLogEvent) {
+                    boolean isDataSourceLoggingOnFrame =
+                            dataSourceConfig.mLogFrequency == WindowTracingLogFrequency.FRAME;
+                    if (!isDataSourceLoggingOnFrame) {
+                        return;
+                    }
+                } else if (dataSourceConfig.mLogFrequency
+                        == WindowTracingLogFrequency.SINGLE_DUMP) {
+                    // If it is a dump, write only the start log event and skip the following ones
                     return;
                 }
 
@@ -140,22 +155,22 @@
         return mCountSessionsOnTransaction.get() > 0;
     }
 
-    private void onStart(WindowTracingDataSource.Config config) {
-        if (config.mLogOnFrame) {
+    void onStart(WindowTracingDataSource.Config config) {
+        if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
             mCountSessionsOnFrame.incrementAndGet();
-        } else {
+        } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
             mCountSessionsOnTransaction.incrementAndGet();
         }
 
         Log.i(TAG, "Started with logLevel: " + config.mLogLevel
-                + " logOnFrame: " + config.mLogOnFrame);
+                + " logFrequency: " + config.mLogFrequency);
         log(WHERE_START_TRACING);
     }
 
-    private void onStop(WindowTracingDataSource.Config config) {
-        if (config.mLogOnFrame) {
+    void onStop(WindowTracingDataSource.Config config) {
+        if (config.mLogFrequency == WindowTracingLogFrequency.FRAME) {
             mCountSessionsOnFrame.decrementAndGet();
-        } else {
+        } else if (config.mLogFrequency == WindowTracingLogFrequency.TRANSACTION) {
             mCountSessionsOnTransaction.decrementAndGet();
         }
         Log.i(TAG, "Stopped");
diff --git a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
index 70c66de..d33313e 100644
--- a/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
+++ b/services/core/java/com/android/server/wm/utils/DesktopModeFlagsUtil.java
@@ -43,7 +43,7 @@
     // All desktop mode related flags to be overridden by developer option toggle will be added here
     DESKTOP_WINDOWING_MODE(
             Flags::enableDesktopWindowingMode, /* shouldOverrideByDevOption= */ true),
-    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true);
+    DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, false);
 
     private static final String TAG = "DesktopModeFlagsUtil";
     // Function called to obtain aconfig flag value.
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index f1e94de..a5085fc 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -211,7 +211,6 @@
         "android.system.suspend-V1-ndk",
         "server_configurable_flags",
         "service.incremental",
-        "android.companion.virtualdevice.flags-aconfig-cc",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 9ec5ae5..6f72760 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -19,273 +19,22 @@
 #include <android-base/unique_fd.h>
 #include <android/input.h>
 #include <android/keycodes.h>
-#include <android_companion_virtualdevice_flags.h>
-#include <errno.h>
-#include <fcntl.h>
 #include <input/Input.h>
 #include <input/VirtualInputDevice.h>
-#include <linux/uinput.h>
-#include <math.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <utils/Log.h>
 
-#include <map>
-#include <set>
 #include <string>
 
 using android::base::unique_fd;
 
 namespace android {
 
-namespace vd_flags = android::companion::virtualdevice::flags;
-
 static constexpr jlong INVALID_PTR = 0;
 
-enum class DeviceType {
-    KEYBOARD,
-    MOUSE,
-    TOUCHSCREEN,
-    DPAD,
-    STYLUS,
-    ROTARY_ENCODER,
-};
-
-static unique_fd invalidFd() {
-    return unique_fd(-1);
-}
-
-/** Creates a new uinput device and assigns a file descriptor. */
-static unique_fd openUinput(const char* readableName, jint vendorId, jint productId,
-                            const char* phys, DeviceType deviceType, jint screenHeight,
-                            jint screenWidth) {
-    unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
-    if (fd < 0) {
-        ALOGE("Error creating uinput device: %s", strerror(errno));
-        return invalidFd();
-    }
-
-    ioctl(fd, UI_SET_PHYS, phys);
-
-    ioctl(fd, UI_SET_EVBIT, EV_KEY);
-    ioctl(fd, UI_SET_EVBIT, EV_SYN);
-    switch (deviceType) {
-        case DeviceType::DPAD:
-            for (const auto& [_, keyCode] : VirtualDpad::DPAD_KEY_CODE_MAPPING) {
-                ioctl(fd, UI_SET_KEYBIT, keyCode);
-            }
-            break;
-        case DeviceType::KEYBOARD:
-            for (const auto& [_, keyCode] : VirtualKeyboard::KEY_CODE_MAPPING) {
-                ioctl(fd, UI_SET_KEYBIT, keyCode);
-            }
-            break;
-        case DeviceType::MOUSE:
-            ioctl(fd, UI_SET_EVBIT, EV_REL);
-            ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
-            ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
-            ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
-            ioctl(fd, UI_SET_KEYBIT, BTN_BACK);
-            ioctl(fd, UI_SET_KEYBIT, BTN_FORWARD);
-            ioctl(fd, UI_SET_RELBIT, REL_X);
-            ioctl(fd, UI_SET_RELBIT, REL_Y);
-            ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
-            ioctl(fd, UI_SET_RELBIT, REL_HWHEEL);
-            if (vd_flags::high_resolution_scroll()) {
-                ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
-                ioctl(fd, UI_SET_RELBIT, REL_HWHEEL_HI_RES);
-            }
-            break;
-        case DeviceType::TOUCHSCREEN:
-            ioctl(fd, UI_SET_EVBIT, EV_ABS);
-            ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
-            ioctl(fd, UI_SET_ABSBIT, ABS_MT_PRESSURE);
-            ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
-            break;
-        case DeviceType::STYLUS:
-            ioctl(fd, UI_SET_EVBIT, EV_ABS);
-            ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
-            ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS);
-            ioctl(fd, UI_SET_KEYBIT, BTN_STYLUS2);
-            ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_PEN);
-            ioctl(fd, UI_SET_KEYBIT, BTN_TOOL_RUBBER);
-            ioctl(fd, UI_SET_ABSBIT, ABS_X);
-            ioctl(fd, UI_SET_ABSBIT, ABS_Y);
-            ioctl(fd, UI_SET_ABSBIT, ABS_TILT_X);
-            ioctl(fd, UI_SET_ABSBIT, ABS_TILT_Y);
-            ioctl(fd, UI_SET_ABSBIT, ABS_PRESSURE);
-            ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
-            break;
-        case DeviceType::ROTARY_ENCODER:
-            ioctl(fd, UI_SET_EVBIT, EV_REL);
-            ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
-            if (vd_flags::high_resolution_scroll()) {
-                ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
-            }
-            break;
-        default:
-            ALOGE("Invalid input device type %d", static_cast<int32_t>(deviceType));
-            return invalidFd();
-    }
-
-    int version;
-    if (ioctl(fd, UI_GET_VERSION, &version) == 0 && version >= 5) {
-        uinput_setup setup;
-        memset(&setup, 0, sizeof(setup));
-        strlcpy(setup.name, readableName, UINPUT_MAX_NAME_SIZE);
-        setup.id.version = 1;
-        setup.id.bustype = BUS_VIRTUAL;
-        setup.id.vendor = vendorId;
-        setup.id.product = productId;
-        if (deviceType == DeviceType::TOUCHSCREEN) {
-            uinput_abs_setup xAbsSetup;
-            xAbsSetup.code = ABS_MT_POSITION_X;
-            xAbsSetup.absinfo.maximum = screenWidth - 1;
-            xAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput x axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup yAbsSetup;
-            yAbsSetup.code = ABS_MT_POSITION_Y;
-            yAbsSetup.absinfo.maximum = screenHeight - 1;
-            yAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput y axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup majorAbsSetup;
-            majorAbsSetup.code = ABS_MT_TOUCH_MAJOR;
-            majorAbsSetup.absinfo.maximum = screenWidth - 1;
-            majorAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &majorAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput major axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup pressureAbsSetup;
-            pressureAbsSetup.code = ABS_MT_PRESSURE;
-            pressureAbsSetup.absinfo.maximum = 255;
-            pressureAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup slotAbsSetup;
-            slotAbsSetup.code = ABS_MT_SLOT;
-            slotAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
-            slotAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &slotAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput slots: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup trackingIdAbsSetup;
-            trackingIdAbsSetup.code = ABS_MT_TRACKING_ID;
-            trackingIdAbsSetup.absinfo.maximum = MAX_POINTERS - 1;
-            trackingIdAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &trackingIdAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput tracking ids: %s", strerror(errno));
-                return invalidFd();
-            }
-        } else if (deviceType == DeviceType::STYLUS) {
-            uinput_abs_setup xAbsSetup;
-            xAbsSetup.code = ABS_X;
-            xAbsSetup.absinfo.maximum = screenWidth - 1;
-            xAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &xAbsSetup) != 0) {
-                ALOGE("Error creating stylus uinput x axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup yAbsSetup;
-            yAbsSetup.code = ABS_Y;
-            yAbsSetup.absinfo.maximum = screenHeight - 1;
-            yAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &yAbsSetup) != 0) {
-                ALOGE("Error creating stylus uinput y axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup tiltXAbsSetup;
-            tiltXAbsSetup.code = ABS_TILT_X;
-            tiltXAbsSetup.absinfo.maximum = 90;
-            tiltXAbsSetup.absinfo.minimum = -90;
-            if (ioctl(fd, UI_ABS_SETUP, &tiltXAbsSetup) != 0) {
-                ALOGE("Error creating stylus uinput tilt x axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup tiltYAbsSetup;
-            tiltYAbsSetup.code = ABS_TILT_Y;
-            tiltYAbsSetup.absinfo.maximum = 90;
-            tiltYAbsSetup.absinfo.minimum = -90;
-            if (ioctl(fd, UI_ABS_SETUP, &tiltYAbsSetup) != 0) {
-                ALOGE("Error creating stylus uinput tilt y axis: %s", strerror(errno));
-                return invalidFd();
-            }
-            uinput_abs_setup pressureAbsSetup;
-            pressureAbsSetup.code = ABS_PRESSURE;
-            pressureAbsSetup.absinfo.maximum = 255;
-            pressureAbsSetup.absinfo.minimum = 0;
-            if (ioctl(fd, UI_ABS_SETUP, &pressureAbsSetup) != 0) {
-                ALOGE("Error creating touchscreen uinput pressure axis: %s", strerror(errno));
-                return invalidFd();
-            }
-        }
-        if (ioctl(fd, UI_DEV_SETUP, &setup) != 0) {
-            ALOGE("Error creating uinput device: %s", strerror(errno));
-            return invalidFd();
-        }
-    } else {
-        // UI_DEV_SETUP was not introduced until version 5. Try setting up manually.
-        ALOGI("Falling back to version %d manual setup", version);
-        uinput_user_dev fallback;
-        memset(&fallback, 0, sizeof(fallback));
-        strlcpy(fallback.name, readableName, UINPUT_MAX_NAME_SIZE);
-        fallback.id.version = 1;
-        fallback.id.bustype = BUS_VIRTUAL;
-        fallback.id.vendor = vendorId;
-        fallback.id.product = productId;
-        if (deviceType == DeviceType::TOUCHSCREEN) {
-            fallback.absmin[ABS_MT_POSITION_X] = 0;
-            fallback.absmax[ABS_MT_POSITION_X] = screenWidth - 1;
-            fallback.absmin[ABS_MT_POSITION_Y] = 0;
-            fallback.absmax[ABS_MT_POSITION_Y] = screenHeight - 1;
-            fallback.absmin[ABS_MT_TOUCH_MAJOR] = 0;
-            fallback.absmax[ABS_MT_TOUCH_MAJOR] = screenWidth - 1;
-            fallback.absmin[ABS_MT_PRESSURE] = 0;
-            fallback.absmax[ABS_MT_PRESSURE] = 255;
-        } else if (deviceType == DeviceType::STYLUS) {
-            fallback.absmin[ABS_X] = 0;
-            fallback.absmax[ABS_X] = screenWidth - 1;
-            fallback.absmin[ABS_Y] = 0;
-            fallback.absmax[ABS_Y] = screenHeight - 1;
-            fallback.absmin[ABS_TILT_X] = -90;
-            fallback.absmax[ABS_TILT_X] = 90;
-            fallback.absmin[ABS_TILT_Y] = -90;
-            fallback.absmax[ABS_TILT_Y] = 90;
-            fallback.absmin[ABS_PRESSURE] = 0;
-            fallback.absmax[ABS_PRESSURE] = 255;
-        }
-        if (TEMP_FAILURE_RETRY(write(fd, &fallback, sizeof(fallback))) != sizeof(fallback)) {
-            ALOGE("Error creating uinput device: %s", strerror(errno));
-            return invalidFd();
-        }
-    }
-
-    if (ioctl(fd, UI_DEV_CREATE) != 0) {
-        ALOGE("Error creating uinput device: %s", strerror(errno));
-        return invalidFd();
-    }
-
-    return fd;
-}
-
 static unique_fd openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
-                               jstring phys, DeviceType deviceType, int screenHeight,
-                               int screenWidth) {
+                               jstring phys, DeviceType deviceType, jint screenHeight,
+                               jint screenWidth) {
     ScopedUtfChars readableName(env, name);
     ScopedUtfChars readablePhys(env, phys);
     return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4d6a90c..67346ab 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -44,6 +44,7 @@
 #include <batteryservice/include/batteryservice/BatteryServiceConstants.h>
 #include <binder/IServiceManager.h>
 #include <com_android_input_flags.h>
+#include <include/gestures.h>
 #include <input/Input.h>
 #include <input/PointerController.h>
 #include <input/PrintTools.h>
@@ -105,6 +106,7 @@
 static struct {
     jclass clazz;
     jmethodID notifyInputDevicesChanged;
+    jmethodID notifyTouchpadHardwareState;
     jmethodID notifySwitch;
     jmethodID notifyInputChannelBroken;
     jmethodID notifyNoFocusedWindowAnr;
@@ -144,6 +146,34 @@
 
 static struct {
     jclass clazz;
+    // fields
+    jfieldID timestamp;
+    jfieldID buttonsDown;
+    jfieldID fingerCount;
+    jfieldID touchCount;
+    jfieldID fingerStates;
+    // methods
+    jmethodID init;
+} gTouchpadHardwareStateClassInfo;
+
+static struct {
+    jclass clazz;
+    // fields
+    jfieldID touchMajor;
+    jfieldID touchMinor;
+    jfieldID widthMajor;
+    jfieldID widthMinor;
+    jfieldID pressure;
+    jfieldID orientation;
+    jfieldID positionX;
+    jfieldID positionY;
+    jfieldID trackingId;
+    // methods
+    jmethodID init;
+} gTouchpadFingerStateClassInfo;
+
+static struct {
+    jclass clazz;
     jfieldID mPtr;
 } gNativeInputManagerServiceImpl;
 
@@ -217,6 +247,23 @@
     jmethodID init;
 } gInputSensorInfo;
 
+static struct TouchpadHardwarePropertiesOffsets {
+    jclass clazz;
+    jmethodID constructor;
+    jfieldID left;
+    jfieldID top;
+    jfieldID right;
+    jfieldID bottom;
+    jfieldID resX;
+    jfieldID resY;
+    jfieldID orientationMinimum;
+    jfieldID orientationMaximum;
+    jfieldID maxFingerCount;
+    jfieldID isButtonPad;
+    jfieldID isHapticPad;
+    jfieldID reportsPressure;
+} gTouchpadHardwarePropertiesOffsets;
+
 // --- Global functions ---
 
 template<typename T>
@@ -291,6 +338,7 @@
     void setTouchpadNaturalScrollingEnabled(bool enabled);
     void setTouchpadTapToClickEnabled(bool enabled);
     void setTouchpadTapDraggingEnabled(bool enabled);
+    void setShouldNotifyTouchpadHardwareState(bool enabled);
     void setTouchpadRightClickZoneEnabled(bool enabled);
     void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
     void setShowTouches(bool enabled);
@@ -313,6 +361,8 @@
 
     void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
     void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
+    void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+                                     int32_t deviceId) override;
     std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
             const InputDeviceIdentifier& identifier,
             const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) override;
@@ -440,6 +490,9 @@
         // True to enable tap dragging on touchpads.
         bool touchpadTapDraggingEnabled{false};
 
+        // True if hardware state update notifications should be sent to the policy.
+        bool shouldNotifyTouchpadHardwareState{false};
+
         // True to enable a zone on the right-hand side of touchpads where clicks will be turned
         // into context (a.k.a. "right") clicks.
         bool touchpadRightClickZoneEnabled{false};
@@ -698,6 +751,7 @@
         outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled;
         outConfig->touchpadTapToClickEnabled = mLocked.touchpadTapToClickEnabled;
         outConfig->touchpadTapDraggingEnabled = mLocked.touchpadTapDraggingEnabled;
+        outConfig->shouldNotifyTouchpadHardwareState = mLocked.shouldNotifyTouchpadHardwareState;
         outConfig->touchpadRightClickZoneEnabled = mLocked.touchpadRightClickZoneEnabled;
 
         outConfig->disabledDevices = mLocked.disabledInputDevices;
@@ -861,6 +915,87 @@
     checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged");
 }
 
+static ScopedLocalRef<jobject> createTouchpadHardwareStateObj(
+        JNIEnv* env, const SelfContainedHardwareState& schs) {
+    ScopedLocalRef<jobject>
+            touchpadHardwareStateObj(env,
+                                     env->NewObject(gTouchpadHardwareStateClassInfo.clazz,
+                                                    gTouchpadHardwareStateClassInfo.init, ""));
+
+    if (!touchpadHardwareStateObj.get()) {
+        return ScopedLocalRef<jobject>(env);
+    }
+
+    env->SetFloatField(touchpadHardwareStateObj.get(), gTouchpadHardwareStateClassInfo.timestamp,
+                       static_cast<jfloat>(schs.state.timestamp));
+    env->SetIntField(touchpadHardwareStateObj.get(), gTouchpadHardwareStateClassInfo.buttonsDown,
+                     static_cast<jint>(schs.state.buttons_down));
+    env->SetIntField(touchpadHardwareStateObj.get(), gTouchpadHardwareStateClassInfo.fingerCount,
+                     static_cast<jint>(schs.state.finger_cnt));
+    env->SetIntField(touchpadHardwareStateObj.get(), gTouchpadHardwareStateClassInfo.touchCount,
+                     static_cast<jint>(schs.state.touch_cnt));
+
+    size_t count = schs.fingers.size();
+    ScopedLocalRef<jobjectArray>
+            fingerStateObjArray(env,
+                                env->NewObjectArray(count, gTouchpadFingerStateClassInfo.clazz,
+                                                    nullptr));
+
+    if (!fingerStateObjArray.get()) {
+        return ScopedLocalRef<jobject>(env);
+    }
+
+    for (size_t i = 0; i < count; i++) {
+        ScopedLocalRef<jobject> fingerStateObj(env,
+                                               env->NewObject(gTouchpadFingerStateClassInfo.clazz,
+                                                              gTouchpadFingerStateClassInfo.init,
+                                                              ""));
+        if (!fingerStateObj.get()) {
+            return ScopedLocalRef<jobject>(env);
+        }
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.touchMajor,
+                           static_cast<jfloat>(schs.fingers[i].touch_major));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.touchMinor,
+                           static_cast<jfloat>(schs.fingers[i].touch_minor));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.widthMajor,
+                           static_cast<jfloat>(schs.fingers[i].width_major));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.widthMinor,
+                           static_cast<jfloat>(schs.fingers[i].width_minor));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.pressure,
+                           static_cast<jfloat>(schs.fingers[i].pressure));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.orientation,
+                           static_cast<jfloat>(schs.fingers[i].orientation));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.positionX,
+                           static_cast<jfloat>(schs.fingers[i].position_x));
+        env->SetFloatField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.positionY,
+                           static_cast<jfloat>(schs.fingers[i].position_y));
+        env->SetIntField(fingerStateObj.get(), gTouchpadFingerStateClassInfo.trackingId,
+                         static_cast<jint>(schs.fingers[i].tracking_id));
+
+        env->SetObjectArrayElement(fingerStateObjArray.get(), i, fingerStateObj.get());
+    }
+
+    env->SetObjectField(touchpadHardwareStateObj.get(),
+                        gTouchpadHardwareStateClassInfo.fingerStates, fingerStateObjArray.get());
+
+    return touchpadHardwareStateObj;
+}
+
+void NativeInputManager::notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
+                                                     int32_t deviceId) {
+    ATRACE_CALL();
+    JNIEnv* env = jniEnv();
+
+    ScopedLocalRef<jobject> hardwareStateObj = createTouchpadHardwareStateObj(env, schs);
+
+    if (hardwareStateObj.get()) {
+        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyTouchpadHardwareState,
+                            hardwareStateObj.get(), deviceId);
+    }
+
+    checkAndClearExceptionFromCallback(env, "notifyTouchpadHardwareState");
+}
+
 std::shared_ptr<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
         const InputDeviceIdentifier& identifier,
         const std::optional<KeyboardLayoutInfo> keyboardLayoutInfo) {
@@ -1260,6 +1395,22 @@
             InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
 }
 
+void NativeInputManager::setShouldNotifyTouchpadHardwareState(bool enabled) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        if (mLocked.shouldNotifyTouchpadHardwareState == enabled) {
+            return;
+        }
+
+        ALOGI("Should touchpad hardware state be notified: %s.", toString(enabled));
+        mLocked.shouldNotifyTouchpadHardwareState = enabled;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+}
+
 void NativeInputManager::setTouchpadRightClickZoneEnabled(bool enabled) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -2144,6 +2295,13 @@
     im->setTouchpadTapDraggingEnabled(enabled);
 }
 
+static void nativeSetShouldNotifyTouchpadHardwareState(JNIEnv* env, jobject nativeImplObj,
+                                                       jboolean enabled) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+    im->setShouldNotifyTouchpadHardwareState(enabled);
+}
+
 static void nativeSetTouchpadRightClickZoneEnabled(JNIEnv* env, jobject nativeImplObj,
                                                    jboolean enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2604,6 +2762,45 @@
     return arr;
 }
 
+static jobject nativeGetTouchpadHardwareProperties(JNIEnv* env, jobject nativeImplObj,
+                                                   jint deviceId) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    std::optional<HardwareProperties> touchpadHardwareProperties =
+            im->getInputManager()->getReader().getTouchpadHardwareProperties(deviceId);
+
+    jobject hwPropsObj = env->NewObject(gTouchpadHardwarePropertiesOffsets.clazz,
+                                        gTouchpadHardwarePropertiesOffsets.constructor);
+    if (hwPropsObj == NULL || !touchpadHardwareProperties.has_value()) {
+        return hwPropsObj;
+    }
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.left,
+                       touchpadHardwareProperties->left);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.top,
+                       touchpadHardwareProperties->top);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.right,
+                       touchpadHardwareProperties->right);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.bottom,
+                       touchpadHardwareProperties->bottom);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.resX,
+                       touchpadHardwareProperties->res_x);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.resY,
+                       touchpadHardwareProperties->res_y);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.orientationMinimum,
+                       touchpadHardwareProperties->orientation_minimum);
+    env->SetFloatField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.orientationMaximum,
+                       touchpadHardwareProperties->orientation_maximum);
+    env->SetIntField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.maxFingerCount,
+                     touchpadHardwareProperties->max_finger_cnt);
+    env->SetBooleanField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.isButtonPad,
+                         touchpadHardwareProperties->is_button_pad);
+    env->SetBooleanField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.isHapticPad,
+                         touchpadHardwareProperties->is_haptic_pad);
+    env->SetBooleanField(hwPropsObj, gTouchpadHardwarePropertiesOffsets.reportsPressure,
+                         touchpadHardwareProperties->reports_pressure);
+
+    return hwPropsObj;
+}
+
 static jboolean nativeEnableSensor(JNIEnv* env, jobject nativeImplObj, jint deviceId,
                                    jint sensorType, jint samplingPeriodUs,
                                    jint maxBatchReportLatencyUs) {
@@ -2762,6 +2959,8 @@
          (void*)nativeSetTouchpadNaturalScrollingEnabled},
         {"setTouchpadTapToClickEnabled", "(Z)V", (void*)nativeSetTouchpadTapToClickEnabled},
         {"setTouchpadTapDraggingEnabled", "(Z)V", (void*)nativeSetTouchpadTapDraggingEnabled},
+        {"setShouldNotifyTouchpadHardwareState", "(Z)V",
+         (void*)nativeSetShouldNotifyTouchpadHardwareState},
         {"setTouchpadRightClickZoneEnabled", "(Z)V", (void*)nativeSetTouchpadRightClickZoneEnabled},
         {"setShowTouches", "(Z)V", (void*)nativeSetShowTouches},
         {"setInteractive", "(Z)V", (void*)nativeSetInteractive},
@@ -2801,6 +3000,9 @@
         {"setKeyRepeatConfiguration", "(II)V", (void*)nativeSetKeyRepeatConfiguration},
         {"getSensorList", "(I)[Landroid/hardware/input/InputSensorInfo;",
          (void*)nativeGetSensorList},
+        {"getTouchpadHardwareProperties",
+         "(I)Lcom/android/server/input/TouchpadHardwareProperties;",
+         (void*)nativeGetTouchpadHardwareProperties},
         {"enableSensor", "(IIII)Z", (void*)nativeEnableSensor},
         {"disableSensor", "(II)V", (void*)nativeDisableSensor},
         {"flushSensor", "(II)Z", (void*)nativeFlushSensor},
@@ -2862,6 +3064,10 @@
     GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz,
             "notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyTouchpadHardwareState, clazz,
+                  "notifyTouchpadHardwareState",
+                  "(Lcom/android/server/input/TouchpadHardwareState;I)V")
+
     GET_METHOD_ID(gServiceClassInfo.notifySwitch, clazz,
             "notifySwitch", "(JII)V");
 
@@ -3060,6 +3266,92 @@
 
     GET_METHOD_ID(gInputSensorInfo.init, gInputSensorInfo.clazz, "<init>", "()V");
 
+    // TouchpadHardwareState
+
+    FIND_CLASS(gTouchpadHardwareStateClassInfo.clazz,
+               "com/android/server/input/TouchpadHardwareState");
+    gTouchpadHardwareStateClassInfo.clazz =
+            reinterpret_cast<jclass>(env->NewGlobalRef(gTouchpadHardwareStateClassInfo.clazz));
+
+    GET_FIELD_ID(gTouchpadHardwareStateClassInfo.touchCount, gTouchpadHardwareStateClassInfo.clazz,
+                 "mTouchCount", "I");
+    GET_FIELD_ID(gTouchpadHardwareStateClassInfo.fingerCount, gTouchpadHardwareStateClassInfo.clazz,
+                 "mFingerCount", "I");
+    GET_FIELD_ID(gTouchpadHardwareStateClassInfo.buttonsDown, gTouchpadHardwareStateClassInfo.clazz,
+                 "mButtonsDown", "I");
+    GET_FIELD_ID(gTouchpadHardwareStateClassInfo.timestamp, gTouchpadHardwareStateClassInfo.clazz,
+                 "mTimestamp", "F");
+    GET_FIELD_ID(gTouchpadHardwareStateClassInfo.fingerStates,
+                 gTouchpadHardwareStateClassInfo.clazz, "mFingerStates",
+                 "[Lcom/android/server/input/TouchpadFingerState;");
+
+    GET_METHOD_ID(gTouchpadHardwareStateClassInfo.init, gTouchpadHardwareStateClassInfo.clazz,
+                  "<init>", "()V");
+
+    // TouchpadFingerState
+
+    FIND_CLASS(gTouchpadFingerStateClassInfo.clazz, "com/android/server/input/TouchpadFingerState");
+    gTouchpadFingerStateClassInfo.clazz =
+            reinterpret_cast<jclass>(env->NewGlobalRef(gTouchpadFingerStateClassInfo.clazz));
+
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.touchMajor, gTouchpadFingerStateClassInfo.clazz,
+                 "mTouchMajor", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.touchMinor, gTouchpadFingerStateClassInfo.clazz,
+                 "mTouchMinor", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.widthMajor, gTouchpadFingerStateClassInfo.clazz,
+                 "mWidthMajor", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.widthMinor, gTouchpadFingerStateClassInfo.clazz,
+                 "mWidthMinor", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.pressure, gTouchpadFingerStateClassInfo.clazz,
+                 "mPressure", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.orientation, gTouchpadFingerStateClassInfo.clazz,
+                 "mOrientation", "F")
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.positionX, gTouchpadFingerStateClassInfo.clazz,
+                 "mPositionX", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.positionY, gTouchpadFingerStateClassInfo.clazz,
+                 "mPositionY", "F");
+    GET_FIELD_ID(gTouchpadFingerStateClassInfo.trackingId, gTouchpadFingerStateClassInfo.clazz,
+                 "mTrackingId", "I");
+
+    GET_METHOD_ID(gTouchpadFingerStateClassInfo.init, gTouchpadFingerStateClassInfo.clazz, "<init>",
+                  "()V");
+
+    // TouchpadHardawreProperties
+    FIND_CLASS(gTouchpadHardwarePropertiesOffsets.clazz,
+               "com/android/server/input/TouchpadHardwareProperties");
+    gTouchpadHardwarePropertiesOffsets.clazz =
+            reinterpret_cast<jclass>(env->NewGlobalRef(gTouchpadHardwarePropertiesOffsets.clazz));
+
+    // Get the constructor ID
+    GET_METHOD_ID(gTouchpadHardwarePropertiesOffsets.constructor,
+                  gTouchpadHardwarePropertiesOffsets.clazz, "<init>", "()V");
+
+    // Get the field IDs
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.left, gTouchpadHardwarePropertiesOffsets.clazz,
+                 "mLeft", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.top, gTouchpadHardwarePropertiesOffsets.clazz,
+                 "mTop", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.right, gTouchpadHardwarePropertiesOffsets.clazz,
+                 "mRight", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.bottom,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mBottom", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.resX, gTouchpadHardwarePropertiesOffsets.clazz,
+                 "mResX", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.resY, gTouchpadHardwarePropertiesOffsets.clazz,
+                 "mResY", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.orientationMinimum,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mOrientationMinimum", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.orientationMaximum,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mOrientationMaximum", "F");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.maxFingerCount,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mMaxFingerCount", "S");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.isButtonPad,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mIsButtonPad", "Z");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.isHapticPad,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mIsHapticPad", "Z");
+    GET_FIELD_ID(gTouchpadHardwarePropertiesOffsets.reportsPressure,
+                 gTouchpadHardwarePropertiesOffsets.clazz, "mReportsPressure", "Z");
+
     return 0;
 }
 
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index f12930a..5c5ac28 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -198,7 +198,8 @@
 }
 
 static Aidl::VendorEffect vendorEffectFromJavaParcel(JNIEnv* env, jobject vendorData,
-                                                     jlong strength, jfloat scale) {
+                                                     jlong strength, jfloat scale,
+                                                     jfloat adaptiveScale) {
     PersistableBundle bundle;
     if (AParcel* parcel = AParcel_fromJavaParcel(env, vendorData); parcel != nullptr) {
         if (binder_status_t status = bundle.readFromParcel(parcel); status == STATUS_OK) {
@@ -217,6 +218,7 @@
     effect.vendorData = bundle;
     effect.strength = static_cast<Aidl::EffectStrength>(strength);
     effect.scale = static_cast<float>(scale);
+    effect.vendorScale = static_cast<float>(adaptiveScale);
     return effect;
 }
 
@@ -319,13 +321,14 @@
 
 static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
                                          jobject vendorData, jlong strength, jfloat scale,
-                                         jlong vibrationId) {
+                                         jfloat adaptiveScale, jlong vibrationId) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper == nullptr) {
         ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
         return -1;
     }
-    Aidl::VendorEffect effect = vendorEffectFromJavaParcel(env, vendorData, strength, scale);
+    Aidl::VendorEffect effect =
+            vendorEffectFromJavaParcel(env, vendorData, strength, scale, adaptiveScale);
     auto callback = wrapper->createCallback(vibrationId);
     auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
         return hal->performVendorEffect(effect, callback);
@@ -511,7 +514,7 @@
         {"off", "(J)V", (void*)vibratorOff},
         {"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
         {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
-        {"performVendorEffect", "(JLandroid/os/Parcel;JFJ)J", (void*)vibratorPerformVendorEffect},
+        {"performVendorEffect", "(JLandroid/os/Parcel;JFFJ)J", (void*)vibratorPerformVendorEffect},
         {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
          (void*)vibratorPerformComposedEffect},
         {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
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 4231149..0eafb59 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -42,7 +42,7 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
-                <xs:element type="thermalThrottling" name="thermalThrottling">
+                <xs:element type="thermalThrottling" name="thermalThrottling" minOccurs="0" maxOccurs="1">
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
@@ -464,7 +464,15 @@
             <xs:annotation name="nonnull"/>
             <xs:annotation name="final"/>
         </xs:element>
-        <xs:element name="pollingWindowMillis" type="xs:nonNegativeInteger">
+        <xs:element name="customAnimationRateSec" type="nonNegativeDecimal" minOccurs="0" maxOccurs="1">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="pollingWindowMaxMillis" type="xs:nonNegativeInteger">
+            <xs:annotation name="nonnull"/>
+            <xs:annotation name="final"/>
+        </xs:element>
+        <xs:element name="pollingWindowMinMillis" type="xs:nonNegativeInteger">
             <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 cec2787..355b0ab 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -345,10 +345,14 @@
   public class PowerThrottlingConfig {
     ctor public PowerThrottlingConfig();
     method @NonNull public final java.math.BigDecimal getBrightnessLowestCapAllowed();
-    method @NonNull public final java.math.BigInteger getPollingWindowMillis();
+    method @NonNull public final java.math.BigDecimal getCustomAnimationRateSec();
+    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 setPollingWindowMillis(@NonNull java.math.BigInteger);
+    method public final void setCustomAnimationRateSec(@NonNull java.math.BigDecimal);
+    method public final void setPollingWindowMaxMillis(@NonNull java.math.BigInteger);
+    method public final void setPollingWindowMinMillis(@NonNull java.math.BigInteger);
   }
 
   public class PowerThrottlingMap {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 6a0dd5a..b982098 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -1325,31 +1325,6 @@
         pw.print("encryptionRequested=");
         pw.println(encryptionRequested);
 
-        if (!Flags.dumpsysPolicyEngineMigrationEnabled()) {
-            pw.print("disableCamera=");
-            pw.println(disableCamera);
-
-            pw.print("disableScreenCapture=");
-            pw.println(disableScreenCapture);
-
-            pw.print("requireAutoTime=");
-            pw.println(requireAutoTime);
-
-            if (permittedInputMethods != null) {
-                pw.print("permittedInputMethods=");
-                pw.println(permittedInputMethods);
-            }
-
-            pw.println("userRestrictions:");
-            UserRestrictionsUtils.dumpRestrictions(pw, "  ", userRestrictions);
-        }
-
-        if (!Flags.policyEngineMigrationV2Enabled()
-                || !Flags.dumpsysPolicyEngineMigrationEnabled()) {
-            pw.print("mUsbDataSignaling=");
-            pw.println(mUsbDataSignalingEnabled);
-        }
-
         pw.print("disableCallerId=");
         pw.println(disableCallerId);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index a08af72..4beb6a8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -230,11 +230,9 @@
 
         synchronized (mLock) {
             PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
-                        policyDefinition, userId)) {
-                    return;
-                }
+            if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
+                    policyDefinition, userId)) {
+                return;
             }
 
             if (policyDefinition.isNonCoexistablePolicy()) {
@@ -354,9 +352,7 @@
             }
             PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
 
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
-            }
+            decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
 
             if (policyDefinition.isNonCoexistablePolicy()) {
                 setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState,
@@ -500,11 +496,9 @@
 
         synchronized (mLock) {
             PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
-                        policyDefinition, UserHandle.USER_ALL)) {
-                    return;
-                }
+            if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
+                    policyDefinition, UserHandle.USER_ALL)) {
+                return;
             }
             // TODO(b/270999567): Move error handling for DISALLOW_CELLULAR_2G into the code
             //  that honors the restriction once there's an API available
@@ -571,9 +565,7 @@
         synchronized (mLock) {
             PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
 
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                decreasePolicySizeForAdmin(policyState, enforcingAdmin);
-            }
+            decreasePolicySizeForAdmin(policyState, enforcingAdmin);
 
             boolean policyChanged = policyState.removePolicy(enforcingAdmin);
 
@@ -1739,25 +1731,23 @@
                 pw.println();
             }
             pw.decreaseIndent();
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                pw.println();
+            pw.println();
 
-                pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
-                pw.println("Current admin policy size limit: " + mPolicySizeLimit);
-                pw.println("Admin Policies size: ");
-                for (int i = 0; i < mAdminPolicySize.size(); i++) {
-                    int userId = mAdminPolicySize.keyAt(i);
-                    pw.printf("User %d:\n", userId);
-                    pw.increaseIndent();
-                    for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
-                        pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
-                                admin));
-                        pw.println();
-                    }
-                    pw.decreaseIndent();
+            pw.println("Default admin policy size limit: " + DEFAULT_POLICY_SIZE_LIMIT);
+            pw.println("Current admin policy size limit: " + mPolicySizeLimit);
+            pw.println("Admin Policies size: ");
+            for (int i = 0; i < mAdminPolicySize.size(); i++) {
+                int userId = mAdminPolicySize.keyAt(i);
+                pw.printf("User %d:\n", userId);
+                pw.increaseIndent();
+                for (EnforcingAdmin admin : mAdminPolicySize.get(userId).keySet()) {
+                    pw.printf("Admin : " + admin + " : " + mAdminPolicySize.get(userId).get(
+                            admin));
+                    pw.println();
                 }
                 pw.decreaseIndent();
             }
+            pw.decreaseIndent();
         }
     }
 
@@ -2018,23 +2008,21 @@
 
         private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
                 throws IOException {
-            if (Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                if (mAdminPolicySize != null) {
-                    for (int i = 0; i < mAdminPolicySize.size(); i++) {
-                        int userId = mAdminPolicySize.keyAt(i);
-                        for (EnforcingAdmin admin : mAdminPolicySize.get(
-                                userId).keySet()) {
-                            serializer.startTag(/* namespace= */ null,
-                                    TAG_ENFORCING_ADMIN_AND_SIZE);
-                            serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
-                            admin.saveToXml(serializer);
-                            serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
-                            serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
-                            serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
-                                    mAdminPolicySize.get(userId).get(admin));
-                            serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
-                            serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
-                        }
+            if (mAdminPolicySize != null) {
+                for (int i = 0; i < mAdminPolicySize.size(); i++) {
+                    int userId = mAdminPolicySize.keyAt(i);
+                    for (EnforcingAdmin admin : mAdminPolicySize.get(
+                            userId).keySet()) {
+                        serializer.startTag(/* namespace= */ null,
+                                TAG_ENFORCING_ADMIN_AND_SIZE);
+                        serializer.startTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+                        admin.saveToXml(serializer);
+                        serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN);
+                        serializer.startTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+                        serializer.attributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE,
+                                mAdminPolicySize.get(userId).get(admin));
+                        serializer.endTag(/* namespace= */ null, TAG_POLICY_SUM_SIZE);
+                        serializer.endTag(/* namespace= */ null, TAG_ENFORCING_ADMIN_AND_SIZE);
                     }
                 }
             }
@@ -2042,9 +2030,6 @@
 
         private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
                 throws IOException {
-            if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                return;
-            }
             serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
             serializer.attributeInt(
                     /* namespace= */ null, ATTR_POLICY_SUM_SIZE, mPolicySizeLimit);
@@ -2192,9 +2177,6 @@
 
         private void readMaxPolicySizeInner(TypedXmlPullParser parser)
                 throws XmlPullParserException, IOException {
-            if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                return;
-            }
             mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
         }
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9ed645b..85e24c15 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -871,6 +871,16 @@
                 EXEMPT_FROM_POWER_RESTRICTIONS, OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS);
     }
 
+    private static final Set<String> METERED_DATA_RESTRICTION_EXEMPT_ROLES =
+            new ArraySet<>();
+    static {
+        // TODO(b/362545319): reference role name from role manager once it's exposed.
+        final String roleDeviceLockController =
+                "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+        METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(roleDeviceLockController);
+        METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(RoleManager.ROLE_FINANCED_DEVICE_KIOSK);
+    }
+
     /**
      * Admin apps targeting Android S+ may not use
      * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -1318,9 +1328,7 @@
                 Bundle prevRestrictions) {
             resetCrossProfileIntentFiltersIfNeeded(userId, newRestrictions, prevRestrictions);
             resetUserVpnIfNeeded(userId, newRestrictions, prevRestrictions);
-            if (Flags.deletePrivateSpaceUnderRestriction()) {
-                removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
-            }
+            removePrivateSpaceIfRestrictionIsSet(userId, newRestrictions, prevRestrictions);
         }
 
         private void resetUserVpnIfNeeded(
@@ -1959,6 +1967,10 @@
             return UserManager.isHeadlessSystemUserMode();
         }
 
+        List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+            return getRoleManager().getRoleHoldersAsUser(role, userHandle);
+        }
+
         @SuppressWarnings("AndroidFrameworkPendingIntentMutability")
         PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
                 @NonNull Intent intent, int flags, Bundle options, UserHandle user) {
@@ -2726,22 +2738,14 @@
             return;
         }
 
-        if (Flags.securityLogV2Enabled()) {
-            boolean auditLoggingEnabled = Boolean.TRUE.equals(
-                    mDevicePolicyEngine.getResolvedPolicy(
-                            PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
-            boolean securityLoggingEnabled = Boolean.TRUE.equals(
-                    mDevicePolicyEngine.getResolvedPolicy(
-                            PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
-            setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
-            mInjector.runCryptoSelfTest();
-        } else {
-            synchronized (getLockObject()) {
-                mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
-                mInjector.runCryptoSelfTest();
-                maybePauseDeviceWideLoggingLocked();
-            }
-        }
+        boolean auditLoggingEnabled = Boolean.TRUE.equals(
+                mDevicePolicyEngine.getResolvedPolicy(
+                        PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
+        boolean securityLoggingEnabled = Boolean.TRUE.equals(
+                mDevicePolicyEngine.getResolvedPolicy(
+                        PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
+        setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+        mInjector.runCryptoSelfTest();
     }
 
     /**
@@ -3399,7 +3403,7 @@
 
     @GuardedBy("getLockObject()")
     private void maybeMigrateSecurityLoggingPolicyLocked() {
-        if (!Flags.securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
+        if (mOwners.isSecurityLoggingMigrated()) {
             return;
         }
 
@@ -3689,9 +3693,6 @@
             }
 
             revertTransferOwnershipIfNecessaryLocked();
-            if (!Flags.policyEngineMigrationV2Enabled()) {
-                updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
-            }
         }
 
         // Check whether work apps were paused via suspension and unsuspend if necessary.
@@ -7150,9 +7151,7 @@
 
         // If there is a profile owner, redirect to that; otherwise query the device owner.
         ComponentName aliasChooser = getProfileOwnerAsUser(caller.getUserId());
-        boolean isDoUser = Flags.headlessSingleUserFixes()
-                ? caller.getUserId() == getDeviceOwnerUserId()
-                : caller.getUserHandle().isSystem();
+        boolean isDoUser = caller.getUserId() == getDeviceOwnerUserId();
         if (aliasChooser == null && isDoUser) {
             synchronized (getLockObject()) {
                 final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
@@ -8162,7 +8161,7 @@
             // First check whether the admin is allowed to wipe the device/user/profile.
             final String restriction;
             boolean shouldFactoryReset = userId == UserHandle.USER_SYSTEM;
-            if (Flags.headlessSingleUserFixes() && getHeadlessDeviceOwnerModeForDeviceOwner()
+            if (getHeadlessDeviceOwnerModeForDeviceOwner()
                     == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
                 shouldFactoryReset = userId == getMainUserId();
             }
@@ -8186,8 +8185,7 @@
                 adminPackage,
                 userId)) {
             // Legacy mode
-            wipeDevice = Flags.headlessSingleUserFixes()
-                    && getHeadlessDeviceOwnerModeForDeviceOwner()
+            wipeDevice = getHeadlessDeviceOwnerModeForDeviceOwner()
                     == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER ? isMainUser : isSystemUser;
         } else {
             // Explicit behaviour
@@ -9371,8 +9369,7 @@
 
     void sendDeviceOwnerOrProfileOwnerCommand(String action, Bundle extras, int userId) {
         if (userId == UserHandle.USER_ALL) {
-            if (Flags.headlessDeviceOwnerDelegateSecurityLoggingBugFix()
-                    && getHeadlessDeviceOwnerModeForDeviceOwner()
+            if (getHeadlessDeviceOwnerModeForDeviceOwner()
                     == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
                 userId = mOwners.getDeviceOwnerUserId();
             } else {
@@ -11487,10 +11484,8 @@
                 pw.println();
                 mStatLogger.dump(pw);
                 pw.println();
-                if (Flags.dumpsysPolicyEngineMigrationEnabled()) {
-                    mDevicePolicyEngine.dump(pw);
-                    pw.println();
-                }
+                mDevicePolicyEngine.dump(pw);
+                pw.println();
                 pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
                 pw.println("Logout user: " + getLogoutUserIdUnchecked());
                 pw.println();
@@ -11860,7 +11855,7 @@
             }
             setBackwardsCompatibleAppRestrictions(
                     caller, packageName, restrictions, caller.getUserHandle());
-        } else if (Flags.dmrhSetAppRestrictions()) {
+        } else {
             final boolean isRoleHolder;
             if (who != null) {
                 // DO or PO
@@ -11907,15 +11902,6 @@
                             caller.getUserHandle());
                 });
             }
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller,
-                    DELEGATION_APP_RESTRICTIONS)));
-            mInjector.binderWithCleanCallingIdentity(() -> {
-                mUserManager.setApplicationRestrictions(packageName, restrictions,
-                        caller.getUserHandle());
-            });
         }
 
         DevicePolicyEventLogger
@@ -12215,34 +12201,30 @@
      * permittedList or are a system app.
      */
     private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
-            List<String> permittedList, int userIdToCheck) {
+            List<String> permittedList, int userId) {
         long id = mInjector.binderClearCallingIdentity();
         try {
-            // If we have an enabled packages list for a managed profile the packages
-            // we should check are installed for the parent user.
-            UserInfo user = getUserInfo(userIdToCheck);
-            if (user.isManagedProfile()) {
-                userIdToCheck = user.profileGroupId;
-            }
-
             for (String enabledPackage : enabledPackages) {
-                boolean systemService = false;
+                if (permittedList.contains(enabledPackage)) {
+                    continue;
+                }
                 try {
                     ApplicationInfo applicationInfo = mIPackageManager.getApplicationInfo(
-                            enabledPackage, PackageManager.MATCH_UNINSTALLED_PACKAGES,
-                            userIdToCheck);
+                            enabledPackage, PackageManager.MATCH_ANY_USER, userId);
 
                     if (applicationInfo == null) {
+                        Slogf.wtf(LOG_TAG, "Can't find ApplicationInfo for %s", enabledPackage);
                         return false;
                     }
 
-                    systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+                    if (!applicationInfo.isSystemApp()) {
+                        Slogf.w(LOG_TAG,
+                                "Enabled package neither permitted nor system: %s", enabledPackage);
+                        return false;
+                    }
                 } catch (RemoteException e) {
                     Slogf.i(LOG_TAG, "Can't talk to package managed", e);
                 }
-                if (!systemService && !permittedList.contains(enabledPackage)) {
-                    return false;
-                }
             }
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -12452,12 +12434,6 @@
         }
 
         if (packageList != null) {
-            if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-                for (String pkg : packageList) {
-                    PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
-                }
-            }
-
             List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
                     InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
             if (enabledImes != null) {
@@ -12694,14 +12670,12 @@
         Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
 
-        if (Flags.headlessDeviceOwnerSingleUserEnabled()) {
-            // Block this method if the device is in headless main user mode
-            Preconditions.checkCallAuthorization(
-                    !mInjector.userManagerIsHeadlessSystemUserMode()
-                            || getHeadlessDeviceOwnerModeForDeviceOwner()
-                            != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER,
-                    "createAndManageUser was called while in headless single user mode");
-        }
+        // Block this method if the device is in headless main user mode
+        Preconditions.checkCallAuthorization(
+                !mInjector.userManagerIsHeadlessSystemUserMode()
+                        || getHeadlessDeviceOwnerModeForDeviceOwner()
+                        != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER,
+                "createAndManageUser was called while in headless single user mode");
 
         // Only allow the system user to use this method
         Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
@@ -13258,7 +13232,7 @@
                 return Bundle.EMPTY;
             }
             return policies.get(enforcingAdmin).getValue();
-        } else if (Flags.dmrhSetAppRestrictions()) {
+        } else {
             final boolean isRoleHolder;
             if (who != null) {
                 // Caller is DO or PO. They cannot call this on parent
@@ -13301,19 +13275,6 @@
                     return bundle != null ? bundle : Bundle.EMPTY;
                 });
             }
-
-        } else {
-            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
-                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
-                    || (caller.hasPackage() && isCallerDelegate(caller,
-                    DELEGATION_APP_RESTRICTIONS)));
-            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;
-            });
         }
     }
 
@@ -13988,11 +13949,9 @@
                     UserManager.DISALLOW_THREAD_NETWORK,
                     new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});
         }
-        if (Flags.assistContentUserRestrictionEnabled()) {
-            USER_RESTRICTION_PERMISSIONS.put(
-                    UserManager.DISALLOW_ASSIST_CONTENT,
-                    new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
-        }
+        USER_RESTRICTION_PERMISSIONS.put(
+                UserManager.DISALLOW_ASSIST_CONTENT,
+                new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
         USER_RESTRICTION_PERMISSIONS.put(
                 UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO, new String[]{MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION});
         USER_RESTRICTION_PERMISSIONS.put(
@@ -14324,10 +14283,6 @@
             return;
         }
 
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
-        }
-
         CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         synchronized (getLockObject()) {
             int affectedUser = getAffectedUser(parent);
@@ -14938,11 +14893,6 @@
     public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
             throws SecurityException {
         Objects.requireNonNull(packages, "packages is null");
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            for (String pkg : packages) {
-                PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
-            }
-        }
 
         CallerIdentity caller = getCallerIdentity(who, callerPackageName);
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
@@ -15223,7 +15173,7 @@
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(
                 isProfileOwner(caller) || isDefaultDeviceOwner(caller));
-        if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+        if (parent) {
             Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
         }
         checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
@@ -15234,7 +15184,7 @@
                         "Permission denial: device owners cannot update %1$s", setting));
             }
             int affectedUser;
-            if (Flags.allowScreenBrightnessControlOnCope() && parent) {
+            if (parent) {
                 affectedUser = getProfileParentId(caller.getUserId());
             } else {
                 affectedUser = caller.getUserId();
@@ -16308,9 +16258,6 @@
 
         @Override
         public void enforceSecurityLoggingPolicy(boolean enabled) {
-            if (!Flags.securityLogV2Enabled()) {
-                return;
-            }
             Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
                     PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL);
             enforceLoggingPolicy(enabled, Boolean.TRUE.equals(auditLoggingEnabled));
@@ -16318,9 +16265,6 @@
 
         @Override
         public void enforceAuditLoggingPolicy(boolean enabled) {
-            if (!Flags.securityLogV2Enabled()) {
-                return;
-            }
             Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
                     PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL);
             enforceLoggingPolicy(Boolean.TRUE.equals(securityLoggingEnabled), enabled);
@@ -16832,13 +16776,11 @@
                     mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
                 }
 
-                if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
-                    final UserHandle user = UserHandle.of(userId);
-                    final String roleHolderPackage = getRoleHolderPackageNameOnUser(
-                            RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
-                    if (roleHolderPackage != null) {
-                        broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
-                    }
+                final UserHandle user = UserHandle.of(userId);
+                final String roleHolderPackage = getRoleHolderPackageNameOnUser(
+                        RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
+                if (roleHolderPackage != null) {
+                    broadcastExplicitIntentToPackage(intent, roleHolderPackage, user);
                 }
             }
         });
@@ -16846,18 +16788,10 @@
 
     @Override
     public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
-        if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
-            CallerIdentity caller = getCallerIdentity(admin, callerPackage);
-            enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
-                    MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
-                    caller.getUserId());
-        } else {
-            Objects.requireNonNull(admin, "ComponentName is null");
-
-            final CallerIdentity caller = getCallerIdentity(admin);
-            Preconditions.checkCallAuthorization(
-                    isDefaultDeviceOwner(caller) || isProfileOwner(caller));
-        }
+        CallerIdentity caller = getCallerIdentity(admin, callerPackage);
+        enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
+                MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
+                caller.getUserId());
         return mOwners.getSystemUpdateInfo();
     }
 
@@ -17333,7 +17267,7 @@
                     return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
                 }
 
-                if (Flags.headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {
+                if (isHeadlessModeSingleUser) {
                     ensureSetUpUser = mUserManagerInternal.getMainUserId();
                     if (ensureSetUpUser == UserHandle.USER_NULL) {
                         return STATUS_HEADLESS_ONLY_SYSTEM_USER;
@@ -17401,17 +17335,10 @@
             @Nullable ComponentName componentName, @UserIdInt int callingUserId) {
         synchronized (getLockObject()) {
             int deviceOwnerUserId = -1;
-            if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
-                deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
-                        && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
-                        == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
-                        ? UserHandle.USER_SYSTEM : callingUserId;
-            } else {
-                deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
-                        && getHeadlessDeviceOwnerModeForDeviceOwner()
-                        == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
-                        ? UserHandle.USER_SYSTEM : callingUserId;
-            }
+            deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
+                    && getHeadlessDeviceOwnerModeForDeviceAdmin(componentName, callingUserId)
+                    == HEADLESS_DEVICE_OWNER_MODE_AFFILIATED
+                    ? UserHandle.USER_SYSTEM : callingUserId;
             Slogf.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
                     callingUserId, deviceOwnerUserId);
             // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
@@ -17924,15 +17851,28 @@
         });
     }
 
+    private Set<String> getMeteredDataRestrictionExemptPackages(int userId) {
+        final Set<String> exemptPkgs = new ArraySet<>();
+        for (String role: METERED_DATA_RESTRICTION_EXEMPT_ROLES) {
+            String pkg = getRoleHolderPackageNameOnUser(role, userId);
+            if (pkg != null) {
+                exemptPkgs.add(pkg);
+            }
+        }
+
+        return exemptPkgs;
+    }
+
     private List<String> removeInvalidPkgsForMeteredDataRestriction(
             int userId, List<String> pkgNames) {
+        final Set<String> exemptRolePkgs = getMeteredDataRestrictionExemptPackages(userId);
         synchronized (getLockObject()) {
             final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
             final List<String> excludedPkgs = new ArrayList<>();
             for (int i = pkgNames.size() - 1; i >= 0; --i) {
                 final String pkgName = pkgNames.get(i);
-                // If the package is an active admin, don't restrict it.
-                if (activeAdmins.contains(pkgName)) {
+                // If the package is an active admin or exempt role, don't restrict it.
+                if (activeAdmins.contains(pkgName) || exemptRolePkgs.contains(pkgName)) {
                     excludedPkgs.add(pkgName);
                     continue;
                 }
@@ -18256,45 +18196,20 @@
         }
         final CallerIdentity caller = getCallerIdentity(who, packageName);
 
-        if (Flags.securityLogV2Enabled()) {
-            EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
-                    who,
-                    MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-                    caller.getPackageName(),
-                    caller.getUserId());
-            if (enabled) {
-                mDevicePolicyEngine.setGlobalPolicy(
-                        PolicyDefinition.SECURITY_LOGGING,
-                        admin,
-                        new BooleanPolicyValue(true));
-            } else {
-                mDevicePolicyEngine.removeGlobalPolicy(
-                        PolicyDefinition.SECURITY_LOGGING,
-                        admin);
-            }
+        EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
+                who,
+                MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+                caller.getPackageName(),
+                caller.getUserId());
+        if (enabled) {
+            mDevicePolicyEngine.setGlobalPolicy(
+                    PolicyDefinition.SECURITY_LOGGING,
+                    admin,
+                    new BooleanPolicyValue(true));
         } else {
-            synchronized (getLockObject()) {
-                if (who != null) {
-                    Preconditions.checkCallAuthorization(
-                            isProfileOwnerOfOrganizationOwnedDevice(caller)
-                                    || isDefaultDeviceOwner(caller));
-                } else {
-                    // A delegate app passes a null admin component, which is expected
-                    Preconditions.checkCallAuthorization(
-                            isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
-                }
-
-                if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) {
-                    return;
-                }
-                mInjector.securityLogSetLoggingEnabledProperty(enabled);
-                if (enabled) {
-                    mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
-                    maybePauseDeviceWideLoggingLocked();
-                } else {
-                    mSecurityLogMonitor.stop();
-                }
-            }
+            mDevicePolicyEngine.removeGlobalPolicy(
+                    PolicyDefinition.SECURITY_LOGGING,
+                    admin);
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_SECURITY_LOGGING_ENABLED)
@@ -18316,29 +18231,14 @@
             return mInjector.securityLogGetLoggingEnabledProperty();
         }
 
-        if (Flags.securityLogV2Enabled()) {
-            final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    admin,
-                    MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-                    caller.getPackageName(),
-                    caller.getUserId());
-            final Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
-                    PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
-            return Boolean.TRUE.equals(policy);
-        } else {
-            synchronized (getLockObject()) {
-                if (admin != null) {
-                    Preconditions.checkCallAuthorization(
-                            isProfileOwnerOfOrganizationOwnedDevice(caller)
-                                    || isDefaultDeviceOwner(caller));
-                } else {
-                    // A delegate app passes a null admin component, which is expected
-                    Preconditions.checkCallAuthorization(
-                            isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
-                }
-                return mInjector.securityLogGetLoggingEnabledProperty();
-            }
-        }
+        final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                admin,
+                MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+                caller.getPackageName(),
+                caller.getUserId());
+        final Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+                PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
+        return Boolean.TRUE.equals(policy);
     }
 
     private void recordSecurityLogRetrievalTime() {
@@ -18414,42 +18314,24 @@
 
         final CallerIdentity caller = getCallerIdentity(admin, packageName);
 
-        if (Flags.securityLogV2Enabled()) {
-            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                    admin,
-                    MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
-                    caller.getPackageName(),
-                    caller.getUserId());
+        EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                admin,
+                MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
+                caller.getPackageName(),
+                caller.getUserId());
 
-            synchronized (getLockObject()) {
-                Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
-                        || areAllUsersAffiliatedWithDeviceLocked());
-            }
-
-            Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
-                    PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
-
-            if (!Boolean.TRUE.equals(policy)) {
-                Slogf.e(LOG_TAG, "%s hasn't enabled security logging but tries to retrieve logs",
-                        caller.getPackageName());
-                return null;
-            }
-        } else {
-            if (admin != null) {
-                Preconditions.checkCallAuthorization(
-                        isProfileOwnerOfOrganizationOwnedDevice(caller)
-                                || isDefaultDeviceOwner(caller));
-            } else {
-                // A delegate app passes a null admin component, which is expected
-                Preconditions.checkCallAuthorization(
-                        isCallerDelegate(caller, DELEGATION_SECURITY_LOGGING));
-            }
+        synchronized (getLockObject()) {
             Preconditions.checkCallAuthorization(isOrganizationOwnedDeviceWithManagedProfile()
                     || areAllUsersAffiliatedWithDeviceLocked());
+        }
 
-            if (!mInjector.securityLogGetLoggingEnabledProperty()) {
-                return null;
-            }
+        Boolean policy = mDevicePolicyEngine.getGlobalPolicySetByAdmin(
+                PolicyDefinition.SECURITY_LOGGING, enforcingAdmin);
+
+        if (!Boolean.TRUE.equals(policy)) {
+            Slogf.e(LOG_TAG, "%s hasn't enabled security logging but tries to retrieve logs",
+                    caller.getPackageName());
+            return null;
         }
 
         recordSecurityLogRetrievalTime();
@@ -18469,10 +18351,6 @@
         }
         final CallerIdentity caller = getCallerIdentity(callingPackage);
 
-        if (!Flags.securityLogV2Enabled()) {
-            throw new UnsupportedOperationException("Audit log not enabled");
-        }
-
         EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
                 null /* admin */,
                 MANAGE_DEVICE_POLICY_AUDIT_LOGGING,
@@ -18497,10 +18375,6 @@
             return false;
         }
 
-        if (!Flags.securityLogV2Enabled()) {
-            throw new UnsupportedOperationException("Audit log not enabled");
-        }
-
         final CallerIdentity caller = getCallerIdentity(callingPackage);
         EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
                 null /* admin */,
@@ -18763,8 +18637,7 @@
 
         // Backup service has to be enabled on the main user in order for it to be enabled on
         // secondary users.
-        if (Flags.headlessSingleUserFixes() && isDeviceOwner(caller)
-                && getHeadlessDeviceOwnerModeForDeviceOwner()
+        if (isDeviceOwner(caller) && getHeadlessDeviceOwnerModeForDeviceOwner()
                 == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER) {
             toggleBackupServiceActive(UserHandle.USER_SYSTEM, enabled);
         }
@@ -19843,16 +19716,14 @@
     }
 
     private void transferSubscriptionOwnership(ComponentName admin, ComponentName target) {
-        if (Flags.esimManagementEnabled()) {
-            SubscriptionManager subscriptionManager = mContext.getSystemService(
-                    SubscriptionManager.class);
-            for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) {
-                try {
-                    subscriptionManager.setGroupOwner(subId, target.getPackageName());
-                } catch (Exception e) {
-                    // Shouldn't happen.
-                    Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId);
-                }
+        SubscriptionManager subscriptionManager = mContext.getSystemService(
+                SubscriptionManager.class);
+        for (int subId : getSubscriptionIdsInternal(admin.getPackageName()).toArray()) {
+            try {
+                subscriptionManager.setGroupOwner(subId, target.getPackageName());
+            } catch (Exception e) {
+                // Shouldn't happen.
+                Slogf.e(LOG_TAG, e, "Error setting group owner for subId: " + subId);
             }
         }
     }
@@ -20750,9 +20621,7 @@
                     // have OP_RUN_ANY_IN_BACKGROUND app op and won't execute in the background. The
                     // code below grants that app op, and once the exemption is in place, the user
                     // won't be able to disable background usage anymore.
-                    if (Flags.powerExemptionBgUsageFix()
-                            && exemption == EXEMPT_FROM_POWER_RESTRICTIONS
-                            && newMode == MODE_ALLOWED) {
+                    if (exemption == EXEMPT_FROM_POWER_RESTRICTIONS && newMode == MODE_ALLOWED) {
                         setBgUsageAppOp(appOpsMgr, appInfo);
                     }
                 }
@@ -21509,13 +21378,7 @@
 
         final CallerIdentity caller = getCallerIdentity(callerPackage);
 
-        if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
-            enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
-        } else {
-            Preconditions.checkCallAuthorization(
-                    isDefaultDeviceOwner(caller) || isProfileOwner(caller)
-                            || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
-        }
+        enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
                     caller.getUserId());
@@ -21843,8 +21706,6 @@
      */
     @Nullable
     private String getRoleHolderPackageNameOnUser(String role, int userId) {
-        RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-
         // Clear calling identity as the RoleManager APIs require privileged permissions.
         return mInjector.binderWithCleanCallingIdentity(() -> {
             List<UserInfo> users;
@@ -21856,7 +21717,7 @@
             }
             for (UserInfo user : users) {
                 List<String> roleHolders =
-                        roleManager.getRoleHoldersAsUser(role, user.getUserHandle());
+                        mInjector.roleManagerGetRoleHoldersAsUser(role, user.getUserHandle());
                 if (!roleHolders.isEmpty()) {
                     return roleHolders.get(0);
                 }
@@ -22006,18 +21867,15 @@
             return;
         }
 
-        if (Flags.copyAccountWithRetryEnabled()) {
-            boolean copySucceeded = false;
-            int retryAttemptsLeft = RETRY_COPY_ACCOUNT_ATTEMPTS;
-            while (!copySucceeded && (retryAttemptsLeft > 0)) {
-                Slogf.i(LOG_TAG, "Copying account. Attempts left : " + retryAttemptsLeft);
-                copySucceeded =
-                        copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
-                retryAttemptsLeft--;
-            }
-        } else {
-            copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
+        boolean copySucceeded = false;
+        int retryAttemptsLeft = RETRY_COPY_ACCOUNT_ATTEMPTS;
+        while (!copySucceeded && (retryAttemptsLeft > 0)) {
+            Slogf.i(LOG_TAG, "Copying account. Attempts left : " + retryAttemptsLeft);
+            copySucceeded =
+                    copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
+            retryAttemptsLeft--;
         }
+
         if (!keepAccountMigrated) {
             removeAccount(accountToMigrate, sourceUserId);
         }
@@ -22116,16 +21974,9 @@
         final long identity = Binder.clearCallingIdentity();
         try {
             boolean isSingleUserMode;
-            if (Flags.headlessDeviceOwnerProvisioningFixEnabled()) {
-                int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
-                        deviceAdmin, caller.getUserId());
-                isSingleUserMode =
-                        headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
-            } else {
-                isSingleUserMode =
-                        getHeadlessDeviceOwnerModeForDeviceOwner()
-                                == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
-            }
+            int headlessDeviceOwnerMode = getHeadlessDeviceOwnerModeForDeviceAdmin(
+                    deviceAdmin, caller.getUserId());
+            isSingleUserMode = headlessDeviceOwnerMode == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
 
             if (Flags.headlessSingleMinTargetSdk()
                     && mInjector.userManagerIsHeadlessSystemUserMode()
@@ -22149,8 +22000,8 @@
             setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
             setLocale(provisioningParams.getLocale());
 
-            int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled()
-                    && isSingleUserMode && mInjector.userManagerIsHeadlessSystemUserMode()
+            int deviceOwnerUserId =
+                    isSingleUserMode && mInjector.userManagerIsHeadlessSystemUserMode()
                     ? mUserManagerInternal.getMainUserId() : UserHandle.USER_SYSTEM;
 
             if (!removeNonRequiredAppsForManagedDevice(
@@ -22524,35 +22375,17 @@
         Objects.requireNonNull(packageName, "Admin package name must be provided");
         final CallerIdentity caller = getCallerIdentity(packageName);
 
-        if (!Flags.policyEngineMigrationV2Enabled()) {
-            Preconditions.checkCallAuthorization(
-                    isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
-                    "USB data signaling can only be controlled by a device owner or "
-                            + "a profile owner on an organization-owned device.");
+        synchronized (getLockObject()) {
+            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+                    /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
+                    caller.getPackageName(),
+                    caller.getUserId());
             Preconditions.checkState(canUsbDataSignalingBeDisabled(),
                     "USB data signaling cannot be disabled.");
-        }
-
-        synchronized (getLockObject()) {
-            if (Flags.policyEngineMigrationV2Enabled()) {
-                EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
-                        /* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
-                        caller.getPackageName(),
-                        caller.getUserId());
-                Preconditions.checkState(canUsbDataSignalingBeDisabled(),
-                        "USB data signaling cannot be disabled.");
-                mDevicePolicyEngine.setGlobalPolicy(
-                        PolicyDefinition.USB_DATA_SIGNALING,
-                        enforcingAdmin,
-                        new BooleanPolicyValue(enabled));
-            } else {
-                ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
-                if (admin.mUsbDataSignalingEnabled != enabled) {
-                    admin.mUsbDataSignalingEnabled = enabled;
-                    saveSettingsLocked(caller.getUserId());
-                    updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
-                }
-            }
+            mDevicePolicyEngine.setGlobalPolicy(
+                    PolicyDefinition.USB_DATA_SIGNALING,
+                    enforcingAdmin,
+                    new BooleanPolicyValue(enabled));
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_USB_DATA_SIGNALING)
@@ -22574,24 +22407,10 @@
     @Override
     public boolean isUsbDataSignalingEnabled(String packageName) {
         final CallerIdentity caller = getCallerIdentity(packageName);
-        if (Flags.policyEngineMigrationV2Enabled()) {
-            Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
-                    PolicyDefinition.USB_DATA_SIGNALING,
-                    caller.getUserId());
-            return enabled == null || enabled;
-        } else {
-            synchronized (getLockObject()) {
-                // If the caller is an admin, return the policy set by itself. Otherwise
-                // return the device-wide policy.
-                if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(
-                        caller)) {
-                    return getProfileOwnerOrDeviceOwnerLocked(
-                            caller.getUserId()).mUsbDataSignalingEnabled;
-                } else {
-                    return isUsbDataSignalingEnabledInternalLocked();
-                }
-            }
-        }
+        Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
+                PolicyDefinition.USB_DATA_SIGNALING,
+                caller.getUserId());
+        return enabled == null || enabled;
     }
 
     private boolean isUsbDataSignalingEnabledInternalLocked() {
@@ -23505,6 +23324,8 @@
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
         CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WIPE_DATA,
                 MANAGE_DEVICE_POLICY_ACROSS_USERS);
+        CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_CONTENT_PROTECTION,
+                MANAGE_DEVICE_POLICY_ACROSS_USERS);
 
         // These permissions may grant access to user data and therefore must be protected with
         // MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL for cross-user calls.
@@ -24140,15 +23961,13 @@
 
     @Override
     public @ContentProtectionPolicy int getContentProtectionPolicy(
-            ComponentName who, String callerPackageName) {
+            ComponentName who, String callerPackageName, int userId) {
         if (!android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled()) {
             return CONTENT_PROTECTION_DISABLED;
         }
 
         CallerIdentity caller = getCallerIdentity(who, callerPackageName);
-        int userId = caller.getUserId();
         enforceCanQuery(MANAGE_DEVICE_POLICY_CONTENT_PROTECTION, callerPackageName, userId);
-
         Integer policy =
                 mDevicePolicyEngine.getResolvedPolicy(PolicyDefinition.CONTENT_PROTECTION, userId);
         if (policy == null) {
@@ -24944,9 +24763,6 @@
 
     @Override
     public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            return;
-        }
         CallerIdentity caller = getCallerIdentity(callerPackageName);
         enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
                 caller.getUserId());
@@ -24960,9 +24776,6 @@
 
     @Override
     public int getMaxPolicyStorageLimit(String callerPackageName) {
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            return -1;
-        }
         CallerIdentity caller = getCallerIdentity(callerPackageName);
         enforcePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS, caller.getPackageName(),
                 caller.getUserId());
@@ -24972,9 +24785,6 @@
 
     @Override
     public void forceSetMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            return;
-        }
         CallerIdentity caller = getCallerIdentity(callerPackageName);
         enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
                 caller.getUserId());
@@ -24985,9 +24795,6 @@
     @Override
     public int getPolicySizeForAdmin(
             String callerPackageName, android.app.admin.EnforcingAdmin admin) {
-        if (!Flags.devicePolicySizeTrackingInternalBugFixEnabled()) {
-            return -1;
-        }
         CallerIdentity caller = getCallerIdentity(callerPackageName);
         enforcePermission(MANAGE_DEVICE_POLICY_STORAGE_LIMIT, caller.getPackageName(),
                 caller.getUserId());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index e65e513..634f1bc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -203,7 +203,8 @@
         // Only system authorities use this constructor.
         mIsSystemAuthority = true;
         mIsRoleAuthority = false;
-        mPackageName = null;
+        // Package name is not used for a system enforcing admin, so an empty string is fine.
+        mPackageName = "";
         mSystemEntity = systemEntity;
         mUserId = UserHandle.USER_SYSTEM;
         mComponentName = null;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 2ea5f16..87fd002 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -361,9 +361,7 @@
 
         @Override
         boolean shouldWrite() {
-            return Flags.alwaysPersistDo()
-                    || (mDeviceOwner != null) || (mSystemUpdatePolicy != null)
-                    || (mSystemUpdateInfo != null);
+            return true;
         }
 
         @Override
@@ -410,9 +408,8 @@
             out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);
             out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine);
             out.attributeBoolean(null, ATTR_MIGRATED_POST_UPGRADE, mPoliciesMigratedPostUpdate);
-            if (Flags.securityLogV2Enabled()) {
-                out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
-            }
+            out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
+
             if (Flags.unmanagedModeMigration()) {
                 out.attributeBoolean(null, ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED,
                         mRequiredPasswordComplexityMigrated);
@@ -483,8 +480,8 @@
                             null, ATTR_MIGRATED_TO_POLICY_ENGINE, false);
                     mPoliciesMigratedPostUpdate = parser.getAttributeBoolean(
                             null, ATTR_MIGRATED_POST_UPGRADE, false);
-                    mSecurityLoggingMigrated = Flags.securityLogV2Enabled()
-                            && parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
+                    mSecurityLoggingMigrated =
+                            parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
                     mRequiredPasswordComplexityMigrated = Flags.unmanagedModeMigration()
                             && parser.getAttributeBoolean(null,
                                     ATTR_REQUIRED_PASSWORD_COMPLEXITY_MIGRATED, false);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index f86d307..24ee46f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -129,9 +129,8 @@
      */
     static PolicyDefinition<Integer> PERMISSION_GRANT(
             @NonNull String packageName, @NonNull String permissionName) {
-        if (packageName == null || permissionName == null) {
-            return GENERIC_PERMISSION_GRANT;
-        }
+        Objects.requireNonNull(packageName, "packageName must not be null");
+        Objects.requireNonNull(permissionName, "permissionName must not be null");
         return GENERIC_PERMISSION_GRANT.createPolicyDefinition(
                 new PackagePermissionPolicyKey(
                         DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY,
@@ -190,10 +189,8 @@
      * {@link #GENERIC_PERSISTENT_PREFERRED_ACTIVITY}.
      */
     static PolicyDefinition<ComponentName> PERSISTENT_PREFERRED_ACTIVITY(
-            IntentFilter intentFilter) {
-        if (intentFilter == null) {
-            return GENERIC_PERSISTENT_PREFERRED_ACTIVITY;
-        }
+            @NonNull IntentFilter intentFilter) {
+        Objects.requireNonNull(intentFilter, "intentFilter must not be null");
         return GENERIC_PERSISTENT_PREFERRED_ACTIVITY.createPolicyDefinition(
                 new IntentFilterPolicyKey(
                         DevicePolicyIdentifiers.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
@@ -216,11 +213,8 @@
      * Passing in {@code null} for {@code packageName} will return
      * {@link #GENERIC_PACKAGE_UNINSTALL_BLOCKED}.
      */
-    static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(
-            String packageName) {
-        if (packageName == null) {
-            return GENERIC_PACKAGE_UNINSTALL_BLOCKED;
-        }
+    static PolicyDefinition<Boolean> PACKAGE_UNINSTALL_BLOCKED(@NonNull String packageName) {
+        Objects.requireNonNull(packageName, "packageName must not be null");
         return GENERIC_PACKAGE_UNINSTALL_BLOCKED.createPolicyDefinition(
                 new PackagePolicyKey(
                         DevicePolicyIdentifiers.PACKAGE_UNINSTALL_BLOCKED_POLICY, packageName));
@@ -247,10 +241,8 @@
      * Passing in {@code null} for {@code packageName} will return
      * {@link #GENERIC_APPLICATION_RESTRICTIONS}.
      */
-    static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(String packageName) {
-        if (packageName == null) {
-            return GENERIC_APPLICATION_RESTRICTIONS;
-        }
+    static PolicyDefinition<Bundle> APPLICATION_RESTRICTIONS(@NonNull String packageName) {
+        Objects.requireNonNull(packageName, "packageName must not be null");
         return GENERIC_APPLICATION_RESTRICTIONS.createPolicyDefinition(
                 new PackagePolicyKey(
                         DevicePolicyIdentifiers.APPLICATION_RESTRICTIONS_POLICY, packageName));
@@ -293,10 +285,8 @@
      * Passing in {@code null} for {@code packageName} will return
      * {@link #GENERIC_APPLICATION_HIDDEN}.
      */
-    static PolicyDefinition<Boolean> APPLICATION_HIDDEN(String packageName) {
-        if (packageName == null) {
-            return GENERIC_APPLICATION_HIDDEN;
-        }
+    static PolicyDefinition<Boolean> APPLICATION_HIDDEN(@NonNull String packageName) {
+        Objects.requireNonNull(packageName, "packageName must not be null");
         return GENERIC_APPLICATION_HIDDEN.createPolicyDefinition(
                 new PackagePolicyKey(
                         DevicePolicyIdentifiers.APPLICATION_HIDDEN_POLICY, packageName));
@@ -319,10 +309,8 @@
      * Passing in {@code null} for {@code accountType} will return
      * {@link #GENERIC_ACCOUNT_MANAGEMENT_DISABLED}.
      */
-    static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(String accountType) {
-        if (accountType == null) {
-            return GENERIC_ACCOUNT_MANAGEMENT_DISABLED;
-        }
+    static PolicyDefinition<Boolean> ACCOUNT_MANAGEMENT_DISABLED(@NonNull String accountType) {
+        Objects.requireNonNull(accountType, "accountType must not be null");
         return GENERIC_ACCOUNT_MANAGEMENT_DISABLED.createPolicyDefinition(
                 new AccountTypePolicyKey(
                         DevicePolicyIdentifiers.ACCOUNT_MANAGEMENT_DISABLED_POLICY, accountType));
@@ -548,7 +536,6 @@
             USER_RESTRICTION_FLAGS.put(
                     UserManager.DISALLOW_THREAD_NETWORK, POLICY_FLAG_GLOBAL_ONLY_POLICY);
         }
-        USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_ASSIST_CONTENT, /* flags= */ 0);
         for (String key : USER_RESTRICTION_FLAGS.keySet()) {
             createAndAddUserRestrictionPolicyDefinition(key, USER_RESTRICTION_FLAGS.get(key));
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index e1cb37d..8068d46 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -238,9 +238,7 @@
             }
 
             for (int user : resolveUsers(userId)) {
-                if (Flags.disallowUserControlBgUsageFix()) {
-                    setBgUsageAppOp(packages, pmi, user, appOpsManager);
-                }
+                setBgUsageAppOp(packages, pmi, user, appOpsManager);
                 if (Flags.disallowUserControlStoppedStateFix()) {
                     for (String packageName : packages) {
                         pmi.setPackageStoppedState(packageName, false, user);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index dd049303..474c48a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -23,7 +23,6 @@
 import android.app.admin.IAuditLogEventsCallback;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.flags.Flags;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
@@ -184,28 +183,6 @@
     @GuardedBy("mLock")
     private final ArrayDeque<SecurityEvent> mAuditLogEventBuffer = new ArrayDeque<>();
 
-    /**
-     * Start security logging.
-     *
-     * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all
-     *     users on the device.
-     */
-    void start(int enabledUser) {
-        Slog.i(TAG, "Starting security logging for user " + enabledUser);
-        mEnabledUser = enabledUser;
-        mLock.lock();
-        try {
-            if (mMonitorThread == null) {
-                resetLegacyBufferLocked();
-                startMonitorThreadLocked();
-            } else {
-                Slog.i(TAG, "Security log monitor thread is already running");
-            }
-        } finally {
-            mLock.unlock();
-        }
-    }
-
     void stop() {
         Slog.i(TAG, "Stopping security logging.");
         mLock.lock();
@@ -467,11 +444,11 @@
             assignLogId(event);
         }
 
-        if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+        if (mLegacyLogEnabled) {
             addToLegacyBufferLocked(dedupedLogs);
         }
 
-        if (Flags.securityLogV2Enabled() && mAuditLogEnabled) {
+        if (mAuditLogEnabled) {
             addAuditLogEventsLocked(dedupedLogs);
         }
     }
@@ -548,7 +525,7 @@
                 saveLastEvents(newLogs);
                 newLogs.clear();
 
-                if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+                if (mLegacyLogEnabled) {
                     notifyDeviceOwnerOrProfileOwnerIfNeeded(force);
                 }
             } catch (IOException e) {
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
index 6393e11..1db9e8d 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
@@ -1,7 +1,7 @@
 aconfig_declarations {
     name: "device_state_flags",
     package: "com.android.server.policy.feature.flags",
-    container: "system",
+    container: "system_ext",
     srcs: [
         "device_state_flags.aconfig",
     ],
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index 21e33dd..f827b55 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -1,5 +1,5 @@
 package: "com.android.server.policy.feature.flags"
-container: "system"
+container: "system_ext"
 
 flag {
     name: "enable_dual_display_blocking"
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9e8811f..ab459df 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,6 +37,7 @@
 import android.app.INotificationManager;
 import android.app.SystemServiceRegistry;
 import android.app.admin.DevicePolicySafetyChecker;
+import android.app.appfunctions.AppFunctionManagerConfiguration;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -105,6 +106,9 @@
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.policy.AttributeCache;
+import com.android.internal.protolog.ProtoLog;
+import com.android.internal.protolog.ProtoLogConfigurationService;
+import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.EmergencyAffordanceManager;
 import com.android.internal.util.FrameworkStatsLog;
@@ -119,6 +123,7 @@
 import com.android.server.ambientcontext.AmbientContextManagerService;
 import com.android.server.app.GameManagerService;
 import com.android.server.appbinding.AppBindingService;
+import com.android.server.appfunctions.AppFunctionManagerService;
 import com.android.server.apphibernation.AppHibernationService;
 import com.android.server.appop.AppOpMigrationHelper;
 import com.android.server.appop.AppOpMigrationHelperImpl;
@@ -253,6 +258,7 @@
 import com.android.server.stats.pull.StatsPullAtomService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.supervision.SupervisionService;
 import com.android.server.systemcaptions.SystemCaptionsManagerService;
 import com.android.server.telecom.TelecomLoaderService;
 import com.android.server.testharness.TestHarnessModeService;
@@ -1087,6 +1093,18 @@
         SystemServerInitThreadPool.submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
         t.traceEnd();
 
+        // Orchestrates some ProtoLogging functionality.
+        if (android.tracing.Flags.clientSideProtoLogging()) {
+            t.traceBegin("StartProtoLogConfigurationService");
+            ServiceManager.addService(
+                    Context.PROTOLOG_CONFIGURATION_SERVICE, new ProtoLogConfigurationService());
+            t.traceEnd();
+        }
+
+        t.traceBegin("InitializeProtoLog");
+        ProtoLog.init(ProtoLogGroup.values());
+        t.traceEnd();
+
         // Platform compat service is used by ActivityManagerService, PackageManagerService, and
         // possibly others in the future. b/135010838.
         t.traceBegin("PlatformCompat");
@@ -1587,6 +1605,12 @@
             mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
             t.traceEnd();
 
+            if (android.app.supervision.flags.Flags.supervisionApi()) {
+                t.traceBegin("StartSupervisionService");
+                mSystemServiceManager.startService(SupervisionService.Lifecycle.class);
+                t.traceEnd();
+            }
+
             if (!isTv) {
                 t.traceBegin("StartVibratorManagerService");
                 mSystemServiceManager.startService(VibratorManagerService.Lifecycle.class);
@@ -1719,6 +1743,11 @@
             mSystemServiceManager.startService(LogcatManagerService.class);
             t.traceEnd();
 
+            if (AppFunctionManagerConfiguration.isSupported(context)) {
+                t.traceBegin("StartAppFunctionManager");
+                mSystemServiceManager.startService(AppFunctionManagerService.class);
+                t.traceEnd();
+            }
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
@@ -2438,8 +2467,8 @@
                 reportWtf("starting RuntimeService", e);
             }
             t.traceEnd();
-
-            if (!isWatch && !disableNetworkTime) {
+            if (!disableNetworkTime && (!isWatch || (isWatch
+                    && android.server.Flags.allowNetworkTimeUpdateService()))) {
                 t.traceBegin("StartNetworkTimeUpdateService");
                 try {
                     networkTimeUpdater = new NetworkTimeUpdateService(context);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 29f3871..ec74ef19 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -28,4 +28,11 @@
      namespace: "wear_frameworks"
      description: "Allow removing VpnManagerService"
      bug: "340928692"
+}
+
+flag {
+    name: "allow_network_time_update_service"
+    namespace: "wear_systems"
+    description: "Allow NetworkTimeUpdateService on Wear"
+    bug: "327508176"
 }
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 7ed23cd..d2c91ff 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -888,7 +888,7 @@
                     val mayGrantByPrivileged =
                         !permission.isPrivileged ||
                             requestingPackageStates.anyIndexed { _, it ->
-                                checkPrivilegedPermissionAllowlist(it, permission)
+                                checkPrivilegedPermissionAllowlistIfNeeded(it, permission)
                             }
                     val shouldGrantBySignature =
                         permission.isSignature &&
@@ -1280,7 +1280,16 @@
         }
     }
 
-    private fun MutateStateScope.checkPrivilegedPermissionAllowlist(
+    /**
+     * We only check privileged permission allowlist for system privileged apps. Hence, for platform
+     * or for normal apps, we return true to indicate that we don't need to check the allowlist and
+     * will let follow-up checks to decide whether we should grant the permission.
+     *
+     * @return `true`, if the permission is allowlisted for system privileged apps, or if we
+     *         don't need to check the allowlist (for platform or for normal apps).
+     *         `false`, if the permission is not allowlisted for system privileged apps.
+     */
+    private fun MutateStateScope.checkPrivilegedPermissionAllowlistIfNeeded(
         packageState: PackageState,
         permission: Permission
     ): Boolean {
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index dab3978..105147f 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -46,12 +46,12 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.art.ArtManagerLocal;
+import com.android.server.profcollect.Utils;
 import com.android.server.wm.ActivityMetricsLaunchObserver;
 import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.Arrays;
-import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -275,30 +275,15 @@
         launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
     }
 
-    private void traceOnAppStart(String packageName) {
-        if (mIProfcollect == null) {
-            return;
-        }
-
-        // Sample for a fraction of app launches.
-        int traceFrequency = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
-                "applaunch_trace_freq", 2);
-        int randomNum = ThreadLocalRandom.current().nextInt(100);
-        if (randomNum < traceFrequency) {
-            BackgroundThread.get().getThreadHandler().post(() -> {
-                try {
-                    mIProfcollect.trace_system("applaunch");
-                } catch (RemoteException e) {
-                    Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
-                }
-            });
-        }
-    }
-
     private class AppLaunchObserver extends ActivityMetricsLaunchObserver {
         @Override
         public void onIntentStarted(Intent intent, long timestampNanos) {
-            traceOnAppStart(intent.getPackage());
+            if (mIProfcollect == null) {
+                return;
+            }
+            if (Utils.withFrequency("applaunch_trace_freq", 5)) {
+                Utils.traceSystem(mIProfcollect, "applaunch");
+            }
         }
     }
 
@@ -318,20 +303,9 @@
         if (mIProfcollect == null) {
             return;
         }
-        // Sample for a fraction of dex2oat runs.
-        final int traceFrequency =
-            DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
-                "dex2oat_trace_freq", 25);
-        int randomNum = ThreadLocalRandom.current().nextInt(100);
-        if (randomNum < traceFrequency) {
+        if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
             // Dex2oat could take a while before it starts. Add a short delay before start tracing.
-            BackgroundThread.get().getThreadHandler().postDelayed(() -> {
-                try {
-                    mIProfcollect.trace_system("dex2oat");
-                } catch (RemoteException e) {
-                    Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
-                }
-            }, 1000);
+            Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
         }
     }
 
@@ -393,27 +367,12 @@
                 if (Arrays.asList(cameraSkipPackages).contains(packageId)) {
                     return;
                 }
-                // Sample for a fraction of camera events.
-                final int traceFrequency =
-                        DeviceConfig.getInt(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
-                        "camera_trace_freq", 10);
-                int randomNum = ThreadLocalRandom.current().nextInt(100);
-                if (randomNum >= traceFrequency) {
-                    return;
+                if (Utils.withFrequency("camera_trace_freq", 10)) {
+                    Utils.traceProcess(mIProfcollect,
+                            "camera",
+                            "android.hardware.camera.provider",
+                            /* durationMs */ 5000);
                 }
-                final int traceDuration = 5000;
-                final String traceTag = "camera";
-                BackgroundThread.get().getThreadHandler().post(() -> {
-                    if (mIProfcollect == null) {
-                        return;
-                    }
-                    try {
-                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider",
-                                traceDuration);
-                    } catch (RemoteException e) {
-                        Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
-                    }
-                });
             }
         }, null);
     }
diff --git a/services/profcollect/src/com/android/server/profcollect/Utils.java b/services/profcollect/src/com/android/server/profcollect/Utils.java
new file mode 100644
index 0000000..8508802
--- /dev/null
+++ b/services/profcollect/src/com/android/server/profcollect/Utils.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.profcollect;
+
+import static com.android.server.profcollect.ProfcollectForwardingService.LOG_TAG;
+
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Log;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.util.concurrent.ThreadLocalRandom;
+
+public final class Utils {
+
+    public static boolean withFrequency(String configName, int defaultFrequency) {
+        int threshold = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, configName, defaultFrequency);
+        int randomNum = ThreadLocalRandom.current().nextInt(100);
+        return randomNum < threshold;
+    }
+
+    public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName) {
+        if (mIProfcollect == null) {
+            return false;
+        }
+        BackgroundThread.get().getThreadHandler().post(() -> {
+            try {
+                mIProfcollect.trace_system(eventName);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
+            }
+        });
+        return true;
+    }
+
+    public static boolean traceSystem(IProfCollectd mIProfcollect, String eventName, int delayMs) {
+        if (mIProfcollect == null) {
+            return false;
+        }
+        BackgroundThread.get().getThreadHandler().postDelayed(() -> {
+            try {
+                mIProfcollect.trace_system(eventName);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
+            }
+        }, delayMs);
+        return true;
+    }
+
+    public static boolean traceProcess(IProfCollectd mIProfcollect,
+            String eventName, String processName, int durationMs) {
+        if (mIProfcollect == null) {
+            return false;
+        }
+        BackgroundThread.get().getThreadHandler().post(() -> {
+            try {
+                mIProfcollect.trace_process(eventName,
+                        processName,
+                        durationMs);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
+            }
+        });
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/services/proguard.flags b/services/proguard.flags
index bf30781..cdd41ab 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -52,9 +52,6 @@
 -keep public class com.android.server.utils.Slogf { *; }
 
 # Referenced in wear-service
-# HIDL interfaces
--keep public class android.hidl.base.** { *; }
--keep public class android.hidl.manager.** { *; }
 -keep public class com.android.server.wm.WindowManagerInternal { *; }
 
 # JNI keep rules
@@ -107,8 +104,23 @@
 -keep,allowoptimization,allowaccessmodification class com.android.server.SystemService { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.SystemService$TargetUser { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.usage.StorageStatsManagerLocal { *; }
--keep,allowoptimization,allowaccessmodification class com.android.internal.util.** { *; }
--keep,allowoptimization,allowaccessmodification class android.os.** { *; }
+
+# Prevent optimizations of any statically linked code that may shadow code in
+# the bootclasspath. See also StrictJavaPackagesTest for details on exceptions.
+# TODO(b/222468116): Resolve such collisions in the build system.
+-keep public class android.gsi.** { *; }
+-keep public class android.hidl.base.** { *; }
+-keep public class android.hidl.manager.** { *; }
+-keep public class android.os.** { *; }
+-keep public class com.android.internal.util.** { *; }
+-keep public class com.android.modules.utils.build.** { *; }
+# Also suppress related duplicate type warnings for the above kept classes.
+-dontwarn android.gsi.**
+-dontwarn android.hidl.base.**
+-dontwarn android.hidl.manager.**
+-dontwarn android.os.**
+-dontwarn com.android.internal.util.**
+-dontwarn com.android.modules.utils.build.**
 
 # CoverageService guards optional jacoco class references with a runtime guard, so we can safely
 # suppress build-time warnings.
diff --git a/services/supervision/Android.bp b/services/supervision/Android.bp
new file mode 100644
index 0000000..93a0c4a
--- /dev/null
+++ b/services/supervision/Android.bp
@@ -0,0 +1,22 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+    name: "services.supervision-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.supervision",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.supervision-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/supervision/OWNERS b/services/supervision/OWNERS
new file mode 100644
index 0000000..e5f4147
--- /dev/null
+++ b/services/supervision/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/supervision/OWNERS
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
new file mode 100644
index 0000000..7ffd0ec
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -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.server.supervision;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.supervision.ISupervisionManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Service for handling system supervision.
+ */
+public class SupervisionService extends ISupervisionManager.Stub {
+    private static final String LOG_TAG = "SupervisionService";
+
+    private final Context mContext;
+
+    public SupervisionService(Context context) {
+        mContext = context.createAttributionContext("SupervisionService");
+    }
+
+    @Override
+    public boolean isSupervisionEnabled() {
+        return false;
+    }
+
+    @Override
+    public void onShellCommand(
+            @Nullable FileDescriptor in,
+            @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args,
+            @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        new SupervisionServiceShellCommand(this)
+                .exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
+    @Override
+    protected void dump(
+            @NonNull FileDescriptor fd, @NonNull PrintWriter fout, @Nullable String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, fout)) return;
+
+        fout.println("Supervision enabled: " + isSupervisionEnabled());
+    }
+
+    public static class Lifecycle extends SystemService {
+        private final SupervisionService mSupervisionService;
+
+        public Lifecycle(@NonNull Context context) {
+            super(context);
+            mSupervisionService = new SupervisionService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
+        }
+    }
+}
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
new file mode 100644
index 0000000..3aba24a
--- /dev/null
+++ b/services/supervision/java/com/android/server/supervision/SupervisionServiceShellCommand.java
@@ -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.server.supervision;
+
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class SupervisionServiceShellCommand extends ShellCommand {
+    private final SupervisionService mService;
+
+    public SupervisionServiceShellCommand(SupervisionService mService) {
+        this.mService = mService;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(null);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "help": return help(pw);
+            case "is-enabled": return isEnabled(pw);
+            default: return handleDefaultCommands(cmd);
+        }
+    }
+
+    private int help(PrintWriter pw) {
+        pw.println("Supervision service commands:");
+        pw.println("  help");
+        pw.println("      Prints this help text");
+        pw.println("  is-enabled");
+        pw.println("      Is supervision enabled");
+        return 0;
+    }
+
+    private int isEnabled(PrintWriter pw) {
+        pw.println(mService.isSupervisionEnabled());
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        help(getOutPrintWriter());
+    }
+}
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index 3bce9b5..9044259 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -138,3 +138,17 @@
         enabled: false,
     },
 }
+
+test_module_config {
+    name: "FrameworksInputMethodSystemServerTests_server_inputmethod",
+    base: "FrameworksInputMethodSystemServerTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.inputmethod"],
+}
+
+test_module_config {
+    name: "FrameworksImeTests_android_inputmethodservice",
+    base: "FrameworksImeTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.inputmethodservice"],
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 0787058..2c785049 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -33,11 +33,15 @@
 import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.InputMethodManager;
 
 import androidx.annotation.NonNull;
@@ -56,6 +60,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -89,6 +94,9 @@
     private String mInputMethodId;
     private boolean mShowImeWithHardKeyboardEnabled;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws Exception {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -155,7 +163,13 @@
                 () -> assertThat(mUiDevice.pressHome()).isTrue(),
                 true /* expected */,
                 false /* inputViewStarted */);
-        assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        if (Flags.refactorInsetsController()) {
+            // The IME visibility is only sent at the end of the animation. Therefore, we have to
+            // wait until the visibility was sent to the server and the IME window hidden.
+            eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+        } else {
+            assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        }
     }
 
     /**
@@ -182,8 +196,13 @@
 
     /**
      * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
+     *
+     * With the refactor in b/298172246, all calls to IMMS#{show,hide}MySoftInputLocked
+     * will be just apply the requested visibility (by using the callback). Therefore, we will
+     * lose flags like HIDE_IMPLICIT_ONLY.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testShowHideSelf() throws Exception {
         setShowImeWithHardKeyboard(true /* enabled */);
 
@@ -375,8 +394,13 @@
     /**
      * This checks that an implicit show request when the IME is not previously shown,
      * and it should be shown in fullscreen mode, results in the IME not being shown.
+     *
+     * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+     * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+     * SHOW_IMPLICIT.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
         setShowImeWithHardKeyboard(true /* enabled */);
 
@@ -425,8 +449,13 @@
     /**
      * This checks that an implicit show request when a hard keyboard is connected,
      * results in the IME not being shown.
+     *
+     * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+     * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+     * SHOW_IMPLICIT.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception {
         setShowImeWithHardKeyboard(false /* enabled */);
 
@@ -484,8 +513,13 @@
      * This checks that an implicit show request followed by connecting a hard keyboard
      * and a configuration change, does not trigger IMS#onFinishInputView,
      * but results in the IME being hidden.
+     *
+     * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+     * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+     * SHOW_IMPLICIT.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception {
         setShowImeWithHardKeyboard(false /* enabled */);
 
@@ -567,8 +601,13 @@
      * This checks that a forced show request directly followed by an explicit show request,
      * and then a hide not always request, still results in the IME being shown
      * (i.e. the explicit show request retains the forced state).
+     *
+     * With the refactor in b/298172246, all calls from InputMethodManager#{show,hide}SoftInput
+     * will be redirected to InsetsController#{show,hide}. Therefore, we will lose flags like
+     * HIDE_NOT_ALWAYS.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways()
             throws Exception {
         setShowImeWithHardKeyboard(true /* enabled */);
@@ -734,7 +773,13 @@
         backButtonUiObject.click();
         mInstrumentation.waitForIdleSync();
 
-        assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        if (Flags.refactorInsetsController()) {
+            // The IME visibility is only sent at the end of the animation. Therefore, we have to
+            // wait until the visibility was sent to the server and the IME window hidden.
+            eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+        } else {
+            assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        }
     }
 
     /**
@@ -766,7 +811,13 @@
         backButtonUiObject.longClick();
         mInstrumentation.waitForIdleSync();
 
-        assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        if (Flags.refactorInsetsController()) {
+            // The IME visibility is only sent at the end of the animation. Therefore, we have to
+            // wait until the visibility was sent to the server and the IME window hidden.
+            eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+        } else {
+            assertThat(mInputMethodService.isInputViewShown()).isFalse();
+        }
     }
 
     /**
@@ -848,7 +899,13 @@
         assertWithMessage("Input Method Switcher Menu is shown")
                 .that(isInputMethodPickerShown(imm))
                 .isTrue();
-        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+        if (Flags.refactorInsetsController()) {
+            // The IME visibility is only sent at the end of the animation. Therefore, we have to
+            // wait until the visibility was sent to the server and the IME window hidden.
+            eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse());
+        } else {
+            assertThat(mInputMethodService.isInputViewShown()).isTrue();
+        }
 
         // Hide the Picker menu before finishing.
         mUiDevice.pressBack();
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 a27ad9a..770451c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -44,7 +44,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
 
 import org.junit.Rule;
@@ -178,19 +177,20 @@
         return items;
     }
 
-    private void assertNextInputMethod(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
-            @NonNull ImeSubtypeListItem currentItem, @Nullable ImeSubtypeListItem nextItem) {
+    private void assertNextInputMethod(@NonNull InputMethodSubtypeSwitchingController controller,
+            boolean onlyCurrentIme, @NonNull ImeSubtypeListItem currentItem,
+            @Nullable ImeSubtypeListItem nextItem) {
         InputMethodSubtype subtype = null;
         if (currentItem.mSubtypeName != null) {
             subtype = createTestSubtype(currentItem.mSubtypeName.toString());
         }
-        final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
+        final ImeSubtypeListItem nextIme = controller.getNextInputMethodLocked(onlyCurrentIme,
                 currentItem.mImi, subtype, MODE_STATIC, true /* forward */);
         assertEquals(nextItem, nextIme);
     }
 
-    private void assertRotationOrder(@NonNull ControllerImpl controller, boolean onlyCurrentIme,
-            ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
+    private void assertRotationOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+            boolean onlyCurrentIme, ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
         final int numItems = expectedRotationOrderOfImeSubtypeList.length;
         for (int i = 0; i < numItems; i++) {
             final int nextIndex = (i + 1) % numItems;
@@ -200,7 +200,7 @@
         }
     }
 
-    private boolean onUserAction(@NonNull ControllerImpl controller,
+    private boolean onUserAction(@NonNull InputMethodSubtypeSwitchingController controller,
             @NonNull ImeSubtypeListItem subtypeListItem) {
         InputMethodSubtype subtype = null;
         if (subtypeListItem.mSubtypeName != null) {
@@ -228,8 +228,8 @@
         final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
 
-        final ControllerImpl controller = ControllerImpl.createFrom(
-                null /* currentInstance */, enabledItems, new ArrayList<>());
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(enabledItems, new ArrayList<>());
 
         // switching-aware loop
         assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -286,8 +286,8 @@
         final ImeSubtypeListItem japaneseIme_ja_jp = enabledItems.get(6);
         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_jp = enabledItems.get(7);
 
-        final ControllerImpl controller = ControllerImpl.createFrom(
-                null /* currentInstance */, enabledItems, new ArrayList<>());
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(enabledItems, new ArrayList<>());
 
         // === switching-aware loop ===
         assertRotationOrder(controller, false /* onlyCurrentIme */,
@@ -336,11 +336,10 @@
 
         // Rotation order should be preserved when created with the same subtype list.
         final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
-        final ControllerImpl newController = ControllerImpl.createFrom(controller,
-                sameEnabledItems, new ArrayList<>());
-        assertRotationOrder(newController, false /* onlyCurrentIme */,
+        controller.update(sameEnabledItems, new ArrayList<>());
+        assertRotationOrder(controller, false /* onlyCurrentIme */,
                 subtypeAwareIme, latinIme_fr, latinIme_en_us, japaneseIme_ja_jp);
-        assertRotationOrder(newController, false /* onlyCurrentIme */,
+        assertRotationOrder(controller, false /* onlyCurrentIme */,
                 switchingUnawareLatinIme_en_uk, switchingUnawareLatinIme_hi, subtypeUnawareIme,
                 switchUnawareJapaneseIme_ja_jp);
 
@@ -348,11 +347,10 @@
         final List<ImeSubtypeListItem> differentEnabledItems = List.of(
                 latinIme_en_us, latinIme_fr, subtypeAwareIme, switchingUnawareLatinIme_en_uk,
                 switchUnawareJapaneseIme_ja_jp, subtypeUnawareIme);
-        final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
-                differentEnabledItems, new ArrayList<>());
-        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+        controller.update(differentEnabledItems, new ArrayList<>());
+        assertRotationOrder(controller, false /* onlyCurrentIme */,
                 latinIme_en_us, latinIme_fr, subtypeAwareIme);
-        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
+        assertRotationOrder(controller, false /* onlyCurrentIme */,
                 switchingUnawareLatinIme_en_uk, switchUnawareJapaneseIme_ja_jp, subtypeUnawareIme);
     }
 
@@ -520,8 +518,8 @@
         final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
         final var hardwareSimpleIme = List.of(hardwareSimple);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         final int mode = MODE_STATIC;
 
@@ -583,8 +581,8 @@
         final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
         final var hardwareSimpleIme = List.of(hardwareSimple);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         final int mode = MODE_RECENT;
 
@@ -666,8 +664,8 @@
         final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
         final var hardwareSimpleIme = List.of(hardwareSimple);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         final int mode = MODE_AUTO;
 
@@ -777,92 +775,73 @@
         final var hardwareLatinIme = List.of(hardwareEnglish, hardwareFrench, hardwareItalian);
         final var hardwareSimpleIme = List.of(hardwareSimple);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         final int mode = MODE_RECENT;
 
         // Recency order is initialized to static order.
         assertNextOrder(controller, false /* forHardware */, mode,
                 items, List.of(latinIme, simpleIme));
-
         assertNextOrder(controller, true /* forHardware */, mode,
                 hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
 
         // User action on french IME.
         assertTrue("Recency updated for french IME", onUserAction(controller, french));
 
-        final var equalItems = new ArrayList<>(items);
-        final var otherItems = new ArrayList<>(items);
-        otherItems.remove(simple);
-
-        final var equalController = ControllerImpl.createFrom(controller, equalItems,
-                hardwareItems);
-        final var otherController = ControllerImpl.createFrom(controller, otherItems,
-                hardwareItems);
-
         final var recencyItems = List.of(french, english, italian, simple);
         final var recencyLatinIme = List.of(french, english, italian);
         final var recencySimpleIme = List.of(simple);
 
+        final var equalItems = new ArrayList<>(items);
+        controller.update(equalItems, hardwareItems);
+
+        // The order of non-hardware items remains unchanged when updated with equal items.
         assertNextOrder(controller, false /* forHardware */, mode,
                 recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
-        // The order of equal non-hardware items is unchanged.
-        assertNextOrder(equalController, false /* forHardware */, mode,
-                recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
-        // The order of other hardware items is reset.
-        assertNextOrder(otherController, false /* forHardware */, mode,
-                latinIme, List.of(latinIme));
-
-        // The order of hardware remains unchanged.
+        // The order of hardware items remains unchanged when only non-hardware items are updated.
         assertNextOrder(controller, true /* forHardware */, mode,
                 hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
 
-        assertNextOrder(equalController, true /* forHardware */, mode,
-                hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
+        final var otherItems = new ArrayList<>(items);
+        otherItems.remove(simple);
+        controller.update(otherItems, hardwareItems);
 
-        assertNextOrder(otherController, true /* forHardware */, mode,
+        // The order of non-hardware items is reset when updated with other items.
+        assertNextOrder(controller, false /* forHardware */, mode,
+                latinIme, List.of(latinIme));
+        // The order of hardware items remains unchanged when only non-hardware items are updated.
+        assertNextOrder(controller, true /* forHardware */, mode,
                 hardwareItems, List.of(hardwareLatinIme, hardwareSimpleIme));
 
         assertTrue("Recency updated for french hardware IME",
                 onUserAction(controller, hardwareFrench));
 
-        final var equalHardwareItems = new ArrayList<>(hardwareItems);
-        final var otherHardwareItems = new ArrayList<>(hardwareItems);
-        otherHardwareItems.remove(hardwareSimple);
-
-        final var equalHardwareController = ControllerImpl.createFrom(controller, items,
-                equalHardwareItems);
-        final var otherHardwareController = ControllerImpl.createFrom(controller, items,
-                otherHardwareItems);
-
         final var recencyHardwareItems =
                 List.of(hardwareFrench, hardwareEnglish, hardwareItalian, hardwareSimple);
         final var recencyHardwareLatinIme =
                 List.of(hardwareFrench, hardwareEnglish, hardwareItalian);
         final var recencyHardwareSimpleIme = List.of(hardwareSimple);
 
-        // The order of non-hardware items remains unchanged.
+        final var equalHardwareItems = new ArrayList<>(hardwareItems);
+        controller.update(otherItems, equalHardwareItems);
+
+        // The order of non-hardware items remains unchanged when only hardware items are updated.
         assertNextOrder(controller, false /* forHardware */, mode,
-                recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
-        assertNextOrder(equalHardwareController, false /* forHardware */, mode,
-                recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
-        assertNextOrder(otherHardwareController, false /* forHardware */, mode,
-                recencyItems, List.of(recencyLatinIme, recencySimpleIme));
-
+                latinIme, List.of(latinIme));
+        // The order of hardware items remains unchanged when updated with equal items.
         assertNextOrder(controller, true /* forHardware */, mode,
                 recencyHardwareItems, List.of(recencyHardwareLatinIme, recencyHardwareSimpleIme));
 
-        // The order of equal hardware items is unchanged.
-        assertNextOrder(equalHardwareController, true /* forHardware */, mode,
-                recencyHardwareItems, List.of(recencyHardwareLatinIme, recencyHardwareSimpleIme));
+        final var otherHardwareItems = new ArrayList<>(hardwareItems);
+        otherHardwareItems.remove(hardwareSimple);
+        controller.update(otherItems, otherHardwareItems);
 
-        // The order of other hardware items is reset.
-        assertNextOrder(otherHardwareController, true /* forHardware */, mode,
+        // The order of non-hardware items remains unchanged when only hardware items are updated.
+        assertNextOrder(controller, false /* forHardware */, mode,
+                latinIme, List.of(latinIme));
+        // The order of hardware items is reset when updated with other items.
+        assertNextOrder(controller, true /* forHardware */, mode,
                 hardwareLatinIme, List.of(hardwareLatinIme));
     }
 
@@ -882,8 +861,8 @@
         addTestImeSubtypeListItems(hardwareItems, "hardwareSwitchUnaware", "hardwareSwitchUnaware",
                 null, false /* supportsSwitchingToNextInputMethod*/);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         for (int mode = MODE_STATIC; mode <= MODE_AUTO; mode++) {
             assertNextOrder(controller, false /* forHardware */, false /* onlyCurrentIme */,
@@ -910,8 +889,7 @@
         addTestImeSubtypeListItems(hardwareItems, "HardwareIme", "HardwareIme",
                 List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, List.of(),
-                List.of());
+        final var controller = new InputMethodSubtypeSwitchingController();
 
         assertNextItemNoAction(controller, false /* forHardware */, items,
                 null /* expectedNext */);
@@ -940,8 +918,8 @@
         addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
                 List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */,
-                items, hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         assertNextItemNoAction(controller, false /* forHardware */, items,
                 null /* expectedNext */);
@@ -979,8 +957,8 @@
         addTestImeSubtypeListItems(unknownHardwareItems, "HardwareUnknownIme", "HardwareUnknownIme",
                 List.of("en", "fr", "it"), true /* supportsSwitchingToNextInputMethod */);
 
-        final var controller = ControllerImpl.createFrom(null /* currentInstance */, items,
-                hardwareItems);
+        final var controller = new InputMethodSubtypeSwitchingController();
+        controller.update(items, hardwareItems);
 
         assertTrue("Recency updated for french IME", onUserAction(controller, french));
 
@@ -1118,8 +1096,9 @@
      * @param allItems    the list of items across all IMEs.
      * @param perImeItems the list of lists of items per IME.
      */
-    private static void assertNextOrder(@NonNull ControllerImpl controller, boolean forHardware,
-            @SwitchMode int mode, boolean forward, @NonNull List<ImeSubtypeListItem> allItems,
+    private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+            boolean forHardware, @SwitchMode int mode, boolean forward,
+            @NonNull List<ImeSubtypeListItem> allItems,
             @NonNull List<List<ImeSubtypeListItem>> perImeItems) {
         assertNextOrder(controller, forHardware, false /* onlyCurrentIme */, mode,
                 forward, allItems);
@@ -1142,8 +1121,8 @@
      * @param allItems    the list of items across all IMEs.
      * @param perImeItems the list of lists of items per IME.
      */
-    private static void assertNextOrder(@NonNull ControllerImpl controller, boolean forHardware,
-            @SwitchMode int mode, @NonNull List<ImeSubtypeListItem> allItems,
+    private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
+            boolean forHardware, @SwitchMode int mode, @NonNull List<ImeSubtypeListItem> allItems,
             @NonNull List<List<ImeSubtypeListItem>> perImeItems) {
         assertNextOrder(controller, forHardware, false /* onlyCurrentIme */, mode,
                 true /* forward */, allItems);
@@ -1170,7 +1149,7 @@
      * @param forward        whether to search forwards or backwards in the list.
      * @param items          the list of items to verify, in the expected order.
      */
-    private static void assertNextOrder(@NonNull ControllerImpl controller,
+    private static void assertNextOrder(@NonNull InputMethodSubtypeSwitchingController controller,
             boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
             @NonNull List<ImeSubtypeListItem> items) {
         final int numItems = items.size();
@@ -1214,7 +1193,7 @@
      * @param item           the item to find the next value from.
      * @param expectedNext   the expected next value.
      */
-    private static void assertNextItem(@NonNull ControllerImpl controller,
+    private static void assertNextItem(@NonNull InputMethodSubtypeSwitchingController controller,
             boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
             @NonNull ImeSubtypeListItem item, @Nullable ImeSubtypeListItem expectedNext) {
         final var nextItem = getNextItem(controller, forHardware, onlyCurrentIme, mode, forward,
@@ -1234,15 +1213,16 @@
      * @return the next item found, otherwise {@code null}.
      */
     @Nullable
-    private static ImeSubtypeListItem getNextItem(@NonNull ControllerImpl controller,
-            boolean forHardware, boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
+    private static ImeSubtypeListItem getNextItem(
+            @NonNull InputMethodSubtypeSwitchingController controller, boolean forHardware,
+            boolean onlyCurrentIme, @SwitchMode int mode, boolean forward,
             @NonNull ImeSubtypeListItem item) {
         final var subtype = item.mSubtypeName != null
                 ? createTestSubtype(item.mSubtypeName.toString()) : null;
         return forHardware
                 ? controller.getNextInputMethodForHardware(
                         onlyCurrentIme, item.mImi, subtype, mode, forward)
-                : controller.getNextInputMethod(
+                : controller.getNextInputMethodLocked(
                         onlyCurrentIme, item.mImi, subtype, mode, forward);
     }
 
@@ -1255,8 +1235,9 @@
      * @param items        the list of items to verify.
      * @param expectedNext the expected next item.
      */
-    private void assertNextItemNoAction(@NonNull ControllerImpl controller, boolean forHardware,
-            @NonNull List<ImeSubtypeListItem> items, @Nullable ImeSubtypeListItem expectedNext) {
+    private void assertNextItemNoAction(@NonNull InputMethodSubtypeSwitchingController controller,
+            boolean forHardware, @NonNull List<ImeSubtypeListItem> items,
+            @Nullable ImeSubtypeListItem expectedNext) {
         for (var item : items) {
             for (int mode = MODE_STATIC; mode <= MODE_AUTO; mode++) {
                 assertNextItem(controller, forHardware, false /* onlyCurrentIme */, mode,
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 6fd21f7..75db316 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -88,3 +88,10 @@
         " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
         " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
 }
+
+test_module_config_host {
+    name: "PackageManagerServiceHostTests_test_overlayactorvisibilitytest",
+    base: "PackageManagerServiceHostTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pm.test.OverlayActorVisibilityTest"],
+}
diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp
index 598e273..e26213e 100644
--- a/services/tests/PackageManagerServiceTests/server/Android.bp
+++ b/services/tests/PackageManagerServiceTests/server/Android.bp
@@ -164,3 +164,25 @@
         "done && " +
         "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
 }
+
+test_module_config {
+    name: "PackageManagerServiceServerTests_server_pm_Presubmit",
+    base: "PackageManagerServiceServerTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.pm."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "PackageManagerServiceServerTests_server_pm_Postsubmit",
+    base: "PackageManagerServiceServerTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.pm."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index c93f482..db88b16 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -47,3 +47,10 @@
     platform_apis: true,
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "PackageManagerServiceUnitTests_verify_domain",
+    base: "PackageManagerServiceUnitTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pm.test.verify.domain"],
+}
diff --git a/services/tests/appfunctions/OWNERS b/services/tests/appfunctions/OWNERS
new file mode 100644
index 0000000..7fa8917
--- /dev/null
+++ b/services/tests/appfunctions/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1627156
+include platform/frameworks/base:/core/java/android/app/appfunctions/OWNERS
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 3bafe72..61350bf 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -56,3 +56,13 @@
         enabled: false,
     },
 }
+
+test_module_config {
+    name: "DisplayServiceTests_server_display",
+    base: "DisplayServiceTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.display"],
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 2a458c42..312df43 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -194,7 +194,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0 /* brightness= */, false /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
     }
 
     @Test
@@ -300,7 +300,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         // There should be a user data point added to the mapper.
         verify(mBrightnessMappingStrategy).addUserDataPoint(/* lux= */ 1000f,
@@ -324,7 +324,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         //Recalculating the spline with RBC enabled, verifying that the short term model is reset,
         //and the interaction is learnt in short term model
@@ -358,7 +358,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
 
@@ -398,7 +398,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0.51f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         when(mBrightnessMappingStrategy.shouldResetShortTermModel(
                 anyFloat(), anyFloat())).thenReturn(true);
@@ -438,7 +438,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
         when(mBrightnessMappingStrategy.getUserBrightness()).thenReturn(0.5f);
@@ -484,7 +484,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
                 /* brightness= */ 0.5f, /* userChangedBrightness= */ true, /* adjustment= */ 0,
                 /* userChanged= */ false, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         when(mBrightnessMappingStrategy.getShortTermModelTimeout()).thenReturn(2000L);
 
@@ -571,7 +571,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         // There should be a user data point added to the mapper.
         verify(mBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
@@ -598,7 +598,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 0.5f /* brightness= */, true /* userChangedBrightness= */, 0 /* adjustment= */,
                 false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         // Ensure we use the correct mapping strategy
         verify(mIdleBrightnessMappingStrategy, times(1)).addUserDataPoint(/* lux= */ 1000f,
@@ -759,7 +759,8 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ true);
         assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
         // The raw brightness value should not have throttling applied
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
@@ -770,7 +771,8 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ true);
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
     }
@@ -907,13 +909,15 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ false);
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false);
         verify(mBrightnessMappingStrategy, never()).clearUserDataPoints();
 
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ true);
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ true);
         verify(mBrightnessMappingStrategy).clearUserDataPoints();
     }
 
@@ -1100,7 +1104,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
                 /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
 
         // Send a new sensor value
         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
@@ -1112,6 +1116,77 @@
     }
 
     @Test
+    public void testAutoBrightnessInDoze_useNormalBrightnessForDozeFalse_scaleScreenOn()
+            throws Exception {
+        when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 0.3f as a brightness value
+        float lux = 100.0f;
+        // Brightness as float (from 0.0f to 1.0f)
+        float normalizedBrightness = 0.3f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+                /* category= */ anyInt())).thenReturn(normalizedBrightness);
+        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+        // Set policy to DOZE
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+                /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+                /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
+                /* useNormalBrightnessForDoze= */ false, /* shouldResetShortTermModel= */ true);
+
+        // Send a new sensor value
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+        // The brightness should be scaled by the doze factor
+        assertEquals(normalizedBrightness * DOZE_SCALE_FACTOR,
+                mController.getAutomaticScreenBrightness(
+                        /* brightnessEvent= */ null), EPSILON);
+    }
+
+    @Test
+    public void testAutoBrightnessInDoze_useNormalBrightnessForDozeTrue_notScaleScreenOn()
+            throws Exception {
+        when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 0.3f as a brightness value
+        float lux = 100.0f;
+        // Brightness as float (from 0.0f to 1.0f)
+        float normalizedBrightness = 0.3f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
+                /* category= */ anyInt())).thenReturn(normalizedBrightness);
+        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+
+        // Set policy to DOZE
+        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
+                /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
+                /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
+                /* useNormalBrightnessForDoze= */ true, /* shouldResetShortTermModel= */ true);
+
+        // Send a new sensor value
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
+
+        // The brightness should not be scaled by the doze factor
+        assertEquals(normalizedBrightness,
+                mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
+    }
+
+    @Test
     public void testAutoBrightnessInDoze_ShouldNotScaleIfUsingDozeCurve() throws Exception {
         ArgumentCaptor<SensorEventListener> listenerCaptor =
                 ArgumentCaptor.forClass(SensorEventListener.class);
@@ -1136,39 +1211,7 @@
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
                 /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_DOZE,
-                /* shouldResetShortTermModel= */ true);
-
-        // Send a new sensor value
-        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
-
-        // The brightness should not be scaled by the doze factor
-        assertEquals(normalizedBrightness,
-                mController.getAutomaticScreenBrightness(/* brightnessEvent= */ null), EPSILON);
-    }
-
-    @Test
-    public void testAutoBrightnessInDoze_ShouldNotScaleIfScreenOn() throws Exception {
-        ArgumentCaptor<SensorEventListener> listenerCaptor =
-                ArgumentCaptor.forClass(SensorEventListener.class);
-        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor),
-                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
-        SensorEventListener listener = listenerCaptor.getValue();
-
-        // Set up system to return 0.3f as a brightness value
-        float lux = 100.0f;
-        // Brightness as float (from 0.0f to 1.0f)
-        float normalizedBrightness = 0.3f;
-        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux)).thenReturn(lux);
-        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
-        when(mBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
-                /* category= */ anyInt())).thenReturn(normalizedBrightness);
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
-
-        // Set policy to DOZE
-        mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
-                /* brightness= */ 0, /* userChangedBrightness= */ false, /* adjustment= */ 0,
-                /* userChanged= */ false, DisplayPowerRequest.POLICY_DOZE, Display.STATE_ON,
-                /* shouldResetShortTermModel= */ true);
+                /* useNormalBrightnessForDoze= */ true, /* shouldResetShortTermModel= */ true);
 
         // Send a new sensor value
         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
index 1f3184d..f589a2c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessRangeControllerTest.kt
@@ -42,31 +42,31 @@
     private val mockToken = mock<IBinder>()
 
     @Test
-    fun `returns HBC max brightness if HBM supported and ON`() {
+    fun testMaxBrightness_HbmSupportedAndOn() {
         val controller = createController()
         assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
     }
 
     @Test
-    fun `returns NBC max brightness if device does not support HBM`() {
+    fun testMaxBrightness_HbmNotSupported() {
         val controller = createController(hbmSupported = false)
         assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
     }
 
     @Test
-    fun `returns NBC max brightness if HBM not allowed`() {
+    fun testMaxBrightness_HbmNotAllowed() {
         val controller = createController(hbmAllowed = false)
         assertThat(controller.currentBrightnessMax).isEqualTo(NORMAL_BRIGHTNESS_LOW)
     }
 
     @Test
-    fun `returns HBC max brightness if NBM is disabled`() {
+    fun testMaxBrightness_HbmDisabledAndNotAllowed() {
         val controller = createController(nbmEnabled = false, hbmAllowed = false)
         assertThat(controller.currentBrightnessMax).isEqualTo(MAX_BRIGHTNESS)
     }
 
     @Test
-    fun `returns HBC max brightness if lower than NBC max brightness`() {
+    fun testMaxBrightness_transitionPointLessThanCurrentNbmLimit() {
         val controller = createController(
             hbmAllowed = false,
             hbmMaxBrightness = TRANSITION_POINT,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index f690b1b..2d4a29b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.hardware.display.BrightnessInfo;
+
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -112,7 +114,10 @@
                 .append("\n    mBrightnessAdjustmentFlag:")
                 .append(displayBrightnessState.getBrightnessAdjustmentFlag())
                 .append("\n    mIsUserInitiatedChange:")
-                .append(displayBrightnessState.isUserInitiatedChange());
+                .append(displayBrightnessState.isUserInitiatedChange())
+                .append("\n    mBrightnessMaxReason:")
+                .append(BrightnessInfo.briMaxReasonToString(
+                        displayBrightnessState.getBrightnessMaxReason()));
         return sb.toString();
     }
 }
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 d450683..fd05b26 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -263,7 +263,9 @@
                 mDisplayDeviceConfig.getPowerThrottlingConfigData();
         assertNotNull(powerThrottlingConfigData);
         assertEquals(0.1f, powerThrottlingConfigData.brightnessLowestCapAllowed, SMALL_DELTA);
-        assertEquals(10, powerThrottlingConfigData.pollingWindowMillis);
+        assertEquals(15f, powerThrottlingConfigData.customAnimationRateSec, SMALL_DELTA);
+        assertEquals(20000, powerThrottlingConfigData.pollingWindowMaxMillis);
+        assertEquals(10000, powerThrottlingConfigData.pollingWindowMinMillis);
     }
 
     @Test
@@ -1295,7 +1297,9 @@
     private String getPowerThrottlingConfig() {
         return  "<powerThrottlingConfig >\n"
                 +       "<brightnessLowestCapAllowed>0.1</brightnessLowestCapAllowed>\n"
-                +       "<pollingWindowMillis>10</pollingWindowMillis>\n"
+                +       "<customAnimationRateSec>15</customAnimationRateSec>\n"
+                +       "<pollingWindowMaxMillis>20000</pollingWindowMaxMillis>\n"
+                +       "<pollingWindowMinMillis>10000</pollingWindowMinMillis>\n"
                 +       "<powerThrottlingMap>\n"
                 +           "<powerThrottlingPoint>\n"
                 +               "<thermalStatus>light</thermalStatus>\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 2b03dc4..5bb8ded 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -64,12 +64,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions.LaunchCookie;
 import android.app.PropertyInvalidatedCache;
 import android.companion.virtual.IVirtualDevice;
 import android.companion.virtual.IVirtualDeviceManager;
 import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.flags.Flags;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -359,6 +359,7 @@
     @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
     @Mock PackageManagerInternal mMockPackageManagerInternal;
     @Mock DisplayManagerInternal mMockDisplayManagerInternal;
+    @Mock ActivityManagerInternal mMockActivityManagerInternal;
     @Mock DisplayAdapter mMockDisplayAdapter;
 
     @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@@ -374,7 +375,6 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
-        mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
 
         mLocalServiceKeeperRule.overrideLocalService(
                 InputManagerInternal.class, mMockInputManagerInternal);
@@ -390,6 +390,8 @@
                 PackageManagerInternal.class, mMockPackageManagerInternal);
         mLocalServiceKeeperRule.overrideLocalService(
                 DisplayManagerInternal.class, mMockDisplayManagerInternal);
+        mLocalServiceKeeperRule.overrideLocalService(
+                ActivityManagerInternal.class, mMockActivityManagerInternal);
         Display display = mock(Display.class);
         when(display.getDisplayAdjustments()).thenReturn(new DisplayAdjustments());
         when(display.getBrightnessInfo()).thenReturn(mock(BrightnessInfo.class));
@@ -1298,44 +1300,11 @@
     }
 
     /**
-     * Tests that it's not allowed to create an auto-mirror virtual display when display mirroring
-     * is not supported in a virtual device.
-     */
-    @Test
-    public void createAutoMirrorDisplay_virtualDeviceDoesntSupportMirroring_throwsException()
-            throws Exception {
-        mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
-        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
-        DisplayManagerInternal localService = displayManager.new LocalService();
-        registerDefaultDisplays(displayManager);
-        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
-        when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn(
-                PackageManager.PERMISSION_DENIED);
-        IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
-        when(virtualDevice.getDeviceId()).thenReturn(1);
-        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
-
-        final VirtualDisplayConfig.Builder builder =
-                new VirtualDisplayConfig.Builder(VIRTUAL_DISPLAY_NAME, 600, 800, 320)
-                        .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
-                        .setUniqueId("uniqueId --- mirror display");
-        assertThrows(SecurityException.class, () -> {
-            localService.createVirtualDisplay(
-                    builder.build(),
-                    mMockAppToken /* callback */,
-                    virtualDevice /* virtualDeviceToken */,
-                    mock(DisplayWindowPolicyController.class),
-                    PACKAGE_NAME);
-        });
-    }
-
-    /**
      * Tests that the virtual display is added to the default display group when created with
      * VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR using a virtual device.
      */
     @Test
     public void createAutoMirrorVirtualDisplay_addsDisplayToDefaultDisplayGroup() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
         registerDefaultDisplays(displayManager);
@@ -1368,7 +1337,6 @@
      */
     @Test
     public void createAutoMirrorVirtualDisplay_mirrorsDefaultDisplay() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
         registerDefaultDisplays(displayManager);
@@ -1400,7 +1368,6 @@
      */
     @Test
     public void createOwnContentOnlyVirtualDisplay_doesNotMirrorAnyDisplay() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
         registerDefaultDisplays(displayManager);
@@ -1436,7 +1403,6 @@
      */
     @Test
     public void createAutoMirrorVirtualDisplay_flagAlwaysUnlockedNotSet() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
         registerDefaultDisplays(displayManager);
@@ -1472,7 +1438,6 @@
      */
     @Test
     public void createAutoMirrorVirtualDisplay_flagPresentationNotSet() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
         registerDefaultDisplays(displayManager);
@@ -2119,6 +2084,31 @@
     }
 
     /**
+     * Tests that the DisplayInfo is updated correctly with a render frame rate even if it not
+     * a divisor of the peak refresh rate.
+     */
+    @Test
+    public void testDisplayInfoRenderFrameRateNonPeakDivisor() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        DisplayManagerService.BinderService displayManagerBinderService =
+                displayManager.new BinderService();
+        registerDefaultDisplays(displayManager);
+        displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+        FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
+                new float[]{120f}, new float[]{240f});
+        int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+                displayDevice);
+        DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+        assertEquals(120f, displayInfo.getRefreshRate(), 0.01f);
+
+        updateRenderFrameRate(displayManager, displayDevice, 80f);
+        displayInfo = displayManagerBinderService.getDisplayInfo(displayId);
+        assertEquals(80f, displayInfo.getRefreshRate(), 0.01f);
+    }
+
+    /**
      * Tests that the mode reflects the render frame rate is in compat mode
      */
     @Test
@@ -3387,13 +3377,26 @@
     }
 
     private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
-
                                                       float[] refreshRates) {
         return createFakeDisplayDevice(displayManager, refreshRates, Display.TYPE_UNKNOWN);
     }
 
     private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
                                                       float[] refreshRates,
+                                                      float[] vsyncRates) {
+        return createFakeDisplayDevice(displayManager, refreshRates, vsyncRates,
+                Display.TYPE_UNKNOWN);
+    }
+
+    private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
+            float[] refreshRates,
+            int displayType) {
+        return createFakeDisplayDevice(displayManager, refreshRates, refreshRates, displayType);
+    }
+
+    private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
+                                                      float[] refreshRates,
+                                                      float[] vsyncRates,
                                                       int displayType) {
         FakeDisplayDevice displayDevice = new FakeDisplayDevice();
         DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
@@ -3402,7 +3405,8 @@
         displayDeviceInfo.supportedModes = new Display.Mode[refreshRates.length];
         for (int i = 0; i < refreshRates.length; i++) {
             displayDeviceInfo.supportedModes[i] =
-                    new Display.Mode(i + 1, width, height, refreshRates[i]);
+                    new Display.Mode(i + 1, width, height, refreshRates[i], vsyncRates[i],
+                            new float[0], new int[0]);
         }
         displayDeviceInfo.modeId = 1;
         displayDeviceInfo.type = displayType;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
index 30c384a..5e868a3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayOffloadSessionImplTest.java
@@ -38,7 +38,7 @@
     private DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
 
     @Mock
-    private DisplayPowerControllerInterface mDisplayPowerController;
+    private DisplayPowerController mDisplayPowerController;
 
     private DisplayOffloadSessionImpl mSession;
 
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 b5278a5..d0aec3b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1014,7 +1014,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ false
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController)
                 .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1040,7 +1041,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+                Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController)
                 .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1070,7 +1072,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+                Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController)
                 .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
@@ -1093,7 +1096,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ false
+                Display.STATE_ON, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController)
                 .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
@@ -1116,7 +1120,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF,
-                Display.STATE_OFF, /* shouldResetShortTermModel= */ false
+                Display.STATE_OFF, /* useNormalBrightnessForDoze */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController).setAutoBrightnessEnabled(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1142,7 +1147,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+                Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController).setAutoBrightnessEnabled(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1172,7 +1178,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE,
-                Display.STATE_DOZE, /* shouldResetShortTermModel= */ false
+                Display.STATE_DOZE, /* useNormalBrightnessForDoze= */ false,
+                /* shouldResetShortTermModel= */ false
         );
         verify(mHolder.hbmController).setAutoBrightnessEnabled(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
@@ -1196,7 +1203,8 @@
                 /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT,
                 /* userChangedBrightness= */ false, /* adjustment= */ 0,
                 /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, /* shouldResetShortTermModel= */ false
+                Display.STATE_ON, /* useNormalBrightnessForDoze */ false,
+                /* shouldResetShortTermModel= */ false
         );
 
         // HBM should be allowed for the follower display
@@ -1613,16 +1621,21 @@
         advanceTime(1); // Run updatePowerState
 
         reset(mHolder.wakelockController);
+        when(mHolder.wakelockController
+                .acquireWakelock(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE))
+                .thenReturn(true);
         mHolder.dpc.overrideDozeScreenState(
                 supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
-        advanceTime(1); // Run updatePowerState
 
         // Should get a wakelock to notify powermanager
-        verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock(
-                eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS));
+        verify(mHolder.wakelockController).acquireWakelock(
+                eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
 
+        advanceTime(1); // Run updatePowerState
         verify(mHolder.displayPowerState)
                 .setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
+        verify(mHolder.wakelockController).releaseWakelock(
+                eq(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE));
     }
 
     @Test
@@ -1955,6 +1968,7 @@
                 /* userChangedAutoBrightnessAdjustment= */ anyBoolean(),
                 /* displayPolicy= */ anyInt(),
                 /* displayState= */ anyInt(),
+                /* useNormalBrightnessForDoze= */ anyBoolean(),
                 /* shouldResetShortTermModel= */ anyBoolean());
         verify(mBrightnessTrackerMock, never()).notifyBrightnessChanged(
                 /* brightness= */ anyFloat(),
@@ -2040,6 +2054,75 @@
     }
 
     @Test
+    public void testManualBrightness_stateOnPolicyDozeUseNormalBrightnessForDozeFalse_brightnessDoze() {
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        float brightness = 0.277f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+        when(mHolder.hbmController.getCurrentBrightnessMax())
+                .thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.dozeScreenState = Display.STATE_ON;
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        dpr.useNormalBrightnessForDoze = false;
+        // Confirm using doze brightness for dozing device.
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState, initialize
+
+        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+        listener.onBrightnessChanged(brightness);
+        advanceTime(1); // Send messages, run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(brightness * DOZE_SCALE_FACTOR),
+                /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+                /* ignoreAnimationLimits= */ anyBoolean());
+    }
+
+    @Test
+    public void testManualBrightness_stateOnPolicyDozeUseNormalBrightnessForDozeTrue_brightnessNormal() {
+        when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+        when(mDisplayManagerFlagsMock.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+        mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        float brightness = 0.277f;
+        when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+        when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+        when(mHolder.hbmController.getCurrentBrightnessMax())
+                .thenReturn(PowerManager.BRIGHTNESS_MAX);
+        when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
+
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        dpr.dozeScreenState = Display.STATE_ON;
+        dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+        dpr.useNormalBrightnessForDoze = true;
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Run updatePowerState, initialize
+
+        ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+                ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+        verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+        BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+        listener.onBrightnessChanged(brightness);
+        advanceTime(1); // Send messages, run updatePowerState
+
+        verify(mHolder.animator).animateTo(eq(brightness),
+                /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+                /* ignoreAnimationLimits= */ anyBoolean());
+    }
+
+    @Test
     public void testDozeManualBrightness_AbcIsNull() {
         when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
@@ -2130,6 +2213,20 @@
                 /* ignoreAnimationLimits= */ anyBoolean());
     }
 
+    @Test
+    public void testManualBrightnessModeSavesBrightness() {
+        DisplayPowerRequest dpr = new DisplayPowerRequest();
+        mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+        advanceTime(1); // Initialize
+
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        advanceTime(1);
+
+        verify(mHolder.brightnessSetting).saveIfNeeded();
+    }
+
     /**
      * Creates a mock and registers it to {@link LocalServices}.
      */
@@ -2490,7 +2587,7 @@
         BrightnessClamperController getBrightnessClamperController(Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data, Context context,
-                DisplayManagerFlags flags, SensorManager sensorManager) {
+                DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
             return mClamperController;
         }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
index 33d3020..a684fdb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerStateTest.kt
@@ -60,7 +60,7 @@
     }
 
     @Test
-    fun `destroys ColorFade on stop`() {
+    fun destroysColorFadeOnStop() {
         displayPowerState.stop()
         val runnableCaptor = argumentCaptor<Runnable>()
 
@@ -71,13 +71,13 @@
     }
 
     @Test
-    fun `GIVEN not prepared WHEN draw runnable is called THEN colorFade not drawn`() {
+    fun testColorFadeDraw_notPrepared() {
         displayPowerState.mColorFadeDrawRunnable.run()
 
         verify(mockColorFade, never()).draw(anyFloat())
     }
     @Test
-    fun `GIVEN prepared WHEN draw runnable is called THEN colorFade is drawn`() {
+    fun testColorFadeDraw_prepared() {
         displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
         clearInvocations(mockColorFade)
 
@@ -87,7 +87,7 @@
     }
 
     @Test
-    fun `GIVEN prepared AND stopped WHEN draw runnable is called THEN colorFade is not drawn`() {
+    fun testColorFadeDraw_preparedAndStopped() {
         displayPowerState.prepareColorFade(mockContext, ColorFade.MODE_FADE)
         clearInvocations(mockColorFade)
         displayPowerState.stop()
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 82acaf8..f728168 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -258,6 +258,7 @@
 
         when(mMockedLogicalDisplay.isEnabledLocked()).thenReturn(false);
         mExternalDisplayPolicy.handleExternalDisplayConnectedLocked(mMockedLogicalDisplay);
+        mHandler.flush();
         verify(mMockedInjector, never()).sendExternalDisplayEventLocked(any(), anyInt());
         verify(mMockedDisplayNotificationManager, times(2))
                 .onHighTemperatureExternalDisplayNotAllowed();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index a7e0ebd..120cc84 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -118,7 +118,7 @@
     @Mock
     private DisplayManagerFlags mFlags;
     @Mock
-    private DisplayPowerControllerInterface mMockedDisplayPowerController;
+    private DisplayPowerController mMockedDisplayPowerController;
 
     private Handler mHandler;
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
index c23d4b1..019b70e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/WakelockControllerTest.java
@@ -64,6 +64,8 @@
                 "[" + DISPLAY_ID + "]prox negative");
         assertEquals(mWakelockController.getSuspendBlockerProxDebounceId(),
                 "[" + DISPLAY_ID + "]prox debounce");
+        assertEquals(mWakelockController.getSuspendBlockerOverrideDozeScreenState(),
+                "[" + DISPLAY_ID + "]override doze screen state");
     }
 
     @Test
@@ -162,6 +164,28 @@
     }
 
     @Test
+    public void acquireOverrideDozeScreenStateSuspendBlocker() throws Exception {
+        // Acquire the suspend blocker
+        verifyWakelockAcquisitionAndReaquisition(WakelockController
+                        .WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+                () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+        // Verify acquire happened only once
+        verify(mDisplayPowerCallbacks, times(1))
+                .acquireSuspendBlocker(mWakelockController
+                        .getSuspendBlockerOverrideDozeScreenState());
+
+        // Release the suspend blocker
+        verifyWakelockReleaseAndRerelease(WakelockController.WAKE_LOCK_OVERRIDE_DOZE_SCREEN_STATE,
+                () -> mWakelockController.isOverrideDozeScreenStateAcquired());
+
+        // Verify suspend blocker was released only once
+        verify(mDisplayPowerCallbacks, times(1))
+                .releaseSuspendBlocker(mWakelockController
+                        .getSuspendBlockerOverrideDozeScreenState());
+    }
+
+    @Test
     public void proximityPositiveRunnableWorksAsExpected() {
         // Acquire the suspend blocker twice
         assertTrue(mWakelockController.acquireWakelock(
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 639d06d..a44c517 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
@@ -187,7 +187,7 @@
     }
 
     @Test
-    public void selectStrategySelectsDozeStrategyWhenValid() {
+    public void selectStrategyWhenValid_useNormalBrightnessForDozeFalse_SelectsDozeStrategy() {
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
                 DisplayManagerInternal.DisplayPowerRequest.class);
         displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
@@ -201,6 +201,22 @@
     }
 
     @Test
+    public void selectStrategyWhenValid_useNormalBrightnessForDozeTrue_doNotSelectsDozeStrategy() {
+        when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+                DisplayManagerInternal.DisplayPowerRequest.class);
+        displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+        displayPowerRequest.dozeScreenBrightness = 0.2f;
+        displayPowerRequest.useNormalBrightnessForDoze = true;
+        when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn(
+                DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING);
+        assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+                        new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE,
+                                0.1f, false, mDisplayOffloadSession)),
+                mDozeBrightnessModeStrategy);
+    }
+
+    @Test
     public void selectStrategyDoesNotSelectDozeStrategyWhenInvalidBrightness() {
         DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
                 DisplayManagerInternal.DisplayPowerRequest.class);
@@ -353,7 +369,8 @@
                 mAutomaticBrightnessStrategy);
         verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON,
                 true, BrightnessReason.REASON_UNKNOWN,
-                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f, false);
+                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+                /* useNormalBrightnessForDoze= */ false, 0.1f, false);
     }
 
     @Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index 0ce9233..f9dc122 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -31,7 +31,6 @@
 
 import android.content.Context;
 import android.hardware.SensorManager;
-import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -161,12 +160,6 @@
     }
 
     @Test
-    public void testMaxReasonIsNoneOnInit() {
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
-                mClamperController.getBrightnessMaxReason());
-    }
-
-    @Test
     public void testOnDisplayChanged_DelegatesToClamper() {
         mClamperController.onDisplayChanged(mMockDisplayDeviceData);
 
@@ -365,7 +358,7 @@
 
     private BrightnessClamperController createBrightnessClamperController() {
         return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener,
-                mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager);
+                mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager, 0);
     }
 
     interface TestDisplayListenerModifier extends BrightnessStateModifier,
@@ -403,7 +396,7 @@
                 Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
                 BrightnessClamperController.DisplayDeviceData data,
-                DisplayManagerFlags flags, Context context) {
+                DisplayManagerFlags flags, Context context, float currentBrightness) {
             mCapturedChangeListener = clamperChangeListener;
             return mClampers;
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
index b3f33ad..c4898da 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessPowerClamperTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import android.os.IThermalService;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.Temperature;
@@ -58,12 +59,18 @@
     private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
             new FakeDeviceConfigInterface();
     private final TestHandler mTestHandler = new TestHandler(null);
+    private final TestInjector mTestInjector = new TestInjector();
     private BrightnessPowerClamper mClamper;
+    private final float mCurrentBrightness = 0.6f;
+    private PowerChangeListener mPowerChangeListener;
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mClamper = new BrightnessPowerClamper(new TestInjector(), mTestHandler,
-                mMockClamperChangeListener, new TestPowerData());
+        mClamper = new BrightnessPowerClamper(mTestInjector, mTestHandler,
+                mMockClamperChangeListener, new TestPowerData(), mCurrentBrightness);
+        mPowerChangeListener = mClamper.getPowerChangeListener();
+        mPmicMonitor = mTestInjector.getPmicMonitor(mPowerChangeListener, null, 5, 10);
+        mPmicMonitor.setPowerChangeListener(mPowerChangeListener);
         mTestHandler.flush();
     }
 
@@ -79,36 +86,27 @@
     }
 
     @Test
-    public void testPowerThrottlingNoOngoingAnimation() throws RemoteException {
-        mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
+    public void testPowerThrottlingWithThermalLevelLight() throws RemoteException {
+        mPmicMonitor.setThermalStatus(Temperature.THROTTLING_LIGHT);
         mTestHandler.flush();
         assertFalse(mClamper.isActive());
         assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
 
         // update a new device config for power-throttling.
         mClamper.onDisplayChanged(new TestPowerData(
-                List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 100f))));
+                List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_LIGHT, 100f))));
 
         mPmicMonitor.setAvgPowerConsumed(200f);
         float expectedBrightness = 0.5f;
-        expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
+        expectedBrightness = expectedBrightness * mCurrentBrightness;
 
         mTestHandler.flush();
         // Assume current brightness as max, as there is no throttling.
         assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-        mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
-        // update a new device config for power-throttling.
-        mClamper.onDisplayChanged(new TestPowerData(
-                List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
-        mPmicMonitor.setAvgPowerConsumed(100f);
-        expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
-        mTestHandler.flush();
-        assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
     }
 
     @Test
-    public void testPowerThrottlingWithOngoingAnimation() throws RemoteException {
+    public void testPowerThrottlingWithThermalLevelSevere() throws RemoteException {
         mPmicMonitor.setThermalStatus(Temperature.THROTTLING_SEVERE);
         mTestHandler.flush();
         assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -119,20 +117,10 @@
 
         mPmicMonitor.setAvgPowerConsumed(200f);
         float expectedBrightness = 0.5f;
-        expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+        expectedBrightness = expectedBrightness * mCurrentBrightness;
         mTestHandler.flush();
         // Assume current brightness as max, as there is no throttling.
         assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-        mPmicMonitor.setThermalStatus(Temperature.THROTTLING_CRITICAL);
-        // update a new device config for power-throttling.
-        mClamper.onDisplayChanged(new TestPowerData(
-                List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 50f))));
-
-        mPmicMonitor.setAvgPowerConsumed(100f);
-        expectedBrightness = 0.5f * PowerManager.BRIGHTNESS_MAX;
-        mTestHandler.flush();
-        assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
     }
 
     @Test
@@ -148,8 +136,7 @@
 
         mPmicMonitor.setAvgPowerConsumed(200f);
         float expectedBrightness = 0.5f;
-        expectedBrightness = expectedBrightness * PowerManager.BRIGHTNESS_MAX;
-
+        expectedBrightness = expectedBrightness * mCurrentBrightness;
         mTestHandler.flush();
 
         assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
@@ -169,10 +156,11 @@
 
     private static class TestPmicMonitor extends PmicMonitor {
         private Temperature mCurrentTemperature;
-        private final PowerChangeListener mListener;
-        TestPmicMonitor(PowerChangeListener listener, int pollingTime) {
-            super(listener, pollingTime);
-            mListener = listener;
+        private PowerChangeListener mListener;
+        TestPmicMonitor(PowerChangeListener listener,
+                        IThermalService thermalService,
+                        int pollingTimeMax, int pollingTimeMin) {
+            super(listener, thermalService, pollingTimeMax, pollingTimeMin);
         }
         public void setAvgPowerConsumed(float power) {
             int status = mCurrentTemperature.getStatus();
@@ -181,13 +169,18 @@
         public void setThermalStatus(@Temperature.ThrottlingStatus int status) {
             mCurrentTemperature = new Temperature(100, Temperature.TYPE_SKIN, "test_temp", status);
         }
+        public void setPowerChangeListener(PowerChangeListener listener) {
+            mListener = listener;
+        }
     }
 
     private class TestInjector extends BrightnessPowerClamper.Injector {
         @Override
         TestPmicMonitor getPmicMonitor(PowerChangeListener listener,
-                int pollingTime) {
-            mPmicMonitor = new TestPmicMonitor(listener, pollingTime);
+                                       IThermalService thermalService,
+                                       int minPollingTimeMillis, int maxPollingTimeMillis) {
+            mPmicMonitor = new TestPmicMonitor(listener, thermalService, maxPollingTimeMillis,
+                    minPollingTimeMillis);
             return mPmicMonitor;
         }
 
@@ -216,7 +209,7 @@
             mUniqueDisplayId = uniqueDisplayId;
             mDataId = dataId;
             mData = PowerThrottlingData.create(data);
-            mConfigData = new PowerThrottlingConfigData(0.1f, 10);
+            mConfigData = new PowerThrottlingConfigData(0.1f, 10, 20, 10);
         }
 
         @NonNull
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
index 4e10b98..e386542 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy2Test.java
@@ -56,6 +56,9 @@
 @RunWith(AndroidJUnit4.class)
 public class AutomaticBrightnessStrategy2Test {
     private static final int DISPLAY_ID = 0;
+
+    private static final boolean DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE = false;
+
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
@@ -78,7 +81,8 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN);
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f);
-        mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy2(mContext, DISPLAY_ID);
+        mAutomaticBrightnessStrategy =
+                new AutomaticBrightnessStrategy2(mContext, DISPLAY_ID);
 
         mBrightnessConfiguration = new BrightnessConfiguration.Builder(
                 new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build();
@@ -106,15 +110,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -130,15 +137,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -152,17 +162,20 @@
         int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
         float lastUserSetBrightness = 0.2f;
         boolean userSetBrightnessChanged = true;
+        boolean useNormalBrightnessForDoze = false;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -178,15 +191,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -200,19 +216,21 @@
         int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
         float lastUserSetBrightness = 0.2f;
         boolean userSetBrightnessChanged = true;
+        boolean useNormalBrightnessForDoze = false;
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.4f,
-                        /* userChangedAutoBrightnessAdjustment= */ true, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -226,19 +244,20 @@
         int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
         float lastUserSetBrightness = 0.2f;
         boolean userSetBrightnessChanged = true;
+        boolean useNormalBrightnessForDoze = false;
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.4f,
-                        /* userChangedAutoBrightnessAdjustment= */ true, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -257,14 +276,16 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, pendingBrightnessAdjustment,
                         /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
                         /* shouldResetShortTermModel */ true);
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -280,19 +301,20 @@
         boolean userSetBrightnessChanged = true;
         int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
         float pendingBrightnessAdjustment = 0.1f;
+        boolean useNormalBrightnessForDoze = false;
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, pendingBrightnessAdjustment,
                         /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
-                        /* shouldResetShortTermModel */ true);
+                        useNormalBrightnessForDoze, /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -312,13 +334,15 @@
         mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
         setTemporaryAutoBrightnessAdjustment(temporaryAutoBrightnessAdjustments);
         mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
-                lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
-                autoBrightnessState);
+                lastUserSetScreenBrightness, policy, targetDisplayState,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                brightnessConfiguration, autoBrightnessState);
         verify(mAutomaticBrightnessController).configure(autoBrightnessState,
                 brightnessConfiguration,
                 lastUserSetScreenBrightness,
                 userSetBrightnessChanged, temporaryAutoBrightnessAdjustments,
                 /* userChangedAutoBrightnessAdjustment= */ false, policy, targetDisplayState,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
                 /* shouldResetShortTermModel= */ true);
         assertTrue(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
         assertFalse(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
@@ -328,8 +352,9 @@
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(null);
         mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
         mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
-                lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
-                autoBrightnessState);
+                lastUserSetScreenBrightness, policy, targetDisplayState,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                brightnessConfiguration, autoBrightnessState);
         assertFalse(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
         assertTrue(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
         assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
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 93ff9e1..50f814d 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
@@ -23,7 +23,9 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 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;
 
@@ -62,6 +64,9 @@
 @RunWith(AndroidJUnit4.class)
 public class AutomaticBrightnessStrategyTest {
     private static final int DISPLAY_ID = 0;
+
+    private static final boolean DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE = false;
+
     @Rule
     public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
@@ -116,15 +121,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -140,15 +148,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -164,15 +175,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -188,15 +202,18 @@
         boolean userSetBrightnessChanged = true;
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.5f,
                         /* userChangedAutoBrightnessAdjustment= */ false, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -214,15 +231,17 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.4f,
-                        /* userChangedAutoBrightnessAdjustment= */ true, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -240,15 +259,17 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness,
                 userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, /* adjustment */ 0.4f,
-                        /* userChangedAutoBrightnessAdjustment= */ true, policy,
-                        targetDisplayState, /* shouldResetShortTermModel */ true);
+                        /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                        /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
     }
@@ -267,14 +288,16 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED,
                         mBrightnessConfiguration,
                         lastUserSetBrightness,
                         userSetBrightnessChanged, pendingBrightnessAdjustment,
                         /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
                         /* shouldResetShortTermModel */ true);
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -294,7 +317,8 @@
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
         mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, lastUserSetBrightness,
                 userSetBrightnessChanged);
         verify(mAutomaticBrightnessController)
                 .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE,
@@ -302,6 +326,7 @@
                         lastUserSetBrightness,
                         userSetBrightnessChanged, pendingBrightnessAdjustment,
                         /* userChangedAutoBrightnessAdjustment= */ true, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
                         /* shouldResetShortTermModel */ true);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled());
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff());
@@ -318,6 +343,7 @@
         boolean userSetBrightnessChanged = true;
         int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
         float pendingBrightnessAdjustment = 0.1f;
+        boolean useNormalBrightnessForDoze = false;
         Settings.System.putFloat(mContext.getContentResolver(),
                 Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
         mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
@@ -325,8 +351,8 @@
         // Validate no interaction when automaticBrightnessController is in idle mode
         when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true);
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController, never())
                 .switchMode(anyInt(), /* sendUpdate= */ anyBoolean());
 
@@ -334,20 +360,57 @@
         // state is ON
         when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
         verify(mAutomaticBrightnessController).switchMode(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
                 /* sendUpdate= */ false);
 
-        // Validate interaction when automaticBrightnessController is in non-idle mode, and display
-        // state is DOZE
+        reset(mAutomaticBrightnessController);
+        when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
+        when(mDisplayManagerFlags.isNormalBrightnessForDozeParameterEnabled()).thenReturn(true);
+        policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, display
+        // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is false.
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
-                allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
-                userSetBrightnessChanged);
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+        // 1st AUTO_BRIGHTNESS_MODE_DOZE
         verify(mAutomaticBrightnessController).switchMode(
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
                 /* sendUpdate= */ false);
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, display
+        // state is ON, policy is DOZE and useNormalBrightnessForDoze is false.
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+        // 2nd AUTO_BRIGHTNESS_MODE_DOZE
+        verify(mAutomaticBrightnessController, times(2)).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE,
+                /* sendUpdate= */ false);
+
+        useNormalBrightnessForDoze = true;
+        // Validate interaction when automaticBrightnessController is in non-idle mode, display
+        // state is DOZE, policy is DOZE and useNormalBrightnessForDoze is true.
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+        // 1st AUTO_BRIGHTNESS_MODE_DEFAULT
+        verify(mAutomaticBrightnessController).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+                /* sendUpdate= */ false);
+
+        // Validate interaction when automaticBrightnessController is in non-idle mode, display
+        // state is ON, policy is DOZE and useNormalBrightnessForDoze is true.
+        mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+                allowAutoBrightnessWhileDozing, brightnessReason, policy,
+                useNormalBrightnessForDoze, lastUserSetBrightness, userSetBrightnessChanged);
+        // 2nd AUTO_BRIGHTNESS_MODE_DEFAULT
+        verify(mAutomaticBrightnessController, times(2)).switchMode(
+                AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT,
+                /* sendUpdate= */ false);
     }
 
     @Test
@@ -365,13 +428,15 @@
         mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
         setTemporaryAutoBrightnessAdjustment(temporaryAutoBrightnessAdjustments);
         mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
-                lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
-                autoBrightnessState);
+                lastUserSetScreenBrightness, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                brightnessConfiguration, autoBrightnessState);
         verify(mAutomaticBrightnessController).configure(autoBrightnessState,
                 brightnessConfiguration,
                 lastUserSetScreenBrightness,
                 userSetBrightnessChanged, temporaryAutoBrightnessAdjustments,
                 /* userChangedAutoBrightnessAdjustment= */ false, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
                 /* shouldResetShortTermModel= */ true);
         assertTrue(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
         assertFalse(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
@@ -381,8 +446,9 @@
         mAutomaticBrightnessStrategy.setAutomaticBrightnessController(null);
         mAutomaticBrightnessStrategy.setShouldResetShortTermModel(true);
         mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(userSetBrightnessChanged,
-                lastUserSetScreenBrightness, policy, targetDisplayState, brightnessConfiguration,
-                autoBrightnessState);
+                lastUserSetScreenBrightness, policy, targetDisplayState,
+                        DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                brightnessConfiguration, autoBrightnessState);
         assertFalse(mAutomaticBrightnessStrategy.isTemporaryAutoBrightnessAdjustmentApplied());
         assertTrue(mAutomaticBrightnessStrategy.shouldResetShortTermModel());
         assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
@@ -504,8 +570,9 @@
     public void isAutoBrightnessValid_returnsFalseWhenBrightnessIsInvalid() {
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, true,
                 BrightnessReason.REASON_UNKNOWN,
-                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f,
-                false);
+                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f,
+                /* userSetBrightnessChanged= */ false);
         when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
                 .thenReturn(Float.NaN);
         assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessValid());
@@ -520,8 +587,9 @@
                 .thenReturn(0.1f);
         mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, true,
                 BrightnessReason.REASON_UNKNOWN,
-                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f,
-                false);
+                DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
+                DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE, /* lastUserSetScreenBrightness= */ 0.1f,
+                /* userSetBrightnessChanged= */ false);
         when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
                 .thenReturn(0.2f);
         assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessValid());
@@ -594,7 +662,8 @@
         mAutomaticBrightnessStrategy.setTemporaryAutoBrightnessAdjustment(temporaryBrightness);
         mAutomaticBrightnessStrategy.accommodateUserBrightnessChanges(true,
                 brightness, DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT,
-                Display.STATE_ON, mock(BrightnessConfiguration.class),
+                Display.STATE_ON, DEFAULT_USE_NORMAL_BRIGHTNESS_FOR_DOZE,
+                mock(BrightnessConfiguration.class),
                 AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED);
         when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()).thenReturn(
                 autoBrightnessAdjustment);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index 34c6ba9..1f3f19f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -50,7 +50,7 @@
     }
 
     @Test
-    fun `test app request votes`(@TestParameter testCase: AppRequestTestCase) {
+    fun testAppRequestVotes(@TestParameter testCase: AppRequestTestCase) {
         whenever(mockFlags.ignoreAppPreferredRefreshRateRequest())
                 .thenReturn(testCase.ignoreRefreshRateRequest)
         val displayModeDirector = DisplayModeDirector(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
index bf2edfe..3841211 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BaseModeRefreshRateVoteTest.kt
@@ -39,7 +39,7 @@
     }
 
     @Test
-    fun `updates summary with base mode refresh rate if not set`() {
+    fun updatesSummary_doesNotUpdateSummary_baseModeRefreshRateNotSet() {
         val summary = createVotesSummary()
 
         baseModeVote.updateSummary(summary)
@@ -48,7 +48,7 @@
     }
 
     @Test
-    fun `keeps summary base mode refresh rate if set`() {
+    fun doesNotUpdateSummary_baseModeRefreshRateSet() {
         val summary = createVotesSummary()
         summary.appRequestBaseModeRefreshRate = OTHER_BASE_REFRESH_RATE
 
@@ -58,7 +58,7 @@
     }
 
     @Test
-    fun `keeps summary with base mode refresh rate if vote refresh rate is negative`() {
+    fun doesNotUpdateSummary_baseModeRefreshRateNotSet_requestedRefreshRateInvalid() {
         val invalidBaseModeVote = BaseModeRefreshRateVote(-10f)
         val summary = createVotesSummary()
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
index 209e5a3..0a3c285 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/CombinedVoteTest.kt
@@ -45,7 +45,7 @@
     }
 
     @Test
-    fun `delegates update to children`() {
+    fun delegatesUpdateToChildren() {
         val summary = createVotesSummary()
 
         combinedVote.updateSummary(summary)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
index 38782c2..5b5ae65 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisableRefreshRateSwitchingVoteTest.kt
@@ -28,7 +28,7 @@
 class DisableRefreshRateSwitchingVoteTest {
 
     @Test
-    fun `disabled refresh rate switching is not changed`(
+    fun testDisableRefreshRateSwitch_alreadyDisabled(
             @TestParameter voteDisableSwitching: Boolean
     ) {
         val summary = createVotesSummary()
@@ -41,7 +41,7 @@
     }
 
     @Test
-    fun `disables refresh rate switching if requested`() {
+    fun disablesRefreshRateSwitch_notDisabled_requested() {
         val summary = createVotesSummary()
         val vote = DisableRefreshRateSwitchingVote(true)
 
@@ -51,7 +51,7 @@
     }
 
     @Test
-    fun `does not disable refresh rate switching if not requested`() {
+    fun doesNotDisableRefreshRateSwitch_notDisabled_notRequested() {
         val summary = createVotesSummary()
         val vote = DisableRefreshRateSwitchingVote(false)
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 62400eb..d91f154 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -1557,6 +1557,19 @@
         when(ddcMock.getIdleScreenRefreshRateTimeoutLuxThresholdPoint())
                 .thenReturn(List.of(getIdleScreenRefreshRateTimeoutLuxThresholdPoint(6, 1000),
                         getIdleScreenRefreshRateTimeoutLuxThresholdPoint(100, 800)));
+        director.defaultDisplayDeviceUpdated(ddcMock); // set the updated ddc
+
+        // idleScreenRefreshRate config is still null because the flag to enable subscription to
+        // light sensor is not enabled
+        sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 4));
+        waitForIdleSync();
+        assertNull(director.getBrightnessObserver().getIdleScreenRefreshRateConfig());
+
+        // Flag to subscribe to light sensor is enabled, and the sensor subscription is attempted
+        // again to load the idle screen refresh rate config
+        when(mDisplayManagerFlags.isIdleScreenConfigInSubscribingLightSensorEnabled())
+                .thenReturn(true);
+        director.defaultDisplayDeviceUpdated(ddcMock); // set the updated ddc
 
         // Sensor reads 5 lux
         sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 5));
@@ -1575,7 +1588,6 @@
         waitForIdleSync();
         assertEquals(new SurfaceControl.IdleScreenRefreshRateConfig(800),
                 director.getBrightnessObserver().getIdleScreenRefreshRateConfig());
-
     }
 
     @Test
@@ -3231,6 +3243,8 @@
 
     @Test
     public void testNotifyDefaultDisplayDeviceUpdated() {
+        when(mDisplayManagerFlags.isIdleScreenConfigInSubscribingLightSensorEnabled())
+                .thenReturn(true);
         when(mResources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate))
             .thenReturn(75);
         when(mResources.getInteger(R.integer.config_defaultRefreshRate))
@@ -3289,6 +3303,8 @@
                 new float[]{ BrightnessSynchronizer.brightnessIntToFloat(5) }, /* delta= */ 0);
         assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThresholds(),
                 new float[]{10}, /* delta= */ 0);
+        assertNull(director.getBrightnessObserver()
+                .getIdleScreenRefreshRateTimeoutLuxThresholdPoints());
 
 
         // Notify that the default display is updated, such that DisplayDeviceConfig has new values
@@ -3300,6 +3316,10 @@
                 /* defaultRefreshRateInHbmSunlight= */ 75,
                 /* lowPowerSupportedModes= */ List.of(),
                 /* lowLightBlockingZoneSupportedModes= */ List.of());
+        List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+                idleScreenRefreshRateTimeoutLuxThresholdPoints =
+                List.of(getIdleScreenRefreshRateTimeoutLuxThresholdPoint(0, 1500),
+                        getIdleScreenRefreshRateTimeoutLuxThresholdPoint(50, 1000));
         when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData);
         when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
         when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
@@ -3311,6 +3331,8 @@
                 .thenReturn(new float[]{0.21f});
         when(displayDeviceConfig.getHighAmbientBrightnessThresholds())
                 .thenReturn(new float[]{2100});
+        when(displayDeviceConfig.getIdleScreenRefreshRateTimeoutLuxThresholdPoint())
+                .thenReturn(idleScreenRefreshRateTimeoutLuxThresholdPoints);
         director.defaultDisplayDeviceUpdated(displayDeviceConfig);
 
         // Verify the new values are from the freshly loaded DisplayDeviceConfig.
@@ -3329,6 +3351,9 @@
                 new float[]{30}, /* delta= */ 0);
         assertEquals(director.getHbmObserver().getRefreshRateInHbmHdr(), 65);
         assertEquals(director.getHbmObserver().getRefreshRateInHbmSunlight(), 75);
+        assertEquals(director.getBrightnessObserver()
+                .getIdleScreenRefreshRateTimeoutLuxThresholdPoints(),
+                idleScreenRefreshRateTimeoutLuxThresholdPoints);
 
         // Notify that the default display is updated, such that DeviceConfig has new values
         FakeDeviceConfig config = mInjector.getDeviceConfig();
@@ -3531,12 +3556,16 @@
                 new RefreshRateRange(refreshRate, refreshRate);
         displayListener.onDisplayChanged(DISPLAY_ID);
 
-        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
         assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
+        assertVoteForRenderFrameRateRange(vote, refreshRate, refreshRate);
 
         mInjector.mDisplayInfo.layoutLimitedRefreshRate = null;
         displayListener.onDisplayChanged(DISPLAY_ID);
 
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
+        assertNull(vote);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
         assertNull(vote);
     }
@@ -3560,6 +3589,8 @@
 
         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
         assertNull(vote);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_REFRESH_RATE);
+        assertNull(vote);
     }
 
     private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index ee79d19..5e240cf 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -23,6 +23,7 @@
 import static com.android.server.display.mode.DisplayModeDirector.SYNCHRONIZED_REFRESH_RATE_TOLERANCE;
 import static com.android.server.display.mode.Vote.PRIORITY_LIMIT_MODE;
 import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_REFRESH_RATE;
+import static com.android.server.display.mode.Vote.PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE;
 import static com.android.server.display.mode.Vote.PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE;
 import static com.android.server.display.mode.VotesStorage.GLOBAL_ID;
 
@@ -360,16 +361,22 @@
                 .thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(
                 Vote.forPhysicalRefreshRates(
                         MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
                         MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(
+                Vote.forRenderFrameRates(
+                        MAX_REFRESH_RATE - SYNCHRONIZED_REFRESH_RATE_TOLERANCE,
+                        MAX_REFRESH_RATE + SYNCHRONIZED_REFRESH_RATE_TOLERANCE));
 
         // Remove external display and check that sync vote is no longer present.
         mObserver.onDisplayRemoved(EXTERNAL_DISPLAY);
 
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
     }
 
     /** External display added, disabled feature refresh rates synchronization */
@@ -383,8 +390,10 @@
                 .thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
     }
 
     /** External display not applied refresh rates synchronization, because
@@ -397,8 +406,10 @@
         when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true);
         init();
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
         mObserver.onDisplayAdded(EXTERNAL_DISPLAY);
         assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null);
+        assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_RENDER_FRAME_RATE)).isEqualTo(null);
     }
 
     private void init() {
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
index 9edcc32..0968edb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/PhysicalVoteTest.kt
@@ -37,7 +37,7 @@
     }
 
     @Test
-    fun `updates minPhysicalRefreshRate if summary has less`() {
+    fun updatesMinPhysicalRefreshRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.minPhysicalRefreshRate = 45f
 
@@ -47,7 +47,7 @@
     }
 
     @Test
-    fun `does not update minPhysicalRefreshRate if summary has more`() {
+    fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.minPhysicalRefreshRate = 75f
 
@@ -57,7 +57,7 @@
     }
 
     @Test
-    fun `updates maxPhysicalRefreshRate if summary has more`() {
+    fun updatesMaxPhysicalRefreshRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.maxPhysicalRefreshRate = 120f
 
@@ -67,7 +67,7 @@
     }
 
     @Test
-    fun `does not update maxPhysicalRefreshRate if summary has less`() {
+    fun doesNotUpdateMaxPhysicalRefreshRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.maxPhysicalRefreshRate = 75f
 
@@ -77,7 +77,7 @@
     }
 
     @Test
-    fun `updates maxRenderFrameRate if summary has more`() {
+    fun updatesMaxRenderFrameRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.maxRenderFrameRate = 120f
 
@@ -87,7 +87,7 @@
     }
 
     @Test
-    fun `does not update maxRenderFrameRate if summary has less`() {
+    fun doesNotUpdateMaxRenderFrameRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.maxRenderFrameRate = 75f
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
index 2d65f1c..9fa1e1b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RenderVoteTest.kt
@@ -38,7 +38,7 @@
     }
 
     @Test
-    fun `updates minRenderFrameRate if summary has less`() {
+    fun updatesMinRenderFrameRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.minRenderFrameRate = 45f
 
@@ -48,7 +48,7 @@
     }
 
     @Test
-    fun `does not update minRenderFrameRate if summary has more`() {
+    fun doesNotUpdateMinRenderFrameRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.minRenderFrameRate = 75f
 
@@ -58,7 +58,7 @@
     }
 
     @Test
-    fun `updates maxRenderFrameRate if summary has more`() {
+    fun updatesMaxPRenderFrameRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.maxRenderFrameRate = 120f
 
@@ -68,7 +68,7 @@
     }
 
     @Test
-    fun `does not update maxRenderFrameRate if summary has less`() {
+    fun doesNotUpdateMaxPRenderFrameRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.maxRenderFrameRate = 75f
 
@@ -78,7 +78,7 @@
     }
 
     @Test
-    fun `updates minPhysicalRefreshRate if summary has less`() {
+    fun updatesMinPhysicalRefreshRateWithBiggerValue() {
         val summary = createVotesSummary()
         summary.minPhysicalRefreshRate = 45f
 
@@ -88,7 +88,7 @@
     }
 
     @Test
-    fun `does not update minPhysicalRefreshRate if summary has more`() {
+    fun doesNotUpdateMinPhysicalRefreshRateWithSmallerValue() {
         val summary = createVotesSummary()
         summary.minPhysicalRefreshRate = 75f
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
index dbe9e4a..be9c563 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -28,7 +28,7 @@
 class RequestedRefreshRateVoteTest {
 
     @Test
-    fun `updates requestedRefreshRates`() {
+    fun testUpdatesRequestedRefreshRates() {
         val refreshRate = 90f
         val vote = RequestedRefreshRateVote(refreshRate)
         val summary = createVotesSummary()
@@ -40,7 +40,7 @@
     }
 
     @Test
-    fun `updates requestedRefreshRates with multiple refresh rates`() {
+    fun testUpdatesRequestedRefreshRates_multipleVotes() {
         val refreshRate1 = 90f
         val vote1 = RequestedRefreshRateVote(refreshRate1)
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4fc574a..d7dcca7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -103,7 +103,7 @@
     }
 
     @Test
-    fun `test low power mode`(@TestParameter testCase: LowPowerTestCase) {
+    fun testLowPowerMode(@TestParameter testCase: LowPowerTestCase) {
         whenever(mockFlags.isVsyncLowPowerVoteEnabled).thenReturn(testCase.vsyncLowPowerVoteEnabled)
         whenever(spyContext.contentResolver)
                 .thenReturn(settingsProviderRule.mockContentResolver(null))
@@ -151,7 +151,7 @@
     }
 
     @Test
-    fun `test settings refresh rates`(@TestParameter testCase: SettingsRefreshRateTestCase) {
+    fun testSettingsRefreshRates(@TestParameter testCase: SettingsRefreshRateTestCase) {
         whenever(mockFlags.isPeakRefreshRatePhysicalLimitEnabled)
                 .thenReturn(testCase.peakRefreshRatePhysicalLimitEnabled)
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
index 1be2fbf..319c21e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SizeVoteTest.kt
@@ -39,7 +39,7 @@
     }
 
     @Test
-    fun `updates size if width and height not set and display resolution voting disabled`() {
+    fun updatesSize_widthAndHeightNotSet_resolutionVotingDisabled() {
         val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
         summary.width = Vote.INVALID_SIZE
         summary.height = Vote.INVALID_SIZE
@@ -55,7 +55,7 @@
     }
 
     @Test
-    fun `does not update size if width set and display resolution voting disabled`() {
+    fun doesNotUpdateSiz_widthSet_resolutionVotingDisabled() {
         val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
         summary.width = 150
         summary.height = Vote.INVALID_SIZE
@@ -71,7 +71,7 @@
     }
 
     @Test
-    fun `does not update size if height set and display resolution voting disabled`() {
+    fun doesNotUpdateSize_heightSet_resolutionVotingDisabled() {
         val summary = createVotesSummary(isDisplayResolutionRangeVotingEnabled = false)
         summary.width = Vote.INVALID_SIZE
         summary.height = 250
@@ -87,7 +87,7 @@
     }
 
     @Test
-    fun `updates width if summary has more and display resolution voting enabled`() {
+    fun updatesWidthWithSmallerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 850
 
@@ -97,7 +97,7 @@
     }
 
     @Test
-    fun `does not update width if summary has less and display resolution voting enabled`() {
+    fun doesNotUpdateWidthWithBiggerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 750
 
@@ -107,7 +107,7 @@
     }
 
     @Test
-    fun `updates height if summary has more and display resolution voting enabled`() {
+    fun updatesHeightWithSmallerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.height = 1650
 
@@ -117,7 +117,7 @@
     }
 
     @Test
-    fun `does not update height if summary has less and display resolution voting enabled`() {
+    fun doesNotUpdateHeightWithBiggerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.height = 1550
 
@@ -127,7 +127,7 @@
     }
 
     @Test
-    fun `updates minWidth if summary has less and display resolution voting enabled`() {
+    fun updatesMinWidthWithSmallerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 150
         summary.minWidth = 350
@@ -138,7 +138,7 @@
     }
 
     @Test
-    fun `does not update minWidth if summary has more and display resolution voting enabled`() {
+    fun doesNotUpdateMinWidthWithBiggerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 150
         summary.minWidth = 450
@@ -149,7 +149,7 @@
     }
 
     @Test
-    fun `updates minHeight if summary has less and display resolution voting enabled`() {
+    fun updatesMinHeightWithSmallerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 150
         summary.minHeight = 1150
@@ -160,7 +160,7 @@
     }
 
     @Test
-    fun `does not update minHeight if summary has more and display resolution voting enabled`() {
+    fun doesNotUpdateMinHeightWithBiggerValue_resolutionVotingEnabled() {
         val summary = createVotesSummary()
         summary.width = 150
         summary.minHeight = 1250
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
index 6ce49b8..2a50a33 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -39,7 +39,7 @@
     }
 
     @Test
-    fun `adds supported mode ids if supportedModeIds in summary is null`() {
+    fun addsSupportedModeIds_summaryHasNull() {
         val summary = createVotesSummary()
 
         supportedModesVote.updateSummary(summary)
@@ -48,7 +48,7 @@
     }
 
     @Test
-    fun `does not add supported mode ids if summary has empty list of modeIds`() {
+    fun doesNotAddSupportedModeIdes_summaryHasEmptyList() {
         val summary = createVotesSummary()
         summary.supportedModeIds = ArrayList()
 
@@ -58,7 +58,7 @@
     }
 
     @Test
-    fun `filters out modes that does not match vote`() {
+    fun filtersModeIdsThatDoesNotMatchVote() {
         val summary = createVotesSummary()
         summary.supportedModeIds = ArrayList(listOf(otherMode, supportedModes[0]))
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
index d0c112b..0da6885 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
@@ -42,7 +42,7 @@
     }
 
     @Test
-    fun `adds supported refresh rates if supportedModes in summary is null`() {
+    fun addsSupportedRefreshRates_summaryHasNull() {
         val summary = createVotesSummary()
 
         supportedRefreshRatesVote.updateSummary(summary)
@@ -51,7 +51,7 @@
     }
 
     @Test
-    fun `does not add supported refresh rates if summary has empty list of refresh rates`() {
+    fun doesNotAddSupportedRefreshRates_summaryHasEmptyList() {
         val summary = createVotesSummary()
         summary.supportedRefreshRates = ArrayList()
 
@@ -61,7 +61,7 @@
     }
 
     @Test
-    fun `filters out supported refresh rates that does not match vote`() {
+    fun filtersSupportedRefreshRatesThatDoesNotMatchVote() {
         val summary = createVotesSummary()
         summary.supportedRefreshRates = ArrayList(listOf(otherMode, refreshRates[0]))
 
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 5cd3a33..b2d83d7 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
@@ -41,7 +41,7 @@
     private val mockConfig = mock<DisplayDeviceConfig>()
 
     @Test
-    fun `test app supported modes`(@TestParameter testCase: AppSupportedModesTestCase) {
+    fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) {
         whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled)
         whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
         val syntheticModeManager = SyntheticModeManager(mockFlags)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
index c49205b..9ea7ea7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -51,7 +51,7 @@
     private val storage = VotesStorage({}, null)
 
     @Test
-    fun `requestDisplayModes adds vote to storage`() {
+    fun testRequestDisplayModes_voteAdded() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -69,7 +69,7 @@
     }
 
     @Test
-    fun `requestDisplayModes overrides votes in storage`() {
+    fun testRequestDisplayModes_voteReplaced() {
         val systemRequestObserver = SystemRequestObserver(storage)
 
         systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, intArrayOf(1, 2, 3))
@@ -89,7 +89,7 @@
     }
 
     @Test
-    fun `requestDisplayModes removes vote to storage`() {
+    fun testRequestDisplayModes_voteRemoved() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -101,7 +101,7 @@
     }
 
     @Test
-    fun `requestDisplayModes calls linkToDeath to token`() {
+    fun testTokenLinkToDeath() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -111,7 +111,7 @@
     }
 
     @Test
-    fun `does not add votes to storage if binder died when requestDisplayModes called`() {
+    fun testBinderDied_voteRemoved() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -123,7 +123,7 @@
     }
 
     @Test
-    fun `removes all votes from storage when binder dies`() {
+    fun testBinderDied_allVotesRemoved() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -138,7 +138,7 @@
     }
 
     @Test
-    fun `calls unlinkToDeath on token when no votes remaining`() {
+    fun testTokenUnlinkToDeath_noMoreVotes() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -149,7 +149,7 @@
     }
 
     @Test
-    fun `does not call unlinkToDeath on token when votes for other display in storage`() {
+    fun testTokenUnlinkToDeathNotCalled_votesForOtherDisplayInStorage() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
 
@@ -161,7 +161,7 @@
     }
 
     @Test
-    fun `requestDisplayModes subset modes from different tokens`() {
+    fun testRequestDisplayModes_differentToken_voteHasModesSubset() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
         systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
@@ -187,7 +187,7 @@
     }
 
     @Test
-    fun `recalculates vote if one binder dies`() {
+    fun testBinderDies_recalculatesVotes() {
         val systemRequestObserver = SystemRequestObserver(storage)
         val requestedModes = intArrayOf(1, 2, 3)
         systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index dd5e1be..239e59b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -80,7 +80,7 @@
     }
 
     @Test
-    fun `filters modes for summary supportedRefreshRates`(
+    fun testFiltersModes_supportedRefreshRates(
             @TestParameter testCase: SupportedRefreshRatesTestCase
     ) {
         val summary = createSummary(testCase.supportedModesVoteEnabled)
@@ -142,9 +142,7 @@
     }
 
     @Test
-    fun `filters modes for summary supportedModes`(
-            @TestParameter testCase: SupportedModesTestCase
-    ) {
+    fun testFiltersModes_supportedModes(@TestParameter testCase: SupportedModesTestCase) {
         val summary = createSummary(testCase.supportedModesVoteEnabled)
         summary.supportedModeIds = testCase.summarySupportedModes
 
@@ -154,7 +152,7 @@
     }
 
     @Test
-    fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+    fun testInvalidSummary_requestedRefreshRateLessThanMinRenderRate() {
         val summary = createSummary()
         summary.requestedRefreshRates = setOf(30f, 90f)
         summary.minRenderFrameRate = 60f
@@ -166,7 +164,7 @@
     }
 
     @Test
-    fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+    fun testInvalidSummary_requestedRefreshRateMoreThanMaxRenderRate() {
         val summary = createSummary()
         summary.requestedRefreshRates = setOf(60f, 240f)
         summary.minRenderFrameRate = 60f
@@ -178,7 +176,7 @@
     }
 
     @Test
-    fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+    fun testValidSummary_requestedRefreshRatesWithingRenderRateLimits() {
         val summary = createSummary()
         summary.requestedRefreshRates = setOf(60f, 90f)
         summary.minRenderFrameRate = 60f
diff --git a/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
index d6c8ceb..97c12bb 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/notifications/DisplayNotificationManagerTest.java
@@ -30,6 +30,7 @@
 
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.os.UserHandle;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
@@ -70,6 +71,8 @@
     private ArgumentCaptor<Integer> mNotifyNoteIdCaptor;
     @Captor
     private ArgumentCaptor<Notification> mNotifyAsUserNotificationCaptor;
+    @Captor
+    private ArgumentCaptor<UserHandle> mNotifyAsUserCaptor;
 
     /** Setup tests. */
     @Before
@@ -127,7 +130,8 @@
         dnm.onDisplayPortLinkTrainingFailure();
         dnm.onCableNotCapableDisplayPort();
         dnm.onHighTemperatureExternalDisplayNotAllowed();
-        verify(mMockedNotificationManager, never()).notify(anyString(), anyInt(), any());
+        verify(mMockedNotificationManager, never()).notifyAsUser(anyString(), anyInt(), any(),
+                any());
     }
 
     @Test
@@ -175,10 +179,11 @@
     }
 
     private void assertExpectedNotification() {
-        verify(mMockedNotificationManager).notify(
+        verify(mMockedNotificationManager).notifyAsUser(
                 mNotifyTagCaptor.capture(),
                 mNotifyNoteIdCaptor.capture(),
-                mNotifyAsUserNotificationCaptor.capture());
+                mNotifyAsUserNotificationCaptor.capture(),
+                mNotifyAsUserCaptor.capture());
         assertThat(mNotifyTagCaptor.getValue()).isEqualTo("DisplayNotificationManager");
         assertThat((int) mNotifyNoteIdCaptor.getValue()).isEqualTo(1);
         final var notification = mNotifyAsUserNotificationCaptor.getValue();
@@ -188,5 +193,7 @@
         assertThat(notification.flags & FLAG_ONGOING_EVENT).isEqualTo(0);
         assertThat(notification.when).isEqualTo(0);
         assertThat(notification.getTimeoutAfter()).isEqualTo(30000L);
+        final var user = mNotifyAsUserCaptor.getValue();
+        assertThat(user).isEqualTo(UserHandle.CURRENT);
     }
 }
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
index 6369d45..1f0e975 100644
--- a/services/tests/dreamservicetests/Android.bp
+++ b/services/tests/dreamservicetests/Android.bp
@@ -42,3 +42,13 @@
         enabled: false,
     },
 }
+
+test_module_config {
+    name: "DreamServiceTests_server_dreams",
+    base: "DreamServiceTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.dreams"],
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
index 54f4607..1abc557 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamOverlayServiceTest.java
@@ -18,7 +18,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -27,7 +29,9 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
 import android.service.dreams.DreamOverlayService;
+import android.service.dreams.Flags;
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
 import android.service.dreams.IDreamOverlayClient;
@@ -136,7 +140,7 @@
 
         // Start the dream.
         client.startDream(mLayoutParams, mOverlayCallback,
-                FIRST_DREAM_COMPONENT.flattenToString(), false);
+                FIRST_DREAM_COMPONENT.flattenToString(), false, false);
 
         // The callback should not have run yet.
         verify(monitor, never()).onStartDream();
@@ -194,22 +198,24 @@
         // Start a dream with the first client and ensure the dream is now active from the
         // overlay's perspective.
         firstClient.startDream(mLayoutParams, mOverlayCallback,
-                FIRST_DREAM_COMPONENT.flattenToString(), false);
+                FIRST_DREAM_COMPONENT.flattenToString(), true, false);
 
 
         verify(monitor).onStartDream();
         assertThat(service.getDreamComponent()).isEqualTo(FIRST_DREAM_COMPONENT);
+        assertThat(service.isDreamInPreviewMode()).isTrue();
 
         Mockito.clearInvocations(monitor);
 
         // Start a dream from the second client and verify that the overlay has both cycled to
         // the new dream (ended/started).
         secondClient.startDream(mLayoutParams, mOverlayCallback,
-                SECOND_DREAM_COMPONENT.flattenToString(), false);
+                SECOND_DREAM_COMPONENT.flattenToString(), false, false);
 
         verify(monitor).onEndDream();
         verify(monitor).onStartDream();
         assertThat(service.getDreamComponent()).isEqualTo(SECOND_DREAM_COMPONENT);
+        assertThat(service.isDreamInPreviewMode()).isFalse();
 
         Mockito.clearInvocations(monitor);
 
@@ -221,6 +227,47 @@
         verify(monitor, never()).onWakeUp();
     }
 
+    /**
+     * Verifies that only the currently started dream is able to affect the overlay.
+     */
+    @Test
+    @EnableFlags(Flags.FLAG_DREAM_WAKE_REDIRECT)
+    public void testRedirectToWakeAcrossClients() throws RemoteException {
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        }).when(mExecutor).execute(any());
+
+        final TestDreamOverlayService.Monitor monitor = Mockito.mock(
+                TestDreamOverlayService.Monitor.class);
+        final TestDreamOverlayService service = new TestDreamOverlayService(monitor, mExecutor);
+        final IBinder binder = service.onBind(new Intent());
+        final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(binder);
+
+        service.redirectWake(true);
+
+        final IDreamOverlayClient client = getClient(overlay);
+
+        // Start the dream.
+        client.startDream(mLayoutParams, mOverlayCallback,
+                FIRST_DREAM_COMPONENT.flattenToString(), false, false);
+        // Make sure redirect state is set on dream.
+        verify(mOverlayCallback).onRedirectWake(eq(true));
+
+        // Make sure new changes are propagated.
+        clearInvocations(mOverlayCallback);
+        service.redirectWake(false);
+        verify(mOverlayCallback).onRedirectWake(eq(false));
+
+
+        // Start another dream, make sure new dream is informed of current state.
+        service.redirectWake(true);
+        clearInvocations(mOverlayCallback);
+        client.startDream(mLayoutParams, mOverlayCallback,
+                FIRST_DREAM_COMPONENT.flattenToString(), false, false);
+        verify(mOverlayCallback).onRedirectWake(eq(true));
+    }
+
     private static IDreamOverlayClient getClient(IDreamOverlay overlay) throws RemoteException {
         final OverlayClientCallback callback = new OverlayClientCallback();
         overlay.getClient(callback);
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
index b4e1abf..265b74d 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamServiceTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -215,4 +216,39 @@
         // Ensure service does not crash from only receiving up event.
         environment.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SPACE));
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+    public void testComeToFront() throws Exception {
+        TestDreamEnvironment environment = new TestDreamEnvironment.Builder(mTestableLooper)
+                .setDreamOverlayPresent(true)
+                .build();
+        environment.advance(TestDreamEnvironment.DREAM_STATE_STARTED);
+
+        // Call comeToFront through binder.
+        environment.resetClientInvocations();
+        environment.comeToFront();
+        mTestableLooper.processAllMessages();
+
+        // Overlay client receives call.
+        verify(environment.getDreamOverlayClient()).comeToFront();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+    public void testComeToFront_noOverlay() throws Exception {
+        // Dream environment with no overlay present
+        TestDreamEnvironment environment = new TestDreamEnvironment.Builder(mTestableLooper)
+                .setDreamOverlayPresent(false)
+                .build();
+        environment.advance(TestDreamEnvironment.DREAM_STATE_STARTED);
+
+        // Call comeToFront through binder.
+        environment.resetClientInvocations();
+        environment.comeToFront();
+        mTestableLooper.processAllMessages();
+
+        // Overlay client receives call.
+        verify(environment.getDreamOverlayClient(), never()).comeToFront();
+    }
 }
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
index e2b93ae..7c239ef 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/TestDreamEnvironment.java
@@ -385,7 +385,8 @@
             final ArgumentCaptor<IDreamOverlayCallback> overlayCallbackCaptor =
                     ArgumentCaptor.forClass(IDreamOverlayCallback.class);
             verify(mDreamOverlayClient, description("dream client not informed of dream start"))
-                    .startDream(any(), overlayCallbackCaptor.capture(), any(), anyBoolean());
+                    .startDream(any(), overlayCallbackCaptor.capture(), any(), anyBoolean(),
+                            anyBoolean());
 
             mDreamOverlayCallback = overlayCallbackCaptor.getValue();
         }
@@ -398,10 +399,14 @@
         mService.dispatchKeyEvent(event);
     }
 
-    private void wakeDream() throws RemoteException {
+    private void wakeDream() {
         mService.wakeUp();
     }
 
+    void comeToFront() throws RemoteException {
+        mDreamServiceWrapper.comeToFront();
+    }
+
     /**
      * Retrieves the dream overlay callback.
      */
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 5b2c0c6..5a76931 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -137,3 +137,269 @@
     ],
     auto_gen_config: true,
 }
+
+FLAKY = ["androidx.test.filters.FlakyTest"]
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_blob",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.blob"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_IdleController",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.DeviceIdleControllerTest"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_AppStateTracker",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.AppStateTrackerTest"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_alarm",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.alarm"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_job_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.job"],
+    exclude_annotations: FLAKY + ["androidx.test.filters.LargeTest"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_job",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_tare_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.tare"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_tare",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_games_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.service.games"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_location",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.location"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_backup",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.backup"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_sensorprivacy",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.sensorprivacy"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_am_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.am."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_am_broadcast",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: [
+        "com.android.server.am.BroadcastQueueTest",
+        "com.android.server.am.BroadcastRecordTest",
+        "com.android.server.am.BroadcastQueueModernImplTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_app",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    // Matches appop too
+    include_filters: ["com.android.server.app"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_appop",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.appop"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_compat_overrides",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.compat.overrides"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_crashrecovery",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.RescuePartyTest"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_pm",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pm."],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_com_android_server_pm_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pm"],
+    exclude_annotations: FLAKY + ["org.junit.Ignore"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_power_Presubmit",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.power"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_power",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_trust",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.trust"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server_utils",
+    base: "FrameworksMockingServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.utils"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_android_server",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_job",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_tare",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_backup",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.backup"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_rescuepartytest",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.RescuePartyTest"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_power",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+    name: "FrameworksMockingServicesTests_server_trust",
+    base: "FrameworksMockingServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.trust"],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index b980ca0..30de0e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -512,6 +512,9 @@
         when(mPermissionManagerInternal.getAppOpPermissionPackages(
                 SCHEDULE_EXACT_ALARM)).thenReturn(EmptyArray.STRING);
 
+        // Initialize timestamps with arbitrary values of time
+        mNowElapsedTest = 12;
+        mNowRtcTest = 345;
         mInjector = new Injector(mMockContext);
         mService = new AlarmManagerService(mMockContext, mInjector);
         spyOn(mService);
@@ -774,6 +777,61 @@
     }
 
     @Test
+    public void timeChangeBroadcastForward() throws Exception {
+        final long timeDelta = 12345;
+        // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms more than boot
+        // time clock.
+        mNowRtcTest += timeDelta + 1001;
+        mNowElapsedTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+
+        verify(mMockContext)
+                .sendBroadcastAsUser(
+                        argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+                        eq(UserHandle.ALL),
+                        isNull(),
+                        any());
+    }
+
+    @Test
+    public void timeChangeBroadcastBackward() throws Exception {
+        final long timeDelta = 12345;
+        // AlarmManagerService sends the broadcast if real time clock proceeds 1000ms less than boot
+        // time clock.
+        mNowRtcTest += timeDelta - 1001;
+        mNowElapsedTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+
+        verify(mMockContext)
+                .sendBroadcastAsUser(
+                        argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+                        eq(UserHandle.ALL),
+                        isNull(),
+                        any());
+    }
+
+    @Test
+    public void timeChangeFilterMinorAdjustment() throws Exception {
+        final long timeDelta = 12345;
+        // AlarmManagerService does not send the broadcast if real time clock proceeds within 1000ms
+        // than boot time clock.
+        mNowRtcTest += timeDelta + 1000;
+        mNowElapsedTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+
+        mNowRtcTest += timeDelta - 1000;
+        mNowElapsedTest += timeDelta;
+        mTestTimer.expire(TIME_CHANGED_MASK);
+
+        verify(mMockContext, never())
+                .sendBroadcastAsUser(
+                        argThat((intent) -> intent.getAction() == Intent.ACTION_TIME_CHANGED),
+                        any(),
+                        any(),
+                        any());
+    }
+
+    @Test
     public void testSingleAlarmExpiration() throws Exception {
         final long triggerTime = mNowElapsedTest + 5000;
         final PendingIntent alarmPi = getNewMockPendingIntent();
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 e610a32..809e13c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -28,8 +28,8 @@
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
-import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.content.ContentResolver.SCHEME_CONTENT;
+import static android.os.PowerExemptionManager.REASON_DENIED;
 import static android.os.UserHandle.USER_ALL;
 import static android.util.DebugUtils.valueToString;
 
@@ -68,11 +68,9 @@
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -84,7 +82,6 @@
 import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
 import android.app.ForegroundServiceDelegationOptions;
-import android.app.IApplicationThread;
 import android.app.IUidObserver;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -129,7 +126,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.sdksandbox.flags.Flags;
 import com.android.server.LocalServices;
-import com.android.server.am.ActivityManagerService.StickyBroadcast;
+import com.android.server.am.BroadcastController.StickyBroadcast;
 import com.android.server.am.ProcessList.IsolatedUidRange;
 import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
 import com.android.server.am.UidObserverController.ChangeRecord;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index ee96c2a..3dd2f24a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -42,6 +43,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.text.TextUtils;
 
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
@@ -121,11 +124,18 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
 
+        mAppStartInfoTracker.mMonotonicClock = new MonotonicClock(
+                Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK);
         mAppStartInfoTracker.clearProcessStartInfo(true);
         mAppStartInfoTracker.mAppStartInfoLoaded.set(true);
         mAppStartInfoTracker.mAppStartInfoHistoryListSize =
                 mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE;
         doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean());
+
+        mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(),
+                AppStartInfoTracker.APP_START_STORE_DIR);
+        mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir,
+                AppStartInfoTracker.APP_START_INFO_FILE);
     }
 
     @After
@@ -135,11 +145,8 @@
 
     @Test
     public void testApplicationStartInfo() throws Exception {
-        mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(),
-                AppStartInfoTracker.APP_START_STORE_DIR);
+        // Make sure we can write to the file.
         assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
-        mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir,
-                AppStartInfoTracker.APP_START_INFO_FILE);
 
         final long appStartTimestampIntentStarted = 1000000;
         final long appStartTimestampActivityLaunchFinished = 2000000;
@@ -482,6 +489,79 @@
         verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS);
     }
 
+    /**
+     * Test to make sure that records are returned in correct order, from most recently added at
+     * index 0 to least recently added at index size - 1.
+     */
+    @Test
+    public void testHistoricalRecordsOrdering() throws Exception {
+        // Clear old records
+        mAppStartInfoTracker.clearProcessStartInfo(false);
+
+        // Add some records with timestamps 0 decreasing as clock increases.
+        ProcessRecord app = makeProcessRecord(
+                APP_1_PID_1,                     // pid
+                APP_1_UID,                       // uid
+                APP_1_UID,                       // packageUid
+                null,                            // definingUid
+                APP_1_PROCESS_NAME,              // processName
+                APP_1_PACKAGE_NAME);             // packageName
+
+        mAppStartInfoTracker.handleProcessBroadcastStart(3, app, buildIntent(COMPONENT),
+                false /* isAlarm */);
+        mAppStartInfoTracker.handleProcessBroadcastStart(2, app, buildIntent(COMPONENT),
+                false /* isAlarm */);
+        mAppStartInfoTracker.handleProcessBroadcastStart(1, app, buildIntent(COMPONENT),
+                false /* isAlarm */);
+
+        // Get records
+        ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+
+        // Confirm that records are in correct order, with index 0 representing the most recently
+        // added record and index size - 1 representing the least recently added one.
+        assertEquals(3, list.size());
+        assertEquals(1L, list.get(0).getStartupTimestamps().get(0).longValue());
+        assertEquals(2L, list.get(1).getStartupTimestamps().get(0).longValue());
+        assertEquals(3L, list.get(2).getStartupTimestamps().get(0).longValue());
+    }
+
+    /**
+     * Test to make sure that persist and restore correctly maintains the state of the monotonic
+     * clock.
+     */
+    @Test
+    public void testPersistAndRestoreMonotonicClock() {
+        // Make sure we can write to the file.
+        assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir));
+
+        // No need to persist records for this test, clear any that may be there.
+        mAppStartInfoTracker.clearProcessStartInfo(false);
+
+        // Set clock with an arbitrary 5 minute offset, just needs to be longer than it would take
+        // for code to run.
+        mAppStartInfoTracker.mMonotonicClock = new MonotonicClock(5 * 60 * 1000,
+                Clock.SYSTEM_CLOCK);
+
+        // Record the current time.
+        long originalMonotonicTime = mAppStartInfoTracker.mMonotonicClock.monotonicTime();
+
+        // Now persist the process start info. Records were cleared above so this should just
+        // persist the monotonic time.
+        mAppStartInfoTracker.persistProcessStartInfo();
+
+        // Null out the clock to make sure its set on load.
+        mAppStartInfoTracker.mMonotonicClock = null;
+        assertNull(mAppStartInfoTracker.mMonotonicClock);
+
+        // Now load from disk.
+        mAppStartInfoTracker.loadExistingProcessStartInfo();
+
+        // Confirm clock has been set and that its current time is greater than the previous one.
+        assertNotNull(mAppStartInfoTracker.mMonotonicClock);
+        assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() > originalMonotonicTime);
+    }
+
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
         try {
             Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 0ba74c6..100b548 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -1176,6 +1176,17 @@
         verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
         verifyPendingRecords(redQueue, List.of(screenOff));
         verifyPendingRecords(blueQueue, List.of(screenOff, screenOn));
+
+        final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions,
+                List.of(greenReceiver, redReceiver, blueReceiver), false);
+        screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED,
+                "testDeliveryGroupPolicy_prioritized_diffReceivers");
+        mImpl.enqueueBroadcastLocked(screenOffRecord);
+        mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions,
+                List.of(greenReceiver, blueReceiver), false));
+        verifyPendingRecords(greenQueue, List.of(screenOff, screenOn));
+        verifyPendingRecords(redQueue, List.of(screenOff));
+        verifyPendingRecords(blueQueue, List.of(screenOn));
     }
 
     /**
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 8656b99..d957355 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -46,11 +46,13 @@
 
 import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG;
 import static com.android.server.am.ProcessList.BACKUP_APP_ADJ;
+import static com.android.server.am.ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
 import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ;
 import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ;
 import static com.android.server.am.ProcessList.FOREGROUND_APP_ADJ;
 import static com.android.server.am.ProcessList.HEAVY_WEIGHT_APP_ADJ;
 import static com.android.server.am.ProcessList.HOME_APP_ADJ;
+import static com.android.server.am.ProcessList.INVALID_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
 import static com.android.server.am.ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ;
@@ -89,7 +91,6 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.ApplicationExitInfo;
-import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.content.ComponentName;
 import android.content.Context;
@@ -106,6 +107,7 @@
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.server.LocalServices;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -163,6 +165,10 @@
 
     private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
             + 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 Context mContext;
     private PackageManagerInternal mPackageManagerInternal;
     private ActivityManagerService mService;
@@ -231,9 +237,6 @@
                         mInjector);
         mService.mOomAdjuster.mAdjSeq = 10000;
         mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
-            sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
-        }
         mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
     }
 
@@ -243,6 +246,7 @@
         mService.mOomAdjuster.resetInternal();
         mService.mOomAdjuster.mActiveUids.clear();
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        mInjector.reset();
     }
 
     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
@@ -472,7 +476,8 @@
         mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
-        final int expectedAdj = sFirstCachedAdj;
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstUiCachedAdj : sFirstCachedAdj;
         assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj,
                 SCHED_GROUP_BACKGROUND);
     }
@@ -700,7 +705,9 @@
             mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
             mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
 
-            assertEquals(sFirstCachedAdj, app.mState.getSetAdj());
+            final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                    ? sFirstUiCachedAdj : sFirstCachedAdj;
+            assertEquals(expectedAdj, app.mState.getSetAdj());
             // Follow up should not have been called again.
             verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
                     followUpTimeCaptor.capture());
@@ -835,7 +842,9 @@
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
 
-        assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ,
+        int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstUiCachedAdj : CACHED_APP_MIN_ADJ;
+        assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
                 SCHED_GROUP_BACKGROUND, "previous-expired");
         // Follow up should not have been called again.
         verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -844,6 +853,55 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    public void testUpdateOomAdj_DoAll_PreviousApp() {
+        final int numberOfApps = 15;
+        final ProcessRecord[] apps = new ProcessRecord[numberOfApps];
+        for (int i = 0; i < numberOfApps; i++) {
+            apps[i] = spy(makeDefaultProcessRecord(MOCKAPP_PID + i, MOCKAPP_UID + i,
+                    MOCKAPP_PROCESSNAME + i, MOCKAPP_PACKAGENAME + i, true));
+            final WindowProcessController wpc = apps[i].getWindowProcessController();
+            doReturn(true).when(wpc).isPreviousProcess();
+            doReturn(true).when(wpc).hasActivities();
+        }
+        mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        setProcessesToLru(apps);
+        mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE);
+
+        for (int i = 0; i < numberOfApps; i++) {
+            assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+                    SCHED_GROUP_BACKGROUND, "previous");
+        }
+
+        if (!Flags.followUpOomadjUpdates()) return;
+
+        for (int i = 0; i < numberOfApps; i++) {
+            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();
+
+        for (int i = 0; i < numberOfApps; i++) {
+            final int mruIndex = numberOfApps - i - 1;
+            int expectedAdj;
+            if (mService.mConstants.USE_TIERED_CACHED_ADJ) {
+                expectedAdj = (i < numberOfApps - sUiTierSize)
+                        ? sFirstNonUiCachedAdj : sFirstUiCachedAdj + mruIndex;
+            } else {
+                expectedAdj = CACHED_APP_MIN_ADJ + (mruIndex * 2 * CACHED_APP_IMPORTANCE_LEVELS);
+                if (expectedAdj > CACHED_APP_MAX_ADJ) {
+                    expectedAdj = CACHED_APP_MAX_ADJ;
+                }
+            }
+            assertProcStates(apps[i], PROCESS_STATE_LAST_ACTIVITY, expectedAdj,
+                    SCHED_GROUP_BACKGROUND, "previous-expired");
+        }
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_DoOne_Backup() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
@@ -959,7 +1017,9 @@
         updateOomAdj(client, app);
         doReturn(null).when(mService).getTopApp();
 
-        assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1009,7 +1069,9 @@
         mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1425,7 +1487,9 @@
         bindProvider(app, app, null, null, false);
         updateOomAdj(app);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1440,7 +1504,9 @@
         mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj(app, client);
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -1548,7 +1614,9 @@
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
 
-        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
                 "cch-empty");
         // Follow up should not have been called again.
         verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -2579,12 +2647,11 @@
                 MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
         final int userOwner = 0;
         final int userOther = 1;
-        final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ
-                ? CACHED_APP_MIN_ADJ + 10
-                : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
-        final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ
-                ? CACHED_APP_MIN_ADJ + 10
-                : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
+
+        // 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;
         doReturn(userOwner).when(mService.mUserController).getCurrentUserId();
 
         final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP();
@@ -2625,8 +2692,12 @@
         mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
         updateOomAdj();
 
-        assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services");
-        assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj2, "cch-started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstUiCachedAdj : cachedAdj1,
+                SCHED_GROUP_BACKGROUND, "cch-started-ui-services", true);
+        assertProcStates(app2, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj2,
+                SCHED_GROUP_BACKGROUND, "cch-started-services", true);
 
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
@@ -2634,7 +2705,8 @@
         app.mState.setHasShownUi(false);
         updateOomAdj();
 
-        assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                "started-services", false);
 
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
@@ -2642,7 +2714,9 @@
         s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         updateOomAdj();
 
-        assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+                SCHED_GROUP_BACKGROUND, "cch-started-services", true);
 
         app.mServices.stopService(s);
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
@@ -2660,8 +2734,11 @@
         app.mServices.startService(s);
         updateOomAdj();
 
-        assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
-        assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                "started-services", false);
+        assertProcStates(app2, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+                SCHED_GROUP_BACKGROUND, "cch-started-services", true);
 
         app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT);
         app.mState.setAdjType(null);
@@ -2670,15 +2747,21 @@
         s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1;
         updateOomAdj();
 
-        assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
-        assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                "started-services", false);
+        assertProcStates(app2, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+                SCHED_GROUP_BACKGROUND, "cch-started-services", true);
 
         doReturn(userOther).when(mService.mUserController).getCurrentUserId();
         mService.mOomAdjuster.handleUserSwitchedLocked();
 
         updateOomAdj();
-        assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services");
-        assertProcStates(app2, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services");
+        assertProcStates(app, PROCESS_STATE_SERVICE,
+                mService.mConstants.USE_TIERED_CACHED_ADJ ? sFirstNonUiCachedAdj : cachedAdj1,
+                SCHED_GROUP_BACKGROUND, "cch-started-services", true);
+        assertProcStates(app2, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND,
+                "started-services", false);
     }
 
     @SuppressWarnings("GuardedBy")
@@ -2954,7 +3037,9 @@
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
 
-        assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app, PROCESS_STATE_SERVICE, expectedAdj, SCHED_GROUP_BACKGROUND,
                 "cch-started-services");
         // Follow up should not have been called again.
         verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG),
@@ -2987,14 +3072,16 @@
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
 
-        assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+        final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ
+                ? sFirstNonUiCachedAdj : sFirstCachedAdj;
+        assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
                 "cch-empty");
 
         verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime(
                 eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture());
         mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue());
         mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked();
-        assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND,
+        assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, expectedAdj, SCHED_GROUP_BACKGROUND,
                 "cch-empty");
     }
 
@@ -3077,8 +3164,10 @@
     private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
             int expectedSchedGroup) {
         final ProcessStateRecord state = app.mState;
+        final int pid = app.getPid();
         assertEquals(expectedProcState, state.getSetProcState());
         assertEquals(expectedAdj, state.getSetAdj());
+        assertEquals(expectedAdj, mInjector.mLastSetOomAdj.get(pid, INVALID_ADJ));
         assertEquals(expectedSchedGroup, state.getSetSchedGroup());
 
         // Below BFGS should never have BFSL.
@@ -3092,41 +3181,19 @@
     }
 
     @SuppressWarnings("GuardedBy")
-    private void assertProcStates(ProcessRecord app, boolean expectedCached,
-            int expectedProcState, int expectedAdj, String expectedAdjType) {
-        final ProcessStateRecord state = app.mState;
-        assertEquals(expectedCached, state.isCached());
-        assertEquals(expectedProcState, state.getSetProcState());
-        assertEquals(expectedAdj, state.getSetAdj());
-        assertEquals(expectedAdjType, state.getAdjType());
-
-        // Below BFGS should never have BFSL.
-        if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
-            assertNoBfsl(app);
-        }
-        // Above FGS should always have BFSL.
-        if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
-            assertBfsl(app);
-        }
-    }
-
-    @SuppressWarnings("GuardedBy")
     private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
             int expectedSchedGroup, String expectedAdjType) {
+        assertProcStates(app, expectedProcState, expectedAdj, expectedSchedGroup);
         final ProcessStateRecord state = app.mState;
         assertEquals(expectedAdjType, state.getAdjType());
-        assertEquals(expectedProcState, state.getSetProcState());
-        assertEquals(expectedAdj, state.getSetAdj());
-        assertEquals(expectedSchedGroup, state.getSetSchedGroup());
+    }
 
-        // Below BFGS should never have BFSL.
-        if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
-            assertNoBfsl(app);
-        }
-        // Above FGS should always have BFSL.
-        if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) {
-            assertBfsl(app);
-        }
+    @SuppressWarnings("GuardedBy")
+    private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj,
+            int expectedSchedGroup, String expectedAdjType, boolean expectedCached) {
+        assertProcStates(app, expectedProcState, expectedAdj, expectedSchedGroup, expectedAdjType);
+        final ProcessStateRecord state = app.mState;
+        assertEquals(expectedCached, state.isCached());
     }
 
     private class ProcessRecordBuilder {
@@ -3211,6 +3278,7 @@
                     eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt());
             ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid,
                     mSdkSandboxClientAppPackage, -1, null);
+            app.setPid(mPid);
             final ProcessStateRecord state = app.mState;
             final ProcessServiceRecord services = app.mServices;
             final ProcessReceiverRecord receivers = app.mReceivers;
@@ -3275,6 +3343,13 @@
     static class OomAdjusterInjector extends OomAdjuster.Injector {
         // Jump ahead in time by this offset amount.
         long mTimeOffsetMillis = 0;
+        private SparseIntArray mLastSetOomAdj = new SparseIntArray();
+
+        void reset() {
+            mTimeOffsetMillis = 0;
+            mLastSetOomAdj.clear();
+        }
+
 
         void jumpUptimeAheadTo(long uptimeMillis) {
             final long jumpMs = uptimeMillis - getUptimeMillis();
@@ -3291,5 +3366,25 @@
         long getElapsedRealtimeMillis() {
             return SystemClock.elapsedRealtime() + mTimeOffsetMillis;
         }
+
+        @Override
+        void batchSetOomAdj(ArrayList<ProcessRecord> procsToOomAdj) {
+            for (ProcessRecord proc : procsToOomAdj) {
+                final int pid = proc.getPid();
+                if (pid <= 0) continue;
+                mLastSetOomAdj.put(pid, proc.mState.getCurAdj());
+            }
+        }
+
+        @Override
+        void setOomAdj(int pid, int uid, int adj) {
+            if (pid <= 0) return;
+            mLastSetOomAdj.put(pid, adj);
+        }
+
+        @Override
+        void setThreadPriority(int tid, int priority) {
+            // do nothing
+        }
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 0703db2..18811de 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -223,6 +223,11 @@
             mShutDownActionReceiver = receiver;
             return null;
         }
+
+        @Override
+        public int getUserId() {
+            return 0;
+        }
     }
 
     @Before
@@ -237,7 +242,7 @@
         mPackageCategories = new HashMap<>();
         mPackageUids = new HashMap<>();
         mPackageName = mMockContext.getPackageName();
-        mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(mPackageName, DEFAULT_PACKAGE_UID, ApplicationInfo.CATEGORY_GAME, -1);
         LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
 
         mSetFlagsRule.enableFlags(Flags.FLAG_GAME_DEFAULT_FRAME_RATE);
@@ -245,7 +250,12 @@
     }
 
     private void mockAppCategory(String packageName, int packageUid,
-            @ApplicationInfo.Category int category)
+            @ApplicationInfo.Category int category) throws Exception {
+        mockAppCategory(packageName, packageUid, category, -1 /*userId*/);
+    }
+
+    private void mockAppCategory(String packageName, int packageUid,
+            @ApplicationInfo.Category int category, int userId)
             throws Exception {
         reset(mMockPackageManager);
         mPackageCategories.put(packageName, category);
@@ -259,8 +269,15 @@
             ApplicationInfo applicationInfo = new ApplicationInfo();
             applicationInfo.packageName = packageName;
             applicationInfo.category = category;
-            when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), anyInt()))
-                    .thenReturn(applicationInfo);
+            if (userId == -1) {
+                when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(),
+                        anyInt()))
+                        .thenReturn(applicationInfo);
+            } else {
+                when(mMockPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(),
+                        eq(userId)))
+                        .thenReturn(applicationInfo);
+            }
 
             final PackageInfo pi = new PackageInfo();
             pi.packageName = packageName;
@@ -2331,10 +2348,12 @@
 
     @Test
     public void testGamePowerMode_twoGames() throws Exception {
-        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
         String someGamePkg = "some.game";
         int somePackageId = DEFAULT_PACKAGE_UID + 1;
-        mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME,
+                ActivityManager.getCurrentUser());
         HashMap<Integer, Boolean> powerState = new HashMap<>();
         doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
                 .when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
@@ -2354,10 +2373,12 @@
 
     @Test
     public void testGamePowerMode_twoGamesOverlap() throws Exception {
-        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
         String someGamePkg = "some.game";
         int somePackageId = DEFAULT_PACKAGE_UID + 1;
-        mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(someGamePkg, somePackageId, ApplicationInfo.CATEGORY_GAME,
+                ActivityManager.getCurrentUser());
         gameManagerService.mUidObserver.onUidStateChanged(
                 DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
         gameManagerService.mUidObserver.onUidStateChanged(
@@ -2372,7 +2393,8 @@
 
     @Test
     public void testGamePowerMode_noPackage() throws Exception {
-        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
         String[] packages = {};
         when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
         gameManagerService.mUidObserver.onUidStateChanged(
@@ -2383,23 +2405,24 @@
     @Test
     public void testGamePowerMode_gameAndNotGameApps_flagOn() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
-        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
-
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
+        int userId = ActivityManager.getCurrentUser();
         String nonGamePkg1 = "not.game1";
         int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
-        mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+        mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE, userId);
 
         String nonGamePkg2 = "not.game2";
         int nonGameUid2 = DEFAULT_PACKAGE_UID + 2;
-        mockAppCategory(nonGamePkg2, nonGameUid2, ApplicationInfo.CATEGORY_IMAGE);
+        mockAppCategory(nonGamePkg2, nonGameUid2, ApplicationInfo.CATEGORY_IMAGE, userId);
 
         String gamePkg1 = "game1";
         int gameUid1 = DEFAULT_PACKAGE_UID + 3;
-        mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME, userId);
 
         String gamePkg2 = "game2";
         int gameUid2 = DEFAULT_PACKAGE_UID + 4;
-        mockAppCategory(gamePkg2, gameUid2, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(gamePkg2, gameUid2, ApplicationInfo.CATEGORY_GAME, userId);
 
         // non-game1 top and background with no-op
         gameManagerService.mUidObserver.onUidStateChanged(
@@ -2470,15 +2493,17 @@
     @Test
     public void testGamePowerMode_gameAndNotGameApps_flagOff() throws Exception {
         mSetFlagsRule.disableFlags(Flags.FLAG_DISABLE_GAME_MODE_WHEN_APP_TOP);
-        GameManagerService gameManagerService = createServiceAndStartUser(USER_ID_1);
+        int userId = ActivityManager.getCurrentUser();
+        GameManagerService gameManagerService = new GameManagerService(mMockContext,
+                mTestLooper.getLooper());
 
         String nonGamePkg1 = "not.game1";
         int nonGameUid1 = DEFAULT_PACKAGE_UID + 1;
-        mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE);
+        mockAppCategory(nonGamePkg1, nonGameUid1, ApplicationInfo.CATEGORY_IMAGE, userId);
 
         String gamePkg1 = "game1";
         int gameUid1 = DEFAULT_PACKAGE_UID + 3;
-        mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME);
+        mockAppCategory(gamePkg1, gameUid1, ApplicationInfo.CATEGORY_GAME, userId);
 
         // non-game1 top and background with no-op
         gameManagerService.mUidObserver.onUidStateChanged(
diff --git a/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java
new file mode 100644
index 0000000..6f38fca
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/crashrecovery/CrashRecoveryUtilsTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.quality.Strictness.LENIENT;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+
+/**
+ * Test CrashRecovery Utils.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CrashRecoveryUtilsTest {
+
+    private MockitoSession mStaticMockSession;
+    private final String mLogMsg = "Logging from test";
+    private final String mCrashrecoveryEventTag = "CrashRecovery Events: ";
+    private File mCacheDir;
+
+    @Before
+    public void setup() throws IOException {
+        Context context = ApplicationProvider.getApplicationContext();
+        mCacheDir = context.getCacheDir();
+        mStaticMockSession = ExtendedMockito.mockitoSession()
+                .spyStatic(Environment.class)
+                .strictness(LENIENT)
+                .startMocking();
+        ExtendedMockito.doReturn(mCacheDir).when(() -> Environment.getDataDirectory());
+
+        createCrashRecoveryEventsTempDir();
+    }
+
+    @After
+    public void tearDown() throws IOException {
+        mStaticMockSession.finishMocking();
+        deleteCrashRecoveryEventsTempFile();
+    }
+
+    @Test
+    public void testCrashRecoveryUtils() {
+        testLogCrashRecoveryEvent();
+        testDumpCrashRecoveryEvents();
+    }
+
+    @Test
+    public void testDumpCrashRecoveryEventsWithoutAnyLogs() {
+        assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse();
+        StringWriter sw = new StringWriter();
+        IndentingPrintWriter ipw = new IndentingPrintWriter(sw, "  ");
+        CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw);
+        ipw.close();
+
+        String dump = sw.getBuffer().toString();
+        assertThat(dump).contains(mCrashrecoveryEventTag);
+        assertThat(dump).doesNotContain(mLogMsg);
+    }
+
+    private void testLogCrashRecoveryEvent() {
+        assertThat(getCrashRecoveryEventsTempFile().exists()).isFalse();
+        CrashRecoveryUtils.logCrashRecoveryEvent(Log.WARN, mLogMsg);
+
+        assertThat(getCrashRecoveryEventsTempFile().exists()).isTrue();
+        String fileContent = null;
+        try {
+            File file = getCrashRecoveryEventsTempFile();
+            FileInputStream fis = new FileInputStream(file);
+            byte[] data = new byte[(int) file.length()];
+            fis.read(data);
+            fis.close();
+            fileContent = new String(data, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            fail("Unable to read the events file");
+        }
+        assertThat(fileContent).contains(mLogMsg);
+    }
+
+    private void testDumpCrashRecoveryEvents() {
+        StringWriter sw = new StringWriter();
+        IndentingPrintWriter ipw = new IndentingPrintWriter(sw, "  ");
+        CrashRecoveryUtils.dumpCrashRecoveryEvents(ipw);
+        ipw.close();
+
+        String dump = sw.getBuffer().toString();
+        assertThat(dump).contains(mCrashrecoveryEventTag);
+        assertThat(dump).contains(mLogMsg);
+    }
+
+    private void createCrashRecoveryEventsTempDir() throws IOException {
+        Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath());
+        File mMockDirectory = new File(mCacheDir, "system");
+        if (!mMockDirectory.exists()) {
+            assertThat(mMockDirectory.mkdir()).isTrue();
+        }
+    }
+
+    private void deleteCrashRecoveryEventsTempFile() throws IOException {
+        Files.deleteIfExists(getCrashRecoveryEventsTempFile().toPath());
+    }
+
+    private File getCrashRecoveryEventsTempFile() {
+        File systemTempDir = new File(mCacheDir, "system");
+        return new File(systemTempDir, "crashrecovery-events.txt");
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index d15c24b..4e1f741 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -31,8 +31,10 @@
 import static com.android.server.job.JobSchedulerService.sUptimeMillisClock;
 import static com.android.server.job.Flags.FLAG_BATCH_ACTIVE_BUCKET_JOBS;
 import static com.android.server.job.Flags.FLAG_BATCH_CONNECTIVITY_JOBS_PER_NETWORK;
+import static com.android.server.job.Flags.FLAG_CREATE_WORK_CHAIN_BY_DEFAULT;
 import static com.android.server.job.Flags.FLAG_THERMAL_RESTRICTIONS_TO_FGS_JOBS;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -58,7 +60,9 @@
 import android.app.job.JobWorkItem;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.PermissionChecker;
 import android.content.pm.PackageManager;
@@ -72,10 +76,14 @@
 import android.os.BatteryManagerInternal;
 import android.os.BatteryManagerInternal.ChargingPolicyChangeListener;
 import android.os.Looper;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.WorkSource;
+import android.os.WorkSource.WorkChain;
 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.flag.junit.SetFlagsRule;
@@ -2496,6 +2504,32 @@
         }
     }
 
+    @RequiresFlagsEnabled(FLAG_CREATE_WORK_CHAIN_BY_DEFAULT)
+    @Test
+    public void testDeriveWorkSource_flagCreateWorkChainByDefaultEnabled() {
+        final WorkSource workSource = mService.deriveWorkSource(TEST_UID, "com.test.pkg");
+        assertEquals(TEST_UID, workSource.getAttributionUid());
+
+        assertEquals(1, workSource.getWorkChains().size());
+        final WorkChain workChain = workSource.getWorkChains().get(0);
+        final int[] expectedUids = {TEST_UID, Process.SYSTEM_UID};
+        assertArrayEquals(expectedUids, workChain.getUids());
+    }
+
+    @RequiresFlagsDisabled(FLAG_CREATE_WORK_CHAIN_BY_DEFAULT)
+    @Test
+    public void testDeriveWorkSource_flagCreateWorkChainByDefaultDisabled() {
+        final ContentResolver contentResolver = mock(ContentResolver.class);
+        doReturn(contentResolver).when(mContext).getContentResolver();
+        final IContentProvider iContentProvider = mock(IContentProvider.class);
+        doReturn(iContentProvider).when(contentResolver).acquireProvider(anyString());
+
+        final WorkSource workSource = mService.deriveWorkSource(TEST_UID, "com.test.pkg");
+        assertEquals(TEST_UID, workSource.getAttributionUid());
+
+        assertNull(workSource.getWorkChains());
+    }
+
     private void setBatteryLevel(int level) {
         doReturn(level).when(mBatteryManagerInternal).getBatteryLevel();
         mService.mBatteryStateTracker
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 9ab607d..0a6edf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -314,6 +314,7 @@
         whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet())
         whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet())
         whenever(mocks.systemConfig.appMetadataFilePaths).thenReturn(ArrayMap())
+        whenever(mocks.systemConfig.oemDefinedUids).thenReturn(ArrayMap())
         wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing()
         wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true)
         wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory)
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/pm/TEST_MAPPING
index 13e255fe4..1f2d11c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/TEST_MAPPING
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/TEST_MAPPING
@@ -1,18 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksMockingServicesTests",
-      "options": [
-        {
-            "include-filter": "com.android.server.pm"
-        },
-        {
-            "exclude-annotation": "androidx.test.filters.FlakyTest"
-        },
-        {
-            "exclude-annotation": "org.junit.Ignore"
-        }
-      ]
+      "name": "FrameworksMockingServicesTests_com_android_server_pm_Presubmit"
     }
   ]
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 37d87c4e..1cba3c5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -52,6 +52,7 @@
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.multiuser.Flags;
 import android.os.PowerManager;
 import android.os.ServiceSpecificException;
@@ -152,6 +153,7 @@
     private File mTestDir;
 
     private Context mSpiedContext;
+    private Resources mSpyResources;
 
     private @Mock PackageManagerService mMockPms;
     private @Mock UserDataPreparer mMockUserDataPreparer;
@@ -193,6 +195,13 @@
         doNothing().when(mSpiedContext).sendBroadcastAsUser(any(), any(), any());
         mockIsLowRamDevice(false);
 
+        // Called when getting boot user. config_bootToHeadlessSystemUser is false by default.
+        mSpyResources = spy(mSpiedContext.getResources());
+        when(mSpiedContext.getResources()).thenReturn(mSpyResources);
+        doReturn(false)
+                .when(mSpyResources)
+                .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+
         // Must construct UserManagerService in the UiThread
         mTestDir = new File(mRealContext.getDataDir(), "umstest");
         mTestDir.mkdirs();
@@ -849,6 +858,16 @@
                         USER_TYPE_PROFILE_PRIVATE, 0, mainUser, null));
     }
 
+    @Test
+    public void testGetBootUser_enableBootToHeadlessSystemUser() {
+        setSystemUserHeadless(true);
+        doReturn(true)
+                .when(mSpyResources)
+                .getBoolean(com.android.internal.R.bool.config_bootToHeadlessSystemUser);
+
+        assertThat(mUms.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
+    }
+
     /**
      * Returns true if the user's XML file has Default restrictions
      * @param userId Id of the user.
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
index e94b8ad..6a16d1e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/Android.bp
@@ -54,3 +54,13 @@
         "automotive-tests",
     ],
 }
+
+test_module_config {
+    name: "RollbackPackageHealthObserverTests_server_rollback",
+    base: "RollbackPackageHealthObserverTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.rollback"],
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
index 0d14c9f..1b9f8d1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
@@ -40,6 +40,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -224,6 +226,45 @@
         }
     }
 
+    private LongArrayQueue getEvents(int userId, String packageName, String tag) {
+        synchronized (mQuotaTracker.mLock) {
+            return mQuotaTracker.getEvents(userId, packageName, tag);
+        }
+    }
+
+    private void updateExecutionStats(final int userId, @NonNull final String packageName,
+            @Nullable final String tag, @NonNull ExecutionStats stats) {
+        synchronized (mQuotaTracker.mLock) {
+            mQuotaTracker.updateExecutionStatsLocked(userId, packageName, tag, stats);
+        }
+    }
+
+    private ExecutionStats getExecutionStats(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        synchronized (mQuotaTracker.mLock) {
+            return mQuotaTracker.getExecutionStatsLocked(userId, packageName, tag);
+        }
+    }
+
+    private void maybeScheduleStartAlarm(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        synchronized (mQuotaTracker.mLock) {
+            mQuotaTracker.maybeScheduleStartAlarmLocked(userId, packageName, tag);
+        }
+    }
+
+    private void maybeScheduleCleanupAlarm() {
+        synchronized (mQuotaTracker.mLock) {
+            mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        }
+    }
+
+    private void deleteObsoleteEvents() {
+        synchronized (mQuotaTracker.mLock) {
+            mQuotaTracker.deleteObsoleteEventsLocked();
+        }
+    }
+
     @Test
     public void testDeleteObsoleteEventsLocked() {
         // Count window size should only apply to event list.
@@ -243,9 +284,9 @@
         expectedEvents.addLast(now - HOUR_IN_MILLIS);
         expectedEvents.addLast(now - 1);
 
-        mQuotaTracker.deleteObsoleteEventsLocked();
+        deleteObsoleteEvents();
 
-        LongArrayQueue remainingEvents = mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE,
+        LongArrayQueue remainingEvents = getEvents(TEST_USER_ID, TEST_PACKAGE,
                 TEST_TAG);
         assertTrue(longArrayQueueEquals(expectedEvents, remainingEvents));
     }
@@ -270,15 +311,15 @@
         removal.putExtra(Intent.EXTRA_UID, TEST_UID);
         mReceiver.onReceive(mContext, removal);
         assertNull(
-                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
+                getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
         assertNull(
-                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
+                getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
         assertNull(
-                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
+                getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
         assertTrue(longArrayQueueEquals(expected1,
-                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
+                getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
         assertTrue(longArrayQueueEquals(expected2,
-                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
+                getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
     }
 
     @Test
@@ -298,10 +339,10 @@
         Intent removal = new Intent(Intent.ACTION_USER_REMOVED);
         removal.putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
         mReceiver.onReceive(mContext, removal);
-        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
-        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
-        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
-        longArrayQueueEquals(expected, mQuotaTracker.getEvents(10, TEST_PACKAGE, "tag4"));
+        assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
+        assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
+        assertNull(getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
+        longArrayQueueEquals(expected, getEvents(10, TEST_PACKAGE, "tag4"));
     }
 
     @Test
@@ -323,7 +364,7 @@
         inputStats.countLimit = expectedStats.countLimit = 3;
         // Invalid time is now +24 hours since there are no sessions at all for the app.
         expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
+        updateExecutionStats(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
                 inputStats);
         assertEquals(expectedStats, inputStats);
 
@@ -333,19 +374,19 @@
         // Invalid time is now since there was an event exactly windowSizeMs ago.
         expectedStats.expirationTimeElapsed = now;
         expectedStats.countInWindow = 1;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + 2 * MINUTE_IN_MILLIS;
         expectedStats.countInWindow = 1;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 4 * MINUTE_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + 3 * MINUTE_IN_MILLIS;
         expectedStats.countInWindow = 1;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
@@ -353,13 +394,13 @@
         // minutes.
         expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
         expectedStats.countInWindow = 2;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + 45 * MINUTE_IN_MILLIS;
         expectedStats.countInWindow = 2;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
@@ -370,28 +411,28 @@
         // App is at event count limit but the oldest session is at the edge of the window, so
         // in quota time is now.
         expectedStats.inQuotaTimeElapsed = now;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
         expectedStats.countInWindow = 3;
         expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * HOUR_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
         expectedStats.countInWindow = 4;
         expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
 
         inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
         expectedStats.expirationTimeElapsed = now + 2 * HOUR_IN_MILLIS;
         expectedStats.countInWindow = 4;
         expectedStats.inQuotaTimeElapsed = now + 5 * HOUR_IN_MILLIS;
-        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
         assertEquals(expectedStats, inputStats);
     }
 
@@ -428,7 +469,7 @@
         expectedStats.countInWindow = 1;
         mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
         assertEquals(expectedStats,
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
 
         // Working
         expectedStats.expirationTimeElapsed = now;
@@ -437,7 +478,7 @@
         expectedStats.countInWindow = 2;
         mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
         assertEquals(expectedStats,
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
 
         // Frequent
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -447,7 +488,7 @@
         expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
         mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
         assertEquals(expectedStats,
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
 
         // Rare
         expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
@@ -457,7 +498,7 @@
         expectedStats.inQuotaTimeElapsed = now + 19 * HOUR_IN_MILLIS;
         mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
         assertEquals(expectedStats,
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
     }
 
     /**
@@ -481,7 +522,7 @@
         expectedStats.countInWindow = 3;
         expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + 30_000;
         assertEquals(expectedStats,
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
     }
 
     @Test
@@ -556,20 +597,20 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
 
         // No sessions saved yet.
-        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        maybeScheduleCleanupAlarm();
         verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
 
         // Test with only one timing session saved.
         final long now = mInjector.getElapsedRealtime();
         logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 6 * HOUR_IN_MILLIS);
-        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        maybeScheduleCleanupAlarm();
         verify(mAlarmManager, timeout(1000).times(1))
                 .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
 
         // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
         logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS);
         logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS);
-        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        maybeScheduleCleanupAlarm();
         verify(mAlarmManager, times(1))
                 .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
     }
@@ -587,14 +628,14 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
 
         // No sessions saved yet.
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test with timing sessions out of window.
         final long now = mInjector.getElapsedRealtime();
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 10 * HOUR_IN_MILLIS, 20);
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
@@ -602,26 +643,26 @@
         final long start = now - (6 * HOUR_IN_MILLIS);
         final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS;
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, start, 5);
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Add some more sessions, but still in quota.
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS, 1);
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 3);
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, never()).setWindow(
                 anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // Test when out of quota.
         logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 1);
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, timeout(1000).times(1)).setWindow(
                 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
                 any(Handler.class));
 
         // Alarm already scheduled, so make sure it's not scheduled again.
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         verify(mAlarmManager, times(1)).setWindow(
                 anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(),
                 any(Handler.class));
@@ -656,7 +697,7 @@
 
         // Start in ACTIVE bucket.
         mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, never())
                 .setWindow(anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(),
                         any(Handler.class));
@@ -665,40 +706,40 @@
         // And down from there.
         final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS);
         mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
                         eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS);
         mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
                         eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS);
         mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedRareAlarmTime), anyLong(),
                         eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         // And back up again.
         mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedFrequentAlarmTime), anyLong(),
                         eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .setWindow(anyInt(), eq(expectedWorkingAlarmTime), anyLong(),
                         eq(TAG_QUOTA_CHECK), any(), any(Handler.class));
 
         mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
-        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        maybeScheduleStartAlarm(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         inOrder.verify(mAlarmManager, timeout(1000).times(1))
                 .cancel(any(AlarmManager.OnAlarmListener.class));
         inOrder.verify(mAlarmManager, timeout(1000).times(0))
@@ -745,14 +786,14 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
 
         ExecutionStats stats =
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         assertEquals(0, stats.countInWindow);
 
         for (int i = 0; i < 10; ++i) {
             mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
             advanceElapsedClock(10 * SECOND_IN_MILLIS);
 
-            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
             assertEquals(0, stats.countInWindow);
         }
     }
@@ -766,14 +807,14 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
 
         ExecutionStats stats =
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         assertEquals(0, stats.countInWindow);
 
         for (int i = 0; i < 10; ++i) {
             mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
             advanceElapsedClock(10 * SECOND_IN_MILLIS);
 
-            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
             assertEquals(i + 1, stats.countInWindow);
         }
     }
@@ -785,14 +826,14 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
 
         ExecutionStats stats =
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         assertEquals(0, stats.countInWindow);
 
         for (int i = 0; i < 10; ++i) {
             mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
             advanceElapsedClock(10 * SECOND_IN_MILLIS);
 
-            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
             assertEquals(0, stats.countInWindow);
         }
     }
@@ -806,14 +847,14 @@
         mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
 
         ExecutionStats stats =
-                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+                getExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
         assertEquals(0, stats.countInWindow);
 
         for (int i = 0; i < 10; ++i) {
             mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
             advanceElapsedClock(10 * SECOND_IN_MILLIS);
 
-            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            updateExecutionStats(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
             assertEquals(i + 1, stats.countInWindow);
         }
     }
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 7d04470..b2ca991 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
@@ -631,7 +631,7 @@
         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 = 9999;
+        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);
@@ -658,7 +658,7 @@
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr1), any());
         verify(mNativeWrapperMock, never()).halSetThreads(eq(sessionPtr2), any());
         // the new TIDs pending list should be updated
-        assertArrayEquals(session2.getTidsInternal(), expectedTids2);
+        assertArrayEquals(expectedTids2, session2.getTidsInternal());
         reset(mNativeWrapperMock);
 
         // this should resume and update the threads so those never-existed invalid isolated
@@ -713,8 +713,8 @@
         // 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(session1.getTidsInternal(), SESSION_TIDS_A);
-        assertArrayEquals(session2.getTidsInternal(), expectedTids2);
+        assertArrayEquals(SESSION_TIDS_A, session1.getTidsInternal());
+        assertArrayEquals(expectedTids2, session2.getTidsInternal());
         verifyAllHintsEnabled(session1, false);
         verifyAllHintsEnabled(session2, false);
         reset(mNativeWrapperMock);
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 729dcbd..f8cc6d0 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -44,3 +44,13 @@
         enabled: false,
     },
 }
+
+test_module_config {
+    name: "PowerServiceTests_server_power",
+    base: "PowerServiceTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power"],
+}
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 d45e312..fc4d8d8 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -61,7 +61,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.Executor;
@@ -264,8 +263,8 @@
                 BatteryStats.WAKE_TYPE_PARTIAL, false);
 
         verifyNoMoreInteractions(mWakeLockLog, mBatteryStats);
-        WorkSource worksourceOld = Mockito.mock(WorkSource.class);
-        WorkSource worksourceNew = Mockito.mock(WorkSource.class);
+        WorkSource worksourceOld = new WorkSource(/*uid=*/ 1);
+        WorkSource worksourceNew = new WorkSource(/*uid=*/ 2);
 
         mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
                 "my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
@@ -309,6 +308,40 @@
         verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
     }
 
+    @Test
+    public void
+            test_notifierProcessesWorkSourceDeepCopy_OnWakelockChanging() throws RemoteException {
+        when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
+        createNotifier();
+        clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager);
+        IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
+            @Override public void onStateChanged(boolean enabled) throws RemoteException {
+                throw new RemoteException("Just testing");
+            }
+        };
+
+        final int uid = 1234;
+        final int pid = 5678;
+        mTestLooper.dispatchAll();
+        WorkSource worksourceOld = new WorkSource(/*uid=*/ 1);
+        WorkSource worksourceNew =  new WorkSource(/*uid=*/ 2);
+
+        mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceOld, /* historyTag= */ null,
+                exceptingCallback,
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
+                "my.package.name", uid, pid, worksourceNew, /* newHistoryTag= */ null,
+                exceptingCallback);
+        // The newWorksource is modified before notifier could process it.
+        worksourceNew.set(/*uid=*/ 3);
+
+        mTestLooper.dispatchAll();
+        verify(mBatteryStats).noteChangeWakelockFromSource(worksourceOld, pid,
+                "wakelockTag", null, BatteryStats.WAKE_TYPE_PARTIAL,
+                new WorkSource(/*uid=*/ 2), pid, "wakelockTag", null,
+                BatteryStats.WAKE_TYPE_FULL, false);
+    }
+
 
     @Test
     public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException {
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
index 39def75..473c8c5 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
@@ -79,8 +79,6 @@
     private static final float BRIGHTNESS = 0.99f;
     private static final float BRIGHTNESS_DOZE = 0.5f;
 
-
-
     private PowerGroup mPowerGroup;
     @Mock private PowerGroup.PowerGroupListener mWakefulnessCallbackMock;
     @Mock private Notifier mNotifier;
@@ -264,6 +262,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -282,6 +281,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -289,6 +289,7 @@
 
     @Test
     public void testUpdateWhileDozing_UpdatesDisplayPowerRequest() {
+        final boolean useNormalBrightnessForDoze = false;
         final boolean batterySaverEnabled = false;
         float brightnessFactor = 0.3f;
         PowerSaveState powerSaveState = new PowerSaveState.Builder()
@@ -306,6 +307,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                useNormalBrightnessForDoze,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -323,6 +325,51 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_ON);
         assertThat(displayPowerRequest.dozeScreenBrightness).isWithin(PRECISION).of(
                 BRIGHTNESS_DOZE);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
+        assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
+        assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
+                brightnessFactor);
+    }
+
+    @Test
+    public void testUpdateWhileDozing_useNormalBrightness() {
+        final boolean batterySaverEnabled = false;
+        final boolean useNormalBrightnessForDoze = true;
+        float brightnessFactor = 0.3f;
+        PowerSaveState powerSaveState = new PowerSaveState.Builder()
+                .setBatterySaverEnabled(batterySaverEnabled)
+                .setBrightnessFactor(brightnessFactor)
+                .build();
+        mPowerGroup.dozeLocked(TIMESTAMP1, UID, GO_TO_SLEEP_REASON_APPLICATION);
+        assertThat(mPowerGroup.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_DOZING);
+        mPowerGroup.setWakeLockSummaryLocked(WAKE_LOCK_DOZE);
+
+        mPowerGroup.updateLocked(/* screenBrightnessOverride= */ BRIGHTNESS,
+                /* overrideTag= */ null,
+                /* useProximitySensor= */ true,
+                /* boostScreenBrightness= */ true,
+                /* dozeScreenStateOverride= */ Display.STATE_ON,
+                /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
+                /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                useNormalBrightnessForDoze,
+                /* overrideDrawWakeLock= */ false,
+                powerSaveState,
+                /* quiescent= */ false,
+                /* dozeAfterScreenOff= */ false,
+                /* bootCompleted= */ true,
+                /* screenBrightnessBoostInProgress= */ false,
+                /* waitForNegativeProximity= */ false,
+                /* brightWhenDozing= */ false);
+        DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
+                mPowerGroup.mDisplayPowerRequest;
+        assertThat(displayPowerRequest.policy).isEqualTo(POLICY_DOZE);
+        assertThat(displayPowerRequest.screenBrightnessOverride).isWithin(PRECISION).of(BRIGHTNESS);
+        assertThat(displayPowerRequest.useProximitySensor).isEqualTo(true);
+        assertThat(displayPowerRequest.boostScreenBrightness).isEqualTo(true);
+        assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_ON);
+        assertThat(displayPowerRequest.dozeScreenBrightness).isWithin(PRECISION).of(
+                BRIGHTNESS_DOZE);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isTrue();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -346,6 +393,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -363,6 +411,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -385,6 +434,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ true,
@@ -402,6 +452,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -424,6 +475,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -441,6 +493,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -464,6 +517,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -481,6 +535,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -502,6 +557,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -519,6 +575,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -541,6 +598,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -558,6 +616,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
@@ -579,6 +638,7 @@
                 /* dozeScreenStateOverride= */ Display.STATE_ON,
                 /* dozeScreenStateReason= */ Display.STATE_REASON_DEFAULT_POLICY,
                 /* dozeScreenBrightness= */ BRIGHTNESS_DOZE,
+                /* useNormalBrightnessForDoze= */ false,
                 /* overrideDrawWakeLock= */ false,
                 powerSaveState,
                 /* quiescent= */ false,
@@ -596,6 +656,7 @@
         assertThat(displayPowerRequest.dozeScreenState).isEqualTo(Display.STATE_UNKNOWN);
         assertThat(displayPowerRequest.dozeScreenBrightness).isEqualTo(
                 PowerManager.BRIGHTNESS_INVALID_FLOAT);
+        assertThat(displayPowerRequest.useNormalBrightnessForDoze).isFalse();
         assertThat(displayPowerRequest.lowPowerMode).isEqualTo(batterySaverEnabled);
         assertThat(displayPowerRequest.screenLowPowerBrightnessFactor).isWithin(PRECISION).of(
                 brightnessFactor);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 40c521a..b58c28b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -1305,7 +1305,8 @@
                         Display.STATE_ON,
                         Display.STATE_REASON_DEFAULT_POLICY,
                         PowerManager.BRIGHTNESS_INVALID_FLOAT,
-                        PowerManager.BRIGHTNESS_DEFAULT);
+                        PowerManager.BRIGHTNESS_DEFAULT,
+                        /* useDozeBrightness= */ false);
         assertTrue(isAcquired[0]);
     }
 
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index 1c4db6a..c1d7c7b 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.PowerManager;
+import android.os.Process;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +55,8 @@
 
         when(mPackageManager.getPackagesForUid(101)).thenReturn(new String[]{ "some.package1" });
         when(mPackageManager.getPackagesForUid(102)).thenReturn(new String[]{ "some.package2" });
+        when(mPackageManager.getPackagesForUid(Process.SYSTEM_UID))
+                .thenReturn(new String[]{ "some.package3" });
     }
 
     @Test
@@ -70,14 +73,20 @@
         log.onWakeLockAcquired("TagFull", 102,
                 PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
 
+        when(injectorSpy.currentTimeMillis()).thenReturn(1250L);
+        log.onWakeLockAcquired("TagSystem", 1000,
+                PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+
         assertEquals("Wake Lock Log\n"
                         + "  01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
                         + "(partial,on-after-release)\n"
                         + "  01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
                         + "(full,acq-causes-wake)\n"
+                        + "  01-01 00:00:01.250 - 1000 (" + WakeLockLog.SYSTEM_PACKAGE_NAME + ")"
+                        + " - ACQ TagSystem (full,acq-causes-wake)\n"
                         + "  -\n"
-                        + "  Events: 2, Time-Resets: 0\n"
-                        + "  Buffer, Bytes used: 6\n",
+                        + "  Events: 3, Time-Resets: 0\n"
+                        + "  Buffer, Bytes used: 9\n",
                 dumpLog(log, false));
     }
 
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index b2a5b02..0e922ce 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -71,9 +71,31 @@
     ],
     srcs: [
         "src/com/android/server/power/stats/*.java",
+        "src/com/android/server/power/stats/format/*.java",
+        "src/com/android/server/power/stats/processor/*.java",
     ],
     java_resources: [
         "res/xml/power_profile*.xml",
     ],
     auto_gen_config: true,
 }
+
+test_module_config {
+    name: "PowerStatsTests_stats_bstatscputimesvalidationtest",
+    base: "PowerStatsTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power.stats.BstatsCpuTimesValidationTest"],
+}
+
+test_module_config {
+    name: "PowerStatsTests_power_stats",
+    base: "PowerStatsTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power.stats"],
+}
diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING
index fb24361..1e8d2de 100644
--- a/services/tests/powerstatstests/TEST_MAPPING
+++ b/services/tests/powerstatstests/TEST_MAPPING
@@ -14,8 +14,7 @@
       "name": "PowerStatsTestsRavenwood",
       "host": true,
       "options": [
-        {"include-filter": "com.android.server.power.stats"},
-        {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"}
+        {"include-filter": "com.android.server.power.stats"}
       ]
     }
   ],
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
deleted file mode 100644
index a4688cc..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AggregatedPowerStatsTest.java
+++ /dev/null
@@ -1,304 +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.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.BatteryConsumer;
-import android.os.PersistableBundle;
-import android.util.SparseArray;
-import android.util.Xml;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.PowerStats;
-import com.android.modules.utils.TypedXmlPullParser;
-import com.android.modules.utils.TypedXmlSerializer;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.text.ParseException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class AggregatedPowerStatsTest {
-    private static final int TEST_POWER_COMPONENT = 1077;
-    private static final int CUSTOM_POWER_COMPONENT = 1042;
-    private static final int APP_1 = 27;
-    private static final int APP_2 = 42;
-    private static final int COMPONENT_STATE_0 = 0;
-    private static final int COMPONENT_STATE_1 = 1;
-    private static final int COMPONENT_STATE_2 = 2;
-
-    private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
-    private PowerStats.Descriptor mPowerComponentDescriptor;
-
-    @Before
-    public void setup() throws ParseException {
-        mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
-        mAggregatedPowerStatsConfig.trackPowerComponent(TEST_POWER_COMPONENT)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-
-        mAggregatedPowerStatsConfig.trackCustomPowerComponents(
-                        () -> new PowerStatsProcessor() {
-                            @Override
-                            void finish(PowerComponentAggregatedPowerStats stats,
-                                    long timestampMs) {
-                            }
-                        })
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-        SparseArray<String> stateLabels = new SparseArray<>();
-        stateLabels.put(COMPONENT_STATE_1, "one");
-        mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2,
-                stateLabels, 1, 3, PersistableBundle.forPair("speed", "fast"));
-    }
-
-    @Test
-    public void aggregation() {
-        AggregatedPowerStats stats = prepareAggregatePowerStats();
-
-        verifyAggregatedPowerStats(stats);
-    }
-
-    @Test
-    public void xmlPersistence() throws Exception {
-        AggregatedPowerStats stats = prepareAggregatePowerStats();
-
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        TypedXmlSerializer serializer = Xml.newFastSerializer();
-        serializer.setOutput(baos, "UTF-8");
-        stats.writeXml(serializer);
-        serializer.flush();
-
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new ByteArrayInputStream(baos.toByteArray()), "UTF-8");
-        AggregatedPowerStats actualStats = AggregatedPowerStats.createFromXml(parser,
-                mAggregatedPowerStatsConfig);
-
-        verifyAggregatedPowerStats(actualStats);
-    }
-
-    private AggregatedPowerStats prepareAggregatePowerStats() {
-        AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
-
-        PowerStats ps = new PowerStats(mPowerComponentDescriptor);
-        stats.addPowerStats(ps, 0);
-
-        stats.addClockUpdate(1000, 456);
-        stats.setDuration(789);
-
-        stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON, 2000);
-        stats.setUidState(APP_1, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
-                BatteryConsumer.PROCESS_STATE_CACHED, 2000);
-        stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000);
-
-        ps.stats[0] = 100;
-        ps.stats[1] = 987;
-
-        ps.stateStats.put(COMPONENT_STATE_0, new long[]{1111});
-        ps.stateStats.put(COMPONENT_STATE_1, new long[]{5000});
-
-        ps.uidStats.put(APP_1, new long[]{389, 0, 739});
-        ps.uidStats.put(APP_2, new long[]{278, 314, 628});
-
-        stats.addPowerStats(ps, 3000);
-
-        stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER, 4000);
-        stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
-
-        ps.stats[0] = 444;
-        ps.stats[1] = 0;
-
-        ps.stateStats.clear();
-        ps.stateStats.put(COMPONENT_STATE_1, new long[]{1000});
-        ps.stateStats.put(COMPONENT_STATE_2, new long[]{9000});
-
-        ps.uidStats.put(APP_1, new long[]{0, 0, 400});
-        ps.uidStats.put(APP_2, new long[]{100, 200, 300});
-
-        stats.addPowerStats(ps, 5000);
-
-        PowerStats custom = new PowerStats(
-                new PowerStats.Descriptor(CUSTOM_POWER_COMPONENT, "cu570m", 1, null, 0, 2,
-                        new PersistableBundle()));
-        custom.stats = new long[]{123};
-        custom.uidStats.put(APP_1, new long[]{500, 600});
-        stats.addPowerStats(custom, 6000);
-        return stats;
-    }
-
-    private void verifyAggregatedPowerStats(AggregatedPowerStats stats) {
-        PowerStats.Descriptor descriptor = stats.getPowerComponentStats(TEST_POWER_COMPONENT)
-                .getPowerStatsDescriptor();
-        assertThat(descriptor.powerComponentId).isEqualTo(TEST_POWER_COMPONENT);
-        assertThat(descriptor.name).isEqualTo("fan");
-        assertThat(descriptor.statsArrayLength).isEqualTo(2);
-        assertThat(descriptor.uidStatsArrayLength).isEqualTo(3);
-        assertThat(descriptor.extras.getString("speed")).isEqualTo("fast");
-
-        assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
-                .isEqualTo(new long[]{322, 987});
-
-        assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
-                .isEqualTo(new long[]{222, 0});
-
-        assertThat(getStateStats(stats, COMPONENT_STATE_0,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
-                .isEqualTo(new long[]{1111});
-
-        assertThat(getStateStats(stats, COMPONENT_STATE_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
-                .isEqualTo(new long[]{5500});
-
-        assertThat(getStateStats(stats, COMPONENT_STATE_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
-                .isEqualTo(new long[]{500});
-
-        assertThat(getStateStats(stats, COMPONENT_STATE_2,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
-                .isEqualTo(new long[]{4500});
-
-        assertThat(getStateStats(stats, COMPONENT_STATE_2,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
-                .isEqualTo(new long[]{4500});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
-                .isEqualTo(new long[]{259, 0, 492});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                BatteryConsumer.PROCESS_STATE_CACHED))
-                .isEqualTo(new long[]{129, 0, 446});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
-                BatteryConsumer.PROCESS_STATE_CACHED))
-                .isEqualTo(new long[]{0, 0, 200});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_2,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
-                .isEqualTo(new long[]{185, 209, 418});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_2,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND))
-                .isEqualTo(new long[]{142, 204, 359});
-
-        assertThat(getUidDeviceStats(stats,
-                TEST_POWER_COMPONENT, APP_2,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND))
-                .isEqualTo(new long[]{50, 100, 150});
-
-        descriptor = stats.getPowerComponentStats(CUSTOM_POWER_COMPONENT)
-                .getPowerStatsDescriptor();
-        assertThat(descriptor.powerComponentId).isEqualTo(CUSTOM_POWER_COMPONENT);
-        assertThat(descriptor.statsArrayLength).isEqualTo(1);
-        assertThat(descriptor.uidStatsArrayLength).isEqualTo(2);
-
-        assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
-                .isEqualTo(new long[]{61});
-        assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
-                .isEqualTo(new long[]{61});
-        assertThat(getUidDeviceStats(stats,
-                CUSTOM_POWER_COMPONENT, APP_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                BatteryConsumer.PROCESS_STATE_CACHED))
-                .isEqualTo(new long[]{250, 300});
-        assertThat(getUidDeviceStats(stats,
-                CUSTOM_POWER_COMPONENT, APP_1,
-                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
-                BatteryConsumer.PROCESS_STATE_CACHED))
-                .isEqualTo(new long[]{250, 300});
-    }
-
-    private static long[] getDeviceStats(AggregatedPowerStats stats, int powerComponentId,
-            int... states) {
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                stats.getPowerComponentStats(powerComponentId);
-        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength];
-        powerComponentStats.getDeviceStats(out, states);
-        return out;
-    }
-
-    private static long[] getStateStats(AggregatedPowerStats stats, int key, int... states) {
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                stats.getPowerComponentStats(TEST_POWER_COMPONENT);
-        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength];
-        powerComponentStats.getStateStats(out, key, states);
-        return out;
-    }
-
-    private static long[] getUidDeviceStats(AggregatedPowerStats stats, int powerComponentId,
-            int uid, int... states) {
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                stats.getPowerComponentStats(powerComponentId);
-        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        powerComponentStats.getUidStats(out, uid, states);
-        return out;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
deleted file mode 100644
index 8d2849b..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/AmbientDisplayPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,183 +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.server.power.stats;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryConsumer;
-import android.os.Handler;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.IntSupplier;
-
-public class AmbientDisplayPowerStatsProcessorTest {
-
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setNumDisplays(2)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0);
-
-    private static final double PRECISION = 0.1;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    @Mock
-    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
-
-    private final Injector mInjector = new Injector() {
-        @Override
-        public Handler getHandler() {
-            return mStatsRule.getHandler();
-        }
-
-        @Override
-        public Clock getClock() {
-            return mStatsRule.getMockClock();
-        }
-
-        @Override
-        public PowerStatsUidResolver getUidResolver() {
-            return new PowerStatsUidResolver();
-        }
-
-        @Override
-        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-            return 0;
-        }
-
-        @Override
-        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-            return mConsumedEnergyRetriever;
-        }
-
-        @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> VOLTAGE_MV;
-        }
-
-        @Override
-        public int getDisplayCount() {
-            return 2;
-        }
-
-        @Override
-        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
-            return mScreenUsageTimeRetriever;
-        }
-    };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void processPowerStats() {
-        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats();
-
-        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 16.2);
-        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 5.4);
-        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 0);
-        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 0);
-    }
-
-    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats() {
-        ScreenPowerStatsProcessor screenPowerStatsProcessor =
-                new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile());
-        AmbientDisplayPowerStatsProcessor ambientDisplayPowerStatsProcessor =
-                new AmbientDisplayPowerStatsProcessor();
-
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
-                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                .trackUidStates(STATE_POWER, STATE_SCREEN)
-                .setProcessor(screenPowerStatsProcessor);
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
-                        BatteryConsumer.POWER_COMPONENT_SCREEN)
-                .setProcessor(ambientDisplayPowerStatsProcessor);
-
-        AggregatedPowerStats stats = new AggregatedPowerStats(config);
-
-        stats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
-        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 0);
-
-        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
-        collector.setEnabled(true);
-
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
-                .thenReturn(new int[0]);
-
-        stats.addPowerStats(collector.collectStats(), 1000);
-
-        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
-                .thenReturn(60_000L);
-        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
-                .thenReturn(120_000L);
-        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
-                .thenReturn(180_000L);
-        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
-                .thenReturn(240_000L);
-        stats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 101_000);
-        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 401_000);
-
-        // Slightly larger than 600_000 total screen time, to simulate a sight race
-        // between state changes and power stats collection
-        stats.addPowerStats(collector.collectStats(), 612_000);
-
-        stats.finish(612_000);
-
-        return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
-    }
-
-    private void assertPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
-            int powerState, int screenState, double expectedPowerEstimate) {
-        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
-        PowerStatsLayout layout = new PowerStatsLayout(descriptor);
-        long[] stats = new long[descriptor.statsArrayLength];
-        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
-        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
-                .of(expectedPowerEstimate);
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index bbab0ee..7b635d4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -38,6 +38,7 @@
 import android.hardware.power.stats.StateResidencyResult;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.platform.test.ravenwood.RavenwoodRule;
 import android.power.PowerStatsInternal;
 import android.util.IntArray;
@@ -88,6 +89,33 @@
     }
 
     @Test
+    public void testUpdateWifiState() {
+        WifiActivityEnergyInfo firstInfo = new WifiActivityEnergyInfo(1111,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 11, 22, 33, 44);
+
+        WifiActivityEnergyInfo delta = mBatteryExternalStatsWorker.extractDeltaLocked(firstInfo);
+
+        assertEquals(1111, delta.getTimeSinceBootMillis());
+        assertEquals(WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, delta.getStackState());
+        assertEquals(0, delta.getControllerTxDurationMillis());
+        assertEquals(0, delta.getControllerRxDurationMillis());
+        assertEquals(0, delta.getControllerScanDurationMillis());
+        assertEquals(0, delta.getControllerIdleDurationMillis());
+
+        WifiActivityEnergyInfo secondInfo = new WifiActivityEnergyInfo(91111,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, 811, 722, 633, 544);
+
+        delta = mBatteryExternalStatsWorker.extractDeltaLocked(secondInfo);
+
+        assertEquals(91111, delta.getTimeSinceBootMillis());
+        assertEquals(WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, delta.getStackState());
+        assertEquals(800, delta.getControllerTxDurationMillis());
+        assertEquals(700, delta.getControllerRxDurationMillis());
+        assertEquals(600, delta.getControllerScanDurationMillis());
+        assertEquals(500, delta.getControllerIdleDurationMillis());
+    }
+
+    @Test
     public void testTargetedEnergyConsumerQuerying() {
         final int numCpuClusters = 4;
         final int numDisplays = 5;
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 a1101cd..1d20538 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
@@ -117,7 +117,7 @@
     private PowerStatsStore mPowerStatsStore;
     private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
     @Mock
-    private PowerStatsExporter mPowerStatsExporter;
+    private PowerAttributor mPowerAttributor;
 
     @Before
     public void setUp() throws IOException {
@@ -149,9 +149,8 @@
         } else {
             context = InstrumentationRegistry.getContext();
         }
-        mPowerStatsStore = new PowerStatsStore(systemDir, mHandler,
-                new AggregatedPowerStatsConfig());
-        mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerStatsExporter,
+        mPowerStatsStore = new PowerStatsStore(systemDir, mHandler);
+        mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mPowerAttributor,
                 mPowerProfile, mBatteryStatsImpl.getCpuScalingPolicies(), mPowerStatsStore,
                 mMockClock);
     }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
index 37d8f2f..c5157b3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsAtomTest.java
@@ -206,22 +206,8 @@
                 20,
                 1234L,
                 UID_0,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND,
-                1000L,
-                "CustomConsumer1",
-                1650.0f,
-                450.0f,
-                0L
-        );
-        verify(statsLogger).buildStatsEvent(
-                1000L,
-                20000L,
-                10000L,
-                20,
-                1234L,
-                UID_0,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND,
-                2000L,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED,
+                0L,
                 "CustomConsumer1",
                 1650.0f,
                 450.0f,
@@ -236,10 +222,10 @@
                 UID_0,
                 BatteryConsumer.PROCESS_STATE_FOREGROUND,
                 1000L,
-                "CustomConsumer2",
+                "CustomConsumer1",
                 1650.0f,
-                500.0f,
-                800L
+                100.0f,
+                0L
         );
         verify(statsLogger).buildStatsEvent(
                 1000L,
@@ -250,6 +236,20 @@
                 UID_0,
                 BatteryConsumer.PROCESS_STATE_BACKGROUND,
                 2000L,
+                "CustomConsumer1",
+                1650.0f,
+                350.0f,
+                0L
+        );
+        verify(statsLogger).buildStatsEvent(
+                1000L,
+                20000L,
+                10000L,
+                20,
+                1234L,
+                UID_0,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED,
+                0,
                 "CustomConsumer2",
                 1650.0f,
                 500.0f,
@@ -352,21 +352,12 @@
 
         for (PowerComponentUsage componentProto : consumerProto.powerComponents) {
             final int componentId = componentProto.component;
-            if (componentId < BatteryConsumer.POWER_COMPONENT_COUNT) {
-                assertEquals(message + " for component " + componentId,
-                        convertMahToDc(consumer.getConsumedPower(componentId)),
-                        componentProto.powerDeciCoulombs);
-                assertEquals(message + " for component " + componentId,
-                        consumer.getUsageDurationMillis(componentId),
-                        componentProto.durationMillis);
-            } else {
-                assertEquals(message + " for custom component " + componentId,
-                        convertMahToDc(consumer.getConsumedPowerForCustomComponent(componentId)),
-                        componentProto.powerDeciCoulombs);
-                assertEquals(message + " for custom component " + componentId,
-                        consumer.getUsageDurationForCustomComponentMillis(componentId),
-                        componentProto.durationMillis);
-            }
+            assertEquals(message + " for component " + componentId,
+                    convertMahToDc(consumer.getConsumedPower(componentId)),
+                    componentProto.powerDeciCoulombs);
+            assertEquals(message + " for component " + componentId,
+                    consumer.getUsageDurationMillis(componentId),
+                    componentProto.durationMillis);
         }
 
         for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
@@ -506,13 +497,13 @@
                         BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CPU, 400)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 450)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 500)
                 .setUsageDurationMillis(
                         BatteryConsumer.POWER_COMPONENT_CPU, 600)
-                .setUsageDurationForCustomComponentMillis(
+                .setUsageDurationMillis(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1, 800);
 
         final BatteryConsumer.Key keyFg = uidBuilder.getKey(BatteryConsumer.POWER_COMPONENT_CPU,
@@ -533,6 +524,17 @@
                 .setConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
                 .setUsageDurationMillis(keyFgs, 8400);
 
+        final BatteryConsumer.Key keyCustomFg = uidBuilder.getKey(
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        final BatteryConsumer.Key keyCustomBg = uidBuilder.getKey(
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+        uidBuilder.setConsumedPower(
+                keyCustomFg, 100, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+        uidBuilder.setConsumedPower(
+                keyCustomBg, 350, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
+
         builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
                 .setPackageWithHighestDrain("myPackage1")
                 .setTimeInProcessStateMs(BatteryConsumer.PROCESS_STATE_FOREGROUND, 1234);
@@ -554,11 +556,11 @@
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
                         BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
                 .setUsageDurationMillis(
                         BatteryConsumer.POWER_COMPONENT_CPU, 20300)
-                .setUsageDurationForCustomComponentMillis(
+                .setUsageDurationMillis(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
 
         // Not used; just to make sure extraneous data doesn't mess things up.
@@ -567,7 +569,7 @@
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CPU, 10100,
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
 
         return builder.build();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 374426a..fde84e9 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -94,8 +94,9 @@
     public void test_getBatteryUsageStats() {
         BatteryStatsImpl batteryStats = prepareBatteryStats();
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
 
         final BatteryUsageStats batteryUsageStats =
                 provider.getBatteryUsageStats(batteryStats, BatteryUsageStatsQuery.DEFAULT);
@@ -130,8 +131,9 @@
     public void test_selectPowerComponents() {
         BatteryStatsImpl batteryStats = prepareBatteryStats();
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
 
         final BatteryUsageStats batteryUsageStats =
                 provider.getBatteryUsageStats(batteryStats,
@@ -235,8 +237,9 @@
             batteryStats.noteAlarmFinishLocked("foo", null, APP_UID, 3_001_000, 2_001_000);
         }
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
 
         final BatteryUsageStats batteryUsageStats =
                 provider.getBatteryUsageStats(batteryStats,
@@ -323,8 +326,9 @@
             }
         }
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
 
         final BatteryUsageStats batteryUsageStats =
                 provider.getBatteryUsageStats(batteryStats,
@@ -408,12 +412,12 @@
 
         PowerStatsStore powerStatsStore = new PowerStatsStore(
                 new File(mStatsRule.getHistoryDir(), "powerstatsstore"),
-                mStatsRule.getHandler(), null);
+                mStatsRule.getHandler());
         powerStatsStore.reset();
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
-                mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
 
         batteryStats.saveBatteryUsageStatsOnReset(provider, powerStatsStore);
         synchronized (batteryStats) {
@@ -522,8 +526,9 @@
             batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
         }
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), null, mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), mMockClock);
 
         PowerStatsStore powerStatsStore = mock(PowerStatsStore.class);
         doAnswer(invocation -> {
@@ -532,15 +537,15 @@
                     BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
             assertThat(device.getCustomPowerComponentName(componentId0)).isEqualTo("FOO");
             assertThat(device.getCustomPowerComponentName(componentId1)).isEqualTo("BAR");
-            assertThat(device.getConsumedPowerForCustomComponent(componentId0))
+            assertThat(device.getConsumedPower(componentId0))
                     .isWithin(PRECISION).of(27.77777);
-            assertThat(device.getConsumedPowerForCustomComponent(componentId1))
+            assertThat(device.getConsumedPower(componentId1))
                     .isWithin(PRECISION).of(55.55555);
 
             UidBatteryConsumer uid = stats.getUidBatteryConsumers().get(0);
-            assertThat(uid.getConsumedPowerForCustomComponent(componentId0))
+            assertThat(uid.getConsumedPower(componentId0))
                     .isWithin(PRECISION).of(8.33333);
-            assertThat(uid.getConsumedPowerForCustomComponent(componentId1))
+            assertThat(uid.getConsumedPower(componentId1))
                     .isWithin(PRECISION).of(8.33333);
             return null;
         }).when(powerStatsStore).storeBatteryUsageStats(anyLong(), any());
@@ -584,9 +589,9 @@
         when(powerStatsStore.loadPowerStatsSpan(1, BatteryUsageStatsSection.TYPE))
                 .thenReturn(span1);
 
-        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, null,
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies(), powerStatsStore,
-                mMockClock);
+        BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext,
+                mock(PowerAttributor.class), mStatsRule.getPowerProfile(),
+                mStatsRule.getCpuScalingPolicies(), powerStatsStore, mMockClock);
 
         BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
                 .aggregateSnapshots(0, 3000)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index 52bb5e8..3ae4c32 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -173,14 +173,15 @@
         assertThat(dump).containsMatch(quote("(not on battery, screen off/doze)") + "\\s*"
                 + "cpu: 123 apps: 123 duration: 456ms");
         assertThat(dump).containsMatch(
-                "UID 271: 1200 fg: 1777 bg: 1888 fgs: 1999 cached: 123\\s*"
-                        + quote("screen=300 cpu=5787 (27s 99ms) cpu:fg=1777 (7s 771ms) "
+                "UID 271: 1200 fg: 1777 bg: 2388 fgs: 1999 cached: 123\\s*"
+                        + quote("screen=300 cpu=400 (600ms) cpu:fg=1777 (7s 771ms) "
                         + "cpu:bg=1888 (8s 881ms) cpu:fgs=1999 (9s 991ms) "
-                        + "cpu:cached=123 (456ms) FOO=500") + "\\s*"
+                        + "cpu:cached=123 (456ms) FOO=500 (800ms) FOO:bg=500 (800ms)") + "\\s*"
                         + quote("(on battery, screen on)") + "\\s*"
-                        + quote("cpu:fg=1777 (7s 771ms)"));
+                        + quote("cpu=1777 (7s 771ms) cpu:fg=1777 (7s 771ms) "
+                        + "FOO=500 (800ms) FOO:bg=500 (800ms)"));
         assertThat(dump).containsMatch("User 42: 30.0\\s*"
-                + quote("cpu=10.0 (30ms) FOO=20.0"));
+                + quote("cpu=10.0 (30ms) FOO=20.0 (40ms)"));
     }
 
     @Test
@@ -198,10 +199,10 @@
         assertThat(dump).contains("cpu: 20100 apps: 10100 duration: 20s 300ms");
         assertThat(dump).contains("FOO: 20200 apps: 10200 duration: 20s 400ms");
         assertThat(dump).containsMatch(
-                "UID 271: 1200 fg: 1777 bg: 1888 fgs: 1999 cached: 123\\s*"
-                        + quote("screen=300 cpu=5787 (600ms) cpu:fg=1777 (7s 771ms) "
+                "UID 271: 1200 fg: 1777 bg: 2388 fgs: 1999 cached: 123\\s*"
+                        + quote("screen=300 cpu=400 (600ms) cpu:fg=1777 (7s 771ms) "
                         + "cpu:bg=1888 (8s 881ms) cpu:fgs=1999 (9s 991ms) "
-                        + "cpu:cached=123 (456ms) FOO=500"));
+                        + "cpu:cached=123 (456ms) FOO=500 (800ms) FOO:bg=500 (800ms)"));
         assertThat(dump).containsMatch("User 42: 30.0\\s*"
                 + quote("cpu=10.0 (30ms) FOO=20.0"));
     }
@@ -225,25 +226,24 @@
                         .add(stats1)
                         .add(stats2)
                         .build();
-
         assertBatteryUsageStats(sum, 42345, 50, 2234, 4345, 1234, 1000, 5000, 5000);
 
         final List<UidBatteryConsumer> uidBatteryConsumers =
                 sum.getUidBatteryConsumers();
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == APP_UID1) {
-                assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
-                        5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 11772,
+                assertUidBatteryConsumer(uidBatteryConsumer, 1200 + 924, null,
+                        5321, 6900, 532, 423, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400 + 345,
                         POWER_MODEL_UNDEFINED,
-                        956, 1167, 1478,
-                        true, 3554, 3776, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
+                        500 + 456, 1167, 1478,
+                        true, 3554, 4732, 3998, 444, 3554, 15542, 3776, 17762, 3998, 19982,
                         444, 1110);
             } else if (uidBatteryConsumer.getUid() == APP_UID2) {
                 assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
-                        1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 5985,
+                        1111, 2220, 2, 333, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 444,
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE,
                         555, 666, 777,
-                        true, 1777, 1888, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
+                        true, 1777, 2443, 1999, 321, 1777, 7771, 1888, 8881, 1999, 9991,
                         321, 654);
             } else {
                 fail("Unexpected UID " + uidBatteryConsumer.getUid());
@@ -291,9 +291,6 @@
         TypedXmlPullParser parser = Xml.newBinaryPullParser();
         parser.setInput(in, StandardCharsets.UTF_8.name());
         final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser);
-
-        System.out.println("stats = " + stats);
-        System.out.println("fromXml = " + fromXml);
         assertBatteryUsageStats1(fromXml, true);
     }
 
@@ -336,11 +333,11 @@
             builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
                     .setConsumedPower(
                             BatteryConsumer.POWER_COMPONENT_CPU, 10)
-                    .setConsumedPowerForCustomComponent(
+                    .setConsumedPower(
                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
                     .setUsageDurationMillis(
                             BatteryConsumer.POWER_COMPONENT_CPU, 30)
-                    .setUsageDurationForCustomComponentMillis(
+                    .setUsageDurationMillis(
                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
         }
         return builder;
@@ -405,11 +402,11 @@
                         BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
                 .setUsageDurationMillis(
                         BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
-                .setUsageDurationForCustomComponentMillis(
+                .setUsageDurationMillis(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
         if (builder.isProcessStateDataNeeded()) {
             final BatteryConsumer.Key cpuFgKey = builder.isScreenStateDataNeeded()
@@ -430,6 +427,15 @@
             final BatteryConsumer.Key cachedKey = uidBuilder.getKey(
                     BatteryConsumer.POWER_COMPONENT_CPU,
                     BatteryConsumer.PROCESS_STATE_CACHED);
+            final BatteryConsumer.Key customBgKey = builder.isScreenStateDataNeeded()
+                    ? uidBuilder.getKey(
+                            BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                            BatteryConsumer.PROCESS_STATE_BACKGROUND,
+                            BatteryConsumer.SCREEN_STATE_ON,
+                            BatteryConsumer.POWER_STATE_BATTERY)
+                    : uidBuilder.getKey(
+                            BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                            BatteryConsumer.PROCESS_STATE_BACKGROUND);
             uidBuilder
                     .setConsumedPower(cpuFgKey, cpuPowerForeground,
                             BatteryConsumer.POWER_MODEL_POWER_PROFILE)
@@ -442,7 +448,10 @@
                     .setUsageDurationMillis(cpuFgsKey, cpuDurationFgs)
                     .setConsumedPower(cachedKey, cpuPowerCached,
                             BatteryConsumer.POWER_MODEL_POWER_PROFILE)
-                    .setUsageDurationMillis(cachedKey, cpuDurationCached);
+                    .setUsageDurationMillis(cachedKey, cpuDurationCached)
+                    .setConsumedPower(customBgKey, customComponentPower,
+                            BatteryConsumer.POWER_MODEL_UNDEFINED)
+                    .setUsageDurationMillis(customBgKey, customComponentDuration);
         }
     }
 
@@ -456,12 +465,12 @@
                         .setConsumedPower(consumedPower)
                         .setConsumedPower(
                                 BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
-                        .setConsumedPowerForCustomComponent(
+                        .setConsumedPower(
                                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
                                 customComponentPower)
                         .setUsageDurationMillis(
                                 BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
-                        .setUsageDurationForCustomComponentMillis(
+                        .setUsageDurationMillis(
                                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
                                 customComponentDuration);
         if (builder.isPowerStateDataNeeded() || builder.isScreenStateDataNeeded()) {
@@ -511,10 +520,10 @@
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == APP_UID1) {
                 assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
-                        1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 5787,
+                        1000, 1500, 500, 300, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 400,
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE,
                         500, 600, 800,
-                        true, 1777, 1888, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
+                        true, 1777, 2388, 1999, 123, 1777, 7771, 1888, 8881, 1999, 9991, 123, 456);
             } else {
                 fail("Unexpected UID " + uidBatteryConsumer.getUid());
             }
@@ -593,11 +602,11 @@
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
         assertThat(uidBatteryConsumer.getPowerModel(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel);
-        assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(uidBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
         assertThat(uidBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
-        assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
+        assertThat(uidBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
                 customComponentDuration);
         assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
@@ -678,11 +687,11 @@
             int cpuDuration, int customComponentDuration) {
         assertThat(userBatteryConsumer.getConsumedPower(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
-        assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(userBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
         assertThat(userBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
-        assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
+        assertThat(userBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
                 customComponentDuration);
         assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
@@ -697,11 +706,11 @@
                 aggregateBatteryConsumerScopeAllApps);
         assertThat(appsBatteryConsumer.getConsumedPower(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
-        assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(appsBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
         assertThat(appsBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
-        assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
+        assertThat(appsBatteryConsumer.getUsageDurationMillis(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
                 customComponentDuration);
         assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
deleted file mode 100644
index be1c121..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BinaryStatePowerStatsProcessorTest.java
+++ /dev/null
@@ -1,283 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-public class BinaryStatePowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int POWER_COMPONENT = BatteryConsumer.POWER_COMPONENT_AUDIO;
-    private static final int TEST_STATE_FLAG = 0x1;
-
-    private final MockClock mClock = new MockClock();
-    private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
-    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
-
-    private static class TestBinaryStatePowerStatsProcessor extends BinaryStatePowerStatsProcessor {
-        TestBinaryStatePowerStatsProcessor(int powerComponentId,
-                double averagePowerMilliAmp, PowerStatsUidResolver uidResolver) {
-            super(powerComponentId, uidResolver, averagePowerMilliAmp);
-        }
-
-        @Override
-        protected int getBinaryState(BatteryStats.HistoryItem item) {
-            return (item.states & TEST_STATE_FLAG) != 0 ? STATE_ON : STATE_OFF;
-        }
-    }
-
-    @Test
-    public void powerProfileModel() {
-        TestBinaryStatePowerStatsProcessor processor = new TestBinaryStatePowerStatsProcessor(
-                POWER_COMPONENT,  /* averagePowerMilliAmp */ 100, mUidResolver);
-
-        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
-
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
-
-        processor.finish(stats, 11000);
-
-        // Total usage duration is 10000
-        // Total estimated power = 10000 * 100 = 1000000 mA-ms = 0.277777 mAh
-        // Screen-on  - 25%
-        // Screen-off - 75%
-        double expectedPower = 0.277778;
-        long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1 =
-        //     6000 * 100 = 600000 mA-ms = 0.166666 mAh
-        //     split between three different states
-        double expectedPower1 = 0.166666;
-        long[] uidStats = new long[stats.getPowerStatsDescriptor().uidStatsArrayLength];
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
-
-        // UID2 =
-        //     4000 * 100 = 400000 mA-ms = 0.111111 mAh
-        //     all in the same state
-        double expectedPower2 = 0.111111;
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2);
-
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0);
-    }
-
-    @Test
-    public void energyConsumerModel() {
-        TestBinaryStatePowerStatsProcessor processor = new TestBinaryStatePowerStatsProcessor(
-                POWER_COMPONENT,  /* averagePowerMilliAmp */ 100, mUidResolver);
-
-        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
-        PersistableBundle extras = new PersistableBundle();
-        statsLayout.toExtras(extras);
-        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(POWER_COMPONENT,
-                statsLayout.getDeviceStatsArrayLength(), null, 0,
-                statsLayout.getUidStatsArrayLength(), extras);
-        PowerStats powerStats = new PowerStats(descriptor);
-        powerStats.stats = new long[descriptor.statsArrayLength];
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        // Establish a baseline
-        processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
-
-        statsLayout.setConsumedEnergy(powerStats.stats, 0, 2_160_000);
-        processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
-
-        mClock.realtime = 11000;
-        statsLayout.setConsumedEnergy(powerStats.stats, 0, 1_440_000);
-        processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-
-        processor.finish(stats, 11000);
-
-        // Total estimated power = 3,600,000 uC = 1.0 mAh
-        // of which 3,000,000 is distributed:
-        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
-        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
-        // and 600,000 was fully with screen off:
-        //     Screen-off - 1440000 uC = 0.4 mAh
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.25);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.35 + 0.4);
-
-        // UID1 =
-        //     2,160,000 uC = 0.6 mAh
-        //     split between three different states
-        //          fg screen-on: 2500/6000
-        //          bg screen-off: 2500/6000
-        //          fgs screen-off: 1000/6000
-        double expectedPower1 = 0.6;
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
-
-        // UID2 =
-        //     1440000 mA-ms = 0.4 mAh
-        //     all in the same state
-        double expectedPower2 = 0.4;
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2);
-
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0);
-    }
-
-
-    @NonNull
-    private BatteryStats.HistoryItem buildHistoryItem(int elapsedRealtime, boolean stateOn,
-            int uid) {
-        mClock.realtime = elapsedRealtime;
-        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
-        historyItem.time = mMonotonicClock.monotonicTime();
-        historyItem.states = stateOn ? TEST_STATE_FLAG : 0;
-        if (stateOn) {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
-        } else {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
-        }
-        historyItem.eventTag = historyItem.localEventTag;
-        historyItem.eventTag.uid = uid;
-        historyItem.eventTag.string = "test";
-        return historyItem;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            BinaryStatePowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(POWER_COMPONENT)
-                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                .setProcessor(processor);
-
-        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                aggregatedPowerStats.getPowerComponentStats(POWER_COMPONENT);
-        processor.start(powerComponentStats, 0);
-
-        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return powerComponentStats;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
index 02c7b74..e392c5d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsCollectorTest.java
@@ -27,6 +27,7 @@
 import android.bluetooth.UidTraffic;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 import android.os.Handler;
@@ -37,6 +38,7 @@
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -48,7 +50,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
-import java.util.function.IntSupplier;
 
 public class BluetoothPowerStatsCollectorTest {
     private static final int APP_UID1 = 42;
@@ -132,11 +133,6 @@
                 }
 
                 @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> 3500;
-                }
-
-                @Override
                 public BluetoothPowerStatsCollector.BluetoothStatsRetriever
                         getBluetoothStatsRetriever() {
                     return mBluetoothStatsRetriever;
@@ -232,6 +228,7 @@
         BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
         collector.setEnabled(true);
 
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
                 .thenReturn(new int[]{777});
 
@@ -242,8 +239,7 @@
 
         mUidScanTimes.put(APP_UID1, 100);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{10000});
+        mockConsumedEnergy(777, 10000);
 
         // Establish a baseline
         collector.collectStats();
@@ -258,13 +254,19 @@
         mUidScanTimes.put(APP_UID2, 300);
         mUidScanTimes.put(ISOLATED_UID, 400);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{64321});
+        mockConsumedEnergy(777, 64321);
 
         mStatsRule.setTime(20000, 20000);
         return collector.collectStats();
     }
 
+    private void mockConsumedEnergy(int consumerId, long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{consumerId})))
+                .thenReturn(new EnergyConsumerResult[]{ecr});
+    }
+
     private BluetoothActivityEnergyInfo mockBluetoothActivityEnergyInfo(long timestamp,
             long rxTimeMs, long txTimeMs, long idleTimeMs, UidTraffic... uidTraffic) {
         if (RavenwoodRule.isOnRavenwood()) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
deleted file mode 100644
index c88f0a9..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BluetoothPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,540 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.UidTraffic;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryConsumer;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-import android.util.SparseLongArray;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerProfile;
-import com.android.server.power.stats.BluetoothPowerStatsCollector.BluetoothStatsRetriever;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.function.IntSupplier;
-
-public class BluetoothPowerStatsProcessorTest {
-
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int BLUETOOTH_ENERGY_CONSUMER_ID = 1;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
-            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0)
-            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0)
-            .initMeasuredEnergyStatsLocked();
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
-
-    private BluetoothActivityEnergyInfo mBluetoothActivityEnergyInfo;
-    private final SparseLongArray mUidScanTimes = new SparseLongArray();
-
-    private final BluetoothPowerStatsCollector.BluetoothStatsRetriever mBluetoothStatsRetriever =
-            new BluetoothPowerStatsCollector.BluetoothStatsRetriever() {
-                @Override
-                public void retrieveBluetoothScanTimes(Callback callback) {
-                    for (int i = 0; i < mUidScanTimes.size(); i++) {
-                        callback.onBluetoothScanTime(mUidScanTimes.keyAt(i),
-                                mUidScanTimes.valueAt(i));
-                    }
-                }
-
-                @Override
-                public boolean requestControllerActivityEnergyInfo(Executor executor,
-                        BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback) {
-                    callback.onBluetoothActivityEnergyInfoAvailable(mBluetoothActivityEnergyInfo);
-                    return true;
-                }
-            };
-
-    private final BluetoothPowerStatsCollector.Injector mInjector =
-            new BluetoothPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mPowerStatsUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PackageManager getPackageManager() {
-                    return mPackageManager;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-
-                @Override
-                public BluetoothStatsRetriever getBluetoothStatsRetriever() {
-                    return mBluetoothStatsRetriever;
-                }
-            };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
-    }
-
-    @Test
-    public void powerProfileModel_mostlyDataTransfer() {
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
-                .thenReturn(new int[0]);
-
-        BluetoothPowerStatsProcessor processor =
-                new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
-        collector.setEnabled(true);
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
-                mockUidTraffic(APP_UID1, 100, 200),
-                mockUidTraffic(APP_UID2, 300, 400));
-
-        mUidScanTimes.put(APP_UID1, 100);
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
-                mockUidTraffic(APP_UID1, 1100, 2200),
-                mockUidTraffic(APP_UID2, 3300, 4400));
-
-        mUidScanTimes.clear();
-        mUidScanTimes.put(APP_UID1, 200);
-        mUidScanTimes.put(APP_UID2, 300);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        BluetoothPowerStatsLayout statsLayout =
-                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // RX power = 'rx-duration * PowerProfile[bluetooth.controller.rx]`
-        //        RX power = 6000 * 50 = 300000 mA-ms = 0.083333 mAh
-        // TX power = 'tx-duration * PowerProfile[bluetooth.controller.tx]`
-        //        TX power = 1000 * 100 = 100000 mA-ms = 0.02777 mAh
-        // Idle power = 'idle-duration * PowerProfile[bluetooth.controller.idle]`
-        //        Idle power = 2000 * 10 = 20000 mA-ms = 0.00555 mAh
-        // Total power = RX + TX + Idle = 0.116666
-        // Screen-on  - 25%
-        // Screen-off - 75%
-        double expectedPower = 0.116666;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1 =
-        //     (1000 / 4000) * 0.083333        // rx
-        //   + (2000 / 6000) * 0.027777        // tx
-        //   = 0.030092 mAh
-        double expectedPower1 = 0.030092;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2 =
-        //     (3000 / 4000) * 0.083333        // rx
-        //   + (4000 / 6000) * 0.027777        // tx
-        //   = 0.08102 mAh
-        double expectedPower2 = 0.08102;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    @Test
-    public void powerProfileModel_mostlyScan() {
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
-                .thenReturn(new int[0]);
-
-        BluetoothPowerStatsProcessor processor =
-                new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
-        collector.setEnabled(true);
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
-                mockUidTraffic(APP_UID1, 100, 200),
-                mockUidTraffic(APP_UID2, 300, 400));
-
-        mUidScanTimes.put(APP_UID1, 100);
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
-                mockUidTraffic(APP_UID1, 1100, 2200),
-                mockUidTraffic(APP_UID2, 3300, 4400));
-
-        // Total scan time exceeding data transfer times
-        mUidScanTimes.clear();
-        mUidScanTimes.put(APP_UID1, 3100);
-        mUidScanTimes.put(APP_UID2, 5000);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        BluetoothPowerStatsLayout statsLayout =
-                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // RX power = 'rx-duration * PowerProfile[bluetooth.controller.rx]`
-        //        RX power = 6000 * 50 = 300000 mA-ms = 0.083333 mAh
-        // TX power = 'tx-duration * PowerProfile[bluetooth.controller.tx]`
-        //        TX power = 1000 * 100 = 100000 mA-ms = 0.02777 mAh
-        // Idle power = 'idle-duration * PowerProfile[bluetooth.controller.idle]`
-        //        Idle power = 2000 * 10 = 20000 mA-ms = 0.00555 mAh
-        // Total power = RX + TX + Idle = 0.116666
-        double expectedPower = 0.116666;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1 =
-        //     (3000 / 8000) * 0.083333        // rx
-        //   + (3000 / 8000) * 0.027777        // tx
-        //   = 0.041666 mAh
-        double expectedPower1 = 0.041666;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2 =
-        //     (5000 / 8000) * 0.083333        // rx
-        //   + (5000 / 8000) * 0.027777        // tx
-        //   = 0.069443 mAh
-        double expectedPower2 = 0.069443;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    @Test
-    public void consumedEnergyModel() {
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
-                .thenReturn(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID});
-
-        BluetoothPowerStatsProcessor processor =
-                new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
-        collector.setEnabled(true);
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
-                mockUidTraffic(APP_UID1, 100, 200),
-                mockUidTraffic(APP_UID2, 300, 400));
-
-        mUidScanTimes.put(APP_UID1, 100);
-
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{BLUETOOTH_ENERGY_CONSUMER_ID})).thenReturn(new long[]{0});
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
-                mockUidTraffic(APP_UID1, 1100, 2200),
-                mockUidTraffic(APP_UID2, 3300, 4400));
-
-        mUidScanTimes.clear();
-        mUidScanTimes.put(APP_UID1, 200);
-        mUidScanTimes.put(APP_UID2, 300);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        // 10 mAh represented as microWattSeconds
-        long energyUws = 10 * 3600 * VOLTAGE_MV;
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{BLUETOOTH_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        BluetoothPowerStatsLayout statsLayout =
-                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // All estimates are computed as in the #powerProfileModel_mostlyDataTransfer test,
-        // except they are all scaled by the same ratio to ensure that the total estimated
-        // energy is equal to the measured energy
-        double expectedPower = 10;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1
-        //   0.030092           // power profile model estimate
-        //   0.116666           // power profile model estimate for total power
-        //   10                 // total consumed energy
-        //   = 0.030092 * (10 / 0.116666) = 2.579365
-        double expectedPower1 = 2.579365;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2 =
-        //   0.08102            // power profile model estimate
-        //   0.116666           // power profile model estimate for total power
-        //   10                 // total consumed energy
-        //   = 0.08102 * (10 / 0.116666) = 6.944444
-        double expectedPower2 = 6.944444;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            BluetoothPowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(
-                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                        .setProcessor(processor);
-
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return aggregatedStats;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private BluetoothActivityEnergyInfo mockBluetoothActivityEnergyInfo(long timestamp,
-            long rxTimeMs, long txTimeMs, long idleTimeMs, UidTraffic... uidTraffic) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            BluetoothActivityEnergyInfo info = mock(BluetoothActivityEnergyInfo.class);
-            when(info.getControllerRxTimeMillis()).thenReturn(rxTimeMs);
-            when(info.getControllerTxTimeMillis()).thenReturn(txTimeMs);
-            when(info.getControllerIdleTimeMillis()).thenReturn(idleTimeMs);
-            when(info.getUidTraffic()).thenReturn(List.of(uidTraffic));
-            return info;
-        } else {
-            final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
-            btActivityEnergyInfoParcel.writeLong(timestamp);
-            btActivityEnergyInfoParcel.writeInt(
-                    BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE);
-            btActivityEnergyInfoParcel.writeLong(txTimeMs);
-            btActivityEnergyInfoParcel.writeLong(rxTimeMs);
-            btActivityEnergyInfoParcel.writeLong(idleTimeMs);
-            btActivityEnergyInfoParcel.writeLong(0L);
-            btActivityEnergyInfoParcel.writeTypedList(List.of(uidTraffic));
-            btActivityEnergyInfoParcel.setDataPosition(0);
-
-            BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
-                    .createFromParcel(btActivityEnergyInfoParcel);
-            btActivityEnergyInfoParcel.recycle();
-            return info;
-        }
-    }
-
-    private UidTraffic mockUidTraffic(int uid, long rxBytes, long txBytes) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            UidTraffic traffic = mock(UidTraffic.class);
-            when(traffic.getUid()).thenReturn(uid);
-            when(traffic.getRxBytes()).thenReturn(rxBytes);
-            when(traffic.getTxBytes()).thenReturn(txBytes);
-            return traffic;
-        } else {
-            final Parcel uidTrafficParcel = Parcel.obtain();
-            uidTrafficParcel.writeInt(uid);
-            uidTrafficParcel.writeLong(rxBytes);
-            uidTrafficParcel.writeLong(txBytes);
-            uidTrafficParcel.setDataPosition(0);
-
-            UidTraffic traffic = UidTraffic.CREATOR.createFromParcel(uidTrafficParcel);
-            uidTrafficParcel.recycle();
-            return traffic;
-        }
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
deleted file mode 100644
index efbd1b7..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CameraPowerStatsTest.java
+++ /dev/null
@@ -1,278 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-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.when;
-
-import android.hardware.power.stats.EnergyConsumerResult;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.Handler;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.IntSupplier;
-
-public class CameraPowerStatsTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_CAMERA, 100.0)
-            .initMeasuredEnergyStatsLocked();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int VOLTAGE_MV = 3500;
-    private static final int ENERGY_CONSUMER_ID = 777;
-
-    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-
-    EnergyConsumerPowerStatsCollector.Injector mInjector =
-            new EnergyConsumerPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-            };
-
-    private MonotonicClock mMonotonicClock;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
-    }
-
-    @Test
-    public void energyConsumerModel() {
-        when(mConsumedEnergyRetriever
-                .getEnergyConsumerIds(eq((int) EnergyConsumerType.CAMERA), any()))
-                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
-
-        CameraPowerStatsProcessor processor = new CameraPowerStatsProcessor(
-                mStatsRule.getPowerProfile(), mUidResolver);
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector);
-        collector.addConsumer(
-                powerStats -> {
-                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-                });
-        collector.setEnabled(true);
-
-        // Establish a baseline
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
-
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
-
-        mStatsRule.setTime(11_000, 11_000);
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
-        collector.collectAndDeliverStats();
-
-        processor.finish(stats, 11_000);
-
-        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
-        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
-        statsLayout.fromExtras(descriptor.extras);
-
-        // Total estimated power = 3,600,000 uC = 1.0 mAh
-        // of which 3,000,000 is distributed:
-        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
-        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
-        // and 600,000 was fully with screen off:
-        //     Screen-off - 1440000 uC = 0.4 mAh
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.25);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.35 + 0.4);
-
-        // UID1 =
-        //     2,160,000 uC = 0.6 mAh
-        //     split between three different states
-        //          fg screen-on: 2500/6000
-        //          bg screen-off: 2500/6000
-        //          fgs screen-off: 1000/6000
-        double expectedPower1 = 0.6;
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
-
-        // UID2 =
-        //     1440000 mA-ms = 0.4 mAh
-        //     all in the same state
-        double expectedPower2 = 0.4;
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2);
-
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0);
-    }
-
-    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
-            int uid) {
-        mStatsRule.setTime(timestamp, timestamp);
-        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
-        historyItem.time = mMonotonicClock.monotonicTime();
-        historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0;
-        if (stateOn) {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
-        } else {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
-        }
-        historyItem.eventTag = historyItem.localEventTag;
-        historyItem.eventTag.uid = uid;
-        historyItem.eventTag.string = "camera";
-        return historyItem;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            BinaryStatePowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(processor);
-
-        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
-        processor.start(powerComponentStats, 0);
-
-        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return powerComponentStats;
-    }
-
-    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
-        EnergyConsumerResult result = new EnergyConsumerResult();
-        result.id = id;
-        result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
-        return new EnergyConsumerResult[]{result};
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index d1105a4..1fea462 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.when;
 
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 import android.os.ConditionVariable;
@@ -42,6 +43,7 @@
 import com.android.internal.os.CpuScalingPolicies;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -53,7 +55,6 @@
 
 import java.io.IOException;
 import java.io.StringWriter;
-import java.util.function.IntSupplier;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -124,11 +125,6 @@
         }
 
         @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> 3500;
-        }
-
-        @Override
         public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
             return 0;
         }
@@ -150,7 +146,9 @@
         mHandlerThread.start();
         mHandler = mHandlerThread.getThreadHandler();
         when(mMockKernelCpuStatsReader.isSupportedFeature()).thenReturn(true);
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt())).thenReturn(new int[0]);
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(anyInt()))
+                .thenReturn(new int[0]);
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
         mUidResolver.noteIsolatedUidAdded(ISOLATED_UID, UID_2);
     }
 
@@ -228,9 +226,7 @@
         assertThat(descriptor.name).isEqualTo("cpu");
         assertThat(descriptor.statsArrayLength).isEqualTo(13);
         assertThat(descriptor.uidStatsArrayLength).isEqualTo(5);
-        CpuPowerStatsLayout layout =
-                new CpuPowerStatsLayout();
-        layout.fromExtras(descriptor.extras);
+        CpuPowerStatsLayout layout = new CpuPowerStatsLayout(descriptor);
 
         long[] deviceStats = new long[descriptor.statsArrayLength];
         layout.setTimeByScalingStep(deviceStats, 2, 42);
@@ -267,8 +263,7 @@
         mockEnergyConsumers();
 
         CpuPowerStatsCollector collector = createCollector(8, 0);
-        CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
-        layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+        CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
 
         mockKernelCpuStats(new long[]{1111, 2222, 3333},
                 new SparseArray<>() {{
@@ -338,8 +333,7 @@
         mockEnergyConsumers();
 
         CpuPowerStatsCollector collector = createCollector(8, 0);
-        CpuPowerStatsLayout layout = new CpuPowerStatsLayout();
-        layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+        CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
 
         mockKernelCpuStats(new long[]{1111, 2222, 3333},
                 new SparseArray<>() {{
@@ -462,17 +456,24 @@
 
     private void mockEnergyConsumers() {
         reset(mConsumedEnergyRetriever);
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER))
                 .thenReturn(new int[]{1, 2});
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{1, 2})))
-                .thenReturn(new long[]{1000, 2000})
-                .thenReturn(new long[]{1500, 2700});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{1, 2})))
+                .thenReturn(new EnergyConsumerResult[]{
+                        mockEnergyConsumer(1000), mockEnergyConsumer(2000)})
+                .thenReturn(new EnergyConsumerResult[]{
+                        mockEnergyConsumer(1500), mockEnergyConsumer(2700)});
+    }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
     }
 
     private static int[] getScalingStepToPowerBracketMap(CpuPowerStatsCollector collector) {
-        CpuPowerStatsLayout layout =
-                new CpuPowerStatsLayout();
-        layout.fromExtras(collector.getPowerStatsDescriptor().extras);
+        CpuPowerStatsLayout layout = new CpuPowerStatsLayout(collector.getPowerStatsDescriptor());
         return layout.getScalingStepToPowerBracketMap();
     }
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
deleted file mode 100644
index b6b759e..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,324 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-
-import android.os.BatteryConsumer;
-import android.os.PersistableBundle;
-import android.platform.test.ravenwood.RavenwoodRule;
-import android.util.LongArray;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class CpuPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
-            .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
-            .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300})
-            .setAveragePowerForCpuScalingPolicy(0, 360)
-            .setAveragePowerForCpuScalingPolicy(2, 480)
-            .setAveragePowerForCpuScalingStep(0, 0, 300)
-            .setAveragePowerForCpuScalingStep(0, 1, 400)
-            .setAveragePowerForCpuScalingStep(2, 0, 500)
-            .setCpuPowerBracketCount(3)
-            .setCpuPowerBracket(0, 0, 0)
-            .setCpuPowerBracket(0, 1, 1)
-            .setCpuPowerBracket(2, 0, 2);
-
-    private AggregatedPowerStatsConfig.PowerComponent mConfig;
-    private CpuPowerStatsProcessor mProcessor;
-    private MockPowerComponentAggregatedPowerStats mStats;
-
-    @Before
-    public void setup() {
-        mConfig = new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
-                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
-
-        mProcessor = new CpuPowerStatsProcessor(
-                mStatsRule.getPowerProfile(), mStatsRule.getCpuScalingPolicies());
-    }
-
-    @Test
-    public void powerProfileModel() {
-        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, false);
-        mStats.setDeviceStats(
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
-                concat(
-                        values(3500, 4500, 3000),   // scaling steps
-                        values(2000, 1000),         // clusters
-                        values(5000)),              // uptime
-                3.113732);
-        mStats.setDeviceStats(
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
-                concat(
-                        values(6000, 6500, 4000),
-                        values(5000, 3000),
-                        values(7000)),
-                4.607245);
-        mStats.setDeviceStats(
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
-                concat(
-                        values(9000, 10000, 7000),
-                        values(8000, 6000),
-                        values(20000)),
-                7.331799);
-        mStats.setUidStats(24,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
-                values(400, 1500, 2000),  1.206947);
-        mStats.setUidStats(42,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
-                values(900, 1000, 1500), 1.016182);
-        mStats.setUidStats(42,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
-                values(600, 500, 300), 0.385042);
-        mStats.setUidStats(42,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
-                values(1500, 2000, 1000), 1.252578);
-
-        mProcessor.finish(mStats, 10_000);
-
-        mStats.verifyPowerEstimates();
-    }
-
-    @Test
-    public void energyConsumerModel() {
-        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, true);
-        mStats.setDeviceStats(
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
-                concat(
-                        values(3500, 4500, 3000),           // scaling steps
-                        values(2000, 1000),                 // clusters
-                        values(5000),                       // uptime
-                        values(5_000_000L, 6_000_000L)),    // energy, uC
-                3.055555);
-        mStats.setDeviceStats(
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
-                concat(
-                        values(6000, 6500, 4000),
-                        values(5000, 3000),
-                        values(7000),
-                        values(5_000_000L, 6_000_000L)),    // same as above
-                3.055555);                                  // same as above - WAI
-        mStats.setDeviceStats(
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
-                concat(
-                        values(9000, 10000, 7000),
-                        values(8000, 6000),
-                        values(20000),
-                        values(8_000_000L, 18_000_000L)),
-                7.222222);
-        mStats.setUidStats(24,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
-                values(400, 1500, 2000),  1.449078);
-        mStats.setUidStats(42,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
-                values(900, 1000, 1500), 1.161902);
-        mStats.setUidStats(42,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
-                values(600, 500, 300), 0.355406);
-        mStats.setUidStats(42,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
-                values(1500, 2000, 1000), 0.80773);
-
-        mProcessor.finish(mStats, 10_000);
-
-        mStats.verifyPowerEstimates();
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private long[] values(long... values) {
-        return values;
-    }
-
-    private long[] concat(long[]... arrays) {
-        LongArray all = new LongArray();
-        for (long[] array : arrays) {
-            for (long value : array) {
-                all.add(value);
-            }
-        }
-        return all.toArray();
-    }
-
-    private static class MockPowerComponentAggregatedPowerStats extends
-            PowerComponentAggregatedPowerStats {
-        private final CpuPowerStatsLayout mStatsLayout;
-        private final PowerStats.Descriptor mDescriptor;
-        private HashMap<String, long[]> mDeviceStats = new HashMap<>();
-        private HashMap<String, long[]> mUidStats = new HashMap<>();
-        private HashSet<Integer> mUids = new HashSet<>();
-        private HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
-        private HashMap<String, Double> mExpectedUidPower = new HashMap<>();
-
-        MockPowerComponentAggregatedPowerStats(AggregatedPowerStatsConfig.PowerComponent config,
-                boolean useEnergyConsumers) {
-            super(new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-            mStatsLayout = new CpuPowerStatsLayout();
-            mStatsLayout.addDeviceSectionCpuTimeByScalingStep(3);
-            mStatsLayout.addDeviceSectionCpuTimeByCluster(2);
-            mStatsLayout.addDeviceSectionUsageDuration();
-            if (useEnergyConsumers) {
-                mStatsLayout.addDeviceSectionEnergyConsumers(2);
-            }
-            mStatsLayout.addDeviceSectionPowerEstimate();
-            mStatsLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0, 1, 2});
-            mStatsLayout.addUidSectionPowerEstimate();
-
-            PersistableBundle extras = new PersistableBundle();
-            mStatsLayout.toExtras(extras);
-            mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
-                    mStatsLayout.getDeviceStatsArrayLength(), null, 0,
-                    mStatsLayout.getUidStatsArrayLength(), extras);
-        }
-
-        @Override
-        public PowerStats.Descriptor getPowerStatsDescriptor() {
-            return mDescriptor;
-        }
-
-        @Override
-        boolean getDeviceStats(long[] outValues, int[] deviceStates) {
-            long[] values = getDeviceStats(deviceStates);
-            System.arraycopy(values, 0, outValues, 0, values.length);
-            return true;
-        }
-
-        private long[] getDeviceStats(int[] deviceStates) {
-            String key = statesToString(getConfig().getDeviceStateConfig(), deviceStates);
-            long[] values = mDeviceStats.get(key);
-            return values == null ? new long[mDescriptor.statsArrayLength] : values;
-        }
-
-        void setDeviceStats(int[] states, long[] values, double expectedPowerEstimate) {
-            setDeviceStats(states, values);
-            mExpectedDevicePower.put(statesToString(getConfig().getDeviceStateConfig(), states),
-                    expectedPowerEstimate);
-        }
-
-        @Override
-        void setDeviceStats(int[] states, long[] values) {
-            String key = statesToString(getConfig().getDeviceStateConfig(), states);
-            mDeviceStats.put(key, Arrays.copyOf(values, mDescriptor.statsArrayLength));
-        }
-
-        @Override
-        boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
-            long[] values = getUidStats(uid, uidStates);
-            assertThat(values).isNotNull();
-            System.arraycopy(values, 0, outValues, 0, values.length);
-            return true;
-        }
-
-        private long[] getUidStats(int uid, int[] uidStates) {
-            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), uidStates);
-            long[] values = mUidStats.get(key);
-            return values == null ? new long[mDescriptor.uidStatsArrayLength] : values;
-        }
-
-        void setUidStats(int uid, int[] states, long[] values, double expectedPowerEstimate) {
-            setUidStats(uid, states, values);
-            mExpectedUidPower.put(
-                    uid + " " + statesToString(getConfig().getUidStateConfig(), states),
-                    expectedPowerEstimate);
-        }
-
-        @Override
-        void setUidStats(int uid, int[] states, long[] values) {
-            mUids.add(uid);
-            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), states);
-            mUidStats.put(key, Arrays.copyOf(values, mDescriptor.uidStatsArrayLength));
-        }
-
-        @Override
-        void collectUids(Collection<Integer> uids) {
-            uids.addAll(mUids);
-        }
-
-        void verifyPowerEstimates() {
-            StringBuilder mismatches = new StringBuilder();
-            for (Map.Entry<String, Double> entry : mExpectedDevicePower.entrySet()) {
-                String key = entry.getKey();
-                double expected = mExpectedDevicePower.get(key);
-                double actual = mStatsLayout.getDevicePowerEstimate(mDeviceStats.get(key));
-                if (Math.abs(expected - actual) > 0.005) {
-                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
-                }
-            }
-            for (Map.Entry<String, Double> entry : mExpectedUidPower.entrySet()) {
-                String key = entry.getKey();
-                double expected = mExpectedUidPower.get(key);
-                double actual = mStatsLayout.getUidPowerEstimate(mUidStats.get(key));
-                if (Math.abs(expected - actual) > 0.005) {
-                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
-                }
-            }
-            if (!mismatches.isEmpty()) {
-                fail("Unexpected power estimations:\n" + mismatches);
-            }
-        }
-
-        private String statesToString(MultiStateStats.States[] config, int[] states) {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < states.length; i++) {
-                sb.append(config[i].getName()).append("=").append(states[i]).append(" ");
-            }
-            return sb.toString();
-        }
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
index 4ab706e..5636242 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
@@ -68,26 +68,26 @@
         mStatsRule.apply(calculator);
 
         UidBatteryConsumer uid = mStatsRule.getUidBatteryConsumer(APP_UID);
-        assertThat(uid.getConsumedPowerForCustomComponent(
+        assertThat(uid.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
                 .isWithin(PRECISION).of(8.333333);
-        assertThat(uid.getConsumedPowerForCustomComponent(
+        assertThat(uid.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
                 .isWithin(PRECISION).of(33.33333);
 
         final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
-        assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(deviceBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
                 .isWithin(PRECISION).of(27.77777);
-        assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(deviceBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
                 .isWithin(PRECISION).of(55.55555);
 
         final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
-        assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(appsBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID))
                 .isWithin(PRECISION).of(27.77777);
-        assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+        assertThat(appsBatteryConsumer.getConsumedPower(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + 1))
                 .isWithin(PRECISION).of(55.55555);
     }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
deleted file mode 100644
index 1621d47d..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CustomEnergyConsumerPowerStatsTest.java
+++ /dev/null
@@ -1,298 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.hardware.power.stats.EnergyConsumerAttribution;
-import android.hardware.power.stats.EnergyConsumerResult;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.os.Handler;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-import java.util.function.IntSupplier;
-
-public class CustomEnergyConsumerPowerStatsTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
-
-    public static final int ENERGY_CONSUMER_ID1 = 77;
-    public static final int ENERGY_CONSUMER_ID2 = 88;
-    private static final int VOLTAGE_MV = 3500;
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final EnergyConsumerPowerStatsLayout POWER_STATS_LAYOUT =
-            new EnergyConsumerPowerStatsLayout();
-
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-
-    private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
-
-    private EnergyConsumerPowerStatsCollector.Injector mInjector =
-            new EnergyConsumerPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mPowerStatsUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-            };
-
-
-    private CustomEnergyConsumerPowerStatsCollector mCollector;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mCollector = new CustomEnergyConsumerPowerStatsCollector(mInjector);
-        mCollector.setEnabled(true);
-    }
-
-    @Test
-    public void collectStats() throws Exception {
-        // Establish a baseline
-        collectPowerStats(0, 10_000, 30_000,
-                createAttribution(APP_UID1, 10_000),
-                createAttribution(APP_UID2, 15_000));
-
-        List<PowerStats> results = collectPowerStats(12345, 45_000, 100_000,
-                createAttribution(APP_UID1, 24_000),
-                createAttribution(APP_UID2, 36_000));
-
-        assertThat(results).hasSize(2);
-        PowerStats ps1 = results.stream()
-                .filter(ps -> ps.descriptor.name.equals("FOO")).findAny().orElseThrow();
-        assertThat(ps1.durationMs).isEqualTo(12345);
-
-        // Energy (uWs) / (voltage (mV) / 1000) -> charge (uC)
-        assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps1.stats, 0))
-                .isEqualTo(10000);
-        assertThat(ps1.uidStats.size()).isEqualTo(0);
-
-        PowerStats ps2 = results.stream()
-                .filter(ps -> ps.descriptor.name.equals("BAR")).findAny().orElseThrow();
-        assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps2.stats, 0))
-                .isEqualTo(20000);
-        assertThat(ps2.uidStats.size()).isEqualTo(2);
-        assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID1), 0))
-                .isEqualTo(4000);
-        assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID2), 0))
-                .isEqualTo(6000);
-    }
-
-    @Test
-    public void processStats() throws Exception {
-        AggregatedPowerStats aggregatedPowerStats = createAggregatedPowerStats();
-        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND,
-                0);
-        aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        List<PowerStats> results = collectPowerStats(0, 10_000, 30_000,
-                createAttribution(APP_UID1, 10_000),
-                createAttribution(APP_UID2, 15_000));
-        for (PowerStats powerStats : results) {
-            aggregatedPowerStats.addPowerStats(powerStats, 0);
-        }
-
-        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 10_000);
-        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 10_000);
-        aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
-                10_000);
-        aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE,
-                PROCESS_STATE_FOREGROUND_SERVICE, 10_000);
-
-        results = collectPowerStats(12345, 45_000, 100_000,
-                createAttribution(APP_UID1, 24_000),
-                createAttribution(APP_UID2, 36_000));
-        for (PowerStats powerStats : results) {
-            aggregatedPowerStats.addPowerStats(powerStats, 40_000);
-        }
-
-        aggregatedPowerStats.finish(40_0000);
-
-        List<PowerComponentAggregatedPowerStats> powerComponentStats =
-                aggregatedPowerStats.getPowerComponentStats();
-
-        PowerComponentAggregatedPowerStats ps1 = powerComponentStats.stream()
-                .filter(ps -> ps.getPowerStatsDescriptor().name.equals("FOO")).findAny()
-                .orElseThrow();
-        PowerComponentAggregatedPowerStats ps2 = powerComponentStats.stream()
-                .filter(ps -> ps.getPowerStatsDescriptor().name.equals("BAR")).findAny()
-                .orElseThrow();
-
-        long[] deviceStats = new long[ps1.getPowerStatsDescriptor().statsArrayLength];
-        long[] uidStats = new long[ps1.getPowerStatsDescriptor().uidStatsArrayLength];
-
-        // Total estimated power = 10,000 uC = 0.00278 mAh
-        double expectedPower = 0.00278;
-        ps1.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        ps1.getDeviceStats(deviceStats, states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER));
-        assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1: estimated power = 4,000 uC = 0.00111 mAh
-        expectedPower = 0.00111;
-        ps2.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        ps2.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID2: estimated power = 6,000 uC = 0.00166 mAh
-        expectedPower = 0.00167;
-        ps2.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        ps2.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-    }
-
-    private List<PowerStats> collectPowerStats(long timestamp, int chargeUc1, int chargeUc2,
-            EnergyConsumerAttribution... attributions2) throws Exception {
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.OTHER))
-                .thenReturn(new int[]{ENERGY_CONSUMER_ID1, ENERGY_CONSUMER_ID2});
-        when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID1))
-                .thenReturn("FOO");
-        when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID2))
-                .thenReturn("BAR");
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID1}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID1, chargeUc1, null));
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID2}))
-                .thenReturn(
-                        createEnergyConsumerResults(ENERGY_CONSUMER_ID2, chargeUc2, attributions2));
-
-        mStatsRule.setTime(timestamp, timestamp);
-        List<PowerStats> results = new ArrayList<>();
-        CountDownLatch latch = new CountDownLatch(2);
-        Consumer<PowerStats> consumer = powerStats -> {
-            results.add(powerStats);
-            latch.countDown();
-        };
-        mCollector.addConsumer(consumer);
-        mCollector.schedule();
-        assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
-        mCollector.removeConsumer(consumer);
-        return results;
-    }
-
-    private static AggregatedPowerStats createAggregatedPowerStats() {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
-                .trackDeviceStates(
-                        STATE_POWER,
-                        STATE_SCREEN)
-                .trackUidStates(
-                        STATE_POWER,
-                        STATE_SCREEN,
-                        STATE_PROCESS_STATE);
-
-        return new AggregatedPowerStats(config);
-    }
-
-    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUws,
-            EnergyConsumerAttribution[] attributions) {
-        EnergyConsumerResult result = new EnergyConsumerResult();
-        result.id = id;
-        result.energyUWs = energyUws;
-        result.attribution = attributions;
-        return new EnergyConsumerResult[]{result};
-    }
-
-    private EnergyConsumerAttribution createAttribution(int uid, long energyUWs) {
-        EnergyConsumerAttribution attribution = new EnergyConsumerAttribution();
-        attribution.uid = uid;
-        attribution.energyUWs = energyUWs;
-        return attribution;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
deleted file mode 100644
index 774be89..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/GnssPowerStatsTest.java
+++ /dev/null
@@ -1,384 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-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.when;
-
-import android.hardware.power.stats.EnergyConsumerResult;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.location.GnssSignalQuality;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.Handler;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.IntSupplier;
-
-public class GnssPowerStatsTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0)
-            .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100})
-            .initMeasuredEnergyStatsLocked();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int VOLTAGE_MV = 3500;
-    private static final int ENERGY_CONSUMER_ID = 777;
-
-    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-
-    EnergyConsumerPowerStatsCollector.Injector mInjector =
-            new EnergyConsumerPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-            };
-
-    private MonotonicClock mMonotonicClock;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
-    }
-
-    @Test
-    public void powerProfileModel() {
-        // ODPM unsupported
-        when(mConsumedEnergyRetriever
-                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
-                .thenReturn(new int[0]);
-
-        GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
-                mStatsRule.getPowerProfile(), mUidResolver);
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
-        collector.addConsumer(
-                powerStats -> {
-                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-                });
-        collector.setEnabled(true);
-
-        // Establish a baseline
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
-
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
-        processor.noteStateChange(stats, buildHistoryItem(7000,
-                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
-        processor.noteStateChange(stats, buildHistoryItem(8000,
-                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
-        mStatsRule.setTime(11_000, 11_000);
-        collector.collectAndDeliverStats();
-
-        processor.finish(stats, 11_000);
-
-        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
-        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
-        statsLayout.fromExtras(descriptor.extras);
-
-        // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
-        // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
-        // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
-        // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.06944);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.12500 + 0.83333);
-
-        // UID1 =
-        //   scr-on FG: 2500 -> 0.06944 mAh
-        //   scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
-        //   scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.06944);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.31944);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.12777);
-
-        // UID2 =
-        //   scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.51111);
-
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0);
-    }
-
-    @Test
-    public void energyConsumerModel() {
-        when(mConsumedEnergyRetriever
-                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
-                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
-        GnssPowerStatsProcessor processor = new GnssPowerStatsProcessor(
-                mStatsRule.getPowerProfile(), mUidResolver);
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
-        collector.addConsumer(
-                powerStats -> {
-                    processor.addPowerStats(stats, powerStats, mMonotonicClock.monotonicTime());
-                });
-        collector.setEnabled(true);
-
-        // Establish a baseline
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1));
-
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
-        collector.collectAndDeliverStats();
-
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2));
-        processor.noteStateChange(stats, buildHistoryItem(7000,
-                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
-        processor.noteStateChange(stats, buildHistoryItem(8000,
-                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
-        mStatsRule.setTime(11_000, 11_000);
-        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
-                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
-        collector.collectAndDeliverStats();
-
-        processor.finish(stats, 11_000);
-
-        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
-        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
-        statsLayout.fromExtras(descriptor.extras);
-
-        // Total estimated power = 3,600,000 uC = 1.0 mAh
-        // of which 3,000,000 is distributed:
-        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
-        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
-        // and 600,000 was fully with screen off:
-        //     Screen-off - 1440000 uC = 0.4 mAh
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.25);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.35 + 0.4);
-
-        // UID1 =
-        //     2,160,000 uC = 0.6 mAh
-        //     split between three different states
-        //          fg screen-on: 2500/6000
-        //          bg screen-off: 2500/6000
-        //          fgs screen-off: 1000/6000
-        double expectedPower1 = 0.6;
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
-
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
-
-        // UID2 =
-        //     1440000 mA-ms = 0.4 mAh
-        //     all in the same state
-        double expectedPower2 = 0.4;
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2);
-
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0);
-    }
-
-    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
-            int uid) {
-        mStatsRule.setTime(timestamp, timestamp);
-        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
-        historyItem.time = mMonotonicClock.monotonicTime();
-        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
-        if (stateOn) {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
-        } else {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
-        }
-        historyItem.eventTag = historyItem.localEventTag;
-        historyItem.eventTag.uid = uid;
-        historyItem.eventTag.string = "gnss";
-        return historyItem;
-    }
-
-    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) {
-        mStatsRule.setTime(timestamp, timestamp);
-        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
-        historyItem.time = mMonotonicClock.monotonicTime();
-        historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
-        historyItem.states2 =
-                signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
-        return historyItem;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            BinaryStatePowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(processor);
-
-        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
-        processor.start(powerComponentStats, 0);
-
-        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return powerComponentStats;
-    }
-
-    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
-        EnergyConsumerResult result = new EnergyConsumerResult();
-        result.id = id;
-        result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
-        return new EnergyConsumerResult[]{result};
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index ef20946..00b911b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -33,6 +33,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
@@ -51,6 +52,7 @@
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -63,7 +65,6 @@
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.IntSupplier;
 import java.util.function.LongSupplier;
 import java.util.function.Supplier;
 
@@ -139,11 +140,6 @@
         }
 
         @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> 3500;
-        }
-
-        @Override
         public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
             return mNetworkStatsSupplier;
         }
@@ -178,6 +174,7 @@
                 return uid;
             }
         });
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
         mBatteryStats = mStatsRule.getBatteryStats();
     }
 
@@ -242,8 +239,7 @@
         assertThat(powerStats.durationMs).isEqualTo(100);
 
         PowerStats.Descriptor descriptor = powerStats.descriptor;
-        MobileRadioPowerStatsLayout layout =
-                new MobileRadioPowerStatsLayout(descriptor);
+        MobileRadioPowerStatsLayout layout = new MobileRadioPowerStatsLayout(descriptor);
         assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
         assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
         assertThat(layout.getDeviceCallTime(powerStats.stats)).isEqualTo(40000);
@@ -252,7 +248,7 @@
                 .isEqualTo((64321 - 10000) * 1000 / 3500);
 
         assertThat(powerStats.stateStats.size()).isEqualTo(2);
-        long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+        long[] state1 = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
                 BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
                 ServiceState.FREQUENCY_RANGE_MMWAVE
         ));
@@ -263,7 +259,7 @@
         assertThat(layout.getStateTxTime(state1, 3)).isEqualTo(4000);
         assertThat(layout.getStateTxTime(state1, 4)).isEqualTo(5000);
 
-        long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+        long[] state2 = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
                 BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
                 ServiceState.FREQUENCY_RANGE_LOW
         ));
@@ -298,15 +294,14 @@
         assertThat(powerStats.durationMs).isEqualTo(100);
 
         PowerStats.Descriptor descriptor = powerStats.descriptor;
-        MobileRadioPowerStatsLayout layout =
-                new MobileRadioPowerStatsLayout(descriptor);
+        MobileRadioPowerStatsLayout layout = new MobileRadioPowerStatsLayout(descriptor);
         assertThat(layout.getDeviceSleepTime(powerStats.stats)).isEqualTo(200);
         assertThat(layout.getDeviceIdleTime(powerStats.stats)).isEqualTo(300);
         assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
                 .isEqualTo((64321 - 10000) * 1000 / 3500);
 
         assertThat(powerStats.stateStats.size()).isEqualTo(1);
-        long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsCollector.makeStateKey(
+        long[] stateStats = powerStats.stateStats.get(MobileRadioPowerStatsLayout.makeStateKey(
                 AccessNetworkConstants.AccessNetworkType.UNKNOWN,
                 ServiceState.FREQUENCY_RANGE_UNKNOWN
         ));
@@ -416,8 +411,8 @@
                 4321, 321, 1234, 23,
                 4000, 40, 2000, 20);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{10000});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10000)});
 
         when(mCallDurationSupplier.getAsLong()).thenReturn(10000L);
         when(mScanDurationSupplier.getAsLong()).thenReturn(20000L);
@@ -439,8 +434,8 @@
                 5321, 421, 3234, 223,
                 8000, 80, 4000, 40);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{64321});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(64321)});
         when(mCallDurationSupplier.getAsLong()).thenReturn(50000L);
         when(mScanDurationSupplier.getAsLong()).thenReturn(80000L);
 
@@ -448,6 +443,12 @@
         return collector.collectStats();
     }
 
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
     private void mockModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
             int networkType1, int freqRange1, int rxTimeMs1, @NonNull int[] txTimeMs1,
             int networkType2, int freqRange2, int rxTimeMs2, @NonNull int[] txTimeMs2) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
deleted file mode 100644
index d7024e5..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,532 +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.server.power.stats;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.net.NetworkStats;
-import android.os.BatteryConsumer;
-import android.os.Handler;
-import android.os.OutcomeReceiver;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.function.IntSupplier;
-import java.util.function.LongSupplier;
-import java.util.function.Supplier;
-
-public class MobileRadioPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
-    @Mock
-    private Context mContext;
-    @Mock
-    private PowerStatsUidResolver mPowerStatsUidResolver;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    @Mock
-    private Supplier<NetworkStats> mNetworkStatsSupplier;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private LongSupplier mCallDurationSupplier;
-    @Mock
-    private LongSupplier mScanDurationSupplier;
-
-    private final MobileRadioPowerStatsCollector.Injector mInjector =
-            new MobileRadioPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mPowerStatsUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PackageManager getPackageManager() {
-                    return mPackageManager;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-
-                @Override
-                public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
-                    return mNetworkStatsSupplier;
-                }
-
-                @Override
-                public TelephonyManager getTelephonyManager() {
-                    return mTelephonyManager;
-                }
-
-                @Override
-                public LongSupplier getCallDurationSupplier() {
-                    return mCallDurationSupplier;
-                }
-
-                @Override
-                public LongSupplier getPhoneSignalScanDurationSupplier() {
-                    return mScanDurationSupplier;
-                }
-            };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
-        when(mPowerStatsUidResolver.mapUid(anyInt()))
-                .thenAnswer(invocation -> invocation.getArgument(0));
-    }
-
-    @Test
-    public void powerProfileModel() {
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
-                .thenReturn(new int[0]);
-
-        mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator");
-
-        MobileRadioPowerStatsProcessor processor =
-                new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                        .setProcessor(processor);
-
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        MobileRadioPowerStatsCollector collector =
-                new MobileRadioPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Initial empty ModemActivityInfo.
-        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
-                new int[]{100, 200, 300, 400, 500}, 600);
-        mockModemActivityInfo(mai);
-
-        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
-        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        PowerStats powerStats = collector.collectStats();
-
-        aggregatedStats.addPowerStats(powerStats, 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        MobileRadioPowerStatsLayout statsLayout =
-                new MobileRadioPowerStatsLayout(
-                        aggregatedStats.getPowerStatsDescriptor());
-
-        //    720 mA * 100 ms  (level 0 TX drain rate * level 0 TX duration)
-        // + 1080 mA * 200 ms  (level 1 TX drain rate * level 1 TX duration)
-        // + 1440 mA * 300 ms  (level 2 TX drain rate * level 2 TX duration)
-        // + 1800 mA * 400 ms  (level 3 TX drain rate * level 3 TX duration)
-        // + 2160 mA * 500 ms  (level 4 TX drain rate * level 4 TX duration)
-        // + 1440 mA * 600 ms  (RX drain rate * RX duration)
-        // +  360 mA * 3000 ms (idle drain rate * idle duration)
-        // +   70 mA * 2000 ms (sleep drain rate * sleep duration)
-        // _________________
-        // =    4604000 mA-ms or 1.27888 mA-h
-        //   25% of 1.27888 = 0.319722
-        //   75% of 1.27888 = 0.959166
-        double totalPower = 0;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.319722);
-        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.959166);
-        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
-
-        assertThat(totalPower).isWithin(PRECISION).of(1.27888);
-
-        //    720 mA * 100 ms  (level 0 TX drain rate * level 0 TX duration)
-        // + 1080 mA * 200 ms  (level 1 TX drain rate * level 1 TX duration)
-        // + 1440 mA * 300 ms  (level 2 TX drain rate * level 2 TX duration)
-        // + 1800 mA * 400 ms  (level 3 TX drain rate * level 3 TX duration)
-        // + 2160 mA * 500 ms  (level 4 TX drain rate * level 4 TX duration)
-        // + 1440 mA * 600 ms  (RX drain rate * RX duration)
-        // _________________
-        // =    3384000 mA-ms or 0.94 mA-h
-        double uidPower1 = 0;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.17625);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.17625);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.3525);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        double uidPower2 = 0;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.05875);
-        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.17625);
-        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
-
-        assertThat(uidPower1 + uidPower2)
-                .isWithin(PRECISION).of(0.94);
-
-        // 3/4 of total packets were sent by APP_UID so 75% of total
-        assertThat(uidPower1 / (uidPower1 + uidPower2))
-                .isWithin(PRECISION).of(0.75);
-    }
-
-    @Test
-    public void energyConsumerModel() {
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                prepareAggregatedStats_energyConsumerModel();
-
-        MobileRadioPowerStatsLayout statsLayout =
-                new MobileRadioPowerStatsLayout(
-                        aggregatedStats.getPowerStatsDescriptor());
-
-        // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh
-        double totalPower = 0;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.671837);
-        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
-        assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.022494);
-        totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(2.01596);
-        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
-        assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.067484);
-        totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
-
-        // These estimates are supposed to add up to the measured energy, 2.77778 mAh
-        assertThat(totalPower).isWithin(PRECISION).of(2.77778);
-
-        double uidPower1 = 0;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.198236);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.198236);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.396473);
-        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
-
-        double uidPower2 = 0;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.066078);
-        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(0.198236);
-        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
-
-        // Total power attributed to apps is significantly less than the grand total,
-        // because we only attribute TX/RX to apps but not maintaining a connection with the cell.
-        assertThat(uidPower1 + uidPower2)
-                .isWithin(PRECISION).of(1.057259);
-
-        // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it
-        assertThat(uidPower1 / (uidPower1 + uidPower2))
-                .isWithin(PRECISION).of(0.75);
-    }
-
-    @Test
-    public void test_toString() {
-        PowerComponentAggregatedPowerStats stats = prepareAggregatedStats_energyConsumerModel();
-        String string = stats.toString();
-        assertThat(string).contains("(pwr-other scr-on)"
-                + " sleep: 500 idle: 750 scan: 1388 call: 50 energy: 2500000 power: 0.672");
-        assertThat(string).contains("(pwr-other scr-other)"
-                + " sleep: 1500 idle: 2250 scan: 4166 call: 150 energy: 7500000 power: 2.02");
-        assertThat(string).contains("(pwr-other scr-on other)"
-                + " rx: 150 tx: [25, 50, 75, 100, 125]");
-        assertThat(string).contains("(pwr-other scr-other other)"
-                + " rx: 450 tx: [75, 150, 225, 300, 375]");
-        assertThat(string).contains("(pwr-other scr-on fg)"
-                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
-        assertThat(string).contains("(pwr-other scr-other bg)"
-                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
-        assertThat(string).contains("(pwr-other scr-other fgs)"
-                + " rx-pkts: 750 rx-B: 5000 tx-pkts: 150 tx-B: 10000 power: 0.396");
-    }
-
-    private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() {
-        // PowerStats hardware is available
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
-                .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
-
-        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
-                .initMeasuredEnergyStatsLocked();
-
-        MobileRadioPowerStatsProcessor processor =
-                new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                        .setProcessor(processor);
-
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        MobileRadioPowerStatsCollector collector =
-                new MobileRadioPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Initial empty ModemActivityInfo.
-        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
-
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
-                .thenReturn(new long[]{0});
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
-                new int[]{100, 200, 300, 400, 500}, 600);
-        mockModemActivityInfo(mai);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
-
-        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
-        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
-
-        PowerStats powerStats = collector.collectStats();
-
-        aggregatedStats.addPowerStats(powerStats, 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-        return aggregatedStats;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
-        doAnswer(invocation -> {
-            OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
-                    receiver = invocation.getArgument(1);
-            receiver.onResult(emptyMai);
-            return null;
-        }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
-    }
-
-    private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
-            NetworkStats.Entry... entries) {
-        NetworkStats stats;
-        if (RavenwoodRule.isOnRavenwood()) {
-            stats = mock(NetworkStats.class);
-            when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
-        } else {
-            stats = new NetworkStats(elapsedTime, initialSize);
-            for (NetworkStats.Entry entry : entries) {
-                stats = stats.addEntry(entry);
-            }
-        }
-        return stats;
-    }
-
-    private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
-            int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
-            long rxPackets, long txBytes, long txPackets, long operations) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
-            when(entry.getUid()).thenReturn(uid);
-            when(entry.getMetered()).thenReturn(metered);
-            when(entry.getRoaming()).thenReturn(roaming);
-            when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
-            when(entry.getRxBytes()).thenReturn(rxBytes);
-            when(entry.getRxPackets()).thenReturn(rxPackets);
-            when(entry.getTxBytes()).thenReturn(txBytes);
-            when(entry.getTxPackets()).thenReturn(txPackets);
-            when(entry.getOperations()).thenReturn(operations);
-            return entry;
-        } else {
-            return new NetworkStats.Entry(iface, uid, set, tag, metered,
-                    roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
-        }
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
deleted file mode 100644
index ae258cd3..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ /dev/null
@@ -1,259 +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.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assert.assertThrows;
-
-import android.os.BatteryConsumer;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class MultiStateStatsTest {
-    public static final int DIMENSION_COUNT = 2;
-
-    @Test
-    public void compositeStateIndex_allEnabled() {
-        MultiStateStats.Factory factory = makeFactory(true, true, true);
-        assertThatCpuPerformanceStatsFactory(factory)
-                .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 4)
-                .haveDifferentSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
-    }
-
-    @Test
-    public void compositeStateIndex_procStateTrackingDisabled() {
-        MultiStateStats.Factory factory = makeFactory(true, false, true);
-        assertThatCpuPerformanceStatsFactory(factory)
-                .hasSerialStateCount(4)
-                .haveDifferentSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
-                .haveSameSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
-                .haveSameSerialStates(
-                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
-                .haveSameSerialStates(
-                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
-                .haveSameSerialStates(
-                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
-    }
-
-    @Test
-    public void compositeStateIndex_screenTrackingDisabled() {
-        MultiStateStats.Factory factory = makeFactory(true, true, false);
-        assertThatCpuPerformanceStatsFactory(factory)
-                .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 2)
-                .haveDifferentSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
-                .haveSameSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
-                .haveSameSerialStates(
-                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
-    }
-
-    @Test
-    public void compositeStateIndex_allDisabled() {
-        MultiStateStats.Factory factory = makeFactory(false, false, false);
-        assertThatCpuPerformanceStatsFactory(factory)
-                .hasSerialStateCount(1)
-                .haveSameSerialStates(
-                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
-    }
-
-    @Test
-    public void tooManyStates() {
-        // 4 bits needed to represent
-        String[] labels = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
-        // 4 * 10 = 40 bits needed to represent the composite state
-        MultiStateStats.States[] states = new MultiStateStats.States[10];
-        for (int i = 0; i < states.length; i++) {
-            states[i] = new MultiStateStats.States("foo", true, labels);
-        }
-        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
-                () -> new MultiStateStats.Factory(DIMENSION_COUNT, states));
-        assertThat(e.getMessage()).contains("40");
-    }
-
-    @Test
-    public void multiStateStats_aggregation() {
-        MultiStateStats.Factory factory = makeFactory(true, true, false);
-        MultiStateStats multiStateStats = factory.create();
-        multiStateStats.setState(0 /* batteryState */, 1 /* on */, 1000);
-        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
-        multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
-
-        multiStateStats.increment(new long[]{100, 200}, 1000);
-
-        multiStateStats.setState(0 /* batteryState */, 0 /* off */, 2000);
-        multiStateStats.setState(2 /* screenState */, 1 /* on */, 2000); // untracked
-
-        multiStateStats.increment(new long[]{300, 500}, 3000);
-
-        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
-
-        multiStateStats.increment(new long[]{200, 200}, 5000);
-
-        long[] stats = new long[DIMENSION_COUNT];
-        multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
-        // (400 - 100) * 0.5 + (600 - 400) * 0.5
-        assertThat(stats).isEqualTo(new long[]{250, 350});
-
-        multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
-        // (400 - 100) * 0.5 + (600 - 400) * 0
-        assertThat(stats).isEqualTo(new long[]{150, 250});
-
-        // Note that screen state does not affect the result, as it is untracked
-        multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_BACKGROUND, 1});
-        // (400 - 100) * 0 + (600 - 400) * 0.5
-        assertThat(stats).isEqualTo(new long[]{100, 100});
-
-        multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0});
-        // Never been in this composite state
-        assertThat(stats).isEqualTo(new long[]{0, 0});
-    }
-
-    @Test
-    public void test_toString() {
-        MultiStateStats.Factory factory = makeFactory(true, true, false);
-        MultiStateStats multiStateStats = factory.create();
-        multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000);
-        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
-        multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
-        multiStateStats.setState(0 /* batteryState */, 1 /* on */, 2000);
-        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000);
-        multiStateStats.increment(new long[]{100, 200}, 5000);
-
-        assertThat(multiStateStats.toString()).isEqualTo(
-                "(plugged-in fg) [25, 50]\n"
-                + "(on-battery fg) [25, 50]\n"
-                + "(on-battery bg) [50, 100]"
-        );
-    }
-
-    private static MultiStateStats.Factory makeFactory(boolean trackBatteryState,
-            boolean trackProcState, boolean trackScreenState) {
-        return new MultiStateStats.Factory(DIMENSION_COUNT,
-                new MultiStateStats.States("bs", trackBatteryState, "plugged-in", "on-battery"),
-                new MultiStateStats.States("ps", trackProcState,
-                        BatteryConsumer.processStateToString(
-                                BatteryConsumer.PROCESS_STATE_UNSPECIFIED),
-                        BatteryConsumer.processStateToString(
-                                BatteryConsumer.PROCESS_STATE_FOREGROUND),
-                        BatteryConsumer.processStateToString(
-                                BatteryConsumer.PROCESS_STATE_BACKGROUND),
-                        BatteryConsumer.processStateToString(
-                                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
-                        BatteryConsumer.processStateToString(
-                                BatteryConsumer.PROCESS_STATE_CACHED)),
-                new MultiStateStats.States("scr", trackScreenState, "screen-off", "plugged-in"));
-    }
-
-    private FactorySubject assertThatCpuPerformanceStatsFactory(
-            MultiStateStats.Factory factory) {
-        FactorySubject subject = new FactorySubject();
-        subject.mFactory = factory;
-        return subject;
-    }
-
-    private static class FactorySubject {
-        private MultiStateStats.Factory mFactory;
-
-        FactorySubject hasSerialStateCount(int stateCount) {
-            assertThat(mFactory.getSerialStateCount()).isEqualTo(stateCount);
-            return this;
-        }
-
-        public FactorySubject haveDifferentSerialStates(State... states) {
-            int[] serialStates = getSerialStates(states);
-            assertWithMessage("Expected all to be different: " + Arrays.toString(serialStates))
-                    .that(Arrays.stream(serialStates).distinct().toArray())
-                    .hasLength(states.length);
-            return this;
-        }
-
-        public FactorySubject haveSameSerialStates(State... states) {
-            int[] serialStates = getSerialStates(states);
-            assertWithMessage("Expected all to be the same: " + Arrays.toString(serialStates))
-                    .that(Arrays.stream(serialStates).distinct().toArray())
-                    .hasLength(1);
-            return this;
-        }
-
-        private int[] getSerialStates(State[] states) {
-            int[] serialStates = new int[states.length];
-            for (int i = 0; i < states.length; i++) {
-                serialStates[i] = mFactory.getSerialState(
-                        new int[]{
-                                states[i].batteryState ? 0 : 1,
-                                states[i].procstate,
-                                states[i].screenState ? 0 : 1
-                        });
-            }
-            return serialStates;
-        }
-    }
-
-    private State state(boolean batteryState, boolean screenState, int procstate) {
-        State state = new State();
-        state.batteryState = batteryState;
-        state.screenState = screenState;
-        state.procstate = procstate;
-        return state;
-    }
-
-    private static class State {
-        public boolean batteryState;
-        public boolean screenState;
-        public int procstate;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
deleted file mode 100644
index c268110..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,237 +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.server.power.stats;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.net.NetworkStats;
-import android.os.BatteryConsumer;
-import android.os.Handler;
-import android.os.OutcomeReceiver;
-import android.platform.test.ravenwood.RavenwoodRule;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.os.Clock;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.IntSupplier;
-import java.util.function.LongSupplier;
-import java.util.function.Supplier;
-
-public class PhoneCallPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    private static final double PRECISION = 0.00001;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
-    @Mock
-    private Context mContext;
-    @Mock
-    private PowerStatsUidResolver mPowerStatsUidResolver;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    @Mock
-    private Supplier<NetworkStats> mNetworkStatsSupplier;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private LongSupplier mCallDurationSupplier;
-    @Mock
-    private LongSupplier mScanDurationSupplier;
-
-    private final MobileRadioPowerStatsCollector.Injector mInjector =
-            new MobileRadioPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mPowerStatsUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PackageManager getPackageManager() {
-                    return mPackageManager;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-
-                @Override
-                public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
-                    return mNetworkStatsSupplier;
-                }
-
-                @Override
-                public TelephonyManager getTelephonyManager() {
-                    return mTelephonyManager;
-                }
-
-                @Override
-                public LongSupplier getCallDurationSupplier() {
-                    return mCallDurationSupplier;
-                }
-
-                @Override
-                public LongSupplier getPhoneSignalScanDurationSupplier() {
-                    return mScanDurationSupplier;
-                }
-            };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
-        when(mPowerStatsUidResolver.mapUid(anyInt()))
-                .thenAnswer(invocation -> invocation.getArgument(0));
-
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
-                .thenReturn(new int[0]);
-
-        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
-    }
-
-    @Test
-    public void copyEstimatesFromMobileRadioPowerStats() {
-        MobileRadioPowerStatsProcessor mobileStatsProcessor =
-                new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PhoneCallPowerStatsProcessor phoneStatsProcessor =
-                new PhoneCallPowerStatsProcessor();
-
-        AggregatedPowerStatsConfig aggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
-        aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                .setProcessor(mobileStatsProcessor);
-        aggregatedPowerStatsConfig.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
-                .setProcessor(phoneStatsProcessor);
-
-        AggregatedPowerStats aggregatedPowerStats =
-                new AggregatedPowerStats(aggregatedPowerStatsConfig);
-        PowerComponentAggregatedPowerStats mobileRadioStats =
-                aggregatedPowerStats.getPowerComponentStats(
-                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
-
-        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-
-        MobileRadioPowerStatsCollector collector =
-                new MobileRadioPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Initial empty ModemActivityInfo.
-        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
-
-        // Establish a baseline
-        aggregatedPowerStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-
-        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
-                new int[]{100, 200, 300, 400, 500}, 600);
-        mockModemActivityInfo(mai);
-
-        // A phone call was made
-        when(mCallDurationSupplier.getAsLong()).thenReturn(7000L);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000);
-
-        mobileStatsProcessor.finish(mobileRadioStats, 10_000);
-
-        PowerComponentAggregatedPowerStats stats =
-                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
-        phoneStatsProcessor.finish(stats, 10_000);
-
-        PowerStatsLayout statsLayout =
-                new PowerStatsLayout(stats.getPowerStatsDescriptor());
-
-        long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(0.7);
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(2.1);
-    }
-
-    private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
-        doAnswer(invocation -> {
-            OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
-                    receiver = invocation.getArgument(1);
-            receiver.onResult(emptyMai);
-            return null;
-        }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
deleted file mode 100644
index 3929137..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsAggregatorTest.java
+++ /dev/null
@@ -1,269 +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.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.PersistableBundle;
-
-import androidx.annotation.NonNull;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.TimeZone;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PowerStatsAggregatorTest {
-    private static final int TEST_POWER_COMPONENT = 77;
-    private static final int TEST_UID = 42;
-    private static final long START_TIME = 1234;
-
-    private final MockClock mClock = new MockClock();
-    private final MonotonicClock mMonotonicClock = new MonotonicClock(START_TIME, mClock);
-    private BatteryStatsHistory mHistory;
-    private PowerStatsAggregator mAggregator;
-    private int mAggregatedStatsCount;
-
-    @Before
-    public void setup() throws ParseException {
-        mHistory = new BatteryStatsHistory(null, null, 0, 1024,
-                mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
-                mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class), null);
-
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(TEST_POWER_COMPONENT)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-        mAggregator = new PowerStatsAggregator(config, mHistory);
-    }
-
-    @Test
-    public void stateUpdates() {
-        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
-                "majorDrain", 1, null, 0, 1, new PersistableBundle());
-        PowerStats powerStats = new PowerStats(descriptor);
-
-        mClock.currentTime = 1222156800000L;    // An important date in world history
-
-        mHistory.forceRecordAllHistory();
-        powerStats.stats = new long[]{0};
-        powerStats.uidStats.put(TEST_UID, new long[]{0});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-
-        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
-        mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
-                BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
-        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND);
-
-        advance(1000);
-
-        powerStats.stats = new long[]{10000};
-        powerStats.uidStats.put(TEST_UID, new long[]{1234});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-
-        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
-        mHistory.recordStateStopEvent(mClock.realtime, mClock.uptime,
-                BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
-
-        advance(1000);
-
-        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND);
-
-        advance(1000);
-
-        mClock.currentTime += 60 * 60 * 1000;       // one hour
-        mHistory.recordCurrentTimeChange(mClock.realtime, mClock.uptime, mClock.currentTime);
-
-        advance(2000);
-
-        powerStats.stats = new long[]{20000};
-        powerStats.uidStats.put(TEST_UID, new long[]{4444});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-
-        mAggregator.aggregatePowerStats(0, MonotonicClock.UNDEFINED, stats -> {
-            assertThat(mAggregatedStatsCount++).isEqualTo(0);
-            assertThat(stats.getStartTime()).isEqualTo(START_TIME);
-
-            List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
-            assertThat(clockUpdates).hasSize(2);
-
-            AggregatedPowerStats.ClockUpdate clockUpdate0 = clockUpdates.get(0);
-            assertThat(clockUpdate0.monotonicTime).isEqualTo(1234);
-            assertThat(formatDateTime(clockUpdate0.currentTime)).isEqualTo("2008-09-23 08:00:00");
-
-            AggregatedPowerStats.ClockUpdate clockUpdate1 = clockUpdates.get(1);
-            assertThat(clockUpdate1.monotonicTime).isEqualTo(1234 + 3000);
-            assertThat(formatDateTime(clockUpdate1.currentTime)).isEqualTo("2008-09-23 09:00:03");
-
-            assertThat(stats.getDuration()).isEqualTo(5000);
-
-            long[] values = new long[1];
-
-            PowerComponentAggregatedPowerStats powerComponentStats = stats.getPowerComponentStats(
-                    TEST_POWER_COMPONENT);
-
-            assertThat(powerComponentStats.getDeviceStats(values, new int[]{
-                    AggregatedPowerStatsConfig.POWER_STATE_OTHER,
-                    AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
-                    .isTrue();
-            assertThat(values).isEqualTo(new long[]{10000});
-
-            assertThat(powerComponentStats.getDeviceStats(values, new int[]{
-                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER}))
-                    .isTrue();
-            assertThat(values).isEqualTo(new long[]{20000});
-
-            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
-                    AggregatedPowerStatsConfig.POWER_STATE_OTHER,
-                    AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                    BatteryConsumer.PROCESS_STATE_FOREGROUND}))
-                    .isTrue();
-            assertThat(values).isEqualTo(new long[]{1234});
-
-            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
-                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
-                    BatteryConsumer.PROCESS_STATE_FOREGROUND}))
-                    .isTrue();
-            assertThat(values).isEqualTo(new long[]{1111});
-
-            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
-                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
-                    BatteryConsumer.PROCESS_STATE_BACKGROUND}))
-                    .isTrue();
-            assertThat(values).isEqualTo(new long[]{3333});
-        });
-    }
-
-    @NonNull
-    private static String formatDateTime(long timeInMillis) {
-        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
-        format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
-        return format.format(new Date(timeInMillis));
-    }
-
-    @Test
-    public void incompatiblePowerStats() {
-        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
-                "majorDrain", 1, null, 0, 1, new PersistableBundle());
-        PowerStats powerStats = new PowerStats(descriptor);
-
-        mHistory.forceRecordAllHistory();
-        powerStats.stats = new long[]{0};
-        powerStats.uidStats.put(TEST_UID, new long[]{0});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
-        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND);
-
-        advance(1000);
-
-        powerStats.stats = new long[]{10000};
-        powerStats.uidStats.put(TEST_UID, new long[]{1234});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-
-        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
-
-        advance(1000);
-
-        descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, null, 0, 1,
-                PersistableBundle.forPair("something", "changed"));
-        powerStats = new PowerStats(descriptor);
-        powerStats.stats = new long[]{20000};
-        powerStats.uidStats.put(TEST_UID, new long[]{4444});
-        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
-
-        advance(1000);
-
-        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 50, /* plugged */ true);
-
-        mAggregator.aggregatePowerStats(0, MonotonicClock.UNDEFINED, stats -> {
-            long[] values = new long[1];
-
-            PowerComponentAggregatedPowerStats powerComponentStats =
-                    stats.getPowerComponentStats(TEST_POWER_COMPONENT);
-
-            if (mAggregatedStatsCount == 0) {
-                assertThat(stats.getStartTime()).isEqualTo(START_TIME);
-                assertThat(stats.getDuration()).isEqualTo(2000);
-
-                assertThat(powerComponentStats.getDeviceStats(values, new int[]{
-                        AggregatedPowerStatsConfig.POWER_STATE_OTHER,
-                        AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
-                        .isTrue();
-                assertThat(values).isEqualTo(new long[]{10000});
-                assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
-                        AggregatedPowerStatsConfig.POWER_STATE_OTHER,
-                        AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                        BatteryConsumer.PROCESS_STATE_FOREGROUND}))
-                        .isTrue();
-                assertThat(values).isEqualTo(new long[]{1234});
-            } else if (mAggregatedStatsCount == 1) {
-                assertThat(stats.getStartTime()).isEqualTo(START_TIME + 2000);
-                assertThat(stats.getDuration()).isEqualTo(1000);
-
-                assertThat(powerComponentStats.getDeviceStats(values, new int[]{
-                        AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                        AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
-                        .isTrue();
-                assertThat(values).isEqualTo(new long[]{20000});
-                assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
-                        AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
-                        AggregatedPowerStatsConfig.SCREEN_STATE_ON,
-                        BatteryConsumer.PROCESS_STATE_FOREGROUND}))
-                        .isTrue();
-                assertThat(values).isEqualTo(new long[]{4444});
-            } else {
-                fail();
-            }
-            mAggregatedStatsCount++;
-        });
-    }
-
-    private void advance(long durationMs) {
-        mClock.realtime += durationMs;
-        mClock.uptime += durationMs;
-        mClock.currentTime += durationMs;
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
index 89d6c1c..a04f721 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsCollectorTest.java
@@ -114,13 +114,15 @@
         mockEnergyConsumers(powerStatsInternal);
 
         PowerStatsCollector.ConsumedEnergyRetrieverImpl retriever =
-                new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal);
+                new PowerStatsCollector.ConsumedEnergyRetrieverImpl(powerStatsInternal, ()-> 3500);
         int[] energyConsumerIds = retriever.getEnergyConsumerIds(EnergyConsumerType.CPU_CLUSTER);
         assertThat(energyConsumerIds).isEqualTo(new int[]{1, 2});
-        long[] energy = retriever.getConsumedEnergyUws(energyConsumerIds);
-        assertThat(energy).isEqualTo(new long[]{1000, 2000});
-        energy = retriever.getConsumedEnergyUws(energyConsumerIds);
-        assertThat(energy).isEqualTo(new long[]{1500, 2700});
+        EnergyConsumerResult[] energy = retriever.getConsumedEnergy(energyConsumerIds);
+        assertThat(energy[0].energyUWs).isEqualTo(1000);
+        assertThat(energy[1].energyUWs).isEqualTo(2000);
+        energy = retriever.getConsumedEnergy(energyConsumerIds);
+        assertThat(energy[0].energyUWs).isEqualTo(1500);
+        assertThat(energy[1].energyUWs).isEqualTo(2700);
     }
 
     @SuppressWarnings("unchecked")
@@ -176,4 +178,11 @@
                 .thenReturn(future1)
                 .thenReturn(future2);
     }
+
+    private EnergyConsumerResult mockEnergyConsumerResult(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
 }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
deleted file mode 100644
index 7f7967b..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsExporterTest.java
+++ /dev/null
@@ -1,377 +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.server.power.stats;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.Mockito.mock;
-
-import android.os.AggregateBatteryConsumer;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.BatteryUsageStats;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.PersistableBundle;
-import android.os.UidBatteryConsumer;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-public class PowerStatsExporterTest {
-
-    private static final int APP_UID1 = 42;
-    private static final int APP_UID2 = 84;
-    private static final double TOLERANCE = 0.01;
-
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
-            .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
-            .setAveragePowerForCpuScalingPolicy(0, 360)
-            .setAveragePowerForCpuScalingStep(0, 0, 300)
-            .setCpuPowerBracketCount(1)
-            .setCpuPowerBracket(0, 0, 0);
-
-    private MockClock mClock = new MockClock();
-    private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
-    private PowerStatsStore mPowerStatsStore;
-    private PowerStatsAggregator mPowerStatsAggregator;
-    private BatteryStatsHistory mHistory;
-    private CpuPowerStatsLayout mCpuStatsArrayLayout;
-    private PowerStats.Descriptor mPowerStatsDescriptor;
-    private final EnergyConsumerPowerStatsLayout mEnergyConsumerPowerStatsLayout =
-            new EnergyConsumerPowerStatsLayout();
-
-    @Before
-    public void setup() throws IOException {
-        File storeDirectory = Files.createTempDirectory("PowerStatsExporterTest").toFile();
-        clearDirectory(storeDirectory);
-
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(
-                        new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
-                                mStatsRule.getCpuScalingPolicies()));
-        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
-
-        mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler(), config);
-        mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
-                mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
-                mMonotonicClock, null, null);
-        mPowerStatsAggregator = new PowerStatsAggregator(config, mHistory);
-
-        mCpuStatsArrayLayout = new CpuPowerStatsLayout();
-        mCpuStatsArrayLayout.addDeviceSectionCpuTimeByScalingStep(1);
-        mCpuStatsArrayLayout.addDeviceSectionCpuTimeByCluster(1);
-        mCpuStatsArrayLayout.addDeviceSectionUsageDuration();
-        mCpuStatsArrayLayout.addDeviceSectionPowerEstimate();
-        mCpuStatsArrayLayout.addUidSectionCpuTimeByPowerBracket(new int[]{0});
-        mCpuStatsArrayLayout.addUidSectionPowerEstimate();
-        PersistableBundle extras = new PersistableBundle();
-        mCpuStatsArrayLayout.toExtras(extras);
-
-        mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
-                mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
-                null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
-    }
-
-    @Test
-    public void breakdownByProcState_fullRange() throws Exception {
-        breakdownByProcState_fullRange(false, false);
-    }
-
-    @Test
-    public void breakdownByProcStateScreenAndPower_fullRange() throws Exception {
-        breakdownByProcState_fullRange(true, true);
-    }
-
-    private void breakdownByProcState_fullRange(boolean includeScreenStateData,
-            boolean includePowerStateData) throws Exception {
-        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
-                new String[]{"cu570m"}, /* includePowerModels */ false,
-                /* includeProcessStateData */ true, includeScreenStateData,
-                includePowerStateData, /* powerThreshold */ 0);
-        exportAggregatedPowerStats(builder, 1000, 10000);
-
-        BatteryUsageStats actual = builder.build();
-        String message = "Actual BatteryUsageStats: " + actual;
-
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
-                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
-                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 3.60);
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 0.360);
-
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082);
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916);
-        assertUidPowerEstimate(message, actual, APP_UID1,
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
-                BatteryConsumer.PROCESS_STATE_ANY, 0.360);
-
-        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
-        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999);
-        assertUidPowerEstimate(message, actual, APP_UID2,
-                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
-                BatteryConsumer.PROCESS_STATE_ANY, 0);
-
-        actual.close();
-    }
-
-    @Test
-    public void breakdownByProcState_subRange() throws Exception {
-        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
-                new String[]{"cu570m"}, /* includePowerModels */ false,
-                /* includeProcessStateData */ true, true, true, /* powerThreshold */ 0);
-        exportAggregatedPowerStats(builder, 3700, 6700);
-
-        BatteryUsageStats actual = builder.build();
-        String message = "Actual BatteryUsageStats: " + actual;
-
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
-                BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
-                BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
-
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 1.193332);
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND, 0.397749);
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND, 0.795583);
-
-        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 3.333249);
-        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.333249);
-
-        actual.close();
-    }
-
-    @Test
-    public void combinedProcessStates() throws Exception {
-        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
-                new String[]{"cu570m"}, /* includePowerModels */ false,
-                /* includeProcessStateData */ false, true, true, /* powerThreshold */ 0);
-        exportAggregatedPowerStats(builder, 1000, 10000);
-
-        BatteryUsageStats actual = builder.build();
-        String message = "Actual BatteryUsageStats: " + actual;
-
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
-                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
-        assertAggregatedPowerEstimate(message, actual,
-                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
-                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
-
-        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
-        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
-                BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
-        UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
-                .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
-        // There shouldn't be any per-procstate data
-        for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
-            if (procState != BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
-                assertThat(uidScope.getConsumedPower(new BatteryConsumer.Dimensions(
-                        BatteryConsumer.POWER_COMPONENT_CPU,
-                        BatteryConsumer.PROCESS_STATE_FOREGROUND))).isEqualTo(0);
-            }
-        }
-        actual.close();
-    }
-
-    private void recordBatteryHistory() {
-        PowerStats powerStats = new PowerStats(mPowerStatsDescriptor);
-        long[] uidStats1 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
-        powerStats.uidStats.put(APP_UID1, uidStats1);
-        long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
-        powerStats.uidStats.put(APP_UID2, uidStats2);
-
-        PowerStats customPowerStats = new PowerStats(
-                new PowerStats.Descriptor(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
-                        "cu570m", mEnergyConsumerPowerStatsLayout.getDeviceStatsArrayLength(),
-                        null, 0, mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength(),
-                        new PersistableBundle()));
-        long[] customUidStats = new long[mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength()];
-        customPowerStats.uidStats.put(APP_UID1, customUidStats);
-
-        mHistory.forceRecordAllHistory();
-
-        mHistory.startRecordingHistory(1000, 1000, false);
-        mHistory.recordPowerStats(1000, 1000, powerStats);
-        mHistory.recordPowerStats(1000, 1000, customPowerStats);
-        mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false);
-        mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
-        mHistory.recordProcessStateChange(1000, 1000, APP_UID1,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND);
-        mHistory.recordProcessStateChange(1000, 1000, APP_UID2,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
-
-        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 11111);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 10000);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 1111);
-        mHistory.recordPowerStats(1000, 1000, powerStats);
-
-        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 12345);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 9876);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469);
-        mHistory.recordPowerStats(3000, 3000, powerStats);
-
-        mPowerStatsAggregator.aggregatePowerStats(0, 3500, stats -> {
-            mPowerStatsStore.storeAggregatedPowerStats(stats);
-        });
-
-        mHistory.recordProcessStateChange(4000, 4000, APP_UID1,
-                BatteryConsumer.PROCESS_STATE_BACKGROUND);
-
-        mHistory.recordStateStopEvent(4000, 4000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
-
-        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 54321);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 14321);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000);
-        mHistory.recordPowerStats(6000, 6000, powerStats);
-
-        mEnergyConsumerPowerStatsLayout.setConsumedEnergy(customPowerStats.stats, 0, 3_600_000);
-        mEnergyConsumerPowerStatsLayout.setUidConsumedEnergy(customUidStats, 0, 360_000);
-        mHistory.recordPowerStats(6010, 6010, customPowerStats);
-
-        mPowerStatsAggregator.aggregatePowerStats(3500, 6500, stats -> {
-            mPowerStatsStore.storeAggregatedPowerStats(stats);
-        });
-
-        mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
-        mHistory.recordProcessStateChange(7000, 7000, APP_UID1,
-                BatteryConsumer.PROCESS_STATE_FOREGROUND);
-        mHistory.recordProcessStateChange(7000, 7000, APP_UID2,
-                BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
-
-        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 23456);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 23456);
-        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 0);
-        mHistory.recordPowerStats(8000, 8000, powerStats);
-
-        assertThat(mPowerStatsStore.getTableOfContents()).hasSize(2);
-    }
-
-    private void exportAggregatedPowerStats(BatteryUsageStats.Builder builder,
-            int monotonicStartTime, int monotonicEndTime) {
-        recordBatteryHistory();
-        PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
-                mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
-        exporter.exportAggregatedPowerStats(builder, monotonicStartTime, monotonicEndTime);
-    }
-
-    private void assertAggregatedPowerEstimate(String message, BatteryUsageStats bus, int scope,
-            int componentId, double expected) {
-        AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(scope);
-        double actual = componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                ? consumer.getConsumedPower(componentId)
-                : consumer.getConsumedPowerForCustomComponent(componentId);
-        assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
-    }
-
-    private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
-            int componentId, int processState, double expected) {
-        List<UidBatteryConsumer> uidScopes = bus.getUidBatteryConsumers();
-        final UidBatteryConsumer uidScope = uidScopes.stream()
-                .filter(us -> us.getUid() == uid).findFirst().orElse(null);
-        assertWithMessage(message).that(uidScope).isNotNull();
-        double actual = componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
-                ? uidScope.getConsumedPower(
-                        new BatteryConsumer.Dimensions(componentId, processState))
-                : uidScope.getConsumedPowerForCustomComponent(componentId);
-        assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
-    }
-
-    private void clearDirectory(File dir) {
-        if (dir.exists()) {
-            for (File child : dir.listFiles()) {
-                if (child.isDirectory()) {
-                    clearDirectory(child);
-                }
-                child.delete();
-            }
-        }
-    }
-
-    private static class TestHandler extends Handler {
-        TestHandler() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
-            msg.getCallback().run();
-            return true;
-        }
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
deleted file mode 100644
index 02e446a..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsProcessorTest.java
+++ /dev/null
@@ -1,137 +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.server.power.stats;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.BatteryConsumer;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PowerStatsProcessorTest {
-
-    @Test
-    public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
-
-        PowerStatsProcessor.PowerEstimationPlan plan =
-                new PowerStatsProcessor.PowerEstimationPlan(config);
-        assertThat(deviceStateEstimatesToStrings(plan))
-                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
-        assertThat(combinedDeviceStatsToStrings(plan))
-                .containsExactly("[[0, 0]]", "[[0, 1]]", "[[1, 0]]", "[[1, 1]]");
-        assertThat(uidStateEstimatesToStrings(plan, config))
-                .containsExactly(
-                        "[[0, 0]]: [ps]: [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4]]",
-                        "[[0, 1]]: [ps]: [[0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4]]",
-                        "[[1, 0]]: [ps]: [[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 0, 3], [1, 0, 4]]",
-                        "[[1, 1]]: [ps]: [[1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]");
-    }
-
-    @Test
-    public void createPowerEstimationPlan_combineDeviceStats() {
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
-
-        PowerStatsProcessor.PowerEstimationPlan plan =
-                new PowerStatsProcessor.PowerEstimationPlan(config);
-
-        assertThat(deviceStateEstimatesToStrings(plan))
-                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
-        assertThat(combinedDeviceStatsToStrings(plan))
-                .containsExactly(
-                        "[[0, 0], [0, 1]]",
-                        "[[1, 0], [1, 1]]");
-        assertThat(uidStateEstimatesToStrings(plan, config))
-                .containsExactly(
-                        "[[0, 0], [0, 1]]: [ps]: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]]",
-                        "[[1, 0], [1, 1]]: [ps]: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]");
-    }
-
-    private static List<String> deviceStateEstimatesToStrings(
-            PowerStatsProcessor.PowerEstimationPlan plan) {
-        return plan.deviceStateEstimations.stream()
-                .map(dse -> dse.stateValues).map(Arrays::toString).toList();
-    }
-
-    private static List<String> combinedDeviceStatsToStrings(
-            PowerStatsProcessor.PowerEstimationPlan plan) {
-        return plan.combinedDeviceStateEstimations.stream()
-                .map(cds -> cds.deviceStateEstimations)
-                .map(dses -> dses.stream()
-                        .map(dse -> dse.stateValues).map(Arrays::toString).toList())
-                .map(Object::toString)
-                .toList();
-    }
-
-    private static List<String> uidStateEstimatesToStrings(
-            PowerStatsProcessor.PowerEstimationPlan plan,
-            AggregatedPowerStatsConfig.PowerComponent config) {
-        MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
-        return plan.uidStateEstimates.stream()
-                .map(use ->
-                        use.combinedDeviceStateEstimate.deviceStateEstimations.stream()
-                                .map(dse -> dse.stateValues).map(Arrays::toString).toList()
-                        + ": "
-                        + Arrays.stream(use.states)
-                                .filter(Objects::nonNull)
-                                .map(MultiStateStats.States::getName).toList()
-                        + ": "
-                        + use.proportionalEstimates.stream()
-                                .map(pe -> trackedStatesToString(uidStateConfig, pe.stateValues))
-                                .toList())
-                .toList();
-    }
-
-    private static Object trackedStatesToString(MultiStateStats.States[] states,
-            int[] stateValues) {
-        StringBuilder sb = new StringBuilder();
-        sb.append("[");
-        boolean first = true;
-        for (int i = 0; i < states.length; i++) {
-            if (!states[i].isTracked()) {
-                continue;
-            }
-
-            if (!first) {
-                sb.append(", ");
-            }
-            first = false;
-            sb.append(stateValues[i]);
-        }
-        sb.append("]");
-        return sb.toString();
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
index beec661..143d046 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsSchedulerTest.java
@@ -18,36 +18,18 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.platform.test.ravenwood.RavenwoodRule;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.os.MonotonicClock;
-
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.IOException;
-import java.nio.file.Files;
 import java.time.Duration;
 import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
 
 @RunWith(AndroidJUnit4.class)
 public class PowerStatsSchedulerTest {
@@ -56,134 +38,10 @@
             .setProvideMainThread(true)
             .build();
 
-    private PowerStatsStore mPowerStatsStore;
-    private Handler mHandler;
-    private MockClock mClock = new MockClock();
-    private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
-    private PowerStatsScheduler mPowerStatsScheduler;
-    private PowerStatsAggregator mPowerStatsAggregator;
-    private AggregatedPowerStatsConfig mAggregatedPowerStatsConfig;
-    private List<Long> mScheduledAlarms = new ArrayList<>();
-    private boolean mPowerStatsCollectionOccurred;
-
-    private static final int START_REALTIME = 7654321;
-
-    @Before
-    @SuppressWarnings("GuardedBy")
-    public void setup() throws IOException {
-        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
-
-        mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
-        mClock.realtime = START_REALTIME;
-
-        HandlerThread bgThread = new HandlerThread("bg thread");
-        bgThread.start();
-        mHandler = new Handler(bgThread.getLooper());
-        mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
-        mPowerStatsStore = new PowerStatsStore(
-                Files.createTempDirectory("PowerStatsSchedulerTest").toFile(),
-                mHandler, mAggregatedPowerStatsConfig);
-        mPowerStatsAggregator = mock(PowerStatsAggregator.class);
-        mPowerStatsScheduler = new PowerStatsScheduler(
-                () -> mPowerStatsCollectionOccurred = true,
-                mPowerStatsAggregator, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
-                mPowerStatsStore,
-                ((triggerAtMillis, tag, onAlarmListener, handler) ->
-                        mScheduledAlarms.add(triggerAtMillis)),
-                mClock, mMonotonicClock, () -> 12345L, mHandler);
-    }
-
-    @Test
-    @SuppressWarnings("unchecked")
-    public void storeAggregatePowerStats() {
-        mPowerStatsStore.reset();
-
-        assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
-
-        mPowerStatsStore.storeAggregatedPowerStats(
-                createAggregatedPowerStats(mMonotonicClock.monotonicTime(), mClock.currentTime,
-                        123));
-
-        long delayBeforeAggregating = TimeUnit.MINUTES.toMillis(90);
-        mClock.realtime += delayBeforeAggregating;
-        mClock.currentTime += delayBeforeAggregating;
-
-        doAnswer(invocation -> {
-            // The first span is longer than 30 min, because the end time is being aligned with
-            // the wall clock.  Subsequent spans should be precisely 30 minutes.
-            long startTime = invocation.getArgument(0);
-            long endTime = invocation.getArgument(1);
-            Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
-
-            long startTimeWallClock =
-                    mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
-            long endTimeWallClock =
-                    mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
-
-            assertThat(startTime).isEqualTo(START_REALTIME + 123);
-            assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
-            assertThat(Instant.ofEpochMilli(endTimeWallClock))
-                    .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
-
-            consumer.accept(
-                    createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
-            return null;
-        }).doAnswer(invocation -> {
-            long startTime = invocation.getArgument(0);
-            long endTime = invocation.getArgument(1);
-            Consumer<AggregatedPowerStats> consumer = invocation.getArgument(2);
-
-            long startTimeWallClock =
-                    mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
-            long endTimeWallClock =
-                    mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
-
-            assertThat(Instant.ofEpochMilli(startTimeWallClock))
-                    .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
-            assertThat(Instant.ofEpochMilli(endTimeWallClock))
-                    .isEqualTo(Instant.parse("2023-01-02T04:30:00Z"));
-
-            consumer.accept(
-                    createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
-            return null;
-        }).when(mPowerStatsAggregator).aggregatePowerStats(anyLong(), anyLong(),
-                any(Consumer.class));
-
-        mPowerStatsScheduler.start(/*enabled*/ true);
-        ConditionVariable done = new ConditionVariable();
-        mHandler.post(done::open);
-        done.block();
-
-        assertThat(mPowerStatsCollectionOccurred).isTrue();
-        assertThat(mScheduledAlarms).containsExactly(
-                START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
-
-        verify(mPowerStatsAggregator, times(2))
-                .aggregatePowerStats(anyLong(), anyLong(), any(Consumer.class));
-
-        List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
-        assertThat(contents).hasSize(3);
-        // Skip the first entry, which was placed in the store at the beginning of this test
-        PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
-        PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
-        assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
-        assertThat(timeFrame2.startMonotonicTime)
-                .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
-        assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
-                .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
-        assertThat(Duration.ofMillis(timeFrame2.duration)).isEqualTo(Duration.ofMinutes(30));
-    }
-
-    private AggregatedPowerStats createAggregatedPowerStats(long monotonicTime, long currentTime,
-            long duration) {
-        AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
-        stats.addClockUpdate(monotonicTime, currentTime);
-        stats.setDuration(duration);
-        return stats;
-    }
-
     @Test
     public void alignToWallClock() {
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
         // Expect the aligned value to be adjusted by 1 min 30 sec - rounded to the next 15 min
         assertThat(PowerStatsScheduler.alignToWallClock(123, TimeUnit.MINUTES.toMillis(15),
                 123 + TimeUnit.HOURS.toMillis(2),
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
index 36d7af5..dc8d920 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PowerStatsStoreTest.java
@@ -59,14 +59,7 @@
         clearDirectory(mStoreDirectory);
 
         mPowerStatsStore = new PowerStatsStore(mStoreDirectory,
-                MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES,
-                new TestHandler(),
-                (sectionType, parser) -> {
-                    if (sectionType.equals(TestSection.TYPE)) {
-                        return TestSection.readXml(parser);
-                    }
-                    return null;
-                });
+                MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES, new TestHandler());
     }
 
     @Test
@@ -144,7 +137,7 @@
         }
 
         @Override
-        void write(TypedXmlSerializer serializer) throws IOException {
+        public void write(TypedXmlSerializer serializer) throws IOException {
             StringBuilder sb = new StringBuilder();
             for (int i = 0; i < mSize; i++) {
                 sb.append("X");
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
index 817fdcb..8c09d1d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsCollectorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.when;
 
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.os.BatteryConsumer;
 import android.os.BatteryStats;
@@ -32,6 +33,7 @@
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
 import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -39,8 +41,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.function.IntSupplier;
-
 public class ScreenPowerStatsCollectorTest {
     private static final int APP_UID1 = 42;
     private static final int APP_UID2 = 24;
@@ -89,11 +89,6 @@
         }
 
         @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> 3500;
-        }
-
-        @Override
         public int getDisplayCount() {
             return 2;
         }
@@ -115,6 +110,7 @@
                 return uid;
             }
         });
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
     }
 
     @Test
@@ -125,8 +121,8 @@
         // Establish a baseline
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
                 .thenReturn(new int[]{77});
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
-                .thenReturn(new long[]{10_000});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
 
         doAnswer(inv -> {
             ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
@@ -139,8 +135,8 @@
 
         collector.collectStats();
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
-                .thenReturn(new long[]{45_000});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(45_000)});
         when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
                 .thenReturn(60_000L);
         when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
@@ -171,8 +167,7 @@
 
         PowerStats powerStats = collector.collectStats();
 
-        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout();
-        layout.fromExtras(powerStats.descriptor.extras);
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(powerStats.descriptor);
 
         // (45000 - 10000) / 3500
         assertThat(layout.getConsumedEnergy(powerStats.stats, 0))
@@ -204,4 +199,10 @@
         assertThat(layout.getUidTopActivityDuration(powerStats.uidStats.get(APP_UID2)))
                 .isEqualTo(10000);
     }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
 }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
deleted file mode 100644
index 9fde61a..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/ScreenPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,287 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_ANY;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.Handler;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerProfile;
-import com.android.internal.os.PowerStats;
-import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.function.IntSupplier;
-
-public class ScreenPowerStatsProcessorTest {
-
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setNumDisplays(2)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 0, 480.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 720.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 4800.0)
-            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 7200.0)
-            .initMeasuredEnergyStatsLocked();
-
-    private static final double PRECISION = 0.1;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    @Mock
-    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
-
-    private final Injector mInjector = new Injector() {
-        @Override
-        public Handler getHandler() {
-            return mStatsRule.getHandler();
-        }
-
-        @Override
-        public Clock getClock() {
-            return mStatsRule.getMockClock();
-        }
-
-        @Override
-        public PowerStatsUidResolver getUidResolver() {
-            return new PowerStatsUidResolver();
-        }
-
-        @Override
-        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-            return 0;
-        }
-
-        @Override
-        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-            return mConsumedEnergyRetriever;
-        }
-
-        @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> VOLTAGE_MV;
-        }
-
-        @Override
-        public int getDisplayCount() {
-            return 2;
-        }
-
-        @Override
-        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
-            return mScreenUsageTimeRetriever;
-        }
-    };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void processPowerStats_powerProfile() {
-        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(false);
-
-        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 195.5, 0);
-        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 0.6);
-        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 97.8, 0);
-        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
-
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 78.2);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 39.1);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
-
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 117.3);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 58.7);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
-    }
-
-    @Test
-    public void processPowerStats_energyConsumer() {
-        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(true);
-
-        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 261.9, 0);
-        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 7.2);
-        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 130.9, 0);
-        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
-
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 104.8);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 52.4);
-        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
-
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 157.1);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 78.6);
-        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
-    }
-
-    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats(
-            boolean energyConsumer) {
-        ScreenPowerStatsProcessor processor =
-                new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
-        collector.setEnabled(true);
-
-        if (energyConsumer) {
-            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
-                    .thenReturn(new int[]{77});
-            when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
-                    .thenReturn(new long[]{10_000});
-        } else {
-            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
-                    .thenReturn(new int[0]);
-        }
-
-        doAnswer(inv -> {
-            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
-                    inv.getArgument(0);
-            callback.onUidTopActivityTime(APP_UID1, 1000);
-            callback.onUidTopActivityTime(APP_UID2, 2000);
-            return null;
-        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
-                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 1000);
-
-        if (energyConsumer) {
-            // 400 mAh represented as microWattSeconds
-            long energyUws = 400L * 3600 * VOLTAGE_MV;
-            when(mConsumedEnergyRetriever.getConsumedEnergyUws(new int[]{77}))
-                    .thenReturn(new long[]{10_000 + energyUws});
-        }
-
-        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
-                .thenReturn(60_000L);
-        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
-                BatteryStats.SCREEN_BRIGHTNESS_DARK))
-                .thenReturn(10_000L);
-        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
-                BatteryStats.SCREEN_BRIGHTNESS_MEDIUM))
-                .thenReturn(20_000L);
-        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
-                BatteryStats.SCREEN_BRIGHTNESS_BRIGHT))
-                .thenReturn(30_000L);
-        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
-                .thenReturn(120_000L);
-        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
-                .thenReturn(180_000L);
-        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
-                .thenReturn(240_000L);
-        doAnswer(inv -> {
-            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
-                    inv.getArgument(0);
-            callback.onUidTopActivityTime(APP_UID1, 3000);
-            callback.onUidTopActivityTime(APP_UID2, 5000);
-            return null;
-        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
-                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_BATTERY, 201_000);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 601_000);
-
-        // Slightly larger than 600_000 total screen time, to simulate a sight race
-        // between state changes and power stats collection
-        aggregatedStats.addPowerStats(collector.collectStats(), 612_000);
-
-        aggregatedStats.getConfig().getProcessor().finish(aggregatedStats, 180_000);
-        return aggregatedStats;
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            ScreenPowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(
-                        BatteryConsumer.POWER_COMPONENT_SCREEN)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN)
-                        .setProcessor(processor);
-
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-
-        return aggregatedStats;
-    }
-
-    private void assertDevicePowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats,
-            int powerState, int screenState, double expectedScreenPowerEstimate,
-            double expectedDozePowerEstimate) {
-        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
-        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
-        long[] stats = new long[descriptor.statsArrayLength];
-        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
-        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
-                .of(expectedScreenPowerEstimate);
-        assertThat(layout.getScreenDozePowerEstimate(stats)).isWithin(PRECISION)
-                .of(expectedDozePowerEstimate);
-    }
-
-    private void assertUidPowerEstimate(PowerComponentAggregatedPowerStats aggregatedStats, int uid,
-            int powerState, int screenState, double expectedScreenPowerEstimate) {
-        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
-        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
-        long[] stats = new long[descriptor.uidStatsArrayLength];
-        aggregatedStats.getUidStats(stats, uid,
-                new int[]{powerState, screenState, PROCESS_STATE_ANY});
-        assertThat(layout.getUidPowerEstimate(stats)).isWithin(PRECISION)
-                .of(expectedScreenPowerEstimate);
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
deleted file mode 100644
index 7000487..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SensorPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,241 +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.server.power.stats;
-
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-import android.hardware.input.InputSensorInfo;
-import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import com.android.internal.os.MonotonicClock;
-import com.android.internal.os.PowerStats;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-public class SensorPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .initMeasuredEnergyStatsLocked();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int SENSOR_HANDLE_1 = 77;
-    private static final int SENSOR_HANDLE_2 = 88;
-    private static final int SENSOR_HANDLE_3 = 99;
-
-    @Mock
-    private SensorManager mSensorManager;
-
-    private MonotonicClock mMonotonicClock;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
-        Sensor sensor1 = createSensor(SENSOR_HANDLE_1, Sensor.TYPE_STEP_COUNTER,
-                Sensor.STRING_TYPE_STEP_COUNTER, "dancing", 100);
-        Sensor sensor2 = createSensor(SENSOR_HANDLE_2, Sensor.TYPE_MOTION_DETECT,
-                "com.example", "tango", 200);
-        Sensor sensor3 = createSensor(SENSOR_HANDLE_3, Sensor.TYPE_MOTION_DETECT,
-                "com.example", "waltz", 300);
-        when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(
-                List.of(sensor1, sensor2, sensor3));
-    }
-
-    @Test
-    public void testPowerEstimation() {
-        SensorPowerStatsProcessor processor = new SensorPowerStatsProcessor(() -> mSensorManager);
-
-        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(processor);
-
-        processor.noteStateChange(stats, buildHistoryItem(0, true, APP_UID1, SENSOR_HANDLE_1));
-
-        // Turn the screen off after 2.5 seconds
-        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
-
-        processor.noteStateChange(stats, buildHistoryItem(6000, false, APP_UID1, SENSOR_HANDLE_1));
-        processor.noteStateChange(stats, buildHistoryItem(7000, true, APP_UID2, SENSOR_HANDLE_1));
-        processor.noteStateChange(stats, buildHistoryItem(8000, true, APP_UID2, SENSOR_HANDLE_2));
-        processor.noteStateChange(stats, buildHistoryItem(9000, false, APP_UID2, SENSOR_HANDLE_1));
-
-        processor.finish(stats, 10000);
-
-        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
-        SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout();
-        statsLayout.fromExtras(descriptor.extras);
-
-        String dump = stats.toString();
-        assertThat(dump).contains(" step_counter: ");
-        assertThat(dump).contains(" com.example.tango: ");
-
-        long[] uidStats = new long[descriptor.uidStatsArrayLength];
-
-        // For UID1:
-        // SENSOR1 was on for 6000 ms.
-        //   Estimated power: 6000 * 100 = 0.167 mAh
-        //     split between three different states
-        //          fg screen-on: 6000 * 2500/10000
-        //          bg screen-off: 6000 * 2500/10000
-        //          fgs screen-off: 6000 * 5000/10000
-        double expectedPower1 = 0.166666;
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
-        stats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 5000 / 10000);
-
-        // For UID2:
-        // SENSOR1 was on for 2000 ms.
-        //   Estimated power: 2000 * 100 = 0.0556 mAh
-        //     split between three different states
-        //          cached screen-on: 2000 * 2500/10000
-        //          cached screen-off: 2000 * 7500/10000
-        // SENSOR2 was on for 2000 ms.
-        //   Estimated power: 2000 * 200 = 0.11111 mAh
-        //     split between three different states
-        //          cached screen-on: 2000 * 2500/10000
-        //          cached screen-off: 2000 * 7500/10000
-        double expectedPower2 = 0.05555 + 0.11111;
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 2500 / 10000);
-        stats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 7500 / 10000);
-
-        long[] deviceStats = new long[descriptor.statsArrayLength];
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 2500 / 10000);
-
-        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 7500 / 10000);
-    }
-
-    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
-            int uid, int sensor) {
-        mStatsRule.setTime(timestamp, timestamp);
-        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
-        historyItem.time = mMonotonicClock.monotonicTime();
-        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG : 0;
-        if (stateOn) {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
-        } else {
-            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
-                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
-        }
-        historyItem.eventTag = historyItem.localEventTag;
-        historyItem.eventTag.uid = uid;
-        historyItem.eventTag.string = "sensor:0x" + Integer.toHexString(sensor);
-        return historyItem;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            SensorPowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
-        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
-                .trackDeviceStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN)
-                .trackUidStates(
-                        AggregatedPowerStatsConfig.STATE_POWER,
-                        AggregatedPowerStatsConfig.STATE_SCREEN,
-                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
-                .setProcessor(processor);
-
-        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
-        PowerComponentAggregatedPowerStats powerComponentStats =
-                aggregatedPowerStats.getPowerComponentStats(
-                        BatteryConsumer.POWER_COMPONENT_SENSORS);
-        processor.start(powerComponentStats, 0);
-
-        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return powerComponentStats;
-    }
-
-    private Sensor createSensor(int handle, int type, String stringType, String name, float power) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            Sensor sensor = mock(Sensor.class);
-            when(sensor.getHandle()).thenReturn(handle);
-            when(sensor.getType()).thenReturn(type);
-            when(sensor.getStringType()).thenReturn(stringType);
-            when(sensor.getName()).thenReturn(name);
-            when(sensor.getPower()).thenReturn(power);
-            return sensor;
-        } else {
-            return new Sensor(new InputSensorInfo(name, "vendor", 0 /* version */,
-                    handle, type, 100.0f /*maxRange */, 0.02f /* resolution */,
-                    (float) power, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
-                    0 /* fifoMaxEventCount */, stringType /* stringType */,
-                    "" /* requiredPermission */, 0 /* maxDelay */, 0 /* flags */, 0 /* id */));
-        }
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index b13fc53..8b5e6ee 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -32,6 +32,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
 import android.net.NetworkStats;
 import android.net.wifi.WifiManager;
@@ -47,6 +48,7 @@
 
 import com.android.internal.os.Clock;
 import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -57,7 +59,6 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.IntSupplier;
 import java.util.function.Supplier;
 
 public class WifiPowerStatsCollectorTest {
@@ -154,11 +155,6 @@
         }
 
         @Override
-        public IntSupplier getVoltageSupplier() {
-            return () -> 3500;
-        }
-
-        @Override
         public Supplier<NetworkStats> getWifiNetworkStatsSupplier() {
             return mNetworkStatsSupplier;
         }
@@ -368,6 +364,7 @@
         WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
         collector.setEnabled(true);
 
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(3500);
         when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
                 .thenReturn(new int[]{777});
 
@@ -385,8 +382,8 @@
         mockWifiScanTimes(APP_UID2, 3000, 4000);
         mockWifiScanTimes(ISOLATED_UID, 5000, 6000);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{10000});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
 
         collector.collectStats();
 
@@ -404,13 +401,19 @@
         mockWifiScanTimes(APP_UID2, 3100, 4200);
         mockWifiScanTimes(ISOLATED_UID, 5300, 6400);
 
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(eq(new int[]{777})))
-                .thenReturn(new long[]{64321});
+        when(mConsumedEnergyRetriever.getConsumedEnergy(eq(new int[]{777})))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(64_321)});
 
         mStatsRule.setTime(20000, 20000);
         return collector.collectStats();
     }
 
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
     private void mockWifiActivityInfo(long timestamp, long rxTimeMs, long txTimeMs, int scanTimeMs,
             int idleTimeMs) {
         int stackState = 0;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
deleted file mode 100644
index 7ddaefd..0000000
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
+++ /dev/null
@@ -1,601 +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.server.power.stats;
-
-import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
-import static android.net.NetworkStats.METERED_NO;
-import static android.net.NetworkStats.ROAMING_NO;
-import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
-import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
-
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_POWER;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
-import static com.android.server.power.stats.AggregatedPowerStatsConfig.STATE_SCREEN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerType;
-import android.net.NetworkStats;
-import android.net.wifi.WifiManager;
-import android.os.BatteryConsumer;
-import android.os.Handler;
-import android.os.Process;
-import android.os.connectivity.WifiActivityEnergyInfo;
-import android.platform.test.ravenwood.RavenwoodRule;
-import android.util.SparseArray;
-
-import com.android.internal.os.Clock;
-import com.android.internal.os.PowerProfile;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-import java.util.function.IntSupplier;
-import java.util.function.Supplier;
-
-public class WifiPowerStatsProcessorTest {
-    @Rule(order = 0)
-    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
-            .setProvideMainThread(true)
-            .build();
-
-    private static final double PRECISION = 0.00001;
-    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
-    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
-    private static final int WIFI_ENERGY_CONSUMER_ID = 1;
-    private static final int VOLTAGE_MV = 3500;
-
-    @Rule(order = 1)
-    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
-            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 360.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
-            .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
-            .initMeasuredEnergyStatsLocked();
-
-    @Mock
-    private Context mContext;
-    @Mock
-    private PowerStatsUidResolver mPowerStatsUidResolver;
-    @Mock
-    private PackageManager mPackageManager;
-    @Mock
-    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
-    @Mock
-    private Supplier<NetworkStats> mNetworkStatsSupplier;
-    @Mock
-    private WifiManager mWifiManager;
-
-    private MockBatteryStatsImpl mBatteryStats;
-
-    private static class ScanTimes {
-        public long scanTimeMs;
-        public long batchScanTimeMs;
-    }
-
-    private final SparseArray<ScanTimes> mScanTimes = new SparseArray<>();
-    private long mWifiActiveDuration;
-
-    private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
-            new WifiPowerStatsCollector.WifiStatsRetriever() {
-        @Override
-        public void retrieveWifiScanTimes(Callback callback) {
-            for (int i = 0; i < mScanTimes.size(); i++) {
-                int uid = mScanTimes.keyAt(i);
-                ScanTimes scanTimes = mScanTimes.valueAt(i);
-                callback.onWifiScanTime(uid, scanTimes.scanTimeMs, scanTimes.batchScanTimeMs);
-            }
-        }
-
-        @Override
-        public long getWifiActiveDuration() {
-            return mWifiActiveDuration;
-        }
-    };
-
-    private final WifiPowerStatsCollector.Injector mInjector =
-            new WifiPowerStatsCollector.Injector() {
-                @Override
-                public Handler getHandler() {
-                    return mStatsRule.getHandler();
-                }
-
-                @Override
-                public Clock getClock() {
-                    return mStatsRule.getMockClock();
-                }
-
-                @Override
-                public PowerStatsUidResolver getUidResolver() {
-                    return mPowerStatsUidResolver;
-                }
-
-                @Override
-                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
-                    return 0;
-                }
-
-                @Override
-                public PackageManager getPackageManager() {
-                    return mPackageManager;
-                }
-
-                @Override
-                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
-                    return mConsumedEnergyRetriever;
-                }
-
-                @Override
-                public IntSupplier getVoltageSupplier() {
-                    return () -> VOLTAGE_MV;
-                }
-
-                @Override
-                public Supplier<NetworkStats> getWifiNetworkStatsSupplier() {
-                    return mNetworkStatsSupplier;
-                }
-
-                @Override
-                public WifiManager getWifiManager() {
-                    return mWifiManager;
-                }
-
-                @Override
-                public WifiPowerStatsCollector.WifiStatsRetriever getWifiStatsRetriever() {
-                    return mWifiStatsRetriever;
-                }
-            };
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
-        when(mPowerStatsUidResolver.mapUid(anyInt()))
-                .thenAnswer(invocation -> invocation.getArgument(0));
-
-        mBatteryStats = mStatsRule.getBatteryStats();
-    }
-
-    @Test
-    public void powerProfileModel_powerController() {
-        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
-
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
-                .thenReturn(new int[0]);
-
-        WifiPowerStatsProcessor processor =
-                new WifiPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Initial empty WifiActivityEnergyInfo.
-        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(0L,
-                WifiActivityEnergyInfo.STACK_STATE_INVALID, 0L, 0L, 0L, 0L));
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        mockWifiScanTimes(APP_UID1, 300, 400);
-        mockWifiScanTimes(APP_UID2, 100, 200);
-
-        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(10000,
-                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 2000, 3000, 100, 600));
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        WifiPowerStatsLayout statsLayout =
-                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // RX power = 'rx-duration * PowerProfile[wifi.controller.rx]`
-        //        RX power = 3000 * 480 = 1440000 mA-ms = 0.4 mAh
-        // TX power = 'tx-duration * PowerProfile[wifi.controller.tx]`
-        //        TX power = 2000 * 720 = 1440000 mA-ms = 0.4 mAh
-        // Scan power = 'scan-duration * PowerProfile[wifi.scan]`
-        //        Scan power = 100 * 480 = 48000 mA-ms = 0.013333 mAh
-        // Idle power = 'idle-duration * PowerProfile[wifi.idle]`
-        //        Idle power = 600 * 360 = 216000 mA-ms = 0.06 mAh
-        // Total power = RX + TX + Scan + Idle = 0.873333
-        // Screen-on  - 25%
-        // Screen-off - 75%
-        double expectedPower = 0.873333;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1 =
-        //     (1500 / 2000) * 0.4        // rx
-        //     + (300 / 400) * 0.4        // tx
-        //     + (700 / 1000) * 0.013333  // scan (basic + batched)
-        //   = 0.609333 mAh
-        double expectedPower1 = 0.609333;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2 =
-        //     (500 / 2000) * 0.4         // rx
-        //     + (100 / 400) * 0.4        // tx
-        //     + (300 / 1000) * 0.013333  // scan (basic + batched)
-        //   = 0.204 mAh
-        double expectedPower2 = 0.204;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    @Test
-    public void consumedEnergyModel_powerController() {
-        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
-
-        // PowerStats hardware is available
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
-                .thenReturn(new int[] {WIFI_ENERGY_CONSUMER_ID});
-
-        WifiPowerStatsProcessor processor =
-                new WifiPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Initial empty WifiActivityEnergyInfo.
-        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(0L,
-                WifiActivityEnergyInfo.STACK_STATE_INVALID, 0L, 0L, 0L, 0L));
-
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{WIFI_ENERGY_CONSUMER_ID}))
-                .thenReturn(new long[]{0});
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        mockWifiScanTimes(APP_UID1, 300, 400);
-        mockWifiScanTimes(APP_UID2, 100, 200);
-
-        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(10000,
-                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 2000, 3000, 100, 600));
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        // 10 mAh represented as microWattSeconds
-        long energyUws = 10 * 3600 * VOLTAGE_MV;
-        when(mConsumedEnergyRetriever.getConsumedEnergyUws(
-                new int[]{WIFI_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        WifiPowerStatsLayout statsLayout =
-                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // All estimates are computed as in the #powerProfileModel_powerController test,
-        // except they are all scaled by the same ratio to ensure that the total estimated
-        // energy is equal to the measured energy
-        double expectedPower = 10;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1
-        //   0.609333           // power profile model estimate
-        //   0.873333           // power profile model estimate for total power
-        //   10                 // total consumed energy
-        //   = 0.609333 * (10 / 0.873333) = 6.9771
-        double expectedPower1 = 6.9771;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2
-        //   0.204              // power profile model estimate
-        //   0.873333           // power profile model estimate for total power
-        //   10                 // total consumed energy
-        //   = 0.204 * (10 / 0.873333) = 2.33588
-        double expectedPower2 = 2.33588;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    @Test
-    public void powerProfileModel_noPowerController() {
-        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(false);
-
-        // No power monitoring hardware
-        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
-                .thenReturn(new int[0]);
-
-        WifiPowerStatsProcessor processor =
-                new WifiPowerStatsProcessor(mStatsRule.getPowerProfile());
-
-        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
-
-        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
-        collector.setEnabled(true);
-
-        // Establish a baseline
-        aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
-        // Turn the screen off after 2.5 seconds
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
-                5000);
-
-        // Note application network activity
-        NetworkStats networkStats = mockNetworkStats(10000, 1,
-                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
-                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
-                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
-        mScanTimes.clear();
-        mWifiActiveDuration = 8000;
-        mockWifiScanTimes(APP_UID1, 300, 400);
-        mockWifiScanTimes(APP_UID2, 100, 200);
-
-        mStatsRule.setTime(10_000, 10_000);
-
-        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
-
-        processor.finish(aggregatedStats, 10_000);
-
-        WifiPowerStatsLayout statsLayout =
-                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
-
-        // Total active power = 'active-duration * PowerProfile[wifi.on]`
-        //        active = 8000 * 360 = 2880000 mA-ms = 0.8 mAh
-        // UID1 rxPackets + txPackets = 1800
-        // UID2 rxPackets + txPackets = 600
-        // Total rx+tx packets = 2400
-        // Total scan power = `scan-duration * PowerProfile[wifi.scan]`
-        //        scan = (100 + 300) * 480 = 192000 mA-ms = 0.05333 mAh
-        // Total batch scan power = `(200 + 400) * PowerProfile[wifi.batchedscan]`
-        //        bscan = (200 + 400) * 720 = 432000 mA-ms = 0.12 mAh
-        //
-        // Expected power = active + scan + bscan = 0.97333
-        double expectedPower = 0.97333;
-        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.25);
-
-        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
-        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
-                .isWithin(PRECISION).of(expectedPower * 0.75);
-
-        // UID1 =
-        //     (1800 / 2400) * 0.8      // active
-        //     + (300 / 400) * 0.05333  // scan
-        //     + (400 / 600) * 0.12     // batched scan
-        //   = 0.72 mAh
-        double expectedPower1 = 0.72;
-        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID1,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower1 * 0.5);
-
-        // UID2 =
-        //     (600 / 2400) * 0.8       // active
-        //     + (100 / 400) * 0.05333  // scan
-        //     + (200 / 600) * 0.12     // batched scan
-        //   = 0.253333 mAh
-        double expectedPower2 = 0.25333;
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.25);
-
-        aggregatedStats.getUidStats(uidStats, APP_UID2,
-                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
-        assertThat(statsLayout.getUidPowerEstimate(uidStats))
-                .isWithin(PRECISION).of(expectedPower2 * 0.75);
-    }
-
-    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
-            WifiPowerStatsProcessor processor) {
-        AggregatedPowerStatsConfig.PowerComponent config =
-                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
-                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
-                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
-                        .setProcessor(processor);
-
-        PowerComponentAggregatedPowerStats aggregatedStats =
-                new PowerComponentAggregatedPowerStats(
-                        new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
-        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
-        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
-        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
-        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
-        return aggregatedStats;
-    }
-
-    private int[] states(int... states) {
-        return states;
-    }
-
-    private void mockWifiActivityEnergyInfo(WifiActivityEnergyInfo waei) {
-        doAnswer(invocation -> {
-            WifiManager.OnWifiActivityEnergyInfoListener
-                    listener = invocation.getArgument(1);
-            listener.onWifiActivityEnergyInfo(waei);
-            return null;
-        }).when(mWifiManager).getWifiActivityEnergyInfoAsync(any(), any());
-    }
-
-    private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
-            NetworkStats.Entry... entries) {
-        NetworkStats stats;
-        if (RavenwoodRule.isOnRavenwood()) {
-            stats = mock(NetworkStats.class);
-            when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
-        } else {
-            stats = new NetworkStats(elapsedTime, initialSize);
-            for (NetworkStats.Entry entry : entries) {
-                stats = stats.addEntry(entry);
-            }
-        }
-        return stats;
-    }
-
-    private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
-            int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
-            long rxPackets, long txBytes, long txPackets, long operations) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
-            when(entry.getUid()).thenReturn(uid);
-            when(entry.getMetered()).thenReturn(metered);
-            when(entry.getRoaming()).thenReturn(roaming);
-            when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
-            when(entry.getRxBytes()).thenReturn(rxBytes);
-            when(entry.getRxPackets()).thenReturn(rxPackets);
-            when(entry.getTxBytes()).thenReturn(txBytes);
-            when(entry.getTxPackets()).thenReturn(txPackets);
-            when(entry.getOperations()).thenReturn(operations);
-            return entry;
-        } else {
-            return new NetworkStats.Entry(iface, uid, set, tag, metered,
-                    roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
-        }
-    }
-
-    private void mockWifiScanTimes(int uid, long scanTimeMs, long batchScanTimeMs) {
-        ScanTimes scanTimes = new ScanTimes();
-        scanTimes.scanTimeMs = scanTimeMs;
-        scanTimes.batchScanTimeMs = batchScanTimeMs;
-        mScanTimes.put(uid, scanTimes);
-    }
-}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
new file mode 100644
index 0000000..0e73329
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.PowerStats;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.text.ParseException;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AggregatedPowerStatsTest {
+    private static final int TEST_POWER_COMPONENT = 1077;
+    private static final int CUSTOM_POWER_COMPONENT = 1042;
+    private static final int APP_1 = 27;
+    private static final int APP_2 = 42;
+    private static final int COMPONENT_STATE_0 = 0;
+    private static final int COMPONENT_STATE_1 = 1;
+    private static final int COMPONENT_STATE_2 = 2;
+
+    private AggregatedPowerStatsConfig
+            mAggregatedPowerStatsConfig;
+    private PowerStats.Descriptor mPowerComponentDescriptor;
+
+    @Before
+    public void setup() throws ParseException {
+        mAggregatedPowerStatsConfig = new AggregatedPowerStatsConfig();
+        mAggregatedPowerStatsConfig.trackPowerComponent(TEST_POWER_COMPONENT)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+
+        mAggregatedPowerStatsConfig.trackCustomPowerComponents(
+                        () -> new PowerStatsProcessor() {
+                            @Override
+                            void finish(
+                                    PowerComponentAggregatedPowerStats stats,
+                                    long timestampMs) {
+                            }
+                        })
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+        SparseArray<String> stateLabels = new SparseArray<>();
+        stateLabels.put(COMPONENT_STATE_1, "one");
+        mPowerComponentDescriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "fan", 2,
+                stateLabels, 1, 3, PersistableBundle.forPair("speed", "fast"));
+    }
+
+    @Test
+    public void aggregation() {
+        AggregatedPowerStats stats = prepareAggregatePowerStats();
+
+        verifyAggregatedPowerStats(stats);
+    }
+
+    @Test
+    public void xmlPersistence() throws Exception {
+        AggregatedPowerStats stats = prepareAggregatePowerStats();
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        serializer.setOutput(baos, "UTF-8");
+        stats.writeXml(serializer);
+        serializer.flush();
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new ByteArrayInputStream(baos.toByteArray()), "UTF-8");
+        AggregatedPowerStats actualStats =
+                AggregatedPowerStats.createFromXml(parser, mAggregatedPowerStatsConfig);
+
+        verifyAggregatedPowerStats(actualStats);
+    }
+
+    private AggregatedPowerStats prepareAggregatePowerStats() {
+        AggregatedPowerStats stats = new AggregatedPowerStats(mAggregatedPowerStatsConfig);
+        stats.start(0);
+
+        PowerStats ps = new PowerStats(mPowerComponentDescriptor);
+        stats.addPowerStats(ps, 0);
+
+        stats.addClockUpdate(1000, 456);
+        stats.setDuration(789);
+
+        stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON, 2000);
+        stats.setUidState(APP_1, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+                BatteryConsumer.PROCESS_STATE_CACHED, 2000);
+        stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 2000);
+
+        ps.stats[0] = 100;
+        ps.stats[1] = 987;
+
+        ps.stateStats.put(COMPONENT_STATE_0, new long[]{1111});
+        ps.stateStats.put(COMPONENT_STATE_1, new long[]{5000});
+
+        ps.uidStats.put(APP_1, new long[]{389, 0, 739});
+        ps.uidStats.put(APP_2, new long[]{278, 314, 628});
+
+        stats.addPowerStats(ps, 3000);
+
+        stats.setDeviceState(AggregatedPowerStatsConfig.STATE_SCREEN,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER, 4000);
+        stats.setUidState(APP_2, AggregatedPowerStatsConfig.STATE_PROCESS_STATE,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
+
+        ps.stats[0] = 444;
+        ps.stats[1] = 0;
+
+        ps.stateStats.clear();
+        ps.stateStats.put(COMPONENT_STATE_1, new long[]{1000});
+        ps.stateStats.put(COMPONENT_STATE_2, new long[]{9000});
+
+        ps.uidStats.put(APP_1, new long[]{0, 0, 400});
+        ps.uidStats.put(APP_2, new long[]{100, 200, 300});
+
+        stats.addPowerStats(ps, 5000);
+
+        PowerStats custom = new PowerStats(
+                new PowerStats.Descriptor(CUSTOM_POWER_COMPONENT, "cu570m", 1, null, 0, 2,
+                        new PersistableBundle()));
+        custom.stats = new long[]{123};
+        custom.uidStats.put(APP_1, new long[]{500, 600});
+        stats.addPowerStats(custom, 6000);
+        return stats;
+    }
+
+    private void verifyAggregatedPowerStats(
+            AggregatedPowerStats stats) {
+        PowerStats.Descriptor descriptor = stats.getPowerComponentStats(TEST_POWER_COMPONENT)
+                .getPowerStatsDescriptor();
+        assertThat(descriptor.powerComponentId).isEqualTo(TEST_POWER_COMPONENT);
+        assertThat(descriptor.name).isEqualTo("fan");
+        assertThat(descriptor.statsArrayLength).isEqualTo(2);
+        assertThat(descriptor.uidStatsArrayLength).isEqualTo(3);
+        assertThat(descriptor.extras.getString("speed")).isEqualTo("fast");
+
+        assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+                .isEqualTo(new long[]{322, 987});
+
+        assertThat(getDeviceStats(stats, TEST_POWER_COMPONENT,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+                .isEqualTo(new long[]{222, 0});
+
+        assertThat(getStateStats(stats, COMPONENT_STATE_0,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+                .isEqualTo(new long[]{1111});
+
+        assertThat(getStateStats(stats, COMPONENT_STATE_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+                .isEqualTo(new long[]{5500});
+
+        assertThat(getStateStats(stats, COMPONENT_STATE_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+                .isEqualTo(new long[]{500});
+
+        assertThat(getStateStats(stats, COMPONENT_STATE_2,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+                .isEqualTo(new long[]{4500});
+
+        assertThat(getStateStats(stats, COMPONENT_STATE_2,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+                .isEqualTo(new long[]{4500});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
+                .isEqualTo(new long[]{259, 0, 492});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_CACHED))
+                .isEqualTo(new long[]{129, 0, 446});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                BatteryConsumer.PROCESS_STATE_CACHED))
+                .isEqualTo(new long[]{0, 0, 200});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_2,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED))
+                .isEqualTo(new long[]{185, 209, 418});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_2,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND))
+                .isEqualTo(new long[]{142, 204, 359});
+
+        assertThat(getUidDeviceStats(stats,
+                TEST_POWER_COMPONENT, APP_2,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .isEqualTo(new long[]{50, 100, 150});
+
+        descriptor = stats.getPowerComponentStats(CUSTOM_POWER_COMPONENT)
+                .getPowerStatsDescriptor();
+        assertThat(descriptor.powerComponentId).isEqualTo(CUSTOM_POWER_COMPONENT);
+        assertThat(descriptor.statsArrayLength).isEqualTo(1);
+        assertThat(descriptor.uidStatsArrayLength).isEqualTo(2);
+
+        assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON))
+                .isEqualTo(new long[]{61});
+        assertThat(getDeviceStats(stats, CUSTOM_POWER_COMPONENT,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER))
+                .isEqualTo(new long[]{61});
+        assertThat(getUidDeviceStats(stats,
+                CUSTOM_POWER_COMPONENT, APP_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_CACHED))
+                .isEqualTo(new long[]{250, 300});
+        assertThat(getUidDeviceStats(stats,
+                CUSTOM_POWER_COMPONENT, APP_1,
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                BatteryConsumer.PROCESS_STATE_CACHED))
+                .isEqualTo(new long[]{250, 300});
+    }
+
+    private static long[] getDeviceStats(
+            AggregatedPowerStats stats, int powerComponentId,
+            int... states) {
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                stats.getPowerComponentStats(powerComponentId);
+        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().statsArrayLength];
+        powerComponentStats.getDeviceStats(out, states);
+        return out;
+    }
+
+    private static long[] getStateStats(
+            AggregatedPowerStats stats, int key, int... states) {
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().stateStatsArrayLength];
+        powerComponentStats.getStateStats(out, key, states);
+        return out;
+    }
+
+    private static long[] getUidDeviceStats(
+            AggregatedPowerStats stats, int powerComponentId,
+            int uid, int... states) {
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                stats.getPowerComponentStats(powerComponentId);
+        long[] out = new long[powerComponentStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        powerComponentStats.getUidStats(out, uid, states);
+        return out;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
new file mode 100644
index 0000000..21e615f
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.processor;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.ScreenPowerStatsCollector;
+import com.android.server.power.stats.ScreenPowerStatsCollector.ScreenUsageTimeRetriever;
+import com.android.server.power.stats.format.PowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AmbientDisplayPowerStatsProcessorTest {
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setNumDisplays(2)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0);
+
+    private static final double PRECISION = 0.1;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+
+    private final ScreenPowerStatsCollector.Injector mInjector =
+            new ScreenPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return new PowerStatsUidResolver();
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public int getDisplayCount() {
+                    return 2;
+                }
+
+                @Override
+                public ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+                    return mScreenUsageTimeRetriever;
+                }
+            };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void processPowerStats() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats();
+
+        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 16.2);
+        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 5.4);
+        assertPowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 0);
+        assertPowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 0);
+    }
+
+    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats() {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN)
+                .setProcessorSupplier(
+                        () -> new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile()));
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
+                        BatteryConsumer.POWER_COMPONENT_SCREEN)
+                .setProcessorSupplier(AmbientDisplayPowerStatsProcessor::new);
+
+        AggregatedPowerStats stats = new AggregatedPowerStats(config);
+        stats.start(0);
+        stats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 0);
+
+        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                .thenReturn(new int[0]);
+
+        stats.addPowerStats(collector.collectStats(), 1000);
+
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
+                .thenReturn(60_000L);
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
+                .thenReturn(120_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
+                .thenReturn(180_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
+                .thenReturn(240_000L);
+        stats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 101_000);
+        stats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 401_000);
+
+        // Slightly larger than 600_000 total screen time, to simulate a sight race
+        // between state changes and power stats collection
+        stats.addPowerStats(collector.collectStats(), 612_000);
+
+        stats.finish(612_000);
+
+        return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
+    }
+
+    private void assertPowerEstimate(
+            PowerComponentAggregatedPowerStats aggregatedStats,
+            int powerState, int screenState, double expectedPowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        PowerStatsLayout layout = new PowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.statsArrayLength];
+        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
+        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedPowerEstimate);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
new file mode 100644
index 0000000..b412ad6
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
@@ -0,0 +1,288 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.function.Supplier;
+
+public class BinaryStatePowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int POWER_COMPONENT = BatteryConsumer.POWER_COMPONENT_AUDIO;
+    private static final int TEST_STATE_FLAG = 0x1;
+
+    private final MockClock mClock = new MockClock();
+    private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+
+    private static class TestBinaryStatePowerStatsProcessor extends BinaryStatePowerStatsProcessor {
+        TestBinaryStatePowerStatsProcessor(int powerComponentId,
+                double averagePowerMilliAmp, PowerStatsUidResolver uidResolver) {
+            super(powerComponentId, uidResolver, averagePowerMilliAmp);
+        }
+
+        @Override
+        protected int getBinaryState(BatteryStats.HistoryItem item) {
+            return (item.states & TEST_STATE_FLAG) != 0 ? STATE_ON : STATE_OFF;
+        }
+    }
+
+    @Test
+    public void powerProfileModel() {
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new TestBinaryStatePowerStatsProcessor(
+                        POWER_COMPONENT,  /* averagePowerMilliAmp */ 100, mUidResolver));
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+
+        stats.finish(11000);
+
+        // Total usage duration is 10000
+        // Total estimated power = 10000 * 100 = 1000000 mA-ms = 0.277777 mAh
+        // Screen-on  - 25%
+        // Screen-off - 75%
+        double expectedPower = 0.277778;
+        long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1 =
+        //     6000 * 100 = 600000 mA-ms = 0.166666 mAh
+        //     split between three different states
+        double expectedPower1 = 0.166666;
+        long[] uidStats = new long[stats.getPowerStatsDescriptor().uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     4000 * 100 = 400000 mA-ms = 0.111111 mAh
+        //     all in the same state
+        double expectedPower2 = 0.111111;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        BinaryStatePowerStatsLayout
+                statsLayout = new BinaryStatePowerStatsLayout();
+        PersistableBundle extras = new PersistableBundle();
+        statsLayout.toExtras(extras);
+        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(POWER_COMPONENT,
+                statsLayout.getDeviceStatsArrayLength(), null, 0,
+                statsLayout.getUidStatsArrayLength(), extras);
+        PowerStats powerStats = new PowerStats(descriptor);
+        powerStats.stats = new long[descriptor.statsArrayLength];
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new TestBinaryStatePowerStatsProcessor(
+                        POWER_COMPONENT,  /* averagePowerMilliAmp */ 100, mUidResolver));
+
+        stats.start(0);
+
+        // Establish a baseline
+        stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime());
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        statsLayout.setConsumedEnergy(powerStats.stats, 0, 2_160_000);
+        stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime());
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+
+        mClock.realtime = 11000;
+        statsLayout.setConsumedEnergy(powerStats.stats, 0, 1_440_000);
+        stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime());
+
+        stats.finish(11000);
+
+        // Total estimated power = 3,600,000 uC = 1.0 mAh
+        // of which 3,000,000 is distributed:
+        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+        // and 600,000 was fully with screen off:
+        //     Screen-off - 1440000 uC = 0.4 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.35 + 0.4);
+
+        // UID1 =
+        //     2,160,000 uC = 0.6 mAh
+        //     split between three different states
+        //          fg screen-on: 2500/6000
+        //          bg screen-off: 2500/6000
+        //          fgs screen-off: 1000/6000
+        double expectedPower1 = 0.6;
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     1440000 mA-ms = 0.4 mAh
+        //     all in the same state
+        double expectedPower2 = 0.4;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+
+    @NonNull
+    private BatteryStats.HistoryItem buildHistoryItem(int elapsedRealtime, boolean stateOn,
+            int uid) {
+        mClock.realtime = elapsedRealtime;
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states = stateOn ? TEST_STATE_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "test";
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(POWER_COMPONENT)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats powerComponentStats =
+                new AggregatedPowerStats(config).getPowerComponentStats(POWER_COMPONENT);
+        powerComponentStats.start(0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
new file mode 100644
index 0000000..6dfc220
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -0,0 +1,547 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.UidTraffic;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseLongArray;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.BluetoothPowerStatsCollector;
+import com.android.server.power.stats.BluetoothPowerStatsCollector.BluetoothStatsRetriever;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BluetoothPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+public class BluetoothPowerStatsProcessorTest {
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int BLUETOOTH_ENERGY_CONSUMER_ID = 1;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_RX, 50.0)
+            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0)
+            .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_IDLE, 10.0)
+            .initMeasuredEnergyStatsLocked();
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
+
+    private BluetoothActivityEnergyInfo mBluetoothActivityEnergyInfo;
+    private final SparseLongArray mUidScanTimes = new SparseLongArray();
+
+    private final BluetoothPowerStatsCollector.BluetoothStatsRetriever mBluetoothStatsRetriever =
+            new BluetoothPowerStatsCollector.BluetoothStatsRetriever() {
+                @Override
+                public void retrieveBluetoothScanTimes(Callback callback) {
+                    for (int i = 0; i < mUidScanTimes.size(); i++) {
+                        callback.onBluetoothScanTime(mUidScanTimes.keyAt(i),
+                                mUidScanTimes.valueAt(i));
+                    }
+                }
+
+                @Override
+                public boolean requestControllerActivityEnergyInfo(Executor executor,
+                        BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback) {
+                    callback.onBluetoothActivityEnergyInfoAvailable(mBluetoothActivityEnergyInfo);
+                    return true;
+                }
+            };
+
+    private final BluetoothPowerStatsCollector.Injector mInjector =
+            new BluetoothPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mPowerStatsUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PackageManager getPackageManager() {
+                    return mPackageManager;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public BluetoothStatsRetriever getBluetoothStatsRetriever() {
+                    return mBluetoothStatsRetriever;
+                }
+            };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
+    }
+
+    @Test
+    public void powerProfileModel_mostlyDataTransfer() {
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
+                mockUidTraffic(APP_UID1, 100, 200),
+                mockUidTraffic(APP_UID2, 300, 400));
+
+        mUidScanTimes.put(APP_UID1, 100);
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
+                mockUidTraffic(APP_UID1, 1100, 2200),
+                mockUidTraffic(APP_UID2, 3300, 4400));
+
+        mUidScanTimes.clear();
+        mUidScanTimes.put(APP_UID1, 200);
+        mUidScanTimes.put(APP_UID2, 300);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        BluetoothPowerStatsLayout statsLayout =
+                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // RX power = 'rx-duration * PowerProfile[bluetooth.controller.rx]`
+        //        RX power = 6000 * 50 = 300000 mA-ms = 0.083333 mAh
+        // TX power = 'tx-duration * PowerProfile[bluetooth.controller.tx]`
+        //        TX power = 1000 * 100 = 100000 mA-ms = 0.02777 mAh
+        // Idle power = 'idle-duration * PowerProfile[bluetooth.controller.idle]`
+        //        Idle power = 2000 * 10 = 20000 mA-ms = 0.00555 mAh
+        // Total power = RX + TX + Idle = 0.116666
+        // Screen-on  - 25%
+        // Screen-off - 75%
+        double expectedPower = 0.116666;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1 =
+        //     (1000 / 4000) * 0.083333        // rx
+        //   + (2000 / 6000) * 0.027777        // tx
+        //   = 0.030092 mAh
+        double expectedPower1 = 0.030092;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2 =
+        //     (3000 / 4000) * 0.083333        // rx
+        //   + (4000 / 6000) * 0.027777        // tx
+        //   = 0.08102 mAh
+        double expectedPower2 = 0.08102;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    @Test
+    public void powerProfileModel_mostlyScan() {
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
+                mockUidTraffic(APP_UID1, 100, 200),
+                mockUidTraffic(APP_UID2, 300, 400));
+
+        mUidScanTimes.put(APP_UID1, 100);
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
+                mockUidTraffic(APP_UID1, 1100, 2200),
+                mockUidTraffic(APP_UID2, 3300, 4400));
+
+        // Total scan time exceeding data transfer times
+        mUidScanTimes.clear();
+        mUidScanTimes.put(APP_UID1, 3100);
+        mUidScanTimes.put(APP_UID2, 5000);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        BluetoothPowerStatsLayout statsLayout =
+                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // RX power = 'rx-duration * PowerProfile[bluetooth.controller.rx]`
+        //        RX power = 6000 * 50 = 300000 mA-ms = 0.083333 mAh
+        // TX power = 'tx-duration * PowerProfile[bluetooth.controller.tx]`
+        //        TX power = 1000 * 100 = 100000 mA-ms = 0.02777 mAh
+        // Idle power = 'idle-duration * PowerProfile[bluetooth.controller.idle]`
+        //        Idle power = 2000 * 10 = 20000 mA-ms = 0.00555 mAh
+        // Total power = RX + TX + Idle = 0.116666
+        double expectedPower = 0.116666;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1 =
+        //     (3000 / 8000) * 0.083333        // rx
+        //   + (3000 / 8000) * 0.027777        // tx
+        //   = 0.041666 mAh
+        double expectedPower1 = 0.041666;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2 =
+        //     (5000 / 8000) * 0.083333        // rx
+        //   + (5000 / 8000) * 0.027777        // tx
+        //   = 0.069443 mAh
+        double expectedPower2 = 0.069443;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    @Test
+    public void consumedEnergyModel() {
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        // Power monitoring hardware exists
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.BLUETOOTH))
+                .thenReturn(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID});
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new BluetoothPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        BluetoothPowerStatsCollector collector = new BluetoothPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1000, 600, 100, 200,
+                mockUidTraffic(APP_UID1, 100, 200),
+                mockUidTraffic(APP_UID2, 300, 400));
+
+        mUidScanTimes.put(APP_UID1, 100);
+
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        mBluetoothActivityEnergyInfo = mockBluetoothActivityEnergyInfo(1100, 6600, 1100, 2200,
+                mockUidTraffic(APP_UID1, 1100, 2200),
+                mockUidTraffic(APP_UID2, 3300, 4400));
+
+        mUidScanTimes.clear();
+        mUidScanTimes.put(APP_UID1, 200);
+        mUidScanTimes.put(APP_UID2, 300);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        // 10 mAh represented as microWattSeconds
+        long energyUws = 10 * 3600 * VOLTAGE_MV;
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{BLUETOOTH_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        BluetoothPowerStatsLayout statsLayout =
+                new BluetoothPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // All estimates are computed as in the #powerProfileModel_mostlyDataTransfer test,
+        // except they are all scaled by the same ratio to ensure that the total estimated
+        // energy is equal to the measured energy
+        double expectedPower = 10;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1
+        //   0.030092           // power profile model estimate
+        //   0.116666           // power profile model estimate for total power
+        //   10                 // total consumed energy
+        //   = 0.030092 * (10 / 0.116666) = 2.579365
+        double expectedPower1 = 2.579365;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2 =
+        //   0.08102            // power profile model estimate
+        //   0.116666           // power profile model estimate for total power
+        //   10                 // total consumed energy
+        //   = 0.08102 * (10 / 0.116666) = 6.944444
+        double expectedPower2 = 6.944444;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_BLUETOOTH)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats aggregatedStats =
+                new AggregatedPowerStats(config).getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_BLUETOOTH);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return aggregatedStats;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
+    private BluetoothActivityEnergyInfo mockBluetoothActivityEnergyInfo(long timestamp,
+            long rxTimeMs, long txTimeMs, long idleTimeMs, UidTraffic... uidTraffic) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            BluetoothActivityEnergyInfo info = mock(BluetoothActivityEnergyInfo.class);
+            when(info.getControllerRxTimeMillis()).thenReturn(rxTimeMs);
+            when(info.getControllerTxTimeMillis()).thenReturn(txTimeMs);
+            when(info.getControllerIdleTimeMillis()).thenReturn(idleTimeMs);
+            when(info.getUidTraffic()).thenReturn(List.of(uidTraffic));
+            return info;
+        } else {
+            final Parcel btActivityEnergyInfoParcel = Parcel.obtain();
+            btActivityEnergyInfoParcel.writeLong(timestamp);
+            btActivityEnergyInfoParcel.writeInt(
+                    BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE);
+            btActivityEnergyInfoParcel.writeLong(txTimeMs);
+            btActivityEnergyInfoParcel.writeLong(rxTimeMs);
+            btActivityEnergyInfoParcel.writeLong(idleTimeMs);
+            btActivityEnergyInfoParcel.writeLong(0L);
+            btActivityEnergyInfoParcel.writeTypedList(List.of(uidTraffic));
+            btActivityEnergyInfoParcel.setDataPosition(0);
+
+            BluetoothActivityEnergyInfo info = BluetoothActivityEnergyInfo.CREATOR
+                    .createFromParcel(btActivityEnergyInfoParcel);
+            btActivityEnergyInfoParcel.recycle();
+            return info;
+        }
+    }
+
+    private UidTraffic mockUidTraffic(int uid, long rxBytes, long txBytes) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            UidTraffic traffic = mock(UidTraffic.class);
+            when(traffic.getUid()).thenReturn(uid);
+            when(traffic.getRxBytes()).thenReturn(rxBytes);
+            when(traffic.getTxBytes()).thenReturn(txBytes);
+            return traffic;
+        } else {
+            final Parcel uidTrafficParcel = Parcel.obtain();
+            uidTrafficParcel.writeInt(uid);
+            uidTrafficParcel.writeLong(rxBytes);
+            uidTrafficParcel.writeLong(txBytes);
+            uidTrafficParcel.setDataPosition(0);
+
+            UidTraffic traffic = UidTraffic.CREATOR.createFromParcel(uidTrafficParcel);
+            uidTrafficParcel.recycle();
+            return traffic;
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
new file mode 100644
index 0000000..0afcbf1
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
@@ -0,0 +1,275 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.CameraPowerStatsCollector;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+public class CameraPowerStatsTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CAMERA, 100.0)
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+    private static final int ENERGY_CONSUMER_ID = 777;
+
+    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+    EnergyConsumerPowerStatsCollector.Injector mInjector =
+            new EnergyConsumerPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+            };
+
+    private MonotonicClock mMonotonicClock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        when(mConsumedEnergyRetriever
+                .getEnergyConsumerIds(eq((int) EnergyConsumerType.CAMERA)))
+                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new CameraPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));
+
+        CameraPowerStatsCollector collector = new CameraPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime()));
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        stats.start(0);
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+
+        mStatsRule.setTime(11_000, 11_000);
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
+        collector.collectAndDeliverStats();
+
+        stats.finish(11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
+
+        // Total estimated power = 3,600,000 uC = 1.0 mAh
+        // of which 3,000,000 is distributed:
+        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+        // and 600,000 was fully with screen off:
+        //     Screen-off - 1440000 uC = 0.4 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.35 + 0.4);
+
+        // UID1 =
+        //     2,160,000 uC = 0.6 mAh
+        //     split between three different states
+        //          fg screen-on: 2500/6000
+        //          bg screen-off: 2500/6000
+        //          fgs screen-off: 1000/6000
+        double expectedPower1 = 0.6;
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     1440000 mA-ms = 0.4 mAh
+        //     all in the same state
+        double expectedPower2 = 0.4;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+            int uid) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states2 = stateOn ? BatteryStats.HistoryItem.STATE2_CAMERA_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "camera";
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig
+                config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CAMERA)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_CAMERA);
+        powerComponentStats.start(0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+
+    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
+        EnergyConsumerResult result = new EnergyConsumerResult();
+        result.id = id;
+        result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
+        return new EnergyConsumerResult[]{result};
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
new file mode 100644
index 0000000..6938615
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CpuPowerStatsProcessorTest.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.os.BatteryConsumer;
+import android.os.PersistableBundle;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.LongArray;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class CpuPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+            .setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
+            .setCpuScalingPolicy(2, new int[]{2, 3}, new int[]{300})
+            .setAveragePowerForCpuScalingPolicy(0, 360)
+            .setAveragePowerForCpuScalingPolicy(2, 480)
+            .setAveragePowerForCpuScalingStep(0, 0, 300)
+            .setAveragePowerForCpuScalingStep(0, 1, 400)
+            .setAveragePowerForCpuScalingStep(2, 0, 500)
+            .setCpuPowerBracketCount(3)
+            .setCpuPowerBracket(0, 0, 0)
+            .setCpuPowerBracket(0, 1, 1)
+            .setCpuPowerBracket(2, 0, 2);
+
+    private AggregatedPowerStatsConfig.PowerComponent mConfig;
+    private MockPowerComponentAggregatedPowerStats mStats;
+
+    @Before
+    public void setup() {
+        mConfig = new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(() -> new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
+                        mStatsRule.getCpuScalingPolicies()));
+    }
+
+    @Test
+    public void powerProfileModel() {
+        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, false);
+        mStats.start(0);
+
+        mStats.setDeviceStats(
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+                concat(
+                        values(3500, 4500, 3000),   // scaling steps
+                        values(2000, 1000),         // clusters
+                        values(5000)),              // uptime
+                3.113732);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+                concat(
+                        values(6000, 6500, 4000),
+                        values(5000, 3000),
+                        values(7000)),
+                4.607245);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+                concat(
+                        values(9000, 10000, 7000),
+                        values(8000, 6000),
+                        values(20000)),
+                7.331799);
+        mStats.setUidStats(24,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(400, 1500, 2000),  1.206947);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(900, 1000, 1500), 1.016182);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+                values(600, 500, 300), 0.385042);
+        mStats.setUidStats(42,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+                values(1500, 2000, 1000), 1.252578);
+
+        mStats.finish(10_000);
+
+        mStats.verifyPowerEstimates();
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        mStats = new MockPowerComponentAggregatedPowerStats(mConfig, true);
+        mStats.start(0);
+
+        mStats.setDeviceStats(
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON),
+                concat(
+                        values(3500, 4500, 3000),           // scaling steps
+                        values(2000, 1000),                 // clusters
+                        values(5000),                       // uptime
+                        values(5_000_000L, 6_000_000L)),    // energy, uC
+                3.055555);
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON),
+                concat(
+                        values(6000, 6500, 4000),
+                        values(5000, 3000),
+                        values(7000),
+                        values(5_000_000L, 6_000_000L)),    // same as above
+                3.055555);                                  // same as above - WAI
+        mStats.setDeviceStats(
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER),
+                concat(
+                        values(9000, 10000, 7000),
+                        values(8000, 6000),
+                        values(20000),
+                        values(8_000_000L, 18_000_000L)),
+                7.222222);
+        mStats.setUidStats(24,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(400, 1500, 2000),  1.449078);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND),
+                values(900, 1000, 1500), 1.161902);
+        mStats.setUidStats(42,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_BACKGROUND),
+                values(600, 500, 300), 0.355406);
+        mStats.setUidStats(42,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED),
+                values(1500, 2000, 1000), 0.80773);
+
+        mStats.finish(10_000);
+
+        mStats.verifyPowerEstimates();
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private long[] values(long... values) {
+        return values;
+    }
+
+    private long[] concat(long[]... arrays) {
+        LongArray all = new LongArray();
+        for (long[] array : arrays) {
+            for (long value : array) {
+                all.add(value);
+            }
+        }
+        return all.toArray();
+    }
+
+    private static class MockPowerComponentAggregatedPowerStats extends
+            PowerComponentAggregatedPowerStats {
+        private final CpuPowerStatsLayout mStatsLayout;
+        private final PowerStats.Descriptor mDescriptor;
+        private final HashMap<String, long[]> mDeviceStats = new HashMap<>();
+        private final HashMap<String, long[]> mUidStats = new HashMap<>();
+        private final HashSet<Integer> mUids = new HashSet<>();
+        private final HashMap<String, Double> mExpectedDevicePower = new HashMap<>();
+        private final HashMap<String, Double> mExpectedUidPower = new HashMap<>();
+
+        MockPowerComponentAggregatedPowerStats(
+                AggregatedPowerStatsConfig.PowerComponent config,
+                boolean useEnergyConsumers) {
+            super(new AggregatedPowerStats(new AggregatedPowerStatsConfig()), config);
+            mStatsLayout = new CpuPowerStatsLayout(useEnergyConsumers ? 2 : 0, 2,
+                    new int[]{0, 1, 2});
+            PersistableBundle extras = new PersistableBundle();
+            mStatsLayout.toExtras(extras);
+            mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+                    mStatsLayout.getDeviceStatsArrayLength(), null, 0,
+                    mStatsLayout.getUidStatsArrayLength(), extras);
+        }
+
+        @Override
+        public PowerStats.Descriptor getPowerStatsDescriptor() {
+            return mDescriptor;
+        }
+
+        @Override
+        boolean getDeviceStats(long[] outValues, int[] deviceStates) {
+            long[] values = getDeviceStats(deviceStates);
+            System.arraycopy(values, 0, outValues, 0, values.length);
+            return true;
+        }
+
+        private long[] getDeviceStats(int[] deviceStates) {
+            String key = statesToString(getConfig().getDeviceStateConfig(), deviceStates);
+            long[] values = mDeviceStats.get(key);
+            return values == null ? new long[mDescriptor.statsArrayLength] : values;
+        }
+
+        void setDeviceStats(int[] states, long[] values, double expectedPowerEstimate) {
+            setDeviceStats(states, values);
+            mExpectedDevicePower.put(statesToString(getConfig().getDeviceStateConfig(), states),
+                    expectedPowerEstimate);
+        }
+
+        @Override
+        void setDeviceStats(int[] states, long[] values) {
+            String key = statesToString(getConfig().getDeviceStateConfig(), states);
+            mDeviceStats.put(key, Arrays.copyOf(values, mDescriptor.statsArrayLength));
+        }
+
+        @Override
+        boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
+            long[] values = getUidStats(uid, uidStates);
+            assertThat(values).isNotNull();
+            System.arraycopy(values, 0, outValues, 0, values.length);
+            return true;
+        }
+
+        private long[] getUidStats(int uid, int[] uidStates) {
+            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), uidStates);
+            long[] values = mUidStats.get(key);
+            return values == null ? new long[mDescriptor.uidStatsArrayLength] : values;
+        }
+
+        void setUidStats(int uid, int[] states, long[] values, double expectedPowerEstimate) {
+            setUidStats(uid, states, values);
+            mExpectedUidPower.put(
+                    uid + " " + statesToString(getConfig().getUidStateConfig(), states),
+                    expectedPowerEstimate);
+        }
+
+        @Override
+        void setUidStats(int uid, int[] states, long[] values) {
+            mUids.add(uid);
+            String key = uid + " " + statesToString(getConfig().getUidStateConfig(), states);
+            mUidStats.put(key, Arrays.copyOf(values, mDescriptor.uidStatsArrayLength));
+        }
+
+        @Override
+        void collectUids(Collection<Integer> uids) {
+            uids.addAll(mUids);
+        }
+
+        void verifyPowerEstimates() {
+            StringBuilder mismatches = new StringBuilder();
+            for (Map.Entry<String, Double> entry : mExpectedDevicePower.entrySet()) {
+                String key = entry.getKey();
+                double expected = mExpectedDevicePower.get(key);
+                double actual = mStatsLayout.getDevicePowerEstimate(mDeviceStats.get(key));
+                if (Math.abs(expected - actual) > 0.005) {
+                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+                }
+            }
+            for (Map.Entry<String, Double> entry : mExpectedUidPower.entrySet()) {
+                String key = entry.getKey();
+                double expected = mExpectedUidPower.get(key);
+                double actual = mStatsLayout.getUidPowerEstimate(mUidStats.get(key));
+                if (Math.abs(expected - actual) > 0.005) {
+                    mismatches.append(key + " expected: " + expected + " actual: " + actual + "\n");
+                }
+            }
+            if (!mismatches.isEmpty()) {
+                fail("Unexpected power estimations:\n" + mismatches);
+            }
+        }
+
+        private String statesToString(MultiStateStats.States[] config, int[] states) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < states.length; i++) {
+                sb.append(config[i].getName()).append("=").append(states[i]).append(" ");
+            }
+            return sb.toString();
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
new file mode 100644
index 0000000..42baba7
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
@@ -0,0 +1,300 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerAttribution;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.CustomEnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+public class CustomEnergyConsumerPowerStatsTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+
+    public static final int ENERGY_CONSUMER_ID1 = 77;
+    public static final int ENERGY_CONSUMER_ID2 = 88;
+    private static final int VOLTAGE_MV = 3500;
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final EnergyConsumerPowerStatsLayout POWER_STATS_LAYOUT =
+            new EnergyConsumerPowerStatsLayout();
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+    private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver();
+
+    private EnergyConsumerPowerStatsCollector.Injector mInjector =
+            new EnergyConsumerPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mPowerStatsUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+            };
+
+
+    private CustomEnergyConsumerPowerStatsCollector mCollector;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mCollector = new CustomEnergyConsumerPowerStatsCollector(mInjector);
+        mCollector.setEnabled(true);
+    }
+
+    @Test
+    public void collectStats() throws Exception {
+        // Establish a baseline
+        collectPowerStats(0, 10_000, 30_000,
+                createAttribution(APP_UID1, 10_000),
+                createAttribution(APP_UID2, 15_000));
+
+        List<PowerStats> results = collectPowerStats(12345, 45_000, 100_000,
+                createAttribution(APP_UID1, 24_000),
+                createAttribution(APP_UID2, 36_000));
+
+        assertThat(results).hasSize(2);
+        PowerStats ps1 = results.stream()
+                .filter(ps -> ps.descriptor.name.equals("FOO")).findAny().orElseThrow();
+        assertThat(ps1.durationMs).isEqualTo(12345);
+
+        // Energy (uWs) / (voltage (mV) / 1000) -> charge (uC)
+        assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps1.stats, 0))
+                .isEqualTo(10000);
+        assertThat(ps1.uidStats.size()).isEqualTo(0);
+
+        PowerStats ps2 = results.stream()
+                .filter(ps -> ps.descriptor.name.equals("BAR")).findAny().orElseThrow();
+        assertThat(POWER_STATS_LAYOUT.getConsumedEnergy(ps2.stats, 0))
+                .isEqualTo(20000);
+        assertThat(ps2.uidStats.size()).isEqualTo(2);
+        assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID1), 0))
+                .isEqualTo(4000);
+        assertThat(POWER_STATS_LAYOUT.getUidConsumedEnergy(ps2.uidStats.get(APP_UID2), 0))
+                .isEqualTo(6000);
+    }
+
+    @Test
+    public void processStats() throws Exception {
+        AggregatedPowerStats aggregatedPowerStats = createAggregatedPowerStats();
+        aggregatedPowerStats.start(0);
+        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND,
+                0);
+        aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        List<PowerStats> results = collectPowerStats(0, 10_000, 30_000,
+                createAttribution(APP_UID1, 10_000),
+                createAttribution(APP_UID2, 15_000));
+        for (PowerStats powerStats : results) {
+            aggregatedPowerStats.addPowerStats(powerStats, 0);
+        }
+
+        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_BATTERY, 10_000);
+        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 10_000);
+        aggregatedPowerStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
+                10_000);
+        aggregatedPowerStats.setUidState(APP_UID2, STATE_PROCESS_STATE,
+                PROCESS_STATE_FOREGROUND_SERVICE, 10_000);
+
+        results = collectPowerStats(12345, 45_000, 100_000,
+                createAttribution(APP_UID1, 24_000),
+                createAttribution(APP_UID2, 36_000));
+        for (PowerStats powerStats : results) {
+            aggregatedPowerStats.addPowerStats(powerStats, 40_000);
+        }
+
+        aggregatedPowerStats.finish(40_0000);
+
+        List<PowerComponentAggregatedPowerStats> powerComponentStats =
+                aggregatedPowerStats.getPowerComponentStats();
+
+        PowerComponentAggregatedPowerStats ps1 = powerComponentStats.stream()
+                .filter(ps -> ps.getPowerStatsDescriptor().name.equals("FOO")).findAny()
+                .orElseThrow();
+        PowerComponentAggregatedPowerStats ps2 = powerComponentStats.stream()
+                .filter(ps -> ps.getPowerStatsDescriptor().name.equals("BAR")).findAny()
+                .orElseThrow();
+
+        long[] deviceStats = new long[ps1.getPowerStatsDescriptor().statsArrayLength];
+        long[] uidStats = new long[ps1.getPowerStatsDescriptor().uidStatsArrayLength];
+
+        // Total estimated power = 10,000 uC = 0.00278 mAh
+        double expectedPower = 0.00278;
+        ps1.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        ps1.getDeviceStats(deviceStats, states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER));
+        assertThat(POWER_STATS_LAYOUT.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1: estimated power = 4,000 uC = 0.00111 mAh
+        expectedPower = 0.00111;
+        ps2.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        ps2.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID2: estimated power = 6,000 uC = 0.00166 mAh
+        expectedPower = 0.00167;
+        ps2.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        ps2.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(POWER_STATS_LAYOUT.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+    }
+
+    private List<PowerStats> collectPowerStats(long timestamp, int chargeUc1, int chargeUc2,
+            EnergyConsumerAttribution... attributions2) throws Exception {
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.OTHER))
+                .thenReturn(new int[]{ENERGY_CONSUMER_ID1, ENERGY_CONSUMER_ID2});
+        when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID1))
+                .thenReturn("FOO");
+        when(mConsumedEnergyRetriever.getEnergyConsumerName(ENERGY_CONSUMER_ID2))
+                .thenReturn("BAR");
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID1}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID1, chargeUc1, null));
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID2}))
+                .thenReturn(
+                        createEnergyConsumerResults(ENERGY_CONSUMER_ID2, chargeUc2, attributions2));
+
+        mStatsRule.setTime(timestamp, timestamp);
+        List<PowerStats> results = new ArrayList<>();
+        CountDownLatch latch = new CountDownLatch(2);
+        Consumer<PowerStats> consumer = powerStats -> {
+            results.add(powerStats);
+            latch.countDown();
+        };
+        mCollector.addConsumer(consumer);
+        mCollector.schedule();
+        assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
+        mCollector.removeConsumer(consumer);
+        return results;
+    }
+
+    private static AggregatedPowerStats createAggregatedPowerStats() {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+                .trackDeviceStates(
+                        STATE_POWER,
+                        STATE_SCREEN)
+                .trackUidStates(
+                        STATE_POWER,
+                        STATE_SCREEN,
+                        STATE_PROCESS_STATE);
+
+        return new AggregatedPowerStats(config);
+    }
+
+    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUws,
+            EnergyConsumerAttribution[] attributions) {
+        EnergyConsumerResult result = new EnergyConsumerResult();
+        result.id = id;
+        result.energyUWs = energyUws;
+        result.attribution = attributions;
+        return new EnergyConsumerResult[]{result};
+    }
+
+    private EnergyConsumerAttribution createAttribution(int uid, long energyUWs) {
+        EnergyConsumerAttribution attribution = new EnergyConsumerAttribution();
+        attribution.uid = uid;
+        attribution.energyUWs = energyUWs;
+        return attribution;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
new file mode 100644
index 0000000..e6207d4
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
@@ -0,0 +1,474 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.location.GnssSignalQuality;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.EnergyConsumerPowerStatsCollector;
+import com.android.server.power.stats.GnssPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.BinaryStatePowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+public class GnssPowerStatsTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_GPS_ON, 100.0)
+            .setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, new double[]{1000, 100})
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+    private static final int ENERGY_CONSUMER_ID = 777;
+    private static final long START_TIME = 10_000_000_000L;
+
+    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+
+    EnergyConsumerPowerStatsCollector.Injector mInjector =
+            new EnergyConsumerPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+            };
+
+    private MonotonicClock mMonotonicClock;
+    private final BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(START_TIME, mStatsRule.getMockClock());
+        mHistoryItem.clear();
+    }
+
+    @Test
+    public void powerProfileModel() {
+        // ODPM unsupported
+        when(mConsumedEnergyRetriever
+                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));
+
+        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime()));
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
+                START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                START_TIME + 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+        stats.noteStateChange(buildHistoryItem(7000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+        stats.noteStateChange(buildHistoryItem(8000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+        mStatsRule.setTime(11_000, 11_000);
+        collector.collectAndDeliverStats();
+
+        stats.finish(START_TIME + 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
+
+        // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
+        // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
+        // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
+        // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.12500 + 0.83333);
+
+        // UID1 =
+        //   scr-on FG: 2500 -> 0.06944 mAh
+        //   scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
+        //   scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.31944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.12777);
+
+        // UID2 =
+        //   scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.51111);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    @Test
+    public void initialStateGnssOn() {
+        // ODPM unsupported
+        when(mConsumedEnergyRetriever
+                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));
+
+        stats.noteStateChange(buildHistoryItemInitialStateGpsOn(0));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
+                START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                START_TIME + 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+        stats.noteStateChange(buildHistoryItem(7000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+        stats.noteStateChange(buildHistoryItem(8000,
+                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+        mStatsRule.setTime(11_000, 11_000);
+
+        stats.finish(START_TIME + 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
+
+        // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
+        // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
+        // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
+        // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.12500 + 0.83333);
+
+        // UID1 =
+        //   scr-on FG: 2500 -> 0.06944 mAh
+        //   scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
+        //   scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.06944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.31944);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.12777);
+
+        // UID2 =
+        //   scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.51111);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        when(mConsumedEnergyRetriever
+                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS)))
+                .thenReturn(new int[]{ENERGY_CONSUMER_ID});
+
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));
+
+        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
+        collector.addConsumer(
+                powerStats -> stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime()));
+        collector.setEnabled(true);
+
+        // Establish a baseline
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
+                START_TIME + 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                START_TIME + 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 2_170_000));
+        collector.collectAndDeliverStats();
+
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
+        stats.noteStateChange(buildHistoryItem(7000, GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
+        stats.noteStateChange(buildHistoryItem(8000, GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
+        mStatsRule.setTime(11_000, 11_000);
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
+                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
+        collector.collectAndDeliverStats();
+
+        stats.finish(START_TIME + 11_000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout(descriptor);
+
+        // Total estimated power = 3,600,000 uC = 1.0 mAh
+        // of which 3,000,000 is distributed:
+        //     Screen-on  - 2500/6000 * 2160000 = 900000 uC = 0.25 mAh
+        //     Screen-off - 3500/6000 * 2160000 = 1260000 uC = 0.35 mAh
+        // and 600,000 was fully with screen off:
+        //     Screen-off - 1440000 uC = 0.4 mAh
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.25);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.35 + 0.4);
+
+        // UID1 =
+        //     2,160,000 uC = 0.6 mAh
+        //     split between three different states
+        //          fg screen-on: 2500/6000
+        //          bg screen-off: 2500/6000
+        //          fgs screen-off: 1000/6000
+        double expectedPower1 = 0.6;
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 6000);
+
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 1000 / 6000);
+
+        // UID2 =
+        //     1440000 mA-ms = 0.4 mAh
+        //     all in the same state
+        double expectedPower2 = 0.4;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2);
+
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItemInitialStateGpsOn(long timestamp) {
+        mStatsRule.setTime(timestamp, timestamp);
+        mHistoryItem.time = mMonotonicClock.monotonicTime();
+        mHistoryItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
+        setGnssSignalLevel(BatteryStats.HistoryItem.GNSS_SIGNAL_QUALITY_NONE);
+        return mHistoryItem;
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(long timestamp, boolean stateOn,
+            int uid) {
+        mStatsRule.setTime(timestamp, timestamp);
+        mHistoryItem.time = mMonotonicClock.monotonicTime();
+        mHistoryItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
+        if (stateOn) {
+            mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        mHistoryItem.eventTag = mHistoryItem.localEventTag;
+        mHistoryItem.eventTag.uid = uid;
+        mHistoryItem.eventTag.string = "gnss";
+        return mHistoryItem;
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(long timestamp, int signalLevel) {
+        mStatsRule.setTime(timestamp, timestamp);
+        mHistoryItem.time = mMonotonicClock.monotonicTime();
+        setGnssSignalLevel(signalLevel);
+        mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
+        mHistoryItem.eventTag = null;
+        return mHistoryItem;
+    }
+
+    private void setGnssSignalLevel(int signalLevel) {
+        mHistoryItem.states2 =
+                (mHistoryItem.states2 & ~BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+                        | signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig
+                config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_GNSS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
+        powerComponentStats.start(START_TIME);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, START_TIME);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, START_TIME);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND,
+                START_TIME);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED,
+                START_TIME);
+
+        return powerComponentStats;
+    }
+
+    private EnergyConsumerResult[] createEnergyConsumerResults(int id, long energyUWs) {
+        EnergyConsumerResult result = new EnergyConsumerResult();
+        result.id = id;
+        result.energyUWs = (long) (energyUWs * (double) VOLTAGE_MV / 1000);
+        return new EnergyConsumerResult[]{result};
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
new file mode 100644
index 0000000..80358c5
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -0,0 +1,533 @@
+/*
+ * 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.processor;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class MobileRadioPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int MOBILE_RADIO_ENERGY_CONSUMER_ID = 1;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+    @Mock
+    private Context mContext;
+    @Mock
+    private PowerStatsUidResolver mPowerStatsUidResolver;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private Supplier<NetworkStats> mNetworkStatsSupplier;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private LongSupplier mCallDurationSupplier;
+    @Mock
+    private LongSupplier mScanDurationSupplier;
+
+    private final MobileRadioPowerStatsCollector.Injector mInjector =
+            new MobileRadioPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mPowerStatsUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PackageManager getPackageManager() {
+                    return mPackageManager;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+                    return mNetworkStatsSupplier;
+                }
+
+                @Override
+                public TelephonyManager getTelephonyManager() {
+                    return mTelephonyManager;
+                }
+
+                @Override
+                public LongSupplier getCallDurationSupplier() {
+                    return mCallDurationSupplier;
+                }
+
+                @Override
+                public LongSupplier getPhoneSignalScanDurationSupplier() {
+                    return mScanDurationSupplier;
+                }
+            };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mPowerStatsUidResolver.mapUid(anyInt()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+    }
+
+    @Test
+    public void powerProfileModel() {
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+                .thenReturn(new int[0]);
+
+        mStatsRule.setTestPowerProfile("power_profile_test_modem_calculator");
+
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        MobileRadioPowerStatsCollector collector =
+                new MobileRadioPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        // Initial empty ModemActivityInfo.
+        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[]{100, 200, 300, 400, 500}, 600);
+        mockModemActivityInfo(mai);
+
+        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        PowerStats powerStats = collector.collectStats();
+
+        aggregatedStats.addPowerStats(powerStats, 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        MobileRadioPowerStatsLayout statsLayout =
+                new MobileRadioPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        //    720 mA * 100 ms  (level 0 TX drain rate * level 0 TX duration)
+        // + 1080 mA * 200 ms  (level 1 TX drain rate * level 1 TX duration)
+        // + 1440 mA * 300 ms  (level 2 TX drain rate * level 2 TX duration)
+        // + 1800 mA * 400 ms  (level 3 TX drain rate * level 3 TX duration)
+        // + 2160 mA * 500 ms  (level 4 TX drain rate * level 4 TX duration)
+        // + 1440 mA * 600 ms  (RX drain rate * RX duration)
+        // +  360 mA * 3000 ms (idle drain rate * idle duration)
+        // +   70 mA * 2000 ms (sleep drain rate * sleep duration)
+        // _________________
+        // =    4604000 mA-ms or 1.27888 mA-h
+        //   25% of 1.27888 = 0.319722
+        //   75% of 1.27888 = 0.959166
+        double totalPower = 0;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.319722);
+        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.959166);
+        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+
+        assertThat(totalPower).isWithin(PRECISION).of(1.27888);
+
+        //    720 mA * 100 ms  (level 0 TX drain rate * level 0 TX duration)
+        // + 1080 mA * 200 ms  (level 1 TX drain rate * level 1 TX duration)
+        // + 1440 mA * 300 ms  (level 2 TX drain rate * level 2 TX duration)
+        // + 1800 mA * 400 ms  (level 3 TX drain rate * level 3 TX duration)
+        // + 2160 mA * 500 ms  (level 4 TX drain rate * level 4 TX duration)
+        // + 1440 mA * 600 ms  (RX drain rate * RX duration)
+        // _________________
+        // =    3384000 mA-ms or 0.94 mA-h
+        double uidPower1 = 0;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.17625);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.17625);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.3525);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        double uidPower2 = 0;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.05875);
+        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.17625);
+        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+        assertThat(uidPower1 + uidPower2)
+                .isWithin(PRECISION).of(0.94);
+
+        // 3/4 of total packets were sent by APP_UID so 75% of total
+        assertThat(uidPower1 / (uidPower1 + uidPower2))
+                .isWithin(PRECISION).of(0.75);
+    }
+
+    @Test
+    public void energyConsumerModel() {
+        PowerComponentAggregatedPowerStats aggregatedStats =
+                prepareAggregatedStats_energyConsumerModel();
+
+        MobileRadioPowerStatsLayout statsLayout =
+                new MobileRadioPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // 10_000_000 micro-Coulomb * 1/1000 milli/micro * 1/3600 hour/second = 2.77778 mAh
+        double totalPower = 0;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.671837);
+        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+        assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.022494);
+        totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(2.01596);
+        totalPower += statsLayout.getDevicePowerEstimate(deviceStats);
+        assertThat(statsLayout.getDeviceCallPowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.067484);
+        totalPower += statsLayout.getDeviceCallPowerEstimate(deviceStats);
+
+        // These estimates are supposed to add up to the measured energy, 2.77778 mAh
+        assertThat(totalPower).isWithin(PRECISION).of(2.77778);
+
+        double uidPower1 = 0;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.198236);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.198236);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.396473);
+        uidPower1 += statsLayout.getUidPowerEstimate(uidStats);
+
+        double uidPower2 = 0;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.066078);
+        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(0.198236);
+        uidPower2 += statsLayout.getUidPowerEstimate(uidStats);
+
+        // Total power attributed to apps is significantly less than the grand total,
+        // because we only attribute TX/RX to apps but not maintaining a connection with the cell.
+        assertThat(uidPower1 + uidPower2)
+                .isWithin(PRECISION).of(1.057259);
+
+        // 3/4 of total packets were sent by APP_UID so 75% of total RX/TX power is attributed to it
+        assertThat(uidPower1 / (uidPower1 + uidPower2))
+                .isWithin(PRECISION).of(0.75);
+    }
+
+    @Test
+    public void test_toString() {
+        PowerComponentAggregatedPowerStats stats = prepareAggregatedStats_energyConsumerModel();
+        String string = stats.toString();
+        assertThat(string).contains("(pwr-other scr-on)"
+                + " sleep: 500 idle: 750 scan: 1388 call: 50 energy: 2500000 power: 0.672");
+        assertThat(string).contains("(pwr-other scr-other)"
+                + " sleep: 1500 idle: 2250 scan: 4166 call: 150 energy: 7500000 power: 2.02");
+        assertThat(string).contains("(pwr-other scr-on other)"
+                + " rx: 150 tx: [25, 50, 75, 100, 125]");
+        assertThat(string).contains("(pwr-other scr-other other)"
+                + " rx: 450 tx: [75, 150, 225, 300, 375]");
+        assertThat(string).contains("(pwr-other scr-on fg)"
+                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+        assertThat(string).contains("(pwr-other scr-other bg)"
+                + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+        assertThat(string).contains("(pwr-other scr-other fgs)"
+                + " rx-pkts: 750 rx-B: 5000 tx-pkts: 150 tx-B: 10000 power: 0.396");
+    }
+
+    private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() {
+        // PowerStats hardware is available
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+                .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
+
+        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
+                .initMeasuredEnergyStatsLocked();
+
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        MobileRadioPowerStatsCollector collector =
+                new MobileRadioPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        // Initial empty ModemActivityInfo.
+        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergy(
+                new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[]{100, 200, 300, 400, 500}, 600);
+        mockModemActivityInfo(mai);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
+
+        when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+        when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+        PowerStats powerStats = collector.collectStats();
+
+        aggregatedStats.addPowerStats(powerStats, 10_000);
+
+        aggregatedStats.finish(10_000);
+        return aggregatedStats;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
+    private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+        doAnswer(invocation -> {
+            OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+                    receiver = invocation.getArgument(1);
+            receiver.onResult(emptyMai);
+            return null;
+        }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+    }
+
+    private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+            NetworkStats.Entry... entries) {
+        NetworkStats stats;
+        if (RavenwoodRule.isOnRavenwood()) {
+            stats = mock(NetworkStats.class);
+            when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+        } else {
+            stats = new NetworkStats(elapsedTime, initialSize);
+            for (NetworkStats.Entry entry : entries) {
+                stats = stats.addEntry(entry);
+            }
+        }
+        return stats;
+    }
+
+    private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+            int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+            long rxPackets, long txBytes, long txPackets, long operations) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+            when(entry.getUid()).thenReturn(uid);
+            when(entry.getMetered()).thenReturn(metered);
+            when(entry.getRoaming()).thenReturn(roaming);
+            when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+            when(entry.getRxBytes()).thenReturn(rxBytes);
+            when(entry.getRxPackets()).thenReturn(rxPackets);
+            when(entry.getTxBytes()).thenReturn(txBytes);
+            when(entry.getTxPackets()).thenReturn(txPackets);
+            when(entry.getOperations()).thenReturn(operations);
+            return entry;
+        } else {
+            return new NetworkStats.Entry(iface, uid, set, tag, metered,
+                    roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.java
new file mode 100644
index 0000000..704ee62
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStatePowerAttributorTest.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.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsScheduler;
+import com.android.server.power.stats.PowerStatsSpan;
+import com.android.server.power.stats.PowerStatsStore;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+public class MultiStatePowerAttributorTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private PowerStatsStore mPowerStatsStore;
+    private Handler mHandler;
+    private final MockClock mClock = new MockClock();
+    private final MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+    private PowerStatsScheduler mPowerStatsScheduler;
+    private PowerStatsAggregator mPowerStatsAggregator;
+    private MultiStatePowerAttributor mPowerAttributor;
+    private final List<Long> mScheduledAlarms = new ArrayList<>();
+    private boolean mPowerStatsCollectionOccurred;
+
+    private static final int START_REALTIME = 7654321;
+
+    @Before
+    public void setup() throws IOException {
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
+        mClock.currentTime = Instant.parse("2023-01-02T03:04:05.00Z").toEpochMilli();
+        mClock.realtime = START_REALTIME;
+
+        HandlerThread bgThread = new HandlerThread("bg thread");
+        bgThread.start();
+        mHandler = new Handler(bgThread.getLooper());
+        mPowerStatsStore = new PowerStatsStore(
+                Files.createTempDirectory("MultiStatePowerAttributorTest").toFile(), mHandler);
+        mPowerStatsAggregator = mock(PowerStatsAggregator.class);
+        mPowerAttributor = new MultiStatePowerAttributor(mPowerStatsStore, mPowerStatsAggregator);
+        mPowerStatsScheduler = new PowerStatsScheduler(
+                () -> mPowerStatsCollectionOccurred = true,
+                mock(BatteryStatsHistory.class),
+                mPowerAttributor, TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1),
+                mPowerStatsStore,
+                ((triggerAtMillis, tag, onAlarmListener, handler) ->
+                        mScheduledAlarms.add(triggerAtMillis)),
+                mClock, mMonotonicClock, () -> 12345L, mHandler);
+    }
+
+    @Test
+    public void storeAggregatedPowerStats() {
+        mPowerStatsStore.reset();
+
+        assertThat(mPowerStatsStore.getTableOfContents()).isEmpty();
+
+        mPowerAttributor.storeAggregatedPowerStats(
+                createAggregatedPowerStats(mMonotonicClock.monotonicTime(), mClock.currentTime,
+                        123));
+
+        long delayBeforeAggregating = TimeUnit.MINUTES.toMillis(90);
+        mClock.realtime += delayBeforeAggregating;
+        mClock.currentTime += delayBeforeAggregating;
+
+        doAnswer(invocation -> {
+            // The first span is longer than 30 min, because the end time is being aligned with
+            // the wall clock.  Subsequent spans should be precisely 30 minutes.
+            long startTime = invocation.getArgument(1);
+            long endTime = invocation.getArgument(2);
+            Consumer<AggregatedPowerStats> consumer = invocation.getArgument(3);
+
+            long startTimeWallClock =
+                    mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+            long endTimeWallClock =
+                    mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+            assertThat(startTime).isEqualTo(START_REALTIME + 123);
+            assertThat(endTime - startTime).isAtLeast(TimeUnit.MINUTES.toMillis(30));
+            assertThat(Instant.ofEpochMilli(endTimeWallClock))
+                    .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+
+            consumer.accept(
+                    createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+            return null;
+        }).doAnswer(invocation -> {
+            long startTime = invocation.getArgument(1);
+            long endTime = invocation.getArgument(2);
+            Consumer<AggregatedPowerStats> consumer = invocation.getArgument(3);
+
+            long startTimeWallClock =
+                    mClock.currentTime - (mMonotonicClock.monotonicTime() - startTime);
+            long endTimeWallClock =
+                    mClock.currentTime - (mMonotonicClock.monotonicTime() - endTime);
+
+            assertThat(Instant.ofEpochMilli(startTimeWallClock))
+                    .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+            assertThat(Instant.ofEpochMilli(endTimeWallClock))
+                    .isEqualTo(Instant.parse("2023-01-02T04:30:00Z"));
+
+            consumer.accept(
+                    createAggregatedPowerStats(startTime, startTimeWallClock, endTime - startTime));
+            return null;
+        }).when(mPowerStatsAggregator).aggregatePowerStats(any(BatteryStatsHistory.class),
+                anyLong(), anyLong(), any(Consumer.class));
+
+        mPowerStatsScheduler.start(/*enabled*/ true);
+        ConditionVariable done = new ConditionVariable();
+        mHandler.post(done::open);
+        done.block();
+
+        assertThat(mPowerStatsCollectionOccurred).isTrue();
+        assertThat(mScheduledAlarms).containsExactly(
+                START_REALTIME + TimeUnit.MINUTES.toMillis(90) + TimeUnit.HOURS.toMillis(1));
+
+        verify(mPowerStatsAggregator, times(2)).aggregatePowerStats(
+                any(BatteryStatsHistory.class), anyLong(), anyLong(), any(Consumer.class));
+
+        List<PowerStatsSpan.Metadata> contents = mPowerStatsStore.getTableOfContents();
+        assertThat(contents).hasSize(3);
+        // Skip the first entry, which was placed in the store at the beginning of this test
+        PowerStatsSpan.TimeFrame timeFrame1 = contents.get(1).getTimeFrames().get(0);
+        PowerStatsSpan.TimeFrame timeFrame2 = contents.get(2).getTimeFrames().get(0);
+        assertThat(timeFrame1.startMonotonicTime).isEqualTo(START_REALTIME + 123);
+        assertThat(timeFrame2.startMonotonicTime)
+                .isEqualTo(timeFrame1.startMonotonicTime + timeFrame1.duration);
+        assertThat(Instant.ofEpochMilli(timeFrame2.startTime))
+                .isEqualTo(Instant.parse("2023-01-02T04:00:00Z"));
+        assertThat(Duration.ofMillis(timeFrame2.duration)).isEqualTo(Duration.ofMinutes(30));
+    }
+
+    private AggregatedPowerStats createAggregatedPowerStats(long monotonicTime, long currentTime,
+            long duration) {
+        AggregatedPowerStats stats = new AggregatedPowerStats(new AggregatedPowerStatsConfig());
+        stats.addClockUpdate(monotonicTime, currentTime);
+        stats.setDuration(duration);
+        return stats;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
new file mode 100644
index 0000000..a232c0c
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.BatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MultiStateStatsTest {
+    public static final int DIMENSION_COUNT = 2;
+
+    @Test
+    public void compositeStateIndex_allEnabled() {
+        MultiStateStats.Factory factory = makeFactory(true, true, true);
+        assertThatCpuPerformanceStatsFactory(factory)
+                .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 4)
+                .haveDifferentSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+    }
+
+    @Test
+    public void compositeStateIndex_procStateTrackingDisabled() {
+        MultiStateStats.Factory factory = makeFactory(true, false, true);
+        assertThatCpuPerformanceStatsFactory(factory)
+                .hasSerialStateCount(4)
+                .haveDifferentSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
+                .haveSameSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .haveSameSerialStates(
+                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .haveSameSerialStates(
+                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .haveSameSerialStates(
+                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+    }
+
+    @Test
+    public void compositeStateIndex_screenTrackingDisabled() {
+        MultiStateStats.Factory factory = makeFactory(true, true, false);
+        assertThatCpuPerformanceStatsFactory(factory)
+                .hasSerialStateCount(BatteryConsumer.PROCESS_STATE_COUNT * 2)
+                .haveDifferentSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .haveSameSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND))
+                .haveSameSerialStates(
+                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+    }
+
+    @Test
+    public void compositeStateIndex_allDisabled() {
+        MultiStateStats.Factory factory = makeFactory(false, false, false);
+        assertThatCpuPerformanceStatsFactory(factory)
+                .hasSerialStateCount(1)
+                .haveSameSerialStates(
+                        state(false, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(false, true, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, false, BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        state(true, true, BatteryConsumer.PROCESS_STATE_BACKGROUND));
+    }
+
+    @Test
+    public void tooManyStates() {
+        // 4 bits needed to represent
+        String[] labels = {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"};
+        // 4 * 10 = 40 bits needed to represent the composite state
+        MultiStateStats.States[] states = new MultiStateStats.States[10];
+        for (int i = 0; i < states.length; i++) {
+            states[i] = new MultiStateStats.States("foo", true, labels);
+        }
+        IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+                () -> new MultiStateStats.Factory(DIMENSION_COUNT, states));
+        assertThat(e.getMessage()).contains("40");
+    }
+
+    @Test
+    public void multiStateStats_aggregation() {
+        MultiStateStats.Factory factory = makeFactory(true, true, false);
+        MultiStateStats multiStateStats = factory.create();
+        multiStateStats.setState(0 /* batteryState */, 1 /* on */, 1000);
+        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
+        multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
+
+        multiStateStats.increment(new long[]{100, 200}, 1000);
+
+        multiStateStats.setState(0 /* batteryState */, 0 /* off */, 2000);
+        multiStateStats.setState(2 /* screenState */, 1 /* on */, 2000); // untracked
+
+        multiStateStats.increment(new long[]{300, 500}, 3000);
+
+        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 4000);
+
+        multiStateStats.increment(new long[]{200, 200}, 5000);
+
+        long[] stats = new long[DIMENSION_COUNT];
+        multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
+        // (400 - 100) * 0.5 + (600 - 400) * 0.5
+        assertThat(stats).isEqualTo(new long[]{250, 350});
+
+        multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_FOREGROUND, 0});
+        // (400 - 100) * 0.5 + (600 - 400) * 0
+        assertThat(stats).isEqualTo(new long[]{150, 250});
+
+        // Note that screen state does not affect the result, as it is untracked
+        multiStateStats.getStats(stats, new int[]{0, BatteryConsumer.PROCESS_STATE_BACKGROUND, 1});
+        // (400 - 100) * 0 + (600 - 400) * 0.5
+        assertThat(stats).isEqualTo(new long[]{100, 100});
+
+        multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0});
+        // Never been in this composite state
+        assertThat(stats).isEqualTo(new long[]{0, 0});
+    }
+
+    @Test
+    public void test_toString() {
+        MultiStateStats.Factory factory = makeFactory(true, true, false);
+        MultiStateStats multiStateStats = factory.create();
+        multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000);
+        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_FOREGROUND, 1000);
+        multiStateStats.setState(2 /* screenState */, 0 /* off */, 1000);
+        multiStateStats.setState(0 /* batteryState */, 1 /* on */, 2000);
+        multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000);
+        multiStateStats.increment(new long[]{100, 200}, 5000);
+
+        assertThat(multiStateStats.toString()).isEqualTo(
+                "(plugged-in fg) [25, 50]\n"
+                + "(on-battery fg) [25, 50]\n"
+                + "(on-battery bg) [50, 100]"
+        );
+    }
+
+    private static MultiStateStats.Factory makeFactory(boolean trackBatteryState,
+            boolean trackProcState, boolean trackScreenState) {
+        return new MultiStateStats.Factory(DIMENSION_COUNT,
+                new MultiStateStats.States("bs", trackBatteryState, "plugged-in", "on-battery"),
+                new MultiStateStats.States("ps", trackProcState,
+                        BatteryConsumer.processStateToString(
+                                BatteryConsumer.PROCESS_STATE_UNSPECIFIED),
+                        BatteryConsumer.processStateToString(
+                                BatteryConsumer.PROCESS_STATE_FOREGROUND),
+                        BatteryConsumer.processStateToString(
+                                BatteryConsumer.PROCESS_STATE_BACKGROUND),
+                        BatteryConsumer.processStateToString(
+                                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE),
+                        BatteryConsumer.processStateToString(
+                                BatteryConsumer.PROCESS_STATE_CACHED)),
+                new MultiStateStats.States("scr", trackScreenState, "screen-off", "plugged-in"));
+    }
+
+    private FactorySubject assertThatCpuPerformanceStatsFactory(MultiStateStats.Factory factory) {
+        FactorySubject subject = new FactorySubject();
+        subject.mFactory = factory;
+        return subject;
+    }
+
+    private static class FactorySubject {
+        private MultiStateStats.Factory mFactory;
+
+        FactorySubject hasSerialStateCount(int stateCount) {
+            assertThat(mFactory.getSerialStateCount()).isEqualTo(stateCount);
+            return this;
+        }
+
+        public FactorySubject haveDifferentSerialStates(State... states) {
+            int[] serialStates = getSerialStates(states);
+            assertWithMessage("Expected all to be different: " + Arrays.toString(serialStates))
+                    .that(Arrays.stream(serialStates).distinct().toArray())
+                    .hasLength(states.length);
+            return this;
+        }
+
+        public FactorySubject haveSameSerialStates(State... states) {
+            int[] serialStates = getSerialStates(states);
+            assertWithMessage("Expected all to be the same: " + Arrays.toString(serialStates))
+                    .that(Arrays.stream(serialStates).distinct().toArray())
+                    .hasLength(1);
+            return this;
+        }
+
+        private int[] getSerialStates(State[] states) {
+            int[] serialStates = new int[states.length];
+            for (int i = 0; i < states.length; i++) {
+                serialStates[i] = mFactory.getSerialState(
+                        new int[]{
+                                states[i].batteryState ? 0 : 1,
+                                states[i].procstate,
+                                states[i].screenState ? 0 : 1
+                        });
+            }
+            return serialStates;
+        }
+    }
+
+    private State state(boolean batteryState, boolean screenState, int procstate) {
+        State state = new State();
+        state.batteryState = batteryState;
+        state.screenState = screenState;
+        state.procstate = procstate;
+        return state;
+    }
+
+    private static class State {
+        public boolean batteryState;
+        public boolean screenState;
+        public int procstate;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
new file mode 100644
index 0000000..535f2da
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -0,0 +1,231 @@
+/*
+ * 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.processor;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.OutcomeReceiver;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.os.Clock;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MobileRadioPowerStatsCollector;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.PowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+public class PhoneCallPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private static final double PRECISION = 0.00001;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
+    @Mock
+    private Context mContext;
+    @Mock
+    private PowerStatsUidResolver mPowerStatsUidResolver;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private Supplier<NetworkStats> mNetworkStatsSupplier;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private LongSupplier mCallDurationSupplier;
+    @Mock
+    private LongSupplier mScanDurationSupplier;
+
+    private final MobileRadioPowerStatsCollector.Injector mInjector =
+            new MobileRadioPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mPowerStatsUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PackageManager getPackageManager() {
+                    return mPackageManager;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
+                    return mNetworkStatsSupplier;
+                }
+
+                @Override
+                public TelephonyManager getTelephonyManager() {
+                    return mTelephonyManager;
+                }
+
+                @Override
+                public LongSupplier getCallDurationSupplier() {
+                    return mCallDurationSupplier;
+                }
+
+                @Override
+                public LongSupplier getPhoneSignalScanDurationSupplier() {
+                    return mScanDurationSupplier;
+                }
+            };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true);
+        when(mPowerStatsUidResolver.mapUid(anyInt()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+                .thenReturn(new int[0]);
+
+        mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
+    }
+
+    @Test
+    public void copyEstimatesFromMobileRadioPowerStats() {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile()));
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_PHONE,
+                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+                .setProcessorSupplier(PhoneCallPowerStatsProcessor::new);
+
+        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
+        PowerComponentAggregatedPowerStats mobileRadioStats =
+                aggregatedPowerStats.getPowerComponentStats(
+                        BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO);
+
+        aggregatedPowerStats.start(0);
+
+        aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+
+        MobileRadioPowerStatsCollector collector =
+                new MobileRadioPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        // Initial empty ModemActivityInfo.
+        mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+        // Establish a baseline
+        aggregatedPowerStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+
+        ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+                new int[]{100, 200, 300, 400, 500}, 600);
+        mockModemActivityInfo(mai);
+
+        // A phone call was made
+        when(mCallDurationSupplier.getAsLong()).thenReturn(7000L);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        aggregatedPowerStats.addPowerStats(collector.collectStats(), 10_000);
+
+        mobileRadioStats.finish(10_000);
+
+        PowerComponentAggregatedPowerStats stats =
+                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_PHONE);
+        stats.finish(10_000);
+
+        PowerStatsLayout statsLayout = new PowerStatsLayout(stats.getPowerStatsDescriptor());
+
+        long[] deviceStats = new long[stats.getPowerStatsDescriptor().statsArrayLength];
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(0.7);
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(2.1);
+    }
+
+    private void mockModemActivityInfo(ModemActivityInfo emptyMai) {
+        doAnswer(invocation -> {
+            OutcomeReceiver<ModemActivityInfo, TelephonyManager.ModemActivityInfoException>
+                    receiver = invocation.getArgument(1);
+            receiver.onResult(emptyMai);
+            return null;
+        }).when(mTelephonyManager).requestModemActivityInfo(any(), any());
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
new file mode 100644
index 0000000..f312bed
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.PersistableBundle;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.MockClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerStatsAggregatorTest {
+    private static final int TEST_POWER_COMPONENT = 77;
+    private static final int TEST_UID = 42;
+    private static final long START_TIME = 1234;
+
+    private final MockClock mClock = new MockClock();
+    private final MonotonicClock mMonotonicClock = new MonotonicClock(START_TIME, mClock);
+    private BatteryStatsHistory mHistory;
+    private com.android.server.power.stats.processor.PowerStatsAggregator mAggregator;
+    private int mAggregatedStatsCount;
+
+    @Before
+    public void setup() throws ParseException {
+        mHistory = new BatteryStatsHistory(null, null, 0, 1024,
+                mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+                mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class), null);
+
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(TEST_POWER_COMPONENT)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+        mAggregator = new PowerStatsAggregator(config);
+    }
+
+    @Test
+    public void stateUpdates() {
+        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+                "majorDrain", 1, null, 0, 1, new PersistableBundle());
+        PowerStats powerStats = new PowerStats(descriptor);
+
+        mClock.currentTime = 1222156800000L;    // An important date in world history
+
+        mHistory.forceRecordAllHistory();
+        powerStats.stats = new long[]{0};
+        powerStats.uidStats.put(TEST_UID, new long[]{0});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
+        mHistory.recordStateStartEvent(mClock.realtime, mClock.uptime,
+                BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+        advance(1000);
+
+        powerStats.stats = new long[]{10000};
+        powerStats.uidStats.put(TEST_UID, new long[]{1234});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
+        mHistory.recordStateStopEvent(mClock.realtime, mClock.uptime,
+                BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+
+        advance(1000);
+
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+        advance(1000);
+
+        mClock.currentTime += 60 * 60 * 1000;       // one hour
+        mHistory.recordCurrentTimeChange(mClock.realtime, mClock.uptime, mClock.currentTime);
+
+        advance(2000);
+
+        powerStats.stats = new long[]{20000};
+        powerStats.uidStats.put(TEST_UID, new long[]{4444});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        mAggregator.aggregatePowerStats(mHistory, 0, MonotonicClock.UNDEFINED, stats -> {
+            assertThat(mAggregatedStatsCount++).isEqualTo(0);
+            assertThat(stats.getStartTime()).isEqualTo(START_TIME);
+
+            List<AggregatedPowerStats.ClockUpdate> clockUpdates = stats.getClockUpdates();
+            assertThat(clockUpdates).hasSize(2);
+
+            AggregatedPowerStats.ClockUpdate clockUpdate0 = clockUpdates.get(0);
+            assertThat(clockUpdate0.monotonicTime).isEqualTo(1234);
+            assertThat(formatDateTime(clockUpdate0.currentTime)).isEqualTo("2008-09-23 08:00:00");
+
+            AggregatedPowerStats.ClockUpdate clockUpdate1 = clockUpdates.get(1);
+            assertThat(clockUpdate1.monotonicTime).isEqualTo(1234 + 3000);
+            assertThat(formatDateTime(clockUpdate1.currentTime)).isEqualTo("2008-09-23 09:00:03");
+
+            assertThat(stats.getDuration()).isEqualTo(5000);
+
+            long[] values = new long[1];
+
+            PowerComponentAggregatedPowerStats
+                    powerComponentStats = stats.getPowerComponentStats(
+                    TEST_POWER_COMPONENT);
+
+            assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+                    AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                    AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
+                    .isTrue();
+            assertThat(values).isEqualTo(new long[]{10000});
+
+            assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER}))
+                    .isTrue();
+            assertThat(values).isEqualTo(new long[]{20000});
+
+            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+                    AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                    AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                    BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+                    .isTrue();
+            assertThat(values).isEqualTo(new long[]{1234});
+
+            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                    BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+                    .isTrue();
+            assertThat(values).isEqualTo(new long[]{1111});
+
+            assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+                    AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                    AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                    BatteryConsumer.PROCESS_STATE_BACKGROUND}))
+                    .isTrue();
+            assertThat(values).isEqualTo(new long[]{3333});
+        });
+    }
+
+    @NonNull
+    private static String formatDateTime(long timeInMillis) {
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+        format.getCalendar().setTimeZone(TimeZone.getTimeZone("GMT"));
+        return format.format(new Date(timeInMillis));
+    }
+
+    @Test
+    public void incompatiblePowerStats() {
+        PowerStats.Descriptor descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT,
+                "majorDrain", 1, null, 0, 1, new PersistableBundle());
+        PowerStats powerStats = new PowerStats(descriptor);
+
+        mHistory.forceRecordAllHistory();
+        powerStats.stats = new long[]{0};
+        powerStats.uidStats.put(TEST_UID, new long[]{0});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 10, /* plugged */ true);
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, TEST_UID,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+        advance(1000);
+
+        powerStats.stats = new long[]{10000};
+        powerStats.uidStats.put(TEST_UID, new long[]{1234});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 90, /* plugged */ false);
+
+        advance(1000);
+
+        descriptor = new PowerStats.Descriptor(TEST_POWER_COMPONENT, "majorDrain", 1, null, 0, 1,
+                PersistableBundle.forPair("something", "changed"));
+        powerStats = new PowerStats(descriptor);
+        powerStats.stats = new long[]{20000};
+        powerStats.uidStats.put(TEST_UID, new long[]{4444});
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
+
+        advance(1000);
+
+        mHistory.recordBatteryState(mClock.realtime, mClock.uptime, 50, /* plugged */ true);
+
+        mAggregator.aggregatePowerStats(mHistory, 0, MonotonicClock.UNDEFINED, stats -> {
+            long[] values = new long[1];
+
+            PowerComponentAggregatedPowerStats powerComponentStats =
+                    stats.getPowerComponentStats(TEST_POWER_COMPONENT);
+
+            if (mAggregatedStatsCount == 0) {
+                assertThat(stats.getStartTime()).isEqualTo(START_TIME);
+                assertThat(stats.getDuration()).isEqualTo(2000);
+
+                assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+                        AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                        AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
+                        .isTrue();
+                assertThat(values).isEqualTo(new long[]{10000});
+                assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+                        AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                        AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                        BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+                        .isTrue();
+                assertThat(values).isEqualTo(new long[]{1234});
+            } else if (mAggregatedStatsCount == 1) {
+                assertThat(stats.getStartTime()).isEqualTo(START_TIME + 2000);
+                assertThat(stats.getDuration()).isEqualTo(1000);
+
+                assertThat(powerComponentStats.getDeviceStats(values, new int[]{
+                        AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                        AggregatedPowerStatsConfig.SCREEN_STATE_ON}))
+                        .isTrue();
+                assertThat(values).isEqualTo(new long[]{20000});
+                assertThat(powerComponentStats.getUidStats(values, TEST_UID, new int[]{
+                        AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                        AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                        BatteryConsumer.PROCESS_STATE_FOREGROUND}))
+                        .isTrue();
+                assertThat(values).isEqualTo(new long[]{4444});
+            } else {
+                fail();
+            }
+            mAggregatedStatsCount++;
+        });
+    }
+
+    private void advance(long durationMs) {
+        mClock.realtime += durationMs;
+        mClock.uptime += durationMs;
+        mClock.currentTime += durationMs;
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
new file mode 100644
index 0000000..024743d
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power.stats.processor;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.mock;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.AggregateBatteryConsumer;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.os.UidBatteryConsumer;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistory;
+import com.android.internal.os.CpuScalingPolicies;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MockClock;
+import com.android.server.power.stats.PowerStatsStore;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.format.CpuPowerStatsLayout;
+import com.android.server.power.stats.format.EnergyConsumerPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class PowerStatsExporterTest {
+
+    private static final int APP_UID1 = 42;
+    private static final int APP_UID2 = 84;
+    private static final double TOLERANCE = 0.01;
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
+            .setCpuScalingPolicy(0, new int[]{0}, new int[]{100})
+            .setAveragePowerForCpuScalingPolicy(0, 360)
+            .setAveragePowerForCpuScalingStep(0, 0, 300)
+            .setCpuPowerBracketCount(1)
+            .setCpuPowerBracket(0, 0, 0);
+
+    private MockClock mClock = new MockClock();
+    private MonotonicClock mMonotonicClock = new MonotonicClock(0, mClock);
+    private PowerStatsStore mPowerStatsStore;
+    private PowerStatsAggregator mPowerStatsAggregator;
+    private MultiStatePowerAttributor mPowerAttributor;
+    private BatteryStatsHistory mHistory;
+    private CpuPowerStatsLayout mCpuStatsArrayLayout;
+    private PowerStats.Descriptor mPowerStatsDescriptor;
+    private final EnergyConsumerPowerStatsLayout mEnergyConsumerPowerStatsLayout =
+            new EnergyConsumerPowerStatsLayout();
+
+    @Before
+    public void setup() throws IOException {
+        File storeDirectory = Files.createTempDirectory("PowerStatsExporterTest").toFile();
+        clearDirectory(storeDirectory);
+
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_CPU)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(
+                        () -> new CpuPowerStatsProcessor(mStatsRule.getPowerProfile(),
+                                mStatsRule.getCpuScalingPolicies()));
+        config.trackCustomPowerComponents(CustomEnergyConsumerPowerStatsProcessor::new)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE);
+
+        mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler());
+        mHistory = new BatteryStatsHistory(Parcel.obtain(), storeDirectory, 0, 10000,
+                mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+                mMonotonicClock, null, null);
+        mPowerStatsAggregator = new PowerStatsAggregator(config);
+
+        mCpuStatsArrayLayout = new CpuPowerStatsLayout(0, 1, new int[]{0});
+        PersistableBundle extras = new PersistableBundle();
+        mCpuStatsArrayLayout.toExtras(extras);
+
+        mPowerStatsDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU,
+                mCpuStatsArrayLayout.getDeviceStatsArrayLength(),
+                null, 0, mCpuStatsArrayLayout.getUidStatsArrayLength(), extras);
+
+        mPowerAttributor = new MultiStatePowerAttributor(mock(Context.class), mPowerStatsStore,
+                mock(PowerProfile.class), mock(CpuScalingPolicies.class),
+                mock(PowerStatsUidResolver.class));
+    }
+
+    @Test
+    public void breakdownByState_processScreenAndPower() throws Exception {
+        BatteryUsageStats actual = prepareBatteryUsageStats(true, true, true);
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                87600000);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                54321);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_ANY,
+                BatteryConsumer.PROCESS_STATE_ANY, 54321);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 54321);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 50020);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND,
+                4301);        // Includes "unspecified" proc state
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_BATTERY, 321);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_BATTERY, 20);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_BATTERY, 301);  // bg + unsp
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_BATTERY, 4000);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_BATTERY, 4000);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_OTHER, 50000);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_OTHER, 50000);
+
+        actual.close();
+    }
+
+    @Test
+    public void breakdownByState_processAndScreen() throws Exception {
+        BatteryUsageStats actual = prepareBatteryUsageStats(true, true, false);
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                7600000);       // off-battery not included
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.SCREEN_STATE_ON, BatteryConsumer.POWER_STATE_ANY,
+                600000);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.SCREEN_STATE_OTHER, BatteryConsumer.POWER_STATE_ANY,
+                7000000);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                4321);       // off-battery not included
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.SCREEN_STATE_ON, BatteryConsumer.POWER_STATE_ANY,
+                321);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.SCREEN_STATE_OTHER, BatteryConsumer.POWER_STATE_ANY,
+                4000);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_ANY,
+                BatteryConsumer.PROCESS_STATE_ANY, 4321);      // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 4321);      // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 20); // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND,
+                4301);    // includes unspecified proc state
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_ANY, 321);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_ANY, 20);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, BatteryConsumer.SCREEN_STATE_ON,
+                BatteryConsumer.POWER_STATE_ANY, 301);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_ANY, 4000);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, BatteryConsumer.SCREEN_STATE_OTHER,
+                BatteryConsumer.POWER_STATE_ANY, 4000);
+
+        actual.close();
+    }
+
+    @Test
+    public void breakdownByState_processStateOnly() throws Exception {
+        BatteryUsageStats actual = prepareBatteryUsageStats(true, false, false);
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                7600000);        // off-battery not included
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU,
+                4321);      // off-battery not included
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_ANY,
+                BatteryConsumer.PROCESS_STATE_ANY, 4321);           // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 4321);           // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 20);      // off-battery not included
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND,
+                4301);    // includes unspecified proc state
+
+        actual.close();
+    }
+
+    private @NonNull BatteryUsageStats prepareBatteryUsageStats(boolean includeProcessStateData,
+            boolean includeScreenStateData, boolean includesPowerStateData) {
+        long[] deviceStats = new long[mCpuStatsArrayLayout.getDeviceStatsArrayLength()];
+        long[] uidStats = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+
+        AggregatedPowerStats aps = new AggregatedPowerStats(mPowerStatsAggregator.getConfig());
+        PowerComponentAggregatedPowerStats stats = aps.getPowerComponentStats(
+                BatteryConsumer.POWER_COMPONENT_CPU);
+        stats.setPowerStatsDescriptor(mPowerStatsDescriptor);
+
+        mCpuStatsArrayLayout.setUidPowerEstimate(uidStats, 1);
+        stats.setUidStats(APP_UID1, new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED}, uidStats);
+
+        mCpuStatsArrayLayout.setUidPowerEstimate(uidStats, 20);
+        stats.setUidStats(APP_UID1, new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND}, uidStats);
+
+        mCpuStatsArrayLayout.setUidPowerEstimate(uidStats, 300);
+        stats.setUidStats(APP_UID1, new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND}, uidStats);
+
+        mCpuStatsArrayLayout.setUidPowerEstimate(uidStats, 4000);
+        stats.setUidStats(APP_UID1, new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND}, uidStats);
+
+        mCpuStatsArrayLayout.setUidPowerEstimate(uidStats, 50000);
+        stats.setUidStats(APP_UID1, new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND}, uidStats);
+
+        mCpuStatsArrayLayout.setDevicePowerEstimate(deviceStats, 600000);
+        stats.setDeviceStats(new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON}, deviceStats);
+
+        mCpuStatsArrayLayout.setDevicePowerEstimate(deviceStats, 7000000);
+        stats.setDeviceStats(new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_BATTERY,
+                AggregatedPowerStatsConfig.SCREEN_STATE_OTHER}, deviceStats);
+
+        mCpuStatsArrayLayout.setDevicePowerEstimate(deviceStats, 80000000);
+        stats.setDeviceStats(new int[]{
+                AggregatedPowerStatsConfig.POWER_STATE_OTHER,
+                AggregatedPowerStatsConfig.SCREEN_STATE_ON}, deviceStats);
+
+        return exportToBatteryUsageStats(aps, includeProcessStateData,
+                includeScreenStateData, includesPowerStateData);
+    }
+
+    private @NonNull BatteryUsageStats exportToBatteryUsageStats(
+            AggregatedPowerStats aps,
+            boolean includeProcessStateData, boolean includeScreenStateData,
+            boolean includesPowerStateData) {
+        PowerStatsExporter
+                exporter = new PowerStatsExporter(mPowerStatsStore,
+                mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
+
+        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0], false,
+                includeProcessStateData, includeScreenStateData, includesPowerStateData, 0);
+        exporter.populateBatteryUsageStatsBuilder(builder, aps);
+        return builder.build();
+    }
+
+    @Test
+    public void breakdownByProcState_fullRange() throws Exception {
+        breakdownByProcState_fullRange(false, false);
+    }
+
+    @Test
+    public void breakdownByProcStateScreenAndPower_fullRange() throws Exception {
+        breakdownByProcState_fullRange(true, true);
+    }
+
+    private void breakdownByProcState_fullRange(boolean includeScreenStateData,
+            boolean includePowerStateData) throws Exception {
+        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+                new String[]{"cu570m"}, /* includePowerModels */ false,
+                /* includeProcessStateData */ true, includeScreenStateData,
+                includePowerStateData, /* powerThreshold */ 0);
+        exportAggregatedPowerStats(builder, 1000, 10000);
+
+        BatteryUsageStats actual = builder.build();
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 3.60);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 0.360);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 2.198082);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, 1.772916);
+        assertUidPowerEstimate(message, actual, APP_UID1,
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                BatteryConsumer.PROCESS_STATE_ANY, 0.360);
+
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.538999);
+        assertUidPowerEstimate(message, actual, APP_UID2,
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                BatteryConsumer.PROCESS_STATE_ANY, 0);
+
+        actual.close();
+    }
+
+    @Test
+    public void breakdownByProcState_subRange() throws Exception {
+        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+                new String[]{"cu570m"}, /* includePowerModels */ false,
+                /* includeProcessStateData */ true, true, true, /* powerThreshold */ 0);
+        exportAggregatedPowerStats(builder, 3700, 6700);
+
+        BatteryUsageStats actual = builder.build();
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU, 4.526749);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 1.193332);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND, 0.397749);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND, 0.795583);
+
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.333249);
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE, 3.333249);
+
+        actual.close();
+    }
+
+    @Test
+    public void combinedProcessStates() throws Exception {
+        BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+                new String[]{"cu570m"}, /* includePowerModels */ false,
+                /* includeProcessStateData */ false, true, true, /* powerThreshold */ 0);
+        exportAggregatedPowerStats(builder, 1000, 10000);
+
+        BatteryUsageStats actual = builder.build();
+        String message = "Actual BatteryUsageStats: " + actual;
+
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+        assertAggregatedPowerEstimate(message, actual,
+                BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+                BatteryConsumer.POWER_COMPONENT_CPU, 7.51016);
+
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_ANY,
+                BatteryConsumer.PROCESS_STATE_ANY, 4.33);
+        assertUidPowerEstimate(message, actual, APP_UID1, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.97099);
+        assertUidPowerEstimate(message, actual, APP_UID1,
+                BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                BatteryConsumer.PROCESS_STATE_ANY, 0.360);
+
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_ANY,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
+        assertUidPowerEstimate(message, actual, APP_UID2, BatteryConsumer.POWER_COMPONENT_CPU,
+                BatteryConsumer.PROCESS_STATE_ANY, 3.538999);
+        UidBatteryConsumer uidScope = actual.getUidBatteryConsumers().stream()
+                .filter(us -> us.getUid() == APP_UID1).findFirst().orElse(null);
+        // There shouldn't be any per-procstate data
+        for (int procState = 0; procState < BatteryConsumer.PROCESS_STATE_COUNT; procState++) {
+            if (procState == BatteryConsumer.PROCESS_STATE_UNSPECIFIED) {
+                continue;
+            }
+            double power = uidScope.getConsumedPower(
+                    new BatteryConsumer.Dimensions(BatteryConsumer.POWER_COMPONENT_CPU, procState));
+            assertWithMessage("procState=" + procState).that(power).isEqualTo(0);
+        }
+        actual.close();
+    }
+
+    private void recordBatteryHistory() {
+        PowerStats powerStats = new PowerStats(mPowerStatsDescriptor);
+        long[] uidStats1 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+        powerStats.uidStats.put(APP_UID1, uidStats1);
+        long[] uidStats2 = new long[mCpuStatsArrayLayout.getUidStatsArrayLength()];
+        powerStats.uidStats.put(APP_UID2, uidStats2);
+
+        PowerStats customPowerStats = new PowerStats(
+                new PowerStats.Descriptor(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID,
+                        "cu570m", mEnergyConsumerPowerStatsLayout.getDeviceStatsArrayLength(),
+                        null, 0, mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength(),
+                        new PersistableBundle()));
+        long[] customUidStats = new long[mEnergyConsumerPowerStatsLayout.getUidStatsArrayLength()];
+        customPowerStats.uidStats.put(APP_UID1, customUidStats);
+
+        mHistory.forceRecordAllHistory();
+
+        mHistory.startRecordingHistory(1000, 1000, false);
+        mHistory.recordPowerStats(1000, 1000, powerStats);
+        mHistory.recordPowerStats(1000, 1000, customPowerStats);
+        mHistory.recordBatteryState(1000, 1000, 70, /* plugged */ false);
+        mHistory.recordStateStartEvent(1000, 1000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+        mHistory.recordProcessStateChange(1000, 1000, APP_UID1,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        mHistory.recordProcessStateChange(1000, 1000, APP_UID2,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
+
+        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 11111);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 10000);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 1111);
+        mHistory.recordPowerStats(1000, 1000, powerStats);
+
+        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 12345);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 9876);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 2469);
+        mHistory.recordPowerStats(3000, 3000, powerStats);
+
+        mPowerStatsAggregator.aggregatePowerStats(mHistory, 0, 3500,
+                stats -> mPowerAttributor.storeAggregatedPowerStats(stats));
+
+        mHistory.recordProcessStateChange(4000, 4000, APP_UID1,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+        mHistory.recordStateStopEvent(4000, 4000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+
+        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 54321);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 14321);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 40000);
+        mHistory.recordPowerStats(6000, 6000, powerStats);
+
+        mEnergyConsumerPowerStatsLayout.setConsumedEnergy(customPowerStats.stats, 0, 3_600_000);
+        mEnergyConsumerPowerStatsLayout.setUidConsumedEnergy(customUidStats, 0, 360_000);
+        mHistory.recordPowerStats(6010, 6010, customPowerStats);
+
+        mPowerStatsAggregator.aggregatePowerStats(mHistory, 3500, 6500,
+                stats -> mPowerAttributor.storeAggregatedPowerStats(stats));
+
+        mHistory.recordStateStartEvent(7000, 7000, BatteryStats.HistoryItem.STATE_SCREEN_ON_FLAG);
+        mHistory.recordProcessStateChange(7000, 7000, APP_UID1,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        mHistory.recordProcessStateChange(7000, 7000, APP_UID2,
+                BatteryConsumer.PROCESS_STATE_UNSPECIFIED);
+
+        mCpuStatsArrayLayout.setTimeByScalingStep(powerStats.stats, 0, 23456);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats1, 0, 23456);
+        mCpuStatsArrayLayout.setUidTimeByPowerBracket(uidStats2, 0, 0);
+        mHistory.recordPowerStats(8000, 8000, powerStats);
+
+        assertThat(mPowerStatsStore.getTableOfContents()).hasSize(2);
+    }
+
+    private void exportAggregatedPowerStats(BatteryUsageStats.Builder builder,
+            int monotonicStartTime, int monotonicEndTime) {
+        recordBatteryHistory();
+        PowerStatsExporter exporter = new PowerStatsExporter(mPowerStatsStore,
+                mPowerStatsAggregator, /* batterySessionTimeSpanSlackMillis */ 0);
+        exporter.exportAggregatedPowerStats(builder, mHistory, monotonicStartTime,
+                monotonicEndTime);
+    }
+
+    private void assertAggregatedPowerEstimate(String message, BatteryUsageStats bus, int scope,
+            int componentId, double expected) {
+        AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(scope);
+        double actual = consumer.getConsumedPower(componentId);
+        assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
+    }
+
+    private void assertAggregatedPowerEstimate(String message, BatteryUsageStats bus, int scope,
+            int componentId, int screenState, int powerState, double expected) {
+        AggregateBatteryConsumer consumer = bus.getAggregateBatteryConsumer(scope);
+        double actual = consumer.getConsumedPower(
+                new BatteryConsumer.Dimensions(componentId, BatteryConsumer.PROCESS_STATE_ANY,
+                        screenState, powerState));
+        assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
+    }
+
+    private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
+            int componentId, int processState, double expected) {
+        assertUidPowerEstimate(message, bus, uid, componentId, processState,
+                BatteryConsumer.SCREEN_STATE_ANY, BatteryConsumer.POWER_STATE_ANY,
+                expected);
+    }
+
+    private void assertUidPowerEstimate(String message, BatteryUsageStats bus, int uid,
+            int componentId, int processState, int screenState, int powerState, double expected) {
+        List<UidBatteryConsumer> uidScopes = bus.getUidBatteryConsumers();
+        final UidBatteryConsumer uidScope = uidScopes.stream()
+                .filter(us -> us.getUid() == uid).findFirst().orElse(null);
+        assertWithMessage(message).that(uidScope).isNotNull();
+        double actual = uidScope.getConsumedPower(
+                new BatteryConsumer.Dimensions(componentId, processState, screenState, powerState));
+        assertWithMessage(message).that(actual).isWithin(TOLERANCE).of(expected);
+    }
+
+    private void clearDirectory(File dir) {
+        if (dir.exists()) {
+            for (File child : dir.listFiles()) {
+                if (child.isDirectory()) {
+                    clearDirectory(child);
+                }
+                child.delete();
+            }
+        }
+    }
+
+    private static class TestHandler extends Handler {
+        TestHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            msg.getCallback().run();
+            return true;
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java
new file mode 100644
index 0000000..13e0d9d
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsProcessorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats.processor;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.BatteryConsumer;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerStatsProcessorTest {
+
+    @Test
+    public void createPowerEstimationPlan_allDeviceStatesPresentInUidStates() {
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE);
+
+        PowerStatsProcessor.PowerEstimationPlan plan =
+                new PowerStatsProcessor.PowerEstimationPlan(config);
+        assertThat(deviceStateEstimatesToStrings(plan))
+                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+        assertThat(combinedDeviceStatsToStrings(plan))
+                .containsExactly("[[0, 0]]", "[[0, 1]]", "[[1, 0]]", "[[1, 1]]");
+        assertThat(uidStateEstimatesToStrings(plan, config))
+                .containsExactly(
+                        "[[0, 0]]: [ps]: [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4]]",
+                        "[[0, 1]]: [ps]: [[0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1, 3], [0, 1, 4]]",
+                        "[[1, 0]]: [ps]: [[1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 0, 3], [1, 0, 4]]",
+                        "[[1, 1]]: [ps]: [[1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 1, 3], [1, 1, 4]]");
+    }
+
+    @Test
+    public void createPowerEstimationPlan_combineDeviceStats() {
+        AggregatedPowerStatsConfig.PowerComponent config =
+                new AggregatedPowerStatsConfig.PowerComponent(BatteryConsumer.POWER_COMPONENT_ANY)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_PROCESS_STATE);
+
+        PowerStatsProcessor.PowerEstimationPlan plan =
+                new PowerStatsProcessor.PowerEstimationPlan(config);
+
+        assertThat(deviceStateEstimatesToStrings(plan))
+                .containsExactly("[0, 0]", "[0, 1]", "[1, 0]", "[1, 1]");
+        assertThat(combinedDeviceStatsToStrings(plan))
+                .containsExactly(
+                        "[[0, 0], [0, 1]]",
+                        "[[1, 0], [1, 1]]");
+        assertThat(uidStateEstimatesToStrings(plan, config))
+                .containsExactly(
+                        "[[0, 0], [0, 1]]: [ps]: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4]]",
+                        "[[1, 0], [1, 1]]: [ps]: [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]");
+    }
+
+    private static List<String> deviceStateEstimatesToStrings(
+            PowerStatsProcessor.PowerEstimationPlan plan) {
+        return plan.deviceStateEstimations.stream()
+                .map(dse -> dse.stateValues).map(Arrays::toString).toList();
+    }
+
+    private static List<String> combinedDeviceStatsToStrings(
+            PowerStatsProcessor.PowerEstimationPlan plan) {
+        return plan.combinedDeviceStateEstimations.stream()
+                .map(cds -> cds.deviceStateEstimations)
+                .map(dses -> dses.stream()
+                        .map(dse -> dse.stateValues).map(Arrays::toString).toList())
+                .map(Object::toString)
+                .toList();
+    }
+
+    private static List<String> uidStateEstimatesToStrings(
+            PowerStatsProcessor.PowerEstimationPlan plan,
+            AggregatedPowerStatsConfig.PowerComponent config) {
+        MultiStateStats.States[] uidStateConfig = config.getUidStateConfig();
+        return plan.uidStateEstimates.stream()
+                .map(use ->
+                        use.combinedDeviceStateEstimate.deviceStateEstimations.stream()
+                                .map(dse -> dse.stateValues).map(Arrays::toString).toList()
+                        + ": "
+                        + Arrays.stream(use.states)
+                                .filter(Objects::nonNull)
+                                .map(MultiStateStats.States::getName).toList()
+                        + ": "
+                        + use.proportionalEstimates.stream()
+                                .map(pe -> trackedStatesToString(uidStateConfig, pe.stateValues))
+                                .toList())
+                .toList();
+    }
+
+    private static Object trackedStatesToString(MultiStateStats.States[] states,
+            int[] stateValues) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        boolean first = true;
+        for (int i = 0; i < states.length; i++) {
+            if (!states[i].isTracked()) {
+                continue;
+            }
+
+            if (!first) {
+                sb.append(", ");
+            }
+            first = false;
+            sb.append(stateValues[i]);
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
new file mode 100644
index 0000000..1852165
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.processor;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_BATTERY;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.ScreenPowerStatsCollector;
+import com.android.server.power.stats.ScreenPowerStatsCollector.Injector;
+import com.android.server.power.stats.format.ScreenPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+public class ScreenPowerStatsProcessorTest {
+
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setNumDisplays(2)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 0, 180.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_AMBIENT, 1, 360.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 0, 480.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 720.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0, 4800.0)
+            .setAveragePowerForOrdinal(PowerProfile.POWER_GROUP_DISPLAY_SCREEN_ON, 1, 7200.0)
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.1;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
+
+    private final Injector mInjector = new Injector() {
+        @Override
+        public Handler getHandler() {
+            return mStatsRule.getHandler();
+        }
+
+        @Override
+        public Clock getClock() {
+            return mStatsRule.getMockClock();
+        }
+
+        @Override
+        public PowerStatsUidResolver getUidResolver() {
+            return new PowerStatsUidResolver();
+        }
+
+        @Override
+        public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+            return 0;
+        }
+
+        @Override
+        public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+            return mConsumedEnergyRetriever;
+        }
+
+        @Override
+        public int getDisplayCount() {
+            return 2;
+        }
+
+        @Override
+        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
+            return mScreenUsageTimeRetriever;
+        }
+    };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+    }
+
+    @Test
+    public void processPowerStats_powerProfile() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(false);
+
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 195.5, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 0.6);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 97.8, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
+
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 78.2);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 39.1);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 117.3);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 58.7);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+    }
+
+    @Test
+    public void processPowerStats_energyConsumer() {
+        PowerComponentAggregatedPowerStats stats = collectAndAggregatePowerStats(true);
+
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_ON, 261.9, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0, 7.2);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_ON, 130.9, 0);
+        assertDevicePowerEstimate(stats, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0, 0);
+
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_ON, 104.8);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_ON, 52.4);
+        assertUidPowerEstimate(stats, APP_UID1, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_ON, 157.1);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_BATTERY, SCREEN_STATE_OTHER, 0);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_ON, 78.6);
+        assertUidPowerEstimate(stats, APP_UID2, POWER_STATE_OTHER, SCREEN_STATE_OTHER, 0);
+    }
+
+    private PowerComponentAggregatedPowerStats collectAndAggregatePowerStats(
+            boolean energyConsumer) {
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new ScreenPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        ScreenPowerStatsCollector collector = new ScreenPowerStatsCollector(mInjector);
+        collector.setEnabled(true);
+
+        if (energyConsumer) {
+            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                    .thenReturn(new int[]{77});
+            when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+                    .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000)});
+        } else {
+            when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.DISPLAY))
+                    .thenReturn(new int[0]);
+        }
+
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 1000);
+            callback.onUidTopActivityTime(APP_UID2, 2000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+        aggregatedStats.start(0);
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 1000);
+
+        if (energyConsumer) {
+            // 400 mAh represented as microWattSeconds
+            long energyUws = 400L * 3600 * VOLTAGE_MV;
+            when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{77}))
+                    .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(10_000 + energyUws)});
+        }
+
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(0))
+                .thenReturn(60_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_DARK))
+                .thenReturn(10_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_MEDIUM))
+                .thenReturn(20_000L);
+        when(mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(0,
+                BatteryStats.SCREEN_BRIGHTNESS_BRIGHT))
+                .thenReturn(30_000L);
+        when(mScreenUsageTimeRetriever.getScreenOnTimeMs(1))
+                .thenReturn(120_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(0))
+                .thenReturn(180_000L);
+        when(mScreenUsageTimeRetriever.getScreenDozeTimeMs(1))
+                .thenReturn(240_000L);
+        doAnswer(inv -> {
+            ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback callback =
+                    inv.getArgument(0);
+            callback.onUidTopActivityTime(APP_UID1, 3000);
+            callback.onUidTopActivityTime(APP_UID2, 5000);
+            return null;
+        }).when(mScreenUsageTimeRetriever).retrieveTopActivityTimes(any(
+                ScreenPowerStatsCollector.ScreenUsageTimeRetriever.Callback.class));
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_BATTERY, 201_000);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 601_000);
+
+        // Slightly larger than 600_000 total screen time, to simulate a sight race
+        // between state changes and power stats collection
+        aggregatedStats.addPowerStats(collector.collectStats(), 612_000);
+
+        aggregatedStats.finish(180_000);
+        return aggregatedStats;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SCREEN)
+                        .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                        .trackUidStates(STATE_POWER, STATE_SCREEN)
+                        .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_SCREEN);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+
+        return aggregatedStats;
+    }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
+    private void assertDevicePowerEstimate(
+            PowerComponentAggregatedPowerStats aggregatedStats,
+            int powerState, int screenState, double expectedScreenPowerEstimate,
+            double expectedDozePowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.statsArrayLength];
+        aggregatedStats.getDeviceStats(stats, new int[]{powerState, screenState});
+        assertThat(layout.getDevicePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedScreenPowerEstimate);
+        assertThat(layout.getScreenDozePowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedDozePowerEstimate);
+    }
+
+    private void assertUidPowerEstimate(
+            PowerComponentAggregatedPowerStats aggregatedStats, int uid,
+            int powerState, int screenState, double expectedScreenPowerEstimate) {
+        PowerStats.Descriptor descriptor = aggregatedStats.getPowerStatsDescriptor();
+        ScreenPowerStatsLayout layout = new ScreenPowerStatsLayout(descriptor);
+        long[] stats = new long[descriptor.uidStatsArrayLength];
+        aggregatedStats.getUidStats(stats, uid,
+                new int[]{powerState, screenState, BatteryConsumer.PROCESS_STATE_UNSPECIFIED});
+        assertThat(layout.getUidPowerEstimate(stats)).isWithin(PRECISION)
+                .of(expectedScreenPowerEstimate);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
new file mode 100644
index 0000000..d972604
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
@@ -0,0 +1,240 @@
+/*
+ * 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.processor;
+
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.BatteryConsumer;
+import android.os.BatteryStats;
+import android.os.Process;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerStats;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.format.SensorPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public class SensorPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .initMeasuredEnergyStatsLocked();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int SENSOR_HANDLE_1 = 77;
+    private static final int SENSOR_HANDLE_2 = 88;
+    private static final int SENSOR_HANDLE_3 = 99;
+
+    @Mock
+    private SensorManager mSensorManager;
+
+    private MonotonicClock mMonotonicClock;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
+        Sensor sensor1 = createSensor(SENSOR_HANDLE_1, Sensor.TYPE_STEP_COUNTER,
+                Sensor.STRING_TYPE_STEP_COUNTER, "dancing", 100);
+        Sensor sensor2 = createSensor(SENSOR_HANDLE_2, Sensor.TYPE_MOTION_DETECT,
+                "com.example", "tango", 200);
+        Sensor sensor3 = createSensor(SENSOR_HANDLE_3, Sensor.TYPE_MOTION_DETECT,
+                "com.example", "waltz", 300);
+        when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(
+                List.of(sensor1, sensor2, sensor3));
+    }
+
+    @Test
+    public void testPowerEstimation() {
+        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
+                () -> new SensorPowerStatsProcessor(() -> mSensorManager));
+
+        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1, SENSOR_HANDLE_1));
+
+        // Turn the screen off after 2.5 seconds
+        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
+
+        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1, SENSOR_HANDLE_1));
+        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2, SENSOR_HANDLE_1));
+        stats.noteStateChange(buildHistoryItem(8000, true, APP_UID2, SENSOR_HANDLE_2));
+        stats.noteStateChange(buildHistoryItem(9000, false, APP_UID2, SENSOR_HANDLE_1));
+
+        stats.finish(10000);
+
+        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
+        SensorPowerStatsLayout statsLayout = new SensorPowerStatsLayout(descriptor);
+
+        String dump = stats.toString();
+        assertThat(dump).contains(" step_counter: ");
+        assertThat(dump).contains(" com.example.tango: ");
+
+        long[] uidStats = new long[descriptor.uidStatsArrayLength];
+
+        // For UID1:
+        // SENSOR1 was on for 6000 ms.
+        //   Estimated power: 6000 * 100 = 0.167 mAh
+        //     split between three different states
+        //          fg screen-on: 6000 * 2500/10000
+        //          bg screen-off: 6000 * 2500/10000
+        //          fgs screen-off: 6000 * 5000/10000
+        double expectedPower1 = 0.166666;
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 5000 / 10000);
+
+        // For UID2:
+        // SENSOR1 was on for 2000 ms.
+        //   Estimated power: 2000 * 100 = 0.0556 mAh
+        //     split between three different states
+        //          cached screen-on: 2000 * 2500/10000
+        //          cached screen-off: 2000 * 7500/10000
+        // SENSOR2 was on for 2000 ms.
+        //   Estimated power: 2000 * 200 = 0.11111 mAh
+        //     split between three different states
+        //          cached screen-on: 2000 * 2500/10000
+        //          cached screen-off: 2000 * 7500/10000
+        double expectedPower2 = 0.05555 + 0.11111;
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 2500 / 10000);
+        stats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 7500 / 10000);
+
+        long[] deviceStats = new long[descriptor.statsArrayLength];
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 2500 / 10000);
+
+        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of((expectedPower1 + expectedPower2) * 7500 / 10000);
+    }
+
+    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
+            int uid, int sensor) {
+        mStatsRule.setTime(timestamp, timestamp);
+        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
+        historyItem.time = mMonotonicClock.monotonicTime();
+        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_SENSOR_ON_FLAG : 0;
+        if (stateOn) {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
+        } else {
+            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
+        }
+        historyItem.eventTag = historyItem.localEventTag;
+        historyItem.eventTag.uid = uid;
+        historyItem.eventTag.string = "sensor:0x" + Integer.toHexString(sensor);
+        return historyItem;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_SENSORS)
+                .trackDeviceStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN)
+                .trackUidStates(
+                        AggregatedPowerStatsConfig.STATE_POWER,
+                        AggregatedPowerStatsConfig.STATE_SCREEN,
+                        AggregatedPowerStatsConfig.STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats powerComponentStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_SENSORS);
+        powerComponentStats.start(0);
+
+        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return powerComponentStats;
+    }
+
+    private Sensor createSensor(int handle, int type, String stringType, String name, float power) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            Sensor sensor = mock(Sensor.class);
+            when(sensor.getHandle()).thenReturn(handle);
+            when(sensor.getType()).thenReturn(type);
+            when(sensor.getStringType()).thenReturn(stringType);
+            when(sensor.getName()).thenReturn(name);
+            when(sensor.getPower()).thenReturn(power);
+            return sensor;
+        } else {
+            return new Sensor(new InputSensorInfo(name, "vendor", 0 /* version */,
+                    handle, type, 100.0f /*maxRange */, 0.02f /* resolution */,
+                    (float) power, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
+                    0 /* fifoMaxEventCount */, stringType /* stringType */,
+                    "" /* requiredPermission */, 0 /* maxDelay */, 0 /* flags */, 0 /* id */));
+        }
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
new file mode 100644
index 0000000..baf468e
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -0,0 +1,608 @@
+/*
+ * 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.processor;
+
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.POWER_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_ON;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.SCREEN_STATE_OTHER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_POWER;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_PROCESS_STATE;
+import static com.android.server.power.stats.processor.AggregatedPowerStatsConfig.STATE_SCREEN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.net.NetworkStats;
+import android.net.wifi.WifiManager;
+import android.os.BatteryConsumer;
+import android.os.Handler;
+import android.os.Process;
+import android.os.connectivity.WifiActivityEnergyInfo;
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.SparseArray;
+
+import com.android.internal.os.Clock;
+import com.android.internal.os.PowerProfile;
+import com.android.server.power.stats.BatteryUsageStatsRule;
+import com.android.server.power.stats.MockBatteryStatsImpl;
+import com.android.server.power.stats.PowerStatsCollector;
+import com.android.server.power.stats.PowerStatsUidResolver;
+import com.android.server.power.stats.WifiPowerStatsCollector;
+import com.android.server.power.stats.WifiPowerStatsCollector.WifiStatsRetriever;
+import com.android.server.power.stats.format.WifiPowerStatsLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public class WifiPowerStatsProcessorTest {
+    @Rule(order = 0)
+    public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+            .setProvideMainThread(true)
+            .build();
+
+    private static final double PRECISION = 0.00001;
+    private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
+    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
+    private static final int WIFI_ENERGY_CONSUMER_ID = 1;
+    private static final int VOLTAGE_MV = 3500;
+
+    @Rule(order = 1)
+    public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_RX, 480.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_TX, 720.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_ACTIVE, 360.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_SCAN, 480.0)
+            .setAveragePower(PowerProfile.POWER_WIFI_BATCHED_SCAN, 720.0)
+            .initMeasuredEnergyStatsLocked();
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private PowerStatsUidResolver mPowerStatsUidResolver;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
+    @Mock
+    private Supplier<NetworkStats> mNetworkStatsSupplier;
+    @Mock
+    private WifiManager mWifiManager;
+
+    private MockBatteryStatsImpl mBatteryStats;
+
+    private static class ScanTimes {
+        public long scanTimeMs;
+        public long batchScanTimeMs;
+    }
+
+    private final SparseArray<ScanTimes> mScanTimes = new SparseArray<>();
+    private long mWifiActiveDuration;
+
+    private final WifiStatsRetriever mWifiStatsRetriever = new WifiStatsRetriever() {
+        @Override
+        public void retrieveWifiScanTimes(Callback callback) {
+            for (int i = 0; i < mScanTimes.size(); i++) {
+                int uid = mScanTimes.keyAt(i);
+                ScanTimes scanTimes = mScanTimes.valueAt(i);
+                callback.onWifiScanTime(uid, scanTimes.scanTimeMs, scanTimes.batchScanTimeMs);
+            }
+        }
+
+        @Override
+        public long getWifiActiveDuration() {
+            return mWifiActiveDuration;
+        }
+    };
+
+    private final WifiPowerStatsCollector.Injector mInjector =
+            new WifiPowerStatsCollector.Injector() {
+                @Override
+                public Handler getHandler() {
+                    return mStatsRule.getHandler();
+                }
+
+                @Override
+                public Clock getClock() {
+                    return mStatsRule.getMockClock();
+                }
+
+                @Override
+                public PowerStatsUidResolver getUidResolver() {
+                    return mPowerStatsUidResolver;
+                }
+
+                @Override
+                public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+                    return 0;
+                }
+
+                @Override
+                public PackageManager getPackageManager() {
+                    return mPackageManager;
+                }
+
+                @Override
+                public PowerStatsCollector.ConsumedEnergyRetriever getConsumedEnergyRetriever() {
+                    return mConsumedEnergyRetriever;
+                }
+
+                @Override
+                public Supplier<NetworkStats> getWifiNetworkStatsSupplier() {
+                    return mNetworkStatsSupplier;
+                }
+
+                @Override
+                public WifiManager getWifiManager() {
+                    return mWifiManager;
+                }
+
+                @Override
+                public WifiStatsRetriever getWifiStatsRetriever() {
+                    return mWifiStatsRetriever;
+                }
+            };
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
+        when(mPowerStatsUidResolver.mapUid(anyInt()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        mBatteryStats = mStatsRule.getBatteryStats();
+    }
+
+    @Test
+    public void powerProfileModel_powerController() {
+        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
+
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new WifiPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        // Initial empty WifiActivityEnergyInfo.
+        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(0L,
+                WifiActivityEnergyInfo.STACK_STATE_INVALID, 0L, 0L, 0L, 0L));
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        mockWifiScanTimes(APP_UID1, 300, 400);
+        mockWifiScanTimes(APP_UID2, 100, 200);
+
+        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(10000,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 2000, 3000, 100, 600));
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        WifiPowerStatsLayout statsLayout =
+                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // RX power = 'rx-duration * PowerProfile[wifi.controller.rx]`
+        //        RX power = 3000 * 480 = 1440000 mA-ms = 0.4 mAh
+        // TX power = 'tx-duration * PowerProfile[wifi.controller.tx]`
+        //        TX power = 2000 * 720 = 1440000 mA-ms = 0.4 mAh
+        // Scan power = 'scan-duration * PowerProfile[wifi.scan]`
+        //        Scan power = 100 * 480 = 48000 mA-ms = 0.013333 mAh
+        // Idle power = 'idle-duration * PowerProfile[wifi.idle]`
+        //        Idle power = 600 * 360 = 216000 mA-ms = 0.06 mAh
+        // Total power = RX + TX + Scan + Idle = 0.873333
+        // Screen-on  - 25%
+        // Screen-off - 75%
+        double expectedPower = 0.873333;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1 =
+        //     (1500 / 2000) * 0.4        // rx
+        //     + (300 / 400) * 0.4        // tx
+        //     + (700 / 1000) * 0.013333  // scan (basic + batched)
+        //   = 0.609333 mAh
+        double expectedPower1 = 0.609333;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2 =
+        //     (500 / 2000) * 0.4         // rx
+        //     + (100 / 400) * 0.4        // tx
+        //     + (300 / 1000) * 0.013333  // scan (basic + batched)
+        //   = 0.204 mAh
+        double expectedPower2 = 0.204;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    @Test
+    public void consumedEnergyModel_powerController() {
+        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
+
+        // PowerStats hardware is available
+        when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
+                .thenReturn(new int[] {WIFI_ENERGY_CONSUMER_ID});
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new WifiPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        // Initial empty WifiActivityEnergyInfo.
+        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(0L,
+                WifiActivityEnergyInfo.STACK_STATE_INVALID, 0L, 0L, 0L, 0L));
+
+        when(mConsumedEnergyRetriever.getConsumedEnergy(
+                new int[]{WIFI_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(0)});
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        mockWifiScanTimes(APP_UID1, 300, 400);
+        mockWifiScanTimes(APP_UID2, 100, 200);
+
+        mockWifiActivityEnergyInfo(new WifiActivityEnergyInfo(10000,
+                WifiActivityEnergyInfo.STACK_STATE_STATE_ACTIVE, 2000, 3000, 100, 600));
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        // 10 mAh represented as microWattSeconds
+        long energyUws = 10 * 3600 * VOLTAGE_MV;
+        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{WIFI_ENERGY_CONSUMER_ID}))
+                .thenReturn(new EnergyConsumerResult[]{mockEnergyConsumer(energyUws)});
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        WifiPowerStatsLayout statsLayout =
+                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // All estimates are computed as in the #powerProfileModel_powerController test,
+        // except they are all scaled by the same ratio to ensure that the total estimated
+        // energy is equal to the measured energy
+        double expectedPower = 10;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1
+        //   0.609333           // power profile model estimate
+        //   0.873333           // power profile model estimate for total power
+        //   10                 // total consumed energy
+        //   = 0.609333 * (10 / 0.873333) = 6.9771
+        double expectedPower1 = 6.9771;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2
+        //   0.204              // power profile model estimate
+        //   0.873333           // power profile model estimate for total power
+        //   10                 // total consumed energy
+        //   = 0.204 * (10 / 0.873333) = 2.33588
+        double expectedPower2 = 2.33588;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    @Test
+    public void powerProfileModel_noPowerController() {
+        when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(false);
+
+        // No power monitoring hardware
+        when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
+                .thenReturn(new int[0]);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(
+                () -> new WifiPowerStatsProcessor(mStatsRule.getPowerProfile()));
+
+        WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, null);
+        collector.setEnabled(true);
+
+        aggregatedStats.start(0);
+
+        // Establish a baseline
+        aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+        // Turn the screen off after 2.5 seconds
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+                5000);
+
+        // Note application network activity
+        NetworkStats networkStats = mockNetworkStats(10000, 1,
+                mockNetworkStatsEntry("wifi", APP_UID1, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+                mockNetworkStatsEntry("wifi", APP_UID2, 0, 0,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+        when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+        mScanTimes.clear();
+        mWifiActiveDuration = 8000;
+        mockWifiScanTimes(APP_UID1, 300, 400);
+        mockWifiScanTimes(APP_UID2, 100, 200);
+
+        mStatsRule.setTime(10_000, 10_000);
+
+        aggregatedStats.addPowerStats(collector.collectStats(), 10_000);
+
+        aggregatedStats.finish(10_000);
+
+        WifiPowerStatsLayout statsLayout =
+                new WifiPowerStatsLayout(aggregatedStats.getPowerStatsDescriptor());
+
+        // Total active power = 'active-duration * PowerProfile[wifi.on]`
+        //        active = 8000 * 360 = 2880000 mA-ms = 0.8 mAh
+        // UID1 rxPackets + txPackets = 1800
+        // UID2 rxPackets + txPackets = 600
+        // Total rx+tx packets = 2400
+        // Total scan power = `scan-duration * PowerProfile[wifi.scan]`
+        //        scan = (100 + 300) * 480 = 192000 mA-ms = 0.05333 mAh
+        // Total batch scan power = `(200 + 400) * PowerProfile[wifi.batchedscan]`
+        //        bscan = (200 + 400) * 720 = 432000 mA-ms = 0.12 mAh
+        //
+        // Expected power = active + scan + bscan = 0.97333
+        double expectedPower = 0.97333;
+        long[] deviceStats = new long[aggregatedStats.getPowerStatsDescriptor().statsArrayLength];
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.25);
+
+        aggregatedStats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
+        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
+                .isWithin(PRECISION).of(expectedPower * 0.75);
+
+        // UID1 =
+        //     (1800 / 2400) * 0.8      // active
+        //     + (300 / 400) * 0.05333  // scan
+        //     + (400 / 600) * 0.12     // batched scan
+        //   = 0.72 mAh
+        double expectedPower1 = 0.72;
+        long[] uidStats = new long[aggregatedStats.getPowerStatsDescriptor().uidStatsArrayLength];
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID1,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower1 * 0.5);
+
+        // UID2 =
+        //     (600 / 2400) * 0.8       // active
+        //     + (100 / 400) * 0.05333  // scan
+        //     + (200 / 600) * 0.12     // batched scan
+        //   = 0.253333 mAh
+        double expectedPower2 = 0.25333;
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.25);
+
+        aggregatedStats.getUidStats(uidStats, APP_UID2,
+                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
+        assertThat(statsLayout.getUidPowerEstimate(uidStats))
+                .isWithin(PRECISION).of(expectedPower2 * 0.75);
+    }
+
+    private static PowerComponentAggregatedPowerStats createAggregatedPowerStats(
+            Supplier<PowerStatsProcessor> processorSupplier) {
+        AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
+        config.trackPowerComponent(BatteryConsumer.POWER_COMPONENT_WIFI)
+                .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+                .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+                .setProcessorSupplier(processorSupplier);
+
+        PowerComponentAggregatedPowerStats aggregatedStats = new AggregatedPowerStats(config)
+                .getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_WIFI);
+
+        aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+        aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+        aggregatedStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+        aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+        return aggregatedStats;
+    }
+
+    private int[] states(int... states) {
+        return states;
+    }
+
+    private EnergyConsumerResult mockEnergyConsumer(long energyUWs) {
+        EnergyConsumerResult ecr = new EnergyConsumerResult();
+        ecr.energyUWs = energyUWs;
+        return ecr;
+    }
+
+    private void mockWifiActivityEnergyInfo(WifiActivityEnergyInfo waei) {
+        doAnswer(invocation -> {
+            WifiManager.OnWifiActivityEnergyInfoListener
+                    listener = invocation.getArgument(1);
+            listener.onWifiActivityEnergyInfo(waei);
+            return null;
+        }).when(mWifiManager).getWifiActivityEnergyInfoAsync(any(), any());
+    }
+
+    private NetworkStats mockNetworkStats(int elapsedTime, int initialSize,
+            NetworkStats.Entry... entries) {
+        NetworkStats stats;
+        if (RavenwoodRule.isOnRavenwood()) {
+            stats = mock(NetworkStats.class);
+            when(stats.iterator()).thenAnswer(inv -> List.of(entries).iterator());
+        } else {
+            stats = new NetworkStats(elapsedTime, initialSize);
+            for (NetworkStats.Entry entry : entries) {
+                stats = stats.addEntry(entry);
+            }
+        }
+        return stats;
+    }
+
+    private static NetworkStats.Entry mockNetworkStatsEntry(@Nullable String iface, int uid,
+            int set, int tag, int metered, int roaming, int defaultNetwork, long rxBytes,
+            long rxPackets, long txBytes, long txPackets, long operations) {
+        if (RavenwoodRule.isOnRavenwood()) {
+            NetworkStats.Entry entry = mock(NetworkStats.Entry.class);
+            when(entry.getUid()).thenReturn(uid);
+            when(entry.getMetered()).thenReturn(metered);
+            when(entry.getRoaming()).thenReturn(roaming);
+            when(entry.getDefaultNetwork()).thenReturn(defaultNetwork);
+            when(entry.getRxBytes()).thenReturn(rxBytes);
+            when(entry.getRxPackets()).thenReturn(rxPackets);
+            when(entry.getTxBytes()).thenReturn(txBytes);
+            when(entry.getTxPackets()).thenReturn(txPackets);
+            when(entry.getOperations()).thenReturn(operations);
+            return entry;
+        } else {
+            return new NetworkStats.Entry(iface, uid, set, tag, metered,
+                    roaming, defaultNetwork, rxBytes, rxPackets, txBytes, txPackets, operations);
+        }
+    }
+
+    private void mockWifiScanTimes(int uid, long scanTimeMs, long batchScanTimeMs) {
+        ScanTimes scanTimes = new ScanTimes();
+        scanTimes.scanTimeMs = scanTimeMs;
+        scanTimes.batchScanTimeMs = batchScanTimeMs;
+        mScanTimes.put(uid, scanTimes);
+    }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 1838fe8..115cdf6 100644
--- a/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -37,6 +38,8 @@
 import android.os.Looper;
 import android.os.PowerMonitor;
 import android.os.ResultReceiver;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
 
@@ -58,6 +61,7 @@
 import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.io.ByteArrayOutputStream;
@@ -70,6 +74,8 @@
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 
 /**
@@ -101,6 +107,8 @@
     private static final int STATE_RESIDENCY_COUNT = 4;
     private static final int APP_UID = 10042;
 
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
     private PowerStatsService mService;
     private TestPowerStatsHALWrapper mPowerStatsHALWrapper = new TestPowerStatsHALWrapper();
@@ -215,6 +223,7 @@
     };
 
     public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper {
+        public RuntimeException exception;
         public EnergyConsumerResult[] energyConsumerResults;
         public EnergyMeasurement[] energyMeasurements;
 
@@ -237,6 +246,9 @@
 
         @Override
         public StateResidencyResult[] getStateResidency(int[] powerEntityIds) {
+            if (exception != null) {
+                throw exception;
+            }
             StateResidencyResult[] stateResidencyResultList =
                     new StateResidencyResult[POWER_ENTITY_COUNT];
             for (int i = 0; i < stateResidencyResultList.length; i++) {
@@ -288,6 +300,9 @@
 
         @Override
         public EnergyConsumerResult[] getEnergyConsumed(int[] energyConsumerIds) {
+            if (exception != null) {
+                throw exception;
+            }
             return energyConsumerResults;
         }
 
@@ -316,6 +331,9 @@
 
         @Override
         public EnergyMeasurement[] readEnergyMeter(int[] channelIds) {
+            if (exception != null) {
+                throw exception;
+            }
             return energyMeasurements;
         }
 
@@ -1198,4 +1216,49 @@
         assertThat(Arrays.stream(supportedPowerMonitorsResult.powerMonitors)
                 .map(PowerMonitor::getName).toList()).contains("ENERGYCONSUMER0");
     }
+
+    @EnableFlags(Flags.FLAG_VERIFY_NON_NULL_ARGUMENTS)
+    @Test
+    public void testGetSupportedPowerMonitors_withNullArguments() {
+        IPowerStatsService iPowerStatsService = mService.getIPowerStatsServiceForTest();
+        assertThrows(NullPointerException.class,
+                () -> iPowerStatsService.getSupportedPowerMonitors(null));
+    }
+
+    @EnableFlags(Flags.FLAG_VERIFY_NON_NULL_ARGUMENTS)
+    @Test
+    public void testGetPowerMonitorReadings_withNullArguments() {
+        IPowerStatsService iPowerStatsService = mService.getIPowerStatsServiceForTest();
+        assertThrows(NullPointerException.class, () -> iPowerStatsService.getPowerMonitorReadings(
+                null, new GetPowerMonitorsResult()));
+        assertThrows(NullPointerException.class, () -> iPowerStatsService.getPowerMonitorReadings(
+                new int[] {0}, null));
+    }
+
+    @Test
+    public void getEnergyConsumedAsync_halException() {
+        mPowerStatsHALWrapper.exception = new IllegalArgumentException();
+        CompletableFuture<EnergyConsumerResult[]> future =
+                mService.getPowerStatsInternal().getEnergyConsumedAsync(new int[]{1});
+        ExecutionException exception = assertThrows(ExecutionException.class, future::get);
+        assertThat(exception.getCause()).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void getStateResidencyAsync_halException() {
+        mPowerStatsHALWrapper.exception = new IllegalArgumentException();
+        CompletableFuture<StateResidencyResult[]> future =
+                mService.getPowerStatsInternal().getStateResidencyAsync(new int[]{1});
+        ExecutionException exception = assertThrows(ExecutionException.class, future::get);
+        assertThat(exception.getCause()).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void readEnergyMeterAsync_halException() {
+        mPowerStatsHALWrapper.exception = new IllegalArgumentException();
+        CompletableFuture<EnergyMeasurement[]> future =
+                mService.getPowerStatsInternal().readEnergyMeterAsync(new int[]{1});
+        ExecutionException exception = assertThrows(ExecutionException.class, future::get);
+        assertThat(exception.getCause()).isInstanceOf(IllegalArgumentException.class);
+    }
 }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a86289b..a862c43 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -36,8 +36,8 @@
         "-Werror",
     ],
     static_libs: [
-        "a11ychecker-protos-java-proto-lite",
         "aatf",
+        "accessibility_protos_lite",
         "cts-input-lib",
         "frameworks-base-testutils",
         "services.accessibility",
@@ -85,6 +85,7 @@
         "securebox",
         "flag-junit",
         "ravenwood-junit",
+        "net-tests-utils",
         "net_flags_lib",
         "CtsVirtualDeviceCommonLib",
         "com_android_server_accessibility_flags_lib",
@@ -272,6 +273,10 @@
         "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res",
 }
 
+FLAKY = [
+    "androidx.test.filters.FlakyTest",
+]
+
 FLAKY_AND_IGNORED = [
     "androidx.test.filters.FlakyTest",
     "org.junit.Ignore",
@@ -328,7 +333,7 @@
     base: "FrameworksServicesTests",
     test_suites: ["device-tests"],
     include_filters: ["com.android.server.recoverysystem."],
-    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+    exclude_annotations: FLAKY,
 }
 
 // server pm TEST_MAPPING
@@ -357,3 +362,492 @@
     test_suites: ["device-tests"],
     include_filters: ["com.android.server.os."],
 }
+
+test_module_config {
+    name: "FrameworksServicesTests_presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_job_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.job"],
+    exclude_annotations: [
+        "androidx.test.filters.LargeTest",
+        "androidx.test.filters.FlakyTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_job",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_tare_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.tare"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_tare",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_usage_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.usage"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_usage",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.usage"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_battery_stats",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.am.BatteryStatsServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_accessibility_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.accessibility"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_accessibility",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.accessibility"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_binary_transparency",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.BinaryTransparencyServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_pinner_service",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.PinnerServiceTest"],
+    exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_am_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.am."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_am",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.am."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_appop",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.appop"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_audio",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.audio"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_compat",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.compat"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_hdmi_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.hdmi"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_hdmi",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.hdmi"],
+    exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_integrity",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.integrity."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_lights",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.lights"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_locales",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.locales."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_location_contexthub_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.location.contexthub."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_locksettings",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.locksettings."],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_logcat_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.logcat"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_logcat",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.logcat"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_net_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.net."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_om",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.om."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_pdb",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pdb.PersistentDataBlockServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_pm_dex",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.pm.dex"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_policy_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.policy."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_policy",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_power",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_power_hint",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.power.hint"],
+    exclude_annotations: FLAKY,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_powerstats",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.powerstats"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_rollback",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.rollback"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_uri",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.uri."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_com_android_server_location_contexthub",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.location.contexthub."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+    exclude_annotations: FLAKY_AND_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_usage",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.usage"],
+    exclude_filters: ["com.android.server.usage.StorageStatsServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_soundtrigger_middleware",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.soundtrigger_middleware"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_android_server_input",
+    base: "FrameworksServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.input"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_job",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.job"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_tare",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.tare"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_usage",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.usage"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_om",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.om"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_accessibility",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.accessibility"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_binarytransparencyservicetest",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.BinaryTransparencyServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_pinnerservicetest",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.PinnerServiceTest"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_am",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.am."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_hdmi",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.hdmi"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_logcat",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.logcat"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_net_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.net."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_policy_Presubmit",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.policy."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_policy",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_power",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_power_hint",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.power.hint"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_location_contexthub_Postsubmit",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.location.contexthub."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+}
+
+test_module_config {
+    name: "FrameworksServicesTests_server_input",
+    base: "FrameworksServicesTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.input"],
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 1afe12f..2e6c93c 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -17,14 +17,21 @@
 package com.android.server.accessibility;
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+import static android.provider.Settings.Secure.NAVIGATION_MODE;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_DIALOG_FOR_TRUSTED_SERVICES;
 
 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
 import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
@@ -95,6 +102,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityWindowAttributes;
 import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IUserInitializationCompleteCallback;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
@@ -121,7 +129,6 @@
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -130,8 +137,8 @@
 import org.mockito.ArgumentMatchers;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 import org.mockito.internal.util.reflection.FieldReader;
 import org.mockito.internal.util.reflection.FieldSetter;
 import org.mockito.stubbing.Answer;
@@ -178,6 +185,8 @@
     private static final String TARGET_ALWAYS_ON_A11Y_SERVICE_TILE_CLASS = "TileService";
     private static final ComponentName TARGET_STANDARD_A11Y_SERVICE =
             new ComponentName("FakePackage", "StandardA11yService");
+    private static final String TARGET_STANDARD_A11Y_SERVICE_NAME =
+            TARGET_STANDARD_A11Y_SERVICE.flattenToString();
 
     static final ComponentName COMPONENT_NAME = new ComponentName(
             "com.android.server.accessibility", "AccessibilityManagerServiceTest");
@@ -202,6 +211,7 @@
     @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
     @Mock private ProxyManager mProxyManager;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
     @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
     private IAccessibilityManager mA11yManagerServiceOnDevice;
     private AccessibilityServiceConnection mAccessibilityServiceConnection;
@@ -229,7 +239,7 @@
         LocalServices.addService(
                 UserManagerInternal.class, mMockUserManagerInternal);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
-        mInputFilter = Mockito.mock(FakeInputFilter.class);
+        mInputFilter = mock(FakeInputFilter.class);
 
         when(mMockMagnificationController.getMagnificationConnectionManager()).thenReturn(
                 mMockMagnificationConnectionManager);
@@ -602,7 +612,7 @@
                 mA11yms.getCurrentUserIdLocked());
         userState.updateShortcutTargetsLocked(Set.of(MAGNIFICATION_CONTROLLER_NAME), HARDWARE);
         userState.setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+                ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
 
         // Invokes client change to trigger onUserStateChanged.
         mA11yms.onClientChangeLocked(/* serviceInfoChanged= */false);
@@ -618,7 +628,7 @@
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
         userState.setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+                ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
         userState.setMagnificationSingleFingerTripleTapEnabledLocked(true);
 
         // Invokes client change to trigger onUserStateChanged.
@@ -635,7 +645,7 @@
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
         userState.setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+                ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
         //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
         userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
 
@@ -654,7 +664,7 @@
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
         userState.setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+                ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
         userState.setMagnificationTwoFingerTripleTapEnabledLocked(true);
 
         // Invokes client change to trigger onUserStateChanged.
@@ -672,7 +682,7 @@
         final AccessibilityUserState userState = mA11yms.mUserStates.get(
                 mA11yms.getCurrentUserIdLocked());
         userState.setMagnificationCapabilitiesLocked(
-                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
+                ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
         //userState.setMagnificationSingleFingerTripleTapEnabledLocked(false);
         userState.setMagnificationTwoFingerTripleTapEnabledLocked(false);
 
@@ -1090,7 +1100,7 @@
         assertThrows(SecurityException.class,
                 () -> mA11yms.enableShortcutsForTargets(
                         /* enable= */true,
-                        UserShortcutType.SOFTWARE,
+                        SOFTWARE,
                         List.of(TARGET_MAGNIFICATION),
                         mA11yms.getCurrentUserIdLocked()));
     }
@@ -1099,7 +1109,7 @@
     public void enableShortcutsForTargets_enableSoftwareShortcut_shortcutTurnedOn()
             throws Exception {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         setupShortcutTargetServices();
@@ -1107,13 +1117,13 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(target),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(ShortcutUtils.isComponentIdExistingInSettings(
-                mTestableContext, UserShortcutType.SOFTWARE, target
+                mTestableContext, SOFTWARE, target
         )).isTrue();
     }
 
@@ -1121,7 +1131,7 @@
     @EnableFlags(Flags.FLAG_ENABLE_HARDWARE_SHORTCUT_DISABLES_WARNING)
     public void enableHardwareShortcutsForTargets_shortcutDialogSetting_isShown() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         Settings.Secure.putInt(
                 mTestableContext.getContentResolver(),
@@ -1151,33 +1161,33 @@
     public void enableShortcutsForTargets_disableSoftwareShortcut_shortcutTurnedOff()
             throws Exception {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         String target = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         enableShortcutsForTargets_enableSoftwareShortcut_shortcutTurnedOn();
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(target),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(ShortcutUtils.isComponentIdExistingInSettings(
-                mTestableContext, UserShortcutType.SOFTWARE, target
+                mTestableContext, SOFTWARE, target
         )).isFalse();
     }
 
     @Test
     public void enableShortcutsForTargets_enableSoftwareShortcutWithMagnification_menuSizeIncreased() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(MAGNIFICATION_CONTROLLER_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1200,7 +1210,7 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(MAGNIFICATION_CONTROLLER_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1217,14 +1227,14 @@
     public void enableShortcutsForTargets_enableAlwaysOnServiceSoftwareShortcut_turnsOnAlwaysOnService()
             throws Exception {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         setupShortcutTargetServices();
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1240,13 +1250,13 @@
     public void enableShortcutsForTargets_disableAlwaysOnServiceSoftwareShortcut_turnsOffAlwaysOnService()
             throws Exception {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableAlwaysOnServiceSoftwareShortcut_turnsOnAlwaysOnService();
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                UserShortcutType.SOFTWARE,
+                SOFTWARE,
                 List.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
@@ -1266,8 +1276,8 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
-                UserShortcutType.SOFTWARE,
-                List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
+                SOFTWARE,
+                List.of(TARGET_STANDARD_A11Y_SERVICE_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
@@ -1282,7 +1292,7 @@
     public void enableShortcutsForTargets_disableStandardServiceSoftwareShortcutWithServiceOn_wontTurnOffService()
             throws Exception {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableStandardServiceSoftwareShortcut_wontTurnOnService();
         AccessibilityUtils.setAccessibilityServiceState(
@@ -1290,8 +1300,8 @@
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
-                UserShortcutType.SOFTWARE,
-                List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
+                SOFTWARE,
+                List.of(TARGET_STANDARD_A11Y_SERVICE_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
@@ -1305,7 +1315,7 @@
     @Test
     public void enableShortcutsForTargets_enableTripleTapShortcut_settingUpdated() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
 
@@ -1327,7 +1337,7 @@
     @Test
     public void enableShortcutsForTargets_disableTripleTapShortcut_settingUpdated() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableTripleTapShortcut_settingUpdated();
 
@@ -1348,7 +1358,7 @@
     @Test
     public void enableShortcutsForTargets_enableMultiFingerMultiTapsShortcut_settingUpdated() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
 
@@ -1370,7 +1380,7 @@
     @Test
     public void enableShortcutsForTargets_disableMultiFingerMultiTapsShortcut_settingUpdated() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableMultiFingerMultiTapsShortcut_settingUpdated();
 
@@ -1392,7 +1402,7 @@
     @Test
     public void enableShortcutsForTargets_enableVolumeKeysShortcut_shortcutSet() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         setupShortcutTargetServices();
@@ -1400,28 +1410,28 @@
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ true,
                 HARDWARE,
-                List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
+                List.of(TARGET_STANDARD_A11Y_SERVICE_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
         assertThat(
                 ShortcutUtils.isComponentIdExistingInSettings(
                         mTestableContext, HARDWARE,
-                        TARGET_STANDARD_A11Y_SERVICE.flattenToString())
+                        TARGET_STANDARD_A11Y_SERVICE_NAME)
         ).isTrue();
     }
 
     @Test
     public void enableShortcutsForTargets_disableVolumeKeysShortcut_shortcutNotSet() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableVolumeKeysShortcut_shortcutSet();
 
         mA11yms.enableShortcutsForTargets(
                 /* enable= */ false,
                 HARDWARE,
-                List.of(TARGET_STANDARD_A11Y_SERVICE.flattenToString()),
+                List.of(TARGET_STANDARD_A11Y_SERVICE_NAME),
                 mA11yms.getCurrentUserIdLocked());
         mTestableLooper.processAllMessages();
 
@@ -1429,14 +1439,14 @@
                         ShortcutUtils.isComponentIdExistingInSettings(
                                 mTestableContext,
                                 HARDWARE,
-                                TARGET_STANDARD_A11Y_SERVICE.flattenToString()))
+                                TARGET_STANDARD_A11Y_SERVICE_NAME))
                 .isFalse();
     }
 
     @Test
     public void enableShortcutsForTargets_enableQuickSettings_shortcutSet() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         setupShortcutTargetServices();
@@ -1464,7 +1474,7 @@
     @Test
     public void enableShortcutsForTargets_disableQuickSettings_shortcutNotSet() {
         // TODO(b/111889696): Remove the user 0 assumption once we support multi-user
-        Assume.assumeTrue("The test is setup to run as a user 0",
+        assumeTrue("The test is setup to run as a user 0",
                 isSameCurrentUser(mA11yms, mTestableContext));
         enableShortcutsForTargets_enableQuickSettings_shortcutSet();
 
@@ -1779,7 +1789,7 @@
         mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY);
         final String servicePrevious = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         final String otherPrevious = TARGET_MAGNIFICATION;
-        final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+        final String serviceRestored = TARGET_STANDARD_A11Y_SERVICE_NAME;
         final AccessibilityUserState userState = new AccessibilityUserState(
                 UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
         mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
@@ -1803,7 +1813,7 @@
             android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
             Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
     public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
-        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         mTestableContext.getOrCreateTestableResources().addOverride(
                 R.string.config_defaultAccessibilityService, serviceDefault);
         final AccessibilityUserState userState = new AccessibilityUserState(
@@ -1831,7 +1841,7 @@
             android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
             Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
     public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
-        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         // Restored value from the broadcast contains both default and non-default service.
         final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
@@ -1858,7 +1868,7 @@
             android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
             Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
     public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
-        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE.flattenToString();
+        final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
         final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
         // Restored value from the broadcast contains both default and non-default service.
         final String combinedRestored = String.join(":", serviceDefault, serviceRestored);
@@ -1884,6 +1894,187 @@
                 .containsExactlyElementsIn(expected);
     }
 
+    @Test
+    @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavButtonNavigation_migratesGestureTargets() {
+        mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        userState.updateShortcutTargetsLocked(
+                Set.of(TARGET_STANDARD_A11Y_SERVICE_NAME), SOFTWARE);
+        userState.updateShortcutTargetsLocked(
+                Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), GESTURE);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertShortcutUserStateAndSetting(userState, GESTURE, Set.of());
+        assertShortcutUserStateAndSetting(userState, SOFTWARE, Set.of(
+                TARGET_STANDARD_A11Y_SERVICE_NAME,
+                TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()
+        ));
+    }
+
+    @Test
+    @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavButtonNavigation_gestureTargets_noButtonTargets_navBarButtonMode() {
+        mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        userState.updateShortcutTargetsLocked(Set.of(), SOFTWARE);
+        userState.updateShortcutTargetsLocked(
+                Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), GESTURE);
+        ShortcutUtils.setButtonMode(
+                mTestableContext, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU, UserHandle.USER_SYSTEM);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertThat(ShortcutUtils.getButtonMode(mTestableContext, UserHandle.USER_SYSTEM))
+                .isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+    }
+
+    @Test
+    @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onGestureNavigation_floatingMenuMode() {
+        mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        ShortcutUtils.setButtonMode(
+                mTestableContext, ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_SYSTEM);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertThat(ShortcutUtils.getButtonMode(mTestableContext, userState.mUserId))
+                .isEqualTo(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+    }
+
+    @Test
+    @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavigation_revertGestureTargets() {
+        mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        userState.updateShortcutTargetsLocked(
+                Set.of(TARGET_STANDARD_A11Y_SERVICE_NAME), SOFTWARE);
+        userState.updateShortcutTargetsLocked(
+                Set.of(TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), GESTURE);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertShortcutUserStateAndSetting(userState, GESTURE, Set.of());
+        assertShortcutUserStateAndSetting(userState, SOFTWARE, Set.of(
+                TARGET_STANDARD_A11Y_SERVICE_NAME,
+                TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()
+        ));
+    }
+
+    @Test
+    @EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavigation_gestureNavigation_gestureButtonMode_migratesTargetsToGesture() {
+        mFakePermissionEnforcer.grant(Manifest.permission.STATUS_BAR_SERVICE);
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        userState.updateShortcutTargetsLocked(Set.of(
+                TARGET_STANDARD_A11Y_SERVICE_NAME,
+                TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()), SOFTWARE);
+        userState.updateShortcutTargetsLocked(Set.of(), GESTURE);
+
+        ShortcutUtils.setButtonMode(
+                mTestableContext, ACCESSIBILITY_BUTTON_MODE_GESTURE, UserHandle.USER_SYSTEM);
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertShortcutUserStateAndSetting(userState, SOFTWARE, Set.of());
+        assertShortcutUserStateAndSetting(userState, GESTURE, Set.of(
+                TARGET_STANDARD_A11Y_SERVICE_NAME,
+                TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString()
+        ));
+    }
+
+    @Test
+    @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavigation_gestureNavigation_correctsButtonMode() {
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        ShortcutUtils.setButtonMode(
+                mTestableContext, ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_SYSTEM);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_GESTURAL, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertThat(ShortcutUtils.getButtonMode(mTestableContext, userState.mUserId))
+                .isEqualTo(ACCESSIBILITY_BUTTON_MODE_GESTURE);
+    }
+
+    @Test
+    @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
+    public void onNavigation_navBarNavigation_correctsButtonMode() {
+        final AccessibilityUserState userState = new AccessibilityUserState(
+                UserHandle.USER_SYSTEM, mTestableContext, mA11yms);
+        mA11yms.mUserStates.put(UserHandle.USER_SYSTEM, userState);
+        setupShortcutTargetServices(userState);
+        ShortcutUtils.setButtonMode(
+                mTestableContext, ACCESSIBILITY_BUTTON_MODE_GESTURE, UserHandle.USER_SYSTEM);
+
+        Settings.Secure.putIntForUser(mTestableContext.getContentResolver(),
+                NAVIGATION_MODE, NAV_BAR_MODE_3BUTTON, userState.mUserId);
+        mA11yms.updateShortcutsForCurrentNavigationMode();
+
+        assertThat(ShortcutUtils.getButtonMode(mTestableContext, userState.mUserId))
+                .isEqualTo(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+    }
+
+    @Test
+    public void registerUserInitializationCompleteCallback_isRegistered() {
+        mA11yms.mUserInitializationCompleteCallbacks.clear();
+
+        mA11yms.registerUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+        assertThat(mA11yms.mUserInitializationCompleteCallbacks).containsExactly(
+                mUserInitializationCompleteCallback);
+    }
+
+    @Test
+    public void unregisterUserInitializationCompleteCallback_isUnregistered() {
+        mA11yms.mUserInitializationCompleteCallbacks.clear();
+        mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+        mA11yms.unregisterUserInitializationCompleteCallback(mUserInitializationCompleteCallback);
+
+        assertThat(mA11yms.mUserInitializationCompleteCallbacks).isEmpty();
+    }
+
+    @Test
+    public void switchUser_callsUserInitializationCompleteCallback() throws RemoteException {
+        mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+
+        mA11yms.switchUser(UserHandle.MIN_SECONDARY_USER_ID);
+
+        verify(mUserInitializationCompleteCallback).onUserInitializationComplete(
+                UserHandle.MIN_SECONDARY_USER_ID);
+    }
+
     private Set<String> readStringsFromSetting(String setting) {
         final Set<String> result = new ArraySet<>();
         mA11yms.readColonDelimitedSettingToSet(
@@ -1917,16 +2108,16 @@
         AccessibilityServiceInfo accessibilityServiceInfo =
                 spy(new AccessibilityServiceInfo());
         accessibilityServiceInfo.setComponentName(componentName);
-        ResolveInfo mockResolveInfo = Mockito.mock(ResolveInfo.class);
+        ResolveInfo mockResolveInfo = mock(ResolveInfo.class);
         when(accessibilityServiceInfo.getResolveInfo()).thenReturn(mockResolveInfo);
-        mockResolveInfo.serviceInfo = Mockito.mock(ServiceInfo.class);
-        mockResolveInfo.serviceInfo.applicationInfo = Mockito.mock(ApplicationInfo.class);
+        mockResolveInfo.serviceInfo = mock(ServiceInfo.class);
+        mockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
         mockResolveInfo.serviceInfo.packageName = componentName.getPackageName();
         mockResolveInfo.serviceInfo.name = componentName.getClassName();
         when(mockResolveInfo.serviceInfo.applicationInfo.isSystemApp()).thenReturn(isSystemApp);
         if (isAlwaysOnService) {
             accessibilityServiceInfo.flags |=
-                    AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
+                    FLAG_REQUEST_ACCESSIBILITY_BUTTON;
             mockResolveInfo.serviceInfo.applicationInfo.targetSdkVersion =
                     Build.VERSION_CODES.R;
         }
@@ -1999,7 +2190,7 @@
 
         A11yTestableContext(Context base) {
             super(base);
-            mMockContext = Mockito.mock(Context.class);
+            mMockContext = mock(Context.class);
         }
 
         @Override
@@ -2050,4 +2241,12 @@
                 shortcutValue,
                 userId);
     }
+
+    private void assertShortcutUserStateAndSetting(AccessibilityUserState userState,
+            @UserShortcutType int shortcutType, Set<String> value) {
+        assertThat(userState.getShortcutTargetsLocked(shortcutType))
+                .containsExactlyElementsIn(value);
+        Set<String> setting = readStringsFromSetting(ShortcutUtils.convertToKey(shortcutType));
+        assertThat(setting).containsExactlyElementsIn(value);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
index 9083a1e..1904145 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerWithAccessibilityWindowTest.java
@@ -1319,7 +1319,6 @@
 
         final AccessibilityWindow window = Mockito.mock(AccessibilityWindow.class);
         when(window.getWindowInfo()).thenReturn(windowInfo);
-        when(window.ignoreRecentsAnimationForAccessibility()).thenReturn(false);
         when(window.isFocused()).thenAnswer(invocation -> windowInfo.focused);
         when(window.isTouchable()).thenReturn(true);
         when(window.getType()).thenReturn(windowInfo.type);
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 8753b25..019ccf9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MouseKeysInterceptorTest.kt
@@ -48,6 +48,7 @@
 import java.util.LinkedList
 import java.util.Queue
 import android.util.ArraySet
+import android.view.InputDevice
 
 /**
  * Tests for {@link MouseKeysInterceptor}
@@ -68,6 +69,8 @@
     }
 
     private lateinit var mouseKeysInterceptor: MouseKeysInterceptor
+    private lateinit var inputDevice: InputDevice
+
     private val clock = OffsettableClock()
     private val testLooper = TestLooper { clock.now() }
     private val nextInterceptor = TrackingInterceptor()
@@ -98,6 +101,10 @@
         testSession = InputManagerGlobal.createTestSession(iInputManager)
         mockInputManager = InputManager(context)
 
+        inputDevice = createInputDevice(DEVICE_ID)
+        Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID))
+                .thenReturn(inputDevice)
+
         Mockito.`when`(mockVirtualDeviceManagerInternal.getDeviceIdsForUid(Mockito.anyInt()))
             .thenReturn(ArraySet(setOf(DEVICE_ID)))
         LocalServices.removeServiceForTest(VirtualDeviceManagerInternal::class.java)
@@ -115,7 +122,8 @@
         Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
         Mockito.`when`(mockAms.traceManager).thenReturn(mockTraceManager)
 
-        mouseKeysInterceptor = MouseKeysInterceptor(mockAms, testLooper.looper, DISPLAY_ID)
+        mouseKeysInterceptor = MouseKeysInterceptor(mockAms, mockInputManager,
+                testLooper.looper, DISPLAY_ID)
         mouseKeysInterceptor.next = nextInterceptor
     }
 
@@ -281,6 +289,17 @@
         }
     }
 
+    private fun createInputDevice(
+            deviceId: Int,
+            generation: Int = -1
+    ): InputDevice =
+            InputDevice.Builder()
+                    .setId(deviceId)
+                    .setName("Device $deviceId")
+                    .setDescriptor("descriptor $deviceId")
+                    .setGeneration(generation)
+                    .build()
+
     private class TrackingInterceptor : BaseEventStreamTransformation() {
         val events: Queue<KeyEvent> = LinkedList()
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java
index c1b3929..5ee86ffa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerManagerTest.java
@@ -23,7 +23,7 @@
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_DEFAULT_BROWSER;
-import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom;
+import static com.android.server.accessibility.a11ychecker.TestUtils.createResult;
 import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +32,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.accessibility.AccessibilityCheckClass;
+import android.accessibility.AccessibilityCheckResultType;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.platform.test.annotations.DisableFlags;
@@ -112,19 +114,19 @@
                         .setViewIdResourceName("node2")
                         .build();
 
-        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+        Set<AndroidAccessibilityCheckerResult> results =
                 mAccessibilityCheckerManager.maybeRunA11yChecker(
                         List.of(mockNodeInfo1, mockNodeInfo2), QUALIFIED_TEST_ACTIVITY_NAME,
                         new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                 TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);
 
         assertThat(results).containsExactly(
-                createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
-                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
-                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2),
-                createAtom(/*viewIdResourceName=*/ "node2", TEST_ACTIVITY_NAME,
-                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
-                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2)
+                createResult(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
+                        AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        AccessibilityCheckResultType.ERROR_CHECK_RESULT_TYPE, /*resultId=*/ 2),
+                createResult(/*viewIdResourceName=*/ "node2", TEST_ACTIVITY_NAME,
+                        AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        AccessibilityCheckResultType.ERROR_CHECK_RESULT_TYPE, /*resultId=*/ 2)
         );
     }
 
@@ -137,7 +139,7 @@
                         .setViewIdResourceName("node1")
                         .build();
 
-        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+        Set<AndroidAccessibilityCheckerResult> results =
                 mAccessibilityCheckerManager.maybeRunA11yChecker(
                         List.of(mockNodeInfo), QUALIFIED_TEST_ACTIVITY_NAME,
                         new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
@@ -158,16 +160,17 @@
                         .setViewIdResourceName("node1")
                         .build();
 
-        Set<A11yCheckerProto.AccessibilityCheckResultReported> results =
+        Set<AndroidAccessibilityCheckerResult> results =
                 mAccessibilityCheckerManager.maybeRunA11yChecker(
                         List.of(mockNodeInfo, mockNodeInfoDuplicate), QUALIFIED_TEST_ACTIVITY_NAME,
                         new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                 TEST_A11Y_SERVICE_CLASS_NAME), /*userId=*/ 0);
 
         assertThat(results).containsExactly(
-                createAtom(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
-                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
-                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, /*resultId=*/ 2)
+                createResult(/*viewIdResourceName=*/ "node1", TEST_ACTIVITY_NAME,
+                        AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        AccessibilityCheckResultType.ERROR_CHECK_RESULT_TYPE, /*resultId=*/
+                        2)
         );
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
index 5b4e72e..cdaeade 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/AccessibilityCheckerUtilsTest.java
@@ -21,13 +21,15 @@
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_ACTIVITY_NAME;
 import static com.android.server.accessibility.a11ychecker.TestUtils.TEST_APP_PACKAGE_NAME;
-import static com.android.server.accessibility.a11ychecker.TestUtils.createAtom;
+import static com.android.server.accessibility.a11ychecker.TestUtils.createResult;
 import static com.android.server.accessibility.a11ychecker.TestUtils.getMockPackageManagerWithInstalledApps;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
+import android.accessibility.AccessibilityCheckClass;
+import android.accessibility.AccessibilityCheckResultType;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -87,22 +89,23 @@
                         AccessibilityCheckResult.AccessibilityCheckResultType.NOT_RUN, null, 5,
                         null);
 
-        Set<A11yCheckerProto.AccessibilityCheckResultReported> atoms =
-                AccessibilityCheckerUtils.processResults(
-                        mockNodeInfo,
-                        List.of(result1, result2, result3, result4),
-                        null,
+
+        AndroidAccessibilityCheckerResult.Builder resultBuilder =
+                AccessibilityCheckerUtils.getCommonResultBuilder(mockNodeInfo, null,
                         mMockPackageManager,
                         new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                 TEST_A11Y_SERVICE_CLASS_NAME));
+        Set<AndroidAccessibilityCheckerResult> results =
+                AccessibilityCheckerUtils.processResults(mockNodeInfo,
+                        List.of(result1, result2, result3, result4), resultBuilder);
 
-        assertThat(atoms).containsExactly(
-                createAtom("TargetNode", "",
-                        A11yCheckerProto.AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK,
-                        A11yCheckerProto.AccessibilityCheckResultType.WARNING, 1),
-                createAtom("TargetNode", "",
-                        A11yCheckerProto.AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
-                        A11yCheckerProto.AccessibilityCheckResultType.ERROR, 2)
+        assertThat(results).containsExactly(
+                createResult("TargetNode", "",
+                        AccessibilityCheckClass.SPEAKABLE_TEXT_PRESENT_CHECK,
+                        AccessibilityCheckResultType.WARNING_CHECK_RESULT_TYPE, 1),
+                createResult("TargetNode", "",
+                        AccessibilityCheckClass.TOUCH_TARGET_SIZE_CHECK,
+                        AccessibilityCheckResultType.ERROR_CHECK_RESULT_TYPE, 2)
         );
     }
 
@@ -126,16 +129,16 @@
                         TouchTargetSizeCheck.class,
                         AccessibilityCheckResult.AccessibilityCheckResultType.ERROR, null, 2, null);
 
-        Set<A11yCheckerProto.AccessibilityCheckResultReported> atoms =
-                AccessibilityCheckerUtils.processResults(
-                        mockNodeInfo,
-                        List.of(result1, result2),
-                        null,
+        AndroidAccessibilityCheckerResult.Builder resultBuilder =
+                AccessibilityCheckerUtils.getCommonResultBuilder(mockNodeInfo, null,
                         mMockPackageManager,
                         new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
                                 TEST_A11Y_SERVICE_CLASS_NAME));
+        Set<AndroidAccessibilityCheckerResult> results =
+                AccessibilityCheckerUtils.processResults(mockNodeInfo,
+                        List.of(result1, result2), resultBuilder);
 
-        assertThat(atoms).isEmpty();
+        assertThat(results).isEmpty();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
index acf64b6..8e0b2ed 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/a11ychecker/TestUtils.java
@@ -20,6 +20,8 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.when;
 
+import android.accessibility.AccessibilityCheckClass;
+import android.accessibility.AccessibilityCheckResultType;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
@@ -90,20 +92,20 @@
         return accessibilityEvent;
     }
 
-    static A11yCheckerProto.AccessibilityCheckResultReported createAtom(
+    static AndroidAccessibilityCheckerResult createResult(
             String viewIdResourceName,
             String activityName,
-            A11yCheckerProto.AccessibilityCheckClass checkClass,
-            A11yCheckerProto.AccessibilityCheckResultType resultType,
+            AccessibilityCheckClass checkClass,
+            AccessibilityCheckResultType resultType,
             int resultId) {
-        return A11yCheckerProto.AccessibilityCheckResultReported.newBuilder()
+        return AndroidAccessibilityCheckerResult.newBuilder()
                 .setPackageName(TEST_APP_PACKAGE_NAME)
                 .setAppVersionCode(TEST_APP_VERSION_CODE)
                 .setUiElementPath(TEST_APP_PACKAGE_NAME + ":" + viewIdResourceName)
                 .setWindowTitle(TEST_WINDOW_TITLE)
                 .setActivityName(activityName)
                 .setSourceComponentName(new ComponentName(TEST_A11Y_SERVICE_SOURCE_PACKAGE_NAME,
-                        TEST_A11Y_SERVICE_CLASS_NAME).flattenToString())
+                        TEST_A11Y_SERVICE_CLASS_NAME))
                 .setSourceVersionCode(TEST_A11Y_SERVICE_SOURCE_VERSION_CODE)
                 .setResultCheckClass(checkClass)
                 .setResultType(resultType)
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 1cd61e9..e5005d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,6 +44,8 @@
 import android.graphics.PointF;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -56,6 +58,7 @@
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.accessibility.AccessibilityTraceManager;
 import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
 import com.android.server.accessibility.utils.GestureLogParser;
 import com.android.server.testutils.OffsettableClock;
 
@@ -119,6 +122,9 @@
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     /**
      * {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
      * is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -154,18 +160,43 @@
         mHandler = new TestHandler();
         mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
         mTouchExplorer.setNext(mCaptor);
+        // Start TouchExplorer in the state where it has already reset InputDispatcher so that
+        // all tests do not start with an irrelevant ACTION_CANCEL.
+        mTouchExplorer.setHasResetInputDispatcherState(true);
     }
 
     @Test
     public void testOneFingerMove_shouldInjectHoverEvents() {
-        goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
-        // Wait for transiting to touch exploring state.
+        triggerTouchExplorationWithOneFingerDownMoveUp();
+        assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        assertState(STATE_TOUCH_EXPLORING);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_RESET_INPUT_DISPATCHER_BEFORE_FIRST_TOUCH_EXPLORATION)
+    public void testStartTouchExploration_shouldResetInputDispatcherStateWithActionCancel() {
+        // Start TouchExplorer in the state where it has *not yet* reset InputDispatcher.
+        mTouchExplorer.setHasResetInputDispatcherState(false);
+        // Trigger touch exploration twice, with a handler fast-forward in between so TouchExplorer
+        // treats these as two separate interactions.
+        triggerTouchExplorationWithOneFingerDownMoveUp();
+        mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
+        triggerTouchExplorationWithOneFingerDownMoveUp();
+
+        assertCapturedEvents(
+                ACTION_CANCEL, // Only one ACTION_CANCEL before the first touch exploration
+                ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT,
+                ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+        assertState(STATE_TOUCH_EXPLORING);
+    }
+
+    private void triggerTouchExplorationWithOneFingerDownMoveUp() {
+        send(downEvent());
+        // Fast forward so that TouchExplorer's timeouts transition us to the touch exploring state.
         mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
         moveEachPointers(mLastEvent, p(10, 10));
         send(mLastEvent);
-        goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
-        assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
-        assertState(STATE_TOUCH_EXPLORING);
+        send(upEvent());
     }
 
     /**
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 957ee06..598d3a3 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
@@ -23,6 +23,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
+import static android.view.MotionEvent.TOOL_TYPE_FINGER;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
@@ -1414,6 +1416,49 @@
     }
 
     @Test
+    public void testSynthesizedGestureEventsDoNotMoveMagnifierViewport() {
+        final EventCaptor eventCaptor = new EventCaptor();
+        mMgh.setNext(eventCaptor);
+
+        float centerX =
+                (INITIAL_MAGNIFICATION_BOUNDS.left + INITIAL_MAGNIFICATION_BOUNDS.width()) / 2.0f;
+        float centerY =
+                (INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
+        float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
+        mFullScreenMagnificationController.setScaleAndCenter(
+                DISPLAY_0, centerX, centerY, scale, /* animate= */ false, 1);
+        centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
+        centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
+
+        // Second finger down on trackpad starts a synthesized two-finger swipe with source
+        // mouse.
+        MotionEvent downEvent = motionEvent(centerX, centerY, ACTION_DOWN,
+                TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+        send(downEvent, InputDevice.SOURCE_MOUSE);
+        fastForward(20);
+
+        // Two-finger swipe creates a synthesized move event, and shouldn't impact magnifier
+        // viewport.
+        MotionEvent moveEvent = motionEvent(centerX - 42, centerY - 42, ACTION_MOVE,
+                TOOL_TYPE_FINGER, CLASSIFICATION_TWO_FINGER_SWIPE);
+        send(moveEvent, InputDevice.SOURCE_MOUSE);
+        fastForward(20);
+
+        assertThat(mFullScreenMagnificationController.getCenterX(DISPLAY_0)).isEqualTo(centerX);
+        assertThat(mFullScreenMagnificationController.getCenterY(DISPLAY_0)).isEqualTo(centerY);
+
+        // The events were not consumed by magnifier.
+        assertThat(eventCaptor.mEvents.size()).isEqualTo(2);
+        assertThat(eventCaptor.mEvents.get(0).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+        assertThat(eventCaptor.mEvents.get(1).getSource()).isEqualTo(InputDevice.SOURCE_MOUSE);
+
+        final List<Integer> expectedActions = new ArrayList();
+        expectedActions.add(Integer.valueOf(ACTION_DOWN));
+        expectedActions.add(Integer.valueOf(ACTION_MOVE));
+        assertActionsInOrder(eventCaptor.mEvents, expectedActions);
+    }
+
+    @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
     public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
         runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
@@ -2130,6 +2175,30 @@
         return MotionEvent.obtain(mLastDownTime, mClock.now(), action, x, y, 0);
     }
 
+    private MotionEvent motionEvent(float x, float y, int action, int toolType,
+            int classification) {
+        // Create a generic motion event to populate the parameters.
+        MotionEvent event = motionEvent(x, y, action);
+        int pointerCount = event.getPointerCount();
+        MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
+        MotionEvent.PointerProperties[] properties =
+                new MotionEvent.PointerProperties[pointerCount];
+        for (int i = 0; i < pointerCount; i++) {
+            properties[i] = new MotionEvent.PointerProperties();
+            event.getPointerProperties(i, properties[i]);
+            properties[i].toolType = toolType;
+            coords[i] = new MotionEvent.PointerCoords();
+            event.getPointerCoords(i, coords[i]);
+        }
+        // Apply the custom classification.
+        return MotionEvent.obtain(event.getDownTime(), event.getEventTime(), action,
+                /*pointerCount=*/1, properties, coords,
+                event.getMetaState(), event.getButtonState(),
+                event.getXPrecision(), event.getYPrecision(), event.getDeviceId(),
+                event.getEdgeFlags(), event.getSource(), event.getDisplayId(), event.getFlags(),
+                classification);
+    }
+
     private MotionEvent mouseEvent(float x, float y, int action) {
         return fromMouse(motionEvent(x, y, action));
     }
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 3931580..d80a1f0 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
@@ -18,13 +18,20 @@
 
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_HOVER_MOVE;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static junit.framework.Assert.assertFalse;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.testng.AssertJUnit.assertTrue;
 
 import android.annotation.NonNull;
+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.provider.Settings;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -32,8 +39,10 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.accessibility.AccessibilityTraceManager;
+import com.android.server.accessibility.Flags;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -45,6 +54,9 @@
 @RunWith(AndroidJUnit4.class)
 public class MagnificationGestureHandlerTest {
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private TestMagnificationGestureHandler mMgh;
     private static final int DISPLAY_0 = 0;
     private static final int FULLSCREEN_MODE =
@@ -81,6 +93,66 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
+        final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+        mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
+
+        mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
+
+        try {
+            assertTrue(mMgh.mIsHandleMouseOrStylusEventCalled);
+        } finally {
+            mouseEvent.recycle();
+        }
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
+        final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+        stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
+
+        mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
+
+        try {
+            assertTrue(mMgh.mIsHandleMouseOrStylusEventCalled);
+        } finally {
+            stylusEvent.recycle();
+        }
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
+        final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+        mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
+
+        mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
+
+        try {
+            assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
+        } finally {
+            mouseEvent.recycle();
+        }
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE)
+    public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
+        final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
+        stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
+
+        mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
+
+        try {
+            assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
+        } finally {
+            stylusEvent.recycle();
+        }
+    }
+
+    @Test
     public void onMotionEvent_downEvent_handleInteractionStart() {
         final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
         downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
@@ -125,6 +197,7 @@
     private static class TestMagnificationGestureHandler extends MagnificationGestureHandler {
 
         boolean mIsInternalMethodCalled = false;
+        boolean mIsHandleMouseOrStylusEventCalled = false;
 
         TestMagnificationGestureHandler(int displayId, boolean detectSingleFingerTripleTap,
                 boolean detectTwoFingerTripleTap,
@@ -135,6 +208,11 @@
         }
 
         @Override
+        void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+            mIsHandleMouseOrStylusEventCalled = true;
+        }
+
+        @Override
         void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
             mIsInternalMethodCalled = true;
         }
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 0c92abc..b9ce8ad 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1163,16 +1163,6 @@
 
         verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
         Bundle result = mBundleCaptor.getValue();
-        Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
-        assertNotNull(sessionBundle);
-        // Assert that session bundle is decrypted and hence data is visible.
-        assertEquals(AccountManagerServiceTestFixtures.SESSION_DATA_VALUE_1,
-                sessionBundle.getString(AccountManagerServiceTestFixtures.SESSION_DATA_NAME_1));
-        // Assert finishSessionAsUser added calling uid and pid into the sessionBundle
-        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_UID));
-        assertTrue(sessionBundle.containsKey(AccountManager.KEY_CALLER_PID));
-        assertEquals(sessionBundle.getString(
-                AccountManager.KEY_ANDROID_PACKAGE_NAME), "APCT.package");
 
         // Verify response data
         assertNull(result.getString(AccountManager.KEY_AUTHTOKEN, null));
@@ -2121,12 +2111,6 @@
                 result.getString(AccountManager.KEY_ACCOUNT_NAME));
         assertEquals(AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1,
                 result.getString(AccountManager.KEY_ACCOUNT_TYPE));
-
-        Bundle optionBundle = result.getParcelable(
-                AccountManagerServiceTestFixtures.KEY_OPTIONS_BUNDLE);
-        // Assert addAccountAsUser added calling uid and pid into the option bundle
-        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_UID));
-        assertTrue(optionBundle.containsKey(AccountManager.KEY_CALLER_PID));
     }
 
     @SmallTest
@@ -3457,6 +3441,52 @@
                 + (readTotalTime.doubleValue() / readerCount / loopSize));
     }
 
+    @SmallTest
+    public void testSanitizeBundle_expectedFields() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
+        bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "type");
+        bundle.putString(AccountManager.KEY_AUTHTOKEN, "token");
+        bundle.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, "label");
+        bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "error message");
+        bundle.putString(AccountManager.KEY_PASSWORD, "password");
+        bundle.putString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN, "status");
+
+        bundle.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 123L);
+        bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+        bundle.putInt(AccountManager.KEY_ERROR_CODE, 456);
+
+        Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_TYPE), "type");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTHTOKEN), "token");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_AUTH_TOKEN_LABEL), "label");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ERROR_MESSAGE), "error message");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_PASSWORD), "password");
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN), "status");
+
+        assertEquals(sanitizedBundle.getLong(
+                AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, 0), 123L);
+        assertEquals(sanitizedBundle.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false), true);
+        assertEquals(sanitizedBundle.getInt(AccountManager.KEY_ERROR_CODE, 0), 456);
+    }
+
+    @SmallTest
+    public void testSanitizeBundle_filtersUnexpectedFields() throws Exception {
+        Bundle bundle = new Bundle();
+        bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "name");
+        bundle.putString("unknown_key", "value");
+        Bundle sessionBundle = new Bundle();
+        bundle.putBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE, sessionBundle);
+
+        Bundle sanitizedBundle = AccountManagerService.sanitizeBundle(bundle);
+
+        assertEquals(sanitizedBundle.getString(AccountManager.KEY_ACCOUNT_NAME), "name");
+        assertFalse(sanitizedBundle.containsKey("unknown_key"));
+        // It is a valid response from Authenticator which will be accessed using original Bundle
+        assertFalse(sanitizedBundle.containsKey(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE));
+    }
+
     private void waitForCyclicBarrier(CyclicBarrier cyclicBarrier) {
         try {
             cyclicBarrier.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
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 dbab54b..a25621a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_TRUE;
+import static android.app.ActivityManager.STOP_USER_ON_SWITCH_FALSE;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
@@ -53,6 +55,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
@@ -87,6 +90,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IRemoteCallback;
+import android.os.IpcDataCache;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManagerInternal;
@@ -103,6 +107,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.AlarmManagerInternal;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 import com.android.server.am.UserState.KeyEvictedCallback;
@@ -122,6 +127,7 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -141,7 +147,6 @@
  */
 @SmallTest
 @Presubmit
-
 public class UserControllerTest {
     // Use big enough user id to avoid picking up already active user id.
     private static final int TEST_USER_ID = 100;
@@ -193,6 +198,9 @@
     @Before
     public void setUp() throws Exception {
         runWithDexmakerShareClassLoader(() -> {
+            // Disable binder caches in this process.
+            IpcDataCache.disableForTestMode();
+
             mInjector = spy(new TestInjector(getInstrumentation().getTargetContext()));
             doNothing().when(mInjector).clearAllLockedTasks(anyString());
             doNothing().when(mInjector).startHomeActivity(anyInt(), anyString());
@@ -201,7 +209,8 @@
             doNothing().when(mInjector).systemServiceManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).systemServiceManagerOnUserCompletedEvent(
                     anyInt(), anyInt());
-            doNothing().when(mInjector).activityManagerForceStopPackage(anyInt(), anyString());
+            doNothing().when(mInjector).activityManagerForceStopUserPackages(anyInt(),
+                    anyString(), anyBoolean());
             doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
             doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
             doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
@@ -588,6 +597,7 @@
     @Test
     public void testScheduleStopOfBackgroundUser_switch() {
         mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+        assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
 
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                 /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -637,6 +647,7 @@
     @Test
     public void testScheduleStopOfBackgroundUser_startInBackground() throws Exception {
         mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+        assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
 
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                 /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -676,6 +687,7 @@
     @Test
     public void testScheduleStopOfBackgroundUser_rescheduleWhenGuest() throws Exception {
         mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+        assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
 
         mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
                 /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
@@ -727,6 +739,40 @@
                 mUserController.getRunningUsersLU());
     }
 
+    /** Test scheduling stopping of background users - reschedule if user with a scheduled alarm. */
+    @Test
+    public void testScheduleStopOfBackgroundUser_rescheduleIfAlarm() throws Exception {
+        mSetFlagsRule.enableFlags(android.multiuser.Flags.FLAG_SCHEDULE_STOP_OF_BACKGROUND_USER);
+        assumeFalse(UserManager.isVisibleBackgroundUsersEnabled());
+
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 10, /* delayUserDataLocking= */ false,
+                /* backgroundUserScheduledStopTimeSecs= */ 2);
+
+        setUpAndStartUserInBackground(TEST_USER_ID);
+        assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID),
+                new HashSet<>(mUserController.getRunningUsersLU()));
+
+        // Initially, the background user has an alarm that will fire soon. So don't stop the user.
+        when(mInjector.mAlarmManagerInternal.getNextAlarmTriggerTimeForUser(eq(TEST_USER_ID)))
+                .thenReturn(System.currentTimeMillis() + Duration.ofMinutes(2).toMillis());
+        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+        assertEquals(newHashSet(SYSTEM_USER_ID, TEST_USER_ID),
+                new HashSet<>(mUserController.getRunningUsersLU()));
+
+        // Now, that alarm is gone and the next alarm isn't for a long time. Do stop the user.
+        when(mInjector.mAlarmManagerInternal.getNextAlarmTriggerTimeForUser(eq(TEST_USER_ID)))
+                .thenReturn(System.currentTimeMillis() + Duration.ofDays(1).toMillis());
+        assertAndProcessScheduledStopBackgroundUser(true, TEST_USER_ID);
+        assertEquals(newHashSet(SYSTEM_USER_ID),
+                new HashSet<>(mUserController.getRunningUsersLU()));
+
+        // No-one is scheduled to stop anymore.
+        assertAndProcessScheduledStopBackgroundUser(false, null);
+        verify(mInjector.mAlarmManagerInternal, never())
+                .getNextAlarmTriggerTimeForUser(eq(SYSTEM_USER_ID));
+    }
+
     /**
      * Process queued SCHEDULED_STOP_BACKGROUND_USER_MSG message, if expected.
      * @param userId the user we are checking to see whether it is scheduled.
@@ -936,6 +982,61 @@
                 new HashSet<>(mUserController.getRunningUsersLU()));
     }
 
+    @Test
+    public void testEarlyPackageKillEnabledForUserSwitch_enabled() {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        assertTrue(mUserController
+                .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+    }
+
+    @Test
+    public void testEarlyPackageKillEnabledForUserSwitch_withoutDelayUserDataLocking() {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ false,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        assertFalse(mUserController
+                .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+    }
+
+    @Test
+    public void testEarlyPackageKillEnabledForUserSwitch_withPrevSystemUser() {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        assertFalse(mUserController
+                .isEarlyPackageKillEnabledForUserSwitch(SYSTEM_USER_ID, TEST_USER_ID1));
+    }
+
+    @Test
+    public void testEarlyPackageKillEnabledForUserSwitch_stopUserOnSwitchModeOn() {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ false,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        mUserController.setStopUserOnSwitch(STOP_USER_ON_SWITCH_TRUE);
+
+        assertTrue(mUserController
+                .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+    }
+
+    @Test
+    public void testEarlyPackageKillEnabledForUserSwitch_stopUserOnSwitchModeOff() {
+        mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+                /* maxRunningUsers= */ 4, /* delayUserDataLocking= */ true,
+                /* backgroundUserScheduledStopTimeSecs= */ -1);
+
+        mUserController.setStopUserOnSwitch(STOP_USER_ON_SWITCH_FALSE);
+
+        assertFalse(mUserController
+                .isEarlyPackageKillEnabledForUserSwitch(TEST_USER_ID, TEST_USER_ID1));
+    }
+
+
     /**
      * Test that, in getRunningUsersLU, parents come after their profile, even if the profile was
      * started afterwards.
@@ -1689,6 +1790,7 @@
         private final WindowManagerService mWindowManagerMock;
         private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
         private final PowerManagerInternal mPowerManagerInternal;
+        private final AlarmManagerInternal mAlarmManagerInternal;
         private final KeyguardManager mKeyguardManagerMock;
         private final LockPatternUtils mLockPatternUtilsMock;
 
@@ -1711,6 +1813,7 @@
             mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
             mStorageManagerMock = mock(IStorageManager.class);
             mPowerManagerInternal = mock(PowerManagerInternal.class);
+            mAlarmManagerInternal = mock(AlarmManagerInternal.class);
             mKeyguardManagerMock = mock(KeyguardManager.class);
             when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
             mLockPatternUtilsMock = mock(LockPatternUtils.class);
@@ -1781,6 +1884,11 @@
         }
 
         @Override
+        AlarmManagerInternal getAlarmManagerInternal() {
+            return mAlarmManagerInternal;
+        }
+
+        @Override
         KeyguardManager getKeyguardManager() {
             return mKeyguardManagerMock;
         }
diff --git a/services/tests/servicestests/src/com/android/server/appfunctions/OWNERS b/services/tests/servicestests/src/com/android/server/appfunctions/OWNERS
new file mode 100644
index 0000000..7fa8917
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appfunctions/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 1627156
+include platform/frameworks/base:/core/java/android/app/appfunctions/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt b/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt
index 79766f8..1566362 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt
+++ b/services/tests/servicestests/src/com/android/server/appwidget/ApiCounterTest.kt
@@ -25,6 +25,7 @@
     private companion object {
         const val RESET_INTERVAL_MS = 10L
         const val MAX_CALLS_PER_INTERVAL = 2
+        const val MAX_PROVIDERS = 10
     }
 
     private var currentTime = 0L
@@ -34,7 +35,9 @@
             /* uid= */ 123,
             ComponentName("com.android.server.appwidget", "FakeProviderClass")
         )
-    private val counter = ApiCounter(RESET_INTERVAL_MS, MAX_CALLS_PER_INTERVAL) { currentTime }
+    private val counter = ApiCounter(RESET_INTERVAL_MS, MAX_CALLS_PER_INTERVAL, MAX_PROVIDERS) {
+        currentTime
+    }
 
     @Test
     fun tryApiCall() {
@@ -58,4 +61,20 @@
         counter.remove(id)
         assertThat(counter.tryApiCall(id)).isTrue()
     }
+
+    @Test
+    fun maxProviders() {
+        for (i in 0 until MAX_PROVIDERS) {
+            for (j in 0 until MAX_CALLS_PER_INTERVAL) {
+                assertThat(counter.tryApiCall(providerId(i))).isTrue()
+            }
+        }
+        assertThat(counter.tryApiCall(providerId(MAX_PROVIDERS))).isFalse()
+        // remove will allow another provider to be added
+        counter.remove(providerId(0))
+        assertThat(counter.tryApiCall(providerId(MAX_PROVIDERS))).isTrue()
+    }
+
+    private fun providerId(i: Int) =
+        AppWidgetServiceImpl.ProviderId(/* uid= */ i, id.componentName)
 }
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 e45ab31..beed0a3d 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -111,6 +111,9 @@
     private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
             AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
 
+    /** Choose a default stream volume value which does not depend on min/max. */
+    private static final int DEFAULT_STREAM_VOLUME = 2;
+
     @Rule
     public final MockitoRule mockito = MockitoJUnit.rule();
 
@@ -144,6 +147,8 @@
 
     private TestLooper mTestLooper;
 
+    private boolean mIsAutomotive;
+
     public static final int[] BASIC_VOLUME_BEHAVIORS = {
             AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
             AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
@@ -232,9 +237,10 @@
                 || packageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
         final boolean isSingleVolume = mContext.getResources().getBoolean(
                 Resources.getSystem().getIdentifier("config_single_volume", "bool", "android"));
-        final boolean automotiveHardened = mContext.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_AUTOMOTIVE) && autoPublicVolumeApiHardening();
-        assumeFalse("Skipping test for fixed, TV, single volume and auto devices",
+        mIsAutomotive = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        final boolean automotiveHardened = mIsAutomotive && autoPublicVolumeApiHardening();
+        assumeFalse("Skipping test for fixed, TV, single volume and auto hardened devices",
                 useFixedVolume || isTelevision || isSingleVolume || automotiveHardened);
 
         InstrumentationRegistry.getInstrumentation().getUiAutomation()
@@ -249,15 +255,14 @@
                 {STREAM_MUSIC, STREAM_NOTIFICATION, STREAM_RING, STREAM_ALARM, STREAM_SYSTEM,
                         STREAM_VOICE_CALL, STREAM_ACCESSIBILITY};
         for (int streamType : usedStreamTypes) {
-            final int streamVolume = (mAm.getStreamMinVolume(streamType) + mAm.getStreamMaxVolume(
-                    streamType)) / 2;
-
-            mAudioService.setStreamVolume(streamType, streamVolume, /*flags=*/0,
+            mAudioService.setStreamVolume(streamType, DEFAULT_STREAM_VOLUME, /*flags=*/0,
                     mContext.getOpPackageName());
         }
 
-        mAudioService.setRingerModeInternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
-        mAudioService.setRingerModeExternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+        if (!mIsAutomotive) {
+            mAudioService.setRingerModeInternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+            mAudioService.setRingerModeExternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+        }
     }
 
     private AudioVolumeGroup getStreamTypeVolumeGroup(int streamType) {
@@ -297,6 +302,7 @@
 
     @Test
     public void setStreamRingVolume0_setsRingerModeVibrate() throws Exception {
+        assumeFalse("Skipping ringer mode test on automotive", mIsAutomotive);
         mAudioService.setStreamVolume(STREAM_RING, 0, /*flags=*/0,
                 mContext.getOpPackageName());
         mTestLooper.dispatchAll();
@@ -462,6 +468,7 @@
 
     @Test
     public void flagAllowRingerModes_onSystemStreams_changesMode() throws Exception {
+        assumeFalse("Skipping ringer mode test on automotive", mIsAutomotive);
         mAudioService.setStreamVolume(STREAM_SYSTEM,
                 mAudioService.getStreamMinVolume(STREAM_SYSTEM), /*flags=*/0,
                 mContext.getOpPackageName());
@@ -476,6 +483,7 @@
 
     @Test
     public void flagAllowRingerModesAbsent_onNonSystemStreams_noModeChange() throws Exception {
+        assumeFalse("Skipping ringer mode test on automotive", mIsAutomotive);
         mAudioService.setStreamVolume(STREAM_MUSIC,
                 mAudioService.getStreamMinVolume(STREAM_MUSIC), /*flags=*/0,
                 mContext.getOpPackageName());
@@ -544,17 +552,23 @@
         mAudioService.setDeviceVolume(volMin, usbDevice, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
 
-        assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice,
-                mContext.getOpPackageName()), volMin);
+        if (!mIsAutomotive) {
+            // there is a min/max index mismatch in automotive
+            assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice,
+                    mContext.getOpPackageName()), volMin);
+        }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+                eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
 
         mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
         mTestLooper.dispatchAll();
-        assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice,
-                mContext.getOpPackageName()), volMid);
+        if (!mIsAutomotive) {
+            // there is a min/max index mismatch in automotive
+            assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice,
+                    mContext.getOpPackageName()), volMid);
+        }
         verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
-                STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+                eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
new file mode 100644
index 0000000..75258f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/autofill/PresentationEventLoggerTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.autofill;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class PresentationEventLoggerTest {
+
+    @Test
+    public void testViewEntered() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        AutofillId id = new AutofillId(13);
+        AutofillValue initialValue = AutofillValue.forText("hello");
+        AutofillValue lastValue = AutofillValue.forText("hello world");
+        ViewState vState = new ViewState(id, null, 0, false);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetFocusedId(id);
+        pEventLogger.onFieldTextUpdated(vState, initialValue);
+        pEventLogger.onFieldTextUpdated(vState, lastValue);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mFieldFirstLength).isEqualTo(initialValue.getTextValue().length());
+        assertThat(event.mFieldLastLength).isEqualTo(lastValue.getTextValue().length());
+        assertThat(event.mFieldModifiedFirstTimestampMs).isNotEqualTo(-1);
+        assertThat(event.mFieldModifiedLastTimestampMs).isNotEqualTo(-1);
+    }
+
+    @Test
+    public void testViewAutofilled() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        String newTextValue = "hello";
+        AutofillValue value = AutofillValue.forText(newTextValue);
+        AutofillId id = new AutofillId(13);
+        ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetFocusedId(id);
+        pEventLogger.onFieldTextUpdated(vState, value);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mFieldFirstLength).isEqualTo(newTextValue.length());
+        assertThat(event.mFieldLastLength).isEqualTo(newTextValue.length());
+        assertThat(event.mAutofilledTimestampMs).isNotEqualTo(-1);
+        assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
+        assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
+    }
+
+    @Test
+    public void testModifiedOnDifferentView() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        String newTextValue = "hello";
+        AutofillValue value = AutofillValue.forText(newTextValue);
+        AutofillId id = new AutofillId(13);
+        ViewState vState = new ViewState(id, null, ViewState.STATE_AUTOFILLED, false);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.onFieldTextUpdated(vState, value);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mFieldFirstLength).isEqualTo(-1);
+        assertThat(event.mFieldLastLength).isEqualTo(-1);
+        assertThat(event.mFieldModifiedFirstTimestampMs).isEqualTo(-1);
+        assertThat(event.mFieldModifiedLastTimestampMs).isEqualTo(-1);
+        assertThat(event.mAutofilledTimestampMs).isEqualTo(-1);
+    }
+
+    @Test
+    public void testSetCountShown() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.logWhenDatasetShown(7);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mCountShown).isEqualTo(7);
+        assertThat(event.mNoPresentationReason)
+                .isEqualTo(PresentationStatsEventLogger.NOT_SHOWN_REASON_ANY_SHOWN);
+    }
+
+    @Test
+    public void testFillDialogShownThenInline() {
+        PresentationStatsEventLogger pEventLogger =
+                PresentationStatsEventLogger.createPresentationLog(1, 1, 1);
+
+        pEventLogger.startNewEvent();
+        pEventLogger.maybeSetDisplayPresentationType(3);
+        pEventLogger.maybeSetDisplayPresentationType(2);
+
+        PresentationStatsEventLogger.PresentationStatsEventInternal event =
+                pEventLogger.getInternalEvent().get();
+        assertThat(event).isNotNull();
+        assertThat(event.mDisplayPresentationType).isEqualTo(3);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
index bc2fd73..2f7b8d2 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java
@@ -200,7 +200,8 @@
                     eq(TEST_REQUEST_ID),
                     eq(sensor.getCookie()),
                     anyBoolean() /* allowBackgroundAuthentication */,
-                    anyBoolean() /* isForLegacyFingerprintManager */);
+                    anyBoolean() /* isForLegacyFingerprintManager */,
+                    eq(false) /* isMandatoryBiometrics */);
         }
 
         final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index a4222ff..6b8e414 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -79,6 +79,7 @@
 import android.os.RemoteException;
 import android.os.UserManager;
 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;
@@ -635,7 +636,8 @@
                 eq(TEST_REQUEST_ID),
                 cookieCaptor.capture() /* cookie */,
                 anyBoolean() /* allowBackgroundAuthentication */,
-                anyBoolean() /* isForLegacyFingerprintManager */);
+                anyBoolean() /* isForLegacyFingerprintManager */,
+                eq(false) /* isMandatoryBiometrics */);
 
         // onReadyForAuthentication, mAuthSession state OK
         mBiometricService.mImpl.onReadyForAuthentication(TEST_REQUEST_ID, cookieCaptor.getValue());
@@ -1488,15 +1490,30 @@
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
     public void testCanAuthenticate_whenLockoutTimed() throws Exception {
         testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_TIMED);
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
     public void testCanAuthenticate_whenLockoutPermanent() throws Exception {
         testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_PERMANENT);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testCanAuthenticate_whenLockoutTimed_returnsLockoutError() throws Exception {
+        testCanAuthenticate_whenLockedOut_returnLockoutError(LockoutTracker.LOCKOUT_TIMED);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testCanAuthenticate_whenLockoutPermanent_returnsLockoutError() throws Exception {
+        testCanAuthenticate_whenLockedOut_returnLockoutError(LockoutTracker.LOCKOUT_PERMANENT);
+    }
+
+    @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
     private void testCanAuthenticate_whenLockedOut(@LockoutTracker.LockoutMode int lockoutMode)
             throws Exception {
         // When only biometric is requested, and sensor is strong enough
@@ -1510,6 +1527,21 @@
                 invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
     }
 
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    private void testCanAuthenticate_whenLockedOut_returnLockoutError(
+            @LockoutTracker.LockoutMode int lockoutMode)
+            throws Exception {
+        // When only biometric is requested, and sensor is strong enough
+        setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+        when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+                .thenReturn(lockoutMode);
+
+        // Lockout is not considered an error for BiometricManager#canAuthenticate
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_LOCKOUT,
+                invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
+    }
+
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
     public void testCanAuthenticate_whenMandatoryBiometricsRequested()
@@ -1529,7 +1561,7 @@
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
 
-        assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
                 invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
     }
 
@@ -1572,7 +1604,7 @@
 
         setupAuthForOnly(TYPE_CREDENTIAL, Authenticators.DEVICE_CREDENTIAL);
 
-        assertEquals(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
                 invokeCanAuthenticate(mBiometricService, Authenticators.MANDATORY_BIOMETRICS));
 
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
index 240da9f..760d38e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java
@@ -231,7 +231,7 @@
 
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
-    public void testMandatoryBiometricsStatus_whenRequirementsNotSatisfiedAndSensorAvailable()
+    public void testMandatoryBiometricsAndStrongBiometricsStatus_whenRequirementsNotSatisfied()
             throws Exception {
         when(mTrustManager.isInSignificantPlace()).thenReturn(true);
 
@@ -246,6 +246,40 @@
         assertThat(preAuthInfo.eligibleSensors).hasSize(1);
     }
 
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testMandatoryBiometricsStatus_whenRequirementsNotSatisfiedAndSensorAvailable()
+            throws Exception {
+        when(mTrustManager.isInSignificantPlace()).thenReturn(true);
+
+        final BiometricSensor sensor = getFaceSensor();
+        final PromptInfo promptInfo = new PromptInfo();
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+                mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+
+        assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(
+                BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
+        assertThat(preAuthInfo.eligibleSensors).hasSize(0);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testMandatoryBiometricsNegativeButtonText_whenSet()
+            throws Exception {
+        when(mTrustManager.isInSignificantPlace()).thenReturn(false);
+
+        final BiometricSensor sensor = getFaceSensor();
+        final PromptInfo promptInfo = new PromptInfo();
+        promptInfo.setAuthenticators(BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
+        promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME);
+        final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager,
+                mSettingObserver, List.of(sensor), 0 /* userId */, promptInfo, TEST_PACKAGE_NAME,
+                false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager);
+        assertThat(promptInfo.getNegativeButtonText()).isEqualTo(TEST_PACKAGE_NAME);
+    }
+
     private BiometricSensor getFingerprintSensor() {
         BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT,
                 TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG,
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index cb75e1a..14cb22d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -26,16 +26,25 @@
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.Flags;
 import android.hardware.biometrics.PromptInfo;
 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 androidx.test.filters.SmallTest;
 
+import org.junit.Rule;
 import org.junit.Test;
 
 @Presubmit
 @SmallTest
 public class UtilsTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
 
     @Test
     public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
@@ -215,7 +224,8 @@
     }
 
     @Test
-    public void testBiometricConstantsConversion() {
+    @RequiresFlagsDisabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testBiometricConstantsConversionLegacy() {
         final int[][] testCases = {
                 {BiometricConstants.BIOMETRIC_SUCCESS,
                         BiometricManager.BIOMETRIC_SUCCESS},
@@ -240,6 +250,34 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS)
+    public void testBiometricConstantsConversion() {
+        final int[][] testCases = {
+                {BiometricConstants.BIOMETRIC_SUCCESS,
+                        BiometricManager.BIOMETRIC_SUCCESS},
+                {BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+                        BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+                {BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL,
+                        BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED},
+                {BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+                        BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE},
+                {BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
+                        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE},
+                {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+                        BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
+                {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+                        BiometricManager.BIOMETRIC_ERROR_LOCKOUT},
+                {BiometricConstants.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE,
+                        BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE}
+        };
+
+        for (int i = 0; i < testCases.length; i++) {
+            assertEquals(testCases[i][1],
+                    Utils.biometricConstantsToBiometricManager(testCases[i][0]));
+        }
+    }
+
+    @Test
     public void testGetAuthenticationTypeForResult_getsCorrectType() {
         assertEquals(Utils.getAuthenticationTypeForResult(
                 BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
index 238a928..8f23ab9 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java
@@ -236,6 +236,13 @@
     }
 
     @Test
+    public void testFingerprintsLoe() {
+        mLogger = createLogger();
+        mLogger.logFingerprintsLoe();
+        verify(mSink).reportFingerprintsLoe(eq(DEFAULT_MODALITY));
+    }
+
+    @Test
     public void testALSCallback() {
         mLogger = createLogger();
         final CallbackWithProbe<Probe> callback =
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index 4604b31..613cb20 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -98,7 +98,8 @@
                 @NonNull ClientMonitorCallbackConverter callback) {
             super(context, lazyDaemon, token, callback, 0 /* userId */, "Test", 0 /* cookie */,
                     TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */,
-                    mock(BiometricLogger.class), mock(BiometricContext.class));
+                    mock(BiometricLogger.class), mock(BiometricContext.class),
+                    false /* isMandatoryBiometrics */);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index ffc7811..4f07380 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -62,7 +62,8 @@
             extends HalClientMonitor<T> {
         public InterruptableMonitor() {
             super(null, null, null, null, 0, null, 0, 0,
-                    mock(BiometricLogger.class), mock(BiometricContext.class));
+                    mock(BiometricLogger.class), mock(BiometricContext.class),
+                    false /* isMandatoryBiometrics */);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 36a7b3d..90c07d4 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -1051,6 +1051,11 @@
         public String getAttributionTag() {
             return null;
         }
+
+        @Override
+        public boolean isMandatoryBiometrics() {
+            return false;
+        }
     }
 
     private static class TestAuthenticationClient
@@ -1176,7 +1181,7 @@
                 @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) {
             super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, TAG,
                     cookie, TEST_SENSOR_ID, mock(BiometricLogger.class),
-                    mock(BiometricContext.class));
+                    mock(BiometricContext.class), false /* isMandatoryBiometrics */);
             mProtoEnum = protoEnum;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
index f477682..6ac95c8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java
@@ -109,6 +109,8 @@
     private AidlResponseHandler mAidlResponseHandler;
     @Mock
     private AuthenticationStateListeners mAuthenticationStateListeners;
+    @Mock
+    private BiometricUtils<Face> mBiometricUtils;
     @Captor
     private ArgumentCaptor<OperationContextExt> mOperationContextCaptor;
     @Captor
@@ -213,7 +215,7 @@
                 mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */,
                 true /* debugConsent */,
                 (new FaceEnrollOptions.Builder()).setEnrollReason(ENROLL_SOURCE).build(),
-                mAuthenticationStateListeners);
+                mAuthenticationStateListeners, mBiometricUtils);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
index 6780e60..bf97086 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java
@@ -50,6 +50,7 @@
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.UserSwitchProvider;
+import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -90,6 +91,8 @@
     private AidlSession mCurrentSession;
     @Mock
     private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+    @Mock
+    private FaceUtils mBiometricUtils;
 
     private final TestLooper mLooper = new TestLooper();
     private final LockoutCache mLockoutCache = new LockoutCache();
@@ -114,7 +117,7 @@
                 mUserSwitchProvider);
         mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
                 mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
-                mAidlResponseHandlerCallback);
+                mAidlResponseHandlerCallback, mBiometricUtils);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
index 4248e5e..24ce569 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java
@@ -206,7 +206,7 @@
                 new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */,
                 SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */,
                 false /* debugConsent */, (new FaceEnrollOptions.Builder()).build(),
-                mAuthenticationStateListeners));
+                mAuthenticationStateListeners, mBiometricUtils));
         mLooper.dispatchAll();
 
         verify(mAidlResponseHandlerCallback).onEnrollSuccess();
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
index 242880c..7dcf841 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java
@@ -182,13 +182,22 @@
     public void invalidBiometricUserState() throws Exception {
         mClient =  createClient();
 
+        final List<Fingerprint> templates = List.of(
+                new Fingerprint("one", 1, 1),
+                new Fingerprint("two", 2, 1),
+                new Fingerprint("three", 3, 1)
+        );
+
         final List<Fingerprint> list = new ArrayList<>();
         doReturn(true).when(mFingerprintUtils)
                 .hasValidBiometricUserState(mContext, 2);
         doReturn(list).when(mFingerprintUtils).getBiometricsForUser(mContext, 2);
 
         mClient.start(mCallback);
-        mClient.onEnumerationResult(null, 0);
+        for (int i = templates.size() - 1; i >= 0; i--) {
+            mClient.getCurrentEnumerateClient().onEnumerationResult(templates.get(i), i);
+        }
+        verify(mLogger).logFingerprintsLoe();
         verify(mFingerprintUtils).deleteStateForUser(2);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
index 698db2e..4ef8782 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java
@@ -51,6 +51,7 @@
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.UserSwitchProvider;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import org.junit.Before;
@@ -96,6 +97,8 @@
     private HandlerThread mThread;
     @Mock
     AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback;
+    @Mock
+    private FingerprintUtils mBiometricUtils;
 
     private final TestLooper mLooper = new TestLooper();
     private final LockoutCache mLockoutCache = new LockoutCache();
@@ -121,7 +124,7 @@
                 mUserSwitchProvider);
         mHalCallback = new AidlResponseHandler(mContext, mScheduler, SENSOR_ID, USER_ID,
                 mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator,
-                mAidlResponseHandlerCallback);
+                mAidlResponseHandlerCallback, mBiometricUtils);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 9317d06..1a593dd 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -37,7 +37,6 @@
 
 import android.app.WindowConfiguration;
 import android.companion.virtual.IVirtualDeviceIntentInterceptor;
-import android.companion.virtual.VirtualDeviceManager;
 import android.content.AttributionSource;
 import android.content.ComponentName;
 import android.content.Context;
@@ -94,15 +93,9 @@
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Mock
-    private VirtualDeviceManager.ActivityListener mActivityListener;
-    @Mock
-    private GenericWindowPolicyController.IntentListenerCallback mIntentListenerCallback;
-    @Mock
-    private GenericWindowPolicyController.ActivityBlockedCallback mActivityBlockedCallback;
+    private GenericWindowPolicyController.ActivityListener mActivityListener;
     @Mock
     private GenericWindowPolicyController.RunningAppsChangedListener mRunningAppsChangedListener;
-    @Mock
-    private GenericWindowPolicyController.SecureWindowCallback mSecureWindowCallback;
 
     @Before
     public void setUp() throws Exception {
@@ -247,6 +240,21 @@
     }
 
     @Test
+    public void addActivityPolicyPackageExemption_openBlockedOnVirtualDisplay_isBlocked() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+        gwpc.setActivityLaunchDefaultAllowed(true);
+        gwpc.addActivityPolicyExemption(BLOCKED_COMPONENT.getPackageName());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                BLOCKED_COMPONENT.getPackageName(),
+                BLOCKED_COMPONENT.getClassName(),
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertActivityIsBlocked(gwpc, activityInfo);
+    }
+
+    @Test
     public void openNotAllowedComponentOnBlocklistVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
@@ -275,6 +283,21 @@
     }
 
     @Test
+    public void addActivityPolicyPackageExemption_openNotAllowedOnVirtualDisplay_isBlocked() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+        gwpc.setActivityLaunchDefaultAllowed(false);
+        gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT.getPackageName());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                BLOCKED_PACKAGE_NAME,
+                BLOCKED_PACKAGE_NAME,
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertActivityIsBlocked(gwpc, activityInfo);
+    }
+
+    @Test
     public void openAllowedComponentOnBlocklistVirtualDisplay_startsActivity() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
@@ -303,6 +326,21 @@
     }
 
     @Test
+    public void addActivityPolicyPackageExemption_openAllowedOnVirtualDisplay_startsActivity() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+        gwpc.setActivityLaunchDefaultAllowed(false);
+        gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT.getPackageName());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                NONBLOCKED_COMPONENT.getPackageName(),
+                NONBLOCKED_COMPONENT.getClassName(),
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+        assertActivityCanBeLaunched(gwpc, activityInfo);
+    }
+
+    @Test
     public void openNonBlockedAppOnMirrorVirtualDisplay_isBlocked() {
         GenericWindowPolicyController gwpc = createGwpc();
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ true);
@@ -374,6 +412,22 @@
     }
 
     @Test
+    public void canActivityBeLaunched_blockedAppStreamingPackageExempt_isNeverBlocked() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+        gwpc.setActivityLaunchDefaultAllowed(true);
+        gwpc.addActivityPolicyExemption(BLOCKED_APP_STREAMING_COMPONENT.getPackageName());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+                BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+
+        assertActivityCanBeLaunched(gwpc, activityInfo);
+    }
+
+    @Test
     public void canActivityBeLaunched_blockedAppStreamingComponentNotAllowlisted_isNeverBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithAllowedComponent(NONBLOCKED_COMPONENT);
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
@@ -404,6 +458,22 @@
     }
 
     @Test
+    public void canActivityBeLaunched_blockedAppStreamingPAckageNotExempt_isNeverBlocked() {
+        GenericWindowPolicyController gwpc = createGwpc();
+        gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
+        gwpc.setActivityLaunchDefaultAllowed(false);
+        gwpc.addActivityPolicyExemption(NONBLOCKED_COMPONENT.getPackageName());
+
+        ActivityInfo activityInfo = getActivityInfo(
+                BLOCKED_APP_STREAMING_COMPONENT.getPackageName(),
+                BLOCKED_APP_STREAMING_COMPONENT.getClassName(),
+                /* displayOnRemoteDevices */ true,
+                /* targetDisplayCategory */ null);
+
+        assertActivityCanBeLaunched(gwpc, activityInfo);
+    }
+
+    @Test
     public void canActivityBeLaunched_customDisplayCategoryMatches_isNotBlocked() {
         GenericWindowPolicyController gwpc = createGwpcWithDisplayCategory(DISPLAY_CATEGORY);
         gwpc.setDisplayId(DISPLAY_ID, /* isMirrorDisplay= */ false);
@@ -592,14 +662,14 @@
                 /* targetDisplayCategory */ null);
 
         // register interceptor and intercept intent
-        when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
+        when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(true);
         assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
                 WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
                 /* isResultExpected = */ false, /* intentSender= */ null))
                 .isFalse();
 
         // unregister interceptor and launch activity
-        when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+        when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
         assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
                 WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
                 /* isResultExpected = */ false, /* intentSender= */ null))
@@ -619,13 +689,12 @@
                 /* targetDisplayCategory */ null);
 
         // register interceptor with different filter
-        when(mIntentListenerCallback.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
+        when(mActivityListener.shouldInterceptIntent(any(Intent.class))).thenReturn(false);
         assertThat(gwpc.canActivityBeLaunched(activityInfo, intent,
                 WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /* isNewTask= */false,
                 /* isResultExpected = */ false, /* intentSender= */ null))
                 .isTrue();
-        verify(mIntentListenerCallback, timeout(TIMEOUT_MILLIS))
-                .shouldInterceptIntent(any(Intent.class));
+        verify(mActivityListener, timeout(TIMEOUT_MILLIS)).shouldInterceptIntent(any(Intent.class));
     }
 
     @Test
@@ -646,8 +715,8 @@
                 /* isResultExpected = */ true, /* intentSender= */ () -> intentSender))
                 .isFalse();
 
-        verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
-                .onActivityBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
+        verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+                .onActivityLaunchBlocked(DISPLAY_ID, activityInfo, /* intentSender= */ null);
     }
 
     @Test
@@ -684,10 +753,10 @@
 
         assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
 
-        verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
-                .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
-        verify(mActivityBlockedCallback, never())
-                .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+                .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+        verify(mActivityListener, never())
+                .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
     }
 
     @Test
@@ -703,10 +772,10 @@
 
         assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, FLAG_SECURE, 0)).isTrue();
 
-        verify(mSecureWindowCallback, timeout(TIMEOUT_MILLIS)).onSecureWindowShown(DISPLAY_ID,
-                activityInfo.applicationInfo.uid);
-        verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
-                .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+        verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+                .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+                .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
     }
 
     @Test
@@ -723,10 +792,10 @@
         assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0,
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)).isTrue();
 
-        verify(mSecureWindowCallback, after(TIMEOUT_MILLIS).never())
-                .onSecureWindowShown(DISPLAY_ID, activityInfo.applicationInfo.uid);
-        verify(mActivityBlockedCallback, never())
-                .onActivityBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+                .onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+        verify(mActivityListener, never())
+                .onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
     }
 
     @Test
@@ -754,12 +823,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ mSecureWindowCallback,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -773,12 +840,10 @@
                 /* allowedUsers= */ new ArraySet<>(),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ mSecureWindowCallback,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -793,12 +858,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ homeComponent);
@@ -813,12 +876,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -833,12 +894,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ false,
                 /* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -853,12 +912,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ Collections.singleton(displayCategory),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -873,12 +930,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ true,
                 /* crossTaskNavigationExemptions= */ Collections.singleton(blockedComponent),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -893,12 +948,10 @@
                 /* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
                 /* activityLaunchAllowedByDefault= */ true,
                 /* activityPolicyExemptions= */ new ArraySet<>(),
+                /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                 /* crossTaskNavigationAllowedByDefault= */ false,
                 /* crossTaskNavigationExemptions= */ Collections.singleton(allowedComponent),
                 /* activityListener= */ mActivityListener,
-                /* activityBlockedCallback= */ mActivityBlockedCallback,
-                /* secureWindowCallback= */ null,
-                /* intentListenerCallback= */ mIntentListenerCallback,
                 /* displayCategories= */ new ArraySet<>(),
                 /* showTasksInHostDeviceRecents= */ true,
                 /* customHomeComponent= */ null);
@@ -944,9 +997,9 @@
         assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
                 isNewTask, /* isResultExpected= */ false, () -> intentSender)).isTrue();
 
-        verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
-                .onActivityBlocked(fromDisplay, activityInfo, intentSender);
-        verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+                .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+        verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
     }
 
     private void assertActivityIsBlocked(GenericWindowPolicyController gwpc,
@@ -961,9 +1014,9 @@
         assertThat(gwpc.canActivityBeLaunched(activityInfo, null, windowingMode, fromDisplay,
                 isNewTask, /* isResultExpected= */ false, () -> intentSender)).isFalse();
 
-        verify(mActivityBlockedCallback, timeout(TIMEOUT_MILLIS))
-                .onActivityBlocked(fromDisplay, activityInfo, intentSender);
-        verify(mIntentListenerCallback, after(TIMEOUT_MILLIS).never())
+        verify(mActivityListener, timeout(TIMEOUT_MILLIS))
+                .onActivityLaunchBlocked(fromDisplay, activityInfo, intentSender);
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
                 .shouldInterceptIntent(any(Intent.class));
     }
 
@@ -975,8 +1028,8 @@
                 /* isResultExpected= */ false, () -> intentSender))
                 .isFalse();
 
-        verify(mActivityBlockedCallback, after(TIMEOUT_MILLIS).never())
-                .onActivityBlocked(eq(fromDisplay), eq(activityInfo), any());
-        verify(mIntentListenerCallback, never()).shouldInterceptIntent(any(Intent.class));
+        verify(mActivityListener, after(TIMEOUT_MILLIS).never())
+                .onActivityLaunchBlocked(eq(fromDisplay), eq(activityInfo), any());
+        verify(mActivityListener, never()).shouldInterceptIntent(any(Intent.class));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c288212..4d067f6 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
@@ -62,7 +62,6 @@
 import android.companion.virtual.VirtualDeviceParams;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
-import android.companion.virtual.flags.Flags;
 import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensorConfig;
@@ -1686,7 +1685,6 @@
 
     @Test
     public void openNonBlockedAppOnMirrorDisplay_flagEnabled_cannotBeLaunched() {
-        mSetFlagsRule.enableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
         when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt()))
                 .thenReturn(Display.DEFAULT_DISPLAY);
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
@@ -1711,31 +1709,6 @@
     }
 
     @Test
-    public void openNonBlockedAppOnMirrorDisplay_flagDisabled_launchesActivity() {
-        mSetFlagsRule.disableFlags(Flags.FLAG_INTERACTIVE_SCREEN_MIRROR);
-        when(mDisplayManagerInternalMock.getDisplayIdToMirror(anyInt()))
-                .thenReturn(Display.DEFAULT_DISPLAY);
-        addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
-        GenericWindowPolicyController gwpc = mDeviceImpl.getDisplayWindowPolicyControllerForTest(
-                DISPLAY_ID_1);
-        doNothing().when(mContext).startActivityAsUser(any(), any(), any());
-
-        ActivityInfo activityInfo = getActivityInfo(
-                NONBLOCKED_APP_PACKAGE_NAME,
-                NONBLOCKED_APP_PACKAGE_NAME,
-                /* displayOnRemoteDevices */ true,
-                /* targetDisplayCategory */ null);
-        assertThat(gwpc.canActivityBeLaunched(activityInfo, null,
-                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /* isNewTask= */ false,
-                /* isResultExpected = */ false, /* intentSender= */ null))
-                .isTrue();
-        Intent blockedAppIntent = BlockedAppStreamingActivity.createIntent(
-                activityInfo, mAssociationInfo.getDisplayName());
-        verify(mContext, never()).startActivityAsUser(argThat(intent ->
-                intent.filterEquals(blockedAppIntent)), any(), any());
-    }
-
-    @Test
     public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         addVirtualDisplay(mDeviceImpl, DISPLAY_ID_1);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index c3db396..51c2ad1 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -83,12 +83,10 @@
                         /* allowedUsers= */ new ArraySet<>(),
                         /* activityLaunchAllowedByDefault= */ true,
                         /* activityPolicyExemptions= */ new ArraySet<>(),
+                        /* activityPolicyPackageExemptions= */ new ArraySet<>(),
                         /* crossTaskNavigationAllowedByDefault= */ true,
                         /* crossTaskNavigationExemptions= */ new ArraySet<>(),
                         /* activityListener= */ null,
-                        /* activityBlockedCallback= */ null,
-                        /* secureWindowCallback= */ null,
-                        /* intentListenerCallback= */ null,
                         /* displayCategories= */ new ArraySet<>(),
                         /* showTasksInHostDeviceRecents= */ true,
                         /* customHomeComponent= */ null);
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
index 24561c5..3d09da3 100644
--- a/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS
@@ -1,3 +1,4 @@
-# Bug component: 544200
+# Bug component: 1040349
 
-include /core/java/android/view/contentcapture/OWNERS
+include /core/java/android/view/contentprotection/OWNERS
+
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index b4cc343..698bda3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -60,6 +60,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -325,6 +326,11 @@
         }
 
         @Override
+        List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+            return services.roleManagerForMock.getRoleHoldersAsUser(role, userHandle);
+        }
+
+        @Override
         PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
                 Intent intent, int flags, Bundle options, UserHandle user) {
             return null;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e72d9e7..cb4269a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -70,14 +70,14 @@
 
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
+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.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.longThat;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -109,6 +109,7 @@
 import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.WifiSsidPolicy;
+import android.app.role.RoleManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -1733,12 +1734,20 @@
         pi.applicationInfo.flags = flags;
         doReturn(pi).when(getServices().ipackageManager).getPackageInfo(
                 eq(packageName),
-                anyLong(),
+                longThat(flg -> (flg & PackageManager.MATCH_ANY_USER) == 0),
+                eq(userId));
+        doReturn(pi).when(getServices().ipackageManager).getPackageInfo(
+                eq(packageName),
+                longThat(flg -> (flg & PackageManager.MATCH_ANY_USER) != 0),
+                anyInt());
+        doReturn(pi.applicationInfo).when(getServices().ipackageManager).getApplicationInfo(
+                eq(packageName),
+                longThat(flg -> (flg & PackageManager.MATCH_ANY_USER) == 0),
                 eq(userId));
         doReturn(pi.applicationInfo).when(getServices().ipackageManager).getApplicationInfo(
                 eq(packageName),
-                anyLong(),
-                eq(userId));
+                longThat(flg -> (flg & PackageManager.MATCH_ANY_USER) != 0),
+                anyInt());
         doReturn(true).when(getServices().ipackageManager).isPackageAvailable(packageName, userId);
         // Setup application UID with the PackageManager
         getServices().addTestPackageUid(packageName, uid);
@@ -1757,7 +1766,7 @@
         mServiceContext.packageName = mRealTestContext.getPackageName();
         mServiceContext.applicationInfo = new ApplicationInfo();
         mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+        when(mContext.resources.getColor(anyInt(), any())).thenReturn(Color.WHITE);
 
         StringParceledListSlice oneCert = asSlice(new String[] {"1"});
         StringParceledListSlice fourCerts = asSlice(new String[] {"1", "2", "3", "4"});
@@ -2881,6 +2890,52 @@
     }
 
     @Test
+    public void testSetMeteredDataDisabledPackagesExemptRoles() throws Exception {
+        // TODO(b/362545319): reference role name from role manager once it's exposed.
+        final String controllerRole = "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+
+        setAsProfileOwner(admin1);
+
+        assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEmpty();
+
+        // Setup
+        final ArrayList<String> pkgsToRestrict = new ArrayList<>();
+        final ArrayList<String> pkgsExpectedAsNotRestricted = new ArrayList<>();
+        final String packageWithControllerRole = "com.example.controller";
+        final String packageWithKioskRole = "com.example.kiosk";
+        final String packageWithNotExemptRole = "com.example.notexempt";
+
+        pkgsToRestrict.add(packageWithControllerRole);
+        pkgsToRestrict.add(packageWithKioskRole);
+        pkgsToRestrict.add(packageWithNotExemptRole);
+
+        pkgsExpectedAsNotRestricted.add(packageWithControllerRole);
+        pkgsExpectedAsNotRestricted.add(packageWithKioskRole);
+
+        setupPackageInPackageManager(packageWithControllerRole, CALLER_USER_HANDLE, 123, 0);
+        setupPackageInPackageManager(packageWithKioskRole, CALLER_USER_HANDLE, 456, 0);
+        setupPackageInPackageManager(packageWithNotExemptRole, CALLER_USER_HANDLE, 789, 0);
+
+        when(getServices().roleManagerForMock.getRoleHoldersAsUser(controllerRole,
+                UserHandle.of(CALLER_USER_HANDLE)))
+                .thenReturn(new ArrayList<>(Arrays.asList(packageWithControllerRole)));
+        when(getServices().roleManagerForMock.getRoleHoldersAsUser(
+                RoleManager.ROLE_FINANCED_DEVICE_KIOSK,
+                UserHandle.of(CALLER_USER_HANDLE)))
+                .thenReturn(new ArrayList<>(Arrays.asList(packageWithKioskRole)));
+
+        List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
+
+        // Verify
+        assertThat(excludedPkgs).containsExactlyElementsIn(pkgsExpectedAsNotRestricted);
+        assertThat(dpm.getMeteredDataDisabledPackages(admin1))
+                .isEqualTo(Arrays.asList(packageWithNotExemptRole));
+        verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
+                MockUtils.checkApps(packageWithNotExemptRole),
+                eq(CALLER_USER_HANDLE));
+    }
+
+    @Test
     public void testSetGetMeteredDataDisabledPackages_deviceAdmin() {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         dpm.setActiveAdmin(admin1, true);
@@ -4551,7 +4606,7 @@
 
         mContext.packageName = admin1.getPackageName();
         mContext.applicationInfo = new ApplicationInfo();
-        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+        when(mContext.resources.getColor(anyInt(), any())).thenReturn(Color.WHITE);
 
         // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
         // feature is disabled because there are non-affiliated secondary users.
@@ -4597,12 +4652,12 @@
         setupDeviceOwner();
         mContext.packageName = admin1.getPackageName();
         mContext.applicationInfo = new ApplicationInfo();
-        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+        when(mContext.resources.getColor(anyInt(), any())).thenReturn(Color.WHITE);
 
         // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
         // feature is disabled because there are non-affiliated secondary users.
         getServices().removeUser(CALLER_USER_HANDLE);
-        when(getServices().iipConnectivityMetrics.addNetdEventCallback(anyInt(), anyObject()))
+        when(getServices().iipConnectivityMetrics.addNetdEventCallback(anyInt(), any()))
                 .thenReturn(true);
 
         // No logs were retrieved so far.
@@ -4667,7 +4722,7 @@
         mContext.packageName = admin1.getPackageName();
         addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.S);
         when(getServices().iipConnectivityMetrics
-                .addNetdEventCallback(anyInt(), anyObject())).thenReturn(true);
+                .addNetdEventCallback(anyInt(), any())).thenReturn(true);
 
         // Check no logs have been retrieved so far.
         assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
@@ -4699,7 +4754,7 @@
         mContext.packageName = admin1.getPackageName();
         mContext.applicationInfo = new ApplicationInfo();
         when(getServices().iipConnectivityMetrics
-                .addNetdEventCallback(anyInt(), anyObject())).thenReturn(true);
+                .addNetdEventCallback(anyInt(), any())).thenReturn(true);
 
         // Check no logs have been retrieved so far.
         assertThat(dpm.getLastNetworkLogRetrievalTime()).isEqualTo(-1);
@@ -6296,13 +6351,13 @@
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         assertThat(dpms.isNotificationListenerServicePermitted(
-        nonSystemPackage, MANAGED_PROFILE_USER_ID)).isTrue();
+                nonSystemPackage, MANAGED_PROFILE_USER_ID)).isTrue();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        systemListener, MANAGED_PROFILE_USER_ID)).isTrue();
+                systemListener, MANAGED_PROFILE_USER_ID)).isTrue();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue();
+                nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        systemListener, UserHandle.USER_SYSTEM)).isTrue();
+                systemListener, UserHandle.USER_SYSTEM)).isTrue();
 
         // Setting an empty allowlist - only system listeners allowed in managed profile, but
         // all allowed in primary profile
@@ -6313,13 +6368,13 @@
 
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         assertThat(dpms.isNotificationListenerServicePermitted(
-        nonSystemPackage, MANAGED_PROFILE_USER_ID)).isFalse();
+                nonSystemPackage, MANAGED_PROFILE_USER_ID)).isFalse();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        systemListener, MANAGED_PROFILE_USER_ID)).isTrue();
+                systemListener, MANAGED_PROFILE_USER_ID)).isTrue();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue();
+                nonSystemPackage, UserHandle.USER_SYSTEM)).isTrue();
         assertThat(dpms.isNotificationListenerServicePermitted(
-        systemListener, UserHandle.USER_SYSTEM)).isTrue();
+                systemListener, UserHandle.USER_SYSTEM)).isTrue();
     }
 
     @Test
@@ -6455,7 +6510,7 @@
         if (admin1.getPackageName().equals(callerContext.getPackageName())) {
             admin1Context = callerContext;
         }
-        when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+        when(admin1Context.resources.getColor(anyInt(), any())).thenReturn(Color.WHITE);
 
         // caller: device admin or delegated certificate installer
         callerContext.applicationInfo = new ApplicationInfo();
@@ -6528,7 +6583,7 @@
         if (admin1.getPackageName().equals(callerContext.getPackageName())) {
             admin1Context = callerContext;
         }
-        when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+        when(admin1Context.resources.getColor(anyInt(), any())).thenReturn(Color.WHITE);
 
         // caller: device admin or delegated certificate installer
         callerContext.applicationInfo = new ApplicationInfo();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 76aa40c..2e200a9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -142,6 +142,7 @@
     public final DevicePolicyManager devicePolicyManager;
     public final LocationManager locationManager;
     public final RoleManager roleManager;
+    public final RoleManagerForMock roleManagerForMock;
     public final SubscriptionManager subscriptionManager;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -200,6 +201,7 @@
         devicePolicyManager = mock(DevicePolicyManager.class);
         locationManager = mock(LocationManager.class);
         roleManager = realContext.getSystemService(RoleManager.class);
+        roleManagerForMock = mock(RoleManagerForMock.class);
         subscriptionManager = mock(SubscriptionManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
@@ -495,6 +497,12 @@
         }
     }
 
+    public static class RoleManagerForMock {
+        public List<String> getRoleHoldersAsUser(String role, UserHandle userHandle) {
+            return new ArrayList<>();
+        }
+    }
+
     public static class SettingsForMock {
         public int settingsSecureGetIntForUser(String name, int def, int userHandle) {
             return 0;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 2b93ccb..a5f1fcd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -29,6 +29,7 @@
 import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
 import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
 import static com.android.server.hdmi.RequestActiveSourceAction.TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS;
+import static com.android.server.hdmi.RoutingControlAction.TIMEOUT_ROUTING_INFORMATION_MS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -77,6 +78,7 @@
 public class HdmiCecLocalDeviceTvTest {
     private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
     private static final int PORT_1 = 1;
+    private static final int PORT_2 = 2;
 
     private static final String[] SADS_NOT_TO_QUERY = new String[]{
             HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
@@ -215,7 +217,7 @@
                 .setEarcSupported(false)
                 .build();
         hdmiPortInfos[1] =
-                new HdmiPortInfo.Builder(2, HdmiPortInfo.PORT_INPUT, 0x2000)
+                new HdmiPortInfo.Builder(PORT_2, HdmiPortInfo.PORT_INPUT, 0x2000)
                         .setCecSupported(true)
                         .setMhlSupported(false)
                         .setArcSupported(true)
@@ -2021,7 +2023,7 @@
                 ADDR_TV);
         mTestLooper.dispatchAll();
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
         mPowerManager.setInteractive(true);
         mTestLooper.dispatchAll();
@@ -2031,14 +2033,14 @@
                 "HdmiCecLocalDeviceTvTest");
         mTestLooper.dispatchAll();
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isTrue();
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
 
         assertThat(mPowerManager.isInteractive()).isFalse();
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
     }
 
@@ -2051,7 +2053,7 @@
         mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
         mTestLooper.dispatchAll();
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
         mPowerManager.setInteractive(true);
 
@@ -2060,7 +2062,7 @@
                 "HdmiCecLocalDeviceTvTest");
         mTestLooper.dispatchAll();
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isTrue();
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
                 .isEqualTo(Constants.HANDLED);
@@ -2076,22 +2078,51 @@
         mHdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
         mTestLooper.dispatchAll();
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
         mPowerManager.setInteractive(true);
 
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
         assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
                 .isEqualTo(Constants.HANDLED);
         mTestLooper.dispatchAll();
 
         assertThat(mPowerManager.isInteractive()).isFalse();
-        assertThat(mHdmiCecLocalDeviceTv.getWasActiveSourceSetToConnectedDevice())
+        assertThat(mHdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
                 .isFalse();
     }
 
     @Test
+    public void handleStandby_fromNonActiveSource_previousActivePathSetToNonCecDevice_Standby() {
+        HdmiCecLocalDeviceTv hdmiCecLocalDeviceTv = new MockTvDevice(mHdmiControlService);
+        hdmiCecLocalDeviceTv.setDeviceInfo(mHdmiCecLocalDeviceTv.getDeviceInfo());
+        mTestLooper.dispatchAll();
+
+        assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+                .isFalse();
+        mPowerManager.setInteractive(true);
+        hdmiCecLocalDeviceTv.doManualPortSwitching(PORT_2, null);
+        mTestLooper.dispatchAll();
+
+        // Timeout the action RoutingControlAction such that the active path would be updated.
+        mTestLooper.moveTimeForward(TIMEOUT_ROUTING_INFORMATION_MS);
+        mTestLooper.dispatchAll();
+
+        assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+                .isTrue();
+        HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(
+                ADDR_PLAYBACK_1, ADDR_TV);
+        assertThat(hdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
+                .isEqualTo(Constants.HANDLED);
+        mTestLooper.dispatchAll();
+
+        assertThat(mPowerManager.isInteractive()).isTrue();
+        assertThat(hdmiCecLocalDeviceTv.getWasActivePathSetToConnectedDevice())
+                .isTrue();
+    }
+
+    @Test
     public void handleReportPhysicalAddress_DeviceDiscoveryActionInProgress_noNewDeviceAction() {
         mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
         mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
@@ -2148,6 +2179,40 @@
                 .hasSize(1);
     }
 
+
+    @Test
+    public void handleReportAudioStatus_SamOnAvrStandby_startSystemAudioActionFromTv() {
+        mHdmiControlService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_CONTROL,
+                HdmiControlManager.SYSTEM_AUDIO_CONTROL_ENABLED);
+        // Emulate Audio device on port 0x1000 (does not support ARC)
+        mNativeWrapper.setPortConnectionStatus(1, true);
+        HdmiCecMessage reportPhysicalAddress =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        ADDR_AUDIO_SYSTEM, 0x1000, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        HdmiCecMessage reportPowerStatus =
+                HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_AUDIO_SYSTEM, ADDR_TV,
+                        HdmiControlManager.POWER_STATUS_STANDBY);
+        mNativeWrapper.onCecMessage(reportPhysicalAddress);
+        mNativeWrapper.onCecMessage(reportPowerStatus);
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).hasSize(0);
+
+        HdmiCecFeatureAction systemAudioAutoInitiationAction =
+                new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv, ADDR_AUDIO_SYSTEM);
+        mHdmiCecLocalDeviceTv.addAndStartAction(systemAudioAutoInitiationAction);
+        HdmiCecMessage reportSystemAudioMode =
+                HdmiCecMessageBuilder.buildReportSystemAudioMode(
+                        ADDR_AUDIO_SYSTEM,
+                        mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress(),
+                        true);
+        mHdmiControlService.handleCecCommand(reportSystemAudioMode);
+        mTestLooper.dispatchAll();
+
+        // SAM must be on; ARC must be off
+        assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).hasSize(1);
+    }
+
     protected static class MockTvDevice extends HdmiCecLocalDeviceTv {
         MockTvDevice(HdmiControlService service) {
             super(service);
@@ -2155,7 +2220,7 @@
 
         @Override
         protected int handleActiveSource(HdmiCecMessage message) {
-            setWasActiveSourceSetToConnectedDevice(true);
+            setWasActivePathSetToConnectedDevice(true);
             return super.handleActiveSource(message);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 1074f7b..6577e09 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -250,120 +250,120 @@
         assertMessageValidity("04:33:0C:08:10:1E:04:30:08:13:AD:06")
                 .isEqualTo(ERROR_PARAMETER_SHORT);
         // Out of range Day of Month
-        assertMessageValidity("04:34:20:0C:16:0F:08:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:20:0C:22:15:08:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
         // Out of range Month of Year
-        assertMessageValidity("04:33:0C:00:10:1E:04:30:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:33:0C:00:16:30:04:48:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Hour
-        assertMessageValidity("04:34:04:0C:18:0F:08:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:04:0C:24:15:08:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Minute
-        assertMessageValidity("04:33:0C:08:10:50:04:30:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:33:0C:08:16:60:04:48:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Duration Hours
-        assertMessageValidity("04:34:04:0C:16:0F:64:37:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:04:0C:22:15:9A:55:00:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Minute
-        assertMessageValidity("04:33:0C:08:10:1E:04:64:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:33:0C:08:16:30:04:60:08:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("04:34:04:0C:16:0F:08:37:88:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:04:0C:22:15:08:55:88:02:EA:60:03").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("04:33:0C:08:10:1E:04:30:A2:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:33:0C:08:16:30:04:48:A2:00:13:AD:06").isEqualTo(ERROR_PARAMETER);
         // Out of range Analogue Broadcast Type
-        assertMessageValidity("04:34:04:0C:16:0F:08:37:00:03:EA:60:03").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:04:0C:22:15:08:55:00:03:EA:60:03").isEqualTo(ERROR_PARAMETER);
         // Out of range Analogue Frequency
-        assertMessageValidity("04:33:0C:08:10:1E:04:30:08:00:FF:FF:06").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:33:0C:08:16:30:04:48:08:00:FF:FF:06").isEqualTo(ERROR_PARAMETER);
         // Out of range Broadcast System
-        assertMessageValidity("04:34:04:0C:16:0F:08:37:00:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:34:04:0C:22:15:08:55:00:02:EA:60:20").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
     public void isValid_setDigitalTimer_clearDigitalTimer() {
         // Services identified by Digital IDs - ARIB Broadcast System
-        assertMessageValidity("04:99:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75:30").isEqualTo(OK);
+        assertMessageValidity("04:99:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75:30").isEqualTo(OK);
         // Service identified by Digital IDs - ATSC Broadcast System
-        assertMessageValidity("04:97:1E:07:12:20:50:28:01:01:8B:5E:39:5A").isEqualTo(OK);
+        assertMessageValidity("04:97:1E:07:18:32:80:40:01:01:8B:5E:39:5A").isEqualTo(OK);
         // Service identified by Digital IDs - DVB Broadcast System
-        assertMessageValidity("04:99:05:0C:06:0A:19:3B:40:19:8B:44:03:11:04:FC").isEqualTo(OK);
+        assertMessageValidity("04:99:05:0C:06:10:25:59:40:19:8B:44:03:11:04:FC").isEqualTo(OK);
         // Service identified by Channel - 1 part channel number
-        assertMessageValidity("04:97:12:06:0C:2D:5A:19:08:91:04:00:B1").isEqualTo(OK);
+        assertMessageValidity("04:97:12:06:12:45:90:25:08:91:04:00:B1").isEqualTo(OK);
         // Service identified by Channel - 2 part channel number
-        assertMessageValidity("04:99:15:09:00:0F:00:2D:04:82:09:C8:72:C8").isEqualTo(OK);
+        assertMessageValidity("04:99:15:09:00:15:00:45:04:82:09:C8:72:C8").isEqualTo(OK);
 
-        assertMessageValidity("4F:97:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75:30")
+        assertMessageValidity("4F:97:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75:30")
                 .isEqualTo(ERROR_DESTINATION);
-        assertMessageValidity("F0:99:15:09:00:0F:00:2D:04:82:09:C8:72:C8").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("F0:99:15:09:00:15:00:45:04:82:09:C8:72:C8").isEqualTo(ERROR_SOURCE);
         assertMessageValidity("04:97:1E:12:20:58:01:01:8B:5E:39:5A")
                 .isEqualTo(ERROR_PARAMETER_SHORT);
         // Out of range Day of Month
-        assertMessageValidity("04:99:24:0C:06:0A:19:3B:40:19:8B:44:03:11:04:FC")
+        assertMessageValidity("04:99:24:0C:06:10:25:59:40:19:8B:44:03:11:04:FC")
                 .isEqualTo(ERROR_PARAMETER);
         // Out of range Month of Year
-        assertMessageValidity("04:97:12:10:0C:2D:5A:19:08:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:97:12:10:12:45:90:25:08:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Hour
-        assertMessageValidity("04:99:0C:08:20:05:04:1E:00:00:C4:C2:11:D8:75:30")
+        assertMessageValidity("04:99:0C:08:24:05:04:30:00:00:C4:C2:11:D8:75:30")
                 .isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Minute
-        assertMessageValidity("04:97:15:09:00:4B:00:2D:04:82:09:C8:72:C8")
+        assertMessageValidity("04:97:15:09:00:60:00:45:04:82:09:C8:72:C8")
                 .isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Duration Hours
-        assertMessageValidity("04:99:1E:07:12:20:78:28:01:01:8B:5E:39:5A")
+        assertMessageValidity("04:99:1E:07:18:32:9A:40:01:01:8B:5E:39:5A")
                 .isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Minute
-        assertMessageValidity("04:97:05:0C:06:0A:19:48:40:19:8B:44:03:11:04:FC")
+        assertMessageValidity("04:97:05:0C:06:10:25:60:40:19:8B:44:03:11:04:FC")
                 .isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("04:99:12:06:0C:2D:5A:19:90:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:99:12:06:12:45:90:25:90:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
         assertMessageValidity("04:97:0C:08:15:05:04:1E:A1:00:C4:C2:11:D8:75:30")
                 .isEqualTo(ERROR_PARAMETER);
 
         // Invalid Digital Broadcast System
-        assertMessageValidity("04:99:1E:07:12:20:50:28:01:04:8B:5E:39:5A")
+        assertMessageValidity("04:99:1E:07:18:32:80:40:01:04:8B:5E:39:5A")
                 .isEqualTo(ERROR_PARAMETER);
         // Invalid Digital Broadcast System
-        assertMessageValidity("04:97:05:0C:06:0A:19:3B:40:93:8B:44:03:11:04:FC")
+        assertMessageValidity("04:97:05:0C:06:10:25:59:40:93:8B:44:03:11:04:FC")
                 .isEqualTo(ERROR_PARAMETER);
         // Insufficient data for ARIB Broadcast system
-        assertMessageValidity("04:99:0C:08:15:05:04:1E:00:00:C4:C2:11:D8:75")
+        assertMessageValidity("04:99:0C:08:21:05:04:30:00:00:C4:C2:11:D8:75")
                 .isEqualTo(ERROR_PARAMETER);
         // Insufficient data for ATSC Broadcast system
-        assertMessageValidity("04:97:1E:07:12:20:50:28:01:01:8B:5E:39").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:97:1E:07:18:32:80:40:01:01:8B:5E:39").isEqualTo(ERROR_PARAMETER);
         // Insufficient data for DVB Broadcast system
-        assertMessageValidity("04:99:05:0C:06:0A:19:3B:40:19:8B:44:03:11:04")
+        assertMessageValidity("04:99:05:0C:06:10:25:59:40:19:8B:44:03:11:04")
                 .isEqualTo(ERROR_PARAMETER);
         // Insufficient data for 2 part channel number
-        assertMessageValidity("04:97:15:09:00:0F:00:2D:04:82:09:C8:72").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:97:15:09:00:15:00:45:04:82:09:C8:72").isEqualTo(ERROR_PARAMETER);
         // Invalid Channel Number format
-        assertMessageValidity("04:99:12:06:0C:2D:5A:19:08:91:0D:00:B1").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:99:12:06:12:45:90:25:08:91:0D:00:B1").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
     public void isValid_setExternalTimer_clearExternalTimer() {
-        assertMessageValidity("40:A1:0C:08:15:05:04:1E:02:04:20").isEqualTo(OK);
-        assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:00").isEqualTo(OK);
+        assertMessageValidity("40:A1:0C:08:21:05:04:30:02:04:20").isEqualTo(OK);
+        assertMessageValidity("40:A2:14:09:18:40:75:25:10:05:10:00").isEqualTo(OK);
 
-        assertMessageValidity("4F:A1:0C:08:15:05:04:1E:02:04:20").isEqualTo(ERROR_DESTINATION);
-        assertMessageValidity("F4:A2:14:09:12:28:4B:19:10:05:10:00").isEqualTo(ERROR_SOURCE);
-        assertMessageValidity("40:A1:0C:08:15:05:04:1E:02:04").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("4F:A1:0C:08:21:05:04:30:02:04:20").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F4:A2:14:09:18:40:75:25:10:05:10:00").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:A1:0C:08:21:05:04:30:02:04").isEqualTo(ERROR_PARAMETER_SHORT);
         // Out of range Day of Month
-        assertMessageValidity("40:A2:28:09:12:28:4B:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:28:09:18:40:75:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
         // Out of range Month of Year
-        assertMessageValidity("40:A1:0C:0F:15:05:04:1E:02:04:20").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A1:0C:0F:21:05:04:30:02:04:20").isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Hour
-        assertMessageValidity("40:A2:14:09:1A:28:4B:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:14:09:24:40:75:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
         // Out of range Start Time - Minute
-        assertMessageValidity("40:A1:0C:08:15:48:04:1E:02:04:20").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A1:0C:08:21:60:04:30:02:04:20").isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Duration Hours
-        assertMessageValidity("40:A2:14:09:12:28:66:19:10:05:10:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:14:09:18:40:9A:25:10:05:10:00").isEqualTo(ERROR_PARAMETER);
         // Out of range Duration - Minute
-        assertMessageValidity("40:A1:0C:08:15:05:04:3F:02:04:20").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A1:0C:08:21:05:04:60:02:04:20").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("40:A2:14:09:12:28:4B:19:84:05:10:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:14:09:18:40:75:25:84:05:10:00").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
         assertMessageValidity("40:A1:0C:08:15:05:04:1E:94:04:20").isEqualTo(ERROR_PARAMETER);
         // Invalid external source specifier
-        assertMessageValidity("40:A2:14:09:12:28:4B:19:10:08:10:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:14:09:18:40:75:25:10:08:10:00").isEqualTo(ERROR_PARAMETER);
         // Invalid External PLug
-        assertMessageValidity("04:A1:0C:08:15:05:04:1E:02:04:00").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("04:A1:0C:08:21:05:04:30:02:04:00").isEqualTo(ERROR_PARAMETER);
         // Invalid Physical Address
-        assertMessageValidity("40:A2:14:09:12:28:4B:19:10:05:10:10").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A2:14:09:18:40:75:25:10:05:10:10").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
@@ -396,9 +396,9 @@
         // Non programmed - Invalid not programmed error info
         assertMessageValidity("40:35:DE").isEqualTo(ERROR_PARAMETER);
         // Programmed - Might not be enough space available - Invalid duration hours
-        assertMessageValidity("40:35:BB:96:1C").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:35:BB:9A:28").isEqualTo(ERROR_PARAMETER);
         // Not programmed - Duplicate - Invalid duration minutes
-        assertMessageValidity("40:35:EE:52:4A").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:35:EE:82:60").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 17b499e..d6f7e21 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -625,25 +625,10 @@
 
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
-        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
-        doNothing()
-                .when(mInjected)
-                .reportMetric(
-                        metricsSuccessCaptor.capture(),
-                        metricsErrorCodeCaptor.capture(),
-                        eq(2) /* Server based */,
-                        eq(1) /* attempt count */,
-                        anyInt(),
-                        eq(0) /* vbmeta status */,
-                        anyInt());
+
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mServiceConnection, never()).unwrap(any(), anyLong());
         verify(mCallbacks, never()).onRebootEscrowRestored(anyByte(), any(), anyInt());
-        assertFalse(metricsSuccessCaptor.getValue());
-        assertEquals(
-                Integer.valueOf(RebootEscrowManager.ERROR_NO_REBOOT_ESCROW_DATA),
-                metricsErrorCodeCaptor.getValue());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index b2fd8aa..161b18c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -165,7 +165,7 @@
 
         assertTrue(resultContains(
                 callShellCommand("reset-throttling", "--user", "10"),
-                "User 10 is not running or locked"));
+                "User (with userId=10) is not running or locked"));
 
         mRunningUsers.put(USER_10, true);
 
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 7912156..e652df5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.IpcDataCache;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -100,6 +101,9 @@
 
     @Before
     public void setUp() throws Exception {
+        // Disable binder caches in this process.
+        IpcDataCache.disableForTestMode();
+
         mOriginalCurrentUserId = ActivityManager.getCurrentUser();
         mUserManager = UserManager.get(mContext);
         mActivityManager = mContext.getSystemService(ActivityManager.class);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 55c48e0..f0a5f75 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -36,7 +36,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,11 +54,6 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
-    @Before
-    public void setUp() {
-        mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
-    }
-
     @Test
     public void testNonNull() {
         Bundle out = UserRestrictionsUtils.nonNull(null);
@@ -144,7 +138,6 @@
 
     @Test
     public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_orgOwned() {
-        mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_SIM_GLOBALLY,
                 false,
@@ -157,7 +150,6 @@
 
     @Test
     public void testCanProfileOwnerChange_restrictionRequiresOrgOwnedDevice_notOrgOwned() {
-        mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_SIM_GLOBALLY,
                 false,
@@ -169,22 +161,7 @@
     }
 
     @Test
-    public void
-            testCanProfileOwnerChange_disabled_restrictionRequiresOrgOwnedDevice_notOrgOwned() {
-        mSetFlagsRule.disableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
-        assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_SIM_GLOBALLY,
-                false,
-                false));
-        assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_SIM_GLOBALLY,
-                true,
-                false));
-    }
-
-    @Test
     public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_orgOwned() {
-        mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 false,
@@ -197,7 +174,6 @@
 
     @Test
     public void testCanProfileOwnerChange_restrictionNotRequiresOrgOwnedDevice_notOrgOwned() {
-        mSetFlagsRule.enableFlags(android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED);
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
                 UserManager.DISALLOW_ADJUST_VOLUME,
                 false,
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index fad10f7..5518082 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -182,7 +182,7 @@
                 + "    <device-state>\n"
                 + "        <identifier>1</identifier>\n"
                 + "        <properties>\n"
-                + "            <property>PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
+                + "            <property>com.android.server.policy.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS</property>\n"
                 + "        </properties>\n"
                 + "        <conditions/>\n"
                 + "    </device-state>\n"
@@ -338,11 +338,9 @@
                 + "        <identifier>4</identifier>\n"
                 + "        <name>THERMAL_TEST</name>\n"
                 + "        <properties>\n"
-                + "            <property>PROPERTY_EMULATED_ONLY</property>\n"
-                + "            <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL"
-                + "</property>\n"
-                + "            <property>PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE"
-                + "</property>\n"
+                + "            <property>com.android.server.policy.PROPERTY_EMULATED_ONLY</property>\n"
+                + "            <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL</property>\n"
+                + "            <property>com.android.server.policy.PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE</property>\n"
                 + "        </properties>\n"
                 + "    </device-state>\n"
                 + "</device-state-config>\n";
diff --git a/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
new file mode 100644
index 0000000..4aa6d39
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/rollback/ApexdRevertLoggerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.rollback;
+
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class ApexdRevertLoggerTest {
+
+    private Context mMockContext = mock(Context.class);
+    private PackageManager mMockPm;
+    private PackageInfo mPackageInfo;
+
+    private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+    private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
+            | PackageManager.GET_META_DATA;
+    private static final List<String> sFailingPackages =
+            List.of("package1", "package2", "package3");
+
+    @Before
+    public void setUp() {
+        mMockPm = mock(PackageManager.class);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+        PackageInstaller mockPi = mock(PackageInstaller.class);
+        when(mMockPm.getPackageInstaller()).thenReturn(mockPi);
+        PackageInstaller.SessionInfo mockSessionInfo = mock(PackageInstaller.SessionInfo.class);
+        when(mockPi.getSessionInfo(anyInt())).thenReturn(mockSessionInfo);
+        mPackageInfo = new PackageInfo();
+    }
+
+    /**
+     * Ensures that we make the correct Package Manager calls in the case that the failing packages
+     * are correctly configured with parent packages.
+     */
+    @Test
+    public void testApexdLoggingCallsWithParents() throws Exception {
+        for (String failingPackage: sFailingPackages) {
+            PackageInfo packageInfo = new PackageInfo();
+            ApplicationInfo applicationInfo = new ApplicationInfo();
+            Bundle bundle = new Bundle();
+            bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
+            applicationInfo.metaData = bundle;
+            packageInfo.applicationInfo = applicationInfo;
+            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+        }
+
+        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+        ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+        for (String failingPackage: sFailingPackages) {
+            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+            verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
+        }
+    }
+
+    /**
+     * Ensures that we don't make any calls to parent packages in the case that packages are not
+     * correctly configured with parent packages.
+     */
+    @Test
+    public void testApexdLoggingCallsWithNoParents() throws Exception {
+        for (String failingPackage: sFailingPackages) {
+            PackageInfo packageInfo = new PackageInfo();
+            packageInfo.applicationInfo = new ApplicationInfo();
+            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+        }
+        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+
+        ApexdRevertLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+        verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
+        for (String failingPackage: sFailingPackages) {
+            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+        }
+    }
+
+    private String getParent(String packageName) {
+        return packageName + "-parent";
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index d1c9643..8257168 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -20,7 +20,6 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -139,52 +138,4 @@
         verify(mMockPm, times(1)).getPackageInfo(
                 sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
     }
-
-    /**
-     * Ensures that we make the correct Package Manager calls in the case that the failing packages
-     * are correctly configured with parent packages.
-     */
-    @Test
-    public void testApexdLoggingCallsWithParents() throws Exception {
-        for (String failingPackage: sFailingPackages) {
-            PackageInfo packageInfo = new PackageInfo();
-            ApplicationInfo applicationInfo = new ApplicationInfo();
-            Bundle bundle = new Bundle();
-            bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
-            applicationInfo.metaData = bundle;
-            packageInfo.applicationInfo = applicationInfo;
-            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
-        }
-
-        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
-        WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
-        for (String failingPackage: sFailingPackages) {
-            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
-            verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
-        }
-    }
-
-    /**
-     * Ensures that we don't make any calls to parent packages in the case that packages are not
-     * correctly configured with parent packages.
-     */
-    @Test
-    public void testApexdLoggingCallsWithNoParents() throws Exception {
-        for (String failingPackage: sFailingPackages) {
-            PackageInfo packageInfo = new PackageInfo();
-            packageInfo.applicationInfo = new ApplicationInfo();
-            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
-        }
-        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
-
-        WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
-        verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
-        for (String failingPackage: sFailingPackages) {
-            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
-        }
-    }
-
-    private String getParent(String packageName) {
-        return packageName + "-parent";
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsUtilsTest.kt b/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsUtilsTest.kt
new file mode 100644
index 0000000..c560c04
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/stats/pull/netstats/NetworkStatsUtilsTest.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.server.stats.pull.netstats
+
+import android.net.NetworkStats
+import android.net.NetworkStats.DEFAULT_NETWORK_NO
+import android.net.NetworkStats.DEFAULT_NETWORK_YES
+import android.net.NetworkStats.Entry
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.ROAMING_NO
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.SET_DEFAULT
+import android.net.NetworkStats.TAG_NONE
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.testutils.assertEntryEquals
+import com.android.testutils.assertNetworkStatsEquals
+import com.android.testutils.makePublicStatsFromAndroidNetStats
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:NetworkStatsUtilsTest
+ */
+@RunWith(AndroidJUnit4::class)
+class NetworkStatsUtilsTest {
+
+    @Test
+    fun testBucketToEntry() {
+        val bucket = makeMockBucket(android.app.usage.NetworkStats.Bucket.UID_ALL,
+                android.app.usage.NetworkStats.Bucket.TAG_NONE,
+                android.app.usage.NetworkStats.Bucket.STATE_DEFAULT,
+                android.app.usage.NetworkStats.Bucket.METERED_YES,
+                android.app.usage.NetworkStats.Bucket.ROAMING_NO,
+                android.app.usage.NetworkStats.Bucket.DEFAULT_NETWORK_ALL, 1024, 8, 2048, 12)
+        val entry = NetworkStatsUtils.fromBucket(bucket)
+        val expectedEntry = NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL,
+                NetworkStats.SET_DEFAULT, NetworkStats.TAG_NONE, NetworkStats.METERED_YES,
+                NetworkStats.ROAMING_NO, NetworkStats.DEFAULT_NETWORK_ALL, 1024, 8, 2048, 12,
+                0 /* operations */)
+
+        assertEntryEquals(expectedEntry, entry)
+    }
+
+    @Test
+    fun testPublicStatsToAndroidNetStats() {
+        val uid1 = 10001
+        val uid2 = 10002
+        val testIface = "wlan0"
+        val testAndroidNetStats = NetworkStats(0L, 3)
+                .addEntry(Entry(testIface, uid1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
+                .addEntry(Entry(
+                        testIface, uid2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 2, 7, 2, 5, 7))
+                .addEntry(Entry(testIface, uid2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 4, 5, 3, 1, 8))
+        val publicStats: android.app.usage.NetworkStats =
+                makePublicStatsFromAndroidNetStats(testAndroidNetStats)
+        val androidNetStats: NetworkStats =
+                NetworkStatsUtils.fromPublicNetworkStats(publicStats)
+
+        // 1. The public `NetworkStats` class does not include interface information.
+        //    Interface details must be removed and items with duplicated
+        //    keys need to be merged before making any comparisons.
+        // 2. The public `NetworkStats` class lacks an operations field.
+        //    Thus, the information will not be preserved during the conversion.
+        val expectedStats = NetworkStats(0L, 2)
+                .addEntry(Entry(null, uid1, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 0))
+                .addEntry(Entry(null, uid2, SET_DEFAULT, TAG_NONE,
+                        METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 6, 12, 5, 6, 0))
+        assertNetworkStatsEquals(expectedStats, androidNetStats)
+    }
+
+    private fun makeMockBucket(
+            uid: Int,
+            tag: Int,
+            state: Int,
+            metered: Int,
+            roaming: Int,
+            defaultNetwork: Int,
+            rxBytes: Long,
+            rxPackets: Long,
+            txBytes: Long,
+            txPackets: Long
+    ): android.app.usage.NetworkStats.Bucket {
+        val ret: android.app.usage.NetworkStats.Bucket =
+                mock(android.app.usage.NetworkStats.Bucket::class.java)
+        doReturn(uid).`when`(ret).getUid()
+        doReturn(tag).`when`(ret).getTag()
+        doReturn(state).`when`(ret).getState()
+        doReturn(metered).`when`(ret).getMetered()
+        doReturn(roaming).`when`(ret).getRoaming()
+        doReturn(defaultNetwork).`when`(ret).getDefaultNetworkStatus()
+        doReturn(rxBytes).`when`(ret).getRxBytes()
+        doReturn(rxPackets).`when`(ret).getRxPackets()
+        doReturn(txBytes).`when`(ret).getTxBytes()
+        doReturn(txPackets).`when`(ret).getTxPackets()
+        return ret
+    }
+}
\ No newline at end of file
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 963b27e..bf58443 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
@@ -38,6 +38,7 @@
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerLnbRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
@@ -69,6 +70,61 @@
     private TunerResourceManagerService mTunerResourceManagerService;
     private boolean mIsForeground;
 
+    private final class TunerClient extends IResourcesReclaimListener.Stub {
+        int[] mClientId;
+        ClientProfile mProfile;
+        boolean mReclaimed;
+
+        TunerClient() {
+            mClientId = new int[1];
+            mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+        }
+
+        public void register(String sessionId, int useCase) {
+            ResourceClientProfile profile = new ResourceClientProfile();
+            profile.tvInputSessionId = sessionId;
+            profile.useCase = useCase;
+            mTunerResourceManagerService.registerClientProfileInternal(
+                    profile, this, mClientId);
+            assertThat(mClientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+            mProfile = mTunerResourceManagerService.getClientProfile(mClientId[0]);
+        }
+
+        public void register(String sessionId, int useCase, int priority, int niceValue) {
+            register(sessionId, useCase);
+            mTunerResourceManagerService.updateClientPriorityInternal(
+                    mClientId[0], priority, niceValue);
+        }
+
+        public void register(String sessionId, int useCase, int priority) {
+            register(sessionId, useCase, priority, 0);
+        }
+
+        public void unregister() {
+            mTunerResourceManagerService.unregisterClientProfileInternal(mClientId[0]);
+            mClientId[0] = TunerResourceManagerService.INVALID_CLIENT_ID;
+            mReclaimed = false;
+        }
+
+        public int getId() {
+            return mClientId[0];
+        }
+
+        public ClientProfile getProfile() {
+            return mProfile;
+        }
+
+        @Override
+        public void onReclaimResources() {
+            mTunerResourceManagerService.clearAllResourcesAndClientMapping(mProfile);
+            mReclaimed = true;
+        }
+
+        public boolean isReclaimed() {
+            return mReclaimed;
+        }
+    }
+
     private static final class TestResourcesReclaimListener extends IResourcesReclaimListener.Stub {
         boolean mReclaimed;
 
@@ -247,13 +303,11 @@
     }
 
     @Test
-    public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() {
-        ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile, null /*listener*/, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+    public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[1];
@@ -262,21 +316,20 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
         assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
+        client0.unregister();
     }
 
     @Test
-    public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() {
-        ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile, null /*listener*/, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+    public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -295,27 +348,23 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(0);
+        client0.unregister();
     }
 
     @Test
-    public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() {
-        ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile0, null /*listener*/, clientId0);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile1, null /*listener*/, clientId1);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+    public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
@@ -335,13 +384,13 @@
 
         int[] frontendHandle = new int[1];
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
 
         request =
-                tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -349,31 +398,20 @@
                 .isTrue();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].handle).isInUse())
                 .isTrue();
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() {
+    public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority()
+            throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        profiles[1] = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientPriorities = {100, 50};
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[0], listener, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId0[0], clientPriorities[0], 0/*niceValue*/);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[1], new TestResourcesReclaimListener(), clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId1[0], clientPriorities[1], 0/*niceValue*/);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 50);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -384,46 +422,36 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
 
         request =
-                tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
-        assertThat(listener.isReclaimed()).isFalse();
+        assertThat(client0.isReclaimed()).isFalse();
 
         request =
-                tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+                tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
-        assertThat(listener.isReclaimed()).isFalse();
+        assertThat(client0.isReclaimed()).isFalse();
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() {
+    public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority()
+            throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        profiles[1] = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientPriorities = {100, 500};
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[0], listener, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId0[0], clientPriorities[0], 0/*niceValue*/);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[1], new TestResourcesReclaimListener(), clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId1[0], clientPriorities[1], 0/*niceValue*/);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -434,17 +462,16 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].handle, infos[1].handle)));
+        assertThat(client0.getProfile().getInUseFrontendHandles())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle)));
 
         request =
-                tunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
+                tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[1].handle);
@@ -453,22 +480,20 @@
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
                 .isInUse()).isTrue();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
-                .getOwnerClientId()).isEqualTo(clientId1[0]);
+                .getOwnerClientId()).isEqualTo(client1.getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
-                .getOwnerClientId()).isEqualTo(clientId1[0]);
-        assertThat(listener.isReclaimed()).isTrue();
+                .getOwnerClientId()).isEqualTo(client1.getId());
+        assertThat(client0.isReclaimed()).isTrue();
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void releaseFrontendTest_UnderTheSameExclusiveGroup() {
+    public void releaseFrontendTest_UnderTheSameExclusiveGroup() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[1];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -479,7 +504,7 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -488,43 +513,29 @@
                 .getFrontendResource(infos[1].handle).isInUse()).isTrue();
 
         // Release frontend
-        mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
-                .getFrontendResource(frontendHandle[0]), clientId[0]);
+        mTunerResourceManagerService.releaseFrontendInternal(frontendHandle[0], client0.getId());
         assertThat(mTunerResourceManagerService
                 .getFrontendResource(frontendHandle[0]).isInUse()).isFalse();
         assertThat(mTunerResourceManagerService
                 .getFrontendResource(infos[1].handle).isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0);
+        assertThat(client0.getProfile().getInUseFrontendHandles().size()).isEqualTo(0);
+        client0.unregister();
     }
 
     @Test
-    public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() {
+    public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        profiles[1] = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientPriorities = {100, 500};
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[0], listener, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId0[0], clientPriorities[0], 0/*niceValue*/);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[1], new TestResourcesReclaimListener(), clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId1[0], clientPriorities[1], 0/*niceValue*/);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
 
         // Init cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
-        CasSessionRequest request = casSessionRequest(clientId0[0], 1 /*casSystemId*/);
+        CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
         int[] casSessionHandle = new int[1];
         // Request for 2 cas sessions.
         assertThat(mTunerResourceManagerService
@@ -533,54 +544,45 @@
                 .requestCasSessionInternal(request, casSessionHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseCasSystemId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCasSystemId())
+                .isEqualTo(1);
         assertThat(mTunerResourceManagerService.getCasResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client0.getId())));
         assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
 
-        request = casSessionRequest(clientId1[0], 1);
+        request = casSessionRequest(client1.getId(), 1);
         assertThat(mTunerResourceManagerService
                 .requestCasSessionInternal(request, casSessionHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .getInUseCasSystemId()).isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(client1.getProfile().getInUseCasSystemId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCasSystemId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
         assertThat(mTunerResourceManagerService.getCasResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client1.getId())));
         assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
-        assertThat(listener.isReclaimed()).isTrue();
+        assertThat(client0.isReclaimed()).isTrue();
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority() {
+    public void requestCiCamTest_NoCiCamAvailable_RequestWithHigherPriority()
+            throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        profiles[1] = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientPriorities = {100, 500};
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[0], listener, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId0[0], clientPriorities[0], 0/*niceValue*/);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[1], new TestResourcesReclaimListener(), clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId1[0], clientPriorities[1], 0/*niceValue*/);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
 
         // Init cicam/cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
-        TunerCiCamRequest request = tunerCiCamRequest(clientId0[0], 1 /*ciCamId*/);
+        TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
         int[] ciCamHandle = new int[1];
         // Request for 2 ciCam sessions.
         assertThat(mTunerResourceManagerService
@@ -589,139 +591,125 @@
                 .requestCiCamInternal(request, ciCamHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseCiCamId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
         assertThat(mTunerResourceManagerService.getCiCamResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0])));
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client0.getId())));
         assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
 
-        request = tunerCiCamRequest(clientId1[0], 1);
+        request = tunerCiCamRequest(client1.getId(), 1);
         assertThat(mTunerResourceManagerService
                 .requestCiCamInternal(request, ciCamHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0])
-                .getInUseCiCamId()).isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(client1.getProfile().getInUseCiCamId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCiCamId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
         assertThat(mTunerResourceManagerService.getCiCamResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0])));
-        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
-        assertThat(listener.isReclaimed()).isTrue();
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client1.getId())));
+        assertThat(mTunerResourceManagerService
+                .getCiCamResource(1).isFullyUsed()).isFalse();
+        assertThat(client0.isReclaimed()).isTrue();
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void releaseCasTest() {
+    public void releaseCasTest() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[1];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
-        CasSessionRequest request = casSessionRequest(clientId[0], 1 /*casSystemId*/);
+        CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/);
         int[] casSessionHandle = new int[1];
         // Request for 1 cas sessions.
         assertThat(mTunerResourceManagerService
                 .requestCasSessionInternal(request, casSessionHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
-                .getInUseCasSystemId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCasSystemId()).isEqualTo(1);
         assertThat(mTunerResourceManagerService.getCasResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client0.getId())));
         assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
 
         // Release cas
         mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService
-                .getCasResource(1), clientId[0]);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
-                .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+                .getCasResource(1), client0.getId());
+        assertThat(client0.getProfile().getInUseCasSystemId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
         assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse();
         assertThat(mTunerResourceManagerService.getCasResource(1)
                 .getOwnerClientIds()).isEmpty();
+        client0.unregister();
     }
 
     @Test
-    public void releaseCiCamTest() {
+    public void releaseCiCamTest() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[1];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init cas resources.
         mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/);
 
-        TunerCiCamRequest request = tunerCiCamRequest(clientId[0], 1 /*ciCamId*/);
+        TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/);
         int[] ciCamHandle = new int[1];
         // Request for 1 ciCam sessions.
         assertThat(mTunerResourceManagerService
                 .requestCiCamInternal(request, ciCamHandle)).isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(ciCamHandle[0]))
                 .isEqualTo(1);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
-                .getInUseCiCamId()).isEqualTo(1);
+        assertThat(client0.getProfile().getInUseCiCamId()).isEqualTo(1);
         assertThat(mTunerResourceManagerService.getCiCamResource(1)
-                .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0])));
-        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+                .getOwnerClientIds()).isEqualTo(
+                        new HashSet<Integer>(Arrays.asList(client0.getId())));
+        assertThat(mTunerResourceManagerService
+                .getCiCamResource(1).isFullyUsed()).isFalse();
 
         // Release ciCam
         mTunerResourceManagerService.releaseCiCamInternal(mTunerResourceManagerService
-                .getCiCamResource(1), clientId[0]);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId[0])
-                .getInUseCiCamId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
-        assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isFalse();
+                .getCiCamResource(1), client0.getId());
+        assertThat(client0.getProfile().getInUseCiCamId())
+                .isEqualTo(ClientProfile.INVALID_RESOURCE_ID);
+        assertThat(mTunerResourceManagerService
+                .getCiCamResource(1).isFullyUsed()).isFalse();
         assertThat(mTunerResourceManagerService.getCiCamResource(1)
                 .getOwnerClientIds()).isEmpty();
+        client0.unregister();
     }
 
     @Test
-    public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() {
+    public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[2];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        profiles[1] = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientPriorities = {100, 500};
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[0], listener, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId0[0], clientPriorities[0], 0/*niceValue*/);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profiles[1], new TestResourcesReclaimListener(), clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                clientId1[0], clientPriorities[1], 0/*niceValue*/);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 100);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500);
 
         // Init lnb resources.
         int[] lnbHandles = {1};
         mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest();
-        request.clientId = clientId0[0];
+        request.clientId = client0.getId();
         int[] lnbHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
         assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
+        assertThat(client0.getProfile().getInUseLnbHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
 
         request = new TunerLnbRequest();
-        request.clientId = clientId1[0];
+        request.clientId = client1.getId();
 
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
@@ -729,29 +717,26 @@
         assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
                 .isInUse()).isTrue();
         assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
-                .getOwnerClientId()).isEqualTo(clientId1[0]);
-        assertThat(listener.isReclaimed()).isTrue();
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseLnbHandles().size()).isEqualTo(0);
+                .getOwnerClientId()).isEqualTo(client1.getId());
+        assertThat(client0.isReclaimed()).isTrue();
+        assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void releaseLnbTest() {
+    public void releaseLnbTest() throws RemoteException {
         // Register clients
-        ResourceClientProfile[] profiles = new ResourceClientProfile[1];
-        profiles[0] = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        TestResourcesReclaimListener listener = new TestResourcesReclaimListener();
-        mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init lnb resources.
         int[] lnbHandles = {0};
         mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest();
-        request.clientId = clientId[0];
+        request.clientId = client0.getId();
         int[] lnbHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
@@ -762,19 +747,16 @@
                 .getLnbResource(lnbHandle[0]));
         assertThat(mTunerResourceManagerService
                 .getLnbResource(lnbHandle[0]).isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(clientId[0]).getInUseLnbHandles().size()).isEqualTo(0);
+        assertThat(client0.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
+        client0.unregister();
     }
 
     @Test
-    public void unregisterClientTest_usingFrontend() {
-        // Register client
-        ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile, null /*listener*/, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+    public void unregisterClientTest_usingFrontend() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[2];
@@ -785,7 +767,7 @@
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
-                tunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
+                tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
@@ -796,26 +778,20 @@
                 .isInUse()).isTrue();
 
         // Unregister client when using frontend
-        mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
+        client0.unregister();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
                 .isInUse()).isFalse();
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
                 .isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
-
+        assertThat(mTunerResourceManagerService.checkClientExists(client0.getId())).isFalse();
     }
 
     @Test
-    public void requestDemuxTest() {
-        // Register client
-        ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId0 = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile0, null /*listener*/, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+    public void requestDemuxTest() throws RemoteException {
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         TunerDemuxInfo[] infos = new TunerDemuxInfo[3];
         infos[0] = tunerDemuxInfo(0 /* handle */, Filter.TYPE_TS | Filter.TYPE_IP);
@@ -825,7 +801,7 @@
 
         int[] demuxHandle0 = new int[1];
         // first with undefined type (should be the first one with least # of caps)
-        TunerDemuxRequest request = tunerDemuxRequest(clientId0[0], Filter.TYPE_UNDEFINED);
+        TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED);
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0))
                 .isTrue();
         assertThat(demuxHandle0[0]).isEqualTo(1);
@@ -846,16 +822,16 @@
         assertThat(demuxHandle0[0]).isEqualTo(2);
 
         // request for another TS
-        int[] clientId1 = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile1, null /*listener*/, clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client1 = new TunerClient();
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+
         int[] demuxHandle1 = new int[1];
-        TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_TS);
+        TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS);
         assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1))
                 .isTrue();
         assertThat(demuxHandle1[0]).isEqualTo(0);
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(demuxHandle1[0]))
+        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(client1.getId()))
                 .isEqualTo(0);
 
         // release demuxes
@@ -863,33 +839,23 @@
         mTunerResourceManagerService.releaseDemuxInternal(dr);
         dr = mTunerResourceManagerService.getDemuxResource(demuxHandle1[0]);
         mTunerResourceManagerService.releaseDemuxInternal(dr);
+
+        client0.unregister();
+        client1.unregister();
     }
 
     @Test
-    public void requestDemuxTest_ResourceReclaim() {
+    public void requestDemuxTest_ResourceReclaim() throws RemoteException {
         // Register clients
-        ResourceClientProfile profile0 = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        ResourceClientProfile profile1 = resourceClientProfile("1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
-        ResourceClientProfile profile2 = resourceClientProfile("2" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
-        int[] clientId0 = new int[1];
-        int[] clientId1 = new int[1];
-        int[] clientId2 = new int[1];
-        TestResourcesReclaimListener listener0 = new TestResourcesReclaimListener();
-        TestResourcesReclaimListener listener1 = new TestResourcesReclaimListener();
-        TestResourcesReclaimListener listener2 = new TestResourcesReclaimListener();
-
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile0, listener0, clientId0);
-        assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile1, listener1, clientId1);
-        assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile2, listener2, clientId1);
-        assertThat(clientId2[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient client0 = new TunerClient();
+        TunerClient client1 = new TunerClient();
+        TunerClient client2 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
+        client1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
+        client2.register("2" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN);
 
         // Init demux resources.
         TunerDemuxInfo[] infos = new TunerDemuxInfo[2];
@@ -897,66 +863,67 @@
         infos[1] = tunerDemuxInfo(1 /*handle*/, Filter.TYPE_TS);
         mTunerResourceManagerService.setDemuxInfoListInternal(infos);
 
-        // let clientId0(prio:100) request for IP - should succeed
-        TunerDemuxRequest request0 = tunerDemuxRequest(clientId0[0], Filter.TYPE_IP);
+        // let client0(prio:100) request for IP - should succeed
+        TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP);
         int[] demuxHandle0 = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request0, demuxHandle0)).isTrue();
         assertThat(demuxHandle0[0]).isEqualTo(0);
 
-        // let clientId1(prio:50) request for IP - should fail
-        TunerDemuxRequest request1 = tunerDemuxRequest(clientId1[0], Filter.TYPE_IP);
+        // let client1(prio:50) request for IP - should fail
+        TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP);
         int[] demuxHandle1 = new int[1];
         demuxHandle1[0] = -1;
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request1, demuxHandle1)).isFalse();
-        assertThat(listener0.isReclaimed()).isFalse();
+        assertThat(client0.isReclaimed()).isFalse();
         assertThat(demuxHandle1[0]).isEqualTo(-1);
 
-        // let clientId1(prio:50) request for TS - should succeed
+        // let client1(prio:50) request for TS - should succeed
         request1.desiredFilterTypes = Filter.TYPE_TS;
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request1, demuxHandle1)).isTrue();
         assertThat(demuxHandle1[0]).isEqualTo(1);
-        assertThat(listener0.isReclaimed()).isFalse();
+        assertThat(client0.isReclaimed()).isFalse();
 
-        // now release demux for the clientId0 (higher priority) and request demux
+        // now release demux for the client0 (higher priority) and request demux
         DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]);
         mTunerResourceManagerService.releaseDemuxInternal(dr);
 
-        // let clientId2(prio:50) request for TS - should succeed
-        TunerDemuxRequest request2 = tunerDemuxRequest(clientId2[0], Filter.TYPE_TS);
+        // let client2(prio:50) request for TS - should succeed
+        TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS);
         int[] demuxHandle2 = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request2, demuxHandle2)).isTrue();
         assertThat(demuxHandle2[0]).isEqualTo(0);
-        assertThat(listener1.isReclaimed()).isFalse();
+        assertThat(client1.isReclaimed()).isFalse();
 
-        // let clientId0(prio:100) request for TS - should reclaim from clientId2
+        // let client0(prio:100) request for TS - should reclaim from client1
         // , who has the smaller caps
         request0.desiredFilterTypes = Filter.TYPE_TS;
         assertThat(mTunerResourceManagerService
                 .requestDemuxInternal(request0, demuxHandle0)).isTrue();
-        assertThat(listener1.isReclaimed()).isFalse();
-        assertThat(listener2.isReclaimed()).isTrue();
+        assertThat(client1.isReclaimed()).isTrue();
+        assertThat(client2.isReclaimed()).isFalse();
+        client0.unregister();
+        client1.unregister();
+        client2.unregister();
     }
 
     @Test
     public void requestDescramblerTest() {
-        // Register client
-        ResourceClientProfile profile = resourceClientProfile("0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
-        int[] clientId = new int[1];
-        mTunerResourceManagerService.registerClientProfileInternal(
-                profile, null /*listener*/, clientId);
-        assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        // Register clients
+        TunerClient client0 = new TunerClient();
+        client0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK);
 
         int[] desHandle = new int[1];
         TunerDescramblerRequest request = new TunerDescramblerRequest();
-        request.clientId = clientId[0];
+        request.clientId = client0.getId();
         assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle))
                 .isTrue();
         assertThat(mTunerResourceManagerService.getResourceIdFromHandle(desHandle[0])).isEqualTo(0);
+        client0.unregister();
     }
 
     @Test
@@ -978,74 +945,26 @@
     }
 
     @Test
-    public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() {
+    public void shareFrontendTest_FrontendWithExclusiveGroupReadyToShare() throws RemoteException {
         /**** Register Clients and Set Priority ****/
-
-        // Int array to save the returned client ids
-        int[] ownerClientId0 = new int[1];
-        int[] ownerClientId1 = new int[1];
-        int[] shareClientId0 = new int[1];
-        int[] shareClientId1 = new int[1];
-
-        // Predefined client profiles
-        ResourceClientProfile[] ownerProfiles = new ResourceClientProfile[2];
-        ResourceClientProfile[] shareProfiles = new ResourceClientProfile[2];
-        ownerProfiles[0] = resourceClientProfile(
-                "0" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
-        ownerProfiles[1] = resourceClientProfile(
-                "1" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE);
-        shareProfiles[0] = resourceClientProfile(
-                "2" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
-        shareProfiles[1] = resourceClientProfile(
-                "3" /*sessionId*/,
-                TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD);
-
-        // Predefined client reclaim listeners
-        TestResourcesReclaimListener ownerListener0 = new TestResourcesReclaimListener();
-        TestResourcesReclaimListener shareListener0 = new TestResourcesReclaimListener();
-        TestResourcesReclaimListener ownerListener1 = new TestResourcesReclaimListener();
-        TestResourcesReclaimListener shareListener1 = new TestResourcesReclaimListener();
-        // Register clients and validate the returned client ids
-        mTunerResourceManagerService
-                .registerClientProfileInternal(ownerProfiles[0], ownerListener0, ownerClientId0);
-        mTunerResourceManagerService
-                .registerClientProfileInternal(shareProfiles[0], shareListener0, shareClientId0);
-        mTunerResourceManagerService
-                .registerClientProfileInternal(ownerProfiles[1], ownerListener1, ownerClientId1);
-        mTunerResourceManagerService
-                .registerClientProfileInternal(shareProfiles[1], shareListener1, shareClientId1);
-        assertThat(ownerClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        assertThat(shareClientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        assertThat(ownerClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
-        assertThat(shareClientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
+        TunerClient ownerClient0 = new TunerClient();
+        TunerClient ownerClient1 = new TunerClient();
+        TunerClient shareClient0 = new TunerClient();
+        TunerClient shareClient1 = new TunerClient();
+        ownerClient0.register("0" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 100);
+        ownerClient1.register("1" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 300);
+        shareClient0.register("2" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 200);
+        shareClient1.register("3" /*sessionId*/,
+                        TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 400);
 
         mTunerResourceManagerService.updateClientPriorityInternal(
-                ownerClientId0[0],
-                100/*priority*/,
-                0/*niceValue*/);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                shareClientId0[0],
-                200/*priority*/,
-                0/*niceValue*/);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                ownerClientId1[0],
-                300/*priority*/,
-                0/*niceValue*/);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                shareClientId1[0],
-                400/*priority*/,
-                0/*niceValue*/);
-        mTunerResourceManagerService.updateClientPriorityInternal(
-                shareClientId1[0],
+                shareClient1.getId(),
                 -1/*invalid priority*/,
                 0/*niceValue*/);
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId1[0])
-                .getPriority())
-                .isEqualTo(400);
+        assertThat(shareClient1.getProfile().getPriority()).isEqualTo(400);
 
         /**** Init Frontend Resources ****/
 
@@ -1072,7 +991,7 @@
         // Predefined frontend request and array to save returned frontend handle
         int[] frontendHandle = new int[1];
         TunerFrontendRequest request = tunerFrontendRequest(
-                ownerClientId0[0] /*clientId*/,
+                ownerClient0.getId() /*clientId*/,
                 FrontendSettings.TYPE_DVBT);
 
         // Request call and validate granted resource and internal mapping
@@ -1080,9 +999,7 @@
                 .requestFrontendInternal(request, frontendHandle))
                 .isTrue();
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendHandles())
+        assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
@@ -1091,11 +1008,11 @@
 
         // Share frontend call and validate the internal mapping
         mTunerResourceManagerService.shareFrontendInternal(
-                shareClientId0[0]/*selfClientId*/,
-                ownerClientId0[0]/*targetClientId*/);
+                shareClient0.getId()/*selfClientId*/,
+                ownerClient0.getId()/*targetClientId*/);
         mTunerResourceManagerService.shareFrontendInternal(
-                shareClientId1[0]/*selfClientId*/,
-                ownerClientId0[0]/*targetClientId*/);
+                shareClient1.getId()/*selfClientId*/,
+                ownerClient0.getId()/*targetClientId*/);
         // Verify fe in use status
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
                 .isInUse()).isTrue();
@@ -1103,31 +1020,24 @@
                 .isInUse()).isTrue();
         // Verify fe owner status
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
-                .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+                .getOwnerClientId()).isEqualTo(ownerClient0.getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
-                .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
+                .getOwnerClientId()).isEqualTo(ownerClient0.getId());
         // Verify share fe client status in the primary owner client
-        assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
-                .getShareFeClientIds())
+        assertThat(ownerClient0.getProfile().getShareFeClientIds())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        shareClientId0[0],
-                        shareClientId1[0])));
+                        shareClient0.getId(),
+                        shareClient1.getId())));
         // Verify in use frontend list in all the primary owner and share owner clients
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendHandles())
+        assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
-                .getInUseFrontendHandles())
+        assertThat(shareClient0.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId1[0])
-                .getInUseFrontendHandles())
+        assertThat(shareClient1.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
@@ -1135,21 +1045,17 @@
         /**** Remove Frontend Share Owner ****/
 
         // Unregister the second share fe client
-        mTunerResourceManagerService.unregisterClientProfileInternal(shareClientId1[0]);
+        shareClient1.unregister();
 
         // Validate the internal mapping
-        assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
-                .getShareFeClientIds())
+        assertThat(ownerClient0.getProfile().getShareFeClientIds())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        shareClientId0[0])));
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendHandles())
+                        shareClient0.getId())));
+        assertThat(ownerClient0.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
+        assertThat(shareClient0.getProfile()
                 .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
@@ -1159,7 +1065,7 @@
 
         // Predefined second frontend request
         request = tunerFrontendRequest(
-                ownerClientId1[0] /*clientId*/,
+                ownerClient1.getId() /*clientId*/,
                 FrontendSettings.TYPE_DVBT);
 
         // Second request call
@@ -1170,43 +1076,35 @@
         // Validate granted resource and internal mapping
         assertThat(frontendHandle[0]).isEqualTo(infos[0].handle);
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
-                .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
+                .getOwnerClientId()).isEqualTo(ownerClient1.getId());
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
-                .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId1[0])
-                .getInUseFrontendHandles())
+                .getOwnerClientId()).isEqualTo(ownerClient1.getId());
+        assertThat(ownerClient1.getProfile().getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         infos[0].handle,
                         infos[1].handle)));
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendHandles()
+        assertThat(ownerClient0.getProfile().getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
-                .getInUseFrontendHandles()
+        assertThat(shareClient0.getProfile().getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId0[0])
-                .getShareFeClientIds()
+        assertThat(ownerClient0.getProfile().getShareFeClientIds()
                 .isEmpty())
                 .isTrue();
-        assertThat(ownerListener0.isReclaimed()).isTrue();
-        assertThat(shareListener0.isReclaimed()).isTrue();
+        assertThat(ownerClient0.isReclaimed()).isTrue();
+        assertThat(shareClient0.isReclaimed()).isTrue();
 
         /**** Release Frontend Resource From Primary Owner ****/
 
         // Reshare the frontend
         mTunerResourceManagerService.shareFrontendInternal(
-                shareClientId0[0]/*selfClientId*/,
-                ownerClientId1[0]/*targetClientId*/);
+                shareClient0.getId()/*selfClientId*/,
+                ownerClient1.getId()/*targetClientId*/);
 
         // Release the frontend resource from the primary owner
-        mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
-                .getFrontendResource(infos[0].handle), ownerClientId1[0]);
+        mTunerResourceManagerService.releaseFrontendInternal(infos[0].handle,
+                ownerClient1.getId());
 
         // Validate the internal mapping
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1214,19 +1112,13 @@
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
                 .isInUse()).isFalse();
         // Verify client status
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId1[0])
-                .getInUseFrontendHandles()
+        assertThat(ownerClient1.getProfile().getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
-                .getInUseFrontendHandles()
+        assertThat(shareClient0.getProfile().getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(ownerClientId1[0])
-                .getShareFeClientIds()
+        assertThat(ownerClient1.getProfile().getShareFeClientIds()
                 .isEmpty())
                 .isTrue();
 
@@ -1234,7 +1126,7 @@
 
         // Predefined Lnb request and handle array
         TunerLnbRequest requestLnb = new TunerLnbRequest();
-        requestLnb.clientId = shareClientId0[0];
+        requestLnb.clientId = shareClient0.getId();
         int[] lnbHandle = new int[1];
 
         // Request for an Lnb
@@ -1247,11 +1139,11 @@
                 .requestFrontendInternal(request, frontendHandle))
                 .isTrue();
         mTunerResourceManagerService.shareFrontendInternal(
-                shareClientId0[0]/*selfClientId*/,
-                ownerClientId1[0]/*targetClientId*/);
+                shareClient0.getId()/*selfClientId*/,
+                ownerClient1.getId()/*targetClientId*/);
 
         // Unregister the primary owner of the shared frontend
-        mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
+        ownerClient1.unregister();
 
         // Validate the internal mapping
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].handle)
@@ -1259,16 +1151,15 @@
         assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle)
                 .isInUse()).isFalse();
         // Verify client status
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
-                .getInUseFrontendHandles()
+        assertThat(shareClient0.getProfile().getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getClientProfile(shareClientId0[0])
-                .getInUseLnbHandles())
+        assertThat(shareClient0.getProfile().getInUseLnbHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
                         lnbHandles[0])));
+
+        ownerClient0.unregister();
+        shareClient0.unregister();
     }
 
     private TunerFrontendInfo tunerFrontendInfo(
diff --git a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
index e27bb4c..b9ece93 100644
--- a/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/StubTransaction.java
@@ -40,12 +40,19 @@
 public class StubTransaction extends SurfaceControl.Transaction {
 
     private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>();
+    private HashSet<SurfaceControl.TransactionCommittedListener> mTransactionCommittedListeners =
+            new HashSet<>();
 
     @Override
     public void apply() {
         for (Runnable listener : mWindowInfosReportedListeners) {
             listener.run();
         }
+        for (SurfaceControl.TransactionCommittedListener listener
+                : mTransactionCommittedListeners) {
+            listener.onTransactionCommitted();
+        }
+        mTransactionCommittedListeners.clear();
     }
 
     @Override
@@ -239,6 +246,9 @@
     @Override
     public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor,
             SurfaceControl.TransactionCommittedListener listener) {
+        SurfaceControl.TransactionCommittedListener listenerInner =
+                () -> executor.execute(listener::onTransactionCommitted);
+        mTransactionCommittedListeners.add(listenerInner);
         return this;
     }
 
diff --git a/services/tests/timetests/Android.bp b/services/tests/timetests/Android.bp
index 23ab859..05a1433 100644
--- a/services/tests/timetests/Android.bp
+++ b/services/tests/timetests/Android.bp
@@ -25,3 +25,27 @@
     certificate: "platform",
     test_suites: ["device-tests"],
 }
+
+test_module_config {
+    name: "FrameworksTimeServicesTests_time",
+    base: "FrameworksTimeServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: [
+        "com.android.server.timezonedetector.",
+        "com.android.server.timedetector.",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksTimeServicesTests_server_timedetector",
+    base: "FrameworksTimeServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.timedetector."],
+}
+
+test_module_config {
+    name: "FrameworksTimeServicesTests_server_timezonedetector",
+    base: "FrameworksTimeServicesTests",
+    test_suites: ["device-tests"],
+    include_filters: ["com.android.server.timezonedetector."],
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index b3ec215..c9d5241 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -30,6 +30,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.pm.UserManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.After;
@@ -41,6 +42,7 @@
 
 public class UiServiceTestCase {
     @Mock protected PackageManagerInternal mPmi;
+    @Mock protected UserManagerInternal mUmi;
     @Mock protected UriGrantsManagerInternal mUgmInternal;
 
     protected static final String PKG_N_MR1 = "com.example.n_mr1";
@@ -92,6 +94,8 @@
                     }
                 });
 
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        LocalServices.addService(UserManagerInternal.class, mUmi);
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
         when(mUgmInternal.checkGrantUriPermission(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
index f6e1162..af7f703 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.notification;
 
-import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_FALSE;
 import static android.service.notification.Condition.STATE_TRUE;
 
@@ -31,13 +30,11 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
-import android.app.Flags;
 import android.content.ComponentName;
 import android.content.ServiceConnection;
 import android.content.pm.IPackageManager;
 import android.net.Uri;
 import android.os.IInterface;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.Condition;
 
@@ -150,57 +147,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MODES_UI)
-    public void notifyConditions_appCannotUndoUserEnablement() {
-        ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
-                mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
-                mock(ServiceConnection.class), 33, 100);
-        // First, user enabled mode
-        Condition[] userConditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
-        };
-        mProviders.notifyConditions("package", msi, userConditions);
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
-        // Second, app tries to disable it, but cannot
-        Condition[] appConditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", STATE_FALSE)
-        };
-        mProviders.notifyConditions("package", msi, appConditions);
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_UI)
-    public void notifyConditions_appCanTakeoverUserEnablement() {
-        ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
-                mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
-                mock(ServiceConnection.class), 33, 100);
-        // First, user enabled mode
-        Condition[] userConditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
-        };
-        mProviders.notifyConditions("package", msi, userConditions);
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
-
-        // Second, app now thinks the rule should be on due it its intelligence
-        Condition[] appConditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", STATE_TRUE)
-        };
-        mProviders.notifyConditions("package", msi, appConditions);
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
-        // Lastly, app can turn rule off when its intelligence think it should be off
-        appConditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", STATE_FALSE)
-        };
-        mProviders.notifyConditions("package", msi, appConditions);
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
-
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
     public void testRemoveDefaultFromConfig() {
         final int userId = 0;
         ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
index 7933f7a..4a19973 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -46,6 +46,7 @@
 import android.content.IntentFilter;
 import android.hardware.display.ColorDisplayManager;
 import android.os.PowerManager;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.ZenDeviceEffects;
 import android.testing.TestableContext;
@@ -64,6 +65,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 @RunWith(TestParameterInjector.class)
 public class DefaultDeviceEffectsApplierTest {
 
@@ -89,6 +93,8 @@
 
         mApplier = new DefaultDeviceEffectsApplier(mContext);
         verify(mWallpaperManager).isWallpaperSupported();
+
+        ZenLog.clear();
     }
 
     @Test
@@ -110,6 +116,41 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_MODES_API)
+    public void apply_logsToZenLog() {
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        ArgumentCaptor<IntentFilter> intentFilterCaptor =
+                ArgumentCaptor.forClass(IntentFilter.class);
+
+        ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+                .setShouldDisplayGrayscale(true)
+                .setShouldUseNightMode(true)
+                .build();
+        mApplier.apply(effects, ORIGIN_APP);
+
+        String zenLog = getZenLog();
+        assertThat(zenLog).contains("apply_device_effect: displayGrayscale -> true");
+        assertThat(zenLog).contains("schedule_device_effect: nightMode -> true");
+        assertThat(zenLog).doesNotContain("apply_device_effect: nightMode");
+
+        verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(),
+                intentFilterCaptor.capture(), anyInt());
+        BroadcastReceiver screenOffReceiver = broadcastReceiverCaptor.getValue();
+        screenOffReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
+
+        zenLog = getZenLog();
+        assertThat(zenLog).contains("apply_device_effect: nightMode -> true");
+    }
+
+    private static String getZenLog() {
+        StringWriter zenLogWriter = new StringWriter();
+        ZenLog.dump(new PrintWriter(zenLogWriter), "");
+        return zenLogWriter.toString();
+    }
+
+    @Test
     public void apply_removesEffects() {
         mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 225c1dc..b97a268 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.notification;
 
+import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME;
 import static android.app.Notification.COLOR_DEFAULT;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_BUBBLE;
@@ -31,10 +32,12 @@
 import static android.app.Notification.VISIBILITY_SECRET;
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
 import static android.service.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUPING;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 
-import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static com.android.server.notification.Flags.FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS;
 import static com.android.server.notification.GroupHelper.AGGREGATE_GROUP_KEY;
 import static com.android.server.notification.GroupHelper.AUTOGROUP_KEY;
 import static com.android.server.notification.GroupHelper.BASE_FLAGS;
@@ -2203,7 +2206,7 @@
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
                 eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
         verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
-                eq(expectedGroupKey_silent), eq(false));
+                eq(expectedGroupKey_silent), eq(true));
 
         // Check that the alerting section group is removed
         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
@@ -2263,13 +2266,15 @@
                 notificationList);
 
         // Check that channel1's notifications are moved to the silent section group
-        expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
-                mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
-                "TEST_CHANNEL_ID1");
-        verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
-                eq(expectedGroupKey_silent), anyInt(), eq(expectedSummaryAttr));
-        verify(mCallback, times(AUTOGROUP_AT_COUNT/2 + 1)).addAutoGroup(anyString(),
-                eq(expectedGroupKey_silent), eq(false));
+        // But not enough to auto-group => remove override group key
+        verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+                anyString(), anyInt(), any());
+        verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+        for (NotificationRecord record: notificationList) {
+            if (record.getChannel().getId().equals(channel1.getId())) {
+                assertThat(record.getSbn().getOverrideGroupKey()).isNull();
+            }
+        }
 
         // Check that the alerting section group is not removed, only updated
         expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
@@ -2342,7 +2347,7 @@
         verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
                 eq(expectedGroupKey_silent), anyInt(), eq(getNotificationAttributes(BASE_FLAGS)));
         verify(mCallback, times(numSilentGroupNotifications)).addAutoGroup(anyString(),
-                eq(expectedGroupKey_silent), eq(false));
+                eq(expectedGroupKey_silent), eq(true));
 
         // Check that the alerting section group is removed
         verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), eq(pkg),
@@ -2352,6 +2357,60 @@
     }
 
     @Test
+    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+    public void testAutogroup_updateChannel_reachedMinAutogroupCount() {
+        final String pkg = "package";
+        final NotificationChannel channel1 = new NotificationChannel("TEST_CHANNEL_ID1",
+                "TEST_CHANNEL_ID1", IMPORTANCE_DEFAULT);
+        final NotificationChannel channel2 = new NotificationChannel("TEST_CHANNEL_ID2",
+                "TEST_CHANNEL_ID2", IMPORTANCE_LOW);
+        final List<NotificationRecord> notificationList = new ArrayList<>();
+        // Post notifications with different channels that would autogroup in different sections
+        NotificationRecord r;
+        // Not enough notifications to autogroup initially
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+            if (i % 2 == 0) {
+                r = getNotificationRecord(pkg, i, String.valueOf(i),
+                    UserHandle.SYSTEM, null, false, channel1);
+            } else {
+                r = getNotificationRecord(pkg, i, String.valueOf(i),
+                    UserHandle.SYSTEM, null, false, channel2);
+            }
+            notificationList.add(r);
+            mGroupHelper.onNotificationPosted(r, false);
+        }
+        verify(mCallback, never()).addAutoGroupSummary(anyInt(), anyString(), anyString(),
+                anyString(), anyInt(), any());
+        verify(mCallback, never()).addAutoGroup(anyString(), anyString(), anyBoolean());
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString());
+        verify(mCallback, never()).updateAutogroupSummary(anyInt(), anyString(), anyString(),
+                any());
+        Mockito.reset(mCallback);
+
+        // Update channel1's importance
+        final String expectedGroupKey_silent = GroupHelper.getFullAggregateGroupKey(pkg,
+                AGGREGATE_GROUP_KEY + "SilentSection", UserHandle.SYSTEM.getIdentifier());
+        channel1.setImportance(IMPORTANCE_LOW);
+        for (NotificationRecord record: notificationList) {
+            if (record.getChannel().getId().equals(channel1.getId())) {
+                record.updateNotificationChannel(channel1);
+            }
+        }
+        mGroupHelper.onChannelUpdated(UserHandle.SYSTEM.getIdentifier(), pkg, channel1,
+                notificationList);
+
+        // Check that channel1's notifications are moved to the silent section & autogroup all
+        NotificationAttributes expectedSummaryAttr = new NotificationAttributes(BASE_FLAGS,
+                mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT,
+                "TEST_CHANNEL_ID1");
+        verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString(),
+                eq(expectedGroupKey_silent), eq(true));
+        verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(),
+                eq(expectedGroupKey_silent), anyInt(), eq(expectedSummaryAttr));
+    }
+
+    @Test
     @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
             Flags.FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS})
     public void testNoGroup_singletonGroup_underLimit() {
@@ -2520,15 +2579,9 @@
 
     @Test
     @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
-    public void testGroupSectioners() {
-        final NotificationRecord notification_alerting = getNotificationRecord(mPkg, 0, "", mUser,
-            "", false, IMPORTANCE_DEFAULT);
-        assertThat(GroupHelper.getSection(notification_alerting).mName).isEqualTo("AlertingSection");
-
-        final NotificationRecord notification_silent = getNotificationRecord(mPkg, 0, "", mUser,
-            "", false, IMPORTANCE_LOW);
-        assertThat(GroupHelper.getSection(notification_silent).mName).isEqualTo("SilentSection");
-
+    @DisableFlags(FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS)
+    public void testNonGroupableNotifications() {
+        // Check that there is no valid section for: conversations, calls, foreground services
         NotificationRecord notification_conversation = mock(NotificationRecord.class);
         when(notification_conversation.isConversation()).thenReturn(true);
         assertThat(GroupHelper.getSection(notification_conversation)).isNull();
@@ -2545,7 +2598,7 @@
         assertThat(GroupHelper.getSection(notification_call)).isNull();
 
         NotificationRecord notification_colorFg = spy(getNotificationRecord(mPkg, 0, "", mUser,
-            "", false, IMPORTANCE_LOW));
+                "", false, IMPORTANCE_LOW));
         sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
         n = mock(Notification.class);
         when(notification_colorFg.isConversation()).thenReturn(false);
@@ -2558,4 +2611,173 @@
         assertThat(GroupHelper.getSection(notification_colorFg)).isNull();
     }
 
+    @Test
+    @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING)
+    @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+    public void testGroupSectioners() {
+        final NotificationRecord notification_alerting = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_DEFAULT);
+        assertThat(GroupHelper.getSection(notification_alerting).mName).isEqualTo(
+                "AlertingSection");
+
+        final NotificationRecord notification_silent = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_LOW);
+        assertThat(GroupHelper.getSection(notification_silent).mName).isEqualTo("SilentSection");
+
+        // Check that special categories are grouped by their importance
+        final NotificationChannel promoChannel = new NotificationChannel(
+                NotificationChannel.PROMOTIONS_ID, NotificationChannel.PROMOTIONS_ID,
+                IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_promotion = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, promoChannel);
+        assertThat(GroupHelper.getSection(notification_promotion).mName).isEqualTo(
+                "AlertingSection");
+
+        final NotificationChannel newsChannel = new NotificationChannel(NotificationChannel.NEWS_ID,
+                NotificationChannel.NEWS_ID, IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_news = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, newsChannel);
+        assertThat(GroupHelper.getSection(notification_news).mName).isEqualTo(
+                "AlertingSection");
+
+        final NotificationChannel socialChannel = new NotificationChannel(
+                NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+                IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_social = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, socialChannel);
+        assertThat(GroupHelper.getSection(notification_social).mName).isEqualTo(
+                "AlertingSection");
+
+        final NotificationChannel recsChannel = new NotificationChannel(NotificationChannel.RECS_ID,
+                NotificationChannel.RECS_ID, IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_recs = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, recsChannel);
+        assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
+                "AlertingSection");
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_CLASSIFICATION})
+    public void testGroupSectioners_withClassificationSections() {
+        final NotificationRecord notification_alerting = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_DEFAULT);
+        assertThat(GroupHelper.getSection(notification_alerting).mName).isEqualTo(
+                "AlertingSection");
+
+        final NotificationRecord notification_silent = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_LOW);
+        assertThat(GroupHelper.getSection(notification_silent).mName).isEqualTo("SilentSection");
+
+        // Check that special categories are grouped in their own sections
+        final NotificationChannel promoChannel = new NotificationChannel(
+                NotificationChannel.PROMOTIONS_ID, NotificationChannel.PROMOTIONS_ID,
+                IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_promotion = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, promoChannel);
+        assertThat(GroupHelper.getSection(notification_promotion).mName).isEqualTo(
+                "PromotionsSection");
+
+        final NotificationChannel newsChannel = new NotificationChannel(NotificationChannel.NEWS_ID,
+                NotificationChannel.NEWS_ID, IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_news = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, newsChannel);
+        assertThat(GroupHelper.getSection(notification_news).mName).isEqualTo(
+                "NewsSection");
+
+        final NotificationChannel socialChannel = new NotificationChannel(
+                NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID,
+                IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_social = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, socialChannel);
+        assertThat(GroupHelper.getSection(notification_social).mName).isEqualTo(
+                "SocialSection");
+
+        final NotificationChannel recsChannel = new NotificationChannel(NotificationChannel.RECS_ID,
+                NotificationChannel.RECS_ID, IMPORTANCE_DEFAULT);
+        final NotificationRecord notification_recs = getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, recsChannel);
+        assertThat(GroupHelper.getSection(notification_recs).mName).isEqualTo(
+                "RecsSection");
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+    public void testNonGroupableNotifications_forceGroupConversations() {
+        // Check that there is no valid section for: calls, foreground services
+        NotificationRecord notification_call = spy(getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_LOW));
+        Notification n = mock(Notification.class);
+        StatusBarNotification sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
+        when(notification_call.isConversation()).thenReturn(false);
+        when(notification_call.getNotification()).thenReturn(n);
+        when(notification_call.getSbn()).thenReturn(sbn);
+        when(sbn.getNotification()).thenReturn(n);
+        when(n.isStyle(Notification.CallStyle.class)).thenReturn(true);
+        assertThat(GroupHelper.getSection(notification_call)).isNull();
+
+        NotificationRecord notification_colorFg = spy(getNotificationRecord(mPkg, 0, "", mUser,
+                "", false, IMPORTANCE_LOW));
+        sbn = spy(getSbn("package", 0, "0", UserHandle.SYSTEM));
+        n = mock(Notification.class);
+        when(notification_colorFg.isConversation()).thenReturn(false);
+        when(notification_colorFg.getNotification()).thenReturn(n);
+        when(notification_colorFg.getSbn()).thenReturn(sbn);
+        when(sbn.getNotification()).thenReturn(n);
+        when(n.isForegroundService()).thenReturn(true);
+        when(n.isColorized()).thenReturn(true);
+        when(n.isStyle(Notification.CallStyle.class)).thenReturn(false);
+        assertThat(GroupHelper.getSection(notification_colorFg)).isNull();
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS})
+    @DisableFlags(FLAG_SORT_SECTION_BY_TIME)
+    public void testConversationGroupSections_disableSortSectionByTime() {
+        // Check that there are separate sections for conversations: alerting and silent
+        NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
+                mUser, "", false, IMPORTANCE_LOW);
+        notification_conversation_silent = spy(notification_conversation_silent);
+        when(notification_conversation_silent.isConversation()).thenReturn(true);
+        assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
+                "PeopleSection(silent)");
+
+        // Check that there is a correct section for conversations
+        NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
+                mUser, "", false, IMPORTANCE_DEFAULT);
+        notification_conversation_alerting = spy(notification_conversation_alerting);
+        when(notification_conversation_alerting.isConversation()).thenReturn(true);
+        assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
+                "PeopleSection(alerting)");
+    }
+
+    @Test
+    @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING,
+            FLAG_NOTIFICATION_FORCE_GROUP_CONVERSATIONS,
+            FLAG_SORT_SECTION_BY_TIME})
+    public void testConversationGroupSections() {
+        // Check that there is a single section for silent/alerting conversations
+        NotificationRecord notification_conversation_silent = getNotificationRecord(mPkg, 0, "",
+                mUser, "", false, IMPORTANCE_LOW);
+        notification_conversation_silent = spy(notification_conversation_silent);
+        when(notification_conversation_silent.isConversation()).thenReturn(true);
+        assertThat(GroupHelper.getSection(notification_conversation_silent).mName).isEqualTo(
+                "PeopleSection");
+
+        NotificationRecord notification_conversation_alerting = getNotificationRecord(mPkg, 0, "",
+                mUser, "", false, IMPORTANCE_DEFAULT);
+        notification_conversation_alerting = spy(notification_conversation_alerting);
+        when(notification_conversation_alerting.isConversation()).thenReturn(true);
+        assertThat(GroupHelper.getSection(notification_conversation_alerting).mName).isEqualTo(
+                "PeopleSection");
+
+        // Check that there is a section for priority conversations
+        NotificationRecord notification_conversation_prio = getNotificationRecord(mPkg, 0, "",
+                mUser, "", false, IMPORTANCE_DEFAULT);
+        notification_conversation_prio = spy(notification_conversation_prio);
+        when(notification_conversation_prio.isConversation()).thenReturn(true);
+        notification_conversation_prio.getChannel().setImportantConversation(true);
+        assertThat(GroupHelper.getSection(notification_conversation_prio).mName).isEqualTo(
+                "PeopleSection(priority)");
+    }
+
 }
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 fb82b872c..48bc9d7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -888,7 +888,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
         service.addApprovedList("a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -919,7 +919,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
         service.addApprovedList("a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -950,7 +950,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
         service.addApprovedList("a/a", 0, true);
 
         service.reregisterService(cn, 0);
@@ -981,7 +981,7 @@
             return true;
         });
 
-        mockServiceInfoWithMetaData(List.of(cn), service, pm, new ArrayMap<>());
+        mockServiceInfoWithMetaData(List.of(cn), service, new ArrayMap<>());
         service.addApprovedList("a/a", 0, false);
 
         service.reregisterService(cn, 0);
@@ -1211,64 +1211,6 @@
     }
 
     @Test
-    public void testUpgradeAppNoIntentFilterNoRebind() throws Exception {
-        Context context = spy(getContext());
-        doReturn(true).when(context).bindServiceAsUser(any(), any(), anyInt(), any());
-
-        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles,
-                mIpm, APPROVAL_BY_COMPONENT);
-
-        List<String> packages = new ArrayList<>();
-        packages.add("package");
-        addExpectedServices(service, packages, 0);
-
-        final ComponentName unapprovedComponent = ComponentName.unflattenFromString("package/C1");
-        final ComponentName approvedComponent = ComponentName.unflattenFromString("package/C2");
-
-        // Both components are approved initially
-        mExpectedPrimaryComponentNames.clear();
-        mExpectedPrimaryPackages.clear();
-        mExpectedPrimaryComponentNames.put(0, "package/C1:package/C2");
-        mExpectedSecondaryComponentNames.clear();
-        mExpectedSecondaryPackages.clear();
-
-        loadXml(service);
-
-        //Component package/C1 loses serviceInterface intent filter
-        ManagedServices.Config config = service.getConfig();
-        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
-            thenAnswer(new Answer<List<ResolveInfo>>() {
-                @Override
-                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
-                    throws Throwable {
-                    Object[] args = invocationOnMock.getArguments();
-                    Intent invocationIntent = (Intent) args[0];
-                    if (invocationIntent != null) {
-                        if (invocationIntent.getAction().equals(config.serviceInterface)
-                            && packages.contains(invocationIntent.getPackage())) {
-                            List<ResolveInfo> dummyServices = new ArrayList<>();
-                            ResolveInfo resolveInfo = new ResolveInfo();
-                            ServiceInfo serviceInfo = new ServiceInfo();
-                            serviceInfo.packageName = invocationIntent.getPackage();
-                            serviceInfo.name = approvedComponent.getClassName();
-                            serviceInfo.permission = service.getConfig().bindPermission;
-                            resolveInfo.serviceInfo = serviceInfo;
-                            dummyServices.add(resolveInfo);
-                            return dummyServices;
-                        }
-                    }
-                    return new ArrayList<>();
-                }
-            });
-
-        // Trigger package update
-        service.onPackagesChanged(false, new String[]{"package"}, new int[]{0});
-
-        assertFalse(service.isComponentEnabledForCurrentProfiles(unapprovedComponent));
-        assertTrue(service.isComponentEnabledForCurrentProfiles(approvedComponent));
-    }
-
-    @Test
     public void testSetPackageOrComponentEnabled() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
@@ -1542,6 +1484,7 @@
         assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c")));
     }
 
+    @SuppressWarnings("GuardedBy")
     @Test
     public void populateComponentsToBind() {
         ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
@@ -1565,7 +1508,8 @@
 
         SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
 
-        service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser);
+        service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
+                /* isVisibleBackgroundUser= */ false);
 
         assertEquals(2, componentsToBind.size());
         assertEquals(1, componentsToBind.get(0).size());
@@ -1575,6 +1519,33 @@
         assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c")));
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void populateComponentsToBind_isVisibleBackgroundUser_addComponentsToBindButNotAddToEnabledComponent() {
+        ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+
+        SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>();
+        ArraySet<ComponentName> allowed = new ArraySet<>();
+        allowed.add(ComponentName.unflattenFromString("pkg1/cmp1"));
+        approvedComponentsByUser.put(11, allowed);
+        IntArray users = new IntArray();
+        users.add(11);
+
+        SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>();
+
+        service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser,
+                /* isVisibleBackgroundUser= */ true);
+
+        assertEquals(1, componentsToBind.size());
+        assertEquals(1, componentsToBind.get(11).size());
+        assertTrue(componentsToBind.get(11).contains(ComponentName.unflattenFromString(
+                "pkg1/cmp1")));
+        assertThat(service.isComponentEnabledForCurrentProfiles(
+                new ComponentName("pkg1", "cmp1"))).isFalse();
+        assertThat(service.isComponentEnabledForPackage("pkg1")).isFalse();
+    }
+
     @Test
     public void testOnNullBinding() throws Exception {
         Context context = mock(Context.class);
@@ -1973,7 +1944,7 @@
         metaDataAutobindAllow.putBoolean(META_DATA_DEFAULT_AUTOBIND, true);
         metaDatas.put(cn_allowed, metaDataAutobindAllow);
 
-        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
 
         service.addApprovedList(cn_allowed.flattenToString(), 0, true);
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
@@ -2018,7 +1989,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2057,7 +2028,7 @@
         metaDataAutobindDisallow.putBoolean(META_DATA_DEFAULT_AUTOBIND, false);
         metaDatas.put(cn_disallowed, metaDataAutobindDisallow);
 
-        mockServiceInfoWithMetaData(componentNames, service, pm, metaDatas);
+        mockServiceInfoWithMetaData(componentNames, service, metaDatas);
 
         service.addApprovedList(cn_disallowed.flattenToString(), 0, true);
 
@@ -2128,8 +2099,8 @@
     }
 
     private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
-            ManagedServices service, PackageManager packageManager,
-            ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException {
+            ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
+            throws RemoteException {
         when(mIpm.getServiceInfo(any(), anyLong(), anyInt())).thenAnswer(
                 (Answer<ServiceInfo>) invocation -> {
                     ComponentName invocationCn = invocation.getArgument(0);
@@ -2144,39 +2115,6 @@
                     return null;
                 }
         );
-
-        // add components to queryIntentServicesAsUser response
-        final List<String> packages = new ArrayList<>();
-        for (ComponentName cn: componentNames) {
-            packages.add(cn.getPackageName());
-        }
-        ManagedServices.Config config = service.getConfig();
-        when(packageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())).
-                thenAnswer(new Answer<List<ResolveInfo>>() {
-                @Override
-                public List<ResolveInfo> answer(InvocationOnMock invocationOnMock)
-                    throws Throwable {
-                    Object[] args = invocationOnMock.getArguments();
-                    Intent invocationIntent = (Intent) args[0];
-                    if (invocationIntent != null) {
-                        if (invocationIntent.getAction().equals(config.serviceInterface)
-                            && packages.contains(invocationIntent.getPackage())) {
-                            List<ResolveInfo> dummyServices = new ArrayList<>();
-                            for (ComponentName cn: componentNames) {
-                                ResolveInfo resolveInfo = new ResolveInfo();
-                                ServiceInfo serviceInfo = new ServiceInfo();
-                                serviceInfo.packageName = invocationIntent.getPackage();
-                                serviceInfo.name = cn.getClassName();
-                                serviceInfo.permission = service.getConfig().bindPermission;
-                                resolveInfo.serviceInfo = serviceInfo;
-                                dummyServices.add(resolveInfo);
-                            }
-                            return dummyServices;
-                        }
-                    }
-                    return new ArrayList<>();
-                }
-            });
     }
 
     private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 14ad15e..62e5b9a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -2007,6 +2007,25 @@
     }
 
     @Test
+    public void testCanInterruptNonRingtoneInsistentBuzzWithOtherBuzzyNotification() {
+        NotificationRecord r = getInsistentBuzzyNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyVibrateLooped();
+        assertTrue(r.isInterruptive());
+        assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+        Mockito.reset(mVibrator);
+
+        // New buzzy notification stops previous looping vibration
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(interrupter, DEFAULT_SIGNALS);
+        verifyStopVibrate();
+        // And then vibrates itself
+        verifyVibrate(1);
+        assertTrue(interrupter.isInterruptive());
+        assertNotEquals(-1, interrupter.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
         NotificationChannel ringtoneChannel =
             new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
@@ -2458,6 +2477,74 @@
     }
 
     @Test
+    public void testBeepVolume_politeNotif_AvalancheStrategy_mixedNotif() throws Exception {
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+        mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+        TestableFlagResolver flagResolver = new TestableFlagResolver();
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+        flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+        initAttentionHelper(flagResolver);
+
+        // Trigger avalanche trigger intent
+        final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        intent.putExtra("state", false);
+        mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+        // Regular notification: should beep at 0% volume
+        NotificationRecord r = getBeepyNotification();
+        mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.0f);
+        assertEquals(-1, r.getLastAudiblyAlertedMs());
+        Mockito.reset(mRingtonePlayer);
+
+        // Conversation notification
+        mChannel = new NotificationChannel("test2", "test2", IMPORTANCE_DEFAULT);
+        NotificationRecord r2 = getConversationNotificationRecord(mId, false /* insistent */,
+                false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+                true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+                "shortcut");
+
+        // Should beep at 100% volume
+        mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+        verifyBeepVolume(1.0f);
+
+        // Conversation notification on a different channel
+        mChannel = new NotificationChannel("test3", "test3", IMPORTANCE_DEFAULT);
+        NotificationRecord r3 = getConversationNotificationRecord(mId, false /* insistent */,
+                false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+                true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+                "shortcut");
+
+        // Should beep at 50% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+        verifyBeepVolume(0.5f);
+
+        // 2nd update should beep at 0% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        verifyBeepVolume(0.0f);
+
+        // Set important conversation
+        mChannel.setImportantConversation(true);
+        r3 = getConversationNotificationRecord(mId, false /* insistent */,
+            false /* once */, true /* noisy */, false /* buzzy*/, false /* lights */, true,
+            true, false, null, Notification.GROUP_ALERT_ALL, false, mUser, mPkg,
+            "shortcut");
+
+        // important conversation should beep at 100% volume
+        Mockito.reset(mRingtonePlayer);
+        mAttentionHelper.buzzBeepBlinkLocked(r3, DEFAULT_SIGNALS);
+        verifyBeepVolume(1.0f);
+
+        verify(mAccessibilityService, times(5)).sendAccessibilityEvent(any(), anyInt());
+        assertNotEquals(-1, r3.getLastAudiblyAlertedMs());
+    }
+
+    @Test
     public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception {
         mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
         mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
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 5a8de58..6a1140c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -3047,6 +3047,41 @@
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void testMultipleCancelOfLifetimeExtendedSendsOneUpdate() throws Exception {
+        final NotificationRecord notif = generateNotificationRecord(null);
+        notif.getSbn().getNotification().flags =
+                Notification.FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(notif);
+        final StatusBarNotification sbn = notif.getSbn();
+
+        assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+        // Send two cancelations.
+        mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
+                sbn.getUserId());
+        waitForIdle();
+        mBinderService.cancelNotificationWithTag(mPkg, mPkg, sbn.getTag(), sbn.getId(),
+                sbn.getUserId());
+        waitForIdle();
+
+        assertThat(mBinderService.getActiveNotifications(sbn.getPackageName()).length).isEqualTo(1);
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+
+        // Checks that only one post update is sent.
+        verify(mWorkerHandler, times(1))
+                .post(any(NotificationManagerService.PostNotificationRunnable.class));
+        ArgumentCaptor<NotificationRecord> captor =
+                ArgumentCaptor.forClass(NotificationRecord.class);
+        verify(mListeners, times(1)).prepareNotifyPostedLocked(captor.capture(), any(),
+                anyBoolean());
+        assertThat(captor.getValue().getNotification().flags
+                & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
+                FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
     public void testCancelAllClearsLifetimeExtended() throws Exception {
         final NotificationRecord notif = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
@@ -6419,12 +6454,31 @@
     @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
     public void testStats_DirectReplyLifetimeExtendedPostsUpdate() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        // Marks the notification as having already been lifetime extended and canceled.
         r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        r.setCanceledAfterLifetimeExtension(true);
+        r.setPostSilently(true);
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         waitForIdle();
 
+        // At the moment prepareNotifyPostedLocked is called on the listeners,
+        // verify that FLAG_ONLY_ALERT_ONCE and shouldPostSilently are set, regardless of initial
+        // values.
+        doAnswer(
+                invocation -> {
+                    int flags = ((NotificationRecord) invocation.getArgument(0))
+                            .getSbn().getNotification().flags;
+                    assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+                    boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+                            .shouldPostSilently();
+                    assertThat(shouldPostSilently).isTrue();
+                    return null;
+                }
+        ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+        // Checks that the record gets marked as a direct reply having occurred.
         assertThat(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied())
                 .isTrue();
         // Checks that a post update is sent.
@@ -6437,9 +6491,65 @@
         assertThat(captor.getValue().getNotification().flags
                 & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
                 FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+        // FLAG_ONLY_ALERT_ONCE was not present on the original notification, so it's not here.
         assertThat(captor.getValue().getNotification().flags
-                & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+                & FLAG_ONLY_ALERT_ONCE).isEqualTo(0);
         assertThat(captor.getValue().shouldPostSilently()).isTrue();
+        assertThat(captor.getValue().isCanceledAfterLifetimeExtension()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void testStats_DirectReplyLifetimeExtendedPostsUpdate_RestorePostSilently()
+            throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        // Marks the notification as having already been lifetime extended and canceled.
+        r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        r.setPostSilently(false);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+        waitForIdle();
+
+        // Checks that a post update is sent with shouldPostSilently set to true.
+        doAnswer(
+                invocation -> {
+                    boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+                            .shouldPostSilently();
+                    assertThat(shouldPostSilently).isTrue();
+                    return null;
+                }
+        ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+        // Checks that shouldPostSilently is restored to its false state afterward.
+        assertThat(mService.getNotificationRecord(r.getKey()).shouldPostSilently()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
+    public void testStats_DirectReplyLifetimeExtendedPostsUpdate_RestoreOnlyAlertOnceFlag()
+            throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        // Marks the notification as having already been lifetime extended and canceled.
+        r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+        waitForIdle();
+
+        // Checks that a post update is sent with FLAG_ONLY_ALERT_ONCE set to true.
+        doAnswer(
+                invocation -> {
+                    int flags = ((NotificationRecord) invocation.getArgument(0))
+                            .getSbn().getNotification().flags;
+                    assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+                    return null;
+                }
+        ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
+        // Checks that the flag is removed afterward.
+        assertThat(mService.getNotificationRecord(r.getKey()).getSbn().getNotification().flags
+                & FLAG_ONLY_ALERT_ONCE).isEqualTo(0);
     }
 
     @Test
@@ -6476,6 +6586,7 @@
                 anyBoolean());
         assertThat(captor.getValue().getNotification().flags
                 & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(0);
+        assertThat(captor.getValue().isCanceledAfterLifetimeExtension()).isFalse();
         assertThat(captor.getValue()
                 .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE).toString())
                 .isEqualTo("new title");
@@ -9143,11 +9254,13 @@
         final int replyIndex = 2;
         final String reply = "Hello";
         final boolean modifiedBeforeSending = true;
-        final boolean generatedByAssistant = true;
 
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         r.getSbn().getNotification().flags |= FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY;
-        r.setSuggestionsGeneratedByAssistant(generatedByAssistant);
+        r.getSbn().getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+        r.setSuggestionsGeneratedByAssistant(true);
+        r.setCanceledAfterLifetimeExtension(true);
+        r.setPostSilently(true);
         mService.addNotification(r);
 
         mService.mNotificationDelegate.onNotificationSmartReplySent(
@@ -9155,6 +9268,21 @@
                 modifiedBeforeSending);
         waitForIdle();
 
+        // At the moment prepareNotifyPostedLocked is called on the listeners,
+        // verify that FLAG_ONLY_ALERT_ONCE and shouldPostSilently are set, regardless of initial
+        // values.
+        doAnswer(
+                invocation -> {
+                    int flags = ((NotificationRecord) invocation.getArgument(0))
+                            .getSbn().getNotification().flags;
+                    assertThat(flags & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+                    boolean shouldPostSilently = ((NotificationRecord) invocation.getArgument(0))
+                            .shouldPostSilently();
+                    assertThat(shouldPostSilently).isTrue();
+                    return null;
+                }
+        ).when(mListeners).prepareNotifyPostedLocked(any(), any(), anyBoolean());
+
         // Checks that a post update is sent.
         verify(mWorkerHandler, times(1))
                 .post(any(NotificationManagerService.PostNotificationRunnable.class));
@@ -9165,8 +9293,10 @@
         assertThat(captor.getValue().getNotification().flags
                 & FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY).isEqualTo(
                 FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY);
+        // Flag was present before, so it's set afterward
         assertThat(captor.getValue().getNotification().flags
                 & FLAG_ONLY_ALERT_ONCE).isEqualTo(FLAG_ONLY_ALERT_ONCE);
+        // Should post silently was set before, so it's set afterward.
         assertThat(captor.getValue().shouldPostSilently()).isTrue();
     }
 
@@ -14865,7 +14995,6 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_acceptsCorrectToken() throws RemoteException {
         Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
@@ -14884,7 +15013,6 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_acceptsNullToken_andPopulatesIt() throws RemoteException {
         Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
@@ -14901,7 +15029,6 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_directlyThroughRunnable_populatesAllowlistToken() {
         Notification receivedWithoutParceling = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
@@ -14924,7 +15051,6 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_rejectsOtherToken() throws RemoteException {
         Notification sent = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
@@ -14942,7 +15068,6 @@
     }
 
     @Test
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void enqueueNotification_customParcelingWithFakeInnerToken_hasCorrectTokenInIntents()
             throws RemoteException {
         Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -15148,7 +15273,6 @@
 
     @Test
     @SuppressWarnings("unchecked")
-    @EnableFlags(android.app.Flags.FLAG_SECURE_ALLOWLIST_TOKEN)
     public void getActiveNotifications_doesNotLeakAllowlistToken() throws RemoteException {
         Notification sentFromApp = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentIntent(createPendingIntent("content"))
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
index 6a99731..411a610 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java
@@ -89,7 +89,7 @@
 import javax.annotation.Nullable;
 
 @RunWith(AndroidJUnit4.class)
-@EnableFlags(Flags.FLAG_VISIT_PERSON_URI)
+@EnableFlags({Flags.FLAG_VISIT_PERSON_URI, Flags.FLAG_API_RICH_ONGOING})
 public class NotificationVisitUrisTest extends UiServiceTestCase {
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 60c4ac7..f8ff1f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -33,6 +33,8 @@
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.ZenModeConfig.XML_VERSION_MODES_API;
 import static android.service.notification.ZenModeConfig.ZEN_TAG;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
@@ -524,7 +526,7 @@
         rule.zenMode = INTERRUPTION_FILTER;
         rule.modified = true;
         rule.name = NAME;
-        rule.snoozing = true;
+        rule.setConditionOverride(OVERRIDE_DEACTIVATE);
         rule.pkg = OWNER.getPackageName();
         rule.zenPolicy = POLICY;
 
@@ -546,7 +548,7 @@
         ZenModeConfig.ZenRule parceled = new ZenModeConfig.ZenRule(parcel);
 
         assertEquals(rule.pkg, parceled.pkg);
-        assertEquals(rule.snoozing, parceled.snoozing);
+        assertEquals(rule.getConditionOverride(), parceled.getConditionOverride());
         assertEquals(rule.enabler, parceled.enabler);
         assertEquals(rule.component, parceled.component);
         assertEquals(rule.configurationActivity, parceled.configurationActivity);
@@ -625,7 +627,7 @@
         rule.zenMode = INTERRUPTION_FILTER;
         rule.modified = true;
         rule.name = NAME;
-        rule.snoozing = true;
+        rule.setConditionOverride(OVERRIDE_DEACTIVATE);
         rule.pkg = OWNER.getPackageName();
         rule.zenPolicy = POLICY;
         rule.zenDeviceEffects = new ZenDeviceEffects.Builder()
@@ -662,7 +664,7 @@
 
         assertEquals(rule.pkg, fromXml.pkg);
         // always resets on reboot
-        assertFalse(fromXml.snoozing);
+        assertEquals(OVERRIDE_NONE, fromXml.getConditionOverride());
         //should all match original
         assertEquals(rule.component, fromXml.component);
         assertEquals(rule.configurationActivity, fromXml.configurationActivity);
@@ -786,6 +788,19 @@
     }
 
     @Test
+    public void testRuleXml_invalidInterruptionFilter_readsDefault() throws Exception {
+        ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+        rule.zenMode = 1979;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        writeRuleXml(rule, baos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+        assertThat(fromXml.zenMode).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+    }
+
+    @Test
     public void testZenPolicyXml_allUnset() throws Exception {
         ZenPolicy policy = new ZenPolicy.Builder().build();
 
@@ -1115,7 +1130,6 @@
         rule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         rule.modified = true;
         rule.name = "name";
-        rule.snoozing = false;
         rule.pkg = "b";
         config.automaticRules.put("key", rule);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
index 9af0021..91eb2ed 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java
@@ -158,7 +158,10 @@
                             RuleDiff.FIELD_ZEN_DEVICE_EFFECTS,
                             RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS));
         }
-        if (!(Flags.modesApi() && Flags.modesUi())) {
+        if (Flags.modesApi() && Flags.modesUi()) {
+            exemptFields.add(RuleDiff.FIELD_SNOOZING); // Obsolete.
+        } else {
+            exemptFields.add(RuleDiff.FIELD_CONDITION_OVERRIDE);
             exemptFields.add(RuleDiff.FIELD_LEGACY_SUPPRESSED_EFFECTS);
         }
         return exemptFields;
@@ -339,7 +342,7 @@
         rule.zenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         rule.modified = false;
         rule.name = "name";
-        rule.snoozing = true;
+        rule.setConditionOverride(ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE);
         rule.pkg = "a";
         if (android.app.Flags.modesApi()) {
             rule.allowManualInvocation = 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 776a840..baa633f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.AutomaticZenRule.TYPE_BEDTIME;
 import static android.app.AutomaticZenRule.TYPE_IMMERSIVE;
+import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
+import static android.app.AutomaticZenRule.TYPE_UNKNOWN;
 import static android.app.Flags.FLAG_MODES_API;
 import static android.app.Flags.FLAG_MODES_UI;
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED;
@@ -51,6 +53,7 @@
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
 import static android.provider.Settings.Global.ZEN_MODE_OFF;
+import static android.service.notification.Condition.SOURCE_CONTEXT;
 import static android.service.notification.Condition.SOURCE_SCHEDULE;
 import static android.service.notification.Condition.SOURCE_USER_ACTION;
 import static android.service.notification.Condition.STATE_FALSE;
@@ -61,6 +64,9 @@
 import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_APP;
 import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_ACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_DEACTIVATE;
+import static android.service.notification.ZenModeConfig.ZenRule.OVERRIDE_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
@@ -78,6 +84,7 @@
 import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL;
 import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE;
 
+import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Iterables.getOnlyElement;
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -2181,6 +2188,80 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testReadXml_upgradeToModesUi_resetsImplicitRuleIcon() throws Exception {
+        setupZenConfig();
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        ZenRule implicitRuleWithModesUi = expectedImplicitRule("pkg",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, POLICY, null);
+
+        // Add one implicit rule in the pre-MODES_UI configuration.
+        ZenRule implicitRuleBeforeModesUi = implicitRuleWithModesUi.copy();
+        implicitRuleBeforeModesUi.iconResName = "pkg_icon";
+        mZenModeHelper.mConfig.automaticRules.put(implicitRuleBeforeModesUi.id,
+                implicitRuleBeforeModesUi);
+        // Plus one other normal rule.
+        ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
+        anotherRule.id = "other_rule";
+        anotherRule.iconResName = "other_icon";
+        anotherRule.type = TYPE_IMMERSIVE;
+        mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
+
+        // Write with pre-modes-ui = (modes_api) version, then re-read.
+        ByteArrayOutputStream baos = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_API);
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        // Implicit rule was updated.
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleBeforeModesUi.id))
+                .isEqualTo(implicitRuleWithModesUi);
+
+        // The other rule was untouched.
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(anotherRule.id))
+                .isEqualTo(anotherRule);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testReadXml_onModesUi_implicitRulesUntouched() throws Exception {
+        setupZenConfig();
+        mZenModeHelper.mConfig.automaticRules.clear();
+
+        // Add one implicit rule already in its post-modes-UI configuration, also customized with
+        // an icon;
+        ZenRule implicitRuleWithModesUi = expectedImplicitRule("pkg",
+                ZEN_MODE_IMPORTANT_INTERRUPTIONS, POLICY, null);
+        implicitRuleWithModesUi.iconResName = "icon_chosen_by_user";
+        mZenModeHelper.mConfig.automaticRules.put(implicitRuleWithModesUi.id,
+                implicitRuleWithModesUi);
+
+        // Plus one other normal rule.
+        ZenRule anotherRule = newZenRule("other_pkg", Instant.now(), null);
+        anotherRule.id = "other_rule";
+        anotherRule.iconResName = "other_icon";
+        anotherRule.type = TYPE_IMMERSIVE;
+        mZenModeHelper.mConfig.automaticRules.put(anotherRule.id, anotherRule);
+
+        // Write with modes_ui version, then re-read.
+        ByteArrayOutputStream baos = writeXmlAndPurge(ZenModeConfig.XML_VERSION_MODES_UI);
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+        // Both rules were untouched
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(implicitRuleWithModesUi.id))
+                .isEqualTo(implicitRuleWithModesUi);
+        assertThat(mZenModeHelper.mConfig.automaticRules.get(anotherRule.id))
+                .isEqualTo(anotherRule);
+    }
+
+    @Test
     public void testCountdownConditionSubscription() throws Exception {
         ZenModeConfig config = new ZenModeConfig();
         mZenModeHelper.mConfig = config;
@@ -2999,11 +3080,13 @@
             assertWithMessage("Failure for origin " + origin.name())
                     .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
             assertWithMessage("Failure for origin " + origin.name())
-                    .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing)
-                    .isTrue();
+                    .that(mZenModeHelper.mConfig.automaticRules
+                            .get(activeRuleId).getConditionOverride())
+                    .isEqualTo(OVERRIDE_DEACTIVATE);
             assertWithMessage("Failure for origin " + origin.name())
-                    .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing)
-                    .isFalse();
+                    .that(mZenModeHelper.mConfig.automaticRules
+                            .get(inactiveRuleId).getConditionOverride())
+                    .isEqualTo(OVERRIDE_NONE);
         }
     }
 
@@ -3038,16 +3121,20 @@
                 assertWithMessage("Failure for origin " + origin.name()).that(
                         mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
                 assertWithMessage("Failure for origin " + origin.name()).that(
-                        config.automaticRules.get(activeRuleId).snoozing).isFalse();
+                        config.automaticRules.get(activeRuleId).getConditionOverride())
+                        .isEqualTo(OVERRIDE_NONE);
                 assertWithMessage("Failure for origin " + origin.name()).that(
-                        config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+                        config.automaticRules.get(inactiveRuleId).getConditionOverride())
+                        .isEqualTo(OVERRIDE_NONE);
             } else {
                 assertWithMessage("Failure for origin " + origin.name()).that(
                         mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
                 assertWithMessage("Failure for origin " + origin.name()).that(
-                        config.automaticRules.get(activeRuleId).snoozing).isTrue();
+                        config.automaticRules.get(activeRuleId).getConditionOverride())
+                        .isEqualTo(OVERRIDE_DEACTIVATE);
                 assertWithMessage("Failure for origin " + origin.name()).that(
-                        config.automaticRules.get(inactiveRuleId).snoozing).isFalse();
+                        config.automaticRules.get(inactiveRuleId).getConditionOverride())
+                        .isEqualTo(OVERRIDE_NONE);
             }
         }
     }
@@ -4288,7 +4375,7 @@
         rule.zenMode = INTERRUPTION_FILTER_ZR;
         rule.modified = true;
         rule.name = NAME;
-        rule.snoozing = true;
+        rule.setConditionOverride(OVERRIDE_DEACTIVATE);
         rule.pkg = OWNER.getPackageName();
         rule.zenPolicy = POLICY;
 
@@ -4962,7 +5049,7 @@
 
         mZenModeHelper.setAutomaticZenRuleState(createdId,
                 new Condition(zenRule.getConditionId(), "", STATE_FALSE),
-                ORIGIN_APP, SYSTEM_UID);
+                ORIGIN_APP, CUSTOM_PKG_UID);
 
         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
         if (CompatChanges.isChangeEnabled(ZenModeHelper.SEND_ACTIVATION_AZR_STATUSES)) {
@@ -5000,7 +5087,8 @@
         mZenModeHelper.updateAutomaticZenRule(createdId, zenRule, ZenModeConfig.ORIGIN_SYSTEM,
                 "", SYSTEM_UID);
 
-        assertEquals(false, mZenModeHelper.mConfig.automaticRules.get(createdId).snoozing);
+        assertEquals(OVERRIDE_NONE,
+                mZenModeHelper.mConfig.automaticRules.get(createdId).getConditionOverride());
     }
 
     @Test
@@ -5526,6 +5614,49 @@
     }
 
     @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void removeAndAddAutomaticZenRule_ifChangingComponent_isAllowedAndDoesNotRestore() {
+        // Start with a rule.
+        mZenModeHelper.mConfig.automaticRules.clear();
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Test", CONDITION_ID)
+                .setOwner(new ComponentName("first", "owner"))
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALL)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), rule,
+                ORIGIN_APP, "add it", CUSTOM_PKG_UID);
+
+        // User customizes it.
+        AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .build();
+        mZenModeHelper.updateAutomaticZenRule(ruleId, userUpdate, ORIGIN_USER_IN_SYSTEMUI,
+                "userUpdate", SYSTEM_UID);
+
+        // App deletes it. It's preserved for a possible restoration.
+        mZenModeHelper.removeAutomaticZenRule(ruleId, ORIGIN_APP, "delete it",
+                CUSTOM_PKG_UID);
+        assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(0);
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(1);
+
+        // App adds it again, but this time with a different owner!
+        AutomaticZenRule readdingWithDifferentOwner = new AutomaticZenRule.Builder(rule)
+                .setOwner(new ComponentName("second", "owner"))
+                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
+                .build();
+        String newRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                readdingWithDifferentOwner, ORIGIN_APP, "add it again", CUSTOM_PKG_UID);
+
+        // Verify that the rule was NOT restored:
+        assertThat(newRuleId).isNotEqualTo(ruleId);
+        AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(newRuleId);
+        assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
+        assertThat(finalRule.getOwner()).isEqualTo(new ComponentName("second", "owner"));
+
+        // Also, we discarded the "deleted rule" since we found it but decided not to use it.
+        assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0);
+    }
+
+    @Test
     @EnableFlags(FLAG_MODES_API)
     public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() {
         mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS,
@@ -5713,7 +5844,7 @@
         assertThat(storedRule.isAutomaticActive()).isFalse();
         assertThat(storedRule.isTrueOrUnknown()).isFalse();
         assertThat(storedRule.condition).isNull();
-        assertThat(storedRule.snoozing).isFalse();
+        assertThat(storedRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
     }
 
@@ -6039,15 +6170,18 @@
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
                 ZEN_MODE_IMPORTANT_INTERRUPTIONS);
         assertThat(mZenModeHelper.mConfig.automaticRules).hasSize(1);
-        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+                .isEqualTo(OVERRIDE_NONE);
 
         mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, ORIGIN_APP, "test", "test", 0);
-        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isTrue();
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+                .isEqualTo(OVERRIDE_DEACTIVATE);
 
         mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID,
                 ZEN_MODE_ALARMS);
 
-        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).snoozing).isFalse();
+        assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).getConditionOverride())
+                .isEqualTo(OVERRIDE_NONE);
         assertThat(mZenModeHelper.mConfig.automaticRules.valueAt(0).condition.state)
                 .isEqualTo(STATE_TRUE);
     }
@@ -6451,6 +6585,344 @@
                 ORIGIN_UNKNOWN);
     }
 
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualActivation_appliesOverride() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+
+        ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+        assertThat(zenRule.condition).isNull();
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualActivationAndThenDeactivation_removesOverride() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+        Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+                SOURCE_CONTEXT);
+        ZenRule zenRule;
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+        assertThat(zenRule.condition).isNull();
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRule.condition).isNull();
+
+        // Bonus check: app has resumed control over the rule and can now turn it on.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn,  ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRule.condition).isEqualTo(autoOn);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualDeactivationAndThenReactivation_removesOverride() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+        Condition autoOn = new Condition(rule.getConditionId(), "auto-on", STATE_TRUE,
+                SOURCE_CONTEXT);
+        Condition autoOff = new Condition(rule.getConditionId(), "auto-off", STATE_FALSE,
+                SOURCE_CONTEXT);
+        ZenRule zenRule;
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOn,  ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRule.condition).isEqualTo(autoOn);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+        assertThat(zenRule.condition).isEqualTo(autoOn);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRule.condition).isEqualTo(autoOn);
+
+        // Bonus check: app has resumed control over the rule and can now turn it off.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId, autoOff,  ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRule.condition).isEqualTo(autoOff);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualDeactivation_appliesOverride() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        ZenRule zenRuleOn = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRuleOn.isAutomaticActive()).isTrue();
+        assertThat(zenRuleOn.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        assertThat(zenRuleOn.condition).isNotNull();
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        ZenRule zenRuleOff = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRuleOff.isAutomaticActive()).isFalse();
+        assertThat(zenRuleOff.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+        assertThat(zenRuleOff.condition).isNotNull();
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_ifManualActive_appCannotDeactivateBeforeActivating() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+        ZenRule zenRule;
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_ifManualInactive_appCannotReactivateBeforeDeactivating() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+        ZenRule zenRule;
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off", STATE_FALSE, SOURCE_USER_ACTION),
+                ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-off", STATE_FALSE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isFalse();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on", STATE_TRUE, SOURCE_CONTEXT),
+                ORIGIN_APP, CUSTOM_PKG_UID);
+        zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
+        assertThat(zenRule.isAutomaticActive()).isTrue();
+        assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_withActivationOverride_userActionFromAppCanDeactivate() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        // User manually turns on rule from SysUI / Settings...
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on-from-sysui", STATE_TRUE,
+                        SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isTrue();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
+
+        // ... and they can turn it off manually from inside the app.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off-from-app", STATE_FALSE,
+                        SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isFalse();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_withDeactivationOverride_userActionFromAppCanActivate() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        // Rule is activated due to its schedule.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-on-from-app", STATE_TRUE,
+                        SOURCE_SCHEDULE), ORIGIN_APP, CUSTOM_PKG_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isTrue();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+        // User manually turns off rule from SysUI / Settings...
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-off-from-sysui", STATE_FALSE,
+                        SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isFalse();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
+
+        // ... and they can turn it on manually from inside the app.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on-from-app", STATE_TRUE,
+                        SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isTrue();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void setAutomaticZenRuleState_manualActionFromApp_isNotOverride() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Rule", Uri.parse("cond"))
+                .setPackage(mPkg)
+                .build();
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mPkg, rule, ORIGIN_APP, "adding",
+                CUSTOM_PKG_UID);
+
+        // Rule is manually activated by the user in the app.
+        // This turns the rule on, but is NOT an override...
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "manual-on-from-app", STATE_TRUE,
+                        SOURCE_USER_ACTION), ORIGIN_USER_IN_APP, CUSTOM_PKG_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isTrue();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+
+        // ... so the app can turn it off when its schedule is over.
+        mZenModeHelper.setAutomaticZenRuleState(ruleId,
+                new Condition(rule.getConditionId(), "auto-off-from-app", STATE_FALSE,
+                        SOURCE_SCHEDULE), ORIGIN_APP, CUSTOM_PKG_UID);
+        assertThat(getZenRule(ruleId).isAutomaticActive()).isFalse();
+        assertThat(getZenRule(ruleId).getConditionOverride()).isEqualTo(OVERRIDE_NONE);
+    }
+
+    private ZenRule getZenRule(String ruleId) {
+        return checkNotNull(mZenModeHelper.mConfig.automaticRules.get(ruleId),
+                "Didn't find rule with id %s", ruleId);
+    }
+
+    @Test
+    @DisableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testDefaultConfig_preModesApi_rulesAreBare() {
+        // Create a new user, which should get a copy of the default policy.
+        mZenModeHelper.onUserSwitched(101);
+
+        ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+        assertThat(eventsRule).isNotNull();
+        assertThat(eventsRule.zenPolicy).isNull();
+        assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+        assertThat(eventsRule.triggerDescription).isNull();
+    }
+
+    @Test
+    @EnableFlags(FLAG_MODES_API)
+    @DisableFlags(FLAG_MODES_UI)
+    public void testDefaultConfig_modesApi_rulesHaveFullPolicy() {
+        // Create a new user, which should get a copy of the default policy.
+        mZenModeHelper.onUserSwitched(201);
+
+        ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+        assertThat(eventsRule).isNotNull();
+        assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+        assertThat(eventsRule.type).isEqualTo(TYPE_UNKNOWN);
+        assertThat(eventsRule.triggerDescription).isNull();
+    }
+
+    @Test
+    @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI})
+    public void testDefaultConfig_modesUi_rulesHaveFullPolicy() {
+        // Create a new user, which should get a copy of the default policy.
+        mZenModeHelper.onUserSwitched(301);
+
+        ZenRule eventsRule = mZenModeHelper.mConfig.automaticRules.get(
+                ZenModeConfig.EVENTS_DEFAULT_RULE_ID);
+
+        assertThat(eventsRule).isNotNull();
+        assertThat(eventsRule.zenPolicy).isEqualTo(mZenModeHelper.getDefaultZenPolicy());
+        assertThat(eventsRule.type).isEqualTo(TYPE_SCHEDULE_CALENDAR);
+        assertThat(eventsRule.triggerDescription).isNotEmpty();
+    }
+
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
@@ -6502,7 +6974,9 @@
         rule.zenPolicy = policy;
         rule.pkg = ownerPkg;
         rule.name = CUSTOM_APP_LABEL;
-        rule.iconResName = ICON_RES_NAME;
+        if (!Flags.modesUi()) {
+            rule.iconResName = ICON_RES_NAME;
+        }
         rule.triggerDescription = mContext.getString(R.string.zen_mode_implicit_trigger_description,
                 CUSTOM_APP_LABEL);
         rule.type = AutomaticZenRule.TYPE_OTHER;
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 43ad44f..2549ff5 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -32,11 +32,11 @@
         "frameworks-base-testutils",
         "frameworks-services-vibrator-testutils",
         "junit",
-        "junit-params",
         "mockito-target-inline-minus-junit4",
         "platform-test-annotations",
         "service-permission.stubs.system_server",
         "services.core",
+        "TestParameterInjector",
     ],
     jni_libs: ["libdexmakerjvmtiagent"],
     platform_apis: true,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
index e0d05df..2d312d2 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -18,32 +18,36 @@
 
 import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
 import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.EFFECT_TICK;
+import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
+import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED;
+import static android.os.vibrator.Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES;
 
 import static com.android.internal.R.xml.haptic_feedback_customization;
-import static com.android.server.vibrator.HapticFeedbackCustomization.CustomizationParserException;
+import static com.android.internal.R.xml.haptic_feedback_customization_source_rotary_encoder;
+import static com.android.internal.R.xml.haptic_feedback_customization_source_touchscreen;
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.VibrationEffect;
 import android.os.VibratorInfo;
-import android.os.vibrator.Flags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.AtomicFile;
-import android.util.SparseArray;
+import android.view.InputDevice;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.R;
-import com.android.internal.annotations.Keep;
 
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
+import com.google.testing.junit.testparameterinjector.TestParameter;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -56,7 +60,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 
-@RunWith(JUnitParamsRunner.class)
+@RunWith(TestParameterInjector.class)
 public class HapticFeedbackCustomizationTest {
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
@@ -69,166 +73,173 @@
     private static final VibrationEffect COMPOSITION_VIBRATION =
             VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
 
-    private static final String PREDEFINED_VIBRATION_XML =
+    private static final String PREDEFINED_VIBRATION_CLICK_XML =
             "<vibration-effect><predefined-effect name=\"click\"/></vibration-effect>";
-    private static final VibrationEffect PREDEFINED_VIBRATION =
+    private static final VibrationEffect PREDEFINED_VIBRATION_CLICK =
             VibrationEffect.createPredefined(EFFECT_CLICK);
 
+    private static final String PREDEFINED_VIBRATION_TICK_XML =
+            "<vibration-effect><predefined-effect name=\"tick\"/></vibration-effect>";
+    private static final VibrationEffect PREDEFINED_VIBRATION_TICK =
+            VibrationEffect.createPredefined(EFFECT_TICK);
+
     private static final String WAVEFORM_VIBRATION_XML = "<vibration-effect>"
             + "<waveform-effect>"
             + "<waveform-entry durationMs=\"123\" amplitude=\"254\"/>"
             + "</waveform-effect>"
             + "</vibration-effect>";
-    private static final VibrationEffect WAVEFORM_VIBARTION =
+    private static final VibrationEffect WAVEFORM_VIBRATION =
             VibrationEffect.createWaveform(new long[] {123}, new int[] {254}, -1);
 
     @Mock private Resources mResourcesMock;
     @Mock private VibratorInfo mVibratorInfoMock;
 
-    @Keep
-    private static Object[][] hapticFeedbackCustomizationTestArguments() {
-        // (boolean hasConfigFile, boolean hasRes).
-        return new Object[][] {{true, true}, {true, false}, {false, true}};
+    private enum CustomizationSource {
+        DEVICE_CONFIG_FILE,
+        DEVICE_RESOURCE,
+        DEVICE_RESOURCE_INPUT_ROTARY,
+        DEVICE_RESOURCE_INPUT_TOUCHSCREEN
     }
 
     @Before
     public void setUp() {
+        clearFileAndResourceSetup();
         when(mVibratorInfoMock.areVibrationFeaturesSupported(any())).thenReturn(true);
-        mSetFlagsRule.enableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
-        mSetFlagsRule.disableFlags(
-                Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
+        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_noCustomization_success(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xml = "<haptic-feedback-constants></haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-        setupParseCustomizations(xml, hasConfigFile, hasRes);
-
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_featureFlagDisabled_returnsNull(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        mSetFlagsRule.disableFlags(Flags.FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
+    public void testParseCustomizations_featureFlagDisabled_customizationNotLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        mSetFlagsRule.disableFlags(FLAG_HAPTIC_FEEDBACK_VIBRATION_OEM_CUSTOMIZATION_ENABLED);
         // Valid customization XML.
         String xml = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + COMPOSITION_VIBRATION_XML
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
 
-        setupParseCustomizations(xml, hasConfigFile, hasRes);
-        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource, customization))
                 .isNull();
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
     public void testParseCustomizations_oneVibrationCustomization_success(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xml = "<haptic-feedback-constants>"
-                + "<constant id=\"10\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-        expectedMapping.put(10, COMPOSITION_VIBRATION);
-
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_oneVibrationSelectCustomization_success(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xml = "<haptic-feedback-constants>"
-                + "<constant id=\"10\">"
-                + "<vibration-select>"
-                + COMPOSITION_VIBRATION_XML
-                + "</vibration-select>"
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-        expectedMapping.put(10, COMPOSITION_VIBRATION);
-
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_multipleCustomizations_success(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xml = "<haptic-feedback-constants>"
-                + "<constant id=\"1\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "<constant id=\"12\">"
-                + "<vibration-select>"
-                + PREDEFINED_VIBRATION_XML
-                + WAVEFORM_VIBRATION_XML
-                + "</vibration-select>"
-                + "</constant>"
-                + "<constant id=\"150\">"
-                + PREDEFINED_VIBRATION_XML
-                + "</constant>"
-                + "<constant id=\"10\">"
-                + "<vibration-select>"
-                + WAVEFORM_VIBRATION_XML
-                + COMPOSITION_VIBRATION_XML
-                + "</vibration-select>"
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-        expectedMapping.put(1, COMPOSITION_VIBRATION);
-        expectedMapping.put(12, PREDEFINED_VIBRATION);
-        expectedMapping.put(150, PREDEFINED_VIBRATION);
-        expectedMapping.put(10, WAVEFORM_VIBARTION);
-
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_multipleCustomizations_noSupportedVibration_success(
-            boolean hasConfigFile, boolean hasRes)
-                throws Exception {
-        makeUnsupported(COMPOSITION_VIBRATION, PREDEFINED_VIBRATION, WAVEFORM_VIBARTION);
-        String xml = "<haptic-feedback-constants>"
-                + "<constant id=\"1\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "<constant id=\"12\">"
-                + "<vibration-select>"
-                + PREDEFINED_VIBRATION_XML
-                + WAVEFORM_VIBRATION_XML
-                + "</vibration-select>"
-                + "</constant>"
-                + "<constant id=\"150\">"
-                + PREDEFINED_VIBRATION_XML
-                + "</constant>"
-                + "<constant id=\"10\">"
-                + "<vibration-select>"
-                + WAVEFORM_VIBRATION_XML
-                + COMPOSITION_VIBRATION_XML
-                + "</vibration-select>"
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_multipleCustomizations_someUnsupportedVibration_success(
-            boolean hasConfigFile, boolean hasRes)
+            @TestParameter CustomizationSource customizationSource)
             throws Exception {
-        makeSupported(PREDEFINED_VIBRATION, WAVEFORM_VIBARTION);
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
+
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource, customization))
+                .isEqualTo(COMPOSITION_VIBRATION);
+    }
+
+    @Test
+    public void testParseCustomizations_oneVibrationSelectCustomization_success(
+            @TestParameter CustomizationSource customizationSource)
+            throws Exception {
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<vibration-select>"
+                + COMPOSITION_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
+
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource, customization))
+                .isEqualTo(COMPOSITION_VIBRATION);
+    }
+
+    @Test
+    public void testParseCustomizations_multipleCustomizations_success(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"1\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"12\">"
+                + "<vibration-select>"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + WAVEFORM_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "<constant id=\"150\">"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + "</constant>"
+                + "<constant id=\"10\">"
+                + "<vibration-select>"
+                + WAVEFORM_VIBRATION_XML
+                + COMPOSITION_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
+
+        assertThat(getEffectForSource(/* effectId= */ 1, customizationSource,
+                customization))
+                .isEqualTo(COMPOSITION_VIBRATION);
+        assertThat(getEffectForSource(/* effectId= */ 12, customizationSource,
+                customization))
+                .isEqualTo(PREDEFINED_VIBRATION_CLICK);
+        assertThat(getEffectForSource(/* effectId= */ 150, customizationSource,
+                customization))
+                .isEqualTo(PREDEFINED_VIBRATION_CLICK);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customization))
+                .isEqualTo(WAVEFORM_VIBRATION);
+    }
+
+    @Test
+    public void testParseCustomizations_multipleCustomizations_noSupportedVibration_success(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        makeUnsupported(COMPOSITION_VIBRATION, PREDEFINED_VIBRATION_CLICK, WAVEFORM_VIBRATION);
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"1\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"12\">"
+                + "<vibration-select>"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + WAVEFORM_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "<constant id=\"150\">"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + "</constant>"
+                + "<constant id=\"10\">"
+                + "<vibration-select>"
+                + WAVEFORM_VIBRATION_XML
+                + COMPOSITION_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
+
+        assertThat(getEffectForSource(/* effectId= */ 1, customizationSource,
+                customization)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 12, customizationSource,
+                customization)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 150, customizationSource,
+                customization)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customization)).isNull();
+    }
+
+    @Test
+    public void testParseCustomizations_multipleCustomizations_someUnsupportedVibration_success(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        makeSupported(PREDEFINED_VIBRATION_CLICK, WAVEFORM_VIBRATION);
         makeUnsupported(COMPOSITION_VIBRATION);
         String xml = "<haptic-feedback-constants>"
                 + "<constant id=\"1\">" // No supported customization.
@@ -236,68 +247,89 @@
                 + "</constant>"
                 + "<constant id=\"12\">" // PREDEFINED_VIBRATION is the first/only supported.
                 + "<vibration-select>"
-                + PREDEFINED_VIBRATION_XML
+                + PREDEFINED_VIBRATION_CLICK_XML
                 + COMPOSITION_VIBRATION_XML
                 + "</vibration-select>"
                 + "</constant>"
-                + "<constant id=\"14\">" // WAVEFORM_VIBARTION is the first/only supported.
+                + "<constant id=\"14\">" // WAVEFORM_VIBRATION is the first/only supported.
                 + "<vibration-select>"
                 + COMPOSITION_VIBRATION_XML
                 + WAVEFORM_VIBRATION_XML
                 + "</vibration-select>"
                 + "</constant>"
                 + "<constant id=\"150\">" // PREDEFINED_VIBRATION is the first/only supported.
-                + PREDEFINED_VIBRATION_XML
+                + PREDEFINED_VIBRATION_CLICK_XML
                 + "</constant>"
                 + "<constant id=\"10\">" // PREDEFINED_VIBRATION is the first supported.
                 + "<vibration-select>"
-                + PREDEFINED_VIBRATION_XML
+                + PREDEFINED_VIBRATION_CLICK_XML
                 + WAVEFORM_VIBRATION_XML
                 + "</vibration-select>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
-        expectedMapping.put(12, PREDEFINED_VIBRATION);
-        expectedMapping.put(14, WAVEFORM_VIBARTION);
-        expectedMapping.put(150, PREDEFINED_VIBRATION);
-        expectedMapping.put(10, PREDEFINED_VIBRATION);
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
 
-        assertParseCustomizationsSucceeds(xml, expectedMapping, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 1, customizationSource,
+                customization)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 12, customizationSource,
+                customization)).isEqualTo(PREDEFINED_VIBRATION_CLICK);
+        assertThat(getEffectForSource(/* effectId= */ 14, customizationSource,
+                customization)).isEqualTo(WAVEFORM_VIBRATION);
+        assertThat(getEffectForSource(/* effectId= */ 150, customizationSource,
+                customization)).isEqualTo(PREDEFINED_VIBRATION_CLICK);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customization)).isEqualTo(PREDEFINED_VIBRATION_CLICK);
     }
 
     @Test
-    public void testParseCustomizations_noCustomizationFile_returnsNull() throws Exception {
-        setCustomizationFilePath("");
+    public void testParseCustomizations_malformedXml_notLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        // No end "<constant>" tag
+        String xmlNoEndConstantTag = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customizationNoEndConstantTag = createCustomizationForSource(
+                xmlNoEndConstantTag, customizationSource);
+        // No start "<haptic-feedback-constants>" tag
+        String xmlNoStartCustomizationTag = "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationNoStartCustomizationTag =
+                createCustomizationForSource(xmlNoStartCustomizationTag, customizationSource);
+        // No end "<haptic-feedback-constants>" tag
+        String xmlNoEndCustomizationTag = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>";
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationNoEndCustomizationTag =
+                createCustomizationForSource(xmlNoEndCustomizationTag, customizationSource);
+        // No start "<constant>" tag
+        String xmlNoStartConstantTag = "<haptic-feedback-constants>"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationNoStartConstantTag = createCustomizationForSource(
+                xmlNoStartConstantTag, customizationSource);
 
-        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
-                .isNull();
-
-        setCustomizationFilePath(null);
-
-        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
-                .isNull();
-
-        setCustomizationFilePath("non_existent_file.xml");
-
-        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
-                .isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationNoEndConstantTag)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationNoStartCustomizationTag)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationNoEndCustomizationTag)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationNoStartConstantTag)).isNull();
     }
 
     @Test
-    public void testParseCustomizations_noCustomizationResource_returnsNull() throws Exception {
-        mSetFlagsRule.enableFlags(
-                Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
-        doThrow(new Resources.NotFoundException())
-                .when(mResourcesMock).getXml(haptic_feedback_customization);
-
-        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock))
-                .isNull();
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_disallowedVibrationForHapticFeedback_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
+    public void testParseCustomizations_disallowedVibrationForHapticFeedback_notLoaded(
+                @TestParameter CustomizationSource customizationSource) throws Exception {
         // The XML content is good, but the serialized vibration is not supported for haptic
         // feedback usage (i.e. repeating vibration).
         String xml = "<haptic-feedback-constants>"
@@ -311,185 +343,245 @@
                 + "</vibration-effect>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
 
-        assertParseCustomizationsFails(xml, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource, customization))
+                .isNull();
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_emptyXml_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        assertParseCustomizationsFails("", hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_noVibrationXml_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
+    public void testParseCustomizations_xmlNoVibration_notLoaded(
+                @TestParameter CustomizationSource customizationSource) throws Exception {
         String xml = "<haptic-feedback-constants>"
                 + "<constant id=\"1\">"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xml,
+                customizationSource);
 
-        assertParseCustomizationsFails(xml, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 1, customizationSource, customization))
+                .isNull();
     }
 
+
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_badEffectId_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        // Negative id
+    public void testParseCustomizations_badEffectId_notLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
         String xmlNegativeId = "<haptic-feedback-constants>"
                 + "<constant id=\"-10\">"
                 + COMPOSITION_VIBRATION_XML
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        // Non-numeral id
-        String xmlNonNumericalId = "<haptic-feedback-constants>"
-                + "<constant id=\"xyz\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(
+                xmlNegativeId, customizationSource);
 
-        assertParseCustomizationsFails(xmlNegativeId, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlNonNumericalId, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ -10, customizationSource, customization))
+                .isNull();
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_malformedXml_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        // No start "<constant>" tag
-        String xmlNoStartConstantTag = "<haptic-feedback-constants>"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        // No end "<constant>" tag
-        String xmlNoEndConstantTag = "<haptic-feedback-constants>"
-                + "<constant id=\"10\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</haptic-feedback-constants>";
-        // No start "<haptic-feedback-constants>" tag
-        String xmlNoStartCustomizationTag = "<constant id=\"10\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>"
-                + "</haptic-feedback-constants>";
-        // No end "<haptic-feedback-constants>" tag
-        String xmlNoEndCustomizationTag = "<haptic-feedback-constants>"
-                + "<constant id=\"10\">"
-                + COMPOSITION_VIBRATION_XML
-                + "</constant>";
-
-        assertParseCustomizationsFails(xmlNoStartConstantTag, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlNoEndConstantTag, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlNoStartCustomizationTag, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlNoEndCustomizationTag, hasConfigFile, hasRes);
-    }
-
-    @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_badVibrationXml_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xmlBad1 = "<haptic-feedback-constants>"
+    public void testParseCustomizations_badVibrationXml_notLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        // Case#1 - bad opening tag <bad-vibration-effect>
+        String xmlBadTag = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + "<bad-vibration-effect></bad-vibration-effect>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        String xmlBad2 = "<haptic-feedback-constants>"
+        HapticFeedbackCustomization customizationBadTag = createCustomizationForSource(
+                xmlBadTag, customizationSource);
+        // Case#2 - bad attribute "name" for tag <predefined-effect>
+        String xmlBadEffectName = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        String xmlBad3 = "<haptic-feedback-constants>"
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationBadEffectName = createCustomizationForSource(
+                xmlBadEffectName, customizationSource);
+        // Case#3 - miss "</vibration-select>"
+        String xmlBadEffectNameAndMissingCloseTag = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + "<vibration-select>"
                 + "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        String xmlBad4 = "<haptic-feedback-constants>"
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationBadEffectNameAndMissingCloseTag =
+                createCustomizationForSource(xmlBadEffectNameAndMissingCloseTag,
+                        customizationSource);
+        // Case#4 - miss "<vibration-select>"
+        String xmlBadEffectNameAndMissingOpenTag = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + "<vibration-effect><predefined-effect name=\"bad-effect\"/></vibration-effect>"
                 + "</vibration-select>"
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationBadEffectNameAndMissingOpenTag =
+                createCustomizationForSource(xmlBadEffectNameAndMissingOpenTag,
+                        customizationSource);
 
-        assertParseCustomizationsFails(xmlBad1, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlBad2, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlBad3, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlBad4, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationBadTag)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationBadEffectName)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationBadEffectNameAndMissingCloseTag)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationBadEffectNameAndMissingOpenTag)).isNull();
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_badConstantAttribute_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
-        String xmlBadConstantAttribute1 = "<haptic-feedback-constants>"
+    public void testParseCustomizations_badConstantAttribute_notLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
+        // Case#1 - bad attribute id for tag <constant>
+        String xmlBadConstantIdAttribute = "<haptic-feedback-constants>"
                 + "<constant iddddd=\"10\">"
                 + COMPOSITION_VIBRATION_XML
                 + "</constant>"
                 + "</haptic-feedback-constants>";
-        String xmlBadConstantAttribute2 = "<haptic-feedback-constants>"
+        HapticFeedbackCustomization customizationBadConstantIdAttribute =
+                createCustomizationForSource(xmlBadConstantIdAttribute, customizationSource);
+        // Case#2 - unexpected attribute "unwanted" for tag <constant>
+        String xmlUnwantedConstantAttribute = "<haptic-feedback-constants>"
                 + "<constant id=\"10\" unwanted-attr=\"1\">"
                 + COMPOSITION_VIBRATION_XML
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        clearFileAndResourceSetup();
+        HapticFeedbackCustomization customizationUnwantedConstantAttribute =
+                createCustomizationForSource(xmlUnwantedConstantAttribute, customizationSource);
 
-        assertParseCustomizationsFails(xmlBadConstantAttribute1, hasConfigFile, hasRes);
-        assertParseCustomizationsFails(xmlBadConstantAttribute2, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationBadConstantIdAttribute)).isNull();
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource,
+                customizationUnwantedConstantAttribute)).isNull();
     }
 
     @Test
-    @Parameters(method = "hapticFeedbackCustomizationTestArguments")
-    public void testParseCustomizations_duplicateEffects_throwsException(
-            boolean hasConfigFile, boolean hasRes) throws Exception {
+    public void testParseCustomizations_duplicateEffects_notLoaded(
+            @TestParameter CustomizationSource customizationSource) throws Exception {
         String xmlDuplicateEffect = "<haptic-feedback-constants>"
                 + "<constant id=\"10\">"
                 + COMPOSITION_VIBRATION_XML
                 + "</constant>"
                 + "<constant id=\"10\">"
-                + PREDEFINED_VIBRATION_XML
+                + PREDEFINED_VIBRATION_CLICK_XML
                 + "</constant>"
                 + "<constant id=\"11\">"
-                + PREDEFINED_VIBRATION_XML
+                + PREDEFINED_VIBRATION_CLICK_XML
                 + "</constant>"
                 + "</haptic-feedback-constants>";
+        HapticFeedbackCustomization customization = createCustomizationForSource(xmlDuplicateEffect,
+                customizationSource);
 
-        assertParseCustomizationsFails(xmlDuplicateEffect, hasConfigFile, hasRes);
+        assertThat(getEffectForSource(/* effectId= */ 10, customizationSource, customization))
+                .isNull();
+        assertThat(getEffectForSource(/* effectId= */ 11, customizationSource, customization))
+                .isNull();
     }
 
-    private void assertParseCustomizationsSucceeds(String xml,
-            SparseArray<VibrationEffect> expectedCustomizations, boolean hasConfigFile,
-            boolean hasRes) throws Exception {
-        setupParseCustomizations(xml, hasConfigFile, hasRes);
-        assertThat(expectedCustomizations.contentEquals(
-                HapticFeedbackCustomization.loadVibrations(mResourcesMock, mVibratorInfoMock)))
-                .isTrue();
-    }
-
-    private void assertParseCustomizationsFails(String xml, boolean hasConfigFile, boolean hasRes)
+    @Test
+    public void testParseCustomizations_withDifferentCustomizations_loadsCorrectOne()
             throws Exception {
-        setupParseCustomizations(xml, hasConfigFile, hasRes);
-        assertThrows("Expected haptic feedback customization to fail",
-                CustomizationParserException.class,
-                () ->  HapticFeedbackCustomization.loadVibrations(
-                        mResourcesMock, mVibratorInfoMock));
+        String xmlBaseCustomization = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"14\">"
+                + "<vibration-select>"
+                + WAVEFORM_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        String xmlRotaryInputCustomization = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<vibration-select>"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + COMPOSITION_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        String xmlTouchScreenInputCustomization = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + PREDEFINED_VIBRATION_TICK_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        setupCustomizations(xmlBaseCustomization, CustomizationSource.DEVICE_RESOURCE);
+        setupCustomizations(xmlRotaryInputCustomization,
+                CustomizationSource.DEVICE_RESOURCE_INPUT_ROTARY);
+        HapticFeedbackCustomization customization = createCustomizationForSource(
+                xmlTouchScreenInputCustomization,
+                CustomizationSource.DEVICE_RESOURCE_INPUT_TOUCHSCREEN);
+
+        // Matching customizations.
+        assertThat(customization.getEffect(/* effectId= */ 10)).isEqualTo(COMPOSITION_VIBRATION);
+        assertThat(customization.getEffect(/* effectId= */ 14)).isEqualTo(WAVEFORM_VIBRATION);
+        assertThat(customization.getEffect(/* effectId= */ 10,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(PREDEFINED_VIBRATION_CLICK);
+        assertThat(customization.getEffect(/* effectId= */ 10,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(PREDEFINED_VIBRATION_TICK);
+        // Missing from input source customization xml. Fallback to base.
+        assertThat(customization.getEffect(/* effectId= */ 14,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(WAVEFORM_VIBRATION);
+        assertThat(customization.getEffect(/* effectId= */ 14,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(WAVEFORM_VIBRATION);
     }
 
-    private void setupParseCustomizations(String xml, boolean hasConfigFile, boolean hasRes)
+    @Test
+    public void testParseCustomizations_customizationsFromConfigFileAndRes_preferConfigFile()
             throws Exception {
-        clearFileAndResourceSetup();
-        if (hasConfigFile) {
-            setupCustomizationFile(xml);
-        }
-        if (hasRes) {
-            setupCustomizationResource(xml);
+        String xmlConfigFileCustomization = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        String xmlResourceCustomization = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<vibration-select>"
+                + PREDEFINED_VIBRATION_CLICK_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "<constant id=\"14\">"
+                + "<vibration-select>"
+                + WAVEFORM_VIBRATION_XML
+                + "</vibration-select>"
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        setupCustomizations(xmlConfigFileCustomization, CustomizationSource.DEVICE_CONFIG_FILE);
+        HapticFeedbackCustomization customization = createCustomizationForSource(
+                xmlResourceCustomization, CustomizationSource.DEVICE_RESOURCE);
+
+        // When config file and resource customizations are both available. Load the config file
+        // Customization.
+        assertThat(customization.getEffect(/* effectId= */ 10)).isEqualTo(COMPOSITION_VIBRATION);
+        assertThat(customization.getEffect(/* effectId= */ 14)).isNull();
+    }
+
+    private HapticFeedbackCustomization createCustomizationForSource(String xml,
+            CustomizationSource customizationSource) throws Exception {
+        setupCustomizations(xml, customizationSource);
+        return new HapticFeedbackCustomization(mResourcesMock, mVibratorInfoMock);
+    }
+
+    private void setupCustomizations(String xml, CustomizationSource customizationSource)
+            throws Exception {
+        switch (customizationSource) {
+            case DEVICE_CONFIG_FILE -> setupCustomizationFile(xml);
+            case DEVICE_RESOURCE -> setupCustomizationResource(xml, haptic_feedback_customization);
+            case DEVICE_RESOURCE_INPUT_ROTARY -> setupCustomizationResource(xml,
+                    haptic_feedback_customization_source_rotary_encoder);
+            case DEVICE_RESOURCE_INPUT_TOUCHSCREEN -> setupCustomizationResource(xml,
+                    haptic_feedback_customization_source_touchscreen);
         }
     }
 
-    private void clearFileAndResourceSetup() {
-        when(mResourcesMock.getString(R.string.config_hapticFeedbackCustomizationFile))
-                .thenReturn(null);
-        when(mResourcesMock.getXml(haptic_feedback_customization)).thenReturn(null);
+    private void setupCustomizationResource(String xml, int xmlResId) throws Exception {
+        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
+        mSetFlagsRule.enableFlags(FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
+        doReturn(FakeXmlResourceParser.fromXml(xml)).when(mResourcesMock).getXml(xmlResId);
     }
 
     private void setupCustomizationFile(String xml) throws Exception {
@@ -498,15 +590,34 @@
     }
 
     private void setCustomizationFilePath(String path) {
-        when(mResourcesMock.getString(R.string.config_hapticFeedbackCustomizationFile))
-                .thenReturn(path);
+        doReturn(path).when(mResourcesMock)
+                .getString(R.string.config_hapticFeedbackCustomizationFile);
     }
 
-    private void setupCustomizationResource(String xml) throws Exception {
-        mSetFlagsRule.enableFlags(
-                Flags.FLAG_LOAD_HAPTIC_FEEDBACK_VIBRATION_CUSTOMIZATION_FROM_RESOURCES);
-        when(mResourcesMock.getXml(haptic_feedback_customization))
-                .thenReturn(FakeXmlResourceParser.fromXml(xml));
+    private void clearFileAndResourceSetup() {
+        doThrow(new Resources.NotFoundException()).when(mResourcesMock)
+                .getString(R.string.config_hapticFeedbackCustomizationFile);
+        doThrow(new Resources.NotFoundException()).when(mResourcesMock)
+                .getXml(haptic_feedback_customization);
+        doThrow(new Resources.NotFoundException()).when(mResourcesMock)
+                .getXml(haptic_feedback_customization_source_rotary_encoder);
+        doThrow(new Resources.NotFoundException()).when(mResourcesMock)
+                .getXml(haptic_feedback_customization_source_touchscreen);
+    }
+
+    @Nullable
+    private VibrationEffect getEffectForSource(int effectId,
+            CustomizationSource customizationSource,
+            HapticFeedbackCustomization hapticFeedbackCustomization) {
+        return switch (customizationSource) {
+            case DEVICE_CONFIG_FILE, DEVICE_RESOURCE -> hapticFeedbackCustomization.getEffect(
+                    effectId);
+            case DEVICE_RESOURCE_INPUT_ROTARY -> hapticFeedbackCustomization.getEffect(effectId,
+                    InputDevice.SOURCE_ROTARY_ENCODER);
+            case DEVICE_RESOURCE_INPUT_TOUCHSCREEN -> hapticFeedbackCustomization.getEffect(
+                    effectId,
+                    InputDevice.SOURCE_TOUCHSCREEN);
+        };
     }
 
     private void makeSupported(VibrationEffect... effects) {
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 240bd1e..6076d33 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java
@@ -16,23 +16,26 @@
 
 package com.android.server.vibrator;
 
-import static android.os.VibrationAttributes.CATEGORY_KEYBOARD;
-import static android.os.VibrationAttributes.CATEGORY_UNKNOWN;
 import static android.os.VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
 import static android.os.VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF;
 import static android.os.VibrationAttributes.USAGE_IME_FEEDBACK;
 import static android.os.VibrationAttributes.USAGE_TOUCH;
 import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_QUICK_RISE;
+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_TEXTURE_TICK;
 import static android.os.VibrationEffect.EFFECT_TICK;
+import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED;
 import static android.view.HapticFeedbackConstants.BIOMETRIC_CONFIRM;
 import static android.view.HapticFeedbackConstants.BIOMETRIC_REJECT;
 import static android.view.HapticFeedbackConstants.CLOCK_TICK;
 import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
+import static android.view.HapticFeedbackConstants.DRAG_START;
 import static android.view.HapticFeedbackConstants.KEYBOARD_RELEASE;
 import static android.view.HapticFeedbackConstants.KEYBOARD_TAP;
+import static android.view.HapticFeedbackConstants.NO_HAPTICS;
 import static android.view.HapticFeedbackConstants.SAFE_MODE_ENABLED;
 import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS;
 import static android.view.HapticFeedbackConstants.SCROLL_LIMIT;
@@ -44,6 +47,7 @@
 
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.vibrator.IVibrator;
@@ -54,11 +58,13 @@
 import android.util.AtomicFile;
 import android.util.SparseArray;
 import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.R;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -77,6 +83,11 @@
             VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
     private static final VibrationEffect PRIMITIVE_CLICK_EFFECT =
             VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK, 0.3497f).compose();
+    private static final VibrationEffect PRIMITIVE_THUD_EFFECT =
+            VibrationEffect.startComposition().addPrimitive(PRIMITIVE_THUD, 0.5497f).compose();
+    private static final VibrationEffect PRIMITIVE_QUICK_RISE_EFFECT =
+            VibrationEffect.startComposition().addPrimitive(PRIMITIVE_QUICK_RISE,
+                    0.6497f).compose();
 
     private static final int[] SCROLL_FEEDBACK_CONSTANTS =
             new int[] {SCROLL_ITEM_FOCUS, SCROLL_LIMIT, SCROLL_TICK};
@@ -92,45 +103,52 @@
 
     @Mock private Resources mResourcesMock;
 
-    @Test
-    public void testNonExistentCustomization_useDefault() throws Exception {
-        // No customization file is set.
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
-
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
-                .isEqualTo(VibrationEffect.get(EFFECT_TICK));
-
-        // The customization file specifies no customization.
-        setupCustomizationFile("<haptic-feedback-constants></haptic-feedback-constants>");
-        hapticProvider = createProviderWithDefaultCustomizations();
-
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
-                .isEqualTo(VibrationEffect.get(EFFECT_TICK));
+    @Before
+    public void setUp() {
+        mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED);
     }
 
     @Test
-    public void testExceptionParsingCustomizations_useDefault() throws Exception {
-        setupCustomizationFile("<bad-xml></bad-xml>");
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+    public void testNonExistentCustomization_useDefault() {
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
-                .isEqualTo(VibrationEffect.get(EFFECT_TICK));
+        // No customization for `CLOCK_TICK`, so the default vibration is used.
+        assertThat(provider.getVibration(CLOCK_TICK)).isEqualTo(
+                VibrationEffect.get(EFFECT_TEXTURE_TICK));
+        assertThat(provider.getVibration(CLOCK_TICK,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(
+                VibrationEffect.get(EFFECT_TEXTURE_TICK));
+        assertThat(provider.getVibration(CLOCK_TICK, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
     }
 
     @Test
-    public void testUseValidCustomizedVibration() throws Exception {
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK);
+    public void testUseValidCustomizedVibration() {
+        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK, PRIMITIVE_THUD,
+                PRIMITIVE_QUICK_RISE);
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
         customizations.put(CONTEXT_CLICK, PRIMITIVE_CLICK_EFFECT);
+        SparseArray<VibrationEffect> customizationsRotary = new SparseArray<>();
+        customizationsRotary.put(CONTEXT_CLICK, PRIMITIVE_TICK_EFFECT);
+        customizationsRotary.put(DRAG_START, PRIMITIVE_QUICK_RISE_EFFECT);
+        SparseArray<VibrationEffect> customizationsTouchScreen = new SparseArray<>();
+        customizationsTouchScreen.put(CONTEXT_CLICK, PRIMITIVE_THUD_EFFECT);
+        customizationsTouchScreen.put(DRAG_START, PRIMITIVE_CLICK_EFFECT);
+        HapticFeedbackVibrationProvider provider = createProvider(customizations,
+                customizationsRotary, customizationsTouchScreen);
 
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
-
-        // The override for `CONTEXT_CLICK` is used.
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
+        // The customization for `CONTEXT_CLICK`.
+        assertThat(provider.getVibration(CONTEXT_CLICK))
                 .isEqualTo(PRIMITIVE_CLICK_EFFECT);
-        // `CLOCK_TICK` has no override, so the default vibration is used.
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CLOCK_TICK))
-                .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
+        assertThat(provider.getVibration(CONTEXT_CLICK,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(PRIMITIVE_TICK_EFFECT);
+        assertThat(provider.getVibration(CONTEXT_CLICK,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(PRIMITIVE_THUD_EFFECT);
+        // The customization for `DRAG_START`.
+        assertThat(provider.getVibration(DRAG_START,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(PRIMITIVE_QUICK_RISE_EFFECT);
+        assertThat(provider.getVibration(DRAG_START,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(PRIMITIVE_CLICK_EFFECT);
     }
 
     @Test
@@ -142,92 +160,151 @@
                 + "</constant>"
                 + "</haptic-feedback-constants>";
         setupCustomizationFile(xml);
-
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         // The override for `CONTEXT_CLICK` is not used because the vibration is not supported.
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CONTEXT_CLICK))
+        assertThat(provider.getVibration(CONTEXT_CLICK))
                 .isEqualTo(VibrationEffect.get(EFFECT_TICK));
         // `CLOCK_TICK` has no override, so the default vibration is used.
-        assertThat(hapticProvider.getVibrationForHapticFeedback(CLOCK_TICK))
+        assertThat(provider.getVibration(CLOCK_TICK))
                 .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
     }
 
     @Test
-    public void testHapticTextDisabled_noVibrationReturnedForTextHandleMove() throws Exception {
+    public void testHapticTextDisabled_noVibrationReturnedForTextHandleMove() {
         mockHapticTextSupport(false);
         mockVibratorPrimitiveSupport(PRIMITIVE_CLICK);
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
         customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
+        HapticFeedbackVibrationProvider provider = createProvider(
+                /* customizations= */ customizations,
+                /* customizationsForRotary= */ customizations,
+                /* customizationsForTouchScreen= */ customizations);
 
         // Test with a customization available for `TEXT_HANDLE_MOVE`.
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
-
-        assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE)).isNull();
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isNull();
+        assertThat(
+                provider.getVibration(TEXT_HANDLE_MOVE, InputDevice.SOURCE_TOUCHSCREEN)).isNull();
 
         // Test with no customization available for `TEXT_HANDLE_MOVE`.
-        hapticProvider = createProvider(/* customizations= */ null);
+        provider = createProviderWithoutCustomizations();
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE)).isNull();
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE)).isNull();
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isNull();
+        assertThat(
+                provider.getVibration(TEXT_HANDLE_MOVE, InputDevice.SOURCE_TOUCHSCREEN)).isNull();
     }
 
     @Test
-    public void testHapticTextEnabled_vibrationReturnedForTextHandleMove() throws Exception {
+    public void testHapticTextEnabled_vibrationReturnedForTextHandleMove() {
         mockHapticTextSupport(true);
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK);
+        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_THUD, PRIMITIVE_TICK);
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
         customizations.put(TEXT_HANDLE_MOVE, PRIMITIVE_CLICK_EFFECT);
-
+        SparseArray<VibrationEffect> customizationsByRotary = new SparseArray<>();
+        customizationsByRotary.put(TEXT_HANDLE_MOVE, PRIMITIVE_TICK_EFFECT);
+        SparseArray<VibrationEffect> customizationsByTouchScreen = new SparseArray<>();
+        customizationsByTouchScreen.put(TEXT_HANDLE_MOVE, PRIMITIVE_THUD_EFFECT);
         // Test with a customization available for `TEXT_HANDLE_MOVE`.
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
+        HapticFeedbackVibrationProvider provider = createProvider(customizations,
+                customizationsByRotary, customizationsByTouchScreen);
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
-                .isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE)).isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(PRIMITIVE_TICK_EFFECT);
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(PRIMITIVE_THUD_EFFECT);
 
         // Test with no customization available for `TEXT_HANDLE_MOVE`.
-        hapticProvider = createProvider(/* customizations= */ null);
+        provider = createProviderWithoutCustomizations();
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(TEXT_HANDLE_MOVE))
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE)).isEqualTo(
+                VibrationEffect.get(EFFECT_TEXTURE_TICK));
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(
+                VibrationEffect.get(EFFECT_TEXTURE_TICK));
+        assertThat(provider.getVibration(TEXT_HANDLE_MOVE, InputDevice.SOURCE_TOUCHSCREEN))
                 .isEqualTo(VibrationEffect.get(EFFECT_TEXTURE_TICK));
     }
 
     @Test
-    public void testValidCustomizationPresentForSafeModeEnabled_usedRegardlessOfVibrationResource()
-                throws Exception {
-        mockSafeModeEnabledVibration(10, 20, 30, 40);
-        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK);
+    public void testFeedbackConstantNoHapticEffect_noVibrationRegardlessCustomizations() {
+        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_THUD, PRIMITIVE_TICK);
         SparseArray<VibrationEffect> customizations = new SparseArray<>();
-        customizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
+        customizations.put(NO_HAPTICS, PRIMITIVE_CLICK_EFFECT);
+        SparseArray<VibrationEffect> customizationsByRotary = new SparseArray<>();
+        customizationsByRotary.put(NO_HAPTICS, PRIMITIVE_TICK_EFFECT);
+        SparseArray<VibrationEffect> customizationsByTouchScreen = new SparseArray<>();
+        customizationsByTouchScreen.put(NO_HAPTICS, PRIMITIVE_THUD_EFFECT);
+        HapticFeedbackVibrationProvider provider = createProvider(customizations,
+                customizationsByRotary, customizationsByTouchScreen);
 
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
-
-        assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
-                .isEqualTo(PRIMITIVE_CLICK_EFFECT);
-
-        mockSafeModeEnabledVibration(null);
-        hapticProvider = createProvider(customizations);
-
-        assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
-                .isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        // Whatever customization set to NO_HAPTICS, no vibration happens.
+        assertThat(provider.getVibration(NO_HAPTICS)).isNull();
+        assertThat(provider.getVibration(NO_HAPTICS, InputDevice.SOURCE_ROTARY_ENCODER)).isNull();
+        assertThat(provider.getVibration(NO_HAPTICS, InputDevice.SOURCE_TOUCHSCREEN)).isNull();
     }
 
     @Test
-    public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed()
-                throws Exception {
+    public void testValidCustomizationPresentForSafeModeEnabled_usedRegardlessOfVibrationResource() {
         mockSafeModeEnabledVibration(10, 20, 30, 40);
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
+        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK, PRIMITIVE_THUD);
+        SparseArray<VibrationEffect> safeModeCustomizations = new SparseArray<>();
+        safeModeCustomizations.put(SAFE_MODE_ENABLED, PRIMITIVE_CLICK_EFFECT);
+        SparseArray<VibrationEffect> safeModeCustomizationsByRotary = new SparseArray<>();
+        safeModeCustomizationsByRotary.put(SAFE_MODE_ENABLED, PRIMITIVE_THUD_EFFECT);
+        SparseArray<VibrationEffect> safeModeCustomizationsByTouchScreen = new SparseArray<>();
+        safeModeCustomizationsByTouchScreen.put(SAFE_MODE_ENABLED, PRIMITIVE_TICK_EFFECT);
+        HapticFeedbackVibrationProvider provider =
+                createProvider(safeModeCustomizations, safeModeCustomizationsByRotary,
+                        safeModeCustomizationsByTouchScreen);
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED))
-                .isEqualTo(VibrationEffect.createWaveform(new long[] {10, 20, 30, 40}, -1));
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED)).isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(PRIMITIVE_THUD_EFFECT);
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(PRIMITIVE_TICK_EFFECT);
+
+        // Resource changed
+        mockSafeModeEnabledVibration(null);
+        provider =
+                createProvider(safeModeCustomizations, safeModeCustomizationsByRotary,
+                        safeModeCustomizationsByTouchScreen);
+
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED)).isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(PRIMITIVE_THUD_EFFECT);
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(PRIMITIVE_TICK_EFFECT);
     }
 
     @Test
-    public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed()
-                throws Exception {
-        mockSafeModeEnabledVibration(null);
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(/* customizations= */ null);
+    public void testNoValidCustomizationPresentForSafeModeEnabled_resourceBasedVibrationUsed() {
+        mockSafeModeEnabledVibration(10, 20, 30, 40);
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(SAFE_MODE_ENABLED)).isNull();
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED))
+                .isEqualTo(VibrationEffect.createWaveform(new long[]{10, 20, 30, 40}, -1));
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(VibrationEffect.createWaveform(new long[]{10, 20, 30, 40}, -1));
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(VibrationEffect.createWaveform(new long[]{10, 20, 30, 40}, -1));
+    }
+
+    @Test
+    public void testNoValidCustomizationAndResourcePresentForSafeModeEnabled_noVibrationUsed() {
+        mockSafeModeEnabledVibration(null);
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
+
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED)).isNull();
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED)).isNull();
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isNull();
+        assertThat(provider.getVibration(SAFE_MODE_ENABLED, InputDevice.SOURCE_TOUCHSCREEN))
+                .isNull();
     }
 
     @Test
@@ -238,45 +315,90 @@
         customizations.put(KEYBOARD_RELEASE, PRIMITIVE_TICK_EFFECT);
 
         // Test with a customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
-        HapticFeedbackVibrationProvider hapticProvider = createProvider(customizations);
+        HapticFeedbackVibrationProvider provider = createProvider(customizations);
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
-                .isEqualTo(PRIMITIVE_CLICK_EFFECT);
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
-                .isEqualTo(PRIMITIVE_TICK_EFFECT);
+        assertThat(provider.getVibration(KEYBOARD_TAP)).isEqualTo(PRIMITIVE_CLICK_EFFECT);
+        assertThat(provider.getVibration(KEYBOARD_RELEASE)).isEqualTo(PRIMITIVE_TICK_EFFECT);
 
         // Test with no customization available for `KEYBOARD_TAP` & `KEYBOARD_RELEASE`.
-        hapticProvider = createProviderWithDefaultCustomizations();
+        provider = createProviderWithoutCustomizations();
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
+        assertThat(provider.getVibration(KEYBOARD_TAP))
                 .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
+        assertThat(provider.getVibration(KEYBOARD_RELEASE))
+                .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+        assertThat(provider.getVibration(KEYBOARD_TAP, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+        assertThat(provider.getVibration(KEYBOARD_RELEASE, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
+        assertThat(provider.getVibration(KEYBOARD_TAP, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(VibrationEffect.get(EFFECT_CLICK, true /* fallback */));
+        assertThat(provider.getVibration(KEYBOARD_RELEASE, InputDevice.SOURCE_TOUCHSCREEN))
                 .isEqualTo(VibrationEffect.get(EFFECT_TICK, false /* fallback */));
     }
 
     @Test
-    public void testKeyboardHaptic_fixAmplitude_keyboardCategoryOn_keyboardVibrationReturned() {
+    public void testKeyboardHaptic_fixAmplitude_keyboardVibrationReturned() {
         mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK);
         mockKeyboardVibrationFixedAmplitude(KEYBOARD_VIBRATION_FIXED_AMPLITUDE);
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        assertThat(provider.getVibration(KEYBOARD_TAP)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+        assertThat(provider.getVibration(KEYBOARD_RELEASE)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+        assertThat(provider.getVibration(KEYBOARD_TAP,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+        assertThat(provider.getVibration(KEYBOARD_RELEASE,
+                InputDevice.SOURCE_ROTARY_ENCODER)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+        assertThat(provider.getVibration(KEYBOARD_TAP,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_CLICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+        assertThat(provider.getVibration(KEYBOARD_RELEASE,
+                InputDevice.SOURCE_TOUCHSCREEN)).isEqualTo(
+                VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK,
+                        KEYBOARD_VIBRATION_FIXED_AMPLITUDE).compose());
+    }
 
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_TAP))
-                .isEqualTo(VibrationEffect.startComposition()
-                        .addPrimitive(PRIMITIVE_CLICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
-                        .compose());
-        assertThat(hapticProvider.getVibrationForHapticFeedback(KEYBOARD_RELEASE))
-                .isEqualTo(VibrationEffect.startComposition()
-                        .addPrimitive(PRIMITIVE_TICK, KEYBOARD_VIBRATION_FIXED_AMPLITUDE)
-                        .compose());
+    @Test
+    public void testKeyboardHaptic_withCustomizations_customEffectsUsed() {
+        mockVibratorPrimitiveSupport(PRIMITIVE_CLICK, PRIMITIVE_TICK, PRIMITIVE_THUD,
+                PRIMITIVE_QUICK_RISE);
+        SparseArray<VibrationEffect> customizations = new SparseArray<>();
+        customizations.put(KEYBOARD_TAP, PRIMITIVE_CLICK_EFFECT);
+        customizations.put(KEYBOARD_RELEASE, PRIMITIVE_TICK_EFFECT);
+        SparseArray<VibrationEffect> customizationsByRotary = new SparseArray<>();
+        customizationsByRotary.put(KEYBOARD_TAP, PRIMITIVE_THUD_EFFECT);
+        customizationsByRotary.put(KEYBOARD_RELEASE, PRIMITIVE_QUICK_RISE_EFFECT);
+        SparseArray<VibrationEffect> customizationsByTouchScreen = new SparseArray<>();
+        customizationsByTouchScreen.put(KEYBOARD_TAP, PRIMITIVE_QUICK_RISE_EFFECT);
+        customizationsByTouchScreen.put(KEYBOARD_RELEASE, PRIMITIVE_THUD_EFFECT);
+        HapticFeedbackVibrationProvider provider = createProvider(customizations,
+                customizationsByRotary, customizationsByTouchScreen);
+
+        assertThat(provider.getVibration(KEYBOARD_TAP, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(PRIMITIVE_THUD_EFFECT);
+        assertThat(provider.getVibration(KEYBOARD_RELEASE, InputDevice.SOURCE_ROTARY_ENCODER))
+                .isEqualTo(PRIMITIVE_QUICK_RISE_EFFECT);
+        assertThat(provider.getVibration(KEYBOARD_TAP, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(PRIMITIVE_QUICK_RISE_EFFECT);
+        assertThat(provider.getVibration(KEYBOARD_RELEASE, InputDevice.SOURCE_TOUCHSCREEN))
+                .isEqualTo(PRIMITIVE_THUD_EFFECT);
     }
 
     @Test
     public void testVibrationAttribute_biometricConstants_returnsCommunicationRequestUsage() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+            VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0, /* privFlags */ 0);
             assertThat(attrs.getUsage()).isEqualTo(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
         }
@@ -284,9 +406,9 @@
 
     @Test
     public void testVibrationAttribute_forNotBypassingIntensitySettings() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
-        VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+        VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                 SAFE_MODE_ENABLED, /* flags */ 0, /* privFlags */ 0);
 
         assertThat(attrs.isFlagSet(FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)).isFalse();
@@ -294,9 +416,9 @@
 
     @Test
     public void testVibrationAttribute_forByassingIntensitySettings() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
-        VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+        VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                 SAFE_MODE_ENABLED,
                 /* flags */ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING, /* privFlags */ 0);
 
@@ -306,10 +428,10 @@
     @Test
     public void testVibrationAttribute_scrollFeedback_scrollApiFlagOn_bypassInterruptPolicy() {
         mSetFlagsRule.enableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+            VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0, /* privFlags */ 0);
             assertWithMessage("Expected FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
                    .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isTrue();
@@ -319,10 +441,10 @@
     @Test
     public void testVibrationAttribute_scrollFeedback_scrollApiFlagOff_noBypassInterruptPolicy() {
         mSetFlagsRule.disableFlags(android.view.flags.Flags.FLAG_SCROLL_FEEDBACK_API);
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : SCROLL_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+            VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0, /* privFlags */ 0);
             assertWithMessage("Expected no FLAG_BYPASS_INTERRUPTION_POLICY for effect " + effectId)
                    .that(attrs.isFlagSet(FLAG_BYPASS_INTERRUPTION_POLICY)).isFalse();
@@ -330,50 +452,60 @@
     }
 
     @Test
-    public void testVibrationAttribute_keyboardCategoryOn_notIme_useTouchUsage() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+    public void testVibrationAttribute_notIme_useTouchUsage() {
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+            VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0, /* privFlags */ 0);
             assertWithMessage("Expected USAGE_TOUCH for effect " + effectId)
                     .that(attrs.getUsage()).isEqualTo(USAGE_TOUCH);
-            assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
-                    .that(attrs.getCategory()).isEqualTo(CATEGORY_UNKNOWN);
         }
     }
 
     @Test
-    public void testVibrationAttribute_keyboardCategoryOn_isIme_useImeFeedbackUsage() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+    public void testVibrationAttribute_isIme_useImeFeedbackUsage() {
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : KEYBOARD_FEEDBACK_CONSTANTS) {
-            VibrationAttributes attrs = hapticProvider.getVibrationAttributesForHapticFeedback(
+            VibrationAttributes attrs = provider.getVibrationAttributesForHapticFeedback(
                     effectId, /* flags */ 0,
                     HapticFeedbackConstants.PRIVATE_FLAG_APPLY_INPUT_METHOD_SETTINGS);
             assertWithMessage("Expected USAGE_IME_FEEDBACK for effect " + effectId)
                     .that(attrs.getUsage()).isEqualTo(USAGE_IME_FEEDBACK);
-            assertWithMessage("Expected CATEGORY_KEYBOARD for effect " + effectId)
-                    .that(attrs.getCategory()).isEqualTo(CATEGORY_KEYBOARD);
         }
     }
 
     @Test
     public void testIsRestricted_biometricConstants_returnsTrue() {
-        HapticFeedbackVibrationProvider hapticProvider = createProviderWithDefaultCustomizations();
+        HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations();
 
         for (int effectId : BIOMETRIC_FEEDBACK_CONSTANTS) {
-            assertThat(hapticProvider.isRestrictedHapticFeedback(effectId)).isTrue();
+            assertThat(provider.isRestrictedHapticFeedback(effectId)).isTrue();
         }
     }
 
-    private HapticFeedbackVibrationProvider createProviderWithDefaultCustomizations() {
-        return createProvider(/* customizations= */ null);
+    private HapticFeedbackVibrationProvider createProviderWithoutCustomizations() {
+        return createProvider(/* customizations= */ new SparseArray<>(),
+                /* customizationsRotary= */ new SparseArray<>(),
+                /* customizationsTouchScreen */ new SparseArray<>());
     }
 
     private HapticFeedbackVibrationProvider createProvider(
             SparseArray<VibrationEffect> customizations) {
-        return new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo, customizations);
+        return createProvider(customizations, /* customizationsRotary= */ new SparseArray<>(),
+                /* customizationsTouchScreen */ new SparseArray<>());
+    }
+
+    private HapticFeedbackVibrationProvider createProvider(
+            @NonNull SparseArray<VibrationEffect> customizations,
+            @NonNull SparseArray<VibrationEffect> customizationsRotary,
+            @NonNull SparseArray<VibrationEffect> customizationsTouchScreen) {
+        return new HapticFeedbackVibrationProvider(mResourcesMock, mVibratorInfo,
+                new HapticFeedbackCustomization(
+                        customizations,
+                        customizationsRotary,
+                        customizationsTouchScreen));
     }
 
     private void mockVibratorPrimitiveSupport(int... supportedPrimitives) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
index f3ecfcc..66788b6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/InputDeviceDelegateTest.java
@@ -45,6 +45,8 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -262,7 +264,7 @@
     public void vibrateIfAvailable_withNoInputDevice_returnsFalse() {
         assertFalse(mInputDeviceDelegate.isAvailable());
         assertFalse(mInputDeviceDelegate.vibrateIfAvailable(
-                new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+                new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
                 SYNCED_EFFECT));
     }
 
@@ -277,7 +279,7 @@
         mInputDeviceDelegate.updateInputDeviceVibrators(/* vibrateInputDevices= */ true);
 
         assertTrue(mInputDeviceDelegate.vibrateIfAvailable(
-                new Vibration.CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
+                new CallerInfo(VIBRATION_ATTRIBUTES, UID, -1, PACKAGE_NAME, REASON),
                 SYNCED_EFFECT));
         verify(mIInputManagerMock).vibrateCombined(eq(1), same(SYNCED_EFFECT), any());
         verify(mIInputManagerMock).vibrateCombined(eq(2), same(SYNCED_EFFECT), any());
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
index 4704691..9681d74 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -281,8 +281,8 @@
 
     @Test
     @EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
-    public void scale_withVendorEffect_setsEffectStrengthBasedOnSettings() {
-        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_LOW);
+    public void scale_withVendorEffect_setsEffectStrengthAndScaleBasedOnSettings() {
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         PersistableBundle vendorData = new PersistableBundle();
         vendorData.putString("key", "value");
@@ -291,20 +291,27 @@
         VibrationEffect.VendorEffect scaled =
                 (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
+        // Notification scales up.
+        assertTrue(scaled.getScale() > 1);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 VIBRATION_INTENSITY_MEDIUM);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+        // Notification does not scale.
+        assertEquals(1, scaled.getScale(), TOLERANCE);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        // Notification scales down.
+        assertTrue(scaled.getScale() < 1);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         // Vibration setting being bypassed will use default setting.
-        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
+        assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+        assertEquals(1, scaled.getScale(), TOLERANCE);
     }
 
     @Test
@@ -348,7 +355,7 @@
         scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128),
                 USAGE_TOUCH));
         // Haptic feedback does not scale.
-        assertEquals(128f / 255, scaled.getAmplitude(), 1e-5);
+        assertEquals(128f / 255, scaled.getAmplitude(), TOLERANCE);
     }
 
     @Test
@@ -373,7 +380,7 @@
 
         scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH));
         // Haptic feedback does not scale.
-        assertEquals(0.5, scaled.getScale(), 1e-5);
+        assertEquals(0.5, scaled.getScale(), TOLERANCE);
     }
 
     @Test
@@ -446,7 +453,7 @@
             android.os.vibrator.Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED,
             android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS,
     })
-    public void scale_adaptiveHapticsOnVendorEffect_setsLinearScaleParameter() {
+    public void scale_adaptiveHapticsOnVendorEffect_setsAdaptiveScaleParameter() {
         setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_HIGH);
 
         mVibrationScaler.updateAdaptiveHapticsScale(USAGE_RINGTONE, 0.5f);
@@ -457,12 +464,12 @@
 
         VibrationEffect.VendorEffect scaled =
                 (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
-        assertEquals(scaled.getLinearScale(), 0.5f);
+        assertEquals(scaled.getAdaptiveScale(), 0.5f);
 
         mVibrationScaler.removeAdaptiveHapticsScale(USAGE_RINGTONE);
 
         scaled = (VibrationEffect.VendorEffect) mVibrationScaler.scale(effect, USAGE_RINGTONE);
-        assertEquals(scaled.getLinearScale(), 1.0f);
+        assertEquals(scaled.getAdaptiveScale(), 1.0f);
     }
 
     private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java
new file mode 100644
index 0000000..f69d1c4
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSessionTest.java
@@ -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.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.util.stream.Collectors.toList;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class VibrationSessionTest {
+
+    @Test
+    public void status_hasUniqueProtoEnumValues() {
+        assertThat(
+                Arrays.stream(VibrationSession.Status.values())
+                        .map(VibrationSession.Status::getProtoEnumValue)
+                        .collect(toList()))
+                .containsNoDuplicates();
+    }
+}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index 72ef888..c7a136a 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -81,6 +81,8 @@
 import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import org.junit.After;
 import org.junit.Before;
@@ -119,6 +121,7 @@
             USAGE_PHYSICAL_EMULATION,
             USAGE_RINGTONE,
             USAGE_TOUCH,
+            USAGE_IME_FEEDBACK
     };
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -291,7 +294,7 @@
             if (expectedAllowedVibrations.contains(usage)) {
                 assertVibrationNotIgnoredForUsage(usage);
             } else {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_BACKGROUND);
             }
         }
     }
@@ -349,7 +352,7 @@
         createSystemReadyVibrationSettings();
 
         for (int usage : ALL_USAGES) {
-            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+            assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
         }
     }
 
@@ -364,7 +367,7 @@
         mRegisteredBatteryBroadcastReceiver.onReceive(mContextSpy, wirelessChargingIntent);
 
         for (int usage : ALL_USAGES) {
-            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+            assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
         }
     }
 
@@ -376,7 +379,7 @@
         createSystemReadyVibrationSettings();
         // Check that initially, all usages are ignored due to the wireless charging.
         for (int usage : ALL_USAGES) {
-            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_ON_WIRELESS_CHARGER);
+            assertVibrationIgnoredForUsage(usage, Status.IGNORED_ON_WIRELESS_CHARGER);
         }
 
         Intent nonWirelessChargingIntent = getBatteryChangedIntent(BATTERY_PLUGGED_USB);
@@ -403,7 +406,7 @@
             if (expectedAllowedVibrations.contains(usage)) {
                 assertVibrationNotIgnoredForUsage(usage);
             } else {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_POWER);
             }
         }
     }
@@ -425,7 +428,7 @@
 
         for (int usage : ALL_USAGES) {
             if (usage == USAGE_RINGTONE || usage == USAGE_NOTIFICATION) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_RINGER_MODE);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -469,7 +472,7 @@
             if (usage == USAGE_ACCESSIBILITY) {
                 assertVibrationNotIgnoredForUsage(usage);
             } else {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             }
             assertVibrationNotIgnoredForUsageAndFlags(usage,
                     VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF);
@@ -511,7 +514,7 @@
 
         for (int usage : ALL_USAGES) {
             if (usage == USAGE_TOUCH) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -525,8 +528,8 @@
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
         for (int usage : ALL_USAGES) {
-            if (usage == USAGE_TOUCH) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            if (usage == USAGE_TOUCH || usage == USAGE_IME_FEEDBACK) {
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -541,7 +544,7 @@
 
         for (int usage : ALL_USAGES) {
             if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -556,7 +559,7 @@
 
         for (int usage : ALL_USAGES) {
             if (usage == USAGE_NOTIFICATION) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -573,7 +576,7 @@
 
         for (int usage : ALL_USAGES) {
             if (usage == USAGE_RINGTONE) {
-                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+                assertVibrationIgnoredForUsage(usage, Status.IGNORED_FOR_SETTINGS);
             } else {
                 assertVibrationNotIgnoredForUsage(usage);
             }
@@ -596,66 +599,62 @@
         mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
                 new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
 
-        assertVibrationIgnoredForUsage(USAGE_RINGTONE, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+        assertVibrationIgnoredForUsage(USAGE_RINGTONE, Status.IGNORED_FOR_RINGER_MODE);
     }
 
     @Test
     public void shouldIgnoreVibration_withKeyboardSettingsOff_shouldIgnoreKeyboardVibration() {
+        setKeyboardVibrationSettingsSupported(true);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 0 /* OFF*/);
-        setKeyboardVibrationSettingsSupported(true);
 
         // Keyboard touch ignored.
         assertVibrationIgnoredForAttributes(
                 new VibrationAttributes.Builder()
-                        .setUsage(USAGE_TOUCH)
-                        .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                        .setUsage(USAGE_IME_FEEDBACK)
                         .build(),
-                Vibration.Status.IGNORED_FOR_SETTINGS);
+                Status.IGNORED_FOR_SETTINGS);
 
         // General touch and keyboard touch with bypass flag not ignored.
         assertVibrationNotIgnoredForUsage(USAGE_TOUCH);
         assertVibrationNotIgnoredForAttributes(
                 new VibrationAttributes.Builder()
-                        .setUsage(USAGE_TOUCH)
-                        .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                        .setUsage(USAGE_IME_FEEDBACK)
                         .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
                         .build());
     }
 
     @Test
     public void shouldIgnoreVibration_withKeyboardSettingsOn_shouldNotIgnoreKeyboardVibration() {
+        setKeyboardVibrationSettingsSupported(true);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
-        setKeyboardVibrationSettingsSupported(true);
 
         // General touch ignored.
-        assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+        assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS);
 
         // Keyboard touch not ignored.
         assertVibrationNotIgnoredForAttributes(
                 new VibrationAttributes.Builder()
-                        .setUsage(USAGE_TOUCH)
-                        .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                        .setUsage(USAGE_IME_FEEDBACK)
                         .build());
     }
 
     @Test
-    public void shouldIgnoreVibration_notSupportKeyboardVibration_ignoresKeyboardTouchVibration() {
+    public void shouldIgnoreVibration_notSupportKeyboardVibration_followsTouchFeedbackSettings() {
+        setKeyboardVibrationSettingsSupported(false);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED, 1 /* ON */);
-        setKeyboardVibrationSettingsSupported(false);
 
         // General touch ignored.
-        assertVibrationIgnoredForUsage(USAGE_TOUCH, Vibration.Status.IGNORED_FOR_SETTINGS);
+        assertVibrationIgnoredForUsage(USAGE_TOUCH, Status.IGNORED_FOR_SETTINGS);
 
         // Keyboard touch ignored.
         assertVibrationIgnoredForAttributes(
                 new VibrationAttributes.Builder()
-                        .setUsage(USAGE_TOUCH)
-                        .setCategory(VibrationAttributes.CATEGORY_KEYBOARD)
+                        .setUsage(USAGE_IME_FEEDBACK)
                         .build(),
-                Vibration.Status.IGNORED_FOR_SETTINGS);
+                Status.IGNORED_FOR_SETTINGS);
     }
 
     @Test
@@ -671,7 +670,7 @@
         // Ignore the vibration when the coming device id represents a virtual device.
         for (int usage : ALL_USAGES) {
             assertVibrationIgnoredForUsageAndDevice(usage, VIRTUAL_DEVICE_ID,
-                    Vibration.Status.IGNORED_FROM_VIRTUAL_DEVICE);
+                    Status.IGNORED_FROM_VIRTUAL_DEVICE);
         }
     }
 
@@ -914,22 +913,21 @@
     }
 
     private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
-            Vibration.Status expectedStatus) {
+            Status expectedStatus) {
         assertVibrationIgnoredForUsageAndDevice(usage, Context.DEVICE_ID_DEFAULT, expectedStatus);
     }
 
     private void assertVibrationIgnoredForUsageAndDevice(@VibrationAttributes.Usage int usage,
-            int deviceId, Vibration.Status expectedStatus) {
-        Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+            int deviceId, Status expectedStatus) {
+        CallerInfo callerInfo = new CallerInfo(
                 VibrationAttributes.createForUsage(usage), UID, deviceId, null, null);
         assertEquals(errorMessageForUsage(usage), expectedStatus,
                 mVibrationSettings.shouldIgnoreVibration(callerInfo));
     }
 
     private void assertVibrationIgnoredForAttributes(VibrationAttributes attrs,
-            Vibration.Status expectedStatus) {
-        Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
-                Context.DEVICE_ID_DEFAULT, null, null);
+            Status expectedStatus) {
+        CallerInfo callerInfo = new CallerInfo(attrs, UID, Context.DEVICE_ID_DEFAULT, null, null);
         assertEquals(errorMessageForAttributes(attrs), expectedStatus,
                 mVibrationSettings.shouldIgnoreVibration(callerInfo));
     }
@@ -951,7 +949,7 @@
     private void assertVibrationNotIgnoredForUsageAndFlagsAndDevice(
             @VibrationAttributes.Usage int usage, int deviceId,
             @VibrationAttributes.Flag int flags) {
-        Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(
+        CallerInfo callerInfo = new CallerInfo(
                 new VibrationAttributes.Builder().setUsage(usage).setFlags(flags).build(), UID,
                 deviceId, null, null);
         assertNull(errorMessageForUsage(usage),
@@ -959,7 +957,7 @@
     }
 
     private void assertVibrationNotIgnoredForAttributes(VibrationAttributes attrs) {
-        Vibration.CallerInfo callerInfo = new Vibration.CallerInfo(attrs, UID,
+        CallerInfo callerInfo = new CallerInfo(attrs, UID,
                 Context.DEVICE_ID_DEFAULT, null, null);
         assertNull(errorMessageForAttributes(attrs),
                 mVibrationSettings.shouldIgnoreVibration(callerInfo));
@@ -1020,10 +1018,10 @@
                 new PowerManager.SleepData(sleepTime, reason));
     }
 
-    private Vibration.CallerInfo createCallerInfo(int uid, String opPkg,
+    private CallerInfo createCallerInfo(int uid, String opPkg,
             @VibrationAttributes.Usage int usage) {
         VibrationAttributes attrs = VibrationAttributes.createForUsage(usage);
-        return new Vibration.CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
+        return new CallerInfo(attrs, uid, VIRTUAL_DEVICE_ID, opPkg, null);
     }
 
     private void setBatteryReceiverRegistrationResult(Intent result) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java
deleted file mode 100644
index 84f8412..0000000
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationTest.java
+++ /dev/null
@@ -1,37 +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.server.vibrator;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static java.util.stream.Collectors.toList;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-
-public class VibrationTest {
-
-    @Test
-    public void status_hasUniqueProtoEnumValues() {
-        assertThat(
-                Arrays.stream(Vibration.Status.values())
-                        .map(Vibration.Status::getProtoEnumValue)
-                        .collect(toList()))
-                .containsNoDuplicates();
-    }
-}
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 0fbdce4..31cc50f1 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -34,13 +34,15 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
-import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ContextWrapper;
 import android.content.pm.PackageManagerInternal;
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
@@ -52,6 +54,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -66,12 +69,17 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.provider.Settings;
 import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.LocalServices;
+import com.android.server.vibrator.VibrationSession.CallerInfo;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import org.junit.After;
 import org.junit.Before;
@@ -105,10 +113,12 @@
     private static final int TEST_DEFAULT_AMPLITUDE = 255;
     private static final float TEST_DEFAULT_SCALE_LEVEL_GAIN = 1.4f;
 
-    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Mock private PackageManagerInternal mPackageManagerInternalMock;
     @Mock private VibrationThread.VibratorManagerHooks mManagerHooks;
@@ -117,6 +127,7 @@
     @Mock private VibrationConfig mVibrationConfigMock;
     @Mock private VibratorFrameworkStatsLogger mStatsLoggerMock;
 
+    private ContextWrapper mContextSpy;
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
     private VibrationSettings mVibrationSettings;
     private VibrationScaler mVibrationScaler;
@@ -149,14 +160,16 @@
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
 
-        Context context = InstrumentationRegistry.getContext();
-        mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
-                mVibrationConfigMock);
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+        mVibrationSettings = new VibrationSettings(mContextSpy,
+                new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
         mVibrationScaler = new VibrationScaler(mVibrationConfigMock, mVibrationSettings);
 
         mockVibrators(VIBRATOR_ID);
 
-        PowerManager.WakeLock wakeLock = context.getSystemService(
+        PowerManager.WakeLock wakeLock = mContextSpy.getSystemService(
                 PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mThread = new VibrationThread(wakeLock, mManagerHooks);
         mThread.start();
@@ -178,7 +191,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
     }
 
     @Test
@@ -191,7 +204,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
     }
 
     @Test
@@ -205,7 +218,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -222,7 +235,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -242,7 +255,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(15)),
@@ -254,6 +267,9 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
+        // No user settings scale.
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
         VibrationEffect effect = VibrationEffect.createWaveform(
@@ -277,6 +293,9 @@
     @Test
     @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
     public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
+        // No user settings scale.
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         VibrationEffect effect = VibrationEffect.createWaveform(
                 new long[]{5, 5, 5}, new int[]{1, 1, 1}, -1);
@@ -309,13 +328,10 @@
         assertTrue(mThread.isRunningVibrationId(vibrationId));
         assertTrue(mControllers.get(VIBRATOR_ID).isVibrating());
 
-        Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(
-                Vibration.Status.CANCELLED_SUPERSEDED, new Vibration.CallerInfo(
-                VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM), /* uid= */
-                1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
-        mVibrationConductor.notifyCancelled(
-                cancelVibrationInfo,
-                /* immediate= */ false);
+        Vibration.EndInfo cancelVibrationInfo = new Vibration.EndInfo(Status.CANCELLED_SUPERSEDED,
+                new CallerInfo(VibrationAttributes.createForUsage(VibrationAttributes.USAGE_ALARM),
+                        /* uid= */ 1, /* deviceId= */ -1, /* opPkg= */ null, /* reason= */ null));
+        mVibrationConductor.notifyCancelled(cancelVibrationInfo, /* immediate= */ false);
         waitForCompletion();
         assertFalse(mThread.isRunningVibrationId(vibrationId));
 
@@ -346,11 +362,10 @@
 
         assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(expectedOneShot(5000)),
                 fakeVibrator.getEffectSegments(vibrationId));
@@ -368,7 +383,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
@@ -389,7 +404,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId))
@@ -416,11 +431,10 @@
         assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5,
                 5000L + TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5))
@@ -449,12 +463,11 @@
         assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
         // PWLE size max was used to generate a single vibrate call with 10 segments.
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
     }
@@ -479,12 +492,11 @@
         assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ false);
         waitForCompletion();
 
         // Composition size max was used to generate a single vibrate call with 10 primitives.
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
     }
@@ -502,11 +514,10 @@
 
         assertTrue(waitUntil(() -> !fakeVibrator.getAmplitudes().isEmpty(), TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(expectedOneShot(5550)),
                 fakeVibrator.getEffectSegments(vibrationId));
@@ -528,11 +539,10 @@
         assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
                 expectedOnDuration + TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         List<VibrationEffectSegment> effectSegments = fakeVibrator.getEffectSegments(vibrationId);
         // First time, turn vibrator ON for the expected fixed duration.
@@ -567,14 +577,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread =
                 new Thread(() -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -597,14 +607,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread =
                 new Thread(() -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SETTINGS_UPDATE);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -624,14 +634,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread =
                 new Thread(() -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -646,7 +656,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_THUD)),
@@ -667,7 +677,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedOneShot(10)),
@@ -684,7 +694,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
         verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
     }
 
@@ -701,7 +711,7 @@
                 eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
 
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
@@ -726,7 +736,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
@@ -745,7 +755,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
         verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.IGNORED_UNSUPPORTED);
+        verifyCallbacksTriggered(vibrationId, Status.IGNORED_UNSUPPORTED);
         assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
     }
 
@@ -767,7 +777,7 @@
         long vibrationId = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         // Vibrator compose called twice.
         verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         assertEquals(3, fakeVibrator.getEffectSegments(vibrationId).size());
@@ -807,7 +817,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedOneShot(10),
@@ -848,7 +858,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         List<VibrationEffectSegment> segments =
@@ -887,7 +897,7 @@
         verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
         assertEquals(Arrays.asList(
                 expectedRamp(/* amplitude= */ 1, /* frequencyHz= */ 150, /* duration= */ 10),
@@ -925,7 +935,7 @@
         long vibrationId = startThreadAndDispatcher(effect);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         // Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
         // Using best split points instead of max-packing PWLEs.
@@ -950,7 +960,7 @@
         waitForCompletion();
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
     }
 
     @Test
@@ -960,7 +970,7 @@
         long vibrationId = startThreadAndDispatcher(VibrationEffect.createOneShot(10, 100));
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         verify(mManagerHooks, never()).prepareSyncedVibration(anyLong(), any());
         verify(mManagerHooks, never()).triggerSyncedVibration(anyLong());
         verify(mManagerHooks, never()).cancelSyncedVibration();
@@ -981,7 +991,7 @@
         verify(mManagerHooks).noteVibratorOff(eq(UID));
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
         verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_TICK)),
@@ -1005,7 +1015,7 @@
         verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
@@ -1048,7 +1058,7 @@
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(4), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
@@ -1100,7 +1110,7 @@
         batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
         batteryVerifier.verify(mManagerHooks).noteVibratorOff(eq(UID));
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
@@ -1149,7 +1159,7 @@
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
         verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
         verify(mManagerHooks, never()).cancelSyncedVibration();
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         VibrationEffectSegment expected = expectedPrimitive(
                 VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
@@ -1196,7 +1206,7 @@
         verify(mManagerHooks).prepareSyncedVibration(eq(expectedCap), eq(vibratorIds));
         verify(mManagerHooks).triggerSyncedVibration(eq(vibrationId));
         verify(mManagerHooks, never()).cancelSyncedVibration();
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
     }
 
     @Test
@@ -1288,7 +1298,7 @@
         verify(mControllerCallbacks).onComplete(eq(1), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(2), eq(vibrationId));
         verify(mControllerCallbacks).onComplete(eq(3), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
         assertFalse(mControllers.get(3).isVibrating());
@@ -1461,8 +1471,7 @@
         // fail at waitForCompletion(cancellingThread).
         Thread cancellingThread = new Thread(
                 () -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                        /* immediate= */ false));
+                        new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false));
         cancellingThread.start();
 
         // Cancelling the vibration should be fast and return right away, even if the thread is
@@ -1471,7 +1480,7 @@
 
         // After the vibrator call ends the vibration is cancelled and the vibrator is turned off.
         waitForCompletion(/* timeout= */ latency + TEST_TIMEOUT_MILLIS);
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
 
@@ -1500,14 +1509,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread = new Thread(
                 () -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1534,14 +1543,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread = new Thread(
                 () -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1568,15 +1577,14 @@
         // fail at waitForCompletion(vibrationThread) if the vibration not cancelled immediately.
         Thread cancellingThread = new Thread(
                 () -> mVibrationConductor.notifyCancelled(
-                        new Vibration.EndInfo(
-                                Vibration.Status.CANCELLED_BY_SCREEN_OFF),
+                        new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF),
                         /* immediate= */ false));
         cancellingThread.start();
 
         waitForCompletion(/* timeout= */ 50);
         cancellingThread.join();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_SCREEN_OFF);
         assertFalse(mControllers.get(1).isVibrating());
         assertFalse(mControllers.get(2).isVibrating());
     }
@@ -1595,7 +1603,7 @@
 
         verify(mVibrationToken).linkToDeath(same(mVibrationConductor), eq(0));
         verify(mVibrationToken).unlinkToDeath(same(mVibrationConductor), eq(0));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BINDER_DIED);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BINDER_DIED);
         assertFalse(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).isEmpty());
         assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
     }
@@ -1611,7 +1619,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         // Duration extended for 5 + 5 + 5 + 15.
         assertEquals(Arrays.asList(expectedOneShot(30)),
@@ -1634,7 +1642,7 @@
 
         // Vibration completed but vibrator not yet released.
         verify(mManagerHooks, timeout(TEST_TIMEOUT_MILLIS)).onVibrationCompleted(eq(vibrationId),
-                eq(new Vibration.EndInfo(Vibration.Status.FINISHED)));
+                eq(new Vibration.EndInfo(Status.FINISHED)));
         verify(mManagerHooks, never()).onVibrationThreadReleased(anyLong());
 
         // Thread still running ramp down.
@@ -1646,13 +1654,12 @@
 
         // Will stop the ramp down right away.
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE),
-                /* immediate= */ true);
+                new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE), /* immediate= */ true);
         waitForCompletion();
 
         // Does not cancel already finished vibration, but releases vibrator.
         verify(mManagerHooks, never()).onVibrationCompleted(eq(vibrationId),
-                eq(new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE)));
+                eq(new Vibration.EndInfo(Status.CANCELLED_BY_SETTINGS_UPDATE)));
         verify(mManagerHooks).onVibrationThreadReleased(vibrationId);
     }
 
@@ -1667,11 +1674,10 @@
         assertTrue(waitUntil(() -> mControllers.get(VIBRATOR_ID).isVibrating(),
                 TEST_TIMEOUT_MILLIS));
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId, Status.CANCELLED_BY_USER);
 
         // Duration extended for 10000 + 15.
         assertEquals(Arrays.asList(expectedOneShot(10_015)),
@@ -1694,7 +1700,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                 mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
@@ -1712,7 +1718,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibrationId))
                 .containsExactly(effect)
@@ -1735,7 +1741,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         assertEquals(
                 Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
@@ -1762,7 +1768,7 @@
         waitForCompletion();
 
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
-        verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
                 fakeVibrator.getEffectSegments(vibrationId));
@@ -1793,14 +1799,13 @@
         long vibrationId1 = startThreadAndDispatcher(effect1);
         waitForCompletion();
         verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
-        verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
 
         long vibrationId2 = startThreadAndDispatcher(effect2);
         // Effect2 won't complete on its own. Cancel it after a couple of repeats.
         Thread.sleep(150);  // More than two TICKs.
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER),
-                /* immediate= */ false);
+                new Vibration.EndInfo(Status.CANCELLED_BY_USER), /* immediate= */ false);
         waitForCompletion();
 
         long vibrationId3 = startThreadAndDispatcher(effect3);
@@ -1810,8 +1815,7 @@
         long start4 = System.currentTimeMillis();
         long vibrationId4 = startThreadAndDispatcher(effect4);
         mVibrationConductor.notifyCancelled(
-                new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF),
-                /* immediate= */ true);
+                new Vibration.EndInfo(Status.CANCELLED_BY_SCREEN_OFF), /* immediate= */ true);
         waitForCompletion();
         long duration4 = System.currentTimeMillis() - start4;
 
@@ -1824,14 +1828,14 @@
 
         // Effect1
         verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibrationId1);
-        verifyCallbacksTriggered(vibrationId1, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId1, Status.FINISHED);
 
         assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
                 fakeVibrator.getEffectSegments(vibrationId1));
 
         // Effect2: repeating, cancelled.
         verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibrationId2);
-        verifyCallbacksTriggered(vibrationId2, Vibration.Status.CANCELLED_BY_USER);
+        verifyCallbacksTriggered(vibrationId2, Status.CANCELLED_BY_USER);
 
         // The exact count of segments might vary, so just check that there's more than 2 and
         // all elements are the same segment.
@@ -1843,13 +1847,13 @@
 
         // Effect3
         verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibrationId3));
-        verifyCallbacksTriggered(vibrationId3, Vibration.Status.FINISHED);
+        verifyCallbacksTriggered(vibrationId3, Status.FINISHED);
         assertEquals(Arrays.asList(
                         expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
                 fakeVibrator.getEffectSegments(vibrationId3));
 
         // Effect4: cancelled quickly.
-        verifyCallbacksTriggered(vibrationId4, Vibration.Status.CANCELLED_BY_SCREEN_OFF);
+        verifyCallbacksTriggered(vibrationId4, Status.CANCELLED_BY_SCREEN_OFF);
         assertTrue("Tested duration=" + duration4, duration4 < 2000);
 
         // Effect5: played normally after effect4, which may or may not have played.
@@ -1864,6 +1868,13 @@
         }
     }
 
+    private void setUserSetting(String settingName, int value) {
+        Settings.System.putIntForUser(
+                mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+        // FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
+        mVibrationSettings.mSettingObserver.onChange(false);
+    }
+
     private long startThreadAndDispatcher(VibrationEffect effect) {
         return startThreadAndDispatcher(CombinedVibration.createParallel(effect));
     }
@@ -1883,7 +1894,7 @@
                 .build();
         HalVibration vib = new HalVibration(mVibrationToken,
                 CombinedVibration.createParallel(effect),
-                new Vibration.CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
+                new CallerInfo(attrs, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
         return startThreadAndDispatcher(vib, requestVibrationParamsFuture);
     }
 
@@ -1920,7 +1931,7 @@
 
     private HalVibration createVibration(CombinedVibration effect) {
         return new HalVibration(mVibrationToken, effect,
-                new Vibration.CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
+                new CallerInfo(ATTRS, UID, DEVICE_ID, PACKAGE_NAME, "reason"));
     }
 
     private SparseArray<VibratorController> createVibratorControllers() {
@@ -1983,7 +1994,7 @@
                 .collect(Collectors.toList());
     }
 
-    private void verifyCallbacksTriggered(long vibrationId, Vibration.Status expectedStatus) {
+    private void verifyCallbacksTriggered(long vibrationId, Status expectedStatus) {
         verifyCallbacksTriggered(vibrationId, new Vibration.EndInfo(expectedStatus));
     }
 
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
index 3466bbb..cd057b6 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorFrameworkStatsLoggerTest.java
@@ -22,6 +22,8 @@
 import android.os.Handler;
 import android.os.test.TestLooper;
 
+import com.android.server.vibrator.VibrationSession.Status;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -113,7 +115,6 @@
     }
 
     private static VibrationStats.StatsInfo newEmptyStatsInfo() {
-        return new VibrationStats.StatsInfo(
-                0, 0, 0, Vibration.Status.FINISHED, new VibrationStats(), 0L);
+        return new VibrationStats.StatsInfo(0, 0, 0, Status.FINISHED, new VibrationStats(), 0L);
     }
 }
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 f009229..4012575 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 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;
@@ -108,6 +110,7 @@
 import com.android.server.LocalServices;
 import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.pm.BackgroundUserSoundNotifier;
+import com.android.server.vibrator.VibrationSession.Status;
 
 import org.junit.After;
 import org.junit.Before;
@@ -192,6 +195,11 @@
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
     private final SparseArray<VibrationEffect>  mHapticFeedbackVibrationMap = new SparseArray<>();
+    private final SparseArray<VibrationEffect>  mHapticFeedbackVibrationMapSourceRotary =
+            new SparseArray<>();
+    private final SparseArray<VibrationEffect>  mHapticFeedbackVibrationMapSourceTouchScreen =
+            new SparseArray<>();
+
     private final List<HalVibration> mPendingVibrations = new ArrayList<>();
 
     private VibratorManagerService mService;
@@ -339,8 +347,10 @@
                     @Override
                     HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider(
                             Resources resources, VibratorInfo vibratorInfo) {
-                        return new HapticFeedbackVibrationProvider(
-                                resources, vibratorInfo, mHapticFeedbackVibrationMap);
+                        return new HapticFeedbackVibrationProvider(resources, vibratorInfo,
+                                new HapticFeedbackCustomization(mHapticFeedbackVibrationMap,
+                                        mHapticFeedbackVibrationMapSourceRotary,
+                                        mHapticFeedbackVibrationMapSourceTouchScreen));
                     }
 
                     @Override
@@ -794,8 +804,8 @@
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
 
-        var vib = vibrate(service,
-                VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), RINGTONE_ATTRS);
+        HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+                new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), RINGTONE_ATTRS);
 
         assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
 
@@ -806,14 +816,80 @@
         service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
 
         assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+        assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_APP_OPS);
+    }
 
-        var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
-        verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
-                .writeVibrationReportedAsync(statsInfoCaptor.capture());
+    @Test
+    public void vibrate_thenPowerModeChanges_getsCancelled() throws Exception {
+        mockVibrators(1, 2);
+        VibratorManagerService service = createSystemReadyService();
 
-        VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0);
-        assertEquals(Vibration.Status.CANCELLED_BY_APP_OPS.getProtoEnumValue(),
-                touchMetrics.status);
+        HalVibration vib = vibrate(service,
+                CombinedVibration.startParallel()
+                        .addVibrator(1, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100))
+                        .combine(),
+                HAPTIC_FEEDBACK_ATTRS);
+
+        // VibrationThread will start this vibration async, so wait until vibration is triggered.
+        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+        assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+        assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE);
+    }
+
+    @Test
+    public void vibrate_thenSettingsRefreshedWithoutChange_doNotCancelVibration() throws Exception {
+        mockVibrators(1);
+        VibratorManagerService service = createSystemReadyService();
+
+        vibrate(service, VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100),
+                HAPTIC_FEEDBACK_ATTRS);
+
+        // VibrationThread will start this vibration async, so wait until vibration is triggered.
+        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+        service.updateServiceState();
+
+        // Vibration is not stopped nearly after updating service.
+        assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+    }
+
+    @Test
+    public void vibrate_thenSettingsChange_getsCancelled() throws Exception {
+        mockVibrators(1);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration vib = vibrate(service,
+                VibrationEffect.createOneShot(2 * TEST_TIMEOUT_MILLIS, 100),
+                HAPTIC_FEEDBACK_ATTRS);
+
+        // VibrationThread will start this vibration async, so wait until vibration is triggered.
+        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        service.mVibrationSettings.mSettingObserver.onChange(false);
+        service.updateServiceState();
+
+        assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+        assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SETTINGS_UPDATE);
+    }
+
+    @Test
+    public void vibrate_thenScreenTurnsOff_getsCancelled() throws Throwable {
+        mockVibrators(1);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+                new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS);
+
+        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+        service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF));
+
+        assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
+        assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_SCREEN_OFF);
     }
 
     @Test
@@ -822,8 +898,8 @@
         mockVibrators(1);
         VibratorManagerService service = createSystemReadyService();
 
-        var vib = vibrate(service,
-                VibrationEffect.createWaveform(new long[]{100, 100, 100, 100}, 0), ALARM_ATTRS);
+        HalVibration vib = vibrate(service, VibrationEffect.createWaveform(
+                new long[]{0, TEST_TIMEOUT_MILLIS, TEST_TIMEOUT_MILLIS}, 0), ALARM_ATTRS);
 
         assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
 
@@ -832,14 +908,7 @@
                 BackgroundUserSoundNotifier.ACTION_MUTE_SOUND));
 
         assertTrue(waitUntil(s -> vib.hasEnded(), service, TEST_TIMEOUT_MILLIS));
-
-        var statsInfoCaptor = ArgumentCaptor.forClass(VibrationStats.StatsInfo.class);
-        verify(mVibratorFrameworkStatsLoggerMock, timeout(TEST_TIMEOUT_MILLIS))
-                .writeVibrationReportedAsync(statsInfoCaptor.capture());
-
-        VibrationStats.StatsInfo touchMetrics = statsInfoCaptor.getAllValues().get(0);
-        assertEquals(Vibration.Status.CANCELLED_BY_FOREGROUND_USER.getProtoEnumValue(),
-                touchMetrics.status);
+        assertThat(vib.getStatus()).isEqualTo(Status.CANCELLED_BY_FOREGROUND_USER);
     }
 
     @Test
@@ -1305,7 +1374,7 @@
     }
 
     @Test
-    public void vibrate_withriggerCallback_finishesVibration() throws Exception {
+    public void vibrate_withTriggerCallback_finishesVibration() throws Exception {
         mockCapabilities(IVibratorManager.CAP_SYNC, IVibratorManager.CAP_PREPARE_COMPOSE);
         mockVibrators(1, 2);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1475,6 +1544,60 @@
     }
 
     @Test
+    public void performHapticFeedbackForInputDevice_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
+        // permissions.
+        denyPermission(android.Manifest.permission.VIBRATE);
+        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));
+        mHapticFeedbackVibrationMapSourceTouchScreen.put(
+                HapticFeedbackConstants.DRAG_START,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK);
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration vibrationByRotary =
+                performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                        service, HapticFeedbackConstants.SCROLL_TICK, /* inputDeviceId= */ 0,
+                        InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ true);
+        HalVibration vibrationByTouchScreen =
+                performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                        service, HapticFeedbackConstants.DRAG_START, /* inputDeviceId= */ 0,
+                        InputDevice.SOURCE_TOUCHSCREEN, /* always= */ true);
+
+        List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+        // 2 haptics: 1 by rotary + 1 by touch screen
+        assertEquals(2, playedSegments.size());
+        // Verify feedback by rotary input
+        PrebakedSegment segmentByRotary = (PrebakedSegment) playedSegments.get(0);
+        assertEquals(VibrationEffect.EFFECT_CLICK, segmentByRotary.getEffectId());
+        VibrationAttributes attrsByRotary = vibrationByRotary.callerInfo.attrs;
+        assertEquals(VibrationAttributes.USAGE_HARDWARE_FEEDBACK, attrsByRotary.getUsage());
+        assertTrue(attrsByRotary.isFlagSet(
+                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+        assertTrue(attrsByRotary.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+        // Verify feedback by touch screen input
+        PrebakedSegment segmentByTouchScreen = (PrebakedSegment) playedSegments.get(1);
+        assertEquals(VibrationEffect.EFFECT_TICK, segmentByTouchScreen.getEffectId());
+        VibrationAttributes attrsByTouchScreen = vibrationByTouchScreen.callerInfo.attrs;
+        assertEquals(VibrationAttributes.USAGE_TOUCH, attrsByTouchScreen.getUsage());
+        assertTrue(attrsByRotary.isFlagSet(
+                VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF));
+        assertTrue(attrsByRotary.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY));
+    }
+
+    @Test
     public void performHapticFeedback_restrictedConstantsWithoutPermission_doesNotVibrate()
             throws Exception {
         // Deny permission to vibrate with restricted constants
@@ -1506,6 +1629,42 @@
     }
 
     @Test
+    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,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+        // Hidden system-only constant, permission required
+        mHapticFeedbackVibrationMapSourceTouchScreen.put(
+                HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setSupportedEffects(
+                VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+        VibratorManagerService service = createSystemReadyService();
+
+        // This vibrates.
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                        service, HapticFeedbackConstants.CONFIRM, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ false);
+        // This doesn't.
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                        service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_TOUCHSCREEN, /* always= */ false);
+
+        List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+        assertEquals(1, playedSegments.size());
+        PrebakedSegment segment = (PrebakedSegment) playedSegments.get(0);
+        assertEquals(VibrationEffect.EFFECT_CLICK, segment.getEffectId());
+    }
+
+    @Test
     public void performHapticFeedback_restrictedConstantsWithPermission_playsVibration()
             throws Exception {
         // Grant permission to vibrate with restricted constants
@@ -1539,33 +1698,95 @@
     }
 
     @Test
+    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
+        mHapticFeedbackVibrationMapSourceRotary.put(
+                HapticFeedbackConstants.CONFIRM,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+        // Hidden system-only constant, permission required
+        mHapticFeedbackVibrationMapSourceTouchScreen.put(
+                HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK));
+        mockVibrators(1);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
+        fakeVibrator.setSupportedEffects(
+                VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK);
+        VibratorManagerService service = createSystemReadyService();
+
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, HapticFeedbackConstants.CONFIRM, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ false);
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, HapticFeedbackConstants.BIOMETRIC_CONFIRM, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_TOUCHSCREEN, /* always= */ false);
+
+        List<VibrationEffectSegment> playedSegments = fakeVibrator.getAllEffectSegments();
+        assertEquals(2, playedSegments.size());
+        assertEquals(VibrationEffect.EFFECT_CLICK,
+                ((PrebakedSegment) playedSegments.get(0)).getEffectId());
+        assertEquals(VibrationEffect.EFFECT_HEAVY_CLICK,
+                ((PrebakedSegment) playedSegments.get(1)).getEffectId());
+    }
+
+    @Test
     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,
                 VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
+        mHapticFeedbackVibrationMapSourceRotary.put(
+                HapticFeedbackConstants.KEYBOARD_TAP,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD));
+        mHapticFeedbackVibrationMapSourceTouchScreen.put(
+                HapticFeedbackConstants.KEYBOARD_TAP,
+                VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
         fakeVibrator.setVibratorInfoLoadSuccessful(false);
-        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
+        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK,
+                VibrationEffect.EFFECT_THUD);
         VibratorManagerService service = createService();
 
+        // performHapticFeedback.
         performHapticFeedbackAndWaitUntilFinished(
                 service, HapticFeedbackConstants.KEYBOARD_TAP, /* always= */ true);
+        // performHapticFeedbackForInputDevice.
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, HapticFeedbackConstants.KEYBOARD_TAP, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ true);
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, HapticFeedbackConstants.KEYBOARD_TAP, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_TOUCHSCREEN, /* always= */ true);
 
         assertTrue(fakeVibrator.getAllEffectSegments().isEmpty());
     }
 
     @Test
     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();
 
         // These are bad haptic feedback IDs, so expect no vibration played.
+        // Test performHapticFeedback
         performHapticFeedbackAndWaitUntilFinished(service, /* constant= */ -1, /* always= */ false);
         performHapticFeedbackAndWaitUntilFinished(
                 service, HapticFeedbackConstants.NO_HAPTICS, /* always= */ true);
+        // Test performHapticFeedbackForInputDevice
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, /* constant= */ -1, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ true);
+        performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, /* constant= */ -1, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_TOUCHSCREEN, /* always= */ true);
 
         assertTrue(mVibratorProviders.get(1).getAllEffectSegments().isEmpty());
     }
@@ -1582,6 +1803,17 @@
     }
 
     @Test
+    public void performHapticFeedbackForInputDevice_usesServiceAsToken() throws Exception {
+        VibratorManagerService service = createSystemReadyService();
+
+        HalVibration vibration = performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+                service, HapticFeedbackConstants.SCROLL_TICK, /* inputDeviceId= */ 0,
+                InputDevice.SOURCE_ROTARY_ENCODER, /* always= */ true);
+
+        assertTrue(vibration.callerToken == service);
+    }
+
+    @Test
     @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
     public void vibrate_vendorEffectsWithoutPermission_doesNotVibrate() throws Exception {
         // Deny permission to vibrate with vendor effects
@@ -1622,7 +1854,12 @@
 
         vibrateAndWaitUntilFinished(service, vendorEffect, RINGTONE_ATTRS);
 
-        assertThat(fakeVibrator.getAllVendorEffects()).containsExactly(vendorEffect);
+        // Compare vendor data only, ignore scale applied by device settings in this test.
+        assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
+        assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().keySet())
+                .containsExactly("key");
+        assertThat(fakeVibrator.getAllVendorEffects().get(0).getVendorData().getString("key"))
+                .isEqualTo("value");
     }
 
     @Test
@@ -1765,42 +2002,8 @@
         assertThat(fakeVibrator.getAllVendorEffects()).hasSize(1);
         VibrationEffect.VendorEffect scaled = fakeVibrator.getAllVendorEffects().get(0);
         assertThat(scaled.getEffectStrength()).isEqualTo(VibrationEffect.EFFECT_STRENGTH_LIGHT);
-        assertThat(scaled.getLinearScale()).isEqualTo(0.4f);
-    }
-
-    @Test
-    public void vibrate_withPowerModeChange_cancelVibrationIfNotAllowed() throws Exception {
-        mockVibrators(1, 2);
-        VibratorManagerService service = createSystemReadyService();
-        vibrate(service,
-                CombinedVibration.startParallel()
-                        .addVibrator(1, VibrationEffect.createOneShot(1000, 100))
-                        .combine(),
-                HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait until vibration is triggered.
-        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
-        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
-        // Haptic feedback cancelled on low power mode.
-        assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-    }
-
-    @Test
-    public void vibrate_withSettingsChange_doNotCancelVibration() throws Exception {
-        mockVibrators(1);
-        VibratorManagerService service = createSystemReadyService();
-
-        vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
-        // VibrationThread will start this vibration async, so wait until vibration is triggered.
-        assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
-
-        service.updateServiceState();
-
-        // Vibration is not stopped nearly after updating service.
-        assertFalse(waitUntil(s -> !s.isVibrating(1), service, 50));
+        assertThat(scaled.getScale()).isAtMost(1); // Scale down or none if default is LOW
+        assertThat(scaled.getAdaptiveScale()).isEqualTo(0.4f);
     }
 
     @Test
@@ -2297,6 +2500,113 @@
     }
 
     @Test
+    public void onExternalVibration_thenDeniedAppOps_doNotCancelVibration() throws Throwable {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        VibratorManagerService service = createSystemReadyService();
+
+        IExternalVibrationController externalVibrationControllerMock =
+                mock(IExternalVibrationController.class);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+
+        assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+        when(mAppOpsManagerMock.checkAudioOpNoThrow(eq(AppOpsManager.OP_VIBRATE),
+                eq(AudioAttributes.USAGE_ALARM), anyInt(), anyString()))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+        service.mAppOpsChangeListener.onOpChanged(AppOpsManager.OP_VIBRATE, null);
+
+        verify(externalVibrationControllerMock, never()).mute();
+    }
+
+    @Test
+    public void onExternalVibration_thenPowerModeChanges_doNotCancelVibration() throws Exception {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        createSystemReadyService();
+
+        IExternalVibrationController externalVibrationControllerMock =
+                mock(IExternalVibrationController.class);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+
+        assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+        mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+        verify(externalVibrationControllerMock, never()).mute();
+    }
+
+    @Test
+    public void onExternalVibration_thenSettingsChange_doNotCancelVibration() throws Exception {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        VibratorManagerService service = createSystemReadyService();
+
+        IExternalVibrationController externalVibrationControllerMock =
+                mock(IExternalVibrationController.class);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+
+        assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
+        service.mVibrationSettings.mSettingObserver.onChange(false);
+        service.updateServiceState();
+
+        verify(externalVibrationControllerMock, never()).mute();
+    }
+
+    @Test
+    public void onExternalVibration_thenScreenTurnsOff_doNotCancelVibration() throws Throwable {
+        mockVibrators(1);
+        mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
+        VibratorManagerService service = createSystemReadyService();
+
+        IExternalVibrationController externalVibrationControllerMock =
+                mock(IExternalVibrationController.class);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+
+        assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+        service.mIntentReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_SCREEN_OFF));
+
+        verify(externalVibrationControllerMock, never()).mute();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(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);
+        VibratorManagerService service = createSystemReadyService();
+
+        IExternalVibrationController externalVibrationControllerMock =
+                mock(IExternalVibrationController.class);
+        ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME,
+                AUDIO_ALARM_ATTRS, externalVibrationControllerMock, mock(IBinder.class));
+        ExternalVibrationScale scale = mExternalVibratorService.onExternalVibrationStart(
+                externalVibration);
+
+        assertThat(scale.scaleLevel).isNotEqualTo(ExternalVibrationScale.ScaleLevel.SCALE_MUTE);
+
+        service.mIntentReceiver.onReceive(mContextSpy, new Intent(
+                BackgroundUserSoundNotifier.ACTION_MUTE_SOUND));
+
+        verify(externalVibrationControllerMock, never()).mute();
+    }
+
+    @Test
     public void frameworkStats_externalVibration_reportsAllMetrics() throws Exception {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
@@ -2323,7 +2633,7 @@
         assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL,
                 statsInfo.vibrationType);
         assertEquals(VibrationAttributes.USAGE_ALARM, statsInfo.usage);
-        assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), statsInfo.status);
+        assertEquals(Status.FINISHED.getProtoEnumValue(), statsInfo.status);
         assertTrue(statsInfo.totalDurationMillis > 0);
         assertTrue(
                 "Expected vibrator ON for at least 10ms, got " + statsInfo.vibratorOnMillis + "ms",
@@ -2355,7 +2665,7 @@
         assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
                 metrics.vibrationType);
         assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
-        assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+        assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
         assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
                 metrics.totalDurationMillis >= 20);
         assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
@@ -2408,7 +2718,7 @@
         assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED,
                 metrics.vibrationType);
         assertEquals(VibrationAttributes.USAGE_RINGTONE, metrics.usage);
-        assertEquals(Vibration.Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status);
+        assertEquals(Status.CANCELLED_BY_USER.getProtoEnumValue(), metrics.status);
         assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
                 metrics.totalDurationMillis >= 100);
         assertTrue("Vibrator ON duration was too low, " + metrics.vibratorOnMillis + "ms",
@@ -2469,7 +2779,7 @@
         assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
                 metrics.vibrationType);
         assertEquals(VibrationAttributes.USAGE_ALARM, metrics.usage);
-        assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+        assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
 
         // At least 4 effect/primitive played, 20ms each, plus configured fallback.
         assertTrue("Total duration was too low, " + metrics.totalDurationMillis + "ms",
@@ -2525,7 +2835,7 @@
         VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
         assertEquals(UID, touchMetrics.uid);
         assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
-        assertEquals(Vibration.Status.CANCELLED_SUPERSEDED.getProtoEnumValue(),
+        assertEquals(Status.CANCELLED_SUPERSEDED.getProtoEnumValue(),
                 touchMetrics.status);
         assertTrue(touchMetrics.endedBySameUid);
         assertEquals(VibrationAttributes.USAGE_ALARM, touchMetrics.endedByUsage);
@@ -2534,7 +2844,7 @@
         VibrationStats.StatsInfo alarmMetrics = argumentCaptor.getAllValues().get(1);
         assertEquals(UID, alarmMetrics.uid);
         assertEquals(VibrationAttributes.USAGE_ALARM, alarmMetrics.usage);
-        assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), alarmMetrics.status);
+        assertEquals(Status.FINISHED.getProtoEnumValue(), alarmMetrics.status);
         assertFalse(alarmMetrics.endedBySameUid);
         assertEquals(-1, alarmMetrics.endedByUsage);
         assertEquals(VibrationAttributes.USAGE_TOUCH, alarmMetrics.interruptedUsage);
@@ -2564,12 +2874,12 @@
         VibrationStats.StatsInfo touchMetrics = argumentCaptor.getAllValues().get(0);
         assertEquals(UID, touchMetrics.uid);
         assertEquals(VibrationAttributes.USAGE_TOUCH, touchMetrics.usage);
-        assertEquals(Vibration.Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status);
+        assertEquals(Status.IGNORED_FOR_POWER.getProtoEnumValue(), touchMetrics.status);
 
         VibrationStats.StatsInfo ringtoneMetrics = argumentCaptor.getAllValues().get(1);
         assertEquals(UID, ringtoneMetrics.uid);
         assertEquals(VibrationAttributes.USAGE_RINGTONE, ringtoneMetrics.usage);
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(),
+        assertEquals(Status.IGNORED_FOR_SETTINGS.getProtoEnumValue(),
                 ringtoneMetrics.status);
 
         for (VibrationStats.StatsInfo metrics : argumentCaptor.getAllValues()) {
@@ -2636,7 +2946,7 @@
         assertEquals(FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE,
                 metrics.vibrationType);
         assertEquals(VibrationAttributes.USAGE_NOTIFICATION, metrics.usage);
-        assertEquals(Vibration.Status.FINISHED.getProtoEnumValue(), metrics.status);
+        assertEquals(Status.FINISHED.getProtoEnumValue(), metrics.status);
         assertTrue(metrics.totalDurationMillis >= 20);
 
         // vibratorOnMillis accumulates both vibrators, it's 20 for each constant.
@@ -2755,6 +3065,21 @@
         return vib;
     }
 
+    private HalVibration performHapticFeedbackForInputDeviceAndWaitUntilFinished(
+            VibratorManagerService service, int constant, int inputDeviceId, int inputSource,
+            boolean always) throws InterruptedException {
+        HalVibration vib = service.performHapticFeedbackForInputDeviceInternal(UID,
+                Context.DEVICE_ID_DEFAULT, PACKAGE_NAME, constant, inputDeviceId, inputSource,
+                "some reason", service,
+                always ? HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING : 0 /* flags */,
+                0 /* privFlags */);
+        if (vib != null) {
+            vib.waitForEnd();
+        }
+
+        return vib;
+    }
+
     private HalVibration vibrateAndWaitUntilFinished(VibratorManagerService service,
             VibrationEffect effect, VibrationAttributes attrs) throws InterruptedException {
         return vibrateAndWaitUntilFinished(
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 96c3e97..031d1c2 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -140,13 +140,13 @@
 
         @Override
         public long performVendorEffect(Parcel vendorData, long strength, float scale,
-                long vibrationId) {
+                float adaptiveScale, long vibrationId) {
             if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
                 return 0;
             }
             PersistableBundle bundle = PersistableBundle.CREATOR.createFromParcel(vendorData);
             recordVendorEffect(vibrationId,
-                    new VibrationEffect.VendorEffect(bundle, (int) strength, scale));
+                    new VibrationEffect.VendorEffect(bundle, (int) strength, scale, adaptiveScale));
             applyLatency(mOnLatency);
             scheduleListener(mVendorEffectDuration, vibrationId);
             // HAL has unknown duration for vendor effects.
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 6ba2c70..289f8a9 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,6 +57,7 @@
         "service-permission.stubs.system_server",
         "androidx.test.runner",
         "androidx.test.rules",
+        "flickerlib",
         "junit-params",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
@@ -108,3 +109,35 @@
         ":OverlayTestApp",
     ],
 }
+
+test_module_config {
+    name: "WmTests_server_policy_Presubmit",
+    base: "WmTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.policy."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "WmTests_server_policy",
+    base: "WmTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.policy."],
+}
+
+test_module_config {
+    name: "WmTests_wm_utils_Presubmit",
+    base: "WmTests",
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    include_filters: ["com.android.server.wm.utils"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
new file mode 100644
index 0000000..8f3adba
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECENT_SYSTEM_UI;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
+import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
+import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
+
+import android.hardware.input.KeyGestureEvent;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.KeyEvent;
+
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.annotations.Keep;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@MediumTest
+@RunWith(JUnitParamsRunner.class)
+public class KeyGestureEventTests extends ShortcutKeyTestBase {
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
+    private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
+    private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
+    private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT);
+    private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT;
+    private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
+
+    @Keep
+    private static Object[][] shortcutTestArguments() {
+        // testName, testKeys, expectedKeyGestureType, expectedKey, expectedModifierState
+        return new Object[][]{
+                {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_H, META_ON},
+                {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME, KeyEvent.KEYCODE_ENTER,
+                        META_ON},
+                {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+                        KeyEvent.KEYCODE_HOME, 0},
+                {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+                        KeyEvent.KEYCODE_RECENT_APPS, 0},
+                {"Meta + Tab -> Open Overview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+                        META_ON},
+                {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, KeyEvent.KEYCODE_TAB,
+                        ALT_ON},
+                {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+                        KeyEvent.KEYCODE_BACK, 0},
+                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_ESCAPE,
+                        META_ON},
+                {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
+                        META_ON},
+                {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BACK, KeyEvent.KEYCODE_DEL, META_ON},
+                {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                        KeyEvent.KEYCODE_APP_SWITCH, 0},
+                {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                        KeyEvent.KEYCODE_ASSIST, 0},
+                {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
+                        META_ON},
+                {"VOICE_ASSIST key -> Launch Voice Assistant",
+                        new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+                        KeyEvent.KEYCODE_VOICE_ASSIST, 0},
+                {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+                        KeyEvent.KEYCODE_I, META_ON},
+                {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_N, META_ON},
+                {"NOTIFICATION key -> Toggle Notification Panel",
+                        new int[]{KeyEvent.KEYCODE_NOTIFICATION},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_NOTIFICATION,
+                        0},
+                {"Meta + Ctrl + S -> Take Screenshot",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
+                        META_ON | CTRL_ON},
+                {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+                        KeyEvent.KEYCODE_SLASH, META_ON},
+                {"BRIGHTNESS_UP key -> Increase Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+                        KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
+                {"BRIGHTNESS_DOWN key -> Decrease Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+                        KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
+                {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
+                {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_UP,
+                        KeyEvent.KEYCODE_VOLUME_UP, 0},
+                {"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_DOWN,
+                        KeyEvent.KEYCODE_VOLUME_DOWN, 0},
+                {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE,
+                        KeyEvent.KEYCODE_VOLUME_MUTE, 0},
+                {"ALL_APPS key -> Open App Drawer in Accessibility mode",
+                        new int[]{KeyEvent.KEYCODE_ALL_APPS},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyEvent.KEYCODE_ALL_APPS, 0},
+                {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+                        KeyEvent.KEYCODE_SEARCH, 0},
+                {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
+                        new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+                        KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
+                {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, META_KEY,
+                        META_ON},
+                {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
+                        META_ON | ALT_ON},
+                {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, META_KEY,
+                        META_ON | ALT_ON},
+                {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+                        KeyEvent.KEYCODE_CAPS_LOCK, 0},
+                {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE,
+                        0},
+                {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+                        KeyEvent.KEYCODE_DPAD_UP,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+                        KeyEvent.KEYCODE_DPAD_LEFT,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+                        KeyEvent.KEYCODE_DPAD_RIGHT,
+                        META_ON | CTRL_ON},
+                {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, KeyEvent.KEYCODE_L,
+                        META_ON},
+                {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES, KeyEvent.KEYCODE_N,
+                        META_ON | CTRL_ON},
+                {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER, KeyEvent.KEYCODE_POWER,
+                        0},
+                {"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_POWER,
+                        KeyEvent.KEYCODE_TV_POWER, 0},
+                {"SYSTEM_NAVIGATION_DOWN key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
+                        0},
+                {"SYSTEM_NAVIGATION_UP key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
+                        0},
+                {"SYSTEM_NAVIGATION_LEFT key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
+                        0},
+                {"SYSTEM_NAVIGATION_RIGHT key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SYSTEM_NAVIGATION,
+                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0},
+                {"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
+                {"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP,
+                        0},
+                {"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
+                {"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
+                        KeyEvent.KEYCODE_MEDIA_PLAY, 0},
+                {"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
+                        KeyEvent.KEYCODE_MEDIA_PAUSE, 0},
+                {"MEDIA_PLAY_PAUSE key -> Media Control",
+                        new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY,
+                        KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
+                {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+                        KeyEvent.KEYCODE_B, META_ON},
+                {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER,
+                        KeyEvent.KEYCODE_EXPLORER, 0},
+                {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+                        KeyEvent.KEYCODE_C, META_ON},
+                {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS,
+                        KeyEvent.KEYCODE_CONTACTS, 0},
+                {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+                        KeyEvent.KEYCODE_E, META_ON},
+                {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL,
+                        KeyEvent.KEYCODE_ENVELOPE, 0},
+                {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+                        KeyEvent.KEYCODE_K, META_ON},
+                {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR,
+                        KeyEvent.KEYCODE_CALENDAR, 0},
+                {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+                        KeyEvent.KEYCODE_P, META_ON},
+                {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC,
+                        KeyEvent.KEYCODE_MUSIC, 0},
+                {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+                        KeyEvent.KEYCODE_U, META_ON},
+                {"CALCULATOR key -> Launch Default Calculator",
+                        new int[]{KeyEvent.KEYCODE_CALCULATOR},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR,
+                        KeyEvent.KEYCODE_CALCULATOR, 0},
+                {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS,
+                        KeyEvent.KEYCODE_M, META_ON},
+                {"Meta + S -> Launch Default Messaging App",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_S},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING,
+                        KeyEvent.KEYCODE_S, META_ON},
+                {"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
+                        KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+                        KeyEvent.KEYCODE_DPAD_DOWN,
+                        META_ON | CTRL_ON}};
+    }
+
+    @Keep
+    private static Object[][] longPressOnHomeTestArguments() {
+        // testName, testKeys, longPressOnHomeBehavior, expectedKeyGestureType, expectedKey,
+        // expectedModifierState
+        return new Object[][]{
+                {"Long press HOME key -> Toggle Notification panel",
+                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_HOME, 0},
+                {"Long press META + ENTER -> Toggle Notification panel",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+                        LONG_PRESS_HOME_NOTIFICATION_PANEL,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_ENTER,
+                        META_ON},
+                {"Long press META + H -> Toggle Notification panel",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_H, META_ON},
+                {"Long press HOME key -> Launch assistant",
+                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ASSIST,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                        KeyEvent.KEYCODE_HOME, 0},
+                {"Long press META + ENTER -> Launch assistant",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ASSIST,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+                        KeyEvent.KEYCODE_ENTER, META_ON},
+                {"Long press META + H -> Launch assistant",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
+                        META_ON},
+                {"Long press HOME key -> Open App Drawer in Accessibility mode",
+                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyEvent.KEYCODE_HOME, 0},
+                {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyEvent.KEYCODE_ENTER, META_ON},
+                {"Long press META + H -> Open App Drawer in Accessibility mode",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_H},
+                        LONG_PRESS_HOME_ALL_APPS,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+                        KeyEvent.KEYCODE_H, META_ON}};
+    }
+
+    @Keep
+    private static Object[][] doubleTapOnHomeTestArguments() {
+        // testName, testKeys, doubleTapOnHomeBehavior, expectedKeyGestureType, expectedKey,
+        // expectedModifierState
+        return new Object[][]{
+                {"Double tap HOME -> Open App switcher",
+                        new int[]{KeyEvent.KEYCODE_HOME}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_HOME,
+                        0},
+                {"Double tap META + ENTER -> Open App switcher",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+                        DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+                        KeyEvent.KEYCODE_ENTER, META_ON},
+                {"Double tap META + H -> Open App switcher",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, KeyEvent.KEYCODE_H,
+                        META_ON}};
+    }
+
+    @Keep
+    private static Object[][] settingsKeyTestArguments() {
+        // testName, testKeys, settingsKeyBehavior, expectedKeyGestureType, expectedKey,
+        // expectedModifierState
+        return new Object[][]{
+                {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
+                        SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL,
+                        KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+                        KeyEvent.KEYCODE_SETTINGS, 0}};
+    }
+
+    @Before
+    public void setUp() {
+        setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
+        mPhoneWindowManager.overrideLaunchHome();
+        mPhoneWindowManager.overrideSearchKeyBehavior(
+                PhoneWindowManager.SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY);
+        mPhoneWindowManager.overrideEnableBugReportTrigger(true);
+        mPhoneWindowManager.overrideStatusBarManagerInternal();
+        mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.overrideSendBroadcast();
+        mPhoneWindowManager.overrideUserSetupComplete();
+        mPhoneWindowManager.setupAssistForLaunch();
+        mPhoneWindowManager.overrideTogglePanel();
+        mPhoneWindowManager.overrideInjectKeyEvent();
+    }
+
+    @Test
+    @Parameters(method = "shortcutTestArguments")
+    public void testShortcut(String testName, int[] testKeys,
+            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
+            int expectedModifierState) {
+        testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
+                expectedModifierState);
+    }
+
+    @Test
+    @Parameters(method = "longPressOnHomeTestArguments")
+    public void testLongPressOnHome(String testName, int[] testKeys, int longPressOnHomeBehavior,
+            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
+            int expectedModifierState) {
+        mPhoneWindowManager.overrideLongPressOnHomeBehavior(longPressOnHomeBehavior);
+        sendLongPressKeyCombination(testKeys);
+        mPhoneWindowManager.assertKeyGestureCompleted(
+                new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
+                "Failed while executing " + testName);
+    }
+
+    @Test
+    @Parameters(method = "doubleTapOnHomeTestArguments")
+    public void testDoubleTapOnHomeBehavior(String testName, int[] testKeys,
+            int doubleTapOnHomeBehavior,
+            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
+            int expectedModifierState) {
+        mPhoneWindowManager.overriderDoubleTapOnHomeBehavior(doubleTapOnHomeBehavior);
+        sendKeyCombination(testKeys, 0 /* duration */);
+        sendKeyCombination(testKeys, 0 /* duration */);
+        mPhoneWindowManager.assertKeyGestureCompleted(
+                new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
+                "Failed while executing " + testName);
+    }
+
+    @Test
+    @Parameters(method = "settingsKeyTestArguments")
+    public void testSettingsKey(String testName, int[] testKeys, int settingsKeyBehavior,
+            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
+            int expectedModifierState) {
+        mPhoneWindowManager.overrideSettingsKeyBehavior(settingsKeyBehavior);
+        testShortcutInternal(testName, testKeys, expectedKeyGestureType, expectedKey,
+                expectedModifierState);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    public void testBugreportShortcutPress() {
+        testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
+                new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
+                KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_DEL,
+                META_ON | CTRL_ON);
+    }
+
+    private void testShortcutInternal(String testName, int[] testKeys,
+            @KeyGestureEvent.KeyGestureType int expectedKeyGestureType, int expectedKey,
+            int expectedModifierState) {
+        sendKeyCombination(testKeys, 0 /* duration */);
+        mPhoneWindowManager.assertKeyGestureCompleted(
+                new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
+                "Failed while executing " + testName);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java
deleted file mode 100644
index e26f3e0..0000000
--- a/services/tests/wmtests/src/com/android/server/policy/KeyboardSystemShortcutTests.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECENT_SYSTEM_UI;
-import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
-import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
-import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_NOTIFICATION_PANEL;
-import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
-
-import android.hardware.input.KeyboardSystemShortcut;
-import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.view.KeyEvent;
-
-import androidx.test.filters.MediumTest;
-
-import com.android.internal.annotations.Keep;
-
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Presubmit
-@MediumTest
-@RunWith(JUnitParamsRunner.class)
-public class KeyboardSystemShortcutTests extends ShortcutKeyTestBase {
-
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule =
-            DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
-    private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
-    private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
-    private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT);
-    private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT;
-    private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
-    private static final int SHIFT_KEY = KeyEvent.KEYCODE_SHIFT_LEFT;
-    private static final int SHIFT_ON = MODIFIER.get(KeyEvent.KEYCODE_SHIFT_LEFT);
-
-    @Keep
-    private static Object[][] shortcutTestArguments() {
-        // testName, testKeys, expectedSystemShortcut, expectedKey, expectedModifierState
-        return new Object[][]{
-                {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME, KeyEvent.KEYCODE_H, META_ON},
-                {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME, KeyEvent.KEYCODE_ENTER,
-                        META_ON},
-                {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME,
-                        KeyEvent.KEYCODE_HOME, 0},
-                {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS,
-                        KeyEvent.KEYCODE_RECENT_APPS, 0},
-                {"Meta + Tab -> Open Overview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS, KeyEvent.KEYCODE_TAB,
-                        META_ON},
-                {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_RECENT_APPS, KeyEvent.KEYCODE_TAB,
-                        ALT_ON},
-                {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK,
-                        KeyEvent.KEYCODE_BACK, 0},
-                {"Meta + Escape -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_ESCAPE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_ESCAPE,
-                        META_ON},
-                {"Meta + Left arrow -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_DPAD_LEFT,
-                        META_ON},
-                {"Meta + Del -> Go back", new int[]{META_KEY, KeyEvent.KEYCODE_DEL},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BACK, KeyEvent.KEYCODE_DEL, META_ON},
-                {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH,
-                        KeyEvent.KEYCODE_APP_SWITCH, 0},
-                {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
-                        KeyEvent.KEYCODE_ASSIST, 0},
-                {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A,
-                        META_ON},
-                {"VOICE_ASSIST key -> Launch Voice Assistant",
-                        new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_VOICE_ASSISTANT,
-                        KeyEvent.KEYCODE_VOICE_ASSIST, 0},
-                {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SYSTEM_SETTINGS,
-                        KeyEvent.KEYCODE_I, META_ON},
-                {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_N, META_ON},
-                {"NOTIFICATION key -> Toggle Notification Panel",
-                        new int[]{KeyEvent.KEYCODE_NOTIFICATION},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_NOTIFICATION,
-                        0},
-                {"Meta + Ctrl + S -> Take Screenshot",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TAKE_SCREENSHOT, KeyEvent.KEYCODE_S,
-                        META_ON | CTRL_ON},
-                {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_SHORTCUT_HELPER,
-                        KeyEvent.KEYCODE_SLASH, META_ON},
-                {"BRIGHTNESS_UP key -> Increase Brightness",
-                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_UP,
-                        KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
-                {"BRIGHTNESS_DOWN key -> Decrease Brightness",
-                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_BRIGHTNESS_DOWN,
-                        KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
-                {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_UP,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
-                {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_DOWN,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
-                {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
-                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_KEYBOARD_BACKLIGHT_TOGGLE,
-                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
-                {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_UP,
-                        KeyEvent.KEYCODE_VOLUME_UP, 0},
-                {"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_DOWN,
-                        KeyEvent.KEYCODE_VOLUME_DOWN, 0},
-                {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_VOLUME_MUTE,
-                        KeyEvent.KEYCODE_VOLUME_MUTE, 0},
-                {"ALL_APPS key -> Open App Drawer in Accessibility mode",
-                        new int[]{KeyEvent.KEYCODE_ALL_APPS},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
-                        KeyEvent.KEYCODE_ALL_APPS, 0},
-                {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_SEARCH,
-                        KeyEvent.KEYCODE_SEARCH, 0},
-                {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
-                        new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LANGUAGE_SWITCH,
-                        KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
-                {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS, META_KEY,
-                        META_ON},
-                {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK, ALT_KEY,
-                        META_ON | ALT_ON},
-                {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK, META_KEY,
-                        META_ON | ALT_ON},
-                {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_CAPS_LOCK,
-                        KeyEvent.KEYCODE_CAPS_LOCK, 0},
-                {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE,
-                        0},
-                {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_MULTI_WINDOW_NAVIGATION,
-                        KeyEvent.KEYCODE_DPAD_UP,
-                        META_ON | CTRL_ON},
-                {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
-                        KeyEvent.KEYCODE_DPAD_LEFT,
-                        META_ON | CTRL_ON},
-                {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SPLIT_SCREEN_NAVIGATION,
-                        KeyEvent.KEYCODE_DPAD_RIGHT,
-                        META_ON | CTRL_ON},
-                {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LOCK_SCREEN, KeyEvent.KEYCODE_L,
-                        META_ON},
-                {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_OPEN_NOTES, KeyEvent.KEYCODE_N,
-                        META_ON | CTRL_ON},
-                {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER, KeyEvent.KEYCODE_POWER,
-                        0},
-                {"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_POWER,
-                        KeyEvent.KEYCODE_TV_POWER, 0},
-                {"SYSTEM_NAVIGATION_DOWN key -> System Navigation",
-                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
-                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
-                        0},
-                {"SYSTEM_NAVIGATION_UP key -> System Navigation",
-                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
-                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
-                        0},
-                {"SYSTEM_NAVIGATION_LEFT key -> System Navigation",
-                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
-                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
-                        0},
-                {"SYSTEM_NAVIGATION_RIGHT key -> System Navigation",
-                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SYSTEM_NAVIGATION,
-                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0},
-                {"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
-                {"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP,
-                        0},
-                {"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
-                {"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
-                        KeyEvent.KEYCODE_MEDIA_PLAY, 0},
-                {"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
-                        KeyEvent.KEYCODE_MEDIA_PAUSE, 0},
-                {"MEDIA_PLAY_PAUSE key -> Media Control",
-                        new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_MEDIA_KEY,
-                        KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
-                {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
-                        KeyEvent.KEYCODE_B, META_ON},
-                {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_BROWSER,
-                        KeyEvent.KEYCODE_EXPLORER, 0},
-                {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
-                        KeyEvent.KEYCODE_C, META_ON},
-                {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CONTACTS,
-                        KeyEvent.KEYCODE_CONTACTS, 0},
-                {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
-                        KeyEvent.KEYCODE_E, META_ON},
-                {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_EMAIL,
-                        KeyEvent.KEYCODE_ENVELOPE, 0},
-                {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
-                        KeyEvent.KEYCODE_K, META_ON},
-                {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALENDAR,
-                        KeyEvent.KEYCODE_CALENDAR, 0},
-                {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
-                        KeyEvent.KEYCODE_P, META_ON},
-                {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MUSIC,
-                        KeyEvent.KEYCODE_MUSIC, 0},
-                {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
-                        KeyEvent.KEYCODE_U, META_ON},
-                {"CALCULATOR key -> Launch Default Calculator",
-                        new int[]{KeyEvent.KEYCODE_CALCULATOR},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_CALCULATOR,
-                        KeyEvent.KEYCODE_CALCULATOR, 0},
-                {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MAPS,
-                        KeyEvent.KEYCODE_M, META_ON},
-                {"Meta + S -> Launch Default Messaging App",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_S},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_DEFAULT_MESSAGING,
-                        KeyEvent.KEYCODE_S, META_ON},
-                {"Meta + Ctrl + DPAD_DOWN -> Enter desktop mode",
-                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_DOWN},
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_DESKTOP_MODE,
-                        KeyEvent.KEYCODE_DPAD_DOWN,
-                        META_ON | CTRL_ON}};
-    }
-
-    @Keep
-    private static Object[][] longPressOnHomeTestArguments() {
-        // testName, testKeys, longPressOnHomeBehavior, expectedSystemShortcut, expectedKey,
-        // expectedModifierState
-        return new Object[][]{
-                {"Long press HOME key -> Toggle Notification panel",
-                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Toggle Notification panel",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        LONG_PRESS_HOME_NOTIFICATION_PANEL,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_ENTER,
-                        META_ON},
-                {"Long press META + H -> Toggle Notification panel",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_NOTIFICATION_PANEL,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_H, META_ON},
-                {"Long press HOME key -> Launch assistant",
-                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ASSIST,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
-                        KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Launch assistant",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ASSIST,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Long press META + H -> Launch assistant",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
-                        META_ON},
-                {"Long press HOME key -> Open App Drawer in Accessibility mode",
-                        new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
-                        KeyEvent.KEYCODE_HOME, 0},
-                {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Long press META + H -> Open App Drawer in Accessibility mode",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H},
-                        LONG_PRESS_HOME_ALL_APPS,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_ACCESSIBILITY_ALL_APPS,
-                        KeyEvent.KEYCODE_H, META_ON}};
-    }
-
-    @Keep
-    private static Object[][] doubleTapOnHomeTestArguments() {
-        // testName, testKeys, doubleTapOnHomeBehavior, expectedSystemShortcut, expectedKey,
-        // expectedModifierState
-        return new Object[][]{
-                {"Double tap HOME -> Open App switcher",
-                        new int[]{KeyEvent.KEYCODE_HOME}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH, KeyEvent.KEYCODE_HOME,
-                        0},
-                {"Double tap META + ENTER -> Open App switcher",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
-                        DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH,
-                        KeyEvent.KEYCODE_ENTER, META_ON},
-                {"Double tap META + H -> Open App switcher",
-                        new int[]{META_KEY, KeyEvent.KEYCODE_H}, DOUBLE_TAP_HOME_RECENT_SYSTEM_UI,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_APP_SWITCH, KeyEvent.KEYCODE_H,
-                        META_ON}};
-    }
-
-    @Keep
-    private static Object[][] settingsKeyTestArguments() {
-        // testName, testKeys, settingsKeyBehavior, expectedSystemShortcut, expectedKey,
-        // expectedModifierState
-        return new Object[][]{
-                {"SETTINGS key -> Toggle Notification panel", new int[]{KeyEvent.KEYCODE_SETTINGS},
-                        SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL,
-                        KeyboardSystemShortcut.SYSTEM_SHORTCUT_TOGGLE_NOTIFICATION_PANEL,
-                        KeyEvent.KEYCODE_SETTINGS, 0}};
-    }
-
-    @Before
-    public void setUp() {
-        setUpPhoneWindowManager(/*supportSettingsUpdate*/ true);
-        mPhoneWindowManager.overrideLaunchHome();
-        mPhoneWindowManager.overrideSearchKeyBehavior(
-                PhoneWindowManager.SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY);
-        mPhoneWindowManager.overrideEnableBugReportTrigger(true);
-        mPhoneWindowManager.overrideStatusBarManagerInternal();
-        mPhoneWindowManager.overrideStartActivity();
-        mPhoneWindowManager.overrideSendBroadcast();
-        mPhoneWindowManager.overrideUserSetupComplete();
-        mPhoneWindowManager.setupAssistForLaunch();
-        mPhoneWindowManager.overrideTogglePanel();
-        mPhoneWindowManager.overrideInjectKeyEvent();
-    }
-
-    @Test
-    @Parameters(method = "shortcutTestArguments")
-    public void testShortcut(String testName, int[] testKeys,
-            @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
-            int expectedModifierState) {
-        testShortcutInternal(testName, testKeys, expectedSystemShortcut, expectedKey,
-                expectedModifierState);
-    }
-
-    @Test
-    @Parameters(method = "longPressOnHomeTestArguments")
-    public void testLongPressOnHome(String testName, int[] testKeys, int longPressOnHomeBehavior,
-            @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
-            int expectedModifierState) {
-        mPhoneWindowManager.overrideLongPressOnHomeBehavior(longPressOnHomeBehavior);
-        sendLongPressKeyCombination(testKeys);
-        mPhoneWindowManager.assertKeyboardShortcutTriggered(
-                new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
-                "Failed while executing " + testName);
-    }
-
-    @Test
-    @Parameters(method = "doubleTapOnHomeTestArguments")
-    public void testDoubleTapOnHomeBehavior(String testName, int[] testKeys,
-            int doubleTapOnHomeBehavior,
-            @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
-            int expectedModifierState) {
-        mPhoneWindowManager.overriderDoubleTapOnHomeBehavior(doubleTapOnHomeBehavior);
-        sendKeyCombination(testKeys, 0 /* duration */);
-        sendKeyCombination(testKeys, 0 /* duration */);
-        mPhoneWindowManager.assertKeyboardShortcutTriggered(
-                new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
-                "Failed while executing " + testName);
-    }
-
-    @Test
-    @Parameters(method = "settingsKeyTestArguments")
-    public void testSettingsKey(String testName, int[] testKeys, int settingsKeyBehavior,
-            @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
-            int expectedModifierState) {
-        mPhoneWindowManager.overrideSettingsKeyBehavior(settingsKeyBehavior);
-        testShortcutInternal(testName, testKeys, expectedSystemShortcut, expectedKey,
-                expectedModifierState);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
-    public void testBugreportShortcutPress() {
-        testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
-                new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
-                KeyboardSystemShortcut.SYSTEM_SHORTCUT_TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_DEL,
-                META_ON | CTRL_ON);
-    }
-
-    private void testShortcutInternal(String testName, int[] testKeys,
-            @KeyboardSystemShortcut.SystemShortcut int expectedSystemShortcut, int expectedKey,
-            int expectedModifierState) {
-        sendKeyCombination(testKeys, 0 /* duration */);
-        mPhoneWindowManager.assertKeyboardShortcutTriggered(
-                new int[]{expectedKey}, expectedModifierState, expectedSystemShortcut,
-                "Failed while executing " + testName);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java b/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java
new file mode 100644
index 0000000..b979335
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java
@@ -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.server.policy;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.view.KeyEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.policy.KeyInterceptionInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Testing {@link PhoneWindowManager} functionality of letting app intercepting key events
+ * containing META.
+ */
+@SmallTest
+public class MetaKeyEventsInterceptionTests extends ShortcutKeyTestBase {
+
+    private static final List<KeyEvent> META_KEY_EVENTS = Arrays.asList(
+            new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
+            new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
+            new KeyEvent(/* downTime= */ 0, /* eventTime= */
+                    0, /* action= */ 0, /* code= */ 0, /* repeat= */ 0,
+                    /* metaState= */ KeyEvent.META_META_ON));
+
+    @Before
+    public void setUp() {
+        setUpPhoneWindowManager();
+    }
+
+    @Test
+    public void doesntInterceptMetaKeyEvents_whenWindowAskedForIt() {
+        mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ true);
+        setWindowKeyInterceptionWithPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS);
+
+        META_KEY_EVENTS.forEach(keyEvent -> {
+            assertKeyInterceptionResult(keyEvent, /* intercepted= */ false);
+        });
+    }
+
+    @Test
+    public void interceptsMetaKeyEvents_whenWindowDoesntHaveFlagSet() {
+        mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ true);
+        setWindowKeyInterceptionWithPrivateFlags(0);
+
+        META_KEY_EVENTS.forEach(keyEvent -> {
+            assertKeyInterceptionResult(keyEvent, /* intercepted= */ true);
+        });
+    }
+
+    @Test
+    public void interceptsMetaKeyEvents_whenWindowDoesntHavePermission() {
+        mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ false);
+        setWindowKeyInterceptionWithPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS);
+
+        META_KEY_EVENTS.forEach(keyEvent -> {
+            assertKeyInterceptionResult(keyEvent, /* intercepted= */ true);
+        });
+    }
+
+    private void setWindowKeyInterceptionWithPrivateFlags(int privateFlags) {
+        KeyInterceptionInfo info = new KeyInterceptionInfo(
+                WindowManager.LayoutParams.TYPE_APPLICATION, privateFlags, "title", 0);
+        mPhoneWindowManager.overrideWindowKeyInterceptionInfo(info);
+    }
+
+    private void assertKeyInterceptionResult(KeyEvent keyEvent, boolean intercepted) {
+        long result = mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent);
+        int expected = intercepted ? -1 : 0;
+        assertThat(result).isEqualTo(expected);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
index d147325..0575d98 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutManagerTests.java
@@ -41,6 +41,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
 import android.view.KeyboardShortcutInfo;
@@ -50,6 +52,8 @@
 import com.android.internal.R;
 
 import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Collections;
@@ -62,7 +66,13 @@
  */
 
 @SmallTest
+@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
 public class ModifierShortcutManagerTests {
+
+    @ClassRule public static final SetFlagsRule.ClassRule SET_FLAGS_CLASS_RULE =
+            new SetFlagsRule.ClassRule();
+    @Rule public final SetFlagsRule mSetFlagsRule = SET_FLAGS_CLASS_RULE.createSetFlagsRule();
+
     private ModifierShortcutManager mModifierShortcutManager;
     private Handler mHandler;
     private Context mContext;
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 71f90a2..43171f8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -44,26 +44,21 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 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.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 @Presubmit
 @SmallTest
+@EnableFlags(com.android.hardware.input.Flags.FLAG_MODIFIER_SHORTCUT_MANAGER_REFACTOR)
 public class ModifierShortcutTests extends ShortcutKeyTestBase {
 
-    @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
     private static final SparseArray<String> INTENT_SHORTCUTS =  new SparseArray<>();
     private static final SparseArray<String> ROLE_SHORTCUTS =  new SparseArray<>();
     static {
@@ -258,7 +253,7 @@
      * META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled.
      */
     @Test
-    @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
     public void testTakeBugReport_flagEnabled() throws RemoteException {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
         mPhoneWindowManager.assertTakeBugreport(true);
@@ -268,7 +263,7 @@
      * META+CTRL+BACKSPACE for taking a bugreport does nothing when the flag is disabledd.
      */
     @Test
-    @RequiresFlagsDisabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+    @DisableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
     public void testTakeBugReport_flagDisabled() throws RemoteException {
         sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_DEL}, 0);
         mPhoneWindowManager.assertTakeBugreport(false);
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 07934ea..e694c0b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -188,7 +188,7 @@
                 .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
         int[] outAppOp = new int[1];
         assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
-                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
         assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
     }
 
@@ -198,7 +198,7 @@
                 .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
         int[] outAppOp = new int[1];
         assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
-                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
         assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_CREATE_ACCESSIBILITY_OVERLAY);
     }
 
@@ -208,7 +208,7 @@
                 .FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
         int[] outAppOp = new int[1];
         assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
-                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp));
+                /* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
         assertThat(outAppOp[0]).isEqualTo(AppOpsManager.OP_NONE);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index f9b5c2a..79c7ac1 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -52,6 +52,7 @@
 import static org.mockito.Mockito.description;
 import static org.mockito.Mockito.mockingDetails;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.withSettings;
 
 import android.app.ActivityManagerInternal;
@@ -70,7 +71,6 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardSystemShortcut;
 import android.media.AudioManagerInternal;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -614,6 +614,10 @@
                 .when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt());
     }
 
+    void overrideWindowKeyInterceptionInfo(KeyInterceptionInfo info) {
+        when(mWindowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info);
+    }
+
     void overrideKeyEventPolicyFlags(int flags) {
         mKeyEventPolicyFlags = flags;
     }
@@ -804,11 +808,11 @@
         Assert.assertEquals(targetActivity, intentCaptor.getValue().getComponent());
     }
 
-    void assertKeyboardShortcutTriggered(int[] keycodes, int modifierState, int systemShortcut,
+    void assertKeyGestureCompleted(int[] keycodes, int modifierState, int gestureType,
             String errorMsg) {
         mTestLooper.dispatchAll();
-        verify(mInputManagerInternal, description(errorMsg)).notifyKeyboardShortcutTriggered(
-                anyInt(), eq(keycodes), eq(modifierState), eq(systemShortcut));
+        verify(mInputManagerInternal, description(errorMsg)).notifyKeyGestureCompleted(
+                anyInt(), eq(keycodes), eq(modifierState), eq(gestureType));
     }
 
     void assertSwitchToTask(int persistentId) throws RemoteException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java
index 3b280d9..50c2b85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java
@@ -33,6 +33,8 @@
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.window.WindowInfosListenerForTest;
+import android.window.WindowInfosListenerForTest.DisplayInfo;
+import android.window.WindowInfosListenerForTest.WindowInfo;
 
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -50,7 +52,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
+import java.util.function.BiConsumer;
 
 /**
  * Internal variant of {@link android.server.wm.window.ActivityRecordInputSinkTests}.
@@ -154,14 +156,15 @@
     private void waitForOverlayApp() throws InterruptedException {
         final var listenerHost = new WindowInfosListenerForTest();
         final var latch = new CountDownLatch(1);
-        final Consumer<List<WindowInfosListenerForTest.WindowInfo>> listener = windowInfos -> {
-            final boolean inputSinkReady = windowInfos.stream().anyMatch(info ->
-                    info.isVisible
-                            && info.name.contains("ActivityRecordInputSink " + OVERLAY_ACTIVITY));
-            if (inputSinkReady) {
-                latch.countDown();
-            }
-        };
+        final BiConsumer<List<WindowInfo>, List<DisplayInfo>> listener =
+            (windowInfos, displayInfos) -> {
+                final boolean inputSinkReady = windowInfos.stream().anyMatch(
+                    info -> info.isVisible
+                        && info.name.contains("ActivityRecordInputSink " + OVERLAY_ACTIVITY));
+                if (inputSinkReady) {
+                    latch.countDown();
+                }
+            };
 
         listenerHost.addWindowInfosListener(listener);
         try {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ea825c7..1e035da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -705,7 +705,7 @@
         assertEquals(ORIENTATION_PORTRAIT, activity.getConfiguration().orientation);
 
         // Clear size compat.
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         mDisplayContent.sendNewConfiguration();
 
@@ -1629,10 +1629,10 @@
     @Test
     public void testCompleteResume_updateCompatDisplayInsets() {
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        doReturn(true).when(activity).shouldCreateCompatDisplayInsets();
+        doReturn(true).when(activity).shouldCreateAppCompatDisplayInsets();
         activity.setState(RESUMED, "test");
         activity.completeResumeLocked();
-        assertNotNull(activity.getCompatDisplayInsets());
+        assertNotNull(activity.getAppCompatDisplayInsets());
     }
 
     /**
@@ -1723,10 +1723,12 @@
     @Test
     public void testDestroyImmediately_hadApp_notFinishing() {
         final ActivityRecord activity = createActivityWithTask();
+        activity.idle = true;
         activity.finishing = false;
         activity.destroyImmediately("test");
 
         assertEquals(DESTROYED, activity.getState());
+        assertFalse(activity.idle);
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
index 14fbbe4..cb17f35f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -212,7 +212,6 @@
                 .build()
                 .getTopMostActivity();
 
-        spyOn(mActivity.mLetterboxUiController);
         spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
         doReturn(true).when(mActivity).inFreeformWindowingMode();
         doReturn(true).when(mActivity.mAppCompatController
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 4f94704..c706d52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -1047,6 +1047,7 @@
         info.packageName = packageName;
         WindowProcessController wpc = new WindowProcessController(
                 mAtm, info, packageName, 0, userId, null, mMockListener);
+        mAtm.mInternal.preBindApplication(wpc, info);
         wpc.setThread(mock(IApplicationThread.class));
         return wpc;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
index a745724..a7a08b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java
@@ -31,10 +31,10 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
-import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.UserMinAspectRatio;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.view.Surface;
@@ -134,11 +134,6 @@
                 isUnresizable);
     }
 
-    void configureTopActivityIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
-        mActivityStack.top().mDisplayContent
-                .setIgnoreOrientationRequest(ignoreOrientationRequest);
-    }
-
     void configureUnresizableTopActivity(@ActivityInfo.ScreenOrientation int screenOrientation) {
         configureTopActivity(/* minAspect */ -1, /* maxAspect */ -1, screenOrientation,
                 /* isUnresizable */ true);
@@ -176,10 +171,14 @@
         return mActivityStack.getFromTop(fromTop);
     }
 
-    void setTaskWindowingMode(@WindowConfiguration.WindowingMode int windowingMode) {
+    void setTaskWindowingMode(@WindowingMode int windowingMode) {
         mTaskStack.top().setWindowingMode(windowingMode);
     }
 
+    void setTaskDisplayAreaWindowingMode(@WindowingMode int windowingMode) {
+        mTaskStack.top().getDisplayArea().setWindowingMode(windowingMode);
+    }
+
     void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
         doReturn(enabled).when(mActivityStack.top().mAppCompatController
                 .getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
@@ -210,7 +209,7 @@
     }
 
     void setShouldCreateCompatDisplayInsets(boolean enabled) {
-        doReturn(enabled).when(mActivityStack.top()).shouldCreateCompatDisplayInsets();
+        doReturn(enabled).when(mActivityStack.top()).shouldCreateAppCompatDisplayInsets();
     }
 
     void setTopActivityInSizeCompatMode(boolean inScm) {
@@ -222,10 +221,14 @@
                 .getAppCompatAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
     }
 
-    void setGetUserMinAspectRatioOverrideCode(@PackageManager.UserMinAspectRatio int orientation) {
-        doReturn(orientation).when(mActivityStack.top()
-                .mAppCompatController.getAppCompatAspectRatioOverrides())
-                .getUserMinAspectRatioOverrideCode();
+    void setGetUserMinAspectRatioOverrideCode(@UserMinAspectRatio int overrideCode) {
+        doReturn(overrideCode).when(mActivityStack.top().mAppCompatController
+                .getAppCompatAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
+    }
+
+    void setGetUserMinAspectRatioOverrideValue(float overrideValue) {
+        doReturn(overrideValue).when(mActivityStack.top().mAppCompatController
+                .getAppCompatAspectRatioOverrides()).getUserMinAspectRatio();
     }
 
     void setIgnoreOrientationRequest(boolean enabled) {
@@ -233,14 +236,25 @@
     }
 
     void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
-        doReturn(inMultiWindowMode).when(mTaskStack.top())
-                .inMultiWindowMode();
+        doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode();
     }
 
     void setTopActivityAsEmbedded(boolean embedded) {
         doReturn(embedded).when(mActivityStack.top()).isEmbedded();
     }
 
+    void setTopActivityVisible(boolean isVisible) {
+        doReturn(isVisible).when(mActivityStack.top()).isVisible();
+    }
+
+    void setTopActivityVisibleRequested(boolean isVisibleRequested) {
+        doReturn(isVisibleRequested).when(mActivityStack.top()).isVisibleRequested();
+    }
+
+    void setTopActivityFillsParent(boolean fillsParent) {
+        doReturn(fillsParent).when(mActivityStack.top()).fillsParent();
+    }
+
     void setTopActivityInMultiWindowMode(boolean multiWindowMode) {
         doReturn(multiWindowMode).when(mActivityStack.top()).inMultiWindowMode();
         if (multiWindowMode) {
@@ -420,7 +434,6 @@
      */
     @CallSuper
     void onPostActivityCreation(@NonNull ActivityRecord activity) {
-        spyOn(activity.mLetterboxUiController);
         if (mOnPostActivityCreation != null) {
             mOnPostActivityCreation.accept(activity);
         }
@@ -486,7 +499,7 @@
             activity.setRequestedOrientation(screenOrientation);
         }
         // Make sure to use the provided configuration to construct the size compat fields.
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         // Make sure the display configuration reflects the change of activity.
         if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index 84ffcb8..ba2a733 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -374,7 +374,6 @@
      * Runs a test scenario providing a Robot.
      */
     void runTestScenario(@NonNull Consumer<CameraOverridesRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final CameraOverridesRobotTest robot = new CameraOverridesRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
index c42228d..2ae23f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraPolicyTest.java
@@ -20,6 +20,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 
 import android.compat.testing.PlatformCompatChangeRule;
@@ -29,7 +31,6 @@
 
 import androidx.annotation.NonNull;
 
-import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -53,18 +54,28 @@
     @Test
     public void testDisplayRotationCompatPolicy_presentWhenEnabled() {
         runTestScenario((robot) -> {
-            robot.conf().enableCameraCompatTreatmentAtBuildTime(true);
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasDisplayRotationCompatPolicy(true);
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
         });
     }
 
     @Test
     public void testDisplayRotationCompatPolicy_notPresentWhenDisabled() {
         runTestScenario((robot) -> {
-            robot.conf().enableCameraCompatTreatmentAtBuildTime(false);
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ false);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasDisplayRotationCompatPolicy(false);
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ false);
+        });
+    }
+
+    @Test
+    public void testDisplayRotationCompatPolicy_startedWhenEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+            robot.checkTopActivityDisplayRotationCompatPolicyIsRunning();
         });
     }
 
@@ -72,9 +83,9 @@
     @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
     public void testCameraCompatFreeformPolicy_presentWhenEnabledAndDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(true);
+            robot.allowEnterDesktopMode(/* isAllowed= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasCameraCompatFreeformPolicy(true);
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
         });
     }
 
@@ -82,9 +93,9 @@
     @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(false);
+            robot.allowEnterDesktopMode(/* isAllowed= */ false);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
         });
     }
 
@@ -92,9 +103,9 @@
     @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlag() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(true);
+            robot.allowEnterDesktopMode(/* isAllowed= */ true);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
         });
     }
 
@@ -102,19 +113,86 @@
     @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
     public void testCameraCompatFreeformPolicy_notPresentWhenNoFlagAndNoDW() {
         runTestScenario((robot) -> {
-            robot.allowEnterDesktopMode(false);
+            robot.allowEnterDesktopMode(/* isAllowed= */ false);
             robot.activity().createActivityWithComponentInNewTaskAndDisplay();
-            robot.checkTopActivityHasCameraCompatFreeformPolicy(false);
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
+        });
+    }
+
+    @Test
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testCameraCompatFreeformPolicy_startedWhenEnabledAndDW() {
+        runTestScenario((robot) -> {
+            robot.allowEnterDesktopMode(/* isAllowed= */ true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+            robot.checkTopActivityCameraCompatFreeformPolicyIsRunning();
+        });
+    }
+
+    @Test
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testCameraStateManager_existsWhenCameraCompatFreeformExists() {
+        runTestScenario((robot) -> {
+            robot.allowEnterDesktopMode(true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+            robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+        });
+    }
+
+    @Test
+    @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testCameraStateManager_startedWhenCameraCompatFreeformExists() {
+        runTestScenario((robot) -> {
+            robot.allowEnterDesktopMode(true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ true);
+            robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+            robot.checkTopActivityCameraStateMonitorIsRunning();
+        });
+    }
+
+    @Test
+    public void testCameraStateManager_existsWhenDisplayRotationCompatPolicyExists() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+            robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+        });
+    }
+
+    @Test
+    public void testCameraStateManager_startedWhenDisplayRotationCompatPolicyExists() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ true);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ true);
+            robot.checkTopActivityHasCameraStateMonitor(/* exists= */ true);
+            robot.checkTopActivityCameraStateMonitorIsRunning();
+        });
+    }
+
+    @Test
+    @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM)
+    public void testCameraStateManager_doesNotExistWhenNoPolicyExists() {
+        runTestScenario((robot) -> {
+            robot.conf().enableCameraCompatTreatmentAtBuildTime(/* enabled= */ false);
+            robot.activity().createActivityWithComponentInNewTaskAndDisplay();
+            robot.checkTopActivityHasDisplayRotationCompatPolicy(/* exists= */ false);
+            robot.checkTopActivityHasCameraCompatFreeformPolicy(/* exists= */ false);
+            robot.checkTopActivityHasCameraStateMonitor(/* exists= */ false);
         });
     }
 
     /**
      * Runs a test scenario providing a Robot.
      */
-    void runTestScenario(@NonNull Consumer<DisplayRotationPolicyRobotTest> consumer) {
+    void runTestScenario(@NonNull Consumer<AppCompatCameraPolicyRobotTest> consumer) {
         spyOn(mWm.mAppCompatConfiguration);
-        final DisplayRotationPolicyRobotTest robot =
-                new DisplayRotationPolicyRobotTest(mWm, mAtm, mSupervisor);
+        final AppCompatCameraPolicyRobotTest robot =
+                new AppCompatCameraPolicyRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
     }
 
@@ -142,9 +220,8 @@
         });
     }
 
-    private static class DisplayRotationPolicyRobotTest extends AppCompatRobotBase {
-
-        DisplayRotationPolicyRobotTest(@NonNull WindowManagerService wm,
+    private static class AppCompatCameraPolicyRobotTest extends AppCompatRobotBase {
+        AppCompatCameraPolicyRobotTest(@NonNull WindowManagerService wm,
                 @NonNull ActivityTaskManagerService atm,
                 @NonNull ActivityTaskSupervisor supervisor) {
             super(wm, atm, supervisor);
@@ -157,17 +234,37 @@
         }
 
         void checkTopActivityHasDisplayRotationCompatPolicy(boolean exists) {
-            Assert.assertEquals(exists, activity().top().mDisplayContent
-                    .mAppCompatCameraPolicy.hasDisplayRotationCompatPolicy());
+            assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .hasDisplayRotationCompatPolicy());
         }
 
         void checkTopActivityHasCameraCompatFreeformPolicy(boolean exists) {
-            Assert.assertEquals(exists, activity().top().mDisplayContent
-                    .mAppCompatCameraPolicy.hasCameraCompatFreeformPolicy());
+            assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .hasCameraCompatFreeformPolicy());
+        }
+
+        void checkTopActivityHasCameraStateMonitor(boolean exists) {
+            assertEquals(exists, activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .hasCameraStateMonitor());
+        }
+
+        void checkTopActivityDisplayRotationCompatPolicyIsRunning() {
+            assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .mDisplayRotationCompatPolicy.isRunning());
+        }
+
+        void checkTopActivityCameraCompatFreeformPolicyIsRunning() {
+            assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .mCameraCompatFreeformPolicy.isRunning());
+        }
+
+        void checkTopActivityCameraStateMonitorIsRunning() {
+            assertTrue(activity().top().mDisplayContent.mAppCompatCameraPolicy
+                    .mCameraStateMonitor.isRunning());
         }
 
         void checkIsCameraCompatTreatmentActiveForTopActivity(boolean active) {
-            Assert.assertEquals(getTopAppCompatCameraPolicy()
+            assertEquals(getTopAppCompatCameraPolicy()
                     .isTreatmentEnabledForActivity(activity().top()), active);
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
index 40a5347..05f6ed6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java
@@ -26,6 +26,8 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
 /**
  * Robot implementation for {@link AppCompatConfiguration}.
  */
@@ -52,6 +54,11 @@
         doReturn(enabled).when(mAppCompatConfiguration).isCameraCompatTreatmentEnabled();
     }
 
+    void enableSplitScreenAspectRatioForUnresizableApps(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration)
+                .getIsSplitScreenAspectRatioForUnresizableAppsEnabled();
+    }
+
     void enableCameraCompatTreatmentAtBuildTime(boolean enabled) {
         doReturn(enabled).when(mAppCompatConfiguration)
                 .isCameraCompatTreatmentEnabledAtBuildTime();
@@ -94,6 +101,32 @@
                 .getThinLetterboxHeightPx();
     }
 
+    void setLetterboxActivityCornersRounded(boolean rounded) {
+        doReturn(rounded).when(mAppCompatConfiguration).isLetterboxActivityCornersRounded();
+    }
+
+    void setLetterboxEducationEnabled(boolean enabled) {
+        doReturn(enabled).when(mAppCompatConfiguration).getIsEducationEnabled();
+    }
+
+    void setLetterboxActivityCornersRadius(int cornerRadius) {
+        doReturn(cornerRadius).when(mAppCompatConfiguration).getLetterboxActivityCornersRadius();
+    }
+
+    void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
+        doReturn(backgroundType).when(mAppCompatConfiguration).getLetterboxBackgroundType();
+    }
+
+    void setLetterboxBackgroundWallpaperBlurRadiusPx(int blurRadiusPx) {
+        doReturn(blurRadiusPx).when(mAppCompatConfiguration)
+                .getLetterboxBackgroundWallpaperBlurRadiusPx();
+    }
+
+    void setLetterboxBackgroundWallpaperDarkScrimAlpha(float darkScrimAlpha) {
+        doReturn(darkScrimAlpha).when(mAppCompatConfiguration)
+                .getLetterboxBackgroundWallpaperDarkScrimAlpha();
+    }
+
     void checkToNextLeftStop(boolean invoked) {
         verify(mAppCompatConfiguration, times(invoked ? 1 : 0))
                 .movePositionForHorizontalReachabilityToNextLeftStop(anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
index 27c5e4e..d8f8453 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatFocusOverridesTest.java
@@ -19,8 +19,6 @@
 import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
 import static android.view.WindowManager.PROPERTY_COMPAT_ENABLE_FAKE_FOCUS;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
 import android.compat.testing.PlatformCompatChangeRule;
 import android.platform.test.annotations.Presubmit;
 
@@ -177,7 +175,6 @@
      * Runs a test scenario providing a Robot.
      */
     void runTestScenario(@NonNull Consumer<FocusOverridesRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final FocusOverridesRobotTest robot = new FocusOverridesRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxOverrideTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxOverrideTest.java
new file mode 100644
index 0000000..af7f881
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxOverrideTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_OVERRIDE_UNSET;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatLetterboxOverrides}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:AppCompatLetterboxOverrideTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatLetterboxOverrideTest extends WindowTestsBase {
+
+    @Test
+    public void testIsLetterboxEducationEnabled() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxEducationEnabled(/* enabled */ true);
+            robot.checkLetterboxEducationEnabled(/* enabled */ true);
+
+            robot.conf().setLetterboxEducationEnabled(/* enabled */ false);
+            robot.checkLetterboxEducationEnabled(/* enabled */ false);
+        });
+    }
+
+    @Test
+    public void testShouldLetterboxHaveRoundedCorners() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.activity().setTopActivityFillsParent(/* fillsParent */ true);
+            robot.checkShouldLetterboxHaveRoundedCorners(/* expected */ true);
+
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ false);
+            robot.checkShouldLetterboxHaveRoundedCorners(/* expected */ false);
+
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.activity().setTopActivityFillsParent(/* fillsParent */ false);
+            robot.checkShouldLetterboxHaveRoundedCorners(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testHasWallpaperBackgroundForLetterbox() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+
+            robot.invokeCheckWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */ false);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+
+            robot.invokeCheckWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */ true);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ true);
+
+            robot.invokeCheckWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */ true);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ true);
+
+            robot.invokeCheckWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */ false);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testCheckWallpaperBackgroundForLetterbox() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+
+            robot.checkWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */
+                    true, /* expected */ true);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ true);
+
+            robot.checkWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */
+                    true, /* expected */ false);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ true);
+
+            robot.checkWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */
+                    false, /* expected */ true);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+
+            robot.checkWallpaperBackgroundForLetterbox(/* wallpaperShouldBeShown */
+                    false, /* expected */ false);
+            robot.checkHasWallpaperBackgroundForLetterbox(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testLetterboxActivityCornersRadius() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxActivityCornersRadius(/* cornerRadius */ 0);
+            robot.checkLetterboxActivityCornersRadius(/* cornerRadius */ 0);
+
+            robot.conf().setLetterboxActivityCornersRadius(/* cornerRadius */ 37);
+            robot.checkLetterboxActivityCornersRadius(/* cornerRadius */ 37);
+
+            robot.conf().setLetterboxActivityCornersRadius(/* cornerRadius */ 5);
+            robot.checkLetterboxActivityCornersRadius(/* cornerRadius */ 5);
+        });
+    }
+
+    @Test
+    public void testLetterboxActivityCornersRaunded() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.checkLetterboxActivityCornersRounded(/* expected */ true);
+
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ false);
+            robot.checkLetterboxActivityCornersRounded(/* expected */ false);
+        });
+    }
+
+    @Test
+    public void testLetterboxBackgroundType() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxBackgroundType(LETTERBOX_BACKGROUND_OVERRIDE_UNSET);
+            robot.checkLetterboxBackgroundType(LETTERBOX_BACKGROUND_OVERRIDE_UNSET);
+
+            robot.conf().setLetterboxBackgroundType(LETTERBOX_BACKGROUND_SOLID_COLOR);
+            robot.checkLetterboxBackgroundType(LETTERBOX_BACKGROUND_SOLID_COLOR);
+
+            robot.conf().setLetterboxBackgroundType(LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND);
+            robot.checkLetterboxBackgroundType(LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND);
+
+            robot.conf().setLetterboxBackgroundType(
+                    LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING);
+            robot.checkLetterboxBackgroundType(LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING);
+
+            robot.conf().setLetterboxBackgroundType(LETTERBOX_BACKGROUND_WALLPAPER);
+            robot.checkLetterboxBackgroundType(LETTERBOX_BACKGROUND_WALLPAPER);
+        });
+    }
+
+    @Test
+    public void testLetterboxBackgroundWallpaperBlurRadiusPx() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxBackgroundWallpaperBlurRadiusPx(-1);
+            robot.checkLetterboxWallpaperBlurRadiusPx(0);
+
+            robot.conf().setLetterboxBackgroundWallpaperBlurRadiusPx(0);
+            robot.checkLetterboxWallpaperBlurRadiusPx(0);
+
+            robot.conf().setLetterboxBackgroundWallpaperBlurRadiusPx(10);
+            robot.checkLetterboxWallpaperBlurRadiusPx(10);
+        });
+    }
+
+    @Test
+    public void testLetterboxBackgroundWallpaperDarkScrimAlpha() {
+        runTestScenario((robot) -> {
+            robot.activity().createActivityWithComponent();
+
+            robot.conf().setLetterboxBackgroundWallpaperDarkScrimAlpha(-1f);
+            robot.checkLetterboxWallpaperDarkScrimAlpha(0);
+
+            robot.conf().setLetterboxBackgroundWallpaperDarkScrimAlpha(1.1f);
+            robot.checkLetterboxWallpaperDarkScrimAlpha(0);
+
+            robot.conf().setLetterboxBackgroundWallpaperDarkScrimAlpha(0.001f);
+            robot.checkLetterboxWallpaperDarkScrimAlpha(0.001f);
+
+            robot.conf().setLetterboxBackgroundWallpaperDarkScrimAlpha(0.999f);
+            robot.checkLetterboxWallpaperDarkScrimAlpha(0.999f);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<LetterboxOverridesRobotTest> consumer) {
+        final LetterboxOverridesRobotTest robot =
+                new LetterboxOverridesRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class LetterboxOverridesRobotTest extends AppCompatRobotBase {
+        LetterboxOverridesRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        void invokeCheckWallpaperBackgroundForLetterbox(boolean wallpaperShouldBeShown) {
+            getLetterboxOverrides().checkWallpaperBackgroundForLetterbox(wallpaperShouldBeShown);
+        }
+
+        void checkLetterboxEducationEnabled(boolean enabled) {
+            assertEquals(enabled, getLetterboxOverrides().isLetterboxEducationEnabled());
+        }
+
+        void checkShouldLetterboxHaveRoundedCorners(boolean expected) {
+            assertEquals(expected,
+                    getLetterboxOverrides().shouldLetterboxHaveRoundedCorners());
+        }
+
+        void checkHasWallpaperBackgroundForLetterbox(boolean expected) {
+            assertEquals(expected,
+                    getLetterboxOverrides().hasWallpaperBackgroundForLetterbox());
+        }
+
+        void checkWallpaperBackgroundForLetterbox(boolean wallpaperShouldBeShown,
+                boolean expected) {
+            assertEquals(expected,
+                    getLetterboxOverrides().checkWallpaperBackgroundForLetterbox(
+                            wallpaperShouldBeShown));
+        }
+
+        void checkLetterboxActivityCornersRadius(int expected) {
+            assertEquals(expected, getLetterboxOverrides().getLetterboxActivityCornersRadius());
+        }
+
+        void checkLetterboxActivityCornersRounded(boolean expected) {
+            assertEquals(expected, getLetterboxOverrides().isLetterboxActivityCornersRounded());
+        }
+
+        void checkLetterboxBackgroundType(@LetterboxBackgroundType int expected) {
+            assertEquals(expected, getLetterboxOverrides().getLetterboxBackgroundType());
+        }
+
+        void checkLetterboxWallpaperBlurRadiusPx(int expected) {
+            assertEquals(expected, getLetterboxOverrides().getLetterboxWallpaperBlurRadiusPx());
+        }
+
+        void checkLetterboxWallpaperDarkScrimAlpha(float expected) {
+            assertEquals(expected, getLetterboxOverrides().getLetterboxWallpaperDarkScrimAlpha(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        @NonNull
+        private AppCompatLetterboxOverrides getLetterboxOverrides() {
+            return activity().top().mAppCompatController.getAppCompatLetterboxOverrides();
+        }
+
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
new file mode 100644
index 0000000..e046f7c
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.wm;
+
+import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
+import android.view.InsetsState;
+import android.view.RoundedCorner;
+import android.view.RoundedCorners;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link AppCompatLetterboxPolicy}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:AppCompatLetterboxPolicyTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class AppCompatLetterboxPolicyTest extends WindowTestsBase {
+
+    @Test
+    public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
+        runTestScenario((robot) -> {
+            robot.configureWindowStateWithTaskBar(/* hasTaskBarInsetsRoundedCorners */ true);
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+            robot.setTopActivityTransparentPolicyRunning(/* running */ true);
+            robot.activity().configureTopActivityBounds(new Rect(0, 0, 500, 300));
+
+            robot.resizeMainWindow(/* newWidth */ 499, /* newHeight */ 299);
+            robot.checkWindowStateHasCropBounds(/* expected */ false);
+
+            robot.resizeMainWindow(/* newWidth */ 500, /* newHeight */ 300);
+            robot.checkWindowStateHasCropBounds(/* expected */ true);
+        });
+    }
+
+    @Test
+    public void testGetCropBoundsIfNeeded_noCrop() {
+        runTestScenario((robot) -> {
+            robot.configureWindowStateWithTaskBar(/* hasTaskBarInsetsRoundedCorners */ false);
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+            // Do not apply crop if taskbar is collapsed
+            robot.collapseTaskBar();
+            robot.checkTaskBarIsExpanded(/* expected */ false);
+
+            robot.activity().configureTopActivityBounds(new Rect(50, 25, 150, 75));
+            robot.checkWindowStateHasCropBounds(/* expected */ true);
+            // Expected the same size of the activity.
+            robot.validateWindowStateCropBounds(0, 0, 100, 50);
+        });
+    }
+
+    @Test
+    public void testGetCropBoundsIfNeeded_appliesCrop() {
+        runTestScenario((robot) -> {
+            robot.configureWindowStateWithTaskBar(/* hasTaskBarInsetsRoundedCorners */ true);
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+            // Apply crop if taskbar is expanded.
+            robot.expandTaskBar();
+            robot.checkTaskBarIsExpanded(/* expected */ true);
+
+            robot.activity().configureTopActivityBounds(new Rect(50, 0, 150, 100));
+            robot.checkWindowStateHasCropBounds(/* expected */ true);
+            // The task bar expanded height is removed from the crop height.
+            robot.validateWindowStateCropBounds(0, 0, 100, 80);
+        });
+    }
+
+    @Test
+    public void testGetCropBoundsIfNeeded_appliesCropWithSizeCompatScaling() {
+        runTestScenario((robot) -> {
+            robot.configureWindowStateWithTaskBar(/* hasTaskBarInsetsRoundedCorners */ true);
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+            // Apply crop if taskbar is expanded.
+            robot.expandTaskBar();
+            robot.checkTaskBarIsExpanded(/* expected */ true);
+            robot.activity().setTopActivityInSizeCompatMode(/* isScm */ true);
+            robot.setInvCompatState(/* scale */ 2.0f);
+
+            robot.activity().configureTopActivityBounds(new Rect(50, 0, 150, 100));
+
+            robot.checkWindowStateHasCropBounds(/* expected */ true);
+            // The width and height, considering task bar, are scaled by 2.
+            robot.validateWindowStateCropBounds(0, 0, 200, 160);
+        });
+    }
+
+    @Test
+    public void testGetRoundedCornersRadius_withRoundedCornersFromInsets() {
+        runTestScenario((robot) -> {
+            robot.conf().setLetterboxActivityCornersRadius(-1);
+            robot.configureWindowState();
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+            robot.setInvCompatState(/* scale */ 0.5f);
+            robot.configureInsetsRoundedCorners(new RoundedCorners(
+                    /*topLeft=*/ null,
+                    /*topRight=*/ null,
+                    /*bottomRight=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT,
+                    /* configurationRadius */ 15, /*centerX=*/ 1, /*centerY=*/ 1),
+                    /*bottomLeft=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT,
+                    30 /*2 is to test selection of the min radius*/,
+                    /*centerX=*/ 1, /*centerY=*/ 1)
+            ));
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 7);
+        });
+    }
+
+
+    @Test
+    public void testGetRoundedCornersRadius_withLetterboxActivityCornersRadius() {
+        runTestScenario((robot) -> {
+            robot.conf().setLetterboxActivityCornersRadius(15);
+            robot.configureWindowState();
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+            robot.setInvCompatState(/* scale */ 0.5f);
+
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ true);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 7);
+
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 7);
+
+            robot.activity().setTopActivityVisibleRequested(/* isVisibleRequested */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ false);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 0);
+
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ true);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 7);
+        });
+    }
+
+    @Test
+    public void testGetRoundedCornersRadius_noScalingApplied() {
+        runTestScenario((robot) -> {
+            robot.conf().setLetterboxActivityCornersRadius(15);
+            robot.configureWindowState();
+            robot.activity().createActivityWithComponent();
+            robot.setTopActivityInLetterboxAnimation(/* inLetterboxAnimation */ false);
+            robot.activity().setTopActivityVisible(/* isVisible */ true);
+            robot.setIsLetterboxedForFixedOrientationAndAspectRatio(/* inLetterbox */ true);
+            robot.conf().setLetterboxActivityCornersRounded(/* rounded */ true);
+            robot.resources().configureGetDimensionPixelSize(R.dimen.taskbar_frame_height, 20);
+
+            robot.setInvCompatState(/* scale */ -1f);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 15);
+
+            robot.setInvCompatState(/* scale */ 0f);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 15);
+
+            robot.setInvCompatState(/* scale */ 1f);
+            robot.checkWindowStateRoundedCornersRadius(/* expected */ 15);
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<LetterboxPolicyRobotTest> consumer) {
+        final LetterboxPolicyRobotTest robot = new LetterboxPolicyRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class LetterboxPolicyRobotTest extends AppCompatRobotBase {
+
+        static final int TASKBAR_COLLAPSED_HEIGHT = 10;
+        static final int TASKBAR_EXPANDED_HEIGHT = 20;
+        private static final int SCREEN_WIDTH = 200;
+        private static final int SCREEN_HEIGHT = 100;
+        static final Rect TASKBAR_COLLAPSED_BOUNDS = new Rect(0,
+                SCREEN_HEIGHT - TASKBAR_COLLAPSED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
+        static final Rect TASKBAR_EXPANDED_BOUNDS = new Rect(0,
+                SCREEN_HEIGHT - TASKBAR_EXPANDED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
+
+        @NonNull
+        private final WindowState mWindowState;
+        @Nullable
+        private InsetsSource mTaskbar;
+        @Nullable
+        private InsetsState mInsetsState;
+
+        LetterboxPolicyRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+            mWindowState = mock(WindowState.class);
+        }
+
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            spyOn(getAspectRatioPolicy());
+            spyOn(getTransparentPolicy());
+        }
+
+        void configureWindowStateWithTaskBar(boolean hasInsetsRoundedCorners) {
+            configureWindowState(/* withTaskBar */ true, hasInsetsRoundedCorners);
+        }
+
+        void configureWindowState() {
+            configureWindowState(/* withTaskBar */ false, /* hasInsetsRoundedCorners */ false);
+        }
+
+        void configureInsetsRoundedCorners(@NonNull RoundedCorners roundedCorners) {
+            mInsetsState.setRoundedCorners(roundedCorners);
+        }
+
+        private void configureWindowState(boolean withTaskBar, boolean hasInsetsRoundedCorners) {
+            mInsetsState = new InsetsState();
+            if (withTaskBar) {
+                mTaskbar = new InsetsSource(/*id=*/ 0,
+                        WindowInsets.Type.navigationBars());
+                if (hasInsetsRoundedCorners) {
+                    mTaskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
+                }
+                mTaskbar.setVisible(true);
+                mInsetsState.addSource(mTaskbar);
+            }
+            mWindowState.mInvGlobalScale = 1f;
+            final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
+            doReturn(mInsetsState).when(mWindowState).getInsetsState();
+            doReturn(attrs).when(mWindowState).getAttrs();
+            doReturn(true).when(mWindowState).isDrawn();
+            doReturn(true).when(mWindowState).isOnScreen();
+            doReturn(false).when(mWindowState).isLetterboxedForDisplayCutout();
+            doReturn(true).when(mWindowState).areAppWindowBoundsLetterboxed();
+        }
+
+        void setInvCompatState(float scale) {
+            mWindowState.mInvGlobalScale = scale;
+        }
+
+        void setTopActivityInLetterboxAnimation(boolean inLetterboxAnimation) {
+            doReturn(inLetterboxAnimation).when(activity().top()).isInLetterboxAnimation();
+        }
+
+        void setTopActivityTransparentPolicyRunning(boolean running) {
+            doReturn(running).when(getTransparentPolicy()).isRunning();
+        }
+
+        void setIsLetterboxedForFixedOrientationAndAspectRatio(boolean isLetterboxed) {
+            doReturn(isLetterboxed).when(getAspectRatioPolicy())
+                    .isLetterboxedForFixedOrientationAndAspectRatio();
+        }
+
+        void resizeMainWindow(int newWidth, int newHeight) {
+            mWindowState.mRequestedWidth = newWidth;
+            mWindowState.mRequestedHeight = newHeight;
+        }
+
+        void collapseTaskBar() {
+            mTaskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
+        }
+
+        void expandTaskBar() {
+            mTaskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
+        }
+
+        void checkWindowStateHasCropBounds(boolean expected) {
+            final Rect cropBounds = getAppCompatLetterboxPolicy().getCropBoundsIfNeeded(
+                    mWindowState);
+            if (expected) {
+                assertNotNull(cropBounds);
+            } else {
+                assertNull(cropBounds);
+            }
+        }
+
+        void checkTaskBarIsExpanded(boolean expected) {
+            final InsetsSource expandedTaskBar = AppCompatUtils.getExpandedTaskbarOrNull(
+                    mWindowState);
+            if (expected) {
+                assertNotNull(expandedTaskBar);
+            } else {
+                assertNull(expandedTaskBar);
+            }
+        }
+
+        void checkWindowStateRoundedCornersRadius(int expected) {
+            assertEquals(expected, getAppCompatLetterboxPolicy()
+                    .getRoundedCornersRadius(mWindowState));
+        }
+
+        void validateWindowStateCropBounds(int left, int top, int right, int bottom) {
+            final Rect cropBounds = getAppCompatLetterboxPolicy().getCropBoundsIfNeeded(
+                    mWindowState);
+            assertEquals(left, cropBounds.left);
+            assertEquals(top, cropBounds.top);
+            assertEquals(right, cropBounds.right);
+            assertEquals(bottom, cropBounds.bottom);
+        }
+
+        @NonNull
+        private AppCompatAspectRatioPolicy getAspectRatioPolicy() {
+            return activity().top().mAppCompatController.getAppCompatAspectRatioPolicy();
+        }
+
+        @NonNull
+        private TransparentPolicy getTransparentPolicy() {
+            return activity().top().mAppCompatController.getTransparentPolicy();
+        }
+
+        @NonNull
+        private AppCompatLetterboxPolicy getAppCompatLetterboxPolicy() {
+            return activity().top().mAppCompatController.getAppCompatLetterboxPolicy();
+        }
+
+    }
+
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
index f6d0744a..9057b6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -514,7 +514,6 @@
      */
     void runTestScenario(boolean withActivity,
                          @NonNull Consumer<OrientationPolicyRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final OrientationPolicyRobotTest robot =
                 new OrientationPolicyRobotTest(mWm, mAtm, mSupervisor, withActivity);
         consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
index 5ff8f02..1edbcd5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -164,7 +164,6 @@
      * Runs a test scenario providing a Robot.
      */
     void runTestScenario(@NonNull Consumer<ReachabilityOverridesRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final ReachabilityOverridesRobotTest robot =
                 new ReachabilityOverridesRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
index 96734b3..ddc4de9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
@@ -228,7 +228,6 @@
      * Runs a test scenario providing a Robot.
      */
     void runTestScenario(@NonNull Consumer<ReachabilityPolicyRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final ReachabilityPolicyRobotTest robot =
                 new ReachabilityPolicyRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
index cade213..b8d554b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -20,8 +20,6 @@
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
 import android.compat.testing.PlatformCompatChangeRule;
 import android.platform.test.annotations.Presubmit;
 
@@ -171,7 +169,6 @@
      * Runs a test scenario providing a Robot.
      */
     void runTestScenario(@NonNull Consumer<ResizeOverridesRobotTest> consumer) {
-        spyOn(mWm.mAppCompatConfiguration);
         final ResizeOverridesRobotTest robot = new ResizeOverridesRobotTest(mWm, mAtm, mSupervisor);
         consumer.accept(robot);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResourcesRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResourcesRobot.java
new file mode 100644
index 0000000..05e9a0f
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResourcesRobot.java
@@ -0,0 +1,44 @@
+/*
+ * 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.res.Resources;
+
+import androidx.annotation.DimenRes;
+import androidx.annotation.NonNull;
+
+/**
+ * Robot for managing {@link Resources} in unit tests.
+ */
+public class AppCompatResourcesRobot {
+
+    @NonNull
+    private final Resources mResources;
+
+    AppCompatResourcesRobot(@NonNull Resources resources) {
+        mResources = resources;
+        spyOn(mResources);
+    }
+
+    void configureGetDimensionPixelSize(@DimenRes int resId, int value) {
+        doReturn(value).when(mResources).getDimensionPixelSize(resId);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
index 4e58e1d..5f2a63a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java
@@ -37,6 +37,8 @@
     private final AppCompatConfigurationRobot mConfigurationRobot;
     @NonNull
     private final AppCompatComponentPropRobot mOptPropRobot;
+    @NonNull
+    private final AppCompatResourcesRobot mResourcesRobot;
 
     AppCompatRobotBase(@NonNull WindowManagerService wm,
             @NonNull ActivityTaskManagerService atm,
@@ -48,6 +50,7 @@
         mConfigurationRobot =
                 new AppCompatConfigurationRobot(wm.mAppCompatConfiguration);
         mOptPropRobot = new AppCompatComponentPropRobot(wm);
+        mResourcesRobot = new AppCompatResourcesRobot(wm.mContext.getResources());
     }
 
     AppCompatRobotBase(@NonNull WindowManagerService wm,
@@ -60,7 +63,7 @@
      * Specific Robots can override this method to add operation to run on a newly created
      * {@link ActivityRecord}. Common case is to invoke spyOn().
      *
-     * @param activity THe newly created {@link ActivityRecord}.
+     * @param activity The newly created {@link ActivityRecord}.
      */
     @CallSuper
     void onPostActivityCreation(@NonNull ActivityRecord activity) {
@@ -81,7 +84,6 @@
         return mConfigurationRobot;
     }
 
-    @NonNull
     void applyOnConf(@NonNull Consumer<AppCompatConfigurationRobot> consumer) {
         consumer.accept(mConfigurationRobot);
     }
@@ -91,7 +93,6 @@
         return mActivityRobot;
     }
 
-    @NonNull
     void applyOnActivity(@NonNull Consumer<AppCompatActivityRobot> consumer) {
         consumer.accept(mActivityRobot);
     }
@@ -101,8 +102,16 @@
         return mOptPropRobot;
     }
 
-    @NonNull
     void applyOnProp(@NonNull Consumer<AppCompatComponentPropRobot> consumer) {
         consumer.accept(mOptPropRobot);
     }
+
+    @NonNull
+    AppCompatResourcesRobot resources() {
+        return mResourcesRobot;
+    }
+
+    void applyOnResources(@NonNull Consumer<AppCompatResourcesRobot> consumer) {
+        consumer.accept(mResourcesRobot);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index af4394a..c294bc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -25,11 +25,15 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -37,6 +41,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.spyOn;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -46,11 +51,14 @@
 import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.gui.DropInputMode;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -86,6 +94,7 @@
     public void setUp() throws Exception {
         assumeFalse(WindowManagerService.sEnableShellTransitions);
         mAppTransitionController = new AppTransitionController(mWm, mDisplayContent);
+        mWm.mAnimator.ready();
     }
 
     @Test
@@ -833,6 +842,353 @@
     }
 
     @Test
+    public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord activity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(activity);
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation run by the remote handler.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_noOverrideWithOnlyTaskFragmentFillingTask() {
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord closingActivity = createActivityRecord(task);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+        // Make sure the TaskFragment is not embedded.
+        assertFalse(taskFragment.isEmbeddedWithBoundsOverride());
+        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(closingActivity);
+        prepareActivityForAppTransition(openingActivity);
+        final int uid = 12345;
+        closingActivity.info.applicationInfo.uid = uid;
+        openingActivity.info.applicationInfo.uid = uid;
+        task.effectiveUid = uid;
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity,
+                null /* changingTaskFragment */);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation is not run by the remote handler because the activity is filling the Task.
+        assertFalse(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_overrideWithTaskFragmentNotFillingTask() {
+        final Task task = createTask(mDisplayContent);
+        final ActivityRecord closingActivity = createActivityRecord(task);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+        // Make sure the TaskFragment is embedded.
+        taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        final Rect embeddedBounds = new Rect(task.getBounds());
+        embeddedBounds.right = embeddedBounds.left + embeddedBounds.width() / 2;
+        taskFragment.setBounds(embeddedBounds);
+        assertTrue(taskFragment.isEmbeddedWithBoundsOverride());
+        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(closingActivity);
+        prepareActivityForAppTransition(openingActivity);
+        final int uid = 12345;
+        closingActivity.info.applicationInfo.uid = uid;
+        openingActivity.info.applicationInfo.uid = uid;
+        task.effectiveUid = uid;
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity,
+                null /* changingTaskFragment */);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation run by the remote handler.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Closing non-embedded activity.
+        final ActivityRecord closingActivity = createActivityRecord(task);
+        prepareActivityForAppTransition(closingActivity);
+        // Opening TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(openingActivity);
+        task.effectiveUid = openingActivity.getUid();
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation run by the remote handler.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Closing TaskFragment with embedded activity.
+        final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
+        prepareActivityForAppTransition(closingActivity);
+        closingActivity.info.applicationInfo.uid = 12345;
+        // Opening TaskFragment with embedded activity with different UID.
+        final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
+        prepareActivityForAppTransition(openingActivity);
+        openingActivity.info.applicationInfo.uid = 54321;
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation run by the remote handler.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Closing activity in Task1.
+        final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
+        prepareActivityForAppTransition(closingActivity);
+        // Opening TaskFragment with embedded activity in Task2.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(openingActivity);
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation not run by the remote handler.
+        assertFalse(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Closing TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(closingActivity);
+        closingActivity.info.applicationInfo.uid = 12345;
+        task.effectiveUid = closingActivity.getUid();
+        // Opening non-embedded activity with different UID.
+        final ActivityRecord openingActivity = createActivityRecord(task);
+        prepareActivityForAppTransition(openingActivity);
+        openingActivity.info.applicationInfo.uid = 54321;
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation should not run by the remote handler when there are non-embedded activities of
+        // different UID.
+        assertFalse(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with embedded activity.
+        final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+        final ActivityRecord activity = taskFragment.getTopMostActivity();
+        prepareActivityForAppTransition(activity);
+        // Set wallpaper as visible.
+        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
+        spyOn(mDisplayContent.mWallpaperController);
+        doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // Animation should not run by the remote handler when there is wallpaper in the transition.
+        assertFalse(remoteAnimationRunner.isAnimationStarted());
+    }
+
+    @Test
+    public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with embedded activities, one is trusted embedded, and the other
+        // one is untrusted embedded.
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(2)
+                .setOrganizer(organizer)
+                .build();
+        final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord();
+        final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord();
+        // Also create a non-embedded activity in the Task.
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+        task.addChild(activity2, POSITION_BOTTOM);
+        prepareActivityForAppTransition(activity0);
+        prepareActivityForAppTransition(activity1);
+        prepareActivityForAppTransition(activity2);
+        doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0);
+        doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1);
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // The animation will be animated remotely by client and all activities are input disabled
+        // for untrusted animation.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+        verify(activity0).setDropInputForAnimation(true);
+        verify(activity1).setDropInputForAnimation(true);
+        verify(activity2).setDropInputForAnimation(true);
+        verify(activity0).setDropInputMode(DropInputMode.ALL);
+        verify(activity1).setDropInputMode(DropInputMode.ALL);
+        verify(activity2).setDropInputMode(DropInputMode.ALL);
+
+        // Reset input after animation is finished.
+        clearInvocations(activity0);
+        clearInvocations(activity1);
+        clearInvocations(activity2);
+        remoteAnimationRunner.finishAnimation();
+
+        verify(activity0).setDropInputForAnimation(false);
+        verify(activity1).setDropInputForAnimation(false);
+        verify(activity2).setDropInputForAnimation(false);
+        verify(activity0).setDropInputMode(DropInputMode.OBSCURED);
+        verify(activity1).setDropInputMode(DropInputMode.NONE);
+        verify(activity2).setDropInputMode(DropInputMode.NONE);
+    }
+
+    /**
+     * Since we don't have any use case to rely on handling input during animation, disable it even
+     * if it is trusted embedding so that it could cover some edge-cases when a previously trusted
+     * host starts doing something bad.
+     */
+    @Test
+    public void testOverrideTaskFragmentAdapter_inputProtectedForTrustedAnimation() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with only trusted embedded activity
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(1)
+                .setOrganizer(organizer)
+                .build();
+        final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
+        prepareActivityForAppTransition(activity);
+        doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity);
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // The animation will be animated remotely by client and all activities are input disabled
+        // for untrusted animation.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+        verify(activity).setDropInputForAnimation(true);
+        verify(activity).setDropInputMode(DropInputMode.ALL);
+
+        // Reset input after animation is finished.
+        clearInvocations(activity);
+        remoteAnimationRunner.finishAnimation();
+
+        verify(activity).setDropInputForAnimation(false);
+        verify(activity).setDropInputMode(DropInputMode.NONE);
+    }
+
+    /**
+     * We don't need to drop input for fully trusted embedding (system app, and embedding in the
+     * same app). This will allow users to do fast tapping.
+     */
+    @Test
+    public void testOverrideTaskFragmentAdapter_noInputProtectedForFullyTrustedAnimation() {
+        final Task task = createTask(mDisplayContent);
+        final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+        final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+        setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+        // Create a TaskFragment with only trusted embedded activity
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(task)
+                .createActivityCount(1)
+                .setOrganizer(organizer)
+                .build();
+        final ActivityRecord activity = taskFragment.getChildAt(0).asActivityRecord();
+        prepareActivityForAppTransition(activity);
+        final int uid = mAtm.mTaskFragmentOrganizerController.getTaskFragmentOrganizerUid(
+                getITaskFragmentOrganizer(organizer));
+        doReturn(true).when(task).isFullyTrustedEmbedding(uid);
+        spyOn(mDisplayContent.mAppTransition);
+
+        // Prepare and start transition.
+        prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
+        waitUntilWindowAnimatorIdle();
+
+        // The animation will be animated remotely by client, but input should not be dropped for
+        // fully trusted.
+        assertTrue(remoteAnimationRunner.isAnimationStarted());
+        verify(activity, never()).setDropInputForAnimation(true);
+        verify(activity, never()).setDropInputMode(DropInputMode.ALL);
+    }
+
+    @Test
     public void testTransitionGoodToGoForTaskFragments() {
         final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
         final Task task = createTask(mDisplayContent);
@@ -898,6 +1254,22 @@
         verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
     }
 
+    /** Registers remote animation for the organizer. */
+    private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
+            TestRemoteAnimationRunner remoteAnimationRunner) {
+        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+                remoteAnimationRunner, 10, 1);
+        final ITaskFragmentOrganizer iOrganizer = getITaskFragmentOrganizer(organizer);
+        final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
+        definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
+        registerTaskFragmentOrganizer(iOrganizer);
+        mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+    }
+
     private static ITaskFragmentOrganizer getITaskFragmentOrganizer(
             TaskFragmentOrganizer organizer) {
         return ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
@@ -931,4 +1303,4 @@
         doReturn(mock(RemoteAnimationTarget.class)).when(activity).createRemoteAnimationTarget(
                 any());
     }
-}
\ No newline at end of file
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 9950541..b6e393d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,10 +39,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 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.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
@@ -51,9 +49,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 
 import android.graphics.Rect;
 import android.os.Binder;
@@ -377,41 +373,6 @@
     }
 
     @Test
-    public void testDelayWhileRecents() {
-        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
-        doReturn(false).when(dc).onDescendantOrientationChanged(any());
-        final Task task = createTask(dc);
-
-        // Simulate activity1 launches activity2.
-        final ActivityRecord activity1 = createActivityRecord(task);
-        activity1.setVisible(true);
-        activity1.setVisibleRequested(false);
-        activity1.allDrawn = true;
-        final ActivityRecord activity2 = createActivityRecord(task);
-        activity2.setVisible(false);
-        activity2.setVisibleRequested(true);
-        activity2.allDrawn = true;
-
-        dc.mClosingApps.add(activity1);
-        dc.mOpeningApps.add(activity2);
-        dc.prepareAppTransition(TRANSIT_OPEN);
-        assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
-
-        // Wait until everything in animation handler get executed to prevent the exiting window
-        // from being removed during WindowSurfacePlacer Traversal.
-        waitUntilHandlersIdle();
-
-        // Start recents
-        doReturn(true).when(task)
-                .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
-
-        dc.mAppTransitionController.handleAppTransitionReady();
-
-        verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
-        verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
-    }
-
-    @Test
     public void testGetAnimationStyleResId() {
         // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
         // specifying window type.
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 44837d7..20dcdde 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -110,6 +110,7 @@
         mWindowManagerInternal = mock(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
         mBackAnimationAdapter = mock(BackAnimationAdapter.class);
+        doReturn(true).when(mBackAnimationAdapter).isAnimatable(anyInt());
         mNavigationMonitor = mock(BackNavigationController.NavigationMonitor.class);
         mRootHomeTask = initHomeActivity();
     }
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 6e48818..3910904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE;
 
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT;
@@ -25,9 +26,11 @@
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW;
+import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK;
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +46,9 @@
 import android.content.pm.PackageManagerInternal;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.DeviceConfig;
 import android.util.Pair;
 
@@ -52,6 +58,7 @@
 import com.android.modules.utils.testing.ExtendedMockitoRule;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -95,7 +102,9 @@
     @Rule
     public final ExtendedMockitoRule extendedMockitoRule =
             new ExtendedMockitoRule.Builder(this).setStrictness(Strictness.LENIENT).build();
-
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
     TestableBackgroundActivityStartController mController;
     @Mock
     ActivityMetricsLogger mActivityMetricsLogger;
@@ -186,7 +195,7 @@
         when(mAppOpsManager.checkOpNoThrow(
                 eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
                 anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
-        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 BalVerdict.BLOCK);
     }
 
@@ -227,7 +236,7 @@
         // call
         BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
                 balState);
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
 
         balState.setResultForCaller(callerVerdict);
@@ -295,7 +304,77 @@
                 checkedOptions);
 
         // call
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+                balState);
+        balState.setResultForRealCaller(realCallerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_VISIBLE_WINDOW);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+    public void testCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+        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.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true);
+        when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                        MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller(
+                balState);
+        balState.setResultForCaller(callerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo(
+                BAL_ALLOW_VISIBLE_WINDOW);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+    public void testRealCaller_appHasVisibleWindowWithIfVisibleOptIn() {
+        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.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true);
+        when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                        MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
         balState.setResultForRealCaller(realCallerVerdict);
 
@@ -320,7 +399,7 @@
         int realCallingPid = REGULAR_PID_2;
 
         // setup state
-        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
         when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
 
@@ -357,7 +436,7 @@
                 mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
                 mCallerApp);
         when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
-        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
 
         // prepare call
@@ -371,7 +450,7 @@
                 checkedOptions);
 
         // call
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
         balState.setResultForRealCaller(realCallerVerdict);
 
@@ -404,9 +483,9 @@
                 mService.getProcessController(eq(realCallingPid), eq(realCallingUid))).thenReturn(
                 mCallerApp);
         when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW);
-        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 BalVerdict.BLOCK);
-        when(otherProcess.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        when(otherProcess.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 new BalVerdict(BAL_ALLOW_FOREGROUND, false, "allowed"));
 
         // prepare call
@@ -420,7 +499,7 @@
                 checkedOptions);
 
         // call
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
         balState.setResultForRealCaller(realCallerVerdict);
 
@@ -456,7 +535,7 @@
                 checkedOptions);
 
         // call
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
         balState.setResultForRealCaller(realCallerVerdict);
 
@@ -466,6 +545,45 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES)
+    public void testRealCaller_isCompanionAppWithOptInIfVisible() {
+        // The app has a service that is bound by a different, visible app. The app bound to the
+        // service must remain visible for the app in the background to start activities
+        // successfully.
+        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
+        final int realCallingUserId = UserHandle.getUserId(realCallingUid);
+        when(mService.isAssociatedCompanionApp(eq(realCallingUserId),
+                eq(realCallingUid))).thenReturn(true);
+
+        // prepare call
+        PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
+        BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+        Intent intent = TEST_INTENT;
+        ActivityOptions checkedOptions = mCheckedOptions
+                .setPendingIntentBackgroundActivityStartMode(
+                        MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
+        BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
+                callingPid, callingPackage, realCallingUid, realCallingPid, null,
+                originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+                checkedOptions);
+
+        // call
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
+                balState);
+        balState.setResultForRealCaller(realCallerVerdict);
+
+        // assertions
+        assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo(
+                BAL_BLOCK);
+    }
+
+    @Test
     public void testCaller_balPermission() {
         int callingUid = REGULAR_UID_1;
         int callingPid = REGULAR_PID_1;
@@ -523,7 +641,7 @@
                 checkedOptions);
 
         // call
-        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedBySender(
+        BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller(
                 balState);
         balState.setResultForRealCaller(realCallerVerdict);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index e364264..6ec789599 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -24,6 +24,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -43,6 +44,7 @@
 import com.android.compatibility.common.util.DeviceConfigStateHelper;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.wm.BackgroundActivityStartController.BalVerdict;
+import com.android.server.wm.BackgroundLaunchProcessController.BalCheckConfiguration;
 
 import org.junit.After;
 import org.junit.Before;
@@ -167,9 +169,9 @@
         }
 
         @Override
-        BalVerdict checkBackgroundActivityStartAllowedBySender(BalState state) {
+        BalVerdict checkBackgroundActivityStartAllowedByRealCaller(BalState state) {
             return mRealCallerVerdict.orElseGet(
-                    () -> super.checkBackgroundActivityStartAllowedBySender(state));
+                    () -> super.checkBackgroundActivityStartAllowedByRealCaller(state));
         }
 
         public void setRealCallerVerdict(BalVerdict verdict) {
@@ -177,11 +179,12 @@
         }
 
         @Override
-        BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state) {
+        BalVerdict checkProcessAllowsBal(WindowProcessController app, BalState state,
+                BalCheckConfiguration checkConfiguration) {
             if (mProcessVerdicts.containsKey(app)) {
                 return mProcessVerdicts.get(app);
             }
-            return super.checkProcessAllowsBal(app, state);
+            return super.checkProcessAllowsBal(app, state, checkConfiguration);
         }
     }
 
@@ -209,7 +212,7 @@
         Mockito.when(mAppOpsManager.checkOpNoThrow(
                 eq(AppOpsManager.OP_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION),
                 anyInt(), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
-        Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt())).thenReturn(
+        Mockito.when(mCallerApp.areBackgroundActivityStartsAllowed(anyInt(), any())).thenReturn(
                 BalVerdict.BLOCK);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
index c9c7e92..27e147d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static com.android.server.wm.ActivityTaskManagerService.ACTIVITY_BG_START_GRACE_PERIOD_MS;
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW;
 import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_DISALLOW;
 import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_BOUND_BY_FOREGROUND;
@@ -95,7 +96,12 @@
     int mUid = 234;
     String mPackageName = "package.name";
     int mAppSwitchState = APP_SWITCH_DISALLOW;
-    boolean mIsCheckingForFgsStart = false;
+    BackgroundLaunchProcessController.BalCheckConfiguration mBalCheckConfiguration =
+            new BackgroundLaunchProcessController.BalCheckConfiguration(
+                    /* isCheckingForFgsStarts */ false,
+                    /* checkVisibility */ true,
+                    /* checkOtherExemptions */ true,
+                    ACTIVITY_BG_START_GRACE_PERIOD_MS);
     boolean mHasActivityInVisibleTask = false;
     boolean mHasBackgroundActivityStartPrivileges = false;
     long mLastStopAppSwitchesTime = 0L;
@@ -106,7 +112,7 @@
     public void testNothingAllows() {
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -118,7 +124,7 @@
         mHasBackgroundActivityStartPrivileges = true;
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -136,7 +142,7 @@
                 BackgroundStartPrivileges.ALLOW_BAL);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -154,7 +160,7 @@
                 BackgroundStartPrivileges.ALLOW_BAL);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -170,7 +176,7 @@
                 BackgroundStartPrivileges.ALLOW_BAL);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -186,7 +192,7 @@
                 BackgroundStartPrivileges.ALLOW_BAL);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -201,7 +207,7 @@
         mHasActiveVisibleWindow.add(999);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -216,7 +222,7 @@
         mHasActiveVisibleWindow.add(999);
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -229,7 +235,7 @@
         mHasActivityInVisibleTask = true;
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
@@ -245,7 +251,7 @@
         mLastActivityFinishTime = now - 100;
         BalVerdict balVerdict = mController.areBackgroundActivityStartsAllowed(
                 mPid, mUid, mPackageName,
-                mAppSwitchState, mIsCheckingForFgsStart,
+                mAppSwitchState, mBalCheckConfiguration,
                 mHasActivityInVisibleTask, mHasBackgroundActivityStartPrivileges,
                 mLastStopAppSwitchesTime, mLastActivityLaunchTime,
                 mLastActivityFinishTime);
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
index f2592d2..5a3ae76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java
@@ -276,7 +276,6 @@
                 .setTask(mTask)
                 .build();
 
-        spyOn(mActivity.mLetterboxUiController);
         spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
         spyOn(mActivity.info);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
index 12f5714f..ad80f82 100644
--- a/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/CameraStateMonitorTests.java
@@ -43,10 +43,10 @@
 import java.util.concurrent.Executor;
 
 /**
- * Tests for {@link DisplayRotationCompatPolicy}.
+ * Tests for {@link CameraStateMonitor}.
  *
  * Build/Install/Run:
- *  atest WmTests:DisplayRotationCompatPolicyTests
+ *  atest WmTests:CameraStateMonitorTests
  */
 @SmallTest
 @Presubmit
@@ -68,23 +68,14 @@
     private ActivityRecord mActivity;
     private Task mTask;
 
-    // Simulates a listener which will not react to the change on a particular activity.
-    private final FakeCameraCompatStateListener mNotInterestedListener =
-            new FakeCameraCompatStateListener(
-                    /*onCameraOpenedReturnValue=*/ false,
-                    /*simulateUnsuccessfulCloseOnce=*/ false);
     // Simulates a listener which will react to the change on a particular activity - for example
     // put the activity in a camera compat mode.
-    private final FakeCameraCompatStateListener mInterestedListener =
-            new FakeCameraCompatStateListener(
-                    /*onCameraOpenedReturnValue=*/ true,
-                    /*simulateUnsuccessfulCloseOnce=*/ false);
+    private final FakeCameraCompatStateListener mListener =
+            new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ false);
     // Simulates a listener which for some reason cannot process `onCameraClosed` event once it
     // first arrives - this means that the update needs to be postponed.
     private final FakeCameraCompatStateListener mListenerCannotClose =
-            new FakeCameraCompatStateListener(
-                    /*onCameraOpenedReturnValue=*/ true,
-                    /*simulateUnsuccessfulCloseOnce=*/ true);
+            new FakeCameraCompatStateListener(/* simulateUnsuccessfulCloseOnce= */ true);
 
     @Before
     public void setUp() throws Exception {
@@ -129,44 +120,31 @@
     @After
     public void tearDown() {
         // Remove all listeners.
-        mCameraStateMonitor.removeCameraStateListener(mNotInterestedListener);
-        mCameraStateMonitor.removeCameraStateListener(mInterestedListener);
+        mCameraStateMonitor.removeCameraStateListener(mListener);
         mCameraStateMonitor.removeCameraStateListener(mListenerCannotClose);
 
         // Reset the listener's state.
-        mNotInterestedListener.resetCounters();
-        mInterestedListener.resetCounters();
+        mListener.resetCounters();
         mListenerCannotClose.resetCounters();
     }
 
     @Test
     public void testOnCameraOpened_listenerAdded_notifiesCameraOpened() {
-        mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
+        mCameraStateMonitor.addCameraStateListener(mListener);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
-        assertEquals(1, mNotInterestedListener.mOnCameraOpenedCounter);
+        assertEquals(1, mListener.mOnCameraOpenedCounter);
     }
 
     @Test
-    public void testOnCameraOpened_listenerReturnsFalse_doesNotNotifyCameraClosed() {
-        mCameraStateMonitor.addCameraStateListener(mNotInterestedListener);
-        // Listener returns false on `onCameraOpened`.
-        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-
-        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
-
-        assertEquals(0, mNotInterestedListener.mOnCameraClosedCounter);
-    }
-
-    @Test
-    public void testOnCameraOpened_listenerReturnsTrue_notifyCameraClosed() {
-        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+    public void testOnCameraOpened_cameraClosed_notifyCameraClosed() {
+        mCameraStateMonitor.addCameraStateListener(mListener);
         // Listener returns true on `onCameraOpened`.
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
 
         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
 
-        assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
+        assertEquals(1, mListener.mOnCameraClosedCounter);
     }
 
     @Test
@@ -182,32 +160,22 @@
 
     @Test
     public void testReconnectedToDifferentCamera_notifiesListener() {
-        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+        mCameraStateMonitor.addCameraStateListener(mListener);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_2, TEST_PACKAGE_1);
 
-        assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
+        assertEquals(2, mListener.mOnCameraOpenedCounter);
     }
 
     @Test
     public void testDifferentAppConnectedToCamera_notifiesListener() {
-        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
+        mCameraStateMonitor.addCameraStateListener(mListener);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
         mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_2);
 
-        assertEquals(2, mInterestedListener.mOnCameraOpenedCounter);
-    }
-
-    @Test
-    public void testCameraAlreadyClosed_notifiesListenerOnce() {
-        mCameraStateMonitor.addCameraStateListener(mInterestedListener);
-        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
-        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
-        mCameraAvailabilityCallback.onCameraClosed(CAMERA_ID_1);
-
-        assertEquals(1, mInterestedListener.mOnCameraClosedCounter);
+        assertEquals(2, mListener.mOnCameraOpenedCounter);
     }
 
     private void configureActivity(@NonNull String packageName) {
@@ -221,7 +189,6 @@
                 .build();
 
         spyOn(mActivity.mAtmService.getLifecycleManager());
-        spyOn(mActivity.mLetterboxUiController);
 
         doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
     }
@@ -232,7 +199,6 @@
         int mOnCameraOpenedCounter = 0;
         int mOnCameraClosedCounter = 0;
 
-        boolean mOnCameraOpenedReturnValue = true;
         private boolean mOnCameraClosedReturnValue = true;
 
         /**
@@ -242,17 +208,14 @@
          *                                      subsequent calls. This fake implementation tests the
          *                                      retry mechanism in {@link CameraStateMonitor}.
          */
-        FakeCameraCompatStateListener(boolean onCameraOpenedReturnValue,
-                boolean simulateUnsuccessfulCloseOnce) {
-            mOnCameraOpenedReturnValue = onCameraOpenedReturnValue;
+        FakeCameraCompatStateListener(boolean simulateUnsuccessfulCloseOnce) {
             mOnCameraClosedReturnValue = !simulateUnsuccessfulCloseOnce;
         }
 
         @Override
-        public boolean onCameraOpened(@NonNull ActivityRecord cameraActivity,
+        public void onCameraOpened(@NonNull ActivityRecord cameraActivity,
                 @NonNull String cameraId) {
             mOnCameraOpenedCounter++;
-            return mOnCameraOpenedReturnValue;
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
new file mode 100644
index 0000000..f4e1d49
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
@@ -0,0 +1,494 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+/**
+ * Test class for {@link DesktopAppCompatAspectRatioPolicy}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:DesktopAppCompatAspectRatioPolicyTests
+ */
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
+    private static final float FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.33f;
+
+    @Test
+    public void testHasMinAspectRatioOverride_userAspectRatioEnabled_returnTrue() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+    public void testHasMinAspectRatioOverride_overrideDisabled_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+    public void testHasMinAspectRatioOverride_overrideEnabled_propertyFalse_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+            robot.prop().disable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+    public void testHasMinAspectRatioOverride_overrideDisabled_propertyTrue_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.activity().createActivityWithComponent();
+            robot.prop().enable(PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE);
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @DisableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
+    public void testHasMinAspectRatioOverride_overrideEnabled_nonPortraitActivity_returnsFalse() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ false);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testHasMinAspectRatioOverride_splitScreenAspectRatioOverride_returnTrue() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testHasMinAspectRatioOverride_largeMinAspectRatioOverride_returnTrue() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testHasMinAspectRatioOverride_mediumMinAspectRatioOverride_returnTrue() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+    public void testHasMinAspectRatioOverride_smallMinAspectRatioOverride_returnTrue() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+
+            robot.checkHasMinAspectRatioOverride(/* expected */ true);
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testCalculateAspectRatio_splitScreenAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ false);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testCalculateAspectRatio_largeMinAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ false);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioLargeAspectRatioOverride();
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testCalculateAspectRatio_mediumMinAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ false);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioMediumAspectRatioOverride();
+        });
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+    public void testCalculateAspectRatio_smallMinAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ false);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioSmallAspectRatioOverride();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_defaultMultiWindowLetterboxAspectRatio() {
+        runTestScenario((robot)-> {
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ false);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+                a.setTaskDisplayAreaWindowingMode(WINDOWING_MODE_FREEFORM);
+            });
+
+            robot.checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_displayAspectRatioEnabledForFixedOrientationLetterbox() {
+        runTestScenario((robot)-> {
+            robot.conf().enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+                        SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+            });
+
+            robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_defaultMinAspectRatio_fixedOrientationAspectRatio() {
+        runTestScenario((robot)-> {
+            robot.applyOnConf((c) -> {
+                c.enableDisplayAspectRatioEnabledForFixedOrientationLetterbox(false);
+                c.setFixedOrientationLetterboxAspectRatio(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
+            });
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.configureTopActivity(/* minAspect */ 0, /* maxAspect */ 0,
+                        SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ false);
+            });
+
+            robot.checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_splitScreenForUnresizeableEnabled() {
+        runTestScenario((robot) -> {
+            robot.conf().enableSplitScreenAspectRatioForUnresizableApps(/* isEnabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
+            });
+
+            robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_user3By2AspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(3 / 2f);
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_3_2);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioUser3By2AspectRatiOverride();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_user4By3AspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(4 / 3f);
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_4_3);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioUser4By3AspectRatiOverride();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_user16By9AspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(16 / 9f);
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_16_9);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioUser16By9AspectRatioOverride();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_userSplitScreenAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(robot.getSplitScreenAspectRatio());
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioSplitScreenAspectRatio();
+        });
+    }
+
+    @Test
+    public void testCalculateAspectRatio_userDisplayAreaAspectRatioOverride() {
+        runTestScenario((robot)-> {
+            robot.conf().enableUserAppAspectRatioSettings(/* enabled */ true);
+            robot.applyOnActivity((a) -> {
+                a.createActivityWithComponent();
+                a.setIgnoreOrientationRequest(/* enabled */ true);
+                a.setGetUserMinAspectRatioOverrideValue(robot.getDisplayAreaAspectRatio());
+                a.setGetUserMinAspectRatioOverrideCode(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+            });
+            robot.setDesiredAspectRatio(1f);
+
+            robot.checkCalculateAspectRatioDisplayAreaAspectRatio();
+        });
+    }
+
+    /**
+     * Runs a test scenario providing a Robot.
+     */
+    void runTestScenario(@NonNull Consumer<DesktopAppCompatAspectRatioPolicyRobotTest> consumer) {
+        final DesktopAppCompatAspectRatioPolicyRobotTest robot =
+                new DesktopAppCompatAspectRatioPolicyRobotTest(mWm, mAtm, mSupervisor);
+        consumer.accept(robot);
+    }
+
+    private static class DesktopAppCompatAspectRatioPolicyRobotTest extends AppCompatRobotBase {
+        DesktopAppCompatAspectRatioPolicyRobotTest(@NonNull WindowManagerService wm,
+                @NonNull ActivityTaskManagerService atm,
+                @NonNull ActivityTaskSupervisor supervisor) {
+            super(wm, atm, supervisor);
+        }
+
+        @Override
+        void onPostActivityCreation(@NonNull ActivityRecord activity) {
+            super.onPostActivityCreation(activity);
+            spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
+            spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+        }
+
+        void setDesiredAspectRatio(float aspectRatio) {
+            doReturn(aspectRatio).when(getDesktopAppCompatAspectRatioPolicy())
+                    .getDesiredAspectRatio(any());
+        }
+
+        DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
+            return getTopActivity().mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        }
+
+        float calculateAspectRatio() {
+            return getDesktopAppCompatAspectRatioPolicy().calculateAspectRatio(
+                    getTopActivity().getTask());
+        }
+
+        ActivityRecord getTopActivity() {
+            return this.activity().top();
+        }
+
+        float getSplitScreenAspectRatio() {
+            return  getTopActivity().mAppCompatController.getAppCompatAspectRatioOverrides()
+                    .getSplitScreenAspectRatio();
+        }
+
+        float getDisplayAreaAspectRatio() {
+            final Rect appBounds = getTopActivity().getDisplayArea().getWindowConfiguration()
+                    .getAppBounds();
+            return AppCompatUtils.computeAspectRatio(appBounds);
+        }
+
+        void checkHasMinAspectRatioOverride(boolean expected) {
+            assertEquals(expected, this.activity().top().mAppCompatController
+                    .getDesktopAppCompatAspectRatioPolicy().hasMinAspectRatioOverride(
+                            this.activity().top().getTask()));
+        }
+
+        void checkCalculateAspectRatioSplitScreenAspectRatio() {
+            assertEquals(getSplitScreenAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioLargeAspectRatioOverride() {
+            assertEquals(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, calculateAspectRatio(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioMediumAspectRatioOverride() {
+            assertEquals(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, calculateAspectRatio(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioSmallAspectRatioOverride() {
+            assertEquals(OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE, calculateAspectRatio(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioDefaultLetterboxAspectRatioForMultiWindow() {
+            assertEquals(DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW, calculateAspectRatio(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioDisplayAreaAspectRatio() {
+            assertEquals(getDisplayAreaAspectRatio(), calculateAspectRatio(), FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioDefaultMinFixedOrientationAspectRatio() {
+            assertEquals(FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO, calculateAspectRatio(),
+                    FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioUser3By2AspectRatiOverride() {
+            assertEquals(3 / 2f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioUser4By3AspectRatiOverride() {
+            assertEquals(4 / 3f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+        }
+
+        void checkCalculateAspectRatioUser16By9AspectRatioOverride() {
+            assertEquals(16 / 9f, calculateAspectRatio(), FLOAT_TOLLERANCE);
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 07e95d8..6d508ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -21,11 +21,19 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
@@ -34,7 +42,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_INITIAL_BOUNDS_SCALE;
 import static com.android.server.wm.DesktopModeBoundsCalculator.DESKTOP_MODE_LANDSCAPE_APP_PADDING;
-import static com.android.server.wm.DesktopModeBoundsCalculator.calculateAspectRatio;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
@@ -44,6 +51,8 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.ActivityOptions;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.platform.test.annotations.DisableFlags;
@@ -55,8 +64,12 @@
 
 import com.android.window.flags.Flags;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
 /**
@@ -74,6 +87,9 @@
     private static final Rect PORTRAIT_DISPLAY_BOUNDS = new Rect(0, 0, 1600, 2560);
     private static final float LETTERBOX_ASPECT_RATIO = 1.3f;
 
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     @Before
     public void setUp() throws Exception {
         mActivity = new ActivityBuilder(mAtm).build();
@@ -199,14 +215,17 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_UNSPECIFIED, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -219,14 +238,17 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -239,7 +261,9 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
 
         spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
         doReturn(true).when(
@@ -251,7 +275,8 @@
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -264,7 +289,9 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
 
         spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
         doReturn(true).when(
@@ -276,7 +303,8 @@
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -285,19 +313,466 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void testResizablePortraitBounds_landscapeDevice_resizable_portraitOrientation() {
         setupDesktopModeLaunchParamsModifier();
-        doReturn(LETTERBOX_ASPECT_RATIO).when(()
-                -> calculateAspectRatio(any(), any()));
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
+
+        spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+        doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
+                .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
 
         final int desiredWidth =
                 (int) ((LANDSCAPE_DISPLAY_BOUNDS.height() / LETTERBOX_ASPECT_RATIO) + 0.5f);
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+    public void testSmallAspectRatioOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth =
+                (int) (desiredHeight / OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testMediumAspectRatioOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth =
+                (int) (desiredHeight / OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testLargeAspectRatioOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth =
+                (int) (desiredHeight / OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testSplitScreenAspectRatioOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth =
+                (int) (desiredHeight / activity.mAppCompatController
+                        .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio());
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_SMALL})
+    public void testSmallAspectRatioOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+        // Mock desired aspect ratio so min override can take effect.
+        setDesiredAspectRatio(activity, /* aspectRatio */ 1f);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * OVERRIDE_MIN_ASPECT_RATIO_SMALL_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+    public void testMediumAspectRatioOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+        // Mock desired aspect ratio so min override can take effect.
+        setDesiredAspectRatio(activity, /* aspectRatio */ 1f);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testLargeAspectRatioOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+        // Mock desired aspect ratio so min override can take effect.
+        setDesiredAspectRatio(activity, /* aspectRatio */ 1f);
+
+        final int desiredHeight =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
+    public void testSplitScreenAspectRatioOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ false);
+        // Mock desired aspect ratio so min override can take effect.
+        setDesiredAspectRatio(activity, /* aspectRatio */ 1f);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * activity.mAppCompatController
+                .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio());
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio32Override_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue3_2 = 3 / 2f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_3_2,
+                userAspectRatioOverrideValue3_2);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValue3_2);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio43Override_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue4_3 = 4 / 3f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_4_3,
+                userAspectRatioOverrideValue4_3);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValue4_3);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio169Override_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue16_9 = 16 / 9f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_16_9,
+                userAspectRatioOverrideValue16_9);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValue16_9);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatioSplitScreenOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValueSplitScreen = activity.mAppCompatController
+                .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio();
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+                userAspectRatioOverrideValueSplitScreen);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValueSplitScreen);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatioDisplaySizeOverride_landscapeDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
+                LANDSCAPE_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValueDisplaySize = activity.mAppCompatController
+                .getAppCompatAspectRatioOverrides().getDisplaySizeMinAspectRatio();
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
+                userAspectRatioOverrideValueDisplaySize);
+
+        final int desiredHeight =
+                (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValueDisplaySize);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio32Override_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue3_2 = 3 / 2f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_3_2,
+                userAspectRatioOverrideValue3_2);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * userAspectRatioOverrideValue3_2);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio43Override_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue4_3 = 4 / 3f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_4_3,
+                userAspectRatioOverrideValue4_3);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * userAspectRatioOverrideValue4_3);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatio169Override_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValue16_9 = 16 / 9f;
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_16_9,
+                userAspectRatioOverrideValue16_9);
+
+        final int desiredHeight =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredWidth = (int) (desiredHeight / userAspectRatioOverrideValue16_9);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatioSplitScreenOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValueSplitScreen = activity.mAppCompatController
+                .getAppCompatAspectRatioOverrides().getSplitScreenAspectRatio();
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+                userAspectRatioOverrideValueSplitScreen);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * userAspectRatioOverrideValueSplitScreen);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
+        assertEquals(desiredWidth, mResult.mBounds.width());
+        assertEquals(desiredHeight, mResult.mBounds.height());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+    public void testUserAspectRatioDisplaySizeOverride_portraitDevice() {
+        setupDesktopModeLaunchParamsModifier();
+
+        final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
+                PORTRAIT_DISPLAY_BOUNDS);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
+        final float userAspectRatioOverrideValueDisplaySize = activity.mAppCompatController
+                .getAppCompatAspectRatioOverrides().getDisplaySizeMinAspectRatio();
+        applyUserMinAspectRatioOverride(activity, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
+                userAspectRatioOverrideValueDisplaySize);
+
+        final int desiredWidth =
+                (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
+        final int desiredHeight = (int) (desiredWidth * userAspectRatioOverrideValueDisplaySize);
+
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -310,14 +785,18 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, false);
+        final Task task = createTask(display, /* isResizeable */ false);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
+
 
         final int desiredWidth =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -326,18 +805,23 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void testUnResizablePortraitBounds_landscapeDevice_unResizable_portraitOrientation() {
         setupDesktopModeLaunchParamsModifier();
-        doReturn(LETTERBOX_ASPECT_RATIO).when(()
-                -> calculateAspectRatio(any(), any()));
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_LANDSCAPE,
                 LANDSCAPE_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, false);
+        final Task task = createTask(display, /* isResizeable */ false);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
+
+        spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+        doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
+                .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
 
         final int desiredHeight =
                 (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredWidth = (int) (desiredHeight / LETTERBOX_ASPECT_RATIO);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -350,14 +834,17 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_UNSPECIFIED, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_UNSPECIFIED,
+                task, /* ignoreOrientationRequest */ true);
 
         final int desiredWidth =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -370,14 +857,17 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
 
         final int desiredWidth =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -390,11 +880,13 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
 
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+        spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
         doReturn(true).when(
-                        mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+                        activity.mAppCompatController.getAppCompatAspectRatioOverrides())
                 .isUserFullscreenOverrideEnabled();
 
         final int desiredWidth =
@@ -402,7 +894,8 @@
         final int desiredHeight =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -415,11 +908,13 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
 
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioOverrides());
+        spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides());
         doReturn(true).when(
-                        mActivity.mAppCompatController.getAppCompatAspectRatioOverrides())
+                        activity.mAppCompatController.getAppCompatAspectRatioOverrides())
                 .isSystemOverrideToFullscreenEnabled();
 
         final int desiredWidth =
@@ -427,7 +922,8 @@
         final int desiredHeight =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -436,19 +932,24 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void testResizableLandscapeBounds_portraitDevice_resizable_landscapeOrientation() {
         setupDesktopModeLaunchParamsModifier();
-        doReturn(LETTERBOX_ASPECT_RATIO).when(()
-                -> calculateAspectRatio(any(), any()));
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, true);
+        final Task task = createTask(display, /* isResizeable */ true);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
+
+        spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+        doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
+                .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
 
         final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
                 - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
         final int desiredHeight = (int)
                 ((PORTRAIT_DISPLAY_BOUNDS.width() / LETTERBOX_ASPECT_RATIO) + 0.5f);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -461,14 +962,18 @@
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_PORTRAIT, false);
+        final Task task = createTask(display, /* isResizeable */ false);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_PORTRAIT,
+                task, /* ignoreOrientationRequest */ true);
+
 
         final int desiredWidth =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
         final int desiredHeight =
                 (int) (PORTRAIT_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -477,18 +982,23 @@
     @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
     public void testUnResizableLandscapeBounds_portraitDevice_unResizable_landscapeOrientation() {
         setupDesktopModeLaunchParamsModifier();
-        doReturn(LETTERBOX_ASPECT_RATIO).when(()
-                -> calculateAspectRatio(any(), any()));
 
         final TestDisplayContent display = createDisplayContent(ORIENTATION_PORTRAIT,
                 PORTRAIT_DISPLAY_BOUNDS);
-        final Task task = createTask(display, SCREEN_ORIENTATION_LANDSCAPE, false);
+        final Task task = createTask(display, /* isResizeable */ false);
+        final ActivityRecord activity = createActivity(display, SCREEN_ORIENTATION_LANDSCAPE,
+                task, /* ignoreOrientationRequest */ true);
+
+        spyOn(activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy());
+        doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
+                .getDesktopAppCompatAspectRatioPolicy()).calculateAspectRatio(any());
 
         final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
                 - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
         final int desiredHeight = (int) (desiredWidth / LETTERBOX_ASPECT_RATIO);
 
-        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task).calculate());
+        assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setTask(task)
+                .setActivity(activity).calculate());
         assertEquals(desiredWidth, mResult.mBounds.width());
         assertEquals(desiredHeight, mResult.mBounds.height());
     }
@@ -755,24 +1265,64 @@
         assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode);
     }
 
-    private Task createTask(DisplayContent display, int orientation, Boolean isResizeable) {
+    private Task createTask(DisplayContent display, Boolean isResizeable) {
         final int resizeMode = isResizeable ? RESIZE_MODE_RESIZEABLE
                 : RESIZE_MODE_UNRESIZEABLE;
         final Task task = new TaskBuilder(mSupervisor).setActivityType(
                 ACTIVITY_TYPE_STANDARD).setDisplay(display).build();
         task.setResizeMode(resizeMode);
-        mActivity = new ActivityBuilder(task.mAtmService)
+        return task;
+    }
+
+    private ActivityRecord createActivity(DisplayContent display, int orientation, Task task,
+            boolean ignoreOrientationRequest) {
+        final ActivityRecord activity = new ActivityBuilder(task.mAtmService)
                 .setTask(task)
+                .setComponent(ComponentName.createRelative(task.mAtmService.mContext,
+                        DesktopModeLaunchParamsModifierTests.class.getName()))
+                .setUid(android.os.Process.myUid())
                 .setScreenOrientation(orientation)
                 .setOnTop(true).build();
+        activity.onDisplayChanged(display);
+        activity.setOccludesParent(true);
+        activity.setVisible(true);
+        activity.setVisibleRequested(true);
+        activity.mDisplayContent.setIgnoreOrientationRequest(ignoreOrientationRequest);
 
-        mActivity.onDisplayChanged(display);
-        mActivity.setOccludesParent(true);
-        mActivity.setVisible(true);
-        mActivity.setVisibleRequested(true);
-        mActivity.mDisplayContent.setIgnoreOrientationRequest(/* ignoreOrientationRequest */ true);
+        return activity;
+    }
 
-        return task;
+    private void setDesiredAspectRatio(ActivityRecord activity, float aspectRatio) {
+        final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
+                activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        spyOn(desktopAppCompatAspectRatioPolicy);
+        doReturn(aspectRatio).when(desktopAppCompatAspectRatioPolicy)
+                .getDesiredAspectRatio(any());
+    }
+
+    private void applyUserMinAspectRatioOverride(ActivityRecord activity, int overrideCode,
+            float overrideValue) {
+        // Set desired aspect ratio to be below minimum so override can take effect.
+        final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
+                activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        spyOn(desktopAppCompatAspectRatioPolicy);
+        doReturn(1f).when(desktopAppCompatAspectRatioPolicy)
+                .getDesiredAspectRatio(any());
+
+        // Enable user aspect ratio settings
+        final AppCompatConfiguration appCompatConfiguration =
+                activity.mWmService.mAppCompatConfiguration;
+        spyOn(appCompatConfiguration);
+        doReturn(true).when(appCompatConfiguration)
+                .isUserAppAspectRatioSettingsEnabled();
+
+        // Simulate user min aspect ratio override being set.
+        final AppCompatAspectRatioOverrides appCompatAspectRatioOverrides =
+                activity.mAppCompatController.getAppCompatAspectRatioOverrides();
+        spyOn(appCompatAspectRatioOverrides);
+        doReturn(overrideValue).when(appCompatAspectRatioOverrides).getUserMinAspectRatio();
+        doReturn(overrideCode).when(appCompatAspectRatioOverrides)
+                .getUserMinAspectRatioOverrideCode();
     }
 
     private TestDisplayContent createDisplayContent(int orientation, Rect displayBounds) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 771e290..e57e36d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -59,6 +59,7 @@
 
         TestWindowContainer(WindowManagerService wm) {
             super(wm);
+            setVisibleRequested(true);
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
index f843386..14276ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -16,12 +16,16 @@
 
 package com.android.server.wm;
 
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 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.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -37,6 +41,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.LocalServices;
 import com.android.server.wm.TransitionController.OnStartCollect;
 import com.android.window.flags.Flags;
 
@@ -57,18 +62,29 @@
 public class DisplayContentDeferredUpdateTests extends WindowTestsBase {
 
     // The fields to override the current DisplayInfo.
-    private String mUniqueId;
+    private String mUniqueId = "initial_unique_id";
+    private String mSecondaryUniqueId = "secondary_initial_unique_id";
     private int mColorMode;
     private int mLogicalDensityDpi;
 
+    private DisplayContent mSecondaryDisplayContent;
+
     private final Message mScreenUnblocker = mock(Message.class);
+    private final Message mSecondaryScreenUnblocker = mock(Message.class);
+
+    private WindowManagerInternal mWmInternal;
 
     @Before
     public void before() {
+        when(mScreenUnblocker.getTarget()).thenReturn(mWm.mH);
         doReturn(true).when(mDisplayContent).getLastHasContent();
-        mockTransitionsController(/* enabled= */ true);
-        mockRemoteDisplayChangeController();
-        performInitialDisplayUpdate();
+
+        mockTransitionsController();
+
+        mockRemoteDisplayChangeController(mDisplayContent);
+        performInitialDisplayUpdate(mDisplayContent);
+
+        mWmInternal = LocalServices.getService(WindowManagerInternal.class);
     }
 
     @Test
@@ -245,24 +261,90 @@
         verify(mScreenUnblocker, never()).sendToTarget();
     }
 
-    private void mockTransitionsController(boolean enabled) {
-        spyOn(mDisplayContent.mTransitionController);
-        when(mDisplayContent.mTransitionController.isShellTransitionsEnabled()).thenReturn(enabled);
-        doReturn(true).when(mDisplayContent.mTransitionController).startCollectOrQueue(any(),
-                any());
+    @Test
+    public void testTwoDisplayUpdateAtTheSameTime_bothDisplaysAreUnblocked() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH);
+        prepareSecondaryDisplay();
+
+        final WindowState defaultDisplayWindow = createWindow(/* parent= */ null,
+                TYPE_BASE_APPLICATION, mDisplayContent, "DefaultDisplayWindow");
+        final WindowState secondaryDisplayWindow = createWindow(/* parent= */ null,
+                TYPE_BASE_APPLICATION, mSecondaryDisplayContent, "SecondaryDisplayWindow");
+        makeWindowVisibleAndNotDrawn(defaultDisplayWindow, secondaryDisplayWindow);
+
+        // Mark as display switching only for the default display as we filter out
+        // non-default display switching events in the display policy
+        mDisplayContent.mDisplayUpdater.onDisplaySwitching(/* switching= */ true);
+
+        mWmInternal.waitForAllWindowsDrawn(mScreenUnblocker,
+                /* timeout= */ Integer.MAX_VALUE, INVALID_DISPLAY);
+        mWmInternal.waitForAllWindowsDrawn(mSecondaryScreenUnblocker,
+                /* timeout= */ Integer.MAX_VALUE, mSecondaryDisplayContent.getDisplayId());
+
+        // Perform display update for both displays at the same time
+        mUniqueId = "new_default_display_unique_id";
+        mSecondaryUniqueId = "new_secondary_display_unique_id";
+        mDisplayContent.requestDisplayUpdate(mock(Runnable.class));
+        mSecondaryDisplayContent.requestDisplayUpdate(mock(Runnable.class));
+
+        when(mDisplayContent.mTransitionController.inTransition()).thenReturn(true);
+
+        // Notify that both transitions started collecting
+        captureStartTransitionCollection().getAllValues().forEach((callback) ->
+                callback.onCollectStarted(/* deferred= */ true));
+
+        // Verify that screens are not unblocked yet
+        verify(mScreenUnblocker, never()).sendToTarget();
+        verify(mSecondaryScreenUnblocker, never()).sendToTarget();
+
+        // Make all secondary display windows drawn
+        secondaryDisplayWindow.mWinAnimator.mDrawState = HAS_DRAWN;
+        mWm.mRoot.performSurfacePlacement();
+
+        // Verify that only secondary screen is unblocked as it uses
+        // the legacy waitForAllWindowsDrawn path
+        verify(mScreenUnblocker, never()).sendToTarget();
+        verify(mSecondaryScreenUnblocker).sendToTarget();
+
+        // Mark start transactions as presented
+        when(mDisplayContent.mTransitionController.inTransition()).thenReturn(false);
+        captureRequestedTransition().getAllValues().forEach(
+                this::makeTransitionTransactionCompleted);
+
+        // Verify that the default screen unblocker is sent only after start transaction
+        // of the Shell transition is presented
+        verify(mScreenUnblocker).sendToTarget();
     }
 
-    private void mockRemoteDisplayChangeController() {
-        spyOn(mDisplayContent.mRemoteDisplayChangeController);
-        doReturn(true).when(mDisplayContent.mRemoteDisplayChangeController)
+    private void prepareSecondaryDisplay() {
+        mSecondaryDisplayContent = createNewDisplay();
+        when(mSecondaryScreenUnblocker.getTarget()).thenReturn(mWm.mH);
+        doReturn(true).when(mSecondaryDisplayContent).getLastHasContent();
+        mockRemoteDisplayChangeController(mSecondaryDisplayContent);
+        performInitialDisplayUpdate(mSecondaryDisplayContent);
+    }
+
+    private void mockTransitionsController() {
+        spyOn(mDisplayContent.mTransitionController);
+        when(mDisplayContent.mTransitionController.isShellTransitionsEnabled())
+                .thenReturn(true);
+        doReturn(mock(Transition.class)).when(mDisplayContent.mTransitionController)
+                .createTransition(anyInt(), anyInt());
+        doReturn(true).when(mDisplayContent.mTransitionController)
+                .startCollectOrQueue(any(), any());
+    }
+
+    private void mockRemoteDisplayChangeController(DisplayContent displayContent) {
+        spyOn(displayContent.mRemoteDisplayChangeController);
+        doReturn(true).when(displayContent.mRemoteDisplayChangeController)
                 .performRemoteDisplayChange(anyInt(), anyInt(), any(), any());
     }
 
     private ArgumentCaptor<OnStartCollect> captureStartTransitionCollection() {
         ArgumentCaptor<OnStartCollect> callbackCaptor =
                 ArgumentCaptor.forClass(OnStartCollect.class);
-        verify(mDisplayContent.mTransitionController, atLeast(1)).startCollectOrQueue(any(),
-                callbackCaptor.capture());
+        verify(mDisplayContent.mTransitionController, atLeast(1))
+                .startCollectOrQueue(any(), callbackCaptor.capture());
         return callbackCaptor;
     }
 
@@ -283,20 +365,23 @@
         }
     }
 
-    private void performInitialDisplayUpdate() {
-        mUniqueId = "initial_unique_id";
+    private void performInitialDisplayUpdate(DisplayContent displayContent) {
         mColorMode = 0;
         mLogicalDensityDpi = 400;
 
-        spyOn(mDisplayContent.mDisplay);
+        spyOn(displayContent.mDisplay);
         doAnswer(invocation -> {
             DisplayInfo info = invocation.getArgument(0);
-            info.uniqueId = mUniqueId;
+            if (displayContent.isDefaultDisplay) {
+                info.uniqueId = mUniqueId;
+            } else {
+                info.uniqueId = mSecondaryUniqueId;
+            }
             info.colorMode = mColorMode;
             info.logicalDensityDpi = mLogicalDensityDpi;
             return null;
-        }).when(mDisplayContent.mDisplay).getDisplayInfo(any());
+        }).when(displayContent.mDisplay).getDisplayInfo(any());
         Runnable onUpdated = mock(Runnable.class);
-        mDisplayContent.requestDisplayUpdate(onUpdated);
+        displayContent.requestDisplayUpdate(onUpdated);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index ab0c8d4..eca4d21 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1144,6 +1144,7 @@
 
     @Test
     public void testOrientationBehind() {
+        assertNull(mDisplayContent.getLastOrientationSource());
         final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true)
                 .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build();
         prev.setVisibleRequested(false);
@@ -1732,25 +1733,6 @@
         assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
     }
 
-    @SetupWindows(addWindows = W_ACTIVITY)
-    @Test
-    public void testRotateSeamlesslyWithFixedRotation() {
-        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
-        final ActivityRecord app = mAppWindow.mActivityRecord;
-        mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
-        mAppWindow.mAttrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
-
-        // Use seamless rotation if the top app is rotated.
-        assertTrue(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */,
-                ROTATION_90 /* newRotation */, false /* forceUpdate */));
-
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(app);
-
-        // Use normal rotation because animating recents is an intermediate state.
-        assertFalse(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */,
-                ROTATION_90 /* newRotation */, false /* forceUpdate */));
-    }
-
     @Test
     public void testFixedRotationWithPip() {
         final DisplayContent displayContent = mDefaultDisplay;
@@ -1828,49 +1810,6 @@
         assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
     }
 
-    @Test
-    public void testRecentsNotRotatingWithFixedRotation() {
-        unblockDisplayRotation(mDisplayContent);
-        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
-        // Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb.
-        doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
-
-        final ActivityRecord activity = createActivityRecord(mDisplayContent);
-        final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
-        recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
-        doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
-
-        // Do not rotate if the recents animation is animating on top.
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
-        displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
-        assertFalse(displayRotation.updateRotationUnchecked(false));
-
-        // Rotation can be updated if the recents animation is finished.
-        mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
-        assertTrue(displayRotation.updateRotationUnchecked(false));
-
-        // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep).
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
-        displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
-        ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false;
-        assertTrue(displayRotation.updateRotationUnchecked(false));
-
-        // Rotation can be updated if the recents animation is animating but it is not on top, e.g.
-        // switching activities in different orientations by quickstep gesture.
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
-        mDisplayContent.setFixedRotationLaunchingAppUnchecked(activity);
-        displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
-        assertTrue(displayRotation.updateRotationUnchecked(false));
-
-        // The recents activity should not apply fixed rotation if the top activity is not opaque.
-        mDisplayContent.mFocusedApp = activity;
-        doReturn(false).when(mDisplayContent.mFocusedApp).occludesParent();
-        doReturn(ROTATION_90).when(mDisplayContent).rotationForActivityInDifferentOrientation(
-                eq(recentsActivity));
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
-        assertFalse(recentsActivity.hasFixedRotationTransform());
-    }
-
     @EnableFlags(com.android.window.flags.Flags.FLAG_RESPECT_NON_TOP_VISIBLE_FIXED_ORIENTATION)
     @Test
     public void testRespectNonTopVisibleFixedOrientation() {
@@ -2656,6 +2595,7 @@
      * display.
      */
     @Test
+    @SuppressWarnings("GuardedBy")
     public void testNotResumeHomeRootTaskOnRemovingDisplay() {
         // Create a display which supports system decoration and allows reparenting root tasks to
         // another display when the display is removed.
@@ -2678,7 +2618,8 @@
         // The removed display should have no focused root task and its home root task should never
         // resume.
         assertNull(display.getFocusedRootTask());
-        verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any());
+        verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(
+                any(), any(), anyBoolean());
     }
 
     /**
@@ -2794,15 +2735,17 @@
         final WindowState imeAppTarget =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
         mDisplayContent.setImeLayeringTarget(imeAppTarget);
-        spyOn(imeAppTarget);
-        doReturn(true).when(imeAppTarget).isRequestedVisible(ime());
+        imeAppTarget.setRequestedVisibleTypes(ime());
         assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
 
         // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
         final WindowState nextImeAppTarget =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
         makeWindowVisibleAndDrawn(nextImeAppTarget);
-        nextImeAppTarget.mActivityRecord.commitVisibility(false, false);
+        // Even if the app still requests IME, the ime dialog should not gain focus if the target
+        // app is invisible.
+        nextImeAppTarget.setRequestedVisibleTypes(ime());
+        nextImeAppTarget.mActivityRecord.setVisibility(false);
         mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
         assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 2dea6ba..8cf593f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -597,7 +597,6 @@
                 .build();
 
         spyOn(mActivity.mAtmService.getLifecycleManager());
-        spyOn(mActivity.mLetterboxUiController);
         spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides());
 
         doReturn(mActivity).when(mDisplayContent).topRunningActivity(anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index ddadbc4..ffe3893 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -244,6 +244,19 @@
         mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId());
         assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth);
         assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight);
+
+        // Forced size can be cleared even if the initial display size is smaller than max width.
+        final int maxWidth = mSecondaryDisplay.mInitialDisplayWidth - 20;
+        mSecondaryDisplay.setMaxUiWidth(maxWidth);
+        assertEquals(maxWidth, mSecondaryDisplay.mBaseDisplayWidth);
+        mSecondaryDisplay.setForcedSize(maxWidth - 10, maxWidth + 10);
+        assertNotEquals(maxWidth, mSecondaryDisplay.mBaseDisplayHeight);
+        assertTrue(mSecondaryDisplay.mIsSizeForced);
+
+        mWm.clearForcedDisplaySize(mSecondaryDisplay.mDisplayId);
+        assertFalse(mSecondaryDisplay.mIsSizeForced);
+        assertEquals(maxWidth, mSecondaryDisplay.mBaseDisplayWidth);
+        assertEquals(0, mSettingsProvider.getSettings(originalInfo).mForcedWidth);
     }
 
     @Test
@@ -309,6 +322,16 @@
     public void testPublicDisplayDefaultToMoveToPrimary() {
         assertEquals(REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY,
                 mDisplayWindowSettings.getRemoveContentModeLocked(mSecondaryDisplay));
+
+        // Sets the remove-content-mode and make sure the mode is updated.
+        mDisplayWindowSettings.setRemoveContentModeLocked(mSecondaryDisplay,
+                REMOVE_CONTENT_MODE_DESTROY);
+        final int removeContentMode = mDisplayWindowSettings.getRemoveContentModeLocked(
+                mSecondaryDisplay);
+        assertEquals(REMOVE_CONTENT_MODE_DESTROY, removeContentMode);
+
+        doReturn(removeContentMode).when(mSecondaryDisplay).getRemoveContentMode();
+        assertTrue(mSecondaryDisplay.shouldDestroyContentOnRemove());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index d8d5729..f70dceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -24,6 +24,8 @@
 
 import android.graphics.PixelFormat;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.view.inputmethod.Flags;
 import android.view.inputmethod.ImeTracker;
 
 import androidx.test.filters.SmallTest;
@@ -49,6 +51,7 @@
     public void setUp() throws Exception {
         mImeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();
         mImeProvider.getSource().setVisible(true);
+        mWm.mAnimator.ready();
     }
 
     @Test
@@ -72,6 +75,7 @@
      * Checks that scheduling with all the state set and manually triggering the show does succeed.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testScheduleShowIme() {
         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
         makeWindowVisibleAndDrawn(ime);
@@ -99,6 +103,7 @@
      * all the state becomes available.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testScheduleShowIme_noInitialState() {
         final WindowState target = createWindow(null, TYPE_APPLICATION, "app");
 
@@ -126,6 +131,7 @@
      * does continue and succeed when the runnable is started.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testScheduleShowIme_delayedAfterPrepareSurfaces() {
         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
         makeWindowVisibleAndDrawn(ime);
@@ -146,8 +152,8 @@
         assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertFalse(mImeProvider.isImeShowing());
 
-        // Starting the afterPrepareSurfacesRunnable picks up the show scheduled above.
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        // Waiting for the afterPrepareSurfacesRunnable picks up the show scheduled above.
+        waitUntilWindowAnimatorIdle();
         // No longer scheduled as it was already shown.
         assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
         assertTrue(mImeProvider.isImeShowing());
@@ -158,6 +164,7 @@
      * when the surface placement happens.
      */
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
     public void testScheduleShowIme_delayedSurfacePlacement() {
         final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
         makeWindowVisibleAndDrawn(ime);
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 6190807..e8d089c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -187,8 +187,8 @@
         assertEquals(mProvider.getControlTarget(), target);
         assertNull(mProvider.getLeash(target));
 
-        // After surface transactions are applied, the leash is ready for dispatching.
-        mProvider.onSurfaceTransactionApplied();
+        // Set the leash to be ready for dispatching.
+        mProvider.mIsLeashReadyForDispatching = true;
         assertNotNull(mProvider.getLeash(target));
 
         // We do have fake control for the fake control target, but that has no leash.
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 0dc56f8..d0d7c06 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -32,6 +32,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -202,6 +203,11 @@
         getController().onImeControlTargetChanged(base);
         base.setRequestedVisibleTypes(ime(), ime());
         getController().onRequestedVisibleTypesChanged(base, null /* statsToken */);
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            // to set the serverVisibility, the IME needs to be drawn and onPostLayout be called.
+            mImeWindow.mWinAnimator.mDrawState = HAS_DRAWN;
+            getController().onPostLayout();
+        }
 
         // Send our spy window (app) into the system so that we can detect the invocation.
         final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
@@ -382,6 +388,7 @@
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(app);
         mDisplayContent.getInsetsPolicy().showTransient(statusBars(),
                 true /* isGestureOnSystemBar */);
+        mWm.mAnimator.ready();
         waitUntilWindowAnimatorIdle();
 
         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(statusBars()));
@@ -500,6 +507,12 @@
         getController().onRequestedVisibleTypesChanged(app, null /* statsToken */);
         assertTrue(ime.getControllableInsetProvider().getSource().isVisible());
 
+        if (android.view.inputmethod.Flags.refactorInsetsController()) {
+            // The IME is only set to shown, after onPostLayout is called and all preconditions
+            // (serverVisible, no givenInsetsPending, etc.) are fulfilled
+            getController().getImeSourceProvider().onPostLayout();
+        }
+
         getController().updateAboveInsetsState(true /* notifyInsetsChange */);
         assertNotNull(app.getInsetsState().peekSource(ID_IME));
         verify(app, atLeastOnce()).notifyInsetsChanged();
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index ffaa2d8..400fe8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -45,6 +46,12 @@
 
 import java.util.function.Supplier;
 
+/**
+ * Test class for {@link Letterbox}.
+ * <p>
+ * Build/Install/Run:
+ * atest WmTests:LetterboxTest
+ */
 @SmallTest
 @Presubmit
 public class LetterboxTest {
@@ -53,21 +60,21 @@
     SurfaceControlMocker mSurfaces;
     SurfaceControl.Transaction mTransaction;
 
-    private boolean mAreCornersRounded = false;
-    private int mColor = Color.BLACK;
-    private boolean mHasWallpaperBackground = false;
-    private int mBlurRadius = 0;
-    private float mDarkScrimAlpha = 0.5f;
     private SurfaceControl mParentSurface = mock(SurfaceControl.class);
+    private AppCompatLetterboxOverrides mLetterboxOverrides;
 
     @Before
     public void setUp() throws Exception {
         mSurfaces = new SurfaceControlMocker();
+        mLetterboxOverrides =  mock(AppCompatLetterboxOverrides.class);
+        doReturn(false).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
+        doReturn(Color.valueOf(Color.BLACK)).when(mLetterboxOverrides)
+                .getLetterboxBackgroundColor();
+        doReturn(false).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
+        doReturn(0).when(mLetterboxOverrides).getLetterboxWallpaperBlurRadiusPx();
+        doReturn(0.5f).when(mLetterboxOverrides).getLetterboxWallpaperDarkScrimAlpha();
         mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
-                () -> mAreCornersRounded, () -> Color.valueOf(mColor),
-                () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
-                mock(AppCompatReachabilityPolicy.class),
-                () -> mParentSurface);
+                mock(AppCompatReachabilityPolicy.class), mLetterboxOverrides, () -> mParentSurface);
         mTransaction = spy(StubTransaction.class);
     }
 
@@ -183,7 +190,8 @@
 
         verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 0, 0});
 
-        mColor = Color.GREEN;
+        doReturn(Color.valueOf(Color.GREEN)).when(mLetterboxOverrides)
+                .getLetterboxBackgroundColor();
 
         assertTrue(mLetterbox.needsApplySurfaceChanges());
 
@@ -200,12 +208,12 @@
         verify(mTransaction).setAlpha(mSurfaces.top, 1.0f);
         assertFalse(mLetterbox.needsApplySurfaceChanges());
 
-        mHasWallpaperBackground = true;
+        doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
 
         assertTrue(mLetterbox.needsApplySurfaceChanges());
 
         applySurfaceChanges();
-        verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, mDarkScrimAlpha);
+        verify(mTransaction).setAlpha(mSurfaces.fullWindowSurface, /* alpha */ 0.5f);
     }
 
     @Test
@@ -234,7 +242,7 @@
 
     @Test
     public void testApplySurfaceChanges_cornersRounded_surfaceFullWindowSurfaceCreated() {
-        mAreCornersRounded = true;
+        doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
         applySurfaceChanges();
 
@@ -243,7 +251,7 @@
 
     @Test
     public void testApplySurfaceChanges_wallpaperBackground_surfaceFullWindowSurfaceCreated() {
-        mHasWallpaperBackground = true;
+        doReturn(true).when(mLetterboxOverrides).hasWallpaperBackgroundForLetterbox();
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
         applySurfaceChanges();
 
@@ -252,7 +260,7 @@
 
     @Test
     public void testNotIntersectsOrFullyContains_cornersRounded() {
-        mAreCornersRounded = true;
+        doReturn(true).when(mLetterboxOverrides).shouldLetterboxHaveRoundedCorners();
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
         applySurfaceChanges();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
deleted file mode 100644
index 695068a..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ /dev/null
@@ -1,313 +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.server.wm;
-
-import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
-
-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.spyOn;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.Nullable;
-import android.compat.testing.PlatformCompatChangeRule;
-import android.content.ComponentName;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.view.InsetsSource;
-import android.view.InsetsState;
-import android.view.RoundedCorner;
-import android.view.RoundedCorners;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.R;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-/**
- * Test class for {@link LetterboxUiController}.
- *
- * Build/Install/Run:
- * atest WmTests:LetterboxUiControllerTest
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class LetterboxUiControllerTest extends WindowTestsBase {
-    private static final int TASKBAR_COLLAPSED_HEIGHT = 10;
-    private static final int TASKBAR_EXPANDED_HEIGHT = 20;
-    private static final int SCREEN_WIDTH = 200;
-    private static final int SCREEN_HEIGHT = 100;
-    private static final Rect TASKBAR_COLLAPSED_BOUNDS = new Rect(0,
-            SCREEN_HEIGHT - TASKBAR_COLLAPSED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
-    private static final Rect TASKBAR_EXPANDED_BOUNDS = new Rect(0,
-            SCREEN_HEIGHT - TASKBAR_EXPANDED_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT);
-
-    @Rule
-    public TestRule compatChangeRule = new PlatformCompatChangeRule();
-
-    private ActivityRecord mActivity;
-    private Task mTask;
-    private DisplayContent mDisplayContent;
-    private LetterboxUiController mController;
-    private AppCompatConfiguration mAppCompatConfiguration;
-    private final Rect mLetterboxedPortraitTaskBounds = new Rect();
-
-    @Before
-    public void setUp() throws Exception {
-        mActivity = setUpActivityWithComponent();
-
-        mAppCompatConfiguration = mWm.mAppCompatConfiguration;
-        spyOn(mAppCompatConfiguration);
-
-        mController = new LetterboxUiController(mWm, mActivity);
-    }
-
-    @Test
-    public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
-        final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
-                WindowInsets.Type.navigationBars());
-        taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
-        final Rect opaqueBounds = new Rect(0, 0, 500, 300);
-        doReturn(opaqueBounds).when(mActivity).getBounds();
-        // Activity is translucent
-        spyOn(mActivity.mAppCompatController.getTransparentPolicy());
-        when(mActivity.mAppCompatController.getTransparentPolicy()
-                .isRunning()).thenReturn(true);
-
-        // Makes requested sizes different
-        mainWindow.mRequestedWidth = opaqueBounds.width() - 1;
-        mainWindow.mRequestedHeight = opaqueBounds.height() - 1;
-        assertNull(mActivity.mLetterboxUiController.getCropBoundsIfNeeded(mainWindow));
-
-        // Makes requested sizes equals
-        mainWindow.mRequestedWidth = opaqueBounds.width();
-        mainWindow.mRequestedHeight = opaqueBounds.height();
-        assertNotNull(mActivity.mLetterboxUiController.getCropBoundsIfNeeded(mainWindow));
-    }
-
-    @Test
-    public void testGetCropBoundsIfNeeded_noCrop() {
-        final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
-                WindowInsets.Type.navigationBars());
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
-
-        // Do not apply crop if taskbar is collapsed
-        taskbar.setFrame(TASKBAR_COLLAPSED_BOUNDS);
-        assertNull(mController.getExpandedTaskbarOrNull(mainWindow));
-
-        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4,
-                SCREEN_WIDTH - SCREEN_WIDTH / 4, SCREEN_HEIGHT - SCREEN_HEIGHT / 4);
-
-        final Rect noCrop = mController.getCropBoundsIfNeeded(mainWindow);
-        assertNotEquals(null, noCrop);
-        assertEquals(0, noCrop.left);
-        assertEquals(0, noCrop.top);
-        assertEquals(mLetterboxedPortraitTaskBounds.width(), noCrop.right);
-        assertEquals(mLetterboxedPortraitTaskBounds.height(), noCrop.bottom);
-    }
-
-    @Test
-    public void testGetCropBoundsIfNeeded_appliesCrop() {
-        final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
-                WindowInsets.Type.navigationBars());
-        taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
-
-        // Apply crop if taskbar is expanded
-        taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
-        assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
-
-        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
-                SCREEN_HEIGHT);
-
-        final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
-        assertNotEquals(null, crop);
-        assertEquals(0, crop.left);
-        assertEquals(0, crop.top);
-        assertEquals(mLetterboxedPortraitTaskBounds.width(), crop.right);
-        assertEquals(mLetterboxedPortraitTaskBounds.height() - TASKBAR_EXPANDED_HEIGHT,
-                crop.bottom);
-    }
-
-    @Test
-    public void testGetCropBoundsIfNeeded_appliesCropWithSizeCompatScaling() {
-        final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
-                WindowInsets.Type.navigationBars());
-        taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
-        final float scaling = 2.0f;
-
-        // Apply crop if taskbar is expanded
-        taskbar.setFrame(TASKBAR_EXPANDED_BOUNDS);
-        assertNotNull(mController.getExpandedTaskbarOrNull(mainWindow));
-        // With SizeCompat scaling
-        doReturn(true).when(mActivity).inSizeCompatMode();
-        mainWindow.mInvGlobalScale = scaling;
-
-        mLetterboxedPortraitTaskBounds.set(SCREEN_WIDTH / 4, 0, SCREEN_WIDTH - SCREEN_WIDTH / 4,
-                SCREEN_HEIGHT);
-
-        final int appWidth = mLetterboxedPortraitTaskBounds.width();
-        final int appHeight = mLetterboxedPortraitTaskBounds.height();
-
-        final Rect crop = mController.getCropBoundsIfNeeded(mainWindow);
-        assertNotEquals(null, crop);
-        assertEquals(0, crop.left);
-        assertEquals(0, crop.top);
-        assertEquals((int) (appWidth * scaling), crop.right);
-        assertEquals((int) ((appHeight - TASKBAR_EXPANDED_HEIGHT) * scaling), crop.bottom);
-    }
-
-    @Test
-    public void testGetRoundedCornersRadius_withRoundedCornersFromInsets() {
-        final float invGlobalScale = 0.5f;
-        final int expectedRadius = 7;
-        final int configurationRadius = 15;
-
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
-        mainWindow.mInvGlobalScale = invGlobalScale;
-        final InsetsState insets = mainWindow.getInsetsState();
-
-        RoundedCorners roundedCorners = new RoundedCorners(
-                /*topLeft=*/ null,
-                /*topRight=*/ null,
-                /*bottomRight=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT,
-                    configurationRadius, /*centerX=*/ 1, /*centerY=*/ 1),
-                /*bottomLeft=*/ new RoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT,
-                    configurationRadius * 2 /*2 is to test selection of the min radius*/,
-                    /*centerX=*/ 1, /*centerY=*/ 1)
-        );
-        insets.setRoundedCorners(roundedCorners);
-        mAppCompatConfiguration.setLetterboxActivityCornersRadius(-1);
-
-        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
-    }
-
-    @Test
-    public void testGetRoundedCornersRadius_withLetterboxActivityCornersRadius() {
-        final float invGlobalScale = 0.5f;
-        final int expectedRadius = 7;
-        final int configurationRadius = 15;
-
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
-        mainWindow.mInvGlobalScale = invGlobalScale;
-        mAppCompatConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
-
-        doReturn(true).when(mActivity).isInLetterboxAnimation();
-        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
-
-        doReturn(false).when(mActivity).isInLetterboxAnimation();
-        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
-
-        doReturn(false).when(mActivity).isVisibleRequested();
-        doReturn(false).when(mActivity).isVisible();
-        assertEquals(0, mController.getRoundedCornersRadius(mainWindow));
-
-        doReturn(true).when(mActivity).isInLetterboxAnimation();
-        assertEquals(expectedRadius, mController.getRoundedCornersRadius(mainWindow));
-    }
-
-    @Test
-    public void testGetRoundedCornersRadius_noScalingApplied() {
-        final int configurationRadius = 15;
-
-        final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(/*taskbar=*/ null);
-        mAppCompatConfiguration.setLetterboxActivityCornersRadius(configurationRadius);
-
-        mainWindow.mInvGlobalScale = -1f;
-        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
-
-        mainWindow.mInvGlobalScale = 0f;
-        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
-
-        mainWindow.mInvGlobalScale = 1f;
-        assertEquals(configurationRadius, mController.getRoundedCornersRadius(mainWindow));
-    }
-
-    private WindowState mockForGetCropBoundsAndRoundedCorners(@Nullable InsetsSource taskbar) {
-        final WindowState mainWindow = mock(WindowState.class);
-        final InsetsState insets = new InsetsState();
-        final Resources resources = mWm.mContext.getResources();
-        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams();
-
-        mainWindow.mInvGlobalScale = 1f;
-        spyOn(resources);
-        spyOn(mActivity);
-        spyOn(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy());
-
-        if (taskbar != null) {
-            taskbar.setVisible(true);
-            insets.addSource(taskbar);
-        }
-        doReturn(mLetterboxedPortraitTaskBounds).when(mActivity).getBounds();
-        doReturn(false).when(mActivity).isInLetterboxAnimation();
-        doReturn(true).when(mActivity).isVisible();
-        doReturn(true).when(mActivity.mAppCompatController
-                .getAppCompatAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
-        doReturn(insets).when(mainWindow).getInsetsState();
-        doReturn(attrs).when(mainWindow).getAttrs();
-        doReturn(true).when(mainWindow).isDrawn();
-        doReturn(true).when(mainWindow).isOnScreen();
-        doReturn(false).when(mainWindow).isLetterboxedForDisplayCutout();
-        doReturn(true).when(mainWindow).areAppWindowBoundsLetterboxed();
-        doReturn(true).when(mAppCompatConfiguration).isLetterboxActivityCornersRounded();
-        doReturn(TASKBAR_EXPANDED_HEIGHT).when(resources).getDimensionPixelSize(
-                R.dimen.taskbar_frame_height);
-
-        // Need to reinitialise due to the change in resources getDimensionPixelSize output.
-        mController = new LetterboxUiController(mWm, mActivity);
-
-        return mainWindow;
-    }
-
-    @Test
-    public void testIsLetterboxEducationEnabled() {
-        mController.isLetterboxEducationEnabled();
-        verify(mAppCompatConfiguration).getIsEducationEnabled();
-    }
-
-    private ActivityRecord setUpActivityWithComponent() {
-        mDisplayContent = new TestDisplayContent
-                .Builder(mAtm, /* dw */ 1000, /* dh */ 2000).build();
-        mTask = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
-        final ActivityRecord activity = new ActivityBuilder(mAtm)
-                .setOnTop(true)
-                .setTask(mTask)
-                // Set the component to be that of the test class in order to enable compat changes
-                .setComponent(ComponentName.createRelative(mContext,
-                        com.android.server.wm.LetterboxUiControllerTest.class.getName()))
-                .build();
-        spyOn(activity.mAppCompatController.getAppCompatCameraOverrides());
-        return activity;
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b95f621..8f3d3c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1481,9 +1481,6 @@
         assertSecurityException(expectCallable,
                 () -> mAtm.unregisterTaskStackListener(null));
         assertSecurityException(expectCallable, () -> mAtm.cancelTaskWindowTransition(0));
-        assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, 0,
-                null));
-        assertSecurityException(expectCallable, () -> mAtm.cancelRecentsAnimation(true));
         assertSecurityException(expectCallable, () -> mAtm.stopAppSwitches());
         assertSecurityException(expectCallable, () -> mAtm.resumeAppSwitches());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
deleted file mode 100644
index 63e3e5c..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ /dev/null
@@ -1,834 +0,0 @@
-/*
- * 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.wm;
-
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
-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.spy;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
-import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseBooleanArray;
-import android.view.IRecentsAnimationRunner;
-import android.view.SurfaceControl;
-import android.view.WindowManager.LayoutParams;
-import android.window.TaskSnapshot;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-
-/**
- * Build/Install/Run:
- *  atest WmTests:RecentsAnimationControllerTest
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class RecentsAnimationControllerTest extends WindowTestsBase {
-
-    @Mock SurfaceControl mMockLeash;
-    @Mock SurfaceControl.Transaction mMockTransaction;
-    @Mock OnAnimationFinishedCallback mFinishedCallback;
-    @Mock IRecentsAnimationRunner mMockRunner;
-    @Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks;
-    @Mock TaskSnapshot mMockTaskSnapshot;
-    private RecentsAnimationController mController;
-    private Task mRootHomeTask;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        doNothing().when(mWm.mRoot).performSurfacePlacement();
-        when(mMockRunner.asBinder()).thenReturn(new Binder());
-        mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
-                DEFAULT_DISPLAY));
-        mRootHomeTask = mDefaultDisplay.getDefaultTaskDisplayArea().getRootHomeTask();
-        assertNotNull(mRootHomeTask);
-    }
-
-    @Test
-    public void testRemovedBeforeStarted_expectCanceled() throws Exception {
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
-                false /* isRecentTaskInvisible */);
-        adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
-                mFinishedCallback);
-
-        // The activity doesn't contain window so the animation target cannot be created.
-        mController.startAnimation();
-
-        // Verify that the finish callback to reparent the leash is called
-        verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), eq(adapter));
-        // Verify the animation canceled callback to the app was made
-        verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
-        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
-    }
-
-    @Test
-    public void testCancelAfterRemove_expectIgnored() {
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        AnimationAdapter adapter = mController.addAnimation(activity.getTask(),
-                false /* isRecentTaskInvisible */);
-        adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_RECENTS,
-                mFinishedCallback);
-
-        // Remove the app window so that the animation target can not be created
-        activity.removeImmediately();
-        mController.startAnimation();
-        mController.cleanupAnimation(REORDER_KEEP_IN_PLACE);
-        try {
-            mController.cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "test");
-        } catch (Exception e) {
-            fail("Unexpected failure when canceling animation after finishing it");
-        }
-    }
-
-    @Test
-    public void testIncludedApps_expectTargetAndVisible() {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final ActivityRecord hiddenActivity = createActivityRecord(mDefaultDisplay);
-        hiddenActivity.setVisible(false);
-        mDefaultDisplay.getConfiguration().windowConfiguration.setRotation(
-                mDefaultDisplay.getRotation());
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        // Ensure that we are animating the target activity as well
-        assertTrue(mController.isAnimatingTask(homeActivity.getTask()));
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-        assertFalse(mController.isAnimatingTask(hiddenActivity.getTask()));
-    }
-
-    @Test
-    public void testLaunchAndStartRecents_expectTargetAndVisible() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final Task task = createTask(mDefaultDisplay);
-        // Emulate that activity1 has just launched activity2, but app transition has not yet been
-        // executed.
-        final ActivityRecord activity1 = createActivityRecord(task);
-        activity1.setVisible(true);
-        activity1.setVisibleRequested(false);
-        activity1.addWindow(createWindowState(new LayoutParams(TYPE_BASE_APPLICATION), activity1));
-
-        final ActivityRecord activity2 = createActivityRecord(task);
-        activity2.setVisible(false);
-        activity2.setVisibleRequested(true);
-
-        mDefaultDisplay.getConfiguration().windowConfiguration.setRotation(
-                mDefaultDisplay.getRotation());
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.startAnimation();
-        verify(mMockRunner, never()).onAnimationCanceled(null /* taskIds */,
-                null /* taskSnapshots */);
-    }
-
-    @Test
-    public void testWallpaperIncluded_expectTarget() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
-                mock(IBinder.class), true, mDefaultDisplay, true /* ownerCanManageAppTokens */);
-        spyOn(mDefaultDisplay.mWallpaperController);
-        doReturn(true).when(mDefaultDisplay.mWallpaperController).isWallpaperVisible();
-
-        mDefaultDisplay.getConfiguration().windowConfiguration.setRotation(
-                mDefaultDisplay.getRotation());
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.startAnimation();
-
-        // Ensure that we are animating the app and wallpaper target
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-        assertTrue(mController.isAnimatingWallpaper(wallpaperWindowToken));
-    }
-
-    @Test
-    public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
-                mock(IBinder.class), true, mDefaultDisplay, true /* ownerCanManageAppTokens */);
-        spyOn(mDefaultDisplay.mWallpaperController);
-        doReturn(true).when(mDefaultDisplay.mWallpaperController).isWallpaperVisible();
-
-        mDefaultDisplay.getConfiguration().windowConfiguration.setRotation(
-                mDefaultDisplay.getRotation());
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.startAnimation();
-
-        // Cancel the animation and ensure the controller is still running
-        wallpaperWindowToken.cancelAnimation();
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-        assertFalse(mController.isAnimatingWallpaper(wallpaperWindowToken));
-        verify(mMockRunner, never()).onAnimationCanceled(null /* taskIds */,
-                null /* taskSnapshots */);
-    }
-
-    @Test
-    public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final WindowState hwin1 = createWindow(null, TYPE_BASE_APPLICATION, homeActivity, "hwin1");
-        homeActivity.addWindow(hwin1);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
-                mock(IBinder.class), true, mDefaultDisplay, true /* ownerCanManageAppTokens */);
-        spyOn(mDefaultDisplay.mWallpaperController);
-        doReturn(true).when(mDefaultDisplay.mWallpaperController).isWallpaperVisible();
-
-        // Start and finish the animation
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.startAnimation();
-
-        assertTrue(mController.isAnimatingTask(homeActivity.getTask()));
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-
-        // Reset at this point since we may remove adapters that couldn't be created
-        clearInvocations(mController);
-        mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
-
-        // Ensure that we remove the task (home & app) and wallpaper adapters
-        verify(mController, times(2)).removeAnimation(any());
-        verify(mController, times(1)).removeWallpaperAnimation(any());
-    }
-
-    @Test
-    public void testDeferCancelAnimation() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        assertEquals(activity.getTask().getTopVisibleActivity(), activity);
-        assertEquals(activity.findMainWindow(), win1);
-
-        mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-
-        mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
-        mController.cancelAnimationWithScreenshot(false /* screenshot */);
-        verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
-
-        // Simulate the app transition finishing
-        mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0);
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
-    }
-
-    @Test
-    public void testDeferCancelAnimationWithScreenShot() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        assertEquals(activity.getTask().getTopVisibleActivity(), activity);
-        assertEquals(activity.findMainWindow(), win1);
-
-        RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation(
-                activity.getTask(), false /* isRecentTaskInvisible */);
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-
-        spyOn(mWm.mTaskSnapshotController);
-        doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
-                anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
-        mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
-        mController.cancelAnimationWithScreenshot(true /* screenshot */);
-        verify(mMockRunner).onAnimationCanceled(any(int[].class) /* taskIds */,
-                any(TaskSnapshot[].class) /* taskSnapshots */);
-
-        // Continue the animation (simulating a call to cleanupScreenshot())
-        mController.continueDeferredCancelAnimation();
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
-    }
-
-    @Test
-    public void testShouldAnimateWhenNoCancelWithDeferredScreenshot() {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-        assertEquals(activity.getTask().getTopVisibleActivity(), activity);
-        assertEquals(activity.findMainWindow(), win1);
-
-        mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-
-        // Assume activity transition should animate when no
-        // IRecentsAnimationController#setDeferCancelUntilNextTransition called.
-        assertFalse(mController.shouldDeferCancelWithScreenshot());
-        assertTrue(activity.shouldAnimate());
-    }
-
-    @Test
-    public void testBinderDiedAfterCancelWithDeferredScreenshot() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.setWillFinishToHome(true);
-
-        // Verify cancel is called with a snapshot and that we've created an overlay
-        spyOn(mWm.mTaskSnapshotController);
-        doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
-                anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
-        mController.cancelAnimationWithScreenshot(true /* screenshot */);
-        verify(mMockRunner).onAnimationCanceled(any(), any());
-
-        // Simulate process crashing and ensure the animation is still canceled
-        mController.binderDied();
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
-    }
-
-    @Test
-    public void testRecentViewInFixedPortraitWhenTopAppInLandscape() {
-        makeDisplayPortrait(mDefaultDisplay);
-        unblockDisplayRotation(mDefaultDisplay);
-        mWm.setRecentsAnimationController(mController);
-
-        final ActivityRecord homeActivity = createHomeActivity();
-        homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-
-        final ActivityRecord landActivity = createActivityRecord(mDefaultDisplay);
-        landActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, landActivity, "win1");
-        landActivity.addWindow(win1);
-
-        assertEquals(landActivity.getTask().getTopVisibleActivity(), landActivity);
-        assertEquals(landActivity.findMainWindow(), win1);
-
-        // Ensure that the display is in Landscape
-        landActivity.onDescendantOrientationChanged(landActivity);
-        assertEquals(Configuration.ORIENTATION_LANDSCAPE,
-                mDefaultDisplay.getConfiguration().orientation);
-
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        assertTrue(mDefaultDisplay.isFixedRotationLaunchingApp(homeActivity));
-
-        // Check that the home app is in portrait
-        assertEquals(Configuration.ORIENTATION_PORTRAIT,
-                homeActivity.getConfiguration().orientation);
-
-        // Home activity won't become top (return to landActivity), so the top rotated record should
-        // be cleared.
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        assertFalse(mDefaultDisplay.isFixedRotationLaunchingApp(homeActivity));
-        assertFalse(mDefaultDisplay.hasTopFixedRotationLaunchingApp());
-        // The transform should keep until the transition is done, so the restored configuration
-        // won't be sent to activity and cause unnecessary configuration change.
-        assertTrue(homeActivity.hasFixedRotationTransform());
-
-        // In real case the transition will be executed from RecentsAnimation#finishAnimation.
-        mDefaultDisplay.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(
-                homeActivity.token);
-        assertFalse(homeActivity.hasFixedRotationTransform());
-    }
-
-    private ActivityRecord prepareFixedRotationLaunchingAppWithRecentsAnim() {
-        final ActivityRecord homeActivity = createHomeActivity();
-        homeActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        // Add a window so it can be animated by the recents.
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
-        activity.addWindow(win);
-        // Assume an activity is launching to different rotation.
-        mDefaultDisplay.setFixedRotationLaunchingApp(activity,
-                (mDefaultDisplay.getRotation() + 1) % 4);
-
-        assertTrue(activity.hasFixedRotationTransform());
-        assertTrue(mDefaultDisplay.isFixedRotationLaunchingApp(activity));
-
-        // Before the transition is done, the recents animation is triggered.
-        initializeRecentsAnimationController(mController, homeActivity);
-        assertFalse(homeActivity.hasFixedRotationTransform());
-        assertTrue(mController.isAnimatingTask(activity.getTask()));
-
-        return activity;
-    }
-
-    @Test
-    public void testClearFixedRotationLaunchingAppAfterCleanupAnimation() {
-        final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim();
-
-        // Simulate giving up the swipe up gesture to keep the original activity as top.
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        // The rotation transform should be cleared after updating orientation with display.
-        assertTopFixedRotationLaunchingAppCleared(activity);
-
-        // Simulate swiping up recents (home) in different rotation.
-        final ActivityRecord home = mDefaultDisplay.getDefaultTaskDisplayArea().getHomeActivity();
-        startRecentsInDifferentRotation(home);
-
-        // If the recents activity becomes the top running activity (e.g. the original top activity
-        // is either finishing or moved to back during recents animation), the display orientation
-        // will be determined by it so the fixed rotation must be cleared.
-        activity.finishing = true;
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        assertTopFixedRotationLaunchingAppCleared(home);
-
-        startRecentsInDifferentRotation(home);
-        // Assume recents activity becomes invisible for some reason (e.g. screen off).
-        home.setVisible(false);
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        // Although there won't be a transition finish callback, the fixed rotation must be cleared.
-        assertTopFixedRotationLaunchingAppCleared(home);
-    }
-
-    @Test
-    public void testKeepFixedRotationWhenMovingRecentsToTop() {
-        final ActivityRecord activity = prepareFixedRotationLaunchingAppWithRecentsAnim();
-        // Assume a transition animation has started running before recents animation. Then the
-        // activity will receive onAnimationFinished that notifies app transition finished when
-        // removing the recents animation of task.
-        activity.getTask().getAnimationSources().add(activity);
-
-        // Simulate swiping to home/recents before the transition is done.
-        mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
-        // The rotation transform should be preserved. In real case, it will be cleared by the next
-        // move-to-top transition.
-        assertTrue(activity.hasFixedRotationTransform());
-    }
-
-    @Test
-    public void testCheckRotationAfterCleanup() {
-        mWm.setRecentsAnimationController(mController);
-        spyOn(mDisplayContent.mFixedRotationTransitionListener);
-        final ActivityRecord recents = mock(ActivityRecord.class);
-        recents.setOverrideOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
-        doReturn(ORIENTATION_PORTRAIT).when(recents)
-                .getRequestedConfigurationOrientation(anyBoolean());
-        mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
-
-        // Rotation update is skipped while the recents animation is running.
-        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
-        final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
-        assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
-        assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, displayRotation.getLastOrientation());
-        final int prevRotation = mDisplayContent.getRotation();
-        mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-
-        // In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout
-        // -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent#
-        // applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG.
-        assertTrue(displayRotation.updateOrientation(topOrientation, false  /* forceUpdate */));
-        // The display should be updated to the changed orientation after the animation is finished.
-        assertNotEquals(displayRotation.getRotation(), prevRotation);
-    }
-
-    @Test
-    public void testWallpaperHasFixedRotationApplied() {
-        makeDisplayPortrait(mDefaultDisplay);
-        unblockDisplayRotation(mDefaultDisplay);
-        mWm.setRecentsAnimationController(mController);
-
-        // Create a portrait home activity, a wallpaper and a landscape activity displayed on top.
-        final ActivityRecord homeActivity = createHomeActivity();
-        homeActivity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-
-        final WindowState homeWindow = createWindow(null, TYPE_BASE_APPLICATION, homeActivity,
-                "homeWindow");
-        makeWindowVisible(homeWindow);
-        homeActivity.addWindow(homeWindow);
-        homeWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
-
-        // Landscape application
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState applicationWindow = createWindow(null, TYPE_BASE_APPLICATION, activity,
-                "applicationWindow");
-        activity.addWindow(applicationWindow);
-        activity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
-
-        // Wallpaper
-        final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
-                mock(IBinder.class), true, mDefaultDisplay, true /* ownerCanManageAppTokens */);
-        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
-                "wallpaperWindow");
-
-        // Make sure the landscape activity is on top and the display is in landscape
-        activity.moveFocusableActivityToTop("test");
-        mDefaultDisplay.getConfiguration().windowConfiguration.setRotation(
-                mDefaultDisplay.getRotation());
-
-        spyOn(mDefaultDisplay.mWallpaperController);
-        doReturn(true).when(mDefaultDisplay.mWallpaperController).isWallpaperVisible();
-
-        // Start the recents animation
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        mDefaultDisplay.mWallpaperController.adjustWallpaperWindows();
-
-        // Check preconditions
-        ArrayList<WallpaperWindowToken> wallpapers = new ArrayList<>(1);
-        mDefaultDisplay.forAllWallpaperWindows(wallpapers::add);
-
-        Truth.assertThat(wallpapers).hasSize(1);
-        Truth.assertThat(wallpapers.get(0).getTopChild()).isEqualTo(wallpaperWindow);
-
-        // Actual check
-        assertEquals(Configuration.ORIENTATION_PORTRAIT,
-                wallpapers.get(0).getConfiguration().orientation);
-
-        mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
-        // The transform state should keep because we expect to listen the signal from the
-        // transition executed by moving the task to front.
-        assertTrue(homeActivity.hasFixedRotationTransform());
-        assertTrue(mDefaultDisplay.isFixedRotationLaunchingApp(homeActivity));
-
-        mDefaultDisplay.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(
-                homeActivity.token);
-        // Wallpaper's transform state should be cleared with home.
-        assertFalse(homeActivity.hasFixedRotationTransform());
-        assertFalse(wallpaperWindowToken.hasFixedRotationTransform());
-    }
-
-    @Test
-    public void testIsAnimatingByRecents() {
-        final ActivityRecord homeActivity = createHomeActivity();
-        final Task rootTask = createTask(mDefaultDisplay);
-        final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */);
-        final Task leafTask = createTaskInRootTask(childTask, 0 /* userId */);
-        spyOn(leafTask);
-        doReturn(true).when(leafTask).isVisible();
-
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        // Verify RecentsAnimationController will animate visible leaf task by default.
-        verify(mController).addAnimation(eq(leafTask), anyBoolean(), anyBoolean(), any());
-        assertTrue(leafTask.isAnimatingByRecents());
-
-        // Make sure isAnimatingByRecents will also return true when it called by the parent task.
-        assertTrue(rootTask.isAnimatingByRecents());
-        assertTrue(childTask.isAnimatingByRecents());
-    }
-
-    @Test
-    public void testRestoreNavBarWhenEnteringRecents_expectAnimation() {
-        setupForShouldAttachNavBarDuringTransition();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final ActivityRecord homeActivity = createHomeActivity();
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
-        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
-
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(false));
-        verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
-        verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
-        assertTrue(mController.isNavigationBarAttachedToApp());
-
-        mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
-        verify(mController).restoreNavigationBarFromApp(eq(true));
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(true));
-        verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
-        assertFalse(mController.isNavigationBarAttachedToApp());
-        assertTrue(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
-    }
-
-    @Test
-    public void testRestoreNavBarWhenBackToApp_expectNoAnimation() {
-        setupForShouldAttachNavBarDuringTransition();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final ActivityRecord homeActivity = createHomeActivity();
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
-        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
-
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(false));
-        verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
-        verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
-        assertTrue(mController.isNavigationBarAttachedToApp());
-
-        final WindowContainer parent = navToken.getParent();
-
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        verify(mController).restoreNavigationBarFromApp(eq(false));
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(true));
-        verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
-        verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
-        assertFalse(mController.isNavigationBarAttachedToApp());
-        assertFalse(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
-    }
-
-    @Test
-    public void testAddTaskToTargets_expectAnimation() {
-        setupForShouldAttachNavBarDuringTransition();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final ActivityRecord homeActivity = createHomeActivity();
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
-        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
-
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(false));
-        verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
-        verify(transaction).setLayer(navToken.getSurfaceControl(), Integer.MAX_VALUE);
-        assertTrue(mController.isNavigationBarAttachedToApp());
-
-        final WindowContainer parent = navToken.getParent();
-
-        mController.addTaskToTargets(createTask(mDefaultDisplay), (type, anim) -> {});
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        verify(mController).restoreNavigationBarFromApp(eq(true));
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(true));
-        verify(transaction).setLayer(navToken.getSurfaceControl(), 0);
-        assertFalse(mController.isNavigationBarAttachedToApp());
-        assertTrue(navToken.isAnimating(ANIMATION_TYPE_TOKEN_TRANSFORM));
-    }
-
-    @Test
-    public void testNotAttachNavigationBar_controlledByFadeRotationAnimation() {
-        setupForShouldAttachNavBarDuringTransition();
-        AsyncRotationController mockController =
-                mock(AsyncRotationController.class);
-        doReturn(mockController).when(mDefaultDisplay).getAsyncRotationController();
-        final ActivityRecord homeActivity = createHomeActivity();
-        initializeRecentsAnimationController(mController, homeActivity);
-        assertFalse(mController.isNavigationBarAttachedToApp());
-    }
-
-    @Test
-    public void testAttachNavBarInSplitScreenMode() {
-        setupForShouldAttachNavBarDuringTransition();
-        TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm);
-        final ActivityRecord primary = createActivityRecordWithParentTask(
-                organizer.createTaskToPrimary(true));
-        final ActivityRecord secondary = createActivityRecordWithParentTask(
-                organizer.createTaskToSecondary(true));
-        final ActivityRecord homeActivity = createHomeActivity();
-        homeActivity.setVisibility(true);
-        initializeRecentsAnimationController(mController, homeActivity);
-
-        WindowState navWindow = mController.getNavigationBarWindow();
-        final WindowToken navToken = navWindow.mToken;
-        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
-
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(false));
-        verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top);
-        verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl());
-        assertTrue(mController.isNavigationBarAttachedToApp());
-        reset(navWindow);
-
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        final WindowContainer parent = navToken.getParent();
-        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
-                eq(mDefaultDisplay.mDisplayId), eq(true));
-        verify(navWindow).setSurfaceTranslationY(0);
-        verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
-        verify(mController).restoreNavigationBarFromApp(eq(false));
-        assertFalse(mController.isNavigationBarAttachedToApp());
-    }
-
-    @Test
-    public void testCleanupAnimation_expectExitAnimationDone() {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.startAnimation();
-
-        spyOn(win1);
-        spyOn(win1.mWinAnimator);
-        // Simulate when the window is exiting and cleanupAnimation invoked
-        // (e.g. screen off during RecentsAnimation animating), will expect the window receives
-        // onExitAnimationDone to destroy the surface when the removal is allowed.
-        win1.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
-        win1.mHasSurface = true;
-        win1.mAnimatingExit = true;
-        win1.mRemoveOnExit = true;
-        win1.mWindowRemovalAllowed = true;
-        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
-        verify(win1).onAnimationFinished(eq(ANIMATION_TYPE_RECENTS), any());
-        verify(win1).onExitAnimationDone();
-        verify(win1).destroySurface(eq(false), eq(false));
-        assertFalse(win1.mAnimatingExit);
-        assertFalse(win1.mHasSurface);
-    }
-
-    @Test
-    public void testCancelForRotation_ReorderToTop() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-
-        mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
-        mController.setWillFinishToHome(true);
-        mController.cancelAnimationForDisplayChange();
-
-        verify(mMockRunner).onAnimationCanceled(any(), any());
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
-    }
-
-    @Test
-    public void testCancelForRotation_ReorderToOriginalPosition() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-
-        mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
-        mController.setWillFinishToHome(false);
-        mController.cancelAnimationForDisplayChange();
-
-        verify(mMockRunner).onAnimationCanceled(any(), any());
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
-    }
-
-    @Test
-    public void testCancelForStartHome() throws Exception {
-        mWm.setRecentsAnimationController(mController);
-        final ActivityRecord homeActivity = createHomeActivity();
-        final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
-        activity.addWindow(win1);
-
-        initializeRecentsAnimationController(mController, homeActivity);
-        mController.setWillFinishToHome(true);
-
-        // Verify cancel is called with a snapshot and that we've created an overlay
-        spyOn(mWm.mTaskSnapshotController);
-        doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
-                anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
-        mController.cancelAnimationForHomeStart();
-        verify(mMockRunner).onAnimationCanceled(any(), any());
-
-        // Continue the animation (simulating a call to cleanupScreenshot())
-        mController.continueDeferredCancelAnimation();
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
-
-        // Assume home was moved to front so will-be-top callback should not be called.
-        homeActivity.moveFocusableActivityToTop("test");
-        spyOn(mDefaultDisplay.mFixedRotationTransitionListener);
-        mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
-        verify(mDefaultDisplay.mFixedRotationTransitionListener, never()).notifyRecentsWillBeTop();
-    }
-
-    private ActivityRecord createHomeActivity() {
-        final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
-                .setParentTask(mRootHomeTask)
-                .setCreateTask(true)
-                .build();
-        // Avoid {@link RecentsAnimationController.TaskAnimationAdapter#createRemoteAnimationTarget}
-        // returning null when calling {@link RecentsAnimationController#createAppAnimations}.
-        homeActivity.setVisibility(true);
-        return homeActivity;
-    }
-
-    private void startRecentsInDifferentRotation(ActivityRecord recentsActivity) {
-        final DisplayContent displayContent = recentsActivity.mDisplayContent;
-        displayContent.setFixedRotationLaunchingApp(recentsActivity,
-                (displayContent.getRotation() + 1) % 4);
-        mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
-                displayContent.getDisplayId());
-        initializeRecentsAnimationController(mController, recentsActivity);
-        assertTrue(recentsActivity.hasFixedRotationTransform());
-    }
-
-    private static void assertTopFixedRotationLaunchingAppCleared(ActivityRecord activity) {
-        assertFalse(activity.hasFixedRotationTransform());
-        assertFalse(activity.mDisplayContent.hasTopFixedRotationLaunchingApp());
-    }
-
-    private void setupForShouldAttachNavBarDuringTransition() {
-        final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"));
-        mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
-        mWm.setRecentsAnimationController(mController);
-        doReturn(navBar).when(mController).getNavigationBarWindow();
-        final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
-        doReturn(displayPolicy).when(mDefaultDisplay).getDisplayPolicy();
-        doReturn(true).when(displayPolicy).shouldAttachNavBarToAppDuringTransition();
-    }
-
-    private static void initializeRecentsAnimationController(RecentsAnimationController controller,
-            ActivityRecord activity) {
-        controller.initialize(activity.getActivityType(), new SparseBooleanArray(), activity);
-    }
-
-    private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
-        verify(binder, atLeast(0)).asBinder();
-        verifyNoMoreInteractions(binder);
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index da437c4..73d1031 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -19,31 +19,19 @@
 import static android.app.ActivityManager.PROCESS_STATE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
-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.eq;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.STOPPING;
-import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 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.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.content.ComponentName;
@@ -51,12 +39,9 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.platform.test.annotations.Presubmit;
-import android.view.IRecentsAnimationRunner;
 
 import androidx.test.filters.MediumTest;
 
-import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,61 +55,17 @@
 @RunWith(WindowTestRunner.class)
 public class RecentsAnimationTest extends WindowTestsBase {
 
-    private static final int TEST_USER_ID = 100;
-
     private final ComponentName mRecentsComponent =
             new ComponentName(mContext.getPackageName(), "RecentsActivity");
-    private RecentsAnimationController mRecentsAnimationController;
 
     @Before
     public void setUp() throws Exception {
-        mRecentsAnimationController = mock(RecentsAnimationController.class);
-        mAtm.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
-        doNothing().when(mAtm.mWindowManager).initializeRecentsAnimation(
-                anyInt(), any(), any(), anyInt(), any(), any());
-
         final RecentTasks recentTasks = mAtm.getRecentTasks();
         spyOn(recentTasks);
         doReturn(mRecentsComponent).when(recentTasks).getRecentsComponent();
     }
 
     @Test
-    public void testRecentsActivityVisiblility() {
-        TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        final WindowProcessController wpc = mSystemServicesTestRule.addProcess(
-                mRecentsComponent.getPackageName(), mRecentsComponent.getPackageName(),
-                // Use real pid/uid of the test so the corresponding process can be mapped by
-                // Binder.getCallingPid/Uid.
-                WindowManagerService.MY_PID, WindowManagerService.MY_UID);
-        ActivityRecord recentActivity = new ActivityBuilder(mAtm)
-                .setComponent(mRecentsComponent)
-                .setTask(recentsStack)
-                .setUseProcess(wpc)
-                .build();
-        ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        topActivity.getRootTask().moveToFront("testRecentsActivityVisiblility");
-
-        doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyBoolean() /* notifyClients */);
-
-        RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
-                mRecentsComponent, true /* getRecentsAnimation */);
-        // The launch-behind state should make the recents activity visible.
-        assertTrue(recentActivity.isVisibleRequested());
-        assertEquals(ActivityTaskManagerService.DEMOTE_TOP_REASON_ANIMATING_RECENTS,
-                mAtm.mDemoteTopAppReasons);
-        assertFalse(mAtm.mInternal.useTopSchedGroupForTopProcess());
-
-        // Simulate the animation is cancelled without changing the stack order.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
-        // The non-top recents activity should be invisible by the restored launch-behind state.
-        assertFalse(recentActivity.isVisibleRequested());
-        assertEquals(0, mAtm.mDemoteTopAppReasons);
-    }
-
-    @Test
     public void testPreloadRecentsActivity() {
         TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task homeStack =
@@ -155,8 +96,7 @@
 
         Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
         // Null animation indicates to preload.
-        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
-                null /* recentsAnimationRunner */);
+        mAtm.preloadRecentsActivity(recentsIntent);
 
         Task recentsStack = defaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_RECENTS);
@@ -168,223 +108,10 @@
         assertThat(recentsActivity.getState()).isEqualTo(STOPPING);
         assertFalse(recentsActivity.isVisibleRequested());
 
-        // Assume it is stopped to test next use case.
-        recentsActivity.activityStopped(null /* newIcicle */, null /* newPersistentState */,
-                null /* description */);
-
         spyOn(recentsActivity);
-        // Start when the recents activity exists. It should ensure the configuration.
-        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
-                null /* recentsAnimationRunner */);
+        // Preload when the recents activity exists. It should ensure the configuration.
+        mAtm.preloadRecentsActivity(recentsIntent);
 
         verify(recentsActivity).ensureActivityConfiguration(eq(true) /* ignoreVisibility */);
     }
-
-    @Test
-    public void testRestartRecentsActivity() throws Exception {
-        // Have a recents activity that is not attached to its process (ActivityRecord.app = null).
-        TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task recentsStack = defaultTaskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        ActivityRecord recentActivity = new ActivityBuilder(mAtm).setComponent(
-                mRecentsComponent).setCreateTask(true).setParentTask(recentsStack).build();
-        WindowProcessController app = recentActivity.app;
-        recentActivity.app = null;
-
-        // Start an activity on top.
-        new ActivityBuilder(mAtm).setCreateTask(true).build().getRootTask().moveToFront(
-                "testRestartRecentsActivity");
-
-        doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyBoolean() /* notifyClients */);
-        doReturn(app).when(mAtm).getProcessController(eq(recentActivity.processName), anyInt());
-        doNothing().when(mClientLifecycleManager).scheduleTransaction(any());
-
-        startRecentsActivity();
-
-        // Recents activity must be restarted, but not be resumed while running recents animation.
-        verify(mRootWindowContainer.mTaskSupervisor).startSpecificActivity(
-                eq(recentActivity), eq(false), anyBoolean());
-        assertThat(recentActivity.getState()).isEqualTo(PAUSED);
-    }
-
-    @Test
-    public void testSetLaunchTaskBehindOfTargetActivity() {
-        TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task homeStack = taskDisplayArea.getRootHomeTask();
-        // Assume the home activity support recents.
-        ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity();
-        if (targetActivity == null) {
-            targetActivity = new ActivityBuilder(mAtm)
-                    .setCreateTask(true)
-                    .setParentTask(homeStack)
-                    .build();
-        }
-
-        // Put another home activity in home stack.
-        ActivityRecord anotherHomeActivity = new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
-                .setCreateTask(true)
-                .setParentTask(homeStack)
-                .build();
-        // Start an activity on top so the recents activity can be started.
-        new ActivityBuilder(mAtm)
-                .setCreateTask(true)
-                .build()
-                .getRootTask()
-                .moveToFront("Activity start");
-
-        // Start the recents animation.
-        RecentsAnimationCallbacks recentsAnimation = startRecentsActivity(
-                targetActivity.getTask().getBaseIntent().getComponent(),
-                true /* getRecentsAnimation */);
-        // Ensure launch-behind is set for being visible.
-        assertTrue(targetActivity.mLaunchTaskBehind);
-
-        anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
-
-        // The test uses mocked RecentsAnimationController so we have to invoke the callback
-        // manually to simulate the flow.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
-        // We should restore the launch-behind of the original target activity.
-        assertFalse(targetActivity.mLaunchTaskBehind);
-    }
-
-    @Test
-    public void testCancelAnimationOnVisibleStackOrderChange() {
-        TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
-                .setCreateTask(true)
-                .setParentTask(fullscreenStack)
-                .build();
-        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(mRecentsComponent)
-                .setCreateTask(true)
-                .setParentTask(recentsStack)
-                .build();
-        Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
-                .setCreateTask(true)
-                .setParentTask(fullscreenStack2)
-                .build();
-
-        // Start the recents animation
-        startRecentsActivity();
-
-        fullscreenStack.moveToFront("Activity start");
-
-        // Assume recents animation already started, set a state that cancel recents animation
-        // with screenshot.
-        doReturn(true).when(mRecentsAnimationController).shouldDeferCancelUntilNextTransition();
-        doReturn(true).when(mRecentsAnimationController).shouldDeferCancelWithScreenshot();
-        // Start another fullscreen activity.
-        fullscreenStack2.moveToFront("Activity start");
-
-        // Ensure that the recents animation was canceled by setCancelOnNextTransitionStart().
-        verify(mRecentsAnimationController, times(1)).setCancelOnNextTransitionStart();
-    }
-
-    @Test
-    public void testKeepAnimationOnHiddenStackOrderChange() {
-        TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
-        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
-                .setCreateTask(true)
-                .setParentTask(fullscreenStack)
-                .build();
-        Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_RECENTS, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(mRecentsComponent)
-                .setCreateTask(true)
-                .setParentTask(recentsStack)
-                .build();
-        Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App2"))
-                .setCreateTask(true)
-                .setParentTask(fullscreenStack2)
-                .build();
-
-        // Start the recents animation
-        startRecentsActivity();
-
-        fullscreenStack.removeIfPossible();
-
-        // Ensure that the recents animation was NOT canceled
-        verify(mAtm.mWindowManager, times(0)).cancelRecentsAnimation(
-                eq(REORDER_KEEP_IN_PLACE), any());
-        verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
-    }
-
-    @Test
-    public void testMultipleUserHomeActivity_findUserHomeTask() {
-        TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay()
-                .getDefaultTaskDisplayArea();
-        Task homeStack = taskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED,
-                ACTIVITY_TYPE_HOME);
-        ActivityRecord otherUserHomeActivity = new ActivityBuilder(mAtm)
-                .setParentTask(homeStack)
-                .setCreateTask(true)
-                .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
-                .build();
-        otherUserHomeActivity.getTask().mUserId = TEST_USER_ID;
-
-        Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        new ActivityBuilder(mAtm)
-                .setComponent(new ComponentName(mContext.getPackageName(), "App1"))
-                .setCreateTask(true)
-                .setParentTask(fullscreenStack)
-                .build();
-
-        doReturn(TEST_USER_ID).when(mAtm).getCurrentUserId();
-        doCallRealMethod().when(mRootWindowContainer).ensureActivitiesVisible(
-                any() /* starting */, anyBoolean() /* notifyClients */);
-
-        startRecentsActivity(otherUserHomeActivity.getTask().getBaseIntent().getComponent(),
-                true);
-
-        // Ensure we find the task for the right user and it is made visible
-        assertTrue(otherUserHomeActivity.isVisibleRequested());
-    }
-
-    private void startRecentsActivity() {
-        startRecentsActivity(mRecentsComponent, false /* getRecentsAnimation */);
-    }
-
-    /**
-     * @return non-null {@link RecentsAnimationCallbacks} if the given {@code getRecentsAnimation}
-     *         is {@code true}.
-     */
-    private RecentsAnimationCallbacks startRecentsActivity(ComponentName recentsComponent,
-            boolean getRecentsAnimation) {
-        RecentsAnimationCallbacks[] recentsAnimation = { null };
-        if (getRecentsAnimation) {
-            doAnswer(invocation -> {
-                // The callback is actually RecentsAnimation.
-                recentsAnimation[0] = invocation.getArgument(2);
-                return null;
-            }).when(mAtm.mWindowManager).initializeRecentsAnimation(
-                    anyInt() /* targetActivityType */, any() /* recentsAnimationRunner */,
-                    any() /* callbacks */, anyInt() /* displayId */, any() /* recentTaskIds */,
-                    any() /* targetActivity */);
-        }
-
-        Intent recentsIntent = new Intent();
-        recentsIntent.setComponent(recentsComponent);
-        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
-                mock(IRecentsAnimationRunner.class));
-        return recentsAnimation[0];
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index f93ffb8..c5cbedb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -36,7 +36,6 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.fail;
 
 import static org.junit.Assert.assertEquals;
@@ -111,6 +110,7 @@
         runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
         mController = new RemoteAnimationController(mWm, mDisplayContent, mAdapter,
                 mHandler, false /*isActivityEmbedding*/);
+        mWm.mAnimator.ready();
     }
 
     private WindowState createAppOverlayWindow() {
@@ -134,7 +134,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -166,7 +166,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -288,7 +288,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -334,7 +334,7 @@
         task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
                 false /* isVoiceInteraction */, null /* sources */);
         mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         try {
@@ -361,7 +361,7 @@
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -415,7 +415,7 @@
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -469,7 +469,7 @@
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -524,7 +524,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -557,7 +557,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -593,7 +593,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -643,7 +643,7 @@
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
             mController.goodToGo(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+            waitUntilWindowAnimatorIdle();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
@@ -728,70 +728,6 @@
         }
     }
 
-    @Test
-    public void testNonAppTarget_notSendNavBar_controlledByRecents() throws Exception {
-        final RecentsAnimationController mockController =
-                mock(RecentsAnimationController.class);
-        doReturn(mockController).when(mWm).getRecentsAnimationController();
-        final int transit = TRANSIT_OLD_TASK_OPEN;
-        setupForNonAppTargetNavBar(transit, true);
-
-        final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
-                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
-        verify(mMockRunner).onAnimationStart(eq(transit),
-                any(), any(), nonAppsCaptor.capture(), any());
-        for (int i = 0; i < nonAppsCaptor.getValue().length; i++) {
-            if (nonAppsCaptor.getValue()[0].windowType == TYPE_NAVIGATION_BAR) {
-                fail("Non-app animation target must not contain navbar");
-            }
-        }
-    }
-
-    @android.platform.test.annotations.RequiresFlagsDisabled(
-            com.android.window.flags.Flags.FLAG_DO_NOT_SKIP_IME_BY_TARGET_VISIBILITY)
-    @SetupWindows(addWindows = W_INPUT_METHOD)
-    @Test
-    public void testLaunchRemoteAnimationWithoutImeBehind() {
-        final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1");
-        final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2");
-
-        // Simulating win1 has shown IME and being IME layering/input target
-        mDisplayContent.setImeLayeringTarget(win1);
-        mDisplayContent.setImeInputTarget(win1);
-        mImeWindow.mWinAnimator.hide(mDisplayContent.getPendingTransaction(), "test");
-        spyOn(mDisplayContent);
-        mImeWindow.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
-        makeWindowVisibleAndDrawn(mImeWindow);
-        assertTrue(mImeWindow.isOnScreen());
-        assertFalse(mImeWindow.isParentWindowHidden());
-
-        try {
-            // Simulating now win1 is being covered by the lockscreen which has no surface,
-            // and then launching an activity win2 with the remote animation
-            win1.mHasSurface = false;
-            win1.mActivityRecord.setVisibility(false);
-            mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
-            final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
-                    win2.mActivityRecord, new Point(50, 100), null,
-                    new Rect(50, 100, 150, 150), null, false).mAdapter;
-            adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
-                    mFinishedCallback);
-
-            mDisplayContent.applySurfaceChangesTransaction();
-            mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
-            mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
-
-            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
-                    any(), any(), any(), any());
-            // Verify the IME window won't apply surface change transaction with forAllImeWindows
-            verify(mDisplayContent, never()).forAllImeWindows(any(), eq(true));
-        } catch (Exception e) {
-            // no-op
-        } finally {
-            mDisplayContent.mOpeningApps.clear();
-        }
-    }
-
     private AnimationAdapter setupForNonAppTargetNavBar(int transit, boolean shouldAttachNavBar) {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mOpeningApps.add(win.mActivityRecord);
@@ -807,7 +743,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         mController.goodToGo(transit);
-        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        waitUntilWindowAnimatorIdle();
         return adapter;
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index e019a41..7cb62c5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -1260,68 +1260,38 @@
 
     @Test
     public void testShouldSleepActivities() {
+        final Task task = new TaskBuilder(mSupervisor).build();
+        task.mDisplayContent = mock(DisplayContent.class);
         // When focused activity and keyguard is going away, we should not sleep regardless
         // of the display state, but keyguard-going-away should only take effects on default
-        // display since there is no keyguard on secondary displays (yet).
-        verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
+        // display because the keyguard-going-away state of secondary displays are already the
+        // same as default display.
+        verifyShouldSleepActivities(task, true /* isVisibleTask */, true /* keyguardGoingAway */,
                 true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
-        verifyShouldSleepActivities(true /* focusedRootTask */, true /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(task, true /* isVisibleTask */, true /* keyguardGoingAway */,
                 true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
 
         // When not the focused root task, defer to display sleeping state.
-        verifyShouldSleepActivities(false /* focusedRootTask */, true /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(task, false /* isVisibleTask */, true /* keyguardGoingAway */,
                 true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
 
         // If keyguard is going away, defer to the display sleeping state.
-        verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(task, true /* isVisibleTask */, false /* keyguardGoingAway */,
                 true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
-        verifyShouldSleepActivities(true /* focusedRootTask */, false /*keyguardGoingAway*/,
+        verifyShouldSleepActivities(task, true /* isVisibleTask */, false /* keyguardGoingAway */,
                 false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
     }
 
-    @Test
-    public void testRootTaskOrderChangedOnRemoveRootTask() {
-        final Task task = new TaskBuilder(mSupervisor).build();
-        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
-        mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
-        try {
-            mDefaultTaskDisplayArea.removeRootTask(task);
-        } finally {
-            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
-        }
-        assertTrue(listener.mChanged);
-    }
+    private static void verifyShouldSleepActivities(Task task, boolean isVisibleTask,
+            boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
+            boolean expected) {
+        final DisplayContent display = task.mDisplayContent;
+        display.isDefaultDisplay = isDefaultDisplay;
+        doReturn(keyguardGoingAway).when(display).isKeyguardGoingAway();
+        doReturn(displaySleeping).when(display).isSleeping();
+        doReturn(isVisibleTask).when(task).shouldBeVisible(null /* starting */);
 
-    @Test
-    public void testRootTaskOrderChangedOnAddPositionRootTask() {
-        final Task task = new TaskBuilder(mSupervisor).build();
-        mDefaultTaskDisplayArea.removeRootTask(task);
-
-        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
-        mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
-        try {
-            task.mReparenting = true;
-            mDefaultTaskDisplayArea.addChild(task, 0);
-        } finally {
-            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
-        }
-        assertTrue(listener.mChanged);
-    }
-
-    @Test
-    public void testRootTaskOrderChangedOnPositionRootTask() {
-        RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener();
-        try {
-            final Task fullscreenRootTask1 = createTaskForShouldBeVisibleTest(
-                    mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
-                    true /* onTop */);
-            mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener);
-            mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenRootTask1,
-                    false /*includingParents*/);
-        } finally {
-            mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener);
-        }
-        assertTrue(listener.mChanged);
+        assertEquals(expected, task.shouldSleepActivities());
     }
 
     @Test
@@ -1450,35 +1420,4 @@
         verify(mSupervisor).startSpecificActivity(any(), eq(false) /* andResume */,
                 anyBoolean());
     }
-
-    private boolean isAssistantOnTop() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_assistantOnTopOfDream);
-    }
-
-    private void verifyShouldSleepActivities(boolean focusedRootTask,
-            boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
-            boolean expected) {
-        final Task task = new TaskBuilder(mSupervisor).build();
-        final DisplayContent display = mock(DisplayContent.class);
-        final KeyguardController keyguardController = mSupervisor.getKeyguardController();
-        display.isDefaultDisplay = isDefaultDisplay;
-
-        task.mDisplayContent = display;
-        doReturn(keyguardGoingAway).when(display).isKeyguardGoingAway();
-        doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay();
-
-        assertEquals(expected, task.shouldSleepActivities());
-    }
-
-    private static class RootTaskOrderChangedListener
-            implements TaskDisplayArea.OnRootTaskOrderChangedListener {
-        public boolean mChanged = false;
-
-        @Override
-        public void onRootTaskOrderChanged(Task rootTask) {
-            mChanged = true;
-        }
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index d29505f..957b5e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -54,6 +54,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.refEq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -204,6 +205,44 @@
     }
 
     @Test
+    public void testAllResumedActivitiesIdle() {
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final WindowProcessController proc2 = activity2.app;
+        activity1.setState(RESUMED, "test");
+        activity2.detachFromProcess();
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();
+
+        activity1.idle = true;
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();
+
+        activity2.setProcess(proc2);
+        activity2.setState(RESUMED, "test");
+        activity2.idle = true;
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isTrue();
+
+        activity1.idle = false;
+        activity1.setVisibleRequested(false);
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isTrue();
+
+        final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+                .setParentTask(activity2.getTask()).build();
+        final ActivityRecord activity3 = new ActivityBuilder(mAtm).build();
+        taskFragment.addChild(activity3);
+        taskFragment.setVisibleRequested(true);
+        activity3.setState(RESUMED, "test");
+        activity3.idle = true;
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isTrue();
+
+        activity3.idle = false;
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();
+
+        activity2.idle = false;
+        activity3.idle = true;
+        assertThat(mWm.mRoot.allResumedActivitiesIdle()).isFalse();
+    }
+
+    @Test
     public void testTaskLayerRank() {
         final Task rootTask = new TaskBuilder(mSupervisor).build();
         final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
@@ -332,8 +371,7 @@
         ensureTaskPlacement(fullscreenTask, firstActivity, secondActivity);
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
-                null /* launchIntoPipHostActivity */, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove");
 
         final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
         Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -342,8 +380,7 @@
         ensureTaskPlacement(fullscreenTask, secondActivity);
 
         // Move second activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
-                null /* launchIntoPipHostActivity */, "secondMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove");
 
         // Need to get root tasks again as a new instance might have been created.
         pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -374,8 +411,7 @@
 
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity,
-                null /* launchIntoPipHostActivity */, "initialMove");
+        mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove");
 
         assertTrue(firstActivity.mRequestForceTransition);
     }
@@ -395,8 +431,7 @@
         transientActivity.setState(RESUMED, "test");
         transientActivity.getTask().moveToFront("test");
 
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity2,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity2, "test");
         assertEquals("Created PiP task must not change focus", transientActivity.getTask(),
                 mRootWindowContainer.getTopDisplayFocusedRootTask());
         final Task newPipTask = activity2.getTask();
@@ -421,8 +456,7 @@
         final Task task = activity.getTask();
 
         // Move activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
 
         // Ensure a task has moved over.
         ensureTaskPlacement(task, activity);
@@ -460,8 +494,7 @@
         final Task task = activity.getTask();
 
         // Move activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
 
         // Ensure a task has moved over.
         ensureTaskPlacement(task, activity);
@@ -485,8 +518,7 @@
         final ActivityRecord secondActivity = taskFragment.getBottomMostActivity();
 
         // Move first activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "test");
 
         final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
         final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
@@ -517,8 +549,7 @@
         final ActivityRecord topActivity = taskFragment.getTopMostActivity();
 
         // Move the top activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(topActivity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(topActivity, "test");
 
         final Task pinnedRootTask = task.getDisplayArea().getRootPinnedTask();
 
@@ -598,7 +629,7 @@
         mRootWindowContainer.applySleepTokens(true);
         verify(task, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleeping();
         verify(task, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
-                null /* target */, null /* targetOptions */);
+                isNull() /* target */, isNull() /* targetOptions */, eq(false) /* deferPause */);
     }
 
     @Test
@@ -790,8 +821,7 @@
         doReturn(rootTask).when(mRootWindowContainer).getTopDisplayFocusedRootTask();
 
         // Use the task as target to resume.
-        mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity,
-                null /* targetOptions */);
+        mRootWindowContainer.resumeFocusedTasksTopActivities(rootTask, activity);
 
         // Verify the target task should resume its activity.
         verify(rootTask, times(1)).resumeTopActivityUncheckedLocked(
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 aa997ac..f743401 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -247,9 +247,9 @@
                 .build();
         mTask.addChild(translucentActivity);
 
-        spyOn(translucentActivity.mLetterboxUiController);
-        doReturn(true).when(translucentActivity.mLetterboxUiController)
-                .shouldShowLetterboxUi(any());
+        spyOn(translucentActivity.mAppCompatController.getAppCompatLetterboxPolicy());
+        doReturn(true).when(translucentActivity.mAppCompatController
+                .getAppCompatLetterboxPolicy()).shouldShowLetterboxUi(any());
 
         addWindowToActivity(translucentActivity);
         translucentActivity.mRootWindowContainer.performSurfacePlacement();
@@ -257,7 +257,7 @@
         final Function<ActivityRecord, Rect> innerBoundsOf =
                 (ActivityRecord a) -> {
                     final Rect bounds = new Rect();
-                    a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+                    a.getLetterboxInnerBounds(bounds);
                     return bounds;
                 };
         final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
@@ -369,7 +369,7 @@
         final Function<ActivityRecord, Rect> innerBoundsOf =
                 (ActivityRecord a) -> {
                     final Rect bounds = new Rect();
-                    a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
+                    a.getLetterboxInnerBounds(bounds);
                     return bounds;
                 };
 
@@ -613,7 +613,7 @@
         assertFalse(mActivity.mDisplayContent.shouldImeAttachedToApp());
 
         // Recompute the natural configuration without resolving size compat configuration.
-        mActivity.clearSizeCompatMode();
+        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
         mActivity.onConfigurationChanged(mTask.getConfiguration());
         // It should keep non-attachable because the resolved bounds will be computed according to
         // the aspect ratio that won't match its parent bounds.
@@ -632,16 +632,15 @@
 
         assertEquals(window, mActivity.findMainWindow());
 
-        spyOn(mActivity.mLetterboxUiController);
         doReturn(true).when(mActivity).isVisibleRequested();
 
-        assertTrue(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
-                mActivity.findMainWindow()));
+        assertTrue(mActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+                .shouldShowLetterboxUi(mActivity.findMainWindow()));
 
         window.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
 
-        assertFalse(mActivity.mLetterboxUiController.shouldShowLetterboxUi(
-                mActivity.findMainWindow()));
+        assertFalse(mActivity.mAppCompatController.getAppCompatLetterboxPolicy()
+                .shouldShowLetterboxUi(mActivity.findMainWindow()));
     }
 
     @Test
@@ -707,7 +706,7 @@
                         / originalBounds.width()));
 
         // Recompute the natural configuration in the new display.
-        mActivity.clearSizeCompatMode();
+        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
         mActivity.ensureActivityConfiguration();
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
@@ -958,12 +957,12 @@
                 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
                 .build();
-        assertTrue(activity.shouldCreateCompatDisplayInsets());
+        assertTrue(activity.shouldCreateAppCompatDisplayInsets());
 
         // The non-resizable activity should not be size compat because it is on a resizable task
         // in multi-window mode.
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
         // Activity should not be sandboxed.
         assertMaxBoundsInheritDisplayAreaBounds();
 
@@ -972,7 +971,7 @@
         mTask.mDisplayContent.getDefaultTaskDisplayArea()
                 .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
         // Activity should not be sandboxed.
         assertMaxBoundsInheritDisplayAreaBounds();
     }
@@ -987,7 +986,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1000,7 +999,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        assertTrue(activity.shouldCreateCompatDisplayInsets());
+        assertTrue(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1013,7 +1012,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1027,7 +1026,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1041,7 +1040,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */false,
                 RESIZE_MODE_UNRESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1055,7 +1054,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        assertTrue(activity.shouldCreateCompatDisplayInsets());
+        assertTrue(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1069,7 +1068,7 @@
         // Create an activity on the same task.
         final ActivityRecord activity = buildActivityRecord(/* supportsSizeChanges= */true,
                 RESIZE_MODE_RESIZEABLE, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        assertTrue(activity.shouldCreateCompatDisplayInsets());
+        assertTrue(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1091,7 +1090,7 @@
         doReturn(USER_MIN_ASPECT_RATIO_FULLSCREEN)
                 .when(activity.mAppCompatController.getAppCompatAspectRatioOverrides())
                 .getUserMinAspectRatioOverrideCode();
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1111,7 +1110,7 @@
         doReturn(true).when(
                 activity.mAppCompatController.getAppCompatAspectRatioOverrides())
                     .isSystemOverrideToFullscreenEnabled();
-        assertFalse(activity.shouldCreateCompatDisplayInsets());
+        assertFalse(activity.shouldCreateAppCompatDisplayInsets());
     }
 
     @Test
@@ -1483,7 +1482,7 @@
 
         // After changing the orientation to portrait the override should be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1512,7 +1511,7 @@
 
         // After changing the orientation to portrait the override should be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override forces the activity into a 3:2 aspect ratio
         assertEquals(1200, activity.getBounds().height());
@@ -1539,7 +1538,7 @@
 
         // After changing the orientation to landscape the override shouldn't be applied.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         // The per-package override should have no effect
         assertEquals(1200, activity.getBounds().height());
@@ -1757,7 +1756,8 @@
         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
-        LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
+        LetterboxDetails letterboxDetails = mActivity.mAppCompatController
+                .getAppCompatLetterboxPolicy().getLetterboxDetails();
 
         assertEquals(dh / scale, letterboxDetails.getLetterboxInnerBounds().width());
         assertEquals(dw / scale, letterboxDetails.getLetterboxInnerBounds().height());
@@ -2394,7 +2394,13 @@
     private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
             float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
             boolean enabled) {
-        final ActivityRecord activity = getActivityBuilderOnSameTask().build();
+        final ActivityRecord activity = getActivityBuilderWithoutTask().build();
+        final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
+                activity.mAppCompatController.getDesktopAppCompatAspectRatioPolicy();
+        spyOn(desktopAppCompatAspectRatioPolicy);
+        doReturn(enabled).when(desktopAppCompatAspectRatioPolicy)
+                .hasMinAspectRatioOverride(any());
+        mTask.addChild(activity);
         activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         spyOn(activity.mWmService.mAppCompatConfiguration);
         doReturn(enabled).when(activity.mWmService.mAppCompatConfiguration)
@@ -3017,6 +3023,7 @@
     }
 
     @Test
+    @SuppressWarnings("GuardedBy")
     public void testDisplayIgnoreOrientationRequest_pausedAppNotLostSizeCompat() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
@@ -3043,10 +3050,14 @@
         assertActivityMaxBoundsSandboxed();
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
-        mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
+        mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */,
+                false /* deferPause */);
 
         // App still in size compat, and the bounds don't change.
-        verify(mActivity, never()).clearSizeCompatMode();
+        final AppCompatSizeCompatModePolicy scmPolicy = mActivity.mAppCompatController
+                .getAppCompatSizeCompatModePolicy();
+        spyOn(scmPolicy);
+        verify(scmPolicy, never()).clearSizeCompatMode();
         assertFalse(mActivity.mAppCompatController.getAppCompatAspectRatioPolicy()
                 .isLetterboxedForFixedOrientationAndAspectRatio());
         assertDownScaled();
@@ -3248,7 +3259,7 @@
         // Activity max bounds are sandboxed since app may enter size compat mode.
         assertActivityMaxBoundsSandboxed();
         assertFalse(mActivity.inSizeCompatMode());
-        assertTrue(mActivity.shouldCreateCompatDisplayInsets());
+        assertTrue(mActivity.shouldCreateAppCompatDisplayInsets());
 
         // Resize display to half the width.
         resizeDisplay(mActivity.getDisplayContent(), 500, 1000);
@@ -3825,7 +3836,8 @@
 
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
-        LetterboxDetails letterboxDetails = mActivity.mLetterboxUiController.getLetterboxDetails();
+        LetterboxDetails letterboxDetails = mActivity.mAppCompatController
+                .getAppCompatLetterboxPolicy().getLetterboxDetails();
 
         // Letterboxed activity at bottom
         assertEquals(new Rect(0, 2100, 1400, 2800), mActivity.getBounds());
@@ -3887,7 +3899,7 @@
 
     private void recomputeNaturalConfigurationOfUnresizableActivity() {
         // Recompute the natural configuration of the non-resizable activity and the split screen.
-        mActivity.clearSizeCompatMode();
+        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         // Draw letterbox.
         mActivity.setVisible(false);
@@ -4093,8 +4105,8 @@
         // To force config to update again but with the same landscape orientation.
         activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
 
-        assertTrue(activity.shouldCreateCompatDisplayInsets());
-        assertNotNull(activity.getCompatDisplayInsets());
+        assertTrue(activity.shouldCreateAppCompatDisplayInsets());
+        assertNotNull(activity.getAppCompatDisplayInsets());
         // Activity is not letterboxed for fixed orientation because orientation is respected
         // with insets, and should not be in size compat mode
         assertFalse(activity.mAppCompatController.getAppCompatAspectRatioPolicy()
@@ -4818,7 +4830,7 @@
         assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
 
         // Activity should exit size compat with new density.
-        mActivity.clearSizeCompatMode();
+        mActivity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
 
         assertFitted();
         assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
@@ -4941,8 +4953,11 @@
     }
 
     private ActivityBuilder getActivityBuilderOnSameTask() {
+        return getActivityBuilderWithoutTask().setTask(mTask);
+    }
+
+    private ActivityBuilder getActivityBuilderWithoutTask() {
         return new ActivityBuilder(mAtm)
-                .setTask(mTask)
                 .setComponent(ComponentName.createRelative(mContext,
                         SizeCompatTests.class.getName()))
                 .setUid(android.os.Process.myUid());
@@ -5001,7 +5016,7 @@
             activity.setRequestedOrientation(screenOrientation);
         }
         // Make sure to use the provided configuration to construct the size compat fields.
-        activity.clearSizeCompatMode();
+        activity.mAppCompatController.getAppCompatSizeCompatModePolicy().clearSizeCompatMode();
         activity.ensureActivityConfiguration();
         // Make sure the display configuration reflects the change of activity.
         if (activity.mDisplayContent.updateOrientation()) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 6c8a7ac..9967cce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -23,7 +23,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -37,7 +36,6 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Builder;
 import android.view.SurfaceControl.Transaction;
-import android.view.SurfaceSession;
 
 import androidx.test.filters.SmallTest;
 
@@ -70,7 +68,6 @@
     @Mock AnimationAdapter mSpec2;
     @Mock Transaction mTransaction;
 
-    private SurfaceSession mSession = new SurfaceSession();
     private MyAnimatable mAnimatable;
     private MyAnimatable mAnimatable2;
     private DeferFinishAnimatable mDeferFinishAnimatable;
@@ -79,9 +76,9 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mAnimatable = new MyAnimatable(mWm, mSession, mTransaction);
-        mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction);
-        mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction);
+        mAnimatable = new MyAnimatable(mWm, mTransaction);
+        mAnimatable2 = new MyAnimatable(mWm, mTransaction);
+        mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mTransaction);
     }
 
     @After
@@ -89,25 +86,23 @@
         mAnimatable = null;
         mAnimatable2 = null;
         mDeferFinishAnimatable = null;
-        mSession.kill();
-        mSession = null;
     }
 
     @Test
     public void testRunAnimation() {
         mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
-                ANIMATION_TYPE_RECENTS);
+                ANIMATION_TYPE_APP_TRANSITION);
         final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
                 OnAnimationFinishedCallback.class);
         assertAnimating(mAnimatable);
         verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash));
-        verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_RECENTS),
+        verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
                 callbackCaptor.capture());
 
-        callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_RECENTS, mSpec);
+        callbackCaptor.getValue().onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, mSpec);
         assertNotAnimating(mAnimatable);
         assertTrue(mAnimatable.mFinishedCallbackCalled);
-        assertEquals(ANIMATION_TYPE_RECENTS, mAnimatable.mFinishedAnimationType);
+        assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType);
         verify(mTransaction).remove(eq(mAnimatable.mLeash));
         // TODO: Verify reparenting once we use mPendingTransaction to reparent it back
     }
@@ -314,7 +309,6 @@
 
     private static class MyAnimatable implements Animatable {
 
-        private final SurfaceSession mSession;
         private final Transaction mTransaction;
         final SurfaceControl mParent;
         final SurfaceControl mSurface;
@@ -323,13 +317,12 @@
         boolean mFinishedCallbackCalled;
         @AnimationType int mFinishedAnimationType;
 
-        MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) {
-            mSession = session;
+        MyAnimatable(WindowManagerService wm, Transaction transaction) {
             mTransaction = transaction;
-            mParent = wm.makeSurfaceBuilder(mSession)
+            mParent = wm.makeSurfaceBuilder()
                     .setName("test surface parent")
                     .build();
-            mSurface = wm.makeSurfaceBuilder(mSession)
+            mSurface = wm.makeSurfaceBuilder()
                     .setName("test surface")
                     .build();
             mFinishedCallbackCalled = false;
@@ -361,7 +354,7 @@
 
         @Override
         public Builder makeAnimationLeash() {
-            return new Builder(mSession) {
+            return new Builder() {
 
                 @Override
                 public SurfaceControl build() {
@@ -407,9 +400,8 @@
 
         Runnable mEndDeferFinishCallback;
 
-        DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session,
-                Transaction transaction) {
-            super(wm, session, transaction);
+        DeferFinishAnimatable(WindowManagerService wm, Transaction transaction) {
+            super(wm, transaction);
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 1e39f0b..71cfbfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -394,7 +394,7 @@
         mWmService = WindowManagerService.main(
                 mContext, mImService, false, wmPolicy, mAtmService,
                 testDisplayWindowSettingsProvider, StubTransaction::new,
-                (unused) -> new MockSurfaceControlBuilder());
+                MockSurfaceControlBuilder::new);
         spyOn(mWmService);
         spyOn(mWmService.mRoot);
         // Invoked during {@link ActivityStack} creation.
@@ -571,7 +571,7 @@
         // This makes sure all previous messages in the handler are fully processed vs. just popping
         // them from the message queue.
         final AtomicBoolean currentMessagesProcessed = new AtomicBoolean(false);
-        wm.mAnimator.getChoreographer().postFrameCallback(time -> {
+        wm.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             synchronized (currentMessagesProcessed) {
                 currentMessagesProcessed.set(true);
                 currentMessagesProcessed.notifyAll();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 6fd5faf..b595383 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -34,6 +34,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -50,6 +51,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
@@ -839,4 +841,16 @@
                 0 /* launchFlags */, pinnedTask /* candidateTask */);
         assertNull(actualRootTask);
     }
+
+    @Test
+    public void testMovedRootTaskToFront() {
+        final TaskDisplayArea tda = mDefaultDisplay.getDefaultTaskDisplayArea();
+        final Task rootTask = createTask(tda, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */, true /* createActivity */, true /* twoLevelTask */);
+        final Task leafTask = rootTask.getTopLeafTask();
+
+        clearInvocations(tda);
+        tda.onTaskMoved(rootTask, true /* toTop */, false /* toBottom */);
+        verify(tda).onLeafTaskMoved(eq(leafTask), anyBoolean(), anyBoolean());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index d013053..0cd036f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -90,6 +90,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationDefinition;
 import android.view.SurfaceControl;
 import android.window.IRemoteTransition;
 import android.window.ITaskFragmentOrganizer;
@@ -139,6 +140,7 @@
     private IBinder mFragmentToken;
     private WindowContainerTransaction mTransaction;
     private WindowContainerToken mFragmentWindowToken;
+    private RemoteAnimationDefinition mDefinition;
     private IBinder mErrorToken;
     private Rect mTaskFragBounds;
 
@@ -167,6 +169,7 @@
         mTransaction = new WindowContainerTransaction();
         mTransaction.setTaskFragmentOrganizer(mIOrganizer);
         mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
+        mDefinition = new RemoteAnimationDefinition();
         mErrorToken = new Binder();
         final Rect displayBounds = mDisplayContent.getBounds();
         mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(),
@@ -576,6 +579,17 @@
     }
 
     @Test
+    public void testRegisterRemoteAnimations() {
+        mController.registerRemoteAnimations(mIOrganizer, mDefinition);
+
+        assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
+
+        mController.unregisterRemoteAnimations(mIOrganizer);
+
+        assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
+    }
+
+    @Test
     public void testApplyTransaction_disallowRemoteTransitionForNonSystemOrganizer() {
         mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
         mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -2121,8 +2135,8 @@
     private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
         doReturn(mockParent).when(taskFragment).getTask();
         doReturn(new TaskFragmentParentInfo(
-                new Configuration(), DEFAULT_DISPLAY, true, true, null /* decorSurface */))
-                .when(mockParent).getTaskFragmentParentInfo();
+                new Configuration(), DEFAULT_DISPLAY, mockParent.mTaskId, true, true,
+                null /* decorSurface */)).when(mockParent).getTaskFragmentParentInfo();
 
         // Task needs to be visible
         mockParent.lastActiveTime = 100;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 6be1af2..cc1805a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -368,8 +368,7 @@
         assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams());
 
         // Move activity to pinned root task.
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
 
         // Ensure taskFragment requested config is reset.
         assertEquals(taskFragment0, activity.getOrganizedTaskFragment());
@@ -399,8 +398,7 @@
         spyOn(mAtm.mTaskFragmentOrganizerController);
 
         // Move activity to pinned.
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity0,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity0, "test");
 
         // Ensure taskFragment requested config is reset.
         assertTrue(taskFragment0.mClearedTaskFragmentForPip);
@@ -434,8 +432,7 @@
                 .createActivityCount(1)
                 .build();
         final ActivityRecord activity = taskFragment.getTopMostActivity();
-        mRootWindowContainer.moveActivityToPinnedRootTask(activity,
-                null /* launchIntoPipHostActivity */, "test");
+        mRootWindowContainer.moveActivityToPinnedRootTask(activity, "test");
         spyOn(mAtm.mTaskFragmentOrganizerController);
         assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 0a592f2..45082d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -230,8 +230,7 @@
         final Task originalTask = activityMain.getTask();
         final ActivityRecord activityPip = new ActivityBuilder(mAtm).setTask(originalTask).build();
         activityPip.setState(RESUMED, "test");
-        mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip,
-                null /* launchIntoPipHostActivity */, "test");
+        mAtm.mRootWindowContainer.moveActivityToPinnedRootTask(activityPip, "test");
         final Task pinnedActivityTask = activityPip.getTask();
 
         // Simulate pinnedActivityTask unintentionally added to recent during top activity resume.
@@ -867,8 +866,8 @@
         // Without limiting to be inside the parent bounds, the out screen size should keep relative
         // to the input bounds.
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
-        final ActivityRecord.CompatDisplayInsets compatInsets =
-                new ActivityRecord.CompatDisplayInsets(
+        final AppCompatDisplayInsets compatInsets =
+                new AppCompatDisplayInsets(
                         display, activity, /* letterboxedContainerBounds */ null,
                         /* useOverrideInsets */ false);
         final TaskFragment.ConfigOverrideHint overrideHint = new TaskFragment.ConfigOverrideHint();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index d62c626..eebb487 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -60,7 +60,7 @@
 
     @Override
     public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
-            int[] outAppOp) {
+            int[] outAppOp, int displayId) {
         return 0;
     }
 
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 49e349c..52a80b0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1251,7 +1251,7 @@
         final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
         app.mTransitionController.requestStartTransition(transition, app.getTask(),
                 null /* remoteTransition */, null /* displayChange */);
-        app.mTransitionController.collectExistenceChange(app.getTask());
+        transition.collectExistenceChange(app.getTask());
         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
         final AsyncRotationController asyncRotationController =
                 mDisplayContent.getAsyncRotationController();
@@ -1416,7 +1416,8 @@
         activity1.setVisibleRequested(false);
         activity2.setVisibleRequested(true);
 
-        openTransition.finishTransition();
+        final ActionChain chain = ActionChain.testFinish(null);
+        openTransition.finishTransition(chain);
 
         // We finished the openTransition. Even though activity1 is visibleRequested=false, since
         // the closeTransition animation hasn't played yet, make sure that we didn't commit
@@ -1429,7 +1430,7 @@
         // normally.
         mWm.mSyncEngine.abort(closeTransition.getSyncId());
 
-        closeTransition.finishTransition();
+        closeTransition.finishTransition(chain);
 
         assertFalse(activity1.isVisible());
         assertTrue(activity2.isVisible());
@@ -1449,7 +1450,7 @@
         activity1.setState(ActivityRecord.State.INITIALIZING, "test");
         activity1.mLaunchTaskBehind = true;
         mWm.mSyncEngine.abort(noChangeTransition.getSyncId());
-        noChangeTransition.finishTransition();
+        noChangeTransition.finishTransition(chain);
         assertTrue(activity1.mLaunchTaskBehind);
     }
 
@@ -1468,7 +1469,7 @@
         // We didn't call abort on the transition itself, so it will still run onTransactionReady
         // normally.
         mWm.mSyncEngine.abort(transition1.getSyncId());
-        transition1.finishTransition();
+        transition1.finishTransition(ActionChain.testFinish(transition1));
 
         verify(transitionEndedListener).run();
 
@@ -1530,7 +1531,7 @@
 
         verify(taskSnapshotController, times(1)).recordSnapshot(eq(task2));
 
-        controller.finishTransition(openTransition);
+        controller.finishTransition(ActionChain.testFinish(openTransition));
 
         // We are now going to simulate closing task1 to return back to (open) task2.
         final Transition closeTransition = createTestTransition(TRANSIT_CLOSE, controller);
@@ -1550,10 +1551,6 @@
 
         // An active transient launch overrides idle state to avoid clearing power mode before the
         // transition is finished.
-        spyOn(mRootWindowContainer.mTransitionController);
-        doAnswer(invocation -> controller.isTransientLaunch(invocation.getArgument(0))).when(
-                mRootWindowContainer.mTransitionController).isTransientLaunch(any());
-        activity2.getTask().setResumedActivity(activity2, "test");
         activity2.idle = true;
         assertFalse(mRootWindowContainer.allResumedActivitiesIdle());
 
@@ -1599,7 +1596,7 @@
         doReturn(true).when(task1).isTranslucentForTransition();
         assertFalse(controller.canApplyDim(task1));
 
-        controller.finishTransition(closeTransition);
+        controller.finishTransition(ActionChain.testFinish(closeTransition));
         assertTrue(wasInFinishingTransition[0]);
         assertFalse(calledListenerOnOtherDisplay[0]);
         assertNull(controller.mFinishingTransition);
@@ -1655,7 +1652,7 @@
         // to avoid the latency to resume the current top, i.e. appB.
         assertTrue(controller.isTransientVisible(taskRecent));
         // The recent is paused after the transient transition is finished.
-        controller.finishTransition(transition);
+        controller.finishTransition(ActionChain.testFinish(transition));
         assertFalse(controller.isTransientVisible(taskRecent));
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
index a0641cd..7a440e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransparentPolicyTest.java
@@ -73,6 +73,7 @@
     public void testPolicyRunningWhenTransparentIsUsed() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchTransparentActivityInTask();
 
                 ta.checkTopActivityTransparentPolicyStartNotInvoked();
@@ -85,6 +86,7 @@
     public void testCleanLetterboxConfigListenerWhenTranslucentIsDestroyed() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchTransparentActivityInTask();
                 ta.checkTopActivityTransparentPolicyStartNotInvoked();
                 ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
@@ -102,6 +104,7 @@
     public void testApplyStrategyAgainWhenOpaqueIsDestroyed() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchOpaqueActivityInTask();
                 ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
 
@@ -133,6 +136,7 @@
     public void testNotApplyStrategyAgainWhenOpaqueIsNotDestroyed() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchOpaqueActivityInTask();
                 ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ false);
 
@@ -152,7 +156,7 @@
                 ta.applyOnActivity((a) -> {
                     a.configureTopActivity(/* minAspect */ 1.2f, /* maxAspect */ 1.5f,
                             SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable */ true);
-                    a.configureTopActivityIgnoreOrientationRequest(true);
+                    a.setIgnoreOrientationRequest(true);
                     a.launchActivity(/* minAspect */ 1.1f, /* maxAspect */ 3f,
                             SCREEN_ORIENTATION_LANDSCAPE, /* transparent */true,
                             /* withComponent */ false, /* addToTask */true);
@@ -172,6 +176,7 @@
     public void testApplyStrategyToTransparentActivitiesRetainsWindowConfigurationProperties() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchTransparentActivity();
 
                 ta.forceChangeInTopActivityConfiguration();
@@ -186,6 +191,7 @@
     public void testApplyStrategyToMultipleTranslucentActivities() {
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
+                ta.activity().setIgnoreOrientationRequest(true);
                 ta.launchTransparentActivityInTask();
                 ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
                 ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
@@ -214,7 +220,7 @@
     @Test
     public void testNotRunStrategyToTranslucentActivitiesIfRespectOrientation() {
         runTestScenario(robot -> robot.transparentActivity(ta -> ta.applyOnActivity((a) -> {
-            a.configureTopActivityIgnoreOrientationRequest(false);
+            a.setIgnoreOrientationRequest(false);
             // The translucent activity is SCREEN_ORIENTATION_PORTRAIT.
             ta.launchTransparentActivityInTask();
             // Though TransparentPolicyState will be started, it won't be considered as running.
@@ -222,7 +228,7 @@
 
             // If the display changes to ignore orientation request, e.g. unfold, the policy should
             // take effect.
-            a.configureTopActivityIgnoreOrientationRequest(true);
+            a.setIgnoreOrientationRequest(true);
             ta.checkTopActivityTransparentPolicyStateIsRunning(/* running */ true);
             ta.setDisplayContentBounds(0, 0, 900, 1800);
             ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
@@ -234,7 +240,7 @@
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
                 ta.applyOnActivity((a) -> {
-                    a.configureTopActivityIgnoreOrientationRequest(true);
+                    a.setIgnoreOrientationRequest(true);
                     a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
                     a.rotateDisplayForTopActivity(ROTATION_90);
                     a.checkTopActivityInSizeCompatMode(/* inScm */ true);
@@ -257,7 +263,7 @@
             robot.transparentActivity((ta) -> {
                 ta.applyOnActivity((a) -> {
                     a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
-                    a.configureTopActivityIgnoreOrientationRequest(true);
+                    a.setIgnoreOrientationRequest(true);
                     ta.launchTransparentActivity();
 
                     a.assertFalseOnTopActivity(ActivityRecord::fillsParent);
@@ -284,7 +290,7 @@
                                 .setLetterboxHorizontalPositionMultiplier(1.0f);
                     });
                     a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
-                    a.configureTopActivityIgnoreOrientationRequest(true);
+                    a.setIgnoreOrientationRequest(true);
                     ta.launchTransparentActivityInTask();
                     ta.checkTopActivityHasInheritedBoundsFrom(/* fromTop */ 1);
 
@@ -309,16 +315,19 @@
         runTestScenario((robot) -> {
             robot.transparentActivity((ta) -> {
                 ta.applyOnActivity((a) -> {
-                    a.configureTopActivityIgnoreOrientationRequest(true);
+                    a.setIgnoreOrientationRequest(true);
                     a.configureUnresizableTopActivity(SCREEN_ORIENTATION_PORTRAIT);
                     // Rotate to put activity in size compat mode.
                     a.rotateDisplayForTopActivity(ROTATION_90);
                     a.checkTopActivityInSizeCompatMode(/* inScm */ true);
 
                     ta.launchTransparentActivityInTask();
-                    a.assertNotNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
-                    a.applyToTopActivity(ActivityRecord::clearSizeCompatMode);
-                    a.assertNullOnTopActivity(ActivityRecord::getCompatDisplayInsets);
+                    a.assertNotNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
+                    a.applyToTopActivity((top) -> {
+                        top.mAppCompatController.getAppCompatSizeCompatModePolicy()
+                                .clearSizeCompatMode();
+                    });
+                    a.assertNullOnTopActivity(ActivityRecord::getAppCompatDisplayInsets);
                 });
             });
         });
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index c65b76e..9602ae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -37,7 +37,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -276,9 +275,8 @@
         final DisplayContent dc = mDisplayContent;
         final WindowState homeWin = createWallpaperTargetWindow(dc);
         final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
-        final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
-        doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
-        mWm.setRecentsAnimationController(recentsController);
+        appWin.mAttrs.flags |= FLAG_SHOW_WALLPAPER;
+        makeWindowVisible(appWin);
 
         dc.mWallpaperController.adjustWallpaperWindows();
         assertEquals(appWin, dc.mWallpaperController.getWallpaperTarget());
@@ -354,46 +352,6 @@
     }
 
     @Test
-    public void testFixedRotationRecentsAnimatingTask() {
-        final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
-        final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
-        final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
-        makeWindowVisible(appWin);
-        final ActivityRecord r = appWin.mActivityRecord;
-        final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
-        doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
-        mWm.setRecentsAnimationController(recentsController);
-
-        r.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(),
-                mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration());
-        // Invisible requested activity should not share its rotation transform.
-        r.setVisibleRequested(false);
-        mDisplayContent.mWallpaperController.adjustWallpaperWindows();
-        assertFalse(wallpaperToken.hasFixedRotationTransform());
-
-        // Wallpaper should link the transform of its target.
-        r.setVisibleRequested(true);
-        mDisplayContent.mWallpaperController.adjustWallpaperWindows();
-        assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
-        assertTrue(r.hasFixedRotationTransform());
-        assertTrue(wallpaperToken.hasFixedRotationTransform());
-
-        // The case with shell transition.
-        registerTestTransitionPlayer();
-        final Transition t = r.mTransitionController.createTransition(TRANSIT_OPEN);
-        final ActivityRecord recents = mock(ActivityRecord.class);
-        t.collect(r.getTask());
-        r.mTransitionController.setTransientLaunch(recents, r.getTask());
-        // The activity in restore-below task should not be the target if keyguard is not locked.
-        mDisplayContent.mWallpaperController.adjustWallpaperWindows();
-        assertNotEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
-        // The activity in restore-below task should not be the target if keyguard is occluded.
-        doReturn(true).when(mDisplayContent).isKeyguardLocked();
-        mDisplayContent.mWallpaperController.adjustWallpaperWindows();
-        assertNotEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
-    }
-
-    @Test
     public void testWallpaperReportConfigChange() {
         final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
         createWallpaperTargetWindow(mDisplayContent);
@@ -449,7 +407,7 @@
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
         token.finishSync(t, token.getSyncGroup(), false /* cancel */);
         transit.onTransactionReady(transit.getSyncId(), t);
-        dc.mTransitionController.finishTransition(transit);
+        dc.mTransitionController.finishTransition(ActionChain.testFinish(transit));
         assertFalse(wallpaperWindow.isVisible());
         assertFalse(token.isVisible());
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 22def51..72935cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -62,8 +62,6 @@
         verify(c).accept(eq(mImeWindow));
     }
 
-    @android.platform.test.annotations.RequiresFlagsEnabled(
-            com.android.window.flags.Flags.FLAG_DO_NOT_SKIP_IME_BY_TARGET_VISIBILITY)
     @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
     @Test
     public void testTraverseImeRegardlessOfImeTarget() {
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 39640fb..916c237 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -1189,7 +1189,7 @@
         final int displayId = mDisplayContent.mDisplayId;
         // Use real surface, so ViewportWindow's BlastBufferQueue can be created.
         final ArrayList<SurfaceControl> surfaceControls = new ArrayList<>();
-        mWm.mSurfaceControlFactory = s -> new SurfaceControl.Builder() {
+        mWm.mSurfaceControlFactory = () -> new SurfaceControl.Builder() {
             @Override
             public SurfaceControl build() {
                 final SurfaceControl sc = super.build();
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 2b611b7..f56825fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -265,7 +265,7 @@
         rootTask.setTaskOrganizer(null);
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
-        verify(mWm.mAtmService).removeTask(eq(rootTask.mTaskId));
+        verify(mWm.mAtmService).removeTask(eq(rootTask));
     }
 
     @Test
@@ -283,7 +283,7 @@
         rootTask.setTaskOrganizer(null);
         mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
 
-        verify(mWm.mAtmService, never()).removeTask(eq(rootTask.mTaskId));
+        verify(mWm.mAtmService, never()).removeTask(eq(rootTask));
     }
 
     @Test
@@ -1488,7 +1488,7 @@
     @Test
     public void testReorderWithParents() {
         /*
-                  root
+                  default TDA
                ____|______
                |         |
            firstTda    secondTda
@@ -1496,10 +1496,12 @@
          firstRootTask    secondRootTask
 
          */
-        final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
-        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
-                mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea",
+        final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
                 FEATURE_VENDOR_FIRST);
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+                FEATURE_VENDOR_FIRST + 1);
         final Task firstRootTask = firstTaskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final Task secondRootTask = secondTaskDisplayArea.createRootTask(
@@ -1508,9 +1510,6 @@
                 .setTask(firstRootTask).build();
         final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
                 .setTask(secondRootTask).build();
-        // This assertion is just a defense to ensure that firstRootTask is not the top most
-        // by default
-        assertThat(mDisplayContent.getTopRootTask()).isEqualTo(secondRootTask);
         WindowContainerTransaction wct = new WindowContainerTransaction();
 
         // Reorder to top
@@ -1533,6 +1532,67 @@
     }
 
     @Test
+    public void testReorderDisplayArea() {
+        /*
+                  defaultTda
+               ____|______
+               |         |
+           firstTda    secondTda
+               |             |
+         firstRootTask    secondRootTask
+
+         */
+        final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
+                FEATURE_VENDOR_FIRST);
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+                FEATURE_VENDOR_FIRST + 1);
+        final Task firstRootTask = firstTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final Task secondRootTask = secondTaskDisplayArea.createRootTask(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+        final ActivityRecord firstActivity = new ActivityBuilder(mAtm)
+                .setTask(firstRootTask).build();
+        final ActivityRecord secondActivity = new ActivityBuilder(mAtm)
+                .setTask(secondRootTask).build();
+
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        // Reorder to top
+        wct.reorder(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(), true /* onTop */,
+                true /* includingParents */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+        assertThat(mDisplayContent.getTopRootTask()).isEqualTo(firstRootTask);
+
+        // Reorder to bottom
+        wct.reorder(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(), false /* onTop */,
+                true /* includingParents */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
+        assertThat(mDisplayContent.getBottomMostTask()).isEqualTo(firstRootTask);
+    }
+
+    @Test
+    public void testReparentDisplayAreaUnsupported() {
+        final TaskDisplayArea firstTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "FirstTaskDisplayArea",
+                FEATURE_VENDOR_FIRST);
+        final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea(
+                mDisplayContent, mRootWindowContainer.mWmService, "SecondTaskDisplayArea",
+                FEATURE_VENDOR_FIRST + 1);
+
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(firstTaskDisplayArea.mRemoteToken.toWindowContainerToken(),
+                secondTaskDisplayArea.mRemoteToken.toWindowContainerToken(),
+                true /* onTop */
+        );
+
+        assertThrows(UnsupportedOperationException.class, () ->
+                mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct)
+        );
+    }
+
+    @Test
     public void testAppearDeferThenVanish() {
         final ITaskOrganizer organizer = registerMockOrganizer();
         final Task rootTask = createRootTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 0cb22ad..9bad2ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -44,7 +44,6 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
@@ -197,21 +196,6 @@
     }
 
     @Test
-    public void testSetRunningBothAnimations() {
-        mWpc.setRunningRemoteAnimation(true);
-        mWpc.setRunningRecentsAnimation(true);
-
-        mWpc.setRunningRecentsAnimation(false);
-        mWpc.setRunningRemoteAnimation(false);
-        waitHandlerIdle(mAtm.mH);
-
-        InOrder orderVerifier = Mockito.inOrder(mMockListener);
-        orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(true));
-        orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(false));
-        orderVerifier.verifyNoMoreInteractions();
-    }
-
-    @Test
     public void testConfigurationForSecondaryScreenDisplayArea() {
         // By default, the process should not listen to any display area.
         assertNull(mWpc.getDisplayArea());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 39276a1..5a54af1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -310,10 +310,8 @@
         // Simulate the window is in split screen root task.
         final Task rootTask = createTask(mDisplayContent,
                 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
-        spyOn(appWindow);
-        spyOn(rootTask);
         rootTask.setFocusable(false);
-        doReturn(rootTask).when(appWindow).getRootTask();
+        appWindow.mActivityRecord.reparent(rootTask, 0 /* position */, "test");
 
         // Make sure canBeImeTarget is false;
         assertFalse(appWindow.canBeImeTarget());
@@ -1035,7 +1033,7 @@
                 mDisplayContent,
                 "SystemDialog", true);
         mDisplayContent.setImeLayeringTarget(mAppWindow);
-        mAppWindow.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         makeWindowVisible(mImeWindow);
         systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
         assertTrue(systemDialogWindow.needsRelativeLayeringToIme());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index ea2abf7..a215c0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -55,6 +55,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static org.junit.Assert.assertEquals;
@@ -693,6 +694,13 @@
         }
     }
 
+    static void makeWindowVisibleAndNotDrawn(WindowState... windows) {
+        makeWindowVisible(windows);
+        for (WindowState win : windows) {
+            win.mWinAnimator.mDrawState = DRAW_PENDING;
+        }
+    }
+
     static void makeLastConfigReportedToClient(WindowState w, boolean visible) {
         w.fillClientWindowFramesAndConfiguration(new ClientWindowFrames(),
                 new MergedConfiguration(), new ActivityWindowInfo(), true /* useLatestConfig */,
@@ -924,7 +932,8 @@
             mSystemServicesTestRule.addProcess("pkgName", "procName",
                     WindowManagerService.MY_PID, WindowManagerService.MY_UID);
         }
-        mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer);
+        mAtm.mTaskFragmentOrganizerController.registerOrganizer(organizer, isSystemOrganizer,
+                new Bundle());
     }
 
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
@@ -2122,7 +2131,7 @@
         }
 
         public void finish() {
-            mController.finishTransition(mLastTransit);
+            mController.finishTransition(ActionChain.testFinish(mLastTransit));
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
index 48a8d55..a1d35a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingLegacyTest.java
@@ -125,7 +125,7 @@
     public void trace_dumpsWindowManagerState_whenTracing() throws Exception {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.logState("where");
-        verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTraceLogLevel.TRIM));
+        verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.TRIM));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
new file mode 100644
index 0000000..c45b99d9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingPerfettoTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+import static java.io.File.createTempFile;
+import static java.nio.file.Files.createTempDirectory;
+
+import android.platform.test.annotations.Presubmit;
+import android.tools.ScenarioBuilder;
+import android.tools.traces.io.ResultWriter;
+import android.tools.traces.monitors.PerfettoTraceMonitor;
+import android.view.Choreographer;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import perfetto.protos.PerfettoConfig.WindowManagerConfig.LogFrequency;
+
+/**
+ * Test class for {@link WindowTracingPerfetto}.
+ */
+@SmallTest
+@Presubmit
+public class WindowTracingPerfettoTest {
+    private WindowManagerService mWmMock;
+    private Choreographer mChoreographer;
+    private WindowTracing mWindowTracing;
+    private PerfettoTraceMonitor mTraceMonitor;
+    private ResultWriter mWriter;
+
+    @Before
+    public void setUp() throws Exception {
+        mWmMock = Mockito.mock(WindowManagerService.class);
+        Mockito.doNothing().when(mWmMock).dumpDebugLocked(Mockito.any(), Mockito.anyInt());
+
+        mChoreographer = Mockito.mock(Choreographer.class);
+
+        mWindowTracing = new WindowTracingPerfetto(mWmMock, mChoreographer,
+                new WindowManagerGlobalLock());
+
+        mWriter = new ResultWriter()
+            .forScenario(new ScenarioBuilder()
+                    .forClass(createTempFile("temp", "").getName()).build())
+            .withOutputDir(createTempDirectory("temp").toFile())
+            .setRunComplete();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        stopTracing();
+    }
+
+    @Test
+    public void isEnabled_returnsFalseByDefault() {
+        assertFalse(mWindowTracing.isEnabled());
+    }
+
+    @Test
+    public void isEnabled_returnsTrueAfterStartThenFalseAfterStop() {
+        startTracing(false);
+        assertTrue(mWindowTracing.isEnabled());
+
+        stopTracing();
+        assertFalse(mWindowTracing.isEnabled());
+    }
+
+    @Test
+    public void trace_ignoresLogStateCalls_ifTracingIsDisabled() {
+        mWindowTracing.logState("where");
+        verifyZeroInteractions(mWmMock);
+    }
+
+    @Test
+    public void trace_writesInitialStateSnapshot_whenTracingStarts() throws Exception {
+        startTracing(false);
+        verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+    }
+
+    @Test
+    public void trace_writesStateSnapshot_onLogStateCall() throws Exception {
+        startTracing(false);
+        mWindowTracing.logState("where");
+        verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+    }
+
+    @Test
+    public void dump_writesOneSingleStateSnapshot() throws Exception {
+        startTracing(true);
+        mWindowTracing.logState("where");
+        verify(mWmMock, times(1)).dumpDebugLocked(any(), eq(WindowTracingLogLevel.ALL));
+    }
+
+    private void startTracing(boolean isDump) {
+        if (isDump) {
+            mTraceMonitor = PerfettoTraceMonitor
+                    .newBuilder()
+                    .enableWindowManagerDump()
+                    .build();
+        } else {
+            mTraceMonitor = PerfettoTraceMonitor
+                    .newBuilder()
+                    .enableWindowManagerTrace(LogFrequency.LOG_FREQUENCY_TRANSACTION)
+                    .build();
+        }
+        mTraceMonitor.start();
+    }
+
+    private void stopTracing() {
+        if (mTraceMonitor == null || !mTraceMonitor.isEnabled()) {
+            return;
+        }
+        mTraceMonitor.stop(mWriter);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 973ab84..4f60106 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -40,25 +40,17 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
-import android.util.SparseBooleanArray;
-import android.view.IRecentsAnimationRunner;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.window.ScreenCapture;
 
 import androidx.test.filters.SmallTest;
@@ -70,7 +62,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
-import java.util.function.Function;
+import java.util.function.Supplier;
 
 /**
  * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method.
@@ -133,8 +125,7 @@
         private LayerRecordingTransaction mTransaction;
         private SurfaceControl mPendingParent;
 
-        HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) {
-            super(s);
+        HierarchyRecorder(LayerRecordingTransaction transaction) {
             mTransaction = transaction;
         }
 
@@ -153,8 +144,8 @@
         }
     }
 
-    private static class HierarchyRecordingBuilderFactory implements Function<SurfaceSession,
-            SurfaceControl.Builder> {
+    private static class HierarchyRecordingBuilderFactory
+            implements Supplier<SurfaceControl.Builder> {
         private LayerRecordingTransaction mTransaction;
 
         HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
@@ -162,9 +153,8 @@
         }
 
         @Override
-        public SurfaceControl.Builder apply(SurfaceSession s) {
-            final LayerRecordingTransaction transaction = mTransaction;
-            return new HierarchyRecorder(s, transaction);
+        public SurfaceControl.Builder get() {
+            return new HierarchyRecorder(mTransaction);
         }
     }
 
@@ -410,30 +400,6 @@
     }
 
     @Test
-    public void testAssignWindowLayers_ForImeOnAppWithRecentsAnimating() {
-        final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
-                mAppWindow.mActivityRecord, "imeAppTarget");
-        mDisplayContent.setImeInputTarget(imeAppTarget);
-        mDisplayContent.setImeLayeringTarget(imeAppTarget);
-        mDisplayContent.setImeControlTarget(imeAppTarget);
-        mDisplayContent.updateImeParent();
-
-        // Simulate the ime layering target task is animating with recents animation.
-        final Task imeAppTargetTask = imeAppTarget.getTask();
-        final SurfaceAnimator imeTargetTaskAnimator = imeAppTargetTask.mSurfaceAnimator;
-        spyOn(imeTargetTaskAnimator);
-        doReturn(ANIMATION_TYPE_RECENTS).when(imeTargetTaskAnimator).getAnimationType();
-        doReturn(true).when(imeTargetTaskAnimator).isAnimating();
-
-        mDisplayContent.assignChildLayers(mTransaction);
-
-        // Ime should on top of the application window when in recents animation and keep
-        // attached on app.
-        assertTrue(mDisplayContent.shouldImeAttachedToApp());
-        assertWindowHigher(mImeWindow, imeAppTarget);
-    }
-
-    @Test
     public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() {
         final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
                 mAppWindow.mActivityRecord, "imeAppTarget");
@@ -493,43 +459,6 @@
     }
 
     @Test
-    public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() {
-        // create RecentsAnimationController
-        IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class);
-        when(mockRunner.asBinder()).thenReturn(new Binder());
-        final int displayId = mDisplayContent.getDisplayId();
-        RecentsAnimationController controller = new RecentsAnimationController(
-                mWm, mockRunner, null, displayId);
-        spyOn(controller);
-        doReturn(mNavBarWindow).when(controller).getNavigationBarWindow();
-        mWm.setRecentsAnimationController(controller);
-
-        // set ime visible
-        spyOn(mDisplayContent.mInputMethodWindow);
-        doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
-
-        DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
-        spyOn(policy);
-        doReturn(true).when(policy).shouldAttachNavBarToAppDuringTransition();
-
-        // create home activity
-        Task rootHomeTask = mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
-        final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
-                .setParentTask(rootHomeTask)
-                .setCreateTask(true)
-                .build();
-        homeActivity.setVisibility(true);
-
-        // start recent animation
-        controller.initialize(homeActivity.getActivityType(), new SparseBooleanArray(),
-                homeActivity);
-
-        mDisplayContent.assignChildLayers(mTransaction);
-        assertZOrderGreaterThan(mTransaction, mNavBarWindow.mToken.getSurfaceControl(),
-                mDisplayContent.getImeContainer().getSurfaceControl());
-    }
-
-    @Test
     public void testPopupWindowAndParentIsImeTarget_expectHigherThanIme_inMultiWindow() {
         // Simulate the app window is in multi windowing mode and being IME target
         mAppWindow.getConfiguration().windowConfiguration.setWindowingMode(
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
index 381e9e4..46b8e3a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/DesktopModeFlagsUtilTest.java
@@ -234,11 +234,11 @@
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
-    public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsFalse() {
+    public void isEnabled_dwFlagOn_overrideOff_featureFlagOn_returnsTrue() {
         setOverride(OVERRIDE_OFF.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
     }
 
     @Test
@@ -296,11 +296,11 @@
             FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
             FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS
     })
-    public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnTrue() {
+    public void isEnabled_dwFlagOff_overrideOn_featureFlagOff_returnFalse() {
         setOverride(OVERRIDE_ON.getSetting());
 
         // Follow override if they exist, and is not equal to default toggle state (dw flag)
-        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isTrue();
+        assertThat(DesktopModeFlagsUtil.DYNAMIC_INITIAL_BOUNDS.isEnabled(mContext)).isFalse();
     }
 
     @Test
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
index 26d9b10..f825f55 100644
--- a/services/usage/OWNERS
+++ b/services/usage/OWNERS
@@ -1,7 +1,10 @@
+# Bug component: 532296
+set noparent
+
 [email protected]
 [email protected]
[email protected]
 [email protected]
[email protected]
 
 per-file *StorageStats* = file:/core/java/android/os/storage/OWNERS
 per-file *Broadcast* = [email protected]
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING
index 6e84543..c878054 100644
--- a/services/usage/java/com/android/server/usage/TEST_MAPPING
+++ b/services/usage/java/com/android/server/usage/TEST_MAPPING
@@ -1,23 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksCoreTests",
-      "options": [
-        {
-          "include-filter": "android.app.usage"
-        }
-      ]
+      "name": "FrameworksCoreTests_usage"
     },
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.usage"
-        },
-        {
-          "exclude-filter": "com.android.server.usage.StorageStatsServiceTest"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_usage"
     },
     {
       "name": "CtsBRSTestCases",
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
index 9ed894b..509d95e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/TEST_MAPPING
@@ -1,12 +1,7 @@
 {
   "presubmit": [
     {
-      "name": "FrameworksServicesTests",
-      "options": [
-        {
-          "include-filter": "com.android.server.soundtrigger_middleware"
-        }
-      ]
+      "name": "FrameworksServicesTests_android_server_soundtrigger_middleware"
     }
   ]
 }
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index 808a575..1e1abf2 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -39,7 +39,10 @@
 
 /**
  * CallControl provides client side control of a call.  Each Call will get an individual CallControl
- * instance in which the client can alter the state of the associated call.
+ * instance in which the client can alter the state of the associated call.  Outgoing and incoming
+ * calls should move to active (via {@link CallControl#setActive(Executor, OutcomeReceiver)} or
+ * answered (via {@link CallControl#answer(int, Executor, OutcomeReceiver)} before 60 seconds.  If
+ * the new call is not moved to active or answered before 60 seconds, the call will be disconnected.
  *
  * <p>
  * Each method is Transactional meaning that it can succeed or fail. If a transaction succeeds,
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 1df6cf78..ad7d987 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2621,7 +2621,9 @@
     }
 
     /**
-     * Sets state to ringing (e.g., an inbound ringing connection).
+     * Sets state to ringing (e.g., an inbound ringing connection).  The Connection should not be
+     * in STATE_RINGING for more than 60 seconds. After 60 seconds, the Connection will
+     * be disconnected.
      */
     public final void setRinging() {
         checkImmutable();
@@ -2645,7 +2647,9 @@
     }
 
     /**
-     * Sets state to dialing (e.g., dialing an outbound connection).
+     * Sets state to dialing (e.g., dialing an outbound connection). The Connection should not be
+     * in STATE_DIALING for more than 60 seconds. After 60 seconds, the Connection will
+     * be disconnected.
      */
     public final void setDialing() {
         checkImmutable();
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index e6fe406..83dac18 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -175,8 +175,15 @@
      * <p>
      * The call recording tone is a 1400 hz tone which repeats every 15 seconds while recording is
      * in progress.
+     *
+     * @deprecated this API was only intended to prevent call recording via the microphone by an app
+     * while in a phone call.  Audio policies no longer make this possible.  Further, this API was
+     * never actually used.  Call recording solutions integrated in an OEM dialer app must use
+     * appropriate recording signals to inform the caller/callee of the recording.
      * @hide
      */
+    @FlaggedApi(com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+    @Deprecated
     @SystemApi
     public static final String EXTRA_PLAY_CALL_RECORDING_TONE =
             "android.telecom.extra.PLAY_CALL_RECORDING_TONE";
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ff4be55..d89c9c1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2339,6 +2339,7 @@
      * @return True if the digits were processed as an MMI code, false otherwise.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handleMmi(String dialString) {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -2364,6 +2365,7 @@
      * @return True if the digits were processed as an MMI code, false otherwise.
      */
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null) {
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index feea55b..a678147 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -33,7 +33,6 @@
 import android.widget.Toast;
 
 import com.android.internal.telephony.TelephonyPermissions;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 /**
@@ -312,18 +311,10 @@
         // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
         // and it doesn't create an info leak risk because the cell location is stored in the phone
         // process anyway, and the system server already has location access.
-        if (Flags.supportPhoneUidCheckForMultiuser()) {
-            if (TelephonyPermissions.isSystemOrPhone(query.callingUid)
-                    || UserHandle.isSameApp(query.callingUid, Process.NETWORK_STACK_UID)
-                    || UserHandle.isSameApp(query.callingUid, Process.ROOT_UID)) {
-                return LocationPermissionResult.ALLOWED;
-            }
-        } else {
-            if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
-                    || query.callingUid == Process.NETWORK_STACK_UID
-                    || query.callingUid == Process.ROOT_UID) {
-                return LocationPermissionResult.ALLOWED;
-            }
+        if (TelephonyPermissions.isSystemOrPhone(query.callingUid)
+                || UserHandle.isSameApp(query.callingUid, Process.NETWORK_STACK_UID)
+                || UserHandle.isSameApp(query.callingUid, Process.ROOT_UID)) {
+            return LocationPermissionResult.ALLOWED;
         }
 
         // Check the system-wide requirements. If the location main switch is off and the caller is
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 6482432..ff9cba2 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -33,7 +33,6 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
 import com.android.internal.telephony.util.TelephonyUtils;
 
 import java.util.ArrayList;
@@ -253,21 +252,17 @@
                     // 3. It has not been installed as an update from its system built-in version
                     // 4. It is in default state (not explicitly DISABLED/DISABLED_BY_USER/ENABLED)
                     // 5. It is currently installed for the calling user
-                    // TODO(b/329739019):
-                    // 1. Merge the nested if conditions below during flag cleaning up phase
-                    // 2. Support user case that NEW carrier app is added during OTA, when emerge.
-                    if (!Flags.hidePreinstalledCarrierAppAtMostOnce() || !hasRunEver) {
-                        if (!isUpdatedSystemApp(ai) && enabledSetting
-                                == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                                && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
-                            Log.i(TAG, "Update state (" + packageName
-                                    + "): DISABLED_UNTIL_USED for user " + userId);
-                            context.createContextAsUser(UserHandle.of(userId), 0)
-                                    .getPackageManager()
-                                    .setSystemAppState(
-                                            packageName,
-                                            PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
-                        }
+                    // TODO(b/329739019):Support user case that NEW carrier app is added during OTA
+                    if (!hasRunEver && !isUpdatedSystemApp(ai) && enabledSetting
+                            == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
+                        Log.i(TAG, "Update state (" + packageName
+                                + "): DISABLED_UNTIL_USED for user " + userId);
+                        context.createContextAsUser(UserHandle.of(userId), 0)
+                                .getPackageManager()
+                                .setSystemAppState(
+                                        packageName,
+                                        PackageManager.SYSTEM_APP_STATE_UNINSTALLED);
                     }
 
                     // Associated apps are more brittle, because we can't rely on the distinction
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 7ed26fb..b80610a 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -35,7 +35,6 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.flags.Flags;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -934,12 +933,8 @@
      * as system user or not.
      */
     public static boolean isSystemOrPhone(int uid) {
-        if (Flags.supportPhoneUidCheckForMultiuser()) {
-            return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid,
-                    Process.PHONE_UID);
-        } else {
-            return uid == Process.SYSTEM_UID || uid == Process.PHONE_UID;
-        }
+        return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || UserHandle.isSameApp(uid,
+                Process.PHONE_UID);
     }
 
     /**
@@ -947,11 +942,7 @@
      * as system user or not.
      */
     public static boolean isRootOrShell(int uid) {
-        if (Flags.supportPhoneUidCheckForMultiuser()) {
-            return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid,
-                    Process.SHELL_UID);
-        } else {
-            return uid == Process.ROOT_UID || uid == Process.SHELL_UID;
-        }
+        return UserHandle.isSameApp(uid, Process.ROOT_UID) || UserHandle.isSameApp(uid,
+                Process.SHELL_UID);
     }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b9a001d..aca0941 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -420,6 +420,7 @@
      * <p>
      * Note: This requires the Telephony config_supports_telephony_audio_device overlay to be true
      * in order to work.
+     * @deprecated this functionality was never used and is no longer supported.
      * @hide
      */
     public static final String KEY_PLAY_CALL_RECORDING_TONE_BOOL = "play_call_recording_tone_bool";
@@ -9992,6 +9993,40 @@
     @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
     public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool";
 
+    /**
+     * Indicate whether carrier roaming to satellite is using P2P SMS.
+     *
+     * This will need agreement with carriers before enabling this flag.
+     *
+     * The default value is false.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL =
+            "satellite_roaming_p2p_sms_supported_bool";
+
+    /**
+     * Defines the NIDD (Non-IP Data Delivery) APN to be used for carrier roaming to satellite
+     * attachment. For more on NIDD, see 3GPP TS 29.542.
+     * Note this config is the only source of truth regarding the definition of the APN.
+     *
+     * @hide
+     */
+    public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING =
+            "satellite_nidd_apn_name_string";
+
+    /**
+     * Default value {@code true}, meaning when an emergency call request comes in, if the device is
+     * in emergency satellite mode but hasn't sent the first satellite datagram, then exits
+     * satellite mode to allow the emergency call to go through.
+     *
+     * If {@code false}, the emergency call is always blocked if device is in emergency satellite
+     * mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed.
+     *
+     * @hide
+     */
+    public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL =
+            "satellite_roaming_turn_off_session_for_emergency_call_bool";
+
     /** @hide */
     @IntDef({
             CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC,
@@ -10063,8 +10098,36 @@
      * The default value is 30 seconds.
      */
     @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public static final String KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
-            "satellite_screen_off_inactivity_timeout_duration_sec_int";
+    public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT =
+            "satellite_roaming_screen_off_inactivity_timeout_sec_int";
+
+    /**
+     * An integer key holds the timeout duration in seconds used to determine whether to exit P2P
+     * SMS mode and start TN scanning.
+     *
+     * The timer is started when the device is not connected, user is not pointing to the
+     * satellite and no data transfer is happening.
+     * When the timer expires, the device will move to IDLE mode upon which TN scanning will start.
+     *
+     * The default value is 180 seconds.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT =
+            "satellite_roaming_p2p_sms_inactivity_timeout_sec_int";
+
+    /**
+     * An integer key holds the timeout duration in seconds used to determine whether to exit ESOS
+     * mode and start TN scanning.
+     *
+     * The timer is started when the device is not connected, user is not pointing to the
+     * satellite and no data transfer is happening.
+     * When the timer expires, the device will move to IDLE mode upon which TN scanning will start.
+     *
+     * The default value is 600 seconds.
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT =
+            "satellite_roaming_esos_inactivity_timeout_sec_int";
 
     /**
      * Indicating whether DUN APN should be disabled when the device is roaming. In that case,
@@ -11224,11 +11287,16 @@
         sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT,
                 (int) TimeUnit.SECONDS.toMillis(30));
         sDefaults.putBoolean(KEY_SATELLITE_ESOS_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL, false);
+        sDefaults.putString(KEY_SATELLITE_NIDD_APN_NAME_STRING, "");
+        sDefaults.putBoolean(KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL, true);
         sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT, 0);
         sDefaults.putInt(KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT,
                 SatelliteManager.EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911);
         sDefaults.putInt(KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT, 180);
-        sDefaults.putInt(KEY_SATELLITE_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
+        sDefaults.putInt(KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT, 30);
+        sDefaults.putInt(KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT, 180);
+        sDefaults.putInt(KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT, 600);
         sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORTS_BUSINESS_CALL_COMPOSER_BOOL, false);
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index 65e8e13..ddf5ef2 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -463,8 +463,9 @@
     public String toString() {
         return "CarrierRestrictionRules(allowed:" + mAllowedCarriers + ", excluded:"
                 + mExcludedCarriers + ", default:" + mCarrierRestrictionDefault
-                + ", MultiSim policy:" + mMultiSimPolicy + getCarrierInfoList() +
-                "  mIsCarrierLockInfoSupported = " + mUseCarrierLockInfo + ")";
+                + ", MultiSim policy:" + mMultiSimPolicy + getCarrierInfoList()
+                + ", mIsCarrierLockInfoSupported = " + mUseCarrierLockInfo
+                + getCarrierRestrictionStatusToLog() + ")";
     }
 
     private String getCarrierInfoList() {
@@ -476,6 +477,13 @@
         }
     }
 
+    private String getCarrierRestrictionStatusToLog() {
+        if(android.os.Build.isDebuggable()) {
+            return ", CarrierRestrictionStatus = " + mCarrierRestrictionStatus;
+        }
+        return "";
+    }
+
     /**
      * Builder for a {@link CarrierRestrictionRules}.
      */
diff --git a/telephony/java/android/telephony/PcoData.java b/telephony/java/android/telephony/PcoData.java
index 39e4f2f..3cc32c6 100644
--- a/telephony/java/android/telephony/PcoData.java
+++ b/telephony/java/android/telephony/PcoData.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.telephony.uicc.IccUtils;
+
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -84,8 +86,8 @@
 
     @Override
     public String toString() {
-        return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + ", contents[" +
-                contents.length + "])";
+        return "PcoData(" + cid + ", " + bearerProto + ", " + pcoId + " "
+                + IccUtils.bytesToHexString(contents) + ")";
     }
 
     @Override
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index db167c05..127bbff 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1211,6 +1211,8 @@
                     .append(", mIsDataRoamingFromRegistration=")
                     .append(mIsDataRoamingFromRegistration)
                     .append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
+                    .append(", mIsUsingNonTerrestrialNetwork=")
+                    .append(isUsingNonTerrestrialNetwork())
                     .append("}").toString();
         }
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index dbe4f27..3ff1e2c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.Manifest;
+import android.annotation.BoolRes;
 import android.annotation.BytesLong;
 import android.annotation.CallbackExecutor;
 import android.annotation.CurrentTimeMillisLong;
@@ -132,6 +133,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.telephony.Rlog;
 
 import java.io.IOException;
@@ -1777,8 +1779,8 @@
 
     /**
      * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
-     * to indicate user to decide whether current SIM should be preferred for all
-     * data / voice / sms. {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate
+     * to indicate the current SIM should be preferred for all data / voice / sms.
+     * {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate
      * which subscription should be the default subscription.
      * @hide
      */
@@ -6885,6 +6887,26 @@
         }
     }
 
+    // Suppressing AndroidFrameworkCompatChange because we're querying vendor
+    // partition SDK level, not application's target SDK version.
+    @SuppressWarnings("AndroidFrameworkCompatChange")
+    private boolean hasCapability(@NonNull String feature, @BoolRes int legacySetting) {
+        if (mContext == null) return true;
+
+        if (mContext.getPackageManager().hasSystemFeature(feature)) return true;
+
+        // Check SDK version of the vendor partition.
+        final int vendorApiLevel = SystemProperties.getInt(
+                "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT);
+        // Devices shipped with 2024Q2 or later are required to declare FEATURE_TELEPHONY_*
+        // for individual sub-features (calling, messaging, data), so there's no need to check
+        // the legacy setting.
+        if (vendorApiLevel < Build.VENDOR_API_2024_Q2) {
+            return mContext.getResources().getBoolean(legacySetting);
+        }
+        return false;
+    }
+
     // TODO(b/316183370): replace all @code with @link in javadoc after feature is released
     /**
      * @return true if the current device is "voice capable".
@@ -6904,11 +6926,9 @@
      * device (when {@code #isDeviceVoiceCapable} return {@code true}), caller should check for
      * subscription-level voice capability as well. See {@code #isDeviceVoiceCapable} for details.
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     @Deprecated
     public boolean isVoiceCapable() {
-        if (mContext == null) return true;
-        return mContext.getResources().getBoolean(
+        return hasCapability(PackageManager.FEATURE_TELEPHONY_CALLING,
                 com.android.internal.R.bool.config_voice_capable);
     }
 
@@ -6931,7 +6951,6 @@
      *
      * @see SubscriptionInfo#getServiceCapabilities()
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_CALLING)
     @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public boolean isDeviceVoiceCapable() {
         return isVoiceCapable();
@@ -6950,10 +6969,9 @@
      * device (when {@code #isDeviceSmsCapable} return {@code true}), caller should check for
      * subscription-level SMS capability as well. See {@code #isDeviceSmsCapable} for details.
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
+    @Deprecated
     public boolean isSmsCapable() {
-        if (mContext == null) return true;
-        return mContext.getResources().getBoolean(
+        return hasCapability(PackageManager.FEATURE_TELEPHONY_MESSAGING,
                 com.android.internal.R.bool.config_sms_capable);
     }
 
@@ -6973,7 +6991,6 @@
      *
      * @see SubscriptionInfo#getServiceCapabilities()
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)
     @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE)
     public boolean isDeviceSmsCapable() {
         return isSmsCapable();
@@ -9975,6 +9992,7 @@
             ALLOWED_NETWORK_TYPES_REASON_POWER,
             ALLOWED_NETWORK_TYPES_REASON_CARRIER,
             ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G,
+            ALLOWED_NETWORK_TYPES_REASON_TEST,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AllowedNetworkTypesReason {
@@ -10013,6 +10031,14 @@
     public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
 
     /**
+     * To indicate allowed network type change is requested by Testing purpose, should be default to
+     * {@link #getAllNetworkTypesBitmask} when done testing.
+     *
+     * @hide
+     */
+    public static final int ALLOWED_NETWORK_TYPES_REASON_TEST = 4;
+
+    /**
      * Set the allowed network types of the device and provide the reason triggering the allowed
      * network change.
      * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
@@ -10118,6 +10144,8 @@
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER:
             case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G:
                 return true;
+            case TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_TEST:
+                return TelephonyUtils.IS_DEBUGGABLE;
         }
         return false;
     }
@@ -13928,7 +13956,10 @@
      *          {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}.
      */
     @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_BASIC_PHONE_STATE,
+            android.Manifest.permission.READ_PHONE_STATE
+    })
     public void getCarrierRestrictionStatus(@NonNull Executor executor,
             @NonNull @CarrierRestrictionStatus
                     Consumer<Integer> resultListener) {
@@ -14527,10 +14558,8 @@
      * data connections over the telephony network.
      * <p>
      */
-    @RequiresFeature(PackageManager.FEATURE_TELEPHONY_DATA)
     public boolean isDataCapable() {
-        if (mContext == null) return true;
-        return mContext.getResources().getBoolean(
+        return hasCapability(PackageManager.FEATURE_TELEPHONY_DATA,
                 com.android.internal.R.bool.config_mobile_data_capable);
     }
 
diff --git a/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
index f981fb1..5f0d986 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteProvisionStateCallback.aidl
@@ -16,6 +16,8 @@
 
 package android.telephony.satellite;
 
+import android.telephony.satellite.SatelliteSubscriberProvisionStatus;
+
 /**
  * Interface for satellite provision state callback.
  * @hide
@@ -27,4 +29,14 @@
      * @param provisioned True means the service is provisioned and false means it is not.
      */
     void onSatelliteProvisionStateChanged(in boolean provisioned);
+
+    /**
+     * Called when the provisioning state of one or more SatelliteSubscriberInfos changes.
+     *
+     * @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states of
+     * the SatelliteSubscriberInfos.
+     * @hide
+     */
+    void onSatelliteSubscriptionProvisionStateChanged(in List<SatelliteSubscriberProvisionStatus>
+        satelliteSubscriberProvisionStatus);
 }
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
deleted file mode 100644
index fe46db8..0000000
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.aidl
+++ /dev/null
@@ -1,19 +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 android.telephony.satellite;
-
-parcelable ProvisionSubscriberId;
diff --git a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java b/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
deleted file mode 100644
index 3e6f743..0000000
--- a/telephony/java/android/telephony/satellite/ProvisionSubscriberId.java
+++ /dev/null
@@ -1,161 +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.telephony.satellite;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.telephony.flags.Flags;
-
-import java.util.Objects;
-
-/**
- * ProvisionSubscriberId
- *
- * Satellite Gateway client will use these subscriber ids to register with satellite gateway service
- * which identify user subscription with unique subscriber ids. These subscriber ids can be any
- * unique value like iccid, imsi or msisdn which is decided based upon carrier requirements.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public final class ProvisionSubscriberId implements Parcelable {
-    /** provision subscriberId */
-    @NonNull
-    private String mSubscriberId;
-
-    /** carrier id */
-    private int mCarrierId;
-
-    /** apn */
-    private String mNiddApn;
-
-    /**
-     * @hide
-     */
-    public ProvisionSubscriberId(@NonNull String subscriberId, @NonNull int carrierId,
-            @NonNull String niddApn) {
-        this.mCarrierId = carrierId;
-        this.mSubscriberId = subscriberId;
-        this.mNiddApn = niddApn;
-    }
-
-    private ProvisionSubscriberId(Parcel in) {
-        readFromParcel(in);
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeString(mSubscriberId);
-        out.writeInt(mCarrierId);
-        out.writeString(mNiddApn);
-    }
-
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public static final @android.annotation.NonNull Creator<ProvisionSubscriberId> CREATOR =
-            new Creator<ProvisionSubscriberId>() {
-                @Override
-                public ProvisionSubscriberId createFromParcel(Parcel in) {
-                    return new ProvisionSubscriberId(in);
-                }
-
-                @Override
-                public ProvisionSubscriberId[] newArray(int size) {
-                    return new ProvisionSubscriberId[size];
-                }
-            };
-
-    /**
-     * @hide
-     */
-    @Override
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * @return provision subscriberId.
-     * @hide
-     */
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public String getSubscriberId() {
-        return mSubscriberId;
-    }
-
-    /**
-     * @return carrierId.
-     * @hide
-     */
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public int getCarrierId() {
-        return mCarrierId;
-    }
-
-    /**
-     * @return niddApn.
-     * @hide
-     */
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public String getNiddApn() {
-        return mNiddApn;
-    }
-
-    @NonNull
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-
-        sb.append("SubscriberId:");
-        sb.append(mSubscriberId);
-        sb.append(",");
-
-        sb.append("CarrierId:");
-        sb.append(mCarrierId);
-        sb.append(",");
-
-        sb.append("NiddApn:");
-        sb.append(mNiddApn);
-        return sb.toString();
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mSubscriberId, mCarrierId, mNiddApn);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        ProvisionSubscriberId that = (ProvisionSubscriberId) o;
-        return mSubscriberId.equals(that.mSubscriberId) && mCarrierId
-                == that.mCarrierId && mNiddApn.equals(that.mNiddApn);
-    }
-
-    private void readFromParcel(Parcel in) {
-        mSubscriberId = in.readString();
-        mCarrierId = in.readInt();
-        mNiddApn = in.readString();
-    }
-}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index ad6db2d..3944b8e 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -234,7 +234,7 @@
 
     /**
      * Bundle key to get the response from
-     * {@link #requestProvisionSubscriberIds(Executor, OutcomeReceiver)}.
+     * {@link #requestSatelliteSubscriberProvisionStatus(Executor, OutcomeReceiver)}.
      * @hide
      */
     public static final String KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN =
@@ -242,19 +242,11 @@
 
     /**
      * Bundle key to get the response from
-     * {@link #requestIsProvisioned(String, Executor, OutcomeReceiver)}.
-     * @hide
-     */
-    public static final String KEY_IS_SATELLITE_PROVISIONED = "request_is_satellite_provisioned";
-
-    /**
-     * Bundle key to get the response from
      * {@link #provisionSatellite(List, Executor, OutcomeReceiver)}.
      * @hide
      */
     public static final String KEY_PROVISION_SATELLITE_TOKENS = "provision_satellite";
 
-
     /**
      * The request was successfully processed.
      */
@@ -420,6 +412,14 @@
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27;
 
+    /**
+     * Disabling satellite is in progress.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+    public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28;
+
     /** @hide */
     @IntDef(prefix = {"SATELLITE_RESULT_"}, value = {
             SATELLITE_RESULT_SUCCESS,
@@ -449,7 +449,8 @@
             SATELLITE_RESULT_MODEM_TIMEOUT,
             SATELLITE_RESULT_LOCATION_DISABLED,
             SATELLITE_RESULT_LOCATION_NOT_AVAILABLE,
-            SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS
+            SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS,
+            SATELLITE_RESULT_DISABLE_IN_PROGRESS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SatelliteResult {}
@@ -554,6 +555,16 @@
     public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2;
 
     /**
+     * This intent will be broadcasted if there are any change to list of subscriber informations.
+     * This intent will be sent only to the app with component defined in
+     * config_satellite_carrier_roaming_esos_provisioned_class and package defined in
+     * config_satellite_gateway_service_package
+     * @hide
+     */
+    public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED =
+            "android.telephony.action.ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
+
+    /**
      * Request to enable or disable the satellite modem and demo mode.
      * If satellite modem and cellular modem cannot work concurrently,
      * then this will disable the cellular modem if satellite modem is enabled,
@@ -590,7 +601,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                telephony.requestSatelliteEnabled(mSubId, attributes.isEnabled(),
+                telephony.requestSatelliteEnabled(attributes.isEnabled(),
                         attributes.isDemoMode(), attributes.isEmergencyMode(), errorCallback);
             } else {
                 Rlog.e(TAG, "requestEnabled() invalid telephony");
@@ -648,7 +659,7 @@
                         }
                     }
                 };
-                telephony.requestIsSatelliteEnabled(mSubId, receiver);
+                telephony.requestIsSatelliteEnabled(receiver);
             } else {
                 loge("requestIsEnabled() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -705,7 +716,7 @@
                         }
                     }
                 };
-                telephony.requestIsDemoModeEnabled(mSubId, receiver);
+                telephony.requestIsDemoModeEnabled(receiver);
             } else {
                 loge("requestIsDemoModeEnabled() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -762,7 +773,7 @@
                         }
                     }
                 };
-                telephony.requestIsEmergencyModeEnabled(mSubId, receiver);
+                telephony.requestIsEmergencyModeEnabled(receiver);
             } else {
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
@@ -819,7 +830,7 @@
                         }
                     }
                 };
-                telephony.requestIsSatelliteSupported(mSubId, receiver);
+                telephony.requestIsSatelliteSupported(receiver);
             } else {
                 loge("requestIsSupported() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -876,7 +887,7 @@
                         }
                     }
                 };
-                telephony.requestSatelliteCapabilities(mSubId, receiver);
+                telephony.requestSatelliteCapabilities(receiver);
             } else {
                 loge("requestCapabilities() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1202,8 +1213,7 @@
                             }
                         };
                 sSatelliteTransmissionUpdateCallbackMap.put(callback, internalCallback);
-                telephony.startSatelliteTransmissionUpdates(mSubId, errorCallback,
-                        internalCallback);
+                telephony.startSatelliteTransmissionUpdates(errorCallback, internalCallback);
             } else {
                 loge("startTransmissionUpdates() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1253,8 +1263,7 @@
                                     () -> resultListener.accept(result)));
                         }
                     };
-                    telephony.stopSatelliteTransmissionUpdates(mSubId, errorCallback,
-                            internalCallback);
+                    telephony.stopSatelliteTransmissionUpdates(errorCallback, internalCallback);
                     // TODO: Notify SmsHandler that pointing UI stopped
                 } else {
                     loge("stopSatelliteTransmissionUpdates: No internal callback.");
@@ -1310,7 +1319,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
+                cancelRemote = telephony.provisionSatelliteService(token, provisionData,
                         errorCallback);
             } else {
                 loge("provisionService() invalid telephony");
@@ -1362,7 +1371,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
+                telephony.deprovisionSatelliteService(token, errorCallback);
             } else {
                 loge("deprovisionService() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1405,10 +1414,19 @@
                                         () -> callback.onSatelliteProvisionStateChanged(
                                                 provisioned)));
                             }
+
+                            @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+                            @Override
+                            public void onSatelliteSubscriptionProvisionStateChanged(
+                                    @NonNull List<SatelliteSubscriberProvisionStatus>
+                                            satelliteSubscriberProvisionStatus) {
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onSatelliteSubscriptionProvisionStateChanged(
+                                                satelliteSubscriberProvisionStatus)));
+                            }
                         };
                 sSatelliteProvisionStateCallbackMap.put(callback, internalCallback);
-                return telephony.registerForSatelliteProvisionStateChanged(
-                        mSubId, internalCallback);
+                return telephony.registerForSatelliteProvisionStateChanged(internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -1441,7 +1459,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForSatelliteProvisionStateChanged(mSubId, internalCallback);
+                    telephony.unregisterForSatelliteProvisionStateChanged(internalCallback);
                 } else {
                     loge("unregisterForProvisionStateChanged: No internal callback.");
                 }
@@ -1498,7 +1516,7 @@
                         }
                     }
                 };
-                telephony.requestIsSatelliteProvisioned(mSubId, receiver);
+                telephony.requestIsSatelliteProvisioned(receiver);
             } else {
                 loge("requestIsProvisioned() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1548,7 +1566,7 @@
                     }
                 };
                 sSatelliteModemStateCallbackMap.put(callback, internalCallback);
-                return telephony.registerForSatelliteModemStateChanged(mSubId, internalCallback);
+                return telephony.registerForSatelliteModemStateChanged(internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -1581,7 +1599,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForModemStateChanged(mSubId, internalCallback);
+                    telephony.unregisterForModemStateChanged(internalCallback);
                 } else {
                     loge("unregisterForModemStateChanged: No internal callback.");
                 }
@@ -1644,7 +1662,7 @@
                             }
                         };
                 sSatelliteDatagramCallbackMap.put(callback, internalCallback);
-                return telephony.registerForIncomingDatagram(mSubId, internalCallback);
+                return telephony.registerForIncomingDatagram(internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -1676,7 +1694,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForIncomingDatagram(mSubId, internalCallback);
+                    telephony.unregisterForIncomingDatagram(internalCallback);
                 } else {
                     loge("unregisterForIncomingDatagram: No internal callback.");
                 }
@@ -1720,7 +1738,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                telephony.pollPendingDatagrams(mSubId, internalCallback);
+                telephony.pollPendingDatagrams(internalCallback);
             } else {
                 loge("pollPendingDatagrams() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(
@@ -1778,7 +1796,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                telephony.sendDatagram(mSubId, datagramType, datagram,
+                telephony.sendDatagram(datagramType, datagram,
                         needFullScreenPointingUI, internalCallback);
             } else {
                 loge("sendDatagram() invalid telephony");
@@ -1896,7 +1914,7 @@
                         }
                     }
                 };
-                telephony.requestTimeForNextSatelliteVisibility(mSubId, receiver);
+                telephony.requestTimeForNextSatelliteVisibility(receiver);
             } else {
                 loge("requestTimeForNextSatelliteVisibility() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -1927,7 +1945,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.setDeviceAlignedWithSatellite(mSubId, isAligned);
+                telephony.setDeviceAlignedWithSatellite(isAligned);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -2191,7 +2209,7 @@
                         }
                     }
                 };
-                telephony.requestNtnSignalStrength(mSubId, receiver);
+                telephony.requestNtnSignalStrength(receiver);
             } else {
                 loge("requestNtnSignalStrength() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
@@ -2242,7 +2260,7 @@
                                                 ntnSignalStrength)));
                             }
                         };
-                telephony.registerForNtnSignalStrengthChanged(mSubId, internalCallback);
+                telephony.registerForNtnSignalStrengthChanged(internalCallback);
                 sNtnSignalStrengthCallbackMap.put(callback, internalCallback);
             } else {
                 throw new IllegalStateException("Telephony service is null.");
@@ -2282,7 +2300,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForNtnSignalStrengthChanged(mSubId, internalCallback);
+                    telephony.unregisterForNtnSignalStrengthChanged(internalCallback);
                 } else {
                     loge("unregisterForNtnSignalStrengthChanged: No internal callback.");
                     throw new IllegalArgumentException("callback is not valid");
@@ -2327,7 +2345,7 @@
                             }
                         };
                 sSatelliteCapabilitiesCallbackMap.put(callback, internalCallback);
-                return telephony.registerForCapabilitiesChanged(mSubId, internalCallback);
+                return telephony.registerForCapabilitiesChanged(internalCallback);
             } else {
                 throw new IllegalStateException("Telephony service is null.");
             }
@@ -2360,7 +2378,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForCapabilitiesChanged(mSubId, internalCallback);
+                    telephony.unregisterForCapabilitiesChanged(internalCallback);
                 } else {
                     loge("unregisterForCapabilitiesChanged: No internal callback.");
                 }
@@ -2436,7 +2454,7 @@
                         };
                 sSatelliteSupportedStateCallbackMap.put(callback, internalCallback);
                 return telephony.registerForSatelliteSupportedStateChanged(
-                        mSubId, internalCallback);
+                        internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -2471,7 +2489,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForSatelliteSupportedStateChanged(mSubId, internalCallback);
+                    telephony.unregisterForSatelliteSupportedStateChanged(internalCallback);
                 } else {
                     loge("unregisterForSupportedStateChanged: No internal callback.");
                 }
@@ -2642,8 +2660,10 @@
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public void requestProvisionSubscriberIds(@NonNull @CallbackExecutor Executor executor,
-            @NonNull OutcomeReceiver<List<ProvisionSubscriberId>, SatelliteException> callback) {
+    public void requestSatelliteSubscriberProvisionStatus(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<List<SatelliteSubscriberProvisionStatus>,
+                    SatelliteException> callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
@@ -2655,10 +2675,10 @@
                     protected void onReceiveResult(int resultCode, Bundle resultData) {
                         if (resultCode == SATELLITE_RESULT_SUCCESS) {
                             if (resultData.containsKey(KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN)) {
-                                List<ProvisionSubscriberId> list =
+                                List<SatelliteSubscriberProvisionStatus> list =
                                         resultData.getParcelableArrayList(
                                                 KEY_REQUEST_PROVISION_SUBSCRIBER_ID_TOKEN,
-                                                ProvisionSubscriberId.class);
+                                                SatelliteSubscriberProvisionStatus.class);
                                 executor.execute(() -> Binder.withCleanCallingIdentity(() ->
                                         callback.onResult(list)));
                             } else {
@@ -2673,79 +2693,23 @@
                         }
                     }
                 };
-                telephony.requestProvisionSubscriberIds(receiver);
+                telephony.requestSatelliteSubscriberProvisionStatus(receiver);
             } else {
-                loge("requestProvisionSubscriberIds() invalid telephony");
+                loge("requestSatelliteSubscriberProvisionStatus() invalid telephony");
                 executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                         new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
             }
         } catch (RemoteException ex) {
-            loge("requestProvisionSubscriberIds() RemoteException: " + ex);
+            loge("requestSatelliteSubscriberProvisionStatus() RemoteException: " + ex);
             executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
                     new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
         }
     }
 
     /**
-     * Request to get provisioned status for given a satellite subscriber id.
+     * Deliver the list of provisioned satellite subscriber infos.
      *
-     * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
-     * @param executor The executor on which the callback will be called.
-     * @param callback callback.
-     *
-     * @throws SecurityException if the caller doesn't have required permission.
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public void requestIsProvisioned(@NonNull String satelliteSubscriberId,
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
-        Objects.requireNonNull(satelliteSubscriberId);
-        Objects.requireNonNull(executor);
-        Objects.requireNonNull(callback);
-
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                ResultReceiver receiver = new ResultReceiver(null) {
-                    @Override
-                    protected void onReceiveResult(int resultCode, Bundle resultData) {
-                        if (resultCode == SATELLITE_RESULT_SUCCESS) {
-                            if (resultData.containsKey(KEY_IS_SATELLITE_PROVISIONED)) {
-                                boolean isIsProvisioned =
-                                        resultData.getBoolean(KEY_IS_SATELLITE_PROVISIONED);
-                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
-                                        callback.onResult(isIsProvisioned)));
-                            } else {
-                                loge("KEY_IS_SATELLITE_PROVISIONED does not exist.");
-                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
-                                        callback.onError(new SatelliteException(
-                                                SATELLITE_RESULT_REQUEST_FAILED))));
-                            }
-                        } else {
-                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
-                                    callback.onError(new SatelliteException(resultCode))));
-                        }
-                    }
-                };
-                telephony.requestIsProvisioned(satelliteSubscriberId, receiver);
-            } else {
-                loge("requestIsSatelliteProvisioned() invalid telephony");
-                executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
-                        new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
-            }
-        } catch (RemoteException ex) {
-            loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
-            executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError(
-                    new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE))));
-        }
-    }
-
-    /**
-     * Deliver the list of provisioned satellite subscriber ids.
-     *
-     * @param list List of ProvisionSubscriberId.
+     * @param list The list of provisioned satellite subscriber infos.
      * @param executor The executor on which the callback will be called.
      * @param callback The callback object to which the result will be delivered.
      *
@@ -2754,7 +2718,7 @@
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-    public void provisionSatellite(@NonNull List<ProvisionSubscriberId> list,
+    public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
new file mode 100644
index 0000000..5e56f84
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
@@ -0,0 +1,132 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * SatelliteModemEnableRequestAttributes is used to pack info needed by modem to allow carrier to
+ * roam to satellite.
+ *
+ * @hide
+ */
+public final class SatelliteModemEnableRequestAttributes implements Parcelable {
+
+    /** {@code true} to enable satellite and {@code false} to disable satellite */
+    private final boolean mIsEnabled;
+    /**
+     * {@code true} to enable demo mode and {@code false} to disable. When disabling satellite,
+     * {@code mIsDemoMode} is always considered as {@code false} by Telephony.
+     */
+    private final boolean mIsDemoMode;
+    /**
+     * {@code true} means satellite is enabled for emergency mode, {@code false} otherwise. When
+     * disabling satellite, {@code isEmergencyMode} is always considered as {@code false} by
+     * Telephony.
+     */
+    private final boolean mIsEmergencyMode;
+
+    /** The subscription related info */
+    @NonNull private final SatelliteSubscriptionInfo mSatelliteSubscriptionInfo;
+
+    public SatelliteModemEnableRequestAttributes(boolean isEnabled, boolean isDemoMode,
+            boolean isEmergencyMode, @NonNull SatelliteSubscriptionInfo satelliteSubscriptionInfo) {
+        mIsEnabled = isEnabled;
+        mIsDemoMode = isDemoMode;
+        mIsEmergencyMode = isEmergencyMode;
+        mSatelliteSubscriptionInfo = satelliteSubscriptionInfo;
+    }
+
+    private SatelliteModemEnableRequestAttributes(Parcel in) {
+        mIsEnabled = in.readBoolean();
+        mIsDemoMode = in.readBoolean();
+        mIsEmergencyMode = in.readBoolean();
+        mSatelliteSubscriptionInfo = in.readParcelable(
+                SatelliteSubscriptionInfo.class.getClassLoader(), SatelliteSubscriptionInfo.class);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeBoolean(mIsEnabled);
+        dest.writeBoolean(mIsDemoMode);
+        dest.writeBoolean(mIsEmergencyMode);
+        mSatelliteSubscriptionInfo.writeToParcel(dest, flags);
+    }
+
+    public static final Creator<SatelliteModemEnableRequestAttributes> CREATOR = new Creator<>() {
+        @Override
+        public SatelliteModemEnableRequestAttributes createFromParcel(Parcel in) {
+            return new SatelliteModemEnableRequestAttributes(in);
+        }
+
+        @Override
+        public SatelliteModemEnableRequestAttributes[] newArray(int size) {
+            return new SatelliteModemEnableRequestAttributes[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return (new StringBuilder()).append("SatelliteModemEnableRequestAttributes{")
+                .append(", mIsEnabled=").append(mIsEnabled)
+                .append(", mIsDemoMode=").append(mIsDemoMode)
+                .append(", mIsDemoMode=").append(mIsDemoMode)
+                .append("mSatelliteSubscriptionInfo=").append(mSatelliteSubscriptionInfo)
+                .append("}")
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SatelliteModemEnableRequestAttributes that = (SatelliteModemEnableRequestAttributes) o;
+        return mIsEnabled == that.mIsEnabled && mIsDemoMode == that.mIsDemoMode
+                && mIsEmergencyMode == that.mIsEmergencyMode && mSatelliteSubscriptionInfo.equals(
+                that.mSatelliteSubscriptionInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsEnabled, mIsDemoMode, mIsEmergencyMode, mSatelliteSubscriptionInfo);
+    }
+
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    public boolean isDemoMode() {
+        return mIsDemoMode;
+    }
+
+    public boolean isEmergencyMode() {
+        return mIsEmergencyMode;
+    }
+
+    @NonNull public SatelliteSubscriptionInfo getSatelliteSubscriptionInfo() {
+        return mSatelliteSubscriptionInfo;
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index a12952b..e8ae0f5 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -17,10 +17,13 @@
 package android.telephony.satellite;
 
 import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 
 import com.android.internal.telephony.flags.Flags;
 
+import java.util.List;
+
 /**
  * A callback class for monitoring satellite provision state change events.
  *
@@ -39,4 +42,16 @@
      */
     @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
     void onSatelliteProvisionStateChanged(boolean provisioned);
+
+    /**
+     * Called when the provisioning state of one or more SatelliteSubscriberInfos changes.
+     *
+     * @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states
+     *                                           of the SatelliteSubscriberInfos.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    default void onSatelliteSubscriptionProvisionStateChanged(
+            @NonNull List<SatelliteSubscriberProvisionStatus>
+                    satelliteSubscriberProvisionStatus) {};
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
new file mode 100644
index 0000000..992c9ae
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.satellite;
+
+parcelable SatelliteSubscriberInfo;
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
new file mode 100644
index 0000000..dbe5dddf
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -0,0 +1,285 @@
+/*
+ * 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.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * SatelliteSubscriberInfo
+ *
+ * Satellite Gateway client will use these subscriber ids to register with satellite gateway service
+ * which identify user subscription with unique subscriber ids. These subscriber ids can be any
+ * unique value like iccid, imsi or msisdn which is decided based upon carrier requirements.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public final class SatelliteSubscriberInfo implements Parcelable {
+    /** provision subscriberId */
+    @NonNull
+    private String mSubscriberId;
+    /** carrier id */
+    private int mCarrierId;
+
+    /** apn */
+    private String mNiddApn;
+    private int mSubId;
+
+    /** SubscriberId format is the ICCID. */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final int ICCID = 0;
+    /** SubscriberId format is the 6 digit of IMSI + MSISDN. */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final int IMSI_MSISDN = 1;
+
+    /** Type of subscriber id */
+    @SubscriberIdType private int mSubscriberIdType;
+    /** @hide */
+    @IntDef(prefix = "SubscriberId_Type_", value = {
+            ICCID,
+            IMSI_MSISDN
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SubscriberIdType {}
+
+    private SatelliteSubscriberInfo(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public SatelliteSubscriberInfo(@NonNull Builder builder) {
+        this.mSubscriberId = builder.mSubscriberId;
+        this.mCarrierId = builder.mCarrierId;
+        this.mNiddApn = builder.mNiddApn;
+        this.mSubId = builder.mSubId;
+        this.mSubscriberIdType = builder.mSubscriberIdType;
+    }
+
+    /**
+     * Builder class for constructing SatelliteSubscriberInfo objects
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static class Builder {
+        @NonNull private String mSubscriberId;
+        private int mCarrierId;
+        @NonNull
+        private String mNiddApn;
+        private int mSubId;
+        @SubscriberIdType
+        private int mSubscriberIdType;
+
+        /**
+         * Set the SubscriberId and returns the Builder class.
+         *
+         * @hide
+         */
+        public Builder setSubscriberId(String subscriberId) {
+            mSubscriberId = subscriberId;
+            return this;
+        }
+
+        /**
+         * Set the CarrierId and returns the Builder class.
+         * @hide
+         */
+        @NonNull
+        public Builder setCarrierId(int carrierId) {
+            mCarrierId = carrierId;
+            return this;
+        }
+
+        /**
+         * Set the niddApn and returns the Builder class.
+         * @hide
+         */
+        @NonNull
+        public Builder setNiddApn(String niddApn) {
+            mNiddApn = niddApn;
+            return this;
+        }
+
+        /**
+         * Set the subId and returns the Builder class.
+         * @hide
+         */
+        @NonNull
+        public Builder setSubId(int subId) {
+            mSubId = subId;
+            return this;
+        }
+
+        /**
+         * Set the SubscriberIdType and returns the Builder class.
+         * @hide
+         */
+        @NonNull
+        public Builder setSubscriberIdType(@SubscriberIdType int subscriberIdType) {
+            mSubscriberIdType = subscriberIdType;
+            return this;
+        }
+
+        /**
+         * Returns SatelliteSubscriberInfo object.
+         * @hide
+         */
+        @NonNull
+        public SatelliteSubscriberInfo build() {
+            return new SatelliteSubscriberInfo(this);
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeString(mSubscriberId);
+        out.writeInt(mCarrierId);
+        out.writeString(mNiddApn);
+        out.writeInt(mSubId);
+        out.writeInt(mSubscriberIdType);
+    }
+
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final @android.annotation.NonNull Creator<SatelliteSubscriberInfo> CREATOR =
+            new Creator<SatelliteSubscriberInfo>() {
+                @Override
+                public SatelliteSubscriberInfo createFromParcel(Parcel in) {
+                    return new SatelliteSubscriberInfo(in);
+                }
+
+                @Override
+                public SatelliteSubscriberInfo[] newArray(int size) {
+                    return new SatelliteSubscriberInfo[size];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * @return provision subscriberId.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public String getSubscriberId() {
+        return mSubscriberId;
+    }
+
+    /**
+     * @return carrierId.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int getCarrierId() {
+        return mCarrierId;
+    }
+
+    /**
+     * @return niddApn.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public String getNiddApn() {
+        return mNiddApn;
+    }
+
+    /**
+     * @return subId.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int getSubId() {
+        return mSubId;
+    }
+
+    /**
+     * @return subscriberIdType.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public @SubscriberIdType int getSubscriberIdType() {
+        return mSubscriberIdType;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("SubscriberId:");
+        sb.append(mSubscriberId);
+        sb.append(",");
+
+        sb.append("CarrierId:");
+        sb.append(mCarrierId);
+        sb.append(",");
+
+        sb.append("NiddApn:");
+        sb.append(mNiddApn);
+        sb.append(",");
+
+        sb.append("SubId:");
+        sb.append(mSubId);
+        sb.append(",");
+
+        sb.append("SubscriberIdType:");
+        sb.append(mSubscriberIdType);
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSubscriberId, mCarrierId, mNiddApn, mSubId, mSubscriberIdType);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SatelliteSubscriberInfo)) return false;
+        SatelliteSubscriberInfo that = (SatelliteSubscriberInfo) o;
+        return Objects.equals(mSubscriberId, that.mSubscriberId) && mCarrierId == that.mCarrierId
+                && Objects.equals(mNiddApn, that.mNiddApn) && mSubId == that.mSubId
+                && mSubscriberIdType == that.mSubscriberIdType;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mSubscriberId = in.readString();
+        mCarrierId = in.readInt();
+        mNiddApn = in.readString();
+        mSubId = in.readInt();
+        mSubscriberIdType = in.readInt();
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl
new file mode 100644
index 0000000..80de779
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.satellite;
+
+parcelable SatelliteSubscriberProvisionStatus;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
new file mode 100644
index 0000000..08ef3f2
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -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 android.telephony.satellite;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.telephony.flags.Flags;
+
+import java.util.Objects;
+
+/**
+ * Represents the provisioning state of SatelliteSubscriberInfo.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+public class SatelliteSubscriberProvisionStatus implements Parcelable {
+    private SatelliteSubscriberInfo mSubscriberInfo;
+    /** {@code true} mean the satellite subscriber is provisioned, {@code false} otherwise. */
+    private boolean mProvisionStatus;
+
+    public SatelliteSubscriberProvisionStatus(@NonNull Builder builder) {
+        mSubscriberInfo = builder.mSubscriberInfo;
+        mProvisionStatus = builder.mProvisionStatus;
+    }
+
+    /**
+     * Builder class for constructing SatelliteSubscriberProvisionStatus objects
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static class Builder {
+        private SatelliteSubscriberInfo mSubscriberInfo;
+        private boolean mProvisionStatus;
+
+        /**
+         * Set the SatelliteSubscriberInfo and returns the Builder class.
+         * @hide
+         */
+        public Builder setSatelliteSubscriberInfo(SatelliteSubscriberInfo satelliteSubscriberInfo) {
+            mSubscriberInfo = satelliteSubscriberInfo;
+            return this;
+        }
+
+        /**
+         * Set the SatelliteSubscriberInfo's provisionStatus and returns the Builder class.
+         * @hide
+         */
+        @NonNull
+        public Builder setProvisionStatus(boolean provisionStatus) {
+            mProvisionStatus = provisionStatus;
+            return this;
+        }
+
+        /**
+         * Returns SatelliteSubscriberProvisionStatus object.
+         * @hide
+         */
+        @NonNull
+        public SatelliteSubscriberProvisionStatus build() {
+            return new SatelliteSubscriberProvisionStatus(this);
+        }
+    }
+
+    private SatelliteSubscriberProvisionStatus(Parcel in) {
+        readFromParcel(in);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(mSubscriberInfo, flags);
+        out.writeBoolean(mProvisionStatus);
+    }
+
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public static final @android.annotation.NonNull Creator<SatelliteSubscriberProvisionStatus>
+            CREATOR =
+            new Creator<SatelliteSubscriberProvisionStatus>() {
+                @Override
+                public SatelliteSubscriberProvisionStatus createFromParcel(Parcel in) {
+                    return new SatelliteSubscriberProvisionStatus(in);
+                }
+
+                @Override
+                public SatelliteSubscriberProvisionStatus[] newArray(int size) {
+                    return new SatelliteSubscriberProvisionStatus[size];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    @Override
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * SatelliteSubscriberInfo that has a provisioning state.
+     * @return SatelliteSubscriberInfo.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public @NonNull SatelliteSubscriberInfo getSatelliteSubscriberInfo() {
+        return mSubscriberInfo;
+    }
+
+    /**
+     * SatelliteSubscriberInfo's provisioning state.
+     * @return {@code true} means provisioning. {@code false} means deprovisioning.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+    public @NonNull  boolean getProvisionStatus() {
+        return mProvisionStatus;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("SatelliteSubscriberInfo:");
+        sb.append(mSubscriberInfo);
+        sb.append(",");
+
+        sb.append("ProvisionStatus:");
+        sb.append(mProvisionStatus);
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mSubscriberInfo, mProvisionStatus);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof SatelliteSubscriberProvisionStatus)) return false;
+        SatelliteSubscriberProvisionStatus that = (SatelliteSubscriberProvisionStatus) o;
+        return Objects.equals(mSubscriberInfo, that.mSubscriberInfo)
+                && mProvisionStatus == that.mProvisionStatus;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mSubscriberInfo = in.readParcelable(SatelliteSubscriberInfo.class.getClassLoader(),
+                SatelliteSubscriberInfo.class);
+        mProvisionStatus = in.readBoolean();
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
new file mode 100644
index 0000000..2ef19f8
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.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.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * SatelliteSubscriptionInfo is used to pack subscription related info needed by modem to allow
+ * carrier to roam to satellite.
+ *
+ * @hide
+ */
+public final class SatelliteSubscriptionInfo implements Parcelable {
+    /**
+     * The ICC ID used for satellite attachment.
+     */
+    @NonNull private final String mIccId;
+
+    /**
+     * The NIDD(Non IP Data) APN to be used for carrier roaming to satellite attachment.
+     */
+    @NonNull private final String mNiddApn;
+
+    public SatelliteSubscriptionInfo(@NonNull String iccId, @NonNull String niddApn) {
+        mIccId = iccId;
+        mNiddApn = niddApn;
+    }
+
+    private SatelliteSubscriptionInfo(Parcel in) {
+        mIccId = in.readString8();
+        mNiddApn = in.readString8();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mIccId);
+        dest.writeString8(mNiddApn);
+    }
+
+    @NonNull public static final Creator<SatelliteSubscriptionInfo> CREATOR = new Creator<>() {
+        @Override
+        public SatelliteSubscriptionInfo createFromParcel(Parcel in) {
+            return new SatelliteSubscriptionInfo(in);
+        }
+
+        @Override
+        public SatelliteSubscriptionInfo[] newArray(int size) {
+            return new SatelliteSubscriptionInfo[size];
+        }
+    };
+
+    @Override
+    @NonNull public String toString() {
+        return (new StringBuilder()).append("SatelliteSubscriptionInfo{")
+                .append("IccId=").append(mIccId)
+                .append(", NiddApn=").append(mNiddApn)
+                .append("}")
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SatelliteSubscriptionInfo that = (SatelliteSubscriptionInfo) o;
+        return mIccId.equals(that.getIccId()) && mNiddApn.equals(that.getNiddApn());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getIccId(), getNiddApn());
+    }
+
+    @NonNull
+    public String getIccId() {
+        return mIccId;
+    }
+
+    @NonNull
+    public String getNiddApn() {
+        return mNiddApn;
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index e66a082..8b51321 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -24,6 +24,7 @@
 import android.telephony.satellite.stub.ISatelliteListener;
 import android.telephony.satellite.stub.SatelliteDatagram;
 import android.telephony.satellite.stub.SystemSelectionSpecifier;
+import android.telephony.satellite.stub.SatelliteModemEnableRequestAttributes;
 
 /**
  * {@hide}
@@ -82,11 +83,15 @@
      * is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
      *
-     * @param enableSatellite True to enable the satellite modem and false to disable.
-     * @param enableDemoMode True to enable demo mode and false to disable.
-     * @param isEmergency To specify the satellite is enabled for emergency session and false for
-     * non emergency session. Note: it is possible that a emergency session started get converted
-     * to a non emergency session and vice versa.
+     * Framework might send an enable request to update the enable attributes of an already-started
+     * satellite session. In such cases, modem needs to apply the new enable attrbitues to the
+     * satellite session. Moreover, modem needs to report its current state and signal strength
+     * level to framework right after receiving this request from framework.
+     *
+     * Framework might send a disable request when an enable request is being processed. In such
+     * cases, modem needs to abort the enable request and process the disable request.
+     *
+     * @param enableAttributes The enable parameters that will be applied to the satellite session
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid result codes returned:
@@ -99,8 +104,8 @@
      *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
      *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
      */
-    void requestSatelliteEnabled(in boolean enableSatellite, in boolean enableDemoMode,
-            in boolean isEmergency, in IIntegerConsumer resultCallback);
+    void requestSatelliteEnabled(in SatelliteModemEnableRequestAttributes enableAttributes,
+            in IIntegerConsumer resultCallback);
 
     /**
      * Request to get whether the satellite modem is enabled.
diff --git a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl b/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
deleted file mode 100644
index 460de8c..0000000
--- a/telephony/java/android/telephony/satellite/stub/ProvisionSubscriberId.aidl
+++ /dev/null
@@ -1,31 +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.telephony.satellite.stub;
-
-/**
- * {@hide}
- */
-parcelable ProvisionSubscriberId {
-    /** provision subscriberId */
-    String subscriberId;
-
-    /** carrier id */
-    int mCarrierId;
-
-    /** apn */
-    String mNiddApn;
-}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index c50e469..4f47210 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -89,12 +89,11 @@
         }
 
         @Override
-        public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
-                boolean isEmergency, IIntegerConsumer resultCallback) throws RemoteException {
+        public void requestSatelliteEnabled(SatelliteModemEnableRequestAttributes enableAttributes,
+                IIntegerConsumer resultCallback) throws RemoteException {
             executeMethodAsync(
                     () -> SatelliteImplBase.this
-                            .requestSatelliteEnabled(
-                                    enableSatellite, enableDemoMode, isEmergency, resultCallback),
+                            .requestSatelliteEnabled(enableAttributes, resultCallback),
                     "requestSatelliteEnabled");
         }
 
@@ -325,11 +324,7 @@
      * enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
      *
-     * @param enableSatellite True to enable the satellite modem and false to disable.
-     * @param enableDemoMode True to enable demo mode and false to disable.
-     * @param isEmergency To specify the satellite is enabled for emergency session and false for
-     * non emergency session. Note: it is possible that a emergency session started get converted
-     * to a non emergency session and vice versa.
+     * @param enableAttributes The enable parameters that will be applied to the satellite session
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid result codes returned:
@@ -342,8 +337,8 @@
      *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
      *   SatelliteResult:SATELLITE_RESULT_NO_RESOURCES
      */
-    public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
-            boolean isEmergency, @NonNull IIntegerConsumer resultCallback) {
+    public void requestSatelliteEnabled(SatelliteModemEnableRequestAttributes enableAttributes,
+            @NonNull IIntegerConsumer resultCallback) {
         // stub implementation
     }
 
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemEnableRequestAttributes.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemEnableRequestAttributes.aidl
new file mode 100644
index 0000000..0a40e15
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemEnableRequestAttributes.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+import android.telephony.satellite.stub.SatelliteSubscriptionInfo;
+
+/**
+ * {@hide}
+ */
+ parcelable SatelliteModemEnableRequestAttributes {
+    /**
+     * {@code true} to enable satellite and {@code false} to disable.
+     */
+    boolean isEnabled;
+    /**
+     * {@code true} to enable demo mode and {@code false} to disable.
+     */
+    boolean isDemoMode;
+    /**
+     * {@code true} to enable emergency modeand {@code false} to disable.
+     */
+    boolean isEmergencyMode;
+    /**
+     * The subscription related info.
+     */
+    SatelliteSubscriptionInfo satelliteSubscriptionInfo;
+ }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
index 162fe2b..24377c7 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
@@ -22,7 +22,7 @@
 @Backing(type="int")
 enum SatelliteModemState {
     /**
-     * Satellite modem is in idle state.
+     * Satellite Idle state during which modem will scan for TN networks.
      */
     SATELLITE_MODEM_STATE_IDLE = 0,
     /**
@@ -46,13 +46,13 @@
      */
     SATELLITE_MODEM_STATE_UNAVAILABLE = 5,
     /**
-     * The satellite modem is powered on but the device is not registered to a satellite cell.
+     * The satellite modem is powered on but the device is out of service.
      */
-    SATELLITE_MODEM_STATE_NOT_CONNECTED = 6,
+    SATELLITE_MODEM_STATE_OUT_OF_SERVICE = 6,
     /**
-     * The satellite modem is powered on and the device is registered to a satellite cell.
+     * The satellite modem is powered on and the device is registered and in service.
      */
-    SATELLITE_MODEM_STATE_CONNECTED = 7,
+    SATELLITE_MODEM_STATE_IN_SERVICE = 7,
     /**
      * Satellite modem state is unknown. This generic modem state should be used only when the
      * modem state cannot be mapped to other specific modem states.
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
new file mode 100644
index 0000000..fb44f87
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriberInfo.aidl
@@ -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 android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+parcelable SatelliteSubscriberInfo {
+    /** provision subscriberId */
+    String subscriberId;
+
+    /** carrier id */
+    int mCarrierId;
+
+    /** apn */
+    String mNiddApn;
+}
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteSubscriptionInfo.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriptionInfo.aidl
new file mode 100644
index 0000000..f664dda
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteSubscriptionInfo.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+ parcelable SatelliteSubscriptionInfo {
+    /**
+     * The ICC ID used for satellite attachment.
+     */
+    String iccId;
+    /**
+     * The NIDD(Non IP Data) APN to be used for carrier roaming to satellite attachment.
+     */
+    String niddApn;
+ }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2f8e957..e852e6b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -78,7 +78,7 @@
 import android.telephony.satellite.NtnSignalStrength;
 import android.telephony.satellite.SatelliteCapabilities;
 import android.telephony.satellite.SatelliteDatagram;
-import android.telephony.satellite.ProvisionSubscriberId;
+import android.telephony.satellite.SatelliteSubscriberInfo;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IBooleanConsumer;
@@ -2743,7 +2743,6 @@
     /**
      * Request to enable or disable the satellite modem.
      *
-     * @param subId The subId of the subscription to enable or disable the satellite modem for.
      * @param enableSatellite True to enable the satellite modem and false to disable.
      * @param enableDemoMode True if demo mode is enabled and false otherwise. When
      *                       disabling satellite, {@code enableDemoMode} is always considered as
@@ -2755,93 +2754,83 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestSatelliteEnabled(int subId, boolean enableSatellite, boolean enableDemoMode,
+    void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
             boolean isEmergency, in IIntegerConsumer callback);
 
     /**
      * Request to get whether the satellite modem is enabled.
      *
-     * @param subId The subId of the subscription to request whether satellite is enabled for.
      * @param receiver Result receiver to get the error code of the request and whether the
      *                 satellite modem is enabled.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestIsSatelliteEnabled(int subId, in ResultReceiver receiver);
+    void requestIsSatelliteEnabled(in ResultReceiver receiver);
 
     /**
      * Request to get whether the satellite service demo mode is enabled.
      *
-     * @param subId The subId of the subscription to request whether the satellite demo mode is
-     *              enabled for.
      * @param receiver Result receiver to get the error code of the request and whether the
      *                 satellite demo mode is enabled.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestIsDemoModeEnabled(int subId, in ResultReceiver receiver);
+    void requestIsDemoModeEnabled(in ResultReceiver receiver);
 
     /**
      * Request to get whether the satellite service is enabled with emergency mode.
      *
-     * @param subId The subId of the subscription to request whether the satellite demo mode is
-     *              enabled for.
      * @param receiver Result receiver to get the error code of the request and whether the
      *                 satellite is enabled with emergency mode.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestIsEmergencyModeEnabled(int subId, in ResultReceiver receiver);
+    void requestIsEmergencyModeEnabled(in ResultReceiver receiver);
 
     /**
      * Request to get whether the satellite service is supported on the device.
      *
-     * @param subId The subId of the subscription to check whether satellite is supported for.
      * @param receiver Result receiver to get the error code of the request and whether the
      *                 satellite service is supported on the device.
      */
-    void requestIsSatelliteSupported(int subId, in ResultReceiver receiver);
+    void requestIsSatelliteSupported(in ResultReceiver receiver);
 
     /**
      * Request to get the capabilities of the satellite service.
      *
-     * @param subId The subId of the subscription to get the capabilities for.
      * @param receiver Result receiver to get the error code of the request and the requested
      *                 capabilities of the satellite service.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestSatelliteCapabilities(int subId, in ResultReceiver receiver);
+    void requestSatelliteCapabilities(in ResultReceiver receiver);
 
     /**
      * Start receiving satellite transmission updates.
      *
-     * @param subId The subId of the subscription to stop satellite transmission updates for.
      * @param resultCallback The callback to get the result of the request.
      * @param callback The callback to handle transmission updates.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void startSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback,
+    void startSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback,
             in ISatelliteTransmissionUpdateCallback callback);
 
     /**
      * Stop receiving satellite transmission updates.
      *
-     * @param subId The subId of the subscritpion to stop satellite transmission updates for.
      * @param resultCallback The callback to get the result of the request.
      * @param callback The callback that was passed to startSatelliteTransmissionUpdates.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void stopSatelliteTransmissionUpdates(int subId, in IIntegerConsumer resultCallback,
+    void stopSatelliteTransmissionUpdates(in IIntegerConsumer resultCallback,
             in ISatelliteTransmissionUpdateCallback callback);
 
     /**
      * Register the subscription with a satellite provider.
      * This is needed to register the subscription if the provider allows dynamic registration.
      *
-     * @param subId The subId of the subscription to be provisioned.
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
      * @provisionData Data from the provisioning app that can be used by provisioning server
@@ -2851,7 +2840,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    ICancellationSignal provisionSatelliteService(int subId, in String token,
+    ICancellationSignal provisionSatelliteService(in String token,
             in byte[] provisionData, in IIntegerConsumer callback);
 
     /**
@@ -2861,110 +2850,99 @@
      * {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged}
      * should report as deprovisioned.
      *
-     * @param subId The subId of the subscription to be deprovisioned.
      * @param token The token of the device/subscription to be deprovisioned.
      * @param callback The callback to get the result of the request.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback);
+    void deprovisionSatelliteService(in String token, in IIntegerConsumer callback);
 
     /**
      * Registers for provision state changed from satellite modem.
      *
-     * @param subId The subId of the subscription to register for provision state changed.
      * @param callback The callback to handle the satellite provision state changed event.
      *
      * @return The {@link SatelliteError} result of the operation.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForSatelliteProvisionStateChanged(int subId,
-            in ISatelliteProvisionStateCallback callback);
+    int registerForSatelliteProvisionStateChanged(in ISatelliteProvisionStateCallback callback);
 
     /**
      * Unregisters for provision state changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for provision state changed.
      * @param callback The callback that was passed to registerForSatelliteProvisionStateChanged.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForSatelliteProvisionStateChanged(int subId,
+    void unregisterForSatelliteProvisionStateChanged(
             in ISatelliteProvisionStateCallback callback);
 
     /**
      * Request to get whether the device is provisioned with a satellite provider.
      *
-     * @param subId The subId of the subscription to get whether the device is provisioned for.
      * @param receiver Result receiver to get the error code of the request and whether the
      *                 device is provisioned with a satellite provider.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestIsSatelliteProvisioned(int subId, in ResultReceiver receiver);
+    void requestIsSatelliteProvisioned(in ResultReceiver receiver);
 
     /**
      * Registers for modem state changed from satellite modem.
      *
-     * @param subId The subId of the subscription to register for satellite modem state changed.
      * @param callback The callback to handle the satellite modem state changed event.
      *
      * @return The {@link SatelliteError} result of the operation.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForSatelliteModemStateChanged(int subId, ISatelliteModemStateCallback callback);
+    int registerForSatelliteModemStateChanged(ISatelliteModemStateCallback callback);
 
     /**
      * Unregisters for modem state changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for satellite modem state changed.
      * @param callback The callback that was passed to registerForSatelliteStateChanged.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForModemStateChanged(int subId, ISatelliteModemStateCallback callback);
+    void unregisterForModemStateChanged(ISatelliteModemStateCallback callback);
 
    /**
      * Register to receive incoming datagrams over satellite.
      *
-     * @param subId The subId of the subscription to register for incoming satellite datagrams.
      * @param callback The callback to handle the incoming datagrams.
      *
      * @return The {@link SatelliteError} result of the operation.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForIncomingDatagram(int subId, ISatelliteDatagramCallback callback);
+    int registerForIncomingDatagram(ISatelliteDatagramCallback callback);
 
    /**
      * Unregister to stop receiving incoming datagrams over satellite.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for incoming satellite datagrams.
      * @param callback The callback that was passed to registerForIncomingDatagram.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForIncomingDatagram(int subId, ISatelliteDatagramCallback callback);
+    void unregisterForIncomingDatagram(ISatelliteDatagramCallback callback);
 
    /**
     * Poll pending satellite datagrams over satellite.
     *
-    * @param subId The subId of the subscription used for receiving datagrams.
     * @param callback The callback to get the result of the request.
     */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
                 + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void pollPendingDatagrams(int subId, IIntegerConsumer callback);
+    void pollPendingDatagrams(IIntegerConsumer callback);
 
    /**
     * Send datagram over satellite.
     *
-    * @param subId The subId of the subscription to send satellite datagrams for.
     * @param datagramType Type of datagram.
     * @param datagram Datagram to send over satellite.
     * @param needFullScreenPointingUI this is used to indicate pointingUI app to open in
@@ -2973,7 +2951,7 @@
     */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void sendDatagram(int subId, int datagramType, in SatelliteDatagram datagram,
+    void sendDatagram(int datagramType, in SatelliteDatagram datagram,
             in boolean needFullScreenPointingUI, IIntegerConsumer callback);
 
     /**
@@ -2991,13 +2969,12 @@
     /**
      * Request to get the time after which the satellite will be visible.
      *
-     * @param subId The subId to get the time after which the satellite will be visible for.
      * @param receiver Result receiver to get the error code of the request and the requested
      *                 time after which the satellite will be visible.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver);
+    void requestTimeForNextSatelliteVisibility(in ResultReceiver receiver);
 
     /**
      * Inform whether the device is aligned with the satellite within in margin for demo mode.
@@ -3007,7 +2984,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void setDeviceAlignedWithSatellite(int subId, boolean isAligned);
+    void setDeviceAlignedWithSatellite(boolean isAligned);
 
     /**
      * This API can be used by only CTS to update satellite vendor service package name.
@@ -3163,20 +3140,18 @@
     /**
      * Request to get the signal strength of the satellite connection.
      *
-     * @param subId The subId of the subscription to request for.
      * @param receiver Result receiver to get the error code of the request and the current signal
      * strength of the satellite connection.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestNtnSignalStrength(int subId, in ResultReceiver receiver);
+    void requestNtnSignalStrength(in ResultReceiver receiver);
 
     /**
      * Registers for NTN signal strength changed from satellite modem. If the registration operation
      * is not successful, a {@link SatelliteException} that contains {@link SatelliteResult} will be
      * thrown.
      *
-     * @param subId The subId of the subscription to request for.
      * @param callback The callback to handle the NTN signal strength changed event. If the
      * operation is successful, {@link NtnSignalStrengthCallback#onNtnSignalStrengthChanged(
      * NtnSignalStrength)} will return an instance of {@link NtnSignalStrength} with a value of
@@ -3185,30 +3160,27 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void registerForNtnSignalStrengthChanged(int subId,
-            in INtnSignalStrengthCallback callback);
+    void registerForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback);
 
     /**
      * Unregisters for NTN signal strength changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for provision state changed.
      * @param callback The callback that was passed to
      * {@link #registerForNtnSignalStrengthChanged(Executor, NtnSignalStrengthCallback)}.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForNtnSignalStrengthChanged(int subId, in INtnSignalStrengthCallback callback);
+    void unregisterForNtnSignalStrengthChanged(in INtnSignalStrengthCallback callback);
 
     /**
      * Registers for satellite capabilities change event from the satellite service.
      *
-     * @param executor The executor on which the callback will be called.
      * @param callback The callback to handle the satellite capabilities changed event.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForCapabilitiesChanged(int subId, in ISatelliteCapabilitiesCallback callback);
+    int registerForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback);
 
     /**
      * Unregisters for satellite capabilities change event from the satellite service.
@@ -3219,8 +3191,7 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForCapabilitiesChanged(int subId,
-            in ISatelliteCapabilitiesCallback callback);
+    void unregisterForCapabilitiesChanged(in ISatelliteCapabilitiesCallback callback);
 
     /**
      * This API can be used by only CTS to override the cached value for the device overlay config
@@ -3329,26 +3300,24 @@
     /**
      * Registers for supported state changed from satellite modem.
      *
-     * @param subId The subId of the subscription to register for supported state changed.
      * @param callback The callback to handle the satellite supported state changed event.
      *
      * @return The {@link SatelliteError} result of the operation.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForSatelliteSupportedStateChanged(int subId,
+    int registerForSatelliteSupportedStateChanged(
             in ISatelliteSupportedStateCallback callback);
 
     /**
      * Unregisters for supported state changed from satellite modem.
      * If callback was not registered before, the request will be ignored.
      *
-     * @param subId The subId of the subscription to unregister for supported state changed.
      * @param callback The callback that was passed to registerForSatelliteSupportedStateChanged.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForSatelliteSupportedStateChanged(int subId,
+    void unregisterForSatelliteSupportedStateChanged(
             in ISatelliteSupportedStateCallback callback);
 
     /**
@@ -3413,28 +3382,16 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestProvisionSubscriberIds(in ResultReceiver result);
+    void requestSatelliteSubscriberProvisionStatus(in ResultReceiver result);
 
     /**
-     * Request to get provisioned status for given a satellite subscriber id.
+     * Deliver the list of provisioned satellite subscriber infos.
      *
-     * @param satelliteSubscriberId Satellite subscriber id requiring provisioned status check.
-     * @param result The result receiver, which returns the provisioned status of the token if the
-     * request is successful or an error code if the request failed.
-     * @hide
-     */
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
-            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void requestIsProvisioned(in String satelliteSubscriberId, in ResultReceiver result);
-
-    /**
-     * Deliver the list of provisioned satellite subscriber ids.
-     *
-     * @param list List of provisioned satellite subscriber ids.
+     * @param list The list of provisioned satellite subscriber infos.
      * @param result The result receiver that returns whether deliver success or fail.
      * @hide
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void provisionSatellite(in List<ProvisionSubscriberId> list, in ResultReceiver result);
+    void provisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result);
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 29286e8..b92ba66 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -169,13 +169,13 @@
 
     /**
      * Set to false to disable SMS receiving, default is
-     * the value of config_sms_capable
+     * the value of TelephonyManager.isDeviceSmsCapable
      */
     static final String PROPERTY_SMS_RECEIVE = "telephony.sms.receive";
 
     /**
      * Set to false to disable SMS sending, default is
-     * the value of config_sms_capable
+     * the value of TelephonyManager.isDeviceSmsCapable
      */
     static final String PROPERTY_SMS_SEND = "telephony.sms.send";
 
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index f518d53..9676bd7 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -967,7 +967,7 @@
         byte[] serializedFplmns = new byte[dataLength];
         int offset = 0;
         for (String fplmn : fplmns) {
-            if (offset >= dataLength) break;
+            if (offset + FPLMN_BYTE_SIZE > dataLength) break;
             stringToBcdPlmn(fplmn, serializedFplmns, offset);
             offset += FPLMN_BYTE_SIZE;
         }
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 4143f59..30cc002 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -171,11 +171,11 @@
                 .setConsumedPower(123)
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CPU, 10100)
-                .setConsumedPowerForCustomComponent(
+                .setConsumedPower(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
                 .setUsageDurationMillis(
                         BatteryConsumer.POWER_COMPONENT_CPU, 10300)
-                .setUsageDurationForCustomComponentMillis(
+                .setUsageDurationMillis(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
 
         for (int i = 0; i < 1000; i++) {
@@ -191,10 +191,9 @@
                 consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
             }
 
-            consumerBuilder.setConsumedPowerForCustomComponent(
-                    BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
-                    .setUsageDurationForCustomComponentMillis(
-                            BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+            consumerBuilder
+                    .setConsumedPower(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+                    .setUsageDurationMillis(BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
         }
         return builder.build();
     }
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
index e09fbf6..c681ce9 100644
--- a/tests/FlickerTests/ActivityEmbedding/Android.bp
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -24,6 +24,9 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
 filegroup {
     name: "FlickerTestsOtherCommon-src",
     srcs: ["src/**/ActivityEmbeddingTestBase.kt"],
@@ -82,3 +85,123 @@
         ":FlickerTestsOtherCommon-src",
     ],
 }
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+android_test {
+    name: "FlickerTestsActivityEmbedding",
+    defaults: ["FlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    package_name: "com.android.server.wm.flicker",
+    instrumentation_target_package: "com.android.server.wm.flicker",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*"],
+    static_libs: [
+        "FlickerTestsBase",
+        "FlickerTestsOtherCommon",
+    ],
+    data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsActivityEmbedding module
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-CatchAll",
+    base: "FlickerTestsActivityEmbedding",
+    exclude_filters: [
+        "com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest",
+        "com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest",
+        "com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest",
+        "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest",
+        "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest",
+        "com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest",
+        "com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest",
+        "com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest",
+        "com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest",
+        "com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest",
+        "com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Close-CloseSecondaryActivityInSplitTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-LayoutChange-HorizontalSplitChangeRatioTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Open-MainActivityStartsSecondaryWithAlwaysExpandTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingPlaceholderSplitTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingSecondaryToSplitTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Open-OpenThirdActivityOverSplitTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Open-OpenTrampolineActivityTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Pip-SecondaryActivityEnterPipTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Rotation-RotateSplitNoChangeTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-Rtl-RTLStartSecondaryWithPlaceholderTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsActivityEmbedding-SplitScreen-EnterSystemSplitTest",
+    base: "FlickerTestsActivityEmbedding",
+    include_filters: ["com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsActivityEmbedding module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
index ee2c05e..06326f8 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -36,7 +36,7 @@
         teardown { testApp.exit(wmHelper) }
         transitions {
             this.setRotation(flicker.scenario.endRotation)
-            if (!flicker.scenario.isTablet) {
+            if (!usesTaskbar) {
                 wmHelper.StateSyncBuilder()
                     .add(navBarInPosition(flicker.scenario.isGesturalNavigation))
                     .waitForAndVerify()
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 379b45c..c3e1a1f 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,6 +24,7 @@
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.traces.parsers.toFlickerComponent
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -177,6 +178,13 @@
     @Ignore("Not applicable to this CUJ.")
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
 
+    @FlakyTest(bugId = 342596801)
+    override fun entireScreenCovered() = super.entireScreenCovered()
+
+    @FlakyTest(bugId = 342596801)
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         /** {@inheritDoc} */
         private var startDisplayBounds = Rect()
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
index d14a178..8b45740 100644
--- a/tests/FlickerTests/AppClose/Android.bp
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -33,3 +33,34 @@
     static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsAppClose module
+
+test_module_config {
+    name: "FlickerTestsAppClose-CatchAll",
+    base: "FlickerTestsAppClose",
+    exclude_filters: [
+        "com.android.server.wm.flicker.close.CloseAppBackButtonTest",
+        "com.android.server.wm.flicker.close.CloseAppHomeButtonTest",
+        "com.android.server.wm.flicker.close.",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppClose-CloseAppBackButtonTest",
+    base: "FlickerTestsAppClose",
+    include_filters: ["com.android.server.wm.flicker.close.CloseAppBackButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppClose-CloseAppHomeButtonTest",
+    base: "FlickerTestsAppClose",
+    include_filters: ["com.android.server.wm.flicker.close.CloseAppHomeButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsAppClose module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp
index 72a9065..b61739f 100644
--- a/tests/FlickerTests/AppLaunch/Android.bp
+++ b/tests/FlickerTests/AppLaunch/Android.bp
@@ -15,6 +15,7 @@
 //
 
 package {
+    default_team: "trendy_team_windowing_animations_transitions",
     // 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"
@@ -23,6 +24,9 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
 filegroup {
     name: "FlickerTestsAppLaunchCommon-src",
     srcs: ["src/**/common/*"],
@@ -69,3 +73,122 @@
     ],
     data: ["trace_config/*"],
 }
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+android_test {
+    name: "FlickerTestsAppLaunch",
+    defaults: ["FlickerTestsDefault"],
+    manifest: "AndroidManifest.xml",
+    test_config_template: "AndroidTestTemplate.xml",
+    srcs: ["src/**/*"],
+    static_libs: [
+        "FlickerTestsBase",
+        "FlickerTestsAppLaunchCommon",
+    ],
+    data: ["trace_config/*"],
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsAppLaunch module
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-CatchAll",
+    base: "FlickerTestsAppLaunch",
+    exclude_filters: [
+        "com.android.server.wm.flicker.launch.TaskTransitionTest",
+        "com.android.server.wm.flicker.launch.ActivityTransitionTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromIconColdTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest",
+        "com.android.server.wm.flicker.launch.OpenAppFromOverviewTest",
+        "com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest",
+        "com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition",
+        "com.android.server.wm.flicker.launch.OverrideTaskTransitionTest",
+        "com.android.server.wm.flicker.launch.TaskTransitionTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-ActivityTransitionTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.ActivityTransitionTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromIconColdTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIconColdTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromIntentColdAfterCameraTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromIntentColdTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromIntentWarmTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromLockscreenViaIntentTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenAppFromOverviewTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromOverviewTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenCameraFromHomeOnDoubleClickPowerButtonTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OpenTransferSplashscreenAppFromLauncherTransition",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-OverrideTaskTransitionTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.OverrideTaskTransitionTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppLaunch-TaskTransitionTest",
+    base: "FlickerTestsAppLaunch",
+    include_filters: ["com.android.server.wm.flicker.launch.TaskTransitionTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsAppLaunch module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 44ae27c..6e6a327 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -75,7 +75,7 @@
     @FlakyTest(bugId = 288341660)
     @Test
     fun navBarLayerVisibilityChanges() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.assertLayers {
             this.isInvisible(ComponentNameMatcher.NAV_BAR)
                 .then()
@@ -97,7 +97,7 @@
     @FlakyTest(bugId = 293581770)
     @Test
     fun navBarWindowsVisibilityChanges() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.assertWm {
             this.isNonAppWindowInvisible(ComponentNameMatcher.NAV_BAR)
                 .then()
@@ -112,7 +112,7 @@
     @Presubmit
     @Test
     fun taskBarLayerIsVisibleAtEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
     }
 
@@ -170,7 +170,7 @@
     @Presubmit
     @Test
     fun navBarLayerIsVisibleAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
     }
 
@@ -184,7 +184,7 @@
     @Presubmit
     @Test
     override fun appLayerBecomesVisible() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         super.appLayerBecomesVisible()
     }
 
@@ -192,11 +192,11 @@
     @FlakyTest(bugId = 227143265)
     @Test
     fun appLayerBecomesVisibleTablet() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         super.appLayerBecomesVisible()
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 338296297)
     @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
index 8a3304b..b497e30 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
@@ -28,6 +28,7 @@
         get() = {
             super.transition(this)
             setup {
+                // By default, launcher doesn't rotate on phones, but rotates on tablets
                 if (flicker.scenario.isTablet) {
                     tapl.setExpectedRotation(flicker.scenario.startRotation.value)
                 } else {
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
index f8fd3586..a6e31d4 100644
--- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
@@ -103,7 +103,7 @@
     @Presubmit
     @Test
     open fun navBarLayerPositionAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerPositionAtEnd()
     }
 
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
index 8040610..cfc818b 100644
--- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
@@ -31,8 +31,7 @@
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
 class CloseAppBackButton3ButtonLandscape :
     CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_90) {
-    // TODO: Missing CUJ (b/300078127)
-    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
     @Test
     override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
 
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
index aacccf4..6bf32a8 100644
--- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
@@ -31,8 +31,7 @@
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
 class CloseAppBackButton3ButtonPortrait :
     CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_0) {
-    // TODO: Missing CUJ (b/300078127)
-    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
     @Test
     override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
 
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
index 74ee460..4b6ab77 100644
--- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
@@ -31,8 +31,7 @@
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
 class CloseAppBackButtonGesturalNavLandscape :
     CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_90) {
-    // TODO: Missing CUJ (b/300078127)
-    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
     @Test
     override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
 
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
index 57463c3..7cc9db0 100644
--- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
@@ -31,8 +31,7 @@
 @RunWith(FlickerServiceJUnit4ClassRunner::class)
 class CloseAppBackButtonGesturalNavPortrait :
     CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) {
-    // TODO: Missing CUJ (b/300078127)
-    @ExpectedScenarios(["ENTIRE_TRACE"])
+    @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
     @Test
     override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
 
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index 78d93e1..f80e6b4 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -24,6 +24,9 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
 filegroup {
     name: "FlickerTestsImeCommon-src",
     srcs: ["src/**/common/*"],
@@ -39,6 +42,9 @@
     srcs: ["src/**/ShowImeOnAppStart*"],
 }
 
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
 android_test {
     name: "FlickerTestsIme",
     defaults: ["FlickerTestsDefault"],
@@ -53,6 +59,9 @@
     data: ["trace_config/*"],
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// Begin to cleanup after CL merges
+
 java_library {
     name: "FlickerTestsImeCommon",
     defaults: ["FlickerTestsDefault"],
@@ -107,3 +116,140 @@
     ],
     data: ["trace_config/*"],
 }
+
+// End to cleanup after CL merges
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsIme module
+
+test_module_config {
+    name: "FlickerTestsIme-CatchAll",
+    base: "FlickerTestsIme",
+    exclude_filters: [
+        "com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest",
+        "com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest",
+        "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest",
+        "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest",
+        "com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest",
+        "com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest",
+        "com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest",
+        "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest",
+        "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest",
+        "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest",
+        "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest",
+        "com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest",
+        "com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest",
+        "com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest",
+        "com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeOnDismissPopupDialogTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeOnGoHomeTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeShownOnAppStartOnGoHomeTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeShownOnAppStartToAppOnPressBackTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeToAppOnPressBackTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-CloseImeToHomeOnFinishActivityTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-OpenImeWindowToFixedPortraitAppTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromOverviewTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeOnUnlockScreenTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeWhenFocusingOnInputFieldTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeWhileDismissingThemedPopupDialogTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsIme-ShowImeWhileEnteringOverviewTest",
+    base: "FlickerTestsIme",
+    include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsIme module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index dc2bd1b..522c68b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -72,7 +72,7 @@
     @Presubmit
     @Test
     override fun navBarLayerPositionAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
         flicker.navBarLayerPositionAtStartAndEnd()
     }
@@ -80,7 +80,7 @@
     @Presubmit
     @Test
     fun navBarLayerPositionAtStartAndEndLandscapeOrSeascapeAtStart() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
         flicker.navBarLayerPositionAtStartAndEnd()
     }
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index c96c760e..eb63e49 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -28,6 +28,7 @@
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
 import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Ignore
@@ -93,7 +94,7 @@
     @Presubmit
     @Test
     fun navBarLayerIsVisibleAtStartAndEnd3Button() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
         flicker.navBarLayerIsVisibleAtStartAndEnd()
     }
@@ -105,7 +106,7 @@
     @Presubmit
     @Test
     fun navBarLayerIsInvisibleInLandscapeGestural() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
@@ -114,7 +115,7 @@
 
     /**
      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
-     * this is fixed and the nav bar shows as invisible
+     * this is fixed and the status bar shows as invisible
      */
     @Presubmit
     @Test
@@ -128,7 +129,7 @@
 
     /**
      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
-     * this is fixed and the nav bar shows as invisible
+     * this is fixed and the status bar shows as invisible
      */
     @Presubmit
     @Test
@@ -149,6 +150,10 @@
     @Ignore("Visibility changes depending on orientation and navigation mode")
     override fun navBarLayerPositionAtStartAndEnd() {}
 
+    @Test
+    @Ignore("Visibility changes depending on orientation and navigation mode")
+    override fun taskBarLayerIsVisibleAtStartAndEnd() {}
+
     /** {@inheritDoc} */
     @Test
     @Ignore("Visibility changes depending on orientation and navigation mode")
@@ -161,7 +166,10 @@
 
     @Presubmit
     @Test
-    override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+    fun taskBarLayerIsVisibleAtStartAndEndForTablets() {
+        Assume.assumeTrue(flicker.scenario.isTablet)
+        flicker.taskBarLayerIsVisibleAtStartAndEnd()
+    }
 
     @Presubmit
     @Test
@@ -174,7 +182,7 @@
     @Test
     fun statusBarLayerIsInvisibleInLandscape() {
         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
         flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
     }
diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp
index 4648383..06daaaf 100644
--- a/tests/FlickerTests/Notification/Android.bp
+++ b/tests/FlickerTests/Notification/Android.bp
@@ -32,3 +32,57 @@
     static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsNotification module
+
+test_module_config {
+    name: "FlickerTestsNotification-CatchAll",
+    base: "FlickerTestsNotification",
+    exclude_filters: [
+        "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest",
+        "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest",
+        "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest",
+        "com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest",
+        "com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationColdTest",
+    base: "FlickerTestsNotification",
+    include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWarmTest",
+    base: "FlickerTestsNotification",
+    include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWithOverlayAppTest",
+    base: "FlickerTestsNotification",
+    include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsNotification-OpenAppFromNotificationColdTest",
+    base: "FlickerTestsNotification",
+    include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsNotification-OpenAppFromNotificationWarmTest",
+    base: "FlickerTestsNotification",
+    include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsNotification module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 07fc230..ad70757 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -151,7 +151,7 @@
     @Presubmit
     @Test
     open fun taskBarWindowIsVisibleAtEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarWindowIsVisibleAtEnd()
     }
 
@@ -163,7 +163,7 @@
     @Presubmit
     @Test
     open fun taskBarLayerIsVisibleAtEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarLayerIsVisibleAtEnd()
     }
 
@@ -171,7 +171,7 @@
     @Presubmit
     @Test
     open fun navBarLayerPositionAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerPositionAtEnd()
     }
 
@@ -179,14 +179,14 @@
     @Presubmit
     @Test
     open fun navBarLayerIsVisibleAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerIsVisibleAtEnd()
     }
 
     @Presubmit
     @Test
     open fun navBarWindowIsVisibleAtEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarWindowIsVisibleAtEnd()
     }
 
diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp
index 8755d0e..4d5dba3 100644
--- a/tests/FlickerTests/QuickSwitch/Android.bp
+++ b/tests/FlickerTests/QuickSwitch/Android.bp
@@ -32,3 +32,41 @@
     static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsQuickswitch module
+
+test_module_config {
+    name: "FlickerTestsQuickswitch-CatchAll",
+    base: "FlickerTestsQuickswitch",
+    exclude_filters: [
+        "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest",
+        "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest",
+        "com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsBackTest",
+    base: "FlickerTestsQuickswitch",
+    include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsForwardTest",
+    base: "FlickerTestsQuickswitch",
+    include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsQuickswitch-QuickSwitchFromLauncherTest",
+    base: "FlickerTestsQuickswitch",
+    include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsQuickswitch module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
index aceb8ba..0884ef9 100644
--- a/tests/FlickerTests/Rotation/Android.bp
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -37,3 +37,41 @@
     static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// Begin breakdowns for FlickerTestsRotation module
+
+test_module_config {
+    name: "FlickerTestsAppRotation-CatchAll",
+    base: "FlickerTestsRotation",
+    exclude_filters: [
+        "com.android.server.wm.flicker.rotation.ChangeAppRotationTest",
+        "com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest",
+        "com.android.server.wm.flicker.rotation.SeamlessAppRotationTest",
+    ],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppRotation-ChangeAppRotationTest",
+    base: "FlickerTestsRotation",
+    include_filters: ["com.android.server.wm.flicker.rotation.ChangeAppRotationTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppRotation-OpenShowWhenLockedSeamlessAppRotationTest",
+    base: "FlickerTestsRotation",
+    include_filters: ["com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "FlickerTestsAppRotation-SeamlessAppRotationTest",
+    base: "FlickerTestsRotation",
+    include_filters: ["com.android.server.wm.flicker.rotation.SeamlessAppRotationTest"],
+    test_suites: ["device-tests"],
+}
+
+// End breakdowns for FlickerTestsRotation module
+////////////////////////////////////////////////////////////////////////////////
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 060015b..851ce02 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -26,6 +26,7 @@
 import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.wm.shell.Flags
 import org.junit.Assume
 import org.junit.AssumptionViolatedException
 import org.junit.Test
@@ -48,6 +49,9 @@
 
     private val logTag = this::class.java.simpleName
 
+    protected val usesTaskbar: Boolean
+        get() = flicker.scenario.isTablet || Flags.enableTaskbarOnPhones()
+
     /** Specification of the test transition to execute */
     abstract val transition: FlickerBuilder.() -> Unit
 
@@ -87,7 +91,7 @@
     @Presubmit
     @Test
     open fun navBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerIsVisibleAtStartAndEnd()
     }
 
@@ -100,7 +104,7 @@
     @Presubmit
     @Test
     open fun navBarLayerPositionAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarLayerPositionAtStartAndEnd()
     }
 
@@ -112,7 +116,7 @@
     @Presubmit
     @Test
     open fun navBarWindowIsAlwaysVisible() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
         flicker.navBarWindowIsAlwaysVisible()
     }
@@ -126,32 +130,28 @@
     @Presubmit
     @Test
     open fun navBarWindowIsVisibleAtStartAndEnd() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(usesTaskbar)
         flicker.navBarWindowIsVisibleAtStartAndEnd()
     }
 
     /**
      * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the
      * transition
-     *
-     * Note: Large screen only
      */
     @Presubmit
     @Test
     open fun taskBarLayerIsVisibleAtStartAndEnd() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarLayerIsVisibleAtStartAndEnd()
     }
 
     /**
      * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
-     *
-     * Note: Large screen only
      */
     @Presubmit
     @Test
     open fun taskBarWindowIsAlwaysVisible() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(usesTaskbar)
         flicker.taskBarWindowIsAlwaysVisible()
     }
 
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 8811e00..3f6a0bf 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
@@ -16,12 +16,17 @@
 
 package com.android.server.wm.flicker.helpers
 
+import android.content.Context
+import android.graphics.Insets
 import android.graphics.Rect
+import android.graphics.Region
 import android.platform.uiautomator_helpers.DeviceHelpers
 import android.tools.device.apphelpers.IStandardAppHelper
 import android.tools.helpers.SYSTEMUI_PACKAGE
 import android.tools.traces.parsers.WindowManagerStateHelper
 import android.tools.traces.wm.WindowingMode
+import android.view.WindowInsets
+import android.view.WindowManager
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.BySelector
 import androidx.test.uiautomator.UiDevice
@@ -43,6 +48,13 @@
         RIGHT_BOTTOM
     }
 
+    enum class Edges {
+        LEFT,
+        RIGHT,
+        TOP,
+        BOTTOM
+    }
+
     /** Wait for an app moved to desktop to finish its transition. */
     private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) {
         wmHelper
@@ -70,9 +82,7 @@
         // Start dragging a little under the top to prevent dragging the notification shade.
         val startY = 10
 
-        val displayRect =
-            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
-                ?: throw IllegalStateException("Default display is null")
+        val displayRect = getDisplayRect(wmHelper)
 
         // The position we want to drag to
         val endY = displayRect.centerY() / 2
@@ -81,18 +91,62 @@
         device.drag(startX, startY, startX, endY, 100)
     }
 
+    private fun getMaximizeButtonForTheApp(caption: UiObject2?): UiObject2 {
+        return caption
+            ?.children
+            ?.find { it.resourceName.endsWith(MAXIMIZE_BUTTON_VIEW) }
+            ?.children
+            ?.get(0)
+            ?: error("Unable to find resource $MAXIMIZE_BUTTON_VIEW\n")
+    }
+
     /** Click maximise button on the app header for the given app. */
     fun maximiseDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) {
         val caption = getCaptionForTheApp(wmHelper, device)
-        val maximizeButton =
-            caption
-                ?.children
-                ?.find { it.resourceName.endsWith(MAXIMIZE_BUTTON_VIEW) }
-                ?.children
-                ?.get(0)
-        maximizeButton?.click()
+        val maximizeButton = getMaximizeButtonForTheApp(caption)
+        maximizeButton.click()
         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
     }
+
+    /** Open maximize menu and click snap resize button on the app header for the given app. */
+    fun snapResizeDesktopApp(
+        wmHelper: WindowManagerStateHelper,
+        device: UiDevice,
+        context: Context,
+        toLeft: Boolean
+    ) {
+        val caption = getCaptionForTheApp(wmHelper, device)
+        val maximizeButton = getMaximizeButtonForTheApp(caption)
+        maximizeButton?.longClick()
+        wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+        val buttonResId = if (toLeft) SNAP_LEFT_BUTTON else SNAP_RIGHT_BUTTON
+        val maximizeMenu = getDesktopAppViewByRes(MAXIMIZE_MENU)
+
+        val snapResizeButton =
+            maximizeMenu
+                ?.wait(Until.findObject(By.res(SYSTEMUI_PACKAGE, buttonResId)), TIMEOUT.toMillis())
+                ?: error("Unable to find object with resource id $buttonResId")
+        snapResizeButton.click()
+
+        val displayRect = getDisplayRect(wmHelper)
+        val insets = getWindowInsets(
+            context, WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars()
+        )
+        displayRect.inset(insets)
+
+        val expectedWidth = displayRect.width() / 2
+        val expectedRect = Rect(displayRect).apply {
+            if (toLeft) right -= expectedWidth else left += expectedWidth
+        }
+
+        wmHelper
+            .StateSyncBuilder()
+            .withAppTransitionIdle()
+            .withSurfaceVisibleRegion(this, Region(expectedRect))
+            .waitForAndVerify()
+    }
+
     /** Click close button on the app header for the given app. */
     fun closeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) {
         val caption = getCaptionForTheApp(wmHelper, device)
@@ -112,8 +166,7 @@
         if (
             wmHelper.getWindow(innerHelper)?.windowingMode !=
             WindowingMode.WINDOWING_MODE_FREEFORM.value
-        )
-            error("expected a freeform window with caption but window is not in freeform mode")
+        ) error("expected a freeform window with caption but window is not in freeform mode")
         val captions =
             device.wait(Until.findObjects(caption), TIMEOUT.toMillis())
                 ?: error("Unable to find view $caption\n")
@@ -142,6 +195,40 @@
         dragWindow(startX, startY, endX, endY, wmHelper, device)
     }
 
+    /** Resize a desktop app from its edges. */
+    fun edgeResize(
+        wmHelper: WindowManagerStateHelper,
+        motionEvent: MotionEventHelper,
+        edge: Edges
+    ) {
+        val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
+        val (startX, startY) = getStartCoordinatesForEdgeResize(windowRect, edge)
+        val verticalChange = when (edge) {
+            Edges.LEFT -> 0
+            Edges.RIGHT -> 0
+            Edges.TOP -> -100
+            Edges.BOTTOM -> 100
+        }
+        val horizontalChange = when (edge) {
+            Edges.LEFT -> -100
+            Edges.RIGHT -> 100
+            Edges.TOP -> 0
+            Edges.BOTTOM -> 0
+        }
+
+        // The position we want to drag to
+        val endY = startY + verticalChange
+        val endX = startX + horizontalChange
+
+        motionEvent.actionDown(startX, startY)
+        motionEvent.actionMove(startX, startY, endX, endY, /* steps= */100)
+        motionEvent.actionUp(endX, endY)
+        wmHelper
+            .StateSyncBuilder()
+            .withAppTransitionIdle()
+            .waitForAndVerify()
+    }
+
     /** Drag a window from a source coordinate to a destination coordinate. */
     fun dragWindow(
         startX: Int, startY: Int,
@@ -156,6 +243,30 @@
             .waitForAndVerify()
     }
 
+    /** Drag a window to a snap resize region, found at the left and right edges of the screen. */
+    fun dragToSnapResizeRegion(
+        wmHelper: WindowManagerStateHelper,
+        device: UiDevice,
+        isLeft: Boolean,
+    ) {
+        val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
+        // Set start x-coordinate as center of app header.
+        val startX = windowRect.centerX()
+        val startY = windowRect.top
+
+        val displayRect = getDisplayRect(wmHelper)
+
+        val endX = if (isLeft) displayRect.left else displayRect.right
+        val endY = displayRect.centerY() / 2
+
+        // drag the window to snap resize
+        device.drag(startX, startY, endX, endY, /* steps= */ 100)
+        wmHelper
+            .StateSyncBuilder()
+            .withAppTransitionIdle()
+            .waitForAndVerify()
+    }
+
     private fun getStartCoordinatesForCornerResize(
         windowRect: Rect,
         corner: Corners
@@ -168,6 +279,18 @@
         }
     }
 
+    private fun getStartCoordinatesForEdgeResize(
+        windowRect: Rect,
+        edge: Edges
+    ): Pair<Int, Int> {
+        return when (edge) {
+            Edges.LEFT -> Pair(windowRect.left, windowRect.bottom / 2)
+            Edges.RIGHT -> Pair(windowRect.right, windowRect.bottom / 2)
+            Edges.TOP -> Pair(windowRect.right / 2, windowRect.top)
+            Edges.BOTTOM -> Pair(windowRect.right / 2, windowRect.bottom)
+        }
+    }
+
     /** Exit desktop mode by dragging the app handle to the top drag zone. */
     fun exitDesktopWithDragToTopDragZone(
         wmHelper: WindowManagerStateHelper,
@@ -179,9 +302,7 @@
 
     private fun dragAppWindowToTopDragZone(wmHelper: WindowManagerStateHelper, device: UiDevice) {
         val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
-        val displayRect =
-            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
-                ?: throw IllegalStateException("Default display is null")
+        val displayRect = getDisplayRect(wmHelper)
 
         val startX = windowRect.centerX()
         val endX = displayRect.centerX()
@@ -194,7 +315,8 @@
 
     fun enterDesktopModeFromAppHandleMenu(
         wmHelper: WindowManagerStateHelper,
-        device: UiDevice) {
+        device: UiDevice
+    ) {
         val windowRect = wmHelper.getWindowRegion(innerHelper).bounds
         val startX = windowRect.centerX()
         // Click a little under the top to prevent opening the notification shade.
@@ -204,7 +326,7 @@
         device.click(startX, startY)
         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
 
-        val pill = getAppHandlePillForWindow()
+        val pill = getDesktopAppViewByRes(PILL_CONTAINER)
         val desktopModeButton =
             pill
                 ?.children
@@ -214,10 +336,13 @@
         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
     }
 
-    private fun getAppHandlePillForWindow(): UiObject2? {
-        val pillContainer: BySelector = By.res(SYSTEMUI_PACKAGE, PILL_CONTAINER)
-        return DeviceHelpers.waitForObj(pillContainer, TIMEOUT)
-    }
+    private fun getDesktopAppViewByRes(viewResId: String): UiObject2? =
+        DeviceHelpers.waitForObj(By.res(SYSTEMUI_PACKAGE, viewResId), TIMEOUT)
+
+    private fun getDisplayRect(wmHelper: WindowManagerStateHelper): Rect =
+        wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+            ?: throw IllegalStateException("Default display is null")
+
 
     /** Wait for transition to full screen to finish. */
     private fun waitForTransitionToFullscreen(wmHelper: WindowManagerStateHelper) {
@@ -228,13 +353,23 @@
             .waitForAndVerify()
     }
 
+    private fun getWindowInsets(context: Context, typeMask: Int): Insets {
+        val wm: WindowManager = context.getSystemService(WindowManager::class.java)
+            ?: error("Unable to connect to WindowManager service")
+        val metricInsets = wm.currentWindowMetrics.windowInsets
+        return metricInsets.getInsetsIgnoringVisibility(typeMask)
+    }
+
     private companion object {
-        val TIMEOUT = Duration.ofSeconds(3)
-        val CAPTION = "desktop_mode_caption"
-        val MAXIMIZE_BUTTON_VIEW = "maximize_button_view"
-        val CLOSE_BUTTON = "close_window"
-        val PILL_CONTAINER = "windowing_pill"
-        val DESKTOP_MODE_BUTTON = "desktop_button"
+        val TIMEOUT: Duration = Duration.ofSeconds(3)
+        const val CAPTION: String = "desktop_mode_caption"
+        const val MAXIMIZE_BUTTON_VIEW: String = "maximize_button_view"
+        const val MAXIMIZE_MENU: String = "maximize_menu"
+        const val CLOSE_BUTTON: String = "close_window"
+        const val PILL_CONTAINER: String = "windowing_pill"
+        const val DESKTOP_MODE_BUTTON: String = "desktop_button"
+        const val SNAP_LEFT_BUTTON: String = "maximize_menu_snap_left_button"
+        const val SNAP_RIGHT_BUTTON: String = "maximize_menu_snap_right_button"
         val caption: BySelector
             get() = By.res(SYSTEMUI_PACKAGE, CAPTION)
     }
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
new file mode 100644
index 0000000..0835398
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MotionEventHelper.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.os.SystemClock
+import android.view.ContentInfo.Source
+import android.view.InputDevice.SOURCE_MOUSE
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.ACTION_UP
+import android.view.MotionEvent.TOOL_TYPE_FINGER
+import android.view.MotionEvent.TOOL_TYPE_MOUSE
+import android.view.MotionEvent.TOOL_TYPE_STYLUS
+import android.view.MotionEvent.ToolType
+
+/**
+ * Helper class for injecting a custom motion event and performing some actions. This is used for
+ * instrumenting input injections like stylus, mouse and touchpad.
+ */
+class MotionEventHelper(
+    private val instr: Instrumentation,
+    private val inputMethod: InputMethod
+) {
+    enum class InputMethod(@ToolType val toolType: Int, @Source val source: Int) {
+        STYLUS(TOOL_TYPE_STYLUS, SOURCE_STYLUS),
+        MOUSE(TOOL_TYPE_MOUSE, SOURCE_MOUSE),
+        TOUCHPAD(TOOL_TYPE_FINGER, SOURCE_MOUSE)
+    }
+
+    fun actionDown(x: Int, y: Int) {
+        injectMotionEvent(ACTION_DOWN, x, y)
+    }
+
+    fun actionUp(x: Int, y: Int) {
+        injectMotionEvent(ACTION_UP, x, y)
+    }
+
+    fun actionMove(startX: Int, startY: Int, endX: Int, endY: Int, steps: Int) {
+        val incrementX = (endX - startX).toFloat() / (steps - 1)
+        val incrementY = (endY - startY).toFloat() / (steps - 1)
+
+        for (i in 0..steps) {
+            val time = SystemClock.uptimeMillis()
+            val x = startX + incrementX * i
+            val y = startY + incrementY * i
+
+            val moveEvent = getMotionEvent(time, time, ACTION_MOVE, x, y)
+            injectMotionEvent(moveEvent)
+        }
+    }
+
+    private fun injectMotionEvent(action: Int, x: Int, y: Int): MotionEvent {
+        val eventTime = SystemClock.uptimeMillis()
+        val event = getMotionEvent(eventTime, eventTime, action, x.toFloat(), y.toFloat())
+        injectMotionEvent(event)
+        return event
+    }
+
+    private fun injectMotionEvent(event: MotionEvent) {
+        instr.uiAutomation.injectInputEvent(event, true, false)
+    }
+
+    private fun getMotionEvent(
+        downTime: Long,
+        eventTime: Long,
+        action: Int,
+        x: Float,
+        y: Float,
+    ): MotionEvent {
+        val properties = MotionEvent.PointerProperties.createArray(1)
+        properties[0].toolType = inputMethod.toolType
+        properties[0].id = 1
+
+        val coords = MotionEvent.PointerCoords.createArray(1)
+        coords[0].x = x
+        coords[0].y = y
+        coords[0].pressure = 1f
+
+        val event =
+            MotionEvent.obtain(
+                downTime,
+                eventTime,
+                action,
+                /* pointerCount= */ 1,
+                properties,
+                coords,
+                /* metaState= */ 0,
+                /* buttonState= */ 0,
+                /* xPrecision = */ 1f,
+                /* yPrecision = */ 1f,
+                /* deviceId = */ 0,
+                /* edgeFlags = */ 0,
+                inputMethod.source,
+                /* flags = */ 0
+            )
+        event.displayId = 0
+        return event
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 43fd57b..931e4f8 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -269,9 +269,23 @@
     /** Expand the PIP window back to full screen via intent and wait until the app is visible */
     fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
 
-    fun changeAspectRatio() {
+    fun changeAspectRatio(wmHelper: WindowManagerStateHelper) {
         val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
         context.sendBroadcast(intent)
+        // Wait on WMHelper on size change upon aspect ratio change
+        val windowRect = getWindowRect(wmHelper)
+        wmHelper
+            .StateSyncBuilder()
+            .add("pipAspectRatioChanged") {
+                val pipAppWindow =
+                    it.wmState.visibleWindows.firstOrNull { window ->
+                        this.windowMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+                val pipRegion = pipAppWindow.frameRegion
+                return@add pipRegion != Region(windowRect)
+            }
+            .waitForAndVerify()
     }
 
     fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 06c2651..65398a2 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -40,7 +40,7 @@
         "frameworks-base-testutils",
         "hamcrest-library",
         "kotlin-test",
-        "mockito-target-minus-junit4",
+        "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "platform-screenshot-diff-core",
         "services.core.unboosted",
diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png
index b2354f8..297244f 100644
--- a/tests/Input/assets/testPointerFillStyle.png
+++ b/tests/Input/assets/testPointerFillStyle.png
Binary files differ
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
new file mode 100644
index 0000000..14aac66
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt
@@ -0,0 +1,202 @@
+/*
+ * 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.hardware.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.os.Handler
+import android.os.HandlerExecutor
+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 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 kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.KeyGestureEventListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyGestureEventListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyGestureEventListenerTest {
+
+    companion object {
+        const val DEVICE_ID = 1
+        val HOME_GESTURE_EVENT = KeyGestureEvent(
+            DEVICE_ID,
+            intArrayOf(KeyEvent.KEYCODE_H),
+            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
+            KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+        )
+    }
+
+    @get:Rule
+    val rule = SetFlagsRule()
+
+    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)
+
+        // Handle key gesture event listener registration.
+        doAnswer {
+            val listener = it.getArgument(0) as IKeyGestureEventListener
+            if (registeredListener != null &&
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                // There can only be one registered key gesture event listener per process.
+                fail("Trying to register a new listener when one already exists")
+            }
+            registeredListener = listener
+            null
+        }.`when`(iInputManagerMock).registerKeyGestureEventListener(any())
+
+        // Handle key gesture event listener being unregistered.
+        doAnswer {
+            val listener = it.getArgument(0) as IKeyGestureEventListener
+            if (registeredListener == null ||
+                    registeredListener!!.asBinder() != listener.asBinder()) {
+                fail("Trying to unregister a listener that is not registered")
+            }
+            registeredListener = null
+            null
+        }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any())
+    }
+
+    @After
+    fun tearDown() {
+        if (this::inputManagerGlobalSession.isInitialized) {
+            inputManagerGlobalSession.close()
+        }
+    }
+
+    private fun notifyKeyGestureEvent(event: KeyGestureEvent) {
+        registeredListener!!.onKeyGestureEvent(
+            event.deviceId,
+            event.keycodes,
+            event.modifierState,
+            event.keyGestureType
+        )
+    }
+
+    @Test
+    fun testListenerHasCorrectGestureNotified() {
+        var callbackCount = 0
+
+        // Add a key gesture event listener
+        inputManager.registerKeyGestureEventListener(executor) {
+            event: KeyGestureEvent ->
+            assertEquals(HOME_GESTURE_EVENT, event)
+            callbackCount++
+        }
+
+        // Notifying key gesture event will notify the listener.
+        notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+        testLooper.dispatchNext()
+        assertEquals(1, callbackCount)
+    }
+
+    @Test
+    fun testAddingListenersRegistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.KeyGestureEventListener { _ -> }
+        val callback2 = InputManager.KeyGestureEventListener { _ -> }
+
+        assertNull(registeredListener)
+
+        // Adding the listener should register the callback with InputManagerService.
+        inputManager.registerKeyGestureEventListener(executor, callback1)
+        assertNotNull(registeredListener)
+
+        // Adding another listener should not register new internal listener.
+        val currListener = registeredListener
+        inputManager.registerKeyGestureEventListener(executor, callback2)
+        assertEquals(currListener, registeredListener)
+    }
+
+    @Test
+    fun testRemovingListenersUnregistersInternalCallbackListener() {
+        // Set up two callbacks.
+        val callback1 = InputManager.KeyGestureEventListener { _ -> }
+        val callback2 = InputManager.KeyGestureEventListener { _ -> }
+
+        inputManager.registerKeyGestureEventListener(executor, callback1)
+        inputManager.registerKeyGestureEventListener(executor, callback2)
+
+        // Only removing all listeners should remove the internal callback
+        inputManager.unregisterKeyGestureEventListener(callback1)
+        assertNotNull(registeredListener)
+        inputManager.unregisterKeyGestureEventListener(callback2)
+        assertNull(registeredListener)
+    }
+
+    @Test
+    fun testMultipleListeners() {
+        // Set up two callbacks.
+        var callbackCount1 = 0
+        var callbackCount2 = 0
+        val callback1 = InputManager.KeyGestureEventListener { _ -> callbackCount1++ }
+        val callback2 = InputManager.KeyGestureEventListener { _ -> callbackCount2++ }
+
+        // Add both key gesture event listeners
+        inputManager.registerKeyGestureEventListener(executor, callback1)
+        inputManager.registerKeyGestureEventListener(executor, callback2)
+
+        // Notifying key gesture event, should notify both the callbacks.
+        notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+        testLooper.dispatchAll()
+        assertEquals(1, callbackCount1)
+        assertEquals(1, callbackCount2)
+
+        inputManager.unregisterKeyGestureEventListener(callback2)
+        // Notifying key gesture event, should still trigger callback1 but not
+        // callback2.
+        notifyKeyGestureEvent(HOME_GESTURE_EVENT)
+        testLooper.dispatchAll()
+        assertEquals(2, callbackCount1)
+        assertEquals(1, callbackCount2)
+    }
+}
diff --git a/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt
deleted file mode 100644
index 24d7291..0000000
--- a/tests/Input/src/android/hardware/input/KeyboardSystemShortcutListenerTest.kt
+++ /dev/null
@@ -1,202 +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 android.hardware.input
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.os.Handler
-import android.os.HandlerExecutor
-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 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 kotlin.test.assertEquals
-import kotlin.test.assertNotNull
-import kotlin.test.assertNull
-import kotlin.test.fail
-
-/**
- * Tests for [InputManager.KeyboardSystemShortcutListener].
- *
- * Build/Install/Run:
- * atest InputTests:KeyboardSystemShortcutListenerTest
- */
-@Presubmit
-@RunWith(MockitoJUnitRunner::class)
-class KeyboardSystemShortcutListenerTest {
-
-    companion object {
-        const val DEVICE_ID = 1
-        val HOME_SHORTCUT = KeyboardSystemShortcut(
-            intArrayOf(KeyEvent.KEYCODE_H),
-            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
-            KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME
-        )
-    }
-
-    @get:Rule
-    val rule = SetFlagsRule()
-
-    private val testLooper = TestLooper()
-    private val executor = HandlerExecutor(Handler(testLooper.looper))
-    private var registeredListener: IKeyboardSystemShortcutListener? = 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)
-
-        // Handle keyboard system shortcut listener registration.
-        doAnswer {
-            val listener = it.getArgument(0) as IKeyboardSystemShortcutListener
-            if (registeredListener != null &&
-                    registeredListener!!.asBinder() != listener.asBinder()) {
-                // There can only be one registered keyboard system shortcut listener per process.
-                fail("Trying to register a new listener when one already exists")
-            }
-            registeredListener = listener
-            null
-        }.`when`(iInputManagerMock).registerKeyboardSystemShortcutListener(any())
-
-        // Handle keyboard system shortcut listener being unregistered.
-        doAnswer {
-            val listener = it.getArgument(0) as IKeyboardSystemShortcutListener
-            if (registeredListener == null ||
-                    registeredListener!!.asBinder() != listener.asBinder()) {
-                fail("Trying to unregister a listener that is not registered")
-            }
-            registeredListener = null
-            null
-        }.`when`(iInputManagerMock).unregisterKeyboardSystemShortcutListener(any())
-    }
-
-    @After
-    fun tearDown() {
-        if (this::inputManagerGlobalSession.isInitialized) {
-            inputManagerGlobalSession.close()
-        }
-    }
-
-    private fun notifyKeyboardSystemShortcutTriggered(id: Int, shortcut: KeyboardSystemShortcut) {
-        registeredListener!!.onKeyboardSystemShortcutTriggered(
-            id,
-            shortcut.keycodes,
-            shortcut.modifierState,
-            shortcut.systemShortcut
-        )
-    }
-
-    @Test
-    fun testListenerHasCorrectSystemShortcutNotified() {
-        var callbackCount = 0
-
-        // Add a keyboard system shortcut listener
-        inputManager.registerKeyboardSystemShortcutListener(executor) {
-            deviceId: Int, systemShortcut: KeyboardSystemShortcut ->
-            assertEquals(DEVICE_ID, deviceId)
-            assertEquals(HOME_SHORTCUT, systemShortcut)
-            callbackCount++
-        }
-
-        // Notifying keyboard system shortcut triggered will notify the listener.
-        notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
-        testLooper.dispatchNext()
-        assertEquals(1, callbackCount)
-    }
-
-    @Test
-    fun testAddingListenersRegistersInternalCallbackListener() {
-        // Set up two callbacks.
-        val callback1 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-        val callback2 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-
-        assertNull(registeredListener)
-
-        // Adding the listener should register the callback with InputManagerService.
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
-        assertNotNull(registeredListener)
-
-        // Adding another listener should not register new internal listener.
-        val currListener = registeredListener
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
-        assertEquals(currListener, registeredListener)
-    }
-
-    @Test
-    fun testRemovingListenersUnregistersInternalCallbackListener() {
-        // Set up two callbacks.
-        val callback1 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-        val callback2 = InputManager.KeyboardSystemShortcutListener {_, _ -> }
-
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
-
-        // Only removing all listeners should remove the internal callback
-        inputManager.unregisterKeyboardSystemShortcutListener(callback1)
-        assertNotNull(registeredListener)
-        inputManager.unregisterKeyboardSystemShortcutListener(callback2)
-        assertNull(registeredListener)
-    }
-
-    @Test
-    fun testMultipleListeners() {
-        // Set up two callbacks.
-        var callbackCount1 = 0
-        var callbackCount2 = 0
-        val callback1 = InputManager.KeyboardSystemShortcutListener { _, _ -> callbackCount1++ }
-        val callback2 = InputManager.KeyboardSystemShortcutListener { _, _ -> callbackCount2++ }
-
-        // Add both keyboard system shortcut listeners
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback1)
-        inputManager.registerKeyboardSystemShortcutListener(executor, callback2)
-
-        // Notifying keyboard system shortcut triggered, should notify both the callbacks.
-        notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
-        testLooper.dispatchAll()
-        assertEquals(1, callbackCount1)
-        assertEquals(1, callbackCount2)
-
-        inputManager.unregisterKeyboardSystemShortcutListener(callback2)
-        // Notifying keyboard system shortcut triggered, should still trigger callback1 but not
-        // callback2.
-        notifyKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
-        testLooper.dispatchAll()
-        assertEquals(2, callbackCount1)
-        assertEquals(1, callbackCount2)
-    }
-}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 3c72498..8829f74 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -151,6 +151,7 @@
         verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean())
         verify(native).setTouchpadTapToClickEnabled(anyBoolean())
         verify(native).setTouchpadTapDraggingEnabled(anyBoolean())
+        verify(native).setShouldNotifyTouchpadHardwareState(anyBoolean())
         verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
         verify(native).setShowTouches(anyBoolean())
         verify(native).setMotionClassifierEnabled(anyBoolean())
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
new file mode 100644
index 0000000..3f611e0
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.server.input
+
+import android.content.Context
+import android.content.ContextWrapper
+import android.hardware.input.IKeyGestureEventListener
+import android.hardware.input.KeyGestureEvent
+import android.platform.test.annotations.Presubmit
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mockito
+import org.mockito.junit.MockitoJUnit
+
+/**
+ * Tests for {@link KeyGestureController}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyGestureControllerTests
+ */
+@Presubmit
+class KeyGestureControllerTests {
+
+    companion object {
+        val DEVICE_ID = 1
+        val HOME_GESTURE_EVENT = KeyGestureEvent(
+            DEVICE_ID,
+            intArrayOf(KeyEvent.KEYCODE_H),
+            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
+            KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+        )
+    }
+
+    @get:Rule
+    val rule = MockitoJUnit.rule()!!
+
+    private lateinit var keyGestureController: KeyGestureController
+    private lateinit var context: Context
+    private var lastEvent: KeyGestureEvent? = null
+
+    @Before
+    fun setup() {
+        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+        keyGestureController = KeyGestureController()
+    }
+
+    @Test
+    fun testKeyGestureEvent_registerUnregisterListener() {
+        val listener = KeyGestureEventListener()
+
+        // Register key gesture event listener
+        keyGestureController.registerKeyGestureEventListener(listener, 0)
+        keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
+        assertEquals(
+            "Listener should get callback on key gesture event",
+            HOME_GESTURE_EVENT,
+            lastEvent!!
+        )
+
+        // Unregister listener
+        lastEvent = null
+        keyGestureController.unregisterKeyGestureEventListener(listener, 0)
+        keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT)
+        assertNull("Listener should not get callback after being unregistered", lastEvent)
+    }
+
+    inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
+        override fun onKeyGestureEvent(
+                deviceId: Int,
+                keycodes: IntArray,
+                modifierState: Int,
+                gestureType: Int
+        ) {
+            lastEvent = KeyGestureEvent(deviceId, keycodes, modifierState, gestureType)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt b/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt
deleted file mode 100644
index 5a40a1c..0000000
--- a/tests/Input/src/com/android/server/input/KeyboardShortcutCallbackHandlerTests.kt
+++ /dev/null
@@ -1,96 +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.server.input
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.hardware.input.IKeyboardSystemShortcutListener
-import android.hardware.input.KeyboardSystemShortcut
-import android.platform.test.annotations.Presubmit
-import android.view.KeyEvent
-import androidx.test.core.app.ApplicationProvider
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.mockito.Mockito
-import org.mockito.junit.MockitoJUnit
-
-/**
- * Tests for {@link KeyboardShortcutCallbackHandler}.
- *
- * Build/Install/Run:
- * atest InputTests:KeyboardShortcutCallbackHandlerTests
- */
-@Presubmit
-class KeyboardShortcutCallbackHandlerTests {
-
-    companion object {
-        val DEVICE_ID = 1
-        val HOME_SHORTCUT = KeyboardSystemShortcut(
-            intArrayOf(KeyEvent.KEYCODE_H),
-            KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON,
-            KeyboardSystemShortcut.SYSTEM_SHORTCUT_HOME
-        )
-    }
-
-    @get:Rule
-    val rule = MockitoJUnit.rule()!!
-
-    private lateinit var keyboardShortcutCallbackHandler: KeyboardShortcutCallbackHandler
-    private lateinit var context: Context
-    private var lastShortcut: KeyboardSystemShortcut? = null
-
-    @Before
-    fun setup() {
-        context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
-        keyboardShortcutCallbackHandler = KeyboardShortcutCallbackHandler()
-    }
-
-    @Test
-    fun testKeyboardSystemShortcutTriggered_registerUnregisterListener() {
-        val listener = KeyboardSystemShortcutListener()
-
-        // Register keyboard system shortcut listener
-        keyboardShortcutCallbackHandler.registerKeyboardSystemShortcutListener(listener, 0)
-        keyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
-        assertEquals(
-            "Listener should get callback on keyboard system shortcut triggered",
-            HOME_SHORTCUT,
-            lastShortcut!!
-        )
-
-        // Unregister listener
-        lastShortcut = null
-        keyboardShortcutCallbackHandler.unregisterKeyboardSystemShortcutListener(listener, 0)
-        keyboardShortcutCallbackHandler.onKeyboardSystemShortcutTriggered(DEVICE_ID, HOME_SHORTCUT)
-        assertNull("Listener should not get callback after being unregistered", lastShortcut)
-    }
-
-    inner class KeyboardSystemShortcutListener : IKeyboardSystemShortcutListener.Stub() {
-        override fun onKeyboardSystemShortcutTriggered(
-                deviceId: Int,
-                keycodes: IntArray,
-                modifierState: Int,
-                shortcut: Int
-        ) {
-            assertEquals(DEVICE_ID, deviceId)
-            lastShortcut = KeyboardSystemShortcut(keycodes, modifierState, shortcut)
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
new file mode 100644
index 0000000..c7ebd3a
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewControllerTests.java
@@ -0,0 +1,215 @@
+/*
+ * 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.server.input.debug;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.hardware.input.InputManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.InputDevice;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.input.InputManagerService;
+
+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;
+
+/**
+ * Build/Install/Run:
+ * atest TouchpadDebugViewControllerTests
+ */
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class TouchpadDebugViewControllerTests {
+    private static final int DEVICE_ID = 1000;
+    private static final String TAG = "TouchpadDebugViewController";
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    private Context mContext;
+    private TouchpadDebugViewController mTouchpadDebugViewController;
+    @Mock
+    private InputManager mInputManagerMock;
+    @Mock
+    private InputManagerService mInputManagerServiceMock;
+    @Mock
+    private WindowManager mWindowManagerMock;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setup() throws Exception {
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        TestableContext mTestableContext = new TestableContext(mContext);
+        mTestableContext.addMockSystemService(WindowManager.class, mWindowManagerMock);
+
+        Rect bounds = new Rect(0, 0, 2560, 1600);
+        WindowMetrics metrics = new WindowMetrics(bounds, new WindowInsets(bounds), 1.0f);
+
+        when(mWindowManagerMock.getCurrentWindowMetrics()).thenReturn(metrics);
+
+        unMockTouchpad();
+
+        mTestableLooper = TestableLooper.get(this);
+
+        mTestableContext.addMockSystemService(InputManager.class, mInputManagerMock);
+
+        mTouchpadDebugViewController = new TouchpadDebugViewController(mTestableContext,
+                mTestableLooper.getLooper(), mInputManagerServiceMock);
+    }
+
+    private InputDevice createTouchpadInputDevice(int id) {
+        return new InputDevice.Builder()
+                .setId(id)
+                .setSources(InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE)
+                .setName("Test Device " + id)
+                .build();
+    }
+
+    private void mockTouchpad() {
+        when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+        when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+                createTouchpadInputDevice(DEVICE_ID));
+    }
+
+    private void unMockTouchpad() {
+        when(mInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{});
+        when(mInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(null);
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingDisabled() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, never()).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+    }
+
+    @Test
+    public void settingEnabledWhileNoTouchpadConnected() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+        verify(mWindowManagerMock, never()).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingEnabled() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingEnabledThenDisabled() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, times(1)).removeView(any());
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingDisabledThenEnabled() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, never()).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingDisabledThenTouchpadDisconnected() throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, never()).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+
+        unMockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);
+
+        verify(mWindowManagerMock, never()).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+    }
+
+    @Test
+    public void touchpadConnectedWhileSettingEnabledThenTouchpadDisconnectedThenSettingDisabled()
+            throws Exception {
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(true);
+
+        mockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceAdded(DEVICE_ID);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, never()).removeView(any());
+
+        unMockTouchpad();
+        mTouchpadDebugViewController.onInputDeviceRemoved(DEVICE_ID);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, times(1)).removeView(any());
+
+        mTouchpadDebugViewController.updateTouchpadVisualizerEnabled(false);
+
+        verify(mWindowManagerMock, times(1)).addView(any(), any());
+        verify(mWindowManagerMock, times(1)).removeView(any());
+    }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
new file mode 100644
index 0000000..ad0ef1b
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/TouchpadDebugViewTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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.server.input.debug;
+
+import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+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.Rect;
+import android.testing.TestableContext;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.cts.input.MotionEventBuilder;
+import com.android.cts.input.PointerBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Build/Install/Run:
+ * atest TouchpadDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class TouchpadDebugViewTest {
+    private static final int TOUCHPAD_DEVICE_ID = 6;
+
+    private TouchpadDebugView mTouchpadDebugView;
+    private WindowManager.LayoutParams mWindowLayoutParams;
+
+    @Mock
+    WindowManager mWindowManager;
+
+    Rect mWindowBounds;
+    WindowMetrics mWindowMetrics;
+    TestableContext mTestableContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        mTestableContext = new TestableContext(context);
+
+        mTestableContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+        mWindowBounds = new Rect(0, 0, 2560, 1600);
+        mWindowMetrics = new WindowMetrics(mWindowBounds, new WindowInsets(mWindowBounds), 1.0f);
+
+        when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+
+        mTouchpadDebugView = new TouchpadDebugView(mTestableContext, TOUCHPAD_DEVICE_ID);
+
+        mTouchpadDebugView.measure(
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+        );
+
+        doAnswer(invocation -> {
+            mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+                    mTouchpadDebugView.getMeasuredHeight());
+            return null;
+        }).when(mWindowManager).addView(any(), any());
+
+        doAnswer(invocation -> {
+            mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+                    mTouchpadDebugView.getMeasuredHeight());
+            return null;
+        }).when(mWindowManager).updateViewLayout(any(), any());
+
+        mWindowLayoutParams = mTouchpadDebugView.getWindowLayoutParams();
+        mWindowLayoutParams.x = 20;
+        mWindowLayoutParams.y = 20;
+
+        mTouchpadDebugView.layout(0, 0, mTouchpadDebugView.getMeasuredWidth(),
+                mTouchpadDebugView.getMeasuredHeight());
+    }
+
+    @Test
+    public void testDragView() {
+        // Initial view position relative to screen.
+        int initialX = mWindowLayoutParams.x;
+        int initialY = mWindowLayoutParams.y;
+
+        float offsetX = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+        float offsetY = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() + 10;
+
+        // Simulate ACTION_DOWN event (initial touch).
+        MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f)
+                        .y(40f)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+        verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+
+        // Simulate ACTION_MOVE event (dragging to the right).
+        MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f + offsetX)
+                        .y(40f + offsetY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+        ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+        // Verify position after ACTION_MOVE
+        assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+
+        // Simulate ACTION_UP event (release touch).
+        MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(40f + offsetX)
+                        .y(40f + offsetY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+        assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+    }
+
+    @Test
+    public void testDragViewOutOfBounds() {
+        int initialX = mWindowLayoutParams.x;
+        int initialY = mWindowLayoutParams.y;
+
+        MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX + 10f)
+                        .y(initialY + 10f)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+        verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+
+        // Simulate ACTION_MOVE event (dragging far to the right and bottom, beyond screen bounds)
+        MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(mWindowBounds.width() + mTouchpadDebugView.getWidth())
+                        .y(mWindowBounds.height() + mTouchpadDebugView.getHeight())
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+        ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+        // Verify the view has been clamped to the right and bottom edges of the screen
+        assertEquals(mWindowBounds.width() - mTouchpadDebugView.getWidth(),
+                mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(mWindowBounds.height() - mTouchpadDebugView.getHeight(),
+                mWindowLayoutParamsCaptor.getValue().y);
+
+        MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(mWindowBounds.width() + mTouchpadDebugView.getWidth())
+                        .y(mWindowBounds.height() + mTouchpadDebugView.getHeight())
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+        // Verify the view has been clamped to the right and bottom edges of the screen
+        assertEquals(mWindowBounds.width() - mTouchpadDebugView.getWidth(),
+                mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(mWindowBounds.height() - mTouchpadDebugView.getHeight(),
+                mWindowLayoutParamsCaptor.getValue().y);
+    }
+
+    @Test
+    public void testSlopOffset() {
+        int initialX = mWindowLayoutParams.x;
+        int initialY = mWindowLayoutParams.y;
+
+        float offsetX = ViewConfiguration.get(mTestableContext).getScaledTouchSlop() / 2.0f;
+        float offsetY = -(ViewConfiguration.get(mTestableContext).getScaledTouchSlop() / 2.0f);
+
+        MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX)
+                        .y(initialY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+        MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX + offsetX)
+                        .y(initialY + offsetY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+        MotionEvent actionUp = new MotionEventBuilder(MotionEvent.ACTION_UP, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX)
+                        .y(initialY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionUp);
+
+        // In this case the updateViewLayout() method wouldn't be called because the drag
+        // distance hasn't exceeded the slop
+        verify(mWindowManager, times(0)).updateViewLayout(any(), any());
+    }
+
+    @Test
+    public void testViewReturnsToInitialPositionOnCancel() {
+        int initialX = mWindowLayoutParams.x;
+        int initialY = mWindowLayoutParams.y;
+
+        float offsetX = 50;
+        float offsetY = 50;
+
+        MotionEvent actionDown = new MotionEventBuilder(MotionEvent.ACTION_DOWN, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX)
+                        .y(initialY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionDown);
+
+        MotionEvent actionMove = new MotionEventBuilder(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX + offsetX)
+                        .y(initialY + offsetY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionMove);
+
+        ArgumentCaptor<WindowManager.LayoutParams> mWindowLayoutParamsCaptor =
+                ArgumentCaptor.forClass(WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(any(), mWindowLayoutParamsCaptor.capture());
+
+        assertEquals(initialX + (long) offsetX, mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(initialY + (long) offsetY, mWindowLayoutParamsCaptor.getValue().y);
+
+        // Simulate ACTION_CANCEL event (canceling the touch event stream)
+        MotionEvent actionCancel = new MotionEventBuilder(MotionEvent.ACTION_CANCEL,
+                SOURCE_TOUCHSCREEN)
+                .pointer(new PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER)
+                        .x(initialX + offsetX)
+                        .y(initialY + offsetY)
+                )
+                .build();
+        mTouchpadDebugView.dispatchTouchEvent(actionCancel);
+
+        // Verify the view returns to its initial position
+        verify(mWindowManager, times(2)).updateViewLayout(any(),
+                mWindowLayoutParamsCaptor.capture());
+        assertEquals(initialX, mWindowLayoutParamsCaptor.getValue().x);
+        assertEquals(initialY, mWindowLayoutParamsCaptor.getValue().y);
+    }
+}
diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml
index 7b67e9e..2d6c650e 100644
--- a/tests/Internal/AndroidTest.xml
+++ b/tests/Internal/AndroidTest.xml
@@ -26,4 +26,12 @@
         <option name="package" value="com.android.internal.tests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
     </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="pull-pattern-keys" value="perfetto_file_path"/>
+        <option name="directory-keys"
+            value="/data/user/0/com.android.internal.tests/files"/>
+        <option name="collect-on-run-ended-only" value="true"/>
+        <option name="clean-up" value="true"/>
+    </metrics_collector>
 </configuration>
\ No newline at end of file
diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
index 5a48327..9657225 100644
--- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java
@@ -214,6 +214,13 @@
         verify(mReader, never()).getViewerString(anyLong());
     }
 
+    @Test
+    public void loadViewerConfigOnLogcatGroupRegistration() {
+        TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+        mProtoLog.registerGroups(TestProtoLogGroup.TEST_GROUP);
+        verify(mReader).loadViewerConfig(any(), any());
+    }
+
     private static class ProtoLogData {
         Long mMessageHash = null;
         Long mElapsedTime = null;
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index fad94d4..27cc923 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -29,7 +29,6 @@
 import static org.mockito.Mockito.when;
 
 import static java.io.File.createTempFile;
-import static java.nio.file.Files.createTempDirectory;
 
 import android.content.Context;
 import android.os.SystemClock;
@@ -44,7 +43,7 @@
 import android.tracing.perfetto.DataSource;
 import android.util.proto.ProtoInputStream;
 
-import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.protolog.common.IProtoLogGroup;
 import com.android.internal.protolog.common.LogDataType;
@@ -67,18 +66,18 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Random;
-import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Test class for {@link ProtoLogImpl}.
  */
 @SuppressWarnings("ConstantConditions")
-@SmallTest
 @Presubmit
 @RunWith(JUnit4.class)
 public class PerfettoProtoLogImplTest {
-    private final File mTracingDirectory = createTempDirectory("temp").toFile();
+    private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
+    private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
+            .getTargetContext().getFilesDir();
 
     private final ResultWriter mWriter = new ResultWriter()
             .forScenario(new ScenarioBuilder()
@@ -93,6 +92,7 @@
             new TraceConfig(false, true, false)
     );
 
+    private ProtoLogConfigurationService mProtoLogConfigurationService;
     private PerfettoProtoLogImpl mProtoLog;
     private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
     private File mFile;
@@ -126,30 +126,35 @@
                                 .setMessage("My Test Debug Log Message %b")
                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
                                 .setGroupId(1)
+                                .setLocation("com/test/MyTestClass.java:123")
                 ).addMessages(
                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
                                 .setMessageId(2)
                                 .setMessage("My Test Verbose Log Message %b")
                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
                                 .setGroupId(1)
+                                .setLocation("com/test/MyTestClass.java:342")
                 ).addMessages(
                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
                                 .setMessageId(3)
                                 .setMessage("My Test Warn Log Message %b")
                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN)
                                 .setGroupId(1)
+                                .setLocation("com/test/MyTestClass.java:563")
                 ).addMessages(
                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
                                 .setMessageId(4)
                                 .setMessage("My Test Error Log Message %b")
                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR)
                                 .setGroupId(1)
+                                .setLocation("com/test/MyTestClass.java:156")
                 ).addMessages(
                         Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
                                 .setMessageId(5)
                                 .setMessage("My Test WTF Log Message %b")
                                 .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF)
                                 .setGroupId(1)
+                                .setLocation("com/test/MyTestClass.java:192")
                 );
 
         ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
@@ -159,10 +164,15 @@
 
         mCacheUpdater = () -> {};
         mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+
+        final ProtoLogDataSourceBuilder dataSourceBuilder =
+                (onStart, onFlush, onStop) -> new ProtoLogDataSource(
+                        onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME);
+        mProtoLogConfigurationService =
+                new ProtoLogConfigurationService(dataSourceBuilder);
         mProtoLog = new PerfettoProtoLogImpl(
-                viewerConfigInputStreamProvider, mReader,
-                () -> mCacheUpdater.run());
-        mProtoLog.registerGroups(TestProtoLogGroup.values());
+                viewerConfigInputStreamProvider, mReader, () -> mCacheUpdater.run(),
+                TestProtoLogGroup.values(), dataSourceBuilder, mProtoLogConfigurationService);
     }
 
     @After
@@ -181,8 +191,9 @@
 
     @Test
     public void isEnabled_returnsTrueAfterStart() {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             assertTrue(mProtoLog.isProtoEnabled());
@@ -193,8 +204,9 @@
 
     @Test
     public void isEnabled_returnsFalseAfterStop() {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             assertTrue(mProtoLog.isProtoEnabled());
@@ -207,8 +219,9 @@
 
     @Test
     public void defaultMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             // Shouldn't be logging anything except WTF unless explicitly requested in the group
@@ -236,11 +249,13 @@
 
     @Test
     public void respectsOverrideConfigs_defaultMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)))
-                        .build();
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -271,10 +286,12 @@
     @Test
     public void respectsOverrideConfigs_allEnabledMode() throws IOException {
         PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+                PerfettoTraceMonitor.newBuilder().enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)))
-                        .build();
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                    ).build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -302,9 +319,9 @@
 
     @Test
     public void respectsAllEnabledMode() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of())
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -379,7 +396,7 @@
                 new Object[]{5});
 
         verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
-                LogLevel.INFO), eq("UNKNOWN MESSAGE#1234 (5)"));
+                LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)"));
         verify(mReader).getViewerString(eq(1234L));
     }
 
@@ -403,8 +420,9 @@
                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
                 "My test message :: %s, %d, %o, %x, %f, %e, %g, %b");
 
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -435,8 +453,9 @@
 
     @Test
     public void log_noProcessing() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -446,8 +465,8 @@
             before = SystemClock.elapsedRealtimeNanos();
             mProtoLog.log(
                     LogLevel.INFO, TestProtoLogGroup.TEST_GROUP,
-                    "My test message :: %s, %d, %o, %x, %f, %b",
-                    "test", 1, 2, 3, 0.4, true);
+                    "My test message :: %s, %d, %x, %f, %b",
+                    "test", 1, 3, 0.4, true);
             after = SystemClock.elapsedRealtimeNanos();
         } finally {
             traceMonitor.stop(mWriter);
@@ -462,7 +481,28 @@
         Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
                 .isAtMost(after);
         Truth.assertThat(protolog.messages.getFirst().getMessage())
-                .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, true");
+                .isEqualTo("My test message :: test, 2, 6, 0.400000, true");
+    }
+
+    @Test
+    public  void supportsLocationInformation() throws IOException {
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
+        try {
+            traceMonitor.start();
+            mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+                    LogDataType.BOOLEAN, new Object[]{true});
+        } finally {
+            traceMonitor.stop(mWriter);
+        }
+
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+        Truth.assertThat(protolog.messages).hasSize(1);
+        Truth.assertThat(protolog.messages.get(0).getLocation())
+                .isEqualTo("com/test/MyTestClass.java:123");
     }
 
     private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
@@ -482,8 +522,9 @@
         final long messageHash = addMessageToConfig(
                 ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO,
                 "My test message :: %s, %d, %f, %b");
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         long before;
         long after;
         try {
@@ -504,8 +545,9 @@
 
     @Test
     public void log_protoDisabled() throws Exception {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(false).build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
         try {
             traceMonitor.start();
             mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
@@ -522,12 +564,14 @@
 
     @Test
     public void stackTraceTrimmed() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(
+                        true,
                         List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                 TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                true)))
-                        .build();
+                                true)),
+                        TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
         try {
             traceMonitor.start();
 
@@ -557,18 +601,18 @@
         final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
         mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
 
-        PerfettoTraceMonitor traceMonitor1 =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
-                                List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
-                                        TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
-                                        false)))
-                        .build();
+        PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true,
+                        List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
+                                TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
+                                false)), TEST_PROTOLOG_DATASOURCE_NAME
+                ).build();
 
         PerfettoTraceMonitor traceMonitor2 =
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0);
@@ -613,14 +657,14 @@
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         PerfettoTraceMonitor traceMonitor2 =
                 PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
                                 List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride(
                                         TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG,
-                                        false)))
+                                        false)), TEST_PROTOLOG_DATASOURCE_NAME)
                         .build();
 
         try {
@@ -690,9 +734,9 @@
 
     @Test
     public void supportsNullString() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
         try {
             traceMonitor.start();
@@ -713,9 +757,9 @@
 
     @Test
     public void supportNullParams() throws IOException {
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog(true)
-                        .build();
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
 
         try {
             traceMonitor.start();
@@ -734,6 +778,81 @@
                 .isEqualTo("My null args: 0, 0, false");
     }
 
+    @Test
+    public void handlesConcurrentTracingSessions() throws IOException {
+        PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
+
+        PerfettoTraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
+
+        final ResultWriter writer2 = new ResultWriter()
+                .forScenario(new ScenarioBuilder()
+                        .forClass(createTempFile("temp", "").getName()).build())
+                .withOutputDir(mTracingDirectory)
+                .setRunComplete();
+
+        try {
+            traceMonitor1.start();
+            traceMonitor2.start();
+
+            mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+                    LogDataType.BOOLEAN, new Object[]{true});
+        } finally {
+            traceMonitor1.stop(mWriter);
+            traceMonitor2.stop(writer2);
+        }
+
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final ProtoLogTrace protologFromMonitor1 = reader.readProtoLogTrace();
+
+        final ResultReader reader2 = new ResultReader(writer2.write(), mTraceConfig);
+        final ProtoLogTrace protologFromMonitor2 = reader2.readProtoLogTrace();
+
+        Truth.assertThat(protologFromMonitor1.messages).hasSize(1);
+        Truth.assertThat(protologFromMonitor1.messages.get(0).getMessage())
+                .isEqualTo("My Test Debug Log Message true");
+
+        Truth.assertThat(protologFromMonitor2.messages).hasSize(1);
+        Truth.assertThat(protologFromMonitor2.messages.get(0).getMessage())
+                .isEqualTo("My Test Debug Log Message true");
+    }
+
+    @Test
+    public void usesDefaultLogFromLevel() throws IOException {
+        PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+                .enableProtoLog(LogLevel.WARN, List.of(), TEST_PROTOLOG_DATASOURCE_NAME)
+                .build();
+        try {
+            traceMonitor.start();
+            mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+                "This message should not be logged");
+            mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP,
+                "This message should logged %d", 123);
+            mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP,
+                "This message should also be logged %d", 567);
+        } finally {
+            traceMonitor.stop(mWriter);
+        }
+
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final ProtoLogTrace protolog = reader.readProtoLogTrace();
+
+        Truth.assertThat(protolog.messages).hasSize(2);
+
+        Truth.assertThat(protolog.messages.get(0).getLevel())
+                .isEqualTo(LogLevel.WARN);
+        Truth.assertThat(protolog.messages.get(0).getMessage())
+                .isEqualTo("This message should logged 123");
+
+        Truth.assertThat(protolog.messages.get(1).getLevel())
+                .isEqualTo(LogLevel.ERROR);
+        Truth.assertThat(protolog.messages.get(1).getMessage())
+                .isEqualTo("This message should also be logged 567");
+    }
+
     private enum TestProtoLogGroup implements IProtoLogGroup {
         TEST_GROUP(true, true, false, "TEST_TAG");
 
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java
index e3ec62d..aba6722 100644
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java
@@ -41,14 +41,14 @@
 public class ProtoLogCommandHandlerTest {
 
     @Mock
-    ProtoLogService mProtoLogService;
+    ProtoLogConfigurationService mProtoLogConfigurationService;
     @Mock
     PrintWriter mPrintWriter;
 
     @Test
     public void printsHelpForAllAvailableCommands() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
         cmdHandler.onHelp();
         validateOnHelpPrinted();
@@ -57,7 +57,7 @@
     @Test
     public void printsHelpIfCommandIsNull() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
         cmdHandler.onCommand(null);
         validateOnHelpPrinted();
@@ -65,13 +65,13 @@
 
     @Test
     public void handlesGroupListCommand() {
-        Mockito.when(mProtoLogService.getGroups())
+        Mockito.when(mProtoLogConfigurationService.getGroups())
                 .thenReturn(new String[] {"MY_TEST_GROUP", "MY_OTHER_GROUP"});
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "groups", "list" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "groups", "list" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("MY_TEST_GROUP"));
@@ -82,10 +82,10 @@
     @Test
     public void handlesIncompleteGroupsCommand() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "groups" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "groups" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("Incomplete command"));
@@ -93,13 +93,14 @@
 
     @Test
     public void handlesGroupStatusCommand() {
-        Mockito.when(mProtoLogService.getGroups()).thenReturn(new String[] {"MY_GROUP"});
-        Mockito.when(mProtoLogService.isLoggingToLogcat("MY_GROUP")).thenReturn(true);
+        Mockito.when(mProtoLogConfigurationService.getGroups())
+                .thenReturn(new String[] {"MY_GROUP"});
+        Mockito.when(mProtoLogConfigurationService.isLoggingToLogcat("MY_GROUP")).thenReturn(true);
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "groups", "status", "MY_GROUP" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("MY_GROUP"));
@@ -109,12 +110,12 @@
 
     @Test
     public void handlesGroupStatusCommandOfUnregisteredGroups() {
-        Mockito.when(mProtoLogService.getGroups()).thenReturn(new String[] {});
+        Mockito.when(mProtoLogConfigurationService.getGroups()).thenReturn(new String[] {});
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "groups", "status", "MY_GROUP" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("MY_GROUP"));
@@ -125,10 +126,10 @@
     @Test
     public void handlesGroupStatusCommandWithNoGroups() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "groups", "status" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "groups", "status" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("Incomplete command"));
@@ -137,10 +138,10 @@
     @Test
     public void handlesIncompleteLogcatCommand() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "logcat" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "logcat" });
 
         Mockito.verify(mPrintWriter, times(1))
                 .println(contains("Incomplete command"));
@@ -149,50 +150,52 @@
     @Test
     public void handlesLogcatEnableCommand() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "logcat", "enable", "MY_GROUP" });
-        Mockito.verify(mProtoLogService).enableProtoLogToLogcat("MY_GROUP");
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "logcat", "enable", "MY_GROUP" });
+        Mockito.verify(mProtoLogConfigurationService).enableProtoLogToLogcat("MY_GROUP");
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err,
                 new String[] { "logcat", "enable", "MY_GROUP", "MY_OTHER_GROUP" });
-        Mockito.verify(mProtoLogService)
+        Mockito.verify(mProtoLogConfigurationService)
                 .enableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP");
     }
 
     @Test
     public void handlesLogcatDisableCommand() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "logcat", "disable", "MY_GROUP" });
-        Mockito.verify(mProtoLogService).disableProtoLogToLogcat("MY_GROUP");
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "logcat", "disable", "MY_GROUP" });
+        Mockito.verify(mProtoLogConfigurationService).disableProtoLogToLogcat("MY_GROUP");
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err,
                 new String[] { "logcat", "disable", "MY_GROUP", "MY_OTHER_GROUP" });
-        Mockito.verify(mProtoLogService)
+        Mockito.verify(mProtoLogConfigurationService)
                 .disableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP");
     }
 
     @Test
     public void handlesLogcatEnableCommandWithNoGroups() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "logcat", "enable" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "logcat", "enable" });
         Mockito.verify(mPrintWriter).println(contains("Incomplete command"));
     }
 
     @Test
     public void handlesLogcatDisableCommandWithNoGroups() {
         final ProtoLogCommandHandler cmdHandler =
-                new ProtoLogCommandHandler(mProtoLogService, mPrintWriter);
+                new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter);
 
-        cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                new String[] { "logcat", "disable" });
+        cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out,
+                FileDescriptor.err, new String[] { "logcat", "disable" });
         Mockito.verify(mPrintWriter).println(contains("Incomplete command"));
     }
 
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
new file mode 100644
index 0000000..e1bdd77
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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.protolog;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+
+import static java.io.File.createTempFile;
+import static java.nio.file.Files.createTempDirectory;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.tools.ScenarioBuilder;
+import android.tools.Tag;
+import android.tools.io.ResultArtifactDescriptor;
+import android.tools.io.TraceType;
+import android.tools.traces.TraceConfig;
+import android.tools.traces.TraceConfigs;
+import android.tools.traces.io.ResultReader;
+import android.tools.traces.io.ResultWriter;
+import android.tools.traces.monitors.PerfettoTraceMonitor;
+
+import com.google.common.truth.Truth;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.junit.Before;
+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;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import perfetto.protos.Protolog.ProtoLogViewerConfig;
+import perfetto.protos.ProtologCommon;
+import perfetto.protos.TraceOuterClass.Trace;
+import perfetto.protos.TracePacketOuterClass.TracePacket;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Test class for {@link ProtoLogImpl}.
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class ProtoLogConfigurationServiceTest {
+
+    private static final String TEST_GROUP = "MY_TEST_GROUP";
+    private static final String OTHER_TEST_GROUP = "MY_OTHER_TEST_GROUP";
+
+    private static final ProtoLogViewerConfig VIEWER_CONFIG =
+            ProtoLogViewerConfig.newBuilder()
+                    .addGroups(
+                            ProtoLogViewerConfig.Group.newBuilder()
+                                    .setId(1)
+                                    .setName(TEST_GROUP)
+                                    .setTag(TEST_GROUP)
+                    ).addMessages(
+                            ProtoLogViewerConfig.MessageData.newBuilder()
+                                    .setMessageId(1)
+                                    .setMessage("My Test Debug Log Message %b")
+                                    .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
+                                    .setGroupId(1)
+                    ).addMessages(
+                            ProtoLogViewerConfig.MessageData.newBuilder()
+                                    .setMessageId(2)
+                                    .setMessage("My Test Verbose Log Message %b")
+                                    .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
+                                    .setGroupId(1)
+                    ).build();
+
+    @Mock
+    IProtoLogClient mMockClient;
+
+    @Mock
+    IProtoLogClient mSecondMockClient;
+
+    @Mock
+    IBinder mMockClientBinder;
+
+    @Mock
+    IBinder mSecondMockClientBinder;
+
+    private final File mTracingDirectory = createTempDirectory("temp").toFile();
+
+    private final ResultWriter mWriter = new ResultWriter()
+            .forScenario(new ScenarioBuilder()
+                    .forClass(createTempFile("temp", "").getName()).build())
+            .withOutputDir(mTracingDirectory)
+            .setRunComplete();
+
+    private final TraceConfigs mTraceConfig = new TraceConfigs(
+            new TraceConfig(false, true, false),
+            new TraceConfig(false, true, false),
+            new TraceConfig(false, true, false),
+            new TraceConfig(false, true, false)
+    );
+
+    @Captor
+    ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientArgumentCaptor;
+
+    @Captor
+    ArgumentCaptor<IBinder.DeathRecipient> mSecondDeathRecipientArgumentCaptor;
+
+    private File mViewerConfigFile;
+
+    public ProtoLogConfigurationServiceTest() throws IOException {
+    }
+
+    @Before
+    public void setUp() {
+        Mockito.when(mMockClient.asBinder()).thenReturn(mMockClientBinder);
+        Mockito.when(mSecondMockClient.asBinder()).thenReturn(mSecondMockClientBinder);
+
+        try {
+            mViewerConfigFile = File.createTempFile("viewer-config", ".pb");
+            try (var fos = new FileOutputStream(mViewerConfigFile);
+                    BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+
+                bos.write(VIEWER_CONFIG.toByteArray());
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void canRegisterClientWithGroupsOnly() throws RemoteException {
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, true));
+        service.registerClient(mMockClient, args);
+
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
+        Truth.assertThat(service.getGroups()).asList().containsExactly(TEST_GROUP);
+    }
+
+    @Test
+    public void willDumpViewerConfigOnlyOnceOnTraceStop()
+            throws RemoteException, InvalidProtocolBufferException {
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, true))
+                        .setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
+        service.registerClient(mMockClient, args);
+        service.registerClient(mSecondMockClient, args);
+
+        PerfettoTraceMonitor traceMonitor =
+                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
+
+        traceMonitor.start();
+        traceMonitor.stop(mWriter);
+        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+        final byte[] traceData = reader.getArtifact()
+                .readBytes(new ResultArtifactDescriptor(TraceType.PERFETTO, Tag.ALL));
+
+        final Trace trace = Trace.parseFrom(traceData);
+
+        final List<TracePacket> configPackets = trace.getPacketList().stream()
+                .filter(it -> it.hasProtologViewerConfig())
+                // Exclude viewer configs from regular system tracing
+                .filter(it ->
+                        it.getProtologViewerConfig().getGroups(0).getName().equals(TEST_GROUP))
+                .toList();
+        Truth.assertThat(configPackets).hasSize(1);
+        Truth.assertThat(configPackets.get(0).getProtologViewerConfig().toString())
+                .isEqualTo(VIEWER_CONFIG.toString());
+    }
+
+    @Test
+    public void willDumpViewerConfigOnLastClientDisconnected()
+            throws RemoteException, FileNotFoundException {
+        final ProtoLogConfigurationService.ViewerConfigFileTracer tracer =
+                Mockito.mock(ProtoLogConfigurationService.ViewerConfigFileTracer.class);
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService(tracer);
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, true))
+                        .setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
+        service.registerClient(mMockClient, args);
+        service.registerClient(mSecondMockClient, args);
+
+        Mockito.verify(mMockClientBinder)
+                .linkToDeath(mDeathRecipientArgumentCaptor.capture(), anyInt());
+        Mockito.verify(mSecondMockClientBinder)
+                .linkToDeath(mSecondDeathRecipientArgumentCaptor.capture(), anyInt());
+
+        mDeathRecipientArgumentCaptor.getValue().binderDied();
+        Mockito.verify(tracer, never()).trace(any(), any());
+        mSecondDeathRecipientArgumentCaptor.getValue().binderDied();
+        Mockito.verify(tracer).trace(any(), eq(mViewerConfigFile.getAbsolutePath()));
+    }
+
+    @Test
+    public void sendEnableLoggingToLogcatToClient() throws RemoteException {
+        final var service = new ProtoLogConfigurationService();
+
+        final var args = new ProtoLogConfigurationService.RegisterClientArgs()
+                .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                        .GroupConfig(TEST_GROUP, false));
+        service.registerClient(mMockClient, args);
+
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
+        service.enableProtoLogToLogcat(TEST_GROUP);
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
+
+        Mockito.verify(mMockClient).toggleLogcat(eq(true),
+                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
+    }
+
+    @Test
+    public void sendDisableLoggingToLogcatToClient() throws RemoteException {
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, true));
+        service.registerClient(mMockClient, args);
+
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
+        service.disableProtoLogToLogcat(TEST_GROUP);
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
+
+        Mockito.verify(mMockClient).toggleLogcat(eq(false),
+                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
+    }
+
+    @Test
+    public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException {
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, false));
+        service.registerClient(mMockClient, args);
+
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
+        service.enableProtoLogToLogcat(OTHER_TEST_GROUP);
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
+
+        Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any());
+    }
+
+    @Test
+    public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException {
+        final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+
+        Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP);
+        service.enableProtoLogToLogcat(TEST_GROUP);
+        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
+
+        final ProtoLogConfigurationService.RegisterClientArgs args =
+                new ProtoLogConfigurationService.RegisterClientArgs()
+                        .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+                                .GroupConfig(TEST_GROUP, false));
+        service.registerClient(mMockClient, args);
+
+        Mockito.verify(mMockClient).toggleLogcat(eq(true),
+                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
+    }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java
deleted file mode 100644
index feac59c..0000000
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java
+++ /dev/null
@@ -1,283 +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.internal.protolog;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-
-import static java.io.File.createTempFile;
-import static java.nio.file.Files.createTempDirectory;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.tools.ScenarioBuilder;
-import android.tools.Tag;
-import android.tools.io.ResultArtifactDescriptor;
-import android.tools.io.TraceType;
-import android.tools.traces.TraceConfig;
-import android.tools.traces.TraceConfigs;
-import android.tools.traces.io.ResultReader;
-import android.tools.traces.io.ResultWriter;
-import android.tools.traces.monitors.PerfettoTraceMonitor;
-
-import com.google.common.truth.Truth;
-import com.google.protobuf.InvalidProtocolBufferException;
-
-import org.junit.Before;
-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;
-import org.mockito.junit.MockitoJUnitRunner;
-
-import perfetto.protos.Protolog.ProtoLogViewerConfig;
-import perfetto.protos.ProtologCommon;
-import perfetto.protos.TraceOuterClass.Trace;
-import perfetto.protos.TracePacketOuterClass.TracePacket;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * Test class for {@link ProtoLogImpl}.
- */
-@Presubmit
-@RunWith(MockitoJUnitRunner.class)
-public class ProtoLogServiceTest {
-
-    private static final String TEST_GROUP = "MY_TEST_GROUP";
-    private static final String OTHER_TEST_GROUP = "MY_OTHER_TEST_GROUP";
-
-    private static final ProtoLogViewerConfig VIEWER_CONFIG =
-            ProtoLogViewerConfig.newBuilder()
-                    .addGroups(
-                            ProtoLogViewerConfig.Group.newBuilder()
-                                    .setId(1)
-                                    .setName(TEST_GROUP)
-                                    .setTag(TEST_GROUP)
-                    ).addMessages(
-                            ProtoLogViewerConfig.MessageData.newBuilder()
-                                    .setMessageId(1)
-                                    .setMessage("My Test Debug Log Message %b")
-                                    .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG)
-                                    .setGroupId(1)
-                    ).addMessages(
-                            ProtoLogViewerConfig.MessageData.newBuilder()
-                                    .setMessageId(2)
-                                    .setMessage("My Test Verbose Log Message %b")
-                                    .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE)
-                                    .setGroupId(1)
-                    ).build();
-
-    @Mock
-    IProtoLogClient mMockClient;
-
-    @Mock
-    IProtoLogClient mSecondMockClient;
-
-    @Mock
-    IBinder mMockClientBinder;
-
-    @Mock
-    IBinder mSecondMockClientBinder;
-
-    private final File mTracingDirectory = createTempDirectory("temp").toFile();
-
-    private final ResultWriter mWriter = new ResultWriter()
-            .forScenario(new ScenarioBuilder()
-                    .forClass(createTempFile("temp", "").getName()).build())
-            .withOutputDir(mTracingDirectory)
-            .setRunComplete();
-
-    private final TraceConfigs mTraceConfig = new TraceConfigs(
-            new TraceConfig(false, true, false),
-            new TraceConfig(false, true, false),
-            new TraceConfig(false, true, false),
-            new TraceConfig(false, true, false)
-    );
-
-    @Captor
-    ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientArgumentCaptor;
-
-    @Captor
-    ArgumentCaptor<IBinder.DeathRecipient> mSecondDeathRecipientArgumentCaptor;
-
-    private File mViewerConfigFile;
-
-    public ProtoLogServiceTest() throws IOException {
-    }
-
-    @Before
-    public void setUp() {
-        Mockito.when(mMockClient.asBinder()).thenReturn(mMockClientBinder);
-        Mockito.when(mSecondMockClient.asBinder()).thenReturn(mSecondMockClientBinder);
-
-        try {
-            mViewerConfigFile = File.createTempFile("viewer-config", ".pb");
-            try (var fos = new FileOutputStream(mViewerConfigFile);
-                    BufferedOutputStream bos = new BufferedOutputStream(fos)) {
-
-                bos.write(VIEWER_CONFIG.toByteArray());
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @Test
-    public void canRegisterClientWithGroupsOnly() throws RemoteException {
-        final ProtoLogService service = new ProtoLogService();
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true));
-        service.registerClient(mMockClient, args);
-
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
-        Truth.assertThat(service.getGroups()).asList().containsExactly(TEST_GROUP);
-    }
-
-    @Test
-    public void willDumpViewerConfigOnlyOnceOnTraceStop()
-            throws RemoteException, InvalidProtocolBufferException {
-        final ProtoLogService service = new ProtoLogService();
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true))
-                .setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
-        service.registerClient(mMockClient, args);
-        service.registerClient(mSecondMockClient, args);
-
-        PerfettoTraceMonitor traceMonitor =
-                PerfettoTraceMonitor.newBuilder().enableProtoLog().build();
-
-        traceMonitor.start();
-        traceMonitor.stop(mWriter);
-        final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
-        final byte[] traceData = reader.getArtifact()
-                .readBytes(new ResultArtifactDescriptor(TraceType.PERFETTO, Tag.ALL));
-
-        final Trace trace = Trace.parseFrom(traceData);
-
-        final List<TracePacket> configPackets = trace.getPacketList().stream()
-                .filter(it -> it.hasProtologViewerConfig())
-                // Exclude viewer configs from regular system tracing
-                .filter(it ->
-                        it.getProtologViewerConfig().getGroups(0).getName().equals(TEST_GROUP))
-                .toList();
-        Truth.assertThat(configPackets).hasSize(1);
-        Truth.assertThat(configPackets.get(0).getProtologViewerConfig().toString())
-                .isEqualTo(VIEWER_CONFIG.toString());
-    }
-
-    @Test
-    public void willDumpViewerConfigOnLastClientDisconnected()
-            throws RemoteException, FileNotFoundException {
-        final ProtoLogService.ViewerConfigFileTracer tracer =
-                Mockito.mock(ProtoLogService.ViewerConfigFileTracer.class);
-        final ProtoLogService service = new ProtoLogService(tracer);
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(
-                        TEST_GROUP, true))
-                .setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
-        service.registerClient(mMockClient, args);
-        service.registerClient(mSecondMockClient, args);
-
-        Mockito.verify(mMockClientBinder)
-                .linkToDeath(mDeathRecipientArgumentCaptor.capture(), anyInt());
-        Mockito.verify(mSecondMockClientBinder)
-                .linkToDeath(mSecondDeathRecipientArgumentCaptor.capture(), anyInt());
-
-        mDeathRecipientArgumentCaptor.getValue().binderDied();
-        Mockito.verify(tracer, never()).trace(any(), any());
-        mSecondDeathRecipientArgumentCaptor.getValue().binderDied();
-        Mockito.verify(tracer).trace(any(), eq(mViewerConfigFile.getAbsolutePath()));
-    }
-
-    @Test
-    public void sendEnableLoggingToLogcatToClient() throws RemoteException {
-        final var service = new ProtoLogService();
-
-        final var args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false));
-        service.registerClient(mMockClient, args);
-
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
-        service.enableProtoLogToLogcat(TEST_GROUP);
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
-
-        Mockito.verify(mMockClient).toggleLogcat(eq(true),
-                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
-    }
-
-    @Test
-    public void sendDisableLoggingToLogcatToClient() throws RemoteException {
-        final ProtoLogService service = new ProtoLogService();
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true));
-        service.registerClient(mMockClient, args);
-
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
-        service.disableProtoLogToLogcat(TEST_GROUP);
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
-
-        Mockito.verify(mMockClient).toggleLogcat(eq(false),
-                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
-    }
-
-    @Test
-    public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException {
-        final ProtoLogService service = new ProtoLogService();
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false));
-        service.registerClient(mMockClient, args);
-
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
-        service.enableProtoLogToLogcat(OTHER_TEST_GROUP);
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse();
-
-        Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any());
-    }
-
-    @Test
-    public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException {
-        final ProtoLogService service = new ProtoLogService();
-
-        Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP);
-        service.enableProtoLogToLogcat(TEST_GROUP);
-        Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
-
-        final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs()
-                .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false));
-        service.registerClient(mMockClient, args);
-
-        Mockito.verify(mMockClient).toggleLogcat(eq(true),
-                Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP)));
-    }
-}
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java
new file mode 100644
index 0000000..9d56a92
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.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 com.android.internal.protolog;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+import com.google.common.truth.Truth;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Test class for {@link ProtoLog}. */
+@SuppressWarnings("ConstantConditions")
+@Presubmit
+@RunWith(JUnit4.class)
+public class ProtoLogTest {
+
+    @Test
+    public void canRunProtoLogInitMultipleTimes() {
+        ProtoLog.init(TEST_GROUP_1);
+        ProtoLog.init(TEST_GROUP_1);
+        ProtoLog.init(TEST_GROUP_2);
+        ProtoLog.init(TEST_GROUP_1, TEST_GROUP_2);
+
+        final var instance = ProtoLog.getSingleInstance();
+        Truth.assertThat(instance.getRegisteredGroups())
+                .containsExactly(TEST_GROUP_1, TEST_GROUP_2);
+    }
+
+    private static final IProtoLogGroup TEST_GROUP_1 = new ProtoLogGroup("TEST_TAG_1", 1);
+    private static final IProtoLogGroup TEST_GROUP_2 = new ProtoLogGroup("TEST_TAG_2", 2);
+
+    private static class ProtoLogGroup implements IProtoLogGroup {
+        private final boolean mEnabled;
+        private volatile boolean mLogToProto;
+        private volatile boolean mLogToLogcat;
+        private final String mTag;
+        private final int mId;
+
+        ProtoLogGroup(String tag, int id) {
+            this(true, true, false, tag, id);
+        }
+
+        ProtoLogGroup(
+                boolean enabled, boolean logToProto, boolean logToLogcat, String tag, int id) {
+            this.mEnabled = enabled;
+            this.mLogToProto = logToProto;
+            this.mLogToLogcat = logToLogcat;
+            this.mTag = tag;
+            this.mId = id;
+        }
+
+        @Override
+        public String name() {
+            return mTag;
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return mEnabled;
+        }
+
+        @Override
+        public boolean isLogToProto() {
+            return mLogToProto;
+        }
+
+        @Override
+        public boolean isLogToLogcat() {
+            return mLogToLogcat;
+        }
+
+        @Override
+        public boolean isLogToAny() {
+            return mLogToLogcat || mLogToProto;
+        }
+
+        @Override
+        public String getTag() {
+            return mTag;
+        }
+
+        @Override
+        public void setLogToProto(boolean logToProto) {
+            this.mLogToProto = logToProto;
+        }
+
+        @Override
+        public void setLogToLogcat(boolean logToLogcat) {
+            this.mLogToLogcat = logToLogcat;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index ab406ef..5b17825 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1867,7 +1867,7 @@
             return true;
         }
 
-        public String getName() {
+        public String getUniqueIdentifier() {
             return mName;
         }
 
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
index 359eb35..5012c23 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
@@ -84,6 +84,7 @@
         content.addView(enableSyncButton,
                 new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                         ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM));
+        content.setFitsSystemWindows(true);
         setContentView(content);
 
         mSv.setZOrderOnTop(false);
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
index 73e0163..4119ea2 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java
@@ -37,6 +37,7 @@
 
     protected void onCreate(Bundle savedInstanceState) {
         FrameLayout content = new FrameLayout(this);
+        content.setFitsSystemWindows(true);
         super.onCreate(savedInstanceState);
         mView = new SurfaceView(this);
         content.addView(mView, new FrameLayout.LayoutParams(
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
index ac7dc9e..5287068 100644
--- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java
@@ -88,6 +88,7 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         LinearLayout content = new LinearLayout(this);
+        content.setFitsSystemWindows(true);
         mLocalSurfaceView = new SurfaceView(this);
         content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams(
                 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index 4e75a1d..8888b32 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -40,3 +40,10 @@
     platform_apis: true,
     certificate: "platform",
 }
+
+test_module_config {
+    name: "TrustTests_trust_test",
+    base: "TrustTests",
+    test_suites: ["device-tests"],
+    include_filters: ["android.trust.test"],
+}
diff --git a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
index 09236ff..459db8a 100644
--- a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java
@@ -74,6 +74,9 @@
         }
     }
 
+    private ObjectAnimator mColorValueAnimator;
+    private ObjectAnimator mYAnimator;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -81,16 +84,28 @@
 
         // animate color to force bitmap uploads
         UploadView uploadView = findViewById(R.id.upload_view);
-        ObjectAnimator colorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
-        colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
-        colorValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
-        colorValueAnimator.start();
+        mColorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255);
+        mColorValueAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mColorValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mColorValueAnimator.start();
 
         // animate scene root to guarantee there's a minimum amount of GPU rendering work
         View uploadRoot = findViewById(R.id.upload_root);
-        ObjectAnimator yAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100);
-        yAnimator.setRepeatMode(ValueAnimator.REVERSE);
-        yAnimator.setRepeatCount(ValueAnimator.INFINITE);
-        yAnimator.start();
+        mYAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100);
+        mYAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mYAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mYAnimator.start();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mColorValueAnimator != null) {
+            mColorValueAnimator.cancel();
+        }
+
+        if (mYAnimator != null) {
+            mYAnimator.cancel();
+        }
     }
 }
diff --git a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
index 882163b..9d10f76 100644
--- a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java
@@ -66,18 +66,29 @@
             return PixelFormat.OPAQUE;
         }
     }
+
+    private ObjectAnimator mObjectAnimator;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         OverdrawDrawable overdraw = new OverdrawDrawable();
         getWindow().setBackgroundDrawable(overdraw);
-
         setContentView(new View(this));
 
-        ObjectAnimator objectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255);
-        objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
-        objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
-        objectAnimator.start();
+        mObjectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255);
+        mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE);
+
+        mObjectAnimator.start();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mObjectAnimator != null) {
+            mObjectAnimator.cancel();
+        }
     }
 }
diff --git a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
index b26a660..1b28dc2 100644
--- a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java
@@ -33,6 +33,7 @@
 public class GlTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
     private ImageFlipRenderThread mRenderThread;
     private TextureView mTextureView;
+    private ObjectAnimator mAnimator;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -54,17 +55,17 @@
         int distance = Math.max(mTextureView.getWidth(), mTextureView.getHeight());
         mTextureView.setCameraDistance(distance * metrics.density);
 
-        ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
-        animator.setRepeatMode(ObjectAnimator.REVERSE);
-        animator.setRepeatCount(ObjectAnimator.INFINITE);
-        animator.setDuration(4000);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+        mAnimator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f);
+        mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+        mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+        mAnimator.setDuration(4000);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 mTextureView.invalidate();
             }
         });
-        animator.start();
+        mAnimator.start();
     }
 
     @Override
@@ -86,4 +87,11 @@
     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+    }
 }
\ No newline at end of file
diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
index 76ed1ae..f1e96c8 100644
--- a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java
@@ -51,6 +51,7 @@
     }
 
     private ColorView[][] mColorViews;
+    private ObjectAnimator mAnimator;
 
     @SuppressWarnings("unused")
     public void setColorValue(int colorValue) {
@@ -80,9 +81,17 @@
             }
         }
 
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
-        animator.setRepeatMode(ValueAnimator.REVERSE);
-        animator.setRepeatCount(ValueAnimator.INFINITE);
-        animator.start();
+        mAnimator = ObjectAnimator.ofInt(this, "colorValue", 0, 255);
+        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mAnimator.start();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
     }
 }
diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
index 804ced1..9563572 100644
--- a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java
@@ -33,6 +33,7 @@
     private final ArrayList<LinearLayout> mLayouts = new ArrayList<>();
 
     private int mColorToggle = 0;
+    private ObjectAnimator mAnimator;
 
     private void createQuadTree(LinearLayout parent, int remainingDepth) {
         mLayouts.add(parent);
@@ -71,9 +72,17 @@
         createQuadTree(root, 8);
         setContentView(root);
 
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000);
-        animator.setRepeatMode(ValueAnimator.REVERSE);
-        animator.setRepeatCount(ValueAnimator.INFINITE);
-        animator.start();
+        mAnimator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000);
+        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mAnimator.start();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
     }
 }
diff --git a/tests/UiBench/src/com/android/test/uibench/ResizeHWLayerActivity.java b/tests/UiBench/src/com/android/test/uibench/ResizeHWLayerActivity.java
index 80d495d..cb26edc 100644
--- a/tests/UiBench/src/com/android/test/uibench/ResizeHWLayerActivity.java
+++ b/tests/UiBench/src/com/android/test/uibench/ResizeHWLayerActivity.java
@@ -30,6 +30,8 @@
  */
 public class ResizeHWLayerActivity extends AppCompatActivity {
 
+    private ValueAnimator mAnimator;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -43,10 +45,10 @@
         PropertyValuesHolder pvhWidth = PropertyValuesHolder.ofInt("width", width, 1);
         PropertyValuesHolder pvhHeight = PropertyValuesHolder.ofInt("height", height, 1);
         final LayoutParams params = child.getLayoutParams();
-        ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(pvhWidth, pvhHeight);
-        animator.setRepeatMode(ValueAnimator.REVERSE);
-        animator.setRepeatCount(ValueAnimator.INFINITE);
-        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+        mAnimator = ValueAnimator.ofPropertyValuesHolder(pvhWidth, pvhHeight);
+        mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+        mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator valueAnimator) {
                 params.width = (Integer)valueAnimator.getAnimatedValue("width");
@@ -54,7 +56,15 @@
                 child.requestLayout();
             }
         });
-        animator.start();
+        mAnimator.start();
         setContentView(child);
     }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 887630b..b5cc553 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -59,7 +59,6 @@
 import android.os.ParcelUuid;
 import android.os.PersistableBundle;
 import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -73,10 +72,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.telephony.flags.Flags;
-
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -133,8 +129,6 @@
         TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
     }
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
 
     @NonNull private final Context mContext;
     @NonNull private final TestLooper mTestLooper;
@@ -193,7 +187,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
         doReturn(2).when(mTelephonyManager).getActiveModemCount();
 
         mCallback = mock(TelephonySubscriptionTrackerCallback.class);
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1c85e9f..a5aecc8 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -151,6 +151,7 @@
   }
 
   if (res->value != nullptr) {
+    res->value->SetFlagStatus(res->flag_status);
     // Attach the comment, source and config to the value.
     res->value->SetComment(std::move(res->comment));
     res->value->SetSource(std::move(res->source));
@@ -546,30 +547,11 @@
   });
 
   std::string resource_type = parser->element_name();
-  std::optional<StringPiece> flag =
-      xml::FindAttribute(parser, "http://schemas.android.com/apk/res/android", "featureFlag");
-  out_resource->flag_status = FlagStatus::NoFlag;
-  if (flag) {
-    auto flag_it = options_.feature_flag_values.find(flag.value());
-    if (flag_it == options_.feature_flag_values.end()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Resource flag value undefined");
-      return false;
-    }
-    const auto& flag_properties = flag_it->second;
-    if (!flag_properties.read_only) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only read only flags may be used with resources");
-      return false;
-    }
-    if (!flag_properties.enabled.has_value()) {
-      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
-                   << "Only flags with a value may be used with resources");
-      return false;
-    }
-    out_resource->flag_status =
-        flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+  auto flag_status = GetFlagStatus(parser);
+  if (!flag_status) {
+    return false;
   }
+  out_resource->flag_status = flag_status.value();
 
   // The value format accepted for this resource.
   uint32_t resource_format = 0u;
@@ -751,6 +733,33 @@
   return false;
 }
 
+std::optional<FlagStatus> ResourceParser::GetFlagStatus(xml::XmlPullParser* parser) {
+  auto flag_status = FlagStatus::NoFlag;
+
+  std::optional<StringPiece> flag = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag");
+  if (flag) {
+    auto flag_it = options_.feature_flag_values.find(flag.value());
+    if (flag_it == options_.feature_flag_values.end()) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Resource flag value undefined");
+      return {};
+    }
+    const auto& flag_properties = flag_it->second;
+    if (!flag_properties.read_only) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Only read only flags may be used with resources");
+      return {};
+    }
+    if (!flag_properties.enabled.has_value()) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Only flags with a value may be used with resources");
+      return {};
+    }
+    flag_status = flag_properties.enabled.value() ? FlagStatus::Enabled : FlagStatus::Disabled;
+  }
+  return flag_status;
+}
+
 bool ResourceParser::ParseItem(xml::XmlPullParser* parser,
                                ParsedResource* out_resource,
                                const uint32_t format) {
@@ -1657,12 +1666,18 @@
     const std::string& element_namespace = parser->element_namespace();
     const std::string& element_name = parser->element_name();
     if (element_namespace.empty() && element_name == "item") {
+      auto flag_status = GetFlagStatus(parser);
+      if (!flag_status) {
+        error = true;
+        continue;
+      }
       std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString);
       if (!item) {
         diag_->Error(android::DiagMessage(item_source) << "could not parse array item");
         error = true;
         continue;
       }
+      item->SetFlagStatus(flag_status.value());
       item->SetSource(item_source);
       array->elements.emplace_back(std::move(item));
 
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 45d41c1..442dea8 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -85,6 +85,8 @@
  private:
   DISALLOW_COPY_AND_ASSIGN(ResourceParser);
 
+  std::optional<FlagStatus> GetFlagStatus(xml::XmlPullParser* parser);
+
   std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
 
   // Parses the XML subtree as a StyleString (flattened XML representation for strings with
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 1cdb715..9751459 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -50,21 +50,21 @@
 
 template <typename T>
 bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, StringPiece rhs) {
-  return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
+  return lhs->name < rhs;
 }
 
 template <typename T>
 bool greater_than_struct_with_name(StringPiece lhs, const std::unique_ptr<T>& rhs) {
-  return rhs->name.compare(0, rhs->name.size(), lhs.data(), lhs.size()) > 0;
+  return rhs->name > lhs;
 }
 
 template <typename T>
 struct NameEqualRange {
   bool operator()(const std::unique_ptr<T>& lhs, StringPiece rhs) const {
-    return less_than_struct_with_name<T>(lhs, rhs);
+    return less_than_struct_with_name(lhs, rhs);
   }
   bool operator()(StringPiece lhs, const std::unique_ptr<T>& rhs) const {
-    return greater_than_struct_with_name<T>(lhs, rhs);
+    return greater_than_struct_with_name(lhs, rhs);
   }
 };
 
@@ -74,7 +74,7 @@
   if (lhs.id != rhs.second) {
     return lhs.id < rhs.second;
   }
-  return lhs.name.compare(0, lhs.name.size(), rhs.first.data(), rhs.first.size()) < 0;
+  return lhs.name < rhs.first;
 }
 
 template <typename T, typename Func, typename Elements>
@@ -90,14 +90,16 @@
   StringPiece product;
 };
 
-template <typename T>
-bool lt_config_key_ref(const T& lhs, const ConfigKey& rhs) {
-  int cmp = lhs->config.compare(*rhs.config);
-  if (cmp == 0) {
-    cmp = StringPiece(lhs->product).compare(rhs.product);
+struct lt_config_key_ref {
+  template <typename T>
+  bool operator()(const T& lhs, const ConfigKey& rhs) const noexcept {
+    int cmp = lhs->config.compare(*rhs.config);
+    if (cmp == 0) {
+      cmp = lhs->product.compare(rhs.product);
+    }
+    return cmp < 0;
   }
-  return cmp < 0;
-}
+};
 
 }  // namespace
 
@@ -159,10 +161,10 @@
 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
                                               android::StringPiece product) {
   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
-                               lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
+                               lt_config_key_ref());
   if (iter != values.end()) {
     ResourceConfigValue* value = iter->get();
-    if (value->config == config && StringPiece(value->product) == product) {
+    if (value->config == config && value->product == product) {
       return value;
     }
   }
@@ -172,10 +174,10 @@
 const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config,
                                                     android::StringPiece product) const {
   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
-                               lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
+                               lt_config_key_ref());
   if (iter != values.end()) {
     ResourceConfigValue* value = iter->get();
-    if (value->config == config && StringPiece(value->product) == product) {
+    if (value->config == config && value->product == product) {
       return value;
     }
   }
@@ -185,10 +187,10 @@
 ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
                                                       StringPiece product) {
   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
-                               lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
+                               lt_config_key_ref());
   if (iter != values.end()) {
     ResourceConfigValue* value = iter->get();
-    if (value->config == config && StringPiece(value->product) == product) {
+    if (value->config == config && value->product == product) {
       return value;
     }
   }
@@ -199,36 +201,21 @@
 
 std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
   std::vector<ResourceConfigValue*> results;
-
-  auto iter = values.begin();
+  auto iter =
+      std::lower_bound(values.begin(), values.end(), ConfigKey{&config, ""}, lt_config_key_ref());
   for (; iter != values.end(); ++iter) {
     ResourceConfigValue* value = iter->get();
-    if (value->config == config) {
-      results.push_back(value);
-      ++iter;
+    if (value->config != config) {
       break;
     }
-  }
-
-  for (; iter != values.end(); ++iter) {
-    ResourceConfigValue* value = iter->get();
-    if (value->config == config) {
-      results.push_back(value);
-    }
+    results.push_back(value);
   }
   return results;
 }
 
 bool ResourceEntry::HasDefaultValue() const {
-  const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
-
   // The default config should be at the top of the list, since the list is sorted.
-  for (auto& config_value : values) {
-    if (config_value->config == default_config) {
-      return true;
-    }
-  }
-  return false;
+  return !values.empty() && values.front()->config == ConfigDescription::DefaultConfig();
 }
 
 ResourceTable::CollisionResult ResourceTable::ResolveFlagCollision(FlagStatus existing,
@@ -364,14 +351,14 @@
     if (found) {
       return &*it;
     }
-    return &*el.insert(it, std::forward<T>(value));
+    return &*el.insert(it, std::move(value));
   }
 };
 
 struct PackageViewComparer {
   bool operator()(const ResourceTablePackageView& lhs, const ResourceTablePackageView& rhs) {
     return less_than_struct_with_name_and_id<ResourceTablePackageView, uint8_t>(
-        lhs, std::make_pair(rhs.name, rhs.id));
+        lhs, std::tie(rhs.name, rhs.id));
   }
 };
 
@@ -384,7 +371,7 @@
 struct EntryViewComparer {
   bool operator()(const ResourceTableEntryView& lhs, const ResourceTableEntryView& rhs) {
     return less_than_struct_with_name_and_id<ResourceTableEntryView, uint16_t>(
-        lhs, std::make_pair(rhs.name, rhs.id));
+        lhs, std::tie(rhs.name, rhs.id));
   }
 };
 
@@ -429,10 +416,10 @@
 const ResourceConfigValue* ResourceTableEntryView::FindValue(const ConfigDescription& config,
                                                              android::StringPiece product) const {
   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
-                               lt_config_key_ref<const ResourceConfigValue*>);
+                               lt_config_key_ref());
   if (iter != values.end()) {
     const ResourceConfigValue* value = *iter;
-    if (value->config == config && StringPiece(value->product) == product) {
+    if (value->config == config && value->product == product) {
       return value;
     }
   }
@@ -605,22 +592,25 @@
     if (!config_value->value) {
       // Resource does not exist, add it now.
       config_value->value = std::move(res.value);
-      config_value->flag_status = res.flag_status;
     } else {
       // When validation is enabled, ensure that a resource cannot have multiple values defined for
       // the same configuration unless protected by flags.
-      auto result = validate ? ResolveFlagCollision(config_value->flag_status, res.flag_status)
-                             : CollisionResult::kKeepBoth;
+      auto result =
+          validate ? ResolveFlagCollision(config_value->value->GetFlagStatus(), res.flag_status)
+                   : CollisionResult::kKeepBoth;
       if (result == CollisionResult::kConflict) {
         result = ResolveValueCollision(config_value->value.get(), res.value.get());
       }
       switch (result) {
-        case CollisionResult::kKeepBoth:
+        case CollisionResult::kKeepBoth: {
           // Insert the value ignoring for duplicate configurations
-          entry->values.push_back(util::make_unique<ResourceConfigValue>(res.config, res.product));
-          entry->values.back()->value = std::move(res.value);
-          entry->values.back()->flag_status = res.flag_status;
+          auto it = entry->values.insert(
+              std::lower_bound(entry->values.begin(), entry->values.end(),
+                               ConfigKey{&res.config, res.product}, lt_config_key_ref()),
+              util::make_unique<ResourceConfigValue>(res.config, res.product));
+          (*it)->value = std::move(res.value);
           break;
+        }
 
         case CollisionResult::kTakeNew:
           // Take the incoming value.
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 4f76e7d..cba6b70 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -104,8 +104,6 @@
   // The actual Value.
   std::unique_ptr<Value> value;
 
-  FlagStatus flag_status = FlagStatus::NoFlag;
-
   ResourceConfigValue(const android::ConfigDescription& config, android::StringPiece product)
       : config(config), product(product) {
   }
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 166b01b..b75e87c 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -971,6 +971,16 @@
   *out << "(array) [" << util::Joiner(elements, ", ") << "]";
 }
 
+void Array::RemoveFlagDisabledElements() {
+  const auto end_iter = elements.end();
+  const auto remove_iter = std::stable_partition(
+      elements.begin(), end_iter, [](const std::unique_ptr<Item>& item) -> bool {
+        return item->GetFlagStatus() != FlagStatus::Disabled;
+      });
+
+  elements.erase(remove_iter, end_iter);
+}
+
 bool Plural::Equals(const Value* value) const {
   const Plural* other = ValueCast<Plural>(value);
   if (!other) {
@@ -1092,6 +1102,7 @@
 std::unique_ptr<T> CopyValueFields(std::unique_ptr<T> new_value, const T* value) {
   new_value->SetSource(value->GetSource());
   new_value->SetComment(value->GetComment());
+  new_value->SetFlagStatus(value->GetFlagStatus());
   return new_value;
 }
 
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 5192c2b..a1b1839 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -65,6 +65,14 @@
     return translatable_;
   }
 
+  void SetFlagStatus(FlagStatus val) {
+    flag_status_ = val;
+  }
+
+  FlagStatus GetFlagStatus() const {
+    return flag_status_;
+  }
+
   // Returns the source where this value was defined.
   const android::Source& GetSource() const {
     return source_;
@@ -109,6 +117,10 @@
   // of brevity and readability. Default implementation just calls Print().
   virtual void PrettyPrint(text::Printer* printer) const;
 
+  // Removes any part of the value that is beind a disabled flag.
+  virtual void RemoveFlagDisabledElements() {
+  }
+
   friend std::ostream& operator<<(std::ostream& out, const Value& value);
 
  protected:
@@ -116,6 +128,7 @@
   std::string comment_;
   bool weak_ = false;
   bool translatable_ = true;
+  FlagStatus flag_status_ = FlagStatus::NoFlag;
 
  private:
   virtual Value* TransformValueImpl(ValueTransformer& transformer) const = 0;
@@ -346,6 +359,7 @@
 
   bool Equals(const Value* value) const override;
   void Print(std::ostream* out) const override;
+  void RemoveFlagDisabledElements() override;
 };
 
 struct Plural : public TransformableValue<Plural, BaseValue<Plural>> {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 2ecc82a..5c64089 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -246,7 +246,7 @@
 message ConfigValue {
   Configuration config = 1;
   Value value = 2;
-  uint32 flag_status = 3;
+  reserved 3;
 }
 
 // The generic meta-data for every value in a resource table.
@@ -280,6 +280,9 @@
     Id id = 6;
     Primitive prim = 7;
   }
+
+  // The status of the flag the value is behind if any
+  uint32 flag_status = 8;
 }
 
 // A CompoundValue is an abstract type. It represents a value that is a made of other values.
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index c132792..6c3eae1 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -244,6 +244,10 @@
     return verbose_;
   }
 
+  void SetVerbose(bool verbose) {
+    verbose_ = verbose;
+  }
+
   int GetMinSdkVersion() override {
     return min_sdk_;
   }
@@ -388,6 +392,8 @@
   }
 
   Context context;
+  context.SetVerbose(verbose_);
+
   StringPiece path = args[0];
   unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
   if (apk == nullptr) {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 56f5288..498e431 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -306,6 +306,7 @@
   OutputFormat output_format = OutputFormat::kApk;
   std::unordered_set<std::string> extensions_to_not_compress;
   std::optional<std::regex> regex_to_not_compress;
+  FeatureFlagValues feature_flag_values;
 };
 
 // A sampling of public framework resource IDs.
@@ -672,6 +673,13 @@
               }
             }
 
+            FeatureFlagsFilterOptions flags_filter_options;
+            flags_filter_options.flags_must_be_readonly = true;
+            FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
+            if (!flags_filter.Consume(context_, doc.get())) {
+              return 1;
+            }
+
             error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
                                  false /*utf16*/, options_.output_format, archive_writer);
           }
@@ -1878,7 +1886,7 @@
       for (auto& type : package->types) {
         for (auto& entry : type->entries) {
           for (auto& config_value : entry->values) {
-            if (config_value->flag_status == FlagStatus::Disabled) {
+            if (config_value->value->GetFlagStatus() == FlagStatus::Disabled) {
               config_value->value->Accept(&visitor);
             }
           }
@@ -1926,6 +1934,7 @@
         static_cast<bool>(options_.generate_proguard_rules_path);
     file_flattener_options.output_format = options_.output_format;
     file_flattener_options.do_not_fail_on_missing_resources = options_.merge_only;
+    file_flattener_options.feature_flag_values = options_.feature_flag_values;
 
     ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
     if (!file_flattener.Flatten(table, writer)) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aaab315..55f5e56 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -534,8 +534,6 @@
           return false;
         }
 
-        config_value->flag_status = (FlagStatus)pb_config_value.flag_status();
-
         config_value->value = DeserializeValueFromPb(pb_config_value.value(), src_pool, config,
                                                      &out_table->string_pool, files, out_error);
         if (config_value->value == nullptr) {
@@ -877,11 +875,12 @@
   return value;
 }
 
-std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
-                                            const android::ResStringPool& src_pool,
-                                            const ConfigDescription& config,
-                                            android::StringPool* value_pool,
-                                            io::IFileCollection* files, std::string* out_error) {
+std::unique_ptr<Item> DeserializeItemFromPbInternal(const pb::Item& pb_item,
+                                                    const android::ResStringPool& src_pool,
+                                                    const ConfigDescription& config,
+                                                    android::StringPool* value_pool,
+                                                    io::IFileCollection* files,
+                                                    std::string* out_error) {
   switch (pb_item.value_case()) {
     case pb::Item::kRef: {
       const pb::Reference& pb_ref = pb_item.ref();
@@ -1010,6 +1009,19 @@
   return {};
 }
 
+std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
+                                            const android::ResStringPool& src_pool,
+                                            const ConfigDescription& config,
+                                            android::StringPool* value_pool,
+                                            io::IFileCollection* files, std::string* out_error) {
+  auto item =
+      DeserializeItemFromPbInternal(pb_item, src_pool, config, value_pool, files, out_error);
+  if (item) {
+    item->SetFlagStatus((FlagStatus)pb_item.flag_status());
+  }
+  return item;
+}
+
 std::unique_ptr<xml::XmlResource> DeserializeXmlResourceFromPb(const pb::XmlNode& pb_node,
                                                                std::string* out_error) {
   if (!pb_node.has_element()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index c1e15bc..5772b3b 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -426,7 +426,6 @@
           pb_config_value->mutable_config()->set_product(config_value->product);
           SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
                              source_pool.get());
-          pb_config_value->set_flag_status((uint32_t)config_value->flag_status);
         }
       }
     }
@@ -720,6 +719,9 @@
   if (src_pool != nullptr) {
     SerializeSourceToPb(value.GetSource(), src_pool, out_value->mutable_source());
   }
+  if (out_value->has_item()) {
+    out_value->mutable_item()->set_flag_status((uint32_t)value.GetFlagStatus());
+  }
 }
 
 void SerializeItemToPb(const Item& item, pb::Item* out_item) {
@@ -727,6 +729,7 @@
   ValueSerializer serializer(&value, nullptr);
   item.Accept(&serializer);
   out_item->MergeFrom(value.item());
+  out_item->set_flag_status((uint32_t)item.GetFlagStatus());
 }
 
 void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file) {
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
index 5932271..c456e5c 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -28,12 +28,16 @@
     srcs: [
         "res/values/bools.xml",
         "res/values/bools2.xml",
+        "res/values/ints.xml",
         "res/values/strings.xml",
+        "res/layout/layout1.xml",
     ],
     out: [
         "values_bools.arsc.flat",
         "values_bools2.arsc.flat",
+        "values_ints.arsc.flat",
         "values_strings.arsc.flat",
+        "layout_layout1.xml.flat",
     ],
     cmd: "$(location aapt2) compile $(in) -o $(genDir) " +
         "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
@@ -50,7 +54,10 @@
     out: [
         "resapp.apk",
     ],
-    cmd: "$(location aapt2) link -o $(out) --manifest $(in)",
+    cmd: "$(location aapt2) link -o $(out) --manifest $(in) " +
+        "-I $(location :current_android_jar) " +
+        "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
+    tool_files: [":current_android_jar"],
 }
 
 genrule {
@@ -64,7 +71,10 @@
     out: [
         "resource-flagging-java/com/android/intenal/flaggedresources/R.java",
     ],
-    cmd: "$(location aapt2) link -o $(genDir)/resapp.apk --java $(genDir)/resource-flagging-java --manifest $(in)",
+    cmd: "$(location aapt2) link -o $(genDir)/resapp.apk --java $(genDir)/resource-flagging-java --manifest $(in) " +
+        "-I $(location :current_android_jar) " +
+        "--feature-flags test.package.falseFlag:ro=false,test.package.trueFlag:ro=true",
+    tool_files: [":current_android_jar"],
 }
 
 java_genrule {
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout1.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout1.xml
new file mode 100644
index 0000000..8b9ce13
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/layout/layout1.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView android:id="@+id/text1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+    <TextView android:id="@+id/disabled_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:featureFlag="test.package.falseFlag" />
+    <TextView android:id="@+id/text2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:featureFlag="test.package.trueFlag" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
index 3e094fb..1ed0c8a 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <bool name="res1">true</bool>
-    <bool name="res1" android:featureFlag="test.package.falseFlag">false</bool>
+    <bool name="bool1">true</bool>
+    <bool name="bool1" android:featureFlag="test.package.falseFlag">false</bool>
 
-    <bool name="res2">false</bool>
-    <bool name="res2" android:featureFlag="test.package.trueFlag">true</bool>
+    <bool name="bool2">false</bool>
+    <bool name="bool2" android:featureFlag="test.package.trueFlag">true</bool>
 
-    <bool name="res3">false</bool>
+    <bool name="bool3">false</bool>
 
-    <bool name="res4" android:featureFlag="test.package.falseFlag">true</bool>
+    <bool name="bool4" android:featureFlag="test.package.falseFlag">true</bool>
 </resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
index e7563aa..248c45f 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools2.xml
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <bool name="res3" android:featureFlag="test.package.trueFlag">true</bool>
+    <bool name="bool3" android:featureFlag="test.package.trueFlag">true</bool>
 </resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
new file mode 100644
index 0000000..26a5c40
--- /dev/null
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/ints.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <integer-array name="intarr1">
+        <item>1</item>
+        <item>2</item>
+        <item android:featureFlag="test.package.falseFlag">666</item>
+        <item>3</item>
+    </integer-array>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
index 5c0fca1..3cbb928 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/strings.xml
@@ -3,4 +3,11 @@
     <string name="str">plain string</string>
 
     <string name="str1" android:featureFlag="test.package.falseFlag">DONTFIND</string>
+
+    <string-array name="strarr1">
+        <item>one</item>
+        <item>two</item>
+        <item android:featureFlag="test.package.falseFlag">remove</item>
+        <item android:featureFlag="test.package.trueFlag">three</item>
+    </string-array>
 </resources>
\ No newline at end of file
diff --git a/tools/aapt2/link/FeatureFlagsFilter.cpp b/tools/aapt2/link/FeatureFlagsFilter.cpp
index 9d40db5..4e7c1b4 100644
--- a/tools/aapt2/link/FeatureFlagsFilter.cpp
+++ b/tools/aapt2/link/FeatureFlagsFilter.cpp
@@ -65,6 +65,13 @@
 
       if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) {
         if (it->second.enabled.has_value()) {
+          if (options_.flags_must_be_readonly && !it->second.read_only) {
+            diagnostics_->Error(android::DiagMessage(node->line_number)
+                                << "attribute 'android:featureFlag' has flag '" << flag_name
+                                << "' which must be readonly but is not");
+            has_error_ = true;
+            return false;
+          }
           if (options_.remove_disabled_elements) {
             // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
             return *it->second.enabled == negated;
diff --git a/tools/aapt2/link/FeatureFlagsFilter.h b/tools/aapt2/link/FeatureFlagsFilter.h
index 1d342a7..61e4c80 100644
--- a/tools/aapt2/link/FeatureFlagsFilter.h
+++ b/tools/aapt2/link/FeatureFlagsFilter.h
@@ -38,6 +38,10 @@
   // If true, `Consume()` will return false (error) if a flag was found whose value in
   // `feature_flag_values` is not defined (std::nullopt).
   bool flags_must_have_value = true;
+
+  // If true, `Consume()` will return false (error) if a flag was found whose value in
+  // `feature_flag_values` is not readonly.
+  bool flags_must_be_readonly = false;
 };
 
 // Looks for the `android:featureFlag` attribute in each XML element, validates the flag names and
diff --git a/tools/aapt2/link/FlagDisabledResourceRemover.cpp b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
index e3289e2..3ac1762 100644
--- a/tools/aapt2/link/FlagDisabledResourceRemover.cpp
+++ b/tools/aapt2/link/FlagDisabledResourceRemover.cpp
@@ -32,12 +32,17 @@
   const auto remove_iter =
       std::stable_partition(entry->values.begin(), end_iter,
                             [](const std::unique_ptr<ResourceConfigValue>& value) -> bool {
-                              return value->flag_status != FlagStatus::Disabled;
+                              return value->value->GetFlagStatus() != FlagStatus::Disabled;
                             });
 
   bool keep = remove_iter != entry->values.begin();
 
   entry->values.erase(remove_iter, end_iter);
+
+  for (auto& value : entry->values) {
+    value->value->RemoveFlagDisabledElements();
+  }
+
   return keep;
 }
 
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index c901b58..3db37c2 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -84,7 +84,7 @@
   std::string output;
   DumpChunksToString(loaded_apk.get(), &output);
 
-  ASSERT_EQ(output.find("res4"), std::string::npos);
+  ASSERT_EQ(output.find("bool4"), std::string::npos);
   ASSERT_EQ(output.find("str1"), std::string::npos);
 }
 
@@ -94,7 +94,7 @@
   std::string r_contents;
   ::android::base::ReadFileToString(r_path, &r_contents);
 
-  ASSERT_NE(r_contents.find("public static final int res4"), std::string::npos);
+  ASSERT_NE(r_contents.find("public static final int bool4"), std::string::npos);
   ASSERT_NE(r_contents.find("public static final int str1"), std::string::npos);
 }
 
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1942fc11..37a039e 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -212,8 +212,8 @@
     collision_result =
         ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
   } else {
-    collision_result = ResourceTable::ResolveFlagCollision(dst_config_value->flag_status,
-                                                           src_config_value->flag_status);
+    collision_result =
+        ResourceTable::ResolveFlagCollision(dst_value->GetFlagStatus(), src_value->GetFlagStatus());
     if (collision_result == CollisionResult::kConflict) {
       collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
     }
@@ -295,7 +295,6 @@
         } else {
           dst_config_value =
               dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product);
-          dst_config_value->flag_status = src_config_value->flag_status;
         }
 
         // Continue if we're taking the new resource.
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index 682adbc..ea77b8d 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -118,7 +118,6 @@
 
 java_test_host {
     name: "hoststubgentest",
-    // main_class: "com.android.hoststubgen.Main",
     srcs: ["test/**/*.kt"],
     static_libs: [
         "hoststubgen",
@@ -143,8 +142,7 @@
     // "--policy-override-file $(location framework-policy-override.txt) " +
     "@$(location :hoststubgen-standard-options) " +
 
-    "--out-stub-jar $(location host_stub.jar) " +
-    "--out-impl-jar $(location host_impl.jar) " +
+    "--out-jar $(location host.jar) " +
 
     // "--keep-all-classes " + // Used it for an experiment. See KeepAllClassesFilter.
     "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " +
@@ -159,10 +157,8 @@
     srcs: [
         ":hoststubgen-standard-options",
     ],
-    // Create two jar files.
     out: [
-        "host_stub.jar",
-        "host_impl.jar",
+        "host.jar",
 
         // Following files are created just as FYI.
         "hoststubgen_keep_all.txt",
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
deleted file mode 100644
index cabdfe0..0000000
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestStub.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hosttest.annotation;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * Mark a class, field or a method as "Stub", meaning tests can see the APIs.
- * When applied to a class, it will _not_ affect the visibility of its members. They need to be
- * individually marked.
- *
- * <p>In order to expose a class and all its members, use {@link HostSideTestWholeClassStub}
- * instead.
- *
- * @hide
- */
-@Target({TYPE, FIELD, METHOD, CONSTRUCTOR})
-@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestStub {
-}
diff --git a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java b/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
deleted file mode 100644
index 1824f6f..0000000
--- a/tools/hoststubgen/hoststubgen/annotations-src/android/hosttest/annotation/HostSideTestWholeClassStub.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hosttest.annotation;
-
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * THIS ANNOTATION IS EXPERIMENTAL. REACH OUT TO g/ravenwood BEFORE USING IT, OR YOU HAVE ANY
- * QUESTIONS ABOUT IT.
- *
- * Same as {@link HostSideTestStub} but it'll change the visibility of all its members too.
- *
- * @hide
- */
-@Target({TYPE})
-@Retention(RetentionPolicy.CLASS)
-public @interface HostSideTestWholeClassStub {
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java
deleted file mode 100644
index 2cc500f..0000000
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInImpl.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.hoststubgen.hosthelper;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation injected to all classes/methods/fields that are kept in the "impl" jar.
- */
-@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HostStubGenKeptInImpl {
-    String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenKeptInImpl.class);
-    String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java
deleted file mode 100644
index 12b9875..0000000
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenKeptInStub.java
+++ /dev/null
@@ -1,38 +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.hosthelper;
-
-import static java.lang.annotation.ElementType.CONSTRUCTOR;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.TYPE;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation injected to all classes/methods/fields that are kept in the "stub" jar.
- *
- * All items in the stub jar are automatically kept in the impl jar as well, so
- * the items with this annotation will all have {@link HostStubGenKeptInImpl} too.
- */
-@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface HostStubGenKeptInStub {
-    String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenKeptInStub.class);
-    String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
-}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
index cb50404..b017103 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsIgnore.java
@@ -23,8 +23,6 @@
 
 /**
  * Annotation injected to all methods processed as "ignore".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
  */
 @Target({METHOD})
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
new file mode 100644
index 0000000..18ef1ba
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsKeep.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.hosthelper;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation injected to all classes/methods/fields that are kept in the processes jar.
+ */
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface HostStubGenProcessedAsKeep {
+    String CLASS_INTERNAL_NAME = HostTestUtils.getInternalName(HostStubGenProcessedAsKeep.class);
+    String CLASS_DESCRIPTOR = "L" + CLASS_INTERNAL_NAME + ";";
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
index cfa4896..99e38c0 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsSubstitute.java
@@ -26,8 +26,6 @@
 
 /**
  * Annotation injected to all methods that are processed as "substitute".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
  */
 @Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
index 0d2da11..4933cf8 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostStubGenProcessedAsThrow.java
@@ -23,8 +23,6 @@
 
 /**
  * Annotation injected to all methods that are processed as "throw".
- *
- * (This annotation is only added in the impl jar, but not the stub jar)
  */
 @Target({METHOD})
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
index 60eb47ee..78fd8f7 100644
--- a/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
+++ b/tools/hoststubgen/hoststubgen/helper-runtime-src/com/android/hoststubgen/hosthelper/HostTestUtils.java
@@ -16,12 +16,8 @@
 package com.android.hoststubgen.hosthelper;
 
 import java.io.PrintStream;
-import java.lang.StackWalker.Option;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.HashMap;
-
-import javax.annotation.concurrent.GuardedBy;
 
 /**
  * Utilities used in the host side test environment.
@@ -101,68 +97,6 @@
                 + methodName + methodDescriptor);
     }
 
-    private static final StackWalker sStackWalker =
-            StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
-
-    /**
-     * Return a {@link StackWalker} that supports {@link StackWalker#getCallerClass()}.
-     */
-    public static StackWalker getStackWalker() {
-        return sStackWalker;
-    }
-
-    /**
-     * Cache used by {@link #isClassAllowedToCallNonStubMethods}.
-     */
-    @GuardedBy("sAllowedClasses")
-    private static final HashMap<Class, Boolean> sAllowedClasses = new HashMap();
-
-    /**
-     * Return true if a given class is allowed to access non-stub methods -- that is, if the class
-     * is in the hoststubgen generated JARs. (not in the test jar.)
-     */
-    private static boolean isClassAllowedToCallNonStubMethods(Class<?> clazz) {
-        synchronized (sAllowedClasses) {
-            var cached = sAllowedClasses.get(clazz);
-            if (cached != null) {
-                return cached;
-            }
-        }
-        // All processed classes have this annotation.
-        var allowed = clazz.getAnnotation(HostStubGenKeptInImpl.class) != null;
-
-        // Java classes should be able to access any methods. (via callbacks, etc.)
-        if (!allowed) {
-            if (clazz.getPackageName().startsWith("java.")
-                    || clazz.getPackageName().startsWith("javax.")) {
-                allowed = true;
-            }
-        }
-        synchronized (sAllowedClasses) {
-            sAllowedClasses.put(clazz, allowed);
-        }
-        return allowed;
-    }
-
-    /**
-     * Called when non-stub methods are called. We do a host-unsupported method direct call check
-     * in here.
-     */
-    public static void onNonStubMethodCalled(
-            String methodClass,
-            String methodName,
-            String methodDescriptor,
-            Class<?> callerClass) {
-        if (SKIP_NON_STUB_METHOD_CHECK) {
-            return;
-        }
-        if (isClassAllowedToCallNonStubMethods(callerClass)) {
-            return; // Generated class is allowed to call framework class.
-        }
-        logPrintStream.println("! " + methodClass + "." + methodName + methodDescriptor
-                + " called by " + callerClass.getCanonicalName());
-    }
-
     /**
      * Called when any top level class (not nested classes) in the impl jar is loaded.
      *
diff --git a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
index c371b5d..e72c9a4 100644
--- a/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
+++ b/tools/hoststubgen/hoststubgen/hoststubgen-standard-options.txt
@@ -3,8 +3,6 @@
 --debug
 
 # Uncomment below lines to enable each feature.
---enable-non-stub-method-check
-# --no-non-stub-method-check
 
 #--default-method-call-hook
 #    com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
@@ -13,15 +11,10 @@
 
 # Standard annotations.
 # Note, each line is a single argument, so we need newlines after each `--xxx-annotation`.
---stub-annotation
-    android.hosttest.annotation.HostSideTestStub
 
 --keep-annotation
     android.hosttest.annotation.HostSideTestKeep
 
---stub-class-annotation
-    android.hosttest.annotation.HostSideTestWholeClassStub
-
 --keep-class-annotation
     android.hosttest.annotation.HostSideTestWholeClassKeep
 
diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
index d97dd7c..5f0368a 100755
--- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
+++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh
@@ -43,9 +43,8 @@
 
 cleanup_temp
 
-JAR=hoststubgen-test-tiny-framework.jar
-STUB=$TEMP/stub.jar
-IMPL=$TEMP/impl.jar
+INJAR=hoststubgen-test-tiny-framework.jar
+OUTJAR=$TEMP/host.jar
 
 ANNOTATION_FILTER=$TEMP/annotation-filter.txt
 
@@ -81,27 +80,18 @@
     cat $ANNOTATION_FILTER
   fi
 
-  local stub_arg=""
-  local impl_arg=""
+  local out_arg=""
 
-  if [[ "$STUB" != "" ]] ; then
-    stub_arg="--out-stub-jar $STUB"
-  fi
-  if [[ "$IMPL" != "" ]] ; then
-    impl_arg="--out-impl-jar $IMPL"
+  if [[ "$OUTJAR" != "" ]] ; then
+    out_arg="--out-jar $OUTJAR"
   fi
 
   hoststubgen \
       --debug \
-      --in-jar $JAR \
-      $stub_arg \
-      $impl_arg \
-      --stub-annotation \
-          android.hosttest.annotation.HostSideTestStub \
+      --in-jar $INJAR \
+      $out_arg \
       --keep-annotation \
           android.hosttest.annotation.HostSideTestKeep \
-      --stub-class-annotation \
-          android.hosttest.annotation.HostSideTestWholeClassStub \
       --keep-class-annotation \
           android.hosttest.annotation.HostSideTestWholeClassKeep \
       --throw-annotation \
@@ -213,9 +203,9 @@
 "
 
 run_hoststubgen_for_failure "One specific class disallowed" \
-    "TinyFrameworkClassAnnotations is not allowed to have Ravenwood annotations" \
+    "TinyFrameworkAnnotations is not allowed to have Ravenwood annotations" \
     "
-!com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+!com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
 * # All other classes allowed
 "
 
@@ -225,11 +215,7 @@
 * # All other classes allowed
 "
 
-STUB="" run_hoststubgen_for_success "No stub generation" ""
-
-IMPL="" run_hoststubgen_for_success "No impl generation" ""
-
-STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" ""
+OUTJAR="" run_hoststubgen_for_success "No output generation" ""
 
 EXTRA_ARGS="--in-jar abc" run_hoststubgen_for_failure "Duplicate arg" \
     "Duplicate or conflicting argument found: --in-jar" \
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
index 910bf59..f59e143 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/Exceptions.kt
@@ -15,6 +15,8 @@
  */
 package com.android.hoststubgen
 
+import java.io.File
+
 /**
  * We will not print the stack trace for exceptions implementing it.
  */
@@ -49,4 +51,22 @@
 /**
  * We use this for general "user" errors.
  */
-class HostStubGenUserErrorException(message: String) : Exception(message), UserErrorException
+class GeneralUserErrorException(message: String) : Exception(message), UserErrorException
+
+/** Base exception class for invalid command line arguments. */
+open class ArgumentsException(message: String?) : Exception(message), UserErrorException
+
+/** Thrown when the same annotation is used with different annotation arguments. */
+class DuplicateAnnotationException(annotationName: String?) :
+    ArgumentsException("Duplicate annotation specified: '$annotationName'")
+
+/** Thrown when an input file does not exist. */
+class InputFileNotFoundException(filename: String) :
+    ArgumentsException("File '$filename' not found")
+
+fun String.ensureFileExists(): String {
+    if (!File(this).exists()) {
+        throw InputFileNotFoundException(this)
+    }
+    return this
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 36bfbef..0f38fe7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -22,21 +22,17 @@
 import com.android.hoststubgen.filters.ConstantFilter
 import com.android.hoststubgen.filters.DefaultHookInjectingFilter
 import com.android.hoststubgen.filters.FilterPolicy
+import com.android.hoststubgen.filters.FilterRemapper
 import com.android.hoststubgen.filters.ImplicitOutputFilter
+import com.android.hoststubgen.filters.NativeFilter
 import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.filters.StubIntersectingFilter
 import com.android.hoststubgen.filters.createFilterFromTextPolicyFile
 import com.android.hoststubgen.filters.printAsTextPolicy
 import com.android.hoststubgen.utils.ClassFilter
 import com.android.hoststubgen.visitors.BaseAdapter
 import com.android.hoststubgen.visitors.PackageRedirectRemapper
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.ClassWriter
-import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.commons.Remapper
-import org.objectweb.asm.util.CheckClassAdapter
 import java.io.BufferedInputStream
+import java.io.BufferedOutputStream
 import java.io.FileOutputStream
 import java.io.InputStream
 import java.io.OutputStream
@@ -44,6 +40,12 @@
 import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
 import java.util.zip.ZipOutputStream
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.ClassVisitor
+import org.objectweb.asm.ClassWriter
+import org.objectweb.asm.commons.ClassRemapper
+import org.objectweb.asm.commons.Remapper
+import org.objectweb.asm.util.CheckClassAdapter
 
 /**
  * Actual main class.
@@ -74,21 +76,22 @@
         }
 
         // Build the filters.
-        val (filter, policyFileRemapper) = buildFilter(errors, allClasses, options)
+        val filter = buildFilter(errors, allClasses, options)
+
+        val filterRemapper = FilterRemapper(filter)
 
         // Transform the jar.
         convert(
-                options.inJar.get,
-                options.outStubJar.get,
-                options.outImplJar.get,
-                filter,
-                options.enableClassChecker.get,
-                allClasses,
-                errors,
-                stats,
-                policyFileRemapper,
-                options.numShards.get,
-                options.shard.get,
+            options.inJar.get,
+            options.outJar.get,
+            filter,
+            options.enableClassChecker.get,
+            allClasses,
+            errors,
+            stats,
+            filterRemapper,
+            options.numShards.get,
+            options.shard.get,
         )
 
         // Dump statistics, if specified.
@@ -113,10 +116,10 @@
      * jars, and "how". (e.g. with substitution?)
      */
     private fun buildFilter(
-            errors: HostStubGenErrors,
-            allClasses: ClassNodes,
-            options: HostStubGenOptions,
-            ): Pair<OutputFilter, Remapper?> {
+        errors: HostStubGenErrors,
+        allClasses: ClassNodes,
+        options: HostStubGenOptions,
+    ): OutputFilter {
         // We build a "chain" of multiple filters here.
         //
         // The filters are build in from "inside", meaning the first filter created here is
@@ -130,6 +133,9 @@
         // The first filter is for the default policy from the command line options.
         var filter: OutputFilter = ConstantFilter(options.defaultPolicy.get, "default-by-options")
 
+        // Next, we build a filter that preserves all native methods by default
+        filter = NativeFilter(allClasses, filter)
+
         // Next, we need a filter that resolves "class-wide" policies.
         // This is used when a member (methods, fields, nested classes) don't get any polices
         // from upper filters. e.g. when a method has no annotations, then this filter will apply
@@ -155,9 +161,7 @@
         filter = AnnotationBasedFilter(
             errors,
             allClasses,
-            options.stubAnnotations,
             options.keepAnnotations,
-            options.stubClassAnnotations,
             options.keepClassAnnotations,
             options.throwAnnotations,
             options.removeAnnotations,
@@ -169,60 +173,34 @@
             filter,
         )
 
-        var policyFileRemapper: Remapper? = null
-
         // Next, "text based" filter, which allows to override polices without touching
         // the target code.
         options.policyOverrideFile.ifSet {
-            val (f, p) = createFilterFromTextPolicyFile(it, allClasses, filter)
-            filter = f
-            policyFileRemapper = p
-        }
-
-        // If `--intersect-stub-jar` is provided, load from these jar files too.
-        // We use this to restrict stub APIs to public/system/test APIs,
-        // by intersecting with a stub jar file created by metalava.
-        if (options.intersectStubJars.size > 0) {
-            val intersectingJars = loadIntersectingJars(options.intersectStubJars)
-
-            filter = StubIntersectingFilter(errors, intersectingJars, filter)
+            filter = createFilterFromTextPolicyFile(it, allClasses, filter)
         }
 
         // Apply the implicit filter.
         filter = ImplicitOutputFilter(errors, allClasses, filter)
 
-        return Pair(filter, policyFileRemapper)
-    }
-
-    /**
-     * Load jar files specified with "--intersect-stub-jar".
-     */
-    private fun loadIntersectingJars(filenames: Set<String>): Map<String, ClassNodes> {
-        val intersectingJars = mutableMapOf<String, ClassNodes>()
-
-        filenames.forEach { filename ->
-            intersectingJars[filename] = ClassNodes.loadClassStructures(filename)
-        }
-        return intersectingJars
+        return filter
     }
 
     /**
      * Convert a JAR file into "stub" and "impl" JAR files.
      */
     private fun convert(
-            inJar: String,
-            outStubJar: String?,
-            outImplJar: String?,
-            filter: OutputFilter,
-            enableChecker: Boolean,
-            classes: ClassNodes,
-            errors: HostStubGenErrors,
-            stats: HostStubGenStats,
-            remapper: Remapper?,
-            numShards: Int,
-            shard: Int,
-            ) {
-        log.i("Converting %s into [stub: %s, impl: %s] ...", inJar, outStubJar, outImplJar)
+        inJar: String,
+        outJar: String?,
+        filter: OutputFilter,
+        enableChecker: Boolean,
+        classes: ClassNodes,
+        errors: HostStubGenErrors,
+        stats: HostStubGenStats,
+        remapper: Remapper?,
+        numShards: Int,
+        shard: Int
+    ) {
+        log.i("Converting %s into %s ...", inJar, outJar)
         log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
 
         log.iTime("Transforming jar") {
@@ -240,29 +218,26 @@
                     val shardStart = numItems * shard / numShards
                     val shardNextStart = numItems * (shard + 1) / numShards
 
-                    maybeWithZipOutputStream(outStubJar) { stubOutStream ->
-                        maybeWithZipOutputStream(outImplJar) { implOutStream ->
-                            val inEntries = inZip.entries()
-                            while (inEntries.hasMoreElements()) {
-                                val entry = inEntries.nextElement()
-                                val inShard = (shardStart <= itemIndex)
-                                        && (itemIndex < shardNextStart)
-                                itemIndex++
-                                if (!inShard) {
-                                    continue
-                                }
-                                convertSingleEntry(
-                                    inZip, entry, stubOutStream, implOutStream,
-                                    filter, packageRedirector, remapper,
-                                    enableChecker, classes, errors, stats
-                                )
-                                numItemsProcessed++
+                    maybeWithZipOutputStream(outJar) { outStream ->
+                        val inEntries = inZip.entries()
+                        while (inEntries.hasMoreElements()) {
+                            val entry = inEntries.nextElement()
+                            val inShard = (shardStart <= itemIndex)
+                                    && (itemIndex < shardNextStart)
+                            itemIndex++
+                            if (!inShard) {
+                                continue
                             }
-                            log.i("Converted all entries.")
+                            convertSingleEntry(
+                                inZip, entry, outStream, filter,
+                                packageRedirector, remapper, enableChecker,
+                                classes, errors, stats
+                            )
+                            numItemsProcessed++
                         }
+                        log.i("Converted all entries.")
                     }
-                    outStubJar?.let { log.i("Created stub: $it") }
-                    outImplJar?.let { log.i("Created impl: $it") }
+                    outJar?.let { log.i("Created: $it") }
                 }
             }
             log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
@@ -273,25 +248,24 @@
         if (filename == null) {
             return block(null)
         }
-        return ZipOutputStream(FileOutputStream(filename)).use(block)
+        return ZipOutputStream(BufferedOutputStream(FileOutputStream(filename))).use(block)
     }
 
     /**
      * Convert a single ZIP entry, which may or may not be a class file.
      */
     private fun convertSingleEntry(
-            inZip: ZipFile,
-            entry: ZipEntry,
-            stubOutStream: ZipOutputStream?,
-            implOutStream: ZipOutputStream?,
-            filter: OutputFilter,
-            packageRedirector: PackageRedirectRemapper,
-            remapper: Remapper?,
-            enableChecker: Boolean,
-            classes: ClassNodes,
-            errors: HostStubGenErrors,
-            stats: HostStubGenStats,
-            ) {
+        inZip: ZipFile,
+        entry: ZipEntry,
+        outStream: ZipOutputStream?,
+        filter: OutputFilter,
+        packageRedirector: PackageRedirectRemapper,
+        remapper: Remapper?,
+        enableChecker: Boolean,
+        classes: ClassNodes,
+        errors: HostStubGenErrors,
+        stats: HostStubGenStats
+    ) {
         log.d("Entry: %s", entry.name)
         log.withIndent {
             val name = entry.name
@@ -303,8 +277,10 @@
 
             // If it's a class, convert it.
             if (name.endsWith(".class")) {
-                processSingleClass(inZip, entry, stubOutStream, implOutStream, filter,
-                        packageRedirector, remapper, enableChecker, classes, errors, stats)
+                processSingleClass(
+                    inZip, entry, outStream, filter, packageRedirector,
+                    remapper, enableChecker, classes, errors, stats
+                )
                 return
             }
 
@@ -312,17 +288,14 @@
 
             // - *.uau seems to contain hidden API information.
             // -  *_compat_config.xml is also about compat-framework.
-            if (name.endsWith(".uau") ||
-                    name.endsWith("_compat_config.xml")) {
+            if (name.endsWith(".uau") || name.endsWith("_compat_config.xml")) {
                 log.d("Not needed: %s", entry.name)
                 return
             }
 
             // Unknown type, we just copy it to both output zip files.
-            // TODO: We probably shouldn't do it for stub jar?
             log.v("Copying: %s", entry.name)
-            stubOutStream?.let { copyZipEntry(inZip, entry, it) }
-            implOutStream?.let { copyZipEntry(inZip, entry, it) }
+            outStream?.let { copyZipEntry(inZip, entry, it) }
         }
     }
 
@@ -330,17 +303,18 @@
      * Copy a single ZIP entry to the output.
      */
     private fun copyZipEntry(
-            inZip: ZipFile,
-            entry: ZipEntry,
-            out: ZipOutputStream,
-            ) {
-        BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
+        inZip: ZipFile,
+        entry: ZipEntry,
+        out: ZipOutputStream,
+    ) {
+        // TODO: It seems like copying entries this way is _very_ slow,
+        // even with out.setLevel(0). Look for other ways to do it.
+
+        inZip.getInputStream(entry).use { ins ->
             // Copy unknown entries as is to the impl out. (but not to the stub out.)
             val outEntry = ZipEntry(entry.name)
             out.putNextEntry(outEntry)
-            while (bis.available() > 0) {
-                out.write(bis.read())
-            }
+            ins.transferTo(out)
             out.closeEntry()
         }
     }
@@ -349,18 +323,17 @@
      * Convert a single class to "stub" and "impl".
      */
     private fun processSingleClass(
-            inZip: ZipFile,
-            entry: ZipEntry,
-            stubOutStream: ZipOutputStream?,
-            implOutStream: ZipOutputStream?,
-            filter: OutputFilter,
-            packageRedirector: PackageRedirectRemapper,
-            remapper: Remapper?,
-            enableChecker: Boolean,
-            classes: ClassNodes,
-            errors: HostStubGenErrors,
-            stats: HostStubGenStats,
-            ) {
+        inZip: ZipFile,
+        entry: ZipEntry,
+        outStream: ZipOutputStream?,
+        filter: OutputFilter,
+        packageRedirector: PackageRedirectRemapper,
+        remapper: Remapper?,
+        enableChecker: Boolean,
+        classes: ClassNodes,
+        errors: HostStubGenErrors,
+        stats: HostStubGenStats
+    ) {
         val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
         val classPolicy = filter.getPolicyForClass(classInternalName)
         if (classPolicy.policy == FilterPolicy.Remove) {
@@ -372,33 +345,22 @@
         remapper?.mapType(classInternalName)?.let { remappedName ->
             if (remappedName != classInternalName) {
                 log.d("Renaming class file: %s -> %s", classInternalName, remappedName)
-                newName = remappedName + ".class"
+                newName = "$remappedName.class"
             }
         }
-        // Generate stub first.
-        if (stubOutStream != null && classPolicy.policy.needsInStub) {
-            log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy)
+
+        if (outStream != null) {
+            log.v("Creating class: %s Policy: %s", classInternalName, classPolicy)
             log.withIndent {
                 BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
                     val newEntry = ZipEntry(newName)
-                    stubOutStream.putNextEntry(newEntry)
-                    convertClass(classInternalName, /*forImpl=*/false, bis,
-                            stubOutStream, filter, packageRedirector, remapper,
-                            enableChecker, classes, errors, null)
-                    stubOutStream.closeEntry()
-                }
-            }
-        }
-        if (implOutStream != null && classPolicy.policy.needsInImpl) {
-            log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy)
-            log.withIndent {
-                BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
-                    val newEntry = ZipEntry(newName)
-                    implOutStream.putNextEntry(newEntry)
-                    convertClass(classInternalName, /*forImpl=*/true, bis,
-                            implOutStream, filter, packageRedirector, remapper,
-                            enableChecker, classes, errors, stats)
-                    implOutStream.closeEntry()
+                    outStream.putNextEntry(newEntry)
+                    convertClass(
+                        classInternalName, bis,
+                        outStream, filter, packageRedirector, remapper,
+                        enableChecker, classes, errors, stats
+                    )
+                    outStream.closeEntry()
                 }
             }
         }
@@ -408,18 +370,17 @@
      * Convert a single class to either "stub" or "impl".
      */
     private fun convertClass(
-            classInternalName: String,
-            forImpl: Boolean,
-            input: InputStream,
-            out: OutputStream,
-            filter: OutputFilter,
-            packageRedirector: PackageRedirectRemapper,
-            remapper: Remapper?,
-            enableChecker: Boolean,
-            classes: ClassNodes,
-            errors: HostStubGenErrors,
-            stats: HostStubGenStats?,
-            ) {
+        classInternalName: String,
+        input: InputStream,
+        out: OutputStream,
+        filter: OutputFilter,
+        packageRedirector: PackageRedirectRemapper,
+        remapper: Remapper?,
+        enableChecker: Boolean,
+        classes: ClassNodes,
+        errors: HostStubGenErrors,
+        stats: HostStubGenStats?
+    ) {
         val cr = ClassReader(input)
 
         // COMPUTE_FRAMES wouldn't be happy if code uses
@@ -438,14 +399,15 @@
         }
 
         val visitorOptions = BaseAdapter.Options(
-                enablePreTrace = options.enablePreTrace.get,
-                enablePostTrace = options.enablePostTrace.get,
-                enableNonStubMethodCallDetection = options.enableNonStubMethodCallDetection.get,
-                errors = errors,
-                stats = stats,
+            errors = errors,
+            stats = stats,
+            enablePreTrace = options.enablePreTrace.get,
+            enablePostTrace = options.enablePostTrace.get,
         )
-        outVisitor = BaseAdapter.getVisitor(classInternalName, classes, outVisitor, filter,
-                packageRedirector, remapper, forImpl, visitorOptions)
+        outVisitor = BaseAdapter.getVisitor(
+            classInternalName, classes, outVisitor, filter,
+            packageRedirector, visitorOptions
+        )
 
         cr.accept(outVisitor, ClassReader.EXPAND_FRAMES)
         val data = cw.toByteArray()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
index 6b01d48..a218c55 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenErrors.kt
@@ -19,6 +19,6 @@
     open fun onErrorFound(message: String) {
         // TODO: For now, we just throw as soon as any error is found, but eventually we should keep
         // all errors and print them at the end.
-        throw HostStubGenUserErrorException(message)
+        throw GeneralUserErrorException(message)
     }
 }
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
index ee4a06f..4bcee40 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenLogger.kt
@@ -89,6 +89,8 @@
         addPrinter(StreamPrinter(level, PrintWriter(BufferedOutputStream(
             FileOutputStream(logFilename)))))
 
+        log.i("Log file set: $logFilename for $level")
+
         return this
     }
 
@@ -121,7 +123,10 @@
         return level.ordinal <= maxLogLevel.ordinal
     }
 
-    private fun println(level: LogLevel, message: String) {
+    fun println(level: LogLevel, message: String) {
+        if (message.isEmpty()) {
+            return // Don't print an empty message.
+        }
         printers.forEach {
             if (it.logLevel.ordinal >= level.ordinal) {
                 it.println(level, indent, message)
@@ -129,7 +134,7 @@
         }
     }
 
-    private fun println(level: LogLevel, format: String, vararg args: Any?) {
+    fun println(level: LogLevel, format: String, vararg args: Any?) {
         if (isEnabled(level)) {
             println(level, String.format(format, *args))
         }
@@ -185,16 +190,45 @@
         println(LogLevel.Debug, format, *args)
     }
 
-    inline fun <T> iTime(message: String, block: () -> T): T {
+    inline fun <T> logTime(level: LogLevel, message: String, block: () -> T): Double {
+        var ret: Double = -1.0
         val start = System.currentTimeMillis()
-        val ret = block()
-        val end = System.currentTimeMillis()
-
-        log.i("%s: took %.1f second(s).", message, (end - start) / 1000.0)
-
+        try {
+            block()
+        } finally {
+            val end = System.currentTimeMillis()
+            ret = (end - start) / 1000.0
+            if (isEnabled(level)) {
+                println(level,
+                    String.format("%s: took %.1f second(s).", message, (end - start) / 1000.0))
+            }
+        }
         return ret
     }
 
+    /** Do an "i" log with how long it took. */
+    inline fun <T> iTime(message: String, block: () -> T): Double {
+        return logTime(LogLevel.Info, message, block)
+    }
+
+    /** Do a "v" log with how long it took. */
+    inline fun <T> vTime(message: String, block: () -> T): Double {
+        return logTime(LogLevel.Verbose, message, block)
+    }
+
+    /** Do a "d" log with how long it took. */
+    inline fun <T> dTime(message: String, block: () -> T): Double {
+        return logTime(LogLevel.Debug, message, block)
+    }
+
+    /**
+     * Similar to the other "xTime" methods, but the message is not supposed to be printed.
+     * It's only used to measure the duration with the same interface as other log methods.
+     */
+    inline fun <T> nTime(block: () -> T): Double {
+        return logTime(LogLevel.Debug, "", block)
+    }
+
     inline fun forVerbose(block: () -> Unit) {
         if (isEnabled(LogLevel.Verbose)) {
             block()
@@ -238,6 +272,21 @@
             }
         }
     }
+
+    /**
+     * Handle log-related command line arguments.
+     */
+    fun maybeHandleCommandLineArg(currentArg: String, nextArgProvider: () -> String): Boolean {
+        when (currentArg) {
+            "-v", "--verbose" -> setConsoleLogLevel(LogLevel.Verbose)
+            "-d", "--debug" -> setConsoleLogLevel(LogLevel.Debug)
+            "-q", "--quiet" -> setConsoleLogLevel(LogLevel.None)
+            "--verbose-log" -> addFilePrinter(LogLevel.Verbose, nextArgProvider())
+            "--debug-log" -> addFilePrinter(LogLevel.Debug, nextArgProvider())
+            else -> return false
+        }
+        return true
+    }
 }
 
 private interface LogPrinter {
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
index 45e7e30..8506466 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenMain.kt
@@ -24,20 +24,32 @@
  */
 fun main(args: Array<String>) {
     executableName = "HostStubGen"
+    runMainWithBoilerplate {
+        // Parse the command line arguments.
+        var clanupOnError = false
+        try {
+            val options = HostStubGenOptions.parseArgs(args)
+            clanupOnError = options.cleanUpOnError.get
 
+            log.v("$executableName started")
+            log.v("Options: $options")
+
+            // Run.
+            HostStubGen(options).run()
+        } catch (e: Throwable) {
+            if (clanupOnError) {
+                TODO("Remove output jars here")
+            }
+            throw e
+        }
+    }
+}
+
+inline fun runMainWithBoilerplate(realMain: () -> Unit) {
     var success = false
-    var clanupOnError = false
 
     try {
-        // Parse the command line arguments.
-        val options = HostStubGenOptions.parseArgs(args)
-        clanupOnError = options.cleanUpOnError.get
-
-        log.v("$executableName started")
-        log.v("Options: $options")
-
-        // Run.
-        HostStubGen(options).run()
+        realMain()
 
         success = true
     } catch (e: Throwable) {
@@ -45,9 +57,6 @@
         if (e !is UserErrorException) {
             e.printStackTrace(PrintWriter(log.getWriter(LogLevel.Error)))
         }
-        if (clanupOnError) {
-            TODO("Remove output jars here")
-        }
     } finally {
         log.i("$executableName finished")
         log.flush()
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 2f833a8..1cedcc3 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -17,20 +17,17 @@
 
 import com.android.hoststubgen.filters.FilterPolicy
 import java.io.BufferedReader
-import java.io.File
 import java.io.FileReader
 
 /**
  * A single value that can only set once.
  */
-class SetOnce<T>(
-        private var value: T,
-) {
+open class SetOnce<T>(private var value: T) {
     class SetMoreThanOnceException : Exception()
 
     private var set = false
 
-    fun set(v: T) {
+    fun set(v: T): T {
         if (set) {
             throw SetMoreThanOnceException()
         }
@@ -39,6 +36,7 @@
         }
         set = true
         value = v
+        return v
     }
 
     val get: T
@@ -59,6 +57,16 @@
     }
 }
 
+class IntSetOnce(value: Int) : SetOnce<Int>(value) {
+    fun set(v: String): Int {
+        try {
+            return this.set(v.toInt())
+        } catch (e: NumberFormatException) {
+            throw ArgumentsException("Invalid integer $v")
+        }
+    }
+}
+
 /**
  * Options that can be set from command line arguments.
  */
@@ -66,21 +74,16 @@
         /** Input jar file*/
         var inJar: SetOnce<String> = SetOnce(""),
 
-        /** Output stub jar file */
-        var outStubJar: SetOnce<String?> = SetOnce(null),
-
-        /** Output implementation jar file */
-        var outImplJar: SetOnce<String?> = SetOnce(null),
+        /** Output jar file */
+        var outJar: SetOnce<String?> = SetOnce(null),
 
         var inputJarDumpFile: SetOnce<String?> = SetOnce(null),
 
         var inputJarAsKeepAllFile: SetOnce<String?> = SetOnce(null),
 
-        var stubAnnotations: MutableSet<String> = mutableSetOf(),
         var keepAnnotations: MutableSet<String> = mutableSetOf(),
         var throwAnnotations: MutableSet<String> = mutableSetOf(),
         var removeAnnotations: MutableSet<String> = mutableSetOf(),
-        var stubClassAnnotations: MutableSet<String> = mutableSetOf(),
         var keepClassAnnotations: MutableSet<String> = mutableSetOf(),
 
         var substituteAnnotations: MutableSet<String> = mutableSetOf(),
@@ -95,8 +98,6 @@
         var defaultClassLoadHook: SetOnce<String?> = SetOnce(null),
         var defaultMethodCallHook: SetOnce<String?> = SetOnce(null),
 
-        var intersectStubJars: MutableSet<String> = mutableSetOf(),
-
         var policyOverrideFile: SetOnce<String?> = SetOnce(null),
 
         var defaultPolicy: SetOnce<FilterPolicy> = SetOnce(FilterPolicy.Remove),
@@ -107,24 +108,15 @@
         var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
         var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
 
-        var enableNonStubMethodCallDetection: SetOnce<Boolean> = SetOnce(false),
-
         var statsFile: SetOnce<String?> = SetOnce(null),
 
         var apiListFile: SetOnce<String?> = SetOnce(null),
 
-        var numShards: SetOnce<Int> = SetOnce(1),
-        var shard: SetOnce<Int> = SetOnce(0),
+        var numShards: IntSetOnce = IntSetOnce(1),
+        var shard: IntSetOnce = IntSetOnce(0),
 ) {
     companion object {
 
-        private fun String.ensureFileExists(): String {
-            if (!File(this).exists()) {
-                throw InputFileNotFoundException(this)
-            }
-            return this
-        }
-
         private fun parsePackageRedirect(fromColonTo: String): Pair<String, String> {
             val colon = fromColonTo.indexOf(':')
             if ((colon < 1) || (colon + 1 >= fromColonTo.length)) {
@@ -137,7 +129,7 @@
         fun parseArgs(args: Array<String>): HostStubGenOptions {
             val ret = HostStubGenOptions()
 
-            val ai = ArgIterator(expandAtFiles(args))
+            val ai = ArgIterator.withAtFiles(args)
 
             var allAnnotations = mutableSetOf<String>()
 
@@ -148,46 +140,29 @@
                 return name
             }
 
-            fun setLogFile(level: LogLevel, filename: String) {
-                log.addFilePrinter(level, filename)
-                log.i("$level log file: $filename")
-            }
-
             while (true) {
-                val arg = ai.nextArgOptional()
-                if (arg == null) {
-                    break
-                }
+                val arg = ai.nextArgOptional() ?: break
 
                 // Define some shorthands...
                 fun nextArg(): String = ai.nextArgRequired(arg)
-                fun SetOnce<String>.setNextStringArg(): String = nextArg().also { this.set(it) }
-                fun SetOnce<String?>.setNextStringArg(): String = nextArg().also { this.set(it) }
                 fun MutableSet<String>.addUniqueAnnotationArg(): String =
                         nextArg().also { this += ensureUniqueAnnotation(it) }
-                fun SetOnce<Int>.setNextIntArg(): String = nextArg().also {
-                    try {
-                        this.set(it.toInt())
-                    } catch (e: NumberFormatException) {
-                        throw ArgumentsException("Invalid integer for $arg: $it")
-                    }
-                }
 
+                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
+                    continue
+                }
                 try {
                     when (arg) {
                         // TODO: Write help
                         "-h", "--help" -> TODO("Help is not implemented yet")
 
-                        "-v", "--verbose" -> log.setConsoleLogLevel(LogLevel.Verbose)
-                        "-d", "--debug" -> log.setConsoleLogLevel(LogLevel.Debug)
-                        "-q", "--quiet" -> log.setConsoleLogLevel(LogLevel.None)
-
-                        "--in-jar" -> ret.inJar.setNextStringArg().ensureFileExists()
-                        "--out-stub-jar" -> ret.outStubJar.setNextStringArg()
-                        "--out-impl-jar" -> ret.outImplJar.setNextStringArg()
+                        "--in-jar" -> ret.inJar.set(nextArg()).ensureFileExists()
+                        // We support both arguments because some AOSP dependencies
+                        // still use the old argument
+                        "--out-jar", "--out-impl-jar" -> ret.outJar.set(nextArg())
 
                         "--policy-override-file" ->
-                            ret.policyOverrideFile.setNextStringArg().ensureFileExists()
+                            ret.policyOverrideFile.set(nextArg())!!.ensureFileExists()
 
                         "--clean-up-on-error" -> ret.cleanUpOnError.set(true)
                         "--no-clean-up-on-error" -> ret.cleanUpOnError.set(false)
@@ -195,17 +170,10 @@
                         "--default-remove" -> ret.defaultPolicy.set(FilterPolicy.Remove)
                         "--default-throw" -> ret.defaultPolicy.set(FilterPolicy.Throw)
                         "--default-keep" -> ret.defaultPolicy.set(FilterPolicy.Keep)
-                        "--default-stub" -> ret.defaultPolicy.set(FilterPolicy.Stub)
-
-                        "--stub-annotation" ->
-                            ret.stubAnnotations.addUniqueAnnotationArg()
 
                         "--keep-annotation" ->
                             ret.keepAnnotations.addUniqueAnnotationArg()
 
-                        "--stub-class-annotation" ->
-                            ret.stubClassAnnotations.addUniqueAnnotationArg()
-
                         "--keep-class-annotation" ->
                             ret.keepClassAnnotations.addUniqueAnnotationArg()
 
@@ -231,19 +199,16 @@
                             ret.packageRedirects += parsePackageRedirect(nextArg())
 
                         "--annotation-allowed-classes-file" ->
-                            ret.annotationAllowedClassesFile.setNextStringArg()
+                            ret.annotationAllowedClassesFile.set(nextArg())
 
                         "--default-class-load-hook" ->
-                            ret.defaultClassLoadHook.setNextStringArg()
+                            ret.defaultClassLoadHook.set(nextArg())
 
                         "--default-method-call-hook" ->
-                            ret.defaultMethodCallHook.setNextStringArg()
-
-                        "--intersect-stub-jar" ->
-                            ret.intersectStubJars += nextArg().ensureFileExists()
+                            ret.defaultMethodCallHook.set(nextArg())
 
                         "--gen-keep-all-file" ->
-                            ret.inputJarAsKeepAllFile.setNextStringArg()
+                            ret.inputJarAsKeepAllFile.set(nextArg())
 
                         // Following options are for debugging.
                         "--enable-class-checker" -> ret.enableClassChecker.set(true)
@@ -255,22 +220,21 @@
                         "--enable-post-trace" -> ret.enablePostTrace.set(true)
                         "--no-post-trace" -> ret.enablePostTrace.set(false)
 
-                        "--enable-non-stub-method-check" ->
-                            ret.enableNonStubMethodCallDetection.set(true)
+                        "--gen-input-dump-file" -> ret.inputJarDumpFile.set(nextArg())
 
-                        "--no-non-stub-method-check" ->
-                            ret.enableNonStubMethodCallDetection.set(false)
+                        "--stats-file" -> ret.statsFile.set(nextArg())
+                        "--supported-api-list-file" -> ret.apiListFile.set(nextArg())
 
-                        "--gen-input-dump-file" -> ret.inputJarDumpFile.setNextStringArg()
-
-                        "--verbose-log" -> setLogFile(LogLevel.Verbose, nextArg())
-                        "--debug-log" -> setLogFile(LogLevel.Debug, nextArg())
-
-                        "--stats-file" -> ret.statsFile.setNextStringArg()
-                        "--supported-api-list-file" -> ret.apiListFile.setNextStringArg()
-
-                        "--num-shards" -> ret.numShards.setNextIntArg()
-                        "--shard-index" -> ret.shard.setNextIntArg()
+                        "--num-shards" -> ret.numShards.set(nextArg()).also {
+                            if (it < 1) {
+                                throw ArgumentsException("$arg must be positive integer")
+                            }
+                        }
+                        "--shard-index" -> ret.shard.set(nextArg()).also {
+                            if (it < 0) {
+                                throw ArgumentsException("$arg must be positive integer or zero")
+                            }
+                        }
 
                         else -> throw ArgumentsException("Unknown option: $arg")
                     }
@@ -282,131 +246,48 @@
             if (!ret.inJar.isSet) {
                 throw ArgumentsException("Required option missing: --in-jar")
             }
-            if (!ret.outStubJar.isSet && !ret.outImplJar.isSet) {
-                log.w("Neither --out-stub-jar nor --out-impl-jar is set." +
-                        " $executableName will not generate jar files.")
+            if (!ret.outJar.isSet) {
+                log.w("--out-jar is not set. $executableName will not generate jar files.")
+            }
+            if (ret.numShards.isSet != ret.shard.isSet) {
+                throw ArgumentsException("--num-shards and --shard-index must be used together")
             }
 
-            if (ret.enableNonStubMethodCallDetection.get) {
-                log.w("--enable-non-stub-method-check is not fully implemented yet." +
-                    " See the todo in doesMethodNeedNonStubCallCheck().")
+            if (ret.numShards.isSet) {
+                if (ret.shard.get >= ret.numShards.get) {
+                    throw ArgumentsException("--shard-index must be smaller than --num-shards")
+                }
             }
 
             return ret
         }
-
-        /**
-         * Scan the arguments, and if any of them starts with an `@`, then load from the file
-         * and use its content as arguments.
-         *
-         * In this file, each line is treated as a single argument.
-         *
-         * The file can contain '#' as comments.
-         */
-        private fun expandAtFiles(args: Array<String>): List<String> {
-            val ret = mutableListOf<String>()
-
-            args.forEach { arg ->
-                if (!arg.startsWith('@')) {
-                    ret += arg
-                    return@forEach
-                }
-                // Read from the file, and add each line to the result.
-                val filename = arg.substring(1).ensureFileExists()
-
-                log.v("Expanding options file $filename")
-
-                BufferedReader(FileReader(filename)).use { reader ->
-                    while (true) {
-                        var line = reader.readLine()
-                        if (line == null) {
-                            break // EOF
-                        }
-
-                        line = normalizeTextLine(line)
-                        if (line.isNotEmpty()) {
-                            ret += line
-                        }
-                    }
-                }
-            }
-            return ret
-        }
-    }
-
-    open class ArgumentsException(message: String?) : Exception(message), UserErrorException
-
-    /** Thrown when the same annotation is used with different annotation arguments. */
-    class DuplicateAnnotationException(annotationName: String?) :
-            ArgumentsException("Duplicate annotation specified: '$annotationName'")
-
-    /** Thrown when an input file does not exist. */
-    class InputFileNotFoundException(filename: String) :
-            ArgumentsException("File '$filename' not found")
-
-    private class ArgIterator(
-            private val args: List<String>,
-            private var currentIndex: Int = -1
-    ) {
-        val current: String
-            get() = args.get(currentIndex)
-
-        /**
-         * Get the next argument, or [null] if there's no more arguments.
-         */
-        fun nextArgOptional(): String? {
-            if ((currentIndex + 1) >= args.size) {
-                return null
-            }
-            return args.get(++currentIndex)
-        }
-
-        /**
-         * Get the next argument, or throw if
-         */
-        fun nextArgRequired(argName: String): String {
-            nextArgOptional().let {
-                if (it == null) {
-                    throw ArgumentsException("Missing parameter for option $argName")
-                }
-                if (it.isEmpty()) {
-                    throw ArgumentsException("Parameter can't be empty for option $argName")
-                }
-                return it
-            }
-        }
     }
 
     override fun toString(): String {
         return """
             HostStubGenOptions{
               inJar='$inJar',
-              outStubJar='$outStubJar',
-              outImplJar='$outImplJar',
+              outJar='$outJar',
               inputJarDumpFile=$inputJarDumpFile,
               inputJarAsKeepAllFile=$inputJarAsKeepAllFile,
-              stubAnnotations=$stubAnnotations,
               keepAnnotations=$keepAnnotations,
               throwAnnotations=$throwAnnotations,
               removeAnnotations=$removeAnnotations,
-              stubClassAnnotations=$stubClassAnnotations,
               keepClassAnnotations=$keepClassAnnotations,
               substituteAnnotations=$substituteAnnotations,
               nativeSubstituteAnnotations=$nativeSubstituteAnnotations,
               classLoadHookAnnotations=$classLoadHookAnnotations,
               keepStaticInitializerAnnotations=$keepStaticInitializerAnnotations,
               packageRedirects=$packageRedirects,
-              $annotationAllowedClassesFile=$annotationAllowedClassesFile,
+              annotationAllowedClassesFile=$annotationAllowedClassesFile,
               defaultClassLoadHook=$defaultClassLoadHook,
               defaultMethodCallHook=$defaultMethodCallHook,
-              intersectStubJars=$intersectStubJars,
               policyOverrideFile=$policyOverrideFile,
               defaultPolicy=$defaultPolicy,
               cleanUpOnError=$cleanUpOnError,
               enableClassChecker=$enableClassChecker,
               enablePreTrace=$enablePreTrace,
               enablePostTrace=$enablePostTrace,
-              enableNonStubMethodCallDetection=$enableNonStubMethodCallDetection,
               statsFile=$statsFile,
               apiListFile=$apiListFile,
               numShards=$numShards,
@@ -415,3 +296,80 @@
             """.trimIndent()
     }
 }
+
+class ArgIterator(
+    private val args: List<String>,
+    private var currentIndex: Int = -1
+) {
+    val current: String
+        get() = args.get(currentIndex)
+
+    /**
+     * Get the next argument, or [null] if there's no more arguments.
+     */
+    fun nextArgOptional(): String? {
+        if ((currentIndex + 1) >= args.size) {
+            return null
+        }
+        return args.get(++currentIndex)
+    }
+
+    /**
+     * Get the next argument, or throw if
+     */
+    fun nextArgRequired(argName: String): String {
+        nextArgOptional().let {
+            if (it == null) {
+                throw ArgumentsException("Missing parameter for option $argName")
+            }
+            if (it.isEmpty()) {
+                throw ArgumentsException("Parameter can't be empty for option $argName")
+            }
+            return it
+        }
+    }
+
+    companion object {
+        fun withAtFiles(args: Array<String>): ArgIterator {
+            return ArgIterator(expandAtFiles(args))
+        }
+    }
+}
+
+/**
+ * Scan the arguments, and if any of them starts with an `@`, then load from the file
+ * and use its content as arguments.
+ *
+ * In this file, each line is treated as a single argument.
+ *
+ * The file can contain '#' as comments.
+ */
+private fun expandAtFiles(args: Array<String>): List<String> {
+    val ret = mutableListOf<String>()
+
+    args.forEach { arg ->
+        if (!arg.startsWith('@')) {
+            ret += arg
+            return@forEach
+        }
+        // Read from the file, and add each line to the result.
+        val filename = arg.substring(1).ensureFileExists()
+
+        log.v("Expanding options file $filename")
+
+        BufferedReader(FileReader(filename)).use { reader ->
+            while (true) {
+                var line = reader.readLine()
+                if (line == null) {
+                    break // EOF
+                }
+
+                line = normalizeTextLine(line)
+                if (line.isNotEmpty()) {
+                    ret += line
+                }
+            }
+        }
+    }
+    return ret
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
index 3f2b13a..7197e0e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt
@@ -58,7 +58,24 @@
     return null
 }
 
-fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): String? {
+fun ClassNode.findAnyAnnotation(set: Set<String>): AnnotationNode? {
+    return findAnyAnnotation(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
+fun MethodNode.findAnyAnnotation(set: Set<String>): AnnotationNode? {
+    return findAnyAnnotation(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
+fun FieldNode.findAnyAnnotation(set: Set<String>): AnnotationNode? {
+    return findAnyAnnotation(set, this.visibleAnnotations, this.invisibleAnnotations)
+}
+
+fun <T> findAnnotationValueAsObject(
+    an: AnnotationNode,
+    propertyName: String,
+    expectedTypeHumanReadableName: String,
+    converter: (Any?) -> T?,
+): T? {
     for (i in 0..(an.values?.size ?: 0) - 2 step 2) {
         val name = an.values[i]
 
@@ -66,16 +83,30 @@
             continue
         }
         val value = an.values[i + 1]
-        if (value is String) {
-            return value
+        if (value == null) {
+            return null
         }
-        throw ClassParseException(
-                "The type of '$name' in annotation \"${an.desc}\" must be String" +
-                        ", but is ${value?.javaClass?.canonicalName}")
+
+        try {
+            return converter(value)
+        } catch (e: ClassCastException) {
+            throw ClassParseException(
+                "The type of '$propertyName' in annotation @${an.desc} must be " +
+                        "$expectedTypeHumanReadableName, but is ${value?.javaClass?.canonicalName}")
+        }
     }
     return null
 }
 
+fun findAnnotationValueAsString(an: AnnotationNode, propertyName: String): String? {
+    return findAnnotationValueAsObject(an, propertyName, "String", {it as String})
+}
+
+fun findAnnotationValueAsType(an: AnnotationNode, propertyName: String): Type? {
+    return findAnnotationValueAsObject(an, propertyName, "Class", {it as Type})
+}
+
+
 val periodOrSlash = charArrayOf('.', '/')
 
 fun getPackageNameFromFullClassName(fullClassName: String): String {
@@ -117,6 +148,32 @@
     return "$defaultPackageName.$className"
 }
 
+fun splitWithLastPeriod(name: String): Pair<String, String>? {
+    val pos = name.lastIndexOf('.')
+    if (pos < 0) {
+        return null
+    }
+    return Pair(name.substring(0, pos), name.substring(pos + 1))
+}
+
+fun String.startsWithAny(vararg prefixes: String): Boolean {
+    prefixes.forEach {
+        if (this.startsWith(it)) {
+            return true
+        }
+    }
+    return false
+}
+
+fun String.endsWithAny(vararg suffixes: String): Boolean {
+    suffixes.forEach {
+        if (this.endsWith(it)) {
+            return true
+        }
+    }
+    return false
+}
+
 fun String.toJvmClassName(): String {
     return this.replace('.', '/')
 }
@@ -129,6 +186,14 @@
     return this.replace('/', '.')
 }
 
+fun zipEntryNameToClassName(entryFilename: String): String? {
+    val suffix = ".class"
+    if (!entryFilename.endsWith(suffix)) {
+        return null
+    }
+    return entryFilename.substring(0, entryFilename.length - suffix.length)
+}
+
 private val numericalInnerClassName = """.*\$\d+$""".toRegex()
 
 fun isAnonymousInnerClass(cn: ClassNode): Boolean {
@@ -137,18 +202,6 @@
 }
 
 /**
- * Take a class name. If it's a nested class, then return the name of its direct outer class name.
- * Otherwise, return null.
- */
-fun getDirectOuterClassName(className: String): String? {
-    val pos = className.lastIndexOf('$')
-    if (pos < 0) {
-        return null
-    }
-    return className.substring(0, pos)
-}
-
-/**
  * Write bytecode to push all the method arguments to the stack.
  * The number of arguments and their type are taken from [methodDescriptor].
  */
@@ -198,11 +251,11 @@
 /**
  * Given a method descriptor, insert an [argType] as the first argument to it.
  */
-fun prependArgTypeToMethodDescriptor(methodDescriptor: String, argType: Type): String {
+fun prependArgTypeToMethodDescriptor(methodDescriptor: String, classInternalName: String): String {
     val returnType = Type.getReturnType(methodDescriptor)
     val argTypes = Type.getArgumentTypes(methodDescriptor).toMutableList()
 
-    argTypes.add(0, argType)
+    argTypes.add(0, Type.getType("L" + classInternalName + ";"))
 
     return Type.getMethodDescriptor(returnType, *argTypes.toTypedArray())
 }
@@ -270,6 +323,18 @@
     return (this.access and Opcodes.ACC_STATIC) != 0
 }
 
+fun MethodNode.isPublic(): Boolean {
+    return (this.access and Opcodes.ACC_PUBLIC) != 0
+}
+
+fun MethodNode.isNative(): Boolean {
+    return (this.access and Opcodes.ACC_NATIVE) != 0
+}
+
+fun MethodNode.isSpecial(): Boolean {
+    return CTOR_NAME == this.name || CLASS_INITIALIZER_NAME == this.name
+}
+
 fun FieldNode.isEnum(): Boolean {
     return (this.access and Opcodes.ACC_ENUM) != 0
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
index 2607df6..e2647eb 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/asm/ClassNodes.kt
@@ -27,6 +27,11 @@
 import java.io.BufferedInputStream
 import java.io.PrintWriter
 import java.util.Arrays
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+import java.util.function.Consumer
+import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
 
 /**
@@ -183,10 +188,43 @@
         /**
          * Load all the classes, without code.
          */
-        fun loadClassStructures(inJar: String): ClassNodes {
-            log.iTime("Reading class structure from $inJar") {
-                val allClasses = ClassNodes()
+        fun loadClassStructures(
+            inJar: String,
+            timeCollector: Consumer<Double>? = null,
+        ): ClassNodes {
+            val allClasses = ClassNodes()
 
+            // Load classes in parallel.
+            val executor = Executors.newFixedThreadPool(4)
+
+            // First exception defected.
+            val exception = AtomicReference<Throwable>()
+
+            // Called on a BG thread. Read a single jar entry and add it to [allClasses].
+            fun parseClass(inZip: ZipFile, entry: ZipEntry) {
+                try {
+                    inZip.getInputStream(entry).use { ins ->
+                        val cr = ClassReader(BufferedInputStream(ins))
+                        val cn = ClassNode()
+                        cr.accept(
+                            cn, ClassReader.SKIP_CODE
+                                    or ClassReader.SKIP_DEBUG
+                                    or ClassReader.SKIP_FRAMES
+                        )
+                        synchronized(allClasses) {
+                            if (!allClasses.addClass(cn)) {
+                                log.w("Duplicate class found: ${cn.name}")
+                            }
+                        }
+                    }
+                } catch (e: Throwable) {
+                    log.e("Failed to load class: $e")
+                    exception.compareAndSet(null, e)
+                }
+            }
+
+            // Actually open the jar and read it on worker threads.
+            val time = log.iTime("Reading class structure from $inJar") {
                 log.withIndent {
                     ZipFile(inJar).use { inZip ->
                         val inEntries = inZip.entries()
@@ -194,40 +232,42 @@
                         while (inEntries.hasMoreElements()) {
                             val entry = inEntries.nextElement()
 
-                            BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
-                                if (entry.name.endsWith(".class")) {
-                                    val cr = ClassReader(bis)
-                                    val cn = ClassNode()
-                                    cr.accept(
-                                        cn, ClassReader.SKIP_CODE
-                                                or ClassReader.SKIP_DEBUG
-                                                or ClassReader.SKIP_FRAMES
-                                    )
-                                    if (!allClasses.addClass(cn)) {
-                                        log.w("Duplicate class found: ${cn.name}")
-                                    }
-                                } else if (entry.name.endsWith(".dex")) {
-                                    // Seems like it's an ART jar file. We can't process it.
-                                    // It's a fatal error.
-                                    throw InvalidJarFileException(
-                                        "$inJar is not a desktop jar file."
-                                        + " It contains a *.dex file."
-                                    )
-                                } else {
-                                    // Unknown file type. Skip.
-                                    while (bis.available() > 0) {
-                                        bis.skip((1024 * 1024).toLong())
-                                    }
+                            if (entry.name.endsWith(".class")) {
+                                executor.submit {
+                                    parseClass(inZip, entry)
                                 }
+                            } else if (entry.name.endsWith(".dex")) {
+                                // Seems like it's an ART jar file. We can't process it.
+                                // It's a fatal error.
+                                throw InvalidJarFileException(
+                                    "$inJar is not a desktop jar file."
+                                            + " It contains a *.dex file."
+                                )
+                            } else {
+                                // Unknown file type. Skip.
                             }
                         }
+                        // Wait for all the work to complete. (must do it before closing the zip)
+                        log.i("Waiting for all loaders to finish...")
+                        executor.shutdown()
+                        executor.awaitTermination(5, TimeUnit.MINUTES)
+                        log.i("All loaders to finished.")
                     }
                 }
+
+                // If any exception is detected, throw it.
+                exception.get()?.let {
+                    throw it
+                }
+
                 if (allClasses.size == 0) {
                     log.w("$inJar contains no *.class files.")
+                } else {
+                    log.i("Loaded ${allClasses.size} classes from $inJar.")
                 }
-                return allClasses
             }
+            timeCollector?.accept(time)
+            return allClasses
         }
     }
 }
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
index aaefee4..5e4e70f 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/dumper/ApiDumper.kt
@@ -44,7 +44,7 @@
         val descriptor: String,
     )
 
-    val javaStandardApiPolicy = FilterPolicy.Stub.withReason("Java standard API")
+    private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API")
 
     private val shownMethods = mutableSetOf<MethodKey>()
 
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
index 248121c..38a41b2 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AnnotationBasedFilter.kt
@@ -17,7 +17,6 @@
 
 import com.android.hoststubgen.ClassParseException
 import com.android.hoststubgen.HostStubGenErrors
-import com.android.hoststubgen.HostStubGenInternalException
 import com.android.hoststubgen.InvalidAnnotationException
 import com.android.hoststubgen.addNonNullElement
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
@@ -35,258 +34,125 @@
 
 // TODO: Detect invalid cases, such as...
 // - Class's visibility is lower than the members'.
-// - HostSideTestSubstituteWith is set, but it doesn't have @Stub or @Keep
 
 /**
  * [OutputFilter] using Java annotations.
  */
 class AnnotationBasedFilter(
-        private val errors: HostStubGenErrors,
-        private val classes: ClassNodes,
-        stubAnnotations_: Set<String>,
-        keepAnnotations_: Set<String>,
-        stubClassAnnotations_: Set<String>,
-        keepClassAnnotations_: Set<String>,
-        throwAnnotations_: Set<String>,
-        removeAnnotations_: Set<String>,
-        substituteAnnotations_: Set<String>,
-        nativeSubstituteAnnotations_: Set<String>,
-        classLoadHookAnnotations_: Set<String>,
-        keepStaticInitializerAnnotations_: Set<String>,
-        private val annotationAllowedClassesFilter: ClassFilter,
-        fallback: OutputFilter,
+    private val errors: HostStubGenErrors,
+    private val classes: ClassNodes,
+    keepAnnotations_: Set<String>,
+    keepClassAnnotations_: Set<String>,
+    throwAnnotations_: Set<String>,
+    removeAnnotations_: Set<String>,
+    substituteAnnotations_: Set<String>,
+    nativeSubstituteAnnotations_: Set<String>,
+    classLoadHookAnnotations_: Set<String>,
+    keepStaticInitializerAnnotations_: Set<String>,
+    private val annotationAllowedClassesFilter: ClassFilter,
+    fallback: OutputFilter,
 ) : DelegatingFilter(fallback) {
-    private var stubAnnotations = convertToInternalNames(stubAnnotations_)
-    private var keepAnnotations = convertToInternalNames(keepAnnotations_)
-    private var stubClassAnnotations = convertToInternalNames(stubClassAnnotations_)
-    private var keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
-    private var throwAnnotations = convertToInternalNames(throwAnnotations_)
-    private var removeAnnotations = convertToInternalNames(removeAnnotations_)
-    private var substituteAnnotations = convertToInternalNames(substituteAnnotations_)
-    private var nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
-    private var classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
-    private var keepStaticInitializerAnnotations =
-            convertToInternalNames(keepStaticInitializerAnnotations_)
+    private val keepAnnotations = convertToInternalNames(keepAnnotations_)
+    private val keepClassAnnotations = convertToInternalNames(keepClassAnnotations_)
+    private val throwAnnotations = convertToInternalNames(throwAnnotations_)
+    private val removeAnnotations = convertToInternalNames(removeAnnotations_)
+    private val substituteAnnotations = convertToInternalNames(substituteAnnotations_)
+    private val nativeSubstituteAnnotations = convertToInternalNames(nativeSubstituteAnnotations_)
+    private val classLoadHookAnnotations = convertToInternalNames(classLoadHookAnnotations_)
+    private val keepStaticInitializerAnnotations =
+        convertToInternalNames(keepStaticInitializerAnnotations_)
 
     /** Annotations that control API visibility. */
-    private var visibilityAnnotations: Set<String> = convertToInternalNames(
-        stubAnnotations_ +
-        keepAnnotations_ +
-        stubClassAnnotations_ +
-        keepClassAnnotations_ +
-        throwAnnotations_ +
-        removeAnnotations_)
+    private val visibilityAnnotations = keepAnnotations +
+            keepClassAnnotations +
+            throwAnnotations +
+            removeAnnotations +
+            substituteAnnotations
+
+    /** All the annotations we use. */
+    private val allAnnotations = visibilityAnnotations +
+            nativeSubstituteAnnotations +
+            classLoadHookAnnotations +
+            keepStaticInitializerAnnotations
 
     /**
      * All the annotations we use. Note, this one is in a [convertToJvmNames] format unlike
      * other ones, because of how it's used.
      */
-    private var allAnnotations: Set<String> = convertToJvmNames(
-        stubAnnotations_ +
-                keepAnnotations_ +
-                stubClassAnnotations_ +
+    private val allAnnotationClasses: Set<String> = convertToJvmNames(
+        keepAnnotations_ +
                 keepClassAnnotations_ +
                 throwAnnotations_ +
                 removeAnnotations_ +
                 substituteAnnotations_ +
                 nativeSubstituteAnnotations_ +
-                classLoadHookAnnotations_)
+                classLoadHookAnnotations_ +
+                keepStaticInitializerAnnotations_
+    )
 
-    private val substitutionHelper = SubstitutionHelper()
+    private val policyCache = mutableMapOf<String, ClassAnnotations>()
 
-    private val reasonAnnotation = "annotation"
-    private val reasonClassAnnotation = "class-annotation"
-
-    /**
-     * Throw if an item has more than one visibility annotations.
-     *
-     * name1 - 4 are only used in exception messages. We take them as separate strings
-     * to avoid unnecessary string concatenations.
-     */
-    private fun detectInvalidAnnotations(
-        visibles: List<AnnotationNode>?,
-        invisibles: List<AnnotationNode>?,
-        type: String,
-        name1: String,
-        name2: String,
-        name3: String,
-    ) {
-        var count = 0
-        for (an in visibles ?: emptyList()) {
-            if (visibilityAnnotations.contains(an.desc)) {
-                count++
-            }
-        }
-        for (an in invisibles ?: emptyList()) {
-            if (visibilityAnnotations.contains(an.desc)) {
-                count++
-            }
-        }
-        if (count > 1) {
-            val description = if (name2 == "" && name3 == "") {
-                "$type $name1"
-            } else {
-                "$type $name1.$name2$name3"
-            }
-            throw InvalidAnnotationException(
-                "Found more than one visibility annotations on $description")
+    private val AnnotationNode.policy: FilterPolicyWithReason? get() {
+        return when (desc) {
+            in keepAnnotations -> FilterPolicy.Keep.withReason(REASON_ANNOTATION)
+            in keepClassAnnotations -> FilterPolicy.KeepClass.withReason(REASON_CLASS_ANNOTATION)
+            in substituteAnnotations -> FilterPolicy.Substitute.withReason(REASON_ANNOTATION)
+            in throwAnnotations -> FilterPolicy.Throw.withReason(REASON_ANNOTATION)
+            in removeAnnotations -> FilterPolicy.Remove.withReason(REASON_ANNOTATION)
+            else -> null
         }
     }
 
-    fun findAnyAnnotation(
-            className: String,
-            anyAnnotations: Set<String>,
-            visibleAnnotations: List<AnnotationNode>?,
-            invisibleAnnotations: List<AnnotationNode>?,
-    ): AnnotationNode? {
-        val ret = findAnyAnnotation(anyAnnotations, visibleAnnotations, invisibleAnnotations)
-
-        if (ret != null) {
-            if (!annotationAllowedClassesFilter.matches(className)) {
-                throw InvalidAnnotationException(
-                        "Class ${className.toHumanReadableClassName()} is not allowed to have " +
-                                "Ravenwood annotations. Contact g/ravenwood for more details.")
-            }
-        }
-
-        return ret
-    }
-
-    /**
-     * Find a visibility annotation.
-     *
-     * name1 - 4 are only used in exception messages.
-     */
-    private fun findAnnotation(
-            className: String,
-            visibles: List<AnnotationNode>?,
-            invisibles: List<AnnotationNode>?,
-            type: String,
-            name1: String,
-            name2: String = "",
-            name3: String = "",
-    ): FilterPolicyWithReason? {
-        detectInvalidAnnotations(visibles, invisibles, type, name1, name2, name3)
-
-        findAnyAnnotation(className, stubAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.Stub.withReason(reasonAnnotation)
-        }
-        findAnyAnnotation(className, stubClassAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.StubClass.withReason(reasonClassAnnotation)
-        }
-        findAnyAnnotation(className, keepAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.Keep.withReason(reasonAnnotation)
-        }
-        findAnyAnnotation(className, keepClassAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.KeepClass.withReason(reasonClassAnnotation)
-        }
-        findAnyAnnotation(className, throwAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.Throw.withReason(reasonAnnotation)
-        }
-        findAnyAnnotation(className, removeAnnotations, visibles, invisibles)?.let {
-            return FilterPolicy.Remove.withReason(reasonAnnotation)
-        }
-
-        return null
+    private fun getAnnotationPolicy(cn: ClassNode): ClassAnnotations {
+        return policyCache.getOrPut(cn.name) { ClassAnnotations(cn) }
     }
 
     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
-        val cn = classes.getClass(className)
-
-        findAnnotation(
-            cn.name,
-            cn.visibleAnnotations,
-            cn.invisibleAnnotations,
-            "class",
-            className)?.let {
-            return it
-        }
-
         // If it's any of the annotations, then always keep it.
-        if (allAnnotations.contains(className)) {
+        if (allAnnotationClasses.contains(className)) {
             return FilterPolicy.KeepClass.withReason("HostStubGen Annotation")
         }
 
-        return super.getPolicyForClass(className)
+        val cn = classes.getClass(className)
+        return getAnnotationPolicy(cn).classPolicy ?: super.getPolicyForClass(className)
     }
 
-    override fun getPolicyForField(
-            className: String,
-            fieldName: String
-    ): FilterPolicyWithReason {
+    override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
         val cn = classes.getClass(className)
-
-        cn.fields?.firstOrNull { it.name == fieldName }?.let {fn ->
-            findAnnotation(
-                cn.name,
-                fn.visibleAnnotations,
-                fn.invisibleAnnotations,
-                "field",
-                className,
-                fieldName
-                )?.let { policy ->
-                // If the item has an annotation, then use it.
-                return policy
-            }
-        }
-        return super.getPolicyForField(className, fieldName)
+        return getAnnotationPolicy(cn).fieldPolicies[fieldName]
+            ?: super.getPolicyForField(className, fieldName)
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): FilterPolicyWithReason {
         val cn = classes.getClass(className)
 
         if (methodName == CLASS_INITIALIZER_NAME && descriptor == CLASS_INITIALIZER_DESC) {
-            findAnyAnnotation(cn.name, keepStaticInitializerAnnotations,
-                    cn.visibleAnnotations, cn.invisibleAnnotations)?.let {
-                return FilterPolicy.Keep.withReason(reasonAnnotation)
+            if (cn.findAnyAnnotation(keepStaticInitializerAnnotations) != null) {
+                return FilterPolicy.Keep.withReason(REASON_ANNOTATION)
             }
         }
 
-        cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
-            // @SubstituteWith is going to complicate the policy here, so we ask helper
-            // what to do.
-            substitutionHelper.getPolicyFromSubstitution(cn, mn.name, mn.desc)?.let {
-                return it
-            }
-
-            // If there's no substitution, then we check the annotation.
-            findAnnotation(
-                cn.name,
-                mn.visibleAnnotations,
-                mn.invisibleAnnotations,
-                "method",
-                className,
-                methodName,
-                descriptor
-            )?.let { policy ->
-                return policy
-            }
-        }
-        return super.getPolicyForMethod(className, methodName, descriptor)
+        return getAnnotationPolicy(cn).methodPolicies[MethodKey(methodName, descriptor)]
+            ?: super.getPolicyForMethod(className, methodName, descriptor)
     }
 
     override fun getRenameTo(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): String? {
         val cn = classes.getClass(className)
-
-        // If the method has a "substitute with" annotation, then return its "value" parameter.
-        cn.methods?.firstOrNull { it.name == methodName && it.desc == descriptor }?.let { mn ->
-            return substitutionHelper.getRenameTo(cn, mn.name, mn.desc)
-        }
-        return null
+        return getAnnotationPolicy(cn).renamedMethods[MethodKey(methodName, descriptor)]
+            ?: super.getRenameTo(className, methodName, descriptor)
     }
 
     override fun getNativeSubstitutionClass(className: String): String? {
         classes.getClass(className).let { cn ->
-            findAnyAnnotation(nativeSubstituteAnnotations,
-                    cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
+            cn.findAnyAnnotation(nativeSubstituteAnnotations)?.let { an ->
                 return getAnnotationField(an, "value")?.toJvmClassName()
             }
         }
@@ -295,8 +161,7 @@
 
     override fun getClassLoadHooks(className: String): List<String> {
         val e = classes.getClass(className).let { cn ->
-            findAnyAnnotation(classLoadHookAnnotations,
-                cn.visibleAnnotations, cn.invisibleAnnotations)?.let { an ->
+            cn.findAnyAnnotation(classLoadHookAnnotations)?.let { an ->
                 getAnnotationField(an, "value")?.toHumanReadableMethodName()
             }
         }
@@ -306,102 +171,130 @@
     private data class MethodKey(val name: String, val desc: String)
 
     /**
-     * In order to handle substitution, we need to build a reverse mapping of substitution
-     * methods.
+     * Every time we see a class, we scan all its methods for substitution attributes,
+     * and compute (implicit) policies caused by them.
      *
-     * This class automatically builds such a map internally that the above methods can
-     * take advantage of.
+     * For example, for the following methods:
+     *
+     *   @Substitute(suffix = "_host")
+     *   private void foo() {
+     *      // This isn't supported on the host side.
+     *   }
+     *   private void foo_host() {
+     *      // Host side implementation
+     *   }
+     *
+     * We internally handle them as:
+     *
+     *   foo() -> Substitute
+     *   foo_host() -> Stub, and then rename it to foo().
      */
-    private inner class SubstitutionHelper {
-        private var currentClass: ClassNode? = null
+    private inner class ClassAnnotations(cn: ClassNode) {
 
-        private var policiesFromSubstitution = mutableMapOf<MethodKey, FilterPolicyWithReason>()
-        private var substituteToMethods = mutableMapOf<MethodKey, String>()
+        val classPolicy: FilterPolicyWithReason?
+        val fieldPolicies = mutableMapOf<String, FilterPolicyWithReason>()
+        val methodPolicies = mutableMapOf<MethodKey, FilterPolicyWithReason>()
+        val renamedMethods = mutableMapOf<MethodKey, String>()
 
-        fun getPolicyFromSubstitution(cn: ClassNode, methodName: String, descriptor: String):
-                FilterPolicyWithReason? {
-            setClass(cn)
-            return policiesFromSubstitution[MethodKey(methodName, descriptor)]
-        }
+        init {
+            val allowAnnotation = annotationAllowedClassesFilter.matches(cn.name)
+            detectInvalidAnnotations(
+                cn.name, allowAnnotation,
+                cn.visibleAnnotations, cn.invisibleAnnotations,
+                "class", cn.name
+            )
+            classPolicy = cn.findAnyAnnotation(visibilityAnnotations)?.policy
 
-        fun getRenameTo(cn: ClassNode, methodName: String, descriptor: String): String? {
-            setClass(cn)
-            return substituteToMethods[MethodKey(methodName, descriptor)]
+            for (fn in cn.fields ?: emptyList()) {
+                detectInvalidAnnotations(
+                    cn.name, allowAnnotation,
+                    fn.visibleAnnotations, fn.invisibleAnnotations,
+                    "field", cn.name, fn.name
+                )
+                fn.findAnyAnnotation(visibilityAnnotations)?.policy?.let {
+                    fieldPolicies[fn.name] = it
+                }
+            }
+
+            for (mn in cn.methods ?: emptyList()) {
+                detectInvalidAnnotations(
+                    cn.name, allowAnnotation,
+                    mn.visibleAnnotations, mn.invisibleAnnotations,
+                    "method", cn.name, mn.name, mn.desc
+                )
+
+                val an = mn.findAnyAnnotation(visibilityAnnotations) ?: continue
+                val policy = an.policy ?: continue
+                methodPolicies[MethodKey(mn.name, mn.desc)] = policy
+
+                if (policy.policy != FilterPolicy.Substitute) continue
+
+                // Handle substitution
+                val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
+                val replacement = mn.name + suffix
+
+                if (replacement == mn.name) {
+                    errors.onErrorFound("@SubstituteWith require a different name")
+                } else {
+                    // The replacement method has to be renamed
+                    methodPolicies[MethodKey(replacement, mn.desc)] =
+                        FilterPolicy.Keep.withReason(REASON_ANNOTATION)
+                    renamedMethods[MethodKey(replacement, mn.desc)] = mn.name
+
+                    log.v("Substitution found: %s%s -> %s", replacement, mn.desc, mn.name)
+                }
+            }
         }
 
         /**
-         * Every time we see a different class, we scan all its methods for substitution attributes,
-         * and compute (implicit) policies caused by them.
+         * Throw if an item has more than one visibility annotations, or the class is not allowed
          *
-         * For example, for the following methods:
-         *
-         *   @Stub
-         *   @Substitute(suffix = "_host")
-         *   private void foo() {
-         *      // This isn't supported on the host side.
-         *   }
-         *   private void foo_host() {
-         *      // Host side implementation
-         *   }
-         *
-         * We internally handle them as:
-         *
-         *   foo() -> Remove
-         *   foo_host() -> Stub, and then rename it to foo().
+         * name1 - 4 are only used in exception messages. We take them as separate strings
+         * to avoid unnecessary string concatenations.
          */
-        private fun setClass(cn: ClassNode) {
-            if (currentClass == cn) {
-                return
-            }
-            // If the class is changing, we'll rebuild the internal structure.
-            currentClass = cn
-
-            policiesFromSubstitution.clear()
-            substituteToMethods.clear()
-
-            for (mn in cn.methods ?: emptyList()) {
-                findAnyAnnotation(substituteAnnotations,
-                        mn.visibleAnnotations,
-                        mn.invisibleAnnotations)?.let { an ->
-
-                    // Find the policy for this method.
-                    val policy = outermostFilter.getPolicyForMethod(cn.name, mn.name, mn.desc)
-                            .policy.resolveClassWidePolicy()
-                    // Make sure it's either Stub or Keep.
-                    if (!(policy.needsInStub || policy.needsInImpl)) {
-                        // TODO: Use the real annotation names in the message
-                        errors.onErrorFound("@SubstituteWith must have either @Stub or @Keep")
-                        return@let
-                    }
-                    if (!policy.isUsableWithMethods) {
-                        throw HostStubGenInternalException("Policy $policy shouldn't show up here")
-                    }
-
-                    val suffix = getAnnotationField(an, "suffix", false) ?: "\$ravenwood"
-                    val renameFrom = mn.name + suffix
-                    val renameTo = mn.name
-
-                    if (renameFrom == renameTo) {
-                        errors.onErrorFound("@SubstituteWith have a different name")
-                        return@let
-                    }
-
-                    // This mn has "SubstituteWith". This means,
-                    // 1. Re move the "rename-to" method, so add it to substitutedMethods.
-                    policiesFromSubstitution[MethodKey(renameTo, mn.desc)] =
-                            FilterPolicy.Remove.withReason("substitute-to")
-
-                    // If the policy is "stub", use "stub".
-                    // Otherwise, it must be "keep" or "throw", but there's no point in using
-                    // "throw", so let's use "keep".
-                    val newPolicy = if (policy.needsInStub) policy else FilterPolicy.Keep
-                    // 2. We also keep the from-to in the map.
-                    policiesFromSubstitution[MethodKey(renameFrom, mn.desc)] =
-                            newPolicy.withReason("substitute-from")
-                    substituteToMethods[MethodKey(renameFrom, mn.desc)] = renameTo
-
-                    log.v("Substitution found: %s%s -> %s", renameFrom, mn.desc, renameTo)
+        private fun detectInvalidAnnotations(
+            className: String,
+            allowAnnotation: Boolean,
+            visibles: List<AnnotationNode>?,
+            invisibles: List<AnnotationNode>?,
+            type: String,
+            name1: String,
+            name2: String = "",
+            name3: String = "",
+        ) {
+            var count = 0
+            var visibleCount = 0
+            for (an in visibles ?: emptyList()) {
+                if (visibilityAnnotations.contains(an.desc)) {
+                    visibleCount++
                 }
+                if (allAnnotations.contains(an.desc)) {
+                    count++
+                }
+            }
+            for (an in invisibles ?: emptyList()) {
+                if (visibilityAnnotations.contains(an.desc)) {
+                    visibleCount++
+                }
+                if (allAnnotations.contains(an.desc)) {
+                    count++
+                }
+            }
+            if (count > 0 && !allowAnnotation) {
+                throw InvalidAnnotationException(
+                    "Class ${className.toHumanReadableClassName()} is not allowed to have " +
+                            "Ravenwood annotations. Contact g/ravenwood for more details."
+                )
+            }
+            if (visibleCount > 1) {
+                val description = if (name2 == "" && name3 == "") {
+                    "$type $name1"
+                } else {
+                    "$type $name1.$name2$name3"
+                }
+                throw InvalidAnnotationException(
+                    "Found more than one visibility annotations on $description"
+                )
             }
         }
     }
@@ -409,8 +302,11 @@
     /**
      * Return the (String) value of 'value' parameter from an annotation.
      */
-    private fun getAnnotationField(an: AnnotationNode, name: String,
-                                   required: Boolean = true): String? {
+    private fun getAnnotationField(
+        an: AnnotationNode,
+        name: String,
+        required: Boolean = true
+    ): String? {
         try {
             val suffix = findAnnotationValueAsString(an, name)
             if (suffix == null && required) {
@@ -424,6 +320,9 @@
     }
 
     companion object {
+        private const val REASON_ANNOTATION = "annotation"
+        private const val REASON_CLASS_ANNOTATION = "class-annotation"
+
         /**
          * Convert from human-readable type names (e.g. "com.android.TypeName") to the internal type
          * names (e.g. "Lcom/android/TypeName).
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
index 47790b1..8ee3a94 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
@@ -16,7 +16,7 @@
 package com.android.hoststubgen.filters
 
 import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.asm.getDirectOuterClassName
+import com.android.hoststubgen.asm.isNative
 
 /**
  * This is used as the second last fallback filter. This filter propagates the class-wide policy
@@ -24,74 +24,80 @@
  */
 class ClassWidePolicyPropagatingFilter(
     private val classes: ClassNodes,
-    fallback: OutputFilter,
-    ) : DelegatingFilter(fallback) {
+    fallback: OutputFilter
+) : DelegatingFilter(fallback) {
 
-    private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
+    /**
+     * We don't use ClassNode.outerClass, because it gives as the top-level
+     * outer class (A$B$C -> A), not the direct outer class (A$B$C -> A$B).
+     *
+     * Sometimes a class name includes `$`, but is not as a nested class name separator
+     * (e.g. a class name like `MyClass$$`). In this case, `MyClass$` is not actually a class.
+     *
+     * So before getting the class policy on a nonexistent class, which may cause an
+     * incorrect result, we make sure the class actually exists.
+     */
+    private fun getDirectOuterClass(className: String): String? {
         var currentClass = className
-
-
-        // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy
-        // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A.
         while (true) {
-            // Sometimes a class name has a `$` in it but not as a nest class name separator --
-            // e.g. class name like "MyClass$$". In this case, `MyClass$` may not actually be
-            // a class name.
-            // So before getting the class policy on a nonexistent class, which may cause an
-            // incorrect result, we make sure if className actually exists.
-            if (classes.hasClass(className)) {
-                outermostFilter.getPolicyForClass(className).let { policy ->
-                    if (policy.policy.isClassWidePolicy) {
-                        val p = if (resolve) {
-                            policy.policy.resolveClassWidePolicy()
-                        } else {
-                            policy.policy
-                        }
-
-                        return p.withReason(policy.reason)
-                            .wrapReason("class-wide in $currentClass")
-                    }
-                    // If the class's policy is remove, then remove it.
-                    if (policy.policy == FilterPolicy.Remove) {
-                        return FilterPolicy.Remove.withReason("class-wide in $currentClass")
-                    }
-                }
-            }
-
-            // Next, look at the outer class...
-            val outer = getDirectOuterClassName(currentClass)
-            if (outer == null) {
+            val pos = currentClass.lastIndexOf('$')
+            if (pos < 0) {
                 return null
             }
-            currentClass = outer
+            currentClass = currentClass.substring(0, pos)
+            if (classes.hasClass(currentClass)) {
+                return currentClass
+            }
         }
     }
 
+    private fun getClassWidePolicy(className: String, resolve: Boolean): FilterPolicyWithReason? {
+        outermostFilter.getPolicyForClass(className).let { policy ->
+            if (policy.policy == FilterPolicy.KeepClass) {
+                val p = if (resolve) {
+                    policy.policy.resolveClassWidePolicy()
+                } else {
+                    policy.policy
+                }
+
+                return p.withReason(policy.reason)
+                    .wrapReason("class-wide in $className")
+            }
+            // If the class's policy is remove, then remove it.
+            if (policy.policy == FilterPolicy.Remove) {
+                return FilterPolicy.Remove.withReason("class-wide in $className")
+            }
+        }
+        return null
+    }
+
     override fun getPolicyForClass(className: String): FilterPolicyWithReason {
-        // If it's a nested class, use the outer class's policy.
-        getDirectOuterClassName(className)?.let { outerName ->
-            getClassWidePolicy(outerName, resolve = false)?.let { policy ->
-                return policy
-            }
-        }
-
-        return super.getPolicyForClass(className)
+        // If the class name is `a.b.c.A$B$C`, then we try to get the class wide policy
+        // from a.b.c.A$B$C, then a.b.c.A$B, and then a.b.c.A, recursively
+        return getDirectOuterClass(className)?.let { getClassWidePolicy(it, resolve = false) }
+            ?: super.getPolicyForClass(className)
     }
 
-    override fun getPolicyForField(
-            className: String,
-            fieldName: String
-    ): FilterPolicyWithReason {
+    override fun getPolicyForField(className: String, fieldName: String): FilterPolicyWithReason {
         return getClassWidePolicy(className, resolve = true)
                 ?: super.getPolicyForField(className, fieldName)
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String
+        className: String,
+        methodName: String,
+        descriptor: String
     ): FilterPolicyWithReason {
-        return getClassWidePolicy(className, resolve = true)
-                ?: super.getPolicyForMethod(className, methodName, descriptor)
+        return outermostFilter.getNativeSubstitutionClass(className)?.let {
+            // First check native substitution
+            classes.findMethod(className, methodName, descriptor)?.let { mn ->
+                if (mn.isNative()) {
+                    FilterPolicy.NativeSubstitute.withReason("class-wide in $className")
+                } else {
+                    null
+                }
+            }
+        } ?: getClassWidePolicy(className, resolve = true)
+        ?: super.getPolicyForMethod(className, methodName, descriptor)
     }
 }
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
index 678e6ea..be3c59c 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ConstantFilter.kt
@@ -26,23 +26,17 @@
  * @param policy the policy. Cannot be a "substitute" policy.
  */
 class ConstantFilter(
-        policy: FilterPolicy,
-        val reason: String
+    policy: FilterPolicy,
+    private val reason: String
 ) : OutputFilter() {
-    val classPolicy: FilterPolicy
-    val fieldPolicy: FilterPolicy
-    val methodPolicy: FilterPolicy
+
+    private val classPolicy: FilterPolicy
+    private val fieldPolicy: FilterPolicy
+    private val methodPolicy: FilterPolicy
 
     init {
-        if (policy.isSubstitute) {
-            throw HostStubGenInternalException(
-                    "ConstantFilter doesn't allow substitution policies.")
-        }
-        if (policy.isClassWidePolicy) {
-            // We prevent it, because there's no point in using class-wide policies because
-            // all members get othe same policy too anyway.
-            throw HostStubGenInternalException(
-                    "ConstantFilter doesn't allow class-wide policies.")
+        if (!policy.isUsableWithDefault) {
+            throw HostStubGenInternalException("ConstantFilter doesn't support $policy.")
         }
         methodPolicy = policy
 
@@ -63,10 +57,10 @@
     }
 
     override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String,
-            ): FilterPolicyWithReason {
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): FilterPolicyWithReason {
         return methodPolicy.withReason(reason)
     }
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
index cdd24e8..6fcffb8 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/DelegatingFilter.kt
@@ -87,4 +87,23 @@
     ): List<String> {
         return fallback.getMethodCallHooks(className, methodName, descriptor)
     }
+
+    override fun remapType(className: String): String? {
+        return fallback.remapType(className)
+    }
+
+    override fun hasAnyMethodCallReplace(): Boolean {
+        return fallback.hasAnyMethodCallReplace()
+    }
+
+    override fun getMethodCallReplaceTo(
+        callerClassName: String,
+        callerMethodName: String,
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): MethodReplaceTarget? {
+        return fallback.getMethodCallReplaceTo(
+            callerClassName, callerMethodName, className, methodName, descriptor)
+    }
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
index 4d21106..ab03874 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicy.kt
@@ -17,37 +17,25 @@
 
 enum class FilterPolicy {
     /**
-     * Keep the item in the stub jar file, so tests can use it.
-     */
-    Stub,
-
-    /**
-     * Keep the item in the impl jar file, but not in the stub file. Tests cannot use it directly,
-     * but indirectly.
+     * Keep the item in the jar file.
      */
     Keep,
 
     /**
-     * Only used for types. Keep the class in the stub, and also all its members.
-     * But each member can have another annotations to override it.
-     */
-    StubClass,
-
-    /**
-     * Only used for types. Keep the class in the impl, not in the stub, and also all its members.
-     * But each member can have another annotations to override it.
+     * Only usable with classes. Keep the class in the jar, and also all its members.
+     * Each member can have another policy to override it.
      */
     KeepClass,
 
     /**
-     * Same as [Stub], but replace it with a "substitution" method. Only usable with methods.
+     * Only usable with methods. Replace a method with a "substitution" method.
      */
-    SubstituteAndStub,
+    Substitute,
 
     /**
-     * Same as [Keep], but replace it with a "substitution" method. Only usable with methods.
+     * Only usable with methods. Replace a native method with a "substitution" method,
      */
-    SubstituteAndKeep,
+    NativeSubstitute,
 
     /**
      * Only usable with methods. The item will be kept in the impl jar file, but when called,
@@ -57,7 +45,7 @@
 
     /**
      * Only usable with methods. The item will be kept in the impl jar file, but when called,
-     * it'll no-op.  Currently only supported for methods returning `void`.
+     * it'll no-op.
      */
     Ignore,
 
@@ -66,20 +54,19 @@
      */
     Remove;
 
-    val isSubstitute: Boolean
-        get() = this == SubstituteAndStub || this == SubstituteAndKeep
-
-    val needsInStub: Boolean
-        get() = this == Stub || this == StubClass || this == SubstituteAndStub
-
-    val needsInImpl: Boolean
-        get() = this != Remove
+    val needsInOutput: Boolean
+        get() {
+            return when (this) {
+                Remove -> false
+                else -> true
+            }
+        }
 
     /** Returns whether a policy can be used with classes */
     val isUsableWithClasses: Boolean
         get() {
             return when (this) {
-                Stub, StubClass, Keep, KeepClass, Remove -> true
+                Keep, KeepClass, Remove -> true
                 else -> false
             }
         }
@@ -88,7 +75,7 @@
     val isUsableWithFields: Boolean
         get() {
             return when (this) {
-                Stub, Keep, Remove -> true
+                Keep, Remove -> true
                 else -> false
             }
         }
@@ -97,16 +84,16 @@
     val isUsableWithMethods: Boolean
         get() {
             return when (this) {
-                StubClass, KeepClass -> false
+                KeepClass -> false
                 else -> true
             }
         }
 
-    /** Returns whether a policy is a class-wide one. */
-    val isClassWidePolicy: Boolean
+    /** Returns whether a policy can be used as default policy. */
+    val isUsableWithDefault: Boolean
         get() {
             return when (this) {
-                StubClass, KeepClass -> true
+                Keep, Throw, Remove -> true
                 else -> false
             }
         }
@@ -116,25 +103,24 @@
         get() {
             return when (this) {
                 // TODO: handle native method with no substitution as being unsupported
-                Stub, StubClass, Keep, KeepClass, SubstituteAndStub, SubstituteAndKeep -> true
+                Keep, KeepClass, Substitute, NativeSubstitute -> true
                 else -> false
             }
         }
 
-    fun getSubstitutionBasePolicy(): FilterPolicy {
-        return when (this) {
-            SubstituteAndKeep -> Keep
-            SubstituteAndStub -> Stub
-            else -> this
+    val isMethodRewriteBody: Boolean
+        get() {
+            return when (this) {
+                NativeSubstitute, Throw, Ignore -> true
+                else -> false
+            }
         }
-    }
 
     /**
-     * Convert {Stub,Keep}Class to the corresponding Stub or Keep.
+     * Convert KeepClass to Keep, or return itself.
      */
     fun resolveClassWidePolicy(): FilterPolicy {
         return when (this) {
-            StubClass -> Stub
             KeepClass -> Keep
             else -> this
         }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
index eb03f66..b10165b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
@@ -30,36 +30,6 @@
         return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]")
     }
 
-    /**
-     * If the visibility is lower than "Keep" (meaning if it's "remove"),
-     * then return a new [FilterPolicy] with "Keep".
-     * Otherwise, return itself
-     */
-    fun promoteToKeep(promotionReason: String): FilterPolicyWithReason {
-        if (policy.needsInImpl) {
-            return this
-        }
-        val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
-
-        return FilterPolicyWithReason(newPolicy,
-                "$promotionReason [original remove reason: ${this.reason}]")
-    }
-
-    /**
-     * If the visibility is above "Keep" (meaning if it's "stub"),
-     * then return a new [FilterPolicy] with "Keep".
-     * Otherwise, return itself
-     */
-    fun demoteToKeep(promotionReason: String): FilterPolicyWithReason {
-        if (!policy.needsInStub) {
-            return this
-        }
-        val newPolicy = if (policy.isClassWidePolicy) FilterPolicy.KeepClass else FilterPolicy.Keep
-
-        return FilterPolicyWithReason(newPolicy,
-                "$promotionReason [original stub reason: ${this.reason}]")
-    }
-
     override fun toString(): String {
         return "[$policy - reason: $reason]"
     }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.kt
new file mode 100644
index 0000000..c5a2f9f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/FilterRemapper.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.hoststubgen.filters
+
+import org.objectweb.asm.commons.Remapper
+
+/**
+ * A [Remapper] that uses [OutputFilter.remapType]
+ */
+class FilterRemapper(val filter: OutputFilter) : Remapper() {
+    private val cache = mutableMapOf<String, String>()
+
+    override fun mapType(typeInternalName: String?): String? {
+        if (typeInternalName == null) {
+            return null
+        }
+
+        cache[typeInternalName]?.let {
+            return it
+        }
+
+        var mapped = filter.remapType(typeInternalName) ?: typeInternalName
+        cache[typeInternalName] = mapped
+        return mapped
+    }
+
+    // TODO Do we need to implement mapPackage(), etc too?
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index 5a26fc6..474da6d 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -25,7 +25,6 @@
 import com.android.hoststubgen.asm.isAutoGeneratedEnumMember
 import com.android.hoststubgen.asm.isEnum
 import com.android.hoststubgen.asm.isSynthetic
-import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
 import com.android.hoststubgen.log
 import org.objectweb.asm.tree.ClassNode
 
@@ -53,7 +52,7 @@
             }
             // If the outer class needs to be in impl, it should be in impl too.
             val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
-            if (outerPolicy.policy.needsInImpl) {
+            if (outerPolicy.policy.needsInOutput) {
                 return FilterPolicy.KeepClass.withReason("anonymous-inner-class")
             }
         }
@@ -79,19 +78,6 @@
         val fallback = super.getPolicyForMethod(className, methodName, descriptor)
         val classPolicy = outermostFilter.getPolicyForClass(className)
 
-        // If the class is in the stub, then we need to put the private constructor in the stub too,
-        // to prevent the class from getting instantiated.
-        if (classPolicy.policy.needsInStub &&
-                !fallback.policy.needsInStub &&
-                (methodName == "<init>") && // Constructor?
-                (descriptor == "()V")) { // Has zero parameters?
-            classes.findMethod(className, methodName, descriptor)?.let { mn ->
-                if (isVisibilityPrivateOrPackagePrivate(mn.access)) {
-                    return FilterPolicy.Stub.withReason("private constructor in stub class")
-                }
-            }
-        }
-
         val cn = classes.getClass(className)
 
         // If we throw from the static initializer, the class would be useless, so we convert it
@@ -107,7 +93,7 @@
         }
 
         log.d("Class ${cn.name} Class policy: $classPolicy")
-        if (classPolicy.policy.needsInImpl) {
+        if (classPolicy.policy.needsInOutput) {
             // Do it only when the class needs to be kept...
 
             // Member policy should be "keep" or "stub".
@@ -152,7 +138,7 @@
         val classPolicy = outermostFilter.getPolicyForClass(className)
 
         log.d("Class ${cn.name} Class policy: $classPolicy")
-        if (classPolicy.policy.needsInImpl) {
+        if (classPolicy.policy.needsInOutput) {
             // Do it only when the class needs to be kept...
 
             // Member policy should be "keep" or "stub".
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/NativeFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/NativeFilter.kt
new file mode 100644
index 0000000..bd71931
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/NativeFilter.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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.isNative
+
+class NativeFilter(
+    private val classes: ClassNodes,
+    fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+    override fun getPolicyForMethod(
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): FilterPolicyWithReason {
+        return classes.findMethod(className, methodName, descriptor)?.let { mn ->
+            // For native methods that weren't handled by outer filters,
+            // we keep it so that native method registration will not crash.
+            if (mn.isNative()) {
+                FilterPolicy.Keep.withReason("native-preserve")
+            } else {
+                null
+            }
+        } ?: super.getPolicyForMethod(className, methodName, descriptor)
+    }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
index 3df16ff..1049e2b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/OutputFilter.kt
@@ -89,4 +89,35 @@
             List<String> {
         return emptyList()
     }
-}
\ No newline at end of file
+
+    /**
+     * Take a class (internal) name. If the class needs to be renamed, return the new name.
+     * This is used by [FilterRemapper].
+     */
+    open fun remapType(className: String): String? {
+        return null
+    }
+
+    data class MethodReplaceTarget(val className: String, val methodName: String)
+
+    /**
+     * Return if this filter may return non-null from [getMethodCallReplaceTo].
+     * (Used for a small optimization)
+     */
+    open fun hasAnyMethodCallReplace(): Boolean {
+        return false
+    }
+
+    /**
+     * If a method call should be forwarded to another method, return the target's class / method.
+     */
+    open fun getMethodCallReplaceTo(
+        callerClassName: String,
+        callerMethodName: String,
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): MethodReplaceTarget? {
+        return null
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.kt
deleted file mode 100644
index f92a027..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/StubIntersectingFilter.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.hoststubgen.filters
-
-import com.android.hoststubgen.HostStubGenErrors
-import com.android.hoststubgen.asm.ClassNodes
-
-private const val REASON = "demoted, not in intersect jars"
-
-/**
- * An [OutputFilter] that will restrict what to put in stub to only what shows up in "intersecting
- * jar" files.
- *
- * For example, if the Android public API stub jar is provided, then the HostStubGen's output
- * stub will be restricted to public APIs.
- */
-class StubIntersectingFilter(
-        private val errors: HostStubGenErrors,
-        /**
-         * If a class / field / method is not in any of these jars, then we will not put it in
-         * stub.
-         */
-        private val intersectingJars: Map<String, ClassNodes>,
-        fallback: OutputFilter,
-) : DelegatingFilter(fallback) {
-    private inline fun exists(predicate: (ClassNodes) -> Boolean): Boolean {
-        intersectingJars.forEach { entry ->
-            if (predicate(entry.value)) {
-                return true
-            }
-        }
-        return false
-    }
-
-    /**
-     * If [origPolicy] is less than "Stub", then return it as-is.
-     *
-     * Otherwise, call [inStubChecker] to see if the API is in any of [intersectingJars].
-     * If yes, then return [origPolicy] as-is. Otherwise, demote to "Keep".
-     */
-    private fun intersectWithStub(
-            origPolicy: FilterPolicyWithReason,
-            inStubChecker: () -> Boolean,
-    ): FilterPolicyWithReason {
-        if (origPolicy.policy.needsInStub) {
-            // Only check the stub jars, when the class is supposed to be in stub otherwise.
-            if (!inStubChecker()) {
-                return origPolicy.demoteToKeep(REASON)
-            }
-        }
-        return origPolicy
-    }
-
-    override fun getPolicyForClass(className: String): FilterPolicyWithReason {
-        return intersectWithStub(super.getPolicyForClass(className)) {
-            exists { classes -> classes.findClass(className) != null }
-        }
-    }
-
-    override fun getPolicyForField(
-            className: String,
-            fieldName: String
-    ): FilterPolicyWithReason {
-        return intersectWithStub(super.getPolicyForField(className, fieldName)) {
-            exists { classes -> classes.findField(className, fieldName) != null }
-        }
-    }
-
-    override fun getPolicyForMethod(
-            className: String,
-            methodName: String,
-            descriptor: String
-    ): FilterPolicyWithReason {
-        return intersectWithStub(super.getPolicyForMethod(className, methodName, descriptor)) {
-            exists { classes -> classes.findMethod(className, methodName, descriptor) != null }
-        }
-    }
-}
\ 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
index 1828003..14fd82b 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -17,12 +17,13 @@
 
 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.commons.Remapper
 import org.objectweb.asm.tree.ClassNode
 import java.io.BufferedReader
 import java.io.FileReader
@@ -62,7 +63,7 @@
         filename: String,
         classes: ClassNodes,
         fallback: OutputFilter,
-        ): Pair<OutputFilter, Remapper?> {
+        ): OutputFilter {
     log.i("Loading offloaded annotations from $filename ...")
     log.withIndent {
         val subclassFilter = SubclassFilter(classes, fallback)
@@ -75,7 +76,9 @@
         var featureFlagsPolicy: FilterPolicyWithReason? = null
         var syspropsPolicy: FilterPolicyWithReason? = null
         var rFilePolicy: FilterPolicyWithReason? = null
-        val typeRenameSpec = mutableListOf<TextFilePolicyRemapper.TypeRenameSpec>()
+        val typeRenameSpec = mutableListOf<TextFilePolicyRemapperFilter.TypeRenameSpec>()
+        val methodReplaceSpec =
+            mutableListOf<TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec>()
 
         try {
             BufferedReader(FileReader(filename)).use { reader ->
@@ -237,7 +240,7 @@
 
                             imf.setPolicyForMethod(className, name, signature,
                                     policy.withReason(FILTER_REASON))
-                            if (policy.isSubstitute) {
+                            if (policy == FilterPolicy.Substitute) {
                                 val fromName = fields[3].substring(1)
 
                                 if (fromName == name) {
@@ -245,13 +248,28 @@
                                             "Substitution must have a different name")
                                 }
 
-                                // Set the policy  for the "from" method.
+                                // Set the policy for the "from" method.
                                 imf.setPolicyForMethod(className, fromName, signature,
-                                        policy.getSubstitutionBasePolicy()
-                                                .withReason(FILTER_REASON))
+                                    FilterPolicy.Keep.withReason(FILTER_REASON))
 
-                                // Keep "from" -> "to" mapping.
-                                imf.setRenameTo(className, fromName, signature, name)
+                                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" -> {
@@ -267,7 +285,7 @@
                             // applied. (Which is needed for services.jar)
                             val prefix = fields[2].trimStart('/')
 
-                            typeRenameSpec += TextFilePolicyRemapper.TypeRenameSpec(
+                            typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
                                 pattern, prefix)
                         }
 
@@ -281,16 +299,19 @@
             throw e.withSourceInfo(filename, lineNo)
         }
 
-        var remapper: TextFilePolicyRemapper? = null
+        var ret: OutputFilter = imf
         if (typeRenameSpec.isNotEmpty()) {
-            remapper = TextFilePolicyRemapper(typeRenameSpec)
+            ret = TextFilePolicyRemapperFilter(typeRenameSpec, ret)
+        }
+        if (methodReplaceSpec.isNotEmpty()) {
+            ret = TextFilePolicyMethodReplaceFilter(methodReplaceSpec, classes, ret)
         }
 
         // Wrap the in-memory-filter with AHF.
-        return Pair(
-            AndroidHeuristicsFilter(
-                classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf),
-            remapper)
+        ret = AndroidHeuristicsFilter(
+                classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, ret)
+
+        return ret
     }
 }
 
@@ -324,17 +345,14 @@
 
 private fun parsePolicy(s: String): FilterPolicy {
     return when (s.lowercase()) {
-        "s", "stub" -> FilterPolicy.Stub
         "k", "keep" -> FilterPolicy.Keep
         "t", "throw" -> FilterPolicy.Throw
         "r", "remove" -> FilterPolicy.Remove
-        "sc", "stubclass" -> FilterPolicy.StubClass
         "kc", "keepclass" -> FilterPolicy.KeepClass
+        "i", "ignore" -> FilterPolicy.Ignore
         else -> {
             if (s.startsWith("@")) {
-                FilterPolicy.SubstituteAndStub
-            } else if (s.startsWith("%")) {
-                FilterPolicy.SubstituteAndKeep
+                FilterPolicy.Substitute
             } else {
                 throw ParseException("Invalid policy \"$s\"")
             }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
new file mode 100644
index 0000000..d45f414
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+
+/**
+ * Filter used by TextFileFilterPolicyParser for "method call relacement".
+ */
+class TextFilePolicyMethodReplaceFilter(
+    val spec: List<MethodCallReplaceSpec>,
+    val classes: ClassNodes,
+    val fallback: OutputFilter,
+) : DelegatingFilter(fallback) {
+
+    data class MethodCallReplaceSpec(
+        val fromClass: String,
+        val fromMethod: String,
+        val fromDescriptor: String,
+        val toClass: String,
+        val toMethod: String,
+    )
+
+    override fun hasAnyMethodCallReplace(): Boolean {
+        return true
+    }
+
+    override fun getMethodCallReplaceTo(
+        callerClassName: String,
+        callerMethodName: String,
+        className: String,
+        methodName: String,
+        descriptor: String,
+    ): MethodReplaceTarget? {
+        // Maybe use 'Tri' if we end up having too many replacements.
+        spec.forEach {
+            if (className == it.fromClass &&
+                methodName == it.fromMethod &&
+                descriptor == it.fromDescriptor
+                ) {
+                return MethodReplaceTarget(it.toClass, it.toMethod)
+            }
+        }
+        return null
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt
deleted file mode 100644
index 2d94bb4..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapper.kt
+++ /dev/null
@@ -1,59 +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.hoststubgen.filters
-
-import com.android.hoststubgen.log
-import org.objectweb.asm.commons.Remapper
-import java.util.regex.Pattern
-
-/**
- * A [Remapper] that provides a simple "jarjar" functionality.
- */
-class TextFilePolicyRemapper(
-    val typeRenameSpecs: List<TypeRenameSpec>
-) : Remapper() {
-    /**
-     * When a package name matches [typeInternalNamePattern], we prepend [typeInternalNamePrefix]
-     * to it.
-     */
-    data class TypeRenameSpec(
-        val typeInternalNamePattern: Pattern,
-        val typeInternalNamePrefix: String,
-    )
-
-    private val cache = mutableMapOf<String, String>()
-
-    override fun mapType(typeInternalName: String): String {
-//        if (typeInternalName == null) {
-//            return null // do we need it??
-//        }
-        cache[typeInternalName]?.let {
-            return it
-        }
-
-        var mapped: String = typeInternalName
-        typeRenameSpecs.forEach {
-            if (it.typeInternalNamePattern.matcher(typeInternalName).matches()) {
-                mapped = it.typeInternalNamePrefix + typeInternalName
-                log.d("Renaming type $typeInternalName to $mapped")
-            }
-        }
-        cache[typeInternalName] = mapped
-        return mapped
-    }
-
-    // TODO Do we need to implement mapPackage(), etc too?
-}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.kt
new file mode 100644
index 0000000..a78c655
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyRemapperFilter.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.hoststubgen.filters
+
+import com.android.hoststubgen.log
+import java.util.regex.Pattern
+
+/**
+ * A filter that provides a simple "jarjar" functionality via [mapType]
+ */
+class TextFilePolicyRemapperFilter(
+    val typeRenameSpecs: List<TypeRenameSpec>,
+    fallback: OutputFilter,
+) : DelegatingFilter(fallback) {
+    /**
+     * When a package name matches [typeInternalNamePattern], we prepend [typeInternalNamePrefix]
+     * to it.
+     */
+    data class TypeRenameSpec(
+        val typeInternalNamePattern: Pattern,
+        val typeInternalNamePrefix: String,
+    )
+
+    private val cache = mutableMapOf<String, String>()
+
+    override fun remapType(className: String): String? {
+        var mapped: String = className
+        typeRenameSpecs.forEach {
+            if (it.typeInternalNamePattern.matcher(className).matches()) {
+                mapped = it.typeInternalNamePrefix + className
+                log.d("Renaming type $className to $mapped")
+            }
+        }
+        cache[className] = mapped
+        return mapped
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
index 01a7ab3..7440b94 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/utils/ClassFilter.kt
@@ -24,19 +24,19 @@
 /**
  * General purpose filter for class names.
  */
-class ClassFilter private constructor (
-        val defaultResult: Boolean,
+class ClassFilter private constructor(
+    private val defaultResult: Boolean,
 ) {
-    private data class FilterElement(
-            val allowed: Boolean,
-            val internalName: String,
-            val isPrefix: Boolean,
+    private class FilterElement(
+        val allowed: Boolean,
+        val internalName: String,
+        val isPrefix: Boolean,
     ) {
         fun matches(classInternalName: String): Boolean {
-            if (isPrefix) {
-                return classInternalName.startsWith(internalName)
+            return if (isPrefix) {
+                classInternalName.startsWith(internalName)
             } else {
-                return classInternalName == internalName
+                classInternalName == internalName
             }
         }
     }
@@ -54,15 +54,16 @@
             return it
         }
 
-        var result = defaultResult
-        run outer@{
-            elements.forEach { e ->
-                if (e.matches(classInternalName)) {
-                    result = e.allowed
-                    return@outer // break equivalent.
-                }
+        val testClasses = sequence {
+            // Yield itself and its outer class(es) one by one
+            var idx = classInternalName.length
+            while (idx > 0) {
+                yield(classInternalName.substring(0, idx))
+                idx = classInternalName.lastIndexOf('$', idx - 1)
             }
         }
+
+        val result = elements.find { testClasses.any(it::matches) }?.allowed ?: defaultResult
         cache[classInternalName] = result
 
         return result
@@ -87,9 +88,9 @@
 
         /** Build a filter from a string (for unit tests). */
         fun buildFromString(
-                filterString: String,
-                defaultResult: Boolean,
-                filenameForErrorMessage: String
+            filterString: String,
+            defaultResult: Boolean,
+            filenameForErrorMessage: String
         ): ClassFilter {
             val ret = ClassFilter(defaultResult)
 
@@ -119,17 +120,20 @@
 
                 // Handle wildcard -- e.g. "package.name.*"
                 if (line.endsWith(".*")) {
-                    ret.elements.add(FilterElement(
-                            allow, line.substring(0, line.length - 2).toJvmClassName(), true))
+                    ret.elements.add(
+                        FilterElement(
+                            allow, line.substring(0, line.length - 2).toJvmClassName(), true
+                        )
+                    )
                     return@forEach
                 }
 
                 // Any other uses of "*" would be an error.
                 if (line.contains('*')) {
                     throw ParseException(
-                            "Wildcard (*) can only show up as the last element",
-                            filenameForErrorMessage,
-                            lineNo
+                        "Wildcard (*) can only show up as the last element",
+                        filenameForErrorMessage,
+                        lineNo
                     )
                 }
                 ret.elements.add(FilterElement(allow, line.toJvmClassName(), false))
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index bad0449..41ba928 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -26,67 +26,47 @@
 import com.android.hoststubgen.filters.FilterPolicy
 import com.android.hoststubgen.filters.FilterPolicyWithReason
 import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-import com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+import com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
 import com.android.hoststubgen.log
+import java.io.PrintWriter
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.FieldVisitor
 import org.objectweb.asm.MethodVisitor
 import org.objectweb.asm.Opcodes
 import org.objectweb.asm.commons.ClassRemapper
-import org.objectweb.asm.commons.Remapper
 import org.objectweb.asm.util.TraceClassVisitor
-import java.io.PrintWriter
 
-val OPCODE_VERSION = Opcodes.ASM9
+const val OPCODE_VERSION = Opcodes.ASM9
 
-abstract class BaseAdapter (
-        protected val classes: ClassNodes,
-        nextVisitor: ClassVisitor,
-        protected val filter: OutputFilter,
-        protected val options: Options,
+abstract class BaseAdapter(
+    protected val classes: ClassNodes,
+    nextVisitor: ClassVisitor,
+    protected val filter: OutputFilter,
+    protected val options: Options,
 ) : ClassVisitor(OPCODE_VERSION, nextVisitor) {
 
     /**
      * Options to control the behavior.
      */
-    data class Options (
-            val errors: HostStubGenErrors,
-            val stats: HostStubGenStats?,
-            val enablePreTrace: Boolean,
-            val enablePostTrace: Boolean,
-            val enableNonStubMethodCallDetection: Boolean,
-            )
+    data class Options(
+        val errors: HostStubGenErrors,
+        val stats: HostStubGenStats?,
+        val enablePreTrace: Boolean,
+        val enablePostTrace: Boolean
+    )
 
     protected lateinit var currentPackageName: String
     protected lateinit var currentClassName: String
     protected var nativeSubstitutionClass: String? = null
     protected lateinit var classPolicy: FilterPolicyWithReason
 
-    /**
-     * Return whether an item with a given policy should be included in the output.
-     */
-    protected abstract fun shouldEmit(policy: FilterPolicy): Boolean
-
-    /**
-     * Inject [HostStubGenKeptInStub] and [HostStubGenKeptInImpl] as needed to an item.
-     */
-    protected fun injectInStubAndKeepAnnotations(policy: FilterPolicy, v: UnifiedVisitor) {
-        if (policy.needsInStub) {
-            v.visitAnnotation(HostStubGenKeptInStub.CLASS_DESCRIPTOR, true)
-        }
-        if (policy.needsInImpl) {
-            v.visitAnnotation(HostStubGenKeptInImpl.CLASS_DESCRIPTOR, true)
-        }
-    }
-
     override fun visit(
-            version: Int,
-            access: Int,
-            name: String,
-            signature: String?,
-            superName: String?,
-            interfaces: Array<String>,
+        version: Int,
+        access: Int,
+        name: String,
+        signature: String?,
+        superName: String?,
+        interfaces: Array<String>,
     ) {
         super.visit(version, access, name, signature, superName, interfaces)
         currentClassName = name
@@ -103,21 +83,25 @@
                 .toJvmClassName()
             log.d("  NativeSubstitutionClass: $fullClassName")
             if (classes.findClass(fullClassName) == null) {
-                log.w("Native substitution class $fullClassName not found. Class must be " +
-                        "available at runtime.")
+                log.w(
+                    "Native substitution class $fullClassName not found. Class must be " +
+                            "available at runtime."
+                )
             } else {
                 // If the class exists, it must have a KeepClass policy.
                 if (filter.getPolicyForClass(fullClassName).policy != FilterPolicy.KeepClass) {
                     // TODO: Use real annotation name.
                     options.errors.onErrorFound(
-                            "Native substitution class $fullClassName should have @Keep.")
+                        "Native substitution class $fullClassName should have @Keep."
+                    )
                 }
             }
 
             nativeSubstitutionClass = fullClassName
         }
+
         // Inject annotations to generated classes.
-        injectInStubAndKeepAnnotations(classPolicy.policy, UnifiedVisitor.on(this))
+        UnifiedVisitor.on(this).visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
     }
 
     override fun visitEnd() {
@@ -141,11 +125,11 @@
     }
 
     override fun visitField(
-            access: Int,
-            name: String,
-            descriptor: String,
-            signature: String?,
-            value: Any?,
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        value: Any?,
     ): FieldVisitor? {
         if (skipMemberModificationNestCount > 0) {
             return super.visitField(access, name, descriptor, signature, value)
@@ -154,7 +138,7 @@
         log.d("visitField: %s %s [%x] Policy: %s", name, descriptor, access, policy)
 
         log.withIndent {
-            if (!shouldEmit(policy.policy)) {
+            if (policy.policy == FilterPolicy.Remove) {
                 log.d("Removing %s %s", name, policy)
                 return null
             }
@@ -162,18 +146,19 @@
             log.v("Emitting field: %s %s %s", name, descriptor, policy)
             val ret = super.visitField(access, name, descriptor, signature, value)
 
-            injectInStubAndKeepAnnotations(policy.policy, UnifiedVisitor.on(ret))
+            UnifiedVisitor.on(ret)
+                .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
 
             return ret
         }
     }
 
     override fun visitMethod(
-            access: Int,
-            name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        exceptions: Array<String>?,
     ): MethodVisitor? {
         if (skipMemberModificationNestCount > 0) {
             return super.visitMethod(access, name, descriptor, signature, exceptions)
@@ -187,11 +172,11 @@
             // Instead of this method, we rename the substitute-to method with the original
             // name, in the "Maybe rename the method" part below.
             val policy = filter.getPolicyForMethod(currentClassName, name, descriptor)
-            if (policy.policy.isSubstitute) {
+            if (policy.policy == FilterPolicy.Substitute) {
                 log.d("Skipping %s%s %s", name, descriptor, policy)
                 return null
             }
-            if (!shouldEmit(p.policy)) {
+            if (p.policy == FilterPolicy.Remove) {
                 log.d("Removing %s%s %s", name, descriptor, policy)
                 return null
             }
@@ -209,13 +194,16 @@
                 // `name` is the name of the method we're currently visiting, so it's usually a
                 // "...$ravewnwood" name.
                 newAccess = checkSubstitutionMethodCompatibility(
-                        classes, currentClassName, newName, name, descriptor, options.errors)
+                    classes, currentClassName, newName, name, descriptor, options.errors
+                )
                 if (newAccess == NOT_COMPATIBLE) {
                     return null
                 }
 
-                log.v("Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
-                        newName, policy)
+                log.v(
+                    "Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
+                    newName, policy
+                )
             } else {
                 log.v("Emitting method: %s%s %s", name, descriptor, policy)
                 newName = name
@@ -225,14 +213,17 @@
             // But note, we only use it when calling the super's method,
             // but not for visitMethodInner(), because when subclass wants to change access,
             // it can do so inside visitMethodInner().
-            newAccess = updateAccessFlags(newAccess, name, descriptor)
+            newAccess = updateAccessFlags(newAccess, name, descriptor, policy.policy)
 
-            val ret = visitMethodInner(access, newName, descriptor, signature, exceptions, policy,
+            val ret = visitMethodInner(
+                access, newName, descriptor, signature, exceptions, policy,
                 renameTo != null,
-                super.visitMethod(newAccess, newName, descriptor, signature, exceptions))
+                super.visitMethod(newAccess, newName, descriptor, signature, exceptions)
+            )
 
             ret?.let {
-                injectInStubAndKeepAnnotations(policy.policy, UnifiedVisitor.on(ret))
+                UnifiedVisitor.on(ret)
+                    .visitAnnotation(HostStubGenProcessedAsKeep.CLASS_DESCRIPTOR, true)
             }
 
             return ret
@@ -240,9 +231,10 @@
     }
 
     open fun updateAccessFlags(
-            access: Int,
-            name: String,
-            descriptor: String,
+        access: Int,
+        name: String,
+        descriptor: String,
+        policy: FilterPolicy,
     ): Int {
         return access
     }
@@ -256,7 +248,7 @@
         policy: FilterPolicyWithReason,
         substituted: Boolean,
         superVisitor: MethodVisitor?,
-        ): MethodVisitor?
+    ): MethodVisitor?
 
     companion object {
         fun getVisitor(
@@ -265,8 +257,6 @@
             nextVisitor: ClassVisitor,
             filter: OutputFilter,
             packageRedirector: PackageRedirectRemapper,
-            remapper: Remapper?,
-            forImpl: Boolean,
             options: Options,
         ): ClassVisitor {
             var next = nextVisitor
@@ -289,23 +279,20 @@
                 if (!packageRedirector.isTarget(classInternalName)) {
                     next = ClassRemapper(next, packageRedirector)
                 } else {
-                    log.v("Class $classInternalName is a redirect-from class, not applying" +
-                            " --package-redirect")
+                    log.v(
+                        "Class $classInternalName is a redirect-from class, not applying" +
+                                " --package-redirect"
+                    )
                 }
             }
 
-            var ret: ClassVisitor
-            if (forImpl) {
-                ret = ImplGeneratingAdapter(classes, next, filter, options)
-            } else {
-                ret = StubGeneratingAdapter(classes, next, filter, options)
-            }
+            next = ImplGeneratingAdapter(classes, next, filter, options)
 
             // Inject TraceClassVisitor for debugging.
             if (options.enablePreTrace) {
-                ret = TraceClassVisitor(ret, verbosePrinter)
+                next = TraceClassVisitor(next, verbosePrinter)
             }
-            return ret
+            return next
         }
     }
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
index 8250412..55d0c0e 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/BodyReplacingMethodVisitor.kt
@@ -20,38 +20,23 @@
 import org.objectweb.asm.Handle
 import org.objectweb.asm.Label
 import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.Opcodes
 import org.objectweb.asm.TypePath
 
 /**
- * A method visitor that removes everything from method body.
+ * A method visitor that creates or replaces a method body.
  *
- * To inject a method body, override [visitCode] and create the opcodes there.
+ * Override [emitNewCode] to build the method body.
  */
 abstract class BodyReplacingMethodVisitor(
-    access: Int,
-    name: String,
-    descriptor: String,
-    signature: String?,
-    exceptions: Array<String>?,
-    next: MethodVisitor?,
+    private val createBody: Boolean,
+    next: MethodVisitor?
 ) : MethodVisitor(OPCODE_VERSION, next) {
-    val isVoid: Boolean
-    val isStatic: Boolean
-
-    init {
-        isVoid = descriptor.endsWith(")V")
-        isStatic = access and Opcodes.ACC_STATIC != 0
-    }
 
     // Following methods are for things that we need to keep.
     // Since they're all calling the super method, we can just remove them, but we keep them
     // just to clarify what we're keeping.
 
-    final override fun visitParameter(
-            name: String?,
-            access: Int
-    ) {
+    final override fun visitParameter(name: String?, access: Int) {
         super.visitParameter(name, access)
     }
 
@@ -59,10 +44,7 @@
         return super.visitAnnotationDefault()
     }
 
-    final override fun visitAnnotation(
-            descriptor: String?,
-            visible: Boolean
-    ): AnnotationVisitor? {
+    final override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
         return super.visitAnnotation(descriptor, visible)
     }
 
@@ -75,17 +57,14 @@
         return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible)
     }
 
-    final override fun visitAnnotableParameterCount(
-            parameterCount: Int,
-            visible: Boolean
-    ) {
+    final override fun visitAnnotableParameterCount(parameterCount: Int, visible: Boolean) {
         super.visitAnnotableParameterCount(parameterCount, visible)
     }
 
     final override fun visitParameterAnnotation(
-            parameter: Int,
-            descriptor: String?,
-            visible: Boolean
+        parameter: Int,
+        descriptor: String?,
+        visible: Boolean
     ): AnnotationVisitor? {
         return super.visitParameterAnnotation(parameter, descriptor, visible)
     }
@@ -94,10 +73,6 @@
         super.visitAttribute(attribute)
     }
 
-    override fun visitEnd() {
-        super.visitEnd()
-    }
-
     /**
      * Control when to emit the code. We use this to ignore all visitXxx method calls caused by
      * the original method, so we'll remove all the original code.
@@ -108,9 +83,18 @@
      * (See also https://asm.ow2.io/asm4-guide.pdf section 3.2.1 about the MethovVisitor
      * call order.)
      */
-    var emitCode = false
+    private var emitCode = false
+
+    /**
+     * This value will be set as true when [visitCode] is called. In [visitEnd], if this value
+     * is still false, this means that the original method does not have a body.
+     *
+     * We want to forcefully inject a method body in [visitEnd] if [createBody] is true.
+     */
+    private var visitedCode = false
 
     final override fun visitCode() {
+        visitedCode = true
         super.visitCode()
 
         try {
@@ -122,15 +106,19 @@
         }
     }
 
+    final override fun visitEnd() {
+        if (!visitedCode && createBody) {
+            visitCode()
+        }
+        super.visitEnd()
+    }
+
     /**
      * Subclass must implement it and emit code, and call [visitMaxs] at the end.
      */
     abstract fun emitNewCode()
 
-    final override fun visitMaxs(
-            maxStack: Int,
-            maxLocals: Int
-    ) {
+    final override fun visitMaxs(maxStack: Int, maxLocals: Int) {
         if (emitCode) {
             super.visitMaxs(maxStack, maxLocals)
         }
@@ -140,11 +128,11 @@
     // emit any of them, so they are all no-op.
 
     final override fun visitFrame(
-            type: Int,
-            numLocal: Int,
-            local: Array<out Any>?,
-            numStack: Int,
-            stack: Array<out Any>?
+        type: Int,
+        numLocal: Int,
+        local: Array<out Any>?,
+        numStack: Int,
+        stack: Array<out Any>?
     ) {
         if (emitCode) {
             super.visitFrame(type, numLocal, local, numStack, stack)
@@ -157,38 +145,29 @@
         }
     }
 
-    final override fun visitIntInsn(
-            opcode: Int,
-            operand: Int
-    ) {
+    final override fun visitIntInsn(opcode: Int, operand: Int) {
         if (emitCode) {
             super.visitIntInsn(opcode, operand)
         }
     }
 
-    final override fun visitVarInsn(
-            opcode: Int,
-            varIndex: Int
-    ) {
+    final override fun visitVarInsn(opcode: Int, varIndex: Int) {
         if (emitCode) {
             super.visitVarInsn(opcode, varIndex)
         }
     }
 
-    final override fun visitTypeInsn(
-            opcode: Int,
-            type: String?
-    ) {
+    final override fun visitTypeInsn(opcode: Int, type: String?) {
         if (emitCode) {
             super.visitTypeInsn(opcode, type)
         }
     }
 
     final override fun visitFieldInsn(
-            opcode: Int,
-            owner: String?,
-            name: String?,
-            descriptor: String?
+        opcode: Int,
+        owner: String?,
+        name: String?,
+        descriptor: String?
     ) {
         if (emitCode) {
             super.visitFieldInsn(opcode, owner, name, descriptor)
@@ -196,11 +175,11 @@
     }
 
     final override fun visitMethodInsn(
-            opcode: Int,
-            owner: String?,
-            name: String?,
-            descriptor: String?,
-            isInterface: Boolean
+        opcode: Int,
+        owner: String?,
+        name: String?,
+        descriptor: String?,
+        isInterface: Boolean
     ) {
         if (emitCode) {
             super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
@@ -208,21 +187,20 @@
     }
 
     final override fun visitInvokeDynamicInsn(
-            name: String?,
-            descriptor: String?,
-            bootstrapMethodHandle: Handle?,
-            vararg bootstrapMethodArguments: Any?
+        name: String?,
+        descriptor: String?,
+        bootstrapMethodHandle: Handle?,
+        vararg bootstrapMethodArguments: Any?
     ) {
         if (emitCode) {
-            super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle,
-                    *bootstrapMethodArguments)
+            super.visitInvokeDynamicInsn(
+                name, descriptor, bootstrapMethodHandle,
+                *bootstrapMethodArguments
+            )
         }
     }
 
-    final override fun visitJumpInsn(
-            opcode: Int,
-            label: Label?
-    ) {
+    final override fun visitJumpInsn(opcode: Int, label: Label?) {
         if (emitCode) {
             super.visitJumpInsn(opcode, label)
         }
@@ -240,20 +218,17 @@
         }
     }
 
-    final override fun visitIincInsn(
-            varIndex: Int,
-            increment: Int
-    ) {
+    final override fun visitIincInsn(varIndex: Int, increment: Int) {
         if (emitCode) {
             super.visitIincInsn(varIndex, increment)
         }
     }
 
     final override fun visitTableSwitchInsn(
-            min: Int,
-            max: Int,
-            dflt: Label?,
-            vararg labels: Label?
+        min: Int,
+        max: Int,
+        dflt: Label?,
+        vararg labels: Label?
     ) {
         if (emitCode) {
             super.visitTableSwitchInsn(min, max, dflt, *labels)
@@ -261,29 +236,26 @@
     }
 
     final override fun visitLookupSwitchInsn(
-            dflt: Label?,
-            keys: IntArray?,
-            labels: Array<out Label>?
+        dflt: Label?,
+        keys: IntArray?,
+        labels: Array<out Label>?
     ) {
         if (emitCode) {
             super.visitLookupSwitchInsn(dflt, keys, labels)
         }
     }
 
-    final override fun visitMultiANewArrayInsn(
-            descriptor: String?,
-            numDimensions: Int
-    ) {
+    final override fun visitMultiANewArrayInsn(descriptor: String?, numDimensions: Int) {
         if (emitCode) {
             super.visitMultiANewArrayInsn(descriptor, numDimensions)
         }
     }
 
     final override fun visitInsnAnnotation(
-            typeRef: Int,
-            typePath: TypePath?,
-            descriptor: String?,
-            visible: Boolean
+        typeRef: Int,
+        typePath: TypePath?,
+        descriptor: String?,
+        visible: Boolean
     ): AnnotationVisitor? {
         if (emitCode) {
             return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible)
@@ -292,10 +264,10 @@
     }
 
     final override fun visitTryCatchBlock(
-            start: Label?,
-            end: Label?,
-            handler: Label?,
-            type: String?
+        start: Label?,
+        end: Label?,
+        handler: Label?,
+        type: String?
     ) {
         if (emitCode) {
             super.visitTryCatchBlock(start, end, handler, type)
@@ -303,10 +275,10 @@
     }
 
     final override fun visitTryCatchAnnotation(
-            typeRef: Int,
-            typePath: TypePath?,
-            descriptor: String?,
-            visible: Boolean
+        typeRef: Int,
+        typePath: TypePath?,
+        descriptor: String?,
+        visible: Boolean
     ): AnnotationVisitor? {
         if (emitCode) {
             return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible)
@@ -315,12 +287,12 @@
     }
 
     final override fun visitLocalVariable(
-            name: String?,
-            descriptor: String?,
-            signature: String?,
-            start: Label?,
-            end: Label?,
-            index: Int
+        name: String?,
+        descriptor: String?,
+        signature: String?,
+        start: Label?,
+        end: Label?,
+        index: Int
     ) {
         if (emitCode) {
             super.visitLocalVariable(name, descriptor, signature, start, end, index)
@@ -328,25 +300,23 @@
     }
 
     final override fun visitLocalVariableAnnotation(
-            typeRef: Int,
-            typePath: TypePath?,
-            start: Array<out Label>?,
-            end: Array<out Label>?,
-            index: IntArray?,
-            descriptor: String?,
-            visible: Boolean
+        typeRef: Int,
+        typePath: TypePath?,
+        start: Array<out Label>?,
+        end: Array<out Label>?,
+        index: IntArray?,
+        descriptor: String?,
+        visible: Boolean
     ): AnnotationVisitor? {
         if (emitCode) {
             return super.visitLocalVariableAnnotation(
-                    typeRef, typePath, start, end, index, descriptor, visible)
+                typeRef, typePath, start, end, index, descriptor, visible
+            )
         }
         return null
     }
 
-    final override fun visitLineNumber(
-            line: Int,
-            start: Label?
-    ) {
+    final override fun visitLineNumber(line: Int, start: Label?) {
         if (emitCode) {
             super.visitLineNumber(line, start)
         }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 416b782..057d653 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -18,7 +18,6 @@
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC
 import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
 import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.asm.isVisibilityPrivateOrPackagePrivate
 import com.android.hoststubgen.asm.prependArgTypeToMethodDescriptor
 import com.android.hoststubgen.asm.writeByteCodeToPushArguments
 import com.android.hoststubgen.asm.writeByteCodeToReturn
@@ -33,22 +32,21 @@
 import org.objectweb.asm.ClassVisitor
 import org.objectweb.asm.MethodVisitor
 import org.objectweb.asm.Opcodes
+import org.objectweb.asm.Opcodes.INVOKEINTERFACE
+import org.objectweb.asm.Opcodes.INVOKESTATIC
+import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
 import org.objectweb.asm.Type
 
 /**
  * An adapter that generates the "impl" class file from an input class file.
  */
 class ImplGeneratingAdapter(
-        classes: ClassNodes,
-        nextVisitor: ClassVisitor,
-        filter: OutputFilter,
-        options: Options,
+    classes: ClassNodes,
+    nextVisitor: ClassVisitor,
+    filter: OutputFilter,
+    options: Options,
 ) : BaseAdapter(classes, nextVisitor, filter, options) {
 
-    override fun shouldEmit(policy: FilterPolicy): Boolean {
-        return policy.needsInImpl
-    }
-
     private var classLoadHooks: List<String> = emptyList()
 
     override fun visit(
@@ -104,14 +102,14 @@
     private fun writeClassLoadHookCalls(mv: MethodVisitor) {
         classLoadHooks.forEach { classLoadHook ->
             // First argument: the class type.
-            mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+            mv.visitLdcInsn(Type.getType("L$currentClassName;"))
 
             // Second argument: method name
             mv.visitLdcInsn(classLoadHook)
 
             // Call HostTestUtils.onClassLoaded().
             mv.visitMethodInsn(
-                Opcodes.INVOKESTATIC,
+                INVOKESTATIC,
                 HostTestUtils.CLASS_INTERNAL_NAME,
                 "onClassLoaded",
                 "(Ljava/lang/Class;Ljava/lang/String;)V",
@@ -121,69 +119,49 @@
     }
 
     override fun updateAccessFlags(
-            access: Int,
-            name: String,
-            descriptor: String,
+        access: Int,
+        name: String,
+        descriptor: String,
+        policy: FilterPolicy,
     ): Int {
-        if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
+        if (policy.isMethodRewriteBody) {
+            // If we are rewriting the entire method body, we need
+            // to convert native methods to non-native
             return access and Opcodes.ACC_NATIVE.inv()
         }
         return access
     }
 
     override fun visitMethodInner(
-            access: Int,
-            name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            policy: FilterPolicyWithReason,
-            substituted: Boolean,
-            superVisitor: MethodVisitor?,
+        access: Int,
+        name: String,
+        descriptor: String,
+        signature: String?,
+        exceptions: Array<String>?,
+        policy: FilterPolicyWithReason,
+        substituted: Boolean,
+        superVisitor: MethodVisitor?,
     ): MethodVisitor? {
-        // Inject method log, if needed.
         var innerVisitor = superVisitor
 
         //  If method logging is enabled, inject call to the logging method.
         val methodCallHooks = filter.getMethodCallHooks(currentClassName, name, descriptor)
         if (methodCallHooks.isNotEmpty()) {
             innerVisitor = MethodCallHookInjectingAdapter(
-                access,
                 name,
                 descriptor,
-                signature,
-                exceptions,
-                innerVisitor,
                 methodCallHooks,
-                )
+                innerVisitor,
+            )
         }
 
         // If this class already has a class initializer and a class load hook is needed, then
         // we inject code.
         if (classLoadHooks.isNotEmpty() &&
             name == CLASS_INITIALIZER_NAME &&
-            descriptor == CLASS_INITIALIZER_DESC) {
-            innerVisitor = ClassLoadHookInjectingMethodAdapter(
-                access,
-                name,
-                descriptor,
-                signature,
-                exceptions,
-                innerVisitor,
-            )
-        }
-
-        // If non-stub method call detection is enabled, then inject a call to the checker.
-        if (options.enableNonStubMethodCallDetection && doesMethodNeedNonStubCallCheck(
-                access, name, descriptor, policy) ) {
-            innerVisitor = NonStubMethodCallDetectingAdapter(
-                    access,
-                    name,
-                    descriptor,
-                    signature,
-                    exceptions,
-                    innerVisitor,
-            )
+            descriptor == CLASS_INITIALIZER_DESC
+        ) {
+            innerVisitor = ClassLoadHookInjectingMethodAdapter(innerVisitor)
         }
 
         fun MethodVisitor.withAnnotation(descriptor: String): MethodVisitor {
@@ -192,38 +170,32 @@
         }
 
         log.withIndent {
-            var willThrow = false
-            if (policy.policy == FilterPolicy.Throw) {
-                log.v("Making method throw...")
-                willThrow = true
-                innerVisitor = ThrowingMethodAdapter(
-                    access, name, descriptor, signature, exceptions, innerVisitor)
-                    .withAnnotation(HostStubGenProcessedAsThrow.CLASS_DESCRIPTOR)
-            }
-            if ((access and Opcodes.ACC_NATIVE) != 0 && nativeSubstitutionClass != null) {
-                log.v("Rewriting native method...")
-                return NativeSubstitutingMethodAdapter(
-                        access, name, descriptor, signature, exceptions, innerVisitor)
-                    .withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
-            }
-            if (willThrow) {
-                return innerVisitor
-            }
-
-            if (policy.policy == FilterPolicy.Ignore) {
-                when (Type.getReturnType(descriptor)) {
-                    Type.VOID_TYPE -> {
-                        log.v("Making method ignored...")
-                        return IgnoreMethodAdapter(
-                                access, name, descriptor, signature, exceptions, innerVisitor)
-                            .withAnnotation(HostStubGenProcessedAsIgnore.CLASS_DESCRIPTOR)
-                    }
-                    else -> {
-                        throw RuntimeException("Ignored policy only allowed for void methods")
-                    }
+            // When we encounter native methods, we want to forcefully
+            // inject a method body. Also see [updateAccessFlags].
+            val forceCreateBody = (access and Opcodes.ACC_NATIVE) != 0
+            when (policy.policy) {
+                FilterPolicy.Throw -> {
+                    log.v("Making method throw...")
+                    return ThrowingMethodAdapter(forceCreateBody, innerVisitor)
+                        .withAnnotation(HostStubGenProcessedAsThrow.CLASS_DESCRIPTOR)
                 }
+                FilterPolicy.Ignore -> {
+                    log.v("Making method ignored...")
+                    return IgnoreMethodAdapter(descriptor, forceCreateBody, innerVisitor)
+                        .withAnnotation(HostStubGenProcessedAsIgnore.CLASS_DESCRIPTOR)
+                }
+                FilterPolicy.NativeSubstitute -> {
+                    log.v("Rewriting native method...")
+                    return NativeSubstitutingMethodAdapter(access, name, descriptor, innerVisitor)
+                        .withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
+                }
+                else -> {}
             }
         }
+
+        if (filter.hasAnyMethodCallReplace()) {
+            innerVisitor = MethodCallReplacingAdapter(name, innerVisitor)
+        }
         if (substituted) {
             innerVisitor?.withAnnotation(HostStubGenProcessedAsSubstitute.CLASS_DESCRIPTOR)
         }
@@ -231,53 +203,32 @@
         return innerVisitor
     }
 
-    fun doesMethodNeedNonStubCallCheck(
-            access: Int,
-            name: String,
-            descriptor: String,
-            policy: FilterPolicyWithReason,
-    ): Boolean {
-        // If a method is in the stub, then no need to check.
-        if (policy.policy.needsInStub) {
-            return false
-        }
-        // If a method is private or package-private, no need to check.
-        // Technically test code can use framework package name, so it's a bit too lenient.
-        if (isVisibilityPrivateOrPackagePrivate(access)) {
-            return false
-        }
-        // TODO: If the method overrides a method that's accessible by tests, then we shouldn't
-        // do the check. (e.g. overrides a stub method or java standard method.)
-
-        return true
-    }
-
     /**
      * A method adapter that replaces the method body with a HostTestUtils.onThrowMethodCalled()
      * call.
      */
     private inner class ThrowingMethodAdapter(
-            access: Int,
-            val name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?
-    ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+        createBody: Boolean,
+        next: MethodVisitor?
+    ) : BodyReplacingMethodVisitor(createBody, next) {
         override fun emitNewCode() {
-            visitMethodInsn(Opcodes.INVOKESTATIC,
-                    HostTestUtils.CLASS_INTERNAL_NAME,
-                    "onThrowMethodCalled",
-                    "()V",
-                    false)
+            visitMethodInsn(
+                INVOKESTATIC,
+                HostTestUtils.CLASS_INTERNAL_NAME,
+                "onThrowMethodCalled",
+                "()V",
+                false
+            )
 
             // We still need a RETURN opcode for the return type.
             // For now, let's just inject a `throw`.
             visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
             visitInsn(Opcodes.DUP)
             visitLdcInsn("Unreachable")
-            visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
-                    "<init>", "(Ljava/lang/String;)V", false)
+            visitMethodInsn(
+                Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
+                "<init>", "(Ljava/lang/String;)V", false
+            )
             visitInsn(Opcodes.ATHROW)
 
             // visitMaxs(3, if (isStatic) 0 else 1)
@@ -289,54 +240,66 @@
      * A method adapter that replaces the method body with a no-op return.
      */
     private inner class IgnoreMethodAdapter(
-            access: Int,
-            val name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?
-    ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
+        val descriptor: String,
+        createBody: Boolean,
+        next: MethodVisitor?
+    ) : BodyReplacingMethodVisitor(createBody, next) {
         override fun emitNewCode() {
-            visitInsn(Opcodes.RETURN)
+            when (Type.getReturnType(descriptor)) {
+                Type.VOID_TYPE -> visitInsn(Opcodes.RETURN)
+                Type.BOOLEAN_TYPE, Type.BYTE_TYPE, Type.CHAR_TYPE, Type.SHORT_TYPE,
+                Type.INT_TYPE -> {
+                    visitInsn(Opcodes.ICONST_0)
+                    visitInsn(Opcodes.IRETURN)
+                }
+                Type.LONG_TYPE -> {
+                    visitInsn(Opcodes.LCONST_0)
+                    visitInsn(Opcodes.LRETURN)
+                }
+                Type.FLOAT_TYPE -> {
+                    visitInsn(Opcodes.FCONST_0)
+                    visitInsn(Opcodes.FRETURN)
+                }
+                Type.DOUBLE_TYPE -> {
+                    visitInsn(Opcodes.DCONST_0)
+                    visitInsn(Opcodes.DRETURN)
+                }
+                else -> {
+                    visitInsn(Opcodes.ACONST_NULL)
+                    visitInsn(Opcodes.ARETURN)
+                }
+            }
             visitMaxs(0, 0) // We let ASM figure them out.
         }
     }
 
     /**
-     * A method adapter that replaces a native method call with a call to the "native substitution"
-     * class.
+     * A method adapter that rewrite a native method body with a
+     * call to a method in the "native substitution" class.
      */
     private inner class NativeSubstitutingMethodAdapter(
-            val access: Int,
-            private val name: String,
-            private val descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?
-    ) : MethodVisitor(OPCODE_VERSION, next) {
-        override fun visitCode() {
-            throw RuntimeException("NativeSubstitutingMethodVisitor should be called on " +
-                    " native method, where visitCode() shouldn't be called.")
-        }
+        access: Int,
+        private val name: String,
+        private val descriptor: String,
+        next: MethodVisitor?
+    ) : BodyReplacingMethodVisitor(true, next) {
 
-        override fun visitEnd() {
-            super.visitCode()
+        private val isStatic = (access and Opcodes.ACC_STATIC) != 0
 
+        override fun emitNewCode() {
             var targetDescriptor = descriptor
             var argOffset = 0
 
             // For non-static native method, we need to tweak it a bit.
-            if ((access and Opcodes.ACC_STATIC) == 0) {
+            if (!isStatic) {
                 // Push `this` as the first argument.
                 this.visitVarInsn(Opcodes.ALOAD, 0)
 
                 // Update the descriptor -- add this class's type as the first argument
                 // to the method descriptor.
-                val thisType = Type.getType("L" + currentClassName + ";")
-
                 targetDescriptor = prependArgTypeToMethodDescriptor(
-                        descriptor,
-                        thisType,
+                    descriptor,
+                    currentClassName,
                 )
 
                 // Shift the original arguments by one.
@@ -345,16 +308,17 @@
 
             writeByteCodeToPushArguments(descriptor, this, argOffset)
 
-            visitMethodInsn(Opcodes.INVOKESTATIC,
-                    nativeSubstitutionClass,
-                    name,
-                    targetDescriptor,
-                    false)
+            visitMethodInsn(
+                INVOKESTATIC,
+                nativeSubstitutionClass,
+                name,
+                targetDescriptor,
+                false
+            )
 
             writeByteCodeToReturn(descriptor, this)
 
             visitMaxs(99, 0) // We let ASM figure them out.
-            super.visitEnd()
         }
     }
 
@@ -365,25 +329,22 @@
      * `this(...)`. The logging code will be injected *before* such calls.
      */
     private inner class MethodCallHookInjectingAdapter(
-            access: Int,
-            val name: String,
-            val descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?,
-            val hooks: List<String>,
+        val name: String,
+        val descriptor: String,
+        val hooks: List<String>,
+        next: MethodVisitor?,
     ) : MethodVisitor(OPCODE_VERSION, next) {
         override fun visitCode() {
             super.visitCode()
 
             hooks.forEach { hook ->
-                mv.visitLdcInsn(Type.getType("L" + currentClassName + ";"))
+                mv.visitLdcInsn(Type.getType("L$currentClassName;"))
                 visitLdcInsn(name)
                 visitLdcInsn(descriptor)
                 visitLdcInsn(hook)
 
                 visitMethodInsn(
-                    Opcodes.INVOKESTATIC,
+                    INVOKESTATIC,
                     HostTestUtils.CLASS_INTERNAL_NAME,
                     "callMethodCallHook",
                     "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
@@ -397,11 +358,6 @@
      * Inject a class load hook call.
      */
     private inner class ClassLoadHookInjectingMethodAdapter(
-        access: Int,
-        val name: String,
-        val descriptor: String,
-        signature: String?,
-        exceptions: Array<String>?,
         next: MethodVisitor?
     ) : MethodVisitor(OPCODE_VERSION, next) {
         override fun visitCode() {
@@ -411,44 +367,57 @@
         }
     }
 
-    /**
-     * A method adapter that detects calls to non-stub methods.
-     */
-    private inner class NonStubMethodCallDetectingAdapter(
-            access: Int,
-            val name: String,
-            val descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?
+    private inner class MethodCallReplacingAdapter(
+        val callerMethodName: String,
+        next: MethodVisitor?,
     ) : MethodVisitor(OPCODE_VERSION, next) {
-        override fun visitCode() {
-            super.visitCode()
+        override fun visitMethodInsn(
+            opcode: Int,
+            owner: String?,
+            name: String?,
+            descriptor: String?,
+            isInterface: Boolean,
+        ) {
+            when (opcode) {
+                INVOKESTATIC, INVOKEVIRTUAL, INVOKEINTERFACE -> {}
+                else -> {
+                    // Don't touch other opcodes.
+                    super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+                    return
+                }
+            }
+            val to = filter.getMethodCallReplaceTo(
+                currentClassName, callerMethodName, owner!!, name!!, descriptor!!
+            )
 
-            // First three arguments to HostTestUtils.onNonStubMethodCalled().
-            visitLdcInsn(currentClassName)
-            visitLdcInsn(name)
-            visitLdcInsn(descriptor)
+            if (to == null
+                // Don't replace if the target is the callsite.
+                || (to.className == currentClassName && to.methodName == callerMethodName)
+            ) {
+                super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
+                return
+            }
 
-            // Call: HostTestUtils.getStackWalker().getCallerClass().
-            // This push the caller Class in the stack.
-            visitMethodInsn(Opcodes.INVOKESTATIC,
-                    HostTestUtils.CLASS_INTERNAL_NAME,
-                    "getStackWalker",
-                    "()Ljava/lang/StackWalker;",
-                    false)
-            visitMethodInsn(Opcodes.INVOKEVIRTUAL,
-                    "java/lang/StackWalker",
-                    "getCallerClass",
-                    "()Ljava/lang/Class;",
-                    false)
+            // Replace the method call with a (static) call to the target method.
+            // If it's a non-static call, the target method's first argument will receive "this".
+            // (Because of that, we don't need to manipulate the stack. Just replace the
+            // method call.)
 
-            // Then call onNonStubMethodCalled().
-            visitMethodInsn(Opcodes.INVOKESTATIC,
-                    HostTestUtils.CLASS_INTERNAL_NAME,
-                    "onNonStubMethodCalled",
-                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V",
-                    false)
+            val toDesc = if (opcode == INVOKESTATIC) {
+                // Static call to static call, no need to change the desc.
+                descriptor
+            } else {
+                // Need to prepend the "this" type to the descriptor.
+                prependArgTypeToMethodDescriptor(descriptor, owner)
+            }
+
+            mv.visitMethodInsn(
+                INVOKESTATIC,
+                to.className,
+                to.methodName,
+                toDesc,
+                false
+            )
         }
     }
 }
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
deleted file mode 100644
index fc20f28..0000000
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/visitors/StubGeneratingAdapter.kt
+++ /dev/null
@@ -1,86 +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.visitors
-
-import com.android.hoststubgen.asm.ClassNodes
-import com.android.hoststubgen.filters.FilterPolicy
-import com.android.hoststubgen.filters.FilterPolicyWithReason
-import com.android.hoststubgen.filters.OutputFilter
-import com.android.hoststubgen.log
-import org.objectweb.asm.ClassVisitor
-import org.objectweb.asm.MethodVisitor
-import org.objectweb.asm.Opcodes
-
-/**
- * An adapter that generates the "impl" class file from an input class file.
- */
-class StubGeneratingAdapter(
-        classes: ClassNodes,
-        nextVisitor: ClassVisitor,
-        filter: OutputFilter,
-        options: Options,
-) : BaseAdapter(classes, nextVisitor, filter, options) {
-
-    override fun shouldEmit(policy: FilterPolicy): Boolean {
-        return policy.needsInStub
-    }
-
-    override fun visitMethodInner(
-            access: Int,
-            name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            policy: FilterPolicyWithReason,
-            substituted: Boolean,
-            superVisitor: MethodVisitor?,
-    ): MethodVisitor? {
-        return StubMethodVisitor(access, name, descriptor, signature, exceptions, superVisitor)
-    }
-
-    private inner class StubMethodVisitor(
-            access: Int,
-            val name: String,
-            descriptor: String,
-            signature: String?,
-            exceptions: Array<String>?,
-            next: MethodVisitor?
-    ) : BodyReplacingMethodVisitor(access, name, descriptor, signature, exceptions, next) {
-        override fun emitNewCode() {
-            log.d("  Generating stub method for $currentClassName.$name")
-
-            // Inject the following code:
-            //   throw new RuntimeException("Stub!");
-
-            /*
-                NEW java/lang/RuntimeException
-                DUP
-                LDC "not supported on host side"
-                INVOKESPECIAL java/lang/RuntimeException.<init> (Ljava/lang/String;)V
-                ATHROW
-                MAXSTACK = 3
-                MAXLOCALS = 2 <- 1 for this, 1 for return value.
-             */
-            visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException")
-            visitInsn(Opcodes.DUP)
-            visitLdcInsn("Stub!")
-            visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException",
-                    "<init>", "(Ljava/lang/String;)V", false)
-            visitInsn(Opcodes.ATHROW)
-            visitMaxs(0, 0) // We let ASM figure them out.
-        }
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
index e7873d6..ba2c869 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/Android.bp
@@ -21,7 +21,7 @@
 
 // Create stub/impl jars from "hoststubgen-test-tiny-framework", using the following 3 rules.
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host",
+    name: "hoststubgen-test-tiny-framework-host-base",
     defaults: ["hoststubgen-command-defaults"],
     cmd: hoststubgen_common_options +
         "--in-jar $(location :hoststubgen-test-tiny-framework) " +
@@ -35,25 +35,13 @@
 }
 
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-stub",
+    name: "hoststubgen-test-tiny-framework-host",
     cmd: "cp $(in) $(out)",
     srcs: [
-        ":hoststubgen-test-tiny-framework-host{host_stub.jar}",
+        ":hoststubgen-test-tiny-framework-host-base{host.jar}",
     ],
     out: [
-        "host_stub.jar",
-    ],
-    visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-impl",
-    cmd: "cp $(in) $(out)",
-    srcs: [
-        ":hoststubgen-test-tiny-framework-host{host_impl.jar}",
-    ],
-    out: [
-        "host_impl.jar",
+        "host.jar",
     ],
     visibility: ["//visibility:private"],
 }
@@ -61,7 +49,7 @@
 // Same as "hoststubgen-test-tiny-framework-host", but with more options, to test more hoststubgen
 // features.
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-ext",
+    name: "hoststubgen-test-tiny-framework-host-ext-base",
     defaults: ["hoststubgen-command-defaults"],
     cmd: hoststubgen_common_options +
         "--in-jar $(location :hoststubgen-test-tiny-framework) " +
@@ -79,37 +67,25 @@
 }
 
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-ext-stub",
+    name: "hoststubgen-test-tiny-framework-host-ext",
     cmd: "cp $(in) $(out)",
     srcs: [
-        ":hoststubgen-test-tiny-framework-host-ext{host_stub.jar}",
+        ":hoststubgen-test-tiny-framework-host-ext-base{host.jar}",
     ],
     out: [
-        "host_stub.jar",
-    ],
-    visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-ext-impl",
-    cmd: "cp $(in) $(out)",
-    srcs: [
-        ":hoststubgen-test-tiny-framework-host-ext{host_impl.jar}",
-    ],
-    out: [
-        "host_impl.jar",
+        "host.jar",
     ],
     visibility: ["//visibility:private"],
 }
 
 // Compile the test jar, using 2 rules.
-// 1. Build the test against the stub.
+// 1. Build the test against the original framework.
 java_library_host {
     name: "hoststubgen-test-tiny-test-lib",
     srcs: ["tiny-test/src/**/*.java"],
 
     libs: [
-        "hoststubgen-test-tiny-framework-host-stub",
+        "hoststubgen-test-tiny-framework",
     ],
     static_libs: [
         "junit",
@@ -129,7 +105,7 @@
     static_libs: [
         "hoststubgen-test-tiny-test-lib",
         "hoststubgen-helper-runtime",
-        "hoststubgen-test-tiny-framework-host-impl",
+        "hoststubgen-test-tiny-framework-host",
     ],
     test_suites: ["general-tests"],
 }
@@ -149,49 +125,25 @@
 }
 
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-stub-dump",
+    name: "hoststubgen-test-tiny-framework-host-dump",
     defaults: ["hoststubgen-jar-dump-defaults"],
     srcs: [
-        ":hoststubgen-test-tiny-framework-host-stub",
+        ":hoststubgen-test-tiny-framework-host",
     ],
     out: [
-        "02-hoststubgen-test-tiny-framework-host-stub-dump.txt",
+        "03-hoststubgen-test-tiny-framework-host-dump.txt",
     ],
     visibility: ["//visibility:private"],
 }
 
 java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-impl-dump",
+    name: "hoststubgen-test-tiny-framework-host-ext-dump",
     defaults: ["hoststubgen-jar-dump-defaults"],
     srcs: [
-        ":hoststubgen-test-tiny-framework-host-impl",
+        ":hoststubgen-test-tiny-framework-host-ext",
     ],
     out: [
-        "03-hoststubgen-test-tiny-framework-host-impl-dump.txt",
-    ],
-    visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-ext-stub-dump",
-    defaults: ["hoststubgen-jar-dump-defaults"],
-    srcs: [
-        ":hoststubgen-test-tiny-framework-host-ext-stub",
-    ],
-    out: [
-        "12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt",
-    ],
-    visibility: ["//visibility:private"],
-}
-
-java_genrule_host {
-    name: "hoststubgen-test-tiny-framework-host-ext-impl-dump",
-    defaults: ["hoststubgen-jar-dump-defaults"],
-    srcs: [
-        ":hoststubgen-test-tiny-framework-host-ext-impl",
-    ],
-    out: [
-        "13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt",
+        "13-hoststubgen-test-tiny-framework-host-ext-dump.txt",
     ],
     visibility: ["//visibility:private"],
 }
@@ -206,11 +158,9 @@
         "golden-output/*.txt",
     ],
     java_data: [
-        "hoststubgen-test-tiny-framework-host-stub-dump",
-        "hoststubgen-test-tiny-framework-host-impl-dump",
         "hoststubgen-test-tiny-framework-orig-dump",
-        "hoststubgen-test-tiny-framework-host-ext-stub-dump",
-        "hoststubgen-test-tiny-framework-host-ext-impl-dump",
+        "hoststubgen-test-tiny-framework-host-dump",
+        "hoststubgen-test-tiny-framework-host-ext-dump",
     ],
     test_suites: ["general-tests"],
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
index bd9e85e..de4cb0c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/annotation-allowed-classes-tiny-framework.txt
@@ -6,10 +6,10 @@
 
 
 # To allow a specific class to use annotations:
-# com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+# com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
 
 # To disallow a specific class to use annotations:
-# !com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
+# !com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
 
 # To allow a specific package to use annotations:
 # com.android.hoststubgen.test.*
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 00cbfe3..3726ca9 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -63,7 +63,7 @@
 
 
 # Build the dump files, which are the input of this test.
-run m tiny-framework-dump-test
+run m  dump-jar tiny-framework-dump-test
 
 
 # Get the path to the generate text files. (not the golden files.)
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index c127e67..5fde14f 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -104,26 +104,6 @@
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
-## Class: android/hosttest/annotation/HostSideTestStub.class
-  Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestStub.java"
-RuntimeVisibleAnnotations:
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
 ## Class: android/hosttest/annotation/HostSideTestSubstitute.class
   Compiled from "HostSideTestSubstitute.java"
 public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
@@ -187,26 +167,6 @@
     java.lang.annotation.Retention(
       value=Ljava/lang/annotation/RetentionPolicy;.CLASS
     )
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
-  Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
 ## Class: android/hosttest/annotation/tests/HostSideTestSuppress.class
   Compiled from "HostSideTestSuppress.java"
 public interface android.hosttest.annotation.tests.HostSideTestSuppress extends java.lang.annotation.Annotation
@@ -394,120 +354,15 @@
   com/android/hoststubgen/test/tinyframework/R$Nested
 InnerClasses:
   public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
-
-  public static int getOneKeep();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_1
-         x: ireturn
-      LineNumberTable:
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  public static int getOneStub();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_1
-         x: ireturn
-      LineNumberTable:
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkCallerCheck.java"
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+  Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
   minor version: 0
   major version: 61
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
-
-  public static int getOne_withCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokestatic  #x                  // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
-         x: ireturn
-      LineNumberTable:
-
-  public static int getOne_noCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
-         x: ireturn
-      LineNumberTable:
-}
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
-  Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 10, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
+  interfaces: 0, fields: 2, methods: 8, attributes: 2
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -519,7 +374,7 @@
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
 
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
     Code:
@@ -528,42 +383,21 @@
          x: invokespecial #x                  // Method java/lang/Object."<init>":()V
          x: aload_0
          x: iconst_1
-         x: putfield      #x                  // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
+         x: putfield      #x                  // Field keep:I
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public int addOne(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-            0       6     1 value   I
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
          x: iload_1
          x: iconst_1
          x: iadd
@@ -571,7 +405,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0       4     1 value   I
     RuntimeInvisibleAnnotations:
       x: #x()
@@ -589,7 +423,7 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0       8     1   foo   Ljava/lang/String;
     RuntimeInvisibleAnnotations:
       x: #x()
@@ -608,11 +442,9 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0      10     1 value   I
     RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
       x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
@@ -630,15 +462,13 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0       4     1 value   I
 
   public static native int nativeAddThree(int);
     descriptor: (I)I
     flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
     RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
       x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
@@ -668,212 +498,19 @@
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
+            0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
     RuntimeInvisibleAnnotations:
       x: #x()
         android.hosttest.annotation.HostSideTestThrow
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
 }
-SourceFile: "TinyFrameworkClassAnnotations.java"
+SourceFile: "TinyFrameworkAnnotations.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
   x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestClassLoadHook(
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
-  Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 10, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-
-  public int remove;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iconst_1
-         x: putfield      #x                  // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       6     1 value   I
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_1
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       4     1 value   I
-
-  public void toBeRemoved(java.lang.String);
-    descriptor: (Ljava/lang/String;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       8     1   foo   Ljava/lang/String;
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String not supported on host side
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0      10     1 value   I
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-      x: #x(#x=s#x)
-        android.hosttest.annotation.HostSideTestSubstitute(
-          suffix="_host"
-        )
-
-  public int addTwo_host(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_2
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       4     1 value   I
-
-  public static native int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-      x: #x(#x=s#x)
-        android.hosttest.annotation.HostSideTestSubstitute(
-          suffix="_host"
-        )
-
-  public static int nativeAddThree_host(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_3
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0 value   I
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
   Compiled from "TinyFrameworkClassLoadHook.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
@@ -935,7 +572,131 @@
 SourceFile: "TinyFrameworkClassLoadHook.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+  Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 6, attributes: 2
+  public int keep;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+
+  public int remove;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestRemove
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                  // Field keep:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0       4     1 value   I
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=2, args_size=2
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String not supported on host side
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0      10     1 value   I
+    RuntimeInvisibleAnnotations:
+      x: #x(#x=s#x)
+        android.hosttest.annotation.HostSideTestSubstitute(
+          suffix="_host"
+        )
+
+  public int addTwo_host(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0       4     1 value   I
+
+  public void toBeRemoved(java.lang.String);
+    descriptor: (Ljava/lang/String;)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0       8     1   foo   Ljava/lang/String;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestRemove
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
   Compiled from "TinyFrameworkClassWithInitializerDefault.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
@@ -950,14 +711,14 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static java.lang.Object sObject;
     descriptor: Ljava/lang/Object;
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault();
     descriptor: ()V
@@ -989,7 +750,7 @@
 SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
   Compiled from "TinyFrameworkClassWithInitializerStub.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
@@ -1004,14 +765,14 @@
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static java.lang.Object sObject;
     descriptor: Ljava/lang/Object;
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub();
     descriptor: ()V
@@ -1047,7 +808,7 @@
       value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
     )
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
@@ -1064,21 +825,21 @@
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   private final java.lang.String mLongName;
     descriptor: Ljava/lang/String;
@@ -1158,7 +919,7 @@
     Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public java.lang.String getLongName();
     descriptor: ()Ljava/lang/String;
@@ -1174,7 +935,7 @@
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public java.lang.String getShortName();
     descriptor: ()Ljava/lang/String;
@@ -1190,7 +951,7 @@
             0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
     descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
@@ -1252,7 +1013,7 @@
 SourceFile: "TinyFrameworkEnumComplex.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
   Compiled from "TinyFrameworkEnumSimple.java"
 public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
@@ -1267,14 +1028,14 @@
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
     descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
@@ -1373,7 +1134,7 @@
 SourceFile: "TinyFrameworkEnumSimple.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
   Compiled from "TinyFrameworkExceptionTester.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
@@ -1427,7 +1188,7 @@
 SourceFile: "TinyFrameworkExceptionTester.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
   Compiled from "TinyFrameworkForTextPolicy.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
@@ -1436,15 +1197,11 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 10, attributes: 1
+  interfaces: 0, fields: 2, methods: 17, attributes: 1
   public int stub;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
 
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-
   public int remove;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -1459,35 +1216,17 @@
          x: aload_0
          x: iconst_1
          x: putfield      #x                  // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
+         x: return
       LineNumberTable:
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
 
   public int addOne(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
     Code:
       stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-            0       6     1 value   I
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
          x: iload_1
          x: iconst_1
          x: iadd
@@ -1513,6 +1252,132 @@
             0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
             0       8     1   foo   Ljava/lang/String;
 
+  public java.lang.String toBeIgnoredObj();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public void toBeIgnoredV();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public boolean toBeIgnoredZ();
+    descriptor: ()Z
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public byte toBeIgnoredB();
+    descriptor: ()B
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public char toBeIgnoredC();
+    descriptor: ()C
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public short toBeIgnoredS();
+    descriptor: ()S
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public int toBeIgnoredI();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public float toBeIgnoredF();
+    descriptor: ()F
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
+  public double toBeIgnoredD();
+    descriptor: ()D
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+
   public int addTwo(int);
     descriptor: (I)I
     flags: (0x0001) ACC_PUBLIC
@@ -1573,19 +1438,6 @@
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
 }
 SourceFile: "TinyFrameworkForTextPolicy.java"
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
@@ -1603,7 +1455,7 @@
     Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
     descriptor: Ljava/util/function/Supplier;
@@ -1611,7 +1463,7 @@
     Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
     descriptor: ()V
@@ -1630,7 +1482,7 @@
             0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public java.util.function.Supplier<java.lang.Integer> getSupplier();
     descriptor: ()Ljava/util/function/Supplier;
@@ -1646,7 +1498,7 @@
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
     descriptor: ()Ljava/util/function/Supplier;
@@ -1659,7 +1511,7 @@
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   private static java.lang.Integer lambda$getSupplier_static$3();
     descriptor: ()Ljava/lang/Integer;
@@ -1714,7 +1566,7 @@
 SourceFile: "TinyFrameworkLambdas.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerKeep
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
@@ -1757,7 +1609,7 @@
     Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
     descriptor: Ljava/util/function/Supplier;
@@ -1765,7 +1617,7 @@
     Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
     descriptor: ()V
@@ -1784,7 +1636,7 @@
             0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public java.util.function.Supplier<java.lang.Integer> getSupplier();
     descriptor: ()Ljava/util/function/Supplier;
@@ -1800,7 +1652,7 @@
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
     descriptor: ()Ljava/util/function/Supplier;
@@ -1813,7 +1665,7 @@
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
     RuntimeInvisibleAnnotations:
       x: #x()
-        android.hosttest.annotation.HostSideTestStub
+        android.hosttest.annotation.HostSideTestKeep
 
   private static java.lang.Integer lambda$getSupplier_static$3();
     descriptor: ()Ljava/lang/Integer;
@@ -1868,7 +1720,7 @@
 SourceFile: "TinyFrameworkLambdas.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestStub
+    android.hosttest.annotation.HostSideTestKeep
   x: #x()
     android.hosttest.annotation.HostSideTestStaticInitializerKeep
 NestMembers:
@@ -1897,6 +1749,174 @@
 InnerClasses:
   public static #x= #x of #x;          // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
   public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo;
+
+  public static void startThread(java.lang.Thread);
+    descriptor: (Ljava/lang/Thread;)V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: iconst_1
+         x: invokevirtual #x                  // Method java/lang/Thread.setDaemon:(Z)V
+         x: aload_0
+         x: invokevirtual #x                 // Method java/lang/Thread.start:()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 thread   Ljava/lang/Thread;
+
+  public static int add(int, int);
+    descriptor: (II)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0     a   I
+            0       4     1     b   I
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+InnerClasses:
+  public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 5, attributes: 5
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace;
+
+  public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
+    descriptor: ()Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=2, args_size=0
+         x: new           #x                  // class java/util/concurrent/atomic/AtomicBoolean
+         x: dup
+         x: iconst_0
+         x: invokespecial #x                  // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V
+         x: astore_0
+         x: new           #x                 // class java/lang/Thread
+        x: dup
+        x: aload_0
+        x: invokedynamic #x,  0             // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable;
+        x: invokespecial #x                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
+        x: astore_1
+        x: aload_1
+        x: invokevirtual #x                 // Method java/lang/Thread.start:()V
+        x: aload_1
+        x: invokevirtual #x                 // Method java/lang/Thread.join:()V
+        x: aload_0
+        x: invokevirtual #x                 // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            9      27     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+           23      13     1    th   Ljava/lang/Thread;
+    Exceptions:
+      throws java.lang.Exception
+
+  public static int staticMethodCallReplaceTester();
+    descriptor: ()I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: iconst_1
+         x: iconst_2
+         x: invokestatic  #x                 // Method originalAdd:(II)I
+         x: ireturn
+      LineNumberTable:
+
+  private static int originalAdd(int, int);
+    descriptor: (II)I
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: iconst_1
+         x: isub
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0     a   I
+            0       6     1     b   I
+
+  private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
+    descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+         x: invokevirtual #x                 // Method java/lang/Thread.isDaemon:()Z
+         x: invokevirtual #x                 // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+}
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()V
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
+      #x ()V
+InnerClasses:
+  public static #x= #x of #x;          // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
   Compiled from "TinyFrameworkNative.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
@@ -1905,7 +1925,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 11, attributes: 2
+  interfaces: 0, fields: 1, methods: 12, attributes: 2
   int value;
     descriptor: I
     flags: (0x0000)
@@ -2000,6 +2020,13 @@
       x: #x()
         android.hosttest.annotation.HostSideTestThrow
 
+  public static native void nativeStillKeep();
+    descriptor: ()V
+    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
   public static void nativeStillNotSupported_should_be_like_this();
     descriptor: ()V
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -2018,7 +2045,7 @@
 SourceFile: "TinyFrameworkNative.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
   x: #x(#x=s#x)
     android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
       value="TinyFrameworkNative_host"
@@ -2390,7 +2417,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 4
+  interfaces: 0, fields: 2, methods: 1, attributes: 3
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2423,9 +2450,6 @@
       <no name>                      final mandated
 }
 SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 InnerClasses:
   public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
@@ -2484,6 +2508,40 @@
 InnerClasses:
   public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 3
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                  // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: bipush        8
+         x: putfield      #x                  // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+}
+SourceFile: "TinyFrameworkNestedClasses.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+InnerClasses:
+  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
   Compiled from "TinyFrameworkNestedClasses.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
@@ -2492,7 +2550,7 @@
   flags: (0x0021) ACC_PUBLIC, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
   super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  interfaces: 0, fields: 1, methods: 2, attributes: 3
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2526,13 +2584,11 @@
     Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
 }
 SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
 NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
 InnerClasses:
   public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
   Compiled from "TinyFrameworkNestedClasses.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
@@ -2643,11 +2699,12 @@
 SourceFile: "TinyFrameworkNestedClasses.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 NestMembers:
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
   com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
@@ -2663,6 +2720,7 @@
   public static #x= #x of #x;          // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   public #x= #x of #x;                 // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;          // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
   #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
   Compiled from "TinyFrameworkPackageRedirect.java"
@@ -2705,7 +2763,7 @@
 SourceFile: "TinyFrameworkPackageRedirect.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
   Compiled from "TinyFrameworkRenamedClassCaller.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
@@ -2747,7 +2805,7 @@
 SourceFile: "TinyFrameworkRenamedClassCaller.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
   Compiled from "TinyFrameworkToBeRenamed.java"
 public class com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
@@ -2794,7 +2852,7 @@
 SourceFile: "TinyFrameworkToBeRenamed.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
 ## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
   Compiled from "A.java"
 public class com.android.hoststubgen.test.tinyframework.packagetest.A
@@ -3540,4 +3598,4 @@
 SourceFile: "UnsupportedClass.java"
 RuntimeInvisibleAnnotations:
   x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
+    android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
deleted file mode 100644
index 17ba48c..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ /dev/null
@@ -1,2530 +0,0 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addOne(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
-  Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 4
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
-  public static int[] ARRAY;
-    descriptor: [I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.R$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.R();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOneStub();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
-  private static #x= #x of #x;           // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 5
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_withCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_noCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
-  Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 5, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
-  Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 8, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int remove;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void toBeRemoved(java.lang.String);
-    descriptor: (Ljava/lang/String;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
-  Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
-    descriptor: Ljava/util/Set;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void onClassLoaded(java.lang.Class<?>);
-    descriptor: (Ljava/lang/Class;)V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // (Ljava/lang/Class<*>;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
-  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
-  Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
-  Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 4, methods: 7, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
-    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=5, args_size=5
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-      <no name>
-      <no name>
-
-  public java.lang.String getLongName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.lang.String getShortName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
-  Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 3, methods: 5, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
-    descriptor: (Ljava/lang/String;I)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=3, args_size=3
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
-  Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int testException();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
-  Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 5, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 7, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 7, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
-  Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 10, attributes: 3
-  int value;
-    descriptor: I
-    flags: (0x0000)
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus_should_be_like_this(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=4, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void setValue(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public native int nativeNonStaticAddToValue(int);
-    descriptor: (I)I
-    flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void nativeStillNotSupported_should_be_like_this();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
-      value="TinyFrameworkNative_host"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 1, attributes: 4
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-}
-InnerClasses:
-  public #x= #x of #x;                   // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 4, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
-  Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
-  Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
-  Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
-  Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
-  Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
-  Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
-  Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
-  Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
-  Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
-  Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
-  Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
-  Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/unsupported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/unsupported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.unsupported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
-  Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
new file mode 100644
index 0000000..e41d46d
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-dump.txt
@@ -0,0 +1,3652 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+  Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  public abstract java.lang.String value();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+  Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+  Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  public abstract java.lang.String value();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+  Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+  Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestStaticInitializerKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestStaticInitializerKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+  Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  public abstract java.lang.String suffix();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.METHOD]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+  Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+  Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
+  Compiled from "IPretendingAidl.java"
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0     a   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
+  Compiled from "IPretendingAidl.java"
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int addOne(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: iconst_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0     a   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
+  Compiled from "IPretendingAidl.java"
+public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 4
+}
+InnerClasses:
+  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_1
+         x: newarray       int
+         x: dup
+         x: iconst_0
+         x: iconst_1
+         x: iastore
+         x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+  Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 6, attributes: 3
+  public int keep;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                  // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field keep:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddThree(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Unreachable
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkAnnotations.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestClassLoadHook(
+      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+    )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+  Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+    descriptor: Ljava/util/Set;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+    descriptor: ()V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void onClassLoaded(java.lang.Class<?>);
+    descriptor: (Ljava/lang/Class;)V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+         x: aload_0
+         x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+         x: pop
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0 clazz   Ljava/lang/Class;
+      LocalVariableTypeTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0 clazz   Ljava/lang/Class<*>;
+    Signature: #x                          // (Ljava/lang/Class<*>;)V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: new           #x                 // class java/util/HashSet
+         x: dup
+         x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
+         x: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+  Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 4, attributes: 3
+  public int keep;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field keep:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Unreachable
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
+  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 0, attributes: 3
+  public static boolean sInitialized;
+    descriptor: Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.lang.Object sObject;
+    descriptor: Ljava/lang/Object;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+}
+SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
+  Compiled from "TinyFrameworkClassWithInitializerStub.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 1, attributes: 3
+  public static boolean sInitialized;
+    descriptor: Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.lang.Object sObject;
+    descriptor: Ljava/lang/Object;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: iconst_1
+         x: putstatic     #x                 // Field sInitialized:Z
+        x: new           #x                  // class java/lang/Object
+        x: dup
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: putstatic     #x                 // Field sObject:Ljava/lang/Object;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkClassWithInitializerStub.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestClassLoadHook(
+      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+    )
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 6, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mLongName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mShortName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      mandated
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=5, args_size=5
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: aload_0
+         x: aload_3
+         x: putfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: aload_0
+        x: aload         4
+        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+            0      18     3 longName   Ljava/lang/String;
+            0      18     4 shortName   Ljava/lang/String;
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      synthetic
+      <no name>                      synthetic
+      <no name>
+      <no name>
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mLongName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mShortName:Ljava/lang/String;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_3
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_2
+        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=6, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: dup
+         x: ldc           #x                 // String RED
+         x: iconst_0
+         x: ldc           #x                 // String Red
+         x: ldc           #x                 // String R
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String GREEN
+        x: iconst_1
+        x: ldc           #x                 // String Green
+        x: ldc           #x                 // String G
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String BLUE
+        x: iconst_2
+        x: ldc           #x                 // String Blue
+        x: ldc           #x                 // String B
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  name   Ljava/lang/String;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      mandated
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=3, locals=3, args_size=3
+         x: aload_0
+         x: aload_1
+         x: iload_2
+         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    Signature: #x                          // ()V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      synthetic
+      <no name>                      synthetic
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: iconst_2
+         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: iconst_0
+         x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: dup
+         x: ldc           #x                 // String CAT
+         x: iconst_0
+         x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String DOG
+        x: iconst_1
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+  Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int testException();
+    descriptor: ()I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=0
+         x: new           #x                 // class java/lang/IllegalStateException
+         x: dup
+         x: ldc           #x                 // String Inner exception
+         x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+         x: athrow
+        x: astore_0
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Outer exception
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+        x: athrow
+      Exception table:
+         from    to  target type
+             0    10    10   Class java/lang/Exception
+      StackMapTable: number_of_entries = 1
+        frame_type = 74 /* same_locals_1_stack_item */
+          stack = [ class java/lang/Exception ]
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0     e   Ljava/lang/Exception;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+  Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 15, attributes: 2
+  public int stub;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                  // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iconst_1
+         x: putfield      #x                 // Field stub:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String toBeIgnoredObj();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aconst_null
+         x: areturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public void toBeIgnoredV();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=0, locals=1, args_size=1
+         x: return
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public boolean toBeIgnoredZ();
+    descriptor: ()Z
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public byte toBeIgnoredB();
+    descriptor: ()B
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public char toBeIgnoredC();
+    descriptor: ()C
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public short toBeIgnoredS();
+    descriptor: ()S
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int toBeIgnoredI();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_0
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public float toBeIgnoredF();
+    descriptor: ()F
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: fconst_0
+         x: freturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public double toBeIgnoredD();
+    descriptor: ()D
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: dconst_0
+         x: dreturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_1
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+            0       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddThree(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: iconst_3
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Unreachable
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+  Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 8, attributes: 6
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static java.lang.Integer lambda$getSupplier_static$3();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: bipush        8
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$getSupplier$2();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: bipush        7
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$static$1();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: bipush        6
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$new$0();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: iconst_5
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+         x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+  Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 8, attributes: 6
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static java.lang.Integer lambda$getSupplier_static$3();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: iconst_4
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$getSupplier$2();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: iconst_3
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$static$1();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: iconst_2
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$new$0();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: iconst_1
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=1, locals=0, args_size=0
+         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+         x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void startThread(java.lang.Thread);
+    descriptor: (Ljava/lang/Thread;)V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: iconst_1
+         x: invokevirtual #x                 // Method java/lang/Thread.setDaemon:(Z)V
+         x: aload_0
+         x: invokevirtual #x                 // Method java/lang/Thread.start:()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0 thread   Ljava/lang/Thread;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int add(int, int);
+    descriptor: (II)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0     a   I
+            0       4     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 4, attributes: 6
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
+    descriptor: ()Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=2, args_size=0
+         x: new           #x                 // class java/util/concurrent/atomic/AtomicBoolean
+         x: dup
+         x: iconst_0
+         x: invokespecial #x                 // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V
+         x: astore_0
+         x: new           #x                 // class java/lang/Thread
+        x: dup
+        x: aload_0
+        x: invokedynamic #x,  0             // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable;
+        x: invokespecial #x                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
+        x: astore_1
+        x: aload_1
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.startThread:(Ljava/lang/Thread;)V
+        x: aload_1
+        x: invokevirtual #x                 // Method java/lang/Thread.join:()V
+        x: aload_0
+        x: invokevirtual #x                 // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            9      27     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+           23      13     1    th   Ljava/lang/Thread;
+    Exceptions:
+      throws java.lang.Exception
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int staticMethodCallReplaceTester();
+    descriptor: ()I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: iconst_1
+         x: iconst_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.add:(II)I
+         x: ireturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
+    descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokestatic  #x                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+         x: invokevirtual #x                 // Method java/lang/Thread.isDaemon:()Z
+         x: invokevirtual #x                 // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;            // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()V
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
+      #x ()V
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+  Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 12, attributes: 3
+  int value;
+    descriptor: I
+    flags: (0x0000)
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo_should_be_like_this(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iload_0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: lload_0
+         x: lload_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+         x: lreturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus_should_be_like_this(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: lload_0
+         x: lload_2
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+         x: lreturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  arg1   J
+            0       6     2  arg2   J
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public void setValue(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field value:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+            0       6     1     v   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int nativeNonStaticAddToValue(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: iload_1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int nativeNonStaticAddToValue_should_be_like_this(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: iload_1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+            0       6     1   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void nativeStillNotSupported();
+    descriptor: ()V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=0, args_size=0
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String Unreachable
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+
+  public static native void nativeStillKeep();
+    descriptor: ()V
+    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static void nativeStillNotSupported_should_be_like_this();
+    descriptor: ()V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+         x: athrow
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static byte nativeBytePlus(byte, byte);
+    descriptor: (BB)B
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+         x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+      value="TinyFrameworkNative_host"
+    )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+  Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 5, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: iload_0
+         x: iconst_2
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: lload_0
+         x: lload_2
+         x: ladd
+         x: lreturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       4     0  arg1   J
+            0       4     2  arg2   J
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: getfield      #x                 // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+         x: iload_1
+         x: iadd
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       7     0 source   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+            0       7     1   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static byte nativeBytePlus(byte, byte);
+    descriptor: (BB)B
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: iload_0
+         x: iload_1
+         x: iadd
+         x: i2b
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  arg1   B
+            0       5     1  arg2   B
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 1, methods: 3, attributes: 6
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0000)
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+            0      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_1
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 3, attributes: 6
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_2
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 1, methods: 3, attributes: 6
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0000)
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+            0      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_3
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 3, attributes: 6
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: iconst_4
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field value:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+            0      10     1     x   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 1, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: aload_1
+         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+        x: iconst_5
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+            0      15     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+}
+InnerClasses:
+  public #x= #x of #x;                    // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 3, attributes: 6
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: bipush        7
+         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 1, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: bipush        8
+         x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: bipush        6
+         x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+         x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: iload_1
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+            0       6     1     x   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 4, attributes: 5
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: dup
+         x: aload_0
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      17     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: dup
+         x: aload_0
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       9     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+         x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: dup
+         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;          // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
+  Compiled from "TinyFrameworkPackageRedirect.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int foo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class com/supported/UnsupportedClass
+         x: dup
+         x: iload_0
+         x: invokespecial #x                 // Method com/supported/UnsupportedClass."<init>":(I)V
+         x: invokevirtual #x                 // Method com/supported/UnsupportedClass.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      12     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkPackageRedirect.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+  Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int foo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: dup
+         x: iload_0
+         x: invokespecial #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
+         x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      12     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+  Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+  Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+  Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+  Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+  Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+  Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+  Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/supported/UnsupportedClass.class
+  Compiled from "UnsupportedClass.java"
+public class com.supported.UnsupportedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/supported/UnsupportedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  private final int mValue;
+    descriptor: I
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.supported.UnsupportedClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field mValue:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/supported/UnsupportedClass;
+            0      10     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mValue:I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/supported/UnsupportedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "UnsupportedClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/unsupported/UnsupportedClass.class
+  Compiled from "UnsupportedClass.java"
+public class com.unsupported.UnsupportedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/unsupported/UnsupportedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 3
+  public com.unsupported.UnsupportedClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String This class is not supported
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      14     0  this   Lcom/unsupported/UnsupportedClass;
+            0      14     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=3, locals=1, args_size=1
+         x: new           #x                 // class java/lang/RuntimeException
+         x: dup
+         x: ldc           #x                 // String This class is not supported
+         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+         x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/unsupported/UnsupportedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "UnsupportedClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+  Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 3
+  private final int mValue;
+    descriptor: I
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=2, locals=2, args_size=2
+         x: aload_0
+         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+         x: aload_0
+         x: iload_1
+         x: putfield      #x                 // Field mValue:I
+         x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+            0      10     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=1, locals=1, args_size=1
+         x: aload_0
+         x: getfield      #x                 // Field mValue:I
+         x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
deleted file mode 100644
index 0f5f7e7..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ /dev/null
@@ -1,4088 +0,0 @@
-## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
-  Compiled from "HostSideTestClassLoadHook.java"
-public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  public abstract java.lang.String value();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestKeep.class
-  Compiled from "HostSideTestKeep.java"
-public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestKeep.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
-  Compiled from "HostSideTestNativeSubstitutionClass.java"
-public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  public abstract java.lang.String value();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestNativeSubstitutionClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestRemove.class
-  Compiled from "HostSideTestRemove.java"
-public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestRemove.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestStub.class
-  Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestSubstitute.class
-  Compiled from "HostSideTestSubstitute.java"
-public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  public abstract java.lang.String suffix();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestSubstitute.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.METHOD]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestThrow.class
-  Compiled from "HostSideTestThrow.java"
-public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestThrow.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
-  Compiled from "HostSideTestWholeClassKeep.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestWholeClassKeep.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
-  Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_2
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0     a   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addOne(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_1
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0     a   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
-  Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 4
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
-  public static int[] ARRAY;
-    descriptor: [I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.R$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: iconst_1
-         x: newarray       int
-         x: dup
-         x: iconst_0
-         x: iconst_1
-         x: iastore
-         x: putstatic     #x                 // Field ARRAY:[I
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.R();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOneKeep();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         x: ldc           #x                 // String getOneKeep
-         x: ldc           #x                 // String ()I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_1
-        x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  public static int getOneStub();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_1
-         x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
-  private static #x= #x of #x;           // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 5
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_withCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
-         x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_noCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
-         x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
-  Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iconst_1
-         x: putfield      #x                 // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-            0       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String addOneInner
-         x: ldc           #x                 // String (I)I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_1
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-           15       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_2
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-            0       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_3
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String unsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestThrow
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
-  Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 8, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int remove;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iconst_1
-         x: putfield      #x                 // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_1
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void toBeRemoved(java.lang.String);
-    descriptor: (Ljava/lang/String;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       8     1   foo   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_2
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-            0       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_3
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
-  Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
-    descriptor: Ljava/util/Set;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void onClassLoaded(java.lang.Class<?>);
-    descriptor: (Ljava/lang/Class;)V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-         x: aload_0
-         x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
-         x: pop
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      11     0 clazz   Ljava/lang/Class;
-      LocalVariableTypeTable:
-        Start  Length  Slot  Name   Signature
-            0      11     0 clazz   Ljava/lang/Class<*>;
-    Signature: #x                          // (Ljava/lang/Class<*>;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: new           #x                 // class java/util/HashSet
-         x: dup
-         x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
-         x: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
-  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
-  Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: iconst_1
-         x: putstatic     #x                 // Field sInitialized:Z
-        x: new           #x                  // class java/lang/Object
-        x: dup
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: putstatic     #x                 // Field sObject:Ljava/lang/Object;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
-  Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 6, methods: 7, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private final java.lang.String mLongName;
-    descriptor: Ljava/lang/String;
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private final java.lang.String mShortName;
-    descriptor: Ljava/lang/String;
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
-         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: aload_0
-         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
-         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  name   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
-    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=5, args_size=5
-         x: aload_0
-         x: aload_1
-         x: iload_2
-         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
-         x: aload_0
-         x: aload_3
-         x: putfield      #x                 // Field mLongName:Ljava/lang/String;
-        x: aload_0
-        x: aload         4
-        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-            0      18     3 longName   Ljava/lang/String;
-            0      18     4 shortName   Ljava/lang/String;
-    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-      <no name>
-      <no name>
-
-  public java.lang.String getLongName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: getfield      #x                 // Field mLongName:Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.lang.String getShortName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: getfield      #x                 // Field mShortName:Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: iconst_3
-         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: dup
-         x: iconst_0
-         x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-         x: aastore
-        x: dup
-        x: iconst_1
-        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: aastore
-        x: dup
-        x: iconst_2
-        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: aastore
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=6, locals=0, args_size=0
-         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: dup
-         x: ldc           #x                 // String RED
-         x: iconst_0
-         x: ldc           #x                 // String Red
-         x: ldc           #x                 // String R
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: ldc           #x                 // String GREEN
-        x: iconst_1
-        x: ldc           #x                 // String Green
-        x: ldc           #x                 // String G
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: ldc           #x                 // String BLUE
-        x: iconst_2
-        x: ldc           #x                 // String Blue
-        x: ldc           #x                 // String B
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
-  Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 3, methods: 5, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-         x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
-         x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: aload_0
-         x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
-         x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  name   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
-    descriptor: (Ljava/lang/String;I)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=3, args_size=3
-         x: aload_0
-         x: aload_1
-         x: iload_2
-         x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    Signature: #x                          // ()V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: iconst_2
-         x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: dup
-         x: iconst_0
-         x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-         x: aastore
-        x: dup
-        x: iconst_1
-        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: aastore
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: dup
-         x: ldc           #x                 // String CAT
-         x: iconst_0
-         x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
-        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: dup
-        x: ldc           #x                 // String DOG
-        x: iconst_1
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
-        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
-  Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int testException();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=0
-         x: new           #x                 // class java/lang/IllegalStateException
-         x: dup
-         x: ldc           #x                 // String Inner exception
-         x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
-         x: athrow
-        x: astore_0
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Outer exception
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
-        x: athrow
-      Exception table:
-         from    to  target type
-             0    10    10   Class java/lang/Exception
-      StackMapTable: number_of_entries = 1
-        frame_type = 74 /* same_locals_1_stack_item */
-          stack = [ class java/lang/Exception ]
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      11     0     e   Ljava/lang/Exception;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
-  Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iconst_1
-         x: putfield      #x                 // Field stub:I
-         x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokevirtual #x                 // Method addOneInner:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-            0       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String addOneInner
-         x: ldc           #x                 // String (I)I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_1
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-           15       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_1
-         x: iconst_2
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-            0       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: iload_0
-         x: iconst_3
-         x: iadd
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String unsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 6
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: bipush        8
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: bipush        7
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: bipush        6
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_5
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-         x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-BootstrapMethods:
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 6
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_4
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_3
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_2
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: iconst_1
-         x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-         x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=1, locals=0, args_size=0
-         x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-         x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-BootstrapMethods:
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
-  Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 11, attributes: 3
-  int value;
-    descriptor: I
-    flags: (0x0000)
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: iload_0
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-         x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: iload_0
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: lload_0
-         x: lload_2
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-         x: lreturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus_should_be_like_this(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: lload_0
-         x: lload_2
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-         x: lreturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  arg1   J
-            0       6     2  arg2   J
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void setValue(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: putfield      #x                 // Field value:I
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-            0       6     1     v   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-         x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-            0       6     1   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void nativeStillNotSupported();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeStillNotSupported
-         x: ldc           #x                 // String ()V
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestThrow
-
-  public static void nativeStillNotSupported_should_be_like_this();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-         x: athrow
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: iload_0
-         x: iload_1
-         x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
-         x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
-      value="TinyFrameworkNative_host"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
-  Compiled from "TinyFrameworkNative_host.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 5, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeAddTwo
-         x: ldc           #x                 // String (I)I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_0
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       4     0   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeLongPlus
-         x: ldc           #x                 // String (JJ)J
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: lload_0
-        x: lload_2
-        x: ladd
-        x: lreturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       4     0  arg1   J
-           15       4     2  arg2   J
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeNonStaticAddToValue
-         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: getfield      #x                 // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
-        x: iload_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       7     0 source   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-           15       7     1   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeBytePlus
-         x: ldc           #x                 // String (BB)B
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_0
-        x: iload_1
-        x: iadd
-        x: i2b
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  arg1   B
-           15       5     1  arg2   B
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative_host.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 1, methods: 3, attributes: 6
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0000)
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: aload_1
-         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-            0      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_1
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 3, attributes: 6
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_2
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 1, methods: 3, attributes: 6
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0000)
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: aload_1
-         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-            0      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_3
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 3, attributes: 6
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_4
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 1, attributes: 4
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iload_1
-         x: putfield      #x                 // Field value:I
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
-            0      10     1     x   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: aload_1
-         x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-        x: iconst_5
-        x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
-            0      15     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-}
-InnerClasses:
-  public #x= #x of #x;                   // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 3, attributes: 6
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: bipush        7
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: bipush        6
-         x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: dup
-         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
-         x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: iload_1
-         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
-            0       6     1     x   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 4, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: dup
-         x: aload_0
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      17     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: dup
-         x: aload_0
-         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       9     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: dup
-         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
-         x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: dup
-         x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
-         x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
-  Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class com/supported/UnsupportedClass
-         x: dup
-         x: iload_0
-         x: invokespecial #x                 // Method com/supported/UnsupportedClass."<init>":(I)V
-         x: invokevirtual #x                 // Method com/supported/UnsupportedClass.getValue:()I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      12     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
-  Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: dup
-         x: iload_0
-         x: invokespecial #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
-         x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      12     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
-  Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
-  Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
-  Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
-  Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
-  Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
-  Compiled from "Class_C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
-  Compiled from "Class_C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
-  Compiled from "Class_C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
-  Compiled from "Class_I1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
-  Compiled from "Class_I1_IA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 2, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_I1_IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
-  Compiled from "Class_I2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
-  Compiled from "Class_I3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "Class_I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
-  Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
-  Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
-  Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
-  Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
-  Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/supported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.supported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/supported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.supported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // String com/supported/UnsupportedClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iload_1
-        x: putfield      #x                 // Field mValue:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15      10     0  this   Lcom/supported/UnsupportedClass;
-           15      10     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // String com/supported/UnsupportedClass
-         x: ldc           #x                 // String getValue
-         x: ldc           #x                 // String ()I
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-         x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: getfield      #x                 // Field mValue:I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           15       5     0  this   Lcom/supported/UnsupportedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassKeep
-## Class: com/unsupported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/unsupported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.unsupported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String This class is not supported
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      14     0  this   Lcom/unsupported/UnsupportedClass;
-            0      14     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String This class is not supported
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/unsupported/UnsupportedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
-  Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=2, locals=2, args_size=2
-         x: aload_0
-         x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-         x: aload_0
-         x: iload_1
-         x: putfield      #x                 // Field mValue:I
-         x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
-            0      10     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=1, locals=1, args_size=1
-         x: aload_0
-         x: getfield      #x                 // Field mValue:I
-         x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-            0       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
deleted file mode 100644
index 17ba48c..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ /dev/null
@@ -1,2530 +0,0 @@
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addOne(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
-  Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 4
-}
-InnerClasses:
-  public static #x= #x of #x;            // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
-  public static int[] ARRAY;
-    descriptor: [I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.R$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.R();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOneStub();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
-  private static #x= #x of #x;           // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 5
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_withCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_noCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
-  Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 5, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
-  Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 8, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int remove;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void toBeRemoved(java.lang.String);
-    descriptor: (Ljava/lang/String;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
-  Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
-    descriptor: Ljava/util/Set;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void onClassLoaded(java.lang.Class<?>);
-    descriptor: (Ljava/lang/Class;)V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // (Ljava/lang/Class<*>;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
-  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
-  Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
-  Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 4, methods: 7, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
-    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=5, args_size=5
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-      <no name>
-      <no name>
-
-  public java.lang.String getLongName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.lang.String getShortName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
-  Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 3, methods: 5, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
-    descriptor: (Ljava/lang/String;I)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=3, locals=3, args_size=3
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
-  Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int testException();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
-  Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 5, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 7, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 7, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
-  Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 10, attributes: 3
-  int value;
-    descriptor: I
-    flags: (0x0000)
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus_should_be_like_this(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=4, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void setValue(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public native int nativeNonStaticAddToValue(int);
-    descriptor: (I)I
-    flags: (0x0101) ACC_PUBLIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void nativeStillNotSupported_should_be_like_this();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static native byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
-      value="TinyFrameworkNative_host"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 1, attributes: 4
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-}
-InnerClasses:
-  public #x= #x of #x;                   // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 4, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=3, locals=0, args_size=0
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
-  Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
-  Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
-  Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
-  Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
-  Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
-  Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
-  Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
-  Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
-  Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
-  Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
-  Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
-  Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 0, attributes: 2
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/unsupported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/unsupported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 3
-  public com.unsupported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
-  Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=2, args_size=2
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=3, locals=1, args_size=1
-         x: new           #x                 // class java/lang/RuntimeException
-         x: dup
-         x: ldc           #x                 // String Stub!
-         x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-         x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
new file mode 100644
index 0000000..2ca723b
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-dump.txt
@@ -0,0 +1,4787 @@
+## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
+  Compiled from "HostSideTestClassLoadHook.java"
+public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 2, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestClassLoadHook
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public abstract java.lang.String value();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestClassLoadHook.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestKeep.class
+  Compiled from "HostSideTestKeep.java"
+public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestKeep
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "HostSideTestKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
+  Compiled from "HostSideTestNativeSubstitutionClass.java"
+public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 2, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestNativeSubstitutionClass
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public abstract java.lang.String value();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestNativeSubstitutionClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestRemove.class
+  Compiled from "HostSideTestRemove.java"
+public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestRemove
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "HostSideTestRemove.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestStaticInitializerKeep.class
+  Compiled from "HostSideTestStaticInitializerKeep.java"
+public interface android.hosttest.annotation.HostSideTestStaticInitializerKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestStaticInitializerKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestStaticInitializerKeep
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "HostSideTestStaticInitializerKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestSubstitute.class
+  Compiled from "HostSideTestSubstitute.java"
+public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 2, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestSubstitute
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public abstract java.lang.String suffix();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "HostSideTestSubstitute.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.METHOD]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestThrow.class
+  Compiled from "HostSideTestThrow.java"
+public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestThrow
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "HostSideTestThrow.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x,e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
+  Compiled from "HostSideTestWholeClassKeep.java"
+public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
+  minor version: 0
+  major version: 61
+  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
+  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestWholeClassKeep
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "HostSideTestWholeClassKeep.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+  x: #x(#x=[e#x.#x])
+    java.lang.annotation.Target(
+      value=[Ljava/lang/annotation/ElementType;.TYPE]
+    )
+  x: #x(#x=e#x.#x)
+    java.lang.annotation.Retention(
+      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
+    )
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
+  Compiled from "IPretendingAidl.java"
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+         x: ldc           #x                 // String addTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iconst_2
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0     a   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;          // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
+  Compiled from "IPretendingAidl.java"
+public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int addOne(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+         x: ldc           #x                 // String addOne
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iconst_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0     a   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
+  Compiled from "IPretendingAidl.java"
+public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+InnerClasses:
+  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
+  public static #x= #x of #x;          // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+SourceFile: "IPretendingAidl.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public static int[] ARRAY;
+    descriptor: [I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.R$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: iconst_1
+        x: newarray       int
+        x: dup
+        x: iconst_0
+        x: iconst_1
+        x: iastore
+        x: putstatic     #x                 // Field ARRAY:[I
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;             // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+  Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.R();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/R$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
+  Compiled from "TinyFrameworkAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 6, attributes: 3
+  public int keep;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iconst_1
+        x: putfield      #x                 // Field keep:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String addOne
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String addTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_2
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddThree(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String nativeAddThree
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iconst_3
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
+         x: ldc           #x                 // String unsupportedMethod
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkAnnotations.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestClassLoadHook(
+      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+    )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
+  Compiled from "TinyFrameworkClassLoadHook.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
+    descriptor: Ljava/util/Set;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
+    descriptor: ()V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void onClassLoaded(java.lang.Class<?>);
+    descriptor: (Ljava/lang/Class;)V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+         x: ldc           #x                 // String onClassLoaded
+         x: ldc           #x                 // String (Ljava/lang/Class;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+        x: aload_0
+        x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
+        x: pop
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0 clazz   Ljava/lang/Class;
+      LocalVariableTypeTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0 clazz   Ljava/lang/Class<*>;
+    Signature: #x                          // (Ljava/lang/Class<*>;)V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: new           #x                 // class java/util/HashSet
+        x: dup
+        x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
+        x: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkClassLoadHook.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.class
+  Compiled from "TinyFrameworkClassWideAnnotations.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 5, attributes: 3
+  public int keep;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWideAnnotations();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iconst_1
+        x: putfield      #x                 // Field keep:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+         x: ldc           #x                 // String addOne
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+         x: ldc           #x                 // String addTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_2
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
+         x: ldc           #x                 // String unsupportedMethod
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+}
+SourceFile: "TinyFrameworkClassWideAnnotations.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
+  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 0, attributes: 3
+  public static boolean sInitialized;
+    descriptor: Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.lang.Object sObject;
+    descriptor: Ljava/lang/Object;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+}
+SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
+  Compiled from "TinyFrameworkClassWithInitializerStub.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 1, attributes: 3
+  public static boolean sInitialized;
+    descriptor: Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.lang.Object sObject;
+    descriptor: Ljava/lang/Object;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
+        x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: iconst_1
+        x: putstatic     #x                 // Field sInitialized:Z
+        x: new           #x                  // class java/lang/Object
+        x: dup
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: putstatic     #x                 // Field sObject:Ljava/lang/Object;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkClassWithInitializerStub.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestClassLoadHook(
+      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
+    )
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
+  Compiled from "TinyFrameworkEnumComplex.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 6, methods: 7, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mLongName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private final java.lang.String mShortName;
+    descriptor: Ljava/lang/String;
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
+        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String valueOf
+         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: aload_0
+        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  name   Ljava/lang/String;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      mandated
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
+    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=4, locals=5, args_size=5
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: iload_2
+        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+        x: aload_0
+        x: aload_3
+        x: putfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: aload_0
+        x: aload         4
+        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+           11      18     3 longName   Ljava/lang/String;
+           11      18     4 shortName   Ljava/lang/String;
+    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      synthetic
+      <no name>                      synthetic
+      <no name>
+      <no name>
+
+  public java.lang.String getLongName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String getLongName
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mLongName:Ljava/lang/String;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.lang.String getShortName();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String getShortName
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mShortName:Ljava/lang/String;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String $values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_3
+        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: iconst_0
+        x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: dup
+        x: iconst_2
+        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=6, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String RED
+        x: iconst_0
+        x: ldc           #x                 // String Red
+        x: ldc           #x                 // String R
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String GREEN
+        x: iconst_1
+        x: ldc           #x                 // String Green
+        x: ldc           #x                 // String G
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
+        x: dup
+        x: ldc           #x                 // String BLUE
+        x: iconst_2
+        x: ldc           #x                 // String Blue
+        x: ldc           #x                // String B
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: invokestatic  #x                // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
+SourceFile: "TinyFrameworkEnumComplex.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
+  Compiled from "TinyFrameworkEnumSimple.java"
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
+  minor version: 0
+  major version: 61
+  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+  super_class: #x                         // java/lang/Enum
+  interfaces: 0, fields: 3, methods: 5, attributes: 4
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
+    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
+        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
+    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String valueOf
+         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: aload_0
+        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  name   Ljava/lang/String;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      mandated
+
+  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
+    descriptor: (Ljava/lang/String;I)V
+    flags: (0x0002) ACC_PRIVATE
+    Code:
+      stack=4, locals=3, args_size=3
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Ljava/lang/String;I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: iload_2
+        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    Signature: #x                          // ()V
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      synthetic
+      <no name>                      synthetic
+
+  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
+    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String $values
+         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_2
+        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: iconst_0
+        x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: dup
+        x: iconst_1
+        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: aastore
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String CAT
+        x: iconst_0
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
+        x: dup
+        x: ldc           #x                 // String DOG
+        x: iconst_1
+        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
+        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
+SourceFile: "TinyFrameworkEnumSimple.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
+  Compiled from "TinyFrameworkExceptionTester.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int testException();
+    descriptor: ()I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
+         x: ldc           #x                 // String testException
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class java/lang/IllegalStateException
+        x: dup
+        x: ldc           #x                 // String Inner exception
+        x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
+        x: athrow
+        x: astore_0
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Outer exception
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
+        x: athrow
+      Exception table:
+         from    to  target type
+            11    21    21   Class java/lang/Exception
+      StackMapTable: number_of_entries = 1
+        frame_type = 85 /* same_locals_1_stack_item */
+          stack = [ class java/lang/Exception ]
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           22      11     0     e   Ljava/lang/Exception;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkExceptionTester.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
+  Compiled from "TinyFrameworkForTextPolicy.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 15, attributes: 2
+  public int stub;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iconst_1
+        x: putfield      #x                 // Field stub:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addOne(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String addOne
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String toBeIgnoredObj();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredObj
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aconst_null
+        x: areturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public void toBeIgnoredV();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredV
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: return
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public boolean toBeIgnoredZ();
+    descriptor: ()Z
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredZ
+         x: ldc           #x                 // String ()Z
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public byte toBeIgnoredB();
+    descriptor: ()B
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredB
+         x: ldc           #x                 // String ()B
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public char toBeIgnoredC();
+    descriptor: ()C
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredC
+         x: ldc           #x                 // String ()C
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public short toBeIgnoredS();
+    descriptor: ()S
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredS
+         x: ldc           #x                 // String ()S
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int toBeIgnoredI();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredI
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_0
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public float toBeIgnoredF();
+    descriptor: ()F
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredF
+         x: ldc           #x                 // String ()F
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: fconst_0
+        x: freturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public double toBeIgnoredD();
+    descriptor: ()D
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String toBeIgnoredD
+         x: ldc           #x                 // String ()D
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: dconst_0
+        x: dreturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsIgnore
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int addTwo(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String addTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_1
+        x: iconst_2
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
+           11       4     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddThree(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String nativeAddThree
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iconst_3
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.String unsupportedMethod();
+    descriptor: ()Ljava/lang/String;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
+         x: ldc           #x                 // String unsupportedMethod
+         x: ldc           #x                 // String ()Ljava/lang/String;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkForTextPolicy.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
+  Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 8, attributes: 6
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String getSupplier
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String getSupplier_static
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static java.lang.Integer lambda$getSupplier_static$3();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String lambda$getSupplier_static$3
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: bipush        8
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$getSupplier$2();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String lambda$getSupplier$2
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: bipush        7
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$static$1();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String lambda$static$1
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: bipush        6
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$new$0();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String lambda$new$0
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_5
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
+  Compiled from "TinyFrameworkLambdas.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 8, attributes: 6
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String getSupplier
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String getSupplier_static
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  private static java.lang.Integer lambda$getSupplier_static$3();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String lambda$getSupplier_static$3
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_4
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$getSupplier$2();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String lambda$getSupplier$2
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_3
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$static$1();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String lambda$static$1
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_2
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static java.lang.Integer lambda$new$0();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String lambda$new$0
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_1
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
+        x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkLambdas.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestKeep
+  x: #x()
+    android.hosttest.annotation.HostSideTestStaticInitializerKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()Ljava/lang/Object;
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
+      #x ()Ljava/lang/Integer;
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 4, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void startThread(java.lang.Thread);
+    descriptor: (Ljava/lang/Thread;)V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                 // String startThread
+         x: ldc           #x                 // String (Ljava/lang/Thread;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: iconst_1
+        x: invokevirtual #x                 // Method java/lang/Thread.setDaemon:(Z)V
+        x: aload_0
+        x: invokevirtual #x                 // Method java/lang/Thread.start:()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0 thread   Ljava/lang/Thread;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int add(int, int);
+    descriptor: (II)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+         x: ldc           #x                 // String add
+         x: ldc           #x                 // String (II)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iload_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0     a   I
+           11       4     1     b   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.class
+  Compiled from "TinyFrameworkMethodCallReplace.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 5, attributes: 6
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static boolean nonStaticMethodCallReplaceTester() throws java.lang.Exception;
+    descriptor: ()Z
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String nonStaticMethodCallReplaceTester
+         x: ldc           #x                 // String ()Z
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class java/util/concurrent/atomic/AtomicBoolean
+        x: dup
+        x: iconst_0
+        x: invokespecial #x                 // Method java/util/concurrent/atomic/AtomicBoolean."<init>":(Z)V
+        x: astore_0
+        x: new           #x                 // class java/lang/Thread
+        x: dup
+        x: aload_0
+        x: invokedynamic #x,  0             // InvokeDynamic #x:run:(Ljava/util/concurrent/atomic/AtomicBoolean;)Ljava/lang/Runnable;
+        x: invokespecial #x                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
+        x: astore_1
+        x: aload_1
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.startThread:(Ljava/lang/Thread;)V
+        x: aload_1
+        x: invokevirtual #x                 // Method java/lang/Thread.join:()V
+        x: aload_0
+        x: invokevirtual #x                 // Method java/util/concurrent/atomic/AtomicBoolean.get:()Z
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           20      27     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+           34      13     1    th   Ljava/lang/Thread;
+    Exceptions:
+      throws java.lang.Exception
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int staticMethodCallReplaceTester();
+    descriptor: ()I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String staticMethodCallReplaceTester
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_1
+        x: iconst_2
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo.add:(II)I
+        x: ireturn
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static void lambda$nonStaticMethodCallReplaceTester$0(java.util.concurrent.atomic.AtomicBoolean);
+    descriptor: (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+         x: ldc           #x                 // String lambda$nonStaticMethodCallReplaceTester$0
+         x: ldc           #x                 // String (Ljava/util/concurrent/atomic/AtomicBoolean;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokestatic  #x                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
+        x: invokevirtual #x                // Method java/lang/Thread.isDaemon:()Z
+        x: invokevirtual #x                // Method java/util/concurrent/atomic/AtomicBoolean.set:(Z)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0    ab   Ljava/util/concurrent/atomic/AtomicBoolean;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // ReplaceTo=class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo of class com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
+  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
+SourceFile: "TinyFrameworkMethodCallReplace.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+BootstrapMethods:
+  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+    Method arguments:
+      #x ()V
+      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.lambda$nonStaticMethodCallReplaceTester$0:(Ljava/util/concurrent/atomic/AtomicBoolean;)V
+      #x ()V
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
+  Compiled from "TinyFrameworkNative.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 13, attributes: 3
+  int value;
+    descriptor: I
+    flags: (0x0000)
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeAddTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo_should_be_like_this(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeAddTwo_should_be_like_this
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeLongPlus
+         x: ldc           #x                 // String (JJ)J
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: lload_0
+        x: lload_2
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+        x: lreturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus_should_be_like_this(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeLongPlus_should_be_like_this
+         x: ldc           #x                 // String (JJ)J
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: lload_0
+        x: lload_2
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
+        x: lreturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  arg1   J
+           11       6     2  arg2   J
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public void setValue(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String setValue
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+           11       6     1     v   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int nativeNonStaticAddToValue(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeNonStaticAddToValue
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: iload_1
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int nativeNonStaticAddToValue_should_be_like_this(int);
+    descriptor: (I)I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeNonStaticAddToValue_should_be_like_this
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: iload_1
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+           11       6     1   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static void nativeStillNotSupported();
+    descriptor: ()V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeStillNotSupported
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String Unreachable
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestThrow
+
+  public static native void nativeStillKeep();
+    descriptor: ()V
+    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    RuntimeInvisibleAnnotations:
+      x: #x()
+        android.hosttest.annotation.HostSideTestKeep
+
+  public static void nativeStillNotSupported_should_be_like_this();
+    descriptor: ()V
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeStillNotSupported_should_be_like_this
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
+        x: athrow
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static byte nativeBytePlus(byte, byte);
+    descriptor: (BB)B
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
+         x: ldc           #x                 // String nativeBytePlus
+         x: ldc           #x                 // String (BB)B
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iload_1
+        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
+        x: ireturn
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkNative.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+  x: #x(#x=s#x)
+    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
+      value="TinyFrameworkNative_host"
+    )
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
+  Compiled from "TinyFrameworkNative_host.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 6, attributes: 3
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeAddTwo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeAddTwo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iconst_2
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static long nativeLongPlus(long, long);
+    descriptor: (JJ)J
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=4, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeLongPlus
+         x: ldc           #x                 // String (JJ)J
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: lload_0
+        x: lload_2
+        x: ladd
+        x: lreturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       4     0  arg1   J
+           11       4     2  arg2   J
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeNonStaticAddToValue
+         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
+        x: iload_1
+        x: iadd
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       7     0 source   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
+           11       7     1   arg   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static byte nativeBytePlus(byte, byte);
+    descriptor: (BB)B
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
+         x: ldc           #x                 // String nativeBytePlus
+         x: ldc           #x                 // String (BB)B
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iload_0
+        x: iload_1
+        x: iadd
+        x: i2b
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  arg1   B
+           11       5     1  arg2   B
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkNative_host.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 1, methods: 4, attributes: 6
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0000)
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+           11      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_1
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 4, attributes: 6
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_2
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 1, methods: 4, attributes: 6
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0000)
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+           11      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_3
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 4, attributes: 6
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: iconst_4
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
+           11      10     1     x   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 2, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
+    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
+    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: aload_1
+        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iconst_5
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
+           11      15     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+    MethodParameters:
+      Name                           Flags
+      <no name>                      final mandated
+}
+InnerClasses:
+  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
+  minor version: 0
+  major version: 61
+  flags: (0x0020) ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 4, attributes: 6
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
+    descriptor: ()V
+    flags: (0x0000)
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Integer get();
+    descriptor: ()Ljava/lang/Integer;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Integer;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: bipush        7
+        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.lang.Object get();
+    descriptor: ()Ljava/lang/Object;
+    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+         x: ldc           #x                 // String get
+         x: ldc           #x                 // String ()Ljava/lang/Object;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
+Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 2, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: bipush        8
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 3, attributes: 4
+  public int value;
+    descriptor: I
+    flags: (0x0001) ACC_PUBLIC
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: bipush        6
+        x: putfield      #x                 // Field value:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+         x: ldc           #x                 // String getSupplier_static
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+        x: dup
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
+        x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  public static #x= #x of #x;           // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  interfaces: 0, fields: 0, methods: 2, attributes: 4
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: iload_1
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
+           11       6     1     x   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
+  Compiled from "TinyFrameworkNestedClasses.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 2, methods: 4, attributes: 5
+  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
+    descriptor: Ljava/util/function/Supplier;
+    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+        x: dup
+        x: aload_0
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      17     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public java.util.function.Supplier<java.lang.Integer> getSupplier();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+         x: ldc           #x                 // String getSupplier
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+        x: dup
+        x: aload_0
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
+        x: areturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       9     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
+    descriptor: ()Ljava/util/function/Supplier;
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+         x: ldc           #x                 // String getSupplier_static
+         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+        x: dup
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
+        x: areturn
+      LineNumberTable:
+    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  static {};
+    descriptor: ()V
+    flags: (0x0008) ACC_STATIC
+    Code:
+      stack=4, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+         x: ldc           #x                 // String <clinit>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+        x: dup
+        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
+        x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
+        x: return
+      LineNumberTable:
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+InnerClasses:
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  public static #x= #x of #x;            // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
+  public static #x= #x of #x;          // Double$NestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+SourceFile: "TinyFrameworkNestedClasses.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+NestMembers:
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
+  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
+  Compiled from "TinyFrameworkPackageRedirect.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int foo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
+         x: ldc           #x                 // String foo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class com/supported/UnsupportedClass
+        x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method com/supported/UnsupportedClass."<init>":(I)V
+        x: invokevirtual #x                 // Method com/supported/UnsupportedClass.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      12     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkPackageRedirect.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
+  Compiled from "TinyFrameworkRenamedClassCaller.java"
+public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
+    descriptor: ()V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String ()V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public static int foo(int);
+    descriptor: (I)I
+    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
+         x: ldc           #x                 // String foo
+         x: ldc           #x                 // String (I)I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+        x: dup
+        x: iload_0
+        x: invokespecial #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
+        x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      12     0 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkRenamedClassCaller.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/A
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
+  Compiled from "A.java"
+public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "A.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+  Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+  Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+  Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+  Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+  Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+  Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+  Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+  Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+  Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+  Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 2, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+  Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+  Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+  Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+  Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I2
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+  Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+  super_class: #x                         // java/lang/Object
+  interfaces: 1, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I3
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+  Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+  Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+  minor version: 0
+  major version: 61
+  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 1, attributes: 2
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+## Class: com/supported/UnsupportedClass.class
+  Compiled from "UnsupportedClass.java"
+public class com.supported.UnsupportedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/supported/UnsupportedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  private final int mValue;
+    descriptor: I
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/supported/UnsupportedClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.supported.UnsupportedClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/supported/UnsupportedClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field mValue:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/supported/UnsupportedClass;
+           11      10     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/supported/UnsupportedClass
+         x: ldc           #x                 // String getValue
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mValue:I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/supported/UnsupportedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "UnsupportedClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: com/unsupported/UnsupportedClass.class
+  Compiled from "UnsupportedClass.java"
+public class com.unsupported.UnsupportedClass
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // com/unsupported/UnsupportedClass
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 0, methods: 3, attributes: 3
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/unsupported/UnsupportedClass
+         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public com.unsupported.UnsupportedClass(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/unsupported/UnsupportedClass
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String This class is not supported
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      14     0  this   Lcom/unsupported/UnsupportedClass;
+           11      14     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/unsupported/UnsupportedClass
+         x: ldc           #x                 // String getValue
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: new           #x                 // class java/lang/RuntimeException
+        x: dup
+        x: ldc           #x                 // String This class is not supported
+        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+        x: athrow
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/unsupported/UnsupportedClass;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "UnsupportedClass.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
+## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
+  Compiled from "TinyFrameworkToBeRenamed.java"
+public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
+  minor version: 0
+  major version: 61
+  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+  super_class: #x                         // java/lang/Object
+  interfaces: 0, fields: 1, methods: 3, attributes: 3
+  private final int mValue;
+    descriptor: I
+    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  private static {};
+    descriptor: ()V
+    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    Code:
+      stack=2, locals=0, args_size=0
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+         x: return
+
+  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
+    descriptor: (I)V
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=2, args_size=2
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                 // String <init>
+         x: ldc           #x                 // String (I)V
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
+        x: aload_0
+        x: iload_1
+        x: putfield      #x                 // Field mValue:I
+        x: return
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+           11      10     1 value   I
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+
+  public int getValue();
+    descriptor: ()I
+    flags: (0x0001) ACC_PUBLIC
+    Code:
+      stack=4, locals=1, args_size=1
+         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
+         x: ldc           #x                 // String getValue
+         x: ldc           #x                 // String ()I
+         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+        x: aload_0
+        x: getfield      #x                 // Field mValue:I
+        x: ireturn
+      LineNumberTable:
+      LocalVariableTable:
+        Start  Length  Slot  Name   Signature
+           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
+    RuntimeVisibleAnnotations:
+      x: #x()
+        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+}
+SourceFile: "TinyFrameworkToBeRenamed.java"
+RuntimeVisibleAnnotations:
+  x: #x()
+    com.android.hoststubgen.hosthelper.HostStubGenProcessedAsKeep
+RuntimeInvisibleAnnotations:
+  x: #x()
+    android.hosttest.annotation.HostSideTestWholeClassKeep
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
deleted file mode 100644
index 3beea64..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ /dev/null
@@ -1,5207 +0,0 @@
-## Class: android/hosttest/annotation/HostSideTestClassLoadHook.class
-  Compiled from "HostSideTestClassLoadHook.java"
-public interface android.hosttest.annotation.HostSideTestClassLoadHook extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 2, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestClassLoadHook
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public abstract java.lang.String value();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestKeep.class
-  Compiled from "HostSideTestKeep.java"
-public interface android.hosttest.annotation.HostSideTestKeep extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestKeep
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestKeep.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestNativeSubstitutionClass.class
-  Compiled from "HostSideTestNativeSubstitutionClass.java"
-public interface android.hosttest.annotation.HostSideTestNativeSubstitutionClass extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestNativeSubstitutionClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 2, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestNativeSubstitutionClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public abstract java.lang.String value();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestNativeSubstitutionClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestRemove.class
-  Compiled from "HostSideTestRemove.java"
-public interface android.hosttest.annotation.HostSideTestRemove extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestRemove
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestRemove.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestStub.class
-  Compiled from "HostSideTestStub.java"
-public interface android.hosttest.annotation.HostSideTestStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestStub
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x,e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE,Ljava/lang/annotation/ElementType;.FIELD,Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestSubstitute.class
-  Compiled from "HostSideTestSubstitute.java"
-public interface android.hosttest.annotation.HostSideTestSubstitute extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 2, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestSubstitute
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public abstract java.lang.String suffix();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "HostSideTestSubstitute.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.METHOD]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestThrow.class
-  Compiled from "HostSideTestThrow.java"
-public interface android.hosttest.annotation.HostSideTestThrow extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestThrow
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestThrow.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x,e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.METHOD,Ljava/lang/annotation/ElementType;.CONSTRUCTOR]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestWholeClassKeep.class
-  Compiled from "HostSideTestWholeClassKeep.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassKeep extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestWholeClassKeep
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestWholeClassKeep.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: android/hosttest/annotation/HostSideTestWholeClassStub.class
-  Compiled from "HostSideTestWholeClassStub.java"
-public interface android.hosttest.annotation.HostSideTestWholeClassStub extends java.lang.annotation.Annotation
-  minor version: 0
-  major version: 61
-  flags: (0x2601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT, ACC_ANNOTATION
-  this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class android/hosttest/annotation/HostSideTestWholeClassStub
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "HostSideTestWholeClassStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-  x: #x(#x=[e#x.#x])
-    java.lang.annotation.Target(
-      value=[Ljava/lang/annotation/ElementType;.TYPE]
-    )
-  x: #x(#x=e#x.#x)
-    java.lang.annotation.Retention(
-      value=Ljava/lang/annotation/RetentionPolicy;.CLASS
-    )
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-         x: ldc           #x                 // String addTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0     a   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;          // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub.class
-  Compiled from "IPretendingAidl.java"
-public class com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int addOne(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-         x: ldc           #x                 // String addOne
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0     a   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;           // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-## Class: com/android/hoststubgen/test/tinyframework/IPretendingAidl.class
-  Compiled from "IPretendingAidl.java"
-public interface com.android.hoststubgen.test.tinyframework.IPretendingAidl
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-InnerClasses:
-  public static #x= #x of #x;           // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
-  public static #x= #x of #x;          // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-SourceFile: "IPretendingAidl.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
-  com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
-## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
-  public static int[] ARRAY;
-    descriptor: [I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.R$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R$Nested;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R$Nested
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: iconst_1
-        x: newarray       int
-        x: dup
-        x: iconst_0
-        x: iconst_1
-        x: iastore
-        x: putstatic     #x                 // Field ARRAY:[I
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/R
-## Class: com/android/hoststubgen/test/tinyframework/R.class
-  Compiled from "R.java"
-public class com.android.hoststubgen.test.tinyframework.R
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.R();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/R
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/R;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
-SourceFile: "R.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/R$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOneKeep();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         x: ldc           #x                 // String getOneKeep
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-        x: ldc           #x                 // String getOneKeep
-        x: ldc           #x                 // String ()I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_1
-        x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  public static int getOneStub();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-         x: ldc           #x                 // String getOneStub
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_1
-        x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.class
-  Compiled from "TinyFrameworkCallerCheck.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 4, attributes: 5
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_withCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-         x: ldc           #x                 // String getOne_withCheck
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneKeep:()I
-        x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int getOne_noCheck();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-         x: ldc           #x                 // String getOne_noCheck
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.getOneStub:()I
-        x: ireturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  private static #x= #x of #x;          // Impl=class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl of class com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck
-SourceFile: "TinyFrameworkCallerCheck.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.class
-  Compiled from "TinyFrameworkClassAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iconst_1
-        x: putfield      #x                 // Field stub:I
-        x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String addOne
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokevirtual #x                 // Method addOneInner:(I)I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-           11       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String addOneInner
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-        x: ldc           #x                 // String addOneInner
-        x: ldc           #x                 // String (I)I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_1
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-           26       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String addTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_1
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-           11       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String nativeAddThree
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iconst_3
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String unsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-        x: ldc           #x                 // String unsupportedMethod
-        x: ldc           #x                 // String ()Ljava/lang/String;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestThrow
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations
-         x: ldc           #x                 // String visibleButUsesUnsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-}
-SourceFile: "TinyFrameworkClassAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.class
-  Compiled from "TinyFrameworkClassClassWideAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 3, methods: 9, attributes: 3
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int remove;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkClassClassWideAnnotations();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iconst_1
-        x: putfield      #x                 // Field stub:I
-        x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String addOne
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokevirtual #x                 // Method addOneInner:(I)I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-           11       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String addOneInner
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_1
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-           11       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void toBeRemoved(java.lang.String);
-    descriptor: (Ljava/lang/String;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String toBeRemoved
-         x: ldc           #x                 // String (Ljava/lang/String;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":()V
-        x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       8     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-           11       8     1   foo   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String addTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_1
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-           11       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String nativeAddThree
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iconst_3
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String unsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String This value shouldn\'t be seen on the host side.
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       3     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations
-         x: ldc           #x                 // String visibleButUsesUnsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassClassWideAnnotations.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.class
-  Compiled from "TinyFrameworkClassLoadHook.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
-    descriptor: Ljava/util/Set;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/Set<Ljava/lang/Class<*>;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook();
-    descriptor: ()V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void onClassLoaded(java.lang.Class<?>);
-    descriptor: (Ljava/lang/Class;)V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-         x: ldc           #x                 // String onClassLoaded
-         x: ldc           #x                 // String (Ljava/lang/Class;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: getstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-        x: aload_0
-        x: invokeinterface #x,  2           // InterfaceMethod java/util/Set.add:(Ljava/lang/Object;)Z
-        x: pop
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      11     0 clazz   Ljava/lang/Class;
-      LocalVariableTypeTable:
-        Start  Length  Slot  Name   Signature
-           11      11     0 clazz   Ljava/lang/Class<*>;
-    Signature: #x                          // (Ljava/lang/Class<*>;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: new           #x                 // class java/util/HashSet
-        x: dup
-        x: invokespecial #x                 // Method java/util/HashSet."<init>":()V
-        x: putstatic     #x                 // Field sLoadedClasses:Ljava/util/Set;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassLoadHook.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.class
-  Compiled from "TinyFrameworkClassWithInitializerDefault.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerDefault
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 0, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-}
-SourceFile: "TinyFrameworkClassWithInitializerDefault.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.class
-  Compiled from "TinyFrameworkClassWithInitializerStub.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkClassWithInitializerStub
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 1, attributes: 3
-  public static boolean sInitialized;
-    descriptor: Z
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.lang.Object sObject;
-    descriptor: Ljava/lang/Object;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
-        x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: iconst_1
-        x: putstatic     #x                 // Field sInitialized:Z
-        x: new           #x                  // class java/lang/Object
-        x: dup
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: putstatic     #x                 // Field sObject:Ljava/lang/Object;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkClassWithInitializerStub.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestClassLoadHook(
-      value="com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded"
-    )
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.class
-  Compiled from "TinyFrameworkEnumComplex.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 6, methods: 7, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex GREEN;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex BLUE;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private final java.lang.String mLongName;
-    descriptor: Ljava/lang/String;
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private final java.lang.String mShortName;
-    descriptor: Ljava/lang/String;
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestKeep
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String values
-         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;".clone:()Ljava/lang/Object;
-        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;"
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String valueOf
-         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: aload_0
-        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
-        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  name   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex(java.lang.String, java.lang.String);
-    descriptor: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=4, locals=5, args_size=5
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: aload_1
-        x: iload_2
-        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
-        x: aload_0
-        x: aload_3
-        x: putfield      #x                 // Field mLongName:Ljava/lang/String;
-        x: aload_0
-        x: aload         4
-        x: putfield      #x                 // Field mShortName:Ljava/lang/String;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      18     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-           11      18     3 longName   Ljava/lang/String;
-           11      18     4 shortName   Ljava/lang/String;
-    Signature: #x                          // (Ljava/lang/String;Ljava/lang/String;)V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-      <no name>
-      <no name>
-
-  public java.lang.String getLongName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String getLongName
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: getfield      #x                 // Field mLongName:Ljava/lang/String;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.lang.String getShortName();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String getShortName
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: getfield      #x                 // Field mShortName:Ljava/lang/String;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String $values
-         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_3
-        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: iconst_0
-        x: getstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: aastore
-        x: dup
-        x: iconst_1
-        x: getstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: aastore
-        x: dup
-        x: iconst_2
-        x: getstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: aastore
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=6, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: ldc           #x                 // String RED
-        x: iconst_0
-        x: ldc           #x                 // String Red
-        x: ldc           #x                 // String R
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field RED:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: ldc           #x                 // String GREEN
-        x: iconst_1
-        x: ldc           #x                 // String Green
-        x: ldc           #x                 // String G
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field GREEN:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
-        x: dup
-        x: ldc           #x                 // String BLUE
-        x: iconst_2
-        x: ldc           #x                // String Blue
-        x: ldc           #x                // String B
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
-        x: putstatic     #x                 // Field BLUE:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: invokestatic  #x                // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;>;
-SourceFile: "TinyFrameworkEnumComplex.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.class
-  Compiled from "TinyFrameworkEnumSimple.java"
-public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple extends java.lang.Enum<com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple>
-  minor version: 0
-  major version: 61
-  flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-  super_class: #x                         // java/lang/Enum
-  interfaces: 0, fields: 3, methods: 5, attributes: 4
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple DOG;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $VALUES;
-    descriptor: [Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: ldc           #x                 // String values
-         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: getstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: invokevirtual #x                 // Method "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;".clone:()Ljava/lang/Object;
-        x: checkcast     #x                 // class "[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;"
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple valueOf(java.lang.String);
-    descriptor: (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: ldc           #x                 // String valueOf
-         x: ldc           #x                 // String (Ljava/lang/String;)Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: aload_0
-        x: invokestatic  #x                 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
-        x: checkcast     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  name   Ljava/lang/String;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      mandated
-
-  private com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple();
-    descriptor: (Ljava/lang/String;I)V
-    flags: (0x0002) ACC_PRIVATE
-    Code:
-      stack=4, locals=3, args_size=3
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (Ljava/lang/String;I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: aload_1
-        x: iload_2
-        x: invokespecial #x                 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       7     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    Signature: #x                          // ()V
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      synthetic
-      <no name>                      synthetic
-
-  private static com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple[] $values();
-    descriptor: ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: ldc           #x                 // String $values
-         x: ldc           #x                 // String ()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_2
-        x: anewarray     #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: dup
-        x: iconst_0
-        x: getstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: aastore
-        x: dup
-        x: iconst_1
-        x: getstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: aastore
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: dup
-        x: ldc           #x                 // String CAT
-        x: iconst_0
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
-        x: putstatic     #x                 // Field CAT:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: new           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
-        x: dup
-        x: ldc           #x                 // String DOG
-        x: iconst_1
-        x: invokespecial #x                 // Method "<init>":(Ljava/lang/String;I)V
-        x: putstatic     #x                 // Field DOG:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: invokestatic  #x                 // Method $values:()[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: putstatic     #x                 // Field $VALUES:[Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-Signature: #x                           // Ljava/lang/Enum<Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;>;
-SourceFile: "TinyFrameworkEnumSimple.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.class
-  Compiled from "TinyFrameworkExceptionTester.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int testException();
-    descriptor: ()I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
-         x: ldc           #x                 // String testException
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class java/lang/IllegalStateException
-        x: dup
-        x: ldc           #x                 // String Inner exception
-        x: invokespecial #x                 // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
-        x: athrow
-        x: astore_0
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Outer exception
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;Ljava/lang/Throwable;)V
-        x: athrow
-      Exception table:
-         from    to  target type
-            11    21    21   Class java/lang/Exception
-      StackMapTable: number_of_entries = 1
-        frame_type = 85 /* same_locals_1_stack_item */
-          stack = [ class java/lang/Exception ]
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           22      11     0     e   Ljava/lang/Exception;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkExceptionTester.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.class
-  Compiled from "TinyFrameworkForTextPolicy.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 2
-  public int stub;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int keep;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkForTextPolicy();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iconst_1
-        x: putfield      #x                 // Field stub:I
-        x: aload_0
-        x: iconst_2
-        x: putfield      #x                 // Field keep:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOne(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String addOne
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokevirtual #x                 // Method addOneInner:(I)I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-           11       6     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addOneInner(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String addOneInner
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-        x: ldc           #x                 // String addOneInner
-        x: ldc           #x                 // String (I)I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_1
-        x: iconst_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-           26       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int addTwo(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String addTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_1
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-           11       4     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddThree(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String nativeAddThree
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iconst_3
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       4     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String unsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String unsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-        x: ldc           #x                 // String unsupportedMethod
-        x: ldc           #x                 // String ()Ljava/lang/String;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.String visibleButUsesUnsupportedMethod();
-    descriptor: ()Ljava/lang/String;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
-         x: ldc           #x                 // String visibleButUsesUnsupportedMethod
-         x: ldc           #x                 // String ()Ljava/lang/String;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method unsupportedMethod:()Ljava/lang/String;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkForTextPolicy.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 6
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas$Nested();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String getSupplier
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String getSupplier_static
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String lambda$getSupplier_static$3
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: bipush        8
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String lambda$getSupplier$2
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: bipush        7
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String lambda$static$1
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: bipush        6
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String lambda$new$0
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_5
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putstatic     #x                // Field sSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;            // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-BootstrapMethods:
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$new$0:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier$2:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$getSupplier_static$3:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested.lambda$static$1:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.class
-  Compiled from "TinyFrameworkLambdas.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 8, attributes: 6
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkLambdas();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      14     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String getSupplier
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String getSupplier_static
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestStub
-
-  private static java.lang.Integer lambda$getSupplier_static$3();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String lambda$getSupplier_static$3
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_4
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$getSupplier$2();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String lambda$getSupplier$2
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_3
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$static$1();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String lambda$static$1
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_2
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static java.lang.Integer lambda$new$0();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String lambda$new$0
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iconst_1
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: invokedynamic #x,  0             // InvokeDynamic #x:get:()Ljava/util/function/Supplier;
-        x: putstatic     #x                // Field sSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested of class com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
-  public static final #x= #x of #x;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
-SourceFile: "TinyFrameworkLambdas.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestStub
-  x: #x()
-    android.hosttest.annotation.HostSideTestStaticInitializerKeep
-BootstrapMethods:
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$new$0:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier$2:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$getSupplier_static$3:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-  x: #x REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
-    Method arguments:
-      #x ()Ljava/lang/Object;
-      #x REF_invokeStatic com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.lambda$static$1:()Ljava/lang/Integer;
-      #x ()Ljava/lang/Integer;
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.class
-  Compiled from "TinyFrameworkNative.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 12, attributes: 3
-  int value;
-    descriptor: I
-    flags: (0x0000)
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeAddTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-        x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeAddTwo_should_be_like_this
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeAddTwo:(I)I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeLongPlus
-         x: ldc           #x                 // String (JJ)J
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: lload_0
-        x: lload_2
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-        x: lreturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus_should_be_like_this(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeLongPlus_should_be_like_this
-         x: ldc           #x                 // String (JJ)J
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: lload_0
-        x: lload_2
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeLongPlus:(JJ)J
-        x: lreturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  arg1   J
-           11       6     2  arg2   J
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public void setValue(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String setValue
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-           11       6     1     v   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeNonStaticAddToValue
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-        x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int nativeNonStaticAddToValue_should_be_like_this(int);
-    descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeNonStaticAddToValue_should_be_like_this
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokestatic  #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeNonStaticAddToValue:(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-           11       6     1   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static void nativeStillNotSupported();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                 // String nativeStillNotSupported
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-        x: ldc           #x                 // String nativeStillNotSupported
-        x: ldc           #x                 // String ()V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onThrowMethodCalled:()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String Unreachable
-        x: invokespecial #x                // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsThrow
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    RuntimeInvisibleAnnotations:
-      x: #x()
-        android.hosttest.annotation.HostSideTestThrow
-
-  public static void nativeStillNotSupported_should_be_like_this();
-    descriptor: ()V
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                // String nativeStillNotSupported_should_be_like_this
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: invokespecial #x                // Method java/lang/RuntimeException."<init>":()V
-        x: athrow
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
-         x: ldc           #x                // String nativeBytePlus
-         x: ldc           #x                // String (BB)B
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: iload_0
-        x: iload_1
-        x: invokestatic  #x                // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.nativeBytePlus:(BB)B
-        x: ireturn
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenProcessedAsSubstitute
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-  x: #x(#x=s#x)
-    android.hosttest.annotation.HostSideTestNativeSubstitutionClass(
-      value="TinyFrameworkNative_host"
-    )
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host.class
-  Compiled from "TinyFrameworkNative_host.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 6, attributes: 3
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-        x: ldc           #x                 // String <init>
-        x: ldc           #x                 // String ()V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeAddTwo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeAddTwo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-        x: ldc           #x                 // String nativeAddTwo
-        x: ldc           #x                 // String (I)I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_0
-        x: iconst_2
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       4     0   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static long nativeLongPlus(long, long);
-    descriptor: (JJ)J
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=4, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeLongPlus
-         x: ldc           #x                 // String (JJ)J
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-        x: ldc           #x                 // String nativeLongPlus
-        x: ldc           #x                 // String (JJ)J
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: lload_0
-        x: lload_2
-        x: ladd
-        x: lreturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       4     0  arg1   J
-           26       4     2  arg2   J
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int nativeNonStaticAddToValue(com.android.hoststubgen.test.tinyframework.TinyFrameworkNative, int);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeNonStaticAddToValue
-         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-        x: ldc           #x                 // String nativeNonStaticAddToValue
-        x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;I)I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: getfield      #x                 // Field com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.value:I
-        x: iload_1
-        x: iadd
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       7     0 source   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNative;
-           26       7     1   arg   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static byte nativeBytePlus(byte, byte);
-    descriptor: (BB)B
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-         x: ldc           #x                 // String nativeBytePlus
-         x: ldc           #x                 // String (BB)B
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
-        x: ldc           #x                 // String nativeBytePlus
-        x: ldc           #x                 // String (BB)B
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iload_0
-        x: iload_1
-        x: iadd
-        x: i2b
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  arg1   B
-           26       5     1  arg2   B
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkNative_host.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassKeep
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 1, methods: 4, attributes: 6
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0000)
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: aload_1
-        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-           11      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Integer;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_1
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Object;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 4, attributes: 6
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Integer;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_2
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Object;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-EnclosingMethod: #x.#x                 // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 1, methods: 4, attributes: 6
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0000)
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: aload_1
-        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-           11      10     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Integer;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_3
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Object;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 4, attributes: 6
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Integer;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: iconst_4
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Object;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.getSupplier_static
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 2, attributes: 4
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iload_1
-        x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass;
-           11      10     1     x   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 2, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
-    descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$InnerClass(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
-    descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: aload_1
-        x: putfield      #x                 // Field this$0:Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iconst_5
-        x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      15     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass;
-           11      15     1 this$0   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-    MethodParameters:
-      Name                           Flags
-      <no name>                      final mandated
-}
-InnerClasses:
-  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1 extends java.lang.Object implements java.util.function.Supplier<java.lang.Integer>
-  minor version: 0
-  major version: 61
-  flags: (0x0020) ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 4, attributes: 6
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
-    descriptor: ()V
-    flags: (0x0000)
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Integer get();
-    descriptor: ()Ljava/lang/Integer;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Integer;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Integer;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: bipush        7
-        x: invokestatic  #x                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.lang.Object get();
-    descriptor: ()Ljava/lang/Object;
-    flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-         x: ldc           #x                 // String get
-         x: ldc           #x                 // String ()Ljava/lang/Object;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-        x: ldc           #x                 // String get
-        x: ldc           #x                 // String ()Ljava/lang/Object;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokevirtual #x                 // Method get:()Ljava/lang/Integer;
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;          // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                     // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-EnclosingMethod: #x.#x                // com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass.getSupplier_static
-Signature: #x                           // Ljava/lang/Object;Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 5
-  public int value;
-    descriptor: I
-    flags: (0x0001) ACC_PUBLIC
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: bipush        6
-        x: putfield      #x                 // Field value:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      11     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-         x: ldc           #x                 // String getSupplier_static
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-        x: dup
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1."<init>":()V
-        x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass extends com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$BaseClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  interfaces: 0, fields: 0, methods: 2, attributes: 4
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: iload_1
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass."<init>":(I)V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       6     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass;
-           11       6     1     x   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-NestHost: class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.class
-  Compiled from "TinyFrameworkNestedClasses.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 2, methods: 4, attributes: 5
-  public final java.util.function.Supplier<java.lang.Integer> mSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static final java.util.function.Supplier<java.lang.Integer> sSupplier;
-    descriptor: Ljava/util/function/Supplier;
-    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
-    Signature: #x                          // Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-        x: dup
-        x: aload_0
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-        x: putfield      #x                 // Field mSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      17     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public java.util.function.Supplier<java.lang.Integer> getSupplier();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-         x: ldc           #x                 // String getSupplier
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-        x: dup
-        x: aload_0
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3."<init>":(Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
-        x: areturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       9     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static java.util.function.Supplier<java.lang.Integer> getSupplier_static();
-    descriptor: ()Ljava/util/function/Supplier;
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-         x: ldc           #x                 // String getSupplier_static
-         x: ldc           #x                 // String ()Ljava/util/function/Supplier;
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-        x: dup
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4."<init>":()V
-        x: areturn
-      LineNumberTable:
-    Signature: #x                          // ()Ljava/util/function/Supplier<Ljava/lang/Integer;>;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  static {};
-    descriptor: ()V
-    flags: (0x0008) ACC_STATIC
-    Code:
-      stack=4, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-         x: ldc           #x                 // String <clinit>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-        x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-        x: new           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-        x: dup
-        x: invokespecial #x                 // Method com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2."<init>":()V
-        x: putstatic     #x                 // Field sSupplier:Ljava/util/function/Supplier;
-        x: return
-      LineNumberTable:
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-InnerClasses:
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  public static #x= #x of #x;           // SubClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // BaseClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public static #x= #x of #x;           // StaticNestedClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  public #x= #x of #x;                  // InnerClass=class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass of class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
-  #x;                                    // class com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-SourceFile: "TinyFrameworkNestedClasses.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-NestMembers:
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
-  com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.class
-  Compiled from "TinyFrameworkPackageRedirect.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
-         x: ldc           #x                 // String foo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class com/supported/UnsupportedClass
-        x: dup
-        x: iload_0
-        x: invokespecial #x                 // Method com/supported/UnsupportedClass."<init>":(I)V
-        x: invokevirtual #x                 // Method com/supported/UnsupportedClass.getValue:()I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      12     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkPackageRedirect.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.class
-  Compiled from "TinyFrameworkRenamedClassCaller.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
-    descriptor: ()V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String ()V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public static int foo(int);
-    descriptor: (I)I
-    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
-         x: ldc           #x                 // String foo
-         x: ldc           #x                 // String (I)I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-        x: dup
-        x: iload_0
-        x: invokespecial #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed."<init>":(I)V
-        x: invokevirtual #x                 // Method rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.getValue:()I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      12     0 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkRenamedClassCaller.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/A
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/packagetest/sub/A.class
-  Compiled from "A.java"
-public class com.android.hoststubgen.test.tinyframework.packagetest.sub.A
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/packagetest/sub/A
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "A.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
-  Compiled from "C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
-  Compiled from "C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
-  Compiled from "C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
-  Compiled from "CA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "CA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
-  Compiled from "CB.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "CB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
-  Compiled from "Class_C1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
-         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_C1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
-  Compiled from "Class_C2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
-         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_C2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
-  Compiled from "Class_C3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
-  super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
-         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_C3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
-  Compiled from "Class_I1.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
-  Compiled from "Class_I1_IA.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 2, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_I1_IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
-  Compiled from "Class_I2.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
-  Compiled from "Class_I3.java"
-public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "Class_I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
-  Compiled from "I1.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "I1.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
-  Compiled from "I2.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I2
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "I2.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
-  Compiled from "I3.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
-  super_class: #x                         // java/lang/Object
-  interfaces: 1, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/I3
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "I3.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
-  Compiled from "IA.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "IA.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
-  Compiled from "IB.java"
-public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
-  minor version: 0
-  major version: 61
-  flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
-  this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 1, attributes: 2
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-}
-SourceFile: "IB.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-## Class: com/supported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.supported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/supported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/supported/UnsupportedClass
-         x: ldc           #x                  // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.supported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/supported/UnsupportedClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/supported/UnsupportedClass
-        x: ldc           #x                 // String <init>
-        x: ldc           #x                 // String (I)V
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iload_1
-        x: putfield      #x                 // Field mValue:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26      10     0  this   Lcom/supported/UnsupportedClass;
-           26      10     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/supported/UnsupportedClass
-         x: ldc           #x                 // String getValue
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: ldc           #x                 // String com/supported/UnsupportedClass
-        x: ldc           #x                 // String getValue
-        x: ldc           #x                 // String ()I
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.getStackWalker:()Ljava/lang/StackWalker;
-        x: invokevirtual #x                 // Method java/lang/StackWalker.getCallerClass:()Ljava/lang/Class;
-        x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onNonStubMethodCalled:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;)V
-        x: aload_0
-        x: getfield      #x                 // Field mValue:I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           26       5     0  this   Lcom/supported/UnsupportedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassKeep
-## Class: com/unsupported/UnsupportedClass.class
-  Compiled from "UnsupportedClass.java"
-public class com.unsupported.UnsupportedClass
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // com/unsupported/UnsupportedClass
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 0, methods: 3, attributes: 3
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                  // class com/unsupported/UnsupportedClass
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public com.unsupported.UnsupportedClass(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                  // class com/unsupported/UnsupportedClass
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String This class is not supported
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      14     0  this   Lcom/unsupported/UnsupportedClass;
-           11      14     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                  // class com/unsupported/UnsupportedClass
-         x: ldc           #x                 // String getValue
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: new           #x                 // class java/lang/RuntimeException
-        x: dup
-        x: ldc           #x                 // String This class is not supported
-        x: invokespecial #x                 // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
-        x: athrow
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/unsupported/UnsupportedClass;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "UnsupportedClass.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
-## Class: rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.class
-  Compiled from "TinyFrameworkToBeRenamed.java"
-public class rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed
-  minor version: 0
-  major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
-  this_class: #x                          // rename_prefix/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-  super_class: #x                         // java/lang/Object
-  interfaces: 0, fields: 1, methods: 3, attributes: 3
-  private final int mValue;
-    descriptor: I
-    flags: (0x0012) ACC_PRIVATE, ACC_FINAL
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  private static {};
-    descriptor: ()V
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
-    Code:
-      stack=2, locals=0, args_size=0
-         x: ldc           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
-         x: return
-
-  public rename_prefix.com.android.hoststubgen.test.tinyframework.TinyFrameworkToBeRenamed(int);
-    descriptor: (I)V
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=2, args_size=2
-         x: ldc           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: ldc           #x                 // String <init>
-         x: ldc           #x                 // String (I)V
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: invokespecial #x                 // Method java/lang/Object."<init>":()V
-        x: aload_0
-        x: iload_1
-        x: putfield      #x                 // Field mValue:I
-        x: return
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11      10     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
-           11      10     1 value   I
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-
-  public int getValue();
-    descriptor: ()I
-    flags: (0x0001) ACC_PUBLIC
-    Code:
-      stack=4, locals=1, args_size=1
-         x: ldc           #x                 // class com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
-         x: ldc           #x                 // String getValue
-         x: ldc           #x                 // String ()I
-         x: ldc           #x                 // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
-         x: invokestatic  #x                 // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-        x: aload_0
-        x: getfield      #x                 // Field mValue:I
-        x: ireturn
-      LineNumberTable:
-      LocalVariableTable:
-        Start  Length  Slot  Name   Signature
-           11       5     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed;
-    RuntimeVisibleAnnotations:
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-      x: #x()
-        com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-}
-SourceFile: "TinyFrameworkToBeRenamed.java"
-RuntimeVisibleAnnotations:
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
-  x: #x()
-    com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
-RuntimeInvisibleAnnotations:
-  x: #x()
-    android.hosttest.annotation.HostSideTestWholeClassStub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 75c9721..3c138d2 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -1,9 +1,8 @@
-class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy	stub
-  field stub	stub
-  field keep	keep
+class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy	keep
+  field stub	keep
   # field remove	remove # Implicitly remove
-  method <init>	()V	            stub
-  method addOne	(I)I	        stub
+  method <init>	()V	            keep
+  method addOne	(I)I	        keep
   method addOneInner	(I)I	keep
   method toBeRemoved	(Ljava/lang/String;)V	remove
   method addTwo	(I)I	        @addTwo_host
@@ -11,28 +10,38 @@
   method nativeAddThree	(I)I	@addThree_host
   # method addThree_host	(I)I	# used as a substitute
   method unsupportedMethod	()Ljava/lang/String;	throw
-  method visibleButUsesUnsupportedMethod	()Ljava/lang/String;	stub
+  method visibleButUsesUnsupportedMethod	()Ljava/lang/String;	keep
+  method toBeIgnoredObj	()Ljava/lang/String;	ignore
+  method toBeIgnoredV	()V	ignore
+  method toBeIgnoredZ	()Z	ignore
+  method toBeIgnoredB	()B	ignore
+  method toBeIgnoredC	()C	ignore
+  method toBeIgnoredS	()S	ignore
+  method toBeIgnoredI	()I	ignore
+  method toBeIgnoredL	()L	ignore
+  method toBeIgnoredF	()F	ignore
+  method toBeIgnoredD	()D	ignore
 
 # Class load hook
 class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy	~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
 
 # Heuristics rule: Stub all the AIDL classes.
-class :aidl stubclass
+class :aidl keepclass
 
 # Heuristics rule: Stub all the R classes.
-class :r stubclass
+class :r keepclass
 
 # Default is "remove", so let's put all the base classes / interfaces in the stub first.
-class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.C3 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.CA stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.CB stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I1 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I2 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.I3 stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.IA stub
-class com.android.hoststubgen.test.tinyframework.subclasstest.IB stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.C2 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.C3 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.CA keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.CB keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I2 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.I3 keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.IA keep
+class com.android.hoststubgen.test.tinyframework.subclasstest.IB keep
 
 # Then define inheritance based policies.
 class *com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
@@ -42,15 +51,25 @@
 class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
 
 # Test package directive
-package com.android.hoststubgen.test.tinyframework.packagetest stub
+package com.android.hoststubgen.test.tinyframework.packagetest keep
 class com.android.hoststubgen.test.tinyframework.packagetest.B remove
 class com.android.hoststubgen.test.tinyframework.packagetest.sub.B remove
 # The following rules are the same as above
-# class com.android.hoststubgen.test.tinyframework.packagetest.A stub
-# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A stub
+# class com.android.hoststubgen.test.tinyframework.packagetest.A keep
+# class com.android.hoststubgen.test.tinyframework.packagetest.sub.A keep
+
+# Used to test method call replacement.
+class com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace keepclass
+  method originalAdd (II)I @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.add
+
+# Used to test method call replacement.
+# Note because java.lang.Thread is _not_ within the tiny-framework jar, the policy ("keep")
+# doesn't realy matter.
+class java.lang.Thread keep
+  method start ()V @com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo.startThread
 
 
 # "rename" takes a type internal name, so '/'s is used as a separator.
 # The leading / in the prefix is not needed (it'll be stripped), but it's added to make
 # sure the stripping works.
-rename ^.*/TinyFrameworkToBeRenamed$ /rename_prefix/
\ No newline at end of file
+rename ^.*/TinyFrameworkToBeRenamed$ /rename_prefix/
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
index 872bbf8..80ebf3a 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/run-test-manually.sh
@@ -43,8 +43,7 @@
 
 tiny_framework_classes=$out/tiny-framework/classes/
 tiny_framework_jar=$out/tiny-framework.jar
-tiny_framework_host_stub_jar=$out/tiny-framework_host_stub.jar
-tiny_framework_host_impl_jar=$out/tiny-framework_host_impl.jar
+tiny_framework_host_jar=$out/tiny-framework_host.jar
 
 tiny_test_classes=$out/tiny-test/classes/
 tiny_test_jar=$out/tiny-test.jar
@@ -87,8 +86,7 @@
 run $HOSTSTUBGEN \
     @../hoststubgen-standard-options.txt \
     --in-jar $tiny_framework_jar \
-    --out-stub-jar $tiny_framework_host_stub_jar \
-    --out-impl-jar $tiny_framework_host_impl_jar \
+    --out-jar $tiny_framework_host_jar \
     --policy-override-file policy-override-tiny-framework.txt \
     --gen-keep-all-file out/tiny-framework_keep_all.txt \
     --gen-input-dump-file out/tiny-framework_dump.txt \
@@ -97,14 +95,14 @@
     $HOSTSTUBGEN_OPTS
 
 # Extract the jar files, so we can look into them.
-extract $tiny_framework_host_stub_jar $tiny_framework_host_impl_jar
+extract $tiny_framework_host_jar
 
 # Build the test
 echo "# Building tiny-test..."
 run $JAVAC \
     -cp $( \
         join : \
-        $tiny_framework_host_stub_jar \
+        $tiny_framework_jar \
         "${test_compile_classpaths[@]}" \
         ) \
     -d $tiny_test_classes \
@@ -124,7 +122,7 @@
     -cp $( \
         join : \
         $tiny_test_jar \
-        $tiny_framework_host_impl_jar \
+        $tiny_framework_host_jar \
         "${test_compile_classpaths[@]}" \
         "${test_runtime_classpaths[@]}" \
         ) \
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
new file mode 100644
index 0000000..ed0fa26
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestKeep;
+import android.hosttest.annotation.HostSideTestRemove;
+import android.hosttest.annotation.HostSideTestSubstitute;
+import android.hosttest.annotation.HostSideTestThrow;
+
+/**
+ * Test without class-wide annotations.
+ */
+@HostSideTestKeep
+@HostSideTestClassLoadHook(
+        "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
+public class TinyFrameworkAnnotations {
+    @HostSideTestKeep
+    public TinyFrameworkAnnotations() {
+    }
+
+    @HostSideTestKeep
+    public int keep = 1;
+
+    // Members will be deleted by default.
+    // Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at
+    // runtime.
+    public int remove;
+
+    @HostSideTestKeep
+    public int addOne(int value) {
+        return value + 1;
+    }
+
+    @HostSideTestRemove // Explicitly remove
+    public void toBeRemoved(String foo) {
+        throw new RuntimeException();
+    }
+
+    @HostSideTestSubstitute(suffix = "_host")
+    public int addTwo(int value) {
+        throw new RuntimeException("not supported on host side");
+    }
+
+    public int addTwo_host(int value) {
+        return value + 2;
+    }
+
+    @HostSideTestSubstitute(suffix = "_host")
+    public static native int nativeAddThree(int value);
+
+    // This method is private, but at runtime, it'll inherit the visibility of the original method
+    private static int nativeAddThree_host(int value) {
+        return value + 3;
+    }
+
+    @HostSideTestThrow
+    public String unsupportedMethod() {
+        return "This value shouldn't be seen on the host side.";
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
deleted file mode 100644
index f530207..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck.java
+++ /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.hoststubgen.test.tinyframework;
-
-import android.hosttest.annotation.HostSideTestKeep;
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
-
-/**
- * Used by the benchmark.
- */
-@HostSideTestWholeClassStub
-public class TinyFrameworkCallerCheck {
-
-    /**
-     * This method uses an inner method (which has the caller check).
-     *
-     * Benchmark result: 768ns
-     */
-    public static int getOne_withCheck() {
-        return Impl.getOneKeep();
-    }
-
-    /**
-     * This method doesn't have any caller check.
-     *
-     * Benchmark result: 2ns
-     */
-    public static int getOne_noCheck() {
-        return Impl.getOneStub();
-    }
-
-    private static class Impl {
-        @HostSideTestKeep
-        public static int getOneKeep() {
-            return 1;
-        }
-
-        @HostSideTestStub
-        public static int getOneStub() {
-            return 1;
-        }
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
deleted file mode 100644
index 6d8a48a..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassAnnotations.java
+++ /dev/null
@@ -1,90 +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.test.tinyframework;
-
-import android.hosttest.annotation.HostSideTestClassLoadHook;
-import android.hosttest.annotation.HostSideTestKeep;
-import android.hosttest.annotation.HostSideTestRemove;
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestSubstitute;
-import android.hosttest.annotation.HostSideTestThrow;
-
-/**
- * Test without class-wide annotations.
- */
-@HostSideTestStub
-@HostSideTestClassLoadHook(
-        "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-public class TinyFrameworkClassAnnotations {
-    @HostSideTestStub
-    public TinyFrameworkClassAnnotations() {
-    }
-
-    @HostSideTestStub
-    public int stub = 1;
-
-    @HostSideTestKeep
-    public int keep = 2;
-
-    // Members will be deleted by default.
-    // Deleted fields cannot have an initial value, because otherwise .ctor will fail to set it at
-    // runtime.
-    public int remove;
-
-    @HostSideTestStub
-    public int addOne(int value) {
-        return addOneInner(value);
-    }
-
-    @HostSideTestKeep
-    public int addOneInner(int value) {
-        return value + 1;
-    }
-
-    @HostSideTestRemove // Explicitly remove
-    public void toBeRemoved(String foo) {
-        throw new RuntimeException();
-    }
-
-    @HostSideTestStub
-    @HostSideTestSubstitute(suffix = "_host")
-    public int addTwo(int value) {
-        throw new RuntimeException("not supported on host side");
-    }
-
-    public int addTwo_host(int value) {
-        return value + 2;
-    }
-
-    @HostSideTestStub
-    @HostSideTestSubstitute(suffix = "_host")
-    public static native int nativeAddThree(int value);
-
-    // This method is private, but at runtime, it'll inherit the visibility of the original method
-    private static int nativeAddThree_host(int value) {
-        return value + 3;
-    }
-
-    @HostSideTestThrow
-    public String unsupportedMethod() {
-        return "This value shouldn't be seen on the host side.";
-    }
-
-    @HostSideTestStub
-    public String visibleButUsesUnsupportedMethod() {
-        return unsupportedMethod();
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
deleted file mode 100644
index 145b65a..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassClassWideAnnotations.java
+++ /dev/null
@@ -1,74 +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.test.tinyframework;
-
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestSubstitute;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
-
-@HostSideTestWholeClassStub
-public class TinyFrameworkClassClassWideAnnotations {
-    public TinyFrameworkClassClassWideAnnotations() {
-    }
-
-    public int stub = 1;
-
-    public int keep = 2;
-
-    // Cannot have an initial value, because otherwise .ctor will fail to set it at runtime.
-    public int remove;
-
-    // @Stub
-    public int addOne(int value) {
-        return addOneInner(value);
-    }
-
-    // @Keep
-    public int addOneInner(int value) {
-        return value + 1;
-    }
-
-    // @Remove
-    public void toBeRemoved(String foo) {
-        throw new RuntimeException();
-    }
-
-    @HostSideTestStub
-    @HostSideTestSubstitute(suffix = "_host")
-    public int addTwo(int value) {
-        throw new RuntimeException("not supported on host side");
-    }
-
-    public int addTwo_host(int value) {
-        return value + 2;
-    }
-
-    @HostSideTestStub
-    @HostSideTestSubstitute(suffix = "_host")
-    public static native int nativeAddThree(int value);
-
-    public static int nativeAddThree_host(int value) {
-        return value + 3;
-    }
-
-    public String unsupportedMethod() {
-        return "This value shouldn't be seen on the host side.";
-    }
-
-    public String visibleButUsesUnsupportedMethod() {
-        return unsupportedMethod();
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
index 98fc634..f734790 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook.java
@@ -15,12 +15,12 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
 import java.util.HashSet;
 import java.util.Set;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkClassLoadHook {
     private TinyFrameworkClassLoadHook() {
     }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
new file mode 100644
index 0000000..e83163e
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations.java
@@ -0,0 +1,55 @@
+/*
+ * 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.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestRemove;
+import android.hosttest.annotation.HostSideTestSubstitute;
+import android.hosttest.annotation.HostSideTestThrow;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
+
+@HostSideTestWholeClassKeep
+public class TinyFrameworkClassWideAnnotations {
+    public TinyFrameworkClassWideAnnotations() {
+    }
+
+    public int keep = 1;
+
+    @HostSideTestRemove
+    public int remove;
+
+    public int addOne(int value) {
+        return value + 1;
+    }
+
+    @HostSideTestSubstitute(suffix = "_host")
+    public int addTwo(int value) {
+        throw new RuntimeException("not supported on host side");
+    }
+
+    public int addTwo_host(int value) {
+        return value + 2;
+    }
+
+    @HostSideTestRemove
+    public void toBeRemoved(String foo) {
+        throw new RuntimeException();
+    }
+
+    @HostSideTestThrow
+    public String unsupportedMethod() {
+        return "This value shouldn't be seen on the host side.";
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
index 8324ed9..3df21d9 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault.java
@@ -15,18 +15,16 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestClassLoadHook;
-import android.hosttest.annotation.HostSideTestStub;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestKeep;
 
-@HostSideTestStub
+@HostSideTestKeep
 public class TinyFrameworkClassWithInitializerDefault {
     static {
         sInitialized = true;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public static boolean sInitialized;
-    @HostSideTestStub
+    @HostSideTestKeep
     public static Object sObject = new Object();
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
index ea1ad93..cc665de 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub.java
@@ -16,20 +16,20 @@
 package com.android.hoststubgen.test.tinyframework;
 
 import android.hosttest.annotation.HostSideTestClassLoadHook;
+import android.hosttest.annotation.HostSideTestKeep;
 import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
-import android.hosttest.annotation.HostSideTestStub;
 
 @HostSideTestClassLoadHook(
         "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-@HostSideTestStub
+@HostSideTestKeep
 @HostSideTestStaticInitializerKeep
 public class TinyFrameworkClassWithInitializerStub {
     static {
         sInitialized = true;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public static boolean sInitialized;
-    @HostSideTestStub
+    @HostSideTestKeep
     public static Object sObject = new Object();
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
index 51f4818..f833ad8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex.java
@@ -16,15 +16,14 @@
 package com.android.hoststubgen.test.tinyframework;
 
 import android.hosttest.annotation.HostSideTestKeep;
-import android.hosttest.annotation.HostSideTestStub;
 
-@HostSideTestStub
+@HostSideTestKeep
 public enum TinyFrameworkEnumComplex {
-    @HostSideTestStub
+    @HostSideTestKeep
     RED("Red", "R"),
-    @HostSideTestStub
+    @HostSideTestKeep
     GREEN("Green", "G"),
-    @HostSideTestStub
+    @HostSideTestKeep
     BLUE("Blue", "B");
 
     @HostSideTestKeep
@@ -33,18 +32,18 @@
     @HostSideTestKeep
     private final String mShortName;
 
-    @HostSideTestStub
+    @HostSideTestKeep
     TinyFrameworkEnumComplex(String longName, String shortName) {
         mLongName = longName;
         mShortName = shortName;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public String getLongName() {
         return mLongName;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public String getShortName() {
         return mShortName;
     }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
index f440d86..c023169 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple.java
@@ -15,12 +15,12 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestStub;
+import android.hosttest.annotation.HostSideTestKeep;
 
-@HostSideTestStub
+@HostSideTestKeep
 public enum TinyFrameworkEnumSimple {
-    @HostSideTestStub
+    @HostSideTestKeep
     CAT,
-    @HostSideTestStub
+    @HostSideTestKeep
     DOG,
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
index 909d3b4..f7cae7d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester.java
@@ -15,9 +15,9 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkExceptionTester {
     public static int testException() {
         try {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
index bde7c35..ec1efba 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy.java
@@ -24,17 +24,11 @@
 
     public int stub = 1;
 
-    public int keep = 2;
-
     // Removed fields cannot have an initial value, because otherwise .ctor will fail to set it at
     // runtime.
     public int remove;
 
     public int addOne(int value) {
-        return addOneInner(value);
-    }
-
-    public int addOneInner(int value) {
         return value + 1;
     }
 
@@ -42,6 +36,42 @@
         throw new RuntimeException();
     }
 
+    public String toBeIgnoredObj() {
+        throw new RuntimeException();
+    }
+
+    public void toBeIgnoredV() {
+        throw new RuntimeException();
+    }
+
+    public boolean toBeIgnoredZ() {
+        throw new RuntimeException();
+    }
+
+    public byte toBeIgnoredB() {
+        throw new RuntimeException();
+    }
+
+    public char toBeIgnoredC() {
+        throw new RuntimeException();
+    }
+
+    public short toBeIgnoredS() {
+        throw new RuntimeException();
+    }
+
+    public int toBeIgnoredI() {
+        throw new RuntimeException();
+    }
+
+    public float toBeIgnoredF() {
+        throw new RuntimeException();
+    }
+
+    public double toBeIgnoredD() {
+        throw new RuntimeException();
+    }
+
     public int addTwo(int value) {
         throw new RuntimeException("not supported on host side");
     }
@@ -59,8 +89,4 @@
     public String unsupportedMethod() {
         return "This value shouldn't be seen on the host side.";
     }
-
-    public String visibleButUsesUnsupportedMethod() {
-        return unsupportedMethod();
-    }
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
index 0d1203b..1ca653e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas.java
@@ -15,8 +15,8 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
+import android.hosttest.annotation.HostSideTestKeep;
 import android.hosttest.annotation.HostSideTestStaticInitializerKeep;
-import android.hosttest.annotation.HostSideTestStub;
 
 import java.util.function.Supplier;
 
@@ -28,48 +28,48 @@
  *
  * Implicit filter should take care of them.
  */
-@HostSideTestStub
+@HostSideTestKeep
 @HostSideTestStaticInitializerKeep
 public class TinyFrameworkLambdas {
-    @HostSideTestStub
+    @HostSideTestKeep
     public TinyFrameworkLambdas() {
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public final Supplier<Integer> mSupplier = () -> 1;
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public static final Supplier<Integer> sSupplier = () -> 2;
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public Supplier<Integer> getSupplier() {
         return () -> 3;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     public static Supplier<Integer> getSupplier_static() {
         return () -> 4;
     }
 
-    @HostSideTestStub
+    @HostSideTestKeep
     @HostSideTestStaticInitializerKeep
     public static class Nested {
-        @HostSideTestStub
+        @HostSideTestKeep
         public Nested() {
         }
 
-        @HostSideTestStub
+        @HostSideTestKeep
         public final Supplier<Integer> mSupplier = () -> 5;
 
-        @HostSideTestStub
+        @HostSideTestKeep
         public static final Supplier<Integer> sSupplier = () -> 6;
 
-        @HostSideTestStub
+        @HostSideTestKeep
         public Supplier<Integer> getSupplier() {
             return () -> 7;
         }
 
-        @HostSideTestStub
+        @HostSideTestKeep
         public static Supplier<Integer> getSupplier_static() {
             return () -> 8;
         }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
new file mode 100644
index 0000000..57c69a3
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace.java
@@ -0,0 +1,58 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@HostSideTestWholeClassKeep
+public class TinyFrameworkMethodCallReplace {
+    //  This method should return true.
+    public static boolean nonStaticMethodCallReplaceTester() throws Exception {
+        final AtomicBoolean ab = new AtomicBoolean(false);
+
+        Thread th = new Thread(() -> {
+            ab.set(Thread.currentThread().isDaemon());
+        });
+        // This Thread.start() call will be redirected to ReplaceTo.startThread()
+        // (because of the policy file directive) which will make the thread "daemon" and start it.
+        th.start();
+        th.join();
+
+        return ab.get(); // This should be true.
+    }
+
+    public static int staticMethodCallReplaceTester() {
+        // This method call will be replaced with ReplaceTo.add().
+        return originalAdd(1, 2);
+    }
+
+    private static int originalAdd(int a, int b) {
+        return a + b - 1; // Original is broken.
+    }
+
+    public static class ReplaceTo {
+        public static void startThread(Thread thread) {
+            thread.setDaemon(true);
+            thread.start();
+        }
+
+        public static int add(int a, int b) {
+            return a + b;
+        }
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
index 09ee183..73b5e2f 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNative.java
@@ -15,11 +15,12 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
+import android.hosttest.annotation.HostSideTestKeep;
 import android.hosttest.annotation.HostSideTestNativeSubstitutionClass;
 import android.hosttest.annotation.HostSideTestThrow;
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 @HostSideTestNativeSubstitutionClass("TinyFrameworkNative_host")
 public class TinyFrameworkNative {
     public static native int nativeAddTwo(int arg);
@@ -49,6 +50,9 @@
     @HostSideTestThrow
     public static native void nativeStillNotSupported();
 
+    @HostSideTestKeep
+    public static native void nativeStillKeep();
+
     public static void nativeStillNotSupported_should_be_like_this() {
         throw new RuntimeException();
     }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
index e1c48bb..c1ea2ee 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses.java
@@ -15,11 +15,11 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
 import java.util.function.Supplier;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkNestedClasses {
     public final Supplier<Integer> mSupplier = new Supplier<Integer>() {
         @Override
@@ -34,6 +34,7 @@
             return 2;
         }
     };
+
     public Supplier<Integer> getSupplier() {
         return new Supplier<Integer>() {
             @Override
@@ -52,12 +53,10 @@
         };
     }
 
-    @HostSideTestWholeClassStub
     public class InnerClass {
         public int value = 5;
     }
 
-    @HostSideTestWholeClassStub
     public static class StaticNestedClass {
         public int value = 6;
 
@@ -70,6 +69,10 @@
                 }
             };
         }
+
+        public static class Double$NestedClass {
+            public int value = 8;
+        }
     }
 
     public static class BaseClass {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
index a82be54..941fcff 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect.java
@@ -15,9 +15,9 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkPackageRedirect {
     /**
      * A method that uses "unsupported" class. HostStubGen will redirect them to the "supported"
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
index 31a164a..707bc0e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller.java
@@ -15,9 +15,9 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkRenamedClassCaller {
     /** Calls the class that'll be renamed. */
     public static int foo(int value) {
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
index 1430bcb..8319ced 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed.java
@@ -15,12 +15,12 @@
  */
 package com.android.hoststubgen.test.tinyframework;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
 /**
  * This class will be renamed by the "rename" directive in the policy file.
  */
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class TinyFrameworkToBeRenamed {
     private final int mValue;
 
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
index 0409b02..92f41ac 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/unsupported/UnsupportedClass.java
@@ -15,10 +15,10 @@
  */
 package com.unsupported;
 
-import android.hosttest.annotation.HostSideTestWholeClassStub;
+import android.hosttest.annotation.HostSideTestWholeClassKeep;
 
 // Used for testing --package-redirect.
-@HostSideTestWholeClassStub
+@HostSideTestWholeClassKeep
 public class UnsupportedClass {
     public UnsupportedClass(int value) {
         throw new RuntimeException("This class is not supported");
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
deleted file mode 100644
index 76bbcad..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/LargeTest.java
+++ /dev/null
@@ -1,28 +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.test.tinyframework;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * We don't want to use any android classes in this module, so we create our own copy of it here.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface LargeTest {}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
new file mode 100644
index 0000000..1ae0493
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotationsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkAnnotationsTest {
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void testSimple() {
+        TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+        assertThat(tfc.addOne(1)).isEqualTo(2);
+        assertThat(tfc.keep).isEqualTo(1);
+    }
+
+    @Test
+    public void testRemove() {
+        TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+        assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+        assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+    }
+
+    @Test
+    public void testSubstitute() {
+        TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+        assertThat(tfc.addTwo(1)).isEqualTo(3);
+    }
+
+    @Test
+    public void testSubstituteNative() {
+        TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+        assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
+    }
+
+    @Test
+    public void testUnsupportedMethod() {
+        TinyFrameworkAnnotations tfc = new TinyFrameworkAnnotations();
+
+        thrown.expect(RuntimeException.class);
+        thrown.expectMessage("not yet supported");
+        tfc.unsupportedMethod();
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
deleted file mode 100644
index d57735b..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkBenchmark.java
+++ /dev/null
@@ -1,71 +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.test.tinyframework;
-
-import org.junit.Test;
-
-import java.text.DecimalFormat;
-
-/**
- * Contains simple micro-benchmarks.
- */
-@LargeTest
-public class TinyFrameworkBenchmark {
-    private static final int MINIMAL_ITERATION = 1000;
-    private static final int MEASURE_SECONDS = 1;
-
-    private static final DecimalFormat sFormatter = new DecimalFormat("#,###");
-
-    private void doBenchmark(String name, Runnable r) {
-        // Worm up
-        for (int i = 0; i < MINIMAL_ITERATION; i++) {
-            r.run();
-        }
-
-        // Start measuring.
-        final long start = System.nanoTime();
-        final long end = start + MEASURE_SECONDS * 1_000_000_000L;
-
-        double iteration = 0;
-        while (System.nanoTime() <= end) {
-            for (int i = 0; i < MINIMAL_ITERATION; i++) {
-                r.run();
-            }
-            iteration += MINIMAL_ITERATION;
-        }
-
-        final long realEnd = System.nanoTime();
-
-        System.out.println(String.format("%s\t%s", name,
-                sFormatter.format((((double) realEnd - start)) / iteration)));
-    }
-
-    /**
-     * Micro-benchmark for a method without a non-stub caller check.
-     */
-    @Test
-    public void benchNoCallerCheck() {
-        doBenchmark("No caller check", TinyFrameworkCallerCheck::getOne_noCheck);
-    }
-
-    /**
-     * Micro-benchmark for a method with a non-stub caller check.
-     */
-    @Test
-    public void benchWithCallerCheck() {
-        doBenchmark("With caller check", TinyFrameworkCallerCheck::getOne_withCheck);
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index bf0f654..14229a0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -17,10 +17,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
 
 import com.android.hoststubgen.test.tinyframework.R.Nested;
-import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -41,16 +40,26 @@
         assertThat(tfc.stub).isEqualTo(1);
     }
 
-//    @Test
-//    public void testDoesntCompile() {
-//        TinyFrameworkClass tfc = new TinyFrameworkClass();
-//
-//        tfc.addOneInner(1); // Shouldn't compile.
-//        tfc.toBeRemoved("abc"); // Shouldn't compile.
-//        tfc.unsupportedMethod(); // Shouldn't compile.
-//        int a = tfc.keep; // Shouldn't compile
-//        int b = tfc.remove; // Shouldn't compile
-//    }
+    @Test
+    public void testRemove() {
+        TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+        assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+        assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+    }
+
+    @Test
+    public void testIgnore() {
+        TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
+        tfc.toBeIgnoredV();
+        assertThat(tfc.toBeIgnoredZ()).isEqualTo(false);
+        assertThat(tfc.toBeIgnoredB()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredC()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredS()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredI()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredF()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredD()).isEqualTo(0);
+        assertThat(tfc.toBeIgnoredObj()).isEqualTo(null);
+    }
 
     @Test
     public void testSubstitute() {
@@ -65,48 +74,12 @@
     }
 
     @Test
-    public void testVisibleButUsesUnsupportedMethod() {
+    public void testUnsupportedMethod() {
         TinyFrameworkForTextPolicy tfc = new TinyFrameworkForTextPolicy();
 
         thrown.expect(RuntimeException.class);
         thrown.expectMessage("not yet supported");
-        tfc.visibleButUsesUnsupportedMethod();
-    }
-
-    @Test
-    public void testNestedClass1() {
-        assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1);
-    }
-
-    @Test
-    public void testNestedClass2() {
-        assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2);
-    }
-
-    @Test
-    public void testNestedClass3() {
-        assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3);
-    }
-
-    @Test
-    public void testNestedClass4() {
-        assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4);
-    }
-
-    @Test
-    public void testNestedClass5() {
-        assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5);
-    }
-
-    @Test
-    public void testNestedClass6() {
-        assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6);
-    }
-
-    @Test
-    public void testNestedClass7() {
-        assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get())
-                .isEqualTo(7);
+        tfc.unsupportedMethod();
     }
 
     @Test
@@ -172,28 +145,22 @@
     }
 
     @Test
-    public void testSubstituteNativeWithThrow() throws Exception {
-        // We can't use TinyFrameworkNative.nativeStillNotSupported() directly in this class,
-        // because @Throw implies @Keep (not @Stub), and we currently compile this test
-        // against the stub jar (so it won't contain @Throw methods).
-        //
-        // But the method exists at runtime, so we can use reflections to call it.
-        //
-        // In the real Ravenwood environment, we don't use HostStubGen's stub jar at all,
-        // so it's not a problem.
+    public void testSubstituteNativeWithThrow() {
+        thrown.expect(RuntimeException.class);
+        thrown.expectMessage("not yet supported");
 
-        final var clazz = TinyFrameworkNative.class;
-        final var method = clazz.getMethod("nativeStillNotSupported");
+        TinyFrameworkNative.nativeStillNotSupported();
+    }
 
-        try {
-            method.invoke(null);
+    @Test
+    public void testSubstituteNativeWithKeep() {
+        // We don't want to complicate the test by setting up JNI,
+        // so to test out whether the native method is preserved, we
+        // check whether calling it will throw UnsatisfiedLinkError,
+        // which would only happen on native methods.
+        thrown.expect(UnsatisfiedLinkError.class);
 
-            fail("java.lang.reflect.InvocationTargetException expected");
-
-        } catch (java.lang.reflect.InvocationTargetException e) {
-            var inner = e.getCause();
-            assertThat(inner.getMessage()).contains("not yet supported");
-        }
+        TinyFrameworkNative.nativeStillKeep();
     }
 
     @Test
@@ -202,12 +169,6 @@
         thrown.expectMessage("Outer exception");
 
         TinyFrameworkExceptionTester.testException();
-
-    }
-
-    @Test
-    public void testMethodCallBeforeSuperCall() {
-        assertThat(new SubClass(3).value).isEqualTo(3);
     }
 
     @Test
@@ -217,7 +178,7 @@
         // Having this line before assertThat() will ensure these class are already loaded.
         var classes = new Class[]{
                 TinyFrameworkClassWithInitializerStub.class,
-                TinyFrameworkClassAnnotations.class,
+                TinyFrameworkAnnotations.class,
                 TinyFrameworkForTextPolicy.class,
         };
 
@@ -339,4 +300,16 @@
     public void testTypeRename() {
         assertThat(TinyFrameworkRenamedClassCaller.foo(1)).isEqualTo(1);
     }
+
+    @Test
+    public void testMethodCallReplaceNonStatic() throws Exception {
+        assertThat(TinyFrameworkMethodCallReplace.nonStaticMethodCallReplaceTester())
+                .isEqualTo(true);
+    }
+
+    @Test
+    public void testMethodCallReplaceStatic() throws Exception {
+        assertThat(TinyFrameworkMethodCallReplace.staticMethodCallReplaceTester())
+                .isEqualTo(3);
+    }
 }
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
new file mode 100644
index 0000000..34c98e9
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotationsTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.hoststubgen.test.tinyframework;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class TinyFrameworkClassWideAnnotationsTest {
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void testSimple() {
+        var tfc = new TinyFrameworkClassWideAnnotations();
+        assertThat(tfc.addOne(1)).isEqualTo(2);
+        assertThat(tfc.keep).isEqualTo(1);
+    }
+
+    @Test
+    public void testRemove() {
+        var tfc = new TinyFrameworkClassWideAnnotations();
+        assertThrows(NoSuchMethodError.class, () -> tfc.toBeRemoved("abc"));
+        assertThrows(NoSuchFieldError.class, () -> tfc.remove = 1);
+    }
+
+    @Test
+    public void testSubstitute() {
+        var tfc = new TinyFrameworkClassWideAnnotations();
+        assertThat(tfc.addTwo(1)).isEqualTo(3);
+    }
+
+    @Test
+    public void testUnsupportedMethod() {
+        var tfc = new TinyFrameworkClassWideAnnotations();
+
+        thrown.expect(RuntimeException.class);
+        thrown.expectMessage("not yet supported");
+        tfc.unsupportedMethod();
+    }
+
+    @Test
+    public void testMethodCallBeforeSuperCall() {
+        assertThat(new TinyFrameworkNestedClasses.SubClass(3).value).isEqualTo(3);
+    }
+
+    @Test
+    public void testNestedClass1() {
+        assertThat(new TinyFrameworkNestedClasses().mSupplier.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void testNestedClass2() {
+        assertThat(TinyFrameworkNestedClasses.sSupplier.get()).isEqualTo(2);
+    }
+
+    @Test
+    public void testNestedClass3() {
+        assertThat(new TinyFrameworkNestedClasses().getSupplier().get()).isEqualTo(3);
+    }
+
+    @Test
+    public void testNestedClass4() {
+        assertThat(TinyFrameworkNestedClasses.getSupplier_static().get()).isEqualTo(4);
+    }
+
+    @Test
+    public void testNestedClass5() {
+        assertThat((new TinyFrameworkNestedClasses()).new InnerClass().value).isEqualTo(5);
+    }
+
+    @Test
+    public void testNestedClass6() {
+        assertThat(new TinyFrameworkNestedClasses.StaticNestedClass().value).isEqualTo(6);
+    }
+
+    @Test
+    public void testNestedClass7() {
+        assertThat(TinyFrameworkNestedClasses.StaticNestedClass.getSupplier_static().get())
+                .isEqualTo(7);
+    }
+
+    @Test
+    public void testNestedClass8() {
+        assertThat(new TinyFrameworkNestedClasses.StaticNestedClass.Double$NestedClass().value)
+                .isEqualTo(8);
+    }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
deleted file mode 100644
index 288c716..0000000
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithAnnotTest.java
+++ /dev/null
@@ -1,66 +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.test.tinyframework;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-public class TinyFrameworkClassWithAnnotTest {
-    @Rule
-    public ExpectedException thrown = ExpectedException.none();
-
-    @Test
-    public void testSimple() {
-        TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
-        assertThat(tfc.addOne(1)).isEqualTo(2);
-        assertThat(tfc.stub).isEqualTo(1);
-    }
-
-//    @Test
-//    public void testDoesntCompile() {
-//        TinyFrameworkClassWithAnnot tfc = new TinyFrameworkClassWithAnnot();
-//
-//        tfc.addOneInner(1); // Shouldn't compile.
-//        tfc.toBeRemoved("abc"); // Shouldn't compile.
-//        tfc.unsupportedMethod(); // Shouldn't compile.
-//        int a = tfc.keep; // Shouldn't compile
-//        int b = tfc.remove; // Shouldn't compile
-//    }
-
-    @Test
-    public void testSubstitute() {
-        TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
-        assertThat(tfc.addTwo(1)).isEqualTo(3);
-    }
-
-    @Test
-    public void testSubstituteNative() {
-        TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
-        assertThat(tfc.nativeAddThree(1)).isEqualTo(4);
-    }
-
-    @Test
-    public void testVisibleButUsesUnsupportedMethod() {
-        TinyFrameworkClassAnnotations tfc = new TinyFrameworkClassAnnotations();
-
-        thrown.expect(RuntimeException.class);
-        thrown.expectMessage("not yet supported");
-        tfc.visibleButUsesUnsupportedMethod();
-    }
-}
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
index 6b46c84..5b2795c 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/asm/AsmUtilsTest.kt
@@ -23,18 +23,6 @@
 import org.objectweb.asm.Opcodes.ACC_STATIC
 
 class AsmUtilsTest {
-    private fun checkGetDirectOuterClassName(input: String, expected: String?) {
-        assertThat(getDirectOuterClassName(input)).isEqualTo(expected)
-    }
-
-    @Test
-    fun testGetDirectOuterClassName() {
-        checkGetDirectOuterClassName("a", null)
-        checkGetDirectOuterClassName("a\$x", "a")
-        checkGetDirectOuterClassName("a.b.c\$x", "a.b.c")
-        checkGetDirectOuterClassName("a.b.c\$x\$y", "a.b.c\$x")
-    }
-
     @Test
     fun testVisibility() {
         fun test(access: Int, expected: Visibility) {
diff --git a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
index f651514..85b6e80 100644
--- a/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
+++ b/tools/hoststubgen/hoststubgen/test/com/android/hoststubgen/utils/ClassFilterTest.kt
@@ -72,6 +72,18 @@
     }
 
     @Test
+    fun testNestedClass() {
+        val f = ClassFilter.buildFromString("a.b.c\nm.n.o\$p\n", false, "X")
+        assertThat(f.matches("a/b/c")).isEqualTo(true)
+        assertThat(f.matches("a/b/c\$d")).isEqualTo(true)
+        assertThat(f.matches("a/b/c\$d\$e")).isEqualTo(true)
+        assertThat(f.matches("m/n/o")).isEqualTo(false)
+        assertThat(f.matches("m/n/o\$p")).isEqualTo(true)
+        assertThat(f.matches("m/n/o\$p\$r")).isEqualTo(true)
+        assertThat(f.matches("m/n/o\$p\$r\$")).isEqualTo(true)
+    }
+
+    @Test
     fun testBadFilter1() {
         try {
             ClassFilter.buildFromString("""
diff --git a/tools/hoststubgen/scripts/dump-jar b/tools/hoststubgen/scripts/dump-jar
index fe546fe..8765245 100755
--- a/tools/hoststubgen/scripts/dump-jar
+++ b/tools/hoststubgen/scripts/dump-jar
@@ -26,12 +26,12 @@
 
         Dump a *.class file
 
-      dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: filename regex] [...]
+      dump-jar [-v] [-s] [-o OUTPUT-FILENAME] JAR-FILE[: class internal name regex] [...]
 
         Dump a jar file.
 
         If a filename contains a ':', then the following part
-        will be used to filter files in the jar file.
+        will be used to filter files in the jar file that matches against class internal names.
 
         For example, "file.jar:/MyClass$" will only dump "MyClass" in file.jar.
 
@@ -78,16 +78,6 @@
   JAVAP_OPTS="-p -c -v"
 fi
 
-
-# Normalize a java class name.
-# Convert '.' to '/'
-# Remove the *.class suffix.
-normalize() {
-  local name="$1"
-  name="${name%.class}" # Remove the .class suffix.
-  echo "$name" | tr '.' '/'
-}
-
 # Convert the output for `-s` as needed.
 filter_output() {
   if (( $simple )) ; then
@@ -124,6 +114,12 @@
   fi
 }
 
+# Read jar file names and remove the .class suffix.
+# Also remove non-class files.
+to_internal_names() {
+    sed -ne 's/\.class$//p'
+}
+
 for file in "${@}"; do
 
     # *.class?
@@ -136,7 +132,7 @@
         # Take the regex. Remove everything up to : in $file
         regex=""
         if [[ "$file" =~ : ]] ; then
-            regex="$(normalize "${file##*:}")"
+            regex="${file##*:}"
         fi
 
         # Remove everything after ':', inclusively, in $file.
@@ -151,13 +147,9 @@
           echo
         fi
 
-        jar tf "$file" | grep '\.class$' | sort | while read -r class ; do
-            if normalize "$class" | grep -q -- "$regex" ; then
-                echo "## Class: $class"
-                javap $dump_code_opt $JAVAP_OPTS -cp $file ${class%.class}
-            else
-                (( $verbose )) && echo "## Skipping class: $class"
-            fi
+        jar tf "$file" | sort | to_internal_names | grep -- "$regex" | while read -r class ; do
+            echo "## Class: $class.class"
+            javap $dump_code_opt $JAVAP_OPTS -cp "$file" "${class}"
         done
 
     else
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
index 3c99e68..c3595b7 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -120,7 +120,7 @@
 
                         logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
                             call.name.toString(), call, context), groupMap.getValue(groupName))
-                    } else if (call.name.id == "initialize") {
+                    } else if (call.name.id == "init") {
                         // No processing
                     } else {
                         // Process non-log message calls
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index aa53005..22858803b5 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -26,7 +26,6 @@
 import com.github.javaparser.ast.Modifier
 import com.github.javaparser.ast.NodeList
 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
-import com.github.javaparser.ast.body.InitializerDeclaration
 import com.github.javaparser.ast.expr.ArrayAccessExpr
 import com.github.javaparser.ast.expr.ArrayCreationExpr
 import com.github.javaparser.ast.expr.ArrayInitializerExpr
@@ -42,7 +41,10 @@
 import com.github.javaparser.ast.expr.ObjectCreationExpr
 import com.github.javaparser.ast.expr.SimpleName
 import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.ast.expr.VariableDeclarationExpr
 import com.github.javaparser.ast.stmt.BlockStmt
+import com.github.javaparser.ast.stmt.ReturnStmt
+import com.github.javaparser.ast.type.ClassOrInterfaceType
 import java.io.File
 import java.io.FileInputStream
 import java.io.FileNotFoundException
@@ -195,6 +197,7 @@
         groups: Map<String, LogGroup>,
         protoLogGroupsClassName: String
     ) {
+        var needsCreateLogGroupsMap = false
         classDeclaration.fields.forEach { field ->
             field.getAnnotationByClass(ProtoLogToolInjected::class.java)
                     .ifPresent { annotationExpr ->
@@ -222,33 +225,10 @@
                                             } ?: NullLiteralExpr())
                                 }
                                 ProtoLogToolInjected.Value.LOG_GROUPS.name -> {
-                                    val initializerBlockStmt = BlockStmt()
-                                    for (group in groups) {
-                                        initializerBlockStmt.addStatement(
-                                            MethodCallExpr()
-                                                    .setName("put")
-                                                    .setArguments(
-                                                        NodeList(StringLiteralExpr(group.key),
-                                                            FieldAccessExpr()
-                                                                    .setScope(
-                                                                        NameExpr(
-                                                                            protoLogGroupsClassName
-                                                                        ))
-                                                                    .setName(group.value.name)))
-                                        )
-                                        group.key
-                                    }
-
-                                    val treeMapCreation = ObjectCreationExpr()
-                                            .setType("TreeMap<String, IProtoLogGroup>")
-                                            .setAnonymousClassBody(NodeList(
-                                                InitializerDeclaration().setBody(
-                                                    initializerBlockStmt
-                                                )
-                                            ))
-
+                                    needsCreateLogGroupsMap = true
                                     field.setFinal(true)
-                                    field.variables.first().setInitializer(treeMapCreation)
+                                    field.variables.first().setInitializer(
+                                        MethodCallExpr().setName("createLogGroupsMap"))
                                 }
                                 ProtoLogToolInjected.Value.CACHE_UPDATER.name -> {
                                     field.setFinal(true)
@@ -261,6 +241,45 @@
                         }
                     }
         }
+
+        if (needsCreateLogGroupsMap) {
+            val body = BlockStmt()
+            body.addStatement(AssignExpr(
+                VariableDeclarationExpr(
+                    ClassOrInterfaceType("TreeMap<String, IProtoLogGroup>"),
+                    "result"
+                ),
+                ObjectCreationExpr().setType("TreeMap<String, IProtoLogGroup>"),
+                AssignExpr.Operator.ASSIGN
+            ))
+            for (group in groups) {
+                body.addStatement(
+                    MethodCallExpr(
+                        NameExpr("result"),
+                        "put",
+                        NodeList(
+                                StringLiteralExpr(group.key),
+                                FieldAccessExpr()
+                                        .setScope(
+                                            NameExpr(
+                                                protoLogGroupsClassName
+                                            ))
+                                        .setName(group.value.name)
+                        )
+                    )
+                )
+            }
+            body.addStatement(ReturnStmt(NameExpr("result")))
+
+            val method = classDeclaration.addMethod(
+                "createLogGroupsMap",
+                Modifier.Keyword.PRIVATE,
+                Modifier.Keyword.STATIC,
+                Modifier.Keyword.FINAL
+            )
+            method.setType("TreeMap<String, IProtoLogGroup>")
+            method.setBody(body)
+        }
     }
 
     private fun injectCacheClass(
diff --git a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
index 0b61948..9c44332 100644
--- a/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
+++ b/tools/sdkparcelables/src/com/android/sdkparcelables/Main.kt
@@ -23,13 +23,21 @@
 import java.util.zip.ZipFile
 
 fun main(args: Array<String>) {
-    if (args.size != 2) {
+    if (args.size < 2 || args.size > 3) {
         usage()
     }
 
     val zipFileName = args[0]
     val aidlFileName = args[1]
 
+    var stable = false
+    if (args.size == 3) {
+        if (args[2] != "--guarantee_stable") {
+            usage()
+        }
+        stable = true
+    }
+
     val zipFile: ZipFile
 
     try {
@@ -55,6 +63,9 @@
         val outFile = File(aidlFileName)
         val outWriter = outFile.bufferedWriter()
         for (parcelable in parcelables) {
+            if (stable) {
+                outWriter.write("@JavaOnlyStableParcelable ")
+            }
             outWriter.write("parcelable ")
             outWriter.write(parcelable.replace('/', '.').replace('$', '.'))
             outWriter.write(";\n")
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
new file mode 100644
index 0000000..aca25eb
--- /dev/null
+++ b/tools/systemfeatures/Android.bp
@@ -0,0 +1,63 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library_host {
+    name: "systemfeatures-gen-lib",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "guava",
+        "javapoet",
+    ],
+}
+
+java_binary_host {
+    name: "systemfeatures-gen-tool",
+    main_class: "com.android.systemfeatures.SystemFeaturesGenerator",
+    static_libs: ["systemfeatures-gen-lib"],
+}
+
+// TODO(b/203143243): Add golden diff test for generated sources.
+// Functional runtime behavior is covered in systemfeatures-gen-tests.
+genrule {
+    name: "systemfeatures-gen-tests-srcs",
+    cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
+        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoNoFeatures --readonly=true --feature-apis=WATCH > $(location RoNoFeatures.java) && " +
+        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwFeatures --readonly=false --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: > $(location RwFeatures.java) && " +
+        "$(location systemfeatures-gen-tool) com.android.systemfeatures.RoFeatures --readonly=true --feature=WATCH:1 --feature=WIFI:0 --feature=VULKAN:-1 --feature=AUTO: --feature-apis=WATCH,PC > $(location RoFeatures.java)",
+    out: [
+        "RwNoFeatures.java",
+        "RoNoFeatures.java",
+        "RwFeatures.java",
+        "RoFeatures.java",
+    ],
+    tools: ["systemfeatures-gen-tool"],
+}
+
+java_test_host {
+    name: "systemfeatures-gen-tests",
+    test_suites: ["general-tests"],
+    srcs: [
+        "tests/**/*.java",
+        ":systemfeatures-gen-tests-srcs",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    static_libs: [
+        "aconfig-annotations-lib",
+        "framework-annotations-lib",
+        "junit",
+        "objenesis",
+        "mockito",
+        "truth",
+    ],
+}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
new file mode 100644
index 0000000..e537ffc
--- /dev/null
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -0,0 +1,267 @@
+/*
+ * 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.systemfeatures
+
+import com.google.common.base.CaseFormat
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+
+/*
+ * Simple Java code generator that takes as input a list of defined features and generates an
+ * accessory class based on the provided versions.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ *   <cmd> com.foo.RoSystemFeatures --readonly=true \
+ *           --feature=WATCH:0 --feature=AUTOMOTIVE: --feature=VULKAN:9348
+ *           --feature-apis=WATCH,PC,LEANBACK
+ * </pre>
+ *
+ * This generates a class that has the following signature:
+ *
+ * <pre>
+ * package com.foo;
+ * public final class RoSystemFeatures {
+ *     @AssumeTrueForR8
+ *     public static boolean hasFeatureWatch(Context context);
+ *     @AssumeFalseForR8
+ *     public static boolean hasFeatureAutomotive(Context context);
+ *     @AssumeTrueForR8
+ *     public static boolean hasFeatureVulkan(Context context);
+ *     public static boolean hasFeaturePc(Context context);
+ *     public static boolean hasFeatureLeanback(Context context);
+ *     public static Boolean maybeHasFeature(String feature, int version);
+ * }
+ * </pre>
+ */
+object SystemFeaturesGenerator {
+    private const val FEATURE_ARG = "--feature="
+    private const val FEATURE_APIS_ARG = "--feature-apis="
+    private const val READONLY_ARG = "--readonly="
+    private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
+    private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
+    private val ASSUME_TRUE_CLASS =
+        ClassName.get("com.android.aconfig.annotations", "AssumeTrueForR8")
+    private val ASSUME_FALSE_CLASS =
+        ClassName.get("com.android.aconfig.annotations", "AssumeFalseForR8")
+
+    private fun usage() {
+        println("Usage: SystemFeaturesGenerator <outputClassName> [options]")
+        println(" Options:")
+        println("  --readonly=true|false    Whether to encode features as build-time constants")
+        println("  --feature=\$NAME:\$VER   A feature+version pair (blank version == disabled)")
+        println("                           This will always generate associated query APIs,")
+        println("                           adding to or replacing those from `--feature-apis=`.")
+        println("  --feature-apis=\$NAME_1,\$NAME_2")
+        println("                           A comma-separated set of features for which to always")
+        println("                           generate named query APIs. If a feature in this set is")
+        println("                           not explicitly defined via `--feature=`, then a simple")
+        println("                           runtime passthrough API will be generated, regardless")
+        println("                           of the `--readonly` flag. This allows decoupling the")
+        println("                           API surface from variations in device feature sets.")
+    }
+
+    /** Main entrypoint for build-time system feature codegen. */
+    @JvmStatic
+    fun main(args: Array<String>) {
+        if (args.size < 1) {
+            usage()
+            return
+        }
+
+        var readonly = false
+        var outputClassName: ClassName? = null
+        val featureArgs = mutableListOf<FeatureArg>()
+        // We could just as easily hardcode this list, as the static API surface should change
+        // somewhat infrequently, but this decouples the codegen from the framework completely.
+        val featureApiArgs = mutableSetOf<String>()
+        for (arg in args) {
+            when {
+                arg.startsWith(READONLY_ARG) ->
+                    readonly = arg.substring(READONLY_ARG.length).toBoolean()
+                arg.startsWith(FEATURE_ARG) -> {
+                    featureArgs.add(parseFeatureArg(arg))
+                }
+                arg.startsWith(FEATURE_APIS_ARG) -> {
+                    featureApiArgs.addAll(
+                        arg.substring(FEATURE_APIS_ARG.length).split(",").map {
+                            parseFeatureName(it)
+                        }
+                    )
+                }
+                else -> outputClassName = ClassName.bestGuess(arg)
+            }
+        }
+
+        // First load in all of the feature APIs we want to generate. Explicit feature definitions
+        // will then override this set with the appropriate readonly and version value.
+        val features = mutableMapOf<String, FeatureInfo>()
+        featureApiArgs.associateByTo(
+            features,
+            { it },
+            { FeatureInfo(it, version = null, readonly = false) },
+        )
+        featureArgs.associateByTo(
+            features,
+            { it.name },
+            { FeatureInfo(it.name, it.version, readonly) },
+        )
+
+        outputClassName
+            ?: run {
+                println("Output class name must be provided.")
+                usage()
+                return
+            }
+
+        val classBuilder =
+            TypeSpec.classBuilder(outputClassName)
+                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+                .addJavadoc("@hide")
+
+        addFeatureMethodsToClass(classBuilder, features.values)
+        addMaybeFeatureMethodToClass(classBuilder, features.values)
+
+        // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
+        JavaFile.builder(outputClassName.packageName(), classBuilder.build())
+            .build()
+            .writeTo(System.out)
+    }
+
+    /*
+     * Parses a feature argument of the form "--feature=$NAME:$VER", where "$VER" is optional.
+     *   * "--feature=WATCH:0" -> Feature enabled w/ version 0 (default version when enabled)
+     *   * "--feature=WATCH:7" -> Feature enabled w/ version 7
+     *   * "--feature=WATCH:"  -> Feature disabled
+     */
+    private fun parseFeatureArg(arg: String): FeatureArg {
+        val featureArgs = arg.substring(FEATURE_ARG.length).split(":")
+        val name = parseFeatureName(featureArgs[0])
+        val version = featureArgs.getOrNull(1)?.toIntOrNull()
+        return FeatureArg(name, version)
+    }
+
+    private fun parseFeatureName(name: String): String =
+        if (name.startsWith("FEATURE_")) name else "FEATURE_$name"
+
+    /*
+     * Adds per-feature query methods to the class with the form:
+     * {@code public static boolean hasFeatureX(Context context)},
+     * returning the fallback value from PackageManager if not readonly.
+     */
+    private fun addFeatureMethodsToClass(
+        builder: TypeSpec.Builder,
+        features: Collection<FeatureInfo>,
+    ) {
+        for (feature in features) {
+            // Turn "FEATURE_FOO" into "hasFeatureFoo".
+            val methodName =
+                "has" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, feature.name)
+            val methodBuilder =
+                MethodSpec.methodBuilder(methodName)
+                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                    .returns(Boolean::class.java)
+                    .addParameter(CONTEXT_CLASS, "context")
+
+            if (feature.readonly) {
+                val featureEnabled = compareValues(feature.version, 0) >= 0
+                methodBuilder.addAnnotation(
+                    if (featureEnabled) ASSUME_TRUE_CLASS else ASSUME_FALSE_CLASS
+                )
+                methodBuilder.addStatement("return $featureEnabled")
+            } else {
+                methodBuilder.addStatement(
+                    "return hasFeatureFallback(context, \$T.\$N)",
+                    PACKAGEMANAGER_CLASS,
+                    feature.name
+                )
+            }
+            builder.addMethod(methodBuilder.build())
+        }
+
+        // This is a trivial method, even if unused based on readonly-codegen, it does little harm
+        // to always include it.
+        builder.addMethod(
+            MethodSpec.methodBuilder("hasFeatureFallback")
+                .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+                .returns(Boolean::class.java)
+                .addParameter(CONTEXT_CLASS, "context")
+                .addParameter(String::class.java, "featureName")
+                .addStatement("return context.getPackageManager().hasSystemFeature(featureName, 0)")
+                .build()
+        )
+    }
+
+    /*
+     * Adds a generic query method to the class with the form: {@code public static boolean
+     * maybeHasFeature(String featureName, int version)}, returning null if the feature version is
+     * undefined or not readonly.
+     *
+     * This method is useful for internal usage within the framework, e.g., from the implementation
+     * of {@link android.content.pm.PackageManager#hasSystemFeature(Context)}, when we may only
+     * want a valid result if it's defined as readonly, and we want a custom fallback otherwise
+     * (e.g., to the existing runtime binder query).
+     */
+    private fun addMaybeFeatureMethodToClass(
+        builder: TypeSpec.Builder,
+        features: Collection<FeatureInfo>,
+    ) {
+        val methodBuilder =
+            MethodSpec.methodBuilder("maybeHasFeature")
+                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                .addAnnotation(ClassName.get("android.annotation", "Nullable"))
+                .returns(Boolean::class.javaObjectType) // Use object type for nullability
+                .addParameter(String::class.java, "featureName")
+                .addParameter(Int::class.java, "version")
+
+        var hasSwitchBlock = false
+        for (feature in features) {
+            // We only return non-null results for queries against readonly-defined features.
+            if (!feature.readonly) {
+                continue
+            }
+            if (!hasSwitchBlock) {
+                // As an optimization, only create the switch block if needed. Even an empty
+                // switch-on-string block can induce a hash, which we can avoid if readonly
+                // support is completely disabled.
+                hasSwitchBlock = true
+                methodBuilder.beginControlFlow("switch (featureName)")
+            }
+            methodBuilder.addCode("case \$T.\$N: ", PACKAGEMANAGER_CLASS, feature.name)
+            if (feature.version != null) {
+                methodBuilder.addStatement("return \$L >= version", feature.version)
+            } else {
+                methodBuilder.addStatement("return false")
+            }
+        }
+        if (hasSwitchBlock) {
+            methodBuilder.addCode("default: ")
+            methodBuilder.addStatement("break")
+            methodBuilder.endControlFlow()
+        }
+        methodBuilder.addStatement("return null")
+        builder.addMethod(methodBuilder.build())
+    }
+
+    private data class FeatureArg(val name: String, val version: Int?)
+
+    private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean)
+}
diff --git a/tools/systemfeatures/tests/Context.java b/tools/systemfeatures/tests/Context.java
new file mode 100644
index 0000000..630bc07
--- /dev/null
+++ b/tools/systemfeatures/tests/Context.java
@@ -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 android.content;
+
+import android.content.pm.PackageManager;
+
+/** Stub for testing. */
+public class Context {
+    /** @hide */
+    public PackageManager getPackageManager() {
+        return null;
+    }
+}
diff --git a/tools/systemfeatures/tests/PackageManager.java b/tools/systemfeatures/tests/PackageManager.java
new file mode 100644
index 0000000..db67048
--- /dev/null
+++ b/tools/systemfeatures/tests/PackageManager.java
@@ -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 android.content.pm;
+
+/** Stub for testing */
+public class PackageManager {
+    public static final String FEATURE_AUTO = "automotive";
+    public static final String FEATURE_PC = "pc";
+    public static final String FEATURE_VULKAN = "vulkan";
+    public static final String FEATURE_WATCH = "watch";
+    public static final String FEATURE_WIFI = "wifi";
+
+    /** @hide */
+    public boolean hasSystemFeature(String featureName, int version) {
+        return false;
+    }
+}
diff --git a/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
new file mode 100644
index 0000000..6dfd244
--- /dev/null
+++ b/tools/systemfeatures/tests/SystemFeaturesGeneratorTest.java
@@ -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.systemfeatures;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(JUnit4.class)
+public class SystemFeaturesGeneratorTest {
+
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock private Context mContext;
+    @Mock private PackageManager mPackageManager;
+
+    @Before
+    public void setUp() {
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+    }
+
+    @Test
+    public void testReadonlyDisabledNoDefinedFeatures() {
+        // Always report null for conditional queries if readonly codegen is disabled.
+        assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+        assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+        assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+        assertThat(RwNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+        assertThat(RwNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+    }
+
+    @Test
+    public void testReadonlyNoDefinedFeatures() {
+        // If no features are explicitly declared as readonly available, always report
+        // null for conditional queries.
+        assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+        assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+        assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+        assertThat(RoNoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+        assertThat(RoNoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+
+        // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
+        // versioned feature definition.
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+        assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+        assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
+    }
+
+    @Test
+    public void testReadonlyDisabledWithDefinedFeatures() {
+        // Always fall back to the PackageManager for defined, explicit features queries.
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+        assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
+
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+        assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
+
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI, 0)).thenReturn(true);
+        assertThat(RwFeatures.hasFeatureWifi(mContext)).isTrue();
+
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN, 0)).thenReturn(false);
+        assertThat(RwFeatures.hasFeatureVulkan(mContext)).isFalse();
+
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(false);
+        assertThat(RwFeatures.hasFeatureAuto(mContext)).isFalse();
+
+        // For defined and undefined features, conditional queries should report null (unknown).
+        assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isNull();
+        assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isNull();
+        assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isNull();
+        assertThat(RwFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
+        assertThat(RwFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+    }
+
+    @Test
+    public void testReadonlyWithDefinedFeatures() {
+        // Always use the build-time feature version for defined, explicit feature queries, never
+        // falling back to the runtime query.
+        assertThat(RoFeatures.hasFeatureWatch(mContext)).isTrue();
+        assertThat(RoFeatures.hasFeatureWifi(mContext)).isTrue();
+        assertThat(RoFeatures.hasFeatureVulkan(mContext)).isFalse();
+        assertThat(RoFeatures.hasFeatureAuto(mContext)).isFalse();
+        verify(mPackageManager, never()).hasSystemFeature(anyString(), anyInt());
+
+        // For defined feature types, conditional queries should reflect the build-time versions.
+        // VERSION=1
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, -1)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 100)).isFalse();
+
+        // VERSION=0
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, -1)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 0)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_WIFI, 100)).isFalse();
+
+        // VERSION=-1
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, -1)).isTrue();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 0)).isFalse();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 100)).isFalse();
+
+        // DISABLED
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, -1)).isFalse();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isFalse();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 100)).isFalse();
+
+        // For feature APIs without an associated feature definition, conditional queries should
+        // report null, and explicit queries should report runtime-defined versions.
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(true);
+        assertThat(RoFeatures.hasFeaturePc(mContext)).isTrue();
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(false);
+        assertThat(RoFeatures.hasFeaturePc(mContext)).isFalse();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, -1)).isNull();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, 0)).isNull();
+        assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, 100)).isNull();
+
+        // For undefined types, conditional queries should report null (unknown).
+        assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", -1)).isNull();
+        assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 0)).isNull();
+        assertThat(RoFeatures.maybeHasFeature("com.arbitrary.feature", 100)).isNull();
+    }
+}
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index b0f68f7..f68ae2c 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -98,6 +98,18 @@
         }
 
         @Override
+        public void onServiceDisconnected() {
+            if (mCallback != null) {
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mCallback.onServiceDisconnected());
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+
+        @Override
         public void onHotspotNetworksUpdated(@NonNull List<HotspotNetwork> networks) {
             if (mCallback != null) {
                 final long token = Binder.clearCallingIdentity();
@@ -247,13 +259,13 @@
                 mService = null;
                 synchronized (mProxyDataLock) {
                     if (!mCallbackProxyCache.isEmpty()) {
-                        mCallbackProxyCache.keySet().forEach(
-                                SharedConnectivityClientCallback::onServiceDisconnected);
+                        mCallbackProxyCache.values().forEach(
+                                SharedConnectivityCallbackProxy::onServiceDisconnected);
                         mCallbackProxyCache.clear();
                     }
                     if (!mProxyMap.isEmpty()) {
-                        mProxyMap.keySet().forEach(
-                                SharedConnectivityClientCallback::onServiceDisconnected);
+                        mProxyMap.values().forEach(
+                                SharedConnectivityCallbackProxy::onServiceDisconnected);
                         mProxyMap.clear();
                     }
                 }
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
index 521f943..7b892af 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/ISharedConnectivityCallback.aidl
@@ -32,4 +32,5 @@
     oneway void onKnownNetworkConnectionStatusChanged(in KnownNetworkConnectionStatus status);
     oneway void onSharedConnectivitySettingsChanged(in SharedConnectivitySettingsState state);
     oneway void onServiceConnected();
+    oneway void onServiceDisconnected();
 }